mega-stream-proxy 0.1.9 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -49
- package/dist/megaFile.js +22 -9
- package/dist/{Player/playerAndroid/MegaVideoPlayerAndroid.d.ts → player/MegaVideoPlayer.d.ts} +4 -13
- package/dist/player/MegaVideoPlayer.js +307 -0
- package/dist/{Player/playerweb → player}/PlayerControls.d.ts +2 -14
- package/dist/player/PlayerControls.js +289 -0
- package/dist/{Player/playerweb → player}/SubtitleDisplay.d.ts +3 -3
- package/dist/{Player/playerweb → player}/SubtitleDisplay.js +1 -1
- package/dist/player/index.d.ts +3 -0
- package/dist/player/index.js +3 -0
- package/dist/{Player/playerweb/MegaVideoPlayer.d.ts → playerweb/MegaVideoPlayerWeb.d.ts} +6 -18
- package/dist/{Player/playerweb/MegaVideoPlayer.js → playerweb/MegaVideoPlayerWeb.js} +18 -18
- package/dist/{Player/playerAndroid/PlayerControlsAndroid.d.ts → playerweb/PlayerControlsWeb.d.ts} +2 -16
- package/dist/playerweb/PlayerControlsWeb.js +289 -0
- package/dist/playerweb/SubtitleDisplayWeb.d.ts +15 -0
- package/dist/playerweb/SubtitleDisplayWeb.js +32 -0
- package/dist/playerweb/index.d.ts +3 -0
- package/dist/playerweb/index.js +3 -0
- package/dist/proxy/native.js +11 -3
- package/expo-module.config.json +3 -0
- package/package.json +15 -18
- package/src/megaFile.ts +28 -15
- package/src/player/MegaVideoPlayer.tsx +474 -0
- package/src/{Player/playerweb → player}/PlayerControls.tsx +200 -369
- package/src/{Player/playerweb → player}/SubtitleDisplay.tsx +3 -3
- package/src/player/index.ts +7 -0
- package/src/{Player/playerweb/MegaVideoPlayer.tsx → playerweb/MegaVideoPlayerWeb.tsx} +41 -58
- package/src/playerweb/PlayerControlsWeb.tsx +629 -0
- package/src/playerweb/SubtitleDisplayWeb.tsx +48 -0
- package/src/playerweb/index.ts +3 -0
- package/src/proxy/native.ts +10 -3
- package/dist/Player/native.d.ts +0 -3
- package/dist/Player/native.js +0 -3
- package/dist/Player/playerAndroid/MegaVideoPlayerAndroid.js +0 -624
- package/dist/Player/playerAndroid/PlayerControlsAndroid.js +0 -370
- package/dist/Player/playerAndroid/SubtitleDisplayAndroid.d.ts +0 -21
- package/dist/Player/playerAndroid/SubtitleDisplayAndroid.js +0 -55
- package/dist/Player/playerAndroid/index.d.ts +0 -3
- package/dist/Player/playerAndroid/index.js +0 -3
- package/dist/Player/playerweb/PlayerControls.js +0 -346
- package/dist/Player/playerweb/index.d.ts +0 -3
- package/dist/Player/playerweb/index.js +0 -3
- package/dist/Player/web.d.ts +0 -3
- package/dist/Player/web.js +0 -3
- package/src/Player/playerAndroid/MegaVideoPlayerAndroid.tsx +0 -825
- package/src/Player/playerAndroid/PlayerControlsAndroid.tsx +0 -824
- package/src/Player/playerAndroid/SubtitleDisplayAndroid.tsx +0 -80
- package/src/Player/playerAndroid/index.ts +0 -7
- package/src/Player/playerweb/index.ts +0 -3
|
@@ -8,9 +8,8 @@ import {
|
|
|
8
8
|
View,
|
|
9
9
|
} from "react-native";
|
|
10
10
|
import { Ionicons } from "@expo/vector-icons";
|
|
11
|
-
import { SubtitleDisplayWeb } from "./SubtitleDisplay.js";
|
|
12
11
|
|
|
13
|
-
export type
|
|
12
|
+
export type PlayerControlsProps = {
|
|
14
13
|
playing: boolean;
|
|
15
14
|
buffering: boolean;
|
|
16
15
|
currentTime: number;
|
|
@@ -40,18 +39,6 @@ export type PlayerControlsWebProps = {
|
|
|
40
39
|
outroRegion?: [number, number];
|
|
41
40
|
onSkipIntro?: () => void;
|
|
42
41
|
onSkipOutro?: () => void;
|
|
43
|
-
/** resolution labels in display order — the FIRST is the default source
|
|
44
|
-
* (the episode URL), e.g. ["Default", "1080p", "720p", "480p"]. */
|
|
45
|
-
resolutions?: string[];
|
|
46
|
-
/** currently active resolution label */
|
|
47
|
-
resolution?: string;
|
|
48
|
-
width?: number;
|
|
49
|
-
height?: number;
|
|
50
|
-
cue?: any;
|
|
51
|
-
enabled?: any;
|
|
52
|
-
fontSize?: any;
|
|
53
|
-
/** select a resolution — the parent resolves the label to its URL */
|
|
54
|
-
onSelectResolution?: (label: string) => void;
|
|
55
42
|
onToggleVisible: () => void;
|
|
56
43
|
onPlayPause: () => void;
|
|
57
44
|
onSkip: (deltaSeconds: number) => void;
|
|
@@ -74,7 +61,7 @@ const fmt = (s: number) => {
|
|
|
74
61
|
|
|
75
62
|
const clamp01 = (v: number) => Math.min(1, Math.max(0, v));
|
|
76
63
|
|
|
77
|
-
export function
|
|
64
|
+
export function PlayerControls(props: PlayerControlsProps) {
|
|
78
65
|
const {
|
|
79
66
|
playing,
|
|
80
67
|
buffering,
|
|
@@ -95,9 +82,6 @@ export function PlayerControlsWeb(props: PlayerControlsWebProps) {
|
|
|
95
82
|
outroRegion,
|
|
96
83
|
onSkipIntro,
|
|
97
84
|
onSkipOutro,
|
|
98
|
-
resolutions,
|
|
99
|
-
resolution,
|
|
100
|
-
onSelectResolution,
|
|
101
85
|
onToggleVisible,
|
|
102
86
|
onPlayPause,
|
|
103
87
|
onSkip,
|
|
@@ -106,21 +90,19 @@ export function PlayerControlsWeb(props: PlayerControlsWebProps) {
|
|
|
106
90
|
onToggleSubtitles,
|
|
107
91
|
onVolume,
|
|
108
92
|
onFullscreen,
|
|
109
|
-
width,
|
|
110
|
-
height,
|
|
111
|
-
cue,
|
|
112
|
-
enabled,
|
|
113
|
-
fontSize,
|
|
114
93
|
} = props;
|
|
115
94
|
|
|
116
95
|
const hasSubs = (subtitleTracks?.length ?? 0) > 0;
|
|
117
|
-
const
|
|
96
|
+
const subLang = (subtitleTrack ?? subtitleTracks?.[0] ?? "CC")
|
|
97
|
+
.split("-")[0]
|
|
98
|
+
.slice(0, 5)
|
|
99
|
+
.toUpperCase();
|
|
118
100
|
|
|
119
101
|
const [trackW, setTrackW] = useState(0);
|
|
120
102
|
const [scrubPct, setScrubPct] = useState<number | null>(null);
|
|
121
103
|
const [volW, setVolW] = useState(0);
|
|
122
|
-
const [
|
|
123
|
-
const [
|
|
104
|
+
const [subOpen, setSubOpen] = useState(false);
|
|
105
|
+
const [subTab, setSubTab] = useState<"lang" | "size">("lang");
|
|
124
106
|
|
|
125
107
|
const pct = duration > 0 ? clamp01(currentTime / duration) : 0;
|
|
126
108
|
const volPct = clamp01(volume);
|
|
@@ -143,13 +125,9 @@ export function PlayerControlsWeb(props: PlayerControlsWebProps) {
|
|
|
143
125
|
onStartShouldSetPanResponder: () => true,
|
|
144
126
|
onMoveShouldSetPanResponder: () => true,
|
|
145
127
|
onPanResponderGrant: (e) =>
|
|
146
|
-
setScrubPct(
|
|
147
|
-
trackW > 0 ? clamp01(e.nativeEvent.locationX / trackW) : 0,
|
|
148
|
-
),
|
|
128
|
+
setScrubPct(trackW > 0 ? clamp01(e.nativeEvent.locationX / trackW) : 0),
|
|
149
129
|
onPanResponderMove: (e) =>
|
|
150
|
-
setScrubPct(
|
|
151
|
-
trackW > 0 ? clamp01(e.nativeEvent.locationX / trackW) : 0,
|
|
152
|
-
),
|
|
130
|
+
setScrubPct(trackW > 0 ? clamp01(e.nativeEvent.locationX / trackW) : 0),
|
|
153
131
|
onPanResponderRelease: (e) => {
|
|
154
132
|
const p = trackW > 0 ? clamp01(e.nativeEvent.locationX / trackW) : 0;
|
|
155
133
|
setScrubPct(null);
|
|
@@ -174,6 +152,7 @@ export function PlayerControlsWeb(props: PlayerControlsWebProps) {
|
|
|
174
152
|
|
|
175
153
|
const SIZE_MIN = 10;
|
|
176
154
|
const SIZE_MAX = 34;
|
|
155
|
+
const [sizeW, setSizeW] = useState(0);
|
|
177
156
|
const currentSize = subtitleFontSize ?? 14;
|
|
178
157
|
const sizePct = clamp01((currentSize - SIZE_MIN) / (SIZE_MAX - SIZE_MIN));
|
|
179
158
|
const sizePan = useMemo(
|
|
@@ -184,39 +163,22 @@ export function PlayerControlsWeb(props: PlayerControlsWebProps) {
|
|
|
184
163
|
onPanResponderGrant: (e) => {
|
|
185
164
|
if (!onChangeSubtitleFontSize) return;
|
|
186
165
|
const p = sizeW > 0 ? clamp01(e.nativeEvent.locationX / sizeW) : 0;
|
|
187
|
-
onChangeSubtitleFontSize?.(
|
|
188
|
-
Math.round(SIZE_MIN + p * (SIZE_MAX - SIZE_MIN)),
|
|
189
|
-
);
|
|
166
|
+
onChangeSubtitleFontSize?.(Math.round(SIZE_MIN + p * (SIZE_MAX - SIZE_MIN)));
|
|
190
167
|
},
|
|
191
168
|
onPanResponderMove: (e) => {
|
|
192
169
|
if (!onChangeSubtitleFontSize) return;
|
|
193
170
|
const p = sizeW > 0 ? clamp01(e.nativeEvent.locationX / sizeW) : 0;
|
|
194
|
-
onChangeSubtitleFontSize?.(
|
|
195
|
-
Math.round(SIZE_MIN + p * (SIZE_MAX - SIZE_MIN)),
|
|
196
|
-
);
|
|
171
|
+
onChangeSubtitleFontSize?.(Math.round(SIZE_MIN + p * (SIZE_MAX - SIZE_MIN)));
|
|
197
172
|
},
|
|
198
173
|
}),
|
|
199
174
|
[sizeW, onChangeSubtitleFontSize],
|
|
200
175
|
);
|
|
201
176
|
|
|
202
177
|
const shownPct = scrubPct ?? pct;
|
|
203
|
-
const volIcon =
|
|
204
|
-
muted || volume === 0
|
|
205
|
-
? "volume-mute"
|
|
206
|
-
: volume < 0.5
|
|
207
|
-
? "volume-low"
|
|
208
|
-
: "volume-high";
|
|
209
|
-
|
|
210
|
-
const showSubsSection = hasSubs || !!onChangeSubtitleFontSize;
|
|
178
|
+
const volIcon = muted || volume === 0 ? "volume-mute" : volume < 0.5 ? "volume-low" : "volume-high";
|
|
211
179
|
|
|
212
180
|
return (
|
|
213
|
-
<View
|
|
214
|
-
style={[
|
|
215
|
-
styles.root,
|
|
216
|
-
StyleSheet.absoluteFill,
|
|
217
|
-
{ width: width, height: height },
|
|
218
|
-
]}
|
|
219
|
-
>
|
|
181
|
+
<View style={styles.root}>
|
|
220
182
|
{/* tap surface toggles the chrome */}
|
|
221
183
|
<TouchableOpacity
|
|
222
184
|
style={StyleSheet.absoluteFill}
|
|
@@ -225,22 +187,13 @@ export function PlayerControlsWeb(props: PlayerControlsWebProps) {
|
|
|
225
187
|
/>
|
|
226
188
|
|
|
227
189
|
{buffering && (
|
|
228
|
-
<View
|
|
229
|
-
style={[StyleSheet.absoluteFill, styles.centerBadge]}
|
|
230
|
-
pointerEvents="none"
|
|
231
|
-
>
|
|
190
|
+
<View style={styles.centerBadge} pointerEvents="none">
|
|
232
191
|
<ActivityIndicator color="#e50914" size="large" />
|
|
233
192
|
</View>
|
|
234
193
|
)}
|
|
235
194
|
|
|
236
195
|
{visible && (
|
|
237
|
-
|
|
238
|
-
style={[
|
|
239
|
-
StyleSheet.absoluteFill,
|
|
240
|
-
{ width: "100%", height: "100%", position: "absolute" },
|
|
241
|
-
]}
|
|
242
|
-
pointerEvents="box-none"
|
|
243
|
-
>
|
|
196
|
+
<>
|
|
244
197
|
{/* top gradient-ish bar */}
|
|
245
198
|
<View style={[styles.bar, styles.top]} pointerEvents="box-none">
|
|
246
199
|
{!!title && (
|
|
@@ -248,24 +201,19 @@ export function PlayerControlsWeb(props: PlayerControlsWebProps) {
|
|
|
248
201
|
{title}
|
|
249
202
|
</Text>
|
|
250
203
|
)}
|
|
204
|
+
<TouchableOpacity onPress={onToggleMute} hitSlop={8}>
|
|
205
|
+
<Ionicons name={muted ? "volume-mute" : "volume-high"} size={20} color="#fff" />
|
|
206
|
+
</TouchableOpacity>
|
|
251
207
|
</View>
|
|
252
208
|
|
|
253
209
|
{/* center buttons */}
|
|
254
|
-
<View
|
|
255
|
-
style={
|
|
256
|
-
pointerEvents="box-none"
|
|
257
|
-
>
|
|
258
|
-
<TouchableOpacity
|
|
259
|
-
style={styles.skipBtn}
|
|
260
|
-
onPress={() => onSkip(-10)}
|
|
261
|
-
>
|
|
210
|
+
<View style={styles.centerRow} pointerEvents="box-none">
|
|
211
|
+
<TouchableOpacity style={styles.skipBtn} onPress={() => onSkip(-10)}>
|
|
262
212
|
<Ionicons name="play-back" size={30} color="#fff" />
|
|
263
213
|
</TouchableOpacity>
|
|
264
214
|
<TouchableOpacity style={styles.ppBtn} onPress={onPlayPause}>
|
|
265
215
|
<Ionicons
|
|
266
|
-
name={
|
|
267
|
-
buffering ? "ellipsis-horizontal" : playing ? "pause" : "play"
|
|
268
|
-
}
|
|
216
|
+
name={buffering ? "ellipsis-horizontal" : playing ? "pause" : "play"}
|
|
269
217
|
size={38}
|
|
270
218
|
color="#fff"
|
|
271
219
|
/>
|
|
@@ -275,7 +223,7 @@ export function PlayerControlsWeb(props: PlayerControlsWebProps) {
|
|
|
275
223
|
</TouchableOpacity>
|
|
276
224
|
</View>
|
|
277
225
|
|
|
278
|
-
{/* bottom bar
|
|
226
|
+
{/* bottom bar */}
|
|
279
227
|
<View style={[styles.bar, styles.bottom]} pointerEvents="box-none">
|
|
280
228
|
<Text style={styles.time}>
|
|
281
229
|
{fmt(scrubPct !== null ? scrubPct * duration : currentTime)}
|
|
@@ -295,32 +243,28 @@ export function PlayerControlsWeb(props: PlayerControlsWebProps) {
|
|
|
295
243
|
]}
|
|
296
244
|
/>
|
|
297
245
|
)}
|
|
298
|
-
{introRegion &&
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
{
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
},
|
|
321
|
-
]}
|
|
322
|
-
/>
|
|
323
|
-
)}
|
|
246
|
+
{introRegion && duration > 0 && introRegion[1] > introRegion[0] && (
|
|
247
|
+
<View
|
|
248
|
+
style={[
|
|
249
|
+
styles.introHighlight,
|
|
250
|
+
{
|
|
251
|
+
left: `${clamp01(introRegion[0] / duration) * 100}%`,
|
|
252
|
+
width: `${clamp01((introRegion[1] - introRegion[0]) / duration) * 100}%`,
|
|
253
|
+
},
|
|
254
|
+
]}
|
|
255
|
+
/>
|
|
256
|
+
)}
|
|
257
|
+
{outroRegion && duration > 0 && outroRegion[1] > outroRegion[0] && (
|
|
258
|
+
<View
|
|
259
|
+
style={[
|
|
260
|
+
styles.outroHighlight,
|
|
261
|
+
{
|
|
262
|
+
left: `${clamp01(outroRegion[0] / duration) * 100}%`,
|
|
263
|
+
width: `${clamp01((outroRegion[1] - outroRegion[0]) / duration) * 100}%`,
|
|
264
|
+
},
|
|
265
|
+
]}
|
|
266
|
+
/>
|
|
267
|
+
)}
|
|
324
268
|
<View style={[styles.fill, { width: `${shownPct * 100}%` }]} />
|
|
325
269
|
</View>
|
|
326
270
|
<View
|
|
@@ -334,96 +278,87 @@ export function PlayerControlsWeb(props: PlayerControlsWebProps) {
|
|
|
334
278
|
|
|
335
279
|
<Text style={styles.time}>{fmt(duration)}</Text>
|
|
336
280
|
|
|
337
|
-
{/*
|
|
338
|
-
<
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
>
|
|
344
|
-
|
|
345
|
-
name="settings-outline"
|
|
346
|
-
size={17}
|
|
347
|
-
color={settingsOpen ? "#7dd3fc" : "#fff"}
|
|
348
|
-
/>
|
|
349
|
-
</TouchableOpacity>
|
|
350
|
-
|
|
351
|
-
{settingsOpen ? (
|
|
352
|
-
<View style={styles.settingsMenu}>
|
|
353
|
-
<Text style={styles.settingsTitle}>Settings</Text>
|
|
354
|
-
|
|
355
|
-
{/* ---- Subtitles ---- */}
|
|
356
|
-
{showSubsSection ? (
|
|
357
|
-
<View style={styles.section}>
|
|
358
|
-
<View style={styles.sectionHead}>
|
|
359
|
-
<Ionicons
|
|
360
|
-
name="chatbox-ellipses-outline"
|
|
361
|
-
size={15}
|
|
362
|
-
color="#9aa0aa"
|
|
363
|
-
/>
|
|
364
|
-
<Text style={styles.sectionLabel}>Subtitles</Text>
|
|
365
|
-
<TouchableOpacity
|
|
366
|
-
onPress={onToggleSubtitles}
|
|
367
|
-
hitSlop={6}
|
|
368
|
-
style={{ marginLeft: "auto" }}
|
|
369
|
-
>
|
|
370
|
-
<View
|
|
371
|
-
style={[
|
|
372
|
-
styles.switchTrack,
|
|
373
|
-
subtitlesOn && styles.switchTrackOn,
|
|
374
|
-
]}
|
|
375
|
-
>
|
|
376
|
-
<View
|
|
377
|
-
style={[
|
|
378
|
-
styles.switchThumb,
|
|
379
|
-
subtitlesOn && styles.switchThumbOn,
|
|
380
|
-
]}
|
|
381
|
-
/>
|
|
382
|
-
</View>
|
|
383
|
-
</TouchableOpacity>
|
|
384
|
-
</View>
|
|
281
|
+
{/* CC toggle */}
|
|
282
|
+
<TouchableOpacity
|
|
283
|
+
style={[styles.ccBtn, subtitlesOn && styles.ccBtnOn]}
|
|
284
|
+
onPress={onToggleSubtitles}
|
|
285
|
+
hitSlop={6}
|
|
286
|
+
>
|
|
287
|
+
<Text style={[styles.ccText, subtitlesOn && styles.ccTextOn]}>CC</Text>
|
|
288
|
+
</TouchableOpacity>
|
|
385
289
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
290
|
+
{/* subtitle selector (shown with at least ONE track so the Text Size
|
|
291
|
+
slider is reachable). Tap to open a dropdown with two tabs:
|
|
292
|
+
language list | text size slider. */}
|
|
293
|
+
{hasSubs ? (
|
|
294
|
+
<View style={styles.subWrap}>
|
|
295
|
+
<TouchableOpacity
|
|
296
|
+
style={[styles.langBtn, subtitlesOn && styles.langBtnOn]}
|
|
297
|
+
onPress={() => setSubOpen((v) => !v)}
|
|
298
|
+
hitSlop={6}
|
|
299
|
+
>
|
|
300
|
+
<Text style={[styles.ccText, subtitlesOn && styles.ccTextOn]}>
|
|
301
|
+
{subLang}
|
|
302
|
+
</Text>
|
|
303
|
+
<Ionicons
|
|
304
|
+
name={subOpen ? "chevron-up" : "chevron-down"}
|
|
305
|
+
size={12}
|
|
306
|
+
color={subtitlesOn ? "#fff" : "#9aa0aa"}
|
|
307
|
+
/>
|
|
308
|
+
</TouchableOpacity>
|
|
309
|
+
{subOpen ? (
|
|
310
|
+
<View style={styles.subMenu}>
|
|
311
|
+
<View style={styles.subTabs}>
|
|
312
|
+
<TouchableOpacity
|
|
313
|
+
style={[styles.subTab, subTab === "lang" && styles.subTabOn]}
|
|
314
|
+
onPress={() => setSubTab("lang")}
|
|
315
|
+
>
|
|
316
|
+
<Text
|
|
317
|
+
style={[styles.subTabText, subTab === "lang" && styles.subTabTextOn]}
|
|
318
|
+
>
|
|
319
|
+
Language
|
|
320
|
+
</Text>
|
|
321
|
+
</TouchableOpacity>
|
|
322
|
+
<TouchableOpacity
|
|
323
|
+
style={[styles.subTab, subTab === "size" && styles.subTabOn]}
|
|
324
|
+
onPress={() => setSubTab("size")}
|
|
325
|
+
>
|
|
326
|
+
<Text
|
|
327
|
+
style={[styles.subTabText, subTab === "size" && styles.subTabTextOn]}
|
|
328
|
+
>
|
|
329
|
+
Text Size
|
|
330
|
+
</Text>
|
|
331
|
+
</TouchableOpacity>
|
|
332
|
+
</View>
|
|
422
333
|
|
|
423
|
-
|
|
424
|
-
|
|
334
|
+
{subTab === "lang" ? (
|
|
335
|
+
<View style={styles.subBody}>
|
|
336
|
+
{subtitleTracks?.map((lang) => {
|
|
337
|
+
const active = lang === subtitleTrack;
|
|
338
|
+
return (
|
|
339
|
+
<TouchableOpacity
|
|
340
|
+
key={lang}
|
|
341
|
+
style={[styles.subItem, active && styles.subItemOn]}
|
|
342
|
+
onPress={() => {
|
|
343
|
+
setSubOpen(false);
|
|
344
|
+
onSelectSubtitleTrack?.(lang);
|
|
345
|
+
}}
|
|
346
|
+
>
|
|
347
|
+
<Text style={[styles.subItemText, active && styles.subItemTextOn]}>
|
|
348
|
+
{lang}
|
|
349
|
+
</Text>
|
|
350
|
+
{active ? (
|
|
351
|
+
<Ionicons name="checkmark" size={14} color="#fff" />
|
|
352
|
+
) : null}
|
|
353
|
+
</TouchableOpacity>
|
|
354
|
+
);
|
|
355
|
+
})}
|
|
356
|
+
</View>
|
|
357
|
+
) : (
|
|
358
|
+
<View style={styles.subBody}>
|
|
359
|
+
<View style={styles.sizeRow}>
|
|
425
360
|
<TouchableOpacity
|
|
426
|
-
style={styles.
|
|
361
|
+
style={styles.sizeBtn}
|
|
427
362
|
onPress={() =>
|
|
428
363
|
onChangeSubtitleFontSize?.(
|
|
429
364
|
Math.max(
|
|
@@ -437,22 +372,18 @@ export function PlayerControlsWeb(props: PlayerControlsWebProps) {
|
|
|
437
372
|
</TouchableOpacity>
|
|
438
373
|
|
|
439
374
|
<View
|
|
440
|
-
style={
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
}
|
|
375
|
+
style={[
|
|
376
|
+
styles.sizeTrack,
|
|
377
|
+
{ opacity: onChangeSubtitleFontSize ? 1 : 0.5 },
|
|
378
|
+
]}
|
|
379
|
+
onLayout={(e) => setSizeW(e.nativeEvent.layout.width)}
|
|
444
380
|
{...sizePan.panHandlers}
|
|
445
381
|
>
|
|
446
|
-
<View
|
|
447
|
-
style={[
|
|
448
|
-
styles.sliderFill,
|
|
449
|
-
{ width: `${sizePct * 100}%` },
|
|
450
|
-
]}
|
|
451
|
-
/>
|
|
382
|
+
<View style={[styles.sizeFill, { width: `${sizePct * 100}%` }]} />
|
|
452
383
|
</View>
|
|
453
384
|
|
|
454
385
|
<TouchableOpacity
|
|
455
|
-
style={styles.
|
|
386
|
+
style={styles.sizeBtn}
|
|
456
387
|
onPress={() =>
|
|
457
388
|
onChangeSubtitleFontSize?.(
|
|
458
389
|
Math.min(
|
|
@@ -464,116 +395,36 @@ export function PlayerControlsWeb(props: PlayerControlsWebProps) {
|
|
|
464
395
|
>
|
|
465
396
|
<Ionicons name="add" size={16} color="#fff" />
|
|
466
397
|
</TouchableOpacity>
|
|
467
|
-
<Text style={styles.sliderValue}>
|
|
468
|
-
{currentSize}px
|
|
469
|
-
</Text>
|
|
470
398
|
</View>
|
|
471
|
-
|
|
472
|
-
</View>
|
|
473
|
-
) : null}
|
|
474
|
-
|
|
475
|
-
{/* ---- Volume ---- */}
|
|
476
|
-
<View style={styles.section}>
|
|
477
|
-
<View style={styles.sectionHead}>
|
|
478
|
-
<Ionicons
|
|
479
|
-
name="volume-medium-outline"
|
|
480
|
-
size={15}
|
|
481
|
-
color="#9aa0aa"
|
|
482
|
-
/>
|
|
483
|
-
<Text style={styles.sectionLabel}>Volume</Text>
|
|
484
|
-
<TouchableOpacity
|
|
485
|
-
onPress={onToggleMute}
|
|
486
|
-
hitSlop={6}
|
|
487
|
-
style={{ marginLeft: "auto" }}
|
|
488
|
-
>
|
|
489
|
-
<Ionicons
|
|
490
|
-
name={volIcon as any}
|
|
491
|
-
size={17}
|
|
492
|
-
color={muted ? "#e50914" : "#fff"}
|
|
493
|
-
/>
|
|
494
|
-
</TouchableOpacity>
|
|
495
|
-
<Text style={styles.sliderValue}>
|
|
496
|
-
{Math.round(volPct * 100)}
|
|
497
|
-
</Text>
|
|
498
|
-
</View>
|
|
499
|
-
<View style={styles.sliderRow}>
|
|
500
|
-
<View
|
|
501
|
-
style={styles.sliderTrack}
|
|
502
|
-
onLayout={(e) => setVolW(e.nativeEvent.layout.width)}
|
|
503
|
-
{...volPan.panHandlers}
|
|
504
|
-
>
|
|
505
|
-
<View
|
|
506
|
-
style={[
|
|
507
|
-
styles.sliderFill,
|
|
508
|
-
{ width: `${volPct * 100}%` },
|
|
509
|
-
]}
|
|
510
|
-
/>
|
|
399
|
+
<Text style={styles.sizeLabel}>{currentSize}px</Text>
|
|
511
400
|
</View>
|
|
512
|
-
|
|
401
|
+
)}
|
|
513
402
|
</View>
|
|
403
|
+
) : null}
|
|
404
|
+
</View>
|
|
405
|
+
) : null}
|
|
514
406
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
{resolutions?.map((label) => {
|
|
528
|
-
const active = label === activeRes;
|
|
529
|
-
return (
|
|
530
|
-
<TouchableOpacity
|
|
531
|
-
key={label}
|
|
532
|
-
style={[
|
|
533
|
-
styles.menuItem,
|
|
534
|
-
active && styles.menuItemOn,
|
|
535
|
-
]}
|
|
536
|
-
onPress={() => {
|
|
537
|
-
setSettingsOpen(false);
|
|
538
|
-
onSelectResolution?.(label);
|
|
539
|
-
}}
|
|
540
|
-
>
|
|
541
|
-
<Text
|
|
542
|
-
style={[
|
|
543
|
-
styles.menuItemText,
|
|
544
|
-
active && styles.menuItemTextOn,
|
|
545
|
-
]}
|
|
546
|
-
>
|
|
547
|
-
{label}
|
|
548
|
-
</Text>
|
|
549
|
-
{active ? (
|
|
550
|
-
<Ionicons
|
|
551
|
-
name="checkmark"
|
|
552
|
-
size={14}
|
|
553
|
-
color="#fff"
|
|
554
|
-
/>
|
|
555
|
-
) : null}
|
|
556
|
-
</TouchableOpacity>
|
|
557
|
-
);
|
|
558
|
-
})}
|
|
559
|
-
</View>
|
|
560
|
-
</View>
|
|
561
|
-
) : null}
|
|
562
|
-
</View>
|
|
563
|
-
) : null}
|
|
407
|
+
{/* volume: icon tap = mute, slider drag = level */}
|
|
408
|
+
<TouchableOpacity onPress={onToggleMute} hitSlop={6}>
|
|
409
|
+
<Ionicons name={volIcon as any} size={17} color="#fff" />
|
|
410
|
+
</TouchableOpacity>
|
|
411
|
+
<View
|
|
412
|
+
style={styles.volTrackWrap}
|
|
413
|
+
onLayout={(e) => setVolW(e.nativeEvent.layout.width)}
|
|
414
|
+
{...volPan.panHandlers}
|
|
415
|
+
>
|
|
416
|
+
<View style={styles.volTrack}>
|
|
417
|
+
<View style={[styles.volFill, { width: `${volPct * 100}%` }]} />
|
|
418
|
+
</View>
|
|
564
419
|
</View>
|
|
565
420
|
|
|
566
421
|
<TouchableOpacity onPress={onFullscreen} hitSlop={8}>
|
|
567
422
|
<Ionicons name="expand" size={18} color="#fff" />
|
|
568
423
|
</TouchableOpacity>
|
|
569
424
|
</View>
|
|
570
|
-
|
|
425
|
+
</>
|
|
571
426
|
)}
|
|
572
|
-
|
|
573
|
-
cue={cue}
|
|
574
|
-
enabled={enabled}
|
|
575
|
-
fontSize={fontSize}
|
|
576
|
-
/>
|
|
427
|
+
|
|
577
428
|
{visible && inIntro && (
|
|
578
429
|
<TouchableOpacity style={styles.skipRegionBtn} onPress={onSkipIntro}>
|
|
579
430
|
<Text style={styles.skipRegionText}>Skip Intro ›</Text>
|
|
@@ -589,9 +440,10 @@ export function PlayerControlsWeb(props: PlayerControlsWebProps) {
|
|
|
589
440
|
}
|
|
590
441
|
|
|
591
442
|
const styles = StyleSheet.create({
|
|
592
|
-
root: { zIndex: 2 },
|
|
443
|
+
root: { ...StyleSheet.absoluteFillObject, zIndex: 2 },
|
|
593
444
|
|
|
594
445
|
centerBadge: {
|
|
446
|
+
...StyleSheet.absoluteFillObject,
|
|
595
447
|
justifyContent: "center",
|
|
596
448
|
alignItems: "center",
|
|
597
449
|
width: "100%",
|
|
@@ -614,6 +466,7 @@ const styles = StyleSheet.create({
|
|
|
614
466
|
title: { color: "#fff", fontWeight: "700", fontSize: 13, flex: 1 },
|
|
615
467
|
|
|
616
468
|
centerRow: {
|
|
469
|
+
...StyleSheet.absoluteFillObject,
|
|
617
470
|
justifyContent: "center",
|
|
618
471
|
alignItems: "center",
|
|
619
472
|
flexDirection: "row",
|
|
@@ -688,80 +541,71 @@ const styles = StyleSheet.create({
|
|
|
688
541
|
},
|
|
689
542
|
thumbIdle: { opacity: 0.85 },
|
|
690
543
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
544
|
+
ccBtn: {
|
|
545
|
+
borderWidth: 1.5,
|
|
546
|
+
borderColor: "#666",
|
|
694
547
|
borderRadius: 4,
|
|
695
|
-
|
|
696
|
-
|
|
548
|
+
paddingHorizontal: 5,
|
|
549
|
+
paddingVertical: 1,
|
|
550
|
+
},
|
|
551
|
+
ccBtnOn: { borderColor: "#e50914", backgroundColor: "rgba(229,9,20,0.25)" },
|
|
552
|
+
ccText: { color: "#888", fontSize: 10, fontWeight: "900" },
|
|
553
|
+
ccTextOn: { color: "#fff" },
|
|
554
|
+
langBtn: {
|
|
555
|
+
borderWidth: 1.5,
|
|
556
|
+
borderColor: "#555",
|
|
557
|
+
borderRadius: 4,
|
|
558
|
+
paddingHorizontal: 6,
|
|
559
|
+
paddingVertical: 1,
|
|
560
|
+
},
|
|
561
|
+
langBtnOn: {
|
|
562
|
+
borderColor: "#7dd3fc",
|
|
563
|
+
backgroundColor: "rgba(125,211,252,0.18)",
|
|
697
564
|
},
|
|
698
|
-
iconBtnOn: { backgroundColor: "rgba(125,211,252,0.18)" },
|
|
699
565
|
|
|
700
|
-
|
|
701
|
-
|
|
566
|
+
subWrap: { position: "relative", zIndex: 20 },
|
|
567
|
+
subMenu: {
|
|
702
568
|
position: "absolute",
|
|
703
|
-
bottom:
|
|
569
|
+
bottom: 26,
|
|
704
570
|
right: 0,
|
|
705
571
|
zIndex: 30,
|
|
706
|
-
width:
|
|
707
|
-
maxHeight: 420,
|
|
572
|
+
width: 190,
|
|
708
573
|
backgroundColor: "#14161c",
|
|
709
574
|
borderWidth: 1,
|
|
710
575
|
borderColor: "#2a2f3a",
|
|
711
576
|
borderRadius: 8,
|
|
712
577
|
overflow: "hidden",
|
|
713
|
-
paddingBottom: 6,
|
|
714
578
|
elevation: 12,
|
|
715
579
|
shadowColor: "#000",
|
|
716
580
|
shadowOpacity: 0.5,
|
|
717
581
|
shadowRadius: 10,
|
|
718
582
|
shadowOffset: { width: 0, height: 3 },
|
|
719
583
|
},
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
paddingTop: 10,
|
|
728
|
-
paddingBottom: 6,
|
|
729
|
-
borderBottomWidth: 1,
|
|
730
|
-
borderBottomColor: "#262a34",
|
|
731
|
-
},
|
|
732
|
-
|
|
733
|
-
section: { paddingHorizontal: 6, paddingTop: 8 },
|
|
734
|
-
sectionHead: {
|
|
735
|
-
flexDirection: "row",
|
|
736
|
-
alignItems: "center",
|
|
737
|
-
gap: 6,
|
|
738
|
-
paddingHorizontal: 6,
|
|
739
|
-
paddingBottom: 2,
|
|
740
|
-
},
|
|
741
|
-
sectionLabel: { color: "#cbd5e1", fontSize: 12, fontWeight: "700" },
|
|
742
|
-
|
|
743
|
-
menuList: { paddingVertical: 4 },
|
|
744
|
-
menuItem: {
|
|
584
|
+
subTabs: { flexDirection: "row", borderBottomWidth: 1, borderBottomColor: "#262a34" },
|
|
585
|
+
subTab: { flex: 1, alignItems: "center", paddingVertical: 7, backgroundColor: "#1a1d24" },
|
|
586
|
+
subTabOn: { backgroundColor: "#232833" },
|
|
587
|
+
subTabText: { color: "#9aa0aa", fontSize: 12, fontWeight: "700" },
|
|
588
|
+
subTabTextOn: { color: "#7dd3fc", fontWeight: "800" },
|
|
589
|
+
subBody: { paddingVertical: 4 },
|
|
590
|
+
subItem: {
|
|
745
591
|
flexDirection: "row",
|
|
746
592
|
alignItems: "center",
|
|
747
593
|
justifyContent: "space-between",
|
|
748
594
|
gap: 10,
|
|
749
|
-
paddingHorizontal:
|
|
750
|
-
paddingVertical:
|
|
751
|
-
borderRadius: 6,
|
|
595
|
+
paddingHorizontal: 12,
|
|
596
|
+
paddingVertical: 7,
|
|
752
597
|
},
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
sliderRow: {
|
|
598
|
+
subItemOn: { backgroundColor: "rgba(125,211,252,0.15)" },
|
|
599
|
+
subItemText: { color: "#cbd5e1", fontSize: 13, fontWeight: "600" },
|
|
600
|
+
subItemTextOn: { color: "#7dd3fc", fontWeight: "800" },
|
|
601
|
+
sizeRow: {
|
|
758
602
|
flexDirection: "row",
|
|
759
603
|
alignItems: "center",
|
|
760
604
|
gap: 10,
|
|
761
605
|
paddingHorizontal: 10,
|
|
762
|
-
paddingVertical:
|
|
606
|
+
paddingVertical: 10,
|
|
763
607
|
},
|
|
764
|
-
|
|
608
|
+
sizeBtn: {
|
|
765
609
|
width: 24,
|
|
766
610
|
height: 24,
|
|
767
611
|
borderRadius: 4,
|
|
@@ -769,30 +613,17 @@ const styles = StyleSheet.create({
|
|
|
769
613
|
justifyContent: "center",
|
|
770
614
|
backgroundColor: "#2a2f3a",
|
|
771
615
|
},
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
616
|
+
sizeTrack: { flex: 1, height: 22, justifyContent: "center" },
|
|
617
|
+
sizeFill: { height: 4, borderRadius: 2, backgroundColor: "#7dd3fc" },
|
|
618
|
+
sizeLabel: {
|
|
775
619
|
color: "#9aa0aa",
|
|
776
620
|
fontSize: 11,
|
|
777
621
|
fontWeight: "700",
|
|
778
|
-
|
|
779
|
-
|
|
622
|
+
textAlign: "center",
|
|
623
|
+
paddingBottom: 6,
|
|
780
624
|
},
|
|
781
625
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
backgroundColor: "#3a3f47",
|
|
787
|
-
justifyContent: "center",
|
|
788
|
-
paddingHorizontal: 2,
|
|
789
|
-
},
|
|
790
|
-
switchTrackOn: { backgroundColor: "rgba(229,9,20,0.85)" },
|
|
791
|
-
switchThumb: {
|
|
792
|
-
width: 16,
|
|
793
|
-
height: 16,
|
|
794
|
-
borderRadius: 8,
|
|
795
|
-
backgroundColor: "#fff",
|
|
796
|
-
},
|
|
797
|
-
switchThumbOn: { alignSelf: "flex-end" },
|
|
798
|
-
});
|
|
626
|
+
volTrackWrap: { width: 56, paddingVertical: 12, justifyContent: "center" },
|
|
627
|
+
volTrack: { height: 4, borderRadius: 2, backgroundColor: "#3a3f47", overflow: "hidden" },
|
|
628
|
+
volFill: { height: 4, backgroundColor: "#fff" },
|
|
629
|
+
});
|