From 0492198dff429985ac08849033471979cc810221 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Tue, 7 Oct 2025 12:00:52 +0200 Subject: [PATCH] Some cleanup --- .../src/core/VideoPlayer.web.ts | 77 +++++++++++++------ .../src/core/VideoPlayerEvents.ts | 3 +- .../src/core/hooks/useVideoPlayer.ts | 2 +- .../src/core/hooks/useVideoPlayer.web.ts | 44 ----------- .../src/core/utils/playerFactory.ts | 3 +- .../src/core/utils/sourceFactory.ts | 10 +-- .../src/core/utils/sourceUtils.ts | 11 +++ 7 files changed, 70 insertions(+), 80 deletions(-) delete mode 100644 packages/react-native-video/src/core/hooks/useVideoPlayer.web.ts create mode 100644 packages/react-native-video/src/core/utils/sourceUtils.ts diff --git a/packages/react-native-video/src/core/VideoPlayer.web.ts b/packages/react-native-video/src/core/VideoPlayer.web.ts index ed07d218..cba7df00 100644 --- a/packages/react-native-video/src/core/VideoPlayer.web.ts +++ b/packages/react-native-video/src/core/VideoPlayer.web.ts @@ -17,12 +17,16 @@ import { WebEventEmiter } from "./WebEventEmiter"; class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase { protected player = new shaka.Player(); protected video: HTMLVideoElement; + protected headers: Record = {}; constructor(source: VideoSource | VideoConfig | VideoPlayerSource) { const video = document.createElement("video"); super(new WebEventEmiter(video)); this.video = video; this.player.attach(this.video); + this.player.getNetworkingEngine()!.registerRequestFilter((_type, request) => { + request.headers = this.headers; + }); this.replaceSourceAsync(source); } @@ -59,7 +63,10 @@ class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase { // Source get source(): VideoPlayerSource { - return this.player.source; + return { + uri: this.player.getAssetUri()!, + config: {}, + }; } // Status @@ -155,30 +162,38 @@ class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase { return true; } - set playInBackground(value: boolean) { - this.player.playInBackground = value; + set playInBackground(_: boolean) { + if (__DEV__) { + console.warn( + "playInBackground is not supported on this platform, it wont have any effect", + ); + } } // Play When Inactive get playWhenInactive(): boolean { - return this.player.playWhenInactive; + return true; } - set playWhenInactive(value: boolean) { - this.player.playWhenInactive = value; + set playWhenInactive(_: boolean) { + if (__DEV__) { + console.warn( + "playWhenInactive is not supported on this platform, it wont have any effect", + ); + } } // Is Playing get isPlaying(): boolean { - return this.player.isPlaying; + return this.status === "readyToPlay" && !this.video.paused; } async initialize(): Promise { - await this.wrapPromise(this.player.initialize()); + // noop on web } async preload(): Promise { - this.player.load(this.media, this.startTime); + // we start loading when initializing the source. } /** @@ -230,33 +245,49 @@ class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase { | NoAutocomplete | null, ): Promise { - this.video.src = - typeof source === "object" && "uri" in source + const src = + typeof source === "object" && source && "uri" in source ? source.uri : source; + if (typeof src === "number") { + console.error("A source uri must be a string. Numbers are only supported on native."); + return; + } + // TODO: handle start time + this.player.load(src) + if (typeof source !== "object") return; + + this.headers = source?.headers ?? {}; + // this.player.configure({ + // drm: undefined, + // streaming: { + // bufferingGoal: source?.bufferConfig?.maxBufferMs, + // }, + // } satisfies Partial); } // Text Track Management getAvailableTextTracks(): TextTrack[] { - try { - return this.player.getAvailableTextTracks(); - } catch (error) { - this.throwError(error); - return []; - } + return this.player.getTextTracks().map(x => ({ + id: x.id.toString(), + label: x.label ?? "", + language: x.language, + selected: x.active, + })); } selectTextTrack(textTrack: TextTrack | null): void { - try { - this.player.selectTextTrack(textTrack); - } catch (error) { - this.throwError(error); - } + this.player.setTextTrackVisibility(textTrack !== null) + if (!textTrack) return; + const track = this.player + .getTextTracks() + .find((x) => x.id === Number(textTrack.id)); + if (track) this.player.selectTextTrack(track); } // Selected Text Track get selectedTrack(): TextTrack | undefined { - return this.player.selectedTrack; + return this.getAvailableTextTracks().find(x => x.selected); } } diff --git a/packages/react-native-video/src/core/VideoPlayerEvents.ts b/packages/react-native-video/src/core/VideoPlayerEvents.ts index 0bc15ed6..18011157 100644 --- a/packages/react-native-video/src/core/VideoPlayerEvents.ts +++ b/packages/react-native-video/src/core/VideoPlayerEvents.ts @@ -1,11 +1,10 @@ -import type { VideoPlayerEventEmitter } from '../spec/nitro/VideoPlayerEventEmitter.nitro'; import { ALL_PLAYER_EVENTS, type AllPlayerEvents as PlayerEvents, } from './types/Events'; export class VideoPlayerEvents { - protected eventEmitter: VideoPlayerEventEmitter; + protected eventEmitter: PlayerEvents; protected eventListeners: Partial< Record void>> > = {}; diff --git a/packages/react-native-video/src/core/hooks/useVideoPlayer.ts b/packages/react-native-video/src/core/hooks/useVideoPlayer.ts index 747cfda1..5e88df74 100644 --- a/packages/react-native-video/src/core/hooks/useVideoPlayer.ts +++ b/packages/react-native-video/src/core/hooks/useVideoPlayer.ts @@ -1,7 +1,7 @@ import type { VideoPlayerSource } from '../../spec/nitro/VideoPlayerSource.nitro'; import type { NoAutocomplete } from '../types/Utils'; import type { VideoConfig, VideoSource } from '../types/VideoConfig'; -import { isVideoPlayerSource } from '../utils/sourceFactory'; +import { isVideoPlayerSource } from '../utils/sourceUtils'; import { VideoPlayer } from '../VideoPlayer'; import { useManagedInstance } from './useManagedInstance'; diff --git a/packages/react-native-video/src/core/hooks/useVideoPlayer.web.ts b/packages/react-native-video/src/core/hooks/useVideoPlayer.web.ts deleted file mode 100644 index 747cfda1..00000000 --- a/packages/react-native-video/src/core/hooks/useVideoPlayer.web.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { VideoPlayerSource } from '../../spec/nitro/VideoPlayerSource.nitro'; -import type { NoAutocomplete } from '../types/Utils'; -import type { VideoConfig, VideoSource } from '../types/VideoConfig'; -import { isVideoPlayerSource } from '../utils/sourceFactory'; -import { VideoPlayer } from '../VideoPlayer'; -import { useManagedInstance } from './useManagedInstance'; - -const sourceEqual = ( - a: T, - b?: T -) => { - if (isVideoPlayerSource(a) && isVideoPlayerSource(b)) { - return a.equals(b); - } - - return JSON.stringify(a) === JSON.stringify(b); -}; - -/** - * Creates a `VideoPlayer` instance and manages its lifecycle. - * - * @param source - The source of the video to play - * @param setup - A function to setup the player - * @returns The `VideoPlayer` instance - */ -export const useVideoPlayer = ( - source: VideoConfig | VideoSource | NoAutocomplete, - setup?: (player: VideoPlayer) => void -) => { - return useManagedInstance( - { - factory: () => { - const player = new VideoPlayer(source); - setup?.(player); - return player; - }, - cleanup: (player) => { - player.__destroy(); - }, - dependenciesEqualFn: sourceEqual, - }, - [JSON.stringify(source)] - ); -}; diff --git a/packages/react-native-video/src/core/utils/playerFactory.ts b/packages/react-native-video/src/core/utils/playerFactory.ts index 7efe0f46..96ec447f 100644 --- a/packages/react-native-video/src/core/utils/playerFactory.ts +++ b/packages/react-native-video/src/core/utils/playerFactory.ts @@ -5,8 +5,9 @@ import type { } from '../../spec/nitro/VideoPlayer.nitro'; import type { VideoPlayerSource } from '../../spec/nitro/VideoPlayerSource.nitro'; import type { VideoConfig, VideoSource } from '../types/VideoConfig'; -import { createSource, isVideoPlayerSource } from './sourceFactory'; +import { createSource } from './sourceFactory'; import { tryParseNativeVideoError } from '../types/VideoError'; +import { isVideoPlayerSource } from './sourceUtils'; const VideoPlayerFactory = NitroModules.createHybridObject('VideoPlayerFactory'); diff --git a/packages/react-native-video/src/core/utils/sourceFactory.ts b/packages/react-native-video/src/core/utils/sourceFactory.ts index 36f49fa2..6db12f7e 100644 --- a/packages/react-native-video/src/core/utils/sourceFactory.ts +++ b/packages/react-native-video/src/core/utils/sourceFactory.ts @@ -12,21 +12,13 @@ import type { VideoSource, } from '../types/VideoConfig'; import { tryParseNativeVideoError } from '../types/VideoError'; +import { isVideoPlayerSource } from './sourceUtils'; const VideoPlayerSourceFactory = NitroModules.createHybridObject( 'VideoPlayerSourceFactory' ); -export const isVideoPlayerSource = (obj: any): obj is VideoPlayerSource => { - return ( - obj && // obj is not null - typeof obj === 'object' && // obj is an object - 'name' in obj && // obj has a name property - obj.name === 'VideoPlayerSource' // obj.name is 'VideoPlayerSource' - ); -}; - /** * Creates a `VideoPlayerSource` instance from a URI (string). * diff --git a/packages/react-native-video/src/core/utils/sourceUtils.ts b/packages/react-native-video/src/core/utils/sourceUtils.ts new file mode 100644 index 00000000..0c82b1cb --- /dev/null +++ b/packages/react-native-video/src/core/utils/sourceUtils.ts @@ -0,0 +1,11 @@ +import type { VideoPlayerSource } from "../../spec/nitro/VideoPlayerSource.nitro"; + +export const isVideoPlayerSource = (obj: any): obj is VideoPlayerSource => { + return ( + obj && // obj is not null + typeof obj === 'object' && // obj is an object + 'name' in obj && // obj has a name property + obj.name === 'VideoPlayerSource' // obj.name is 'VideoPlayerSource' + ); +}; +