From a1ff0d2a926b465655f4107519293a3e4fe34322 Mon Sep 17 00:00:00 2001 From: Keep Creating Online <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Sat, 25 Jan 2025 09:10:59 -0500 Subject: [PATCH] greatly improved playback speed on firefox based browsers --- .gitignore | 4 +- src/index.html | 6 +- src/js/webtorrent.js | 729 ++++++++++++++++++++++++++++--------------- 3 files changed, 478 insertions(+), 261 deletions(-) diff --git a/.gitignore b/.gitignore index b644382..b112af9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -saved_config.yaml \ No newline at end of file +saved_config.yaml +repo-context.txt +src/webtorrent-docs/ \ No newline at end of file diff --git a/src/index.html b/src/index.html index 542e342..5e787b5 100644 --- a/src/index.html +++ b/src/index.html @@ -271,8 +271,10 @@

This platform is currently in development and only supports - Chrome-based browsers. Other browsers are not supported at this - time. You may encounter bugs or missing features. + Chrome and Firefox-based browsers. Other browsers are not + supported at this time. You may encounter bugs or missing + features. Give it a sec. Videos might take 10 to 60 seconds to + load initially.

diff --git a/src/js/webtorrent.js b/src/js/webtorrent.js index 46716d4..7247741 100644 --- a/src/js/webtorrent.js +++ b/src/js/webtorrent.js @@ -1,286 +1,499 @@ +// + // js/webtorrent.js -import WebTorrent from './webtorrent.min.js' +import WebTorrent from "./webtorrent.min.js"; export class TorrentClient { - constructor() { - this.client = new WebTorrent() - this.currentTorrent = null - this.TIMEOUT_DURATION = 60000 // 60 seconds - this.statsInterval = null - } + constructor() { + // Create WebTorrent client + this.client = new WebTorrent(); + this.currentTorrent = null; + this.TIMEOUT_DURATION = 60000; // 60 seconds + this.statsInterval = null; + } - log(msg) { - console.log(msg) - } + log(msg) { + console.log(msg); + } - async isBrave() { - return (navigator.brave?.isBrave && await navigator.brave.isBrave()) || false - } + async isBrave() { + return ( + (navigator.brave?.isBrave && (await navigator.brave.isBrave())) || false + ); + } - async waitForServiceWorkerActivation(registration) { + isFirefox() { + return /firefox/i.test(window.navigator.userAgent); + } + + async waitForServiceWorkerActivation(registration) { + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + reject(new Error("Service worker activation timeout")); + }, this.TIMEOUT_DURATION); + + this.log("Waiting for service worker activation..."); + + const checkActivation = () => { + if (registration.active) { + clearTimeout(timeout); + this.log("Service worker is active"); + resolve(registration); + return true; + } + return false; + }; + + if (checkActivation()) return; + + registration.addEventListener("activate", () => { + checkActivation(); + }); + + if (registration.waiting) { + this.log("Service worker is waiting, sending skip waiting message"); + registration.waiting.postMessage({ type: "SKIP_WAITING" }); + } + + registration.addEventListener("statechange", () => { + checkActivation(); + }); + }); + } + + /** + * Registers the service worker, waiting until it's fully active before proceeding. + */ + async setupServiceWorker() { + try { + const isBraveBrowser = await this.isBrave(); + + if (!window.isSecureContext) { + throw new Error("HTTPS or localhost required"); + } + + if (!("serviceWorker" in navigator) || !navigator.serviceWorker) { + throw new Error("Service Worker not supported or disabled"); + } + + // If Brave, we optionally clear all service workers so we can re-register cleanly + if (isBraveBrowser) { + this.log("Checking Brave configuration..."); + + if (!navigator.serviceWorker) { + throw new Error( + "Please enable Service Workers in Brave Shield settings" + ); + } + + if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { + throw new Error("Please enable WebRTC in Brave Shield settings"); + } + + const registrations = await navigator.serviceWorker.getRegistrations(); + for (const registration of registrations) { + await registration.unregister(); + } + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + + const currentPath = window.location.pathname; + const basePath = currentPath.substring( + 0, + currentPath.lastIndexOf("/") + 1 + ); + + this.log("Registering service worker..."); + const registration = await navigator.serviceWorker.register( + "./sw.min.js", + { + scope: basePath, + updateViaCache: "none", + } + ); + this.log("Service worker registered"); + + if (registration.installing) { + this.log("Waiting for installation..."); + await new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + reject(new Error("Installation timeout")); + }, this.TIMEOUT_DURATION); + + registration.installing.addEventListener("statechange", (e) => { + this.log("Service worker state:", e.target.state); + if ( + e.target.state === "activated" || + e.target.state === "redundant" + ) { + clearTimeout(timeout); + resolve(); + } + }); + }); + } + + // Wait for service worker to become active + await this.waitForServiceWorkerActivation(registration); + this.log("Service worker activated"); + + // Make sure it’s truly active + const readyRegistration = await Promise.race([ + navigator.serviceWorker.ready, + new Promise((_, reject) => + setTimeout( + () => reject(new Error("Service worker ready timeout")), + this.TIMEOUT_DURATION + ) + ), + ]); + + if (!readyRegistration.active) { + throw new Error("Service worker not active after ready state"); + } + + this.log("Service worker ready"); + return registration; + } catch (error) { + this.log("Service worker setup error:", error); + throw error; + } + } + + formatBytes(bytes) { + if (bytes === 0) return "0 B"; + const k = 1024; + const sizes = ["B", "KB", "MB", "GB"]; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`; + } + + /** + * Streams the given magnet URI to the specified