From 34f9e44020f5c52d87563f9b66cd4f754ba853ee Mon Sep 17 00:00:00 2001 From: lenafx Date: Sun, 11 Jan 2026 18:16:49 +0100 Subject: [PATCH] support for extension sources on watchparties --- desktop/src/scripts/room.js | 183 +++++++++++++++++++++++++++++++----- desktop/views/room.html | 9 +- docker/src/scripts/room.js | 183 +++++++++++++++++++++++++++++++----- docker/views/room.html | 9 +- 4 files changed, 330 insertions(+), 54 deletions(-) diff --git a/desktop/src/scripts/room.js b/desktop/src/scripts/room.js index cf89c8c..75e29c9 100644 --- a/desktop/src/scripts/room.js +++ b/desktop/src/scripts/room.js @@ -88,6 +88,7 @@ const RoomsApp = (function() { // Anime Search Modal animeSearchModal: document.getElementById('anime-search-modal'), + searchSourceSelect: document.getElementById('search-source-select'), animeSearchInput: document.getElementById('anime-search-input'), animeResults: document.getElementById('anime-results'), closeSearchBtn: document.getElementById('close-search-modal'), @@ -222,6 +223,24 @@ const RoomsApp = (function() { ); extensionsReady = true; + + // AÑADE ESTO AQUÍ: Llenar el dropdown de búsqueda inmediatamente + populateSearchDropdown(); + } + + // AÑADE ESTA NUEVA FUNCIÓN FUERA (o dentro del scope de RoomsApp) + function populateSearchDropdown() { + if (!elements.searchSourceSelect) return; + + // Reiniciar y poner AniList primero + elements.searchSourceSelect.innerHTML = ''; + + extensionsStore.list.forEach(ext => { + const opt = document.createElement('option'); + opt.value = ext; + opt.textContent = ext[0].toUpperCase() + ext.slice(1); + elements.searchSourceSelect.appendChild(opt); + }); } function setupEventListeners() { @@ -369,6 +388,16 @@ const RoomsApp = (function() { }); elements.roomExtSelect.value = selectedAnimeData.source || extensionsStore.list[0]; + if (elements.searchSourceSelect) { + elements.searchSourceSelect.innerHTML = ''; + + extensionsStore.list.forEach(ext => { + const opt = document.createElement('option'); + opt.value = ext; + opt.textContent = ext[0].toUpperCase() + ext.slice(1); + elements.searchSourceSelect.appendChild(opt); + }); + } await onQuickExtensionChange(null, true); } @@ -459,17 +488,28 @@ const RoomsApp = (function() { e.preventDefault(); e.stopPropagation(); - let title, img, id; + let title, img, id, source; - const titleEl = itemLink.querySelector('.search-title'); - const imgEl = itemLink.querySelector('.search-poster, img'); + // Try to get data from dataset (Extension Results) + if (itemLink.dataset.source) { + id = itemLink.dataset.id; + source = itemLink.dataset.source; + title = itemLink.dataset.title; + img = itemLink.dataset.image; + } + // Fallback to DOM parsing (AniList/SearchManager Results) + else { + const titleEl = itemLink.querySelector('.search-title'); + const imgEl = itemLink.querySelector('.search-poster, img'); - title = titleEl ? titleEl.textContent : (itemLink.textContent.trim() || 'Unknown'); - img = imgEl ? (imgEl.src || imgEl.dataset.src || '/public/assets/placeholder.svg') : '/public/assets/placeholder.svg'; + title = titleEl ? titleEl.textContent : (itemLink.textContent.trim() || 'Unknown'); + img = imgEl ? (imgEl.src || imgEl.dataset.src || '/public/assets/placeholder.svg') : '/public/assets/placeholder.svg'; - const href = itemLink.getAttribute('href') || ''; - const hrefParts = href.split('/').filter(p => p); - id = hrefParts[hrefParts.length - 1] || itemLink.dataset.id; + const href = itemLink.getAttribute('href') || ''; + const hrefParts = href.split('/').filter(p => p); + id = hrefParts[hrefParts.length - 1] || itemLink.dataset.id; + source = 'anilist'; + } if (!id) return; @@ -477,12 +517,14 @@ const RoomsApp = (function() { id: id, title: title, image: img, - source: 'anilist' + source: source // Set the detected source }; + const animeResultObj = { id: id, title: title, - cover: img + cover: img, + source: source // Pass to next function }; showConfigStep(); @@ -649,18 +691,34 @@ const RoomsApp = (function() { let episodeToPlay = activeContext.episode; if (fromModal && elements.inpEpisode) episodeToPlay = elements.inpEpisode.value; - const ext = overrides.forceExtension || + let ext = overrides.forceExtension || (fromModal ? (configState.extension || elements.selExtension?.value) : null) || activeContext.extension || (elements.roomExtSelect ? elements.roomExtSelect.value : null); - const server = overrides.forceServer || - (fromModal ? (configState.server || elements.selServer?.value) : null) || - activeContext.server || - (elements.roomServerSelect ? elements.roomServerSelect.value : null); - const category = elements.roomSdToggle?.getAttribute('data-state') || activeContext.category || 'sub'; + let currentSource = selectedAnimeData.source || 'anilist'; + + if (currentSource !== 'anilist') { + ext = currentSource; + } + + let server = overrides.forceServer; + + if (!server) { + if (fromModal) { + server = configState.server || elements.selServer?.value; + } else if (currentSource !== 'anilist' || ext === elements.roomExtSelect?.value) { + server = elements.roomServerSelect?.value; + } + } + + if (!server && extensionsStore.settings[ext]) { + const extSettings = extensionsStore.settings[ext]; + server = extSettings.episodeServers?.[0] || 'Default'; + } + if (!ext || !server) { console.warn("Faltan datos (ext o server).", {ext, server}); if (fromModal && elements.configError) { @@ -681,8 +739,8 @@ const RoomsApp = (function() { } try { - const apiUrl = `/api/watch/stream?animeId=${selectedAnimeData.id}&episode=${episodeToPlay}&server=${encodeURIComponent(server)}&category=${category}&ext=${ext}&source=anilist`; - console.log('Fetching stream:', apiUrl); + const currentSource = selectedAnimeData.source || 'anilist'; + const apiUrl = `/api/watch/stream?animeId=${selectedAnimeData.id}&episode=${episodeToPlay}&server=${encodeURIComponent(server)}&category=${category}&ext=${ext}&source=${currentSource}`; console.log('Fetching stream:', apiUrl); const res = await fetch(apiUrl); if (!res.ok) throw new Error(`Error ${res.status}: Failed to fetch stream`); @@ -699,8 +757,13 @@ const RoomsApp = (function() { let proxyUrl = `/api/proxy?url=${encodeURIComponent(source.url)}`; const headers = data.headers || {}; - if (headers['Referer']) proxyUrl += `&referer=${encodeURIComponent(headers['Referer'])}`; + if (headers['Referer']) { + proxyUrl += `&referer=${encodeURIComponent(headers['Referer'])}`; + } + if (headers['User-Agent']) { + proxyUrl += `&userAgent=${encodeURIComponent(headers['User-Agent'])}`; + } const subtitles = (source.subtitles || []).map(sub => ({ label: sub.language, srclang: sub.id || sub.language.toLowerCase().slice(0, 2), @@ -797,14 +860,22 @@ const RoomsApp = (function() { updateCountBtn(); try { - const response = await fetch(`/api/anime/${animeResult.id}?source=anilist`); + // Use the source from the result (default to anilist) + const sourceParam = animeResult.source || 'anilist'; + + // Fetch details using the specific source + const response = await fetch(`/api/anime/${animeResult.id}?source=${sourceParam}`); + if (!response.ok) throw new Error("Failed to load anime details"); const data = await response.json(); currentAnimeDetails = data; + // Save metadata if (selectedAnimeData) { - selectedAnimeData.malId = data.idMal; + selectedAnimeData.malId = data.idMal; // Might be null for extensions, that's okay + // Ensure source is persisted + selectedAnimeData.source = sourceParam; } modalTotalEpisodes = data.episodes || 12; @@ -813,9 +884,19 @@ const RoomsApp = (function() { renderModalEpisodes(); setupModalPaginationControls(); + // Auto-select the extension in the config dropdown if it matches + if (extensionsReady && elements.selExtension && sourceParam !== 'anilist') { + // If the extension we searched with is in the list, select it + if (Array.from(elements.selExtension.options).some(o => o.value === sourceParam)) { + elements.selExtension.value = sourceParam; + handleModalExtensionChange(); + } + } + } catch (error) { console.error("Error fetching details", error); gridContainer.innerHTML = ''; + // If details fail (common with strict scrapers), show manual input document.querySelector('.manual-ep-input').style.display = 'block'; document.getElementById('modal-pagination').style.display = 'none'; } @@ -1985,12 +2066,62 @@ const RoomsApp = (function() { async function searchAnime() { const query = elements.animeSearchInput.value.trim(); if (!query) return; - elements.animeResults.innerHTML = '
Searching...
'; - if (window.SearchManager) { - await window.SearchManager.search(query, 'anime', elements.animeResults); - } else { - elements.animeResults.innerHTML = 'SearchManager not loaded'; + + const source = elements.searchSourceSelect ? elements.searchSourceSelect.value : 'anilist'; + elements.animeResults.innerHTML = '
Searching...
'; + + // 1. ANILIST SEARCH (Legacy) + if (source === 'anilist') { + if (window.SearchManager) { + await window.SearchManager.search(query, 'anime', elements.animeResults); + } else { + elements.animeResults.innerHTML = 'SearchManager not loaded'; + } + return; } + + // 2. EXTENSION SEARCH + try { + const res = await fetch(`/api/search/${source}?q=${encodeURIComponent(query)}`); + const data = await res.json(); + renderExtensionResults(data.results || [], source); + } catch (e) { + console.error("Search error:", e); + elements.animeResults.innerHTML = '
Search failed
'; + } + } + + function renderExtensionResults(results, sourceName) { + elements.animeResults.innerHTML = ''; + + if (results.length === 0) { + elements.animeResults.innerHTML = '
No results found
'; + return; + } + + results.forEach(item => { + const title = item.title.english || item.title.romaji || item.title.native || 'Unknown Title'; + const image = item.coverImage?.large || item.coverImage?.medium || '/public/assets/placeholder.svg'; + + // We add data-source attribute to identify where it came from + const div = document.createElement('a'); + div.className = 'anime-result-item'; + div.href = '#'; // Prevent navigation + div.dataset.id = item.id; + div.dataset.source = sourceName; // Important: Store the extension name + div.dataset.title = title; + div.dataset.image = image; + + div.innerHTML = ` + +
+
${escapeHtml(title)}
+
${sourceName}
+
+ `; + + elements.animeResults.appendChild(div); + }); } function escapeHtml(text) { diff --git a/desktop/views/room.html b/desktop/views/room.html index 12ffc68..b18564a 100644 --- a/desktop/views/room.html +++ b/desktop/views/room.html @@ -206,7 +206,14 @@