- Split large renderer.js file into 8 separate class files: - AppState.js: Manages application state - FileListManager.js: Handles file list display and manipulation - TagManager.js: Manages file tagging functionality - EpisodeManager.js: Handles episode number editing and highlighting - SearchManager.js: Manages TVDB search and show selection - FileManager.js: Handles file operations - ModalManager.js: Manages video preview modal - ProgressManager.js: Manages progress display - UIManager.js: Main coordinator for UI functionality - Follows SOLID principles and single responsibility - Improves code maintainability and testability - All syntax verified with node --check
195 lines
5.2 KiB
JavaScript
195 lines
5.2 KiB
JavaScript
const path = require('path');
|
|
const { ipcRenderer } = require('electron');
|
|
|
|
/**
|
|
* FileManager - Handles file operations
|
|
*/
|
|
class FileManager {
|
|
constructor() {
|
|
}
|
|
|
|
/**
|
|
* Select directory
|
|
* @returns {Promise<Object>} Selection result
|
|
*/
|
|
async selectDirectory() {
|
|
try {
|
|
// Use IPC to handle directory selection
|
|
const result = await ipcRenderer.invoke('select-directory');
|
|
|
|
if (result.success) {
|
|
return result;
|
|
} else {
|
|
throw new Error(result.error);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error selecting directory:', error);
|
|
return { success: false, error: error.message };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Scan directory for media files
|
|
* @param {string} directoryPath - Directory path
|
|
* @returns {Promise<Object>} Scan result
|
|
*/
|
|
async scanDirectory(directoryPath) {
|
|
try {
|
|
const result = await ipcRenderer.invoke('scan-directory', directoryPath);
|
|
|
|
if (result.success) {
|
|
return result;
|
|
} else {
|
|
throw new Error(result.error);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error scanning directory:', error);
|
|
return { success: false, error: error.message };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Rename a file
|
|
* @param {string} oldPath - Old file path
|
|
* @param {string} newName - New file name
|
|
* @returns {Promise<Object>} Rename result
|
|
*/
|
|
async renameFile(oldPath, newName) {
|
|
try {
|
|
const result = await ipcRenderer.invoke('rename-file', {
|
|
oldPath: oldPath,
|
|
newName: newName
|
|
});
|
|
|
|
if (result.success) {
|
|
return result;
|
|
} else {
|
|
throw new Error(result.error);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error renaming file:', error);
|
|
return { success: false, error: error.message };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Begin mapping files to Jellyfin naming convention
|
|
* @param {string} directory - Directory path
|
|
* @param {Array} files - Files to map
|
|
* @param {string|null} tvdbId - TVDB ID
|
|
* @returns {Promise<Object>} Mapping result
|
|
*/
|
|
async beginMapping(directory, files, tvdbId) {
|
|
try {
|
|
const result = await ipcRenderer.invoke('begin-mapping', {
|
|
directory: directory,
|
|
files: files,
|
|
tvdbId: tvdbId
|
|
});
|
|
|
|
if (result.success) {
|
|
return result;
|
|
} else {
|
|
throw new Error(result.error);
|
|
}
|
|
} catch (error) {
|
|
console.error('Mapping error:', error);
|
|
return { success: false, error: error.message };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Log audit event
|
|
* @param {string} directoryPath - Directory path
|
|
* @param {string} action - Action type
|
|
* @param {Object} details - Event details
|
|
* @returns {Promise<Object>} Audit result
|
|
*/
|
|
async logAuditEvent(directoryPath, action, details) {
|
|
try {
|
|
const result = await ipcRenderer.invoke('log-audit-event', {
|
|
directoryPath: directoryPath,
|
|
action: action,
|
|
details: details
|
|
});
|
|
return result;
|
|
} catch (error) {
|
|
console.error('Failed to log audit event:', error);
|
|
return { success: false, error: error.message };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Move file to folder
|
|
* @param {string} filePath - File path
|
|
* @param {string} folderName - Folder name
|
|
* @returns {Promise<Object>} Move result
|
|
*/
|
|
async moveFileToFolder(filePath, folderName) {
|
|
try {
|
|
const result = await ipcRenderer.invoke('move-file-to-folder', {
|
|
filePath: filePath,
|
|
folderName: folderName
|
|
});
|
|
return result;
|
|
} catch (error) {
|
|
console.error('Error moving file:', error);
|
|
return { success: false, error: error.message };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Log file info for debugging
|
|
* @param {string} filePath - File path
|
|
* @returns {Promise<Object>} File info result
|
|
*/
|
|
async logFileInfo(filePath) {
|
|
try {
|
|
const result = await ipcRenderer.invoke('log-file-info', filePath);
|
|
return result;
|
|
} catch (error) {
|
|
console.error('Error logging file info:', error);
|
|
return { success: false, error: error.message };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Open file in player
|
|
* @param {string} filePath - File path
|
|
*/
|
|
openFileInPlayer(filePath) {
|
|
ipcRenderer.invoke('open-file-in-player', filePath);
|
|
}
|
|
|
|
/**
|
|
* Collect file data with episode ranges from UI order
|
|
* @param {HTMLElement} fileListEl - File list element
|
|
* @returns {Array} Array of file data objects
|
|
*/
|
|
collectFileData(fileListEl) {
|
|
const fileItems = fileListEl.querySelectorAll('.file-item:not(.folder-item)');
|
|
const filesToMap = [];
|
|
|
|
fileItems.forEach((item, index) => {
|
|
const fileNameEl = item.querySelector('.file-name');
|
|
const qualityEl = item.querySelector('.file-quality');
|
|
const episodeNumEl = item.querySelector('.episode-number');
|
|
|
|
if (fileNameEl && fileNameEl.dataset.filePath) {
|
|
const episodeStart = episodeNumEl ? parseInt(episodeNumEl.dataset.episodeStart || (index + 1)) : (index + 1);
|
|
const episodeEnd = episodeNumEl ? parseInt(episodeNumEl.dataset.episodeEnd || episodeStart) : episodeStart;
|
|
|
|
filesToMap.push({
|
|
filePath: fileNameEl.dataset.filePath,
|
|
quality: qualityEl ? qualityEl.textContent : '',
|
|
episodeStart: episodeStart,
|
|
episodeEnd: episodeEnd
|
|
});
|
|
}
|
|
});
|
|
|
|
return filesToMap;
|
|
}
|
|
}
|
|
|
|
module.exports = FileManager; |