From fff0ebe85de3f7bd36c92f0472047ee90fe32486 Mon Sep 17 00:00:00 2001 From: "Timothy Z." Date: Mon, 19 May 2025 17:02:32 +0300 Subject: [PATCH 01/23] fix(Player): workaround for binge watching --- src/routes/Player/Player.js | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index 740ed46d4..ce4cd3c3e 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -103,7 +103,7 @@ const Player = ({ urlParams, queryParams }) => { video.setProp('extraSubtitlesOutlineColor', settings.subtitlesOutlineColor); }, [settings.subtitlesSize, settings.subtitlesOffset, settings.subtitlesTextColor, settings.subtitlesBackgroundColor, settings.subtitlesOutlineColor]); - const handleNextVideoNavigation = React.useCallback((deepLinks) => { + const handleNextVideoNavigation = (deepLinks) => { if (deepLinks.player) { isNavigating.current = true; window.location.replace(deepLinks.player); @@ -111,20 +111,16 @@ const Player = ({ urlParams, queryParams }) => { isNavigating.current = true; window.location.replace(deepLinks.metaDetailsStreams); } - }, []); - - const onEnded = React.useCallback(() => { - if (isNavigating.current) { - return; - } + }; + const onEnded = () => { ended(); - if (player.nextVideo !== null) { + if (window.playerNextVideo !== null) { onNextVideoRequested(); } else { window.history.back(); } - }, [player.nextVideo, onNextVideoRequested]); + }; const onError = React.useCallback((error) => { console.error('Player', error); @@ -229,14 +225,14 @@ const Player = ({ urlParams, queryParams }) => { nextVideoPopupDismissed.current = true; }, []); - const onNextVideoRequested = React.useCallback(() => { - if (player.nextVideo !== null) { + const onNextVideoRequested = () => { + if (window.playerNextVideo !== null) { nextVideo(); - const deepLinks = player.nextVideo.deepLinks; + const deepLinks = window.playerNextVideo.deepLinks; handleNextVideoNavigation(deepLinks); } - }, [player.nextVideo, handleNextVideoNavigation]); + }; const onVideoClick = React.useCallback(() => { if (video.state.paused !== null) { @@ -394,6 +390,12 @@ const Player = ({ urlParams, queryParams }) => { closeNextVideoPopup(); } } + if (player.nextVideo) { + // This is a workaround for the fact that when we call onEnded nextVideo from the player is already set to null since core unloads the stream + // we explicitly set it to a global variable so we can access it in the onEnded function + // this is not a good solution but it works for now + window.playerNextVideo = player.nextVideo; + } }, [player.nextVideo, video.state.time, video.state.duration]); React.useEffect(() => { @@ -429,6 +431,7 @@ const Player = ({ urlParams, queryParams }) => { defaultSubtitlesSelected.current = false; defaultAudioTrackSelected.current = false; nextVideoPopupDismissed.current = false; + isNavigating.current = false; }, [video.state.stream]); React.useEffect(() => { From b4c0ab551ecbb73ea074e035ea4b294b0755c15b Mon Sep 17 00:00:00 2001 From: "Timothy Z." Date: Tue, 20 May 2025 15:09:50 +0300 Subject: [PATCH 02/23] fix(Player): binge watching --- src/routes/Player/Player.js | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index ce4cd3c3e..1ddb14599 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -103,7 +103,7 @@ const Player = ({ urlParams, queryParams }) => { video.setProp('extraSubtitlesOutlineColor', settings.subtitlesOutlineColor); }, [settings.subtitlesSize, settings.subtitlesOffset, settings.subtitlesTextColor, settings.subtitlesBackgroundColor, settings.subtitlesOutlineColor]); - const handleNextVideoNavigation = (deepLinks) => { + const handleNextVideoNavigation = React.useCallback((deepLinks) => { if (deepLinks.player) { isNavigating.current = true; window.location.replace(deepLinks.player); @@ -111,16 +111,23 @@ const Player = ({ urlParams, queryParams }) => { isNavigating.current = true; window.location.replace(deepLinks.metaDetailsStreams); } - }; + }, []); + + const onEnded = React.useCallback(() => { + if (isNavigating.current) { + return; + } - const onEnded = () => { ended(); if (window.playerNextVideo !== null) { - onNextVideoRequested(); + nextVideo(); + + const deepLinks = window.playerNextVideo.deepLinks; + handleNextVideoNavigation(deepLinks); } else { window.history.back(); } - }; + }, []); const onError = React.useCallback((error) => { console.error('Player', error); @@ -225,14 +232,14 @@ const Player = ({ urlParams, queryParams }) => { nextVideoPopupDismissed.current = true; }, []); - const onNextVideoRequested = () => { - if (window.playerNextVideo !== null) { + const onNextVideoRequested = React.useCallback(() => { + if (player.nextVideo !== null) { nextVideo(); - const deepLinks = window.playerNextVideo.deepLinks; + const deepLinks = player.nextVideo.deepLinks; handleNextVideoNavigation(deepLinks); } - }; + }, [player.nextVideo, handleNextVideoNavigation]); const onVideoClick = React.useCallback(() => { if (video.state.paused !== null) { @@ -431,7 +438,6 @@ const Player = ({ urlParams, queryParams }) => { defaultSubtitlesSelected.current = false; defaultAudioTrackSelected.current = false; nextVideoPopupDismissed.current = false; - isNavigating.current = false; }, [video.state.stream]); React.useEffect(() => { From 365294946a8a68d321bebe8cfb11f3f36301cc30 Mon Sep 17 00:00:00 2001 From: "Timothy Z." Date: Tue, 20 May 2025 15:12:29 +0300 Subject: [PATCH 03/23] chore(Player): add logs --- src/routes/Player/Player.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index 1ddb14599..c0443ac74 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -114,6 +114,8 @@ const Player = ({ urlParams, queryParams }) => { }, []); const onEnded = React.useCallback(() => { + // here we need to explicitly check for isNavigating.current + // because the ended event can be calleb multiple times by MPV inside Shell if (isNavigating.current) { return; } From 38f7e5a0f805bc0192934e4d39fa53de984e5c20 Mon Sep 17 00:00:00 2001 From: "Timothy Z." Date: Tue, 20 May 2025 15:32:02 +0300 Subject: [PATCH 04/23] chore(Player): update logs --- src/routes/Player/Player.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index c0443ac74..7a4d5b035 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -115,7 +115,7 @@ const Player = ({ urlParams, queryParams }) => { const onEnded = React.useCallback(() => { // here we need to explicitly check for isNavigating.current - // because the ended event can be calleb multiple times by MPV inside Shell + // the ended event can be called multiple times by MPV inside Shell if (isNavigating.current) { return; } From 440713ee680fa034fe981e0119777480030464a3 Mon Sep 17 00:00:00 2001 From: "Timothy Z." Date: Tue, 20 May 2025 15:54:20 +0300 Subject: [PATCH 05/23] fix(Player): reset the flag back to false --- src/routes/Player/Player.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index 7a4d5b035..f1e0657d9 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -440,6 +440,9 @@ const Player = ({ urlParams, queryParams }) => { defaultSubtitlesSelected.current = false; defaultAudioTrackSelected.current = false; nextVideoPopupDismissed.current = false; + // we need a timeout here to make sure that previous page unloads and the new one loads + // avoiding race conditions and flickering + setTimeout(() => isNavigating.current = false, 1000); }, [video.state.stream]); React.useEffect(() => { From 28578e1deac922fe8e1e81d388ccc967d7304b30 Mon Sep 17 00:00:00 2001 From: "Timothy Z." Date: Wed, 21 May 2025 15:11:32 +0300 Subject: [PATCH 06/23] refactor(Player): reset global var --- src/routes/Player/Player.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index f1e0657d9..48218d126 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -404,6 +404,8 @@ const Player = ({ urlParams, queryParams }) => { // we explicitly set it to a global variable so we can access it in the onEnded function // this is not a good solution but it works for now window.playerNextVideo = player.nextVideo; + } else { + window.playerNextVideo = null; } }, [player.nextVideo, video.state.time, video.state.duration]); From 73823e9e0744bb10c58bf3af5b38365298abca47 Mon Sep 17 00:00:00 2001 From: Abdalrzag Eisa Date: Fri, 13 Jun 2025 21:56:36 +0300 Subject: [PATCH 07/23] Pass `currentlyPlayingVideoID` into `SideDrawer` --- src/routes/Player/Player.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index 8ba813786..b10cbef42 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -787,6 +787,7 @@ const Player = ({ urlParams, queryParams }) => { metaItem={player.metaItem?.content} seriesInfo={player.seriesInfo} closeSideDrawer={closeSideDrawer} + currentlyPlayingVideoID={player.selected?.streamRequest.path.id} /> { From 40871dc8f2f64ae95d525ec1fced4f50dcb0a079 Mon Sep 17 00:00:00 2001 From: Abdalrzag Eisa Date: Fri, 13 Jun 2025 21:59:19 +0300 Subject: [PATCH 08/23] Add button `jump-to-currently-playing-video` --- src/routes/Player/SideDrawer/SideDrawer.tsx | 52 +++++++++++++-------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/src/routes/Player/SideDrawer/SideDrawer.tsx b/src/routes/Player/SideDrawer/SideDrawer.tsx index cb94e24e5..365ba87ae 100644 --- a/src/routes/Player/SideDrawer/SideDrawer.tsx +++ b/src/routes/Player/SideDrawer/SideDrawer.tsx @@ -1,11 +1,11 @@ // Copyright (C) 2017-2024 Smart code 203358507 -import React, { useMemo, useCallback, useState, forwardRef, memo } from 'react'; +import React, { useMemo, useCallback, useState, forwardRef, memo, useRef } from 'react'; import classNames from 'classnames'; import Icon from '@stremio/stremio-icons/react'; import { useServices } from 'stremio/services'; import { CONSTANTS } from 'stremio/common'; -import { MetaPreview, Video } from 'stremio/components'; +import { MetaPreview, Video, Button } from 'stremio/components'; import SeasonsBar from 'stremio/routes/MetaDetails/VideosList/SeasonsBar'; import styles from './SideDrawer.less'; @@ -13,6 +13,7 @@ type Props = { className?: string; seriesInfo: SeriesInfo; metaItem: MetaItem; + currentlyPlayingVideoID: string; closeSideDrawer: () => void; }; @@ -75,6 +76,14 @@ const SideDrawer = memo(forwardRef(({ seriesInfo, classNa event.stopPropagation(); }; + const currentlyPlayingVideoRef = useRef(null); + + const jumpToCurrentlyPlaying = () => { + const { current } = currentlyPlayingVideoRef; + + if (current) current.scrollIntoView({ behavior: 'smooth', block: 'center' }); + }; + return (
@@ -102,25 +111,28 @@ const SideDrawer = memo(forwardRef(({ seriesInfo, classNa onSelect={seasonOnSelect} />
+ {videos.map((video, index) => ( -
From 7dc0958e392d707b90ea6e26c93f808ae25ed090 Mon Sep 17 00:00:00 2001 From: Abdalrzag Eisa Date: Fri, 13 Jun 2025 22:00:17 +0300 Subject: [PATCH 09/23] Add `jump-to-currently-playing-btn` class --- src/routes/Player/SideDrawer/SideDrawer.less | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/routes/Player/SideDrawer/SideDrawer.less b/src/routes/Player/SideDrawer/SideDrawer.less index 2d2178c3e..e970a8ca7 100644 --- a/src/routes/Player/SideDrawer/SideDrawer.less +++ b/src/routes/Player/SideDrawer/SideDrawer.less @@ -1,5 +1,6 @@ // Copyright (C) 2017-2024 Smart code 203358507 +@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less'; @import (reference) '~stremio/common/screen-sizes.less'; :import('~stremio/components/MetaPreview/styles.less') { @@ -71,10 +72,22 @@ flex: 2; display: flex; flex-direction: column; + position: relative; .videos { overflow-y: auto; } + + .jump-to-currently-playing-btn { + position: absolute; + bottom: 1rem; + right: 1rem; + z-index: 1; + width: 2.5rem; + height: 2.5rem; + border-radius: 50%; + background-color: @color-primaryvariant1-light1-90; + } } } From 524bcd90da368ba7784febf12f80980e0df82366 Mon Sep 17 00:00:00 2001 From: Abdalrzag Eisa Date: Sat, 14 Jun 2025 02:38:11 +0300 Subject: [PATCH 10/23] automatically jump to video on mount, remove button --- src/routes/Player/SideDrawer/SideDrawer.less | 13 ------------- src/routes/Player/SideDrawer/SideDrawer.tsx | 14 +++++++------- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/src/routes/Player/SideDrawer/SideDrawer.less b/src/routes/Player/SideDrawer/SideDrawer.less index e970a8ca7..2d2178c3e 100644 --- a/src/routes/Player/SideDrawer/SideDrawer.less +++ b/src/routes/Player/SideDrawer/SideDrawer.less @@ -1,6 +1,5 @@ // Copyright (C) 2017-2024 Smart code 203358507 -@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less'; @import (reference) '~stremio/common/screen-sizes.less'; :import('~stremio/components/MetaPreview/styles.less') { @@ -72,22 +71,10 @@ flex: 2; display: flex; flex-direction: column; - position: relative; .videos { overflow-y: auto; } - - .jump-to-currently-playing-btn { - position: absolute; - bottom: 1rem; - right: 1rem; - z-index: 1; - width: 2.5rem; - height: 2.5rem; - border-radius: 50%; - background-color: @color-primaryvariant1-light1-90; - } } } diff --git a/src/routes/Player/SideDrawer/SideDrawer.tsx b/src/routes/Player/SideDrawer/SideDrawer.tsx index 365ba87ae..11ff4b995 100644 --- a/src/routes/Player/SideDrawer/SideDrawer.tsx +++ b/src/routes/Player/SideDrawer/SideDrawer.tsx @@ -1,11 +1,11 @@ // Copyright (C) 2017-2024 Smart code 203358507 -import React, { useMemo, useCallback, useState, forwardRef, memo, useRef } from 'react'; +import React, { useMemo, useCallback, useState, forwardRef, memo, useRef, useEffect } from 'react'; import classNames from 'classnames'; import Icon from '@stremio/stremio-icons/react'; import { useServices } from 'stremio/services'; import { CONSTANTS } from 'stremio/common'; -import { MetaPreview, Video, Button } from 'stremio/components'; +import { MetaPreview, Video } from 'stremio/components'; import SeasonsBar from 'stremio/routes/MetaDetails/VideosList/SeasonsBar'; import styles from './SideDrawer.less'; @@ -78,11 +78,12 @@ const SideDrawer = memo(forwardRef(({ seriesInfo, classNa const currentlyPlayingVideoRef = useRef(null); - const jumpToCurrentlyPlaying = () => { + useEffect(() => { const { current } = currentlyPlayingVideoRef; - - if (current) current.scrollIntoView({ behavior: 'smooth', block: 'center' }); - }; + if (current) { + setTimeout(() => { current.scrollIntoView({ behavior: 'smooth', block: 'center' }); }, 300); + } + }, []); return (
@@ -111,7 +112,6 @@ const SideDrawer = memo(forwardRef(({ seriesInfo, classNa onSelect={seasonOnSelect} />
- {videos.map((video, index) => (