expo-video 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/android/build.gradle +2 -2
- package/expo-module.config.json +1 -1
- package/ios/ExpoVideo.podspec +1 -1
- package/ios/NowPlayingManager.swift +1 -1
- package/ios/VideoModule.swift +15 -2
- package/ios/VideoPlayer.swift +7 -3
- package/ios/VideoView.swift +57 -3
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,18 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 1.2.2 — 2024-07-03
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug fixes
|
|
16
|
+
|
|
17
|
+
- [iOS] Fix crashes on iOS 16 and lower when source HTTP headers are undefined. ([#30104](https://github.com/expo/expo/pull/30104) by [@behenate](https://github.com/behenate))
|
|
18
|
+
|
|
19
|
+
## 1.2.1 — 2024-06-27
|
|
20
|
+
|
|
21
|
+
### 🎉 New features
|
|
22
|
+
|
|
23
|
+
- [iOS] Support Apple TV. ([#29560](https://github.com/expo/expo/pull/29560) by [@douglowder](https://github.com/douglowder))
|
|
24
|
+
|
|
13
25
|
## 1.2.0 — 2024-06-20
|
|
14
26
|
|
|
15
27
|
### 🎉 New features
|
package/android/build.gradle
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
apply plugin: 'com.android.library'
|
|
2
2
|
|
|
3
3
|
group = 'host.exp.exponent'
|
|
4
|
-
version = '1.2.
|
|
4
|
+
version = '1.2.2'
|
|
5
5
|
|
|
6
6
|
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
7
7
|
apply from: expoModulesCorePlugin
|
|
@@ -14,7 +14,7 @@ android {
|
|
|
14
14
|
namespace "expo.modules.video"
|
|
15
15
|
defaultConfig {
|
|
16
16
|
versionCode 1
|
|
17
|
-
versionName '1.2.
|
|
17
|
+
versionName '1.2.2'
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
package/expo-module.config.json
CHANGED
package/ios/ExpoVideo.podspec
CHANGED
|
@@ -10,7 +10,7 @@ Pod::Spec.new do |s|
|
|
|
10
10
|
s.license = package['license']
|
|
11
11
|
s.author = package['author']
|
|
12
12
|
s.homepage = package['homepage']
|
|
13
|
-
s.
|
|
13
|
+
s.platforms = { :ios => '13.4', :tvos => '13.4' }
|
|
14
14
|
s.swift_version = '5.4'
|
|
15
15
|
s.source = { git: 'https://github.com/expo/expo.git' }
|
|
16
16
|
s.static_framework = true
|
|
@@ -177,7 +177,7 @@ class NowPlayingManager: VideoPlayerObserverDelegate {
|
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
private func loadMetadata(for mediaItem: AVPlayerItem) async throws -> [AVMetadataItem] {
|
|
180
|
-
if #available(iOS 15.0, *) {
|
|
180
|
+
if #available(iOS 15.0, tvOS 15.0, *) {
|
|
181
181
|
return try await mediaItem.asset.loadMetadata(for: .iTunesMetadata)
|
|
182
182
|
}
|
|
183
183
|
|
package/ios/VideoModule.swift
CHANGED
|
@@ -6,8 +6,11 @@ public final class VideoModule: Module {
|
|
|
6
6
|
public func definition() -> ModuleDefinition {
|
|
7
7
|
Name("ExpoVideo")
|
|
8
8
|
|
|
9
|
-
Function("isPictureInPictureSupported") {
|
|
10
|
-
|
|
9
|
+
Function("isPictureInPictureSupported") { () -> Bool in
|
|
10
|
+
if #available(iOS 13.4, tvOS 14.0, *) {
|
|
11
|
+
return AVPictureInPictureController.isPictureInPictureSupported()
|
|
12
|
+
}
|
|
13
|
+
return false
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
View(VideoView.self) {
|
|
@@ -22,6 +25,10 @@ public final class VideoModule: Module {
|
|
|
22
25
|
|
|
23
26
|
Prop("nativeControls") { (view, nativeControls: Bool?) in
|
|
24
27
|
view.playerViewController.showsPlaybackControls = nativeControls ?? true
|
|
28
|
+
#if os(tvOS)
|
|
29
|
+
view.playerViewController.isSkipForwardEnabled = nativeControls ?? true
|
|
30
|
+
view.playerViewController.isSkipBackwardEnabled = nativeControls ?? true
|
|
31
|
+
#endif
|
|
25
32
|
}
|
|
26
33
|
|
|
27
34
|
Prop("contentFit") { (view, contentFit: VideoContentFit?) in
|
|
@@ -40,11 +47,15 @@ public final class VideoModule: Module {
|
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
Prop("allowsFullscreen") { (view, allowsFullscreen: Bool?) in
|
|
50
|
+
#if !os(tvOS)
|
|
43
51
|
view.playerViewController.setValue(allowsFullscreen ?? true, forKey: "allowsEnteringFullScreen")
|
|
52
|
+
#endif
|
|
44
53
|
}
|
|
45
54
|
|
|
46
55
|
Prop("showsTimecodes") { (view, showsTimecodes: Bool?) in
|
|
56
|
+
#if !os(tvOS)
|
|
47
57
|
view.playerViewController.showsTimecodes = showsTimecodes ?? true
|
|
58
|
+
#endif
|
|
48
59
|
}
|
|
49
60
|
|
|
50
61
|
Prop("requiresLinearPlayback") { (view, requiresLinearPlayback: Bool?) in
|
|
@@ -56,7 +67,9 @@ public final class VideoModule: Module {
|
|
|
56
67
|
}
|
|
57
68
|
|
|
58
69
|
Prop("startsPictureInPictureAutomatically") { (view, startsPictureInPictureAutomatically: Bool?) in
|
|
70
|
+
#if !os(tvOS)
|
|
59
71
|
view.startPictureInPictureAutomatically = startsPictureInPictureAutomatically ?? false
|
|
72
|
+
#endif
|
|
60
73
|
}
|
|
61
74
|
|
|
62
75
|
AsyncFunction("enterFullscreen") { view in
|
package/ios/VideoPlayer.swift
CHANGED
|
@@ -16,7 +16,7 @@ internal final class VideoPlayer: SharedRef<AVPlayer>, Hashable, VideoPlayerObse
|
|
|
16
16
|
if oldValue != playbackRate {
|
|
17
17
|
safeEmit(event: "playbackRateChange", arguments: playbackRate, oldValue)
|
|
18
18
|
}
|
|
19
|
-
if #available(iOS 16.0, *) {
|
|
19
|
+
if #available(iOS 16.0, tvOS 16.0, *) {
|
|
20
20
|
pointer.defaultRate = playbackRate
|
|
21
21
|
}
|
|
22
22
|
pointer.rate = playbackRate
|
|
@@ -98,7 +98,11 @@ internal final class VideoPlayer: SharedRef<AVPlayer>, Hashable, VideoPlayerObse
|
|
|
98
98
|
return
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
let asset =
|
|
101
|
+
let asset = if let headers = videoSource.headers {
|
|
102
|
+
AVURLAsset(url: url, options: ["AVURLAssetHTTPHeaderFieldsKey": headers])
|
|
103
|
+
} else {
|
|
104
|
+
AVURLAsset(url: url)
|
|
105
|
+
}
|
|
102
106
|
let playerItem = VideoPlayerItem(asset: asset, videoSource: videoSource)
|
|
103
107
|
|
|
104
108
|
if let drm = videoSource.drm {
|
|
@@ -143,7 +147,7 @@ internal final class VideoPlayer: SharedRef<AVPlayer>, Hashable, VideoPlayerObse
|
|
|
143
147
|
}
|
|
144
148
|
|
|
145
149
|
func onRateChanged(player: AVPlayer, oldRate: Float?, newRate: Float) {
|
|
146
|
-
if #available(iOS 16.0, *) {
|
|
150
|
+
if #available(iOS 16.0, tvOS 16.0, *) {
|
|
147
151
|
if player.defaultRate != playbackRate {
|
|
148
152
|
// User changed the playback speed in the native controls. Update the desiredRate variable
|
|
149
153
|
playbackRate = player.defaultRate
|
package/ios/VideoView.swift
CHANGED
|
@@ -12,8 +12,14 @@ public final class VideoView: ExpoView, AVPlayerViewControllerDelegate {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
#if os(tvOS)
|
|
16
|
+
var wasPlaying: Bool = false
|
|
17
|
+
#endif
|
|
15
18
|
var isFullscreen: Bool = false
|
|
16
19
|
var isInPictureInPicture = false
|
|
20
|
+
#if os(tvOS)
|
|
21
|
+
let startPictureInPictureAutomatically = false
|
|
22
|
+
#else
|
|
17
23
|
var startPictureInPictureAutomatically = false {
|
|
18
24
|
didSet {
|
|
19
25
|
if #available(iOS 14.2, *) {
|
|
@@ -21,12 +27,15 @@ public final class VideoView: ExpoView, AVPlayerViewControllerDelegate {
|
|
|
21
27
|
}
|
|
22
28
|
}
|
|
23
29
|
}
|
|
30
|
+
#endif
|
|
24
31
|
|
|
25
32
|
var allowPictureInPicture: Bool = false {
|
|
26
33
|
didSet {
|
|
27
34
|
// PiP requires `.playback` audio session category in `.moviePlayback` mode
|
|
28
|
-
|
|
29
|
-
|
|
35
|
+
if #available(iOS 13.4, tvOS 14.0, *) {
|
|
36
|
+
VideoManager.shared.setAppropriateAudioSessionOrWarn()
|
|
37
|
+
playerViewController.allowsPictureInPicturePlayback = allowPictureInPicture
|
|
38
|
+
}
|
|
30
39
|
}
|
|
31
40
|
}
|
|
32
41
|
|
|
@@ -40,7 +49,10 @@ public final class VideoView: ExpoView, AVPlayerViewControllerDelegate {
|
|
|
40
49
|
}
|
|
41
50
|
|
|
42
51
|
lazy var supportsPictureInPicture: Bool = {
|
|
43
|
-
|
|
52
|
+
if #available(iOS 13.4, tvOS 14.0, *) {
|
|
53
|
+
return AVPictureInPictureController.isPictureInPictureSupported()
|
|
54
|
+
}
|
|
55
|
+
return false
|
|
44
56
|
}()
|
|
45
57
|
|
|
46
58
|
public required init(appContext: AppContext? = nil) {
|
|
@@ -53,7 +65,9 @@ public final class VideoView: ExpoView, AVPlayerViewControllerDelegate {
|
|
|
53
65
|
playerViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
54
66
|
playerViewController.view.backgroundColor = .clear
|
|
55
67
|
// Now playing is managed by the `NowPlayingManager`
|
|
68
|
+
#if !os(tvOS)
|
|
56
69
|
playerViewController.updatesNowPlayingInfoCenter = false
|
|
70
|
+
#endif
|
|
57
71
|
|
|
58
72
|
addSubview(playerViewController.view)
|
|
59
73
|
}
|
|
@@ -71,6 +85,16 @@ public final class VideoView: ExpoView, AVPlayerViewControllerDelegate {
|
|
|
71
85
|
|
|
72
86
|
if playerViewController.responds(to: selectorToForceFullScreenMode) {
|
|
73
87
|
playerViewController.perform(selectorToForceFullScreenMode, with: true, with: nil)
|
|
88
|
+
} else {
|
|
89
|
+
#if os(tvOS)
|
|
90
|
+
// For TV, save the currently playing state,
|
|
91
|
+
// remove the view controller from its superview,
|
|
92
|
+
// and present the view controller normally
|
|
93
|
+
wasPlaying = player?.isPlaying == true
|
|
94
|
+
self.playerViewController.view.removeFromSuperview()
|
|
95
|
+
self.reactViewController().present(self.playerViewController, animated: true)
|
|
96
|
+
isFullscreen = true
|
|
97
|
+
#endif
|
|
74
98
|
}
|
|
75
99
|
}
|
|
76
100
|
|
|
@@ -110,6 +134,31 @@ public final class VideoView: ExpoView, AVPlayerViewControllerDelegate {
|
|
|
110
134
|
|
|
111
135
|
// MARK: - AVPlayerViewControllerDelegate
|
|
112
136
|
|
|
137
|
+
#if os(tvOS)
|
|
138
|
+
// TV actually presents the playerViewController, so it implements the view controller
|
|
139
|
+
// dismissal delegate methods
|
|
140
|
+
public func playerViewControllerWillBeginDismissalTransition(_ playerViewController: AVPlayerViewController) {
|
|
141
|
+
// Start an appearance transition
|
|
142
|
+
self.playerViewController.beginAppearanceTransition(true, animated: true)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
public func playerViewControllerDidEndDismissalTransition(_ playerViewController: AVPlayerViewController) {
|
|
146
|
+
self.isFullscreen = false
|
|
147
|
+
// Reset the bounds of the view controller and add it back to our view
|
|
148
|
+
self.playerViewController.view.frame = self.bounds
|
|
149
|
+
addSubview(self.playerViewController.view)
|
|
150
|
+
// End the appearance transition
|
|
151
|
+
self.playerViewController.endAppearanceTransition()
|
|
152
|
+
// Ensure playing state is preserved
|
|
153
|
+
if wasPlaying {
|
|
154
|
+
self.player?.pointer.play()
|
|
155
|
+
} else {
|
|
156
|
+
self.player?.pointer.pause()
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
#endif
|
|
160
|
+
|
|
161
|
+
#if !os(tvOS)
|
|
113
162
|
public func playerViewController(
|
|
114
163
|
_ playerViewController: AVPlayerViewController,
|
|
115
164
|
willBeginFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator
|
|
@@ -134,6 +183,7 @@ public final class VideoView: ExpoView, AVPlayerViewControllerDelegate {
|
|
|
134
183
|
}
|
|
135
184
|
}
|
|
136
185
|
}
|
|
186
|
+
#endif
|
|
137
187
|
|
|
138
188
|
public func playerViewControllerDidStartPictureInPicture(_ playerViewController: AVPlayerViewController) {
|
|
139
189
|
isInPictureInPicture = true
|
|
@@ -146,6 +196,10 @@ public final class VideoView: ExpoView, AVPlayerViewControllerDelegate {
|
|
|
146
196
|
}
|
|
147
197
|
|
|
148
198
|
public override func didMoveToWindow() {
|
|
199
|
+
// TV is doing a normal view controller present, so we should not execute
|
|
200
|
+
// this code
|
|
201
|
+
#if !os(tvOS)
|
|
149
202
|
playerViewController.beginAppearanceTransition(self.window != nil, animated: true)
|
|
203
|
+
#endif
|
|
150
204
|
}
|
|
151
205
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-video",
|
|
3
3
|
"title": "Expo Video",
|
|
4
|
-
"version": "1.2.
|
|
4
|
+
"version": "1.2.2",
|
|
5
5
|
"description": "A cross-platform, performant video component for React Native and Expo with Web support",
|
|
6
6
|
"main": "build/index.js",
|
|
7
7
|
"types": "build/index.d.ts",
|
|
@@ -36,5 +36,5 @@
|
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"expo": "*"
|
|
38
38
|
},
|
|
39
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "c37efa4f4ebe6137c34eb5813843df7015f3c22f"
|
|
40
40
|
}
|