Drei Luecken derselben Familie, die dazu fuehrten, dass dieselben Songs ein zweites Mal geladen wurden: - Kuenstlernamen wurden zeichengenau verglichen: "Cécile Corbel" und "Cecile Corbel" galten als zwei Personen. Welche Schreibweise ankommt, haengt an der Quelle (Deezer vs. Spotify). Der Vergleich normalisiert jetzt Akzente weg — nur der Vergleich, Tags und Dateinamen behalten das Original. - Typografische Apostrophe: "Arrietty’s Song" (U+2019) und "Arrietty's Song" fielen auseinander. Fiel beim Testen auf, betrifft viele Soundtracks. - findExistingTrack durchsucht nur den Ordner des uebergebenen Kuenstlers. Bei Soundtracks weicht der Album- vom Titel-Kuenstler ab, der Song landet je nach Ladeweg unter dem einen oder anderen — geprueft wurde jeweils der falsche. Weicht der Titel-Kuenstler ab, wird nun auch dessen Ordner geprueft (~0,6 ms bei 28.801 Dateien, gegenueber 3-9 s pro Download). Sieben neue Tests zielen gezielt auf Fehlalarme: aehnliche Namen (Cecil Taylor vs. Cécile Corbel), gleicher Titel bei verschiedenen Kuenstlern, Remix gegen Basistitel, fremder Titel im Soundtrack-Ordner — alle bleiben getrennt. Gesamte Suite: 42 Tests gruen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
116 lines
6.0 KiB
JavaScript
116 lines
6.0 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);
|
|
});
|