;;; jao-mpc.el --- Using mpc client -*- lexical-binding: t; -*- ;; Copyright (C) 2021 jao ;; Author: jao ;; Keywords: convenience ;; Version: 0.1 ;; Package-requires: ((emacs "27.1")) ;; URL: https://codeberg.org/jao/lib/media ;; 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: ;; Simple mpd interaction using elmpd and mpc. ;;; Code: (require 'jao-lyrics) (require 'jao-random-album) (defconst jao-mpc--albums "*MPC Albums*") (defconst jao-mpc--playlist "*MPC Playlist*") (defvar jao-mpc-port 6600) (defvar-local jao-mpc--local-port nil) (defun jao-mpc--cmd (cmd) (shell-command-to-string (format "mpc -p %s %s" (or jao-mpc--local-port jao-mpc-port) cmd))) (defconst jao-mpc--fields '(artist album composer originaldate genre title track position time name)) (defconst jao-mpc--stfmt (mapconcat (lambda (f) (format "%s:::%%%s%%" f f)) jao-mpc--fields "\n")) (defun jao-mpc--current () (let ((s (jao-mpc--cmd (format "-f '%s' current" jao-mpc--stfmt))) (res)) (dolist (s (split-string s "\n" t " ") res) (when (string-match "\\(.+\\):::\\(.+\\)" s) (push (cons (intern (match-string 1 s)) (match-string 2 s)) res))))) (defun jao-mpc--current-str (&optional current len) (let* ((current (or current (jao-mpc--current))) (len (or len (jao-mpc--queue-len))) (title (alist-get 'title current (alist-get 'name current ""))) (album (alist-get 'album current)) (artist (alist-get 'artist current)) (composer (alist-get 'composer current)) (no (string-to-number (alist-get 'track current "0"))) (time (alist-get 'time current ""))) (format " %s%s %s%s%s%s" (jao--put-face (if (zerop no) "" (format "%02d/%s " no len)) 'jao-themes-f02) (jao--put-face title 'jao-themes-f00) (jao--put-face artist 'jao-themes-f01) (jao--put-face (if composer (format " [%s]" composer) "") 'jao-themes-f01) (jao--put-face (if album (format " (%s)" album) "") 'jao-themes-f11) (if (string-blank-p time) "" (jao--put-face (format " [%s]" time) 'jao-themes-dimm))))) (defvar jao-mpc-minibuffer-str "") (defun jao-mpc--playing-p () (not (string-blank-p (jao-mpc--cmd "status|grep '\\[playing\\]'")))) (defun jao-mpc--queue-len () (string-to-number (jao-mpc--cmd "playlist|wc -l"))) (defun jao-mpc--set-current-str (&optional _proc _str) (setq jao-mpc-minibuffer-str (if (jao-mpc--playing-p) (jao-mpc--current-str) (when (and jao-random-album-p (not (jao-mpc--current))) (jao-random-album-next)) ""))) (defvar jao-mpc--idle-proc nil) (defun jao-mpc--idle-loop () (when jao-mpc--idle-proc (kill-process jao-mpc--idle-proc)) (setq jao-mpc--idle-proc (make-process :name "jao-mpc-idleloop" :buffer "*jao-mpc-idleloop*" :command '("mpc" "idleloop" "player") :filter #'jao-mpc--set-current-str))) (define-derived-mode jao-mpc-albums-mode fundamental-mode "MPC Albums" "Mode to display the list of albums known by mpd." (read-only-mode -1) (delete-region (point-min) (point-max)) (insert (shell-command-to-string "mpc list album")) (goto-char (point-min)) (read-only-mode 1)) (define-key jao-mpc-albums-mode-map (kbd "n") #'next-line) (define-key jao-mpc-albums-mode-map (kbd "p") #'previous-line) (define-key jao-mpc-albums-mode-map (kbd "RET") #'jao-mpc-add-and-play) (define-key jao-mpc-albums-mode-map (kbd "q") #'bury-buffer) (defun jao-mpc--album-buffer () (if-let (b (get-buffer jao-mpc--albums)) b (with-current-buffer (get-buffer-create jao-mpc--albums) (jao-mpc-albums-mode) (current-buffer)))) (defun jao-mpc--add-and-play (&optional album) (interactive) (let ((album (or album (string-trim (thing-at-point 'line))))) (jao-mpc--cmd "clear") (jao-mpc--cmd (format "findadd album \"%s\"" album)) (jao-mpc--cmd "play"))) (define-derived-mode jao-mpc-playlist-mode fundamental-mode "MPC Playlist" "Mode to display the list of playlist known by mpd." (read-only-mode -1) (delete-region (point-min) (point-max)) (setq-local jao-mpc--local-port jao-mpc-port) (insert (jao-mpc--cmd "playlist")) (goto-char (point-min)) (display-line-numbers-mode) (read-only-mode 1)) (defun jao-mpc--playlist-goto-current () (interactive) (let ((c (string-trim (or (jao-mpc--cmd "current") "")))) (unless (string-blank-p c) (goto-char (point-min)) (when (re-search-forward (regexp-quote c) nil t) (beginning-of-line))))) (defun jao-mpc--playlist-play () (interactive) (jao-mpc--cmd (format "play %s" (line-number-at-pos)))) (define-key jao-mpc-playlist-mode-map (kbd "n") #'next-line) (define-key jao-mpc-playlist-mode-map (kbd "p") #'previous-line) (define-key jao-mpc-playlist-mode-map (kbd "q") #'bury-buffer) (define-key jao-mpc-playlist-mode-map (kbd ".") #'jao-mpc--playlist-goto-current) (define-key jao-mpc-playlist-mode-map (kbd "RET") #'jao-mpc--playlist-play) (define-key jao-mpc-playlist-mode-map (kbd "C") #'jao-mpc-clear) (defun jao-mpc--playlist-buffer () (with-current-buffer (get-buffer-create jao-mpc--playlist) (jao-mpc-playlist-mode) (current-buffer))) ;;;###autoload (defun jao-mpc-stop () (interactive) (jao-mpc--cmd "stop")) ;;;###autoload (defun jao-mpc-toggle () (interactive) (jao-mpc--cmd "pause")) ;;;###autoload (defun jao-mpc-play () (interactive) (jao-mpc--cmd "play")) ;;;###autoload (defun jao-mpc-next () (interactive) (jao-mpc--cmd "next")) ;;;###autoload (defun jao-mpc-previous () (interactive) (jao-mpc--cmd "previous")) ;;;###autoload (defun jao-mpc-seek (delta) (interactive "nDelta") (jao-mpc--cmd (format "seekcur %s%s" (if (> delta 0) "+" "") delta))) ;;;###autoload (defun jao-mpc-clear () (interactive) (jao-mpc--cmd "clear")) ;;;###autoload (defun jao-mpc-echo-current () (interactive) (jao-notify (jao-mpc--current-str))) ;;;###autoload (defun jao-mpc-show-albums () "Show album list." (interactive) (pop-to-buffer (jao-mpc--album-buffer))) ;;;###autoload (defun jao-mpc-show-playlist () "Show current playlist." (interactive) (pop-to-buffer (jao-mpc--playlist-buffer)) (jao-mpc--playlist-goto-current)) ;;;###autoload (defun jao-mpc-lyrics-track-data () (let ((c (string-trim (jao-mpc--cmd "current")))) (unless (string-blank-p c) (when (string-match "\\(.\\) - \\(.+\\)" c) (cons (match-string 2 c) (match-string 1 c)))))) ;;;###autoload (defun jao-mpc-setup () (setq jao-lyrics-info-function #'jao-mpc-lyrics-track-data) (jao-random-album-setup #'jao-mpc--album-buffer #'jao-mpc--add-and-play #'jao-mpc-stop) (jao-mpc--idle-loop) (jao-minibuffer-add-msg-variable 'jao-mpc-minibuffer-str 1)) (provide 'jao-mpc) ;;; jao-mpc.el ends here