X-Git-Url: http://git.vanrenterghem.biz/musicbrainz.git/blobdiff_plain/91a352486919bcf1b316273a79a5c250e1d25435..0203ac0b617c2da3d53a43f2e40dab17d9db8d9f:/musicbrainz.el diff --git a/musicbrainz.el b/musicbrainz.el index e5f41a9..506a23b 100644 --- a/musicbrainz.el +++ b/musicbrainz.el @@ -27,18 +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) - ;;; ;; ;; ; ; ; ; ; ; ;; ;; API config @@ -56,6 +61,12 @@ Documentation available at https://musicbrainz.org/doc/MusicBrainz_API" :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 +114,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 +176,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 +238,8 @@ 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) + (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,6 +249,7 @@ 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 @@ -230,38 +258,77 @@ or in the Lucene docs." 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" query) (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 + ;; search (search/browse/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 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. +QUERY SUBQUERY 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 +345,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 +358,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 +373,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." @@ -319,8 +396,8 @@ Optionally return LIMIT number of results." (lambda (i) (let-alist i (if (not limit) - (format "%s | %s |\n" .name .id) - (format "%s | %s | %s (%s%s) | %s |\n" + (format "%s | [[https://musicbrainz.org/label/%s][%s]] |\n" .name .id .id) + (format "%s | %s | %s (%s%s) | [[https://musicbrainz.org/label/%s][%s]] |\n" .score .name (if .disambiguation .disambiguation "") (if .life-span.begin @@ -328,42 +405,51 @@ Optionally return LIMIT number of results." (if .life-span.end (format "—%s" .life-span.end) "ongoing") - .id)))) + .id .id)))) .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 +461,26 @@ 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) + (message "MusicBrainz: lookup: %s/%s" entity mbid) (if (and (musicbrainz-core-entity-p entity) (musicbrainz-mbid-p mbid)) (let* ((add (if inc inc "")) @@ -414,59 +500,221 @@ Subqueries (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.")) + `(defun ,f (mbid) ,doc + (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.")) + `(defun ,f (mbid) ,doc + (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 -;; 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-series +(musicbrainz--deflookup-1 "series" + "| %s | [[https://musicbrainz.org/series/%s][%s]] |\n" + (.title .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 +749,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,6 +757,7 @@ 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 @@ -517,7 +766,6 @@ Optionally limit the search to TYPE results for ENTITY." response)) -;;; (provide 'musicbrainz)