]> git.vanrenterghem.biz Git - musicbrainz.git/commitdiff
See the windows in your hand
authornik gaffney <nik@fo.am>
Sat, 20 May 2023 11:45:28 +0000 (13:45 +0200)
committernik gaffney <nik@fo.am>
Sat, 20 May 2023 11:45:28 +0000 (13:45 +0200)
README.org
musicbrainz.el

index 3d0c2894f333f5c2e07eb006bda9a10409d21403..686475f8481f396c1a501ad9b90a601c3b1eec7e 100644 (file)
@@ -1,7 +1,7 @@
 # -*- mode: org; coding: utf-8; -*-
 #+OPTIONS: toc:2 num:nil html-style:nil
 #+author:
-#+title:  MusicBrainz & ListenBrainz & other
+#+title:  MusicBrainz & ListenBrainz & others
 
 * MusicBrainz
 
@@ -11,7 +11,7 @@ MusicBrainz is a community-maintained open source encyclopedia of [[https://musi
 
 This code provides a simple, incomplete yet possibly useful interface to some of the MusicBrainz and ListenBrainz APIs from emacs for exploratory  use in =org-mode= or behind the scenes sending listening metadata.
 
-*  MusicBrainz API
+** MusicBrainz API
 
 - Recording documentation: https://musicbrainz.org/doc/Recording
 - Release documentation: https://musicbrainz.org/doc/Release
@@ -54,11 +54,11 @@ The MBID can be used for specific lookups (and checked if needed using =musicbra
 | Autechre | electronic music duo | Group | [[https://musicbrainz.org/artist/410c9baf-5469-44f6-9852-826524b80c61][410c9baf-5469-44f6-9852-826524b80c61]] |
 
 #+BEGIN_SRC emacs-lisp
-(musicbrainz-lookup-artist-releases  "410c9baf-5469-44f6-9852-826524b80c61")
+(musicbrainz-lookup-artist-releases "410c9baf-5469-44f6-9852-826524b80c61")
 #+END_SRC
 
 #+BEGIN_SRC emacs-lisp
-(musicbrainz-lookup-artist-recordings  "410c9baf-5469-44f6-9852-826524b80c61")
+(musicbrainz-lookup-artist-recordings "410c9baf-5469-44f6-9852-826524b80c61")
 #+END_SRC
 
 #+BEGIN_SRC emacs-lisp
@@ -92,9 +92,7 @@ The MBID can be used for specific lookups (and checked if needed using =musicbra
 
 ** MBID
 
-“One of MusicBrainz' aims is to be the universal lingua franca for music by providing a reliable and unambiguous form of music identification; this music identification is performed through the use of MusicBrainz Identifiers  (MBIDs). An MBID is a 36 character Universally Unique Identifier that is permanently assigned to each entity in the database, i.e. artists, release groups, releases, recordings, works, labels, areas, places and URLs. MBIDs are also assigned to Tracks, though tracks do not share many other properties of entities.”
-
-https://musicbrainz.org/doc/MusicBrainz_Identifier
+“One of MusicBrainz' aims is to be the universal lingua franca for music by providing a reliable and unambiguous form of music identification; this music identification is performed through the use of MusicBrainz Identifiers  (MBIDs). An MBID is a 36 character Universally Unique Identifier that is permanently assigned to each entity in the database, i.e. artists, release groups, releases, recordings, works, labels, areas, places and URLs. MBIDs are also assigned to Tracks, though tracks do not share many other properties of entities.” https://musicbrainz.org/doc/MusicBrainz_Identifier
 
 #+BEGIN_SRC emacs-lisp
 (musicbrainz-artist-to-mbid "Autechre")
@@ -136,7 +134,6 @@ Which returns the MBID =7feb02f2-51fa-422d-838e-2c14ecb4c7b8= for “Tomorrows B
 (musicbrainz-disambiguate-artist "Bad Seeds" 7)
 #+END_SRC
 
-#+RESULTS:
 |     | Artist: Bad Seeds                                         | MBID                                 |
 | 100 | Tomorrows Bad Seeds, nil                                  | [[https://musicbrainz.org/artist/7feb02f2-51fa-422d-838e-2c14ecb4c7b8][7feb02f2-51fa-422d-838e-2c14ecb4c7b8]] |
 |  98 | The Bad Seeds, 60s Texas rock band                        | [[https://musicbrainz.org/artist/3e593712-9f70-4b7a-b21b-466016998a3d][3e593712-9f70-4b7a-b21b-466016998a3d]] |
@@ -146,6 +143,64 @@ Which returns the MBID =7feb02f2-51fa-422d-838e-2c14ecb4c7b8= for “Tomorrows B
 |  50 | The Lightning Seeds, nil                                  | [[https://musicbrainz.org/artist/1ba601a0-3401-4b28-8ddd-9af8203661e8][1ba601a0-3401-4b28-8ddd-9af8203661e8]] |
 |  49 | Seeds, UK dancehall                                       | [[https://musicbrainz.org/artist/a03cf587-a3d3-4847-ac41-e488f779a313][a03cf587-a3d3-4847-ac41-e488f779a313]] |
 
+#+BEGIN_SRC emacs-lisp
+(musicbrainz-lookup-artist "172e1f1a-504d-4488-b053-6344ba63e6d0")
+#+END_SRC
+
+** incompleteness
+
+general, specific and partial API coverage
+
+*** Searching & browsing
+
+Search supports the full  [[https://lucene.apache.org/core/7_7_2/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#package.description][Lucene search syntax]] in queries.
+
+| (musicbrainz-search /type/ /query/ &optional /limit/ /offset/) | see =musicbrainz-search-types= |
+| (musicbrainz-browse /entity/ /link/ /query/ &optional /type/)  | and =musicbrainz-entities-*=   |
+
+| (musicbrainz-search-artist /artist/ &optional /limit/)       |   |
+| (musicbrainz-artist-to-mbid /artist/)                      |   |
+| (musicbrainz-disambiguate-artist /artist/ &optional /limit/) |   |
+| (musicbrainz-search-label /label/ &optional /limit/)         |   |
+| (musicbrainz-search-recording /query/ &optional /limit/)     |   |
+| (musicbrainz-search-release /query/ &optional /limit/)       |   |
+
+*** Lookup queries & subqueries
+
+| (musicbrainz-lookup /entity/ /MBID/ &optional /inc/) | valid entities listed in =musicbrainz-entities-core= |
+
+| *Functions & formatted output*                          | example MBID                         |
+| (musicbrainz-lookup-area /MBID/)                        | [[https://musicbrainz.org/area/c9ac1239-e832-41bc-9930-e252a1fd1105][c9ac1239-e832-41bc-9930-e252a1fd1105]] |
+| (musicbrainz-lookup-artist /MBID/)                      | [[https://musicbrainz.org/artist/410c9baf-5469-44f6-9852-826524b80c61][410c9baf-5469-44f6-9852-826524b80c61]] |
+| (musicbrainz-lookup-artist-recordings /MBID/)           | …                                    |
+| (musicbrainz-lookup-artist-releases /MBID/)             | …                                    |
+| (musicbrainz-lookup-artist-release-groups /MBID/)       | …                                    |
+| (musicbrainz-lookup-artist-works /MBID/)                | …                                    |
+| (musicbrainz-lookup-collection /MBID/)                  | …                                    |
+| (musicbrainz-lookup-collection-user-collections /MBID/) | …                                    |
+| (musicbrainz-lookup-event /MBID/)                       | [[https://musicbrainz.org/event/7c132556-e902-4481-b9cb-ec76a175628a][7c132556-e902-4481-b9cb-ec76a175628a]] |
+| (musicbrainz-lookup-genre /MBID/)                       | [[https://musicbrainz.org/genre/68c81274-5770-4e7b-a4bf-ab0d7d425d99][68c81274-5770-4e7b-a4bf-ab0d7d425d99]] |
+| (musicbrainz-lookup-instrument /MBID/)                  | [[https://musicbrainz.org/instrument/303d4f1a-f799-4c42-9bac-dbedd9139e91][303d4f1a-f799-4c42-9bac-dbedd9139e91]] |
+| (musicbrainz-lookup-label /MBID/)                       | [[https://musicbrainz.org/label/8943d408-940c-403b-a01d-9036c227d50f][8943d408-940c-403b-a01d-9036c227d50f]] |
+| (musicbrainz-lookup-label-releases /MBID/)              | …                                    |
+| (musicbrainz-lookup-place /MBID/)                       | [[https://musicbrainz.org/place/73cba8a4-cacb-45b9-8e02-654f716e2e7a][73cba8a4-cacb-45b9-8e02-654f716e2e7a]] |
+| (musicbrainz-lookup-recording /MBID/)                   | [[https://musicbrainz.org/recording/ef8b34c1-8548-472c-872f-03e0d8d3bb37][ef8b34c1-8548-472c-872f-03e0d8d3bb37]] |
+| (musicbrainz-lookup-recording-artists /MBID/)           | …                                    |
+| (musicbrainz-lookup-recording-releases /MBID/)          | …                                    |
+| (musicbrainz-lookup-recording-isrcs /MBID/)             | …                                    |
+| (musicbrainz-lookup-recording-url-rels /MBID/)          | …                                    |
+| (musicbrainz-lookup-release /MBID/)                     | …                                    |
+| (musicbrainz-lookup-release-artists /MBID/)             | …                                    |
+| (musicbrainz-lookup-release-collections /MBID/)         | …                                    |
+| (musicbrainz-lookup-release-labels /MBID/)              | …                                    |
+| (musicbrainz-lookup-release-recordings /MBID/)          | …                                    |
+| (musicbrainz-lookup-release-release-groups /MBID/)      | …                                    |
+| (musicbrainz-lookup-release-group /MBID/)               | [[https://musicbrainz.org/release-group/fe4acfe9-6d1e-3565-8857-fb16ddc492ab][fe4acfe9-6d1e-3565-8857-fb16ddc492ab]] |
+| (musicbrainz-lookup-release-group-artists /MBID/)       | …                                    |
+| (musicbrainz-lookup-release-group-releases /MBID/)      | …                                    |
+| (musicbrainz-lookup-series /MBID/)                      | …                                    |
+| (musicbrainz-lookup-work /MBID/)                        | …                                    |
+| (musicbrainz-lookup-url /MBID/)                         | …                                    |
 
 
 * ListenBrainz
@@ -153,12 +208,12 @@ Which returns the MBID =7feb02f2-51fa-422d-838e-2c14ecb4c7b8= for “Tomorrows B
 
 [[file:img/listenbrainz-logo.svg]]
 
-* listening
+** listening
 
 - https://listenbrainz.org
 - https://listenbrainz.readthedocs.io/
 
-* examples
+** examples
 
 #+BEGIN_SRC emacs-lisp
 (setq listenbrainz-api-token "000-000-000")
@@ -204,9 +259,8 @@ Which returns the MBID =7feb02f2-51fa-422d-838e-2c14ecb4c7b8= for “Tomorrows B
 (listenbrainz-stats-recordings "zzzkt" 13 "month")
 #+END_SRC
 
-* incompleteness
-
-** Core API endpoints
+** incompleteness
+*** Core API endpoints
 
 https://listenbrainz.readthedocs.io/en/production/dev/api/#core-api-endpoints
 
@@ -227,7 +281,7 @@ https://listenbrainz.readthedocs.io/en/production/dev/api/#core-api-endpoints
 | GET /1/latest-import                                    | -                                 |
 | POST /1/latest-import                                   | -                                 |
 
-** Feedback API Endpoints
+*** Feedback API Endpoints
 
 https://listenbrainz.readthedocs.io/en/production/dev/api/#feedback-api-endpoints
 
@@ -236,13 +290,13 @@ https://listenbrainz.readthedocs.io/en/production/dev/api/#feedback-api-endpoint
 | GET /1/feedback/user/(user_name)/get-feedback-for-recordings | – |
 | GET /1/feedback/user/(user_name)/get-feedback                | - |
 
-** Recording Recommendation API Endpoints
+*** Recording Recommendation API Endpoints
 
 https://listenbrainz.readthedocs.io/en/production/dev/api/#core-api-endpoints
 
 | GET /1/cf/recommendation/user/(user_name)/recording        | - |
 
-** Recording Recommendation Feedback API Endpoints
+*** Recording Recommendation Feedback API Endpoints
 
 https://listenbrainz.readthedocs.io/en/production/dev/api/#recording-recommendation-feedback-api-endpoints
 
@@ -251,7 +305,7 @@ https://listenbrainz.readthedocs.io/en/production/dev/api/#recording-recommendat
 | GET /1/recommendation/feedback/user/(user_name)/recordings | - |
 | GET /1/recommendation/feedback/user/(user_name)            | - |
 
-** Statistics API Endpoints
+*** Statistics API Endpoints
 
 https://listenbrainz.readthedocs.io/en/production/dev/api/#statistics-api-endpoints
 
@@ -263,13 +317,13 @@ https://listenbrainz.readthedocs.io/en/production/dev/api/#statistics-api-endpoi
 | GET /1/stats/user/(user_name)/releases           | listenbrainz-stats-releases   |
 | GET /1/stats/user/(user_name)/artists            | listenbrainz-stats-artists    |
 
-** Status API Endpoints
+*** Status API Endpoints
 
 https://listenbrainz.readthedocs.io/en/production/dev/api/#status-api-endpoints
 
 | GET /1/status/get-dump-info | - |
 
-** User Timeline API Endpoints
+*** User Timeline API Endpoints
 
 https://listenbrainz.readthedocs.io/en/production/dev/api/#user-timeline-api-endpoints
 
@@ -278,7 +332,7 @@ https://listenbrainz.readthedocs.io/en/production/dev/api/#user-timeline-api-end
 | POST /1/user/(user_name)/feed/events/delete                 | - |
 | GET /1/user/(user_name)/feed/events                         | - |
 
-** Social API Endpoints
+*** Social API Endpoints
 
 https://listenbrainz.readthedocs.io/en/production/dev/api/#social-api-endpoints
 
@@ -287,7 +341,7 @@ https://listenbrainz.readthedocs.io/en/production/dev/api/#social-api-endpoints
 | POST /1/user/(user_name)/unfollow | -                      |
 | POST /1/user/(user_name)/follow   | -                      |
 
-** Pinned Recording API Endpoints
+*** Pinned Recording API Endpoints
 
 https://listenbrainz.readthedocs.io/en/production/dev/api/#pinned-recording-api-endpoints
 
@@ -305,5 +359,7 @@ https://listenbrainz.readthedocs.io/en/production/dev/api/#pinned-recording-api-
  - [[https://coverartarchive.org/][Cover art archive]]
 
 * further
+- https://labs.api.listenbrainz.org/
+- https://troi.readthedocs.io/en/latest/
 - https://listenbrainz.org/user/troi-bot/playlists/
 - https://github.com/metabrainz/bono-data-sets/blob/main/top_discoveries.py
index 4107f4e5a391791ec3f03fc3b88bc7393fd55ece..068cbb7daf2565cd9151c41e146f5f6dc6dbf610 100644 (file)
 (require 'request)
 (require 'json)
 
+;; debug level for http requests
+(setq request-log-level 'warn
+      request-message-level 'warn)
+
 
 ;;; ;; ;; ;  ; ;   ;  ;      ;
 ;;
@@ -56,6 +60,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
@@ -159,7 +169,7 @@ 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))
@@ -171,6 +181,11 @@ An MBID is a 36 character Universally Unique Identifier, see https://musicbrainz
   (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 +227,7 @@ 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)
   (let* ((max (if limit limit 1))
          (from (if offset offset ""))
          (response
@@ -222,6 +237,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 +246,30 @@ 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))
         )))
 
 
-
 ;; 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 +286,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 +299,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 +314,6 @@ Outputs an `org-mode' table with descriptions and MBID link to artists pages."
                       .artists)))))
 
 
-;;;###autoload
 (defun musicbrainz-search-label (label &optional limit)
   "Search for a LABEL and show matches.
 Optionally return LIMIT number of results."
@@ -332,7 +337,6 @@ Optionally return LIMIT number of results."
       .labels))))
 
 
-;;;###autoload
 (defun musicbrainz-search-recording (query &optional limit)
   "Search for a recording using QUERY and show matches.
 Optionally return LIMIT number of results."
@@ -347,11 +351,6 @@ Optionally return LIMIT number of results."
       .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."
@@ -375,26 +374,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 ""))
@@ -415,58 +414,213 @@ Subqueries
              musicbrainz-entities-core)))
 
 
-;; 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))))
-
+;; 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
 
-(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))))
+;; lookup ->  musicbrainz-lookup-series
+(musicbrainz--deflookup-1 "series"
+                           "| %s | [[https://musicbrainz.org/series/%s][%s]] |\n"
+                          (.name .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))))
+;; lookup ->  musicbrainz-lookup-work
+(musicbrainz--deflookup-1 "work"
+                           "| %s | [[https://musicbrainz.org/work/%s][%s]] |\n"
+                          (.name .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 +655,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 +663,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