// SHINYY WORKSPACE - Shared Data System // Connects Face Swapper, Compressor, and Workflow Hub class ShinyyWorkspace { constructor() { this.storageKey = 'shinyy_workspace'; this.data = this.loadData(); this.listeners = []; } // Load data from localStorage loadData() { try { const stored = localStorage.getItem(this.storageKey); return stored ? JSON.parse(stored) : this.getDefaultData(); } catch (e) { console.warn('Failed to load workspace data:', e); return this.getDefaultData(); } } // Get default data structure getDefaultData() { return { faceSwapResults: [], compressedFiles: [], workflowFiles: [], activeWorkflow: null, recentProjects: [], settings: { autoTransfer: true, preserveQuality: true, defaultCompression: 0.8 } }; } // Save data to localStorage saveData() { try { localStorage.setItem(this.storageKey, JSON.stringify(this.data)); this.notifyListeners(); } catch (e) { console.error('Failed to save workspace data:', e); } } // Add face swap results addFaceSwapResults(results) { console.log('addFaceSwapResults called with:', results); if (!Array.isArray(results)) results = [results]; const processedResults = results.map(result => ({ id: result.id || this.generateId(), type: 'face_swap', timestamp: result.timestamp || Date.now(), originalSize: result.originalSize || 0, resultImage: result.resultImage || result.image, preview: result.preview || result.resultImage, fileName: result.fileName || `face_swap_${Date.now()}.jpg`, transferredTo: result.transferredTo || [], metadata: { sourceImages: result.sourceImages || [], targetImages: result.targetImages || [], processingTime: result.processingTime || 0, model: result.model || 'default' } })); console.log('Processed results:', processedResults); this.data.faceSwapResults.push(...processedResults); console.log('Total face swap results after adding:', this.data.faceSwapResults.length); this.data.recentProjects.unshift({ type: 'face_swap', timestamp: Date.now(), count: processedResults.length, preview: processedResults[0].preview }); this.saveData(); console.log('Data saved to localStorage'); return processedResults; } // Add compressed files addCompressedFiles(files) { if (!Array.isArray(files)) files = [files]; const processedFiles = files.map(file => ({ id: this.generateId(), type: 'compressed', timestamp: Date.now(), originalFile: file.originalFile || file.name, compressedUrl: file.compressedUrl || file.url, compressedSize: file.compressedSize || file.size, originalSize: file.originalSize || 0, fileName: file.fileName || file.name, format: file.format || 'jpeg', quality: file.quality || 0.8, metadata: { compressionRatio: file.compressionRatio || 0, processingTime: file.processingTime || 0, adjustments: file.adjustments || {} } })); this.data.compressedFiles.push(...processedFiles); this.data.recentProjects.unshift({ type: 'compression', timestamp: Date.now(), count: processedFiles.length, preview: processedFiles[0].compressedUrl }); this.saveData(); return processedFiles; } // Get files for transfer between tools getFilesForTransfer(fromTool, toTool) { switch(fromTool) { case 'face_swapper': return this.data.faceSwapResults.filter(result => !result.transferredTo?.includes(toTool)); case 'compressor': return this.data.compressedFiles.filter(file => !file.transferredTo?.includes(toTool)); default: return []; } } // Transfer files between tools transferFiles(fileIds, fromTool, toTool) { fileIds.forEach(id => { let file; if (fromTool === 'face_swapper') { file = this.data.faceSwapResults.find(f => f.id === id); } else if (fromTool === 'compressor') { file = this.data.compressedFiles.find(f => f.id === id); } if (file) { if (!file.transferredTo) file.transferredTo = []; file.transferredTo.push(toTool); // Add to workflow files for the target tool this.data.workflowFiles.push({ ...file, transferredFrom: fromTool, transferredTo: toTool, transferTimestamp: Date.now() }); } }); this.saveData(); } // Get workflow files for specific tool getWorkflowFiles(tool) { return this.data.workflowFiles.filter(file => file.transferredTo === tool); } // Set active workflow setActiveWorkflow(workflow) { this.data.activeWorkflow = { ...workflow, timestamp: Date.now() }; this.saveData(); } // Get active workflow getActiveWorkflow() { return this.data.activeWorkflow; } // Clear old data (cleanup) clearOldData(daysOld = 7) { const cutoffTime = Date.now() - (daysOld * 24 * 60 * 60 * 1000); this.data.faceSwapResults = this.data.faceSwapResults.filter(item => item.timestamp > cutoffTime); this.data.compressedFiles = this.data.compressedFiles.filter(item => item.timestamp > cutoffTime); this.data.workflowFiles = this.data.workflowFiles.filter(item => item.transferTimestamp > cutoffTime); this.data.recentProjects = this.data.recentProjects.filter(item => item.timestamp > cutoffTime); this.saveData(); } // Export data exportData() { return JSON.stringify(this.data, null, 2); } // Import data importData(jsonData) { try { const imported = JSON.parse(jsonData); this.data = { ...this.data, ...imported }; this.saveData(); return true; } catch (e) { console.error('Failed to import data:', e); return false; } } // Utility functions generateId() { return Math.random().toString(36).substr(2, 9) + Date.now().toString(36); } formatFileSize(bytes) { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } // Event listeners addListener(callback) { this.listeners.push(callback); } removeListener(callback) { this.listeners = this.listeners.filter(l => l !== callback); } notifyListeners() { this.listeners.forEach(callback => callback(this.data)); } // Statistics getStats() { return { totalFaceSwaps: this.data.faceSwapResults.length, totalCompressions: this.data.compressedFiles.length, totalTransfers: this.data.workflowFiles.length, recentProjects: this.data.recentProjects.length, storageUsed: JSON.stringify(this.data).length }; } } // Global workspace instance window.shinyyWorkspace = new ShinyyWorkspace(); // Auto-cleanup old data on load setTimeout(() => { window.shinyyWorkspace.clearOldData(); }, 5000); // Export for module systems if (typeof module !== 'undefined' && module.exports) { module.exports = ShinyyWorkspace; }