123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- ;;; moc-player.el
- ;; Copyright (C) 2020 by eDgar
- ;; Author: eDgar ;<[% edgar __at__ openmail.cc %]>;
- ;; moc-player 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.
- ;;
- ;; moc-player 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 moc-player; see the file COPYING. If not, write to the
- ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- ;; Boston, MA 02110-1301, USA.
- ;;; Commentary:
- ;; This code allows to have some commands to control MOC
- ;; (Music on console; https://moc.daper.net) with Emacs
- ;;
- ;; (use-package moc-player
- ;; :load-path "/path/to/this/file"
- ;; :bind
- ;; (("s-m s-m" . mocp-ensure-command)
- ;; ("s-m a" . mocp-append-file)
- ;; ("<XF86AudioPrev>" . mocp-previous)
- ;; ("<XF86AudioNext>" . mocp-next)
- ;; ("s-m SPC" . mocp-toggle-pause-or-play)
- ;; ("s-m n" . mocp-next)
- ;; ("s-m p" . mocp-previous)
- ;; ("<XF86AudioPlay>" . mocp-toggle-pause-or-play)
- ;; ("s-m S" . mocp-seek)
- ;; ("s-m <" . mocp-seek-back)
- ;; ("s-m >" . mocp-seek-ahead)
- ;; ("s-m s" . mocp-stop)
- ;; ("s-m C" . mocp-clear)
- ;; ("s-m C-c s" . mocp-shuffle)
- ;; ("<XF86AudioStop>" . mocp-stop)))
- (defun moc-simple-call-process (cmd &rest args)
- "Runs `cmd' (string) with `call-process' using `args' as
- arguments"
- (interactive)
- (apply 'call-process
- cmd nil nil nil
- ;; Unwrap arguments
- ;; https://stackoverflow.com/a/24562026
- ;; (mapcar (lambda (arg) (identity arg)) '("/tmp/t")))
- (if args
- (mapcar #'identity args))))
- (defun mocp-command (&rest args)
- "Runs `mocp' with `args' as arguments. It yields the return
- code of `mocp'"
- (interactive)
- (apply 'moc-simple-call-process
- "mocp"
- (mapcar #'identity args)))
- (defun mocp-ensure-command (&rest args)
- "Tries to run `mocp' with `args' as arguments. If it fails,
- runs `mocp -n' and tries again"
- (interactive)
- ;; mocp-command will yield the return value of `mocp':
- ;; success ==> 0
- (if (not
- ;; returns a value != 0 when not able to do it
- (apply 'mocp-command (mapcar #'identity args)))
- ;; Do nothing if the mocp command succeeds
- nil
- ;; Start `mocp' and run the command again
- (mocp-command "-nS")
- (apply 'mocp-command (mapcar #'identity args))))
- (defun mocp-custom-find-file (msg)
- "Show a prompt to find a file (I don't know why `find-file'
- is not showing with Helm)"
- (interactive "sFind file: ")
- (let ((file-check
- ;; from `find-file' (call-interactively 'find-file)
- ;; not showing helm minibuffer
- (find-file-read-args
- msg
- (confirm-nonexistent-file-or-buffer))))
- (if (last file-check)
- (nbutlast file-check))))
- (defun mocp-play-file (fname)
- "Tries to play a file with name `fname' with `mocp'. If it
- fails, it runs `mocp -n', and tells `mocp' to play the
- file again"
- (interactive (mocp-custom-find-file "Play file: "))
- (mocp-ensure-command "--playit" (expand-file-name fname)))
- (defun mocp-append-file (fname)
- "Tries to append a path with name `fname' with `mocp'."
- (interactive (mocp-custom-find-file "Append file: "))
- (mocp-ensure-command)
- (async-shell-command (format "mocp --append %s" (expand-file-name fname))))
- (defun mocp-enqueue-file (fname)
- "Tries to enqueue a file with name `fname' with `mocp'."
- (interactive (mocp-custom-find-file "Enqueue file: "))
- (async-shell-command (format "mocp --enqueue %s" (expand-file-name fname))))
- (defun mocp-clear ()
- "Tries to clear the playist with `mocp'."
- (interactive)
- (mocp-command "--clear"))
- (defun mocp-play ()
- "Tries to start playing from the first item on the
- `mocp' playlist. If it fails, it runs `mocp -n' and tries again"
- (interactive)
- (mocp-ensure-command "--play"))
- (defun mocp-stop ()
- "Tries to stop playing with `mocp'."
- (interactive)
- (mocp-command "--stop"))
- (defun mocp-next ()
- "Tries to play the next song with `mocp'."
- (interactive)
- (mocp-command "--next"))
- (defun mocp-previous ()
- "Tries to play the previous song with `mocp'."
- (interactive)
- (mocp-command "--previous"))
- (defun mocp-exit ()
- "Tries to exit `mocp'."
- (interactive)
- (mocp-command "--exit"))
- (defun mocp-pause ()
- "Tries to pause `mocp'."
- (interactive)
- (mocp-command "--pause"))
- (defun mocp-unpause ()
- "Tries to unpause `mocp'."
- (interactive)
- (mocp-command "--unpause"))
- (defun mocp-toggle-pause ()
- "Tries to toggle-pause `mocp'."
- (interactive)
- (mocp-command "--toggle-pause"))
- (defun mocp-seek (N)
- "Tries to seek by `N' seconds (can be negative) with
- `mocp'. Unlike `mocp''s original command, it preemptively and
- temporarily starts to play the track (then pauses it)."
- (interactive "sSeek:")
- (mocp-temp-enable-cmd "--seek" N))
- (defun mocp-temp-enable-cmd (cmd &optional N)
- (let ((status (mocp-state)))
- (cond ((equal "PAUSE" status)
- (mocp-unpause)
- (mocp-command cmd N)
- (mocp-pause))
- ((equal "PLAY" status)
- (mocp-command cmd N)))))
- (defcustom mocp-seek-val "5"
- "Value to seek with `mocp-seek-back' or `mocp-seek-ahead'"
- :type 'string
- :group 'moc-player)
- (defun mocp-seek-back ()
- "Tries to seek backwards by `mocp-seek-val' seconds (can be
- negative) with `mocp'."
- (interactive)
- (mocp-command "--seek" (concat "-" mocp-seek-val)))
- (defun mocp-seek-ahead ()
- "Tries to seek forward by `mocp-seek-val' seconds (can be
- negative) with `mocp'."
- (interactive)
- (mocp-command "--seek" mocp-seek-val))
- (defun mocp-jump (val)
- "Tries to jump to some position of the current track. Unlike
- `mocp''s original command, it preemptively and temporarily
- starts to play the track (then pauses it)."
- (interactive "sJump:")
- (mocp-temp-enable-cmd "--jump" val))
- (defun mocp-jump-time (N)
- "Tries to jump to some (time) position of the current track. Unlike
- `mocp''s original command, it preemptively and temporarily
- starts to play the track (then pauses it)."
- (interactive "sJump:")
- (mocp-jump (format "%ss" N)))
- (defun mocp-jump-% (N)
- "Tries to jump to some % position of the current track. Unlike
- `mocp''s original command, it preemptively and temporarily
- starts to play the track (then pauses it)."
- (interactive "sJump:")
- (mocp-jump (format "%s%" N)))
- (defun mocp-control-on (cont)
- "Tries to turn a control `cont' on (shuffle, autonext,
- repeat) with `mocp'. If it fails, it runs `mocp -n' and
- tries again"
- (interactive)
- (mocp-ensure-command "--on" cont))
- (defun mocp-control-off (cont)
- "Tries to turn a control `cont' off (shuffle, autonext,
- repeat) with `mocp'."
- (interactive)
- (mocp-command "--off" cont))
- (defun mocp-control-toggle (cont)
- "Tries to toggle a control `cont' (shuffle, autonext, repeat)
- with `mocp'."
- (interactive)
- (mocp-command "--off" cont))
- (defun mocp-set-option (opt_val)
- "Tries to toggle a control `cont' (shuffle, autonext, repeat)
- with `mocp'."
- (interactive)
- (mocp-command "--set-option" opt_val))
- (defun mocp-fmt-info (fmt)
- "Tries to print formatted info about the file being played
- with `mocp'.
- fmt Return value
- -----------------------
- %state State
- %file File
- %title Title
- %artist Artist
- %song SongTitle
- %album Album
- %tt TotalTime
- %tl TimeLeft
- %ts TotalSec
- %ct CurrentTime
- %cs CurrentSec
- %b Bitrate
- %r Rate "
- (interactive)
- (substring
- (shell-command-to-string
- (concat "mocp " "--format " (shell-quote-argument fmt)))
- ;; Remove the newline that MOC always returns (in Linux).
- 0 -1))
- (defun mocp-track-title ()
- "Tries to print the title, current and total time about the
- file being played with `mocp'."
- (interactive)
- (mocp-fmt-info "%title"))
- (defun mocp-track-file ()
- "Tries to print the title, current and total time about the
- file being played with `mocp'."
- (interactive)
- (message (mocp-fmt-info "%file")))
- (defun mocp-state ()
- "Tries to print the title, current and total time about the
- file being played with `mocp'."
- (interactive)
- (format (mocp-fmt-info "%state")))
- (defun mocp-state-show ()
- "Tries to print the title, current and total time about the
- file being played with `mocp'."
- (interactive)
- (message (mocp-state)))
- (defun mocp-track-name-time ()
- "Tries to print the title, current and total time about the
- file being played with `mocp'."
- (interactive)
- (let ((tit (mocp-track-title))
- (time (shell-quote-argument "[%ct/%tt] ")))
- (if (string-empty-p tit)
- (message (concat (mocp-fmt-info time) (file-name-nondirectory
- (mocp-track-file))))
- (message (concat (mocp-fmt-info time) (mocp-track-title))))))
- (defun mocp-toggle-pause-or-play ()
- "Makes sure that `mocp' is running, and checks the state.
- If it is stopped, just plays (from the first item on the
- playlist). Otherwiser toggles the pause state"
- (interactive)
- ;; Make sure that MOC is playing
- (mocp-ensure-command)
- (if (equal "STOP" (mocp-state))
- (mocp-play)
- (mocp-toggle-pause))
- (mocp-state-show))
- (defun mocp-term ()
- "Launches `mocp' in a `term' buffer."
- (interactive)
- (term "/usr/bin/mocp"))
- (provide 'moc-player)
- ;;; moc-player.el ends here
|