mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2025-09-07 06:38:40 +00:00
172 lines
5.0 KiB
JavaScript
172 lines
5.0 KiB
JavaScript
(() => {
|
||
"use strict";
|
||
|
||
let cancelled = false;
|
||
|
||
// Handle messages from clients
|
||
self.addEventListener("message", (event) => {
|
||
if (event.data && event.data.type === "SKIP_WAITING") {
|
||
self.skipWaiting();
|
||
}
|
||
if (event.data && event.data.type === "CLEAR_CACHES") {
|
||
caches
|
||
.keys()
|
||
.then((cacheNames) =>
|
||
Promise.all(cacheNames.map((cacheName) => caches.delete(cacheName)))
|
||
);
|
||
}
|
||
});
|
||
|
||
// Immediately install and skip waiting
|
||
self.addEventListener("install", (event) => {
|
||
self.skipWaiting();
|
||
});
|
||
|
||
// Claim clients on activation and clear caches
|
||
self.addEventListener("activate", (event) => {
|
||
event.waitUntil(
|
||
Promise.all([
|
||
clients.claim(),
|
||
self.skipWaiting(),
|
||
caches
|
||
.keys()
|
||
.then((cacheNames) =>
|
||
Promise.all(cacheNames.map((cacheName) => caches.delete(cacheName)))
|
||
),
|
||
])
|
||
);
|
||
});
|
||
|
||
// Handle fetch events
|
||
self.addEventListener("fetch", (event) => {
|
||
const requestURL = event.request.url;
|
||
|
||
// Only handle WebTorrent streaming requests
|
||
if (!requestURL.includes("/webtorrent/")) {
|
||
return;
|
||
}
|
||
|
||
// Create a promise to handle the request
|
||
const responsePromise = (async () => {
|
||
// 1) Keepalive check
|
||
if (requestURL.includes("/webtorrent/keepalive/")) {
|
||
return new Response();
|
||
}
|
||
|
||
// 2) Cancel check
|
||
if (requestURL.includes("/webtorrent/cancel/")) {
|
||
return new Response(
|
||
new ReadableStream({
|
||
cancel() {
|
||
cancelled = true;
|
||
},
|
||
})
|
||
);
|
||
}
|
||
|
||
// 3) Streaming requests
|
||
// We define an async function and immediately invoke it with `event`
|
||
return (async function ({ request }) {
|
||
const { url, method, headers, destination } = request;
|
||
|
||
// 3a) Find open window clients
|
||
const windowClients = await clients.matchAll({
|
||
type: "window",
|
||
includeUncontrolled: true,
|
||
});
|
||
|
||
// 3b) We send a message to each client with a MessageChannel
|
||
const [clientResponse, port] = await new Promise((resolve) => {
|
||
for (const client of windowClients) {
|
||
const channel = new MessageChannel();
|
||
channel.port1.onmessage = ({ data }) => {
|
||
resolve([data, channel.port1]);
|
||
};
|
||
client.postMessage(
|
||
{
|
||
url,
|
||
method,
|
||
headers: Object.fromEntries(headers.entries()),
|
||
scope: self.registration.scope,
|
||
destination,
|
||
type: "webtorrent",
|
||
},
|
||
[channel.port2]
|
||
);
|
||
}
|
||
});
|
||
|
||
// 3c) Setup a small helper to close the port
|
||
let timeoutId = null;
|
||
const closeChannel = () => {
|
||
port.postMessage(false);
|
||
clearTimeout(timeoutId);
|
||
port.onmessage = null;
|
||
};
|
||
|
||
// 3d) Build a Headers object that prevents caching
|
||
const responseHeaders = new Headers(clientResponse.headers);
|
||
responseHeaders.set(
|
||
"Cache-Control",
|
||
"no-cache, no-store, must-revalidate, max-age=0"
|
||
);
|
||
responseHeaders.set("Pragma", "no-cache");
|
||
responseHeaders.set("Expires", "0");
|
||
|
||
// 3e) If the response is not a streaming request, return it directly
|
||
if (clientResponse.body !== "STREAM") {
|
||
closeChannel();
|
||
return new Response(clientResponse.body, {
|
||
status: clientResponse.status,
|
||
statusText: clientResponse.statusText,
|
||
headers: responseHeaders,
|
||
});
|
||
}
|
||
|
||
// 3f) Otherwise, create a streaming response
|
||
return new Response(
|
||
new ReadableStream({
|
||
pull(controller) {
|
||
return new Promise((resolvePull) => {
|
||
port.onmessage = ({ data }) => {
|
||
if (data) {
|
||
controller.enqueue(data);
|
||
} else {
|
||
closeChannel();
|
||
controller.close();
|
||
}
|
||
resolvePull();
|
||
};
|
||
|
||
// If not cancelled, auto‐close after 5s of no data
|
||
// *** Increase the timeout to avoid frequent CPU wake-ups ***
|
||
if (!cancelled && destination !== "document") {
|
||
clearTimeout(timeoutId);
|
||
timeoutId = setTimeout(() => {
|
||
closeChannel();
|
||
resolvePull();
|
||
}, 30000); // 30 seconds
|
||
}
|
||
|
||
// Request next chunk from client
|
||
port.postMessage(true);
|
||
});
|
||
},
|
||
cancel() {
|
||
closeChannel();
|
||
},
|
||
}),
|
||
{
|
||
status: clientResponse.status,
|
||
statusText: clientResponse.statusText,
|
||
headers: responseHeaders,
|
||
}
|
||
);
|
||
})(event);
|
||
})();
|
||
|
||
// respondWith the promise if it exists
|
||
event.respondWith(responsePromise);
|
||
});
|
||
})();
|