jaynesmax commited on
Commit
90089fa
·
verified ·
1 Parent(s): 8fdf19d

Upload index.html

Browse files
Files changed (1) hide show
  1. index.html +76 -4
index.html CHANGED
@@ -114,6 +114,14 @@
114
  .poster-title{font-size:12.5px; font-weight:600; margin-top:6px; line-height:1.25;
115
  display:-webkit-box; -webkit-line-clamp:1; -webkit-box-orient:vertical; overflow:hidden;}
116
  .poster-sub{font-size:11px; color:var(--muted); margin-top:2px;}
 
 
 
 
 
 
 
 
117
  .rank-num{position:absolute; left:6px; bottom:0; font-size:58px; font-weight:900; line-height:1; letter-spacing:-3px; color:transparent;
118
  -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;}
119
  .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;}
@@ -2876,10 +2884,10 @@ const I18N_EXTRA9 = {
2876
  };
2877
  Object.keys(I18N_EXTRA9).forEach(l => { I18N[l] = Object.assign(I18N[l] || {}, I18N_EXTRA9[l]); });
2878
  const I18N_EXTRA10 = {
2879
- sw: { tr_trending: 'Zinazovuma Sasa', tr_new: 'Mpya Karibuni', tr_more: 'Zaidi za Kutazama' },
2880
- en: { tr_trending: 'Trending Now', tr_new: 'New Releases', tr_more: 'More to Watch' },
2881
- ar: { tr_trending: 'الأكثر رواجاً الآن', tr_new: 'أحدث الإصدارات', tr_more: 'المزيد للمشاهدة' },
2882
- fr: { tr_trending: 'Tendances actuelles', tr_new: 'Nouveautés', tr_more: 'À découvrir' }
2883
  };
2884
  Object.keys(I18N_EXTRA10).forEach(l => { I18N[l] = Object.assign(I18N[l] || {}, I18N_EXTRA10[l]); });
2885
  function T(k, v) { return t(k, v); }
@@ -3582,11 +3590,73 @@ async function fetchChannelPosters() {
3582
 
3583
  // Inarudisha orodha bapa (flat) ya channels zote za Live TV — inatumika
3584
  // na loadLive() (kuonyesha kwa rails) na search (kuchuja kwa query).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3585
  async function fetchAllLiveItems() {
3586
  const items = [];
3587
  let anyOk = false, lastErr = null;
3588
  const posters = await fetchChannelPosters();
3589
 
 
 
 
 
 
3590
  try {
3591
  const res = await fetch(JAYNES_MAX_TV_BASE + '/api/content/channels', {
3592
  headers: { 'Authorization': 'Bearer ' + JAYNES_MAX_TV_KEY, 'apikey': JAYNES_MAX_TV_KEY },
@@ -6031,6 +6101,7 @@ function stopMediaOnly(){
6031
  currentBlobUrls = [];
6032
  }
6033
  function stopPlayer(){
 
6034
  playerEl.classList.remove('mini');
6035
  clearUpNext();
6036
  if (document.pictureInPictureElement) { document.exitPictureInPicture().catch(()=>{}); }
@@ -6739,6 +6810,7 @@ function failPlayback(gen, showFn){
6739
  }
6740
 
6741
  async function openLiveChannel(item){
 
6742
  openPlayerShell(item.title);
6743
  liveFb = { title: item.title, used: false, pending: false };
6744
  try {
 
114
  .poster-title{font-size:12.5px; font-weight:600; margin-top:6px; line-height:1.25;
115
  display:-webkit-box; -webkit-line-clamp:1; -webkit-box-orient:vertical; overflow:hidden;}
116
  .poster-sub{font-size:11px; color:var(--muted); margin-top:2px;}
117
+ .jtv-bar{position:absolute; left:10px; right:10px; top:10px; padding:8px 10px; border-radius:12px; background:rgba(0,0,0,.6); color:#fff; font-size:13px; z-index:5; transition:opacity .3s;}
118
+ .jtv-bar.hide{opacity:0; pointer-events:none;}
119
+ .jtv-row{display:flex; gap:8px; align-items:center; flex-wrap:wrap;}
120
+ .jtv-live{background:#e5484d; border-radius:6px; padding:2px 7px; font-weight:800; font-size:11px; letter-spacing:.5px;}
121
+ .jtv-m{opacity:.8; font-size:12px; margin-top:2px;}
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;}
 
2884
  };
2885
  Object.keys(I18N_EXTRA9).forEach(l => { I18N[l] = Object.assign(I18N[l] || {}, I18N_EXTRA9[l]); });
2886
  const I18N_EXTRA10 = {
2887
+ sw: { jtv_next: 'Inayofuata', jtv_schedule: 'Ratiba', jtv_down: 'JaynesTV haipatikani kwa sasa', tr_trending: 'Zinazovuma Sasa', tr_new: 'Mpya Karibuni', tr_more: 'Zaidi za Kutazama' },
2888
+ en: { jtv_next: 'Next', jtv_schedule: 'Schedule', jtv_down: 'JaynesTV is unavailable right now', tr_trending: 'Trending Now', tr_new: 'New Releases', tr_more: 'More to Watch' },
2889
+ ar: { jtv_next: 'التالي', jtv_schedule: 'الجدول', jtv_down: 'JaynesTV غير متاح حاليًا', tr_trending: 'الأكثر رواجاً الآن', tr_new: 'أحدث الإصدارات', tr_more: 'المزيد للمشاهدة' },
2890
+ fr: { jtv_next: 'Ensuite', jtv_schedule: 'Programme', jtv_down: 'JaynesTV est indisponible', tr_trending: 'Tendances actuelles', tr_new: 'Nouveautés', tr_more: 'À découvrir' }
2891
  };
2892
  Object.keys(I18N_EXTRA10).forEach(l => { I18N[l] = Object.assign(I18N[l] || {}, I18N_EXTRA10[l]); });
2893
  function T(k, v) { return t(k, v); }
 
3590
 
3591
  // Inarudisha orodha bapa (flat) ya channels zote za Live TV — inatumika
3592
  // na loadLive() (kuonyesha kwa rails) na search (kuchuja kwa query).
3593
+ // ── JAYNESTV: live TV ya kubuni kutoka video za dataset yako (ratiba: build_jaynestv.py) ──
3594
+ const JTV_URL = 'https://huggingface.co/datasets/jaynesmaxtv/movie-file/resolve/main/jaynestv/schedule.json';
3595
+ let jtvP = null, jtvAt = 0;
3596
+ function jtvSchedule(force) { // ratiba inahifadhiwa dakika 5; ikikosekana JaynesTV haionekani
3597
+ if (!jtvP || force || Date.now() - jtvAt > 300000) {
3598
+ jtvAt = Date.now();
3599
+ jtvP = fetch(JTV_URL, { cache: 'no-cache' }).then(r => r.ok ? r.json() : null)
3600
+ .then(s => (s && Array.isArray(s.items) && s.items.length && s.total > 0) ? s : null).catch(() => null);
3601
+ }
3602
+ return jtvP;
3603
+ }
3604
+ function jtvNow(s, nowMs) { // kipindi cha sasa + vinavyofuata, kwa saa: kila mtazamaji anaona kile kile wakati ule ule
3605
+ let pos = ((((nowMs - (Date.parse(s.epoch) || 0)) / 1000) % s.total) + s.total) % s.total, i = 0;
3606
+ while (i < s.items.length - 1 && pos >= s.items[i].dur) { pos -= s.items[i].dur; i++; }
3607
+ const upcoming = []; let at = nowMs + (s.items[i].dur - pos) * 1000;
3608
+ for (let k = 1; k <= 8; k++) { const it = s.items[(i + k) % s.items.length]; upcoming.push({ item: it, at }); at += it.dur * 1000; }
3609
+ return { idx: i, item: s.items[i], offset: pos, left: s.items[i].dur - pos, upcoming };
3610
+ }
3611
+ async function openJaynesTV() {
3612
+ openPlayerShell('JaynesTV');
3613
+ const s = await jtvSchedule(true);
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
+ const v = document.getElementById('vid'), bar = document.getElementById('jtvBar');
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
+ const nx = n.upcoming[0];
3624
+ bar.innerHTML = '<div class="jtv-row"><span class="jtv-live">● LIVE</span><b>' + esc(it.title) + '</b>' + (it.dj ? '<span class="jtv-m">' + esc(it.dj) + '</span>' : '') +
3625
+ '<button class="jtv-b" data-j="epg">' + esc(T('jtv_schedule')) + '</button></div>' +
3626
+ '<div class="jtv-m">' + esc(T('jtv_next')) + ': ' + esc(nx.item.title) + ' · ' + hhmm(nx.at) + '</div>' +
3627
+ (epg ? '<div class="jtv-epg">' + n.upcoming.map(u => '<div><span>' + hhmm(u.at) + '</span> ' + esc(u.item.title) + '</div>').join('') + '</div>' : '');
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; v.src = it.url + '#t=' + Math.floor(off);
3634
+ v.addEventListener('loadedmetadata', () => { if (Math.abs(v.currentTime - off) > 3) v.currentTime = off; const p = v.play(); if (p && p.catch) p.catch(() => {}); }, { once: true });
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
+ bar.onclick = (e) => { if (e.target.closest('[data-j="epg"]')) { epg = !epg; draw(last, last.item); show(epg ? 0 : 6000); } };
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
+ window.__jtvStop = () => { clearInterval(timer); clearTimeout(hideT); };
3647
+ sync();
3648
+ }
3649
+
3650
  async function fetchAllLiveItems() {
3651
  const items = [];
3652
  let anyOk = false, lastErr = null;
3653
  const posters = await fetchChannelPosters();
3654
 
3655
+ try { // JaynesTV (ikiwa ratiba ipo): kadi inaonyesha kipindi kinachochezwa sasa
3656
+ const js = await jtvSchedule();
3657
+ if (js) { const n = jtvNow(js, Date.now()); items.push({ id: 'jaynestv', kind: 'live', title: n.item.title, poster: n.item.poster || js.logo || null, subtitle: 'JaynesTV', imgFallbackOn: true }); }
3658
+ } catch (_) {}
3659
+
3660
  try {
3661
  const res = await fetch(JAYNES_MAX_TV_BASE + '/api/content/channels', {
3662
  headers: { 'Authorization': 'Bearer ' + JAYNES_MAX_TV_KEY, 'apikey': JAYNES_MAX_TV_KEY },
 
6101
  currentBlobUrls = [];
6102
  }
6103
  function stopPlayer(){
6104
+ if (window.__jtvStop) { try { window.__jtvStop(); } catch (_) {} window.__jtvStop = null; }
6105
  playerEl.classList.remove('mini');
6106
  clearUpNext();
6107
  if (document.pictureInPictureElement) { document.exitPictureInPicture().catch(()=>{}); }
 
6810
  }
6811
 
6812
  async function openLiveChannel(item){
6813
+ if (String(item.id) === 'jaynestv') return openJaynesTV();
6814
  openPlayerShell(item.title);
6815
  liveFb = { title: item.title, used: false, pending: false };
6816
  try {