Mousco commited on
Commit
e711652
·
verified ·
1 Parent(s): cf423eb

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +717 -19
index.html CHANGED
@@ -1,19 +1,717 @@
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="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>YouTube Lounge Viewer</title>
7
+ <!-- Importation de la police Inter -->
8
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet">
9
+ <!-- Importation de FontAwesome pour les icônes -->
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
11
+
12
+ <style>
13
+ /* --- VARIABLES CSS & THEME --- */
14
+ :root {
15
+ --bg-body: #0f0f0f;
16
+ --bg-sidebar: #1e1e1e;
17
+ --bg-card: #2a2a2a;
18
+ --bg-input: #333333;
19
+ --text-primary: #ffffff;
20
+ --text-secondary: #aaaaaa;
21
+ --accent-color: #ff0000; /* YouTube Red */
22
+ --accent-hover: #cc0000;
23
+ --border-color: #3f3f3f;
24
+ --shadow: 0 4px 15px rgba(0,0,0,0.5);
25
+ --transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
26
+ --header-height: 70px;
27
+ }
28
+
29
+ /* --- RESET & BASE --- */
30
+ * {
31
+ box-sizing: border-box;
32
+ margin: 0;
33
+ padding: 0;
34
+ outline: none;
35
+ }
36
+
37
+ body {
38
+ font-family: 'Inter', sans-serif;
39
+ background-color: var(--bg-body);
40
+ color: var(--text-primary);
41
+ height: 100vh;
42
+ display: flex;
43
+ flex-direction: column;
44
+ overflow: hidden; /* Empêche le scroll global, on scroll dans les zones */
45
+ }
46
+
47
+ a { text-decoration: none; color: inherit; }
48
+ ul { list-style: none; }
49
+ button { cursor: pointer; border: none; font-family: inherit; }
50
+
51
+ /* --- HEADER --- */
52
+ header {
53
+ height: var(--header-height);
54
+ background-color: rgba(30, 30, 30, 0.95);
55
+ backdrop-filter: blur(10px);
56
+ border-bottom: 1px solid var(--border-color);
57
+ display: flex;
58
+ align-items: center;
59
+ justify-content: space-between;
60
+ padding: 0 2rem;
61
+ z-index: 100;
62
+ }
63
+
64
+ .logo {
65
+ font-size: 1.5rem;
66
+ font-weight: 700;
67
+ display: flex;
68
+ align-items: center;
69
+ gap: 10px;
70
+ }
71
+
72
+ .logo i { color: var(--accent-color); font-size: 1.8rem; }
73
+
74
+ .header-controls {
75
+ display: flex;
76
+ align-items: center;
77
+ gap: 1.5rem;
78
+ }
79
+
80
+ .anycoder-link {
81
+ font-size: 0.85rem;
82
+ color: var(--text-secondary);
83
+ transition: var(--transition);
84
+ background: rgba(255, 255, 255, 0.05);
85
+ padding: 5px 10px;
86
+ border-radius: 4px;
87
+ }
88
+
89
+ .anycoder-link:hover {
90
+ color: var(--text-primary);
91
+ background: rgba(255, 255, 255, 0.1);
92
+ }
93
+
94
+ /* --- MAIN LAYOUT --- */
95
+ main {
96
+ flex: 1;
97
+ display: grid;
98
+ grid-template-columns: 1fr 350px; /* Player area | Sidebar */
99
+ height: calc(100vh - var(--header-height));
100
+ overflow: hidden;
101
+ }
102
+
103
+ /* --- PLAYER SECTION --- */
104
+ .player-container {
105
+ display: flex;
106
+ flex-direction: column;
107
+ justify-content: center;
108
+ align-items: center;
109
+ padding: 2rem;
110
+ background-color: #000;
111
+ position: relative;
112
+ }
113
+
114
+ .video-wrapper {
115
+ width: 100%;
116
+ max-width: 1200px;
117
+ aspect-ratio: 16 / 9;
118
+ background: #111;
119
+ border-radius: 12px;
120
+ overflow: hidden;
121
+ box-shadow: var(--shadow);
122
+ position: relative;
123
+ }
124
+
125
+ iframe {
126
+ width: 100%;
127
+ height: 100%;
128
+ border: none;
129
+ }
130
+
131
+ .video-info {
132
+ width: 100%;
133
+ max-width: 1200px;
134
+ margin-top: 1.5rem;
135
+ }
136
+
137
+ .video-title {
138
+ font-size: 1.5rem;
139
+ font-weight: 600;
140
+ margin-bottom: 0.5rem;
141
+ line-height: 1.3;
142
+ }
143
+
144
+ .video-meta {
145
+ color: var(--text-secondary);
146
+ font-size: 0.9rem;
147
+ display: flex;
148
+ gap: 1rem;
149
+ }
150
+
151
+ /* --- SIDEBAR / PLAYLIST --- */
152
+ .sidebar {
153
+ background-color: var(--bg-sidebar);
154
+ border-left: 1px solid var(--border-color);
155
+ display: flex;
156
+ flex-direction: column;
157
+ height: 100%;
158
+ }
159
+
160
+ .sidebar-header {
161
+ padding: 1.5rem;
162
+ border-bottom: 1px solid var(--border-color);
163
+ }
164
+
165
+ .sidebar-title {
166
+ font-size: 1.2rem;
167
+ font-weight: 600;
168
+ margin-bottom: 1rem;
169
+ display: flex;
170
+ justify-content: space-between;
171
+ align-items: center;
172
+ }
173
+
174
+ /* Formulaire d'ajout */
175
+ .add-video-form {
176
+ display: flex;
177
+ flex-direction: column;
178
+ gap: 10px;
179
+ }
180
+
181
+ .input-group {
182
+ position: relative;
183
+ }
184
+
185
+ .input-group i {
186
+ position: absolute;
187
+ left: 12px;
188
+ top: 50%;
189
+ transform: translateY(-50%);
190
+ color: var(--text-secondary);
191
+ }
192
+
193
+ input[type="text"], input[type="url"] {
194
+ width: 100%;
195
+ background-color: var(--bg-input);
196
+ border: 1px solid transparent;
197
+ color: var(--text-primary);
198
+ padding: 10px 10px 10px 35px;
199
+ border-radius: 6px;
200
+ font-size: 0.9rem;
201
+ transition: var(--transition);
202
+ }
203
+
204
+ input:focus {
205
+ border-color: var(--accent-color);
206
+ background-color: #3a3a3a;
207
+ }
208
+
209
+ .btn-add {
210
+ background-color: var(--accent-color);
211
+ color: white;
212
+ padding: 10px;
213
+ border-radius: 6px;
214
+ font-weight: 600;
215
+ display: flex;
216
+ align-items: center;
217
+ justify-content: center;
218
+ gap: 8px;
219
+ transition: var(--transition);
220
+ }
221
+
222
+ .btn-add:hover {
223
+ background-color: var(--accent-hover);
224
+ transform: translateY(-1px);
225
+ }
226
+
227
+ /* Liste des vidéos */
228
+ .playlist {
229
+ flex: 1;
230
+ overflow-y: auto;
231
+ padding: 1rem;
232
+ }
233
+
234
+ /* Scrollbar custom */
235
+ .playlist::-webkit-scrollbar { width: 6px; }
236
+ .playlist::-webkit-scrollbar-track { background: var(--bg-sidebar); }
237
+ .playlist::-webkit-scrollbar-thumb { background: #444; border-radius: 3px; }
238
+ .playlist::-webkit-scrollbar-thumb:hover { background: #555; }
239
+
240
+ .playlist-item {
241
+ display: flex;
242
+ gap: 10px;
243
+ padding: 10px;
244
+ border-radius: 8px;
245
+ cursor: pointer;
246
+ transition: var(--transition);
247
+ margin-bottom: 8px;
248
+ border: 1px solid transparent;
249
+ }
250
+
251
+ .playlist-item:hover {
252
+ background-color: var(--bg-card);
253
+ }
254
+
255
+ .playlist-item.active {
256
+ background-color: rgba(255, 0, 0, 0.1);
257
+ border-color: rgba(255, 0, 0, 0.3);
258
+ }
259
+
260
+ .playlist-thumb {
261
+ width: 100px;
262
+ height: 56px; /* 16:9 approx */
263
+ background-color: #000;
264
+ border-radius: 4px;
265
+ overflow: hidden;
266
+ flex-shrink: 0;
267
+ position: relative;
268
+ }
269
+
270
+ .playlist-thumb img {
271
+ width: 100%;
272
+ height: 100%;
273
+ object-fit: cover;
274
+ }
275
+
276
+ .playlist-info {
277
+ display: flex;
278
+ flex-direction: column;
279
+ justify-content: center;
280
+ overflow: hidden;
281
+ }
282
+
283
+ .playlist-title {
284
+ font-size: 0.9rem;
285
+ font-weight: 500;
286
+ white-space: nowrap;
287
+ overflow: hidden;
288
+ text-overflow: ellipsis;
289
+ margin-bottom: 4px;
290
+ }
291
+
292
+ .playlist-actions {
293
+ margin-top: auto;
294
+ display: flex;
295
+ justify-content: space-between;
296
+ align-items: center;
297
+ }
298
+
299
+ .btn-delete {
300
+ color: var(--text-secondary);
301
+ font-size: 0.8rem;
302
+ padding: 4px;
303
+ transition: color 0.2s;
304
+ }
305
+ .btn-delete:hover { color: var(--accent-color); }
306
+
307
+ .empty-state {
308
+ text-align: center;
309
+ color: var(--text-secondary);
310
+ margin-top: 2rem;
311
+ font-size: 0.9rem;
312
+ }
313
+
314
+ /* --- CINEMA MODE --- */
315
+ body.cinema-mode header,
316
+ body.cinema-mode .sidebar {
317
+ display: none;
318
+ }
319
+
320
+ body.cinema-mode main {
321
+ grid-template-columns: 1fr;
322
+ height: 100vh;
323
+ }
324
+
325
+ body.cinema-mode .player-container {
326
+ height: 100vh;
327
+ padding: 0;
328
+ }
329
+
330
+ body.cinema-mode .video-wrapper {
331
+ max-width: 100%;
332
+ border-radius: 0;
333
+ height: 100%;
334
+ }
335
+
336
+ .btn-cinema {
337
+ background: transparent;
338
+ color: var(--text-primary);
339
+ font-size: 1.2rem;
340
+ padding: 8px;
341
+ border-radius: 50%;
342
+ transition: var(--transition);
343
+ }
344
+
345
+ .btn-cinema:hover {
346
+ background-color: rgba(255,255,255,0.1);
347
+ }
348
+
349
+ /* --- TOAST NOTIFICATIONS --- */
350
+ #toast-container {
351
+ position: fixed;
352
+ bottom: 20px;
353
+ left: 50%;
354
+ transform: translateX(-50%);
355
+ display: flex;
356
+ flex-direction: column;
357
+ gap: 10px;
358
+ z-index: 1000;
359
+ }
360
+
361
+ .toast {
362
+ background-color: #333;
363
+ color: #fff;
364
+ padding: 12px 24px;
365
+ border-radius: 50px;
366
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3);
367
+ font-size: 0.9rem;
368
+ display: flex;
369
+ align-items: center;
370
+ gap: 10px;
371
+ animation: slideUp 0.3s ease-out forwards;
372
+ min-width: 250px;
373
+ justify-content: center;
374
+ }
375
+
376
+ .toast.success { border-left: 4px solid #4caf50; }
377
+ .toast.error { border-left: 4px solid #f44336; }
378
+
379
+ @keyframes slideUp {
380
+ from { opacity: 0; transform: translateY(20px); }
381
+ to { opacity: 1; transform: translateY(0); }
382
+ }
383
+
384
+ /* --- RESPONSIVE --- */
385
+ @media (max-width: 900px) {
386
+ main {
387
+ grid-template-columns: 1fr;
388
+ overflow-y: auto;
389
+ height: auto;
390
+ }
391
+
392
+ .player-container {
393
+ padding: 1rem;
394
+ min-height: 50vh;
395
+ }
396
+
397
+ .sidebar {
398
+ border-left: none;
399
+ border-top: 1px solid var(--border-color);
400
+ height: auto;
401
+ }
402
+
403
+ .playlist {
404
+ max-height: 500px;
405
+ }
406
+ }
407
+ </style>
408
+ </head>
409
+ <body>
410
+
411
+ <!-- Header -->
412
+ <header>
413
+ <div class="logo">
414
+ <i class="fa-brands fa-youtube"></i>
415
+ <span>YT Lounge</span>
416
+ </div>
417
+
418
+ <div class="header-controls">
419
+ <button class="btn-cinema" id="cinema-toggle" title="Mode Cinéma">
420
+ <i class="fa-solid fa-expand"></i>
421
+ </button>
422
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
423
+ Built with anycoder
424
+ </a>
425
+ </div>
426
+ </header>
427
+
428
+ <!-- Main Content -->
429
+ <main>
430
+ <!-- Player Section -->
431
+ <section class="player-container">
432
+ <div class="video-wrapper">
433
+ <!-- Iframe YouTube par défaut -->
434
+ <iframe id="yt-player"
435
+ src="https://www.youtube.com/embed/jfKfPfyJRdk?enablejsapi=1"
436
+ title="YouTube video player"
437
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
438
+ allowfullscreen>
439
+ </iframe>
440
+ </div>
441
+
442
+ <div class="video-info">
443
+ <h1 class="video-title" id="current-title">Lofi Hip Hop Radio - Beats to Relax/Study to</h1>
444
+ <div class="video-meta">
445
+ <span><i class="fa-regular fa-clock"></i> Lecture en cours</span>
446
+ <span><i class="fa-solid fa-tv"></i> YouTube Embed</span>
447
+ </div>
448
+ </div>
449
+ </section>
450
+
451
+ <!-- Sidebar / Playlist -->
452
+ <aside class="sidebar">
453
+ <div class="sidebar-header">
454
+ <div class="sidebar-title">
455
+ <span>Mes Vidéos</span>
456
+ <span style="font-size: 0.8rem; font-weight: 400; color: var(--text-secondary);" id="playlist-count">0</span>
457
+ </div>
458
+
459
+ <form class="add-video-form" id="add-form">
460
+ <div class="input-group">
461
+ <i class="fa-solid fa-link"></i>
462
+ <input type="url" id="video-url" placeholder="Lien YouTube (ex: youtube.com/watch?v=...)" required>
463
+ </div>
464
+ <!-- Optionnel : Titre personnalisé, sinon on utilisera l'ID généré -->
465
+ <button type="submit" class="btn-add">
466
+ <i class="fa-solid fa-plus"></i> Ajouter
467
+ </button>
468
+ </form>
469
+ </div>
470
+
471
+ <ul class="playlist" id="playlist">
472
+ <!-- Les éléments seront injectés ici par JS -->
473
+ </ul>
474
+ </aside>
475
+ </main>
476
+
477
+ <!-- Toast Container -->
478
+ <div id="toast-container"></div>
479
+
480
+ <script>
481
+ /**
482
+ * APPLICATION YOUTUBE LOUNGE
483
+ * Logique : Vanilla JS
484
+ * Stockage : LocalStorage
485
+ */
486
+
487
+ // --- État de l'application ---
488
+ let playlist = [];
489
+ const STORAGE_KEY = 'yt_lounge_playlist_v1';
490
+
491
+ // Éléments DOM
492
+ const playlistEl = document.getElementById('playlist');
493
+ const playerEl = document.getElementById('yt-player');
494
+ const currentTitleEl = document.getElementById('current-title');
495
+ const addForm = document.getElementById('add-form');
496
+ const urlInput = document.getElementById('video-url');
497
+ const playlistCountEl = document.getElementById('playlist-count');
498
+ const cinemaToggle = document.getElementById('cinema-toggle');
499
+
500
+ // --- Initialisation ---
501
+ document.addEventListener('DOMContentLoaded', () => {
502
+ loadPlaylist();
503
+
504
+ // Si la playlist est vide au premier lancement, ajouter une vidéo par défaut
505
+ if (playlist.length === 0) {
506
+ addVideoToState('https://www.youtube.com/watch?v=jfKfPfyJRdk', 'Lofi Hip Hop Radio - Beats to Relax/Study to');
507
+ } else {
508
+ // Charger la première vidéo
509
+ loadVideo(playlist[0].id);
510
+ }
511
+
512
+ renderPlaylist();
513
+ });
514
+
515
+ // --- Fonctions Principales ---
516
+
517
+ // 1. Charger la playlist depuis le LocalStorage
518
+ function loadPlaylist() {
519
+ const stored = localStorage.getItem(STORAGE_KEY);
520
+ if (stored) {
521
+ try {
522
+ playlist = JSON.parse(stored);
523
+ } catch (e) {
524
+ console.error("Erreur de lecture du storage", e);
525
+ playlist = [];
526
+ }
527
+ }
528
+ }
529
+
530
+ // 2. Sauvegarder dans le LocalStorage
531
+ function savePlaylist() {
532
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(playlist));
533
+ updateCount();
534
+ }
535
+
536
+ // 3. Extraire l'ID YouTube depuis une URL
537
+ function extractYouTubeID(url) {
538
+ const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;
539
+ const match = url.match(regExp);
540
+ return (match && match[2].length === 11) ? match[2] : null;
541
+ }
542
+
543
+ // 4. Ajouter une vidéo
544
+ function addVideoToState(url, customTitle = null) {
545
+ const videoId = extractYouTubeID(url);
546
+
547
+ if (!videoId) {
548
+ showToast("Lien YouTube invalide", "error");
549
+ return false;
550
+ }
551
+
552
+ // Vérifier doublon
553
+ const exists = playlist.find(v => v.id === videoId);
554
+ if (exists) {
555
+ showToast("Cette vidéo est déjà dans la liste", "error");
556
+ return false;
557
+ }
558
+
559
+ const title = customTitle || `Vidéo YouTube (${videoId})`;
560
+
561
+ const newVideo = {
562
+ id: videoId,
563
+ title: title,
564
+ thumbnail: `https://img.youtube.com/vi/${videoId}/mqdefault.jpg`,
565
+ addedAt: Date.now()
566
+ };
567
+
568
+ playlist.unshift(newVideo); // Ajouter au début
569
+ savePlaylist();
570
+ renderPlaylist();
571
+ showToast("Vidéo ajoutée avec succès", "success");
572
+ return true;
573
+ }
574
+
575
+ // 5. Charger une vidéo dans le lecteur
576
+ function loadVideo(id) {
577
+ // Mise à jour de l'iframe
578
+ playerEl.src = `https://www.youtube.com/embed/${id}?autoplay=1&rel=0`;
579
+
580
+ // Mise à jour de l'UI active
581
+ document.querySelectorAll('.playlist-item').forEach(item => {
582
+ item.classList.remove('active');
583
+ if(item.dataset.id === id) {
584
+ item.classList.add('active');
585
+ }
586
+ });
587
+
588
+ // Mise à jour du titre
589
+ const vidObj = playlist.find(v => v.id === id);
590
+ if (vidObj) {
591
+ currentTitleEl.textContent = vidObj.title;
592
+ }
593
+ }
594
+
595
+ // 6. Supprimer une vidéo
596
+ function deleteVideo(id, event) {
597
+ event.stopPropagation(); // Empêcher le clic de jouer la vidéo
598
+
599
+ if(confirm("Voulez-vous vraiment supprimer cette vidéo de la liste ?")) {
600
+ playlist = playlist.filter(v => v.id !== id);
601
+ savePlaylist();
602
+ renderPlaylist();
603
+ showToast("Vidéo supprimée", "success");
604
+
605
+ // Si on supprime la vidéo en cours, on peut remettre la première ou arrêter
606
+ // Ici on laisse la iframe telle quelle pour ne pas couper brutalement
607
+ }
608
+ }
609
+
610
+ // 7. Rendu de la liste
611
+ function renderPlaylist() {
612
+ playlistEl.innerHTML = '';
613
+
614
+ if (playlist.length === 0) {
615
+ playlistEl.innerHTML = `
616
+ <div class="empty-state">
617
+ <i class="fa-solid fa-film" style="font-size: 2rem; margin-bottom: 10px; display:block;"></i>
618
+ Votre liste est vide.<br>Ajoutez un lien YouTube ci-dessus.
619
+ </div>
620
+ `;
621
+ updateCount();
622
+ return;
623
+ }
624
+
625
+ playlist.forEach(video => {
626
+ const li = document.createElement('li');
627
+ li.className = 'playlist-item';
628
+ li.dataset.id = video.id;
629
+ li.onclick = () => loadVideo(video.id);
630
+
631
+ li.innerHTML = `
632
+ <div class="playlist-thumb">
633
+ <img src="${video.thumbnail}" alt="Thumbnail" loading="lazy">
634
+ </div>
635
+ <div class="playlist-info">
636
+ <div class="playlist-title" title="${video.title}">${video.title}</div>
637
+ <div class="playlist-actions">
638
+ <span style="font-size: 0.7rem; color: #666;">ID: ${video.id}</span>
639
+ <button class="btn-delete" onclick="deleteVideo('${video.id}', event)" title="Supprimer">
640
+ <i class="fa-solid fa-trash"></i>
641
+ </button>
642
+ </div>
643
+ </div>
644
+ `;
645
+ playlistEl.appendChild(li);
646
+ });
647
+
648
+ updateCount();
649
+ }
650
+
651
+ function updateCount() {
652
+ playlistCountEl.textContent = playlist.length;
653
+ }
654
+
655
+ // --- Gestion des événements ---
656
+
657
+ addForm.addEventListener('submit', (e) => {
658
+ e.preventDefault();
659
+ const url = urlInput.value.trim();
660
+ if (!url) return;
661
+
662
+ const success = addVideoToState(url);
663
+ if (success) {
664
+ urlInput.value = '';
665
+ }
666
+ });
667
+
668
+ // Mode Cinéma
669
+ cinemaToggle.addEventListener('click', () => {
670
+ document.body.classList.toggle('cinema-mode');
671
+ const isCinema = document.body.classList.contains('cinema-mode');
672
+ const icon = cinemaToggle.querySelector('i');
673
+
674
+ if (isCinema) {
675
+ icon.classList.remove('fa-expand');
676
+ icon.classList.add('fa-compress');
677
+ showToast("Mode Cinéma activé. Appuyez sur Esc pour quitter.", "success");
678
+ } else {
679
+ icon.classList.remove('fa-compress');
680
+ icon.classList.add('fa-expand');
681
+ }
682
+ });
683
+
684
+ // Quitter le mode cinéma avec Echap
685
+ document.addEventListener('keydown', (e) => {
686
+ if (e.key === 'Escape' && document.body.classList.contains('cinema-mode')) {
687
+ document.body.classList.remove('cinema-mode');
688
+ cinemaToggle.querySelector('i').classList.remove('fa-compress');
689
+ cinemaToggle.querySelector('i').classList.add('fa-expand');
690
+ }
691
+ });
692
+
693
+ // --- Système de Toast (Notifications) ---
694
+ function showToast(message, type = 'success') {
695
+ const container = document.getElementById('toast-container');
696
+ const toast = document.createElement('div');
697
+ toast.className = `toast ${type}`;
698
+
699
+ const icon = type === 'success' ? '<i class="fa-solid fa-check-circle"></i>' : '<i class="fa-solid fa-exclamation-circle"></i>';
700
+
701
+ toast.innerHTML = `${icon} <span>${message}</span>`;
702
+
703
+ container.appendChild(toast);
704
+
705
+ // Auto remove
706
+ setTimeout(() => {
707
+ toast.style.opacity = '0';
708
+ toast.style.transform = 'translateY(20px)';
709
+ toast.addEventListener('transitionend', () => {
710
+ toast.remove();
711
+ });
712
+ }, 3000);
713
+ }
714
+
715
+ </script>
716
+ </body>
717
+ </html>