[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/listen ffb0a91371 01/12: Add: (listen-mpv) MPV support
From: |
ELPA Syncer |
Subject: |
[elpa] externals/listen ffb0a91371 01/12: Add: (listen-mpv) MPV support |
Date: |
Sun, 18 Aug 2024 06:58:36 -0400 (EDT) |
branch: externals/listen
commit ffb0a91371beed804db67e168320859aac8ea81d
Author: Adam Porter <adam@alphapapa.net>
Commit: Adam Porter <adam@alphapapa.net>
Add: (listen-mpv) MPV support
Seems to work well so far.
---
README.org | 2 +
docs/README.org | 2 +
listen-lib.el | 4 +-
listen-mpv.el | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
listen.el | 10 ++++
5 files changed, 183 insertions(+), 3 deletions(-)
diff --git a/README.org b/README.org
index d742680b8d..d1df928a03 100644
--- a/README.org
+++ b/README.org
@@ -226,6 +226,8 @@ The ~listen-mode~ minor mode runs a timer which plays the
next track in the curr
** v0.10-pre
*Additions*
+- [[https://mpv.io/][MPV]] support.
+- Option ~listen-backend~, which sets the backend to use: MPV or VLC. (The
default is to auto-detect which is available at load time, with MPV being
preferred due to more robust IPC support.)
- Faces for parts of mode line lighter.
*Fixes*
diff --git a/docs/README.org b/docs/README.org
index 63db70a7fb..398978af09 100644
--- a/docs/README.org
+++ b/docs/README.org
@@ -238,6 +238,8 @@ The ~listen-mode~ minor mode runs a timer which plays the
next track in the curr
** v0.10-pre
*Additions*
++ [[https://mpv.io/][MPV]] support.
++ Option ~listen-backend~, which sets the backend to use: MPV or VLC. (The
default is to auto-detect which is available at load time, with MPV being
preferred due to more robust IPC support.)
+ Faces for parts of mode line lighter.
*Fixes*
diff --git a/listen-lib.el b/listen-lib.el
index 600afc5056..7287e774f4 100644
--- a/listen-lib.el
+++ b/listen-lib.el
@@ -139,12 +139,10 @@ return a list of values; otherwise return the sole value."
;;;; Functions
-;; FIXME: Declare this differently or something.
-(declare-function make-listen-player-vlc "listen-vlc")
(defun listen-current-player ()
"Return variable `listen-player' or a newly set one if nil."
(or listen-player
- (setf listen-player (make-listen-player-vlc))))
+ (setf listen-player (funcall (defvar listen-backend)))))
(cl-defun listen-current-track (&optional (player listen-player))
"Return track playing on PLAYER, if any."
diff --git a/listen-mpv.el b/listen-mpv.el
new file mode 100755
index 0000000000..ecdb7c0638
--- /dev/null
+++ b/listen-mpv.el
@@ -0,0 +1,168 @@
+;;; listen-mpv.el --- MPV support for Emacs Music Player
-*- lexical-binding: t; -*-
+
+;; Copyright (C) 2024 Free Software Foundation, Inc.
+
+;; Author: Adam Porter <adam@alphapapa.net>
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;
+
+;;; Code:
+
+;;;; Requirements
+
+(require 'cl-lib)
+(require 'map)
+
+(require 'listen-lib)
+
+;;;; Types
+
+(cl-defstruct
+ (listen-player-mpv
+ (:include listen-player
+ (command "mpv")
+ (args '("--no-msg-color" "--idle"))
+ (max-volume 100)
+ (etc '((:request-id . 0))))))
+
+;;;; Functions
+
+(cl-defmethod listen--info ((player listen-player-mpv))
+ (map-apply (lambda (key value)
+ ;; TODO: Consider using symbols as keys (VLC returns strings,
MPV's decodes as
+ ;; symbols).
+ (cons (downcase (symbol-name key)) value))
+ (listen-mpv--get-property player "metadata")))
+
+(cl-defmethod listen--filename ((player listen-player-mpv))
+ "Return filename of PLAYER's current track."
+ (let ((status (listen-mpv--get-property player "path" )))
+ (when (string-match (rx bol "( new input: file://" (group (1+ nonl)) " )"
) status)
+ (match-string 1 status))))
+
+(cl-defmethod listen--title ((player listen-player-mpv))
+ (listen-mpv--get-property player "media-title" ))
+
+(cl-defmethod listen--ensure ((player listen-player-mpv))
+ "Ensure PLAYER is ready."
+ (pcase-let* (((cl-struct listen-player command args process) player)
+ (socket (make-temp-name (expand-file-name "listen-mpv-socket-"
temporary-file-directory)))
+ (args (append args (list (format "--input-ipc-server=%s"
socket)))))
+ (unless (process-live-p process)
+ (setf (listen-player-process player)
+ (apply #'start-process "listen-player-mpv" (generate-new-buffer "
*listen-player-mpv*")
+ command args))
+ (sleep-for 1)
+ (setf (map-elt (listen-player-etc player) :network-process)
+ (make-network-process :name "listen-player-mpv-socket" :family
'local
+ :remote socket
+ :buffer (generate-new-buffer "
*listen-player-mpv-socket*")))
+ (set-process-query-on-exit-flag (listen-player-process player) nil))))
+
+(cl-defmethod listen--play ((player listen-player-mpv) file)
+ "Play FILE with PLAYER.
+Stops playing, clears playlist, adds FILE, and plays it."
+ (listen--send player "loadfile" (expand-file-name file)))
+
+;; (cl-defmethod listen--stop ((player listen-player-mpv))
+;; "Stop playing with PLAYER."
+;; (listen--send player "stop"))
+
+(cl-defmethod listen--status ((player listen-player-mpv))
+ (if (and (listen--playing-p player)
+ (not (listen-mpv--get-property listen-player "pause")))
+ "playing"
+ ;; TODO: Consider using "eof-reached" proeprty.
+ (if (listen-mpv--get-property listen-player "pause")
+ "paused"
+ "stopped")))
+
+(cl-defmethod listen--pause ((player listen-player-mpv))
+ "Pause playing with PLAYER."
+ (if (listen-mpv--get-property listen-player "pause")
+ (listen-mpv--set-property listen-player "pause" "no")
+ (listen-mpv--set-property listen-player "pause" "yes")))
+
+(cl-defmethod listen--playing-p ((player listen-player-mpv))
+ "Return non-nil if PLAYER is playing."
+ (not (listen-mpv--get-property player "idle-active")))
+
+(cl-defmethod listen--elapsed ((player listen-player-mpv))
+ "Return seconds elapsed for PLAYER's track."
+ (listen-mpv--get-property player "time-pos"))
+
+(cl-defmethod listen--length ((player listen-player-mpv))
+ "Return length of PLAYER's track in seconds."
+ (listen-mpv--get-property player "duration"))
+
+(require 'json)
+
+(cl-defmethod listen--send ((player listen-player-mpv) command &rest args)
+ "Send COMMAND to PLAYER and return output."
+ (listen--ensure player)
+ (pcase-let* (((cl-struct listen-player (etc (map :network-process))) player)
+ (request-id (cl-incf (map-elt (listen-player-etc player)
:request-id))))
+ (with-current-buffer (process-buffer network-process)
+ (let ((pos (marker-position (process-mark network-process)))
+ (json (json-encode
+ `(("command" ,command ,@args)
+ ("request_id" . ,request-id)))))
+ (process-send-string network-process json)
+ (process-send-string network-process "\n")
+ (with-local-quit
+ (accept-process-output network-process 2))
+ ;; TODO: Consider using `json-read', etc.
+ (goto-char (point-min))
+ (let ((json-false nil))
+ (prog1 (cl-loop for result = (json-read)
+ while result
+ when (equal request-id (map-elt result 'request_id))
+ return result)
+ (unless listen-debug-p
+ (erase-buffer))))))))
+
+(cl-defmethod listen--seek ((player listen-player-mpv) seconds)
+ "Seek PLAYER to SECONDS."
+ (listen--send player "seek" seconds "absolute"))
+
+(cl-defmethod listen--volume ((player listen-player-mpv) &optional volume)
+ "Return or set PLAYER's VOLUME.
+VOLUME is an integer percentage."
+ (pcase-let (((cl-struct listen-player max-volume) player))
+ (if volume
+ (progn
+ (unless (<= 0 volume max-volume)
+ (error "VOLUME must be 0-%s" max-volume))
+ (listen-mpv--set-property player "volume" volume))
+ (listen-mpv--get-property player "volume"))))
+
+(cl-defmethod listen-mpv--get-property ((player listen-player-mpv) property)
+ (pcase-let (((map error data) (listen--send player "get_property" property)))
+ (pcase error
+ ("success" data)
+ (_ (error "listen-mpv--get-property: Getting property %S failed: %S"
property error)))))
+
+(cl-defmethod listen-mpv--set-property ((player listen-player-mpv) property
&rest args)
+ (pcase-let (((map error data) (apply #'listen--send player "set_property"
property args)))
+ (pcase error
+ ("success" data)
+ (_ (error "listen-mpv--set-property: Setting property %S failed: %S"
property error)))))
+
+(provide 'listen-mpv)
+
+;;; listen-mpv.el ends here
diff --git a/listen.el b/listen.el
index 8b16f24941..e21f769f0b 100755
--- a/listen.el
+++ b/listen.el
@@ -60,6 +60,8 @@
(require 'map)
(require 'listen-lib)
+;; TODO: Can we load these as-needed?
+(require 'listen-mpv)
(require 'listen-vlc)
;;;; Variables
@@ -119,6 +121,14 @@ Called with one argument, the player (if the player has a
queue,
its current track will be the one that just finished playing)."
:type 'hook)
+(defcustom listen-backend
+ (cond ((executable-find "mpv") #'make-listen-player-mpv)
+ ((executable-find "vlc") #'make-listen-player-vlc)
+ (t (display-warning 'listen-backend "Unable to find MPV or VLC."
:error)))
+ "Player backend."
+ :type '(choice (const :tag "MPV" make-listen-player-mpv)
+ (const :tag "VLC" make-listen-player-vlc)))
+
;;;; Commands
(defun listen-quit (player)
- [elpa] externals/listen updated (d97f308f34 -> c5b7f0b524), ELPA Syncer, 2024/08/18
- [elpa] externals/listen 40adfdeffa 03/12: Fix: (listen--status MPV) Variable, ELPA Syncer, 2024/08/18
- [elpa] externals/listen d75248f11c 06/12: Tidy: Compilation warning, ELPA Syncer, 2024/08/18
- [elpa] externals/listen eedac8822b 08/12: Docs: Clarify, ELPA Syncer, 2024/08/18
- [elpa] externals/listen ffb0a91371 01/12: Add: (listen-mpv) MPV support,
ELPA Syncer <=
- [elpa] externals/listen f9d95497cd 04/12: Tidy: (listen--send MPV), ELPA Syncer, 2024/08/18
- [elpa] externals/listen c5b7f0b524 12/12: Docs: Mention MPV-MPRIS, ELPA Syncer, 2024/08/18
- [elpa] externals/listen 193e04b3aa 02/12: Merge: MPV support, ELPA Syncer, 2024/08/18
- [elpa] externals/listen 9d64fd574b 09/12: Docs: Add ELPA-devel badge, ELPA Syncer, 2024/08/18
- [elpa] externals/listen 748a148188 11/12: Fix: (listen-current-player), ELPA Syncer, 2024/08/18
- [elpa] externals/listen 35e6b832e5 05/12: Tidy: Indentation, ELPA Syncer, 2024/08/18
- [elpa] externals/listen be91375f7e 10/12: Docs: Update another player mention, ELPA Syncer, 2024/08/18
- [elpa] externals/listen 94c0432a21 07/12: Docs: Update player mentions, ELPA Syncer, 2024/08/18