expo-video 3.0.8 → 3.0.9

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.
Files changed (57) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/AndroidManifest.xml +0 -8
  4. package/android/src/main/java/expo/modules/video/VideoExceptions.kt +8 -0
  5. package/android/src/main/java/expo/modules/video/playbackService/ExpoVideoPlaybackService.kt +125 -46
  6. package/android/src/main/java/expo/modules/video/playbackService/PlaybackServiceConnection.kt +35 -12
  7. package/android/src/main/java/expo/modules/video/player/VideoPlayer.kt +36 -4
  8. package/build/VideoPlayer.types.d.ts +5 -0
  9. package/build/VideoPlayer.types.d.ts.map +1 -1
  10. package/build/VideoPlayer.types.js.map +1 -1
  11. package/expo-module.config.json +1 -1
  12. package/local-maven-repo/host/exp/exponent/expo.modules.video/{3.0.8/expo.modules.video-3.0.8-sources.jar → 3.0.9/expo.modules.video-3.0.9-sources.jar} +0 -0
  13. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.9/expo.modules.video-3.0.9-sources.jar.md5 +1 -0
  14. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.9/expo.modules.video-3.0.9-sources.jar.sha1 +1 -0
  15. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.9/expo.modules.video-3.0.9-sources.jar.sha256 +1 -0
  16. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.9/expo.modules.video-3.0.9-sources.jar.sha512 +1 -0
  17. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.9/expo.modules.video-3.0.9.aar +0 -0
  18. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.9/expo.modules.video-3.0.9.aar.md5 +1 -0
  19. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.9/expo.modules.video-3.0.9.aar.sha1 +1 -0
  20. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.9/expo.modules.video-3.0.9.aar.sha256 +1 -0
  21. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.9/expo.modules.video-3.0.9.aar.sha512 +1 -0
  22. package/local-maven-repo/host/exp/exponent/expo.modules.video/{3.0.8/expo.modules.video-3.0.8.module → 3.0.9/expo.modules.video-3.0.9.module} +22 -22
  23. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.9/expo.modules.video-3.0.9.module.md5 +1 -0
  24. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.9/expo.modules.video-3.0.9.module.sha1 +1 -0
  25. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.9/expo.modules.video-3.0.9.module.sha256 +1 -0
  26. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.9/expo.modules.video-3.0.9.module.sha512 +1 -0
  27. package/local-maven-repo/host/exp/exponent/expo.modules.video/{3.0.8/expo.modules.video-3.0.8.pom → 3.0.9/expo.modules.video-3.0.9.pom} +1 -1
  28. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.9/expo.modules.video-3.0.9.pom.md5 +1 -0
  29. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.9/expo.modules.video-3.0.9.pom.sha1 +1 -0
  30. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.9/expo.modules.video-3.0.9.pom.sha256 +1 -0
  31. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.9/expo.modules.video-3.0.9.pom.sha512 +1 -0
  32. package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml +4 -4
  33. package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.md5 +1 -1
  34. package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.sha1 +1 -1
  35. package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.sha256 +1 -1
  36. package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.sha512 +1 -1
  37. package/package.json +2 -2
  38. package/plugin/build/withExpoVideo.js +44 -8
  39. package/plugin/src/withExpoVideo.ts +51 -7
  40. package/src/VideoPlayer.types.ts +5 -0
  41. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8-sources.jar.md5 +0 -1
  42. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8-sources.jar.sha1 +0 -1
  43. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8-sources.jar.sha256 +0 -1
  44. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8-sources.jar.sha512 +0 -1
  45. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.aar +0 -0
  46. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.aar.md5 +0 -1
  47. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.aar.sha1 +0 -1
  48. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.aar.sha256 +0 -1
  49. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.aar.sha512 +0 -1
  50. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.module.md5 +0 -1
  51. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.module.sha1 +0 -1
  52. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.module.sha256 +0 -1
  53. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.module.sha512 +0 -1
  54. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.pom.md5 +0 -1
  55. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.pom.sha1 +0 -1
  56. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.pom.sha256 +0 -1
  57. package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.pom.sha512 +0 -1
package/CHANGELOG.md CHANGED
@@ -10,6 +10,16 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 3.0.9 — 2025-09-03
14
+
15
+ ### 🛠 Breaking changes
16
+
17
+ - [Android] In order to show the now playing notification, the `supportsBackgroundPlayground` property of the config plugin has to be `true`. ([#38980](https://github.com/expo/expo/pull/38980) by [@kerwanp](https://github.com/kerwanp))
18
+
19
+ ### 🐛 Bug fixes
20
+
21
+ - [Android] Fix video player stopping playback a few minutes after locking the device. ([#38980](https://github.com/expo/expo/pull/38980) by [@kerwanp](https://github.com/kerwanp))
22
+
13
23
  ## 3.0.8 — 2025-09-02
14
24
 
15
25
  _This version does not introduce any user-facing changes._
@@ -4,13 +4,13 @@ plugins {
4
4
  }
5
5
 
6
6
  group = 'host.exp.exponent'
7
- version = '3.0.8'
7
+ version = '3.0.9'
8
8
 
9
9
  android {
10
10
  namespace "expo.modules.video"
11
11
  defaultConfig {
12
12
  versionCode 1
13
- versionName '3.0.8'
13
+ versionName '3.0.9'
14
14
  }
15
15
  }
16
16
 
@@ -8,13 +8,5 @@
8
8
  android:supportsPictureInPicture="true"
9
9
  android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|navigation"
10
10
  android:theme="@style/Fullscreen" />
11
- <service
12
- android:name=".playbackService.ExpoVideoPlaybackService"
13
- android:exported="false"
14
- android:foregroundServiceType="mediaPlayback">
15
- <intent-filter>
16
- <action android:name="androidx.media3.session.MediaSessionService" />
17
- </intent-filter>
18
- </service>
19
11
  </application>
20
12
  </manifest>
@@ -3,6 +3,8 @@ package expo.modules.video
3
3
  import expo.modules.kotlin.exception.CodedException
4
4
  import expo.modules.video.enums.DRMType
5
5
 
6
+ private const val defaultServiceBindingTip = "Make sure that the expo-video config plugin is properly configured to avoid issues with displaying the now playing notification and sustaining background playback."
7
+
6
8
  internal class FullScreenVideoViewNotFoundException :
7
9
  CodedException("VideoView id wasn't passed to the activity")
8
10
 
@@ -35,3 +37,9 @@ internal class FailedToGetAudioFocusManagerException :
35
37
 
36
38
  internal class VideoCacheException(message: String?, cause: Throwable? = null) :
37
39
  CodedException(message ?: "Unexpected expo-video cache error", cause)
40
+
41
+ internal class NowPlayingException(message: String?, cause: Throwable? = null) :
42
+ CodedException(message ?: "Unexpected expo-video now playing exception", cause)
43
+
44
+ internal fun getPlaybackServiceErrorMessage(message: String?, tip: String = defaultServiceBindingTip) =
45
+ (message ?: "Expo-video playback service binder error") + ". $tip"
@@ -2,12 +2,14 @@ package expo.modules.video.playbackService
2
2
 
3
3
  import android.app.NotificationChannel
4
4
  import android.app.NotificationManager
5
+ import android.app.Service
5
6
  import android.content.Context
6
7
  import android.content.Intent
7
8
  import android.os.Binder
8
9
  import android.os.Build
9
10
  import android.os.Bundle
10
11
  import android.os.IBinder
12
+ import androidx.annotation.MainThread
11
13
  import androidx.annotation.OptIn
12
14
  import androidx.core.app.NotificationCompat
13
15
  import androidx.media3.common.util.UnstableApi
@@ -19,15 +21,27 @@ import androidx.media3.session.MediaStyleNotificationHelper
19
21
  import androidx.media3.session.SessionCommand
20
22
  import com.google.common.collect.ImmutableList
21
23
  import expo.modules.kotlin.AppContext
24
+ import expo.modules.kotlin.exception.Exceptions
22
25
  import expo.modules.video.R
26
+ import expo.modules.video.getPlaybackServiceErrorMessage
23
27
  import expo.modules.video.player.VideoPlayer
28
+ import kotlinx.coroutines.launch
29
+ import java.lang.ref.WeakReference
24
30
 
25
31
  class PlaybackServiceBinder(val service: ExpoVideoPlaybackService) : Binder()
26
32
 
27
33
  @OptIn(UnstableApi::class)
28
34
  class ExpoVideoPlaybackService : MediaSessionService() {
35
+ private lateinit var weakContext: WeakReference<AppContext>
36
+ var appContext: AppContext
37
+ get() = weakContext.get() ?: throw Exceptions.AppContextLost()
38
+ set(value) {
39
+ weakContext = WeakReference(value)
40
+ }
29
41
  private val mediaSessions = mutableMapOf<ExoPlayer, MediaSession>()
30
42
  private val binder = PlaybackServiceBinder(this)
43
+ private var mostRecentInteractionSession: MediaSession? = null
44
+ private var isForeground: Boolean = false
31
45
 
32
46
  private val commandSeekForward = SessionCommand(SEEK_FORWARD_COMMAND, Bundle.EMPTY)
33
47
  private val commandSeekBackward = SessionCommand(SEEK_BACKWARD_COMMAND, Bundle.EMPTY)
@@ -44,42 +58,50 @@ class ExpoVideoPlaybackService : MediaSessionService() {
44
58
  .build()
45
59
 
46
60
  fun setShowNotification(showNotification: Boolean, player: ExoPlayer) {
47
- val sessionExtras = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
48
- mediaSessions[player]?.sessionExtras?.deepCopy() ?: Bundle()
49
- } else {
50
- Bundle()
51
- }
52
- sessionExtras.putBoolean(SESSION_SHOW_NOTIFICATION, showNotification)
53
- mediaSessions[player]?.let {
54
- it.sessionExtras = sessionExtras
55
- onUpdateNotification(it, showNotification)
61
+ appContext.mainQueue.launch {
62
+ val sessionExtras = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
63
+ mediaSessions[player]?.sessionExtras?.deepCopy() ?: Bundle()
64
+ } else {
65
+ Bundle()
66
+ }
67
+ sessionExtras.putBoolean(SESSION_SHOW_NOTIFICATION, showNotification)
68
+ mediaSessions[player]?.let {
69
+ it.sessionExtras = sessionExtras
70
+ onUpdateNotification(it, showNotification && player.playWhenReady)
71
+ }
56
72
  }
57
73
  }
58
74
 
59
75
  fun registerPlayer(videoPlayer: VideoPlayer) {
60
- val player = videoPlayer.player
61
- if (mediaSessions[player] != null) {
62
- return
63
- }
76
+ appContext.mainQueue.launch {
77
+ val player = videoPlayer.player
78
+ if (mediaSessions[player] != null) {
79
+ return@launch
80
+ }
64
81
 
65
- val mediaSession = MediaSession.Builder(this, player)
66
- .setId("ExpoVideoPlaybackService_${player.hashCode()}")
67
- .setCallback(VideoMediaSessionCallback())
68
- .setCustomLayout(ImmutableList.of(seekBackwardButton, seekForwardButton))
69
- .build()
82
+ val mediaSession = MediaSession.Builder(this@ExpoVideoPlaybackService, player)
83
+ .setId("ExpoVideoPlaybackService_${player.hashCode()}")
84
+ .setCallback(VideoMediaSessionCallback())
85
+ .setCustomLayout(ImmutableList.of(seekBackwardButton, seekForwardButton))
86
+ .build()
70
87
 
71
- mediaSessions[player] = mediaSession
72
- addSession(mediaSession)
73
- setShowNotification(videoPlayer.showNowPlayingNotification, player)
88
+ mediaSessions[player] = mediaSession
89
+ addSession(mediaSession)
90
+ setShowNotification(videoPlayer.showNowPlayingNotification, player)
91
+ }
74
92
  }
75
93
 
76
94
  fun unregisterPlayer(player: ExoPlayer) {
77
- hidePlayerNotification(player)
78
- val session = mediaSessions.remove(player)
79
- session?.release()
80
- if (mediaSessions.isEmpty()) {
81
- cleanup()
82
- stopSelf()
95
+ appContext.mainQueue.launch {
96
+ hidePlayerNotification(player)
97
+ val session = mediaSessions.remove(player)
98
+ session?.release()
99
+ if (mediaSessions.isEmpty()) {
100
+ cleanup()
101
+ stopSelf()
102
+ } else {
103
+ setMostRecentInteractionSession(findMostRecentInteractionSession())
104
+ }
83
105
  }
84
106
  }
85
107
 
@@ -89,15 +111,48 @@ class ExpoVideoPlaybackService : MediaSessionService() {
89
111
  }
90
112
 
91
113
  override fun onUpdateNotification(session: MediaSession, startInForegroundRequired: Boolean) {
92
- if (session.sessionExtras.getBoolean(SESSION_SHOW_NOTIFICATION, false)) {
93
- createNotification(session)
94
- } else {
95
- (session.player as? ExoPlayer)?.let {
96
- hidePlayerNotification(it)
114
+ appContext.mainQueue.launch {
115
+ if (startInForegroundRequired && session.wantsToShowNotification()) {
116
+ setMostRecentInteractionSession(session)
117
+ } else {
118
+ setMostRecentInteractionSession(findMostRecentInteractionSession())
97
119
  }
98
120
  }
99
121
  }
100
122
 
123
+ @MainThread
124
+ private fun setMostRecentInteractionSession(session: MediaSession?) {
125
+ if (session?.player?.playWhenReady == false) {
126
+ stopForeground(STOP_FOREGROUND_DETACH)
127
+ isForeground = false
128
+ }
129
+
130
+ if (mostRecentInteractionSession != session) {
131
+ hideAllNotifications()
132
+ }
133
+
134
+ mostRecentInteractionSession = session
135
+ session?.let {
136
+ createNotification(it, it.player.playWhenReady)
137
+ } ?: run {
138
+ stopForeground(STOP_FOREGROUND_REMOVE)
139
+ isForeground = false
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Finds a session that is playing media. And wants to show a notification
145
+ * If none exists, returns a session that wants to show a notification.
146
+ * The current mostRecentInteractionSession always has priority to reduce the number of unnecessary notification changes.
147
+ */
148
+ @MainThread
149
+ private fun findMostRecentInteractionSession(): MediaSession? {
150
+ val prioritizedSessions = (listOfNotNull(mostRecentInteractionSession) + mediaSessions.values.toList()).distinct()
151
+
152
+ return prioritizedSessions.firstOrNull { it.wantsToShowNotification() && it.player.playWhenReady }
153
+ ?: prioritizedSessions.firstOrNull { it.wantsToShowNotification() }
154
+ }
155
+
101
156
  override fun onTaskRemoved(rootIntent: Intent?) {
102
157
  cleanup()
103
158
  stopSelf()
@@ -112,7 +167,8 @@ class ExpoVideoPlaybackService : MediaSessionService() {
112
167
  super.onDestroy()
113
168
  }
114
169
 
115
- private fun createNotification(session: MediaSession) {
170
+ @MainThread
171
+ private fun createNotification(session: MediaSession, startInForegroundRequired: Boolean = false) {
116
172
  if (session.player.currentMediaItem == null) {
117
173
  return
118
174
  }
@@ -130,33 +186,55 @@ class ExpoVideoPlaybackService : MediaSessionService() {
130
186
  .setStyle(MediaStyleNotificationHelper.MediaStyle(session))
131
187
  .build()
132
188
 
133
- // Each of the players has it's own notification when playing.
134
- notificationManager.notify(session.player.hashCode(), notificationCompat)
189
+ val notificationId = session.player.hashCode()
190
+
191
+ if (startInForegroundRequired) {
192
+ try {
193
+ startForeground(notificationId, notificationCompat)
194
+ isForeground = true
195
+ } catch (e: Exception) {
196
+ appContext.jsLogger?.error(getPlaybackServiceErrorMessage("Failed to start the expo-video foreground service"), e)
197
+ }
198
+ } else {
199
+ notificationManager.notify(notificationId, notificationCompat)
200
+ }
135
201
  }
136
202
 
137
203
  private fun cleanup() {
138
- hideAllNotifications()
204
+ appContext.mainQueue.launch {
205
+ stopForeground(Service.STOP_FOREGROUND_REMOVE)
206
+ isForeground = false
207
+
208
+ hideAllNotifications()
209
+
210
+ val sessionsToRelease = mediaSessions.values.toList()
211
+ mediaSessions.clear()
212
+ for (session in sessionsToRelease) {
213
+ session.release()
214
+ }
139
215
 
140
- val sessionsToRelease = mediaSessions.values.toList()
141
- mediaSessions.clear()
142
- for (session in sessionsToRelease) {
143
- session.release()
216
+ val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
217
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
218
+ notificationManager.deleteNotificationChannel(CHANNEL_ID)
219
+ }
144
220
  }
145
221
  }
146
222
 
223
+ @MainThread
147
224
  private fun hidePlayerNotification(player: ExoPlayer) {
148
225
  val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
149
226
  notificationManager.cancel(player.hashCode())
150
227
  }
151
228
 
229
+ @MainThread
152
230
  private fun hideAllNotifications() {
153
231
  val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
154
232
  notificationManager.cancelAll()
155
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
156
- notificationManager.deleteNotificationChannel(CHANNEL_ID)
157
- }
158
233
  }
159
234
 
235
+ private fun MediaSession.wantsToShowNotification(): Boolean =
236
+ this.sessionExtras.getBoolean(SESSION_SHOW_NOTIFICATION, false)
237
+
160
238
  companion object {
161
239
  const val SEEK_FORWARD_COMMAND = "SEEK_FORWARD"
162
240
  const val SEEK_BACKWARD_COMMAND = "SEEK_REWIND"
@@ -164,7 +242,7 @@ class ExpoVideoPlaybackService : MediaSessionService() {
164
242
  const val SESSION_SHOW_NOTIFICATION = "showNotification"
165
243
  const val SEEK_INTERVAL_MS = 10000L
166
244
 
167
- fun startService(appContext: AppContext, context: Context, serviceConnection: PlaybackServiceConnection) {
245
+ fun startService(appContext: AppContext, context: Context, serviceConnection: PlaybackServiceConnection): Boolean {
168
246
  appContext.reactContext?.apply {
169
247
  val intent = Intent(context, ExpoVideoPlaybackService::class.java)
170
248
  intent.action = SERVICE_INTERFACE
@@ -172,13 +250,14 @@ class ExpoVideoPlaybackService : MediaSessionService() {
172
250
  startService(intent)
173
251
 
174
252
  val flags = if (Build.VERSION.SDK_INT >= 29) {
175
- BIND_AUTO_CREATE or Context.BIND_INCLUDE_CAPABILITIES
253
+ BIND_AUTO_CREATE or BIND_INCLUDE_CAPABILITIES
176
254
  } else {
177
255
  BIND_AUTO_CREATE
178
256
  }
179
257
 
180
- bindService(intent, serviceConnection, flags)
258
+ return bindService(intent, serviceConnection, flags)
181
259
  }
260
+ return false
182
261
  }
183
262
  }
184
263
  }
@@ -3,37 +3,60 @@ package expo.modules.video.playbackService
3
3
  import android.content.ComponentName
4
4
  import android.content.ServiceConnection
5
5
  import android.os.IBinder
6
- import android.util.Log
7
6
  import androidx.annotation.OptIn
8
7
  import androidx.media3.common.util.UnstableApi
8
+ import expo.modules.kotlin.AppContext
9
+ import expo.modules.kotlin.exception.Exceptions
10
+ import expo.modules.video.getPlaybackServiceErrorMessage
9
11
  import expo.modules.video.player.VideoPlayer
10
12
  import java.lang.ref.WeakReference
11
13
 
12
14
  @OptIn(UnstableApi::class)
13
- class PlaybackServiceConnection(val player: WeakReference<VideoPlayer>) : ServiceConnection {
15
+ class PlaybackServiceConnection(val player: WeakReference<VideoPlayer>, appContext: AppContext) : ServiceConnection {
14
16
  var playbackServiceBinder: PlaybackServiceBinder? = null
17
+ private set
18
+ var isConnected = false
19
+ private set
20
+ private val _appContext = WeakReference(appContext)
21
+ private val appContext: AppContext
22
+ get() = _appContext.get() ?: throw Exceptions.AppContextLost()
15
23
 
16
24
  override fun onServiceConnected(componentName: ComponentName, binder: IBinder) {
17
25
  val player = player.get() ?: return
18
- playbackServiceBinder = binder as? PlaybackServiceBinder
19
- playbackServiceBinder?.service?.registerPlayer(player) ?: run {
20
- Log.w(
21
- "ExpoVideo",
22
- "Expo Video could not bind to the playback service. " +
23
- "This will cause issues with playback notifications and sustaining background playback."
26
+ val serviceBinder: PlaybackServiceBinder = binder as? PlaybackServiceBinder ?: run {
27
+ appContext.jsLogger?.error(
28
+ getPlaybackServiceErrorMessage("Expo-video could not bind to the playback service")
24
29
  )
30
+ return
25
31
  }
32
+
33
+ isConnected = true
34
+ playbackServiceBinder = serviceBinder
35
+ serviceBinder.service.appContext = appContext
36
+ serviceBinder.service.registerPlayer(player)
26
37
  }
27
38
 
28
39
  override fun onServiceDisconnected(componentName: ComponentName) {
29
40
  playbackServiceBinder = null
41
+ isConnected = false
42
+ }
43
+
44
+ override fun onBindingDied(name: ComponentName?) {
45
+ isConnected = false
46
+ appContext.jsLogger?.error(
47
+ getPlaybackServiceErrorMessage(
48
+ "Expo-video has lost connection to the playback service binder",
49
+ "This will cause issues with now playing notification and sustaining background playback."
50
+ )
51
+ )
52
+ super.onBindingDied(name)
30
53
  }
31
54
 
32
55
  override fun onNullBinding(componentName: ComponentName) {
33
- Log.w(
34
- "ExpoVideo",
35
- "Expo Video could not bind to the playback service. " +
36
- "This will cause issues with playback notifications and sustaining background playback."
56
+ isConnected = false
57
+ appContext.jsLogger?.error(
58
+ getPlaybackServiceErrorMessage("Expo Video could not bind to the playback service")
37
59
  )
60
+ super.onNullBinding(componentName)
38
61
  }
39
62
  }
@@ -23,6 +23,7 @@ import androidx.media3.exoplayer.analytics.AnalyticsListener
23
23
  import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
24
24
  import androidx.media3.ui.PlayerView
25
25
  import expo.modules.kotlin.AppContext
26
+ import expo.modules.kotlin.exception.Exceptions
26
27
  import expo.modules.kotlin.sharedobjects.SharedObject
27
28
  import expo.modules.video.IntervalUpdateClock
28
29
  import expo.modules.video.IntervalUpdateEmitter
@@ -31,6 +32,7 @@ import expo.modules.video.delegates.IgnoreSameSet
31
32
  import expo.modules.video.enums.AudioMixingMode
32
33
  import expo.modules.video.enums.PlayerStatus
33
34
  import expo.modules.video.enums.PlayerStatus.*
35
+ import expo.modules.video.getPlaybackServiceErrorMessage
34
36
  import expo.modules.video.playbackService.ExpoVideoPlaybackService
35
37
  import expo.modules.video.playbackService.PlaybackServiceConnection
36
38
  import expo.modules.video.records.BufferOptions
@@ -64,7 +66,7 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
64
66
  .build()
65
67
 
66
68
  private val firstFrameEventGenerator = createFirstFrameEventGenerator()
67
- val serviceConnection = PlaybackServiceConnection(WeakReference(this))
69
+ val serviceConnection = PlaybackServiceConnection(WeakReference(this), appContext)
68
70
  val intervalUpdateClock = IntervalUpdateClock(this)
69
71
 
70
72
  var playing by IgnoreSameSet(false) { new, old ->
@@ -81,6 +83,12 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
81
83
  var status: PlayerStatus = IDLE
82
84
  var requiresLinearPlayback = false
83
85
  var staysActiveInBackground = false
86
+ set(value) {
87
+ field = value
88
+ if (value) {
89
+ startPlaybackService()
90
+ }
91
+ }
84
92
  var preservesPitch = false
85
93
  set(preservesPitch) {
86
94
  field = preservesPitch
@@ -89,7 +97,7 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
89
97
  var showNowPlayingNotification = false
90
98
  set(value) {
91
99
  field = value
92
- serviceConnection.playbackServiceBinder?.service?.setShowNotification(value, this.player)
100
+ serviceSetShowNotification(value)
93
101
  }
94
102
  var duration = 0f
95
103
  var isLive = false
@@ -291,7 +299,6 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
291
299
  }
292
300
 
293
301
  init {
294
- ExpoVideoPlaybackService.startService(appContext, context, serviceConnection)
295
302
  player.addListener(playerListener)
296
303
  player.addAnalyticsListener(analyticsListener)
297
304
  VideoManager.registerVideoPlayer(this)
@@ -303,7 +310,9 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
303
310
  }
304
311
 
305
312
  override fun close() {
306
- appContext?.reactContext?.unbindService(serviceConnection)
313
+ if (serviceConnection.isConnected) {
314
+ appContext?.reactContext?.unbindService(serviceConnection)
315
+ }
307
316
  serviceConnection.playbackServiceBinder?.service?.unregisterPlayer(player)
308
317
  VideoManager.unregisterVideoPlayer(this@VideoPlayer)
309
318
 
@@ -398,6 +407,29 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
398
407
  isLive = false
399
408
  }
400
409
 
410
+ private fun startPlaybackService(): Boolean {
411
+ if (serviceConnection.playbackServiceBinder?.service != null) {
412
+ // PlaybackService already running.
413
+ return true
414
+ }
415
+ val appContext = appContext ?: throw Exceptions.AppContextLost()
416
+ val serviceStarted = ExpoVideoPlaybackService.startService(appContext, context, serviceConnection)
417
+
418
+ if (!serviceStarted) {
419
+ appContext.jsLogger?.error(
420
+ getPlaybackServiceErrorMessage("Expo-video has failed to bind with the playback service binder")
421
+ )
422
+ }
423
+ return serviceStarted
424
+ }
425
+
426
+ private fun serviceSetShowNotification(showNotification: Boolean) {
427
+ if (showNotification) {
428
+ startPlaybackService()
429
+ }
430
+ serviceConnection.playbackServiceBinder?.service?.setShowNotification(showNotification, this.player)
431
+ }
432
+
401
433
  fun addListener(videoPlayerListener: VideoPlayerListener) {
402
434
  if (listeners.all { it.get() != videoPlayerListener }) {
403
435
  listeners.add(WeakReference(videoPlayerListener))
@@ -115,6 +115,8 @@ export declare class VideoPlayer extends SharedObject<VideoPlayerEvents> {
115
115
  /**
116
116
  * Boolean value determining whether the player should show the now playing notification.
117
117
  *
118
+ * > **Note**: On Android, `supportsBackgroundPlayback` property of the [config plugin](#configuration-in-app-config)
119
+ * > has to be `true` for the now playing notification to work.
118
120
  * @default false
119
121
  * @platform android
120
122
  * @platform ios
@@ -122,6 +124,9 @@ export declare class VideoPlayer extends SharedObject<VideoPlayerEvents> {
122
124
  showNowPlayingNotification: boolean;
123
125
  /**
124
126
  * Determines whether the player should continue playing after the app enters the background.
127
+ *
128
+ * > **Note**: The `supportsBackgroundPlayback` property of the [config plugin](#configuration-in-app-config)
129
+ * > has to be `true` for the background playback to work.
125
130
  * @default false
126
131
  * @platform ios
127
132
  * @platform android
@@ -1 +1 @@
1
- {"version":3,"file":"VideoPlayer.types.d.ts","sourceRoot":"","sources":["../src/VideoPlayer.types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"VideoPlayer.types.d.ts","sourceRoot":"","sources":["../src/VideoPlayer.types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"VideoPlayer.types.js","sourceRoot":"","sources":["../src/VideoPlayer.types.ts"],"names":[],"mappings":"","sourcesContent":["import { SharedObject } from 'expo';\n\nimport { VideoPlayerEvents } from './VideoPlayerEvents.types';\nimport { VideoThumbnail } from './VideoThumbnail';\n\n/**\n * A class that represents an instance of the video player.\n */\nexport declare class VideoPlayer extends SharedObject<VideoPlayerEvents> {\n /**\n * Boolean value whether the player is currently playing.\n * > Use `play` and `pause` methods to control the playback.\n */\n readonly playing: boolean;\n\n /**\n * Determines whether the player should automatically replay after reaching the end of the video.\n * @default false\n */\n loop: boolean;\n\n /**\n * Determines whether the player should allow external playback.\n * @default true\n * @platform ios\n */\n allowsExternalPlayback: boolean;\n\n /**\n * Determines how the player will interact with other audio playing in the system.\n *\n * @default 'auto'\n * @platform android\n * @platform ios\n */\n audioMixingMode: AudioMixingMode;\n\n /**\n * Boolean value whether the player is currently muted.\n * Setting this property to `true`/`false` will mute/unmute the player.\n * @default false\n */\n muted: boolean;\n\n /**\n * Float value indicating the current playback time in seconds.\n *\n * If the player is not yet playing, this value indicates the time position\n * at which playback will begin once the `play()` method is called.\n *\n * Setting `currentTime` to a new value seeks the player to the given time.\n * Note that frame accurate seeking may incur additional decoding delay which can impact seeking performance.\n * Consider using the [`seekBy`](#seekbyseconds) function if the time does not have to be set precisely.\n */\n currentTime: number;\n\n /**\n * The exact timestamp when the currently displayed video frame was sent from the server,\n * based on the `EXT-X-PROGRAM-DATE-TIME` tag in the livestream metadata.\n * If this metadata is missing, this property will return `null`.\n * @platform android\n * @platform ios\n */\n readonly currentLiveTimestamp: number | null;\n\n /**\n * Float value indicating the latency of the live stream in seconds.\n * If a livestream doesn't have the required metadata, this will return `null`.\n * @platform android\n * @platform ios\n */\n readonly currentOffsetFromLive: number | null;\n\n /**\n * Float value indicating the time offset from the live in seconds.\n * @platform ios\n */\n targetOffsetFromLive: number;\n\n /**\n * Float value indicating the duration of the current video in seconds.\n */\n readonly duration: number;\n\n /**\n * Float value between `0` and `1.0` representing the current volume.\n * Muting the player doesn't affect the volume. In other words, when the player is muted, the volume is the same as\n * when unmuted. Similarly, setting the volume doesn't unmute the player.\n * @default 1.0\n */\n volume: number;\n\n /**\n * Boolean value indicating if the player should correct audio pitch when the playback speed changes.\n * @default true\n */\n preservesPitch: boolean;\n\n /**\n * Float value indicating the interval in seconds at which the player will emit the [`timeUpdate`](#videoplayerevents) event.\n * When the value is equal to `0`, the event will not be emitted.\n *\n * @default 0\n */\n timeUpdateEventInterval: number;\n\n /**\n * Float value between `0` and `16.0` indicating the current playback speed of the player.\n * @default 1.0\n */\n playbackRate: number;\n\n /**\n * Boolean indicating if the player should keep the screen on while playing.\n *\n * > On Android, this property has an effect only when a [`VideoView`](#videoview) is visible. If you want to keep the screen awake at all times use [`expo-keep-awake`](./keep-awake/).\n *\n * @default true\n * @platform android\n * @platform ios\n */\n keepScreenOnWhilePlaying: boolean;\n\n /**\n * Boolean value indicating whether the player is currently playing a live stream.\n */\n readonly isLive: boolean;\n\n /**\n * Indicates the current status of the player.\n */\n readonly status: VideoPlayerStatus;\n\n /**\n * Boolean value determining whether the player should show the now playing notification.\n *\n * @default false\n * @platform android\n * @platform ios\n */\n showNowPlayingNotification: boolean;\n\n /**\n * Determines whether the player should continue playing after the app enters the background.\n * @default false\n * @platform ios\n * @platform android\n */\n staysActiveInBackground: boolean;\n\n /**\n * Float value indicating how far the player has buffered the video in seconds.\n *\n * This value is 0 when the player has not buffered up to the current playback time.\n * When it's impossible to determine the buffer state (for example, when the player isn't playing any media), this value is -1.\n */\n readonly bufferedPosition: number;\n\n /**\n * Specifies buffer options which will be used by the player when buffering the video.\n *\n * > You should provide a `BufferOptions` object when setting this property. Setting individual buffer properties is not supported.\n * @platform android\n * @platform ios\n */\n bufferOptions: BufferOptions;\n\n /**\n * Specifies the subtitle track which is currently displayed by the player. `null` when no subtitles are displayed.\n *\n * > To ensure a valid subtitle track, always assign one of the subtitle tracks from the [`availableSubtitleTracks`](#availablesubtitletracks) array.\n *\n * @default null\n * @platform android\n * @platform ios\n */\n subtitleTrack: SubtitleTrack | null;\n\n /**\n * Specifies the audio track currently played by the player. `null` when no audio is played.\n *\n * @default null\n * @platform android\n * @platform ios\n */\n audioTrack: AudioTrack | null;\n\n /**\n * An array of audio tracks available for the current video.\n *\n * @platform android\n * @platform ios\n */\n readonly availableAudioTracks: AudioTrack[];\n\n /**\n * An array of subtitle tracks available for the current video.\n *\n * @platform android\n * @platform ios\n */\n readonly availableSubtitleTracks: SubtitleTrack[];\n\n /**\n * Specifies the video track currently played by the player. `null` when no video is displayed.\n *\n * @default null\n * @platform android\n * @platform ios\n */\n readonly videoTrack: VideoTrack | null;\n\n /**\n * An array of video tracks available for the current video.\n *\n * > On iOS, when using a HLS source, make sure that the uri contains `.m3u8` extension or that the [`contentType`](#contenttype) property of the [`VideoSource`](#videosource) has been set to `'hls'`. Otherwise, the video tracks will not be available.\n *\n * @platform android\n * @platform ios\n */\n readonly availableVideoTracks: VideoTrack[];\n\n /**\n * Indicates whether the player is currently playing back the media to an external device via AirPlay.\n *\n * @platform ios\n */\n readonly isExternalPlaybackActive: boolean;\n\n /**\n * Initializes a new video player instance with the given source.\n *\n * @param source The source of the video to be played.\n * @param useSynchronousReplace Optional parameter, when `true` `source` from the first parameter will be loaded on the main thread.\n * @hidden\n */\n constructor(source: VideoSource, useSynchronousReplace?: boolean);\n\n /**\n * Resumes the player.\n */\n play(): void;\n\n /**\n * Pauses the player.\n */\n pause(): void;\n\n /**\n * Replaces the current source with a new one.\n *\n * > On iOS, this method loads the asset data synchronously on the UI thread and can block it for extended periods of time.\n * > Use `replaceAsync` to load the asset asynchronously and avoid UI lags.\n *\n * > This method will be deprecated in the future.\n */\n replace(source: VideoSource, disableWarning?: boolean): void;\n\n /**\n * Replaces the current source with a new one, while offloading loading of the asset to a different thread.\n *\n * > On Android and Web, this method is equivalent to `replace`.\n */\n replaceAsync(source: VideoSource): Promise<void>;\n\n /**\n * Seeks the playback by the given number of seconds. The time to which the player seeks may differ from the specified requested time for efficiency,\n * depending on the encoding and what is currently buffered by the player. Use this function to implement playback controls that seek by specific amount of time,\n * in which case, the actual time usually does not have to be precise. For frame accurate seeking, use the [`currentTime`](#currenttime) property.\n */\n seekBy(seconds: number): void;\n\n /**\n * Seeks the playback to the beginning.\n */\n replay(): void;\n\n /**\n * Generates thumbnails from the currently played asset. The thumbnails are references to native images,\n * thus they can be used as a source of the `Image` component from `expo-image`.\n * @platform android\n * @platform ios\n */\n generateThumbnailsAsync(\n times: number | number[],\n options?: VideoThumbnailOptions\n ): Promise<VideoThumbnail[]>;\n}\n\n/**\n * Additional options for video thumbnails generation.\n */\nexport type VideoThumbnailOptions = {\n /**\n * If provided, the generated thumbnail will not exceed this width in pixels, preserving its aspect ratio.\n * @platform android\n * @platform ios\n */\n maxWidth?: number;\n\n /**\n * If provided, the generated thumbnail will not exceed this height in pixels, preserving its aspect ratio.\n * @platform android\n * @platform ios\n */\n maxHeight?: number;\n};\n\n/**\n * Describes the current status of the player.\n * - `idle`: The player is not playing or loading any videos.\n * - `loading`: The player is loading video data from the provided source\n * - `readyToPlay`: The player has loaded enough data to start playing or to continue playback.\n * - `error`: The player has encountered an error while loading or playing the video.\n */\nexport type VideoPlayerStatus = 'idle' | 'loading' | 'readyToPlay' | 'error';\n\nexport type VideoSource =\n | string\n | number\n | null\n | {\n /**\n * The URI of the video.\n *\n * This property is exclusive with the `assetId` property. When both are present, the `assetId` will be ignored.\n */\n uri?: string;\n\n /**\n * The asset ID of a local video asset, acquired with the `require` function.\n * This property is exclusive with the `uri` property. When both are present, the `assetId` will be ignored.\n */\n assetId?: number;\n\n /**\n * Specifies the DRM options which will be used by the player while loading the video.\n */\n drm?: DRMOptions;\n\n /**\n * Specifies information which will be displayed in the now playing notification.\n * When undefined the player will display information contained in the video metadata.\n * @platform android\n * @platform ios\n */\n metadata?: VideoMetadata;\n\n /**\n * Specifies headers sent with the video request.\n * > For DRM license headers use the `headers` field of [`DRMOptions`](#drmoptions).\n * @platform android\n * @platform ios\n */\n headers?: Record<string, string>;\n\n /**\n * Specifies whether the player should use caching for the video.\n * > Due to platform limitations, the cache cannot be used with HLS video sources on iOS. Caching DRM-protected videos is not supported on Android and iOS.\n * @default false\n * @platform android\n * @platform ios\n */\n useCaching?: boolean;\n\n /**\n * Specifies the content type of the video source. When set to `'auto'`, the player will try to automatically determine the content type.\n *\n * You should use this property when playing HLS, SmoothStreaming or DASH videos from an uri, which does not contain a standardized extension for the corresponding media type.\n * @default 'auto'\n * @platform android\n * @platform ios\n */\n contentType?: ContentType;\n };\n\n/**\n * Contains information about any errors that the player encountered during the playback\n */\nexport type PlayerError = {\n message: string;\n};\n\n/**\n * Contains information that will be displayed in the now playing notification when the video is playing.\n * @platform android\n * @platform ios\n */\nexport type VideoMetadata = {\n /**\n * The title of the video.\n * @platform android\n * @platform ios\n */\n title?: string;\n /**\n * Secondary text that will be displayed under the title.\n * @platform android\n * @platform ios\n */\n artist?: string;\n /**\n * The uri of the video artwork.\n * @platform android\n * @platform ios\n */\n artwork?: string;\n};\n\n/**\n * Specifies which type of DRM to use:\n * - Android supports ClearKey, PlayReady and Widevine.\n * - iOS supports FairPlay.\n */\nexport type DRMType = 'clearkey' | 'fairplay' | 'playready' | 'widevine';\n\n/**\n * Specifies DRM options which will be used by the player while loading the video.\n */\nexport type DRMOptions = {\n /**\n * Determines which type of DRM to use.\n */\n type: DRMType;\n\n /**\n * Determines the license server URL.\n */\n licenseServer: string;\n\n /**\n * Determines headers sent to the license server on license requests.\n */\n headers?: Record<string, string>;\n\n /**\n * Specifies whether the DRM is a multi-key DRM.\n * @platform android\n */\n multiKey?: boolean;\n\n /**\n * Specifies the content ID of the stream.\n * @platform ios\n */\n contentId?: string;\n\n /**\n * Specifies the certificate URL for the FairPlay DRM.\n * @platform ios\n */\n certificateUrl?: string;\n\n /**\n * Specifies the base64 encoded certificate data for the FairPlay DRM.\n * When this property is set, the `certificateUrl` property is ignored.\n * @platform ios\n */\n base64CertificateData?: string;\n};\n\n/**\n * Specifies buffer options which will be used by the player when buffering the video.\n *\n * @platform android\n * @platform ios\n */\nexport type BufferOptions = {\n /**\n * The duration in seconds which determines how much media the player should buffer ahead of the current playback time.\n *\n * On iOS when set to `0` the player will automatically decide appropriate buffer duration.\n *\n * Equivalent to [`AVPlayerItem.preferredForwardBufferDuration`](https://developer.apple.com/documentation/avfoundation/avplayeritem/1643630-preferredforwardbufferduration).\n * @default Android: 20, iOS: 0\n * @platform android\n * @platform ios\n */\n readonly preferredForwardBufferDuration?: number;\n\n /**\n * A Boolean value that indicates whether the player should automatically delay playback in order to minimize stalling.\n *\n * Equivalent to [`AVPlayer.automaticallyWaitsToMinimizeStalling`](https://developer.apple.com/documentation/avfoundation/avplayer/1643482-automaticallywaitstominimizestal).\n * @default true\n * @platform ios\n */\n readonly waitsToMinimizeStalling?: boolean;\n\n /**\n * Minimum duration of the buffer in seconds required to continue playing after the player has been paused or started buffering.\n *\n * > This property will be ignored if `preferredForwardBufferDuration` is lower.\n * @default 2\n * @platform android\n */\n readonly minBufferForPlayback?: number;\n\n /**\n * The maximum number of bytes that the player can buffer from the network.\n * When 0 the player will automatically decide appropriate buffer size.\n *\n * @default 0\n * @platform android\n */\n readonly maxBufferBytes?: number | null;\n\n /**\n * A Boolean value which determines whether the player should prioritize time over size when buffering media.\n *\n * @default false\n * @platform android\n */\n readonly prioritizeTimeOverSizeThreshold?: boolean;\n};\n\n/**\n * Specifies the content type of the source.\n *\n * - `auto`: The player will automatically determine the content type of the video.\n * - `progressive`: The player will use progressive download content type. This is the default `ContentType` when the uri does not contain an extension.\n * - `hls`: The player will use HLS content type.\n * - `dash`: The player will use DASH content type (Android-only).\n * - `smoothStreaming`: The player will use SmoothStreaming content type (Android-only).\n *\n * @default `auto`\n */\nexport type ContentType = 'auto' | 'progressive' | 'hls' | 'dash' | 'smoothStreaming';\n\n/**\n * Specifies the audio mode that the player should use. Audio mode is set on per-app basis, if there are multiple players playing and\n * have different a `AudioMode` specified, the highest priority mode will be used. Priority order: 'doNotMix' > 'auto' > 'duckOthers' > 'mixWithOthers'.\n *\n * - `mixWithOthers`: The player will mix its audio output with other apps.\n * - `duckOthers`: The player will lower the volume of other apps if any of the active players is outputting audio.\n * - `auto`: The player will allow other apps to keep playing audio only when it is muted. On iOS it will always interrupt other apps when `showNowPlayingNotification` is `true` due to system requirements.\n * - `doNotMix`: The player will pause playback in other apps, even when it's muted.\n *\n * > On iOS, the Now Playing notification is dependent on the audio mode. If the audio mode is different from `doNotMix` or `auto` this feature will not work.\n */\nexport type AudioMixingMode = 'mixWithOthers' | 'duckOthers' | 'auto' | 'doNotMix';\n\nexport type SubtitleTrack = {\n /**\n * A string used by `expo-video` to identify the subtitle track.\n *\n * @platform android\n */\n id: string;\n\n /**\n * Language of the subtitle track. For example, `en`, `pl`, `de`.\n */\n language: string;\n\n /**\n * Label of the subtitle track in the language of the device.\n */\n label: string;\n};\n\n/**\n * Specifies a VideoTrack loaded from a [`VideoSource`](#videosource).\n */\nexport type VideoTrack = {\n /**\n * The id of the video track.\n *\n * > This field is platform-specific and may return different depending on the operating system.\n */\n id: string;\n\n /**\n * Size of the video track.\n */\n size: VideoSize;\n\n /**\n * MimeType of the video track or null if unknown.\n */\n mimeType: string | null;\n\n /**\n * Indicates whether the video track format is supported by the device.\n *\n * @platform android\n */\n isSupported: boolean;\n\n /**\n * Specifies the bitrate in bits per second. This is the peak bitrate if known, or else the average bitrate if known, or else null.\n */\n bitrate: number | null;\n\n /**\n * Specifies the frame rate of the video track in frames per second.\n */\n frameRate: number | null;\n};\n\n/**\n * Specifies the size of a video track.\n */\nexport type VideoSize = {\n /**\n * Width of the video track in pixels.\n */\n width: number;\n /**\n * Height of the video track in pixels.\n */\n height: number;\n};\n\nexport type AudioTrack = {\n /**\n * A string used by expo-video to identify the audio track.\n * @platform android\n */\n id: string;\n\n /**\n * Language of the audio track. For example, 'en', 'pl', 'de'.\n */\n language: string;\n\n /**\n * Label of the audio track in the language of the device.\n */\n label: string;\n};\n"]}
1
+ {"version":3,"file":"VideoPlayer.types.js","sourceRoot":"","sources":["../src/VideoPlayer.types.ts"],"names":[],"mappings":"","sourcesContent":["import { SharedObject } from 'expo';\n\nimport { VideoPlayerEvents } from './VideoPlayerEvents.types';\nimport { VideoThumbnail } from './VideoThumbnail';\n\n/**\n * A class that represents an instance of the video player.\n */\nexport declare class VideoPlayer extends SharedObject<VideoPlayerEvents> {\n /**\n * Boolean value whether the player is currently playing.\n * > Use `play` and `pause` methods to control the playback.\n */\n readonly playing: boolean;\n\n /**\n * Determines whether the player should automatically replay after reaching the end of the video.\n * @default false\n */\n loop: boolean;\n\n /**\n * Determines whether the player should allow external playback.\n * @default true\n * @platform ios\n */\n allowsExternalPlayback: boolean;\n\n /**\n * Determines how the player will interact with other audio playing in the system.\n *\n * @default 'auto'\n * @platform android\n * @platform ios\n */\n audioMixingMode: AudioMixingMode;\n\n /**\n * Boolean value whether the player is currently muted.\n * Setting this property to `true`/`false` will mute/unmute the player.\n * @default false\n */\n muted: boolean;\n\n /**\n * Float value indicating the current playback time in seconds.\n *\n * If the player is not yet playing, this value indicates the time position\n * at which playback will begin once the `play()` method is called.\n *\n * Setting `currentTime` to a new value seeks the player to the given time.\n * Note that frame accurate seeking may incur additional decoding delay which can impact seeking performance.\n * Consider using the [`seekBy`](#seekbyseconds) function if the time does not have to be set precisely.\n */\n currentTime: number;\n\n /**\n * The exact timestamp when the currently displayed video frame was sent from the server,\n * based on the `EXT-X-PROGRAM-DATE-TIME` tag in the livestream metadata.\n * If this metadata is missing, this property will return `null`.\n * @platform android\n * @platform ios\n */\n readonly currentLiveTimestamp: number | null;\n\n /**\n * Float value indicating the latency of the live stream in seconds.\n * If a livestream doesn't have the required metadata, this will return `null`.\n * @platform android\n * @platform ios\n */\n readonly currentOffsetFromLive: number | null;\n\n /**\n * Float value indicating the time offset from the live in seconds.\n * @platform ios\n */\n targetOffsetFromLive: number;\n\n /**\n * Float value indicating the duration of the current video in seconds.\n */\n readonly duration: number;\n\n /**\n * Float value between `0` and `1.0` representing the current volume.\n * Muting the player doesn't affect the volume. In other words, when the player is muted, the volume is the same as\n * when unmuted. Similarly, setting the volume doesn't unmute the player.\n * @default 1.0\n */\n volume: number;\n\n /**\n * Boolean value indicating if the player should correct audio pitch when the playback speed changes.\n * @default true\n */\n preservesPitch: boolean;\n\n /**\n * Float value indicating the interval in seconds at which the player will emit the [`timeUpdate`](#videoplayerevents) event.\n * When the value is equal to `0`, the event will not be emitted.\n *\n * @default 0\n */\n timeUpdateEventInterval: number;\n\n /**\n * Float value between `0` and `16.0` indicating the current playback speed of the player.\n * @default 1.0\n */\n playbackRate: number;\n\n /**\n * Boolean indicating if the player should keep the screen on while playing.\n *\n * > On Android, this property has an effect only when a [`VideoView`](#videoview) is visible. If you want to keep the screen awake at all times use [`expo-keep-awake`](./keep-awake/).\n *\n * @default true\n * @platform android\n * @platform ios\n */\n keepScreenOnWhilePlaying: boolean;\n\n /**\n * Boolean value indicating whether the player is currently playing a live stream.\n */\n readonly isLive: boolean;\n\n /**\n * Indicates the current status of the player.\n */\n readonly status: VideoPlayerStatus;\n\n /**\n * Boolean value determining whether the player should show the now playing notification.\n *\n * > **Note**: On Android, `supportsBackgroundPlayback` property of the [config plugin](#configuration-in-app-config)\n * > has to be `true` for the now playing notification to work.\n * @default false\n * @platform android\n * @platform ios\n */\n showNowPlayingNotification: boolean;\n\n /**\n * Determines whether the player should continue playing after the app enters the background.\n *\n * > **Note**: The `supportsBackgroundPlayback` property of the [config plugin](#configuration-in-app-config)\n * > has to be `true` for the background playback to work.\n * @default false\n * @platform ios\n * @platform android\n */\n staysActiveInBackground: boolean;\n\n /**\n * Float value indicating how far the player has buffered the video in seconds.\n *\n * This value is 0 when the player has not buffered up to the current playback time.\n * When it's impossible to determine the buffer state (for example, when the player isn't playing any media), this value is -1.\n */\n readonly bufferedPosition: number;\n\n /**\n * Specifies buffer options which will be used by the player when buffering the video.\n *\n * > You should provide a `BufferOptions` object when setting this property. Setting individual buffer properties is not supported.\n * @platform android\n * @platform ios\n */\n bufferOptions: BufferOptions;\n\n /**\n * Specifies the subtitle track which is currently displayed by the player. `null` when no subtitles are displayed.\n *\n * > To ensure a valid subtitle track, always assign one of the subtitle tracks from the [`availableSubtitleTracks`](#availablesubtitletracks) array.\n *\n * @default null\n * @platform android\n * @platform ios\n */\n subtitleTrack: SubtitleTrack | null;\n\n /**\n * Specifies the audio track currently played by the player. `null` when no audio is played.\n *\n * @default null\n * @platform android\n * @platform ios\n */\n audioTrack: AudioTrack | null;\n\n /**\n * An array of audio tracks available for the current video.\n *\n * @platform android\n * @platform ios\n */\n readonly availableAudioTracks: AudioTrack[];\n\n /**\n * An array of subtitle tracks available for the current video.\n *\n * @platform android\n * @platform ios\n */\n readonly availableSubtitleTracks: SubtitleTrack[];\n\n /**\n * Specifies the video track currently played by the player. `null` when no video is displayed.\n *\n * @default null\n * @platform android\n * @platform ios\n */\n readonly videoTrack: VideoTrack | null;\n\n /**\n * An array of video tracks available for the current video.\n *\n * > On iOS, when using a HLS source, make sure that the uri contains `.m3u8` extension or that the [`contentType`](#contenttype) property of the [`VideoSource`](#videosource) has been set to `'hls'`. Otherwise, the video tracks will not be available.\n *\n * @platform android\n * @platform ios\n */\n readonly availableVideoTracks: VideoTrack[];\n\n /**\n * Indicates whether the player is currently playing back the media to an external device via AirPlay.\n *\n * @platform ios\n */\n readonly isExternalPlaybackActive: boolean;\n\n /**\n * Initializes a new video player instance with the given source.\n *\n * @param source The source of the video to be played.\n * @param useSynchronousReplace Optional parameter, when `true` `source` from the first parameter will be loaded on the main thread.\n * @hidden\n */\n constructor(source: VideoSource, useSynchronousReplace?: boolean);\n\n /**\n * Resumes the player.\n */\n play(): void;\n\n /**\n * Pauses the player.\n */\n pause(): void;\n\n /**\n * Replaces the current source with a new one.\n *\n * > On iOS, this method loads the asset data synchronously on the UI thread and can block it for extended periods of time.\n * > Use `replaceAsync` to load the asset asynchronously and avoid UI lags.\n *\n * > This method will be deprecated in the future.\n */\n replace(source: VideoSource, disableWarning?: boolean): void;\n\n /**\n * Replaces the current source with a new one, while offloading loading of the asset to a different thread.\n *\n * > On Android and Web, this method is equivalent to `replace`.\n */\n replaceAsync(source: VideoSource): Promise<void>;\n\n /**\n * Seeks the playback by the given number of seconds. The time to which the player seeks may differ from the specified requested time for efficiency,\n * depending on the encoding and what is currently buffered by the player. Use this function to implement playback controls that seek by specific amount of time,\n * in which case, the actual time usually does not have to be precise. For frame accurate seeking, use the [`currentTime`](#currenttime) property.\n */\n seekBy(seconds: number): void;\n\n /**\n * Seeks the playback to the beginning.\n */\n replay(): void;\n\n /**\n * Generates thumbnails from the currently played asset. The thumbnails are references to native images,\n * thus they can be used as a source of the `Image` component from `expo-image`.\n * @platform android\n * @platform ios\n */\n generateThumbnailsAsync(\n times: number | number[],\n options?: VideoThumbnailOptions\n ): Promise<VideoThumbnail[]>;\n}\n\n/**\n * Additional options for video thumbnails generation.\n */\nexport type VideoThumbnailOptions = {\n /**\n * If provided, the generated thumbnail will not exceed this width in pixels, preserving its aspect ratio.\n * @platform android\n * @platform ios\n */\n maxWidth?: number;\n\n /**\n * If provided, the generated thumbnail will not exceed this height in pixels, preserving its aspect ratio.\n * @platform android\n * @platform ios\n */\n maxHeight?: number;\n};\n\n/**\n * Describes the current status of the player.\n * - `idle`: The player is not playing or loading any videos.\n * - `loading`: The player is loading video data from the provided source\n * - `readyToPlay`: The player has loaded enough data to start playing or to continue playback.\n * - `error`: The player has encountered an error while loading or playing the video.\n */\nexport type VideoPlayerStatus = 'idle' | 'loading' | 'readyToPlay' | 'error';\n\nexport type VideoSource =\n | string\n | number\n | null\n | {\n /**\n * The URI of the video.\n *\n * This property is exclusive with the `assetId` property. When both are present, the `assetId` will be ignored.\n */\n uri?: string;\n\n /**\n * The asset ID of a local video asset, acquired with the `require` function.\n * This property is exclusive with the `uri` property. When both are present, the `assetId` will be ignored.\n */\n assetId?: number;\n\n /**\n * Specifies the DRM options which will be used by the player while loading the video.\n */\n drm?: DRMOptions;\n\n /**\n * Specifies information which will be displayed in the now playing notification.\n * When undefined the player will display information contained in the video metadata.\n * @platform android\n * @platform ios\n */\n metadata?: VideoMetadata;\n\n /**\n * Specifies headers sent with the video request.\n * > For DRM license headers use the `headers` field of [`DRMOptions`](#drmoptions).\n * @platform android\n * @platform ios\n */\n headers?: Record<string, string>;\n\n /**\n * Specifies whether the player should use caching for the video.\n * > Due to platform limitations, the cache cannot be used with HLS video sources on iOS. Caching DRM-protected videos is not supported on Android and iOS.\n * @default false\n * @platform android\n * @platform ios\n */\n useCaching?: boolean;\n\n /**\n * Specifies the content type of the video source. When set to `'auto'`, the player will try to automatically determine the content type.\n *\n * You should use this property when playing HLS, SmoothStreaming or DASH videos from an uri, which does not contain a standardized extension for the corresponding media type.\n * @default 'auto'\n * @platform android\n * @platform ios\n */\n contentType?: ContentType;\n };\n\n/**\n * Contains information about any errors that the player encountered during the playback\n */\nexport type PlayerError = {\n message: string;\n};\n\n/**\n * Contains information that will be displayed in the now playing notification when the video is playing.\n * @platform android\n * @platform ios\n */\nexport type VideoMetadata = {\n /**\n * The title of the video.\n * @platform android\n * @platform ios\n */\n title?: string;\n /**\n * Secondary text that will be displayed under the title.\n * @platform android\n * @platform ios\n */\n artist?: string;\n /**\n * The uri of the video artwork.\n * @platform android\n * @platform ios\n */\n artwork?: string;\n};\n\n/**\n * Specifies which type of DRM to use:\n * - Android supports ClearKey, PlayReady and Widevine.\n * - iOS supports FairPlay.\n */\nexport type DRMType = 'clearkey' | 'fairplay' | 'playready' | 'widevine';\n\n/**\n * Specifies DRM options which will be used by the player while loading the video.\n */\nexport type DRMOptions = {\n /**\n * Determines which type of DRM to use.\n */\n type: DRMType;\n\n /**\n * Determines the license server URL.\n */\n licenseServer: string;\n\n /**\n * Determines headers sent to the license server on license requests.\n */\n headers?: Record<string, string>;\n\n /**\n * Specifies whether the DRM is a multi-key DRM.\n * @platform android\n */\n multiKey?: boolean;\n\n /**\n * Specifies the content ID of the stream.\n * @platform ios\n */\n contentId?: string;\n\n /**\n * Specifies the certificate URL for the FairPlay DRM.\n * @platform ios\n */\n certificateUrl?: string;\n\n /**\n * Specifies the base64 encoded certificate data for the FairPlay DRM.\n * When this property is set, the `certificateUrl` property is ignored.\n * @platform ios\n */\n base64CertificateData?: string;\n};\n\n/**\n * Specifies buffer options which will be used by the player when buffering the video.\n *\n * @platform android\n * @platform ios\n */\nexport type BufferOptions = {\n /**\n * The duration in seconds which determines how much media the player should buffer ahead of the current playback time.\n *\n * On iOS when set to `0` the player will automatically decide appropriate buffer duration.\n *\n * Equivalent to [`AVPlayerItem.preferredForwardBufferDuration`](https://developer.apple.com/documentation/avfoundation/avplayeritem/1643630-preferredforwardbufferduration).\n * @default Android: 20, iOS: 0\n * @platform android\n * @platform ios\n */\n readonly preferredForwardBufferDuration?: number;\n\n /**\n * A Boolean value that indicates whether the player should automatically delay playback in order to minimize stalling.\n *\n * Equivalent to [`AVPlayer.automaticallyWaitsToMinimizeStalling`](https://developer.apple.com/documentation/avfoundation/avplayer/1643482-automaticallywaitstominimizestal).\n * @default true\n * @platform ios\n */\n readonly waitsToMinimizeStalling?: boolean;\n\n /**\n * Minimum duration of the buffer in seconds required to continue playing after the player has been paused or started buffering.\n *\n * > This property will be ignored if `preferredForwardBufferDuration` is lower.\n * @default 2\n * @platform android\n */\n readonly minBufferForPlayback?: number;\n\n /**\n * The maximum number of bytes that the player can buffer from the network.\n * When 0 the player will automatically decide appropriate buffer size.\n *\n * @default 0\n * @platform android\n */\n readonly maxBufferBytes?: number | null;\n\n /**\n * A Boolean value which determines whether the player should prioritize time over size when buffering media.\n *\n * @default false\n * @platform android\n */\n readonly prioritizeTimeOverSizeThreshold?: boolean;\n};\n\n/**\n * Specifies the content type of the source.\n *\n * - `auto`: The player will automatically determine the content type of the video.\n * - `progressive`: The player will use progressive download content type. This is the default `ContentType` when the uri does not contain an extension.\n * - `hls`: The player will use HLS content type.\n * - `dash`: The player will use DASH content type (Android-only).\n * - `smoothStreaming`: The player will use SmoothStreaming content type (Android-only).\n *\n * @default `auto`\n */\nexport type ContentType = 'auto' | 'progressive' | 'hls' | 'dash' | 'smoothStreaming';\n\n/**\n * Specifies the audio mode that the player should use. Audio mode is set on per-app basis, if there are multiple players playing and\n * have different a `AudioMode` specified, the highest priority mode will be used. Priority order: 'doNotMix' > 'auto' > 'duckOthers' > 'mixWithOthers'.\n *\n * - `mixWithOthers`: The player will mix its audio output with other apps.\n * - `duckOthers`: The player will lower the volume of other apps if any of the active players is outputting audio.\n * - `auto`: The player will allow other apps to keep playing audio only when it is muted. On iOS it will always interrupt other apps when `showNowPlayingNotification` is `true` due to system requirements.\n * - `doNotMix`: The player will pause playback in other apps, even when it's muted.\n *\n * > On iOS, the Now Playing notification is dependent on the audio mode. If the audio mode is different from `doNotMix` or `auto` this feature will not work.\n */\nexport type AudioMixingMode = 'mixWithOthers' | 'duckOthers' | 'auto' | 'doNotMix';\n\nexport type SubtitleTrack = {\n /**\n * A string used by `expo-video` to identify the subtitle track.\n *\n * @platform android\n */\n id: string;\n\n /**\n * Language of the subtitle track. For example, `en`, `pl`, `de`.\n */\n language: string;\n\n /**\n * Label of the subtitle track in the language of the device.\n */\n label: string;\n};\n\n/**\n * Specifies a VideoTrack loaded from a [`VideoSource`](#videosource).\n */\nexport type VideoTrack = {\n /**\n * The id of the video track.\n *\n * > This field is platform-specific and may return different depending on the operating system.\n */\n id: string;\n\n /**\n * Size of the video track.\n */\n size: VideoSize;\n\n /**\n * MimeType of the video track or null if unknown.\n */\n mimeType: string | null;\n\n /**\n * Indicates whether the video track format is supported by the device.\n *\n * @platform android\n */\n isSupported: boolean;\n\n /**\n * Specifies the bitrate in bits per second. This is the peak bitrate if known, or else the average bitrate if known, or else null.\n */\n bitrate: number | null;\n\n /**\n * Specifies the frame rate of the video track in frames per second.\n */\n frameRate: number | null;\n};\n\n/**\n * Specifies the size of a video track.\n */\nexport type VideoSize = {\n /**\n * Width of the video track in pixels.\n */\n width: number;\n /**\n * Height of the video track in pixels.\n */\n height: number;\n};\n\nexport type AudioTrack = {\n /**\n * A string used by expo-video to identify the audio track.\n * @platform android\n */\n id: string;\n\n /**\n * Language of the audio track. For example, 'en', 'pl', 'de'.\n */\n language: string;\n\n /**\n * Label of the audio track in the language of the device.\n */\n label: string;\n};\n"]}
@@ -8,7 +8,7 @@
8
8
  "publication": {
9
9
  "groupId": "host.exp.exponent",
10
10
  "artifactId": "expo.modules.video",
11
- "version": "3.0.8",
11
+ "version": "3.0.9",
12
12
  "repository": "local-maven-repo"
13
13
  }
14
14
  }
@@ -0,0 +1 @@
1
+ 5efae1439855a664269c02cbb19144477c94a4ac3af7dbd6a7874739d851cd4b
@@ -0,0 +1 @@
1
+ f04da6fb2f5c1ba96f029687f46e31c261026c814121a81cdc257af33fa1383b5de77a8862123e632c93888bfce716edcc2acdd0d07460c678917139a9901b11
@@ -0,0 +1 @@
1
+ 5173ce3f6b4ff3693b325a6e0858571f9c162e38
@@ -0,0 +1 @@
1
+ 87fe6f2ca3a6fca794a15b4555d1ea3cdd3b38c82c48b75fd03c4f55ec7941e0
@@ -0,0 +1 @@
1
+ d7750e9155149c0131d04463b2eba12059ecc2bb5e5e3c800e90f5d5df85fe67d26b2e73ac96f8c5772212249ab2dcc4bbbc522bf3f1adad03e76337457155c3
@@ -3,7 +3,7 @@
3
3
  "component": {
4
4
  "group": "host.exp.exponent",
5
5
  "module": "expo.modules.video",
6
- "version": "3.0.8",
6
+ "version": "3.0.9",
7
7
  "attributes": {
8
8
  "org.gradle.status": "release"
9
9
  }
@@ -24,13 +24,13 @@
24
24
  },
25
25
  "files": [
26
26
  {
27
- "name": "expo.modules.video-3.0.8.aar",
28
- "url": "expo.modules.video-3.0.8.aar",
29
- "size": 485021,
30
- "sha512": "7f14a429ae74d60c2b8f86a5be88f1fa98d79436184df4e6d7a0cd0a34d3c0fc18ab51f96846148098c9815fdd920d30c138a99159c3356bd0c27dca165f3ece",
31
- "sha256": "4fc1f0c4ea41f15031193905296adc667542c94af9734825c454465799a7d76f",
32
- "sha1": "17071b6c2f69fbce08b45d695db421aa1a627177",
33
- "md5": "2898619f8a2c3e8da7584236d436d0dc"
27
+ "name": "expo.modules.video-3.0.9.aar",
28
+ "url": "expo.modules.video-3.0.9.aar",
29
+ "size": 499112,
30
+ "sha512": "d7750e9155149c0131d04463b2eba12059ecc2bb5e5e3c800e90f5d5df85fe67d26b2e73ac96f8c5772212249ab2dcc4bbbc522bf3f1adad03e76337457155c3",
31
+ "sha256": "87fe6f2ca3a6fca794a15b4555d1ea3cdd3b38c82c48b75fd03c4f55ec7941e0",
32
+ "sha1": "5173ce3f6b4ff3693b325a6e0858571f9c162e38",
33
+ "md5": "3a9f65c14799d372d9ff3cf1e911e79c"
34
34
  }
35
35
  ]
36
36
  },
@@ -113,13 +113,13 @@
113
113
  ],
114
114
  "files": [
115
115
  {
116
- "name": "expo.modules.video-3.0.8.aar",
117
- "url": "expo.modules.video-3.0.8.aar",
118
- "size": 485021,
119
- "sha512": "7f14a429ae74d60c2b8f86a5be88f1fa98d79436184df4e6d7a0cd0a34d3c0fc18ab51f96846148098c9815fdd920d30c138a99159c3356bd0c27dca165f3ece",
120
- "sha256": "4fc1f0c4ea41f15031193905296adc667542c94af9734825c454465799a7d76f",
121
- "sha1": "17071b6c2f69fbce08b45d695db421aa1a627177",
122
- "md5": "2898619f8a2c3e8da7584236d436d0dc"
116
+ "name": "expo.modules.video-3.0.9.aar",
117
+ "url": "expo.modules.video-3.0.9.aar",
118
+ "size": 499112,
119
+ "sha512": "d7750e9155149c0131d04463b2eba12059ecc2bb5e5e3c800e90f5d5df85fe67d26b2e73ac96f8c5772212249ab2dcc4bbbc522bf3f1adad03e76337457155c3",
120
+ "sha256": "87fe6f2ca3a6fca794a15b4555d1ea3cdd3b38c82c48b75fd03c4f55ec7941e0",
121
+ "sha1": "5173ce3f6b4ff3693b325a6e0858571f9c162e38",
122
+ "md5": "3a9f65c14799d372d9ff3cf1e911e79c"
123
123
  }
124
124
  ]
125
125
  },
@@ -133,13 +133,13 @@
133
133
  },
134
134
  "files": [
135
135
  {
136
- "name": "expo.modules.video-3.0.8-sources.jar",
137
- "url": "expo.modules.video-3.0.8-sources.jar",
138
- "size": 62083,
139
- "sha512": "0ef259fa9249b9b46fc87d60eb84b3440db5154dfea10a06067e5b75254fd607fb719eed4742c1468187f62d469e41aab377bce6f4d34940a5bfb03ebc9c377d",
140
- "sha256": "5877768a9233bafe5a37a3f3ba450b9e7faf59b8b7c9d094c80156595f6ebfb6",
141
- "sha1": "3a5c844a6f13bc9e6b790be66ddcaff1e52eb18e",
142
- "md5": "e0f6acfda837a7e5aca5a8d003a4314e"
136
+ "name": "expo.modules.video-3.0.9-sources.jar",
137
+ "url": "expo.modules.video-3.0.9-sources.jar",
138
+ "size": 63354,
139
+ "sha512": "f04da6fb2f5c1ba96f029687f46e31c261026c814121a81cdc257af33fa1383b5de77a8862123e632c93888bfce716edcc2acdd0d07460c678917139a9901b11",
140
+ "sha256": "5efae1439855a664269c02cbb19144477c94a4ac3af7dbd6a7874739d851cd4b",
141
+ "sha1": "24846cc393de2fd8ca5be69222980cc63f123ebb",
142
+ "md5": "8e05f3222326dcb5038e8d0ecd554094"
143
143
  }
144
144
  ]
145
145
  }
@@ -0,0 +1 @@
1
+ a3b7d4fac71a2213fe70909b50efa3b53b0337997839223162f049a372e18955
@@ -0,0 +1 @@
1
+ 9ce60e3520dc6b2f3fa0eb62a0e0c3204c8d575043cd8544bc7b0ffbf68722597c0d7a0d221b87f92174833292216bdd15c1ad87e3840297b7ec87e7b02cd169
@@ -9,7 +9,7 @@
9
9
  <modelVersion>4.0.0</modelVersion>
10
10
  <groupId>host.exp.exponent</groupId>
11
11
  <artifactId>expo.modules.video</artifactId>
12
- <version>3.0.8</version>
12
+ <version>3.0.9</version>
13
13
  <packaging>aar</packaging>
14
14
  <name>expo.modules.video</name>
15
15
  <url>https://github.com/expo/expo</url>
@@ -0,0 +1 @@
1
+ 0c7b1efb0eeee962d4bf702c2ff3979e7e1e11cd
@@ -0,0 +1 @@
1
+ 61fcc640dddd65c9ae1d73f7d7fa9868a915dd873a74266323310fec53dfeb97
@@ -0,0 +1 @@
1
+ aa9e0a17692fe2e4e09198ff75ff7ef4231db7041b74daf28b2c7a4dd3088133fb0237417548fd817622fde5677452e97ef168682c23d6e0aa10f1ea0113ddd7
@@ -3,11 +3,11 @@
3
3
  <groupId>host.exp.exponent</groupId>
4
4
  <artifactId>expo.modules.video</artifactId>
5
5
  <versioning>
6
- <latest>3.0.8</latest>
7
- <release>3.0.8</release>
6
+ <latest>3.0.9</latest>
7
+ <release>3.0.9</release>
8
8
  <versions>
9
- <version>3.0.8</version>
9
+ <version>3.0.9</version>
10
10
  </versions>
11
- <lastUpdated>20250902175111</lastUpdated>
11
+ <lastUpdated>20250903164526</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- 86f4d700078f6dd53df629c201e04853
1
+ 9cbafe00b1787bf123b208f6c7e368c7
@@ -1 +1 @@
1
- 021e70758d02f4b73af19b034a90ae1118cfd6b1
1
+ 31942a54bb947a12b7bc87fdb7a1b07e28f1e7af
@@ -1 +1 @@
1
- 84a533a322fe08e8ec3b9f6f34b78b6d97614fe239e61cf7f05951c2038db54d
1
+ 11c276d67c072d486db31682e60d28633be98d889375b781f3c34713da773eb4
@@ -1 +1 @@
1
- e7f43429c359432805452eb8f5913eab93969ac8b9e11992093324eec47d243f8642b74a93dabd2ea8a5139619af8b448fe4a62a78a5fe003f4cc08c1c4053a0
1
+ 43a94228aa7fab8da9f52521497995dad645c764a73d6ef1186103cce7986c1d0fa4347a61d0a0e2d1840b8db961935def842c02c174c7fe17fe03f2c29bb6bc
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "expo-video",
3
3
  "title": "Expo Video",
4
- "version": "3.0.8",
4
+ "version": "3.0.9",
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",
@@ -38,5 +38,5 @@
38
38
  "react": "*",
39
39
  "react-native": "*"
40
40
  },
41
- "gitHead": "d635404a12ea7996b987ce0fb7679a238ebcf31b"
41
+ "gitHead": "f97e6d1cae0cb14d5eed1b08fad6d12a7144af3a"
42
42
  }
@@ -20,15 +20,51 @@ const withExpoVideo = (config, { supportsBackgroundPlayback, supportsPictureInPi
20
20
  });
21
21
  (0, config_plugins_1.withAndroidManifest)(config, (config) => {
22
22
  const activity = config_plugins_1.AndroidConfig.Manifest.getMainActivityOrThrow(config.modResults);
23
- // No-op if the values are not defined
24
- if (typeof supportsPictureInPicture === 'undefined') {
25
- return config;
26
- }
27
- if (supportsPictureInPicture) {
28
- activity.$['android:supportsPictureInPicture'] = 'true';
23
+ // Handle Picture in Picture configuration
24
+ if (typeof supportsPictureInPicture !== 'undefined') {
25
+ if (supportsPictureInPicture) {
26
+ activity.$['android:supportsPictureInPicture'] = 'true';
27
+ }
28
+ else {
29
+ delete activity.$['android:supportsPictureInPicture'];
30
+ }
29
31
  }
30
- else {
31
- delete activity.$['android:supportsPictureInPicture'];
32
+ // Handle background playback configuration
33
+ if (supportsBackgroundPlayback) {
34
+ // Add required permissions for foreground service
35
+ config_plugins_1.AndroidConfig.Permissions.ensurePermissions(config.modResults, [
36
+ 'android.permission.FOREGROUND_SERVICE',
37
+ 'android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK',
38
+ ]);
39
+ // Add the foreground service to the manifest
40
+ const application = config_plugins_1.AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults);
41
+ // Check if the service already exists
42
+ const existingService = application.service?.find((service) => service.$?.['android:name'] ===
43
+ 'expo.modules.video.playbackService.ExpoVideoPlaybackService');
44
+ if (!existingService) {
45
+ // Add the service if it doesn't exist
46
+ if (!application.service) {
47
+ application.service = [];
48
+ }
49
+ application.service.push({
50
+ $: {
51
+ 'android:name': 'expo.modules.video.playbackService.ExpoVideoPlaybackService',
52
+ 'android:exported': 'false',
53
+ 'android:foregroundServiceType': 'mediaPlayback',
54
+ },
55
+ 'intent-filter': [
56
+ {
57
+ action: [
58
+ {
59
+ $: {
60
+ 'android:name': 'androidx.media3.session.MediaSessionService',
61
+ },
62
+ },
63
+ ],
64
+ },
65
+ ],
66
+ });
67
+ }
32
68
  }
33
69
  return config;
34
70
  });
@@ -39,16 +39,60 @@ const withExpoVideo: ConfigPlugin<WithExpoVideoOptions> = (
39
39
  withAndroidManifest(config, (config) => {
40
40
  const activity = AndroidConfig.Manifest.getMainActivityOrThrow(config.modResults);
41
41
 
42
- // No-op if the values are not defined
43
- if (typeof supportsPictureInPicture === 'undefined') {
44
- return config;
42
+ // Handle Picture in Picture configuration
43
+ if (typeof supportsPictureInPicture !== 'undefined') {
44
+ if (supportsPictureInPicture) {
45
+ activity.$['android:supportsPictureInPicture'] = 'true';
46
+ } else {
47
+ delete activity.$['android:supportsPictureInPicture'];
48
+ }
45
49
  }
46
50
 
47
- if (supportsPictureInPicture) {
48
- activity.$['android:supportsPictureInPicture'] = 'true';
49
- } else {
50
- delete activity.$['android:supportsPictureInPicture'];
51
+ // Handle background playback configuration
52
+ if (supportsBackgroundPlayback) {
53
+ // Add required permissions for foreground service
54
+ AndroidConfig.Permissions.ensurePermissions(config.modResults, [
55
+ 'android.permission.FOREGROUND_SERVICE',
56
+ 'android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK',
57
+ ]);
58
+
59
+ // Add the foreground service to the manifest
60
+ const application = AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults);
61
+
62
+ // Check if the service already exists
63
+ const existingService = application.service?.find(
64
+ (service: any) =>
65
+ service.$?.['android:name'] ===
66
+ 'expo.modules.video.playbackService.ExpoVideoPlaybackService'
67
+ );
68
+
69
+ if (!existingService) {
70
+ // Add the service if it doesn't exist
71
+ if (!application.service) {
72
+ application.service = [];
73
+ }
74
+
75
+ application.service.push({
76
+ $: {
77
+ 'android:name': 'expo.modules.video.playbackService.ExpoVideoPlaybackService',
78
+ 'android:exported': 'false',
79
+ 'android:foregroundServiceType': 'mediaPlayback',
80
+ },
81
+ 'intent-filter': [
82
+ {
83
+ action: [
84
+ {
85
+ $: {
86
+ 'android:name': 'androidx.media3.session.MediaSessionService',
87
+ },
88
+ },
89
+ ],
90
+ },
91
+ ],
92
+ });
93
+ }
51
94
  }
95
+
52
96
  return config;
53
97
  });
54
98
  return config;
@@ -134,6 +134,8 @@ export declare class VideoPlayer extends SharedObject<VideoPlayerEvents> {
134
134
  /**
135
135
  * Boolean value determining whether the player should show the now playing notification.
136
136
  *
137
+ * > **Note**: On Android, `supportsBackgroundPlayback` property of the [config plugin](#configuration-in-app-config)
138
+ * > has to be `true` for the now playing notification to work.
137
139
  * @default false
138
140
  * @platform android
139
141
  * @platform ios
@@ -142,6 +144,9 @@ export declare class VideoPlayer extends SharedObject<VideoPlayerEvents> {
142
144
 
143
145
  /**
144
146
  * Determines whether the player should continue playing after the app enters the background.
147
+ *
148
+ * > **Note**: The `supportsBackgroundPlayback` property of the [config plugin](#configuration-in-app-config)
149
+ * > has to be `true` for the background playback to work.
145
150
  * @default false
146
151
  * @platform ios
147
152
  * @platform android
@@ -1 +0,0 @@
1
- 5877768a9233bafe5a37a3f3ba450b9e7faf59b8b7c9d094c80156595f6ebfb6
@@ -1 +0,0 @@
1
- 0ef259fa9249b9b46fc87d60eb84b3440db5154dfea10a06067e5b75254fd607fb719eed4742c1468187f62d469e41aab377bce6f4d34940a5bfb03ebc9c377d
@@ -1 +0,0 @@
1
- 17071b6c2f69fbce08b45d695db421aa1a627177
@@ -1 +0,0 @@
1
- 4fc1f0c4ea41f15031193905296adc667542c94af9734825c454465799a7d76f
@@ -1 +0,0 @@
1
- 7f14a429ae74d60c2b8f86a5be88f1fa98d79436184df4e6d7a0cd0a34d3c0fc18ab51f96846148098c9815fdd920d30c138a99159c3356bd0c27dca165f3ece
@@ -1 +0,0 @@
1
- 3d197ee38ee03b31c89c563184125d126638eb30
@@ -1 +0,0 @@
1
- a7c9acc8d7171bbaa9e5a1a4851553d08fa3af20a59dd9e1242a6ac2d02f594a
@@ -1 +0,0 @@
1
- 55175d2001f7a5fc2ea27e53edbb3af0a0f3a6404c94d0b398f1448b9182c5ae3960e800df060fd56880c4740a511886069c62b18445895b899dc667ff4cdac6
@@ -1 +0,0 @@
1
- 7ed4cb9b0757a2f1d05a9cb15ca410090e7ba3ff
@@ -1 +0,0 @@
1
- adabcf86f93ccb240d76a48e2ef36506e508db62399ed0f5bed887bec420a38c
@@ -1 +0,0 @@
1
- 7300acf5b59e6fddfdf6b4ec32e2fe4fd3c982d94598c111e3f5f6c8c249dd15a07c1d3de5e28f59678436c488018173474e3d635c3ec4873a8bad1271532c10