diff --git a/AGENTS.md b/AGENTS.md index eb2c74fc..9f60b0d4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,7 +22,6 @@ - Kotlin: `2.3.0`, JVM target: `11` (compile/target compatibility in both `composeApp/build.gradle.kts` and `plugin_interfaces/build.gradle.kts`). ## Codegen and plugin packaging -- OpenAPI client generated from `composeApp/specs/listenbrainz-openapi.yaml` via KMPGen tasks (`kmpgenPrepare`, `kmpgenGenerateAll`) in `:composeApp`. - JS plugin bundles: `:js_plugin_example:packageDevelopmentPlugin` and `:js_plugin_example:packageProductionPlugin`. Output is `.smplug` files in `js_plugin_example/build/distributions/`. - Zipline plugin entrypoint: `mainFunction = "dev.krtirtho.js_plugin_example.main"` in `js_plugin_example/build.gradle.kts`. Plugin metadata from `js_plugin_example/plugin.json`. diff --git a/build.gradle.kts b/build.gradle.kts index c2b11201..eb5958c7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -29,6 +29,5 @@ plugins { alias(libs.plugins.androidKotlinMultiplatformLibrary) apply false alias(libs.plugins.zipline.gradle.plugin) apply false alias(libs.plugins.spotubeGradle) apply false - alias(libs.plugins.kmpgen) apply false alias(libs.plugins.vlcjBundler) apply false } \ No newline at end of file diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index c0a1fd96..5252f648 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -29,7 +29,6 @@ plugins { alias(libs.plugins.composeHotReload) alias(libs.plugins.kotlinSerialization) alias(libs.plugins.zipline.gradle.plugin) - alias(libs.plugins.kmpgen) alias(libs.plugins.vlcjBundler) } @@ -38,14 +37,6 @@ vlcjBundler { objectName = "VLCBundleLoaderGenerated" // or rename the object } -kmpgen { - spec( - packageName = "dev.krtirtho.spotube.listenbrainz" - ) { - specFile = file("./specs/listenbrainz-openapi.yaml") - } -} - kotlin { // Note: For Android application modules, androidTarget() is still required as of AGP 8.x. // The deprecation warning is expected. For libraries, use the androidKotlinMultiplatformLibrary plugin instead. @@ -103,6 +94,8 @@ kotlin { // ktor implementation(libs.ktor.client.core) implementation(libs.ktor.client.logging) + implementation(libs.ktor.client.content.negotiation) + implementation(libs.ktor.client.serialization.kotlinx.json) implementation(libs.ktor.client.cio) implementation(libs.ktor.server.core) implementation(libs.ktor.server.cio) diff --git a/composeApp/specs/listenbrainz-openapi.yaml b/composeApp/specs/listenbrainz-openapi.yaml deleted file mode 100644 index 73ed71bc..00000000 --- a/composeApp/specs/listenbrainz-openapi.yaml +++ /dev/null @@ -1,12322 +0,0 @@ -# Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -openapi: 3.0.3 -info: - description: OpenAPI client defintion. - license: - name: Apache 2.0 - url: http://www.apache.org/licenses/LICENSE-2.0.html - termsOfService: https://listenbrainz.org/terms-of-service/ - title: ListenBrainz Swagger - OpenAPI 3.0 - version: v-2025-11-04.0 -servers: -- url: https://api.listenbrainz.org -tags: -- description: |- - ListenBrainz has a statistics infrastructure that collects and computes - statistics from the listen data that has been stored in the database. - The endpoints in this section offer a way to get this data programmatically. - name: lbStats -- description: The ListenBrainz server supports the following end-points for submitting - and fetching listens. - name: lbCore -- description: The playlists API allows for the creation and editing of lists of recordings. - name: lbPlaylists -- description: Feedback API and Pinned Recording API. - name: lbRecordings -- description: The popularity APIs return the total listen and listeners count for - various entities and also a way to query top entities for a given artist. - name: lbPopularity -- description: The metadata API looks up MusicBrainz metadata for recordings. - name: lbMetadata -- description: These api endpoints allow to create and fetch timeline events for a - user. - name: lbSocial -- description: "ListenBrainz uses collaborative filtering to generate recording recommendations,\ - \ which may be further processed to generate playlists for users." - name: lbRecommendations -- description: Various ListenBrainz API endpoints that are not documented elsewhere. - name: lbMisc -- description: ListenBrainz has a (cover) art infrastructure that creates new cover - art from a user's statistics or a user's instructions on how to composite a cover - art grid. - name: lbArt -paths: - /1/search/users: - get: - operationId: searchUsers - parameters: - - description: Input on which search operation is to be performed. - in: query - name: search_term - required: true - schema: - type: string - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/searchUsers" - description: Successful operation. - summary: Search a ListenBrainz-registered user. - tags: - - lbCore - /1/submit-listens: - post: - operationId: submitListens - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/submitListens" - required: true - responses: - "200": - description: Listen(s) accepted. - "400": - description: Invalid JSON sent. - "401": - description: Invalid authorization. - security: - - ApiKeyAuth: [] - summary: Submit listens to the server. - tags: - - lbCore - /1/validate-token: - get: - operationId: validateToken - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/validateToken" - description: The user token is valid/invalid. - "400": - description: No token was sent to the endpoint. - security: - - ApiKeyAuth: [] - summary: Check whether a User Token is a valid entry in the database. - tags: - - lbCore - /1/user/{user_name}/listens: - get: - operationId: listensForUser - parameters: - - in: path - name: user_name - required: true - schema: - type: string - - description: "If you specify a 'max_ts' timestamp, listens with listened_at\ - \ less than (but not including) this value will be returned." - in: query - name: max_ts - schema: - type: integer - - description: "If you specify a 'min_ts' timestamp, listens with listened_at\ - \ greater than (but not including) this value will be returned." - in: query - name: min_ts - schema: - type: integer - - description: "Optional, number of listens to return." - in: query - name: count - required: false - schema: - default: 25 - type: integer - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/listensForUser" - description: "Yay, you have data!" - "404": - description: The requested user was not found. - summary: Get listens for user 'user_name'. - tags: - - lbCore - /1/user/{user_name}/listen-count: - get: - operationId: listenCountForUser - parameters: - - in: path - name: user_name - required: true - schema: - type: string - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/listenCountForUser" - description: "Yay, you have listen counts!" - "404": - description: The requested user was not found. - summary: Get the number of listens for a user 'user_name'. - tags: - - lbCore - /1/user/{user_name}/playing-now: - get: - operationId: playingNowForUser - parameters: - - in: path - name: user_name - required: true - schema: - type: string - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/playingNowForUser" - description: "Yay, you have data!" - "404": - description: The requested user was not found. - summary: Get the listen being played right now for user 'user_name' - tags: - - lbCore - /1/user/{user_name}/similar-users: - get: - operationId: similarUsersForUser - parameters: - - in: path - name: user_name - required: true - schema: - type: string - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/similarUsersForUser" - description: "Yay, you have data!" - "404": - description: The requested user was not found. - summary: Get the listen being played right now for user 'user_name'. - tags: - - lbCore - /1/user/{user_name}/similar-to/{other_user_name}: - get: - operationId: similarityOfUserForUser - parameters: - - in: path - name: user_name - required: true - schema: - type: string - - in: path - name: other_user_name - required: true - schema: - type: string - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/similarityOfUserForUser" - description: "Yay, you have data!" - "404": - description: The requested user was not found. - summary: "Get the similarity of 'user_name' and 'other_user_name', based on\ - \ their listening history." - tags: - - lbCore - /1/delete-listen: - post: - operationId: deleteListen - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/deleteListen" - required: true - responses: - "200": - description: Listen deleted. - "400": - description: Invalid JSON sent. - "401": - description: Invalid authorization. - security: - - ApiKeyAuth: [] - summary: Delete a particular listen from a user's listen history. - tags: - - lbCore - /1/latest-import: - get: - operationId: latestImport - parameters: - - description: The MusicBrainz ID of the user whose data is needed. - in: query - name: user_name - required: true - schema: - type: string - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/latestImport" - description: "Yay, you have data!" - security: - - ApiKeyAuth: [] - summary: Get the timestamp of the newest listen submitted by a user in previous - imports to ListenBrainz. - tags: - - lbCore - /1/user/{user_name}/services: - get: - operationId: servicesForUser - parameters: - - in: path - name: user_name - required: true - schema: - type: string - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/servicesForUser" - description: "Yay, you have data!" - "401": - description: Invalid authorization. - "403": - description: "Forbidden, you do not have permissions to view this user's\ - \ information." - "404": - description: The requested user was not found. - security: - - ApiKeyAuth: [] - summary: Get list of services which are connected to a given user's account. - tags: - - lbCore - /1/user/{playlist_user_name}/playlists/recommendations: - get: - operationId: recommendationPlaylistsForUser - parameters: - - in: path - name: playlist_user_name - required: true - schema: - type: string - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/playlists" - description: Success. - "404": - description: User not found. - summary: Fetch recommendation playlist metadata in JSPF format without recordings - for 'playlist_user_name'. This endpoint only lists playlists that are to be - shown on the listenbrainz.org recommendations pages. - tags: - - lbCore - /1/user/{playlist_user_name}/playlists/search: - get: - operationId: searchPlaylistForUser - parameters: - - in: path - name: playlist_user_name - required: true - schema: - type: string - - in: query - name: query - required: true - schema: - type: string - - description: The number of playlists to return (for pagination). - in: query - name: count - required: false - schema: - default: 25 - type: integer - - description: The offset of into the list of playlists to return (for pagination). - in: query - name: offset - required: false - schema: - type: integer - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/playlists" - description: "Yay, you have data!" - "404": - description: The requested user was not found. - summary: Search for a playlist by name for a user. - tags: - - lbCore - /1/lb-radio/artist/{seed_artist_mbid}: - get: - operationId: lbRadioRecordingsForArtist - parameters: - - in: path - name: seed_artist_mbid - required: true - schema: - format: uuid - type: string - - description: "The mode that LB radio should use. Must be easy, medium or hard." - in: query - name: mode - required: true - schema: - $ref: "#/components/schemas/Mode" - - description: The maximum number of similar artists to return recordings for. - in: query - name: max_similar_artists - required: true - schema: - type: integer - - description: "The maximum number of recordings to return for each artist.\ - \ If there are aren’t enough recordings, all available recordings will be\ - \ returned." - in: query - name: max_recordings_per_artist - required: true - schema: - type: integer - - description: "Popularity range percentage lower bound. A popularity range\ - \ is given to narrow down the recordings into a smaller target group. The\ - \ most popular recording(s) on LB have a pop percent of 100. The least popular\ - \ recordings have a score of 0. This range is not coupled to the specified\ - \ mode, but the mode would often determine the popularity range, so that\ - \ less popular recordings can be returned on the medium and harder modes." - in: query - name: pop_begin - required: true - schema: - type: integer - - description: Popularity range percentage upper bound. - in: query - name: pop_end - required: true - schema: - type: integer - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/lbRadioRecordingsForArtist" - description: "Yay, you have data!" - "400": - description: Invalid or missing param in request. - summary: "Get recordings for use in LB radio with the given seed artist. The\ - \ endpoint returns a dict of all the similar artists, including the seed artist.\ - \ For each artists, there will be a list of dicts that contain recording_mbid,\ - \ similar_artist_mbid and total_listen_count." - tags: - - lbCore - /1/lb-radio/tags: - get: - operationId: lbRadioTags - parameters: - - description: "The MusicBrainz tag to fetch recordings for, this parameter\ - \ can be specified multiple times. if more than one tag is specified, the\ - \ operator param should also be specified." - in: query - name: tag - required: true - schema: - type: string - - description: "Specify AND to retrieve recordings that have all the tags, otherwise\ - \ specify OR to retrieve recordings that have any one of the tags." - in: query - name: operator - required: false - schema: - $ref: "#/components/schemas/Operator" - - description: "Popularity range percentage lower bound. A popularity range\ - \ is given to narrow down the recordings into a smaller target group. The\ - \ most popular recording(s) on LB have a pop percent of 100. The least popular\ - \ recordings have a score of 0. This range is not coupled to the specified\ - \ mode, but the mode would often determine the popularity range, so that\ - \ less popular recordings can be returned on the medium and harder modes." - in: query - name: pop_begin - required: true - schema: - type: integer - - description: Popularity range percentage upper bound. - in: query - name: pop_end - required: true - schema: - type: integer - - description: "Optional, number of listens to return." - in: query - name: count - required: true - schema: - default: 25 - type: integer - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/lbRadioTags" - description: "Yay, you have data!" - "400": - description: Invalid or missing param in request. - summary: Get recordings for use in LB radio with the specified tags that match - the requested criteria. - tags: - - lbCore - /1/stats/user/{user_name}/artists: - get: - operationId: topArtistsForUser - parameters: - - in: path - name: user_name - required: true - schema: - type: string - - in: query - name: count - required: false - schema: - default: 25 - description: "Optional, number of artists to return" - type: integer - - in: query - name: offset - required: false - schema: - default: 0 - description: "Optional, number of artists to skip from the beginning, for\ - \ pagination" - type: integer - - in: query - name: range - required: false - schema: - $ref: "#/components/schemas/AllowedStatisticsRange" - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/topArtistsForUser" - description: successful operation - "204": - description: "Statistics for the user haven't been calculated, empty response\ - \ will be returned." - "400": - description: Bad request. - "404": - description: User not found. - summary: Get top artists for user 'user_name'. - tags: - - lbStats - /1/stats/user/{user_name}/releases: - get: - operationId: topReleasesForUser - parameters: - - in: path - name: user_name - required: true - schema: - type: string - - in: query - name: count - required: false - schema: - default: 25 - description: "Optional, number of artists to return" - type: integer - - in: query - name: offset - required: false - schema: - default: 0 - description: "Optional, number of artists to skip from the beginning, for\ - \ pagination" - type: integer - - in: query - name: range - required: false - schema: - $ref: "#/components/schemas/AllowedStatisticsRange" - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/topReleasesForUser" - description: "Successful query, you have data!" - "204": - description: "Statistics for the user haven't been calculated, empty response\ - \ will be returned." - "400": - description: Bad request. - "404": - description: User not found. - summary: Get top releases for user 'user_name'. - tags: - - lbStats - /1/stats/user/{user_name}/release-groups: - get: - operationId: topReleaseGroupsForUser - parameters: - - in: path - name: user_name - required: true - schema: - type: string - - in: query - name: count - required: false - schema: - default: 25 - description: "Optional, number of artists to return" - type: integer - - in: query - name: offset - required: false - schema: - default: 0 - description: "Optional, number of artists to skip from the beginning, for\ - \ pagination" - type: integer - - in: query - name: range - required: false - schema: - $ref: "#/components/schemas/AllowedStatisticsRange" - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/topReleaseGroupsForUser" - description: successful operation - "204": - description: "Statistics for the user haven't been calculated, empty response\ - \ will be returned." - "400": - description: Bad request. - "404": - description: User not found - summary: Get top release groups for user 'user_name'. - tags: - - lbStats - /1/stats/user/{user_name}/recordings: - get: - operationId: topRecordingsForUser - parameters: - - in: path - name: user_name - required: true - schema: - type: string - - in: query - name: count - required: false - schema: - default: 25 - description: "Optional, number of artists to return" - type: integer - - in: query - name: offset - required: false - schema: - default: 0 - description: "Optional, number of artists to skip from the beginning, for\ - \ pagination" - type: integer - - in: query - name: range - required: false - schema: - $ref: "#/components/schemas/AllowedStatisticsRange" - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/topRecordingsForUser" - description: successful operation - "204": - description: "Statistics for the user haven't been calculated, empty response\ - \ will be returned." - "400": - description: Bad request. - "404": - description: User not found. - summary: Get top recordings for user 'user_name'. - tags: - - lbStats - /1/stats/user/{user_name}/listening-activity: - get: - operationId: listeningActivityForUser - parameters: - - in: path - name: user_name - required: true - schema: - type: string - - in: query - name: range - required: false - schema: - $ref: "#/components/schemas/AllowedStatisticsRange" - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/listeningActivityForUser" - description: successful operation - "204": - description: "Statistics for the user haven't been calculated, empty response\ - \ will be returned." - "400": - description: Bad request. - "404": - description: User not found. - summary: Get the listening activity for user 'user_name'. - tags: - - lbStats - /1/stats/user/{user_name}/daily-activity: - get: - operationId: dailyActivityForUser - parameters: - - in: path - name: user_name - required: true - schema: - type: string - - in: query - name: range - required: false - schema: - $ref: "#/components/schemas/AllowedStatisticsRange" - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/dailyActivityForUser" - description: successful operation - "204": - description: "Statistics for the user haven't been calculated, empty response\ - \ will be returned." - "400": - description: Bad request. - "404": - description: User not found. - summary: Get the daily activity for user 'user_name'. - tags: - - lbStats - /1/stats/user/{user_name}/artist-map: - get: - operationId: artistMapForUser - parameters: - - in: path - name: user_name - required: true - schema: - type: string - - in: query - name: range - required: false - schema: - $ref: "#/components/schemas/AllowedStatisticsRange" - - description: "Optional, recalculate the data instead of returning the cached\ - \ result." - in: query - name: force_recalculate - required: false - schema: - default: false - type: boolean - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/artistMapForUser" - description: successful operation - "204": - description: "Statistics for the user haven't been calculated, empty response\ - \ will be returned." - "400": - description: Bad request. - "404": - description: User not found. - summary: Get the artist map for user 'user_name'. - tags: - - lbStats - /1/stats/artist/{artist_mbid}/listeners: - get: - operationId: listenersForArtist - parameters: - - in: path - name: artist_mbid - required: true - schema: - format: uuid - type: string - - in: query - name: range - required: false - schema: - $ref: "#/components/schemas/AllowedStatisticsRange" - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/listenersForArtist" - description: successful operation - "204": - description: "Statistics for the artist haven't been calculated, empty response\ - \ will be returned." - "400": - description: Bad request. - "404": - description: Entity not found. - summary: Get top listeners for artist 'artist_mbid'. - tags: - - lbStats - /1/stats/release-group/{release_group_mbid}/listeners: - get: - operationId: listenersForReleaseGroup - parameters: - - in: path - name: release_group_mbid - required: true - schema: - format: uuid - type: string - - in: query - name: range - required: false - schema: - $ref: "#/components/schemas/AllowedStatisticsRange" - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/listenersForReleaseGroup" - description: successful operation - "204": - description: "Statistics for the artist haven't been calculated, empty response\ - \ will be returned." - "400": - description: Bad request. - "404": - description: Entity not found. - summary: Get top listeners for release group 'release_group_mbid'. - tags: - - lbStats - /1/stats/sitewide/artists: - get: - operationId: sitewideTopArtists - parameters: - - in: query - name: count - required: false - schema: - default: 25 - description: "Optional, number of artists to return." - type: integer - - in: query - name: offset - required: false - schema: - default: 0 - description: "Optional, number of artists to skip from the beginning, for\ - \ pagination." - type: integer - - in: query - name: range - required: false - schema: - $ref: "#/components/schemas/AllowedStatisticsRange" - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/sitewideTopArtists" - description: successful operation - "204": - description: "Statistics for the artist haven't been calculated, empty response\ - \ will be returned." - "400": - description: Bad request. - "404": - description: Entity not found. - summary: Get sitewide top artists. - tags: - - lbStats - /1/stats/sitewide/releases: - get: - operationId: sitewideTopReleases - parameters: - - in: query - name: count - required: false - schema: - default: 25 - description: "Optional, number of artists to return." - type: integer - - in: query - name: offset - required: false - schema: - default: 0 - description: "Optional, number of artists to skip from the beginning, for\ - \ pagination." - type: integer - - in: query - name: range - required: false - schema: - $ref: "#/components/schemas/AllowedStatisticsRange" - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/sitewideTopReleases" - description: successful operation - "204": - description: "Statistics for the artist haven't been calculated, empty response\ - \ will be returned." - "400": - description: Bad request. - "404": - description: Entity not found. - summary: Get sitewide top releases. - tags: - - lbStats - /1/stats/sitewide/release-groups: - get: - operationId: sitewideTopReleaseGroups - parameters: - - in: query - name: count - required: false - schema: - default: 25 - description: "Optional, number of artists to return." - type: integer - - in: query - name: offset - required: false - schema: - default: 0 - description: "Optional, number of artists to skip from the beginning, for\ - \ pagination." - type: integer - - in: query - name: range - required: false - schema: - $ref: "#/components/schemas/AllowedStatisticsRange" - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/sitewideTopReleaseGroups" - description: successful operation - "204": - description: "Statistics haven't been calculated, empty response will be\ - \ returned." - "400": - description: Bad request. - summary: Get sitewide top release groups. - tags: - - lbStats - /1/stats/sitewide/recordings: - get: - operationId: sitewideTopRecordings - parameters: - - in: query - name: count - required: false - schema: - default: 25 - description: "Optional, number of artists to return." - type: integer - - in: query - name: offset - required: false - schema: - default: 0 - description: "Optional, number of artists to skip from the beginning, for\ - \ pagination." - type: integer - - in: query - name: range - required: false - schema: - $ref: "#/components/schemas/AllowedStatisticsRange" - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/sitewideTopRecordings" - description: successful operation - "204": - description: "Statistics haven't been calculated, empty response will be\ - \ returned." - "400": - description: Bad request. - summary: Get sitewide top recordings. - tags: - - lbStats - /1/stats/sitewide/listening-activity: - get: - operationId: sitewideListeningActivity - parameters: - - in: query - name: range - required: false - schema: - $ref: "#/components/schemas/AllowedStatisticsRange" - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/sitewideListeningActivity" - description: successful operation - "204": - description: "Statistics haven't been calculated, empty response will be\ - \ returned." - "400": - description: Bad request. - summary: Get sitewide top recordings. - tags: - - lbStats - /1/stats/sitewide/artist-map: - get: - operationId: sitewideArtistMap - parameters: - - in: query - name: range - required: false - schema: - $ref: "#/components/schemas/AllowedStatisticsRange" - - description: "Optional, recalculate the data instead of returning the cached\ - \ result." - in: query - name: force_recalculate - required: false - schema: - default: false - type: boolean - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/sitewideArtistMap" - description: successful operation - "204": - description: "Statistics haven't been calculated, empty response will be\ - \ returned." - "400": - description: Bad request. - summary: Get sitewide top recordings. - tags: - - lbStats - /1/stats/user/{user_name}/year-in-music/{year}: - get: - operationId: yearInMusicForUser - parameters: - - in: path - name: user_name - required: true - schema: - type: string - - in: path - name: year - required: true - schema: - type: integer - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/yearInMusicForUser" - description: "Successful query, you have data!" - "204": - description: "Statistics haven't been calculated, empty response will be\ - \ returned." - "400": - description: Bad request. - summary: Get sitewide top recordings. - tags: - - lbStats - /1/user/{playlist_user_name}/playlists: - get: - operationId: playlistsForUser - parameters: - - in: path - name: playlist_user_name - required: true - schema: - type: string - - in: query - name: count - required: false - schema: - default: 25 - description: The number of playlists to return (for pagination). - type: integer - - in: query - name: offset - required: false - schema: - description: The offset of into the list of playlists to return (for pagination). - type: integer - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/playlists" - description: "Yay, you have data!" - "404": - description: The requested user was not found. - summary: Fetch playlist metadata in JSPF format without recordings for the given - user. - tags: - - lbPlaylists - /1/user/{playlist_user_name}/playlists/collaborator: - get: - operationId: playlistsForUserCollaborator - parameters: - - in: path - name: playlist_user_name - required: true - schema: - type: string - - in: query - name: count - required: false - schema: - default: 25 - description: The number of playlists to return (for pagination). - type: integer - - in: query - name: offset - required: false - schema: - description: The offset of into the list of playlists to return (for pagination). - type: integer - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/playlists" - description: "Yay, you have data!" - "404": - description: User not found. - summary: "Fetch playlist metadata in JSPF format without recordings for which\ - \ a user is a collaborator. If a playlist is private, it will only be returned\ - \ if the caller is authorized to edit that playlist." - tags: - - lbPlaylists - /1/user/{playlist_user_name}/playlists/createdfor: - get: - operationId: playlistsCreatedForUser - parameters: - - in: path - name: playlist_user_name - required: true - schema: - type: string - - in: query - name: count - required: false - schema: - default: 25 - description: The number of playlists to return (for pagination). - type: integer - - in: query - name: offset - required: false - schema: - description: The offset of into the list of playlists to return (for pagination). - type: integer - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/playlists" - description: "Yay, you have data!" - "404": - description: The requested user was not found. - summary: Fetch playlist metadata in JSPF format without recordings that have - been created for the user. - tags: - - lbPlaylists - /1/playlist/create: - post: - description: |- - Create a playlist. The playlist must be in JSPF format with MusicBrainz extensions, which is defined here: https://musicbrainz.org/doc/jspf . To create an empty playlist, you can send an empty playlist with only the title field filled out. If you would like to create a playlist populated with recordings, each of the track items in the playlist must have an identifier element that contains the MusicBrainz recording that includes the recording MBID. - - When creating a playlist, only the playlist title and the track identifier elements will be used - all other elements in the posted JSPF wil be ignored. - - If a created_for field is found and the user is not an approved playlist bot, then a 403 forbidden will be raised. - operationId: createPlaylist - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/createPlaylist_request" - required: true - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/createPlaylist_200_response" - description: Playlist accepted. - "400": - description: Invalid JSON sent. - "401": - description: Invalid authorization. - "403": - description: The submitting user is not allowed to create playlists for - other users. - security: - - ApiKeyAuth: [] - summary: Create a playlist - tags: - - lbPlaylists - /1/playlist/search: - get: - operationId: searchPlaylists - parameters: - - in: query - name: query - required: true - schema: - type: string - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/playlists" - description: "Yay, you have data!" - "400": - description: "Invalid query string, see error message for details." - "401": - description: Invalid authorization. See error message for details. - summary: Search for playlists by name or description. The search query must - be at least 3 characters long. - tags: - - lbPlaylists - /1/playlist/edit/{playlist_mbid}: - post: - operationId: editPlaylist - parameters: - - description: The playlist mbid to edit. - in: path - name: playlist_mbid - required: true - schema: - format: uuid - type: string - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/createPlaylist_request" - required: true - responses: - "200": - description: Playlist accepted. - "400": - description: Invalid JSON sent. - "401": - description: Invalid authorization. - "403": - description: The submitting user is not allowed to create playlists for - other users. - security: - - ApiKeyAuth: [] - summary: |- - Create a playlist. The playlist must be in JSPF format with MusicBrainz extensions, which is defined here: https://musicbrainz.org/doc/jspf . To create an empty playlist, you can send an empty playlist with only the title field filled out. If you would like to create a playlist populated with recordings, each of the track items in the playlist must have an identifier element that contains the MusicBrainz recording that includes the recording MBID. - - When creating a playlist, only the playlist title and the track identifier elements will be used - all other elements in the posted JSPF wil be ignored. - - If a created_for field is found and the user is not an approved playlist bot, then a 403 forbidden will be raised. - tags: - - lbPlaylists - /1/playlist/{playlist_mbid}: - get: - operationId: fetchPlaylist - parameters: - - description: The playlist mbid to fetch. - in: path - name: playlist_mbid - required: true - schema: - format: uuid - type: string - - description: "Optional, pass value ‘false' to skip lookup up recording metadata." - in: query - name: fetch_metadata - required: false - schema: - type: boolean - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/createPlaylist_request" - description: "Yay, you have data!" - "401": - description: Invalid authorization. See error message for details. - "404": - description: Playlist not found. - summary: Fetch the given playlist. - tags: - - lbPlaylists - /1/playlist/{playlist_mbid}/item/add/{offset}: - post: - operationId: appendRecordings - parameters: - - description: The playlist mbid to append to. - in: path - name: playlist_mbid - required: true - schema: - format: uuid - type: string - - description: Offset. - in: path - name: offset - required: true - schema: - type: integer - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/playlist" - required: true - responses: - "200": - description: Playlist accepted. - "400": - description: "Invalid JSON sent, see error message for details." - "401": - description: Invalid authorization. See error message for details. - "403": - description: Forbidden. The requesting user was not allowed to carry out - this operation. - security: - - ApiKeyAuth: [] - summary: |- - Append recordings to an existing playlist by posting a playlist with one of more recordings in it. The playlist must be in JSPF format with MusicBrainz extensions, which is defined here: https://musicbrainz.org/doc/jspf . - - If the offset is provided in the URL, then the recordings will be added at that offset, otherwise they will be added at the end of the playlist. - tags: - - lbPlaylists - /1/playlist/{playlist_mbid}/item/move: - post: - operationId: moveItem - parameters: - - description: The playlist mbid to append to. - in: path - name: playlist_mbid - required: true - schema: - format: uuid - type: string - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/moveItem_request" - required: true - responses: - "200": - description: Move operation succeeded. - "400": - description: "Invalid JSON sent, see error message for details." - "401": - description: Invalid authorization. See error message for details. - "403": - description: Forbidden. The requesting user was not allowed to carry out - this operation. - security: - - ApiKeyAuth: [] - summary: "To move an item in a playlist, the POST data needs to specify the\ - \ recording MBID and current index of the track to move (from), where to move\ - \ it to (to) and how many tracks from that position should be moved (count)." - tags: - - lbPlaylists - /1/playlist/{playlist_mbid}/item/delete: - post: - description: "To delete an item in a playlist, the POST data needs to specify\ - \ the recording MBID and current index of the track to delete, and how many\ - \ tracks from that position should be moved deleted." - operationId: itemDelete - parameters: - - description: The playlist mbid to fetch. - in: path - name: playlist_mbid - required: true - schema: - format: uuid - type: string - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/itemDelete_request" - required: true - responses: - "200": - description: Playlist accepted. - "400": - description: Invalid JSON sent. - "401": - description: Invalid authorization. - "403": - description: The requesting user was not allowed to carry out this operation. - security: - - ApiKeyAuth: [] - summary: Delete an item in a playlist. - tags: - - lbPlaylists - /1/playlist/{playlist_mbid}/delete: - post: - description: POST body data does not need to contain anything. - operationId: deletePlaylist - parameters: - - description: The playlist mbid to fetch. - in: path - name: playlist_mbid - required: true - schema: - format: uuid - type: string - responses: - "200": - description: Playlist deleted. - "401": - description: Invalid authorization. - "403": - description: The requesting user was not allowed to carry out this operation. - "404": - description: Playlist not found. - security: - - ApiKeyAuth: [] - summary: Delete a playlist. - tags: - - lbPlaylists - /1/playlist/{playlist_mbid}/copy: - post: - operationId: copyPlaylist - parameters: - - description: The playlist mbid to append to. - in: path - name: playlist_mbid - required: true - schema: - format: uuid - type: string - responses: - "200": - description: Playlist copied. - "401": - description: Invalid authorization. See error message for details. - "403": - description: Forbidden. The requesting user was not allowed to carry out - this operation. - "404": - description: Playlist not found. - security: - - ApiKeyAuth: [] - summary: Copy a playlist - the new playlist will be given the name “Copy of - ”. POST body data does not need to contain anything. - tags: - - lbPlaylists - /1/feedback/recording-feedback: - post: - operationId: recordingFeedback - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/recordingFeedback_request" - required: true - responses: - "200": - description: Feedback accepted. - "400": - description: Invalid JSON sent. - "401": - description: Invalid authorization. - security: - - ApiKeyAuth: [] - summary: Submit recording feedback (love/hate) to the server. A user token (found - on https://listenbrainz.org/settings/ ) must be provided in the Authorization - header! Each request should contain only one feedback in the payload. - tags: - - lbRecordings - /1/feedback/user/{user_name}/get-feedback: - get: - operationId: getFeedback - parameters: - - in: path - name: user_name - required: true - schema: - type: string - - in: query - name: score - required: false - schema: - description: "Optional, If 1 then returns the loved recordings, if -1 returns\ - \ hated recordings." - type: integer - - in: query - name: count - required: false - schema: - default: 25 - description: "Optional, number of feedback items to return." - maximum: 1000 - type: integer - - in: query - name: offset - required: false - schema: - default: 0 - description: "Optional, number of feedback items to skip from the beginning,\ - \ for pagination. Ex. An offset of 5 means the top 5 feedback will be\ - \ skipped, defaults to 0." - type: integer - - in: query - name: metadata - required: false - schema: - description: "Optional, 'true' or 'false' if this call should return the\ - \ metadata for the feedback." - type: string - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/feedbackResponse" - description: "Yay, you have data!" - summary: |- - Get feedback given by user 'user_name'. The format for the JSON returned is defined in our Feedback JSON Documentation. - - If the optional argument score is not given, this endpoint will return all the feedback submitted by the user. Otherwise filters the feedback to be returned by score. - tags: - - lbRecordings - /1/feedback/recording/{recording_mbid}/get-feedback-mbid: - get: - operationId: getFeedbackMbid - parameters: - - in: path - name: recording_mbid - required: true - schema: - format: uuid - type: string - - in: query - name: score - required: false - schema: - description: "Optional, If 1 then returns the loved recordings, if -1 returns\ - \ hated recordings." - type: integer - - in: query - name: count - required: false - schema: - default: 25 - description: "Optional, number of feedback items to return." - maximum: 1000 - type: integer - - in: query - name: offset - required: false - schema: - default: 0 - description: "Optional, number of feedback items to skip from the beginning,\ - \ for pagination. Ex. An offset of 5 means the top 5 feedback will be\ - \ skipped, defaults to 0." - type: integer - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/feedbackResponse" - description: "Yay, you have data!" - summary: Get feedback for recording with given recording_mbid. - tags: - - lbRecordings - /1/feedback/user/{recording_msid}/get-feedback: - get: - operationId: getFeedbackMsid - parameters: - - in: path - name: recording_msid - required: true - schema: - type: string - - in: query - name: score - required: false - schema: - description: "Optional, If 1 then returns the loved recordings, if -1 returns\ - \ hated recordings." - type: integer - - in: query - name: count - required: false - schema: - default: 25 - description: "Optional, number of feedback items to return." - maximum: 1000 - type: integer - - in: query - name: offset - required: false - schema: - default: 0 - description: "Optional, number of feedback items to skip from the beginning,\ - \ for pagination. Ex. An offset of 5 means the top 5 feedback will be\ - \ skipped, defaults to 0." - type: integer - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/feedbackResponse" - description: "Yay, you have data!" - summary: Get feedback for recording with given recording_msid. - tags: - - lbRecordings - /1/feedback/user/{user_name}/get-feedback-for-recordings: - get: - operationId: getFeedbackForRecordings - parameters: - - in: path - name: user_name - required: true - schema: - type: string - - description: Comma separated list of recording_msids for which feedback records - are to be fetched. - in: query - name: recording_msids - required: false - schema: - items: - format: uuid - type: string - type: array - - description: Comma separated list of recording_mbids for which feedback records - are to be fetched. - in: query - name: recording_mbids - required: false - schema: - items: - format: uuid - type: string - type: array - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/feedbackResponse" - description: "Yay, you have data!" - summary: Get feedback given by user user_name for the list of recordings supplied. - tags: - - lbRecordings - /1/pin: - post: - operationId: pin - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/pin_request" - required: true - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/pin_200_response" - description: Feedback accepted. - "400": - description: Invalid JSON sent. - "401": - description: Invalid authorization. - security: - - ApiKeyAuth: [] - summary: Pin a recording for user. A user token (found on https://listenbrainz.org/settings/) - must be provided in the Authorization header! Each request should contain - only one pinned recording item in the payload. - tags: - - lbRecordings - /1/unpin: - post: - operationId: unpin - responses: - "200": - description: Recording unpinned. - "401": - description: Invalid authorization. - "404": - description: Could not find the active recording to unpin for the user. - security: - - ApiKeyAuth: [] - summary: Unpins the currently active pinned recording for the user. A user token - (found on https://listenbrainz.org/settings/) must be provided in the Authorization - header! - tags: - - lbRecordings - /1/pin/delete/{row_id}: - post: - operationId: pinDelete - parameters: - - in: path - name: row_id - required: true - schema: - type: integer - responses: - "200": - description: Recording unpinned. - "401": - description: Invalid authorization. - "404": - description: The requested row_id for the user was not found. - security: - - ApiKeyAuth: [] - summary: Deletes the pinned recording with given row_id from the server. A user - token (found on https://listenbrainz.org/settings/) must be provided in the - Authorization header! - tags: - - lbRecordings - /1/{user_name}/pins: - get: - operationId: getPins - parameters: - - in: path - name: user_name - required: true - schema: - type: string - - in: query - name: count - required: false - schema: - default: 25 - description: "Optional, number of feedback items to return." - maximum: 1000 - type: integer - - in: query - name: offset - required: false - schema: - default: 0 - description: "Optional, number of pinned recording items to skip from the\ - \ beginning, for pagination. Ex. An offset of 5 means the top 5 feedback\ - \ will be skipped, defaults to 0." - type: integer - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/getPins" - description: "Yay, you have data!" - "400": - description: "Invalid query parameters, see error message for details." - "404": - description: The requested user was not found. - summary: Get a list of all recordings ever pinned by a user with given user_name - in descending order of the time they were originally pinned. - tags: - - lbRecordings - /1/{user_name}/pins/following: - get: - operationId: getPinsFollowing - parameters: - - in: path - name: user_name - required: true - schema: - type: string - - in: query - name: count - required: false - schema: - default: 25 - description: "Optional, number of feedback items to return." - maximum: 1000 - type: integer - - in: query - name: offset - required: false - schema: - default: 0 - description: "Optional, number of pinned recording items to skip from the\ - \ beginning, for pagination. Ex. An offset of 5 means the top 5 feedback\ - \ will be skipped, defaults to 0." - type: integer - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/getPins" - description: "Yay, you have data!" - "400": - description: "Invalid query parameters, see error message for details." - "404": - description: The requested user was not found. - summary: Get a list containing the active pinned recordings for all users in - a user's user_name following list. The returned pinned recordings are sorted - in descending order of the time they were pinned. - tags: - - lbRecordings - /1/{user_name}/pins/current: - get: - operationId: getPinsCurrent - parameters: - - in: path - name: user_name - required: true - schema: - type: string - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/getPinsCurrent_200_response" - description: "Yay, you have data!" - "400": - description: "Invalid query parameters, see error message for details." - "404": - description: The requested user was not found. - summary: Get the currently pinned recording by a user with given user_name. - tags: - - lbRecordings - /1/pin/update/{row_id}: - post: - operationId: updatePin - parameters: - - in: path - name: row_id - required: true - schema: - type: integer - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/updatePin_request" - required: true - responses: - "200": - description: Feedback accepted. - "400": - description: Invalid JSON sent. - "401": - description: Invalid authorization. - security: - - ApiKeyAuth: [] - summary: Updates the blurb content of a pinned recording for the user. A user - token (found on https://listenbrainz.org/settings/) must be provided in the - Authorization header! Each request should contain only one pinned recording - item in the payload. - tags: - - lbRecordings - /1/popularity/top-recordings-for-artist/{artist_mbid}: - get: - operationId: topRecordingsForArtist - parameters: - - in: path - name: artist_mbid - required: true - schema: - format: uuid - type: string - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/topRecordingsForArtist" - description: "Yay, you have data!" - "400": - description: "Invalid query parameters, see error message for details." - "404": - description: The requested user was not found. - summary: Get a list of all recordings ever pinned by a user with given user_name - in descending order of the time they were originally pinned. - tags: - - lbPopularity - /1/popularity/top-release-groups-for-artist/{artist_mbid}: - get: - operationId: topReleaseGroupForArtist - parameters: - - in: path - name: artist_mbid - required: true - schema: - format: uuid - type: string - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/topReleaseGroupsForArtist" - description: "Yay, you have data!" - "400": - description: "Invalid query parameters, see error message for details." - "404": - description: The requested user was not found. - summary: Get the top release groups by listen count for a given artist. - tags: - - lbPopularity - /1/popularity/recording: - post: - operationId: recording - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/recording_request" - required: true - responses: - "200": - content: - application/json: - schema: - items: - $ref: "#/components/schemas/recording_200_response_inner" - type: array - description: Feedback accepted. - "400": - description: Invalid JSON sent. - "401": - description: Invalid authorization. - summary: |- - Get the total listen count and total unique listeners count for a given recording. - - A JSON document with a list of recording_mbids and inc string must be POSTed. Up to MAX_ITEMS_PER_GET items can be requested at once. - tags: - - lbPopularity - /1/popularity/artist: - post: - operationId: artist - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/artist_request" - required: true - responses: - "200": - content: - application/json: - schema: - items: - $ref: "#/components/schemas/artist_200_response_inner" - type: array - description: Feedback accepted. - "400": - description: Invalid JSON sent. - "401": - description: Invalid authorization. - summary: |- - Get the total listen count and total unique listeners count for a given artist. - - A JSON document with a list of artists and inc string must be POSTed. Up to MAX_ITEMS_PER_GET items can be requested at once. - tags: - - lbPopularity - /1/popularity/release: - post: - operationId: release - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/release_request" - required: true - responses: - "200": - content: - application/json: - schema: - items: - $ref: "#/components/schemas/release_200_response_inner" - type: array - description: Feedback accepted. - "400": - description: Invalid JSON sent. - "401": - description: Invalid authorization. - summary: |- - Get the total listen count and total unique listeners count for a given release. - - A JSON document with a list of releases and inc string must be POSTed. Up to MAX_ITEMS_PER_GET items can be requested at once. - tags: - - lbPopularity - /1/popularity/release-group: - post: - operationId: releaseGroup - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/releaseGroup_request" - required: true - responses: - "200": - content: - application/json: - schema: - items: - $ref: "#/components/schemas/releaseGroup_200_response_inner" - type: array - description: Feedback accepted. - "400": - description: Invalid JSON sent. - "401": - description: Invalid authorization. - summary: |- - Get the total listen count and total unique listeners count for a given release group. - - A JSON document with a list of release groups and inc string must be POSTed. Up to MAX_ITEMS_PER_GET items can be requested at once. - tags: - - lbPopularity - /1/metadata/recording/: - get: - operationId: recordingMetadata - parameters: - - description: A comma separated list of recording_mbids. - in: query - name: recording_mbids - required: true - schema: - items: - format: uuid - type: string - type: array - - description: "A space separated list of “artist”, “tag” and/or “release” to\ - \ indicate which portions of metadata you're interested in fetching. We\ - \ encourage users to only fetch the data they plan to consume." - in: query - name: inc - required: true - schema: - type: string - responses: - "200": - content: - application/json: - schema: - additionalProperties: - $ref: "#/components/schemas/recordingMetadata" - type: object - description: "Yay, you have data!" - "400": - description: "Invalid query parameters, see error message for details." - "404": - description: The requested user was not found. - summary: "This endpoint takes in a list of recording_mbids and returns an array\ - \ of dicts that contain recording metadata suitable for showing in a context\ - \ that requires as much detail about a recording and the artist. Using the\ - \ inc parameter, you can control which portions of metadata to fetch." - tags: - - lbMetadata - /1/metadata/release_group/: - get: - operationId: releaseGroupMetadata - parameters: - - description: A comma separated list of release_group_mbids. - explode: false - in: query - name: release_group_mbids - required: true - schema: - items: - format: uuid - type: string - type: array - style: form - - description: "A space separated list of “artist”, “tag” and/or “release” to\ - \ indicate which portions of metadata you're interested in fetching. We\ - \ encourage users to only fetch the data they plan to consume." - in: query - name: inc - required: true - schema: - type: string - responses: - "200": - content: - application/json: - schema: - additionalProperties: - $ref: "#/components/schemas/releaseGroupMetadata" - type: object - description: "Yay, you have data!" - "400": - description: "Invalid query parameters, see error message for details." - summary: "This endpoint takes in a list of release_group_mbids and returns an\ - \ array of dicts that contain release_group metadata suitable for showing\ - \ in a context that requires as much detail about a release_group and the\ - \ artist. Using the inc parameter, you can control which portions of metadata\ - \ to fetch." - tags: - - lbMetadata - /1/metadata/lookup/: - get: - operationId: lookup - parameters: - - description: Artist name of the listen. - in: query - name: artist_name - required: true - schema: - type: string - - description: Track name of the listen. - in: query - name: recording_name - required: true - schema: - type: string - - description: Release name of the listen. - in: query - name: release_name - required: false - schema: - type: string - - description: "Should extra metadata be also returned if a match is found,\ - \ see /metadata/recording for details." - in: query - name: metadata - required: true - schema: - type: boolean - - description: "A space separated list of “artist”, “tag” and/or “release” to\ - \ indicate which portions of metadata you're interested in fetching. We\ - \ encourage users to only fetch the data they plan to consume." - in: query - name: inc - required: true - schema: - type: string - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/lookup" - description: "Yay, you have data!" - "400": - description: Invalid arguments. - summary: "This endpoint looks up mbid metadata for the given artist, recording\ - \ and optionally a release name. The total number of characters in the artist\ - \ name, recording name and release name query arguments should be less than\ - \ or equal to MAX_MAPPING_QUERY_LENGTH." - tags: - - lbMetadata - /1/metadata/submit_manual_mapping/: - post: - operationId: submitManualMapping - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/submitManualMapping" - required: true - responses: - "200": - description: Listen(s) accepted. - "400": - description: Invalid JSON sent. - "401": - description: Invalid authorization. - security: - - ApiKeyAuth: [] - summary: Submit a manual mapping of a recording messybrainz ID to a musicbrainz - recording id. - tags: - - lbMetadata - /1/metadata/get_manual_mapping/: - get: - operationId: getManualMapping - parameters: - - in: query - name: recording_msid - required: true - schema: - format: uuid - type: string - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/getManualMapping" - description: "Yay, you have data!" - "404": - description: No such mapping for this user/recording msid. - security: - - ApiKeyAuth: [] - summary: Get the manual mapping of a recording messybrainz ID that a user added. - tags: - - lbMetadata - /1/metadata/artist/: - get: - operationId: artistMetadata - parameters: - - description: A comma separated list of recording_mbids. - in: query - name: artist_mbids - required: true - schema: - items: - format: uuid - type: string - type: array - - description: "A space separated list of “artist”, “tag” and/or “release” to\ - \ indicate which portions of metadata you're interested in fetching. We\ - \ encourage users to only fetch the data they plan to consume." - in: query - name: inc - required: true - schema: - type: string - responses: - "200": - content: - application/json: - schema: - items: - $ref: "#/components/schemas/artistMetadata" - type: array - description: "Yay, you have data!" - "400": - description: Invalid arguments. - summary: "This endpoint takes in a list of artist_mbids and returns an array\ - \ of dicts that contain recording metadata suitable for showing in a context\ - \ that requires as much detail about a recording and the artist. Using the\ - \ inc parameter, you can control which portions of metadata to fetch." - tags: - - lbMetadata - /1/user/{user_name}/timeline-event/create/recording: - post: - operationId: recommendRecording - parameters: - - in: path - name: user_name - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/recommendRecording_request" - required: true - responses: - "200": - description: "Successful query, recording has been recommended!" - "400": - description: Bad request. - "401": - description: "Unauthorized, you do not have permissions to recommend recordings\ - \ on the behalf of this user." - "403": - description: "Forbidden, you are not an approved user." - "404": - description: User not found. - security: - - ApiKeyAuth: [] - summary: Make the user recommend a recording to their followers. - tags: - - lbSocial - /1/user/{user_name}/timeline-event/create/notification: - post: - operationId: createNotification - parameters: - - in: path - name: user_name - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/createNotification_request" - required: true - responses: - "200": - description: "Successful query, message has been posted!" - "400": - description: Bad request. - "401": - description: "Unauthorized, you do not have permissions to recommend recordings\ - \ on the behalf of this user." - "403": - description: "Forbidden, you are not an approved user." - "404": - description: User not found. - security: - - ApiKeyAuth: [] - summary: Post a message with a link on a user's timeline. Only approved users - are allowed to perform this action. - tags: - - lbSocial - /1/user/{user_name}/timeline-event/create/review: - post: - operationId: createReview - parameters: - - in: path - name: user_name - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/createReview_request" - required: true - responses: - "200": - description: "Successful query, message has been posted!" - "400": - description: Bad request. - "403": - description: "Forbidden, you have not linked with a CritiqueBrainz account." - "404": - description: User not found. - security: - - ApiKeyAuth: [] - summary: Creates a CritiqueBrainz review event for the user. This also creates - a corresponding review in CritiqueBrainz. Users need to have linked their - ListenBrainz account with CritiqueBrainz first to use this endpoint successfully. - tags: - - lbSocial - /1/user/{user_name}/feed/events: - get: - operationId: feedEvents - parameters: - - in: path - name: user_name - required: true - schema: - description: The MusicBrainz ID of the user whose timeline is being requested. - type: string - - description: "If you specify a 'max_ts' timestamp, events with timestamps\ - \ less than the value will be returned." - in: query - name: max_ts - schema: - type: integer - - description: "If you specify a 'min_ts' timestamp, events with timestamps\ - \ greater than the value will be returned." - in: query - name: min_ts - schema: - type: integer - - in: query - name: count - required: false - schema: - default: 25 - description: "Optional, number of listens to return." - type: integer - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/feedEvents" - description: "Successful query, you have feed events!" - "400": - description: Bad request. - "401": - description: "Unauthorized, you do not have permission to view this user's\ - \ feed." - "403": - description: "Forbidden, you do not have permission to view this user's\ - \ feed." - "404": - description: User not found. - security: - - ApiKeyAuth: [] - summary: CreatGet feed events for a user's timeline. - tags: - - lbSocial - /1/user/{user_name}/feed/events/listens/following: - get: - operationId: feedEventsListensFollowing - parameters: - - in: path - name: user_name - required: true - schema: - description: The MusicBrainz ID of the user whose timeline is being requested. - type: string - - description: "If you specify a 'max_ts' timestamp, events with timestamps\ - \ less than the value will be returned." - in: query - name: max_ts - schema: - type: integer - - description: "If you specify a 'min_ts' timestamp, events with timestamps\ - \ greater than the value will be returned." - in: query - name: min_ts - schema: - type: integer - - in: query - name: count - required: false - schema: - default: 25 - description: "Optional, number of listens to return." - type: integer - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/feedEvents" - description: "Successful query, you have feed listen-events!" - "400": - description: Bad request. - "401": - description: Invalid authorization. See error message for details. - "403": - description: "Forbidden, you do not have permission to view this user's\ - \ feed." - "404": - description: User not found. - security: - - ApiKeyAuth: [] - summary: Get feed's listen events for followed users. - tags: - - lbSocial - /1/user/{user_name}/feed/events/listens/similar: - get: - operationId: feedEventsListensSimilar - parameters: - - in: path - name: user_name - required: true - schema: - description: The MusicBrainz ID of the user whose timeline is being requested. - type: string - - description: "If you specify a 'max_ts' timestamp, events with timestamps\ - \ less than the value will be returned." - in: query - name: max_ts - schema: - type: integer - - description: "If you specify a 'min_ts' timestamp, events with timestamps\ - \ greater than the value will be returned." - in: query - name: min_ts - schema: - type: integer - - in: query - name: count - required: false - schema: - default: 25 - description: "Optional, number of listens to return." - type: integer - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/feedEventsListensSimilar" - description: "Successful query, you have feed listen-events!" - "400": - description: Bad request. - "401": - description: Invalid authorization. See error message for details. - "403": - description: "Forbidden, you do not have permission to view this user's\ - \ feed." - "404": - description: User not found. - security: - - ApiKeyAuth: [] - summary: Get feed's listen events for similar users. - tags: - - lbSocial - /1/user/{user_name}/feed/events/delete: - post: - operationId: feedEventsDelete - parameters: - - in: path - name: user_name - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/feedEventsDelete_request" - required: true - responses: - "200": - description: Successful deletion. - "400": - description: Bad request. - "401": - description: Unauthorized. - "403": - description: "Forbidden, you do not have permission to delete from this\ - \ user's feed." - "404": - description: User not found. - "500": - description: API Internal Server Error. - security: - - ApiKeyAuth: [] - summary: "Delete those events from user's feed that belong to them. Supports\ - \ deletion of recommendation and notification. Along with the authorization\ - \ token, post the event type and event id." - tags: - - lbSocial - /1/user/{user_name}/feed/events/hide: - post: - operationId: feedEventsHide - parameters: - - in: path - name: user_name - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/feedEventsDelete_request" - required: true - responses: - "200": - description: Event hidden successfully. - "400": - description: Bad request. - "401": - description: Unauthorized. - "403": - description: "Forbidden, you don't have permissions to hide events from\ - \ this user's timeline." - "404": - description: User not found. - "500": - description: API Internal Server Error. - security: - - ApiKeyAuth: [] - summary: "Hide events from the user feed, only recording_recommendation and\ - \ recording_pin events that have been generated by the people one is following\ - \ can be deleted via this endpoint." - tags: - - lbSocial - /1/user/{user_name}/feed/events/unhide: - post: - operationId: feedEventsUnhide - parameters: - - in: path - name: user_name - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/feedEventsDelete_request" - required: true - responses: - "200": - description: Event unhidden successfully. - "400": - description: Bad request. - "401": - description: Unauthorized. - "403": - description: Forbidden. - "404": - description: User not found. - "500": - description: API Internal Server Error. - security: - - ApiKeyAuth: [] - summary: "Delete hidden events from the user feed, aka unhide events." - tags: - - lbSocial - /1/user/{user_name}/timeline-event/create/recommend-personal: - post: - operationId: recommendPersonalRecording - parameters: - - in: path - name: user_name - required: true - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/recommendPersonalRecording_request" - required: true - responses: - "200": - description: "Successful query, recording has been recommended!" - "400": - description: Bad request. - "401": - description: "Unauthorized, you do not have permissions to recommend." - security: - - ApiKeyAuth: [] - summary: "Make the user recommend a recording to their followers. The request\ - \ should post the following data about the recording being recommended (either\ - \ one of recording_msid or recording_mbid is sufficient), and also the list\ - \ of followers getting recommended." - tags: - - lbSocial - /1/user/{user_name}/followers: - get: - operationId: followers - parameters: - - in: path - name: user_name - required: true - schema: - description: The MusicBrainz ID of the user whose timeline is being requested. - type: string - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/followers" - description: "Yay, you have data!" - "400": - description: Bad request. - "404": - description: User not found. - security: - - ApiKeyAuth: [] - summary: Fetch the list of followers of the user 'user_name'. - tags: - - lbSocial - /1/user/{user_name}/following: - get: - operationId: following - parameters: - - in: path - name: user_name - required: true - schema: - description: The MusicBrainz ID of the user whose timeline is being requested. - type: string - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/following" - description: "Yay, you have data!" - "400": - description: Bad request. - "404": - description: User not found. - security: - - ApiKeyAuth: [] - summary: Fetch the list of users followed by the user 'user_name'. - tags: - - lbSocial - /1/user/{user_name}/follow: - post: - operationId: follow - parameters: - - in: path - name: user_name - required: true - schema: - type: string - responses: - "200": - description: Successfully followed the user 'user_name'. - "400": - description: "Already following the user user_name, or trying to follow\ - \ yourself." - "401": - description: Invalid authorization. See error message for details. - security: - - ApiKeyAuth: [] - summary: Follow the user 'user_name'. A user token (found on https://listenbrainz.org/settings/ - ) must be provided in the Authorization header! - tags: - - lbSocial - /1/user/{user_name}/unfollow: - post: - operationId: unfollow - parameters: - - in: path - name: user_name - required: true - schema: - type: string - responses: - "200": - description: Successfully unfollowed the user 'user_name'. - "401": - description: Invalid authorization. See error message for details. - security: - - ApiKeyAuth: [] - summary: Unfollow the user 'user_name'. A user token (found on https://listenbrainz.org/settings/ - ) must be provided in the Authorization header! - tags: - - lbSocial - /1/cf/recommendation/user/{user_name}/recording: - get: - operationId: recordingRecommendations - parameters: - - description: The MusicBrainz ID of the user whose timeline is being requested. - in: path - name: user_name - required: true - schema: - type: string - - description: "Optional, number of recording mbids to return." - in: query - name: count - schema: - default: 25 - type: integer - - description: "Optional, number of mbids to skip from the beginning, for pagination.\ - \ Ex. An offset of 5 means the 5 mbids will be skipped." - in: query - name: offset - schema: - default: 25 - type: integer - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/recordingRecommendations" - description: "Successful query, you have data!" - "204": - description: "Recommendations for the user haven't been generated, empty\ - \ response will be returned." - "400": - description: Bad request. - "404": - description: User not found. - summary: Get recommendations sorted on rating and ratings for user 'user_name'. - tags: - - lbRecommendations - /1/recommendation/feedback/submit: - post: - operationId: submitFeedback - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/submitFeedback_request" - required: true - responses: - "200": - description: Feedback accepted. - "400": - description: Invalid JSON sent. - "401": - description: Invalid authorization. - security: - - ApiKeyAuth: [] - summary: Submit recommendation feedback. A user token (found on https://listenbrainz.org/settings/ - ) must be provided in the Authorization header! Each request should contain - only one feedback in the payload. - tags: - - lbRecommendations - /1/recommendations/feedback/delete: - post: - operationId: deleteFeedback - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/deleteFeedback_request" - required: true - responses: - "200": - description: Feedback accepted. - "400": - description: Invalid JSON sent. - "401": - description: Invalid authorization. - security: - - ApiKeyAuth: [] - summary: Delete feedback for a user. A user token (found on https://listenbrainz.org/settings/ - ) must be provided in the Authorization header! Each request should contain - only one recording mbid in the payload. - tags: - - lbRecommendations - /1/recommendations/feedback/user/{user_name}: - get: - operationId: feedbackGivenBy - parameters: - - description: The MusicBrainz ID of the user whose timeline is being requested. - in: path - name: user_name - required: true - schema: - type: string - - description: "Optional, refer to db/model/recommendation_feedback.py for allowed\ - \ rating values." - in: query - name: rating - required: false - schema: - type: string - - description: "Optional, number of recording mbids to return." - in: query - name: count - required: false - schema: - default: 25 - type: integer - - description: "Optional, number of mbids to skip from the beginning, for pagination.\ - \ Ex. An offset of 5 means the 5 mbids will be skipped." - in: query - name: offset - required: false - schema: - default: 25 - type: integer - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/feedbackGivenBy" - description: "Yay, you have data!" - "204": - description: "Recommendations for the user haven't been generated, empty\ - \ response will be returned." - "400": - description: Bad request. - "404": - description: User not found. - summary: Get feedback given by user 'user_name'. - tags: - - lbRecommendations - /1/recommendations/feedback/user/{user_name}/recordings: - get: - operationId: recordingsFeedbackGivenBy - parameters: - - description: The MusicBrainz ID of the user whose timeline is being requested. - in: path - name: user_name - required: true - schema: - type: string - - description: Comma separated list of recording_mbids for which feedback records - are to be fetched. - in: query - name: mbids - required: true - schema: - items: - format: uuid - type: string - type: array - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/recordingsFeedbackGivenBy" - description: "Yay, you have data!" - "400": - description: Bad request. - "404": - description: User not found. - summary: Get feedback given by user 'user_name' for the list of recordings supplied. - tags: - - lbRecommendations - /1/explore/fresh-releases/: - get: - operationId: freshReleases - parameters: - - description: Fresh releases will be shown around this pivot date. Must be - in YYYY-MM-DD format. - in: query - name: release_date - required: false - schema: - type: string - - description: The number of days of fresh releases to show. Max 90 days. - in: query - name: days - required: false - schema: - type: integer - - description: The sort order of the results. - in: query - name: sort - required: false - schema: - default: release_name - enum: - - release_date - - artist_credit_name - - release_name - type: string - - description: Whether to show releases in the past. - in: query - name: past - required: false - schema: - default: true - type: boolean - - description: Whether to show releases in the future. - in: query - name: future - required: false - schema: - default: true - type: boolean - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/freshReleases" - description: "Yay, you have data!" - "400": - description: Invalid date or number of days passed. - summary: This endpoint fetches upcoming and recently released (fresh) releases. - tags: - - lbMisc - /1/explore/color/{color}: - get: - operationId: color - parameters: - - description: Color must be a 6 digit hex color code. - in: path - name: color - required: true - schema: - type: string - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/color" - description: Success. - summary: Fetch a list of releases that have cover art that has a predominant - color that is close to the given color. - tags: - - lbMisc - /1/explore/lb-radio: - get: - operationId: lbRadio - parameters: - - description: The LB Radio prompt from which to generate playlists. - in: query - name: prompt - required: true - schema: - type: string - - description: "The mode that LB radio should use. Must be easy, medium or hard." - in: query - name: mode - required: true - schema: - $ref: "#/components/schemas/Mode" - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/lbRadio" - description: Success. - summary: Generate a playlist with LB Radio. - tags: - - lbMisc - /1/status/get-dump-info: - get: - operationId: getDumpInfo - parameters: - - description: "Integer specifying the ID of the dump, if not provided, the\ - \ endpoint returns information about the latest data dump." - in: query - name: id - required: false - schema: - type: integer - responses: - "200": - content: - application/json: - schema: - $ref: "#/components/schemas/getDumpInfo" - description: Success. - "400": - description: You did not provide a valid dump ID. See error message for - details. - "404": - description: Dump with given ID does not exist. - summary: Get information about ListenBrainz data dumps. You need to pass the - id parameter in a GET request to get data about that particular dump. - tags: - - lbMisc - /1/art/grid/: - post: - operationId: createCoverArtGrid - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/createCoverArtGrid" - required: true - responses: - "200": - content: - image/svg+xml: - schema: - format: binary - type: string - description: Cover art created successfully. - "400": - description: Invalid JSON or invalid options in JSON passed. - summary: Create a cover art grid SVG file from the POSTed JSON data to this - endpoint. - tags: - - lbArt - /1/art/grid-stats/{user_name}/{time_range}/{dimension}/{layout}/{image_size}: - get: - operationId: createCoverArtGridForUser - parameters: - - description: The name of the user for whom to create the cover art. - in: path - name: user_name - required: true - schema: - type: string - - description: Must be a statistics time range. - in: path - name: time_range - required: true - schema: - $ref: "#/components/schemas/AllowedStatisticsRange" - - description: "The dimension to use for this grid. A grid of dimension 3 has\ - \ 3 images across and 3 images down, for a total of 9 images." - in: path - name: dimension - required: true - schema: - type: integer - - description: "The layout to be used for this grid. Layout 0 is always a simple\ - \ grid, but other layouts may have image images be of different sizes. See\ - \ https://art.listenbrainz.org for examples of the available layouts." - in: path - name: layout - required: true - schema: - type: integer - - description: The size of the cover art image. - in: path - name: image_size - required: true - schema: - type: integer - responses: - "200": - content: - image/svg+xml: - schema: - format: binary - type: string - description: Cover art created successfully. - "400": - description: Invalid JSON or invalid options in JSON passed. - summary: Create a cover art grid SVG file from the stats of a given user. - tags: - - lbArt - /1/art/{custom_name}/{user_name}/{time_range}/{image_size}: - get: - operationId: createCustomCoverArt - parameters: - - description: The name of cover art to be generated. - in: path - name: custom_name - required: true - schema: - $ref: "#/components/schemas/CoverTypes" - - description: The name of the user for whom to create the cover art. - in: path - name: user_name - required: true - schema: - type: string - - description: Must be a statistics time range. - in: path - name: time_range - required: true - schema: - $ref: "#/components/schemas/AllowedStatisticsRange" - - description: The size of the cover art image. - in: path - name: image_size - required: true - schema: - type: integer - responses: - "200": - content: - image/svg+xml: - schema: - format: binary - type: string - description: Cover art created successfully. - "400": - description: Invalid JSON or invalid options in JSON passed. - summary: Create a custom cover art SVG file from the stats of a given user. - tags: - - lbArt - /1/art/year-in-music/{year}/{user_name}: - get: - operationId: yearInMusic - parameters: - - in: path - name: year - required: true - schema: - type: integer - - description: The name of the user for whom to create the cover art. - in: path - name: user_name - required: true - schema: - type: string - - in: query - name: image - required: true - schema: - $ref: "#/components/schemas/YearInMusicImage" - responses: - "200": - content: - image/svg+xml: - schema: - format: binary - type: string - description: Cover art created successfully. - "400": - description: Invalid JSON or invalid options in JSON passed. - summary: Create the shareable svg image using YIM stats. - tags: - - lbArt -components: - schemas: - YearInMusicImage: - enum: - - overview - - stats - - artists - - albums - - tracks - - discovery-playlist - - missed-playlist - type: string - ListenType: - enum: - - single - - playing_now - - import - type: string - Mode: - description: mode is the LB radio mode to be used for this query - enum: - - easy - - medium - - hard - type: string - AllowedStatisticsRange: - enum: - - all_time - - month - - week - - year - - quarter - - half_yearly - - this_week - - this_month - - this_year - type: string - Operator: - enum: - - AND - - OR - type: string - AllowedRatings: - enum: - - like - - love - - dislike - - hate - - bad_recommendation - type: string - CoverTypes: - enum: - - designer-top-5 - - designer-top-10 - - lps-on-the-floor - - grid-stats - - grid-stats-special - type: string - searchUsers: - example: - users: - - user_name: user_name - - user_name: user_name - properties: - users: - items: - $ref: "#/components/schemas/searchUsers_users_inner" - type: array - required: - - users - type: object - submitListens: - properties: - listen_type: - $ref: "#/components/schemas/ListenType" - payload: - items: - $ref: "#/components/schemas/submitListens_payload_inner" - type: array - required: - - listen_type - - payload - type: object - validateToken: - example: - valid: true - code: 0 - user_name: user_name - message: message - properties: - code: - type: integer - message: - type: string - valid: - type: boolean - user_name: - type: string - required: - - code - - message - - valid - type: object - listensForUser: - example: - payload: - listens: - - recording_msid: recording_msid - listened_at: 5 - user_name: user_name - inserted_at: 1 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - - recording_msid: recording_msid - listened_at: 5 - user_name: user_name - inserted_at: 1 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - user_id: user_id - latest_listen_ts: 6 - count: 0 - oldest_listen_ts: 2 - properties: - payload: - $ref: "#/components/schemas/listensForUser_payload" - required: - - payload - type: object - listenCountForUser: - example: - payload: - count: 0 - properties: - payload: - $ref: "#/components/schemas/listenCountForUser_payload" - required: - - payload - type: object - playingNowForUser: - example: - payload: - listens: - - playing_now: true - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - - playing_now: true - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - playing_now: true - user_id: user_id - count: 0 - properties: - payload: - $ref: "#/components/schemas/playingNowForUser_payload" - required: - - payload - type: object - similarUsersForUser: - example: - payload: - - similarity: 0 - user_name: user_name - - similarity: 0 - user_name: user_name - properties: - payload: - items: - $ref: "#/components/schemas/similarUsersForUser_payload_inner" - type: array - required: - - payload - type: object - similarityOfUserForUser: - example: - payload: - similarity: 0 - user_name: user_name - properties: - payload: - $ref: "#/components/schemas/similarityOfUserForUser_payload" - required: - - payload - type: object - deleteListen: - properties: - listened_at: - type: integer - recording_msid: - type: string - required: - - listened_at - - recording_msid - type: object - latestImport: - example: - latest_import: latest_import - musicbrainz_id: musicbrainz_id - status: - count: 0 - state: state - properties: - musicbrainz_id: - description: the MusicBrainz ID of the user - type: string - latest_import: - description: the timestamp of the newest listen submitted in previous imports. - Defaults to 0 - type: string - status: - $ref: "#/components/schemas/latestImport_status" - required: - - latest_import - - musicbrainz_id - type: object - servicesForUser: - example: - user_name: user_name - services: - - services - - services - properties: - services: - items: - type: string - type: array - user_name: - type: string - required: - - services - - user_name - type: object - playlists: - example: - playlist_count: 1 - offset: 6 - count: 0 - playlists: - - playlist: - annotation: annotation - date: date - duration: 5 - identifier: identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#playlist: - creator: creator - created_for: created_for - public: true - collaborators: - - collaborators - - collaborators - copied_from: copied_from - copied_from_deleted: true - last_modified_at: last_modified_at - additional_metadata: - algorithm_metadata: - source_patch: source_patch - album: album - title: title - track: - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - - playlist: - annotation: annotation - date: date - duration: 5 - identifier: identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#playlist: - creator: creator - created_for: created_for - public: true - collaborators: - - collaborators - - collaborators - copied_from: copied_from - copied_from_deleted: true - last_modified_at: last_modified_at - additional_metadata: - algorithm_metadata: - source_patch: source_patch - album: album - title: title - track: - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - properties: - count: - type: integer - offset: - type: integer - playlist_count: - type: integer - playlists: - items: - $ref: "#/components/schemas/createPlaylist_request" - type: array - type: object - lbRadioRecordingsForArtist: - additionalProperties: - items: - $ref: "#/components/schemas/lbRadioRecordingsForArtist_value_inner" - type: array - type: object - lbRadioTags: - items: - $ref: "#/components/schemas/lbRadioTags_inner" - type: array - topArtistsForUser: - example: - payload: - last_updated: 5 - offset: 5 - artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - to_ts: 2 - user_id: user_id - from_ts: 1 - count: 6 - range: range - total_artist_count: 7 - properties: - payload: - $ref: "#/components/schemas/topArtistsForUser_payload" - required: - - payload - type: object - topReleasesForUser: - example: - payload: - last_updated: 1 - offset: 5 - to_ts: 7 - user_id: user_id - from_ts: 6 - count: 0 - total_release_count: 9 - range: range - releases: - - listen_count: 2 - caa_id: 5 - release_name: release_name - artist_name: artist_name - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 2 - caa_id: 5 - release_name: release_name - artist_name: artist_name - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - payload: - $ref: "#/components/schemas/topReleasesForUser_payload" - required: - - payload - type: object - topReleaseGroupsForUser: - example: - payload: - total_release_group_count: 3 - last_updated: 1 - offset: 5 - to_ts: 9 - user_id: user_id - from_ts: 6 - count: 0 - range: range - release_groups: - - listen_count: 7 - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_name: release_group_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 7 - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_name: release_group_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - payload: - $ref: "#/components/schemas/topReleaseGroupsForUser_payload" - required: - - payload - type: object - topRecordingsForUser: - example: - payload: - last_updated: 1 - offset: 5 - recordings: - - listen_count: 2 - caa_id: 5 - release_name: release_name - artist_name: artist_name - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - track_name: track_name - - listen_count: 2 - caa_id: 5 - release_name: release_name - artist_name: artist_name - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - track_name: track_name - to_ts: 7 - user_id: user_id - from_ts: 6 - count: 0 - range: range - total_recording_count: 9 - properties: - payload: - $ref: "#/components/schemas/topRecordingsForUser_payload" - required: - - payload - type: object - listeningActivityForUser: - example: - payload: - last_updated: 6 - listening_activity: - - listen_count: 5 - time_range: time_range - to_ts: 5 - from_ts: 1 - - listen_count: 5 - time_range: time_range - to_ts: 5 - from_ts: 1 - to_ts: 2 - user_id: user_id - from_ts: 0 - range: range - properties: - payload: - $ref: "#/components/schemas/listeningActivityForUser_payload" - required: - - payload - type: object - dailyActivityForUser: - example: - payload: - last_updated: 5 - to_ts: 5 - user_id: user_id - from_ts: 1 - range: range - daily_activity: - Monday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - Thursday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - Friday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - Sunday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - Wednesday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - Tuesday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - Saturday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - properties: - payload: - $ref: "#/components/schemas/dailyActivityForUser_payload" - required: - - payload - type: object - artistMapForUser: - example: - payload: - last_updated: 5 - artist_map: - - listen_count: 6 - country: country - artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_count: 0 - - listen_count: 6 - country: country - artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_count: 0 - to_ts: 5 - user_id: user_id - from_ts: 1 - range: range - properties: - payload: - $ref: "#/components/schemas/artistMapForUser_payload" - required: - - payload - type: object - listenersForArtist: - example: - payload: - last_updated: 6 - listeners: - - listen_count: 1 - user_name: user_name - - listen_count: 1 - user_name: user_name - artist_name: artist_name - to_ts: 5 - from_ts: 0 - total_listen_count: 5 - total_user_count: 2 - stats_range: stats_range - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - payload: - $ref: "#/components/schemas/listenersForArtist_payload" - required: - - payload - type: object - listenersForReleaseGroup: - example: - payload: - last_updated: 1 - caa_id: 0 - listeners: - - listen_count: 1 - user_name: user_name - - listen_count: 1 - user_name: user_name - artist_name: artist_name - from_ts: 6 - total_listen_count: 5 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - total_user_count: 2 - stats_range: stats_range - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - to_ts: 5 - release_group_name: release_group_name - properties: - payload: - $ref: "#/components/schemas/listenersForReleaseGroup_payload" - required: - - payload - type: object - sitewideTopArtists: - example: - payload: - last_updated: 1 - offset: 5 - artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - to_ts: 5 - from_ts: 6 - count: 0 - range: range - total_artist_count: 2 - properties: - payload: - $ref: "#/components/schemas/sitewideTopArtists_payload" - required: - - payload - type: object - sitewideTopReleases: - example: - payload: - last_updated: 1 - offset: 5 - to_ts: 7 - from_ts: 6 - count: 0 - total_release_count: 9 - range: range - releases: - - listen_count: 2 - caa_id: 5 - release_name: release_name - artists: artists - artist_name: artist_name - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 2 - caa_id: 5 - release_name: release_name - artists: artists - artist_name: artist_name - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - payload: - $ref: "#/components/schemas/sitewideTopReleases_payload" - required: - - payload - type: object - sitewideTopReleaseGroups: - example: - payload: - total_release_group_count: 2 - last_updated: 1 - offset: 5 - to_ts: 5 - from_ts: 6 - count: 0 - range: range - release_groups: - - listen_count: 7 - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_name: release_group_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 7 - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_name: release_group_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - payload: - $ref: "#/components/schemas/sitewideTopReleaseGroups_payload" - required: - - payload - type: object - sitewideTopRecordings: - example: - payload: - last_updated: 1 - offset: 5 - recordings: - - listen_count: 2 - caa_id: 5 - release_name: release_name - artists: - - artists - - artists - artist_name: artist_name - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - track_name: track_name - - listen_count: 2 - caa_id: 5 - release_name: release_name - artists: - - artists - - artists - artist_name: artist_name - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - track_name: track_name - to_ts: 7 - from_ts: 6 - count: 0 - range: range - total_recording_count: 9 - properties: - payload: - $ref: "#/components/schemas/sitewideTopRecordings_payload" - required: - - payload - type: object - sitewideListeningActivity: - example: - payload: - last_updated: 6 - listening_activity: - - listen_count: 5 - time_range: time_range - to_ts: 5 - from_ts: 1 - - listen_count: 5 - time_range: time_range - to_ts: 5 - from_ts: 1 - to_ts: 1 - from_ts: 0 - range: range - properties: - payload: - $ref: "#/components/schemas/sitewideListeningActivity_payload" - required: - - payload - type: object - sitewideArtistMap: - example: - payload: - last_updated: 6 - artist_map: - - listen_count: 6 - country: country - artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_count: 0 - - listen_count: 6 - country: country - artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_count: 0 - to_ts: 1 - from_ts: 0 - stats_range: stats_range - properties: - payload: - $ref: "#/components/schemas/sitewideArtistMap_payload" - required: - - payload - type: object - yearInMusicForUser: - example: - payload: - data: - most_listened_year: - key: 0 - top_release_groups: - - listen_count: 7 - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_name: release_group_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 7 - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_name: release_group_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - total_new_artists_discovered: 3 - similar_users: - key: 1 - listens_per_day: - - listen_count: 5 - time_range: time_range - to_ts: 5 - from_ts: 1 - - listen_count: 5 - time_range: time_range - to_ts: 5 - from_ts: 1 - total_listen_count: 7 - total_artists_count: 2 - total_recordings_count: 2 - new_releases_of_top_artists: - - artist_credit_name: artist_credit_name - caa_id: 6 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - title: title - artist_credit_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - caa_id: 6 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - title: title - artist_credit_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - top_artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - top_recordings: - - listen_count: 2 - caa_id: 5 - release_name: release_name - artist_name: artist_name - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - track_name: track_name - - listen_count: 2 - caa_id: 5 - release_name: release_name - artist_name: artist_name - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - track_name: track_name - total_listening_time: 9 - playlist-top-missed-recordings-for-year: - annotation: annotation - date: date - duration: 5 - identifier: identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#playlist: - creator: creator - created_for: created_for - public: true - collaborators: - - collaborators - - collaborators - copied_from: copied_from - copied_from_deleted: true - last_modified_at: last_modified_at - additional_metadata: - algorithm_metadata: - source_patch: source_patch - album: album - title: title - track: - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - artist_map: - - listen_count: 6 - country: country - artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_count: 0 - - listen_count: 6 - country: country - artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_count: 0 - top_genres: - - genre_count_percent: 5 - genre_count: 5 - genre: genre - - genre_count_percent: 5 - genre_count: 5 - genre: genre - total_release_groups_count: 4 - playlist-top-discoveries-for-year: - annotation: annotation - date: date - duration: 5 - identifier: identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#playlist: - creator: creator - created_for: created_for - public: true - collaborators: - - collaborators - - collaborators - copied_from: copied_from - copied_from_deleted: true - last_modified_at: last_modified_at - additional_metadata: - algorithm_metadata: - source_patch: source_patch - album: album - title: title - track: - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - day_of_week: day_of_week - user_name: user_name - properties: - payload: - $ref: "#/components/schemas/yearInMusicForUser_payload" - required: - - payload - type: object - playlist: - example: - annotation: annotation - date: date - duration: 5 - identifier: identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#playlist: - creator: creator - created_for: created_for - public: true - collaborators: - - collaborators - - collaborators - copied_from: copied_from - copied_from_deleted: true - last_modified_at: last_modified_at - additional_metadata: - algorithm_metadata: - source_patch: source_patch - album: album - title: title - track: - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - properties: - album: - type: string - annotation: - type: string - creator: - type: string - date: - type: string - duration: - type: integer - extension: - $ref: "#/components/schemas/playlist-extension" - identifier: - type: string - title: - type: string - track: - items: - $ref: "#/components/schemas/playlist_track_inner" - type: array - type: object - feedbackResponse: - example: - feedback: - - score: 1 - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - user_id: user_id - created: 6 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - - score: 1 - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - user_id: user_id - created: 6 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - offset: 5 - total_count: 5 - count: 0 - properties: - count: - type: integer - feedback: - items: - $ref: "#/components/schemas/feedback" - type: array - offset: - type: integer - total_count: - type: integer - type: object - trackMetadata: - example: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - properties: - additional_info: - $ref: "#/components/schemas/additionalInfo" - artist_name: - type: string - brainzplayer_metadata: - $ref: "#/components/schemas/trackMetadata_brainzplayer_metadata" - mbid_mapping: - $ref: "#/components/schemas/mbidMapping" - release_name: - type: string - track_name: - type: string - type: object - getPins: - example: - offset: 6 - total_count: 2 - user_name: user_name - count: 0 - pinned_recordings: - - recording_msid: recording_msid - pinned_until: 5 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - created: 1 - blurb_content: blurb_content - row_id: 5 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - - recording_msid: recording_msid - pinned_until: 5 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - created: 1 - blurb_content: blurb_content - row_id: 5 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - properties: - count: - type: integer - offset: - type: integer - pinned_recordings: - items: - $ref: "#/components/schemas/getPins_pinned_recordings_inner" - type: array - total_count: - type: integer - user_name: - type: string - required: - - count - - offset - - pinned_recordings - - user_name - type: object - topRecordingsForArtist: - items: - $ref: "#/components/schemas/topRecordingsForArtist_inner" - type: array - topReleaseGroupsForArtist: - items: - $ref: "#/components/schemas/topReleaseGroupsForArtist_inner" - type: array - recordingMetadata: - properties: - artist: - $ref: "#/components/schemas/recordingMetadata_artist" - recording: - $ref: "#/components/schemas/recordingMetadata_recording" - tag: - $ref: "#/components/schemas/recordingMetadata_tag" - type: object - releaseGroupMetadata: - properties: - release_group: - $ref: "#/components/schemas/releaseGroupMetadata_release_group" - tag: - $ref: "#/components/schemas/topReleaseGroupsForArtist_inner_tag" - artist: - $ref: "#/components/schemas/releaseGroupMetadata_artist" - release: - $ref: "#/components/schemas/releaseGroupMetadata_release_group" - type: object - lookup: - example: - artist_credit_name: artist_credit_name - metadata: - artist: - artist_credit_id: 0 - artists: - - area: area - join_phrase: join_phrase - name: name - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - begin_year: 6 - rels: - youtube: youtube - download for free: download for free - free streaming: free streaming - purchase for download: purchase for download - blog: blog - social network: social network - streaming: streaming - official homepage: official homepage - crowdfunding: crowdfunding - purchase for mail-order: purchase for mail-order - patronage: patronage - lyrics: lyrics - wikidata: wikidata - - area: area - join_phrase: join_phrase - name: name - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - begin_year: 6 - rels: - youtube: youtube - download for free: download for free - free streaming: free streaming - purchase for download: purchase for download - blog: blog - social network: social network - streaming: streaming - official homepage: official homepage - crowdfunding: crowdfunding - purchase for mail-order: purchase for mail-order - patronage: patronage - lyrics: lyrics - wikidata: wikidata - name: name - release: - caa_id: 5 - mbid: mbid - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - year: 5 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - name: name - album_artist_name: album_artist_name - recording: - isrcs: - - isrcs - - isrcs - first_release_date: 2000-01-23 - length: 1 - name: name - items: items - rels: - - artist_name: artist_name - instrument: instrument - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_name: artist_name - instrument: instrument - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tag: - artist: - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 2 - tag: tag - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 2 - tag: tag - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording: - - count: 2 - tag: tag - - count: 2 - tag: tag - release_group: - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 9 - tag: tag - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 9 - tag: tag - recording_name: recording_name - release_name: release_name - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - artist_credit_name: - type: string - artist_mbids: - items: - format: uuid - type: string - type: array - metadata: - $ref: "#/components/schemas/lookup_metadata" - recording_mbid: - format: uuid - type: string - recording_name: - type: string - release_mbid: - format: uuid - type: string - release_name: - type: string - type: object - submitManualMapping: - properties: - recording_mbid: - format: uuid - type: string - recording_msid: - format: uuid - type: string - required: - - a - - b - type: object - getManualMapping: - example: - mapping: - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - user_id: 0 - created: created - status: status - properties: - mapping: - $ref: "#/components/schemas/getManualMapping_mapping" - status: - type: string - required: - - mapping - - status - type: object - artistMetadata: - example: - area: area - mbid: mbid - gender: gender - name: name - tag: - artist: - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 2 - tag: tag - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 2 - tag: tag - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - begin_year: 0 - rels: - youtube: youtube - download for free: download for free - free streaming: free streaming - purchase for download: purchase for download - blog: blog - social network: social network - streaming: streaming - official homepage: official homepage - crowdfunding: crowdfunding - purchase for mail-order: purchase for mail-order - patronage: patronage - lyrics: lyrics - wikidata: wikidata - properties: - area: - type: string - artist_mbid: - format: uuid - type: string - begin_year: - type: integer - gender: - type: string - mbid: - type: string - name: - type: string - rels: - $ref: "#/components/schemas/rels_1" - tag: - $ref: "#/components/schemas/artistMetadata_tag" - type: - type: string - type: object - feedEvents: - example: - payload: - user_id: user_id - count: 0 - events: - - metadata: - relationship_type: relationship_type - playing_now: true - listened_at: 2 - listened_at_iso: listened_at_iso - created: 5 - user_name: user_name - blurb_content: blurb_content - message: message - inserted_at: 5 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - event_type: event_type - hidden: true - created: 6 - user_name: user_name - similarity: 7.061401241503109 - id: 1 - message: message - - metadata: - relationship_type: relationship_type - playing_now: true - listened_at: 2 - listened_at_iso: listened_at_iso - created: 5 - user_name: user_name - blurb_content: blurb_content - message: message - inserted_at: 5 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - event_type: event_type - hidden: true - created: 6 - user_name: user_name - similarity: 7.061401241503109 - id: 1 - message: message - properties: - payload: - $ref: "#/components/schemas/feedEvents_payload" - type: object - feedEventsListensSimilar: - example: - payload: - user_id: user_id - count: 0 - events: - - metadata: - relationship_type: relationship_type - playing_now: true - listened_at: 2 - listened_at_iso: listened_at_iso - created: 5 - user_name: user_name - blurb_content: blurb_content - message: message - inserted_at: 5 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - event_type: event_type - hidden: true - created: 6 - similarity: 1 - user_name: user_name - id: id - - metadata: - relationship_type: relationship_type - playing_now: true - listened_at: 2 - listened_at_iso: listened_at_iso - created: 5 - user_name: user_name - blurb_content: blurb_content - message: message - inserted_at: 5 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - event_type: event_type - hidden: true - created: 6 - similarity: 1 - user_name: user_name - id: id - properties: - payload: - $ref: "#/components/schemas/feedEventsListensSimilar_payload" - required: - - payload - type: object - followers: - example: - followers: - - followers - - followers - user: user - properties: - followers: - items: - type: string - type: array - user: - type: string - required: - - followers - - user - type: object - following: - example: - following: - - following - - following - user: user - properties: - following: - items: - type: string - type: array - user: - type: string - required: - - following - - user - type: object - recordingRecommendations: - example: - payload: - last_updated: 6 - model_url: model_url - offset: 5 - user_name: user_name - count: 0 - model_id: model_id - mbids: - - score: 1 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - latest_listened_at: latest_listened_at - - score: 1 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - latest_listened_at: latest_listened_at - total_mbid_count: 5 - entity: entity - properties: - payload: - $ref: "#/components/schemas/recordingRecommendations_payload" - required: - - payload - type: object - feedbackGivenBy: - example: - feedback: - - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - created: 6 - rating: rating - - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - created: 6 - rating: rating - offset: 1 - total_count: 5 - user_name: user_name - count: 0 - properties: - count: - type: integer - feedback: - items: - $ref: "#/components/schemas/feedbackGivenBy_feedback_inner" - type: array - offset: - type: integer - total_count: - type: integer - user_name: - type: string - required: - - count - - feedback - - offset - - total_count - - user_name - type: object - recordingsFeedbackGivenBy: - example: - feedback: - - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - created: 6 - rating: rating - - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - created: 6 - rating: rating - user_name: user_name - properties: - feedback: - items: - $ref: "#/components/schemas/feedbackGivenBy_feedback_inner" - type: array - user_name: - type: string - required: - - feedback - - user_name - type: object - freshReleases: - example: - payload: - total_count: 6 - releases: - - artist_credit_name: artist_credit_name - listen_count: 0 - release_name: release_name - release_date: release_date - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_tags: - - key: release_tags - - key: release_tags - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_primary_type: release_group_primary_type - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - listen_count: 0 - release_name: release_name - release_date: release_date - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_tags: - - key: release_tags - - key: release_tags - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_primary_type: release_group_primary_type - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - payload: - $ref: "#/components/schemas/freshReleases_payload" - required: - - payload - type: object - color: - example: - payload: - releases: - - caa_id: 0 - color: - - 6 - - 6 - release_name: release_name - artist_name: artist_name - recordings: - - track_metadata: - release_name: release_name - additional_info: - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - track_name: track_name - - track_metadata: - release_name: release_name - additional_info: - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - track_name: track_name - dist: 1 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - caa_id: 0 - color: - - 6 - - 6 - release_name: release_name - artist_name: artist_name - recordings: - - track_metadata: - release_name: release_name - additional_info: - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - track_name: track_name - - track_metadata: - release_name: release_name - additional_info: - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - track_name: track_name - dist: 1 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - payload: - $ref: "#/components/schemas/color_payload" - required: - - payload - type: object - lbRadio: - example: - payload: - feedback: - - feedback - - feedback - jspf: - playlist: - annotation: annotation - date: date - duration: 5 - identifier: identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#playlist: - creator: creator - created_for: created_for - public: true - collaborators: - - collaborators - - collaborators - copied_from: copied_from - copied_from_deleted: true - last_modified_at: last_modified_at - additional_metadata: - algorithm_metadata: - source_patch: source_patch - album: album - title: title - track: - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - properties: - payload: - $ref: "#/components/schemas/lbRadio_payload" - required: - - payload - type: object - getDumpInfo: - example: - id: 0 - timestamp: timestamp - properties: - id: - type: integer - timestamp: - type: string - required: - - id - - timestamp - type: object - createCoverArtGrid: - properties: - background: - description: The background for the cover art. - enum: - - transparent - - white - - black - type: string - image_size: - description: The size of the cover art image. - type: integer - dimension: - description: "The dimension to use for this grid. A grid of dimension 3\ - \ has 3 images across and 3 images down, for a total of 9 images." - type: integer - skip-missing: - description: "If cover art is missing for a given release_mbid, skip it\ - \ and move on to the next one, if true is passed. If false, the show-caa\ - \ option will decide what happens." - type: boolean - show-caa: - description: "If cover art is missing and skip-missing is false, then show-caa\ - \ will determine if a blank square is shown or if the Cover Art Archive\ - \ missing image is shown." - type: boolean - tiles: - description: "The tiles paramater is a list of strings that determines the\ - \ location where cover art images should be placed. Each string is a comma\ - \ separated list of image cells. A grid of dimension 3 has 9 cells, from\ - \ 0 in the upper left hand corner, 2 in the upper right hand corner, 6\ - \ in the lower left corner and 8 in the lower right corner. Specifying\ - \ only a single cell will have the image cover that cell exactly. If more\ - \ than one cell is specified, the image will cover the area defined by\ - \ the bounding box of all the given cells. These tiles only define bounding\ - \ box areas - no clipping of images that may fall outside of these tiles\ - \ will be performed." - items: - type: string - type: array - release_mbids: - description: An ordered list of release_mbids. The images will be loaded - and processed in the order that this list is in. The cover art for the - release_mbids will be placed on the tiles defined by the tiles parameter. - items: - format: uuid - type: string - type: array - required: - - background - - dimension - - image_size - - release_mbids - - show-caa - - skip-missing - - tiles - type: object - releaseGroups: - items: - $ref: "#/components/schemas/releaseGroups_inner" - type: array - playlist-extension: - example: - https://musicbrainz.org/doc/jspf#playlist: - creator: creator - created_for: created_for - public: true - collaborators: - - collaborators - - collaborators - copied_from: copied_from - copied_from_deleted: true - last_modified_at: last_modified_at - additional_metadata: - algorithm_metadata: - source_patch: source_patch - properties: - https://musicbrainz.org/doc/jspf#playlist: - $ref: "#/components/schemas/playlistExtensionPayload" - type: object - feedback: - example: - score: 1 - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - user_id: user_id - created: 6 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - properties: - created: - type: integer - recording_mbid: - format: uuid - type: string - recording_msid: - format: uuid - type: string - score: - type: integer - track_metadata: - $ref: "#/components/schemas/trackMetadata" - user_id: - type: string - type: object - additionalInfo: - additionalProperties: - type: string - example: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - properties: - media_player: - type: string - media_player_version: - type: string - submission_client: - type: string - submission_client_version: - type: string - music_service: - type: string - music_service_name: - type: string - origin_url: - type: string - release_mbid: - format: uuid - type: string - artist_mbids: - items: - format: uuid - type: string - type: array - recording_mbid: - format: uuid - type: string - recording_msid: - format: uuid - type: string - tags: - items: - type: string - type: array - duration: - type: integer - duration_ms: - type: integer - tracknumber: - type: integer - release_group_mbid: - format: uuid - type: string - track_mbid: - format: uuid - type: string - work_mbids: - items: - format: uuid - type: string - type: array - isrc: - type: string - spotify_id: - type: string - discnumber: - type: integer - listening_from: - type: string - release_artist_name: - type: string - release_artist_names: - items: - type: string - type: array - spotify_album_artist_ids: - items: - type: string - type: array - spotify_album_id: - type: string - spotify_artist_ids: - items: - type: string - type: array - youtube_id: - type: string - albumartist: - type: string - comment: - type: string - date: - type: string - genre: - type: string - artist_names: - items: - type: string - type: array - trackNumber: - type: string - type: object - mbidMapping: - example: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - artist_mbids: - items: - format: uuid - type: string - type: array - artists: - items: - $ref: "#/components/schemas/topReleasesForUser_payload_releases_inner_artists_inner" - type: array - caa_id: - type: integer - caa_release_mbid: - format: uuid - type: string - recording_mbid: - format: uuid - type: string - recording_name: - type: string - release_mbid: - format: uuid - type: string - type: object - musicBrainzArtist: - example: - area: area - gender: gender - join_phrase: join_phrase - name: name - type: type - end_year: 1 - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - begin_year: 6 - rels: - youtube: youtube - download for free: download for free - free streaming: free streaming - purchase for download: purchase for download - blog: blog - social network: social network - streaming: streaming - official homepage: official homepage - crowdfunding: crowdfunding - purchase for mail-order: purchase for mail-order - patronage: patronage - lyrics: lyrics - wikidata: wikidata - properties: - area: - type: string - artist_mbid: - format: uuid - type: string - begin_year: - type: integer - end_year: - type: integer - gender: - type: string - join_phrase: - type: string - name: - type: string - rels: - $ref: "#/components/schemas/rels_1" - type: - type: string - type: object - rels: - example: - artist_name: artist_name - instrument: instrument - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - artist_mbid: - format: uuid - type: string - artist_name: - type: string - instrument: - type: string - type: - type: string - type: object - rels_1: - example: - youtube: youtube - download for free: download for free - free streaming: free streaming - purchase for download: purchase for download - blog: blog - social network: social network - streaming: streaming - official homepage: official homepage - crowdfunding: crowdfunding - purchase for mail-order: purchase for mail-order - patronage: patronage - lyrics: lyrics - wikidata: wikidata - properties: - free streaming: - type: string - lyrics: - type: string - official homepage: - type: string - purchase for download: - type: string - download for free: - type: string - purchase for mail-order: - type: string - social network: - type: string - streaming: - type: string - wikidata: - type: string - youtube: - type: string - patronage: - type: string - crowdfunding: - type: string - blog: - type: string - type: object - metadata: - additionalProperties: - type: string - example: - relationship_type: relationship_type - playing_now: true - listened_at: 2 - listened_at_iso: listened_at_iso - created: 5 - user_name: user_name - blurb_content: blurb_content - message: message - inserted_at: 5 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - properties: - created: - type: integer - relationship_type: - type: string - message: - type: string - blurb_content: - type: string - inserted_at: - type: integer - listened_at: - type: integer - listened_at_iso: - type: string - playing_now: - type: boolean - track_metadata: - $ref: "#/components/schemas/trackMetadata" - user_name: - type: string - type: object - createPlaylist_request: - example: - playlist: - annotation: annotation - date: date - duration: 5 - identifier: identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#playlist: - creator: creator - created_for: created_for - public: true - collaborators: - - collaborators - - collaborators - copied_from: copied_from - copied_from_deleted: true - last_modified_at: last_modified_at - additional_metadata: - algorithm_metadata: - source_patch: source_patch - album: album - title: title - track: - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - properties: - playlist: - $ref: "#/components/schemas/playlist" - type: object - createPlaylist_200_response: - example: - playlist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - status: status - properties: - playlist_mbid: - format: uuid - type: string - status: - type: string - type: object - moveItem_request: - properties: - mbid: - format: uuid - type: string - from: - type: integer - to: - type: integer - count: - type: integer - type: object - itemDelete_request: - properties: - index: - type: integer - count: - type: integer - type: object - recordingFeedback_request: - properties: - recording_mbid: - format: uuid - type: string - recording_msid: - format: uuid - type: string - score: - maximum: 1 - minimum: -1 - type: integer - type: object - pin_request: - properties: - recording_msid: - format: uuid - type: string - recording_mbid: - format: uuid - type: string - blurb_content: - type: string - pinned_until: - format: int64 - type: integer - type: object - pin_200_response_pinned_recording: - example: - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - pinned_until: 6 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - created: 0 - blurb_content: blurb_content - row_id: 1 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - properties: - created: - type: integer - pinned_until: - type: integer - recording_mbid: - format: uuid - type: string - recording_msid: - format: uuid - type: string - row_id: - type: integer - blurb_content: - type: string - track_metadata: - $ref: "#/components/schemas/trackMetadata" - type: object - pin_200_response: - example: - pinned_recording: - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - pinned_until: 6 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - created: 0 - blurb_content: blurb_content - row_id: 1 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - properties: - pinned_recording: - $ref: "#/components/schemas/pin_200_response_pinned_recording" - type: object - getPinsCurrent_200_response_pinned_recording: - example: - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - pinned_until: 6 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - created: 0 - blurb_content: blurb_content - row_id: 1 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - properties: - blurb_content: - type: string - created: - type: integer - pinned_until: - type: integer - recording_mbid: - format: uuid - type: string - recording_msid: - format: uuid - type: string - row_id: - type: integer - track_metadata: - $ref: "#/components/schemas/trackMetadata" - type: object - getPinsCurrent_200_response: - example: - pinned_recording: - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - pinned_until: 6 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - created: 0 - blurb_content: blurb_content - row_id: 1 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - user_name: user_name - properties: - pinned_recording: - $ref: "#/components/schemas/getPinsCurrent_200_response_pinned_recording" - user_name: - type: string - type: object - updatePin_request: - properties: - blurb_content: - type: string - type: object - recording_request: - properties: - recording_mbids: - items: - format: uuid - type: string - maxItems: 1000 - type: array - required: - - recording_mbids - type: object - recording_200_response_inner: - example: - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - total_listen_count: 0 - total_user_count: 6 - properties: - recording_mbid: - format: uuid - type: string - total_listen_count: - type: integer - total_user_count: - type: integer - type: object - artist_request: - properties: - artist_mbids: - items: - format: uuid - type: string - maxItems: 1000 - type: array - required: - - artist_mbids - type: object - artist_200_response_inner: - example: - total_listen_count: 0 - total_user_count: 6 - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - artist_mbid: - format: uuid - type: string - total_listen_count: - type: integer - total_user_count: - type: integer - type: object - release_request: - properties: - release_mbids: - items: - format: uuid - type: string - maxItems: 1000 - type: array - required: - - release_mbids - type: object - release_200_response_inner: - example: - total_listen_count: 0 - total_user_count: 6 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - release_mbid: - format: uuid - type: string - total_listen_count: - type: integer - total_user_count: - type: integer - type: object - releaseGroup_request: - properties: - release_group_mbids: - items: - format: uuid - type: string - maxItems: 1000 - type: array - required: - - release_group_mbids - type: object - releaseGroup_200_response_inner: - example: - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - total_listen_count: 0 - total_user_count: 6 - properties: - release_group_mbid: - format: uuid - type: string - total_listen_count: - type: integer - total_user_count: - type: integer - type: object - recommendRecording_request_metadata: - properties: - recording_msid: - format: uuid - type: string - recording_mbid: - format: uuid - type: string - type: object - recommendRecording_request: - properties: - metadata: - $ref: "#/components/schemas/recommendRecording_request_metadata" - type: object - createNotification_request_metadata: - properties: - message: - type: string - type: object - createNotification_request: - properties: - metadata: - $ref: "#/components/schemas/createNotification_request_metadata" - type: object - createReview_request_metadata: - properties: - entity_name: - type: string - entity_id: - type: string - entity_type: - type: string - text: - type: string - language: - type: string - rating: - type: integer - type: object - createReview_request: - properties: - metadata: - $ref: "#/components/schemas/createReview_request_metadata" - type: object - feedEventsDelete_request: - properties: - event_type: - type: string - id: - type: integer - type: object - recommendPersonalRecording_request_metadata: - properties: - recording_msid: - format: uuid - type: string - recording_mbid: - format: uuid - type: string - users: - type: string - blurb_content: - type: string - type: object - recommendPersonalRecording_request: - properties: - metadata: - $ref: "#/components/schemas/recommendPersonalRecording_request_metadata" - type: object - submitFeedback_request: - properties: - recording_mbid: - format: uuid - type: string - rating: - $ref: "#/components/schemas/AllowedRatings" - type: object - deleteFeedback_request: - properties: - recording_mbid: - format: uuid - type: string - type: object - searchUsers_users_inner: - example: - user_name: user_name - properties: - user_name: - type: string - type: object - submitListens_payload_inner: - properties: - listened_at: - format: int32 - minimum: 0 - type: integer - track_metadata: - $ref: "#/components/schemas/trackMetadata" - type: object - listensForUser_payload_listens_inner: - example: - recording_msid: recording_msid - listened_at: 5 - user_name: user_name - inserted_at: 1 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - properties: - inserted_at: - type: integer - listened_at: - type: integer - recording_msid: - type: string - track_metadata: - $ref: "#/components/schemas/trackMetadata" - user_name: - type: string - type: object - listensForUser_payload: - example: - listens: - - recording_msid: recording_msid - listened_at: 5 - user_name: user_name - inserted_at: 1 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - - recording_msid: recording_msid - listened_at: 5 - user_name: user_name - inserted_at: 1 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - user_id: user_id - latest_listen_ts: 6 - count: 0 - oldest_listen_ts: 2 - properties: - count: - type: integer - latest_listen_ts: - type: integer - listens: - items: - $ref: "#/components/schemas/listensForUser_payload_listens_inner" - type: array - oldest_listen_ts: - type: integer - user_id: - type: string - type: object - listenCountForUser_payload: - example: - count: 0 - properties: - count: - type: integer - required: - - count - type: object - playingNowForUser_payload_listens_inner: - example: - playing_now: true - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - properties: - playing_now: - type: boolean - track_metadata: - $ref: "#/components/schemas/trackMetadata" - type: object - playingNowForUser_payload: - example: - listens: - - playing_now: true - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - - playing_now: true - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - playing_now: true - user_id: user_id - count: 0 - properties: - count: - type: integer - listens: - items: - $ref: "#/components/schemas/playingNowForUser_payload_listens_inner" - type: array - playing_now: - type: boolean - user_id: - type: string - required: - - count - - listens - - playing_now - - user_id - type: object - similarUsersForUser_payload_inner: - example: - similarity: 0 - user_name: user_name - properties: - similarity: - type: integer - user_name: - type: string - type: object - similarityOfUserForUser_payload: - example: - similarity: 0 - user_name: user_name - properties: - similarity: - type: integer - user_name: - type: string - required: - - similarity - - user_name - type: object - latestImport_status: - example: - count: 0 - state: state - properties: - state: - description: a short string denoting the state of the import - type: string - count: - description: the number of listens that have been imported for the user - by the importer - type: integer - type: object - lbRadioRecordingsForArtist_value_inner: - properties: - recording_mbid: - format: uuid - type: string - similar_artist_mbid: - format: uuid - type: string - similar_artist_name: - type: string - total_listen_count: - type: integer - type: object - lbRadioTags_inner: - example: - tag_count: 6 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - source: source - percent: 0 - properties: - percent: - type: integer - recording_mbid: - format: uuid - type: string - source: - type: string - tag_count: - type: integer - type: object - topArtistsForUser_payload_artists_inner: - example: - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - artist_mbid: - format: uuid - type: string - artist_name: - type: string - listen_count: - type: integer - type: object - topArtistsForUser_payload: - example: - last_updated: 5 - offset: 5 - artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - to_ts: 2 - user_id: user_id - from_ts: 1 - count: 6 - range: range - total_artist_count: 7 - properties: - artists: - items: - $ref: "#/components/schemas/topArtistsForUser_payload_artists_inner" - type: array - count: - type: integer - from_ts: - type: integer - last_updated: - type: integer - offset: - type: integer - range: - type: string - to_ts: - type: integer - total_artist_count: - type: integer - user_id: - type: string - required: - - artists - - count - - from_ts - - last_updated - - offset - - range - - to_ts - - total_artist_count - - user_id - type: object - topReleasesForUser_payload_releases_inner_artists_inner: - example: - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - artist_credit_name: - type: string - artist_mbid: - format: uuid - type: string - join_phrase: - type: string - type: object - topReleasesForUser_payload_releases_inner: - example: - listen_count: 2 - caa_id: 5 - release_name: release_name - artist_name: artist_name - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - artist_mbids: - items: - format: uuid - type: string - type: array - artist_name: - type: string - artists: - items: - $ref: "#/components/schemas/topReleasesForUser_payload_releases_inner_artists_inner" - type: array - caa_id: - type: integer - caa_release_mbid: - format: uuid - type: string - listen_count: - type: integer - release_mbid: - format: uuid - type: string - release_name: - type: string - type: object - topReleasesForUser_payload: - example: - last_updated: 1 - offset: 5 - to_ts: 7 - user_id: user_id - from_ts: 6 - count: 0 - total_release_count: 9 - range: range - releases: - - listen_count: 2 - caa_id: 5 - release_name: release_name - artist_name: artist_name - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 2 - caa_id: 5 - release_name: release_name - artist_name: artist_name - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - count: - type: integer - from_ts: - type: integer - last_updated: - type: integer - offset: - type: integer - range: - type: string - releases: - items: - $ref: "#/components/schemas/topReleasesForUser_payload_releases_inner" - type: array - to_ts: - type: integer - total_release_count: - type: integer - user_id: - type: string - required: - - count - - from_ts - - last_updated - - offset - - range - - releases - - to_ts - - total_release_count - - user_id - type: object - topReleaseGroupsForUser_payload: - example: - total_release_group_count: 3 - last_updated: 1 - offset: 5 - to_ts: 9 - user_id: user_id - from_ts: 6 - count: 0 - range: range - release_groups: - - listen_count: 7 - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_name: release_group_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 7 - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_name: release_group_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - count: - type: integer - from_ts: - type: integer - last_updated: - type: integer - offset: - type: integer - range: - type: string - release_groups: - items: - $ref: "#/components/schemas/releaseGroups_inner" - type: array - to_ts: - type: integer - total_release_group_count: - type: integer - user_id: - type: string - required: - - count - - from_ts - - last_updated - - offset - - range - - release_groups - - to_ts - - total_release_group_count - - user_id - type: object - topRecordingsForUser_payload_recordings_inner: - example: - listen_count: 2 - caa_id: 5 - release_name: release_name - artist_name: artist_name - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - track_name: track_name - properties: - artist_mbids: - items: - format: uuid - type: string - type: array - artist_name: - type: string - artists: - items: - $ref: "#/components/schemas/topReleasesForUser_payload_releases_inner_artists_inner" - type: array - caa_id: - type: integer - caa_release_mbid: - format: uuid - type: string - listen_count: - type: integer - recording_mbid: - format: uuid - type: string - release_mbid: - format: uuid - type: string - release_name: - type: string - track_name: - type: string - type: object - topRecordingsForUser_payload: - example: - last_updated: 1 - offset: 5 - recordings: - - listen_count: 2 - caa_id: 5 - release_name: release_name - artist_name: artist_name - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - track_name: track_name - - listen_count: 2 - caa_id: 5 - release_name: release_name - artist_name: artist_name - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - track_name: track_name - to_ts: 7 - user_id: user_id - from_ts: 6 - count: 0 - range: range - total_recording_count: 9 - properties: - count: - type: integer - from_ts: - type: integer - last_updated: - type: integer - offset: - type: integer - range: - type: string - recordings: - items: - $ref: "#/components/schemas/topRecordingsForUser_payload_recordings_inner" - type: array - to_ts: - type: integer - total_recording_count: - type: integer - user_id: - type: string - required: - - count - - from_ts - - last_updated - - offset - - range - - recordings - - to_ts - - total_recording_count - - user_id - type: object - listeningActivityForUser_payload_listening_activity_inner: - example: - listen_count: 5 - time_range: time_range - to_ts: 5 - from_ts: 1 - properties: - from_ts: - type: integer - listen_count: - type: integer - time_range: - type: string - to_ts: - type: integer - type: object - listeningActivityForUser_payload: - example: - last_updated: 6 - listening_activity: - - listen_count: 5 - time_range: time_range - to_ts: 5 - from_ts: 1 - - listen_count: 5 - time_range: time_range - to_ts: 5 - from_ts: 1 - to_ts: 2 - user_id: user_id - from_ts: 0 - range: range - properties: - from_ts: - type: integer - last_updated: - type: integer - listening_activity: - items: - $ref: "#/components/schemas/listeningActivityForUser_payload_listening_activity_inner" - type: array - range: - type: string - to_ts: - type: integer - user_id: - type: string - required: - - from_ts - - last_updated - - listening_activity - - range - - to_ts - - user_id - type: object - dailyActivityForUser_payload_daily_activity_Friday_inner: - example: - listen_count: 6 - hour: 0 - properties: - hour: - type: integer - listen_count: - type: integer - type: object - dailyActivityForUser_payload_daily_activity: - example: - Monday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - Thursday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - Friday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - Sunday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - Wednesday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - Tuesday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - Saturday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - properties: - Friday: - items: - $ref: "#/components/schemas/dailyActivityForUser_payload_daily_activity_Friday_inner" - type: array - Monday: - items: - $ref: "#/components/schemas/dailyActivityForUser_payload_daily_activity_Friday_inner" - type: array - Saturday: - items: - $ref: "#/components/schemas/dailyActivityForUser_payload_daily_activity_Friday_inner" - type: array - Sunday: - items: - $ref: "#/components/schemas/dailyActivityForUser_payload_daily_activity_Friday_inner" - type: array - Thursday: - items: - $ref: "#/components/schemas/dailyActivityForUser_payload_daily_activity_Friday_inner" - type: array - Tuesday: - items: - $ref: "#/components/schemas/dailyActivityForUser_payload_daily_activity_Friday_inner" - type: array - Wednesday: - items: - $ref: "#/components/schemas/dailyActivityForUser_payload_daily_activity_Friday_inner" - type: array - required: - - Friday - - Monday - - Saturday - - Sunday - - Thursday - - Tuesday - - Wednesday - type: object - dailyActivityForUser_payload: - example: - last_updated: 5 - to_ts: 5 - user_id: user_id - from_ts: 1 - range: range - daily_activity: - Monday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - Thursday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - Friday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - Sunday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - Wednesday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - Tuesday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - Saturday: - - listen_count: 6 - hour: 0 - - listen_count: 6 - hour: 0 - properties: - daily_activity: - $ref: "#/components/schemas/dailyActivityForUser_payload_daily_activity" - from_ts: - type: integer - last_updated: - type: integer - range: - type: string - to_ts: - type: integer - user_id: - type: string - required: - - daily_activity - - from_ts - - last_updated - - range - - to_ts - - user_id - type: object - artistMapForUser_payload_artist_map_inner: - example: - listen_count: 6 - country: country - artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_count: 0 - properties: - artist_count: - type: integer - artists: - items: - $ref: "#/components/schemas/topArtistsForUser_payload_artists_inner" - type: array - country: - type: string - listen_count: - type: integer - type: object - artistMapForUser_payload: - example: - last_updated: 5 - artist_map: - - listen_count: 6 - country: country - artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_count: 0 - - listen_count: 6 - country: country - artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_count: 0 - to_ts: 5 - user_id: user_id - from_ts: 1 - range: range - properties: - artist_map: - items: - $ref: "#/components/schemas/artistMapForUser_payload_artist_map_inner" - type: array - from_ts: - type: integer - last_updated: - type: integer - range: - type: string - to_ts: - type: integer - user_id: - type: string - required: - - artist_map - - from_ts - - last_updated - - range - - to_ts - - user_id - type: object - listenersForArtist_payload_listeners_inner: - example: - listen_count: 1 - user_name: user_name - properties: - listen_count: - type: integer - user_name: - type: string - type: object - listenersForArtist_payload: - example: - last_updated: 6 - listeners: - - listen_count: 1 - user_name: user_name - - listen_count: 1 - user_name: user_name - artist_name: artist_name - to_ts: 5 - from_ts: 0 - total_listen_count: 5 - total_user_count: 2 - stats_range: stats_range - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - artist_mbid: - format: uuid - type: string - artist_name: - type: string - from_ts: - type: integer - last_updated: - type: integer - listeners: - items: - $ref: "#/components/schemas/listenersForArtist_payload_listeners_inner" - type: array - stats_range: - type: string - to_ts: - type: integer - total_listen_count: - type: integer - total_user_count: - type: integer - required: - - artist_mbid - - artist_name - - from_ts - - last_updated - - listeners - - stats_range - - to_ts - - total_listen_count - - total_user_count - type: object - listenersForReleaseGroup_payload: - example: - last_updated: 1 - caa_id: 0 - listeners: - - listen_count: 1 - user_name: user_name - - listen_count: 1 - user_name: user_name - artist_name: artist_name - from_ts: 6 - total_listen_count: 5 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - total_user_count: 2 - stats_range: stats_range - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - to_ts: 5 - release_group_name: release_group_name - properties: - artist_mbids: - items: - format: uuid - type: string - type: array - artist_name: - type: string - caa_id: - type: integer - caa_release_mbid: - format: uuid - type: string - from_ts: - type: integer - last_updated: - type: integer - listeners: - items: - $ref: "#/components/schemas/listenersForArtist_payload_listeners_inner" - type: array - release_group_mbid: - format: uuid - type: string - release_group_name: - type: string - stats_range: - type: string - to_ts: - type: integer - total_listen_count: - type: integer - total_user_count: - type: integer - required: - - artist_mbids - - artist_name - - caa_id - - caa_release_mbid - - from_ts - - last_updated - - listeners - - release_group_mbid - - release_group_name - - stats_range - - to_ts - - total_listen_count - - total_user_count - type: object - sitewideTopArtists_payload: - example: - last_updated: 1 - offset: 5 - artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - to_ts: 5 - from_ts: 6 - count: 0 - range: range - total_artist_count: 2 - properties: - artists: - items: - $ref: "#/components/schemas/topArtistsForUser_payload_artists_inner" - type: array - count: - type: integer - from_ts: - type: integer - last_updated: - type: integer - offset: - type: integer - range: - type: string - to_ts: - type: integer - total_artist_count: - type: integer - required: - - artists - - count - - from_ts - - last_updated - - offset - - range - - to_ts - - total_artist_count - type: object - sitewideTopReleases_payload_releases_inner: - example: - listen_count: 2 - caa_id: 5 - release_name: release_name - artists: artists - artist_name: artist_name - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - artists: - nullable: true - type: string - artist_mbids: - items: - format: uuid - type: string - type: array - artist_name: - type: string - caa_id: - type: integer - caa_release_mbid: - format: uuid - type: string - listen_count: - type: integer - release_mbid: - format: uuid - type: string - release_name: - type: string - type: object - sitewideTopReleases_payload: - example: - last_updated: 1 - offset: 5 - to_ts: 7 - from_ts: 6 - count: 0 - total_release_count: 9 - range: range - releases: - - listen_count: 2 - caa_id: 5 - release_name: release_name - artists: artists - artist_name: artist_name - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 2 - caa_id: 5 - release_name: release_name - artists: artists - artist_name: artist_name - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - count: - type: integer - from_ts: - type: integer - last_updated: - type: integer - offset: - type: integer - range: - type: string - releases: - items: - $ref: "#/components/schemas/sitewideTopReleases_payload_releases_inner" - type: array - to_ts: - type: integer - total_release_count: - type: integer - required: - - count - - from_ts - - last_updated - - offset - - range - - releases - - to_ts - - total_release_count - type: object - sitewideTopReleaseGroups_payload: - example: - total_release_group_count: 2 - last_updated: 1 - offset: 5 - to_ts: 5 - from_ts: 6 - count: 0 - range: range - release_groups: - - listen_count: 7 - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_name: release_group_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 7 - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_name: release_group_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - count: - type: integer - from_ts: - type: integer - last_updated: - type: integer - offset: - type: integer - range: - type: string - release_groups: - items: - $ref: "#/components/schemas/releaseGroups_inner" - type: array - to_ts: - type: integer - total_release_group_count: - type: integer - required: - - count - - from_ts - - last_updated - - offset - - range - - release_groups - - to_ts - - total_release_group_count - type: object - sitewideTopRecordings_payload_recordings_inner: - example: - listen_count: 2 - caa_id: 5 - release_name: release_name - artists: - - artists - - artists - artist_name: artist_name - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - track_name: track_name - properties: - artists: - items: - type: string - type: array - artist_mbids: - items: - format: uuid - type: string - type: array - artist_name: - type: string - caa_id: - type: integer - caa_release_mbid: - format: uuid - type: string - listen_count: - type: integer - recording_mbid: - format: uuid - type: string - release_mbid: - format: uuid - type: string - release_name: - type: string - track_name: - type: string - type: object - sitewideTopRecordings_payload: - example: - last_updated: 1 - offset: 5 - recordings: - - listen_count: 2 - caa_id: 5 - release_name: release_name - artists: - - artists - - artists - artist_name: artist_name - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - track_name: track_name - - listen_count: 2 - caa_id: 5 - release_name: release_name - artists: - - artists - - artists - artist_name: artist_name - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - track_name: track_name - to_ts: 7 - from_ts: 6 - count: 0 - range: range - total_recording_count: 9 - properties: - count: - type: integer - from_ts: - type: integer - last_updated: - type: integer - offset: - type: integer - range: - type: string - recordings: - items: - $ref: "#/components/schemas/sitewideTopRecordings_payload_recordings_inner" - type: array - to_ts: - type: integer - total_recording_count: - type: integer - required: - - count - - from_ts - - last_updated - - offset - - range - - recordings - - to_ts - - total_recording_count - type: object - sitewideListeningActivity_payload: - example: - last_updated: 6 - listening_activity: - - listen_count: 5 - time_range: time_range - to_ts: 5 - from_ts: 1 - - listen_count: 5 - time_range: time_range - to_ts: 5 - from_ts: 1 - to_ts: 1 - from_ts: 0 - range: range - properties: - from_ts: - type: integer - last_updated: - type: integer - listening_activity: - items: - $ref: "#/components/schemas/listeningActivityForUser_payload_listening_activity_inner" - type: array - range: - type: string - to_ts: - type: integer - required: - - from_ts - - last_updated - - listening_activity - - range - - to_ts - type: object - sitewideArtistMap_payload: - example: - last_updated: 6 - artist_map: - - listen_count: 6 - country: country - artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_count: 0 - - listen_count: 6 - country: country - artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_count: 0 - to_ts: 1 - from_ts: 0 - stats_range: stats_range - properties: - artist_map: - items: - $ref: "#/components/schemas/artistMapForUser_payload_artist_map_inner" - type: array - from_ts: - type: integer - last_updated: - type: integer - stats_range: - type: string - to_ts: - type: integer - required: - - artist_map - - from_ts - - last_updated - - range - - to_ts - type: object - yearInMusicForUser_payload_data_new_releases_of_top_artists_inner: - example: - artist_credit_name: artist_credit_name - caa_id: 6 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - title: title - artist_credit_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - artist_credit_mbids: - items: - format: uuid - type: string - type: array - artist_credit_name: - type: string - artists: - items: - $ref: "#/components/schemas/topReleasesForUser_payload_releases_inner_artists_inner" - type: array - caa_id: - type: integer - caa_release_mbid: - format: uuid - type: string - release_group_mbid: - format: uuid - type: string - title: - type: string - type: object - yearInMusicForUser_payload_data_top_genres_inner: - example: - genre_count_percent: 5 - genre_count: 5 - genre: genre - properties: - genre: - type: string - genre_count: - type: integer - genre_count_percent: - type: integer - type: object - yearInMusicForUser_payload_data: - example: - most_listened_year: - key: 0 - top_release_groups: - - listen_count: 7 - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_name: release_group_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 7 - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_name: release_group_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - total_new_artists_discovered: 3 - similar_users: - key: 1 - listens_per_day: - - listen_count: 5 - time_range: time_range - to_ts: 5 - from_ts: 1 - - listen_count: 5 - time_range: time_range - to_ts: 5 - from_ts: 1 - total_listen_count: 7 - total_artists_count: 2 - total_recordings_count: 2 - new_releases_of_top_artists: - - artist_credit_name: artist_credit_name - caa_id: 6 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - title: title - artist_credit_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - caa_id: 6 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - title: title - artist_credit_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - top_artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - top_recordings: - - listen_count: 2 - caa_id: 5 - release_name: release_name - artist_name: artist_name - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - track_name: track_name - - listen_count: 2 - caa_id: 5 - release_name: release_name - artist_name: artist_name - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - track_name: track_name - total_listening_time: 9 - playlist-top-missed-recordings-for-year: - annotation: annotation - date: date - duration: 5 - identifier: identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#playlist: - creator: creator - created_for: created_for - public: true - collaborators: - - collaborators - - collaborators - copied_from: copied_from - copied_from_deleted: true - last_modified_at: last_modified_at - additional_metadata: - algorithm_metadata: - source_patch: source_patch - album: album - title: title - track: - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - artist_map: - - listen_count: 6 - country: country - artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_count: 0 - - listen_count: 6 - country: country - artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_count: 0 - top_genres: - - genre_count_percent: 5 - genre_count: 5 - genre: genre - - genre_count_percent: 5 - genre_count: 5 - genre: genre - total_release_groups_count: 4 - playlist-top-discoveries-for-year: - annotation: annotation - date: date - duration: 5 - identifier: identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#playlist: - creator: creator - created_for: created_for - public: true - collaborators: - - collaborators - - collaborators - copied_from: copied_from - copied_from_deleted: true - last_modified_at: last_modified_at - additional_metadata: - algorithm_metadata: - source_patch: source_patch - album: album - title: title - track: - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - day_of_week: day_of_week - properties: - artist_map: - items: - $ref: "#/components/schemas/artistMapForUser_payload_artist_map_inner" - type: array - day_of_week: - type: string - listens_per_day: - items: - $ref: "#/components/schemas/listeningActivityForUser_payload_listening_activity_inner" - type: array - most_listened_year: - additionalProperties: - type: integer - type: object - new_releases_of_top_artists: - items: - $ref: "#/components/schemas/yearInMusicForUser_payload_data_new_releases_of_top_artists_inner" - type: array - playlist-top-discoveries-for-year: - $ref: "#/components/schemas/playlist" - playlist-top-missed-recordings-for-year: - $ref: "#/components/schemas/playlist" - similar_users: - additionalProperties: - type: integer - type: object - top_artists: - items: - $ref: "#/components/schemas/topArtistsForUser_payload_artists_inner" - type: array - top_genres: - items: - $ref: "#/components/schemas/yearInMusicForUser_payload_data_top_genres_inner" - type: array - top_recordings: - items: - $ref: "#/components/schemas/topRecordingsForUser_payload_recordings_inner" - type: array - top_release_groups: - items: - $ref: "#/components/schemas/releaseGroups_inner" - type: array - total_artists_count: - type: integer - total_listen_count: - type: integer - total_listening_time: - type: integer - total_new_artists_discovered: - type: integer - total_recordings_count: - type: integer - total_release_groups_count: - type: integer - required: - - artist_map - - day_of_week - - listens_per_day - - most_listened_year - - new_releases_of_top_artists - - playlist-top-discoveries-for-year - - playlist-top-missed-recordings-for-year - - similar_users - - top_artists - - top_genres - - top_recordings - - top_release_groups - - total_artists_count - - total_listen_count - - total_listening_time - - total_new_artists_discovered - - total_recordings_count - - total_release_groups_count - type: object - yearInMusicForUser_payload: - example: - data: - most_listened_year: - key: 0 - top_release_groups: - - listen_count: 7 - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_name: release_group_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 7 - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_name: release_group_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - total_new_artists_discovered: 3 - similar_users: - key: 1 - listens_per_day: - - listen_count: 5 - time_range: time_range - to_ts: 5 - from_ts: 1 - - listen_count: 5 - time_range: time_range - to_ts: 5 - from_ts: 1 - total_listen_count: 7 - total_artists_count: 2 - total_recordings_count: 2 - new_releases_of_top_artists: - - artist_credit_name: artist_credit_name - caa_id: 6 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - title: title - artist_credit_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - caa_id: 6 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - title: title - artist_credit_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - top_artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - top_recordings: - - listen_count: 2 - caa_id: 5 - release_name: release_name - artist_name: artist_name - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - track_name: track_name - - listen_count: 2 - caa_id: 5 - release_name: release_name - artist_name: artist_name - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - track_name: track_name - total_listening_time: 9 - playlist-top-missed-recordings-for-year: - annotation: annotation - date: date - duration: 5 - identifier: identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#playlist: - creator: creator - created_for: created_for - public: true - collaborators: - - collaborators - - collaborators - copied_from: copied_from - copied_from_deleted: true - last_modified_at: last_modified_at - additional_metadata: - algorithm_metadata: - source_patch: source_patch - album: album - title: title - track: - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - artist_map: - - listen_count: 6 - country: country - artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_count: 0 - - listen_count: 6 - country: country - artists: - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - listen_count: 0 - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_count: 0 - top_genres: - - genre_count_percent: 5 - genre_count: 5 - genre: genre - - genre_count_percent: 5 - genre_count: 5 - genre: genre - total_release_groups_count: 4 - playlist-top-discoveries-for-year: - annotation: annotation - date: date - duration: 5 - identifier: identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#playlist: - creator: creator - created_for: created_for - public: true - collaborators: - - collaborators - - collaborators - copied_from: copied_from - copied_from_deleted: true - last_modified_at: last_modified_at - additional_metadata: - algorithm_metadata: - source_patch: source_patch - album: album - title: title - track: - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - day_of_week: day_of_week - user_name: user_name - properties: - data: - $ref: "#/components/schemas/yearInMusicForUser_payload_data" - user_name: - type: string - required: - - data - - user_name - type: object - playlist_track_inner_extension_https___musicbrainz_org_doc_jspf_track_additional_metadata: - example: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - caa_release_mbid: - format: uuid - type: string - caa_id: - type: integer - artists: - items: - $ref: "#/components/schemas/topReleasesForUser_payload_releases_inner_artists_inner" - type: array - type: object - playlist_track_inner_extension_https___musicbrainz_org_doc_jspf_track: - example: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - artist_identifiers: - items: - type: string - type: array - release_identifier: - type: string - added_at: - type: string - added_by: - type: string - additional_metadata: - $ref: "#/components/schemas/playlist_track_inner_extension_https___musicbrainz_org_doc_jspf_track_additional_metadata" - type: object - playlist_track_inner_extension: - example: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - https://musicbrainz.org/doc/jspf#track: - $ref: "#/components/schemas/playlist_track_inner_extension_https___musicbrainz_org_doc_jspf_track" - required: - - https://musicbrainz.org/doc/jspf#track - type: object - playlist_track_inner: - example: - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - properties: - album: - type: string - creator: - type: string - duration: - type: integer - extension: - $ref: "#/components/schemas/playlist_track_inner_extension" - identifier: - items: - type: string - type: array - title: - type: string - type: object - trackMetadata_brainzplayer_metadata: - example: - release_name: release_name - artist_name: artist_name - track_name: track_name - properties: - artist_name: - type: string - release_name: - type: string - track_name: - type: string - type: object - getPins_pinned_recordings_inner: - example: - recording_msid: recording_msid - pinned_until: 5 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - created: 1 - blurb_content: blurb_content - row_id: 5 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - properties: - blurb_content: - type: string - created: - type: integer - pinned_until: - type: integer - recording_mbid: - format: uuid - type: string - recording_msid: - type: string - row_id: - format: uuid - type: integer - track_metadata: - $ref: "#/components/schemas/trackMetadata" - type: object - topRecordingsForArtist_inner_release_color: - example: - red: 5 - green: 5 - blue: 1 - properties: - blue: - type: integer - green: - type: integer - red: - type: integer - type: object - topRecordingsForArtist_inner_tags_inner: - example: - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 9 - tag: tag - properties: - count: - type: integer - genre_mbid: - format: uuid - type: string - tag: - type: string - type: object - topRecordingsForArtist_inner: - example: - recording_name: recording_name - caa_id: 0 - artist_name: artist_name - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - total_listen_count: 2 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - length: 6 - total_user_count: 7 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tags: - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 9 - tag: tag - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 9 - tag: tag - release_name: release_name - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_color: - red: 5 - green: 5 - blue: 1 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - artist_mbids: - items: - format: uuid - type: string - type: array - artist_name: - type: string - artists: - items: - $ref: "#/components/schemas/topReleasesForUser_payload_releases_inner_artists_inner" - type: array - caa_id: - type: integer - caa_release_mbid: - format: uuid - type: string - length: - type: integer - recording_mbid: - format: uuid - type: string - recording_name: - type: string - release_color: - $ref: "#/components/schemas/topRecordingsForArtist_inner_release_color" - release_mbid: - format: uuid - type: string - release_name: - type: string - total_listen_count: - type: integer - total_user_count: - type: integer - tags: - items: - $ref: "#/components/schemas/topRecordingsForArtist_inner_tags_inner" - type: array - type: object - topReleaseGroupsForArtist_inner_artist: - example: - artist_credit_id: 0 - artists: - - area: area - gender: gender - join_phrase: join_phrase - name: name - type: type - end_year: 1 - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - begin_year: 6 - rels: - youtube: youtube - download for free: download for free - free streaming: free streaming - purchase for download: purchase for download - blog: blog - social network: social network - streaming: streaming - official homepage: official homepage - crowdfunding: crowdfunding - purchase for mail-order: purchase for mail-order - patronage: patronage - lyrics: lyrics - wikidata: wikidata - - area: area - gender: gender - join_phrase: join_phrase - name: name - type: type - end_year: 1 - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - begin_year: 6 - rels: - youtube: youtube - download for free: download for free - free streaming: free streaming - purchase for download: purchase for download - blog: blog - social network: social network - streaming: streaming - official homepage: official homepage - crowdfunding: crowdfunding - purchase for mail-order: purchase for mail-order - patronage: patronage - lyrics: lyrics - wikidata: wikidata - name: name - properties: - artist_credit_id: - type: integer - artists: - items: - $ref: "#/components/schemas/musicBrainzArtist" - type: array - name: - type: string - type: object - topReleaseGroupsForArtist_inner_release: - example: - date: date - caa_id: 5 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - name: name - type: type - rels: - - artist_name: artist_name - instrument: instrument - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_name: artist_name - instrument: instrument - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - caa_id: - type: integer - caa_release_mbid: - format: uuid - type: string - rels: - items: - $ref: "#/components/schemas/rels" - type: array - date: - type: string - name: - type: string - type: - type: string - type: object - topReleaseGroupsForArtist_inner_release_group: - example: - date: date - caa_id: 5 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - name: name - type: type - rels: - - artist_name: artist_name - instrument: instrument - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_name: artist_name - instrument: instrument - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - caa_id: - type: integer - caa_release_mbid: - format: uuid - type: string - date: - type: string - name: - type: string - type: - type: string - rels: - items: - $ref: "#/components/schemas/rels" - type: array - type: object - topReleaseGroupsForArtist_inner_tag_artist_inner: - example: - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 2 - tag: tag - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - artist_mbid: - format: uuid - type: string - count: - type: integer - genre_mbid: - format: uuid - type: string - tag: - type: string - type: object - topReleaseGroupsForArtist_inner_tag: - example: - artist: - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 2 - tag: tag - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 2 - tag: tag - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group: - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 9 - tag: tag - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 9 - tag: tag - properties: - artist: - items: - $ref: "#/components/schemas/topReleaseGroupsForArtist_inner_tag_artist_inner" - type: array - release_group: - items: - $ref: "#/components/schemas/topRecordingsForArtist_inner_tags_inner" - type: array - type: object - topReleaseGroupsForArtist_inner: - example: - artist: - artist_credit_id: 0 - artists: - - area: area - gender: gender - join_phrase: join_phrase - name: name - type: type - end_year: 1 - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - begin_year: 6 - rels: - youtube: youtube - download for free: download for free - free streaming: free streaming - purchase for download: purchase for download - blog: blog - social network: social network - streaming: streaming - official homepage: official homepage - crowdfunding: crowdfunding - purchase for mail-order: purchase for mail-order - patronage: patronage - lyrics: lyrics - wikidata: wikidata - - area: area - gender: gender - join_phrase: join_phrase - name: name - type: type - end_year: 1 - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - begin_year: 6 - rels: - youtube: youtube - download for free: download for free - free streaming: free streaming - purchase for download: purchase for download - blog: blog - social network: social network - streaming: streaming - official homepage: official homepage - crowdfunding: crowdfunding - purchase for mail-order: purchase for mail-order - patronage: patronage - lyrics: lyrics - wikidata: wikidata - name: name - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release: - date: date - caa_id: 5 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - name: name - type: type - rels: - - artist_name: artist_name - instrument: instrument - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_name: artist_name - instrument: instrument - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - total_listen_count: 7 - release_color: - red: 5 - green: 5 - blue: 1 - total_user_count: 9 - release_group: - date: date - caa_id: 5 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - name: name - type: type - rels: - - artist_name: artist_name - instrument: instrument - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_name: artist_name - instrument: instrument - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tag: - artist: - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 2 - tag: tag - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 2 - tag: tag - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group: - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 9 - tag: tag - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 9 - tag: tag - properties: - artist: - $ref: "#/components/schemas/topReleaseGroupsForArtist_inner_artist" - release: - $ref: "#/components/schemas/topReleaseGroupsForArtist_inner_release" - release_color: - $ref: "#/components/schemas/topRecordingsForArtist_inner_release_color" - release_group: - $ref: "#/components/schemas/topReleaseGroupsForArtist_inner_release_group" - release_group_mbid: - format: uuid - type: string - tag: - $ref: "#/components/schemas/topReleaseGroupsForArtist_inner_tag" - total_listen_count: - type: integer - total_user_count: - type: integer - type: object - recordingMetadata_artist_artists_inner: - example: - area: area - join_phrase: join_phrase - name: name - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - begin_year: 6 - rels: - youtube: youtube - download for free: download for free - free streaming: free streaming - purchase for download: purchase for download - blog: blog - social network: social network - streaming: streaming - official homepage: official homepage - crowdfunding: crowdfunding - purchase for mail-order: purchase for mail-order - patronage: patronage - lyrics: lyrics - wikidata: wikidata - properties: - area: - type: string - artist_mbid: - format: uuid - type: string - begin_year: - type: integer - join_phrase: - type: string - name: - type: string - rels: - $ref: "#/components/schemas/rels_1" - type: - type: string - type: object - recordingMetadata_artist: - example: - artist_credit_id: 0 - artists: - - area: area - join_phrase: join_phrase - name: name - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - begin_year: 6 - rels: - youtube: youtube - download for free: download for free - free streaming: free streaming - purchase for download: purchase for download - blog: blog - social network: social network - streaming: streaming - official homepage: official homepage - crowdfunding: crowdfunding - purchase for mail-order: purchase for mail-order - patronage: patronage - lyrics: lyrics - wikidata: wikidata - - area: area - join_phrase: join_phrase - name: name - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - begin_year: 6 - rels: - youtube: youtube - download for free: download for free - free streaming: free streaming - purchase for download: purchase for download - blog: blog - social network: social network - streaming: streaming - official homepage: official homepage - crowdfunding: crowdfunding - purchase for mail-order: purchase for mail-order - patronage: patronage - lyrics: lyrics - wikidata: wikidata - name: name - properties: - artist_credit_id: - type: integer - artists: - items: - $ref: "#/components/schemas/recordingMetadata_artist_artists_inner" - type: array - name: - type: string - type: object - recordingMetadata_recording: - example: - isrcs: - - isrcs - - isrcs - first_release_date: 2000-01-23 - length: 1 - name: name - items: items - rels: - - artist_name: artist_name - instrument: instrument - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_name: artist_name - instrument: instrument - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - first_release_date: - format: date - type: string - isrcs: - items: - description: TODO default missing array inner type to string - type: string - type: array - items: - type: string - length: - type: integer - name: - type: string - rels: - items: - $ref: "#/components/schemas/rels" - type: array - type: object - recordingMetadata_tag: - properties: - artist: - items: - $ref: "#/components/schemas/topReleaseGroupsForArtist_inner_tag_artist_inner" - type: array - recording: - items: - $ref: "#/components/schemas/topRecordingsForArtist_inner_tags_inner" - type: array - release_group: - items: - $ref: "#/components/schemas/topRecordingsForArtist_inner_tags_inner" - type: array - type: object - releaseGroupMetadata_release_group: - properties: - caa_id: - type: integer - caa_release_mbid: - format: uuid - type: string - date: - type: string - name: - type: string - rels: - items: - additionalProperties: - type: string - type: object - type: array - type: - type: string - type: object - releaseGroupMetadata_artist_artists_inner: - properties: - area: - type: string - artist_mbid: - format: uuid - type: string - begin_year: - type: integer - end_year: - type: integer - join_phrase: - type: string - name: - type: string - rels: - $ref: "#/components/schemas/rels_1" - type: - type: string - type: object - releaseGroupMetadata_artist: - properties: - artist_credit_id: - type: integer - artists: - items: - $ref: "#/components/schemas/releaseGroupMetadata_artist_artists_inner" - type: array - name: - type: string - type: object - lookup_metadata_release: - example: - caa_id: 5 - mbid: mbid - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - year: 5 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - name: name - album_artist_name: album_artist_name - properties: - album_artist_name: - type: string - caa_id: - type: integer - caa_release_mbid: - format: uuid - type: string - mbid: - type: string - name: - type: string - release_group_mbid: - format: uuid - type: string - year: - type: integer - type: object - lookup_metadata_tag_recording_inner: - example: - count: 2 - tag: tag - properties: - count: - type: integer - tag: - type: string - type: object - lookup_metadata_tag: - example: - artist: - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 2 - tag: tag - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 2 - tag: tag - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording: - - count: 2 - tag: tag - - count: 2 - tag: tag - release_group: - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 9 - tag: tag - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 9 - tag: tag - properties: - artist: - items: - $ref: "#/components/schemas/topReleaseGroupsForArtist_inner_tag_artist_inner" - type: array - recording: - items: - $ref: "#/components/schemas/lookup_metadata_tag_recording_inner" - type: array - release_group: - items: - $ref: "#/components/schemas/topRecordingsForArtist_inner_tags_inner" - type: array - type: object - lookup_metadata: - example: - artist: - artist_credit_id: 0 - artists: - - area: area - join_phrase: join_phrase - name: name - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - begin_year: 6 - rels: - youtube: youtube - download for free: download for free - free streaming: free streaming - purchase for download: purchase for download - blog: blog - social network: social network - streaming: streaming - official homepage: official homepage - crowdfunding: crowdfunding - purchase for mail-order: purchase for mail-order - patronage: patronage - lyrics: lyrics - wikidata: wikidata - - area: area - join_phrase: join_phrase - name: name - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - begin_year: 6 - rels: - youtube: youtube - download for free: download for free - free streaming: free streaming - purchase for download: purchase for download - blog: blog - social network: social network - streaming: streaming - official homepage: official homepage - crowdfunding: crowdfunding - purchase for mail-order: purchase for mail-order - patronage: patronage - lyrics: lyrics - wikidata: wikidata - name: name - release: - caa_id: 5 - mbid: mbid - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - year: 5 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - name: name - album_artist_name: album_artist_name - recording: - isrcs: - - isrcs - - isrcs - first_release_date: 2000-01-23 - length: 1 - name: name - items: items - rels: - - artist_name: artist_name - instrument: instrument - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_name: artist_name - instrument: instrument - type: type - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tag: - artist: - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 2 - tag: tag - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 2 - tag: tag - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording: - - count: 2 - tag: tag - - count: 2 - tag: tag - release_group: - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 9 - tag: tag - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 9 - tag: tag - properties: - artist: - $ref: "#/components/schemas/recordingMetadata_artist" - recording: - $ref: "#/components/schemas/recordingMetadata_recording" - release: - $ref: "#/components/schemas/lookup_metadata_release" - tag: - $ref: "#/components/schemas/lookup_metadata_tag" - type: object - getManualMapping_mapping: - example: - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - user_id: 0 - created: created - properties: - created: - type: string - recording_mbid: - format: uuid - type: string - recording_msid: - format: uuid - type: string - user_id: - type: integer - required: - - created - - recording_mbid - - recording_msid - - user_id - type: object - artistMetadata_tag: - example: - artist: - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 2 - tag: tag - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - genre_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - count: 2 - tag: tag - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - artist: - items: - $ref: "#/components/schemas/topReleaseGroupsForArtist_inner_tag_artist_inner" - type: array - required: - - artist - type: object - feedEvents_payload_events_inner: - example: - metadata: - relationship_type: relationship_type - playing_now: true - listened_at: 2 - listened_at_iso: listened_at_iso - created: 5 - user_name: user_name - blurb_content: blurb_content - message: message - inserted_at: 5 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - event_type: event_type - hidden: true - created: 6 - user_name: user_name - similarity: 7.061401241503109 - id: 1 - message: message - properties: - created: - type: integer - event_type: - type: string - hidden: - type: boolean - id: - type: integer - message: - type: string - metadata: - $ref: "#/components/schemas/metadata" - user_name: - type: string - similarity: - type: number - type: object - feedEvents_payload: - example: - user_id: user_id - count: 0 - events: - - metadata: - relationship_type: relationship_type - playing_now: true - listened_at: 2 - listened_at_iso: listened_at_iso - created: 5 - user_name: user_name - blurb_content: blurb_content - message: message - inserted_at: 5 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - event_type: event_type - hidden: true - created: 6 - user_name: user_name - similarity: 7.061401241503109 - id: 1 - message: message - - metadata: - relationship_type: relationship_type - playing_now: true - listened_at: 2 - listened_at_iso: listened_at_iso - created: 5 - user_name: user_name - blurb_content: blurb_content - message: message - inserted_at: 5 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - event_type: event_type - hidden: true - created: 6 - user_name: user_name - similarity: 7.061401241503109 - id: 1 - message: message - properties: - count: - type: integer - events: - items: - $ref: "#/components/schemas/feedEvents_payload_events_inner" - type: array - user_id: - type: string - type: object - feedEventsListensSimilar_payload_events_inner: - example: - metadata: - relationship_type: relationship_type - playing_now: true - listened_at: 2 - listened_at_iso: listened_at_iso - created: 5 - user_name: user_name - blurb_content: blurb_content - message: message - inserted_at: 5 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - event_type: event_type - hidden: true - created: 6 - similarity: 1 - user_name: user_name - id: id - properties: - id: - type: string - created: - type: integer - event_type: - type: string - hidden: - type: boolean - metadata: - $ref: "#/components/schemas/metadata" - similarity: - type: integer - user_name: - type: string - type: object - feedEventsListensSimilar_payload: - example: - user_id: user_id - count: 0 - events: - - metadata: - relationship_type: relationship_type - playing_now: true - listened_at: 2 - listened_at_iso: listened_at_iso - created: 5 - user_name: user_name - blurb_content: blurb_content - message: message - inserted_at: 5 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - event_type: event_type - hidden: true - created: 6 - similarity: 1 - user_name: user_name - id: id - - metadata: - relationship_type: relationship_type - playing_now: true - listened_at: 2 - listened_at_iso: listened_at_iso - created: 5 - user_name: user_name - blurb_content: blurb_content - message: message - inserted_at: 5 - track_metadata: - release_name: release_name - additional_info: - date: date - artist_names: - - artist_names - - artist_names - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_artist_name: release_artist_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - work_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - duration: 5 - discnumber: 9 - spotify_album_artist_ids: - - spotify_album_artist_ids - - spotify_album_artist_ids - genre: genre - media_player_version: media_player_version - origin_url: origin_url - submission_client: submission_client - trackNumber: trackNumber - spotify_id: spotify_id - spotify_album_id: spotify_album_id - submission_client_version: submission_client_version - isrc: isrc - release_artist_names: - - release_artist_names - - release_artist_names - youtube_id: youtube_id - tags: - - tags - - tags - duration_ms: 2 - spotify_artist_ids: - - spotify_artist_ids - - spotify_artist_ids - music_service_name: music_service_name - recording_msid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - comment: comment - music_service: music_service - track_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - listening_from: listening_from - media_player: media_player - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - tracknumber: 7 - albumartist: albumartist - artist_name: artist_name - mbid_mapping: - recording_name: recording_name - caa_id: 3 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - brainzplayer_metadata: - release_name: release_name - artist_name: artist_name - track_name: track_name - track_name: track_name - event_type: event_type - hidden: true - created: 6 - similarity: 1 - user_name: user_name - id: id - properties: - count: - type: integer - events: - items: - $ref: "#/components/schemas/feedEventsListensSimilar_payload_events_inner" - type: array - user_id: - type: string - required: - - count - - events - - user_id - type: object - recordingRecommendations_payload_mbids_inner: - example: - score: 1 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - latest_listened_at: latest_listened_at - properties: - latest_listened_at: - type: string - recording_mbid: - format: uuid - type: string - score: - type: integer - type: object - recordingRecommendations_payload: - example: - last_updated: 6 - model_url: model_url - offset: 5 - user_name: user_name - count: 0 - model_id: model_id - mbids: - - score: 1 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - latest_listened_at: latest_listened_at - - score: 1 - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - latest_listened_at: latest_listened_at - total_mbid_count: 5 - entity: entity - properties: - count: - type: integer - entity: - type: string - last_updated: - type: integer - mbids: - items: - $ref: "#/components/schemas/recordingRecommendations_payload_mbids_inner" - type: array - model_id: - type: string - model_url: - type: string - offset: - type: integer - total_mbid_count: - type: integer - user_name: - type: string - required: - - count - - entity - - last_updated - - mbids - - model_id - - model_url - - offset - - total_mbid_count - - user_name - type: object - feedbackGivenBy_feedback_inner: - example: - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - created: 6 - rating: rating - properties: - created: - type: integer - rating: - type: string - recording_mbid: - format: uuid - type: string - type: object - freshReleases_payload_releases_inner: - example: - artist_credit_name: artist_credit_name - listen_count: 0 - release_name: release_name - release_date: release_date - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_tags: - - key: release_tags - - key: release_tags - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_primary_type: release_group_primary_type - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - artist_credit_name: - type: string - artist_mbids: - items: - format: uuid - type: string - type: array - listen_count: - type: integer - release_date: - type: string - release_group_mbid: - format: uuid - type: string - release_group_primary_type: - type: string - release_mbid: - format: uuid - type: string - release_name: - type: string - release_tags: - items: - additionalProperties: - type: string - type: object - type: array - type: object - freshReleases_payload: - example: - total_count: 6 - releases: - - artist_credit_name: artist_credit_name - listen_count: 0 - release_name: release_name - release_date: release_date - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_tags: - - key: release_tags - - key: release_tags - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_primary_type: release_group_primary_type - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - listen_count: 0 - release_name: release_name - release_date: release_date - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_tags: - - key: release_tags - - key: release_tags - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_primary_type: release_group_primary_type - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - releases: - items: - $ref: "#/components/schemas/freshReleases_payload_releases_inner" - type: array - total_count: - type: integer - required: - - releases - - total_count - type: object - color_payload_releases_inner_recordings_inner_track_metadata_additional_info: - example: - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - artist_mbids: - items: - format: uuid - type: string - type: array - recording_mbid: - format: uuid - type: string - release_mbid: - format: uuid - type: string - required: - - artist_mbids - - recording_mbid - - release_mbid - type: object - color_payload_releases_inner_recordings_inner_track_metadata: - example: - release_name: release_name - additional_info: - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - track_name: track_name - properties: - additional_info: - $ref: "#/components/schemas/color_payload_releases_inner_recordings_inner_track_metadata_additional_info" - artist_name: - type: string - release_name: - type: string - track_name: - type: string - required: - - additional_info - - artist_name - - release_name - - track_name - type: object - color_payload_releases_inner_recordings_inner: - example: - track_metadata: - release_name: release_name - additional_info: - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - track_name: track_name - properties: - track_metadata: - $ref: "#/components/schemas/color_payload_releases_inner_recordings_inner_track_metadata" - type: object - color_payload_releases_inner: - example: - caa_id: 0 - color: - - 6 - - 6 - release_name: release_name - artist_name: artist_name - recordings: - - track_metadata: - release_name: release_name - additional_info: - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - track_name: track_name - - track_metadata: - release_name: release_name - additional_info: - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - track_name: track_name - dist: 1 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - artist_name: - type: string - caa_id: - type: integer - color: - items: - type: integer - type: array - dist: - type: integer - recordings: - items: - $ref: "#/components/schemas/color_payload_releases_inner_recordings_inner" - type: array - release_mbid: - format: uuid - type: string - release_name: - type: string - type: object - color_payload: - example: - releases: - - caa_id: 0 - color: - - 6 - - 6 - release_name: release_name - artist_name: artist_name - recordings: - - track_metadata: - release_name: release_name - additional_info: - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - track_name: track_name - - track_metadata: - release_name: release_name - additional_info: - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - track_name: track_name - dist: 1 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - caa_id: 0 - color: - - 6 - - 6 - release_name: release_name - artist_name: artist_name - recordings: - - track_metadata: - release_name: release_name - additional_info: - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - track_name: track_name - - track_metadata: - release_name: release_name - additional_info: - recording_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - track_name: track_name - dist: 1 - release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - releases: - items: - $ref: "#/components/schemas/color_payload_releases_inner" - type: array - required: - - releases - type: object - lbRadio_payload: - example: - feedback: - - feedback - - feedback - jspf: - playlist: - annotation: annotation - date: date - duration: 5 - identifier: identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#playlist: - creator: creator - created_for: created_for - public: true - collaborators: - - collaborators - - collaborators - copied_from: copied_from - copied_from_deleted: true - last_modified_at: last_modified_at - additional_metadata: - algorithm_metadata: - source_patch: source_patch - album: album - title: title - track: - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - - duration: 5 - identifier: - - identifier - - identifier - creator: creator - extension: - https://musicbrainz.org/doc/jspf#track: - added_at: added_at - added_by: added_by - release_identifier: release_identifier - artist_identifiers: - - artist_identifiers - - artist_identifiers - additional_metadata: - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - join_phrase: join_phrase - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - album: album - title: title - properties: - feedback: - items: - type: string - type: array - jspf: - $ref: "#/components/schemas/createPlaylist_request" - required: - - feedback - - jspf - type: object - releaseGroups_inner_artists_inner: - example: - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - artist_mbid: - format: uuid - type: string - artist_name: - type: string - artist_credit_name: - type: string - listen_count: - type: integer - join_phrase: - type: string - type: object - releaseGroups_inner: - example: - listen_count: 7 - caa_id: 2 - artists: - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - artist_credit_name: artist_credit_name - listen_count: 5 - join_phrase: join_phrase - artist_name: artist_name - artist_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - artist_name: artist_name - release_group_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - caa_release_mbid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - release_group_name: release_group_name - artist_mbids: - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - - 046b6c7f-0b8a-43b9-b35d-6489e6daee91 - properties: - artists: - items: - $ref: "#/components/schemas/releaseGroups_inner_artists_inner" - type: array - artist_mbids: - items: - format: uuid - type: string - type: array - artist_name: - type: string - caa_id: - type: integer - caa_release_mbid: - format: uuid - type: string - listen_count: - type: integer - release_group_mbid: - format: uuid - type: string - release_group_name: - type: string - type: object - playlistExtensionPayload_additional_metadata_algorithm_metadata: - example: - source_patch: source_patch - properties: - source_patch: - type: string - type: object - playlistExtensionPayload_additional_metadata: - additionalProperties: - type: string - example: - algorithm_metadata: - source_patch: source_patch - properties: - algorithm_metadata: - $ref: "#/components/schemas/playlistExtensionPayload_additional_metadata_algorithm_metadata" - type: object - playlistExtensionPayload: - example: - creator: creator - created_for: created_for - public: true - collaborators: - - collaborators - - collaborators - copied_from: copied_from - copied_from_deleted: true - last_modified_at: last_modified_at - additional_metadata: - algorithm_metadata: - source_patch: source_patch - properties: - created_for: - type: string - creator: - type: string - collaborators: - items: - type: string - type: array - copied_from: - type: string - copied_from_deleted: - type: boolean - public: - type: boolean - last_modified_at: - type: string - additional_metadata: - $ref: "#/components/schemas/playlistExtensionPayload_additional_metadata" - title: playlistExtensionPayload - type: object - securitySchemes: - ApiKeyAuth: - in: header - name: Authorization - type: apiKey diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/BuiltInPluginService.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/BuiltInPluginService.kt index 3d9b19f6..a2be45f6 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/BuiltInPluginService.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/BuiltInPluginService.kt @@ -21,27 +21,20 @@ import app.cash.zipline.ZiplineService import dev.krtirtho.plugin_interfaces.plugin_apis.audio.AudioAPI import dev.krtirtho.plugin_interfaces.plugin_apis.core.CoreAPI import dev.krtirtho.plugin_interfaces.plugin_apis.lyrics.LyricsAPI -import dev.krtirtho.spotube.core.webview.WebViewController -import dev.krtirtho.spotube.core.zipline.host_apis.RealPersistedStorageAPI import dev.krtirtho.spotube.core.zipline.plugin_apis.common.RealCoreAPI import dev.krtirtho.spotube.core.zipline.plugin_apis.lrclib.RealLRCLibLyricsAPI -import dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.createMusicbrainzListenbrainzPluginAPIs import dev.krtirtho.spotube.core.zipline.plugin_apis.newpipe_yt.RealNewPipeAudioAPI import dev.krtirtho.spotube.modules.plugin.LRCLIB_BUILT_IN_PLUGIN -import dev.krtirtho.spotube.modules.plugin.MUSICBRAINZ_LISTENBRAINZ_BUILT_IN_PLUGIN import dev.krtirtho.spotube.modules.plugin.NEWPIPE_YOUTUBE_BUILT_IN_PLUGIN import dev.krtirtho.spotube.modules.plugin.PluginEntry -import io.ktor.client.HttpClient import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.IO import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent -import org.koin.core.component.inject import kotlin.reflect.KClass class BuiltInPluginService( @@ -52,10 +45,10 @@ class BuiltInPluginService( override val loggedInFlow = loggedInStateFlow.asStateFlow() val servicesRegistry = mutableMapOf, ZiplineService>() - val webViewController: WebViewController by inject() +// val webViewController: WebViewController by inject() +// val persistedStorage by lazy { RealPersistedStorageAPI(pluginInfo) } val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) - val persistedStorage by lazy { RealPersistedStorageAPI(pluginInfo) } private fun runLogInFlowObservers() = scope.launch { val coreAPI = servicesRegistry[CoreAPI::class] as CoreAPI? @@ -72,17 +65,6 @@ class BuiltInPluginService( servicesRegistry[AudioAPI::class] = RealNewPipeAudioAPI() } - MUSICBRAINZ_LISTENBRAINZ_BUILT_IN_PLUGIN -> { - servicesRegistry.putAll( - createMusicbrainzListenbrainzPluginAPIs( - scope = scope, - httpClient = HttpClient(), - webViewController = webViewController, - persistedStorage = persistedStorage - ) - ) - } - LRCLIB_BUILT_IN_PLUGIN -> { servicesRegistry[CoreAPI::class] = RealCoreAPI() servicesRegistry[LyricsAPI::class] = RealLRCLibLyricsAPI() diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/EmulatedAlbumArtist.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/EmulatedAlbumArtist.kt deleted file mode 100644 index 1a18bf2b..00000000 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/EmulatedAlbumArtist.kt +++ /dev/null @@ -1,469 +0,0 @@ -/* - * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz - -import arrow.core.Either -import dev.krtirtho.plugin_interfaces.host_apis.PersistedStorageAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.album.MetadataAlbum -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.artist.MetadataArtist -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationStrategy -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationResult -import dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.musicbrainz.MusicbrainzRepository -import dev.krtirtho.spotube.listenbrainz.Api -import dev.krtirtho.spotube.listenbrainz.Auth -import dev.krtirtho.spotube.listenbrainz.api.LbCoreApi -import dev.krtirtho.spotube.listenbrainz.api.LbPlaylistsApi -import dev.krtirtho.spotube.listenbrainz.models.CreatePlaylistRequest -import dev.krtirtho.spotube.listenbrainz.models.ItemDeleteRequest -import dev.krtirtho.spotube.listenbrainz.models.Playlist -import dev.krtirtho.spotube.listenbrainz.models.PlaylistExtension -import dev.krtirtho.spotube.listenbrainz.models.PlaylistExtensionPayload -import dev.krtirtho.spotube.listenbrainz.models.PlaylistTrackInner -import kotlin.uuid.Uuid - -class EmulatedAlbumArtist( - private val musicbrainzRepository: MusicbrainzRepository, - private val persistedStorage: PersistedStorageAPI, -) { - private val playlistCache = mutableMapOf>() - private var cachedUsername: String? = null - - suspend fun savedAlbums( - pagination: PaginationStrategy?, - filterIds: List - ): PaginationResult { - val paging = pagination as? PaginationStrategy.Offset ?: PaginationStrategy.Offset(0, 20) - - val username = requireUsername() - val playlistId = getOrCreatePlaylistId(username) - val tracks = getPlaylistTracks(playlistId, fetchMetadata = true) - val albums = tracks.toAlbums() - - val filtered = if (filterIds.isNotEmpty()) { - albums.filter { filterIds.contains(it.id) } - } else albums - - val slice = filtered.drop(paging.offset).take(paging.limit) - val nextOffset = if (paging.offset + paging.limit < filtered.size) { - paging.offset + paging.limit - } else null - - cacheSavedAlbumIdsFromAlbumsDetailed(albums) - - return PaginationResult( - items = slice, - totalCount = filtered.size, - nextPagination = nextOffset?.let { PaginationStrategy.Offset(it, paging.limit) }, - ) - } - - suspend fun isSavedAlbums(ids: List): List { - val cached = persistedStorage.getString("saved_album_ids")?.takeIf { it.isNotBlank() } - ?.split(",") - ?.filter { it.isNotBlank() } - val savedIds = cached ?: loadSavedAlbumIds() - return ids.map { savedIds.contains(it) } - } - - suspend fun saveAlbums(ids: List) { - if (ids.isEmpty()) return - val alreadySaved = isSavedAlbums(ids) - if (alreadySaved.any { it }) { - throw IllegalStateException("Some albums are already saved") - } - - val username = requireUsername() - val playlistId = getOrCreatePlaylistId(username) - - val recordingIds = ids.mapNotNull { albumId -> - musicbrainzRepository.searchRecordings(query = "rgid:$albumId", limit = 1, offset = 0) - .recordings - .firstOrNull()?.id - } - - if (recordingIds.isEmpty()) return - - val body = Playlist( - track = recordingIds.map { recId -> - PlaylistTrackInner( - identifier = listOf("https://musicbrainz.org/recording/$recId") - ) - } - ) - - val tracks = getPlaylistTracks(playlistId, false) - val offset = tracks.size.toLong() - - LbPlaylistsApi.appendRecordings(Uuid.parse(playlistId), offset, body) - - playlistCache.remove(playlistId) - cacheSavedAlbumIds(loadSavedAlbumIds().plus(ids).distinct()) - } - - suspend fun removeSavedAlbums(ids: List) { - if (ids.isEmpty()) return - val username = requireUsername() - val playlistId = getOrCreatePlaylistId(username) - val tracks = getPlaylistTracks(playlistId, fetchMetadata = true) - - val releaseIds = tracks.mapNotNull { it.extractReleaseId() } - val releaseGroups = musicbrainzRepository.searchReleases( - query = releaseIds.joinToString(" OR ") { "reid:$it" }, - limit = releaseIds.size, - offset = 0 - ).releases - - val releaseIdToGroup = releaseGroups.associate { release -> - val releaseId = release.id - val groupId = release.releaseGroup?.id ?: release.id - releaseId to groupId - } - - val indexes = tracks.mapIndexedNotNull { idx, track -> - val releaseId = track.extractReleaseId() - val groupId = releaseIdToGroup[releaseId] - if (groupId != null && ids.contains(groupId)) idx else null - } - - if (indexes.isEmpty()) return - - indexes.forEach { index -> - LbPlaylistsApi.itemDelete( - Uuid.parse(playlistId), - ItemDeleteRequest( - index = index.toLong(), - count = 1 - ) - ) - } - - playlistCache.remove(playlistId) - cacheSavedAlbumIds(loadSavedAlbumIds().filterNot { ids.contains(it) }) - } - - private suspend fun loadSavedAlbumIds(): List { - val username = requireUsername() - val playlistId = getOrCreatePlaylistId(username) - val tracks = getPlaylistTracks(playlistId, fetchMetadata = true) - val releaseIds = tracks.mapNotNull { it.extractReleaseId() } - if (releaseIds.isEmpty()) return emptyList() - - val releases = musicbrainzRepository.searchReleases( - query = releaseIds.joinToString(" OR ") { "reid:$it" }, - limit = releaseIds.size, - offset = 0 - ).releases - - val albumIds = releases.map { it.releaseGroup?.id ?: it.id } - cacheSavedAlbumIds(albumIds) - return albumIds - } - - private suspend fun getOrCreatePlaylistId(username: String, type: String = "album"): String { - val key = "saved_${type}_playlist_id" - persistedStorage.getString(key)?.takeIf { it.isNotBlank() }?.let { return it } - - val playlistName = "$username saved ${type}s by Spotube" - val existing = searchPlaylist(username, playlistName) - val playlistId = existing ?: createPlaylist(playlistName, type) - persistedStorage.putString(key, playlistId) - return playlistId - } - - private suspend fun searchPlaylist(username: String, name: String): String? { - val response = LbCoreApi.searchPlaylistForUser( - playlistUserName = username, - query = name, - count = 1, - offset = 0 - ) - - val playlists = response.getOrNull()?.data?.playlists - val match = playlists?.firstOrNull { element -> - val playlist = element.playlist - val creator = playlist?.creator - val title = playlist?.title - creator == username && title == name - } - - return match?.playlist?.identifier?.substringAfterLast('/') - } - - private suspend fun createPlaylist(name: String, type: String): String { - val body = CreatePlaylistRequest( - playlist = Playlist( - title = name, - annotation = "This playlist contains all ${type}s saved by Spotube. Autogenerated. Do not edit.", - extension = PlaylistExtension( - httpsMusicbrainzOrgDocJspfPlaylist = PlaylistExtensionPayload( - collaborators = emptyList(), - public = false - ) - ) - ) - ) - - val response = LbPlaylistsApi.createPlaylist(body) - - val bodyJson = response.getOrNull()?.data - return bodyJson?.playlistMbid?.toString() - ?: throw IllegalStateException("Unable to create playlist") - } - - private suspend fun getPlaylistTracks( - playlistId: String, - fetchMetadata: Boolean - ): List { - playlistCache[playlistId]?.let { return it } - - val response = LbPlaylistsApi.fetchPlaylist( - playlistMbid = Uuid.parse(playlistId), - fetchMetadata = fetchMetadata - ) - - val tracks = response.getOrNull()?.data?.playlist?.track - ?: emptyList() - - playlistCache[playlistId] = tracks - return tracks - } - - private suspend fun requireUsername(): String { - cachedUsername?.let { return it } - - // Setup auth provider globally - val auth = Auth.ApiKeyAuth { - val token = - persistedStorage.getString("listenbrainz_auth_token") ?: return@ApiKeyAuth null - if (token.startsWith("Token ", ignoreCase = true)) token else "Token $token" - } - Api.setAuthProvider(auth) - - val username = when (val res = LbCoreApi.validateToken()) { - is Either.Left -> throw IllegalStateException("Unable to resolve ListenBrainz username: ${res.value}") - is Either.Right -> res.value.data.userName - } - cachedUsername = username!! - return username - } - - private fun PlaylistTrackInner.extractReleaseId(): String? { - val extension = this.extension?.httpsMusicbrainzOrgDocJspfTrack - ?: return null - val additional = extension.additionalMetadata ?: return null - return additional.caaReleaseMbid?.toString() - } - - private suspend fun List.toAlbums(): List { - val releaseIds = mapNotNull { it.extractReleaseId() } - if (releaseIds.isEmpty()) return emptyList() - - val releases = try { - musicbrainzRepository.searchReleases( - query = releaseIds.joinToString(" OR ") { "reid:$it" }, - limit = releaseIds.size, - offset = 0 - ).releases - } catch (_: Throwable) { - emptyList() - } - - return releases - .groupBy { it.releaseGroup?.id ?: it.id } - .values - .mapNotNull { group -> - val release = group.firstOrNull() ?: return@mapNotNull null - val releaseGroupId = release.releaseGroup?.id ?: release.id - release.toMetadataAlbumDetailed(releaseGroupId) - } - } - - private suspend fun cacheSavedAlbumIdsFromAlbumsDetailed(albums: List) { - cacheSavedAlbumIds(albums.map { it.id }) - } - - private suspend fun cacheSavedAlbumIds(ids: List) { - persistedStorage.putString("saved_album_ids", ids.distinct().joinToString(",")) - } - - suspend fun savedArtists( - pagination: PaginationStrategy, - filterIds: List - ): PaginationResult { - val paging = pagination as? PaginationStrategy.Offset ?: PaginationStrategy.Offset(0, 20) - val username = requireUsername() - val playlistId = getOrCreatePlaylistId(username, "artist") - val tracks = getPlaylistTracks(playlistId, fetchMetadata = true) - val artists = tracks.toArtists() - - val filtered = if (filterIds.isNotEmpty()) { - artists.filter { filterIds.contains(it.id) } - } else artists - - val slice = filtered.drop(paging.offset).take(paging.limit) - val nextOffset = if (paging.offset + paging.limit < filtered.size) { - paging.offset + paging.limit - } else null - - cacheSavedArtistIdsFromArtistsDetailed(artists) - - return PaginationResult( - items = slice, - totalCount = filtered.size, - nextPagination = nextOffset?.let { PaginationStrategy.Offset(it, paging.limit) }, - ) - } - - suspend fun isSavedArtists(ids: List): List { - val cached = persistedStorage.getString("saved_artist_ids")?.takeIf { it.isNotBlank() } - ?.split(",") - ?.filter { it.isNotBlank() } - val savedIds = cached ?: loadSavedArtistIds() - return ids.map { savedIds.contains(it) } - } - - suspend fun saveArtists(ids: List) { - if (ids.isEmpty()) return - val alreadySaved = isSavedArtists(ids) - if (alreadySaved.any { it }) { - throw IllegalStateException("Some artists are already saved") - } - - val username = requireUsername() - val playlistId = getOrCreatePlaylistId(username, "artist") - - val recordingIds = ids.mapNotNull { artistId -> - musicbrainzRepository.searchRecordings(query = "arid:$artistId", limit = 1, offset = 0) - .recordings - .firstOrNull()?.id - } - - if (recordingIds.isEmpty()) return - - val body = Playlist( - track = recordingIds.map { recId -> - PlaylistTrackInner( - identifier = listOf("https://musicbrainz.org/recording/$recId") - ) - } - ) - - val tracks = getPlaylistTracks(playlistId, false) - val offset = tracks.size.toLong() - - LbPlaylistsApi.appendRecordings(Uuid.parse(playlistId), offset, body) - - playlistCache.remove(playlistId) - cacheSavedArtistIds(loadSavedArtistIds().plus(ids).distinct()) - } - - suspend fun removeSavedArtists(ids: List) { - if (ids.isEmpty()) return - val username = requireUsername() - val playlistId = getOrCreatePlaylistId(username, "artist") - val tracks = getPlaylistTracks(playlistId, fetchMetadata = true) - - val trackArtistPairs = tracks.mapIndexedNotNull { index, track -> - track.extractArtistId(musicbrainzRepository)?.let { artistId -> - index to artistId - } - } - - val indexes = trackArtistPairs.filter { (_, artistId) -> ids.contains(artistId) } - .map { it.first } - - if (indexes.isEmpty()) return - - indexes.forEach { index -> - LbPlaylistsApi.itemDelete( - Uuid.parse(playlistId), - ItemDeleteRequest( - index = index.toLong(), - count = 1 - ) - ) - } - - playlistCache.remove(playlistId) - cacheSavedArtistIds(loadSavedArtistIds().filterNot { ids.contains(it) }) - } - - private suspend fun loadSavedArtistIds(): List { - val username = requireUsername() - val playlistId = getOrCreatePlaylistId(username, "artist") - val tracks = getPlaylistTracks(playlistId, fetchMetadata = true) - - val artists = tracks.toArtists() - val ids = artists.map { it.id } - cacheSavedArtistIds(ids) - return ids - } - - private suspend fun cacheSavedArtistIdsFromArtistsDetailed(artists: List) { - cacheSavedArtistIds(artists.map { it.id }) - } - - private suspend fun cacheSavedArtistIds(ids: List) { - persistedStorage.putString("saved_artist_ids", ids.distinct().joinToString(",")) - } - - private suspend fun PlaylistTrackInner.extractArtistId(repository: MusicbrainzRepository): String? { - val idUrl = identifier?.firstOrNull() ?: return null - val recordingId = idUrl.substringAfterLast("/") - if (recordingId.isBlank()) return null - - return try { - val recording = - repository.getRecordingByMbid(recordingId, includes = listOf("artist-credits")) - recording.artistCredit.firstOrNull()?.artist?.id - } catch (_: Exception) { - null - } - } - - private suspend fun List.toArtists(): List { - val recordingIds = mapNotNull { - val idUrl = it.identifier?.firstOrNull() ?: return@mapNotNull null - idUrl.substringAfterLast("/").takeIf { it.isNotBlank() } - } - - if (recordingIds.isEmpty()) return emptyList() - - val chunks = recordingIds.chunked(20) - val artists = mutableListOf() - - chunks.forEach { chunk -> - try { - val query = chunk.joinToString(" OR ") { "rid:$it" } - val response = - musicbrainzRepository.searchRecordings(query, limit = chunk.size, offset = 0) - - response.recordings.forEach { rec -> - val artist = rec.artistCredit.firstOrNull()?.artist - if (artist != null) { - artists.add(artist.toMetadataArtistDetailed()) - } - } - } catch (_: Exception) { - // ignore - } - } - - return artists - } -} diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/MusicbrainzListenbrainzEnrich.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/MusicbrainzListenbrainzEnrich.kt deleted file mode 100644 index 8455b03c..00000000 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/MusicbrainzListenbrainzEnrich.kt +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz - -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.album.MetadataAlbum -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.album.MetadataAlbumType -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.artist.MetadataArtist -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.Thumbnail -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.track.MetadataTrack -import dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.musicbrainz.MusicbrainzArtist -import dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.musicbrainz.MusicbrainzRecording -import dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.musicbrainz.MusicbrainzRelease - -fun MusicbrainzRelease.toMetadataAlbum(groupId: String): MetadataAlbum.Detailed { - val releaseGroupId = releaseGroup?.id ?: groupId - val type = releaseGroup?.primaryType?.lowercase()?.let { - when (it) { - "single" -> MetadataAlbumType.Single - "album" -> MetadataAlbumType.Album - else -> MetadataAlbumType.Collection - } - } ?: MetadataAlbumType.Collection - - return MetadataAlbum.Detailed( - releaseDate = date, - genres = emptyList(), - trackCount = trackCount ?: 0, - id = releaseGroupId, - title = releaseGroup?.title ?: title, - description = null, - thumbnails = listOf( - Thumbnail( - url = "https://coverartarchive.org/release-group/${releaseGroupId}/front-250.jpg", - width = 250, - height = 250 - ), - Thumbnail( - url = "https://coverartarchive.org/release-group/${releaseGroupId}/front-500.jpg", - width = 500, - height = 500 - ), - ), - albumType = type, - artists = artistCredit.mapNotNull { credit -> - credit.artist?.let { - MetadataArtist.Basic( - id = it.id, - name = it.name, - thumbnails = emptyList(), - externalUri = "https://musicbrainz.org/artist/${it.id}" - ) - } - }, - externalUri = "https://musicbrainz.org/release-group/${releaseGroupId}" - ) -} - -fun List.pickOfficialRelease(): MusicbrainzRelease? { - return firstOrNull { release -> - release.status == "Official" && release.country == "US" && - release.artistCredit.none { it.artist?.name == "Various Artists" } - } -} - -fun MusicbrainzRecording.toMetadataTrack(album: MetadataAlbum.Detailed): MetadataTrack { - val explicit = disambiguation?.lowercase() == "explicit" - return MetadataTrack( - id = id, - title = title, - durationMs = (length ?: 0).toLong(), - trackNumber = null, - discNumber = null, - artists = artistCredit.mapNotNull { credit -> - credit.artist?.let { - MetadataArtist.Basic( - id = it.id, - name = it.name, - thumbnails = emptyList(), - externalUri = "https://musicbrainz.org/artist/${it.id}" - ) - } - }, - album = album, - explicit = explicit, - popularity = null, - isrcCode = isrcs.firstOrNull(), - externalUri = "https://musicbrainz.org/recording/${id}", - thumbnails = null, - ) -} - -fun MusicbrainzRelease.toMetadataAlbumDetailed(groupId: String): MetadataAlbum.Detailed { - val releaseGroupId = releaseGroup?.id ?: groupId - val type = releaseGroup?.primaryType?.lowercase()?.let { - when (it) { - "single" -> MetadataAlbumType.Single - "album" -> MetadataAlbumType.Album - else -> MetadataAlbumType.Collection - } - } ?: MetadataAlbumType.Collection - - return MetadataAlbum.Detailed( - releaseDate = date, - genres = emptyList(), - trackCount = trackCount ?: 0, - id = releaseGroupId, - title = releaseGroup?.title ?: title, - description = null, - thumbnails = listOf( - Thumbnail( - url = "https://coverartarchive.org/release-group/${releaseGroupId}/front-250.jpg", - width = 250, - height = 250 - ), - Thumbnail( - url = "https://coverartarchive.org/release-group/${releaseGroupId}/front-500.jpg", - width = 500, - height = 500 - ), - ), - albumType = type, - artists = artistCredit.mapNotNull { credit -> - credit.artist?.let { - MetadataArtist.Basic( - id = it.id, - name = it.name, - thumbnails = emptyList(), - externalUri = "https://musicbrainz.org/artist/${it.id}" - ) - } - }, - externalUri = "https://musicbrainz.org/release-group/${releaseGroupId}" - ) -} - -fun MusicbrainzArtist.toMetadataArtistDetailed(): MetadataArtist.Detailed { - return MetadataArtist.Detailed( - id = id, - name = name, - thumbnails = emptyList(), - externalUri = "https://musicbrainz.org/artist/$id", - genres = tags.map { it.name }, - biography = disambiguation, - followersCount = null - ) -} - -fun MusicbrainzArtist.toMetadataArtistBasic(): MetadataArtist.Basic { - return MetadataArtist.Basic( - id = id, - name = name, - thumbnails = emptyList(), - externalUri = "https://musicbrainz.org/artist/$id" - ) -} - diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/MusicbrainzListenbrainzPlugin.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/MusicbrainzListenbrainzPlugin.kt deleted file mode 100644 index 9fcf6c6e..00000000 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/MusicbrainzListenbrainzPlugin.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz - -import app.cash.zipline.ZiplineService -import dev.krtirtho.plugin_interfaces.host_apis.PersistedStorageAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.core.CoreAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.album.MetadataAlbumAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.artist.MetadataArtistAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.playlist.MetadataPlaylistAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.search.MetadataSearchAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.track.MetadataTrackAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.user.MetadataUserAPI -import dev.krtirtho.spotube.core.webview.WebViewController -import dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.musicbrainz.KtorMusicbrainzRepository -import dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.musicbrainz.MusicbrainzArtistEnricher -import dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.wikidata.WikidataRepository -import io.ktor.client.HttpClient -import kotlinx.coroutines.CoroutineScope -import kotlin.reflect.KClass - -fun createMusicbrainzListenbrainzPluginAPIs( - scope: CoroutineScope, - httpClient: HttpClient, - webViewController: WebViewController, - persistedStorage: PersistedStorageAPI -): Map, ZiplineService> { - val repository = KtorMusicbrainzRepository(httpClient) - val wikidataRepository = WikidataRepository(httpClient) - val artistEnricher = MusicbrainzArtistEnricher(repository, wikidataRepository) - - return mapOf( - CoreAPI::class to RealMusicbrainzListenbrainzCoreAPI(scope, webViewController, persistedStorage), - MetadataBrowseAPI::class to RealMusicbrainzListenbrainzMetadataBrowseAPI(persistedStorage), - MetadataPlaylistAPI::class to RealMusicbrainzListenbrainzMetadataPlaylistAPI(repository, persistedStorage), - MetadataTrackAPI::class to RealMusicbrainzListenbrainzMetadataTrackAPI(repository, persistedStorage), - MetadataAlbumAPI::class to RealMusicbrainzListenbrainzMetadataAlbumAPI(repository, persistedStorage), - MetadataArtistAPI::class to RealMusicbrainzListenbrainzMetadataArtistAPI(repository, persistedStorage), - MetadataSearchAPI::class to RealMusicbrainzListenbrainzMetadataSearchAPI(repository, artistEnricher, httpClient), - MetadataUserAPI::class to RealMusicbrainsListenbrainzMetadataUserAPI(repository, persistedStorage) - ) -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainsListenbrainzMetadataUserAPI.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainsListenbrainzMetadataUserAPI.kt deleted file mode 100644 index f4715d2e..00000000 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainsListenbrainzMetadataUserAPI.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz - -import arrow.core.Either -import com.kroegerama.openapi.kmp.gen.companion.appendSerializedQueryParameter -import dev.krtirtho.plugin_interfaces.host_apis.PersistedStorageAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.user.MetadataUser -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.user.MetadataUserAPI -import dev.krtirtho.spotube.core.zipline.host_apis.RealPersistedStorageAPI -import dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.musicbrainz.MusicbrainzRepository -import dev.krtirtho.spotube.listenbrainz.Auth -import dev.krtirtho.spotube.listenbrainz.api.LbCoreApi - -class RealMusicbrainsListenbrainzMetadataUserAPI( - private val musicbrainzRepository: MusicbrainzRepository, - private val persistedStorage: PersistedStorageAPI -) : MetadataUserAPI { - override suspend fun getUser(id: String): MetadataUser? { - val token = persistedStorage.getString("listenbrainz_auth_token") ?: return null - if(token.isEmpty()) return null - when (val res = LbCoreApi.validateToken { - appendSerializedQueryParameter("token", token) - }) { - is Either.Left -> { - println("Error validating token: ${res.value}") - return null - } - - is Either.Right -> { - val user = res.value.data - return MetadataUser( - id = user.userName as String, - username = user.userName, - displayName = user.userName, - thumbnails = emptyList(), - externalUri = "https://listenbrainz.org/user/${user.userName}", - ) - } - } - } -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzCoreAPI.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzCoreAPI.kt deleted file mode 100644 index db6914de..00000000 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzCoreAPI.kt +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz - -import arrow.core.Either -import com.kroegerama.openapi.kmp.gen.companion.appendSerializedQueryParameter -import dev.krtirtho.plugin_interfaces.host_apis.PersistedStorageAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.core.CoreAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.core.PluginUpdateInfo -import dev.krtirtho.spotube.core.webview.WebViewController -import dev.krtirtho.spotube.listenbrainz.Api -import dev.krtirtho.spotube.listenbrainz.Auth -import dev.krtirtho.spotube.listenbrainz.api.LbCoreApi -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch -import net.swiftzer.semver.SemVer - -class RealMusicbrainzListenbrainzCoreAPI( - private val scope: CoroutineScope, - private val webViewController: WebViewController, - private val persistedStorage: PersistedStorageAPI -) : CoreAPI { - init { - scope.launch { - val token = persistedStorage.getString("listenbrainz_auth_token") - if (token != null) { - auth = Auth.ApiKeyAuth { token } - Api.setAuthProvider(auth!!) - loggedInState.value = true - } else { - loggedInState.value = false - } - } - } - - override suspend fun checkPluginUpdates(currentVersion: SemVer): PluginUpdateInfo? { - return null - } - - override fun supportMarkdownText(currentVersion: SemVer): String { - return "Keep supporting Spotube!" - } - - override val requiresAuthentication = true - - private val loggedInState = MutableStateFlow(false) - override val loggedInFlow = loggedInState.asStateFlow() - - private var auth: Auth? = null - - override suspend fun login() { - webViewController.navigateToHTML( - """ - - - - Login to Listenbrainz - - -

Login to Listenbrainz

-
- - -
- Mirror: - - - - """.trimIndent() - ) - - println("[RealMusicbrainzListenbrainzCoreAPI.login] Waiting for postMessagesFlow message") - val actualCreds = webViewController.postMessagesFlow - .onEach { - println("[postMessagesFlow.onEach] Received token from WebView: $it") - } - .filter { it.isNotBlank() } - .first() - println("Received token from WebView: $actualCreds") - auth = Auth.ApiKeyAuth { - actualCreds - } - Api.setAuthProvider(auth = auth!!) - when (val res = LbCoreApi.validateToken { - appendSerializedQueryParameter("token", actualCreds) - }) { - is Either.Left -> { - webViewController.closeWebview() - throw res.value - } - - is Either.Right -> { - if (!res.value.data.valid) { - webViewController.closeWebview() - throw Exception("Invalid token") - } - persistedStorage.putString("listenbrainz_auth_token", actualCreds) - loggedInState.value = true - webViewController.closeWebview() - } - } - } - - override suspend fun logout() { - if (auth == null) return - Api.clearAuthProvider(auth!!) - auth = null - persistedStorage.remove("listenbrainz_auth_token") - loggedInState.value = false - } -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzMetadataAlbumAPI.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzMetadataAlbumAPI.kt deleted file mode 100644 index 2c0711a5..00000000 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzMetadataAlbumAPI.kt +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz - -import dev.krtirtho.plugin_interfaces.host_apis.PersistedStorageAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.album.MetadataAlbum -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.album.MetadataAlbumAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationStrategy -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationResult -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.track.MetadataTrack -import dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.musicbrainz.MusicbrainzRepository - - -class RealMusicbrainzListenbrainzMetadataAlbumAPI( - private val musicbrainzRepository: MusicbrainzRepository, - private val persistedStorage: PersistedStorageAPI -) : MetadataAlbumAPI { - private val emulator by lazy { - EmulatedAlbumArtist(musicbrainzRepository, persistedStorage) - } - - override suspend fun getAlbum(id: String): MetadataAlbum.Detailed { - val release = musicbrainzRepository - .searchReleases(query = "rgid:$id", limit = 1, offset = 0) - .releases - .firstOrNull() - ?: throw IllegalArgumentException("Album $id not found") - - return release.toMetadataAlbumDetailed(id) - } - - override suspend fun getTrackAlbum(track: MetadataTrack): MetadataAlbum.Detailed { - TODO("Not yet implemented") - } - - override suspend fun getAlbumTracks( - id: String, - pagination: PaginationStrategy? - ): PaginationResult { - val paging: PaginationStrategy.Offset = - pagination as? PaginationStrategy.Offset ?: PaginationStrategy.Offset(0, 20) - - val releases = musicbrainzRepository.searchReleases( - query = "rgid:$id", - limit = 10, - offset = 0 - ).releases - - val officialRelease = releases.pickOfficialRelease() - ?: releases.firstOrNull() - ?: throw IllegalArgumentException("Album $id not found") - - val recordings = musicbrainzRepository.searchRecordings( - query = "reid:${officialRelease.id}", - limit = paging.limit, - offset = paging.offset, - ) - - val album = officialRelease.toMetadataAlbumDetailed(id) - val items = recordings.recordings.map { it.toMetadataTrack(album) } - - val nextOffset = if (paging.offset + paging.limit < recordings.count) { - paging.offset + paging.limit - } else null - - return PaginationResult( - items = items, - totalCount = recordings.count, - nextPagination = nextOffset?.let { PaginationStrategy.Offset(it, paging.limit) } - ) - } - - override suspend fun savedAlbums( - pagination: PaginationStrategy? - ): PaginationResult { - val paging = pagination as? PaginationStrategy.Offset ?: PaginationStrategy.Offset(0, 20) - return emulator.savedAlbums(paging, emptyList()) - } - - override suspend fun isSavedAlbums(ids: List): List { - return emulator.isSavedAlbums(ids) - } - - override suspend fun saveAlbums(ids: List) { - emulator.saveAlbums(ids) - } - - override suspend fun removeSavedAlbums(ids: List) { - emulator.removeSavedAlbums(ids) - } -} - diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzMetadataArtistAPI.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzMetadataArtistAPI.kt deleted file mode 100644 index 3650cf0e..00000000 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzMetadataArtistAPI.kt +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz - - -import dev.krtirtho.plugin_interfaces.host_apis.PersistedStorageAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.album.MetadataAlbum -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.artist.MetadataArtist -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.artist.MetadataArtistAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationStrategy -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationResult -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.track.MetadataTrack -import dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.musicbrainz.MusicbrainzRepository - -class RealMusicbrainzListenbrainzMetadataArtistAPI( - private val musicbrainzRepository: MusicbrainzRepository, - private val persistedStorage: PersistedStorageAPI -) : MetadataArtistAPI { - - private val emulator by lazy { - EmulatedAlbumArtist(musicbrainzRepository, persistedStorage) - } - - override suspend fun getArtist(id: String): MetadataArtist.Detailed { - val artist = musicbrainzRepository.getArtistByMbid(id, includes = listOf("url-rels")) - return artist.toMetadataArtistDetailed() - } - - override suspend fun getArtistTop10Tracks(id: String): List { - val recordings = musicbrainzRepository.searchRecordings( - query = "arid:$id", - limit = 10, - offset = 0 - ).recordings - - return recordings.mapNotNull { recording -> - val release = recording.releases.pickOfficialRelease() - ?: recording.releases.firstOrNull() - - release?.let { - val groupId = it.releaseGroup?.id ?: it.id - val album = it.toMetadataAlbumDetailed(groupId) - recording.toMetadataTrack(album) - } - } - } - - override suspend fun getArtistAlbums( - id: String, - pagination: PaginationStrategy? - ): PaginationResult { - val paging = pagination as? PaginationStrategy.Offset ?: PaginationStrategy.Offset(0, 20) - val releases = musicbrainzRepository.searchReleases( - query = "arid:$id", - limit = paging.limit, - offset = paging.offset - ) - - val items = releases.releases.map { release -> - val groupId = release.releaseGroup?.id ?: release.id - release.toMetadataAlbumDetailed(groupId) - } - - val nextOffset = if (paging.offset + paging.limit < releases.count) { - paging.offset + paging.limit - } else null - - return PaginationResult( - items = items, - totalCount = releases.count, - nextPagination = nextOffset?.let { PaginationStrategy.Offset(it, paging.limit) } - ) - } - - override suspend fun savedArtists( - pagination: PaginationStrategy? - ): PaginationResult { - val paging = pagination as? PaginationStrategy.Offset ?: PaginationStrategy.Offset(0, 20) - return emulator.savedArtists(paging, emptyList()) - } - - override suspend fun isSavedArtists(ids: List): List { - return emulator.isSavedArtists(ids) - } - - override suspend fun saveArtists(ids: List) { - emulator.saveArtists(ids) - } - - override suspend fun removeSavedArtists(ids: List) { - emulator.removeSavedArtists(ids) - } -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzMetadataBrowseAPI.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzMetadataBrowseAPI.kt deleted file mode 100644 index d8910d38..00000000 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzMetadataBrowseAPI.kt +++ /dev/null @@ -1,566 +0,0 @@ -/* - * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz - -import arrow.core.Either -import com.kroegerama.openapi.kmp.gen.companion.AuthPlugin.Plugin.authKeys -import dev.krtirtho.plugin_interfaces.host_apis.PersistedStorageAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseItem -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse.MetadataBrowseSection -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationStrategy -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationResult -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.Thumbnail -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.playlist.MetadataPlaylist -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.user.MetadataUser -import dev.krtirtho.spotube.core.di.injectLogger -import dev.krtirtho.spotube.listenbrainz.Api -import dev.krtirtho.spotube.listenbrainz.Auth -import dev.krtirtho.spotube.listenbrainz.api.LbCoreApi -import dev.krtirtho.spotube.listenbrainz.api.LbMiscApi -import dev.krtirtho.spotube.listenbrainz.api.LbPlaylistsApi -import dev.krtirtho.spotube.listenbrainz.api.LbStatsApi -import dev.krtirtho.spotube.listenbrainz.models.AllowedStatisticsRange -import dev.krtirtho.spotube.listenbrainz.models.Mode -import dev.krtirtho.spotube.listenbrainz.models.Playlist -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.Json -import kotlin.time.Clock -import org.koin.core.component.KoinComponent - -class RealMusicbrainzListenbrainzMetadataBrowseAPI( - private val persistedStorage: PersistedStorageAPI, -) : MetadataBrowseAPI, KoinComponent { - private val logger by injectLogger() - private var cachedUsername: String? = null - - @Serializable - private data class LbRadioPlaylistCacheEntry( - val cachedAtEpochMs: Long, - val playlist: MetadataPlaylist, - ) - - @Serializable - private data class BrowseSectionsCacheEntry( - val cachedAtEpochMs: Long, - val sections: List, - ) - - private val cacheJson = Json { - ignoreUnknownKeys = true - encodeDefaults = true - } - - @Serializable - private data class BrowseSectionData( - val id: String, - val title: String, - val description: String? = null, - val moreLink: String? = null, - val items: List, - ) - - private data class MoodSeed( - val key: String, - val tag: String, - val title: String, - val annotation: String, - ) - - companion object { - private const val SECTION_TOP_ARTIST_RADIOS = "top-artist-radios" - private const val SECTION_MOOD_PLAYLISTS = "mood-playlists" - private const val SECTION_CREATED_FOR = "created-for-playlists" - private const val LB_RADIO_CACHE_TTL_MS = 3L * 24 * 60 * 60 * 1000 - private const val LB_RADIO_CACHE_KEY_PREFIX = "lb_radio_playlist_cache" - private const val BROWSE_SECTIONS_CACHE_KEY_PREFIX = "lb_browse_sections_cache" - - private val moodSeeds = listOf( - MoodSeed("chill", "ambient", "Chill playlist", "Yo chill my friend!"), - MoodSeed("energetic", "energetic", "Pump it up!", "Get ready to move!"), - MoodSeed("happy", "upbeat", "Happy Vibes", "Feel good tunes to brighten your day!"), - MoodSeed("sad", "melancholy", "Melancholy Moments", "For those reflective times."), - MoodSeed("focus", "instrumental", "Focus Beats", "Concentration is key."), - MoodSeed("workout", "electronic", "Workout Jams", "Get pumped with these beats!"), - MoodSeed("party", "dance", "Party Anthems", "Let's get this party started!"), - MoodSeed("romantic", "romantic", "Romantic Evenings", "For those special moments."), - ) - } - - override suspend fun featured(): List { - logger.i("featured(): Starting to build featured items") - try { - val sections = buildSections() - logger.d("featured(): Built ${sections.size} sections") - val result = sections.flatMap { it.items }.take(12) - logger.i("featured(): Returning ${result.size} featured items") - return result - } catch (e: Exception) { - logger.e(e) { "featured(): Error building featured items" } - return emptyList() - } - } - - override suspend fun list(pagination: PaginationStrategy?): PaginationResult { - val paging = pagination as? PaginationStrategy.Offset ?: PaginationStrategy.Offset(0, 20) - logger.i("list(): Starting with offset=${paging.offset}, pageSize=${paging.limit}") - try { - val sections = buildSections() - logger.d("list(): Built ${sections.size} total sections") - val items = sections - .drop(paging.offset) - .take(paging.limit) - .map { - MetadataBrowseSection( - title = it.title, - description = it.description, - items = it.items, - moreLink = it.moreLink, - ) - } - - val nextOffset = if (paging.offset + items.size < sections.size) { - paging.offset + paging.limit - } else { - null - } - - logger.d("list(): Returning ${items.size} items, nextOffset=$nextOffset") - return PaginationResult( - items = items, - totalCount = sections.size, - nextPagination = nextOffset?.let { PaginationStrategy.Offset(it, paging.limit) }, - ) - } catch (e: Exception) { - logger.e(e) { "list(): Error fetching paginated sections" } - return PaginationResult( - items = emptyList(), - totalCount = 0, - nextPagination = null, - ) - } - } - - override suspend fun sublist( - sectionId: String, - pagination: PaginationStrategy? - ): PaginationResult { - val paging = pagination as? PaginationStrategy.Offset ?: PaginationStrategy.Offset(0, 20) - logger.i("sublist(): Fetching section=$sectionId with offset=${paging.offset}, pageSize=${paging.limit}") - try { - val allSections = buildSections() - logger.d("sublist(): Built ${allSections.size} total sections") - val sectionItems = allSections.firstOrNull { it.id == sectionId }?.items ?: emptyList() - logger.d("sublist(): Found ${sectionItems.size} items in section $sectionId") - - val items = sectionItems.drop(paging.offset).take(paging.limit) - val nextOffset = if (paging.offset + items.size < sectionItems.size) { - paging.offset + paging.limit - } else { - null - } - - logger.d("sublist(): Returning ${items.size} paginated items, nextOffset=$nextOffset") - return PaginationResult( - items = items, - totalCount = sectionItems.size, - nextPagination = nextOffset?.let { PaginationStrategy.Offset(it, paging.limit) }, - ) - } catch (e: Exception) { - logger.e(e) { "sublist(): Error fetching sublist for section=$sectionId" } - return PaginationResult( - items = emptyList(), - totalCount = 0, - nextPagination = null, - ) - } - } - - private suspend fun buildSections(): List { - logger.d("buildSections(): Starting section building process") - val username = try { - val user = requireUsername() - logger.d("buildSections(): Successfully resolved username: $user") - user - } catch (e: Exception) { - logger.w(e) { "buildSections(): Failed to retrieve username, returning empty sections" } - return emptyList() - } - - val nowEpochMs = Clock.System.now().toEpochMilliseconds() - val sectionsCacheKey = buildBrowseSectionsCacheKey(username) - val cachedSections = readCachedBrowseSections(sectionsCacheKey) - if (cachedSections != null) { - val ageMs = nowEpochMs - cachedSections.cachedAtEpochMs - if (ageMs <= LB_RADIO_CACHE_TTL_MS) { - logger.d("buildSections(): Returning cached sections for key=$sectionsCacheKey (ageMs=$ageMs)") - return cachedSections.sections - } - logger.d("buildSections(): Cached sections stale for key=$sectionsCacheKey (ageMs=$ageMs), rebuilding") - } - - return try { - logger.d("buildSections(): Building Top Artist Radios section...") - val topArtistRadios = buildTopArtistRadiosSection(username) - logger.d("buildSections(): Built Top Artist Radios with ${topArtistRadios.items.size} items") - - logger.d("buildSections(): Building Mood Playlists section...") - val moodPlaylists = buildMoodPlaylistsSection(username) - logger.d("buildSections(): Built Mood Playlists with ${moodPlaylists.items.size} items") - - logger.d("buildSections(): Building Created For section...") - val createdFor = buildCreatedForSection(username) - logger.d("buildSections(): Built Created For with ${createdFor.items.size} items") - - val sections = listOf(topArtistRadios, moodPlaylists, createdFor) - writeCachedBrowseSections( - key = sectionsCacheKey, - entry = BrowseSectionsCacheEntry( - cachedAtEpochMs = nowEpochMs, - sections = sections, - ) - ) - logger.i("buildSections(): Successfully built ${sections.size} sections with ${sections.sumOf { it.items.size }} total items") - sections - } catch (e: Exception) { - logger.e(e) { "buildSections(): Failed to build fresh sections" } - if (cachedSections != null) { - logger.w("buildSections(): Returning stale cached sections for key=$sectionsCacheKey") - cachedSections.sections - } else { - emptyList() - } - } - } - - private suspend fun buildTopArtistRadiosSection(username: String): BrowseSectionData { - logger.d("buildTopArtistRadiosSection(): Fetching top artists for user: $username") - try { - val artistsResult = LbStatsApi.topArtistsForUser( - userName = username, - count = 10, - offset = 0, - range = AllowedStatisticsRange.QUARTER, - ) - val artists = when (artistsResult) { - is Either.Left -> { - val error = artistsResult.value - logger.e("buildTopArtistRadiosSection(): Failed to fetch top artists: $error") - throw error - } - - is Either.Right -> artistsResult.value.data.payload.artists - } - logger.d("buildTopArtistRadiosSection(): Retrieved ${artists.size} top artists") - - val playlists = artists.mapNotNull { artist -> - val mbid = artist.artistMbid?.toString() ?: return@mapNotNull null - val name = artist.artistName ?: return@mapNotNull null - logger.d("buildTopArtistRadiosSection(): Generating radio playlist for artist: $name (mbid: $mbid)") - - generateLbRadioPlaylist( - prompt = "artist:($mbid)", - mode = Mode.EASY, - title = "$name Radio", - annotation = "A radio playlist based on $name's music", - imageUrl = null, - ) - } - logger.d("buildTopArtistRadiosSection(): Generated ${playlists.size} radio playlists") - - return BrowseSectionData( - id = SECTION_TOP_ARTIST_RADIOS, - title = "Top Artist Radios", - moreLink = "https://listenbrainz.org/explore/lb-radio", - items = playlists.map { MetadataBrowseItem.Playlist(it) }, - ) - } catch (e: Exception) { - logger.e(e) { "buildTopArtistRadiosSection(): Error building top artist radios section" } - return BrowseSectionData( - id = SECTION_TOP_ARTIST_RADIOS, - title = "Top Artist Radios", - items = emptyList(), - ) - } - } - - private suspend fun buildMoodPlaylistsSection(username: String): BrowseSectionData { - logger.d("buildMoodPlaylistsSection(): Starting to build mood playlists (count: ${moodSeeds.size})") - try { - val playlists = moodSeeds.mapNotNull { mood -> - logger.d("buildMoodPlaylistsSection(): Generating playlist for mood: ${mood.title} (tag: ${mood.tag})") - generateLbRadioPlaylist( - prompt = "tag:(${mood.tag}) stats:$username::all_time", - mode = Mode.HARD, - title = mood.title, - annotation = mood.annotation, - imageUrl = "https://res.cloudinary.com/dszpk1pk9/image/upload/t_media_lib_thumb/spotube-plugin-musicbrainz-listenbrainz/moods/${mood.key}.webp", - ) - } - logger.d("buildMoodPlaylistsSection(): Successfully generated ${playlists.size} mood playlists") - - return BrowseSectionData( - id = SECTION_MOOD_PLAYLISTS, - title = "Based on your mood", - moreLink = "https://listenbrainz.org/explore/lb-radio", - items = playlists.map { MetadataBrowseItem.Playlist(it) }, - ) - } catch (e: Exception) { - logger.e(e) { "buildMoodPlaylistsSection(): Error building mood playlists section" } - return BrowseSectionData( - id = SECTION_MOOD_PLAYLISTS, - title = "Based on your mood", - items = emptyList(), - ) - } - } - - private suspend fun buildCreatedForSection(username: String): BrowseSectionData { - logger.d("buildCreatedForSection(): Fetching playlists created for user: $username") - try { - val playlistsResult = LbPlaylistsApi.playlistsCreatedForUser( - playlistUserName = username, - count = 25, - offset = 0, - ) - val playlists = when (playlistsResult) { - is Either.Left -> { - val error = playlistsResult.value - logger.e("buildCreatedForSection(): Failed to fetch created playlists: $error") - throw error - } - - is Either.Right -> playlistsResult.value.data.playlists.orEmpty() - } - logger.d("buildCreatedForSection(): Retrieved ${playlists.size} playlists") - - val items = playlists.mapNotNull { wrapper -> - val playlist = wrapper.playlist ?: return@mapNotNull null - val title = playlist.title.orEmpty() - val imageName = if (title.contains("Weekly Exploration", ignoreCase = true)) { - "weekly-exploration" - } else { - "weekly-jams" - } - logger.d("buildCreatedForSection(): Processing playlist: $title (imageName: $imageName)") - val imageUrl = - "https://res.cloudinary.com/dszpk1pk9/image/upload/t_media_lib_thumb/spotube-plugin-musicbrainz-listenbrainz/created_for/${imageName}.webp" - playlist.toMetadataPlaylist(thumbnailOverride = imageUrl) - } - logger.d("buildCreatedForSection(): Successfully processed ${items.size} playlists") - - return BrowseSectionData( - id = SECTION_CREATED_FOR, - title = "Created for you", - moreLink = "https://listenbrainz.org/user/$username/recommendations/", - items = items.map { MetadataBrowseItem.Playlist(it) }, - ) - } catch (e: Exception) { - logger.e(e) { "buildCreatedForSection(): Error building created for section" } - return BrowseSectionData( - id = SECTION_CREATED_FOR, - title = "Created for you", - items = emptyList(), - ) - } - } - - private suspend fun generateLbRadioPlaylist( - prompt: String, - mode: Mode, - title: String, - annotation: String?, - imageUrl: String?, - ): MetadataPlaylist? { - logger.d("generateLbRadioPlaylist(): Generating playlist with prompt='$prompt', mode=$mode, title='$title'") - val nowEpochMs = Clock.System.now().toEpochMilliseconds() - val cacheKey = buildLbRadioCacheKey(prompt = prompt, mode = mode) - val cachedEntry = readCachedLbRadioPlaylist(cacheKey) - if (cachedEntry != null) { - val ageMs = nowEpochMs - cachedEntry.cachedAtEpochMs - if (ageMs <= LB_RADIO_CACHE_TTL_MS) { - logger.d("generateLbRadioPlaylist(): Cache hit for key=$cacheKey (ageMs=$ageMs)") - return cachedEntry.playlist - } - logger.d("generateLbRadioPlaylist(): Cache stale for key=$cacheKey (ageMs=$ageMs), refreshing") - } - - try { - val lbRadioResult = LbMiscApi.lbRadio(prompt = prompt, mode = mode) { - authKeys( - Auth.ApiKeyAuth.ID, - ) - } - val lbRadio = when (lbRadioResult) { - is Either.Left -> { - val error = lbRadioResult.value - logger.e("generateLbRadioPlaylist(): API call failed with error: $error") - throw error - } - - is Either.Right -> lbRadioResult.value.data - } - val playlist = lbRadio.payload.jspf.playlist ?: run { - logger.w("generateLbRadioPlaylist(): No playlist data in response for prompt='$prompt'") - return null - } - val modeId = mode.name.lowercase() - val syntheticId = "lb-radio-playlist-$prompt-$modeId" - - val normalizedPlaylist = playlist.copy( - identifier = "https://listenbrainz.org/playlist/$syntheticId", - title = title, - annotation = annotation ?: playlist.annotation, - ) - - val result = normalizedPlaylist.toMetadataPlaylist(thumbnailOverride = imageUrl) - if (result != null) { - writeCachedLbRadioPlaylist( - key = cacheKey, - entry = LbRadioPlaylistCacheEntry( - cachedAtEpochMs = nowEpochMs, - playlist = result, - ) - ) - } - logger.d("generateLbRadioPlaylist(): Successfully generated playlist: $title") - return result - } catch (e: Exception) { - logger.e(e) { "generateLbRadioPlaylist(): Error generating playlist for prompt='$prompt'" } - if (cachedEntry != null) { - logger.w("generateLbRadioPlaylist(): Returning stale cache for key=$cacheKey due to API failure") - return cachedEntry.playlist - } - return null - } - } - - private fun buildLbRadioCacheKey(prompt: String, mode: Mode): String { - val promptHash = prompt.hashCode().toUInt().toString(16) - return "$LB_RADIO_CACHE_KEY_PREFIX:${mode.name.lowercase()}:$promptHash" - } - - private fun buildBrowseSectionsCacheKey(username: String): String { - val usernameHash = username.lowercase().hashCode().toUInt().toString(16) - return "$BROWSE_SECTIONS_CACHE_KEY_PREFIX:$usernameHash" - } - - private suspend fun readCachedLbRadioPlaylist(key: String): LbRadioPlaylistCacheEntry? { - val raw = persistedStorage.getString(key) ?: return null - return runCatching { - cacheJson.decodeFromString(raw) - }.onFailure { throwable -> - logger.w(throwable) { "readCachedLbRadioPlaylist(): Corrupt cache entry for key=$key, removing" } - persistedStorage.remove(key) - }.getOrNull() - } - - private suspend fun writeCachedLbRadioPlaylist(key: String, entry: LbRadioPlaylistCacheEntry) { - runCatching { - persistedStorage.putString(key, cacheJson.encodeToString(entry)) - }.onFailure { throwable -> - logger.w(throwable) { "writeCachedLbRadioPlaylist(): Failed to persist cache for key=$key" } - } - } - - private suspend fun readCachedBrowseSections(key: String): BrowseSectionsCacheEntry? { - val raw = persistedStorage.getString(key) ?: return null - return runCatching { - cacheJson.decodeFromString(raw) - }.onFailure { throwable -> - logger.w(throwable) { "readCachedBrowseSections(): Corrupt cache entry for key=$key, removing" } - persistedStorage.remove(key) - }.getOrNull() - } - - private suspend fun writeCachedBrowseSections(key: String, entry: BrowseSectionsCacheEntry) { - runCatching { - persistedStorage.putString(key, cacheJson.encodeToString(entry)) - }.onFailure { throwable -> - logger.w(throwable) { "writeCachedBrowseSections(): Failed to persist cache for key=$key" } - } - } - - private fun Playlist.toMetadataPlaylist(thumbnailOverride: String? = null): MetadataPlaylist? { - val identifier = identifier ?: return null - val id = identifier.substringAfterLast('/').takeIf { it.isNotBlank() } ?: return null - val creator = creator ?: "Unknown" - - return MetadataPlaylist( - id = id, - title = title ?: "Untitled", - description = annotation, - thumbnails = listOf( - Thumbnail( - url = thumbnailOverride - ?: "https://ui-avatars.com/api/?name=${title ?: "Playlist"}&background=random", - width = 300, - height = 300 - ) - ), - trackCount = track?.size ?: 0, - externalUri = identifier, - owner = MetadataUser( - id = creator, - username = creator, - displayName = creator, - thumbnails = emptyList(), - externalUri = "https://listenbrainz.org/user/$creator/", - ), - ) - } - - private suspend fun requireUsername(): String { - logger.d("requireUsername(): Checking for cached username") - cachedUsername?.let { - logger.d("requireUsername(): Using cached username: $it") - return it - } - - logger.d("requireUsername(): Retrieving auth token from persistent storage") - val auth = Auth.ApiKeyAuth { - val token = - persistedStorage.getString("listenbrainz_auth_token") ?: run { - logger.w("requireUsername(): No auth token found in persistent storage") - return@ApiKeyAuth null - } - logger.d("requireUsername(): Auth token retrieved, length: ${token.length}") - if (token.startsWith("Token ", ignoreCase = true)) token else "Token $token" - } - Api.setAuthProvider(auth) - - logger.i("requireUsername(): Validating token with ListenBrainz API") - val username = when (val res = LbCoreApi.validateToken()) { - is Either.Left -> { - val error = res.value - logger.e("requireUsername(): Token validation failed with error: $error") - throw error - } - - is Either.Right -> { - val user = res.value.data.userName - logger.i("requireUsername(): Token validation successful, username: $user") - user - } - } - cachedUsername = username - logger.d("requireUsername(): Caching username: $username") - return username!! - } - -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzMetadataPlaylistAPI.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzMetadataPlaylistAPI.kt deleted file mode 100644 index 7db28b5a..00000000 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzMetadataPlaylistAPI.kt +++ /dev/null @@ -1,414 +0,0 @@ -/* - * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz - -import arrow.core.Either -import dev.krtirtho.plugin_interfaces.host_apis.PersistedStorageAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationStrategy -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationResult -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.Thumbnail -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.playlist.MetadataPlaylist -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.playlist.MetadataPlaylistAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.track.MetadataTrack -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.user.MetadataUser -import dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.musicbrainz.MusicbrainzRepository -import dev.krtirtho.spotube.listenbrainz.Api -import dev.krtirtho.spotube.listenbrainz.Auth -import dev.krtirtho.spotube.listenbrainz.api.LbCoreApi -import dev.krtirtho.spotube.listenbrainz.api.LbPlaylistsApi -import dev.krtirtho.spotube.listenbrainz.models.CreatePlaylistRequest -import dev.krtirtho.spotube.listenbrainz.models.Playlist -import dev.krtirtho.spotube.listenbrainz.models.PlaylistTrackInner -import kotlin.uuid.Uuid - -class RealMusicbrainzListenbrainzMetadataPlaylistAPI( - private val musicbrainzRepository: MusicbrainzRepository, - private val persistedStorage: PersistedStorageAPI -) : MetadataPlaylistAPI { - - private var cachedUsername: String? = null - - private suspend fun requireUsername(): String { - cachedUsername?.let { return it } - - // Setup auth provider globally - val auth = Auth.ApiKeyAuth { - val token = - persistedStorage.getString("listenbrainz_auth_token") ?: return@ApiKeyAuth null - if (token.startsWith("Token ", ignoreCase = true)) token else "Token $token" - } - Api.setAuthProvider(auth) - - val username = when (val res = LbCoreApi.validateToken()) { - is Either.Left -> throw IllegalStateException("Unable to resolve ListenBrainz username: ${res.value}") - is Either.Right -> res.value.data.userName - } - cachedUsername = username!! - return username - } - - override suspend fun getPlaylist(id: String): MetadataPlaylist { - // Ensure auth is set up if possible - try { - requireUsername() - } catch (_: Exception) { - } - - val response = LbPlaylistsApi.fetchPlaylist( - playlistMbid = Uuid.parse(id), - fetchMetadata = false - ) - - val playlist = response.getOrNull()?.data?.playlist - ?: throw IllegalStateException("Playlist not found") - - return playlistToMetadata(playlist, id) - ?: throw IllegalStateException("Invalid playlist data") - } - - override suspend fun getPlaylistTracks( - id: String, - pagination: PaginationStrategy? - ): PaginationResult { - val paging = pagination as? PaginationStrategy.Offset ?: PaginationStrategy.Offset(0, 20) - - try { - requireUsername() - } catch (_: Exception) { - } - - val tracks = try { - LbPlaylistsApi.fetchPlaylist( - playlistMbid = Uuid.parse(id), - fetchMetadata = false - ).getOrNull()?.data?.playlist?.track ?: emptyList() - } catch (_: Exception) { - return PaginationResult( - items = emptyList(), - totalCount = 0, - nextPagination = null - ) - } - - val slice = tracks.drop(paging.offset).take(paging.limit) - - val recordingIds = slice.mapNotNull { track -> - track.identifier?.firstOrNull()?.substringAfterLast("/")?.takeIf { it.isNotBlank() } - } - val nextOffset = if (paging.offset + paging.limit < tracks.size) paging.offset + paging.limit else null - if (recordingIds.isEmpty()) { - return PaginationResult( - items = emptyList(), - totalCount = tracks.size, - nextPagination = nextOffset?.let { PaginationStrategy.Offset(it, paging.limit) } - ) - } - - val query = recordingIds.joinToString(" OR ") { "rid:$it" } - val mbResponse = musicbrainzRepository.searchRecordings( - query = query, - limit = recordingIds.size, - offset = 0 - ) - - val recordingsMap = mbResponse.recordings.associateBy { it.id } - - val metadataTracks = recordingIds.mapNotNull { rid -> - val recording = recordingsMap[rid] ?: return@mapNotNull null - // We pick the first release as the album. - val release = recording.releases.firstOrNull() ?: return@mapNotNull null - val releaseGroupId = release.releaseGroup?.id ?: release.id - val album = release.toMetadataAlbumDetailed(releaseGroupId) - - recording.toMetadataTrack(album) - } - - return PaginationResult( - items = metadataTracks, - totalCount = tracks.size, - nextPagination = nextOffset?.let { PaginationStrategy.Offset(it, paging.limit) } - ) - } - - override suspend fun savedPlaylists( - pagination: PaginationStrategy? - ): PaginationResult { - val paging = pagination as? PaginationStrategy.Offset ?: PaginationStrategy.Offset(0, 20) - // 1. Fetch User Playlists (Remote) - // 2. Fetch Saved Playlists (Local - from ids) - - var username: String? = null - try { - username = requireUsername() - } catch (_: Exception) { - // If no username, we can't fetch remote user playlists - } - - var remoteTotal = 0L - var remoteItems: List = emptyList() - - if (username != null) { - try { - val countRes = LbPlaylistsApi.playlistsForUser(username, count = 1, offset = 0) - remoteTotal = countRes.getOrNull()?.data?.playlistCount ?: 0L - - if (paging.offset < remoteTotal) { - val limit = (paging.limit).toLong() - val res = LbPlaylistsApi.playlistsForUser( - username, - count = limit, - offset = paging.offset.toLong() - ) - res.getOrNull()?.data?.let { data -> - // playlistCount might be updated - remoteTotal = data.playlistCount ?: remoteTotal - remoteItems = data.playlists?.mapNotNull { req -> - req.playlist?.let { - playlistToMetadata( - it, - req.playlist.identifier?.substringAfterLast("/") - .takeIf { id -> id != req.playlist.identifier } ?: "") - } - } ?: emptyList() - } - } - } catch (_: Exception) { - // e.printStackTrace() // Removed printStackTrace in KMP common code usually - } - } - - val ids = persistedStorage.getString(SAVED_PLAYLISTS_KEY) - ?.split(",") - ?.filter { it.isNotBlank() } - ?: emptyList() - val savedCount = ids.size - val savedItems = mutableListOf() - - // Calculate how many slots in pageSize are left to fill from Saved items - val remoteFetchedCount = remoteItems.size - val neededFromSaved = paging.limit - remoteFetchedCount - - if (neededFromSaved > 0) { - // We need checks: - // 1. Did we exhaust remote? (pagination.offset + remoteFetchedCount >= remoteTotal) - // 2. Or is pagination.offset already starting inside Saved list? (pagination.offset >= remoteTotal) - val startInSaved: Long = if (paging.offset >= remoteTotal) { - paging.offset - remoteTotal - } else { - // We were fetching from remote, and maybe it finished, so we append from start of saved - 0 - } - - if (startInSaved < savedCount) { - val endInSaved = (startInSaved + neededFromSaved).coerceAtMost(savedCount.toLong()) - val pageIds = ids.subList(startInSaved.toInt(), endInSaved.toInt()) - - val fetchedSaved = pageIds.mapNotNull { id -> - try { - getPlaylist(id) - } catch (_: Exception) { - null - } - } - savedItems.addAll(fetchedSaved) - } - } - - val allItems = remoteItems + savedItems - val totalCount = remoteTotal + savedCount - - val nextOffset = if (paging.offset + allItems.size < totalCount) paging.offset + allItems.size else null - - return PaginationResult( - items = allItems, - totalCount = totalCount.toInt(), - nextPagination = nextOffset?.let { PaginationStrategy.Offset(it, paging.limit) } - ) - } - - private fun playlistToMetadata(playlist: Playlist, id: String): MetadataPlaylist? { - // Identifier usually contains URL, we need to extract ID if not passed - val mbid = id.ifBlank { - playlist.identifier?.substringAfterLast("/") - ?.takeIf { it != playlist.identifier } ?: return null - } - - val creator = playlist.creator ?: "Unknown" - - return MetadataPlaylist( - id = mbid, - title = playlist.title ?: "Untitled", - description = playlist.annotation, - thumbnails = listOf( - Thumbnail( - url = "https://ui-avatars.com/api/?name=${playlist.title}&background=random", - width = 300, - height = 300 - ) - ), - trackCount = playlist.track?.size ?: 0, - externalUri = playlist.identifier ?: "https://listenbrainz.org/playlist/$mbid", - owner = MetadataUser( - id = creator, - username = creator, - displayName = creator, - thumbnails = emptyList(), // no avatar easily available - externalUri = "https://listenbrainz.org/user/$creator/" - ) - ) - } - - override suspend fun isSavedPlaylists(ids: List): List { - val savedIds = persistedStorage.getString(SAVED_PLAYLISTS_KEY) - ?.split(",") - ?.toSet() - ?: emptySet() - - return ids.map { savedIds.contains(it) } - } - - override suspend fun savePlaylists(ids: List) { - val savedIds = (persistedStorage.getString(SAVED_PLAYLISTS_KEY) - ?.split(",") - ?.filter { it.isNotBlank() } - ?.toMutableSet() - ?: mutableSetOf()).apply { - addAll(ids) - } - persistedStorage.putString(SAVED_PLAYLISTS_KEY, savedIds.joinToString(",")) - } - - override suspend fun removeSavedPlaylists(ids: List) { - val savedIds = (persistedStorage.getString(SAVED_PLAYLISTS_KEY) - ?.split(",") - ?.filter { it.isNotBlank() } - ?.toMutableSet() - ?: mutableSetOf()).apply { - removeAll(ids.toSet()) - } - persistedStorage.putString(SAVED_PLAYLISTS_KEY, savedIds.joinToString(",")) - } - - companion object { - private const val SAVED_PLAYLISTS_KEY = "saved_playlists" - } - - override suspend fun createPlaylist( - name: String, - description: String?, - isPublic: Boolean, - isCollaborating: Boolean, - imageBase64: String, - trackIds: List - ): MetadataPlaylist { - val username = requireUsername() - - val body = CreatePlaylistRequest( - playlist = Playlist( - title = name, - annotation = description, - track = trackIds.map { - PlaylistTrackInner( - identifier = listOf("https://musicbrainz.org/recording/$it") - ) - } - ) - ) - - val res = LbPlaylistsApi.createPlaylist(body) - val created = - res.getOrNull()?.data ?: throw IllegalStateException("Failed to create playlist") - - // The create response usually contains the MBID - val mbid = - created.playlistMbid?.toString() ?: throw IllegalStateException("No MBID returned") - - return MetadataPlaylist( - id = mbid, - title = name, - description = description, - thumbnails = listOf( - Thumbnail( - url = "https://ui-avatars.com/api/?name=$name&background=random", - width = 300, - height = 300 - ) - ), - trackCount = trackIds.size, - externalUri = "https://listenbrainz.org/playlist/$mbid", - owner = MetadataUser( - id = username, - username = username, - displayName = username, - thumbnails = emptyList(), - externalUri = "https://listenbrainz.org/user/$username/" - ) - ) - } - - override suspend fun updatePlaylist( - id: String, - name: String?, - description: String?, - isPublic: Boolean?, - isCollaborating: Boolean?, - imageBase64: String?, - trackIds: List? - ): MetadataPlaylist { - requireUsername() - // If we need to fetch first to get existing values? - // LB edit API usually replaces fields. - - val body = CreatePlaylistRequest( - playlist = Playlist( - title = name ?: "Untitled", - annotation = description, - track = trackIds?.map { - PlaylistTrackInner( - identifier = listOf("https://musicbrainz.org/recording/$it") - ) - } - ) - ) - - val res = LbPlaylistsApi.editPlaylist(Uuid.parse(id), body) - if (res.isLeft()) throw IllegalStateException("Failed to update playlist: ${res.leftOrNull()}") - - return getPlaylist(id) - } - - override suspend fun deletePlaylist(id: String) { - requireUsername() - LbPlaylistsApi.deletePlaylist(Uuid.parse(id)) - } - - override suspend fun addTracksToPlaylist( - playlistId: String, - trackIds: List - ) { - TODO("Not yet implemented") - } - - override suspend fun removeTracksFromPlaylist( - playlistId: String, - trackIds: List - ) { - TODO("Not yet implemented") - } - -} diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzMetadataSearchAPI.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzMetadataSearchAPI.kt deleted file mode 100644 index e99fe3e9..00000000 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzMetadataSearchAPI.kt +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz - -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.album.MetadataAlbum -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.album.MetadataAlbumType -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.artist.MetadataArtist -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationStrategy -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationResult -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.Thumbnail -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.playlist.MetadataPlaylist -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.search.MetadataSearchAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.search.MetadataSearchResult -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.search.MetadataSupportedSearchType -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.track.MetadataTrack -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.user.MetadataUser -import dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.listenbrainz.LBPlaylistSearchResponse -import dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.musicbrainz.MusicbrainzArtistEnricher -import dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.musicbrainz.MusicbrainzRepository -import io.ktor.client.HttpClient -import io.ktor.client.request.get -import io.ktor.client.request.parameter -import io.ktor.client.request.url -import io.ktor.client.statement.bodyAsText -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope -import kotlinx.serialization.json.Json - -class RealMusicbrainzListenbrainzMetadataSearchAPI( - private val musicbrainzRepository: MusicbrainzRepository, - private val artistEnricher: MusicbrainzArtistEnricher, - private val httpClient: HttpClient -): MetadataSearchAPI { - private val json = Json { ignoreUnknownKeys = true; isLenient = true } - - override val supportedSearchTypes: List = listOf( - MetadataSupportedSearchType.TRACK, - MetadataSupportedSearchType.ARTIST, - MetadataSupportedSearchType.ALBUM, - MetadataSupportedSearchType.PLAYLIST, - ) - - override suspend fun search(query: String): List = coroutineScope { - val playlists = async { searchPlaylists(query, PaginationStrategy.Offset(offset = 0, limit = 5)).items } - val tracks = async { searchTracks(query, PaginationStrategy.Offset(offset = 0, limit = 5)).items } - val artists = async { searchArtists(query, PaginationStrategy.Offset(offset = 0, limit = 5)).items } - val albums = async { searchAlbums(query, PaginationStrategy.Offset(offset = 0, limit = 5)).items } - - val results = mutableListOf() - results.addAll(playlists.await()) - results.addAll(tracks.await()) - results.addAll(artists.await()) - results.addAll(albums.await()) - results - } - - override suspend fun searchTracks( - query: String, - pagination: PaginationStrategy? - ): PaginationResult { - val paging = pagination as? PaginationStrategy.Offset ?: PaginationStrategy.Offset(0, 20) - - val result = musicbrainzRepository.searchRecordings( - query = query, - limit = paging.limit, - offset = paging.offset - ) - val items = result.recordings.map { recording -> - val release = recording.releases.firstOrNull() - val releaseGroupId = release?.releaseGroup?.id ?: release?.id ?: recording.id - - val albumDetailed = MetadataAlbum.Detailed( - id = releaseGroupId, - title = release?.releaseGroup?.title ?: release?.title ?: recording.title, // Fallback - description = null, - thumbnails = listOf( - Thumbnail("https://coverartarchive.org/release-group/$releaseGroupId/front-250.jpg", 250, 250), - Thumbnail("https://coverartarchive.org/release-group/$releaseGroupId/front-500.jpg", 500, 500) - ), - albumType = MetadataAlbumType.Album, // Default - artists = emptyList(), // Can populate if needed - externalUri = "https://musicbrainz.org/release-group/$releaseGroupId", - releaseDate = release?.date, - genres = emptyList(), - trackCount = release?.trackCount ?: 0 - ) - - MetadataSearchResult.Track( - data = MetadataTrack( - id = recording.id, - title = recording.title, - durationMs = recording.length?.toLong() ?: 0L, - trackNumber = null, - discNumber = null, - artists = recording.artistCredit.mapNotNull { credit -> - credit.artist?.let { artist -> - MetadataArtist.Basic( - id = artist.id, - name = artist.name, - thumbnails = emptyList(), - externalUri = "https://musicbrainz.org/artist/${artist.id}" - ) - } - }, - album = albumDetailed, - explicit = recording.tags.any { it.name.contains("explicit", ignoreCase = true) }, - popularity = null, - isrcCode = recording.isrcs.firstOrNull(), - externalUri = "https://musicbrainz.org/recording/${recording.id}", - thumbnails = null, - ) - ) - } - - val nextOffset = if (paging.offset + paging.limit < result.count) { - paging.offset + paging.limit - } else null - - return PaginationResult( - items = items, - totalCount = result.count, - nextPagination = nextOffset?.let { PaginationStrategy.Offset(it, paging.limit) }, - ) - } - - override suspend fun searchArtists( - query: String, - pagination: PaginationStrategy? - ): PaginationResult { - val paging = pagination as? PaginationStrategy.Offset ?: PaginationStrategy.Offset(0, 20) - val result = musicbrainzRepository.searchArtists( - query = query, - limit = paging.limit, - offset = paging.offset - ) - - val artistIds = result.artists.map { it.id } - val enriched = artistEnricher.getEnrichedArtists(artistIds) - - val items = enriched.map { (artist, images) -> - MetadataSearchResult.Artist( - data = MetadataArtist.Basic( - id = artist.id, - name = artist.name, - thumbnails = images.map { Thumbnail(it, 300, 300) }, - externalUri = "https://musicbrainz.org/artist/${artist.id}" - ) - ) - } - - val nextOffset = if (paging.offset + paging.limit < result.count) { - paging.offset + paging.limit - } else null - - return PaginationResult( - items = items, - totalCount = result.count, - nextPagination = nextOffset?.let { PaginationStrategy.Offset(it, paging.limit) }, - ) - } - - override suspend fun searchAlbums( - query: String, - pagination: PaginationStrategy? - ): PaginationResult { - val paging = pagination as? PaginationStrategy.Offset ?: PaginationStrategy.Offset(0, 20) - val result = musicbrainzRepository.searchReleaseGroups( - query = query, - limit = paging.limit, - offset = paging.offset - ) - - val items = result.releaseGroups.map { group -> - MetadataSearchResult.Album( - data = MetadataAlbum.Basic( - id = group.id, - title = group.title, - description = null, - thumbnails = listOf( - Thumbnail("https://coverartarchive.org/release-group/${group.id}/front-250.jpg", 250, 250), - Thumbnail("https://coverartarchive.org/release-group/${group.id}/front-500.jpg", 500, 500) - ), - albumType = when (group.primaryType?.lowercase()) { - "album" -> MetadataAlbumType.Album - "single" -> MetadataAlbumType.Single - "compilation" -> MetadataAlbumType.Collection - else -> MetadataAlbumType.Album - }, - artists = emptyList(), // Populate? - externalUri = "https://musicbrainz.org/release-group/${group.id}" - ) - ) - } - - val nextOffset = if (paging.offset + paging.limit < result.count) { - paging.offset + paging.limit - } else null - - return PaginationResult( - items = items, - totalCount = result.count, - nextPagination = nextOffset?.let { PaginationStrategy.Offset(it, paging.limit) }, - ) - } - - override suspend fun searchPlaylists( - query: String, - pagination: PaginationStrategy? - ): PaginationResult { - val paging = pagination as? PaginationStrategy.Offset ?: PaginationStrategy.Offset(0, 20) - try { - val responseText = httpClient.get { - url("https://api.listenbrainz.org/1/playlist/search") - parameter("query", query) - parameter("count", paging.limit) - parameter("offset", paging.offset) - }.bodyAsText() - - val response = json.decodeFromString(responseText) - - val items = response.playlists.map { it.playlist }.map { playlist -> - val id = playlist.identifier.substringAfterLast("/") - MetadataSearchResult.Playlist( - data = MetadataPlaylist( - id = id, - title = playlist.title, - description = playlist.annotation, - thumbnails = emptyList(), // Listenbrainz search doesn't return playlist covers usually - trackCount = 0, // Not available in search result - owner = MetadataUser( - id = playlist.creator, - username = playlist.creator, - displayName = playlist.creator, - thumbnails = emptyList(), - externalUri = "https://listenbrainz.org/user/${playlist.creator}" - ), - externalUri = "https://listenbrainz.org/playlist/$id" - ) - ) - } - - val nextOffset = if (paging.offset + paging.limit < response.count) { - paging.offset + paging.limit - } else null - - return PaginationResult( - items = items, - totalCount = response.count, - nextPagination = nextOffset?.let { PaginationStrategy.Offset(it, paging.limit) }, - ) - } catch (e: Exception) { - e.printStackTrace() - return PaginationResult(emptyList(), 0, null) - } - } - - override suspend fun searchUsers( - query: String, - pagination: PaginationStrategy? - ): PaginationResult { - return PaginationResult(emptyList(), 0, null) - } - -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzMetadataTrackAPI.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzMetadataTrackAPI.kt deleted file mode 100644 index 1687af1b..00000000 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/RealMusicbrainzListenbrainzMetadataTrackAPI.kt +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz - -import arrow.core.Either -import dev.krtirtho.plugin_interfaces.host_apis.PersistedStorageAPI -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationStrategy -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common.PaginationResult -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.track.MetadataTrack -import dev.krtirtho.plugin_interfaces.plugin_apis.metadata.track.MetadataTrackAPI -import dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.musicbrainz.MusicbrainzRepository -import dev.krtirtho.spotube.listenbrainz.Api -import dev.krtirtho.spotube.listenbrainz.Auth -import dev.krtirtho.spotube.listenbrainz.api.LbCoreApi -import dev.krtirtho.spotube.listenbrainz.api.LbRecordingsApi -import dev.krtirtho.spotube.listenbrainz.models.RecordingFeedbackRequest -import io.ktor.client.request.get -import io.ktor.client.request.header -import io.ktor.client.request.parameter -import io.ktor.client.statement.bodyAsText -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.contentOrNull -import kotlinx.serialization.json.jsonArray -import kotlinx.serialization.json.jsonObject -import kotlinx.serialization.json.jsonPrimitive -import kotlin.uuid.Uuid - -class RealMusicbrainzListenbrainzMetadataTrackAPI( - private val musicbrainzRepository: MusicbrainzRepository, - private val persistedStorage: PersistedStorageAPI -) : MetadataTrackAPI { - - private var cachedUsername: String? = null - - private suspend fun requireUsername(): String { - cachedUsername?.let { return it } - - val auth = Auth.ApiKeyAuth { - val token = persistedStorage.getString("listenbrainz_auth_token") ?: return@ApiKeyAuth null - if (token.startsWith("Token ", ignoreCase = true)) token else "Token $token" - } - Api.setAuthProvider(auth) - - val username = when (val res = LbCoreApi.validateToken()) { - is Either.Left -> throw IllegalStateException("Unable to resolve ListenBrainz username: ${res.value}") - is Either.Right -> res.value.data.userName - } - cachedUsername = username!! - return username - } - - override suspend fun getTrack(id: String): MetadataTrack { - val recording = musicbrainzRepository.getRecordingByMbid( - mbid = id, - includes = listOf("artists", "releases", "artist-credits", "release-groups") - ) - val release = recording.releases.firstOrNull() - ?: throw IllegalStateException("No release found for track") - - val album = release.toMetadataAlbumDetailed(release.releaseGroup?.id ?: release.id) - - // This creates basic track metadata from MusicBrainz - return recording.toMetadataTrack(album) - } - - override suspend fun savedTracks( - pagination: PaginationStrategy? - ): PaginationResult { - val paging = pagination as? PaginationStrategy.Offset ?: PaginationStrategy.Offset(0, 20) - - val username = try { - requireUsername() - } catch (_: Exception) { - return PaginationResult( - items = emptyList(), - totalCount = 0, - nextPagination = null - ) - } - - val res = LbRecordingsApi.getFeedback( - userName = username, - score = 1, - count = paging.limit.toLong(), - offset = paging.offset.toLong() - ) - - val feedbackResponse = res.getOrNull()?.data ?: return PaginationResult( - items = emptyList(), - totalCount = 0, - nextPagination = null - ) - val feedbacks = feedbackResponse.feedback ?: emptyList() - val mbids = feedbacks.mapNotNull { it.recordingMbid?.toString() } - - if (mbids.isEmpty()) { - return PaginationResult( - items = emptyList(), - totalCount = (feedbackResponse.totalCount ?: 0).toInt(), - nextPagination = null - ) - } - - // Batch fetch metadata - val query = mbids.joinToString(" OR ") { "rid:$it" } - val searchRes = musicbrainzRepository.searchRecordings(query, limit = mbids.size) - val recordingMap = searchRes.recordings.associateBy { it.id } - - val tracks = mbids.mapNotNull { mbid -> - val recording = recordingMap[mbid] ?: return@mapNotNull null - val release = recording.releases.firstOrNull() ?: return@mapNotNull null - val album = release.toMetadataAlbumDetailed(release.releaseGroup?.id ?: release.id) - recording.toMetadataTrack(album) - } - - val nextOffset = if ((paging.offset + paging.limit) < (feedbackResponse.totalCount ?: 0)) { - paging.offset + paging.limit - } else null - - return PaginationResult( - items = tracks, - totalCount = (feedbackResponse.totalCount ?: 0).toInt(), - nextPagination = nextOffset?.let { PaginationStrategy.Offset(it, paging.limit) } - ) - } - - override suspend fun isSavedTracks(ids: List): List { - val username = try { - requireUsername() - } catch (_: Exception) { - return ids.map { false } - } - - val uuids = ids.mapNotNull { - try { Uuid.parse(it) } catch(_: Exception) { null } - } - if (uuids.isEmpty()) return ids.map { false } - - val res = LbRecordingsApi.getFeedbackForRecordings( - userName = username, - recordingMbids = uuids - ) - - val feedbackMap = res.getOrNull()?.data?.feedback?.associateBy { it.recordingMbid.toString() } ?: emptyMap() - - return ids.map { id -> - feedbackMap[id]?.score == 1L - } - } - - override suspend fun saveTracks(ids: List) { - requireUsername() - ids.forEach { id -> - try { - LbRecordingsApi.recordingFeedback( - RecordingFeedbackRequest( - recordingMbid = Uuid.parse(id), - score = 1 - ) - ) - } catch (_: Exception) {} - } - } - - override suspend fun removeSavedTracks(ids: List) { - requireUsername() - ids.forEach { id -> - try { - LbRecordingsApi.recordingFeedback( - RecordingFeedbackRequest( - recordingMbid = Uuid.parse(id), - score = 0 - ) - ) - } catch (_: Exception) {} - } - } - - override suspend fun recommendationsBasedOnTracks( - seedTrackIds: List, - limit: Int - ): List { - requireUsername() - val idsParam = seedTrackIds.joinToString(",") - - val jsonStr = try { - val response = Api.client.get("https://api.listenbrainz.org/1/recommendation/playground/recording_recommendations") { - parameter("recording_mbids", idsParam) - parameter("count", limit) - val token = persistedStorage.getString("listenbrainz_auth_token") - if (token != null) { - header("Authorization", "Token $token") - } - } - response.bodyAsText() - } catch (_: Exception) { - return emptyList() - } - - val json = Json { ignoreUnknownKeys = true } - val root = json.parseToJsonElement(jsonStr).jsonObject - val payload = root["payload"]?.jsonObject - val recordings = payload?.get("recordings")?.jsonArray ?: return emptyList() - - val mbids = recordings.mapNotNull { - it.jsonObject["recording_mbid"]?.jsonPrimitive?.contentOrNull - } - - if (mbids.isEmpty()) return emptyList() - - val query = mbids.joinToString(" OR ") { "rid:$it" } - val searchRes = musicbrainzRepository.searchRecordings(query, limit = mbids.size) - val recordingMap = searchRes.recordings.associateBy { it.id } - - return mbids.mapNotNull { mbid -> - val recording = recordingMap[mbid] ?: return@mapNotNull null - val release = recording.releases.firstOrNull() ?: return@mapNotNull null - val album = release.toMetadataAlbumDetailed(release.releaseGroup?.id ?: release.id) - recording.toMetadataTrack(album) - } - } -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/listenbrainz/ListenbrainzModels.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/listenbrainz/ListenbrainzModels.kt deleted file mode 100644 index fb7f7a2f..00000000 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/listenbrainz/ListenbrainzModels.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.listenbrainz - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.JsonElement - -@Serializable -data class LBPlaylistExtensionSpopf( - @SerialName("public") - val isPublic: Boolean? = null -) - -@Serializable -data class LBPlaylistExtension( - @SerialName("https://musicbrainz.org/doc/jspf#playlist") - val spopf: LBPlaylistExtensionSpopf? = null -) - -@Serializable -data class LBPlaylistUser( - val name: String -) - -@Serializable -data class LBPlaylist( - val identifier: String, - val title: String, - val annotation: String? = null, - val creator: String, // In search response it is string (username) - val extension: LBPlaylistExtension? = null, - val date: String? = null, -) - -@Serializable -data class LBPlaylistObject( - val playlist: LBPlaylist, -) - -@Serializable -data class LBPlaylistSearchResponse( - @SerialName("playlist_count") - val count: Int = 0, - val offset: Int = 0, - val playlists: List = emptyList(), -) - diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/musicbrainz/MusicBrainzModels.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/musicbrainz/MusicBrainzModels.kt deleted file mode 100644 index 4d1a08dd..00000000 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/musicbrainz/MusicBrainzModels.kt +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.musicbrainz - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class MusicbrainzTag( - val name: String, - val count: Int? = null, -) - -@Serializable -data class MusicbrainzLifeSpan( - val begin: String? = null, - val end: String? = null, - val ended: Boolean? = null, -) - -@Serializable -data class MusicbrainzTextRepresentation( - val language: String? = null, - val script: String? = null, -) - -@Serializable -data class MusicbrainzArtist( - val id: String, - val name: String, - val score: String? = null, - @SerialName("sort-name") - val sortName: String? = null, - val country: String? = null, - val type: String? = null, - val gender: String? = null, - val disambiguation: String? = null, - @SerialName("life-span") - val lifeSpan: MusicbrainzLifeSpan? = null, - val tags: List = emptyList(), -) - -@Serializable -data class MusicbrainzArtistCredit( - val name: String? = null, - @SerialName("joinphrase") - val joinPhrase: String? = null, - val artist: MusicbrainzArtist? = null, -) - -@Serializable -data class MusicbrainzReleaseGroup( - val id: String, - val title: String, - @SerialName("primary-type") - val primaryType: String? = null, - @SerialName("secondary-types") - val secondaryTypes: List = emptyList(), - @SerialName("first-release-date") - val firstReleaseDate: String? = null, -) - -@Serializable -data class MusicbrainzRelease( - val id: String, - val title: String, - val score: String? = null, - val status: String? = null, - val quality: String? = null, - val date: String? = null, - val country: String? = null, - @SerialName("barcode") - val barCode: String? = null, - @SerialName("track-count") - val trackCount: Int? = null, - @SerialName("text-representation") - val textRepresentation: MusicbrainzTextRepresentation? = null, - @SerialName("artist-credit") - val artistCredit: List = emptyList(), - @SerialName("release-group") - val releaseGroup: MusicbrainzReleaseGroup? = null, -) - -@Serializable -data class MusicbrainzRecording( - val id: String, - val title: String, - val length: Int? = null, - val disambiguation: String? = null, - val video: Boolean? = null, - val score: String? = null, - @SerialName("first-release-date") - val firstReleaseDate: String? = null, - @SerialName("artist-credit") - val artistCredit: List = emptyList(), - val releases: List = emptyList(), - val tags: List = emptyList(), - val isrcs: List = emptyList(), -) - -@Serializable -data class MusicbrainzRecordingSearchResponse( - val created: String? = null, - val count: Int = 0, - val offset: Int = 0, - val recordings: List = emptyList(), -) - -@Serializable -data class MusicbrainzArtistSearchResponse( - val created: String? = null, - val count: Int = 0, - val offset: Int = 0, - val artists: List = emptyList(), -) - -@Serializable -data class MusicbrainzReleaseSearchResponse( - val created: String? = null, - val count: Int = 0, - val offset: Int = 0, - val releases: List = emptyList(), -) - -@Serializable -data class MusicbrainzReleaseGroupSearchResponse( - val created: String? = null, - val count: Int = 0, - val offset: Int = 0, - @SerialName("release-groups") - val releaseGroups: List = emptyList(), -) - -@Serializable -data class MusicbrainzUrlRelation( - val artist: MusicbrainzArtist? = null, -) - -@Serializable -data class MusicbrainzUrlRelationList( - val relations: List = emptyList(), -) - -@Serializable -data class MusicbrainzUrl( - val resource: String, - @SerialName("relation-list") - val relationList: List = emptyList(), -) - -@Serializable -data class MusicbrainzUrlResponse( - val urls: List = emptyList(), -) - -@Serializable -data class MusicbrainzIsrcLookupResponse( - val isrc: String, - val recordings: List = emptyList(), -) diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/musicbrainz/MusicbrainzArtistEnricher.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/musicbrainz/MusicbrainzArtistEnricher.kt deleted file mode 100644 index 8ad20857..00000000 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/musicbrainz/MusicbrainzArtistEnricher.kt +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.musicbrainz - -import dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.wikidata.WikidataRepository - -class MusicbrainzArtistEnricher( - private val musicbrainzRepository: MusicbrainzRepository, - private val wikidataRepository: WikidataRepository -) { - suspend fun getArtistsWithImages(artistIds: List): List { - if (artistIds.isEmpty()) return emptyList() - - // 1. Find Wikidata URLs for these artists - val idsQuery = artistIds.joinToString(" OR ") { "targetid:$it" } - val query = "relationtype:wikidata AND targettype:artist AND ($idsQuery)" - - val urlResponse = musicbrainzRepository.searchUrls( - query = query, - limit = artistIds.size - ) - - val urls = urlResponse.urls - val wikidataIds = urls.map { it.resource.substringAfterLast("/") } - - // Map MBID to Wikidata ID based on response - // The response structure for /url search is a bit complex. - // It returns URLs, and each URL has relation-list -> relations -> artist -> id - - val mbidToWikidataId = mutableMapOf() - - urls.forEach { url -> - val wikidataId = url.resource.substringAfterLast("/") - val artistId = url.relationList.firstOrNull()?.relations?.firstOrNull()?.artist?.id - if (artistId != null) { - mbidToWikidataId[artistId] = wikidataId - } - } - - // 2. Fetch images for Wikidata IDs - val imagesMap = wikidataRepository.getArtistImages(wikidataIds) - - // 3. Construct result - // We need to return MusicbrainzArtist objects. We might need to fetch details if we don't have them. - // But here we are enriching. - // Logic in search.ht: - // - Get wikidata IDs - // - Fetch images - // - Fetch artist details for artists WITHOUT wikidata link (using /artist search or lookup) - // - Combine - - // Let's search/fetch artist details for all IDs using their MBIDs - // Optimally we can batch fetch if possible, but MB API usually requires individual lookups or search. - // search.ht uses search endpoint with "arid:ID OR arid:ID..." - - val artistsResponse = musicbrainzRepository.searchArtists( - query = artistIds.joinToString(" OR ") { "arid:$it" }, - limit = artistIds.size - ) - - val artists = artistsResponse.artists.map { artist -> - // Check if we have an image for this artist - // We need to know which wikidata ID corresponds to this artist - val wikidataId = mbidToWikidataId[artist.id] - val imageUrl = wikidataId?.let { imagesMap[it] } - - // We don't have a field for 'images' in MusicbrainzArtist model yet. - // We should probably return a Pair or a new model, or just rely on the fact that we can't modify MusicbrainzArtist easily if it's data class. - // But wait, search.ht returns list of items which are maps. - - // I'll return MusicbrainzArtist, but I need to attach the image somehow. - // I'll assume passing the image URL up is handled by the caller or I'll wrap it. - // But MusicbrainzArtist is a data class. - - // Wait, Kotlin data classes are immutable. - // references used in search.ht: - // return { id: ..., name: ..., images: [url, ...] } - - // So this Enricher should probably return a data structure that holds Artist + Image list. - EnrichedArtist(artist, imageUrl?.let { listOf(it) } ?: emptyList()) - } - - return artists.map { it.artist } // Wait, how to attach image? - } - - data class EnrichedArtist( - val artist: MusicbrainzArtist, - val images: List - ) - - suspend fun getEnrichedArtists(artistIds: List): List { - if (artistIds.isEmpty()) return emptyList() - - // 1. Find Wikidata URLs for these artists - val idsQuery = artistIds.joinToString(" OR ") { "targetid:$it" } - val query = "relationtype:wikidata AND targettype:artist AND ($idsQuery)" - - val urlResponse = musicbrainzRepository.searchUrls( - query = query, - limit = artistIds.size - ) - - val urls = urlResponse.urls - - val mbidToWikidataId = mutableMapOf() - urls.forEach { url -> - val wikidataId = url.resource.substringAfterLast("/") - val artistId = url.relationList.firstOrNull()?.relations?.firstOrNull()?.artist?.id - if (artistId != null) { - mbidToWikidataId[artistId] = wikidataId - } - } - - val wikidataIds = mbidToWikidataId.values.toList() - - // 2. Fetch images for Wikidata IDs - val imagesMap = if (wikidataIds.isNotEmpty()) { - wikidataRepository.getArtistImages(wikidataIds) - } else { - emptyMap() - } - - // 3. Fetch artist details - // search.ht fetches "missingArtistIds" (artists without wikidata link) separately. - // But here we can just fetch ALL artists using one query (arid:...) because we need details for all of them anyway. - // search.ht fetches "artistWithImages" from wikidata info (it gets artist object from /url response relation), - // and "artistWithoutImages" from /artist search. - // The /url response relation includes the artist object! - - // Let's follow search.ht optimization: - // Use artists from /url response for those that have wikidata. - // Use /artist search for those that don't. - - val artistsFromUrl = urls.mapNotNull { url -> - val artist = url.relationList.firstOrNull()?.relations?.firstOrNull()?.artist - val wikidataId = url.resource.substringAfterLast("/") - val imageUrl = imagesMap[wikidataId] - - if (artist != null) { - EnrichedArtist(artist, imageUrl?.let { listOf(it) } ?: emptyList()) - } else null - } - - val foundArtistIds = artistsFromUrl.map { it.artist.id }.toSet() - val missingRequestIds = artistIds.filter { !foundArtistIds.contains(it) } - - val artistsFromSearch = if (missingRequestIds.isNotEmpty()) { - val artistsResponse = musicbrainzRepository.searchArtists( - query = missingRequestIds.joinToString(" OR ") { "arid:$it" }, - limit = missingRequestIds.size - ) - artistsResponse.artists.map { EnrichedArtist(it, emptyList()) } - } else { - emptyList() - } - - return artistsFromUrl + artistsFromSearch - } -} - diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/musicbrainz/MusicbrainzRepository.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/musicbrainz/MusicbrainzRepository.kt deleted file mode 100644 index 752f6e7c..00000000 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/musicbrainz/MusicbrainzRepository.kt +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.musicbrainz - -import io.ktor.client.HttpClient -import io.ktor.client.request.get -import io.ktor.client.request.header -import io.ktor.client.request.parameter -import io.ktor.client.request.url -import io.ktor.client.statement.bodyAsText -import io.ktor.client.statement.request -import io.ktor.client.statement.HttpResponse -import io.ktor.http.HttpHeaders -import io.ktor.http.isSuccess -import kotlinx.serialization.json.Json - -interface MusicbrainzRepository { - suspend fun searchRecordings( - query: String, - limit: Int = 25, - offset: Int = 0, - ): MusicbrainzRecordingSearchResponse - - suspend fun searchArtists( - query: String, - limit: Int = 25, - offset: Int = 0, - ): MusicbrainzArtistSearchResponse - - suspend fun searchReleases( - query: String, - limit: Int = 25, - offset: Int = 0, - ): MusicbrainzReleaseSearchResponse - - suspend fun searchReleaseGroups( - query: String, - limit: Int = 25, - offset: Int = 0, - ): MusicbrainzReleaseGroupSearchResponse - - suspend fun searchUrls( - query: String, - limit: Int = 25, - ): MusicbrainzUrlResponse - - suspend fun getRecordingByMbid( - mbid: String, - includes: List = emptyList(), - ): MusicbrainzRecording - - suspend fun getArtistByMbid( - mbid: String, - includes: List = emptyList(), - ): MusicbrainzArtist - - suspend fun getReleaseByMbid( - mbid: String, - includes: List = emptyList(), - ): MusicbrainzRelease - - suspend fun lookupIsrc( - isrc: String, - includes: List = emptyList(), - ): MusicbrainzIsrcLookupResponse -} - -class KtorMusicbrainzRepository( - private val httpClient: HttpClient, - private val baseUrl: String = "https://musicbrainz.org", - private val userAgent: String = "Spotube/0.1 (https://github.com/KRTirtho/spotube)", -) : MusicbrainzRepository { - private val json = Json { - ignoreUnknownKeys = true - isLenient = true - } - - override suspend fun searchRecordings( - query: String, - limit: Int, - offset: Int, - ): MusicbrainzRecordingSearchResponse { - val payload = fetch( - endpoint = "recording", - query = query, - limit = limit, - offset = offset, - ) - return json.decodeFromString(payload) - } - - override suspend fun searchArtists( - query: String, - limit: Int, - offset: Int, - ): MusicbrainzArtistSearchResponse { - val payload = fetch( - endpoint = "artist", - query = query, - limit = limit, - offset = offset, - ) - return json.decodeFromString(payload) - } - - override suspend fun searchReleases( - query: String, - limit: Int, - offset: Int, - ): MusicbrainzReleaseSearchResponse { - val payload = fetch( - endpoint = "release", - query = query, - limit = limit, - offset = offset, - ) - return json.decodeFromString(payload) - } - - override suspend fun searchReleaseGroups( - query: String, - limit: Int, - offset: Int, - ): MusicbrainzReleaseGroupSearchResponse { - val payload = fetch( - endpoint = "release-group", - query = query, - limit = limit, - offset = offset, - ) - return json.decodeFromString(payload) - } - - override suspend fun searchUrls( - query: String, - limit: Int, - ): MusicbrainzUrlResponse { - val payload = fetch( - endpoint = "url", - query = query, - limit = limit, - ) - return json.decodeFromString(payload) - } - - override suspend fun getRecordingByMbid( - mbid: String, - includes: List, - ): MusicbrainzRecording { - val payload = fetch(endpoint = "recording/${mbid.trim()}", inc = includes) - return json.decodeFromString(payload) - } - - override suspend fun getArtistByMbid( - mbid: String, - includes: List, - ): MusicbrainzArtist { - val payload = fetch(endpoint = "artist/${mbid.trim()}", inc = includes) - return json.decodeFromString(payload) - } - - override suspend fun getReleaseByMbid( - mbid: String, - includes: List, - ): MusicbrainzRelease { - val payload = fetch(endpoint = "release/${mbid.trim()}", inc = includes) - return json.decodeFromString(payload) - } - - override suspend fun lookupIsrc( - isrc: String, - includes: List, - ): MusicbrainzIsrcLookupResponse { - val payload = fetch(endpoint = "isrc/${isrc.trim()}", inc = includes) - return json.decodeFromString(payload) - } - - private suspend fun fetch( - endpoint: String, - query: String? = null, - limit: Int? = null, - offset: Int? = null, - inc: List = emptyList(), - ): String { - val response = httpClient.get { - url("${baseUrl.trimEnd('/')}/ws/2/$endpoint") - header(HttpHeaders.Accept, "application/json") - header(HttpHeaders.UserAgent, userAgent) - parameter("fmt", "json") - query?.takeIf { it.isNotBlank() }?.let { parameter("query", it) } - limit?.let { parameter("limit", it) } - offset?.let { parameter("offset", it) } - inc.toMusicbrainzInc()?.let { parameter("inc", it) } - } - - return response.requireBody() - } -} - -class MusicbrainzApiException( - val statusCode: Int, - message: String, -) : RuntimeException(message) - -private suspend fun HttpResponse.requireBody(): String { - val text = bodyAsText() - if (status.isSuccess()) return text - - throw MusicbrainzApiException( - statusCode = status.value, - message = "MusicBrainz request failed (${status.value}) for ${request.url}. Response: ${text.take(512)}" - ) -} - -private fun List.toMusicbrainzInc(): String? { - val cleaned = map { it.trim() }.filter { it.isNotBlank() } - if (cleaned.isEmpty()) return null - return cleaned.joinToString(separator = "+") -} diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/wikidata/WikidataModels.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/wikidata/WikidataModels.kt deleted file mode 100644 index 4f256aa6..00000000 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/wikidata/WikidataModels.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.wikidata - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.JsonElement - -@Serializable -data class WikidataEntityValue( - val value: String? = null -) - -@Serializable -data class WikidataEntityDataValue( - val value: String? = null // It can be object but for P18 (image) it is string (filename) -) - -@Serializable -data class WikidataEntitySnak( - val datavalue: WikidataEntityDataValue? = null -) - -@Serializable -data class WikidataEntityClaim( - val mainsnak: WikidataEntitySnak? = null -) - -@Serializable -data class WikidataEntity( - val id: String, - val claims: Map>? = null -) - -@Serializable -data class WikidataEntitiesResponse( - val entities: Map = emptyMap() -) - -@Serializable -data class WikimediaImageInfo( - val thumburl: String? = null, - val thumbwidth: Int? = null, - val thumbheight: Int? = null, - val url: String? = null, - val width: Int? = null, - val height: Int? = null -) - -@Serializable -data class WikimediaPage( - val pageid: Long, - val title: String, - val imageinfo: List? = null -) - -@Serializable -data class WikimediaQuery( - val pages: Map = emptyMap() -) - -@Serializable -data class WikimediaResponse( - val query: WikimediaQuery? = null -) - diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/wikidata/WikidataRepository.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/wikidata/WikidataRepository.kt deleted file mode 100644 index fad7f7bc..00000000 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/zipline/plugin_apis/musicbrainz_listenbrainz/wikidata/WikidataRepository.kt +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package dev.krtirtho.spotube.core.zipline.plugin_apis.musicbrainz_listenbrainz.wikidata - -import io.ktor.client.HttpClient -import io.ktor.client.request.get -import io.ktor.client.request.parameter -import io.ktor.client.request.url -import io.ktor.client.statement.bodyAsText -import kotlinx.serialization.json.Json - -class WikidataRepository(private val httpClient: HttpClient) { - private val json = Json { - ignoreUnknownKeys = true - isLenient = true - } - - suspend fun getArtistImages(wikidataIds: List): Map { - if (wikidataIds.isEmpty()) return emptyMap() - - try { - // 1. Get image filenames from Wikidata - val wikidataResponseText = httpClient.get { - url("https://www.wikidata.org/w/api.php") - parameter("format", "json") - parameter("props", "claims") - parameter("ids", wikidataIds.joinToString("|")) - parameter("action", "wbgetentities") - }.bodyAsText() - - val wikidataResponse = json.decodeFromString(wikidataResponseText) - - val idsWithImageNames = wikidataIds.map { id -> - val imageName = wikidataResponse.entities[id]?.claims?.get("P18")?.firstOrNull()?.mainsnak?.datavalue?.value - id to imageName - } - - val imageNames = idsWithImageNames.mapNotNull { it.second } - if (imageNames.isEmpty()) return wikidataIds.associateWith { null } - - val titles = imageNames.map { "File:$it" }.joinToString("|") - - // 2. Get image URLs from Wikimedia Commons - val commonsResponseText = httpClient.get { - url("https://commons.wikimedia.org/w/api.php") - parameter("prop", "imageinfo") - parameter("action", "query") - parameter("iiprop", "url|size") - parameter("iiurlheight", 300) - parameter("iiurlwidth", 300) - parameter("format", "json") - parameter("titles", titles) - }.bodyAsText() - - val commonsResponse = json.decodeFromString(commonsResponseText) - val pages = commonsResponse.query?.pages?.values ?: emptyList() - - val imagesMap = mutableMapOf() - - // Initialize all with null - wikidataIds.forEach { imagesMap[it] = null } - - for (page in pages) { - val imageName = page.title.removePrefix("File:") - val imageUrl = page.imageinfo?.firstOrNull()?.thumburl ?: page.imageinfo?.firstOrNull()?.url - - // Find original Wikidata ID for this image name - val wikidataId = idsWithImageNames.firstOrNull { it.second == imageName }?.first - if (wikidataId != null) { - imagesMap[wikidataId] = imageUrl - } - } - - return imagesMap - } catch (e: Exception) { - e.printStackTrace() - return wikidataIds.associateWith { null } - } - } -} - diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/BuiltInPlugins.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/BuiltInPlugins.kt index a78069cf..6ab97e80 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/BuiltInPlugins.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/BuiltInPlugins.kt @@ -29,20 +29,6 @@ val NEWPIPE_YOUTUBE_BUILT_IN_PLUGIN = PluginEntry( ), abilities = listOf(PluginAbility.AUDIO) ) -val MUSICBRAINZ_LISTENBRAINZ_BUILT_IN_PLUGIN = PluginEntry( - name = "MusicBrainz ListenBrainz", - version = "0.1.0", - apiVersion = PLUGIN_API_VERSION, - description = "MusicBrainz ListenBrainz plugin for scrobbling tracks and fetching metadata.", - author = "Spotube Team", - capabilities = listOf( - PluginCapability.NETWORK_REQUESTS, - PluginCapability.PERSISTENT_STORAGE, - PluginCapability.WEBVIEW, - ), - abilities = listOf(PluginAbility.METADATA, PluginAbility.SCROBBLE) -) - val LRCLIB_BUILT_IN_PLUGIN = PluginEntry( name = "LRCLib Lyrics", version = "0.1.0", @@ -55,7 +41,6 @@ val LRCLIB_BUILT_IN_PLUGIN = PluginEntry( abilities = listOf(PluginAbility.LYRICS) ) val BUILT_IN_PLUGINS = listOf( - MUSICBRAINZ_LISTENBRAINZ_BUILT_IN_PLUGIN, NEWPIPE_YOUTUBE_BUILT_IN_PLUGIN, LRCLIB_BUILT_IN_PLUGIN, ) diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/PluginManager.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/PluginManager.kt index 39087d0b..767959d5 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/PluginManager.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/PluginManager.kt @@ -103,8 +103,8 @@ class PluginManager( .map { p -> val json = p[DatabaseKeys.PLUGINS_STATE_KEY] val defaultSelectedPlugins = mapOf( - PluginAbility.METADATA to MUSICBRAINZ_LISTENBRAINZ_BUILT_IN_PLUGIN, PluginAbility.AUDIO to NEWPIPE_YOUTUBE_BUILT_IN_PLUGIN, + PluginAbility.SCROBBLE to LRCLIB_BUILT_IN_PLUGIN, ) if (json == null) { PluginManagerStates.Data( diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dbc5c8f1..99253336 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -67,7 +67,6 @@ material3-window-size = "1.9.0" kotlinx-datetime = "0.7.1" media3 = "1.8.0" androidx-datastore = "1.2.1" -kmpgen = "1.3.0" ksp = "2.3.5" spotube-gradle = "0.1.0" cryptography = "0.6.0" @@ -127,6 +126,8 @@ ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "k ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" } ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" } +ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" } +ktor-client-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" } @@ -171,7 +172,6 @@ kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", versio jetbrainsKotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "jetbrainsKotlinJvm" } androidKotlinMultiplatformLibrary = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" } zipline-gradle-plugin = { id = "app.cash.zipline", version.ref = "ziplineVersion" } -kmpgen = { id = "com.kroegerama.openapi-kmp-gen", version.ref = "kmpgen" } mavenPublish = { id = "maven-publish" } spotubeGradle = { id = "dev.krtirtho.spotube.gradle-plugin", version.ref = "spotube-gradle" } vlcjBundler = { id = "dev.krtirtho.vlcj-bundler.gradle-plugin", version.ref = "vlcjBundler" } \ No newline at end of file