const searchInput = document.getElementById('search-input'); const searchResults = document.getElementById('search-results'); let searchTimeout; let availableExtensions = []; searchInput.addEventListener('input', (e) => { const query = e.target.value; clearTimeout(searchTimeout); if (query.length < 2) { searchResults.classList.remove('active'); searchResults.innerHTML = ''; searchInput.style.borderRadius = '99px'; return; } searchTimeout = setTimeout(() => { fetchSearh(query); }, 300); }); document.addEventListener('click', (e) => { if (!e.target.closest('.search-wrapper')) { searchResults.classList.remove('active'); searchInput.style.borderRadius = '99px'; } }); async function fetchSearh(query) { try { let apiUrl = `/api/search?q=${encodeURIComponent(query)}`; let extensionName = null; let finalQuery = query; const parts = query.split(':'); if (parts.length >= 2) { const potentialExtension = parts[0].trim().toLowerCase(); const foundExtension = availableExtensions.find(ext => ext.toLowerCase() === potentialExtension); if (foundExtension) { extensionName = foundExtension; finalQuery = parts.slice(1).join(':').trim(); if (finalQuery.length === 0) { renderSearchResults([]); return; } apiUrl = `/api/search/${extensionName}?q=${encodeURIComponent(finalQuery)}`; } } const res = await fetch(apiUrl); const data = await res.json(); const resultsWithExtension = (data.results || []).map(anime => { if (extensionName) { return { ...anime, isExtensionResult: true, extensionName: extensionName }; } return anime; }); renderSearchResults(resultsWithExtension); } catch (err) { console.error("Search Error:", err); } } async function loadContinueWatching() { const token = localStorage.getItem('token'); if (!token) return; try { const res = await fetch('/api/list/filter?status=watching&entry_type=ANIME', { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }); if (!res.ok) return; const data = await res.json(); const list = data.results || []; renderContinueWatching('my-status', list); } catch (err) { console.error('Continue Watching Error:', err); } } function renderContinueWatching(id, list) { const container = document.getElementById(id); if (!container) return; container.innerHTML = ''; if (list.length === 0) { container.innerHTML = `
No watching anime
`; return; } list.sort((a, b) => new Date(b.updated_at) - new Date(a.updated_at)); list.forEach(item => { const el = document.createElement('div'); el.className = 'card'; el.onclick = () => { const ep = item.progress || 1; if (item.source === 'anilist') { window.location.href = `http://localhost:54322/watch/${item.entry_id}/${ep + 1}`; } else { window.location.href = `http://localhost:54322/watch/${item.entry_id}/${ep + 1}?${item.source}`; } }; const progressText = item.total_episodes ? `${item.progress || 0}/${item.total_episodes}` : `${item.progress || 0}`; el.innerHTML = `

${item.title}

Ep ${progressText} - ${item.source}

`; container.appendChild(el); }); } function renderSearchResults(results) { searchResults.innerHTML = ''; if (results.length === 0) { searchResults.innerHTML = '
No results found
'; } else { results.forEach(anime => { const title = getTitle(anime); const img = anime.coverImage.medium || anime.coverImage.large; const rating = anime.averageScore ? `${anime.averageScore}%` : 'N/A'; const year = anime.seasonYear || ''; const format = anime.format || 'TV'; let href; if (anime.isExtensionResult) { href = `/anime/${anime.extensionName}/${anime.id}`; } else { href = `/anime/${anime.id}`; } const item = document.createElement('a'); item.className = 'search-item'; item.href = href; item.innerHTML = ` ${title}
${title}
${rating} • ${year} • ${format}
`; searchResults.appendChild(item); }); } searchResults.classList.add('active'); searchInput.style.borderRadius = '12px 12px 0 0'; } function scrollCarousel(id, direction) { const container = document.getElementById(id); if(container) { const scrollAmount = container.clientWidth * 0.75; container.scrollBy({ left: direction * scrollAmount, behavior: 'smooth' }); } } let trendingAnimes = []; let currentHeroIndex = 0; let player; let heroInterval; var tag = document.createElement('script'); tag.src = "https://www.youtube.com/iframe_api"; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); function onYouTubeIframeAPIReady() { player = new YT.Player('player', { height: '100%', width: '100%', playerVars: { 'autoplay': 1, 'controls': 0, 'mute': 1, 'loop': 1, 'showinfo': 0, 'modestbranding': 1 }, events: { 'onReady': (e) => { e.target.mute(); if(trendingAnimes.length) updateHeroVideo(trendingAnimes[currentHeroIndex]); } } }); } async function fetchContent(isUpdate = false) { try { const resExt = await fetch('/api/extensions/anime'); const dataExt = await resExt.json(); if (dataExt.extensions) { availableExtensions = dataExt.extensions; console.log("Extensiones de anime disponibles cargadas:", availableExtensions); } const trendingRes = await fetch('/api/trending'); const trendingData = await trendingRes.json(); if (trendingData.results && trendingData.results.length > 0) { trendingAnimes = trendingData.results; if (!isUpdate) { updateHeroUI(trendingAnimes[0]); startHeroCycle(); } renderList('trending', trendingAnimes); } else if (!isUpdate) { setTimeout(() => fetchContent(false), 2000); } const topRes = await fetch('/api/top-airing'); const topData = await topRes.json(); if (topData.results && topData.results.length > 0) { renderList('top-airing', topData.results); } } catch (e) { console.error("Fetch Error:", e); if(!isUpdate) setTimeout(() => fetchContent(false), 5000); } } function startHeroCycle() { if(heroInterval) clearInterval(heroInterval); heroInterval = setInterval(() => { if(trendingAnimes.length > 0) { currentHeroIndex = (currentHeroIndex + 1) % trendingAnimes.length; updateHeroUI(trendingAnimes[currentHeroIndex]); } }, 10000); } function getTitle(anime) { return anime.title.english || anime.title.romaji || "Unknown Title"; } function updateHeroUI(anime) { if(!anime) return; const title = getTitle(anime); const score = anime.averageScore ? anime.averageScore + '% Match' : 'N/A'; const year = anime.seasonYear || ''; const type = anime.format || 'TV'; const desc = anime.description || 'No description available.'; const poster = anime.coverImage ? anime.coverImage.extraLarge : ''; const banner = anime.bannerImage || poster; document.getElementById('hero-title').innerText = title; document.getElementById('hero-desc').innerHTML = desc; document.getElementById('hero-score').innerText = score; document.getElementById('hero-year').innerText = year; document.getElementById('hero-type').innerText = type; document.getElementById('hero-poster').src = poster; const watchBtn = document.getElementById('watch-btn'); if(watchBtn) watchBtn.onclick = () => window.location.href = `/anime/${anime.id}`; const bgImg = document.getElementById('hero-bg-media'); if(bgImg && bgImg.src !== banner) bgImg.src = banner; updateHeroVideo(anime); document.getElementById('hero-loading-ui').style.display = 'none'; document.getElementById('hero-real-ui').style.display = 'block'; } function updateHeroVideo(anime) { if (!player || !player.loadVideoById) return; const videoContainer = document.getElementById('player'); if (anime.trailer && anime.trailer.site === 'youtube' && anime.trailer.id) { if(player.getVideoData && player.getVideoData().video_id !== anime.trailer.id) { player.loadVideoById(anime.trailer.id); player.mute(); } videoContainer.style.opacity = "1"; } else { videoContainer.style.opacity = "0"; player.stopVideo(); } } function renderList(id, list) { const container = document.getElementById(id); const firstId = list.length > 0 ? list[0].id : null; const currentFirstId = container.firstElementChild?.dataset?.id; if (currentFirstId && parseInt(currentFirstId) === firstId && container.children.length === list.length) { return; } container.innerHTML = ''; list.forEach(anime => { const title = getTitle(anime); const cover = anime.coverImage ? anime.coverImage.large : ''; const ep = anime.nextAiringEpisode ? 'Ep ' + anime.nextAiringEpisode.episode : (anime.episodes ? anime.episodes + ' Eps' : 'TV'); const score = anime.averageScore || '--'; const el = document.createElement('div'); el.className = 'card'; el.dataset.id = anime.id; el.onclick = () => window.location.href = `/anime/${anime.id}`; el.innerHTML = `

${title}

${score}% • ${ep}

`; container.appendChild(el); }); } fetchContent(); loadContinueWatching(); setInterval(() => fetchContent(true), 60000);