| 1 | ;;; emms-idapi-browser.el --- EMMS Music ID API support -*- lexical-binding: t; -*- |
| 2 | ;; |
| 3 | |
| 4 | ;; Copyright (C) 2024 Free Software Foundation, Inc. |
| 5 | |
| 6 | ;; Author: Yoni Rabkin <yrk@gnu.org> |
| 7 | |
| 8 | ;; This file is part of EMMS. |
| 9 | |
| 10 | ;; EMMS is free software; you can redistribute it and/or modify it |
| 11 | ;; under the terms of the GNU General Public License as published by |
| 12 | ;; the Free Software Foundation; either version 3, or (at your option) |
| 13 | ;; any later version. |
| 14 | |
| 15 | ;; EMMS is distributed in the hope that it will be useful, but WITHOUT |
| 16 | ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| 17 | ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
| 18 | ;; License for more details. |
| 19 | |
| 20 | ;; You should have received a copy of the GNU General Public License |
| 21 | ;; along with EMMS; see the file COPYING. If not, write to the Free |
| 22 | ;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
| 23 | ;; MA 02110-1301, USA. |
| 24 | |
| 25 | |
| 26 | ;;; Commentary: |
| 27 | ;; |
| 28 | |
| 29 | (defvar emms-idapi-browser-debug-name |
| 30 | " *Emms Search Debug Browser*" |
| 31 | "Name of the search browser debug buffer") |
| 32 | |
| 33 | (defvar emms-idapi-browser-debug-buffer |
| 34 | nil |
| 35 | "Search browser debug buffer") |
| 36 | |
| 37 | (defvar emms-idapi-browser-name |
| 38 | "Emms Search Browser" |
| 39 | "Name of the search browser buffer") |
| 40 | |
| 41 | (defvar emms-idapi-browser-buffer |
| 42 | nil |
| 43 | "Search browser buffer") |
| 44 | |
| 45 | (defvar emms-idapi-browser-mode-hook nil |
| 46 | "Emms search browser mode hook.") |
| 47 | |
| 48 | (defvar emms-idapi-browser-field-alist |
| 49 | '(("artist" . info-artist) |
| 50 | ("album" . info-album) |
| 51 | ("track" . info-title)) |
| 52 | "Association list of readable fields and track properties.") |
| 53 | |
| 54 | ;;; Code: |
| 55 | (require 'emms-idapi) |
| 56 | |
| 57 | |
| 58 | ;;; ------------------------------------------------------------------ |
| 59 | ;;; Search Mode |
| 60 | ;;; ------------------------------------------------------------------ |
| 61 | (defun emms-idapi-browser-get-buffer () |
| 62 | "Get/create and return `emms-idapi-browser-mode' buffer." |
| 63 | (when (or (not emms-idapi-browser-buffer) |
| 64 | (not (buffer-live-p emms-idapi-browser-buffer))) |
| 65 | (with-current-buffer (get-buffer-create emms-idapi-browser-name) |
| 66 | (when (not (equal major-mode 'emms-idapi-browser-mode)) |
| 67 | (emms-idapi-browser-mode)))) |
| 68 | emms-idapi-browser-buffer) |
| 69 | |
| 70 | (defvar emms-idapi-browser-mode-map |
| 71 | (let ((map (make-sparse-keymap))) |
| 72 | (define-key map (kbd "q") #'bury-buffer) |
| 73 | map) |
| 74 | "Keymap for `emms-idapi-browser-mode'.") |
| 75 | |
| 76 | (defun emms-idapi-browser-mode () |
| 77 | "A major mode for the Emms search browser. |
| 78 | \\{emms-idapi-browser-mode-map}" |
| 79 | (interactive) |
| 80 | |
| 81 | (use-local-map emms-idapi-browser-mode-map) |
| 82 | |
| 83 | (setq major-mode 'emms-idapi-browser-mode |
| 84 | mode-name "Emms-Search-Browser" |
| 85 | buffer-read-only t |
| 86 | emms-idapi-browser-buffer (current-buffer)) |
| 87 | |
| 88 | (run-hooks 'emms-idapi-browser-mode-hook)) |
| 89 | |
| 90 | |
| 91 | ;;; ------------------------------------------------------------------ |
| 92 | ;;; Call |
| 93 | ;;; ------------------------------------------------------------------ |
| 94 | (defun emms-idapi-browser-track-at () |
| 95 | "Return a copy of the track at point." |
| 96 | (let* ((originial-track (emms-playlist-track-at (point))) |
| 97 | (track (copy-sequence originial-track))) |
| 98 | (when (not (emms-track-p track)) |
| 99 | (error "could not read Emms track at point")) |
| 100 | track)) |
| 101 | |
| 102 | (defun emms-idapi-browser-search-recording-artist (track) |
| 103 | "Search for the recording and artist of TRACK." |
| 104 | (let ((recording (alist-get 'info-title track)) |
| 105 | (artist (or (alist-get 'info-artist track) |
| 106 | (alist-get 'info-albumartist track)))) |
| 107 | (list |
| 108 | (cons 'info-title (read-string "search for recording (track): " recording)) |
| 109 | (cons 'info-artist (read-string "search for artist: " artist))))) |
| 110 | |
| 111 | (defun emms-idapi-browser-search-recording (track) |
| 112 | "Search for the recording of TRACK." |
| 113 | (let ((recording (alist-get 'info-title track))) |
| 114 | (list |
| 115 | (cons 'info-title (read-string "search for recording (track): " recording))))) |
| 116 | |
| 117 | (defun emms-idapi-browser-search-artist (track) |
| 118 | "Search for the artist of TRACK." |
| 119 | (let ((artist (or (alist-get 'info-artist track) |
| 120 | (alist-get 'info-albumartist track)))) |
| 121 | (list |
| 122 | (cons 'info-artist (read-string "search for artist: " artist))))) |
| 123 | |
| 124 | (defun emms-idapi-browser-search-album (track) |
| 125 | "Search for the album of TRACK." |
| 126 | (let ((album (alist-get 'info-album track))) |
| 127 | (list |
| 128 | (cons 'info-album (read-string "search for album: " album))))) |
| 129 | |
| 130 | (defun emms-idapi-browser-search-album-artist (track) |
| 131 | "Search for both artist and album of TRACK." |
| 132 | (let ((artist (or (alist-get 'info-artist track) |
| 133 | (alist-get 'info-albumartist track))) |
| 134 | (album (alist-get 'info-album track)) |
| 135 | search-album) |
| 136 | (setq search-album (read-string "search for album: " album)) |
| 137 | (list |
| 138 | (cons 'info-album search-album) |
| 139 | (cons 'info-artist (read-string |
| 140 | (format "search for album \"%s\" by artist: " search-album) |
| 141 | artist))))) |
| 142 | |
| 143 | (defun emms-idapi-browser-search-recording-artist-at () |
| 144 | "Search for the recording and artist of the track at point." |
| 145 | (interactive) |
| 146 | (emms-idapi-browser-show |
| 147 | (emms-idapi-search emms-idapi-service |
| 148 | (emms-idapi-browser-search-recording-artist |
| 149 | (emms-playlist-track-at (point)))))) |
| 150 | |
| 151 | (defun emms-idapi-browser-search-recording-at () |
| 152 | "Search for the recording of the track at point." |
| 153 | (interactive) |
| 154 | (emms-idapi-browser-show |
| 155 | (emms-idapi-search emms-idapi-service |
| 156 | (emms-idapi-browser-search-recording |
| 157 | (emms-playlist-track-at (point)))))) |
| 158 | |
| 159 | (defun emms-idapi-browser-search-artist-at () |
| 160 | "Search for the artist of the track at point." |
| 161 | (interactive) |
| 162 | (emms-idapi-browser-show |
| 163 | (emms-idapi-search emms-idapi-service |
| 164 | (emms-idapi-browser-search-artist |
| 165 | (emms-playlist-track-at (point)))))) |
| 166 | |
| 167 | (defun emms-idapi-browser-search-album-at () |
| 168 | "Search for the album of the track at point." |
| 169 | (interactive) |
| 170 | (emms-idapi-browser-show |
| 171 | (emms-idapi-search emms-idapi-service |
| 172 | (emms-idapi-browser-search-album |
| 173 | (emms-playlist-track-at (point)))))) |
| 174 | |
| 175 | (defun emms-idapi-browser-search-album-artist-at () |
| 176 | "Search for the album and artist of the track at point." |
| 177 | (interactive) |
| 178 | (emms-idapi-browser-show |
| 179 | (emms-idapi-search emms-idapi-service |
| 180 | (emms-idapi-browser-search-album-artist |
| 181 | (emms-playlist-track-at (point)))))) |
| 182 | |
| 183 | ;;; ------------------------------------------------------------------ |
| 184 | ;;; Response |
| 185 | ;;; ------------------------------------------------------------------ |
| 186 | (defun emms-idapi-browser-write-debug (response) |
| 187 | "Write RESPONSE to the browser debug buffer." |
| 188 | (let ((buffer (get-buffer-create emms-idapi-browser-debug-name))) |
| 189 | (with-current-buffer buffer |
| 190 | (erase-buffer) |
| 191 | (insert (format "%s" response)) |
| 192 | (setq emms-idapi-browser-debug-buffer buffer)))) |
| 193 | |
| 194 | (defun emms-idapi-browser-print-header (header) |
| 195 | "Print the material for the search HEADER." |
| 196 | (let ((artist (alist-get 'info-artist header)) |
| 197 | (album (alist-get 'info-album header)) |
| 198 | (title (alist-get 'info-title header)) |
| 199 | (service (alist-get emms-idapi-service emms-idapi-services-alist))) |
| 200 | (when (not (or artist album title)) |
| 201 | (error "could not read header: %s" header)) |
| 202 | (insert (format "service: %s (%s)\n" |
| 203 | (alist-get 'name service) |
| 204 | (alist-get 'website service))) |
| 205 | (when artist |
| 206 | (insert (format "artist: %s\n" artist))) |
| 207 | (when album |
| 208 | (insert (format "album: %s\n" album))) |
| 209 | (when title |
| 210 | (insert (format "title: %s\n" title))) |
| 211 | (insert "\n"))) |
| 212 | |
| 213 | (defun emms-idapi-browser-entry-packaging (entry) |
| 214 | "Print packaging information for ENTRY." |
| 215 | (let ((packaging (alist-get 'info-packaging entry))) |
| 216 | (if (and packaging |
| 217 | (not (string= "None" packaging))) |
| 218 | (format ", %s" packaging) |
| 219 | ""))) |
| 220 | |
| 221 | (defun emms-idapi-browser-print-entry-artist (entry) |
| 222 | "Return artist ENTRY." |
| 223 | (format "%s%s%s\n\n" |
| 224 | (alist-get 'info-artist entry) |
| 225 | (if (alist-get 'info-country entry) |
| 226 | (format " (%s) " (alist-get 'info-country entry)) |
| 227 | "") |
| 228 | (let ((begin (alist-get 'begin (alist-get 'info-time entry))) |
| 229 | (end (alist-get 'end (alist-get 'info-time entry)))) |
| 230 | (format "%s%s" |
| 231 | (if begin begin "") |
| 232 | (if end (format " - %s, " end) ""))))) |
| 233 | |
| 234 | (defun emms-idapi-browser-print-entry (entry) |
| 235 | "Print ENTRY." |
| 236 | (cond ((equal 'idapi-release (alist-get 'type entry)) |
| 237 | (insert (format "\"%s\" by %s%s\n" |
| 238 | (alist-get 'info-album entry) |
| 239 | (alist-get 'info-artist entry) |
| 240 | (if (alist-get 'info-date entry) |
| 241 | (format ", released on %s" (alist-get 'info-date entry)) |
| 242 | ""))) |
| 243 | |
| 244 | (insert (format "%s tracks%s%s\n\n" |
| 245 | (alist-get 'info-track-count entry) |
| 246 | (emms-idapi-browser-entry-packaging entry) |
| 247 | (if (alist-get 'info-country entry) |
| 248 | (format ", (%s)" (alist-get 'info-country entry)) |
| 249 | "")))) |
| 250 | |
| 251 | ((equal 'idapi-artist (alist-get 'type entry)) |
| 252 | (insert (emms-idapi-browser-print-entry-artist entry))) |
| 253 | |
| 254 | (t (insert (format "unhandled entry:\n\n%s\n" entry))))) |
| 255 | |
| 256 | (defun emms-idapi-browser-show (response) |
| 257 | "Display RESPONSE in a search buffer." |
| 258 | (emms-idapi-browser-write-debug response) |
| 259 | (let ((buffer (emms-idapi-browser-get-buffer))) |
| 260 | (pop-to-buffer buffer) |
| 261 | (let ((inhibit-read-only t)) |
| 262 | (erase-buffer) |
| 263 | (insert "Emms Music Search Query\n\n") |
| 264 | (emms-idapi-browser-print-header (car response)) |
| 265 | (insert (format "%d results\n\n" (length (cdr response)))) |
| 266 | (dolist (e (cdr response)) |
| 267 | (emms-idapi-browser-print-entry e))))) |
| 268 | |
| 269 | |
| 270 | (provide 'emms-idapi-browser) |
| 271 | |
| 272 | ;;; emms-idapi-browser.el ends here |