mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
fix: keep autoplay fallback from saving mute
(cherry picked from commit f373bb9ce7)
This commit is contained in:
1 parent
2cda5025d4
commit
2f3028f2e4
7 files changed
+96
-10
No files matched your search
@@ -232,7 +232,18 @@ export function createAuthSessionCoordinator(deps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.applyAuthenticatedUiState();
|
this.applyAuthenticatedUiState();
|
||||||
this.commentController?.refreshAuthState?.();
|
const activeModalVideo =
|
||||||
|
typeof this.videoModal?.getCurrentVideo === "function"
|
||||||
|
? this.videoModal.getCurrentVideo()
|
||||||
|
: this.commentController?.currentVideo || null;
|
||||||
|
if (this.commentController && activeModalVideo) {
|
||||||
|
// Login can reconfigure relay-backed services. Reload the active thread
|
||||||
|
// instead of only enabling its composer so readable comments survive
|
||||||
|
// the transition from guest to authenticated state.
|
||||||
|
this.commentController.load(activeModalVideo);
|
||||||
|
} else {
|
||||||
|
this.commentController?.refreshAuthState?.();
|
||||||
|
}
|
||||||
this.updateShareNostrAuthState({ reason: "auth-login" });
|
this.updateShareNostrAuthState({ reason: "auth-login" });
|
||||||
if (typeof this.refreshUnreadDmIndicator === "function") {
|
if (typeof this.refreshUnreadDmIndicator === "function") {
|
||||||
void this.refreshUnreadDmIndicator({ reason: "auth-login" });
|
void this.refreshUnreadDmIndicator({ reason: "auth-login" });
|
||||||
|
|||||||
@@ -237,7 +237,7 @@ export function createPlaybackCoordinator(deps) {
|
|||||||
this.log("Autoplay failed:", err);
|
this.log("Autoplay failed:", err);
|
||||||
if (!this.modalVideo.muted) {
|
if (!this.modalVideo.muted) {
|
||||||
this.log("Falling back to muted autoplay.");
|
this.log("Falling back to muted autoplay.");
|
||||||
this.modalVideo.muted = true;
|
this.playbackService.muteForAutoplayFallback(this.modalVideo);
|
||||||
this.modalVideo.play().catch((err2) => {
|
this.modalVideo.play().catch((err2) => {
|
||||||
this.log("Muted autoplay also failed:", err2);
|
this.log("Muted autoplay also failed:", err2);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { userLogger } from "../utils/logger.js";
|
import { userLogger } from "../utils/logger.js";
|
||||||
import { PLAYBACK_START_TIMEOUT } from "../constants.js";
|
import { PLAYBACK_START_TIMEOUT } from "../constants.js";
|
||||||
import { getCurrentVideo, setCurrentVideo } from "../state/appState.js";
|
import { getCurrentVideo, setCurrentVideo } from "../state/appState.js";
|
||||||
|
import {
|
||||||
|
consumeAutoplayFallbackMute,
|
||||||
|
muteForAutoplayFallback,
|
||||||
|
} from "../utils/autoplayPreference.js";
|
||||||
import {
|
import {
|
||||||
SimpleEventEmitter,
|
SimpleEventEmitter,
|
||||||
extractWebSeedsFromMagnet,
|
extractWebSeedsFromMagnet,
|
||||||
@@ -330,11 +334,17 @@ export class PlaybackService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const storedUnmuted = localStorage.getItem("unmutedAutoplay");
|
const storedUnmuted = localStorage.getItem("unmutedAutoplay");
|
||||||
const userWantsUnmuted = storedUnmuted === "true";
|
// Sound is the default for an explicit watch action. Browsers may still
|
||||||
|
// reject unmuted autoplay, in which case the coordinator retries muted.
|
||||||
|
// Preserve a viewer's explicit mute choice across subsequent videos.
|
||||||
|
const userWantsUnmuted = storedUnmuted !== "false";
|
||||||
videoElement.muted = !userWantsUnmuted;
|
videoElement.muted = !userWantsUnmuted;
|
||||||
|
|
||||||
if (!videoElement.dataset.autoplayBound) {
|
if (!videoElement.dataset.autoplayBound) {
|
||||||
videoElement.addEventListener("volumechange", () => {
|
videoElement.addEventListener("volumechange", () => {
|
||||||
|
if (consumeAutoplayFallbackMute(videoElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
"unmutedAutoplay",
|
"unmutedAutoplay",
|
||||||
(!videoElement.muted).toString()
|
(!videoElement.muted).toString()
|
||||||
@@ -344,6 +354,10 @@ export class PlaybackService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
muteForAutoplayFallback(videoElement) {
|
||||||
|
return muteForAutoplayFallback(videoElement);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a set of event listeners ("watchdogs") on the video element to detect
|
* Registers a set of event listeners ("watchdogs") on the video element to detect
|
||||||
* when playback has stalled or failed.
|
* when playback has stalled or failed.
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
const FALLBACK_MUTE_DATASET_KEY = "bitvidAutoplayFallbackMute";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mute playback after an unmuted autoplay rejection without recording the
|
||||||
|
* browser-policy fallback as a viewer preference.
|
||||||
|
*/
|
||||||
|
export function muteForAutoplayFallback(videoElement) {
|
||||||
|
if (!videoElement || videoElement.muted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoElement.dataset) {
|
||||||
|
videoElement.dataset[FALLBACK_MUTE_DATASET_KEY] = "true";
|
||||||
|
}
|
||||||
|
videoElement.muted = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Consume the marker attached to the next fallback-generated volume change. */
|
||||||
|
export function consumeAutoplayFallbackMute(videoElement) {
|
||||||
|
if (videoElement?.dataset?.[FALLBACK_MUTE_DATASET_KEY] !== "true") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete videoElement.dataset[FALLBACK_MUTE_DATASET_KEY];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
+2
-1
@@ -30,6 +30,7 @@ import {
|
|||||||
waitForActiveController as waitForActiveControllerHelper,
|
waitForActiveController as waitForActiveControllerHelper,
|
||||||
} from "./webtorrentHelpers.js";
|
} from "./webtorrentHelpers.js";
|
||||||
import { devLogger, userLogger } from "./utils/logger.js";
|
import { devLogger, userLogger } from "./utils/logger.js";
|
||||||
|
import { muteForAutoplayFallback } from "./utils/autoplayPreference.js";
|
||||||
import { emit } from "./embedDiagnostics.js";
|
import { emit } from "./embedDiagnostics.js";
|
||||||
|
|
||||||
const DEFAULT_PROBE_TRACKERS = Object.freeze([...WSS_TRACKERS]);
|
const DEFAULT_PROBE_TRACKERS = Object.freeze([...WSS_TRACKERS]);
|
||||||
@@ -561,7 +562,7 @@ export class TorrentClient {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.log(`Retrying with muted autoplay (${context} path).`);
|
this.log(`Retrying with muted autoplay (${context} path).`);
|
||||||
videoElement.muted = true;
|
muteForAutoplayFallback(videoElement);
|
||||||
videoElement.play().catch((err2) => {
|
videoElement.play().catch((err2) => {
|
||||||
this.log(`Muted autoplay also failed (${context} path):`, err2);
|
this.log(`Muted autoplay also failed (${context} path):`, err2);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -151,3 +151,30 @@ test("login does not eagerly load DM history; it is deferred", async () => {
|
|||||||
"the lightweight unread DM indicator should still run so the badge stays accurate",
|
"the lightweight unread DM indicator should still run so the badge stays accurate",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("login reloads the open video comment thread", async () => {
|
||||||
|
globalThis.window.location = { search: "", href: "http://localhost/" };
|
||||||
|
|
||||||
|
const coord = makeCoordinator({
|
||||||
|
loadDirectMessages: async () => ({ ok: true }),
|
||||||
|
refreshUnreadDmIndicator: async () => {},
|
||||||
|
});
|
||||||
|
const activeVideo = { id: "b".repeat(64), title: "Open video" };
|
||||||
|
const loadedVideos = [];
|
||||||
|
coord.videoModal = { getCurrentVideo: () => activeVideo };
|
||||||
|
coord.commentController = {
|
||||||
|
load: (video) => loadedVideos.push(video),
|
||||||
|
refreshAuthState: () => {
|
||||||
|
throw new Error("an open thread should be reloaded, not only refreshed");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await coord.handleAuthLogin({
|
||||||
|
pubkey: ACTIVE_PUBKEY,
|
||||||
|
identityChanged: true,
|
||||||
|
postLogin: { profile: { name: "tester" } },
|
||||||
|
postLoginPromise: Promise.resolve({ profile: { name: "tester" } }),
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(loadedVideos, [activeVideo]);
|
||||||
|
});
|
||||||
@@ -44,21 +44,27 @@ describe("PlaybackService", () => {
|
|||||||
const service = new PlaybackService();
|
const service = new PlaybackService();
|
||||||
const video = document.createElement("video");
|
const video = document.createElement("video");
|
||||||
|
|
||||||
// Default: muted (unless localStorage says unmuted)
|
// Default: sound on unless the viewer explicitly chose mute.
|
||||||
localStorage.removeItem("unmutedAutoplay");
|
localStorage.removeItem("unmutedAutoplay");
|
||||||
service.prepareVideoElement(video);
|
service.prepareVideoElement(video);
|
||||||
assert.equal(video.muted, true);
|
assert.equal(video.muted, false);
|
||||||
assert.equal(video.dataset.autoplayBound, "true");
|
assert.equal(video.dataset.autoplayBound, "true");
|
||||||
|
|
||||||
// Simulate volume change
|
// Simulate an explicit mute choice.
|
||||||
video.muted = false;
|
video.muted = true;
|
||||||
video.dispatchEvent(new window.Event("volumechange"));
|
video.dispatchEvent(new window.Event("volumechange"));
|
||||||
assert.equal(localStorage.getItem("unmutedAutoplay"), "true");
|
assert.equal(localStorage.getItem("unmutedAutoplay"), "false");
|
||||||
|
|
||||||
// Test respecting stored value
|
// Test respecting stored value
|
||||||
const video2 = document.createElement("video");
|
const video2 = document.createElement("video");
|
||||||
service.prepareVideoElement(video2);
|
service.prepareVideoElement(video2);
|
||||||
assert.equal(video2.muted, false);
|
assert.equal(video2.muted, true);
|
||||||
|
|
||||||
|
// A browser-policy fallback is temporary and must not become a saved mute.
|
||||||
|
localStorage.setItem("unmutedAutoplay", "true");
|
||||||
|
video2.muted = false;
|
||||||
|
service.muteForAutoplayFallback(video2);
|
||||||
|
assert.equal(localStorage.getItem("unmutedAutoplay"), "true");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("registerUrlPlaybackWatchdogs triggers onFallback on error", () => {
|
test("registerUrlPlaybackWatchdogs triggers onFallback on error", () => {
|
||||||
|
|||||||
Reference in new issue
Block a user