215 lines
6.1 KiB
JavaScript
215 lines
6.1 KiB
JavaScript
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 = '<p>No media files found in this directory.</p>';
|
|
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 = `
|
|
<div class="file-name" data-file-path="${file.path}">${file.name}</div>
|
|
<div class="file-duration">${duration}</div>
|
|
<div class="file-quality">${quality}</div>
|
|
`;
|
|
|
|
// Add click event to make file name editable
|
|
const fileNameElement = fileItem.querySelector('.file-name');
|
|
fileNameElement.addEventListener('click', function(e) {
|
|
makeEditable(e.target);
|
|
});
|
|
|
|
fileListEl.appendChild(fileItem);
|
|
});
|
|
}
|
|
|
|
// Make a file name editable
|
|
function makeEditable(element) {
|
|
// Prevent editing if already in edit mode
|
|
if (element.contentEditable === 'true') return;
|
|
|
|
// Store original text
|
|
const originalText = element.textContent;
|
|
|
|
// Make element editable
|
|
element.contentEditable = 'true';
|
|
element.focus();
|
|
element.classList.add('editing');
|
|
|
|
// Select all text when editing starts
|
|
const range = document.createRange();
|
|
range.selectNodeContents(element);
|
|
const selection = window.getSelection();
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
|
|
// Handle saving when user finishes editing
|
|
const saveEdit = async function() {
|
|
if (element.textContent.trim() !== originalText.trim()) {
|
|
// Send request to main process to rename the file
|
|
try {
|
|
const result = await ipcRenderer.invoke('rename-file', {
|
|
oldPath: element.dataset.filePath,
|
|
newName: element.textContent.trim()
|
|
});
|
|
|
|
if (result.success) {
|
|
console.log('File renamed successfully:', result.message);
|
|
// Update the file name in the UI to reflect the change
|
|
element.textContent = element.textContent.trim();
|
|
} else {
|
|
console.error('Failed to rename file:', result.error);
|
|
// Revert to original name on failure
|
|
element.textContent = originalText;
|
|
}
|
|
} catch (error) {
|
|
console.error('Error renaming file:', error);
|
|
// Revert to original name on error
|
|
element.textContent = originalText;
|
|
}
|
|
}
|
|
|
|
// Clean up
|
|
element.contentEditable = 'false';
|
|
element.classList.remove('editing');
|
|
};
|
|
|
|
// Save on Enter key or blur
|
|
element.addEventListener('keydown', function(e) {
|
|
if (e.key === 'Enter') {
|
|
e.preventDefault();
|
|
saveEdit();
|
|
}
|
|
});
|
|
|
|
element.addEventListener('blur', saveEdit);
|
|
}
|
|
|
|
// Search shows function
|
|
async function searchShows() {
|
|
const query = searchInput.value.trim();
|
|
|
|
if (query.length < 2) {
|
|
searchResultsEl.innerHTML = '';
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const result = await ipcRenderer.invoke('search-tvdb', query);
|
|
|
|
if (result.success) {
|
|
displaySearchResults(result.results);
|
|
} else {
|
|
throw new Error(result.error);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error searching shows:', error);
|
|
searchResultsEl.innerHTML = `<p>Error: ${error.message}</p>`;
|
|
}
|
|
}
|
|
|
|
// Display search results
|
|
function displaySearchResults(results) {
|
|
searchResultsEl.innerHTML = '';
|
|
|
|
if (results.length === 0) {
|
|
searchResultsEl.innerHTML = '<p>No shows found.</p>';
|
|
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'); |