const { ipcRenderer } = require('electron'); /** * SearchManager - Manages TVDB search and show selection */ class SearchManager { constructor(searchInputEl, searchResultsEl) { this.searchInputEl = searchInputEl; this.searchResultsEl = searchResultsEl; this.currentShow = null; } /** * Search shows by query * @param {string} query - Search query * @returns {Promise} Search results */ async searchShows(query) { if (query.length < 2) { this.clearSearchResults(); return { success: true, results: [] }; } try { const result = await ipcRenderer.invoke('search-tvdb', query); if (result.success) { this.displaySearchResults(result.results); return result; } else { throw new Error(result.error); } } catch (error) { console.error('Error searching shows:', error); this.searchResultsEl.innerHTML = `

Error: ${error.message}

`; return { success: false, error: error.message }; } } /** * Display search results * @param {Array} results - Search results array */ displaySearchResults(results) { this.searchResultsEl.innerHTML = ''; if (results.length === 0) { this.searchResultsEl.innerHTML = '

No shows found.

'; return; } results.forEach(show => { const resultItem = document.createElement('div'); resultItem.className = 'search-result-item'; // Create a more detailed display with separate elements const showName = show.seriesName || show.name; const showYear = show.firstAired ? show.firstAired.split('-')[0] : ''; resultItem.innerHTML = `
${showName}
${showYear ? `
${showYear}
` : ''} `; resultItem.addEventListener('click', () => this.selectShow(show)); this.searchResultsEl.appendChild(resultItem); }); } /** * Clear search results */ clearSearchResults() { this.searchResultsEl.innerHTML = ''; } /** * Select a show * @param {Object} show - Show object * @param {Function} onShowTitleUpdate - Callback to update show title * @param {Function} onSeasonsDisplay - Callback to display seasons */ async selectShow(show, onShowTitleUpdate, onSeasonsDisplay) { console.log('Selected show:', show); this.currentShow = show; // Clear search results when a show is selected this.clearSearchResults(); // Display show title if (onShowTitleUpdate) { onShowTitleUpdate(show.seriesName || show.name); } // Fetch show details including seasons try { const result = await ipcRenderer.invoke('get-show-details', show.id); if (result.success && result.data) { const showData = result.data; const seasons = showData.seasons || []; // Display seasons if (onSeasonsDisplay) { if (seasons.length === 0) { onSeasonsDisplay('

Seasons will be loaded when you click on a season.

'); } else { onSeasonsDisplay(seasons); } } } else { console.error('Failed to fetch show details:', result.error); if (onSeasonsDisplay) { onSeasonsDisplay('

Error loading seasons: ' + (result.error || 'Unknown error') + '

'); } } } catch (error) { console.error('Error fetching show details:', error); if (onSeasonsDisplay) { onSeasonsDisplay('

Error loading seasons: ' + error.message + '

'); } } } /** * Display seasons * @param {Array} seasons - Seasons array * @param {Function} onEpisodeFetch - Callback to fetch episodes * @param {Function} onBackToSeasons - Callback to go back to seasons */ displaySeasons(seasons, onEpisodeFetch, onBackToSeasons) { const seasonsContainer = document.getElementById('seasons-container'); seasonsContainer.innerHTML = ''; if (!seasons || seasons.length === 0) { seasonsContainer.innerHTML = '

No seasons available.

'; return; } seasons.forEach(season => { const seasonItem = document.createElement('div'); seasonItem.className = 'season-item'; // Format season display with episode count let seasonDisplay = `Season ${season.number}`; if (season.type && season.type !== 'Unknown') { seasonDisplay += ` (${season.type})`; } const episodeCount = season.episodeCount || 0; seasonItem.innerHTML = `
${seasonDisplay} ${episodeCount} eps
`; // Add click event to fetch episodes seasonItem.addEventListener('click', () => { // Remove selected class from all seasons document.querySelectorAll('.season-item').forEach(item => item.classList.remove('selected')); // Add selected class to clicked season seasonItem.classList.add('selected'); if (onEpisodeFetch) { onEpisodeFetch(season.number); } }); seasonsContainer.appendChild(seasonItem); }); } /** * Fetch and display episodes * @param {string} showId - Show ID * @param {number} seasonNumber - Season number * @param {Function} onEpisodesDisplay - Callback to display episodes */ async fetchAndDisplayEpisodes(showId, seasonNumber, onEpisodesDisplay) { try { // Show loading message while fetching const seasonsContainer = document.getElementById('seasons-container'); seasonsContainer.innerHTML = '

Loading episodes...

'; const result = await ipcRenderer.invoke('get-season-episodes', showId, seasonNumber); if (result.success && result.data) { const episodes = result.data.episodes || []; if (onEpisodesDisplay) { onEpisodesDisplay(episodes); } } else { console.error('Failed to fetch episodes:', result.error); if (onEpisodesDisplay) { onEpisodesDisplay(null, 'Error loading episodes: ' + (result.error || 'Unknown error')); } } } catch (error) { console.error('Error fetching episodes:', error); // Show a more user-friendly error message if (onEpisodesDisplay) { onEpisodesDisplay(null, `Error loading episodes: ${error.message || 'Failed to load episodes'}`); } } } /** * Display episodes * @param {Array} episodes - Episodes array * @param {number} episodeCount - Episode count * @param {Function} onBackToSeasons - Callback to go back to seasons * @param {Function} onFileListUpdate - Callback to update file list */ displayEpisodes(episodes, episodeCount, onBackToSeasons, onFileListUpdate) { const seasonsContainer = document.getElementById('seasons-container'); seasonsContainer.innerHTML = ''; if (!episodes || episodes.length === 0) { seasonsContainer.innerHTML = '

No episodes available.

'; if (onBackToSeasons) onBackToSeasons(0); return; } if (onBackToSeasons) { onBackToSeasons(episodes.length); } // Create a back button to return to seasons const backButton = document.createElement('div'); backButton.className = 'season-item'; backButton.innerHTML = '← Back to Seasons'; backButton.addEventListener('click', () => { if (onBackToSeasons) onBackToSeasons(0); if (onEpisodesDisplay) { // Re-display seasons } }); seasonsContainer.appendChild(backButton); // Create episodes container const episodesContainer = document.createElement('div'); episodesContainer.id = 'episodes-container'; episodes.forEach(episode => { const episodeRow = document.createElement('div'); episodeRow.className = 'episode-item'; // Format episode display const episodeName = episode.name || 'Untitled'; const episodeRuntime = episode.runtime ? `${episode.runtime}m` : ''; episodeRow.innerHTML = `
E${episode.number} ${episodeName} ${episodeRuntime ? `${episodeRuntime}` : ''}
`; episodesContainer.appendChild(episodeRow); }); seasonsContainer.appendChild(episodesContainer); // Also update the main file list to show episode matching information if (onFileListUpdate) { onFileListUpdate(episodes); } } /** * Update file list with episode info * @param {Array} episodes - Episodes array * @param {Array} currentFiles - Current files array */ updateFileListWithEpisodeInfo(episodes, currentFiles) { // When we have episodes, we want to show basic episode information if (currentFiles && currentFiles.length > 0 && episodes && episodes.length > 0) { console.log('Updating file list with episode info for', episodes.length, 'episodes'); // Create a simple note about episode availability const fileListContainer = document.getElementById('file-list'); // Add a simple note about episodes const episodeNote = document.createElement('div'); episodeNote.style.marginTop = '15px'; episodeNote.style.padding = '10px'; episodeNote.style.backgroundColor = '#e7f3ff'; episodeNote.style.border = '1px solid #b3d9ff'; episodeNote.style.borderRadius = '5px'; episodeNote.style.fontSize = '14px'; episodeNote.innerHTML = ` Episode Information: ${episodes.length} episodes available for this show.
Episode details are displayed in the sidebar. `; // Add this note to the file list container if (fileListContainer.firstChild) { fileListContainer.insertBefore(episodeNote, fileListContainer.firstChild); } else { fileListContainer.appendChild(episodeNote); } } } /** * Test TVDB API connectivity * @returns {Promise} Test result */ async testTVDBAPI() { try { const result = await ipcRenderer.invoke('test-tvdb-api'); console.log('TVDB API Test:', result); if (result.success) { console.log('TVDB API is working:', result.message); alert('TVDB API Test Successful!\n' + result.message); } else { console.error('TVDB API test failed:', result.error); alert('TVDB API Test Failed!\n' + result.error); } return result; } catch (error) { console.error('Error testing TVDB API:', error); alert('Error testing TVDB API: ' + error.message); return { success: false, error: error.message }; } } } module.exports = SearchManager;