From: plugd Date: Thu, 26 Aug 2021 15:45:52 +0000 (+0200) Subject: Added support for m3u playlists (and hence internet radio) X-Git-Url: https://thelambdalab.xyz/gitweb/index.cgi?a=commitdiff_plain;h=df12eade89f87cc8a6f516fe61f3ebfc0635703a;p=emus.git Added support for m3u playlists (and hence internet radio) --- diff --git a/emus.el b/emus.el index cfa674a..08df9f5 100644 --- a/emus.el +++ b/emus.el @@ -57,12 +57,12 @@ :type '(string)) (defface emus-artist - '(((background dark) :inherit font-lock-string-face :inverse-video t :extend t) + '((((background dark)) :inherit font-lock-string-face :background "#222" :extend t) (t :inherit font-lock-string-face :background "#ddd" :extend t)) "Face used for artist names in browser.") (defface emus-album - '(((background dark) :inherit font-lock-constant-face :inverse-video t :extend t) + '((((background dark)) :inherit font-lock-constant-face :background "#111" :extend t) (t :inherit font-lock-constant-face :background "#eee" :extend t)) "Face used for album names in browser.") @@ -71,7 +71,7 @@ "Face used for track titles in browser.") (defface emus-track-current - '((t :inherit font-lock-keyword-face :inverse-video t)) + '((t :inherit font-lock-keyword-face :inverse-video t :extend t)) "Face used for track titles in browser.") (defface emus-cursor @@ -143,17 +143,22 @@ be used by `emus--load-library'." (defun emus-get-audio-files () "Get all mp3 files in main emus directory." (mapcar - (lambda (f) - (expand-file-name f)) + #'expand-file-name (directory-files-recursively emus-directory ".*\\.mp3"))) +(defun emus-get-playlist-files () + "Get all m3u files in the main emus music directory." + (mapcar + #'expand-file-name + (directory-files-recursively emus-directory ".*\\.m3u"))) + (defun emus-make-track (artist album title filename &optional pos) "Create an object representing an emus track. ARTIST, ALBUM and TITLE are used to describe the track, FILENAME refers to the mp3 file containing the track. If non-nil, POS specifies the position of the record representing this track in the emus browser buffer." - (vector artist album title filename pos)) + (list artist album title filename pos)) (defun emus-track-artist (track) "The artist corresponding to TRACK." @@ -177,7 +182,7 @@ emus browser buffer." (defun emus-set-track-browser-pos (track pos) "Set the location of the browser buffer record corresponding to TRACK to POS." - (aset track 4 pos)) + (setf (seq-elt track 4) pos)) (defun emus--load-library (then) "Initialize the emus track library. @@ -186,10 +191,10 @@ Once the library is initialized, the function THEN is called." (setq emus--proc-in-use t) (emus--suspend-cp) (setq emus-state 'stopped) + (setq emus-tracks (emus--make-tracks-from-playlist-files (emus-get-playlist-files))) (let ((proc (emus-get-process)) (tagstr "") (filenames (emus-get-audio-files))) - (setq emus-tracks nil) (set-process-filter proc (lambda (proc string) (setq tagstr (concat tagstr string)) (when (string-suffix-p "@P 1\n" string) @@ -210,6 +215,9 @@ Once the library is initialized, the function THEN is called." (setq emus--proc-in-use nil))))) (emus--send-cmd-raw "lp" (car filenames))))) +(defun emus--dump-tracks () + emus-tracks) + (defun emus--make-track-from-tagstr (filename tagstr) "Parse TAGSTR to populate the fields of a track corresponding to FILENAME." (let ((artist "") @@ -223,21 +231,52 @@ Once the library is initialized, the function THEN is called." (found-artist (setq artist found-artist)) (found-album (setq album found-album)) (found-title (setq title found-title))))) - (emus-make-track artist album title filename nil))) + (emus-make-track artist album title filename))) + +(defun emus--make-tracks-from-playlist-files (filenames) + (let ((tracks nil)) + (dolist (filename filenames) + (let ((artist "Playlists") + (album (file-name-base filename)) + (title nil) + (lines (split-string (with-temp-buffer + (insert-file-contents filename) + (buffer-string)) + "\n"))) + (dolist (line lines) + (pcase (string-trim line) + ((rx (: string-start + (* space) + "#extinf:" + (* (not ",")) "," + (let display-title (* any)) + string-end)) + (setq title display-title)) + ((rx (: string-start (* space) "#"))) ;skip other comments + ((rx (let filename (+ any))) + (setq tracks (cons (emus-make-track artist album (or title filename) filename) + tracks)) + (setq title nil)))))) + tracks)) (defun emus--sort-tracks () "Sort the library tracks according to artist and album. Leaves the track titles unsorted, so they will appear in the order specified by the filesystem." - (sort emus-tracks - (lambda (r1 r2) - (let ((artist1 (emus-track-artist r1)) - (artist2 (emus-track-artist r2))) - (if (string= artist1 artist2) - (let ((album1 (emus-track-album r1)) - (album2 (emus-track-album r2))) - (string< album1 album2)) - (string< artist1 artist2)))))) + (setq emus-tracks + (sort emus-tracks + (lambda (r1 r2) + (let ((artist1 (emus-track-artist r1)) + (artist2 (emus-track-artist r2))) + (if (string= artist1 artist2) + (let ((album1 (emus-track-album r1)) + (album2 (emus-track-album r2))) + (string< album1 album2)) + (if (equal artist1 "Playlists") + t + (if (equal artist2 "Playlists") + nil + (string< artist1 artist2))))))))) (defmacro emus--with-library (&rest body) "Evaluate BODY with the library initialized." @@ -415,12 +454,13 @@ If PREV is non-nil, plays the last track of the previous album." ('paused " [Paused]") ('playing " [Playing]") (_ "")) - (if emus-current-track - (format " - %.30s (%.20s)" - (emus-track-title emus-current-track) - (emus-track-artist emus-current-track)) - "")) - emus-current-volume))) + " %s") + emus-current-volume + (if emus-current-track + (format "- %.30s (%.20s)" + (emus-track-title emus-current-track) + (emus-track-artist emus-current-track)) + "")))) ;;; Browser @@ -437,6 +477,7 @@ and thus requires both artist and album headers." (let* ((artist (emus-track-artist track)) (album (emus-track-album track)) (title (emus-track-title track)) + (album-symb (intern (concat artist album))) (help-str (format "mouse-1, RET: Play '%.30s' (%.20s)" title artist)) (field (intern album))) ;Allows easy jumping between albums with cursor. (when (or prev-track first) @@ -482,12 +523,14 @@ and thus requires both artist and album headers." 'follow-link t 'help-echo help-str 'emus-track track + 'invisible album-symb 'field field) (insert (propertize "\n" 'face (if is-current 'emus-track-current 'emus-track) - 'field field))))) + 'field field + 'invisible album-symb))))) (defun emus--update-track (track) "Rerender entry for TRACK in emus browser buffer. @@ -660,7 +703,8 @@ Used to update browser display when `emus-current-track' and/or `emus-state' cha "Keymap for emus browser.") (define-derived-mode emus-browser-mode special-mode "emus-browser" - "Major mode for EMUS music player file browser.") + "Major mode for EMUS music player file browser." + (setq-local buffer-invisibility-spec nil)) (when (fboundp 'evil-set-initial-state) (evil-set-initial-state 'emus-browser-mode 'motion))