From 1a68940f3ec6ad674ad2261f2589231336a62ade Mon Sep 17 00:00:00 2001 From: Edgar Vincent Date: Thu, 7 Oct 2021 23:44:31 +0200 Subject: [PATCH] Add support for lms --- evil-collection.el | 1 + modes/lms/evil-collection-lms.el | 402 +++++++++++++++++++++++++++++++ 2 files changed, 403 insertions(+) create mode 100644 modes/lms/evil-collection-lms.el diff --git a/evil-collection.el b/evil-collection.el index 2fe57b1..0c1a9f0 100644 --- a/evil-collection.el +++ b/evil-collection.el @@ -237,6 +237,7 @@ See `evil-collection-init' and `evil-collection--modes-with-delayed-setup'." js2-mode leetcode lispy + lms log-edit log-view lsp-ui-imenu diff --git a/modes/lms/evil-collection-lms.el b/modes/lms/evil-collection-lms.el new file mode 100644 index 0000000..52a8054 --- /dev/null +++ b/modes/lms/evil-collection-lms.el @@ -0,0 +1,402 @@ +;;; evil-collection-lms.el --- Evil bindings for lms.el -*- lexical-binding: t -*- +;; Copyright (C) 2021-2023 Edgar Vincent + +;; Author: Edgar Vincent +;; URL: https://github.com/emacs-evil/evil-collection +;; Version: 0.0.1 +;; Package-Requires: ((emacs "25.1")) +;; Keywords: evil, multimedia, music, logitechmediaserver, squeezebox + +;; This file is not part of GNU Emacs. +;; +;; This file 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, or (at your +;; option) any later version. +;; +;; This file 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. +;; +;; For a full copy of the GNU General Public License +;; see . + +;;; Commentary: +;; Evil bindings for lms.el. +;; +;;; Code: +(require 'lms nil t) +(require 'evil-collection) + +(defvar lms-ui-playing-now-mode-map) +(defvar lms-ui-players-mode-map) +(defvar lms-ui-playlist-mode-map) +(defvar lms-ui-track-info-mode-map) +(defvar lms-ui-tracks-list-mode-map) +(defvar lms--ui-pl-tracks) + +(declare-function lms-ui-playing-now-refresh "lms") + +(defconst evil-collection-lms-maps '(lms-ui-players-mode-map + lms-ui-playing-now-mode-map + lms-ui-playlist-mode-map + lms-ui-track-info-mode-map + lms-ui-tracks-list-mode-map + lms-ui-year-album-artist-list-mode-map)) + +(defconst evil-collection-lms-modes '(lms-players-mode + lms-ui-playing-now-mode + lms-ui-track-info-mode + lms-ui-players-mode + lms-ui-playlist-mode + lms-ui-tracks-list-mode + lms-ui-year-album-artist-list-mode)) + +(defun evil-collection-lms-ui-track-info-mode-quit () + "Exit `lms-ui-track-info-mode'." + (interactive) + (kill-buffer "*LMS: Track Information*") + (lms-ui-playing-now-refresh)) + +(defun evil-collection-lms-ui-players-mode-quit () + "Exit `lms-ui-players-mode'." + (interactive) + (kill-buffer (format "*LMS: Players*")) + (lms-ui-playing-now-refresh)) + +(defun evil-collection-lms-ui-playlist-mode-quit () + "Exit `lms-ui-playlist-mode'." + (interactive) + (kill-buffer (format "*LMS: Playlist [%d tracks]*" (length lms--ui-pl-tracks))) + (lms-ui-playing-now-refresh)) + +(defun evil-collection-lms-ui-tracks-list-mode-quit () + "Exit `lms-ui-tracks-list-mode'." + (interactive) + (kill-buffer) + (lms-ui-playing-now-refresh)) + +(defalias 'evil-collection-lms-ui-year-album-artist-list-mode-quit + 'evil-collection-lms-ui-tracks-list-mode-quit + "Exit lms-ui-tracks-list-mode.") + +(defun evil-collection-lms-goto-line (count) + "Move the point to line COUNT. By default the penultimate line." + (interactive "P") + (evil-goto-line count) + (unless count + (forward-line -1))) + +(defun evil-collection-lms-next-line () + "Move the point to the next line, except if it is at the end of the buffer." + (interactive) + (forward-line 1) + (when (eobp) (forward-line -1))) + +(defun evil-collection-lms-ui-playing-now () + "Modify the *LMS: Playing Now* buffer to reflect evil keybinding changes." + (setq-local buffer-read-only nil) + (setq lms--ui-editing-p t) + (while (search-forward-regexp "Press 'h' for help" nil 'noerror) + (replace-match "") + (insert (propertize "Press 'gh' or 'g?' for help" 'face '(variable-pitch (:height 0.85 :slant italic :foreground "gray40"))))) + (setq-local buffer-read-only t) + (setq lms--ui-editing-p nil)) + +(defvar evil-collection-lms-ui-docs + "#+TITLE: lms.el Documentation +#+AUTHOR: Iñigo Serna +#+DATE: 2020/06/14 + +* Introduction +This is an *emacs* frontend to interact with Squeezebox Server / Logitech Media Server. +Released under GPL version 3 license or later. + +It requires emacs version 25 or higher. + +More information on what a *squeezebox controller* is at https://inigo.katxi.org/blog/2017/07/31/lms_el.html. + +Quick instructions: customize some basic parameters such as 'lms-url' and run it with *lms* or *lms-ui*. +From there, you could read complete documentation after pressing *gh* or *g?*. +You can also run 'emacsclient -e \"(lms-float)\"' to display an independent small frame. + +Package should appear in [[https://melpa.org][MELPA repository]], and the code is in [[https://hg.serna.eu/emacs/lms][the code repository]] as well. + +* Features +This is Squeezebox controller, i.e. a program which can handle your local music library. + +Some of the features: +- Display song: title, artist, album, year, cover… +- Play, pause, stop, select next / previous song +- Control players: select player, power on/off, volume, repeat and shuffle modes +- Playlist control: list, select song, delete track, clear +- Show track information and change rating + +It is not aimed to be a complete controller, as it can't - and won't - manage external sources such us BBC, Deezer, Pandora, Spotify, or TuneIn Radio. + +* Configuration +There are some parameters you could customize: +|--------------------------+-----------------------------------------------+------------------------------------| +| Parameter | Description | Default | +|--------------------------+-----------------------------------------------+------------------------------------| +| lms-url | Logitech Media Server hostname or ip and port | http://lms_server:9000 | +| lms-default-player | Name of default player | nil (1) | +| lms-ui-cover-width | Cover image width | 500 (2) | +| lms-ui-update-interval | Time in seconds between UI updates | 1 (3) | +| lms-number-recent-albums | Number of recent albums to show | 25 | +| lms-number-random-albums | Number of random albums to show | 25 | +| lms-number-random-songs | Number of random songs to show | 50 | +| lms-set-rating-function | Function to use to set song rating | lms--set-rating-with-trackstat (4) | +|--------------------------+-----------------------------------------------+------------------------------------| +Notes: +(1) If *lms-default-player* is not defined or a player with that name does not exist, it will ask for one at start. +(2) It's recomendable not to change *lms-ui-cover-width*. +(3) Note that small values in *lms-ui-update-interval* could freeze your Emacs use while refreshing window. +(4) LMS does not have any means to set the rating of a song by itself, so it depends on an external plugin. + TrackStat (function *lms--set-rating-with-trackstat*) is a popular one, and RatingsLight (function *lms--set-rating-with-ratingslight*) is another option. +** Faces +The colors and font attributes of text can be customized in some views: +|----------------------------+-------------------------------+---------------------| +| Face name | Description | Default | +|----------------------------+-------------------------------+---------------------| +| lms-playing-face | Playing symbol | DarkTurquoise, bold | +| lms-title-face | Song title | SlateGray, italic | +| lms-artist-face | Artist | RosyBrown, bold | +| lms-year-face | Song year | SteelBlue | +| lms-album-face | Album | CadetBlue | +| lms-tracknum-face | Track number | gray40 | +| lms-duration-face | Song duration | gray60 | +| lms-players-selected-face | Selected icon in players list | SteelBlue | +| lms-players-isplaying-face | Isplaying in players list | RosyBrown | +| lms-players-name-face | Player name in players list | CadetBlue | +| lms-players-model-face | Player model in players list | SlateGray | +| lms-players-playerid-face | Player id in players list | gray60 | +| lms-players-ip-face | Player IP in players list | gray40 | +| lms-players-power-face | Ispower in players list | Maroon | +|----------------------------+-------------------------------+---------------------| + +* Playing now +Main window showing information about current track and player status. +The actions triggered by pressing keys refer to the current track. +** Key bindings +|-----------------+--------------------------------| +| Ctrl-p | select player | +| t | change player power state | +| R | change track rating | +| Ctrl-= | set volume | +| x, | toggle play/pause | +| p | play | +| s | stop playing | +| Ctrl-k, | play previous song in playlist | +| Ctrl-j, | play next song in playlist | +| m | toggle mute volume | +| +, = | volume up +5 | +| - | volume down -5 | +| r | cycle repeat mode | +| S | cycle shuffle mode | +| gr | update window contents | +| i | display track information | +| P | display playlist | +| T | show all albums by artist | +| L | show all tracks of album | +| Y | show all albums of this year | +| M | browse music libray | +| gh, g? | show this documentation | +| q | quit LMS | +|-----------------+--------------------------------| + +* Track information +Display track information. +Previous/next track only works when *Track information* window was called from a list, but not from *Playing now*. +** Key bindings +|-----------------+-------------------------| +| R | change track rating | +| Ctrl-k, | show previous track | +| Ctrl-j, | show next track | +| gh, g? | show this documentation | +| q | close window | +|-----------------+-------------------------| + +* Players list +Players list. +** Key bindings +|--------------------+--------------------------------| +| k, , j, | move cursor | +| | select player and close window | +| x | toggle player play/pause | +| t | toggle player power state | +| gh, g? | show this documentation | +| q | close window | +|--------------------+--------------------------------| + +* Playlist +Playlist view. +The actions triggered by pressing keys refer to the track under cursor. +** Key bindings +|--------------------+------------------------------------| +| k, , j, | move cursor | +| | play track | +| i | show track information | +| C | jump to current track | +| d, | remove track from playlist | +| c c | clear playlist | +| c u | remove tracks from start to cursor | +| c f | remove tracks from cursor to end | +| u | update window contents | +| A | show all albums by artist | +| T | show all tracks of album | +| Y | show all albums of this year | +| gh, g? | show this documentation | +| q | close window | +|--------------------+------------------------------------| + +* Year - Album - Artist list +View all albums of an artist, sorted by date/year. +The actions triggered by pressing keys refer to the album under cursor. +** Key bindings +|--------------------+------------------------------| +| k, , j, | move cursor | +| , a | show all tracks of album | +| A | show all albums by artist | +| Y | show all albums of this year | +| p | add album to playlist | +| gh, g? | show this documentation | +| q | close window | +|--------------------+------------------------------| + +* Tracks list +View list of tracks. +The actions triggered by pressing keys refer to the track under cursor. +** Key bindings +|--------------------+------------------------------| +| j, , k, | move cursor | +| , i | display track information | +| A | show all albums by artist | +| Y | show all albums of this year | +| p | add song to playlist | +| P | add all songs to playlist | +| gh, g? | show this documentation | +| q | close window | +|--------------------+------------------------------| +" + "LMS documentation.") + +;;;###autoload +(defun evil-collection-lms-setup () + "Set up `evil' bindings for `lms'." + + (dolist (map evil-collection-lms-maps) + (evil-collection-inhibit-insert-state map)) + + (dolist (mode evil-collection-lms-modes) + (evil-set-initial-state mode 'normal)) + + (evil-collection-define-key 'normal 'lms-ui-playing-now-mode-map + "t" 'lms-ui-playing-now-change-player-power-state + (kbd "C-p") 'lms-ui-playing-now-players-list + "R" 'lms-ui-playing-now-change-rating + (kbd "C-=") 'lms-ui-playing-now-set-volume + (kbd "RET") 'lms-ui-playing-now-play-pause + "x" 'lms-ui-playing-now-play-pause + "p" 'lms-ui-playing-now-play + "s" 'lms-ui-playing-now-stop + (kbd "C-j") 'lms-ui-playing-now-next + (kbd "") 'lms-ui-playing-now-next + (kbd "C-k") 'lms-ui-playing-now-prev + (kbd "") 'lms-ui-playing-now-prev + "+" 'lms-ui-playing-now-volume-up + "=" 'lms-ui-playing-now-volume-up + "-" 'lms-ui-playing-now-volume-down + "m" 'lms-ui-playing-now-volume-mute + "r" 'lms-ui-playing-now-cycle-repeat + "S" 'lms-ui-playing-now-cycle-shuffle + "gr" 'lms-ui-playing-now-refresh + "i" 'lms-ui-playing-now-show-track-info + "P" 'lms-ui-playing-now-show-playlist + "T" 'lms-ui-playing-now-album-tracks-list + "A" 'lms-ui-playing-now-artist-albums-list + "Y" 'lms-ui-playing-now-year-albums-list + "M" 'lms-ui-playing-now-browse-music-library + "g?" 'lms-ui-playing-now-help + "gh" 'lms-ui-playing-now-help + "q" 'lms-ui-playing-now-quit) + + (evil-collection-define-key 'normal 'lms-ui-track-info-mode-map + "R" 'lms-ui-track-info-change-rating + (kbd "C-k") 'lms-ui-track-info-prev + (kbd "") 'lms-ui-track-info-prev + (kbd "C-j") 'lms-ui-track-info-next + (kbd "") 'lms-ui-track-info-next + "g?" 'lms-ui-playing-now-help + "gh" 'lms-ui-playing-now-help + "q" 'evil-collection-lms-ui-track-info-mode-quit) + + (evil-collection-define-key 'normal 'lms-ui-players-mode-map + [remap evil-goto-line] 'evil-collection-lms-goto-line + [remap end-of-defun] 'evil-collection-lms-goto-line + [remap evil-forward-paragraph] 'evil-collection-lms-goto-line + [remap evil-next-line] 'evil-collection-lms-next-line + (kbd "RET") 'lms-ui-players-select + "x" 'lms-ui-players-playpause + "t" 'lms-ui-players-toggle-power + "g?" 'lms-ui-playing-now-help + "gh" 'lms-ui-playing-now-help + "q" 'evil-collection-lms-ui-players-mode-quit) + + (evil-collection-define-key 'normal 'lms-ui-playlist-mode-map + [remap evil-goto-line] 'evil-collection-lms-goto-line + [remap end-of-defun] 'evil-collection-lms-goto-line + [remap evil-forward-paragraph] 'evil-collection-lms-goto-line + [remap evil-next-line] 'evil-collection-lms-next-line + (kbd "RET") 'lms-ui-playlist-play + "i" 'lms-ui-playlist-track-info + "C" 'lms-ui-playlist-jump-to-current + "d" 'lms-ui-playlist-delete-track + (kbd "") 'lms-ui-playlist-delete-track + "cc" 'lms-ui-playlist-clear + "ct" 'lms-ui-playlist-clear-until-track + "cf" 'lms-ui-playlist-clear-from-track + "u" 'lms-ui-playlist + "A" 'lms-ui-playlist-artist-albums-list + "T" 'lms-ui-playlist-album-tracks-list + "Y" 'lms-ui-playlist-year-albums-list + "g?" 'lms-ui-playing-now-help + "gh" 'lms-ui-playing-now-help + "q" 'evil-collection-lms-ui-playlist-mode-quit) + + (evil-collection-define-key 'normal 'lms-ui-tracks-list-mode-map + [remap evil-goto-line] 'evil-collection-lms-goto-line + [remap end-of-defun] 'evil-collection-lms-goto-line + [remap evil-forward-paragraph] 'evil-collection-lms-goto-line + [remap evil-next-line] 'evil-collection-lms-next-line + "i" 'lms-ui-tl-track-info + (kbd "RET") 'lms-ui-tl-track-info + "p" 'lms-ui-tl-to-playlist + "P" 'lms-ui-tl-all-to-playlist + "Y" 'lms-ui-tl-by-year + "A" 'lms-ui-tl-by-artist + "g?" 'lms-ui-playing-now-help + "gh" 'lms-ui-playing-now-help + "q" 'evil-collection-lms-ui-tracks-list-mode-quit) + + (evil-collection-define-key 'normal 'lms-ui-year-album-artist-list-mode-map + [remap evil-goto-line] 'evil-collection-lms-goto-line + [remap end-of-defun] 'evil-collection-lms-goto-line + [remap evil-forward-paragraph] 'evil-collection-lms-goto-line + [remap evil-next-line] 'evil-collection-lms-next-line + "Y" 'lms-ui-yaal-by-year + "A" 'lms-ui-yaal-by-artist + (kbd "RET") 'lms-ui-yaal-by-album + "a" 'lms-ui-yaal-by-album + "p" 'lms-ui-yaal-to-playlist + "g?" 'lms-ui-playing-now-help + "gh" 'lms-ui-playing-now-help + "q" 'evil-collection-lms-ui-year-album-artist-list-mode-quit) + + ;; Reflect help keybinding changes in the lms.el UI. + (advice-add 'lms-ui-playing-now :after 'evil-collection-lms-ui-playing-now) + (setq lms-ui-docs evil-collection-lms-ui-docs)) + +(provide 'evil-collection-lms) +;;; evil-collection-lms.el ends here