From fdf8ede86113f62211a7d873b19b19698464e003 Mon Sep 17 00:00:00 2001 From: Mike Kazantsev Date: Mon, 28 May 2018 08:19:11 +0500 Subject: * lisp/emms-player-mpv: use emms-player-mpv- prefix and sharp-quotes consistently --- lisp/emms-player-mpv.el | 495 +++++++++++++++++++++++++----------------------- 1 file changed, 255 insertions(+), 240 deletions(-) diff --git a/lisp/emms-player-mpv.el b/lisp/emms-player-mpv.el index fb99801..d3d728b 100644 --- a/lisp/emms-player-mpv.el +++ b/lisp/emms-player-mpv.el @@ -49,8 +49,8 @@ ;; ;; See "M-x customize-group emms-player-mpv" and mpv manpage for more options. ;; -;; See `emms-mpv-event-connect-hook' and `emms-mpv-event-functions', -;; as well as `emms-mpv-ipc-req-send' for handling more mpv events, +;; See `emms-player-mpv-event-connect-hook' and `emms-player-mpv-event-functions', +;; as well as `emms-player-mpv-ipc-req-send' for handling more mpv events, ;; processing more playback info and metadata from it, as well as extending ;; control over its vast functionality. ;; @@ -65,9 +65,9 @@ (defcustom emms-player-mpv (emms-player - 'emms-player-mpv-start - 'emms-player-mpv-stop - 'emms-player-mpv-playable-p) + #'emms-player-mpv-start + #'emms-player-mpv-stop + #'emms-player-mpv-playable-p) "*Parameters for mpv player." :type '(cons symbol alist) :group 'emms-player-mpv) @@ -100,7 +100,7 @@ Adding nil as an element to this list will discard emacs (defcustom emms-player-mpv-ipc-method nil "Switch for which IPC method to use with mpv. Possible symbols: detect, ipc-server, unix-socket, file. -Defaults to nil value, which will cause `emms-mpv-ipc-detect' +Defaults to nil value, which will cause `emms-player-mpv-ipc-detect' to pick one based on mpv --version output. Using JSON-IPC variants (ipc-server and unix-socket) enables support for various feedback and metadata options from mpv." @@ -112,148 +112,162 @@ support for various feedback and metadata options from mpv." :group 'emms-player-mpv) (defcustom emms-player-mpv-ipc-socket - (expand-file-name (locate-user-emacs-file "emms-mpv-ipc.sock")) + (expand-file-name (locate-user-emacs-file "emms-player-mpv-ipc.sock")) "Unix IPC socket or FIFO to use with mpv --input-* options, depending on `emms-player-mpv-ipc-method' value and/or mpv version." :type 'file :group 'emms-player-mpv) -(defvar emms-mpv-ipc-proc nil) ; to avoid warnings while keeping useful defs at the top +(defvar emms-player-mpv-ipc-proc nil) ; to avoid warnings while keeping useful defs at the top (defcustom emms-player-mpv-update-duration t "Update track duration when played by mpv. -Uses `emms-mpv-event-functions' hook." +Uses `emms-player-mpv-event-functions' hook." :type 'boolean :set (lambda (sym value) (run-at-time 0.1 nil (lambda (value) (if value - (add-hook 'emms-mpv-event-functions 'emms-mpv-info-duration-event-func) - (remove-hook 'emms-mpv-event-functions 'emms-mpv-info-duration-event-func))) + (add-hook + 'emms-player-mpv-event-functions + #'emms-player-mpv-info-duration-event-func) + (remove-hook + 'emms-player-mpv-event-functions + #'emms-player-mpv-info-duration-event-func))) value)) :group 'emms-player-mpv) (defcustom emms-player-mpv-update-metadata nil "Update track info (artist, album, name, etc) from mpv events, when it is played. This allows to dynamically update stream info from ICY tags, for example. -Uses `emms-mpv-event-connect-hook' and `emms-mpv-event-functions' hooks." +Uses `emms-player-mpv-event-connect-hook' and `emms-player-mpv-event-functions' hooks." :type 'boolean :set (lambda (sym value) (run-at-time 0.1 nil (lambda (value) (if value (progn - (add-hook 'emms-mpv-event-connect-hook 'emms-mpv-info-meta-connect-func) - (add-hook 'emms-mpv-event-functions 'emms-mpv-info-meta-event-func) - (when (process-live-p emms-mpv-ipc-proc) (emms-mpv-info-meta-connect-func))) + (add-hook + 'emms-player-mpv-event-connect-hook + #'emms-player-mpv-info-meta-connect-func) + (add-hook + 'emms-player-mpv-event-functions + #'emms-player-mpv-info-meta-event-func) + (when (process-live-p emms-player-mpv-ipc-proc) + (emms-player-mpv-info-meta-connect-func))) (progn - (remove-hook 'emms-mpv-event-connect-hook 'emms-mpv-info-meta-connect-func) - (remove-hook 'emms-mpv-event-functions 'emms-mpv-info-meta-event-func)))) + (remove-hook + 'emms-player-mpv-event-connect-hook + #'emms-player-mpv-info-meta-connect-func) + (remove-hook + 'emms-player-mpv-event-functions + #'emms-player-mpv-info-meta-event-func)))) value)) :group 'emms-player-mpv) -(defvar emms-mpv-proc nil +(defvar emms-player-mpv-proc nil "Running mpv process, controlled over --input-ipc-server/--input-file sockets.") -(defvar emms-mpv-proc-kill-delay 5 - "Delay until SIGKILL gets sent to `emms-mpv-proc', -if it refuses to exit cleanly on `emms-mpv-proc-stop'.") +(defvar emms-player-mpv-proc-kill-delay 5 + "Delay until SIGKILL gets sent to `emms-player-mpv-proc', +if it refuses to exit cleanly on `emms-player-mpv-proc-stop'.") -(defvar emms-mpv-ipc-proc nil - "Unix socket process that communicates with running `emms-mpv-proc' instance.") +(defvar emms-player-mpv-ipc-proc nil + "Unix socket process that communicates with running `emms-player-mpv-proc' instance.") -(defvar emms-mpv-ipc-buffer " *emms-mpv-ipc*" - "Buffer to associate with `emms-mpv-ipc-proc' socket/pipe process.") +(defvar emms-player-mpv-ipc-buffer " *emms-player-mpv-ipc*" + "Buffer to associate with `emms-player-mpv-ipc-proc' socket/pipe process.") -(defvar emms-mpv-ipc-connect-timer nil +(defvar emms-player-mpv-ipc-connect-timer nil "Timer for connection attempts to JSON IPC unix socket.") -(defvar emms-mpv-ipc-connect-delays +(defvar emms-player-mpv-ipc-connect-delays '(0.1 0.1 0.1 0.1 0.1 0.1 0.2 0.2 0.3 0.3 0.5 1.0 1.0 2.0) "List of delays before initiating socket connection for new mpv process.") -(defvar emms-mpv-ipc-connect-command nil - "JSON command for `emms-mpv-ipc-sentinel' to run as soon as it connects to mpv. +(defvar emms-player-mpv-ipc-connect-command nil + "JSON command for `emms-player-mpv-ipc-sentinel' to run as soon as it connects to mpv. I.e. last command that either initiated connection or was used while connecting to mpv. -Set by `emms-player-mpv-start' and such, cleared once it gets sent by `emms-mpv-ipc-sentinel'.") +Set by `emms-player-mpv-start' and such, +cleared once it gets sent by `emms-player-mpv-ipc-sentinel'.") -(defvar emms-mpv-ipc-id 1 +(defvar emms-player-mpv-ipc-id 1 "Auto-incremented value sent in JSON requests for request_id and observe_property id's. -Use `emms-mpv-ipc-id-get' to get and increment this value, instead of using it directly. -Wraps-around upon reaching `emms-mpv-ipc-id-max' (unlikely to ever happen).") +Use `emms-player-mpv-ipc-id-get' to get and increment this value, instead of using it directly. +Wraps-around upon reaching `emms-player-mpv-ipc-id-max' (unlikely to ever happen).") -(defvar emms-mpv-ipc-id-max (expt 2 30) - "Max value for `emms-mpv-ipc-id' to wrap around after. +(defvar emms-player-mpv-ipc-id-max (expt 2 30) + "Max value for `emms-player-mpv-ipc-id' to wrap around after. Should be fine with both mpv and emacs, and probably never reached anyway.") -(defvar emms-mpv-ipc-req-table nil +(defvar emms-player-mpv-ipc-req-table nil "Auto-initialized hash table of outstanding API req_ids to their handler funcs.") -(defvar emms-mpv-event-connect-hook nil +(defvar emms-player-mpv-event-connect-hook nil "Normal hook run right after establishing new JSON IPC -connection to mpv instance and before `emms-mpv-ipc-connect-command' if any. +connection to mpv instance and before `emms-player-mpv-ipc-connect-command' if any. Best place to send any observe_property, request_log_messages, enable_event commands. -Use `emms-mpv-ipc-id-get' to get unique id values for these. -See also `emms-mpv-event-functions'.") +Use `emms-player-mpv-ipc-id-get' to get unique id values for these. +See also `emms-player-mpv-event-functions'.") -(defvar emms-mpv-event-functions nil +(defvar emms-player-mpv-event-functions nil "List of functions to call for each event emitted from JSON IPC. One argument is passed to each function - JSON line, as sent by mpv and decoded by `json-read-from-string'. -See also `emms-mpv-event-connect-hook'.") +See also `emms-player-mpv-event-connect-hook'.") -(defvar emms-mpv-stopped nil +(defvar emms-player-mpv-stopped nil "Non-nil if playback was stopped by call from emms. Similar to `emms-player-stopped-p', but set for future async events, to indicate that playback should stop instead of switching to next track.") -(defvar emms-mpv-idle-timer (timer-create) +(defvar emms-player-mpv-idle-timer (timer-create) "Timer to delay `emms-player-stopped' when mpv unexpectedly goes idle.") -(defvar emms-mpv-idle-delay 0.5 +(defvar emms-player-mpv-idle-delay 0.5 "Delay before issuing `emms-player-stopped' when mpv unexpectedly goes idle.") ;; ----- helpers -(defvar emms-mpv-debug nil +(defvar emms-player-mpv-debug nil "Enable to print sent/received JSON lines and process -start/stop events to *Messages* buffer using `emms-mpv-debug-msg'.") +start/stop events to *Messages* buffer using `emms-player-mpv-debug-msg'.") -(defvar emms-mpv-debug-ts-offset nil - "Timestamp offset for `emms-mpv-debug-msg'. +(defvar emms-player-mpv-debug-ts-offset nil + "Timestamp offset for `emms-player-mpv-debug-msg'. Set on first use, with intent to both shorten and obfuscate time in logs.") -(defun emms-mpv-debug-trim (s) +(defun emms-player-mpv-debug-trim (s) (if (stringp s) (replace-regexp-in-string "\\(^[ \t\n\r]+\\|[ \t\n\r]+$\\)" "" s t t) s)) -(defun emms-mpv-debug-msg (tpl-or-msg &rest tpl-values) - "Print debug message to *Messages* if `emms-mpv-debug' is non-nil. +(defun emms-player-mpv-debug-msg (tpl-or-msg &rest tpl-values) + "Print debug message to *Messages* if `emms-player-mpv-debug' is non-nil. Message is only formatted if TPL-VALUES is non-empty. Strips whitespace from start/end of TPL-OR-MSG and strings in TPL-VALUES." - (when emms-mpv-debug + (when emms-player-mpv-debug (setq - tpl-or-msg (emms-mpv-debug-trim tpl-or-msg) - tpl-values (seq-map 'emms-mpv-debug-trim tpl-values)) + tpl-or-msg (emms-player-mpv-debug-trim tpl-or-msg) + tpl-values (seq-map #'emms-player-mpv-debug-trim tpl-values)) (unless tpl-values (setq tpl-or-msg (replace-regexp-in-string "%" "%%" tpl-or-msg t t))) (let ((ts (float-time))) - (unless emms-mpv-debug-ts-offset (setq emms-mpv-debug-ts-offset ts)) + (unless emms-player-mpv-debug-ts-offset (setq emms-player-mpv-debug-ts-offset ts)) (apply 'message - (concat "emms-mpv %.1f " tpl-or-msg) - (- ts emms-mpv-debug-ts-offset) tpl-values)))) + (concat "emms-player-mpv %.1f " tpl-or-msg) + (- ts emms-player-mpv-debug-ts-offset) tpl-values)))) -(defun emms-mpv-ipc-fifo-p () +(defun emms-player-mpv-ipc-fifo-p () "Returns non-nil if --input-file fifo should be used. -Runs `emms-mpv-ipc-detect' to detect/set `emms-player-mpv-ipc-method' if necessary." +Runs `emms-player-mpv-ipc-detect' to detect/set `emms-player-mpv-ipc-method' if necessary." (unless emms-player-mpv-ipc-method (setq emms-player-mpv-ipc-method - (emms-mpv-ipc-detect emms-player-mpv-command-name))) + (emms-player-mpv-ipc-detect emms-player-mpv-command-name))) (eq emms-player-mpv-ipc-method 'file)) -(defun emms-mpv-ipc-detect (cmd) +(defun emms-player-mpv-ipc-detect (cmd) "Run mpv --version and return symbol for best IPC method supported. CMD should be either name of mpv binary to use or full path to it. Return values correspond to `emms-player-mpv-ipc-method' options. @@ -277,27 +291,27 @@ Error is signaled if mpv binary fails to run." ;; ----- mpv process -(defun emms-mpv-proc-playing-p (&optional proc) - "Return whether playback in PROC or `emms-mpv-proc' is started, +(defun emms-player-mpv-proc-playing-p (&optional proc) + "Return whether playback in PROC or `emms-player-mpv-proc' is started, which is distinct from 'start-command sent' and 'process is running' states. Used to signal emms via `emms-player-started' and `emms-player-stopped' calls." - (let ((proc (or proc emms-mpv-proc))) + (let ((proc (or proc emms-player-mpv-proc))) (if proc (process-get proc 'mpv-playing) nil))) -(defun emms-mpv-proc-playing (state &optional proc) - "Set process mpv-playing state flag for `emms-mpv-proc-playing-p'." - (let ((proc (or proc emms-mpv-proc))) +(defun emms-player-mpv-proc-playing (state &optional proc) + "Set process mpv-playing state flag for `emms-player-mpv-proc-playing-p'." + (let ((proc (or proc emms-player-mpv-proc))) (when proc (process-put proc 'mpv-playing state)))) -(defun emms-mpv-proc-symbol-id (sym &optional proc) +(defun emms-player-mpv-proc-symbol-id (sym &optional proc) "Get unique process-specific id integer for SYM or nil if it was already requested." (let - ((proc (or proc emms-mpv-proc)) + ((proc (or proc emms-player-mpv-proc)) (sym-id (intern (concat "mpv-sym-" (symbol-name sym))))) (unless (process-get proc sym-id) - (let ((id (emms-mpv-ipc-id-get))) (process-put proc sym-id id) id)))) + (let ((id (emms-player-mpv-ipc-id-get))) (process-put proc sym-id id) id)))) -(defun emms-mpv-proc-init-fifo (path &optional mode) +(defun emms-player-mpv-proc-init-fifo (path &optional mode) "Create named pipe (fifo) socket for mpv --input-file PATH, if not exists already. Optional MODE should be 12-bit octal integer, e.g. #o600 (safe default). Signals error if mkfifo exits with non-zero code." @@ -311,20 +325,20 @@ Signals error if mkfifo exits with non-zero code." (format "--mode=%o" (or mode #o600)) path)) (error (format "Failed to run mkfifo for mpv --input-file path: %s" path)))))) -(defun emms-mpv-proc-sentinel (proc ev) +(defun emms-player-mpv-proc-sentinel (proc ev) (let ((status (process-status proc)) - (playing (emms-mpv-proc-playing-p proc))) - (emms-mpv-debug-msg + (playing (emms-player-mpv-proc-playing-p proc))) + (emms-player-mpv-debug-msg "proc[%s]: %s (status=%s, playing=%s)" proc ev status playing) (when (and (memq status '(exit signal)) playing) (emms-player-stopped)))) -(defun emms-mpv-proc-init (&rest media-args) - "initialize new mpv process as `emms-mpv-proc'. +(defun emms-player-mpv-proc-init (&rest media-args) + "initialize new mpv process as `emms-player-mpv-proc'. MEDIA-ARGS are used instead of --idle, if specified." - (emms-mpv-proc-stop) - (when (emms-mpv-ipc-fifo-p) - (emms-mpv-proc-init-fifo emms-player-mpv-ipc-socket)) + (emms-player-mpv-proc-stop) + (when (emms-player-mpv-ipc-fifo-p) + (emms-player-mpv-proc-init-fifo emms-player-mpv-ipc-socket)) (let* ((argv emms-player-mpv-parameters) (argv (append @@ -336,40 +350,40 @@ MEDIA-ARGS are used instead of --idle, if specified." (env emms-player-mpv-environment) (process-environment (append (unless (seq-some 'not env) process-environment) (seq-filter 'identity env)))) - (setq emms-mpv-proc - (make-process :name "emms-mpv" - :buffer nil :command argv :noquery t :sentinel 'emms-mpv-proc-sentinel)) - (when (emms-mpv-ipc-fifo-p) (emms-mpv-proc-playing emms-mpv-proc)) - (emms-mpv-debug-msg "proc[%s]: start %s" emms-mpv-proc argv))) - -(defun emms-mpv-proc-stop () - "Stop running `emms-mpv-proc' instance via SIGINT, if any. -`delete-process' (SIGKILL) timer is started if `emms-mpv-proc-kill-delay' is non-nil." - (when emms-mpv-proc - (let ((proc emms-mpv-proc)) - (emms-mpv-debug-msg "proc[%s]: stop" proc) - (setq emms-mpv-proc nil) + (setq emms-player-mpv-proc + (make-process :name "emms-player-mpv" + :buffer nil :command argv :noquery t :sentinel #'emms-player-mpv-proc-sentinel)) + (when (emms-player-mpv-ipc-fifo-p) (emms-player-mpv-proc-playing t)) + (emms-player-mpv-debug-msg "proc[%s]: start %s" emms-player-mpv-proc argv))) + +(defun emms-player-mpv-proc-stop () + "Stop running `emms-player-mpv-proc' instance via SIGINT, if any. +`delete-process' (SIGKILL) timer is started if `emms-player-mpv-proc-kill-delay' is non-nil." + (when emms-player-mpv-proc + (let ((proc emms-player-mpv-proc)) + (emms-player-mpv-debug-msg "proc[%s]: stop" proc) + (setq emms-player-mpv-proc nil) (if (not (process-live-p proc)) (delete-process proc) - (emms-mpv-proc-playing nil proc) + (emms-player-mpv-proc-playing nil proc) (interrupt-process proc) - (when emms-mpv-proc-kill-delay + (when emms-player-mpv-proc-kill-delay (run-at-time - emms-mpv-proc-kill-delay nil + emms-player-mpv-proc-kill-delay nil (lambda (proc) (delete-process proc)) proc)))))) ;; ----- IPC socket/fifo -(defun emms-mpv-ipc-sentinel (proc ev) - (emms-mpv-debug-msg "ipc[%s]: %s" proc ev) +(defun emms-player-mpv-ipc-sentinel (proc ev) + (emms-player-mpv-debug-msg "ipc[%s]: %s" proc ev) (when (memq (process-status proc) '(open run)) - (run-hooks 'emms-mpv-event-connect-hook) - (when emms-mpv-ipc-connect-command - (let ((cmd emms-mpv-ipc-connect-command)) - (setq emms-mpv-ipc-connect-command nil) - (emms-mpv-ipc-req-send cmd nil proc))))) + (run-hooks 'emms-player-mpv-event-connect-hook) + (when emms-player-mpv-ipc-connect-command + (let ((cmd emms-player-mpv-ipc-connect-command)) + (setq emms-player-mpv-ipc-connect-command nil) + (emms-player-mpv-ipc-req-send cmd nil proc))))) -(defun emms-mpv-ipc-filter (proc s) +(defun emms-player-mpv-ipc-filter (proc s) (when (buffer-live-p (process-buffer proc)) (with-current-buffer (process-buffer proc) (let ((moving (= (point) (process-mark proc)))) @@ -388,199 +402,200 @@ MEDIA-ARGS are used instead of --idle, if specified." ((p1 (point)) (json (buffer-substring p0 p1))) (delete-region p0 (+ p1 1)) - (emms-mpv-ipc-recv json))))))) + (emms-player-mpv-ipc-recv json))))))) -(defun emms-mpv-ipc-connect (delays) +(defun emms-player-mpv-ipc-connect (delays) "Make IPC connection attempt, rescheduling if there's no socket by (car DELAYS). (cdr DELAYS) gets passed to next connection attempt, so it can be rescheduled further until function runs out of DELAYS values. -Sets `emms-mpv-ipc-proc' value to resulting process on success." - (emms-mpv-debug-msg "ipc: connect-delay %s" (car delays)) - (setq emms-mpv-ipc-proc +Sets `emms-player-mpv-ipc-proc' value to resulting process on success." + (emms-player-mpv-debug-msg "ipc: connect-delay %s" (car delays)) + (setq emms-player-mpv-ipc-proc (make-network-process ; returns nil if there's no socket yet - :name "emms-mpv-ipc" + :name "emms-player-mpv-ipc" :family 'local :service emms-player-mpv-ipc-socket :nowait t :coding '(utf-8 . utf-8) - :buffer (get-buffer-create emms-mpv-ipc-buffer) + :buffer (get-buffer-create emms-player-mpv-ipc-buffer) :noquery t - :filter 'emms-mpv-ipc-filter - :sentinel 'emms-mpv-ipc-sentinel)) - (when (and (not emms-mpv-ipc-proc) delays) - (run-at-time (car delays) nil 'emms-mpv-ipc-connect (cdr delays)))) + :filter #'emms-player-mpv-ipc-filter + :sentinel #'emms-player-mpv-ipc-sentinel)) + (when (and (not emms-player-mpv-ipc-proc) delays) + (run-at-time (car delays) nil #'emms-player-mpv-ipc-connect (cdr delays)))) -(defun emms-mpv-ipc-connect-fifo () - "Set `emms-mpv-ipc-proc' to process wrapper for +(defun emms-player-mpv-ipc-connect-fifo () + "Set `emms-player-mpv-ipc-proc' to process wrapper for writing to a named pipe (fifo) file/node or signal error." - (setq emms-mpv-ipc-proc - (start-process-shell-command "emms-mpv-input-file" nil + (setq emms-player-mpv-ipc-proc + (start-process-shell-command "emms-player-mpv-input-file" nil (format "cat > '%s'" (replace-regexp-in-string "'" "'\"'\"'" emms-player-mpv-ipc-socket t t)))) - (set-process-query-on-exit-flag emms-mpv-ipc-proc nil) - (unless emms-mpv-ipc-proc (error (format + (set-process-query-on-exit-flag emms-player-mpv-ipc-proc nil) + (unless emms-player-mpv-ipc-proc (error (format "Failed to start cat-pipe to fifo: %s" emms-player-mpv-ipc-socket))) - (when emms-mpv-ipc-connect-command - (let ((cmd emms-mpv-ipc-connect-command)) - (setq emms-mpv-ipc-connect-command nil) - (emms-mpv-ipc-fifo-cmd cmd emms-mpv-ipc-proc)))) + (when emms-player-mpv-ipc-connect-command + (let ((cmd emms-player-mpv-ipc-connect-command)) + (setq emms-player-mpv-ipc-connect-command nil) + (emms-player-mpv-ipc-fifo-cmd cmd emms-player-mpv-ipc-proc)))) -(defun emms-mpv-ipc-init () +(defun emms-player-mpv-ipc-init () "initialize new mpv ipc socket/file process and associated state." - (emms-mpv-ipc-stop) - (emms-mpv-debug-msg "ipc: init") - (if (emms-mpv-ipc-fifo-p) (emms-mpv-ipc-connect-fifo) - (when emms-mpv-ipc-connect-timer (cancel-timer emms-mpv-ipc-connect-timer)) - (with-current-buffer (get-buffer-create emms-mpv-ipc-buffer) (erase-buffer)) + (emms-player-mpv-ipc-stop) + (emms-player-mpv-debug-msg "ipc: init") + (if (emms-player-mpv-ipc-fifo-p) (emms-player-mpv-ipc-connect-fifo) + (when emms-player-mpv-ipc-connect-timer (cancel-timer emms-player-mpv-ipc-connect-timer)) + (with-current-buffer (get-buffer-create emms-player-mpv-ipc-buffer) (erase-buffer)) (setq - emms-mpv-ipc-id 1 - emms-mpv-ipc-req-table nil - emms-mpv-ipc-connect-timer nil - emms-mpv-ipc-connect-timer - (run-at-time (car emms-mpv-ipc-connect-delays) - nil 'emms-mpv-ipc-connect (cdr emms-mpv-ipc-connect-delays))))) - -(defun emms-mpv-ipc-stop () - (when emms-mpv-ipc-proc - (emms-mpv-debug-msg "ipc: stop") - (delete-process emms-mpv-ipc-proc) - (setq emms-mpv-ipc-proc nil))) - -(defun emms-mpv-ipc () + emms-player-mpv-ipc-id 1 + emms-player-mpv-ipc-req-table nil + emms-player-mpv-ipc-connect-timer nil + emms-player-mpv-ipc-connect-timer + (run-at-time (car emms-player-mpv-ipc-connect-delays) nil + #'emms-player-mpv-ipc-connect (cdr emms-player-mpv-ipc-connect-delays))))) + +(defun emms-player-mpv-ipc-stop () + (when emms-player-mpv-ipc-proc + (emms-player-mpv-debug-msg "ipc: stop") + (delete-process emms-player-mpv-ipc-proc) + (setq emms-player-mpv-ipc-proc nil))) + +(defun emms-player-mpv-ipc () "Returns open ipc socket/fifo process or nil, (re-)starting mpv/connection if necessary. Will return nil when starting async process/connection, and any follow-up -command should be stored to `emms-mpv-ipc-connect-command' in this case." +command should be stored to `emms-player-mpv-ipc-connect-command' in this case." (unless ;; Don't start idle processes for fifo - just ignore all ipc requests there - (and (not (process-live-p emms-mpv-proc)) (emms-mpv-ipc-fifo-p)) - (unless (process-live-p emms-mpv-proc) (emms-mpv-proc-init)) - (unless (process-live-p emms-mpv-ipc-proc) (emms-mpv-ipc-init)) + (and (not (process-live-p emms-player-mpv-proc)) (emms-player-mpv-ipc-fifo-p)) + (unless (process-live-p emms-player-mpv-proc) (emms-player-mpv-proc-init)) + (unless (process-live-p emms-player-mpv-ipc-proc) (emms-player-mpv-ipc-init)) (and - emms-mpv-ipc-proc - (memq (process-status emms-mpv-ipc-proc) '(open run)) - emms-mpv-ipc-proc))) + emms-player-mpv-ipc-proc + (memq (process-status emms-player-mpv-ipc-proc) '(open run)) + emms-player-mpv-ipc-proc))) ;; ----- IPC protocol -(defun emms-mpv-ipc-id-get () - "Get new connection-unique id value, tracked via `emms-mpv-ipc-id'." - (let ((ipc-id emms-mpv-ipc-id)) - (setq emms-mpv-ipc-id - (if (< emms-mpv-ipc-id emms-mpv-ipc-id-max) (1+ emms-mpv-ipc-id) 1)) +(defun emms-player-mpv-ipc-id-get () + "Get new connection-unique id value, tracked via `emms-player-mpv-ipc-id'." + (let ((ipc-id emms-player-mpv-ipc-id)) + (setq emms-player-mpv-ipc-id + (if (< emms-player-mpv-ipc-id emms-player-mpv-ipc-id-max) (1+ emms-player-mpv-ipc-id) 1)) ipc-id)) -(defun emms-mpv-ipc-req-send (cmd &optional handler proc) +(defun emms-player-mpv-ipc-req-send (cmd &optional handler proc) "Send JSON IPC request and assign HANDLER to response for it, if any. CMD value is encoded via `json-encode'. HANDLER func will be called with decoded response JSON as (handler data err), where ERR will be either nil on \"success\", 'connection-error or whatever is in JSON. -If HANDLER is nil, default `emms-mpv-ipc-req-error-printer' will be used to at least log errors. -PROC can be specified to avoid `emms-mpv-ipc' call (e.g. from sentinel/filter funcs)." +If HANDLER is nil, default `emms-player-mpv-ipc-req-error-printer' +will be used to at least log errors. +PROC can be specified to avoid `emms-player-mpv-ipc' call (e.g. from sentinel/filter funcs)." (let - ((req-id (emms-mpv-ipc-id-get)) - (req-proc (or proc (emms-mpv-ipc))) - (handler (or handler 'emms-mpv-ipc-req-error-printer))) - (unless emms-mpv-ipc-req-table - (setq emms-mpv-ipc-req-table (make-hash-table))) + ((req-id (emms-player-mpv-ipc-id-get)) + (req-proc (or proc (emms-player-mpv-ipc))) + (handler (or handler #'emms-player-mpv-ipc-req-error-printer))) + (unless emms-player-mpv-ipc-req-table + (setq emms-player-mpv-ipc-req-table (make-hash-table))) (let ((json (concat (json-encode (list :command cmd :request_id req-id)) "\n"))) - (emms-mpv-debug-msg "json >> %s" json) + (emms-player-mpv-debug-msg "json >> %s" json) (condition-case err (process-send-string req-proc json) ; can disconnect at any time (error - (emms-mpv-proc-stop) ; assume that mpv process is to blame and force restart + (emms-player-mpv-proc-stop) ; assume that mpv process is to blame and force restart (funcall handler nil 'connection-error) (setq handler nil)))) - (when handler (puthash req-id handler emms-mpv-ipc-req-table)))) + (when handler (puthash req-id handler emms-player-mpv-ipc-req-table)))) -(defun emms-mpv-ipc-req-resolve (req-id data err) +(defun emms-player-mpv-ipc-req-resolve (req-id data err) "Run handler-func for specified req-id." - (when emms-mpv-ipc-req-table + (when emms-player-mpv-ipc-req-table (let - ((handler (gethash req-id emms-mpv-ipc-req-table)) + ((handler (gethash req-id emms-player-mpv-ipc-req-table)) (err (if (string= err "success") nil err))) - (remhash req-id emms-mpv-ipc-req-table) + (remhash req-id emms-player-mpv-ipc-req-table) (when handler (funcall handler data err))))) -(defun emms-mpv-ipc-req-error-printer (data err) - "Simple default `emms-mpv-ipc-req-send' handler to log errors, if any." - (when err (message "emms-mpv-ipc-error: %s" err))) +(defun emms-player-mpv-ipc-req-error-printer (data err) + "Simple default `emms-player-mpv-ipc-req-send' handler to log errors, if any." + (when err (message "emms-player-mpv ipc-error: %s" err))) -(defun emms-mpv-ipc-recv (json) +(defun emms-player-mpv-ipc-recv (json) "Handler for all JSON lines from mpv process. Only used with JSON IPC, never called with --input-file as there's no feedback there." - (emms-mpv-debug-msg "json << %s" json) + (emms-player-mpv-debug-msg "json << %s" json) (let* ((json-data (json-read-from-string json)) (req-id (alist-get 'request_id json-data)) (ev (alist-get 'event json-data))) (when req-id ; response to command - (emms-mpv-ipc-req-resolve req-id + (emms-player-mpv-ipc-req-resolve req-id (alist-get 'data json-data) (alist-get 'error json-data))) (when ev ; mpv event - (emms-mpv-event-handler json-data) - (run-hook-with-args 'emms-mpv-event-functions json-data)))) + (emms-player-mpv-event-handler json-data) + (run-hook-with-args 'emms-player-mpv-event-functions json-data)))) -(defun emms-mpv-ipc-fifo-cmd (cmd &optional proc) +(defun emms-player-mpv-ipc-fifo-cmd (cmd &optional proc) "Send --input-file command string for older mpv versions. -PROC can be specified to avoid `emms-mpv-ipc' call." +PROC can be specified to avoid `emms-player-mpv-ipc' call." (let - ((proc (or proc (emms-mpv-ipc))) + ((proc (or proc (emms-player-mpv-ipc))) (cmd-line (concat (mapconcat (lambda (s) (format "%s" s)) cmd " ") "\n"))) - (emms-mpv-debug-msg "fifo >> %s" cmd-line) + (emms-player-mpv-debug-msg "fifo >> %s" cmd-line) (process-send-string proc cmd-line))) -(defun emms-mpv-observe-property (sym) +(defun emms-player-mpv-observe-property (sym) "Send mpv observe_property command for property identified by SYM. Only sends command once per process, removing any potential duplication if used for same properties from different functions." - (let ((id (emms-mpv-proc-symbol-id sym))) - (when id (emms-mpv-ipc-req-send `(observe_property ,id ,sym))))) + (let ((id (emms-player-mpv-proc-symbol-id sym))) + (when id (emms-player-mpv-ipc-req-send `(observe_property ,id ,sym))))) -(defun emms-mpv-event-idle () +(defun emms-player-mpv-event-idle () "Delayed check for switching tracks when mpv goes idle for no good reason." - (emms-mpv-debug-msg "idle-check (stopped=%s)" emms-mpv-stopped) - (unless emms-mpv-stopped (emms-player-stopped))) + (emms-player-mpv-debug-msg "idle-check (stopped=%s)" emms-player-mpv-stopped) + (unless emms-player-mpv-stopped (emms-player-stopped))) -(defun emms-mpv-event-handler (json-data) +(defun emms-player-mpv-event-handler (json-data) "Handler for supported mpv events, including property changes. -Called before `emms-mpv-event-functions' and does same thing as these hooks." +Called before `emms-player-mpv-event-functions' and does same thing as these hooks." (pcase (alist-get 'event json-data) ("playback-restart" - ;; Separate emms-mpv-proc-playing state is used for emms started/stopped signals, - ;; because start-file/end-file are also emitted after track-change and for playlists, - ;; and don't correspond to actual playback state. - (unless (emms-mpv-proc-playing-p) - (emms-mpv-proc-playing t) + ;; Separate emms-player-mpv-proc-playing state is used for emms started/stopped signals, + ;; because start-file/end-file are also emitted after track-change and for playlists, + ;; and don't correspond to actual playback state. + (unless (emms-player-mpv-proc-playing-p) + (emms-player-mpv-proc-playing t) (emms-player-started emms-player-mpv))) ("end-file" - (when (emms-mpv-proc-playing-p) - (emms-mpv-proc-playing nil) + (when (emms-player-mpv-proc-playing-p) + (emms-player-mpv-proc-playing nil) (emms-player-stopped))) ("idle" ;; Can mean any kind of error before or during playback. ;; Example can be access/format error, resulting in start+end without playback-restart. - (cancel-timer emms-mpv-idle-timer) - (setq emms-mpv-idle-timer - (run-at-time emms-mpv-idle-delay nil 'emms-mpv-event-idle))) - ("start-file" (cancel-timer emms-mpv-idle-timer)))) + (cancel-timer emms-player-mpv-idle-timer) + (setq emms-player-mpv-idle-timer + (run-at-time emms-player-mpv-idle-delay nil #'emms-player-mpv-event-idle))) + ("start-file" (cancel-timer emms-player-mpv-idle-timer)))) ;; ----- Metadata update hooks -(defun emms-mpv-info-meta-connect-func () - "Hook function for `emms-mpv-event-connect-hook' to update metadata from mpv." - (emms-mpv-observe-property 'metadata)) +(defun emms-player-mpv-info-meta-connect-func () + "Hook function for `emms-player-mpv-event-connect-hook' to update metadata from mpv." + (emms-player-mpv-observe-property 'metadata)) -(defun emms-mpv-info-meta-event-func (json-data) - "Hook function for `emms-mpv-event-functions' to update metadata from mpv." +(defun emms-player-mpv-info-meta-event-func (json-data) + "Hook function for `emms-player-mpv-event-functions' to update metadata from mpv." (when (and (string= (alist-get 'event json-data) "property-change") (string= (alist-get 'name json-data) "metadata")) (let ((info-alist (alist-get 'data json-data))) - (when info-alist (emms-mpv-info-meta-update-track info-alist))))) + (when info-alist (emms-player-mpv-info-meta-update-track info-alist))))) -(defun emms-mpv-info-meta-update-track (info-alist &optional track) +(defun emms-player-mpv-info-meta-update-track (info-alist &optional track) "Update TRACK with mpv metadata from INFO-ALIST. `emms-playlist-current-selected-track' is used by default." (mapc @@ -603,26 +618,26 @@ Called before `emms-mpv-event-functions' and does same thing as these hooks." note (key comment)) (emms-track-updated track))) -(defun emms-mpv-info-duration-event-func (json-data) - "Hook function for `emms-mpv-event-functions' to update track duration from mpv." +(defun emms-player-mpv-info-duration-event-func (json-data) + "Hook function for `emms-player-mpv-event-functions' to update track duration from mpv." (when (string= (alist-get 'event json-data) "playback-restart") - (emms-mpv-info-duration-check))) + (emms-player-mpv-info-duration-check))) -(defun emms-mpv-info-duration-check () +(defun emms-player-mpv-info-duration-check () "Check whether current mpv track has reliable duration info and request it." - (emms-mpv-ipc-req-send '(get_property stream-end) (lambda (pts-end err) + (emms-player-mpv-ipc-req-send '(get_property stream-end) (lambda (pts-end err) (if err (unless (and (stringp err) (string= err "property unavailable")) - (emms-mpv-ipc-req-error-printer pts-end err)) + (emms-player-mpv-ipc-req-error-printer pts-end err)) (when pts-end - (emms-mpv-ipc-req-send '(get_property duration) - 'emms-mpv-info-duration-handler)))))) + (emms-player-mpv-ipc-req-send '(get_property duration) + #'emms-player-mpv-info-duration-handler)))))) -(defun emms-mpv-info-duration-handler (duration err) +(defun emms-player-mpv-info-duration-handler (duration err) "Duration property request handler to update it for current emms track." (if err - (emms-mpv-debug-msg "duration-req-error: %s" err) + (emms-player-mpv-debug-msg "duration-req-error: %s" err) (when (and (numberp duration) (> duration 0)) ; can be nil/0 for network streams (let ((duration (round duration)) @@ -637,19 +652,19 @@ Called before `emms-mpv-event-functions' and does same thing as these hooks." (defun emms-player-mpv-cmd (cmd &optional handler) "Send mpv command to process/connection if both are running, or otherwise schedule start/connect and set -`emms-mpv-ipc-start-track' for `emms-mpv-ipc-sentinel'. -PROC can be specified to avoid `emms-mpv-ipc' call." - (setq emms-mpv-ipc-connect-command nil) - (let ((proc (emms-mpv-ipc))) +`emms-player-mpv-ipc-start-track' for `emms-player-mpv-ipc-sentinel'. +PROC can be specified to avoid `emms-player-mpv-ipc' call." + (setq emms-player-mpv-ipc-connect-command nil) + (let ((proc (emms-player-mpv-ipc))) (if proc - (if (emms-mpv-ipc-fifo-p) - (emms-mpv-ipc-fifo-cmd cmd proc) - (emms-mpv-ipc-req-send cmd handler proc)) - (setq emms-mpv-ipc-connect-command cmd)))) + (if (emms-player-mpv-ipc-fifo-p) + (emms-player-mpv-ipc-fifo-cmd cmd proc) + (emms-player-mpv-ipc-req-send cmd handler proc)) + (setq emms-player-mpv-ipc-connect-command cmd)))) (defmacro emms-player-mpv-cmd-prog (cmd &rest handler-body) "Macro around `emms-player-mpv-cmd' that creates -handler callback (see `emms-mpv-ipc-req-send') from HANDLER-BODY forms, +handler callback (see `emms-player-mpv-ipc-req-send') from HANDLER-BODY forms, which have following bindings: - mpv-cmd for CMD. - mpv-data for response data (decoded json, nil if none). @@ -662,15 +677,15 @@ which have following bindings: (memq (emms-track-type track) '(file url streamlist playlist))) (defun emms-player-mpv-start (track) - (setq emms-mpv-stopped nil) - (emms-mpv-proc-playing nil) + (setq emms-player-mpv-stopped nil) + (emms-player-mpv-proc-playing nil) (let ((track-name (emms-track-get track 'name)) (track-is-playlist (memq (emms-track-get track 'type) '(streamlist playlist)))) - (if (emms-mpv-ipc-fifo-p) + (if (emms-player-mpv-ipc-fifo-p) (progn - (emms-mpv-ipc-stop) ; to clear any buffered commands - (emms-mpv-proc-init (if track-is-playlist "--playlist" "--") track-name) + (emms-player-mpv-ipc-stop) ; to clear any buffered commands + (emms-player-mpv-proc-init (if track-is-playlist "--playlist" "--") track-name) (emms-player-started emms-player-mpv)) (emms-player-mpv-cmd-prog (list (if track-is-playlist 'loadlist 'loadfile) track-name 'replace) @@ -682,8 +697,8 @@ which have following bindings: (emms-player-mpv-cmd `(set pause no))))))) (defun emms-player-mpv-stop () - (setq emms-mpv-stopped t) - (emms-mpv-proc-playing nil) + (setq emms-player-mpv-stopped t) + (emms-player-mpv-proc-playing nil) (emms-player-mpv-cmd `(stop)) (emms-player-stopped)) @@ -700,10 +715,10 @@ which have following bindings: (defun emms-player-mpv-seek-to (sec) (emms-player-mpv-cmd `(seek ,sec absolute))) -(emms-player-set emms-player-mpv 'pause 'emms-player-mpv-pause) -(emms-player-set emms-player-mpv 'resume 'emms-player-mpv-resume) -(emms-player-set emms-player-mpv 'seek 'emms-player-mpv-seek) -(emms-player-set emms-player-mpv 'seek-to 'emms-player-mpv-seek-to) +(emms-player-set emms-player-mpv 'pause #'emms-player-mpv-pause) +(emms-player-set emms-player-mpv 'resume #'emms-player-mpv-resume) +(emms-player-set emms-player-mpv 'seek #'emms-player-mpv-seek) +(emms-player-set emms-player-mpv 'seek-to #'emms-player-mpv-seek-to) (provide 'emms-player-mpv) -- cgit v1.2.3