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 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
@@ -1,7 +1,7 @@
1
1
  apply plugin: 'com.android.library'
2
2
 
3
3
  group = 'host.exp.exponent'
4
- version = '1.2.0'
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.0'
17
+ versionName '1.2.2'
18
18
  }
19
19
  }
20
20
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "platforms": ["ios", "android"],
2
+ "platforms": ["apple", "android"],
3
3
  "ios": {
4
4
  "modules": ["VideoModule"]
5
5
  },
@@ -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.platform = :ios, '13.4'
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
 
@@ -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
- return AVPictureInPictureController.isPictureInPictureSupported()
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
@@ -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 = AVURLAsset(url: url, options: ["AVURLAssetHTTPHeaderFieldsKey": videoSource.headers])
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
@@ -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
- VideoManager.shared.setAppropriateAudioSessionOrWarn()
29
- playerViewController.allowsPictureInPicturePlayback = allowPictureInPicture
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
- return AVPictureInPictureController.isPictureInPictureSupported()
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.0",
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": "0d1a3567cb0fce9c54e1185654be88bd0c7842d4"
39
+ "gitHead": "c37efa4f4ebe6137c34eb5813843df7015f3c22f"
40
40
  }