From 5c69a8137ce145b6ea0d173669823e111b16bc25 Mon Sep 17 00:00:00 2001 From: Honey Date: Sat, 23 May 2026 22:12:50 +0200 Subject: [PATCH] feat: rename file and move to correct folder on metadata edit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- backend/novasonic-server.js | 53 +++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/backend/novasonic-server.js b/backend/novasonic-server.js index 352ca17..057d1f2 100644 --- a/backend/novasonic-server.js +++ b/backend/novasonic-server.js @@ -684,15 +684,52 @@ app.post('/api/edit-metadata', async (req, res) => { fs.renameSync(tmpFile, filePath); if (artFile) try { fs.unlinkSync(artFile); } catch {} - // Rename album folder if album changed and folder name matches old album dir - if (album) { - const dir = path.dirname(filePath); - const newDirName = sanitize(album); - if (path.basename(dir) !== newDirName) { - const newDir = path.join(path.dirname(dir), newDirName); - if (!fs.existsSync(newDir)) { - try { fs.renameSync(dir, newDir); } catch (e) { console.error('[edit-metadata] folder rename:', e.message); } + // ── Post-save: rename file and/or move to correct artist/album folder ── + let currentFile = filePath; + + // 1. Rename file if title changed — convention: {01 - }?{sanitized title}.ext + if (title) { + const safeTitle = sanitize(title); + const prefix = trackNum ? String(trackNum).padStart(2, '0') + ' - ' : ''; + 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); } } }