`;
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 += `
${timestamp}${photo.sessionType || 'Training'}
`;
}
});
// 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 += `
${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}"
`;
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 = '