about summary refs log tree commit diff
path: root/patches
diff options
context:
space:
mode:
authorHailey <me@haileyok.com>2024-09-05 08:27:28 -0700
committerGitHub <noreply@github.com>2024-09-05 08:27:28 -0700
commit93c171b403a993a5b942a6333b4de54b99b9265a (patch)
tree904a45cb1a02ed101b1a73d4e1c53f94463c20ee /patches
parent6d8ed5c3c8d0d5176532d3fbc06f0b0257c6e743 (diff)
downloadvoidsky-93c171b403a993a5b942a6333b4de54b99b9265a.tar.zst
[Video] Use `expo-video` from fork (#5159)
Diffstat (limited to 'patches')
-rw-r--r--patches/expo-video+1.2.4.patch608
-rw-r--r--patches/expo-video+1.2.4.patch.md31
2 files changed, 0 insertions, 639 deletions
diff --git a/patches/expo-video+1.2.4.patch b/patches/expo-video+1.2.4.patch
deleted file mode 100644
index bc20fa0ea..000000000
--- a/patches/expo-video+1.2.4.patch
+++ /dev/null
@@ -1,608 +0,0 @@
-diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerEvent.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerEvent.kt
-index 473f964..f37aff9 100644
---- a/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerEvent.kt
-+++ b/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerEvent.kt
-@@ -41,6 +41,11 @@ sealed class PlayerEvent {
-     override val name = "playToEnd"
-   }
-
-+  data class PlayerTimeRemainingChanged(val timeRemaining: Double): PlayerEvent() {
-+    override val name = "timeRemainingChange"
-+    override val arguments = arrayOf(timeRemaining)
-+  }
-+
-   fun emit(player: VideoPlayer, listeners: List<VideoPlayerListener>) {
-     when (this) {
-       is StatusChanged -> listeners.forEach { it.onStatusChanged(player, status, oldStatus, error) }
-@@ -49,6 +54,7 @@ sealed class PlayerEvent {
-       is SourceChanged -> listeners.forEach { it.onSourceChanged(player, source, oldSource) }
-       is PlaybackRateChanged -> listeners.forEach { it.onPlaybackRateChanged(player, rate, oldRate) }
-       is PlayedToEnd -> listeners.forEach { it.onPlayedToEnd(player) }
-+      is PlayerTimeRemainingChanged -> listeners.forEach { it.onPlayerTimeRemainingChanged(player, timeRemaining) }
-     }
-   }
- }
-diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerViewExtension.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerViewExtension.kt
-index 9905e13..47342ff 100644
---- a/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerViewExtension.kt
-+++ b/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerViewExtension.kt
-@@ -11,6 +11,7 @@ internal fun PlayerView.applyRequiresLinearPlayback(requireLinearPlayback: Boole
-   setShowPreviousButton(!requireLinearPlayback)
-   setShowNextButton(!requireLinearPlayback)
-   setTimeBarInteractive(requireLinearPlayback)
-+  setShowSubtitleButton(true)
- }
-
- @androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
-@@ -27,7 +28,8 @@ internal fun PlayerView.setTimeBarInteractive(interactive: Boolean) {
-
- @androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
- internal fun PlayerView.setFullscreenButtonVisibility(visible: Boolean) {
--  val fullscreenButton = findViewById<android.widget.ImageButton>(androidx.media3.ui.R.id.exo_fullscreen)
-+  val fullscreenButton =
-+    findViewById<android.widget.ImageButton>(androidx.media3.ui.R.id.exo_fullscreen)
-   fullscreenButton?.visibility = if (visible) {
-     android.view.View.VISIBLE
-   } else {
-diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/ProgressTracker.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/ProgressTracker.kt
-new file mode 100644
-index 0000000..0249e23
---- /dev/null
-+++ b/node_modules/expo-video/android/src/main/java/expo/modules/video/ProgressTracker.kt
-@@ -0,0 +1,29 @@
-+import android.os.Handler
-+import android.os.Looper
-+import androidx.annotation.OptIn
-+import androidx.media3.common.util.UnstableApi
-+import expo.modules.video.PlayerEvent
-+import expo.modules.video.VideoPlayer
-+import kotlin.math.floor
-+
-+@OptIn(UnstableApi::class)
-+class ProgressTracker(private val videoPlayer: VideoPlayer) : Runnable {
-+  private val handler: Handler = Handler(Looper.getMainLooper())
-+  private val player = videoPlayer.player
-+
-+  init {
-+    handler.post(this)
-+  }
-+
-+  override fun run() {
-+    val currentPosition = player.currentPosition
-+    val duration = player.duration
-+    val timeRemaining = floor(((duration - currentPosition) / 1000).toDouble())
-+    videoPlayer.sendEvent(PlayerEvent.PlayerTimeRemainingChanged(timeRemaining))
-+    handler.postDelayed(this, 1000 /* ms */)
-+  }
-+
-+  fun remove() {
-+    handler.removeCallbacks(this)
-+  }
-+}
-\ No newline at end of file
-diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoManager.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoManager.kt
-index 4b6c6d8..e20f51a 100644
---- a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoManager.kt
-+++ b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoManager.kt
-@@ -1,5 +1,6 @@
- package expo.modules.video
-
-+import android.provider.MediaStore.Video
- import androidx.annotation.OptIn
- import androidx.media3.common.util.UnstableApi
- import expo.modules.kotlin.AppContext
-@@ -15,6 +16,8 @@ object VideoManager {
-   // Keeps track of all existing VideoPlayers, and whether they are attached to a VideoView
-   private var videoPlayersToVideoViews = mutableMapOf<VideoPlayer, MutableList<VideoView>>()
-
-+  private var previouslyPlayingViews: MutableList<VideoView>? = null
-+
-   private lateinit var audioFocusManager: AudioFocusManager
-
-   fun onModuleCreated(appContext: AppContext) {
-@@ -69,16 +72,24 @@ object VideoManager {
-     return videoPlayersToVideoViews[videoPlayer]?.isNotEmpty() ?: false
-   }
-
--  fun onAppForegrounded() = Unit
-+  fun onAppForegrounded() {
-+    val previouslyPlayingViews = this.previouslyPlayingViews ?: return
-+    for (videoView in previouslyPlayingViews) {
-+      val player = videoView.videoPlayer?.player ?: continue
-+      player.play()
-+    }
-+    this.previouslyPlayingViews = null
-+  }
-
-   fun onAppBackgrounded() {
-+    val previouslyPlayingViews = mutableListOf<VideoView>()
-     for (videoView in videoViews.values) {
--      if (videoView.videoPlayer?.staysActiveInBackground == false &&
--        !videoView.willEnterPiP &&
--        !videoView.isInFullscreen
--      ) {
--        videoView.videoPlayer?.player?.pause()
-+      val player = videoView.videoPlayer?.player ?: continue
-+      if (player.isPlaying) {
-+        player.pause()
-+        previouslyPlayingViews.add(videoView)
-       }
-     }
-+    this.previouslyPlayingViews = previouslyPlayingViews
-   }
- }
-diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoModule.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoModule.kt
-index ec3da2a..5a1397a 100644
---- a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoModule.kt
-+++ b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoModule.kt
-@@ -43,7 +43,9 @@ class VideoModule : Module() {
-     View(VideoView::class) {
-       Events(
-         "onPictureInPictureStart",
--        "onPictureInPictureStop"
-+        "onPictureInPictureStop",
-+        "onEnterFullscreen",
-+        "onExitFullscreen"
-       )
-
-       Prop("player") { view: VideoView, player: VideoPlayer ->
-diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoPlayer.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoPlayer.kt
-index 58f00af..5ad8237 100644
---- a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoPlayer.kt
-+++ b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoPlayer.kt
-@@ -1,5 +1,6 @@
- package expo.modules.video
-
-+import ProgressTracker
- import android.content.Context
- import android.view.SurfaceView
- import androidx.media3.common.MediaItem
-@@ -35,11 +36,13 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
-     .Builder(context, renderersFactory)
-     .setLooper(context.mainLooper)
-     .build()
-+  var progressTracker: ProgressTracker? = null
-
-   val serviceConnection = PlaybackServiceConnection(WeakReference(player))
-
-   var playing by IgnoreSameSet(false) { new, old ->
-     sendEvent(PlayerEvent.IsPlayingChanged(new, old))
-+    addOrRemoveProgressTracker()
-   }
-
-   var uncommittedSource: VideoSource? = source
-@@ -141,6 +144,9 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
-   }
-
-   override fun close() {
-+    this.progressTracker?.remove()
-+    this.progressTracker = null
-+
-     appContext?.reactContext?.unbindService(serviceConnection)
-     serviceConnection.playbackServiceBinder?.service?.unregisterPlayer(player)
-     VideoManager.unregisterVideoPlayer(this@VideoPlayer)
-@@ -228,7 +234,7 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
-     listeners.removeAll { it.get() == videoPlayerListener }
-   }
-
--  private fun sendEvent(event: PlayerEvent) {
-+  fun sendEvent(event: PlayerEvent) {
-     // Emits to the native listeners
-     event.emit(this, listeners.mapNotNull { it.get() })
-     // Emits to the JS side
-@@ -240,4 +246,13 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
-       sendEvent(eventName, *args)
-     }
-   }
-+
-+  private fun addOrRemoveProgressTracker() {
-+    this.progressTracker?.remove()
-+    if (this.playing) {
-+      this.progressTracker = ProgressTracker(this)
-+    } else {
-+      this.progressTracker = null
-+    }
-+  }
- }
-diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoPlayerListener.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoPlayerListener.kt
-index f654254..dcfe3f0 100644
---- a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoPlayerListener.kt
-+++ b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoPlayerListener.kt
-@@ -15,4 +15,5 @@ interface VideoPlayerListener {
-   fun onSourceChanged(player: VideoPlayer, source: VideoSource?, oldSource: VideoSource?) {}
-   fun onPlaybackRateChanged(player: VideoPlayer, rate: Float, oldRate: Float?) {}
-   fun onPlayedToEnd(player: VideoPlayer) {}
-+  fun onPlayerTimeRemainingChanged(player: VideoPlayer, timeRemaining: Double) {}
- }
-diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoView.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoView.kt
-index a951d80..3932535 100644
---- a/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoView.kt
-+++ b/node_modules/expo-video/android/src/main/java/expo/modules/video/VideoView.kt
-@@ -36,6 +36,8 @@ class VideoView(context: Context, appContext: AppContext) : ExpoView(context, ap
-   val playerView: PlayerView = PlayerView(context.applicationContext)
-   val onPictureInPictureStart by EventDispatcher<Unit>()
-   val onPictureInPictureStop by EventDispatcher<Unit>()
-+  val onEnterFullscreen by EventDispatcher()
-+  val onExitFullscreen by EventDispatcher()
-
-   var willEnterPiP: Boolean = false
-   var isInFullscreen: Boolean = false
-@@ -154,6 +156,7 @@ class VideoView(context: Context, appContext: AppContext) : ExpoView(context, ap
-       @Suppress("DEPRECATION")
-       currentActivity.overridePendingTransition(0, 0)
-     }
-+    onEnterFullscreen(mapOf())
-     isInFullscreen = true
-   }
-
-@@ -162,6 +165,7 @@ class VideoView(context: Context, appContext: AppContext) : ExpoView(context, ap
-     val fullScreenButton: ImageButton = playerView.findViewById(androidx.media3.ui.R.id.exo_fullscreen)
-     fullScreenButton.setImageResource(androidx.media3.ui.R.drawable.exo_icon_fullscreen_enter)
-     videoPlayer?.changePlayerView(playerView)
-+    this.onExitFullscreen(mapOf())
-     isInFullscreen = false
-   }
-
-diff --git a/node_modules/expo-video/build/VideoPlayer.types.d.ts b/node_modules/expo-video/build/VideoPlayer.types.d.ts
-index a09fcfe..46cbae7 100644
---- a/node_modules/expo-video/build/VideoPlayer.types.d.ts
-+++ b/node_modules/expo-video/build/VideoPlayer.types.d.ts
-@@ -128,6 +128,8 @@ export type VideoPlayerEvents = {
-      * Handler for an event emitted when the current media source of the player changes.
-      */
-     sourceChange(newSource: VideoSource, previousSource: VideoSource): void;
-+
-+    timeRemainingChange(timeRemaining: number): void;
- };
- /**
-  * Describes the current status of the player.
-@@ -136,7 +138,7 @@ export type VideoPlayerEvents = {
-  * - `readyToPlay`: The player has loaded enough data to start playing or to continue playback.
-  * - `error`: The player has encountered an error while loading or playing the video.
-  */
--export type VideoPlayerStatus = 'idle' | 'loading' | 'readyToPlay' | 'error';
-+export type VideoPlayerStatus = 'idle' | 'loading' | 'readyToPlay' | 'error' | 'waitingToPlayAtSpecifiedRate';
- export type VideoSource = string | {
-     /**
-      * The URI of the video.
-diff --git a/node_modules/expo-video/build/VideoView.types.d.ts b/node_modules/expo-video/build/VideoView.types.d.ts
-index cb9ca6d..ed8bb7e 100644
---- a/node_modules/expo-video/build/VideoView.types.d.ts
-+++ b/node_modules/expo-video/build/VideoView.types.d.ts
-@@ -89,5 +89,8 @@ export interface VideoViewProps extends ViewProps {
-      * @platform ios 16.0+
-      */
-     allowsVideoFrameAnalysis?: boolean;
-+
-+    onEnterFullscreen?: () => void;
-+    onExitFullscreen?: () => void;
- }
- //# sourceMappingURL=VideoView.types.d.ts.map
-\ No newline at end of file
-diff --git a/node_modules/expo-video/ios/Enums/PlayerStatus.swift b/node_modules/expo-video/ios/Enums/PlayerStatus.swift
-index 6af69ca..189fbbe 100644
---- a/node_modules/expo-video/ios/Enums/PlayerStatus.swift
-+++ b/node_modules/expo-video/ios/Enums/PlayerStatus.swift
-@@ -6,5 +6,8 @@ internal enum PlayerStatus: String, Enumerable {
-   case idle
-   case loading
-   case readyToPlay
-+  case waitingToPlayAtSpecifiedRate
-+  case unlikeToKeepUp
-+  case playbackBufferEmpty
-   case error
- }
-diff --git a/node_modules/expo-video/ios/VideoManager.swift b/node_modules/expo-video/ios/VideoManager.swift
-index 094a8b0..16e7081 100644
---- a/node_modules/expo-video/ios/VideoManager.swift
-+++ b/node_modules/expo-video/ios/VideoManager.swift
-@@ -12,6 +12,7 @@ class VideoManager {
-
-   private var videoViews = NSHashTable<VideoView>.weakObjects()
-   private var videoPlayers = NSHashTable<VideoPlayer>.weakObjects()
-+  private var previouslyPlayingPlayers: [VideoPlayer]?
-
-   func register(videoPlayer: VideoPlayer) {
-     videoPlayers.add(videoPlayer)
-@@ -33,63 +34,70 @@ class VideoManager {
-     for videoPlayer in videoPlayers.allObjects {
-       videoPlayer.setTracksEnabled(true)
-     }
-+
-+    if let previouslyPlayingPlayers = self.previouslyPlayingPlayers {
-+      previouslyPlayingPlayers.forEach { player in
-+        player.pointer.play()
-+      }
-+    }
-   }
-
-   func onAppBackgrounded() {
-+    var previouslyPlayingPlayers: [VideoPlayer] = []
-     for videoView in videoViews.allObjects {
-       guard let player = videoView.player else {
-         continue
-       }
--      if player.staysActiveInBackground == true {
--        player.setTracksEnabled(videoView.isInPictureInPicture)
--      } else if !videoView.isInPictureInPicture {
-+      if player.isPlaying {
-         player.pointer.pause()
-+        previouslyPlayingPlayers.append(player)
-       }
-     }
-+    self.previouslyPlayingPlayers = previouslyPlayingPlayers
-   }
-
-   // MARK: - Audio Session Management
-
-   internal func setAppropriateAudioSessionOrWarn() {
--    let audioSession = AVAudioSession.sharedInstance()
--    var audioSessionCategoryOptions: AVAudioSession.CategoryOptions = []
--
--    let isAnyPlayerPlaying = videoPlayers.allObjects.contains { player in
--      player.isPlaying
--    }
--    let areAllPlayersMuted = videoPlayers.allObjects.allSatisfy { player in
--      player.isMuted
--    }
--    let needsPiPSupport = videoViews.allObjects.contains { view in
--      view.allowPictureInPicture
--    }
--    let anyPlayerShowsNotification = videoPlayers.allObjects.contains { player in
--      player.showNowPlayingNotification
--    }
--    // The notification won't be shown if we allow the audio to mix with others
--    let shouldAllowMixing = (!isAnyPlayerPlaying || areAllPlayersMuted) && !anyPlayerShowsNotification
--    let isOutputtingAudio = !areAllPlayersMuted && isAnyPlayerPlaying
--    let shouldUpdateToAllowMixing = !audioSession.categoryOptions.contains(.mixWithOthers) && shouldAllowMixing
--
--    if shouldAllowMixing {
--      audioSessionCategoryOptions.insert(.mixWithOthers)
--    }
--
--    if isOutputtingAudio || needsPiPSupport || shouldUpdateToAllowMixing || anyPlayerShowsNotification {
--      do {
--        try audioSession.setCategory(.playback, mode: .moviePlayback)
--      } catch {
--        log.warn("Failed to set audio session category. This might cause issues with audio playback and Picture in Picture. \(error.localizedDescription)")
--      }
--    }
--
--    // Make sure audio session is active if any video is playing
--    if isAnyPlayerPlaying {
--      do {
--        try audioSession.setActive(true)
--      } catch {
--        log.warn("Failed to activate the audio session. This might cause issues with audio playback. \(error.localizedDescription)")
--      }
--    }
-+//    let audioSession = AVAudioSession.sharedInstance()
-+//    var audioSessionCategoryOptions: AVAudioSession.CategoryOptions = []
-+//
-+//    let isAnyPlayerPlaying = videoPlayers.allObjects.contains { player in
-+//      player.isPlaying
-+//    }
-+//    let areAllPlayersMuted = videoPlayers.allObjects.allSatisfy { player in
-+//      player.isMuted
-+//    }
-+//    let needsPiPSupport = videoViews.allObjects.contains { view in
-+//      view.allowPictureInPicture
-+//    }
-+//    let anyPlayerShowsNotification = videoPlayers.allObjects.contains { player in
-+//      player.showNowPlayingNotification
-+//    }
-+//    // The notification won't be shown if we allow the audio to mix with others
-+//    let shouldAllowMixing = (!isAnyPlayerPlaying || areAllPlayersMuted) && !anyPlayerShowsNotification
-+//    let isOutputtingAudio = !areAllPlayersMuted && isAnyPlayerPlaying
-+//    let shouldUpdateToAllowMixing = !audioSession.categoryOptions.contains(.mixWithOthers) && shouldAllowMixing
-+//
-+//    if shouldAllowMixing {
-+//      audioSessionCategoryOptions.insert(.mixWithOthers)
-+//    }
-+//
-+//    if isOutputtingAudio || needsPiPSupport || shouldUpdateToAllowMixing || anyPlayerShowsNotification {
-+//      do {
-+//        try audioSession.setCategory(.playback, mode: .moviePlayback)
-+//      } catch {
-+//        log.warn("Failed to set audio session category. This might cause issues with audio playback and Picture in Picture. \(error.localizedDescription)")
-+//      }
-+//    }
-+//
-+//    // Make sure audio session is active if any video is playing
-+//    if isAnyPlayerPlaying {
-+//      do {
-+//        try audioSession.setActive(true)
-+//      } catch {
-+//        log.warn("Failed to activate the audio session. This might cause issues with audio playback. \(error.localizedDescription)")
-+//      }
-+//    }
-   }
- }
-diff --git a/node_modules/expo-video/ios/VideoModule.swift b/node_modules/expo-video/ios/VideoModule.swift
-index c537a12..e4a918f 100644
---- a/node_modules/expo-video/ios/VideoModule.swift
-+++ b/node_modules/expo-video/ios/VideoModule.swift
-@@ -16,7 +16,9 @@ public final class VideoModule: Module {
-     View(VideoView.self) {
-       Events(
-         "onPictureInPictureStart",
--        "onPictureInPictureStop"
-+        "onPictureInPictureStop",
-+        "onEnterFullscreen",
-+        "onExitFullscreen"
-       )
-
-       Prop("player") { (view, player: VideoPlayer?) in
-diff --git a/node_modules/expo-video/ios/VideoPlayer.swift b/node_modules/expo-video/ios/VideoPlayer.swift
-index 3315b88..733ab1f 100644
---- a/node_modules/expo-video/ios/VideoPlayer.swift
-+++ b/node_modules/expo-video/ios/VideoPlayer.swift
-@@ -185,6 +185,10 @@ internal final class VideoPlayer: SharedRef<AVPlayer>, Hashable, VideoPlayerObse
-     safeEmit(event: "sourceChange", arguments: newVideoPlayerItem?.videoSource, oldVideoPlayerItem?.videoSource)
-   }
-
-+  func onPlayerTimeRemainingChanged(player: AVPlayer, timeRemaining: Double) {
-+    safeEmit(event: "timeRemainingChange", arguments: timeRemaining)
-+  }
-+
-   func safeEmit<each A: AnyArgument>(event: String, arguments: repeat each A) {
-     if self.appContext != nil {
-       self.emit(event: event, arguments: repeat each arguments)
-diff --git a/node_modules/expo-video/ios/VideoPlayerObserver.swift b/node_modules/expo-video/ios/VideoPlayerObserver.swift
-index d289e26..7de8cbf 100644
---- a/node_modules/expo-video/ios/VideoPlayerObserver.swift
-+++ b/node_modules/expo-video/ios/VideoPlayerObserver.swift
-@@ -21,6 +21,7 @@ protocol VideoPlayerObserverDelegate: AnyObject {
-   func onItemChanged(player: AVPlayer, oldVideoPlayerItem: VideoPlayerItem?, newVideoPlayerItem: VideoPlayerItem?)
-   func onIsMutedChanged(player: AVPlayer, oldIsMuted: Bool?, newIsMuted: Bool)
-   func onPlayerItemStatusChanged(player: AVPlayer, oldStatus: AVPlayerItem.Status?, newStatus: AVPlayerItem.Status)
-+  func onPlayerTimeRemainingChanged(player: AVPlayer, timeRemaining: Double)
- }
-
- // Default implementations for the delegate
-@@ -33,6 +34,7 @@ extension VideoPlayerObserverDelegate {
-   func onItemChanged(player: AVPlayer, oldVideoPlayerItem: VideoPlayerItem?, newVideoPlayerItem: VideoPlayerItem?) {}
-   func onIsMutedChanged(player: AVPlayer, oldIsMuted: Bool?, newIsMuted: Bool) {}
-   func onPlayerItemStatusChanged(player: AVPlayer, oldStatus: AVPlayerItem.Status?, newStatus: AVPlayerItem.Status) {}
-+  func onPlayerTimeRemainingChanged(player: AVPlayer, timeRemaining: Double) {}
- }
-
- // Wrapper used to store WeakReferences to the observer delegate
-@@ -91,6 +93,7 @@ class VideoPlayerObserver {
-   private var playerVolumeObserver: NSKeyValueObservation?
-   private var playerCurrentItemObserver: NSKeyValueObservation?
-   private var playerIsMutedObserver: NSKeyValueObservation?
-+  private var playerPeriodicTimeObserver: Any?
-
-   // Current player item observers
-   private var playbackBufferEmptyObserver: NSKeyValueObservation?
-@@ -152,6 +155,9 @@ class VideoPlayerObserver {
-     playerVolumeObserver?.invalidate()
-     playerIsMutedObserver?.invalidate()
-     playerCurrentItemObserver?.invalidate()
-+    if let playerPeriodicTimeObserver = self.playerPeriodicTimeObserver {
-+      player?.removeTimeObserver(playerPeriodicTimeObserver)
-+    }
-   }
-
-   private func initializeCurrentPlayerItemObservers(player: AVPlayer, playerItem: AVPlayerItem) {
-@@ -265,23 +271,24 @@ class VideoPlayerObserver {
-     if player.timeControlStatus != .waitingToPlayAtSpecifiedRate && player.status == .readyToPlay && currentItem?.isPlaybackBufferEmpty != true {
-       status = .readyToPlay
-     } else if player.timeControlStatus == .waitingToPlayAtSpecifiedRate {
--      status = .loading
-+      status = .waitingToPlayAtSpecifiedRate
-     }
-
-     if isPlaying != (player.timeControlStatus == .playing) {
-       isPlaying = player.timeControlStatus == .playing
-+      addPeriodicTimeObserverIfNeeded()
-     }
-   }
-
-   private func onIsBufferEmptyChanged(_ playerItem: AVPlayerItem, _ change: NSKeyValueObservedChange<Bool>) {
-     if playerItem.isPlaybackBufferEmpty {
--      status = .loading
-+      status = .playbackBufferEmpty
-     }
-   }
-
-   private func onPlayerLikelyToKeepUpChanged(_ playerItem: AVPlayerItem, _ change: NSKeyValueObservedChange<Bool>) {
-     if !playerItem.isPlaybackLikelyToKeepUp && playerItem.isPlaybackBufferEmpty {
--      status = .loading
-+      status = .unlikeToKeepUp
-     } else if playerItem.isPlaybackLikelyToKeepUp {
-       status = .readyToPlay
-     }
-@@ -310,4 +317,28 @@ class VideoPlayerObserver {
-       }
-     }
-   }
-+
-+  private func onPlayerTimeRemainingChanged(_ player: AVPlayer, _ timeRemaining: Double) {
-+    delegates.forEach { delegate in
-+      delegate.value?.onPlayerTimeRemainingChanged(player: player, timeRemaining: timeRemaining)
-+    }
-+  }
-+
-+  private func addPeriodicTimeObserverIfNeeded() {
-+    guard self.playerPeriodicTimeObserver == nil, let player = self.player else {
-+      return
-+    }
-+
-+    if isPlaying {
-+      // Add the time update listener
-+      playerPeriodicTimeObserver = player.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1.0, preferredTimescale: Int32(NSEC_PER_SEC)), queue: nil) { event in
-+        guard let duration = player.currentItem?.duration else {
-+          return
-+        }
-+
-+        let timeRemaining = (duration.seconds - event.seconds).rounded()
-+        self.onPlayerTimeRemainingChanged(player, timeRemaining)
-+      }
-+    }
-+  }
- }
-diff --git a/node_modules/expo-video/ios/VideoView.swift b/node_modules/expo-video/ios/VideoView.swift
-index f4579e4..10c5908 100644
---- a/node_modules/expo-video/ios/VideoView.swift
-+++ b/node_modules/expo-video/ios/VideoView.swift
-@@ -41,6 +41,8 @@ public final class VideoView: ExpoView, AVPlayerViewControllerDelegate {
-
-   let onPictureInPictureStart = EventDispatcher()
-   let onPictureInPictureStop = EventDispatcher()
-+  let onEnterFullscreen = EventDispatcher()
-+  let onExitFullscreen = EventDispatcher()
-
-   public override var bounds: CGRect {
-     didSet {
-@@ -163,6 +165,7 @@ public final class VideoView: ExpoView, AVPlayerViewControllerDelegate {
-     _ playerViewController: AVPlayerViewController,
-     willBeginFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator
-   ) {
-+    onEnterFullscreen()
-     isFullscreen = true
-   }
-
-@@ -179,6 +182,7 @@ public final class VideoView: ExpoView, AVPlayerViewControllerDelegate {
-         if wasPlaying {
-           self.player?.pointer.play()
-         }
-+        self.onExitFullscreen()
-         self.isFullscreen = false
-       }
-     }
-diff --git a/node_modules/expo-video/src/VideoPlayer.types.ts b/node_modules/expo-video/src/VideoPlayer.types.ts
-index aaf4b63..5ff6b7a 100644
---- a/node_modules/expo-video/src/VideoPlayer.types.ts
-+++ b/node_modules/expo-video/src/VideoPlayer.types.ts
-@@ -151,6 +151,8 @@ export type VideoPlayerEvents = {
-    * Handler for an event emitted when the current media source of the player changes.
-    */
-   sourceChange(newSource: VideoSource, previousSource: VideoSource): void;
-+
-+  timeRemainingChange(timeRemaining: number): void;
- };
-
- /**
-@@ -160,7 +162,7 @@ export type VideoPlayerEvents = {
-  * - `readyToPlay`: The player has loaded enough data to start playing or to continue playback.
-  * - `error`: The player has encountered an error while loading or playing the video.
-  */
--export type VideoPlayerStatus = 'idle' | 'loading' | 'readyToPlay' | 'error';
-+export type VideoPlayerStatus = 'idle' | 'loading' | 'readyToPlay' | 'error' | 'waitingToPlayAtSpecifiedRate';
-
- export type VideoSource =
-   | string
-diff --git a/node_modules/expo-video/src/VideoView.types.ts b/node_modules/expo-video/src/VideoView.types.ts
-index 29fe5db..e1fbf59 100644
---- a/node_modules/expo-video/src/VideoView.types.ts
-+++ b/node_modules/expo-video/src/VideoView.types.ts
-@@ -100,4 +100,7 @@ export interface VideoViewProps extends ViewProps {
-    * @platform ios 16.0+
-    */
-   allowsVideoFrameAnalysis?: boolean;
-+
-+  onEnterFullscreen?: () => void;
-+  onExitFullscreen?: () => void;
- }
diff --git a/patches/expo-video+1.2.4.patch.md b/patches/expo-video+1.2.4.patch.md
deleted file mode 100644
index 7cd4d363a..000000000
--- a/patches/expo-video+1.2.4.patch.md
+++ /dev/null
@@ -1,31 +0,0 @@
-## uwu woad beawing, do not wemove
-
-## `expo-video` Patch
-
-### `onEnterFullScreen`/`onExitFullScreen`
-
-Adds two props to `VideoView`: `onEnterFullscreen` and `onExitFullscreen` which do exactly what they say on
-the tin.
-
-### Removing audio session management
-
-This patch also removes the audio session management that Expo does on its own, as we handle audio session management
-ourselves.
-
-### Pausing/playing on background/foreground
-
-Instead of handling the pausing/playing of videos in React, we'll handle them here. There's some logic that we do not
-need (around PIP mode) that we can remove, and just pause any playing players on background and then resume them on
-foreground.
-
-### Additional `statusChange` Events
-
-`expo-video` uses the `loading` status for a variety of cases where the video is not actually "loading". We're making
-those status events more specific here, so that we can determine if a video is truly loading or not. These statuses are:
-
-- `waitingToPlayAtSpecifiedRate`
-- `unlikelyToKeepUp`
-- `playbackBufferEmpty`
-
-It's unlikely we will ever need to pay attention to these statuses, so they are not being include in the TypeScript
-types.