diff --git a/packages/backend/debug-mp3-files.js b/packages/backend/debug-mp3-files.js new file mode 100644 index 0000000..4ef9b07 --- /dev/null +++ b/packages/backend/debug-mp3-files.js @@ -0,0 +1,116 @@ +import { createClient } from 'webdav'; +import fs from 'fs'; + +async function debugMP3Files() { + try { + // Load configuration + const configData = fs.readFileSync('storage-config.json', 'utf-8'); + const config = JSON.parse(configData); + + console.log('🔍 WebDAV Configuration:'); + console.log('URL:', config.url); + console.log('Username:', config.username); + console.log('Base Path:', config.basePath); + console.log(''); + + // Create WebDAV client + const client = createClient(config.url, { + username: config.username, + password: config.password, + }); + + console.log('🔗 Testing connection...'); + const basePath = config.basePath || '/Music'; + + try { + // Test deep listing + console.log('📁 Testing deep directory listing for MP3 files...'); + const deepContents = await client.getDirectoryContents(basePath, { deep: true }); + + console.log('Deep listing response type:', typeof deepContents); + console.log('Is array:', Array.isArray(deepContents)); + + if (Array.isArray(deepContents)) { + console.log('Total items found:', deepContents.length); + + // Filter for MP3 files specifically + const mp3Files = deepContents.filter(item => { + if (item && typeof item === 'object' && 'type' in item && item.type === 'file') { + const filename = item.basename || item.filename.split('/').pop() || ''; + return filename.toLowerCase().endsWith('.mp3'); + } + return false; + }); + + console.log('🎵 MP3 Files found:', mp3Files.length); + console.log(''); + + // Show first 20 MP3 files + console.log('🎵 First 20 MP3 files:'); + mp3Files.slice(0, 20).forEach((file, index) => { + const relativePath = file.filename.replace(basePath + '/', ''); + console.log(` ${index + 1}. ${relativePath} (${file.size} bytes)`); + }); + + if (mp3Files.length > 20) { + console.log(` ... and ${mp3Files.length - 20} more MP3 files`); + } + + // Check if there are any patterns in the file paths + console.log(''); + console.log('📁 Directory distribution of MP3 files:'); + const dirCounts = new Map(); + mp3Files.forEach(file => { + const relativePath = file.filename.replace(basePath + '/', ''); + const dir = relativePath.split('/')[0]; + dirCounts.set(dir, (dirCounts.get(dir) || 0) + 1); + }); + + const sortedDirs = Array.from(dirCounts.entries()).sort((a, b) => b[1] - a[1]); + sortedDirs.forEach(([dir, count]) => { + console.log(` 📁 ${dir}: ${count} MP3 files`); + }); + + // Test if there's a limit by checking specific directories + console.log(''); + console.log('🔍 Testing specific directories for MP3 files...'); + + const testDirs = ['Gekocht', 'Merijn Music', 'Musica']; + for (const testDir of testDirs) { + try { + const dirPath = `${basePath}/${testDir}`; + const dirContents = await client.getDirectoryContents(dirPath, { deep: true }); + const dirItems = Array.isArray(dirContents) ? dirContents : [dirContents]; + const dirMp3Files = dirItems.filter(item => { + if (item && typeof item === 'object' && 'type' in item && item.type === 'file') { + const filename = item.basename || item.filename.split('/').pop() || ''; + return filename.toLowerCase().endsWith('.mp3'); + } + return false; + }); + console.log(` 📁 ${testDir}: ${dirMp3Files.length} MP3 files`); + } catch (error) { + console.log(` ❌ ${testDir}: Error - ${error.message}`); + } + } + + } else { + console.log('❌ Deep listing returned non-array response:', deepContents); + } + + } catch (error) { + console.error('❌ Error during WebDAV operations:', error); + console.error('Error details:', error.message); + if (error.response) { + console.error('Response status:', error.response.status); + console.error('Response data:', error.response.data); + } + } + + } catch (error) { + console.error('❌ Failed to load configuration or create client:', error); + } +} + +// Run the debug function +debugMP3Files().catch(console.error); diff --git a/packages/backend/debug-webdav-files.js b/packages/backend/debug-webdav-files.js new file mode 100644 index 0000000..3709196 --- /dev/null +++ b/packages/backend/debug-webdav-files.js @@ -0,0 +1,122 @@ +import { createClient } from 'webdav'; +import fs from 'fs'; + +async function debugWebDAVFiles() { + try { + // Load configuration + const configData = fs.readFileSync('storage-config.json', 'utf-8'); + const config = JSON.parse(configData); + + console.log('🔍 WebDAV Configuration:'); + console.log('URL:', config.url); + console.log('Username:', config.username); + console.log('Base Path:', config.basePath); + console.log(''); + + // Create WebDAV client + const client = createClient(config.url, { + username: config.username, + password: config.password, + }); + + console.log('🔗 Testing connection...'); + const basePath = config.basePath || '/Music'; + + try { + const baseContents = await client.getDirectoryContents(basePath); + console.log('✅ Connection successful'); + console.log('Base directory contents count:', Array.isArray(baseContents) ? baseContents.length : 1); + console.log(''); + + // Test deep listing + console.log('📁 Testing deep directory listing...'); + const deepContents = await client.getDirectoryContents(basePath, { deep: true }); + + console.log('Deep listing response type:', typeof deepContents); + console.log('Is array:', Array.isArray(deepContents)); + + if (Array.isArray(deepContents)) { + console.log('Total items found:', deepContents.length); + + // Count files vs directories + let fileCount = 0; + let dirCount = 0; + const fileExtensions = new Map(); + const directories = new Set(); + + for (const item of deepContents) { + if (item && typeof item === 'object' && 'type' in item) { + if (item.type === 'file') { + fileCount++; + + // Track file extensions + const ext = item.basename?.split('.').pop()?.toLowerCase() || 'unknown'; + fileExtensions.set(ext, (fileExtensions.get(ext) || 0) + 1); + } else if (item.type === 'directory') { + dirCount++; + const relativePath = item.filename.replace(basePath + '/', ''); + if (relativePath) { + directories.add(relativePath); + } + } + } + } + + console.log('📊 File Statistics:'); + console.log('Files:', fileCount); + console.log('Directories:', dirCount); + console.log('Total items:', fileCount + dirCount); + console.log(''); + + console.log('📁 Directory structure (first 20):'); + const sortedDirs = Array.from(directories).sort(); + sortedDirs.slice(0, 20).forEach(dir => { + console.log(' 📁', dir); + }); + if (sortedDirs.length > 20) { + console.log(` ... and ${sortedDirs.length - 20} more directories`); + } + console.log(''); + + console.log('🎵 File extensions:'); + const sortedExts = Array.from(fileExtensions.entries()).sort((a, b) => b[1] - a[1]); + sortedExts.forEach(([ext, count]) => { + console.log(` .${ext}: ${count} files`); + }); + console.log(''); + + // Show some sample files + console.log('🎵 Sample files (first 10):'); + const sampleFiles = deepContents + .filter(item => item && typeof item === 'object' && 'type' in item && item.type === 'file') + .slice(0, 10); + + sampleFiles.forEach((file, index) => { + const relativePath = file.filename.replace(basePath + '/', ''); + console.log(` ${index + 1}. ${relativePath} (${file.size} bytes)`); + }); + + if (fileCount > 10) { + console.log(` ... and ${fileCount - 10} more files`); + } + + } else { + console.log('❌ Deep listing returned non-array response:', deepContents); + } + + } catch (error) { + console.error('❌ Error during WebDAV operations:', error); + console.error('Error details:', error.message); + if (error.response) { + console.error('Response status:', error.response.status); + console.error('Response data:', error.response.data); + } + } + + } catch (error) { + console.error('❌ Failed to load configuration or create client:', error); + } +} + +// Run the debug function +debugWebDAVFiles().catch(console.error); diff --git a/packages/backend/src/services/audioMetadataService.ts b/packages/backend/src/services/audioMetadataService.ts index c9c9d53..d635b13 100644 --- a/packages/backend/src/services/audioMetadataService.ts +++ b/packages/backend/src/services/audioMetadataService.ts @@ -37,6 +37,9 @@ export class AudioMetadataService { 'wma': 'WMA', 'OPUS': 'OPUS', 'opus': 'OPUS', + 'AIFF': 'AIFF', + 'aiff': 'AIFF', + 'aif': 'AIFF', }; // Try to map the container format @@ -204,7 +207,7 @@ export class AudioMetadataService { */ isAudioFile(fileName: string): boolean { const supportedFormats = [ - 'mp3', 'wav', 'flac', 'aac', 'ogg', 'm4a', 'wma', 'opus' + 'mp3', 'wav', 'flac', 'aac', 'ogg', 'm4a', 'wma', 'opus', 'aif', 'aiff' ]; const extension = fileName.split('.').pop()?.toLowerCase(); diff --git a/packages/backend/src/services/backgroundJobService.ts b/packages/backend/src/services/backgroundJobService.ts index 0ed0d72..f8800a9 100644 --- a/packages/backend/src/services/backgroundJobService.ts +++ b/packages/backend/src/services/backgroundJobService.ts @@ -1,6 +1,6 @@ export interface JobProgress { jobId: string; - type: 's3-sync' | 'song-matching'; + type: 'storage-sync' | 'song-matching'; status: 'running' | 'completed' | 'failed'; progress: number; // 0-100 current: number; @@ -185,6 +185,8 @@ class BackgroundJobService { case 'ogg': return 'audio/ogg'; case 'opus': return 'audio/opus'; case 'wma': return 'audio/x-ms-wma'; + case 'aif': return 'audio/aiff'; + case 'aiff': return 'audio/aiff'; default: return 'application/octet-stream'; } }; diff --git a/packages/backend/src/services/webdavService.ts b/packages/backend/src/services/webdavService.ts index 9dd5f57..ca2be97 100644 --- a/packages/backend/src/services/webdavService.ts +++ b/packages/backend/src/services/webdavService.ts @@ -112,6 +112,7 @@ export class WebDAVService implements StorageProvider { try { const response = await this.client.getDirectoryContents(searchPath, { deep: true, + maxDepth: -1, // No depth limit }); // Handle both single item and array responses @@ -146,6 +147,7 @@ export class WebDAVService implements StorageProvider { try { const response = await this.client.getDirectoryContents(searchPath, { deep: true, + maxDepth: -1, // No depth limit }); // Handle both single item and array responses @@ -273,6 +275,8 @@ export class WebDAVService implements StorageProvider { case 'ogg': return 'audio/ogg'; case 'opus': return 'audio/opus'; case 'wma': return 'audio/x-ms-wma'; + case 'aif': return 'audio/aiff'; + case 'aiff': return 'audio/aiff'; default: return 'application/octet-stream'; } } diff --git a/packages/backend/storage-config.json b/packages/backend/storage-config.json index ad7cc2c..f077091 100644 --- a/packages/backend/storage-config.json +++ b/packages/backend/storage-config.json @@ -3,5 +3,5 @@ "url": "https://cloud.geertrademakers.nl/remote.php/dav/files/admin", "username": "admin", "password": "XPZK2-MGQ5W-7Yetf-nr8gf-s5g5Z", - "basePath": "/Test" + "basePath": "/Music" } \ No newline at end of file diff --git a/packages/backend/test-audio-detection.js b/packages/backend/test-audio-detection.js new file mode 100644 index 0000000..90cd82f --- /dev/null +++ b/packages/backend/test-audio-detection.js @@ -0,0 +1,45 @@ +import { AudioMetadataService } from './dist/services/audioMetadataService.js'; + +const audioService = new AudioMetadataService(); + +// Test files from the debug output +const testFiles = [ + '01 Gas Op Die Lollie.mp3', + 'ACRAZE - Do It To It (Extended Mix).mp3', + 'test.flac', + 'sample.wav', + 'music.m4a', + 'song.aac', + 'track.ogg', + 'audio.opus', + 'file.wma', + 'sound.aif', + 'music.aiff', + 'image.jpg', + 'archive.zip', + 'script.py', + 'info.nfo', + 'video.mp4', + 'installer.dmg', + 'playlist.m3u', + 'readme.md', + 'script.sh' +]; + +console.log('🎵 Testing audio file detection:'); +console.log(''); + +testFiles.forEach(filename => { + const isAudio = audioService.isAudioFile(filename); + const status = isAudio ? '✅' : '❌'; + console.log(`${status} ${filename} -> ${isAudio ? 'AUDIO' : 'NOT AUDIO'}`); +}); + +console.log(''); +console.log('📊 Summary:'); +const audioFiles = testFiles.filter(f => audioService.isAudioFile(f)); +const nonAudioFiles = testFiles.filter(f => !audioService.isAudioFile(f)); + +console.log(`Audio files: ${audioFiles.length}`); +console.log(`Non-audio files: ${nonAudioFiles.length}`); +console.log(`Total files: ${testFiles.length}`); diff --git a/packages/backend/test-background-job-flow.js b/packages/backend/test-background-job-flow.js new file mode 100644 index 0000000..0fb0226 --- /dev/null +++ b/packages/backend/test-background-job-flow.js @@ -0,0 +1,113 @@ +import { StorageProviderFactory } from './dist/services/storageProvider.js'; +import { AudioMetadataService } from './dist/services/audioMetadataService.js'; + +async function testBackgroundJobFlow() { + try { + console.log('🔍 Testing Background Job Flow:'); + console.log(''); + + // Step 1: Load config and create provider (same as background job) + console.log('1️⃣ Loading storage configuration...'); + const config = await StorageProviderFactory.loadConfig(); + console.log('Config provider:', config.provider); + console.log('Config basePath:', config.basePath); + console.log(''); + + // Step 2: Create storage service (same as background job) + console.log('2️⃣ Creating storage service...'); + const storageService = await StorageProviderFactory.createProvider(config); + console.log('Storage service created:', storageService.constructor.name); + console.log(''); + + // Step 3: List all files (same as background job) + console.log('3️⃣ Listing all files from storage...'); + const startTime = Date.now(); + const storageFiles = await storageService.listAllFiles(); + const endTime = Date.now(); + console.log(`✅ listAllFiles completed in ${endTime - startTime}ms`); + console.log('Total storage files found:', storageFiles.length); + console.log(''); + + // Step 4: Filter for audio files (same as background job) + console.log('4️⃣ Filtering for audio files...'); + const audioMetadataService = new AudioMetadataService(); + + const audioFiles = storageFiles.filter(storageFile => { + const filename = storageFile.key.split('/').pop() || storageFile.key; + const isAudio = audioMetadataService.isAudioFile(filename); + if (!isAudio) { + console.log(` ❌ Not audio: ${filename}`); + } + return isAudio; + }); + + console.log('Audio files found:', audioFiles.length); + console.log(''); + + // Step 5: Show breakdown by file type + console.log('5️⃣ File type breakdown:'); + const fileTypes = new Map(); + storageFiles.forEach(file => { + const filename = file.key.split('/').pop() || file.key; + const ext = filename.split('.').pop()?.toLowerCase() || 'no-extension'; + fileTypes.set(ext, (fileTypes.get(ext) || 0) + 1); + }); + + const sortedTypes = Array.from(fileTypes.entries()).sort((a, b) => b[1] - a[1]); + sortedTypes.forEach(([ext, count]) => { + const isAudio = audioMetadataService.isAudioFile(`test.${ext}`); + const status = isAudio ? '🎵' : '📄'; + console.log(` ${status} .${ext}: ${count} files`); + }); + console.log(''); + + // Step 6: Show MP3 breakdown + console.log('6️⃣ MP3 files breakdown:'); + const mp3Files = storageFiles.filter(file => { + const filename = file.key.split('/').pop() || file.key; + return filename.toLowerCase().endsWith('.mp3'); + }); + + console.log('MP3 files found:', mp3Files.length); + + // Show directory distribution of MP3 files + const mp3DirCounts = new Map(); + mp3Files.forEach(file => { + const dir = file.key.split('/')[0]; + mp3DirCounts.set(dir, (mp3DirCounts.get(dir) || 0) + 1); + }); + + const sortedMp3Dirs = Array.from(mp3DirCounts.entries()).sort((a, b) => b[1] - a[1]); + console.log('MP3 files by directory:'); + sortedMp3Dirs.forEach(([dir, count]) => { + console.log(` 📁 ${dir}: ${count} MP3 files`); + }); + console.log(''); + + // Step 7: Test with different prefixes (if WebDAV) + if (config.provider === 'webdav') { + console.log('7️⃣ Testing with different prefixes...'); + const testPrefixes = ['', 'Gekocht', 'Merijn Music', 'Musica']; + for (const prefix of testPrefixes) { + try { + const prefixFiles = await storageService.listAllFiles(prefix); + const prefixMp3Files = prefixFiles.filter(file => { + const filename = file.key.split('/').pop() || file.key; + return filename.toLowerCase().endsWith('.mp3'); + }); + console.log(` 📁 Prefix "${prefix}": ${prefixFiles.length} total, ${prefixMp3Files.length} MP3`); + } catch (error) { + console.log(` ❌ Prefix "${prefix}": Error - ${error.message}`); + } + } + } + + } catch (error) { + console.error('❌ Failed to test background job flow:', error); + console.error('Error details:', error.message); + console.error('Stack trace:', error.stack); + } +} + +// Run the test +testBackgroundJobFlow().catch(console.error); diff --git a/packages/backend/test-background-job-simulation.js b/packages/backend/test-background-job-simulation.js new file mode 100644 index 0000000..381fcd5 --- /dev/null +++ b/packages/backend/test-background-job-simulation.js @@ -0,0 +1,94 @@ +import { StorageProviderFactory } from './dist/services/storageProvider.js'; +import { AudioMetadataService } from './dist/services/audioMetadataService.js'; + +async function testBackgroundJobSimulation() { + try { + console.log('🔍 Testing Background Job Simulation:'); + console.log(''); + + // Step 1: Load config and create provider (exactly like background job) + console.log('1️⃣ Loading storage configuration...'); + const config = await StorageProviderFactory.loadConfig(); + console.log('Config provider:', config.provider); + console.log('Config basePath:', config.basePath); + console.log(''); + + // Step 2: Create storage service (exactly like background job) + console.log('2️⃣ Creating storage service...'); + const storageService = await StorageProviderFactory.createProvider(config); + console.log('Storage service created:', storageService.constructor.name); + console.log(''); + + // Step 3: Create audio metadata service (exactly like background job) + console.log('3️⃣ Creating audio metadata service...'); + const audioMetadataService = new AudioMetadataService(); + console.log('Audio metadata service created'); + console.log(''); + + // Step 4: List all files (exactly like background job) + console.log('4️⃣ Listing all files from storage...'); + const startTime = Date.now(); + const storageFiles = await storageService.listAllFiles(); + const endTime = Date.now(); + console.log(`✅ listAllFiles completed in ${endTime - startTime}ms`); + console.log('Total storage files found:', storageFiles.length); + console.log(''); + + // Step 5: Filter for audio files (exactly like background job) + console.log('5️⃣ Filtering for audio files...'); + const audioFiles = storageFiles.filter(storageFile => { + const filename = storageFile.key.split('/').pop() || storageFile.key; + const isAudio = audioMetadataService.isAudioFile(filename); + return isAudio; + }); + + console.log('Audio files found:', audioFiles.length); + console.log(''); + + // Step 6: Show MP3 breakdown + console.log('6️⃣ MP3 files breakdown:'); + const mp3Files = audioFiles.filter(file => { + const filename = file.key.split('/').pop() || file.key; + return filename.toLowerCase().endsWith('.mp3'); + }); + + console.log('MP3 files found:', mp3Files.length); + console.log(''); + + // Step 7: Show file type breakdown + console.log('7️⃣ File type breakdown:'); + const fileTypes = new Map(); + audioFiles.forEach(file => { + const filename = file.key.split('/').pop() || file.key; + const ext = filename.split('.').pop()?.toLowerCase() || 'no-extension'; + fileTypes.set(ext, (fileTypes.get(ext) || 0) + 1); + }); + + const sortedTypes = Array.from(fileTypes.entries()).sort((a, b) => b[1] - a[1]); + sortedTypes.forEach(([ext, count]) => { + console.log(` .${ext}: ${count} files`); + }); + console.log(''); + + // Step 8: Show directory breakdown + console.log('8️⃣ Directory breakdown:'); + const dirCounts = new Map(); + audioFiles.forEach(file => { + const dir = file.key.split('/')[0]; + dirCounts.set(dir, (dirCounts.get(dir) || 0) + 1); + }); + + const sortedDirs = Array.from(dirCounts.entries()).sort((a, b) => b[1] - a[1]); + sortedDirs.forEach(([dir, count]) => { + console.log(` 📁 ${dir}: ${count} audio files`); + }); + + } catch (error) { + console.error('❌ Failed to test background job simulation:', error); + console.error('Error details:', error.message); + console.error('Stack trace:', error.stack); + } +} + +// Run the test +testBackgroundJobSimulation().catch(console.error); diff --git a/packages/backend/test-current-webdav.js b/packages/backend/test-current-webdav.js new file mode 100644 index 0000000..f81f180 --- /dev/null +++ b/packages/backend/test-current-webdav.js @@ -0,0 +1,51 @@ +import { StorageProviderFactory } from './dist/services/storageProvider.js'; + +async function testCurrentWebDAV() { + try { + console.log('🔍 Testing Current WebDAV Service:'); + console.log(''); + + // Load config and create provider (same as background job) + const config = await StorageProviderFactory.loadConfig(); + console.log('Config provider:', config.provider); + console.log('Config basePath:', config.basePath); + console.log(''); + + // Create storage service + const storageService = await StorageProviderFactory.createProvider(config); + console.log('Storage service created:', storageService.constructor.name); + console.log(''); + + // List all files + console.log('📁 Listing all files...'); + const startTime = Date.now(); + const storageFiles = await storageService.listAllFiles(); + const endTime = Date.now(); + console.log(`✅ listAllFiles completed in ${endTime - startTime}ms`); + console.log('Total storage files found:', storageFiles.length); + console.log(''); + + // Filter for MP3 files + const mp3Files = storageFiles.filter(file => { + const filename = file.key.split('/').pop() || file.key; + return filename.toLowerCase().endsWith('.mp3'); + }); + + console.log('🎵 MP3 files found:', mp3Files.length); + console.log(''); + + // Show first 10 MP3 files + console.log('🎵 First 10 MP3 files:'); + mp3Files.slice(0, 10).forEach((file, index) => { + console.log(` ${index + 1}. ${file.key} (${file.size} bytes)`); + }); + + } catch (error) { + console.error('❌ Failed to test current WebDAV service:', error); + console.error('Error details:', error.message); + console.error('Stack trace:', error.stack); + } +} + +// Run the test +testCurrentWebDAV().catch(console.error); diff --git a/packages/backend/test-server-webdav.js b/packages/backend/test-server-webdav.js new file mode 100644 index 0000000..8830c1c --- /dev/null +++ b/packages/backend/test-server-webdav.js @@ -0,0 +1,54 @@ +import fetch from 'node-fetch'; + +async function testServerWebDAV() { + try { + console.log('🔍 Testing Server WebDAV via API:'); + console.log(''); + + // Test the storage sync endpoint + console.log('1️⃣ Testing storage sync endpoint...'); + const response = await fetch('http://localhost:3000/api/music/sync-s3', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ force: true }) + }); + + if (response.ok) { + const result = await response.json(); + console.log('✅ Storage sync started:', result); + console.log(''); + + // Wait a bit and check job progress + console.log('2️⃣ Waiting for job to start...'); + await new Promise(resolve => setTimeout(resolve, 5000)); + + const jobsResponse = await fetch('http://localhost:3000/api/background-jobs/jobs'); + if (jobsResponse.ok) { + const jobs = await jobsResponse.json(); + const latestJob = jobs.jobs[jobs.jobs.length - 1]; + console.log('📊 Latest job status:', { + jobId: latestJob.jobId, + status: latestJob.status, + progress: latestJob.progress, + message: latestJob.message, + current: latestJob.current, + total: latestJob.total + }); + + if (latestJob.result) { + console.log('📊 Job result:', latestJob.result); + } + } + } else { + console.error('❌ Failed to start storage sync:', response.status, response.statusText); + } + + } catch (error) { + console.error('❌ Failed to test server WebDAV:', error); + } +} + +// Run the test +testServerWebDAV().catch(console.error); diff --git a/packages/backend/test-webdav-service.js b/packages/backend/test-webdav-service.js new file mode 100644 index 0000000..8aceb6a --- /dev/null +++ b/packages/backend/test-webdav-service.js @@ -0,0 +1,92 @@ +import { WebDAVService } from './src/services/webdavService.js'; +import fs from 'fs'; + +async function testWebDAVService() { + try { + // Load configuration + const configData = fs.readFileSync('storage-config.json', 'utf-8'); + const config = JSON.parse(configData); + + console.log('🔍 Testing WebDAV Service:'); + console.log('URL:', config.url); + console.log('Username:', config.username); + console.log('Base Path:', config.basePath); + console.log(''); + + // Create WebDAV service + const webdavService = new WebDAVService(config); + + console.log('🔗 Testing connection...'); + const connectionTest = await webdavService.testConnection(); + console.log('Connection test:', connectionTest ? '✅ Success' : '❌ Failed'); + console.log(''); + + if (connectionTest) { + console.log('📁 Testing listAllFiles...'); + const startTime = Date.now(); + const allFiles = await webdavService.listAllFiles(); + const endTime = Date.now(); + + console.log(`✅ listAllFiles completed in ${endTime - startTime}ms`); + console.log('Total files found:', allFiles.length); + console.log(''); + + // Filter for MP3 files + const mp3Files = allFiles.filter(file => { + const filename = file.key.split('/').pop() || file.key; + return filename.toLowerCase().endsWith('.mp3'); + }); + + console.log('🎵 MP3 Files found by service:', mp3Files.length); + console.log(''); + + // Show first 20 MP3 files + console.log('🎵 First 20 MP3 files from service:'); + mp3Files.slice(0, 20).forEach((file, index) => { + console.log(` ${index + 1}. ${file.key} (${file.size} bytes)`); + }); + + if (mp3Files.length > 20) { + console.log(` ... and ${mp3Files.length - 20} more MP3 files`); + } + + // Check directory distribution + console.log(''); + console.log('📁 Directory distribution of MP3 files:'); + const dirCounts = new Map(); + mp3Files.forEach(file => { + const dir = file.key.split('/')[0]; + dirCounts.set(dir, (dirCounts.get(dir) || 0) + 1); + }); + + const sortedDirs = Array.from(dirCounts.entries()).sort((a, b) => b[1] - a[1]); + sortedDirs.forEach(([dir, count]) => { + console.log(` 📁 ${dir}: ${count} MP3 files`); + }); + + // Test with different prefixes + console.log(''); + console.log('🔍 Testing with different prefixes...'); + + const testPrefixes = ['', 'Gekocht', 'Merijn Music', 'Musica']; + for (const prefix of testPrefixes) { + try { + const prefixFiles = await webdavService.listAllFiles(prefix); + const prefixMp3Files = prefixFiles.filter(file => { + const filename = file.key.split('/').pop() || file.key; + return filename.toLowerCase().endsWith('.mp3'); + }); + console.log(` 📁 Prefix "${prefix}": ${prefixMp3Files.length} MP3 files`); + } catch (error) { + console.log(` ❌ Prefix "${prefix}": Error - ${error.message}`); + } + } + } + + } catch (error) { + console.error('❌ Failed to test WebDAV service:', error); + } +} + +// Run the test +testWebDAVService().catch(console.error); diff --git a/packages/frontend/src/components/BackgroundJobProgress.tsx b/packages/frontend/src/components/BackgroundJobProgress.tsx index 7eed1bb..28d9743 100644 --- a/packages/frontend/src/components/BackgroundJobProgress.tsx +++ b/packages/frontend/src/components/BackgroundJobProgress.tsx @@ -56,98 +56,42 @@ export const BackgroundJobProgress: React.FC = ({ const { isOpen, onClose } = useDisclosure(); const intervalRef = useRef(null); - // Load all jobs - const loadJobs = async () => { + // Simple polling function + const pollJobs = async () => { try { - setLoading(true); const jobsData = await api.getAllJobs(); setJobs(jobsData); - setError(null); - } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to load jobs'); - } finally { - setLoading(false); - } - }; - // Update specific job progress - const updateJobProgress = async (jobId: string) => { - try { - const progress = await api.getJobProgress(jobId); - - setJobs(prev => prev.map(job => - job.jobId === jobId ? progress : job - )); - - // Handle job completion - if (progress.status === 'completed' && onJobComplete) { - onJobComplete(progress.result); - } else if (progress.status === 'failed' && onJobError) { - onJobError(progress.error || 'Job failed'); + // Handle job completion for the specific job if provided + if (jobId) { + const specificJob = jobsData.find((j: JobProgress) => j.jobId === jobId); + if (specificJob) { + if (specificJob.status === 'completed' && onJobComplete) { + onJobComplete(specificJob.result); + } else if (specificJob.status === 'failed' && onJobError) { + onJobError(specificJob.error || 'Job failed'); + } + } } } catch (err) { - console.error('Error updating job progress:', err); - } - }; - - // Start polling for jobs and update progress - const startPolling = () => { - if (intervalRef.current) { - clearInterval(intervalRef.current); - } - - const tick = async () => { - try { - // Always reload job list to detect newly started jobs - const jobsData = await api.getAllJobs(); - setJobs(jobsData); - - // Update progress for active jobs - const activeJobIds = jobsData.filter((j: JobProgress) => j.status === 'running').map((j: JobProgress) => j.jobId); - for (const id of activeJobIds) { - await updateJobProgress(id); - } - - if (jobId) { - await updateJobProgress(jobId); - } - } catch (err) { - // ignore transient polling errors - } - }; - - // Adaptive interval: 2s if active jobs, else 10s - const schedule = async () => { - await tick(); - const hasActive = (jobs || []).some(j => j.status === 'running'); - const delay = hasActive ? 2000 : 10000; - intervalRef.current = setTimeout(schedule, delay) as any; - }; - - schedule(); - }; - - // Stop polling - const stopPolling = () => { - if (intervalRef.current) { - clearTimeout(intervalRef.current as any); - intervalRef.current = null; + // ignore transient polling errors } }; // Start polling on mount and stop on unmount useEffect(() => { - loadJobs(); - startPolling(); - return () => stopPolling(); - }, []); + // Initial poll + pollJobs(); + + // Set up interval polling + const interval = setInterval(() => { + pollJobs(); + }, 10000); // Simple 10-second interval - // Cleanup on unmount - useEffect(() => { return () => { - stopPolling(); + clearInterval(interval); }; - }, []); + }, [jobId, onJobComplete, onJobError]); const getStatusColor = (status: string) => { switch (status) { @@ -246,7 +190,7 @@ export const BackgroundJobProgress: React.FC = ({ All Jobs -