From ecb0f1de27ba2511aa22ac781603831c9f6822ec Mon Sep 17 00:00:00 2001 From: pinoaffe Date: Fri, 30 May 2025 08:46:32 +0200 Subject: [PATCH] Add genres field to songs --- README.org | 2 ++ libmpdel.el | 49 +++++++++++++++++++++++++++++++++++++++++++ test/libmpdel-test.el | 2 ++ 3 files changed, 53 insertions(+) diff --git a/README.org b/README.org index 7746a88..7632bb0 100644 --- a/README.org +++ b/README.org @@ -45,6 +45,7 @@ The library is implemented around a set of entities. | song | structure | name, album, file, … | | | album | structure | name, artist | | | artist | structure | name | | +| genre | structure | name | | | directory | structure | name, path | | | stored-playlist | structure | name | A named user-specified sequence of songs | | search-criteria | structure | type, what | Read the [[https://www.musicpd.org/doc/protocol/database.html][protocol documentation]] | @@ -52,6 +53,7 @@ The library is implemented around a set of entities. |------------------+-----------+----------------------+---------------------------------------------------------| | artists | symbol | /none/ | Represent the set of all artists | | albums | symbol | /none/ | Represent the set of all albums | +| genres | symbol | /none/ | Represent the set of all genres | | directories | symbol | /none/ | Represent all directories in ~libmpdel-music-directory~ | | current-playlist | symbol | /none/ | Represent the currently played sequence of songs | | stored-playlists | symbol | /none/ | Represent the set of all stored playlists | diff --git a/libmpdel.el b/libmpdel.el index f2601fb..5a6ffca 100644 --- a/libmpdel.el +++ b/libmpdel.el @@ -181,6 +181,7 @@ message from the server.") (file nil :read-only t) (album nil :read-only t) (performers nil :read-only t) + (genres nil :read-only t) (disc nil :read-only t) (date nil :read-only t) (id nil :read-only t) @@ -191,6 +192,11 @@ message from the server.") (:conc-name libmpdel--stored-playlist-)) (name nil :read-only t)) +(cl-defstruct (libmpdel-genre + (:constructor libmpdel--genre-create) + (:conc-name libmpdel--genre-)) + (name nil :read-only t)) + (cl-defstruct (libmpdel-search-criteria (:constructor libmpdel-search-criteria-create) (:conc-name libmpdel--search-criteria-)) @@ -240,6 +246,14 @@ message from the server.") "Return SONG's performers." (libmpdel--song-performers song)) +(cl-defmethod libmpdel-genres ((song libmpdel-song)) + "Return SONG's genres." + (libmpdel--song-genres song)) + +(cl-defmethod libmpdel-genres ((genre libmpdel-genre)) + "Return singleton list GENRE." + (list genre)) + (cl-defgeneric libmpdel-entity-name (entity) "Return the name of ENTITY.") @@ -251,6 +265,10 @@ message from the server.") "Return ALBUM's name." (libmpdel--album-name album)) +(cl-defmethod libmpdel-entity-name ((genre libmpdel-genre)) + "Return GENRE's name." + (libmpdel--genre-name genre)) + (cl-defmethod libmpdel-entity-name ((song libmpdel-song)) "Return SONG's name. @@ -270,6 +288,10 @@ If the SONG's name is nil, return the filename instead." "Return a string describing the `albums' entity." "All albums") +(cl-defmethod libmpdel-entity-name ((_entity (eql genres))) + "Return a string describing the `genres' entity." + "All genres") + (cl-defmethod libmpdel-entity-name ((_entity (eql current-playlist))) "Return a string describing the `current-playlist' entity." "Current playlist") @@ -300,6 +322,10 @@ If the SONG's name is nil, return the filename instead." "Return ALBUM's artist." (libmpdel-artist album)) +(cl-defmethod libmpdel-entity-parent ((_genre libmpdel-genre)) + "Return the `genres' entity." + 'genres) + (cl-defmethod libmpdel-entity-parent ((_artist libmpdel-artist)) "Return the `artists' entity." 'artists) @@ -350,6 +376,12 @@ If the SONG's name is nil, return the filename instead." (libmpdel--artist-create :name name)) artist-names)) +(defun libmpdel--genres-create (genre-names) + "Return a list of genres whose names are GENRE-NAMES." + (mapcar (lambda (name) + (libmpdel--genre-create :name name)) + genre-names)) + (defun libmpdel--create-song-from-data (song-data) "Return a song from SONG-DATA, a server's response." (libmpdel--song-create @@ -357,6 +389,7 @@ If the SONG's name is nil, return the filename instead." :track (cdr (assq 'Track song-data)) :file (cdr (assq 'file song-data)) :performers (libmpdel--artists-create (libmpdel-entries song-data 'Performer)) + :genres (libmpdel--genres-create (libmpdel-entries song-data 'Genre)) :album (libmpdel--album-create :name (cdr (assq 'Album song-data)) :artist (libmpdel--artist-create :name (cdr (assq 'Artist song-data)))) @@ -935,6 +968,11 @@ If HANDLER is nil, ignore response." (libmpdel-entity-to-criteria (libmpdel-artist album)) (libmpdel-entity-name album))) +(cl-defmethod libmpdel-entity-to-criteria ((genre libmpdel-genre)) + "Return search query matching all songs from GENRE." + (format "genre %S" + (libmpdel-entity-name genre))) + (cl-defmethod libmpdel-entity-to-criteria ((song libmpdel-song)) "Return search query matching SONG." (format "%s title %S" @@ -971,6 +1009,17 @@ If HANDLER is nil, ignore response." :artist libmpdel--unknown-artist)) (libmpdel-sorted-entries data 'Album)))))) +(cl-defmethod libmpdel-list ((_entity (eql genres)) function) + "Call FUNCTION with all genres as parameter." + (libmpdel-send-command + "list genre" + (lambda (data) + (funcall function + (mapcar + (lambda (genre-name) + (libmpdel--genre-create :name genre-name)) + (libmpdel-sorted-entries data 'Genre)))))) + (cl-defmethod libmpdel-list ((_entity (eql stored-playlists)) function) "Call FUNCTION with all stored playlists as parameter." (libmpdel-send-command diff --git a/test/libmpdel-test.el b/test/libmpdel-test.el index 4f23b0a..78a4a28 100644 --- a/test/libmpdel-test.el +++ b/test/libmpdel-test.el @@ -120,9 +120,11 @@ (Performer . "The Violinist") (Performer . "The Pianist") (Performer . "The Singer") + (Genre . "The Genre") (Artist . "The Artist"))))) (should (equal "The song" (libmpdel-entity-name song))) (should (equal "foo/song.ogg" (libmpdel-song-file song))) + (should (equal (list "The Genre") (mapcar #'libmpdel-entity-name (libmpdel-genres song)))) (should (equal "The Album" (libmpdel-entity-name (libmpdel-album song)))) (should (equal (list "The Violinist" "The Pianist" "The Singer") (mapcar #'libmpdel-entity-name (libmpdel-performers song))))