Spaces:
Running
Running
File size: 4,346 Bytes
ba1e29d 6a88eb7 ba1e29d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | // dl-sw.js β downloads za background (Background Fetch) kwa JAYNES MAX
// Weka mstari huu JUU kabisa kwenye sw.js yako: importScripts('dl-sw.js');
const JM_OFFLINE_DIR = 'jm-offline'; // lazima iwe sawa na OFFLINE_DIR kwenye index.html
async function jmDir(){ const root = await navigator.storage.getDirectory(); return root.getDirectoryHandle(JM_OFFLINE_DIR, { create: true }); }
async function jmReadMeta(dir, oid){
try { const fh = await dir.getFileHandle(oid + '.json'); return JSON.parse(await (await fh.getFile()).text()); } catch (_) { return null; }
}
async function jmWriteMeta(dir, oid, obj){
const fh = await dir.getFileHandle(oid + '.json', { create: true });
const w = await fh.createWritable(); await w.write(JSON.stringify(obj)); await w.close();
}
async function jmBroadcast(msg){
const all = await self.clients.matchAll({ type: 'window', includeUncontrolled: true });
all.forEach(c => c.postMessage(msg));
}
async function jmOpenDownloads(){
const all = await self.clients.matchAll({ type: 'window', includeUncontrolled: true });
for (const c of all) {
if ('focus' in c) { try { await c.focus(); } catch (_) {} c.postMessage({ type: 'jm-open-downloads' }); return; }
}
await self.clients.openWindow('./?nav=downloads');
}
// Ukurasa unauliza "je dl-sw.js ipo?"
self.addEventListener('message', (e) => {
if (e.data && e.data.type === 'jm-ping' && e.source) e.source.postMessage({ type: 'jm-pong' });
});
// Download imekamilika (hata app ikiwa imefungwa): hamisha faili kwenda OPFS
self.addEventListener('backgroundfetchsuccess', (event) => {
const bgf = event.registration;
event.waitUntil((async () => {
const oid = bgf.id.replace(/^jm-/, '');
const dir = await jmDir();
const meta = (await jmReadMeta(dir, oid)) || { id: oid, title: 'Video' };
let ok = false;
try {
const records = await bgf.matchAll();
const res = await records[0].responseReady;
const ct = (res.headers.get('content-type') || '').toLowerCase();
if (!res.ok || !res.body || /mpegurl|text\/|html|json/.test(ct)) throw new Error('not_direct_video');
const fh = await dir.getFileHandle(oid + '.bin', { create: true });
const w = await fh.createWritable();
await res.body.pipeTo(w); // stream β haitumii RAM
const size = (await fh.getFile()).size;
// Thibitisha ukubwa: usikubali "imehifadhiwa" kwa faili lililokatika nusu
let expected = 0;
const cr = /bytes\s+\d+-\d+\/(\d+)/i.exec(res.headers.get('content-range') || '');
if (cr) expected = Number(cr[1]);
else if (!res.headers.get('content-encoding')) expected = Number(res.headers.get('content-length')) || 0;
if (!size) throw new Error('empty_file');
if (expected && size !== expected) throw new Error('size_mismatch');
if (bgf.downloadTotal && size < bgf.downloadTotal) throw new Error('incomplete');
await jmWriteMeta(dir, oid, { ...meta, state: 'done', size });
ok = true;
} catch (e) {
try { await dir.removeEntry(oid + '.bin'); } catch (_) {}
await jmWriteMeta(dir, oid, { ...meta, state: 'failed', reason: String((e && e.message) || e) });
}
try { event.updateUI({ title: (meta.title || 'Video') + (ok ? ' β imehifadhiwa β' : ' β imeshindwa') }); } catch (_) {}
await jmBroadcast({ type: 'jm-bg-update', bgId: bgf.id });
})());
});
async function jmMarkFailed(bgf, reason){
const oid = bgf.id.replace(/^jm-/, '');
const dir = await jmDir();
const meta = (await jmReadMeta(dir, oid)) || { id: oid };
await jmWriteMeta(dir, oid, { ...meta, state: 'failed', reason });
await jmBroadcast({ type: 'jm-bg-update', bgId: bgf.id });
}
self.addEventListener('backgroundfetchfail', (e) => { e.waitUntil(jmMarkFailed(e.registration, e.registration.failureReason || 'failed')); });
self.addEventListener('backgroundfetchabort', (e) => { e.waitUntil(jmMarkFailed(e.registration, 'aborted')); });
self.addEventListener('backgroundfetchclick', (e) => { e.waitUntil(jmOpenDownloads()); });
// Bonyeza notification ya "xx%" / "Imekamilika" (njia ya ndani ya ukurasa) β fungua Downloads
self.addEventListener('notificationclick', (e) => {
if (!/^jm-dl-/.test((e.notification && e.notification.tag) || '')) return;
e.notification.close();
e.waitUntil(jmOpenDownloads());
});
|