fordon_greeman / voidfiles

Branch: 2079ec5
Switch branches/tags
main
theidioticdev add emacs 2079ec5 23h ago
120 lines · 5.1 KB Raw Blame History
1 ;;; emms-lyrics-lrclib.el --- Fetch synchronized lyrics through LRCLIB -*- lexical-binding: t; -*-
2
3 ;; Copyright (C) 2024 Free Software Foundation, Inc.
4
5 ;; Author: Daniel Semyonov <daniel@dsemy.com>
6
7 ;; This file is part of EMMS.
8
9 ;; EMMS is free software; you can redistribute it and/or modify it
10 ;; under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 3, or (at your option)
12 ;; any later version.
13
14 ;; EMMS is distributed in the hope that it will be useful, but WITHOUT
15 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
17 ;; License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with EMMS; see the file COPYING. If not, write to the Free
21 ;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 ;; MA 02110-1301, USA.
23
24 ;;; Commentary:
25
26 ;; This file provides a command/track initialization function which
27 ;; automatically fetches synchronized lyrics for tracks (the current
28 ;; track interactively) through an LRCLIB server.
29
30 ;;; Code:
31
32 (eval-when-compile
33 (declare-function json-parse-buffer "json.c"))
34
35 (require 'emms-lyrics)
36 (require 'emms-later-do)
37
38 (defgroup emms-lyrics-lrclib nil
39 "EMMS module for fetching synchronized lyrics through LRCLIB servers."
40 :group 'emms-lyrics
41 :prefix "emms-lyrics-lrclib-")
42
43 (defcustom emms-lyrics-lrclib-url "https://lrclib.net/api/"
44 "Base URL for LRCLIB API requests."
45 :type 'string)
46
47 (defconst emms-lyrics-lrclib-max-requests 250
48 "Maximum number of concurrent requests to LRCLIB.")
49
50 (defvar emms-lyrics-lrclib-requests 0
51 "Current number of concurrent requests to LRCLIB.")
52
53 (defun emms-lyrics-lrclib-encode-name (name)
54 "Encode (artist/album/track) NAME for an LRCLIB search."
55 (and (stringp name) (string-replace " " "+" name)))
56
57 (defun emms-lyrics-lrclib-parse (_ file track interactive)
58 "Parse and save synced lyrics in FILE.
59 If TRACK is the selected track in the current playlist, catch up.
60 When INTERACTIVE is non-nil, display messages and confirm overwrite."
61 (unwind-protect
62 (let (lyrics)
63 (search-forward "\n\n")
64 (if-let* (((functionp 'json-available-p))
65 ((json-available-p))
66 (p (json-parse-buffer :null-object nil)))
67 (and (hash-table-p p) (setq lyrics (gethash "syncedLyrics" p)))
68 (when-let* ((beg (search-forward "\"syncedLyrics\":\"" nil t))
69 (end (1- (search-forward-regexp "[^\\]\"" nil t))))
70 (replace-string-in-region "\\n" "\n" beg end)
71 (setq lyrics (buffer-substring-no-properties
72 beg (1- (point))))))
73 (and lyrics interactive (file-exists-p file)
74 (not (y-or-n-p (format "Overwrite existing file (\"%s\")?" file)))
75 (setq lyrics nil))
76 (when lyrics
77 (with-temp-file file (insert lyrics))
78 (when interactive (message "Saved synced lyrics at \"%s\"" file))
79 (and (boundp 'emms-lyrics-display-p)
80 emms-lyrics-display-p emms-player-playing-p
81 (equal track (emms-playlist-current-selected-track))
82 (emms-lyrics-catchup file))))
83 (setq emms-lyrics-lrclib-requests (1- emms-lyrics-lrclib-requests))))
84
85 ;;;###autoload
86 (defun emms-lyrics-lrclib-get (&optional track force interactive)
87 "Search for synchronized lyrics for TRACK through LRCLIB's API.
88 If TRACK is omitted or nil, use the selected track in the current playlist.
89 The lyrics are saved in an \".lrc\" file alongside the track, unless the
90 file already exists (in which case the search isn't performed).
91 When called interactively (non-nil INTERACTIVE), display informative
92 messages, and with prefix argument FORCE, ask to overwrite existing
93 \".lrc\" files."
94 (interactive (list nil current-prefix-arg t))
95 (if (> emms-lyrics-lrclib-requests emms-lyrics-lrclib-max-requests)
96 (emms-later-do #'emms-lyrics-lrclib-get track force interactive)
97 (when-let* ((track (or track (emms-playlist-current-selected-track)))
98 ((eq (emms-track-type track) 'file))
99 (file (emms-track-name track))
100 (lrc (replace-regexp-in-string "\\.[^.]+\\'" ".lrc" file))
101 ((or force (not (file-exists-p lrc))))
102 ((file-writable-p lrc))
103 (title (emms-lyrics-lrclib-encode-name
104 (emms-track-get track 'info-title)))
105 (artist (emms-lyrics-lrclib-encode-name
106 (emms-track-get track 'info-artist)))
107 (album (emms-lyrics-lrclib-encode-name
108 (emms-track-get track 'info-album)))
109 (time (emms-track-get track 'info-playing-time)))
110 (setq emms-lyrics-lrclib-requests (1+ emms-lyrics-lrclib-requests))
111 (when interactive (message "Searching for lyrics..."))
112 (url-retrieve
113 (url-encode-url
114 (format "%sget?artist_name=%s&track_name=%s&album_name=%s&duration=%d"
115 emms-lyrics-lrclib-url artist title album time))
116 #'emms-lyrics-lrclib-parse (list lrc track interactive)))))
117
118 (provide 'emms-lyrics-lrclib)
119
120 ;;; emms-lyrics-lrclib.el ends here