emms-player-simple-mpv-20180316.1549/ 0000755 0000000 0000000 00000000000 13252763631 015101 5 ustar 00 0000000 0000000 emms-player-simple-mpv-20180316.1549/emms-player-simple-mpv-control-functions.el 0000644 0000000 0000000 00000043546 13252763631 025445 0 ustar 00 0000000 0000000 ;;; emms-player-simple-mpv-control-functions.el --- functions to control mpv via emms-player-simple-mpv.el -*- lexical-binding: t -*-
;; Copyright (C) 2015-2017 momomo5717
;; Author: momomo5717
;; URL: https://github.com/momomo5717/emms-player-simple-mpv
;; 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 .
;;; Commentary:
;; This provides functions to control mpv via emms-player-simple-mpv.el.
;;; Code:
(require 'emms-player-simple-mpv)
;;;###autoload
(defun emms-player-simple-mpv-cycle (property)
"Cycle PROPERTY."
(emms-player-simple-mpv-tq-enqueue
(list "cycle" property)
property
(lambda (property ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(emms-player-simple-mpv-tq-enqueue
(list "get_property_string" property)
nil
(emms-player-simple-mpv-tq-data-message
(concat "mpv " property " : %s")))
(message "mpv %s : error" property)))))
;;;###autoload
(defun emms-player-simple-mpv-seek-to-% (per)
"Seek to PER(percent position)."
(interactive "nmpv seek to (%%) : ")
(setq per (cond ((< per 0) 0) ((> per 100) 100) (t per)))
(emms-player-simple-mpv-tq-enqueue
'("get_property" "duration")
per
(lambda (per ans-ls)
(let ((successp (emms-player-simple-mpv-tq-success-p ans-ls) )
(data (emms-player-simple-mpv-tq-assq-v 'data ans-ls)))
(if (and successp (numberp data) (> data 0.0))
(let* ((total-time (emms-player-simple-mpv--time-string data))
(pos (floor (* per data) 100))
(time (emms-player-simple-mpv--time-string pos)))
(emms-player-simple-mpv-tq-enqueue
(list "seek" per "absolute-percent")
(format "mpv seek to (%%%%) : %.1f (%s / %s)" per time total-time)
(lambda (form ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(message form)
(message "mpv seek to (%%) : error")))))
(message "mpv seek to (%%) : error"))))))
;;;###autoload
(defun emms-player-simple-mpv-volume-to (v)
"Set volume to V."
(interactive "nmpv volume to : ")
(emms-player-simple-mpv-set_property "volume" v))
;;;###autoload
(defun emms-player-simple-mpv-mute-on ()
"Mute on."
(emms-player-simple-mpv-set_property_string
"mute" "yes" :spec "success" :msg "mute on" :err-msg "mute on"))
;;;###autoload
(defun emms-player-simple-mpv-mute-off ()
"Mute off."
(emms-player-simple-mpv-set_property_string
"mute" "no" :spec "success" :msg "mute off" :err-msg "mute off"))
;;;###autoload
(defun emms-player-simple-mpv-mute ()
"Cycle mute."
(interactive)
(emms-player-simple-mpv-cycle "mute"))
;;;###autoload
(defun emms-player-simple-mpv-time-pos ()
"Display position in current file."
(interactive)
(emms-player-simple-mpv-tq-enqueue
'("get_property" "time-pos")
nil
(emms-player-simple-mpv-tq-data-message
"mpv time position : %s" :err-form "mpv time position : error"
:fn #'emms-player-simple-mpv--time-string)))
(defun emms-player-simple-mpv-time-pos-%-1 (form duration)
"Helper function for `emms-player-simple-mpv-time-pos-%'."
(emms-player-simple-mpv-tq-enqueue
'("get_property" "percent-pos")
nil
(emms-player-simple-mpv-tq-data-message
"%s" :err-form "mpv time position (%%) : error"
:fn (lambda (data)
(format form data (emms-player-simple-mpv--time-string (/ (* data duration) 100.0)))))))
;;;###autoload
(defun emms-player-simple-mpv-time-pos-% ()
"Display position (0-100) in current file."
(interactive)
(emms-player-simple-mpv-tq-enqueue
'("get_property" "duration")
nil
(lambda (_ ans-ls)
(let ((successp (emms-player-simple-mpv-tq-success-p ans-ls))
(data (emms-player-simple-mpv-tq-assq-v 'data ans-ls)))
(if (and successp (numberp data) (> data 0.0))
(let* ((form "mpv time position (%%%%) : %%.1f (%%s / %s)")
(form (format form (emms-player-simple-mpv--time-string data))))
(emms-player-simple-mpv-time-pos-%-1 form data))
(message "mpv time position (%%) : error"))))))
(defmacro emms-player-simple-mpv--playlist-change-1 (str)
"Helper macro for emms-player-simple-mpv--playlist-next/prev."
(let ((n (if (string= str "next") 1 -1)))
`(emms-player-simple-mpv-tq-enqueue
'("get_property" "playlist-pos") nil
(lambda (_ ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(let* ((data (emms-player-simple-mpv-tq-assq-v 'data ans-ls))
(form (format "mpv playlist_%s position %s : %%s"
,str (+ data ,n))))
(emms-player-simple-mpv-tq-enqueue
'(,(format "playlist_%s" str)) nil
(emms-player-simple-mpv-tq-error-message form)))
(message ,(format "mpv playlist_%s : error" str)))))))
;;;###autoload
(defun emms-player-simple-mpv-playlist-next ()
"Go to the next entry on the playlist."
(interactive)
(emms-player-simple-mpv--playlist-change-1 "next"))
;;;###autoload
(defun emms-player-simple-mpv-playlist-prev ()
"Go to the previous entry on the playlist."
(interactive)
(emms-player-simple-mpv--playlist-change-1 "prev"))
(defun emms-player-simple-mpv--playlist-to-1 (n)
"Helper function for `emms-player-simple-mpv-playlist-to'.
Set playlist-pos to N."
(emms-player-simple-mpv-set_property
"playlist-pos" n :msg "playlist position" :err-msg "playlist to"))
(defun emms-player-simple-mpv--playlist-to-2 ()
"Helper function for `emms-player-simple-mpv-playlist-to'."
(emms-player-simple-mpv-tq-enqueue
'("get_property" "playlist-count")
nil
(lambda (_ ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(let* ((data (emms-player-simple-mpv-tq-assq-v 'data ans-ls))
(n (read-number
(format "mpv playlist to (0 - %s) : " (1- data)))))
(emms-player-simple-mpv--playlist-to-1 n))
(message "mpv playlist to : error")))))
;;;###autoload
(defun emms-player-simple-mpv-playlist-to (&optional n)
"Go to the Nth entry on the playlist."
(interactive)
(if (called-interactively-p 'any)
(emms-player-simple-mpv--playlist-to-2)
(emms-player-simple-mpv--playlist-to-1 n)))
;;;###autoload
(defun emms-player-simple-mpv-playlist-pos ()
"Display current position on the playlist."
(interactive)
(emms-player-simple-mpv-tq-enqueue
'("get_property" "playlist-count")
nil
(lambda (_ ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(emms-player-simple-mpv-tq-enqueue
'("get_property" "playlist-pos")
nil
(emms-player-simple-mpv-tq-data-message
(format "mpv playlist position : %%s (total %s)"
(emms-player-simple-mpv-tq-assq-v 'data ans-ls))))
(message "mpv playlist position : error")))))
(defun emms-player-simple-mpv--with-playlist-pos (fn)
"Run FN with playlist-pos data."
(emms-player-simple-mpv-tq-enqueue
'("get_property" "playlist-pos")
nil
(lambda (_ ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(funcall fn (emms-player-simple-mpv-tq-assq-v 'data ans-ls))
(message "mpv playlist-pos : %s"
(emms-player-simple-mpv-tq-assq-v 'error ans-ls))))))
(defun emms-player-simple-mpv--playlist (&optional p1 p2 fn)
"Display playlist/P1/P2.
Run FN with data, if non-nil."
(let ((com (mapconcat (lambda (p) (format "%s" p))
(delete nil (list "playlist" p1 p2))
"/")))
(emms-player-simple-mpv-tq-enqueue
`("get_property" ,com)
com
(lambda (com ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(if fn (funcall fn (emms-player-simple-mpv-tq-assq-v 'data ans-ls))
(message "mpv %s : %s" com (emms-player-simple-mpv-tq-assq-v 'data ans-ls)))
(message "mpv %s : %s" com (emms-player-simple-mpv-tq-assq-v 'error ans-ls)))))))
;;;###autoload
(defun emms-player-simple-mpv-playlist-nth-title (n)
"Display title of N th entry.
N is 0-base"
(interactive "nInput playlist position: ")
(emms-player-simple-mpv--playlist n "title"))
;;;###autoload
(defun emms-player-simple-mpv-playlist-current-title ()
"Display title of the current entry."
(interactive)
(emms-player-simple-mpv--with-playlist-pos
(lambda (n) (emms-player-simple-mpv--playlist n "title"))))
;;;###autoload
(defun emms-player-simple-mpv-playlist-nth-filename (n)
"Display filename of N th entry.
N is 0-base."
(interactive "nInput playlist position: ")
(emms-player-simple-mpv--playlist n "filename"))
;;;###autoload
(defun emms-player-simple-mpv-playlist-current-filename ()
"Display title current entry."
(interactive)
(emms-player-simple-mpv--with-playlist-pos
(lambda (n) (emms-player-simple-mpv--playlist n "filename"))))
;;;###autoload
(defun emms-player-simple-mpv-playlist-move (index1 index2)
"Run playlist-move INDEX1 INDEX2."
(interactive "nInput index1: \nnInput index2: ")
(emms-player-simple-mpv-tq-enqueue
`("playlist-move" ,index1 ,index2) nil
(emms-player-simple-mpv-tq-error-message
(format "mpv playlist-move: %s %s : %%s" index1 index2))))
;;;###autoload
(defun emms-player-simple-mpv-playlist-shuffle ()
"Run playlist-shuffle."
(interactive)
(emms-player-simple-mpv-tq-enqueue
'("playlist-shuffle") nil
(emms-player-simple-mpv-tq-error-message "mpv playlist-shuffle: %s")))
;;;###autoload
(defun emms-player-simple-mpv-playlist-remove-current ()
"Run playlist-remove current."
(interactive)
(emms-player-simple-mpv-tq-enqueue
'("playlist-remove" "current") nil
(emms-player-simple-mpv-tq-error-message "mpv playlist-remove current: %s")))
;;;###autoload
(defun emms-player-simple-mpv-playlist-remove-index (index)
"Run playlist-remove INDEX."
(interactive "nInput index: ")
(emms-player-simple-mpv-tq-enqueue
`("playlist-remove" ,index) nil
(emms-player-simple-mpv-tq-error-message
(format "mpv playlist-remove %s : %%s" index))))
;;;###autoload
(defun emms-player-simple-mpv-speed-to (v)
"Set speed to V."
(interactive "nmpv speed to (0.01 - 100): ")
(setq v (cond ((< v 0.01) 0.01)
((> v 100) 100)
(t v)))
(emms-player-simple-mpv-set_property "speed" v :spec "%.2f"))
;;;###autoload
(defun emms-player-simple-mpv-speed-normal ()
"Change speed to normal."
(interactive)
(emms-player-simple-mpv-speed-to 1))
(defun emms-player-simple-mpv--speed-1 (v ans-ls)
"Helper function for `emms-player-simple-mpv-speed'."
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(let* ((speed (+ (emms-player-simple-mpv-tq-assq-v 'data ans-ls) v)))
(emms-player-simple-mpv-speed-to speed))
(message "mpv speed : error")))
;;;###autoload
(defun emms-player-simple-mpv-speed (v)
"Change speed by V."
(interactive "nmpv speed : ")
(emms-player-simple-mpv-tq-enqueue
(list "get_property" "speed")
v 'emms-player-simple-mpv--speed-1))
(defun emms-player-simple-mpv--speed-n% (n ans-ls)
"Helper function for `emms-player-simple-mpv-speed-%'."
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(let* ((speed (/ (* (emms-player-simple-mpv-tq-assq-v 'data ans-ls) n)
100.0)))
(emms-player-simple-mpv-speed-to speed))
(message "mpv speed : error")))
;;;###autoload
(defun emms-player-simple-mpv-speed-% (n)
"N % times speed."
(emms-player-simple-mpv-tq-enqueue
(list "get_property" "speed")
n 'emms-player-simple-mpv--speed-n%))
;;;###autoload
(defun emms-player-simple-mpv-speed-increase ()
"Increase speed by 10%."
(interactive)
(emms-player-simple-mpv-speed-% 110))
;;;###autoload
(defun emms-player-simple-mpv-speed-decrease ()
"Decrease speed by 10%."
(interactive)
(emms-player-simple-mpv-speed-% 90))
;;;###autoload
(defun emms-player-simple-mpv-speed-double ()
"Double speed."
(interactive)
(emms-player-simple-mpv-speed-% 200))
;;;###autoload
(defun emms-player-simple-mpv-speed-halve ()
"Halve speed."
(interactive)
(emms-player-simple-mpv-speed-% 50))
(defun emms-player-simple-mpv--ab-loop-1 ()
"Helper function for `emms-player-simple-mpv-ab-loop'."
(emms-player-simple-mpv-tq-enqueue
'("get_property" "ab-loop-a")
nil
(lambda (_ ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(emms-player-simple-mpv-tq-enqueue
'("get_property" "ab-loop-b")
(emms-player-simple-mpv-tq-assq-v 'data ans-ls)
#'emms-player-simple-mpv-ab-loop-2)
(message "mpv ab-loop : success")))))
(defun emms-player-simple-mpv-ab-loop-2 (loop-a ans-ls)
"Helper function for `emms-player-simple-mpv--ab-loop-1'."
(let ((loop-b (emms-player-simple-mpv-tq-assq-v 'data ans-ls)))
(cond ((numberp loop-b)
(message "mpv ab-loop : point B %s"
(emms-player-simple-mpv--time-string loop-b)))
((numberp loop-a)
(message "mpv ab-loop : point A %s"
(emms-player-simple-mpv--time-string loop-a)))
((equal loop-a "no") (message "mpv ab-loop : clear"))
(t (message "mpv ab-loop : success")))))
;;;###autoload
(defun emms-player-simple-mpv-ab-loop ()
"Cycle ab-loop."
(interactive)
(emms-player-simple-mpv-tq-enqueue
'("ab_loop") nil
(lambda (_ ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(emms-player-simple-mpv--ab-loop-1)
(message "mpv ab-loop : error")))))
;;;###autoload
(defun emms-player-simple-mpv-loop-to (n)
"Set loop to N.
If N is less than 1, set loop to \"inf\"."
(interactive "nmpv loop to : ")
(emms-player-simple-mpv-set_property
"loop" (cond ((< n 1) "inf")
((= n 1) "no")
(t n))
:fn (lambda (v) (if (numberp v) (format "%s times" v) v))))
;;;###autoload
(defun emms-player-simple-mpv-loop-file-to (n)
"Set loop-file to N.
If N is less than 0, set loop-file to \"inf\"."
(interactive "nmpv loop-file to : ")
(emms-player-simple-mpv-set_property
"loop-file" (if (< n 0) "inf" n)
:fn (lambda (v) (if (numberp v) (format "%s times" v) v))))
;;;###autoload
(defun emms-player-simple-mpv-ontop ()
"Cycle ontop."
(interactive)
(emms-player-simple-mpv-cycle "ontop"))
;;;###autoload
(defun emms-player-simple-mpv-fullscreen ()
"Cycle fullscreen."
(interactive)
(emms-player-simple-mpv-cycle "fullscreen"))
(defun emms-player-simple-mpv--metadata-1 (name)
"Helper function for `emms-player-simple-mpv-metadata'.
Display NAME in minibuffer."
(emms-player-simple-mpv-tq-enqueue
`("get_property" ,name) nil
(lambda (_ ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(let ((data (emms-player-simple-mpv-tq-assq-v 'data ans-ls))
(str (list (format "mpv %s:\n" name))))
(cl-loop for (n . v) in (nreverse data) do
(push (format "\n %s: %s" n v) str)
finally do (push "\n" str))
(let ((max-mini-window-height 0.9)
(minibuffer-message-timeout nil)
(resize-mini-windows 'grow-only))
(minibuffer-message (apply #'concat (nreverse str)))))
(message "mpv %s : error" name)))))
;;;###autoload
(defun emms-player-simple-mpv-metadata ()
"Display metadata."
(interactive)
(emms-player-simple-mpv--metadata-1 "metadata"))
;;;###autoload
(defun emms-player-simple-mpv-filtered-metadata ()
"Display filtered-metadata."
(interactive)
(emms-player-simple-mpv--metadata-1 "filtered-metadata"))
(defvar emms-player-simple-mpv--property-list nil
"List of property names.")
(defvar emms-player-simple-mpv--property-list-history nil
"`minibuffer-history' for `emms-player-simple-mpv-display-property'.")
(defun emms-player-simple-mpv--property-list-1 ()
"Helper function for `emms-player-simple-mpv-property-list'."
(let ((property
(completing-read "mpv property: "
emms-player-simple-mpv--property-list
nil nil nil
'emms-player-simple-mpv--property-list-history)))
(when property
(emms-player-simple-mpv-tq-enqueue
`("get_property_string" ,property) nil
(lambda (_ ans-ls)
(if (and (listp ans-ls)
(emms-player-simple-mpv-tq-success-p ans-ls))
(let ((data (cdr (assq 'data ans-ls))))
(message "mpv %s : %s" property data))
(message "mpv %s : %s" property
(if (and (listp ans-ls) (assq 'error ans-ls))
"error"
"Failed to get data"))))))))
;;;###autoload
(defun emms-player-simple-mpv-property-list ()
"Display the current value of a property via get_property_string."
(interactive)
(if emms-player-simple-mpv--property-list
(emms-player-simple-mpv--property-list-1)
(emms-player-simple-mpv-tq-enqueue
'("get_property" "property-list") nil
(lambda (_ ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(let ((data (cdr (assq 'data ans-ls))))
(setq emms-player-simple-mpv--property-list
(sort (append data nil) #'string<))
(run-with-idle-timer
0 nil
(lambda ()
(let ((this-command 'emms-player-simple-mpv-property-list))
(emms-player-simple-mpv--property-list-1)))))
(message "mpv property-list : error"))))))
(provide 'emms-player-simple-mpv-control-functions)
;;; emms-player-simple-mpv-control-functions.el ends here
emms-player-simple-mpv-20180316.1549/emms-player-simple-mpv-e.g.hydra.el 0000644 0000000 0000000 00000011606 13252763631 023526 0 ustar 00 0000000 0000000 ;;; emms-player-simple-mpv-e.g.hydra.el --- A setting example for hydra -*- lexical-binding: t -*-
;; Copyright (C) 2015-2017 momomo5717
;; Author: momomo5717
;; URL: https://github.com/momomo5717/
;; 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 .
;;; Commentary:
;; This provides a setting example of hydra for emms-player-simple-mpv.
;;
;; Setting `emms-player-simple-mpv-hydra/body' to a global key bind would be useful.
;; (global-set-key (kbd " m") 'emms-player-simple-mpv-hydra/body)
;;; Code:
(require 'emms-player-simple-mpv-control-functions)
(require 'hydra nil t)
;; This setting example emulates default key bindings of mpv player as mutch as possible.
(defvar emms-player-simple-mpv-hydra-docstring
"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Keyboard Control for emms simple player of mpv ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
_Q_ Quit emms-player-simple-mpv-hydra.
─────────────────────────────
__ and __
Seek backward/forward 5 seconds.
_S-_ and _S-_
Seek backward/forward 1 seconds.
__ and
Seek backward/forward 1 minute.
_S-_ and S-
Seek backward/forward 5 seconds.
─────────────────────────────
_\[_ and _\]_
Decrease/increase current playback speed by 10 %%%%.
_\{_ and _\}_
Halve/double current playback speed.
__
Reset playback speed to normal.
─────────────────────────────
_<_ and _>_
Go backward/forward in the playlist.
__
Go forward in the playlist.
─────────────────────────────
_p_ / _SPC_
Pause (pressing again unpauses).
─────────────────────────────
_q_ Stop playing and quit.
─────────────────────────────
_/_ and _*_
Decrease/increase volume.
_9_ and _0_
Decrease/increase volume.
─────────────────────────────
_m_ Mute sound.
─────────────────────────────
_f_ Toggle fullscreen.
─────────────────────────────
_T_ Toggle stay-on-top.
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
"
"Docstring for `emms-player-simple-mpv-hydra/body'.")
(eval-after-load "hydra"
`(defhydra emms-player-simple-mpv-hydra
(:foreign-keys warn :hint nil)
,emms-player-simple-mpv-hydra-docstring
("Q" nil)
("" (lambda () (interactive) (emms-seek -5)))
("S-" (lambda () (interactive) (emms-seek -1)))
("" (lambda () (interactive) (emms-seek -60)))
("S-" (lambda () (interactive) (emms-seek -5)))
("" (lambda () (interactive) (emms-seek 5)))
("S-" (lambda () (interactive) (emms-seek 1)))
("" (lambda () (interactive) (emms-seek 60)))
("S-" (lambda () (interactive) (emms-seek 5)))
("[" emms-player-simple-mpv-speed-decrease)
("]" emms-player-simple-mpv-speed-increase)
("{" emms-player-simple-mpv-speed-halve)
("}" emms-player-simple-mpv-speed-double)
("" emms-player-simple-mpv-speed-normal)
("<" emms-player-simple-mpv-playlist-prev)
(">" emms-player-simple-mpv-playlist-next)
("" emms-player-simple-mpv-playlist-next)
("p" emms-pause)
("SPC" emms-pause)
("q" (lambda () (interactive)
(when (y-or-n-p "emms-stop?")
(emms-stop))) :exit t)
("/" emms-volume-lower)
("*" emms-volume-raise)
("9" emms-volume-lower)
("0" emms-volume-raise)
("m" emms-player-simple-mpv-mute)
("f" emms-player-simple-mpv-fullscreen)
("T" emms-player-simple-mpv-ontop)))
(provide 'emms-player-simple-mpv-e.g.hydra)
;;; emms-player-simple-mpv-e.g.hydra.el ends here
emms-player-simple-mpv-20180316.1549/emms-player-simple-mpv-e.g.playlist-fname.el 0000644 0000000 0000000 00000004641 13252763631 025345 0 ustar 00 0000000 0000000 ;;; emms-player-simple-mpv-e.g.playlist-fname.el --- A setting example of TQ event hooks -*- lexical-binding: t -*-
;; Copyright (C) 2015-2017 momomo5717
;; Author: momomo5717
;; URL: https://github.com/momomo5717/
;; 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 .
;;; Commentary:
;; A setting example for current playlist's filename.
;;
;; (require 'emms-player-simple-mpv-e.g.playlist-fname)
;;; Code:
(require 'emms-player-simple-mpv)
(require 'emms-mode-line)
(defvar emms-mode-line-cycle) ; Suppres a warning message.
(defvar emms-mode-line-icon-before-format)
(defvar emms-mode-line-icon-image-cache)
(defun emms-player-simple-mpv-update-playlist-filename (fname)
"Set the current playlist's FNAME to `emms-mode-line-string'."
(when (and (stringp fname)
(eq (emms-track-type (emms-playlist-current-selected-track))
'playlist))
(setq emms-mode-line-string
(cond
((and (fboundp 'emms-mode-line-cycle-mode-line-function)
(or emms-mode-line-cycle
(eq emms-mode-line-mode-line-function
'emms-mode-line-cycle-mode-line-function)))
(emms-mode-line-cycle-mode-line-function fname))
((and (featurep 'emms-mode-line-icon)
(eq emms-mode-line-mode-line-function 'emms-mode-line-icon-function))
(concat " "
emms-mode-line-icon-before-format
(emms-propertize "NP:" 'display emms-mode-line-icon-image-cache)
(format emms-mode-line-format fname)))
(t (format emms-mode-line-format fname))))))
(add-hook 'emms-player-simple-mpv-tq-event-filename-functions
'emms-player-simple-mpv-update-playlist-filename)
(provide 'emms-player-simple-mpv-e.g.playlist-fname)
;;; emms-player-simple-mpv-e.g.playlist-fname.el ends here
emms-player-simple-mpv-20180316.1549/emms-player-simple-mpv-e.g.time-display.el 0000644 0000000 0000000 00000010211 13252763631 025007 0 ustar 00 0000000 0000000 ;;; emms-player-simple-mpv-e.g.time-display.el --- A setting example of TQ event hooks -*- lexical-binding: t -*-
;; Copyright (C) 2015-2017 momomo5717
;; Author: momomo5717
;; URL: https://github.com/momomo5717/
;; 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 .
;;; Commentary:
;; A setting example for `emms-playing-time-display'.
;;
;; (require 'emms-player-simple-mpv-e.g.time-display)
;;; Code:
(require 'emms-player-simple-mpv)
(require 'emms-mode-line)
(require 'emms-playing-time)
(defvar emms-mode-line-cycle) ; Suppress a warning message.
(defvar emms-state-mode)
(declare-function emms-state-set-total-playing-time "ext:emms-state")
;; Update playing-time
(defun emms-player-simple-mpv-reset-playing-time-display-timer (&optional speed)
"Reset `emms-playing-time'.
SPEED is a mpv property of speed."
(emms-player-simple-mpv-tq-enqueue
'("get_property" "time-pos")
(float-time)
(lambda (sent-time ans-ls)
(let ((time (emms-player-simple-mpv-tq-assq-v 'data ans-ls))
(update-fn (cond ((bound-and-true-p emms-state-mode) 'emms-state-playing-time-step)
(emms-playing-time-p 'emms-playing-time-display))))
(when (and (emms-player-simple-mpv-playing-p) (numberp time)
(functionp update-fn))
(when emms-playing-time-display-timer
(emms-cancel-timer emms-playing-time-display-timer)
(setq emms-playing-time-display-timer nil))
(unless emms-player-paused-p
(setq time (+ time (- (float-time) sent-time))))
(setq emms-playing-time (1- (floor time)))
(let (emms-mode-line-cycle)
(funcall update-fn))
(force-mode-line-update t)
(unless emms-player-paused-p
(setq speed (or (and (numberp speed)
(<= 0.01 speed) (<= speed 100) speed)
(and (emms-player-simple-mpv-last-speed-available-p)
emms-player-simple-mpv-last-speed)))
(setq emms-playing-time-display-timer
(if speed
(run-at-time (/ (- 1.0 (- time (ffloor time))) speed)
(/ 1.0 speed)
update-fn)
(run-at-time (- 1.0 (- time (ffloor time)))
1.0
update-fn)))))))))
(add-hook 'emms-player-simple-mpv-tq-event-unpause-hook
'emms-player-simple-mpv-reset-playing-time-display-timer)
(add-hook 'emms-player-simple-mpv-tq-event-playback-restart-hook
'emms-player-simple-mpv-reset-playing-time-display-timer)
(add-hook 'emms-player-simple-mpv-tq-event-speed-functions
'emms-player-simple-mpv-reset-playing-time-display-timer)
;; Update info-playing-time
(defvar emms-player-simple-mpv-info-playing-time-ignore-types nil
"List of type.")
(defun emms-player-simple-mpv-set-info-playing-time (duration)
"Set info-playing-time to DURATION."
(when (and (numberp duration) (> duration 0.0)
(not (memq (emms-track-type (emms-playlist-current-selected-track))
emms-player-simple-mpv-info-playing-time-ignore-types)))
(emms-track-set
(emms-playlist-current-selected-track)
'info-playing-time (floor duration))
(when (bound-and-true-p emms-state-mode) (emms-state-set-total-playing-time))))
(add-hook 'emms-player-simple-mpv-tq-event-duration-functions
'emms-player-simple-mpv-set-info-playing-time)
(provide 'emms-player-simple-mpv-e.g.time-display)
;;; emms-player-simple-mpv-e.g.time-display.el ends here
emms-player-simple-mpv-20180316.1549/emms-player-simple-mpv-pkg.el 0000644 0000000 0000000 00000000714 13252763631 022526 0 ustar 00 0000000 0000000 (define-package "emms-player-simple-mpv" "20180316.1549" "An extension of emms-player-simple.el for mpv JSON IPC"
'((emacs "24")
(cl-lib "0.5")
(emms "4.0"))
:commit "101d120ccdee1c2c213fd2f0423c858b21649c00" :authors
'(("momomo5717"))
:maintainers
'(("momomo5717"))
:maintainer
'("momomo5717")
:keywords
'("emms" "mpv")
:url "https://github.com/momomo5717/emms-player-simple-mpv")
;; Local Variables:
;; no-byte-compile: t
;; End:
emms-player-simple-mpv-20180316.1549/emms-player-simple-mpv-playlist-mode.el 0000644 0000000 0000000 00000057767 13252763631 024554 0 ustar 00 0000000 0000000 ;;; emms-player-simple-mpv-playlist-mode.el --- Playlist for mpv -*- lexical-binding: t -*-
;; Copyright (C) 2017 momomo5717
;; Author: momomo5717
;; URL: https://github.com/momomo5717/emms-player-simple-mpv
;; 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 .
;;; Commentary:
;; Setup:
;; (require 'emms-player-simple-mpv-playlist-mode)
;;
;; ;; Some mpv control functions can be added to `emms-player-simple-mpv-playlist-mode-map'.
;; (emms-player-simple-mpv-playlist-mode-setup-keybinds)
;;
;; Usage:
;;
;; M-x emms-player-simple-mpv-playlist-popup
;;
;; `emms-player-simple-mpv-playlist-mode-display-action' will be used
;; for the action argument of `display-buffer'.
;;; Code:
(require 'emms-player-simple-mpv-control-functions)
(defgroup emms-simple-player-mpv-playlist-mode nil
"Display mpv playlist."
:group 'emms-simple-player-mpv
:prefix "emms-simple-player-mpv-playlist-mode-")
(defface emms-simple-player-mpv-playlist-mode-selected-face
'((t :inherit emms-playlist-selected-face))
"Face used for the selected entry."
:group 'emms-simple-player-mpv-playlist-mode)
(defface emms-simple-player-mpv-playlist-mode-entry-face
'((t :inherit emms-playlist-track-face))
"Face used for entries."
:group 'emms-simple-player-mpv-playlist-mode)
(defcustom emms-player-simple-mpv-playlist-mode-display-action
'((display-buffer-reuse-window
display-buffer-at-bottom
display-buffer-pop-up-window)
(window-height . 0.3))
"Action for `emms-player-simple-mpv-playlist-popup'.
This will be used for the action arg of `display-buffer'."
:type (get 'display-buffer-base-action 'custom-type)
:group 'emms-simple-player-mpv-playlist-mode)
(defcustom emms-player-simple-mpv-playlist-mode-format-function
#'emms-player-simple-mpv-playlist-mode-default-format-function
"Function to format an entry.
It takes an entry and the position as arguments.
This will be used for `emms-player-simple-mpv-plm--insert-entry'."
:type 'function
:group 'emms-simple-player-mpv-playlist-mode)
(defvar emms-player-simple-mpv-plm--buffer-name " *EMMS mpv Playlist*"
"Buffer name for mpv playlist.")
(defvar emms-player-simple-mpv-plm--buffer nil
"Buffer for mpv playlist.")
(defvar emms-player-simple-mpv-plm--mpv-socket nil
"Store current `emms-player-simple-mpv--socket'.
This will be used as a buffer local variable.")
(make-variable-buffer-local 'emms-player-simple-mpv-plm--mpv-socket)
(defvar emms-player-simple-mpv-plm--wait-response-p nil
"Wait for the response if non-nil.")
(defun emms-player-simple-mpv-plm--wait-response (&optional id)
"Set `emms-player-simple-mpv-plm--wait-response-p' to ID.
If ID is nil, `emms-player-simple-mpv--tq-id-counter' will be used.
This function needs to calling before `emms-player-simple-mpv-tq-enqueue'."
(setq emms-player-simple-mpv-plm--wait-response-p
(or id emms-player-simple-mpv--tq-id-counter)))
(defun emms-player-simple-mpv-plm--maybe-response-p (id)
"Return t if ID equals `emms-player-simple-mpv-plm--wait-response-p'.
and set `emms-player-simple-mpv-plm--wait-response-p' to nil.
If ID is an alist, request_id value will be used."
(when (or (eq emms-player-simple-mpv-plm--wait-response-p id)
(and (listp id)
(eq emms-player-simple-mpv-plm--wait-response-p
(cdr (assq 'request_id id)))))
(setq emms-player-simple-mpv-plm--wait-response-p nil)
t))
(defvar emms-player-simple-mpv-plm--last-playlist []
"Last mpv playlist.")
(defvar emms-player-simple-mpv-plm--last-length nil)
(defun emms-player-simple-mpv-plm--last-length (&optional resetp)
"Get the last playlist length.
Reset it if RESETP is no-nil."
(if (or resetp (null emms-player-simple-mpv-plm--last-length))
(setq emms-player-simple-mpv-plm--last-length
(length emms-player-simple-mpv-plm--last-playlist))
emms-player-simple-mpv-plm--last-length))
(defvar emms-player-simple-mpv-plm--last-cpos nil
"The current entry position of the last playlist.")
(defun emms-player-simple-mpv-plm--last-cpos (&optional resetp)
"Get the current entry position of the last playlist.
Reset it if RESETP is no-nil."
(if (or resetp (null emms-player-simple-mpv-plm--last-cpos))
(setq emms-player-simple-mpv-plm--last-cpos
(cl-position t emms-player-simple-mpv-plm--last-playlist
:key (lambda (e) (cdr (assq 'current e)))))
emms-player-simple-mpv-plm--last-cpos))
(defun emms-player-simple-mpv-plm--set-last-cpos (pos)
"Set `emms-player-simple-mpv-plm--last-cpos' to POS."
(let* ((cpos (emms-player-simple-mpv-plm--last-cpos))
(playlist emms-player-simple-mpv-plm--last-playlist)
(entry1 (aref playlist cpos))
(entry2 (aref playlist pos)))
(aset playlist cpos (dolist (key '(current playing) entry1)
(setq entry1 (assq-delete-all key entry1))))
(aset playlist pos `((current . t) (playing . t) ,@entry2))
(setq emms-player-simple-mpv-plm--last-cpos pos)))
(defun emms-player-simple-mpv-plm--get-buffer (&optional buffer)
"Get `emms-player-simple-mpv-plm--buffer-name' buffer.
If BUFFER is no-nil, it will be used."
(setq emms-player-simple-mpv-plm--buffer
(or buffer
(and (buffer-live-p emms-player-simple-mpv-plm--buffer)
emms-player-simple-mpv-plm--buffer)
(get-buffer-create emms-player-simple-mpv-plm--buffer-name))))
(defun emms-player-simple-mpv-playlist-mode-default-format-function (entry position)
"Return a concatenated string of ENTRY and POSITION."
(format "%03d: %s"
position
(cdr (or (assq 'title entry)
(assq 'filename entry)))))
(defun emms-player-simple-mpv-plm--insert-entry (entry position)
"Insert ENTRY with POSITION."
(let ((inhibit-read-only t))
(insert (propertize
(concat
(funcall emms-player-simple-mpv-playlist-mode-format-function entry position)
"\n")
'mpv-playlist-entry entry
'mpv-playlist-entry-pos position
'face (if (eq (cdr (assq 'current entry)) t)
'emms-simple-player-mpv-playlist-mode-selected-face
'emms-simple-player-mpv-playlist-mode-entry-face)))))
(defun emms-player-simple-mpv-plm--insert-playlist (playlist)
"Insert PLAYLIST and return point of the current entry."
(cl-loop with current = nil
for pos from 0
for entry across playlist do
(when (assq 'current entry) (setq current (point)))
(emms-player-simple-mpv-plm--insert-entry entry pos)
finally return current))
(defun emms-player-simple-mpv-plm--entry-pos-at (&optional point)
"Get mpv-playlist-entry-pos property at POINT."
(get-text-property (or point (point)) 'mpv-playlist-entry-pos))
(defun emms-player-simple-mpv-plm--entry-at (&optional point)
"Get entry at POINT."
(get-text-property (or point (point)) 'mpv-playlist-entry))
(defun emms-player-simple-mpv-plm--filename-at (&optional point)
"Get filename of the entry at POINT."
(cdr (assq 'filename (emms-player-simple-mpv-plm--entry-at point))))
(defun emms-player-simple-mpv-plm--title-at (&optional point)
"Get title of the entry at POINT."
(cdr (assq 'title (emms-player-simple-mpv-plm--entry-at point))))
(defun emms-player-simple-mpv-plm--current-at (&optional point)
"Get current of the entry at POINT."
(cdr (assq 'current (emms-player-simple-mpv-plm--entry-at point))))
(defun emms-player-simple-mpv-plm--playing-at (&optional point)
"Get playing of the entry at POINT."
(cdr (assq 'playing (emms-player-simple-mpv-plm--entry-at point))))
(defun emms-player-simple-mpv-plm--get-nth-point (n)
"Return point of the N th entry."
(with-current-buffer (emms-player-simple-mpv-plm--get-buffer)
(save-excursion
(goto-char (point-min))
(forward-line n)
(point))))
(defun emms-player-simple-mpv-plm--run-with-playlist (fn)
"`run-with-timer' FN with playlist."
(emms-player-simple-mpv--playlist
nil nil
(lambda (playlist)
(setq emms-player-simple-mpv-plm--last-playlist playlist)
(setq emms-player-simple-mpv-plm--last-length nil)
(setq emms-player-simple-mpv-plm--last-cpos nil)
(run-with-timer 0 nil fn playlist))))
(defun emms-player-simple-mpv-plm--reload (playlist &optional goto-currentp)
"Reload PLAYLIST and return point of the current entry.
Go to the current entry if GOTO-CURRENTP is non-nil."
(with-current-buffer (emms-player-simple-mpv-plm--get-buffer)
(let* ((inhibit-read-only t)
(point (point))
(selected-winp (eq (current-buffer)
(window-buffer (selected-window))))
(win-start (when selected-winp (window-start)))
current)
(erase-buffer)
(emms-player-simple-mpv-playlist-mode)
(setq current (emms-player-simple-mpv-plm--insert-playlist playlist))
(goto-char (if goto-currentp (if current current (point-min)) point))
(when win-start
(if goto-currentp
(recenter)
(set-window-start (selected-window) win-start t)))
current)))
(defun emms-player-simple-mpv-plm--refresh (playlist)
"`erase-buffer' and `insert' PLAYLIST."
(emms-player-simple-mpv-plm--reload playlist t))
;;;###autoload
(defun emms-player-simple-mpv-playlist-mode-reload ()
"Reload mpv playlist buffer."
(interactive)
(emms-player-simple-mpv-plm--run-with-playlist
#'emms-player-simple-mpv-plm--reload))
;;;###autoload
(defun emms-player-simple-mpv-playlist-mode-refresh ()
"Refreash mpv playlist buffer."
(interactive)
(emms-player-simple-mpv-plm--run-with-playlist
#'emms-player-simple-mpv-plm--refresh))
;;;###autoload
(defun emms-player-simple-mpv-playlist-mode-goto-nth (n)
"Go to the N the entry."
(interactive
(list (if current-prefix-arg
(prefix-numeric-value current-prefix-arg)
(read-number
(format "Entry number (0-%d): "
(1- (emms-player-simple-mpv-plm--last-length)))))))
(let ((pt (emms-player-simple-mpv-plm--get-nth-point n)))
(when pt
(or (use-region-p) (push-mark))
(goto-char pt)
(recenter))))
;;;###autoload
(defun emms-player-simple-mpv-playlist-mode-goto-current ()
"Go to the current entry."
(interactive)
(let ((cpos (emms-player-simple-mpv-plm--last-cpos)))
(when cpos (emms-player-simple-mpv-playlist-mode-goto-nth cpos))))
(defun emms-player-simple-mpv-plm--update-playlist-pos (pos &optional gotop)
"Set the POS entry to the current entry for `emms-player-simple-mpv-plm--buffer'.
Go to the begging of the POS if GOTOP is non-nil."
(with-current-buffer (emms-player-simple-mpv-plm--get-buffer)
(let* ((inhibit-read-only t)
(playlist emms-player-simple-mpv-plm--last-playlist)
(cpos (emms-player-simple-mpv-plm--last-cpos))
(face1 'emms-simple-player-mpv-playlist-mode-entry-face)
(face2 'emms-simple-player-mpv-playlist-mode-selected-face)
(dls (list (- cpos (or (emms-player-simple-mpv-plm--entry-pos-at)
(and (eobp)
(emms-player-simple-mpv-plm--last-length))))
(- pos cpos)))
start)
(save-excursion
(emms-player-simple-mpv-plm--set-last-cpos pos)
(cl-loop for (i . face) in `((,cpos . ,face1) (,pos . ,face2))
for dl in dls do
(forward-line dl)
(setq start (point))
(add-text-properties
start (next-single-char-property-change start 'mpv-playlist-entry-pos)
`(face ,face mpv-playlist-entry ,(aref playlist i)))))
(when gotop (goto-char start)))))
;;;###autoload
(defun emms-player-simple-mpv-plm-update-playlist-pos (pos)
"Update playlist-pos(POS) for `emms-player-simple-mpv-plm--buffer'."
(when (and (buffer-live-p emms-player-simple-mpv-plm--buffer)
(eq (buffer-local-value 'emms-player-simple-mpv-plm--mpv-socket
emms-player-simple-mpv-plm--buffer)
emms-player-simple-mpv--socket)
(> (emms-player-simple-mpv-plm--last-length) 1))
(unless (eq pos (emms-player-simple-mpv-plm--last-cpos))
(emms-player-simple-mpv-plm--update-playlist-pos pos))))
;;;###autoload
(defun emms-player-simple-mpv-playlist-mode-play-at (&optional point)
"Set playlist-pos to the entry at POINT."
(interactive)
(when (eq emms-player-simple-mpv-plm--mpv-socket emms-player-simple-mpv--socket)
(let ((pos (emms-player-simple-mpv-plm--entry-pos-at (or point (point))))
(response-id emms-player-simple-mpv--tq-id-counter))
(when (and pos (not emms-player-simple-mpv-plm--wait-response-p))
(emms-player-simple-mpv-plm--wait-response response-id)
(emms-player-simple-mpv-set_property
"playlist-pos" pos
:fn (lambda (pos)
(when (emms-player-simple-mpv-plm--maybe-response-p response-id)
(emms-player-simple-mpv-plm-update-playlist-pos pos)))
:msg nil
:err-msg (format "set_property playlist-pos %s" pos))))))
;;;###autoload
(defun emms-player-simple-mpv-playlist-mode-remove-at (&optional point)
"Remove the entry at POINT."
(interactive)
(when (eq emms-player-simple-mpv-plm--mpv-socket emms-player-simple-mpv--socket)
(let ((pos (emms-player-simple-mpv-plm--entry-pos-at (or point (point)))))
(when pos
(forward-line 0)
(emms-player-simple-mpv-tq-enqueue
`("playlist-remove" ,pos) nil
(lambda (_ ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(emms-player-simple-mpv-playlist-mode-reload)
(message "mpv playlist-remove %s : %s" pos
(cdr (assq 'error ans-ls))))))))))
(defun emms-player-simple-mpv-plm--with-playlist-move (fn index1 index2)
"Run FN with INDEX1 and INDEX2.
Display error message if FN is nil."
(unless emms-player-simple-mpv-plm--wait-response-p
(emms-player-simple-mpv-plm--wait-response)
(emms-player-simple-mpv-tq-enqueue
(list "playlist-move" index1 index2) nil
(lambda (_ ans-ls)
(when (emms-player-simple-mpv-plm--maybe-response-p ans-ls)
(if (and fn (emms-player-simple-mpv-tq-success-p ans-ls))
(funcall fn index1 index2)
(message "mpv playlist-move %s %s : %s"
index1 index2 (cdr (assq 'error ans-ls)))))))))
(defun emms-player-simple-mpv-plm--move-slide (pos1 pos2 &optional up)
"Slide and insert POS1 entry after POS2 entry if UP is nil.
or Slide and insert POS2 entry before POS1 entry, if UP is non-nil.
and Return point which was at POS1 entry."
(unless (< pos2 (emms-player-simple-mpv-plm--last-length))
(error "Invalid value: pos2 or emms-player-simple-mpv-plm--last-length"))
(with-current-buffer (emms-player-simple-mpv-plm--get-buffer)
(save-excursion
(save-restriction
(forward-line (- pos1 (emms-player-simple-mpv-plm--entry-pos-at)))
(let* ((inhibit-read-only t)
(playlist emms-player-simple-mpv-plm--last-playlist)
(entry1 (aref playlist pos1))
(entry2 (aref playlist pos2))
(start (point))
(dpos (- pos2 pos1)))
(forward-line (1+ dpos))
(narrow-to-region start (point))
(if up
;; move-up: pos1 e0 e2 e3 pos2 => pos2 pos1 e0 e2 e3
(cl-loop for i downfrom pos2 above pos1 do
(aset playlist i (aref playlist (1- i)))
finally do (aset playlist pos1 entry2))
;; move-down: pos1 e0 e2 e3 pos2 => e0 e2 e3 pos2 pos1
(cl-loop for i from pos1 below pos2 do
(aset playlist i (aref playlist (1+ i)))
finally do (aset playlist pos2 entry1)))
(aset playlist (if up pos1 pos2) (if up entry2 entry1))
(delete-region (point-min) (point-max))
(goto-char (point-min))
(cl-loop for i from pos1 to pos2 do
(emms-player-simple-mpv-plm--insert-entry (aref playlist i) i))
start)))))
(defun emms-player-simple-mpv-plm--move-up (index1 index2)
"Move INDEX1 to INDEX2."
(let ((pos1 index1)
(pos2 (if (>= index1 index2) index2 (1- index2))))
(with-current-buffer (emms-player-simple-mpv-plm--get-buffer)
(goto-char (emms-player-simple-mpv-plm--move-slide
(min pos1 pos2) (max pos1 pos2) (>= index1 index2)))
(when (< pos1 pos2) (forward-line (- pos2 pos1))))))
;;;###autoload
(defun emms-player-simple-mpv-playlist-mode-move-up (n)
"Move up the entry at the point N times."
(interactive "p")
(when (and (eq emms-player-simple-mpv-plm--mpv-socket emms-player-simple-mpv--socket)
(null emms-player-simple-mpv-plm--wait-response-p))
(if (< n 0) (emms-player-simple-mpv-playlist-mode-move-down (abs n))
(let* ((pos (emms-player-simple-mpv-plm--entry-pos-at (point)))
(pos2 (max (- pos (if (> n 1) n 1)) 0)))
(when (and pos (> pos 0))
(emms-player-simple-mpv-plm--with-playlist-move
#'emms-player-simple-mpv-plm--move-up
pos pos2))))))
;;;###autoload
(defun emms-player-simple-mpv-playlist-mode-move-down (n)
"Move down the entry at the point N times."
(interactive "p")
(when (and (eq emms-player-simple-mpv-plm--mpv-socket emms-player-simple-mpv--socket)
(null emms-player-simple-mpv-plm--wait-response-p))
(if (< n 0) (emms-player-simple-mpv-playlist-mode-move-up (abs n))
(let* ((pos (emms-player-simple-mpv-plm--entry-pos-at (point)))
(pos2 (min (+ pos (if (> n 1) n 1))
(1- (emms-player-simple-mpv-plm--last-length)))))
(when (and pos (not (eq pos pos2)))
(emms-player-simple-mpv-plm--with-playlist-move
#'emms-player-simple-mpv-plm--move-up
pos (1+ pos2)))))))
(defun emms-player-simple-mpv-plm--next (&optional prevp)
"Run playlist-next and update.
Run playlist-prev and update if PREVP is non-nil."
(let ((com (if prevp "playlist-prev" "playlist-next")))
(unless emms-player-simple-mpv-plm--wait-response-p
(emms-player-simple-mpv-plm--wait-response)
(emms-player-simple-mpv-tq-enqueue
(list com)
com
(lambda (com ans-ls)
(when (emms-player-simple-mpv-plm--maybe-response-p ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(emms-player-simple-mpv-plm--update-playlist-pos
(+ (if prevp -1 1) (emms-player-simple-mpv-plm--last-cpos)) t)
(message "mpv %s : %s" com
(cdr (assq 'error ans-ls))))))))))
;;;###autoload
(defun emms-player-simple-mpv-playlist-mode-next ()
"Run playlist-next and reload."
(interactive)
(emms-player-simple-mpv-plm--next))
;;;###autoload
(defun emms-player-simple-mpv-playlist-mode-prev ()
"Run playlist-prev and reload."
(interactive)
(emms-player-simple-mpv-plm--next t))
;;;###autoload
(defun emms-player-simple-mpv-playlist-mode-shuffle ()
"Shuffle playlist."
(interactive)
(emms-player-simple-mpv-tq-enqueue
'("playlist-shuffle") nil
(lambda (_ ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(emms-player-simple-mpv-playlist-mode-refresh)
(message "mpv playlist-shuffle : %s"
(cdr (assq 'error ans-ls)))))))
(defun emms-player-simple-mpv-plm--shuffle-restart ()
"Set playlist-pos to 0 and refresh."
(emms-player-simple-mpv-set_property
"playlist-pos" 0
:fn (lambda (_) (emms-player-simple-mpv-playlist-mode-refresh))
:msg nil
:err-msg "set_property playlist-pos 0"))
;;;###autoload
(defun emms-player-simple-mpv-playlist-mode-shuffle-restart ()
"Shuffle playlist and Set playlist-pos to 0."
(interactive)
(emms-player-simple-mpv-tq-enqueue
'("playlist-shuffle") nil
(lambda (_ ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(emms-player-simple-mpv-plm--shuffle-restart)
(message "mpv playlist-shuffle : %s"
(cdr (assq 'error ans-ls)))))))
(defvar emms-player-simple-mpv-playlist-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "RET") 'emms-player-simple-mpv-playlist-mode-play-at)
(define-key map (kbd "d") 'emms-player-simple-mpv-playlist-mode-remove-at)
(define-key map (kbd "g") 'emms-player-simple-mpv-playlist-mode-reload)
(define-key map (kbd "") 'emms-player-simple-mpv-playlist-mode-move-up)
(define-key map (kbd "") 'emms-player-simple-mpv-playlist-mode-move-down)
(define-key map (kbd ">") 'emms-player-simple-mpv-playlist-mode-next)
(define-key map (kbd "<") 'emms-player-simple-mpv-playlist-mode-prev)
(define-key map (kbd "r") 'emms-player-simple-mpv-playlist-mode-shuffle)
(define-key map (kbd "R") 'emms-player-simple-mpv-playlist-mode-shuffle-restart)
(define-key map (kbd ".") 'emms-player-simple-mpv-playlist-mode-refresh)
(define-key map (kbd "c") 'emms-player-simple-mpv-playlist-mode-goto-current)
map)
"Keymap used in EMMS MPV playlist.")
;;;###autoload
(defun emms-player-simple-mpv-playlist-mode-setup-keybinds ()
"Set some control functions to `emms-player-simple-mpv-playlist-mode-map'."
(let ((map emms-player-simple-mpv-playlist-mode-map))
(define-key map (kbd "m") 'emms-player-simple-mpv-mute)
(define-key map (kbd "p") 'emms-pause)
(define-key map (kbd "") (lambda () (interactive) (emms-seek -5)))
(define-key map (kbd "") (lambda () (interactive) (emms-seek 5)))
(define-key map (kbd "S-") (lambda () (interactive) (emms-seek -1)))
(define-key map (kbd "S-") (lambda () (interactive) (emms-seek 1)))
(define-key map (kbd "[") 'emms-player-simple-mpv-speed-decrease)
(define-key map (kbd "]") 'emms-player-simple-mpv-speed-increase)
(define-key map (kbd "{") 'emms-player-simple-mpv-speed-halve)
(define-key map (kbd "}") 'emms-player-simple-mpv-speed-double)
(define-key map (kbd "") 'emms-player-simple-mpv-speed-normal)
(define-key map (kbd "T") 'emms-player-simple-mpv-ontop)
(define-key map (kbd "f") 'emms-player-simple-mpv-fullscreen)
(define-key map (kbd "9") 'emms-volume-lower)
(define-key map (kbd "0") 'emms-volume-raise)
(define-key map (kbd "l") 'emms-player-simple-mpv-ab-loop)))
;;;###autoload
(defun emms-player-simple-mpv-playlist-popup (&optional action frame)
"Popup mpv playlist buffer.
ACTION and FRAME will be used as arguments for `display-buffer'.
If ACTION is nil,
`emms-player-simple-mpv-playlist-mode-display-action' will be uesd."
(interactive)
(emms-player-simple-mpv-plm--run-with-playlist
(lambda (playlist)
(let ((current (emms-player-simple-mpv-plm--reload playlist))
(win (display-buffer
(emms-player-simple-mpv-plm--get-buffer)
(or action
emms-player-simple-mpv-playlist-mode-display-action)
frame)))
(select-window win)
(goto-char current)
(recenter)))))
;;;###autoload
(define-derived-mode emms-player-simple-mpv-playlist-mode special-mode
"EMMS mpv playlist"
"Major mode for displaying mpv playlist.
\\{emms-player-simple-mpv-playlist-mode-map}"
(buffer-disable-undo)
(setq-local emms-player-simple-mpv-plm--mpv-socket
emms-player-simple-mpv--socket)
(setq-local truncate-lines t)
(setq emms-player-simple-mpv-plm--wait-response-p nil)
(add-hook 'emms-player-simple-mpv-tq-event-playlist-pos-functions
'emms-player-simple-mpv-plm-update-playlist-pos))
(provide 'emms-player-simple-mpv-playlist-mode)
;;; emms-player-simple-mpv-playlist-mode.el ends here
emms-player-simple-mpv-20180316.1549/emms-player-simple-mpv.el 0000644 0000000 0000000 00000112147 13252763631 021753 0 ustar 00 0000000 0000000 ;;; emms-player-simple-mpv.el --- An extension of emms-player-simple.el for mpv JSON IPC -*- lexical-binding: t -*-
;; Copyright (C) 2015-2017 momomo5717
;; Keywords: emms, mpv
;; Version: 0.4.0
;; Package-Requires: ((emacs "24") (cl-lib "0.5") (emms "4.0"))
;; Author: momomo5717
;; URL: https://github.com/momomo5717/emms-player-simple-mpv
;; 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 .
;;; Commentary:
;;
;; This is an extension of emms-player-simple.el for mpv JSON IPC.
;; It provides macros and functions for defining emms simple players of mpv.
;; emms-player-simple-mpv-control-functions.el provides other functions to control mpv.
;;
;; Further information is available from:
;; https://github.com/momomo5717/emms-player-simple-mpv
;;
;;
;; Other Requirements:
;;
;; + mpv v0.10.0 or later
;; + Unix Sockets
;;
;; Setup:
;;
;; (require 'emms-player-simple-mpv)
;; ;; This plugin provides control functions (e.g. ab-loop, speed, fullscreen).
;; (require 'emms-player-simple-mpv-control-functions)
;;
;; Usage:
;;
;; ;; An example of setting like emms-player-mplayer.el
;; ;; `emms-player-my-mpv' is defined in this case.
;; (define-emms-simple-player-mpv my-mpv '(file url streamlist playlist)
;; (concat "\\`\\(http[s]?\\|mms\\)://\\|"
;; (apply #'emms-player-simple-regexp
;; "aac" "pls" "m3u"
;; emms-player-base-format-list))
;; "mpv" "--no-terminal" "--force-window=no" "--audio-display=no")
;;
;; (emms-player-simple-mpv-add-to-converters
;; 'emms-player-my-mpv "." '(playlist)
;; (lambda (track-name) (format "--playlist=%s" track-name)))
;;
;; (add-to-list 'emms-player-list 'emms-player-my-mpv)
;;
;; ;; Playing YouTube playlist in reverse order.
;; `emms-player-my-mpv-ytpl-reverse' will be defined in this case.
;; (define-emms-simple-player-mpv my-mpv-ytpl-reverse '(url)
;; "\\`http[s]://www\\.youtube\\.com/playlist\\?list="
;; "mpv" "--no-terminal" "--force-window=no" "--audio-display=no"
;; "--ytdl" "--ytdl-raw-options=playlist-reverse=")
;;
;; (add-to-list 'emms-player-list 'emms-player-my-mpv-ytpl-reverse)
;;
;; ;; M-x emms-player-simple-mpv-playlist-popup can display playlist
;;
;; The following example configuration files are available:
;;
;; + emms-player-simple-mpv-e.g.time-display.el
;; + emms-player-simple-mpv-e.g.playlist-fname.el
;; + emms-player-simple-mpv-e.g.hydra.el
;;; Code:
(require 'emms-player-simple)
(require 'emms-volume)
(require 'cl-lib)
(require 'json)
(require 'tq)
(require 'later-do)
(defconst emms-player-simple-mpv-version "0.4.0")
(defgroup emms-simple-player-mpv nil
"An extension of emms-simple-player.el."
:group 'emms-player
:prefix "emms-simple-player-mpv-")
(defcustom emms-player-simple-mpv-ipc-option-name nil
"IPC option name.
Renamed --input-unix-socket to --input-ipc-server since mpv v0.17.0"
:group 'emms-simple-player-mpv
:type '(choice (const :tag "Set automatically" nil)
(const :tag "mpv v0.17.0 or later" "--input-ipc-server")
(const :tag "mpv v0.10.0 to v0.16.0 " "--input-unix-socket")))
;;;###autoload
(defun emms-player-simple-mpv-get-version ()
"Return mpv version."
(with-temp-buffer
(unless (zerop (call-process "mpv" nil t "--verison"))
(error "Failed to get mpv version"))
(goto-char (point-min))
(if (re-search-forward
"^mpv\\s-+\\(\\([0-9]+\\.\\)\\{2\\}[0-9]+\\)[^0-9]" nil t)
(match-string-no-properties 1)
(error "Failed to get mpv version"))))
(defun emms-player-simple-mpv-get-ipc-option-name ()
"Return ipc option name."
(if (version< (emms-player-simple-mpv-get-version) "0.17.0")
"--input-unix-socket"
"--input-ipc-server"))
(defcustom emms-player-simple-mpv-ipc-dir nil
"Directory name for IPC.
If nil, `temporary-file-directory' will be used."
:group 'emms-simple-player-mpv
:type '(choice (const :tag "Use `temporary-file-directory'." nil)
(directory :tag "Directory name.")))
(defvar emms-player-simple-mpv-ipc-fname-prefix "mpv--socket"
"File name prefix for IPC.
This variable will be used with `make-temp-name'.")
(defcustom emms-player-simple-mpv-use-volume-change-function-p t
"If non-nil, `emms-player-simple-mpv-volume-change' is used as `emms-volume-change-function'."
:group 'emms-simple-player-mpv
:type 'boolean)
(defvar emms-player-simple-mpv-default-volume-function emms-volume-change-function
"Set emms-volume-change-function for buckup.")
(defcustom emms-player-simple-mpv-use-start-tq-error-message-p t
"If non-nil, display error message when failed to start tq process."
:group 'emms-simple-player-mpv
:type 'boolean)
(defcustom emms-player-simple-mpv-tq-event-pause-hook nil
"Normal hook run when TQ process receives \"pause\" from mpv."
:group 'emms-simple-player-mpv
:type 'hook)
(defcustom emms-player-simple-mpv-tq-event-unpause-hook nil
"Normal hook run when TQ process receives \"unpause\" from mpv."
:group 'emms-simple-player-mpv
:type 'hook)
(defcustom emms-player-simple-mpv-tq-event-playback-restart-hook nil
"Normal hook run when TQ process receives \"playback-restart\" from mpv."
:group 'emms-simple-player-mpv
:type 'hook)
(defcustom emms-player-simple-mpv-tq-event-filename-functions nil
"Abnormal hook run with one argument which is filename."
:group 'emms-simple-player-mpv
:type 'hook)
(defcustom emms-player-simple-mpv-tq-event-volume-functions nil
"Abnormal hook run with one argument which is volume."
:group 'emms-simple-player-mpv
:type 'hook)
(defvar emms-player-simple-mpv-last-volume nil
"Last volume value.")
(add-hook 'emms-player-simple-mpv-tq-event-volume-functions
(lambda (vol) (setq emms-player-simple-mpv-last-volume vol)))
(defcustom emms-player-simple-mpv-tq-event-speed-functions nil
"Abnormal hook run with one argument which is speed."
:group 'emms-simple-player-mpv
:type 'hook)
(defvar emms-player-simple-mpv-last-speed nil
"Last speed value.")
(add-hook 'emms-player-simple-mpv-tq-event-speed-functions
(lambda (speed) (setq emms-player-simple-mpv-last-speed speed)))
(defcustom emms-player-simple-mpv-tq-event-duration-functions nil
"Abnormal hook run with one argument which is duration."
:group 'emms-simple-player-mpv
:type 'hook)
(define-obsolete-variable-alias 'emms-player-simple-mpv-tq-event-length-functions
'emms-player-simple-mpv-tq-event-duration-functions
"20170930")
(defcustom emms-player-simple-mpv-tq-event-playlist-pos-functions nil
"Abnormal hook run with one argument which is playlist-pos."
:group 'emms-simple-player-mpv
:type 'hook)
(defcustom emms-player-simple-mpv-tq-event-property-change-functions-alist
(list '("filename" . emms-player-simple-mpv-tq-event-filename-functions)
'("volume" . emms-player-simple-mpv-tq-event-volume-functions)
'("speed" . emms-player-simple-mpv-tq-event-speed-functions)
'("duration" . emms-player-simple-mpv-tq-event-duration-functions)
'("playlist-pos" . emms-player-simple-mpv-tq-event-playlist-pos-functions))
"Alist of property name and abnormal hook.
Abnormal hook run with one argument for data
when TQ process receives \"property-change\" from mpv."
:group 'emms-simple-player-mpv
:type '(alist :key-type string :value-type symbol))
(defcustom emms-player-simple-mpv-keep-properties
(list '("volume"
emms-player-simple-mpv-keep-volume-available-p
emms-player-simple-mpv-last-volume)
'("speed"
emms-player-simple-mpv-keep-speed-available-p
emms-player-simple-mpv-last-speed))
"Alist of property name , function and symbol which has the last value.
The function takes no arguments and returns boolean."
:group 'emms-simple-player-mpv
:type '(alist :key-type string (group function symbol)))
(define-minor-mode emms-player-simple-mpv-keep-volume-mode
"Last volume value is used when new track starts."
:group 'emms-simple-player-mpv
:global t)
(defun emms-player-simple-mpv-keep-volume-available-p ()
"Return t if keep-volume-mode is t and last-volume is available."
(and emms-player-simple-mpv-keep-volume-mode
(emms-player-simple-mpv-last-volume-available-p)))
(defun emms-player-simple-mpv-last-volume-available-p ()
"Retrun t if `emms-player-simple-mpv-last-volume' is available."
(and (numberp emms-player-simple-mpv-last-volume)
(<= 0 emms-player-simple-mpv-last-volume)))
(define-minor-mode emms-player-simple-mpv-keep-speed-mode
"Last speed value is used when new track starts."
:group 'emms-simple-player-mpv
:global t)
(defun emms-player-simple-mpv-keep-speed-available-p ()
"Return t if keep-speed-mode is t and last-speed is available.
Only track type of file is available."
(when (eq (emms-track-type (emms-playlist-current-selected-track))
'file)
(and emms-player-simple-mpv-keep-speed-mode
(emms-player-simple-mpv-last-speed-available-p))))
(defun emms-player-simple-mpv-last-speed-available-p ()
"Retrun t if `emms-player-simple-mpv-last-speed' is available."
(and (numberp emms-player-simple-mpv-last-speed)
(<= 0.01 emms-player-simple-mpv-last-speed)
(<= emms-player-simple-mpv-last-speed 100)))
;;;###autoload
(defmacro define-emms-simple-player-mpv (name types regex command &rest args)
"Extension of `define-emms-simple-player' for mpv JSON IPC."
(let ((group (intern (format "emms-player-%s" name)))
(command-name (intern (format "emms-player-%s-command-name" name)))
(parameters (intern (format "emms-player-%s-parameters" name)))
(player-name (intern (format "emms-player-%s" name)))
(start (intern (format "emms-player-%s-start" name)))
(stop (intern (format "emms-player-%s-stop" name)))
(playablep (intern (format "emms-player-%s-playable-p" name))))
`(progn
(defgroup ,group nil
,(format "EMMS player for %s." command)
:group 'emms-player
:prefix ,(format "emms-player-%s-" name))
(defcustom ,command-name ,command
,(format "*The command name of %s." command)
:type 'string
:group ',group)
(defcustom ,parameters ',args
,(format "*The arguments to `%s'." command-name)
:type '(repeat string)
:group ',group)
(defcustom ,player-name (emms-player ',start ',stop ',playablep)
"*A player for EMMS."
:type '(cons symbol alist)
:group ',group)
(emms-player-set ,player-name 'regex ,regex)
(emms-player-set ,player-name 'pause 'emms-player-simple-mpv-pause)
(emms-player-set ,player-name 'resume 'emms-player-simple-mpv-unpause)
(emms-player-set ,player-name 'seek 'emms-player-simple-mpv-seek)
(emms-player-set ,player-name 'seek-to 'emms-player-simple-mpv-seek-to)
(emms-player-set ,player-name 'mpv-track-name-converters '())
(emms-player-set ,player-name 'mpv-start-process-function
'emms-player-simple-mpv-default-start-process)
(defun ,start (track)
"Start the player process."
(emms-player-simple-mpv-start track
,player-name
,command-name
,parameters))
(defun ,stop ()
"Stop the player process."
(emms-player-simple-stop))
(defun ,playablep (track)
"Return non-nil when we can play this track."
(and (executable-find ,command-name)
(memq (emms-track-type track) ,types)
(string-match (emms-player-get ,player-name 'regex)
(emms-track-name track)))))))
;; Utility for tq
(defvar emms-player-simple-mpv--tq nil
"TQ process.")
(defvar emms-player-simple-mpv--tq-id-counter 0
"Counter for request_id.")
(defvar emms-player-simple-mpv--tq-hash (make-hash-table)
"Key: request_id, Value: \(closure . fn).")
(defvar emms-player-simple-mpv-tq-process-name
"emms-player-simple-mpv-tq-process")
(defvar emms-player-simple-mpv--socket
(expand-file-name (make-temp-name emms-player-simple-mpv-ipc-fname-prefix)
(or emms-player-simple-mpv-ipc-dir temporary-file-directory)))
(defun emms-player-simple-mpv--socket ()
(setq emms-player-simple-mpv--socket
(expand-file-name (make-temp-name emms-player-simple-mpv-ipc-fname-prefix)
(or emms-player-simple-mpv-ipc-dir temporary-file-directory))))
(defun emms-player-simple-mpv--tq-create ()
(setq emms-player-simple-mpv--tq-id-counter 0)
(setq emms-player-simple-mpv--tq-hash (make-hash-table))
(tq-create (make-network-process
:name emms-player-simple-mpv-tq-process-name
:family 'local
:service emms-player-simple-mpv--socket)))
(defun emms-player-simple-mpv--tq-close ()
(when emms-player-simple-mpv--tq
(tq-close emms-player-simple-mpv--tq)
(setq emms-player-simple-mpv--tq nil))
(when (file-exists-p emms-player-simple-mpv--socket)
(ignore-errors (delete-file emms-player-simple-mpv--socket))))
(add-hook 'emms-player-stopped-hook 'emms-player-simple-mpv--tq-close)
(add-hook 'emms-player-finished-hook 'emms-player-simple-mpv--tq-close)
(defun emms-player-simple-mpv--socket-filter (_proc string)
(emms-player-simple-mpv--tq-filter emms-player-simple-mpv--tq string))
(defun emms-player-simple-mpv--tq-filter (tq string)
"Append STRING to the TQ's buffer; then process the new data. See tq.el."
(let ((buffer (tq-buffer tq)))
(when (buffer-live-p buffer)
(with-current-buffer buffer
(goto-char (point-max))
(insert string)
(when (and (eobp) (bolp))
(emms-player-simple-mpv--tq-process-buffer tq))))))
(defun emms-player-simple-mpv--tq-process-buffer (tq)
"Check TQ's buffer at the head of the queue.
See `tq-process-buffer'."
(let ((buffer (tq-buffer tq)))
(while (and (buffer-live-p buffer) (set-buffer buffer) (> (buffer-size) 0))
(goto-char (point-min))
(let* ((answer-ls (ignore-errors (json-read)))
(point (if (re-search-forward "{" nil t)
(goto-char (match-beginning 0))
(point)))
(request_id (emms-player-simple-mpv-tq-assq-v 'request_id answer-ls))
(hash-v (when request_id (gethash request_id emms-player-simple-mpv--tq-hash)))
(closure (car hash-v))
(fn (cdr hash-v))
(event (and (listp answer-ls)
(emms-player-simple-mpv-tq-assq-v 'event answer-ls))))
(delete-region (point-min)
(if (ignore-errors (json-read)) point (point-max)))
(if event (emms-player-simple-mpv--tq-event-action event answer-ls)
(unwind-protect
(when fn (condition-case err
(funcall fn closure answer-ls)
(error (message "Error: mpv tq-process-buffer : %s"
(error-message-string err)))))
(remhash request_id emms-player-simple-mpv--tq-hash)))))))
(defun emms-player-simple-mpv-playing-p ()
"Return t when `emms-player-simple-mpv--tq' process is open."
(let ((process (tq-process emms-player-simple-mpv--tq)))
(when process
(eq (process-status process) 'open))))
;;;###autoload
(defun emms-player-simple-mpv-tq-clear ()
"Clear tq-enque if it remains."
(interactive)
(when emms-player-simple-mpv--tq
(let ((buffer (tq-buffer emms-player-simple-mpv--tq)))
(setcar emms-player-simple-mpv--tq nil)
(setq emms-player-simple-mpv--tq-id-counter 0)
(setq emms-player-simple-mpv--tq-hash (make-hash-table))
(when (buffer-live-p buffer)
(with-current-buffer buffer
(delete-region (point-min) (point-max)))))))
(defun emms-player-simple-mpv--tq-make-command (com-ls id)
"Build JSON command from COM-LS and request ID.
`emms-player-simple-mpv--tq-id-counter' will be used as request_id."
(concat (json-encode `(("command" . ,com-ls) ("request_id" . ,id)))
"\n"))
;;;###autoload
(defun emms-player-simple-mpv-tq-enqueue (com-ls closure fn)
"Work like `tq-enqueue' except for using a hash table.
and return the request_id.
COM-LS is a list of a command name and params.
CLOSURE will be used as a first arg for FN.
FN will take CLOSURE and a parsed json object \(alist) after receiving a reply."
(when (emms-player-simple-mpv-playing-p)
(puthash emms-player-simple-mpv--tq-id-counter
(cons closure fn) emms-player-simple-mpv--tq-hash)
(process-send-string (tq-process emms-player-simple-mpv--tq)
(funcall #'emms-player-simple-mpv--tq-make-command
com-ls emms-player-simple-mpv--tq-id-counter))
(1- (cl-incf emms-player-simple-mpv--tq-id-counter))))
(defun emms-player-simple-mpv-tq-success-p (ans)
"Check command response from ANS."
(let ((err-msg
(if (atom (caar ans))
(cdr (assq 'error ans)) ;; For decoded JSON obj
(cl-loop for obj in ans ;; For decoded JSON obj list
for msg = (cdr (assq 'error obj))
when msg return msg))))
(and (stringp err-msg)
(string= err-msg "success"))))
(defun emms-player-simple-mpv-tq-assq (key ans)
"Return the association for KEY in ANS."
(if (atom (caar ans))
(assq key ans) ;; For decoded JSON obj
(cl-loop for obj in ans ;; For decoded JSON obj list
for assoc = (assq key obj)
when assoc return assoc)))
(defun emms-player-simple-mpv-tq-assq-v (key ans)
"Return a value of the association for KEY in ANS."
(cdr (emms-player-simple-mpv-tq-assq key ans)))
;;;###autoload
(cl-defun emms-player-simple-mpv-tq-data-message
(form &key (fn #'identity) (err-form form))
"Return function to display a data message by FORM.
FORM can include a format specification for data.
:FN takes data as an argument.
:ERR-FORM can include a format specification %s."
(lambda (_ ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(let ((data (emms-player-simple-mpv-tq-assq-v 'data ans-ls)))
(if data (message form (funcall fn data))
(message "mpv : nothing data message")))
(message err-form (emms-player-simple-mpv-tq-assq-v 'error ans-ls)))))
;;;###autoload
(defun emms-player-simple-mpv-tq-error-message (form)
"Return function to display an error message by FORM.
FORM can include a format specification %s."
(lambda (_ ans-ls)
(let ((err (emms-player-simple-mpv-tq-assq-v 'error ans-ls)))
(if err (message form err)
(message "mpv : nothing error message")))))
;; Event action
(defun emms-player-simple-mpv--tq-event-action (event ans-ls)
"Action for the EVENT from mpv.
If event is \"property-change\", ANS-LS is used."
(when (stringp event)
(cond
((string= event "playback-restart")
(emms-player-simple-mpv--tq-event-action-playback-restart))
((string= event "property-change")
(emms-player-simple-mpv--tq-event-action-property-change ans-ls)))))
;; Event for playback-restart
(defvar emms-player-simple-mpv--sent-observe_property-p) ; Suppress a warning message
(defsubst emms-player-simple-mpv--tq-event-action-playback-restart-dummy ()
"Dummy action for getting observe_property's message (for mpv v.0.11)."
(emms-player-simple-mpv-tq-enqueue
'("get_property" "volume") nil
(lambda (_ ans-ls)
(when (emms-player-simple-mpv-tq-success-p ans-ls)
(emms-player-simple-mpv-tq-enqueue
`("set_property" "volume"
,(emms-player-simple-mpv-tq-assq-v 'data ans-ls)) nil
(lambda (_ __)))))))
(defun emms-player-simple-mpv--tq-event-action-playback-restart ()
"Event action for playback-restart."
(unless emms-player-simple-mpv--sent-observe_property-p
(emms-player-simple-mpv--send-observe_property)
(emms-player-simple-mpv--tq-event-action-playback-restart-dummy))
(condition-case err
(run-hooks 'emms-player-simple-mpv-tq-event-playback-restart-hook)
(error (message "Error : mpv evet hook for playback-restart : %s"
(error-message-string err)))))
;; Event for pause
(defun emms-player-simple-mpv--tq-event-action-pause ()
"Event action for pause."
(setq emms-player-paused-p t)
(run-hooks 'emms-player-paused-hook)
(condition-case err
(run-hooks 'emms-player-simple-mpv-tq-event-pause-hook)
(error (message "Error : mpv evet hook for pause : %s"
(error-message-string err)))))
;; Event for unpause
(defun emms-player-simple-mpv--tq-event-action-unpause ()
"Event action for unpause."
(setq emms-player-paused-p nil)
(run-hooks 'emms-player-paused-hook)
(condition-case err
(run-hooks 'emms-player-simple-mpv-tq-event-unpause-hook)
(error (message "Error : mpv evet hook for unpause : %s"
(error-message-string err)))))
;; Event for property-change
(defun emms-player-simple-mpv--tq-event-action-property-change (ans-ls)
"Event action for property-change which is from ANS-LS."
(let ((name (emms-player-simple-mpv-tq-assq-v 'name ans-ls) )
(data (emms-player-simple-mpv-tq-assq-v 'data ans-ls)))
(when (stringp name)
(cond
((string= name "pause")
(if (eq data t) (emms-player-simple-mpv--tq-event-action-pause)
(emms-player-simple-mpv--tq-event-action-unpause)))
(t (emms-player-simple-mpv--run-tq-event-property-change-functions name data))))))
(defvar emms-player-simple-mpv--observe_property-name-als nil
"Alist of property name and id.")
(defvar emms-player-simple-mpv--observe_property-id-counter
(let ((count 0)) (lambda () (cl-incf count)))
"Return new property id number.")
(defun emms-player-simple-mpv--observe_property-id-counter (&optional initializep)
"Make property id counter."
(if initializep
(setq emms-player-simple-mpv--observe_property-id-counter
(let ((count 0)) (lambda () (cl-incf count))))
(funcall emms-player-simple-mpv--observe_property-id-counter)))
(defvar emms-player-simple-mpv--sent-observe_property-p nil
"If non-nil, `emms-player-simple-mpv--send-observe_property' has done.")
(defun emms-player-simple-mpv--send-observe_property ()
"Send observe_property for initialization."
(emms-player-simple-mpv-observe_property "pause")
(cl-loop for (name . _)
in emms-player-simple-mpv-tq-event-property-change-functions-alist do
(emms-player-simple-mpv-observe_property name))
(setq emms-player-simple-mpv--sent-observe_property-p t))
(defun emms-player-simple-mpv--run-tq-event-property-change-functions (name data)
"Event action for NAME.
DATA is used for the argument of name's abnormal hook.
`emms-player-simple-mpv-tq-event-property-change-functions-alist' is used."
(let ((functions (assoc-default name emms-player-simple-mpv-tq-event-property-change-functions-alist)))
(condition-case err
(when functions (run-hook-with-args functions data))
(error (message "Error : mpv evet hook for %s : %s"
name (error-message-string err))))))
;; Functions to start mpv
;;;###autoload
(defun emms-player-simple-mpv-add-to-converters (player regexp types fn &optional appendp)
"Add a converter to PLAYER's mpv-track-name-converters like `add-to-list'.
Converter is \(list REGEXP TYPES FN\).
If APPENDP is no-nil,add converter to last.
TYPES is type list or t.
FN takes track-name as an argument."
(let ((converters (emms-player-get player 'mpv-track-name-converters))
(converter (list regexp types fn)))
(unless (cl-find converter converters :test #'equal)
(emms-player-set player 'mpv-track-name-converters
(if appendp
(nconc converters (list converter))
(cons converter converters))))
(emms-player-get player 'mpv-track-name-converters)))
;;;###autoload
(defun emms-player-simple-mpv-remove-converter (player regexp)
"Remove the converter from PLAYER's mpv-track-name-converters which has REGEXP."
(let ((converters (emms-player-get player 'mpv-track-name-converters)))
(emms-player-set player 'mpv-track-name-converters
(cl-delete regexp converters :key #'car :test #'equal))
(emms-player-get player 'mpv-track-name-converters)))
(defun emms-player-simple-mpv--track-to-input-form (track track-name-converters)
"Convert TRACK to mpv input form by TRACK-NAME-CONVERTERS."
(let* ((track-name (emms-track-name track))
(track-type (emms-track-type track))
(converter
(cl-loop for (regexp types fn) in track-name-converters
when (and (string-match-p regexp track-name)
(or (eq types t) (memq track-type types)))
return fn)))
(if converter (funcall converter track-name) track-name)))
(defun emms-player-simple-mpv--start-tq-error-message (params input-form)
"Error message when tq-process fails to start."
(message "Failed to start mpv--tq. Check parameters or input form.\n%s%s\n%s%s"
" " (mapconcat #'identity params " ") " " input-form))
(defun emms-player-simple-mpv-default-start-process
(cmdname params input-form _track)
"Default function for mpv-start-process-function."
(apply #'start-process
emms-player-simple-process-name
nil
cmdname
`(,@params ,input-form)))
(defun emms-player-simple-mpv--start-initialize ()
"Initalize golobal variables before making a process."
(emms-player-simple-mpv--tq-close)
(emms-player-simple-mpv--observe_property-id-counter t)
(setq emms-player-simple-mpv--observe_property-name-als nil
emms-player-simple-mpv--sent-observe_property-p nil))
(defun emms-player-simple-mpv--add-keep-property-params (params)
"Add `emms-player-simple-mpv-keep-properties' to PARAMS."
(when emms-player-simple-mpv-keep-properties
(setq params (cl-copy-list params))
(cl-loop for (name availablep val-name) in emms-player-simple-mpv-keep-properties
when (funcall availablep) do
(setq params
(cons (format "--%s=%s" name (symbol-value val-name))
(cl-delete-if (lambda (param)
(string-match (format "\\`--%s=" name)
param))
params)))))
params)
(defvar emms-player-simple-mpv--connect-socket-timer nil)
(defvar emms-player-simple-mpv--connect-socket-timer-interval 0.05)
(defvar emms-player-simple-mpv--connect-socket-timeout 300.0)
(defun emms-player-simple-mpv--connect-socket-cancel-timer ()
"Cancel `emms-player-simple-mpv--connect-socket-timer'."
(when emms-player-simple-mpv--connect-socket-timer
(cancel-timer emms-player-simple-mpv--connect-socket-timer)
(setq emms-player-simple-mpv--connect-socket-timer nil)))
(defun emms-player-simple-mpv--set-tq-socket ()
"Set `emms-player-simple-mpv--tq' to a new tq."
(setq emms-player-simple-mpv--tq (emms-player-simple-mpv--tq-create))
(set-process-filter (tq-process emms-player-simple-mpv--tq)
'emms-player-simple-mpv--socket-filter)
(when emms-player-simple-mpv-use-volume-change-function-p
(emms-player-simple-mpv--set-volume-change-function)))
(defun emms-player-simple-mpv--connect-socket (process)
"Try to connect `emms-player-simple-mpv--socket' which PROCESS made."
(emms-player-simple-mpv--connect-socket-cancel-timer)
(let ((counter 0.0)
(timeout emms-player-simple-mpv--connect-socket-timeout)
(interval emms-player-simple-mpv--connect-socket-timer-interval)
(dir (file-name-directory emms-player-simple-mpv--socket)))
(unless (file-exists-p dir)
(error "Failed to find the directory: %s" dir))
(setq emms-player-simple-mpv--connect-socket-timer
(run-at-time
interval interval
(lambda ()
(cl-incf counter interval)
(cond
((> counter timeout)
(emms-player-simple-mpv--connect-socket-cancel-timer)
(message "Timeout: failed to find the socket file: %s"
emms-player-simple-mpv--socket))
((not (and (processp process) (eq (process-status process) 'run)))
(emms-player-simple-mpv--connect-socket-cancel-timer))
((file-exists-p emms-player-simple-mpv--socket)
(emms-player-simple-mpv--connect-socket-cancel-timer)
(condition-case err
(emms-player-simple-mpv--set-tq-socket)
(error (message "%s" (error-message-string err)))))))))))
;;;###autoload
(defun emms-player-simple-mpv-start (track player cmdname params)
"Emulate `emms-player-simple-start' but the first arg."
(unless emms-player-simple-mpv-ipc-option-name
(setq emms-player-simple-mpv-ipc-option-name
(emms-player-simple-mpv-get-ipc-option-name)))
(emms-player-simple-mpv--start-initialize)
(let* ((input-socket
(format "%s=%s" emms-player-simple-mpv-ipc-option-name
(emms-player-simple-mpv--socket)))
(input-form
(emms-player-simple-mpv--track-to-input-form
track (emms-player-get player 'mpv-track-name-converters)))
(params (emms-player-simple-mpv--add-keep-property-params params))
(process
(funcall (emms-player-get player 'mpv-start-process-function)
cmdname `(,input-socket ,@params)
input-form track)))
(set-process-sentinel process 'emms-player-simple-sentinel)
(emms-player-started player)
(setq emms-player-paused-p t)
(run-hooks 'emms-player-paused-hook)
(emms-player-simple-mpv--connect-socket process)))
;; Functions to control mpv
;;;###autoload
(defun emms-player-simple-mpv-observe_property (name)
"Set observe_property of NAME."
(let ((id (emms-player-simple-mpv--observe_property-id-counter)))
(unless (assoc name emms-player-simple-mpv--observe_property-name-als)
(emms-player-simple-mpv-tq-enqueue
(list "observe_property" id name) nil
(lambda (_ ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(push (cons name id) emms-player-simple-mpv--observe_property-name-als)
(message "mpv : Failed to set \"observe_property\" of %s" name)))))))
(defun emms-player-simple-mpv--set_property-1
(com property value spec msg err-msg fn)
"Helper function for emms-player-simple-mpv-set_property\(_string\)."
(emms-player-simple-mpv-tq-enqueue
(list com property value) nil
(lambda (_ ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(if msg
(message (format "mpv %s : %s" msg spec)
(if fn (funcall fn value) value))
(when fn (funcall fn value)))
(when err-msg
(message "mpv %s : %s" err-msg (cdr (assq 'error ans-ls))))))))
;;;###autoload
(cl-defun emms-player-simple-mpv-set_property
(property value &key (spec "%s") (msg property) (err-msg property) (fn #'identity))
"Set PROPERTY to VALUE via set_property.
:SPEC is a format specification for VALUE.
:MSG is displayed when command succeeds. If nil, it will be ignored.
:ERR-MSG is displayed when command fails. If nil, it will be ignored.
:FN takes VALUE as an argument. Its returned value will be used for :SPEC if :MSG is non-nil."
(emms-player-simple-mpv--set_property-1
"set_property" property value spec msg err-msg fn))
;;;###autoload
(cl-defun emms-player-simple-mpv-set_property_string
(property value &key (spec "%s") (msg property) (err-msg property) (fn #'identity))
"Set PROPERTY to VALUE via property_string.
:SPEC is a format specification for VALUE.
:MSG is displayed when command succeeds. If nil, it will be ignored.
:ERR-MSG is displayed when command fails. If nil, it will be ignored.
:FN takes VALUE as an argument. Its returned value will be used for :SPEC if :MSG is non-nil."
(emms-player-simple-mpv--set_property-1
"set_property_string" property value spec msg err-msg fn))
(defsubst emms-player-simple-mpv--time-string (sec)
"SEC to \"%02h:%02m:%02s\"."
(let* ((h (floor sec 3600))
(m (floor (- sec (* 3600 h)) 60))
(s (- sec (* 60 (+ (* 60 h) m)))))
(format "%02d:%02d:%02d" h m s)))
;; pause
;;;###autoload
(defun emms-player-simple-mpv-pause ()
"Pause."
(emms-player-simple-mpv-set_property_string
"pause" "yes" :spec "success"))
;;;###autoload
(defun emms-player-simple-mpv-unpause ()
"Unpause."
(emms-player-simple-mpv-set_property_string
"pause" "no" :spec "success" :msg "unpause" :err-msg "unpause"))
;; seek
(defun emms-player-simple-mpv--seek-1 (als ans-ls)
"Helper funcion for `emms-player-simple-mpv-seek'."
;; als : ((sec . n0) (len . n1))
;; data : time-pos
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(let* ((sec (cdr (assq 'sec als)))
(len (cdr (assq 'len als)))
(data (emms-player-simple-mpv-tq-assq-v 'data ans-ls))
(data+ (+ sec data))
(next-sec (cond
((< data+ 0) 0)
((> data+ len) len)
(t data+)))
(time (emms-player-simple-mpv--time-string next-sec)))
(emms-player-simple-mpv-tq-enqueue
(list "seek" next-sec "absolute")
(format "mpv seek %s : %s" (if (>= sec 0) ">>" "<<") time)
(lambda (form ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(message form)
(message "mpv seek : error")))))
(message "mpv seek : error")))
(defun emms-player-simple-mpv--seek-2 (sec)
"Helper funcion for `emms-player-simple-mpv-seek'.
For a track which does not have duration property."
(emms-player-simple-mpv-tq-enqueue
(list "seek" sec "relative")
nil
(emms-player-simple-mpv-tq-error-message
(format "mpv seek %s %+d : %%s" (if (>= sec 0) ">>" "<<") sec))))
;;;###autoload
(defun emms-player-simple-mpv-seek (sec)
"Seek by SEC."
(emms-player-simple-mpv-tq-enqueue
'("get_property" "duration")
sec
(lambda (sec ans-ls)
(let ((successp (emms-player-simple-mpv-tq-success-p ans-ls))
(data (emms-player-simple-mpv-tq-assq-v 'data ans-ls)))
(if (and successp (numberp data) (> data 0.0))
(emms-player-simple-mpv-tq-enqueue
'("get_property" "time-pos")
`((sec . ,sec) (len . ,data))
'emms-player-simple-mpv--seek-1)
(emms-player-simple-mpv--seek-2 sec))))))
;;;###autoload
(defun emms-player-simple-mpv-seek-to (sec)
"Seek to SEC."
(interactive "nmpv seek to (sec) : ")
(emms-player-simple-mpv-tq-enqueue
(list "seek" sec "absolute")
sec
(lambda (sec ans-ls)
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(message "mpv seek to : %s" (emms-player-simple-mpv--time-string sec))
(message "mpv seek to : error")))))
;; volume
(defun emms-player-simple-mpv--volume-change-1 (v ans-ls)
"Set volume plus V in `emms-player-simple-mpv-volume-change'.
ANS-LS includes data value."
(if (emms-player-simple-mpv-tq-success-p ans-ls)
(let* ((data (emms-player-simple-mpv-tq-assq-v 'data ans-ls))
(data+ (round (+ data v)))
(vol (if (< data+ 0) 0 data+)))
(emms-player-simple-mpv-set_property
"volume" vol :err-msg (format ": set volume to %s" vol)))
(message "mpv volume : error")))
;;;###autoload
(defun emms-player-simple-mpv-volume-change (v)
"Change volume by V."
(emms-player-simple-mpv-tq-enqueue
(list "get_property" "volume")
v 'emms-player-simple-mpv--volume-change-1))
(defun emms-player-simple-mpv--set-default-volume-change-function ()
"Set default volume change function to `emms-volume-change-function'."
(let ((default-volume-function
(get 'emms-player-simple-mpv-volume-change
:default-volume-change-function)))
(if (null default-volume-function)
(setq emms-volume-change-function emms-player-simple-mpv-default-volume-function)
(setq emms-volume-change-function default-volume-function)
(put 'emms-player-simple-mpv-volume-change
:default-volume-change-function nil)))
(remove-hook 'emms-player-stopped-hook
'emms-player-simple-mpv--set-default-volume-change-function)
(remove-hook 'emms-player-finished-hook
'emms-player-simple-mpv--set-default-volume-change-function))
(defun emms-player-simple-mpv--set-volume-change-function ()
"Set `emms-player-simple-mpv-volume-change' to `emms-volume-change-function'."
(put 'emms-player-simple-mpv-volume-change
:default-volume-change-function emms-volume-change-function)
(setq emms-volume-change-function 'emms-player-simple-mpv-volume-change)
(add-hook 'emms-player-stopped-hook
'emms-player-simple-mpv--set-default-volume-change-function)
(add-hook 'emms-player-finished-hook
'emms-player-simple-mpv--set-default-volume-change-function))
(provide 'emms-player-simple-mpv)
;;; emms-player-simple-mpv.el ends here