@symbiote-native/video 0.0.0 → 0.1.0
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 +208 -35
- package/build/svelte/use-video-player.svelte.js +5 -1
- package/build-ngc/angular/video-airplay-button/index.js.map +1 -1
- package/build-ngc/angular/video-view/index.js.map +1 -1
- package/build-ngc/core/native-module.js.map +1 -1
- package/build-ngc/core/video-player-controller.js.map +1 -1
- package/build-ngc/core/video-view.js.map +1 -1
- package/native-link.json +3 -1
- package/package.json +15 -15
- package/src/angular/video-airplay-button/index.ts +3 -4
- package/src/angular/video-view/index.ts +3 -2
- package/src/core/native-module.ts +2 -3
- package/src/core/player-types.ts +6 -10
- package/src/core/video-player-controller.ts +4 -1
- package/src/core/video-view.ts +15 -3
- package/src/react/video-view.ts +3 -1
- package/src/svelte/use-video-player.svelte.ts +5 -1
- package/src/vue/use-video-player.ts +3 -1
- package/src/vue/video-view.ts +4 -1
package/README.md
CHANGED
|
@@ -4,17 +4,79 @@
|
|
|
4
4
|
[SymbioteNative](../../README.md) adapter: React, Vue, Svelte, Solid and Angular. The player, the
|
|
5
5
|
view, thumbnails, the video cache and the AirPlay button.
|
|
6
6
|
|
|
7
|
+
Built the same way as [`@symbiote-native/clipboard`](../clipboard)'s `ClipboardPasteButton`, an
|
|
8
|
+
`expo-modules-core` native view reached through `requireNativeViewManager` (see the
|
|
9
|
+
`symbiote-expo-native-module` project skill: why `expo-modules-core` is depended on directly and never
|
|
10
|
+
the `expo` meta-package, why the upstream JS is hand-ported into `core/`, and how autolinking finds the
|
|
11
|
+
native module). One render function in `core/` returns a descriptor and every adapter only turns it into
|
|
12
|
+
its own element.
|
|
13
|
+
|
|
7
14
|
## Install
|
|
8
15
|
|
|
16
|
+
**New app:**
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx @symbiote-native/cli new my-app --video
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Existing SymbioteNative app:**
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx @symbiote-native/cli add --video
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Either way: installs `@symbiote-native/video` and wires the native autolinking automatically,
|
|
29
|
+
see [`@symbiote-native/cli`](../cli).
|
|
30
|
+
|
|
31
|
+
<details>
|
|
32
|
+
<summary>Manual install (no CLI - installing and wiring native autolinking by hand)</summary>
|
|
33
|
+
|
|
9
34
|
```bash
|
|
10
|
-
|
|
11
|
-
|
|
35
|
+
npm install @symbiote-native/video
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Depends on `expo-video` and `expo-modules-core` directly (regular dependencies, pinned to exact
|
|
39
|
+
versions, since this package's `core/` is hand-ported against one specific native API shape).
|
|
40
|
+
Never install `expo-video` yourself, and never add the `expo` package to this project: it bundles
|
|
41
|
+
its own Metro/Babel pipeline that conflicts with this project's own.
|
|
42
|
+
|
|
43
|
+
### Required one-time step: native autolinking wiring
|
|
44
|
+
|
|
45
|
+
`expo-video`'s native code is discovered by `expo-modules-autolinking`, not by RN's own
|
|
46
|
+
`react-native.config.cjs` mechanism. Wire it into the native host app **once**, it covers this package
|
|
47
|
+
and every other `expo-modules-core` package with zero further changes. The steps and the
|
|
48
|
+
mechanics behind them are in the `symbiote-expo-native-module` skill and in
|
|
49
|
+
[`@symbiote-native/network`](../network)'s README; the reference app is `examples/expo-react`.
|
|
50
|
+
|
|
51
|
+
### Background playback and Picture in Picture
|
|
52
|
+
|
|
53
|
+
The package writes nothing by default. For iOS add `audio` to `UIBackgroundModes` in `Info.plist`.
|
|
54
|
+
For Android choose the "Background video playback" bundle (the foreground service and its permissions),
|
|
55
|
+
and give the main activity `android:supportsPictureInPicture="true"` for Picture in Picture.
|
|
56
|
+
|
|
57
|
+
</details>
|
|
58
|
+
|
|
59
|
+
## Shape
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
src/core/ video-player.ts and video-player-controller.ts (the player and its lifecycle over
|
|
63
|
+
the engine's createJsonKeyedResourceController), video-view.ts
|
|
64
|
+
(createVideoView(getNode)), video-airplay-button.ts, video-source.ts,
|
|
65
|
+
video-module.ts (cache and Picture in Picture functions), video-thumbnail.ts.
|
|
66
|
+
src/react/ @symbiote-native/video/react - VideoView, useVideoPlayer
|
|
67
|
+
src/vue/ @symbiote-native/video/vue - VideoView, useVideoPlayer (a computed ref)
|
|
68
|
+
src/svelte/ @symbiote-native/video/svelte - VideoView, useVideoPlayer ({ current })
|
|
69
|
+
src/solid/ @symbiote-native/video/solid - VideoView, useVideoPlayer (an accessor)
|
|
70
|
+
src/angular/ @symbiote-native/video/angular - VideoView, injectVideoPlayer (a signal)
|
|
12
71
|
```
|
|
13
72
|
|
|
14
|
-
|
|
15
|
-
|
|
73
|
+
`VideoView` is one component per platform (`VideoView` on iOS, `SurfaceVideoView` or `TextureVideoView` on
|
|
74
|
+
Android), and the view functions follow the surface the last render chose. `VideoAirPlayButton` is a plain
|
|
75
|
+
view off iOS. `useEvent` and `useEventListener` come from each adapter, not from this package.
|
|
16
76
|
|
|
17
|
-
##
|
|
77
|
+
## Use it
|
|
78
|
+
|
|
79
|
+
### React
|
|
18
80
|
|
|
19
81
|
```tsx
|
|
20
82
|
import { VideoView, useVideoPlayer } from '@symbiote-native/video/react';
|
|
@@ -27,42 +89,153 @@ const player = useVideoPlayer('https://example.com/video.mp4', player => {
|
|
|
27
89
|
<VideoView player={player} style={{ width: 320, height: 180 }} nativeControls />;
|
|
28
90
|
```
|
|
29
91
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
92
|
+
### Vue
|
|
93
|
+
|
|
94
|
+
```vue
|
|
95
|
+
<script setup lang="ts">
|
|
96
|
+
import { VideoView, useVideoPlayer } from '@symbiote-native/video/vue';
|
|
97
|
+
|
|
98
|
+
const player = useVideoPlayer(
|
|
99
|
+
() => 'https://example.com/video.mp4',
|
|
100
|
+
instance => {
|
|
101
|
+
instance.loop = true;
|
|
102
|
+
instance.play();
|
|
103
|
+
},
|
|
104
|
+
);
|
|
105
|
+
</script>
|
|
106
|
+
|
|
107
|
+
<template>
|
|
108
|
+
<VideoView :player="player" :style="{ width: 320, height: 180 }" :nativeControls="true" />
|
|
109
|
+
</template>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Angular
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import { Component } from '@angular/core';
|
|
116
|
+
import { VideoView, injectVideoPlayer } from '@symbiote-native/video/angular';
|
|
117
|
+
|
|
118
|
+
@Component({
|
|
119
|
+
standalone: true,
|
|
120
|
+
imports: [VideoView],
|
|
121
|
+
template: `
|
|
122
|
+
<VideoView [player]="player()" [style]="{ width: 320, height: 180 }" [nativeControls]="true" />
|
|
123
|
+
`,
|
|
124
|
+
})
|
|
125
|
+
export class Clip {
|
|
126
|
+
readonly player = injectVideoPlayer(
|
|
127
|
+
() => 'https://example.com/video.mp4',
|
|
128
|
+
instance => {
|
|
129
|
+
instance.loop = true;
|
|
130
|
+
instance.play();
|
|
131
|
+
},
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
```
|
|
35
135
|
|
|
36
|
-
|
|
37
|
-
| ------- | ----------------------------------------------------------------------- |
|
|
38
|
-
| React | `useVideoPlayer(source, setup?, options?)` |
|
|
39
|
-
| Vue | `useVideoPlayer(source, setup?, options?)`, returns a computed ref |
|
|
40
|
-
| Solid | `useVideoPlayer(() => source, setup?, () => options)`, returns an accessor |
|
|
41
|
-
| Svelte | `useVideoPlayer(() => source, setup?, () => options).current` |
|
|
42
|
-
| Angular | `injectVideoPlayer(() => source, setup?, () => options)`, a signal |
|
|
136
|
+
### Svelte
|
|
43
137
|
|
|
44
|
-
|
|
138
|
+
```svelte
|
|
139
|
+
<script lang="ts">
|
|
140
|
+
import { VideoView, useVideoPlayer } from '@symbiote-native/video/svelte';
|
|
45
141
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
142
|
+
const player = useVideoPlayer(
|
|
143
|
+
() => 'https://example.com/video.mp4',
|
|
144
|
+
instance => {
|
|
145
|
+
instance.loop = true;
|
|
146
|
+
instance.play();
|
|
147
|
+
},
|
|
148
|
+
);
|
|
149
|
+
</script>
|
|
49
150
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
thumbnails. `VideoAirPlayButton` is the iOS route picker and a plain view elsewhere.
|
|
151
|
+
<VideoView player={player.current} style={{ width: 320, height: 180 }} nativeControls />
|
|
152
|
+
```
|
|
53
153
|
|
|
54
|
-
|
|
154
|
+
### Solid
|
|
55
155
|
|
|
56
|
-
|
|
57
|
-
|
|
156
|
+
```tsx
|
|
157
|
+
import { VideoView, useVideoPlayer } from '@symbiote-native/video/solid';
|
|
58
158
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
159
|
+
const player = useVideoPlayer(
|
|
160
|
+
() => 'https://example.com/video.mp4',
|
|
161
|
+
instance => {
|
|
162
|
+
instance.loop = true;
|
|
163
|
+
instance.play();
|
|
164
|
+
},
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
<VideoView player={player()} style={{ width: 320, height: 180 }} nativeControls />;
|
|
168
|
+
```
|
|
63
169
|
|
|
64
|
-
##
|
|
170
|
+
## API
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
useVideoPlayer(source, setup?, options?): VideoPlayer // released on unmount, per adapter see Shape
|
|
174
|
+
createVideoPlayer(source, options?): VideoPlayer // you call player.release()
|
|
175
|
+
|
|
176
|
+
<VideoView player nativeControls? contentFit? surfaceType?={'surfaceView' | 'textureView'} // Android
|
|
177
|
+
allowsPictureInPicture? ... />
|
|
178
|
+
<VideoAirPlayButton ... /> // plain view off iOS
|
|
179
|
+
|
|
180
|
+
isPictureInPictureSupported(): boolean
|
|
181
|
+
setVideoCacheSizeAsync(sizeBytes): Promise<void> clearVideoCacheAsync(): Promise<void>
|
|
182
|
+
getCurrentVideoCacheSize(): number // all three work only while no player exists
|
|
183
|
+
```
|
|
65
184
|
|
|
66
|
-
The
|
|
67
|
-
|
|
68
|
-
|
|
185
|
+
The player is the native `VideoPlayer` shared object, `{ uri, useCaching }` sources enable the cache.
|
|
186
|
+
Ported from upstream's `VideoPlayer.ts`, `VideoView.tsx`, `VideoAirPlayButton.tsx` and `index.ts`.
|
|
187
|
+
|
|
188
|
+
## Notes
|
|
189
|
+
|
|
190
|
+
- **One player, one `VideoView` on Android.** Mounting several views with the same player does not
|
|
191
|
+
work there, a platform limitation. iOS accepts it. Create a player per visible view, or move the
|
|
192
|
+
player to the new view only after the old one is gone.
|
|
193
|
+
- **`useVideoPlayer` releases the player for you.** It disposes of it when the component unmounts,
|
|
194
|
+
and a changed source or options make a new one. A player from `createVideoPlayer` is yours:
|
|
195
|
+
call `release()` when you are done, or it leaks.
|
|
196
|
+
- **Android renders on a surface by default.** `surfaceType="surfaceView"` is the efficient choice,
|
|
197
|
+
`surfaceType="textureView"` composes like an ordinary view and costs more performance.
|
|
198
|
+
- **Caching is per source.** Set `useCaching: true` on the source object. The cache is persistent
|
|
199
|
+
and trimmed least-recently-used, 1 GB by default, and works offline for what it holds.
|
|
200
|
+
iOS cannot cache HLS, and DRM-protected video is not cached on either platform.
|
|
201
|
+
- **The cache functions work only while no player exists**, so call `setVideoCacheSizeAsync` and
|
|
202
|
+
`clearVideoCacheAsync` before you create one.
|
|
203
|
+
- **`useEvent` and `useEventListener` come from the adapter**, not from this package. See the aside
|
|
204
|
+
above.
|
|
205
|
+
- **Not ported.** The web build. `useEvent` / `useEventListener` ship with the `expo` package upstream, here the adapters export them for any emitter (`useEvent(player, 'statusChange')`, `injectEvent` in Angular, `@symbiote-native/svelte/runes/use-event` in Svelte). Upstream ships no tests, the suites here are written against its behavior.
|
|
206
|
+
|
|
207
|
+
## Common questions
|
|
208
|
+
|
|
209
|
+
- **Black screen on Android.** One player in several `VideoView`s does not work there. Keep one view
|
|
210
|
+
per player and unmount the old view first.
|
|
211
|
+
- **Overlap or out of bounds on Android.** An ExoPlayer bug with `contentFit="cover"`. Set
|
|
212
|
+
`surfaceType="textureView"`.
|
|
213
|
+
- **`zIndex` ignored on Android.** The default surface draws above its surroundings. Try
|
|
214
|
+
`surfaceType="textureView"`.
|
|
215
|
+
- **Touch handlers do not fire on Android.** Put the handler on a wrapping view.
|
|
216
|
+
- **Not released.** `useVideoPlayer` releases on unmount, a player from `createVideoPlayer` needs
|
|
217
|
+
`player.release()`.
|
|
218
|
+
- **Fullscreen will not rotate on Android.** It follows the app's orientation lock, unlock it with
|
|
219
|
+
`@symbiote-native/screen-orientation`.
|
|
220
|
+
- **Caching.** `{ uri, useCaching: true }`. 1 GB by default, not for HLS on iOS or DRM. The cache
|
|
221
|
+
functions work only while no player exists.
|
|
222
|
+
|
|
223
|
+
Sources: [Expo docs: Video](https://docs.expo.dev/versions/latest/sdk/video/),
|
|
224
|
+
[expo/expo#35012](https://github.com/expo/expo/issues/35012),
|
|
225
|
+
[expo/expo#31248](https://github.com/expo/expo/issues/31248),
|
|
226
|
+
[expo/expo#31722](https://github.com/expo/expo/issues/31722),
|
|
227
|
+
[expo/expo#30275](https://github.com/expo/expo/issues/30275),
|
|
228
|
+
[expo/expo#34630](https://github.com/expo/expo/issues/34630),
|
|
229
|
+
[expo/expo#33804](https://github.com/expo/expo/issues/33804),
|
|
230
|
+
[expo/expo#9899](https://github.com/expo/expo/issues/9899).
|
|
231
|
+
|
|
232
|
+
## Test it
|
|
233
|
+
|
|
234
|
+
No device is needed for the logic. The core tests (`src/core/*.test.ts`) replace `expo-modules-core`
|
|
235
|
+
(`requireNativeViewManager`, `Platform`) and assert the descriptor a render function returns. The
|
|
236
|
+
adapter tests (`src/{react,vue,solid,angular}/**/*.test.*`, `vitest`) render the real component over the
|
|
237
|
+
recording Fabric with an injected view config and assert the committed payload, and Svelte's
|
|
238
|
+
`*.smoke.test.ts` compile the `.svelte` files and mount them. Painting itself is verified on a device in
|
|
239
|
+
the six `examples/expo-*` canary apps (`examples/expo-react`, `examples/expo-vue-sfc`,
|
|
240
|
+
`examples/expo-vue-tsx`, `examples/expo-svelte`, `examples/expo-solid`, `examples/expo-angular`), see the
|
|
241
|
+
parent [README](../../README.md).
|
|
@@ -7,5 +7,9 @@ const usePlayerResource = createResourceHook(createVideoPlayerController);
|
|
|
7
7
|
* `setup` runs once on each new player, a changed source or options make a new one
|
|
8
8
|
*/
|
|
9
9
|
export function useVideoPlayer(getSource, setup, getPlayerBuilderOptions = () => undefined) {
|
|
10
|
-
return usePlayerResource(() => [
|
|
10
|
+
return usePlayerResource(() => [
|
|
11
|
+
getSource(),
|
|
12
|
+
getPlayerBuilderOptions(),
|
|
13
|
+
setup,
|
|
14
|
+
]);
|
|
11
15
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/angular/video-airplay-button/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE5E,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;;AAGtD,oFAAoF;AACpF,MAAM,cAAc,GAAG;IACrB,MAAM;IACN,YAAY;IACZ,wBAAwB;IACxB,yBAAyB;IACzB,uBAAuB;CACf,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/angular/video-airplay-button/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE5E,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;;AAGtD,oFAAoF;AACpF,MAAM,cAAc,GAAG;IACrB,MAAM;IACN,YAAY;IACZ,wBAAwB;IACxB,yBAAyB;IACzB,uBAAuB;CACf,CAAC;AAOX,gFAAgF;AAWhF,MAAM,OAAO,kBAAmB,SAAQ,cAAc;IACxB,SAAS,GAAG,cAAc,CAAC;IAEpC,UAAU,CAAC,KAAa;QACzC,OAAO,wBAAwB,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC;uGALU,kBAAkB;2FAAlB,kBAAkB,+SAJnB;;IAER,4DALQ,gBAAgB;;2FAOf,kBAAkB;kBAV9B,SAAS;mBAAC;oBACT,QAAQ,EAAE,oBAAoB;oBAC9B,UAAU,EAAE,IAAI;oBAChB,OAAO,EAAE,CAAC,gBAAgB,CAAC;oBAC3B,MAAM,EAAE,CAAC,GAAG,cAAc,CAAC;oBAC3B,eAAe,EAAE,uBAAuB,CAAC,MAAM;oBAC/C,QAAQ,EAAE;;IAER;iBACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/angular/video-view/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE5E,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;;AAG7C,oFAAoF;AACpF,MAAM,iBAAiB,GAAG;IACxB,QAAQ;IACR,gBAAgB;IAChB,YAAY;IACZ,mBAAmB;IACnB,gBAAgB;IAChB,wBAAwB;IACxB,eAAe;IACf,aAAa;IACb,iBAAiB;IACjB,yBAAyB;IACzB,wBAAwB;IACxB,wBAAwB;IACxB,qCAAqC;IACrC,0BAA0B;IAC1B,mBAAmB;IACnB,kBAAkB;IAClB,oBAAoB;IACpB,eAAe;CACP,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/angular/video-view/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE5E,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;;AAG7C,oFAAoF;AACpF,MAAM,iBAAiB,GAAG;IACxB,QAAQ;IACR,gBAAgB;IAChB,YAAY;IACZ,mBAAmB;IACnB,gBAAgB;IAChB,wBAAwB;IACxB,eAAe;IACf,aAAa;IACb,iBAAiB;IACjB,yBAAyB;IACzB,wBAAwB;IACxB,wBAAwB;IACxB,qCAAqC;IACrC,0BAA0B;IAC1B,mBAAmB;IACnB,kBAAkB;IAClB,oBAAoB;IACpB,eAAe;CACP,CAAC;AAOX,iDAAiD;AAWjD,MAAM,OAAO,SAAU,SAAQ,cAAc;IAC1B,IAAI,GAAG,eAAe,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IAE/D,sFAAsF;IAC7E,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC;IACnD,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC;IACjD,qBAAqB,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAAC;IAC/D,oBAAoB,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;IAE1C,SAAS,GAAG,iBAAiB,CAAC;IAEvC,UAAU,CAAC,KAAa;QACzC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;uGAbU,SAAS;2FAAT,SAAS,o0BAJV;;IAER,4DALQ,gBAAgB;;2FAOf,SAAS;kBAVrB,SAAS;mBAAC;oBACT,QAAQ,EAAE,WAAW;oBACrB,UAAU,EAAE,IAAI;oBAChB,OAAO,EAAE,CAAC,gBAAgB,CAAC;oBAC3B,MAAM,EAAE,CAAC,GAAG,iBAAiB,CAAC;oBAC9B,eAAe,EAAE,uBAAuB,CAAC,MAAM;oBAC/C,QAAQ,EAAE;;IAER;iBACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAYhD,MAAM,CAAC,MAAM,SAAS,
|
|
1
|
+
{"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAYhD,MAAM,CAAC,MAAM,SAAS,GACpB,mBAAmB,CAAqB,iBAAiB,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video-player-controller.js","sourceRoot":"","sources":["../../src/core/video-player-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iCAAiC,EAAE,MAAM,yBAAyB,CAAC;AAO5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"video-player-controller.js","sourceRoot":"","sources":["../../src/core/video-player-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iCAAiC,EAAE,MAAM,yBAAyB,CAAC;AAO5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAqB7C,gFAAgF;AAChF,MAAM,UAAU,2BAA2B;IACzC,+EAA+E;IAC/E,IAAI,WAAwD,CAAC;IAC7D,MAAM,UAAU,GAAG,iCAAiC,CAIlD,GAAG,CAAC,EAAE;QACJ,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QAC1D,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC;QACtB,OAAO,MAAM,CAAC;IAChB,CAAC,EACD,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,CAC3B,CAAC;IACF,OAAO;QACL,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YAClC,WAAW,GAAG,KAAK,CAAC;YACpB,OAAO,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QACtE,CAAC;QACD,YAAY,EAAE,UAAU,CAAC,YAAY;QACrC,OAAO,EAAE,UAAU,CAAC,OAAO;KAC5B,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video-view.js","sourceRoot":"","sources":["../../src/core/video-view.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,OAAO,EACL,QAAQ,EACR,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,EAAE,EAAE,MAAM,6BAA6B,CAAC;AAEjD,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,YAAY,EACZ,UAAU,GACX,MAAM,yBAAyB,CAAC;AAKjC,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C,MAAM,UAAU,GAAG;IACjB,KAAK,EAAE,gBAAgB,CAAC,KAAK;IAC7B,OAAO,EAAE,gBAAgB,CAAC,OAAO;IACjC,OAAO,EAAE,gBAAgB,CAAC,OAAO;CACe,CAAC;AAEnD,2EAA2E;AAC3E,MAAM,KAAK,GAAG,qBAAqB,CACjC,wBAAwB,EACxB,iBAAiB,EACjB,UAAU,CACX,CAAC;AAEF,MAAM,OAAO,GAAiD;IAC5D,KAAK,EAAE,qBAAqB,
|
|
1
|
+
{"version":3,"file":"video-view.js","sourceRoot":"","sources":["../../src/core/video-view.ts"],"names":[],"mappings":"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"}
|
package/native-link.json
CHANGED
|
@@ -22,7 +22,9 @@
|
|
|
22
22
|
{
|
|
23
23
|
"name": "expo.modules.video.playbackService.ExpoVideoPlaybackService",
|
|
24
24
|
"foregroundServiceType": "mediaPlayback",
|
|
25
|
-
"intentFilterActions": [
|
|
25
|
+
"intentFilterActions": [
|
|
26
|
+
"androidx.media3.session.MediaSessionService"
|
|
27
|
+
]
|
|
26
28
|
}
|
|
27
29
|
]
|
|
28
30
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/video",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "expo-video wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, Svelte, Solid, and Angular adapters. Video playback: the player, the view, thumbnails, the cache and the AirPlay button.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"expo-video": "57.0.5",
|
|
69
69
|
"expo-modules-core": "57.0.20",
|
|
70
|
-
"@symbiote-native/components": "3.
|
|
70
|
+
"@symbiote-native/components": "3.2.0"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
73
|
"@angular/core": ">=20",
|
|
@@ -77,12 +77,12 @@
|
|
|
77
77
|
"solid-js": ">=1.9.0",
|
|
78
78
|
"svelte": ">=5.56.0",
|
|
79
79
|
"vue": ">=3.5.0",
|
|
80
|
-
"@symbiote-native/
|
|
81
|
-
"@symbiote-native/
|
|
82
|
-
"@symbiote-native/
|
|
83
|
-
"@symbiote-native/
|
|
84
|
-
"@symbiote-native/
|
|
85
|
-
"@symbiote-native/solid": "^3.
|
|
80
|
+
"@symbiote-native/angular": "^3.3.0",
|
|
81
|
+
"@symbiote-native/engine": "^1.6.0",
|
|
82
|
+
"@symbiote-native/react": "^3.3.0",
|
|
83
|
+
"@symbiote-native/vue": "^3.3.0",
|
|
84
|
+
"@symbiote-native/svelte": "^3.2.0",
|
|
85
|
+
"@symbiote-native/solid": "^3.2.0"
|
|
86
86
|
},
|
|
87
87
|
"peerDependenciesMeta": {
|
|
88
88
|
"@symbiote-native/angular": {
|
|
@@ -130,13 +130,13 @@
|
|
|
130
130
|
"solid-js": "^1.9.14",
|
|
131
131
|
"svelte": "^5.56.0",
|
|
132
132
|
"typescript": "~6.0.0",
|
|
133
|
-
"@symbiote-native/
|
|
134
|
-
"@symbiote-native/
|
|
135
|
-
"@symbiote-native/
|
|
136
|
-
"@symbiote-native/
|
|
137
|
-
"@symbiote-native/
|
|
138
|
-
"@symbiote-native/
|
|
139
|
-
"@symbiote-native/
|
|
133
|
+
"@symbiote-native/angular": "3.3.0",
|
|
134
|
+
"@symbiote-native/engine": "1.6.0",
|
|
135
|
+
"@symbiote-native/react": "3.3.0",
|
|
136
|
+
"@symbiote-native/solid": "3.2.0",
|
|
137
|
+
"@symbiote-native/svelte": "3.2.0",
|
|
138
|
+
"@symbiote-native/vue": "3.3.0",
|
|
139
|
+
"@symbiote-native/test-utils": "0.4.7"
|
|
140
140
|
},
|
|
141
141
|
"scripts": {
|
|
142
142
|
"typecheck": "tsc --build",
|
|
@@ -14,10 +14,9 @@ const AIRPLAY_INPUTS = [
|
|
|
14
14
|
] as const;
|
|
15
15
|
|
|
16
16
|
// Types the inputs for the template, the decorators that would do it are replaced by `inputs`
|
|
17
|
-
export interface VideoAirPlayButton
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
> {}
|
|
17
|
+
export interface VideoAirPlayButton extends Partial<
|
|
18
|
+
Pick<IVideoAirPlayButtonProps, (typeof AIRPLAY_INPUTS)[number]>
|
|
19
|
+
> {}
|
|
21
20
|
|
|
22
21
|
/** Angular twin of `expo-video`'s `VideoAirPlayButton`, a plain view off iOS */
|
|
23
22
|
@Component({
|
|
@@ -27,8 +27,9 @@ const VIDEO_VIEW_INPUTS = [
|
|
|
27
27
|
] as const;
|
|
28
28
|
|
|
29
29
|
// Types the inputs for the template, the decorators that would do it are replaced by `inputs`
|
|
30
|
-
export interface VideoView
|
|
31
|
-
|
|
30
|
+
export interface VideoView extends Partial<
|
|
31
|
+
Pick<IVideoViewProps, (typeof VIDEO_VIEW_INPUTS)[number]>
|
|
32
|
+
> {}
|
|
32
33
|
|
|
33
34
|
/** Angular twin of `expo-video`'s `VideoView` */
|
|
34
35
|
@Component({
|
|
@@ -11,6 +11,5 @@ export type INativeVideoModule = {
|
|
|
11
11
|
getCurrentVideoCacheSize(): number;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
export const expoVideo =
|
|
15
|
-
VIDEO_MODULE_NAME
|
|
16
|
-
);
|
|
14
|
+
export const expoVideo =
|
|
15
|
+
requireNativeModule<INativeVideoModule>(VIDEO_MODULE_NAME);
|
package/src/core/player-types.ts
CHANGED
|
@@ -3,11 +3,7 @@ import type { SharedObject, SharedRef } from 'expo-modules-core';
|
|
|
3
3
|
export type IVideoPlayerStatus = 'idle' | 'loading' | 'readyToPlay' | 'error';
|
|
4
4
|
|
|
5
5
|
export type IVideoContentType =
|
|
6
|
-
| '
|
|
7
|
-
| 'progressive'
|
|
8
|
-
| 'hls'
|
|
9
|
-
| 'dash'
|
|
10
|
-
| 'smoothStreaming';
|
|
6
|
+
'auto' | 'progressive' | 'hls' | 'dash' | 'smoothStreaming';
|
|
11
7
|
|
|
12
8
|
export type IVideoDrmType = 'clearkey' | 'fairplay' | 'playready' | 'widevine';
|
|
13
9
|
|
|
@@ -49,10 +45,7 @@ export type IVideoSource = string | number | null | IVideoSourceObject;
|
|
|
49
45
|
export type IVideoPlayerError = { message: string };
|
|
50
46
|
|
|
51
47
|
export type IVideoAudioMixingMode =
|
|
52
|
-
| '
|
|
53
|
-
| 'duckOthers'
|
|
54
|
-
| 'auto'
|
|
55
|
-
| 'doNotMix';
|
|
48
|
+
'mixWithOthers' | 'duckOthers' | 'auto' | 'doNotMix';
|
|
56
49
|
|
|
57
50
|
export type IVideoBufferOptions = {
|
|
58
51
|
/** iOS only, in seconds */
|
|
@@ -146,7 +139,10 @@ export type IVideoPlayerEvents = {
|
|
|
146
139
|
currentOffsetFromLive: number | null;
|
|
147
140
|
bufferedPosition: number;
|
|
148
141
|
}): void;
|
|
149
|
-
sourceChange(payload: {
|
|
142
|
+
sourceChange(payload: {
|
|
143
|
+
source: IVideoSource;
|
|
144
|
+
oldSource?: IVideoSource;
|
|
145
|
+
}): void;
|
|
150
146
|
availableSubtitleTracksChange(payload: {
|
|
151
147
|
availableSubtitleTracks: IVideoSubtitleTrack[];
|
|
152
148
|
oldAvailableSubtitleTracks?: IVideoSubtitleTrack[];
|
|
@@ -20,7 +20,10 @@ export type IVideoPlayerController = {
|
|
|
20
20
|
options?: IVideoPlayerBuilderOptions,
|
|
21
21
|
setup?: (player: VideoPlayer) => void,
|
|
22
22
|
) => VideoPlayer;
|
|
23
|
-
flushDispose: IResourceController<
|
|
23
|
+
flushDispose: IResourceController<
|
|
24
|
+
IVideoPlayerKey,
|
|
25
|
+
VideoPlayer
|
|
26
|
+
>['flushDispose'];
|
|
24
27
|
dispose: IResourceController<IVideoPlayerKey, VideoPlayer>['dispose'];
|
|
25
28
|
};
|
|
26
29
|
|
package/src/core/video-view.ts
CHANGED
|
@@ -35,9 +35,21 @@ const views = defineExpoNativeViews(
|
|
|
35
35
|
);
|
|
36
36
|
|
|
37
37
|
const callers: Record<IVideoViewKey, IExpoViewMethodCaller> = {
|
|
38
|
-
video: defineExpoViewMethods(
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
video: defineExpoViewMethods(
|
|
39
|
+
requireNativeModule,
|
|
40
|
+
VIDEO_MODULE_NAME,
|
|
41
|
+
VIEW_NAMES.video,
|
|
42
|
+
),
|
|
43
|
+
surface: defineExpoViewMethods(
|
|
44
|
+
requireNativeModule,
|
|
45
|
+
VIDEO_MODULE_NAME,
|
|
46
|
+
VIEW_NAMES.surface,
|
|
47
|
+
),
|
|
48
|
+
texture: defineExpoViewMethods(
|
|
49
|
+
requireNativeModule,
|
|
50
|
+
VIDEO_MODULE_NAME,
|
|
51
|
+
VIEW_NAMES.texture,
|
|
52
|
+
),
|
|
41
53
|
};
|
|
42
54
|
|
|
43
55
|
export type IVideoViewHandle = {
|
package/src/react/video-view.ts
CHANGED
|
@@ -28,6 +28,8 @@ export function VideoView({
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/** React twin of `expo-video`'s `VideoAirPlayButton`, a plain view off iOS */
|
|
31
|
-
export function VideoAirPlayButton(
|
|
31
|
+
export function VideoAirPlayButton(
|
|
32
|
+
props: IVideoAirPlayButtonReactProps,
|
|
33
|
+
): ReactElement {
|
|
32
34
|
return descriptorToReact(renderVideoAirPlayButton(props));
|
|
33
35
|
}
|
|
@@ -23,5 +23,9 @@ export function useVideoPlayer(
|
|
|
23
23
|
getPlayerBuilderOptions: () => IVideoPlayerBuilderOptions | undefined = () =>
|
|
24
24
|
undefined,
|
|
25
25
|
): IUseVideoPlayerResult {
|
|
26
|
-
return usePlayerResource(() => [
|
|
26
|
+
return usePlayerResource(() => [
|
|
27
|
+
getSource(),
|
|
28
|
+
getPlayerBuilderOptions(),
|
|
29
|
+
setup,
|
|
30
|
+
]);
|
|
27
31
|
}
|
|
@@ -21,7 +21,9 @@ const usePlayerResource = createResourceHook(createVideoPlayerController);
|
|
|
21
21
|
export function useVideoPlayer(
|
|
22
22
|
source: MaybeRefOrGetter<IVideoSource>,
|
|
23
23
|
setup?: (player: VideoPlayer) => void,
|
|
24
|
-
playerBuilderOptions?: MaybeRefOrGetter<
|
|
24
|
+
playerBuilderOptions?: MaybeRefOrGetter<
|
|
25
|
+
IVideoPlayerBuilderOptions | undefined
|
|
26
|
+
>,
|
|
25
27
|
): ComputedRef<VideoPlayer> {
|
|
26
28
|
return usePlayerResource(() => [
|
|
27
29
|
toValue(source),
|
package/src/vue/video-view.ts
CHANGED
|
@@ -7,7 +7,10 @@ import {
|
|
|
7
7
|
import { createVideoView, renderVideoAirPlayButton } from '../core';
|
|
8
8
|
|
|
9
9
|
/** Vue twin of `expo-video`'s `VideoView` */
|
|
10
|
-
export const VideoView = defineNativeViewComponent(
|
|
10
|
+
export const VideoView = defineNativeViewComponent(
|
|
11
|
+
'VideoView',
|
|
12
|
+
createVideoView,
|
|
13
|
+
);
|
|
11
14
|
|
|
12
15
|
/** Vue twin of `expo-video`'s `VideoAirPlayButton`, a plain view off iOS */
|
|
13
16
|
export const VideoAirPlayButton = defineComponent({
|