support for extension sources on watchparties
This commit is contained in:
@@ -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 = '<option value="anilist">AniList</option>';
|
||||
|
||||
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 = '<option value="anilist">AniList</option>';
|
||||
|
||||
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 = '<div style="padding:20px;text-align:center;color:#888;">Searching...</div>';
|
||||
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 = '<div style="padding:20px;text-align:center;color:#888;"><div class="spinner" style="margin:0 auto 10px;"></div>Searching...</div>';
|
||||
|
||||
// 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 = '<div style="padding:20px;text-align:center;color:#ff6b6b;">Search failed</div>';
|
||||
}
|
||||
}
|
||||
|
||||
function renderExtensionResults(results, sourceName) {
|
||||
elements.animeResults.innerHTML = '';
|
||||
|
||||
if (results.length === 0) {
|
||||
elements.animeResults.innerHTML = '<div style="padding:20px;text-align:center;color:#888;">No results found</div>';
|
||||
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 = `
|
||||
<img src="${image}" class="search-poster" loading="lazy">
|
||||
<div class="search-info">
|
||||
<div class="search-title">${escapeHtml(title)}</div>
|
||||
<div class="search-meta" style="color:var(--brand-color)">${sourceName}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
elements.animeResults.appendChild(div);
|
||||
});
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
|
||||
@@ -204,7 +204,14 @@
|
||||
<div id="step-search">
|
||||
<h2 class="modal-title">Select Anime</h2>
|
||||
<div class="search-bar">
|
||||
<input type="text" id="anime-search-input" placeholder="Search anime..." />
|
||||
<div class="quick-select-wrapper" style="min-width: 130px; background: rgba(255,255,255,0.05);">
|
||||
<select id="search-source-select" class="quick-select">
|
||||
<option value="anilist">AniList</option>
|
||||
</select>
|
||||
<div class="select-arrow">▼</div>
|
||||
</div>
|
||||
|
||||
<input type="text" id="anime-search-input" placeholder="Search anime..." autocomplete="off"/>
|
||||
<button id="anime-search-btn">Search</button>
|
||||
</div>
|
||||
<div id="anime-results" class="anime-results"></div>
|
||||
|
||||
Reference in New Issue
Block a user