diff --git a/js/app.js b/js/app.js index 216ff94a..8130548d 100644 --- a/js/app.js +++ b/js/app.js @@ -5382,9 +5382,13 @@ class bitvidApp { * Cleanup resources on unload or modal close. */ async cleanup({ preserveSubscriptions = false, preserveObservers = false } = {}) { + this.log( + `[cleanup] Requested (preserveSubscriptions=${preserveSubscriptions}, preserveObservers=${preserveObservers})` + ); // Serialise teardown so overlapping calls (e.g. close button spam) don't // race each other and clobber a fresh playback setup. if (this.cleanupPromise) { + this.log("[cleanup] Waiting for in-flight cleanup to finish before starting a new run."); try { await this.cleanupPromise; } catch (err) { @@ -5393,6 +5397,9 @@ class bitvidApp { } const runCleanup = async () => { + this.log( + `[cleanup] Begin (preserveSubscriptions=${preserveSubscriptions}, preserveObservers=${preserveObservers})` + ); try { this.clearActiveIntervals(); this.cleanupUrlPlaybackWatchdog(); @@ -5437,8 +5444,11 @@ class bitvidApp { } // Tell webtorrent to cleanup await torrentClient.cleanup(); + this.log("[cleanup] WebTorrent cleanup resolved."); } catch (err) { console.error("Cleanup error:", err); + } finally { + this.log("[cleanup] Finished."); } }; @@ -5460,7 +5470,9 @@ class bitvidApp { } try { + this.log("[waitForCleanup] Awaiting previous cleanup before continuing."); await this.cleanupPromise; + this.log("[waitForCleanup] Previous cleanup completed."); } catch (err) { console.warn("waitForCleanup observed a rejected cleanup:", err); } @@ -5488,6 +5500,9 @@ class bitvidApp { teardownVideoElement(videoElement, { replaceNode = false } = {}) { if (!videoElement) { + this.log( + `[teardownVideoElement] No video provided (replaceNode=${replaceNode}); skipping.` + ); return videoElement; } @@ -5499,6 +5514,18 @@ class bitvidApp { } }; + const describeSource = () => { + try { + return videoElement.currentSrc || videoElement.src || ""; + } catch (err) { + return ""; + } + }; + + this.log( + `[teardownVideoElement] Resetting video (replaceNode=${replaceNode}) readyState=${videoElement.readyState} networkState=${videoElement.networkState} src=${describeSource()}` + ); + safe(() => videoElement.pause()); safe(() => { @@ -5526,6 +5553,9 @@ class bitvidApp { }); if (!replaceNode || !videoElement.parentNode) { + this.log( + `[teardownVideoElement] Completed without node replacement (readyState=${videoElement.readyState}).` + ); return videoElement; } @@ -5586,6 +5616,10 @@ class bitvidApp { } }); + this.log( + `[teardownVideoElement] Replaced modal video node (readyState=${clone.readyState} networkState=${clone.networkState}).` + ); + return clone; } @@ -5600,6 +5634,9 @@ class bitvidApp { } const normalizedStallMs = Number.isFinite(stallMs) && stallMs > 0 ? stallMs : 0; + this.log( + `[registerUrlPlaybackWatchdogs] Installing watchdogs (stallMs=${normalizedStallMs}) readyState=${videoElement.readyState} networkState=${videoElement.networkState}` + ); let active = true; let stallTimerId = null; @@ -5624,6 +5661,9 @@ class bitvidApp { if (!active) { return; } + this.log( + `[registerUrlPlaybackWatchdogs] Triggering fallback (${reason}) readyState=${videoElement.readyState} networkState=${videoElement.networkState}` + ); cleanup(); onFallback(reason); }; @@ -5632,6 +5672,9 @@ class bitvidApp { if (!active) { return; } + this.log( + "[registerUrlPlaybackWatchdogs] Hosted playback signaled success; clearing watchdogs." + ); cleanup(); if (typeof onSuccess === "function") { onSuccess(); @@ -5678,6 +5721,7 @@ class bitvidApp { } this.urlPlaybackWatchdogCleanup = () => { + this.log("[registerUrlPlaybackWatchdogs] Manual watchdog cleanup invoked."); cleanup(); }; @@ -8077,6 +8121,10 @@ class bitvidApp { : ""; const magnetProvided = playbackConfig.provided; + this.log( + `[playVideoWithFallback] Session start urlProvided=${!!sanitizedUrl} magnetProvided=${magnetProvided} magnetUsable=${!!magnetForPlayback}` + ); + if (this.currentVideo) { this.currentVideo.magnet = magnetForPlayback; this.currentVideo.normalizedMagnet = magnetForPlayback; @@ -8089,6 +8137,8 @@ class bitvidApp { this.currentMagnetUri = magnetForPlayback || null; this.setCopyMagnetState(!!magnetForPlayback); + let ensureDebugListenersRemoved = () => {}; + try { if (!this.modalVideo) { throw new Error("Video element is not ready for playback."); @@ -8126,6 +8176,55 @@ class bitvidApp { const activeVideoEl = this.teardownVideoElement(this.modalVideo); if (activeVideoEl) { this.modalVideo = activeVideoEl; + this.log( + `[playVideoWithFallback] Modal video prepared (readyState=${activeVideoEl.readyState} networkState=${activeVideoEl.networkState}).` + ); + } + + if (activeVideoEl) { + const debugEvents = [ + "loadedmetadata", + "loadeddata", + "canplay", + "canplaythrough", + "play", + "playing", + "pause", + "stalled", + "suspend", + "waiting", + "ended", + "error", + ]; + const debugHandlers = []; + ensureDebugListenersRemoved = () => { + if (!debugHandlers.length) { + return; + } + for (const [eventName, handler] of debugHandlers) { + activeVideoEl.removeEventListener(eventName, handler); + } + debugHandlers.length = 0; + ensureDebugListenersRemoved = () => {}; + }; + for (const eventName of debugEvents) { + const handler = () => { + const { readyState, networkState, currentTime, paused, error } = + activeVideoEl; + let suffix = `readyState=${readyState} networkState=${networkState} currentTime=${Number.isFinite(currentTime) ? currentTime.toFixed(2) : currentTime} paused=${paused}`; + if (eventName === "error" && error) { + const errorMessage = error.message || ""; + suffix += ` code=${error.code || ""} message=${errorMessage}`; + } + this.log( + `[playVideoWithFallback]