Something went wrong. Try again.
[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
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141import { describe, it } from 'mocha';import { expect } from 'chai';import clone from 'clone';import type { PlayObject } from '../../../core/Atomic.ts';import { generateArtistCredits, generatePlay, withBrainz } from '../../../core/tests/utils/PlayTestUtils.ts';import { dedupAlbumArtists, spotifyMeta, stripCredits, withImage, withMetadata } from '../../../core/MusicMetadata.ts';import { trackDataToResult } from '../../common/metadataProviders/MetadataProviderUtils.ts';import { albumSearchResultSchema, artistSearchResultSchema, trackSearchResultSchema } from '../../../core/Api.ts';import { generateAlbumSearchResult, generateArtistSearchResult, generateTrackSearchResult } from '../../../core/tests/utils/apiFixtures.ts';import { nameToCredit } from "../../../core/MusicMetadata.ts";import { metaInvariantTransform, playContentBasicInvariantTransform, playContentCacheHash, playContentInvariantTransform, playDateInvariantTransform, type PlayTransformer} from '../../utils/PlayComparisonUtils.ts';import { hashObject } from '../../utils/StringUtils.ts';
const invariants: Record<string, PlayTransformer> = { metaInvariantTransform, playDateInvariantTransform, playContentInvariantTransform, playContentBasicInvariantTransform};
/** The same play with and without service ids/images on its credits */const playPair = (): [PlayObject, PlayObject] => { const plain = generatePlay({ artists: generateArtistCredits(2, 2, { mbidVal: false }), albumArtists: generateArtistCredits(1, 1, { mbidVal: false }) }); const rich = withBrainz(clone(plain), { include: ['track', 'recording', 'album', 'releaseGroup', 'artist'] }); rich.data.track = withMetadata(rich.data.track, spotifyMeta('abc', 'track')); rich.data.album = withImage(rich.data.album, 'https://example.com/album.jpg'); rich.data.albumArtists = rich.data.albumArtists!.map(x => withImage(withMetadata(x, spotifyMeta('def', 'artist')), 'https://example.com/artist.jpg')); return [plain, rich];}
describe('#Play Invariant Transforms', function () {
for (const [name, transform] of Object.entries(invariants)) {
describe(name, function () {
it('is equal for plays that differ only by credit metadata', function () { const [plain, rich] = playPair(); expect(hashObject(plain.data)).to.not.eq(hashObject(rich.data)); expect(hashObject(transform(rich))).to.eq(hashObject(transform(plain))); });
it('returns credits with only names', function () { const [, rich] = playPair(); const { track, album, artists, albumArtists } = transform(rich).data; expect(hashObject({ track, album, artists, albumArtists })).to.eq(hashObject(stripCredits({ track: rich.data.track, album: rich.data.album, artists: rich.data.artists, albumArtists: rich.data.albumArtists }))); for (const credit of [track, album, ...artists!, ...albumArtists!]) { expect(Object.keys(credit!)).to.eql(['name']); } });
it('is not equal for plays with different names', function () { const [plain] = playPair(); const other = clone(plain); other.data.track = nameToCredit(`${plain.data.track!.name} (Different)`); expect(hashObject(transform(other))).to.not.eq(hashObject(transform(plain)));
const otherArtist = clone(plain); otherArtist.data.artists![0] = nameToCredit(`${plain.data.artists![0].name} (Different)`); expect(hashObject(transform(otherArtist))).to.not.eq(hashObject(transform(plain))); });
it('does not modify the original play', function () { const [, rich] = playPair(); const before = hashObject(rich.data); transform(rich); expect(hashObject(rich.data)).to.eq(before); }); }); }
describe('playContentCacheHash', function () {
it('differs for plays that differ only by credit metadata', function () { const [plain, rich] = playPair(); expect(playContentCacheHash(rich)).to.not.eq(playContentCacheHash(plain)); });
it('is equal for plays that differ only by play date', function () { const [, rich] = playPair(); const later = clone(rich); later.data.playDate = rich.data.playDate!.add(10, 'm'); expect(playContentCacheHash(later)).to.eq(playContentCacheHash(rich)); }); });});
describe('#Album Artists Dedup', function () {
const artists = [nameToCredit('Artist A'), nameToCredit('Artist B')];
it('removes album artists that are all track artists', function () { expect(dedupAlbumArtists({ artists, albumArtists: [nameToCredit(' artist a')] })).to.not.have.property('albumArtists'); expect(dedupAlbumArtists({ artists, albumArtists: [...artists] })).to.not.have.property('albumArtists'); });
it('removes empty album artists', function () { expect(dedupAlbumArtists({ artists, albumArtists: [] })).to.not.have.property('albumArtists'); });
it('keeps album artists when any is not a track artist', function () { const albumArtists = [nameToCredit('Artist A'), nameToCredit('Various Artists')]; expect(dedupAlbumArtists({ artists, albumArtists }).albumArtists).to.eql(albumArtists); expect(dedupAlbumArtists({ albumArtists }).albumArtists).to.eql(albumArtists); });});
describe('#Metadata Search Results', function () {
it('are Credit and TrackData shapes', function () { expect(() => trackSearchResultSchema.parse(generateTrackSearchResult())).to.not.throw(); expect(() => albumSearchResultSchema.parse(generateAlbumSearchResult())).to.not.throw(); expect(() => artistSearchResultSchema.parse(generateArtistSearchResult())).to.not.throw(); });
it('nests artists and album as results from the same service', function () { const artist = nameToCredit('Artist A', spotifyMeta('ar1', 'artist')); const result = trackDataToResult( { track: nameToCredit('Track'), artists: [artist, nameToCredit('Artist B')], album: nameToCredit('Album', spotifyMeta('al1', 'album')) }, { id: 't1', service: 'spotify', score: 90, albumCount: 1 }, { albumType: 'single' } ); expect(result.artists).to.eql([{ ...artist, service: 'spotify', id: 'ar1' }, { name: 'Artist B', service: 'spotify', id: 'Artist B' }]); expect(result.album).to.include({ name: 'Album', service: 'spotify', id: 'al1', albumType: 'single' }); expect(result).to.not.have.property('albumType'); expect(() => trackSearchResultSchema.parse(result)).to.not.throw(); });});