X-Git-Url: https://thelambdalab.xyz/gitweb/index.cgi?p=emus.git;a=blobdiff_plain;f=emus.el;h=cfa674a86ea8b49a1142003d5b40738dbeb3d607;hp=08df9f5a9988aa8594810cfb53675fd2aa29b481;hb=HEAD;hpb=df12eade89f87cc8a6f516fe61f3ebfc0635703a diff --git a/emus.el b/emus.el index 08df9f5..e5f2ea3 100644 --- a/emus.el +++ b/emus.el @@ -56,6 +56,10 @@ "Name of (and, optionally, path to) mpg123 binary." :type '(string)) +(defcustom emus-mpg123-args nil + "Arguments to pass to mpg123." + :type '(repeat string)) + (defface emus-artist '((((background dark)) :inherit font-lock-string-face :background "#222" :extend t) (t :inherit font-lock-string-face :background "#ddd" :extend t)) @@ -100,6 +104,14 @@ Used to prevent commands from interfering with library construction.") (defvar emus-current-volume 100 "The current playback volume.") +(defvar emus-current-progress "" + "String describing the progress through the current track.") + +(defvar emus-progress-enabled t + "Current state of progress tracking. + +To enable or disable progress tracking, using `emus-toggle-progress-tracking'. +(Changing the value of this variable will not affect anything.)") ;;; mpg123 process ;; @@ -117,9 +129,12 @@ Used to prevent commands from interfering with library construction.") emus-process (let ((proc (make-process :name "emus-process" - :command `(,emus-mpg123-program "-R")))) + :command `(,emus-mpg123-program + ,@emus-mpg123-args + "-R")))) (set-process-query-on-exit-flag proc nil) - (process-send-string proc "silence\n") + (unless emus-progress-enabled + (process-send-string proc "silence\n")) proc)))) (defun emus--send-cmd-raw (cmd &rest args) @@ -136,6 +151,14 @@ be used by `emus--load-library'." (unless emus--proc-in-use (apply #'emus--send-cmd-raw cmd args))) +(defun emus-kill-process () + "Kill any existing mpg123 process." + (let ((emus-process (get-process "emus-process"))) + (if emus-process + (kill-process emus-process)) + (setq emus-state 'stopped + emus--proc-in-use nil + emus-tracks nil))) ;;; Library ;; @@ -191,7 +214,7 @@ 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))) + (setq emus-tracks nil) (let ((proc (emus-get-process)) (tagstr "") (filenames (emus-get-audio-files))) @@ -208,6 +231,7 @@ Once the library is initialized, the function THEN is called." (set-process-filter proc nil) (setq emus-tracks (reverse emus-tracks)) (emus--sort-tracks) + (emus--add-tracks-from-playlist-files) (unless emus-current-track (setq emus-current-track (car emus-tracks))) (funcall then) @@ -220,9 +244,9 @@ Once the library is initialized, the function THEN is called." (defun emus--make-track-from-tagstr (filename tagstr) "Parse TAGSTR to populate the fields of a track corresponding to FILENAME." - (let ((artist "") - (album "") - (title "")) + (let ((artist "Unknown Artist") + (album "Unknown Album") + (title filename)) (dolist (line (split-string tagstr "\n")) (let ((found-artist (elt (split-string line "@I ID3v2.artist:") 1)) (found-album (elt (split-string line "@I ID3v2.album:") 1)) @@ -233,9 +257,9 @@ Once the library is initialized, the function THEN is called." (found-title (setq title found-title))))) (emus-make-track artist album title filename))) -(defun emus--make-tracks-from-playlist-files (filenames) +(defun emus--add-tracks-from-playlist-files () (let ((tracks nil)) - (dolist (filename filenames) + (dolist (filename (emus-get-playlist-files)) (let ((artist "Playlists") (album (file-name-base filename)) (title nil) @@ -257,7 +281,7 @@ Once the library is initialized, the function THEN is called." (setq tracks (cons (emus-make-track artist album (or title filename) filename) tracks)) (setq title nil)))))) - tracks)) + (setq emus-tracks (append emus-tracks (reverse tracks))))) (defun emus--sort-tracks () "Sort the library tracks according to artist and album. @@ -272,11 +296,7 @@ by the filesystem." (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))))))))) + (string< artist1 artist2))))))) (defmacro emus--with-library (&rest body) "Evaluate BODY with the library initialized." @@ -295,13 +315,13 @@ by the filesystem." (defun emus--resume-cp () "Resume continuous playback." - (setq emus-continuous-playback t) - (set-process-filter (emus-get-process) - (lambda (_proc string) - (and emus-continuous-playback - (eq emus-state 'playing) - (string-suffix-p "@P 0\n" string) - (emus-play-next))))) + (setq emus-continuous-playback t)) + +(defun emus--timestamp (seconds-total) + "Produce a timestamp string representation of SECONDS-TOTAL." + (let* ((seconds (truncate (mod seconds-total 60))) + (minutes (truncate (/ seconds-total 60)))) + (format "%02d:%02d" minutes seconds))) (defun emus-play-track (track) "Set TRACK as current and start playing." @@ -310,6 +330,40 @@ by the filesystem." (emus-send-cmd "l" (emus-track-file track)) (setq emus-state 'playing) (setq emus-current-track track) + (setq emus-current-progress (if emus-progress-enabled "" " (progress disabled)")) + (set-process-filter + (emus-get-process) + (lambda (_proc string) + (dolist (line (string-split string "\n")) + (pcase line + ((and "@P 0" + (guard emus-continuous-playback) + (guard (eq emus-state 'playing))) + (emus-play-next)) + ((and (guard emus-progress-enabled) + (rx (: string-start + "@I ICY-META: StreamTitle=" + (let str (+ (not ";"))) + ";"))) + (message (concat "Emus: Playing stream " str))) + ((and (guard emus-progress-enabled) + (rx (: string-start + "@F " + (+ digit) + " " + (+ digit) + " " + (let left-str (+ (not " "))) + " " + (let right-str (+ any))))) + (let* ((left (string-to-number left-str)) + (right (string-to-number right-str)) + (total (+ left right))) + (setq emus-current-progress + (format " %s/%s" + (emus--timestamp left) + (emus--timestamp total))))) + )))) (emus--update-track old-track) (emus--update-track track) (emus--resume-cp) @@ -443,6 +497,16 @@ If PREV is non-nil, plays the last track of the previous album." (interactive) (emus-jump -10)) +(defun emus-jump-1m-forward () + "Jump 1 minute forward in current track." + (interactive) + (emus-jump 60)) + +(defun emus-jump-1m-backward () + "Jump 1 minute backward in current track." + (interactive) + (emus-jump -60)) + (defun emus-display-status () "Display the current playback status in the minibuffer." (interactive) @@ -451,17 +515,30 @@ If PREV is non-nil, plays the last track of the previous album." (concat "Emus: Volume %d%%" (pcase emus-state ('stopped " [Stopped]") - ('paused " [Paused]") - ('playing " [Playing]") + ('paused (format " [Paused%s]" emus-current-progress)) + ('playing (format " [Playing%s]" emus-current-progress)) (_ "")) " %s") emus-current-volume (if emus-current-track - (format "- %.30s (%.20s)" + (format "- %.30s (%.20s, %.20s)" (emus-track-title emus-current-track) + (emus-track-album emus-current-track) (emus-track-artist emus-current-track)) "")))) +(defun emus-toggle-progress-tracking () + "Enable/disable progress tracking." + (interactive) + (setq emus-progress-enabled (not emus-progress-enabled)) + (if emus-progress-enabled + (progn + (emus-send-cmd "progress") + (setq emus-current-progress "")) + (progn + (emus-send-cmd "silence") + (setq emus-current-progress " (progress diabled)")))) + ;;; Browser ;; @@ -654,6 +731,24 @@ Used to update browser display when `emus-current-track' and/or `emus-state' cha (emus-jump-10s-backward) (emus-display-status)) +(defun emus-jump-1m-forward-status () + "Jump 10s forward in current track, then display the emus status in the minibuffer." + (interactive) + (emus-jump-1m-forward) + (emus-display-status)) + +(defun emus-jump-1m-backward-status () + "Jump 10s backward in current track, then display the emus status in the minibuffer." + (interactive) + (emus-jump-1m-backward) + (emus-display-status)) + +(defun emus-toggle-progress-status () + "Toggle progress tracking, then display the emus status in the minibuffer." + (interactive) + (emus-toggle-progress-tracking) + (emus-display-status)) + (defun emus-goto-current-status () "Move point to the current track, then display the emus status in the minibuffer." (interactive) @@ -665,9 +760,23 @@ Used to update browser display when `emus-current-track' and/or `emus-state' cha (interactive) (emus-stop) (setq emus-tracks nil) - (emus--with-library - (emus-browse) - (emus-display-status))) + (emus-browse) + (emus-display-status)) + +(defun emus-restart-browse () + "Restart the emus process, then refresh the browse window." + (interactive) + (message "Restarting mpg123.") + (emus-kill-process) + (run-at-time 0.1 nil #'emus-browse)) ;Slight delay to wait for kill signal to take effect + +(defun emus-restart-status () + "Restart the emus process, then display the status." + (interactive) + (message "Restarting mpg123.") + (emus-kill-process) + (run-at-time 0.1 nil #'emus-display-status)) ;Slight delay to wait for kill signal to take effect + (defvar emus-browser-mode-map (let ((map (make-sparse-keymap))) @@ -683,22 +792,30 @@ Used to update browser display when `emus-current-track' and/or `emus-state' cha (define-key map (kbd "P") 'emus-play-prev-album-status) (define-key map (kbd ",") 'emus-jump-10s-backward-status) (define-key map (kbd ".") 'emus-jump-10s-forward-status) + (define-key map (kbd "<") 'emus-jump-1m-backward-status) + (define-key map (kbd ">") 'emus-jump-1m-forward-status) (define-key map (kbd "c") 'emus-goto-current-status) + (define-key map (kbd "#") 'emus-toggle-progress-status) + (define-key map (kbd "!") 'emus-restart-browse) (when (fboundp 'evil-define-key*) (evil-define-key* 'motion map - (kbd "SPC") 'emus-playpause-status - (kbd "o") 'emus-stop-status - (kbd "+") 'emus-volume-up-status - (kbd "=") 'emus-volume-up-status - (kbd "-") 'emus-volume-down-status - (kbd "R") 'emus-refresh-status - (kbd "n") 'emus-play-next-status - (kbd "p") 'emus-play-prev-status - (kbd "N") 'emus-play-next-album-status - (kbd "P") 'emus-play-prev-album-status - (kbd ",") 'emus-jump-10s-backward-status - (kbd ".") 'emus-jump-10s-forward-status - (kbd "c") 'emus-goto-current-status)) + (kbd "SPC") 'emus-playpause-status + (kbd "o") 'emus-stop-status + (kbd "+") 'emus-volume-up-status + (kbd "=") 'emus-volume-up-status + (kbd "-") 'emus-volume-down-status + (kbd "R") 'emus-refresh-status + (kbd "n") 'emus-play-next-status + (kbd "p") 'emus-play-prev-status + (kbd "N") 'emus-play-next-album-status + (kbd "P") 'emus-play-prev-album-status + (kbd ",") 'emus-jump-10s-backward-status + (kbd ".") 'emus-jump-10s-forward-status + (kbd "<") 'emus-jump-1m-backward-status + (kbd ">") 'emus-jump-1m-forward-status + (kbd "c") 'emus-goto-current-status + (kbd "#") 'emus-toggle-progress-status + (kbd "!") #'emus-restart-browse)) map) "Keymap for emus browser.")