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