Adiciona player.html estático, corrige Pure mode e desativa translate no Chromium
---
- Criado `public/player.html` como arquivo estático verdadeiro (servido pelo Next.js sem processamento, equivalente ao nginx), com HLS.js, tentativa direta ao MediaMTX em `:8888`, fallback para proxy, e integração silenciosa com `WebOSServiceBridge` para supressão do screensaver WebOS;
- Pure mode "Run HTML" atualizado para apontar a `/player.html?id={id}` em vez de rota dinâmica; rewrite removido do `next.config.ts`;
- Toggles "Pure mode" e "Open in new tab" no menu do card corrigidos: toggle agora à esquerda e texto à direita, alinhados com os demais itens;
- Adicionado `TranslateEnabled: false` na managed policy do Chromium no `Dockerfile`, solução definitiva para suprimir o popup de tradução do Google;
---
This commit is contained in:
+2
-2
@@ -46,9 +46,9 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
|||||||
&& apt-get clean \
|
&& apt-get clean \
|
||||||
&& find /usr/lib/chromium/locales -name '*.pak' ! -name 'en-US.pak' -delete 2>/dev/null || true \
|
&& find /usr/lib/chromium/locales -name '*.pak' ! -name 'en-US.pak' -delete 2>/dev/null || true \
|
||||||
\
|
\
|
||||||
# Chromium managed policy: disable password manager and autofill save prompts
|
# Chromium managed policy: disable password manager, autofill and translate
|
||||||
&& mkdir -p /etc/chromium/policies/managed \
|
&& mkdir -p /etc/chromium/policies/managed \
|
||||||
&& printf '{"PasswordManagerEnabled":false,"AutofillAddressEnabled":false,"AutofillCreditCardEnabled":false}' \
|
&& printf '{"PasswordManagerEnabled":false,"AutofillAddressEnabled":false,"AutofillCreditCardEnabled":false,"TranslateEnabled":false}' \
|
||||||
> /etc/chromium/policies/managed/policy.json \
|
> /etc/chromium/policies/managed/policy.json \
|
||||||
\
|
\
|
||||||
&& rm -rf \
|
&& rm -rf \
|
||||||
|
|||||||
@@ -2,14 +2,6 @@ import type { NextConfig } from "next";
|
|||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
output: "standalone",
|
output: "standalone",
|
||||||
async rewrites() {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
source: "/player/:id.html",
|
|
||||||
destination: "/api/player-html/:id",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<style>
|
||||||
|
*{margin:0;padding:0;box-sizing:border-box}
|
||||||
|
html,body{background:#000;overflow:hidden;width:100%;height:100%}
|
||||||
|
video{width:100vw;height:100vh;display:block;object-fit:contain}
|
||||||
|
#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;
|
||||||
|
if(hls)hls.destroy();
|
||||||
|
hls=new Hls({
|
||||||
|
liveSyncDurationCount:2,liveMaxLatencyDurationCount:4,
|
||||||
|
manifestLoadingTimeOut:10000,manifestLoadingMaxRetry:10,
|
||||||
|
fragLoadingTimeOut:10000,fragLoadingMaxRetry:10
|
||||||
|
});
|
||||||
|
hls.loadSource(src);
|
||||||
|
hls.attachMedia(document.getElementById('v'));
|
||||||
|
hls.on(Hls.Events.MANIFEST_PARSED,function(){document.getElementById('v').play();});
|
||||||
|
hls.on(Hls.Events.ERROR,function(e,d){
|
||||||
|
if(d.fatal){showMsg('Error: '+d.type+' — reconnecting...');setTimeout(function(){load(activeSrc);},3000);}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var last=0;
|
||||||
|
setInterval(function(){
|
||||||
|
var v=document.getElementById('v');
|
||||||
|
if(v.currentTime===last&&!v.paused){showMsg('Stream stalled — reloading...');load(activeSrc);}
|
||||||
|
last=v.currentTime;
|
||||||
|
},10000);
|
||||||
|
|
||||||
|
fetch(directUrl,{method:'HEAD',signal:AbortSignal.timeout(2000)})
|
||||||
|
.then(function(){load(directUrl);})
|
||||||
|
.catch(function(){load(proxyUrl);});
|
||||||
|
|
||||||
|
// 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>
|
||||||
@@ -174,7 +174,7 @@ export function StreamCard({ stream, status, localStatus, cardSize = "md", onRef
|
|||||||
|
|
||||||
function handleRunHtml() {
|
function handleRunHtml() {
|
||||||
navigate(prefs.pureMode
|
navigate(prefs.pureMode
|
||||||
? `/player/${stream.id}.html`
|
? `/player.html?id=${stream.id}`
|
||||||
: `/static/${stream.id}`)
|
: `/static/${stream.id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,12 +298,12 @@ const playBtn = `w-full flex items-center rounded border border-border bg-muted
|
|||||||
</button>
|
</button>
|
||||||
<div className="border-t border-border" />
|
<div className="border-t border-border" />
|
||||||
<button onClick={() => togglePref("pureMode")} className={menuItem}>
|
<button onClick={() => togglePref("pureMode")} className={menuItem}>
|
||||||
<span className="flex-1">Pure mode</span>
|
|
||||||
<Toggle on={prefs.pureMode} />
|
<Toggle on={prefs.pureMode} />
|
||||||
|
<span>Pure mode</span>
|
||||||
</button>
|
</button>
|
||||||
<button onClick={() => togglePref("newTab")} className={menuItem}>
|
<button onClick={() => togglePref("newTab")} className={menuItem}>
|
||||||
<span className="flex-1">Open in new tab</span>
|
|
||||||
<Toggle on={prefs.newTab} />
|
<Toggle on={prefs.newTab} />
|
||||||
|
<span>Open in new tab</span>
|
||||||
</button>
|
</button>
|
||||||
<div className="border-t border-border" />
|
<div className="border-t border-border" />
|
||||||
<button onClick={remove} className={cn(menuItem, "text-destructive")}>
|
<button onClick={remove} className={cn(menuItem, "text-destructive")}>
|
||||||
|
|||||||
Reference in New Issue
Block a user