docs: README, typos in docstrings

This commit is contained in:
Pavel Korytov 2021-08-07 17:07:19 +03:00
parent a5abc0f729
commit 5a752a2cab
4 changed files with 155 additions and 42 deletions

View file

@ -1 +1,94 @@
#+TITLE: lyrics-fetcher.el #+TITLE: lyrics-fetcher.el
A package to fetch song lyrics and album covers. Integrates with EMMS.
[[./img/screenshot.png]]
As of now, the only available backend is [[https://genius.com/][genius.com]], but in principle, new ones can be added.
* Installation
As the package isn't yet available anywhere but in this repository, you can clone the repository, add it to the =load-path= and =require= the package:
#+begin_src emacs-lisp
(require 'lyrics-fetcher)
#+end_src
My preferred way is to use =use-package= with =straight=:
#+begin_src emacs-lisp
(use-package lyrics-fetcher
:straight (:host github :repo "SqrtMinusOne/lyrics-fetcher.el")
:after (emms))
#+end_src
Install [[https://imagemagick.org/index.php][imagemagick]] if you want to download covers.
Set [[https://docs.genius.com/][genius.com]] client access token. To do that, [[https://genius.com/api-clients/new][create a new client,]] click "Generate Access Token" and put the result to the =lyrics-fetcher-genius-access-token= variable. I do this with password-store:
#+begin_src emacs-lisp
(setq lyrics-fetcher-genius-access-token
(password-store-get "My_Online/APIs/genius.com"))
#+end_src
But of course, you can just hardcode the string.
* Usage
Available commands:
- ~M-x lyrics-fetcher-show-lyrics~ - show lyrics for the current playing track.
The resulting lyric files are saved to the ~lyrics-fetcher-lyrics-folder~ and have the ~lyrics-fetcher-lyrics-file-extension~ extension. The folder will be created if it doesn't exist.
By default, the function opens an already saved lyrics file if one exists, otherwise tries to fetch the lyrics.
If called with =C-u=, then tries to fetch the text regardless of the latter.
If called with =C-u C-u=, prompts the user to select a matching song. That is helpful when there are multiple songs with similar names, and the top one isn't the right one.
- ~M-x lyrics-fetcher-show-lyrics-query~ - fetch lyrics by a text query.
Modified by =C-u= the same way as ~lyrics-fetcher-show-lyrics~.
EMMS integration:
- ~M-x lyrics-fetcher-emms-browser-show-at-point~ - fetch data for the current point in EMMS browser.
If the point contains just one song, it will be fetched the usual way and lyrics will be shown upon successful completion.
If the point contains many songs (e.g. it's an album), the lyrics will be fetched consequentially for every song. The process then will stop at the first failure.
Modified by =C-u= the same way as ~lyrics-fetcher-show-lyrics~.
- ~M-x lyrics-fetcher-emms-browser-fetch-covers-at-point~ - fetch album covers for the current point in the EMMS browser.
This functionality requires songs' directories to be grouped by albums, i.e. one album per one folder.
The files will be saved to the folder with names like "cover_small.jpg", "cover_med.jpg", "cover_large.jpg".
You can customize the sizes via the ~lyrics-fetcher-small-cover-size~ and ~lyrics-fetcher-medium-cover-size~ variables.
Modified by =C-u= the same way as ~lyrics-fetcher-show-lyrics~.
- ~M-x lyrics-fetcher-emms-browser-open-large-cover-at-point~ - open large_cover for the current point in EMMS browser.
Lyric view mode keybindings:
- =q= - close the lyrics buffer
- =r= - refetch the lyrics in the buffer
* Customization and extension
** Lyrics file naming and location
As was outlined above, lyrics files are saved to ~lyrics-fetcher-lyrics-folder~ and have an extension set in ~lyrics-fetcher-lyrics-file-extension~.
Take a look at the ~lyrics-fetcher-format-song-name-method~ and ~lyrics-fetcher-format-file-name-method~ variables if you want to customize the lyrics buffer and file naming.
** Using another player than EMMS
To use another player, customize ~lyrics-fetcher-current-track-method~.
This variable contains a function that returns the current playing track. The return format has to be either a string or (recommended) an EMMS-like alist, which has to have the following fields:
- =info-artist= or =info-albumartist=
- =info-title=
** Adding another backend
A function to perform the lyric fetching is set in ~lyrics-fetcher-fetch-method~.
The function has to receive 3 arguments:
- =track= - a string or alist, as outlined [[*Using other player than EMMS][above]].
- =callback= - the function which has to be called with the resulting lyrics string
- =sync= - if non-nil, inquire the user about the possible choices. This is called =sync= because then it is reasonable to perform the request synchronously, as otherwise, it won't be nice to suddenly throw a prompt at the user.
The album cover fetching is similar. The corresponding function is set in ~lyrics-fetcher-download-cover-method~ and has to receive the following parameters:
- =track= - as above
- =callback= - has to be called with the path to the resulting file. This file should be named =cover_large.<extension>=.
- =folder= - where the file has to be put
- =sync= - as above.
The first argument is =track= because in EMMS all the required information is stored in tracks, and album data is deduced from tracks. So this package just takes a sample track in the album.

BIN
img/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 KiB

View file

@ -43,12 +43,12 @@
(defun lyrics-fetcher-genius-do-search (track callback &optional sync) (defun lyrics-fetcher-genius-do-search (track callback &optional sync)
"Perform a lyrics search on 'genius.com'. "Perform a lyrics search on 'genius.com'.
Requies `lyrics-fetcher-genius-access-token' to be set. Requires `lyrics-fetcher-genius-access-token' to be set.
The flow is at follows: The flow is as follows:
1. Send a GET /search request with a text query 1. Send a GET /search request with a text query
2. Pick first result (or prompt user if SYNC is non-nil) 2. Pick the first result (or prompt user if SYNC is non-nil)
3. Fetch lyrics from HTML page of the result 3. Fetch lyrics from the HTML page of the result
4. Call CALLBACK with the resulting lyrics string 4. Call CALLBACK with the resulting lyrics string
TRACK should be EMMS-compatible alist or string, take a look at TRACK should be EMMS-compatible alist or string, take a look at
@ -69,14 +69,14 @@ user to pick the matching search result."
(defun lyrics-fetcher--genius-do-query (track callback &optional sync) (defun lyrics-fetcher--genius-do-query (track callback &optional sync)
"Perform a song search on genius.com. "Perform a song search on genius.com.
Requies `lyrics-fetcher-genius-access-token' to be set. Requires `lyrics-fetcher-genius-access-token' to be set.
TRACK should be EMMS-compatible alist or string, take a look at TRACK should be EMMS-compatible alist or string, take a look at
`lyrics-fetcher--genius-format-query'. If the search is `lyrics-fetcher--genius-format-query'. If the search is
successful, CALLBACK will be called with the result. successful, CALLBACK will be called with the result.
SYNC determines whether the request is syncronous. The parameter SYNC determines whether the request is synchronous. The parameter
is useful when it is neccessary to ask user for something right is useful when it is necessary to ask the user for something right
after the request." after the request."
(when (string-empty-p lyrics-fetcher-genius-access-token) (when (string-empty-p lyrics-fetcher-genius-access-token)
(error "Genius client access token not set!")) (error "Genius client access token not set!"))
@ -115,9 +115,9 @@ contains `info-albumartist' or `info-artist' and `info-title'"
(cdr (assoc 'lyrics_state result))))) (cdr (assoc 'lyrics_state result)))))
(defun lyrics-fetcher--genius-get-data-from-response (data key &optional ask) (defun lyrics-fetcher--genius-get-data-from-response (data key &optional ask)
"Retrive a song KEY from the Genius response DATA. "Retrieve a song KEY from the Genius response DATA.
If ASK is non-nil, prompt user for a choice, otherwise select the If ASK is non-nil, prompt the user for a choice, otherwise select the
first song." first song."
(when (not (= (cdr (assoc 'status (assoc 'meta data))) 200)) (when (not (= (cdr (assoc 'status (assoc 'meta data))) 200))
(error "Error: %" (cdr (assoc 'message (assoc 'meta data))))) (error "Error: %" (cdr (assoc 'message (assoc 'meta data)))))
@ -146,7 +146,7 @@ first song."
(assoc key (assoc 'result (car results-songs))))))) (assoc key (assoc 'result (car results-songs)))))))
(defun lyrics-fetcher--genius-fetch-lyrics (url callback &optional sync) (defun lyrics-fetcher--genius-fetch-lyrics (url callback &optional sync)
"Fetch lyrics from genius.com page at URL and call CALLBACK with result. "Fetch lyrics from genius.com page at URL and call CALLBACK with the result.
If SYNC is non-nil, the request will be performed synchronously." If SYNC is non-nil, the request will be performed synchronously."
(message "Getting lyrics from %s" url) (message "Getting lyrics from %s" url)
@ -174,23 +174,23 @@ If SYNC is non-nil, the request will be performed synchronously."
(defun lyrics-fetcher-genius-download-cover (track callback folder &optional sync) (defun lyrics-fetcher-genius-download-cover (track callback folder &optional sync)
"Downloads album cover of TRACK. "Downloads album cover of TRACK.
Requies `lyrics-fetcher-genius-access-token' to be set and Requires `lyrics-fetcher-genius-access-token' to be set and
imagemagick's \"convert\" to be available in PATH. imagemagick's \"convert\" to be available in PATH.
TRACK should be EMMS-compatible alist or string, take a look at TRACK should be EMMS-compatible alist or string, take a look at
`lyrics-fetcher--genius-format-query'. If the search is `lyrics-fetcher--genius-format-query'. If the search is successful,
successful, CALLBACK will be called with the resulting filename of the CALLBACK will be called with the resulting filename of the large
large cover. cover.
In EMMS, track contains all posible information about the album, so a In EMMS, the track contains all possible information about the album,
sample track is used instead of an actual album object. so a sample track is used instead of an actual album object.
The file will be saved to FOLDER and will be named The file will be saved to FOLDER and will be named
\"cover_large.<extension>\". \"cover_large.<extension>\".
CALLBACK will be called with a path to the resulting file. CALLBACK will be called with a path to the resulting file.
If SYNC is non-nil, user will be prompted for a matching song." If SYNC is non-nil, the user will be prompted for a matching song."
(lyrics-fetcher--genius-do-query (lyrics-fetcher--genius-do-query
track track
(lambda (data) (lambda (data)
@ -201,7 +201,7 @@ If SYNC is non-nil, user will be prompted for a matching song."
sync)) sync))
(defun lyrics-fetcher--genius-save-album-picture (id callback folder) (defun lyrics-fetcher--genius-save-album-picture (id callback folder)
"Save an album cover of a song of given ID. "Save an album cover of a song of a given ID.
The file will be saved to FOLDER and will be named The file will be saved to FOLDER and will be named
\"cover_large.<extension>\". \"cover_large.<extension>\".

View file

@ -1,4 +1,4 @@
;;; lyrics-fetcher.el --- fetch song lyrics -*- lexical-binding: t -*- ;;; lyrics-fetcher.el --- fetch song lyrics and album covers -*- lexical-binding: t -*-
;; Copyright (C) 2021 Korytov Pavel ;; Copyright (C) 2021 Korytov Pavel
@ -25,7 +25,8 @@
;;; Commentary: ;;; Commentary:
;; Fetch song lyrics TODO ;; A package to fetch song lyrics and album covers, mainly to use with
;; EMMS. Take a look at the package README.org for more information.
;;; Code: ;;; Code:
(require 'lyrics-fetcher-genius) (require 'lyrics-fetcher-genius)
@ -33,24 +34,29 @@
(require 'emms) (require 'emms)
(defgroup lyrics-fetcher () (defgroup lyrics-fetcher ()
"TODO Fetch lyrics." "Fetch song and album covers."
:link '(url-link :tag "GitHub" "https://github.com/SqrtMinusOne/lyrics-fetcher.el")) :link '(url-link :tag "GitHub" "https://github.com/SqrtMinusOne/lyrics-fetcher.el"))
(defcustom lyrics-fetcher-fetch-method (defcustom lyrics-fetcher-fetch-method
'lyrics-fetcher-genius-do-search 'lyrics-fetcher-genius-do-search
"A function to perform fetching. "A function to perform fetching.
As of now, only genius is available, but this is a point of As of now, genius.com is the only one available, but this is a
extensibility." point of extensibility."
:type 'function :type 'function
:options '(lyrics-fetcher-genius-do-search) :options '(lyrics-fetcher-genius-do-search)
:group 'lyrics-fetcher) :group 'lyrics-fetcher)
(defcustom lyrics-fetcher-current-track-method (defcustom lyrics-fetcher-current-track-method
'emms-playlist-current-selected-track 'emms-playlist-current-selected-track
"A function to get current playing track. "A function to get the current playing track.
By default uses the current selected track in EMMS playlist." By default uses the currently selected track in the EMMS playlist.
This function has to return either a string or (recommended) an
EMMS-like alist, which has to have the following fields:
- info-artist or info-albumartist
- info-title"
:group 'lyrics-fetcher :group 'lyrics-fetcher
:type 'function) :type 'function)
@ -62,7 +68,7 @@ By default uses the current selected track in EMMS playlist."
(defcustom lyrics-fetcher-lyrics-file-extension (defcustom lyrics-fetcher-lyrics-file-extension
".txt" ".txt"
"Default extension for lyric files." "Default extension for the lyric files."
:group 'lyrics-fetcher :group 'lyrics-fetcher
:type 'string) :type 'string)
@ -77,7 +83,7 @@ Has to receive either a string or EMMS alist. Take a look at
(defcustom lyrics-fetcher-format-file-name-method (defcustom lyrics-fetcher-format-file-name-method
'lyrics-fetcher-format-file-name 'lyrics-fetcher-format-file-name
"A function to format song name to a valid filename. "A function to format a song name to a valid filename.
Has to receive either a string or EMMS alist. Take a look at Has to receive either a string or EMMS alist. Take a look at
`lyrics-fetcher-format-file-name' for the default implementation." `lyrics-fetcher-format-file-name' for the default implementation."
@ -88,8 +94,7 @@ Has to receive either a string or EMMS alist. Take a look at
'lyrics-fetcher-genius-download-cover 'lyrics-fetcher-genius-download-cover
"A function to perform downloading album cover. "A function to perform downloading album cover.
As of now, only genius is available, but this is a point of As of now, genius.com is the only one available."
extensibility."
:type 'function :type 'function
:group 'lyrics-fetcher) :group 'lyrics-fetcher)
@ -118,14 +123,14 @@ TRACK should be either a string or EMMS alist."
(cdr (assoc 'info-title track))))) (cdr (assoc 'info-title track)))))
(defun lyrics-fetcher--prepare-string (string) (defun lyrics-fetcher--prepare-string (string)
"Prepare a STRING to be saved as a part of filename." "Prepare a STRING to be saved as a part of a filename."
(replace-regexp-in-string (replace-regexp-in-string
(rx (or "<" ">" ":" "\"" "/" "\\" "|" "?" "*")) (rx (or "<" ">" ":" "\"" "/" "\\" "|" "?" "*"))
"_" "_"
string)) string))
(defun lyrics-fetcher-format-file-name (track) (defun lyrics-fetcher-format-file-name (track)
"Convert TRACK to a vaild filename. "Convert TRACK to a valid filename.
TRACK should be either a string or EMMS alist. TRACK should be either a string or EMMS alist.
@ -161,6 +166,10 @@ otherwise performs fetch according to
`lyrics-fetcher-current-track-method'. The resulting file will be `lyrics-fetcher-current-track-method'. The resulting file will be
saved with a name from `lyrics-fetcher-format-file-name-method'. saved with a name from `lyrics-fetcher-format-file-name-method'.
Resulting lyric files are saved to the
`lyrics-fetcher-lyrics-folder' and have the
`lyrics-fetcher-lyrics-file-extension' extension
If SUPPRESS-OPEN is non-nil, don't pop up a window with lyrics. This If SUPPRESS-OPEN is non-nil, don't pop up a window with lyrics. This
is useful when performing a mass fetch. is useful when performing a mass fetch.
@ -220,7 +229,7 @@ See `lyrics-fetcher-show-lyrics' for behavior."
(cl-defun lyrics-fetcher--fetch-many (tracks &optional &key start force-fetch sync) (cl-defun lyrics-fetcher--fetch-many (tracks &optional &key start force-fetch sync)
"Fetch lyrics for every track in the TRACKS list. "Fetch lyrics for every track in the TRACKS list.
This functions calls itself recursively. START is an indicator of This function calls itself recursively. START is an indicator of
position in the list. position in the list.
FORCE-FETCH and SYNC are passed to `lyrics-fetcher-show-lyrics'." FORCE-FETCH and SYNC are passed to `lyrics-fetcher-show-lyrics'."
@ -245,17 +254,17 @@ FORCE-FETCH and SYNC are passed to `lyrics-fetcher-show-lyrics'."
;;;###autoload ;;;###autoload
(defun lyrics-fetcher-emms-browser-show-at-point () (defun lyrics-fetcher-emms-browser-show-at-point ()
"Fetch data for the current point in EMMS browser. "Fetch data for the current point in the EMMS browser.
If the point contains just one song, it will be fetched the usual way If the point contains just one song, it will be fetched the usual way
via `lyrics-fetcher-show-lyrics'. Lyrics will be show upon successful via `lyrics-fetcher-show-lyrics'. Lyrics will be shown upon successful
completion. completion.
If the point contains many songs (e.g. it's an album), the lyrics If the point contains many songs (e.g. it's an album), the lyrics
will be fetched consequentially for every song. The process stops at will be fetched consequentially for every song. The process stops at
the first failure. the first failure.
Behavior of the function is modified by \\[universal-argument] The behavior of the function is modified by \\[universal-argument]
the same way as `lyrics-fetcher-show-lyrics'." the same way as `lyrics-fetcher-show-lyrics'."
(interactive) (interactive)
(let ((data (emms-browser-bdata-at-point))) (let ((data (emms-browser-bdata-at-point)))
@ -277,14 +286,25 @@ the same way as `lyrics-fetcher-show-lyrics'."
;;;###autoload ;;;###autoload
(defun lyrics-fetcher-emms-browser-fetch-covers-at-point () (defun lyrics-fetcher-emms-browser-fetch-covers-at-point ()
"Fetch album covers for the current point in EMMS browser. "Fetch album covers for the current point in the EMMS browser.
If the point contains multiple albums, the covers will be fetched If the point contains multiple albums, the covers will be fetched
consequentially for each album. The process stops at consequentially for each album. The process stops at the first
the first failure. failure.
Behavior of the function is modified by \\[universal-argument] Requires imagemagick's \"covert\" to be available in PATH.
the same way as `lyrics-fetcher-show-lyrics'."
This requires songs' directories to be grouped by albums, i.e. one
album per one folder.
The files will be saved to the folder with names like
\"cover_small.jpg\", \"cover_med.jpg\", \"cover_large.jpg\".
You can customize the sizes via the `lyrics-fetcher-small-cover-size'
and `lyrics-fetcher-medium-cover-size' variables.
The behavior of the function is modified by \\[universal-argument] the
same way as `lyrics-fetcher-show-lyrics'."
(interactive) (interactive)
(let ((data (emms-browser-bdata-at-point))) (let ((data (emms-browser-bdata-at-point)))
(if (not data) (if (not data)
@ -330,7 +350,7 @@ One sample song is given per each album."
(defun lyrics-fetcher--save-lyrics (text filename) (defun lyrics-fetcher--save-lyrics (text filename)
"Save TEXT of lyrics in `lyrics-fetcher-lyrics-folder'. "Save TEXT of lyrics in `lyrics-fetcher-lyrics-folder'.
FILENAME shoud be given without extension." FILENAME should be given without extension."
(unless (f-exists-p lyrics-fetcher-lyrics-folder) (unless (f-exists-p lyrics-fetcher-lyrics-folder)
(f-mkdir lyrics-fetcher-lyrics-folder)) (f-mkdir lyrics-fetcher-lyrics-folder))
(f-write text 'utf-8 (lyrics-fetcher--process-filename filename))) (f-write text 'utf-8 (lyrics-fetcher--process-filename filename)))
@ -426,7 +446,7 @@ FORCE-FETCH and SYNC are passed to `lyrics-fetcher--fetch-cover'."
Call CALLBACK with the resulting filename of full cover. Call CALLBACK with the resulting filename of full cover.
If SYNC is non-nil, prompt user for a matching track. If SYNC is non-nil, prompt the user for a matching track.
If FORCE-FETCH is non-nil, always fetch regardless of whether the If FORCE-FETCH is non-nil, always fetch regardless of whether the
file exists." file exists."