fix: wire _findFileByStem into both file resolution fallback chains
Handles "File not found" for songs with non-standard folder/artist names (e.g. artist separator • or year-only album folders) by doing a deep recursive title-stem search as a last resort after rebase and dir scan. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
8989b15767
commit
c405465f5c
1 file changed
+31
-2
@@ -91,6 +91,31 @@ function _findFileInDir(dir, basename) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Last-resort: deep recursive search by title stem (handles folder renames after metadata edit).
|
||||||
|
// Convention: MusicRoot/AlbumArtist/Album/{01 - }Artist - Title.ext
|
||||||
|
// The title stem is everything after the optional "NN - Artist - " prefix.
|
||||||
|
// We match any audio file whose stem ends with the same title words.
|
||||||
|
function _findFileByStem(dir, titleStem, depth = 0) {
|
||||||
|
if (depth > 4) return null;
|
||||||
|
try {
|
||||||
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
const found = _findFileByStem(path.join(dir, entry.name), titleStem, depth + 1);
|
||||||
|
if (found) return found;
|
||||||
|
} else if (AUDIO_EXT_RE.test(entry.name)) {
|
||||||
|
// Strip extension, strip leading "NN - ", then strip optional "Artist - " prefix
|
||||||
|
const bare = entry.name.replace(AUDIO_EXT_RE, '').replace(/^\d+\s*-\s*/, '');
|
||||||
|
// bare may be "Artist - Title" or just "Title" — match from the end
|
||||||
|
const bareStem = bare.toLowerCase();
|
||||||
|
if (bareStem === titleStem || bareStem.endsWith(' - ' + titleStem) || bareStem.endsWith(titleStem)) {
|
||||||
|
return path.join(dir, entry.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// ─── QUEUE ────────────────────────────────────────────────────────────────────
|
// ─── QUEUE ────────────────────────────────────────────────────────────────────
|
||||||
let activeDownloads = 0;
|
let activeDownloads = 0;
|
||||||
const downloadQueue = [];
|
const downloadQueue = [];
|
||||||
@@ -701,9 +726,11 @@ app.get('/api/song-sorttags', async (req, res) => {
|
|||||||
const fileName = path.basename(filePath);
|
const fileName = path.basename(filePath);
|
||||||
const rebased = _rebasePath(filePath, CONFIG.musicPath);
|
const rebased = _rebasePath(filePath, CONFIG.musicPath);
|
||||||
const rebasedDir = rebased ? path.dirname(rebased) : null;
|
const rebasedDir = rebased ? path.dirname(rebased) : null;
|
||||||
|
const titleStem = fileName.replace(AUDIO_EXT_RE, '').replace(/^\d+\s*-\s*/, '').toLowerCase();
|
||||||
const found = rebased
|
const found = rebased
|
||||||
|| _findFileRecursive(CONFIG.musicPath, fileName)
|
|| _findFileRecursive(CONFIG.musicPath, fileName)
|
||||||
|| _findFileInDir(rebasedDir || path.dirname(filePath), fileName);
|
|| _findFileInDir(rebasedDir || path.dirname(filePath), fileName)
|
||||||
|
|| _findFileByStem(CONFIG.musicPath, titleStem);
|
||||||
if (found) filePath = found; else return res.status(404).json({ error: 'File not found' });
|
if (found) filePath = found; else return res.status(404).json({ error: 'File not found' });
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -834,9 +861,11 @@ app.post('/api/edit-metadata', async (req, res) => {
|
|||||||
// Also check the original dir — the file may have been renamed (e.g. artist added to filename)
|
// Also check the original dir — the file may have been renamed (e.g. artist added to filename)
|
||||||
// but Navidrome's index is stale and still points to the old name.
|
// but Navidrome's index is stale and still points to the old name.
|
||||||
const originalDir = path.dirname(filePath);
|
const originalDir = path.dirname(filePath);
|
||||||
|
const titleStem = fileName.replace(AUDIO_EXT_RE, '').replace(/^\d+\s*-\s*/, '').toLowerCase();
|
||||||
const found = rebased
|
const found = rebased
|
||||||
|| _findFileRecursive(CONFIG.musicPath, fileName)
|
|| _findFileRecursive(CONFIG.musicPath, fileName)
|
||||||
|| _findFileInDir(rebasedDir || originalDir, fileName);
|
|| _findFileInDir(rebasedDir || originalDir, fileName)
|
||||||
|
|| _findFileByStem(CONFIG.musicPath, titleStem);
|
||||||
if (found) { filePath = found; }
|
if (found) { filePath = found; }
|
||||||
else return res.status(404).json({ error: `File not found: ${filePath} (music root: ${CONFIG.musicPath})` });
|
else return res.status(404).json({ error: `File not found: ${filePath} (music root: ${CONFIG.musicPath})` });
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user