fix(backend): Umschrift-Muster toleranter, Kanji-Kuenstler ueber Romaji erkennbar

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
marcandClaude Opus 5 committed 2026-09-25 00:09:01 +02:00
1 parent 06b13caea9
commit 748b1446a2
3 files changed
+49 -4

No files matched your search

+10 -2
View File
@@ -237,8 +237,16 @@ Die Android-App zählt eigenständig weiter (mehrere APKs pro Release). Welche d
- Bleibt am Ende weder ein Titel- noch ein Künstlermerkmal übrig, wird **gar nicht gesucht**: - Bleibt am Ende weder ein Titel- noch ein Künstlermerkmal übrig, wird **gar nicht gesucht**:
ohne Merkmal landet sonst der erste Treffer passender Länge auf der Platte. Der Direktlink im ohne Merkmal landet sonst der erste Treffer passender Länge auf der Platte. Der Direktlink im
Importer ist dafür der vorgesehene Weg. Importer ist dafür der vorgesehene Weg.
- Fünf neue Prüfungen in `backend/test/ytdlp.test.js` (59 grün), darunter „anderes Stück - **Die Umschrift ist nur *eine* Schreibweise.** Der Tokenizer liefert „Ginpatsu No Shounen",
desselben Albums darf nicht passen". Uploader schreiben „Ginpatsu no Shonen", setzen Bindestriche oder lassen den
Katakana-Längungsstrich weg (der bei „テーゼ" sogar unübersetzt stehenbleibt: „TeーZe"). Das
Muster lässt diese Abweichungen zu — langes o/u optional, Längungsstrich weg, Trennzeichen
frei. Sonst wäre die Alternative so streng, dass sie praktisch nie greift.
- Dieselbe Hilfe für **Kanji-Künstlernamen**: Kanäle und Videotitel schreiben sie meist
lateinisch, also zählt die Umschrift jetzt auch als Künstlermerkmal. Vorher blieb dort nur das
`artist`-Feld in Originalschrift.
- Sieben neue Prüfungen in `backend/test/ytdlp.test.js` (61 grün), darunter „anderes Stück
desselben Albums darf nicht passen" und die Schreibvarianten der Umschrift.
- **Das Mischen-Symbol zeigte nie an, dass es an ist (Android)** — und der Grund lag nicht in der - **Das Mischen-Symbol zeigte nie an, dass es an ist (Android)** — und der Grund lag nicht in der
Anzeige. `toggleShuffle` baute den Rest der Warteschlange als Schleife aus `removeMediaItem` + Anzeige. `toggleShuffle` baute den Rest der Warteschlange als Schleife aus `removeMediaItem` +
`addMediaItem` um; dazwischen hatte der Controller vorübergehend weniger Titel als unsere Liste. `addMediaItem` um; dazwischen hatte der Controller vorübergehend weniger Titel als unsere Liste.
+23 -2
View File
@@ -29,7 +29,17 @@ function buildTitleKeywords(title, romaji = '') {
const alternativen = [escRe(title.trim())]; const alternativen = [escRe(title.trim())];
const r = (romaji || '').trim(); const r = (romaji || '').trim();
if (r && r.toLowerCase() !== title.trim().toLowerCase() && !JP_RE.test(r)) { if (r && r.toLowerCase() !== title.trim().toLowerCase() && !JP_RE.test(r)) {
alternativen.push(escRe(r).replace(/\s+/g, '[ -]?')); // Die Umschrift des Tokenizers ist EINE Schreibweise von mehreren. Uploader schreiben
// "Shonen" statt "Shounen", setzen Bindestriche oder lassen den Katakana-Laengungsstrich
// weg. Das Muster laesst diese Abweichungen zu — sonst waere die Alternative so streng,
// dass sie praktisch nie greift.
alternativen.push(
escRe(r)
.replace(/ー/g, '') // kommt in lateinischen Titeln nie vor
.replace(/\s+/g, '[ -]?')
.replace(/ou/gi, '(?:ou|o|\u014d|oh)')
.replace(/uu/gi, '(?:uu|u|\u016b)')
);
} }
return [`(?:${alternativen.join('|')})`]; return [`(?:${alternativen.join('|')})`];
} }
@@ -212,8 +222,19 @@ async function downloadTrack({ jobId, artist, rawArtist, albumArtist, title, alb
// Ausnahme hatte ein japanischer Kuenstler ueberhaupt keine Identitaetspruefung. // Ausnahme hatte ein japanischer Kuenstler ueberhaupt keine Identitaetspruefung.
const artistMetaPart = (artistFirstWord.length >= 3 || (JP_RE.test(artistFirstWord) && artistFirstWord.length >= 2)) const artistMetaPart = (artistFirstWord.length >= 3 || (JP_RE.test(artistFirstWord) && artistFirstWord.length >= 2))
? ` & artist ~= (?i)${escRe(artistFirstWord)}` : ''; ? ` & artist ~= (?i)${escRe(artistFirstWord)}` : '';
// Kanji-Kuenstler: Kanaele und Videotitel schreiben den Namen meist lateinisch. Ohne diese
// Alternative bliebe fuer sie nur das artist-Feld in Originalschrift — und steht dort die
// lateinische Schreibweise, faellt der richtige Upload durch.
const artistRomajiWord = (JP_RE.test(artistFirstWord) && isTokenizerReady())
? ((romajiForStorage(artistFirstWord) || '').split(/\s+/)
.filter(w => w.length >= 3 && !JP_RE.test(w))
.sort((a, b) => b.length - a.length)[0] || '')
: '';
const artistRomajiParts = artistRomajiWord
? [` & channel ~= (?i)${escRe(artistRomajiWord)}`, ` & artist ~= (?i)${escRe(artistRomajiWord)}`]
: [];
// Alle Wege, den Kuenstler zu erkennen. yt-dlp verodert mehrere --match-filter. // Alle Wege, den Kuenstler zu erkennen. yt-dlp verodert mehrere --match-filter.
const identitaet = [artistChannelPart, artistMetaPart].filter(Boolean); const identitaet = [artistChannelPart, artistMetaPart, ...artistRomajiParts].filter(Boolean);
// Use a space (not " - ") between artist and title: this matches how users type // Use a space (not " - ") between artist and title: this matches how users type
// in the YTM search box and ranks the exact official track first. A dash skews // in the YTM search box and ranks the exact official track first. A dash skews
+16
View File
@@ -149,3 +149,19 @@ test('lateinische Titel bleiben unveraendert', () => {
assert.deepEqual(buildTitleKeywords('Bohemian Rhapsody'), ['Bohemian', 'Rhapsody']); assert.deepEqual(buildTitleKeywords('Bohemian Rhapsody'), ['Bohemian', 'Rhapsody']);
assert.deepEqual(buildTitleKeywords('Dearly Beloved'), ['Beloved', 'Dearly']); assert.deepEqual(buildTitleKeywords('Dearly Beloved'), ['Beloved', 'Dearly']);
}); });
test('Umschrift toleriert die ueblichen Schreibvarianten', () => {
const re = new RegExp(buildTitleKeywords('銀髪の少年', 'Ginpatsu No Shounen')[0], 'i');
assert.ok(re.test('Ginpatsu no Shounen'), 'wie der Tokenizer schreibt');
assert.ok(re.test('Ginpatsu no Shonen'), 'ohne langes o — die haeufigste Variante');
assert.ok(re.test('Ginpatsu-no-Shonen'), 'mit Bindestrichen');
assert.ok(re.test('GINPATSU NO SHŌNEN'), 'mit Makron');
assert.ok(!re.test('Ginpatsu no Otona'), 'anderer Titel faellt weiterhin durch');
});
test('Katakana-Laengungsstrich faellt aus der Umschrift', () => {
// romajiForStorage laesst "ー" stehen ("TeーZe"); in einem lateinischen Titel steht es nie.
const re = new RegExp(buildTitleKeywords('残酷な天使のテーゼ', 'Zankoku Na Tenshi No TeーZe')[0], 'i');
assert.ok(re.test('Zankoku na Tenshi no Teze'), 'ohne Strich geschrieben');
assert.ok(re.test('残酷な天使のテーゼ'), 'Originaltitel weiterhin');
});