diff --git a/index.html b/index.html index f49f75f..5b0dab4 100644 --- a/index.html +++ b/index.html @@ -422,7 +422,7 @@ transition: transform 0.2s; } - .category-item img:hover { + .category-item:hover img { transform: scale(1.05); } @@ -457,6 +457,205 @@ width: 100%; margin-bottom: 10px; } + + /* Movie details modal styles */ + .details-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.8); + z-index: 1100; + display: flex; + justify-content: center; + align-items: center; + overflow-y: auto; + padding: 20px; + } + + .details-content { + background-color: white; + width: 90%; + max-width: 900px; + border-radius: 12px; + overflow: hidden; + max-height: 90vh; + display: flex; + flex-direction: column; + } + + .dark-mode .details-content { + background-color: #333; + color: white; + } + + .night-light .details-content { + background-color: #f5deb3; + color: black; + } + + .details-backdrop { + position: relative; + height: 300px; + background-size: cover; + background-position: center; + } + + .backdrop-overlay { + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 60%; + background: linear-gradient(to top, rgba(0,0,0,0.9), transparent); + padding: 20px; + display: flex; + flex-direction: column; + justify-content: flex-end; + } + + .details-body { + padding: 20px; + overflow-y: auto; + } + + .details-close { + position: absolute; + top: 15px; + right: 15px; + width: 36px; + height: 36px; + background-color: rgba(0,0,0,0.6); + color: white; + border: none; + border-radius: 50%; + font-size: 20px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + } + + .genre-tag { + display: inline-block; + padding: 4px 10px; + margin: 0 5px 5px 0; + background-color: rgba(255,255,255,0.1); + border-radius: 20px; + font-size: 0.8rem; + } + + .dark-mode .genre-tag { + background-color: rgba(255,255,255,0.2); + } + + .night-light .genre-tag { + background-color: rgba(0,0,0,0.1); + } + + .action-buttons { + display: flex; + gap: 10px; + margin-top: 20px; + margin-bottom: 20px; + } + + .action-button { + flex: 1; + padding: 10px; + border: none; + border-radius: 5px; + font-weight: bold; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + } + + .watch-button { + background-color: #32cd32; + color: white; + } + + .trailer-button { + background-color: #ff6347; + color: white; + } + + .cast-section { + margin-top: 20px; + } + + .cast-list { + display: flex; + gap: 15px; + overflow-x: auto; + padding-bottom: 10px; + } + + .cast-item { + flex: 0 0 auto; + width: 100px; + text-align: center; + } + + .cast-image { + width: 80px; + height: 80px; + border-radius: 50%; + object-fit: cover; + margin-bottom: 8px; + } + + /* Picture-in-picture trailer styles */ + .pip-container { + position: fixed; + bottom: 20px; + right: 20px; + width: 300px; + background-color: black; + border-radius: 8px; + overflow: hidden; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); + z-index: 1200; + } + + .pip-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 12px; + background-color: rgba(0, 0, 0, 0.7); + } + + .pip-title { + color: white; + font-size: 14px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-right: 10px; + } + + .pip-close { + background: none; + border: none; + color: white; + font-size: 20px; + cursor: pointer; + padding: 0; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + } + + .pip-video { + width: 100%; + height: 169px; /* 16:9 aspect ratio for 300px width */ + } @@ -672,28 +871,63 @@ const movieTile = document.createElement('div'); movieTile.className = 'movie-tile'; movieTile.innerHTML = ` - +
Poster for ${title} -

${title}

-

${overview.substring(0, 100)}${overview.length > 100 ? '...' : ''}

-
- -
${mediaType.toUpperCase()}
+
${mediaType.toUpperCase()}
+ ${result.vote_average ? `
${result.vote_average.toFixed(1)} ⭐
` : ''} +
+

${title}

+

${overview.substring(0, 100)}${overview.length > 100 ? '...' : ''}

+
+ + +
`; container.appendChild(movieTile); } }); } - function fetchTrailer(title) { + function fetchTrailer(title, movieId = null, mediaType = 'movie') { showProgress('Finding trailer...'); - const youtubeSearchUrl = `https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=1&q=${encodeURIComponent(title + ' trailer')}&key=${youtubeApiKey}`; + + // If we have a movieId, get trailer directly from TMDB + if (movieId) { + const detailsUrl = `https://api.themoviedb.org/3/${mediaType}/${movieId}/videos?api_key=${apiKey}`; + fetch(detailsUrl) + .then(response => response.json()) + .then(data => { + const trailer = data.results.find(video => + (video.type === 'Trailer' || video.type === 'Teaser') && + video.site === 'YouTube' + ); + + if (trailer) { + openTrailerInPictureInPicture(trailer.key, title); + showProgress('Trailer loaded'); + } else { + // Fall back to YouTube search if no trailer in TMDB + searchYouTubeForTrailer(title); + } + }) + .catch(error => { + console.error('Error fetching TMDB trailer:', error); + // Fall back to YouTube search + searchYouTubeForTrailer(title); + }); + } else { + searchYouTubeForTrailer(title); + } + } + + function searchYouTubeForTrailer(title) { + const youtubeSearchUrl = `https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=1&q=${encodeURIComponent(title + ' official trailer')}&key=${youtubeApiKey}`; fetch(youtubeSearchUrl) .then(response => response.json()) .then(data => { if (data.items && data.items.length > 0) { const videoId = data.items[0].id.videoId; - openTrailerModal(videoId); + openTrailerInPictureInPicture(videoId, title); showProgress('Trailer loaded'); } else { alert('Trailer not found.'); @@ -706,6 +940,80 @@ }); } + function openTrailerInPictureInPicture(videoId, title) { + // Remove any existing pip container + const existingPip = document.querySelector('.pip-container'); + if (existingPip) { + existingPip.remove(); + } + + // Create the PiP container + const pipContainer = document.createElement('div'); + pipContainer.className = 'pip-container'; + pipContainer.innerHTML = ` +
+
Trailer: ${title}
+ +
+ + `; + + // Add to the DOM + document.body.appendChild(pipContainer); + + // Add event listener to close button + const closeButton = pipContainer.querySelector('.pip-close'); + closeButton.addEventListener('click', () => { + pipContainer.remove(); + }); + + // Make the PiP container draggable + makeDraggable(pipContainer); + } + + function makeDraggable(element) { + let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; + const header = element.querySelector('.pip-header'); + + if (header) { + header.onmousedown = dragMouseDown; + } else { + element.onmousedown = dragMouseDown; + } + + function dragMouseDown(e) { + e = e || window.event; + e.preventDefault(); + // Get the mouse cursor position at startup + pos3 = e.clientX; + pos4 = e.clientY; + document.onmouseup = closeDragElement; + // Call function when mouse moves + document.onmousemove = elementDrag; + } + + function elementDrag(e) { + e = e || window.event; + e.preventDefault(); + // Calculate the new cursor position + pos1 = pos3 - e.clientX; + pos2 = pos4 - e.clientY; + pos3 = e.clientX; + pos4 = e.clientY; + // Set the element's new position + element.style.top = (element.offsetTop - pos2) + "px"; + element.style.left = (element.offsetLeft - pos1) + "px"; + element.style.right = "auto"; // Clear right position when dragging + element.style.bottom = "auto"; // Clear bottom position when dragging + } + + function closeDragElement() { + // Stop moving when mouse button is released + document.onmouseup = null; + document.onmousemove = null; + } + } + function openTrailerModal(videoId) { const videoUrl = `https://www.youtube.com/embed/${videoId}`; const modal = document.getElementById("videoModal"); @@ -727,6 +1035,133 @@ document.title = title; } + // Detailed movie info modal + async function openMovieDetails(mediaType, id) { + try { + showProgress('Loading details...'); + + // Remove any existing details modal + const existingModal = document.getElementById('movieDetailsModal'); + if (existingModal) { + existingModal.remove(); + } + + // Fetch complete movie details with append_to_response to minimize API calls + const url = `https://api.themoviedb.org/3/${mediaType}/${id}?api_key=${apiKey}&append_to_response=credits,videos,release_dates`; + const response = await fetch(url); + const movie = await response.json(); + + // Create the modal + const detailsModal = document.createElement('div'); + detailsModal.className = 'details-modal'; + detailsModal.id = 'movieDetailsModal'; + + // Find a trailer if available + const trailer = movie.videos?.results.find(video => + (video.type === 'Trailer' || video.type === 'Teaser') && + video.site === 'YouTube' + ); + + // Get certification (rating) + let certification = ''; + if (mediaType === 'movie' && movie.release_dates?.results) { + const usReleases = movie.release_dates.results.find(country => country.iso_3166_1 === 'US'); + if (usReleases && usReleases.release_dates.length > 0) { + certification = usReleases.release_dates.find(r => r.certification)?.certification || ''; + } + } + + // Prepare content + const title = movie.title || movie.name; + const backdropPath = movie.backdrop_path ? + `https://image.tmdb.org/t/p/w1280${movie.backdrop_path}` : + (movie.poster_path ? `https://image.tmdb.org/t/p/w780${movie.poster_path}` : ''); + + detailsModal.innerHTML = ` +
+
+ +
+

${title}

+
+ ${movie.release_date ? `${new Date(movie.release_date).getFullYear()}` : ''} + ${movie.runtime ? `${Math.floor(movie.runtime / 60)}h ${movie.runtime % 60}m` : ''} + ${certification ? `${certification}` : ''} + ${movie.vote_average ? `${movie.vote_average.toFixed(1)} ⭐` : ''} +
+
+
+ +
+
+ ${movie.genres.map(genre => `${genre.name}`).join('')} +
+ +

${movie.overview}

+ +
+ + ${trailer ? ` + + ` : ''} +
+ + ${movie.credits && movie.credits.cast && movie.credits.cast.length > 0 ? ` +
+

Cast

+
+ ${movie.credits.cast.slice(0, 8).map(person => ` +
+ ${person.name} +
${person.name}
+
${person.character}
+
+ `).join('')} +
+
+ ` : ''} +
+
+ `; + + // Add to the DOM + document.body.appendChild(detailsModal); + + // Add event listener to close button + const closeBtn = detailsModal.querySelector('.details-close'); + closeBtn.addEventListener('click', () => { + detailsModal.remove(); + }); + + // Close on click outside content + detailsModal.addEventListener('click', (e) => { + if (e.target === detailsModal) { + detailsModal.remove(); + } + }); + + showProgress('Details loaded'); + } catch (error) { + console.error('Error loading movie details:', error); + showProgress('Error loading details'); + } + } + // Improved openModal function with season/episode selector async function openModal(mediaType, tmdbId, season = 1, episode = 1) { try { @@ -800,8 +1235,8 @@ videoUrl += `tv/${currentImdbId}/${currentSeason}/${currentEpisode}`; } } else { - // vidsrc.xyz format - exactly as in the original code - videoUrl = 'https://vidsrc.xyz/embed/'; + // vidsrc.xyz format - exactly as in the original code + videoUrl = 'https://vidsrc.xyz/embed/'; if (currentMediaType === 'movie') { videoUrl += `movie/${currentTmdbId}`; } else if (currentMediaType === 'tv') { @@ -990,12 +1425,21 @@ const rating = movie.vote_average ? `
${movie.vote_average.toFixed(1)} ⭐
` : ''; + // Enhanced movie card with info button movieCard.innerHTML = ` - +
${movie.title} ${rating} -

${movie.title}

-
+
+

${movie.title}

+
+ + +
`; container.appendChild(movieCard); } @@ -1047,51 +1491,52 @@ }); } - // Function to setup all dynamic categories -function setupCategories() { - // Create category sections with placeholders first - createCategorySection('💥 Action-Packed Thrills'); - createCategorySection('🏆 Critics\' Favorites'); - createCategorySection('🆕 Fresh Off The Press'); - createCategorySection('🍿 Crowd Pleasers'); - createCategorySection('👻 Spooky Stuff'); - createCategorySection('🦖 So Bad They\'re Extinct'); - createCategorySection('🧠 Movies That Make No Sense'); - createCategorySection('😴 Perfect For Napping'); - createCategorySection('🔪 Don\'t Watch Alone'); - createCategorySection('🌈 Movies Your Parents Hated'); - createCategorySection('🚗 Explosions Go Boom'); - createCategorySection('👽 Weird Stuff From Space'); - - // Fetch movies for each category - fetchMoviesByGenre(genreMap.action, '💥 Action-Packed Thrills'); - fetchHighRatedMovies('🏆 Critics\' Favorites'); - fetchRecentReleases('🆕 Fresh Off The Press'); - fetchMoviesByGenre(genreMap.comedy, '🍿 Crowd Pleasers'); - fetchMoviesByGenre(genreMap.horror, '👻 Spooky Stuff'); - fetchMoviesByGenre(genreMap.western, '🦖 So Bad They\'re Extinct'); - fetchMoviesByGenre(genreMap.scienceFiction, '🧠 Movies That Make No Sense'); - fetchMoviesByGenre(genreMap.documentary, '😴 Perfect For Napping'); - fetchMoviesByGenre(genreMap.thriller, '🔪 Don\'t Watch Alone'); - fetchMoviesByGenre(genreMap.family, '🌈 Movies Your Parents Hated'); - fetchActionExplosionMovies('🚗 Explosions Go Boom'); - fetchMoviesByGenre(genreMap.scienceFiction, '👽 Weird Stuff From Space'); -} + // Additional function to get action movies with high popularity + function fetchActionExplosionMovies(categoryTitle) { + const url = `https://api.themoviedb.org/3/discover/movie?api_key=${apiKey}&with_genres=${genreMap.action}&sort_by=popularity.desc&vote_count.gte=500`; + + fetch(url) + .then(response => response.json()) + .then(data => { + displayCategoryMovies(data.results, categoryTitle); + }) + .catch(error => { + console.error(`Error fetching explosion movies:`, error); + showProgress(`Failed to load ${categoryTitle}`); + }); + } + + // Function to setup all dynamic categories + function setupCategories() { + // Create category sections with placeholders first + createCategorySection('💥 Action-Packed Thrills'); + createCategorySection('🏆 Critics\' Favorites'); + createCategorySection('🆕 Fresh Off The Press'); + createCategorySection('🍿 Crowd Pleasers'); + createCategorySection('👻 Spooky Stuff'); + createCategorySection('🦖 So Bad They\'re Extinct'); + createCategorySection('🧠 Movies That Make No Sense'); + createCategorySection('😴 Perfect For Napping'); + createCategorySection('🔪 Don\'t Watch Alone'); + createCategorySection('🌈 Movies Your Parents Hated'); + createCategorySection('🚗 Explosions Go Boom'); + createCategorySection('👽 Weird Stuff From Space'); + + // Fetch movies for each category + fetchMoviesByGenre(genreMap.action, '💥 Action-Packed Thrills'); + fetchHighRatedMovies('🏆 Critics\' Favorites'); + fetchRecentReleases('🆕 Fresh Off The Press'); + fetchMoviesByGenre(genreMap.comedy, '🍿 Crowd Pleasers'); + fetchMoviesByGenre(genreMap.horror, '👻 Spooky Stuff'); + fetchMoviesByGenre(genreMap.western, '🦖 So Bad They\'re Extinct'); + fetchMoviesByGenre(genreMap.scienceFiction, '🧠 Movies That Make No Sense'); + fetchMoviesByGenre(genreMap.documentary, '😴 Perfect For Napping'); + fetchMoviesByGenre(genreMap.thriller, '🔪 Don\'t Watch Alone'); + fetchMoviesByGenre(genreMap.family, '🌈 Movies Your Parents Hated'); + fetchActionExplosionMovies('🚗 Explosions Go Boom'); + fetchMoviesByGenre(genreMap.scienceFiction, '👽 Weird Stuff From Space'); + } -// Additional function to get action movies with high popularity -function fetchActionExplosionMovies(categoryTitle) { - const url = `https://api.themoviedb.org/3/discover/movie?api_key=${apiKey}&with_genres=${genreMap.action}&sort_by=popularity.desc&vote_count.gte=500`; - - fetch(url) - .then(response => response.json()) - .then(data => { - displayCategoryMovies(data.results, categoryTitle); - }) - .catch(error => { - console.error(`Error fetching explosion movies:`, error); - showProgress(`Failed to load ${categoryTitle}`); - }); -} document.addEventListener('DOMContentLoaded', function() { // Setup all categories setupCategories(); @@ -1105,9 +1550,25 @@ function fetchActionExplosionMovies(categoryTitle) { if (e.key === '/' && document.activeElement.tagName !== 'INPUT') { e.preventDefault(); document.querySelector('[name="search"]').focus(); - } else if (e.key === 'Escape' && modal.style.display === 'block') { - modal.style.display = "none"; - videoIframe.src = ""; + } else if (e.key === 'Escape') { + // Close any open modals + const videoModal = document.getElementById("videoModal"); + if (videoModal.style.display === 'block') { + videoModal.style.display = "none"; + videoIframe.src = ""; + } + + // Close any details modal + const detailsModal = document.getElementById('movieDetailsModal'); + if (detailsModal) { + detailsModal.remove(); + } + + // Close any pip trailers + const pipContainer = document.querySelector('.pip-container'); + if (pipContainer) { + pipContainer.remove(); + } } }); @@ -1147,10 +1608,15 @@ function fetchActionExplosionMovies(categoryTitle) { const movieCard = document.createElement('div'); movieCard.className = 'slider-item'; movieCard.innerHTML = ` - +
${movie.title} -

${movie.title}

-
+ ${movie.vote_average ? `
${movie.vote_average.toFixed(1)} ⭐
` : ''} +
+

${movie.title}

+
+ + +
`; container.appendChild(movieCard); } @@ -1164,13 +1630,18 @@ function fetchActionExplosionMovies(categoryTitle) { if (movie.poster_path) { const movieTile = document.createElement('div'); movieTile.className = 'movie-tile'; + movieTile.innerHTML = ` - +
${movie.title} -

${movie.title}

-

${movie.overview ? movie.overview.substring(0, 100) + (movie.overview.length > 100 ? '...' : '') : 'No description available.'}

-
- + ${movie.vote_average ? `
${movie.vote_average.toFixed(1)} ⭐
` : ''} +
+

${movie.title}

+

${movie.overview ? movie.overview.substring(0, 100) + (movie.overview.length > 100 ? '...' : '') : 'No description available.'}

+
+ + +
`; container.appendChild(movieTile); }