···11import { stringSameness } from '@foxxmd/string-sameness';
22import dayjs from "dayjs";
33import request, { Request, Response } from 'superagent';
44-import { PlayObject, URLData } from "../../../core/Atomic.js";
44+import { BrainzMeta, PlayObject, URLData } from "../../../core/Atomic.js";
55import { combinePartsToString, slice } from "../../../core/StringUtils.js";
66import {
77 findDelimiters,
···2424import { log } from 'console';
2525import { version } from '../../ioc.js';
26262727+/*
2828+ * https://musicbrainz.org/doc/MusicBrainz_Database/Schema#Overview
2929+*/
3030+3131+/** A unique product a Recording is issued on.
3232+ *
3333+ * This is like an album (release group) but is specific to the type, year, catalog, etc... for this release
3434+ *
3535+ * EX: 1984 US release of "The Wall" by "Pink Floyd", release on label "Columbia Records" with catalog number "C2K 36183"
3636+ *
3737+ * @see https://musicbrainz.org/doc/Release
3838+ *
3939+ * Referred to in MB api response as release_mbid
4040+ *
4141+*/
4242+type ReleaseMbid = string;
4343+4444+/** The "abstract", non-unique album/single/EP the Recording belongs to
4545+ *
4646+ * This is what people normally think of as an album (release group)
4747+ *
4848+ * EX: "The Wall" by "Pink Floyd"
4949+ *
5050+ * @see https://musicbrainz.org/doc/Release
5151+ *
5252+ * Referred to in MB api response as release_group -> mbid
5353+ *
5454+*/
5555+type ReleaseGroupMbid = string;
5656+5757+/** A unique mix/edit/master of a Work
5858+ *
5959+ * This is like a song but is unique to the master/edit of the song
6060+ *
6161+ *
6262+ * * Album version of the track "Into the Blue" by "Moby"
6363+ * * Remix "Into the Blue (Buzz Boys Main Room Mayhem mix)" by "Moby"
6464+ *
6565+ * @see https://musicbrainz.org/doc/Recording
6666+ *
6767+ * Referred to in MB api response as recording_mbid
6868+ */
6969+type RecordingMbid = string;
7070+7171+/** The "abstract", non-unique Song produced by an Artist
7272+ *
7373+ * All Recordings "belong" to a single Work
7474+ *
7575+ * EX: Song "Into the Blue" by "Moby"
7676+ *
7777+ * @see Song "Into the Blue" by "Moby"
7878+ */
7979+type WorkMbid = string;
8080+8181+/** A musician or group or musicians that release music
8282+ *
8383+ * @see https://musicbrainz.org/doc/Artist
8484+ *
8585+ * MB does not distinguish between Artist and Album Artists in API responses except for by release_artist_name in additional_info
8686+ * All artists/album artists are included in mbid_mappings artists
8787+ *
8888+*/
8989+type ArtistMbid = string;
9090+9191+/** A unique, random identifier used for each scrobble. Not the same as recording_mbid */
9292+type RecordingMsid = string;
27932894export interface ArtistMBIDMapping {
2995 artist_credit_name: string
3030- artist_mbid: string
9696+ artist_mbid: ArtistMbid
3197 join_phrase: string
3298}
3399···38104}
3910540106export interface AdditionalTrackInfo {
4141- artist_mbids?: string[]
4242- release_mbid?: string
4343- release_group_mbid?: string
4444- recording_mbid?: string
107107+ artist_mbids?: ArtistMbid[]
108108+ release_mbid?: ReleaseMbid
109109+ release_group_mbid?: ReleaseGroupMbid
110110+ recording_mbid?: RecordingMbid
45111 submission_client?: string
46112 submission_client_version?: string
47113 spotify_id?: string
···5612257123 duration_ms?: number
58124 track_mbid?: string
5959- work_mbids?: string[]
125125+ work_mbids?: WorkMbid[]
60126}
6112762128export interface Track {
63129 artist_name: string;
64130 track_name: string;
65131 release_name?: string;
6666- artist_mbids?: string[];
6767- artist_msid?: string;
132132+ artist_mbids?: ArtistMbid[];
133133+ artist_msid?: ArtistMbid;
68134 recording_mbid?: string;
6969- release_mbid?: string;
135135+ release_mbid?: ReleaseMbid;
70136 release_msid?: string;
71137 tags?: string[];
72138···74140}
7514176142export interface AdditionalTrackInfoResponse extends AdditionalTrackInfo {
7777- recording_msid?: string
143143+ recording_msid?: RecordingMsid
144144+ release_artist_name?: string
145145+ release_artists_names?: string
78146}
7914780148// using submit-listens example from openapi https://rain0r.github.io/listenbrainz-openapi/index.html#/lbCore/submitListens
···9616497165export interface ListenPayload {
98166 listened_at: Date | number;
9999- recording_msid?: string;
167167+ recording_msid?: RecordingMsid;
100168 track_metadata: TrackPayload;
101169}
102170···117185 additional_info: AdditionalTrackInfoResponse
118186 mbid_mapping: {
119187 recording_name?: string
120120- artist_mbids?: string[]
188188+ artist_mbids?: ArtistMbid[]
121189 artists?: ArtistMBIDMapping[]
122190 caa_id?: number
191191+ /** cover album archive mbid, not related to anything else I think */
123192 caa_release_mbid?: string
124124- recording_mbid?: string
125125- release_mbid?: string
193193+ recording_mbid?: RecordingMbid
194194+ release_mbid?: ReleaseMbid
126195 }
127196}
128197···135204136205 inserted_at: number
137206 listened_at: number;
138138- recording_msid?: string;
207207+ recording_msid?: RecordingMsid;
139208 track_metadata: TrackResponse;
140209}
141210export class ListenbrainzApiClient extends AbstractApiClient {
···398467 mbid_mapping: {
399468 recording_name,
400469 artists: artistMappings = [],
401401- recording_mbid: mRecordingMbid
470470+ recording_mbid: mRecordingMbid,
471471+ release_mbid
472472+ } = {},
473473+ additional_info: {
474474+ release_artists_names = [],
475475+ release_group_mbid
402476 } = {}
403477 } = {}
404478 } = listen;
···419493420494 let filteredSubmittedArtistName = artist_name;
421495 let filteredSubmittedTrackName = track_name;
496496+497497+ let primaryArtist;
422498423499 if(artistMappings.length > 0) {
424500···516592 // now we check that primary artist exists in mapped artists
517593 const candidatedPrimaryArtist = artistsFromUserValues[0];
518594 const normalizedCandidatePrimary = normalizeStr(candidatedPrimaryArtist);
519519- const primaryArtist = mappedArtists.find(x => {
595595+ primaryArtist = mappedArtists.find(x => {
520596 if(normalizeStr(x) === normalizedCandidatePrimary)
521597 {
522598 return true;
···536612 // now we have the primary artist matched!
537613 // at this point we assume any differences between user and MB artists is a data discrepancy rather than being outright wrong
538614615615+ /*
616616+ *
617617+ * Can safely use musicbrainz metadata beyond this point!
618618+ *
619619+ */
620620+539621 // next we match up user artists with mapped artists in order to use "more correct" spelling/punctuation/etc. from MB mapping
540622 const mappedUserArtists: string[] = [];
541623 for(const userArtist of artistsFromUserValues) {
···597679 normalTrackName = recording_name;
598680 }
599681600600- return {
682682+ const brainzMetaRaw: BrainzMeta = {
683683+ ...(naivePlay.data.meta.brainz ?? {}),
684684+ artist: artistMappings.map(x => x.artist_mbid),
685685+ album: release_mbid,
686686+ releaseGroup: release_group_mbid
687687+ }
688688+689689+ // this should always find an artist but to be safe...
690690+ const primaryArtistMBMapping = artistMappings.find(x => x.artist_credit_name === primaryArtist);
691691+ if(primaryArtistMBMapping !== undefined) {
692692+ // only include as primary if musicbrainz does not disagree with us
693693+ if(release_artists_names.length === 0 || (release_artists_names.length > 0 && release_artists_names.includes(primaryArtist))) {
694694+ brainzMetaRaw.albumArtist = primaryArtistMBMapping.artist_mbid;
695695+ }
696696+ }
697697+698698+ const brainzMeta = removeUndefinedKeys(brainzMetaRaw);
699699+700700+ const play: PlayObject = {
601701 data: {
602702 ...naivePlay.data,
603703 track: normalTrackName,
604604- artists: derivedArtists,
704704+ artists: derivedArtists
605705 },
606706 meta: naivePlay.meta
607707 }
708708+709709+ if(brainzMeta !== undefined) {
710710+ play.data.meta = {
711711+ brainz: brainzMeta
712712+ }
713713+ }
714714+715715+ return play;
608716 }
609717610718 /**
···662770 }
663771 artists = uniqueNormalizedStrArr(artists);
664772665665- return {
773773+ const play: PlayObject = {
666774 data: {
667775 playDate: dayjs.unix(listened_at),
668776 track: normalTrackName,
···672780 },
673781 meta: {
674782 source: 'listenbrainz',
675675- trackId,
676783 playId,
677784 deviceId: combinePartsToString([music_service_name ?? music_service, submission_client, submission_client_version])
678785 }
679786 }
787787+788788+ // we shouldn't include more metdata here because we don't know if the MB mapped data is actually correct
789789+ if(trackId !== undefined) {
790790+ play.data.meta = {
791791+ brainz: {
792792+ track: trackId
793793+ }
794794+ }
795795+ play.meta.trackid = trackId;
796796+ }
797797+798798+ return play;
680799 }
681800682801 static submitToPlayObj(submitObj: SubmitPayload, playObj: PlayObject): PlayObject {
+18
src/core/Atomic.ts
···6666 end: ListenProgress
6767}
68686969+/** https://musicbrainz.org/doc/MusicBrainz_Database/Schema#Overview */
6970export interface BrainzMeta {
7171+ /**
7272+ * artist_mbids
7373+ *
7474+ * All artists, including ft guests etc... go here */
7075 artist?: string[]
7676+ /**
7777+ * artists_mbid
7878+ *
7979+ * If multiple artists for track this is the "original" artist who is releasing the single/album */
7180 albumArtist?: string
8181+ /**
8282+ * release_mbid
8383+ *
8484+ * The unique release like --> 1984 US release of "The Wall" by "Pink Floyd", release on label "Columbia Records" with catalog number "C2K 36183"
8585+ * */
7286 album?: string
8787+ /** Unique track id, recording_mbid */
7388 track?: string
8989+ /**
9090+ *
9191+ * The "consolidated" album like --> "The Wall" by "Pink Floyd" */
7492 releaseGroup?: string
7593}
7694