Refac prepare updating watched state based on progress endpoint

This commit is contained in:
MerlinScheurer
2025-01-30 18:33:46 +01:00
parent 9146ecb82d
commit 43bfa1ab93
6 changed files with 35 additions and 47 deletions

View File

@@ -1,11 +1,23 @@
import APIClient from '../../functions/APIClient'; import APIClient from '../../functions/APIClient';
type VideoProgressResponseType = {
watched: boolean;
duration: number;
duration_str: string;
watched_date: number;
position: number;
youtube_id: string;
};
type VideoProgressProp = { type VideoProgressProp = {
youtubeId: string; youtubeId: string;
currentProgress: number; currentProgress: number;
}; };
const updateVideoProgressById = async ({ youtubeId, currentProgress }: VideoProgressProp) => { const updateVideoProgressById = async ({
youtubeId,
currentProgress,
}: VideoProgressProp): Promise<VideoProgressResponseType> => {
return APIClient(`/api/video/${youtubeId}/progress/`, { return APIClient(`/api/video/${youtubeId}/progress/`, {
method: 'POST', method: 'POST',
body: { position: currentProgress }, body: { position: currentProgress },

View File

@@ -115,6 +115,7 @@ const EmbeddableVideoPlayer = ({ videoId }: EmbeddableVideoPlayerProps) => {
setSearchParams({}); setSearchParams({});
}} }}
/> />
<WatchedCheckBox <WatchedCheckBox
watched={watched} watched={watched}
onClick={async status => { onClick={async status => {
@@ -127,12 +128,16 @@ const EmbeddableVideoPlayer = ({ videoId }: EmbeddableVideoPlayerProps) => {
setRefresh(true); setRefresh(true);
}} }}
/> />
{cast && ( {cast && (
<GoogleCast <GoogleCast
video={video} video={video}
setRefresh={() => { setRefresh={() => {
setRefresh(true); setRefresh(true);
}} }}
onWatchStateChanged={() => {
setRefresh(true);
}}
/> />
)} )}

View File

@@ -1,8 +1,6 @@
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { VideoType } from '../pages/Home'; import { VideoType } from '../pages/Home';
import updateWatchedState from '../api/actions/updateWatchedState';
import updateVideoProgressById from '../api/actions/updateVideoProgressById'; import updateVideoProgressById from '../api/actions/updateVideoProgressById';
import watchedThreshold from '../functions/watchedThreshold';
const getURL = () => { const getURL = () => {
return window.location.origin; return window.location.origin;
@@ -29,6 +27,7 @@ async function castVideoProgress(
duration: number; duration: number;
}, },
video: VideoType | undefined, video: VideoType | undefined,
onWatchStateChanged?: (status: boolean) => void,
) { ) {
if (!video) { if (!video) {
console.log('castVideoProgress: Video to cast not found...'); console.log('castVideoProgress: Video to cast not found...');
@@ -42,19 +41,13 @@ async function castVideoProgress(
if (currentTime % 10 <= 1.0 && currentTime !== 0 && duration !== 0) { if (currentTime % 10 <= 1.0 && currentTime !== 0 && duration !== 0) {
// Check progress every 10 seconds or else progress is checked a few times a second // Check progress every 10 seconds or else progress is checked a few times a second
await updateVideoProgressById({ const videoProgressResponse = await updateVideoProgressById({
youtubeId: videoId, youtubeId: videoId,
currentProgress: currentTime, currentProgress: currentTime,
}); });
if (!video.player.watched) { if (videoProgressResponse.watched && video.player.watched !== videoProgressResponse.watched) {
// Check if video is already marked as watched onWatchStateChanged?.(true);
if (watchedThreshold(currentTime, duration)) {
await updateWatchedState({
id: videoId,
is_watched: true,
});
}
} }
} }
} }
@@ -93,9 +86,10 @@ async function castVideoPaused(
type GoogleCastProps = { type GoogleCastProps = {
video?: VideoType; video?: VideoType;
setRefresh?: () => void; setRefresh?: () => void;
onWatchStateChanged?: (status: boolean) => void;
}; };
const GoogleCast = ({ video, setRefresh }: GoogleCastProps) => { const GoogleCast = ({ video, setRefresh, onWatchStateChanged }: GoogleCastProps) => {
const [isConnected, setIsConnected] = useState(false); const [isConnected, setIsConnected] = useState(false);
const setup = useCallback(() => { const setup = useCallback(() => {
@@ -118,12 +112,14 @@ const GoogleCast = ({ video, setRefresh }: GoogleCastProps) => {
setIsConnected(player.isConnected); setIsConnected(player.isConnected);
}, },
); );
playerController.addEventListener( playerController.addEventListener(
cast.framework.RemotePlayerEventType.CURRENT_TIME_CHANGED, cast.framework.RemotePlayerEventType.CURRENT_TIME_CHANGED,
function () { function () {
castVideoProgress(player, video); castVideoProgress(player, video, onWatchStateChanged);
}, },
); );
playerController.addEventListener( playerController.addEventListener(
cast.framework.RemotePlayerEventType.IS_PAUSED_CHANGED, cast.framework.RemotePlayerEventType.IS_PAUSED_CHANGED,
function () { function () {
@@ -131,6 +127,8 @@ const GoogleCast = ({ video, setRefresh }: GoogleCastProps) => {
setRefresh?.(); setRefresh?.();
}, },
); );
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [setRefresh, video]); }, [setRefresh, video]);
const startPlayback = useCallback(() => { const startPlayback = useCallback(() => {

View File

@@ -1,7 +1,6 @@
import updateVideoProgressById from '../api/actions/updateVideoProgressById'; import updateVideoProgressById from '../api/actions/updateVideoProgressById';
import updateWatchedState from '../api/actions/updateWatchedState'; import updateWatchedState from '../api/actions/updateWatchedState';
import { SponsorBlockSegmentType, SponsorBlockType, VideoResponseType } from '../pages/Video'; import { SponsorBlockSegmentType, SponsorBlockType, VideoResponseType } from '../pages/Video';
import watchedThreshold from '../functions/watchedThreshold';
import { Dispatch, Fragment, SetStateAction, SyntheticEvent, useState } from 'react'; import { Dispatch, Fragment, SetStateAction, SyntheticEvent, useState } from 'react';
import formatTime from '../functions/formatTime'; import formatTime from '../functions/formatTime';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
@@ -82,21 +81,13 @@ const handleTimeUpdate =
if (currentTime < 10) return; if (currentTime < 10) return;
if (Number((currentTime % 10).toFixed(1)) <= 0.2) { if (Number((currentTime % 10).toFixed(1)) <= 0.2) {
// Check progress every 10 seconds or else progress is checked a few times a second // Check progress every 10 seconds or else progress is checked a few times a second
await updateVideoProgressById({ const videoProgressResponse = await updateVideoProgressById({
youtubeId, youtubeId,
currentProgress: currentTime, currentProgress: currentTime,
}); });
if (!watched) { if (videoProgressResponse.watched && watched !== videoProgressResponse.watched) {
// Check if video is already marked as watched onWatchStateChanged?.(true);
if (watchedThreshold(currentTime, duration)) {
await updateWatchedState({
id: youtubeId,
is_watched: true,
});
onWatchStateChanged?.(true);
}
} }
} }
}; };

View File

@@ -1,21 +0,0 @@
function watchedThreshold(currentTime: number, duration: number) {
let watched = false;
if (duration <= 1800) {
// If video is less than 30 min
if (currentTime / duration >= 0.9) {
// Mark as watched at 90%
watched = true;
}
} else {
// If video is more than 30 min
if (currentTime >= duration - 120) {
// Mark as watched if there is two minutes left
watched = true;
}
}
return watched;
}
export default watchedThreshold;

View File

@@ -229,6 +229,9 @@ const Video = () => {
setRefresh={() => { setRefresh={() => {
setRefreshVideoList(true); setRefreshVideoList(true);
}} }}
onWatchStateChanged={() => {
setRefreshVideoList(true);
}}
/> />
)} )}
<h1 id="video-title">{video.title}</h1> <h1 id="video-title">{video.title}</h1>