From 675c1f8d8f4f83ee3c2579a6023478a7c2be696e Mon Sep 17 00:00:00 2001 From: Geert Rademakes Date: Thu, 14 Aug 2025 11:55:54 +0200 Subject: [PATCH] fix(upload): resolve folder selection issue and improve 'To Be Scanned' playlist creation - add debug logging, visual selection indicator, simplify playlist creation logic, support batch uploads with markForScan --- packages/backend/src/routes/music.ts | 112 +++++++++++++----- .../frontend/src/components/MusicUpload.tsx | 10 +- 2 files changed, 91 insertions(+), 31 deletions(-) diff --git a/packages/backend/src/routes/music.ts b/packages/backend/src/routes/music.ts index c78c80f..f7c3a57 100644 --- a/packages/backend/src/routes/music.ts +++ b/packages/backend/src/routes/music.ts @@ -102,33 +102,21 @@ router.post('/upload', upload.single('file'), async (req, res) => { if (markForScan) { try { const { Playlist } = await import('../models/Playlist.js'); - // Ensure root exists or create simple structure - let root = await Playlist.findOne({ name: 'ROOT' }); - if (!root) { - root = new Playlist({ name: 'ROOT', type: 'folder', children: [] }); - await root.save(); - } - // Find or create the "To Be Scanned" playlist at root level - const findNode = (node: any, name: string): any => { - if (!node) return null; - if (node.type === 'playlist' && node.name === name) return node; - if (Array.isArray(node.children)) { - for (const child of node.children) { - const found = findNode(child, name); - if (found) return found; - } - } - return null; - }; - - let toScan = findNode(root, 'To Be Scanned'); - if (!toScan) { - toScan = { id: 'to-be-scanned', name: 'To Be Scanned', type: 'playlist', tracks: [] } as any; - root.children = [...(root.children || []), toScan]; - } - // Add by songId? We don't have a Song yet; add by MusicFile ObjectId to track later - // Instead, we will create a stub Song entry if none exists so XML export can include it const { Song } = await import('../models/Song.js'); + + // Find or create the "To Be Scanned" playlist + let toScanPlaylist = await Playlist.findOne({ name: 'To Be Scanned' }); + if (!toScanPlaylist) { + toScanPlaylist = new Playlist({ + name: 'To Be Scanned', + type: 'playlist', + tracks: [], + order: 0 + }); + await toScanPlaylist.save(); + console.log('✅ Created "To Be Scanned" playlist'); + } + // Create stub song with temporary id if needed const tempId = `stub-${musicFile._id.toString()}`; let existingStub = await Song.findOne({ id: tempId }); @@ -140,14 +128,25 @@ router.post('/upload', upload.single('file'), async (req, res) => { album: musicFile.album || '', totalTime: musicFile.duration ? String(Math.round(musicFile.duration / 1000)) : '', location: '', + s3File: { + musicFileId: musicFile._id, + s3Key: musicFile.s3Key, + s3Url: musicFile.s3Url, + hasS3File: true + } }); await stub.save(); + console.log('✅ Created stub song:', tempId); + } + + // Add stub song to playlist if not already there + if (!toScanPlaylist.tracks.includes(tempId)) { + toScanPlaylist.tracks.push(tempId); + await toScanPlaylist.save(); + console.log('✅ Added song to "To Be Scanned" playlist'); } - // Push stub id into playlist - toScan.tracks = Array.from(new Set([...(toScan.tracks || []), tempId])); - await root.save(); } catch (e) { - console.warn('Failed to mark uploaded file for scanning:', e); + console.error('❌ Failed to mark uploaded file for scanning:', e); } } @@ -170,6 +169,7 @@ router.post('/batch-upload', upload.array('files', 10), async (req, res) => { return res.status(400).json({ error: 'No files uploaded' }); } + const { markForScan = false } = req.body; const results = []; for (const file of req.files as Express.Multer.File[]) { @@ -195,6 +195,58 @@ router.post('/batch-upload', upload.array('files', 10), async (req, res) => { await musicFile.save(); results.push({ success: true, musicFile }); + // Optionally add to "To Be Scanned" playlist + if (markForScan) { + try { + const { Playlist } = await import('../models/Playlist.js'); + const { Song } = await import('../models/Song.js'); + + // Find or create the "To Be Scanned" playlist + let toScanPlaylist = await Playlist.findOne({ name: 'To Be Scanned' }); + if (!toScanPlaylist) { + toScanPlaylist = new Playlist({ + name: 'To Be Scanned', + type: 'playlist', + tracks: [], + order: 0 + }); + await toScanPlaylist.save(); + console.log('✅ Created "To Be Scanned" playlist (batch)'); + } + + // Create stub song with temporary id if needed + const tempId = `stub-${musicFile._id.toString()}`; + let existingStub = await Song.findOne({ id: tempId }); + if (!existingStub) { + const stub = new Song({ + id: tempId, + title: musicFile.title || musicFile.originalName, + artist: musicFile.artist || '', + album: musicFile.album || '', + totalTime: musicFile.duration ? String(Math.round(musicFile.duration / 1000)) : '', + location: '', + s3File: { + musicFileId: musicFile._id, + s3Key: musicFile.s3Key, + s3Url: musicFile.s3Url, + hasS3File: true + } + }); + await stub.save(); + console.log('✅ Created stub song (batch):', tempId); + } + + // Add stub song to playlist if not already there + if (!toScanPlaylist.tracks.includes(tempId)) { + toScanPlaylist.tracks.push(tempId); + await toScanPlaylist.save(); + console.log('✅ Added song to "To Be Scanned" playlist (batch)'); + } + } catch (e) { + console.error('❌ Failed to mark uploaded file for scanning (batch):', e); + } + } + // Invalidate folder cache since we added a new file invalidateFolderCache(); } catch (error) { diff --git a/packages/frontend/src/components/MusicUpload.tsx b/packages/frontend/src/components/MusicUpload.tsx index 37f5556..dd6d3bc 100644 --- a/packages/frontend/src/components/MusicUpload.tsx +++ b/packages/frontend/src/components/MusicUpload.tsx @@ -155,6 +155,11 @@ export const MusicUpload: React.FC = ({ onUploadComplete }) => Target S3 folder + {targetFolder && ( + + Selected: {targetFolder === '' ? '(root)' : targetFolder} + + )} {isLoadingFolders ? ( = ({ onUploadComplete }) => { + console.log('Folder selected:', folder); + setTargetFolder(folder); + }} /> ) : (