cheaptoner2016 commited on
Commit
0d4b1b9
Β·
verified Β·
1 Parent(s): cbdb449

make this good

Browse files
Files changed (2) hide show
  1. README.md +9 -5
  2. index.html +651 -19
README.md CHANGED
@@ -1,10 +1,14 @@
1
  ---
2
- title: Cinemagic Voyager
3
- emoji: πŸ“š
4
- colorFrom: green
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: CineMagic Voyager 🎬✨
3
+ colorFrom: yellow
4
+ colorTo: blue
5
+ emoji: 🐳
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite-v3
10
  ---
11
 
12
+ # Welcome to your new DeepSite project!
13
+ This project was created with [DeepSite](https://deepsite.hf.co).
14
+
index.html CHANGED
@@ -1,19 +1,651 @@
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>MovieDB - Full Screen Media Banner</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
16
+ height: 100vh;
17
+ background-color: #0a0a0a;
18
+ background-repeat: no-repeat;
19
+ background-size: cover;
20
+ background-position: center;
21
+ position: relative;
22
+ overflow: hidden;
23
+ transition: background-image 1.2s cubic-bezier(0.4, 0, 0.2, 1);
24
+ }
25
+
26
+ body::before {
27
+ content: '';
28
+ position: absolute;
29
+ top: 0;
30
+ left: 0;
31
+ width: 100%;
32
+ height: 100%;
33
+ background: linear-gradient(
34
+ 135deg,
35
+ rgba(0, 0, 0, 0.85) 0%,
36
+ rgba(0, 0, 0, 0.7) 30%,
37
+ rgba(0, 0, 0, 0.4) 60%,
38
+ rgba(0, 0, 0, 0.2) 100%
39
+ );
40
+ z-index: 1;
41
+ }
42
+ #movie-container {
43
+ position: relative;
44
+ z-index: 2;
45
+ height: 100%;
46
+ opacity: 0;
47
+ transition: opacity 0.8s cubic-bezier(0.4, 0, 0.2, 1);
48
+ display: flex;
49
+ align-items: center;
50
+ padding: 0 8%;
51
+ }
52
+
53
+ .media-content {
54
+ max-width: 65%;
55
+ color: white;
56
+ text-shadow: 0 4px 12px rgba(0, 0, 0, 0.8);
57
+ }
58
+
59
+ .media-type-badge {
60
+ background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%);
61
+ color: #1a1a1a;
62
+ padding: 0.5rem 1.2rem;
63
+ border-radius: 25px;
64
+ font-size: 0.85rem;
65
+ font-weight: 700;
66
+ margin-bottom: 1rem;
67
+ display: inline-block;
68
+ box-shadow: 0 4px 15px rgba(255, 215, 0, 0.3);
69
+ letter-spacing: 0.5px;
70
+ border: 1px solid rgba(255, 255, 255, 0.2);
71
+ }
72
+
73
+ .media-title {
74
+ font-size: clamp(2.5rem, 5vw, 5rem);
75
+ font-weight: 800;
76
+ margin-bottom: 1.5rem;
77
+ line-height: 1.05;
78
+ background: linear-gradient(135deg, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0.6) 100%);
79
+ padding: 1rem 1.5rem;
80
+ border-radius: 16px;
81
+ display: inline-block;
82
+ backdrop-filter: blur(10px);
83
+ border: 1px solid rgba(255, 255, 255, 0.1);
84
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
85
+ }
86
+ .text-content {
87
+ opacity: 0;
88
+ transform: translateY(30px);
89
+ transition: all 0.8s cubic-bezier(0.4, 0, 0.2, 1);
90
+ }
91
+
92
+ .text-content.visible {
93
+ opacity: 1;
94
+ transform: translateY(0);
95
+ }
96
+
97
+ .media-subtitle {
98
+ font-size: clamp(1rem, 1.4vw, 1.4rem);
99
+ margin-bottom: 1.5rem;
100
+ background: linear-gradient(135deg, rgba(0, 0, 0, 0.7) 0%, rgba(0, 0, 0, 0.5) 100%);
101
+ padding: 0.8rem 1.2rem;
102
+ border-radius: 12px;
103
+ display: inline-block;
104
+ backdrop-filter: blur(8px);
105
+ border: 1px solid rgba(255, 255, 255, 0.1);
106
+ line-height: 1.4;
107
+ }
108
+
109
+ .media-rating {
110
+ margin-bottom: 2rem;
111
+ background: linear-gradient(135deg, rgba(0, 0, 0, 0.7) 0%, rgba(0, 0, 0, 0.5) 100%);
112
+ padding: 0.8rem 1.2rem;
113
+ border-radius: 12px;
114
+ display: inline-block;
115
+ backdrop-filter: blur(8px);
116
+ border: 1px solid rgba(255, 255, 255, 0.1);
117
+ }
118
+ .media-overview-container {
119
+ position: absolute;
120
+ bottom: 3rem;
121
+ right: 3rem;
122
+ max-width: 550px;
123
+ background: linear-gradient(135deg, rgba(0, 0, 0, 0.9) 0%, rgba(0, 0, 0, 0.7) 100%);
124
+ padding: 2rem;
125
+ border-radius: 20px;
126
+ backdrop-filter: blur(15px);
127
+ border: 1px solid rgba(255, 255, 255, 0.15);
128
+ opacity: 0;
129
+ transform: translateY(30px) scale(0.95);
130
+ transition: all 0.8s cubic-bezier(0.4, 0, 0.2, 1);
131
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
132
+ }
133
+
134
+ .media-overview-container.visible {
135
+ opacity: 1;
136
+ transform: translateY(0) scale(1);
137
+ }
138
+
139
+ .overview-title {
140
+ font-size: 1.4rem;
141
+ font-weight: 700;
142
+ margin-bottom: 1rem;
143
+ color: #ffd700;
144
+ text-shadow: 0 2px 8px rgba(255, 215, 0, 0.3);
145
+ letter-spacing: 0.5px;
146
+ }
147
+
148
+ .media-overview {
149
+ font-size: 1.1rem;
150
+ line-height: 1.6;
151
+ color: #f0f0f0;
152
+ max-height: 220px;
153
+ overflow-y: auto;
154
+ padding-right: 1rem;
155
+ font-weight: 400;
156
+ }
157
+
158
+ .media-overview::-webkit-scrollbar {
159
+ width: 6px;
160
+ }
161
+
162
+ .media-overview::-webkit-scrollbar-track {
163
+ background: rgba(255, 255, 255, 0.05);
164
+ border-radius: 3px;
165
+ }
166
+
167
+ .media-overview::-webkit-scrollbar-thumb {
168
+ background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%);
169
+ border-radius: 3px;
170
+ }
171
+
172
+ .media-overview::-webkit-scrollbar-thumb:hover {
173
+ background: linear-gradient(135deg, #ffed4e 0%, #ffd700 100%);
174
+ }
175
+ .loading-indicator {
176
+ position: absolute;
177
+ top: 50%;
178
+ left: 50%;
179
+ transform: translate(-50%, -50%);
180
+ color: white;
181
+ font-size: 1.8rem;
182
+ font-weight: 600;
183
+ z-index: 3;
184
+ background: linear-gradient(135deg, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0.6) 100%);
185
+ padding: 1.5rem 2.5rem;
186
+ border-radius: 15px;
187
+ backdrop-filter: blur(10px);
188
+ border: 1px solid rgba(255, 255, 255, 0.1);
189
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
190
+ }
191
+
192
+ .progress-bar {
193
+ position: absolute;
194
+ bottom: 0;
195
+ left: 0;
196
+ width: 100%;
197
+ height: 4px;
198
+ background: rgba(255, 255, 255, 0.15);
199
+ z-index: 4;
200
+ display: none;
201
+ overflow: hidden;
202
+ }
203
+
204
+ .progress-bar-fill {
205
+ height: 100%;
206
+ background: linear-gradient(90deg, #ffd700 0%, #ffed4e 50%, #ffd700 100%);
207
+ width: 0%;
208
+ transition: width 5s cubic-bezier(0.4, 0, 0.2, 1);
209
+ position: relative;
210
+ }
211
+
212
+ .progress-bar-fill::after {
213
+ content: '';
214
+ position: absolute;
215
+ top: 0;
216
+ left: -100%;
217
+ width: 100%;
218
+ height: 100%;
219
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
220
+ animation: shimmer 2s infinite;
221
+ }
222
+
223
+ @keyframes shimmer {
224
+ 0% { left: -100%; }
225
+ 100% { left: 100%; }
226
+ }
227
+
228
+ .image-loading {
229
+ position: absolute;
230
+ top: 50%;
231
+ left: 50%;
232
+ transform: translate(-50%, -50%);
233
+ color: white;
234
+ font-size: 1.2rem;
235
+ font-weight: 500;
236
+ z-index: 3;
237
+ display: none;
238
+ background: linear-gradient(135deg, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0.6) 100%);
239
+ padding: 1rem 2rem;
240
+ border-radius: 12px;
241
+ backdrop-filter: blur(8px);
242
+ border: 1px solid rgba(255, 255, 255, 0.1);
243
+ }
244
+ @media (max-width: 768px) {
245
+ .media-content {
246
+ max-width: 90%;
247
+ }
248
+
249
+ .media-overview-container {
250
+ position: relative;
251
+ bottom: auto;
252
+ right: auto;
253
+ max-width: 100%;
254
+ margin-top: 2rem;
255
+ }
256
+
257
+ #movie-container {
258
+ padding: 2rem 5%;
259
+ align-items: flex-start;
260
+ }
261
+ }
262
+ </style>
263
+ </head>
264
+ <body>
265
+ <div class="loading-indicator" id="loading">Loading media...</div>
266
+ <div class="image-loading" id="image-loading">Loading image...</div>
267
+ <div class="progress-bar" id="progress-bar">
268
+ <div class="progress-bar-fill" id="progress-fill"></div>
269
+ </div>
270
+
271
+ <div id="movie-container">
272
+ <div class="media-content">
273
+ <div class="media-type-badge" id="media-type-badge"></div>
274
+ <h1 id="media-title" class="media-title"></h1>
275
+ <div class="text-content" id="text-content">
276
+ <p id="media-subtitle" class="media-subtitle"></p>
277
+ <div class="media-rating">
278
+ <rating-bar id="rating-bar"></rating-bar>
279
+ </div>
280
+ </div>
281
+ </div>
282
+
283
+ <div class="media-overview-container" id="overview-container">
284
+ <div class="overview-title">Overview</div>
285
+ <div id="media-overview" class="media-overview"></div>
286
+ </div>
287
+ </div>
288
+
289
+ <script>
290
+ class MediaBanner {
291
+ constructor() {
292
+ this.apiKey = '6b8e3eaa1a03ebb45642e9531d8a76d2';
293
+ this.currentIndex = 0;
294
+ this.mediaItems = [];
295
+ this.nextImage = null;
296
+ this.totalPages = 3;
297
+ this.currentPage = 1;
298
+ this.textTimer = null;
299
+
300
+ this.elements = {
301
+ container: document.getElementById('movie-container'),
302
+ loading: document.getElementById('loading'),
303
+ imageLoading: document.getElementById('image-loading'),
304
+ mediaTypeBadge: document.getElementById('media-type-badge'),
305
+ title: document.getElementById('media-title'),
306
+ subtitle: document.getElementById('media-subtitle'),
307
+ overview: document.getElementById('media-overview'),
308
+ ratingBar: document.getElementById('rating-bar'),
309
+ textContent: document.getElementById('text-content'),
310
+ overviewContainer: document.getElementById('overview-container'),
311
+ progressBar: document.getElementById('progress-bar'),
312
+ progressFill: document.getElementById('progress-fill')
313
+ };
314
+
315
+ this.init();
316
+ }
317
+
318
+ async init() {
319
+ try {
320
+ await this.fetchMixedMedia();
321
+ this.elements.loading.style.display = 'none';
322
+
323
+ // Start with first media item
324
+ await this.updateMediaInfo();
325
+
326
+ // Set interval for subsequent updates
327
+ setInterval(() => this.updateMediaInfo(), 15000);
328
+ } catch (error) {
329
+ console.error('Initialization error:', error);
330
+ this.elements.loading.textContent = 'Error loading media';
331
+ }
332
+ }
333
+
334
+ async fetchMixedMedia() {
335
+ // Fetch both movies and TV shows, then mix them together
336
+ const [movies, tvShows] = await Promise.all([
337
+ this.fetchMovies(),
338
+ this.fetchTVShows()
339
+ ]);
340
+
341
+ // Shuffle and mix the arrays
342
+ this.mediaItems = this.shuffleArray([...movies, ...tvShows]);
343
+ console.log(`Loaded ${movies.length} movies and ${tvShows.length} TV shows`);
344
+ }
345
+
346
+ async fetchMovies() {
347
+ const movies = [];
348
+ let page = 1;
349
+
350
+ while (page <= this.totalPages) {
351
+ try {
352
+ const response = await fetch(
353
+ `https://api.themoviedb.org/3/trending/movie/week?api_key=${this.apiKey}&page=${page}&sort_by=vote_average.desc&language=en-GB`
354
+ );
355
+ const data = await response.json();
356
+ movies.push(...data.results.map(movie => ({
357
+ id: movie.id,
358
+ type: 'movie',
359
+ title: movie.title,
360
+ backdrop_path: movie.backdrop_path,
361
+ poster_path: movie.poster_path
362
+ })));
363
+ page++;
364
+ } catch (error) {
365
+ console.error('Error fetching movies:', error);
366
+ break;
367
+ }
368
+ }
369
+ return movies;
370
+ }
371
+
372
+ async fetchTVShows() {
373
+ const tvShows = [];
374
+ let page = 1;
375
+
376
+ while (page <= this.totalPages) {
377
+ try {
378
+ const response = await fetch(
379
+ `https://api.themoviedb.org/3/trending/tv/week?api_key=${this.apiKey}&page=${page}&sort_by=vote_average.desc&language=en-GB`
380
+ );
381
+ const data = await response.json();
382
+ tvShows.push(...data.results.map(tv => ({
383
+ id: tv.id,
384
+ type: 'tv',
385
+ title: tv.name,
386
+ backdrop_path: tv.backdrop_path,
387
+ poster_path: tv.poster_path
388
+ })));
389
+ page++;
390
+ } catch (error) {
391
+ console.error('Error fetching TV shows:', error);
392
+ break;
393
+ }
394
+ }
395
+ return tvShows;
396
+ }
397
+
398
+ async preloadNextImage() {
399
+ if (this.mediaItems.length === 0) return;
400
+
401
+ const nextIndex = (this.currentIndex + 1) % this.mediaItems.length;
402
+ const nextMedia = this.mediaItems[nextIndex];
403
+
404
+ try {
405
+ const response = await fetch(
406
+ `https://api.themoviedb.org/3/${nextMedia.type}/${nextMedia.id}?api_key=${this.apiKey}&language=en-GB`
407
+ );
408
+ const data = await response.json();
409
+ this.nextImage = new Image();
410
+ this.nextImage.src = `https://image.tmdb.org/t/p/original${data.backdrop_path || data.poster_path}`;
411
+ } catch (error) {
412
+ console.error('Error preloading next image:', error);
413
+ }
414
+ }
415
+
416
+ shuffleArray(array) {
417
+ for (let i = array.length - 1; i > 0; i--) {
418
+ const j = Math.floor(Math.random() * (i + 1));
419
+ [array[i], array[j]] = [array[j], array[i]];
420
+ }
421
+ return array;
422
+ }
423
+
424
+ async updateMediaInfo() {
425
+ if (this.mediaItems.length === 0) return;
426
+
427
+ const mediaItem = this.mediaItems[this.currentIndex];
428
+
429
+ try {
430
+ // Hide text content immediately when starting new media
431
+ this.hideTextContent();
432
+
433
+ // Show image loading indicator
434
+ this.elements.imageLoading.style.display = 'block';
435
+
436
+ const response = await fetch(
437
+ `https://api.themoviedb.org/3/${mediaItem.type}/${mediaItem.id}?api_key=${this.apiKey}&language=en-GB`
438
+ );
439
+ const data = await response.json();
440
+
441
+ // Load the image first and wait for it to load
442
+ await this.loadBackgroundImage(data);
443
+
444
+ // Hide image loading indicator
445
+ this.elements.imageLoading.style.display = 'none';
446
+
447
+ this.elements.container.style.opacity = '0';
448
+
449
+ setTimeout(() => {
450
+ this.updateDOM(data, mediaItem.type);
451
+ this.elements.container.style.opacity = '1';
452
+
453
+ // Start the 5-second delay for text (only after image is loaded)
454
+ this.startTextDelay();
455
+
456
+ this.currentIndex = (this.currentIndex + 1) % this.mediaItems.length;
457
+ this.preloadNextImage();
458
+ }, 500);
459
+
460
+ } catch (error) {
461
+ console.error('Error updating media info:', error);
462
+ this.elements.imageLoading.style.display = 'none';
463
+ }
464
+ }
465
+
466
+ loadBackgroundImage(mediaData) {
467
+ return new Promise((resolve, reject) => {
468
+ const imagePath = `https://image.tmdb.org/t/p/original${mediaData.backdrop_path || mediaData.poster_path}`;
469
+ const img = new Image();
470
+
471
+ img.onload = () => {
472
+ console.log('Background image loaded successfully');
473
+ document.body.style.backgroundImage = `url('${imagePath}')`;
474
+ resolve();
475
+ };
476
+
477
+ img.onerror = () => {
478
+ console.log('Background image failed to load');
479
+ reject(new Error('Image loading failed'));
480
+ };
481
+
482
+ img.src = imagePath;
483
+ });
484
+ }
485
+
486
+ updateDOM(mediaData, mediaType) {
487
+ // Background image is already loaded at this point
488
+
489
+ // Update media type badge
490
+ this.elements.mediaTypeBadge.textContent = mediaType === 'movie' ? '🎬 MOVIE' : 'πŸ“Ί TV SERIES';
491
+
492
+ // Update title with year
493
+ const releaseYear = mediaType === 'movie'
494
+ ? new Date(mediaData.release_date).getFullYear()
495
+ : new Date(mediaData.first_air_date).getFullYear();
496
+
497
+ const title = mediaType === 'movie' ? mediaData.title : mediaData.name;
498
+ this.elements.title.textContent = `${title} (${releaseYear})`;
499
+
500
+ // Update subtitle based on media type
501
+ const genres = mediaData.genres.map(genre => `🎬 ${genre.name}`).join(' ');
502
+
503
+ let subtitle = '';
504
+ if (mediaType === 'movie') {
505
+ const countries = mediaData.production_companies && mediaData.production_companies.length > 0
506
+ ? [...new Set(mediaData.production_companies.map(comp => comp.origin_country).filter(Boolean))].join(', ')
507
+ : 'Unknown';
508
+ const hours = Math.floor(mediaData.runtime / 60);
509
+ const minutes = mediaData.runtime % 60;
510
+ subtitle = `πŸ“€ ${mediaData.release_date} (${countries}) | ${genres} | πŸ• ${hours}h ${minutes}m`;
511
+ } else {
512
+ const countries = mediaData.production_companies && mediaData.production_companies.length > 0
513
+ ? [...new Set(mediaData.production_companies.map(comp => comp.origin_country).filter(Boolean))].join(', ')
514
+ : 'Unknown';
515
+ const seasons = mediaData.number_of_seasons;
516
+ const episodes = mediaData.number_of_episodes;
517
+ subtitle = `πŸ“Ί ${mediaData.first_air_date} (${countries}) | ${genres} | πŸ“Š ${seasons} seasons, ${episodes} episodes`;
518
+ }
519
+
520
+ this.elements.subtitle.textContent = subtitle;
521
+
522
+ // Update rating
523
+ this.elements.ratingBar.setAttribute('value', mediaData.vote_average);
524
+
525
+ // Update overview
526
+ this.elements.overview.textContent = mediaData.overview;
527
+ }
528
+
529
+ hideTextContent() {
530
+ // Clear any existing timer
531
+ if (this.textTimer) {
532
+ clearTimeout(this.textTimer);
533
+ this.textTimer = null;
534
+ }
535
+
536
+ // Hide text content and overview
537
+ this.elements.textContent.classList.remove('visible');
538
+ this.elements.overviewContainer.classList.remove('visible');
539
+
540
+ // Hide and reset progress bar
541
+ this.elements.progressBar.style.display = 'none';
542
+ this.elements.progressFill.style.width = '0%';
543
+ }
544
+
545
+ startTextDelay() {
546
+ // Show progress bar (only after image is loaded)
547
+ this.elements.progressBar.style.display = 'block';
548
+
549
+ // Reset and start progress animation
550
+ this.elements.progressFill.style.transition = 'none';
551
+ this.elements.progressFill.style.width = '0%';
552
+
553
+ setTimeout(() => {
554
+ this.elements.progressFill.style.transition = 'width 5s linear';
555
+ this.elements.progressFill.style.width = '100%';
556
+ }, 10);
557
+
558
+ // Set timer to show text after 5 seconds
559
+ this.textTimer = setTimeout(() => {
560
+ this.showTextContent();
561
+ }, 5000);
562
+ }
563
+
564
+ showTextContent() {
565
+ this.elements.textContent.classList.add('visible');
566
+ this.elements.overviewContainer.classList.add('visible');
567
+
568
+ // Hide progress bar after a short delay
569
+ setTimeout(() => {
570
+ this.elements.progressBar.style.display = 'none';
571
+ }, 500);
572
+ }
573
+ }
574
+
575
+ // Custom rating bar element
576
+ customElements.define("rating-bar", class RatingBar extends HTMLElement {
577
+ constructor() {
578
+ super();
579
+ this.attachShadow({ mode: "open" });
580
+ this.maxValue = 10;
581
+ }
582
+
583
+ static get observedAttributes() {
584
+ return ["value"];
585
+ }
586
+
587
+ attributeChangedCallback(name, oldValue, newValue) {
588
+ if (name === "value") {
589
+ this.render();
590
+ }
591
+ }
592
+
593
+ connectedCallback() {
594
+ this.render();
595
+ }
596
+
597
+ render() {
598
+ const value = parseFloat(this.getAttribute("value")) || 0;
599
+ const percentage = (value / this.maxValue) * 100;
600
+
601
+ this.shadowRoot.innerHTML = `
602
+ <style>
603
+ .rating-container {
604
+ display: flex;
605
+ align-items: center;
606
+ gap: 10px;
607
+ font-family: Arial, sans-serif;
608
+ }
609
+
610
+ .stars {
611
+ display: flex;
612
+ position: relative;
613
+ }
614
+
615
+ .stars-background {
616
+ color: #666;
617
+ }
618
+
619
+ .stars-foreground {
620
+ position: absolute;
621
+ top: 0;
622
+ left: 0;
623
+ color: #ffd700;
624
+ overflow: hidden;
625
+ width: ${percentage}%;
626
+ }
627
+
628
+ .rating-value {
629
+ color: white;
630
+ font-weight: bold;
631
+ font-size: 1.1rem;
632
+ }
633
+ </style>
634
+ <div class="rating-container">
635
+ <div class="stars">
636
+ <div class="stars-background">β˜…β˜…β˜…β˜…β˜…β˜…β˜…β˜…β˜…β˜…</div>
637
+ <div class="stars-foreground">β˜…β˜…β˜…β˜…β˜…β˜…β˜…β˜…β˜…β˜…</div>
638
+ </div>
639
+ <span class="rating-value">${value.toFixed(1)}</span>
640
+ </div>
641
+ `;
642
+ }
643
+ });
644
+
645
+ // Initialize the media banner when the page loads
646
+ document.addEventListener('DOMContentLoaded', () => {
647
+ new MediaBanner();
648
+ });
649
+ </script>
650
+ </body>
651
+ </html>