import { getServers } from './db.js'; import { fetchLibraries, fetchLibraryContents } from './plex.js'; import { showNotification, _ } from './utils.js'; document.addEventListener('DOMContentLoaded', () => { const m3uServerSelect = document.getElementById('m3u-server-select'); const m3uLibrariesContainer = document.getElementById('m3u-libraries-container'); const m3uLibrariesLoader = document.getElementById('m3u-libraries-loader'); const m3uLibrariesStep = document.getElementById('m3u-libraries-step'); const m3uConfigPanel = document.querySelector('.m3u-config-panel'); const m3uInfoPanel = document.querySelector('.m3u-info-panel'); const downloadM3uBtn = document.getElementById('download-m3u-btn'); let isInitialized = false; // GSAP animations gsap.set([m3uConfigPanel, m3uInfoPanel], { autoAlpha: 0, y: 50 }); async function initializeM3uGenerator() { if (isInitialized) return; isInitialized = true; gsap.to(m3uConfigPanel, { autoAlpha: 1, y: 0, duration: 0.8, ease: "power3.out" }); gsap.to(m3uInfoPanel, { autoAlpha: 1, y: 0, duration: 0.8, ease: "power3.out", delay: 0.2 }); try { const servers = await getServers(); m3uServerSelect.innerHTML = ``; servers.forEach(server => { const option = document.createElement('option'); option.value = server.id; option.textContent = server.name; m3uServerSelect.appendChild(option); }); } catch (error) { console.error('Error loading servers for M3U generator:', error); } } m3uServerSelect.addEventListener('change', async () => { const serverId = m3uServerSelect.value; m3uLibrariesContainer.innerHTML = ''; downloadM3uBtn.disabled = true; if (!serverId) { gsap.to(m3uLibrariesStep, { autoAlpha: 0, height: 0, duration: 0.3, onComplete: () => m3uLibrariesStep.style.display = 'none' }); return; } m3uLibrariesStep.style.display = 'block'; gsap.fromTo(m3uLibrariesStep, { autoAlpha: 0, height: 0 }, { autoAlpha: 1, height: 'auto', duration: 0.5, ease: "power3.out" }); m3uLibrariesContainer.innerHTML = ''; // Clear previous libraries m3uLibrariesLoader.style.display = 'block'; // Show loader downloadM3uBtn.disabled = true; try { const servers = await getServers(); const server = servers.find(s => s.id == serverId); if (!server) return; const libraries = await fetchLibraries(server.accessToken, server.publicUrl, server.localUrl); const checkboxes = []; libraries.forEach(library => { if (library.type === 'movie' || library.type === 'show' || library.type === 'music') { const checkbox = document.createElement('div'); checkbox.classList.add('form-check'); checkbox.innerHTML = ` `; m3uLibrariesContainer.appendChild(checkbox); checkboxes.push(checkbox); // Make the entire form-check div clickable checkbox.addEventListener('click', () => { const input = checkbox.querySelector('.form-check-input'); input.checked = !input.checked; // Update download button state downloadM3uBtn.disabled = m3uLibrariesContainer.querySelectorAll('input:checked').length === 0; }); } }); gsap.from(checkboxes, { opacity: 0, y: 20, stagger: 0.05, duration: 0.3 }); downloadM3uBtn.disabled = m3uLibrariesContainer.querySelectorAll('input:checked').length === 0; } catch (error) { console.error('Error fetching libraries:', error); showNotification('Error fetching libraries.', 'error'); } finally { m3uLibrariesLoader.style.display = 'none'; // Hide loader } }); downloadM3uBtn.addEventListener('click', async () => { const serverId = m3uServerSelect.value; const selectedLibraries = Array.from(m3uLibrariesContainer.querySelectorAll('input:checked')).map(input => input.value); if (!serverId || selectedLibraries.length === 0) { showNotification('Please select a server and at least one library.', 'error'); return; } downloadM3uBtn.disabled = true; downloadM3uBtn.innerHTML = ` Generating...`; try { const servers = await getServers(); const server = servers.find(s => s.id == serverId); if (!server) return; let m3uContent = '#EXTM3U\n'; let hasErrors = false; for (const libraryKey of selectedLibraries) { 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' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'playlist.m3u'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); 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'); } finally { downloadM3uBtn.disabled = false; downloadM3uBtn.innerHTML = ` __MSG_downloadM3u__`; } }); const navLink = document.getElementById('nav-m3u-generator'); if (navLink) { const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.attributeName === 'class' && mutation.target.classList.contains('active')) { initializeM3uGenerator(); } }); }); observer.observe(navLink, { attributes: true }); // Initial check in case it's already active if (navLink.classList.contains('active')) { initializeM3uGenerator(); } } });