Улучшить управление видеоплеером и громкостью

- Автоматическая установка громкости на 100% при запуске плеера
- Постоянный мониторинг громкости каждые 2 секунды во время воспроизведения
- Усиление надежности команд Play/Pause/Seek (тройная отправка с задержками)
- Улучшенная перемотка ±10 секунд для всех типов плееров
- Поддержка громкости для нативного HTML5 видео (AniLibria)
- Агрессивная установка громкости в первые 15 секунд после загрузки iframe

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Nikita 2026-10-29 15:48:39 +05:00
parent 2ab0457568
commit 7da9ab00b1

View file

@ -697,34 +697,39 @@ export default function Cinema({
const isPlay = command === "play"; const isPlay = command === "play";
const isToggle = command === "toggle"; const isToggle = command === "toggle";
// 1. Kodik Player API (Supports both object and stringified commands) // Отправляем команды многократно для надежности (разные плееры реагируют в разное время)
broadcastToWindowTree({ key: "kodik_player_api", value: { method: isToggle ? "toggle" : (isPlay ? "play" : "pause") } }); for (let i = 0; i < 3; i++) {
if (!isToggle) { setTimeout(() => {
broadcastToWindowTree({ key: "kodik_player_api", value: { method: isPlay ? "play" : "pause" } }); // 1. Kodik Player API (Supports both object and stringified commands)
broadcastToWindowTree({ key: "kodik_player_api", value: { method: isToggle ? "toggle" : (isPlay ? "play" : "pause") } });
if (!isToggle) {
broadcastToWindowTree({ key: "kodik_player_api", value: { method: isPlay ? "play" : "pause" } });
}
// 2. Playerjs API (Used by Alloha, Kinoplay, Collaps, VideoCDN, Ashdi, HDRezka)
broadcastToWindowTree({ api: isToggle ? "toggle" : (isPlay ? "play" : "pause") });
if (!isToggle) {
broadcastToWindowTree({ api: isPlay ? "play" : "pause" });
}
// 3. Alloha API
broadcastToWindowTree({ action: isPlay ? "play" : "pause" });
broadcastToWindowTree({ method: isPlay ? "play" : "pause" });
// 4. VidLink Pro / Video.js API
broadcastToWindowTree({ type: isPlay ? "PLAYER_PLAY" : "PLAYER_PAUSE" });
broadcastToWindowTree({ type: isPlay ? "play" : "pause" });
broadcastToWindowTree({ action: isPlay ? "play" : "pause" });
broadcastToWindowTree({ event: isPlay ? "play" : "pause" });
// 5. YouTube / VidSrc / AutoEmbed HTML5 API
broadcastToWindowTree({ event: "command", func: isPlay ? "playVideo" : "pauseVideo", args: "" });
// 6. Generic HTML5 postMessage
broadcastToWindowTree({ method: isPlay ? "play" : "pause" });
broadcastToWindowTree({ command: isPlay ? "play" : "pause" });
}, i * 100);
} }
// 2. Playerjs API (Used by Alloha, Kinoplay, Collaps, VideoCDN, Ashdi, HDRezka)
broadcastToWindowTree({ api: isToggle ? "toggle" : (isPlay ? "play" : "pause") });
if (!isToggle) {
broadcastToWindowTree({ api: isPlay ? "play" : "pause" });
}
// 3. Alloha API
broadcastToWindowTree({ action: isPlay ? "play" : "pause" });
broadcastToWindowTree({ method: isPlay ? "play" : "pause" });
// 4. VidLink Pro / Video.js API
broadcastToWindowTree({ type: isPlay ? "PLAYER_PLAY" : "PLAYER_PAUSE" });
broadcastToWindowTree({ type: isPlay ? "play" : "pause" });
broadcastToWindowTree({ action: isPlay ? "play" : "pause" });
broadcastToWindowTree({ event: isPlay ? "play" : "pause" });
// 5. YouTube / VidSrc / AutoEmbed HTML5 API
broadcastToWindowTree({ event: "command", func: isPlay ? "playVideo" : "pauseVideo", args: "" });
// 6. Generic HTML5 postMessage
broadcastToWindowTree({ method: isPlay ? "play" : "pause" });
broadcastToWindowTree({ command: isPlay ? "play" : "pause" });
}, [broadcastToWindowTree]); }, [broadcastToWindowTree]);
const setPlayerVolumeMax = useCallback((showToast: boolean = false) => { const setPlayerVolumeMax = useCallback((showToast: boolean = false) => {
@ -802,7 +807,10 @@ export default function Cinema({
}, 120); }, 120);
} }
} }
// Установка громкости 100% каждый раз при воспроизведении
setPlayerVolumeMax(false); setPlayerVolumeMax(false);
setTimeout(() => setPlayerVolumeMax(false), 500);
setTimeout(() => setPlayerVolumeMax(false), 1500);
setIsPlayingState(true); setIsPlayingState(true);
showTvToast("Воспроизведение", "▶", 1200); showTvToast("Воспроизведение", "▶", 1200);
}, [sendIframePlaybackCommand, setPlayerVolumeMax, showTvToast]); }, [sendIframePlaybackCommand, setPlayerVolumeMax, showTvToast]);
@ -846,39 +854,44 @@ export default function Cinema({
const targetTime = Math.max(0, curTime + secondsDelta); const targetTime = Math.max(0, curTime + secondsDelta);
currentPlaybackRef.current.currentTime = targetTime; currentPlaybackRef.current.currentTime = targetTime;
// 1. Kodik API (Seek to absolute seconds & relative seek delta) // Отправляем команды перемотки несколько раз для надежности
broadcastToWindowTree({ key: "kodik_player_api", value: { method: "seek", time: targetTime } }); for (let i = 0; i < 3; i++) {
broadcastToWindowTree({ key: "kodik_player_api", value: { method: "seek_delta", time: secondsDelta } }); setTimeout(() => {
broadcastToWindowTree({ key: "kodik_player_api", value: { method: secondsDelta > 0 ? "forward" : "rewind", time: Math.abs(secondsDelta) } }); // 1. Kodik API (Seek to absolute seconds & relative seek delta)
broadcastToWindowTree({ key: "kodik_player_api", value: { method: "seek", time: targetTime } });
broadcastToWindowTree({ key: "kodik_player_api", value: { method: "seek_delta", time: secondsDelta } });
broadcastToWindowTree({ key: "kodik_player_api", value: { method: secondsDelta > 0 ? "forward" : "rewind", time: Math.abs(secondsDelta) } });
// 2. Playerjs API (Used by Alloha, Kinoplay, Collaps, VideoCDN, Ashdi, HDRezka) // 2. Playerjs API (Used by Alloha, Kinoplay, Collaps, VideoCDN, Ashdi, HDRezka)
broadcastToWindowTree({ api: "seek", set: targetTime }); broadcastToWindowTree({ api: "seek", set: targetTime });
broadcastToWindowTree({ api: "seek", set: secondsDelta > 0 ? `+${secondsDelta}` : `${secondsDelta}` }); broadcastToWindowTree({ api: "seek", set: secondsDelta > 0 ? `+${secondsDelta}` : `${secondsDelta}` });
broadcastToWindowTree({ api: "seek", time: targetTime }); broadcastToWindowTree({ api: "seek", time: targetTime });
broadcastToWindowTree({ api: "seek", delta: secondsDelta }); broadcastToWindowTree({ api: "seek", delta: secondsDelta });
broadcastToWindowTree({ api: "seek_delta", time: secondsDelta }); broadcastToWindowTree({ api: "seek_delta", time: secondsDelta });
// 3. Alloha API // 3. Alloha API
broadcastToWindowTree({ action: "seek", time: targetTime }); broadcastToWindowTree({ action: "seek", time: targetTime });
broadcastToWindowTree({ action: "seek", delta: secondsDelta }); broadcastToWindowTree({ action: "seek", delta: secondsDelta });
broadcastToWindowTree({ method: "seek", time: targetTime }); broadcastToWindowTree({ method: "seek", time: targetTime });
broadcastToWindowTree({ method: "seekTo", value: targetTime }); broadcastToWindowTree({ method: "seekTo", value: targetTime });
// 4. VidLink Pro / Video.js API // 4. VidLink Pro / Video.js API
broadcastToWindowTree({ type: "SEEK", data: { time: targetTime } }); broadcastToWindowTree({ type: "SEEK", data: { time: targetTime } });
broadcastToWindowTree({ type: "PLAYER_SEEK", time: targetTime }); broadcastToWindowTree({ type: "PLAYER_SEEK", time: targetTime });
broadcastToWindowTree({ type: "seek", time: targetTime }); broadcastToWindowTree({ type: "seek", time: targetTime });
broadcastToWindowTree({ action: "seek", time: targetTime }); broadcastToWindowTree({ action: "seek", time: targetTime });
broadcastToWindowTree({ action: "seek", delta: secondsDelta }); broadcastToWindowTree({ action: "seek", delta: secondsDelta });
// 5. YouTube / VidSrc / AutoEmbed HTML5 API // 5. YouTube / VidSrc / AutoEmbed HTML5 API
broadcastToWindowTree({ event: "command", func: "seekTo", args: [targetTime, true] }); broadcastToWindowTree({ event: "command", func: "seekTo", args: [targetTime, true] });
broadcastToWindowTree({ event: "command", func: "seek", args: [targetTime] }); broadcastToWindowTree({ event: "command", func: "seek", args: [targetTime] });
broadcastToWindowTree({ action: "seekTo", value: targetTime }); broadcastToWindowTree({ action: "seekTo", value: targetTime });
// 6. Generic HTML5 postMessage // 6. Generic HTML5 postMessage
broadcastToWindowTree({ method: "seek", time: targetTime }); broadcastToWindowTree({ method: "seek", time: targetTime });
broadcastToWindowTree({ command: "seek", time: targetTime }); broadcastToWindowTree({ command: "seek", time: targetTime });
}, i * 100);
}
} }
showTvToast(secondsDelta > 0 ? `+${secondsDelta} сек` : `${secondsDelta} сек`, secondsDelta > 0 ? "⏩" : "⏪", 1000); showTvToast(secondsDelta > 0 ? `+${secondsDelta} сек` : `${secondsDelta} сек`, secondsDelta > 0 ? "⏩" : "⏪", 1000);
}, [broadcastToWindowTree, showTvToast]); }, [broadcastToWindowTree, showTvToast]);
@ -1684,6 +1697,11 @@ export default function Cinema({
setTimeout(() => { setTimeout(() => {
setPlayerVolumeMax(false); setPlayerVolumeMax(false);
}, 4500); }, 4500);
// Агрессивная установка громкости каждые 3 секунды (первые 15 секунд)
const volumeInterval = setInterval(() => {
setPlayerVolumeMax(false);
}, 3000);
setTimeout(() => clearInterval(volumeInterval), 15000);
}, [sendIframePlaybackCommand, setPlayerVolumeMax]); }, [sendIframePlaybackCommand, setPlayerVolumeMax]);
// Start playback for a media item (movie or specific series episode) // Start playback for a media item (movie or specific series episode)
@ -1976,6 +1994,16 @@ export default function Cinema({
const activeMedia = playingMedia || selectedMedia; const activeMedia = playingMedia || selectedMedia;
currentPlaybackRef.current = { currentTime, duration }; currentPlaybackRef.current = { currentTime, duration };
// Постоянно поддерживаем громкость на 100% для нативного видео
try {
if (videoRef.current.volume !== 1.0) {
videoRef.current.volume = 1.0;
}
if (videoRef.current.muted) {
videoRef.current.muted = false;
}
} catch {}
if (duration > 0 && isTauri) { if (duration > 0 && isTauri) {
const payload: CinemaPlayback = { const payload: CinemaPlayback = {
title_id: activeMedia.id, title_id: activeMedia.id,
@ -2119,6 +2147,18 @@ export default function Cinema({
return () => clearInterval(interval); return () => clearInterval(interval);
}, [playingEpisode, playerMode, allMirrorsFailed, playingMedia, selectedMedia, playingSeasonNumber, isTauri, currentUser?.email]); }, [playingEpisode, playerMode, allMirrorsFailed, playingMedia, selectedMedia, playingSeasonNumber, isTauri, currentUser?.email]);
// Постоянный мониторинг и поддержание громкости на 100% во время воспроизведения
useEffect(() => {
if (!playingEpisode) return;
// Устанавливаем громкость каждые 2 секунды во время воспроизведения
const volumeKeeper = setInterval(() => {
setPlayerVolumeMax(false);
}, 2000);
return () => clearInterval(volumeKeeper);
}, [playingEpisode, setPlayerVolumeMax]);
// Listen to postMessage from embedded players (VidLink, Kodik, Alloha) // Listen to postMessage from embedded players (VidLink, Kodik, Alloha)
useEffect(() => { useEffect(() => {
const handleWindowMessage = (e: MessageEvent) => { const handleWindowMessage = (e: MessageEvent) => {