From 7434e0b01718901d6d60e811ce744bfa2e820cbe Mon Sep 17 00:00:00 2001 From: Junwei Zhao Date: Sun, 2 Mar 2025 10:49:38 +1100 Subject: [PATCH] Make iptv works on mobile --- links/templates/links/iptv/list.html | 144 +++++++++++++++------------ 1 file changed, 83 insertions(+), 61 deletions(-) diff --git a/links/templates/links/iptv/list.html b/links/templates/links/iptv/list.html index 8a96f75..4695c0e 100644 --- a/links/templates/links/iptv/list.html +++ b/links/templates/links/iptv/list.html @@ -122,8 +122,8 @@ @@ -171,8 +171,8 @@
- - + +
@@ -351,65 +351,86 @@ const channelTitle = document.getElementById('channel-title'); let hls = null; let activeChannelElement = null; -function initHls() { - if (!Hls.isSupported()) { - alert('Your browser does not support HLS streaming'); - return null; - } - - return new Hls({ - debug: false, - enableWorker: true, - lowLatencyMode: true, - backBufferLength: 90 - }); -} - function loadStream(url) { + // Clean up any existing HLS instance if (hls) { hls.destroy(); + hls = null; } - hls = initHls(); - if (!hls) return false; - - try { - hls.loadSource(url); - hls.attachMedia(videoPlayer); - - hls.on(Hls.Events.MANIFEST_PARSED, () => { - console.log('HLS manifest loaded'); - if (document.documentElement.hasAttribute('data-user-interacted')) { - playVideo(); - } else { - playOverlay.classList.remove('hidden'); - } - }); - - hls.on(Hls.Events.ERROR, (event, data) => { - if (data.fatal) { - switch (data.type) { - case Hls.ErrorTypes.NETWORK_ERROR: - console.error('Network error:', data); - hls.startLoad(); - break; - case Hls.ErrorTypes.MEDIA_ERROR: - console.error('Media error:', data); - hls.recoverMediaError(); - break; - default: - console.error('Fatal error:', data); - hls.destroy(); - alert('Error loading stream. Please try another channel.'); - break; - } - } - }); - + // Show the video player and hide empty state + videoPlayer.classList.remove('hidden'); + emptyState.classList.add('hidden'); + + // First check if the browser natively supports HLS + if (videoPlayer.canPlayType('application/vnd.apple.mpegurl')) { + // Safari on iOS has native HLS support + console.log('Using native HLS support'); + videoPlayer.src = url; + + // Show play overlay if user hasn't interacted yet + if (document.documentElement.hasAttribute('data-user-interacted')) { + playVideo(); + } else { + playOverlay.classList.remove('hidden'); + } + return true; - } catch (error) { - console.error('Error initializing stream:', error); - alert('Error loading stream. Please try another channel.'); + } + // Otherwise use hls.js if it's supported + else if (Hls.isSupported()) { + console.log('Using HLS.js'); + hls = new Hls({ + debug: false, + enableWorker: true, + lowLatencyMode: true, + backBufferLength: 90 + }); + + try { + hls.loadSource(url); + hls.attachMedia(videoPlayer); + + hls.on(Hls.Events.MANIFEST_PARSED, () => { + console.log('HLS manifest loaded'); + if (document.documentElement.hasAttribute('data-user-interacted')) { + playVideo(); + } else { + playOverlay.classList.remove('hidden'); + } + }); + + hls.on(Hls.Events.ERROR, (event, data) => { + if (data.fatal) { + switch (data.type) { + case Hls.ErrorTypes.NETWORK_ERROR: + console.error('Network error:', data); + hls.startLoad(); + break; + case Hls.ErrorTypes.MEDIA_ERROR: + console.error('Media error:', data); + hls.recoverMediaError(); + break; + default: + console.error('Fatal error:', data); + hls.destroy(); + alert('Error loading stream. Please try another channel.'); + break; + } + } + }); + + return true; + } catch (error) { + console.error('Error initializing stream:', error); + alert('Error loading stream. Please try another channel.'); + return false; + } + } else { + // Neither native support nor hls.js support + console.error('HLS is not supported in this browser'); + alert('Your browser does not support HLS streaming'); + cleanupPlayer(); return false; } } @@ -431,8 +452,6 @@ function selectChannel(url, title, element = null) { } if (loadStream(url)) { - videoPlayer.classList.remove('hidden'); - emptyState.classList.add('hidden'); channelTitle.textContent = title; } else { cleanupPlayer(); @@ -448,8 +467,6 @@ function updatePlayOverlay() { } function playVideo() { - if (!hls || !videoPlayer) return; - videoPlayer.play() .then(() => { document.documentElement.setAttribute('data-user-interacted', 'true'); @@ -512,6 +529,11 @@ function cleanupPlayer() { hls.destroy(); hls = null; } + + // Reset video source + videoPlayer.src = ''; + videoPlayer.load(); + if (activeChannelElement) { activeChannelElement.classList.remove('bg-indigo-50', 'border-l-4', 'border-indigo-500'); activeChannelElement = null;