AptlyDigital commited on
Commit
dbe26ce
·
verified ·
1 Parent(s): 36ed1c2

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +208 -120
index.html CHANGED
@@ -677,6 +677,61 @@
677
  box-shadow: 0 0 0 3px rgba(0, 102, 255, 0.1);
678
  }
679
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
680
  .quick-presets {
681
  margin-top: 1.5rem;
682
  }
@@ -1143,68 +1198,6 @@
1143
  right: 1rem;
1144
  z-index: 100;
1145
  }
1146
-
1147
- /* TV Guide (Mini EPG) */
1148
- .tv-guide {
1149
- position: absolute;
1150
- bottom: 5rem;
1151
- left: 2rem;
1152
- right: 2rem;
1153
- background: var(--glass-bg);
1154
- backdrop-filter: blur(40px);
1155
- border-radius: 16px;
1156
- border: 1px solid var(--border);
1157
- padding: 1.5rem;
1158
- display: none;
1159
- z-index: 50;
1160
- box-shadow: var(--shadow-xl);
1161
- }
1162
-
1163
- .guide-header {
1164
- display: flex;
1165
- justify-content: space-between;
1166
- align-items: center;
1167
- margin-bottom: 1rem;
1168
- }
1169
-
1170
- .guide-title {
1171
- font-size: 1rem;
1172
- font-weight: 600;
1173
- display: flex;
1174
- align-items: center;
1175
- gap: 0.5rem;
1176
- }
1177
-
1178
- .guide-timeline {
1179
- display: flex;
1180
- gap: 1rem;
1181
- overflow-x: auto;
1182
- padding-bottom: 0.5rem;
1183
- }
1184
-
1185
- .guide-item {
1186
- min-width: 200px;
1187
- padding: 1rem;
1188
- background: rgba(255, 255, 255, 0.03);
1189
- border-radius: 12px;
1190
- border: 1px solid var(--border);
1191
- }
1192
-
1193
- .guide-time {
1194
- font-size: 0.85rem;
1195
- color: var(--text-secondary);
1196
- margin-bottom: 0.5rem;
1197
- }
1198
-
1199
- .guide-show {
1200
- font-weight: 600;
1201
- margin-bottom: 0.5rem;
1202
- }
1203
-
1204
- .guide-desc {
1205
- font-size: 0.85rem;
1206
- color: var(--text-secondary);
1207
- }
1208
  </style>
1209
  </head>
1210
  <body>
@@ -1346,6 +1339,16 @@
1346
  </div>
1347
  </div>
1348
 
 
 
 
 
 
 
 
 
 
 
1349
  <div class="quick-presets">
1350
  <div class="preset-title">
1351
  <i class="fas fa-bolt"></i>
@@ -1370,7 +1373,7 @@
1370
  <div class="action-buttons">
1371
  <button class="tv-btn tv-btn-primary" id="loadPlaylistBtn">
1372
  <i class="fas fa-play-circle"></i>
1373
- <span>LOAD</span>
1374
  </button>
1375
  <button class="tv-btn tv-btn-secondary" id="clearBtn">
1376
  <i class="fas fa-trash"></i>
@@ -1403,7 +1406,7 @@
1403
  <i class="fas fa-broadcast-tower"></i>
1404
  </div>
1405
  <h3>No Channels Loaded</h3>
1406
- <p>Enter a playlist URL and click LOAD to begin</p>
1407
  </div>
1408
  </div>
1409
  </div>
@@ -1439,26 +1442,6 @@
1439
  </div>
1440
  </div>
1441
  </aside>
1442
-
1443
- <!-- TV Guide Overlay -->
1444
- <div class="tv-guide" id="tvGuide">
1445
- <div class="guide-header">
1446
- <div class="guide-title">
1447
- <i class="fas fa-tv"></i>
1448
- <span>NOW PLAYING</span>
1449
- </div>
1450
- <button class="tv-control-btn" id="closeGuide">
1451
- <i class="fas fa-times"></i>
1452
- </button>
1453
- </div>
1454
- <div class="guide-timeline" id="guideTimeline">
1455
- <div class="guide-item">
1456
- <div class="guide-time">NOW</div>
1457
- <div class="guide-show">Select a channel</div>
1458
- <div class="guide-desc">No program information available</div>
1459
- </div>
1460
- </div>
1461
- </div>
1462
  </div>
1463
 
1464
  <!-- Loading Overlay -->
@@ -1492,13 +1475,17 @@
1492
  bitrate: 0,
1493
  buffer: 0,
1494
  health: 100
1495
- }
 
1496
  };
1497
 
1498
  // DOM Elements
1499
  this.elements = {
1500
  video: document.getElementById('videoPlayer'),
1501
  playlistUrl: document.getElementById('playlistUrl'),
 
 
 
1502
  loadPlaylistBtn: document.getElementById('loadPlaylistBtn'),
1503
  clearBtn: document.getElementById('clearBtn'),
1504
  channelsList: document.getElementById('channelsList'),
@@ -1524,8 +1511,8 @@
1524
  initialLoadOverlay: document.getElementById('initialLoadOverlay'),
1525
  favoritesBtn: document.getElementById('favoritesBtn'),
1526
  guideBtn: document.getElementById('guideBtn'),
1527
- tvGuide: document.getElementById('tvGuide'),
1528
- closeGuide: document.getElementById('closeGuide')
1529
  };
1530
 
1531
  // Initialize
@@ -1560,11 +1547,43 @@
1560
  }
1561
 
1562
  setupEventListeners() {
1563
- // Load playlist button
1564
  this.elements.loadPlaylistBtn.addEventListener('click', () => {
1565
  this.loadPlaylistFromUrl(this.elements.playlistUrl.value);
1566
  });
1567
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1568
  // Clear button
1569
  this.elements.clearBtn.addEventListener('click', () => {
1570
  this.clearPlaylist();
@@ -1629,7 +1648,7 @@
1629
  });
1630
 
1631
  // Fullscreen
1632
- document.getElementById('fullscreenBtn').addEventListener('click', () => {
1633
  this.toggleFullscreen();
1634
  });
1635
 
@@ -1639,16 +1658,6 @@
1639
  this.elements.channelsSidebar.style.display === 'none' ? 'flex' : 'none';
1640
  });
1641
 
1642
- // Guide toggle
1643
- this.elements.guideBtn.addEventListener('click', () => {
1644
- this.elements.tvGuide.style.display =
1645
- this.elements.tvGuide.style.display === 'block' ? 'none' : 'block';
1646
- });
1647
-
1648
- this.elements.closeGuide.addEventListener('click', () => {
1649
- this.elements.tvGuide.style.display = 'none';
1650
- });
1651
-
1652
  // Video events
1653
  this.elements.video.addEventListener('play', () => {
1654
  this.state.isPlaying = true;
@@ -1704,26 +1713,9 @@
1704
  }, 3000);
1705
  });
1706
 
1707
- // Drag and drop for playlist URL
1708
- this.elements.playlistUrl.addEventListener('dragover', (e) => {
1709
- e.preventDefault();
1710
- e.currentTarget.style.borderColor = 'var(--primary)';
1711
- e.currentTarget.style.background = 'rgba(0, 102, 255, 0.1)';
1712
- });
1713
-
1714
- this.elements.playlistUrl.addEventListener('dragleave', (e) => {
1715
- e.currentTarget.style.borderColor = 'var(--border)';
1716
- e.currentTarget.style.background = 'rgba(255, 255, 255, 0.05)';
1717
- });
1718
-
1719
- this.elements.playlistUrl.addEventListener('drop', (e) => {
1720
- e.preventDefault();
1721
- const text = e.dataTransfer.getData('text');
1722
- if (text) {
1723
- this.elements.playlistUrl.value = text;
1724
- }
1725
- e.currentTarget.style.borderColor = 'var(--border)';
1726
- e.currentTarget.style.background = 'rgba(255, 255, 255, 0.05)';
1727
  });
1728
  }
1729
 
@@ -1776,12 +1768,44 @@
1776
  if (document.fullscreenElement) {
1777
  document.exitFullscreen();
1778
  }
1779
- this.elements.tvGuide.style.display = 'none';
1780
  break;
1781
  }
1782
  });
1783
  }
1784
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1785
  async loadPlaylistFromUrl(url) {
1786
  if (!url) {
1787
  this.showNotification('Please enter a playlist URL', 'error');
@@ -2215,6 +2239,7 @@
2215
  this.state.channels = [];
2216
  this.state.filteredChannels = [];
2217
  this.state.currentChannel = null;
 
2218
 
2219
  if (this.state.hls) {
2220
  this.state.hls.destroy();
@@ -2223,6 +2248,8 @@
2223
 
2224
  this.elements.video.pause();
2225
  this.elements.video.src = '';
 
 
2226
 
2227
  this.renderChannels();
2228
  this.elements.channelCount.textContent = '0';
@@ -2239,6 +2266,46 @@
2239
  this.elements.loadingOverlay.style.display = show ? 'flex' : 'none';
2240
  }
2241
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2242
  showNotification(message, type = 'info') {
2243
  // Create notification
2244
  const notification = document.createElement('div');
@@ -2285,6 +2352,27 @@
2285
  }
2286
  }, 3000);
2287
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2288
  }
2289
 
2290
  // Initialize Clarke Player Ultra
 
677
  box-shadow: 0 0 0 3px rgba(0, 102, 255, 0.1);
678
  }
679
 
680
+ /* File Upload Area */
681
+ .file-upload-area {
682
+ border: 2px dashed var(--border);
683
+ border-radius: 12px;
684
+ padding: 2rem 1rem;
685
+ text-align: center;
686
+ cursor: pointer;
687
+ transition: var(--transition);
688
+ background: rgba(255, 255, 255, 0.02);
689
+ position: relative;
690
+ overflow: hidden;
691
+ }
692
+
693
+ .file-upload-area:hover {
694
+ border-color: var(--primary);
695
+ background: rgba(255, 255, 255, 0.05);
696
+ }
697
+
698
+ .file-upload-area.dragover {
699
+ border-color: var(--primary);
700
+ background: rgba(0, 102, 255, 0.1);
701
+ box-shadow: var(--glow-primary);
702
+ }
703
+
704
+ .file-upload-area i {
705
+ font-size: 2.5rem;
706
+ color: var(--text-muted);
707
+ margin-bottom: 1rem;
708
+ display: block;
709
+ }
710
+
711
+ .file-upload-area p {
712
+ font-size: 0.9rem;
713
+ color: var(--text-secondary);
714
+ margin: 0;
715
+ line-height: 1.4;
716
+ }
717
+
718
+ .file-upload-area .file-name {
719
+ font-size: 0.85rem;
720
+ color: var(--primary-light);
721
+ margin-top: 0.5rem;
722
+ font-weight: 500;
723
+ }
724
+
725
+ .file-input {
726
+ position: absolute;
727
+ width: 100%;
728
+ height: 100%;
729
+ top: 0;
730
+ left: 0;
731
+ opacity: 0;
732
+ cursor: pointer;
733
+ }
734
+
735
  .quick-presets {
736
  margin-top: 1.5rem;
737
  }
 
1198
  right: 1rem;
1199
  z-index: 100;
1200
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1201
  </style>
1202
  </head>
1203
  <body>
 
1339
  </div>
1340
  </div>
1341
 
1342
+ <!-- File Upload Area -->
1343
+ <div class="input-group">
1344
+ <div class="file-upload-area" id="fileUploadArea">
1345
+ <i class="fas fa-file-upload"></i>
1346
+ <p>Drag & drop .m3u/.m3u8 file here<br>or click to browse</p>
1347
+ <div class="file-name" id="fileName"></div>
1348
+ <input type="file" id="fileInput" class="file-input" accept=".m3u,.m3u8">
1349
+ </div>
1350
+ </div>
1351
+
1352
  <div class="quick-presets">
1353
  <div class="preset-title">
1354
  <i class="fas fa-bolt"></i>
 
1373
  <div class="action-buttons">
1374
  <button class="tv-btn tv-btn-primary" id="loadPlaylistBtn">
1375
  <i class="fas fa-play-circle"></i>
1376
+ <span>LOAD URL</span>
1377
  </button>
1378
  <button class="tv-btn tv-btn-secondary" id="clearBtn">
1379
  <i class="fas fa-trash"></i>
 
1406
  <i class="fas fa-broadcast-tower"></i>
1407
  </div>
1408
  <h3>No Channels Loaded</h3>
1409
+ <p>Enter a playlist URL or upload a file to begin</p>
1410
  </div>
1411
  </div>
1412
  </div>
 
1442
  </div>
1443
  </div>
1444
  </aside>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1445
  </div>
1446
 
1447
  <!-- Loading Overlay -->
 
1475
  bitrate: 0,
1476
  buffer: 0,
1477
  health: 100
1478
+ },
1479
+ currentFile: null
1480
  };
1481
 
1482
  // DOM Elements
1483
  this.elements = {
1484
  video: document.getElementById('videoPlayer'),
1485
  playlistUrl: document.getElementById('playlistUrl'),
1486
+ fileInput: document.getElementById('fileInput'),
1487
+ fileUploadArea: document.getElementById('fileUploadArea'),
1488
+ fileName: document.getElementById('fileName'),
1489
  loadPlaylistBtn: document.getElementById('loadPlaylistBtn'),
1490
  clearBtn: document.getElementById('clearBtn'),
1491
  channelsList: document.getElementById('channelsList'),
 
1511
  initialLoadOverlay: document.getElementById('initialLoadOverlay'),
1512
  favoritesBtn: document.getElementById('favoritesBtn'),
1513
  guideBtn: document.getElementById('guideBtn'),
1514
+ settingsBtn: document.getElementById('settingsBtn'),
1515
+ fullscreenBtn: document.getElementById('fullscreenBtn')
1516
  };
1517
 
1518
  // Initialize
 
1547
  }
1548
 
1549
  setupEventListeners() {
1550
+ // Load playlist from URL button
1551
  this.elements.loadPlaylistBtn.addEventListener('click', () => {
1552
  this.loadPlaylistFromUrl(this.elements.playlistUrl.value);
1553
  });
1554
 
1555
+ // File input change
1556
+ this.elements.fileInput.addEventListener('change', (e) => {
1557
+ if (e.target.files.length > 0) {
1558
+ this.handleFileSelect(e.target.files[0]);
1559
+ }
1560
+ });
1561
+
1562
+ // Drag and drop for file upload
1563
+ this.elements.fileUploadArea.addEventListener('dragover', (e) => {
1564
+ e.preventDefault();
1565
+ e.currentTarget.classList.add('dragover');
1566
+ });
1567
+
1568
+ this.elements.fileUploadArea.addEventListener('dragleave', (e) => {
1569
+ e.preventDefault();
1570
+ e.currentTarget.classList.remove('dragover');
1571
+ });
1572
+
1573
+ this.elements.fileUploadArea.addEventListener('drop', (e) => {
1574
+ e.preventDefault();
1575
+ e.currentTarget.classList.remove('dragover');
1576
+
1577
+ if (e.dataTransfer.files.length > 0) {
1578
+ const file = e.dataTransfer.files[0];
1579
+ if (file.name.endsWith('.m3u') || file.name.endsWith('.m3u8')) {
1580
+ this.handleFileSelect(file);
1581
+ } else {
1582
+ this.showNotification('Please select a .m3u or .m3u8 file', 'error');
1583
+ }
1584
+ }
1585
+ });
1586
+
1587
  // Clear button
1588
  this.elements.clearBtn.addEventListener('click', () => {
1589
  this.clearPlaylist();
 
1648
  });
1649
 
1650
  // Fullscreen
1651
+ this.elements.fullscreenBtn.addEventListener('click', () => {
1652
  this.toggleFullscreen();
1653
  });
1654
 
 
1658
  this.elements.channelsSidebar.style.display === 'none' ? 'flex' : 'none';
1659
  });
1660
 
 
 
 
 
 
 
 
 
 
 
1661
  // Video events
1662
  this.elements.video.addEventListener('play', () => {
1663
  this.state.isPlaying = true;
 
1713
  }, 3000);
1714
  });
1715
 
1716
+ // Settings button
1717
+ this.elements.settingsBtn.addEventListener('click', () => {
1718
+ this.showSettings();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1719
  });
1720
  }
1721
 
 
1768
  if (document.fullscreenElement) {
1769
  document.exitFullscreen();
1770
  }
 
1771
  break;
1772
  }
1773
  });
1774
  }
1775
 
1776
+ handleFileSelect(file) {
1777
+ if (!file.name.endsWith('.m3u') && !file.name.endsWith('.m3u8')) {
1778
+ this.showNotification('Please select a .m3u or .m3u8 file', 'error');
1779
+ return;
1780
+ }
1781
+
1782
+ this.state.currentFile = file;
1783
+ this.elements.fileName.textContent = file.name;
1784
+ this.updateStatus('File selected: ' + file.name);
1785
+
1786
+ // Auto-load the file
1787
+ this.loadPlaylistFromFile(file);
1788
+ }
1789
+
1790
+ async loadPlaylistFromFile(file) {
1791
+ this.updateStatus('Loading playlist file...');
1792
+ this.showLoading(true);
1793
+
1794
+ try {
1795
+ const text = await file.text();
1796
+ await this.parsePlaylist(text, file.name);
1797
+
1798
+ this.showNotification(`Playlist loaded: ${file.name}`, 'success');
1799
+
1800
+ } catch (error) {
1801
+ console.error('Failed to load file:', error);
1802
+ this.showNotification('Failed to load playlist file', 'error');
1803
+ this.updateStatus('Load failed');
1804
+ } finally {
1805
+ this.showLoading(false);
1806
+ }
1807
+ }
1808
+
1809
  async loadPlaylistFromUrl(url) {
1810
  if (!url) {
1811
  this.showNotification('Please enter a playlist URL', 'error');
 
2239
  this.state.channels = [];
2240
  this.state.filteredChannels = [];
2241
  this.state.currentChannel = null;
2242
+ this.state.currentFile = null;
2243
 
2244
  if (this.state.hls) {
2245
  this.state.hls.destroy();
 
2248
 
2249
  this.elements.video.pause();
2250
  this.elements.video.src = '';
2251
+ this.elements.fileName.textContent = '';
2252
+ this.elements.fileInput.value = '';
2253
 
2254
  this.renderChannels();
2255
  this.elements.channelCount.textContent = '0';
 
2266
  this.elements.loadingOverlay.style.display = show ? 'flex' : 'none';
2267
  }
2268
 
2269
+ showSettings() {
2270
+ const settings = `
2271
+ <div class="overlay-content" style="max-width: 500px;">
2272
+ <div class="overlay-icon">
2273
+ <i class="fas fa-cog"></i>
2274
+ </div>
2275
+ <h2 class="overlay-title">Settings</h2>
2276
+ <div style="text-align: left; margin-bottom: 2rem;">
2277
+ <div style="margin-bottom: 1rem;">
2278
+ <label style="display: block; margin-bottom: 0.5rem; color: var(--text-secondary);">Video Quality</label>
2279
+ <select style="width: 100%; padding: 0.75rem; background: rgba(255,255,255,0.05); border: 1px solid var(--border); border-radius: 8px; color: var(--text-primary);">
2280
+ <option>Auto</option>
2281
+ <option>1080p</option>
2282
+ <option>720p</option>
2283
+ <option>480p</option>
2284
+ </select>
2285
+ </div>
2286
+ <div style="margin-bottom: 1rem;">
2287
+ <label style="display: block; margin-bottom: 0.5rem; color: var(--text-secondary);">Buffer Size</label>
2288
+ <select style="width: 100%; padding: 0.75rem; background: rgba(255,255,255,0.05); border: 1px solid var(--border); border-radius: 8px; color: var(--text-primary);">
2289
+ <option>30 seconds</option>
2290
+ <option>60 seconds</option>
2291
+ <option>90 seconds</option>
2292
+ </select>
2293
+ </div>
2294
+ </div>
2295
+ <div class="overlay-buttons">
2296
+ <button class="tv-btn tv-btn-primary" style="padding: 0.75rem 2rem;">
2297
+ Save
2298
+ </button>
2299
+ <button class="tv-btn tv-btn-secondary" style="padding: 0.75rem 2rem;">
2300
+ Cancel
2301
+ </button>
2302
+ </div>
2303
+ </div>
2304
+ `;
2305
+
2306
+ this.showOverlay(settings);
2307
+ }
2308
+
2309
  showNotification(message, type = 'info') {
2310
  // Create notification
2311
  const notification = document.createElement('div');
 
2352
  }
2353
  }, 3000);
2354
  }
2355
+
2356
+ showOverlay(content) {
2357
+ const overlay = document.createElement('div');
2358
+ overlay.className = 'tv-overlay';
2359
+ overlay.style.display = 'flex';
2360
+ overlay.innerHTML = content;
2361
+
2362
+ document.body.appendChild(overlay);
2363
+
2364
+ // Add close button functionality
2365
+ overlay.addEventListener('click', (e) => {
2366
+ if (e.target === overlay || e.target.closest('.tv-btn-secondary')) {
2367
+ overlay.style.animation = 'fadeIn 0.3s ease reverse';
2368
+ setTimeout(() => {
2369
+ if (overlay.parentNode) {
2370
+ overlay.parentNode.removeChild(overlay);
2371
+ }
2372
+ }, 300);
2373
+ }
2374
+ });
2375
+ }
2376
  }
2377
 
2378
  // Initialize Clarke Player Ultra