;;; PLS.fnl ;; a parser for *.pls files ;; Copyright (C) 2022 Case Duckworth ;; License: Good Choices (https://acdw.casa/gcl) ;; https://en.wikipedia.org/wiki/PLS_(file_format) (fn split-lines [str] "Split STR into a list of lines." (icollect [ln (: str :gmatch "([^\n]*)\n?")] ln)) (fn parse [str] "Parse a list of LINES into a playlist." ;; This /maybe/ should take a string, I suppose. (let [t []] (each [_ l (ipairs (split-lines str))] (let [(ltype num title) (: l :match "^(.*)([0-9]+)=(.*)")] (when (and ltype num title) (let [ltype (string.lower ltype) num (tonumber num)] (if (. t num) (tset t num ltype title) (tset t num {ltype title})))))) t)) (fn read [file] "Read a .pls FILE into a playlist." (with-open [p (io.open file)] (parse (: p :read :*a)))) (fn serialize [playlist] "Serialize a PLAYLIST into a .pls-formatted string." (let [s (icollect [n i (ipairs playlist)] (.. (string.format "File%d=%s\n" n (. i :file)) (if (. i :title) (string.format "Title%d=%s\n" n (. i :title)) "") (if (. i :length) (string.format "Length%d=%s\n" n (. i :length)) "")))] (.. "[playlist]\n\n" (table.concat s "\n") "\n\n" :NumberOfEntries= (length s) "\n" "Version=2\n"))) (fn write [playlist file] "Write PLAYLIST to FILE." (with-open [p (io.open file :w)] (: p :write (serialize playlist)))) {: parse : read : serialize : write}