feat: rename file and move to correct folder on metadata edit

After saving metadata with ffmpeg:
1. Title change → rename the mp3 in place to "{01 - }?{sanitized title}.ext"
   (same convention as the downloader uses)
2. Album-artist or album change → compute new {artistFirstPart}/{album}
   directory, move the file there (create dirs as needed)
3. Old album dir is deleted if empty after the move; old artist dir
   is deleted too if it also becomes empty

When only the album name changes (no artist change) the old in-place
folder-rename behaviour is preserved.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Sonnet 4.6 committed 2026-05-23 22:12:50 +02:00
1 parent 17414ca888
commit 5c69a8137c
1 file changed
+45 -8
+45 -8
View File
@@ -684,16 +684,53 @@ app.post('/api/edit-metadata', async (req, res) => {
fs.renameSync(tmpFile, filePath); fs.renameSync(tmpFile, filePath);
if (artFile) try { fs.unlinkSync(artFile); } catch {} if (artFile) try { fs.unlinkSync(artFile); } catch {}
// Rename album folder if album changed and folder name matches old album dir // ── Post-save: rename file and/or move to correct artist/album folder ──
if (album) { let currentFile = filePath;
const dir = path.dirname(filePath);
const newDirName = sanitize(album); // 1. Rename file if title changed — convention: {01 - }?{sanitized title}.ext
if (path.basename(dir) !== newDirName) { if (title) {
const newDir = path.join(path.dirname(dir), newDirName); const safeTitle = sanitize(title);
if (!fs.existsSync(newDir)) { const prefix = trackNum ? String(trackNum).padStart(2, '0') + ' - ' : '';
try { fs.renameSync(dir, newDir); } catch (e) { console.error('[edit-metadata] folder rename:', e.message); } const newName = `${prefix}${safeTitle}${fileExt}`;
const newPath = path.join(path.dirname(currentFile), newName);
if (newPath !== currentFile && !fs.existsSync(newPath)) {
try { fs.renameSync(currentFile, newPath); currentFile = newPath; }
catch (e) { console.error('[edit-metadata] file rename:', e.message); }
} }
} }
// 2. Move to new artist/album folder when albumArtist or album changed
const oldAlbumDir = path.dirname(currentFile);
const oldArtistDir = path.dirname(oldAlbumDir);
const newAlbumName = album ? sanitize(album) : path.basename(oldAlbumDir);
const newArtistName = albumArtist ? sanitize(albumArtist.split(/[,;]/)[0].trim()) : path.basename(oldArtistDir);
const newDir = path.join(CONFIG.musicPath, newArtistName, newAlbumName);
if (newDir !== oldAlbumDir) {
try {
fs.mkdirSync(newDir, { recursive: true });
const destFile = path.join(newDir, path.basename(currentFile));
if (!fs.existsSync(destFile)) {
fs.renameSync(currentFile, destFile);
currentFile = destFile;
}
// Remove old album dir if now empty, then old artist dir if also empty
const oldAlbumLeft = fs.readdirSync(oldAlbumDir).filter(f => !f.startsWith('.'));
if (oldAlbumLeft.length === 0) {
try { fs.rmdirSync(oldAlbumDir); } catch {}
try {
const oldArtistLeft = fs.readdirSync(oldArtistDir).filter(f => !f.startsWith('.'));
if (oldArtistLeft.length === 0) fs.rmdirSync(oldArtistDir);
} catch {}
}
} catch (e) { console.error('[edit-metadata] move:', e.message); }
} else if (album && path.basename(oldAlbumDir) !== sanitize(album)) {
// Only album name changed (no artist move) — rename album folder in place
const renamedDir = path.join(oldArtistDir, sanitize(album));
if (!fs.existsSync(renamedDir)) {
try { fs.renameSync(oldAlbumDir, renamedDir); }
catch (e) { console.error('[edit-metadata] folder rename:', e.message); }
}
} }
// Trigger Navidrome rescan // Trigger Navidrome rescan