fix: playlist matching for romaji-stored Japanese tracks + punctuation cleanup

- Clean Japanese punctuation (、。・) in romajiForStorage so it doesn't pass through
- Add POST /api/romaji-convert endpoint for batch title conversion
- Add romajiConvert() to api.js
- Add third fallback in _createNdPlaylist: convert Japanese title to romaji and
  search Navidrome with it (for songs already downloaded with romaji tags)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Sonnet 4.6 committed 2026-05-25 01:20:37 +02:00
1 parent c7bebd5691
commit f1eabf7099
4 files changed
+48 -5

No files matched your search

+7 -1
View File
@@ -90,8 +90,14 @@ function romajiForStorage(text) {
const needsSpace = /[a-zA-Z0-9]$/.test(parts[i - 1]) && /^[a-zA-Z]/.test(part);
return acc + (needsSpace ? ' ' : '') + part;
}, '');
// Clean up Japanese punctuation that passes through as-is
const cleaned = joined
.replace(/[、。]/g, ' ') // Japanese comma/period → space
.replace(/[・・]/g, ' ') // middle dot → space
.replace(/[「」『』【】〔〕〈〉《》]/g, '') // Japanese brackets → remove
.replace(/(/g, '(').replace(/)/g, ')'); // fullwidth parens → ASCII
// Title-case: capitalize first lowercase letter of each word, preserve existing uppercase
return joined.replace(/\b([a-z])/g, c => c.toUpperCase()).replace(/\s+/g, ' ').trim();
return cleaned.replace(/\b([a-z])/g, c => c.toUpperCase()).replace(/\s+/g, ' ').trim();
}
// ─── INDEX BUILDER ────────────────────────────────────────────────────────────
+8
View File
@@ -109,6 +109,14 @@ app.delete('/api/romaji-index', (req, res) => {
res.json({ ok: true });
});
// POST /api/romaji-convert — convert an array of strings to romaji
app.post('/api/romaji-convert', (req, res) => {
const { texts } = req.body;
if (!Array.isArray(texts)) return res.status(400).json({ error: 'texts must be array' });
if (!isTokenizerReady()) return res.status(503).json({ error: 'tokenizer not ready' });
res.json({ results: texts.map(t => romajiForStorage(t) || t) });
});
// GET /api/genre-playcounts?ndUrl=...&user=...&pass=...
// Returns { [genre]: totalPlayCount } sorted by plays desc, cached for 20 min.
const _genrePlayCache = new Map();