moc-player.el 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. ;;; moc-player.el
  2. ;; Copyright (C) 2020 by eDgar
  3. ;; Author: eDgar ;<[% edgar __at__ openmail.cc %]>;
  4. ;; moc-player is free software; you can redistribute it and/or modify it
  5. ;; under the terms of the GNU General Public License as published by
  6. ;; the Free Software Foundation; either version 3.
  7. ;;
  8. ;; moc-player is distributed in the hope that it will be useful, but WITHOUT
  9. ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  10. ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
  11. ;; License for more details.
  12. ;;
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with moc-player; see the file COPYING. If not, write to the
  15. ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  16. ;; Boston, MA 02110-1301, USA.
  17. ;;; Commentary:
  18. ;; This code allows to have some commands to control MOC
  19. ;; (Music on console; https://moc.daper.net) with Emacs
  20. ;;
  21. ;; (use-package moc-player
  22. ;; :load-path "/path/to/this/file"
  23. ;; :bind
  24. ;; (("s-m s-m" . mocp-ensure-command)
  25. ;; ("s-m a" . mocp-append-file)
  26. ;; ("<XF86AudioPrev>" . mocp-previous)
  27. ;; ("<XF86AudioNext>" . mocp-next)
  28. ;; ("s-m SPC" . mocp-toggle-pause-or-play)
  29. ;; ("s-m n" . mocp-next)
  30. ;; ("s-m p" . mocp-previous)
  31. ;; ("<XF86AudioPlay>" . mocp-toggle-pause-or-play)
  32. ;; ("s-m S" . mocp-seek)
  33. ;; ("s-m <" . mocp-seek-back)
  34. ;; ("s-m >" . mocp-seek-ahead)
  35. ;; ("s-m s" . mocp-stop)
  36. ;; ("s-m C" . mocp-clear)
  37. ;; ("s-m C-c s" . mocp-shuffle)
  38. ;; ("<XF86AudioStop>" . mocp-stop)))
  39. (defun moc-simple-call-process (cmd &rest args)
  40. "Runs `cmd' (string) with `call-process' using `args' as
  41. arguments"
  42. (interactive)
  43. (apply 'call-process
  44. cmd nil nil nil
  45. ;; Unwrap arguments
  46. ;; https://stackoverflow.com/a/24562026
  47. ;; (mapcar (lambda (arg) (identity arg)) '("/tmp/t")))
  48. (if args
  49. (mapcar #'identity args))))
  50. (defun mocp-command (&rest args)
  51. "Runs `mocp' with `args' as arguments. It yields the return
  52. code of `mocp'"
  53. (interactive)
  54. (apply 'moc-simple-call-process
  55. "mocp"
  56. (mapcar #'identity args)))
  57. (defun mocp-ensure-command (&rest args)
  58. "Tries to run `mocp' with `args' as arguments. If it fails,
  59. runs `mocp -n' and tries again"
  60. (interactive)
  61. ;; mocp-command will yield the return value of `mocp':
  62. ;; success ==> 0
  63. (if (not
  64. ;; returns a value != 0 when not able to do it
  65. (apply 'mocp-command (mapcar #'identity args)))
  66. ;; Do nothing if the mocp command succeeds
  67. nil
  68. ;; Start `mocp' and run the command again
  69. (mocp-command "-nS")
  70. (apply 'mocp-command (mapcar #'identity args))))
  71. (defun mocp-custom-find-file (msg)
  72. "Show a prompt to find a file (I don't know why `find-file'
  73. is not showing with Helm)"
  74. (interactive "sFind file: ")
  75. (let ((file-check
  76. ;; from `find-file' (call-interactively 'find-file)
  77. ;; not showing helm minibuffer
  78. (find-file-read-args
  79. msg
  80. (confirm-nonexistent-file-or-buffer))))
  81. (if (last file-check)
  82. (nbutlast file-check))))
  83. (defun mocp-play-file (fname)
  84. "Tries to play a file with name `fname' with `mocp'. If it
  85. fails, it runs `mocp -n', and tells `mocp' to play the
  86. file again"
  87. (interactive (mocp-custom-find-file "Play file: "))
  88. (mocp-ensure-command "--playit" (expand-file-name fname)))
  89. (defun mocp-append-file (fname)
  90. "Tries to append a path with name `fname' with `mocp'."
  91. (interactive (mocp-custom-find-file "Append file: "))
  92. (mocp-ensure-command)
  93. (async-shell-command (format "mocp --append %s" (expand-file-name fname))))
  94. (defun mocp-enqueue-file (fname)
  95. "Tries to enqueue a file with name `fname' with `mocp'."
  96. (interactive (mocp-custom-find-file "Enqueue file: "))
  97. (async-shell-command (format "mocp --enqueue %s" (expand-file-name fname))))
  98. (defun mocp-clear ()
  99. "Tries to clear the playist with `mocp'."
  100. (interactive)
  101. (mocp-command "--clear"))
  102. (defun mocp-play ()
  103. "Tries to start playing from the first item on the
  104. `mocp' playlist. If it fails, it runs `mocp -n' and tries again"
  105. (interactive)
  106. (mocp-ensure-command "--play"))
  107. (defun mocp-stop ()
  108. "Tries to stop playing with `mocp'."
  109. (interactive)
  110. (mocp-command "--stop"))
  111. (defun mocp-next ()
  112. "Tries to play the next song with `mocp'."
  113. (interactive)
  114. (mocp-command "--next"))
  115. (defun mocp-previous ()
  116. "Tries to play the previous song with `mocp'."
  117. (interactive)
  118. (mocp-command "--previous"))
  119. (defun mocp-exit ()
  120. "Tries to exit `mocp'."
  121. (interactive)
  122. (mocp-command "--exit"))
  123. (defun mocp-pause ()
  124. "Tries to pause `mocp'."
  125. (interactive)
  126. (mocp-command "--pause"))
  127. (defun mocp-unpause ()
  128. "Tries to unpause `mocp'."
  129. (interactive)
  130. (mocp-command "--unpause"))
  131. (defun mocp-toggle-pause ()
  132. "Tries to toggle-pause `mocp'."
  133. (interactive)
  134. (mocp-command "--toggle-pause"))
  135. (defun mocp-seek (N)
  136. "Tries to seek by `N' seconds (can be negative) with
  137. `mocp'. Unlike `mocp''s original command, it preemptively and
  138. temporarily starts to play the track (then pauses it)."
  139. (interactive "sSeek:")
  140. (mocp-temp-enable-cmd "--seek" N))
  141. (defun mocp-temp-enable-cmd (cmd &optional N)
  142. (let ((status (mocp-state)))
  143. (cond ((equal "PAUSE" status)
  144. (mocp-unpause)
  145. (mocp-command cmd N)
  146. (mocp-pause))
  147. ((equal "PLAY" status)
  148. (mocp-command cmd N)))))
  149. (defcustom mocp-seek-val "5"
  150. "Value to seek with `mocp-seek-back' or `mocp-seek-ahead'"
  151. :type 'string
  152. :group 'moc-player)
  153. (defun mocp-seek-back ()
  154. "Tries to seek backwards by `mocp-seek-val' seconds (can be
  155. negative) with `mocp'."
  156. (interactive)
  157. (mocp-command "--seek" (concat "-" mocp-seek-val)))
  158. (defun mocp-seek-ahead ()
  159. "Tries to seek forward by `mocp-seek-val' seconds (can be
  160. negative) with `mocp'."
  161. (interactive)
  162. (mocp-command "--seek" mocp-seek-val))
  163. (defun mocp-jump (val)
  164. "Tries to jump to some position of the current track. Unlike
  165. `mocp''s original command, it preemptively and temporarily
  166. starts to play the track (then pauses it)."
  167. (interactive "sJump:")
  168. (mocp-temp-enable-cmd "--jump" val))
  169. (defun mocp-jump-time (N)
  170. "Tries to jump to some (time) position of the current track. Unlike
  171. `mocp''s original command, it preemptively and temporarily
  172. starts to play the track (then pauses it)."
  173. (interactive "sJump:")
  174. (mocp-jump (format "%ss" N)))
  175. (defun mocp-jump-% (N)
  176. "Tries to jump to some % position of the current track. Unlike
  177. `mocp''s original command, it preemptively and temporarily
  178. starts to play the track (then pauses it)."
  179. (interactive "sJump:")
  180. (mocp-jump (format "%s%" N)))
  181. (defun mocp-control-on (cont)
  182. "Tries to turn a control `cont' on (shuffle, autonext,
  183. repeat) with `mocp'. If it fails, it runs `mocp -n' and
  184. tries again"
  185. (interactive)
  186. (mocp-ensure-command "--on" cont))
  187. (defun mocp-control-off (cont)
  188. "Tries to turn a control `cont' off (shuffle, autonext,
  189. repeat) with `mocp'."
  190. (interactive)
  191. (mocp-command "--off" cont))
  192. (defun mocp-control-toggle (cont)
  193. "Tries to toggle a control `cont' (shuffle, autonext, repeat)
  194. with `mocp'."
  195. (interactive)
  196. (mocp-command "--off" cont))
  197. (defun mocp-set-option (opt_val)
  198. "Tries to toggle a control `cont' (shuffle, autonext, repeat)
  199. with `mocp'."
  200. (interactive)
  201. (mocp-command "--set-option" opt_val))
  202. (defun mocp-fmt-info (fmt)
  203. "Tries to print formatted info about the file being played
  204. with `mocp'.
  205. fmt Return value
  206. -----------------------
  207. %state State
  208. %file File
  209. %title Title
  210. %artist Artist
  211. %song SongTitle
  212. %album Album
  213. %tt TotalTime
  214. %tl TimeLeft
  215. %ts TotalSec
  216. %ct CurrentTime
  217. %cs CurrentSec
  218. %b Bitrate
  219. %r Rate "
  220. (interactive)
  221. (substring
  222. (shell-command-to-string
  223. (concat "mocp " "--format " (shell-quote-argument fmt)))
  224. ;; Remove the newline that MOC always returns (in Linux).
  225. 0 -1))
  226. (defun mocp-track-title ()
  227. "Tries to print the title, current and total time about the
  228. file being played with `mocp'."
  229. (interactive)
  230. (mocp-fmt-info "%title"))
  231. (defun mocp-track-file ()
  232. "Tries to print the title, current and total time about the
  233. file being played with `mocp'."
  234. (interactive)
  235. (message (mocp-fmt-info "%file")))
  236. (defun mocp-state ()
  237. "Tries to print the title, current and total time about the
  238. file being played with `mocp'."
  239. (interactive)
  240. (format (mocp-fmt-info "%state")))
  241. (defun mocp-state-show ()
  242. "Tries to print the title, current and total time about the
  243. file being played with `mocp'."
  244. (interactive)
  245. (message (mocp-state)))
  246. (defun mocp-track-name-time ()
  247. "Tries to print the title, current and total time about the
  248. file being played with `mocp'."
  249. (interactive)
  250. (let ((tit (mocp-track-title))
  251. (time (shell-quote-argument "[%ct/%tt] ")))
  252. (if (string-empty-p tit)
  253. (message (concat (mocp-fmt-info time) (file-name-nondirectory
  254. (mocp-track-file))))
  255. (message (concat (mocp-fmt-info time) (mocp-track-title))))))
  256. (defun mocp-toggle-pause-or-play ()
  257. "Makes sure that `mocp' is running, and checks the state.
  258. If it is stopped, just plays (from the first item on the
  259. playlist). Otherwiser toggles the pause state"
  260. (interactive)
  261. ;; Make sure that MOC is playing
  262. (mocp-ensure-command)
  263. (if (equal "STOP" (mocp-state))
  264. (mocp-play)
  265. (mocp-toggle-pause))
  266. (mocp-state-show))
  267. (defun mocp-term ()
  268. "Launches `mocp' in a `term' buffer."
  269. (interactive)
  270. (term "/usr/bin/mocp"))
  271. (provide 'moc-player)
  272. ;;; moc-player.el ends here