chore: lint project (#3279)

* chore: update eslint config
* chore: lint lib files
This commit is contained in:
Krzysztof Moch 2023-10-07 12:56:35 +02:00 committed by GitHub
parent e6e8f621fe
commit 067adde124
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 521 additions and 354 deletions

View File

@ -1 +1,2 @@
examples/
lib/

View File

@ -1,8 +1,11 @@
{
"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

View File

@ -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<VideoRef, ReactVideoProps>(
@ -84,7 +90,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
onVideoTracks,
...rest
},
ref
ref,
) => {
const nativeRef = useRef<ComponentRef<VideoComponentType>>(null);
const [showPoster, setShowPoster] = useState(!!poster);
@ -98,25 +104,31 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
() => ({
...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<VideoRef, ReactVideoProps>(
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<VideoRef, ReactVideoProps>(
}, [resizeMode]);
const _drm = useMemo(() => {
if (!drm) return;
if (!drm) {
return;
}
return {
drmType: drm.type,
licenseServer: drm.licenseServer,
@ -160,59 +174,64 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
};
}, [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<VideoRef, ReactVideoProps>(
(restored: boolean) => {
setRestoreUserInterfaceForPIPStopCompletionHandler(restored);
},
[setRestoreUserInterfaceForPIPStopCompletionHandler]
[setRestoreUserInterfaceForPIPStopCompletionHandler],
);
const onVideoLoadStart = useCallback(
(e: NativeSyntheticEvent<OnLoadStartData>) => {
onLoadStart?.(e.nativeEvent);
},
[onLoadStart]
[onLoadStart],
);
const onVideoLoad = useCallback(
(e: NativeSyntheticEvent<OnLoadData>) => {
if (Platform.OS === "windows") setShowPoster(false);
if (Platform.OS === 'windows') {
setShowPoster(false);
}
onLoad?.(e.nativeEvent);
},
[onLoad, setShowPoster]
[onLoad, setShowPoster],
);
const onVideoError = useCallback(
(e: NativeSyntheticEvent<OnVideoErrorData>) => {
onError?.(e.nativeEvent);
},
[onError]
[onError],
);
const onVideoProgress = useCallback(
(e: NativeSyntheticEvent<OnProgressData>) => {
onProgress?.(e.nativeEvent);
},
[onProgress]
[onProgress],
);
const onVideoSeek = useCallback(
(e: NativeSyntheticEvent<OnSeekData>) => {
onSeek?.(e.nativeEvent);
},
[onSeek]
[onSeek],
);
// android only
const onVideoPlaybackStateChanged = useCallback((e: NativeSyntheticEvent<OnPlaybackStateChangedData>) => {
onPlaybackStateChanged?.(e.nativeEvent);
}, [onPlaybackStateChanged])
const onVideoPlaybackStateChanged = useCallback(
(e: NativeSyntheticEvent<OnPlaybackStateChangedData>) => {
onPlaybackStateChanged?.(e.nativeEvent);
},
[onPlaybackStateChanged],
);
// android only
const onVideoIdle = useCallback(() => {
onIdle?.()
}, [onIdle])
onIdle?.();
}, [onIdle]);
const _onTimedMetadata = useCallback(
(e: NativeSyntheticEvent<OnTimedMetadataData>) => {
onTimedMetadata?.(e.nativeEvent);
},
[onTimedMetadata]
[onTimedMetadata],
);
const _onAudioTracks = useCallback((e: NativeSyntheticEvent<OnAudioTracksData>) => {
onAudioTracks?.(e.nativeEvent)
}, [onAudioTracks])
const _onAudioTracks = useCallback(
(e: NativeSyntheticEvent<OnAudioTracksData>) => {
onAudioTracks?.(e.nativeEvent);
},
[onAudioTracks],
);
const _onTextTracks = useCallback((e: NativeSyntheticEvent<OnTextTracksData>) => {
onTextTracks?.(e.nativeEvent)
}, [onTextTracks])
const _onTextTracks = useCallback(
(e: NativeSyntheticEvent<OnTextTracksData>) => {
onTextTracks?.(e.nativeEvent);
},
[onTextTracks],
);
const _onVideoTracks = useCallback((e: NativeSyntheticEvent<OnVideoTracksData>) => {
onVideoTracks?.(e.nativeEvent)
}, [onVideoTracks])
const _onVideoTracks = useCallback(
(e: NativeSyntheticEvent<OnVideoTracksData>) => {
onVideoTracks?.(e.nativeEvent);
},
[onVideoTracks],
);
const _onPlaybackRateChange = useCallback(
(e: NativeSyntheticEvent<Readonly<{ playbackRate: number }>>) => {
(e: NativeSyntheticEvent<Readonly<{playbackRate: number}>>) => {
onPlaybackRateChange?.(e.nativeEvent);
},
[onPlaybackRateChange]
[onPlaybackRateChange],
);
const _onReadyForDisplay = useCallback(() => {
@ -322,51 +355,91 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
(e: NativeSyntheticEvent<OnPictureInPictureStatusChangedData>) => {
onPictureInPictureStatusChanged?.(e.nativeEvent);
},
[onPictureInPictureStatusChanged]
[onPictureInPictureStatusChanged],
);
const _onAudioFocusChanged = useCallback((e: NativeSyntheticEvent<OnAudioFocusChangedData>) => {
onAudioFocusChanged?.(e.nativeEvent)
}, [onAudioFocusChanged])
const _onAudioFocusChanged = useCallback(
(e: NativeSyntheticEvent<OnAudioFocusChangedData>) => {
onAudioFocusChanged?.(e.nativeEvent);
},
[onAudioFocusChanged],
);
const onVideoBuffer = useCallback((e: NativeSyntheticEvent<OnBufferData>) => {
onBuffer?.(e.nativeEvent);
}, [onBuffer]);
const onVideoBuffer = useCallback(
(e: NativeSyntheticEvent<OnBufferData>) => {
onBuffer?.(e.nativeEvent);
},
[onBuffer],
);
const onVideoExternalPlaybackChange = useCallback((e: NativeSyntheticEvent<OnExternalPlaybackChangeData>) => {
onExternalPlaybackChange?.(e.nativeEvent);
}, [onExternalPlaybackChange])
const onVideoExternalPlaybackChange = useCallback(
(e: NativeSyntheticEvent<OnExternalPlaybackChangeData>) => {
onExternalPlaybackChange?.(e.nativeEvent);
},
[onExternalPlaybackChange],
);
const _onBandwidthUpdate = useCallback((e: NativeSyntheticEvent<OnBandwidthUpdateData>) => {
onBandwidthUpdate?.(e.nativeEvent);
}, [onBandwidthUpdate]);
const _onBandwidthUpdate = useCallback(
(e: NativeSyntheticEvent<OnBandwidthUpdateData>) => {
onBandwidthUpdate?.(e.nativeEvent);
},
[onBandwidthUpdate],
);
const _onReceiveAdEvent = useCallback((e: NativeSyntheticEvent<OnReceiveAdEventData>) => {
onReceiveAdEvent?.(e.nativeEvent);
}, [onReceiveAdEvent]);
const _onReceiveAdEvent = useCallback(
(e: NativeSyntheticEvent<OnReceiveAdEventData>) => {
onReceiveAdEvent?.(e.nativeEvent);
},
[onReceiveAdEvent],
);
const onGetLicense = useCallback(
(event: NativeSyntheticEvent<OnGetLicenseData>) => {
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<VideoRef, ReactVideoProps>(
pause,
resume,
restoreUserInterfaceForPictureInPictureStopCompleted,
]
],
);
return (
@ -438,12 +511,12 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
onReceiveAdEvent={_onReceiveAdEvent}
/>
{showPoster ? (
<Image style={posterStyle} source={{ uri: poster }} />
<Image style={posterStyle} source={{uri: poster}} />
) : null}
</View>
);
}
},
);
Video.displayName = "Video";
Video.displayName = 'Video';
export default Video;

View File

@ -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<string, any>
type Headers = Record<string, string>;
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<Readonly<{
title: string;
language: string;
type: string;
uri: string;
}>>
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<Readonly<{
value?: string
identifier: string
}>>
}>
metadata: ReadonlyArray<
Readonly<{
value?: string;
identifier: string;
}>
>;
}>;
export type OnAudioTracksData = Readonly<{
audioTracks: ReadonlyArray<Readonly<{
index: number
title?: string
language?: string
bitrate?: number
type?: string
selected?: boolean
}>>
}>
audioTracks: ReadonlyArray<
Readonly<{
index: number;
title?: string;
language?: string;
bitrate?: number;
type?: string;
selected?: boolean;
}>
>;
}>;
export type OnTextTracksData = Readonly<{
textTracks: ReadonlyArray<Readonly<{
index: number
title?: string
language?: string
/**
* iOS only supports VTT, Android supports all 3
*/
type?: 'srt' | 'ttml' | 'vtt'
selected?: boolean
}>>
}>
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<Readonly<{
trackId: number
codecs?: string
width?: number
height?: number
bitrate?: number
selected?: boolean
}>>
}>
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<OnLoadData>) => void;
onVideoLoadStart?: (event: NativeSyntheticEvent<OnLoadStartData>) => void;
onVideoBuffer?: (event: NativeSyntheticEvent<OnBufferData>) => void;
onVideoError?: (event: NativeSyntheticEvent<OnVideoErrorData>) => void;
onVideoProgress?: (event: NativeSyntheticEvent<OnProgressData>) => void;
onBandwidthUpdate?: (event: NativeSyntheticEvent<OnBandwidthUpdateData>) => void;
onBandwidthUpdate?: (
event: NativeSyntheticEvent<OnBandwidthUpdateData>,
) => void;
onVideoSeek?: (event: NativeSyntheticEvent<OnSeekData>) => void;
onVideoEnd?: (event: NativeSyntheticEvent<Readonly<{}>>) => void; // all
onVideoAudioBecomingNoisy?: (event: NativeSyntheticEvent<Readonly<{}>>) => void;
onVideoFullscreenPlayerWillPresent?: (event: NativeSyntheticEvent<Readonly<{}>>) => void; // ios, android
onVideoFullscreenPlayerDidPresent?: (event: NativeSyntheticEvent<Readonly<{}>>) => void; // ios, android
onVideoFullscreenPlayerWillDismiss?: (event: NativeSyntheticEvent<Readonly<{}>>) => void; // ios, android
onVideoFullscreenPlayerDidDismiss?: (event: NativeSyntheticEvent<Readonly<{}>>) => void; // ios, android
onReadyForDisplay?: (event: NativeSyntheticEvent<Readonly<{}>>) => void;
onVideoEnd?: (event: NativeSyntheticEvent<Readonly<object>>) => void; // all
onVideoAudioBecomingNoisy?: (
event: NativeSyntheticEvent<Readonly<object>>,
) => void;
onVideoFullscreenPlayerWillPresent?: (
event: NativeSyntheticEvent<Readonly<object>>,
) => void; // ios, android
onVideoFullscreenPlayerDidPresent?: (
event: NativeSyntheticEvent<Readonly<object>>,
) => void; // ios, android
onVideoFullscreenPlayerWillDismiss?: (
event: NativeSyntheticEvent<Readonly<object>>,
) => void; // ios, android
onVideoFullscreenPlayerDidDismiss?: (
event: NativeSyntheticEvent<Readonly<object>>,
) => void; // ios, android
onReadyForDisplay?: (event: NativeSyntheticEvent<Readonly<object>>) => void;
onPlaybackRateChange?: (event: NativeSyntheticEvent<OnPlaybackData>) => void; // all
onVideoExternalPlaybackChange?: (event: NativeSyntheticEvent<OnExternalPlaybackChangeData>) => void;
onVideoExternalPlaybackChange?: (
event: NativeSyntheticEvent<OnExternalPlaybackChangeData>,
) => void;
onGetLicense?: (event: NativeSyntheticEvent<OnGetLicenseData>) => void;
onPictureInPictureStatusChanged?: (event: NativeSyntheticEvent<OnPictureInPictureStatusChangedData>) => void;
onRestoreUserInterfaceForPictureInPictureStop?: (event: NativeSyntheticEvent<Readonly<{}>>) => void;
onReceiveAdEvent?: (event: NativeSyntheticEvent<OnReceiveAdEventData>) => void;
onVideoPlaybackStateChanged?: (event: NativeSyntheticEvent<OnPlaybackStateChangedData>) => void; // android only
onVideoIdle?: (event: NativeSyntheticEvent<{}>) => void; // android only (nowhere in document, so do not use as props. just type declaration)
onAudioFocusChanged?: (event: NativeSyntheticEvent<OnAudioFocusChangedData>) => void; // android only (nowhere in document, so do not use as props. just type declaration)
onPictureInPictureStatusChanged?: (
event: NativeSyntheticEvent<OnPictureInPictureStatusChangedData>,
) => void;
onRestoreUserInterfaceForPictureInPictureStop?: (
event: NativeSyntheticEvent<Readonly<object>>,
) => void;
onReceiveAdEvent?: (
event: NativeSyntheticEvent<OnReceiveAdEventData>,
) => void;
onVideoPlaybackStateChanged?: (
event: NativeSyntheticEvent<OnPlaybackStateChangedData>,
) => void; // android only
onVideoIdle?: (event: NativeSyntheticEvent<object>) => void; // android only (nowhere in document, so do not use as props. just type declaration)
onAudioFocusChanged?: (
event: NativeSyntheticEvent<OnAudioFocusChangedData>,
) => void; // android only (nowhere in document, so do not use as props. just type declaration)
onTimedMetadata?: (event: NativeSyntheticEvent<OnTimedMetadataData>) => void; // ios, android
onAudioTracks?: (event: NativeSyntheticEvent<OnAudioTracksData>) => void; // android
onTextTracks?: (event: NativeSyntheticEvent<OnTextTracksData>) => void; // android
@ -296,28 +332,45 @@ export type VideoComponentType = HostComponent<VideoNativeProps>;
export interface VideoManagerType {
save: (reactTag: number) => Promise<void>;
setPlayerPauseState: (paused: boolean, reactTag: number) => Promise<void>;
setLicenseResult: (result: string, licenseUrl: string, reactTag: number) => Promise<void>;
setLicenseResultError: (error: string, licenseUrl: string, reactTag: number) => Promise<void>;
setLicenseResult: (
result: string,
licenseUrl: string,
reactTag: number,
) => Promise<void>;
setLicenseResultError: (
error: string,
licenseUrl: string,
reactTag: number,
) => Promise<void>;
}
export interface VideoDecoderPropertiesType {
getWidevineLevel: () => Promise<number>;
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<VideoNativeProps>('RCTVideo') as VideoComponentType;
export default requireNativeComponent<VideoNativeProps>(
'RCTVideo',
) as VideoComponentType;

View File

@ -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;

View File

@ -1,6 +1,6 @@
export default {
WIDEVINE: 'widevine',
PLAYREADY: 'playready',
CLEARKEY: 'clearkey',
FAIRPLAY: 'fairplay',
WIDEVINE: 'widevine',
PLAYREADY: 'playready',
CLEARKEY: 'clearkey',
FAIRPLAY: 'fairplay',
} as const;

View File

@ -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;

View File

@ -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
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
}

View File

@ -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<string, string>;
@ -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;
}

View File

@ -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,22 +14,30 @@ export function resolveAssetSourceForVideo(source: Source): ReactVideoSource {
return source as ReactVideoSource;
}
export function getReactTag(ref: RefObject<Component<any, any, any> | ComponentClass<any, any> | null>): number {
export function getReactTag(
ref: RefObject<
| Component<unknown, unknown, unknown>
| ComponentClass<unknown, unknown>
| 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);
}