Spaces:
Running
Running
Upload index.html
Browse files- index.html +53 -16
index.html
CHANGED
|
@@ -122,6 +122,12 @@
|
|
| 122 |
.jtv-b{margin-left:auto; background:rgba(255,255,255,.18); color:#fff; border:0; border-radius:8px; padding:5px 10px; font:inherit; font-size:12px;}
|
| 123 |
.jtv-epg{margin-top:6px; max-height:38vh; overflow:auto; border-top:1px solid rgba(255,255,255,.2); padding-top:6px;}
|
| 124 |
.jtv-epg div{padding:3px 0;} .jtv-epg span{display:inline-block; min-width:48px; opacity:.7;}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
.rank-num{position:absolute; left:6px; bottom:0; font-size:58px; font-weight:900; line-height:1; letter-spacing:-3px; color:transparent;
|
| 126 |
-webkit-text-stroke:2px rgba(255,255,255,.92); text-shadow:0 4px 14px rgba(0,0,0,.65); pointer-events:none; z-index:2;}
|
| 127 |
.poster-art:has(.rank-num)::after{content:''; position:absolute; inset:auto 0 0 0; height:46%; background:linear-gradient(transparent,rgba(0,0,0,.7)); pointer-events:none;}
|
|
@@ -3610,40 +3616,71 @@ function jtvNow(s, nowMs) { // kipindi cha sasa + vinavyofuata, kwa saa: kila mt
|
|
| 3610 |
}
|
| 3611 |
async function openJaynesTV() {
|
| 3612 |
openPlayerShell('JaynesTV');
|
| 3613 |
-
|
| 3614 |
if (!s) return statusMsg('<span style="color:#ffb4ab">' + esc(T('jtv_down')) + '</span>');
|
| 3615 |
stage.style.position = 'relative';
|
| 3616 |
-
stage.innerHTML = '<video id="vid" controls autoplay playsinline></video><div class="jtv-bar" id="jtvBar"></div>'
|
| 3617 |
-
|
|
|
|
|
|
|
| 3618 |
attachPlayerEnhancements(v); ensureSound(v);
|
| 3619 |
-
const bad = new Set(); let cur = null, epg = false, hideT = null, last = null;
|
| 3620 |
const hhmm = ms => new Date(ms).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
|
|
| 3621 |
const show = (ms) => { bar.classList.remove('hide'); clearTimeout(hideT); if (ms) hideT = setTimeout(() => { if (!epg) bar.classList.add('hide'); }, ms); };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3622 |
const draw = (n, it) => {
|
| 3623 |
-
|
| 3624 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3625 |
'<button class="jtv-b" data-j="epg">' + esc(T('jtv_schedule')) + '</button></div>' +
|
| 3626 |
-
'<div class="jtv-m">' +
|
| 3627 |
-
(epg ? '<div class="jtv-epg">' +
|
|
|
|
|
|
|
| 3628 |
};
|
| 3629 |
const sync = () => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3630 |
const n = jtvNow(s, Date.now()), isBad = bad.has(n.idx);
|
| 3631 |
const it = isBad ? n.upcoming[0].item : n.item, off = isBad ? 0 : n.offset; // video mbovu: cheza inayofuata kutoka mwanzo
|
| 3632 |
if (cur !== it.url) {
|
| 3633 |
-
cur = it.url;
|
| 3634 |
-
|
| 3635 |
-
show(8000);
|
| 3636 |
} else if (!isBad && v.readyState > 1 && Math.abs(v.currentTime - off) > 4) v.currentTime = off; // rudi kwenye 'live'
|
| 3637 |
last = n; draw(n, it);
|
| 3638 |
};
|
| 3639 |
-
|
|
|
|
| 3640 |
v.addEventListener('ended', sync);
|
| 3641 |
v.addEventListener('play', sync);
|
| 3642 |
-
v.addEventListener('seeked', () => { if (last && Math.abs(v.currentTime - last.offset) > 6) sync(); });
|
| 3643 |
-
v.addEventListener('error', () => { if (last) { bad.add(last.idx); sync(); } });
|
| 3644 |
v.addEventListener('click', () => show(6000));
|
| 3645 |
-
const timer = setInterval(sync, 15000);
|
| 3646 |
-
|
|
|
|
| 3647 |
sync();
|
| 3648 |
}
|
| 3649 |
|
|
|
|
| 122 |
.jtv-b{margin-left:auto; background:rgba(255,255,255,.18); color:#fff; border:0; border-radius:8px; padding:5px 10px; font:inherit; font-size:12px;}
|
| 123 |
.jtv-epg{margin-top:6px; max-height:38vh; overflow:auto; border-top:1px solid rgba(255,255,255,.2); padding-top:6px;}
|
| 124 |
.jtv-epg div{padding:3px 0;} .jtv-epg span{display:inline-block; min-width:48px; opacity:.7;}
|
| 125 |
+
.jtv-logo{position:absolute; right:12px; bottom:56px; width:64px; max-height:40px; object-fit:contain; opacity:.75; z-index:4; pointer-events:none; filter:drop-shadow(0 1px 3px rgba(0,0,0,.6));}
|
| 126 |
+
.jtv-ad{position:absolute; left:12px; bottom:56px; z-index:6; background:#ffc53d; color:#1b1230; font-weight:900; font-size:11px; letter-spacing:1px; padding:3px 9px; border-radius:6px; display:none;}
|
| 127 |
+
.jtv-ad.on{display:block;}
|
| 128 |
+
.jtv-ad i{font-style:normal; font-weight:700; opacity:.8; margin-left:6px;}
|
| 129 |
+
.jtv-epg .now{color:#ffc53d; font-weight:700;}
|
| 130 |
+
.jtv-epg .k{font-size:10px; opacity:.6; margin-left:6px;}
|
| 131 |
.rank-num{position:absolute; left:6px; bottom:0; font-size:58px; font-weight:900; line-height:1; letter-spacing:-3px; color:transparent;
|
| 132 |
-webkit-text-stroke:2px rgba(255,255,255,.92); text-shadow:0 4px 14px rgba(0,0,0,.65); pointer-events:none; z-index:2;}
|
| 133 |
.poster-art:has(.rank-num)::after{content:''; position:absolute; inset:auto 0 0 0; height:46%; background:linear-gradient(transparent,rgba(0,0,0,.7)); pointer-events:none;}
|
|
|
|
| 3616 |
}
|
| 3617 |
async function openJaynesTV() {
|
| 3618 |
openPlayerShell('JaynesTV');
|
| 3619 |
+
let s = await jtvSchedule(true);
|
| 3620 |
if (!s) return statusMsg('<span style="color:#ffb4ab">' + esc(T('jtv_down')) + '</span>');
|
| 3621 |
stage.style.position = 'relative';
|
| 3622 |
+
stage.innerHTML = '<video id="vid" controls autoplay playsinline></video><div class="jtv-bar" id="jtvBar"></div>' +
|
| 3623 |
+
'<div class="jtv-ad" id="jtvAd">TANGAZO<i></i></div>' + (s.logo ? '<img class="jtv-logo" id="jtvLogo" alt="" src="' + esc(s.logo) + '">' : '');
|
| 3624 |
+
const v = document.getElementById('vid'), bar = document.getElementById('jtvBar'), adEl = document.getElementById('jtvAd');
|
| 3625 |
+
const logoEl = document.getElementById('jtvLogo'); if (logoEl) logoEl.onerror = () => logoEl.remove();
|
| 3626 |
attachPlayerEnhancements(v); ensureSound(v);
|
| 3627 |
+
const bad = new Set(); let cur = null, epg = false, hideT = null, last = null, liveOn = false, hls = null;
|
| 3628 |
const hhmm = ms => new Date(ms).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
| 3629 |
+
const kindOf = it => (it && it.kind) || 'program';
|
| 3630 |
const show = (ms) => { bar.classList.remove('hide'); clearTimeout(hideT); if (ms) hideT = setTimeout(() => { if (!epg) bar.classList.add('hide'); }, ms); };
|
| 3631 |
+
const nextProgram = n => (n.upcoming.find(u => kindOf(u.item) === 'program') || n.upcoming[0]);
|
| 3632 |
+
const stopHls = () => { if (hls) { try { hls.destroy(); } catch (_) {} hls = null; } };
|
| 3633 |
+
const playSrc = (url, off) => { // MP4 au HLS (.m3u8) — live ya studio ni HLS
|
| 3634 |
+
stopHls();
|
| 3635 |
+
const isHls = /\.m3u8(\?|$)/i.test(url);
|
| 3636 |
+
if (isHls && !v.canPlayType('application/vnd.apple.mpegurl') && window.Hls && Hls.isSupported()) {
|
| 3637 |
+
hls = new Hls(); hls.loadSource(url); hls.attachMedia(v);
|
| 3638 |
+
} else v.src = isHls ? url : url + '#t=' + Math.floor(off || 0);
|
| 3639 |
+
v.addEventListener('loadedmetadata', () => { if (!isHls && Math.abs(v.currentTime - off) > 3) v.currentTime = off; const p = v.play(); if (p && p.catch) p.catch(() => {}); }, { once: true });
|
| 3640 |
+
};
|
| 3641 |
const draw = (n, it) => {
|
| 3642 |
+
if (liveOn) {
|
| 3643 |
+
bar.innerHTML = '<div class="jtv-row"><span class="jtv-live">● LIVE</span><b>' + esc(s.live.title || 'Matangazo ya moja kwa moja') + '</b>' +
|
| 3644 |
+
'<span class="jtv-m">Studio</span></div>';
|
| 3645 |
+
adEl.classList.remove('on'); return;
|
| 3646 |
+
}
|
| 3647 |
+
const k = kindOf(it), nx = nextProgram(n);
|
| 3648 |
+
adEl.classList.toggle('on', k === 'ad');
|
| 3649 |
+
if (k === 'ad') adEl.querySelector('i').textContent = Math.max(0, Math.ceil(n.left)) + 's';
|
| 3650 |
+
const nowTitle = k === 'program' ? it.title : (nx.item.title); // wakati wa tangazo/kitambulisho: "inaanza punde"
|
| 3651 |
+
bar.innerHTML = '<div class="jtv-row"><span class="jtv-live">● LIVE</span><b>' + (k === 'program' ? '' : 'Punde: ') + esc(nowTitle) + '</b>' +
|
| 3652 |
+
(k === 'program' && it.dj ? '<span class="jtv-m">' + esc(it.dj) + '</span>' : '') +
|
| 3653 |
'<button class="jtv-b" data-j="epg">' + esc(T('jtv_schedule')) + '</button></div>' +
|
| 3654 |
+
(k === 'program' ? '<div class="jtv-m">Sasa hivi · ' + T('jtv_next') + ': ' + esc(nx.item.title) + ' · ' + hhmm(nx.at) + '</div>' : '') +
|
| 3655 |
+
(epg ? '<div class="jtv-epg">' +
|
| 3656 |
+
(k === 'program' ? '<div class="now"><span>' + hhmm(Date.now() - (it.dur - n.left) * 1000) + '</span> ' + esc(it.title) + '<span class="k">SASA</span></div>' : '') +
|
| 3657 |
+
n.upcoming.filter(u => kindOf(u.item) === 'program').map(u => '<div><span>' + hhmm(u.at) + '</span> ' + esc(u.item.title) + '</div>').join('') + '</div>' : '');
|
| 3658 |
};
|
| 3659 |
const sync = () => {
|
| 3660 |
+
const liveUrl = s.live && s.live.active && s.live.url;
|
| 3661 |
+
if (liveUrl) { // studio iko hewani: hamia live yenyewe
|
| 3662 |
+
if (!liveOn || cur !== liveUrl) { liveOn = true; cur = liveUrl; playSrc(liveUrl, 0); show(8000); }
|
| 3663 |
+
draw(null, null); return;
|
| 3664 |
+
}
|
| 3665 |
+
if (liveOn) { liveOn = false; cur = null; } // live imeisha: rudi kwenye ratiba
|
| 3666 |
const n = jtvNow(s, Date.now()), isBad = bad.has(n.idx);
|
| 3667 |
const it = isBad ? n.upcoming[0].item : n.item, off = isBad ? 0 : n.offset; // video mbovu: cheza inayofuata kutoka mwanzo
|
| 3668 |
if (cur !== it.url) {
|
| 3669 |
+
cur = it.url; playSrc(it.url, off);
|
| 3670 |
+
if (kindOf(it) === 'program') show(8000); else bar.classList.add('hide');
|
|
|
|
| 3671 |
} else if (!isBad && v.readyState > 1 && Math.abs(v.currentTime - off) > 4) v.currentTime = off; // rudi kwenye 'live'
|
| 3672 |
last = n; draw(n, it);
|
| 3673 |
};
|
| 3674 |
+
const refresh = async () => { const ns = await jtvSchedule(true); if (ns) { s = ns; sync(); } }; // kuangalia live/ratiba mpya
|
| 3675 |
+
bar.onclick = (e) => { if (e.target.closest('[data-j="epg"]') && last) { epg = !epg; draw(last, bad.has(last.idx) ? last.upcoming[0].item : last.item); show(epg ? 0 : 6000); } };
|
| 3676 |
v.addEventListener('ended', sync);
|
| 3677 |
v.addEventListener('play', sync);
|
| 3678 |
+
v.addEventListener('seeked', () => { if (!liveOn && last && Math.abs(v.currentTime - last.offset) > 6) sync(); });
|
| 3679 |
+
v.addEventListener('error', () => { if (liveOn) { s = Object.assign({}, s, { live: { active: false } }); cur = null; sync(); } else if (last) { bad.add(last.idx); sync(); } });
|
| 3680 |
v.addEventListener('click', () => show(6000));
|
| 3681 |
+
const timer = setInterval(sync, 15000), tick = setInterval(() => { if (last && !liveOn && adEl.classList.contains('on')) draw(jtvNow(s, Date.now()), jtvNow(s, Date.now()).item); }, 1000);
|
| 3682 |
+
const liveTimer = setInterval(refresh, 60000);
|
| 3683 |
+
window.__jtvStop = () => { clearInterval(timer); clearInterval(tick); clearInterval(liveTimer); clearTimeout(hideT); stopHls(); };
|
| 3684 |
sync();
|
| 3685 |
}
|
| 3686 |
|