AptlyDigital commited on
Commit
bc95524
·
verified ·
1 Parent(s): 17adb7d

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +2382 -2
index.html CHANGED
@@ -6,7 +6,1469 @@
6
  <title>Clarke Player Pro • 4K IPTV Player</title>
7
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
8
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
9
- <link rel="stylesheet" href="style.css">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  </head>
11
  <body>
12
  <div class="app-container">
@@ -371,6 +1833,924 @@
371
 
372
  <!-- Scripts -->
373
  <script src="https://cdn.jsdelivr.net/npm/hls.js@1.4.10/dist/hls.min.js"></script>
374
- <script src="app.js"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
375
  </body>
376
  </html>
 
6
  <title>Clarke Player Pro • 4K IPTV Player</title>
7
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
8
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
9
+ <style>
10
+ /* Clarke Player Pro - Complete CSS */
11
+ :root {
12
+ --primary: #8B5CF6;
13
+ --primary-dark: #7C3AED;
14
+ --primary-light: #A78BFA;
15
+ --accent: #10B981;
16
+ --accent-dark: #059669;
17
+ --danger: #EF4444;
18
+ --warning: #F59E0B;
19
+ --info: #3B82F6;
20
+
21
+ --bg-dark: #0F172A;
22
+ --bg-card: #1E293B;
23
+ --bg-panel: #334155;
24
+ --bg-surface: rgba(255, 255, 255, 0.05);
25
+
26
+ --text-primary: #F1F5F9;
27
+ --text-secondary: #94A3B8;
28
+ --text-muted: #64748B;
29
+
30
+ --border: rgba(255, 255, 255, 0.1);
31
+ --border-light: rgba(255, 255, 255, 0.05);
32
+
33
+ --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
34
+ --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
35
+ --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
36
+ --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
37
+
38
+ --gradient-primary: linear-gradient(135deg, var(--primary), var(--primary-dark));
39
+ --gradient-accent: linear-gradient(135deg, var(--accent), var(--accent-dark));
40
+ --gradient-dark: linear-gradient(135deg, var(--bg-dark), #1A1F35);
41
+
42
+ --glass-bg: rgba(255, 255, 255, 0.025);
43
+ --glass-border: rgba(255, 255, 255, 0.1);
44
+ --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
45
+
46
+ --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
47
+ --transition-slow: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
48
+ }
49
+
50
+ * {
51
+ margin: 0;
52
+ padding: 0;
53
+ box-sizing: border-box;
54
+ }
55
+
56
+ body {
57
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
58
+ background: var(--bg-dark);
59
+ color: var(--text-primary);
60
+ height: 100vh;
61
+ overflow: hidden;
62
+ font-weight: 400;
63
+ line-height: 1.5;
64
+ -webkit-font-smoothing: antialiased;
65
+ -moz-osx-font-smoothing: grayscale;
66
+ }
67
+
68
+ .app-container {
69
+ display: flex;
70
+ flex-direction: column;
71
+ height: 100vh;
72
+ position: relative;
73
+ overflow: hidden;
74
+ }
75
+
76
+ /* Animated Background */
77
+ .bg-animation {
78
+ position: fixed;
79
+ top: 0;
80
+ left: 0;
81
+ right: 0;
82
+ bottom: 0;
83
+ z-index: -1;
84
+ overflow: hidden;
85
+ }
86
+
87
+ .gradient-circle {
88
+ position: absolute;
89
+ border-radius: 50%;
90
+ filter: blur(80px);
91
+ opacity: 0.15;
92
+ animation: float 20s infinite ease-in-out;
93
+ }
94
+
95
+ .gradient-circle:nth-child(1) {
96
+ width: 600px;
97
+ height: 600px;
98
+ background: var(--primary);
99
+ top: -300px;
100
+ left: -300px;
101
+ animation-delay: 0s;
102
+ }
103
+
104
+ .gradient-circle:nth-child(2) {
105
+ width: 500px;
106
+ height: 500px;
107
+ background: var(--accent);
108
+ bottom: -250px;
109
+ right: -250px;
110
+ animation-delay: 5s;
111
+ }
112
+
113
+ .gradient-circle:nth-child(3) {
114
+ width: 400px;
115
+ height: 400px;
116
+ background: var(--info);
117
+ top: 50%;
118
+ left: 50%;
119
+ transform: translate(-50%, -50%);
120
+ animation-delay: 10s;
121
+ }
122
+
123
+ @keyframes float {
124
+ 0%, 100% { transform: translateY(0) scale(1); }
125
+ 50% { transform: translateY(-20px) scale(1.05); }
126
+ }
127
+
128
+ /* Main Header */
129
+ .main-header {
130
+ display: flex;
131
+ justify-content: space-between;
132
+ align-items: center;
133
+ padding: 1rem 2rem;
134
+ background: var(--glass-bg);
135
+ backdrop-filter: blur(20px);
136
+ -webkit-backdrop-filter: blur(20px);
137
+ border-bottom: 1px solid var(--glass-border);
138
+ z-index: 100;
139
+ box-shadow: var(--glass-shadow);
140
+ }
141
+
142
+ .header-left {
143
+ display: flex;
144
+ align-items: center;
145
+ gap: 2rem;
146
+ }
147
+
148
+ .logo {
149
+ display: flex;
150
+ align-items: center;
151
+ gap: 0.75rem;
152
+ }
153
+
154
+ .logo i {
155
+ font-size: 2rem;
156
+ color: var(--primary-light);
157
+ background: var(--gradient-primary);
158
+ -webkit-background-clip: text;
159
+ -webkit-text-fill-color: transparent;
160
+ background-clip: text;
161
+ }
162
+
163
+ .logo h1 {
164
+ font-size: 1.75rem;
165
+ font-weight: 700;
166
+ background: linear-gradient(135deg, var(--text-primary), var(--primary-light));
167
+ -webkit-background-clip: text;
168
+ -webkit-text-fill-color: transparent;
169
+ background-clip: text;
170
+ }
171
+
172
+ .logo h1 span {
173
+ color: var(--primary);
174
+ }
175
+
176
+ .logo sup {
177
+ font-size: 0.7rem;
178
+ background: var(--gradient-accent);
179
+ -webkit-background-clip: text;
180
+ -webkit-text-fill-color: transparent;
181
+ background-clip: text;
182
+ margin-left: 0.25rem;
183
+ font-weight: 600;
184
+ }
185
+
186
+ .player-status {
187
+ display: flex;
188
+ align-items: center;
189
+ gap: 0.5rem;
190
+ background: rgba(255, 255, 255, 0.05);
191
+ padding: 0.5rem 1rem;
192
+ border-radius: 20px;
193
+ border: 1px solid var(--border-light);
194
+ }
195
+
196
+ .status-indicator {
197
+ width: 8px;
198
+ height: 8px;
199
+ border-radius: 50%;
200
+ background: var(--accent);
201
+ animation: pulse 2s infinite;
202
+ }
203
+
204
+ @keyframes pulse {
205
+ 0% { opacity: 1; transform: scale(1); }
206
+ 50% { opacity: 0.5; transform: scale(1.1); }
207
+ 100% { opacity: 1; transform: scale(1); }
208
+ }
209
+
210
+ .header-right {
211
+ display: flex;
212
+ align-items: center;
213
+ gap: 1rem;
214
+ }
215
+
216
+ .ui-btn {
217
+ width: 40px;
218
+ height: 40px;
219
+ border-radius: 12px;
220
+ background: var(--glass-bg);
221
+ border: 1px solid var(--glass-border);
222
+ color: var(--text-secondary);
223
+ cursor: pointer;
224
+ display: flex;
225
+ align-items: center;
226
+ justify-content: center;
227
+ transition: var(--transition);
228
+ }
229
+
230
+ .ui-btn:hover {
231
+ background: rgba(255, 255, 255, 0.1);
232
+ color: var(--text-primary);
233
+ transform: translateY(-2px);
234
+ box-shadow: var(--shadow);
235
+ }
236
+
237
+ .user-profile .avatar {
238
+ width: 40px;
239
+ height: 40px;
240
+ border-radius: 12px;
241
+ background: var(--gradient-primary);
242
+ display: flex;
243
+ align-items: center;
244
+ justify-content: center;
245
+ font-weight: 600;
246
+ font-size: 0.9rem;
247
+ color: white;
248
+ cursor: pointer;
249
+ transition: var(--transition);
250
+ }
251
+
252
+ .user-profile .avatar:hover {
253
+ transform: scale(1.05);
254
+ box-shadow: var(--shadow);
255
+ }
256
+
257
+ /* Main Content */
258
+ .main-content {
259
+ display: grid;
260
+ grid-template-columns: 320px 1fr 320px;
261
+ gap: 1.5rem;
262
+ padding: 1.5rem;
263
+ flex: 1;
264
+ overflow: hidden;
265
+ }
266
+
267
+ @media (max-width: 1920px) {
268
+ .main-content {
269
+ grid-template-columns: 300px 1fr 300px;
270
+ }
271
+ }
272
+
273
+ /* Panel Cards */
274
+ .panel-card {
275
+ background: var(--glass-bg);
276
+ backdrop-filter: blur(20px);
277
+ -webkit-backdrop-filter: blur(20px);
278
+ border: 1px solid var(--glass-border);
279
+ border-radius: 20px;
280
+ padding: 1.5rem;
281
+ margin-bottom: 1.5rem;
282
+ box-shadow: var(--glass-shadow);
283
+ transition: var(--transition);
284
+ }
285
+
286
+ .panel-card:hover {
287
+ border-color: rgba(255, 255, 255, 0.15);
288
+ }
289
+
290
+ .panel-card h3 {
291
+ font-size: 1rem;
292
+ font-weight: 600;
293
+ color: var(--text-primary);
294
+ margin-bottom: 1.5rem;
295
+ display: flex;
296
+ align-items: center;
297
+ gap: 0.5rem;
298
+ }
299
+
300
+ .panel-card h3 i {
301
+ color: var(--primary-light);
302
+ }
303
+
304
+ /* Left Panel - Playlist Input */
305
+ .input-group {
306
+ margin-bottom: 1.5rem;
307
+ }
308
+
309
+ .input-group label {
310
+ display: block;
311
+ font-size: 0.875rem;
312
+ font-weight: 500;
313
+ color: var(--text-secondary);
314
+ margin-bottom: 0.5rem;
315
+ }
316
+
317
+ .input-with-icon {
318
+ position: relative;
319
+ }
320
+
321
+ .input-with-icon i {
322
+ position: absolute;
323
+ left: 1rem;
324
+ top: 50%;
325
+ transform: translateY(-50%);
326
+ color: var(--text-muted);
327
+ }
328
+
329
+ .input-with-icon input {
330
+ width: 100%;
331
+ padding: 0.875rem 1rem 0.875rem 2.75rem;
332
+ background: rgba(255, 255, 255, 0.05);
333
+ border: 1px solid var(--border);
334
+ border-radius: 12px;
335
+ color: var(--text-primary);
336
+ font-size: 0.875rem;
337
+ transition: var(--transition);
338
+ }
339
+
340
+ .input-with-icon input:focus {
341
+ outline: none;
342
+ border-color: var(--primary);
343
+ background: rgba(255, 255, 255, 0.08);
344
+ box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.1);
345
+ }
346
+
347
+ .file-upload-area {
348
+ border: 2px dashed var(--border);
349
+ border-radius: 12px;
350
+ padding: 2rem 1rem;
351
+ text-align: center;
352
+ cursor: pointer;
353
+ transition: var(--transition);
354
+ background: rgba(255, 255, 255, 0.02);
355
+ }
356
+
357
+ .file-upload-area:hover {
358
+ border-color: var(--primary);
359
+ background: rgba(255, 255, 255, 0.05);
360
+ }
361
+
362
+ .file-upload-area i {
363
+ font-size: 2rem;
364
+ color: var(--text-muted);
365
+ margin-bottom: 0.75rem;
366
+ }
367
+
368
+ .file-upload-area p {
369
+ font-size: 0.875rem;
370
+ color: var(--text-secondary);
371
+ margin: 0;
372
+ }
373
+
374
+ .quick-presets {
375
+ margin-bottom: 1.5rem;
376
+ }
377
+
378
+ .quick-presets h4 {
379
+ font-size: 0.875rem;
380
+ color: var(--text-secondary);
381
+ margin-bottom: 0.75rem;
382
+ }
383
+
384
+ .preset-buttons {
385
+ display: flex;
386
+ flex-direction: column;
387
+ gap: 0.5rem;
388
+ }
389
+
390
+ .preset-btn {
391
+ padding: 0.75rem 1rem;
392
+ background: rgba(255, 255, 255, 0.05);
393
+ border: 1px solid var(--border);
394
+ border-radius: 10px;
395
+ color: var(--text-secondary);
396
+ font-size: 0.875rem;
397
+ display: flex;
398
+ align-items: center;
399
+ gap: 0.5rem;
400
+ cursor: pointer;
401
+ transition: var(--transition);
402
+ }
403
+
404
+ .preset-btn:hover {
405
+ background: rgba(255, 255, 255, 0.1);
406
+ border-color: var(--primary);
407
+ color: var(--text-primary);
408
+ transform: translateX(4px);
409
+ }
410
+
411
+ .action-buttons {
412
+ display: flex;
413
+ gap: 1rem;
414
+ margin-bottom: 1.5rem;
415
+ }
416
+
417
+ .btn-primary, .btn-secondary {
418
+ flex: 1;
419
+ padding: 1rem;
420
+ border: none;
421
+ border-radius: 12px;
422
+ font-size: 0.875rem;
423
+ font-weight: 600;
424
+ cursor: pointer;
425
+ transition: var(--transition);
426
+ display: flex;
427
+ align-items: center;
428
+ justify-content: center;
429
+ gap: 0.5rem;
430
+ }
431
+
432
+ .btn-primary {
433
+ background: var(--gradient-primary);
434
+ color: white;
435
+ }
436
+
437
+ .btn-primary:hover {
438
+ transform: translateY(-2px);
439
+ box-shadow: 0 10px 20px rgba(139, 92, 246, 0.3);
440
+ }
441
+
442
+ .btn-secondary {
443
+ background: rgba(255, 255, 255, 0.05);
444
+ border: 1px solid var(--border);
445
+ color: var(--text-secondary);
446
+ }
447
+
448
+ .btn-secondary:hover {
449
+ background: rgba(255, 255, 255, 0.1);
450
+ color: var(--text-primary);
451
+ }
452
+
453
+ .playlist-info .info-grid {
454
+ display: grid;
455
+ grid-template-columns: repeat(3, 1fr);
456
+ gap: 1rem;
457
+ }
458
+
459
+ .info-item {
460
+ text-align: center;
461
+ }
462
+
463
+ .info-item span {
464
+ display: block;
465
+ font-size: 0.75rem;
466
+ color: var(--text-secondary);
467
+ margin-bottom: 0.25rem;
468
+ }
469
+
470
+ .info-item strong {
471
+ font-size: 1.25rem;
472
+ font-weight: 700;
473
+ color: var(--text-primary);
474
+ }
475
+
476
+ /* Center Panel - Video Player */
477
+ .center-panel {
478
+ display: flex;
479
+ flex-direction: column;
480
+ gap: 1.5rem;
481
+ }
482
+
483
+ .video-container {
484
+ position: relative;
485
+ background: #000;
486
+ border-radius: 20px;
487
+ overflow: hidden;
488
+ aspect-ratio: 16/9;
489
+ box-shadow: var(--shadow-xl);
490
+ }
491
+
492
+ #videoPlayer {
493
+ width: 100%;
494
+ height: 100%;
495
+ display: block;
496
+ outline: none;
497
+ }
498
+
499
+ .video-overlay {
500
+ position: absolute;
501
+ top: 0;
502
+ left: 0;
503
+ right: 0;
504
+ bottom: 0;
505
+ background: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.7));
506
+ display: flex;
507
+ align-items: flex-end;
508
+ padding: 2rem;
509
+ opacity: 0;
510
+ transition: var(--transition);
511
+ }
512
+
513
+ .video-container:hover .video-overlay {
514
+ opacity: 1;
515
+ }
516
+
517
+ .now-playing {
518
+ display: flex;
519
+ align-items: center;
520
+ gap: 1rem;
521
+ }
522
+
523
+ .channel-logo-large {
524
+ width: 60px;
525
+ height: 60px;
526
+ border-radius: 15px;
527
+ background: rgba(255, 255, 255, 0.1);
528
+ display: flex;
529
+ align-items: center;
530
+ justify-content: center;
531
+ font-size: 1.5rem;
532
+ color: var(--primary-light);
533
+ }
534
+
535
+ .now-playing-info h2 {
536
+ font-size: 1.5rem;
537
+ font-weight: 600;
538
+ margin-bottom: 0.25rem;
539
+ }
540
+
541
+ .now-playing-info p {
542
+ color: var(--text-secondary);
543
+ font-size: 0.875rem;
544
+ }
545
+
546
+ .player-controls {
547
+ position: absolute;
548
+ bottom: 2rem;
549
+ left: 50%;
550
+ transform: translateX(-50%);
551
+ display: flex;
552
+ align-items: center;
553
+ gap: 2rem;
554
+ background: rgba(0, 0, 0, 0.7);
555
+ backdrop-filter: blur(20px);
556
+ padding: 1rem 2rem;
557
+ border-radius: 50px;
558
+ border: 1px solid rgba(255, 255, 255, 0.1);
559
+ opacity: 0;
560
+ transition: var(--transition);
561
+ }
562
+
563
+ .video-container:hover .player-controls {
564
+ opacity: 1;
565
+ }
566
+
567
+ .control-group {
568
+ display: flex;
569
+ gap: 1rem;
570
+ }
571
+
572
+ .control-btn {
573
+ width: 48px;
574
+ height: 48px;
575
+ border-radius: 50%;
576
+ background: rgba(255, 255, 255, 0.1);
577
+ border: 1px solid rgba(255, 255, 255, 0.2);
578
+ color: white;
579
+ cursor: pointer;
580
+ display: flex;
581
+ align-items: center;
582
+ justify-content: center;
583
+ transition: var(--transition);
584
+ }
585
+
586
+ .control-btn:hover {
587
+ background: rgba(255, 255, 255, 0.2);
588
+ transform: scale(1.1);
589
+ }
590
+
591
+ .control-btn.play-btn {
592
+ background: var(--gradient-primary);
593
+ border: none;
594
+ }
595
+
596
+ .control-btn.play-btn:hover {
597
+ background: var(--primary-dark);
598
+ box-shadow: 0 0 20px rgba(139, 92, 246, 0.5);
599
+ }
600
+
601
+ .volume-control {
602
+ display: flex;
603
+ align-items: center;
604
+ gap: 0.75rem;
605
+ }
606
+
607
+ .volume-control i {
608
+ color: var(--text-secondary);
609
+ }
610
+
611
+ .volume-control input[type="range"] {
612
+ width: 100px;
613
+ height: 4px;
614
+ background: rgba(255, 255, 255, 0.1);
615
+ border-radius: 2px;
616
+ outline: none;
617
+ -webkit-appearance: none;
618
+ }
619
+
620
+ .volume-control input[type="range"]::-webkit-slider-thumb {
621
+ -webkit-appearance: none;
622
+ width: 16px;
623
+ height: 16px;
624
+ border-radius: 50%;
625
+ background: var(--primary);
626
+ cursor: pointer;
627
+ border: 2px solid white;
628
+ }
629
+
630
+ .loading-overlay {
631
+ position: absolute;
632
+ top: 0;
633
+ left: 0;
634
+ right: 0;
635
+ bottom: 0;
636
+ background: rgba(0, 0, 0, 0.8);
637
+ display: none;
638
+ flex-direction: column;
639
+ align-items: center;
640
+ justify-content: center;
641
+ gap: 1rem;
642
+ }
643
+
644
+ .loading-overlay .spinner {
645
+ width: 60px;
646
+ height: 60px;
647
+ border: 4px solid rgba(255, 255, 255, 0.1);
648
+ border-top-color: var(--primary);
649
+ border-radius: 50%;
650
+ animation: spin 1s linear infinite;
651
+ }
652
+
653
+ @keyframes spin {
654
+ to { transform: rotate(360deg); }
655
+ }
656
+
657
+ /* EPG Timeline */
658
+ .epg-timeline {
659
+ background: var(--glass-bg);
660
+ backdrop-filter: blur(20px);
661
+ border: 1px solid var(--glass-border);
662
+ border-radius: 15px;
663
+ padding: 1.25rem;
664
+ }
665
+
666
+ .timeline-header {
667
+ display: flex;
668
+ justify-content: space-between;
669
+ align-items: center;
670
+ margin-bottom: 1rem;
671
+ }
672
+
673
+ .timeline-header h4 {
674
+ font-size: 0.875rem;
675
+ font-weight: 600;
676
+ color: var(--text-primary);
677
+ display: flex;
678
+ align-items: center;
679
+ gap: 0.5rem;
680
+ }
681
+
682
+ .timeline-nav {
683
+ display: flex;
684
+ gap: 0.5rem;
685
+ }
686
+
687
+ .nav-btn {
688
+ width: 32px;
689
+ height: 32px;
690
+ border-radius: 8px;
691
+ background: rgba(255, 255, 255, 0.05);
692
+ border: 1px solid var(--border);
693
+ color: var(--text-secondary);
694
+ cursor: pointer;
695
+ display: flex;
696
+ align-items: center;
697
+ justify-content: center;
698
+ transition: var(--transition);
699
+ }
700
+
701
+ .nav-btn:hover {
702
+ background: rgba(255, 255, 255, 0.1);
703
+ color: var(--text-primary);
704
+ }
705
+
706
+ .timeline-content {
707
+ display: flex;
708
+ gap: 1rem;
709
+ overflow-x: auto;
710
+ padding-bottom: 0.5rem;
711
+ }
712
+
713
+ .timeline-content::-webkit-scrollbar {
714
+ height: 4px;
715
+ }
716
+
717
+ .timeline-content::-webkit-scrollbar-track {
718
+ background: rgba(255, 255, 255, 0.05);
719
+ border-radius: 2px;
720
+ }
721
+
722
+ .timeline-content::-webkit-scrollbar-thumb {
723
+ background: var(--primary);
724
+ border-radius: 2px;
725
+ }
726
+
727
+ .epg-item {
728
+ flex-shrink: 0;
729
+ padding: 0.75rem 1rem;
730
+ background: rgba(255, 255, 255, 0.05);
731
+ border-radius: 10px;
732
+ border: 1px solid transparent;
733
+ transition: var(--transition);
734
+ min-width: 180px;
735
+ }
736
+
737
+ .epg-item.active {
738
+ background: var(--gradient-primary);
739
+ border-color: var(--primary);
740
+ }
741
+
742
+ .epg-time {
743
+ font-size: 0.75rem;
744
+ font-weight: 600;
745
+ color: var(--text-secondary);
746
+ margin-bottom: 0.25rem;
747
+ }
748
+
749
+ .epg-item.active .epg-time {
750
+ color: rgba(255, 255, 255, 0.9);
751
+ }
752
+
753
+ .epg-show {
754
+ font-size: 0.875rem;
755
+ color: var(--text-primary);
756
+ }
757
+
758
+ .epg-item.active .epg-show {
759
+ color: white;
760
+ font-weight: 500;
761
+ }
762
+
763
+ /* Channels Grid */
764
+ .channels-grid {
765
+ background: var(--glass-bg);
766
+ backdrop-filter: blur(20px);
767
+ border: 1px solid var(--glass-border);
768
+ border-radius: 20px;
769
+ overflow: hidden;
770
+ flex: 1;
771
+ display: flex;
772
+ flex-direction: column;
773
+ }
774
+
775
+ .grid-header {
776
+ padding: 1.5rem;
777
+ border-bottom: 1px solid var(--border);
778
+ display: flex;
779
+ justify-content: space-between;
780
+ align-items: center;
781
+ }
782
+
783
+ .grid-header h3 {
784
+ font-size: 1rem;
785
+ font-weight: 600;
786
+ color: var(--text-primary);
787
+ display: flex;
788
+ align-items: center;
789
+ gap: 0.5rem;
790
+ }
791
+
792
+ .grid-controls {
793
+ display: flex;
794
+ align-items: center;
795
+ gap: 1rem;
796
+ }
797
+
798
+ .search-box {
799
+ display: flex;
800
+ align-items: center;
801
+ background: rgba(255, 255, 255, 0.05);
802
+ border: 1px solid var(--border);
803
+ border-radius: 12px;
804
+ padding: 0.5rem 0.75rem;
805
+ gap: 0.5rem;
806
+ }
807
+
808
+ .search-box i {
809
+ color: var(--text-muted);
810
+ font-size: 0.875rem;
811
+ }
812
+
813
+ .search-box input {
814
+ background: transparent;
815
+ border: none;
816
+ color: var(--text-primary);
817
+ font-size: 0.875rem;
818
+ width: 200px;
819
+ outline: none;
820
+ }
821
+
822
+ .search-box input::placeholder {
823
+ color: var(--text-muted);
824
+ }
825
+
826
+ .view-toggle {
827
+ display: flex;
828
+ gap: 0.25rem;
829
+ background: rgba(255, 255, 255, 0.05);
830
+ border: 1px solid var(--border);
831
+ border-radius: 12px;
832
+ padding: 0.25rem;
833
+ }
834
+
835
+ .view-btn {
836
+ width: 32px;
837
+ height: 32px;
838
+ border-radius: 8px;
839
+ background: transparent;
840
+ border: none;
841
+ color: var(--text-secondary);
842
+ cursor: pointer;
843
+ display: flex;
844
+ align-items: center;
845
+ justify-content: center;
846
+ transition: var(--transition);
847
+ }
848
+
849
+ .view-btn.active {
850
+ background: rgba(255, 255, 255, 0.1);
851
+ color: var(--text-primary);
852
+ }
853
+
854
+ .view-btn:hover:not(.active) {
855
+ background: rgba(255, 255, 255, 0.05);
856
+ }
857
+
858
+ .channels-container {
859
+ flex: 1;
860
+ padding: 1.5rem;
861
+ overflow-y: auto;
862
+ display: grid;
863
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
864
+ gap: 1rem;
865
+ align-content: start;
866
+ }
867
+
868
+ .channels-container::-webkit-scrollbar {
869
+ width: 8px;
870
+ }
871
+
872
+ .channels-container::-webkit-scrollbar-track {
873
+ background: rgba(255, 255, 255, 0.05);
874
+ border-radius: 4px;
875
+ }
876
+
877
+ .channels-container::-webkit-scrollbar-thumb {
878
+ background: var(--primary);
879
+ border-radius: 4px;
880
+ }
881
+
882
+ .channels-container::-webkit-scrollbar-thumb:hover {
883
+ background: var(--primary-dark);
884
+ }
885
+
886
+ .channel-card {
887
+ background: rgba(255, 255, 255, 0.03);
888
+ border: 1px solid var(--border);
889
+ border-radius: 15px;
890
+ padding: 1.25rem;
891
+ cursor: pointer;
892
+ transition: var(--transition);
893
+ position: relative;
894
+ overflow: hidden;
895
+ }
896
+
897
+ .channel-card::before {
898
+ content: '';
899
+ position: absolute;
900
+ top: 0;
901
+ left: 0;
902
+ right: 0;
903
+ height: 2px;
904
+ background: var(--gradient-primary);
905
+ transform: scaleX(0);
906
+ transition: var(--transition);
907
+ }
908
+
909
+ .channel-card:hover {
910
+ background: rgba(255, 255, 255, 0.06);
911
+ border-color: var(--primary);
912
+ transform: translateY(-4px);
913
+ box-shadow: var(--shadow-lg);
914
+ }
915
+
916
+ .channel-card:hover::before {
917
+ transform: scaleX(1);
918
+ }
919
+
920
+ .channel-card.active {
921
+ background: rgba(139, 92, 246, 0.1);
922
+ border-color: var(--primary);
923
+ }
924
+
925
+ .channel-card.active::before {
926
+ transform: scaleX(1);
927
+ }
928
+
929
+ .channel-logo {
930
+ width: 48px;
931
+ height: 48px;
932
+ border-radius: 12px;
933
+ background: rgba(255, 255, 255, 0.1);
934
+ display: flex;
935
+ align-items: center;
936
+ justify-content: center;
937
+ margin-bottom: 1rem;
938
+ font-size: 1.25rem;
939
+ color: var(--primary-light);
940
+ }
941
+
942
+ .channel-info h4 {
943
+ font-size: 0.875rem;
944
+ font-weight: 600;
945
+ color: var(--text-primary);
946
+ margin-bottom: 0.25rem;
947
+ white-space: nowrap;
948
+ overflow: hidden;
949
+ text-overflow: ellipsis;
950
+ }
951
+
952
+ .channel-meta {
953
+ display: flex;
954
+ justify-content: space-between;
955
+ align-items: center;
956
+ font-size: 0.75rem;
957
+ color: var(--text-secondary);
958
+ }
959
+
960
+ .channel-number {
961
+ font-weight: 600;
962
+ color: var(--primary-light);
963
+ }
964
+
965
+ .channel-fav {
966
+ position: absolute;
967
+ top: 1rem;
968
+ right: 1rem;
969
+ color: var(--text-muted);
970
+ cursor: pointer;
971
+ transition: var(--transition);
972
+ }
973
+
974
+ .channel-fav:hover {
975
+ color: #FFD700;
976
+ transform: scale(1.2);
977
+ }
978
+
979
+ .channel-fav.active {
980
+ color: #FFD700;
981
+ }
982
+
983
+ .welcome-state {
984
+ grid-column: 1 / -1;
985
+ display: flex;
986
+ flex-direction: column;
987
+ align-items: center;
988
+ justify-content: center;
989
+ padding: 3rem;
990
+ text-align: center;
991
+ color: var(--text-secondary);
992
+ }
993
+
994
+ .welcome-state i {
995
+ font-size: 4rem;
996
+ margin-bottom: 1.5rem;
997
+ color: var(--primary-light);
998
+ opacity: 0.5;
999
+ }
1000
+
1001
+ .welcome-state h3 {
1002
+ font-size: 1.5rem;
1003
+ color: var(--text-primary);
1004
+ margin-bottom: 0.5rem;
1005
+ }
1006
+
1007
+ /* Right Panel */
1008
+ .current-channel-info {
1009
+ margin-bottom: 1.5rem;
1010
+ }
1011
+
1012
+ .channel-display {
1013
+ display: flex;
1014
+ align-items: center;
1015
+ gap: 1rem;
1016
+ margin-bottom: 1rem;
1017
+ }
1018
+
1019
+ .channel-logo {
1020
+ width: 48px;
1021
+ height: 48px;
1022
+ border-radius: 12px;
1023
+ background: rgba(255, 255, 255, 0.1);
1024
+ display: flex;
1025
+ align-items: center;
1026
+ justify-content: center;
1027
+ font-size: 1.25rem;
1028
+ color: var(--primary-light);
1029
+ }
1030
+
1031
+ .channel-details {
1032
+ flex: 1;
1033
+ }
1034
+
1035
+ .channel-details h4 {
1036
+ font-size: 1rem;
1037
+ font-weight: 600;
1038
+ color: var(--text-primary);
1039
+ margin-bottom: 0.25rem;
1040
+ }
1041
+
1042
+ .channel-region {
1043
+ font-size: 0.75rem;
1044
+ color: var(--text-secondary);
1045
+ background: rgba(255, 255, 255, 0.05);
1046
+ padding: 0.25rem 0.5rem;
1047
+ border-radius: 6px;
1048
+ display: inline-block;
1049
+ }
1050
+
1051
+ .fav-btn {
1052
+ width: 40px;
1053
+ height: 40px;
1054
+ border-radius: 10px;
1055
+ background: rgba(255, 255, 255, 0.05);
1056
+ border: 1px solid var(--border);
1057
+ color: var(--text-muted);
1058
+ cursor: pointer;
1059
+ display: flex;
1060
+ align-items: center;
1061
+ justify-content: center;
1062
+ transition: var(--transition);
1063
+ }
1064
+
1065
+ .fav-btn:hover {
1066
+ background: rgba(255, 255, 255, 0.1);
1067
+ color: #FFD700;
1068
+ }
1069
+
1070
+ .fav-btn.active {
1071
+ color: #FFD700;
1072
+ border-color: rgba(255, 215, 0, 0.3);
1073
+ }
1074
+
1075
+ .quality-indicator {
1076
+ display: flex;
1077
+ justify-content: space-between;
1078
+ align-items: center;
1079
+ padding: 0.75rem;
1080
+ background: rgba(255, 255, 255, 0.03);
1081
+ border-radius: 10px;
1082
+ border: 1px solid var(--border);
1083
+ }
1084
+
1085
+ .quality-badge {
1086
+ padding: 0.25rem 0.75rem;
1087
+ background: var(--gradient-accent);
1088
+ color: white;
1089
+ font-size: 0.75rem;
1090
+ font-weight: 600;
1091
+ border-radius: 20px;
1092
+ }
1093
+
1094
+ .resolution {
1095
+ font-size: 0.75rem;
1096
+ color: var(--text-secondary);
1097
+ }
1098
+
1099
+ .program-info {
1100
+ margin-bottom: 1.5rem;
1101
+ }
1102
+
1103
+ .program-info h4 {
1104
+ font-size: 0.875rem;
1105
+ color: var(--text-secondary);
1106
+ margin-bottom: 0.75rem;
1107
+ }
1108
+
1109
+ .program-details p {
1110
+ font-size: 0.875rem;
1111
+ color: var(--text-primary);
1112
+ margin-bottom: 0.75rem;
1113
+ line-height: 1.5;
1114
+ }
1115
+
1116
+ .program-time {
1117
+ display: flex;
1118
+ align-items: center;
1119
+ gap: 0.75rem;
1120
+ }
1121
+
1122
+ .time-badge {
1123
+ padding: 0.25rem 0.5rem;
1124
+ background: var(--danger);
1125
+ color: white;
1126
+ font-size: 0.75rem;
1127
+ font-weight: 600;
1128
+ border-radius: 6px;
1129
+ }
1130
+
1131
+ .duration {
1132
+ font-size: 0.75rem;
1133
+ color: var(--text-secondary);
1134
+ }
1135
+
1136
+ .audio-tracks h4 {
1137
+ font-size: 0.875rem;
1138
+ color: var(--text-secondary);
1139
+ margin-bottom: 0.75rem;
1140
+ }
1141
+
1142
+ .track-list {
1143
+ display: flex;
1144
+ flex-direction: column;
1145
+ gap: 0.5rem;
1146
+ }
1147
+
1148
+ .track-item {
1149
+ padding: 0.75rem;
1150
+ background: rgba(255, 255, 255, 0.03);
1151
+ border: 1px solid var(--border);
1152
+ border-radius: 10px;
1153
+ display: flex;
1154
+ align-items: center;
1155
+ gap: 0.75rem;
1156
+ font-size: 0.875rem;
1157
+ color: var(--text-secondary);
1158
+ cursor: pointer;
1159
+ transition: var(--transition);
1160
+ }
1161
+
1162
+ .track-item:hover {
1163
+ background: rgba(255, 255, 255, 0.06);
1164
+ border-color: var(--primary);
1165
+ }
1166
+
1167
+ .track-item.active {
1168
+ background: rgba(139, 92, 246, 0.1);
1169
+ border-color: var(--primary);
1170
+ color: var(--text-primary);
1171
+ }
1172
+
1173
+ .filter-group {
1174
+ display: flex;
1175
+ flex-direction: column;
1176
+ gap: 1rem;
1177
+ }
1178
+
1179
+ .filter-item label {
1180
+ display: block;
1181
+ font-size: 0.875rem;
1182
+ color: var(--text-secondary);
1183
+ margin-bottom: 0.5rem;
1184
+ }
1185
+
1186
+ .filter-item select {
1187
+ width: 100%;
1188
+ padding: 0.75rem;
1189
+ background: rgba(255, 255, 255, 0.05);
1190
+ border: 1px solid var(--border);
1191
+ border-radius: 10px;
1192
+ color: var(--text-primary);
1193
+ font-size: 0.875rem;
1194
+ cursor: pointer;
1195
+ transition: var(--transition);
1196
+ }
1197
+
1198
+ .filter-item select:focus {
1199
+ outline: none;
1200
+ border-color: var(--primary);
1201
+ background: rgba(255, 255, 255, 0.08);
1202
+ }
1203
+
1204
+ .quality-filter {
1205
+ display: flex;
1206
+ gap: 1rem;
1207
+ }
1208
+
1209
+ .checkbox-label {
1210
+ display: flex;
1211
+ align-items: center;
1212
+ gap: 0.5rem;
1213
+ font-size: 0.875rem;
1214
+ color: var(--text-secondary);
1215
+ cursor: pointer;
1216
+ transition: var(--transition);
1217
+ }
1218
+
1219
+ .checkbox-label input[type="checkbox"] {
1220
+ width: 16px;
1221
+ height: 16px;
1222
+ border-radius: 4px;
1223
+ border: 1px solid var(--border);
1224
+ background: rgba(255, 255, 255, 0.05);
1225
+ cursor: pointer;
1226
+ transition: var(--transition);
1227
+ }
1228
+
1229
+ .checkbox-label input[type="checkbox"]:checked {
1230
+ background: var(--primary);
1231
+ border-color: var(--primary);
1232
+ }
1233
+
1234
+ .checkbox-label:hover {
1235
+ color: var(--text-primary);
1236
+ }
1237
+
1238
+ .sort-buttons {
1239
+ display: flex;
1240
+ gap: 0.5rem;
1241
+ }
1242
+
1243
+ .sort-btn {
1244
+ flex: 1;
1245
+ padding: 0.5rem;
1246
+ background: rgba(255, 255, 255, 0.05);
1247
+ border: 1px solid var(--border);
1248
+ border-radius: 8px;
1249
+ color: var(--text-secondary);
1250
+ font-size: 0.75rem;
1251
+ cursor: pointer;
1252
+ transition: var(--transition);
1253
+ }
1254
+
1255
+ .sort-btn.active {
1256
+ background: rgba(139, 92, 246, 0.1);
1257
+ border-color: var(--primary);
1258
+ color: var(--text-primary);
1259
+ }
1260
+
1261
+ .sort-btn:hover:not(.active) {
1262
+ background: rgba(255, 255, 255, 0.08);
1263
+ }
1264
+
1265
+ .stats-grid {
1266
+ display: grid;
1267
+ grid-template-columns: repeat(3, 1fr);
1268
+ gap: 1rem;
1269
+ }
1270
+
1271
+ .stat-item {
1272
+ text-align: center;
1273
+ padding: 1rem;
1274
+ background: rgba(255, 255, 255, 0.03);
1275
+ border-radius: 12px;
1276
+ border: 1px solid var(--border);
1277
+ }
1278
+
1279
+ .stat-icon {
1280
+ width: 40px;
1281
+ height: 40px;
1282
+ border-radius: 10px;
1283
+ background: rgba(139, 92, 246, 0.1);
1284
+ display: flex;
1285
+ align-items: center;
1286
+ justify-content: center;
1287
+ margin: 0 auto 0.75rem;
1288
+ color: var(--primary-light);
1289
+ font-size: 1.25rem;
1290
+ }
1291
+
1292
+ .stat-info span {
1293
+ display: block;
1294
+ font-size: 0.75rem;
1295
+ color: var(--text-secondary);
1296
+ margin-bottom: 0.25rem;
1297
+ }
1298
+
1299
+ .stat-info strong {
1300
+ font-size: 1.25rem;
1301
+ font-weight: 700;
1302
+ color: var(--text-primary);
1303
+ }
1304
+
1305
+ /* Bottom Bar */
1306
+ .bottom-bar {
1307
+ background: var(--glass-bg);
1308
+ backdrop-filter: blur(20px);
1309
+ border-top: 1px solid var(--glass-border);
1310
+ padding: 0.75rem 2rem;
1311
+ box-shadow: var(--glass-shadow);
1312
+ }
1313
+
1314
+ .progress-bar {
1315
+ width: 100%;
1316
+ height: 3px;
1317
+ background: rgba(255, 255, 255, 0.05);
1318
+ border-radius: 2px;
1319
+ margin-bottom: 0.75rem;
1320
+ overflow: hidden;
1321
+ }
1322
+
1323
+ .progress-fill {
1324
+ height: 100%;
1325
+ background: var(--gradient-primary);
1326
+ width: 0%;
1327
+ transition: width 0.3s ease;
1328
+ }
1329
+
1330
+ .footer-content {
1331
+ display: flex;
1332
+ justify-content: space-between;
1333
+ align-items: center;
1334
+ }
1335
+
1336
+ .playback-info {
1337
+ font-size: 0.875rem;
1338
+ color: var(--text-secondary);
1339
+ display: flex;
1340
+ align-items: center;
1341
+ gap: 0.25rem;
1342
+ }
1343
+
1344
+ .separator {
1345
+ color: var(--text-muted);
1346
+ }
1347
+
1348
+ .shortcuts-info {
1349
+ display: flex;
1350
+ gap: 1.5rem;
1351
+ }
1352
+
1353
+ .shortcut-item {
1354
+ display: flex;
1355
+ align-items: center;
1356
+ gap: 0.5rem;
1357
+ }
1358
+
1359
+ .shortcut-item kbd {
1360
+ padding: 0.25rem 0.5rem;
1361
+ background: rgba(255, 255, 255, 0.1);
1362
+ border: 1px solid var(--border);
1363
+ border-radius: 6px;
1364
+ font-family: 'Inter', monospace;
1365
+ font-size: 0.75rem;
1366
+ color: var(--text-primary);
1367
+ min-width: 36px;
1368
+ text-align: center;
1369
+ }
1370
+
1371
+ .shortcut-item span {
1372
+ font-size: 0.75rem;
1373
+ color: var(--text-secondary);
1374
+ }
1375
+
1376
+ .version-info {
1377
+ font-size: 0.75rem;
1378
+ color: var(--text-muted);
1379
+ opacity: 0.7;
1380
+ }
1381
+
1382
+ /* Favorites Section */
1383
+ .favorites-list {
1384
+ display: flex;
1385
+ flex-direction: column;
1386
+ gap: 0.5rem;
1387
+ max-height: 200px;
1388
+ overflow-y: auto;
1389
+ }
1390
+
1391
+ .favorites-list::-webkit-scrollbar {
1392
+ width: 4px;
1393
+ }
1394
+
1395
+ .favorites-list::-webkit-scrollbar-track {
1396
+ background: rgba(255, 255, 255, 0.05);
1397
+ }
1398
+
1399
+ .favorites-list::-webkit-scrollbar-thumb {
1400
+ background: var(--primary);
1401
+ border-radius: 2px;
1402
+ }
1403
+
1404
+ .favorite-item {
1405
+ display: flex;
1406
+ align-items: center;
1407
+ gap: 0.75rem;
1408
+ padding: 0.75rem;
1409
+ background: rgba(255, 255, 255, 0.03);
1410
+ border: 1px solid var(--border);
1411
+ border-radius: 10px;
1412
+ cursor: pointer;
1413
+ transition: var(--transition);
1414
+ }
1415
+
1416
+ .favorite-item:hover {
1417
+ background: rgba(255, 255, 255, 0.06);
1418
+ border-color: var(--primary);
1419
+ }
1420
+
1421
+ .favorite-item i {
1422
+ color: #FFD700;
1423
+ font-size: 0.875rem;
1424
+ }
1425
+
1426
+ .favorite-item span {
1427
+ font-size: 0.875rem;
1428
+ color: var(--text-primary);
1429
+ flex: 1;
1430
+ }
1431
+
1432
+ .empty-favorites {
1433
+ text-align: center;
1434
+ padding: 2rem 1rem;
1435
+ color: var(--text-secondary);
1436
+ }
1437
+
1438
+ .empty-favorites i {
1439
+ font-size: 2rem;
1440
+ margin-bottom: 0.75rem;
1441
+ opacity: 0.5;
1442
+ }
1443
+
1444
+ .empty-favorites p {
1445
+ font-size: 0.875rem;
1446
+ }
1447
+
1448
+ /* Responsive */
1449
+ @media (max-width: 1600px) {
1450
+ .main-content {
1451
+ grid-template-columns: 280px 1fr 280px;
1452
+ }
1453
+ }
1454
+
1455
+ @media (max-width: 1366px) {
1456
+ .main-content {
1457
+ grid-template-columns: 260px 1fr 260px;
1458
+ }
1459
+ }
1460
+
1461
+ @media (max-width: 1200px) {
1462
+ .main-content {
1463
+ grid-template-columns: 1fr;
1464
+ grid-template-rows: auto 1fr auto;
1465
+ }
1466
+
1467
+ .left-panel, .right-panel {
1468
+ display: none;
1469
+ }
1470
+ }
1471
+ </style>
1472
  </head>
1473
  <body>
1474
  <div class="app-container">
 
1833
 
1834
  <!-- Scripts -->
1835
  <script src="https://cdn.jsdelivr.net/npm/hls.js@1.4.10/dist/hls.min.js"></script>
1836
+ <script>
1837
+ // Clarke Player Pro - Complete JavaScript
1838
+ class ClarkePlayerPro {
1839
+ constructor() {
1840
+ this.state = {
1841
+ playlists: [],
1842
+ currentPlaylist: null,
1843
+ channels: [],
1844
+ filteredChannels: [],
1845
+ favorites: JSON.parse(localStorage.getItem('clarke_favs')) || {},
1846
+ currentChannel: null,
1847
+ hls: null,
1848
+ isPlaying: false,
1849
+ quality: 'Auto',
1850
+ searchQuery: '',
1851
+ filterCategory: 'all'
1852
+ };
1853
+
1854
+ this.elements = {
1855
+ video: document.getElementById('videoPlayer'),
1856
+ playlistUrl: document.getElementById('playlistUrl'),
1857
+ fileInput: document.getElementById('fileInput'),
1858
+ uploadArea: document.getElementById('uploadArea'),
1859
+ loadPlaylistBtn: document.getElementById('loadPlaylistBtn'),
1860
+ clearBtn: document.getElementById('clearBtn'),
1861
+ channelsContainer: document.getElementById('channelsContainer'),
1862
+ currentChannel: document.getElementById('currentChannel'),
1863
+ currentProgram: document.getElementById('currentProgram'),
1864
+ nowPlayingChannel: document.getElementById('nowPlayingChannel'),
1865
+ nowPlayingRegion: document.getElementById('nowPlayingRegion'),
1866
+ statusText: document.getElementById('statusText'),
1867
+ channelCount: document.getElementById('channelCount'),
1868
+ lastUpdate: document.getElementById('lastUpdate'),
1869
+ avgQuality: document.getElementById('avgQuality'),
1870
+ loadingOverlay: document.getElementById('loadingOverlay'),
1871
+ channelSearch: document.getElementById('channelSearch'),
1872
+ toggleFav: document.getElementById('toggleFav'),
1873
+ playPauseBtn: document.getElementById('playPauseBtn'),
1874
+ prevChannel: document.getElementById('prevChannel'),
1875
+ nextChannel: document.getElementById('nextChannel'),
1876
+ volumeSlider: document.getElementById('volumeSlider'),
1877
+ favoritesList: document.getElementById('favoritesList'),
1878
+ categoryFilter: document.getElementById('categoryFilter'),
1879
+ fullscreenBtn: document.getElementById('fullscreenBtn'),
1880
+ settingsBtn: document.getElementById('settingsBtn'),
1881
+ bitrateStat: document.getElementById('bitrateStat'),
1882
+ bufferStat: document.getElementById('bufferStat'),
1883
+ healthStat: document.getElementById('healthStat'),
1884
+ qualityBadge: document.getElementById('qualityBadge'),
1885
+ bufferProgress: document.getElementById('bufferProgress'),
1886
+ currentTime: document.getElementById('currentTime'),
1887
+ totalTime: document.getElementById('totalTime')
1888
+ };
1889
+
1890
+ this.init();
1891
+ }
1892
+
1893
+ init() {
1894
+ console.log('🚀 Clarke Player Pro v2.1 Initializing...');
1895
+
1896
+ this.setupEventListeners();
1897
+ this.setupKeyboardControls();
1898
+ this.loadDefaultPlaylist();
1899
+ this.updateStats();
1900
+ this.renderFavorites();
1901
+
1902
+ // Set initial volume
1903
+ this.elements.video.volume = this.elements.volumeSlider.value / 100;
1904
+ }
1905
+
1906
+ setupEventListeners() {
1907
+ // Load playlist from URL
1908
+ this.elements.loadPlaylistBtn.addEventListener('click', () => {
1909
+ this.loadPlaylistFromUrl();
1910
+ });
1911
+
1912
+ // Quick preset buttons
1913
+ document.querySelectorAll('.preset-btn').forEach(btn => {
1914
+ btn.addEventListener('click', (e) => {
1915
+ const url = e.currentTarget.dataset.url;
1916
+ this.elements.playlistUrl.value = url;
1917
+ this.loadPlaylistFromUrl();
1918
+ });
1919
+ });
1920
+
1921
+ // File upload
1922
+ this.elements.uploadArea.addEventListener('click', () => {
1923
+ this.elements.fileInput.click();
1924
+ });
1925
+
1926
+ this.elements.fileInput.addEventListener('change', (e) => {
1927
+ if (e.target.files.length > 0) {
1928
+ this.loadPlaylistFromFile(e.target.files[0]);
1929
+ }
1930
+ });
1931
+
1932
+ // Drag and drop for file upload
1933
+ this.elements.uploadArea.addEventListener('dragover', (e) => {
1934
+ e.preventDefault();
1935
+ e.currentTarget.style.borderColor = 'var(--primary)';
1936
+ e.currentTarget.style.background = 'rgba(139, 92, 246, 0.1)';
1937
+ });
1938
+
1939
+ this.elements.uploadArea.addEventListener('dragleave', (e) => {
1940
+ e.currentTarget.style.borderColor = 'var(--border)';
1941
+ e.currentTarget.style.background = 'rgba(255, 255, 255, 0.02)';
1942
+ });
1943
+
1944
+ this.elements.uploadArea.addEventListener('drop', (e) => {
1945
+ e.preventDefault();
1946
+ e.currentTarget.style.borderColor = 'var(--border)';
1947
+ e.currentTarget.style.background = 'rgba(255, 255, 255, 0.02)';
1948
+
1949
+ const file = e.dataTransfer.files[0];
1950
+ if (file && (file.name.endsWith('.m3u') || file.name.endsWith('.m3u8'))) {
1951
+ this.loadPlaylistFromFile(file);
1952
+ } else {
1953
+ this.showNotification('Please drop a valid .m3u or .m3u8 file', 'error');
1954
+ }
1955
+ });
1956
+
1957
+ // Clear button
1958
+ this.elements.clearBtn.addEventListener('click', () => {
1959
+ this.clearPlaylist();
1960
+ });
1961
+
1962
+ // Search
1963
+ this.elements.channelSearch.addEventListener('input', (e) => {
1964
+ this.state.searchQuery = e.target.value.toLowerCase();
1965
+ this.filterChannels();
1966
+ });
1967
+
1968
+ // Category filter
1969
+ this.elements.categoryFilter.addEventListener('change', (e) => {
1970
+ this.state.filterCategory = e.target.value;
1971
+ this.filterChannels();
1972
+ });
1973
+
1974
+ // Player controls
1975
+ this.elements.playPauseBtn.addEventListener('click', () => {
1976
+ this.togglePlayPause();
1977
+ });
1978
+
1979
+ this.elements.prevChannel.addEventListener('click', () => {
1980
+ this.selectPreviousChannel();
1981
+ });
1982
+
1983
+ this.elements.nextChannel.addEventListener('click', () => {
1984
+ this.selectNextChannel();
1985
+ });
1986
+
1987
+ this.elements.toggleFav.addEventListener('click', () => {
1988
+ if (this.state.currentChannel) {
1989
+ this.toggleFavorite(this.state.currentChannel.id);
1990
+ }
1991
+ });
1992
+
1993
+ // Volume control
1994
+ this.elements.volumeSlider.addEventListener('input', (e) => {
1995
+ this.elements.video.volume = e.target.value / 100;
1996
+ });
1997
+
1998
+ // Fullscreen
1999
+ this.elements.fullscreenBtn.addEventListener('click', () => {
2000
+ this.toggleFullscreen();
2001
+ });
2002
+
2003
+ // Video events
2004
+ this.elements.video.addEventListener('play', () => {
2005
+ this.state.isPlaying = true;
2006
+ this.elements.playPauseBtn.innerHTML = '<i class="fas fa-pause"></i>';
2007
+ this.updateStatus('Playing');
2008
+ });
2009
+
2010
+ this.elements.video.addEventListener('pause', () => {
2011
+ this.state.isPlaying = false;
2012
+ this.elements.playPauseBtn.innerHTML = '<i class="fas fa-play"></i>';
2013
+ this.updateStatus('Paused');
2014
+ });
2015
+
2016
+ this.elements.video.addEventListener('waiting', () => {
2017
+ this.showLoading(true);
2018
+ this.updateStatus('Buffering...');
2019
+ });
2020
+
2021
+ this.elements.video.addEventListener('playing', () => {
2022
+ this.showLoading(false);
2023
+ this.updateStatus('Playing');
2024
+ });
2025
+
2026
+ this.elements.video.addEventListener('timeupdate', () => {
2027
+ this.updatePlaybackInfo();
2028
+ });
2029
+
2030
+ this.elements.video.addEventListener('loadedmetadata', () => {
2031
+ this.updatePlaybackInfo();
2032
+ });
2033
+
2034
+ // Auto-hide controls
2035
+ let controlsTimeout;
2036
+ this.elements.video.addEventListener('mousemove', () => {
2037
+ const overlay = document.querySelector('.video-overlay');
2038
+ const controls = document.querySelector('.player-controls');
2039
+
2040
+ overlay.style.opacity = '1';
2041
+ controls.style.opacity = '1';
2042
+
2043
+ clearTimeout(controlsTimeout);
2044
+ controlsTimeout = setTimeout(() => {
2045
+ if (!document.fullscreenElement) {
2046
+ overlay.style.opacity = '0';
2047
+ controls.style.opacity = '0';
2048
+ }
2049
+ }, 3000);
2050
+ });
2051
+ }
2052
+
2053
+ setupKeyboardControls() {
2054
+ document.addEventListener('keydown', (e) => {
2055
+ if (e.target.tagName === 'INPUT') return;
2056
+
2057
+ switch (e.key.toLowerCase()) {
2058
+ case ' ':
2059
+ e.preventDefault();
2060
+ this.togglePlayPause();
2061
+ break;
2062
+
2063
+ case 'arrowleft':
2064
+ e.preventDefault();
2065
+ this.seek(-10);
2066
+ break;
2067
+
2068
+ case 'arrowright':
2069
+ e.preventDefault();
2070
+ this.seek(10);
2071
+ break;
2072
+
2073
+ case 'arrowup':
2074
+ e.preventDefault();
2075
+ this.selectPreviousChannel();
2076
+ break;
2077
+
2078
+ case 'arrowdown':
2079
+ e.preventDefault();
2080
+ this.selectNextChannel();
2081
+ break;
2082
+
2083
+ case 'f':
2084
+ e.preventDefault();
2085
+ this.toggleFullscreen();
2086
+ break;
2087
+
2088
+ case 'm':
2089
+ e.preventDefault();
2090
+ this.toggleMute();
2091
+ break;
2092
+
2093
+ case 'l':
2094
+ e.preventDefault();
2095
+ this.loadPlaylistFromUrl();
2096
+ break;
2097
+
2098
+ case 'escape':
2099
+ if (document.fullscreenElement) {
2100
+ document.exitFullscreen();
2101
+ }
2102
+ break;
2103
+ }
2104
+ });
2105
+ }
2106
+
2107
+ async loadDefaultPlaylist() {
2108
+ const defaultUrl = this.elements.playlistUrl.value;
2109
+ if (defaultUrl) {
2110
+ await this.loadPlaylistFromUrl();
2111
+ }
2112
+ }
2113
+
2114
+ async loadPlaylistFromUrl() {
2115
+ const url = this.elements.playlistUrl.value.trim();
2116
+
2117
+ if (!url) {
2118
+ this.showNotification('Please enter a playlist URL', 'error');
2119
+ return;
2120
+ }
2121
+
2122
+ this.updateStatus('Loading playlist...');
2123
+ this.showLoading(true);
2124
+
2125
+ try {
2126
+ const response = await fetch(url);
2127
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
2128
+
2129
+ const text = await response.text();
2130
+ await this.parsePlaylist(text, url);
2131
+
2132
+ this.showNotification('Playlist loaded successfully', 'success');
2133
+ this.elements.lastUpdate.textContent = new Date().toLocaleTimeString();
2134
+
2135
+ } catch (error) {
2136
+ console.error('Failed to load playlist:', error);
2137
+ this.showNotification('Failed to load playlist. Please check the URL.', 'error');
2138
+ this.updateStatus('Load failed');
2139
+ } finally {
2140
+ this.showLoading(false);
2141
+ }
2142
+ }
2143
+
2144
+ async loadPlaylistFromFile(file) {
2145
+ this.updateStatus('Reading playlist file...');
2146
+ this.showLoading(true);
2147
+
2148
+ try {
2149
+ const text = await file.text();
2150
+ await this.parsePlaylist(text, file.name);
2151
+
2152
+ this.showNotification(`Loaded playlist: ${file.name}`, 'success');
2153
+ this.elements.lastUpdate.textContent = new Date().toLocaleTimeString();
2154
+
2155
+ } catch (error) {
2156
+ console.error('Failed to load file:', error);
2157
+ this.showNotification('Failed to load playlist file', 'error');
2158
+ } finally {
2159
+ this.showLoading(false);
2160
+ }
2161
+ }
2162
+
2163
+ async parsePlaylist(content, source) {
2164
+ const channels = [];
2165
+ const lines = content.split('\n');
2166
+ let currentChannel = {};
2167
+ let channelNumber = 1;
2168
+
2169
+ for (let line of lines) {
2170
+ line = line.trim();
2171
+
2172
+ if (line.startsWith('#EXTINF')) {
2173
+ const titleMatch = line.match(/,(.*)$/);
2174
+ const logoMatch = line.match(/tvg-logo="([^"]+)"/);
2175
+ const groupMatch = line.match(/group-title="([^"]+)"/);
2176
+
2177
+ currentChannel = {
2178
+ id: `channel_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
2179
+ title: titleMatch ? this.cleanTitle(titleMatch[1]) : `Channel ${channelNumber}`,
2180
+ logo: logoMatch ? logoMatch[1] : '',
2181
+ group: groupMatch ? groupMatch[1] : 'General',
2182
+ number: channelNumber,
2183
+ source: source,
2184
+ url: '',
2185
+ isFavorite: false,
2186
+ category: this.detectCategory(titleMatch ? titleMatch[1] : '')
2187
+ };
2188
+
2189
+ channelNumber++;
2190
+ }
2191
+ else if (line.startsWith('http')) {
2192
+ currentChannel.url = line;
2193
+ channels.push({...currentChannel});
2194
+ }
2195
+ }
2196
+
2197
+ this.state.channels = channels;
2198
+ this.state.filteredChannels = [...channels];
2199
+
2200
+ this.updateChannelCount();
2201
+ this.renderChannels();
2202
+
2203
+ if (channels.length > 0) {
2204
+ this.selectChannel(0);
2205
+ }
2206
+ }
2207
+
2208
+ cleanTitle(title) {
2209
+ return title
2210
+ .replace(/^Pluto TV\s*/i, '')
2211
+ .replace(/^\[.*?\]\s*/, '')
2212
+ .replace(/\|.*$/, '')
2213
+ .trim();
2214
+ }
2215
+
2216
+ detectCategory(title) {
2217
+ const lowerTitle = title.toLowerCase();
2218
+
2219
+ if (lowerTitle.includes('news') || lowerTitle.includes('cnn') || lowerTitle.includes('bbc')) {
2220
+ return 'news';
2221
+ } else if (lowerTitle.includes('sport') || lowerTitle.includes('espn') || lowerTitle.includes('football')) {
2222
+ return 'sports';
2223
+ } else if (lowerTitle.includes('movie') || lowerTitle.includes('cinema') || lowerTitle.includes('film')) {
2224
+ return 'movies';
2225
+ } else if (lowerTitle.includes('music') || lowerTitle.includes('mtv') || lowerTitle.includes('vibe')) {
2226
+ return 'music';
2227
+ } else if (lowerTitle.includes('kids') || lowerTitle.includes('cartoon') || lowerTitle.includes('disney')) {
2228
+ return 'kids';
2229
+ } else {
2230
+ return 'entertainment';
2231
+ }
2232
+ }
2233
+
2234
+ renderChannels() {
2235
+ const container = this.elements.channelsContainer;
2236
+
2237
+ if (this.state.filteredChannels.length === 0) {
2238
+ container.innerHTML = `
2239
+ <div class="welcome-state">
2240
+ <i class="fas fa-broadcast-tower"></i>
2241
+ <h3>No Channels Found</h3>
2242
+ <p>Try a different search term or filter</p>
2243
+ </div>
2244
+ `;
2245
+ return;
2246
+ }
2247
+
2248
+ let html = '';
2249
+
2250
+ this.state.filteredChannels.forEach((channel, index) => {
2251
+ const isActive = this.state.currentChannel &&
2252
+ this.state.currentChannel.id === channel.id;
2253
+ const isFavorite = this.state.favorites[channel.id];
2254
+
2255
+ html += `
2256
+ <div class="channel-card ${isActive ? 'active' : ''}"
2257
+ data-index="${index}"
2258
+ data-id="${channel.id}">
2259
+ <div class="channel-logo">
2260
+ ${channel.logo ?
2261
+ `<img src="${channel.logo}" alt="${channel.title}"
2262
+ onerror="this.style.display='none'; this.parentElement.innerHTML='<i class=\"fas fa-tv\"></i>'">` :
2263
+ `<i class="fas fa-tv"></i>`}
2264
+ </div>
2265
+ <div class="channel-info">
2266
+ <h4>${channel.title}</h4>
2267
+ <div class="channel-meta">
2268
+ <span class="channel-number">${channel.number}</span>
2269
+ <span class="channel-category">${channel.category}</span>
2270
+ </div>
2271
+ </div>
2272
+ <div class="channel-fav ${isFavorite ? 'active' : ''}"
2273
+ data-id="${channel.id}">
2274
+ <i class="fas fa-star"></i>
2275
+ </div>
2276
+ </div>
2277
+ `;
2278
+ });
2279
+
2280
+ container.innerHTML = html;
2281
+
2282
+ // Add event listeners
2283
+ container.querySelectorAll('.channel-card').forEach(card => {
2284
+ card.addEventListener('click', (e) => {
2285
+ if (!e.target.closest('.channel-fav')) {
2286
+ const index = parseInt(card.dataset.index);
2287
+ this.selectChannel(index);
2288
+ }
2289
+ });
2290
+ });
2291
+
2292
+ container.querySelectorAll('.channel-fav').forEach(fav => {
2293
+ fav.addEventListener('click', (e) => {
2294
+ e.stopPropagation();
2295
+ const channelId = fav.dataset.id;
2296
+ this.toggleFavorite(channelId);
2297
+ fav.classList.toggle('active');
2298
+ });
2299
+ });
2300
+ }
2301
+
2302
+ filterChannels() {
2303
+ let filtered = this.state.channels;
2304
+
2305
+ // Apply search filter
2306
+ if (this.state.searchQuery) {
2307
+ filtered = filtered.filter(ch =>
2308
+ ch.title.toLowerCase().includes(this.state.searchQuery) ||
2309
+ ch.group.toLowerCase().includes(this.state.searchQuery) ||
2310
+ ch.category.toLowerCase().includes(this.state.searchQuery)
2311
+ );
2312
+ }
2313
+
2314
+ // Apply category filter
2315
+ if (this.state.filterCategory !== 'all') {
2316
+ filtered = filtered.filter(ch =>
2317
+ ch.category === this.state.filterCategory
2318
+ );
2319
+ }
2320
+
2321
+ this.state.filteredChannels = filtered;
2322
+ this.renderChannels();
2323
+ this.updateChannelCount();
2324
+ }
2325
+
2326
+ selectChannel(index) {
2327
+ if (index < 0 || index >= this.state.filteredChannels.length) return;
2328
+
2329
+ const channel = this.state.filteredChannels[index];
2330
+ this.state.currentChannel = channel;
2331
+
2332
+ // Update UI
2333
+ this.elements.currentChannel.textContent = channel.title;
2334
+ this.elements.currentProgram.textContent = `Now Playing • ${channel.group}`;
2335
+ this.elements.nowPlayingChannel.textContent = channel.title;
2336
+ this.elements.nowPlayingRegion.textContent = channel.group;
2337
+
2338
+ // Update favorite button
2339
+ this.elements.toggleFav.classList.toggle('active', this.state.favorites[channel.id]);
2340
+
2341
+ // Update active state in list
2342
+ document.querySelectorAll('.channel-card').forEach(card => {
2343
+ card.classList.remove('active');
2344
+ });
2345
+
2346
+ const selectedCard = document.querySelector(`.channel-card[data-index="${index}"]`);
2347
+ if (selectedCard) {
2348
+ selectedCard.classList.add('active');
2349
+ selectedCard.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
2350
+ }
2351
+
2352
+ // Play the channel
2353
+ this.playChannel(channel.url);
2354
+ this.updateStatus(`Playing: ${channel.title}`);
2355
+ }
2356
+
2357
+ selectNextChannel() {
2358
+ if (!this.state.currentChannel || this.state.filteredChannels.length === 0) return;
2359
+
2360
+ const currentIndex = this.state.filteredChannels.findIndex(
2361
+ ch => ch.id === this.state.currentChannel.id
2362
+ );
2363
+
2364
+ if (currentIndex >= 0) {
2365
+ const nextIndex = (currentIndex + 1) % this.state.filteredChannels.length;
2366
+ this.selectChannel(nextIndex);
2367
+ } else if (this.state.filteredChannels.length > 0) {
2368
+ this.selectChannel(0);
2369
+ }
2370
+ }
2371
+
2372
+ selectPreviousChannel() {
2373
+ if (!this.state.currentChannel || this.state.filteredChannels.length === 0) return;
2374
+
2375
+ const currentIndex = this.state.filteredChannels.findIndex(
2376
+ ch => ch.id === this.state.currentChannel.id
2377
+ );
2378
+
2379
+ if (currentIndex >= 0) {
2380
+ const prevIndex = currentIndex - 1 >= 0 ?
2381
+ currentIndex - 1 :
2382
+ this.state.filteredChannels.length - 1;
2383
+ this.selectChannel(prevIndex);
2384
+ } else if (this.state.filteredChannels.length > 0) {
2385
+ this.selectChannel(0);
2386
+ }
2387
+ }
2388
+
2389
+ playChannel(url) {
2390
+ this.showLoading(true);
2391
+
2392
+ // Destroy previous HLS instance
2393
+ if (this.state.hls) {
2394
+ this.state.hls.destroy();
2395
+ this.state.hls = null;
2396
+ }
2397
+
2398
+ // Stop current video
2399
+ this.elements.video.pause();
2400
+ this.elements.video.src = '';
2401
+
2402
+ if (Hls.isSupported()) {
2403
+ this.state.hls = new Hls({
2404
+ enableWorker: true,
2405
+ lowLatencyMode: true,
2406
+ backBufferLength: 30,
2407
+ maxBufferLength: 60,
2408
+ debug: false,
2409
+ liveSyncDurationCount: 3,
2410
+ liveMaxLatencyDurationCount: 10
2411
+ });
2412
+
2413
+ this.state.hls.loadSource(url);
2414
+ this.state.hls.attachMedia(this.elements.video);
2415
+
2416
+ this.state.hls.on(Hls.Events.MANIFEST_PARSED, () => {
2417
+ this.elements.video.play().then(() => {
2418
+ this.showLoading(false);
2419
+ this.state.isPlaying = true;
2420
+ this.elements.playPauseBtn.innerHTML = '<i class="fas fa-pause"></i>';
2421
+ }).catch(error => {
2422
+ console.log('Autoplay prevented:', error);
2423
+ this.showLoading(false);
2424
+ this.updateStatus('Click play button to start');
2425
+ this.elements.playPauseBtn.innerHTML = '<i class="fas fa-play"></i>';
2426
+ });
2427
+ });
2428
+
2429
+ this.state.hls.on(Hls.Events.ERROR, (event, data) => {
2430
+ console.error('HLS Error:', data);
2431
+
2432
+ if (data.fatal) {
2433
+ switch (data.type) {
2434
+ case Hls.ErrorTypes.NETWORK_ERROR:
2435
+ this.updateStatus('Network error - retrying...');
2436
+ this.state.hls.startLoad();
2437
+ break;
2438
+ case Hls.ErrorTypes.MEDIA_ERROR:
2439
+ this.updateStatus('Media error - recovering...');
2440
+ this.state.hls.recoverMediaError();
2441
+ break;
2442
+ default:
2443
+ this.state.hls.destroy();
2444
+ this.showNotification('Playback failed. Trying next channel...', 'error');
2445
+ this.selectNextChannel();
2446
+ break;
2447
+ }
2448
+ }
2449
+ });
2450
+
2451
+ this.state.hls.on(Hls.Events.LEVEL_SWITCHED, (event, data) => {
2452
+ const level = this.state.hls.levels[data.level];
2453
+ if (level) {
2454
+ const bitrate = Math.round(level.bitrate / 1000);
2455
+ const height = level.height || 'Auto';
2456
+
2457
+ this.state.quality = height === 'Auto' ? 'Auto' : `${height}p`;
2458
+ this.elements.qualityBadge.textContent = this.state.quality;
2459
+ this.elements.bitrateStat.textContent = `${bitrate} Mbps`;
2460
+
2461
+ this.updateQualityStats();
2462
+ }
2463
+ });
2464
+
2465
+ this.state.hls.on(Hls.Events.BUFFER_CREATED, () => {
2466
+ this.updateBufferStats();
2467
+ });
2468
+
2469
+ } else if (this.elements.video.canPlayType('application/vnd.apple.mpegurl')) {
2470
+ // Safari native HLS support
2471
+ this.elements.video.src = url;
2472
+ this.elements.video.play().then(() => {
2473
+ this.showLoading(false);
2474
+ this.state.isPlaying = true;
2475
+ this.elements.playPauseBtn.innerHTML = '<i class="fas fa-pause"></i>';
2476
+ }).catch(error => {
2477
+ console.log('Native HLS autoplay prevented:', error);
2478
+ this.showLoading(false);
2479
+ this.updateStatus('Click play button to start');
2480
+ this.elements.playPauseBtn.innerHTML = '<i class="fas fa-play"></i>';
2481
+ });
2482
+ } else {
2483
+ this.showLoading(false);
2484
+ this.showNotification('Your browser does not support HLS streaming', 'error');
2485
+ }
2486
+ }
2487
+
2488
+ togglePlayPause() {
2489
+ if (this.elements.video.paused) {
2490
+ this.elements.video.play();
2491
+ this.elements.playPauseBtn.innerHTML = '<i class="fas fa-pause"></i>';
2492
+ } else {
2493
+ this.elements.video.pause();
2494
+ this.elements.playPauseBtn.innerHTML = '<i class="fas fa-play"></i>';
2495
+ }
2496
+ }
2497
+
2498
+ toggleFavorite(channelId) {
2499
+ if (this.state.favorites[channelId]) {
2500
+ delete this.state.favorites[channelId];
2501
+ this.showNotification('Removed from favorites', 'info');
2502
+ } else {
2503
+ this.state.favorites[channelId] = true;
2504
+ this.showNotification('Added to favorites', 'success');
2505
+ }
2506
+
2507
+ localStorage.setItem('clarke_favs', JSON.stringify(this.state.favorites));
2508
+ this.renderFavorites();
2509
+
2510
+ // Update favorite button if this is the current channel
2511
+ if (this.state.currentChannel && this.state.currentChannel.id === channelId) {
2512
+ this.elements.toggleFav.classList.toggle('active', this.state.favorites[channelId]);
2513
+ }
2514
+ }
2515
+
2516
+ renderFavorites() {
2517
+ const container = this.elements.favoritesList;
2518
+ const favoriteChannels = this.state.channels.filter(ch => this.state.favorites[ch.id]);
2519
+
2520
+ if (favoriteChannels.length === 0) {
2521
+ container.innerHTML = `
2522
+ <div class="empty-favorites">
2523
+ <i class="fas fa-star"></i>
2524
+ <p>No favorites yet</p>
2525
+ </div>
2526
+ `;
2527
+ return;
2528
+ }
2529
+
2530
+ let html = '';
2531
+
2532
+ favoriteChannels.forEach(channel => {
2533
+ html += `
2534
+ <div class="favorite-item" data-id="${channel.id}">
2535
+ <i class="fas fa-star"></i>
2536
+ <span>${channel.title}</span>
2537
+ </div>
2538
+ `;
2539
+ });
2540
+
2541
+ container.innerHTML = html;
2542
+
2543
+ // Add click listeners
2544
+ container.querySelectorAll('.favorite-item').forEach(item => {
2545
+ item.addEventListener('click', () => {
2546
+ const channelId = item.dataset.id;
2547
+ const channelIndex = this.state.filteredChannels.findIndex(ch => ch.id === channelId);
2548
+ if (channelIndex >= 0) {
2549
+ this.selectChannel(channelIndex);
2550
+ }
2551
+ });
2552
+ });
2553
+ }
2554
+
2555
+ toggleFullscreen() {
2556
+ if (!document.fullscreenElement) {
2557
+ document.documentElement.requestFullscreen().catch(err => {
2558
+ console.log(`Fullscreen error: ${err.message}`);
2559
+ });
2560
+ } else {
2561
+ document.exitFullscreen();
2562
+ }
2563
+ }
2564
+
2565
+ toggleMute() {
2566
+ this.elements.video.muted = !this.elements.video.muted;
2567
+ this.updateStatus(this.elements.video.muted ? 'Muted' : 'Unmuted');
2568
+ }
2569
+
2570
+ seek(seconds) {
2571
+ this.elements.video.currentTime += seconds;
2572
+ }
2573
+
2574
+ updateStatus(text) {
2575
+ this.elements.statusText.textContent = text;
2576
+ }
2577
+
2578
+ updateChannelCount() {
2579
+ const count = this.state.filteredChannels.length;
2580
+ const total = this.state.channels.length;
2581
+ this.elements.channelCount.textContent = `${count}`;
2582
+
2583
+ // Update average quality estimation
2584
+ if (total > 0) {
2585
+ const hdCount = this.state.channels.filter(ch =>
2586
+ ch.title.toLowerCase().includes('hd') ||
2587
+ ch.title.toLowerCase().includes('1080') ||
2588
+ ch.title.toLowerCase().includes('4k')
2589
+ ).length;
2590
+
2591
+ const hdPercentage = Math.round((hdCount / total) * 100);
2592
+ this.elements.avgQuality.textContent = `${hdPercentage}% HD`;
2593
+ }
2594
+ }
2595
+
2596
+ updatePlaybackInfo() {
2597
+ const current = this.elements.video.currentTime;
2598
+ const duration = this.elements.video.duration || 0;
2599
+
2600
+ // Format time
2601
+ const formatTime = (seconds) => {
2602
+ const mins = Math.floor(seconds / 60);
2603
+ const secs = Math.floor(seconds % 60);
2604
+ return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
2605
+ };
2606
+
2607
+ this.elements.currentTime.textContent = formatTime(current);
2608
+ this.elements.totalTime.textContent = formatTime(duration);
2609
+
2610
+ // Update buffer progress
2611
+ if (this.elements.video.buffered.length > 0) {
2612
+ const bufferedEnd = this.elements.video.buffered.end(this.elements.video.buffered.length - 1);
2613
+ const bufferPercentage = duration > 0 ? (bufferedEnd / duration) * 100 : 0;
2614
+ this.elements.bufferProgress.style.width = `${bufferPercentage}%`;
2615
+ }
2616
+ }
2617
+
2618
+ updateStats() {
2619
+ // Update health based on buffering
2620
+ if (this.state.hls) {
2621
+ const bufferLength = this.state.hls.media.buffered.length;
2622
+ const health = bufferLength > 0 ? 100 : 80;
2623
+ this.elements.healthStat.textContent = `${health}%`;
2624
+ }
2625
+
2626
+ // Update buffer time
2627
+ if (this.elements.video.buffered.length > 0) {
2628
+ const bufferedEnd = this.elements.video.buffered.end(this.elements.video.buffered.length - 1);
2629
+ const bufferTime = Math.round(bufferedEnd - this.elements.video.currentTime);
2630
+ this.elements.bufferStat.textContent = `${bufferTime}s`;
2631
+ }
2632
+
2633
+ // Update every second
2634
+ setTimeout(() => this.updateStats(), 1000);
2635
+ }
2636
+
2637
+ updateBufferStats() {
2638
+ if (this.state.hls) {
2639
+ const bufferInfo = this.state.hls.media.buffered;
2640
+ if (bufferInfo.length > 0) {
2641
+ const bufferTime = bufferInfo.end(bufferInfo.length - 1) - this.elements.video.currentTime;
2642
+ this.elements.bufferStat.textContent = `${Math.round(bufferTime)}s`;
2643
+ }
2644
+ }
2645
+ }
2646
+
2647
+ updateQualityStats() {
2648
+ // Update quality distribution
2649
+ if (this.state.channels.length > 0) {
2650
+ const hdChannels = this.state.channels.filter(ch =>
2651
+ ch.title.toLowerCase().includes('hd') ||
2652
+ ch.title.toLowerCase().includes('1080') ||
2653
+ ch.title.toLowerCase().includes('4k')
2654
+ ).length;
2655
+
2656
+ const hdPercentage = Math.round((hdChannels / this.state.channels.length) * 100);
2657
+ this.elements.avgQuality.textContent = `${hdPercentage}% HD`;
2658
+ }
2659
+ }
2660
+
2661
+ clearPlaylist() {
2662
+ this.state.channels = [];
2663
+ this.state.filteredChannels = [];
2664
+ this.state.currentChannel = null;
2665
+
2666
+ if (this.state.hls) {
2667
+ this.state.hls.destroy();
2668
+ this.state.hls = null;
2669
+ }
2670
+
2671
+ this.elements.video.pause();
2672
+ this.elements.video.src = '';
2673
+
2674
+ this.renderChannels();
2675
+ this.updateChannelCount();
2676
+ this.updateStatus('Playlist cleared');
2677
+
2678
+ this.elements.currentChannel.textContent = 'Welcome to Clarke Player Pro';
2679
+ this.elements.currentProgram.textContent = 'Load a playlist to start streaming';
2680
+ this.elements.nowPlayingChannel.textContent = '--';
2681
+ this.elements.nowPlayingRegion.textContent = '--';
2682
+
2683
+ this.showNotification('Playlist cleared', 'info');
2684
+ }
2685
+
2686
+ showLoading(show) {
2687
+ this.elements.loadingOverlay.style.display = show ? 'flex' : 'none';
2688
+ }
2689
+
2690
+ showNotification(message, type = 'info') {
2691
+ // Create notification element
2692
+ const notification = document.createElement('div');
2693
+ notification.className = `notification notification-${type}`;
2694
+ notification.innerHTML = `
2695
+ <i class="fas fa-${type === 'success' ? 'check-circle' : type === 'error' ? 'exclamation-circle' : 'info-circle'}"></i>
2696
+ <span>${message}</span>
2697
+ `;
2698
+
2699
+ // Add styles for notification
2700
+ notification.style.cssText = `
2701
+ position: fixed;
2702
+ top: 20px;
2703
+ right: 20px;
2704
+ background: ${type === 'success' ? 'var(--accent)' : type === 'error' ? 'var(--danger)' : 'var(--info)'};
2705
+ color: white;
2706
+ padding: 1rem 1.5rem;
2707
+ border-radius: 12px;
2708
+ display: flex;
2709
+ align-items: center;
2710
+ gap: 0.75rem;
2711
+ z-index: 10000;
2712
+ animation: slideIn 0.3s ease;
2713
+ box-shadow: var(--shadow-lg);
2714
+ max-width: 300px;
2715
+ `;
2716
+
2717
+ document.body.appendChild(notification);
2718
+
2719
+ // Remove after 3 seconds
2720
+ setTimeout(() => {
2721
+ notification.style.animation = 'slideOut 0.3s ease';
2722
+ setTimeout(() => {
2723
+ if (notification.parentNode) {
2724
+ notification.parentNode.removeChild(notification);
2725
+ }
2726
+ }, 300);
2727
+ }, 3000);
2728
+
2729
+ // Add animation keyframes
2730
+ const style = document.createElement('style');
2731
+ style.textContent = `
2732
+ @keyframes slideIn {
2733
+ from { transform: translateX(100%); opacity: 0; }
2734
+ to { transform: translateX(0); opacity: 1; }
2735
+ }
2736
+ @keyframes slideOut {
2737
+ from { transform: translateX(0); opacity: 1; }
2738
+ to { transform: translateX(100%); opacity: 0; }
2739
+ }
2740
+ `;
2741
+
2742
+ if (!document.querySelector('#notification-styles')) {
2743
+ style.id = 'notification-styles';
2744
+ document.head.appendChild(style);
2745
+ }
2746
+ }
2747
+ }
2748
+
2749
+ // Initialize Clarke Player Pro
2750
+ let player;
2751
+ document.addEventListener('DOMContentLoaded', () => {
2752
+ player = new ClarkePlayerPro();
2753
+ });
2754
+ </script>
2755
  </body>
2756
  </html>