257 lines
7.9 KiB
JavaScript
257 lines
7.9 KiB
JavaScript
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<Object>} 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<Object>} 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; |