mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-11-04 03:27:14 -05:00 
			
		
		
		
	Player cleanups
This commit is contained in:
		
							parent
							
								
									67da1563be
								
							
						
					
					
						commit
						b88cd583d3
					
				@ -80,6 +80,11 @@ const GlobalCssTheme = () => {
 | 
				
			|||||||
					width: 100%;
 | 
										width: 100%;
 | 
				
			||||||
					height: 100%;
 | 
										height: 100%;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									::cue {
 | 
				
			||||||
 | 
										background-color: transparent;
 | 
				
			||||||
 | 
										text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			`}</style>
 | 
								`}</style>
 | 
				
			||||||
			<WebTooltip theme={theme} />
 | 
								<WebTooltip theme={theme} />
 | 
				
			||||||
			<SkeletonCss />
 | 
								<SkeletonCss />
 | 
				
			||||||
 | 
				
			|||||||
@ -20,17 +20,18 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import { QueryIdentifier, QueryPage, WatchItem, WatchItemP, useFetch } from "@kyoo/models";
 | 
					import { QueryIdentifier, QueryPage, WatchItem, WatchItemP, useFetch } from "@kyoo/models";
 | 
				
			||||||
import { Head } from "@kyoo/primitives";
 | 
					import { Head } from "@kyoo/primitives";
 | 
				
			||||||
import { useState, useEffect, PointerEvent as ReactPointerEvent, ComponentProps } from "react";
 | 
					import { useState, useEffect, ComponentProps } from "react";
 | 
				
			||||||
import { Platform, Pressable, StyleSheet, View } from "react-native";
 | 
					import { Platform, Pressable, StyleSheet } from "react-native";
 | 
				
			||||||
import { useAtom, useAtomValue, useSetAtom } from "jotai";
 | 
					import { useTranslation } from "react-i18next";
 | 
				
			||||||
import { percent, useYoshiki } from "yoshiki/native";
 | 
					import { useRouter } from "solito/router";
 | 
				
			||||||
 | 
					import { useAtom } from "jotai";
 | 
				
			||||||
 | 
					import { useYoshiki } from "yoshiki/native";
 | 
				
			||||||
import { Back, Hover, LoadingIndicator } from "./components/hover";
 | 
					import { Back, Hover, LoadingIndicator } from "./components/hover";
 | 
				
			||||||
import { fullscreenAtom, playAtom, Video } from "./state";
 | 
					import { fullscreenAtom, playAtom, Video } from "./state";
 | 
				
			||||||
import { episodeDisplayNumber } from "../details/episode";
 | 
					import { episodeDisplayNumber } from "../details/episode";
 | 
				
			||||||
import { useVideoKeyboard } from "./keyboard";
 | 
					import { useVideoKeyboard } from "./keyboard";
 | 
				
			||||||
import { MediaSessionManager } from "./media-session";
 | 
					import { MediaSessionManager } from "./media-session";
 | 
				
			||||||
import { ErrorView } from "../fetch";
 | 
					import { ErrorView } from "../fetch";
 | 
				
			||||||
import { useTranslation } from "react-i18next";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const query = (slug: string): QueryIdentifier<WatchItem> => ({
 | 
					const query = (slug: string): QueryIdentifier<WatchItem> => ({
 | 
				
			||||||
	path: ["watch", slug],
 | 
						path: ["watch", slug],
 | 
				
			||||||
@ -68,6 +69,7 @@ let touchTimeout: NodeJS.Timeout;
 | 
				
			|||||||
export const Player: QueryPage<{ slug: string }> = ({ slug }) => {
 | 
					export const Player: QueryPage<{ slug: string }> = ({ slug }) => {
 | 
				
			||||||
	const { css } = useYoshiki();
 | 
						const { css } = useYoshiki();
 | 
				
			||||||
	const { t } = useTranslation();
 | 
						const { t } = useTranslation();
 | 
				
			||||||
 | 
						const router = useRouter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const [playbackError, setPlaybackError] = useState<string | undefined>(undefined);
 | 
						const [playbackError, setPlaybackError] = useState<string | undefined>(undefined);
 | 
				
			||||||
	const { data, error } = useFetch(query(slug));
 | 
						const { data, error } = useFetch(query(slug));
 | 
				
			||||||
@ -78,8 +80,6 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => {
 | 
				
			|||||||
	const next =
 | 
						const next =
 | 
				
			||||||
		data && !data.isMovie && data.nextEpisode ? `/watch/${data.nextEpisode.slug}` : undefined;
 | 
							data && !data.isMovie && data.nextEpisode ? `/watch/${data.nextEpisode.slug}` : undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// const { playerRef, videoProps, onVideoClick } = useVideoController(data?.link);
 | 
					 | 
				
			||||||
	// useSubtitleController(playerRef, data?.subtitles, data?.fonts);
 | 
					 | 
				
			||||||
	useVideoKeyboard(data?.subtitles, data?.fonts, previous, next);
 | 
						useVideoKeyboard(data?.subtitles, data?.fonts, previous, next);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const [isFullscreen, setFullscreen] = useAtom(fullscreenAtom);
 | 
						const [isFullscreen, setFullscreen] = useAtom(fullscreenAtom);
 | 
				
			||||||
@ -107,9 +107,6 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => {
 | 
				
			|||||||
		return () => document.removeEventListener("pointermove", handler);
 | 
							return () => document.removeEventListener("pointermove", handler);
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// useEffect(() => {
 | 
					 | 
				
			||||||
	// 	setPlay(true);
 | 
					 | 
				
			||||||
	// }, [slug, setPlay]);
 | 
					 | 
				
			||||||
	useEffect(() => {
 | 
						useEffect(() => {
 | 
				
			||||||
		if (Platform.OS !== "web" || !/Mobi/i.test(window.navigator.userAgent)) return;
 | 
							if (Platform.OS !== "web" || !/Mobi/i.test(window.navigator.userAgent)) return;
 | 
				
			||||||
		setFullscreen(true);
 | 
							setFullscreen(true);
 | 
				
			||||||
@ -148,12 +145,6 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => {
 | 
				
			|||||||
				next={next}
 | 
									next={next}
 | 
				
			||||||
				previous={previous}
 | 
									previous={previous}
 | 
				
			||||||
			/>
 | 
								/>
 | 
				
			||||||
			{/* <style jsx global>{` */}
 | 
					 | 
				
			||||||
			{/* 	::cue { */}
 | 
					 | 
				
			||||||
			{/* 		background-color: transparent; */}
 | 
					 | 
				
			||||||
			{/* 		text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000; */}
 | 
					 | 
				
			||||||
			{/* 	} */}
 | 
					 | 
				
			||||||
			{/* `}</style> */}
 | 
					 | 
				
			||||||
			<Pressable
 | 
								<Pressable
 | 
				
			||||||
				focusable={false}
 | 
									focusable={false}
 | 
				
			||||||
				onHoverOut={() => setMouseMoved(false)}
 | 
									onHoverOut={() => setMouseMoved(false)}
 | 
				
			||||||
@ -181,7 +172,7 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => {
 | 
				
			|||||||
										}, 400);
 | 
															}, 400);
 | 
				
			||||||
									setPlay(!isPlaying);
 | 
														setPlay(!isPlaying);
 | 
				
			||||||
							  }
 | 
												  }
 | 
				
			||||||
							: () => displayControls ? setMouseMoved(false) : show()
 | 
												: () => (displayControls ? setMouseMoved(false) : show())
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
					{...css(StyleSheet.absoluteFillObject)}
 | 
										{...css(StyleSheet.absoluteFillObject)}
 | 
				
			||||||
				>
 | 
									>
 | 
				
			||||||
@ -189,15 +180,15 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => {
 | 
				
			|||||||
						links={data?.link}
 | 
											links={data?.link}
 | 
				
			||||||
						setError={setPlaybackError}
 | 
											setError={setPlaybackError}
 | 
				
			||||||
						fonts={data?.fonts}
 | 
											fonts={data?.fonts}
 | 
				
			||||||
 | 
											onEnd={() => {
 | 
				
			||||||
 | 
												if (!data) return;
 | 
				
			||||||
 | 
												if (data.isMovie) router.push(`/movie/${data.slug}`);
 | 
				
			||||||
 | 
												else
 | 
				
			||||||
 | 
													router.push(
 | 
				
			||||||
 | 
														data.nextEpisode ? `/watch/${data.nextEpisode.slug}` : `/show/${data.showSlug}`,
 | 
				
			||||||
 | 
													);
 | 
				
			||||||
 | 
											}}
 | 
				
			||||||
						{...css(StyleSheet.absoluteFillObject)}
 | 
											{...css(StyleSheet.absoluteFillObject)}
 | 
				
			||||||
						// onEnded={() => {
 | 
					 | 
				
			||||||
						// 	if (!data) return;
 | 
					 | 
				
			||||||
						// 	if (data.isMovie) router.push(`/movie/${data.slug}`);
 | 
					 | 
				
			||||||
						// 	else
 | 
					 | 
				
			||||||
						// 		router.push(
 | 
					 | 
				
			||||||
						// 			data.nextEpisode ? `/watch/${data.nextEpisode.slug}` : `/show/${data.showSlug}`,
 | 
					 | 
				
			||||||
						// 		);
 | 
					 | 
				
			||||||
						// }}
 | 
					 | 
				
			||||||
					/>
 | 
										/>
 | 
				
			||||||
				</Pressable>
 | 
									</Pressable>
 | 
				
			||||||
				<LoadingIndicator />
 | 
									<LoadingIndicator />
 | 
				
			||||||
 | 
				
			|||||||
@ -19,7 +19,7 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Track, WatchItem } from "@kyoo/models";
 | 
					import { Track, WatchItem } from "@kyoo/models";
 | 
				
			||||||
import { atom, useAtomValue, useSetAtom } from "jotai";
 | 
					import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
 | 
				
			||||||
import { memo, useEffect, useLayoutEffect, useRef } from "react";
 | 
					import { memo, useEffect, useLayoutEffect, useRef } from "react";
 | 
				
			||||||
import NativeVideo, { VideoProperties as VideoProps } from "./video";
 | 
					import NativeVideo, { VideoProperties as VideoProps } from "./video";
 | 
				
			||||||
import { bakedAtom } from "../jotai-utils";
 | 
					import { bakedAtom } from "../jotai-utils";
 | 
				
			||||||
@ -75,13 +75,9 @@ export const Video = memo(function _Video({
 | 
				
			|||||||
	setError: (error: string | undefined) => void;
 | 
						setError: (error: string | undefined) => void;
 | 
				
			||||||
} & Partial<VideoProps>) {
 | 
					} & Partial<VideoProps>) {
 | 
				
			||||||
	const ref = useRef<NativeVideo | null>(null);
 | 
						const ref = useRef<NativeVideo | null>(null);
 | 
				
			||||||
	const isPlaying = useAtomValue(playAtom);
 | 
						const [isPlaying, setPlay] = useAtom(playAtom);
 | 
				
			||||||
	const setLoad = useSetAtom(loadAtom);
 | 
						const setLoad = useSetAtom(loadAtom);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	useLayoutEffect(() => {
 | 
					 | 
				
			||||||
		setLoad(true);
 | 
					 | 
				
			||||||
	}, [setLoad]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const publicProgress = useAtomValue(publicProgressAtom);
 | 
						const publicProgress = useAtomValue(publicProgressAtom);
 | 
				
			||||||
	const setPrivateProgress = useSetAtom(privateProgressAtom);
 | 
						const setPrivateProgress = useSetAtom(privateProgressAtom);
 | 
				
			||||||
	const setBuffered = useSetAtom(bufferedAtom);
 | 
						const setBuffered = useSetAtom(bufferedAtom);
 | 
				
			||||||
@ -90,6 +86,13 @@ export const Video = memo(function _Video({
 | 
				
			|||||||
		ref.current?.seek(publicProgress);
 | 
							ref.current?.seek(publicProgress);
 | 
				
			||||||
	}, [publicProgress]);
 | 
						}, [publicProgress]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						useLayoutEffect(() => {
 | 
				
			||||||
 | 
							// Reset the state when a new video is loaded.
 | 
				
			||||||
 | 
							setLoad(true);
 | 
				
			||||||
 | 
							setPrivateProgress(0);
 | 
				
			||||||
 | 
							setPlay(true);
 | 
				
			||||||
 | 
						}, [links, setLoad, setPrivateProgress, setPlay]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const volume = useAtomValue(volumeAtom);
 | 
						const volume = useAtomValue(volumeAtom);
 | 
				
			||||||
	const isMuted = useAtomValue(mutedAtom);
 | 
						const isMuted = useAtomValue(mutedAtom);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -125,6 +128,7 @@ export const Video = memo(function _Video({
 | 
				
			|||||||
			onLoad={(info) => {
 | 
								onLoad={(info) => {
 | 
				
			||||||
				setDuration(info.duration);
 | 
									setDuration(info.duration);
 | 
				
			||||||
			}}
 | 
								}}
 | 
				
			||||||
 | 
								onPlayPause={setPlay}
 | 
				
			||||||
			selectedTextTrack={
 | 
								selectedTextTrack={
 | 
				
			||||||
				subtitle
 | 
									subtitle
 | 
				
			||||||
					? {
 | 
										? {
 | 
				
			||||||
@ -135,10 +139,6 @@ export const Video = memo(function _Video({
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			fonts={fonts}
 | 
								fonts={fonts}
 | 
				
			||||||
			// TODO: textTracks: external subtitles
 | 
								// TODO: textTracks: external subtitles
 | 
				
			||||||
			// onError: () => {
 | 
					 | 
				
			||||||
			// 	if (player?.current?.error?.code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED)
 | 
					 | 
				
			||||||
			// 		setPlayMode(PlayMode.Transmux);
 | 
					 | 
				
			||||||
			// },
 | 
					 | 
				
			||||||
		/>
 | 
							/>
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
@ -28,15 +28,16 @@ import {
 | 
				
			|||||||
	useRef,
 | 
						useRef,
 | 
				
			||||||
} from "react";
 | 
					} from "react";
 | 
				
			||||||
import { VideoProps } from "react-native-video";
 | 
					import { VideoProps } from "react-native-video";
 | 
				
			||||||
import { atom, useAtom, useAtomValue } from "jotai";
 | 
					import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
 | 
				
			||||||
import { useYoshiki } from "yoshiki";
 | 
					import { useYoshiki } from "yoshiki";
 | 
				
			||||||
import SubtitleOctopus from "libass-wasm";
 | 
					import SubtitleOctopus from "libass-wasm";
 | 
				
			||||||
import { subtitleAtom } from "./state";
 | 
					import { playAtom, subtitleAtom } from "./state";
 | 
				
			||||||
import Hls from "hls.js";
 | 
					import Hls from "hls.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare module "react-native-video" {
 | 
					declare module "react-native-video" {
 | 
				
			||||||
	interface VideoProperties {
 | 
						interface VideoProperties {
 | 
				
			||||||
		fonts?: Font[];
 | 
							fonts?: Font[];
 | 
				
			||||||
 | 
							onPlayPause: (isPlaying: boolean) => void;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	export type VideoProps = Omit<VideoProperties, "source"> & {
 | 
						export type VideoProps = Omit<VideoProperties, "source"> & {
 | 
				
			||||||
		source: { uri?: string; transmux?: string };
 | 
							source: { uri?: string; transmux?: string };
 | 
				
			||||||
@ -52,7 +53,19 @@ const playModeAtom = atom<PlayMode>(PlayMode.Direct);
 | 
				
			|||||||
let hls: Hls | null = null;
 | 
					let hls: Hls | null = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Video = forwardRef<{ seek: (value: number) => void }, VideoProps>(function _Video(
 | 
					const Video = forwardRef<{ seek: (value: number) => void }, VideoProps>(function _Video(
 | 
				
			||||||
	{ source, paused, muted, volume, onBuffer, onLoad, onProgress, onError, fonts },
 | 
						{
 | 
				
			||||||
 | 
							source,
 | 
				
			||||||
 | 
							paused,
 | 
				
			||||||
 | 
							muted,
 | 
				
			||||||
 | 
							volume,
 | 
				
			||||||
 | 
							onBuffer,
 | 
				
			||||||
 | 
							onLoad,
 | 
				
			||||||
 | 
							onProgress,
 | 
				
			||||||
 | 
							onError,
 | 
				
			||||||
 | 
							onEnd,
 | 
				
			||||||
 | 
							onPlayPause,
 | 
				
			||||||
 | 
							fonts,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
	forwaredRef,
 | 
						forwaredRef,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
	const ref = useRef<HTMLVideoElement>(null);
 | 
						const ref = useRef<HTMLVideoElement>(null);
 | 
				
			||||||
@ -87,7 +100,6 @@ const Video = forwardRef<{ seek: (value: number) => void }, VideoProps>(function
 | 
				
			|||||||
	}, [source.uri, setPlayMode]);
 | 
						}, [source.uri, setPlayMode]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	useLayoutEffect(() => {
 | 
						useLayoutEffect(() => {
 | 
				
			||||||
		console.log("toto");
 | 
					 | 
				
			||||||
		const src = playMode === PlayMode.Direct ? source?.uri : source?.transmux;
 | 
							const src = playMode === PlayMode.Direct ? source?.uri : source?.transmux;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!ref?.current || !src) return;
 | 
							if (!ref?.current || !src) return;
 | 
				
			||||||
@ -105,9 +117,17 @@ const Video = forwardRef<{ seek: (value: number) => void }, VideoProps>(function
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}, [playMode, source?.uri, source?.transmux]);
 | 
						}, [playMode, source?.uri, source?.transmux]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const setPlay = useSetAtom(playAtom);
 | 
				
			||||||
 | 
						useEffect(() => {
 | 
				
			||||||
 | 
							if (!ref.current) return;
 | 
				
			||||||
 | 
							// Set play state to the player's value (if autoplay is denied)
 | 
				
			||||||
 | 
							setPlay(!ref.current.paused);
 | 
				
			||||||
 | 
						}, [setPlay]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<video
 | 
							<video
 | 
				
			||||||
			ref={ref}
 | 
								ref={ref}
 | 
				
			||||||
 | 
								src={source.uri}
 | 
				
			||||||
			muted={muted}
 | 
								muted={muted}
 | 
				
			||||||
			autoPlay={!paused}
 | 
								autoPlay={!paused}
 | 
				
			||||||
			onCanPlay={() => onBuffer?.call(null, { isBuffering: false })}
 | 
								onCanPlay={() => onBuffer?.call(null, { isBuffering: false })}
 | 
				
			||||||
@ -138,6 +158,9 @@ const Video = forwardRef<{ seek: (value: number) => void }, VideoProps>(function
 | 
				
			|||||||
					});
 | 
										});
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}}
 | 
								}}
 | 
				
			||||||
 | 
								onPlay={() => onPlayPause?.call(null, true)}
 | 
				
			||||||
 | 
								onPause={() => onPlayPause?.call(null, false)}
 | 
				
			||||||
 | 
								onEnded={onEnd}
 | 
				
			||||||
			{...css({ width: "100%", height: "100%" })}
 | 
								{...css({ width: "100%", height: "100%" })}
 | 
				
			||||||
		/>
 | 
							/>
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user