diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..02a1473 --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +# Logs +logs +*.log +npm-debug.log +yarn-debug.log +yarn-error.log + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Dependency directories +node_modules/ +jspm_packages/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional type definition files +*.d.ts + +# Environment variables +.env +.env.local +.env.*.local + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Build directories +build/ +dist/ +*.build.js + +# Temporary files +*.tmp +*.temp \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5ed73b7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,49 @@ +{ + "name": "MovieMapper", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "fluent-ffmpeg": "^2.1.3" + } + }, + "node_modules/async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==" + }, + "node_modules/fluent-ffmpeg": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.3.tgz", + "integrity": "sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT", + "dependencies": { + "async": "^0.2.9", + "which": "^1.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..eeefc5f --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "fluent-ffmpeg": "^2.1.3" + } +} diff --git a/renderer.js b/renderer.js new file mode 100644 index 0000000..4b06c42 --- /dev/null +++ b/renderer.js @@ -0,0 +1,215 @@ +const { ipcRenderer } = require('electron'); +const path = require('path'); + +// DOM Elements +const selectDirBtn = document.getElementById('select-dir-btn'); +const selectedDirEl = document.getElementById('selected-dir'); +const searchInput = document.getElementById('search-input'); +const searchResultsEl = document.getElementById('search-results'); +const fileListEl = document.getElementById('file-list'); + +// Current state +let currentDirectory = null; +let currentFiles = []; + +// Event Listeners +selectDirBtn.addEventListener('click', selectDirectory); +searchInput.addEventListener('input', debounce(searchShows, 300)); + +// Select directory function +async function selectDirectory() { + try { + // Use IPC to handle directory selection + const result = await ipcRenderer.invoke('select-directory'); + + if (result.success) { + const directory = result.directory; + currentDirectory = directory; + selectedDirEl.textContent = `Selected: ${directory}`; + + // Scan the directory for media files + await scanDirectory(directory); + } else { + throw new Error(result.error); + } + } catch (error) { + console.error('Error selecting directory:', error); + alert(`Error: ${error.message}`); + } +} + +// Scan directory for media files +async function scanDirectory(directoryPath) { + try { + const result = await ipcRenderer.invoke('scan-directory', directoryPath); + + if (result.success) { + currentFiles = result.files; + displayFiles(currentFiles); + } else { + throw new Error(result.error); + } + } catch (error) { + console.error('Error scanning directory:', error); + alert(`Error scanning directory: ${error.message}`); + } +} + +// Display files in the UI +function displayFiles(files) { + fileListEl.innerHTML = ''; + + if (files.length === 0) { + fileListEl.innerHTML = '
No media files found in this directory.
'; + return; + } + + files.forEach(file => { + const fileItem = document.createElement('div'); + fileItem.className = 'file-item'; + + // Use actual duration from file metadata + const duration = file.duration || '00:00'; + const quality = 'unknown'; // Placeholder for quality + + fileItem.innerHTML = ` +Error: ${error.message}
`; + } +} + +// Display search results +function displaySearchResults(results) { + searchResultsEl.innerHTML = ''; + + if (results.length === 0) { + searchResultsEl.innerHTML = 'No shows found.
'; + return; + } + + results.forEach(show => { + const resultItem = document.createElement('div'); + resultItem.className = 'search-result-item'; + resultItem.textContent = show.seriesName || show.name; + resultItem.addEventListener('click', () => selectShow(show)); + searchResultsEl.appendChild(resultItem); + }); +} + +// Select a show (placeholder for actual implementation) +function selectShow(show) { + console.log('Selected show:', show); + // In a real implementation, this would fetch show details and display seasons + alert(`Selected show: ${show.seriesName || show.name}`); +} + +// Debounce function for search input +function debounce(func, wait) { + let timeout; + return function executedFunction(...args) { + const later = () => { + clearTimeout(timeout); + func(...args); + }; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; +} + +// Initialize the application +console.log('Movie Mapper application initialized'); \ No newline at end of file diff --git a/utils/fileUtils.js b/utils/fileUtils.js new file mode 100644 index 0000000..f70bc92 --- /dev/null +++ b/utils/fileUtils.js @@ -0,0 +1,120 @@ +const fs = require('fs'); +const path = require('path'); +const ffmpeg = require('fluent-ffmpeg'); + +// Supported media file extensions +const MEDIA_EXTENSIONS = ['.mp4', '.mkv', '.avi', '.mov', '.flv', '.webm']; + +/** + * Check if a file has a media extension + * @param {string} filePath - Path to the file + * @returns {boolean} - True if file is a media file + */ +function isMediaFile(filePath) { + const ext = path.extname(filePath).toLowerCase(); + return MEDIA_EXTENSIONS.includes(ext); +} + +/** + * Scan a directory for media files + * @param {string} directoryPath - Path to the directory to scan + * @returns {Promise