From a855284d8d9465c45950fc895224a2ae8673a0e3 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Sat, 7 Oct 2023 15:14:10 +0200 Subject: [PATCH] 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> --- API.md | 5 +++-- examples/exampletvOS/App.tsx | 2 ++ examples/exampletvOS/ios/Podfile.lock | 8 ++++---- ios/Video/DataStructures/VideoSource.swift | 3 +++ ios/Video/Features/RCTVideoUtils.swift | 11 +++++++++++ ios/Video/RCTVideo.swift | 5 +++++ src/Video.tsx | 4 ++++ src/VideoNativeComponent.ts | 4 ++++ src/types/video.ts | 12 ++++++++++++ 9 files changed, 48 insertions(+), 6 deletions(-) diff --git a/API.md b/API.md index 67759f52..b5e0b4df 100644 --- a/API.md +++ b/API.md @@ -981,7 +981,7 @@ Platforms: iOS, Android ##### 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. Example: @@ -991,7 +991,8 @@ source={{ uri: 'https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8', title: 'Custom Title', subtitle: 'Custom Subtitle', - description: 'Custom Description' + description: 'Custom Description', + customImageUri: 'https://pbs.twimg.com/profile_images/1498641868397191170/6qW2XkuI_400x400.png' }} ``` diff --git a/examples/exampletvOS/App.tsx b/examples/exampletvOS/App.tsx index b257187b..9a6d630d 100644 --- a/examples/exampletvOS/App.tsx +++ b/examples/exampletvOS/App.tsx @@ -16,6 +16,8 @@ export default function App() { title: 'Custom Title', subtitle: 'Custom Subtitle', description: 'Custom Description', + customImageUri: + 'https://pbs.twimg.com/profile_images/1498641868397191170/6qW2XkuI_400x400.png', }} style={[styles.fullScreen, StyleSheet.absoluteFillObject]} controls diff --git a/examples/exampletvOS/ios/Podfile.lock b/examples/exampletvOS/ios/Podfile.lock index 4051f0e5..42cd04f2 100644 --- a/examples/exampletvOS/ios/Podfile.lock +++ b/examples/exampletvOS/ios/Podfile.lock @@ -319,10 +319,10 @@ PODS: - React-jsinspector (0.71.12-0) - React-logger (0.71.12-0): - glog - - react-native-video (6.0.0-alpha.7): + - react-native-video (6.0.0-alpha.8): - React-Core - - react-native-video/Video (= 6.0.0-alpha.7) - - 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.8): - PromisesSwift - React-Core - React-perflogger (0.71.12-0) @@ -598,7 +598,7 @@ SPEC CHECKSUMS: React-jsiexecutor: 0c8c5e8b2171be52295f59097923babf84d1cf66 React-jsinspector: f8e6919523047a9bd1270ade75b4eca0108963b4 React-logger: 16c56636d4209cc204d06c5ba347cee21b960012 - react-native-video: 4c8d6b0f82b32a3f02a9fde0287704f455b7211d + react-native-video: 86950ad481cec184d7c9420ec3bca0c27904bbcd React-perflogger: 355109dc9d6f34e35bc35dabb32310f8ed2d29a2 React-RCTActionSheet: 9d1be4d43972f2aae4b31d9e53ffb030115fa445 React-RCTAnimation: aab7e1ecd325db67e1f2a947d85a52adf86594b7 diff --git a/ios/Video/DataStructures/VideoSource.swift b/ios/Video/DataStructures/VideoSource.swift index d3f16dbf..59dca89f 100644 --- a/ios/Video/DataStructures/VideoSource.swift +++ b/ios/Video/DataStructures/VideoSource.swift @@ -12,6 +12,7 @@ struct VideoSource { let title: String? let subtitle: String? let description: String? + let customImageUri: String? let json: NSDictionary? @@ -29,6 +30,7 @@ struct VideoSource { self.title = nil self.subtitle = nil self.description = nil + self.customImageUri = nil return } self.json = json @@ -43,5 +45,6 @@ struct VideoSource { self.title = json["title"] as? String self.subtitle = json["subtitle"] as? String self.description = json["description"] as? String + self.customImageUri = json["customImageUri"] as? String } } diff --git a/ios/Video/Features/RCTVideoUtils.swift b/ios/Video/Features/RCTVideoUtils.swift index ce87afe1..44526177 100644 --- a/ios/Video/Features/RCTVideoUtils.swift +++ b/ios/Video/Features/RCTVideoUtils.swift @@ -330,4 +330,15 @@ enum RCTVideoUtils { item.extendedLanguageTag = "und" 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 + } } diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index 927b7064..5bf977d2 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -408,6 +408,11 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH mapping[.commonIdentifierDescription] = description } + if let customImageUri = _source?.customImageUri, + let imageData = RCTVideoUtils.createImageMetadataItem(imageUri: customImageUri) { + mapping[.commonIdentifierArtwork] = imageData + } + if #available(iOS 12.2, *), !mapping.isEmpty { playerItem.externalMetadata = RCTVideoUtils.createMetadataItems(for: mapping) } diff --git a/src/Video.tsx b/src/Video.tsx index 9a839d24..7f83d7ad 100644 --- a/src/Video.tsx +++ b/src/Video.tsx @@ -143,6 +143,10 @@ const Video = forwardRef( requestHeaders: resolvedSource?.headers || {}, startTime: resolvedSource.startTime || 0, endTime: resolvedSource.endTime, + title: resolvedSource.title, + subtitle: resolvedSource.subtitle, + description: resolvedSource.description, + customImageUri: resolvedSource.customImageUri, }; }, [source]); diff --git a/src/VideoNativeComponent.ts b/src/VideoNativeComponent.ts index e79ff99e..5aae026b 100644 --- a/src/VideoNativeComponent.ts +++ b/src/VideoNativeComponent.ts @@ -22,6 +22,10 @@ type VideoSrc = Readonly<{ requestHeaders?: Headers; startTime?: number; endTime?: number; + title?: string; + subtitle?: string; + description?: string; + customImageUri?: string; }>; export type Filter = diff --git a/src/types/video.ts b/src/types/video.ts index 4fb414ac..b3d479f0 100644 --- a/src/types/video.ts +++ b/src/types/video.ts @@ -33,6 +33,10 @@ export type ReactVideoSource = Readonly<{ headers?: Headers; startTime?: number; endTime?: number; + title?: string; + subtitle?: string; + description?: string; + customImageUri?: string; }>; export type ReactVideoDrm = Readonly<{ @@ -86,6 +90,13 @@ type TextTracks = { uri: string; }[]; +type Chapters = { + title: string; + startTime: number; + endTime: number; + uri?: string; +}; + export interface ReactVideoProps extends ReactVideoEvents { source?: ReactVideoSource; drm?: ReactVideoDrm; @@ -95,6 +106,7 @@ export interface ReactVideoProps extends ReactVideoEvents { automaticallyWaitsToMinimizeStalling?: boolean; // iOS backBufferDurationMs?: number; // Android bufferConfig?: BufferConfig; // Android + chapters?: Chapters[]; // iOS contentStartTime?: number; // Android controls?: boolean; currentPlaybackTime?: number; // Android