feat(tvos): add custom image metadata option for tvos and add missing types for custom metadata properties (#3280)

* fix: add typescript types for custom metadata properties
* chore: add possibility to override image metadata of video playback

---------

Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>
This commit is contained in:
Konstantin 2023-10-07 15:14:10 +02:00 committed by GitHub
parent 067adde124
commit a855284d8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 48 additions and 6 deletions

5
API.md
View File

@ -981,7 +981,7 @@ Platforms: iOS, Android
##### Overriding the metadata of a source ##### Overriding the metadata of a source
Provide an optional `title`, `subtitle` and/or `description` properties for the video. Provide an optional `title`, `subtitle`, `customImageUri` and/or `description` properties for the video.
Useful when to adapt the tvOS playback experience. Useful when to adapt the tvOS playback experience.
Example: Example:
@ -991,7 +991,8 @@ source={{
uri: 'https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8', uri: 'https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8',
title: 'Custom Title', title: 'Custom Title',
subtitle: 'Custom Subtitle', subtitle: 'Custom Subtitle',
description: 'Custom Description' description: 'Custom Description',
customImageUri: 'https://pbs.twimg.com/profile_images/1498641868397191170/6qW2XkuI_400x400.png'
}} }}
``` ```

View File

@ -16,6 +16,8 @@ export default function App() {
title: 'Custom Title', title: 'Custom Title',
subtitle: 'Custom Subtitle', subtitle: 'Custom Subtitle',
description: 'Custom Description', description: 'Custom Description',
customImageUri:
'https://pbs.twimg.com/profile_images/1498641868397191170/6qW2XkuI_400x400.png',
}} }}
style={[styles.fullScreen, StyleSheet.absoluteFillObject]} style={[styles.fullScreen, StyleSheet.absoluteFillObject]}
controls controls

View File

@ -319,10 +319,10 @@ PODS:
- React-jsinspector (0.71.12-0) - React-jsinspector (0.71.12-0)
- React-logger (0.71.12-0): - React-logger (0.71.12-0):
- glog - glog
- react-native-video (6.0.0-alpha.7): - react-native-video (6.0.0-alpha.8):
- React-Core - React-Core
- react-native-video/Video (= 6.0.0-alpha.7) - react-native-video/Video (= 6.0.0-alpha.8)
- react-native-video/Video (6.0.0-alpha.7): - react-native-video/Video (6.0.0-alpha.8):
- PromisesSwift - PromisesSwift
- React-Core - React-Core
- React-perflogger (0.71.12-0) - React-perflogger (0.71.12-0)
@ -598,7 +598,7 @@ SPEC CHECKSUMS:
React-jsiexecutor: 0c8c5e8b2171be52295f59097923babf84d1cf66 React-jsiexecutor: 0c8c5e8b2171be52295f59097923babf84d1cf66
React-jsinspector: f8e6919523047a9bd1270ade75b4eca0108963b4 React-jsinspector: f8e6919523047a9bd1270ade75b4eca0108963b4
React-logger: 16c56636d4209cc204d06c5ba347cee21b960012 React-logger: 16c56636d4209cc204d06c5ba347cee21b960012
react-native-video: 4c8d6b0f82b32a3f02a9fde0287704f455b7211d react-native-video: 86950ad481cec184d7c9420ec3bca0c27904bbcd
React-perflogger: 355109dc9d6f34e35bc35dabb32310f8ed2d29a2 React-perflogger: 355109dc9d6f34e35bc35dabb32310f8ed2d29a2
React-RCTActionSheet: 9d1be4d43972f2aae4b31d9e53ffb030115fa445 React-RCTActionSheet: 9d1be4d43972f2aae4b31d9e53ffb030115fa445
React-RCTAnimation: aab7e1ecd325db67e1f2a947d85a52adf86594b7 React-RCTAnimation: aab7e1ecd325db67e1f2a947d85a52adf86594b7

View File

@ -12,6 +12,7 @@ struct VideoSource {
let title: String? let title: String?
let subtitle: String? let subtitle: String?
let description: String? let description: String?
let customImageUri: String?
let json: NSDictionary? let json: NSDictionary?
@ -29,6 +30,7 @@ struct VideoSource {
self.title = nil self.title = nil
self.subtitle = nil self.subtitle = nil
self.description = nil self.description = nil
self.customImageUri = nil
return return
} }
self.json = json self.json = json
@ -43,5 +45,6 @@ struct VideoSource {
self.title = json["title"] as? String self.title = json["title"] as? String
self.subtitle = json["subtitle"] as? String self.subtitle = json["subtitle"] as? String
self.description = json["description"] as? String self.description = json["description"] as? String
self.customImageUri = json["customImageUri"] as? String
} }
} }

View File

@ -330,4 +330,15 @@ enum RCTVideoUtils {
item.extendedLanguageTag = "und" item.extendedLanguageTag = "und"
return item.copy() as! AVMetadataItem return item.copy() as! AVMetadataItem
} }
static func createImageMetadataItem(imageUri: String) -> Data? {
if let uri = URL(string: imageUri),
let imgData = try? Data(contentsOf: uri),
let image = UIImage(data: imgData),
let pngData = image.pngData() {
return pngData
}
return nil
}
} }

View File

@ -408,6 +408,11 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
mapping[.commonIdentifierDescription] = description mapping[.commonIdentifierDescription] = description
} }
if let customImageUri = _source?.customImageUri,
let imageData = RCTVideoUtils.createImageMetadataItem(imageUri: customImageUri) {
mapping[.commonIdentifierArtwork] = imageData
}
if #available(iOS 12.2, *), !mapping.isEmpty { if #available(iOS 12.2, *), !mapping.isEmpty {
playerItem.externalMetadata = RCTVideoUtils.createMetadataItems(for: mapping) playerItem.externalMetadata = RCTVideoUtils.createMetadataItems(for: mapping)
} }

View File

@ -143,6 +143,10 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
requestHeaders: resolvedSource?.headers || {}, requestHeaders: resolvedSource?.headers || {},
startTime: resolvedSource.startTime || 0, startTime: resolvedSource.startTime || 0,
endTime: resolvedSource.endTime, endTime: resolvedSource.endTime,
title: resolvedSource.title,
subtitle: resolvedSource.subtitle,
description: resolvedSource.description,
customImageUri: resolvedSource.customImageUri,
}; };
}, [source]); }, [source]);

View File

@ -22,6 +22,10 @@ type VideoSrc = Readonly<{
requestHeaders?: Headers; requestHeaders?: Headers;
startTime?: number; startTime?: number;
endTime?: number; endTime?: number;
title?: string;
subtitle?: string;
description?: string;
customImageUri?: string;
}>; }>;
export type Filter = export type Filter =

View File

@ -33,6 +33,10 @@ export type ReactVideoSource = Readonly<{
headers?: Headers; headers?: Headers;
startTime?: number; startTime?: number;
endTime?: number; endTime?: number;
title?: string;
subtitle?: string;
description?: string;
customImageUri?: string;
}>; }>;
export type ReactVideoDrm = Readonly<{ export type ReactVideoDrm = Readonly<{
@ -86,6 +90,13 @@ type TextTracks = {
uri: string; uri: string;
}[]; }[];
type Chapters = {
title: string;
startTime: number;
endTime: number;
uri?: string;
};
export interface ReactVideoProps extends ReactVideoEvents { export interface ReactVideoProps extends ReactVideoEvents {
source?: ReactVideoSource; source?: ReactVideoSource;
drm?: ReactVideoDrm; drm?: ReactVideoDrm;
@ -95,6 +106,7 @@ export interface ReactVideoProps extends ReactVideoEvents {
automaticallyWaitsToMinimizeStalling?: boolean; // iOS automaticallyWaitsToMinimizeStalling?: boolean; // iOS
backBufferDurationMs?: number; // Android backBufferDurationMs?: number; // Android
bufferConfig?: BufferConfig; // Android bufferConfig?: BufferConfig; // Android
chapters?: Chapters[]; // iOS
contentStartTime?: number; // Android contentStartTime?: number; // Android
controls?: boolean; controls?: boolean;
currentPlaybackTime?: number; // Android currentPlaybackTime?: number; // Android