diff --git a/js/m3u-generator.js b/js/m3u-generator.js index 089d619..ba0e092 100644 --- a/js/m3u-generator.js +++ b/js/m3u-generator.js @@ -110,20 +110,32 @@ document.addEventListener('DOMContentLoaded', () => { if (!server) return; let m3uContent = '#EXTM3U\n'; + let hasErrors = false; + for (const libraryKey of selectedLibraries) { - const libraryElement = m3uLibrariesContainer.querySelector(`#library-${libraryKey}`); - const libraryType = libraryElement.getAttribute('data-type'); - const libraryTitle = libraryElement ? libraryElement.nextElementSibling.textContent.trim() : ''; - const items = await fetchLibraryContents(server.accessToken, server.publicUrl, server.localUrl, libraryKey, libraryType); - items.forEach(item => { - const duration = item.duration ? Math.round(item.duration / 1000) : -1; - const groupTitle = item.seriesTitle && item.seasonNumber - ? `group-title="${item.seriesTitle} - Season ${item.seasonNumber}"` - : `group-title="${libraryTitle}"`; - const tvgLogo = item.thumb ? `tvg-logo="${item.thumb}"` : ''; - m3uContent += `#EXTINF:${duration} ${tvgLogo} ${groupTitle},${item.title}\n`; - m3uContent += `${item.url}\n`; - }); + try { + const libraryElement = m3uLibrariesContainer.querySelector(`#library-${libraryKey}`); + const libraryType = libraryElement.getAttribute('data-type'); + const libraryTitle = libraryElement ? libraryElement.nextElementSibling.textContent.trim() : ''; + const items = await fetchLibraryContents(server.accessToken, server.publicUrl, server.localUrl, libraryKey, libraryType, 60000); + items.forEach(item => { + const duration = item.duration ? Math.round(item.duration / 1000) : -1; + const groupTitle = item.seriesTitle && item.seasonNumber + ? `group-title="${item.seriesTitle} - Season ${item.seasonNumber}"` + : `group-title="${libraryTitle}"`; + const tvgLogo = item.thumb ? `tvg-logo="${item.thumb}"` : ''; + m3uContent += `#EXTINF:${duration} ${tvgLogo} ${groupTitle},${item.title}\n`; + m3uContent += `${item.url}\n`; + }); + } catch (error) { + hasErrors = true; + console.error(`Error processing library ${libraryKey}:`, error); + showNotification(`Error processing library ${libraryKey}. Skipping.`, 'warning'); + } + } + + if (m3uContent.split('\n').length <= 2 && hasErrors) { + throw new Error("All selected libraries failed to process."); } const blob = new Blob([m3uContent], { type: 'audio/x-mpegurl;charset=utf-8' }); @@ -136,7 +148,11 @@ document.addEventListener('DOMContentLoaded', () => { document.body.removeChild(a); URL.revokeObjectURL(url); - showNotification('M3U playlist downloaded successfully.', 'success'); + if (hasErrors) { + showNotification('M3U generated with some errors. Some libraries may be missing.', 'warning'); + } else { + showNotification('M3U playlist downloaded successfully.', 'success'); + } } catch (error) { console.error('Error generating M3U file:', error); showNotification('Error generating M3U file.', 'error'); diff --git a/js/plex.js b/js/plex.js index a6a07aa..3cd7c36 100644 --- a/js/plex.js +++ b/js/plex.js @@ -42,10 +42,10 @@ export async function fetchLibraries(accessToken, publicUrl, localUrl) { })); } -export async function fetchLibraryContents(accessToken, publicUrl, localUrl, libraryKey, libraryType) { +export async function fetchLibraryContents(accessToken, publicUrl, localUrl, libraryKey, libraryType, timeout = 7000) { const endpoint = libraryType === 'show' ? 'allLeaves' : 'all'; const url = `${localUrl || publicUrl}/library/sections/${libraryKey}/${endpoint}?X-Plex-Token=${accessToken}`; - const contentXml = await fetchSectionContent(url, new AbortController().signal); + const contentXml = await fetchSectionContent(url, new AbortController().signal, timeout); const items = []; const baseUrl = localUrl || publicUrl;