2026-04-27 16:41:39 -03:00
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<head>
|
|
|
|
|
<meta charset="UTF-8">
|
2026-04-28 10:34:04 -03:00
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
2026-04-27 16:41:39 -03:00
|
|
|
<style>
|
|
|
|
|
*{margin:0;padding:0;box-sizing:border-box}
|
|
|
|
|
html,body{background:#000;overflow:hidden;width:100%;height:100%}
|
2026-04-28 10:34:04 -03:00
|
|
|
video{width:100vw;height:100vh;height:100dvh;display:block;object-fit:contain}
|
2026-04-27 16:41:39 -03:00
|
|
|
#msg{
|
|
|
|
|
position:fixed;top:16px;left:50%;transform:translateX(-50%);
|
|
|
|
|
background:rgba(0,0,0,0.75);color:#fff;padding:8px 20px;
|
|
|
|
|
border-radius:8px;font-family:sans-serif;font-size:34px;
|
|
|
|
|
display:none;z-index:9
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
<video id="v" autoplay muted playsinline></video>
|
|
|
|
|
<div id="msg"></div>
|
|
|
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/hls.js/1.4.12/hls.min.js"></script>
|
|
|
|
|
<script>
|
|
|
|
|
var params = new URLSearchParams(window.location.search);
|
|
|
|
|
var id = params.get('id') || '';
|
|
|
|
|
var proxyUrl = '/api/hls/live/' + id + '/index.m3u8';
|
|
|
|
|
var directUrl= 'http://' + window.location.hostname + ':8888/live/' + id + '/index.m3u8';
|
|
|
|
|
var activeSrc= proxyUrl;
|
|
|
|
|
var hls;
|
|
|
|
|
|
|
|
|
|
function showMsg(t){
|
|
|
|
|
var m=document.getElementById('msg');
|
|
|
|
|
m.textContent=t;m.style.display='block';
|
|
|
|
|
setTimeout(function(){m.style.display='none';},4000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function load(src){
|
|
|
|
|
activeSrc=src;
|
2026-04-28 10:34:04 -03:00
|
|
|
var v=document.getElementById('v');
|
2026-04-27 16:41:39 -03:00
|
|
|
if(hls)hls.destroy();
|
2026-04-28 10:34:04 -03:00
|
|
|
if(!Hls.isSupported()){
|
|
|
|
|
if(v.canPlayType('application/vnd.apple.mpegurl')){
|
|
|
|
|
v.src=src;
|
|
|
|
|
var p=v.play();
|
|
|
|
|
if(p)p.catch(function(){v.muted=true;v.play();});
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-04-27 16:41:39 -03:00
|
|
|
hls=new Hls({
|
|
|
|
|
liveSyncDurationCount:2,liveMaxLatencyDurationCount:4,
|
|
|
|
|
manifestLoadingTimeOut:10000,manifestLoadingMaxRetry:10,
|
|
|
|
|
fragLoadingTimeOut:10000,fragLoadingMaxRetry:10
|
|
|
|
|
});
|
|
|
|
|
hls.loadSource(src);
|
2026-04-28 10:34:04 -03:00
|
|
|
hls.attachMedia(v);
|
|
|
|
|
hls.on(Hls.Events.MANIFEST_PARSED,function(){v.play();});
|
2026-04-27 16:41:39 -03:00
|
|
|
hls.on(Hls.Events.ERROR,function(e,d){
|
|
|
|
|
if(d.fatal){showMsg('Error: '+d.type+' — reconnecting...');setTimeout(function(){load(activeSrc);},3000);}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-28 10:34:04 -03:00
|
|
|
var last=0,started=false;
|
2026-04-27 16:41:39 -03:00
|
|
|
setInterval(function(){
|
|
|
|
|
var v=document.getElementById('v');
|
2026-04-28 10:34:04 -03:00
|
|
|
if(!started&&v.currentTime>0)started=true;
|
|
|
|
|
if(started&&v.currentTime===last&&!v.paused){
|
|
|
|
|
showMsg('Stream stalled — reloading...');
|
|
|
|
|
if(hls)load(activeSrc);
|
|
|
|
|
else{var s=v.src;v.src='';v.src=s;v.play().catch(function(){});}
|
|
|
|
|
}
|
2026-04-27 16:41:39 -03:00
|
|
|
last=v.currentTime;
|
|
|
|
|
},10000);
|
|
|
|
|
|
2026-04-28 10:34:04 -03:00
|
|
|
var ctrl=new AbortController();
|
|
|
|
|
var fetchTimer=setTimeout(function(){ctrl.abort();},2000);
|
|
|
|
|
fetch(directUrl,{method:'HEAD',signal:ctrl.signal})
|
|
|
|
|
.then(function(){clearTimeout(fetchTimer);load(directUrl);})
|
|
|
|
|
.catch(function(){clearTimeout(fetchTimer);load(proxyUrl);});
|
2026-04-27 16:41:39 -03:00
|
|
|
|
2026-04-27 17:56:10 -03:00
|
|
|
// Auto-reload — reads global-prefs written by the main UI
|
|
|
|
|
try {
|
|
|
|
|
var gp=JSON.parse(localStorage.getItem('global-prefs')||'{}');
|
|
|
|
|
if(gp.autoReload){
|
|
|
|
|
setTimeout(function(){location.reload();},Math.max(1,gp.reloadInterval||2)*60*1000);
|
|
|
|
|
}
|
|
|
|
|
}catch(e){}
|
|
|
|
|
|
2026-04-27 16:41:39 -03:00
|
|
|
// WebOS screensaver suppression — fails silently if not available
|
|
|
|
|
if(typeof WebOSServiceBridge!=='undefined'){
|
|
|
|
|
var bridge=new WebOSServiceBridge();
|
|
|
|
|
bridge.onservicecallback=function(msg){
|
|
|
|
|
var m=JSON.parse(msg);
|
|
|
|
|
if(m.state==='Active'){
|
|
|
|
|
bridge.call(
|
|
|
|
|
'luna://com.webos.service.tvpower/power/responseScreenSaverRequest',
|
|
|
|
|
JSON.stringify({clientName:'decapstream',ack:false,timestamp:m.timestamp})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
bridge.call(
|
|
|
|
|
'luna://com.webos.service.tvpower/power/registerScreenSaverRequest',
|
|
|
|
|
JSON.stringify({subscribe:true,clientName:'decapstream'})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|