X-Git-Url: http://git.vanrenterghem.biz/musicbrainz.git/blobdiff_plain/21ade49d85a5e2df2ee02e66b8320fefcc1e26f9..006cf0949378dc3f0c3368f0e2e29093ec67e4da:/musicbrainz.el diff --git a/musicbrainz.el b/musicbrainz.el index 4107f4e..e49f6ae 100644 --- a/musicbrainz.el +++ b/musicbrainz.el @@ -27,17 +27,23 @@ ;;; Commentary: -;; - basic MusicBrainz interface -;; - partial & incomplete -;; - no error checks -;; - sync -> async - +;; An interface to the MusicBrainz "open music encyclopedia" collection +;; of music metadata. The main entry points are `musicbrainz-search' for +;; general searches and `musicbrainz-lookup' for the more specific. +;; There are also some narrower searches such as `musicbrainz-search-artist' +;; +;; Naming follows the MusicBrainz API reasonably closely, so the official API +;; documentation can provide insight into how searching, browsing and lookups +;; are structured. MusicBrainz has it's particular taxonomy and quirks, so +;; some familiarity may be required to get useful results in some cases. +;; +;; https://musicbrainz.org/doc/MusicBrainz_API ;;; Code: (require 'request) (require 'json) - +(require 'pp) ;;; ;; ;; ; ; ; ; ; ; ;; @@ -46,16 +52,28 @@ ;;; ; ;; ;; (defcustom musicbrainz-api-url "https://musicbrainz.org/ws/2" - "URL for musicbrainz API. + "URL for MusicBrainz API. Documentation available at https://musicbrainz.org/doc/MusicBrainz_API" :type 'string :group 'musicbrainz) +(defcustom musicbrainz-coverart-api-url "http://coverartarchive.org" + "URL for MusicBrainz Cover Art Archive API. +Documentation available at https://musicbrainz.org/doc/Cover_Art_Archive/API" + :type 'string + :group 'musicbrainz) + (defcustom musicbrainz-api-token "" "An auth token is required for some functions." :type 'string :group 'musicbrainz) +(defcustom musicbrainz-user-agent "musicbrainz.el/0.1" + "A User-Agent header to identify source of API requests. +As seen in https://wiki.musicbrainz.org/MusicBrainz_API/Rate_Limiting" + :type 'string + :group 'musicbrainz) + ;;; ; ; ;;; ; ; ;; ;; API entities @@ -103,6 +121,12 @@ Documentation available at https://musicbrainz.org/doc/MusicBrainz_API" "series" "tag" "work" "url") "Valid TYPE parameters for MusicBrainz searches.") +(defconst musicbrainz-relationships + (list "area-rels" "artist-rels" "event-rels" "instrument-rels" + "label-rels" "place-rels" "recording-rels" "release-rels" + "release-group-rels" "series-rels" "url-rels" "work-rels") + "Valid relationships for lookups.") + ;; entity checks @@ -159,18 +183,27 @@ An MBID is a 36 character Universally Unique Identifier, see https://musicbrainz (string-match-p (rx (repeat 8 hex) ;; [A-F0-9]{8} "-" (repeat 4 hex) ;; -[A-F0-9]{4} - "-4" (repeat 3 hex) ;; -4[A-F0-9]{3} + "-" (repeat 4 hex) ;; -[34][A-F0-9]{3} "-" (repeat 4 hex) ;; -[89AB][A-F0-9]{3} "-" (repeat 12 hex)) ;; -[A-F0-9]{12} mbid)) t nil)) +;; https://lucene.apache.org/core/4_3_0/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#Escaping_Special_Characters +(defconst musicbrainz-qeury-special-chars + (list "+" "-" "&" "|" "!" "(" ")" "{" "}" "[" "]" "^" "\"" "~" "*" "?" ":" "\\" "/")) + (defun musicbrainz-format (response) "Format a generic RESPONSE." (format "%s" (pp response))) +(defun musicbrainz--unwrap-0 (entity) + "Unwrap (fragile) .artist-credit ENTITY -> .name more or less." + (format "%s" (cdar (aref entity 0)))) + + ;;; ;; ;; ; ; ; ; ; ; ;; ;; Search API @@ -212,7 +245,9 @@ The QUERY field supports the full Lucene Search syntax, some details can be found near https://musicbrainz.org/doc/MusicBrainz_API/Search or in the Lucene docs." - (message "musicbrainz: searching %s=%s" type query) + (interactive "sMusicBrainz search type: \nsMusicBrainz search query: ") + (message "MusicBrainz: searching %s=%s" type query) + ;; queries may need to be escaped (let* ((max (if limit limit 1)) (from (if offset offset "")) (response @@ -222,46 +257,88 @@ or in the Lucene docs." (format "%s/%s?query=%s&fmt=json&limit=%s&offset=%s" musicbrainz-api-url type query max from)) :type "GET" + :headers (list `("User-Agent" . ,musicbrainz-user-agent)) :parser 'json-read :sync t - :success (cl-function - (lambda (&key data &allow-other-keys) - (message "ok"))))))) - response)) - + :sucess (cl-function + (lambda (&key data &allow-other-keys) + (message "ok: %s" data))))))) + (if (called-interactively-p 'any) + (message "%s" (pp response)) + response))) ;;;###autoload (defun musicbrainz-find (query &rest extras) - "Search the MusicBrainz database for QUERY or recommend a more specific search. -MusicBrainz makes a distinction between `search' and `browse' this a more general -entry point to searching/browsing the database. + "Search MusicBrainz for QUERY (and EXTRAS) or recommend a more specific search. +MusicBrainz makes a distinction between `search', `lookup' and `browse' this +provides a more general entry point to searching/browsing the database. Heuristics. +- not yet - if QUERY is an MBID, check artist, recording, etc - if QUERY is text, search for artists or recordings, etc" - (message "musicbrainz: finding: %s" query) + (message "MusicBrainz: query %s %s" query (if extras extras "")) (if (musicbrainz-mbid-p query) ;; search (lookup) for things that could have an mbid (let ((mbid query)) - (message "searching mbid: %s" mbid)) - ;; search (query) for other things - (progn - (message "searching other: %s" mbid) - ;; (message "searching artist: %s" query) - ;; (musicbrainz-format (musicbrainz-search "artist" query)) - ;; (message "searching label: %s" query) - ;; (musicbrainz-format (musicbrainz-search "label" query)) - ;; (message "searching release: %s" query) - ;; (musicbrainz-format (musicbrainz-search "release" query)) - ))) + (message "searching mbid: %s" mbid) + ;; search (search/browse/query) for other things + (progn + (message "searching other: %s" mbid))))) + + +;; generate search functions + +(defmacro musicbrainz--defsearch-1 (name format-string format-args) + "Generate search function to format a single item. +NAME FORMAT-STRING FORMAT-ARGS +See listenbrainz--deformatter for details." + (let ((f (intern (concat "musicbrainz-search-" name))) + (doc (format "Search for %s using QUERY and show matches. +Optionally return LIMIT number of results." name))) + `(defun ,f (query &optional limit) ,doc + (let* ((max (if limit limit 1)) + (response + (musicbrainz-search ,name query max))) + (let-alist response + (format ,format-string ,@format-args)))))) + + +(defmacro musicbrainz--defsearch-2 (name format-string format-args alist) + "Generate lookup function to format multiple items. +NAME FORMAT-STRING FORMAT-ARGS ALIST +See listenbrainz--deformatter for details." + (let ((f (intern (concat "musicbrainz-search-" name))) + (doc (format "Search for %s using QUERY and show matches. +Optionally return LIMIT number of results." name))) + `(defun ,f (query &optional limit) ,doc + (let* ((max (if limit limit 1)) + (response + (musicbrainz-search ,name query max))) + (let-alist response + (seq-map + (lambda (i) + (let-alist i + (format ,format-string ,@format-args))) + ,alist)))))) +;; various specific searches +;; search -> musicbrainz-search-annotation +(musicbrainz--defsearch-2 "annotation" + "%s | %s | %s | %s | [[https://musicbrainz.org/%s/%s][%s]] |\n" + (.score .type .name .text .type .entity .entity) + .annotations) + +;; search -> musicbrainz-search-area +(musicbrainz--defsearch-2 "area" + "%s | [[https://musicbrainz.org/area/%s][%s]] |\n" + (.name .id .id) + .areas) -;; various specific searches -;;;###autoload (defun musicbrainz-search-artist (artist &optional limit) "Search for an ARTIST and show matches. Optionally return LIMIT number of results." @@ -278,7 +355,6 @@ Optionally return LIMIT number of results." .artists)))) -;;;###autoload (defun musicbrainz-artist-to-mbid (artist) "Find an MBID for ARTIST (with 100% match). See `musicbrainz-disambiguate-artist' if there are multiple matches." @@ -292,7 +368,6 @@ See `musicbrainz-disambiguate-artist' if there are multiple matches." .artists)))))) -;;;###autoload (defun musicbrainz-disambiguate-artist (artist &optional limit) "More ARTIST data. less ambiguity (with optional LIMIT). Outputs an `org-mode' table with descriptions and MBID link to artists pages." @@ -308,7 +383,19 @@ Outputs an `org-mode' table with descriptions and MBID link to artists pages." .artists))))) -;;;###autoload +;; search -> musicbrainz-search-event +(musicbrainz--defsearch-2 "event" + "%s | [[https://musicbrainz.org/event/%s][%s]] |\n" + (.name .id .id) + .events) + +;; search -> musicbrainz-search-instrument +(musicbrainz--defsearch-2 "instrument" + "| %s | %s | [[https://musicbrainz.org/instrument/%s][%s]] |\n" + (.name .type .id .id) + .instruments) + + (defun musicbrainz-search-label (label &optional limit) "Search for a LABEL and show matches. Optionally return LIMIT number of results." @@ -332,38 +419,47 @@ Optionally return LIMIT number of results." .labels)))) -;;;###autoload -(defun musicbrainz-search-recording (query &optional limit) - "Search for a recording using QUERY and show matches. -Optionally return LIMIT number of results." - (let ((data (musicbrainz-search "recording" query limit))) - (let-alist - data - (seq-map - (lambda (i) - (let-alist i - (format "%s | %s, %s | [[https://musicbrainz.org/release/%s][%s]] |\n" - .score .title (musicbrainz--unwrap-0 .artist-credit) .id .id))) - .recordings)))) - - -(defun musicbrainz--unwrap-0 (entity) - "Unwrap (fragile) .artist-credit ENTITY -> .name more or less." - (format "%s" (cdar (aref entity 0)))) - -;;;###autoload -(defun musicbrainz-search-release (query &optional limit) - "Search for a release using QUERY and show matches. -Optionally return LIMIT number of results." - (let ((data (musicbrainz-search "release" query limit))) - (let-alist - data - (seq-map - (lambda (i) - (let-alist i - (format "%s | %s, %s | [[https://musicbrainz.org/release/%s][%s]] |\n" - .score .title (musicbrainz--unwrap-0 .artist-credit) .id .id))) - .releases)))) +;; search -> musicbrainz-search-place +(musicbrainz--defsearch-2 "place" + "%s | [[https://musicbrainz.org/place/%s][%s]] |\n" + (.name .id .id) + .places) + +;; search -> musicbrainz-search-recording +(musicbrainz--defsearch-2 "recording" + "%s | %s, %s | [[https://musicbrainz.org/recording/%s][%s]] |\n" + (.score .title (musicbrainz--unwrap-0 .artist-credit) .id .id) + .recordings) + +;; search -> musicbrainz-search-release +(musicbrainz--defsearch-2 "release" + "%s | %s | %s | [[https://musicbrainz.org/release/%s][%s]] |\n" + (.score .title (musicbrainz--unwrap-0 .artist-credit) .id .id) + .releases) + +;; search -> musicbrainz-search-release-group +(musicbrainz--defsearch-2 "release-group" + "%s | %s | %s | [[https://musicbrainz.org/release-group/%s][%s]] |\n" + (.first-release-date .title .primary-type .id .id) + .release-groups) + +;; search -> musicbrainz-search-series +(musicbrainz--defsearch-2 "series" + "%s | [[https://musicbrainz.org/series/%s][%s]] |\n" + (.name .id .id) + .series) + +;; search -> musicbrainz-search-work +(musicbrainz--defsearch-2 "work" + "%s | %s | [[https://musicbrainz.org/work/%s][%s]] |\n" + (.score .title .id .id) + .works) + +;; search -> musicbrainz-search-url +(musicbrainz--defsearch-2 "url" + "%s | [[%s][%s]] | [[https://musicbrainz.org/url/%s][%s]] |\n" + (.score .resource .resource .id .id) + .urls) ;;; ;; ;; ; ; ; ; ; ; @@ -375,26 +471,27 @@ Optionally return LIMIT number of results." ;;;###autoload (defun musicbrainz-lookup (entity mbid &optional inc) - "Search (lookup not browse) the MusicBrainz database for ENTITY with MBID. + "Search (lookup not browse) MusicBrainz for ENTITY with MBID. Optionally add an INC list. Subqueries /ws/2/area - /ws/2/artist recordings, releases, release-groups, works - /ws/2/collection user-collections (includes private collections, requires authentication) + /ws/2/artist recordings, releases, release-groups, works + /ws/2/collection user-collections (requires authentication) /ws/2/event /ws/2/genre /ws/2/instrument - /ws/2/label releases + /ws/2/label releases /ws/2/place - /ws/2/recording artists, releases, isrcs, url-rels - /ws/2/release artists, collections, labels, recordings, release-groups - /ws/2/release-group artists, releases + /ws/2/recording artists, releases, isrcs, url-rels + /ws/2/release artists, collections, labels, recordings, release-groups + /ws/2/release-group artists, releases /ws/2/series /ws/2/work /ws/2/url" - (message "musicbrainz: lookup: %s/%s" entity mbid) + (interactive "sMusicBrainz entity type: \nsMusicBrainz MBID for entity: ") + (message "MusicBrainz: lookup: %s/%s" entity mbid) (if (and (musicbrainz-core-entity-p entity) (musicbrainz-mbid-p mbid)) (let* ((add (if inc inc "")) @@ -409,64 +506,233 @@ Subqueries :sync t :success (cl-function (lambda (&key data &allow-other-keys) - (message "%s data: %s" entity mbid))))))) - response) - (error "MusicBrainz: search requires a valid MBID and entity (i.e. one of %s)" - musicbrainz-entities-core))) + (when data + (message "%s data: %s" entity mbid)))))))) + (if (called-interactively-p 'any) + (message "%s" (pp response)) + response)) + (user-error "MusicBrainz: search requires a valid MBID and entity (i.e. one of %s)" + musicbrainz-entities-core))) + +;; relationship lookups + +(defun musicbrainz-relations (entity relation mbid) + "Lookup relationships of type RELATION to ENTITY with MBID." + ;; no sanity and/or error checks + (musicbrainz-lookup entity mbid (format "%s-rels" relation))) + + +;; specific MBID lookup requests & subrequests (limited to 25 results?) + +(defmacro musicbrainz--deflookup-1 (name format-string format-args) + "Generate lookup function to format a single item. +NAME FORMAT-STRING FORMAT-ARGS +See listenbrainz--deformatter for details." + (let ((f (intern (concat "musicbrainz-lookup-" name))) + (doc "MusicBrainz lookup.") + (prompt (format "sMusicBrainz lookup %s by MBID: " name))) + `(defun ,f (mbid) ,doc + (interactive ,prompt) + (let ((response + (musicbrainz-lookup ,name mbid))) + (let-alist response + (format ,format-string ,@format-args)))))) + + +(defmacro musicbrainz--deflookup-2 (query subquery format-string format-args alist) + "Generate lookup function to format multiple items. +QUERY SUBQUERY FORMAT-STRING FORMAT-ARGS ALIST +See listenbrainz--deformatter for details." + (let ((f (intern (format "musicbrainz-lookup-%s-%s" query subquery))) + (doc "MusicBrainz lookup.") + (prompt (format "sMusicBrainz lookup %s %s by MBID: " query subquery))) + `(defun ,f (mbid) ,doc + (interactive ,prompt) + (let ((response + (musicbrainz-lookup ,query mbid ,subquery))) + (let-alist response + (seq-map + (lambda (i) + (let-alist i + (format ,format-string ,@format-args))) + ,alist)))))) + + +;; (defun musicbrainz-lookup-artist (mbid) +;; "MusicBrainz lookup for artist with MBID." +;; (let ((response +;; (musicbrainz-lookup "artist" mbid))) +;; (let-alist response +;; (format "| %s | %s | %s | [[https://musicbrainz.org/artist/%s][%s]] |\n" +;; .name .disambiguation .type .id .id)))) + + +;; (defun musicbrainz-lookup-artist-recordings (mbid) +;; "MusicBrainz lookup for recordings from artist with MBID." +;; (let ((response +;; (musicbrainz-lookup "artist" mbid "recordings"))) +;; (let-alist response +;; (seq-map +;; (lambda (i) +;; (let-alist i +;; (format "%s | [[https://musicbrainz.org/recording/%s][%s]] |\n" +;; .title .id .id))) +;; .recordings)))) + + +;; lookup -> musicbrainz-lookup-area +(musicbrainz--deflookup-1 "area" + "| %s | [[https://musicbrainz.org/area/%s][%s]] |\n" + (.name .id .id)) + +;; lookup -> musicbrainz-lookup-artist +(musicbrainz--deflookup-1 "artist" + "| %s | %s | %s | [[https://musicbrainz.org/artist/%s][%s]] |\n" + (.name .disambiguation .type .id .id)) + +;; lookup -> musicbrainz-lookup-artist-recordings +(musicbrainz--deflookup-2 "artist" "recordings" + "%s | [[https://musicbrainz.org/recording/%s][%s]] |\n" + (.title .id .id) + .recordings) + +;; lookup -> musicbrainz-lookup-artist-releases +(musicbrainz--deflookup-2 "artist" "releases" + "%s | %s | %s | [[https://musicbrainz.org/release/%s][%s]] |\n" + (.date .title .packaging .id .id) + .releases) + +;; lookup -> musicbrainz-lookup-artist-release-groups +(musicbrainz--deflookup-2 "artist" "release-groups" + "%s | %s | %s | [[https://musicbrainz.org/release-group/%s][%s]] |\n" + (.first-release-date .title .primary-type .id .id) + .release-groups) + +;; lookup -> musicbrainz-lookup-artist-works +(musicbrainz--deflookup-2 "artist" "works" + " %s | [[https://musicbrainz.org/work/%s][%s]] |\n" + (.title .id .id) + .works) + +;; lookup -> musicbrainz-lookup-collection +(musicbrainz--deflookup-1 "collection" + "| %s | [[https://musicbrainz.org/collection/%s][%s]] |\n" + (.name .id .id)) + +;; lookup -> musicbrainz-lookup-collection-user-collections (requires authentication) +(musicbrainz--deflookup-2 "collection" "user-collections" + " %s | [[https://musicbrainz.org/collection/%s][%s]] |\n" + (.name .id .id) + .collection) + +;; lookup -> musicbrainz-lookup-event +(musicbrainz--deflookup-1 "event" + "| %s | [[https://musicbrainz.org/event/%s][%s]] |\n" + (.name .id .id)) + +;; lookup -> musicbrainz-lookup-genre +(musicbrainz--deflookup-1 "genre" + "| %s | [[https://musicbrainz.org/genre/%s][%s]] |\n" + (.name .id .id)) + +;; lookup -> musicbrainz-lookup-instrument +(musicbrainz--deflookup-1 "instrument" + "| %s | %s | [[https://musicbrainz.org/instrument/%s][%s]] |\n" + (.name .type .id .id)) + +;; lookup -> musicbrainz-lookup-label +(musicbrainz--deflookup-1 "label" + "| %s | %s | [[https://musicbrainz.org/label/%s][%s]] |\n" + (.name .disambiguation .id .id)) + + +;; lookup -> musicbrainz-lookup-label-releases +(musicbrainz--deflookup-2 "label" "releases" + "%s | %s | [[https://musicbrainz.org/release/%s][%s]] |\n" + (.date .title .id .id) + .releases) + +;; lookup -> musicbrainz-lookup-place +(musicbrainz--deflookup-1 "place" + "| %s | [[https://musicbrainz.org/place/%s][%s]] |\n" + (.name .id .id)) + +;; lookup -> musicbrainz-lookup-recording +(musicbrainz--deflookup-1 "recording" + "| %s | %s | [[https://musicbrainz.org/recording/%s][%s]] |\n" + (.first-release-date .title .id .id)) + + +;; lookup -> musicbrainz-lookup-recording-artists +(musicbrainz--deflookup-2 "recording" "artists" + "%s | [[https://musicbrainz.org/artist/%s][%s]] |\n" + (.artist.name .artist.id .artist.id) + .artist-credit) + +;; lookup -> musicbrainz-lookup-recording-releases +(musicbrainz--deflookup-2 "recording" "releases" + "%s | %s | %s | [[https://musicbrainz.org/release/%s][%s]] |\n" + (.date .title .packaging .id .id) + .releases) + +;; lookup -> musicbrainz-lookup-recording-isrcs +(musicbrainz--deflookup-2 "recording" "isrcs" + "%s | [[https://musicbrainz.org/isrc/%s][%s]] |\n" + (.name .id .id) + .isrcs) + +;; lookup -> musicbrainz-lookup-recording-url-rels +(musicbrainz--deflookup-2 "recording" "url-rels" + "%s | [[https://musicbrainz.org/recording/%s][%s]] |\n" + (.name .id .id) + .relations) + +;; lookup -> musicbrainz-lookup-release +(musicbrainz--deflookup-1 "release" + "| %s | %s | %s | [[https://musicbrainz.org/release/%s][%s]] |\n" + (.date .title .packaging .id .id)) + +;; lookup -> musicbrainz-lookup-release-artists +(musicbrainz--deflookup-2 "release" "artists" + "%s | [[https://musicbrainz.org/artist/%s][%s]] |\n" + (.artist.name .artist.id .artist.id) + .artist-credit) + +;; lookup -> musicbrainz-lookup-release-collections + +;; lookup -> musicbrainz-lookup-release-labels + +;; lookup -> musicbrainz-lookup-release-recordings + +;; lookup -> musicbrainz-lookup-release-release-groups + +;; lookup -> musicbrainz-lookup-release-group +(musicbrainz--deflookup-1 "release-group" + "| %s | %s | %s | [[https://musicbrainz.org/release-group/%s][%s]] |\n" + (.first-release-date .title .primary-type .id .id)) + +;; lookup -> musicbrainz-lookup-release-group-artists +(musicbrainz--deflookup-2 "release-group" "artists" + "%s | [[https://musicbrainz.org/artist/%s][%s]] |\n" + (.artist.name .artist.id .artist.id) + .artist-credit) + +;; lookup -> musicbrainz-lookup-release-group-releases +;; lookup -> musicbrainz-lookup-series +(musicbrainz--deflookup-1 "series" + "| %s | [[https://musicbrainz.org/series/%s][%s]] |\n" + (.title .id .id)) -;; specific MBID subrequests (limited to 25 results?) - -(defun musicbrainz-lookup-artist (mbid) - "MusicBrainz lookup for artist with MBID." - (let ((response - (musicbrainz-lookup "artist" mbid))) - (let-alist response - (format "| %s | %s | %s | [[https://musicbrainz.org/artist/%s][%s]] |\n" - .name .disambiguation .type .id .id)))) +;; lookup -> musicbrainz-lookup-work +(musicbrainz--deflookup-1 "work" + "| %s | [[https://musicbrainz.org/work/%s][%s]] |\n" + (.title .id .id)) - -(defun musicbrainz-lookup-release (mbid) - "MusicBrainz lookup for release with MBID." - (let ((response - (musicbrainz-lookup "release" mbid))) - (let-alist response - (format "| %s | %s | %s | [[https://musicbrainz.org/release/%s][%s]] |\n" - .date .title .packaging .id .id)))) - -(defun musicbrainz-lookup-recording (mbid) - "MusicBrainz lookup for recording with MBID." - (let ((response - (musicbrainz-lookup "recording" mbid))) - (let-alist response - (format "%s | [[https://musicbrainz.org/recording/%s][%s]] |\n" - .title .id .id)))) - - -(defun musicbrainz-lookup-artist-releases (mbid) - "MusicBrainz lookup for releases from artist with MBID." - (let ((response - (musicbrainz-lookup "artist" mbid "releases"))) - (let-alist response - (seq-map - (lambda (i) - (let-alist i - (format "%s | %s | %s | [[https://musicbrainz.org/release/%s][%s]] |\n" - .date .title .packaging .id .id))) - .releases)))) - - -(defun musicbrainz-lookup-artist-recordings (mbid) - "MusicBrainz lookup for recordings from artist with MBID." - (let ((response - (musicbrainz-lookup "artist" mbid "recordings"))) - (let-alist response - (seq-map - (lambda (i) - (let-alist i - (format "%s | [[https://musicbrainz.org/recording/%s][%s]] |\n" - .title .id .id))) - .recordings)))) +;; lookup -> musicbrainz-lookup-url +(musicbrainz--deflookup-1 "url" + "| %s | [[https://musicbrainz.org/url/%s][%s]] |\n" + (.name .id .id)) @@ -501,7 +767,7 @@ Subqueries (defun musicbrainz-browse (entity link query &optional type) "Search the MusicBrainz database for ENTITY with LINK matching QUERY. Optionally limit the search to TYPE results for ENTITY." - (message "musicbrainz: browsing %s linked to %s" entity link) + (message "MusicBrainz: browsing %s linked to %s" entity link) (message "url: %s/%s?%s=%s&type=%s&fmt=json" musicbrainz-api-url entity link query type) (let ((response (request-response-data @@ -509,15 +775,89 @@ Optionally limit the search to TYPE results for ENTITY." (url-encode-url (format "%s/%s?%s=%s&type=%s&fmt=json" musicbrainz-api-url entity link query type)) :type "GET" + :header (list `("User-Agent" . ,musicbrainz-user-agent)) + :parser 'json-read + :sync t + :success (cl-function + (lambda (&key data &allow-other-keys) + (message "ok: %s" (if data data "")))))))) + response)) + + + +;;;;;; ; ; ;; ; ; ; ; ; ;; ; +;; +;; Cover Art Archive API +;; https://musicbrainz.org/doc/Cover_Art_Archive/API +;; +;;;; ; ; ; ; ; + +;; /release/{mbid}/ +;; /release/{mbid}/front +;; /release/{mbid}/back +;; /release/{mbid}/{id} +;; /release/{mbid}/({id}|front|back)-(250|500|1200) +;; +;; /release-group/{mbid}/ +;; /release-group/{mbid}/front[-(250|500|1200)] + +;;;###autoload +(defun musicbrainz-coverart (mbid &optional release-group) + "Search MusicBrainz Cover Art Archive for release MBID. +When RELEASE-GROUP is non-nil MBID is for a release group, rather than release." + (message "MusicBrainz: cover art for %s" mbid) + (message "url: %s/release/%s" musicbrainz-coverart-api-url mbid) + (let ((response + (request-response-data + (request + (url-encode-url + (format "%s/release%s/%s" + musicbrainz-coverart-api-url + (if release-group "-group" "") + mbid)) + :type "GET" + :header (list `("User-Agent" . ,musicbrainz-user-agent)) :parser 'json-read :sync t :success (cl-function (lambda (&key data &allow-other-keys) - (message "ok"))))))) + (message "ok: %s" (if data data "")))))))) response)) +(defun musicbrainz-coverart-file-front (mbid) + "Get the MusicBrainz Cover Art front cover file for MBID." + (message "MusicBrainz: cover art (front) for %s" mbid) + (message "url: %s/release/%s/front" musicbrainz-coverart-api-url mbid) + (let ((response + (request-response-data + (request + (url-encode-url + (format "%s/release/%s/front" musicbrainz-coverart-api-url mbid)) + :type "GET" + :header (list `("User-Agent" . ,musicbrainz-user-agent)) + :sync t + :success (cl-function + (lambda (&key data &allow-other-keys) + (message "ok: %s" (if data data "")))))))) + response)) + +(defun musicbrainz-coverart-file-back (mbid) + "Get the MusicBrainz Cover Art back cover file for MBID." + (message "MusicBrainz: cover art (back) for %s" mbid) + (message "url: %s/release/%s/back" musicbrainz-coverart-api-url mbid) + (let ((response + (request-response-data + (request + (url-encode-url + (format "%s/release/%s/back" musicbrainz-coverart-api-url mbid)) + :type "GET" + :header (list `("User-Agent" . ,musicbrainz-user-agent)) + :sync t + :success (cl-function + (lambda (&key data &allow-other-keys) + (message "ok: %s" (if data data "")))))))) + response)) -;;; (provide 'musicbrainz)