@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.
Files changed (104) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +230 -0
  3. package/app.plugin.cjs +3 -0
  4. package/dist/cast.cjs +100 -0
  5. package/dist/cast.cjs.map +1 -0
  6. package/dist/cast.d.cts +15 -0
  7. package/dist/cast.d.ts +15 -0
  8. package/dist/cast.mjs +92 -0
  9. package/dist/cast.mjs.map +1 -0
  10. package/dist/chunk-67DJ7NOB.mjs +294 -0
  11. package/dist/chunk-67DJ7NOB.mjs.map +1 -0
  12. package/dist/chunk-7U2LKIGU.mjs +12 -0
  13. package/dist/chunk-7U2LKIGU.mjs.map +1 -0
  14. package/dist/chunk-BJTO5JO5.mjs +10 -0
  15. package/dist/chunk-BJTO5JO5.mjs.map +1 -0
  16. package/dist/index.cjs +3934 -0
  17. package/dist/index.cjs.map +1 -0
  18. package/dist/index.d.cts +450 -0
  19. package/dist/index.d.ts +450 -0
  20. package/dist/index.mjs +3571 -0
  21. package/dist/index.mjs.map +1 -0
  22. package/dist/layout.cjs +217 -0
  23. package/dist/layout.cjs.map +1 -0
  24. package/dist/layout.d.cts +20 -0
  25. package/dist/layout.d.ts +20 -0
  26. package/dist/layout.mjs +4 -0
  27. package/dist/layout.mjs.map +1 -0
  28. package/dist/moviie-cast-adapter-DmSU2u3j.d.cts +53 -0
  29. package/dist/moviie-cast-adapter-DmSU2u3j.d.ts +53 -0
  30. package/dist/plugin/with-moviie.cjs +88 -0
  31. package/dist/plugin/with-moviie.cjs.map +1 -0
  32. package/dist/plugin/with-moviie.d.cts +52 -0
  33. package/dist/plugin/with-moviie.d.ts +52 -0
  34. package/dist/plugin/with-moviie.mjs +76 -0
  35. package/dist/plugin/with-moviie.mjs.map +1 -0
  36. package/package.json +134 -0
  37. package/plugin/validate-options.ts +31 -0
  38. package/plugin/with-moviie-types.ts +21 -0
  39. package/plugin/with-moviie.ts +84 -0
  40. package/src/apply-expo-moviie-endpoints.ts +47 -0
  41. package/src/cast/google-cast-adapter.ts +111 -0
  42. package/src/cast/index.ts +12 -0
  43. package/src/components/controls/moviie-bottom-timeline.tsx +477 -0
  44. package/src/components/controls/moviie-cast-buttons.tsx +96 -0
  45. package/src/components/controls/moviie-chrome-edge-gradients.tsx +162 -0
  46. package/src/components/controls/moviie-controls.tsx +585 -0
  47. package/src/components/controls/moviie-skin-chrome-context.tsx +374 -0
  48. package/src/components/controls/moviie-skin-smart-progress.tsx +157 -0
  49. package/src/components/icons/embed-media-icons.tsx +282 -0
  50. package/src/components/icons/moviie-embed-brand-mark.tsx +58 -0
  51. package/src/components/moviie-error-boundary.tsx +80 -0
  52. package/src/components/moviie-player-error-shell.tsx +143 -0
  53. package/src/components/moviie-player-loading-shell.tsx +59 -0
  54. package/src/components/moviie-skin-custom-fullscreen-modal.tsx +232 -0
  55. package/src/components/moviie-video-props.ts +134 -0
  56. package/src/components/moviie-video.tsx +568 -0
  57. package/src/components/moviie-video.web.tsx +167 -0
  58. package/src/components/overlays/moviie-watermark.tsx +53 -0
  59. package/src/constants.ts +374 -0
  60. package/src/hooks/use-moviie-event.ts +103 -0
  61. package/src/hooks/use-moviie-playback-ended.ts +14 -0
  62. package/src/hooks/use-moviie-playback-resume-persistence.ts +76 -0
  63. package/src/hooks/use-moviie-playback.ts +99 -0
  64. package/src/hooks/use-moviie-player-types.ts +55 -0
  65. package/src/hooks/use-moviie-player.ts +236 -0
  66. package/src/hooks/use-moviie-player.web.ts +57 -0
  67. package/src/hooks/use-moviie-telemetry.ts +133 -0
  68. package/src/index.ts +90 -0
  69. package/src/layout.ts +4 -0
  70. package/src/lib/add-moviie-playback-ended-listener.ts +16 -0
  71. package/src/lib/build-moviie-branding-marketing-url.ts +14 -0
  72. package/src/lib/build-moviie-embed-brand-home-url.ts +16 -0
  73. package/src/lib/build-moviie-skin-layout-metrics.ts +166 -0
  74. package/src/lib/build-moviie-video-presentation.ts +42 -0
  75. package/src/lib/build-moviie-watch-embed-url.ts +30 -0
  76. package/src/lib/cast-adapter-registry.ts +13 -0
  77. package/src/lib/clamp-unit-interval.ts +3 -0
  78. package/src/lib/compute-custom-fullscreen-stage-dimensions.ts +39 -0
  79. package/src/lib/compute-moviie-playback-ended.ts +31 -0
  80. package/src/lib/compute-playback-buffering.ts +41 -0
  81. package/src/lib/compute-playback-progress-ratio.ts +33 -0
  82. package/src/lib/compute-timeline-scrub-commit-time.ts +40 -0
  83. package/src/lib/custom-fullscreen-native-orientation.ts +88 -0
  84. package/src/lib/format-playback-clock.ts +27 -0
  85. package/src/lib/jsx-native-bridge.ts +45 -0
  86. package/src/lib/map-smart-progress-display-ratio.ts +43 -0
  87. package/src/lib/moviie-cast-adapter.ts +53 -0
  88. package/src/lib/moviie-error-display.ts +149 -0
  89. package/src/lib/moviie-shell-tokens.ts +27 -0
  90. package/src/lib/moviie-telemetry-callbacks.ts +76 -0
  91. package/src/lib/optional-status-bar.ts +48 -0
  92. package/src/lib/partition-moviie-video-host-style.ts +54 -0
  93. package/src/lib/remember-playback-position-storage.ts +98 -0
  94. package/src/lib/resolve-moviie-skin-layout-scale.ts +17 -0
  95. package/src/lib/resolve-orientation-lock-for-rotate-z-deg.ts +21 -0
  96. package/src/lib/resolve-skin-accent-color.ts +10 -0
  97. package/src/lib/resolve-web-embed-iframe-src.ts +17 -0
  98. package/src/lib/warn-once.ts +23 -0
  99. package/src/platform/native-application-id.ts +10 -0
  100. package/src/platform/platform-client-info.ts +31 -0
  101. package/src/playback/fetch-playback-fresh.ts +27 -0
  102. package/src/playback/playback-memory-cache.ts +52 -0
  103. package/src/provider/moviie-provider.tsx +99 -0
  104. package/src/secure-store-viewer-token-store.ts +80 -0
package/dist/index.mjs ADDED
@@ -0,0 +1,3571 @@
1
+ import { MOVIIE_SMART_PROGRESS_Z_INDEX, MOVIIE_SMART_PROGRESS_BAR_HEIGHT_DP, MOVIIE_SKIN_CUSTOM_FULLSCREEN_ENTER_SCALE_START, MOVIIE_SKIN_CUSTOM_FULLSCREEN_TRANSITION_MS, MOVIIE_SKIN_CUSTOM_FULLSCREEN_ROTATE_Z_DEGREES, MOVIIE_SKIN_CUSTOM_FULLSCREEN_STAGE_BACKGROUND_HEX, MOVIIE_SKIN_CHROME_CONTROL_PRESSED_OPACITY, warnOnce, MOVIIE_PLAYBACK_PROFILE, MOVIIE_VIDEO_DEFAULT_CONTENT_FIT, MOVIIE_SKIN_CUSTOM_FULLSCREEN_VIDEO_CONTENT_FIT, MOVIIE_SKIN_CUSTOM_FULLSCREEN_STAGE_OVERSCAN_PX, MOVIIE_EXPO_SCREEN_ORIENTATION_LOCK_VALUE, MOVIIE_SKIN_CHROME_FOREGROUND_HEX, MOVIIE_SKIN_CONTROLS_FADE_MS, MOVIIE_SKIN_SEEK_JUMP_SECONDS, MOVIIE_SKIN_CHROME_SCRIM_BLACK_RGBA, MOVIIE_SKIN_TIMELINE_END_SNAP_EPSILON_SECONDS, MOVIIE_SKIN_CHROME_SVG_GRADIENT_TOP_EDGE_ID, MOVIIE_SKIN_CHROME_GRADIENT_EDGE_ALPHA, MOVIIE_SKIN_CHROME_GRADIENT_BLACK_HEX, MOVIIE_SKIN_CHROME_SVG_GRADIENT_BOTTOM_EDGE_ID, MOVIIE_SKIN_FLOATING_HUD_TRAILING_HIT_SLOP_PX, MOVIIE_SKIN_TOP_ICON_CAST_VISUAL_SCALE, MOVIIE_SKIN_TOP_ICON_PIP_VISUAL_SCALE, MOVIIE_SKIN_TOP_ICON_FULLSCREEN_VISUAL_SCALE, MOVIIE_EMBED_BRAND_VIEW_BOX, MOVIIE_EMBED_BRAND_LETTERMARK_STROKE_WIDTH, MOVIIE_SMART_PROGRESS_FILL_TRANSITION_MS, MOVIIE_SKIN_TIMELINE_THUMB_DRAG_DIAMETER_MULTIPLIER, MOVIIE_SKIN_TIMELINE_SCRUB_HIT_SLOP_HORIZONTAL_DP, MOVIIE_SKIN_TIMELINE_SCRUB_HIT_SLOP_BOTTOM_DP, MOVIIE_SKIN_TIMELINE_SCRUB_HIT_SLOP_TOP_DP, MOVIIE_SKIN_BUFFER_UNDERRUN_LEAD_SECONDS, MOVIIE_SKIN_CHROME_EDGE_GRADIENT_MIN_CLEAR_MIDDLE_PX, MOVIIE_SKIN_CHROME_EDGE_GRADIENT_EDGE_SOLID_FADE_END_FRACTION, MOVIIE_SKIN_CHROME_CONTROL_REST_OPACITY, MOVIIE_BRANDING_MARKETING_ORIGIN, MOVIIE_BRANDING_UTM_PARAM_SOURCE, MOVIIE_EMBED_BRAND_UTM, MOVIIE_BRANDING_UTM_PARAM_MEDIUM, MOVIIE_BRANDING_UTM_PARAM_CAMPAIGN, MOVIIE_SKIN_DEFAULT_ACCENT_HEX, MOVIIE_SMART_PROGRESS_CONFIG, MOVIIE_SKIN_TIMELINE_BUFFER_LAYER_RGBA, MOVIIE_SKIN_TIMELINE_RAIL_BACKGROUND_RGBA, MOVIIE_SKIN_TIMELINE_THUMB_BORDER_RGBA, MOVIIE_SKIN_TIMELINE_TOUCH_EXPANSION_VERTICAL_DP, MOVIIE_SKIN_TIMELINE_CHAPTER_SNAP_THRESHOLD_SECONDS, MOVIIE_SKIN_BRAND_MARK_ARTBOARD_HEIGHT_PX, MOVIIE_SKIN_BRAND_MARK_ARTBOARD_WIDTH_PX, MOVIIE_SKIN_CUSTOM_FULLSCREEN_HUD_EDGE_BOOST_REFERENCE_PT, useMoviieContext, MOVIIE_WATCH_ORIGIN_DEFAULT, MOVIIE_SKIN_LAYOUT_SCALE_MAX, MOVIIE_SKIN_LAYOUT_SCALE_MIN, MOVIIE_SKIN_BRAND_MARK_TARGET_WIDTH_AT_REFERENCE_PT, MOVIIE_SKIN_TIMELINE_THUMB_DIAMETER_PX, MOVIIE_SKIN_TIMELINE_BAR_HEIGHT_PX, MOVIIE_SKIN_TIMELINE_TOUCH_PADDING_VERTICAL_PX, MOVIIE_SKIN_FLOATING_HUD_EDGE_INSET_PX, MOVIIE_SKIN_FLOATING_HUD_BUTTON_GAP_AT_REFERENCE_PT, MOVIIE_SKIN_FLOATING_FULLSCREEN_TOP_ICON_AT_REFERENCE_PT, MOVIIE_SKIN_FLOATING_FULLSCREEN_ICON_AT_REFERENCE_PT, MOVIIE_SKIN_FLOATING_FULLSCREEN_SCRIM_DIAMETER_AT_REFERENCE_PT, MOVIIE_SKIN_CHROME_EDGE_GRADIENT_HEIGHT_AT_REFERENCE_PT, MOVIIE_SKIN_BUFFERING_INDICATOR_SLOT_PX, MOVIIE_SKIN_CLOCK_TEXT_FONT_SIZE_AT_REFERENCE_PT, MOVIIE_SKIN_CENTER_PLAY_SCRIM_DIAMETER_AT_REFERENCE_PT, MOVIIE_SKIN_CENTER_CLUSTER_COLUMN_GAP_PX, MOVIIE_SKIN_CENTER_PLAY_ICON_PX, MOVIIE_SKIN_CENTER_SEEK_SCRIM_DIAMETER_AT_REFERENCE_PT, MOVIIE_SKIN_SEEK_ICON_PX, MOVIIE_SKIN_CONTROL_ICON_PX, MOVIIE_SKIN_FLOATING_HUD_CLOCK_CORNER_RADIUS_PX, MOVIIE_SKIN_FLOATING_HUD_ABOVE_TIMELINE_GAP_AT_REFERENCE_PT, MOVIIE_SKIN_LAYOUT_REFERENCE_WIDTH_PT, MOVIIE_REMEMBER_POSITION_DEBOUNCE_MS, MOVIIE_REMEMBER_POSITION_STORAGE_PREFIX, MOVIIE_PLAYBACK_MEMORY_CACHE_TTL_MS } from './chunk-67DJ7NOB.mjs';
2
+ export { EXPO_PUBLIC_MOVIIE_API_BASE_URL_ENV, EXPO_PUBLIC_MOVIIE_EVENTS_BASE_URL_ENV, EXPO_PUBLIC_MOVIIE_WATCH_ORIGIN_ENV, MOVIIE_PLAYBACK_MEMORY_CACHE_TTL_MS, MOVIIE_PLAYBACK_PROFILE, MOVIIE_PLAYER_EXPO_EMBED_JS_MARKER_ATTR, MOVIIE_PLAYER_EXPO_PKG_VERSION, MOVIIE_REMEMBER_POSITION_DEBOUNCE_MS, MOVIIE_REMEMBER_POSITION_STORAGE_PREFIX, MOVIIE_SKIN_CONTROLS_AUTO_HIDE_MS, MOVIIE_SKIN_CONTROL_ICON_PX, MOVIIE_SKIN_DEFAULT_ACCENT_HEX, MOVIIE_SKIN_TIMELINE_END_SNAP_EPSILON_SECONDS, MOVIIE_SKIN_VOLUME_LOW_ICON_THRESHOLD, MOVIIE_VIDEO_DEFAULT_CONTENT_FIT, MOVIIE_VIEWER_TOKEN_SECURE_STORE_KEY, MOVIIE_WATCH_EMBED_JS_PATH, MOVIIE_WATCH_ORIGIN_DEFAULT, MoviieProvider, SecureStoreViewerTokenStore, useMoviieContext } from './chunk-67DJ7NOB.mjs';
3
+ import { getRegisteredCastAdapter } from './chunk-7U2LKIGU.mjs';
4
+ import { __require } from './chunk-BJTO5JO5.mjs';
5
+ import { MoviieAuthError, MoviieNotFoundError, MoviieBundleBlockedError, MoviieReferrerBlockedError, MoviieSubscriptionInactiveError, MoviieNetworkError, MoviieRateLimitError, deriveTelemetryEventsBaseUrlFromBootstrapUrl, TelemetryClient, PLAYBACK_EVENT_TYPE } from '@moviie/player-sdk';
6
+ export * from '@moviie/player-sdk';
7
+ import React, { createContext, forwardRef, useRef, useMemo, useCallback, useImperativeHandle, useEffect, useState, createElement, useContext } from 'react';
8
+ import { isPictureInPictureSupported, VideoView, useVideoPlayer } from 'expo-video';
9
+ import { Animated, StyleSheet, Platform, useWindowDimensions, View, Modal, Pressable, Text, Image, ActivityIndicator, Linking, Easing as Easing$1, AppState } from 'react-native';
10
+ import { PLAYER_API_EVENTS } from '@moviie/player-types';
11
+ export { PLAYER_API_EVENTS, PLAYER_API_METHODS, PLAYER_API_PROPERTIES } from '@moviie/player-types';
12
+ import AnimatedReanimated, { useSharedValue, withTiming, runOnJS, useAnimatedStyle, Easing, useDerivedValue } from 'react-native-reanimated';
13
+ import RNSvg, { Defs as Defs$1, LinearGradient as LinearGradient$1, Stop as Stop$1, Rect as Rect$1, Path as Path$1 } from 'react-native-svg';
14
+ import { useSafeAreaInsets, SafeAreaProvider } from 'react-native-safe-area-context';
15
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
16
+ import { Gesture, GestureDetector } from 'react-native-gesture-handler';
17
+ import { getOrientationLockAsync, lockAsync, OrientationLock, unlockAsync } from 'expo-screen-orientation';
18
+
19
+ // src/playback/playback-memory-cache.ts
20
+ var playbackCache = /* @__PURE__ */ new Map();
21
+ function cacheKey(scope, embedId) {
22
+ return `${scope ?? "__anon__"}::${embedId}`;
23
+ }
24
+ function resolveEntryExpiry(data) {
25
+ const now = Date.now();
26
+ const fallback = now + MOVIIE_PLAYBACK_MEMORY_CACHE_TTL_MS;
27
+ const serverExpiry = data.playback?.expiresAt;
28
+ if (!serverExpiry) return fallback;
29
+ const parsed = Date.parse(serverExpiry);
30
+ if (!Number.isFinite(parsed)) return fallback;
31
+ return Math.min(fallback, parsed);
32
+ }
33
+ function readPlaybackCache(scope, embedId) {
34
+ const key = cacheKey(scope, embedId);
35
+ const hit = playbackCache.get(key);
36
+ if (!hit || hit.expiresAt <= Date.now()) {
37
+ playbackCache.delete(key);
38
+ return null;
39
+ }
40
+ return hit.data;
41
+ }
42
+ function writePlaybackCache(scope, embedId, data) {
43
+ playbackCache.set(cacheKey(scope, embedId), {
44
+ data,
45
+ expiresAt: resolveEntryExpiry(data)
46
+ });
47
+ }
48
+
49
+ // src/playback/fetch-playback-fresh.ts
50
+ async function fetchPlaybackFreshWriteCache(scope, embedId, signal, getPlayback) {
51
+ try {
52
+ const data = await getPlayback(embedId, signal);
53
+ writePlaybackCache(scope, embedId, data);
54
+ return { ok: true, data };
55
+ } catch (unknownError) {
56
+ const error = unknownError instanceof Error ? unknownError : new Error(String(unknownError));
57
+ return { ok: false, error };
58
+ }
59
+ }
60
+
61
+ // src/hooks/use-moviie-playback.ts
62
+ function useMoviiePlayback(args) {
63
+ const trimmedEmbedId = args.embedId?.trim();
64
+ const missingEmbedId = !trimmedEmbedId;
65
+ const { client, publishableKey } = useMoviieContext();
66
+ const [data, setData] = useState(null);
67
+ const [fetchError, setFetchError] = useState(null);
68
+ const [isLoading, setIsLoading] = useState(!missingEmbedId);
69
+ const [retryNonce, setRetryNonce] = useState(0);
70
+ const retry = useCallback(() => {
71
+ setRetryNonce((n) => n + 1);
72
+ }, []);
73
+ useEffect(() => {
74
+ if (!trimmedEmbedId) {
75
+ return;
76
+ }
77
+ const embedKey = trimmedEmbedId;
78
+ const controller = new AbortController();
79
+ let cancelled = false;
80
+ async function load() {
81
+ setIsLoading(true);
82
+ setFetchError(null);
83
+ try {
84
+ const cached = readPlaybackCache(publishableKey, embedKey);
85
+ if (cached && !controller.signal.aborted) {
86
+ setData(cached);
87
+ }
88
+ const outcome = await fetchPlaybackFreshWriteCache(
89
+ publishableKey,
90
+ embedKey,
91
+ controller.signal,
92
+ (id, sig) => client.getPlayback(id, sig)
93
+ );
94
+ if (cancelled || controller.signal.aborted) {
95
+ return;
96
+ }
97
+ if (outcome.ok) {
98
+ setData(outcome.data);
99
+ } else {
100
+ setFetchError(outcome.error);
101
+ setData(readPlaybackCache(publishableKey, embedKey));
102
+ }
103
+ } finally {
104
+ if (!cancelled && !controller.signal.aborted) {
105
+ setIsLoading(false);
106
+ }
107
+ }
108
+ }
109
+ void load();
110
+ return () => {
111
+ cancelled = true;
112
+ controller.abort();
113
+ };
114
+ }, [trimmedEmbedId, client, publishableKey, retryNonce]);
115
+ const error = missingEmbedId ? new Error("Informe um embedId v\xE1lido (UUID p\xFAblico do embed).") : fetchError;
116
+ return {
117
+ data: missingEmbedId ? null : data,
118
+ error,
119
+ isLoading: missingEmbedId ? false : isLoading,
120
+ retry
121
+ };
122
+ }
123
+ function useMoviieTelemetry(player, playback, eventsBaseUrl, defaultHeaders, viewerTokenStore) {
124
+ const hasStartedPlaybackRef = useRef(false);
125
+ useEffect(() => {
126
+ if (!playback?.telemetry.bootstrapUrl) {
127
+ return;
128
+ }
129
+ const telemetry = new TelemetryClient({
130
+ eventsBaseUrl,
131
+ defaultHeaders,
132
+ viewerTokenStore,
133
+ getPositionSeconds: () => player.currentTime
134
+ });
135
+ let disposed = false;
136
+ const subs = [];
137
+ void (async () => {
138
+ try {
139
+ await telemetry.bootstrap({
140
+ bootstrapUrl: playback.telemetry.bootstrapUrl
141
+ });
142
+ if (disposed) {
143
+ return;
144
+ }
145
+ await telemetry.recordEvent({
146
+ eventType: PLAYBACK_EVENT_TYPE.SESSION_START,
147
+ positionSeconds: 0
148
+ });
149
+ } catch {
150
+ }
151
+ })();
152
+ subs.push(
153
+ player.addListener("playingChange", ({ isPlaying }) => {
154
+ telemetry.updatePosition(Math.max(0, player.currentTime));
155
+ if (isPlaying) {
156
+ void telemetry.recordEvent({
157
+ eventType: hasStartedPlaybackRef.current ? PLAYBACK_EVENT_TYPE.RESUME : PLAYBACK_EVENT_TYPE.PLAY_START,
158
+ positionSeconds: Math.floor(Math.max(0, player.currentTime))
159
+ });
160
+ hasStartedPlaybackRef.current = true;
161
+ telemetry.startHeartbeats();
162
+ } else {
163
+ void telemetry.recordEvent({
164
+ eventType: PLAYBACK_EVENT_TYPE.PAUSE,
165
+ positionSeconds: Math.floor(Math.max(0, player.currentTime))
166
+ });
167
+ telemetry.stopHeartbeats();
168
+ }
169
+ })
170
+ );
171
+ subs.push(
172
+ player.addListener("playToEnd", () => {
173
+ telemetry.updatePosition(Math.max(0, player.currentTime));
174
+ void telemetry.recordEvent({
175
+ eventType: PLAYBACK_EVENT_TYPE.ENDED,
176
+ positionSeconds: Math.floor(Math.max(0, player.duration))
177
+ });
178
+ telemetry.stopHeartbeats();
179
+ })
180
+ );
181
+ subs.push(
182
+ player.addListener("statusChange", ({ status }) => {
183
+ if (status === "error") {
184
+ void telemetry.recordEvent({
185
+ eventType: PLAYBACK_EVENT_TYPE.ERROR,
186
+ positionSeconds: Math.floor(Math.max(0, player.currentTime)),
187
+ errorCode: "player_status_error"
188
+ });
189
+ }
190
+ })
191
+ );
192
+ subs.push(
193
+ player.addListener("timeUpdate", ({ currentTime }) => {
194
+ telemetry.updatePosition(Math.max(0, currentTime));
195
+ })
196
+ );
197
+ const appStateSub = AppState.addEventListener(
198
+ "change",
199
+ (next) => {
200
+ if (next === "active") {
201
+ telemetry.resumeHeartbeats();
202
+ } else {
203
+ telemetry.pauseHeartbeats();
204
+ void telemetry.flush();
205
+ }
206
+ }
207
+ );
208
+ return () => {
209
+ disposed = true;
210
+ hasStartedPlaybackRef.current = false;
211
+ subs.forEach((s) => {
212
+ s.remove();
213
+ });
214
+ appStateSub.remove();
215
+ void telemetry.destroy();
216
+ };
217
+ }, [
218
+ player,
219
+ playback?.embedId,
220
+ playback?.telemetry.bootstrapUrl,
221
+ eventsBaseUrl,
222
+ defaultHeaders,
223
+ viewerTokenStore
224
+ ]);
225
+ }
226
+
227
+ // src/lib/remember-playback-position-storage.ts
228
+ var RESUME_SECURE_STORE_MISSING_WARNING = "[@moviie/player-expo] `expo-secure-store` n\xE3o est\xE1 instalado \u2014 resume position n\xE3o ser\xE1 persistido. Instala com `pnpm add expo-secure-store` se quiseres retomar onde o utilizador parou.";
229
+ var SECURE_STORE_KEY_SAFE_PATTERN = /^[A-Za-z0-9._-]+$/;
230
+ function isSecureStoreSafeEmbedId(embedId) {
231
+ return SECURE_STORE_KEY_SAFE_PATTERN.test(embedId);
232
+ }
233
+ function rememberPlaybackPositionStorageKey(embedId) {
234
+ return `${MOVIIE_REMEMBER_POSITION_STORAGE_PREFIX}${embedId}`;
235
+ }
236
+ function parseStoredResumeSeconds(raw) {
237
+ if (raw == null || raw === "") {
238
+ return null;
239
+ }
240
+ const n = Number(raw);
241
+ if (!Number.isFinite(n) || n < 0) {
242
+ return null;
243
+ }
244
+ return n;
245
+ }
246
+ function clampResumeTimeSeconds(seconds, durationSeconds) {
247
+ if (seconds == null || !Number.isFinite(seconds) || seconds < 0.25) {
248
+ return null;
249
+ }
250
+ if (durationSeconds != null && Number.isFinite(durationSeconds) && durationSeconds > 0) {
251
+ if (seconds >= durationSeconds - 1) {
252
+ return null;
253
+ }
254
+ return Math.min(seconds, durationSeconds - 0.25);
255
+ }
256
+ return seconds;
257
+ }
258
+ async function loadSecureStore() {
259
+ try {
260
+ return await import('expo-secure-store');
261
+ } catch {
262
+ warnOnce("moviie:expo-secure-store-missing-resume", RESUME_SECURE_STORE_MISSING_WARNING);
263
+ return null;
264
+ }
265
+ }
266
+ async function readStoredResumeSeconds(embedId) {
267
+ if (!isSecureStoreSafeEmbedId(embedId)) {
268
+ return null;
269
+ }
270
+ const SecureStore = await loadSecureStore();
271
+ if (!SecureStore) {
272
+ return null;
273
+ }
274
+ try {
275
+ const raw = await SecureStore.getItemAsync(rememberPlaybackPositionStorageKey(embedId));
276
+ return parseStoredResumeSeconds(raw);
277
+ } catch {
278
+ return null;
279
+ }
280
+ }
281
+ async function writeStoredResumeSeconds(embedId, seconds) {
282
+ if (!isSecureStoreSafeEmbedId(embedId)) {
283
+ return;
284
+ }
285
+ const SecureStore = await loadSecureStore();
286
+ if (!SecureStore) {
287
+ return;
288
+ }
289
+ try {
290
+ await SecureStore.setItemAsync(rememberPlaybackPositionStorageKey(embedId), String(seconds));
291
+ } catch {
292
+ return;
293
+ }
294
+ }
295
+ async function clearStoredResumeSeconds(embedId) {
296
+ if (!isSecureStoreSafeEmbedId(embedId)) {
297
+ return;
298
+ }
299
+ const SecureStore = await loadSecureStore();
300
+ if (!SecureStore) {
301
+ return;
302
+ }
303
+ try {
304
+ await SecureStore.deleteItemAsync(rememberPlaybackPositionStorageKey(embedId));
305
+ } catch {
306
+ return;
307
+ }
308
+ }
309
+
310
+ // src/hooks/use-moviie-playback-resume-persistence.ts
311
+ function useMoviiePlaybackResumePersistence(options) {
312
+ const { player, embedId, durationSeconds, enabled } = options;
313
+ const debounceTimerRef = useRef(null);
314
+ useEffect(() => {
315
+ if (!enabled || embedId == null || embedId === "") {
316
+ return;
317
+ }
318
+ const clearDebounce = () => {
319
+ if (debounceTimerRef.current != null) {
320
+ clearTimeout(debounceTimerRef.current);
321
+ debounceTimerRef.current = null;
322
+ }
323
+ };
324
+ const persistNow = (seconds) => {
325
+ const clamped = clampResumeTimeSeconds(seconds, durationSeconds);
326
+ if (clamped == null) {
327
+ void clearStoredResumeSeconds(embedId);
328
+ return;
329
+ }
330
+ void writeStoredResumeSeconds(embedId, clamped);
331
+ };
332
+ const schedulePersist = (seconds) => {
333
+ clearDebounce();
334
+ debounceTimerRef.current = setTimeout(() => {
335
+ debounceTimerRef.current = null;
336
+ persistNow(seconds);
337
+ }, MOVIIE_REMEMBER_POSITION_DEBOUNCE_MS);
338
+ };
339
+ const subTime = player.addListener("timeUpdate", ({ currentTime }) => {
340
+ if (player.playing) {
341
+ schedulePersist(currentTime);
342
+ }
343
+ });
344
+ const subPlaying = player.addListener("playingChange", ({ isPlaying }) => {
345
+ if (!isPlaying) {
346
+ clearDebounce();
347
+ persistNow(player.currentTime);
348
+ }
349
+ });
350
+ const subEnd = player.addListener("playToEnd", () => {
351
+ clearDebounce();
352
+ void clearStoredResumeSeconds(embedId);
353
+ });
354
+ return () => {
355
+ clearDebounce();
356
+ subTime.remove();
357
+ subPlaying.remove();
358
+ subEnd.remove();
359
+ };
360
+ }, [durationSeconds, embedId, enabled, player]);
361
+ }
362
+
363
+ // src/lib/build-moviie-video-presentation.ts
364
+ function buildMoviieVideoPresentationProps(args) {
365
+ const allowsPictureInPicture = args.pictureInPicture !== void 0 ? args.pictureInPicture : args.showPip;
366
+ return {
367
+ allowsPictureInPicture,
368
+ startsPictureInPictureAutomatically: args.pictureInPictureAutoStart ?? false,
369
+ requiresLinearPlayback: args.profile === MOVIIE_PLAYBACK_PROFILE.VSL
370
+ };
371
+ }
372
+
373
+ // src/hooks/use-moviie-player.ts
374
+ var PLAYBACK_REQUEST_HEADER_ALLOWLIST = /* @__PURE__ */ new Set([
375
+ "authorization",
376
+ "referer",
377
+ "user-agent",
378
+ "x-moviie-playback-token",
379
+ "x-moviie-signature",
380
+ "x-playback-token"
381
+ ]);
382
+ function filterPlaybackRequestHeaders(raw) {
383
+ if (!raw) return void 0;
384
+ const safe = {};
385
+ for (const [name, value] of Object.entries(raw)) {
386
+ if (typeof name !== "string" || typeof value !== "string") continue;
387
+ if (!PLAYBACK_REQUEST_HEADER_ALLOWLIST.has(name.toLowerCase())) continue;
388
+ if (/[\r\n]/.test(value)) continue;
389
+ safe[name] = value;
390
+ }
391
+ return Object.keys(safe).length > 0 ? safe : void 0;
392
+ }
393
+ function buildVideoSource(data) {
394
+ const headers = filterPlaybackRequestHeaders(data.playback.requestHeaders);
395
+ const base = {
396
+ uri: data.playback.uri,
397
+ metadata: {
398
+ title: data.title,
399
+ artist: data.branding.organizationName ?? void 0,
400
+ artwork: data.posterUrl ?? void 0
401
+ }
402
+ };
403
+ if (headers) {
404
+ return { ...base, headers };
405
+ }
406
+ return base;
407
+ }
408
+ function configurePlaybackDefaults(player, data) {
409
+ const c = data.controls;
410
+ player.loop = c.loop;
411
+ player.muted = c.muted;
412
+ player.timeUpdateEventInterval = 1;
413
+ }
414
+ function applyNativePlaybackPresentation(player, opts) {
415
+ player.staysActiveInBackground = opts.backgroundPlayback ?? false;
416
+ player.showNowPlayingNotification = opts.lockScreenNowPlaying ?? false;
417
+ }
418
+ function syncPlaybackIntent(player, shouldPlay) {
419
+ if (shouldPlay) {
420
+ player.play();
421
+ } else {
422
+ player.pause();
423
+ }
424
+ }
425
+ var EMPTY_PRESENTATION = buildMoviieVideoPresentationProps({
426
+ profile: "",
427
+ showPip: false
428
+ });
429
+ function useMoviiePlayer(options) {
430
+ const ctx = useMoviieContext();
431
+ const { data, error, isLoading, retry } = useMoviiePlayback({
432
+ embedId: options.embedId
433
+ });
434
+ const rememberEnabled = useMemo(() => {
435
+ if (options.rememberPosition === false) {
436
+ return false;
437
+ }
438
+ if (options.rememberPosition === true) {
439
+ return true;
440
+ }
441
+ return data?.controls.rememberPosition === true;
442
+ }, [data?.controls.rememberPosition, options.rememberPosition]);
443
+ const player = useVideoPlayer(null, (initialPlayer) => {
444
+ initialPlayer.timeUpdateEventInterval = 1;
445
+ });
446
+ useMoviiePlaybackResumePersistence({
447
+ durationSeconds: data?.durationSeconds,
448
+ embedId: data?.embedId,
449
+ enabled: rememberEnabled && data != null,
450
+ player
451
+ });
452
+ const resumeHandledRef = useRef(false);
453
+ useEffect(() => {
454
+ resumeHandledRef.current = false;
455
+ }, [data?.embedId, data?.playback.uri]);
456
+ useEffect(() => {
457
+ if (!data) {
458
+ return;
459
+ }
460
+ const src = buildVideoSource(data);
461
+ const shouldPlay = options.autoplay !== void 0 ? options.autoplay : data.controls.autoplay;
462
+ player.replace(src);
463
+ configurePlaybackDefaults(player, data);
464
+ applyNativePlaybackPresentation(player, {
465
+ backgroundPlayback: options.backgroundPlayback,
466
+ lockScreenNowPlaying: options.lockScreenNowPlaying
467
+ });
468
+ const restoreAndSync = async () => {
469
+ if (player.status !== "readyToPlay") {
470
+ return;
471
+ }
472
+ if (!resumeHandledRef.current) {
473
+ resumeHandledRef.current = true;
474
+ if (rememberEnabled) {
475
+ const stored = await readStoredResumeSeconds(data.embedId);
476
+ const clamped = clampResumeTimeSeconds(stored, data.durationSeconds);
477
+ if (clamped != null) {
478
+ player.currentTime = clamped;
479
+ }
480
+ }
481
+ }
482
+ syncPlaybackIntent(player, shouldPlay);
483
+ };
484
+ const sub = player.addListener("statusChange", (payload) => {
485
+ if (payload.status === "readyToPlay") {
486
+ void restoreAndSync();
487
+ }
488
+ });
489
+ void restoreAndSync();
490
+ return () => {
491
+ sub.remove();
492
+ };
493
+ }, [
494
+ data,
495
+ player,
496
+ options.autoplay,
497
+ options.backgroundPlayback,
498
+ options.lockScreenNowPlaying,
499
+ rememberEnabled
500
+ ]);
501
+ const videoPresentation = useMemo(() => {
502
+ if (!data) {
503
+ return EMPTY_PRESENTATION;
504
+ }
505
+ return buildMoviieVideoPresentationProps({
506
+ pictureInPicture: options.pictureInPicture,
507
+ pictureInPictureAutoStart: options.pictureInPictureAutoStart,
508
+ profile: data.profile,
509
+ showPip: data.controls.showPip
510
+ });
511
+ }, [
512
+ data,
513
+ options.pictureInPicture,
514
+ options.pictureInPictureAutoStart
515
+ ]);
516
+ const eventsBaseUrl = data?.telemetry.bootstrapUrl != null ? deriveTelemetryEventsBaseUrlFromBootstrapUrl(data.telemetry.bootstrapUrl) ?? ctx.client.getEventsBaseUrl() : ctx.client.getEventsBaseUrl();
517
+ useMoviieTelemetry(
518
+ player,
519
+ data,
520
+ eventsBaseUrl,
521
+ ctx.telemetryHeaders,
522
+ ctx.viewerTokenStore
523
+ );
524
+ return {
525
+ player,
526
+ playback: data,
527
+ error,
528
+ isLoading,
529
+ videoPresentation,
530
+ retry
531
+ };
532
+ }
533
+ function useMoviieEvent(player, eventName, handler) {
534
+ useEffect(() => {
535
+ if (!player) {
536
+ return;
537
+ }
538
+ if (eventName === PLAYER_API_EVENTS.TIME_UPDATE) {
539
+ const previousInterval = player.timeUpdateEventInterval;
540
+ if (player.timeUpdateEventInterval <= 0) {
541
+ player.timeUpdateEventInterval = 0.25;
542
+ }
543
+ const sub = player.addListener("timeUpdate", () => {
544
+ handler();
545
+ });
546
+ return () => {
547
+ sub.remove();
548
+ player.timeUpdateEventInterval = previousInterval;
549
+ };
550
+ }
551
+ if (eventName === PLAYER_API_EVENTS.PLAY) {
552
+ const sub = player.addListener("playingChange", ({ isPlaying }) => {
553
+ if (isPlaying) {
554
+ handler();
555
+ }
556
+ });
557
+ return () => {
558
+ sub.remove();
559
+ };
560
+ }
561
+ if (eventName === PLAYER_API_EVENTS.PAUSE) {
562
+ const sub = player.addListener("playingChange", ({ isPlaying }) => {
563
+ if (!isPlaying) {
564
+ handler();
565
+ }
566
+ });
567
+ return () => {
568
+ sub.remove();
569
+ };
570
+ }
571
+ if (eventName === PLAYER_API_EVENTS.ENDED) {
572
+ const sub = player.addListener("playToEnd", () => {
573
+ handler();
574
+ });
575
+ return () => {
576
+ sub.remove();
577
+ };
578
+ }
579
+ if (eventName === PLAYER_API_EVENTS.DURATION_CHANGE) {
580
+ const sub = player.addListener("sourceChange", () => {
581
+ handler();
582
+ });
583
+ return () => {
584
+ sub.remove();
585
+ };
586
+ }
587
+ if (eventName === PLAYER_API_EVENTS.VOLUME_CHANGE) {
588
+ const sub = player.addListener("volumeChange", () => {
589
+ handler();
590
+ });
591
+ return () => {
592
+ sub.remove();
593
+ };
594
+ }
595
+ if (eventName === PLAYER_API_EVENTS.READY) {
596
+ const sub = player.addListener("statusChange", ({ status }) => {
597
+ if (status === "readyToPlay") {
598
+ handler();
599
+ }
600
+ });
601
+ return () => {
602
+ sub.remove();
603
+ };
604
+ }
605
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
606
+ console.warn(
607
+ `[@moviie/player-expo] useMoviieEvent: "${eventName}" n\xE3o tem mapeamento para expo-video nesta vers\xE3o`
608
+ );
609
+ }
610
+ return void 0;
611
+ }, [player, eventName, handler]);
612
+ }
613
+ function useMoviiePlaybackEnded(player, onEnded) {
614
+ useMoviieEvent(player, PLAYER_API_EVENTS.ENDED, onEnded);
615
+ }
616
+
617
+ // src/lib/compute-moviie-playback-ended.ts
618
+ function computeMoviiePlaybackEnded(args) {
619
+ if (args.loop) {
620
+ return false;
621
+ }
622
+ if (args.isLive) {
623
+ return false;
624
+ }
625
+ if (args.playing) {
626
+ return false;
627
+ }
628
+ if (!Number.isFinite(args.duration) || args.duration <= 0) {
629
+ return false;
630
+ }
631
+ if (!Number.isFinite(args.currentTime)) {
632
+ return false;
633
+ }
634
+ return args.currentTime >= args.duration - MOVIIE_SKIN_TIMELINE_END_SNAP_EPSILON_SECONDS;
635
+ }
636
+
637
+ // src/lib/add-moviie-playback-ended-listener.ts
638
+ function addMoviiePlaybackEndedListener(player, onEnded) {
639
+ const sub = player.addListener("playToEnd", onEnded);
640
+ return { remove: () => sub.remove() };
641
+ }
642
+
643
+ // src/lib/build-moviie-watch-embed-url.ts
644
+ function normalizeWatchOrigin(origin) {
645
+ return origin.trim().replace(/\/+$/, "");
646
+ }
647
+ function buildMoviieWatchEmbedUrl(embedId, watchOrigin) {
648
+ const base = normalizeWatchOrigin(watchOrigin);
649
+ const id = embedId.trim();
650
+ return `${base}/embed/${encodeURIComponent(id)}`;
651
+ }
652
+ function resolveMoviieWatchOrigin() {
653
+ try {
654
+ const raw = typeof process !== "undefined" ? process.env.EXPO_PUBLIC_MOVIIE_WATCH_ORIGIN?.trim() : void 0;
655
+ if (raw) {
656
+ return normalizeWatchOrigin(raw);
657
+ }
658
+ } catch {
659
+ }
660
+ return MOVIIE_WATCH_ORIGIN_DEFAULT;
661
+ }
662
+ var Svg = RNSvg;
663
+ var jsx_native_bridge_default = Svg;
664
+ var Path = Path$1;
665
+ var Defs = Defs$1;
666
+ var LinearGradient = LinearGradient$1;
667
+ var Rect = Rect$1;
668
+ var Stop = Stop$1;
669
+ var AnimatedView = Animated.View;
670
+ var ReanimatedView = AnimatedReanimated.View;
671
+
672
+ // src/lib/moviie-telemetry-callbacks.ts
673
+ function safeInvokeMoviieTelemetry(callback, ...args) {
674
+ if (callback == null) {
675
+ return;
676
+ }
677
+ try {
678
+ callback(...args);
679
+ } catch (err) {
680
+ console.warn("[@moviie/player-expo] telemetry callback threw \u2014 ignorado", err);
681
+ }
682
+ }
683
+
684
+ // src/lib/moviie-shell-tokens.ts
685
+ var MOVIIE_SHELL_BACKGROUND_HEX = "#0a0a0a";
686
+ var MOVIIE_SHELL_RADIAL_INNER_HEX = "#1a1a1a";
687
+ var MOVIIE_SHELL_HEADING_HEX = "#f8fafc";
688
+ var MOVIIE_SHELL_DESCRIPTION_HEX = "#e5e7eb";
689
+ var MOVIIE_SHELL_ERROR_CODE_HEX = "#4b5563";
690
+ var MOVIIE_SHELL_BUTTON_BG_HEX = "#0a0a0a";
691
+ var MOVIIE_SHELL_BUTTON_BORDER_RGBA = "rgba(255,255,255,0.15)";
692
+ var MOVIIE_SHELL_BUTTON_LABEL_HEX = "#f8fafc";
693
+ var MOVIIE_SHELL_HEADING_FONT_SIZE_PX = 22;
694
+ var MOVIIE_SHELL_DESCRIPTION_FONT_SIZE_PX = 14;
695
+ var MOVIIE_SHELL_BUTTON_LABEL_FONT_SIZE_PX = 14;
696
+ var MOVIIE_SHELL_CONTENT_MAX_WIDTH_PX = 280;
697
+ var MOVIIE_SHELL_BUTTON_PADDING_H_PX = 22;
698
+ var MOVIIE_SHELL_BUTTON_PADDING_V_PX = 12;
699
+ var MOVIIE_SHELL_BUTTON_RADIUS_PX = 9999;
700
+ var MOVIIE_SHELL_CROSSFADE_DURATION_MS = 240;
701
+ var MOVIIE_SHELL_DEFAULT_ASPECT_RATIO = 16 / 9;
702
+ var MOVIIE_DASHBOARD_URL = "https://app.moviie.ai";
703
+
704
+ // src/lib/partition-moviie-video-host-style.ts
705
+ var VIDEO_SURFACE_STYLE_KEYS = /* @__PURE__ */ new Set([
706
+ "backgroundColor",
707
+ "borderRadius",
708
+ "borderTopLeftRadius",
709
+ "borderTopRightRadius",
710
+ "borderBottomLeftRadius",
711
+ "borderBottomRightRadius",
712
+ "borderCurve",
713
+ "borderWidth",
714
+ "borderColor"
715
+ ]);
716
+ function partitionMoviieVideoHostStyle(flatStyle) {
717
+ if (flatStyle == null || Object.keys(flatStyle).length === 0) {
718
+ return { columnStyle: void 0, surfaceStyle: void 0 };
719
+ }
720
+ const flat = flatStyle;
721
+ const column = {};
722
+ const surface = {};
723
+ for (const key of Object.keys(flat)) {
724
+ const value = flat[key];
725
+ if (value === void 0) {
726
+ continue;
727
+ }
728
+ if (VIDEO_SURFACE_STYLE_KEYS.has(key)) {
729
+ surface[key] = value;
730
+ } else {
731
+ column[key] = value;
732
+ }
733
+ }
734
+ return {
735
+ columnStyle: Object.keys(column).length > 0 ? column : void 0,
736
+ surfaceStyle: Object.keys(surface).length > 0 ? surface : void 0
737
+ };
738
+ }
739
+
740
+ // src/lib/compute-custom-fullscreen-stage-dimensions.ts
741
+ function computeCustomFullscreenStageDimensions(args) {
742
+ const pad = args.stageOverscanPx;
743
+ const usableW = Math.max(
744
+ 0,
745
+ args.windowWidth - args.safeInsets.left - args.safeInsets.right - 2 * pad
746
+ );
747
+ const usableH = Math.max(
748
+ 0,
749
+ args.windowHeight - args.safeInsets.top - args.safeInsets.bottom - 2 * pad
750
+ );
751
+ const longSide = Math.max(usableW, usableH);
752
+ const shortSide = Math.min(usableW, usableH);
753
+ return {
754
+ longSide,
755
+ shortSide,
756
+ layoutMetricsBasisWidth: longSide
757
+ };
758
+ }
759
+
760
+ // src/lib/format-playback-clock.ts
761
+ function pad2(value) {
762
+ return String(Math.floor(value)).padStart(2, "0");
763
+ }
764
+ function formatSegment(totalSeconds) {
765
+ if (!Number.isFinite(totalSeconds) || totalSeconds < 0) {
766
+ return "0:00";
767
+ }
768
+ const seconds = totalSeconds % 60;
769
+ const minutes = Math.floor(totalSeconds / 60) % 60;
770
+ const hours = Math.floor(totalSeconds / 3600);
771
+ if (hours > 0) {
772
+ return `${hours}:${pad2(minutes)}:${pad2(seconds)}`;
773
+ }
774
+ return `${Math.floor(minutes)}:${pad2(seconds)}`;
775
+ }
776
+ function formatPlaybackClock(currentSeconds, durationSeconds) {
777
+ const current = formatSegment(currentSeconds);
778
+ if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) {
779
+ return current;
780
+ }
781
+ return `${current} / ${formatSegment(durationSeconds)}`;
782
+ }
783
+ var MOVIIE_EMBED_MEDIA_ICON_VIEW_BOX = "0 0 32 32";
784
+ function IconShell({
785
+ size,
786
+ children
787
+ }) {
788
+ const pixels = size ?? 22;
789
+ return /* @__PURE__ */ jsx(
790
+ jsx_native_bridge_default,
791
+ {
792
+ accessibilityElementsHidden: true,
793
+ height: pixels,
794
+ importantForAccessibility: "no",
795
+ viewBox: MOVIIE_EMBED_MEDIA_ICON_VIEW_BOX,
796
+ width: pixels,
797
+ children
798
+ }
799
+ );
800
+ }
801
+ function EmbedMediaIconPlay(props) {
802
+ return /* @__PURE__ */ jsx(IconShell, { size: props.size, children: /* @__PURE__ */ jsx(
803
+ Path,
804
+ {
805
+ d: "M10.6667 6.6548C10.6667 6.10764 11.2894 5.79346 11.7295 6.11862L24.377 15.4634C24.7377 15.7298 24.7377 16.2692 24.3771 16.5357L11.7295 25.8813C11.2895 26.2065 10.6667 25.8923 10.6667 25.3451L10.6667 6.6548Z",
806
+ fill: props.color
807
+ }
808
+ ) });
809
+ }
810
+ function EmbedMediaIconPause(props) {
811
+ return /* @__PURE__ */ jsxs(IconShell, { size: props.size, children: [
812
+ /* @__PURE__ */ jsx(
813
+ Path,
814
+ {
815
+ d: "M8.66667 6.66667C8.29848 6.66667 8 6.96514 8 7.33333V24.6667C8 25.0349 8.29848 25.3333 8.66667 25.3333H12.6667C13.0349 25.3333 13.3333 25.0349 13.3333 24.6667V7.33333C13.3333 6.96514 13.0349 6.66667 12.6667 6.66667H8.66667Z",
816
+ fill: props.color
817
+ }
818
+ ),
819
+ /* @__PURE__ */ jsx(
820
+ Path,
821
+ {
822
+ d: "M19.3333 6.66667C18.9651 6.66667 18.6667 6.96514 18.6667 7.33333V24.6667C18.6667 25.0349 18.9651 25.3333 19.3333 25.3333H23.3333C23.7015 25.3333 24 25.0349 24 24.6667V7.33333C24 6.96514 23.7015 6.66667 23.3333 6.66667H19.3333Z",
823
+ fill: props.color
824
+ }
825
+ )
826
+ ] });
827
+ }
828
+ function EmbedMediaIconReplay(props) {
829
+ return /* @__PURE__ */ jsx(IconShell, { size: props.size, children: /* @__PURE__ */ jsx(
830
+ Path,
831
+ {
832
+ d: "M15.6038 12.2147C16.0439 12.5399 16.6667 12.2257 16.6667 11.6786V10.1789C16.6667 10.1001 16.7351 10.0384 16.8134 10.0479C20.1116 10.4494 22.6667 13.2593 22.6667 16.6659C22.6667 20.3481 19.6817 23.3332 15.9995 23.3332C12.542 23.3332 9.69927 20.7014 9.36509 17.332C9.32875 16.9655 9.03371 16.6662 8.66548 16.6662L6.66655 16.6666C6.29841 16.6666 5.99769 16.966 6.02187 17.3334C6.36494 22.5454 10.7012 26.6667 16 26.6667C21.5228 26.6667 26 22.1895 26 16.6667C26 11.4103 21.9444 7.10112 16.7916 6.69757C16.7216 6.69209 16.6667 6.63396 16.6667 6.56372V4.98824C16.6667 4.44106 16.0439 4.12689 15.6038 4.45206L11.0765 7.79738C10.7159 8.06387 10.7159 8.60326 11.0766 8.86973L15.6038 12.2147Z",
833
+ fill: props.color
834
+ }
835
+ ) });
836
+ }
837
+ function EmbedMediaIconSeekBackward10(props) {
838
+ const fill = props.color;
839
+ return /* @__PURE__ */ jsxs(IconShell, { size: props.size, children: [
840
+ /* @__PURE__ */ jsx(
841
+ Path,
842
+ {
843
+ d: "M16.6667 10.3452C16.6667 10.8924 16.0439 11.2066 15.6038 10.8814L11.0766 7.5364C10.7159 7.26993 10.7159 6.73054 11.0766 6.46405L15.6038 3.11873C16.0439 2.79356 16.6667 3.10773 16.6667 3.6549V5.22682C16.6667 5.29746 16.7223 5.35579 16.7927 5.36066C22.6821 5.76757 27.3333 10.674 27.3333 16.6667C27.3333 22.9259 22.2592 28 16 28C9.96483 28 5.03145 23.2827 4.68601 17.3341C4.66466 16.9665 4.96518 16.6673 5.33339 16.6673H7.3334C7.70157 16.6673 7.99714 16.9668 8.02743 17.3337C8.36638 21.4399 11.8064 24.6667 16 24.6667C20.4183 24.6667 24 21.085 24 16.6667C24 12.5225 20.8483 9.11428 16.8113 8.70739C16.7337 8.69957 16.6667 8.76096 16.6667 8.83893V10.3452Z",
844
+ fill
845
+ }
846
+ ),
847
+ /* @__PURE__ */ jsx(
848
+ Path,
849
+ {
850
+ clipRule: "evenodd",
851
+ d: "M17.0879 19.679C17.4553 19.9195 17.8928 20.0398 18.4004 20.0398C18.9099 20.0398 19.3474 19.9205 19.7129 19.6818C20.0803 19.4413 20.3635 19.0938 20.5623 18.6392C20.7612 18.1847 20.8606 17.6373 20.8606 16.9972C20.8625 16.3608 20.764 15.8192 20.5652 15.3722C20.3663 14.9252 20.0822 14.5853 19.7129 14.3523C19.3455 14.1175 18.908 14 18.4004 14C17.8928 14 17.4553 14.1175 17.0879 14.3523C16.7224 14.5853 16.4402 14.9252 16.2413 15.3722C16.0443 15.8173 15.9449 16.3589 15.943 16.9972C15.9411 17.6354 16.0396 18.1818 16.2385 18.6364C16.4373 19.089 16.7205 19.4366 17.0879 19.679ZM19.1362 18.4262C18.9487 18.7349 18.7034 18.8892 18.4004 18.8892C18.1996 18.8892 18.0226 18.8211 17.8691 18.6847C17.7157 18.5464 17.5964 18.3372 17.5112 18.0568C17.4279 17.7765 17.3871 17.4233 17.389 16.9972C17.3909 16.3684 17.4847 15.9025 17.6703 15.5995C17.8559 15.2945 18.0993 15.1421 18.4004 15.1421C18.603 15.1421 18.7801 15.2093 18.9316 15.3438C19.0832 15.4782 19.2015 15.6828 19.2868 15.9574C19.372 16.2301 19.4146 16.5767 19.4146 16.9972C19.4165 17.6392 19.3237 18.1156 19.1362 18.4262Z",
852
+ fill,
853
+ fillRule: "evenodd"
854
+ }
855
+ ),
856
+ /* @__PURE__ */ jsx(
857
+ Path,
858
+ {
859
+ d: "M13.7746 19.8978C13.8482 19.8978 13.9079 19.8381 13.9079 19.7644V14.2129C13.9079 14.1393 13.8482 14.0796 13.7746 14.0796H12.642C12.6171 14.0796 12.5927 14.0865 12.5716 14.0997L11.2322 14.9325C11.1931 14.9568 11.1693 14.9996 11.1693 15.0457V15.9497C11.1693 16.0539 11.2833 16.1178 11.3722 16.0635L12.464 15.396C12.4682 15.3934 12.473 15.3921 12.4779 15.3921C12.4926 15.3921 12.5045 15.404 12.5045 15.4187V19.7644C12.5045 19.8381 12.5642 19.8978 12.6378 19.8978H13.7746Z",
860
+ fill
861
+ }
862
+ )
863
+ ] });
864
+ }
865
+ function EmbedMediaIconSeekForward10(props) {
866
+ const fill = props.color;
867
+ return /* @__PURE__ */ jsxs(IconShell, { size: props.size, children: [
868
+ /* @__PURE__ */ jsx(
869
+ Path,
870
+ {
871
+ d: "M15.3333 10.3452C15.3333 10.8924 15.9561 11.2066 16.3962 10.8814L20.9234 7.5364C21.2841 7.26993 21.2841 6.73054 20.9235 6.46405L16.3962 3.11873C15.9561 2.79356 15.3333 3.10773 15.3333 3.6549V5.22682C15.3333 5.29746 15.2778 5.35579 15.2073 5.36066C9.31791 5.76757 4.66667 10.674 4.66667 16.6667C4.66667 22.9259 9.74078 28 16 28C22.0352 28 26.9686 23.2827 27.314 17.3341C27.3354 16.9665 27.0348 16.6673 26.6666 16.6673H24.6666C24.2984 16.6673 24.0029 16.9668 23.9726 17.3337C23.6336 21.4399 20.1937 24.6667 16 24.6667C11.5817 24.6667 8 21.085 8 16.6667C8 12.5225 11.1517 9.11428 15.1887 8.70739C15.2663 8.69957 15.3333 8.76096 15.3333 8.83893V10.3452Z",
872
+ fill
873
+ }
874
+ ),
875
+ /* @__PURE__ */ jsx(
876
+ Path,
877
+ {
878
+ clipRule: "evenodd",
879
+ d: "M17.0879 19.679C17.4553 19.9195 17.8928 20.0398 18.4004 20.0398C18.9099 20.0398 19.3474 19.9205 19.7129 19.6818C20.0803 19.4413 20.3635 19.0938 20.5623 18.6392C20.7612 18.1847 20.8606 17.6373 20.8606 16.9972C20.8625 16.3608 20.764 15.8192 20.5652 15.3722C20.3663 14.9252 20.0822 14.5853 19.7129 14.3523C19.3455 14.1175 18.908 14 18.4004 14C17.8928 14 17.4553 14.1175 17.0879 14.3523C16.7224 14.5853 16.4402 14.9252 16.2413 15.3722C16.0443 15.8173 15.9449 16.3589 15.943 16.9972C15.9411 17.6354 16.0396 18.1818 16.2385 18.6364C16.4373 19.089 16.7205 19.4366 17.0879 19.679ZM19.1362 18.4262C18.9487 18.7349 18.7034 18.8892 18.4004 18.8892C18.1996 18.8892 18.0225 18.8211 17.8691 18.6847C17.7157 18.5464 17.5964 18.3372 17.5112 18.0568C17.4278 17.7765 17.3871 17.4233 17.389 16.9972C17.3909 16.3684 17.4847 15.9025 17.6703 15.5995C17.8559 15.2945 18.0992 15.1421 18.4004 15.1421C18.603 15.1421 18.7801 15.2093 18.9316 15.3438C19.0831 15.4782 19.2015 15.6828 19.2867 15.9574C19.372 16.2301 19.4146 16.5767 19.4146 16.9972C19.4165 17.6392 19.3237 18.1156 19.1362 18.4262Z",
880
+ fill,
881
+ fillRule: "evenodd"
882
+ }
883
+ ),
884
+ /* @__PURE__ */ jsx(
885
+ Path,
886
+ {
887
+ d: "M13.7746 19.8978C13.8482 19.8978 13.9079 19.8381 13.9079 19.7644V14.2129C13.9079 14.1393 13.8482 14.0796 13.7746 14.0796H12.642C12.6171 14.0796 12.5927 14.0865 12.5716 14.0997L11.2322 14.9325C11.1931 14.9568 11.1693 14.9996 11.1693 15.0457V15.9497C11.1693 16.0539 11.2833 16.1178 11.3722 16.0635L12.464 15.396C12.4682 15.3934 12.473 15.3921 12.4779 15.3921C12.4926 15.3921 12.5045 15.404 12.5045 15.4187V19.7644C12.5045 19.8381 12.5642 19.8978 12.6378 19.8978H13.7746Z",
888
+ fill
889
+ }
890
+ )
891
+ ] });
892
+ }
893
+ function EmbedMediaIconFullscreen(props) {
894
+ return /* @__PURE__ */ jsxs(IconShell, { size: props.size, children: [
895
+ /* @__PURE__ */ jsx(
896
+ Path,
897
+ {
898
+ d: "M25.3299 7.26517C25.2958 6.929 25.0119 6.66666 24.6667 6.66666H19.3334C18.9652 6.66666 18.6667 6.96514 18.6667 7.33333V9.33333C18.6667 9.70152 18.9652 10 19.3334 10L21.8667 10C21.9403 10 22 10.0597 22 10.1333V12.6667C22 13.0349 22.2985 13.3333 22.6667 13.3333H24.6667C25.0349 13.3333 25.3334 13.0349 25.3334 12.6667V7.33333C25.3334 7.31032 25.3322 7.28758 25.3299 7.26517Z",
899
+ fill: props.color
900
+ }
901
+ ),
902
+ /* @__PURE__ */ jsx(
903
+ Path,
904
+ {
905
+ d: "M22 21.8667C22 21.9403 21.9403 22 21.8667 22L19.3334 22C18.9652 22 18.6667 22.2985 18.6667 22.6667V24.6667C18.6667 25.0349 18.9652 25.3333 19.3334 25.3333L24.6667 25.3333C25.0349 25.3333 25.3334 25.0349 25.3334 24.6667V19.3333C25.3334 18.9651 25.0349 18.6667 24.6667 18.6667H22.6667C22.2985 18.6667 22 18.9651 22 19.3333V21.8667Z",
906
+ fill: props.color
907
+ }
908
+ ),
909
+ /* @__PURE__ */ jsx(
910
+ Path,
911
+ {
912
+ d: "M12.6667 22H10.1334C10.0597 22 10 21.9403 10 21.8667V19.3333C10 18.9651 9.70154 18.6667 9.33335 18.6667H7.33335C6.96516 18.6667 6.66669 18.9651 6.66669 19.3333V24.6667C6.66669 25.0349 6.96516 25.3333 7.33335 25.3333H12.6667C13.0349 25.3333 13.3334 25.0349 13.3334 24.6667V22.6667C13.3334 22.2985 13.0349 22 12.6667 22Z",
913
+ fill: props.color
914
+ }
915
+ ),
916
+ /* @__PURE__ */ jsx(
917
+ Path,
918
+ {
919
+ d: "M10 12.6667V10.1333C10 10.0597 10.0597 10 10.1334 10L12.6667 10C13.0349 10 13.3334 9.70152 13.3334 9.33333V7.33333C13.3334 6.96514 13.0349 6.66666 12.6667 6.66666H7.33335C6.96516 6.66666 6.66669 6.96514 6.66669 7.33333V12.6667C6.66669 13.0349 6.96516 13.3333 7.33335 13.3333H9.33335C9.70154 13.3333 10 13.0349 10 12.6667Z",
920
+ fill: props.color
921
+ }
922
+ )
923
+ ] });
924
+ }
925
+ function EmbedMediaIconFullscreenExit(props) {
926
+ return /* @__PURE__ */ jsxs(IconShell, { size: props.size, children: [
927
+ /* @__PURE__ */ jsx(
928
+ Path,
929
+ {
930
+ d: "M19.3334 13.3333C18.9652 13.3333 18.6667 13.0349 18.6667 12.6667L18.6667 7.33333C18.6667 6.96514 18.9652 6.66666 19.3334 6.66666H21.3334C21.7015 6.66666 22 6.96514 22 7.33333V9.86666C22 9.9403 22.0597 10 22.1334 10L24.6667 10C25.0349 10 25.3334 10.2985 25.3334 10.6667V12.6667C25.3334 13.0349 25.0349 13.3333 24.6667 13.3333L19.3334 13.3333Z",
931
+ fill: props.color
932
+ }
933
+ ),
934
+ /* @__PURE__ */ jsx(
935
+ Path,
936
+ {
937
+ d: "M13.3334 19.3333C13.3334 18.9651 13.0349 18.6667 12.6667 18.6667H7.33335C6.96516 18.6667 6.66669 18.9651 6.66669 19.3333V21.3333C6.66669 21.7015 6.96516 22 7.33335 22H9.86669C9.94032 22 10 22.0597 10 22.1333L10 24.6667C10 25.0349 10.2985 25.3333 10.6667 25.3333H12.6667C13.0349 25.3333 13.3334 25.0349 13.3334 24.6667L13.3334 19.3333Z",
938
+ fill: props.color
939
+ }
940
+ ),
941
+ /* @__PURE__ */ jsx(
942
+ Path,
943
+ {
944
+ d: "M18.6667 24.6667C18.6667 25.0349 18.9652 25.3333 19.3334 25.3333H21.3334C21.7015 25.3333 22 25.0349 22 24.6667V22.1333C22 22.0597 22.0597 22 22.1334 22H24.6667C25.0349 22 25.3334 21.7015 25.3334 21.3333V19.3333C25.3334 18.9651 25.0349 18.6667 24.6667 18.6667L19.3334 18.6667C18.9652 18.6667 18.6667 18.9651 18.6667 19.3333L18.6667 24.6667Z",
945
+ fill: props.color
946
+ }
947
+ ),
948
+ /* @__PURE__ */ jsx(
949
+ Path,
950
+ {
951
+ d: "M10.6667 13.3333H12.6667C13.0349 13.3333 13.3334 13.0349 13.3334 12.6667L13.3334 10.6667V7.33333C13.3334 6.96514 13.0349 6.66666 12.6667 6.66666H10.6667C10.2985 6.66666 10 6.96514 10 7.33333L10 9.86666C10 9.9403 9.94033 10 9.86669 10L7.33335 10C6.96516 10 6.66669 10.2985 6.66669 10.6667V12.6667C6.66669 13.0349 6.96516 13.3333 7.33335 13.3333L10.6667 13.3333Z",
952
+ fill: props.color
953
+ }
954
+ )
955
+ ] });
956
+ }
957
+ function EmbedMediaIconPictureInPicture(props) {
958
+ const pixels = props.size ?? 22;
959
+ return /* @__PURE__ */ jsx(
960
+ jsx_native_bridge_default,
961
+ {
962
+ accessibilityElementsHidden: true,
963
+ height: pixels,
964
+ importantForAccessibility: "no",
965
+ viewBox: "0 0 24 24",
966
+ width: pixels,
967
+ children: /* @__PURE__ */ jsx(
968
+ Path,
969
+ {
970
+ d: "M19 7h-8v6h8V7zm0-2c1.1 0 2 .9 2 2v6c0 1.1-.9 2-2 2h-8c-1.1 0-2-.9-2-2V7c0-1.1.9-2 2-2h8zM5 5v14h14v-2H7V5H5z",
971
+ fill: props.color
972
+ }
973
+ )
974
+ }
975
+ );
976
+ }
977
+ function EmbedMediaIconCast(props) {
978
+ const pixels = props.size ?? 22;
979
+ return /* @__PURE__ */ jsx(
980
+ jsx_native_bridge_default,
981
+ {
982
+ accessibilityElementsHidden: true,
983
+ height: pixels,
984
+ importantForAccessibility: "no",
985
+ viewBox: "0 0 24 24",
986
+ width: pixels,
987
+ children: /* @__PURE__ */ jsx(
988
+ Path,
989
+ {
990
+ d: "M21 3H3c-1.1 0-2 .9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm0-4v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11z",
991
+ fill: props.color
992
+ }
993
+ )
994
+ }
995
+ );
996
+ }
997
+ function EmbedMediaIconCastConnected(props) {
998
+ const pixels = props.size ?? 22;
999
+ return /* @__PURE__ */ jsx(
1000
+ jsx_native_bridge_default,
1001
+ {
1002
+ accessibilityElementsHidden: true,
1003
+ height: pixels,
1004
+ importantForAccessibility: "no",
1005
+ viewBox: "0 0 24 24",
1006
+ width: pixels,
1007
+ children: /* @__PURE__ */ jsx(
1008
+ Path,
1009
+ {
1010
+ d: "M1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm18-7H5v1.63c3.96 1.28 7.09 4.41 8.37 8.37H19V7zM1 10v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11zm20-7H3c-1.1 0-2 .9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z",
1011
+ fill: props.color
1012
+ }
1013
+ )
1014
+ }
1015
+ );
1016
+ }
1017
+
1018
+ // src/lib/build-moviie-embed-brand-home-url.ts
1019
+ function buildMoviieEmbedBrandHomeUrl() {
1020
+ const url = new URL(`${MOVIIE_BRANDING_MARKETING_ORIGIN}/`);
1021
+ url.searchParams.set(MOVIIE_BRANDING_UTM_PARAM_SOURCE, MOVIIE_EMBED_BRAND_UTM.SOURCE);
1022
+ url.searchParams.set(MOVIIE_BRANDING_UTM_PARAM_MEDIUM, MOVIIE_EMBED_BRAND_UTM.MEDIUM);
1023
+ url.searchParams.set(MOVIIE_BRANDING_UTM_PARAM_CAMPAIGN, MOVIIE_EMBED_BRAND_UTM.CAMPAIGN);
1024
+ return url.toString();
1025
+ }
1026
+
1027
+ // src/lib/resolve-moviie-skin-layout-scale.ts
1028
+ function clampBetween(value, min, max) {
1029
+ return Math.min(Math.max(value, min), max);
1030
+ }
1031
+ function resolveMoviieSkinLayoutScale(windowWidth) {
1032
+ if (!Number.isFinite(windowWidth) || windowWidth <= 0) {
1033
+ return 1;
1034
+ }
1035
+ const raw = windowWidth / MOVIIE_SKIN_LAYOUT_REFERENCE_WIDTH_PT;
1036
+ return clampBetween(raw, MOVIIE_SKIN_LAYOUT_SCALE_MIN, MOVIIE_SKIN_LAYOUT_SCALE_MAX);
1037
+ }
1038
+
1039
+ // src/lib/build-moviie-skin-layout-metrics.ts
1040
+ function scaleSkinLayoutDp(layoutScale, baseAtReference) {
1041
+ return baseAtReference * layoutScale;
1042
+ }
1043
+ function buildMoviieSkinLayoutMetrics(windowWidth, options) {
1044
+ const layoutScale = resolveMoviieSkinLayoutScale(windowWidth);
1045
+ const brandMarkWidthPx = scaleSkinLayoutDp(
1046
+ layoutScale,
1047
+ MOVIIE_SKIN_BRAND_MARK_TARGET_WIDTH_AT_REFERENCE_PT
1048
+ );
1049
+ const brandMarkHeightPx = brandMarkWidthPx * (MOVIIE_SKIN_BRAND_MARK_ARTBOARD_HEIGHT_PX / MOVIIE_SKIN_BRAND_MARK_ARTBOARD_WIDTH_PX);
1050
+ const timelineThumbDiameterPx = scaleSkinLayoutDp(
1051
+ layoutScale,
1052
+ MOVIIE_SKIN_TIMELINE_THUMB_DIAMETER_PX
1053
+ );
1054
+ const timelineBarHeightPx = scaleSkinLayoutDp(layoutScale, MOVIIE_SKIN_TIMELINE_BAR_HEIGHT_PX);
1055
+ const timelineThumbBottomInsetPx = timelineBarHeightPx / 2 - timelineThumbDiameterPx / 2;
1056
+ const timelineTouchPaddingVerticalPx = scaleSkinLayoutDp(
1057
+ layoutScale,
1058
+ MOVIIE_SKIN_TIMELINE_TOUCH_PADDING_VERTICAL_PX
1059
+ );
1060
+ const fullscreenHudBoostPx = options?.fullscreenHudProtection === true ? scaleSkinLayoutDp(layoutScale, MOVIIE_SKIN_CUSTOM_FULLSCREEN_HUD_EDGE_BOOST_REFERENCE_PT) : 0;
1061
+ const hudEdgeInsetPx = scaleSkinLayoutDp(layoutScale, MOVIIE_SKIN_FLOATING_HUD_EDGE_INSET_PX) + fullscreenHudBoostPx;
1062
+ return {
1063
+ layoutScale,
1064
+ brandMarkWidthPx,
1065
+ brandMarkHeightPx,
1066
+ hudEdgeInsetPx,
1067
+ floatingHudAboveTimelineGapPx: scaleSkinLayoutDp(
1068
+ layoutScale,
1069
+ MOVIIE_SKIN_FLOATING_HUD_ABOVE_TIMELINE_GAP_AT_REFERENCE_PT
1070
+ ),
1071
+ hudClockCornerRadiusPx: scaleSkinLayoutDp(
1072
+ layoutScale,
1073
+ MOVIIE_SKIN_FLOATING_HUD_CLOCK_CORNER_RADIUS_PX
1074
+ ),
1075
+ controlIconPx: scaleSkinLayoutDp(layoutScale, MOVIIE_SKIN_CONTROL_ICON_PX),
1076
+ seekIconPx: scaleSkinLayoutDp(layoutScale, MOVIIE_SKIN_SEEK_ICON_PX),
1077
+ seekScrimOuterDiameterPx: scaleSkinLayoutDp(
1078
+ layoutScale,
1079
+ MOVIIE_SKIN_CENTER_SEEK_SCRIM_DIAMETER_AT_REFERENCE_PT
1080
+ ),
1081
+ centerPlayIconPx: scaleSkinLayoutDp(layoutScale, MOVIIE_SKIN_CENTER_PLAY_ICON_PX),
1082
+ centerClusterColumnGapPx: scaleSkinLayoutDp(
1083
+ layoutScale,
1084
+ MOVIIE_SKIN_CENTER_CLUSTER_COLUMN_GAP_PX
1085
+ ),
1086
+ playScrimOuterDiameterPx: scaleSkinLayoutDp(
1087
+ layoutScale,
1088
+ MOVIIE_SKIN_CENTER_PLAY_SCRIM_DIAMETER_AT_REFERENCE_PT
1089
+ ),
1090
+ clockTextFontSizePx: scaleSkinLayoutDp(
1091
+ layoutScale,
1092
+ MOVIIE_SKIN_CLOCK_TEXT_FONT_SIZE_AT_REFERENCE_PT
1093
+ ),
1094
+ timelineThumbDiameterPx,
1095
+ timelineTouchPaddingVerticalPx,
1096
+ timelineBarHeightPx,
1097
+ timelineThumbBottomInsetPx,
1098
+ timelineStackHeightChromeVisiblePx: timelineTouchPaddingVerticalPx + timelineThumbDiameterPx,
1099
+ bufferingIndicatorSlotPx: scaleSkinLayoutDp(
1100
+ layoutScale,
1101
+ MOVIIE_SKIN_BUFFERING_INDICATOR_SLOT_PX
1102
+ ),
1103
+ chromeEdgeGradientHeightPx: scaleSkinLayoutDp(
1104
+ layoutScale,
1105
+ MOVIIE_SKIN_CHROME_EDGE_GRADIENT_HEIGHT_AT_REFERENCE_PT
1106
+ ),
1107
+ floatingHudFullscreenScrimDiameterPx: scaleSkinLayoutDp(
1108
+ layoutScale,
1109
+ MOVIIE_SKIN_FLOATING_FULLSCREEN_SCRIM_DIAMETER_AT_REFERENCE_PT
1110
+ ),
1111
+ floatingHudFullscreenIconPx: scaleSkinLayoutDp(
1112
+ layoutScale,
1113
+ MOVIIE_SKIN_FLOATING_FULLSCREEN_ICON_AT_REFERENCE_PT
1114
+ ),
1115
+ floatingHudFullscreenTopIconPx: scaleSkinLayoutDp(
1116
+ layoutScale,
1117
+ MOVIIE_SKIN_FLOATING_FULLSCREEN_TOP_ICON_AT_REFERENCE_PT
1118
+ ),
1119
+ floatingHudButtonGapPx: scaleSkinLayoutDp(
1120
+ layoutScale,
1121
+ MOVIIE_SKIN_FLOATING_HUD_BUTTON_GAP_AT_REFERENCE_PT
1122
+ )
1123
+ };
1124
+ }
1125
+
1126
+ // src/lib/compute-playback-buffering.ts
1127
+ var PLAYER_STATUS_LOADING = "loading";
1128
+ function computePlaybackBuffering(state) {
1129
+ if (computeMoviiePlaybackEnded({
1130
+ playing: state.playing,
1131
+ currentTime: state.currentTime,
1132
+ duration: state.duration,
1133
+ isLive: state.isLive,
1134
+ loop: state.loop
1135
+ })) {
1136
+ return false;
1137
+ }
1138
+ if (state.status === PLAYER_STATUS_LOADING) {
1139
+ return true;
1140
+ }
1141
+ if (state.playing && Number.isFinite(state.duration) && state.duration > 0 && state.bufferedPosition >= 0 && state.currentTime > 0.08 && state.bufferedPosition < state.currentTime + MOVIIE_SKIN_BUFFER_UNDERRUN_LEAD_SECONDS) {
1142
+ return true;
1143
+ }
1144
+ return false;
1145
+ }
1146
+ function usePlaybackUiSync(player) {
1147
+ const [snapshot, setSnapshot] = useState({
1148
+ bufferedPosition: player.bufferedPosition,
1149
+ currentTime: player.currentTime,
1150
+ duration: player.duration,
1151
+ playing: player.playing,
1152
+ status: player.status
1153
+ });
1154
+ useEffect(() => {
1155
+ const subscriptions = [
1156
+ player.addListener("timeUpdate", ({ currentTime }) => {
1157
+ setSnapshot((previous) => ({
1158
+ ...previous,
1159
+ bufferedPosition: player.bufferedPosition,
1160
+ currentTime,
1161
+ duration: player.duration,
1162
+ status: player.status
1163
+ }));
1164
+ }),
1165
+ player.addListener("playingChange", ({ isPlaying }) => {
1166
+ setSnapshot((previous) => ({
1167
+ ...previous,
1168
+ playing: isPlaying,
1169
+ bufferedPosition: player.bufferedPosition,
1170
+ status: player.status
1171
+ }));
1172
+ }),
1173
+ player.addListener("sourceChange", () => {
1174
+ setSnapshot({
1175
+ bufferedPosition: player.bufferedPosition,
1176
+ currentTime: player.currentTime,
1177
+ duration: player.duration,
1178
+ playing: player.playing,
1179
+ status: player.status
1180
+ });
1181
+ }),
1182
+ player.addListener("statusChange", ({ status }) => {
1183
+ setSnapshot((previous) => ({
1184
+ ...previous,
1185
+ bufferedPosition: player.bufferedPosition,
1186
+ status
1187
+ }));
1188
+ }),
1189
+ player.addListener("playToEnd", () => {
1190
+ setSnapshot((previous) => ({
1191
+ ...previous,
1192
+ playing: false,
1193
+ bufferedPosition: player.bufferedPosition,
1194
+ currentTime: player.currentTime,
1195
+ duration: player.duration,
1196
+ status: player.status
1197
+ }));
1198
+ })
1199
+ ];
1200
+ return () => {
1201
+ for (const sub of subscriptions) {
1202
+ sub.remove();
1203
+ }
1204
+ };
1205
+ }, [player]);
1206
+ return snapshot;
1207
+ }
1208
+ var MoviieSkinChromeContext = createContext(
1209
+ null
1210
+ );
1211
+ function useMoviieSkinChrome() {
1212
+ const ctx = useContext(MoviieSkinChromeContext);
1213
+ if (ctx == null) {
1214
+ throw new Error("useMoviieSkinChrome must be used within MoviieSkinChromeProvider");
1215
+ }
1216
+ return ctx;
1217
+ }
1218
+ function MoviieSkinChromeProvider({
1219
+ children,
1220
+ player,
1221
+ playback,
1222
+ videoViewRef,
1223
+ customFullscreen = false,
1224
+ skinLayoutMetricsWidth,
1225
+ onRequestEnterCustomFullscreen,
1226
+ onRequestExitCustomFullscreenAnimated,
1227
+ autoHideMs: _unusedAutoHideMs,
1228
+ visibleByDefault = true,
1229
+ pictureInPictureControlEnabled = false,
1230
+ pictureInPictureActive = false,
1231
+ togglePictureInPicture: togglePictureInPictureProp,
1232
+ castControlEnabled = false,
1233
+ castConnected = false,
1234
+ openCastPicker: openCastPickerProp
1235
+ }) {
1236
+ const noopPictureInPicture = useCallback(() => {
1237
+ }, []);
1238
+ const togglePictureInPicture = togglePictureInPictureProp ?? noopPictureInPicture;
1239
+ const noopOpenCastPicker = useCallback(() => {
1240
+ }, []);
1241
+ const openCastPicker = openCastPickerProp ?? noopOpenCastPicker;
1242
+ const { width: windowWidth } = useWindowDimensions();
1243
+ const layoutBasisWidth = skinLayoutMetricsWidth ?? windowWidth;
1244
+ const layout = useMemo(
1245
+ () => buildMoviieSkinLayoutMetrics(layoutBasisWidth, {
1246
+ fullscreenHudProtection: customFullscreen
1247
+ }),
1248
+ [customFullscreen, layoutBasisWidth]
1249
+ );
1250
+ const ui = usePlaybackUiSync(player);
1251
+ const [chromeVisible, setChromeVisible] = useState(visibleByDefault);
1252
+ const [timelineScrubClockSeconds, setTimelineScrubClockSeconds] = useState(null);
1253
+ const playbackEndedForChrome = useMemo(
1254
+ () => computeMoviiePlaybackEnded({
1255
+ playing: ui.playing,
1256
+ currentTime: ui.currentTime,
1257
+ duration: ui.duration,
1258
+ isLive: player.isLive,
1259
+ loop: player.loop
1260
+ }),
1261
+ [ui.playing, ui.currentTime, ui.duration, player.isLive, player.loop]
1262
+ );
1263
+ useEffect(() => {
1264
+ if (playbackEndedForChrome) {
1265
+ setChromeVisible(true);
1266
+ }
1267
+ }, [playbackEndedForChrome]);
1268
+ const chromeOpacity = useRef(new Animated.Value(visibleByDefault ? 1 : 0)).current;
1269
+ useEffect(() => {
1270
+ Animated.timing(chromeOpacity, {
1271
+ duration: MOVIIE_SKIN_CONTROLS_FADE_MS,
1272
+ easing: Easing$1.out(Easing$1.quad),
1273
+ toValue: chromeVisible ? 1 : 0,
1274
+ useNativeDriver: true
1275
+ }).start();
1276
+ }, [chromeOpacity, chromeVisible]);
1277
+ const bumpChromeInteraction = useCallback(() => {
1278
+ setChromeVisible(true);
1279
+ }, []);
1280
+ const dismissChrome = useCallback(() => {
1281
+ setChromeVisible(false);
1282
+ }, []);
1283
+ const togglePlay = useCallback(() => {
1284
+ if (player.playing) {
1285
+ player.pause();
1286
+ return;
1287
+ }
1288
+ if (computeMoviiePlaybackEnded({
1289
+ playing: false,
1290
+ currentTime: player.currentTime,
1291
+ duration: player.duration,
1292
+ isLive: player.isLive,
1293
+ loop: player.loop
1294
+ })) {
1295
+ player.currentTime = 0;
1296
+ }
1297
+ player.play();
1298
+ setChromeVisible(false);
1299
+ }, [player]);
1300
+ const seekBack = useCallback(() => {
1301
+ void player.seekBy(-MOVIIE_SKIN_SEEK_JUMP_SECONDS);
1302
+ setChromeVisible(false);
1303
+ }, [player]);
1304
+ const seekForward = useCallback(() => {
1305
+ void player.seekBy(MOVIIE_SKIN_SEEK_JUMP_SECONDS);
1306
+ setChromeVisible(false);
1307
+ }, [player]);
1308
+ const toggleFullscreen = useCallback(() => {
1309
+ if (customFullscreen) {
1310
+ onRequestExitCustomFullscreenAnimated?.();
1311
+ return;
1312
+ }
1313
+ setChromeVisible(false);
1314
+ if (onRequestEnterCustomFullscreen != null) {
1315
+ onRequestEnterCustomFullscreen();
1316
+ return;
1317
+ }
1318
+ void videoViewRef.current?.enterFullscreen?.().catch(() => void 0);
1319
+ }, [
1320
+ customFullscreen,
1321
+ onRequestEnterCustomFullscreen,
1322
+ onRequestExitCustomFullscreenAnimated,
1323
+ videoViewRef
1324
+ ]);
1325
+ const isBuffering = useMemo(
1326
+ () => computePlaybackBuffering({
1327
+ ...ui,
1328
+ isLive: player.isLive,
1329
+ loop: player.loop
1330
+ }),
1331
+ [ui, player.isLive, player.loop]
1332
+ );
1333
+ const value = useMemo(
1334
+ () => ({
1335
+ playback,
1336
+ player,
1337
+ videoViewRef,
1338
+ chromeVisible,
1339
+ chromeOpacity,
1340
+ timelineScrubClockSeconds,
1341
+ setTimelineScrubClockSeconds,
1342
+ bumpChromeInteraction,
1343
+ dismissChrome,
1344
+ ui,
1345
+ isBuffering,
1346
+ layout,
1347
+ togglePlay,
1348
+ seekBack,
1349
+ seekForward,
1350
+ toggleFullscreen,
1351
+ isCustomFullscreen: customFullscreen,
1352
+ pictureInPictureActive,
1353
+ pictureInPictureControlEnabled,
1354
+ togglePictureInPicture,
1355
+ castControlEnabled,
1356
+ castConnected,
1357
+ openCastPicker
1358
+ }),
1359
+ [
1360
+ playback,
1361
+ player,
1362
+ videoViewRef,
1363
+ chromeVisible,
1364
+ chromeOpacity,
1365
+ timelineScrubClockSeconds,
1366
+ setTimelineScrubClockSeconds,
1367
+ bumpChromeInteraction,
1368
+ dismissChrome,
1369
+ ui,
1370
+ isBuffering,
1371
+ layout,
1372
+ togglePlay,
1373
+ seekBack,
1374
+ seekForward,
1375
+ toggleFullscreen,
1376
+ customFullscreen,
1377
+ pictureInPictureActive,
1378
+ pictureInPictureControlEnabled,
1379
+ togglePictureInPicture,
1380
+ castControlEnabled,
1381
+ castConnected,
1382
+ openCastPicker
1383
+ ]
1384
+ );
1385
+ return /* @__PURE__ */ jsx(MoviieSkinChromeContext.Provider, { value, children });
1386
+ }
1387
+ function MoviieEmbedBrandMark() {
1388
+ const { layout } = useMoviieSkinChrome();
1389
+ const fg = MOVIIE_SKIN_CHROME_FOREGROUND_HEX;
1390
+ const openMarketingSite = useCallback(() => {
1391
+ void Linking.openURL(buildMoviieEmbedBrandHomeUrl());
1392
+ }, []);
1393
+ return /* @__PURE__ */ jsx(
1394
+ Pressable,
1395
+ {
1396
+ accessibilityHint: "Abre o site da Moviie no navegador",
1397
+ accessibilityLabel: "Moviie \u2014 p\xE1gina inicial",
1398
+ accessibilityRole: "link",
1399
+ hitSlop: MOVIIE_SKIN_FLOATING_HUD_TRAILING_HIT_SLOP_PX,
1400
+ onPress: openMarketingSite,
1401
+ children: /* @__PURE__ */ jsxs(
1402
+ jsx_native_bridge_default,
1403
+ {
1404
+ accessibilityElementsHidden: true,
1405
+ height: layout.brandMarkHeightPx,
1406
+ importantForAccessibility: "no",
1407
+ viewBox: MOVIIE_EMBED_BRAND_VIEW_BOX,
1408
+ width: layout.brandMarkWidthPx,
1409
+ children: [
1410
+ /* @__PURE__ */ jsx(
1411
+ Path,
1412
+ {
1413
+ d: "M6.066 43.725H0V0h6.066L21.8 36.77 37.533 0h6.192v43.725H37.66V26.43c0-8.165.063-11.128.38-14.212L24.706 43.725h-5.813L5.623 12.337c.317 2.117.443 6.593.443 12.7z",
1414
+ fill: fg
1415
+ }
1416
+ ),
1417
+ /* @__PURE__ */ jsx(
1418
+ Path,
1419
+ {
1420
+ d: "M47.527 29.467c0-9.494 6.454-16.16 15.239-16.16 8.725 0 15.179 6.666 15.179 16.16s-6.454 16.16-15.179 16.16c-8.785 0-15.239-6.666-15.239-16.16m5.678 0c0 6.413 3.884 10.878 9.561 10.878 5.617 0 9.562-4.465 9.562-10.878S68.383 18.59 62.766 18.59c-5.677 0-9.561 4.465-9.561 10.878M159.227 45.626c-8.737 0-14.744-6.526-14.744-16.065 0-9.601 5.886-16.253 14.501-16.253 8.434 0 13.955 6.024 13.955 15.124v2.196l-22.934.063c.424 6.464 3.701 10.04 9.343 10.04 4.43 0 7.342-1.882 8.313-5.396H173c-1.456 6.589-6.431 10.291-13.773 10.291m-.243-27.36c-4.975 0-8.13 3.074-8.858 8.471h17.11c0-5.083-3.216-8.472-8.252-8.472",
1421
+ fill: fg
1422
+ }
1423
+ ),
1424
+ /* @__PURE__ */ jsx(
1425
+ Path,
1426
+ {
1427
+ d: "m140.681 15.209-12.313 28.516h-4.794l12.313-28.516zm-14.384 0-12.312 28.516h-4.811l12.313-28.516zm-39.363 0 6.553 16.138c1.06 2.768 1.927 5.221 2.42 7.006l.944-.007c.49-1.892 1.476-4.393 2.539-6.988l6.68-16.15h5.054L98.799 43.726h-5.15L81.747 15.21z",
1428
+ fill: "none",
1429
+ stroke: fg,
1430
+ strokeLinejoin: "bevel",
1431
+ strokeWidth: MOVIIE_EMBED_BRAND_LETTERMARK_STROKE_WIDTH
1432
+ }
1433
+ )
1434
+ ]
1435
+ }
1436
+ )
1437
+ }
1438
+ );
1439
+ }
1440
+ function pressOpacity(pressed) {
1441
+ return {
1442
+ opacity: pressed ? MOVIIE_SKIN_CHROME_CONTROL_PRESSED_OPACITY : MOVIIE_SKIN_CHROME_CONTROL_REST_OPACITY
1443
+ };
1444
+ }
1445
+ function ScrimPad(props) {
1446
+ return /* @__PURE__ */ jsx(
1447
+ Pressable,
1448
+ {
1449
+ accessibilityLabel: props.accessibilityLabel,
1450
+ accessibilityRole: "button",
1451
+ accessibilityState: { selected: props.selected },
1452
+ hitSlop: props.hitSlop,
1453
+ onPress: props.onPress,
1454
+ style: (state) => [props.chromeScrimBaseStyle, props.style, pressOpacity(state.pressed)],
1455
+ children: props.children
1456
+ }
1457
+ );
1458
+ }
1459
+ function MoviieSkinCastCircleButton(props) {
1460
+ const Icon = props.castConnected ? EmbedMediaIconCastConnected : EmbedMediaIconCast;
1461
+ return /* @__PURE__ */ jsx(
1462
+ ScrimPad,
1463
+ {
1464
+ accessibilityLabel: props.castConnected ? "Encerrar transmiss\xE3o" : "Transmitir",
1465
+ chromeScrimBaseStyle: props.chromeScrimBaseStyle,
1466
+ hitSlop: MOVIIE_SKIN_FLOATING_HUD_TRAILING_HIT_SLOP_PX,
1467
+ onPress: props.onPress,
1468
+ selected: props.castConnected,
1469
+ style: props.circleScrimStyle,
1470
+ children: /* @__PURE__ */ jsx(
1471
+ Icon,
1472
+ {
1473
+ color: MOVIIE_SKIN_CHROME_FOREGROUND_HEX,
1474
+ size: props.layout.floatingHudFullscreenIconPx
1475
+ }
1476
+ )
1477
+ }
1478
+ );
1479
+ }
1480
+ function MoviieSkinCastTopBareButton(props) {
1481
+ const Icon = props.castConnected ? EmbedMediaIconCastConnected : EmbedMediaIconCast;
1482
+ return /* @__PURE__ */ jsx(
1483
+ Pressable,
1484
+ {
1485
+ accessibilityLabel: props.castConnected ? "Encerrar transmiss\xE3o" : "Transmitir",
1486
+ accessibilityRole: "button",
1487
+ accessibilityState: { selected: props.castConnected },
1488
+ hitSlop: MOVIIE_SKIN_FLOATING_HUD_TRAILING_HIT_SLOP_PX,
1489
+ onPress: props.onPress,
1490
+ style: (state) => pressOpacity(state.pressed),
1491
+ children: /* @__PURE__ */ jsx(View, { children: /* @__PURE__ */ jsx(
1492
+ Icon,
1493
+ {
1494
+ color: MOVIIE_SKIN_CHROME_FOREGROUND_HEX,
1495
+ size: props.layout.floatingHudFullscreenTopIconPx * MOVIIE_SKIN_TOP_ICON_CAST_VISUAL_SCALE
1496
+ }
1497
+ ) })
1498
+ }
1499
+ );
1500
+ }
1501
+ function MoviieSkinChromeEdgeGradients(props) {
1502
+ const [hostWidth, setHostWidth] = useState(0);
1503
+ const [hostHeight, setHostHeight] = useState(0);
1504
+ const onHostLayout = useCallback((event) => {
1505
+ const { height, width } = event.nativeEvent.layout;
1506
+ setHostWidth(width);
1507
+ setHostHeight(height);
1508
+ }, []);
1509
+ const rawTopPx = props.layout.chromeEdgeGradientHeightPx;
1510
+ const rawBottomPx = props.layout.chromeEdgeGradientHeightPx + (props.showProgress ? props.layout.timelineStackHeightChromeVisiblePx : 0);
1511
+ const { effBottomPx, effTopPx, svgWidthPx } = useMemo(() => {
1512
+ if (hostWidth <= 0 || hostHeight <= 0) {
1513
+ return { effBottomPx: 0, effTopPx: 0, svgWidthPx: 0 };
1514
+ }
1515
+ const svgWidthPx2 = hostWidth;
1516
+ const maxTotalPx = Math.max(
1517
+ 0,
1518
+ hostHeight - MOVIIE_SKIN_CHROME_EDGE_GRADIENT_MIN_CLEAR_MIDDLE_PX
1519
+ );
1520
+ const rawSum = rawTopPx + rawBottomPx;
1521
+ const scale = rawSum > maxTotalPx && maxTotalPx > 0 && rawSum > 0 ? maxTotalPx / rawSum : 1;
1522
+ return {
1523
+ effBottomPx: rawBottomPx * scale,
1524
+ effTopPx: rawTopPx * scale,
1525
+ svgWidthPx: svgWidthPx2
1526
+ };
1527
+ }, [hostHeight, hostWidth, rawBottomPx, rawTopPx]);
1528
+ const topFillUrl = `url(#${MOVIIE_SKIN_CHROME_SVG_GRADIENT_TOP_EDGE_ID})`;
1529
+ const bottomFillUrl = `url(#${MOVIIE_SKIN_CHROME_SVG_GRADIENT_BOTTOM_EDGE_ID})`;
1530
+ const layoutReady = hostWidth > 0 && hostHeight > 0 && svgWidthPx > 0;
1531
+ const fadeEndOffset = String(MOVIIE_SKIN_CHROME_EDGE_GRADIENT_EDGE_SOLID_FADE_END_FRACTION);
1532
+ return /* @__PURE__ */ jsxs(
1533
+ View,
1534
+ {
1535
+ accessibilityElementsHidden: true,
1536
+ importantForAccessibility: "no-hide-descendants",
1537
+ onLayout: onHostLayout,
1538
+ pointerEvents: "none",
1539
+ style: StyleSheet.absoluteFillObject,
1540
+ testID: "moviie-chrome-edge-gradients",
1541
+ children: [
1542
+ layoutReady && effTopPx > 0 ? /* @__PURE__ */ jsx(
1543
+ View,
1544
+ {
1545
+ accessibilityElementsHidden: true,
1546
+ importantForAccessibility: "no-hide-descendants",
1547
+ pointerEvents: "none",
1548
+ style: {
1549
+ height: effTopPx,
1550
+ left: 0,
1551
+ overflow: "hidden",
1552
+ position: "absolute",
1553
+ top: 0,
1554
+ width: hostWidth
1555
+ },
1556
+ children: /* @__PURE__ */ jsxs(jsx_native_bridge_default, { accessibilityElementsHidden: true, height: effTopPx, width: svgWidthPx, children: [
1557
+ /* @__PURE__ */ jsx(Defs, { children: /* @__PURE__ */ jsxs(
1558
+ LinearGradient,
1559
+ {
1560
+ gradientUnits: "objectBoundingBox",
1561
+ id: MOVIIE_SKIN_CHROME_SVG_GRADIENT_TOP_EDGE_ID,
1562
+ x1: "0",
1563
+ x2: "0",
1564
+ y1: "0",
1565
+ y2: "1",
1566
+ children: [
1567
+ /* @__PURE__ */ jsx(
1568
+ Stop,
1569
+ {
1570
+ offset: "0",
1571
+ stopColor: MOVIIE_SKIN_CHROME_GRADIENT_BLACK_HEX,
1572
+ stopOpacity: MOVIIE_SKIN_CHROME_GRADIENT_EDGE_ALPHA
1573
+ }
1574
+ ),
1575
+ /* @__PURE__ */ jsx(
1576
+ Stop,
1577
+ {
1578
+ offset: fadeEndOffset,
1579
+ stopColor: MOVIIE_SKIN_CHROME_GRADIENT_BLACK_HEX,
1580
+ stopOpacity: 0
1581
+ }
1582
+ ),
1583
+ /* @__PURE__ */ jsx(Stop, { offset: "1", stopColor: MOVIIE_SKIN_CHROME_GRADIENT_BLACK_HEX, stopOpacity: 0 })
1584
+ ]
1585
+ }
1586
+ ) }),
1587
+ /* @__PURE__ */ jsx(Rect, { fill: topFillUrl, height: effTopPx, width: svgWidthPx })
1588
+ ] })
1589
+ }
1590
+ ) : null,
1591
+ layoutReady && effBottomPx > 0 ? /* @__PURE__ */ jsx(
1592
+ View,
1593
+ {
1594
+ accessibilityElementsHidden: true,
1595
+ importantForAccessibility: "no-hide-descendants",
1596
+ pointerEvents: "none",
1597
+ style: {
1598
+ bottom: 0,
1599
+ height: effBottomPx,
1600
+ left: 0,
1601
+ overflow: "hidden",
1602
+ position: "absolute",
1603
+ width: hostWidth
1604
+ },
1605
+ children: /* @__PURE__ */ jsxs(jsx_native_bridge_default, { accessibilityElementsHidden: true, height: effBottomPx, width: svgWidthPx, children: [
1606
+ /* @__PURE__ */ jsx(Defs, { children: /* @__PURE__ */ jsxs(
1607
+ LinearGradient,
1608
+ {
1609
+ gradientUnits: "objectBoundingBox",
1610
+ id: MOVIIE_SKIN_CHROME_SVG_GRADIENT_BOTTOM_EDGE_ID,
1611
+ x1: "0",
1612
+ x2: "0",
1613
+ y1: "1",
1614
+ y2: "0",
1615
+ children: [
1616
+ /* @__PURE__ */ jsx(
1617
+ Stop,
1618
+ {
1619
+ offset: "0",
1620
+ stopColor: MOVIIE_SKIN_CHROME_GRADIENT_BLACK_HEX,
1621
+ stopOpacity: MOVIIE_SKIN_CHROME_GRADIENT_EDGE_ALPHA
1622
+ }
1623
+ ),
1624
+ /* @__PURE__ */ jsx(
1625
+ Stop,
1626
+ {
1627
+ offset: fadeEndOffset,
1628
+ stopColor: MOVIIE_SKIN_CHROME_GRADIENT_BLACK_HEX,
1629
+ stopOpacity: 0
1630
+ }
1631
+ ),
1632
+ /* @__PURE__ */ jsx(Stop, { offset: "1", stopColor: MOVIIE_SKIN_CHROME_GRADIENT_BLACK_HEX, stopOpacity: 0 })
1633
+ ]
1634
+ }
1635
+ ) }),
1636
+ /* @__PURE__ */ jsx(Rect, { fill: bottomFillUrl, height: effBottomPx, width: svgWidthPx })
1637
+ ] })
1638
+ }
1639
+ ) : null
1640
+ ]
1641
+ }
1642
+ );
1643
+ }
1644
+
1645
+ // src/lib/clamp-unit-interval.ts
1646
+ function clampUnitInterval(value) {
1647
+ return Math.min(1, Math.max(0, value));
1648
+ }
1649
+
1650
+ // src/lib/compute-playback-progress-ratio.ts
1651
+ function computePlaybackProgressRatio(state) {
1652
+ if (!state.durationOk || !Number.isFinite(state.currentTime) || !Number.isFinite(state.duration) || state.duration <= 0) {
1653
+ return 0;
1654
+ }
1655
+ const raw = state.currentTime / state.duration;
1656
+ if (raw >= 1) {
1657
+ return 1;
1658
+ }
1659
+ const snapToEndWhilePaused = !state.playing && state.currentTime >= state.duration - MOVIIE_SKIN_TIMELINE_END_SNAP_EPSILON_SECONDS;
1660
+ const ratio = snapToEndWhilePaused ? 1 : raw;
1661
+ return clampUnitInterval(ratio);
1662
+ }
1663
+
1664
+ // src/lib/compute-timeline-scrub-commit-time.ts
1665
+ function computeTimelineScrubCommitTime(params) {
1666
+ const {
1667
+ ratio,
1668
+ durationSeconds,
1669
+ chapters,
1670
+ snapThresholdSeconds = MOVIIE_SKIN_TIMELINE_CHAPTER_SNAP_THRESHOLD_SECONDS
1671
+ } = params;
1672
+ const rawTime = ratio * durationSeconds;
1673
+ let nearest = null;
1674
+ let nearestDist = Infinity;
1675
+ for (const chapter of chapters) {
1676
+ const dist = Math.abs(chapter.startTimeSeconds - rawTime);
1677
+ if (dist < nearestDist) {
1678
+ nearestDist = dist;
1679
+ nearest = chapter.startTimeSeconds;
1680
+ }
1681
+ }
1682
+ return nearest !== null && nearestDist <= snapThresholdSeconds ? nearest : rawTime;
1683
+ }
1684
+
1685
+ // src/lib/resolve-skin-accent-color.ts
1686
+ var HEX_COLOR_REGEX = /^#[0-9A-Fa-f]{6}$/;
1687
+ function resolveSkinAccentColor(primaryColor) {
1688
+ if (primaryColor != null && HEX_COLOR_REGEX.test(primaryColor)) {
1689
+ return primaryColor;
1690
+ }
1691
+ return MOVIIE_SKIN_DEFAULT_ACCENT_HEX;
1692
+ }
1693
+ function createMoviieSkinBottomTimelineStyles(layout) {
1694
+ return StyleSheet.create({
1695
+ root: {
1696
+ bottom: 0,
1697
+ left: 0,
1698
+ overflow: "visible",
1699
+ pointerEvents: "box-none",
1700
+ position: "absolute",
1701
+ right: 0,
1702
+ zIndex: 24
1703
+ },
1704
+ touchExpansion: {
1705
+ overflow: "visible",
1706
+ paddingBottom: 0,
1707
+ paddingTop: layout.timelineTouchPaddingVerticalPx + MOVIIE_SKIN_TIMELINE_TOUCH_EXPANSION_VERTICAL_DP,
1708
+ position: "relative"
1709
+ },
1710
+ trackStack: {
1711
+ overflow: "visible",
1712
+ width: "100%"
1713
+ },
1714
+ thumbBase: {
1715
+ borderColor: MOVIIE_SKIN_TIMELINE_THUMB_BORDER_RGBA,
1716
+ borderWidth: 1,
1717
+ elevation: 8,
1718
+ position: "absolute",
1719
+ zIndex: 10
1720
+ },
1721
+ unifiedBarSlot: {
1722
+ overflow: "visible",
1723
+ position: "relative",
1724
+ width: "100%"
1725
+ },
1726
+ railLayer: {
1727
+ backgroundColor: MOVIIE_SKIN_TIMELINE_RAIL_BACKGROUND_RGBA,
1728
+ bottom: 0,
1729
+ height: layout.timelineBarHeightPx,
1730
+ left: 0,
1731
+ position: "absolute",
1732
+ right: 0,
1733
+ zIndex: 0
1734
+ },
1735
+ bufferLayer: {
1736
+ backgroundColor: MOVIIE_SKIN_TIMELINE_BUFFER_LAYER_RGBA,
1737
+ bottom: 0,
1738
+ height: layout.timelineBarHeightPx,
1739
+ left: 0,
1740
+ position: "absolute",
1741
+ zIndex: 1
1742
+ },
1743
+ progressLayer: {
1744
+ bottom: 0,
1745
+ height: layout.timelineBarHeightPx,
1746
+ left: 0,
1747
+ position: "absolute",
1748
+ zIndex: 2
1749
+ }
1750
+ });
1751
+ }
1752
+ function MoviieSkinBottomTimeline() {
1753
+ const {
1754
+ playback,
1755
+ chromeVisible,
1756
+ layout,
1757
+ player,
1758
+ setTimelineScrubClockSeconds,
1759
+ ui
1760
+ } = useMoviieSkinChrome();
1761
+ const styles6 = useMemo(
1762
+ () => createMoviieSkinBottomTimelineStyles(layout),
1763
+ [layout]
1764
+ );
1765
+ const { controls } = playback;
1766
+ const accent = resolveSkinAccentColor(playback.branding.primaryColor);
1767
+ const foreground = MOVIIE_SKIN_CHROME_FOREGROUND_HEX;
1768
+ const durationOk = controls.showProgress && Number.isFinite(ui.duration) && ui.duration > 0 && !player.isLive;
1769
+ const progressRatioLive = computePlaybackProgressRatio({
1770
+ currentTime: ui.currentTime,
1771
+ duration: ui.duration,
1772
+ durationOk,
1773
+ playing: ui.playing
1774
+ });
1775
+ const bufferRatioLive = durationOk && Number.isFinite(ui.bufferedPosition) && ui.bufferedPosition >= 0 ? clampUnitInterval(ui.bufferedPosition / ui.duration) : 0;
1776
+ const isDraggingSV = useSharedValue(false);
1777
+ const awaitingUnlockSV = useSharedValue(false);
1778
+ const scrubRatioSV = useSharedValue(0);
1779
+ const progressRatioSV = useSharedValue(progressRatioLive);
1780
+ const bufferRatioSV = useSharedValue(bufferRatioLive);
1781
+ const trackWidthSV = useSharedValue(0);
1782
+ const durationSV = useSharedValue(ui.duration);
1783
+ const chromeVisibleSV = useSharedValue(chromeVisible);
1784
+ const timelineAccentEngagedSV = useSharedValue(false);
1785
+ const thumbBaseDiamSV = useSharedValue(layout.timelineThumbDiameterPx);
1786
+ const barHeightSV = useSharedValue(layout.timelineBarHeightPx);
1787
+ const isDraggingRef = useRef(false);
1788
+ const shouldResumeRef = useRef(false);
1789
+ const progressRatioLiveRef = useRef(progressRatioLive);
1790
+ progressRatioLiveRef.current = progressRatioLive;
1791
+ const chaptersRef = useRef(playback.chapters);
1792
+ chaptersRef.current = playback.chapters;
1793
+ const chromeVisibleRef = useRef(chromeVisible);
1794
+ chromeVisibleRef.current = chromeVisible;
1795
+ useEffect(() => {
1796
+ if (!isDraggingRef.current) {
1797
+ progressRatioSV.value = progressRatioLive;
1798
+ }
1799
+ }, [progressRatioLive, progressRatioSV]);
1800
+ useEffect(() => {
1801
+ bufferRatioSV.value = bufferRatioLive;
1802
+ }, [bufferRatioLive, bufferRatioSV]);
1803
+ useEffect(() => {
1804
+ durationSV.value = ui.duration;
1805
+ }, [ui.duration, durationSV]);
1806
+ useEffect(() => {
1807
+ thumbBaseDiamSV.value = layout.timelineThumbDiameterPx;
1808
+ barHeightSV.value = layout.timelineBarHeightPx;
1809
+ }, [
1810
+ layout.timelineThumbDiameterPx,
1811
+ layout.timelineBarHeightPx,
1812
+ thumbBaseDiamSV,
1813
+ barHeightSV
1814
+ ]);
1815
+ useEffect(() => {
1816
+ chromeVisibleSV.value = chromeVisible;
1817
+ if (chromeVisible) {
1818
+ timelineAccentEngagedSV.value = false;
1819
+ awaitingUnlockSV.value = false;
1820
+ }
1821
+ }, [
1822
+ chromeVisible,
1823
+ chromeVisibleSV,
1824
+ timelineAccentEngagedSV,
1825
+ awaitingUnlockSV
1826
+ ]);
1827
+ useEffect(() => {
1828
+ const sub = player.addListener("statusChange", ({ status }) => {
1829
+ if (shouldResumeRef.current && status === "readyToPlay" && !isDraggingRef.current) {
1830
+ player.play();
1831
+ }
1832
+ });
1833
+ return () => sub.remove();
1834
+ }, [player]);
1835
+ useEffect(() => {
1836
+ const sub = player.addListener("playingChange", ({ isPlaying }) => {
1837
+ if (isPlaying && !isDraggingRef.current) {
1838
+ shouldResumeRef.current = false;
1839
+ }
1840
+ });
1841
+ return () => sub.remove();
1842
+ }, [player]);
1843
+ const effectiveRatioSV = useDerivedValue(
1844
+ () => isDraggingSV.value ? scrubRatioSV.value : progressRatioSV.value
1845
+ );
1846
+ const showAccentSV = useDerivedValue(
1847
+ () => chromeVisibleSV.value || isDraggingSV.value || timelineAccentEngagedSV.value
1848
+ );
1849
+ const showThumbSV = useDerivedValue(
1850
+ () => chromeVisibleSV.value || isDraggingSV.value || timelineAccentEngagedSV.value
1851
+ );
1852
+ const thumbDiamSV = useDerivedValue(
1853
+ () => isDraggingSV.value ? Math.round(
1854
+ thumbBaseDiamSV.value * MOVIIE_SKIN_TIMELINE_THUMB_DRAG_DIAMETER_MULTIPLIER
1855
+ ) : thumbBaseDiamSV.value
1856
+ );
1857
+ const progressBarStyle = useAnimatedStyle(() => ({
1858
+ backgroundColor: showAccentSV.value ? accent : foreground,
1859
+ width: `${effectiveRatioSV.value * 100}%`
1860
+ }));
1861
+ const bufferBarStyle = useAnimatedStyle(() => ({
1862
+ width: `${bufferRatioSV.value * 100}%`
1863
+ }));
1864
+ const thumbStyle = useAnimatedStyle(() => {
1865
+ if (!showThumbSV.value) {
1866
+ return { opacity: 0 };
1867
+ }
1868
+ const diameter = thumbDiamSV.value;
1869
+ const radius = diameter / 2;
1870
+ const w = trackWidthSV.value;
1871
+ const left = w > 0 ? Math.min(w - diameter, Math.max(0, effectiveRatioSV.value * w - radius)) : 0;
1872
+ const bh = barHeightSV.value;
1873
+ return {
1874
+ opacity: 1,
1875
+ backgroundColor: showAccentSV.value ? accent : foreground,
1876
+ left,
1877
+ width: diameter,
1878
+ height: diameter,
1879
+ borderRadius: radius,
1880
+ bottom: bh / 2 - radius
1881
+ };
1882
+ });
1883
+ const onScrubBegin = useCallback(() => {
1884
+ isDraggingRef.current = true;
1885
+ if (!shouldResumeRef.current) {
1886
+ shouldResumeRef.current = player.playing;
1887
+ }
1888
+ }, [player]);
1889
+ const onScrubUpdate = useCallback(
1890
+ (ratio) => {
1891
+ setTimelineScrubClockSeconds(ratio * durationSV.value);
1892
+ },
1893
+ [setTimelineScrubClockSeconds, durationSV]
1894
+ );
1895
+ const onScrubCommit = useCallback(
1896
+ (ratio) => {
1897
+ isDraggingRef.current = false;
1898
+ setTimelineScrubClockSeconds(null);
1899
+ const dur = durationSV.value;
1900
+ if (!Number.isFinite(dur) || dur <= 0) return;
1901
+ const targetTime = computeTimelineScrubCommitTime({
1902
+ ratio,
1903
+ durationSeconds: dur,
1904
+ chapters: chaptersRef.current
1905
+ });
1906
+ player.currentTime = targetTime;
1907
+ if (shouldResumeRef.current) {
1908
+ player.play();
1909
+ }
1910
+ },
1911
+ [setTimelineScrubClockSeconds, player, durationSV]
1912
+ );
1913
+ const onScrubCancel = useCallback(() => {
1914
+ isDraggingRef.current = false;
1915
+ setTimelineScrubClockSeconds(null);
1916
+ progressRatioSV.value = progressRatioLiveRef.current;
1917
+ }, [setTimelineScrubClockSeconds, progressRatioSV]);
1918
+ const onAccentEngage = useCallback(() => {
1919
+ timelineAccentEngagedSV.value = true;
1920
+ }, [timelineAccentEngagedSV]);
1921
+ const onAccentDisengageCancel = useCallback(() => {
1922
+ timelineAccentEngagedSV.value = false;
1923
+ }, [timelineAccentEngagedSV]);
1924
+ const panGesture = useMemo(
1925
+ () => Gesture.Pan().activeOffsetX([-8, 8]).failOffsetY([-15, 15]).enabled(durationOk).onBegin(() => {
1926
+ "worklet";
1927
+ if (!chromeVisibleSV.value && !timelineAccentEngagedSV.value) {
1928
+ awaitingUnlockSV.value = true;
1929
+ runOnJS(onAccentEngage)();
1930
+ }
1931
+ }).onStart((e) => {
1932
+ "worklet";
1933
+ if (awaitingUnlockSV.value) {
1934
+ return;
1935
+ }
1936
+ isDraggingSV.value = true;
1937
+ runOnJS(onScrubBegin)();
1938
+ const w = trackWidthSV.value;
1939
+ if (w <= 0) return;
1940
+ const ratio = Math.max(0, Math.min(1, e.x / w));
1941
+ scrubRatioSV.value = ratio;
1942
+ runOnJS(onScrubUpdate)(ratio);
1943
+ }).onUpdate((e) => {
1944
+ "worklet";
1945
+ const w = trackWidthSV.value;
1946
+ if (w <= 0) return;
1947
+ const ratio = Math.max(0, Math.min(1, e.x / w));
1948
+ if (awaitingUnlockSV.value) {
1949
+ awaitingUnlockSV.value = false;
1950
+ isDraggingSV.value = true;
1951
+ runOnJS(onScrubBegin)();
1952
+ }
1953
+ if (!isDraggingSV.value) return;
1954
+ scrubRatioSV.value = ratio;
1955
+ runOnJS(onScrubUpdate)(ratio);
1956
+ }).onEnd(() => {
1957
+ "worklet";
1958
+ if (awaitingUnlockSV.value) {
1959
+ awaitingUnlockSV.value = false;
1960
+ runOnJS(onAccentDisengageCancel)();
1961
+ return;
1962
+ }
1963
+ if (!isDraggingSV.value) return;
1964
+ const committed = scrubRatioSV.value;
1965
+ progressRatioSV.value = committed;
1966
+ isDraggingSV.value = false;
1967
+ scrubRatioSV.value = 0;
1968
+ runOnJS(onScrubCommit)(committed);
1969
+ }).onFinalize(() => {
1970
+ "worklet";
1971
+ if (awaitingUnlockSV.value) {
1972
+ awaitingUnlockSV.value = false;
1973
+ runOnJS(onAccentDisengageCancel)();
1974
+ return;
1975
+ }
1976
+ if (isDraggingSV.value) {
1977
+ isDraggingSV.value = false;
1978
+ scrubRatioSV.value = 0;
1979
+ runOnJS(onScrubCancel)();
1980
+ }
1981
+ }),
1982
+ [
1983
+ durationOk,
1984
+ isDraggingSV,
1985
+ awaitingUnlockSV,
1986
+ scrubRatioSV,
1987
+ progressRatioSV,
1988
+ trackWidthSV,
1989
+ chromeVisibleSV,
1990
+ timelineAccentEngagedSV,
1991
+ onAccentEngage,
1992
+ onAccentDisengageCancel,
1993
+ onScrubBegin,
1994
+ onScrubUpdate,
1995
+ onScrubCommit,
1996
+ onScrubCancel
1997
+ ]
1998
+ );
1999
+ const onTrackLayout = useCallback(
2000
+ (event) => {
2001
+ trackWidthSV.value = event.nativeEvent.layout.width;
2002
+ },
2003
+ [trackWidthSV]
2004
+ );
2005
+ if (!durationOk) {
2006
+ return null;
2007
+ }
2008
+ const unifiedSlotHeightPx = Math.max(
2009
+ layout.timelineThumbDiameterPx,
2010
+ Math.round(
2011
+ layout.timelineThumbDiameterPx * MOVIIE_SKIN_TIMELINE_THUMB_DRAG_DIAMETER_MULTIPLIER
2012
+ )
2013
+ );
2014
+ const scrubHitSlop = {
2015
+ top: MOVIIE_SKIN_TIMELINE_SCRUB_HIT_SLOP_TOP_DP,
2016
+ bottom: MOVIIE_SKIN_TIMELINE_SCRUB_HIT_SLOP_BOTTOM_DP,
2017
+ left: MOVIIE_SKIN_TIMELINE_SCRUB_HIT_SLOP_HORIZONTAL_DP,
2018
+ right: MOVIIE_SKIN_TIMELINE_SCRUB_HIT_SLOP_HORIZONTAL_DP
2019
+ };
2020
+ return /* @__PURE__ */ jsx(View, { pointerEvents: "box-none", style: styles6.root, children: /* @__PURE__ */ jsx(GestureDetector, { gesture: panGesture, children: /* @__PURE__ */ jsx(View, { hitSlop: scrubHitSlop, pointerEvents: "auto", style: styles6.touchExpansion, children: /* @__PURE__ */ jsx(View, { style: styles6.trackStack, children: /* @__PURE__ */ jsxs(
2021
+ View,
2022
+ {
2023
+ accessibilityRole: "progressbar",
2024
+ accessibilityValue: {
2025
+ max: 1,
2026
+ min: 0,
2027
+ now: progressRatioLive
2028
+ },
2029
+ collapsable: false,
2030
+ style: [styles6.unifiedBarSlot, { height: unifiedSlotHeightPx }],
2031
+ onLayout: onTrackLayout,
2032
+ children: [
2033
+ /* @__PURE__ */ jsx(View, { style: styles6.railLayer }),
2034
+ /* @__PURE__ */ jsx(ReanimatedView, { style: [styles6.bufferLayer, bufferBarStyle] }),
2035
+ /* @__PURE__ */ jsx(ReanimatedView, { style: [styles6.progressLayer, progressBarStyle] }),
2036
+ /* @__PURE__ */ jsx(ReanimatedView, { style: [styles6.thumbBase, thumbStyle] })
2037
+ ]
2038
+ }
2039
+ ) }) }) }) });
2040
+ }
2041
+
2042
+ // src/lib/map-smart-progress-display-ratio.ts
2043
+ function easeOutQuad(t) {
2044
+ return t * (2 - t);
2045
+ }
2046
+ function easeInQuad(t) {
2047
+ return t * t;
2048
+ }
2049
+ function mapSmartProgressRawRatio(realUnitInterval) {
2050
+ const r = Math.min(1, Math.max(0, realUnitInterval));
2051
+ const {
2052
+ R1,
2053
+ P1,
2054
+ R2,
2055
+ P2,
2056
+ P_MAX_BEFORE_END: pMax
2057
+ } = MOVIIE_SMART_PROGRESS_CONFIG;
2058
+ if (r <= 0) {
2059
+ return 0;
2060
+ }
2061
+ if (r >= 1) {
2062
+ return pMax;
2063
+ }
2064
+ if (r <= R1) {
2065
+ return P1 * easeOutQuad(r / R1);
2066
+ }
2067
+ if (r <= R2) {
2068
+ const t2 = (r - R1) / (R2 - R1);
2069
+ return P1 + (P2 - P1) * t2;
2070
+ }
2071
+ const t = (r - R2) / (1 - R2);
2072
+ return P2 + (pMax - P2) * easeInQuad(t);
2073
+ }
2074
+ var styles = StyleSheet.create({
2075
+ container: {
2076
+ bottom: 0,
2077
+ height: MOVIIE_SMART_PROGRESS_BAR_HEIGHT_DP,
2078
+ left: 0,
2079
+ overflow: "hidden",
2080
+ position: "absolute",
2081
+ right: 0,
2082
+ zIndex: MOVIIE_SMART_PROGRESS_Z_INDEX
2083
+ },
2084
+ fill: {
2085
+ bottom: 0,
2086
+ height: "100%",
2087
+ left: 0,
2088
+ position: "absolute",
2089
+ top: 0
2090
+ }
2091
+ });
2092
+ function MoviieSkinSmartProgress() {
2093
+ const { playback, player, timelineScrubClockSeconds, ui } = useMoviieSkinChrome();
2094
+ const accent = resolveSkinAccentColor(playback.branding.primaryColor);
2095
+ const durationOk = playback.smartProgressEnabled && Number.isFinite(ui.duration) && ui.duration > 0 && !player.isLive;
2096
+ const [barVisible, setBarVisible] = useState(() => !player.muted);
2097
+ const hasUserInteractedRef = useRef(!player.muted);
2098
+ const lastDisplayedRef = useRef(0);
2099
+ const progressAnim = useRef(new Animated.Value(0)).current;
2100
+ const syncMutedVisibility = useCallback(() => {
2101
+ const muted = player.muted;
2102
+ if (!muted) {
2103
+ hasUserInteractedRef.current = true;
2104
+ setBarVisible(true);
2105
+ return;
2106
+ }
2107
+ if (!hasUserInteractedRef.current) {
2108
+ setBarVisible(false);
2109
+ }
2110
+ }, [player]);
2111
+ useEffect(() => {
2112
+ syncMutedVisibility();
2113
+ const subMuted = player.addListener("mutedChange", syncMutedVisibility);
2114
+ const subVolume = player.addListener("volumeChange", syncMutedVisibility);
2115
+ return () => {
2116
+ subMuted.remove();
2117
+ subVolume.remove();
2118
+ };
2119
+ }, [player, syncMutedVisibility]);
2120
+ useEffect(() => {
2121
+ lastDisplayedRef.current = 0;
2122
+ progressAnim.setValue(0);
2123
+ }, [playback.embedId, progressAnim]);
2124
+ useEffect(() => {
2125
+ if (!durationOk || !barVisible) {
2126
+ return;
2127
+ }
2128
+ const scrubbing = timelineScrubClockSeconds != null;
2129
+ const clockSeconds = scrubbing ? timelineScrubClockSeconds : ui.currentTime;
2130
+ const realRatio = computePlaybackProgressRatio({
2131
+ currentTime: clockSeconds,
2132
+ duration: ui.duration,
2133
+ durationOk: true,
2134
+ playing: ui.playing
2135
+ });
2136
+ const mapped = mapSmartProgressRawRatio(realRatio);
2137
+ const next = scrubbing ? mapped : Math.max(mapped, lastDisplayedRef.current);
2138
+ lastDisplayedRef.current = next;
2139
+ Animated.timing(progressAnim, {
2140
+ duration: MOVIIE_SMART_PROGRESS_FILL_TRANSITION_MS,
2141
+ toValue: next,
2142
+ useNativeDriver: false
2143
+ }).start();
2144
+ }, [
2145
+ barVisible,
2146
+ durationOk,
2147
+ progressAnim,
2148
+ timelineScrubClockSeconds,
2149
+ ui.currentTime,
2150
+ ui.duration,
2151
+ ui.playing
2152
+ ]);
2153
+ useEffect(() => {
2154
+ if (!durationOk || !barVisible) {
2155
+ return;
2156
+ }
2157
+ const sub = player.addListener("playToEnd", () => {
2158
+ lastDisplayedRef.current = 1;
2159
+ Animated.timing(progressAnim, {
2160
+ duration: MOVIIE_SMART_PROGRESS_FILL_TRANSITION_MS,
2161
+ toValue: 1,
2162
+ useNativeDriver: false
2163
+ }).start();
2164
+ });
2165
+ return () => sub.remove();
2166
+ }, [barVisible, durationOk, player, progressAnim]);
2167
+ if (!playback.smartProgressEnabled || !durationOk) {
2168
+ return null;
2169
+ }
2170
+ if (!barVisible) {
2171
+ return null;
2172
+ }
2173
+ const widthInterpolated = progressAnim.interpolate({
2174
+ extrapolate: "clamp",
2175
+ inputRange: [0, 1],
2176
+ outputRange: ["0%", "100%"]
2177
+ });
2178
+ return /* @__PURE__ */ jsx(View, { pointerEvents: "none", style: styles.container, children: /* @__PURE__ */ jsx(
2179
+ Animated.View,
2180
+ {
2181
+ style: [
2182
+ styles.fill,
2183
+ {
2184
+ backgroundColor: accent,
2185
+ width: widthInterpolated
2186
+ }
2187
+ ]
2188
+ }
2189
+ ) });
2190
+ }
2191
+ function resolveChromeControlPressOpacityStyle(pressed) {
2192
+ return {
2193
+ opacity: pressed ? MOVIIE_SKIN_CHROME_CONTROL_PRESSED_OPACITY : MOVIIE_SKIN_CHROME_CONTROL_REST_OPACITY
2194
+ };
2195
+ }
2196
+ function createMoviieSkinOverlayStyles(layout) {
2197
+ return StyleSheet.create({
2198
+ chromeScrimBase: {
2199
+ backgroundColor: MOVIIE_SKIN_CHROME_SCRIM_BLACK_RGBA
2200
+ },
2201
+ floatingHud: {
2202
+ bottom: 0,
2203
+ left: 0,
2204
+ pointerEvents: "box-none",
2205
+ position: "absolute",
2206
+ right: 0
2207
+ },
2208
+ floatingHudInner: {
2209
+ alignItems: "center",
2210
+ flexDirection: "row",
2211
+ justifyContent: "space-between",
2212
+ paddingBottom: layout.floatingHudAboveTimelineGapPx,
2213
+ paddingHorizontal: layout.hudEdgeInsetPx
2214
+ },
2215
+ floatingLeftSpacer: {
2216
+ flex: 1
2217
+ },
2218
+ floatingRight: {
2219
+ alignItems: "center",
2220
+ columnGap: layout.floatingHudButtonGapPx,
2221
+ flexDirection: "row",
2222
+ flexShrink: 0
2223
+ },
2224
+ clockScrim: {
2225
+ borderRadius: layout.hudClockCornerRadiusPx,
2226
+ overflow: "hidden",
2227
+ paddingHorizontal: layout.hudEdgeInsetPx,
2228
+ paddingVertical: layout.hudEdgeInsetPx
2229
+ },
2230
+ clockText: {
2231
+ color: MOVIIE_SKIN_CHROME_FOREGROUND_HEX,
2232
+ fontSize: layout.clockTextFontSizePx,
2233
+ fontVariant: ["tabular-nums"]
2234
+ },
2235
+ centerCluster: {
2236
+ ...StyleSheet.absoluteFillObject,
2237
+ alignItems: "center",
2238
+ justifyContent: "center",
2239
+ pointerEvents: "box-none"
2240
+ },
2241
+ centerClusterRow: {
2242
+ alignItems: "center",
2243
+ columnGap: layout.centerClusterColumnGapPx,
2244
+ flexDirection: "row",
2245
+ justifyContent: "center"
2246
+ },
2247
+ seekCircularScrim: {
2248
+ alignItems: "center",
2249
+ borderRadius: layout.seekScrimOuterDiameterPx / 2,
2250
+ height: layout.seekScrimOuterDiameterPx,
2251
+ justifyContent: "center",
2252
+ overflow: "hidden",
2253
+ width: layout.seekScrimOuterDiameterPx
2254
+ },
2255
+ playScrim: {
2256
+ alignItems: "center",
2257
+ borderRadius: layout.playScrimOuterDiameterPx / 2,
2258
+ height: layout.playScrimOuterDiameterPx,
2259
+ justifyContent: "center",
2260
+ overflow: "hidden",
2261
+ width: layout.playScrimOuterDiameterPx
2262
+ },
2263
+ playPlaceholder: {
2264
+ height: layout.playScrimOuterDiameterPx,
2265
+ width: layout.playScrimOuterDiameterPx
2266
+ },
2267
+ fullscreenHudCircleScrim: {
2268
+ alignItems: "center",
2269
+ borderRadius: layout.floatingHudFullscreenScrimDiameterPx / 2,
2270
+ height: layout.floatingHudFullscreenScrimDiameterPx,
2271
+ justifyContent: "center",
2272
+ overflow: "hidden",
2273
+ width: layout.floatingHudFullscreenScrimDiameterPx
2274
+ },
2275
+ topFullscreenCorner: {
2276
+ alignItems: "center",
2277
+ columnGap: layout.floatingHudButtonGapPx,
2278
+ flexDirection: "row",
2279
+ pointerEvents: "box-none",
2280
+ position: "absolute",
2281
+ right: layout.hudEdgeInsetPx,
2282
+ top: layout.hudEdgeInsetPx,
2283
+ zIndex: 8
2284
+ }
2285
+ });
2286
+ }
2287
+ function ChromeScrimPad(props) {
2288
+ const padStyle = [props.chromeScrimBaseStyle, props.style];
2289
+ if (props.onPress != null) {
2290
+ return /* @__PURE__ */ jsx(
2291
+ Pressable,
2292
+ {
2293
+ accessibilityLabel: props.accessibilityLabel,
2294
+ accessibilityRole: props.accessibilityRole ?? "button",
2295
+ hitSlop: props.hitSlop,
2296
+ onPress: props.onPress,
2297
+ style: (state) => [
2298
+ props.chromeScrimBaseStyle,
2299
+ props.style,
2300
+ resolveChromeControlPressOpacityStyle(state.pressed)
2301
+ ],
2302
+ children: props.children
2303
+ }
2304
+ );
2305
+ }
2306
+ return /* @__PURE__ */ jsx(View, { style: padStyle, children: props.children });
2307
+ }
2308
+ function MoviieSkinPictureInPictureCircleButton(props) {
2309
+ return /* @__PURE__ */ jsx(
2310
+ ChromeScrimPad,
2311
+ {
2312
+ accessibilityLabel: props.pictureInPictureActive ? "Fechar imagem no imagem" : "Imagem no imagem",
2313
+ chromeScrimBaseStyle: props.styles.chromeScrimBase,
2314
+ hitSlop: MOVIIE_SKIN_FLOATING_HUD_TRAILING_HIT_SLOP_PX,
2315
+ onPress: props.onPress,
2316
+ style: props.styles.fullscreenHudCircleScrim,
2317
+ children: /* @__PURE__ */ jsx(
2318
+ EmbedMediaIconPictureInPicture,
2319
+ {
2320
+ color: MOVIIE_SKIN_CHROME_FOREGROUND_HEX,
2321
+ size: props.layout.floatingHudFullscreenIconPx
2322
+ }
2323
+ )
2324
+ }
2325
+ );
2326
+ }
2327
+ function MoviieSkinPictureInPictureTopBareButton(props) {
2328
+ return /* @__PURE__ */ jsx(
2329
+ Pressable,
2330
+ {
2331
+ accessibilityLabel: props.pictureInPictureActive ? "Fechar imagem no imagem" : "Imagem no imagem",
2332
+ accessibilityRole: "button",
2333
+ hitSlop: MOVIIE_SKIN_FLOATING_HUD_TRAILING_HIT_SLOP_PX,
2334
+ onPress: props.onPress,
2335
+ style: (state) => resolveChromeControlPressOpacityStyle(state.pressed),
2336
+ children: /* @__PURE__ */ jsx(
2337
+ EmbedMediaIconPictureInPicture,
2338
+ {
2339
+ color: MOVIIE_SKIN_CHROME_FOREGROUND_HEX,
2340
+ size: props.layout.floatingHudFullscreenTopIconPx * MOVIIE_SKIN_TOP_ICON_PIP_VISUAL_SCALE
2341
+ }
2342
+ )
2343
+ }
2344
+ );
2345
+ }
2346
+ function MoviieSkinFullscreenCircleButton(props) {
2347
+ return /* @__PURE__ */ jsx(
2348
+ ChromeScrimPad,
2349
+ {
2350
+ accessibilityLabel: props.isFullscreen ? "Sair de ecr\xE3 inteiro" : "Ecr\xE3 inteiro",
2351
+ chromeScrimBaseStyle: props.styles.chromeScrimBase,
2352
+ hitSlop: MOVIIE_SKIN_FLOATING_HUD_TRAILING_HIT_SLOP_PX,
2353
+ onPress: props.onPress,
2354
+ style: props.styles.fullscreenHudCircleScrim,
2355
+ children: props.isFullscreen ? /* @__PURE__ */ jsx(
2356
+ EmbedMediaIconFullscreenExit,
2357
+ {
2358
+ color: MOVIIE_SKIN_CHROME_FOREGROUND_HEX,
2359
+ size: props.layout.floatingHudFullscreenIconPx
2360
+ }
2361
+ ) : /* @__PURE__ */ jsx(
2362
+ EmbedMediaIconFullscreen,
2363
+ {
2364
+ color: MOVIIE_SKIN_CHROME_FOREGROUND_HEX,
2365
+ size: props.layout.floatingHudFullscreenIconPx
2366
+ }
2367
+ )
2368
+ }
2369
+ );
2370
+ }
2371
+ function MoviieSkinFullscreenTopBareButton(props) {
2372
+ const size = props.layout.floatingHudFullscreenTopIconPx * MOVIIE_SKIN_TOP_ICON_FULLSCREEN_VISUAL_SCALE;
2373
+ return /* @__PURE__ */ jsx(
2374
+ Pressable,
2375
+ {
2376
+ accessibilityLabel: props.isFullscreen ? "Sair de ecr\xE3 inteiro" : "Ecr\xE3 inteiro",
2377
+ accessibilityRole: "button",
2378
+ hitSlop: MOVIIE_SKIN_FLOATING_HUD_TRAILING_HIT_SLOP_PX,
2379
+ onPress: props.onPress,
2380
+ style: (state) => resolveChromeControlPressOpacityStyle(state.pressed),
2381
+ children: props.isFullscreen ? /* @__PURE__ */ jsx(EmbedMediaIconFullscreenExit, { color: MOVIIE_SKIN_CHROME_FOREGROUND_HEX, size }) : /* @__PURE__ */ jsx(EmbedMediaIconFullscreen, { color: MOVIIE_SKIN_CHROME_FOREGROUND_HEX, size })
2382
+ }
2383
+ );
2384
+ }
2385
+ function MoviieSkinBufferingIndicator() {
2386
+ const { isBuffering, layout } = useMoviieSkinChrome();
2387
+ const bufferingHudStyle = useMemo(
2388
+ () => ({
2389
+ alignItems: "center",
2390
+ height: layout.bufferingIndicatorSlotPx,
2391
+ justifyContent: "center",
2392
+ left: layout.hudEdgeInsetPx,
2393
+ position: "absolute",
2394
+ top: layout.hudEdgeInsetPx,
2395
+ width: layout.bufferingIndicatorSlotPx,
2396
+ zIndex: 16
2397
+ }),
2398
+ [layout]
2399
+ );
2400
+ if (!isBuffering) {
2401
+ return null;
2402
+ }
2403
+ return /* @__PURE__ */ jsx(View, { pointerEvents: "none", style: bufferingHudStyle, children: /* @__PURE__ */ jsx(
2404
+ ActivityIndicator,
2405
+ {
2406
+ accessibilityLabel: "A carregar v\xEDdeo",
2407
+ color: MOVIIE_SKIN_CHROME_FOREGROUND_HEX,
2408
+ size: "small"
2409
+ }
2410
+ ) });
2411
+ }
2412
+ function MoviieSkinVideoOverlay() {
2413
+ const {
2414
+ chromeOpacity,
2415
+ chromeVisible,
2416
+ bumpChromeInteraction,
2417
+ castConnected,
2418
+ castControlEnabled,
2419
+ dismissChrome,
2420
+ isCustomFullscreen,
2421
+ openCastPicker,
2422
+ pictureInPictureActive,
2423
+ pictureInPictureControlEnabled,
2424
+ toggleFullscreen,
2425
+ togglePictureInPicture,
2426
+ layout,
2427
+ playback,
2428
+ player,
2429
+ seekBack,
2430
+ seekForward,
2431
+ timelineScrubClockSeconds,
2432
+ togglePlay,
2433
+ ui
2434
+ } = useMoviieSkinChrome();
2435
+ const handlePictureInPicturePress = useCallback(() => {
2436
+ togglePictureInPicture();
2437
+ dismissChrome();
2438
+ }, [togglePictureInPicture, dismissChrome]);
2439
+ const handleCastPress = useCallback(() => {
2440
+ dismissChrome();
2441
+ setTimeout(() => openCastPicker(), 150);
2442
+ }, [openCastPicker, dismissChrome]);
2443
+ const styles6 = useMemo(() => createMoviieSkinOverlayStyles(layout), [layout]);
2444
+ const { controls } = playback;
2445
+ const fg = MOVIIE_SKIN_CHROME_FOREGROUND_HEX;
2446
+ const clockDisplaySeconds = timelineScrubClockSeconds ?? ui.currentTime;
2447
+ const playbackEndedForUi = useMemo(
2448
+ () => computeMoviiePlaybackEnded({
2449
+ playing: ui.playing,
2450
+ currentTime: clockDisplaySeconds,
2451
+ duration: ui.duration,
2452
+ isLive: player.isLive,
2453
+ loop: player.loop
2454
+ }),
2455
+ [ui.playing, clockDisplaySeconds, ui.duration, player.isLive, player.loop]
2456
+ );
2457
+ const { centerSeekBackwardEnabled, centerSeekForwardEnabled } = useMemo(() => {
2458
+ const alignSeekButtonsWithPlay = controls.showPlay && !player.isLive;
2459
+ const hideSeekClusterAtEnd = playbackEndedForUi;
2460
+ return {
2461
+ centerSeekBackwardEnabled: !hideSeekClusterAtEnd && (controls.seekBackwardEnabled || alignSeekButtonsWithPlay),
2462
+ centerSeekForwardEnabled: !hideSeekClusterAtEnd && (controls.seekForwardEnabled || alignSeekButtonsWithPlay)
2463
+ };
2464
+ }, [
2465
+ controls.seekBackwardEnabled,
2466
+ controls.seekForwardEnabled,
2467
+ controls.showPlay,
2468
+ player.isLive,
2469
+ playbackEndedForUi
2470
+ ]);
2471
+ const centerPlayShowsReplay = playbackEndedForUi;
2472
+ const centerPlayAccessibilityLabel = ui.playing ? "Pausar" : centerPlayShowsReplay ? "Come\xE7ar novamente" : "Reproduzir";
2473
+ const clockLabel = controls.showCurrentTime || controls.showDuration ? formatPlaybackClock(clockDisplaySeconds, ui.duration) : null;
2474
+ const showSeekCluster = centerSeekBackwardEnabled || centerSeekForwardEnabled || controls.showPlay;
2475
+ const seekSlot = layout.seekScrimOuterDiameterPx;
2476
+ const showPictureInPictureTrailing = pictureInPictureControlEnabled;
2477
+ const showCastTrailing = castControlEnabled;
2478
+ const hasFloatingHudTrailing = playback.branding.showWatermark || !playback.branding.showWatermark && (controls.showFullscreen || showPictureInPictureTrailing || showCastTrailing);
2479
+ const showFloatingHud = clockLabel != null || hasFloatingHudTrailing;
2480
+ const showFullscreenBottomTrailing = controls.showFullscreen && !playback.branding.showWatermark;
2481
+ const showTopCornerChrome = playback.branding.showWatermark && (controls.showFullscreen || showPictureInPictureTrailing || showCastTrailing);
2482
+ return /* @__PURE__ */ jsxs(View, { pointerEvents: "box-none", style: StyleSheet.absoluteFillObject, children: [
2483
+ /* @__PURE__ */ jsx(
2484
+ Pressable,
2485
+ {
2486
+ accessibilityLabel: chromeVisible ? "\xC1rea do v\xEDdeo \u2014 toque para esconder os controles" : "Mostrar controles do v\xEDdeo",
2487
+ accessibilityRole: "button",
2488
+ onPress: chromeVisible ? dismissChrome : bumpChromeInteraction,
2489
+ style: [StyleSheet.absoluteFillObject, { zIndex: 0 }]
2490
+ }
2491
+ ),
2492
+ /* @__PURE__ */ jsxs(
2493
+ AnimatedView,
2494
+ {
2495
+ pointerEvents: !chromeVisible ? "none" : "box-none",
2496
+ style: [
2497
+ StyleSheet.absoluteFillObject,
2498
+ {
2499
+ opacity: chromeOpacity,
2500
+ zIndex: 2
2501
+ }
2502
+ ],
2503
+ children: [
2504
+ /* @__PURE__ */ jsx(MoviieSkinChromeEdgeGradients, { layout, showProgress: controls.showProgress }),
2505
+ showTopCornerChrome ? /* @__PURE__ */ jsxs(View, { pointerEvents: "box-none", style: styles6.topFullscreenCorner, children: [
2506
+ showCastTrailing ? /* @__PURE__ */ jsx(
2507
+ MoviieSkinCastTopBareButton,
2508
+ {
2509
+ castConnected,
2510
+ layout,
2511
+ onPress: handleCastPress
2512
+ }
2513
+ ) : null,
2514
+ showPictureInPictureTrailing ? /* @__PURE__ */ jsx(
2515
+ MoviieSkinPictureInPictureTopBareButton,
2516
+ {
2517
+ layout,
2518
+ pictureInPictureActive,
2519
+ onPress: handlePictureInPicturePress
2520
+ }
2521
+ ) : null,
2522
+ controls.showFullscreen ? /* @__PURE__ */ jsx(
2523
+ MoviieSkinFullscreenTopBareButton,
2524
+ {
2525
+ isFullscreen: isCustomFullscreen,
2526
+ layout,
2527
+ onPress: toggleFullscreen
2528
+ }
2529
+ ) : null
2530
+ ] }) : null,
2531
+ showFloatingHud ? /* @__PURE__ */ jsx(
2532
+ View,
2533
+ {
2534
+ pointerEvents: "box-none",
2535
+ style: [
2536
+ styles6.floatingHud,
2537
+ controls.showProgress ? { bottom: layout.timelineStackHeightChromeVisiblePx } : null
2538
+ ],
2539
+ children: /* @__PURE__ */ jsxs(View, { style: styles6.floatingHudInner, children: [
2540
+ clockLabel ? /* @__PURE__ */ jsx(
2541
+ ChromeScrimPad,
2542
+ {
2543
+ chromeScrimBaseStyle: styles6.chromeScrimBase,
2544
+ style: styles6.clockScrim,
2545
+ children: /* @__PURE__ */ jsx(Text, { accessibilityLabel: `Tempo ${clockLabel}`, style: styles6.clockText, children: clockLabel })
2546
+ }
2547
+ ) : /* @__PURE__ */ jsx(View, { style: styles6.floatingLeftSpacer }),
2548
+ /* @__PURE__ */ jsx(View, { style: styles6.floatingRight, children: playback.branding.showWatermark ? /* @__PURE__ */ jsx(MoviieEmbedBrandMark, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [
2549
+ showCastTrailing ? /* @__PURE__ */ jsx(
2550
+ MoviieSkinCastCircleButton,
2551
+ {
2552
+ castConnected,
2553
+ chromeScrimBaseStyle: styles6.chromeScrimBase,
2554
+ circleScrimStyle: styles6.fullscreenHudCircleScrim,
2555
+ layout,
2556
+ onPress: handleCastPress
2557
+ }
2558
+ ) : null,
2559
+ showPictureInPictureTrailing ? /* @__PURE__ */ jsx(
2560
+ MoviieSkinPictureInPictureCircleButton,
2561
+ {
2562
+ layout,
2563
+ pictureInPictureActive,
2564
+ onPress: handlePictureInPicturePress,
2565
+ styles: styles6
2566
+ }
2567
+ ) : null,
2568
+ showFullscreenBottomTrailing ? /* @__PURE__ */ jsx(
2569
+ MoviieSkinFullscreenCircleButton,
2570
+ {
2571
+ isFullscreen: isCustomFullscreen,
2572
+ layout,
2573
+ onPress: toggleFullscreen,
2574
+ styles: styles6
2575
+ }
2576
+ ) : null
2577
+ ] }) })
2578
+ ] })
2579
+ }
2580
+ ) : null,
2581
+ showSeekCluster ? /* @__PURE__ */ jsx(View, { pointerEvents: "box-none", style: styles6.centerCluster, children: /* @__PURE__ */ jsxs(View, { style: styles6.centerClusterRow, children: [
2582
+ centerSeekBackwardEnabled ? /* @__PURE__ */ jsx(
2583
+ ChromeScrimPad,
2584
+ {
2585
+ accessibilityLabel: "Retroceder 10 segundos",
2586
+ chromeScrimBaseStyle: styles6.chromeScrimBase,
2587
+ onPress: seekBack,
2588
+ style: styles6.seekCircularScrim,
2589
+ children: /* @__PURE__ */ jsx(EmbedMediaIconSeekBackward10, { color: fg, size: layout.seekIconPx })
2590
+ }
2591
+ ) : /* @__PURE__ */ jsx(View, { style: { height: seekSlot, width: seekSlot } }),
2592
+ controls.showPlay ? /* @__PURE__ */ jsx(
2593
+ ChromeScrimPad,
2594
+ {
2595
+ accessibilityLabel: centerPlayAccessibilityLabel,
2596
+ chromeScrimBaseStyle: styles6.chromeScrimBase,
2597
+ onPress: togglePlay,
2598
+ style: styles6.playScrim,
2599
+ children: ui.playing ? /* @__PURE__ */ jsx(EmbedMediaIconPause, { color: fg, size: layout.centerPlayIconPx }) : centerPlayShowsReplay ? /* @__PURE__ */ jsx(EmbedMediaIconReplay, { color: fg, size: layout.centerPlayIconPx }) : /* @__PURE__ */ jsx(EmbedMediaIconPlay, { color: fg, size: layout.centerPlayIconPx })
2600
+ }
2601
+ ) : /* @__PURE__ */ jsx(View, { style: styles6.playPlaceholder }),
2602
+ centerSeekForwardEnabled ? /* @__PURE__ */ jsx(
2603
+ ChromeScrimPad,
2604
+ {
2605
+ accessibilityLabel: "Avan\xE7ar 10 segundos",
2606
+ chromeScrimBaseStyle: styles6.chromeScrimBase,
2607
+ onPress: seekForward,
2608
+ style: styles6.seekCircularScrim,
2609
+ children: /* @__PURE__ */ jsx(EmbedMediaIconSeekForward10, { color: fg, size: layout.seekIconPx })
2610
+ }
2611
+ ) : /* @__PURE__ */ jsx(View, { style: { height: seekSlot, width: seekSlot } })
2612
+ ] }) }) : null
2613
+ ]
2614
+ }
2615
+ ),
2616
+ /* @__PURE__ */ jsx(MoviieSkinBufferingIndicator, {}),
2617
+ /* @__PURE__ */ jsx(MoviieSkinSmartProgress, {}),
2618
+ /* @__PURE__ */ jsx(MoviieSkinBottomTimeline, {})
2619
+ ] });
2620
+ }
2621
+
2622
+ // src/lib/optional-status-bar.ts
2623
+ var cachedModule;
2624
+ function loadModule() {
2625
+ if (cachedModule !== void 0) {
2626
+ return cachedModule;
2627
+ }
2628
+ try {
2629
+ cachedModule = __require("expo-status-bar");
2630
+ } catch {
2631
+ cachedModule = null;
2632
+ warnOnce(
2633
+ "moviie:expo-status-bar-missing",
2634
+ "[@moviie/player-expo] `expo-status-bar` n\xE3o est\xE1 instalado. Status bar n\xE3o ser\xE1 ocultada em fullscreen JS. Instala com `pnpm add expo-status-bar` se quiser este comportamento."
2635
+ );
2636
+ }
2637
+ return cachedModule;
2638
+ }
2639
+ function moviieSetStatusBarHidden(hidden, animation = "fade") {
2640
+ const mod = loadModule();
2641
+ if (mod == null) return;
2642
+ try {
2643
+ mod.setStatusBarHidden(hidden, animation);
2644
+ } catch {
2645
+ }
2646
+ }
2647
+ async function restoreOrientationLockPolicy(previousLock) {
2648
+ try {
2649
+ if (previousLock === void 0 || previousLock === OrientationLock.DEFAULT || previousLock === OrientationLock.UNKNOWN) {
2650
+ await unlockAsync();
2651
+ return;
2652
+ }
2653
+ await lockAsync(previousLock);
2654
+ } catch {
2655
+ try {
2656
+ await unlockAsync();
2657
+ } catch {
2658
+ }
2659
+ }
2660
+ }
2661
+ function subscribeCustomFullscreenNativeOrientation(targetLock) {
2662
+ if (Platform.OS === "web") {
2663
+ return { finish: async () => {
2664
+ } };
2665
+ }
2666
+ let cancelled = false;
2667
+ let locked = false;
2668
+ let previousLock;
2669
+ const setup = (async () => {
2670
+ try {
2671
+ previousLock = await getOrientationLockAsync();
2672
+ if (cancelled) {
2673
+ return;
2674
+ }
2675
+ await lockAsync(targetLock);
2676
+ if (cancelled) {
2677
+ await restoreOrientationLockPolicy(previousLock);
2678
+ return;
2679
+ }
2680
+ locked = true;
2681
+ } catch {
2682
+ }
2683
+ })();
2684
+ let finishPromise = null;
2685
+ return {
2686
+ finish: () => {
2687
+ if (finishPromise != null) {
2688
+ return finishPromise;
2689
+ }
2690
+ cancelled = true;
2691
+ finishPromise = (async () => {
2692
+ await setup;
2693
+ if (locked) {
2694
+ locked = false;
2695
+ await restoreOrientationLockPolicy(previousLock);
2696
+ }
2697
+ })();
2698
+ return finishPromise;
2699
+ }
2700
+ };
2701
+ }
2702
+
2703
+ // src/lib/resolve-orientation-lock-for-rotate-z-deg.ts
2704
+ function resolveOrientationLockValueForRotateZDeg(rotateZDeg) {
2705
+ const normalized = (rotateZDeg % 360 + 360) % 360;
2706
+ if (normalized === 90) {
2707
+ return MOVIIE_EXPO_SCREEN_ORIENTATION_LOCK_VALUE.LANDSCAPE_RIGHT;
2708
+ }
2709
+ if (normalized === 270) {
2710
+ return MOVIIE_EXPO_SCREEN_ORIENTATION_LOCK_VALUE.LANDSCAPE_LEFT;
2711
+ }
2712
+ return MOVIIE_EXPO_SCREEN_ORIENTATION_LOCK_VALUE.LANDSCAPE;
2713
+ }
2714
+ var CUSTOM_FULLSCREEN_TIMING = {
2715
+ duration: MOVIIE_SKIN_CUSTOM_FULLSCREEN_TRANSITION_MS
2716
+ };
2717
+ var CUSTOM_FULLSCREEN_USE_JS_STAGE_ROTATION = Platform.OS === "web";
2718
+ var MoviieSkinCustomFullscreenModalInner = forwardRef(function MoviieSkinCustomFullscreenModalInner2(props, ref) {
2719
+ const orientationFinishRef = useRef(null);
2720
+ const insets = useSafeAreaInsets();
2721
+ const { height: winH, width: winW } = useWindowDimensions();
2722
+ const { longSide, shortSide } = useMemo(
2723
+ () => computeCustomFullscreenStageDimensions({
2724
+ windowHeight: winH,
2725
+ safeInsets: insets,
2726
+ stageOverscanPx: MOVIIE_SKIN_CUSTOM_FULLSCREEN_STAGE_OVERSCAN_PX,
2727
+ windowWidth: winW
2728
+ }),
2729
+ [winW, winH, insets.bottom, insets.left, insets.right, insets.top]
2730
+ );
2731
+ const rotateZDeg = useSharedValue(0);
2732
+ const scale = useSharedValue(1);
2733
+ const emitExitComplete = useCallback(() => {
2734
+ props.onExitComplete();
2735
+ }, [props.onExitComplete]);
2736
+ const completeExitAfterNativeOrientationRestore = useCallback(() => {
2737
+ void (async () => {
2738
+ await orientationFinishRef.current?.();
2739
+ emitExitComplete();
2740
+ })();
2741
+ }, [emitExitComplete]);
2742
+ const animateExit = useCallback(() => {
2743
+ scale.value = withTiming(
2744
+ MOVIIE_SKIN_CUSTOM_FULLSCREEN_ENTER_SCALE_START,
2745
+ CUSTOM_FULLSCREEN_TIMING,
2746
+ (finishedScale) => {
2747
+ if (!CUSTOM_FULLSCREEN_USE_JS_STAGE_ROTATION && finishedScale) {
2748
+ runOnJS(completeExitAfterNativeOrientationRestore)();
2749
+ }
2750
+ }
2751
+ );
2752
+ if (CUSTOM_FULLSCREEN_USE_JS_STAGE_ROTATION) {
2753
+ rotateZDeg.value = withTiming(0, CUSTOM_FULLSCREEN_TIMING, (finished) => {
2754
+ if (finished) {
2755
+ runOnJS(emitExitComplete)();
2756
+ }
2757
+ });
2758
+ return;
2759
+ }
2760
+ rotateZDeg.value = 0;
2761
+ }, [completeExitAfterNativeOrientationRestore, emitExitComplete, rotateZDeg, scale]);
2762
+ useImperativeHandle(ref, () => ({ animateExit }), [animateExit]);
2763
+ useEffect(() => {
2764
+ if (!props.visible) {
2765
+ return;
2766
+ }
2767
+ rotateZDeg.value = 0;
2768
+ scale.value = MOVIIE_SKIN_CUSTOM_FULLSCREEN_ENTER_SCALE_START;
2769
+ if (CUSTOM_FULLSCREEN_USE_JS_STAGE_ROTATION) {
2770
+ rotateZDeg.value = withTiming(
2771
+ MOVIIE_SKIN_CUSTOM_FULLSCREEN_ROTATE_Z_DEGREES,
2772
+ CUSTOM_FULLSCREEN_TIMING
2773
+ );
2774
+ }
2775
+ scale.value = withTiming(1, CUSTOM_FULLSCREEN_TIMING);
2776
+ }, [props.visible, rotateZDeg, scale]);
2777
+ useEffect(() => {
2778
+ if (!props.visible) {
2779
+ return;
2780
+ }
2781
+ moviieSetStatusBarHidden(true, "fade");
2782
+ return () => {
2783
+ moviieSetStatusBarHidden(false, "fade");
2784
+ };
2785
+ }, [props.visible]);
2786
+ useEffect(() => {
2787
+ if (!props.visible || CUSTOM_FULLSCREEN_USE_JS_STAGE_ROTATION) {
2788
+ orientationFinishRef.current = null;
2789
+ return;
2790
+ }
2791
+ const targetLock = resolveOrientationLockValueForRotateZDeg(
2792
+ MOVIIE_SKIN_CUSTOM_FULLSCREEN_ROTATE_Z_DEGREES
2793
+ );
2794
+ const sub = subscribeCustomFullscreenNativeOrientation(targetLock);
2795
+ orientationFinishRef.current = () => sub.finish();
2796
+ return () => {
2797
+ orientationFinishRef.current = null;
2798
+ void sub.finish();
2799
+ };
2800
+ }, [props.visible]);
2801
+ const animatedStyle = useAnimatedStyle(() => {
2802
+ if (CUSTOM_FULLSCREEN_USE_JS_STAGE_ROTATION) {
2803
+ return {
2804
+ transform: [{ rotateZ: `${rotateZDeg.value}deg` }, { scale: scale.value }]
2805
+ };
2806
+ }
2807
+ return {
2808
+ transform: [{ scale: scale.value }]
2809
+ };
2810
+ });
2811
+ const stageStaticStyle = useMemo(
2812
+ () => ({
2813
+ height: shortSide,
2814
+ width: longSide
2815
+ }),
2816
+ [longSide, shortSide]
2817
+ );
2818
+ return /* @__PURE__ */ jsx(
2819
+ View,
2820
+ {
2821
+ style: [styles2.root, { backgroundColor: MOVIIE_SKIN_CUSTOM_FULLSCREEN_STAGE_BACKGROUND_HEX }],
2822
+ children: /* @__PURE__ */ jsx(View, { style: styles2.centerSheet, children: /* @__PURE__ */ jsx(ReanimatedView, { style: [styles2.rotatingStage, stageStaticStyle, animatedStyle], children: props.children }) })
2823
+ }
2824
+ );
2825
+ });
2826
+ var MoviieSkinCustomFullscreenModal = forwardRef(function MoviieSkinCustomFullscreenModal2(props, ref) {
2827
+ return /* @__PURE__ */ jsx(
2828
+ Modal,
2829
+ {
2830
+ animationType: "none",
2831
+ hardwareAccelerated: Platform.OS === "android",
2832
+ onRequestClose: () => {
2833
+ if (ref != null && typeof ref !== "function") {
2834
+ ref.current?.animateExit();
2835
+ }
2836
+ },
2837
+ presentationStyle: "fullScreen",
2838
+ statusBarTranslucent: Platform.OS === "android",
2839
+ supportedOrientations: [
2840
+ "portrait",
2841
+ "portrait-upside-down",
2842
+ "landscape",
2843
+ "landscape-left",
2844
+ "landscape-right"
2845
+ ],
2846
+ visible: props.visible,
2847
+ children: /* @__PURE__ */ jsx(SafeAreaProvider, { children: /* @__PURE__ */ jsx(MoviieSkinCustomFullscreenModalInner, { ref, ...props }) })
2848
+ }
2849
+ );
2850
+ });
2851
+ var styles2 = StyleSheet.create({
2852
+ root: {
2853
+ flex: 1
2854
+ },
2855
+ centerSheet: {
2856
+ alignItems: "center",
2857
+ flex: 1,
2858
+ justifyContent: "center",
2859
+ overflow: "visible"
2860
+ },
2861
+ rotatingStage: {
2862
+ position: "relative"
2863
+ }
2864
+ });
2865
+ var ERROR_NUMERIC_CODE = {
2866
+ auth: "E1001",
2867
+ not_found: "E1002",
2868
+ bundle_blocked: "E1003",
2869
+ referrer_blocked: "E1004",
2870
+ subscription_inactive: "E1005",
2871
+ network: "E2001",
2872
+ rate_limit: "E2002",
2873
+ unknown: "E9999"
2874
+ };
2875
+ function resolveErrorCode(error) {
2876
+ if (error instanceof MoviieAuthError) return "auth";
2877
+ if (error instanceof MoviieNotFoundError) return "not_found";
2878
+ if (error instanceof MoviieBundleBlockedError) return "bundle_blocked";
2879
+ if (error instanceof MoviieReferrerBlockedError) return "referrer_blocked";
2880
+ if (error instanceof MoviieSubscriptionInactiveError) return "subscription_inactive";
2881
+ if (error instanceof MoviieNetworkError) return "network";
2882
+ if (error instanceof MoviieRateLimitError) return "rate_limit";
2883
+ const code = error.code;
2884
+ if (typeof code === "string") {
2885
+ const known = [
2886
+ "auth",
2887
+ "not_found",
2888
+ "bundle_blocked",
2889
+ "referrer_blocked",
2890
+ "subscription_inactive",
2891
+ "network",
2892
+ "rate_limit"
2893
+ ];
2894
+ if (known.includes(code)) {
2895
+ return code;
2896
+ }
2897
+ }
2898
+ return "unknown";
2899
+ }
2900
+ function getErrorDisplay(error) {
2901
+ const code = resolveErrorCode(error);
2902
+ switch (code) {
2903
+ case "auth":
2904
+ return {
2905
+ code,
2906
+ numericCode: ERROR_NUMERIC_CODE[code],
2907
+ heading: "Configura\xE7\xE3o inv\xE1lida",
2908
+ description: "Verifique a chave public\xE1vel do SDK.",
2909
+ ctaLabel: "Acessar Moviie",
2910
+ ctaKind: "external",
2911
+ externalUrl: MOVIIE_DASHBOARD_URL
2912
+ };
2913
+ case "not_found":
2914
+ return {
2915
+ code,
2916
+ numericCode: ERROR_NUMERIC_CODE[code],
2917
+ heading: "Reprodu\xE7\xE3o indispon\xEDvel",
2918
+ description: "N\xE3o conseguimos encontrar este v\xEDdeo.",
2919
+ ctaLabel: "Acessar Moviie",
2920
+ ctaKind: "external",
2921
+ externalUrl: MOVIIE_DASHBOARD_URL
2922
+ };
2923
+ case "bundle_blocked":
2924
+ return {
2925
+ code,
2926
+ numericCode: ERROR_NUMERIC_CODE[code],
2927
+ heading: "Aplicativo n\xE3o autorizado",
2928
+ description: "Este aplicativo n\xE3o est\xE1 autorizado a reproduzir este v\xEDdeo.",
2929
+ ctaLabel: "Ajustar permiss\xF5es",
2930
+ ctaKind: "external",
2931
+ externalUrl: MOVIIE_DASHBOARD_URL
2932
+ };
2933
+ case "referrer_blocked":
2934
+ return {
2935
+ code,
2936
+ numericCode: ERROR_NUMERIC_CODE[code],
2937
+ heading: "Origem n\xE3o autorizada",
2938
+ description: "Origem n\xE3o autorizada para reprodu\xE7\xE3o.",
2939
+ ctaLabel: "Acessar Moviie",
2940
+ ctaKind: "external",
2941
+ externalUrl: MOVIIE_DASHBOARD_URL
2942
+ };
2943
+ case "subscription_inactive":
2944
+ return {
2945
+ code,
2946
+ numericCode: ERROR_NUMERIC_CODE[code],
2947
+ heading: "Reprodu\xE7\xE3o temporariamente indispon\xEDvel",
2948
+ description: "Acesse a Moviie para continuar assistindo.",
2949
+ ctaLabel: "Ir para a Moviie",
2950
+ ctaKind: "external",
2951
+ externalUrl: MOVIIE_DASHBOARD_URL
2952
+ };
2953
+ case "network":
2954
+ return {
2955
+ code,
2956
+ numericCode: ERROR_NUMERIC_CODE[code],
2957
+ heading: "Erro de conex\xE3o",
2958
+ description: "Verifique sua conex\xE3o e tente novamente.",
2959
+ ctaLabel: "Tentar de novo",
2960
+ ctaKind: "retry",
2961
+ externalUrl: null
2962
+ };
2963
+ case "rate_limit":
2964
+ return {
2965
+ code,
2966
+ numericCode: ERROR_NUMERIC_CODE[code],
2967
+ heading: "Muitas solicita\xE7\xF5es",
2968
+ description: "Aguarde alguns instantes e tente novamente.",
2969
+ ctaLabel: "Tentar de novo",
2970
+ ctaKind: "retry",
2971
+ externalUrl: null
2972
+ };
2973
+ case "unknown":
2974
+ default:
2975
+ return {
2976
+ code: "unknown",
2977
+ numericCode: ERROR_NUMERIC_CODE["unknown"],
2978
+ heading: "Erro tempor\xE1rio",
2979
+ description: "Tente novamente em instantes.",
2980
+ ctaLabel: "Tentar de novo",
2981
+ ctaKind: "retry",
2982
+ externalUrl: null
2983
+ };
2984
+ }
2985
+ }
2986
+ function MoviiePlayerErrorShell({
2987
+ error,
2988
+ onRetry
2989
+ }) {
2990
+ const display = getErrorDisplay(error);
2991
+ const handlePress = useCallback(() => {
2992
+ if (display.ctaKind === "retry") {
2993
+ onRetry?.();
2994
+ return;
2995
+ }
2996
+ if (display.ctaKind === "external" && display.externalUrl) {
2997
+ void Linking.openURL(display.externalUrl).catch(() => {
2998
+ });
2999
+ }
3000
+ }, [display, onRetry]);
3001
+ const showCta = display.ctaLabel != null && display.ctaKind !== "none";
3002
+ return /* @__PURE__ */ jsxs(View, { style: styles3.root, accessibilityRole: "alert", children: [
3003
+ /* @__PURE__ */ jsx(View, { style: styles3.radialGlow }),
3004
+ /* @__PURE__ */ jsx(Text, { style: styles3.errorCode, selectable: true, children: display.numericCode }),
3005
+ /* @__PURE__ */ jsxs(View, { style: styles3.content, children: [
3006
+ /* @__PURE__ */ jsx(Text, { style: styles3.heading, numberOfLines: 2, children: display.heading }),
3007
+ /* @__PURE__ */ jsx(Text, { style: styles3.description, numberOfLines: 4, children: display.description }),
3008
+ showCta ? /* @__PURE__ */ jsx(
3009
+ Pressable,
3010
+ {
3011
+ accessibilityRole: "button",
3012
+ accessibilityLabel: display.ctaLabel ?? void 0,
3013
+ onPress: handlePress,
3014
+ style: ({ pressed }) => [styles3.button, pressed ? styles3.buttonPressed : null],
3015
+ children: /* @__PURE__ */ jsx(Text, { style: styles3.buttonLabel, children: display.ctaLabel })
3016
+ }
3017
+ ) : null
3018
+ ] })
3019
+ ] });
3020
+ }
3021
+ var styles3 = StyleSheet.create({
3022
+ root: {
3023
+ ...StyleSheet.absoluteFillObject,
3024
+ backgroundColor: MOVIIE_SHELL_BACKGROUND_HEX,
3025
+ overflow: "hidden",
3026
+ alignItems: "center",
3027
+ justifyContent: "center"
3028
+ },
3029
+ /** Simulates radial gradient: dark outer, lighter center. */
3030
+ radialGlow: {
3031
+ position: "absolute",
3032
+ width: "150%",
3033
+ aspectRatio: 1,
3034
+ borderRadius: 9999,
3035
+ backgroundColor: MOVIIE_SHELL_RADIAL_INNER_HEX
3036
+ },
3037
+ content: {
3038
+ ...StyleSheet.absoluteFillObject,
3039
+ alignItems: "center",
3040
+ justifyContent: "center",
3041
+ paddingHorizontal: 24,
3042
+ gap: 10
3043
+ },
3044
+ heading: {
3045
+ color: MOVIIE_SHELL_HEADING_HEX,
3046
+ fontSize: MOVIIE_SHELL_HEADING_FONT_SIZE_PX,
3047
+ fontWeight: "600",
3048
+ textAlign: "center",
3049
+ maxWidth: MOVIIE_SHELL_CONTENT_MAX_WIDTH_PX
3050
+ },
3051
+ description: {
3052
+ color: MOVIIE_SHELL_DESCRIPTION_HEX,
3053
+ fontSize: MOVIIE_SHELL_DESCRIPTION_FONT_SIZE_PX,
3054
+ lineHeight: 20,
3055
+ textAlign: "center",
3056
+ maxWidth: MOVIIE_SHELL_CONTENT_MAX_WIDTH_PX
3057
+ },
3058
+ errorCode: {
3059
+ position: "absolute",
3060
+ bottom: 8,
3061
+ right: 10,
3062
+ color: MOVIIE_SHELL_ERROR_CODE_HEX,
3063
+ fontSize: 10,
3064
+ fontFamily: "Menlo",
3065
+ opacity: 0.45,
3066
+ zIndex: 2
3067
+ },
3068
+ button: {
3069
+ marginTop: 6,
3070
+ paddingVertical: MOVIIE_SHELL_BUTTON_PADDING_V_PX,
3071
+ paddingHorizontal: MOVIIE_SHELL_BUTTON_PADDING_H_PX,
3072
+ backgroundColor: MOVIIE_SHELL_BUTTON_BG_HEX,
3073
+ borderColor: MOVIIE_SHELL_BUTTON_BORDER_RGBA,
3074
+ borderWidth: StyleSheet.hairlineWidth * 2,
3075
+ borderRadius: MOVIIE_SHELL_BUTTON_RADIUS_PX
3076
+ },
3077
+ buttonPressed: {
3078
+ opacity: MOVIIE_SKIN_CHROME_CONTROL_PRESSED_OPACITY
3079
+ },
3080
+ buttonLabel: {
3081
+ color: MOVIIE_SHELL_BUTTON_LABEL_HEX,
3082
+ fontSize: MOVIIE_SHELL_BUTTON_LABEL_FONT_SIZE_PX,
3083
+ fontWeight: "600",
3084
+ textAlign: "center"
3085
+ }
3086
+ });
3087
+ function MoviiePlayerLoadingShell({
3088
+ posterUrl,
3089
+ primaryColor
3090
+ }) {
3091
+ const accent = resolveSkinAccentColor(primaryColor ?? null);
3092
+ return /* @__PURE__ */ jsxs(View, { style: styles4.root, accessibilityRole: "progressbar", accessibilityLabel: "Carregando v\xEDdeo", children: [
3093
+ /* @__PURE__ */ jsx(View, { style: styles4.radialGlow }),
3094
+ posterUrl ? /* @__PURE__ */ jsx(Image, { source: { uri: posterUrl }, style: styles4.poster, resizeMode: "cover" }) : null,
3095
+ /* @__PURE__ */ jsx(View, { style: styles4.center, children: /* @__PURE__ */ jsx(ActivityIndicator, { size: "large", color: accent }) })
3096
+ ] });
3097
+ }
3098
+ var styles4 = StyleSheet.create({
3099
+ root: {
3100
+ ...StyleSheet.absoluteFillObject,
3101
+ backgroundColor: MOVIIE_SHELL_BACKGROUND_HEX,
3102
+ overflow: "hidden",
3103
+ alignItems: "center",
3104
+ justifyContent: "center"
3105
+ },
3106
+ /** Simulates radial gradient: dark outer, lighter center. */
3107
+ radialGlow: {
3108
+ position: "absolute",
3109
+ width: "150%",
3110
+ aspectRatio: 1,
3111
+ borderRadius: 9999,
3112
+ backgroundColor: MOVIIE_SHELL_RADIAL_INNER_HEX
3113
+ },
3114
+ poster: {
3115
+ ...StyleSheet.absoluteFillObject,
3116
+ opacity: 0.35
3117
+ },
3118
+ center: {
3119
+ ...StyleSheet.absoluteFillObject,
3120
+ alignItems: "center",
3121
+ justifyContent: "center"
3122
+ }
3123
+ });
3124
+ function assignForwardedRef(forwarded, instance) {
3125
+ if (typeof forwarded === "function") {
3126
+ forwarded(instance);
3127
+ return;
3128
+ }
3129
+ if (forwarded) {
3130
+ forwarded.current = instance;
3131
+ }
3132
+ }
3133
+ var HostVideoView = VideoView;
3134
+ var CROSSFADE_TIMING = {
3135
+ duration: MOVIIE_SHELL_CROSSFADE_DURATION_MS,
3136
+ easing: Easing.out(Easing.quad)
3137
+ };
3138
+ var MoviieVideo = forwardRef(
3139
+ function MoviieVideo2(props, ref) {
3140
+ return /* @__PURE__ */ jsx(SafeAreaProvider, { children: /* @__PURE__ */ jsx(MoviieVideoImpl, { ref, ...props }) });
3141
+ }
3142
+ );
3143
+ var MoviieVideoImpl = forwardRef(
3144
+ function MoviieVideoImpl2(props, forwardedRef) {
3145
+ const {
3146
+ player,
3147
+ nativeControls,
3148
+ native = false,
3149
+ playback = null,
3150
+ controlsAutoHideMs,
3151
+ controlsVisibleByDefault,
3152
+ pictureInPicture,
3153
+ pictureInPictureAutoStart,
3154
+ allowsPictureInPicture: passthroughAllowsPictureInPicture,
3155
+ startsPictureInPictureAutomatically: passthroughStartsPictureInPictureAutomatically,
3156
+ requiresLinearPlayback: passthroughRequiresLinearPlayback,
3157
+ onPictureInPictureStart: userOnPictureInPictureStart,
3158
+ onPictureInPictureStop: userOnPictureInPictureStop,
3159
+ children,
3160
+ style,
3161
+ contentFit,
3162
+ error = null,
3163
+ isLoading = false,
3164
+ aspectRatio,
3165
+ onRetry,
3166
+ retry,
3167
+ castAdapter: castAdapterProp,
3168
+ cast,
3169
+ onTelemetryError,
3170
+ onTelemetryEvent,
3171
+ ...videoRest
3172
+ } = props;
3173
+ const castAdapter = castAdapterProp ?? getRegisteredCastAdapter();
3174
+ const lastReportedErrorRef = useRef(null);
3175
+ useEffect(() => {
3176
+ if (error == null) {
3177
+ lastReportedErrorRef.current = null;
3178
+ return;
3179
+ }
3180
+ if (lastReportedErrorRef.current === error) {
3181
+ return;
3182
+ }
3183
+ lastReportedErrorRef.current = error;
3184
+ safeInvokeMoviieTelemetry(onTelemetryError, error, {
3185
+ phase: "playback_fetch"
3186
+ });
3187
+ }, [error, onTelemetryError]);
3188
+ const useMoviieChrome = !native;
3189
+ const shellState = playback != null ? "ready" : error != null ? "error" : "loading";
3190
+ const loadingOpacity = useSharedValue(shellState === "loading" ? 1 : 0);
3191
+ const errorOpacity = useSharedValue(shellState === "error" ? 1 : 0);
3192
+ const playerOpacity = useSharedValue(shellState === "ready" ? 1 : 0);
3193
+ useEffect(() => {
3194
+ loadingOpacity.value = withTiming(shellState === "loading" ? 1 : 0, CROSSFADE_TIMING);
3195
+ errorOpacity.value = withTiming(shellState === "error" ? 1 : 0, CROSSFADE_TIMING);
3196
+ playerOpacity.value = withTiming(shellState === "ready" ? 1 : 0, CROSSFADE_TIMING);
3197
+ }, [shellState, loadingOpacity, errorOpacity, playerOpacity]);
3198
+ const loadingAnimatedStyle = useAnimatedStyle(() => ({
3199
+ opacity: loadingOpacity.value
3200
+ }));
3201
+ const errorAnimatedStyle = useAnimatedStyle(() => ({
3202
+ opacity: errorOpacity.value
3203
+ }));
3204
+ const playerAnimatedStyle = useAnimatedStyle(() => ({
3205
+ opacity: playerOpacity.value
3206
+ }));
3207
+ const effectiveOnRetry = useMemo(() => {
3208
+ if (onRetry) return onRetry;
3209
+ if (retry) return retry;
3210
+ return void 0;
3211
+ }, [onRetry, retry]);
3212
+ const nativePresentationVideoProps = useMemo(() => {
3213
+ if (playback != null) {
3214
+ const embedBaseline = buildMoviieVideoPresentationProps({
3215
+ pictureInPicture,
3216
+ pictureInPictureAutoStart,
3217
+ profile: playback.profile,
3218
+ showPip: playback.controls.showPip
3219
+ });
3220
+ return {
3221
+ allowsPictureInPicture: passthroughAllowsPictureInPicture ?? embedBaseline.allowsPictureInPicture,
3222
+ startsPictureInPictureAutomatically: passthroughStartsPictureInPictureAutomatically ?? embedBaseline.startsPictureInPictureAutomatically,
3223
+ requiresLinearPlayback: passthroughRequiresLinearPlayback ?? embedBaseline.requiresLinearPlayback
3224
+ };
3225
+ }
3226
+ return {
3227
+ allowsPictureInPicture: passthroughAllowsPictureInPicture ?? false,
3228
+ startsPictureInPictureAutomatically: passthroughStartsPictureInPictureAutomatically ?? false,
3229
+ requiresLinearPlayback: passthroughRequiresLinearPlayback ?? false
3230
+ };
3231
+ }, [
3232
+ playback,
3233
+ pictureInPicture,
3234
+ pictureInPictureAutoStart,
3235
+ passthroughAllowsPictureInPicture,
3236
+ passthroughStartsPictureInPictureAutomatically,
3237
+ passthroughRequiresLinearPlayback
3238
+ ]);
3239
+ const pipControlEnabled = useMemo(() => {
3240
+ if (!playback || !useMoviieChrome) {
3241
+ return false;
3242
+ }
3243
+ return isPictureInPictureSupported() && nativePresentationVideoProps.allowsPictureInPicture;
3244
+ }, [playback, useMoviieChrome, nativePresentationVideoProps.allowsPictureInPicture]);
3245
+ const innerRef = useRef(null);
3246
+ const fullscreenModalRef = useRef(null);
3247
+ const [customFullscreen, setCustomFullscreen] = useState(false);
3248
+ const [pipActive, setPipActive] = useState(false);
3249
+ const [castState, setCastState] = useState(
3250
+ () => castAdapter?.isAvailable() ? castAdapter.getState() : "unavailable"
3251
+ );
3252
+ const castDesired = useMemo(() => {
3253
+ if (!playback || !useMoviieChrome) return false;
3254
+ if (cast === false) return false;
3255
+ if (cast === true) return true;
3256
+ return playback.controls.chromecast === true;
3257
+ }, [cast, playback, useMoviieChrome]);
3258
+ const castControlEnabled = useMemo(() => {
3259
+ if (!castDesired) return false;
3260
+ return castAdapter?.isAvailable() === true;
3261
+ }, [castAdapter, castDesired]);
3262
+ const castConnected = castState === "connected";
3263
+ const openCastPicker = useCallback(() => {
3264
+ void castAdapter?.showDevicePicker().catch(() => {
3265
+ });
3266
+ }, [castAdapter]);
3267
+ useEffect(() => {
3268
+ if (!castAdapter?.isAvailable()) {
3269
+ setCastState("unavailable");
3270
+ return;
3271
+ }
3272
+ setCastState(castAdapter.getState());
3273
+ const unsubscribe = castAdapter.subscribe((next) => {
3274
+ setCastState(next);
3275
+ });
3276
+ return () => {
3277
+ unsubscribe();
3278
+ };
3279
+ }, [castAdapter]);
3280
+ useEffect(() => {
3281
+ if (castDesired && (!castAdapter || !castAdapter.isAvailable())) {
3282
+ warnOnce(
3283
+ "moviie-cast-missing-adapter",
3284
+ '[@moviie/player-expo] O embed habilita Chromecast mas a lib n\xE3o est\xE1 dispon\xEDvel. Instale `react-native-google-cast` e adicione `import "@moviie/player-expo/cast"` no seu _layout.tsx (ver docs/player-expo/cast). Para desligar explicitamente, passe `cast={false}` em <MoviieVideo />.'
3285
+ );
3286
+ }
3287
+ }, [castDesired, castAdapter]);
3288
+ useEffect(() => {
3289
+ if (!castAdapter?.isAvailable() || !playback || !player) return;
3290
+ if (castState !== "connected") return;
3291
+ const startSeconds = player.currentTime ?? 0;
3292
+ void castAdapter.loadMedia({
3293
+ uri: playback.playback.uri,
3294
+ title: playback.title,
3295
+ posterUrl: playback.posterUrl,
3296
+ durationSeconds: playback.durationSeconds ?? null,
3297
+ startSeconds
3298
+ }).then(() => {
3299
+ try {
3300
+ player.pause();
3301
+ } catch {
3302
+ }
3303
+ }).catch(() => {
3304
+ });
3305
+ }, [castAdapter, castState, playback, player]);
3306
+ const { width: winW, height: winH } = useWindowDimensions();
3307
+ const safeAreaInsets = useSafeAreaInsets();
3308
+ const fullscreenStageLayout = useMemo(
3309
+ () => computeCustomFullscreenStageDimensions({
3310
+ windowHeight: winH,
3311
+ safeInsets: safeAreaInsets,
3312
+ stageOverscanPx: MOVIIE_SKIN_CUSTOM_FULLSCREEN_STAGE_OVERSCAN_PX,
3313
+ windowWidth: winW
3314
+ }),
3315
+ [
3316
+ winW,
3317
+ winH,
3318
+ safeAreaInsets.bottom,
3319
+ safeAreaInsets.left,
3320
+ safeAreaInsets.right,
3321
+ safeAreaInsets.top
3322
+ ]
3323
+ );
3324
+ useEffect(() => {
3325
+ setPipActive(false);
3326
+ }, [playback?.embedId]);
3327
+ const handleNativePictureInPictureStart = useCallback(() => {
3328
+ if (useMoviieChrome && playback != null) {
3329
+ setPipActive(true);
3330
+ }
3331
+ userOnPictureInPictureStart?.();
3332
+ }, [playback, useMoviieChrome, userOnPictureInPictureStart]);
3333
+ const handleNativePictureInPictureStop = useCallback(() => {
3334
+ if (useMoviieChrome && playback != null) {
3335
+ setPipActive(false);
3336
+ }
3337
+ userOnPictureInPictureStop?.();
3338
+ }, [playback, useMoviieChrome, userOnPictureInPictureStop]);
3339
+ const togglePictureInPicture = useCallback(async () => {
3340
+ const surface = innerRef.current;
3341
+ if (!surface?.startPictureInPicture) {
3342
+ return;
3343
+ }
3344
+ try {
3345
+ if (pipActive) {
3346
+ await surface.stopPictureInPicture?.();
3347
+ } else {
3348
+ await surface.startPictureInPicture();
3349
+ }
3350
+ } catch {
3351
+ }
3352
+ }, [pipActive]);
3353
+ const requestExitCustomFullscreenAnimated = useCallback(() => {
3354
+ fullscreenModalRef.current?.animateExit();
3355
+ }, []);
3356
+ const setRefs = useCallback(
3357
+ (node) => {
3358
+ innerRef.current = node;
3359
+ assignForwardedRef(forwardedRef, node);
3360
+ },
3361
+ [forwardedRef]
3362
+ );
3363
+ const resolvedNativeControlsDefault = native ? playback?.profile === MOVIIE_PLAYBACK_PROFILE.VSL ? false : true : false;
3364
+ const effectiveNativeControls = nativeControls ?? resolvedNativeControlsDefault;
3365
+ const intrinsicAspectRatio = playback?.videoWidthPx != null && playback?.videoHeightPx != null && playback.videoWidthPx > 0 && playback.videoHeightPx > 0 ? playback.videoWidthPx / playback.videoHeightPx : null;
3366
+ const effectiveAspectRatio = intrinsicAspectRatio ?? aspectRatio ?? MOVIIE_SHELL_DEFAULT_ASPECT_RATIO;
3367
+ const effectiveContentFit = contentFit ?? (playback != null ? playback.isVertical ? MOVIIE_VIDEO_DEFAULT_CONTENT_FIT.VERTICAL : MOVIIE_VIDEO_DEFAULT_CONTENT_FIT.HORIZONTAL : void 0);
3368
+ const { columnStyle, surfaceStyle } = partitionMoviieVideoHostStyle(StyleSheet.flatten(style));
3369
+ const stageContainerStyle = [
3370
+ styles5.videoStage,
3371
+ {
3372
+ width: "100%",
3373
+ aspectRatio: effectiveAspectRatio,
3374
+ alignSelf: "stretch",
3375
+ flex: 0
3376
+ },
3377
+ surfaceStyle,
3378
+ useMoviieChrome && playback ? styles5.videoStageClipNone : null
3379
+ ];
3380
+ const inlineVideoAndOverlay = player != null ? useMoviieChrome && playback ? /* @__PURE__ */ jsxs(Fragment, { children: [
3381
+ createElement(HostVideoView, {
3382
+ ...videoRest,
3383
+ ...nativePresentationVideoProps,
3384
+ ref: setRefs,
3385
+ player,
3386
+ nativeControls: effectiveNativeControls,
3387
+ onPictureInPictureStart: handleNativePictureInPictureStart,
3388
+ onPictureInPictureStop: handleNativePictureInPictureStop,
3389
+ ...effectiveContentFit != null ? { contentFit: effectiveContentFit } : {},
3390
+ style: styles5.videoFill
3391
+ }),
3392
+ /* @__PURE__ */ jsx(MoviieSkinVideoOverlay, {})
3393
+ ] }) : createElement(HostVideoView, {
3394
+ ...videoRest,
3395
+ ...nativePresentationVideoProps,
3396
+ ref: setRefs,
3397
+ player,
3398
+ nativeControls: effectiveNativeControls,
3399
+ onPictureInPictureStart: handleNativePictureInPictureStart,
3400
+ onPictureInPictureStop: handleNativePictureInPictureStop,
3401
+ ...effectiveContentFit != null ? { contentFit: effectiveContentFit } : {},
3402
+ style: styles5.videoFill
3403
+ }) : null;
3404
+ const renderInlineStage = (active) => {
3405
+ if (!playback || !useMoviieChrome) {
3406
+ return inlineVideoAndOverlay;
3407
+ }
3408
+ if (!customFullscreen) {
3409
+ return inlineVideoAndOverlay;
3410
+ }
3411
+ return /* @__PURE__ */ jsx(
3412
+ View,
3413
+ {
3414
+ style: [
3415
+ styles5.videoFill,
3416
+ {
3417
+ backgroundColor: MOVIIE_SKIN_CUSTOM_FULLSCREEN_STAGE_BACKGROUND_HEX
3418
+ }
3419
+ ]
3420
+ }
3421
+ );
3422
+ };
3423
+ const stageContent = /* @__PURE__ */ jsxs(View, { style: stageContainerStyle, children: [
3424
+ /* @__PURE__ */ jsx(
3425
+ ReanimatedView,
3426
+ {
3427
+ style: [StyleSheet.absoluteFillObject, loadingAnimatedStyle, styles5.shellLayer],
3428
+ pointerEvents: shellState === "loading" ? "auto" : "none",
3429
+ children: shellState === "loading" || loadingOpacity.value > 0 ? /* @__PURE__ */ jsx(
3430
+ MoviiePlayerLoadingShell,
3431
+ {
3432
+ posterUrl: playback?.posterUrl ?? null,
3433
+ primaryColor: playback?.branding.primaryColor ?? null
3434
+ }
3435
+ ) : null
3436
+ }
3437
+ ),
3438
+ /* @__PURE__ */ jsx(
3439
+ ReanimatedView,
3440
+ {
3441
+ style: [StyleSheet.absoluteFillObject, errorAnimatedStyle, styles5.shellLayer],
3442
+ pointerEvents: shellState === "error" ? "auto" : "none",
3443
+ children: error ? /* @__PURE__ */ jsx(MoviiePlayerErrorShell, { error, onRetry: effectiveOnRetry }) : null
3444
+ }
3445
+ ),
3446
+ /* @__PURE__ */ jsx(
3447
+ ReanimatedView,
3448
+ {
3449
+ style: [StyleSheet.absoluteFillObject, playerAnimatedStyle],
3450
+ pointerEvents: shellState === "ready" ? "auto" : "none",
3451
+ children: renderInlineStage()
3452
+ }
3453
+ )
3454
+ ] });
3455
+ if (useMoviieChrome && playback && player) {
3456
+ return /* @__PURE__ */ jsx(
3457
+ MoviieSkinChromeProvider,
3458
+ {
3459
+ autoHideMs: controlsAutoHideMs,
3460
+ customFullscreen,
3461
+ pictureInPictureActive: pipActive,
3462
+ pictureInPictureControlEnabled: pipControlEnabled,
3463
+ togglePictureInPicture,
3464
+ castControlEnabled,
3465
+ castConnected,
3466
+ openCastPicker,
3467
+ onRequestEnterCustomFullscreen: () => setCustomFullscreen(true),
3468
+ onRequestExitCustomFullscreenAnimated: requestExitCustomFullscreenAnimated,
3469
+ playback,
3470
+ player,
3471
+ skinLayoutMetricsWidth: customFullscreen ? fullscreenStageLayout.layoutMetricsBasisWidth : void 0,
3472
+ videoViewRef: innerRef,
3473
+ visibleByDefault: controlsVisibleByDefault,
3474
+ children: /* @__PURE__ */ jsxs(Fragment, { children: [
3475
+ /* @__PURE__ */ jsxs(View, { style: [styles5.column, columnStyle], children: [
3476
+ stageContent,
3477
+ children
3478
+ ] }),
3479
+ /* @__PURE__ */ jsx(
3480
+ MoviieSkinCustomFullscreenModal,
3481
+ {
3482
+ ref: fullscreenModalRef,
3483
+ visible: customFullscreen,
3484
+ onExitComplete: () => setCustomFullscreen(false),
3485
+ children: /* @__PURE__ */ jsxs(View, { style: styles5.fullscreenStage, children: [
3486
+ createElement(HostVideoView, {
3487
+ ...videoRest,
3488
+ ...nativePresentationVideoProps,
3489
+ ref: setRefs,
3490
+ player,
3491
+ nativeControls: effectiveNativeControls,
3492
+ onPictureInPictureStart: handleNativePictureInPictureStart,
3493
+ onPictureInPictureStop: handleNativePictureInPictureStop,
3494
+ contentFit: MOVIIE_SKIN_CUSTOM_FULLSCREEN_VIDEO_CONTENT_FIT,
3495
+ style: StyleSheet.absoluteFillObject
3496
+ }),
3497
+ /* @__PURE__ */ jsx(MoviieSkinVideoOverlay, {})
3498
+ ] })
3499
+ }
3500
+ )
3501
+ ] })
3502
+ }
3503
+ );
3504
+ }
3505
+ return /* @__PURE__ */ jsxs(View, { style: [styles5.column, columnStyle], children: [
3506
+ stageContent,
3507
+ children,
3508
+ castAdapter?.renderAndroidProxy?.()
3509
+ ] });
3510
+ }
3511
+ );
3512
+ var styles5 = StyleSheet.create({
3513
+ column: {
3514
+ alignSelf: "stretch",
3515
+ width: "100%"
3516
+ },
3517
+ videoStage: {
3518
+ overflow: "hidden",
3519
+ position: "relative",
3520
+ width: "100%"
3521
+ },
3522
+ videoStageClipNone: {
3523
+ overflow: "visible"
3524
+ },
3525
+ videoFill: {
3526
+ flex: 1,
3527
+ width: "100%",
3528
+ alignSelf: "stretch"
3529
+ },
3530
+ fullscreenStage: {
3531
+ flex: 1,
3532
+ position: "relative",
3533
+ width: "100%"
3534
+ },
3535
+ shellLayer: {
3536
+ zIndex: 1
3537
+ }
3538
+ });
3539
+ var MoviieErrorBoundary = class extends React.Component {
3540
+ state = { error: null };
3541
+ static getDerivedStateFromError(error) {
3542
+ return { error };
3543
+ }
3544
+ componentDidCatch(error, info) {
3545
+ if (this.props.onError != null) {
3546
+ try {
3547
+ this.props.onError(error, info);
3548
+ } catch {
3549
+ }
3550
+ }
3551
+ }
3552
+ reset = () => {
3553
+ this.setState({ error: null });
3554
+ };
3555
+ render() {
3556
+ if (this.state.error != null) {
3557
+ if (this.props.fallback != null) {
3558
+ return this.props.fallback({
3559
+ error: this.state.error,
3560
+ reset: this.reset
3561
+ });
3562
+ }
3563
+ return null;
3564
+ }
3565
+ return this.props.children;
3566
+ }
3567
+ };
3568
+
3569
+ export { MoviieErrorBoundary, MoviieVideo, addMoviiePlaybackEndedListener, buildMoviieVideoPresentationProps, buildMoviieWatchEmbedUrl, computeMoviiePlaybackEnded, normalizeWatchOrigin, resolveMoviieWatchOrigin, useMoviieEvent, useMoviiePlayback, useMoviiePlaybackEnded, useMoviiePlayer };
3570
+ //# sourceMappingURL=index.mjs.map
3571
+ //# sourceMappingURL=index.mjs.map