Guymy97 commited on
Commit
59bdd10
·
verified ·
1 Parent(s): d8b95a0

Deploy from anycoder

Browse files
Files changed (1) hide show
  1. index.html +829 -19
index.html CHANGED
@@ -1,19 +1,829 @@
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>TiviStream - Web IPTV Player</title>
7
+
8
+ <!-- Fonts & Icons -->
9
+ <link rel="preconnect" href="https://fonts.googleapis.com">
10
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet">
12
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
13
+
14
+ <style>
15
+ :root {
16
+ --bg-dark: #0f1115;
17
+ --bg-overlay: rgba(15, 17, 21, 0.85);
18
+ --bg-panel: rgba(30, 34, 42, 0.6);
19
+ --primary: #3b82f6;
20
+ --primary-glow: rgba(59, 130, 246, 0.5);
21
+ --text-main: #ffffff;
22
+ --text-muted: #9ca3af;
23
+ --border-color: rgba(255, 255, 255, 0.1);
24
+ --focus-color: rgba(255, 255, 255, 0.15);
25
+ --transition: all 0.2s ease-out;
26
+ }
27
+
28
+ * {
29
+ box-sizing: border-box;
30
+ margin: 0;
31
+ padding: 0;
32
+ user-select: none; /* TV interface feel */
33
+ }
34
+
35
+ body {
36
+ font-family: 'Inter', sans-serif;
37
+ background-color: #000;
38
+ color: var(--text-main);
39
+ height: 100vh;
40
+ width: 100vw;
41
+ overflow: hidden;
42
+ display: flex;
43
+ flex-direction: column;
44
+ }
45
+
46
+ /* Video Background Layer */
47
+ #video-layer {
48
+ position: fixed;
49
+ top: 0;
50
+ left: 0;
51
+ width: 100%;
52
+ height: 100%;
53
+ z-index: 0;
54
+ background: #000;
55
+ }
56
+
57
+ video {
58
+ width: 100%;
59
+ height: 100%;
60
+ object-fit: cover;
61
+ opacity: 0.6; /* Dimmed when UI is active */
62
+ transition: opacity 0.5s ease;
63
+ }
64
+
65
+ body.ui-hidden video {
66
+ opacity: 1;
67
+ }
68
+
69
+ /* UI Layer */
70
+ #ui-layer {
71
+ position: relative;
72
+ z-index: 10;
73
+ height: 100%;
74
+ display: flex;
75
+ flex-direction: column;
76
+ background: linear-gradient(to right, var(--bg-overlay) 0%, var(--bg-overlay) 35%, rgba(0,0,0,0.2) 100%);
77
+ backdrop-filter: blur(5px);
78
+ transition: transform 0.3s ease, opacity 0.3s ease;
79
+ }
80
+
81
+ body.ui-hidden #ui-layer {
82
+ opacity: 0;
83
+ pointer-events: none;
84
+ }
85
+
86
+ /* Header */
87
+ header {
88
+ display: flex;
89
+ justify-content: space-between;
90
+ align-items: center;
91
+ padding: 1.5rem 2rem;
92
+ border-bottom: 1px solid var(--border-color);
93
+ }
94
+
95
+ .brand {
96
+ display: flex;
97
+ align-items: center;
98
+ gap: 10px;
99
+ font-size: 1.5rem;
100
+ font-weight: 700;
101
+ color: var(--primary);
102
+ text-shadow: 0 0 15px var(--primary-glow);
103
+ }
104
+
105
+ .brand i {
106
+ font-size: 1.8rem;
107
+ }
108
+
109
+ .header-right {
110
+ display: flex;
111
+ align-items: center;
112
+ gap: 20px;
113
+ }
114
+
115
+ .anycoder-link {
116
+ color: var(--text-muted);
117
+ text-decoration: none;
118
+ font-size: 0.9rem;
119
+ padding: 5px 10px;
120
+ border-radius: 4px;
121
+ transition: var(--transition);
122
+ border: 1px solid transparent;
123
+ }
124
+
125
+ .anycoder-link:hover {
126
+ color: var(--text-main);
127
+ background: rgba(255,255,255,0.1);
128
+ border-color: var(--border-color);
129
+ }
130
+
131
+ .clock {
132
+ font-size: 1.2rem;
133
+ font-weight: 600;
134
+ font-variant-numeric: tabular-nums;
135
+ }
136
+
137
+ /* Main Layout */
138
+ .main-container {
139
+ display: flex;
140
+ flex: 1;
141
+ overflow: hidden;
142
+ }
143
+
144
+ /* Sidebar (Groups) */
145
+ .sidebar {
146
+ width: 260px;
147
+ background: rgba(0,0,0,0.3);
148
+ border-right: 1px solid var(--border-color);
149
+ display: flex;
150
+ flex-direction: column;
151
+ padding: 1rem 0;
152
+ overflow-y: auto;
153
+ }
154
+
155
+ .sidebar-title {
156
+ padding: 0 1.5rem 1rem;
157
+ font-size: 0.8rem;
158
+ text-transform: uppercase;
159
+ letter-spacing: 1px;
160
+ color: var(--text-muted);
161
+ }
162
+
163
+ .group-item {
164
+ padding: 12px 1.5rem;
165
+ cursor: pointer;
166
+ display: flex;
167
+ align-items: center;
168
+ justify-content: space-between;
169
+ color: var(--text-muted);
170
+ transition: var(--transition);
171
+ border-left: 3px solid transparent;
172
+ }
173
+
174
+ .group-item:hover {
175
+ background: var(--focus-color);
176
+ color: var(--text-main);
177
+ }
178
+
179
+ .group-item.active {
180
+ background: linear-gradient(90deg, rgba(59, 130, 246, 0.15), transparent);
181
+ color: var(--text-main);
182
+ border-left-color: var(--primary);
183
+ font-weight: 600;
184
+ }
185
+
186
+ .badge {
187
+ background: rgba(255,255,255,0.1);
188
+ padding: 2px 8px;
189
+ border-radius: 10px;
190
+ font-size: 0.75rem;
191
+ }
192
+
193
+ /* Channel List (EPG) */
194
+ .channel-list-container {
195
+ flex: 1;
196
+ display: flex;
197
+ flex-direction: column;
198
+ overflow: hidden;
199
+ position: relative;
200
+ }
201
+
202
+ .epg-header {
203
+ padding: 1rem 1.5rem;
204
+ display: flex;
205
+ justify-content: space-between;
206
+ border-bottom: 1px solid var(--border-color);
207
+ background: rgba(0,0,0,0.2);
208
+ }
209
+
210
+ .epg-time-indicator {
211
+ color: var(--primary);
212
+ font-weight: 600;
213
+ font-size: 0.9rem;
214
+ }
215
+
216
+ .channel-list {
217
+ overflow-y: auto;
218
+ padding-bottom: 100px; /* Space for bottom info */
219
+ }
220
+
221
+ /* Custom Scrollbar */
222
+ ::-webkit-scrollbar {
223
+ width: 6px;
224
+ }
225
+ ::-webkit-scrollbar-track {
226
+ background: transparent;
227
+ }
228
+ ::-webkit-scrollbar-thumb {
229
+ background: rgba(255,255,255,0.2);
230
+ border-radius: 3px;
231
+ }
232
+
233
+ .channel-row {
234
+ display: grid;
235
+ grid-template-columns: 60px 80px 1.5fr 2fr 80px; /* No, Logo, Name, Program, Time */
236
+ align-items: center;
237
+ padding: 15px 1.5rem;
238
+ border-bottom: 1px solid rgba(255,255,255,0.03);
239
+ cursor: pointer;
240
+ transition: var(--transition);
241
+ }
242
+
243
+ .channel-row:hover {
244
+ background: var(--focus-color);
245
+ }
246
+
247
+ .channel-row.active {
248
+ background: var(--primary);
249
+ color: white;
250
+ box-shadow: 0 4px 20px rgba(0,0,0,0.3);
251
+ transform: scale(1.005);
252
+ border-radius: 4px;
253
+ margin: 5px 10px;
254
+ width: calc(100% - 20px);
255
+ border: none;
256
+ }
257
+
258
+ .ch-num {
259
+ font-weight: 300;
260
+ opacity: 0.7;
261
+ }
262
+
263
+ .ch-logo {
264
+ width: 40px;
265
+ height: 40px;
266
+ background: #fff;
267
+ border-radius: 6px;
268
+ display: flex;
269
+ align-items: center;
270
+ justify-content: center;
271
+ color: #333;
272
+ font-weight: bold;
273
+ font-size: 0.8rem;
274
+ overflow: hidden;
275
+ }
276
+
277
+ .ch-logo img {
278
+ width: 100%;
279
+ height: 100%;
280
+ object-fit: contain;
281
+ }
282
+
283
+ .ch-name {
284
+ font-weight: 600;
285
+ padding-right: 15px;
286
+ }
287
+
288
+ .program-info {
289
+ display: flex;
290
+ flex-direction: column;
291
+ gap: 4px;
292
+ }
293
+
294
+ .program-title {
295
+ font-size: 0.95rem;
296
+ white-space: nowrap;
297
+ overflow: hidden;
298
+ text-overflow: ellipsis;
299
+ }
300
+
301
+ .program-progress {
302
+ width: 100%;
303
+ height: 4px;
304
+ background: rgba(255,255,255,0.2);
305
+ border-radius: 2px;
306
+ overflow: hidden;
307
+ }
308
+
309
+ .progress-bar {
310
+ height: 100%;
311
+ background: var(--text-main);
312
+ width: 0%;
313
+ }
314
+
315
+ .channel-row.active .progress-bar {
316
+ background: #fff; /* High contrast on active blue bg */
317
+ }
318
+
319
+ .program-time {
320
+ text-align: right;
321
+ font-size: 0.85rem;
322
+ opacity: 0.8;
323
+ }
324
+
325
+ /* Bottom Info Panel */
326
+ .info-panel {
327
+ position: absolute;
328
+ bottom: 0;
329
+ left: 0;
330
+ width: 100%;
331
+ background: linear-gradient(to top, #000 0%, rgba(15,17,21,0.95) 100%);
332
+ padding: 1.5rem 2rem;
333
+ border-top: 1px solid var(--border-color);
334
+ display: flex;
335
+ justify-content: space-between;
336
+ align-items: flex-end;
337
+ }
338
+
339
+ .current-details h2 {
340
+ font-size: 1.8rem;
341
+ margin-bottom: 0.5rem;
342
+ }
343
+
344
+ .meta-tags {
345
+ display: flex;
346
+ gap: 10px;
347
+ margin-bottom: 1rem;
348
+ }
349
+
350
+ .tag {
351
+ background: rgba(255,255,255,0.15);
352
+ padding: 2px 8px;
353
+ border-radius: 4px;
354
+ font-size: 0.75rem;
355
+ font-weight: 600;
356
+ }
357
+
358
+ .tag.hd { background: #f59e0b; color: #000; }
359
+ .tag.live { background: #ef4444; color: #fff; }
360
+
361
+ .description {
362
+ font-size: 0.9rem;
363
+ color: var(--text-muted);
364
+ max-width: 600px;
365
+ line-height: 1.4;
366
+ }
367
+
368
+ .player-controls {
369
+ display: flex;
370
+ gap: 1rem;
371
+ align-items: center;
372
+ }
373
+
374
+ .btn-control {
375
+ background: transparent;
376
+ border: 1px solid var(--border-color);
377
+ color: var(--text-main);
378
+ width: 40px;
379
+ height: 40px;
380
+ border-radius: 50%;
381
+ display: flex;
382
+ align-items: center;
383
+ justify-content: center;
384
+ cursor: pointer;
385
+ transition: var(--transition);
386
+ }
387
+
388
+ .btn-control:hover {
389
+ background: var(--text-main);
390
+ color: #000;
391
+ }
392
+
393
+ .btn-fullscreen {
394
+ background: var(--primary);
395
+ border: none;
396
+ }
397
+
398
+ /* Responsive */
399
+ @media (max-width: 768px) {
400
+ .sidebar {
401
+ display: none; /* Hide sidebar on mobile for cleaner look, could implement hamburger menu */
402
+ }
403
+
404
+ .channel-row {
405
+ grid-template-columns: 50px 1fr 60px;
406
+ grid-template-areas:
407
+ "logo name time"
408
+ "logo prog prog";
409
+ gap: 5px;
410
+ }
411
+
412
+ .ch-num { display: none; }
413
+ .ch-logo { grid-area: logo; }
414
+ .ch-name { grid-area: name; }
415
+ .program-info { grid-area: prog; }
416
+ .program-time { grid-area: time; }
417
+
418
+ .info-panel {
419
+ flex-direction: column;
420
+ align-items: flex-start;
421
+ gap: 1rem;
422
+ }
423
+
424
+ .description { display: none; }
425
+ }
426
+
427
+ /* Loader */
428
+ .loader {
429
+ position: absolute;
430
+ top: 50%;
431
+ left: 50%;
432
+ transform: translate(-50%, -50%);
433
+ width: 50px;
434
+ height: 50px;
435
+ border: 4px solid rgba(255,255,255,0.1);
436
+ border-left-color: var(--primary);
437
+ border-radius: 50%;
438
+ animation: spin 1s linear infinite;
439
+ display: none;
440
+ z-index: 5;
441
+ }
442
+
443
+ @keyframes spin {
444
+ to { transform: translate(-50%, -50%) rotate(360deg); }
445
+ }
446
+
447
+ /* Notification / Toast */
448
+ .toast {
449
+ position: fixed;
450
+ top: 20px;
451
+ left: 50%;
452
+ transform: translateX(-50%);
453
+ background: var(--primary);
454
+ color: white;
455
+ padding: 10px 20px;
456
+ border-radius: 30px;
457
+ font-size: 0.9rem;
458
+ box-shadow: 0 5px 15px rgba(0,0,0,0.3);
459
+ opacity: 0;
460
+ pointer-events: none;
461
+ transition: opacity 0.3s;
462
+ z-index: 100;
463
+ }
464
+ .toast.show {
465
+ opacity: 1;
466
+ }
467
+
468
+ </style>
469
+ </head>
470
+ <body>
471
+
472
+ <!-- Video Player Background -->
473
+ <div id="video-layer">
474
+ <!-- Using a public domain video for demonstration purposes -->
475
+ <video id="main-player" loop muted playsinline poster="https://images.unsplash.com/photo-1517604931442-71053e3e2e3c?q=80&w=2070&auto=format&fit=crop">
476
+ <source src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4" type="video/mp4">
477
+ Your browser does not support the video tag.
478
+ </video>
479
+ <div class="loader" id="video-loader"></div>
480
+ </div>
481
+
482
+ <!-- Toast Notification -->
483
+ <div class="toast" id="toast">Channel Loaded</div>
484
+
485
+ <!-- UI Overlay -->
486
+ <div id="ui-layer">
487
+ <header>
488
+ <div class="brand">
489
+ <i class="fa-solid fa-tv"></i>
490
+ <span>TiviStream</span>
491
+ </div>
492
+ <div class="header-right">
493
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">Built with anycoder</a>
494
+ <div class="clock" id="clock">12:00 PM</div>
495
+ </div>
496
+ </header>
497
+
498
+ <div class="main-container">
499
+ <!-- Sidebar: Groups -->
500
+ <div class="sidebar">
501
+ <div class="sidebar-title">Groups</div>
502
+ <div class="group-item active" onclick="filterChannels('All')">
503
+ <span><i class="fa-solid fa-layer-group" style="width:20px"></i> All Channels</span>
504
+ <span class="badge">42</span>
505
+ </div>
506
+ <div class="group-item" onclick="filterChannels('Favorites')">
507
+ <span><i class="fa-solid fa-star" style="width:20px"></i> Favorites</span>
508
+ <span class="badge">5</span>
509
+ </div>
510
+ <div class="group-item" onclick="filterChannels('Sports')">
511
+ <span><i class="fa-solid fa-basketball" style="width:20px"></i> Sports</span>
512
+ <span class="badge">12</span>
513
+ </div>
514
+ <div class="group-item" onclick="filterChannels('Movies')">
515
+ <span><i class="fa-solid fa-film" style="width:20px"></i> Movies</span>
516
+ <span class="badge">8</span>
517
+ </div>
518
+ <div class="group-item" onclick="filterChannels('News')">
519
+ <span><i class="fa-solid fa-newspaper" style="width:20px"></i> News</span>
520
+ <span class="badge">6</span>
521
+ </div>
522
+ <div class="group-item" onclick="filterChannels('Kids')">
523
+ <span><i class="fa-solid fa-child" style="width:20px"></i> Kids</span>
524
+ <span class="badge">4</span>
525
+ </div>
526
+ <div class="group-item" onclick="filterChannels('Documentary')">
527
+ <span><i class="fa-solid fa-earth-americas" style="width:20px"></i> Documentary</span>
528
+ <span class="badge">7</span>
529
+ </div>
530
+ </div>
531
+
532
+ <!-- Channel List -->
533
+ <div class="channel-list-container">
534
+ <div class="epg-header">
535
+ <span>Current Guide</span>
536
+ <span class="epg-time-indicator" id="date-display">Mon, 01 Jan</span>
537
+ </div>
538
+ <div class="channel-list" id="channel-list">
539
+ <!-- Channels injected via JS -->
540
+ </div>
541
+ </div>
542
+ </div>
543
+
544
+ <!-- Info Panel -->
545
+ <div class="info-panel">
546
+ <div class="current-details">
547
+ <div class="meta-tags">
548
+ <span class="tag live">LIVE</span>
549
+ <span class="tag hd">HD</span>
550
+ <span class="tag">1080p</span>
551
+ </div>
552
+ <h2 id="info-title">Select a Channel</h2>
553
+ <div class="program-info-bar" style="margin-bottom:10px; opacity:0.8; font-size:0.9rem">
554
+ <span id="info-time">--:-- - --:--</span>
555
+ </div>
556
+ <p class="description" id="info-desc">
557
+ Browse the channel list to start watching. Use the sidebar to filter by category. Click on a channel to tune in.
558
+ </p>
559
+ </div>
560
+ <div class="player-controls">
561
+ <button class="btn-control" title="Add to Favorites"><i class="fa-regular fa-star"></i></button>
562
+ <button class="btn-control" title="EPG"><i class="fa-solid fa-list"></i></button>
563
+ <button class="btn-control" id="toggle-ui-btn" title="Hide UI (Press Esc)"><i class="fa-regular fa-eye-slash"></i></button>
564
+ <button class="btn-control btn-fullscreen" id="fullscreen-btn"><i class="fa-solid fa-expand"></i></button>
565
+ </div>
566
+ </div>
567
+ </div>
568
+
569
+ <script>
570
+ // --- Data Simulation ---
571
+ const categories = ['Sports', 'Movies', 'News', 'Kids', 'Documentary', 'Music', 'Entertainment'];
572
+ const channelNames = [
573
+ { name: "ESPN 1", cat: "Sports", icon: "fa-basketball" },
574
+ { name: "Sky Sports", cat: "Sports", icon: "fa-futbol" },
575
+ { name: "HBO", cat: "Movies", icon: "fa-film" },
576
+ { name: "Cinema+", cat: "Movies", icon: "fa-video" },
577
+ { name: "CNN", cat: "News", icon: "fa-newspaper" },
578
+ { name: "BBC World", cat: "News", icon: "fa-earth-europe" },
579
+ { name: "Disney Ch", cat: "Kids", icon: "fa-wand-magic-sparkles" },
580
+ { name: "Nat Geo", cat: "Documentary", icon: "fa-leaf" },
581
+ { name: "Discovery", cat: "Documentary", icon: "fa-binoculars" },
582
+ { name: "MTV Hits", cat: "Music", icon: "fa-music" },
583
+ { name: "Fox", cat: "Entertainment", icon: "fa-tv" },
584
+ { name: "ABC", cat: "Entertainment", icon: "fa-font" },
585
+ { name: "NBC", cat: "Entertainment", icon: "fa-feather" },
586
+ { name: "Cartoon Net", cat: "Kids", icon: "fa-ghost" },
587
+ { name: "Eurosport", cat: "Sports", icon: "fa-person-running" }
588
+ ];
589
+
590
+ const programs = {
591
+ 'Sports': ['Live: Premier League', 'NBA Highlights', 'F1 Grand Prix', 'SportsCenter', 'UFC Fight Night'],
592
+ 'Movies': ['The Dark Knight', 'Inception', 'Interstellar', 'Avengers: Endgame', 'Pulp Fiction', 'The Matrix'],
593
+ 'News': ['World News Now', 'The Daily Report', 'Breaking News', 'Finance Live', 'Morning Joe'],
594
+ 'Kids': ['SpongeBob', 'Bluey', 'Tom & Jerry', 'Peppa Pig', 'Paw Patrol'],
595
+ 'Documentary': ['Planet Earth III', 'Cosmos', 'Ancient Aliens', 'Shark Week', 'How It\'s Made'],
596
+ 'Music': ['Top 40 Hits', 'Classic Rock', 'Hip Hop Nation', 'Morning Vibes', 'K-Pop Countdown'],
597
+ 'Entertainment': ['Friends', 'The Office', 'Breaking Bad', 'Game of Thrones', 'Stranger Things']
598
+ };
599
+
600
+ // Generate Channel Data
601
+ let channels = [];
602
+ for(let i=0; i < 42; i++) {
603
+ let template = channelNames[i % channelNames.length];
604
+ // Randomize slightly to create variety
605
+ let cat = i < 15 ? template.cat : categories[Math.floor(Math.random() * categories.length)];
606
+
607
+ // Generate dummy current program times
608
+ let now = new Date();
609
+ let startOffset = Math.floor(Math.random() * 30) + 5; // Started 5-35 mins ago
610
+ let duration = Math.floor(Math.random() * 60) + 30; // Lasts 30-90 mins
611
+
612
+ let startTime = new Date(now.getTime() - startOffset * 60000);
613
+ let endTime = new Date(startTime.getTime() + duration * 60000);
614
+
615
+ let progList = programs[cat] || programs['Entertainment'];
616
+ let currentProg = progList[Math.floor(Math.random() * progList.length)];
617
+
618
+ channels.push({
619
+ id: i + 1,
620
+ number: 100 + i,
621
+ name: i < 15 ? template.name : `Channel ${100+i}`,
622
+ category: cat,
623
+ icon: i < 15 ? template.icon : "fa-tv",
624
+ currentProgram: currentProg,
625
+ start: startTime,
626
+ end: endTime,
627
+ description: `Watch ${currentProg} on ${i < 15 ? template.name : `Channel ${100+i}`}. High definition streaming provided by TiviStream.`,
628
+ isFavorite: Math.random() > 0.8
629
+ });
630
+ }
631
+
632
+ // --- State ---
633
+ let currentCategory = 'All';
634
+ let activeChannelId = null;
635
+ let uiVisible = true;
636
+
637
+ // --- DOM Elements ---
638
+ const channelListEl = document.getElementById('channel-list');
639
+ const clockEl = document.getElementById('clock');
640
+ const dateDisplayEl = document.getElementById('date-display');
641
+ const videoEl = document.getElementById('main-player');
642
+ const videoLoader = document.getElementById('video-loader');
643
+ const infoTitle = document.getElementById('info-title');
644
+ const infoTime = document.getElementById('info-time');
645
+ const infoDesc = document.getElementById('info-desc');
646
+ const toggleUiBtn = document.getElementById('toggle-ui-btn');
647
+ const fullscreenBtn = document.getElementById('fullscreen-btn');
648
+
649
+ // --- Functions ---
650
+
651
+ function formatTime(date) {
652
+ return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
653
+ }
654
+
655
+ function updateClock() {
656
+ const now = new Date();
657
+ clockEl.innerText = formatTime(now);
658
+ dateDisplayEl.innerText = now.toLocaleDateString([], { weekday: 'short', day: 'numeric', month: 'short' });
659
+
660
+ // Update progress bars
661
+ document.querySelectorAll('.channel-row').forEach(row => {
662
+ const id = parseInt(row.dataset.id);
663
+ const ch = channels.find(c => c.id === id);
664
+ if(ch) {
665
+ updateProgressBar(row.querySelector('.progress-bar'), ch);
666
+ }
667
+ });
668
+ }
669
+
670
+ function updateProgressBar(barEl, channel) {
671
+ const now = new Date();
672
+ const total = channel.end - channel.start;
673
+ const elapsed = now - channel.start;
674
+ let pct = (elapsed / total) * 100;
675
+ if (pct < 0) pct = 0;
676
+ if (pct > 100) pct = 100;
677
+ barEl.style.width = `${pct}%`;
678
+ }
679
+
680
+ function renderChannels(category) {
681
+ channelListEl.innerHTML = '';
682
+ const filtered = category === 'All' ? channels :
683
+ category === 'Favorites' ? channels.filter(c => c.isFavorite) :
684
+ channels.filter(c => c.category === category);
685
+
686
+ filtered.forEach(ch => {
687
+ const row = document.createElement('div');
688
+ row.className = `channel-row ${activeChannelId === ch.id ? 'active' : ''}`;
689
+ row.dataset.id = ch.id;
690
+ row.onclick = () => selectChannel(ch);
691
+
692
+ // Calculate Progress
693
+ const now = new Date();
694
+ // Reset times if expired (simulation logic)
695
+ if (now > ch.end) {
696
+ ch.start = now;
697
+ ch.end = new Date(now.getTime() + 45 * 60000);
698
+ }
699
+
700
+ row.innerHTML = `
701
+ <div class="ch-num">${ch.number}</div>
702
+ <div class="ch-logo"><i class="fa-solid ${ch.icon}"></i></div>
703
+ <div class="ch-name">${ch.name}</div>
704
+ <div class="program-info">
705
+ <div class="program-title">${ch.currentProgram}</div>
706
+ <div class="program-progress">
707
+ <div class="progress-bar" style="width: 0%"></div>
708
+ </div>
709
+ </div>
710
+ <div class="program-time">${formatTime(ch.start)}</div>
711
+ `;
712
+
713
+ // Init progress bar
714
+ updateProgressBar(row.querySelector('.progress-bar'), ch);
715
+
716
+ channelListEl.appendChild(row);
717
+ });
718
+ }
719
+
720
+ function filterChannels(category) {
721
+ currentCategory = category;
722
+ // Update Sidebar UI
723
+ document.querySelectorAll('.group-item').forEach(item => {
724
+ if(item.innerText.includes(category)) {
725
+ item.classList.add('active');
726
+ } else {
727
+ item.classList.remove('active');
728
+ }
729
+ // Handle "All Channels" vs "All" string match edge case
730
+ if(category === 'All' && item.innerText.includes('All Channels')) item.classList.add('active');
731
+ });
732
+ renderChannels(category);
733
+ }
734
+
735
+ function selectChannel(channel) {
736
+ if (activeChannelId === channel.id) return; // Already active
737
+
738
+ activeChannelId = channel.id;
739
+
740
+ // UI Updates
741
+ renderChannels(currentCategory); // Re-render to update active class
742
+
743
+ // Info Panel Update
744
+ infoTitle.innerText = `${channel.number} - ${channel.name}`;
745
+ infoTime.innerText = `${formatTime(channel.start)} - ${formatTime(channel.end)} • ${channel.currentProgram}`;
746
+ infoDesc.innerText = channel.description;
747
+
748
+ // Simulate Video Loading
749
+ videoEl.pause();
750
+ videoLoader.style.display = 'block';
751
+
752
+ showToast(`Tuning into ${channel.name}...`);
753
+
754
+ setTimeout(() => {
755
+ videoLoader.style.display = 'none';
756
+ videoEl.play();
757
+ // In a real app, we would switch the src here:
758
+ // videoEl.src = channel.streamUrl;
759
+ }, 1000);
760
+ }
761
+
762
+ function showToast(msg) {
763
+ const toast = document.getElementById('toast');
764
+ toast.innerText = msg;
765
+ toast.classList.add('show');
766
+ setTimeout(() => toast.classList.remove('show'), 3000);
767
+ }
768
+
769
+ function toggleUI() {
770
+ uiVisible = !uiVisible;
771
+ if(uiVisible) {
772
+ document.body.classList.remove('ui-hidden');
773
+ toggleUiBtn.innerHTML = '<i class="fa-regular fa-eye-slash"></i>';
774
+ } else {
775
+ document.body.classList.add('ui-hidden');
776
+ toggleUiBtn.innerHTML = '<i class="fa-regular fa-eye"></i>';
777
+ showToast("Press ESC to show menu");
778
+ }
779
+ }
780
+
781
+ function toggleFullscreen() {
782
+ if (!document.fullscreenElement) {
783
+ document.documentElement.requestFullscreen();
784
+ } else {
785
+ if (document.exitFullscreen) {
786
+ document.exitFullscreen();
787
+ }
788
+ }
789
+ }
790
+
791
+ // --- Event Listeners ---
792
+
793
+ toggleUiBtn.addEventListener('click', toggleUI);
794
+ fullscreenBtn.addEventListener('click', toggleFullscreen);
795
+
796
+ document.addEventListener('keydown', (e) => {
797
+ if (e.key === 'Escape') {
798
+ if (!uiVisible) toggleUI();
799
+ }
800
+ // Simple arrow key navigation logic could be added here
801
+ });
802
+
803
+ // --- Initialization ---
804
+ setInterval(updateClock, 1000); // Update clock every second
805
+ setInterval(updateClock, 30000); // Update progress bars aggressively
806
+ updateClock();
807
+ renderChannels('All');
808
+
809
+ // Auto-select first channel
810
+ if(channels.length > 0) {
811
+ // Don't trigger video load immediately on load to prevent auto-play policy issues
812
+ // Just set UI
813
+ activeChannelId = channels[0].id;
814
+ renderChannels('All');
815
+ infoTitle.innerText = `${channels[0].number} - ${channels[0].name}`;
816
+ infoTime.innerText = `${formatTime(channels[0].start)} - ${formatTime(channels[0].end)} • ${channels[0].currentProgram}`;
817
+ }
818
+
819
+ // Handle Video Play on Interaction (Browsers block auto-play with sound)
820
+ document.body.addEventListener('click', () => {
821
+ if(videoEl.paused) {
822
+ videoEl.muted = false; // Unmute on interaction
823
+ videoEl.play().catch(e => console.log("Audio play prevented"));
824
+ }
825
+ }, { once: true });
826
+
827
+ </script>
828
+ </body>
829
+ </html>