feat: delete song from disk via edit-metadata modal
- /api/delete-song endpoint: resolves path via same fallback chain as edit-metadata, validates file is inside music root + is an audio file, deletes it, then prunes the album folder if empty and the artist folder if that too becomes empty — no recursive rm, depth strictly limited to two levels above the file so no accidental mass-deletes - Safety: path.resolve() guard ensures no path-traversal outside music root; rmdirSync (non-recursive) fails if dir is non-empty as extra safety net - Frontend: delete button (red outline, trash icon) at bottom of edit mode with confirmation dialog showing title + folder-cleanup warning Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
0788c801fa
commit
a2714dddaa
4 files changed
+118
-2
No files matched your search
+2
-2
@@ -45,7 +45,7 @@ import {
|
||||
toggleMobileSearch, closeMobileSearch, switchSetupTab,
|
||||
toggleNotifPanel, deleteNotif, clearNotifs, initNotifBell, _notifNavigate,
|
||||
_pickTheme,
|
||||
toggleInfoEdit, onInfoArtFile, autofillMusicBrainz, autofillItunes, saveMetadata,
|
||||
toggleInfoEdit, onInfoArtFile, autofillMusicBrainz, autofillItunes, saveMetadata, deleteSong,
|
||||
} from './ui.js';
|
||||
|
||||
// ─── CONNECT ───
|
||||
@@ -316,7 +316,7 @@ Object.assign(window, {
|
||||
loadSoundCloudImporter, resolveSoundCloudLink, clearSoundCloudPreview, scDlAll, scDlSingle, scDlTrack,
|
||||
executeSpotifyDownload, resolveYouTubeLink, executeYouTubeDownload,
|
||||
openPlModal, closePlModal, addSongsToPlaylist, createAndAddPlaylist, createPlaylistPrompt,
|
||||
showSongInfo, closeInfoModal, toggleInfoEdit, onInfoArtFile, autofillMusicBrainz, autofillItunes, saveMetadata,
|
||||
showSongInfo, closeInfoModal, toggleInfoEdit, onInfoArtFile, autofillMusicBrainz, autofillItunes, saveMetadata, deleteSong,
|
||||
openDlModal, closeDlModal, selectDlType, executeDownload,
|
||||
openEditPlModal, closeEditPlModal, saveEditPlaylist,
|
||||
editPlUploadCover, editPlCoverSelected, editPlRemoveCover, deletePlaylist, _confirmOk, _confirmCancel,
|
||||
|
||||
@@ -515,6 +515,32 @@ export async function saveMetadata() {
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteSong() {
|
||||
if (!S.cfg.backendUrl) { toast('⚠ Kein Backend konfiguriert'); return; }
|
||||
const s = S.infoSong;
|
||||
if (!s?.id) return;
|
||||
const ok = await showConfirm(
|
||||
'Song löschen',
|
||||
`"${s.title}" wird unwiderruflich von der Festplatte gelöscht. Leere Album- und Künstlerordner werden ebenfalls entfernt.`,
|
||||
'Löschen'
|
||||
);
|
||||
if (!ok) return;
|
||||
const status = document.getElementById('info-edit-status');
|
||||
status.textContent = '🗑 Löschen…';
|
||||
try {
|
||||
const r = await fetch(`${S.cfg.backendUrl}/api/delete-song`, {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ ndUrl: S.cfg.url, user: S.cfg.user, pass: S.cfg.pass, songId: s.id }),
|
||||
});
|
||||
const d = await r.json();
|
||||
if (!r.ok) throw new Error(d.error || `HTTP ${r.status}`);
|
||||
toast(`🗑 "${s.title}" gelöscht`);
|
||||
closeInfoModal();
|
||||
} catch (e) {
|
||||
status.textContent = '❌ Fehler: ' + e.message;
|
||||
}
|
||||
}
|
||||
|
||||
// ─── DOWNLOAD MODAL ───
|
||||
export function openDlModal(track) {
|
||||
if(!S.cfg.backendUrl) { toast('⚠ Kein Backend konfiguriert. Bitte in Einstellungen eintragen.'); return; }
|
||||
|
||||
Reference in new issue
Block a user