]> git.vanrenterghem.biz Git - musicbrainz.git/blob - musicbrainz.el
See the windows in your hand
[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 ;; - basic MusicBrainz interface
31 ;; - partial & incomplete
32 ;; - no error checks
33 ;; - sync -> async
36 ;;; Code:
38 (require 'request)
39 (require 'json)
41 ;; debug level for http requests
42 (setq request-log-level 'warn
43       request-message-level 'warn)
46 ;;; ;; ;; ;  ; ;   ;  ;      ;
47 ;;
48 ;; API config
49 ;;
50 ;;; ; ;; ;;
52 (defcustom musicbrainz-api-url "https://musicbrainz.org/ws/2"
53   "URL for musicbrainz API.
54 Documentation available at https://musicbrainz.org/doc/MusicBrainz_API"
55   :type 'string
56   :group 'musicbrainz)
58 (defcustom musicbrainz-api-token ""
59   "An auth token is required for some functions."
60   :type 'string
61   :group 'musicbrainz)
63 (defcustom musicbrainz-user-agent "musicbrainz.el/0.1"
64   "A User-Agent header to identify source of API requests.
65 As seen in https://wiki.musicbrainz.org/MusicBrainz_API/Rate_Limiting"
66   :type 'string
67   :group 'musicbrainz)
69 ;;; ; ; ;;;   ;  ;
70 ;;
71 ;; API entities
72 ;;  https://musicbrainz.org/doc/MusicBrainz_API#Browse
73 ;;
74 ;; On each entity resource, you can perform three different GET requests:
75 ;;  lookup:   /<ENTITY_TYPE>/<MBID>?inc=<INC>
76 ;;  browse:   /<RESULT_ENTITY_TYPE>?<BROWSING_ENTITY_TYPE>=<MBID>&limit=<LIMIT>&offset=<OFFSET>&inc=<INC>
77 ;;  search:   /<ENTITY_TYPE>?query=<QUERY>&limit=<LIMIT>&offset=<OFFSET>
78 ;;
79 ;; Note: Keep in mind only the search request is available without an MBID
80 ;; (or, in specific cases, a disc ID, ISRC or ISWC). If all you have is the
81 ;; name of an artist or album, for example, you'll need to make a search and
82 ;; pick the right result to get its MBID; only then will you able to use it
83 ;; in a lookup or browse request.
84 ;;
85 ;; On the genre resource, we support an "all" sub-resource to fetch all genres,
86 ;; paginated, in alphabetical order:
87 ;;
88 ;;  all:      /genre/all?limit=<LIMIT>&offset=<OFFSET>
89 ;;
90 ;; ; ;;; ;
92 (defconst musicbrainz-entities-core
93   (list "area" "artist" "event" "genre" "instrument" "label" "place"
94         "recording" "release" "release-group" "series" "work" "url")
95   "API resources which represent core entities in the MusicBrainz database.")
97 (defconst musicbrainz-entities-non-core
98   (list "rating" "tag" "collection")
99   "API resources which represent non-core entities in the MusicBrainz database.")
101 (defconst musicbrainz-entities-uids
102   (list "discid" "isrc" "iswc")
103   "API resources based on other unique identifiers in the MusicBrainz database.")
105 (defconst musicbrainz-entities-linked
106   (list "area" "artist" "collection" "event" "instrument" "label" "place"
107         "recording" "release" "release-group" "series" "work" "url")
108   "API resources for linked entites in the MusicBrainz database.")
110 (defconst musicbrainz-search-types
111   (list "annotation" "area" "artist" "cdstub" "event" "instrument"
112         "label" "place" "recording" "release" "release-group"
113         "series" "tag" "work" "url")
114   "Valid TYPE parameters for MusicBrainz searches.")
117 ;; entity checks
119 (defun musicbrainz-core-entity-p (entity)
120   "Check if ENTITY is a core entity."
121   (if (member entity musicbrainz-entities-core) t nil))
123 (defun musicbrainz-non-core-entity-p (entity)
124   "Check if ENTITY is a non-core entity."
125   (if (member entity musicbrainz-entities-non-core) t nil))
127 (defun musicbrainz-uid-entity-p (entity)
128   "Check if ENTITY is a unique identifier entity."
129   (if (member entity musicbrainz-entities-uids) t nil))
131 (defun musicbrainz-search-type-p (type)
132   "Check if TYPE is a valid search type."
133   (if (member type musicbrainz-search-types) t nil))
136 ;; Linked entities
138 (defun musicbrainz-linked-entity-p (entity)
139   "Check if ENTITY can be used in a browse request (incomplete).
141 The following list shows which linked entities you can use in a browse request:
143  /ws/2/area            collection
144  /ws/2/artist          area, collection, recording, release, release-group, work
145  /ws/2/collection      area, artist, editor, event, label, place, recording,
146                        release, release-group, work
147  /ws/2/event           area, artist, collection, place
148  /ws/2/instrument      collection
149  /ws/2/label           area, collection, release
150  /ws/2/place           area, collection
151  /ws/2/recording       artist, collection, release, work
152  /ws/2/release         area, artist, collection, label, track, track_artist,
153                        recording, release-group
154  /ws/2/release-group   artist, collection, release
155  /ws/2/series          collection
156  /ws/2/work            artist, collection
157  /ws/2/url             resource"
159   (if (member entity musicbrainz-entities-linked) t nil))
162 ;; utils & aux
164 (defun musicbrainz-mbid-p (mbid)
165   "Check (permissive) if MBID is valid and/or well formatted.
166 An MBID is a 36 character Universally Unique Identifier, see https://musicbrainz.org/doc/MusicBrainz_Identifier for details."
168   (if (and (length= mbid 36) ;; length= requires emacs > 28.1
169            (string-match-p
170             (rx (repeat 8 hex)        ;;  [A-F0-9]{8}
171                 "-" (repeat 4 hex)    ;; -[A-F0-9]{4}
172                 "-" (repeat 4 hex)    ;; -[34][A-F0-9]{3}
173                 "-" (repeat 4 hex)    ;; -[89AB][A-F0-9]{3}
174                 "-" (repeat 12 hex))  ;; -[A-F0-9]{12}
175             mbid))
176       t nil))
179 (defun musicbrainz-format (response)
180   "Format a generic RESPONSE."
181   (format "%s" (pp response)))
184 (defun musicbrainz--unwrap-0 (entity)
185   "Unwrap (fragile) .artist-credit ENTITY -> .name more or less."
186   (format "%s" (cdar (aref entity 0))))
189 ;;; ;; ;; ;  ; ;   ;  ;      ;
190 ;;
191 ;; Search API
192 ;;  https://musicbrainz.org/doc/MusicBrainz_API/Search
193 ;;
194 ;; The MusicBrainz API search requests provide a way to search for MusicBrainz
195 ;; entities based on different sorts of queries and are provided by a search
196 ;; server built using Lucene technology.
197 ;;
198 ;; Parameters common to all resources
199 ;;
200 ;;  type    Selects the entity index to be searched: annotation, area, artist,
201 ;;          cdstub, event, instrument, label, place, recording, release,
202 ;;          release-group, series, tag, work, url
203 ;;
204 ;;  query   Lucene search query. This is mandatory
205 ;;
206 ;;  limit   An integer value defining how many entries should be returned.
207 ;;          Only values between 1 and 100 (both inclusive) are allowed.
208 ;;          If not given, this defaults to 25.
209 ;;
210 ;;  offset  Return search results starting at a given offset.
211 ;;          Used for paging through more than one page of results.
212 ;;
213 ;;  dismax  If set to "true", switches the Solr query parser from edismax to dismax,
214 ;;          which will escape certain special query syntax characters by default
215 ;;          for ease of use. This is equivalent to switching from the "Indexed search
216 ;;          with advanced query syntax" method to the plain "Indexed search" method
217 ;;          on the website. Defaults to "false".
218 ;;
219 ;; ;; ; ;  ;
221 ;;;###autoload
222 (defun musicbrainz-search (type query &optional limit offset)
223   "Search the MusicBrainz database for TYPE matching QUERY.
224 Optionally return only LIMIT number of results from OFFSET.
226 The QUERY field supports the full Lucene Search syntax, some details
227 can be found near https://musicbrainz.org/doc/MusicBrainz_API/Search
228 or in the Lucene docs."
230   (message "MusicBrainz: searching %s=%s" type query)
231   (let* ((max (if limit limit 1))
232          (from (if offset offset ""))
233          (response
234            (request-response-data
235             (request
236              (url-encode-url
237               (format "%s/%s?query=%s&fmt=json&limit=%s&offset=%s"
238                       musicbrainz-api-url type query max from))
239              :type "GET"
240              :headers (list `("User-Agent" . ,musicbrainz-user-agent))
241              :parser 'json-read
242              :sync t
243              :success (cl-function
244                        (lambda (&key data &allow-other-keys)
245                          (message "ok")))))))
246     response))
249 ;;;###autoload
250 (defun musicbrainz-find (query &rest extras)
251   "Search MusicBrainz for QUERY (and EXTRAS) or recommend a more specific search.
252 MusicBrainz makes a distinction between `search', `lookup' and `browse' this
253 provides a more general entry point to searching/browsing the database.
255 Heuristics.
256 - not yet
257 - if QUERY is an MBID, check artist, recording, etc
258 - if QUERY is text, search for artists or recordings, etc"
260   (message "MusicBrainz: query %s" query)
261   (if (musicbrainz-mbid-p query)
262       ;; search (lookup) for things that could have an mbid
263       (let ((mbid query))
264         (message "searching mbid: %s" mbid))
265       ;; search (search/browse/query) for other things
266       (progn
267         (message "searching other: %s" mbid)
268         )))
271 ;; various specific searches
273 (defun musicbrainz-search-artist (artist &optional limit)
274   "Search for an ARTIST and show matches.
275 Optionally return LIMIT number of results."
276   (let ((data (musicbrainz-search "artist" artist limit)))
277     (let-alist
278      data
279      (seq-map
280       (lambda (i)
281         (let-alist i
282                    (if (not limit)
283                        (format "%s | %s |\n" .name .id)
284                        (format "%s | %s | %s |\n"
285                                .score .name .id))))
286       .artists))))
289 (defun musicbrainz-artist-to-mbid (artist)
290   "Find an MBID for ARTIST (with 100% match).
291 See `musicbrainz-disambiguate-artist' if there are multiple matches."
292   (let ((data (musicbrainz-search "artist" artist)))
293     (let-alist data
294                (car (remove nil (seq-map
295                                  (lambda (i)
296                                    (let-alist i
297                                               (when (= 100 .score)
298                                                 (format "%s" .id))))
299                                  .artists))))))
302 (defun musicbrainz-disambiguate-artist (artist &optional limit)
303   "More ARTIST data. less ambiguity (with optional LIMIT).
304 Outputs an `org-mode' table with descriptions and MBID link to artists pages."
305   (let* ((max (if limit limit 11))
306          (data (musicbrainz-search "artist" artist max)))
307     (let-alist data
308                (cons (format "| Artist: %s| MBID |\n" artist)
309                      (seq-map
310                       (lambda (i)
311                         (let-alist i
312                                    (format "%s | %s, %s | [[https://musicbrainz.org/artist/%s][%s]] |\n"
313                                            .score .name .disambiguation .id .id)))
314                       .artists)))))
317 (defun musicbrainz-search-label (label &optional limit)
318   "Search for a LABEL and show matches.
319 Optionally return LIMIT number of results."
320   (let ((data (musicbrainz-search "label" label limit)))
321     (let-alist
322      data
323      (seq-map
324       (lambda (i)
325         (let-alist i
326                    (if (not limit)
327                        (format "%s | [[https://musicbrainz.org/label/%s][%s]] |\n" .name .id .id)
328                        (format "%s | %s | %s (%s%s) |  [[https://musicbrainz.org/label/%s][%s]]  |\n"
329                                .score .name
330                                (if .disambiguation .disambiguation "")
331                                (if .life-span.begin
332                                    (format "%s " .life-span.begin) "")
333                                (if .life-span.end
334                                    (format "—%s" .life-span.end)
335                                    "ongoing")
336                                .id .id))))
337       .labels))))
340 (defun musicbrainz-search-recording (query &optional limit)
341   "Search for a recording using QUERY and show matches.
342 Optionally return LIMIT number of results."
343   (let ((data (musicbrainz-search "recording" query limit)))
344     (let-alist
345      data
346      (seq-map
347       (lambda (i)
348         (let-alist i
349                    (format "%s | %s, %s | [[https://musicbrainz.org/release/%s][%s]] |\n"
350                            .score .title (musicbrainz--unwrap-0 .artist-credit) .id .id)))
351       .recordings))))
354 (defun musicbrainz-search-release (query &optional limit)
355   "Search for a release using QUERY and show matches.
356 Optionally return LIMIT number of results."
357   (let ((data (musicbrainz-search "release" query limit)))
358     (let-alist
359      data
360      (seq-map
361       (lambda (i)
362         (let-alist i
363                    (format "%s | %s, %s | [[https://musicbrainz.org/release/%s][%s]] |\n"
364                            .score .title (musicbrainz--unwrap-0 .artist-credit) .id .id)))
365       .releases))))
368 ;;; ;; ;; ;  ; ;   ;  ;      ;
369 ;;
370 ;; Lookups
371 ;;  https://musicbrainz.org/doc/MusicBrainz_API#Lookups
372 ;;
373 ;;; ;; ;;   ; ;
375 ;;;###autoload
376 (defun musicbrainz-lookup (entity mbid &optional inc)
377   "Search (lookup not browse) MusicBrainz for ENTITY with MBID.
378 Optionally add an INC list.
380 Subqueries
381  /ws/2/area
382  /ws/2/artist          recordings, releases, release-groups, works
383  /ws/2/collection      user-collections (requires authentication)
384  /ws/2/event
385  /ws/2/genre
386  /ws/2/instrument
387  /ws/2/label           releases
388  /ws/2/place
389  /ws/2/recording       artists, releases, isrcs, url-rels
390  /ws/2/release         artists, collections, labels, recordings, release-groups
391  /ws/2/release-group   artists, releases
392  /ws/2/series
393  /ws/2/work
394  /ws/2/url"
396   (message "MusicBrainz: lookup: %s/%s" entity mbid)
397   (if (and (musicbrainz-core-entity-p entity)
398            (musicbrainz-mbid-p mbid))
399       (let* ((add (if inc inc ""))
400              (response
401                (request-response-data
402                 (request
403                  (url-encode-url
404                   (format "%s/%s/%s?inc=%s&fmt=json"
405                           musicbrainz-api-url entity mbid add))
406                  :type "GET"
407                  :parser 'json-read
408                  :sync t
409                  :success (cl-function
410                            (lambda (&key data &allow-other-keys)
411                              (message "%s data: %s" entity mbid)))))))
412         response)
413       (error "MusicBrainz: search requires a valid MBID and entity (i.e. one of %s)"
414              musicbrainz-entities-core)))
417 ;; specific MBID lookup requests & subrequests (limited to 25 results?)
419 (defmacro musicbrainz--deflookup-1 (name format-string format-args)
420   "Generate lookup function to format a single item.
421 NAME FORMAT-STRING FORMAT-ARGS
422 See listenbrainz--deformatter for details."
423   (let ((f (intern (concat "musicbrainz-lookup-" name)))
424         (doc "MusicBrainz lookup."))
425     `(defun ,f (mbid) ,doc
426        (let ((response
427                (musicbrainz-lookup ,name mbid)))
428          (let-alist response
429                     (format ,format-string ,@format-args))))))
432 (defmacro musicbrainz--deflookup-2 (query subquery format-string format-args alist)
433   "Generate lookup function to format multiple items.
434 QUERY SUBQUERY FORMAT-STRING FORMAT-ARGS ALIST
435 See listenbrainz--deformatter for details."
436   (let ((f (intern (format "musicbrainz-lookup-%s-%s" query subquery)))
437         (doc "MusicBrainz lookup."))
438     `(defun ,f (mbid) ,doc
439        (let ((response
440                (musicbrainz-lookup ,query mbid ,subquery)))
441          (let-alist response
442                     (seq-map
443                      (lambda (i)
444                        (let-alist i
445                                   (format ,format-string ,@format-args)))
446                      ,alist))))))
449 ;; (defun musicbrainz-lookup-artist (mbid)
450 ;;   "MusicBrainz lookup for artist with MBID."
451 ;;   (let ((response
452 ;;           (musicbrainz-lookup "artist" mbid)))
453 ;;     (let-alist response
454 ;;                (format "| %s | %s | %s | [[https://musicbrainz.org/artist/%s][%s]] |\n"
455 ;;                        .name .disambiguation .type .id .id))))
458 ;; (defun musicbrainz-lookup-artist-recordings (mbid)
459 ;;   "MusicBrainz lookup for recordings from artist with MBID."
460 ;;   (let ((response
461 ;;           (musicbrainz-lookup "artist" mbid "recordings")))
462 ;;     (let-alist response
463 ;;                (seq-map
464 ;;                 (lambda (i)
465 ;;                   (let-alist i
466 ;;                              (format "%s | [[https://musicbrainz.org/recording/%s][%s]] |\n"
467 ;;                                      .title .id .id)))
468 ;;                 .recordings))))
471 ;; lookup ->  musicbrainz-lookup-area
472 (musicbrainz--deflookup-1 "area"
473                            "| %s | [[https://musicbrainz.org/area/%s][%s]] |\n"
474                           (.name .id .id))
476 ;; lookup ->  musicbrainz-lookup-artist
477 (musicbrainz--deflookup-1 "artist"
478                            "| %s | %s | %s | [[https://musicbrainz.org/artist/%s][%s]] |\n"
479                           (.name .disambiguation .type .id .id))
481 ;; lookup ->  musicbrainz-lookup-artist-recordings
482 (musicbrainz--deflookup-2 "artist" "recordings"
483                           "%s | [[https://musicbrainz.org/recording/%s][%s]] |\n"
484                           (.title .id .id)
485                           .recordings)
487 ;; lookup ->  musicbrainz-lookup-artist-releases
488 (musicbrainz--deflookup-2 "artist" "releases"
489                            "%s | %s | %s | [[https://musicbrainz.org/release/%s][%s]] |\n"
490                           (.date .title .packaging .id .id)
491                           .releases)
493 ;; lookup ->  musicbrainz-lookup-artist-release-groups
494 (musicbrainz--deflookup-2 "artist" "release-groups"
495                            "%s | %s | %s | [[https://musicbrainz.org/release-group/%s][%s]] |\n"
496                           (.first-release-date .title .primary-type .id .id)
497                           .release-groups)
499 ;; lookup ->  musicbrainz-lookup-artist-works
500 (musicbrainz--deflookup-2 "artist" "works"
501                            " %s | [[https://musicbrainz.org/work/%s][%s]] |\n"
502                           (.title .id .id)
503                           .works)
505 ;; lookup ->  musicbrainz-lookup-collection
506 (musicbrainz--deflookup-1 "collection"
507                           "| %s | [[https://musicbrainz.org/collection/%s][%s]] |\n"
508                           (.name .id .id))
510 ;; lookup ->  musicbrainz-lookup-collection-user-collections (requires authentication)
511 (musicbrainz--deflookup-2 "collection" "user-collections"
512                            " %s | [[https://musicbrainz.org/collection/%s][%s]] |\n"
513                           (.name .id .id)
514                           .collection)
516 ;; lookup ->  musicbrainz-lookup-event
517 (musicbrainz--deflookup-1 "event"
518                            "| %s | [[https://musicbrainz.org/event/%s][%s]] |\n"
519                           (.name .id .id))
521 ;; lookup ->  musicbrainz-lookup-genre
522 (musicbrainz--deflookup-1 "genre"
523                            "| %s | [[https://musicbrainz.org/genre/%s][%s]] |\n"
524                           (.name .id .id))
526 ;; lookup ->  musicbrainz-lookup-instrument
527 (musicbrainz--deflookup-1 "instrument"
528                            "| %s | %s | [[https://musicbrainz.org/instrument/%s][%s]] |\n"
529                           (.name .type  .id .id))
531 ;; lookup ->  musicbrainz-lookup-label
532 (musicbrainz--deflookup-1 "label"
533                            "| %s | %s | [[https://musicbrainz.org/label/%s][%s]] |\n"
534                           (.name .disambiguation .id .id))
537 ;; lookup ->  musicbrainz-lookup-label-releases
538 (musicbrainz--deflookup-2 "label" "releases"
539                            "%s | %s | [[https://musicbrainz.org/release/%s][%s]] |\n"
540                           (.date .title .id .id)
541                           .releases)
543 ;; lookup ->  musicbrainz-lookup-place
544 (musicbrainz--deflookup-1 "place"
545                            "| %s | [[https://musicbrainz.org/place/%s][%s]] |\n"
546                           (.name .id .id))
548 ;; lookup ->  musicbrainz-lookup-recording
549 (musicbrainz--deflookup-1 "recording"
550                            "| %s | %s | [[https://musicbrainz.org/recording/%s][%s]] |\n"
551                           (.first-release-date .title .id .id))
554 ;; lookup ->  musicbrainz-lookup-recording-artists
555 (musicbrainz--deflookup-2 "recording" "artists"
556                            "%s | [[https://musicbrainz.org/artist/%s][%s]] |\n"
557                           (.artist.name .artist.id .artist.id)
558                           .artist-credit)
560 ;; lookup ->  musicbrainz-lookup-recording-releases
561 (musicbrainz--deflookup-2 "recording" "releases"
562                            "%s | %s |  %s | [[https://musicbrainz.org/release/%s][%s]] |\n"
563                           (.date .title .packaging .id .id)
564                           .releases)
566 ;; lookup ->  musicbrainz-lookup-recording-isrcs
567 (musicbrainz--deflookup-2 "recording" "isrcs"
568                            "%s | [[https://musicbrainz.org/isrc/%s][%s]] |\n"
569                           (.name .id .id)
570                           .isrcs)
572 ;; lookup ->  musicbrainz-lookup-recording-url-rels
573 (musicbrainz--deflookup-2 "recording" "url-rels"
574                            "%s | [[https://musicbrainz.org/recording/%s][%s]] |\n"
575                           (.name .id .id)
576                           .relations)
578 ;; lookup ->  musicbrainz-lookup-release
579 (musicbrainz--deflookup-1 "release"
580                           "| %s | %s | %s | [[https://musicbrainz.org/release/%s][%s]] |\n"
581                           (.date .title .packaging .id .id))
583 ;; lookup ->  musicbrainz-lookup-release-artists
584 (musicbrainz--deflookup-2 "release" "artists"
585                           "%s | [[https://musicbrainz.org/artist/%s][%s]] |\n"
586                           (.artist.name .artist.id .artist.id)
587                           .artist-credit)
589 ;; lookup ->  musicbrainz-lookup-release-collections
591 ;; lookup ->  musicbrainz-lookup-release-labels
593 ;; lookup ->  musicbrainz-lookup-release-recordings
595 ;; lookup ->  musicbrainz-lookup-release-release-groups
597 ;; lookup ->  musicbrainz-lookup-release-group
598 (musicbrainz--deflookup-1 "release-group"
599                            "| %s | %s | %s | [[https://musicbrainz.org/release-group/%s][%s]] |\n"
600                           (.first-release-date .title .primary-type .id .id))
602 ;; lookup ->  musicbrainz-lookup-release-group-artists
603 (musicbrainz--deflookup-2 "release-group" "artists"
604                           "%s | [[https://musicbrainz.org/artist/%s][%s]] |\n"
605                           (.artist.name .artist.id .artist.id)
606                           .artist-credit)
608 ;; lookup ->  musicbrainz-lookup-release-group-releases
610 ;; lookup ->  musicbrainz-lookup-series
611 (musicbrainz--deflookup-1 "series"
612                            "| %s | [[https://musicbrainz.org/series/%s][%s]] |\n"
613                           (.name .id .id))
615 ;; lookup ->  musicbrainz-lookup-work
616 (musicbrainz--deflookup-1 "work"
617                            "| %s | [[https://musicbrainz.org/work/%s][%s]] |\n"
618                           (.name .id .id))
620 ;; lookup ->  musicbrainz-lookup-url
621 (musicbrainz--deflookup-1 "url"
622                            "| %s | [[https://musicbrainz.org/url/%s][%s]] |\n"
623                           (.name .id .id))
627 ;;;;;; ; ; ;; ;   ;     ;  ; ; ;;   ;
628 ;;
629 ;; Browse API
630 ;;  https://musicbrainz.org/doc/MusicBrainz_API#Browse
631 ;;
632 ;;;; ; ; ; ; ;
634 ;; Browse requests are a direct lookup of all the entities directly linked
635 ;; to another entity ("directly linked" here meaning it does not include
636 ;; entities linked by a relationship). For example, you may want to see all
637 ;; releases on the label ubiktune:
639 ;; /ws/2/release?label=47e718e1-7ee4-460c-b1cc-1192a841c6e5
641 ;; Note that browse requests are not searches: in order to browse all the releases
642 ;; on the ubiktune label you will need to know the MBID of ubiktune.
644 ;; The order of the results depends on what linked entity you are browsing
645 ;; by (however it will always be consistent). If you need to sort the entities,
646 ;; you will have to fetch all entities and sort them yourself.
648 ;; Keep in mind only the search request is available without an MBID (or, in
649 ;; specific cases, a disc ID, ISRC or ISWC). If all you have is the name of an
650 ;; artist or album, for example, you'll need to make a search and pick the right
651 ;; result to get its MBID to use it in a lookup or browse request.
654 ;;;###autoload
655 (defun musicbrainz-browse (entity link query &optional type)
656   "Search the MusicBrainz database for ENTITY with LINK matching QUERY.
657 Optionally limit the search to TYPE results for ENTITY."
658   (message "MusicBrainz: browsing %s linked to %s" entity link)
659   (message "url: %s/%s?%s=%s&type=%s&fmt=json" musicbrainz-api-url entity link query type)
660   (let ((response
661           (request-response-data
662            (request
663             (url-encode-url
664              (format "%s/%s?%s=%s&type=%s&fmt=json" musicbrainz-api-url entity link query type))
665             :type "GET"
666             :header (list `("User-Agent" . ,musicbrainz-user-agent))
667             :parser 'json-read
668             :sync t
669             :success (cl-function
670                       (lambda (&key data &allow-other-keys)
671                         (message "ok")))))))
672     response))
675 ;;;
677 (provide 'musicbrainz)
679 ;;; musicbrainz.el ends here