Guymy97 commited on
Commit
164ec2b
·
verified ·
1 Parent(s): b238bdb

Update via AnyCoder - streamlit_app.py

Browse files
Files changed (1) hide show
  1. streamlit_app.py +676 -0
streamlit_app.py ADDED
@@ -0,0 +1,676 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import streamlit.components.v1 as components
3
+
4
+ # --- Page Configuration ---
5
+ st.set_page_config(
6
+ page_title="TiviStream Pro",
7
+ page_icon="📺",
8
+ layout="wide",
9
+ initial_sidebar_state="collapsed"
10
+ )
11
+
12
+ # --- CSS to remove Streamlit clutter ---
13
+ st.markdown("""
14
+ <style>
15
+ /* Hide Streamlit header, footer, and hamburger menu */
16
+ #MainMenu {visibility: hidden;}
17
+ footer {visibility: hidden;}
18
+ header {visibility: hidden;}
19
+
20
+ /* Remove padding to allow full-screen iframe */
21
+ .block-container {
22
+ padding-top: 0rem;
23
+ padding-bottom: 0rem;
24
+ padding-left: 0rem;
25
+ padding-right: 0rem;
26
+ }
27
+
28
+ /* Ensure the iframe container takes full height */
29
+ iframe {
30
+ height: 100vh !important;
31
+ }
32
+ </style>
33
+ """, unsafe_allow_html=True)
34
+
35
+ # --- Header Link ---
36
+ st.markdown(
37
+ """
38
+ <div style="position: absolute; top: 10px; right: 10px; z-index: 9999;">
39
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" style="color: rgba(255,255,255,0.5); text-decoration: none; font-family: sans-serif; font-size: 12px; background: rgba(0,0,0,0.5); padding: 5px 10px; border-radius: 15px;">Built with anycoder</a>
40
+ </div>
41
+ """,
42
+ unsafe_allow_html=True
43
+ )
44
+
45
+ # --- Main Application Logic ---
46
+ # We embed the sophisticated HTML/JS app here.
47
+ # This provides a much smoother "TV-like" experience than standard Streamlit widgets.
48
+
49
+ html_code = """
50
+ <!DOCTYPE html>
51
+ <html lang="en">
52
+ <head>
53
+ <meta charset="UTF-8">
54
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
55
+ <title>TiviStream Pro</title>
56
+
57
+ <!-- Fonts & Icons -->
58
+ <link rel="preconnect" href="https://fonts.googleapis.com">
59
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
60
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
61
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
62
+
63
+ <style>
64
+ :root {
65
+ --bg-dark: #0b0d12;
66
+ --bg-glass: rgba(15, 23, 42, 0.85);
67
+ --bg-panel: rgba(30, 41, 59, 0.7);
68
+ --primary: #3b82f6;
69
+ --primary-hover: #2563eb;
70
+ --accent: #f59e0b;
71
+ --text-main: #f8fafc;
72
+ --text-muted: #94a3b8;
73
+ --border: rgba(255, 255, 255, 0.08);
74
+ --focus-bg: rgba(255, 255, 255, 0.1);
75
+ --transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
76
+ }
77
+
78
+ * {
79
+ box-sizing: border-box;
80
+ margin: 0;
81
+ padding: 0;
82
+ user-select: none;
83
+ -webkit-font-smoothing: antialiased;
84
+ }
85
+
86
+ body {
87
+ font-family: 'Inter', sans-serif;
88
+ background-color: #000;
89
+ color: var(--text-main);
90
+ height: 100vh;
91
+ width: 100vw;
92
+ overflow: hidden;
93
+ }
94
+
95
+ /* --- Layers --- */
96
+ #video-layer {
97
+ position: fixed;
98
+ top: 0;
99
+ left: 0;
100
+ width: 100%;
101
+ height: 100%;
102
+ z-index: 0;
103
+ }
104
+
105
+ video {
106
+ width: 100%;
107
+ height: 100%;
108
+ object-fit: cover;
109
+ transition: transform 0.3s ease;
110
+ }
111
+
112
+ #ui-layer {
113
+ position: relative;
114
+ z-index: 10;
115
+ height: 100%;
116
+ display: flex;
117
+ flex-direction: column;
118
+ background: linear-gradient(to right, rgba(0,0,0,0.9) 0%, rgba(0,0,0,0.7) 40%, rgba(0,0,0,0) 100%);
119
+ transition: opacity 0.3s ease;
120
+ }
121
+
122
+ /* --- Login Modal --- */
123
+ #login-modal {
124
+ position: fixed;
125
+ top: 0;
126
+ left: 0;
127
+ width: 100%;
128
+ height: 100%;
129
+ z-index: 100;
130
+ background: rgba(0,0,0,0.85);
131
+ backdrop-filter: blur(15px);
132
+ display: flex;
133
+ align-items: center;
134
+ justify-content: center;
135
+ transition: opacity 0.4s ease;
136
+ }
137
+
138
+ #login-modal.hidden {
139
+ opacity: 0;
140
+ pointer-events: none;
141
+ }
142
+
143
+ .login-container {
144
+ background: #1e293b;
145
+ width: 600px;
146
+ border-radius: 16px;
147
+ border: 1px solid var(--border);
148
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
149
+ overflow: hidden;
150
+ display: flex;
151
+ flex-direction: column;
152
+ }
153
+
154
+ .login-header {
155
+ padding: 20px 30px;
156
+ background: rgba(0,0,0,0.2);
157
+ border-bottom: 1px solid var(--border);
158
+ display: flex;
159
+ justify-content: space-between;
160
+ align-items: center;
161
+ }
162
+
163
+ .login-tabs {
164
+ display: flex;
165
+ background: rgba(0,0,0,0.3);
166
+ padding: 5px;
167
+ }
168
+
169
+ .tab-btn {
170
+ flex: 1;
171
+ padding: 15px;
172
+ background: transparent;
173
+ border: none;
174
+ color: var(--text-muted);
175
+ cursor: pointer;
176
+ font-weight: 600;
177
+ transition: var(--transition);
178
+ border-bottom: 2px solid transparent;
179
+ }
180
+
181
+ .tab-btn.active {
182
+ color: var(--primary);
183
+ border-bottom-color: var(--primary);
184
+ background: rgba(59, 130, 246, 0.05);
185
+ }
186
+
187
+ .login-body {
188
+ padding: 30px;
189
+ }
190
+
191
+ .input-group {
192
+ margin-bottom: 20px;
193
+ }
194
+
195
+ .input-group label {
196
+ display: block;
197
+ margin-bottom: 8px;
198
+ color: var(--text-muted);
199
+ font-size: 0.9rem;
200
+ }
201
+
202
+ .form-input {
203
+ width: 100%;
204
+ padding: 12px 15px;
205
+ background: rgba(0,0,0,0.3);
206
+ border: 1px solid var(--border);
207
+ border-radius: 8px;
208
+ color: white;
209
+ font-size: 1rem;
210
+ outline: none;
211
+ transition: var(--transition);
212
+ }
213
+
214
+ .form-input:focus {
215
+ border-color: var(--primary);
216
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
217
+ }
218
+
219
+ .btn-primary {
220
+ width: 100%;
221
+ padding: 14px;
222
+ background: var(--primary);
223
+ color: white;
224
+ border: none;
225
+ border-radius: 8px;
226
+ font-weight: 600;
227
+ font-size: 1rem;
228
+ cursor: pointer;
229
+ transition: var(--transition);
230
+ display: flex;
231
+ align-items: center;
232
+ justify-content: center;
233
+ gap: 10px;
234
+ }
235
+
236
+ .btn-primary:hover {
237
+ background: var(--primary-hover);
238
+ transform: translateY(-1px);
239
+ }
240
+
241
+ /* --- Main UI --- */
242
+ header {
243
+ padding: 1.5rem 2rem;
244
+ display: flex;
245
+ justify-content: space-between;
246
+ align-items: center;
247
+ }
248
+
249
+ .brand {
250
+ font-size: 1.5rem;
251
+ font-weight: 700;
252
+ color: white;
253
+ display: flex;
254
+ align-items: center;
255
+ gap: 12px;
256
+ }
257
+
258
+ .brand span {
259
+ background: linear-gradient(135deg, #fff 0%, #94a3b8 100%);
260
+ -webkit-background-clip: text;
261
+ -webkit-text-fill-color: transparent;
262
+ }
263
+
264
+ .top-meta {
265
+ display: flex;
266
+ gap: 20px;
267
+ align-items: center;
268
+ font-size: 0.9rem;
269
+ color: var(--text-muted);
270
+ }
271
+
272
+ .connection-badge {
273
+ background: rgba(16, 185, 129, 0.1);
274
+ color: #10b981;
275
+ padding: 4px 10px;
276
+ border-radius: 20px;
277
+ border: 1px solid rgba(16, 185, 129, 0.2);
278
+ font-size: 0.75rem;
279
+ font-weight: 600;
280
+ display: flex;
281
+ align-items: center;
282
+ gap: 6px;
283
+ }
284
+
285
+ .content-area {
286
+ display: flex;
287
+ flex: 1;
288
+ overflow: hidden;
289
+ padding-bottom: 20px;
290
+ }
291
+
292
+ /* Sidebar */
293
+ .sidebar {
294
+ width: 280px;
295
+ padding: 10px 0;
296
+ display: flex;
297
+ flex-direction: column;
298
+ border-right: 1px solid var(--border);
299
+ background: rgba(0,0,0,0.2);
300
+ }
301
+
302
+ .group-item {
303
+ padding: 12px 25px;
304
+ color: var(--text-muted);
305
+ cursor: pointer;
306
+ display: flex;
307
+ justify-content: space-between;
308
+ align-items: center;
309
+ transition: var(--transition);
310
+ border-left: 3px solid transparent;
311
+ }
312
+
313
+ .group-item:hover {
314
+ background: var(--focus-bg);
315
+ color: white;
316
+ }
317
+
318
+ .group-item.active {
319
+ background: linear-gradient(90deg, rgba(59, 130, 246, 0.1), transparent);
320
+ color: white;
321
+ border-left-color: var(--primary);
322
+ font-weight: 600;
323
+ }
324
+
325
+ /* EPG List */
326
+ .epg-container {
327
+ flex: 1;
328
+ display: flex;
329
+ flex-direction: column;
330
+ padding-left: 20px;
331
+ position: relative;
332
+ }
333
+
334
+ .channel-list {
335
+ overflow-y: auto;
336
+ padding-right: 20px;
337
+ mask-image: linear-gradient(to bottom, black 90%, transparent 100%);
338
+ }
339
+
340
+ .channel-row {
341
+ display: grid;
342
+ grid-template-columns: 60px 60px 200px 1fr 80px;
343
+ align-items: center;
344
+ padding: 12px 15px;
345
+ border-radius: 8px;
346
+ margin-bottom: 4px;
347
+ cursor: pointer;
348
+ border: 1px solid transparent;
349
+ transition: var(--transition);
350
+ }
351
+
352
+ .channel-row:hover {
353
+ background: var(--focus-bg);
354
+ }
355
+
356
+ .channel-row.active {
357
+ background: var(--primary);
358
+ border-color: rgba(255,255,255,0.2);
359
+ box-shadow: 0 4px 15px rgba(59, 130, 246, 0.3);
360
+ transform: scale(1.01);
361
+ }
362
+
363
+ .ch-logo {
364
+ width: 40px;
365
+ height: 30px;
366
+ background: white;
367
+ border-radius: 4px;
368
+ display: flex;
369
+ align-items: center;
370
+ justify-content: center;
371
+ color: #333;
372
+ font-size: 0.8rem;
373
+ }
374
+
375
+ .program-bar-container {
376
+ height: 4px;
377
+ background: rgba(255,255,255,0.15);
378
+ border-radius: 2px;
379
+ margin-top: 6px;
380
+ overflow: hidden;
381
+ }
382
+
383
+ .program-bar-fill {
384
+ height: 100%;
385
+ background: var(--text-muted);
386
+ width: 0%;
387
+ }
388
+
389
+ .channel-row.active .program-bar-fill {
390
+ background: white;
391
+ }
392
+
393
+ /* Info Panel (Bottom) */
394
+ .info-panel {
395
+ position: absolute;
396
+ bottom: 0;
397
+ left: 0;
398
+ width: 100%;
399
+ padding: 30px 40px;
400
+ background: linear-gradient(to top, #000 10%, rgba(0,0,0,0.8) 60%, transparent 100%);
401
+ display: flex;
402
+ align-items: flex-end;
403
+ justify-content: space-between;
404
+ }
405
+
406
+ .info-content h1 {
407
+ font-size: 2.5rem;
408
+ margin-bottom: 10px;
409
+ font-weight: 700;
410
+ text-shadow: 0 2px 4px rgba(0,0,0,0.5);
411
+ }
412
+
413
+ .info-meta {
414
+ display: flex;
415
+ gap: 15px;
416
+ margin-bottom: 15px;
417
+ }
418
+
419
+ .tag {
420
+ background: rgba(255,255,255,0.15);
421
+ padding: 4px 10px;
422
+ border-radius: 6px;
423
+ font-size: 0.8rem;
424
+ font-weight: 600;
425
+ backdrop-filter: blur(4px);
426
+ }
427
+
428
+ .tag.hd { background: #f59e0b; color: #000; }
429
+
430
+ .description {
431
+ max-width: 600px;
432
+ color: var(--text-muted);
433
+ line-height: 1.5;
434
+ font-size: 1rem;
435
+ text-shadow: 0 1px 2px rgba(0,0,0,0.8);
436
+ }
437
+
438
+ /* Utils */
439
+ ::-webkit-scrollbar { width: 6px; }
440
+ ::-webkit-scrollbar-track { background: transparent; }
441
+ ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); border-radius: 3px; }
442
+
443
+ .loader {
444
+ border: 3px solid rgba(255,255,255,0.1);
445
+ border-top: 3px solid var(--primary);
446
+ border-radius: 50%;
447
+ width: 24px;
448
+ height: 24px;
449
+ animation: spin 1s linear infinite;
450
+ display: none;
451
+ }
452
+
453
+ @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
454
+
455
+ .hidden { display: none !important; }
456
+
457
+ </style>
458
+ </head>
459
+ <body>
460
+
461
+ <!-- Video Layer -->
462
+ <div id="video-layer">
463
+ <video id="main-player" loop muted playsinline poster="https://images.unsplash.com/photo-1593784991095-a205069470b6?q=80&w=2070&auto=format&fit=crop">
464
+ <!-- Placeholder video -->
465
+ <source src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4" type="video/mp4">
466
+ </video>
467
+ </div>
468
+
469
+ <!-- Login / Connection Modal -->
470
+ <div id="login-modal">
471
+ <div class="login-container">
472
+ <div class="login-header">
473
+ <div class="brand" style="font-size: 1.2rem;">
474
+ <i class="fa-solid fa-tv"></i> <span>TiviStream Pro</span>
475
+ </div>
476
+ <div style="color: var(--text-muted); font-size: 0.9rem;">Connection Setup</div>
477
+ </div>
478
+
479
+ <div class="login-tabs">
480
+ <button class="tab-btn active" onclick="switchTab('m3u')">M3U Playlist</button>
481
+ <button class="tab-btn" onclick="switchTab('xtream')">Xtream Codes</button>
482
+ <button class="tab-btn" onclick="switchTab('mac')">Stalker MAC</button>
483
+ </div>
484
+
485
+ <div class="login-body">
486
+ <!-- M3U Form -->
487
+ <div id="form-m3u" class="auth-form">
488
+ <div class="input-group">
489
+ <label>Playlist Name</label>
490
+ <input type="text" class="form-input" placeholder="My TV List" value="Demo Playlist">
491
+ </div>
492
+ <div class="input-group">
493
+ <label>M3U URL</label>
494
+ <input type="text" class="form-input" placeholder="http://example.com/playlist.m3u" value="https://iptv-org.github.io/iptv/index.m3u">
495
+ </div>
496
+ </div>
497
+
498
+ <!-- Xtream Form -->
499
+ <div id="form-xtream" class="auth-form hidden">
500
+ <div class="input-group">
501
+ <label>Server URL</label>
502
+ <input type="text" class="form-input" placeholder="http://server-url.com:8080">
503
+ </div>
504
+ <div style="display: flex; gap: 15px;">
505
+ <div class="input-group" style="flex:1">
506
+ <label>Username</label>
507
+ <input type="text" class="form-input" placeholder="user">
508
+ </div>
509
+ <div class="input-group" style="flex:1">
510
+ <label>Password</label>
511
+ <input type="password" class="form-input" placeholder="pass">
512
+ </div>
513
+ </div>
514
+ </div>
515
+
516
+ <!-- MAC Form -->
517
+ <div id="form-mac" class="auth-form hidden">
518
+ <div class="input-group">
519
+ <label>Portal URL</label>
520
+ <input type="text" class="form-input" placeholder="http://mag.portal.com">
521
+ </div>
522
+ <div class="input-group">
523
+ <label>MAC Address</label>
524
+ <input type="text" class="form-input" placeholder="00:1A:79:XX:XX:XX" value="00:1A:79:00:00:01">
525
+ </div>
526
+ </div>
527
+
528
+ <button class="btn-primary" onclick="connectService()">
529
+ <span class="loader" id="btn-loader"></span>
530
+ <span id="btn-text">Connect Service</span>
531
+ </button>
532
+
533
+ <div style="margin-top: 15px; font-size: 0.8rem; color: var(--text-muted); text-align: center;">
534
+ <i class="fa-solid fa-lock"></i> Secure Connection •
535
+ <span style="color: var(--primary); cursor: pointer;" onclick="loadDemo()">Load Demo Mode</span>
536
+ </div>
537
+ </div>
538
+ </div>
539
+ </div>
540
+
541
+ <!-- Main UI Layer -->
542
+ <div id="ui-layer" class="hidden">
543
+ <header>
544
+ <div class="brand">
545
+ <i class="fa-solid fa-tv" style="color: var(--primary)"></i>
546
+ <span>TiviStream Pro</span>
547
+ </div>
548
+ <div class="top-meta">
549
+ <div class="connection-badge" id="conn-status">
550
+ <i class="fa-solid fa-circle" style="font-size: 6px;"></i> Live
551
+ </div>
552
+ <div id="clock">12:00 PM</div>
553
+ <i class="fa-solid fa-gear" style="cursor: pointer;" onclick="showSettings()"></i>
554
+ </div>
555
+ </header>
556
+
557
+ <div class="content-area">
558
+ <!-- Groups Sidebar -->
559
+ <div class="sidebar" id="group-list">
560
+ <!-- Groups injected via JS -->
561
+ </div>
562
+
563
+ <!-- EPG List -->
564
+ <div class="epg-container">
565
+ <div style="padding: 10px 15px; color: var(--text-muted); font-size: 0.85rem; border-bottom: 1px solid rgba(255,255,255,0.05); margin-bottom: 10px; display: flex; justify-content: space-between;">
566
+ <span>CHANNEL LIST</span>
567
+ <span id="date-display">Mon, Jan 1</span>
568
+ </div>
569
+ <div class="channel-list" id="channel-list">
570
+ <!-- Channels injected via JS -->
571
+ </div>
572
+ </div>
573
+ </div>
574
+
575
+ <!-- Bottom Info Panel -->
576
+ <div class="info-panel">
577
+ <div class="info-content">
578
+ <div class="info-meta">
579
+ <span class="tag hd">HD</span>
580
+ <span class="tag">LIVE</span>
581
+ <span class="tag" id="cat-tag">Sports</span>
582
+ </div>
583
+ <h1 id="info-title">Select Channel</h1>
584
+ <div style="color:white; font-weight:600; margin-bottom:8px; font-size: 1.1rem;">
585
+ <span id="prog-title">Current Program</span>
586
+ <span style="color: var(--text-muted); font-weight: 400; font-size: 0.9rem; margin-left: 10px;" id="prog-time">12:00 - 13:00</span>
587
+ </div>
588
+ <p class="description" id="info-desc">
589
+ Welcome to TiviStream Pro. Select a connection method to start, or use Demo Mode to preview the interface.
590
+ </p>
591
+ </div>
592
+ </div>
593
+ </div>
594
+
595
+ <script>
596
+ // --- Data Simulation ---
597
+ const categories = ['All Channels', 'Favorites', 'Sports', 'Movies', 'News', 'Kids', 'Documentary', 'Music', 'UHD 4K'];
598
+
599
+ const channelDB = [
600
+ { id: 1, num: 101, name: "Sky Sports Main", cat: "Sports", icon: "fa-futbol", prog: "Live: Man City vs Liverpool", desc: "Exclusive live coverage of the Premier League match." },
601
+ { id: 2, num: 102, name: "ESPN", cat: "Sports", icon: "fa-basketball", prog: "NBA: Lakers at Warriors", desc: "Live basketball action from the Crypto.com Arena." },
602
+ { id: 3, num: 103, name: "HBO", cat: "Movies", icon: "fa-film", prog: "Dune: Part Two", desc: "Paul Atreides unites with Chani and the Fremen while on a warpath of revenge." },
603
+ { id: 4, num: 104, name: "CNN International", cat: "News", icon: "fa-newspaper", prog: "Global News Hour", desc: "Breaking news and analysis from around the world." },
604
+ { id: 5, num: 105, name: "Nat Geo Wild", cat: "Documentary", icon: "fa-leaf", prog: "Savage Kingdom", desc: "The struggle for survival in the African savanna." },
605
+ { id: 6, num: 106, name: "Disney Channel", cat: "Kids", icon: "fa-shapes", prog: "Bluey", desc: "Bluey and Bingo play a game of magic statues." },
606
+ { id: 7, num: 107, name: "MTV Live", cat: "Music", icon: "fa-music", prog: "Top 20 Hits", desc: "Countdown of the hottest tracks right now." },
607
+ { id: 8, num: 108, name: "BBC One", cat: "News", icon: "fa-tv", prog: "BBC News at Six", desc: "The latest national and international news stories." },
608
+ { id: 9, num: 109, name: "Cinema Action", cat: "Movies", icon: "fa-video", prog: "John Wick 4", desc: "John Wick uncovers a path to defeating The High Table." },
609
+ { id: 10, num: 110, name: "Discovery Science", cat: "Documentary", icon: "fa-flask", prog: "How It's Made", desc: "Exploring how everyday objects are manufactured." }
610
+ ];
611
+
612
+ // Generate more dummy channels
613
+ for(let i=11; i<=50; i++) {
614
+ channelDB.push({
615
+ id: i,
616
+ num: 100 + i,
617
+ name: `Channel ${100+i}`,
618
+ cat: categories[Math.floor(Math.random() * (categories.length - 2)) + 2],
619
+ icon: "fa-tv",
620
+ prog: "Regular Programming",
621
+ desc: "High definition broadcast stream."
622
+ });
623
+ }
624
+
625
+ // --- State ---
626
+ let activeCategory = 'All Channels';
627
+ let activeChannelId = null;
628
+ let connectionType = 'm3u'; // m3u, xtream, mac
629
+
630
+ // --- DOM Elements ---
631
+ const modal = document.getElementById('login-modal');
632
+ const uiLayer = document.getElementById('ui-layer');
633
+ const videoEl = document.getElementById('main-player');
634
+
635
+ // --- Functions ---
636
+
637
+ function switchTab(type) {
638
+ connectionType = type;
639
+ document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
640
+ event.target.classList.add('active');
641
+
642
+ document.querySelectorAll('.auth-form').forEach(f => f.classList.add('hidden'));
643
+ document.getElementById(`form-${type}`).classList.remove('hidden');
644
+ }
645
+
646
+ function connectService() {
647
+ const btnText = document.getElementById('btn-text');
648
+ const loader = document.getElementById('btn-loader');
649
+
650
+ btnText.innerText = "Authenticating...";
651
+ loader.style.display = "inline-block";
652
+
653
+ // Simulate API Latency
654
+ setTimeout(() => {
655
+ loadInterface();
656
+ }, 1500);
657
+ }
658
+
659
+ function loadDemo() {
660
+ loadInterface();
661
+ }
662
+
663
+ function loadInterface() {
664
+ modal.classList.add('hidden');
665
+ uiLayer.classList.remove('hidden');
666
+
667
+ // Init UI
668
+ renderGroups();
669
+ renderChannels('All Channels');
670
+ startClock();
671
+
672
+ // Select first channel automatically
673
+ selectChannel(channelDB[0]);
674
+
675
+ // Update connection badge
676
+ const statusText = connectionType