expo-video 1.2.5 → 1.2.7

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 (46) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/android/build.gradle +4 -3
  3. package/android/src/main/java/expo/modules/video/PlayerEvent.kt +1 -1
  4. package/android/src/main/java/expo/modules/video/VideoManager.kt +1 -1
  5. package/android/src/main/java/expo/modules/video/VideoModule.kt +39 -6
  6. package/android/src/main/java/expo/modules/video/VideoPlayer.kt +7 -4
  7. package/android/src/main/java/expo/modules/video/VideoView.kt +4 -0
  8. package/android/src/main/java/expo/modules/video/playbackService/ExpoVideoPlaybackService.kt +5 -2
  9. package/android/src/main/java/expo/modules/video/playbackService/PlaybackServiceConnection.kt +3 -3
  10. package/android/src/main/java/expo/modules/video/records/VideoSource.kt +35 -2
  11. package/android/src/main/java/expo/modules/video/utils/DataSourceUtils.kt +1 -1
  12. package/build/VideoPlayer.d.ts.map +1 -1
  13. package/build/VideoPlayer.js +19 -1
  14. package/build/VideoPlayer.js.map +1 -1
  15. package/build/VideoPlayer.types.d.ts +35 -2
  16. package/build/VideoPlayer.types.d.ts.map +1 -1
  17. package/build/VideoPlayer.types.js.map +1 -1
  18. package/build/VideoPlayer.web.d.ts +3 -0
  19. package/build/VideoPlayer.web.d.ts.map +1 -1
  20. package/build/VideoPlayer.web.js +11 -1
  21. package/build/VideoPlayer.web.js.map +1 -1
  22. package/build/VideoView.types.d.ts +8 -0
  23. package/build/VideoView.types.d.ts.map +1 -1
  24. package/build/VideoView.types.js.map +1 -1
  25. package/build/VideoView.web.d.ts.map +1 -1
  26. package/build/VideoView.web.js +21 -0
  27. package/build/VideoView.web.js.map +1 -1
  28. package/build/resolveAssetSource.d.ts +3 -0
  29. package/build/resolveAssetSource.d.ts.map +1 -0
  30. package/build/resolveAssetSource.js +3 -0
  31. package/build/resolveAssetSource.js.map +1 -0
  32. package/build/resolveAssetSource.web.d.ts +4 -0
  33. package/build/resolveAssetSource.web.d.ts.map +1 -0
  34. package/build/resolveAssetSource.web.js +16 -0
  35. package/build/resolveAssetSource.web.js.map +1 -0
  36. package/ios/NowPlayingManager.swift +1 -0
  37. package/ios/VideoModule.swift +37 -5
  38. package/ios/VideoView.swift +6 -0
  39. package/package.json +2 -2
  40. package/src/VideoPlayer.tsx +21 -1
  41. package/src/VideoPlayer.types.ts +42 -1
  42. package/src/VideoPlayer.web.tsx +12 -1
  43. package/src/VideoView.types.ts +10 -0
  44. package/src/VideoView.web.tsx +23 -0
  45. package/src/resolveAssetSource.ts +2 -0
  46. package/src/resolveAssetSource.web.ts +17 -0
package/CHANGELOG.md CHANGED
@@ -10,6 +10,35 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 1.2.7 — 2024-09-26
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - [Web] Fix invalid `resolveAssetSource` for web. ([#31664](https://github.com/expo/expo/pull/31664) by [@behenate](https://github.com/behenate))
18
+
19
+ ## 1.2.6 — 2024-09-13
20
+
21
+ ### 🛠 Breaking changes
22
+
23
+ - `showNowPlayingNotification` property of the player now defaults to `false`. ([#31261](https://github.com/expo/expo/pull/31261) by [@behenate](https://github.com/behenate))
24
+
25
+ ### 🎉 New features
26
+
27
+ - [Android][iOS] Add properties for more advanced live stream configuration. ([#30648](https://github.com/expo/expo/pull/30648) by [@justjoostnl](https://github.com/justjoostnl))
28
+ - [iOS] Add live indicator in the now playing info. ([#30629](https://github.com/expo/expo/pull/30629) by [@justjoostnl](https://github.com/justjoostnl))
29
+ - Add fullscreen enter and exit events. ([#30922](https://github.com/expo/expo/pull/30922) by [@fobos531](https://github.com/fobos531))
30
+ - Add support for playback of local assets imported with the `require` function. ([#30837](https://github.com/expo/expo/pull/30837) by [@behenate](https://github.com/behenate))
31
+
32
+ ### 🐛 Bug fixes
33
+
34
+ - [iOS] Fixed `player.currentTime` being `NaN` when source is not provided and `player.duration` being `NaN` inside the hook callback when the source is updated. ([#31011](https://github.com/expo/expo/pull/31011) by [@AlirezaHadjar](https://github.com/AlirezaHadjar))
35
+ - [Android] Fix wrong event being sent when the volume is changed. ([#30891](https://github.com/expo/expo/pull/30891) by [@behenate](https://github.com/behenate))
36
+
37
+ ### 💡 Others
38
+
39
+ - Bump media3 version to 1.4.0. ([#31239](https://github.com/expo/expo/pull/31239) by [@behenate](https://github.com/behenate))
40
+ - [Android] The media will now be buffered even when the player is unmounted to match iOS behavior. ([#30626](https://github.com/expo/expo/pull/30626) by [@behenate](https://github.com/behenate))
41
+
13
42
  ## 1.2.5 — 2024-08-20
14
43
 
15
44
  ### 🐛 Bug fixes
@@ -1,7 +1,7 @@
1
1
  apply plugin: 'com.android.library'
2
2
 
3
3
  group = 'host.exp.exponent'
4
- version = '1.2.5'
4
+ version = '1.2.7'
5
5
 
6
6
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
7
7
  apply from: expoModulesCorePlugin
@@ -14,14 +14,15 @@ android {
14
14
  namespace "expo.modules.video"
15
15
  defaultConfig {
16
16
  versionCode 1
17
- versionName '1.2.5'
17
+ versionName '1.2.7'
18
18
  }
19
19
  }
20
20
 
21
21
  dependencies {
22
22
  implementation 'com.facebook.react:react-android'
23
23
 
24
- def androidxMedia3Version = "1.3.1"
24
+ // Remember to keep this in sync with the version in `expo-audio`
25
+ def androidxMedia3Version = "1.4.0"
25
26
  implementation "androidx.media3:media3-session:${androidxMedia3Version}"
26
27
  implementation "androidx.media3:media3-exoplayer:${androidxMedia3Version}"
27
28
  implementation "androidx.media3:media3-exoplayer-dash:${androidxMedia3Version}"
@@ -23,7 +23,7 @@ sealed class PlayerEvent {
23
23
  }
24
24
 
25
25
  data class VolumeChanged(val newValue: VolumeEvent, val oldValue: VolumeEvent?) : PlayerEvent() {
26
- override val name = "playingChange"
26
+ override val name = "volumeChange"
27
27
  override val arguments = arrayOf(newValue, oldValue)
28
28
  }
29
29
 
@@ -52,7 +52,7 @@ object VideoManager {
52
52
  }
53
53
 
54
54
  if (videoPlayersToVideoViews[videoPlayer]?.size == 1) {
55
- videoPlayer.serviceConnection.playbackServiceBinder?.service?.registerPlayer(videoPlayer.player)
55
+ videoPlayer.serviceConnection.playbackServiceBinder?.service?.registerPlayer(videoPlayer)
56
56
  }
57
57
  }
58
58
 
@@ -4,9 +4,11 @@ package expo.modules.video
4
4
 
5
5
  import android.app.Activity
6
6
  import android.net.Uri
7
+ import androidx.media3.common.C
7
8
  import androidx.media3.common.PlaybackParameters
8
9
  import androidx.media3.common.Player.REPEAT_MODE_OFF
9
10
  import androidx.media3.common.Player.REPEAT_MODE_ONE
11
+ import androidx.media3.common.Timeline
10
12
  import com.facebook.react.uimanager.PixelUtil
11
13
  import com.facebook.react.uimanager.Spacing
12
14
  import com.facebook.react.uimanager.ViewProps
@@ -43,12 +45,13 @@ class VideoModule : Module() {
43
45
  View(VideoView::class) {
44
46
  Events(
45
47
  "onPictureInPictureStart",
46
- "onPictureInPictureStop"
48
+ "onPictureInPictureStop",
49
+ "onFullscreenEnter",
50
+ "onFullscreenExit"
47
51
  )
48
52
 
49
53
  Prop("player") { view: VideoView, player: VideoPlayer ->
50
54
  view.videoPlayer = player
51
- player.prepare()
52
55
  }
53
56
 
54
57
  Prop("nativeControls") { view: VideoView, useNativeControls: Boolean ->
@@ -149,7 +152,11 @@ class VideoModule : Module() {
149
152
 
150
153
  Class(VideoPlayer::class) {
151
154
  Constructor { source: VideoSource? ->
152
- VideoPlayer(activity.applicationContext, appContext, source)
155
+ val player = VideoPlayer(activity.applicationContext, appContext, source)
156
+ appContext.mainQueue.launch {
157
+ player.prepare()
158
+ }
159
+ return@Constructor player
153
160
  }
154
161
 
155
162
  Property("playing")
@@ -193,6 +200,34 @@ class VideoModule : Module() {
193
200
  }
194
201
  }
195
202
 
203
+ Property("currentLiveTimestamp")
204
+ .get { ref: VideoPlayer ->
205
+ // TODO: same as `currentTime`
206
+ runBlocking(appContext.mainQueue.coroutineContext) {
207
+ val window = Timeline.Window()
208
+ if (!ref.player.currentTimeline.isEmpty) {
209
+ ref.player.currentTimeline.getWindow(ref.player.currentMediaItemIndex, window)
210
+ }
211
+ if (window.windowStartTimeMs == C.TIME_UNSET) {
212
+ null
213
+ } else {
214
+ window.windowStartTimeMs + ref.player.currentPosition
215
+ }
216
+ }
217
+ }
218
+
219
+ Property("currentOffsetFromLive")
220
+ .get { ref: VideoPlayer ->
221
+ // TODO: same as `currentTime`
222
+ runBlocking(appContext.mainQueue.coroutineContext) {
223
+ if (ref.player.currentLiveOffset == C.TIME_UNSET) {
224
+ null
225
+ } else {
226
+ ref.player.currentLiveOffset / 1000f
227
+ }
228
+ }
229
+ }
230
+
196
231
  Property("duration")
197
232
  .get { ref: VideoPlayer ->
198
233
  ref.duration
@@ -284,9 +319,7 @@ class VideoModule : Module() {
284
319
 
285
320
  appContext.mainQueue.launch {
286
321
  ref.uncommittedSource = videoSource
287
- if (VideoManager.isVideoPlayerAttachedToView(ref)) {
288
- ref.prepare()
289
- }
322
+ ref.prepare()
290
323
  }
291
324
  }
292
325
 
@@ -36,7 +36,7 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
36
36
  .setLooper(context.mainLooper)
37
37
  .build()
38
38
 
39
- val serviceConnection = PlaybackServiceConnection(WeakReference(player))
39
+ val serviceConnection = PlaybackServiceConnection(WeakReference(this))
40
40
 
41
41
  var playing by IgnoreSameSet(false) { new, old ->
42
42
  sendEvent(PlayerEvent.IsPlayingChanged(new, old))
@@ -57,7 +57,7 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
57
57
  field = preservesPitch
58
58
  playbackParameters = applyPitchCorrection(playbackParameters)
59
59
  }
60
- var showNowPlayingNotification = true
60
+ var showNowPlayingNotification = false
61
61
  set(value) {
62
62
  field = value
63
63
  serviceConnection.playbackServiceBinder?.service?.setShowNotification(value, this.player)
@@ -67,11 +67,12 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
67
67
 
68
68
  var volume: Float by IgnoreSameSet(1f) { new: Float, old: Float ->
69
69
  player.volume = if (muted) 0f else new
70
+ userVolume = volume
70
71
  sendEvent(PlayerEvent.VolumeChanged(VolumeEvent(new, muted), VolumeEvent(old, muted)))
71
72
  }
72
73
 
73
74
  var muted: Boolean by IgnoreSameSet(false) { new: Boolean, old: Boolean ->
74
- volume = if (new) 0f else userVolume
75
+ player.volume = if (new) 0f else userVolume
75
76
  sendEvent(PlayerEvent.VolumeChanged(VolumeEvent(volume, new), VolumeEvent(volume, old)))
76
77
  }
77
78
 
@@ -113,7 +114,9 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
113
114
  }
114
115
 
115
116
  override fun onVolumeChanged(volume: Float) {
116
- this@VideoPlayer.volume = volume
117
+ if (!muted) {
118
+ this@VideoPlayer.volume = volume
119
+ }
117
120
  }
118
121
 
119
122
  override fun onPlaybackParametersChanged(playbackParameters: PlaybackParameters) {
@@ -36,6 +36,8 @@ class VideoView(context: Context, appContext: AppContext) : ExpoView(context, ap
36
36
  val playerView: PlayerView = PlayerView(context.applicationContext)
37
37
  val onPictureInPictureStart by EventDispatcher<Unit>()
38
38
  val onPictureInPictureStop by EventDispatcher<Unit>()
39
+ val onFullscreenEnter by EventDispatcher<Unit>()
40
+ val onFullscreenExit by EventDispatcher<Unit>()
39
41
 
40
42
  var willEnterPiP: Boolean = false
41
43
  var isInFullscreen: Boolean = false
@@ -154,6 +156,7 @@ class VideoView(context: Context, appContext: AppContext) : ExpoView(context, ap
154
156
  @Suppress("DEPRECATION")
155
157
  currentActivity.overridePendingTransition(0, 0)
156
158
  }
159
+ onFullscreenEnter(Unit)
157
160
  isInFullscreen = true
158
161
  }
159
162
 
@@ -162,6 +165,7 @@ class VideoView(context: Context, appContext: AppContext) : ExpoView(context, ap
162
165
  val fullScreenButton: ImageButton = playerView.findViewById(androidx.media3.ui.R.id.exo_fullscreen)
163
166
  fullScreenButton.setImageResource(androidx.media3.ui.R.drawable.exo_icon_fullscreen_enter)
164
167
  videoPlayer?.changePlayerView(playerView)
168
+ onFullscreenExit(Unit)
165
169
  isInFullscreen = false
166
170
  }
167
171
 
@@ -20,6 +20,7 @@ import androidx.media3.session.SessionCommand
20
20
  import com.google.common.collect.ImmutableList
21
21
  import expo.modules.kotlin.AppContext
22
22
  import expo.modules.video.R
23
+ import expo.modules.video.VideoPlayer
23
24
 
24
25
  class PlaybackServiceBinder(val service: ExpoVideoPlaybackService) : Binder()
25
26
 
@@ -51,7 +52,8 @@ class ExpoVideoPlaybackService : MediaSessionService() {
51
52
  }
52
53
  }
53
54
 
54
- fun registerPlayer(player: ExoPlayer) {
55
+ fun registerPlayer(videoPlayer: VideoPlayer) {
56
+ val player = videoPlayer.player
55
57
  if (mediaSessions[player] != null) {
56
58
  return
57
59
  }
@@ -64,6 +66,7 @@ class ExpoVideoPlaybackService : MediaSessionService() {
64
66
 
65
67
  mediaSessions[player] = mediaSession
66
68
  addSession(mediaSession)
69
+ setShowNotification(videoPlayer.showNowPlayingNotification, player)
67
70
  }
68
71
 
69
72
  fun unregisterPlayer(player: ExoPlayer) {
@@ -82,7 +85,7 @@ class ExpoVideoPlaybackService : MediaSessionService() {
82
85
  }
83
86
 
84
87
  override fun onUpdateNotification(session: MediaSession, startInForegroundRequired: Boolean) {
85
- if (session.sessionExtras.getBoolean(SESSION_SHOW_NOTIFICATION, true)) {
88
+ if (session.sessionExtras.getBoolean(SESSION_SHOW_NOTIFICATION, false)) {
86
89
  createNotification(session)
87
90
  } else {
88
91
  (session.player as? ExoPlayer)?.let {
@@ -4,14 +4,14 @@ import android.content.ComponentName
4
4
  import android.content.ServiceConnection
5
5
  import android.os.IBinder
6
6
  import android.util.Log
7
- import androidx.media3.exoplayer.ExoPlayer
7
+ import expo.modules.video.VideoPlayer
8
8
  import java.lang.ref.WeakReference
9
9
 
10
- class PlaybackServiceConnection(val player: WeakReference<ExoPlayer>) : ServiceConnection {
10
+ class PlaybackServiceConnection(val player: WeakReference<VideoPlayer>) : ServiceConnection {
11
11
  var playbackServiceBinder: PlaybackServiceBinder? = null
12
12
 
13
13
  override fun onServiceConnected(componentName: ComponentName, binder: IBinder) {
14
- val player: ExoPlayer = player.get() ?: return
14
+ val player = player.get() ?: return
15
15
  playbackServiceBinder = binder as? PlaybackServiceBinder
16
16
  playbackServiceBinder?.service?.registerPlayer(player) ?: run {
17
17
  Log.w(
@@ -1,10 +1,16 @@
1
1
  package expo.modules.video.records
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.content.ContentResolver
2
5
  import android.content.Context
3
6
  import android.net.Uri
7
+ import android.util.Log
4
8
  import androidx.annotation.OptIn
5
9
  import androidx.media3.common.MediaItem
6
10
  import androidx.media3.common.MediaMetadata
7
11
  import androidx.media3.common.util.UnstableApi
12
+ import androidx.media3.datasource.DataSpec
13
+ import androidx.media3.datasource.RawResourceDataSource
8
14
  import androidx.media3.exoplayer.source.MediaSource
9
15
  import expo.modules.kotlin.records.Field
10
16
  import expo.modules.kotlin.records.Record
@@ -35,10 +41,10 @@ class VideoSource(
35
41
  return buildMediaSourceWithHeaders(context, this)
36
42
  }
37
43
 
38
- fun toMediaItem() = MediaItem
44
+ fun toMediaItem(context: Context) = MediaItem
39
45
  .Builder()
40
46
  .apply {
41
- setUri(uri)
47
+ setUri(parseLocalAssetId(uri, context))
42
48
  setMediaId(toMediaId())
43
49
  drm?.let {
44
50
  if (it.type.isSupported()) {
@@ -57,4 +63,31 @@ class VideoSource(
57
63
  )
58
64
  }
59
65
  .build()
66
+
67
+ // Using `resolveAssetSource` to generate a local asset URI returns a resource name for android release builds
68
+ // we have to get the raw resource URI to play the video
69
+ @SuppressLint("DiscouragedApi") // AFAIK, in this case, there's no other way to get the resource URI
70
+ private fun parseLocalAssetId(uri: Uri?, context: Context): Uri? {
71
+ if (uri == null || uri.scheme != null) {
72
+ return uri
73
+ }
74
+ try {
75
+ val resourceId: Int = context.resources.getIdentifier(
76
+ uri.toString(),
77
+ "raw",
78
+ context.packageName
79
+ )
80
+ val parsedUri = Uri.Builder()
81
+ .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
82
+ .appendPath(resourceId.toString())
83
+ .build()
84
+ val dataSpec = DataSpec(parsedUri)
85
+ val rawResourceDataSource = RawResourceDataSource(context)
86
+ rawResourceDataSource.open(dataSpec)
87
+ return rawResourceDataSource.uri
88
+ } catch (e: RawResourceDataSource.RawResourceDataSourceException) {
89
+ Log.e("ExpoVideo", "Error parsing local asset id, falling back to original uri", e)
90
+ return uri
91
+ }
92
+ }
60
93
  }
@@ -44,7 +44,7 @@ fun buildMediaSourceFactory(context: Context, dataSourceFactory: DataSource.Fact
44
44
  fun buildMediaSourceWithHeaders(context: Context, videoSource: VideoSource): MediaSource {
45
45
  val dataSourceFactory = buildDataSourceFactory(context, videoSource)
46
46
  val mediaSourceFactory = buildMediaSourceFactory(context, dataSourceFactory)
47
- val mediaItem = videoSource.toMediaItem()
47
+ val mediaItem = videoSource.toMediaItem(context)
48
48
  return mediaSourceFactory.createMediaSource(mediaItem)
49
49
  }
50
50
 
@@ -1 +1 @@
1
- {"version":3,"file":"VideoPlayer.d.ts","sourceRoot":"","sources":["../src/VideoPlayer.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEpE;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GACpC,WAAW,CAQb;;AAED,wBAA6C"}
1
+ {"version":3,"file":"VideoPlayer.d.ts","sourceRoot":"","sources":["../src/VideoPlayer.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AASpE;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GACpC,WAAW,CAQb;;AAeD,wBAA6C"}
@@ -1,17 +1,35 @@
1
1
  import { useReleasingSharedObject } from 'expo-modules-core';
2
2
  import NativeVideoModule from './NativeVideoModule';
3
+ import resolveAssetSource from './resolveAssetSource';
4
+ // TODO: Temporary solution until we develop a way of overriding prototypes that won't break the lazy loading of the module.
5
+ const replace = NativeVideoModule.VideoPlayer.prototype.replace;
6
+ NativeVideoModule.VideoPlayer.prototype.replace = function (source) {
7
+ return replace.call(this, parseSource(source));
8
+ };
3
9
  /**
4
10
  * Creates a `VideoPlayer`, which will be automatically cleaned up when the component is unmounted.
5
11
  * @param source - A video source that is used to initialize the player.
6
12
  * @param setup - A function that allows setting up the player. It will run after the player is created.
7
13
  */
8
14
  export function useVideoPlayer(source, setup) {
9
- const parsedSource = typeof source === 'string' ? { uri: source } : source;
15
+ const parsedSource = parseSource(source);
10
16
  return useReleasingSharedObject(() => {
11
17
  const player = new NativeVideoModule.VideoPlayer(parsedSource);
12
18
  setup?.(player);
13
19
  return player;
14
20
  }, [JSON.stringify(parsedSource)]);
15
21
  }
22
+ function parseSource(source) {
23
+ if (typeof source === 'number') {
24
+ return { uri: resolveAssetSource(source).uri };
25
+ }
26
+ else if (typeof source === 'string') {
27
+ return { uri: source };
28
+ }
29
+ if (typeof source?.assetId === 'number' && !source.uri) {
30
+ return { ...source, uri: resolveAssetSource(source.assetId).uri };
31
+ }
32
+ return source;
33
+ }
16
34
  export default NativeVideoModule.VideoPlayer;
17
35
  //# sourceMappingURL=VideoPlayer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"VideoPlayer.js","sourceRoot":"","sources":["../src/VideoPlayer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAE7D,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AAGpD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAmB,EACnB,KAAqC;IAErC,MAAM,YAAY,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IAE3E,OAAO,wBAAwB,CAAC,GAAG,EAAE;QACnC,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;QAC/D,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC;QAChB,OAAO,MAAM,CAAC;IAChB,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AACrC,CAAC;AAED,eAAe,iBAAiB,CAAC,WAAW,CAAC","sourcesContent":["import { useReleasingSharedObject } from 'expo-modules-core';\n\nimport NativeVideoModule from './NativeVideoModule';\nimport type { VideoPlayer, VideoSource } from './VideoPlayer.types';\n\n/**\n * Creates a `VideoPlayer`, which will be automatically cleaned up when the component is unmounted.\n * @param source - A video source that is used to initialize the player.\n * @param setup - A function that allows setting up the player. It will run after the player is created.\n */\nexport function useVideoPlayer(\n source: VideoSource,\n setup?: (player: VideoPlayer) => void\n): VideoPlayer {\n const parsedSource = typeof source === 'string' ? { uri: source } : source;\n\n return useReleasingSharedObject(() => {\n const player = new NativeVideoModule.VideoPlayer(parsedSource);\n setup?.(player);\n return player;\n }, [JSON.stringify(parsedSource)]);\n}\n\nexport default NativeVideoModule.VideoPlayer;\n"]}
1
+ {"version":3,"file":"VideoPlayer.js","sourceRoot":"","sources":["../src/VideoPlayer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAE7D,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AAEpD,OAAO,kBAAkB,MAAM,sBAAsB,CAAC;AAEtD,4HAA4H;AAC5H,MAAM,OAAO,GAAG,iBAAiB,CAAC,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC;AAChE,iBAAiB,CAAC,WAAW,CAAC,SAAS,CAAC,OAAO,GAAG,UAAU,MAAmB;IAC7E,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;AACjD,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAmB,EACnB,KAAqC;IAErC,MAAM,YAAY,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IAEzC,OAAO,wBAAwB,CAAC,GAAG,EAAE;QACnC,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;QAC/D,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC;QAChB,OAAO,MAAM,CAAC;IAChB,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,WAAW,CAAC,MAAmB;IACtC,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;QAC9B,OAAO,EAAE,GAAG,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC;KAChD;SAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;QACrC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;KACxB;IAED,IAAI,OAAO,MAAM,EAAE,OAAO,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE;QACtD,OAAO,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC;KACnE;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,eAAe,iBAAiB,CAAC,WAAW,CAAC","sourcesContent":["import { useReleasingSharedObject } from 'expo-modules-core';\n\nimport NativeVideoModule from './NativeVideoModule';\nimport type { VideoPlayer, VideoSource } from './VideoPlayer.types';\nimport resolveAssetSource from './resolveAssetSource';\n\n// TODO: Temporary solution until we develop a way of overriding prototypes that won't break the lazy loading of the module.\nconst replace = NativeVideoModule.VideoPlayer.prototype.replace;\nNativeVideoModule.VideoPlayer.prototype.replace = function (source: VideoSource) {\n return replace.call(this, parseSource(source));\n};\n\n/**\n * Creates a `VideoPlayer`, which will be automatically cleaned up when the component is unmounted.\n * @param source - A video source that is used to initialize the player.\n * @param setup - A function that allows setting up the player. It will run after the player is created.\n */\nexport function useVideoPlayer(\n source: VideoSource,\n setup?: (player: VideoPlayer) => void\n): VideoPlayer {\n const parsedSource = parseSource(source);\n\n return useReleasingSharedObject(() => {\n const player = new NativeVideoModule.VideoPlayer(parsedSource);\n setup?.(player);\n return player;\n }, [JSON.stringify(parsedSource)]);\n}\n\nfunction parseSource(source: VideoSource): VideoSource {\n if (typeof source === 'number') {\n return { uri: resolveAssetSource(source).uri };\n } else if (typeof source === 'string') {\n return { uri: source };\n }\n\n if (typeof source?.assetId === 'number' && !source.uri) {\n return { ...source, uri: resolveAssetSource(source.assetId).uri };\n }\n return source;\n}\n\nexport default NativeVideoModule.VideoPlayer;\n"]}
@@ -28,6 +28,28 @@ export declare class VideoPlayer extends SharedObject<VideoPlayerEvents> {
28
28
  * Setting `currentTime` to a new value seeks the player to the given time.
29
29
  */
30
30
  currentTime: number;
31
+ /**
32
+ * The exact timestamp when the currently displayed video frame was sent from the server,
33
+ * based on the `EXT-X-PROGRAM-DATE-TIME` tag in the livestream metadata.
34
+ * If this metadata is missing, this property will return `null`.
35
+ * > This property is read-only.
36
+ * @platform android
37
+ * @platform ios
38
+ */
39
+ readonly currentLiveTimestamp: number | null;
40
+ /**
41
+ * Float value indicating the latency of the live stream in seconds.
42
+ * If a livestream doesn't have the required metadata, this will return `null`.
43
+ * > This property is get-only
44
+ * @platform android
45
+ * @platform ios
46
+ */
47
+ readonly currentOffsetFromLive: number | null;
48
+ /**
49
+ * Float value indicating the time offset from the live in seconds.
50
+ * @platform ios
51
+ */
52
+ targetOffsetFromLive: number;
31
53
  /**
32
54
  * Float value indicating the duration of the current video in seconds.
33
55
  * > This property is get-only
@@ -65,6 +87,10 @@ export declare class VideoPlayer extends SharedObject<VideoPlayerEvents> {
65
87
  status: VideoPlayerStatus;
66
88
  /**
67
89
  * Boolean value determining whether the player should show the now playing notification.
90
+ *
91
+ * @default false
92
+ * @platrorm android
93
+ * @platform ios
68
94
  */
69
95
  showNowPlayingNotification: boolean;
70
96
  /**
@@ -137,11 +163,18 @@ export type VideoPlayerEvents = {
137
163
  * - `error`: The player has encountered an error while loading or playing the video.
138
164
  */
139
165
  export type VideoPlayerStatus = 'idle' | 'loading' | 'readyToPlay' | 'error';
140
- export type VideoSource = string | {
166
+ export type VideoSource = string | number | {
141
167
  /**
142
168
  * The URI of the video.
169
+ *
170
+ * This property is exclusive with the `assetId` property. When both are present, the `assetId` will be ignored.
171
+ */
172
+ uri?: string;
173
+ /**
174
+ * The asset ID of a local video asset, acquired with the `require` function.
175
+ * This property is exclusive with the `uri` property. When both are present, the `assetId` will be ignored.
143
176
  */
144
- uri: string;
177
+ assetId?: number;
145
178
  /**
146
179
  * Specifies the DRM options which will be used by the player while loading the video.
147
180
  */
@@ -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 type { SharedObject } from 'expo-modules-core';\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 * > This property is get-only, use `play` and `pause` methods to control the playback.\n */\n 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 * 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 */\n currentTime: number;\n\n /**\n * Float value indicating the duration of the current video in seconds.\n * > This property is get-only\n */\n duration: number;\n\n /**\n * Float value between 0 and 1 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 * > On web, changing this property is not supported, the player will always correct the pitch.\n * @default true\n * @platform android\n * @platform ios\n */\n preservesPitch: boolean;\n\n /**\n * Float value between 0 and 16 indicating the current playback speed of the player.\n * @default 1.0\n */\n playbackRate: number;\n\n /**\n * Boolean value indicating whether the player is currently playing a live stream.\n * > This property is get-only\n */\n isLive: boolean;\n\n /**\n * Indicates the current status of the player.\n * > This property is get-only\n */\n status: VideoPlayerStatus;\n\n /**\n * Boolean value determining whether the player should show the now playing notification.\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 * Initializes a new video player instance with the given source.\n * @hidden\n */\n constructor(source: VideoSource);\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 replace(source: VideoSource): void;\n\n /**\n * Seeks the playback by the given number of seconds.\n */\n seekBy(seconds: number): void;\n\n /**\n * Seeks the playback to the beginning.\n */\n replay(): void;\n}\n\n/**\n * Handlers for events which can be emitted by the player.\n */\nexport type VideoPlayerEvents = {\n /**\n * Handler for an event emitted when the status of the player changes.\n */\n statusChange(\n newStatus: VideoPlayerStatus,\n oldStatus: VideoPlayerStatus,\n error?: PlayerError\n ): void;\n /**\n * Handler for an event emitted when the player starts or stops playback.\n */\n playingChange(newIsPlaying: boolean, oldIsPlaying: boolean): void;\n /**\n * Handler for an event emitted when the `playbackRate` property of the player changes.\n */\n playbackRateChange(newPlaybackRate: number, oldPlaybackRate: number): void;\n /**\n * Handler for an event emitted when the `volume` property of the player changes.\n */\n volumeChange(newVolume: VolumeEvent, oldVolume: VolumeEvent): void;\n /**\n * Handler for an event emitted when the player plays to the end of the current source.\n */\n playToEnd(): void;\n /**\n * Handler for an event emitted when the current media source of the player changes.\n */\n sourceChange(newSource: VideoSource, previousSource: VideoSource): void;\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 | {\n /**\n * The URI of the video.\n */\n uri: string;\n /**\n * Specifies the DRM options which will be used by the player while loading the video.\n */\n drm?: DRMOptions;\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 */\n metadata?: VideoMetadata;\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 | null;\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 about the current volume and whether the player is muted.\n */\nexport type VolumeEvent = {\n volume: number;\n isMuted: boolean;\n};\n\n/**\n * Contains information that will be displayed in the now playing notification when the video is playing.\n */\nexport type VideoMetadata = {\n /**\n * The title of the video.\n */\n title?: string;\n /**\n * Secondary text that will be displayed under the title.\n */\n artist?: string;\n};\n\n/**\n * Specifies which type of DRM to use. Android supports Widevine, PlayReady and ClearKey, 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?: { [key: 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"]}
1
+ {"version":3,"file":"VideoPlayer.types.js","sourceRoot":"","sources":["../src/VideoPlayer.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { SharedObject } from 'expo-modules-core';\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 * > This property is get-only, use `play` and `pause` methods to control the playback.\n */\n 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 * 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 */\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 * > This property is read-only.\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 * > This property is get-only\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 * > This property is get-only\n */\n duration: number;\n\n /**\n * Float value between 0 and 1 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 * > On web, changing this property is not supported, the player will always correct the pitch.\n * @default true\n * @platform android\n * @platform ios\n */\n preservesPitch: boolean;\n\n /**\n * Float value between 0 and 16 indicating the current playback speed of the player.\n * @default 1.0\n */\n playbackRate: number;\n\n /**\n * Boolean value indicating whether the player is currently playing a live stream.\n * > This property is get-only\n */\n isLive: boolean;\n\n /**\n * Indicates the current status of the player.\n * > This property is get-only\n */\n status: VideoPlayerStatus;\n\n /**\n * Boolean value determining whether the player should show the now playing notification.\n *\n * @default false\n * @platrorm 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 * Initializes a new video player instance with the given source.\n * @hidden\n */\n constructor(source: VideoSource);\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 replace(source: VideoSource): void;\n\n /**\n * Seeks the playback by the given number of seconds.\n */\n seekBy(seconds: number): void;\n\n /**\n * Seeks the playback to the beginning.\n */\n replay(): void;\n}\n\n/**\n * Handlers for events which can be emitted by the player.\n */\nexport type VideoPlayerEvents = {\n /**\n * Handler for an event emitted when the status of the player changes.\n */\n statusChange(\n newStatus: VideoPlayerStatus,\n oldStatus: VideoPlayerStatus,\n error?: PlayerError\n ): void;\n /**\n * Handler for an event emitted when the player starts or stops playback.\n */\n playingChange(newIsPlaying: boolean, oldIsPlaying: boolean): void;\n /**\n * Handler for an event emitted when the `playbackRate` property of the player changes.\n */\n playbackRateChange(newPlaybackRate: number, oldPlaybackRate: number): void;\n /**\n * Handler for an event emitted when the `volume` property of the player changes.\n */\n volumeChange(newVolume: VolumeEvent, oldVolume: VolumeEvent): void;\n /**\n * Handler for an event emitted when the player plays to the end of the current source.\n */\n playToEnd(): void;\n /**\n * Handler for an event emitted when the current media source of the player changes.\n */\n sourceChange(newSource: VideoSource, previousSource: VideoSource): void;\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 | {\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 */\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 | null;\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 about the current volume and whether the player is muted.\n */\nexport type VolumeEvent = {\n volume: number;\n isMuted: boolean;\n};\n\n/**\n * Contains information that will be displayed in the now playing notification when the video is playing.\n */\nexport type VideoMetadata = {\n /**\n * The title of the video.\n */\n title?: string;\n /**\n * Secondary text that will be displayed under the title.\n */\n artist?: string;\n};\n\n/**\n * Specifies which type of DRM to use. Android supports Widevine, PlayReady and ClearKey, 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?: { [key: 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"]}
@@ -17,6 +17,9 @@ export default class VideoPlayerWeb extends globalThis.expo.SharedObject<VideoPl
17
17
  _error: PlayerError | null;
18
18
  staysActiveInBackground: boolean;
19
19
  showNowPlayingNotification: boolean;
20
+ currentLiveTimestamp: number | null;
21
+ currentOffsetFromLive: number | null;
22
+ targetOffsetFromLive: number;
20
23
  set muted(value: boolean);
21
24
  get muted(): boolean;
22
25
  set playbackRate(value: number);
@@ -1 +1 @@
1
- {"version":3,"file":"VideoPlayer.web.d.ts","sourceRoot":"","sources":["../src/VideoPlayer.web.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACZ,MAAM,qBAAqB,CAAC;AAE7B,wBAAgB,cAAc,CAC5B,MAAM,EAAE,WAAW,EACnB,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GACpC,WAAW,CAQb;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAK/D;AAED,MAAM,CAAC,OAAO,OAAO,cACnB,SAAQ,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,iBAAiB,CACtD,YAAW,WAAW;gBAEV,MAAM,EAAE,WAAW;IAK/B,GAAG,EAAE,WAAW,CAAQ;IACxB,WAAW,EAAE,WAAW,CAAQ;IAChC,cAAc,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAa;IAClD,WAAW,EAAE,GAAG,CAAC,2BAA2B,CAAC,CAAa;IAC1D,OAAO,EAAE,OAAO,CAAS;IACzB,MAAM,EAAE,OAAO,CAAS;IACxB,OAAO,EAAE,MAAM,CAAK;IACpB,KAAK,EAAE,OAAO,CAAS;IACvB,aAAa,EAAE,MAAM,CAAO;IAC5B,eAAe,EAAE,OAAO,CAAQ;IAChC,OAAO,EAAE,iBAAiB,CAAU;IACpC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAQ;IAClC,uBAAuB,EAAE,OAAO,CAAS;IACzC,0BAA0B,EAAE,OAAO,CAAS;IAE5C,IAAI,KAAK,CAAC,KAAK,EAAE,OAAO,EAKvB;IAED,IAAI,KAAK,IAAI,OAAO,CAEnB;IAED,IAAI,YAAY,CAAC,KAAK,EAAE,MAAM,EAI7B;IAED,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,IAAI,MAAM,CAAC,KAAK,EAAE,MAAM,EAKvB;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,EAKtB;IAED,IAAI,IAAI,IAAI,OAAO,CAElB;IAED,IAAI,WAAW,IAAI,MAAM,CAGxB;IAED,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,EAI5B;IAED,IAAI,QAAQ,IAAI,MAAM,CAGrB;IAED,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED,IAAI,cAAc,CAAC,KAAK,EAAE,OAAO,EAKhC;IAED,IAAI,MAAM,IAAI,iBAAiB,CAE9B;IAED,OAAO,KAAK,MAAM,QAUjB;IAED,cAAc,CAAC,KAAK,EAAE,gBAAgB;IActC,gBAAgB,CAAC,KAAK,EAAE,gBAAgB;IAIxC,cAAc,CACZ,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,QAAQ,EACtB,eAAe,EAAE,2BAA2B,GAC3C,IAAI;IAYP,gBAAgB,CACd,KAAK,EAAE,gBAAgB,EACvB,YAAY,EAAE,YAAY,EAC1B,eAAe,EAAE,2BAA2B;IAe9C,IAAI,IAAI,IAAI;IAMZ,KAAK,IAAI,IAAI;IAMb,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAmBlC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAM7B,MAAM,IAAI,IAAI;IAQd,0BAA0B,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;IAezD;;;OAGG;IACH,SAAS,CAAC,SAAS,SAAS,MAAM,iBAAiB,EACjD,WAAW,EAAE,gBAAgB,EAC7B,SAAS,EAAE,SAAS,EACpB,GAAG,IAAI,EAAE,UAAU,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,GAChD,IAAI;IAOP,aAAa,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;CAiF7C"}
1
+ {"version":3,"file":"VideoPlayer.web.d.ts","sourceRoot":"","sources":["../src/VideoPlayer.web.tsx"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { useMemo } from 'react';
2
+ import resolveAssetSource from './resolveAssetSource';
2
3
  export function useVideoPlayer(source, setup) {
3
4
  const parsedSource = typeof source === 'string' ? { uri: source } : source;
4
5
  return useMemo(() => {
@@ -8,9 +9,15 @@ export function useVideoPlayer(source, setup) {
8
9
  }, [JSON.stringify(source)]);
9
10
  }
10
11
  export function getSourceUri(source) {
11
- if (typeof source == 'string') {
12
+ if (typeof source === 'string') {
12
13
  return source;
13
14
  }
15
+ if (typeof source === 'number') {
16
+ return resolveAssetSource(source)?.uri ?? null;
17
+ }
18
+ if (typeof source?.assetId === 'number' && !source?.uri) {
19
+ return resolveAssetSource(source.assetId)?.uri ?? null;
20
+ }
14
21
  return source?.uri ?? null;
15
22
  }
16
23
  export default class VideoPlayerWeb extends globalThis.expo.SharedObject {
@@ -32,6 +39,9 @@ export default class VideoPlayerWeb extends globalThis.expo.SharedObject {
32
39
  _error = null;
33
40
  staysActiveInBackground = false; // Not supported on web. Dummy to match the interface.
34
41
  showNowPlayingNotification = false; // Not supported on web. Dummy to match the interface.
42
+ currentLiveTimestamp = null; // Not supported on web. Dummy to match the interface.
43
+ currentOffsetFromLive = null; // Not supported on web. Dummy to match the interface.
44
+ targetOffsetFromLive = 0; // Not supported on web. Dummy to match the interface.
35
45
  set muted(value) {
36
46
  this._mountedVideos.forEach((video) => {
37
47
  video.muted = value;