@banou/media-player 0.8.19 → 0.8.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -0
- package/dist/index.js +123 -105
- package/dist/react/video-player.d.ts +10 -0
- package/dist/remote/index.d.ts +155 -0
- package/dist/remote/index.js +416 -0
- package/dist/remote/protocol.d.ts +91 -0
- package/package.json +10 -1
- package/src/lib/react/video-player.tsx +42 -1
- package/src/lib/remote/index.ts +709 -0
- package/src/lib/remote/protocol.ts +152 -0
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
import type { ReactNode } from 'react'
|
|
3
3
|
import type { DownloadedRange } from './source-feature'
|
|
4
4
|
import type { DelegatedTracks, ExternalThumbnails, PlayerMedia } from './media'
|
|
5
|
+
import type { ExposePlayerOptions } from '../remote'
|
|
5
6
|
|
|
6
|
-
import { useEffect, useState } from 'react'
|
|
7
|
+
import { useEffect, useRef, useState } from 'react'
|
|
7
8
|
import { css } from '@emotion/react'
|
|
8
9
|
import { useContainerAttach, useMediaAttach } from '@videojs/react'
|
|
9
10
|
|
|
@@ -23,6 +24,15 @@ export type MediaPlayerSource =
|
|
|
23
24
|
|
|
24
25
|
/** Shared by both arms: nothing here depends on who owns the media. */
|
|
25
26
|
type CommonOptions = {
|
|
27
|
+
/**
|
|
28
|
+
* Serve this player to the document that frames this one, so it can be read and driven from there
|
|
29
|
+
* through `mediaPlayer` (from `@banou/media-player/remote`). `true` serves the framing window and
|
|
30
|
+
* nothing else; an options object narrows the embedder by origin or replaces the transport, and may
|
|
31
|
+
* be written inline, since `id`, `origin` and `channel` are watched by value. A `transport` or
|
|
32
|
+
* `signal` is read when the player is served and changing either needs a remount. Off by default, and a
|
|
33
|
+
* no-op in a document nobody frames.
|
|
34
|
+
*/
|
|
35
|
+
expose?: boolean | ExposePlayerOptions
|
|
26
36
|
/**
|
|
27
37
|
* Drawn across the top of the picture, in a layer of its own.
|
|
28
38
|
*
|
|
@@ -192,6 +202,37 @@ const PlayerRoot = ({ options, children }: { options: MediaPlayerOptions, childr
|
|
|
192
202
|
const media = remote?.media ?? video
|
|
193
203
|
useEffect(() => { setMedia?.(media); return () => setMedia?.(null) }, [media, setMedia])
|
|
194
204
|
|
|
205
|
+
// Served after it is attached, and re-served when the media changes: what an embedder drives is
|
|
206
|
+
// whatever this player drives at the time. Keyed on the option's `origin` and `channel`, not on the
|
|
207
|
+
// object: an inline `expose={{ origin }}` is a new object on every render, and re-serving on each
|
|
208
|
+
// would tell the embedder the media was swapped several times a second. `transport` and `signal`
|
|
209
|
+
// are read at serve time and a change to either needs a remount, which the prop's doc says.
|
|
210
|
+
//
|
|
211
|
+
// `id` is watched: a prop whose id changes has to be re-served under the new one, or the document
|
|
212
|
+
// keeps announcing under the old id and an embedder asking for the new one waits for ever.
|
|
213
|
+
//
|
|
214
|
+
// Imported lazily, so `osra` is a dependency of `@banou/media-player/remote` and not of every app
|
|
215
|
+
// that renders a player without ever exposing one.
|
|
216
|
+
const { expose } = options
|
|
217
|
+
const exposeOptions = expose === true ? {} : expose || undefined
|
|
218
|
+
const latestExpose = useRef(exposeOptions)
|
|
219
|
+
latestExpose.current = exposeOptions
|
|
220
|
+
const exposeId = exposeOptions ? exposeOptions.id : undefined
|
|
221
|
+
const exposeOrigin = exposeOptions ? exposeOptions.origin : undefined
|
|
222
|
+
const exposeChannel = exposeOptions ? exposeOptions.channel : undefined
|
|
223
|
+
useEffect(() => {
|
|
224
|
+
if (!expose || !media) return
|
|
225
|
+
let stop: (() => void) | undefined
|
|
226
|
+
let dropped = false
|
|
227
|
+
import('../remote')
|
|
228
|
+
.then(({ exposePlayer }) => {
|
|
229
|
+
if (dropped) return
|
|
230
|
+
stop = exposePlayer(media, latestExpose.current ?? {})
|
|
231
|
+
})
|
|
232
|
+
.catch(() => {})
|
|
233
|
+
return () => { dropped = true; stop?.() }
|
|
234
|
+
}, [media, !!expose, exposeId, exposeOrigin, exposeChannel])
|
|
235
|
+
|
|
195
236
|
// Each of these no-ops on null inputs, which is what a remote arm supplies: it renders no <video>,
|
|
196
237
|
// so there is nothing for them to attach to and nothing to guard at the call site.
|
|
197
238
|
usePlayback(video, canvas, local)
|