@moviie/player-expo 0.4.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/LICENSE +21 -0
- package/README.md +230 -0
- package/app.plugin.cjs +3 -0
- package/dist/cast.cjs +100 -0
- package/dist/cast.cjs.map +1 -0
- package/dist/cast.d.cts +15 -0
- package/dist/cast.d.ts +15 -0
- package/dist/cast.mjs +92 -0
- package/dist/cast.mjs.map +1 -0
- package/dist/chunk-67DJ7NOB.mjs +294 -0
- package/dist/chunk-67DJ7NOB.mjs.map +1 -0
- package/dist/chunk-7U2LKIGU.mjs +12 -0
- package/dist/chunk-7U2LKIGU.mjs.map +1 -0
- package/dist/chunk-BJTO5JO5.mjs +10 -0
- package/dist/chunk-BJTO5JO5.mjs.map +1 -0
- package/dist/index.cjs +3934 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +450 -0
- package/dist/index.d.ts +450 -0
- package/dist/index.mjs +3571 -0
- package/dist/index.mjs.map +1 -0
- package/dist/layout.cjs +217 -0
- package/dist/layout.cjs.map +1 -0
- package/dist/layout.d.cts +20 -0
- package/dist/layout.d.ts +20 -0
- package/dist/layout.mjs +4 -0
- package/dist/layout.mjs.map +1 -0
- package/dist/moviie-cast-adapter-DmSU2u3j.d.cts +53 -0
- package/dist/moviie-cast-adapter-DmSU2u3j.d.ts +53 -0
- package/dist/plugin/with-moviie.cjs +88 -0
- package/dist/plugin/with-moviie.cjs.map +1 -0
- package/dist/plugin/with-moviie.d.cts +52 -0
- package/dist/plugin/with-moviie.d.ts +52 -0
- package/dist/plugin/with-moviie.mjs +76 -0
- package/dist/plugin/with-moviie.mjs.map +1 -0
- package/package.json +134 -0
- package/plugin/validate-options.ts +31 -0
- package/plugin/with-moviie-types.ts +21 -0
- package/plugin/with-moviie.ts +84 -0
- package/src/apply-expo-moviie-endpoints.ts +47 -0
- package/src/cast/google-cast-adapter.ts +111 -0
- package/src/cast/index.ts +12 -0
- package/src/components/controls/moviie-bottom-timeline.tsx +477 -0
- package/src/components/controls/moviie-cast-buttons.tsx +96 -0
- package/src/components/controls/moviie-chrome-edge-gradients.tsx +162 -0
- package/src/components/controls/moviie-controls.tsx +585 -0
- package/src/components/controls/moviie-skin-chrome-context.tsx +374 -0
- package/src/components/controls/moviie-skin-smart-progress.tsx +157 -0
- package/src/components/icons/embed-media-icons.tsx +282 -0
- package/src/components/icons/moviie-embed-brand-mark.tsx +58 -0
- package/src/components/moviie-error-boundary.tsx +80 -0
- package/src/components/moviie-player-error-shell.tsx +143 -0
- package/src/components/moviie-player-loading-shell.tsx +59 -0
- package/src/components/moviie-skin-custom-fullscreen-modal.tsx +232 -0
- package/src/components/moviie-video-props.ts +134 -0
- package/src/components/moviie-video.tsx +568 -0
- package/src/components/moviie-video.web.tsx +167 -0
- package/src/components/overlays/moviie-watermark.tsx +53 -0
- package/src/constants.ts +374 -0
- package/src/hooks/use-moviie-event.ts +103 -0
- package/src/hooks/use-moviie-playback-ended.ts +14 -0
- package/src/hooks/use-moviie-playback-resume-persistence.ts +76 -0
- package/src/hooks/use-moviie-playback.ts +99 -0
- package/src/hooks/use-moviie-player-types.ts +55 -0
- package/src/hooks/use-moviie-player.ts +236 -0
- package/src/hooks/use-moviie-player.web.ts +57 -0
- package/src/hooks/use-moviie-telemetry.ts +133 -0
- package/src/index.ts +90 -0
- package/src/layout.ts +4 -0
- package/src/lib/add-moviie-playback-ended-listener.ts +16 -0
- package/src/lib/build-moviie-branding-marketing-url.ts +14 -0
- package/src/lib/build-moviie-embed-brand-home-url.ts +16 -0
- package/src/lib/build-moviie-skin-layout-metrics.ts +166 -0
- package/src/lib/build-moviie-video-presentation.ts +42 -0
- package/src/lib/build-moviie-watch-embed-url.ts +30 -0
- package/src/lib/cast-adapter-registry.ts +13 -0
- package/src/lib/clamp-unit-interval.ts +3 -0
- package/src/lib/compute-custom-fullscreen-stage-dimensions.ts +39 -0
- package/src/lib/compute-moviie-playback-ended.ts +31 -0
- package/src/lib/compute-playback-buffering.ts +41 -0
- package/src/lib/compute-playback-progress-ratio.ts +33 -0
- package/src/lib/compute-timeline-scrub-commit-time.ts +40 -0
- package/src/lib/custom-fullscreen-native-orientation.ts +88 -0
- package/src/lib/format-playback-clock.ts +27 -0
- package/src/lib/jsx-native-bridge.ts +45 -0
- package/src/lib/map-smart-progress-display-ratio.ts +43 -0
- package/src/lib/moviie-cast-adapter.ts +53 -0
- package/src/lib/moviie-error-display.ts +149 -0
- package/src/lib/moviie-shell-tokens.ts +27 -0
- package/src/lib/moviie-telemetry-callbacks.ts +76 -0
- package/src/lib/optional-status-bar.ts +48 -0
- package/src/lib/partition-moviie-video-host-style.ts +54 -0
- package/src/lib/remember-playback-position-storage.ts +98 -0
- package/src/lib/resolve-moviie-skin-layout-scale.ts +17 -0
- package/src/lib/resolve-orientation-lock-for-rotate-z-deg.ts +21 -0
- package/src/lib/resolve-skin-accent-color.ts +10 -0
- package/src/lib/resolve-web-embed-iframe-src.ts +17 -0
- package/src/lib/warn-once.ts +23 -0
- package/src/platform/native-application-id.ts +10 -0
- package/src/platform/platform-client-info.ts +31 -0
- package/src/playback/fetch-playback-fresh.ts +27 -0
- package/src/playback/playback-memory-cache.ts +52 -0
- package/src/provider/moviie-provider.tsx +99 -0
- package/src/secure-store-viewer-token-store.ts +80 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Moviie
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# @moviie/player-expo
|
|
2
|
+
|
|
3
|
+
React Native / Expo bindings for the Moviie video platform. Re-exports [`@moviie/player-sdk`](https://www.npmjs.com/package/@moviie/player-sdk) and ships `expo-video` integrations (`MoviieVideo`, hooks, telemetry wiring, optional Chromecast adapter).
|
|
4
|
+
|
|
5
|
+
## Quickstart (3 steps)
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# 1. Install (5 mandatory deps)
|
|
9
|
+
pnpm add @moviie/player-expo \
|
|
10
|
+
expo expo-video expo-application expo-screen-orientation \
|
|
11
|
+
react-native-svg react-native-reanimated react-native-gesture-handler \
|
|
12
|
+
react-native-safe-area-context
|
|
13
|
+
|
|
14
|
+
# Optional — gain extra features:
|
|
15
|
+
# pnpm add expo-secure-store # persistent resume position + viewer token
|
|
16
|
+
# pnpm add expo-status-bar # hide status bar in custom fullscreen
|
|
17
|
+
# pnpm add react-native-google-cast # Chromecast support (subpath /cast)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
// 2. app.json — add config plugin
|
|
22
|
+
{
|
|
23
|
+
"expo": {
|
|
24
|
+
"plugins": [
|
|
25
|
+
["@moviie/player-expo", { "backgroundPlayback": true, "pictureInPicture": true }],
|
|
26
|
+
["expo-video", { "supportsBackgroundPlayback": true, "supportsPictureInPicture": true }]
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
// 3. App.tsx
|
|
34
|
+
import { MoviieProvider, MoviieVideo, useMoviiePlayer } from "@moviie/player-expo"
|
|
35
|
+
|
|
36
|
+
function Player({ embedId }: { embedId: string }) {
|
|
37
|
+
const moviie = useMoviiePlayer({ embedId })
|
|
38
|
+
return <MoviieVideo {...moviie} aspectRatio={16 / 9} />
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default function App() {
|
|
42
|
+
return (
|
|
43
|
+
<MoviieProvider publishableKey={process.env.EXPO_PUBLIC_MOVIIE_PUBLISHABLE_KEY}>
|
|
44
|
+
<Player embedId="YOUR-EMBED-UUID" />
|
|
45
|
+
</MoviieProvider>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Run `pnpm expo prebuild && pnpm expo run:ios` (or `run:android`) to compile the
|
|
51
|
+
native module — `expo-video` doesn't ship with Expo Go.
|
|
52
|
+
|
|
53
|
+
### Optional dependencies (graceful degradation)
|
|
54
|
+
|
|
55
|
+
| Package | Without it |
|
|
56
|
+
|---------|------------|
|
|
57
|
+
| `expo-secure-store` | Viewer token stays in memory (lost on app kill); resume position **does not persist** between sessions |
|
|
58
|
+
| `expo-status-bar` | Status bar stays visible during custom fullscreen overlay |
|
|
59
|
+
| `react-native-google-cast` | Chromecast button doesn't appear; player works normally |
|
|
60
|
+
|
|
61
|
+
When a feature requiring an optional package is exercised without that package
|
|
62
|
+
installed, the SDK emits a **single `console.warn`** per session pointing to the
|
|
63
|
+
install command. No crash, no hidden errors.
|
|
64
|
+
|
|
65
|
+
### API endpoint override (dev only)
|
|
66
|
+
|
|
67
|
+
For local development against a non-prod backend, set env vars in `.env.local`
|
|
68
|
+
(Metro inlines any `EXPO_PUBLIC_*` at build time — no `expo-constants` needed):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
EXPO_PUBLIC_MOVIIE_API_BASE_URL=http://localhost:3000/api/v1
|
|
72
|
+
EXPO_PUBLIC_MOVIIE_EVENTS_BASE_URL=http://localhost:3000/telemetry/v1
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Overrides only apply when `__DEV__ === true`. Production builds always use the
|
|
76
|
+
default `https://api.moviie.ai/v1`.
|
|
77
|
+
|
|
78
|
+
Full docs: [docs.moviie.ai/player-expo](https://docs.moviie.ai/player-expo) ·
|
|
79
|
+
FAQ: [docs.moviie.ai/player-expo/faq](https://docs.moviie.ai/player-expo/faq) ·
|
|
80
|
+
Migration: [docs.moviie.ai/player-expo/migration](https://docs.moviie.ai/player-expo/migration)
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Requirements
|
|
85
|
+
|
|
86
|
+
- Expo SDK 52+
|
|
87
|
+
- `expo-video` 2+
|
|
88
|
+
- Dev client recommended (`expo-video` does not run in Expo Go)
|
|
89
|
+
- **Publishable API key** (`mvi_pub_*`) for `MoviieClient.getPlayback` — set `EXPO_PUBLIC_MOVIIE_PUBLISHABLE_KEY` (or equivalent) before fetching playback
|
|
90
|
+
|
|
91
|
+
## App config
|
|
92
|
+
|
|
93
|
+
Register **`@moviie/player-expo`** so native manifests receive background audio and PiP-related defaults. Pair it with the **`expo-video`** plugin toggles from the [Expo Video docs](https://docs.expo.dev/versions/latest/sdk/video/). After changing plugin options, run **`expo prebuild`** (or trigger an EAS native build) so **Info.plist** and **AndroidManifest.xml** pick up the updates.
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"expo": {
|
|
98
|
+
"plugins": [
|
|
99
|
+
[
|
|
100
|
+
"@moviie/player-expo",
|
|
101
|
+
{
|
|
102
|
+
"backgroundPlayback": true,
|
|
103
|
+
"pictureInPicture": true
|
|
104
|
+
}
|
|
105
|
+
],
|
|
106
|
+
[
|
|
107
|
+
"expo-video",
|
|
108
|
+
{
|
|
109
|
+
"supportsBackgroundPlayback": true,
|
|
110
|
+
"supportsPictureInPicture": true
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Plugin options:**
|
|
119
|
+
|
|
120
|
+
- **`backgroundPlayback`**: iOS `UIBackgroundModes` → `audio`
|
|
121
|
+
- **`pictureInPicture`**: iOS adds `picture-in-picture` plus `audio`; Android main activity `supportsPictureInPicture` + `resizeableActivity`
|
|
122
|
+
|
|
123
|
+
API endpoint overrides são **dev only** via env vars `EXPO_PUBLIC_MOVIIE_API_BASE_URL` / `EXPO_PUBLIC_MOVIIE_EVENTS_BASE_URL` (não expostos no plugin).
|
|
124
|
+
|
|
125
|
+
## Usage
|
|
126
|
+
|
|
127
|
+
See the internal playground app `apps/playground-expo` and product docs at [docs.moviie.ai](https://docs.moviie.ai).
|
|
128
|
+
|
|
129
|
+
Import the SDK (re-exported from `@moviie/player-sdk`) and Expo bindings from this package:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { MoviieClient, MOVIIE_PLAYER_EXPO_PKG_VERSION } from "@moviie/player-expo"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Fim da reprodução (`ended`)
|
|
136
|
+
|
|
137
|
+
O expo-video emite `playToEnd` quando o vídeo termina naturalmente. Este pacote alinha isso a `PLAYER_API_EVENTS.ENDED`.
|
|
138
|
+
|
|
139
|
+
**React — hook dedicado:**
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
import { useMoviiePlaybackEnded } from "@moviie/player-expo"
|
|
143
|
+
|
|
144
|
+
useMoviiePlaybackEnded(player, () => {
|
|
145
|
+
// vídeo chegou ao fim
|
|
146
|
+
})
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**React — evento genérico:**
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import { PLAYER_API_EVENTS, useMoviieEvent } from "@moviie/player-expo"
|
|
153
|
+
|
|
154
|
+
useMoviieEvent(player, PLAYER_API_EVENTS.ENDED, () => {
|
|
155
|
+
// vídeo chegou ao fim
|
|
156
|
+
})
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Fora de React — subscription:**
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import { addMoviiePlaybackEndedListener } from "@moviie/player-expo"
|
|
163
|
+
|
|
164
|
+
const sub = addMoviiePlaybackEndedListener(player, () => {
|
|
165
|
+
// vídeo chegou ao fim
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
sub.remove()
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**UI derivada de estado (ex.: ícone “repetir” no centro):** use `computeMoviiePlaybackEnded` com `playing`, `currentTime`, `duration`, `isLive` e `loop` — a mesma heurística usada pela skin Moviie, com `MOVIIE_SKIN_TIMELINE_END_SNAP_EPSILON_SECONDS` disponível no pacote se precisar espelhar o snap ao fim da timeline.
|
|
172
|
+
|
|
173
|
+
### Resilience: error boundary
|
|
174
|
+
|
|
175
|
+
Wrap `MoviieVideo` in `MoviieErrorBoundary` to isolate crashes (bug in
|
|
176
|
+
`expo-video`, missing native module, etc.) from the rest of your app:
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
import * as Sentry from "@sentry/react-native"
|
|
180
|
+
import { MoviieErrorBoundary, MoviieVideo } from "@moviie/player-expo"
|
|
181
|
+
|
|
182
|
+
<MoviieErrorBoundary
|
|
183
|
+
onError={(err, info) => Sentry.captureException(err, { contexts: { react: info } })}
|
|
184
|
+
fallback={({ error, reset }) => <YourFallback message={error.message} onRetry={reset} />}
|
|
185
|
+
>
|
|
186
|
+
<MoviieVideo {...moviie} />
|
|
187
|
+
</MoviieErrorBoundary>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Telemetry hooks (Sentry / PostHog / Datadog)
|
|
191
|
+
|
|
192
|
+
`@moviie/player-expo` ships its own telemetry to the Moviie backend. To **also**
|
|
193
|
+
mirror events into your monitoring pipeline, pass the opt-in callbacks:
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
<MoviieVideo
|
|
197
|
+
{...moviie}
|
|
198
|
+
onTelemetryError={(error, ctx) =>
|
|
199
|
+
Sentry.captureException(error, { tags: { embedId: ctx.embedId, phase: ctx.phase } })
|
|
200
|
+
}
|
|
201
|
+
onTelemetryEvent={(event) => posthog.capture(`moviie.${event.name}`, event)}
|
|
202
|
+
/>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Exceptions thrown by the callbacks are swallowed and logged — they never crash
|
|
206
|
+
the player. No PII flows through these hooks.
|
|
207
|
+
|
|
208
|
+
### Cast (Chromecast, optional)
|
|
209
|
+
|
|
210
|
+
Cast support is provided via the optional subpath `@moviie/player-expo/cast`. Add
|
|
211
|
+
the peer dependency and wire the adapter:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
pnpm add react-native-google-cast
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
import { createGoogleCastAdapter } from "@moviie/player-expo/cast"
|
|
219
|
+
|
|
220
|
+
const castAdapter = useMemo(() => createGoogleCastAdapter(), [])
|
|
221
|
+
|
|
222
|
+
<MoviieVideo {...moviie} castAdapter={castAdapter} />
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
If you don't use cast, **don't install `react-native-google-cast`** — the package
|
|
226
|
+
never imports it from the main entry, so your bundle stays slim.
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
MIT — see [`LICENSE`](./LICENSE). Copyright (c) 2026 Moviie.
|
package/app.plugin.cjs
ADDED
package/dist/cast.cjs
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var reactNative = require('react-native');
|
|
5
|
+
var GoogleCast = require('react-native-google-cast');
|
|
6
|
+
|
|
7
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
+
|
|
9
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
10
|
+
var GoogleCast__default = /*#__PURE__*/_interopDefault(GoogleCast);
|
|
11
|
+
|
|
12
|
+
// src/cast/google-cast-adapter.ts
|
|
13
|
+
var DEFAULT_HLS_CONTENT_TYPE = "application/x-mpegURL";
|
|
14
|
+
function mapCastState(state) {
|
|
15
|
+
switch (state) {
|
|
16
|
+
case GoogleCast.CastState.CONNECTED:
|
|
17
|
+
return "connected";
|
|
18
|
+
case GoogleCast.CastState.CONNECTING:
|
|
19
|
+
return "connecting";
|
|
20
|
+
case GoogleCast.CastState.NOT_CONNECTED:
|
|
21
|
+
return "disconnected";
|
|
22
|
+
case GoogleCast.CastState.NO_DEVICES_AVAILABLE:
|
|
23
|
+
default:
|
|
24
|
+
return "unavailable";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function createGoogleCastAdapter() {
|
|
28
|
+
let lastKnownState = "unavailable";
|
|
29
|
+
void GoogleCast__default.default.getCastState().then((state) => {
|
|
30
|
+
lastKnownState = mapCastState(state);
|
|
31
|
+
}).catch(() => {
|
|
32
|
+
lastKnownState = "unavailable";
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
isAvailable: () => true,
|
|
36
|
+
getState: () => lastKnownState,
|
|
37
|
+
subscribe(callback) {
|
|
38
|
+
void GoogleCast__default.default.getCastState().then((state) => {
|
|
39
|
+
lastKnownState = mapCastState(state);
|
|
40
|
+
callback(lastKnownState);
|
|
41
|
+
}).catch(() => void 0);
|
|
42
|
+
const subscription = GoogleCast__default.default.onCastStateChanged((castState) => {
|
|
43
|
+
lastKnownState = mapCastState(castState);
|
|
44
|
+
callback(lastKnownState);
|
|
45
|
+
});
|
|
46
|
+
return () => {
|
|
47
|
+
subscription.remove();
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
async showDevicePicker() {
|
|
51
|
+
await GoogleCast__default.default.showCastDialog();
|
|
52
|
+
},
|
|
53
|
+
async loadMedia(payload) {
|
|
54
|
+
const sessionManager = GoogleCast__default.default.getSessionManager();
|
|
55
|
+
const session = await sessionManager.getCurrentCastSession();
|
|
56
|
+
if (!session) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
await session.client.loadMedia({
|
|
60
|
+
autoplay: true,
|
|
61
|
+
startTime: payload.startSeconds,
|
|
62
|
+
mediaInfo: {
|
|
63
|
+
contentUrl: payload.uri,
|
|
64
|
+
contentType: payload.contentType ?? DEFAULT_HLS_CONTENT_TYPE,
|
|
65
|
+
streamDuration: payload.durationSeconds ?? void 0,
|
|
66
|
+
metadata: {
|
|
67
|
+
type: "movie",
|
|
68
|
+
title: payload.title,
|
|
69
|
+
images: payload.posterUrl ? [{ url: payload.posterUrl }] : void 0
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
},
|
|
74
|
+
async endSession() {
|
|
75
|
+
await GoogleCast__default.default.getSessionManager().endCurrentSession(true);
|
|
76
|
+
},
|
|
77
|
+
renderAndroidProxy() {
|
|
78
|
+
if (reactNative.Platform.OS !== "android") return null;
|
|
79
|
+
return React__default.default.createElement(
|
|
80
|
+
reactNative.View,
|
|
81
|
+
{
|
|
82
|
+
style: { position: "absolute", top: -9999, left: -9999 },
|
|
83
|
+
pointerEvents: "none"
|
|
84
|
+
},
|
|
85
|
+
React__default.default.createElement(GoogleCast.CastButton, {
|
|
86
|
+
style: { width: 48, height: 48 }
|
|
87
|
+
})
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function _registerCastAdapter(adapter) {
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/cast/index.ts
|
|
96
|
+
_registerCastAdapter(createGoogleCastAdapter());
|
|
97
|
+
|
|
98
|
+
exports.createGoogleCastAdapter = createGoogleCastAdapter;
|
|
99
|
+
//# sourceMappingURL=cast.cjs.map
|
|
100
|
+
//# sourceMappingURL=cast.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cast/google-cast-adapter.ts","../src/lib/cast-adapter-registry.ts","../src/cast/index.ts"],"names":["CastState","GoogleCast","Platform","React","View","CastButton"],"mappings":"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","file":"cast.cjs","sourcesContent":["/**\n * Implementação do {@link MoviieCastAdapter} sobre `react-native-google-cast`.\n *\n * **Atenção**: este arquivo importa `react-native-google-cast` no topo. Por isso\n * só deve ser importado via subpath `@moviie/player-expo/cast`, **nunca** pelo\n * package principal. Consumidores que não usam cast não precisam instalar a lib.\n */\n\nimport React from 'react'\nimport { Platform, View } from 'react-native'\nimport GoogleCast, { CastButton, CastState } from 'react-native-google-cast'\n\nimport type {\n MoviieCastAdapter,\n MoviieCastMediaPayload,\n MoviieCastState,\n} from '../lib/moviie-cast-adapter'\n\nconst DEFAULT_HLS_CONTENT_TYPE = 'application/x-mpegURL'\n\nfunction mapCastState(state: CastState | null | undefined): MoviieCastState {\n switch (state) {\n case CastState.CONNECTED:\n return 'connected'\n case CastState.CONNECTING:\n return 'connecting'\n case CastState.NOT_CONNECTED:\n return 'disconnected'\n case CastState.NO_DEVICES_AVAILABLE:\n default:\n return 'unavailable'\n }\n}\n\nexport function createGoogleCastAdapter(): MoviieCastAdapter {\n let lastKnownState: MoviieCastState = 'unavailable'\n\n // Hidrata estado inicial em background; subscribe() já entrega o valor real\n void GoogleCast.getCastState()\n .then((state) => {\n lastKnownState = mapCastState(state)\n })\n .catch(() => {\n lastKnownState = 'unavailable'\n })\n\n return {\n isAvailable: () => true,\n getState: () => lastKnownState,\n subscribe(callback) {\n // Entrega estado atual imediatamente (async, não bloqueia)\n void GoogleCast.getCastState()\n .then((state) => {\n lastKnownState = mapCastState(state)\n callback(lastKnownState)\n })\n .catch(() => undefined)\n\n const subscription = GoogleCast.onCastStateChanged((castState) => {\n lastKnownState = mapCastState(castState)\n callback(lastKnownState)\n })\n\n return () => {\n subscription.remove()\n }\n },\n async showDevicePicker() {\n await GoogleCast.showCastDialog()\n },\n async loadMedia(payload: MoviieCastMediaPayload) {\n const sessionManager = GoogleCast.getSessionManager()\n const session = await sessionManager.getCurrentCastSession()\n if (!session) {\n return\n }\n await session.client.loadMedia({\n autoplay: true,\n startTime: payload.startSeconds,\n mediaInfo: {\n contentUrl: payload.uri,\n contentType: payload.contentType ?? DEFAULT_HLS_CONTENT_TYPE,\n streamDuration: payload.durationSeconds ?? undefined,\n metadata: {\n type: 'movie',\n title: payload.title,\n images: payload.posterUrl ? [{ url: payload.posterUrl }] : undefined,\n },\n },\n })\n },\n async endSession() {\n await GoogleCast.getSessionManager().endCurrentSession(true)\n },\n renderAndroidProxy() {\n if (Platform.OS !== 'android') return null\n // Register a MediaRouteButton in the window so showCastDialog() can use it\n // as the primary path. Positioned off-screen so it is never visible.\n return React.createElement(\n View,\n {\n style: { position: 'absolute', top: -9999, left: -9999 },\n pointerEvents: 'none',\n },\n React.createElement(CastButton, {\n style: { width: 48, height: 48 },\n })\n )\n },\n }\n}\n","import type { MoviieCastAdapter } from './moviie-cast-adapter'\n\nlet _registered: MoviieCastAdapter | null = null\n\n/** Called automatically when `@moviie/player-expo/cast` is imported. */\nexport function _registerCastAdapter(adapter: MoviieCastAdapter): void {\n _registered = adapter\n}\n\n/** Returns the auto-registered adapter, or null if cast subpath was never imported. */\nexport function getRegisteredCastAdapter(): MoviieCastAdapter | null {\n return _registered\n}\n","export { createGoogleCastAdapter } from './google-cast-adapter'\nexport type {\n MoviieCastAdapter,\n MoviieCastMediaPayload,\n MoviieCastState,\n} from '../lib/moviie-cast-adapter'\n\n// Side-effect: auto-register adapter when this subpath is imported.\n// Enables the zero-config pattern: `import \"@moviie/player-expo/cast\"` in _layout.tsx.\nimport { createGoogleCastAdapter } from './google-cast-adapter'\nimport { _registerCastAdapter } from '../lib/cast-adapter-registry'\n_registerCastAdapter(createGoogleCastAdapter())\n"]}
|
package/dist/cast.d.cts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { M as MoviieCastAdapter } from './moviie-cast-adapter-DmSU2u3j.cjs';
|
|
2
|
+
export { a as MoviieCastMediaPayload, b as MoviieCastState } from './moviie-cast-adapter-DmSU2u3j.cjs';
|
|
3
|
+
import 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Implementação do {@link MoviieCastAdapter} sobre `react-native-google-cast`.
|
|
7
|
+
*
|
|
8
|
+
* **Atenção**: este arquivo importa `react-native-google-cast` no topo. Por isso
|
|
9
|
+
* só deve ser importado via subpath `@moviie/player-expo/cast`, **nunca** pelo
|
|
10
|
+
* package principal. Consumidores que não usam cast não precisam instalar a lib.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
declare function createGoogleCastAdapter(): MoviieCastAdapter;
|
|
14
|
+
|
|
15
|
+
export { MoviieCastAdapter, createGoogleCastAdapter };
|
package/dist/cast.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { M as MoviieCastAdapter } from './moviie-cast-adapter-DmSU2u3j.js';
|
|
2
|
+
export { a as MoviieCastMediaPayload, b as MoviieCastState } from './moviie-cast-adapter-DmSU2u3j.js';
|
|
3
|
+
import 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Implementação do {@link MoviieCastAdapter} sobre `react-native-google-cast`.
|
|
7
|
+
*
|
|
8
|
+
* **Atenção**: este arquivo importa `react-native-google-cast` no topo. Por isso
|
|
9
|
+
* só deve ser importado via subpath `@moviie/player-expo/cast`, **nunca** pelo
|
|
10
|
+
* package principal. Consumidores que não usam cast não precisam instalar a lib.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
declare function createGoogleCastAdapter(): MoviieCastAdapter;
|
|
14
|
+
|
|
15
|
+
export { MoviieCastAdapter, createGoogleCastAdapter };
|
package/dist/cast.mjs
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { _registerCastAdapter } from './chunk-7U2LKIGU.mjs';
|
|
2
|
+
import './chunk-BJTO5JO5.mjs';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Platform, View } from 'react-native';
|
|
5
|
+
import GoogleCast, { CastButton, CastState } from 'react-native-google-cast';
|
|
6
|
+
|
|
7
|
+
var DEFAULT_HLS_CONTENT_TYPE = "application/x-mpegURL";
|
|
8
|
+
function mapCastState(state) {
|
|
9
|
+
switch (state) {
|
|
10
|
+
case CastState.CONNECTED:
|
|
11
|
+
return "connected";
|
|
12
|
+
case CastState.CONNECTING:
|
|
13
|
+
return "connecting";
|
|
14
|
+
case CastState.NOT_CONNECTED:
|
|
15
|
+
return "disconnected";
|
|
16
|
+
case CastState.NO_DEVICES_AVAILABLE:
|
|
17
|
+
default:
|
|
18
|
+
return "unavailable";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function createGoogleCastAdapter() {
|
|
22
|
+
let lastKnownState = "unavailable";
|
|
23
|
+
void GoogleCast.getCastState().then((state) => {
|
|
24
|
+
lastKnownState = mapCastState(state);
|
|
25
|
+
}).catch(() => {
|
|
26
|
+
lastKnownState = "unavailable";
|
|
27
|
+
});
|
|
28
|
+
return {
|
|
29
|
+
isAvailable: () => true,
|
|
30
|
+
getState: () => lastKnownState,
|
|
31
|
+
subscribe(callback) {
|
|
32
|
+
void GoogleCast.getCastState().then((state) => {
|
|
33
|
+
lastKnownState = mapCastState(state);
|
|
34
|
+
callback(lastKnownState);
|
|
35
|
+
}).catch(() => void 0);
|
|
36
|
+
const subscription = GoogleCast.onCastStateChanged((castState) => {
|
|
37
|
+
lastKnownState = mapCastState(castState);
|
|
38
|
+
callback(lastKnownState);
|
|
39
|
+
});
|
|
40
|
+
return () => {
|
|
41
|
+
subscription.remove();
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
async showDevicePicker() {
|
|
45
|
+
await GoogleCast.showCastDialog();
|
|
46
|
+
},
|
|
47
|
+
async loadMedia(payload) {
|
|
48
|
+
const sessionManager = GoogleCast.getSessionManager();
|
|
49
|
+
const session = await sessionManager.getCurrentCastSession();
|
|
50
|
+
if (!session) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
await session.client.loadMedia({
|
|
54
|
+
autoplay: true,
|
|
55
|
+
startTime: payload.startSeconds,
|
|
56
|
+
mediaInfo: {
|
|
57
|
+
contentUrl: payload.uri,
|
|
58
|
+
contentType: payload.contentType ?? DEFAULT_HLS_CONTENT_TYPE,
|
|
59
|
+
streamDuration: payload.durationSeconds ?? void 0,
|
|
60
|
+
metadata: {
|
|
61
|
+
type: "movie",
|
|
62
|
+
title: payload.title,
|
|
63
|
+
images: payload.posterUrl ? [{ url: payload.posterUrl }] : void 0
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
async endSession() {
|
|
69
|
+
await GoogleCast.getSessionManager().endCurrentSession(true);
|
|
70
|
+
},
|
|
71
|
+
renderAndroidProxy() {
|
|
72
|
+
if (Platform.OS !== "android") return null;
|
|
73
|
+
return React.createElement(
|
|
74
|
+
View,
|
|
75
|
+
{
|
|
76
|
+
style: { position: "absolute", top: -9999, left: -9999 },
|
|
77
|
+
pointerEvents: "none"
|
|
78
|
+
},
|
|
79
|
+
React.createElement(CastButton, {
|
|
80
|
+
style: { width: 48, height: 48 }
|
|
81
|
+
})
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// src/cast/index.ts
|
|
88
|
+
_registerCastAdapter(createGoogleCastAdapter());
|
|
89
|
+
|
|
90
|
+
export { createGoogleCastAdapter };
|
|
91
|
+
//# sourceMappingURL=cast.mjs.map
|
|
92
|
+
//# sourceMappingURL=cast.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cast/google-cast-adapter.ts","../src/cast/index.ts"],"names":[],"mappings":"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","file":"cast.mjs","sourcesContent":["/**\n * Implementação do {@link MoviieCastAdapter} sobre `react-native-google-cast`.\n *\n * **Atenção**: este arquivo importa `react-native-google-cast` no topo. Por isso\n * só deve ser importado via subpath `@moviie/player-expo/cast`, **nunca** pelo\n * package principal. Consumidores que não usam cast não precisam instalar a lib.\n */\n\nimport React from 'react'\nimport { Platform, View } from 'react-native'\nimport GoogleCast, { CastButton, CastState } from 'react-native-google-cast'\n\nimport type {\n MoviieCastAdapter,\n MoviieCastMediaPayload,\n MoviieCastState,\n} from '../lib/moviie-cast-adapter'\n\nconst DEFAULT_HLS_CONTENT_TYPE = 'application/x-mpegURL'\n\nfunction mapCastState(state: CastState | null | undefined): MoviieCastState {\n switch (state) {\n case CastState.CONNECTED:\n return 'connected'\n case CastState.CONNECTING:\n return 'connecting'\n case CastState.NOT_CONNECTED:\n return 'disconnected'\n case CastState.NO_DEVICES_AVAILABLE:\n default:\n return 'unavailable'\n }\n}\n\nexport function createGoogleCastAdapter(): MoviieCastAdapter {\n let lastKnownState: MoviieCastState = 'unavailable'\n\n // Hidrata estado inicial em background; subscribe() já entrega o valor real\n void GoogleCast.getCastState()\n .then((state) => {\n lastKnownState = mapCastState(state)\n })\n .catch(() => {\n lastKnownState = 'unavailable'\n })\n\n return {\n isAvailable: () => true,\n getState: () => lastKnownState,\n subscribe(callback) {\n // Entrega estado atual imediatamente (async, não bloqueia)\n void GoogleCast.getCastState()\n .then((state) => {\n lastKnownState = mapCastState(state)\n callback(lastKnownState)\n })\n .catch(() => undefined)\n\n const subscription = GoogleCast.onCastStateChanged((castState) => {\n lastKnownState = mapCastState(castState)\n callback(lastKnownState)\n })\n\n return () => {\n subscription.remove()\n }\n },\n async showDevicePicker() {\n await GoogleCast.showCastDialog()\n },\n async loadMedia(payload: MoviieCastMediaPayload) {\n const sessionManager = GoogleCast.getSessionManager()\n const session = await sessionManager.getCurrentCastSession()\n if (!session) {\n return\n }\n await session.client.loadMedia({\n autoplay: true,\n startTime: payload.startSeconds,\n mediaInfo: {\n contentUrl: payload.uri,\n contentType: payload.contentType ?? DEFAULT_HLS_CONTENT_TYPE,\n streamDuration: payload.durationSeconds ?? undefined,\n metadata: {\n type: 'movie',\n title: payload.title,\n images: payload.posterUrl ? [{ url: payload.posterUrl }] : undefined,\n },\n },\n })\n },\n async endSession() {\n await GoogleCast.getSessionManager().endCurrentSession(true)\n },\n renderAndroidProxy() {\n if (Platform.OS !== 'android') return null\n // Register a MediaRouteButton in the window so showCastDialog() can use it\n // as the primary path. Positioned off-screen so it is never visible.\n return React.createElement(\n View,\n {\n style: { position: 'absolute', top: -9999, left: -9999 },\n pointerEvents: 'none',\n },\n React.createElement(CastButton, {\n style: { width: 48, height: 48 },\n })\n )\n },\n }\n}\n","export { createGoogleCastAdapter } from './google-cast-adapter'\nexport type {\n MoviieCastAdapter,\n MoviieCastMediaPayload,\n MoviieCastState,\n} from '../lib/moviie-cast-adapter'\n\n// Side-effect: auto-register adapter when this subpath is imported.\n// Enables the zero-config pattern: `import \"@moviie/player-expo/cast\"` in _layout.tsx.\nimport { createGoogleCastAdapter } from './google-cast-adapter'\nimport { _registerCastAdapter } from '../lib/cast-adapter-registry'\n_registerCastAdapter(createGoogleCastAdapter())\n"]}
|