Use explicit YouTube play pause actions
This commit is contained in:
+7
-1
@@ -316,6 +316,7 @@ When KEEP-OPEN is nil, close PROCESS after writing."
|
||||
(from (alist-get 'from payload))
|
||||
(type (alist-get 'type payload))
|
||||
(video-id (format "%s" (or (alist-get 'videoId payload) "")))
|
||||
(action (format "%s" (or (alist-get 'action payload) "")))
|
||||
(playlist (kkkmeet-server--normalize-playlist (alist-get 'playlist payload)))
|
||||
(safe-room-id (if (string-empty-p room-id)
|
||||
kkkmeet-server--default-room-id
|
||||
@@ -327,6 +328,10 @@ When KEEP-OPEN is nil, close PROCESS after writing."
|
||||
(append playlist nil))
|
||||
video-id
|
||||
""))
|
||||
(safe-action (if (and (equal action "play")
|
||||
(not (string-empty-p active-video-id)))
|
||||
"play"
|
||||
"pause"))
|
||||
(watch-state `((type . "watch-state")
|
||||
(from . ,from)
|
||||
(name . ,(kkkmeet-server--normalize-name
|
||||
@@ -334,7 +339,8 @@ When KEEP-OPEN is nil, close PROCESS after writing."
|
||||
(videoId . ,active-video-id)
|
||||
(playlist . ,playlist)
|
||||
(position . ,(or (alist-get 'position payload) 0))
|
||||
(playing . ,(if (alist-get 'playing payload) t :false))
|
||||
(playing . ,(if (equal safe-action "play") t :false))
|
||||
(action . ,safe-action)
|
||||
(updatedAt . ,(or (alist-get 'updatedAt payload)
|
||||
(floor (* 1000 (float-time))))))))
|
||||
(puthash safe-room-id watch-state kkkmeet-server--watch-states)
|
||||
|
||||
+53
-15
@@ -44,6 +44,8 @@ let youtubePlayer;
|
||||
let youtubeReady = false;
|
||||
let currentYoutubeVideoId = "";
|
||||
let applyingWatchStateUntil = 0;
|
||||
let pauseEnforcementUntil = 0;
|
||||
let desiredYoutubePlaying = false;
|
||||
let watchPlaylist = [];
|
||||
const peers = new Map();
|
||||
|
||||
@@ -297,9 +299,15 @@ async function ensureYoutubePlayer() {
|
||||
return youtubePlayer;
|
||||
}
|
||||
|
||||
async function sendWatchState({ videoId = currentYoutubeVideoId, position = 0, playing = false }) {
|
||||
async function sendWatchState({
|
||||
videoId = currentYoutubeVideoId,
|
||||
position = 0,
|
||||
playing = false,
|
||||
action = playing ? "play" : "pause"
|
||||
}) {
|
||||
if (!peerId) return;
|
||||
|
||||
const watchAction = action === "play" ? "play" : "pause";
|
||||
const playlist = playlistWithActivePosition(position);
|
||||
|
||||
await fetch(`/watch/${encodeURIComponent(roomId)}`, {
|
||||
@@ -312,7 +320,8 @@ async function sendWatchState({ videoId = currentYoutubeVideoId, position = 0, p
|
||||
videoId,
|
||||
playlist,
|
||||
position,
|
||||
playing,
|
||||
playing: watchAction === "play" && Boolean(videoId),
|
||||
action: watchAction,
|
||||
updatedAt: Date.now()
|
||||
})
|
||||
});
|
||||
@@ -326,7 +335,8 @@ function currentWatchState(playing) {
|
||||
videoId: currentYoutubeVideoId,
|
||||
playlist: watchPlaylist,
|
||||
position: snapshot.position,
|
||||
playing
|
||||
playing,
|
||||
action: playing ? "play" : "pause"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -336,11 +346,25 @@ function handleYoutubeStateChange(event) {
|
||||
return;
|
||||
}
|
||||
|
||||
const state = currentWatchState(event.data === YT.PlayerState.PLAYING);
|
||||
if (
|
||||
event.data === YT.PlayerState.PLAYING &&
|
||||
!desiredYoutubePlaying &&
|
||||
Date.now() < pauseEnforcementUntil
|
||||
) {
|
||||
youtubePlayer?.pauseVideo?.();
|
||||
return;
|
||||
}
|
||||
|
||||
const action = event.data === YT.PlayerState.PLAYING ? "play" : "pause";
|
||||
desiredYoutubePlaying = action === "play";
|
||||
const state = currentWatchState(action === "play");
|
||||
if (state) {
|
||||
state.action = action;
|
||||
if (event.data === YT.PlayerState.ENDED) {
|
||||
state.position = 0;
|
||||
state.playing = false;
|
||||
state.action = "pause";
|
||||
desiredYoutubePlaying = false;
|
||||
watchPlaylist = playlistWithActivePosition(0);
|
||||
state.playlist = watchPlaylist;
|
||||
renderPlaylist();
|
||||
@@ -352,9 +376,11 @@ function handleYoutubeStateChange(event) {
|
||||
}
|
||||
}
|
||||
|
||||
async function applyWatchState(state) {
|
||||
async function applyWatchState(state, { suppressEvents = true } = {}) {
|
||||
const shouldPlay = state?.action === "play" || (!state?.action && Boolean(state?.playing));
|
||||
watchPlaylist = normalizedPlaylist(state?.playlist);
|
||||
currentYoutubeVideoId = state?.videoId || "";
|
||||
desiredYoutubePlaying = shouldPlay;
|
||||
if (currentYoutubeVideoId) {
|
||||
const position = Math.max(0, Number(state.position) || 0);
|
||||
watchPlaylist = watchPlaylist.map((item) => (
|
||||
@@ -364,25 +390,31 @@ async function applyWatchState(state) {
|
||||
renderPlaylist();
|
||||
|
||||
if (!currentYoutubeVideoId) {
|
||||
desiredYoutubePlaying = false;
|
||||
youtubePlayer?.stopVideo?.();
|
||||
youtubeShell.classList.remove("is-active");
|
||||
setWatchCollapsed(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.playing) {
|
||||
if (shouldPlay) {
|
||||
setWatchCollapsed(false);
|
||||
}
|
||||
|
||||
youtubeShell.classList.add("is-active");
|
||||
const player = await ensureYoutubePlayer();
|
||||
applyingWatchStateUntil = Date.now() + 1200;
|
||||
if (suppressEvents) {
|
||||
applyingWatchStateUntil = Date.now() + 3000;
|
||||
}
|
||||
if (!shouldPlay) {
|
||||
pauseEnforcementUntil = Math.max(pauseEnforcementUntil, Date.now() + 3000);
|
||||
}
|
||||
|
||||
const elapsed = state.playing ? Math.max(0, (Date.now() - state.updatedAt) / 1000) : 0;
|
||||
const elapsed = shouldPlay ? Math.max(0, (Date.now() - state.updatedAt) / 1000) : 0;
|
||||
const position = Math.max(0, Number(state.position || 0) + elapsed);
|
||||
|
||||
if (youtubeReady) {
|
||||
if (state.playing) {
|
||||
if (shouldPlay) {
|
||||
const sameVideo = player.getVideoData?.().video_id === currentYoutubeVideoId;
|
||||
const playerState = player.getPlayerState?.();
|
||||
if (sameVideo && playerState === YT.PlayerState.PLAYING) {
|
||||
@@ -405,12 +437,18 @@ async function applyWatchState(state) {
|
||||
setTimeout(() => {
|
||||
player.pauseVideo();
|
||||
}, 150);
|
||||
setTimeout(() => {
|
||||
if (!desiredYoutubePlaying) player.pauseVideo();
|
||||
}, 1200);
|
||||
setTimeout(() => {
|
||||
if (!desiredYoutubePlaying) player.pauseVideo();
|
||||
}, 2500);
|
||||
}
|
||||
} else {
|
||||
const interval = setInterval(() => {
|
||||
if (!youtubeReady) return;
|
||||
clearInterval(interval);
|
||||
applyWatchState(state).catch(console.error);
|
||||
applyWatchState(state, { suppressEvents }).catch(console.error);
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
@@ -803,7 +841,7 @@ async function addYoutubeVideo(value) {
|
||||
position: savedPositionForVideo(videoId),
|
||||
playing: true,
|
||||
updatedAt: Date.now()
|
||||
});
|
||||
}, { suppressEvents: false });
|
||||
await sendWatchState({ videoId, position: savedPositionForVideo(videoId), playing: true });
|
||||
}
|
||||
|
||||
@@ -818,7 +856,7 @@ async function switchYoutubeVideo(videoId) {
|
||||
position,
|
||||
playing: true,
|
||||
updatedAt: Date.now()
|
||||
});
|
||||
}, { suppressEvents: false });
|
||||
await sendWatchState({ videoId, position, playing: true });
|
||||
}
|
||||
|
||||
@@ -835,7 +873,7 @@ async function deleteYoutubeVideo(videoId) {
|
||||
position,
|
||||
playing: Boolean(nextVideoId),
|
||||
updatedAt: Date.now()
|
||||
});
|
||||
}, { suppressEvents: false });
|
||||
await sendWatchState({ videoId: nextVideoId, position, playing: Boolean(nextVideoId) });
|
||||
}
|
||||
|
||||
@@ -847,8 +885,8 @@ async function clearYoutubePlaylist() {
|
||||
position: 0,
|
||||
playing: false,
|
||||
updatedAt: Date.now()
|
||||
});
|
||||
await sendWatchState({ videoId: "", position: 0, playing: false });
|
||||
}, { suppressEvents: false });
|
||||
await sendWatchState({ videoId: "", position: 0, playing: false, action: "pause" });
|
||||
}
|
||||
|
||||
async function startScreenShare() {
|
||||
|
||||
@@ -221,14 +221,17 @@ const server = http.createServer(async (req, res) => {
|
||||
|
||||
const playlist = normalizePlaylist(payload.playlist);
|
||||
const videoId = String(payload.videoId || "").trim();
|
||||
const activeVideoId = playlist.some((item) => item.videoId === videoId) ? videoId : "";
|
||||
const action = payload.action === "play" && activeVideoId ? "play" : "pause";
|
||||
const watchState = {
|
||||
type: "watch-state",
|
||||
from: payload.from,
|
||||
name: normalizeName(payload.name),
|
||||
videoId: playlist.some((item) => item.videoId === videoId) ? videoId : "",
|
||||
videoId: activeVideoId,
|
||||
playlist,
|
||||
position: Number(payload.position) || 0,
|
||||
playing: Boolean(payload.playing),
|
||||
playing: action === "play",
|
||||
action,
|
||||
updatedAt: Number(payload.updatedAt) || Date.now()
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user