]> git.vanrenterghem.biz Git - musicbrainz.git/blob - musicbrainz.el
Decadent and symmetrical
[musicbrainz.git] / musicbrainz.el
1 ;;; musicbrainz.el --- MusicBrainz API interface -*- coding: utf-8; lexical-binding: t -*-
3 ;; Copyright 2023 FoAM
4 ;;
5 ;; Author: nik gaffney <nik@fo.am>
6 ;; Created: 2023-05-05
7 ;; Version: 0.1
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/>.
28 ;;; Commentary:
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'
34 ;;
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.
39 ;;
40 ;; https://musicbrainz.org/doc/MusicBrainz_API
42 ;;; Code:
44 (require 'request)
45 (require 'json)
47 ;;; ;; ;; ;  ; ;   ;  ;      ;
48 ;;
49 ;; API config
50 ;;
51 ;;; ; ;; ;;
53 (defcustom musicbrainz-api-url "https://musicbrainz.org/ws/2"
54   "URL for musicbrainz API.
55 Documentation available at https://musicbrainz.org/doc/MusicBrainz_API"
56   :type 'string
57   :group 'musicbrainz)
59 (defcustom musicbrainz-api-token ""
60   "An auth token is required for some functions."
61   :type 'string
62   :group 'musicbrainz)
64 (defcustom musicbrainz-user-agent "musicbrainz.el/0.1"
65   "A User-Agent header to identify source of API requests.
66 As seen in https://wiki.musicbrainz.org/MusicBrainz_API/Rate_Limiting"
67   :type 'string
68   :group 'musicbrainz)
70 ;;; ; ; ;;;   ;  ;
71 ;;
72 ;; API entities
73 ;;  https://musicbrainz.org/doc/MusicBrainz_API#Browse
74 ;;
75 ;; On each entity resource, you can perform three different GET requests:
76 ;;  lookup:   /<ENTITY_TYPE>/<MBID>?inc=<INC>
77 ;;  browse:   /<RESULT_ENTITY_TYPE>?<BROWSING_ENTITY_TYPE>=<MBID>&limit=<LIMIT>&offset=<OFFSET>&inc=<INC>
78 ;;  search:   /<ENTITY_TYPE>?query=<QUERY>&limit=<LIMIT>&offset=<OFFSET>
79 ;;
80 ;; Note: Keep in mind only the search request is available without an MBID
81 ;; (or, in specific cases, a disc ID, ISRC or ISWC). If all you have is the
82 ;; name of an artist or album, for example, you'll need to make a search and
83 ;; pick the right result to get its MBID; only then will you able to use it
84 ;; in a lookup or browse request.
85 ;;
86 ;; On the genre resource, we support an "all" sub-resource to fetch all genres,
87 ;; paginated, in alphabetical order:
88 ;;
89 ;;  all:      /genre/all?limit=<LIMIT>&offset=<OFFSET>
90 ;;
91 ;; ; ;;; ;
93 (defconst musicbrainz-entities-core
94   (list "area" "artist" "event" "genre" "instrument" "label" "place"
95         "recording" "release" "release-group" "series" "work" "url")
96   "API resources which represent core entities in the MusicBrainz database.")
98 (defconst musicbrainz-entities-non-core
99   (list "rating" "tag" "collection")
100   "API resources which represent non-core entities in the MusicBrainz database.")
102 (defconst musicbrainz-entities-uids
103   (list "discid" "isrc" "iswc")
104   "API resources based on other unique identifiers in the MusicBrainz database.")
106 (defconst musicbrainz-entities-linked
107   (list "area" "artist" "collection" "event" "instrument" "label" "place"
108         "recording" "release" "release-group" "series" "work" "url")
109   "API resources for linked entites in the MusicBrainz database.")
111 (defconst musicbrainz-search-types
112   (list "annotation" "area" "artist" "cdstub" "event" "instrument"
113         "label" "place" "recording" "release" "release-group"
114         "series" "tag" "work" "url")
115   "Valid TYPE parameters for MusicBrainz searches.")
117 (defconst musicbrainz-relationships
118   (list "area-rels" "artist-rels" "event-rels" "instrument-rels"
119         "label-rels" "place-rels" "recording-rels" "release-rels"
120         "release-group-rels" "series-rels" "url-rels" "work-rels")
121   "Valid relationships for lookups.")
124 ;; entity checks
126 (defun musicbrainz-core-entity-p (entity)
127   "Check if ENTITY is a core entity."
128   (if (member entity musicbrainz-entities-core) t nil))
130 (defun musicbrainz-non-core-entity-p (entity)
131   "Check if ENTITY is a non-core entity."
132   (if (member entity musicbrainz-entities-non-core) t nil))
134 (defun musicbrainz-uid-entity-p (entity)
135   "Check if ENTITY is a unique identifier entity."
136   (if (member entity musicbrainz-entities-uids) t nil))
138 (defun musicbrainz-search-type-p (type)
139   "Check if TYPE is a valid search type."
140   (if (member type musicbrainz-search-types) t nil))
143 ;; Linked entities
145 (defun musicbrainz-linked-entity-p (entity)
146   "Check if ENTITY can be used in a browse request (incomplete).
148 The following list shows which linked entities you can use in a browse request:
150  /ws/2/area            collection
151  /ws/2/artist          area, collection, recording, release, release-group, work
152  /ws/2/collection      area, artist, editor, event, label, place, recording,
153                        release, release-group, work
154  /ws/2/event           area, artist, collection, place
155  /ws/2/instrument      collection
156  /ws/2/label           area, collection, release
157  /ws/2/place           area, collection
158  /ws/2/recording       artist, collection, release, work
159  /ws/2/release         area, artist, collection, label, track, track_artist,
160                        recording, release-group
161  /ws/2/release-group   artist, collection, release
162  /ws/2/series          collection
163  /ws/2/work            artist, collection
164  /ws/2/url             resource"
166   (if (member entity musicbrainz-entities-linked) t nil))
169 ;; utils & aux
171 (defun musicbrainz-mbid-p (mbid)
172   "Check (permissive) if MBID is valid and/or well formatted.
173 An MBID is a 36 character Universally Unique Identifier, see https://musicbrainz.org/doc/MusicBrainz_Identifier for details."
175   (if (and (length= mbid 36) ;; length= requires emacs > 28.1
176            (string-match-p
177             (rx (repeat 8 hex)        ;;  [A-F0-9]{8}
178                 "-" (repeat 4 hex)    ;; -[A-F0-9]{4}
179                 "-" (repeat 4 hex)    ;; -[34][A-F0-9]{3}
180                 "-" (repeat 4 hex)    ;; -[89AB][A-F0-9]{3}
181                 "-" (repeat 12 hex))  ;; -[A-F0-9]{12}
182             mbid))
183       t nil))
185 ;; https://lucene.apache.org/core/4_3_0/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#Escaping_Special_Characters
186 (defconst musicbrainz-qeury-special-chars
187   (list "+" "-" "&" "|" "!" "(" ")" "{" "}"  "[" "]" "^" "\"" "~" "*" "?" ":" "\\" "/"))
190 (defun musicbrainz-format (response)
191   "Format a generic RESPONSE."
192   (format "%s" (pp response)))
195 (defun musicbrainz--unwrap-0 (entity)
196   "Unwrap (fragile) .artist-credit ENTITY -> .name more or less."
197   (format "%s" (cdar (aref entity 0))))
200 ;;; ;; ;; ;  ; ;   ;  ;      ;
201 ;;
202 ;; Search API
203 ;;  https://musicbrainz.org/doc/MusicBrainz_API/Search
204 ;;
205 ;; The MusicBrainz API search requests provide a way to search for MusicBrainz
206 ;; entities based on different sorts of queries and are provided by a search
207 ;; server built using Lucene technology.
208 ;;
209 ;; Parameters common to all resources
210 ;;
211 ;;  type    Selects the entity index to be searched: annotation, area, artist,
212 ;;          cdstub, event, instrument, label, place, recording, release,
213 ;;          release-group, series, tag, work, url
214 ;;
215 ;;  query   Lucene search query. This is mandatory
216 ;;
217 ;;  limit   An integer value defining how many entries should be returned.
218 ;;          Only values between 1 and 100 (both inclusive) are allowed.
219 ;;          If not given, this defaults to 25.
220 ;;
221 ;;  offset  Return search results starting at a given offset.
222 ;;          Used for paging through more than one page of results.
223 ;;
224 ;;  dismax  If set to "true", switches the Solr query parser from edismax to dismax,
225 ;;          which will escape certain special query syntax characters by default
226 ;;          for ease of use. This is equivalent to switching from the "Indexed search
227 ;;          with advanced query syntax" method to the plain "Indexed search" method
228 ;;          on the website. Defaults to "false".
229 ;;
230 ;; ;; ; ;  ;
232 ;;;###autoload
233 (defun musicbrainz-search (type query &optional limit offset)
234   "Search the MusicBrainz database for TYPE matching QUERY.
235 Optionally return only LIMIT number of results from OFFSET.
237 The QUERY field supports the full Lucene Search syntax, some details
238 can be found near https://musicbrainz.org/doc/MusicBrainz_API/Search
239 or in the Lucene docs."
241   (message "MusicBrainz: searching %s=%s" type query)
242   ;; queries may need to be escaped
243   (let* ((max (if limit limit 1))
244          (from (if offset offset ""))
245          (response
246            (request-response-data
247             (request
248              (url-encode-url
249               (format "%s/%s?query=%s&fmt=json&limit=%s&offset=%s"
250                       musicbrainz-api-url type query max from))
251              :type "GET"
252              :headers (list `("User-Agent" . ,musicbrainz-user-agent))
253              :parser 'json-read
254              :sync t
255              :success (cl-function
256                        (lambda (&key data &allow-other-keys)
257                          (message "ok")))))))
258     response))
261 ;;;###autoload
262 (defun musicbrainz-find (query &rest extras)
263   "Search MusicBrainz for QUERY (and EXTRAS) or recommend a more specific search.
264 MusicBrainz makes a distinction between `search', `lookup' and `browse' this
265 provides a more general entry point to searching/browsing the database.
267 Heuristics.
268 - not yet
269 - if QUERY is an MBID, check artist, recording, etc
270 - if QUERY is text, search for artists or recordings, etc"
272   (message "MusicBrainz: query %s" query)
273   (if (musicbrainz-mbid-p query)
274       ;; search (lookup) for things that could have an mbid
275       (let ((mbid query))
276         (message "searching mbid: %s" mbid))
277       ;; search (search/browse/query) for other things
278       (progn
279         (message "searching other: %s" mbid))))
282 ;; generate search functions
284 (defmacro musicbrainz--defsearch-1 (name format-string format-args)
285   "Generate search function to format a single item.
286 NAME FORMAT-STRING FORMAT-ARGS
287 See listenbrainz--deformatter for details."
288   (let ((f (intern (concat "musicbrainz-search-" name)))
289         (doc (format "Search for %s using QUERY and show matches.
290 Optionally return LIMIT number of results." name)))
291     `(defun ,f (query &optional limit) ,doc
292        (let* ((max (if limit limit 1))
293               (response
294                (musicbrainz-search ,name query max)))
295          (let-alist response
296                     (format ,format-string ,@format-args))))))
299 (defmacro musicbrainz--defsearch-2 (name format-string format-args alist)
300   "Generate lookup function to format multiple items.
301 QUERY SUBQUERY FORMAT-STRING FORMAT-ARGS ALIST
302 See listenbrainz--deformatter for details."
303   (let ((f (intern (concat "musicbrainz-search-" name)))
304         (doc (format "Search for %s using QUERY and show matches.
305 Optionally return LIMIT number of results." name)))
306     `(defun ,f (query &optional limit) ,doc
307        (let* ((max (if limit limit 1))
308               (response
309                 (musicbrainz-search ,name query max)))
310          (let-alist response
311                     (seq-map
312                      (lambda (i)
313                        (let-alist i
314                                   (format ,format-string ,@format-args)))
315                      ,alist))))))
317 ;; various specific searches
319 ;; search ->  musicbrainz-search-annotation
320 (musicbrainz--defsearch-2 "annotation"
321                           "%s | %s |  %s | %s | [[https://musicbrainz.org/%s/%s][%s]] |\n"
322                           (.score .type .name .text .type .entity .entity)
323                           .annotations)
325 ;; search ->  musicbrainz-search-area
326 (musicbrainz--defsearch-2 "area"
327                           "%s | [[https://musicbrainz.org/area/%s][%s]] |\n"
328                           (.name .id .id)
329                           .areas)
332 (defun musicbrainz-search-artist (artist &optional limit)
333   "Search for an ARTIST and show matches.
334 Optionally return LIMIT number of results."
335   (let ((data (musicbrainz-search "artist" artist limit)))
336     (let-alist
337      data
338      (seq-map
339       (lambda (i)
340         (let-alist i
341                    (if (not limit)
342                        (format "%s | %s |\n" .name .id)
343                        (format "%s | %s | %s |\n"
344                                .score .name .id))))
345       .artists))))
348 (defun musicbrainz-artist-to-mbid (artist)
349   "Find an MBID for ARTIST (with 100% match).
350 See `musicbrainz-disambiguate-artist' if there are multiple matches."
351   (let ((data (musicbrainz-search "artist" artist)))
352     (let-alist data
353                (car (remove nil (seq-map
354                                  (lambda (i)
355                                    (let-alist i
356                                               (when (= 100 .score)
357                                                 (format "%s" .id))))
358                                  .artists))))))
361 (defun musicbrainz-disambiguate-artist (artist &optional limit)
362   "More ARTIST data. less ambiguity (with optional LIMIT).
363 Outputs an `org-mode' table with descriptions and MBID link to artists pages."
364   (let* ((max (if limit limit 11))
365          (data (musicbrainz-search "artist" artist max)))
366     (let-alist data
367                (cons (format "| Artist: %s| MBID |\n" artist)
368                      (seq-map
369                       (lambda (i)
370                         (let-alist i
371                                    (format "%s | %s, %s | [[https://musicbrainz.org/artist/%s][%s]] |\n"
372                                            .score .name .disambiguation .id .id)))
373                       .artists)))))
376 ;; search ->  musicbrainz-search-event
377 (musicbrainz--defsearch-2 "event"
378                           "%s | [[https://musicbrainz.org/event/%s][%s]] |\n"
379                           (.name .id .id)
380                           .events)
382 ;; search ->  musicbrainz-search-instrument
383 (musicbrainz--defsearch-2 "instrument"
384                           "| %s | %s | [[https://musicbrainz.org/instrument/%s][%s]] |\n"
385                           (.name .type  .id .id)
386                           .instruments)
389 (defun musicbrainz-search-label (label &optional limit)
390   "Search for a LABEL and show matches.
391 Optionally return LIMIT number of results."
392   (let ((data (musicbrainz-search "label" label limit)))
393     (let-alist
394      data
395      (seq-map
396       (lambda (i)
397         (let-alist i
398                    (if (not limit)
399                        (format "%s | [[https://musicbrainz.org/label/%s][%s]] |\n" .name .id .id)
400                        (format "%s | %s | %s (%s%s) |  [[https://musicbrainz.org/label/%s][%s]]  |\n"
401                                .score .name
402                                (if .disambiguation .disambiguation "")
403                                (if .life-span.begin
404                                    (format "%s " .life-span.begin) "")
405                                (if .life-span.end
406                                    (format "—%s" .life-span.end)
407                                    "ongoing")
408                                .id .id))))
409       .labels))))
412 ;; search ->  musicbrainz-search-place
413 (musicbrainz--defsearch-2 "place"
414                           "%s | [[https://musicbrainz.org/place/%s][%s]] |\n"
415                           (.name .id .id)
416                           .places)
418 ;; search ->  musicbrainz-search-recording
419 (musicbrainz--defsearch-2 "recording"
420                           "%s | %s, %s | [[https://musicbrainz.org/recording/%s][%s]] |\n"
421                           (.score .title (musicbrainz--unwrap-0 .artist-credit) .id .id)
422                           .recordings)
424 ;; search ->  musicbrainz-search-release
425 (musicbrainz--defsearch-2 "release"
426                           "%s | %s | %s | [[https://musicbrainz.org/release/%s][%s]] |\n"
427                           (.score .title (musicbrainz--unwrap-0 .artist-credit) .id .id)
428                           .releases)
430 ;; search ->  musicbrainz-search-release-group
431 (musicbrainz--defsearch-2 "release-group"
432                           "%s | %s | %s | [[https://musicbrainz.org/release-group/%s][%s]] |\n"
433                           (.first-release-date .title .primary-type .id .id)
434                           .release-groups)
436 ;; search ->  musicbrainz-search-series
437 (musicbrainz--defsearch-2 "series"
438                           "%s | [[https://musicbrainz.org/series/%s][%s]] |\n"
439                           (.name .id .id)
440                           .series)
442 ;; search ->  musicbrainz-search-work
443 (musicbrainz--defsearch-2 "work"
444                           "%s | %s | [[https://musicbrainz.org/work/%s][%s]] |\n"
445                           (.score .title .id .id)
446                           .works)
448 ;; search ->  musicbrainz-search-url
449 (musicbrainz--defsearch-2 "url"
450                           "%s | [[%s][%s]] | [[https://musicbrainz.org/url/%s][%s]] |\n"
451                           (.score .resource .resource .id .id)
452                           .urls)
455 ;;; ;; ;; ;  ; ;   ;  ;      ;
456 ;;
457 ;; Lookups
458 ;;  https://musicbrainz.org/doc/MusicBrainz_API#Lookups
459 ;;
460 ;;; ;; ;;   ; ;
462 ;;;###autoload
463 (defun musicbrainz-lookup (entity mbid &optional inc)
464   "Search (lookup not browse) MusicBrainz for ENTITY with MBID.
465 Optionally add an INC list.
467 Subqueries
468  /ws/2/area
469  /ws/2/artist          recordings, releases, release-groups, works
470  /ws/2/collection      user-collections (requires authentication)
471  /ws/2/event
472  /ws/2/genre
473  /ws/2/instrument
474  /ws/2/label           releases
475  /ws/2/place
476  /ws/2/recording       artists, releases, isrcs, url-rels
477  /ws/2/release         artists, collections, labels, recordings, release-groups
478  /ws/2/release-group   artists, releases
479  /ws/2/series
480  /ws/2/work
481  /ws/2/url"
483   (message "MusicBrainz: lookup: %s/%s" entity mbid)
484   (if (and (musicbrainz-core-entity-p entity)
485            (musicbrainz-mbid-p mbid))
486       (let* ((add (if inc inc ""))
487              (response
488                (request-response-data
489                 (request
490                  (url-encode-url
491                   (format "%s/%s/%s?inc=%s&fmt=json"
492                           musicbrainz-api-url entity mbid add))
493                  :type "GET"
494                  :parser 'json-read
495                  :sync t
496                  :success (cl-function
497                            (lambda (&key data &allow-other-keys)
498                              (message "%s data: %s" entity mbid)))))))
499         response)
500       (error "MusicBrainz: search requires a valid MBID and entity (i.e. one of %s)"
501              musicbrainz-entities-core)))
503 ;; relationship lookups
505 (defun musicbrainz-relations (entity relation mbid)
506   "Lookup relationships of type RELATION to ENTITY with MBID."
507   ;; no sanity and/or error checks
508   (musicbrainz-lookup entity mbid (format "%s-rels" relation)))
511 ;; specific MBID lookup requests & subrequests (limited to 25 results?)
513 (defmacro musicbrainz--deflookup-1 (name format-string format-args)
514   "Generate lookup function to format a single item.
515 NAME FORMAT-STRING FORMAT-ARGS
516 See listenbrainz--deformatter for details."
517   (let ((f (intern (concat "musicbrainz-lookup-" name)))
518         (doc "MusicBrainz lookup."))
519     `(defun ,f (mbid) ,doc
520        (let ((response
521                (musicbrainz-lookup ,name mbid)))
522          (let-alist response
523                     (format ,format-string ,@format-args))))))
526 (defmacro musicbrainz--deflookup-2 (query subquery format-string format-args alist)
527   "Generate lookup function to format multiple items.
528 QUERY SUBQUERY FORMAT-STRING FORMAT-ARGS ALIST
529 See listenbrainz--deformatter for details."
530   (let ((f (intern (format "musicbrainz-lookup-%s-%s" query subquery)))
531         (doc "MusicBrainz lookup."))
532     `(defun ,f (mbid) ,doc
533        (let ((response
534                (musicbrainz-lookup ,query mbid ,subquery)))
535          (let-alist response
536                     (seq-map
537                      (lambda (i)
538                        (let-alist i
539                                   (format ,format-string ,@format-args)))
540                      ,alist))))))
543 ;; (defun musicbrainz-lookup-artist (mbid)
544 ;;   "MusicBrainz lookup for artist with MBID."
545 ;;   (let ((response
546 ;;           (musicbrainz-lookup "artist" mbid)))
547 ;;     (let-alist response
548 ;;                (format "| %s | %s | %s | [[https://musicbrainz.org/artist/%s][%s]] |\n"
549 ;;                        .name .disambiguation .type .id .id))))
552 ;; (defun musicbrainz-lookup-artist-recordings (mbid)
553 ;;   "MusicBrainz lookup for recordings from artist with MBID."
554 ;;   (let ((response
555 ;;           (musicbrainz-lookup "artist" mbid "recordings")))
556 ;;     (let-alist response
557 ;;                (seq-map
558 ;;                 (lambda (i)
559 ;;                   (let-alist i
560 ;;                              (format "%s | [[https://musicbrainz.org/recording/%s][%s]] |\n"
561 ;;                                      .title .id .id)))
562 ;;                 .recordings))))
565 ;; lookup ->  musicbrainz-lookup-area
566 (musicbrainz--deflookup-1 "area"
567                           "| %s | [[https://musicbrainz.org/area/%s][%s]] |\n"
568                           (.name .id .id))
570 ;; lookup ->  musicbrainz-lookup-artist
571 (musicbrainz--deflookup-1 "artist"
572                           "| %s | %s | %s | [[https://musicbrainz.org/artist/%s][%s]] |\n"
573                           (.name .disambiguation .type .id .id))
575 ;; lookup ->  musicbrainz-lookup-artist-recordings
576 (musicbrainz--deflookup-2 "artist" "recordings"
577                           "%s | [[https://musicbrainz.org/recording/%s][%s]] |\n"
578                           (.title .id .id)
579                           .recordings)
581 ;; lookup ->  musicbrainz-lookup-artist-releases
582 (musicbrainz--deflookup-2 "artist" "releases"
583                           "%s | %s | %s | [[https://musicbrainz.org/release/%s][%s]] |\n"
584                           (.date .title .packaging .id .id)
585                           .releases)
587 ;; lookup ->  musicbrainz-lookup-artist-release-groups
588 (musicbrainz--deflookup-2 "artist" "release-groups"
589                           "%s | %s | %s | [[https://musicbrainz.org/release-group/%s][%s]] |\n"
590                           (.first-release-date .title .primary-type .id .id)
591                           .release-groups)
593 ;; lookup ->  musicbrainz-lookup-artist-works
594 (musicbrainz--deflookup-2 "artist" "works"
595                           " %s | [[https://musicbrainz.org/work/%s][%s]] |\n"
596                           (.title .id .id)
597                           .works)
599 ;; lookup ->  musicbrainz-lookup-collection
600 (musicbrainz--deflookup-1 "collection"
601                           "| %s | [[https://musicbrainz.org/collection/%s][%s]] |\n"
602                           (.name .id .id))
604 ;; lookup ->  musicbrainz-lookup-collection-user-collections (requires authentication)
605 (musicbrainz--deflookup-2 "collection" "user-collections"
606                           " %s | [[https://musicbrainz.org/collection/%s][%s]] |\n"
607                           (.name .id .id)
608                           .collection)
610 ;; lookup ->  musicbrainz-lookup-event
611 (musicbrainz--deflookup-1 "event"
612                           "| %s | [[https://musicbrainz.org/event/%s][%s]] |\n"
613                           (.name .id .id))
615 ;; lookup ->  musicbrainz-lookup-genre
616 (musicbrainz--deflookup-1 "genre"
617                           "| %s | [[https://musicbrainz.org/genre/%s][%s]] |\n"
618                           (.name .id .id))
620 ;; lookup ->  musicbrainz-lookup-instrument
621 (musicbrainz--deflookup-1 "instrument"
622                           "| %s | %s | [[https://musicbrainz.org/instrument/%s][%s]] |\n"
623                           (.name .type  .id .id))
625 ;; lookup ->  musicbrainz-lookup-label
626 (musicbrainz--deflookup-1 "label"
627                           "| %s | %s | [[https://musicbrainz.org/label/%s][%s]] |\n"
628                           (.name .disambiguation .id .id))
631 ;; lookup ->  musicbrainz-lookup-label-releases
632 (musicbrainz--deflookup-2 "label" "releases"
633                           "%s | %s | [[https://musicbrainz.org/release/%s][%s]] |\n"
634                           (.date .title .id .id)
635                           .releases)
637 ;; lookup ->  musicbrainz-lookup-place
638 (musicbrainz--deflookup-1 "place"
639                           "| %s | [[https://musicbrainz.org/place/%s][%s]] |\n"
640                           (.name .id .id))
642 ;; lookup ->  musicbrainz-lookup-recording
643 (musicbrainz--deflookup-1 "recording"
644                           "| %s | %s | [[https://musicbrainz.org/recording/%s][%s]] |\n"
645                           (.first-release-date .title .id .id))
648 ;; lookup ->  musicbrainz-lookup-recording-artists
649 (musicbrainz--deflookup-2 "recording" "artists"
650                           "%s | [[https://musicbrainz.org/artist/%s][%s]] |\n"
651                           (.artist.name .artist.id .artist.id)
652                           .artist-credit)
654 ;; lookup ->  musicbrainz-lookup-recording-releases
655 (musicbrainz--deflookup-2 "recording" "releases"
656                           "%s | %s |  %s | [[https://musicbrainz.org/release/%s][%s]] |\n"
657                           (.date .title .packaging .id .id)
658                           .releases)
660 ;; lookup ->  musicbrainz-lookup-recording-isrcs
661 (musicbrainz--deflookup-2 "recording" "isrcs"
662                           "%s | [[https://musicbrainz.org/isrc/%s][%s]] |\n"
663                           (.name .id .id)
664                           .isrcs)
666 ;; lookup ->  musicbrainz-lookup-recording-url-rels
667 (musicbrainz--deflookup-2 "recording" "url-rels"
668                           "%s | [[https://musicbrainz.org/recording/%s][%s]] |\n"
669                           (.name .id .id)
670                           .relations)
672 ;; lookup ->  musicbrainz-lookup-release
673 (musicbrainz--deflookup-1 "release"
674                           "| %s | %s | %s | [[https://musicbrainz.org/release/%s][%s]] |\n"
675                           (.date .title .packaging .id .id))
677 ;; lookup ->  musicbrainz-lookup-release-artists
678 (musicbrainz--deflookup-2 "release" "artists"
679                           "%s | [[https://musicbrainz.org/artist/%s][%s]] |\n"
680                           (.artist.name .artist.id .artist.id)
681                           .artist-credit)
683 ;; lookup ->  musicbrainz-lookup-release-collections
685 ;; lookup ->  musicbrainz-lookup-release-labels
687 ;; lookup ->  musicbrainz-lookup-release-recordings
689 ;; lookup ->  musicbrainz-lookup-release-release-groups
691 ;; lookup ->  musicbrainz-lookup-release-group
692 (musicbrainz--deflookup-1 "release-group"
693                           "| %s | %s | %s | [[https://musicbrainz.org/release-group/%s][%s]] |\n"
694                           (.first-release-date .title .primary-type .id .id))
696 ;; lookup ->  musicbrainz-lookup-release-group-artists
697 (musicbrainz--deflookup-2 "release-group" "artists"
698                           "%s | [[https://musicbrainz.org/artist/%s][%s]] |\n"
699                           (.artist.name .artist.id .artist.id)
700                           .artist-credit)
702 ;; lookup ->  musicbrainz-lookup-release-group-releases
704 ;; lookup ->  musicbrainz-lookup-series
705 (musicbrainz--deflookup-1 "series"
706                           "| %s | [[https://musicbrainz.org/series/%s][%s]] |\n"
707                           (.title .id .id))
709 ;; lookup ->  musicbrainz-lookup-work
710 (musicbrainz--deflookup-1 "work"
711                           "| %s | [[https://musicbrainz.org/work/%s][%s]] |\n"
712                           (.title .id .id))
714 ;; lookup ->  musicbrainz-lookup-url
715 (musicbrainz--deflookup-1 "url"
716                           "| %s | [[https://musicbrainz.org/url/%s][%s]] |\n"
717                           (.name .id .id))
721 ;;;;;; ; ; ;; ;   ;     ;  ; ; ;;   ;
722 ;;
723 ;; Browse API
724 ;;  https://musicbrainz.org/doc/MusicBrainz_API#Browse
725 ;;
726 ;;;; ; ; ; ; ;
728 ;; Browse requests are a direct lookup of all the entities directly linked
729 ;; to another entity ("directly linked" here meaning it does not include
730 ;; entities linked by a relationship). For example, you may want to see all
731 ;; releases on the label ubiktune:
733 ;; /ws/2/release?label=47e718e1-7ee4-460c-b1cc-1192a841c6e5
735 ;; Note that browse requests are not searches: in order to browse all the releases
736 ;; on the ubiktune label you will need to know the MBID of ubiktune.
738 ;; The order of the results depends on what linked entity you are browsing
739 ;; by (however it will always be consistent). If you need to sort the entities,
740 ;; you will have to fetch all entities and sort them yourself.
742 ;; Keep in mind only the search request is available without an MBID (or, in
743 ;; specific cases, a disc ID, ISRC or ISWC). If all you have is the name of an
744 ;; artist or album, for example, you'll need to make a search and pick the right
745 ;; result to get its MBID to use it in a lookup or browse request.
748 ;;;###autoload
749 (defun musicbrainz-browse (entity link query &optional type)
750   "Search the MusicBrainz database for ENTITY with LINK matching QUERY.
751 Optionally limit the search to TYPE results for ENTITY."
752   (message "MusicBrainz: browsing %s linked to %s" entity link)
753   (message "url: %s/%s?%s=%s&type=%s&fmt=json" musicbrainz-api-url entity link query type)
754   (let ((response
755           (request-response-data
756            (request
757             (url-encode-url
758              (format "%s/%s?%s=%s&type=%s&fmt=json" musicbrainz-api-url entity link query type))
759             :type "GET"
760             :header (list `("User-Agent" . ,musicbrainz-user-agent))
761             :parser 'json-read
762             :sync t
763             :success (cl-function
764                       (lambda (&key data &allow-other-keys)
765                         (message "ok")))))))
766     response))
770 (provide 'musicbrainz)
772 ;;; musicbrainz.el ends here