diff --git a/vidflow.html b/vidflow.html
index 0631a04..d6fb8d9 100644
--- a/vidflow.html
+++ b/vidflow.html
@@ -72,6 +72,10 @@ html,body{width:100%;height:100%;overflow:hidden;background:var(--bg);font-famil
.gl{position:absolute;top:0;left:0;bottom:0;width:180px;background:linear-gradient(90deg,rgba(6,6,15,.85) 0%,rgba(6,6,15,.4) 50%,transparent 100%);z-index:2;pointer-events:none}
#scanlines{position:absolute;inset:0;z-index:3;pointer-events:none;background:repeating-linear-gradient(0deg,transparent,transparent 3px,rgba(0,0,0,.055) 3px,rgba(0,0,0,.055) 4px);opacity:.5}
#sc{position:absolute;inset:0;z-index:11;display:none}
+#ch-flash{position:absolute;inset:0;z-index:12;display:flex;align-items:center;justify-content:center;pointer-events:none;opacity:0}
+#ch-flash.active{animation:ch-show 2s ease forwards}
+#chf-num{font-family:var(--font-d);font-size:min(13vw,96px);color:var(--accent);letter-spacing:4px;text-shadow:0 0 60px rgba(255,34,0,.6),0 2px 12px rgba(0,0,0,.9)}
+@keyframes ch-show{0%{opacity:0}10%{opacity:1}80%{opacity:1}100%{opacity:0}}
/* No channel */
#nco{position:absolute;inset:0;z-index:6;display:flex;align-items:center;justify-content:center;background:var(--bg);background-image:radial-gradient(ellipse 70% 50% at 50% 50%,rgba(255,34,0,.07) 0%,transparent 70%)}
@@ -353,6 +357,8 @@ body.idle .gt,body.idle .gb,body.idle .gr,body.idle .gl{opacity:0;transition:opa
+
+
@@ -450,7 +456,9 @@ const A = {
tickerRaf:null, tickerPos:9999, tcTimer:null, idleTimer:null,
tab:'channels',
lastActivity:Date.now(), isIdle:false,
- popoutWin:null
+ popoutWin:null,
+ prefetchNext:null, // video ID last checked by checkNextVid
+ prefetchOk:true // whether that video is playable (optimistic default)
};
const FD = "'Bebas Neue',Impact,'Arial Black',sans-serif";
const DEBUG = false;
@@ -534,13 +542,21 @@ function onPState(e){
pb.textContent='⏸'; A.playing=true;
updateNP(); startPB(); hideTap(); hideNCO();
try{ window.focus(); }catch(err){}
+ setTimeout(checkNextVid, 2000); // preflight the next video after player settles
} else if(e.data===S.PAUSED){
pb.textContent='▶'; A.playing=false;
} else if(e.data===S.CUED||e.data===-1){
// browser blocked autoplay — prompt user to tap
pb.textContent='▶'; A.playing=false; showTap();
} else if(e.data===S.ENDED){
- try{ A.player.nextVideo(); }catch(e){}
+ if(A.prefetchOk === false){
+ // Next video known-bad — advance twice to skip over it, then reset
+ try{ A.player.nextVideo(); A.player.nextVideo(); }catch(e){}
+ A.prefetchOk = true;
+ } else {
+ try{ A.player.nextVideo(); }catch(e){}
+ }
+ // Fallback: if player stalls at ENDED (e.g. single-video playlist), restart from beginning
setTimeout(()=>{
try{
if(A.player?.getPlayerState?.()===YT.PlayerState.ENDED) A.player.playVideoAt(0);
@@ -549,13 +565,15 @@ function onPState(e){
}
}
-// Player error (private video, geo-blocked, etc.) — wait 1.2s then skip
-function onPErr(e){ dbg('onPErr',e?.data); hideLoad(); setTimeout(()=>A.player?.nextVideo?.(),1200); }
+// Player error (private video, geo-blocked, deleted, etc.) — skip quickly now that prefetch catches most cases
+function onPErr(e){ dbg('onPErr',e?.data); hideLoad(); setTimeout(()=>A.player?.nextVideo?.(),400); }
// ── Channel flip static effect ──
// Renders a TV-static transition on #sc canvas before switching channels — chNum is 0-indexed channel index
function flip(chNum, cb){
const cv=qs('#sc');
+ // Show channel number overlay: CSS animation handles fade-in, hold, fade-out (~2s)
+ const fl=qs('#ch-flash'),fn=qs('#chf-num'); if(fl&&fn){ fn.textContent='CH '+(chNum+1); fl.classList.remove('active'); void fl.offsetWidth; fl.classList.add('active'); }
cv.width=window.innerWidth; cv.height=window.innerHeight;
cv.style.display='block';
const ctx=cv.getContext('2d');
@@ -569,14 +587,6 @@ function flip(chNum, cb){
d[i]=v;d[i+1]=v;d[i+2]=v;d[i+3]=fade;
}
ctx.putImageData(img,0,0);
- // Briefly flash the channel number in the middle of the static burst
- if(f>=4&&f<=15){
- const alpha=1-Math.abs(f-9.5)/9;
- ctx.fillStyle=`rgba(255,34,0,${alpha*.92})`;
- ctx.font=`bold ${Math.min(cv.width*.13,96)}px ${FD}`;
- ctx.textAlign='center'; ctx.textBaseline='middle';
- ctx.fillText('CH '+(chNum+1), cv.width/2, cv.height/2);
- }
f++;
if(f