173 lines
8.5 KiB
JavaScript
173 lines
8.5 KiB
JavaScript
const { test, before, after } = require('node:test');
|
||
const assert = require('node:assert/strict');
|
||
const fs = require('fs');
|
||
const os = require('os');
|
||
const path = require('path');
|
||
const { findExistingTrack } = require('../lib/metadata');
|
||
|
||
let ROOT;
|
||
const touch = (artist, album, file) => {
|
||
const dir = path.join(ROOT, artist, album);
|
||
fs.mkdirSync(dir, { recursive: true });
|
||
fs.writeFileSync(path.join(dir, file), '');
|
||
};
|
||
|
||
before(() => {
|
||
ROOT = fs.mkdtempSync(path.join(os.tmpdir(), 'ns-dedup-'));
|
||
// KH single: normal "01 - " prefix, title contains multiple " - "
|
||
touch('Geek Music', 'Kingdom Hearts 2 - Dearly Beloved - Main Theme - Single',
|
||
'01 - Kingdom Hearts 2 - Dearly Beloved - Main Theme.mp3');
|
||
// KH album variant: garbage "01-01" artist prefix, no track number
|
||
touch('Geek Music', 'Kingdom Hearts 2 - Dearly Beloved - Main Theme',
|
||
'01-01 - Kingdom Hearts 2 - Dearly Beloved - Main Theme.mp3');
|
||
// Normal "Artist - Title" file
|
||
touch('Some Artist', 'Therapy - Single', '01 - Some Artist - Therapy.mp3');
|
||
// Akzent-Schreibweise: liegt MIT Akzent auf der Platte
|
||
touch('Cécile Corbel', 'SongBook Vol. 2', '03 - Cécile Corbel - Arrietty\u2019s Song.mp3');
|
||
// Soundtrack: liegt unter dem ALBUM-Kuenstler, nicht unter dem Titel-Kuenstler
|
||
touch('Arcane', 'Arcane League of Legends (Soundtrack)', '01 - Bea Miller - Playground.mp3');
|
||
// Zwei verschiedene Kuenstler mit demselben Songtitel — duerfen sich NIE gegenseitig treffen
|
||
touch('Cro', 'Raop', '03 - Cro - Easy.mp3');
|
||
touch('Kraftklub', 'Mit K', '05 - Kraftklub - Easy.mp3');
|
||
// Aehnlicher, aber anderer Kuenstlername
|
||
touch('Cecil Taylor', 'Unit Structures', '01 - Cecil Taylor - Steps.mp3');
|
||
});
|
||
after(() => { try { fs.rmSync(ROOT, { recursive: true, force: true }); } catch {} });
|
||
|
||
test('detects a re-download when the title itself contains " - " (KH bug)', () => {
|
||
assert.ok(findExistingTrack(ROOT, 'Geek Music', 'Kingdom Hearts 2 - Dearly Beloved - Main Theme'));
|
||
});
|
||
|
||
test('detects it even with a garbage "01-01 - " artist prefix', () => {
|
||
// Both KH files should resolve; at least one must be found → dedup skips the re-download.
|
||
const hit = findExistingTrack(ROOT, 'Geek Music', 'Kingdom Hearts 2 - Dearly Beloved - Main Theme');
|
||
assert.ok(hit && hit.includes('Kingdom Hearts 2 - Dearly Beloved - Main Theme'));
|
||
});
|
||
|
||
test('normal "Artist - Title" filename still matches on title alone', () => {
|
||
assert.ok(findExistingTrack(ROOT, 'Some Artist', 'Therapy'));
|
||
});
|
||
|
||
test('no false positive: a different title is not matched', () => {
|
||
assert.equal(findExistingTrack(ROOT, 'Geek Music', 'Some Unrelated Song'), null);
|
||
assert.equal(findExistingTrack(ROOT, 'Some Artist', 'Kingdom Hearts 2'), null);
|
||
});
|
||
|
||
test('remix stays distinct from the base track (no regression from the version-marker fix)', () => {
|
||
assert.equal(findExistingTrack(ROOT, 'Some Artist', 'Therapy (Remix)'), null);
|
||
assert.ok(findExistingTrack(ROOT, 'Some Artist', 'Therapy')); // base still found
|
||
});
|
||
|
||
|
||
// ── Akzente (Cécile Corbel) ────────────────────────────────────────────────────
|
||
test('findet den Song auch ohne Akzent in der Anfrage', () => {
|
||
assert.ok(findExistingTrack(ROOT, 'Cecile Corbel', "Arrietty's Song"),
|
||
'ASCII-Schreibweise muss die Datei mit Akzent finden');
|
||
});
|
||
|
||
test('findet ihn ebenso mit Akzent', () => {
|
||
assert.ok(findExistingTrack(ROOT, 'Cécile Corbel', "Arrietty's Song"));
|
||
});
|
||
|
||
test('kein Fehlalarm: aehnlicher Name ist nicht dieselbe Person', () => {
|
||
// "Cecil Taylor" darf nicht auf "Cécile Corbel" passen — und umgekehrt.
|
||
assert.equal(findExistingTrack(ROOT, 'Cécile Corbel', 'Steps'), null);
|
||
assert.equal(findExistingTrack(ROOT, 'Cecil Taylor', "Arrietty's Song"), null);
|
||
});
|
||
|
||
test('kein Fehlalarm: Akzent-Normalisierung kollabiert keine echten Titel', () => {
|
||
assert.equal(findExistingTrack(ROOT, 'Cecile Corbel', 'Ein anderer Song'), null);
|
||
});
|
||
|
||
// ── Gleicher Titel, verschiedene Kuenstler ─────────────────────────────────────
|
||
test('kein Fehlalarm: gleicher Titel bei zwei Kuenstlern bleibt getrennt', () => {
|
||
const cro = findExistingTrack(ROOT, 'Cro', 'Easy');
|
||
const kk = findExistingTrack(ROOT, 'Kraftklub', 'Easy');
|
||
assert.ok(cro && cro.includes('Cro'), 'Cro-Version muss im Cro-Ordner gefunden werden');
|
||
assert.ok(kk && kk.includes('Kraftklub'), 'Kraftklub-Version im Kraftklub-Ordner');
|
||
assert.notEqual(cro, kk, 'die beiden duerfen nicht dieselbe Datei sein');
|
||
// Ein dritter Kuenstler mit demselben Titel findet nichts.
|
||
assert.equal(findExistingTrack(ROOT, 'Irgendwer', 'Easy'), null);
|
||
});
|
||
|
||
// ── Soundtrack: Album-Kuenstler vs. Titel-Kuenstler ────────────────────────────
|
||
test('Soundtrack unter dem Album-Kuenstler wird ueber diesen gefunden', () => {
|
||
assert.ok(findExistingTrack(ROOT, 'Arcane', 'Playground'));
|
||
});
|
||
|
||
test('unter dem Titel-Kuenstler allein wird er NICHT gefunden (Grund fuer die Zweitpruefung)', () => {
|
||
// Genau diese Luecke erzeugte die Dublette: geprueft wurde nur "Bea Miller".
|
||
assert.equal(findExistingTrack(ROOT, 'Bea Miller', 'Playground'), null);
|
||
});
|
||
|
||
test('die Zweitpruefung schliesst die Luecke, ohne Fremdes zu treffen', () => {
|
||
const pruefe = (albumArtist, trackArtist, titel) =>
|
||
findExistingTrack(ROOT, albumArtist, titel)
|
||
|| (trackArtist !== albumArtist ? findExistingTrack(ROOT, trackArtist, titel) : null);
|
||
// Soundtrack-Fall: wird jetzt erkannt.
|
||
assert.ok(pruefe('Bea Miller', 'Bea Miller', 'Playground') === null,
|
||
'ohne Album-Kuenstler weiterhin nichts — richtig, die Datei liegt unter Arcane');
|
||
assert.ok(pruefe('Arcane', 'Bea Miller', 'Playground'), 'mit Album-Kuenstler gefunden');
|
||
// Fremder Titel im selben Ordner darf trotz zweier Durchlaeufe nicht anschlagen.
|
||
assert.equal(pruefe('Arcane', 'Bea Miller', 'Irgendein anderer Titel'), null);
|
||
// Remix bleibt auch ueber zwei Durchlaeufe hinweg eigenstaendig.
|
||
assert.equal(pruefe('Arcane', 'Bea Miller', 'Playground (Remix)'), null);
|
||
});
|
||
|
||
// ─── Dateisuche: Titelstamm und Schraegstrich im Namen ───────────────────────
|
||
// Regression zu "K/DA – VILLAIN": Navidromes Tag-Pfad wurde zu "K/DA/ALL OUT/…", auf der Platte
|
||
// steht "K_DA/ALL OUT/…". Der Pfad fand nichts, und die Aehnlichkeitssuche lieferte
|
||
// "Bad Omens - Like A Villain", weil deren Stamm auf "villain" ENDET.
|
||
const { findFileByStem, albumDirFromTags } = require('../lib/metadata');
|
||
|
||
let SLASH_ROOT;
|
||
before(() => {
|
||
SLASH_ROOT = fs.mkdtempSync(path.join(os.tmpdir(), 'ns-slash-'));
|
||
const put = (...segs) => {
|
||
const file = segs.pop();
|
||
const dir = path.join(SLASH_ROOT, ...segs);
|
||
fs.mkdirSync(dir, { recursive: true });
|
||
fs.writeFileSync(path.join(dir, file), '');
|
||
};
|
||
put('K_DA', 'ALL OUT', '03 - K_DA - VILLAIN.mp3');
|
||
put('K_DA', 'POP_STARS', '01 - K_DA - POP_STARS.mp3');
|
||
put('Bad Omens', 'THE DEATH OF PEACE OF MIND', '06 - Bad Omens - Like A Villain.mp3');
|
||
});
|
||
after(() => { try { fs.rmSync(SLASH_ROOT, { recursive: true, force: true }); } catch {} });
|
||
|
||
test('Titelstamm trifft nicht auf blosse Endsilbe', () => {
|
||
// "Like A Villain" darf NICHT als "VILLAIN" durchgehen
|
||
const t = findFileByStem(path.join(SLASH_ROOT, 'Bad Omens'), 'villain');
|
||
assert.equal(t, null);
|
||
});
|
||
|
||
test('Titelstamm trifft "Kuenstler - Titel" weiterhin', () => {
|
||
const t = findFileByStem(path.join(SLASH_ROOT, 'Bad Omens'), 'like a villain');
|
||
assert.ok(t && t.endsWith('06 - Bad Omens - Like A Villain.mp3'));
|
||
});
|
||
|
||
test('Ganze Bibliothek: VILLAIN findet K_DA, nicht Bad Omens', () => {
|
||
const t = findFileByStem(SLASH_ROOT, 'villain');
|
||
assert.ok(t && t.includes(path.join('K_DA', 'ALL OUT')), `unerwartet: ${t}`);
|
||
});
|
||
|
||
test('Schraegstrich im Kuenstlernamen fuehrt zum richtigen Ordner', () => {
|
||
assert.equal(albumDirFromTags(SLASH_ROOT, 'K/DA', 'ALL OUT'), path.join(SLASH_ROOT, 'K_DA', 'ALL OUT'));
|
||
assert.equal(albumDirFromTags(SLASH_ROOT, 'K/DA', 'POP/STARS'), path.join(SLASH_ROOT, 'K_DA', 'POP_STARS'));
|
||
});
|
||
|
||
test('Unbekanntes Album faellt auf den Kuenstlerordner zurueck', () => {
|
||
assert.equal(albumDirFromTags(SLASH_ROOT, 'K/DA', 'Gibt Es Nicht'), path.join(SLASH_ROOT, 'K_DA'));
|
||
});
|
||
|
||
test('Mehrere Kuenstler: Ordner haengt am ersten', () => {
|
||
assert.equal(
|
||
albumDirFromTags(SLASH_ROOT, 'K/DA, Madison Beer, Kim Petras', 'ALL OUT'),
|
||
path.join(SLASH_ROOT, 'K_DA', 'ALL OUT'),
|
||
);
|
||
});
|
||
|
||
test('Kein Treffer bleibt null', () => {
|
||
assert.equal(albumDirFromTags(SLASH_ROOT, 'Niemand', 'Nichts'), null);
|
||
});
|