Spaces:
Running
Running
Upload 2 files
Browse files- config.js +2 -1
- index.html +54 -102
config.js
CHANGED
|
@@ -21,7 +21,8 @@ window.JM_CONFIG = Object.freeze({
|
|
| 21 |
TRANSLATED_FN: 'movie-zilizotafsriwa',
|
| 22 |
|
| 23 |
// Michezo (fawa-live) — mradi mpya (function imewekwa bila JWT, key haihitajiki lakini inatumwa tu)
|
| 24 |
-
FAWA_URL: 'https://
|
|
|
|
| 25 |
FAWA_KEY: 'sb_publishable_0XQ9PQ0CRJnC0A_Tz18oGg_fSONkxB3',
|
| 26 |
|
| 27 |
// Web Push (VAPID) — funguo YA UMMA tu. Private key iwe Supabase secrets pekee.
|
|
|
|
| 21 |
TRANSLATED_FN: 'movie-zilizotafsriwa',
|
| 22 |
|
| 23 |
// Michezo (fawa-live) — mradi mpya (function imewekwa bila JWT, key haihitajiki lakini inatumwa tu)
|
| 24 |
+
FAWA_URL: 'https://fawa-live.jaynes-max.workers.dev',
|
| 25 |
+
FAWA_FALLBACK_URL: 'https://kqqdsumvvbuvyoneanku.supabase.co', // akiba: Supabase fawa-live (hutumika Cloudflare ikishindwa)
|
| 26 |
FAWA_KEY: 'sb_publishable_0XQ9PQ0CRJnC0A_Tz18oGg_fSONkxB3',
|
| 27 |
|
| 28 |
// Web Push (VAPID) — funguo YA UMMA tu. Private key iwe Supabase secrets pekee.
|
index.html
CHANGED
|
@@ -1226,13 +1226,6 @@
|
|
| 1226 |
/* Nafasi ya kusogeza (scroll) ili kipengele chenye focus kisifichwe na topbar / nav */
|
| 1227 |
.poster,.cw-card,.tab-chip,.settings-row,.dq-item,.tr-ep,.cast-item{scroll-margin:110px 3vw 110px 3vw;}
|
| 1228 |
</style>
|
| 1229 |
-
<style>
|
| 1230 |
-
.tr-filter{margin:0 0 10px;}
|
| 1231 |
-
.tr-filter .tabs{padding:10px 16px 4px;}
|
| 1232 |
-
.tr-filter .tr-dj-row{padding-top:4px;}
|
| 1233 |
-
.tr-filter .tr-dj-row .tab-chip{font-size:12px; padding:6px 12px;}
|
| 1234 |
-
.tr-filter-note{padding:4px 16px 6px; font-size:12px; color:var(--muted);}
|
| 1235 |
-
</style>
|
| 1236 |
</head>
|
| 1237 |
<body>
|
| 1238 |
<div id="gate">
|
|
@@ -1486,6 +1479,8 @@ const SEARCH_FN_URL = JM_CONFIG.CONTENT_URL + '/functions/v1/search';
|
|
| 1486 |
// fawa-live (mechi/michezo moja kwa moja) — function tofauti, key ile ile
|
| 1487 |
const FAWA_BASE = (JM_CONFIG.FAWA_URL || JM_CONFIG.CONTENT_URL) + '/functions/v1/fawa-live';
|
| 1488 |
const FAWA_KEY = JM_CONFIG.FAWA_KEY || SUPABASE_ANON_KEY;
|
|
|
|
|
|
|
| 1489 |
|
| 1490 |
// jaynes-max-tv (LIVE TV — channel za kawaida, DASH/HLS + ClearKey) — function yetu wenyewe
|
| 1491 |
const JAYNES_MAX_TV_BASE = JM_CONFIG.CONTENT_URL + '/functions/v1/jaynes-max-tv';
|
|
@@ -1503,24 +1498,34 @@ const PIXTVMAX_KEY = SUPABASE_ANON_KEY;
|
|
| 1503 |
const TRAILER_BASE = JM_CONFIG.CONTENT_URL + '/functions/v1/movie-trailer';
|
| 1504 |
const TRAILER_KEY = SUPABASE_ANON_KEY;
|
| 1505 |
|
| 1506 |
-
//
|
| 1507 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1508 |
function normDj(raw) { // jina safi la DJ (bila maneno ya ziada na bila tofauti za herufi kubwa/ndogo)
|
| 1509 |
const s = String(raw || '').replace(/\(.*?\)|\[.*?\]/g, ' ').replace(TR_DJ_NOISE, ' ').replace(/[|_\-\u2013\u2014:,.]+/g, ' ').replace(/\s+/g, ' ').trim();
|
| 1510 |
if (!s || /^dj$/i.test(s)) return '';
|
| 1511 |
-
|
| 1512 |
-
return TR_DJ_ALIAS[name.toLowerCase()] || name;
|
| 1513 |
}
|
| 1514 |
-
// Majina tofauti ya DJ mmoja (andika kwa herufi ndogo upande wa kushoto)
|
| 1515 |
-
const TR_DJ_ALIAS = { 'dj blaki': 'DJ Black', 'blaki': 'DJ Black', 'black': 'DJ Black' };
|
| 1516 |
// Model iliyofunzwa (train.py -> HF Model). Ikikosekana, app inafanya kazi kama kawaida (Trending = mpya zaidi).
|
| 1517 |
-
const TRANSLATED_ALL = JM_CONFIG.CONTENT_URL + '/functions/v1/movie-translated-all';
|
| 1518 |
const TR_MODEL_URL = 'https://huggingface.co/jaynesmaxtv/translated-recommender/resolve/main/model.json';
|
| 1519 |
let trModelP = null;
|
| 1520 |
function loadTrModel() {
|
| 1521 |
if (!trModelP) trModelP = fetch(TR_MODEL_URL, { cache: 'no-cache' }).then(r => r.ok ? r.json() : null).catch(() => null);
|
| 1522 |
return trModelP;
|
| 1523 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1524 |
// Rails za mlalo: Trending Now (Top 10, kwa model au mpya zaidi) -> New Releases -> kila DJ (DJ Mack kwanza) -> Series -> More to Watch
|
| 1525 |
function buildTranslatedSections(items, model) {
|
| 1526 |
const pop = (model && model.popularity) || {}, sections = [], covered = new Set();
|
|
@@ -1543,11 +1548,13 @@ function buildTranslatedSections(items, model) {
|
|
| 1543 |
if (more.length) sections.push({ title: t('tr_more'), items: more.slice(0, 40) });
|
| 1544 |
return sections;
|
| 1545 |
}
|
| 1546 |
-
async function fetchTranslatedMovie(id) { // -> { ok, error?, movie } kutoka
|
| 1547 |
-
const
|
| 1548 |
-
const
|
| 1549 |
-
|
| 1550 |
-
|
|
|
|
|
|
|
| 1551 |
}
|
| 1552 |
const PIXTVMAX_CATEGORY_SW = {
|
| 1553 |
'Sports': 'cat_sports', 'Movie Channels': 'cat_movies', 'News': 'cat_news',
|
|
@@ -2945,13 +2952,6 @@ const I18N_EXTRA10 = {
|
|
| 2945 |
fr: { jtv_next: 'Ensuite', jtv_schedule: 'Programme', jtv_down: 'JaynesTV est indisponible', tr_trending: 'Tendances actuelles', tr_new: 'Nouveautés', tr_more: 'À découvrir' }
|
| 2946 |
};
|
| 2947 |
Object.keys(I18N_EXTRA10).forEach(l => { I18N[l] = Object.assign(I18N[l] || {}, I18N_EXTRA10[l]); });
|
| 2948 |
-
const I18N_EXTRA_TRF = {
|
| 2949 |
-
sw: { trf_all: 'Zote', trf_new: 'Mpya', trf_trend_movie: 'Movie Zinazovuma', trf_trend_series: 'Series Zinazovuma', trf_movie: 'Movie', trf_series: 'Series', trf_dj: 'Kwa DJ', trf_no_trend: 'Bado hakuna takwimu za kutosha za zinazovuma, tunaonyesha mpya zaidi kwanza.' },
|
| 2950 |
-
en: { trf_all: 'All', trf_new: 'New Releases', trf_trend_movie: 'Trending Movies', trf_trend_series: 'Trending Series', trf_movie: 'Movies', trf_series: 'Series', trf_dj: 'By DJ', trf_no_trend: 'Not enough viewing data yet, showing newest first.' },
|
| 2951 |
-
ar: { trf_all: 'الكل', trf_new: 'الجديد', trf_trend_movie: 'أفلام رائجة', trf_trend_series: 'مسلسلات رائجة', trf_movie: 'أفلام', trf_series: 'مسلسلات', trf_dj: 'حسب DJ', trf_no_trend: 'لا توجد بيانات مشاهدة كافية بعد، نعرض الأحدث أولاً.' },
|
| 2952 |
-
fr: { trf_all: 'Tout', trf_new: 'Nouveautés', trf_trend_movie: 'Films tendance', trf_trend_series: 'Séries tendance', trf_movie: 'Films', trf_series: 'Séries', trf_dj: 'Par DJ', trf_no_trend: "Pas encore assez de données, les plus récents d'abord." }
|
| 2953 |
-
};
|
| 2954 |
-
Object.keys(I18N_EXTRA_TRF).forEach(l => { I18N[l] = Object.assign(I18N[l] || {}, I18N_EXTRA_TRF[l]); });
|
| 2955 |
function T(k, v) { return t(k, v); }
|
| 2956 |
// Maandishi yaliyoandikwa kwenye HTML kupitia data-i18n / data-i18n-ph / data-i18n-title
|
| 2957 |
function applyI18nAttrs() {
|
|
@@ -3835,78 +3835,25 @@ async function fetchTranslatedListRaw() {
|
|
| 3835 |
id: idPrefix + m.id, kind: 'translated', title: m.title,
|
| 3836 |
poster: m.thumb_url || m.poster_url || null, imgFallbackOn: true,
|
| 3837 |
subtitle: normDj(m.dj) || (m.category === 'series' ? 'Series' : 'Movie'), isSeries: m.category === 'series',
|
| 3838 |
-
added: m.created_at || m.updated_at || '',
|
| 3839 |
});
|
| 3840 |
-
const
|
| 3841 |
-
|
| 3842 |
-
|
| 3843 |
-
|
| 3844 |
-
|
| 3845 |
-
|
| 3846 |
-
|
| 3847 |
-
|
| 3848 |
-
|
| 3849 |
-
|
| 3850 |
-
|
| 3851 |
-
|
| 3852 |
-
|
| 3853 |
-
const
|
| 3854 |
-
|
| 3855 |
-
|
| 3856 |
-
|
| 3857 |
-
|
| 3858 |
-
});
|
| 3859 |
-
return Object.values(g).sort((a, b) => (/mack/i.test(b.name) - /mack/i.test(a.name)) || b.list.length - a.list.length || a.name.localeCompare(b.name));
|
| 3860 |
-
}
|
| 3861 |
-
function trByNewest(list) { // mpya -> zamani kwa tarehe ya kuongezwa; zisizo na tarehe zinabaki kwa mpangilio wa seva
|
| 3862 |
-
return list.map((it, i) => [it, i])
|
| 3863 |
-
.sort((a, b) => (Date.parse(b[0].added) || 0) - (Date.parse(a[0].added) || 0) || a[1] - b[1]).map(x => x[0]);
|
| 3864 |
-
}
|
| 3865 |
-
function trTrending(list, model) { // kwa uchezaji halisi (model); zikipungua, zinaongezwa mpya zaidi bila namba
|
| 3866 |
-
const pop = (model && model.popularity) || {};
|
| 3867 |
-
const hot = list.filter(it => pop[it.id] > 0).sort((a, b) => pop[b.id] - pop[a.id]);
|
| 3868 |
-
const ranked = hot.slice(0, 30).map((it, i) => ({ ...it, rank: i < 10 ? i + 1 : undefined }));
|
| 3869 |
-
if (ranked.length < 10) {
|
| 3870 |
-
const have = new Set(ranked.map(it => it.id));
|
| 3871 |
-
trByNewest(list).forEach(it => { if (ranked.length < 30 && !have.has(it.id)) ranked.push(it); });
|
| 3872 |
-
}
|
| 3873 |
-
return { items: ranked, fallback: hot.length === 0 };
|
| 3874 |
-
}
|
| 3875 |
-
function renderTranslatedView() {
|
| 3876 |
-
if (!trCache) return;
|
| 3877 |
-
const { items, model } = trCache;
|
| 3878 |
-
const movies = items.filter(it => !it.isSeries), series = items.filter(it => it.isSeries);
|
| 3879 |
-
const groups = trDjGroups(items);
|
| 3880 |
-
if (!TR_FILTERS.includes(trFilter)) trFilter = 'all';
|
| 3881 |
-
if (trFilter === 'dj' && !groups.some(g => g.name === trDj)) trDj = groups[0] ? groups[0].name : '';
|
| 3882 |
-
let sections, grid = true, note = '';
|
| 3883 |
-
if (trFilter === 'new') sections = [{ title: t('tr_new'), items: trByNewest(items) }];
|
| 3884 |
-
else if (trFilter === 'trend_movie' || trFilter === 'trend_series') {
|
| 3885 |
-
const r = trTrending(trFilter === 'trend_movie' ? movies : series, model);
|
| 3886 |
-
if (r.fallback) note = t('trf_no_trend');
|
| 3887 |
-
sections = [{ title: t('trf_' + trFilter), items: r.items }];
|
| 3888 |
-
}
|
| 3889 |
-
else if (trFilter === 'movie') sections = [{ title: t('trf_movie'), items: trByNewest(movies) }];
|
| 3890 |
-
else if (trFilter === 'series') sections = [{ title: t('trf_series'), items: trByNewest(series) }];
|
| 3891 |
-
else if (trFilter === 'dj') {
|
| 3892 |
-
const g = groups.find(x => x.name === trDj);
|
| 3893 |
-
sections = g ? [{ title: g.name, items: trByNewest(g.list).map(it => ({ ...it, subtitle: it.isSeries ? 'Series' : '' })) }] : [];
|
| 3894 |
-
}
|
| 3895 |
-
else { sections = buildTranslatedSections(items, model); grid = false; }
|
| 3896 |
-
renderRails(sections, grid);
|
| 3897 |
-
const bar = document.createElement('div');
|
| 3898 |
-
bar.className = 'tr-filter';
|
| 3899 |
-
bar.innerHTML = `<div class="tabs">${TR_FILTERS.map(f => `<div class="tab-chip${f === trFilter ? ' active' : ''}" data-trf="${f}">${esc(t('trf_' + f))}</div>`).join('')}</div>` +
|
| 3900 |
-
(trFilter === 'dj' && groups.length ? `<div class="tabs tr-dj-row">${groups.map(g => `<div class="tab-chip${g.name === trDj ? ' active' : ''}" data-trdj="${esc(g.name)}">${esc(g.name)} · ${g.list.length}</div>`).join('')}</div>` : '') +
|
| 3901 |
-
(note ? `<div class="tr-filter-note">${esc(note)}</div>` : '');
|
| 3902 |
-
bar.onclick = (e) => {
|
| 3903 |
-
const c = e.target.closest('.tab-chip'); if (!c) return;
|
| 3904 |
-
if (c.dataset.trf) trFilter = c.dataset.trf; else if (c.dataset.trdj != null) trDj = c.dataset.trdj; else return;
|
| 3905 |
-
try { sessionStorage.setItem('jm_tr_filter', trFilter); sessionStorage.setItem('jm_tr_dj', trDj); } catch (_) {}
|
| 3906 |
-
renderTranslatedView();
|
| 3907 |
-
};
|
| 3908 |
-
railsEl.insertBefore(bar, railsEl.firstChild);
|
| 3909 |
-
bar.querySelectorAll('.tab-chip.active').forEach(c => { const row = c.parentElement; row.scrollLeft = c.offsetLeft - (row.clientWidth - c.offsetWidth) / 2; });
|
| 3910 |
}
|
| 3911 |
|
| 3912 |
async function loadTranslated() {
|
|
@@ -3914,8 +3861,7 @@ async function loadTranslated() {
|
|
| 3914 |
renderSkeletonRails(2);
|
| 3915 |
try {
|
| 3916 |
const [items, model] = await Promise.all([fetchTranslatedList(), loadTrModel()]);
|
| 3917 |
-
|
| 3918 |
-
renderTranslatedView();
|
| 3919 |
} catch (e) {
|
| 3920 |
railsEl.innerHTML = '';
|
| 3921 |
railsEl.appendChild(stateBox(t('err_failed', { e: esc(e.message || t('err_generic')) }), true, loadTranslated));
|
|
@@ -4147,6 +4093,7 @@ function playTranslatedUrl(url, title, playlist, meta) {
|
|
| 4147 |
v.addEventListener('ended', () => { const c = window.__pcPlaylist; if (c && typeof c.next === 'function') c.next(); });
|
| 4148 |
// Tangazo la video kwanza (likishindikana/likiruka, filamu inaanza mara moja)
|
| 4149 |
v.src = url;
|
|
|
|
| 4150 |
// Route ya player: #/translated/ID/play?se=..&ep=..
|
| 4151 |
try {
|
| 4152 |
const id = currentTranslatedDetail && currentTranslatedDetail.id;
|
|
@@ -6396,10 +6343,15 @@ async function playHlsUrl(url, opts = {}) {
|
|
| 6396 |
|
| 6397 |
async function openFawaMatch(item) {
|
| 6398 |
openPlayerShell(item.title);
|
| 6399 |
-
|
| 6400 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6401 |
try {
|
| 6402 |
-
|
| 6403 |
const ct = (r.headers.get('content-type') || '').toLowerCase();
|
| 6404 |
if (!r.ok) throw new Error(T('err_generic') + ' ' + r.status);
|
| 6405 |
|
|
|
|
| 1226 |
/* Nafasi ya kusogeza (scroll) ili kipengele chenye focus kisifichwe na topbar / nav */
|
| 1227 |
.poster,.cw-card,.tab-chip,.settings-row,.dq-item,.tr-ep,.cast-item{scroll-margin:110px 3vw 110px 3vw;}
|
| 1228 |
</style>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1229 |
</head>
|
| 1230 |
<body>
|
| 1231 |
<div id="gate">
|
|
|
|
| 1479 |
// fawa-live (mechi/michezo moja kwa moja) — function tofauti, key ile ile
|
| 1480 |
const FAWA_BASE = (JM_CONFIG.FAWA_URL || JM_CONFIG.CONTENT_URL) + '/functions/v1/fawa-live';
|
| 1481 |
const FAWA_KEY = JM_CONFIG.FAWA_KEY || SUPABASE_ANON_KEY;
|
| 1482 |
+
// Seva ya akiba (Supabase) — inatumika TU pale Worker (Cloudflare) inaposhindwa kwa chaneli fulani
|
| 1483 |
+
const FAWA_BASE_FB = JM_CONFIG.FAWA_FALLBACK_URL ? JM_CONFIG.FAWA_FALLBACK_URL + '/functions/v1/fawa-live' : null;
|
| 1484 |
|
| 1485 |
// jaynes-max-tv (LIVE TV — channel za kawaida, DASH/HLS + ClearKey) — function yetu wenyewe
|
| 1486 |
const JAYNES_MAX_TV_BASE = JM_CONFIG.CONTENT_URL + '/functions/v1/jaynes-max-tv';
|
|
|
|
| 1498 |
const TRAILER_BASE = JM_CONFIG.CONTENT_URL + '/functions/v1/movie-trailer';
|
| 1499 |
const TRAILER_KEY = SUPABASE_ANON_KEY;
|
| 1500 |
|
| 1501 |
+
// movie-zilizotafsiriwa — mradi TOFAUTI (DJ movies/series), function ya umma pekee (GET)
|
| 1502 |
+
const TRANSLATED_BASE = JM_CONFIG.TRANSLATED_URL + '/functions/v1/' + JM_CONFIG.TRANSLATED_FN;
|
| 1503 |
+
const TRANSLATED_KEY = JM_CONFIG.TRANSLATED_KEY;
|
| 1504 |
+
// movie-zilizotafsriwa2 — filamu zenye MP4 (draft/public kutoka admin); project ile ile ya CONTENT_URL.
|
| 1505 |
+
// Id zake zinaanza na 'n2-' ili zisigongane na za chanzo cha zamani.
|
| 1506 |
+
const TRANSLATED2_BASE = JM_CONFIG.CONTENT_URL + '/functions/v1/movie-zilizotafsriwa2';
|
| 1507 |
+
const TR2_PREFIX = 'n2-';
|
| 1508 |
+
const TR_DJ_NOISE = /\b(movies?|filamu|zote|zilizotafsiriwa|zilizotafsiri|imetafsiriwa|iliyotafsiriwa|tafsiriwa|kiswahili|swahili|kwa|mpya|kali|translated|new|all|series|full)\b/gi;
|
| 1509 |
function normDj(raw) { // jina safi la DJ (bila maneno ya ziada na bila tofauti za herufi kubwa/ndogo)
|
| 1510 |
const s = String(raw || '').replace(/\(.*?\)|\[.*?\]/g, ' ').replace(TR_DJ_NOISE, ' ').replace(/[|_\-\u2013\u2014:,.]+/g, ' ').replace(/\s+/g, ' ').trim();
|
| 1511 |
if (!s || /^dj$/i.test(s)) return '';
|
| 1512 |
+
return s.split(' ').map(w => /^dj$/i.test(w) ? 'DJ' : w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(' ');
|
|
|
|
| 1513 |
}
|
|
|
|
|
|
|
| 1514 |
// Model iliyofunzwa (train.py -> HF Model). Ikikosekana, app inafanya kazi kama kawaida (Trending = mpya zaidi).
|
|
|
|
| 1515 |
const TR_MODEL_URL = 'https://huggingface.co/jaynesmaxtv/translated-recommender/resolve/main/model.json';
|
| 1516 |
let trModelP = null;
|
| 1517 |
function loadTrModel() {
|
| 1518 |
if (!trModelP) trModelP = fetch(TR_MODEL_URL, { cache: 'no-cache' }).then(r => r.ok ? r.json() : null).catch(() => null);
|
| 1519 |
return trModelP;
|
| 1520 |
}
|
| 1521 |
+
function logTrPlay(id) { // rekodi uchezaji bila kuzuia player; kitambulisho cha kifaa ni cha nasibu (hakuna jina/email)
|
| 1522 |
+
if (!id) return;
|
| 1523 |
+
let dev = '';
|
| 1524 |
+
try { dev = localStorage.getItem('jm_dev') || ''; if (!dev) { dev = (crypto.randomUUID && crypto.randomUUID()) || (Date.now() + '-' + Math.random().toString(36).slice(2)); localStorage.setItem('jm_dev', dev); } } catch (_) {}
|
| 1525 |
+
fetch(TRANSLATED2_BASE + '?action=play', { method: 'POST', keepalive: true,
|
| 1526 |
+
headers: { apikey: SUPABASE_ANON_KEY, 'Content-Type': 'application/json' },
|
| 1527 |
+
body: JSON.stringify({ movie_id: String(id), device_id: dev }) }).catch(() => {});
|
| 1528 |
+
}
|
| 1529 |
// Rails za mlalo: Trending Now (Top 10, kwa model au mpya zaidi) -> New Releases -> kila DJ (DJ Mack kwanza) -> Series -> More to Watch
|
| 1530 |
function buildTranslatedSections(items, model) {
|
| 1531 |
const pop = (model && model.popularity) || {}, sections = [], covered = new Set();
|
|
|
|
| 1548 |
if (more.length) sections.push({ title: t('tr_more'), items: more.slice(0, 40) });
|
| 1549 |
return sections;
|
| 1550 |
}
|
| 1551 |
+
async function fetchTranslatedMovie(id) { // -> { ok, error?, movie } kutoka chanzo sahihi
|
| 1552 |
+
const isNew = String(id).startsWith(TR2_PREFIX);
|
| 1553 |
+
const url = isNew ? TRANSLATED2_BASE + '?id=' + encodeURIComponent(String(id).slice(TR2_PREFIX.length))
|
| 1554 |
+
: TRANSLATED_BASE + '?id=' + encodeURIComponent(id);
|
| 1555 |
+
const r = await fetch(url, { headers: { apikey: isNew ? SUPABASE_ANON_KEY : TRANSLATED_KEY } });
|
| 1556 |
+
const d = await r.json();
|
| 1557 |
+
return isNew ? { ok: r.ok && !d.error, error: d.error, movie: d } : d;
|
| 1558 |
}
|
| 1559 |
const PIXTVMAX_CATEGORY_SW = {
|
| 1560 |
'Sports': 'cat_sports', 'Movie Channels': 'cat_movies', 'News': 'cat_news',
|
|
|
|
| 2952 |
fr: { jtv_next: 'Ensuite', jtv_schedule: 'Programme', jtv_down: 'JaynesTV est indisponible', tr_trending: 'Tendances actuelles', tr_new: 'Nouveautés', tr_more: 'À découvrir' }
|
| 2953 |
};
|
| 2954 |
Object.keys(I18N_EXTRA10).forEach(l => { I18N[l] = Object.assign(I18N[l] || {}, I18N_EXTRA10[l]); });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2955 |
function T(k, v) { return t(k, v); }
|
| 2956 |
// Maandishi yaliyoandikwa kwenye HTML kupitia data-i18n / data-i18n-ph / data-i18n-title
|
| 2957 |
function applyI18nAttrs() {
|
|
|
|
| 3835 |
id: idPrefix + m.id, kind: 'translated', title: m.title,
|
| 3836 |
poster: m.thumb_url || m.poster_url || null, imgFallbackOn: true,
|
| 3837 |
subtitle: normDj(m.dj) || (m.category === 'series' ? 'Series' : 'Movie'), isSeries: m.category === 'series',
|
|
|
|
| 3838 |
});
|
| 3839 |
+
const oldP = (async () => { // chanzo cha zamani
|
| 3840 |
+
const r = await fetch(TRANSLATED_BASE + '?limit=100', { headers: { apikey: TRANSLATED_KEY } });
|
| 3841 |
+
if (!r.ok) throw new Error(T('err_generic') + ' ' + r.status);
|
| 3842 |
+
const d = await r.json();
|
| 3843 |
+
if (!d.ok) throw new Error(d.error || 'Seva imekataa ombi.');
|
| 3844 |
+
return (d.movies || []).map(m => card(m, ''));
|
| 3845 |
+
})();
|
| 3846 |
+
const newP = (async () => { // movie-zilizotafsriwa2: public tu (seva inachuja)
|
| 3847 |
+
const r = await fetch(TRANSLATED2_BASE, { headers: { apikey: SUPABASE_ANON_KEY } });
|
| 3848 |
+
if (!r.ok) throw new Error(T('err_generic') + ' ' + r.status);
|
| 3849 |
+
const d = await r.json();
|
| 3850 |
+
return (Array.isArray(d) ? d : []).map(m => card(m, TR2_PREFIX));
|
| 3851 |
+
})();
|
| 3852 |
+
const [a, b] = await Promise.allSettled([oldP, newP]);
|
| 3853 |
+
if (a.status === 'rejected' && b.status === 'rejected') throw a.reason;
|
| 3854 |
+
const fresh = b.status === 'fulfilled' ? b.value : [], older = a.status === 'fulfilled' ? a.value : [];
|
| 3855 |
+
const seen = new Set(fresh.map(x => String(x.title || '').trim().toLowerCase()));
|
| 3856 |
+
return fresh.concat(older.filter(x => !seen.has(String(x.title || '').trim().toLowerCase())));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3857 |
}
|
| 3858 |
|
| 3859 |
async function loadTranslated() {
|
|
|
|
| 3861 |
renderSkeletonRails(2);
|
| 3862 |
try {
|
| 3863 |
const [items, model] = await Promise.all([fetchTranslatedList(), loadTrModel()]);
|
| 3864 |
+
renderRails(buildTranslatedSections(items, model), false);
|
|
|
|
| 3865 |
} catch (e) {
|
| 3866 |
railsEl.innerHTML = '';
|
| 3867 |
railsEl.appendChild(stateBox(t('err_failed', { e: esc(e.message || t('err_generic')) }), true, loadTranslated));
|
|
|
|
| 4093 |
v.addEventListener('ended', () => { const c = window.__pcPlaylist; if (c && typeof c.next === 'function') c.next(); });
|
| 4094 |
// Tangazo la video kwanza (likishindikana/likiruka, filamu inaanza mara moja)
|
| 4095 |
v.src = url;
|
| 4096 |
+
try { logTrPlay(currentTranslatedDetail && currentTranslatedDetail.id); } catch (_) {}
|
| 4097 |
// Route ya player: #/translated/ID/play?se=..&ep=..
|
| 4098 |
try {
|
| 4099 |
const id = currentTranslatedDetail && currentTranslatedDetail.id;
|
|
|
|
| 6343 |
|
| 6344 |
async function openFawaMatch(item) {
|
| 6345 |
openPlayerShell(item.title);
|
| 6346 |
+
// Jaribu Worker (Cloudflare) kwanza; ikishindwa (mf. 502) jaribu Supabase
|
| 6347 |
+
let url, opts, r = null;
|
| 6348 |
+
for (const b of [FAWA_BASE, FAWA_BASE_FB].filter(Boolean)) {
|
| 6349 |
+
url = b + '/proxy?slug=' + encodeURIComponent(item.id);
|
| 6350 |
+
opts = { authHeader: 'Bearer ' + FAWA_KEY, sameOriginBase: b };
|
| 6351 |
+
try { r = await fetch(url, { headers: { Authorization: opts.authHeader } }); if (r.ok) break; } catch (_) { r = null; }
|
| 6352 |
+
}
|
| 6353 |
try {
|
| 6354 |
+
if (!r) throw new Error(T('err_generic'));
|
| 6355 |
const ct = (r.headers.get('content-type') || '').toLowerCase();
|
| 6356 |
if (!r.ok) throw new Error(T('err_generic') + ' ' + r.status);
|
| 6357 |
|