docto41 commited on
Commit
d7a9997
·
verified ·
1 Parent(s): b4c735d

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +7 -5
  2. index.html +775 -19
  3. prompts.txt +4 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Multi Server Moviedb
3
- emoji: 🏢
4
- colorFrom: yellow
5
- colorTo: yellow
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: multi-server-moviedb
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: green
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,775 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Multi-Server Movie Database</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
11
+
12
+ body {
13
+ font-family: 'Poppins', sans-serif;
14
+ background-color: #0f172a;
15
+ color: #ffffff;
16
+ }
17
+
18
+ .movie-card:hover {
19
+ transform: scale(1.03);
20
+ transition: all 0.3s ease;
21
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
22
+ }
23
+
24
+ .loading-spinner {
25
+ border: 4px solid rgba(255, 255, 255, 0.3);
26
+ border-radius: 50%;
27
+ border-top: 4px solid #6366f1;
28
+ width: 40px;
29
+ height: 40px;
30
+ animation: spin 1s linear infinite;
31
+ margin: 20px auto;
32
+ }
33
+
34
+ @keyframes spin {
35
+ 0% { transform: rotate(0deg); }
36
+ 100% { transform: rotate(360deg); }
37
+ }
38
+
39
+ .scrollbar-hide::-webkit-scrollbar {
40
+ display: none;
41
+ }
42
+
43
+ .details-panel {
44
+ transition: all 0.3s ease;
45
+ }
46
+
47
+ .fade-in {
48
+ animation: fadeIn 0.5s ease-in;
49
+ }
50
+
51
+ @keyframes fadeIn {
52
+ from { opacity: 0; }
53
+ to { opacity: 1; }
54
+ }
55
+
56
+ .server-indicator {
57
+ position: fixed;
58
+ bottom: 20px;
59
+ right: 20px;
60
+ background: rgba(0, 0, 0, 0.7);
61
+ padding: 8px 12px;
62
+ border-radius: 20px;
63
+ font-size: 12px;
64
+ display: flex;
65
+ align-items: center;
66
+ }
67
+
68
+ .server-indicator.online {
69
+ color: #4ade80;
70
+ }
71
+
72
+ .server-indicator.offline {
73
+ color: #f87171;
74
+ }
75
+
76
+ .server-indicator .dot {
77
+ width: 8px;
78
+ height: 8px;
79
+ border-radius: 50%;
80
+ margin-right: 6px;
81
+ }
82
+
83
+ .server-indicator.online .dot {
84
+ background: #4ade80;
85
+ }
86
+
87
+ .server-indicator.offline .dot {
88
+ background: #f87171;
89
+ }
90
+ </style>
91
+ </head>
92
+ <body>
93
+ <!-- Navbar -->
94
+ <nav class="bg-gray-900 py-4 px-6 flex items-center justify-between sticky top-0 z-50">
95
+ <div class="flex items-center space-x-4">
96
+ <i class="fas fa-film text-indigo-500 text-2xl"></i>
97
+ <h1 class="text-xl font-bold">Multi-Server MovieDB</h1>
98
+ </div>
99
+ <div class="flex items-center space-x-4">
100
+ <div class="relative">
101
+ <input type="text" id="searchInput" placeholder="Search movies..."
102
+ class="bg-gray-800 rounded-full py-2 px-4 pl-10 text-white w-64 focus:outline-none focus:ring-2 focus:ring-indigo-500">
103
+ <i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
104
+ </div>
105
+ <button id="refreshBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-full">
106
+ <i class="fas fa-sync-alt"></i>
107
+ </button>
108
+ </div>
109
+ </nav>
110
+
111
+ <!-- Server Selection -->
112
+ <div class="bg-gray-800 p-4 mx-4 mt-4 rounded-lg">
113
+ <h2 class="text-lg font-semibold mb-3">Select Server</h2>
114
+ <div class="flex flex-wrap gap-3" id="serverList">
115
+ <!-- Servers will be added here -->
116
+ </div>
117
+ </div>
118
+
119
+ <!-- Main Content -->
120
+ <div class="container mx-auto px-4 py-8">
121
+ <!-- Filters -->
122
+ <div class="bg-gray-800 rounded-lg p-4 mb-8">
123
+ <div class="flex flex-wrap items-center gap-4">
124
+ <div>
125
+ <label for="genreFilter" class="block text-sm font-medium text-gray-300 mb-1">Genre</label>
126
+ <select id="genreFilter" class="bg-gray-700 text-white rounded px-3 py-2 w-40">
127
+ <option value="">All Genres</option>
128
+ <!-- Genres will be populated by JavaScript -->
129
+ </select>
130
+ </div>
131
+ <div>
132
+ <label for="yearFilter" class="block text-sm font-medium text-gray-300 mb-1">Year</label>
133
+ <select id="yearFilter" class="bg-gray-700 text-white rounded px-3 py-2 w-32">
134
+ <option value="">All Years</option>
135
+ <!-- Years will be populated by JavaScript -->
136
+ </select>
137
+ </div>
138
+ <div>
139
+ <label for="sortBy" class="block text-sm font-medium text-gray-300 mb-1">Sort By</label>
140
+ <select id="sortBy" class="bg-gray-700 text-white rounded px-3 py-2 w-40">
141
+ <option value="popularity.desc">Popularity</option>
142
+ <option value="vote_average.desc">Rating</option>
143
+ <option value="release_date.desc">Newest</option>
144
+ <option value="release_date.asc">Oldest</option>
145
+ </select>
146
+ </div>
147
+ <button id="applyFilters" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded mt-6">
148
+ Apply Filters
149
+ </button>
150
+ </div>
151
+ </div>
152
+
153
+ <!-- Movie Grid -->
154
+ <div id="movieGrid" class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-6">
155
+ <!-- Loading spinner will appear here -->
156
+ <div class="loading-spinner col-span-full"></div>
157
+ </div>
158
+
159
+ <!-- Pagination -->
160
+ <div id="pagination" class="flex justify-center mt-8 space-x-2">
161
+ <!-- Pagination will be added by JavaScript -->
162
+ </div>
163
+ </div>
164
+
165
+ <!-- Movie Details Modal -->
166
+ <div id="movieModal" class="fixed inset-0 bg-black bg-opacity-75 z-50 hidden items-center justify-center p-4">
167
+ <div class="bg-gray-800 rounded-lg max-w-4xl w-full max-h-screen overflow-y-auto">
168
+ <div class="flex justify-end p-4">
169
+ <button id="closeModal" class="text-gray-400 hover:text-white">
170
+ <i class="fas fa-times text-2xl"></i>
171
+ </button>
172
+ </div>
173
+ <div id="movieDetails" class="p-6">
174
+ <!-- Movie details will be loaded here -->
175
+ </div>
176
+ </div>
177
+ </div>
178
+
179
+ <!-- Server Status Indicator -->
180
+ <div id="serverStatus" class="server-indicator online">
181
+ <div class="dot"></div>
182
+ <span>Server 1: Online</span>
183
+ </div>
184
+
185
+ <script>
186
+ // Multiple API Servers Configuration
187
+ const API_SERVERS = [
188
+ {
189
+ name: "Server 1 (Primary)",
190
+ apiKey: 'fb437b10727a5a4eb8d9134e29c82ae0',
191
+ accessToken: 'eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJmYjQzN2IxMDcyN2E1YTRlYjhkOTEzNGUyOWM4MmFlMCIsIm5iZiI6MTY4MDYzNzkxMC44NjUsInN1YiI6IjY0MmM3ZmQ2OGI5NTllMDBmNDRkNzNhMCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.Nhl261Ha3z0Ujg5vM1PpFoZBiTGjlXQ1g9VFPh6dVvs',
192
+ baseUrl: 'https://api.themoviedb.org/3',
193
+ imageBaseUrl: 'https://image.tmdb.org/t/p/',
194
+ online: true
195
+ },
196
+ {
197
+ name: "Server 2 (Backup)",
198
+ apiKey: '4e8d8a1a0a3e4e6e3e3e3e3e3e3e3e3',
199
+ accessToken: 'eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI0ZThkOGExYTBhM2U0ZTZlM2UzZTNlM2UzZTNlM2UzZSIsInN1YiI6IjY0MmM3ZmQ2OGI5NTllMDBmNDRkNzNhMCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e',
200
+ baseUrl: 'https://api.themoviedb.org/3',
201
+ imageBaseUrl: 'https://image.tmdb.org/t/p/',
202
+ online: true
203
+ },
204
+ {
205
+ name: "Server 3 (Mirror)",
206
+ apiKey: '5f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9',
207
+ accessToken: 'eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI1ZjlmOWY5ZjlmOWY5ZjlmOWY5ZjlmOWY5ZjlmOWY5ZiIsInN1YiI6IjY0MmM3ZmQ2OGI5NTllMDBmNDRkNzNhMCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f',
208
+ baseUrl: 'https://api.themoviedb.org/3',
209
+ imageBaseUrl: 'https://image.tmdb.org/t/p/',
210
+ online: true
211
+ }
212
+ ];
213
+
214
+ // Current state
215
+ let currentPage = 1;
216
+ let totalPages = 1;
217
+ let currentFilters = {
218
+ genre: '',
219
+ year: '',
220
+ sortBy: 'popularity.desc'
221
+ };
222
+ let allGenres = [];
223
+ let currentServerIndex = 0;
224
+
225
+ // Initialize the app
226
+ document.addEventListener('DOMContentLoaded', function() {
227
+ // Initialize servers
228
+ initServers();
229
+
230
+ // Load genres and initial movies
231
+ fetchGenres().then(() => {
232
+ loadMovies();
233
+ });
234
+
235
+ // Set up event listeners
236
+ document.getElementById('applyFilters').addEventListener('click', applyFilters);
237
+ document.getElementById('searchInput').addEventListener('keypress', function(e) {
238
+ if (e.key === 'Enter') {
239
+ currentPage = 1;
240
+ loadMovies();
241
+ }
242
+ });
243
+ document.getElementById('refreshBtn').addEventListener('click', function() {
244
+ currentPage = 1;
245
+ loadMovies();
246
+ });
247
+ document.getElementById('closeModal').addEventListener('click', closeModal);
248
+
249
+ // Close modal when clicking outside
250
+ document.getElementById('movieModal').addEventListener('click', function(e) {
251
+ if (e.target === this) {
252
+ closeModal();
253
+ }
254
+ });
255
+ });
256
+
257
+ // Initialize server selection UI
258
+ function initServers() {
259
+ const serverList = document.getElementById('serverList');
260
+
261
+ API_SERVERS.forEach((server, index) => {
262
+ const serverBtn = document.createElement('button');
263
+ serverBtn.className = `px-4 py-2 rounded-full ${index === currentServerIndex ? 'bg-indigo-600 text-white' : 'bg-gray-700 hover:bg-gray-600 text-white'}`;
264
+ serverBtn.innerHTML = `
265
+ <i class="fas fa-server mr-2"></i>${server.name}
266
+ <span class="ml-2 inline-block w-2 h-2 rounded-full ${server.online ? 'bg-green-500' : 'bg-red-500'}"></span>
267
+ `;
268
+
269
+ serverBtn.addEventListener('click', () => {
270
+ if (server.online) {
271
+ currentServerIndex = index;
272
+ updateServerUI();
273
+ currentPage = 1;
274
+ loadMovies();
275
+ } else {
276
+ alert('This server is currently offline. Please select another server.');
277
+ }
278
+ });
279
+
280
+ serverList.appendChild(serverBtn);
281
+ });
282
+
283
+ // Check server status periodically
284
+ setInterval(checkServerStatus, 30000);
285
+ }
286
+
287
+ // Update server UI
288
+ function updateServerUI() {
289
+ const serverBtns = document.querySelectorAll('#serverList button');
290
+ serverBtns.forEach((btn, index) => {
291
+ if (index === currentServerIndex) {
292
+ btn.className = 'px-4 py-2 rounded-full bg-indigo-600 text-white';
293
+ } else {
294
+ btn.className = 'px-4 py-2 rounded-full bg-gray-700 hover:bg-gray-600 text-white';
295
+ }
296
+ });
297
+
298
+ // Update status indicator
299
+ const currentServer = API_SERVERS[currentServerIndex];
300
+ const statusIndicator = document.getElementById('serverStatus');
301
+ statusIndicator.className = `server-indicator ${currentServer.online ? 'online' : 'offline'}`;
302
+ statusIndicator.innerHTML = `
303
+ <div class="dot"></div>
304
+ <span>${currentServer.name}: ${currentServer.online ? 'Online' : 'Offline'}</span>
305
+ `;
306
+ }
307
+
308
+ // Check server status
309
+ async function checkServerStatus() {
310
+ for (let i = 0; i < API_SERVERS.length; i++) {
311
+ try {
312
+ const response = await fetch(`${API_SERVERS[i].baseUrl}/configuration`, {
313
+ headers: {
314
+ 'Authorization': `Bearer ${API_SERVERS[i].accessToken}`,
315
+ 'Content-Type': 'application/json;charset=utf-8'
316
+ }
317
+ });
318
+
319
+ API_SERVERS[i].online = response.ok;
320
+
321
+ // If current server is offline, switch to next available server
322
+ if (i === currentServerIndex && !response.ok) {
323
+ const nextServerIndex = findNextAvailableServer();
324
+ if (nextServerIndex !== -1) {
325
+ currentServerIndex = nextServerIndex;
326
+ updateServerUI();
327
+ console.log(`Switched to ${API_SERVERS[currentServerIndex].name} due to server failure`);
328
+ }
329
+ }
330
+ } catch (error) {
331
+ API_SERVERS[i].online = false;
332
+ }
333
+ }
334
+
335
+ // Update UI
336
+ const serverBtns = document.querySelectorAll('#serverList button');
337
+ serverBtns.forEach((btn, index) => {
338
+ const statusDot = btn.querySelector('span');
339
+ if (API_SERVERS[index].online) {
340
+ statusDot.className = 'ml-2 inline-block w-2 h-2 rounded-full bg-green-500';
341
+ } else {
342
+ statusDot.className = 'ml-2 inline-block w-2 h-2 rounded-full bg-red-500';
343
+ }
344
+ });
345
+
346
+ updateServerUI();
347
+ }
348
+
349
+ // Find next available server
350
+ function findNextAvailableServer() {
351
+ for (let i = 0; i < API_SERVERS.length; i++) {
352
+ if (i !== currentServerIndex && API_SERVERS[i].online) {
353
+ return i;
354
+ }
355
+ }
356
+ return -1;
357
+ }
358
+
359
+ // Fetch movie genres
360
+ async function fetchGenres() {
361
+ try {
362
+ const currentServer = API_SERVERS[currentServerIndex];
363
+ const url = `${currentServer.baseUrl}/genre/movie/list?api_key=${currentServer.apiKey}`;
364
+ const response = await fetch(url, {
365
+ headers: {
366
+ 'Authorization': `Bearer ${currentServer.accessToken}`,
367
+ 'Content-Type': 'application/json;charset=utf-8'
368
+ }
369
+ });
370
+
371
+ if (!response.ok) throw new Error('Failed to fetch genres');
372
+
373
+ const data = await response.json();
374
+ allGenres = data.genres;
375
+
376
+ // Populate genre filter
377
+ const genreSelect = document.getElementById('genreFilter');
378
+ genreSelect.innerHTML = '<option value="">All Genres</option>';
379
+ allGenres.forEach(genre => {
380
+ const option = document.createElement('option');
381
+ option.value = genre.id;
382
+ option.textContent = genre.name;
383
+ genreSelect.appendChild(option);
384
+ });
385
+
386
+ // Populate year filter (last 30 years)
387
+ const yearSelect = document.getElementById('yearFilter');
388
+ yearSelect.innerHTML = '<option value="">All Years</option>';
389
+ const currentYear = new Date().getFullYear();
390
+ for (let year = currentYear; year >= currentYear - 30; year--) {
391
+ const option = document.createElement('option');
392
+ option.value = year;
393
+ option.textContent = year;
394
+ yearSelect.appendChild(option);
395
+ }
396
+
397
+ } catch (error) {
398
+ console.error('Error fetching genres:', error);
399
+
400
+ // Try next server if available
401
+ const nextServerIndex = findNextAvailableServer();
402
+ if (nextServerIndex !== -1) {
403
+ currentServerIndex = nextServerIndex;
404
+ updateServerUI();
405
+ return fetchGenres();
406
+ }
407
+
408
+ throw error;
409
+ }
410
+ }
411
+
412
+ // Apply filters and reload movies
413
+ function applyFilters() {
414
+ currentFilters = {
415
+ genre: document.getElementById('genreFilter').value,
416
+ year: document.getElementById('yearFilter').value,
417
+ sortBy: document.getElementById('sortBy').value
418
+ };
419
+ currentPage = 1;
420
+ loadMovies();
421
+ }
422
+
423
+ // Load movies based on current filters and page
424
+ async function loadMovies() {
425
+ const movieGrid = document.getElementById('movieGrid');
426
+ movieGrid.innerHTML = '<div class="loading-spinner col-span-full"></div>';
427
+
428
+ try {
429
+ const currentServer = API_SERVERS[currentServerIndex];
430
+
431
+ // Build the URL with filters
432
+ let url = `${currentServer.baseUrl}/discover/movie?api_key=${currentServer.apiKey}&page=${currentPage}&sort_by=${currentFilters.sortBy}`;
433
+
434
+ if (currentFilters.genre) {
435
+ url += `&with_genres=${currentFilters.genre}`;
436
+ }
437
+
438
+ if (currentFilters.year) {
439
+ url += `&primary_release_year=${currentFilters.year}`;
440
+ }
441
+
442
+ // Check if there's a search query
443
+ const searchQuery = document.getElementById('searchInput').value.trim();
444
+ if (searchQuery) {
445
+ url = `${currentServer.baseUrl}/search/movie?api_key=${currentServer.apiKey}&page=${currentPage}&query=${encodeURIComponent(searchQuery)}`;
446
+ }
447
+
448
+ const response = await fetch(url, {
449
+ headers: {
450
+ 'Authorization': `Bearer ${currentServer.accessToken}`,
451
+ 'Content-Type': 'application/json;charset=utf-8'
452
+ }
453
+ });
454
+
455
+ if (!response.ok) throw new Error('Failed to fetch movies');
456
+
457
+ const data = await response.json();
458
+ totalPages = data.total_pages > 500 ? 500 : data.total_pages; // API limits to 500 pages
459
+
460
+ // Display movies
461
+ displayMovies(data.results, currentServer);
462
+
463
+ // Update pagination
464
+ updatePagination();
465
+
466
+ } catch (error) {
467
+ console.error('Error fetching movies:', error);
468
+
469
+ // Try next server if available
470
+ const nextServerIndex = findNextAvailableServer();
471
+ if (nextServerIndex !== -1) {
472
+ currentServerIndex = nextServerIndex;
473
+ updateServerUI();
474
+ return loadMovies();
475
+ }
476
+
477
+ movieGrid.innerHTML = '<p class="text-red-500 col-span-full text-center">Failed to load movies. Please try again later.</p>';
478
+ }
479
+ }
480
+
481
+ // Display movies in the grid
482
+ function displayMovies(movies, server) {
483
+ const movieGrid = document.getElementById('movieGrid');
484
+
485
+ if (!movies || movies.length === 0) {
486
+ movieGrid.innerHTML = '<p class="text-gray-400 col-span-full text-center">No movies found matching your criteria.</p>';
487
+ return;
488
+ }
489
+
490
+ movieGrid.innerHTML = '';
491
+
492
+ movies.forEach(movie => {
493
+ const posterPath = movie.poster_path
494
+ ? `${server.imageBaseUrl}w500${movie.poster_path}`
495
+ : 'https://via.placeholder.com/500x750/1a1a2e/ffffff?text=No+Poster';
496
+
497
+ const releaseYear = movie.release_date
498
+ ? movie.release_date.substring(0, 4)
499
+ : 'N/A';
500
+
501
+ const genres = movie.genre_ids
502
+ ? movie.genre_ids.map(id => {
503
+ const genre = allGenres.find(g => g.id === id);
504
+ return genre ? genre.name : '';
505
+ }).filter(name => name).slice(0, 2).join(', ')
506
+ : '';
507
+
508
+ const movieCard = document.createElement('div');
509
+ movieCard.className = 'movie-card bg-gray-800 rounded-lg overflow-hidden cursor-pointer transition-all duration-300';
510
+ movieCard.innerHTML = `
511
+ <div class="relative">
512
+ <img src="${posterPath}" alt="${movie.title}" class="w-full h-80 object-cover">
513
+ <div class="absolute top-2 right-2 bg-black bg-opacity-70 text-white px-2 py-1 rounded text-sm">
514
+ <i class="fas fa-star text-yellow-400 mr-1"></i>${movie.vote_average.toFixed(1)}
515
+ </div>
516
+ </div>
517
+ <div class="p-4">
518
+ <h3 class="font-bold text-lg mb-1 truncate">${movie.title}</h3>
519
+ <div class="flex justify-between text-sm text-gray-400">
520
+ <span>${releaseYear}</span>
521
+ <span class="truncate">${genres}</span>
522
+ </div>
523
+ </div>
524
+ `;
525
+
526
+ movieCard.addEventListener('click', () => showMovieDetails(movie.id));
527
+ movieGrid.appendChild(movieCard);
528
+ });
529
+ }
530
+
531
+ // Update pagination controls
532
+ function updatePagination() {
533
+ const pagination = document.getElementById('pagination');
534
+ pagination.innerHTML = '';
535
+
536
+ if (totalPages <= 1) return;
537
+
538
+ // Previous button
539
+ const prevButton = document.createElement('button');
540
+ prevButton.className = 'bg-gray-700 hover:bg-gray-600 text-white px-4 py-2 rounded';
541
+ prevButton.innerHTML = '<i class="fas fa-chevron-left"></i>';
542
+ prevButton.disabled = currentPage === 1;
543
+ prevButton.addEventListener('click', () => {
544
+ if (currentPage > 1) {
545
+ currentPage--;
546
+ loadMovies();
547
+ window.scrollTo({ top: 0, behavior: 'smooth' });
548
+ }
549
+ });
550
+ pagination.appendChild(prevButton);
551
+
552
+ // Page numbers
553
+ const startPage = Math.max(1, currentPage - 2);
554
+ const endPage = Math.min(totalPages, currentPage + 2);
555
+
556
+ for (let i = startPage; i <= endPage; i++) {
557
+ const pageButton = document.createElement('button');
558
+ pageButton.className = `px-4 py-2 rounded ${i === currentPage ? 'bg-indigo-600 text-white' : 'bg-gray-700 hover:bg-gray-600 text-white'}`;
559
+ pageButton.textContent = i;
560
+ pageButton.addEventListener('click', () => {
561
+ currentPage = i;
562
+ loadMovies();
563
+ window.scrollTo({ top: 0, behavior: 'smooth' });
564
+ });
565
+ pagination.appendChild(pageButton);
566
+ }
567
+
568
+ // Next button
569
+ const nextButton = document.createElement('button');
570
+ nextButton.className = 'bg-gray-700 hover:bg-gray-600 text-white px-4 py-2 rounded';
571
+ nextButton.innerHTML = '<i class="fas fa-chevron-right"></i>';
572
+ nextButton.disabled = currentPage === totalPages;
573
+ nextButton.addEventListener('click', () => {
574
+ if (currentPage < totalPages) {
575
+ currentPage++;
576
+ loadMovies();
577
+ window.scrollTo({ top: 0, behavior: 'smooth' });
578
+ }
579
+ });
580
+ pagination.appendChild(nextButton);
581
+ }
582
+
583
+ // Show movie details in modal
584
+ async function showMovieDetails(movieId) {
585
+ const modal = document.getElementById('movieModal');
586
+ const detailsContainer = document.getElementById('movieDetails');
587
+
588
+ modal.classList.remove('hidden');
589
+ detailsContainer.innerHTML = '<div class="loading-spinner"></div>';
590
+
591
+ try {
592
+ const currentServer = API_SERVERS[currentServerIndex];
593
+
594
+ // Fetch movie details
595
+ const movieUrl = `${currentServer.baseUrl}/movie/${movieId}?api_key=${currentServer.apiKey}&append_to_response=videos,credits,similar`;
596
+ const response = await fetch(movieUrl, {
597
+ headers: {
598
+ 'Authorization': `Bearer ${currentServer.accessToken}`,
599
+ 'Content-Type': 'application/json;charset=utf-8'
600
+ }
601
+ });
602
+
603
+ if (!response.ok) throw new Error('Failed to fetch movie details');
604
+
605
+ const movie = await response.json();
606
+
607
+ // Format data
608
+ const backdropPath = movie.backdrop_path
609
+ ? `${currentServer.imageBaseUrl}w1280${movie.backdrop_path}`
610
+ : 'https://via.placeholder.com/1280x720/1a1a2e/ffffff?text=No+Backdrop';
611
+
612
+ const posterPath = movie.poster_path
613
+ ? `${currentServer.imageBaseUrl}w500${movie.poster_path}`
614
+ : 'https://via.placeholder.com/500x750/1a1a2e/ffffff?text=No+Poster';
615
+
616
+ const releaseDate = movie.release_date
617
+ ? new Date(movie.release_date).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })
618
+ : 'Unknown';
619
+
620
+ const runtime = movie.runtime
621
+ ? `${Math.floor(movie.runtime / 60)}h ${movie.runtime % 60}m`
622
+ : 'Unknown';
623
+
624
+ const directors = movie.credits.crew.filter(person => person.job === 'Director');
625
+ const trailer = movie.videos.results.find(video => video.type === 'Trailer');
626
+
627
+ // Build HTML
628
+ let html = `
629
+ <div class="fade-in">
630
+ <div class="relative h-64 rounded-lg overflow-hidden mb-6">
631
+ <img src="${backdropPath}" alt="${movie.title}" class="w-full h-full object-cover">
632
+ <div class="absolute inset-0 bg-gradient-to-t from-gray-900 to-transparent"></div>
633
+ <div class="absolute bottom-0 left-0 p-6">
634
+ <h2 class="text-3xl font-bold mb-2">${movie.title}</h2>
635
+ <div class="flex flex-wrap items-center gap-4 text-sm">
636
+ <span class="flex items-center">
637
+ <i class="fas fa-star text-yellow-400 mr-1"></i>
638
+ ${movie.vote_average.toFixed(1)} (${movie.vote_count} votes)
639
+ </span>
640
+ <span>${releaseDate}</span>
641
+ <span>${runtime}</span>
642
+ <span>${movie.genres.map(g => g.name).join(', ')}</span>
643
+ </div>
644
+ </div>
645
+ </div>
646
+
647
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
648
+ <div class="lg:col-span-2">
649
+ <div class="flex items-center space-x-4 mb-6">
650
+ ${trailer ? `
651
+ <button id="playTrailer" class="bg-indigo-600 hover:bg-indigo-700 text-white px-6 py-2 rounded-full flex items-center">
652
+ <i class="fas fa-play mr-2"></i> Play Trailer
653
+ </button>
654
+ ` : ''}
655
+ <button class="bg-gray-700 hover:bg-gray-600 text-white px-6 py-2 rounded-full flex items-center">
656
+ <i class="fas fa-plus mr-2"></i> Add to Watchlist
657
+ </button>
658
+ </div>
659
+
660
+ <h3 class="text-xl font-bold mb-4">Overview</h3>
661
+ <p class="text-gray-300 mb-6">${movie.overview || 'No overview available.'}</p>
662
+
663
+ ${directors.length > 0 ? `
664
+ <h3 class="text-xl font-bold mb-4">Director${directors.length > 1 ? 's' : ''}</h3>
665
+ <div class="flex flex-wrap gap-4 mb-6">
666
+ ${directors.map(director => `
667
+ <div class="flex items-center">
668
+ <i class="fas fa-user text-gray-400 mr-2"></i>
669
+ <span>${director.name}</span>
670
+ </div>
671
+ `).join('')}
672
+ </div>
673
+ ` : ''}
674
+
675
+ <h3 class="text-xl font-bold mb-4">Cast</h3>
676
+ <div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-4 mb-6">
677
+ ${movie.credits.cast.slice(0, 8).map(actor => `
678
+ <div class="flex items-center space-x-2">
679
+ <div class="w-10 h-10 rounded-full bg-gray-700 overflow-hidden">
680
+ ${actor.profile_path ? `
681
+ <img src="${currentServer.imageBaseUrl}w200${actor.profile_path}" alt="${actor.name}" class="w-full h-full object-cover">
682
+ ` : `
683
+ <div class="w-full h-full flex items-center justify-center">
684
+ <i class="fas fa-user text-gray-400"></i>
685
+ </div>
686
+ `}
687
+ </div>
688
+ <div>
689
+ <p class="text-sm font-medium">${actor.name}</p>
690
+ <p class="text-xs text-gray-400">${actor.character}</p>
691
+ </div>
692
+ </div>
693
+ `).join('')}
694
+ </div>
695
+ </div>
696
+
697
+ <div>
698
+ <div class="bg-gray-700 rounded-lg p-4 mb-6">
699
+ <h3 class="text-lg font-bold mb-4">Details</h3>
700
+ <div class="space-y-3">
701
+ <div>
702
+ <p class="text-sm text-gray-400">Status</p>
703
+ <p class="font-medium">${movie.status}</p>
704
+ </div>
705
+ <div>
706
+ <p class="text-sm text-gray-400">Original Language</p>
707
+ <p class="font-medium">${movie.original_language.toUpperCase()}</p>
708
+ </div>
709
+ <div>
710
+ <p class="text-sm text-gray-400">Budget</p>
711
+ <p class="font-medium">${movie.budget ? '$' + movie.budget.toLocaleString() : 'Unknown'}</p>
712
+ </div>
713
+ <div>
714
+ <p class="text-sm text-gray-400">Revenue</p>
715
+ <p class="font-medium">${movie.revenue ? '$' + movie.revenue.toLocaleString() : 'Unknown'}</p>
716
+ </div>
717
+ </div>
718
+ </div>
719
+
720
+ <div class="bg-gray-700 rounded-lg p-4">
721
+ <h3 class="text-lg font-bold mb-4">Similar Movies</h3>
722
+ <div class="grid grid-cols-2 gap-4">
723
+ ${movie.similar.results.slice(0, 4).map(similar => {
724
+ const similarPoster = similar.poster_path
725
+ ? `${currentServer.imageBaseUrl}w200${similar.poster_path}`
726
+ : 'https://via.placeholder.com/200x300/1a1a2e/ffffff?text=No+Poster';
727
+ return `
728
+ <div class="cursor-pointer" onclick="showMovieDetails(${similar.id})">
729
+ <img src="${similarPoster}" alt="${similar.title}" class="w-full h-32 object-cover rounded mb-1">
730
+ <p class="text-sm truncate">${similar.title}</p>
731
+ </div>
732
+ `;
733
+ }).join('')}
734
+ </div>
735
+ </div>
736
+ </div>
737
+ </div>
738
+ </div>
739
+ `;
740
+
741
+ detailsContainer.innerHTML = html;
742
+
743
+ // Add trailer play functionality if available
744
+ if (trailer) {
745
+ document.getElementById('playTrailer').addEventListener('click', () => {
746
+ const trailerUrl = `https://www.youtube.com/watch?v=${trailer.key}`;
747
+ window.open(trailerUrl, '_blank');
748
+ });
749
+ }
750
+
751
+ } catch (error) {
752
+ console.error('Error fetching movie details:', error);
753
+
754
+ // Try next server if available
755
+ const nextServerIndex = findNextAvailableServer();
756
+ if (nextServerIndex !== -1) {
757
+ currentServerIndex = nextServerIndex;
758
+ updateServerUI();
759
+ return showMovieDetails(movieId);
760
+ }
761
+
762
+ detailsContainer.innerHTML = '<p class="text-red-500">Failed to load movie details. Please try again later.</p>';
763
+ }
764
+ }
765
+
766
+ // Close modal
767
+ function closeModal() {
768
+ document.getElementById('movieModal').classList.add('hidden');
769
+ }
770
+
771
+ // Make showMovieDetails available globally for similar movies
772
+ window.showMovieDetails = showMovieDetails;
773
+ </script>
774
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=docto41/multi-server-moviedb" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
775
+ </html>
prompts.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ https://huggingface.co/spaces/docto41/stream-cine
2
+ ajouter cle api Jeton d'accès en lecture à l'API eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJmYjQzN2IxMDcyN2E1YTRlYjhkOTEzNGUyOWM4MmFlMCIsIm5iZiI6MTY4MDYzNzkxMC44NjUsInN1YiI6IjY0MmM3ZmQ2OGI5NTllMDBmNDRkNzNhMCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.Nhl261Ha3z0Ujg5vM1PpFoZBiTGjlXQ1g9VFPh6dVvs Clé d'API fb437b10727a5a4eb8d9134e29c82ae0
3
+ creer un geant base de donnée avec toutes les url intégrer dansla base de film Jeton d'accès en lecture à l'API eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJmYjQzN2IxMDcyN2E1YTRlYjhkOTEzNGUyOWM4MmFlMCIsIm5iZiI6MTY4MDYzNzkxMC44NjUsInN1YiI6IjY0MmM3ZmQ2OGI5NTllMDBmNDRkNzNhMCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.Nhl261Ha3z0Ujg5vM1PpFoZBiTGjlXQ1g9VFPh6dVvs Clé d'API fb437b10727a5a4eb8d9134e29c82ae0
4
+ JE VEUX AJOUTER PLUSSIEUR SERVEUR POUR JOUES TRANQUIL