From 067adde12460b2a63893e156fe4cfd130523704d Mon Sep 17 00:00:00 2001 From: Krzysztof Moch Date: Sat, 7 Oct 2023 12:56:35 +0200 Subject: [PATCH] chore: lint project (#3279) * chore: update eslint config * chore: lint lib files --- .eslintignore | 1 + .eslintrc | 7 +- src/Video.tsx | 321 ++++++++++++++++++++++-------------- src/VideoNativeComponent.ts | 285 +++++++++++++++++++------------- src/index.ts | 16 +- src/lib/DRMType.ts | 8 +- src/lib/FilterType.ts | 32 ++-- src/lib/VideoResizeMode.ts | 2 +- src/types/events.ts | 76 +++++---- src/types/language.ts | 2 +- src/types/video.ts | 99 ++++++----- src/utils.ts | 26 ++- 12 files changed, 521 insertions(+), 354 deletions(-) diff --git a/.eslintignore b/.eslintignore index d838da98..badede9e 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,2 @@ examples/ +lib/ \ No newline at end of file diff --git a/.eslintrc b/.eslintrc index e67012ec..2c39af83 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,10 +1,13 @@ { + "plugins": ["@typescript-eslint"], "extends": [ "@react-native", "eslint:recommended", - "plugin:react/recommended" + "plugin:react/recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" ], "parserOptions": { "requireConfigFile": false } -} \ No newline at end of file +} diff --git a/src/Video.tsx b/src/Video.tsx index d7b3e43e..9a839d24 100644 --- a/src/Video.tsx +++ b/src/Video.tsx @@ -6,17 +6,21 @@ import React, { forwardRef, useImperativeHandle, type ComponentRef, -} from "react"; -import { - View, - StyleSheet, - Image, - Platform, -} from "react-native"; -import NativeVideoComponent, { RCTVideoConstants } from "./VideoNativeComponent"; -import type { NativeVideoResizeMode, OnAudioFocusChangedData, OnAudioTracksData, OnPlaybackStateChangedData, OnTextTracksData, OnTimedMetadataData, OnVideoErrorData, OnVideoTracksData } from './VideoNativeComponent' +} from 'react'; +import {View, StyleSheet, Image, Platform} from 'react-native'; +import NativeVideoComponent, {RCTVideoConstants} from './VideoNativeComponent'; +import type { + NativeVideoResizeMode, + OnAudioFocusChangedData, + OnAudioTracksData, + OnPlaybackStateChangedData, + OnTextTracksData, + OnTimedMetadataData, + OnVideoErrorData, + OnVideoTracksData, +} from './VideoNativeComponent'; -import type { StyleProp, ImageStyle, NativeSyntheticEvent } from "react-native"; +import type {StyleProp, ImageStyle, NativeSyntheticEvent} from 'react-native'; import { type VideoComponentType, type OnLoadData, @@ -30,9 +34,9 @@ import { type OnExternalPlaybackChangeData, type OnReceiveAdEventData, VideoManager, -} from "./VideoNativeComponent"; -import type { ReactVideoProps } from "./types/video"; -import { getReactTag, resolveAssetSourceForVideo } from "./utils"; +} from './VideoNativeComponent'; +import type {ReactVideoProps} from './types/video'; +import {getReactTag, resolveAssetSourceForVideo} from './utils'; export interface VideoRef { seek: (time: number, tolerance?: number) => void; @@ -40,7 +44,9 @@ export interface VideoRef { pause: () => void; presentFullscreenPlayer: () => void; dismissFullscreenPlayer: () => void; - restoreUserInterfaceForPictureInPictureStopCompleted: (restore: boolean) => void; + restoreUserInterfaceForPictureInPictureStopCompleted: ( + restore: boolean, + ) => void; } const Video = forwardRef( @@ -84,7 +90,7 @@ const Video = forwardRef( onVideoTracks, ...rest }, - ref + ref, ) => { const nativeRef = useRef>(null); const [showPoster, setShowPoster] = useState(!!poster); @@ -98,25 +104,31 @@ const Video = forwardRef( () => ({ ...StyleSheet.absoluteFillObject, resizeMode: - posterResizeMode && posterResizeMode !== "none" + posterResizeMode && posterResizeMode !== 'none' ? posterResizeMode - : "contain", + : 'contain', }), - [posterResizeMode] + [posterResizeMode], ); const src = useMemo(() => { - if (!source) return undefined; + if (!source) { + return undefined; + } const resolvedSource = resolveAssetSourceForVideo(source); - let uri = resolvedSource.uri || ""; - if (uri && uri.match(/^\//)) uri = `file://${uri}`; - if (!uri) console.warn("Trying to load empty source"); + let uri = resolvedSource.uri || ''; + if (uri && uri.match(/^\//)) { + uri = `file://${uri}`; + } + if (!uri) { + console.warn('Trying to load empty source'); + } const isNetwork = !!(uri && uri.match(/^https?:/)); const isAsset = !!( uri && uri.match( - /^(assets-library|ipod-library|file|content|ms-appx|ms-appdata):/ + /^(assets-library|ipod-library|file|content|ms-appx|ms-appdata):/, ) ); @@ -125,22 +137,22 @@ const Video = forwardRef( isNetwork, isAsset, shouldCache: resolvedSource.shouldCache || false, - type: resolvedSource.type || "", + type: resolvedSource.type || '', mainVer: resolvedSource.mainVer || 0, patchVer: resolvedSource.patchVer || 0, requestHeaders: resolvedSource?.headers || {}, startTime: resolvedSource.startTime || 0, - endTime: resolvedSource.endTime + endTime: resolvedSource.endTime, }; }, [source]); const _resizeMode: NativeVideoResizeMode = useMemo(() => { switch (resizeMode) { - case "contain": + case 'contain': return RCTVideoConstants.ScaleAspectFit; - case "cover": + case 'cover': return RCTVideoConstants.ScaleAspectFill; - case "stretch": + case 'stretch': return RCTVideoConstants.ScaleToFill; default: return RCTVideoConstants.ScaleNone; @@ -148,7 +160,9 @@ const Video = forwardRef( }, [resizeMode]); const _drm = useMemo(() => { - if (!drm) return; + if (!drm) { + return; + } return { drmType: drm.type, licenseServer: drm.licenseServer, @@ -160,59 +174,64 @@ const Video = forwardRef( }; }, [drm]); - const _selectedTextTrack = useMemo(() => { - if (!selectedTextTrack) return; - if (typeof selectedTextTrack?.value === 'number') return { - seletedTextType: selectedTextTrack?.type, - index: selectedTextTrack?.value, + if (!selectedTextTrack) { + return; + } + if (typeof selectedTextTrack?.value === 'number') { + return { + selectedTextType: selectedTextTrack?.type, + index: selectedTextTrack?.value, + }; } return { selectedTextType: selectedTextTrack?.type, value: selectedTextTrack?.value, - } + }; }, [selectedTextTrack]); const _selectedAudioTrack = useMemo(() => { - if (!selectedAudioTrack) return; - if (typeof selectedAudioTrack?.value === 'number') return { - selectedAudioType: selectedAudioTrack?.type, - index: selectedAudioTrack?.value, + if (!selectedAudioTrack) { + return; + } + if (typeof selectedAudioTrack?.value === 'number') { + return { + selectedAudioType: selectedAudioTrack?.type, + index: selectedAudioTrack?.value, + }; } return { selectedAudioType: selectedAudioTrack?.type, value: selectedAudioTrack?.value, - } + }; }, [selectedAudioTrack]); + const seek = useCallback(async (time: number, tolerance?: number) => { + if (isNaN(time)) { + throw new Error('Specified time is not a number'); + } - const seek = useCallback( - async (time: number, tolerance?: number) => { - if (isNaN(time)) throw new Error("Specified time is not a number"); + if (!nativeRef.current) { + console.warn('Video Component is not mounted'); + return; + } - if (!nativeRef.current) { - console.warn("Video Component is not mounted"); - return; - } - - Platform.select({ - ios: () => { - nativeRef.current?.setNativeProps({ - seek: { - time, - tolerance, - }, - }); - }, - default: () => { - nativeRef.current?.setNativeProps({ - seek: time, - }); - }, - })(); - }, - [] - ); + Platform.select({ + ios: () => { + nativeRef.current?.setNativeProps({ + seek: { + time, + tolerance, + }, + }); + }, + default: () => { + nativeRef.current?.setNativeProps({ + seek: time, + }); + }, + })(); + }, []); const presentFullscreenPlayer = useCallback(() => { setIsFullscreen(true); @@ -238,79 +257,93 @@ const Video = forwardRef( (restored: boolean) => { setRestoreUserInterfaceForPIPStopCompletionHandler(restored); }, - [setRestoreUserInterfaceForPIPStopCompletionHandler] + [setRestoreUserInterfaceForPIPStopCompletionHandler], ); const onVideoLoadStart = useCallback( (e: NativeSyntheticEvent) => { onLoadStart?.(e.nativeEvent); }, - [onLoadStart] + [onLoadStart], ); const onVideoLoad = useCallback( (e: NativeSyntheticEvent) => { - if (Platform.OS === "windows") setShowPoster(false); + if (Platform.OS === 'windows') { + setShowPoster(false); + } onLoad?.(e.nativeEvent); }, - [onLoad, setShowPoster] + [onLoad, setShowPoster], ); const onVideoError = useCallback( (e: NativeSyntheticEvent) => { onError?.(e.nativeEvent); }, - [onError] + [onError], ); const onVideoProgress = useCallback( (e: NativeSyntheticEvent) => { onProgress?.(e.nativeEvent); }, - [onProgress] + [onProgress], ); const onVideoSeek = useCallback( (e: NativeSyntheticEvent) => { onSeek?.(e.nativeEvent); }, - [onSeek] + [onSeek], ); // android only - const onVideoPlaybackStateChanged = useCallback((e: NativeSyntheticEvent) => { - onPlaybackStateChanged?.(e.nativeEvent); - }, [onPlaybackStateChanged]) + const onVideoPlaybackStateChanged = useCallback( + (e: NativeSyntheticEvent) => { + onPlaybackStateChanged?.(e.nativeEvent); + }, + [onPlaybackStateChanged], + ); // android only const onVideoIdle = useCallback(() => { - onIdle?.() - }, [onIdle]) + onIdle?.(); + }, [onIdle]); const _onTimedMetadata = useCallback( (e: NativeSyntheticEvent) => { onTimedMetadata?.(e.nativeEvent); }, - [onTimedMetadata] + [onTimedMetadata], ); - const _onAudioTracks = useCallback((e: NativeSyntheticEvent) => { - onAudioTracks?.(e.nativeEvent) - }, [onAudioTracks]) + const _onAudioTracks = useCallback( + (e: NativeSyntheticEvent) => { + onAudioTracks?.(e.nativeEvent); + }, + [onAudioTracks], + ); - const _onTextTracks = useCallback((e: NativeSyntheticEvent) => { - onTextTracks?.(e.nativeEvent) - }, [onTextTracks]) + const _onTextTracks = useCallback( + (e: NativeSyntheticEvent) => { + onTextTracks?.(e.nativeEvent); + }, + [onTextTracks], + ); - const _onVideoTracks = useCallback((e: NativeSyntheticEvent) => { - onVideoTracks?.(e.nativeEvent) - }, [onVideoTracks]) + const _onVideoTracks = useCallback( + (e: NativeSyntheticEvent) => { + onVideoTracks?.(e.nativeEvent); + }, + [onVideoTracks], + ); const _onPlaybackRateChange = useCallback( - (e: NativeSyntheticEvent>) => { + (e: NativeSyntheticEvent>) => { onPlaybackRateChange?.(e.nativeEvent); }, - [onPlaybackRateChange] + [onPlaybackRateChange], ); const _onReadyForDisplay = useCallback(() => { @@ -322,51 +355,91 @@ const Video = forwardRef( (e: NativeSyntheticEvent) => { onPictureInPictureStatusChanged?.(e.nativeEvent); }, - [onPictureInPictureStatusChanged] + [onPictureInPictureStatusChanged], ); - const _onAudioFocusChanged = useCallback((e: NativeSyntheticEvent) => { - onAudioFocusChanged?.(e.nativeEvent) - }, [onAudioFocusChanged]) + const _onAudioFocusChanged = useCallback( + (e: NativeSyntheticEvent) => { + onAudioFocusChanged?.(e.nativeEvent); + }, + [onAudioFocusChanged], + ); - const onVideoBuffer = useCallback((e: NativeSyntheticEvent) => { - onBuffer?.(e.nativeEvent); - }, [onBuffer]); + const onVideoBuffer = useCallback( + (e: NativeSyntheticEvent) => { + onBuffer?.(e.nativeEvent); + }, + [onBuffer], + ); - const onVideoExternalPlaybackChange = useCallback((e: NativeSyntheticEvent) => { - onExternalPlaybackChange?.(e.nativeEvent); - }, [onExternalPlaybackChange]) + const onVideoExternalPlaybackChange = useCallback( + (e: NativeSyntheticEvent) => { + onExternalPlaybackChange?.(e.nativeEvent); + }, + [onExternalPlaybackChange], + ); - const _onBandwidthUpdate = useCallback((e: NativeSyntheticEvent) => { - onBandwidthUpdate?.(e.nativeEvent); - }, [onBandwidthUpdate]); + const _onBandwidthUpdate = useCallback( + (e: NativeSyntheticEvent) => { + onBandwidthUpdate?.(e.nativeEvent); + }, + [onBandwidthUpdate], + ); - const _onReceiveAdEvent = useCallback((e: NativeSyntheticEvent) => { - onReceiveAdEvent?.(e.nativeEvent); - }, [onReceiveAdEvent]); + const _onReceiveAdEvent = useCallback( + (e: NativeSyntheticEvent) => { + onReceiveAdEvent?.(e.nativeEvent); + }, + [onReceiveAdEvent], + ); const onGetLicense = useCallback( (event: NativeSyntheticEvent) => { if (drm && drm.getLicense instanceof Function) { const data = event.nativeEvent; if (data && data.spcBase64) { - const getLicenseOverride = drm.getLicense(data.spcBase64, data.contentId, data.licenseUrl); + const getLicenseOverride = drm.getLicense( + data.spcBase64, + data.contentId, + data.licenseUrl, + ); const getLicensePromise = Promise.resolve(getLicenseOverride); // Handles both scenarios, getLicenseOverride being a promise and not. - getLicensePromise.then((result => { - if (result !== undefined) { - nativeRef.current && VideoManager.setLicenseResult(result, data.licenseUrl, getReactTag(nativeRef)); - } else { - nativeRef.current && VideoManager.setLicenseResultError('Empty license result', data.licenseUrl, getReactTag(nativeRef)); - } - })).catch(() => { - nativeRef.current && VideoManager.setLicenseResultError('fetch error', data.licenseUrl, getReactTag(nativeRef)); - }); + getLicensePromise + .then((result) => { + if (result !== undefined) { + nativeRef.current && + VideoManager.setLicenseResult( + result, + data.licenseUrl, + getReactTag(nativeRef), + ); + } else { + nativeRef.current && + VideoManager.setLicenseResultError( + 'Empty license result', + data.licenseUrl, + getReactTag(nativeRef), + ); + } + }) + .catch(() => { + nativeRef.current && + VideoManager.setLicenseResultError( + 'fetch error', + data.licenseUrl, + getReactTag(nativeRef), + ); + }); } else { - VideoManager.setLicenseResultError('No spc received', data.licenseUrl, getReactTag(nativeRef)); + VideoManager.setLicenseResultError( + 'No spc received', + data.licenseUrl, + getReactTag(nativeRef), + ); } } - }, - [drm] + }, + [drm], ); useImperativeHandle( @@ -388,7 +461,7 @@ const Video = forwardRef( pause, resume, restoreUserInterfaceForPictureInPictureStopCompleted, - ] + ], ); return ( @@ -438,12 +511,12 @@ const Video = forwardRef( onReceiveAdEvent={_onReceiveAdEvent} /> {showPoster ? ( - + ) : null} ); - } + }, ); -Video.displayName = "Video"; -export default Video; \ No newline at end of file +Video.displayName = 'Video'; +export default Video; diff --git a/src/VideoNativeComponent.ts b/src/VideoNativeComponent.ts index 2cf6bbb6..e79ff99e 100644 --- a/src/VideoNativeComponent.ts +++ b/src/VideoNativeComponent.ts @@ -1,11 +1,15 @@ -import type { HostComponent, NativeSyntheticEvent, ViewProps } from 'react-native'; -import { NativeModules, requireNativeComponent } from 'react-native'; -import { getViewManagerConfig } from './utils'; +import type { + HostComponent, + NativeSyntheticEvent, + ViewProps, +} from 'react-native'; +import {NativeModules, requireNativeComponent} from 'react-native'; +import {getViewManagerConfig} from './utils'; // -------- There are types for native component (future codegen) -------- // if you are looking for types for react component, see src/types/video.ts -type Headers = Record +type Headers = Record; type VideoSrc = Readonly<{ uri?: string; @@ -18,25 +22,25 @@ type VideoSrc = Readonly<{ requestHeaders?: Headers; startTime?: number; endTime?: number; -}> +}>; - -export type Filter = 'None' | -'CIColorInvert' | -'CIColorMonochrome' | -'CIColorPosterize' | -'CIFalseColor' | -'CIMaximumComponent' | -'CIMinimumComponent' | -'CIPhotoEffectChrome' | -'CIPhotoEffectFade' | -'CIPhotoEffectInstant' | -'CIPhotoEffectMono' | -'CIPhotoEffectNoir' | -'CIPhotoEffectProcess' | -'CIPhotoEffectTonal' | -'CIPhotoEffectTransfer' | -'CISepiaTone'; +export type Filter = + | 'None' + | 'CIColorInvert' + | 'CIColorMonochrome' + | 'CIColorPosterize' + | 'CIFalseColor' + | 'CIMaximumComponent' + | 'CIMinimumComponent' + | 'CIPhotoEffectChrome' + | 'CIPhotoEffectFade' + | 'CIPhotoEffectInstant' + | 'CIPhotoEffectMono' + | 'CIPhotoEffectNoir' + | 'CIPhotoEffectProcess' + | 'CIPhotoEffectTonal' + | 'CIPhotoEffectTransfer' + | 'CISepiaTone'; export type DrmType = 'widevine' | 'playready' | 'clearkey' | 'fairplay'; @@ -48,14 +52,16 @@ type Drm = Readonly<{ certificateUrl?: string; // ios base64Certificate?: boolean; // ios default: false useExternalGetLicense?: boolean; // ios -}> +}>; -type TextTracks = ReadonlyArray> +type TextTracks = ReadonlyArray< + Readonly<{ + title: string; + language: string; + type: string; + uri: string; + }> +>; type TextTrackType = 'system' | 'disabled' | 'title' | 'language' | 'index'; @@ -63,7 +69,7 @@ type SelectedTextTrack = Readonly<{ selectedTextType: TextTrackType; value?: string; index?: number; -}> +}>; type AudioTrackType = 'system' | 'disabled' | 'title' | 'language' | 'index'; @@ -71,12 +77,12 @@ type SelectedAudioTrack = Readonly<{ selectedAudioType: AudioTrackType; value?: string; index?: number; -}> +}>; export type Seek = Readonly<{ time: number; tolerance?: number; -}> +}>; type BufferConfig = Readonly<{ minBufferMs?: number; @@ -86,12 +92,12 @@ type BufferConfig = Readonly<{ maxHeapAllocationPercent?: number; minBackBufferMemoryReservePercent?: number; minBufferMemoryReservePercent?: number; -}> +}>; type SelectedVideoTrack = Readonly<{ type: 'auto' | 'disabled' | 'resolution' | 'index'; value?: number; -}> +}>; type SubtitleStyle = Readonly<{ fontSize?: number; @@ -99,7 +105,7 @@ type SubtitleStyle = Readonly<{ paddingBottom?: number; paddingLeft?: number; paddingRight?: number; -}> +}>; export type OnLoadData = Readonly<{ currentTime: number; @@ -109,22 +115,21 @@ export type OnLoadData = Readonly<{ height: number; orientation: 'portrait' | 'landscape'; }>; -}> +}>; export type OnLoadStartData = Readonly<{ isNetwork: boolean; type: string; uri: string; -}> - -export type OnBufferData = Readonly<{ isBuffering: boolean }>; +}>; +export type OnBufferData = Readonly<{isBuffering: boolean}>; export type OnProgressData = Readonly<{ currentTime: number; playableDuration: number; seekableDuration: number; -}> +}>; export type OnBandwidthUpdateData = Readonly<{ bitrate: number; @@ -134,54 +139,61 @@ export type OnSeekData = Readonly<{ currentTime: number; seekTime: number; finished: boolean; -}> +}>; export type OnPlaybackStateChangedData = Readonly<{ isPlaying: boolean; -}> +}>; export type OnTimedMetadataData = Readonly<{ - metadata: ReadonlyArray> -}> - + metadata: ReadonlyArray< + Readonly<{ + value?: string; + identifier: string; + }> + >; +}>; export type OnAudioTracksData = Readonly<{ - audioTracks: ReadonlyArray> -}> + audioTracks: ReadonlyArray< + Readonly<{ + index: number; + title?: string; + language?: string; + bitrate?: number; + type?: string; + selected?: boolean; + }> + >; +}>; export type OnTextTracksData = Readonly<{ - textTracks: ReadonlyArray> -}> + textTracks: ReadonlyArray< + Readonly<{ + index: number; + title?: string; + language?: string; + /** + * iOS only supports VTT, Android supports all 3 + */ + type?: 'srt' | 'ttml' | 'vtt'; + selected?: boolean; + }> + >; +}>; export type OnVideoTracksData = Readonly<{ - videoTracks: ReadonlyArray> -}> + videoTracks: ReadonlyArray< + Readonly<{ + trackId: number; + codecs?: string; + width?: number; + height?: number; + bitrate?: number; + selected?: boolean; + }> + >; +}>; export type OnPlaybackData = Readonly<{ playbackRate: number; @@ -189,31 +201,31 @@ export type OnPlaybackData = Readonly<{ export type OnExternalPlaybackChangeData = Readonly<{ isExternalPlaybackActive: boolean; -}> +}>; export type OnGetLicenseData = Readonly<{ licenseUrl: string; contentId: string; spcBase64: string; -}> +}>; export type OnPictureInPictureStatusChangedData = Readonly<{ isActive: boolean; -}> +}>; export type OnReceiveAdEventData = Readonly<{ event: string; -}> +}>; export type OnVideoErrorData = Readonly<{ error: string; -}> +}>; export type OnAudioFocusChangedData = Readonly<{ hasFocus: boolean; -}> +}>; -export type NativeVideoResizeMode = 'ScaleNone' | 'ScaleToFill' | 'ScaleAspectFit' | 'ScaleAspectFill'; +export type NativeVideoResizeMode = unknown; export interface VideoNativeProps extends ViewProps { src?: VideoSrc; drm?: Drm; @@ -222,7 +234,7 @@ export interface VideoNativeProps extends ViewProps { maxBitRate?: number; resizeMode?: NativeVideoResizeMode; repeat?: boolean; - automaticallyWaitsToMinimizeStalling?: boolean + automaticallyWaitsToMinimizeStalling?: boolean; textTracks?: TextTracks; selectedTextTrack?: SelectedTextTrack; selectedAudioTrack?: SelectedAudioTrack; @@ -252,39 +264,63 @@ export interface VideoNativeProps extends ViewProps { contentStartTime?: number; // Android currentPlaybackTime?: number; // Android disableDisconnectError?: boolean; // Android - focusable?: boolean; // Android + focusable?: boolean; // Android hideShutterView?: boolean; // Android - minLoadRetryCount?: number; // Android + minLoadRetryCount?: number; // Android reportBandwidth?: boolean; //Android selectedVideoTrack?: SelectedVideoTrack; // android - subtitleStyle?: SubtitleStyle // android + subtitleStyle?: SubtitleStyle; // android trackId?: string; // Android - useTextureView?: boolean; // Android - useSecureView?: boolean; // Android + useTextureView?: boolean; // Android + useSecureView?: boolean; // Android onVideoLoad?: (event: NativeSyntheticEvent) => void; onVideoLoadStart?: (event: NativeSyntheticEvent) => void; onVideoBuffer?: (event: NativeSyntheticEvent) => void; onVideoError?: (event: NativeSyntheticEvent) => void; onVideoProgress?: (event: NativeSyntheticEvent) => void; - onBandwidthUpdate?: (event: NativeSyntheticEvent) => void; + onBandwidthUpdate?: ( + event: NativeSyntheticEvent, + ) => void; onVideoSeek?: (event: NativeSyntheticEvent) => void; - onVideoEnd?: (event: NativeSyntheticEvent>) => void; // all - onVideoAudioBecomingNoisy?: (event: NativeSyntheticEvent>) => void; - onVideoFullscreenPlayerWillPresent?: (event: NativeSyntheticEvent>) => void; // ios, android - onVideoFullscreenPlayerDidPresent?: (event: NativeSyntheticEvent>) => void; // ios, android - onVideoFullscreenPlayerWillDismiss?: (event: NativeSyntheticEvent>) => void; // ios, android - onVideoFullscreenPlayerDidDismiss?: (event: NativeSyntheticEvent>) => void; // ios, android - onReadyForDisplay?: (event: NativeSyntheticEvent>) => void; + onVideoEnd?: (event: NativeSyntheticEvent>) => void; // all + onVideoAudioBecomingNoisy?: ( + event: NativeSyntheticEvent>, + ) => void; + onVideoFullscreenPlayerWillPresent?: ( + event: NativeSyntheticEvent>, + ) => void; // ios, android + onVideoFullscreenPlayerDidPresent?: ( + event: NativeSyntheticEvent>, + ) => void; // ios, android + onVideoFullscreenPlayerWillDismiss?: ( + event: NativeSyntheticEvent>, + ) => void; // ios, android + onVideoFullscreenPlayerDidDismiss?: ( + event: NativeSyntheticEvent>, + ) => void; // ios, android + onReadyForDisplay?: (event: NativeSyntheticEvent>) => void; onPlaybackRateChange?: (event: NativeSyntheticEvent) => void; // all - onVideoExternalPlaybackChange?: (event: NativeSyntheticEvent) => void; + onVideoExternalPlaybackChange?: ( + event: NativeSyntheticEvent, + ) => void; onGetLicense?: (event: NativeSyntheticEvent) => void; - onPictureInPictureStatusChanged?: (event: NativeSyntheticEvent) => void; - onRestoreUserInterfaceForPictureInPictureStop?: (event: NativeSyntheticEvent>) => void; - onReceiveAdEvent?: (event: NativeSyntheticEvent) => void; - onVideoPlaybackStateChanged?: (event: NativeSyntheticEvent) => void; // android only - onVideoIdle?: (event: NativeSyntheticEvent<{}>) => void; // android only (nowhere in document, so do not use as props. just type declaration) - onAudioFocusChanged?: (event: NativeSyntheticEvent) => void; // android only (nowhere in document, so do not use as props. just type declaration) + onPictureInPictureStatusChanged?: ( + event: NativeSyntheticEvent, + ) => void; + onRestoreUserInterfaceForPictureInPictureStop?: ( + event: NativeSyntheticEvent>, + ) => void; + onReceiveAdEvent?: ( + event: NativeSyntheticEvent, + ) => void; + onVideoPlaybackStateChanged?: ( + event: NativeSyntheticEvent, + ) => void; // android only + onVideoIdle?: (event: NativeSyntheticEvent) => void; // android only (nowhere in document, so do not use as props. just type declaration) + onAudioFocusChanged?: ( + event: NativeSyntheticEvent, + ) => void; // android only (nowhere in document, so do not use as props. just type declaration) onTimedMetadata?: (event: NativeSyntheticEvent) => void; // ios, android onAudioTracks?: (event: NativeSyntheticEvent) => void; // android onTextTracks?: (event: NativeSyntheticEvent) => void; // android @@ -296,28 +332,45 @@ export type VideoComponentType = HostComponent; export interface VideoManagerType { save: (reactTag: number) => Promise; setPlayerPauseState: (paused: boolean, reactTag: number) => Promise; - setLicenseResult: (result: string, licenseUrl: string, reactTag: number) => Promise; - setLicenseResultError: (error: string, licenseUrl: string, reactTag: number) => Promise; + setLicenseResult: ( + result: string, + licenseUrl: string, + reactTag: number, + ) => Promise; + setLicenseResultError: ( + error: string, + licenseUrl: string, + reactTag: number, + ) => Promise; } export interface VideoDecoderPropertiesType { getWidevineLevel: () => Promise; - isCodecSupported: (mimeType: string, width: number, height: number) => Promise<'unsupported' | 'hardware' | 'software'>; + isCodecSupported: ( + mimeType: string, + width: number, + height: number, + ) => Promise<'unsupported' | 'hardware' | 'software'>; isHEVCSupported: () => Promise<'unsupported' | 'hardware' | 'software'>; } export type VideoViewManagerConfig = { Constants: { - ScaleNone: any; - ScaleToFill: any; - ScaleAspectFit: any; - ScaleAspectFill: any; + ScaleNone: unknown; + ScaleToFill: unknown; + ScaleAspectFit: unknown; + ScaleAspectFill: unknown; }; - Commands: { [key: string]: number; }; + Commands: {[key: string]: number}; }; export const VideoManager = NativeModules.VideoManager as VideoManagerType; -export const VideoDecoderProperties = NativeModules.VideoDecoderProperties as VideoDecoderPropertiesType; -export const RCTVideoConstants = (getViewManagerConfig('RCTVideo') as VideoViewManagerConfig).Constants; +export const VideoDecoderProperties = + NativeModules.VideoDecoderProperties as VideoDecoderPropertiesType; +export const RCTVideoConstants = ( + getViewManagerConfig('RCTVideo') as VideoViewManagerConfig +).Constants; -export default requireNativeComponent('RCTVideo') as VideoComponentType; +export default requireNativeComponent( + 'RCTVideo', +) as VideoComponentType; diff --git a/src/index.ts b/src/index.ts index 7412ec80..fb396b3e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,11 @@ -import Video from "./Video"; +import Video from './Video'; -export { default as FilterType } from './lib/FilterType'; -export { default as VideoResizeMode } from './lib/VideoResizeMode'; -export { default as TextTrackType } from './lib/TextTrackType'; -export { default as DRMType } from './lib/DRMType'; -export { VideoDecoderProperties } from './VideoNativeComponent'; +export {default as FilterType} from './lib/FilterType'; +export {default as VideoResizeMode} from './lib/VideoResizeMode'; +export {default as TextTrackType} from './lib/TextTrackType'; +export {default as DRMType} from './lib/DRMType'; +export {VideoDecoderProperties} from './VideoNativeComponent'; -export type { VideoRef } from './Video'; +export type {VideoRef} from './Video'; -export default Video; \ No newline at end of file +export default Video; diff --git a/src/lib/DRMType.ts b/src/lib/DRMType.ts index e59791fe..4982965b 100644 --- a/src/lib/DRMType.ts +++ b/src/lib/DRMType.ts @@ -1,6 +1,6 @@ export default { - WIDEVINE: 'widevine', - PLAYREADY: 'playready', - CLEARKEY: 'clearkey', - FAIRPLAY: 'fairplay', + WIDEVINE: 'widevine', + PLAYREADY: 'playready', + CLEARKEY: 'clearkey', + FAIRPLAY: 'fairplay', } as const; diff --git a/src/lib/FilterType.ts b/src/lib/FilterType.ts index e096415f..32be8b07 100644 --- a/src/lib/FilterType.ts +++ b/src/lib/FilterType.ts @@ -1,18 +1,18 @@ export default { - NONE: '', - INVERT: 'CIColorInvert', - MONOCHROME: 'CIColorMonochrome', - POSTERIZE: 'CIColorPosterize', - FALSE: 'CIFalseColor', - MAXIMUMCOMPONENT: 'CIMaximumComponent', - MINIMUMCOMPONENT: 'CIMinimumComponent', - CHROME: 'CIPhotoEffectChrome', - FADE: 'CIPhotoEffectFade', - INSTANT: 'CIPhotoEffectInstant', - MONO: 'CIPhotoEffectMono', - NOIR: 'CIPhotoEffectNoir', - PROCESS: 'CIPhotoEffectProcess', - TONAL: 'CIPhotoEffectTonal', - TRANSFER: 'CIPhotoEffectTransfer', - SEPIA: 'CISepiaTone', + NONE: '', + INVERT: 'CIColorInvert', + MONOCHROME: 'CIColorMonochrome', + POSTERIZE: 'CIColorPosterize', + FALSE: 'CIFalseColor', + MAXIMUMCOMPONENT: 'CIMaximumComponent', + MINIMUMCOMPONENT: 'CIMinimumComponent', + CHROME: 'CIPhotoEffectChrome', + FADE: 'CIPhotoEffectFade', + INSTANT: 'CIPhotoEffectInstant', + MONO: 'CIPhotoEffectMono', + NOIR: 'CIPhotoEffectNoir', + PROCESS: 'CIPhotoEffectProcess', + TONAL: 'CIPhotoEffectTonal', + TRANSFER: 'CIPhotoEffectTransfer', + SEPIA: 'CISepiaTone', } as const; diff --git a/src/lib/VideoResizeMode.ts b/src/lib/VideoResizeMode.ts index 235803e2..f407a212 100644 --- a/src/lib/VideoResizeMode.ts +++ b/src/lib/VideoResizeMode.ts @@ -2,4 +2,4 @@ export default { contain: 'contain', cover: 'cover', stretch: 'stretch', -} as const; \ No newline at end of file +} as const; diff --git a/src/types/events.ts b/src/types/events.ts index cbdacc9e..36399e7e 100644 --- a/src/types/events.ts +++ b/src/types/events.ts @@ -1,30 +1,50 @@ -import type { OnBandwidthUpdateData, OnBufferData, OnLoadData, OnLoadStartData, OnProgressData, OnSeekData, OnPlaybackData, OnExternalPlaybackChangeData, OnPictureInPictureStatusChangedData, OnReceiveAdEventData, OnVideoErrorData, OnPlaybackStateChangedData, OnAudioFocusChangedData, OnTimedMetadataData, OnAudioTracksData, OnTextTracksData, OnVideoTracksData } from "../VideoNativeComponent"; +import type { + OnBandwidthUpdateData, + OnBufferData, + OnLoadData, + OnLoadStartData, + OnProgressData, + OnSeekData, + OnPlaybackData, + OnExternalPlaybackChangeData, + OnPictureInPictureStatusChangedData, + OnReceiveAdEventData, + OnVideoErrorData, + OnPlaybackStateChangedData, + OnAudioFocusChangedData, + OnTimedMetadataData, + OnAudioTracksData, + OnTextTracksData, + OnVideoTracksData, +} from '../VideoNativeComponent'; export interface ReactVideoEvents { - onAudioBecomingNoisy?: () => void //Android, iOS - onAudioFocusChanged?: (e: OnAudioFocusChangedData) => void // Android - onIdle?: () => void // Android - onBandwidthUpdate?: (e: OnBandwidthUpdateData) => void //Android - onBuffer?: (e: OnBufferData) => void //Android, iOS - onEnd?: () => void //All - onError?: (e: OnVideoErrorData) => void //Android, iOS - onExternalPlaybackChange?: (e: OnExternalPlaybackChangeData) => void //iOS - onFullscreenPlayerWillPresent?: () => void //Android, iOS - onFullscreenPlayerDidPresent?: () => void //Android, iOS - onFullscreenPlayerWillDismiss?: () => void //Android, iOS - onFullscreenPlayerDidDismiss?: () => void //Android, iOS - onLoad?: (e: OnLoadData) => void //All - onLoadStart?: (e: OnLoadStartData) => void //All - onPictureInPictureStatusChanged?: (e: OnPictureInPictureStatusChangedData) => void //iOS - onPlaybackRateChange?: (e: OnPlaybackData) => void //All - onProgress?: (e: OnProgressData) => void //All - onReadyForDisplay?: () => void //Android, iOS, Web - onReceiveAdEvent?: (e: OnReceiveAdEventData) => void //Android, iOS - onRestoreUserInterfaceForPictureInPictureStop?: () => void //iOS - onSeek?: (e: OnSeekData) => void //Android, iOS, Windows UWP - onPlaybackStateChanged?: (e: OnPlaybackStateChangedData) => void // Android - onTimedMetadata?: (e: OnTimedMetadataData) => void //Android, iOS - onAudioTracks?: (e: OnAudioTracksData) => void // Android - onTextTracks?: (e: OnTextTracksData) => void //Android - onVideoTracks?: (e: OnVideoTracksData) => void //Android -} \ No newline at end of file + onAudioBecomingNoisy?: () => void; //Android, iOS + onAudioFocusChanged?: (e: OnAudioFocusChangedData) => void; // Android + onIdle?: () => void; // Android + onBandwidthUpdate?: (e: OnBandwidthUpdateData) => void; //Android + onBuffer?: (e: OnBufferData) => void; //Android, iOS + onEnd?: () => void; //All + onError?: (e: OnVideoErrorData) => void; //Android, iOS + onExternalPlaybackChange?: (e: OnExternalPlaybackChangeData) => void; //iOS + onFullscreenPlayerWillPresent?: () => void; //Android, iOS + onFullscreenPlayerDidPresent?: () => void; //Android, iOS + onFullscreenPlayerWillDismiss?: () => void; //Android, iOS + onFullscreenPlayerDidDismiss?: () => void; //Android, iOS + onLoad?: (e: OnLoadData) => void; //All + onLoadStart?: (e: OnLoadStartData) => void; //All + onPictureInPictureStatusChanged?: ( + e: OnPictureInPictureStatusChangedData, + ) => void; //iOS + onPlaybackRateChange?: (e: OnPlaybackData) => void; //All + onProgress?: (e: OnProgressData) => void; //All + onReadyForDisplay?: () => void; //Android, iOS, Web + onReceiveAdEvent?: (e: OnReceiveAdEventData) => void; //Android, iOS + onRestoreUserInterfaceForPictureInPictureStop?: () => void; //iOS + onSeek?: (e: OnSeekData) => void; //Android, iOS, Windows UWP + onPlaybackStateChanged?: (e: OnPlaybackStateChangedData) => void; // Android + onTimedMetadata?: (e: OnTimedMetadataData) => void; //Android, iOS + onAudioTracks?: (e: OnAudioTracksData) => void; // Android + onTextTracks?: (e: OnTextTracksData) => void; //Android + onVideoTracks?: (e: OnVideoTracksData) => void; //Android +} diff --git a/src/types/language.ts b/src/types/language.ts index ec721833..8aa3b311 100644 --- a/src/types/language.ts +++ b/src/types/language.ts @@ -181,4 +181,4 @@ export type ISO639_1 = | 'yo' | 'za' | 'zh' - | 'zu'; \ No newline at end of file + | 'zu'; diff --git a/src/types/video.ts b/src/types/video.ts index 2a824cc3..4fb414ac 100644 --- a/src/types/video.ts +++ b/src/types/video.ts @@ -1,25 +1,24 @@ -import type { ISO639_1 } from './language'; -import type { ReactVideoEvents } from './events'; -import type { StyleProp, ViewStyle } from 'react-native' - -type Filter = | 'None' - | 'CIColorInvert' - | 'CIColorMonochrome' - | 'CIColorPosterize' - | 'CIFalseColor' - | 'CIMaximumComponent' - | 'CIMinimumComponent' - | 'CIPhotoEffectChrome' - | 'CIPhotoEffectFade' - | 'CIPhotoEffectInstant' - | 'CIPhotoEffectMono' - | 'CIPhotoEffectNoir' - | 'CIPhotoEffectProcess' - | 'CIPhotoEffectTonal' - | 'CIPhotoEffectTransfer' - | 'CISepiaTone' - +import type {ISO639_1} from './language'; +import type {ReactVideoEvents} from './events'; +import type {StyleProp, ViewStyle} from 'react-native'; +type Filter = + | 'None' + | 'CIColorInvert' + | 'CIColorMonochrome' + | 'CIColorPosterize' + | 'CIFalseColor' + | 'CIMaximumComponent' + | 'CIMinimumComponent' + | 'CIPhotoEffectChrome' + | 'CIPhotoEffectFade' + | 'CIPhotoEffectInstant' + | 'CIPhotoEffectMono' + | 'CIPhotoEffectNoir' + | 'CIPhotoEffectProcess' + | 'CIPhotoEffectTonal' + | 'CIPhotoEffectTransfer' + | 'CISepiaTone'; type Headers = Record; @@ -43,8 +42,14 @@ export type ReactVideoDrm = Readonly<{ contentId?: string; // ios certificateUrl?: string; // ios base64Certificate?: boolean; // ios default: false - getLicense?: (licenseUrl: string, contentId: string, spcBase64: string) => void; // ios -}> + /* eslint-disable @typescript-eslint/no-unused-vars */ + getLicense?: ( + licenseUrl: string, + contentId: string, + spcBase64: string, + ) => void; // ios + /* eslint-enable @typescript-eslint/no-unused-vars */ +}>; type BufferConfig = { minBufferMs?: number; @@ -54,17 +59,17 @@ type BufferConfig = { maxHeapAllocationPercent?: number; minBackBufferMemoryReservePercent?: number; minBufferMemoryReservePercent?: number; -} +}; type SelectedTrack = { type: 'system' | 'disabled' | 'title' | 'language' | 'index'; value?: string | number; -} +}; type SelectedVideoTrack = { - type: 'auto' | 'disabled' | 'resolution' | 'index' + type: 'auto' | 'disabled' | 'resolution' | 'index'; value?: number; -} +}; type SubtitleStyle = { fontSize?: number; @@ -72,16 +77,14 @@ type SubtitleStyle = { paddingBottom?: number; paddingLeft?: number; paddingRight?: number; -} +}; type TextTracks = { title: string; language: ISO639_1; - type: | 'application/x-subrip' - | 'application/ttml+xml' - | 'text/vtt'; + type: 'application/x-subrip' | 'application/ttml+xml' | 'text/vtt'; uri: string; -}[] +}[]; export interface ReactVideoProps extends ReactVideoEvents { source?: ReactVideoSource; @@ -98,24 +101,30 @@ export interface ReactVideoProps extends ReactVideoEvents { disableFocus?: boolean; disableDisconnectError?: boolean; // Android filter?: Filter; // iOS - filterEnabled?: boolean; // iOS - focusable?: boolean; // Android + filterEnabled?: boolean; // iOS + focusable?: boolean; // Android fullscreen?: boolean; // iOS fullscreenAutorotate?: boolean; // iOS - fullscreenOrientation?: 'all' | 'landscape' | 'portrait'; // iOS + fullscreenOrientation?: 'all' | 'landscape' | 'portrait'; // iOS hideShutterView?: boolean; // Android - ignoreSilentSwitch?: 'inherit' | 'ignore' | 'obey' // iOS - minLoadRetryCount?: number; // Android + ignoreSilentSwitch?: 'inherit' | 'ignore' | 'obey'; // iOS + minLoadRetryCount?: number; // Android maxBitRate?: number; mixWithOthers?: 'inherit' | 'mix' | 'duck'; // iOS muted?: boolean; paused?: boolean; - pictureInPicture?: boolean // iOS + pictureInPicture?: boolean; // iOS playInBackground?: boolean; - playWhenInactive?: boolean // iOS + playWhenInactive?: boolean; // iOS poster?: string; - posterResizeMode?: 'contain' | 'center' | 'cover' | 'none' | 'repeat' | 'stretch'; - preferredForwardBufferDuration?: number// iOS + posterResizeMode?: + | 'contain' + | 'center' + | 'cover' + | 'none' + | 'repeat' + | 'stretch'; + preferredForwardBufferDuration?: number; // iOS preventsDisplaySleepDuringVideoPlayback?: boolean; progressUpdateInterval?: number; rate?: number; @@ -125,11 +134,11 @@ export interface ReactVideoProps extends ReactVideoEvents { selectedAudioTrack?: SelectedTrack; selectedTextTrack?: SelectedTrack; selectedVideoTrack?: SelectedVideoTrack; // android - subtitleStyle?: SubtitleStyle // android + subtitleStyle?: SubtitleStyle; // android textTracks?: TextTracks; trackId?: string; // Android - useTextureView?: boolean; // Android - useSecureView?: boolean; // Android + useTextureView?: boolean; // Android + useSecureView?: boolean; // Android volume?: number; localSourceEncryptionKeyScheme?: string; -} \ No newline at end of file +} diff --git a/src/utils.ts b/src/utils.ts index 8a32035d..d24bc025 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,7 +1,7 @@ -import type { Component, RefObject, ComponentClass } from 'react'; -import { Image, UIManager, findNodeHandle } from "react-native"; -import type { ImageSourcePropType } from 'react-native'; -import type { ReactVideoSource } from './types/video'; +import type {Component, RefObject, ComponentClass} from 'react'; +import {Image, UIManager, findNodeHandle} from 'react-native'; +import type {ImageSourcePropType} from 'react-native'; +import type {ReactVideoSource} from './types/video'; type Source = ImageSourcePropType | ReactVideoSource; @@ -14,24 +14,32 @@ export function resolveAssetSourceForVideo(source: Source): ReactVideoSource { return source as ReactVideoSource; } -export function getReactTag(ref: RefObject | ComponentClass | null>): number { +export function getReactTag( + ref: RefObject< + | Component + | ComponentClass + | null + >, +): number { if (!ref.current) { - throw new Error("Video Component is not mounted"); + throw new Error('Video Component is not mounted'); } const reactTag = findNodeHandle(ref.current); if (!reactTag) { - throw new Error("Cannot find reactTag for Video Component in components tree"); + throw new Error( + 'Cannot find reactTag for Video Component in components tree', + ); } return reactTag; } export function getViewManagerConfig(name: string) { - if('getViewManagerConfig' in UIManager) { + if ('getViewManagerConfig' in UIManager) { return UIManager.getViewManagerConfig(name); } return UIManager[name]; -} \ No newline at end of file +}