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