[READ-ONLY] Mirror of https://github.com/FoxxMD/multi-scrobbler. Scrobble plays from multiple sources to multiple clients docs.multi-scrobbler.app
deezer docker jellyfin koito lastfm listenbrainz maloja mopidy mpris music music-assistant plex scrobble self-hosted spotify subsonic tautulli youtube-music
0

Configure Feed

Select the types of activity you want to include in your feed.

refactor: Move original play data to top-level property in play

* Simplifies lifecycle
* Simplifies play generation for testing

FoxxMD (Jun 10, 2026, 1:54 AM UTC) 3bb88776 c65504ee

+49 -499
+8 -4
src/backend/common/database/drizzle/repositories/PlayRepository.ts
··· 833 833 play: { 834 834 meta: { 835 835 lifecycle: { 836 - input, 837 - original, 836 + //input, 837 + //original, 838 838 ...lifecycleRest 839 839 } = {}, 840 840 ...metaRest 841 841 }, 842 + original: { 843 + play: playOriginal, 844 + data: playData 845 + } = {}, 842 846 ...playRest 843 847 }, 844 848 ...rest ··· 857 861 }, 858 862 ...rest, 859 863 input: { 860 - play: original, 861 - data: input 864 + play: playOriginal, 865 + data: playData 862 866 } 863 867 } 864 868 }
+2 -1
src/backend/utils/PlayComparisonUtils.ts
··· 19 19 meta: { 20 20 trackId 21 21 } = {}, 22 + data = {} 22 23 } = play; 23 24 return { 24 - ...play, 25 + data, 25 26 meta: { 26 27 trackId 27 28 }
+5 -5
src/backend/utils/PlayTransformUtils.ts
··· 182 182 seenAt: dayjs(), 183 183 ...play.meta, 184 184 lifecycle: { 185 - input: data, 186 - original: play, 187 185 steps: [] 188 186 } 187 + }, 188 + original: { 189 + input: data, 190 + play, 189 191 } 190 192 } 191 - basePlay.meta.lifecycle.original.meta.seenAt = basePlay.meta.seenAt; 193 + basePlay.original.play.meta.seenAt = basePlay.meta.seenAt; 192 194 return basePlay; 193 195 } 194 196 195 197 export const defaultLifecycle = (extra?: PlayLifecycle): PlayLifecycle => { 196 198 const { 197 - original = {data: {}, meta: {}}, 198 199 steps = [], 199 200 ...rest 200 201 } = extra ?? {}; 201 202 return { 202 - original, 203 203 steps, 204 204 ...rest, 205 205 }
+8 -2
src/core/Atomic.ts
··· 311 311 } 312 312 313 313 export interface PlayLifecycle<D extends DateLike = Dayjs> { 314 - input?: object 315 - original?: PlayObjectLifecycleless<D> 314 + //input?: object 315 + //original?: PlayObjectLifecycleless<D> 316 316 steps: LifecycleStep[] 317 317 scrobble?: ScrobbleResult<D> 318 318 } ··· 357 357 358 358 export type DateLike = Dayjs | string 359 359 360 + export interface PlayOriginal<D extends DateLike = Dayjs> { 361 + data?: object 362 + play?: PlayObjectLifecycleless<D> 363 + } 364 + 360 365 export interface AmbPlayObject<D extends DateLike = Dayjs> { 361 366 id?: number 362 367 uid?: string 363 368 data: PlayData<D>, 364 369 meta: PlayMetaLifecycleless 370 + original?: PlayOriginal<D> 365 371 } 366 372 367 373 export const isPlayObject = (obj: object): obj is PlayObject => {
+16 -16
src/core/PlayTestUtils.ts
··· 78 78 ...defaultMeta 79 79 } 80 80 81 - if(cleanPlay.meta.lifecycle?.original?.data?.playDate !== undefined) { 82 - cleanPlay.meta.lifecycle.original.data.playDate = lastDate; 83 - } 84 - if(cleanPlay.meta.lifecycle?.original?.data?.playDateCompleted !== undefined) { 85 - cleanPlay.meta.lifecycle.original.data.playDate = lastDate.add(cleanPlay.data.listenedFor ?? cleanPlay.data.duration, 's'); 86 - } 81 + // if(cleanPlay.meta.lifecycle?.original?.data?.playDate !== undefined) { 82 + // cleanPlay.meta.lifecycle.original.data.playDate = lastDate; 83 + // } 84 + // if(cleanPlay.meta.lifecycle?.original?.data?.playDateCompleted !== undefined) { 85 + // cleanPlay.meta.lifecycle.original.data.playDate = lastDate.add(cleanPlay.data.listenedFor ?? cleanPlay.data.duration, 's'); 86 + // } 87 87 88 88 if(index + 1 <= plays.length - 1) { 89 89 const listenTime = (plays[index+1].data.duration ?? defaultDuration) + faker.number.int({min: 0, max: 2}); ··· 121 121 ...defaultMeta 122 122 } 123 123 124 - if(cleanPlay.meta.lifecycle?.original?.data?.playDate !== undefined) { 125 - cleanPlay.meta.lifecycle.original.data.playDate = lastDate; 126 - } 127 - if(cleanPlay.meta.lifecycle?.original?.data?.playDateCompleted !== undefined) { 128 - cleanPlay.meta.lifecycle.original.data.playDate = lastDate.add(cleanPlay.data.listenedFor ?? cleanPlay.data.duration, 's'); 129 - } 124 + // if(cleanPlay.meta.lifecycle?.original?.data?.playDate !== undefined) { 125 + // cleanPlay.meta.lifecycle.original.data.playDate = lastDate; 126 + // } 127 + // if(cleanPlay.meta.lifecycle?.original?.data?.playDateCompleted !== undefined) { 128 + // cleanPlay.meta.lifecycle.original.data.playDate = lastDate.add(cleanPlay.data.listenedFor ?? cleanPlay.data.duration, 's'); 129 + // } 130 130 131 131 if(progressDirection === 'newer') { 132 132 const listenTime = (cleanPlay.data.duration ?? defaultDuration) + faker.number.int({min: 0, max: 2}); ··· 189 189 seenAt: dayjs(), 190 190 ...meta, 191 191 lifecycle: { 192 - original: { 193 - data: {}, 194 - meta: {} 195 - }, 192 + // original: { 193 + // data: {}, 194 + // meta: {} 195 + // }, 196 196 steps: [] 197 197 }, 198 198 url: {
+10 -471
src/stories/storyUtils.ts
··· 1 1 import { JsonPlayObject, PlayLifecycle } from "../core/Atomic.js"; 2 2 3 3 const exampleLifecycle = (): PlayLifecycle<string> => ({ 4 - "input": { 5 - "device": { 6 - "id": "3cc6dc47a8551498a70e519ee09cd8423ff9d48c", 7 - "is_active": true, 8 - "is_private_session": false, 9 - "is_restricted": false, 10 - "name": "Pixel 9a", 11 - "supports_volume": false, 12 - "type": "Smartphone", 13 - "volume_percent": 100 14 - }, 15 - "shuffle_state": true, 16 - "smart_shuffle": false, 17 - "repeat_state": "context", 18 - "is_playing": true, 19 - "timestamp": 1773588429309, 20 - "context": { 21 - "external_urls": { 22 - "spotify": "https://open.spotify.com/collection/tracks" 23 - }, 24 - "href": "https://api.spotify.com/v1/me/tracks", 25 - "type": "collection", 26 - "uri": "spotify:user:socrates13:collection" 27 - }, 28 - "progress_ms": 5455, 29 - "item": { 30 - "album": { 31 - "album_type": "album", 32 - "artists": [ 33 - { 34 - "external_urls": { 35 - "spotify": "https://open.spotify.com/artist/4V8LLVI7PbaPR0K2TGSxFF" 36 - }, 37 - "href": "https://api.spotify.com/v1/artists/4V8LLVI7PbaPR0K2TGSxFF", 38 - "id": "4V8LLVI7PbaPR0K2TGSxFF", 39 - "name": "Tyler, The Creator", 40 - "type": "artist", 41 - "uri": "spotify:artist:4V8LLVI7PbaPR0K2TGSxFF" 42 - } 43 - ], 44 - "external_urls": { 45 - "spotify": "https://open.spotify.com/album/2nkto6YNI4rUYTLqEwWJ3o" 46 - }, 47 - "href": "https://api.spotify.com/v1/albums/2nkto6YNI4rUYTLqEwWJ3o", 48 - "id": "2nkto6YNI4rUYTLqEwWJ3o", 49 - "images": [ 50 - { 51 - "height": 640, 52 - "url": "https://i.scdn.co/image/ab67616d0000b2738940ac99f49e44f59e6f7fb3", 53 - "width": 640 54 - }, 55 - { 56 - "height": 300, 57 - "url": "https://i.scdn.co/image/ab67616d00001e028940ac99f49e44f59e6f7fb3", 58 - "width": 300 59 - }, 60 - { 61 - "height": 64, 62 - "url": "https://i.scdn.co/image/ab67616d000048518940ac99f49e44f59e6f7fb3", 63 - "width": 64 64 - } 65 - ], 66 - "name": "Flower Boy", 67 - "release_date": "2017-07-21", 68 - "release_date_precision": "day", 69 - "total_tracks": 14, 70 - "type": "album", 71 - "uri": "spotify:album:2nkto6YNI4rUYTLqEwWJ3o" 72 - }, 73 - "artists": [ 74 - { 75 - "external_urls": { 76 - "spotify": "https://open.spotify.com/artist/4V8LLVI7PbaPR0K2TGSxFF" 77 - }, 78 - "href": "https://api.spotify.com/v1/artists/4V8LLVI7PbaPR0K2TGSxFF", 79 - "id": "4V8LLVI7PbaPR0K2TGSxFF", 80 - "name": "Tyler, The Creator", 81 - "type": "artist", 82 - "uri": "spotify:artist:4V8LLVI7PbaPR0K2TGSxFF" 83 - }, 84 - { 85 - "external_urls": { 86 - "spotify": "https://open.spotify.com/artist/7pbDxGE6nQSZVfiFdq9lOL" 87 - }, 88 - "href": "https://api.spotify.com/v1/artists/7pbDxGE6nQSZVfiFdq9lOL", 89 - "id": "7pbDxGE6nQSZVfiFdq9lOL", 90 - "name": "Rex Orange County", 91 - "type": "artist", 92 - "uri": "spotify:artist:7pbDxGE6nQSZVfiFdq9lOL" 93 - }, 94 - { 95 - "external_urls": { 96 - "spotify": "https://open.spotify.com/artist/1mSJCvDX0W7Dn7S9C6vmvI" 97 - }, 98 - "href": "https://api.spotify.com/v1/artists/1mSJCvDX0W7Dn7S9C6vmvI", 99 - "id": "1mSJCvDX0W7Dn7S9C6vmvI", 100 - "name": "Anna of the North", 101 - "type": "artist", 102 - "uri": "spotify:artist:1mSJCvDX0W7Dn7S9C6vmvI" 103 - } 104 - ], 105 - "disc_number": 1, 106 - "duration_ms": 320720, 107 - "explicit": true, 108 - "external_ids": { 109 - "isrc": "USQX91701279" 110 - }, 111 - "external_urls": { 112 - "spotify": "https://open.spotify.com/track/5WNYg3usc6H8N3MBEp4zVk" 113 - }, 114 - "href": "https://api.spotify.com/v1/tracks/5WNYg3usc6H8N3MBEp4zVk", 115 - "id": "5WNYg3usc6H8N3MBEp4zVk", 116 - "is_local": false, 117 - "name": "Boredom (feat. Rex Orange County & Anna of the North)", 118 - "popularity": 66, 119 - "preview_url": "https://p.scdn.co/mp3-preview/64c36bb59fe8c48d0ed5b38dc5a7b0189c1a642b?cid=f85f5aa4211f4ea78aa364547798b340", 120 - "track_number": 8, 121 - "type": "track", 122 - "uri": "spotify:track:5WNYg3usc6H8N3MBEp4zVk" 123 - }, 124 - "currently_playing_type": "track", 125 - "actions": { 126 - "disallows": { 127 - "resuming": true 128 - } 129 - } 130 - }, 131 - "original": { 132 - "data": { 133 - "artists": [ 134 - "Tyler, The Creator", 135 - "Rex Orange County", 136 - "Anna of the North" 137 - ], 138 - "albumArtists": [ 139 - "Tyler, The Creator" 140 - ], 141 - "album": "Flower Boy", 142 - "track": "Boredom (feat. Rex Orange County & Anna of the North)", 143 - "duration": 320.72, 144 - // @ts-ignore 145 - "playDate": "2026-03-15T15:27:09.309Z", 146 - "isrc": "USQX91701279", 147 - "meta": { 148 - "spotify": { 149 - "track": "5WNYg3usc6H8N3MBEp4zVk", 150 - "artist": [ 151 - "4V8LLVI7PbaPR0K2TGSxFF", 152 - "7pbDxGE6nQSZVfiFdq9lOL", 153 - "1mSJCvDX0W7Dn7S9C6vmvI" 154 - ], 155 - "albumArtist": [ 156 - "4V8LLVI7PbaPR0K2TGSxFF" 157 - ], 158 - "album": "2nkto6YNI4rUYTLqEwWJ3o" 159 - }, 160 - "brainz": { 161 - "trackNumber": 8 162 - } 163 - } 164 - }, 165 - "meta": { 166 - "deviceId": "3cc6dc47a8-Pixel 9a", 167 - "source": "Spotify", 168 - "musicService": "Spotify", 169 - "trackId": "5WNYg3usc6H8N3MBEp4zVk", 170 - "trackProgressPosition": 5.455, 171 - "scrobbleTsSOC": 1, 172 - "newFromSource": true, 173 - "url": { 174 - "web": "https://open.spotify.com/track/5WNYg3usc6H8N3MBEp4zVk" 175 - }, 176 - "art": { 177 - "album": "https://i.scdn.co/image/ab67616d00001e028940ac99f49e44f59e6f7fb3" 178 - } 179 - } 180 - }, 181 4 "steps": [ 182 5 { 183 6 "name": "preCompare", ··· 807 630 "closestMatchedPlay": { 808 631 "data": { 809 632 "artists": [ 810 - "Gorillaz" 633 + {"name": "Gorillaz"} 811 634 ], 812 635 "track": "Tormenta", 813 636 "album": "Cracker Island", ··· 827 650 "web": "https://www.last.fm/music/Gorillaz/_/Tormenta" 828 651 }, 829 652 "lifecycle": { 830 - "input": { 831 - "artist": { 832 - "url": "https://www.last.fm/music/Gorillaz", 833 - "name": "Gorillaz", 834 - "image": [ 835 - { 836 - "size": "small", 837 - "#text": "https://lastfm.freetls.fastly.net/i/u/34s/2a96cbd8b46e442fc41c2b86b821562f.png" 838 - }, 839 - { 840 - "size": "medium", 841 - "#text": "https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png" 842 - }, 843 - { 844 - "size": "large", 845 - "#text": "https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png" 846 - }, 847 - { 848 - "size": "extralarge", 849 - "#text": "https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png" 850 - } 851 - ], 852 - "mbid": "" 853 - }, 854 - "date": { 855 - "uts": "1773520517", 856 - "#text": "14 Mar 2026, 20:35" 857 - }, 858 - "mbid": "", 859 - "name": "Tormenta", 860 - "image": [ 861 - { 862 - "size": "small", 863 - "#text": "https://lastfm.freetls.fastly.net/i/u/34s/507c3f4c1863a05626a445057871b56e.png" 864 - }, 865 - { 866 - "size": "medium", 867 - "#text": "https://lastfm.freetls.fastly.net/i/u/64s/507c3f4c1863a05626a445057871b56e.png" 868 - }, 869 - { 870 - "size": "large", 871 - "#text": "https://lastfm.freetls.fastly.net/i/u/174s/507c3f4c1863a05626a445057871b56e.png" 872 - }, 873 - { 874 - "size": "extralarge", 875 - "#text": "https://lastfm.freetls.fastly.net/i/u/300x300/507c3f4c1863a05626a445057871b56e.png" 876 - } 877 - ], 878 - "url": "https://www.last.fm/music/Gorillaz/_/Tormenta", 879 - "streamable": "0", 880 - "album": { 881 - "mbid": "52518f4a-9543-4f9c-9a86-89052f4bf458", 882 - "#text": "Cracker Island" 883 - }, 884 - "loved": "0" 885 - }, 886 - "original": { 887 - "data": { 888 - "artists": [ 889 - "Gorillaz" 890 - ], 891 - "track": "Tormenta", 892 - "album": "Cracker Island", 893 - // @ts-ignore 894 - "playDate": "2026-03-14T20:35:17.000Z", 895 - "meta": { 896 - "brainz": { 897 - "album": "52518f4a-9543-4f9c-9a86-89052f4bf458" 898 - } 899 - } 900 - }, 901 - "meta": { 902 - "nowPlaying": false, 903 - "mbid": "", 904 - "source": "Lastfm", 905 - "url": { 906 - "web": "https://www.last.fm/music/Gorillaz/_/Tormenta" 907 - } 908 - } 909 - }, 910 653 "steps": [] 911 654 } 912 - } 655 + }, 913 656 } 914 657 }, 915 658 "payload": { ··· 958 701 "mergedScrobble": { 959 702 "data": { 960 703 "artists": [ 961 - "Tyler, The Creator", 962 - "Rex Orange County", 963 - "Anna of the North" 704 + {"name": "Tyler, The Creator"}, 705 + {"name": "Rex Orange County"}, 706 + {"name": "Anna of the North"} 964 707 ], 965 708 "album": "Scum Fuck Flower Boy", 966 709 "track": "Boredom", ··· 1032 775 export const examplePlay = (): JsonPlayObject => ({ 1033 776 "data": { 1034 777 "artists": [ 1035 - "Tyler, The Creator", 1036 - "Rex Orange County", 1037 - "Anna of the North" 778 + {"name": "Tyler, The Creator"}, 779 + {"name": "Rex Orange County"}, 780 + {"name": "Anna of the North"} 1038 781 ], 1039 782 "album": "Scum Fuck Flower Boy", 1040 783 "track": "Boredom", ··· 1102 845 }); 1103 846 1104 847 const lastfmErrorLifcycle: PlayLifecycle<string> = { 1105 - "input": { 1106 - "track": { 1107 - "album": { 1108 - "album_type": "single", 1109 - "artists": [ 1110 - { 1111 - "external_urls": { 1112 - "spotify": "https://open.spotify.com/artist/4l8MDydHy2RGwcGscG0uCB" 1113 - }, 1114 - "href": "https://api.spotify.com/v1/artists/4l8MDydHy2RGwcGscG0uCB", 1115 - "id": "4l8MDydHy2RGwcGscG0uCB", 1116 - "name": "Mpho.Wav", 1117 - "type": "artist", 1118 - "uri": "spotify:artist:4l8MDydHy2RGwcGscG0uCB" 1119 - } 1120 - ], 1121 - "external_urls": { 1122 - "spotify": "https://open.spotify.com/album/1TCD8jrQdFclnpYVQxjon2" 1123 - }, 1124 - "href": "https://api.spotify.com/v1/albums/1TCD8jrQdFclnpYVQxjon2", 1125 - "id": "1TCD8jrQdFclnpYVQxjon2", 1126 - "images": [ 1127 - { 1128 - "height": 640, 1129 - "url": "https://i.scdn.co/image/ab67616d0000b273b0e9752208d1c426fcda8820", 1130 - "width": 640 1131 - }, 1132 - { 1133 - "height": 300, 1134 - "url": "https://i.scdn.co/image/ab67616d00001e02b0e9752208d1c426fcda8820", 1135 - "width": 300 1136 - }, 1137 - { 1138 - "height": 64, 1139 - "url": "https://i.scdn.co/image/ab67616d00004851b0e9752208d1c426fcda8820", 1140 - "width": 64 1141 - } 1142 - ], 1143 - "name": "Into The Deep", 1144 - "release_date": "2026-02-27", 1145 - "release_date_precision": "day", 1146 - "total_tracks": 3, 1147 - "type": "album", 1148 - "uri": "spotify:album:1TCD8jrQdFclnpYVQxjon2" 1149 - }, 1150 - "artists": [ 1151 - { 1152 - "external_urls": { 1153 - "spotify": "https://open.spotify.com/artist/4l8MDydHy2RGwcGscG0uCB" 1154 - }, 1155 - "href": "https://api.spotify.com/v1/artists/4l8MDydHy2RGwcGscG0uCB", 1156 - "id": "4l8MDydHy2RGwcGscG0uCB", 1157 - "name": "Mpho.Wav", 1158 - "type": "artist", 1159 - "uri": "spotify:artist:4l8MDydHy2RGwcGscG0uCB" 1160 - } 1161 - ], 1162 - "disc_number": 1, 1163 - "duration_ms": 318875, 1164 - "explicit": false, 1165 - "external_ids": { 1166 - "isrc": "US23A9495906" 1167 - }, 1168 - "external_urls": { 1169 - "spotify": "https://open.spotify.com/track/3Uar9flTUlakmFt9nkJdNZ" 1170 - }, 1171 - "href": "https://api.spotify.com/v1/tracks/3Uar9flTUlakmFt9nkJdNZ", 1172 - "id": "3Uar9flTUlakmFt9nkJdNZ", 1173 - "is_local": false, 1174 - "name": "Bab'omdala", 1175 - "popularity": 26, 1176 - "preview_url": "https://p.scdn.co/mp3-preview/ec6e58ff47870192adfa02cd02b963e269ff330e?cid=f85f5aa4211f4ea78aa364547798b340", 1177 - "track_number": 1, 1178 - "type": "track", 1179 - "uri": "spotify:track:3Uar9flTUlakmFt9nkJdNZ" 1180 - }, 1181 - "played_at": "2026-03-10T18:20:34.606Z", 1182 - "context": { 1183 - "type": "album", 1184 - "external_urls": { 1185 - "spotify": "https://open.spotify.com/album/4JgbPlF06nOTO03mSBSgtq" 1186 - }, 1187 - "href": "https://api.spotify.com/v1/albums/4JgbPlF06nOTO03mSBSgtq", 1188 - "uri": "spotify:album:4JgbPlF06nOTO03mSBSgtq" 1189 - } 1190 - }, 1191 - "original": { 1192 - "data": { 1193 - "album": "Into The Deep", 1194 - "track": "Bab'omdala", 1195 - "duration": 318.875, 1196 - "isrc": "US23A9495906", 1197 - "artists": [ 1198 - "Mpho.Wav" 1199 - ], 1200 - "albumArtists": [], 1201 - // @ts-ignore 1202 - "playDate": "2026-03-10T18:20:34.606Z", 1203 - // @ts-ignore 1204 - "playDateCompleted": "2026-03-10T18:20:34.606Z", 1205 - "meta": { 1206 - "spotify": { 1207 - "track": "3Uar9flTUlakmFt9nkJdNZ", 1208 - "artist": [ 1209 - "4l8MDydHy2RGwcGscG0uCB" 1210 - ], 1211 - "albumArtist": [], 1212 - "album": "1TCD8jrQdFclnpYVQxjon2" 1213 - }, 1214 - "brainz": { 1215 - "trackNumber": 1 1216 - } 1217 - } 1218 - }, 1219 - "meta": { 1220 - "deviceId": "NoDevice-SingleUser", 1221 - "source": "Spotify", 1222 - "musicService": "Spotify", 1223 - "trackId": "3Uar9flTUlakmFt9nkJdNZ", 1224 - "scrobbleTsSOC": 2, 1225 - "newFromSource": false, 1226 - "url": { 1227 - "web": "https://open.spotify.com/track/3Uar9flTUlakmFt9nkJdNZ" 1228 - }, 1229 - "art": { 1230 - "album": "https://i.scdn.co/image/ab67616d00001e02b0e9752208d1c426fcda8820" 1231 - } 1232 - } 1233 - }, 1234 848 "steps": [ 1235 849 { 1236 850 "name": "preCompare", ··· 1278 892 "closestMatchedPlay": { 1279 893 "data": { 1280 894 "artists": [ 1281 - "Monique Bingham" 895 + {"name": "Monique Bingham"} 1282 896 ], 1283 897 "track": "Deep In The Bottom (of Africa) (feat. Black Coffee) - JazzWRLD Remix Edit (Radio Edit)", 1284 898 "album": "Deep In The Bottom (Of Africa) The Remixes [Pt. 1]", ··· 1293 907 "web": "https://www.last.fm/music/Monique+Bingham/_/Deep+In+The+Bottom+(of+Africa)+(feat.+Black+Coffee)+-+JazzWRLD+Remix+Edit+(Radio+Edit)" 1294 908 }, 1295 909 "lifecycle": { 1296 - "input": { 1297 - "artist": { 1298 - "url": "https://www.last.fm/music/Monique+Bingham", 1299 - "name": "Monique Bingham", 1300 - "image": [ 1301 - { 1302 - "size": "small", 1303 - "#text": "https://lastfm.freetls.fastly.net/i/u/34s/2a96cbd8b46e442fc41c2b86b821562f.png" 1304 - }, 1305 - { 1306 - "size": "medium", 1307 - "#text": "https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png" 1308 - }, 1309 - { 1310 - "size": "large", 1311 - "#text": "https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png" 1312 - }, 1313 - { 1314 - "size": "extralarge", 1315 - "#text": "https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png" 1316 - } 1317 - ], 1318 - "mbid": "" 1319 - }, 1320 - "date": { 1321 - "uts": "1773166515", 1322 - "#text": "10 Mar 2026, 18:15" 1323 - }, 1324 - "mbid": "", 1325 - "name": "Deep In The Bottom (of Africa) (feat. Black Coffee) - JazzWRLD Remix Edit (Radio Edit)", 1326 - "image": [ 1327 - { 1328 - "size": "small", 1329 - "#text": "https://lastfm.freetls.fastly.net/i/u/34s/2a96cbd8b46e442fc41c2b86b821562f.png" 1330 - }, 1331 - { 1332 - "size": "medium", 1333 - "#text": "https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png" 1334 - }, 1335 - { 1336 - "size": "large", 1337 - "#text": "https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png" 1338 - }, 1339 - { 1340 - "size": "extralarge", 1341 - "#text": "https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png" 1342 - } 1343 - ], 1344 - "url": "https://www.last.fm/music/Monique+Bingham/_/Deep+In+The+Bottom+(of+Africa)+(feat.+Black+Coffee)+-+JazzWRLD+Remix+Edit+(Radio+Edit)", 1345 - "streamable": "0", 1346 - "album": { 1347 - "mbid": "", 1348 - "#text": "Deep In The Bottom (Of Africa) The Remixes [Pt. 1]" 1349 - }, 1350 - "loved": "0" 1351 - }, 1352 - "original": { 1353 - "data": { 1354 - "track": "Deep In The Bottom (of Africa) (feat. Black Coffee) - JazzWRLD Remix Edit (Radio Edit)", 1355 - "album": "Deep In The Bottom (Of Africa) The Remixes [Pt. 1]", 1356 - "artists": [ 1357 - "Monique Bingham" 1358 - ], 1359 - // @ts-ignore 1360 - "playDate": "2026-03-10T18:15:15.000Z" 1361 - }, 1362 - "meta": { 1363 - "nowPlaying": false, 1364 - "mbid": "", 1365 - "source": "Lastfm", 1366 - "url": { 1367 - "web": "https://www.last.fm/music/Monique+Bingham/_/Deep+In+The+Bottom+(of+Africa)+(feat.+Black+Coffee)+-+JazzWRLD+Remix+Edit+(Radio+Edit)" 1368 - } 1369 - } 1370 - }, 1371 910 "steps": [] 1372 911 } 1373 912 } ··· 1383 922 "duration": 318.875, 1384 923 "isrc": "US23A9495906", 1385 924 "artists": [ 1386 - "Mpho.Wav" 925 + {"name": "Mpho.Wav"} 1387 926 ], 1388 927 "albumArtists": [], 1389 928 // @ts-ignore