Spaces:
Running
Running
const TMDB_API_KEY = '3fd2be6f0c70a2a598f084ddfb75487c'; | |
const TMDB_IMAGE_BASE = 'https://image.tmdb.org/t/p/original'; | |
let currentHeroIndex = 0; | |
let trendingMovies = []; | |
function updateHeroSlide() { | |
const movie = trendingMovies[currentHeroIndex]; | |
const heroElement = document.getElementById('trending-hero'); | |
heroElement.style.backgroundImage = `linear-gradient(to top, #0f171e, transparent), url(${TMDB_IMAGE_BASE}${movie.backdrop_path})`; | |
document.getElementById('hero-title').textContent = movie.title; | |
document.getElementById('hero-overview').textContent = movie.overview; | |
heroElement.onclick = () => openModal(movie.id, 'movie'); | |
updateDots(); | |
} | |
function updateDots() { | |
const dotsContainer = document.getElementById('trending-dots'); | |
dotsContainer.innerHTML = trendingMovies.slice(0, 5).map((_, index) => `<div class="dot ${index === currentHeroIndex ? 'active' : ''}" | |
onclick="setHeroSlide(${index})"></div>`).join(''); | |
dotsContainer.onclick = e => e.stopPropagation(); | |
} | |
function setHeroSlide(index) { | |
event.stopPropagation(); | |
currentHeroIndex = index; | |
updateHeroSlide(); | |
} | |
function autoAdvanceHero() { | |
currentHeroIndex = (currentHeroIndex + 1) % Math.min(trendingMovies.length, 5); | |
updateHeroSlide(); | |
} | |
async function fetchTMDBData(endpoint) { | |
const response = await fetch(`https://api.themoviedb.org/3${endpoint}?api_key=${TMDB_API_KEY}`); | |
return await response.json(); | |
} | |
async function searchTMDB(query) { | |
if (!query) return []; | |
const response = await fetch(`https://api.themoviedb.org/3/search/multi?api_key=${TMDB_API_KEY}&query=${encodeURIComponent(query)}`); | |
const data = await response.json(); | |
return data.results.filter(item => item.media_type !== 'person' && item.poster_path && item.vote_average > 0); | |
} | |
let searchTimeout; | |
const searchInput = document.getElementById('search-input'); | |
const searchResults = document.getElementById('search-results'); | |
searchInput.addEventListener('input', e => { | |
clearTimeout(searchTimeout); | |
const query = e.target.value; | |
if (query.length < 2) { | |
searchResults.style.display = 'none'; | |
return; | |
} | |
searchTimeout = setTimeout(async () => { | |
const results = await searchTMDB(query); | |
displaySearchResults(results); | |
}, 300); | |
}); | |
function displaySearchResults(results) { | |
searchResults.innerHTML = ''; | |
if (results.length === 0) { | |
searchResults.style.display = 'none'; | |
return; | |
} | |
results.slice(0, 6).forEach(item => { | |
const div = document.createElement('div'); | |
div.className = 'search-result-item'; | |
div.innerHTML = ` | |
<img class="search-result-thumb" | |
src="${item.poster_path ? TMDB_IMAGE_BASE + item.poster_path : 'https://via.placeholder.com/60x90'}" | |
alt="${item.title || item.name}"> | |
<div class="search-result-info"> | |
<div class="search-result-title">${item.title || item.name}</div> | |
<div class="search-result-rating">β ${item.vote_average.toFixed(1)}</div> | |
</div> | |
`; | |
div.onclick = () => { | |
openModal(item.id, item.media_type); | |
searchResults.style.display = 'none'; | |
searchInput.value = ''; | |
}; | |
searchResults.appendChild(div); | |
}); | |
searchResults.style.display = 'block'; | |
} | |
document.addEventListener('click', e => { | |
if (!searchResults.contains(e.target) && e.target !== searchInput) { | |
searchResults.style.display = 'none'; | |
} | |
}); | |
async function initializeApp() { | |
const trending = await fetchTMDBData('/trending/movie/day'); | |
trendingMovies = trending.results.filter(movie => movie.backdrop_path && movie.vote_average); | |
updateHeroSlide(); | |
setInterval(autoAdvanceHero, 5000); | |
const [newMovies, newTV] = await Promise.all([fetchTMDBData('/movie/now_playing'), fetchTMDBData('/tv/on_the_air')]); | |
const newReleasesContainer = document.getElementById('new-releases'); | |
const combinedNew = [...newMovies.results, ...newTV.results].sort(() => Math.random() - 0.5).slice(0, 20); | |
combinedNew.forEach(item => { | |
const type = item.title ? 'movie' : 'tv'; | |
const card = createMovieCard(item, type); | |
if (card) newReleasesContainer.appendChild(card); | |
}); | |
const topMovies = await fetchTMDBData('/movie/top_rated'); | |
const topMoviesContainer = document.getElementById('top-movies'); | |
topMovies.results.forEach(movie => { | |
const card = createMovieCard(movie, 'movie'); | |
if (card) topMoviesContainer.appendChild(card); | |
}); | |
const topTV = await fetchTMDBData('/tv/top_rated'); | |
const topTVContainer = document.getElementById('top-tv'); | |
topTV.results.forEach(show => { | |
const card = createMovieCard(show, 'tv'); | |
if (card) topTVContainer.appendChild(card); | |
}); | |
} | |
function createMovieCard(item, type) { | |
if (!item.poster_path || !item.vote_average) return null; | |
const div = document.createElement('div'); | |
div.className = 'movie-card'; | |
div.innerHTML = ` | |
<img class="movie-poster" src="${TMDB_IMAGE_BASE}${item.poster_path}" alt="${item.title || item.name}"> | |
<div class="movie-title">${item.title || item.name}</div> | |
`; | |
div.onclick = () => openModal(item.id, type); | |
return div; | |
} | |
async function openModal(id, type) { | |
const modal = document.getElementById('modal'); | |
const iframe = document.getElementById('player-iframe'); | |
const loader = document.getElementById('loader'); | |
modal.style.display = 'block'; | |
iframe.style.opacity = '0'; | |
loader.style.display = 'block'; | |
const [details, credits] = await Promise.all([ | |
fetchTMDBData(`/${type}/${id}`), | |
fetchTMDBData(`/${type}/${id}/credits`) | |
]); | |
const movieDetails = document.createElement('div'); | |
movieDetails.className = 'movie-details'; | |
let seasonEpisodeHTML = ''; | |
if (type === 'tv') { | |
const seasons = details.seasons.filter(s => s.season_number > 0); | |
seasonEpisodeHTML = ` | |
<div class="season-episode-selector"> | |
<select id="season-select" onchange="updateEpisodes(${id})"> | |
${seasons.map(season => | |
`<option value="${season.season_number}">Season ${season.season_number}</option>` | |
).join('')} | |
</select> | |
<select id="episode-select" onchange="updatePlayer(${id})"> | |
${Array.from({length: seasons[0]?.episode_count || 0}, (_, i) => | |
`<option value="${i + 1}">Episode ${i + 1}</option>` | |
).join('')} | |
</select> | |
</div> | |
`; | |
} | |
movieDetails.innerHTML = ` | |
<h2>${details.title || details.name}</h2> | |
<div class="movie-meta"> | |
<span>${new Date(details.release_date || details.first_air_date).getFullYear()}</span> | |
<span>${details.vote_average.toFixed(1)} β </span> | |
<span>${details.runtime || details.episode_run_time?.[0] || ''} min</span> | |
</div> | |
<p class="movie-overview">${details.overview}</p> | |
${seasonEpisodeHTML} | |
<h3>Cast</h3> | |
<div class="cast-list"> | |
${credits.cast.slice(0, 6).map(actor => actor.profile_path ? ` | |
<div class="cast-item"> | |
<img class="cast-photo" | |
src="${TMDB_IMAGE_BASE + actor.profile_path}" | |
alt="${actor.name}"> | |
<div class="cast-info"> | |
<div class="cast-name">${actor.name}</div> | |
<div class="cast-character">${actor.character}</div> | |
</div> | |
</div> | |
` : '').join('')} | |
</div> | |
`; | |
const modalContent = document.querySelector('.modal-content'); | |
modalContent.innerHTML = ''; | |
const iframeContainer = document.createElement('div'); | |
iframeContainer.className = 'iframe-container'; | |
iframeContainer.innerHTML = ` | |
<span class="close-modal" onclick="closeModal()">Γ</span> | |
<div class="loader" id="loader"></div> | |
<iframe id="player-iframe" allowfullscreen></iframe> | |
`; | |
modalContent.appendChild(iframeContainer); | |
modalContent.appendChild(movieDetails); | |
const newIframe = document.getElementById('player-iframe'); | |
if (type === 'movie') { | |
newIframe.src = `https://picfy.xyz/embed/movie/${id}`; | |
} else { | |
newIframe.src = `https://picfy.xyz/embed/tv/${id}/1/1`; | |
} | |
newIframe.onload = () => { | |
document.getElementById('loader').style.display = 'none'; | |
newIframe.style.opacity = '1'; | |
}; | |
} | |
async function updateEpisodes(showId) { | |
const seasonSelect = document.getElementById('season-select'); | |
const episodeSelect = document.getElementById('episode-select'); | |
const seasonNum = seasonSelect.value; | |
const seasonDetails = await fetchTMDBData(`/tv/${showId}/season/${seasonNum}`); | |
episodeSelect.innerHTML = seasonDetails.episodes.map(episode => | |
`<option value="${episode.episode_number}">Episode ${episode.episode_number}</option>` | |
).join(''); | |
updatePlayer(showId); | |
} | |
function updatePlayer(showId) { | |
const seasonSelect = document.getElementById('season-select'); | |
const episodeSelect = document.getElementById('episode-select'); | |
const iframe = document.getElementById('player-iframe'); | |
const loader = document.getElementById('loader'); | |
loader.style.display = 'block'; | |
iframe.style.opacity = '0'; | |
iframe.src = `https://picfy.xyz/embed/tv/${showId}/${seasonSelect.value}/${episodeSelect.value}`; | |
iframe.onload = () => { | |
loader.style.display = 'none'; | |
iframe.style.opacity = '1'; | |
}; | |
} | |
function closeModal() { | |
const modal = document.getElementById('modal'); | |
const iframe = document.getElementById('player-iframe'); | |
iframe.src = ''; | |
modal.style.display = 'none'; | |
} | |
document.getElementById('modal').addEventListener('click', e => { | |
if (e.target.id === 'modal') { | |
closeModal(); | |
} | |
}); | |
document.querySelectorAll('.carousel-arrow').forEach(arrow => { | |
arrow.addEventListener('click', () => { | |
const carousel = arrow.parentElement.querySelector('.carousel'); | |
const scrollAmount = 400; | |
if (arrow.classList.contains('prev')) { | |
carousel.scrollLeft -= scrollAmount; | |
} else { | |
carousel.scrollLeft += scrollAmount; | |
} | |
}); | |
}); | |
initializeApp(); |