1 ;;; musicbrainz.el --- MusicBrainz API interface -*- coding: utf-8; lexical-binding: t -*-
5 ;; Author: nik gaffney <nik@fo.am>
8 ;; Package-Requires: ((emacs "28.1") (request "0.3"))
9 ;; Keywords: music, scrobbling, multimedia
10 ;; URL: https://github.com/zzkt/metabrainz
12 ;; This file is not part of GNU Emacs.
14 ;; This program is free software; you can redistribute it and/or modify
15 ;; it under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation, either version 3 of the License, or
17 ;; (at your option) any later version.
19 ;; This program is distributed in the hope that it will be useful,
20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 ;; GNU General Public License for more details.
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with this program. If not, see <https://www.gnu.org/licenses/>.
30 ;; An interface to the MusicBrainz "open music encyclopedia" collection
31 ;; of music metadata. The main entry points are `musicbrainz-search' for
32 ;; general searches and `musicbrainz-lookup' for the more specific.
33 ;; There are also some narrower searches such as `musicbrainz-search-artist'
35 ;; Naming follows the MusicBrainz API reasonably closely, so the official API
36 ;; documentation can provide insight into how searching, browsing and lookups
37 ;; are structured. MusicBrainz has it's particular taxonomy and quirks, so
38 ;; some familiarity may be required to get useful results in some cases.
40 ;; https://musicbrainz.org/doc/MusicBrainz_API
54 (defcustom musicbrainz-api-url "https://musicbrainz.org/ws/2"
55 "URL for MusicBrainz API.
56 Documentation available at https://musicbrainz.org/doc/MusicBrainz_API"
60 (defcustom musicbrainz-coverart-api-url "http://coverartarchive.org"
61 "URL for MusicBrainz Cover Art Archive API.
62 Documentation available at https://musicbrainz.org/doc/Cover_Art_Archive/API"
66 (defcustom musicbrainz-api-token ""
67 "An auth token is required for some functions."
71 (defcustom musicbrainz-user-agent "musicbrainz.el/0.1"
72 "A User-Agent header to identify source of API requests.
73 As seen in https://wiki.musicbrainz.org/MusicBrainz_API/Rate_Limiting"
80 ;; https://musicbrainz.org/doc/MusicBrainz_API#Browse
82 ;; On each entity resource, you can perform three different GET requests:
83 ;; lookup: /<ENTITY_TYPE>/<MBID>?inc=<INC>
84 ;; browse: /<RESULT_ENTITY_TYPE>?<BROWSING_ENTITY_TYPE>=<MBID>&limit=<LIMIT>&offset=<OFFSET>&inc=<INC>
85 ;; search: /<ENTITY_TYPE>?query=<QUERY>&limit=<LIMIT>&offset=<OFFSET>
87 ;; Note: Keep in mind only the search request is available without an MBID
88 ;; (or, in specific cases, a disc ID, ISRC or ISWC). If all you have is the
89 ;; name of an artist or album, for example, you'll need to make a search and
90 ;; pick the right result to get its MBID; only then will you able to use it
91 ;; in a lookup or browse request.
93 ;; On the genre resource, we support an "all" sub-resource to fetch all genres,
94 ;; paginated, in alphabetical order:
96 ;; all: /genre/all?limit=<LIMIT>&offset=<OFFSET>
100 (defconst musicbrainz-entities-core
101 (list "area" "artist" "event" "genre" "instrument" "label" "place"
102 "recording" "release" "release-group" "series" "work" "url")
103 "API resources which represent core entities in the MusicBrainz database.")
105 (defconst musicbrainz-entities-non-core
106 (list "rating" "tag" "collection")
107 "API resources which represent non-core entities in the MusicBrainz database.")
109 (defconst musicbrainz-entities-uids
110 (list "discid" "isrc" "iswc")
111 "API resources based on other unique identifiers in the MusicBrainz database.")
113 (defconst musicbrainz-entities-linked
114 (list "area" "artist" "collection" "event" "instrument" "label" "place"
115 "recording" "release" "release-group" "series" "work" "url")
116 "API resources for linked entites in the MusicBrainz database.")
118 (defconst musicbrainz-search-types
119 (list "annotation" "area" "artist" "cdstub" "event" "instrument"
120 "label" "place" "recording" "release" "release-group"
121 "series" "tag" "work" "url")
122 "Valid TYPE parameters for MusicBrainz searches.")
124 (defconst musicbrainz-relationships
125 (list "area-rels" "artist-rels" "event-rels" "instrument-rels"
126 "label-rels" "place-rels" "recording-rels" "release-rels"
127 "release-group-rels" "series-rels" "url-rels" "work-rels")
128 "Valid relationships for lookups.")
133 (defun musicbrainz-core-entity-p (entity)
134 "Check if ENTITY is a core entity."
135 (if (member entity musicbrainz-entities-core) t nil))
137 (defun musicbrainz-non-core-entity-p (entity)
138 "Check if ENTITY is a non-core entity."
139 (if (member entity musicbrainz-entities-non-core) t nil))
141 (defun musicbrainz-uid-entity-p (entity)
142 "Check if ENTITY is a unique identifier entity."
143 (if (member entity musicbrainz-entities-uids) t nil))
145 (defun musicbrainz-search-type-p (type)
146 "Check if TYPE is a valid search type."
147 (if (member type musicbrainz-search-types) t nil))
152 (defun musicbrainz-linked-entity-p (entity)
153 "Check if ENTITY can be used in a browse request (incomplete).
155 The following list shows which linked entities you can use in a browse request:
157 /ws/2/area collection
158 /ws/2/artist area, collection, recording, release, release-group, work
159 /ws/2/collection area, artist, editor, event, label, place, recording,
160 release, release-group, work
161 /ws/2/event area, artist, collection, place
162 /ws/2/instrument collection
163 /ws/2/label area, collection, release
164 /ws/2/place area, collection
165 /ws/2/recording artist, collection, release, work
166 /ws/2/release area, artist, collection, label, track, track_artist,
167 recording, release-group
168 /ws/2/release-group artist, collection, release
169 /ws/2/series collection
170 /ws/2/work artist, collection
173 (if (member entity musicbrainz-entities-linked) t nil))
178 (defun musicbrainz-mbid-p (mbid)
179 "Check (permissive) if MBID is valid and/or well formatted.
180 An MBID is a 36 character Universally Unique Identifier, see https://musicbrainz.org/doc/MusicBrainz_Identifier for details."
182 (if (and (length= mbid 36) ;; length= requires emacs > 28.1
184 (rx (repeat 8 hex) ;; [A-F0-9]{8}
185 "-" (repeat 4 hex) ;; -[A-F0-9]{4}
186 "-" (repeat 4 hex) ;; -[34][A-F0-9]{3}
187 "-" (repeat 4 hex) ;; -[89AB][A-F0-9]{3}
188 "-" (repeat 12 hex)) ;; -[A-F0-9]{12}
192 ;; https://lucene.apache.org/core/4_3_0/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#Escaping_Special_Characters
193 (defconst musicbrainz-qeury-special-chars
194 (list "+" "-" "&" "|" "!" "(" ")" "{" "}" "[" "]" "^" "\"" "~" "*" "?" ":" "\\" "/"))
197 (defun musicbrainz-format (response)
198 "Format a generic RESPONSE."
199 (format "%s" (pp response)))
202 (defun musicbrainz--unwrap-0 (entity)
203 "Unwrap (fragile) .artist-credit ENTITY -> .name more or less."
204 (format "%s" (cdar (aref entity 0))))
207 ;;; ;; ;; ; ; ; ; ; ;
210 ;; https://musicbrainz.org/doc/MusicBrainz_API/Search
212 ;; The MusicBrainz API search requests provide a way to search for MusicBrainz
213 ;; entities based on different sorts of queries and are provided by a search
214 ;; server built using Lucene technology.
216 ;; Parameters common to all resources
218 ;; type Selects the entity index to be searched: annotation, area, artist,
219 ;; cdstub, event, instrument, label, place, recording, release,
220 ;; release-group, series, tag, work, url
222 ;; query Lucene search query. This is mandatory
224 ;; limit An integer value defining how many entries should be returned.
225 ;; Only values between 1 and 100 (both inclusive) are allowed.
226 ;; If not given, this defaults to 25.
228 ;; offset Return search results starting at a given offset.
229 ;; Used for paging through more than one page of results.
231 ;; dismax If set to "true", switches the Solr query parser from edismax to dismax,
232 ;; which will escape certain special query syntax characters by default
233 ;; for ease of use. This is equivalent to switching from the "Indexed search
234 ;; with advanced query syntax" method to the plain "Indexed search" method
235 ;; on the website. Defaults to "false".
240 (defun musicbrainz-search (type query &optional limit offset)
241 "Search the MusicBrainz database for TYPE matching QUERY.
242 Optionally return only LIMIT number of results from OFFSET.
244 The QUERY field supports the full Lucene Search syntax, some details
245 can be found near https://musicbrainz.org/doc/MusicBrainz_API/Search
246 or in the Lucene docs."
248 (interactive "sMusicBrainz search type: \nsMusicBrainz search query: ")
249 (message "MusicBrainz: searching %s=%s" type query)
250 ;; queries may need to be escaped
251 (let* ((max (if limit limit 1))
252 (from (if offset offset ""))
254 (request-response-data
257 (format "%s/%s?query=%s&fmt=json&limit=%s&offset=%s"
258 musicbrainz-api-url type query max from))
260 :headers (list `("User-Agent" . ,musicbrainz-user-agent))
264 (lambda (&key data &allow-other-keys)
265 (message "ok: %s" data)))
267 (if (called-interactively-p 'any)
268 (message "%s" (pp response))
273 (defun musicbrainz-find (query &rest extras)
274 "Search MusicBrainz for QUERY (and EXTRAS) or recommend a more specific search.
275 MusicBrainz makes a distinction between `search', `lookup' and `browse' this
276 provides a more general entry point to searching/browsing the database.
280 - if QUERY is an MBID, check artist, recording, etc
281 - if QUERY is text, search for artists or recordings, etc"
283 (message "MusicBrainz: query %s %s" query (if extras extras ""))
284 (if (musicbrainz-mbid-p query)
285 ;; search (lookup) for things that could have an mbid
287 (message "searching mbid: %s" mbid)
288 ;; search (search/browse/query) for other things
290 (message "searching other: %s" mbid)))))
293 ;; generate search functions
295 (defmacro musicbrainz--defsearch-1 (name format-string format-args)
296 "Generate search function to format a single item.
297 NAME FORMAT-STRING FORMAT-ARGS
298 See listenbrainz--deformatter for details."
299 (let ((f (intern (concat "musicbrainz-search-" name)))
300 (doc (format "Search for %s using QUERY and show matches.
301 Optionally return LIMIT number of results." name)))
302 `(defun ,f (query &optional limit) ,doc
303 (let* ((max (if limit limit 1))
305 (musicbrainz-search ,name query max)))
307 (format ,format-string ,@format-args))))))
310 (defmacro musicbrainz--defsearch-2 (name format-string format-args alist)
311 "Generate lookup function to format multiple items.
312 NAME FORMAT-STRING FORMAT-ARGS ALIST
313 See listenbrainz--deformatter for details."
314 (let ((f (intern (concat "musicbrainz-search-" name)))
315 (doc (format "Search for %s using QUERY and show matches.
316 Optionally return LIMIT number of results." name)))
317 `(defun ,f (query &optional limit) ,doc
318 (let* ((max (if limit limit 1))
320 (musicbrainz-search ,name query max)))
325 (format ,format-string ,@format-args)))
328 ;; various specific searches
330 ;; search -> musicbrainz-search-annotation
331 (musicbrainz--defsearch-2 "annotation"
332 "%s | %s | %s | %s | [[https://musicbrainz.org/%s/%s][%s]] |\n"
333 (.score .type .name .text .type .entity .entity)
336 ;; search -> musicbrainz-search-area
337 (musicbrainz--defsearch-2 "area"
338 "%s | [[https://musicbrainz.org/area/%s][%s]] |\n"
343 (defun musicbrainz-search-artist (artist &optional limit)
344 "Search for an ARTIST and show matches.
345 Optionally return LIMIT number of results."
346 (let ((data (musicbrainz-search "artist" artist limit)))
353 (format "%s | %s |\n" .name .id)
354 (format "%s | %s | %s |\n"
359 (defun musicbrainz-artist-to-mbid (artist)
360 "Find an MBID for ARTIST (with 100% match).
361 See `musicbrainz-disambiguate-artist' if there are multiple matches."
362 (let ((data (musicbrainz-search "artist" artist)))
364 (car (remove nil (seq-map
372 (defun musicbrainz-disambiguate-artist (artist &optional limit)
373 "More ARTIST data. less ambiguity (with optional LIMIT).
374 Outputs an `org-mode' table with descriptions and MBID link to artists pages."
375 (let* ((max (if limit limit 11))
376 (data (musicbrainz-search "artist" artist max)))
378 (cons (format "| Artist: %s| MBID |\n" artist)
382 (format "%s | %s, %s | [[https://musicbrainz.org/artist/%s][%s]] |\n"
383 .score .name .disambiguation .id .id)))
387 ;; search -> musicbrainz-search-event
388 (musicbrainz--defsearch-2 "event"
389 "%s | [[https://musicbrainz.org/event/%s][%s]] |\n"
393 ;; search -> musicbrainz-search-instrument
394 (musicbrainz--defsearch-2 "instrument"
395 "| %s | %s | [[https://musicbrainz.org/instrument/%s][%s]] |\n"
396 (.name .type .id .id)
400 (defun musicbrainz-search-label (label &optional limit)
401 "Search for a LABEL and show matches.
402 Optionally return LIMIT number of results."
403 (let ((data (musicbrainz-search "label" label limit)))
410 (format "%s | [[https://musicbrainz.org/label/%s][%s]] |\n" .name .id .id)
411 (format "%s | %s | %s (%s%s) | [[https://musicbrainz.org/label/%s][%s]] |\n"
413 (if .disambiguation .disambiguation "")
415 (format "%s " .life-span.begin) "")
417 (format "—%s" .life-span.end)
423 ;; search -> musicbrainz-search-place
424 (musicbrainz--defsearch-2 "place"
425 "%s | [[https://musicbrainz.org/place/%s][%s]] |\n"
429 ;; search -> musicbrainz-search-recording
430 (musicbrainz--defsearch-2 "recording"
431 "%s | %s, %s | [[https://musicbrainz.org/recording/%s][%s]] |\n"
432 (.score .title (musicbrainz--unwrap-0 .artist-credit) .id .id)
435 ;; search -> musicbrainz-search-release
436 (musicbrainz--defsearch-2 "release"
437 "%s | %s | %s | [[https://musicbrainz.org/release/%s][%s]] |\n"
438 (.score .title (musicbrainz--unwrap-0 .artist-credit) .id .id)
441 ;; search -> musicbrainz-search-release-group
442 (musicbrainz--defsearch-2 "release-group"
443 "%s | %s | %s | [[https://musicbrainz.org/release-group/%s][%s]] |\n"
444 (.first-release-date .title .primary-type .id .id)
447 ;; search -> musicbrainz-search-series
448 (musicbrainz--defsearch-2 "series"
449 "%s | [[https://musicbrainz.org/series/%s][%s]] |\n"
453 ;; search -> musicbrainz-search-work
454 (musicbrainz--defsearch-2 "work"
455 "%s | %s | [[https://musicbrainz.org/work/%s][%s]] |\n"
456 (.score .title .id .id)
459 ;; search -> musicbrainz-search-url
460 (musicbrainz--defsearch-2 "url"
461 "%s | [[%s][%s]] | [[https://musicbrainz.org/url/%s][%s]] |\n"
462 (.score .resource .resource .id .id)
466 ;;; ;; ;; ; ; ; ; ; ;
469 ;; https://musicbrainz.org/doc/MusicBrainz_API#Lookups
474 (defun musicbrainz-lookup (entity mbid &optional inc)
475 "Search (lookup not browse) MusicBrainz for ENTITY with MBID.
476 Optionally add an INC list.
480 /ws/2/artist recordings, releases, release-groups, works
481 /ws/2/collection user-collections (requires authentication)
487 /ws/2/recording artists, releases, isrcs, url-rels
488 /ws/2/release artists, collections, labels, recordings, release-groups
489 /ws/2/release-group artists, releases
494 (interactive "sMusicBrainz entity type: \nsMusicBrainz MBID for entity: ")
495 (message "MusicBrainz: lookup: %s/%s" entity mbid)
496 (if (and (musicbrainz-core-entity-p entity)
497 (musicbrainz-mbid-p mbid))
498 (let* ((add (if inc inc ""))
500 (request-response-data
503 (format "%s/%s/%s?inc=%s&fmt=json"
504 musicbrainz-api-url entity mbid add))
508 :success (cl-function
509 (lambda (&key data &allow-other-keys)
511 (message "%s data: %s" entity mbid))))))))
512 (if (called-interactively-p 'any)
513 (message "%s" (pp response))
515 (user-error "MusicBrainz: search requires a valid MBID and entity (i.e. one of %s)"
516 musicbrainz-entities-core)))
518 ;; relationship lookups
520 (defun musicbrainz-relations (entity relation mbid)
521 "Lookup relationships of type RELATION to ENTITY with MBID."
522 ;; no sanity and/or error checks
523 (musicbrainz-lookup entity mbid (format "%s-rels" relation)))
526 ;; specific MBID lookup requests & subrequests (limited to 25 results?)
528 (defmacro musicbrainz--deflookup-1 (name format-string format-args)
529 "Generate lookup function to format a single item.
530 NAME FORMAT-STRING FORMAT-ARGS
531 See listenbrainz--deformatter for details."
532 (let ((f (intern (concat "musicbrainz-lookup-" name)))
533 (doc "MusicBrainz lookup.")
534 (prompt (format "sMusicBrainz lookup %s by MBID: " name)))
535 `(defun ,f (mbid) ,doc
536 (interactive ,prompt)
538 (musicbrainz-lookup ,name mbid)))
540 (format ,format-string ,@format-args))))))
543 (defmacro musicbrainz--deflookup-2 (query subquery format-string format-args alist)
544 "Generate lookup function to format multiple items.
545 QUERY SUBQUERY FORMAT-STRING FORMAT-ARGS ALIST
546 See listenbrainz--deformatter for details."
547 (let ((f (intern (format "musicbrainz-lookup-%s-%s" query subquery)))
548 (doc "MusicBrainz lookup.")
549 (prompt (format "sMusicBrainz lookup %s %s by MBID: " query subquery)))
550 `(defun ,f (mbid) ,doc
551 (interactive ,prompt)
553 (musicbrainz-lookup ,query mbid ,subquery)))
558 (format ,format-string ,@format-args)))
562 ;; (defun musicbrainz-lookup-artist (mbid)
563 ;; "MusicBrainz lookup for artist with MBID."
565 ;; (musicbrainz-lookup "artist" mbid)))
566 ;; (let-alist response
567 ;; (format "| %s | %s | %s | [[https://musicbrainz.org/artist/%s][%s]] |\n"
568 ;; .name .disambiguation .type .id .id))))
571 ;; (defun musicbrainz-lookup-artist-recordings (mbid)
572 ;; "MusicBrainz lookup for recordings from artist with MBID."
574 ;; (musicbrainz-lookup "artist" mbid "recordings")))
575 ;; (let-alist response
579 ;; (format "%s | [[https://musicbrainz.org/recording/%s][%s]] |\n"
584 ;; lookup -> musicbrainz-lookup-area
585 (musicbrainz--deflookup-1 "area"
586 "| %s | [[https://musicbrainz.org/area/%s][%s]] |\n"
589 ;; lookup -> musicbrainz-lookup-artist
590 (musicbrainz--deflookup-1 "artist"
591 "| %s | %s | %s | [[https://musicbrainz.org/artist/%s][%s]] |\n"
592 (.name .disambiguation .type .id .id))
594 ;; lookup -> musicbrainz-lookup-artist-recordings
595 (musicbrainz--deflookup-2 "artist" "recordings"
596 "%s | [[https://musicbrainz.org/recording/%s][%s]] |\n"
600 ;; lookup -> musicbrainz-lookup-artist-releases
601 (musicbrainz--deflookup-2 "artist" "releases"
602 "%s | %s | %s | [[https://musicbrainz.org/release/%s][%s]] |\n"
603 (.date .title .packaging .id .id)
606 ;; lookup -> musicbrainz-lookup-artist-release-groups
607 (musicbrainz--deflookup-2 "artist" "release-groups"
608 "%s | %s | %s | [[https://musicbrainz.org/release-group/%s][%s]] |\n"
609 (.first-release-date .title .primary-type .id .id)
612 ;; lookup -> musicbrainz-lookup-artist-works
613 (musicbrainz--deflookup-2 "artist" "works"
614 " %s | [[https://musicbrainz.org/work/%s][%s]] |\n"
618 ;; lookup -> musicbrainz-lookup-collection
619 (musicbrainz--deflookup-1 "collection"
620 "| %s | [[https://musicbrainz.org/collection/%s][%s]] |\n"
623 ;; lookup -> musicbrainz-lookup-collection-user-collections (requires authentication)
624 (musicbrainz--deflookup-2 "collection" "user-collections"
625 " %s | [[https://musicbrainz.org/collection/%s][%s]] |\n"
629 ;; lookup -> musicbrainz-lookup-event
630 (musicbrainz--deflookup-1 "event"
631 "| %s | [[https://musicbrainz.org/event/%s][%s]] |\n"
634 ;; lookup -> musicbrainz-lookup-genre
635 (musicbrainz--deflookup-1 "genre"
636 "| %s | [[https://musicbrainz.org/genre/%s][%s]] |\n"
639 ;; lookup -> musicbrainz-lookup-instrument
640 (musicbrainz--deflookup-1 "instrument"
641 "| %s | %s | [[https://musicbrainz.org/instrument/%s][%s]] |\n"
642 (.name .type .id .id))
644 ;; lookup -> musicbrainz-lookup-label
645 (musicbrainz--deflookup-1 "label"
646 "| %s | %s | [[https://musicbrainz.org/label/%s][%s]] |\n"
647 (.name .disambiguation .id .id))
650 ;; lookup -> musicbrainz-lookup-label-releases
651 (musicbrainz--deflookup-2 "label" "releases"
652 "%s | %s | [[https://musicbrainz.org/release/%s][%s]] |\n"
653 (.date .title .id .id)
656 ;; lookup -> musicbrainz-lookup-place
657 (musicbrainz--deflookup-1 "place"
658 "| %s | [[https://musicbrainz.org/place/%s][%s]] |\n"
661 ;; lookup -> musicbrainz-lookup-recording
662 (musicbrainz--deflookup-1 "recording"
663 "| %s | %s | [[https://musicbrainz.org/recording/%s][%s]] |\n"
664 (.first-release-date .title .id .id))
667 ;; lookup -> musicbrainz-lookup-recording-artists
668 (musicbrainz--deflookup-2 "recording" "artists"
669 "%s | [[https://musicbrainz.org/artist/%s][%s]] |\n"
670 (.artist.name .artist.id .artist.id)
673 ;; lookup -> musicbrainz-lookup-recording-releases
674 (musicbrainz--deflookup-2 "recording" "releases"
675 "%s | %s | %s | [[https://musicbrainz.org/release/%s][%s]] |\n"
676 (.date .title .packaging .id .id)
679 ;; lookup -> musicbrainz-lookup-recording-isrcs
680 (musicbrainz--deflookup-2 "recording" "isrcs"
681 "%s | [[https://musicbrainz.org/isrc/%s][%s]] |\n"
685 ;; lookup -> musicbrainz-lookup-recording-url-rels
686 (musicbrainz--deflookup-2 "recording" "url-rels"
687 "%s | [[https://musicbrainz.org/recording/%s][%s]] |\n"
691 ;; lookup -> musicbrainz-lookup-release
692 (musicbrainz--deflookup-1 "release"
693 "| %s | %s | %s | [[https://musicbrainz.org/release/%s][%s]] |\n"
694 (.date .title .packaging .id .id))
696 ;; lookup -> musicbrainz-lookup-release-artists
697 (musicbrainz--deflookup-2 "release" "artists"
698 "%s | [[https://musicbrainz.org/artist/%s][%s]] |\n"
699 (.artist.name .artist.id .artist.id)
702 ;; lookup -> musicbrainz-lookup-release-collections
704 ;; lookup -> musicbrainz-lookup-release-labels
706 ;; lookup -> musicbrainz-lookup-release-recordings
708 ;; lookup -> musicbrainz-lookup-release-release-groups
710 ;; lookup -> musicbrainz-lookup-release-group
711 (musicbrainz--deflookup-1 "release-group"
712 "| %s | %s | %s | [[https://musicbrainz.org/release-group/%s][%s]] |\n"
713 (.first-release-date .title .primary-type .id .id))
715 ;; lookup -> musicbrainz-lookup-release-group-artists
716 (musicbrainz--deflookup-2 "release-group" "artists"
717 "%s | [[https://musicbrainz.org/artist/%s][%s]] |\n"
718 (.artist.name .artist.id .artist.id)
721 ;; lookup -> musicbrainz-lookup-release-group-releases
723 ;; lookup -> musicbrainz-lookup-series
724 (musicbrainz--deflookup-1 "series"
725 "| %s | [[https://musicbrainz.org/series/%s][%s]] |\n"
728 ;; lookup -> musicbrainz-lookup-work
729 (musicbrainz--deflookup-1 "work"
730 "| %s | [[https://musicbrainz.org/work/%s][%s]] |\n"
733 ;; lookup -> musicbrainz-lookup-url
734 (musicbrainz--deflookup-1 "url"
735 "| %s | [[https://musicbrainz.org/url/%s][%s]] |\n"
740 ;;;;;; ; ; ;; ; ; ; ; ; ;; ;
743 ;; https://musicbrainz.org/doc/MusicBrainz_API#Browse
747 ;; Browse requests are a direct lookup of all the entities directly linked
748 ;; to another entity ("directly linked" here meaning it does not include
749 ;; entities linked by a relationship). For example, you may want to see all
750 ;; releases on the label ubiktune:
752 ;; /ws/2/release?label=47e718e1-7ee4-460c-b1cc-1192a841c6e5
754 ;; Note that browse requests are not searches: in order to browse all the releases
755 ;; on the ubiktune label you will need to know the MBID of ubiktune.
757 ;; The order of the results depends on what linked entity you are browsing
758 ;; by (however it will always be consistent). If you need to sort the entities,
759 ;; you will have to fetch all entities and sort them yourself.
761 ;; Keep in mind only the search request is available without an MBID (or, in
762 ;; specific cases, a disc ID, ISRC or ISWC). If all you have is the name of an
763 ;; artist or album, for example, you'll need to make a search and pick the right
764 ;; result to get its MBID to use it in a lookup or browse request.
768 (defun musicbrainz-browse (entity link query &optional type)
769 "Search the MusicBrainz database for ENTITY with LINK matching QUERY.
770 Optionally limit the search to TYPE results for ENTITY."
771 (message "MusicBrainz: browsing %s linked to %s" entity link)
772 (message "url: %s/%s?%s=%s&type=%s&fmt=json" musicbrainz-api-url entity link query type)
774 (request-response-data
777 (format "%s/%s?%s=%s&type=%s&fmt=json" musicbrainz-api-url entity link query type))
779 :header (list `("User-Agent" . ,musicbrainz-user-agent))
782 :success (cl-function
783 (lambda (&key data &allow-other-keys)
789 ;;;;;; ; ; ;; ; ; ; ; ; ;; ;
791 ;; Cover Art Archive API
792 ;; https://musicbrainz.org/doc/Cover_Art_Archive/API
797 ;; /release/{mbid}/front
798 ;; /release/{mbid}/back
799 ;; /release/{mbid}/{id}
800 ;; /release/{mbid}/({id}|front|back)-(250|500|1200)
802 ;; /release-group/{mbid}/
803 ;; /release-group/{mbid}/front[-(250|500|1200)]
806 (defun musicbrainz-coverart (mbid &optional release-group)
807 "Search MusicBrainz Cover Art Archive for release MBID.
808 When RELEASE-GROUP is non-nil MBID is for a release group, rather than release."
809 (message "MusicBrainz: cover art for %s" mbid)
810 (message "url: %s/release/%s" musicbrainz-coverart-api-url mbid)
812 (request-response-data
815 (format "%s/release/%s" musicbrainz-coverart-api-url mbid))
817 :header (list `("User-Agent" . ,musicbrainz-user-agent))
820 :success (cl-function
821 (lambda (&key data &allow-other-keys)
825 (defun musicbrainz-coverart-file-front (mbid)
826 "Get the MusicBrainz Cover Art front cover file for MBID."
827 (message "MusicBrainz: cover art (front) for %s" mbid)
828 (message "url: %s/release/%s/front" musicbrainz-coverart-api-url mbid)
830 (request-response-data
833 (format "%s/release/%s/front" musicbrainz-coverart-api-url mbid))
835 :header (list `("User-Agent" . ,musicbrainz-user-agent))
837 :success (cl-function
838 (lambda (&key data &allow-other-keys)
842 (defun musicbrainz-coverart-file-back (mbid)
843 "Get the MusicBrainz Cover Art back cover file for MBID."
844 (message "MusicBrainz: cover art (back) for %s" mbid)
845 (message "url: %s/release/%s/back" musicbrainz-coverart-api-url mbid)
847 (request-response-data
850 (format "%s/release/%s/back" musicbrainz-coverart-api-url mbid))
852 :header (list `("User-Agent" . ,musicbrainz-user-agent))
854 :success (cl-function
855 (lambda (&key data &allow-other-keys)
860 (provide 'musicbrainz)
862 ;;; musicbrainz.el ends here