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.
9.0 kB · 224 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224import { assert, expect } from 'chai';import { describe, it } from 'mocha';
import { generateArtistsStr, } from "../../../core/tests/utils/PlayTestUtils.ts";import { parseArtistCredits, parseCredits, parseTrackCredits, uniqueNormalizedStrArr } from "../../utils/StringUtils.ts";import testData from '../utils/playTestData.json' with { type: "json" };import { intersect } from "../../utils.ts";import type {ExpectedResults} from "../utils/interfaces.ts";
interface PlayTestFixture { caseHints: string[] data: { track: string artists: string[] album?: string } expected: ExpectedResults}
describe('#PlayParse Parsing Artists from String', function() {
it('Parses Artists from an Artist-like string', function () { for(const i of Array(40)) { const [str, primaries, secondaries] = generateArtistsStr({primary: {max: 3, ambiguousJoinedNames: true, trailingAmpersand: true, finalJoiner: false}}); const credits = parseArtistCredits(str); const allArtists = primaries.concat(secondaries); const parsed = [credits!.primary].concat(credits!.secondary ?? []); expect(primaries.concat(secondaries),`'${str}'Expected => ${allArtists.join(' || ')}Found => ${parsed.join(' || ')}`)
.eql(parsed) } });
it('Parses & as "local" joiner when other delimiters present', function () {
const data = [ { str: `Melendi \\ Ryan Lewis \\ The Righteous Brothers (featuring Joan Jett & The Blackhearts \\ Robin Schulz)`, expected: ['Melendi', 'Ryan Lewis', 'The Righteous Brothers', 'Joan Jett & The Blackhearts', 'Robin Schulz'] }, { str: `Gigi D'Agostino \\ YOASOBI (vs Sam Hunt, Lisa Loeb & Booba)`, expected: [`Gigi D'Agostino`, 'YOASOBI', 'Sam Hunt', 'Lisa Loeb', 'Booba'] }, { str: `Wham!, Hillsong Worship & Bruce Channel feat. I Prevail`, expected: ['Wham!', 'Hillsong Worship & Bruce Channel', 'I Prevail'] } ];
for(const d of data) { const credits = parseArtistCredits(d.str); const parsed = [credits!.primary].concat(credits!.secondary ?? []) expect(d.expected).eql(parsed) } });
it('Only parses & as "global" joiner when no other delimiters present', function () {
const data = [{ str: `Melendi & Ryan Lewis & The Righteous Brothers (featuring The Blackhearts \\ Robin Schulz)`, expected: ['Melendi', 'Ryan Lewis', 'The Righteous Brothers', 'The Blackhearts', 'Robin Schulz'] }];
for(const d of data) { const credits = parseArtistCredits(d.str); const parsed = [credits!.primary].concat(credits!.secondary ?? []) expect(d.expected).eql(parsed) } });
it('Does not split artist name when only one joiner is present', function () {
const data = [ { str: `Melendi & Ryan Lewis`, expected: ['Melendi & Ryan Lewis'] },{ str: `Melendi and Ryan Lewis`, expected: ['Melendi and Ryan Lewis'] }, ];
for(const d of data) { const credits = parseArtistCredits(d.str); const parsed = [credits!.primary].concat(credits!.secondary ?? []) expect(d.expected).eql(parsed) } });
it('Parses secondary free regex', function () {
const data = [{ str: `Diddy & Grand Funk Railroad feat. Daya & (G)I-DLE`, expected: ['Diddy', 'Grand Funk Railroad', 'Daya', '(G)I-DLE'] }];
for(const d of data) { const credits = parseArtistCredits(d.str); const parsed = [credits!.primary].concat(credits!.secondary ?? []) expect(d.expected).eql(parsed) } });
it('Parses singlar Artist with wrapped vs multiple', function () { const [str, primaries, secondaries] = generateArtistsStr({primary: 1, secondary: {num: 2, ft: 'vs', joiner: '/', ftWrap: true}}); const credits = parseArtistCredits(str); const moreCredits = parseCredits(str); expect(true).eq(true); });
describe('When joiner is known', function () {
it('Parses many primary artists', function () { for(const i of Array(40)) { const [str, primaries, secondaries] = generateArtistsStr({primary: {max: 3, joiner: '/'}, secondary: 0}); const credits = parseArtistCredits(str, ['/']); const allArtists = primaries.concat(secondaries); const parsed = [credits!.primary].concat(credits!.secondary ?? []) expect(primaries.concat(secondaries),`'${str}'Expected => ${allArtists.join(' || ')}Found => ${parsed.join(' || ')}`) .eql(parsed) } });
it('Parses many secondary artists', function () { // fails on -- Peso Pluma / Lil Baby / R. Kelly (featuring TOMORROW X TOGETHER / AC/DC / DaVido) for(const i of Array(40)) { const [str, primaries, secondaries] = generateArtistsStr({primary: {max: 3, joiner: '/'}, secondary: {joiner: '/', finalJoiner: false}}); const credits = parseArtistCredits(str, ['/']); const allArtists = primaries.concat(secondaries); const parsed = [credits!.primary].concat(credits!.secondary ?? []) expect(primaries.concat(secondaries),`'${str}'Expected => ${allArtists.join(' || ')}Found => ${parsed.join(' || ')}Primaries => ${primaries.join(' || ')}Secondaries => ${secondaries.join(' || ')}`) .eql(parsed) } });
it('Parses edge case artists formats', function () { const edgeCases: ReturnType<typeof generateArtistsStr>[] = [ [ 'Paul Whiteman / BROCKHAMPTON / The Rolling Stones featuring Sonu Nigam / Supertramp / Cliff Edwards (Ukelele Ike)', ['Paul Whiteman','BROCKHAMPTON','The Rolling Stones'], ['Sonu Nigam','Supertramp','Cliff Edwards (Ukelele Ike)'] ], [ 'Stray Kids / Mario Lanza vs Cliff Edwards (Ukelele Ike)', ['Stray Kids','Mario Lanza'], ['Cliff Edwards (Ukelele Ike)'] ], [ 'Peso Pluma / Lil Baby / R. Kelly (featuring TOMORROW X TOGETHER / AC/DC / DaVido)', ['Peso Pluma','Lil Baby', 'R. Kelly'], ['TOMORROW X TOGETHER', 'AC/DC','DaVido'] ], [ 'Roy Orbison featuring Snow Patrol / The Detroit Spinners / Cliff Edwards (Ukelele Ike)', ['Roy Orbison'], ['Snow Patrol','The Detroit Spinners','Cliff Edwards (Ukelele Ike)'] ], [ 'Stray Kids / Mario Lanza (featuring Snow Patrol / Cliff Edwards (Ukelele Ike))', ['Stray Kids','Mario Lanza'], ['Snow Patrol','Cliff Edwards (Ukelele Ike)'] ], [ 'Cliff Edwards (Ukelele Ike) / Stray Kids feat. Snow Patrol', ['Cliff Edwards (Ukelele Ike)','Stray Kids'], ['Snow Patrol'] ], ]; for(const edge of edgeCases) { const [str, primaries, secondaries] = edge; const credits = parseArtistCredits(str, ['/']); const allArtists = primaries.concat(secondaries); const parsed = [credits!.primary].concat(credits!.secondary ?? []) expect(primaries.concat(secondaries),`'${str}'Expected => ${allArtists.join(' || ')}Found => ${parsed.join(' || ')}`) .eql(parsed) } });
});
});
describe('Play Track Strings',function () {
it('should keep unwrapped trailing parenthetical as track suffix', function() { const res = parseTrackCredits('Criminal mind Ft Akon (Remix Braquer vos têtes)')!; expect(res.secondary).eql(['Akon']); expect(res.primaryComposite).eq('Criminal mind (Remix Braquer vos têtes)'); });
const testFixtures = testData as unknown as PlayTestFixture[]; const joinerData = testFixtures.filter(x => intersect(['joiner','track'], x.caseHints).length === 2);
it('should parse joiners from track title', function() { for(const test of joinerData) { const res = parseTrackCredits(test.data.track)!; let artists: string[] = [...test.data.artists]; if(res.secondary !== undefined) { artists = uniqueNormalizedStrArr([...artists, ...res.secondary]); } assert.equal(res.primaryComposite, test.expected.track); assert.sameDeepMembers(artists, test.expected.artists); } });});