// Library Manager - All library-related functions extracted from index.html // This file contains functions for managing Images, Audio, Video, and Gallery tabs async function setupLibraryImagesTab(retryCount = 0) { console.log('Setting up images tab functionality...'); // Wait for game to be available (max 10 retries) if (!window.game && retryCount < 10) { console.log(`⏳ Game not ready yet, retrying in 500ms... (attempt ${retryCount + 1}/10)`); setTimeout(() => setupLibraryImagesTab(retryCount + 1), 500); return; } if (!window.game) { console.error('❌ Game not available after 10 retries, aborting image tab setup'); return; } // Get current image stats from multiple possible locations let taskImages = []; let consequenceImages = []; // Try dataManager first (most likely location based on console output) if (window.game.dataManager && window.game.dataManager.gameData) { taskImages = window.game.dataManager.gameData.taskImages || []; consequenceImages = window.game.dataManager.gameData.consequenceImages || []; } // Try gameData directly if (taskImages.length === 0 && window.game.gameData) { taskImages = window.game.gameData.taskImages || []; consequenceImages = window.game.gameData.consequenceImages || []; } console.log(`📊 Found ${taskImages.length} task images, ${consequenceImages.length} consequence images`); // Update image count display const imageCountElement = document.getElementById('lib-image-count'); if (imageCountElement) { imageCountElement.textContent = `${taskImages.length + consequenceImages.length} images`; } // Populate image gallery const imageGallery = document.getElementById('lib-image-gallery'); if (imageGallery && (taskImages.length > 0 || consequenceImages.length > 0)) { imageGallery.innerHTML = ''; // Add task images taskImages.forEach((image, index) => { const imgElement = document.createElement('div'); imgElement.className = 'gallery-item'; imgElement.innerHTML = ` Task Image ${index + 1} `; imageGallery.appendChild(imgElement); }); // Add consequence images consequenceImages.forEach((image, index) => { const imgElement = document.createElement('div'); imgElement.className = 'gallery-item'; imgElement.innerHTML = ` Consequence Image ${index + 1} `; imageGallery.appendChild(imgElement); }); console.log(`✅ Created ${taskImages.length + consequenceImages.length} image gallery items`); } else if (imageGallery) { imageGallery.innerHTML = `

🗃️ No images found

Import images to get started

`; } // Set up image directory management buttons const addImageDirBtn = document.getElementById('lib-add-image-directory-btn'); const addIndividualImagesBtn = document.getElementById('lib-add-individual-images-btn'); const refreshImageDirBtn = document.getElementById('lib-refresh-image-directories-btn'); const clearImageDirBtn = document.getElementById('lib-clear-image-directories-btn'); if (addImageDirBtn) { addImageDirBtn.onclick = () => { console.log('Adding image directory...'); handleAddImageDirectory(); }; } if (addIndividualImagesBtn) { addIndividualImagesBtn.onclick = () => { console.log('Adding individual images...'); handleAddIndividualImages(); }; } if (refreshImageDirBtn) { refreshImageDirBtn.onclick = () => { console.log('Refreshing image directories...'); handleRefreshImageDirectories(); }; } if (clearImageDirBtn) { clearImageDirBtn.onclick = () => { console.log('Clearing image directories...'); handleClearImageDirectories(); }; } // Set up category filter dropdown const categoryFilter = document.getElementById('lib-image-category-filter'); if (categoryFilter) { // Populate with all available tags populateImageFilterTags(); categoryFilter.onchange = () => { console.log('Filtering images by category:', categoryFilter.value); // Use stored images and apply filter if (window.allLinkedImages) { const filteredImages = filterImagesByCategory(window.allLinkedImages, categoryFilter.value); populateImageGallery(filteredImages); // Update count display const countElement = document.getElementById('lib-image-count'); if (countElement) { countElement.textContent = `${filteredImages.length} images`; } } }; } // Initialize linked image directories display updateImageDirectoriesList(); // Check if we have linked directories first const linkedDirs = JSON.parse(localStorage.getItem('linkedImageDirectories') || '[]'); console.log('📁 Checking for linked directories...', linkedDirs.length); console.log('📁 electronAPI available:', !!window.electronAPI); if (linkedDirs.length > 0) { console.log('📁 Using linked directories instead of built-in directories'); await loadLinkedImages(); return; // Skip built-in directory scanning } else { console.log('📁 No linked directories found, using built-in directories'); } } function setupLibraryAudioTab(retryCount = 0) { console.log('Setting up audio tab functionality...'); // Wait for game to be available (max 10 retries) if (!window.game && retryCount < 10) { console.log(`⏳ Game not ready yet, retrying in 500ms... (attempt ${retryCount + 1}/10)`); setTimeout(() => setupLibraryAudioTab(retryCount + 1), 500); return; } if (!window.game) { console.error('❌ Game not available after 10 retries, aborting audio tab setup'); return; } // Get current audio stats from multiple possible locations let audioLibrary = {}; let backgroundTracks = []; let ambientTracks = []; // Try audioManager first if (window.game.audioManager && window.game.audioManager.audioLibrary) { audioLibrary = window.game.audioManager.audioLibrary; backgroundTracks = audioLibrary.background || []; ambientTracks = audioLibrary.ambient || []; } // If audioLibrary is empty, try checking if tracks are stored differently if (backgroundTracks.length === 0 && window.game.audioManager) { // Check if background tracks are stored in a different property backgroundTracks = window.game.audioManager.backgroundTracks || window.game.audioManager.background || []; } if (ambientTracks.length === 0 && window.game.audioManager) { // Check if ambient tracks are stored in a different property ambientTracks = window.game.audioManager.ambientTracks || window.game.audioManager.ambient || []; } console.log(`📊 Found ${backgroundTracks.length} background tracks, ${ambientTracks.length} ambient tracks`); console.log('📍 Audio manager available:', !!window.game.audioManager); // Store audio tracks globally for filtering window.allAudioTracks = { background: backgroundTracks, ambient: ambientTracks }; // Apply current filter and populate the gallery const categoryFilter = document.getElementById('lib-audio-category-filter'); const selectedCategory = categoryFilter ? categoryFilter.value : 'all'; const filteredTracks = filterAudioByCategory(backgroundTracks, ambientTracks, selectedCategory); populateAudioGallery(filteredTracks.background, filteredTracks.ambient); // Update count display const audioCountElement = document.getElementById('lib-audio-count'); if (audioCountElement) { const totalCount = filteredTracks.background.length + filteredTracks.ambient.length; audioCountElement.textContent = `${totalCount} files`; } // Set up category filter dropdown if (categoryFilter) { categoryFilter.onchange = () => { console.log('Filtering audio by category:', categoryFilter.value); // Use stored audio tracks and apply filter if (window.allAudioTracks) { const filtered = filterAudioByCategory( window.allAudioTracks.background, window.allAudioTracks.ambient, categoryFilter.value ); populateAudioGallery(filtered.background, filtered.ambient); // Update count display const totalCount = filtered.background.length + filtered.ambient.length; audioCountElement.textContent = `${totalCount} files`; } }; } } function filterAudioByCategory(backgroundTracks, ambientTracks, category) { switch (category) { case 'background': return { background: backgroundTracks, ambient: [] }; case 'ambient': return { background: [], ambient: ambientTracks }; case 'all': default: return { background: backgroundTracks, ambient: ambientTracks }; } } function populateAudioGallery(backgroundTracks, ambientTracks) { const audioGallery = document.getElementById('lib-audio-gallery'); if (!audioGallery) return; if (backgroundTracks.length === 0 && ambientTracks.length === 0) { audioGallery.innerHTML = `

🎵 No audio files found

Import audio to get started

`; return; } audioGallery.innerHTML = ''; // Add background tracks backgroundTracks.forEach((track, index) => { const audioElement = document.createElement('div'); audioElement.className = 'gallery-item audio-item'; audioElement.innerHTML = `
🎵
${track.name || `Track ${index + 1}`}
Background
`; audioGallery.appendChild(audioElement); }); // Add ambient tracks ambientTracks.forEach((track, index) => { const audioElement = document.createElement('div'); audioElement.className = 'gallery-item audio-item'; audioElement.innerHTML = `
🌊
${track.name || `Ambient ${index + 1}`}
Ambient
`; audioGallery.appendChild(audioElement); }); console.log(`✅ Created ${backgroundTracks.length + ambientTracks.length} audio gallery items`); } async function setupLibraryVideoTab(retryCount = 0) { console.log('Setting up video tab functionality...'); // Wait for game to be available (max 10 retries) if (!window.game && retryCount < 10) { console.log(`⏳ Game not ready yet, retrying in 500ms... (attempt ${retryCount + 1}/10)`); setTimeout(() => setupLibraryVideoTab(retryCount + 1), 500); return; } if (!window.game) { console.error('❌ Game not available after 10 retries, aborting video tab setup'); return; } console.log('📍 Video manager types:', Object.keys(window.game).filter(key => key.toLowerCase().includes('video'))); // Update video count display const videoCountElement = document.getElementById('lib-video-count'); if (videoCountElement) { videoCountElement.textContent = `0 files`; } // Populate video gallery const videoGallery = document.getElementById('lib-video-gallery'); if (videoGallery) { videoGallery.innerHTML = `

🎬 No video files found

Import videos to get started

`; } // Set up video directory management buttons const addVideoDirBtn = document.getElementById('lib-add-video-directory-btn'); const addIndividualVideosBtn = document.getElementById('lib-add-individual-videos-btn'); const refreshVideoDirBtn = document.getElementById('lib-refresh-video-directories-btn'); const clearVideoDirBtn = document.getElementById('lib-clear-video-directories-btn'); if (addVideoDirBtn) { addVideoDirBtn.onclick = () => { console.log('Adding video directory...'); handleAddVideoDirectory(); }; } if (addIndividualVideosBtn) { addIndividualVideosBtn.onclick = () => { console.log('Adding individual videos...'); handleAddIndividualVideos(); }; } if (refreshVideoDirBtn) { refreshVideoDirBtn.onclick = () => { console.log('Refreshing video directories...'); handleRefreshVideoDirectories(); }; } if (clearVideoDirBtn) { clearVideoDirBtn.onclick = () => { console.log('Clearing video directories...'); handleClearVideoDirectories(); }; } // Set up category filter dropdown const categoryFilter = document.getElementById('lib-video-category-filter'); if (categoryFilter) { // Populate with all available tags populateVideoFilterTags(); categoryFilter.onchange = () => { console.log('Filtering videos by category:', categoryFilter.value); // Use stored videos and apply filter if (window.allLinkedVideos) { const filteredVideos = filterVideosByCategory(window.allLinkedVideos, categoryFilter.value); populateVideoGallery(filteredVideos); // Update count display const videoCountElement = document.getElementById('lib-video-count'); if (videoCountElement) { videoCountElement.textContent = `${filteredVideos.length} videos`; } } }; } // Initialize linked video directories display updateVideoDirectoriesList(); // Load linked videos - prefer desktopFileManager if available if (window.desktopFileManager && window.desktopFileManager.externalVideoDirectories && window.desktopFileManager.externalVideoDirectories.length > 0) { console.log('📁 Loading videos from desktopFileManager...'); loadVideosFromDesktopFileManager(); } else { // Fallback to legacy localStorage method let linkedVideoDirs; let individualVideos; try { const rawData = localStorage.getItem('linkedVideoDirectories'); if (rawData) { const parsed = JSON.parse(rawData); linkedVideoDirs = Array.isArray(parsed) ? parsed : (parsed.directories || []); } else { linkedVideoDirs = []; } individualVideos = JSON.parse(localStorage.getItem('linkedIndividualVideos') || '[]'); if (!Array.isArray(individualVideos)) { individualVideos = []; } } catch (e) { console.log('Error parsing video directories, resetting to empty arrays:', e); linkedVideoDirs = []; individualVideos = []; } if (linkedVideoDirs.length > 0 || individualVideos.length > 0) { console.log('📁 Loading linked video directories (legacy)...'); loadLinkedVideos(); } } } function setupLibraryGalleryTab() { console.log('Setting up gallery tab functionality...'); // Load captured photos from file system (Electron only) loadCapturedPhotosForGallery(); } async function loadCapturedPhotosForGallery() { let capturedPhotos = []; let verificationPhotos = []; // Try loading from file system first (Electron) if (window.desktopFileManager && window.desktopFileManager.isElectron) { console.log('📸 Loading captured photos from file system...'); const filePhotos = await window.desktopFileManager.loadCapturedPhotos(); if (filePhotos && filePhotos.length > 0) { capturedPhotos = filePhotos; console.log(`📸 Loaded ${capturedPhotos.length} photos from file system (photos/captured/)`); } else { console.log('📸 No captured photos found in file system'); } } else { console.log('📸 Desktop file manager not available - browser mode (no persistent photo storage)'); } // Load verification photos from localStorage (these are always in localStorage) verificationPhotos = JSON.parse(localStorage.getItem('verificationPhotos') || '[]'); console.log(`📷 Found ${verificationPhotos.length} verification photos`); const allPhotosGrid = document.getElementById('lib-all-photos-grid'); const allPhotosCount = document.getElementById('lib-all-photos-count'); if (allPhotosGrid) { if (capturedPhotos.length === 0 && verificationPhotos.length === 0) { allPhotosGrid.innerHTML = `

📸 No photos found

Take some photos during gameplay to see them here

`; if (allPhotosCount) allPhotosCount.textContent = '0 photos'; } else { // Create photo gallery grid - start with captured photos let photosHtml = ''; capturedPhotos.forEach((photo, index) => { const timestamp = new Date(photo.timestamp || Date.now()).toLocaleDateString(); const imageData = photo.url || photo.path || photo.imageData || photo.dataURL; // Support file URLs and data URLs const mediaId = photo.path || photo.filename || `photo_${index}`; if (imageData) { photosHtml += ` `; } }); // Add verification photos to the gallery verificationPhotos.forEach((photo, index) => { const timestamp = new Date(photo.timestamp || Date.now()).toLocaleDateString(); const photoType = photo.phase === 'start' ? '🟢 START Position' : '🔴 END Position'; const degradingMessage = photo.message || 'Position verification photo'; const verificationIndex = capturedPhotos.length + index; // Offset by captured photos length const imageData = photo.data || photo.dataUrl; // Support both formats if (imageData) { photosHtml += `
Verification Photo ${index + 1}
${timestamp} ${photoType} "${degradingMessage}"
`; } }); allPhotosGrid.innerHTML = photosHtml; const totalPhotos = capturedPhotos.length + verificationPhotos.length; if (allPhotosCount) allPhotosCount.textContent = `${totalPhotos} photos`; // Add tagging to photo items after rendering if (window.mediaTaggingIntegration) { setTimeout(() => { const photoItems = allPhotosGrid.querySelectorAll('.photo-item'); photoItems.forEach(item => { const mediaId = item.dataset.photoPath; if (mediaId) { window.mediaTaggingIntegration.addTagsToGalleryItem(item, mediaId, 'photo'); } }); }, 100); } } } // Initialize bulk action event listeners setTimeout(initializeBulkActions, 100); } // Delete a photo from the gallery async function deletePhoto(index, filePath = '') { // Load photos from file system if available let capturedPhotos = []; if (window.desktopFileManager && window.desktopFileManager.isElectron) { capturedPhotos = await window.desktopFileManager.loadCapturedPhotos(); } if (index < 0 || index >= capturedPhotos.length) { console.error('Invalid photo index:', index); return; } const photo = capturedPhotos[index]; const photoType = photo.sessionType || 'Training'; const photoDate = new Date(photo.timestamp || Date.now()).toLocaleDateString(); // Show confirmation dialog const confirmed = confirm(`Are you sure you want to delete this photo?\n\nType: ${photoType}\nDate: ${photoDate}\n\nThis action cannot be undone.`); if (confirmed) { // Delete from file system if in Electron if (window.desktopFileManager && window.desktopFileManager.isElectron) { const deleteSuccess = await window.desktopFileManager.deletePhoto(photo.path || filePath); if (deleteSuccess) { console.log(`🗑️ Deleted photo from file system: ${photo.filename}`); if (window.game && window.game.flashMessageManager) { window.game.flashMessageManager.show(`📸 Photo deleted successfully!`, 'info'); } } else { console.error('Failed to delete photo from file system'); if (window.game && window.game.flashMessageManager) { window.game.flashMessageManager.show(`❌ Failed to delete photo`, 'error'); } return; } } // Refresh the photo galleries setupLibraryGalleryTab(); console.log(`🗑️ Deleted photo ${index + 1} (${photoType})`); } } // Update selection count and enable/disable bulk action buttons function updateSelectionCount() { const selectedCheckboxes = document.querySelectorAll('.photo-select:checked'); const count = selectedCheckboxes.length; const selectedCountSpan = document.getElementById('selected-count'); const downloadBtn = document.getElementById('download-selected-photos'); const deleteBtn = document.getElementById('delete-selected-photos'); if (selectedCountSpan) selectedCountSpan.textContent = `${count} selected`; if (downloadBtn) downloadBtn.disabled = count === 0; if (deleteBtn) deleteBtn.disabled = count === 0; } // Select all photos function selectAllPhotos() { const checkboxes = document.querySelectorAll('.photo-select'); checkboxes.forEach(checkbox => { checkbox.checked = true; }); updateSelectionCount(); } // Deselect all photos function deselectAllPhotos() { const checkboxes = document.querySelectorAll('.photo-select'); checkboxes.forEach(checkbox => { checkbox.checked = false; }); updateSelectionCount(); } // Download single photo async function downloadSinglePhoto(index, photoFilename = '', filePath = '') { // Load photos from file system if available let capturedPhotos = []; if (window.desktopFileManager && window.desktopFileManager.isElectron) { capturedPhotos = await window.desktopFileManager.loadCapturedPhotos(); } if (index < 0 || index >= capturedPhotos.length) { console.error('Invalid photo index:', index); return; } const photo = capturedPhotos[index]; // Handle different photo data structures let imageData; if (photo.data) { // Verification photos store in 'data' property imageData = photo.data; } else if (photo.dataURL) { // Regular photos store in 'dataURL' property imageData = photo.dataURL; } else if (photo.imageData) { // Fallback for other formats imageData = photo.imageData; } else if (photo.url || photo.path) { // File system photos - use the file URL imageData = photo.url || photo.path; } else { console.error('Photo missing image data:', photo); return; } const timestamp = new Date(photo.timestamp || Date.now()).toISOString().slice(0, 19).replace(/:/g, '-'); const downloadFilename = photoFilename || photo.filename || `photo-${timestamp}.png`; // Create download link const link = document.createElement('a'); link.href = imageData; link.download = downloadFilename; document.body.appendChild(link); link.click(); document.body.removeChild(link); if (window.game && window.game.flashMessageManager) { window.game.flashMessageManager.show(`📥 Photo downloaded: ${downloadFilename}`, 'info'); } console.log(`📥 Downloaded photo: ${downloadFilename}`); } // Download single verification photo function downloadVerificationPhoto(index) { const verificationPhotos = JSON.parse(localStorage.getItem('verificationPhotos') || '[]'); if (index < 0 || index >= verificationPhotos.length) { console.error('Invalid verification photo index:', index); return; } const photo = verificationPhotos[index]; const timestamp = new Date(photo.timestamp || Date.now()); const dateStr = timestamp.toISOString().split('T')[0]; const timeStr = timestamp.toTimeString().split(' ')[0].replace(/:/g, '-'); const photoType = photo.phase === 'start' ? 'START' : 'END'; const imageData = photo.data || photo.dataUrl; // Support both formats if (!imageData) { console.error('No image data found for verification photo:', photo); return; } // Create download link const link = document.createElement('a'); link.href = imageData; link.download = `verification-${photoType}-${dateStr}_${timeStr}.png`; document.body.appendChild(link); link.click(); document.body.removeChild(link); if (window.game && window.game.flashMessageManager) { window.game.flashMessageManager.show(`📥 Downloaded verification photo: ${photoType}`, 'success'); } } function deleteVerificationPhoto(index) { const verificationPhotos = JSON.parse(localStorage.getItem('verificationPhotos') || '[]'); if (index < 0 || index >= verificationPhotos.length) { console.error('Invalid verification photo index:', index); return; } const photo = verificationPhotos[index]; const photoType = photo.phase === 'start' ? 'START' : 'END'; const confirmed = confirm(`Are you sure you want to delete this ${photoType} verification photo?\n\nThis action cannot be undone.`); if (confirmed) { // Remove the photo from the array verificationPhotos.splice(index, 1); // Update localStorage localStorage.setItem('verificationPhotos', JSON.stringify(verificationPhotos)); // Refresh the gallery setupLibraryGalleryTab(); if (window.game && window.game.flashMessageManager) { window.game.flashMessageManager.show(`🗑️ Deleted verification photo: ${photoType}`, 'info'); } console.log(`🗑️ Deleted verification photo ${index} (${photoType})`); } } // Show photo preview in modal function showPhotoPreview(imageData, title) { // Create modal overlay const overlay = document.createElement('div'); overlay.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.9); display: flex; justify-content: center; align-items: center; z-index: 10000; cursor: pointer; `; // Create image element const img = document.createElement('img'); img.src = imageData; img.alt = title; img.style.cssText = ` max-width: 90%; max-height: 90%; border-radius: 10px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); `; // Create title const titleDiv = document.createElement('div'); titleDiv.textContent = title; titleDiv.style.cssText = ` position: absolute; top: 20px; left: 50%; transform: translateX(-50%); color: white; font-size: 1.2em; font-weight: bold; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8); `; // Add elements to overlay overlay.appendChild(img); overlay.appendChild(titleDiv); // Close on click overlay.addEventListener('click', () => { document.body.removeChild(overlay); }); // Add to page document.body.appendChild(overlay); } function showVerificationPhotoPreview(imageData, title, message, isStart, timestamp) { const photoDate = timestamp ? new Date(timestamp).toLocaleString() : 'Unknown date'; const photoType = isStart ? '🟢 START Position' : '🔴 END Position'; // Create modal overlay const overlay = document.createElement('div'); overlay.style.cssText = ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.9); display: flex; align-items: center; justify-content: center; z-index: 10000; cursor: pointer; `; overlay.innerHTML = `

${title}

${photoType} ${photoDate}

Degrading Message:

"${message}"

${title}
`; overlay.addEventListener('click', (e) => { if (e.target === overlay) { overlay.remove(); } }); document.body.appendChild(overlay); } // Initialize bulk action event listeners function initializeBulkActions() { const selectAllBtn = document.getElementById('select-all-photos'); const deselectAllBtn = document.getElementById('deselect-all-photos'); const downloadSelectedBtn = document.getElementById('download-selected-photos'); const deleteSelectedBtn = document.getElementById('delete-selected-photos'); if (selectAllBtn) selectAllBtn.addEventListener('click', selectAllPhotos); if (deselectAllBtn) deselectAllBtn.addEventListener('click', deselectAllPhotos); if (downloadSelectedBtn) downloadSelectedBtn.addEventListener('click', () => { console.log('Download selected photos functionality to be implemented'); }); if (deleteSelectedBtn) deleteSelectedBtn.addEventListener('click', () => { console.log('Delete selected photos functionality to be implemented'); }); } // Image Directory Management Functions function handleAddImageDirectory() { console.log('Adding new image directory...'); if (window.electronAPI && window.electronAPI.selectDirectory) { console.log('📍 Calling electronAPI.selectDirectory...'); try { const result = window.electronAPI.selectDirectory(); console.log('📍 selectDirectory returned:', result, typeof result); // Handle both sync and async results if (result && typeof result.then === 'function') { result.then((directoryResult) => { console.log('Directory selection result (async):', directoryResult); handleDirectoryResult(directoryResult); }).catch(error => { console.error('Error selecting image directory (async):', error); if (window.game && window.game.showNotification) { window.game.showNotification('❌ Failed to select directory', 'error'); } }); } else { console.log('Directory selection result (sync):', result); handleDirectoryResult(result); } } catch (error) { console.error('Error calling selectDirectory:', error); if (window.game && window.game.showNotification) { window.game.showNotification('❌ Failed to open directory dialog', 'error'); } } } else { console.log('❌ electronAPI.selectDirectory not available'); if (window.game && window.game.showNotification) { window.game.showNotification('Directory linking is only available in desktop mode', 'warning'); } } } function handleDirectoryResult(result) { console.log('📍 Processing directory result:', result); let selectedPaths = []; if (result) { // Handle array of paths (multiple selection) if (Array.isArray(result)) { selectedPaths = result; } // Handle object with filePaths array else if (result.filePaths && Array.isArray(result.filePaths)) { selectedPaths = result.filePaths; } // Handle single path string (legacy support) else if (typeof result === 'string') { selectedPaths = [result]; } // Handle object with filePath property else if (result.filePath) { selectedPaths = [result.filePath]; } // Handle object with paths array else if (result.paths && Array.isArray(result.paths)) { selectedPaths = result.paths; } } if (selectedPaths && selectedPaths.length > 0) { console.log(`Selected ${selectedPaths.length} image director${selectedPaths.length === 1 ? 'y' : 'ies'}:`, selectedPaths); let addedCount = 0; selectedPaths.forEach(path => { if (addImageDirectory(path)) { addedCount++; } }); if (addedCount > 0 && window.game && window.game.showNotification) { window.game.showNotification(`✅ Added ${addedCount} director${addedCount === 1 ? 'y' : 'ies'}`, 'success'); } } else { console.log('No directory selected or selection was canceled'); } } function addImageDirectory(directoryPath, skipNotifications = false) { const linkedDirs = JSON.parse(localStorage.getItem('linkedImageDirectories') || '[]'); if (linkedDirs.some(dir => dir.path === directoryPath)) { if (!skipNotifications && window.game && window.game.showNotification) { window.game.showNotification('Directory already linked', 'warning'); } return false; } const newDir = { id: Date.now().toString() + Math.random().toString(36).substr(2, 9), path: directoryPath, name: directoryPath.split('\\').pop() || directoryPath.split('/').pop(), addedAt: new Date().toISOString() }; linkedDirs.push(newDir); localStorage.setItem('linkedImageDirectories', JSON.stringify(linkedDirs)); console.log('Added image directory:', newDir); updateImageDirectoriesList(); loadLinkedImages(); if (!skipNotifications && window.game && window.game.showNotification) { window.game.showNotification(`✅ Added image directory: ${newDir.name}`, 'success'); } return true; } function handleAddIndividualImages() { console.log('Adding individual images - feature to be implemented'); if (window.game && window.game.showNotification) { window.game.showNotification('Individual image selection coming soon', 'info'); } } function handleRefreshImageDirectories() { console.log('Refreshing image directories...'); if (window.game && window.game.showNotification) { window.game.showNotification('🔄 Refreshing image directories...', 'info'); } loadLinkedImages(); if (window.game && window.game.showNotification) { window.game.showNotification('✅ Image directories refreshed!', 'success'); } } function handleClearImageDirectories() { if (!confirm('Are you sure you want to unlink all image directories? This will not delete your actual image files.')) { return; } console.log('Clearing all image directories...'); localStorage.removeItem('linkedImageDirectories'); localStorage.removeItem('linkedIndividualImages'); updateImageDirectoriesList(); loadLinkedImages(); if (window.game && window.game.showNotification) { window.game.showNotification('🗑️ All image directories unlinked', 'info'); } } function removeImageDirectory(directoryId) { const linkedDirs = JSON.parse(localStorage.getItem('linkedImageDirectories') || '[]'); const updatedDirs = linkedDirs.filter(dir => dir.id !== directoryId); localStorage.setItem('linkedImageDirectories', JSON.stringify(updatedDirs)); updateImageDirectoriesList(); loadLinkedImages(); if (window.game && window.game.showNotification) { window.game.showNotification('🗑️ Image directory unlinked', 'info'); } } function updateImageDirectoriesList() { const listContainer = document.getElementById('linked-image-directories-list'); if (!listContainer) return; const linkedDirs = JSON.parse(localStorage.getItem('linkedImageDirectories') || '[]'); const dirCountElement = document.getElementById('lib-directories-count'); if (dirCountElement) { dirCountElement.textContent = `${linkedDirs.length} directories linked`; } if (linkedDirs.length === 0) { listContainer.innerHTML = '
No image directories linked yet
'; return; } listContainer.innerHTML = linkedDirs.map(dir => `
${dir.name}
${dir.path}
`).join(''); } async function loadLinkedImages() { const linkedDirs = JSON.parse(localStorage.getItem('linkedImageDirectories') || '[]'); let allImages = []; console.log(`📁 Loading images from ${linkedDirs.length} linked directories...`); console.log('📁 electronAPI available:', !!window.electronAPI); console.log('📁 readDirectory available:', !!(window.electronAPI && window.electronAPI.readDirectory)); if (window.electronAPI && linkedDirs.length > 0) { const imageExtensions = /\.(jpg|jpeg|png|gif|webp|bmp)$/i; for (const dir of linkedDirs) { console.log(`📁 Scanning directory: ${dir.path}`); try { if (window.electronAPI.readImageDirectoryRecursive) { const filesPromise = window.electronAPI.readImageDirectoryRecursive(dir.path); let files = []; if (filesPromise && typeof filesPromise.then === 'function') { files = await filesPromise; } else if (Array.isArray(filesPromise)) { files = filesPromise; } if (files && files.length > 0) { const imageFiles = files.filter(file => { const fileName = typeof file === 'object' ? file.name : file; return imageExtensions.test(fileName); }); if (imageFiles.length > 0) { const dirImages = imageFiles.map(file => { if (typeof file === 'object' && file.name && file.path) { return { path: file.path, name: file.name, directory: dir.name, directoryId: dir.id }; } else { const fileName = typeof file === 'object' ? file.name : file; const fullPath = window.electronAPI.pathJoin ? window.electronAPI.pathJoin(dir.path, fileName) : `${dir.path}\\${fileName}`; return { path: fullPath, name: fileName, directory: dir.name, directoryId: dir.id }; } }); allImages = allImages.concat(dirImages); console.log(`📸 Added ${dirImages.length} images from ${dir.name}`); } } } } catch (error) { console.error(`Error scanning directory ${dir.path}:`, error); } } } console.log(`📸 Total images found: ${allImages.length}`); const imageCountElement = document.getElementById('lib-image-count'); if (imageCountElement) { imageCountElement.textContent = `${allImages.length} images`; } window.allLinkedImages = allImages; // Populate filter tags after images are loaded populateImageFilterTags(); const categoryFilter = document.getElementById('lib-image-category-filter'); const selectedCategory = categoryFilter ? categoryFilter.value : 'all'; const filteredImages = filterImagesByCategory(allImages, selectedCategory); populateImageGallery(filteredImages); } function populateImageFilterTags() { const categoryFilter = document.getElementById('lib-image-category-filter'); if (!categoryFilter) { console.warn('⚠️ Category filter element not found'); return; } // Store current selection const currentValue = categoryFilter.value; // Keep the base options const baseOptions = ` `; // Get all tags if available let tagOptions = ''; if (window.globalMediaTagManager) { const allTags = window.globalMediaTagManager.getAllTags(); console.log('📋 Available tags for filter:', allTags.length); if (allTags && allTags.length > 0) { // Sort tags alphabetically const sortedTags = [...allTags].sort((a, b) => a.name.localeCompare(b.name)); tagOptions = sortedTags.map(tag => { const emoji = tag.emoji || '🏷️'; return ``; }).join(''); } } else { console.warn('⚠️ globalMediaTagManager not available'); } categoryFilter.innerHTML = baseOptions + tagOptions; console.log('✅ Filter populated with', baseOptions.split('option').length - 1, 'base options and', tagOptions.split('option').length - 1, 'tag options'); // Restore previous selection if it still exists if (currentValue) { const option = categoryFilter.querySelector(`option[value="${currentValue}"]`); if (option) { categoryFilter.value = currentValue; } } } function filterImagesByCategory(images, category) { console.log(`🔍 Filtering ${images.length} images by category: ${category}`); if (category === 'all') { return images; } // Untagged filter if (category === 'untagged') { const untagged = images.filter(image => { if (!window.globalMediaTagManager) return true; const tags = window.globalMediaTagManager.getTagsForMedia(image.path); return !tags || tags.length === 0; }); console.log(`📝 Found ${untagged.length} untagged images`); return untagged; } // Tag-based filtering if (window.globalMediaTagManager) { const allTags = window.globalMediaTagManager.getAllTags(); const tagMatch = allTags.find(tag => tag.name === category); if (tagMatch) { const tagged = images.filter(image => { const tags = window.globalMediaTagManager.getTagsForMedia(image.path); return tags && tags.some(tag => tag.name === category); }); console.log(`🏷️ Found ${tagged.length} images with tag: ${category}`); return tagged; } } console.log(`⚠️ No matching filter for category: ${category}, returning all images`); // Fallback: no filtering return images; } function populateVideoFilterTags() { const categoryFilter = document.getElementById('lib-video-category-filter'); if (!categoryFilter) { console.warn('⚠️ Video category filter element not found'); return; } // Store current selection const currentValue = categoryFilter.value; // Keep the base options const baseOptions = ` `; // Get all tags if available let tagOptions = ''; if (window.globalMediaTagManager) { const allTags = window.globalMediaTagManager.getAllTags(); console.log('📋 Available tags for video filter:', allTags.length); if (allTags && allTags.length > 0) { // Sort tags alphabetically const sortedTags = [...allTags].sort((a, b) => a.name.localeCompare(b.name)); tagOptions = sortedTags.map(tag => { const emoji = tag.emoji || '🏷️'; return ``; }).join(''); } } else { console.warn('⚠️ globalMediaTagManager not available for video filter'); } categoryFilter.innerHTML = baseOptions + tagOptions; console.log('✅ Video filter populated with', baseOptions.split('option').length - 1, 'base options and', tagOptions.split('option').length - 1, 'tag options'); // Restore previous selection if it still exists if (currentValue) { const option = categoryFilter.querySelector(`option[value="${currentValue}"]`); if (option) { categoryFilter.value = currentValue; } } } function filterVideosByCategory(videos, category) { console.log(`🔍 Filtering ${videos.length} videos by category: ${category}`); if (category === 'all') { return videos; } // Untagged filter if (category === 'untagged') { const untagged = videos.filter(video => { if (!window.globalMediaTagManager) return true; const tags = window.globalMediaTagManager.getTagsForMedia(video.path); return !tags || tags.length === 0; }); console.log(`📝 Found ${untagged.length} untagged videos`); return untagged; } // Tag-based filtering if (window.globalMediaTagManager) { const allTags = window.globalMediaTagManager.getAllTags(); const tagMatch = allTags.find(tag => tag.name === category); if (tagMatch) { const tagged = videos.filter(video => { const tags = window.globalMediaTagManager.getTagsForMedia(video.path); return tags && tags.some(tag => tag.name === category); }); console.log(`🏷️ Found ${tagged.length} videos with tag: ${category}`); return tagged; } } console.log(`⚠️ No matching filter for category: ${category}, returning all videos`); // Fallback: no filtering return videos; } function populateImageGallery(images) { const imageGallery = document.getElementById('lib-image-gallery'); if (!imageGallery) { console.error('❌ lib-image-gallery element not found!'); return; } console.log(`📸 Populating gallery with ${images.length} images`); if (images.length === 0) { imageGallery.innerHTML = `

🗃️ No images found in linked directories

Click "Add Directory" to link a folder containing images

`; return; } imageGallery.innerHTML = ''; images.forEach((image, index) => { const imgElement = document.createElement('div'); imgElement.className = 'gallery-item image-item'; // Use image path as unique media ID for tagging const mediaId = image.path; imgElement.setAttribute('data-media-id', mediaId); imgElement.setAttribute('data-image-path', image.path); imgElement.innerHTML = ` ${image.name} `; imgElement.addEventListener('click', function() { previewImage(image.path, image.name); }); imageGallery.appendChild(imgElement); // Add tagging functionality to this item if (window.mediaTaggingIntegration) { window.mediaTaggingIntegration.addTagsToGalleryItem(imgElement, mediaId, 'photo'); } }); console.log(`✅ Created ${images.length} image gallery items with tagging support`); } function previewImage(imageUrl, imageName) { let modal = document.getElementById('image-preview-modal'); if (!modal) { modal = document.createElement('div'); modal.id = 'image-preview-modal'; modal.className = 'image-preview-modal'; modal.style.cssText = ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.95); display: flex; align-items: center; justify-content: center; z-index: 10000; `; modal.innerHTML = `
${imageName}
${imageName}
`; document.body.appendChild(modal); modal.addEventListener('click', (e) => { if (e.target === modal) { modal.remove(); } }); } else { modal.querySelector('img').src = imageUrl; modal.querySelector('img').alt = imageName; modal.querySelector('div > div').textContent = imageName; modal.style.display = 'flex'; } } // Video filtering and gallery functions function populateVideoGallery(videos) { const videoGallery = document.getElementById('lib-video-gallery'); if (!videoGallery) { console.error('❌ lib-video-gallery element not found!'); return; } console.log(`🎬 Populating gallery with ${videos.length} videos`); if (videos.length === 0) { videoGallery.innerHTML = `

🎬 No videos found in linked directories

Click "Add Directory" to link a folder containing videos

`; return; } videoGallery.innerHTML = ''; videos.forEach((video, index) => { const videoElement = document.createElement('div'); videoElement.className = 'video-item gallery-item'; videoElement.setAttribute('data-video-index', index); videoElement.setAttribute('data-video-name', video.name); videoElement.setAttribute('data-video-url', video.path); // Use video path as unique media ID for tagging const mediaId = video.path; videoElement.setAttribute('data-media-id', mediaId); videoElement.setAttribute('data-video-path', video.path); // Check if video is disabled const disabledVideos = JSON.parse(localStorage.getItem('disabledVideos') || '[]'); const isDisabled = disabledVideos.includes(video.path); if (isDisabled) { videoElement.classList.add('video-disabled'); } videoElement.innerHTML = `
${isDisabled ? '
❌ DISABLED
' : ''}
`; // Add click handler to the thumbnail container for preview const thumbnailContainer = videoElement.querySelector('.video-thumbnail-container'); thumbnailContainer.addEventListener('click', function(e) { e.stopPropagation(); previewVideo(video.path, video.name); }); // Add click handler to the disable/enable button const toggleDisableBtn = videoElement.querySelector('.toggle-disable-btn'); toggleDisableBtn.addEventListener('click', function(e) { e.stopPropagation(); toggleVideoDisabled(video.path, video.name); }); videoGallery.appendChild(videoElement); // Add tagging functionality to this item if (window.mediaTaggingIntegration) { window.mediaTaggingIntegration.addTagsToGalleryItem(videoElement, mediaId, 'video'); } }); console.log(`✅ Created ${videos.length} video gallery items with tagging support`); } function previewVideo(videoUrl, videoName) { let modal = document.getElementById('video-preview-modal'); if (!modal) { modal = document.createElement('div'); modal.id = 'video-preview-modal'; modal.className = 'video-preview-modal'; modal.style.cssText = ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.95); display: flex; align-items: center; justify-content: center; z-index: 10000; `; modal.innerHTML = `
${videoName}
`; document.body.appendChild(modal); modal.addEventListener('click', (e) => { if (e.target === modal) { const video = modal.querySelector('video'); if (video) video.pause(); modal.remove(); } }); } else { const video = modal.querySelector('video'); video.src = videoUrl; video.load(); video.play(); modal.querySelector('div > div').textContent = videoName; modal.style.display = 'flex'; } } // Video Directory Management Functions function handleAddVideoDirectory() { console.log('Adding new video directory...'); if (window.electronAPI && window.electronAPI.selectDirectory) { console.log('📍 Calling electronAPI.selectDirectory...'); try { const result = window.electronAPI.selectDirectory(); console.log('📍 selectDirectory returned:', result, typeof result); // Handle both sync and async results if (result && typeof result.then === 'function') { result.then((directoryResult) => { console.log('Directory selection result (async):', directoryResult); handleVideoDirectoryResult(directoryResult); }).catch(error => { console.error('Error selecting video directory (async):', error); if (window.game && window.game.showNotification) { window.game.showNotification('❌ Failed to select directory', 'error'); } }); } else { console.log('Directory selection result (sync):', result); handleVideoDirectoryResult(result); } } catch (error) { console.error('Error calling selectDirectory:', error); if (window.game && window.game.showNotification) { window.game.showNotification('❌ Failed to open directory dialog', 'error'); } } } else { console.log('❌ electronAPI.selectDirectory not available'); if (window.game && window.game.showNotification) { window.game.showNotification('Directory linking is only available in desktop mode', 'warning'); } } } function handleVideoDirectoryResult(result) { console.log('📍 Processing video directory result:', result); let selectedPaths = []; if (result) { // Handle array of paths (multiple selection) if (Array.isArray(result)) { selectedPaths = result; } // Handle object with filePaths array else if (result.filePaths && Array.isArray(result.filePaths)) { selectedPaths = result.filePaths; } // Handle single path string (legacy support) else if (typeof result === 'string') { selectedPaths = [result]; } // Handle object with filePath property else if (result.filePath) { selectedPaths = [result.filePath]; } // Handle object with paths array else if (result.paths && Array.isArray(result.paths)) { selectedPaths = result.paths; } } if (selectedPaths && selectedPaths.length > 0) { console.log(`Selected ${selectedPaths.length} video director${selectedPaths.length === 1 ? 'y' : 'ies'}:`, selectedPaths); let addedCount = 0; selectedPaths.forEach(path => { if (addVideoDirectory(path, true)) { addedCount++; } }); if (addedCount > 0 && window.game && window.game.showNotification) { window.game.showNotification(`✅ Added ${addedCount} director${addedCount === 1 ? 'y' : 'ies'}`, 'success'); } } else { console.log('No directory selected or selection was canceled'); } } function addVideoDirectory(directoryPath, skipNotifications = false) { // Use desktopFileManager if available (preferred method) if (window.desktopFileManager && window.electronAPI) { console.log('📁 Adding directory via desktopFileManager:', directoryPath); // Check if already exists if (window.desktopFileManager.externalVideoDirectories.some(dir => dir.path === directoryPath)) { if (!skipNotifications && window.game && window.game.showNotification) { window.game.showNotification('Directory already linked', 'warning'); } return false; } // Scan directory and add it manually (async () => { try { if (!skipNotifications && window.game && window.game.showNotification) { window.game.showNotification('🔍 Scanning directory for videos...', 'info'); } const videoFiles = await window.electronAPI.readVideoDirectoryRecursive(directoryPath); console.log(`Found ${videoFiles.length} video files in ${directoryPath}`); if (videoFiles.length === 0) { if (!skipNotifications && window.game && window.game.showNotification) { window.game.showNotification('No video files found in directory', 'warning'); } return; } const dirName = directoryPath.split('\\').pop() || directoryPath.split('/').pop(); const directoryObj = { id: Date.now(), name: dirName, path: directoryPath, videoCount: videoFiles.length, dateAdded: new Date().toISOString(), isRecursive: true }; // Process videos const videoExtensions = /\.(mp4|webm|mov|avi|mkv|m4v)$/i; const linkedVideos = videoFiles .filter(file => videoExtensions.test(file.name || file)) .map(file => ({ path: file.path || file, name: file.name || (typeof file === 'string' ? file.split(/[\\\/]/).pop() : 'unknown'), directoryId: directoryObj.id, directoryName: dirName })); // Add to desktopFileManager window.desktopFileManager.externalVideoDirectories.push(directoryObj); window.desktopFileManager.allLinkedVideos.push(...linkedVideos); // Save to storage await window.desktopFileManager.saveLinkedDirectories(); await window.desktopFileManager.updateUnifiedVideoStorage(); // Refresh UI updateVideoDirectoriesList(); loadLinkedVideos(); if (!skipNotifications && window.game && window.game.showNotification) { window.game.showNotification(`✅ Added ${dirName} (${videoFiles.length} videos)`, 'success'); } } catch (error) { console.error('Error adding directory:', error); if (!skipNotifications && window.game && window.game.showNotification) { window.game.showNotification('❌ Failed to add directory', 'error'); } } })(); return true; } // Fallback to localStorage (legacy method) console.log('📁 Adding directory via localStorage (legacy):', directoryPath); let linkedDirs; try { linkedDirs = JSON.parse(localStorage.getItem('linkedVideoDirectories') || '[]'); if (!Array.isArray(linkedDirs)) { console.warn('linkedVideoDirectories was not an array, resetting to []'); linkedDirs = []; } } catch (e) { console.error('Error parsing linkedVideoDirectories, resetting to []:', e); linkedDirs = []; } if (linkedDirs.some(dir => dir.path === directoryPath)) { if (!skipNotifications && window.game && window.game.showNotification) { window.game.showNotification('Directory already linked', 'warning'); } return false; } const newDir = { id: Date.now().toString() + Math.random().toString(36).substr(2, 9), path: directoryPath, name: directoryPath.split('\\').pop() || directoryPath.split('/').pop(), addedAt: new Date().toISOString() }; linkedDirs.push(newDir); localStorage.setItem('linkedVideoDirectories', JSON.stringify(linkedDirs)); console.log('Added video directory:', newDir); updateVideoDirectoriesList(); loadLinkedVideos(); if (!skipNotifications && window.game && window.game.showNotification) { window.game.showNotification(`✅ Added video directory: ${newDir.name}`, 'success'); } return true; } function handleAddIndividualVideos() { console.log('Adding individual videos - feature to be implemented'); if (window.game && window.game.showNotification) { window.game.showNotification('Individual video selection coming soon', 'info'); } } function handleRefreshVideoDirectories() { console.log('Refreshing video directories...'); if (window.game && window.game.showNotification) { window.game.showNotification('🔄 Refreshing video directories...', 'info'); } // Use desktopFileManager refresh if available if (window.desktopFileManager && window.desktopFileManager.refreshAllDirectories) { console.log('📁 Refreshing via desktopFileManager...'); window.desktopFileManager.refreshAllDirectories().then(() => { updateVideoDirectoriesList(); loadLinkedVideos(); if (window.game && window.game.showNotification) { window.game.showNotification('✅ Video directories refreshed!', 'success'); } }); return; } // Fallback to just reloading loadLinkedVideos(); if (window.game && window.game.showNotification) { window.game.showNotification('✅ Video directories refreshed!', 'success'); } } function handleClearVideoDirectories() { if (!confirm('Are you sure you want to unlink all video directories? This will not delete your actual video files.')) { return; } // Use desktopFileManager if available if (window.desktopFileManager) { console.log('📁 Clearing directories via desktopFileManager'); (async () => { window.desktopFileManager.externalVideoDirectories = []; window.desktopFileManager.allLinkedVideos = []; await window.desktopFileManager.saveLinkedDirectories(); await window.desktopFileManager.updateUnifiedVideoStorage(); updateVideoDirectoriesList(); loadLinkedVideos(); if (window.game && window.game.showNotification) { window.game.showNotification('🗑️ All video directories unlinked', 'info'); } })(); return; } // Fallback to localStorage (legacy) localStorage.removeItem('linkedVideoDirectories'); localStorage.removeItem('linkedIndividualVideos'); updateVideoDirectoriesList(); loadLinkedVideos(); if (window.game && window.game.showNotification) { window.game.showNotification('🗑️ All video directories unlinked', 'info'); } } function updateVideoDirectoriesList() { const listContainer = document.getElementById('linked-video-directories-list'); console.log('📁 updateVideoDirectoriesList called'); console.log('📁 listContainer found:', !!listContainer); if (!listContainer) return; let linkedDirs = []; // Try to get from desktopFileManager first (preferred method) if (window.desktopFileManager && window.desktopFileManager.externalVideoDirectories) { linkedDirs = window.desktopFileManager.externalVideoDirectories; console.log('📁 Got video directories from desktopFileManager:', linkedDirs.length); console.log('📁 Directories:', linkedDirs); } else { console.log('📁 desktopFileManager not available or has no externalVideoDirectories'); console.log('📁 window.desktopFileManager:', !!window.desktopFileManager); if (window.desktopFileManager) { console.log('📁 externalVideoDirectories:', window.desktopFileManager.externalVideoDirectories); } // Fallback to localStorage (legacy support) try { const rawData = localStorage.getItem('linkedVideoDirectories'); console.log('📁 localStorage linkedVideoDirectories:', rawData); if (rawData) { const parsed = JSON.parse(rawData); // Handle both formats: direct array or object with directories property if (Array.isArray(parsed)) { linkedDirs = parsed; console.log('📁 Got video directories from localStorage (array format):', linkedDirs.length); } else if (parsed && parsed.directories && Array.isArray(parsed.directories)) { linkedDirs = parsed.directories; console.log('📁 Got video directories from localStorage (object format):', linkedDirs.length); } else { console.warn('📁 linkedVideoDirectories has unexpected format:', typeof parsed); linkedDirs = []; } } } catch (e) { console.error('Error parsing linkedVideoDirectories:', e); linkedDirs = []; } } const dirCountElement = document.getElementById('lib-video-directories-count'); if (dirCountElement) { dirCountElement.textContent = `${linkedDirs.length} directories linked`; } if (linkedDirs.length === 0) { listContainer.innerHTML = '
No video directories linked yet
'; return; } listContainer.innerHTML = linkedDirs.map(dir => `
📁 ${dir.name}
${dir.path}
`).join(''); } function removeVideoDirectory(directoryId) { // Convert to number if it's a string number const id = typeof directoryId === 'string' ? parseInt(directoryId, 10) : directoryId; // Use desktopFileManager if available if (window.desktopFileManager && window.desktopFileManager.removeVideoDirectory) { console.log('📁 Removing directory via desktopFileManager:', id); window.desktopFileManager.removeVideoDirectory(id).then(() => { updateVideoDirectoriesList(); // Reload from desktopFileManager after removal loadVideosFromDesktopFileManager(); if (window.game && window.game.showNotification) { window.game.showNotification('🗑️ Video directory unlinked', 'info'); } }).catch(error => { console.error('Error removing directory:', error); }); return; } // Fallback to localStorage (legacy) let linkedDirs; try { linkedDirs = JSON.parse(localStorage.getItem('linkedVideoDirectories') || '[]'); if (!Array.isArray(linkedDirs)) { linkedDirs = []; } } catch (e) { console.error('Error parsing linkedVideoDirectories:', e); linkedDirs = []; } const updatedDirs = linkedDirs.filter(dir => dir.id !== id && dir.id !== directoryId); localStorage.setItem('linkedVideoDirectories', JSON.stringify(updatedDirs)); updateVideoDirectoriesList(); loadLinkedVideos(); if (window.game && window.game.showNotification) { window.game.showNotification('🗑️ Video directory unlinked', 'info'); } } async function loadVideosFromDesktopFileManager() { console.log('🎬 Loading videos from desktopFileManager...'); if (!window.desktopFileManager) { console.error('❌ desktopFileManager not available'); return; } // Get all videos from the desktop file manager const allVideos = window.desktopFileManager.getAllVideos(); console.log(`✅ Loaded ${allVideos.length} videos from desktopFileManager`); // Store globally for filtering window.allLinkedVideos = allVideos; // Populate filter tags after videos are loaded populateVideoFilterTags(); // Populate the gallery populateVideoGallery(allVideos); // Update count const videoCountElement = document.getElementById('lib-video-count'); if (videoCountElement) { videoCountElement.textContent = `${allVideos.length} videos`; } } async function loadLinkedVideos() { let linkedDirs = []; // Try to get from desktopFileManager first (preferred method) if (window.desktopFileManager && window.desktopFileManager.externalVideoDirectories) { linkedDirs = window.desktopFileManager.externalVideoDirectories; console.log(`🎬 Got ${linkedDirs.length} linked directories from desktopFileManager`); } else { // Fallback to localStorage (legacy support) try { const rawData = localStorage.getItem('linkedVideoDirectories'); if (rawData) { const parsed = JSON.parse(rawData); // Handle both formats: direct array or object with directories property if (Array.isArray(parsed)) { linkedDirs = parsed; } else if (parsed && parsed.directories && Array.isArray(parsed.directories)) { linkedDirs = parsed.directories; } } } catch (e) { console.error('Error parsing linkedVideoDirectories:', e); linkedDirs = []; } } let allVideos = []; console.log(`🎬 Loading videos from ${linkedDirs.length} linked directories...`); console.log('🎬 electronAPI available:', !!window.electronAPI); console.log('🎬 readVideoDirectory available:', !!(window.electronAPI && window.electronAPI.readVideoDirectory)); if (window.electronAPI && linkedDirs.length > 0) { const videoExtensions = /\.(mp4|webm|mov|avi|mkv|m4v)$/i; for (const dir of linkedDirs) { console.log(`🎬 Scanning directory: ${dir.path}`); try { if (window.electronAPI.readVideoDirectoryRecursive) { const filesPromise = window.electronAPI.readVideoDirectoryRecursive(dir.path); let files = []; if (filesPromise && typeof filesPromise.then === 'function') { files = await filesPromise; } else if (Array.isArray(filesPromise)) { files = filesPromise; } console.log(`🎬 Found ${files.length} total files in ${dir.name}`); if (files.length > 0) { console.log(`🎬 First file sample:`, files[0]); } if (files && files.length > 0) { const videoFiles = files.filter(file => { const fileName = typeof file === 'object' ? file.name : file; const isVideo = videoExtensions.test(fileName); if (isVideo) { console.log(`✅ Video file found: ${fileName}`); } return isVideo; }); if (videoFiles.length > 0) { const dirVideos = videoFiles.map(file => { if (typeof file === 'object' && file.name && file.path) { return { path: file.path, name: file.name, directory: dir.name, directoryId: dir.id }; } else { const fileName = typeof file === 'object' ? file.name : file; const fullPath = window.electronAPI.pathJoin ? window.electronAPI.pathJoin(dir.path, fileName) : `${dir.path}\\${fileName}`; return { path: fullPath, name: fileName, directory: dir.name, directoryId: dir.id }; } }); allVideos = allVideos.concat(dirVideos); console.log(`🎬 Added ${dirVideos.length} videos from ${dir.name}`); } } } } catch (error) { console.error(`Error scanning directory ${dir.path}:`, error); } } } console.log(`🎬 Total videos found: ${allVideos.length}`); const videoCountElement = document.getElementById('lib-video-count'); if (videoCountElement) { videoCountElement.textContent = `${allVideos.length} videos`; } window.allLinkedVideos = allVideos; const categoryFilter = document.getElementById('lib-video-category-filter'); const selectedCategory = categoryFilter ? categoryFilter.value : 'all'; const filteredVideos = filterVideosByCategory(allVideos, selectedCategory); populateVideoGallery(filteredVideos); } function toggleVideoDisabled(videoPath, videoName) { const disabledVideos = JSON.parse(localStorage.getItem('disabledVideos') || '[]'); const index = disabledVideos.indexOf(videoPath); if (index > -1) { // Enable the video disabledVideos.splice(index, 1); localStorage.setItem('disabledVideos', JSON.stringify(disabledVideos)); if (window.game && window.game.showNotification) { window.game.showNotification(`✅ Enabled: ${videoName}`, 'success'); } } else { // Disable the video disabledVideos.push(videoPath); localStorage.setItem('disabledVideos', JSON.stringify(disabledVideos)); if (window.game && window.game.showNotification) { window.game.showNotification(`❌ Disabled: ${videoName}`, 'warning'); } } // Refresh the gallery to update UI if (window.allLinkedVideos) { const categoryFilter = document.getElementById('lib-video-category-filter'); const selectedCategory = categoryFilter ? categoryFilter.value : 'all'; const filteredVideos = filterVideosByCategory(window.allLinkedVideos, selectedCategory); populateVideoGallery(filteredVideos); } else if (window.desktopFileManager) { loadVideosFromDesktopFileManager(); } else { loadLinkedVideos(); } } // Make functions globally available window.setupLibraryImagesTab = setupLibraryImagesTab; window.setupLibraryAudioTab = setupLibraryAudioTab; window.setupLibraryVideoTab = setupLibraryVideoTab; window.setupLibraryGalleryTab = setupLibraryGalleryTab; window.handleAddImageDirectory = handleAddImageDirectory; window.handleAddIndividualImages = handleAddIndividualImages; window.handleRefreshImageDirectories = handleRefreshImageDirectories; window.handleClearImageDirectories = handleClearImageDirectories; window.handleAddVideoDirectory = handleAddVideoDirectory; window.handleAddIndividualVideos = handleAddIndividualVideos; window.handleRefreshVideoDirectories = handleRefreshVideoDirectories; window.handleClearVideoDirectories = handleClearVideoDirectories; window.removeImageDirectory = removeImageDirectory; window.removeVideoDirectory = removeVideoDirectory; window.deletePhoto = deletePhoto; window.downloadSinglePhoto = downloadSinglePhoto; window.downloadVerificationPhoto = downloadVerificationPhoto; window.deleteVerificationPhoto = deleteVerificationPhoto; window.showPhotoPreview = showPhotoPreview; window.showVerificationPhotoPreview = showVerificationPhotoPreview; window.updateSelectionCount = updateSelectionCount; window.loadLinkedImages = loadLinkedImages;