Spaces:
Running
Running
Upload 2 files
Browse files
amz.png
ADDED
![]() |
script.js
ADDED
@@ -0,0 +1,258 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const TMDB_API_KEY = '3fd2be6f0c70a2a598f084ddfb75487c';
|
2 |
+
const TMDB_IMAGE_BASE = 'https://image.tmdb.org/t/p/original';
|
3 |
+
let currentHeroIndex = 0;
|
4 |
+
let trendingMovies = [];
|
5 |
+
function updateHeroSlide() {
|
6 |
+
const movie = trendingMovies[currentHeroIndex];
|
7 |
+
const heroElement = document.getElementById('trending-hero');
|
8 |
+
heroElement.style.backgroundImage = `linear-gradient(to top, #0f171e, transparent), url(${TMDB_IMAGE_BASE}${movie.backdrop_path})`;
|
9 |
+
document.getElementById('hero-title').textContent = movie.title;
|
10 |
+
document.getElementById('hero-overview').textContent = movie.overview;
|
11 |
+
heroElement.onclick = () => openModal(movie.id, 'movie');
|
12 |
+
updateDots();
|
13 |
+
}
|
14 |
+
function updateDots() {
|
15 |
+
const dotsContainer = document.getElementById('trending-dots');
|
16 |
+
dotsContainer.innerHTML = trendingMovies.slice(0, 5).map((_, index) => `<div class="dot ${index === currentHeroIndex ? 'active' : ''}"
|
17 |
+
onclick="setHeroSlide(${index})"></div>`).join('');
|
18 |
+
dotsContainer.onclick = e => e.stopPropagation();
|
19 |
+
}
|
20 |
+
function setHeroSlide(index) {
|
21 |
+
event.stopPropagation();
|
22 |
+
currentHeroIndex = index;
|
23 |
+
updateHeroSlide();
|
24 |
+
}
|
25 |
+
function autoAdvanceHero() {
|
26 |
+
currentHeroIndex = (currentHeroIndex + 1) % Math.min(trendingMovies.length, 5);
|
27 |
+
updateHeroSlide();
|
28 |
+
}
|
29 |
+
async function fetchTMDBData(endpoint) {
|
30 |
+
const response = await fetch(`https://api.themoviedb.org/3${endpoint}?api_key=${TMDB_API_KEY}`);
|
31 |
+
return await response.json();
|
32 |
+
}
|
33 |
+
async function searchTMDB(query) {
|
34 |
+
if (!query) return [];
|
35 |
+
const response = await fetch(`https://api.themoviedb.org/3/search/multi?api_key=${TMDB_API_KEY}&query=${encodeURIComponent(query)}`);
|
36 |
+
const data = await response.json();
|
37 |
+
return data.results.filter(item => item.media_type !== 'person' && item.poster_path && item.vote_average > 0);
|
38 |
+
}
|
39 |
+
let searchTimeout;
|
40 |
+
const searchInput = document.getElementById('search-input');
|
41 |
+
const searchResults = document.getElementById('search-results');
|
42 |
+
searchInput.addEventListener('input', e => {
|
43 |
+
clearTimeout(searchTimeout);
|
44 |
+
const query = e.target.value;
|
45 |
+
if (query.length < 2) {
|
46 |
+
searchResults.style.display = 'none';
|
47 |
+
return;
|
48 |
+
}
|
49 |
+
searchTimeout = setTimeout(async () => {
|
50 |
+
const results = await searchTMDB(query);
|
51 |
+
displaySearchResults(results);
|
52 |
+
}, 300);
|
53 |
+
});
|
54 |
+
function displaySearchResults(results) {
|
55 |
+
searchResults.innerHTML = '';
|
56 |
+
if (results.length === 0) {
|
57 |
+
searchResults.style.display = 'none';
|
58 |
+
return;
|
59 |
+
}
|
60 |
+
results.slice(0, 6).forEach(item => {
|
61 |
+
const div = document.createElement('div');
|
62 |
+
div.className = 'search-result-item';
|
63 |
+
div.innerHTML = `
|
64 |
+
<img class="search-result-thumb"
|
65 |
+
src="${item.poster_path ? TMDB_IMAGE_BASE + item.poster_path : 'https://via.placeholder.com/60x90'}"
|
66 |
+
alt="${item.title || item.name}">
|
67 |
+
<div class="search-result-info">
|
68 |
+
<div class="search-result-title">${item.title || item.name}</div>
|
69 |
+
<div class="search-result-rating">★ ${item.vote_average.toFixed(1)}</div>
|
70 |
+
</div>
|
71 |
+
`;
|
72 |
+
div.onclick = () => {
|
73 |
+
openModal(item.id, item.media_type);
|
74 |
+
searchResults.style.display = 'none';
|
75 |
+
searchInput.value = '';
|
76 |
+
};
|
77 |
+
searchResults.appendChild(div);
|
78 |
+
});
|
79 |
+
searchResults.style.display = 'block';
|
80 |
+
}
|
81 |
+
document.addEventListener('click', e => {
|
82 |
+
if (!searchResults.contains(e.target) && e.target !== searchInput) {
|
83 |
+
searchResults.style.display = 'none';
|
84 |
+
}
|
85 |
+
});
|
86 |
+
async function initializeApp() {
|
87 |
+
const trending = await fetchTMDBData('/trending/movie/day');
|
88 |
+
trendingMovies = trending.results.filter(movie => movie.backdrop_path && movie.vote_average);
|
89 |
+
updateHeroSlide();
|
90 |
+
setInterval(autoAdvanceHero, 5000);
|
91 |
+
const [newMovies, newTV] = await Promise.all([fetchTMDBData('/movie/now_playing'), fetchTMDBData('/tv/on_the_air')]);
|
92 |
+
const newReleasesContainer = document.getElementById('new-releases');
|
93 |
+
const combinedNew = [...newMovies.results, ...newTV.results].sort(() => Math.random() - 0.5).slice(0, 20);
|
94 |
+
combinedNew.forEach(item => {
|
95 |
+
const type = item.title ? 'movie' : 'tv';
|
96 |
+
const card = createMovieCard(item, type);
|
97 |
+
if (card) newReleasesContainer.appendChild(card);
|
98 |
+
});
|
99 |
+
const topMovies = await fetchTMDBData('/movie/top_rated');
|
100 |
+
const topMoviesContainer = document.getElementById('top-movies');
|
101 |
+
topMovies.results.forEach(movie => {
|
102 |
+
const card = createMovieCard(movie, 'movie');
|
103 |
+
if (card) topMoviesContainer.appendChild(card);
|
104 |
+
});
|
105 |
+
const topTV = await fetchTMDBData('/tv/top_rated');
|
106 |
+
const topTVContainer = document.getElementById('top-tv');
|
107 |
+
topTV.results.forEach(show => {
|
108 |
+
const card = createMovieCard(show, 'tv');
|
109 |
+
if (card) topTVContainer.appendChild(card);
|
110 |
+
});
|
111 |
+
}
|
112 |
+
function createMovieCard(item, type) {
|
113 |
+
if (!item.poster_path || !item.vote_average) return null;
|
114 |
+
const div = document.createElement('div');
|
115 |
+
div.className = 'movie-card';
|
116 |
+
div.innerHTML = `
|
117 |
+
<img class="movie-poster" src="${TMDB_IMAGE_BASE}${item.poster_path}" alt="${item.title || item.name}">
|
118 |
+
<div class="movie-title">${item.title || item.name}</div>
|
119 |
+
`;
|
120 |
+
div.onclick = () => openModal(item.id, type);
|
121 |
+
return div;
|
122 |
+
}
|
123 |
+
async function openModal(id, type) {
|
124 |
+
const modal = document.getElementById('modal');
|
125 |
+
const iframe = document.getElementById('player-iframe');
|
126 |
+
const loader = document.getElementById('loader');
|
127 |
+
modal.style.display = 'block';
|
128 |
+
iframe.style.opacity = '0';
|
129 |
+
loader.style.display = 'block';
|
130 |
+
|
131 |
+
const [details, credits] = await Promise.all([
|
132 |
+
fetchTMDBData(`/${type}/${id}`),
|
133 |
+
fetchTMDBData(`/${type}/${id}/credits`)
|
134 |
+
]);
|
135 |
+
|
136 |
+
const movieDetails = document.createElement('div');
|
137 |
+
movieDetails.className = 'movie-details';
|
138 |
+
|
139 |
+
let seasonEpisodeHTML = '';
|
140 |
+
if (type === 'tv') {
|
141 |
+
const seasons = details.seasons.filter(s => s.season_number > 0);
|
142 |
+
seasonEpisodeHTML = `
|
143 |
+
<div class="season-episode-selector">
|
144 |
+
<select id="season-select" onchange="updateEpisodes(${id})">
|
145 |
+
${seasons.map(season =>
|
146 |
+
`<option value="${season.season_number}">Season ${season.season_number}</option>`
|
147 |
+
).join('')}
|
148 |
+
</select>
|
149 |
+
<select id="episode-select" onchange="updatePlayer(${id})">
|
150 |
+
${Array.from({length: seasons[0]?.episode_count || 0}, (_, i) =>
|
151 |
+
`<option value="${i + 1}">Episode ${i + 1}</option>`
|
152 |
+
).join('')}
|
153 |
+
</select>
|
154 |
+
</div>
|
155 |
+
`;
|
156 |
+
}
|
157 |
+
|
158 |
+
movieDetails.innerHTML = `
|
159 |
+
<h2>${details.title || details.name}</h2>
|
160 |
+
<div class="movie-meta">
|
161 |
+
<span>${new Date(details.release_date || details.first_air_date).getFullYear()}</span>
|
162 |
+
<span>${details.vote_average.toFixed(1)} ★</span>
|
163 |
+
<span>${details.runtime || details.episode_run_time?.[0] || ''} min</span>
|
164 |
+
</div>
|
165 |
+
<p class="movie-overview">${details.overview}</p>
|
166 |
+
${seasonEpisodeHTML}
|
167 |
+
<h3>Cast</h3>
|
168 |
+
<div class="cast-list">
|
169 |
+
${credits.cast.slice(0, 6).map(actor => actor.profile_path ? `
|
170 |
+
<div class="cast-item">
|
171 |
+
<img class="cast-photo"
|
172 |
+
src="${TMDB_IMAGE_BASE + actor.profile_path}"
|
173 |
+
alt="${actor.name}">
|
174 |
+
<div class="cast-info">
|
175 |
+
<div class="cast-name">${actor.name}</div>
|
176 |
+
<div class="cast-character">${actor.character}</div>
|
177 |
+
</div>
|
178 |
+
</div>
|
179 |
+
` : '').join('')}
|
180 |
+
</div>
|
181 |
+
`;
|
182 |
+
|
183 |
+
const modalContent = document.querySelector('.modal-content');
|
184 |
+
modalContent.innerHTML = '';
|
185 |
+
const iframeContainer = document.createElement('div');
|
186 |
+
iframeContainer.className = 'iframe-container';
|
187 |
+
iframeContainer.innerHTML = `
|
188 |
+
<span class="close-modal" onclick="closeModal()">×</span>
|
189 |
+
<div class="loader" id="loader"></div>
|
190 |
+
<iframe id="player-iframe" allowfullscreen></iframe>
|
191 |
+
`;
|
192 |
+
modalContent.appendChild(iframeContainer);
|
193 |
+
modalContent.appendChild(movieDetails);
|
194 |
+
|
195 |
+
const newIframe = document.getElementById('player-iframe');
|
196 |
+
if (type === 'movie') {
|
197 |
+
newIframe.src = `https://picfy.xyz/embed/movie/${id}`;
|
198 |
+
} else {
|
199 |
+
newIframe.src = `https://picfy.xyz/embed/tv/${id}/1/1`;
|
200 |
+
}
|
201 |
+
|
202 |
+
newIframe.onload = () => {
|
203 |
+
document.getElementById('loader').style.display = 'none';
|
204 |
+
newIframe.style.opacity = '1';
|
205 |
+
};
|
206 |
+
}
|
207 |
+
async function updateEpisodes(showId) {
|
208 |
+
const seasonSelect = document.getElementById('season-select');
|
209 |
+
const episodeSelect = document.getElementById('episode-select');
|
210 |
+
const seasonNum = seasonSelect.value;
|
211 |
+
|
212 |
+
const seasonDetails = await fetchTMDBData(`/tv/${showId}/season/${seasonNum}`);
|
213 |
+
|
214 |
+
episodeSelect.innerHTML = seasonDetails.episodes.map(episode =>
|
215 |
+
`<option value="${episode.episode_number}">Episode ${episode.episode_number}</option>`
|
216 |
+
).join('');
|
217 |
+
|
218 |
+
updatePlayer(showId);
|
219 |
+
}
|
220 |
+
function updatePlayer(showId) {
|
221 |
+
const seasonSelect = document.getElementById('season-select');
|
222 |
+
const episodeSelect = document.getElementById('episode-select');
|
223 |
+
const iframe = document.getElementById('player-iframe');
|
224 |
+
const loader = document.getElementById('loader');
|
225 |
+
|
226 |
+
loader.style.display = 'block';
|
227 |
+
iframe.style.opacity = '0';
|
228 |
+
|
229 |
+
iframe.src = `https://picfy.xyz/embed/tv/${showId}/${seasonSelect.value}/${episodeSelect.value}`;
|
230 |
+
|
231 |
+
iframe.onload = () => {
|
232 |
+
loader.style.display = 'none';
|
233 |
+
iframe.style.opacity = '1';
|
234 |
+
};
|
235 |
+
}
|
236 |
+
function closeModal() {
|
237 |
+
const modal = document.getElementById('modal');
|
238 |
+
const iframe = document.getElementById('player-iframe');
|
239 |
+
iframe.src = '';
|
240 |
+
modal.style.display = 'none';
|
241 |
+
}
|
242 |
+
document.getElementById('modal').addEventListener('click', e => {
|
243 |
+
if (e.target.id === 'modal') {
|
244 |
+
closeModal();
|
245 |
+
}
|
246 |
+
});
|
247 |
+
document.querySelectorAll('.carousel-arrow').forEach(arrow => {
|
248 |
+
arrow.addEventListener('click', () => {
|
249 |
+
const carousel = arrow.parentElement.querySelector('.carousel');
|
250 |
+
const scrollAmount = 400;
|
251 |
+
if (arrow.classList.contains('prev')) {
|
252 |
+
carousel.scrollLeft -= scrollAmount;
|
253 |
+
} else {
|
254 |
+
carousel.scrollLeft += scrollAmount;
|
255 |
+
}
|
256 |
+
});
|
257 |
+
});
|
258 |
+
initializeApp();
|