al-emms-mpv.el 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. ;;; al-emms-mpv.el --- Additional functionality for using EMMS with mpv -*- lexical-binding: t -*-
  2. ;; Copyright © 2015–2019 Alex Kost
  3. ;; This program is free software; you can redistribute it and/or modify
  4. ;; it under the terms of the GNU General Public License as published by
  5. ;; the Free Software Foundation, either version 3 of the License, or
  6. ;; (at your option) any later version.
  7. ;;
  8. ;; This program is distributed in the hope that it will be useful,
  9. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. ;; GNU General Public License for more details.
  12. ;;
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Code:
  16. (require 'emms-player-simple-mpv)
  17. (defun al/emms-mpv-playing-radio? ()
  18. "Return non-nil, if current player is 'mpv' and current track
  19. type is 'url' or 'streamlist'."
  20. (and emms-player-playing-p
  21. (eq (emms-player-get emms-player-playing-p 'start)
  22. 'emms-player-mpv-start)
  23. (memq (emms-track-get (emms-playlist-current-selected-track)
  24. 'type)
  25. '(url streamlist))))
  26. (defun al/emms-mpv-call-with-property (property function &optional fallback)
  27. "Call FUNCTION on the value of PROPERTY of the current mpv track.
  28. If there is no such PROPERTY, call FALLBACK function without arguments."
  29. (emms-player-simple-mpv-tq-enqueue
  30. (list "get_property" property)
  31. nil
  32. (lambda (_ answer)
  33. (if (emms-player-simple-mpv-tq-success-p answer)
  34. (funcall function
  35. (emms-player-simple-mpv-tq-assq-v 'data answer))
  36. (if fallback
  37. (funcall fallback)
  38. (message "mpv refuses to return '%s' property" property))))))
  39. (defun al/emms-mpv-call-with-metadata (function)
  40. "Call FUNCTION on the metadata of the current mpv track."
  41. (al/emms-mpv-call-with-property
  42. "metadata"
  43. (lambda (value)
  44. (funcall function value))))
  45. (defun al/emms-mpv-run-command (command &optional closure function)
  46. "Run mpv COMMAND for the current EMMS mpv process.
  47. This is a wrapper for `emms-player-simple-mpv-tq-enqueue' (just
  48. to make CLOSURE and FUNCTION optional arguments).
  49. Command is what may be put in mpv conf-file, except it
  50. should be a list of values, e.g.:
  51. (\"cycle\" \"mute\")
  52. (\"show_text\" \"${playback-time}\")
  53. (\"add\" \"speed\" 0.2)"
  54. (emms-player-simple-mpv-tq-enqueue
  55. ;; OSD prefixes are disabled for JSON API by default:
  56. ;; <https://github.com/mpv-player/mpv/issues/4517>.
  57. (cons "osd-auto" command)
  58. closure (or function #'ignore)))
  59. (defun al/emms-mpv-show-property (property)
  60. "Display PROPERTY of the current TRACK."
  61. (interactive "smpv property: ")
  62. (al/emms-mpv-call-with-property property
  63. (lambda (value)
  64. (message "mpv %s: %S" property value))))
  65. (declare-function pp-display-expression "pp" (expression buffer-name))
  66. (defun al/emms-mpv-show-metadata ()
  67. "Display metadata of the current TRACK."
  68. (interactive)
  69. (require 'pp)
  70. (al/emms-mpv-call-with-metadata
  71. (lambda (data)
  72. (pp-display-expression data "*EMMS track metadata*"))))
  73. (defun al/emms-mpv-show-radio-description ()
  74. "Display a message about the current radio (url or streamlist) TRACK."
  75. (interactive)
  76. (al/emms-mpv-call-with-metadata
  77. (lambda (data)
  78. (let ((name (cdr (assq 'icy-name data)))
  79. (title (cdr (assq 'icy-title data)))
  80. (description (cdr (assq 'icy-description data))))
  81. (message
  82. (mapconcat #'identity
  83. ;; Remove nils and empty strings.
  84. (cl-remove-if (lambda (elt)
  85. (or (null elt)
  86. (and (stringp elt)
  87. (string= elt ""))))
  88. (list title name description))
  89. "\n"))))))
  90. (declare-function al/emms-notify "al-emms-notification" ())
  91. (defun al/emms-mpv-show-progress ()
  92. "Notify about the current mpv track.
  93. Show progress in the OSD if video is playing, or display
  94. notification for an audio track."
  95. (interactive)
  96. (al/emms-mpv-call-with-property
  97. "video-codec"
  98. (lambda (_)
  99. (al/emms-mpv-run-command '("show-progress")))
  100. (lambda ()
  101. (require 'al-emms-notification)
  102. (al/emms-notify))))
  103. (defun al/emms-mpv-show-osd-text (text)
  104. "Display TEXT in OSD."
  105. (interactive "sShow OSD text: ")
  106. (al/emms-mpv-run-command (list "show-text" text)))
  107. (defun al/emms-mpv-toggle-fullscreen ()
  108. "Toggle fullscreen."
  109. (interactive)
  110. (al/emms-mpv-run-command '("cycle" "fullscreen")))
  111. (defun al/emms-mpv-speed-normal ()
  112. "Change speed to normal."
  113. (interactive)
  114. (al/emms-mpv-run-command
  115. ;; Unlike "add" or "multiply", "set" requires a string, not a number!
  116. '("set" "speed" "1")
  117. nil
  118. (lambda (_closure _answer)
  119. (al/emms-mpv-show-property "speed"))))
  120. (defun al/emms-mpv-speed-up (&optional value)
  121. "Increase playback speed by VALUE (0.1 by default).
  122. Interactively with prefix, prompt for VALUE."
  123. (interactive
  124. (when current-prefix-arg
  125. (list (read-number "Increase speed by: " 0.1))))
  126. (al/emms-mpv-run-command
  127. (list "add" "speed" (number-to-string (or value 0.1)))
  128. nil
  129. (lambda (_closure _answer)
  130. (al/emms-mpv-show-property "speed"))))
  131. (defun al/emms-mpv-switch-volume (&optional value)
  132. "Set volume to VALUE or switch between default values.
  133. Interactively with '\\[universal-argument]' argument, prompt for VALUE.
  134. If prefix argument is numerical, use it for VALUE."
  135. (interactive
  136. (when current-prefix-arg
  137. (list (if (numberp current-prefix-arg)
  138. current-prefix-arg
  139. (read-number "Set volume to: " 100)))))
  140. (al/emms-mpv-run-command
  141. (if value
  142. (list "set" "volume" (number-to-string value))
  143. '("cycle-values" "volume" "50" "90" "130" "170"))
  144. nil
  145. (lambda (_closure _answer)
  146. (al/emms-mpv-show-property "volume"))))
  147. (defun al/emms-mpv-speed-down (&optional value)
  148. "Decrease playback speed by VALUE (0.1 by default).
  149. Interactively with prefix, prompt for VALUE."
  150. (interactive
  151. (when current-prefix-arg
  152. (list (read-number "Decrease speed by: " 0.1))))
  153. (al/emms-mpv-speed-up (- (or value 0.1))))
  154. (defvar emms-playing-time)
  155. (defun al/emms-mpv-sync-playing-time ()
  156. "Synchronize `emms-playing-time' with the real time reported by mpv."
  157. (interactive)
  158. (al/emms-mpv-call-with-property
  159. "time-pos"
  160. (lambda (value)
  161. (let ((sec (round value)))
  162. (message "Old playing time: %d; new time: %d"
  163. emms-playing-time sec)
  164. (setq emms-playing-time sec)))))
  165. (define-emms-simple-player-mpv mpv
  166. '(file url streamlist playlist)
  167. (concat "\\`\\(http\\|mms\\)://\\|"
  168. (emms-player-simple-regexp
  169. "ogg" "mp3" "wav" "mpg" "mpeg" "wmv" "wma"
  170. "mov" "avi" "divx" "oga" "ogm" "ogv" "asf" "mkv"
  171. "rm" "rmvb" "mp4" "flac" "vob" "m4a" "ape"
  172. "flv" "webm"))
  173. "mpv" "--no-terminal")
  174. (emms-player-simple-mpv-add-to-converters
  175. 'emms-player-mpv "." '(playlist)
  176. (lambda (track-name)
  177. (format "--playlist=%s" track-name)))
  178. (provide 'al-emms-mpv)
  179. ;;; al-emms-mpv.el ends here