mirror of
https://github.com/imgfloat/server.git
synced 2026-02-05 03:39:26 +00:00
Add playback check
This commit is contained in:
@@ -13,6 +13,7 @@ const animatedCache = new Map();
|
|||||||
const blobCache = new Map();
|
const blobCache = new Map();
|
||||||
const animationFailures = new Map();
|
const animationFailures = new Map();
|
||||||
const audioControllers = new Map();
|
const audioControllers = new Map();
|
||||||
|
const videoPlaybackStates = new WeakMap();
|
||||||
const pendingAudioUnlock = new Set();
|
const pendingAudioUnlock = new Set();
|
||||||
const TARGET_FPS = 60;
|
const TARGET_FPS = 60;
|
||||||
const MIN_FRAME_TIME = 1000 / TARGET_FPS;
|
const MIN_FRAME_TIME = 1000 / TARGET_FPS;
|
||||||
@@ -88,6 +89,7 @@ function removeAsset(assetId) {
|
|||||||
assets.delete(assetId);
|
assets.delete(assetId);
|
||||||
layerOrder = layerOrder.filter((id) => id !== assetId);
|
layerOrder = layerOrder.filter((id) => id !== assetId);
|
||||||
clearMedia(assetId);
|
clearMedia(assetId);
|
||||||
|
stopUserJavaScriptWorker(assetId);
|
||||||
renderStates.delete(assetId);
|
renderStates.delete(assetId);
|
||||||
visibilityStates.delete(assetId);
|
visibilityStates.delete(assetId);
|
||||||
}
|
}
|
||||||
@@ -120,7 +122,12 @@ function connect() {
|
|||||||
|
|
||||||
function renderAssets(list) {
|
function renderAssets(list) {
|
||||||
layerOrder = [];
|
layerOrder = [];
|
||||||
list.forEach((asset) => storeAsset(asset, "append"));
|
list.forEach((asset) => {
|
||||||
|
storeAsset(asset, "append");
|
||||||
|
if (isCodeAsset(asset)) {
|
||||||
|
spawnUserJavaScriptWorker(asset);
|
||||||
|
}
|
||||||
|
});
|
||||||
draw();
|
draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,6 +238,7 @@ function hideAssetWithTransition(asset) {
|
|||||||
}
|
}
|
||||||
const merged = normalizePayload({ ...(existing || {}), ...payload, hidden: true });
|
const merged = normalizePayload({ ...(existing || {}), ...payload, hidden: true });
|
||||||
storeAsset(merged);
|
storeAsset(merged);
|
||||||
|
stopUserJavaScriptWorker(merged.id);
|
||||||
stopAudio(payload.id);
|
stopAudio(payload.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,6 +252,8 @@ function upsertVisibleAsset(asset, placement = "keep") {
|
|||||||
ensureMedia(payload);
|
ensureMedia(payload);
|
||||||
if (isAudioAsset(payload)) {
|
if (isAudioAsset(payload)) {
|
||||||
playAudioImmediately(payload);
|
playAudioImmediately(payload);
|
||||||
|
} else if (isCodeAsset(payload)) {
|
||||||
|
spawnUserJavaScriptWorker(payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,6 +292,7 @@ function applyPatch(assetId, patch) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const merged = normalizePayload({ ...existing, ...sanitizedPatch });
|
const merged = normalizePayload({ ...existing, ...sanitizedPatch });
|
||||||
|
console.log(merged)
|
||||||
const isAudio = isAudioAsset(merged);
|
const isAudio = isAudioAsset(merged);
|
||||||
if (sanitizedPatch.hidden) {
|
if (sanitizedPatch.hidden) {
|
||||||
hideAssetWithTransition(merged);
|
hideAssetWithTransition(merged);
|
||||||
@@ -300,6 +311,10 @@ function applyPatch(assetId, patch) {
|
|||||||
}
|
}
|
||||||
storeAsset(merged);
|
storeAsset(merged);
|
||||||
ensureMedia(merged);
|
ensureMedia(merged);
|
||||||
|
if (isCodeAsset(merged)) {
|
||||||
|
console.info(`Spawning JS worker for patched asset: ${merged.id}`);
|
||||||
|
spawnUserJavaScriptWorker(merged);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw() {
|
function draw() {
|
||||||
@@ -438,6 +453,17 @@ function isVideoAsset(asset) {
|
|||||||
function isAudioAsset(asset) {
|
function isAudioAsset(asset) {
|
||||||
return asset?.mediaType?.startsWith("audio/");
|
return asset?.mediaType?.startsWith("audio/");
|
||||||
}
|
}
|
||||||
|
function getVideoPlaybackState(element) {
|
||||||
|
if (!element) {
|
||||||
|
return { playRequested: false, unmuteOnPlay: false };
|
||||||
|
}
|
||||||
|
let state = videoPlaybackStates.get(element);
|
||||||
|
if (!state) {
|
||||||
|
state = { playRequested: false, unmuteOnPlay: false };
|
||||||
|
videoPlaybackStates.set(element, state);
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
function isCodeAsset(asset) {
|
function isCodeAsset(asset) {
|
||||||
const type = (asset?.mediaType || asset?.originalMediaType || "").toLowerCase();
|
const type = (asset?.mediaType || asset?.originalMediaType || "").toLowerCase();
|
||||||
@@ -694,6 +720,17 @@ function ensureMedia(asset) {
|
|||||||
element.onloadedmetadata = () => recordDuration(asset.id, element.duration);
|
element.onloadedmetadata = () => recordDuration(asset.id, element.duration);
|
||||||
element.preload = "auto";
|
element.preload = "auto";
|
||||||
element.addEventListener("error", () => clearMedia(asset.id));
|
element.addEventListener("error", () => clearMedia(asset.id));
|
||||||
|
const playbackState = getVideoPlaybackState(element);
|
||||||
|
element.addEventListener("playing", () => {
|
||||||
|
playbackState.playRequested = false;
|
||||||
|
if (playbackState.unmuteOnPlay) {
|
||||||
|
element.muted = false;
|
||||||
|
playbackState.unmuteOnPlay = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
element.addEventListener("pause", () => {
|
||||||
|
playbackState.playRequested = false;
|
||||||
|
});
|
||||||
applyMediaVolume(element, asset);
|
applyMediaVolume(element, asset);
|
||||||
element.muted = true;
|
element.muted = true;
|
||||||
setVideoSource(element, asset);
|
setVideoSource(element, asset);
|
||||||
@@ -867,6 +904,7 @@ function applyMediaSettings(element, asset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function startVideoPlayback(element, asset) {
|
function startVideoPlayback(element, asset) {
|
||||||
|
const playbackState = getVideoPlaybackState(element);
|
||||||
const nextSpeed = asset.speed ?? 1;
|
const nextSpeed = asset.speed ?? 1;
|
||||||
const effectiveSpeed = Math.max(nextSpeed, 0.01);
|
const effectiveSpeed = Math.max(nextSpeed, 0.01);
|
||||||
if (element.playbackRate !== effectiveSpeed) {
|
if (element.playbackRate !== effectiveSpeed) {
|
||||||
@@ -878,6 +916,8 @@ function startVideoPlayback(element, asset) {
|
|||||||
|
|
||||||
if (effectiveSpeed === 0) {
|
if (effectiveSpeed === 0) {
|
||||||
element.pause();
|
element.pause();
|
||||||
|
playbackState.playRequested = false;
|
||||||
|
playbackState.unmuteOnPlay = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -887,13 +927,19 @@ function startVideoPlayback(element, asset) {
|
|||||||
if (!element.paused && element.readyState >= 2) {
|
if (!element.paused && element.readyState >= 2) {
|
||||||
element.muted = false;
|
element.muted = false;
|
||||||
} else {
|
} else {
|
||||||
element.addEventListener(
|
playbackState.unmuteOnPlay = true;
|
||||||
"playing",
|
}
|
||||||
() => {
|
}
|
||||||
element.muted = false;
|
|
||||||
},
|
if (element.paused || element.ended) {
|
||||||
{ once: true },
|
if (!playbackState.playRequested) {
|
||||||
);
|
playbackState.playRequested = true;
|
||||||
|
const playPromise = element.play();
|
||||||
|
if (playPromise?.catch) {
|
||||||
|
playPromise.catch(() => {
|
||||||
|
playbackState.playRequested = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user