cursedbelt 5.102.0 → 5.103.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/dist/react/engagement/index.d.ts +1 -0
- package/dist/react/engagement/index.d.ts.map +1 -1
- package/dist/react/engagement/index.js +3 -1
- package/dist/react/engagement/index.js.map +1 -1
- package/dist/react/engagement/playbackBeacon.d.ts +3 -0
- package/dist/react/engagement/playbackBeacon.d.ts.map +1 -0
- package/dist/react/engagement/playbackBeacon.js +27 -0
- package/dist/react/engagement/playbackBeacon.js.map +1 -0
- package/dist/react/media/MediaLightbox.js +1 -1
- package/dist/react/media/MediaLightbox.js.map +1 -1
- package/dist/react/media/VideoPlayer.d.ts +3 -1
- package/dist/react/media/VideoPlayer.d.ts.map +1 -1
- package/dist/react/media/VideoPlayer.js +62 -14
- package/dist/react/media/VideoPlayer.js.map +1 -1
- package/dist/react/media/VideoSlideContent.d.ts +12 -7
- package/dist/react/media/VideoSlideContent.d.ts.map +1 -1
- package/dist/react/media/VideoSlideContent.js +19 -9
- package/dist/react/media/VideoSlideContent.js.map +1 -1
- package/dist/react/media/mediaPhoto.d.ts +3 -0
- package/dist/react/media/mediaPhoto.d.ts.map +1 -1
- package/dist/react/media/mediaPhoto.js.map +1 -1
- package/dist/react/media/playbackWatch.d.ts +83 -0
- package/dist/react/media/playbackWatch.d.ts.map +1 -0
- package/dist/react/media/playbackWatch.js +167 -0
- package/dist/react/media/playbackWatch.js.map +1 -0
- package/dist/react/media/playerSource.d.ts +24 -0
- package/dist/react/media/playerSource.d.ts.map +1 -0
- package/dist/react/media/playerSource.js +22 -0
- package/dist/react/media/playerSource.js.map +1 -0
- package/dist/react/media/sourceFault.d.ts +5 -59
- package/dist/react/media/sourceFault.d.ts.map +1 -1
- package/dist/react/media/sourceFault.js +69 -2
- package/dist/react/media/sourceFault.js.map +1 -1
- package/dist/react/media/usePlaybackWatch.d.ts +14 -0
- package/dist/react/media/usePlaybackWatch.d.ts.map +1 -0
- package/dist/react/media/usePlaybackWatch.js +32 -0
- package/dist/react/media/usePlaybackWatch.js.map +1 -0
- package/dist/react/media-gallery/MediaGallery.d.ts.map +1 -1
- package/dist/react/media-gallery/MediaGallery.js +21 -5
- package/dist/react/media-gallery/MediaGallery.js.map +1 -1
- package/package.json +2 -2
- package/src/react/engagement/index.ts +3 -1
- package/src/react/engagement/playbackBeacon.ts +27 -0
- package/src/react/media/MediaLightbox.tsx +2 -2
- package/src/react/media/VideoPlayer.spec.tsx +24 -0
- package/src/react/media/VideoPlayer.tsx +72 -12
- package/src/react/media/VideoSlideContent.spec.tsx +28 -0
- package/src/react/media/VideoSlideContent.tsx +49 -32
- package/src/react/media/mediaPhoto.ts +3 -0
- package/src/react/media/playbackWatch.spec.ts +196 -0
- package/src/react/media/playbackWatch.ts +218 -0
- package/src/react/media/playerSource.ts +30 -0
- package/src/react/media/sourceFault.spec.tsx +12 -0
- package/src/react/media/sourceFault.tsx +25 -1
- package/src/react/media/usePlaybackWatch.tsx +34 -0
- package/src/react/media-gallery/MediaGallery.tsx +32 -13
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How long a video took to start, and what went wrong if it did not — measured in the browser,
|
|
3
|
+
* because nothing else can see it.
|
|
4
|
+
*
|
|
5
|
+
* The owner, 2026-10-30: *"since a bug can make it load forever it has to proactively send metrics
|
|
6
|
+
* when the delay is taking longer than expected for a video."* A report sent when loading FINISHES
|
|
7
|
+
* can never describe the worst case, so the clock is the point: from the moment a video is asked
|
|
8
|
+
* for, {@link SLOW_CHECKPOINTS_MS} report it as still `waiting`, however the wait ends. Closing the
|
|
9
|
+
* page or the player mid-wait reports `abandoned` — a person giving up on a spinner, which is the
|
|
10
|
+
* signature of the bug nobody tells you about.
|
|
11
|
+
*
|
|
12
|
+
* One open is one {@link PlaybackReport#id}, so a sink can keep one row per open and let each report
|
|
13
|
+
* replace the last. A fast load costs exactly one report.
|
|
14
|
+
*
|
|
15
|
+
* Nothing runs until an app installs a reporter ({@link setPlaybackReporter}). The seam is the
|
|
16
|
+
* player itself, so every surface and every app gets it from one line at boot rather than a prop
|
|
17
|
+
* threaded through each gallery. Failure is silence, as in `engagement/beaconClient.ts`.
|
|
18
|
+
*
|
|
19
|
+
* Pure of the DOM on purpose: the clock and the timers are injected, so a spec can state a stuck
|
|
20
|
+
* load and read the reports without waiting for one.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* When a wait stops being normal. The first is what "longer than expected" means: a warm start
|
|
24
|
+
* takes well under a second and a cold ladder segment about two (`cwip/edge-warm`'s measurements).
|
|
25
|
+
* The list ends so that a tab left on a spinner overnight does not report all night.
|
|
26
|
+
*/
|
|
27
|
+
export const SLOW_CHECKPOINTS_MS = [5_000, 15_000, 30_000, 60_000, 120_000, 300_000, 600_000];
|
|
28
|
+
/** A stall this long is reported as it happens rather than when the player closes. */
|
|
29
|
+
export const SLOW_STALL_MS = 5_000;
|
|
30
|
+
/** Stepping past a video in under this is not a person giving up on it. */
|
|
31
|
+
const MIN_ABANDONED_MS = 1_500;
|
|
32
|
+
let reporter = null;
|
|
33
|
+
/** Install the sink — once, at boot. Null removes it. */
|
|
34
|
+
export function setPlaybackReporter(next) {
|
|
35
|
+
reporter = next;
|
|
36
|
+
}
|
|
37
|
+
export function createPlaybackWatch(input, deps = {}) {
|
|
38
|
+
const now = deps.now ?? (() => Date.now());
|
|
39
|
+
const setTimer = deps.setTimer ?? ((run, ms) => setTimeout(run, ms));
|
|
40
|
+
const clearTimer = deps.clearTimer ?? ((timer) => clearTimeout(timer));
|
|
41
|
+
const isHidden = deps.isHidden ?? (() => typeof document !== 'undefined' && document.visibilityState === 'hidden');
|
|
42
|
+
const id = deps.newId?.() ?? crypto.randomUUID();
|
|
43
|
+
let state = 'waiting';
|
|
44
|
+
let source = 'none';
|
|
45
|
+
let startedAt = null;
|
|
46
|
+
let readyMs;
|
|
47
|
+
let stalls = 0;
|
|
48
|
+
let stallMs = 0;
|
|
49
|
+
let stallBegan = null;
|
|
50
|
+
let error;
|
|
51
|
+
let hidden = false;
|
|
52
|
+
let timer = null;
|
|
53
|
+
let over = false;
|
|
54
|
+
const clear = () => {
|
|
55
|
+
if (timer !== null)
|
|
56
|
+
clearTimer(timer);
|
|
57
|
+
timer = null;
|
|
58
|
+
};
|
|
59
|
+
const send = (leaving = false) => {
|
|
60
|
+
if (startedAt === null)
|
|
61
|
+
return;
|
|
62
|
+
const at = now();
|
|
63
|
+
const report = {
|
|
64
|
+
id,
|
|
65
|
+
itemId: input.itemId,
|
|
66
|
+
surface: input.surface,
|
|
67
|
+
source,
|
|
68
|
+
state,
|
|
69
|
+
waitedMs: readyMs ?? at - startedAt,
|
|
70
|
+
...(readyMs !== undefined ? { readyMs } : {}),
|
|
71
|
+
stalls,
|
|
72
|
+
stallMs: stallMs + (stallBegan === null ? 0 : at - stallBegan),
|
|
73
|
+
...(error ? { error } : {}),
|
|
74
|
+
hidden: hidden || isHidden(),
|
|
75
|
+
};
|
|
76
|
+
try {
|
|
77
|
+
reporter?.(report, leaving);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Telemetry never becomes the viewer's problem.
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const nextCheckpoint = (index) => {
|
|
84
|
+
const at = SLOW_CHECKPOINTS_MS[index];
|
|
85
|
+
if (at === undefined || startedAt === null)
|
|
86
|
+
return;
|
|
87
|
+
timer = setTimer(() => {
|
|
88
|
+
timer = null;
|
|
89
|
+
if (state !== 'waiting')
|
|
90
|
+
return;
|
|
91
|
+
hidden ||= isHidden();
|
|
92
|
+
send();
|
|
93
|
+
nextCheckpoint(index + 1);
|
|
94
|
+
}, Math.max(0, at - (now() - startedAt)));
|
|
95
|
+
};
|
|
96
|
+
return {
|
|
97
|
+
itemId: input.itemId,
|
|
98
|
+
start() {
|
|
99
|
+
if (startedAt !== null || over || !reporter)
|
|
100
|
+
return;
|
|
101
|
+
startedAt = now();
|
|
102
|
+
nextCheckpoint(0);
|
|
103
|
+
},
|
|
104
|
+
source(next) {
|
|
105
|
+
source = next;
|
|
106
|
+
},
|
|
107
|
+
ready() {
|
|
108
|
+
if (over || startedAt === null || readyMs !== undefined)
|
|
109
|
+
return;
|
|
110
|
+
clear();
|
|
111
|
+
readyMs = now() - startedAt;
|
|
112
|
+
state = 'ready';
|
|
113
|
+
send();
|
|
114
|
+
},
|
|
115
|
+
stalled() {
|
|
116
|
+
if (over || readyMs === undefined || stallBegan !== null)
|
|
117
|
+
return;
|
|
118
|
+
stallBegan = now();
|
|
119
|
+
stalls += 1;
|
|
120
|
+
timer = setTimer(() => {
|
|
121
|
+
timer = null;
|
|
122
|
+
state = 'stalled';
|
|
123
|
+
send();
|
|
124
|
+
}, SLOW_STALL_MS);
|
|
125
|
+
},
|
|
126
|
+
resumed() {
|
|
127
|
+
if (stallBegan === null)
|
|
128
|
+
return;
|
|
129
|
+
stallMs += now() - stallBegan;
|
|
130
|
+
stallBegan = null;
|
|
131
|
+
clear();
|
|
132
|
+
state = 'ready';
|
|
133
|
+
},
|
|
134
|
+
note(next) {
|
|
135
|
+
error = next;
|
|
136
|
+
},
|
|
137
|
+
failed(next) {
|
|
138
|
+
if (over || state === 'failed')
|
|
139
|
+
return;
|
|
140
|
+
clear();
|
|
141
|
+
error = next;
|
|
142
|
+
state = 'failed';
|
|
143
|
+
send();
|
|
144
|
+
},
|
|
145
|
+
cancel() {
|
|
146
|
+
over = true;
|
|
147
|
+
clear();
|
|
148
|
+
},
|
|
149
|
+
close() {
|
|
150
|
+
if (over)
|
|
151
|
+
return;
|
|
152
|
+
over = true;
|
|
153
|
+
clear();
|
|
154
|
+
if (startedAt === null)
|
|
155
|
+
return;
|
|
156
|
+
if (state === 'waiting') {
|
|
157
|
+
if (now() - startedAt < MIN_ABANDONED_MS)
|
|
158
|
+
return;
|
|
159
|
+
state = 'abandoned';
|
|
160
|
+
send(true);
|
|
161
|
+
}
|
|
162
|
+
else if (state !== 'failed' && stalls > 0)
|
|
163
|
+
send(true);
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=playbackWatch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playbackWatch.js","sourceRoot":"","sources":["../../../src/react/media/playbackWatch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { PlayerVideo } from './VideoPlayer.js';
|
|
2
|
+
/**
|
|
3
|
+
* What the element is handed, given what the sources have answered.
|
|
4
|
+
*
|
|
5
|
+
* 🔴 "The copy that always exists" stopped being true when a film's progressive copies began to be
|
|
6
|
+
* released against its ladder (collections, forge task 3228). With no `streamUrl`, handing over
|
|
7
|
+
* from the ladder is handing over to NOTHING: no source, no element, no event, a spinner for ever
|
|
8
|
+
* (forge task 4062). So `progressive` is absent for a ladder alone, and its failure is the fault.
|
|
9
|
+
*
|
|
10
|
+
* 🔴 The type is STATED, never left for Vidstack to work out. A URL with no extension is read as
|
|
11
|
+
* unknown, and Vidstack then asks it with a HEAD and trusts the Content-Type. A service worker that
|
|
12
|
+
* decrypts media answers only GETs, so that HEAD reached the store and came back
|
|
13
|
+
* `application/x-collections-cipher`, which no loader plays: `error` code 4 on a perfectly playable
|
|
14
|
+
* video (forge task 4062). The element sniffs the real container from the bytes either way.
|
|
15
|
+
*/
|
|
16
|
+
export declare function playerSource(video: Pick<PlayerVideo, 'hlsUrl' | 'streamUrl'>, ladderAnswered: boolean): {
|
|
17
|
+
src?: {
|
|
18
|
+
src: string;
|
|
19
|
+
type: 'application/x-mpegurl' | 'video/mp4';
|
|
20
|
+
};
|
|
21
|
+
onLadder: boolean;
|
|
22
|
+
progressive?: string;
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=playerSource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playerSource.d.ts","sourceRoot":"","sources":["../../../src/react/media/playerSource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,GAAG,WAAW,CAAC,EAChD,cAAc,EAAE,OAAO,GACtB;IACD,GAAG,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,uBAAuB,GAAG,WAAW,CAAA;KAAE,CAAC;IACnE,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAMA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the element is handed, given what the sources have answered.
|
|
3
|
+
*
|
|
4
|
+
* 🔴 "The copy that always exists" stopped being true when a film's progressive copies began to be
|
|
5
|
+
* released against its ladder (collections, forge task 3228). With no `streamUrl`, handing over
|
|
6
|
+
* from the ladder is handing over to NOTHING: no source, no element, no event, a spinner for ever
|
|
7
|
+
* (forge task 4062). So `progressive` is absent for a ladder alone, and its failure is the fault.
|
|
8
|
+
*
|
|
9
|
+
* 🔴 The type is STATED, never left for Vidstack to work out. A URL with no extension is read as
|
|
10
|
+
* unknown, and Vidstack then asks it with a HEAD and trusts the Content-Type. A service worker that
|
|
11
|
+
* decrypts media answers only GETs, so that HEAD reached the store and came back
|
|
12
|
+
* `application/x-collections-cipher`, which no loader plays: `error` code 4 on a perfectly playable
|
|
13
|
+
* video (forge task 4062). The element sniffs the real container from the bytes either way.
|
|
14
|
+
*/
|
|
15
|
+
export function playerSource(video, ladderAnswered) {
|
|
16
|
+
const progressive = video.streamUrl || undefined;
|
|
17
|
+
const fallback = progressive ? { progressive } : {};
|
|
18
|
+
if (video.hlsUrl && !ladderAnswered)
|
|
19
|
+
return { src: { src: video.hlsUrl, type: 'application/x-mpegurl' }, onLadder: true, ...fallback };
|
|
20
|
+
return { ...(progressive ? { src: { src: progressive, type: 'video/mp4' } } : {}), onLadder: false, ...fallback };
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=playerSource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playerSource.js","sourceRoot":"","sources":["../../../src/react/media/playerSource.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAgD,EAChD,cAAuB;IAMvB,MAAM,WAAW,GAAG,KAAK,CAAC,SAAS,IAAI,SAAS,CAAC;IACjD,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACpD,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,cAAc;QACjC,OAAO,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,uBAAuB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,QAAQ,EAAE,CAAC;IACpG,OAAO,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,CAAC;AACpH,CAAC"}
|
|
@@ -1,61 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* What a media URL ANSWERED, when what it answered was not the bytes.
|
|
3
|
-
*
|
|
4
|
-
* ── 🔴 The report this exists for ───────────────────────────────────────────────────────
|
|
5
|
-
* Owner, 2026-09-17: *"make a 404 stop the media player spinner so I don't think it's going
|
|
6
|
-
* to eventually load."* One item in his catalog has no ladder, so the very first request a
|
|
7
|
-
* player makes is answered honestly and immediately:
|
|
8
|
-
*
|
|
9
|
-
* GET …/u_fd668e6a….hls/master.m3u8 → 404 {"error":"not_found"} in 0.2 s
|
|
10
|
-
*
|
|
11
|
-
* and the spinner then ran until he gave up. **A 404 is a COMPLETE ANSWER** — there is
|
|
12
|
-
* nothing still in flight, nothing to wait for, and no amount of waiting changes it — and it
|
|
13
|
-
* was the one status every surface here treated as "still loading".
|
|
14
|
-
*
|
|
15
|
-
* ── 🔴 The distinction this module is entirely about ────────────────────────────────────
|
|
16
|
-
* A hang and an answer must not look the same to somebody watching a spinner:
|
|
17
|
-
*
|
|
18
|
-
* | what happened | is it an answer? | what the viewer should see |
|
|
19
|
-
* |---|---|---|
|
|
20
|
-
* | slow segment, tunnel blinked, origin waking | no — still in flight | keep spinning |
|
|
21
|
-
* | `404` / `410` | yes, and it is final | the reason, now |
|
|
22
|
-
* | `401` / `403` | yes, and it is RECOVERABLE | re-ask the app, then the reason |
|
|
23
|
-
* | any other `4xx`/`5xx` | yes, and we cannot name it | the reason, now |
|
|
24
|
-
*
|
|
25
|
-
* `403` is split out because it is the one that comes back on its own: every signed URL in
|
|
26
|
-
* this fleet carries `exp = iat + 2 × window`, so a page left open across a window boundary
|
|
27
|
-
* meets a stale token legitimately and the fix is to ASK THE APP AGAIN, not to tell the
|
|
28
|
-
* viewer his film is gone. Collapsing it into "gone" would put a false and permanent-sounding
|
|
29
|
-
* sentence on screen for the one fault that clears itself.
|
|
30
|
-
*
|
|
31
|
-
* ── 🔴 Why a probe, and why only on the failure path ────────────────────────────────────
|
|
32
|
-
* `hls.js` hands us the status directly (`ErrorData.response.code`), so the ladder and its
|
|
33
|
-
* rungs cost nothing. An `<img>` and a `<video>` do NOT: both fire a bare `error` event that
|
|
34
|
-
* carries no status at all, so "the file is gone" and "the token went stale" and "the codec
|
|
35
|
-
* is unplayable" arrive as the same undifferentiated failure. {@link probeSourceFault} is the
|
|
36
|
-
* one way to tell them apart, and it runs ONLY after the element has already given up — so a
|
|
37
|
-
* gallery that is working costs exactly nothing, and a one-byte `Range` request is the whole
|
|
38
|
-
* price of a sentence that is true.
|
|
39
|
-
*
|
|
40
|
-
* (`bytes=0-0` is a CORS-safelisted range, so the probe is a simple request and never costs a
|
|
41
|
-
* preflight; the 404 in the report above answers `access-control-allow-origin: *`, which is
|
|
42
|
-
* what makes the status readable cross-origin at all. When it is not readable the fetch
|
|
43
|
-
* throws, and that is `unknown` — honest, still not a spinner.)
|
|
44
|
-
*
|
|
45
|
-
* ── 🔴 Why one module holds the reading, the wording AND the overlay ────────────────────
|
|
46
|
-
* Three surfaces have to agree about the same fault — the gallery's picture stage, its native
|
|
47
|
-
* video stage and the shared player — and the thing they must agree on is not the mechanism,
|
|
48
|
-
* it is the SENTENCE. Split across three files this becomes three wordings, and the owner
|
|
49
|
-
* meets whichever one his file happens to be behind.
|
|
50
|
-
*
|
|
51
|
-
* It is deliberately NOT a published subpath and nothing here is re-exported from
|
|
52
|
-
* `react/media/index.ts`. `publicSurface.baseline` charges for a permanent promise, and none
|
|
53
|
-
* of this is one: the wording is copy, the overlay is chrome, and a consumer that wanted to
|
|
54
|
-
* draw its own would be re-implementing the corner rather than importing it. (Same call the
|
|
55
|
-
* badge stack in `VideoPlayer.tsx` made — see that baseline entry's "what did NOT go on the
|
|
56
|
-
* surface".) A `.tsx` holding a component beside its helpers is `galleryItemMedia.tsx`'s
|
|
57
|
-
* shape, for the same reason.
|
|
58
|
-
*/
|
|
59
1
|
/** Which of the three answers came back. `expired` is the only one worth re-asking. */
|
|
60
2
|
export type SourceFaultKind =
|
|
61
3
|
/** The source said there is nothing here — `404`/`410`. Final. */
|
|
@@ -123,6 +65,8 @@ export declare function sourceFaultMessage(fault: SourceFault, subject: string):
|
|
|
123
65
|
* every file that reads one of its payloads.
|
|
124
66
|
*/
|
|
125
67
|
export declare function hlsErrorIsAnswer(detail: unknown): boolean;
|
|
68
|
+
/** A fatal hls.js error as one short word for telemetry, or null for one it recovers from. */
|
|
69
|
+
export declare function hlsErrorName(detail: unknown): string | null;
|
|
126
70
|
/**
|
|
127
71
|
* What a video surface says once every source has ANSWERED.
|
|
128
72
|
*
|
|
@@ -139,8 +83,10 @@ export declare function hlsErrorIsAnswer(detail: unknown): boolean;
|
|
|
139
83
|
* later. The gallery's stages do the opposite and REPLACE their element — there the `<video>`
|
|
140
84
|
* is the spinner, so nothing short of removing it would do.
|
|
141
85
|
*/
|
|
142
|
-
export declare function SourceFaultOverlay({ fault, name, ...rest }: {
|
|
86
|
+
export declare function SourceFaultOverlay({ fault, name, onRetry, ...rest }: {
|
|
143
87
|
fault: SourceFault;
|
|
144
88
|
name: string;
|
|
89
|
+
/** A fresh attempt without reloading the page. Most failures here are a moment, not a verdict. */
|
|
90
|
+
onRetry?: () => void;
|
|
145
91
|
}): import("react").JSX.Element;
|
|
146
92
|
//# sourceMappingURL=sourceFault.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sourceFault.d.ts","sourceRoot":"","sources":["../../../src/react/media/sourceFault.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sourceFault.d.ts","sourceRoot":"","sources":["../../../src/react/media/sourceFault.tsx"],"names":[],"mappings":"AA4DA,uFAAuF;AACvF,MAAM,MAAM,eAAe;AACzB,kEAAkE;AAChE,MAAM;AACR,6EAA6E;GAC3E,SAAS;AACX,qFAAqF;GACnF,SAAS,CAAC;AAEd,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,CAAC;IACtB,6FAA6F;IAC7F,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAKxE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAE9D;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAqBpF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAS9E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAKzD;AAED,8FAA8F;AAC9F,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAK3D;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,IAAI,EACJ,OAAO,EACP,GAAG,IAAI,EACR,EAAE;IACD,KAAK,EAAE,WAAW,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,kGAAkG;IAClG,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB,+BAkCA"}
|
|
@@ -1,4 +1,63 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* What a media URL ANSWERED, when what it answered was not the bytes.
|
|
4
|
+
*
|
|
5
|
+
* ── 🔴 The report this exists for ───────────────────────────────────────────────────────
|
|
6
|
+
* Owner, 2026-09-17: *"make a 404 stop the media player spinner so I don't think it's going
|
|
7
|
+
* to eventually load."* One item in his catalog has no ladder, so the very first request a
|
|
8
|
+
* player makes is answered honestly and immediately:
|
|
9
|
+
*
|
|
10
|
+
* GET …/u_fd668e6a….hls/master.m3u8 → 404 {"error":"not_found"} in 0.2 s
|
|
11
|
+
*
|
|
12
|
+
* and the spinner then ran until he gave up. **A 404 is a COMPLETE ANSWER** — there is
|
|
13
|
+
* nothing still in flight, nothing to wait for, and no amount of waiting changes it — and it
|
|
14
|
+
* was the one status every surface here treated as "still loading".
|
|
15
|
+
*
|
|
16
|
+
* ── 🔴 The distinction this module is entirely about ────────────────────────────────────
|
|
17
|
+
* A hang and an answer must not look the same to somebody watching a spinner:
|
|
18
|
+
*
|
|
19
|
+
* | what happened | is it an answer? | what the viewer should see |
|
|
20
|
+
* |---|---|---|
|
|
21
|
+
* | slow segment, tunnel blinked, origin waking | no — still in flight | keep spinning |
|
|
22
|
+
* | `404` / `410` | yes, and it is final | the reason, now |
|
|
23
|
+
* | `401` / `403` | yes, and it is RECOVERABLE | re-ask the app, then the reason |
|
|
24
|
+
* | any other `4xx`/`5xx` | yes, and we cannot name it | the reason, now |
|
|
25
|
+
*
|
|
26
|
+
* `403` is split out because it is the one that comes back on its own: every signed URL in
|
|
27
|
+
* this fleet carries `exp = iat + 2 × window`, so a page left open across a window boundary
|
|
28
|
+
* meets a stale token legitimately and the fix is to ASK THE APP AGAIN, not to tell the
|
|
29
|
+
* viewer his film is gone. Collapsing it into "gone" would put a false and permanent-sounding
|
|
30
|
+
* sentence on screen for the one fault that clears itself.
|
|
31
|
+
*
|
|
32
|
+
* ── 🔴 Why a probe, and why only on the failure path ────────────────────────────────────
|
|
33
|
+
* `hls.js` hands us the status directly (`ErrorData.response.code`), so the ladder and its
|
|
34
|
+
* rungs cost nothing. An `<img>` and a `<video>` do NOT: both fire a bare `error` event that
|
|
35
|
+
* carries no status at all, so "the file is gone" and "the token went stale" and "the codec
|
|
36
|
+
* is unplayable" arrive as the same undifferentiated failure. {@link probeSourceFault} is the
|
|
37
|
+
* one way to tell them apart, and it runs ONLY after the element has already given up — so a
|
|
38
|
+
* gallery that is working costs exactly nothing, and a one-byte `Range` request is the whole
|
|
39
|
+
* price of a sentence that is true.
|
|
40
|
+
*
|
|
41
|
+
* (`bytes=0-0` is a CORS-safelisted range, so the probe is a simple request and never costs a
|
|
42
|
+
* preflight; the 404 in the report above answers `access-control-allow-origin: *`, which is
|
|
43
|
+
* what makes the status readable cross-origin at all. When it is not readable the fetch
|
|
44
|
+
* throws, and that is `unknown` — honest, still not a spinner.)
|
|
45
|
+
*
|
|
46
|
+
* ── 🔴 Why one module holds the reading, the wording AND the overlay ────────────────────
|
|
47
|
+
* Three surfaces have to agree about the same fault — the gallery's picture stage, its native
|
|
48
|
+
* video stage and the shared player — and the thing they must agree on is not the mechanism,
|
|
49
|
+
* it is the SENTENCE. Split across three files this becomes three wordings, and the owner
|
|
50
|
+
* meets whichever one his file happens to be behind.
|
|
51
|
+
*
|
|
52
|
+
* It is deliberately NOT a published subpath and nothing here is re-exported from
|
|
53
|
+
* `react/media/index.ts`. `publicSurface.baseline` charges for a permanent promise, and none
|
|
54
|
+
* of this is one: the wording is copy, the overlay is chrome, and a consumer that wanted to
|
|
55
|
+
* draw its own would be re-implementing the corner rather than importing it. (Same call the
|
|
56
|
+
* badge stack in `VideoPlayer.tsx` made — see that baseline entry's "what did NOT go on the
|
|
57
|
+
* surface".) A `.tsx` holding a component beside its helpers is `galleryItemMedia.tsx`'s
|
|
58
|
+
* shape, for the same reason.
|
|
59
|
+
*/
|
|
60
|
+
import { Button } from '../components/Button.js';
|
|
2
61
|
/**
|
|
3
62
|
* Classify one HTTP status. `null` for anything below 400 — a source that answered `200` and
|
|
4
63
|
* still would not play is not a fault of the URL, and saying otherwise would blame the store
|
|
@@ -101,6 +160,14 @@ export function hlsErrorIsAnswer(detail) {
|
|
|
101
160
|
return false;
|
|
102
161
|
return sourceFaultFromStatus(code) !== null;
|
|
103
162
|
}
|
|
163
|
+
/** A fatal hls.js error as one short word for telemetry, or null for one it recovers from. */
|
|
164
|
+
export function hlsErrorName(detail) {
|
|
165
|
+
const data = detail;
|
|
166
|
+
if (!data?.fatal)
|
|
167
|
+
return null;
|
|
168
|
+
const code = data.response?.code;
|
|
169
|
+
return `hls:${data.details ?? 'unknown'}${typeof code === 'number' ? `:${code}` : ''}`;
|
|
170
|
+
}
|
|
104
171
|
/**
|
|
105
172
|
* What a video surface says once every source has ANSWERED.
|
|
106
173
|
*
|
|
@@ -117,7 +184,7 @@ export function hlsErrorIsAnswer(detail) {
|
|
|
117
184
|
* later. The gallery's stages do the opposite and REPLACE their element — there the `<video>`
|
|
118
185
|
* is the spinner, so nothing short of removing it would do.
|
|
119
186
|
*/
|
|
120
|
-
export function SourceFaultOverlay({ fault, name, ...rest }) {
|
|
187
|
+
export function SourceFaultOverlay({ fault, name, onRetry, ...rest }) {
|
|
121
188
|
return (_jsxs("div", { ...rest, role: 'status', style: {
|
|
122
189
|
position: 'absolute',
|
|
123
190
|
inset: 0,
|
|
@@ -134,6 +201,6 @@ export function SourceFaultOverlay({ fault, name, ...rest }) {
|
|
|
134
201
|
background: 'rgba(0, 0, 0, 0.82)',
|
|
135
202
|
// guardrails-ignore no-hardcoded-colors: see comment above.
|
|
136
203
|
color: 'rgba(255, 255, 255, 0.92)',
|
|
137
|
-
}, children: [_jsx("span", { style: { fontSize: 13, fontWeight: 600 }, children: name }), _jsx("span", { style: { fontSize: 13, lineHeight: 1.5, maxWidth: '42ch' }, children: sourceFaultMessage(fault, 'This video') })] }));
|
|
204
|
+
}, children: [_jsx("span", { style: { fontSize: 13, fontWeight: 600 }, children: name }), _jsx("span", { style: { fontSize: 13, lineHeight: 1.5, maxWidth: '42ch' }, children: sourceFaultMessage(fault, 'This video') }), onRetry ? (_jsx(Button, { onClick: onRetry, size: 'sm', tooltip: 'Ask for this video again', tooltipMobile: 'off', variant: 'secondary', children: "Try again" })) : null] }));
|
|
138
205
|
}
|
|
139
206
|
//# sourceMappingURL=sourceFault.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sourceFault.js","sourceRoot":"","sources":["../../../src/react/media/sourceFault.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"sourceFault.js","sourceRoot":"","sources":["../../../src/react/media/sourceFault.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAiBjD;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAc;IAClD,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IACtE,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IACzE,IAAI,MAAM,IAAI,GAAG;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IACtD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAkB;IACnD,OAAO,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC;AAClC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,GAAuB;IAC5D,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC;IACnC,IAAI,CAAC,GAAG,IAAI,OAAO,SAAS,KAAK,UAAU;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IACxE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE;YAC/B,MAAM,EAAE,KAAK;YACb,sFAAsF;YACtF,oCAAoC;YACpC,OAAO,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE;YAC/B,KAAK,EAAE,UAAU;SAClB,CAAC,CAAC;QACH,0FAA0F;QAC1F,uFAAuF;QACvF,0FAA0F;QAC1F,0EAA0E;QAC1E,OAAO,qBAAqB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,qFAAqF;QACrF,gEAAgE;QAChE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAC7B,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAkB,EAAE,OAAe;IACpE,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACrE,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC1B,OAAO,GAAG,OAAO,mCAAmC,MAAM,8BAA8B,CAAC;IAC3F,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,GAAG,OAAO,sBAAsB,MAAM,+BAA+B,CAAC;IAC/E,CAAC;IACD,OAAO,GAAG,OAAO,uBAAuB,MAAM,8BAA8B,CAAC;AAC/E,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAe;IAC9C,MAAM,IAAI,GAAG,MAA8E,CAAC;IAC5F,MAAM,IAAI,GAAG,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC;IAClC,IAAI,CAAC,IAAI,EAAE,KAAK,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC3D,OAAO,qBAAqB,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;AAC9C,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,YAAY,CAAC,MAAe;IAC1C,MAAM,IAAI,GAAG,MAAgG,CAAC;IAC9G,IAAI,CAAC,IAAI,EAAE,KAAK;QAAE,OAAO,IAAI,CAAC;IAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC;IACjC,OAAO,OAAO,IAAI,CAAC,OAAO,IAAI,SAAS,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AACzF,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,kBAAkB,CAAC,EACjC,KAAK,EACL,IAAI,EACJ,OAAO,EACP,GAAG,IAAI,EAMR;IACC,OAAO,CACL,kBACM,IAAI,EACR,IAAI,EAAC,QAAQ,EACb,KAAK,EAAE;YACL,QAAQ,EAAE,UAAU;YACpB,KAAK,EAAE,CAAC;YACR,OAAO,EAAE,MAAM;YACf,aAAa,EAAE,QAAQ;YACvB,UAAU,EAAE,QAAQ;YACpB,cAAc,EAAE,QAAQ;YACxB,GAAG,EAAE,CAAC;YACN,OAAO,EAAE,EAAE;YACX,SAAS,EAAE,QAAQ;YACnB,uFAAuF;YACvF,qEAAqE;YACrE,4DAA4D;YAC5D,UAAU,EAAE,qBAAqB;YACjC,4DAA4D;YAC5D,KAAK,EAAE,2BAA2B;SACnC,aAED,eAAM,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,YAAG,IAAI,GAAQ,EAC7D,eAAM,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,YAC7D,kBAAkB,CAAC,KAAK,EAAE,YAAY,CAAC,GACnC,EACN,OAAO,CAAC,CAAC,CAAC,CACT,KAAC,MAAM,IAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAC,IAAI,EAAC,OAAO,EAAC,0BAA0B,EAAC,aAAa,EAAC,KAAK,EAAC,OAAO,EAAC,WAAW,0BAErG,CACV,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
import { type PlaybackWatch } from './playbackWatch.js';
|
|
3
|
+
/**
|
|
4
|
+
* The watch a surface opened before its player existed, so the clock covers the wait for a URL too:
|
|
5
|
+
* a hung `resolveMedia` is a spinner with no player under it. A `VideoPlayer` with none opens its own.
|
|
6
|
+
*/
|
|
7
|
+
export declare const PlaybackWatchContext: import("react").Context<RefObject<PlaybackWatch | null> | null>;
|
|
8
|
+
/**
|
|
9
|
+
* One watch per open of one video. Made during render, because a child's effects run before its
|
|
10
|
+
* parent's and a player mounting with a cached URL names its source first; only the timers wait
|
|
11
|
+
* for the effect. Pass a null `itemId` for anything that is not a video.
|
|
12
|
+
*/
|
|
13
|
+
export declare function usePlaybackWatch(itemId: string | null, surface: string): RefObject<PlaybackWatch | null>;
|
|
14
|
+
//# sourceMappingURL=usePlaybackWatch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePlaybackWatch.d.ts","sourceRoot":"","sources":["../../../src/react/media/usePlaybackWatch.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAiC,MAAM,OAAO,CAAC;AACrF,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAE7E;;;GAGG;AACH,eAAO,MAAM,oBAAoB,iEAA8D,CAAC;AAEhG;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC,aAAa,GAAG,IAAI,CAAC,CAmBxG"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createContext, useContext, useEffect, useRef } from 'react';
|
|
2
|
+
import { createPlaybackWatch } from './playbackWatch.js';
|
|
3
|
+
/**
|
|
4
|
+
* The watch a surface opened before its player existed, so the clock covers the wait for a URL too:
|
|
5
|
+
* a hung `resolveMedia` is a spinner with no player under it. A `VideoPlayer` with none opens its own.
|
|
6
|
+
*/
|
|
7
|
+
export const PlaybackWatchContext = createContext(null);
|
|
8
|
+
/**
|
|
9
|
+
* One watch per open of one video. Made during render, because a child's effects run before its
|
|
10
|
+
* parent's and a player mounting with a cached URL names its source first; only the timers wait
|
|
11
|
+
* for the effect. Pass a null `itemId` for anything that is not a video.
|
|
12
|
+
*/
|
|
13
|
+
export function usePlaybackWatch(itemId, surface) {
|
|
14
|
+
const inherited = useContext(PlaybackWatchContext);
|
|
15
|
+
const own = useRef(null);
|
|
16
|
+
if (!inherited && own.current?.itemId !== (itemId ?? undefined))
|
|
17
|
+
own.current = itemId ? createPlaybackWatch({ itemId, surface }) : null;
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (inherited || !itemId)
|
|
20
|
+
return;
|
|
21
|
+
const watch = own.current;
|
|
22
|
+
watch?.start();
|
|
23
|
+
const leaving = () => watch?.close();
|
|
24
|
+
addEventListener('pagehide', leaving);
|
|
25
|
+
return () => {
|
|
26
|
+
removeEventListener('pagehide', leaving);
|
|
27
|
+
watch?.close();
|
|
28
|
+
};
|
|
29
|
+
}, [inherited, itemId]);
|
|
30
|
+
return inherited ?? own;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=usePlaybackWatch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePlaybackWatch.js","sourceRoot":"","sources":["../../../src/react/media/usePlaybackWatch.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAkB,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACrF,OAAO,EAAE,mBAAmB,EAAsB,MAAM,oBAAoB,CAAC;AAE7E;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,aAAa,CAAyC,IAAI,CAAC,CAAC;AAEhG;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAqB,EAAE,OAAe;IACrE,MAAM,SAAS,GAAG,UAAU,CAAC,oBAAoB,CAAC,CAAC;IACnD,MAAM,GAAG,GAAG,MAAM,CAAuB,IAAI,CAAC,CAAC;IAC/C,IAAI,CAAC,SAAS,IAAI,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,CAAC,MAAM,IAAI,SAAS,CAAC;QAC7D,GAAG,CAAC,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,mBAAmB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEzE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,SAAS,IAAI,CAAC,MAAM;YAAE,OAAO;QACjC,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC;QAC1B,KAAK,EAAE,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,GAAS,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC;QAC3C,gBAAgB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACtC,OAAO,GAAG,EAAE;YACV,mBAAmB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACzC,KAAK,EAAE,KAAK,EAAE,CAAC;QACjB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;IAExB,OAAO,SAAS,IAAI,GAAG,CAAC;AAC1B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaGallery.d.ts","sourceRoot":"","sources":["../../../src/react/media-gallery/MediaGallery.tsx"],"names":[],"mappings":"AAAA,OAAO,oBAAoB,CAAC;AA4D5B,OAAO,KAAK,EAAE,EACZ,KAAK,SAAS,EAEd,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"MediaGallery.d.ts","sourceRoot":"","sources":["../../../src/react/media-gallery/MediaGallery.tsx"],"names":[],"mappings":"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"}
|
|
@@ -43,6 +43,7 @@ import { DenseGalleryDetail, DenseGalleryShell } from '../media/DenseGalleryShel
|
|
|
43
43
|
import { CBGD_BARE_TILE_HEIGHT } from '../media/VirtualMediaGrid.js';
|
|
44
44
|
import { useGalleryReturn } from './galleryReturn.js';
|
|
45
45
|
import { useHlsSource } from '../media/hlsSource.js';
|
|
46
|
+
import { PlaybackWatchContext, usePlaybackWatch } from '../media/usePlaybackWatch.js';
|
|
46
47
|
import { VideoQualitySelect } from '../media/VideoQualitySelect.js';
|
|
47
48
|
import { ImageOriginalToggle } from '../media/ImageOriginalToggle.js';
|
|
48
49
|
import { useImageOriginal } from '../media/useImageOriginal.js';
|
|
@@ -740,6 +741,21 @@ function ImageSurface({ item, url, originalUrl, reAsk, }) {
|
|
|
740
741
|
function GalleryStage({ item, mediaCache, bytesCache, loadBytes, nativeVideoStage, repeat, onEnded, onStep, canStep, fullscreenOnDoubleClick, fillWindow, onFillWindowChange, }) {
|
|
741
742
|
const category = galleryItemCategory(item);
|
|
742
743
|
const { media, resolving, refresh } = useItemMedia(item, mediaCache);
|
|
744
|
+
/*
|
|
745
|
+
* 🔴 The clock starts HERE, at the stage, and not at the player: a hung `resolveMedia` is a
|
|
746
|
+
* spinner with no player under it, and the player's own clock would never have started. See
|
|
747
|
+
* `media/playbackWatch.ts`. A video still converting is a wait the app already explains.
|
|
748
|
+
*/
|
|
749
|
+
const watch = usePlaybackWatch(category === 'video' ? item.id : null, 'stage');
|
|
750
|
+
const noSource = !(media.streamUrl || media.url || media.hlsUrl);
|
|
751
|
+
const stillConverting = noSource && Boolean(media.pending);
|
|
752
|
+
const unavailable = noSource && !media.pending && !resolving;
|
|
753
|
+
useEffect(() => {
|
|
754
|
+
if (stillConverting)
|
|
755
|
+
watch.current?.cancel();
|
|
756
|
+
else if (unavailable)
|
|
757
|
+
watch.current?.failed('unavailable');
|
|
758
|
+
}, [watch, stillConverting, unavailable]);
|
|
743
759
|
if (category === 'video') {
|
|
744
760
|
const src = media.streamUrl ?? media.url ?? '';
|
|
745
761
|
/*
|
|
@@ -777,11 +793,11 @@ function GalleryStage({ item, mediaCache, bytesCache, loadBytes, nativeVideoStag
|
|
|
777
793
|
if (nativeVideoStage) {
|
|
778
794
|
return frame(_jsx(NativeVideoSurface, { item: item, media: media, streamUrl: src, repeat: repeat, reAsk: refresh, ...(onEnded ? { onEnded } : {}) }, media.hlsUrl ?? src));
|
|
779
795
|
}
|
|
780
|
-
return frame(_jsx(React.Suspense, { fallback: _jsx(StageLoading, { label: 'Loading player\u2026' }), children: _jsx(LazyVideoPlayer, { video: stagePlayableVideo(item, media, src), autoPlay: true,
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
796
|
+
return frame(_jsx(React.Suspense, { fallback: _jsx(StageLoading, { label: 'Loading player\u2026' }), children: _jsx(PlaybackWatchContext.Provider, { value: watch, children: _jsx(LazyVideoPlayer, { video: stagePlayableVideo(item, media, src), autoPlay: true,
|
|
797
|
+
// 🔴 The STAGE's own volume memory, not the shared primitive's default key — see
|
|
798
|
+
// `playbackPreferences.ts`. It is what makes the level he set on this clip apply to
|
|
799
|
+
// the next one, and it is per session, so a refresh is silent again.
|
|
800
|
+
volumeKey: STAGE_VOLUME_KEY, loop: repeat, ...(repeat ? {} : { onEnded }), ...(onStep ? { onStep, canStep: canStep !== false } : {}), ...(fullscreenOnDoubleClick === false ? { fullscreenOnDoubleClick: false } : {}), ...(onFillWindowChange ? { fillWindow: fillWindow === true, onFillWindowChange } : {}) }, item.id) }) }));
|
|
785
801
|
}
|
|
786
802
|
if (category === 'image') {
|
|
787
803
|
if (!media.url)
|