const { ipcRenderer } = require('electron'); /** * TagManager - Manages file tagging functionality */ class TagManager { constructor() { this.tagColors = { extra: '#28a745', // Green behindTheScenes: '#17a2b8', // Teal delete: '#dc3545' // Red }; } /** * Tag a file with a specific tag type * @param {string} filePath - File path * @param {string} tagType - Tag type (extra, behindTheScenes, delete) * @param {Function} callback - Callback function */ tagFile(filePath, tagType, callback) { console.log(`Tagging file ${filePath} as ${tagType}`); // Find the file item in the UI const fileItems = document.querySelectorAll('.file-item'); fileItems.forEach(item => { const fileNameElement = item.querySelector('.file-name'); if (fileNameElement && fileNameElement.dataset.filePath === filePath) { this._applyTagVisuals(item, tagType); item.setAttribute('data-tagged-' + tagType, 'true'); this._enablePlayButton(item); } }); // Update the tagged count display if (callback && typeof callback === 'function') { callback(); } console.log(`File ${filePath} tagged as ${tagType}`); } /** * Apply visual styling for a tag * @param {HTMLElement} fileItem - File item element * @param {string} tagType - Tag type * @private */ _applyTagVisuals(fileItem, tagType) { const tagIcon = fileItem.querySelector(`.${tagType}-tag`); if (tagIcon) { const tagColor = this.tagColors[tagType]; // Make the icon fully saturated and highlight tagIcon.style.opacity = '1'; tagIcon.style.filter = 'none'; tagIcon.style.color = tagColor; tagIcon.style.textShadow = `0 0 15px ${tagColor}`; tagIcon.style.transform = 'scale(1.3)'; } } /** * Enable play button for a file * @param {HTMLElement} fileItem - File item element * @private */ _enablePlayButton(fileItem) { const playButton = fileItem.querySelector('.play-button'); if (playButton) { playButton.style.opacity = '1'; playButton.style.cursor = 'pointer'; playButton.disabled = false; playButton.style.pointerEvents = 'auto'; } } /** * Untag a file * @param {string} filePath - File path * @param {string} tagType - Tag type */ untagFile(filePath, tagType) { console.log(`Untagging file ${filePath} from ${tagType}`); // Find the file item in the UI const fileItems = document.querySelectorAll('.file-item'); fileItems.forEach(item => { const fileNameElement = item.querySelector('.file-name'); if (fileNameElement && fileNameElement.dataset.filePath === filePath) { this._removeTagVisuals(item, tagType); item.removeAttribute('data-tagged-' + tagType); this._checkAndDisablePlayButton(item); } }); } /** * Remove tag visuals * @param {HTMLElement} fileItem - File item element * @param {string} tagType - Tag type * @private */ _removeTagVisuals(fileItem, tagType) { const tagIcon = fileItem.querySelector(`.${tagType}-tag`); if (tagIcon) { // Reset to original appearance tagIcon.style.opacity = '0.7'; tagIcon.style.filter = 'none'; tagIcon.style.color = ''; tagIcon.style.textShadow = 'none'; tagIcon.style.transform = 'scale(1)'; tagIcon.style.boxShadow = 'none'; } } /** * Check and disable play button if no tags remain * @param {HTMLElement} fileItem - File item element * @private */ _checkAndDisablePlayButton(fileItem) { const hasOtherTags = fileItem.hasAttribute('data-tagged-extra') || fileItem.hasAttribute('data-tagged-behind-the-scenes') || fileItem.hasAttribute('data-tagged-delete'); if (!hasOtherTags) { const playButton = fileItem.querySelector('.play-button'); if (playButton) { playButton.style.opacity = '0.3'; playButton.style.cursor = 'default'; playButton.disabled = true; playButton.style.pointerEvents = 'none'; } } } /** * Move a tagged file * @param {string} filePath - File path * @param {string} tagType - Tag type * @returns {Promise} Move result */ async moveTaggedFile(filePath, tagType) { console.log(`Moving file ${filePath} to ${tagType} folder`); // Send request to main process to move the file const result = await ipcRenderer.invoke('move-file-to-folder', { filePath: filePath, folderName: tagType }); if (result.success) { console.log(`File moved successfully to ${tagType} folder`); return { success: true, filePath }; } else { console.error(`Failed to move file: ${result.error}`); return { success: false, error: result.error, filePath }; } } /** * Move all tagged files * @param {Function} onUpdateCount - Callback to update count * @param {Function} onEpisodesUpdated - Callback to update episodes * @param {Function} onMatchCheck - Callback to check episode match * @returns {Promise} Results summary */ async moveAllTaggedFiles(onUpdateCount, onEpisodesUpdated, onMatchCheck) { const taggedItems = document.querySelectorAll('.file-item[data-tagged-extra], .file-item[data-tagged-behind-the-scenes], .file-item[data-tagged-delete]'); if (taggedItems.length === 0) { console.log('No tagged files to move'); return { success: true, successful: 0, failed: 0 }; } console.log(`Moving ${taggedItems.length} tagged files`); // Visual feedback - change circle color while processing const taggedCircle = document.getElementById('tagged-circle'); if (taggedCircle) { taggedCircle.style.backgroundColor = '#ffc107'; taggedCircle.style.pointerEvents = 'none'; } const results = []; for (const item of taggedItems) { const filePath = item.querySelector('.file-name').dataset.filePath; let tagType; if (item.hasAttribute('data-tagged-extra')) { tagType = 'extra'; } else if (item.hasAttribute('data-tagged-behind-the-scenes')) { tagType = 'behindTheScenes'; } else if (item.hasAttribute('data-tagged-delete')) { tagType = 'delete'; } const result = await this.moveTaggedFile(filePath, tagType); results.push(result); if (result.success) { // Remove the item from the file list after successful move item.remove(); } } // Reset circle appearance if (taggedCircle) { taggedCircle.style.backgroundColor = ''; taggedCircle.style.pointerEvents = ''; } // Update the tagged count after all moves if (onUpdateCount) onUpdateCount(); if (onEpisodesUpdated) onEpisodesUpdated(); if (onMatchCheck) onMatchCheck(); // Summary of results const successful = results.filter(r => r.success).length; const failed = results.filter(r => !r.success).length; if (failed > 0) { alert(`Moved ${successful} files. ${failed} files failed to move.`); } else if (successful > 0) { console.log(`Successfully moved all ${successful} files`); } return { success: true, successful, failed }; } /** * Get all tagged files * @returns {Array} Array of tagged file objects */ getTaggedFiles() { const taggedItems = document.querySelectorAll('.file-item[data-tagged-extra], .file-item[data-tagged-behind-the-scenes], .file-item[data-tagged-delete]'); const taggedFiles = []; taggedItems.forEach(item => { const filePath = item.querySelector('.file-name').dataset.filePath; let tagType; if (item.hasAttribute('data-tagged-extra')) { tagType = 'extra'; } else if (item.hasAttribute('data-tagged-behind-the-scenes')) { tagType = 'behindTheScenes'; } else if (item.hasAttribute('data-tagged-delete')) { tagType = 'delete'; } taggedFiles.push({ filePath, tagType }); }); return taggedFiles; } } module.exports = TagManager;