sanity-plugin-mux-input 2.14.0 → 2.16.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 +25 -24
- package/dist/index.d.mts +13 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js +1057 -470
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1059 -472
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/_exports/index.ts +1 -0
- package/src/actions/secrets.ts +6 -1
- package/src/actions/upload.ts +1 -1
- package/src/components/ConfigureApi.tsx +51 -5
- package/src/components/EditCaptionDialog.tsx +2 -2
- package/src/components/InputBrowser.tsx +8 -2
- package/src/components/PageSelector.tsx +4 -7
- package/src/components/Player.styled.tsx +7 -2
- package/src/components/PlayerActionsMenu.tsx +15 -1
- package/src/components/ResyncMetadata.tsx +152 -73
- package/src/components/SelectAsset.tsx +9 -3
- package/src/components/StudioTool.tsx +2 -2
- package/src/components/TextTracksManager.tsx +11 -55
- package/src/components/UploadConfiguration.tsx +104 -343
- package/src/components/Uploader.tsx +18 -7
- package/src/components/VideoDetails/VideoDetails.tsx +55 -19
- package/src/components/VideoDetails/useVideoDetails.ts +15 -1
- package/src/components/VideoInBrowser.tsx +53 -6
- package/src/components/VideoPlayer.tsx +120 -47
- package/src/components/VideoThumbnail.tsx +84 -72
- package/src/components/VideosBrowser.tsx +7 -5
- package/src/components/uploadConfiguration/PlaybackPolicy.tsx +95 -6
- package/src/components/uploadConfiguration/PlaybackPolicyOption.tsx +26 -10
- package/src/components/uploadConfiguration/ResolutionTierSelector.tsx +71 -0
- package/src/components/uploadConfiguration/StaticRenditionSelector.tsx +179 -0
- package/src/context/DrmPlaybackWarningContext.tsx +93 -0
- package/src/hooks/useFetchFileSize.ts +54 -0
- package/src/hooks/useMediaMetadata.ts +100 -0
- package/src/hooks/useResyncAsset.ts +110 -0
- package/src/hooks/useResyncMuxMetadata.ts +33 -0
- package/src/hooks/useSaveSecrets.ts +10 -3
- package/src/hooks/useSecretsDocumentValues.ts +9 -1
- package/src/hooks/useSecretsFormState.ts +6 -3
- package/src/schema.ts +5 -0
- package/src/util/addKeysToMuxData.ts +30 -0
- package/src/util/asserters.ts +14 -0
- package/src/util/createUrlParamsObject.ts +7 -3
- package/src/util/generateJwt.ts +11 -2
- package/src/util/getPlaybackPolicy.ts +63 -4
- package/src/util/getStoryboardSrc.ts +7 -3
- package/src/util/getVideoMetadata.ts +1 -0
- package/src/util/getVideoSrc.ts +9 -9
- package/src/util/readSecrets.ts +3 -1
- package/src/util/textTracks.ts +6 -3
- package/src/util/tryWithSuspend.ts +22 -0
- package/src/util/types.ts +27 -2
- package/src/util/getPlaybackId.ts +0 -9
package/dist/index.js
CHANGED
|
@@ -36,7 +36,45 @@ const ToolIcon = () => /* @__PURE__ */ jsxRuntime.jsx(
|
|
|
36
36
|
xmlns: "http://www.w3.org/2000/svg",
|
|
37
37
|
children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 3H3c-1.11 0-2 .89-2 2v12c0 1.1.89 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.11-.9-2-2-2zm0 14H3V5h18v12zm-5-6l-7 4V7z" })
|
|
38
38
|
}
|
|
39
|
-
),
|
|
39
|
+
), LOCAL_STORAGE_HAS_SHOWN_WARNING_KEY = "mux-plugin-has-shown-drm-playback-warning", DrmPlaybackWarningContext = React.createContext({
|
|
40
|
+
hasShownWarning: !1,
|
|
41
|
+
setHasWarnedAboutDrmPlayback: () => null
|
|
42
|
+
}), DrmPlaybackWarningContextProvider = ({
|
|
43
|
+
config,
|
|
44
|
+
children
|
|
45
|
+
}) => {
|
|
46
|
+
const hasWarned = (config?.disableDrmPlaybackWarning ?? !1) || window.localStorage.getItem(LOCAL_STORAGE_HAS_SHOWN_WARNING_KEY) === "true", [hasWarnedAboutDrmPlayback, setHasWarnedAboutDrmPlayback] = React.useState(hasWarned), setHasShownWarning = (b) => {
|
|
47
|
+
window.localStorage.setItem(LOCAL_STORAGE_HAS_SHOWN_WARNING_KEY, b.toString()), setHasWarnedAboutDrmPlayback(b);
|
|
48
|
+
};
|
|
49
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
50
|
+
DrmPlaybackWarningContext.Provider,
|
|
51
|
+
{
|
|
52
|
+
value: {
|
|
53
|
+
hasShownWarning: hasWarnedAboutDrmPlayback,
|
|
54
|
+
setHasWarnedAboutDrmPlayback: setHasShownWarning
|
|
55
|
+
},
|
|
56
|
+
children
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
}, useDrmPlaybackWarningContext = () => React.useContext(DrmPlaybackWarningContext), DRMWarningDialog = ({ onClose }) => {
|
|
60
|
+
const { setHasWarnedAboutDrmPlayback } = useDrmPlaybackWarningContext(), _onClose = () => {
|
|
61
|
+
setHasWarnedAboutDrmPlayback(!0), onClose();
|
|
62
|
+
};
|
|
63
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
64
|
+
ui.Dialog,
|
|
65
|
+
{
|
|
66
|
+
open: !0,
|
|
67
|
+
id: "drm-playback-warn",
|
|
68
|
+
onClose: _onClose,
|
|
69
|
+
header: "DRM Playback Warning",
|
|
70
|
+
footer: /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { padding: 3, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { mode: "ghost", tone: "primary", onClick: _onClose, text: "Ok" }) }),
|
|
71
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, padding: 3, children: [
|
|
72
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Card, { padding: [3, 3, 3], radius: 2, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 3, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, weight: "semibold", children: "DRM-protected playback will generate a license with a small associated cost. The plugin will attempt to play signed or public playback IDs instead whenever possible." }) }) }),
|
|
73
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Card, { padding: [3, 3, 3], radius: 2, tone: "suggest", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 3, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, weight: "semibold", children: "This is a one time warning. If it persists, you can disable it from your plugin configuration." }) }) })
|
|
74
|
+
] })
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
}, SANITY_API_VERSION = "2024-03-05";
|
|
40
78
|
function useClient() {
|
|
41
79
|
return sanity.useClient({ apiVersion: SANITY_API_VERSION });
|
|
42
80
|
}
|
|
@@ -109,7 +147,7 @@ function useAssets() {
|
|
|
109
147
|
function useDialogState() {
|
|
110
148
|
return React.useState(!1);
|
|
111
149
|
}
|
|
112
|
-
function saveSecrets(client, token, secretKey, enableSignedUrls, signingKeyId, signingKeyPrivate) {
|
|
150
|
+
function saveSecrets(client, token, secretKey, enableSignedUrls, signingKeyId, signingKeyPrivate, drmConfigId) {
|
|
113
151
|
const doc = {
|
|
114
152
|
_id: "secrets.mux",
|
|
115
153
|
_type: "mux.apiKey",
|
|
@@ -117,9 +155,10 @@ function saveSecrets(client, token, secretKey, enableSignedUrls, signingKeyId, s
|
|
|
117
155
|
secretKey,
|
|
118
156
|
enableSignedUrls,
|
|
119
157
|
signingKeyId,
|
|
120
|
-
signingKeyPrivate
|
|
158
|
+
signingKeyPrivate,
|
|
159
|
+
drmConfigId
|
|
121
160
|
};
|
|
122
|
-
return client.createOrReplace(doc);
|
|
161
|
+
return doc.signingKeyId = enableSignedUrls ? signingKeyId : "", doc.signingKeyPrivate = enableSignedUrls ? signingKeyPrivate : "", client.createOrReplace(doc);
|
|
123
162
|
}
|
|
124
163
|
async function createSigningKeys(client) {
|
|
125
164
|
try {
|
|
@@ -172,7 +211,8 @@ const useSaveSecrets = (client, secrets) => React.useCallback(
|
|
|
172
211
|
async ({
|
|
173
212
|
token,
|
|
174
213
|
secretKey,
|
|
175
|
-
enableSignedUrls
|
|
214
|
+
enableSignedUrls,
|
|
215
|
+
drmConfigId
|
|
176
216
|
}) => {
|
|
177
217
|
let { signingKeyId, signingKeyPrivate } = secrets;
|
|
178
218
|
try {
|
|
@@ -182,7 +222,8 @@ const useSaveSecrets = (client, secrets) => React.useCallback(
|
|
|
182
222
|
secretKey,
|
|
183
223
|
enableSignedUrls,
|
|
184
224
|
signingKeyId,
|
|
185
|
-
signingKeyPrivate
|
|
225
|
+
signingKeyPrivate,
|
|
226
|
+
drmConfigId
|
|
186
227
|
), !(await testSecrets(client))?.status && token && secretKey)
|
|
187
228
|
throw new Error("Invalid secrets");
|
|
188
229
|
} catch (err) {
|
|
@@ -201,7 +242,8 @@ const useSaveSecrets = (client, secrets) => React.useCallback(
|
|
|
201
242
|
secretKey,
|
|
202
243
|
enableSignedUrls,
|
|
203
244
|
signingKeyId,
|
|
204
|
-
signingKeyPrivate
|
|
245
|
+
signingKeyPrivate,
|
|
246
|
+
drmConfigId ?? ""
|
|
205
247
|
);
|
|
206
248
|
} catch (err) {
|
|
207
249
|
throw console.log("Error while creating and saving signing key:", err?.message), err;
|
|
@@ -211,11 +253,19 @@ const useSaveSecrets = (client, secrets) => React.useCallback(
|
|
|
211
253
|
secretKey,
|
|
212
254
|
enableSignedUrls,
|
|
213
255
|
signingKeyId,
|
|
214
|
-
signingKeyPrivate
|
|
256
|
+
signingKeyPrivate,
|
|
257
|
+
drmConfigId
|
|
215
258
|
};
|
|
216
259
|
},
|
|
217
260
|
[client, secrets]
|
|
218
|
-
), name = "mux-input", cacheNs = "sanity-plugin-mux-input", muxSecretsDocumentId = "secrets.mux", DIALOGS_Z_INDEX = 6e4, THUMBNAIL_ASPECT_RATIO = 1.7777777777777777, MIN_ASPECT_RATIO = 5 / 4, AUDIO_ASPECT_RATIO = 5 / 1, path$1 = [
|
|
261
|
+
), name = "mux-input", cacheNs = "sanity-plugin-mux-input", muxSecretsDocumentId = "secrets.mux", DIALOGS_Z_INDEX = 6e4, THUMBNAIL_ASPECT_RATIO = 1.7777777777777777, MIN_ASPECT_RATIO = 5 / 4, AUDIO_ASPECT_RATIO = 5 / 1, path$1 = [
|
|
262
|
+
"token",
|
|
263
|
+
"secretKey",
|
|
264
|
+
"enableSignedUrls",
|
|
265
|
+
"signingKeyId",
|
|
266
|
+
"signingKeyPrivate",
|
|
267
|
+
"drmConfigId"
|
|
268
|
+
], useSecretsDocumentValues = () => {
|
|
219
269
|
const { error, isLoading, value } = sanity.useDocumentValues(
|
|
220
270
|
muxSecretsDocumentId,
|
|
221
271
|
path$1
|
|
@@ -225,7 +275,8 @@ const useSaveSecrets = (client, secrets) => React.useCallback(
|
|
|
225
275
|
secretKey: value?.secretKey || null,
|
|
226
276
|
enableSignedUrls: value?.enableSignedUrls || !1,
|
|
227
277
|
signingKeyId: value?.signingKeyId || null,
|
|
228
|
-
signingKeyPrivate: value?.signingKeyPrivate || null
|
|
278
|
+
signingKeyPrivate: value?.signingKeyPrivate || null,
|
|
279
|
+
drmConfigId: value?.drmConfigId || null
|
|
229
280
|
};
|
|
230
281
|
return {
|
|
231
282
|
isInitialSetup: !exists,
|
|
@@ -235,7 +286,7 @@ const useSaveSecrets = (client, secrets) => React.useCallback(
|
|
|
235
286
|
}, [value]);
|
|
236
287
|
return { error, isLoading, value: cache };
|
|
237
288
|
};
|
|
238
|
-
function init({ token, secretKey, enableSignedUrls }) {
|
|
289
|
+
function init({ token, secretKey, enableSignedUrls, drmConfigId }) {
|
|
239
290
|
return {
|
|
240
291
|
submitting: !1,
|
|
241
292
|
error: null,
|
|
@@ -243,7 +294,8 @@ function init({ token, secretKey, enableSignedUrls }) {
|
|
|
243
294
|
// This ensures the `dirty` check works correctly
|
|
244
295
|
token: token ?? "",
|
|
245
296
|
secretKey: secretKey ?? "",
|
|
246
|
-
enableSignedUrls: enableSignedUrls ?? !1
|
|
297
|
+
enableSignedUrls: enableSignedUrls ?? !1,
|
|
298
|
+
drmConfigId: drmConfigId ?? ""
|
|
247
299
|
};
|
|
248
300
|
}
|
|
249
301
|
function reducer(state, action) {
|
|
@@ -271,7 +323,8 @@ function readSecrets(client) {
|
|
|
271
323
|
secretKey,
|
|
272
324
|
enableSignedUrls,
|
|
273
325
|
signingKeyId,
|
|
274
|
-
signingKeyPrivate
|
|
326
|
+
signingKeyPrivate,
|
|
327
|
+
drmConfigId
|
|
275
328
|
}`,
|
|
276
329
|
{ _id }
|
|
277
330
|
);
|
|
@@ -280,7 +333,8 @@ function readSecrets(client) {
|
|
|
280
333
|
secretKey: data?.secretKey || null,
|
|
281
334
|
enableSignedUrls: !!data?.enableSignedUrls || !1,
|
|
282
335
|
signingKeyId: data?.signingKeyId || null,
|
|
283
|
-
signingKeyPrivate: data?.signingKeyPrivate || null
|
|
336
|
+
signingKeyPrivate: data?.signingKeyPrivate || null,
|
|
337
|
+
drmConfigId: data?.drmConfigId || null
|
|
284
338
|
};
|
|
285
339
|
}, [cacheNs, _id, projectId, dataset]);
|
|
286
340
|
}
|
|
@@ -343,20 +397,20 @@ function FormField(props) {
|
|
|
343
397
|
] });
|
|
344
398
|
}
|
|
345
399
|
var FormField$1 = React.memo(FormField);
|
|
346
|
-
const fieldNames = ["token", "secretKey", "enableSignedUrls"];
|
|
400
|
+
const fieldNames = ["token", "secretKey", "enableSignedUrls", "drmConfigId"];
|
|
347
401
|
function ConfigureApiDialog({ secrets, setDialogState }) {
|
|
348
402
|
const client = useClient(), [state, dispatch] = useSecretsFormState(secrets), hasSecretsInitially = React.useMemo(() => secrets.token && secrets.secretKey, [secrets]), handleClose = React.useCallback(() => setDialogState(!1), [setDialogState]), dirty = React.useMemo(
|
|
349
|
-
() => secrets.token !== state.token || secrets.secretKey !== state.secretKey || secrets.enableSignedUrls !== state.enableSignedUrls,
|
|
403
|
+
() => secrets.token !== state.token || secrets.secretKey !== state.secretKey || secrets.enableSignedUrls !== state.enableSignedUrls || secrets.drmConfigId !== state.drmConfigId,
|
|
350
404
|
[secrets, state]
|
|
351
|
-
), id = `ConfigureApi${React.useId()}`, [tokenId, secretKeyId, enableSignedUrlsId] = React.useMemo(
|
|
405
|
+
), id = `ConfigureApi${React.useId()}`, [tokenId, secretKeyId, enableSignedUrlsId, drmConfigIdId] = React.useMemo(
|
|
352
406
|
() => fieldNames.map((field) => `${id}-${field}`),
|
|
353
407
|
[id]
|
|
354
408
|
), firstField = React.useRef(null), handleSaveSecrets = useSaveSecrets(client, secrets), saving = React.useRef(!1), handleSubmit = React.useCallback(
|
|
355
409
|
(event) => {
|
|
356
410
|
if (event.preventDefault(), !saving.current && event.currentTarget.reportValidity()) {
|
|
357
411
|
saving.current = !0, dispatch({ type: "submit" });
|
|
358
|
-
const { token, secretKey, enableSignedUrls } = state;
|
|
359
|
-
handleSaveSecrets({ token, secretKey, enableSignedUrls }).then((savedSecrets) => {
|
|
412
|
+
const { token, secretKey, enableSignedUrls, drmConfigId } = state;
|
|
413
|
+
handleSaveSecrets({ token, secretKey, enableSignedUrls, drmConfigId }).then((savedSecrets) => {
|
|
360
414
|
const { projectId, dataset } = client.config();
|
|
361
415
|
suspendReact.clear([cacheNs, _id, projectId, dataset]), suspendReact.preload(() => Promise.resolve(savedSecrets), [cacheNs, _id, projectId, dataset]), setDialogState(!1);
|
|
362
416
|
}).catch((err) => dispatch({ type: "error", payload: err.message })).finally(() => {
|
|
@@ -389,6 +443,14 @@ function ConfigureApiDialog({ secrets, setDialogState }) {
|
|
|
389
443
|
});
|
|
390
444
|
},
|
|
391
445
|
[dispatch]
|
|
446
|
+
), handleChangeDrmConfigId = React.useCallback(
|
|
447
|
+
(event) => {
|
|
448
|
+
dispatch({
|
|
449
|
+
type: "change",
|
|
450
|
+
payload: { name: "drmConfigId", value: event.currentTarget.value }
|
|
451
|
+
});
|
|
452
|
+
},
|
|
453
|
+
[dispatch]
|
|
392
454
|
);
|
|
393
455
|
return React.useEffect(() => {
|
|
394
456
|
firstField.current && firstField.current.focus();
|
|
@@ -475,6 +537,45 @@ function ConfigureApiDialog({ secrets, setDialogState }) {
|
|
|
475
537
|
] })
|
|
476
538
|
] }) }) : null
|
|
477
539
|
] }),
|
|
540
|
+
/* @__PURE__ */ jsxRuntime.jsx(FormField$1, { title: "DRM Configuration ID", inputId: drmConfigIdId, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
541
|
+
ui.TextInput,
|
|
542
|
+
{
|
|
543
|
+
id: drmConfigIdId,
|
|
544
|
+
onChange: handleChangeDrmConfigId,
|
|
545
|
+
type: "text",
|
|
546
|
+
value: state.drmConfigId ?? "",
|
|
547
|
+
required: !1
|
|
548
|
+
}
|
|
549
|
+
) }),
|
|
550
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Card, { padding: [3, 3, 3], radius: 2, shadow: 1, tone: "neutral", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
|
|
551
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: 1, children: [
|
|
552
|
+
"DRM (Digital Rights Management) provides an extra layer of content security for video content streamed from Mux. For additional information check out our",
|
|
553
|
+
" ",
|
|
554
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
555
|
+
"a",
|
|
556
|
+
{
|
|
557
|
+
href: "https://www.mux.com/docs/guides/protect-videos-with-drm#play-drm-protected-videos",
|
|
558
|
+
target: "_blank",
|
|
559
|
+
rel: "noopener noreferrer",
|
|
560
|
+
children: "DRM Guide"
|
|
561
|
+
}
|
|
562
|
+
),
|
|
563
|
+
"."
|
|
564
|
+
] }),
|
|
565
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: 1, children: [
|
|
566
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
567
|
+
"a",
|
|
568
|
+
{
|
|
569
|
+
href: "https://www.mux.com/support/human",
|
|
570
|
+
target: "_blank",
|
|
571
|
+
rel: "noopener noreferrer",
|
|
572
|
+
children: "Contact us"
|
|
573
|
+
}
|
|
574
|
+
),
|
|
575
|
+
" ",
|
|
576
|
+
"to get started using DRM."
|
|
577
|
+
] })
|
|
578
|
+
] }) }),
|
|
478
579
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Inline, { space: 2, children: [
|
|
479
580
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
480
581
|
ui.Button,
|
|
@@ -778,6 +879,35 @@ function useInView(ref, options = {}) {
|
|
|
778
879
|
};
|
|
779
880
|
}, [options, ref]), inView;
|
|
780
881
|
}
|
|
882
|
+
function getPlaybackId(asset, priority = ["drm", "signed", "public"]) {
|
|
883
|
+
try {
|
|
884
|
+
if (!asset)
|
|
885
|
+
throw new TypeError("Tried to get playback Id with no asset");
|
|
886
|
+
const playbackIds = asset.data?.playback_ids;
|
|
887
|
+
if (playbackIds && playbackIds.length > 0) {
|
|
888
|
+
for (const policy of priority) {
|
|
889
|
+
const match = playbackIds.find((entry) => entry.policy === policy);
|
|
890
|
+
if (match)
|
|
891
|
+
return match.id;
|
|
892
|
+
}
|
|
893
|
+
return playbackIds[0].id;
|
|
894
|
+
}
|
|
895
|
+
throw new TypeError("Missing playbackId");
|
|
896
|
+
} catch (e) {
|
|
897
|
+
throw console.error("Asset is missing a playbackId", { asset }, e), e;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
function getPlaybackPolicy(asset) {
|
|
901
|
+
return asset.data?.playback_ids?.find(
|
|
902
|
+
(playbackId) => getPlaybackId(asset, ["drm", "signed", "public"]) === playbackId.id
|
|
903
|
+
) ?? { id: "", policy: "public" };
|
|
904
|
+
}
|
|
905
|
+
function getPlaybackPolicyById(asset, playbackId) {
|
|
906
|
+
return asset.data?.playback_ids?.find((entry) => playbackId === entry.id);
|
|
907
|
+
}
|
|
908
|
+
function hasPlaybackPolicy(data, policy) {
|
|
909
|
+
return data.advanced_playback_policies && data.advanced_playback_policies.find((p) => p.policy === policy) || data.playback_policy?.find((p) => p === policy);
|
|
910
|
+
}
|
|
781
911
|
function generateJwt(client, playbackId, aud, payload) {
|
|
782
912
|
const { signingKeyId, signingKeyPrivate } = readSecrets(client);
|
|
783
913
|
if (!signingKeyId)
|
|
@@ -798,20 +928,13 @@ function generateJwt(client, playbackId, aud, payload) {
|
|
|
798
928
|
}
|
|
799
929
|
);
|
|
800
930
|
}
|
|
801
|
-
function getPlaybackId(asset) {
|
|
802
|
-
if (!asset?.playbackId)
|
|
803
|
-
throw console.error("Asset is missing a playbackId", { asset }), new TypeError("Missing playbackId");
|
|
804
|
-
return asset.playbackId;
|
|
805
|
-
}
|
|
806
|
-
function getPlaybackPolicy(asset) {
|
|
807
|
-
return asset.data?.playback_ids?.find((playbackId) => asset.playbackId === playbackId.id)?.policy ?? "public";
|
|
808
|
-
}
|
|
809
931
|
function createUrlParamsObject(client, asset, params, audience) {
|
|
810
932
|
const playbackId = getPlaybackId(asset);
|
|
811
933
|
let searchParams = new URLSearchParams(
|
|
812
934
|
JSON.parse(JSON.stringify(params, (_, v) => v ?? void 0))
|
|
813
935
|
);
|
|
814
|
-
|
|
936
|
+
const playbackPolicy = getPlaybackPolicyById(asset, playbackId)?.policy;
|
|
937
|
+
if (playbackPolicy === "signed" || playbackPolicy === "drm") {
|
|
815
938
|
const token = generateJwt(client, playbackId, audience, params);
|
|
816
939
|
searchParams = new URLSearchParams({ token });
|
|
817
940
|
}
|
|
@@ -842,6 +965,15 @@ function getPosterSrc({
|
|
|
842
965
|
const { playbackId, searchParams } = createUrlParamsObject(client, asset, params, "t");
|
|
843
966
|
return `https://image.mux.com/${playbackId}/thumbnail.png?${searchParams}`;
|
|
844
967
|
}
|
|
968
|
+
function tryWithSuspend(block, onError) {
|
|
969
|
+
try {
|
|
970
|
+
return block();
|
|
971
|
+
} catch (errorOrPromise) {
|
|
972
|
+
if (errorOrPromise instanceof Promise)
|
|
973
|
+
throw errorOrPromise;
|
|
974
|
+
return onError ? onError(errorOrPromise) : void 0;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
845
977
|
const Image = styledComponents.styled.img`
|
|
846
978
|
transition: opacity 0.175s ease-out 0s;
|
|
847
979
|
display: block;
|
|
@@ -859,22 +991,22 @@ function VideoThumbnail({
|
|
|
859
991
|
width,
|
|
860
992
|
staticImage = !1
|
|
861
993
|
}) {
|
|
862
|
-
const ref = React.useRef(null), inView = useInView(ref),
|
|
863
|
-
|
|
994
|
+
const posterWidth = width || 250, client = useClient(), ref = React.useRef(null), inView = useInView(ref), [status, setStatus] = React.useState("loading"), [error, setError] = React.useState(null), thumbnailSrc = React.useMemo(() => tryWithSuspend(
|
|
995
|
+
() => {
|
|
864
996
|
let thumbnail;
|
|
865
997
|
return staticImage ? thumbnail = getPosterSrc({ asset, client, width: posterWidth }) : thumbnail = getAnimatedPosterSrc({ asset, client, width: posterWidth }), thumbnail;
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
|
|
998
|
+
},
|
|
999
|
+
(err) => {
|
|
1000
|
+
handleError(err.message);
|
|
869
1001
|
}
|
|
870
|
-
|
|
1002
|
+
), [asset, client, posterWidth, staticImage]);
|
|
871
1003
|
function handleLoad() {
|
|
872
1004
|
setStatus("loaded");
|
|
873
1005
|
}
|
|
874
|
-
function handleError() {
|
|
875
|
-
setStatus("error");
|
|
1006
|
+
function handleError(err) {
|
|
1007
|
+
setStatus("error"), setError(err || "Failed loading thumbnail");
|
|
876
1008
|
}
|
|
877
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1009
|
+
return /* @__PURE__ */ jsxRuntime.jsx(React.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Preparing thumbnail" }), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
878
1010
|
ui.Card,
|
|
879
1011
|
{
|
|
880
1012
|
style: {
|
|
@@ -915,23 +1047,23 @@ function VideoThumbnail({
|
|
|
915
1047
|
},
|
|
916
1048
|
children: [
|
|
917
1049
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 4, muted: !0, children: /* @__PURE__ */ jsxRuntime.jsx(icons.ErrorOutlineIcon, { style: { fontSize: "1.75em" } }) }),
|
|
918
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { muted: !0, align: "center", children:
|
|
1050
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { muted: !0, align: "center", children: error })
|
|
919
1051
|
]
|
|
920
1052
|
}
|
|
921
1053
|
),
|
|
922
1054
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
923
1055
|
Image,
|
|
924
1056
|
{
|
|
925
|
-
src,
|
|
1057
|
+
src: thumbnailSrc ?? void 0,
|
|
926
1058
|
alt: `Preview for ${staticImage ? "image" : "video"} ${asset.filename || asset.assetId}`,
|
|
927
1059
|
onLoad: handleLoad,
|
|
928
|
-
onError: handleError,
|
|
1060
|
+
onError: () => handleError(),
|
|
929
1061
|
style: { opacity: status === "loaded" ? 1 : 0 }
|
|
930
1062
|
}
|
|
931
1063
|
)
|
|
932
1064
|
] }) : null
|
|
933
1065
|
}
|
|
934
|
-
);
|
|
1066
|
+
) });
|
|
935
1067
|
}
|
|
936
1068
|
const MissingAssetCheckbox = styledComponents.styled(ui.Checkbox)`
|
|
937
1069
|
position: static !important;
|
|
@@ -1165,7 +1297,7 @@ const PageSelector = (props) => {
|
|
|
1165
1297
|
style: { cursor: "pointer" },
|
|
1166
1298
|
disabled: page <= 0,
|
|
1167
1299
|
onClick: () => {
|
|
1168
|
-
setPage((
|
|
1300
|
+
setPage((p) => Math.min(props.total - 1, Math.max(0, p - 1)));
|
|
1169
1301
|
}
|
|
1170
1302
|
}
|
|
1171
1303
|
),
|
|
@@ -1184,12 +1316,32 @@ const PageSelector = (props) => {
|
|
|
1184
1316
|
style: { cursor: "pointer" },
|
|
1185
1317
|
disabled: page >= props.total - 1,
|
|
1186
1318
|
onClick: () => {
|
|
1187
|
-
setPage((
|
|
1319
|
+
setPage((p) => Math.min(props.total - 1, Math.max(0, p + 1)));
|
|
1188
1320
|
}
|
|
1189
1321
|
}
|
|
1190
1322
|
)
|
|
1191
1323
|
] });
|
|
1192
1324
|
};
|
|
1325
|
+
function addKeysToMuxData(data) {
|
|
1326
|
+
return {
|
|
1327
|
+
...data,
|
|
1328
|
+
tracks: data.tracks?.map((track) => ({
|
|
1329
|
+
...track,
|
|
1330
|
+
_key: uuid.uuid()
|
|
1331
|
+
})),
|
|
1332
|
+
playback_ids: data.playback_ids?.map((playbackId) => ({
|
|
1333
|
+
...playbackId,
|
|
1334
|
+
_key: uuid.uuid()
|
|
1335
|
+
})),
|
|
1336
|
+
static_renditions: data.static_renditions ? {
|
|
1337
|
+
...data.static_renditions,
|
|
1338
|
+
files: data.static_renditions.files?.map((file) => ({
|
|
1339
|
+
...file,
|
|
1340
|
+
_key: uuid.uuid()
|
|
1341
|
+
}))
|
|
1342
|
+
} : void 0
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1193
1345
|
function useResyncMuxMetadata() {
|
|
1194
1346
|
const documentStore = sanity.useDocumentStore(), client = sanity.useClient({
|
|
1195
1347
|
apiVersion: SANITY_API_VERSION
|
|
@@ -1238,6 +1390,27 @@ function useResyncMuxMetadata() {
|
|
|
1238
1390
|
}
|
|
1239
1391
|
}
|
|
1240
1392
|
}
|
|
1393
|
+
async function syncFullData() {
|
|
1394
|
+
if (matchedAssets) {
|
|
1395
|
+
setResyncState("syncing");
|
|
1396
|
+
try {
|
|
1397
|
+
const tx = client.transaction();
|
|
1398
|
+
matchedAssets.forEach((matched) => {
|
|
1399
|
+
if (!matched.muxAsset) return;
|
|
1400
|
+
const dataWithKeys = addKeysToMuxData(matched.muxAsset);
|
|
1401
|
+
tx.patch(matched.sanityDoc._id, {
|
|
1402
|
+
set: {
|
|
1403
|
+
filename: matched.muxTitle || matched.currentTitle || "",
|
|
1404
|
+
status: matched.muxAsset.status,
|
|
1405
|
+
data: dataWithKeys
|
|
1406
|
+
}
|
|
1407
|
+
});
|
|
1408
|
+
}), await tx.commit({ returnDocuments: !1 }), setResyncState("done");
|
|
1409
|
+
} catch (error) {
|
|
1410
|
+
setResyncState("error"), setResyncError(error);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1241
1414
|
return {
|
|
1242
1415
|
sanityAssetsLoading,
|
|
1243
1416
|
closeDialog,
|
|
@@ -1247,6 +1420,7 @@ function useResyncMuxMetadata() {
|
|
|
1247
1420
|
hasSecrets,
|
|
1248
1421
|
syncAllVideos,
|
|
1249
1422
|
syncOnlyEmpty,
|
|
1423
|
+
syncFullData,
|
|
1250
1424
|
matchedAssets,
|
|
1251
1425
|
muxAssets,
|
|
1252
1426
|
openDialog
|
|
@@ -1262,22 +1436,78 @@ const useSanityAssets = sanity.createHookFromObservableFactory(
|
|
|
1262
1436
|
}
|
|
1263
1437
|
)
|
|
1264
1438
|
);
|
|
1439
|
+
function OptionCard({
|
|
1440
|
+
id,
|
|
1441
|
+
selected,
|
|
1442
|
+
onSelect,
|
|
1443
|
+
title,
|
|
1444
|
+
count,
|
|
1445
|
+
description,
|
|
1446
|
+
disabled
|
|
1447
|
+
}) {
|
|
1448
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1449
|
+
ui.Card,
|
|
1450
|
+
{
|
|
1451
|
+
as: "label",
|
|
1452
|
+
padding: 3,
|
|
1453
|
+
radius: 2,
|
|
1454
|
+
border: !0,
|
|
1455
|
+
tone: selected ? "primary" : "default",
|
|
1456
|
+
style: {
|
|
1457
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
1458
|
+
opacity: disabled ? 0.5 : 1
|
|
1459
|
+
},
|
|
1460
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gap: 3, align: "flex-start", children: [
|
|
1461
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Box, { paddingTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1462
|
+
ui.Radio,
|
|
1463
|
+
{
|
|
1464
|
+
checked: selected,
|
|
1465
|
+
onChange: () => onSelect(id),
|
|
1466
|
+
disabled,
|
|
1467
|
+
name: "sync-option"
|
|
1468
|
+
}
|
|
1469
|
+
) }),
|
|
1470
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, flex: 1, children: [
|
|
1471
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { align: "center", gap: 2, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: 2, weight: "semibold", children: [
|
|
1472
|
+
title,
|
|
1473
|
+
" (",
|
|
1474
|
+
count,
|
|
1475
|
+
")"
|
|
1476
|
+
] }) }),
|
|
1477
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, muted: !0, children: description })
|
|
1478
|
+
] })
|
|
1479
|
+
] })
|
|
1480
|
+
}
|
|
1481
|
+
);
|
|
1482
|
+
}
|
|
1265
1483
|
function ResyncMetadataDialog(props) {
|
|
1266
|
-
const { resyncState } = props,
|
|
1484
|
+
const { resyncState } = props, videosToUpdate = props.matchedAssets?.filter((m) => m.muxAsset).length || 0, videosWithEmptyOrPlaceholder = props.matchedAssets?.filter(
|
|
1267
1485
|
(m) => m.muxAsset && m.muxTitle && isEmptyOrPlaceholderTitle(m.currentTitle, m.muxAsset.id)
|
|
1268
|
-
).length || 0
|
|
1486
|
+
).length || 0, hasEmptyTitles = videosWithEmptyOrPlaceholder > 0, defaultOption = hasEmptyTitles ? "fillEmpty" : "syncTitles", [selectedOption, setSelectedOption] = React.useState(defaultOption), canTriggerResync = resyncState === "idle" || resyncState === "error", isResyncing = resyncState === "syncing", isDone = resyncState === "done", isLoading = props.muxAssets.loading || props.sanityAssetsLoading, handleSync = () => {
|
|
1487
|
+
switch (selectedOption) {
|
|
1488
|
+
case "fillEmpty":
|
|
1489
|
+
props.syncOnlyEmpty();
|
|
1490
|
+
break;
|
|
1491
|
+
case "syncTitles":
|
|
1492
|
+
props.syncAllVideos();
|
|
1493
|
+
break;
|
|
1494
|
+
case "fullResync":
|
|
1495
|
+
props.syncFullData();
|
|
1496
|
+
break;
|
|
1497
|
+
}
|
|
1498
|
+
};
|
|
1269
1499
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1270
1500
|
ui.Dialog,
|
|
1271
1501
|
{
|
|
1272
1502
|
animate: !0,
|
|
1273
|
-
header: "
|
|
1503
|
+
header: "Sync with Mux",
|
|
1274
1504
|
zOffset: DIALOGS_Z_INDEX,
|
|
1275
1505
|
id: "resync-metadata-dialog",
|
|
1276
1506
|
onClose: props.closeDialog,
|
|
1277
1507
|
onClickOutside: props.closeDialog,
|
|
1278
1508
|
width: 1,
|
|
1279
1509
|
position: "fixed",
|
|
1280
|
-
footer: !isDone && /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { padding: 3, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { justify: "
|
|
1510
|
+
footer: !isDone && /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { padding: 3, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { justify: "flex-end", gap: 2, children: [
|
|
1281
1511
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1282
1512
|
ui.Button,
|
|
1283
1513
|
{
|
|
@@ -1285,97 +1515,104 @@ function ResyncMetadataDialog(props) {
|
|
|
1285
1515
|
padding: 3,
|
|
1286
1516
|
mode: "ghost",
|
|
1287
1517
|
text: "Cancel",
|
|
1288
|
-
tone: "critical",
|
|
1289
1518
|
onClick: props.closeDialog,
|
|
1290
1519
|
disabled: isResyncing
|
|
1291
1520
|
}
|
|
1292
1521
|
),
|
|
1293
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1307
|
-
ui.Button,
|
|
1308
|
-
{
|
|
1309
|
-
icon: icons.SyncIcon,
|
|
1310
|
-
fontSize: 2,
|
|
1311
|
-
padding: 3,
|
|
1312
|
-
mode: "ghost",
|
|
1313
|
-
text: `Update all (${videosToUpdate})`,
|
|
1314
|
-
tone: "positive",
|
|
1315
|
-
onClick: props.syncAllVideos,
|
|
1316
|
-
iconRight: isResyncing && ui.Spinner,
|
|
1317
|
-
disabled: !canTriggerResync
|
|
1318
|
-
}
|
|
1319
|
-
)
|
|
1320
|
-
] })
|
|
1522
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1523
|
+
ui.Button,
|
|
1524
|
+
{
|
|
1525
|
+
icon: icons.SyncIcon,
|
|
1526
|
+
fontSize: 2,
|
|
1527
|
+
padding: 3,
|
|
1528
|
+
text: "Run sync",
|
|
1529
|
+
tone: "primary",
|
|
1530
|
+
onClick: handleSync,
|
|
1531
|
+
iconRight: isResyncing && ui.Spinner,
|
|
1532
|
+
disabled: !canTriggerResync || isLoading
|
|
1533
|
+
}
|
|
1534
|
+
)
|
|
1321
1535
|
] }) }),
|
|
1322
1536
|
children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Box, { padding: 4, children: [
|
|
1323
|
-
|
|
1537
|
+
isLoading && /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { tone: "primary", marginBottom: 4, padding: 3, border: !0, radius: 2, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 4, children: [
|
|
1324
1538
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, { muted: !0, size: 4 }),
|
|
1325
1539
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, children: [
|
|
1326
1540
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, weight: "semibold", children: "Loading assets from Mux" }),
|
|
1327
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, children: "This may take a while." })
|
|
1541
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, muted: !0, children: "This may take a while." })
|
|
1328
1542
|
] })
|
|
1329
1543
|
] }) }),
|
|
1330
|
-
props.muxAssets.error && /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { tone: "critical", marginBottom:
|
|
1544
|
+
props.muxAssets.error && /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { tone: "critical", marginBottom: 4, padding: 3, border: !0, radius: 2, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, children: [
|
|
1331
1545
|
/* @__PURE__ */ jsxRuntime.jsx(icons.ErrorOutlineIcon, { fontSize: 36 }),
|
|
1332
1546
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, children: [
|
|
1333
1547
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, weight: "semibold", children: "There was an error getting data from Mux" }),
|
|
1334
1548
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, children: "Please try again or contact a developer for help." })
|
|
1335
1549
|
] })
|
|
1336
1550
|
] }) }),
|
|
1337
|
-
resyncState === "syncing" && /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { tone: "primary", marginBottom:
|
|
1551
|
+
resyncState === "syncing" && /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { tone: "primary", marginBottom: 4, padding: 3, border: !0, radius: 2, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 4, children: [
|
|
1338
1552
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, { muted: !0, size: 4 }),
|
|
1339
1553
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, children: [
|
|
1340
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, weight: "semibold", children: "
|
|
1341
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, children: "
|
|
1554
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, weight: "semibold", children: "Syncing metadata" }),
|
|
1555
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, muted: !0, children: "Updating videos from Mux..." })
|
|
1342
1556
|
] })
|
|
1343
1557
|
] }) }),
|
|
1344
|
-
resyncState === "error" && /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { tone: "critical", marginBottom:
|
|
1558
|
+
resyncState === "error" && /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { tone: "critical", marginBottom: 4, padding: 3, border: !0, radius: 2, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, children: [
|
|
1345
1559
|
/* @__PURE__ */ jsxRuntime.jsx(icons.ErrorOutlineIcon, { fontSize: 36 }),
|
|
1346
1560
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, children: [
|
|
1347
1561
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, weight: "semibold", children: "There was an error syncing metadata" }),
|
|
1348
1562
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, children: props.resyncError ? `Error: ${props.resyncError}` : "Please try again or contact a developer for help." })
|
|
1349
1563
|
] })
|
|
1350
1564
|
] }) }),
|
|
1351
|
-
resyncState === "done" && /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { paddingY: 5,
|
|
1565
|
+
resyncState === "done" && /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { paddingY: 5, space: 3, style: { textAlign: "center" }, children: [
|
|
1352
1566
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(icons.CheckmarkCircleIcon, { fontSize: 48 }) }),
|
|
1353
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { size: 2, children: "
|
|
1354
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, children: "
|
|
1567
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { size: 2, children: "Sync completed" }),
|
|
1568
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, muted: !0, children: "Videos have been updated from Mux." })
|
|
1355
1569
|
] }),
|
|
1356
|
-
|
|
1357
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.
|
|
1358
|
-
"
|
|
1359
|
-
videosToUpdate === 1 ? "is" : "are",
|
|
1360
|
-
" ",
|
|
1570
|
+
!isDone && !isLoading && !props.muxAssets.error && /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 4, children: [
|
|
1571
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: 1, muted: !0, children: [
|
|
1572
|
+
"Found ",
|
|
1361
1573
|
videosToUpdate,
|
|
1362
1574
|
" video",
|
|
1363
1575
|
videosToUpdate === 1 ? "" : "s",
|
|
1364
|
-
"
|
|
1576
|
+
" linked to Mux."
|
|
1365
1577
|
] }),
|
|
1366
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
"
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1578
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
|
|
1579
|
+
hasEmptyTitles && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1580
|
+
OptionCard,
|
|
1581
|
+
{
|
|
1582
|
+
id: "fillEmpty",
|
|
1583
|
+
selected: selectedOption === "fillEmpty",
|
|
1584
|
+
onSelect: setSelectedOption,
|
|
1585
|
+
title: "Fill missing titles only",
|
|
1586
|
+
count: videosWithEmptyOrPlaceholder,
|
|
1587
|
+
description: "Updates only videos without a title or with placeholder titles (e.g., 'Asset #123') using the title from Mux.",
|
|
1588
|
+
disabled: isResyncing
|
|
1589
|
+
}
|
|
1590
|
+
),
|
|
1591
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1592
|
+
OptionCard,
|
|
1593
|
+
{
|
|
1594
|
+
id: "syncTitles",
|
|
1595
|
+
selected: selectedOption === "syncTitles",
|
|
1596
|
+
onSelect: setSelectedOption,
|
|
1597
|
+
title: "Sync all titles",
|
|
1598
|
+
count: videosToUpdate,
|
|
1599
|
+
description: "Replaces the title in Sanity with the title from Mux for all videos.",
|
|
1600
|
+
disabled: isResyncing
|
|
1601
|
+
}
|
|
1602
|
+
),
|
|
1603
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1604
|
+
OptionCard,
|
|
1605
|
+
{
|
|
1606
|
+
id: "fullResync",
|
|
1607
|
+
selected: selectedOption === "fullResync",
|
|
1608
|
+
onSelect: setSelectedOption,
|
|
1609
|
+
title: "Full resync",
|
|
1610
|
+
count: videosToUpdate,
|
|
1611
|
+
description: "Updates all fields from Mux including status, duration, tracks, captions, and renditions.",
|
|
1612
|
+
disabled: isResyncing
|
|
1613
|
+
}
|
|
1614
|
+
)
|
|
1615
|
+
] })
|
|
1379
1616
|
] })
|
|
1380
1617
|
] })
|
|
1381
1618
|
}
|
|
@@ -1384,7 +1621,7 @@ function ResyncMetadataDialog(props) {
|
|
|
1384
1621
|
function ResyncMetadata() {
|
|
1385
1622
|
const resyncMetadata = useResyncMuxMetadata();
|
|
1386
1623
|
if (resyncMetadata.hasSecrets)
|
|
1387
|
-
return resyncMetadata.dialogOpen ? /* @__PURE__ */ jsxRuntime.jsx(ResyncMetadataDialog, { ...resyncMetadata }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { mode: "bleed", text: "
|
|
1624
|
+
return resyncMetadata.dialogOpen ? /* @__PURE__ */ jsxRuntime.jsx(ResyncMetadataDialog, { ...resyncMetadata }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { mode: "bleed", text: "Sync with Mux", onClick: resyncMetadata.openDialog });
|
|
1388
1625
|
}
|
|
1389
1626
|
const CONTEXT_MENU_POPOVER_PROPS = {
|
|
1390
1627
|
constrainSize: !0,
|
|
@@ -1465,6 +1702,55 @@ function StopWatchIcon(props) {
|
|
|
1465
1702
|
}
|
|
1466
1703
|
);
|
|
1467
1704
|
}
|
|
1705
|
+
function useResyncAsset(options) {
|
|
1706
|
+
const client = useClient(), toast = ui.useToast(), [resyncState, setResyncState] = React.useState("idle"), [resyncError, setResyncError] = React.useState(null), showToast = options?.showToast ?? !1, resyncAsset = React.useCallback(
|
|
1707
|
+
async (asset) => {
|
|
1708
|
+
if (!asset.assetId) {
|
|
1709
|
+
showToast && toast.push({
|
|
1710
|
+
title: "Cannot resync",
|
|
1711
|
+
description: "Asset has no Mux ID",
|
|
1712
|
+
status: "error"
|
|
1713
|
+
}), options?.onError?.(new Error("Asset has no Mux ID"));
|
|
1714
|
+
return;
|
|
1715
|
+
}
|
|
1716
|
+
if (!asset._id) {
|
|
1717
|
+
showToast && toast.push({
|
|
1718
|
+
title: "Cannot resync",
|
|
1719
|
+
description: "Asset has no document ID",
|
|
1720
|
+
status: "error"
|
|
1721
|
+
}), options?.onError?.(new Error("Asset has no document ID"));
|
|
1722
|
+
return;
|
|
1723
|
+
}
|
|
1724
|
+
setResyncState("syncing"), setResyncError(null);
|
|
1725
|
+
try {
|
|
1726
|
+
const muxData = (await getAsset(client, asset.assetId)).data, dataWithKeys = addKeysToMuxData(muxData);
|
|
1727
|
+
return await client.patch(asset._id).set({
|
|
1728
|
+
status: muxData.status,
|
|
1729
|
+
data: dataWithKeys,
|
|
1730
|
+
...muxData.meta?.title && { filename: muxData.meta.title }
|
|
1731
|
+
}).commit({ returnDocuments: !1 }), setResyncState("success"), showToast && toast.push({
|
|
1732
|
+
title: "Asset synced",
|
|
1733
|
+
description: "Data has been updated from Mux",
|
|
1734
|
+
status: "success"
|
|
1735
|
+
}), options?.onSuccess?.(muxData), muxData;
|
|
1736
|
+
} catch (error) {
|
|
1737
|
+
setResyncState("error"), setResyncError(error), console.error("Failed to refresh asset data:", error), showToast && toast.push({
|
|
1738
|
+
title: "Sync failed",
|
|
1739
|
+
description: "Could not sync asset from Mux",
|
|
1740
|
+
status: "error"
|
|
1741
|
+
}), options?.onError?.(error);
|
|
1742
|
+
return;
|
|
1743
|
+
}
|
|
1744
|
+
},
|
|
1745
|
+
[client, toast, options, showToast]
|
|
1746
|
+
);
|
|
1747
|
+
return {
|
|
1748
|
+
resyncState,
|
|
1749
|
+
resyncError,
|
|
1750
|
+
resyncAsset,
|
|
1751
|
+
isResyncing: resyncState === "syncing"
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1468
1754
|
function extractErrorMessage(error, defaultMessage = "Failed to process request") {
|
|
1469
1755
|
let message = "";
|
|
1470
1756
|
if (error && typeof error == "object") {
|
|
@@ -1548,9 +1834,9 @@ async function downloadVttFile(client, asset, track) {
|
|
|
1548
1834
|
const playbackId = getPlaybackId(asset);
|
|
1549
1835
|
if (!playbackId)
|
|
1550
1836
|
throw new Error("Playback ID is required");
|
|
1551
|
-
const playbackPolicy = getPlaybackPolicy(asset);
|
|
1837
|
+
const playbackPolicy = getPlaybackPolicy(asset)?.policy;
|
|
1552
1838
|
let downloadUrl = `https://stream.mux.com/${playbackId}/text/${track.id}.vtt`;
|
|
1553
|
-
if (playbackPolicy === "signed") {
|
|
1839
|
+
if (playbackPolicy === "signed" || playbackPolicy === "drm") {
|
|
1554
1840
|
const token = generateJwt(client, playbackId, "v");
|
|
1555
1841
|
downloadUrl += `?token=${token}`;
|
|
1556
1842
|
}
|
|
@@ -2028,7 +2314,7 @@ function EditCaptionDialog({ asset, track, onUpdate, onClose }) {
|
|
|
2028
2314
|
const playbackId = getPlaybackId(asset);
|
|
2029
2315
|
if (!playbackId) return "";
|
|
2030
2316
|
let url = `https://stream.mux.com/${playbackId}/text/${track.id}.vtt`;
|
|
2031
|
-
if (getPlaybackPolicy(asset) === "signed") {
|
|
2317
|
+
if (getPlaybackPolicy(asset)?.policy === "signed") {
|
|
2032
2318
|
const token = generateJwt(client, playbackId, "v");
|
|
2033
2319
|
url += `?token=${token}`;
|
|
2034
2320
|
}
|
|
@@ -2367,20 +2653,7 @@ function TextTracksManager({
|
|
|
2367
2653
|
tracks: propTracks,
|
|
2368
2654
|
collapseTracks = !1
|
|
2369
2655
|
}) {
|
|
2370
|
-
const client = useClient(), toast = ui.useToast(), dialogId = `DeleteCaptionDialog${React.useId()}`, [downloadingTrackId, setDownloadingTrackId] = React.useState(null), [deletingTrackId, setDeletingTrackId] = React.useState(null), [addedTracks, setAddedTracks] = React.useState([]), [updatedTracks, setUpdatedTracks] = React.useState(/* @__PURE__ */ new Map()), [trackActivityOrder, setTrackActivityOrder] = React.useState(/* @__PURE__ */ new Map()), [autogeneratedTrackIds, setAutogeneratedTrackIds] = React.useState(/* @__PURE__ */ new Set()), [trackToDelete, setTrackToDelete] = React.useState(null), [trackToEdit, setTrackToEdit] = React.useState(null), [showAddDialog, setShowAddDialog] = React.useState(!1), [isExpanded, setIsExpanded] = React.useState(!1), MAX_VISIBLE_TRACKS = 4
|
|
2371
|
-
React.useEffect(() => {
|
|
2372
|
-
if (!asset.assetId || !asset._id) return;
|
|
2373
|
-
const assetId = asset.assetId, documentId = asset._id;
|
|
2374
|
-
(async () => {
|
|
2375
|
-
try {
|
|
2376
|
-
const response = await getAsset(client, assetId);
|
|
2377
|
-
await client.patch(documentId).set({ data: response.data, status: response.data.status }).commit();
|
|
2378
|
-
} catch (error) {
|
|
2379
|
-
console.error("Failed to refresh asset data:", error);
|
|
2380
|
-
}
|
|
2381
|
-
})();
|
|
2382
|
-
}, [asset.assetId, asset._id, client]);
|
|
2383
|
-
const activeTracks = (propTracks || asset.data?.tracks?.filter((track) => track.type === "text") || []).filter(
|
|
2656
|
+
const client = useClient(), toast = ui.useToast(), dialogId = `DeleteCaptionDialog${React.useId()}`, { resyncAsset } = useResyncAsset(), [downloadingTrackId, setDownloadingTrackId] = React.useState(null), [deletingTrackId, setDeletingTrackId] = React.useState(null), [addedTracks, setAddedTracks] = React.useState([]), [updatedTracks, setUpdatedTracks] = React.useState(/* @__PURE__ */ new Map()), [trackActivityOrder, setTrackActivityOrder] = React.useState(/* @__PURE__ */ new Map()), [autogeneratedTrackIds, setAutogeneratedTrackIds] = React.useState(/* @__PURE__ */ new Set()), [trackToDelete, setTrackToDelete] = React.useState(null), [trackToEdit, setTrackToEdit] = React.useState(null), [showAddDialog, setShowAddDialog] = React.useState(!1), [isExpanded, setIsExpanded] = React.useState(!1), MAX_VISIBLE_TRACKS = 4, activeTracks = (propTracks || asset.data?.tracks?.filter((track) => track.type === "text") || []).filter(
|
|
2384
2657
|
(track) => track.id && (track.status === "ready" || track.status === "preparing" || track.status === "errored")
|
|
2385
2658
|
), allTracks = React.useMemo(() => {
|
|
2386
2659
|
const tracksWithUpdates = activeTracks.map((track) => updatedTracks.get(track.id) || track), isMockTrackReplaced = (mockTrack, realTracksList) => !mockTrack.id || !mockTrack.id.startsWith("generating-") ? !1 : realTracksList.some((realTrack) => {
|
|
@@ -2411,11 +2684,11 @@ function TextTracksManager({
|
|
|
2411
2684
|
}, [activeTracks, addedTracks]), React.useEffect(() => {
|
|
2412
2685
|
if (allTracks.filter((track) => track.status === "preparing").length === 0 || !asset.assetId || !asset._id)
|
|
2413
2686
|
return;
|
|
2414
|
-
const
|
|
2687
|
+
const interval = setInterval(async () => {
|
|
2415
2688
|
try {
|
|
2416
|
-
const
|
|
2417
|
-
|
|
2418
|
-
const fetchedTracks =
|
|
2689
|
+
const muxData = await resyncAsset(asset);
|
|
2690
|
+
if (!muxData) return;
|
|
2691
|
+
const fetchedTracks = muxData.tracks?.filter((track) => track.type === "text") || [], isMockTrackReplaced = (mockTrack, fetchedTracksList) => !mockTrack.id || !mockTrack.id.startsWith("generating-") ? !1 : fetchedTracksList.some((realTrack) => {
|
|
2419
2692
|
const nameMatches = realTrack.name === mockTrack.name, languageMatches = realTrack.language_code === mockTrack.language_code;
|
|
2420
2693
|
return !nameMatches || !languageMatches ? !1 : realTrack.status === "ready" ? realTrack.text_source === "generated_live" || realTrack.text_source === "generated_live_final" || realTrack.text_source === "generated_vod" : realTrack.status === "preparing";
|
|
2421
2694
|
}), newAutogeneratedIds = /* @__PURE__ */ new Set();
|
|
@@ -2452,7 +2725,7 @@ function TextTracksManager({
|
|
|
2452
2725
|
}
|
|
2453
2726
|
}, 3e3);
|
|
2454
2727
|
return () => clearInterval(interval);
|
|
2455
|
-
}, [allTracks, asset
|
|
2728
|
+
}, [allTracks, asset, resyncAsset]);
|
|
2456
2729
|
const visibleTracks = allTracks.filter(
|
|
2457
2730
|
(track) => track.status === "ready" || track.status === "preparing" || track.status === "errored"
|
|
2458
2731
|
).sort((a2, b) => {
|
|
@@ -2488,14 +2761,7 @@ function TextTracksManager({
|
|
|
2488
2761
|
try {
|
|
2489
2762
|
if (!asset.assetId)
|
|
2490
2763
|
throw new Error("Asset ID is required");
|
|
2491
|
-
|
|
2492
|
-
try {
|
|
2493
|
-
const response = await getAsset(client, asset.assetId);
|
|
2494
|
-
await client.patch(asset._id).set({ data: response.data, status: response.data.status }).commit();
|
|
2495
|
-
} catch (refreshError) {
|
|
2496
|
-
console.error("Failed to refresh asset data:", refreshError);
|
|
2497
|
-
}
|
|
2498
|
-
toast.push({
|
|
2764
|
+
await deleteTextTrack(client, asset.assetId, track.id), await resyncAsset(asset), toast.push({
|
|
2499
2765
|
title: "Successfully deleted caption track",
|
|
2500
2766
|
status: "success"
|
|
2501
2767
|
}), setAddedTracks((prev) => prev.filter((t) => t.id !== track.id)), setUpdatedTracks((prev) => {
|
|
@@ -2523,7 +2789,7 @@ function TextTracksManager({
|
|
|
2523
2789
|
return newMap.set(track.id, prev.size + 1), newMap;
|
|
2524
2790
|
}), setShowAddDialog(!1);
|
|
2525
2791
|
}, handleUpdateTrack = async (updatedTrack, oldTrackId) => {
|
|
2526
|
-
|
|
2792
|
+
oldTrackId && (setAddedTracks((prev) => prev.filter((t) => t.id !== oldTrackId)), setUpdatedTracks((prev) => {
|
|
2527
2793
|
const newMap = new Map(prev);
|
|
2528
2794
|
return newMap.delete(oldTrackId), newMap;
|
|
2529
2795
|
}), setTrackActivityOrder((prev) => {
|
|
@@ -2538,13 +2804,7 @@ function TextTracksManager({
|
|
|
2538
2804
|
}), setTrackActivityOrder((prev) => {
|
|
2539
2805
|
const newMap = new Map(prev);
|
|
2540
2806
|
return newMap.set(updatedTrack.id, prev.size + 1), newMap;
|
|
2541
|
-
}), setTrackToEdit(null),
|
|
2542
|
-
try {
|
|
2543
|
-
const response = await getAsset(client, asset.assetId);
|
|
2544
|
-
await client.patch(asset._id).set({ data: response.data, status: response.data.status }).commit();
|
|
2545
|
-
} catch (refreshError) {
|
|
2546
|
-
console.error("Failed to refresh asset data:", refreshError);
|
|
2547
|
-
}
|
|
2807
|
+
}), setTrackToEdit(null), await resyncAsset(asset);
|
|
2548
2808
|
}, getTrackSourceLabel = (track) => track.id && track.id.startsWith("generating-") || track.id && autogeneratedTrackIds.has(track.id) || track.text_source === "generated_live_final" || track.text_source === "generated_live" || track.text_source === "generated_vod" ? "Auto-generated" : track.text_source === "uploaded" ? "Uploaded" : "Custom";
|
|
2549
2809
|
if (visibleTracks.length === 0 && !showAddDialog)
|
|
2550
2810
|
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
|
|
@@ -2684,13 +2944,13 @@ const DialogStateContext = React.createContext({
|
|
|
2684
2944
|
setDialogState,
|
|
2685
2945
|
children
|
|
2686
2946
|
}) => /* @__PURE__ */ jsxRuntime.jsx(DialogStateContext.Provider, { value: { dialogState, setDialogState }, children }), useDialogStateContext = () => React.useContext(DialogStateContext);
|
|
2687
|
-
function getVideoSrc({
|
|
2688
|
-
const
|
|
2689
|
-
if (
|
|
2690
|
-
const token = generateJwt(client,
|
|
2947
|
+
function getVideoSrc({ client, muxPlaybackId: muxPlaybackId2 }) {
|
|
2948
|
+
const searchParams = new URLSearchParams();
|
|
2949
|
+
if (muxPlaybackId2.policy === "signed" || muxPlaybackId2.policy === "drm") {
|
|
2950
|
+
const token = generateJwt(client, muxPlaybackId2.id, "v");
|
|
2691
2951
|
searchParams.set("token", token);
|
|
2692
2952
|
}
|
|
2693
|
-
return `https://stream.mux.com/${
|
|
2953
|
+
return `https://stream.mux.com/${muxPlaybackId2.id}.m3u8?${searchParams}`;
|
|
2694
2954
|
}
|
|
2695
2955
|
function CaptionsDialog({ asset }) {
|
|
2696
2956
|
const { setDialogState } = useDialogStateContext(), dialogId = `CaptionsDialog${React.useId()}`;
|
|
@@ -2800,24 +3060,56 @@ function VideoPlayer({
|
|
|
2800
3060
|
hlsConfig,
|
|
2801
3061
|
...props
|
|
2802
3062
|
}) {
|
|
2803
|
-
const client = useClient(), { dialogState } = useDialogStateContext(), isAudio = assetIsAudio(asset), muxPlayer = React.useRef(null), {
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
3063
|
+
const client = useClient(), { dialogState } = useDialogStateContext(), isAudio = assetIsAudio(asset), muxPlayer = React.useRef(null), [error, setError] = React.useState(), playbackId = React.useMemo(() => {
|
|
3064
|
+
try {
|
|
3065
|
+
return getPlaybackId(asset, ["public", "signed", "drm"]);
|
|
3066
|
+
} catch {
|
|
3067
|
+
setError(new TypeError("Asset has no playback ID"));
|
|
3068
|
+
return;
|
|
3069
|
+
}
|
|
3070
|
+
}, [asset]), muxPlaybackId2 = React.useMemo(() => {
|
|
3071
|
+
if (playbackId)
|
|
3072
|
+
return getPlaybackPolicyById(asset, playbackId);
|
|
3073
|
+
}, [asset, playbackId]), src = React.useMemo(() => {
|
|
3074
|
+
if (playbackId && muxPlaybackId2)
|
|
3075
|
+
return tryWithSuspend(
|
|
3076
|
+
() => getVideoSrc({ muxPlaybackId: muxPlaybackId2, client }),
|
|
3077
|
+
(e) => {
|
|
3078
|
+
setError(e);
|
|
3079
|
+
}
|
|
3080
|
+
);
|
|
3081
|
+
}, [muxPlaybackId2, playbackId, client]), poster = React.useMemo(() => tryWithSuspend(
|
|
3082
|
+
() => getPosterSrc({ asset, client, width: thumbnailWidth }),
|
|
3083
|
+
(e) => {
|
|
3084
|
+
setError(e);
|
|
3085
|
+
}
|
|
3086
|
+
), [asset, client, thumbnailWidth]), signedToken = React.useMemo(() => {
|
|
2808
3087
|
try {
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
return { error: error2 };
|
|
3088
|
+
return new URL(src).searchParams.get("token");
|
|
3089
|
+
} catch {
|
|
3090
|
+
return;
|
|
2813
3091
|
}
|
|
2814
|
-
}, [
|
|
3092
|
+
}, [src]), drmToken = React.useMemo(() => {
|
|
3093
|
+
if (playbackId && muxPlaybackId2?.policy === "drm")
|
|
3094
|
+
return tryWithSuspend(
|
|
3095
|
+
() => generateJwt(client, playbackId, "d"),
|
|
3096
|
+
(e) => {
|
|
3097
|
+
setError(e);
|
|
3098
|
+
}
|
|
3099
|
+
);
|
|
3100
|
+
}, [client, muxPlaybackId2?.policy, playbackId]), tokens = React.useMemo(() => {
|
|
2815
3101
|
try {
|
|
2816
|
-
|
|
3102
|
+
const partialTokens = {
|
|
3103
|
+
playback: void 0,
|
|
3104
|
+
thumbnail: void 0,
|
|
3105
|
+
storyboard: void 0,
|
|
3106
|
+
drm: void 0
|
|
3107
|
+
};
|
|
3108
|
+
return signedToken && (partialTokens.playback = signedToken, partialTokens.thumbnail = signedToken, partialTokens.storyboard = signedToken), drmToken && (partialTokens.drm = drmToken), { ...partialTokens };
|
|
2817
3109
|
} catch {
|
|
2818
|
-
return
|
|
3110
|
+
return;
|
|
2819
3111
|
}
|
|
2820
|
-
}, [
|
|
3112
|
+
}, [signedToken, drmToken]), [width, height] = (asset?.data?.aspect_ratio ?? "16:9").split(":").map(Number), targetAspectRatio = props.forceAspectRatio || (Number.isNaN(width) ? 16 / 9 : width / height);
|
|
2821
3113
|
let aspectRatio = Math.max(MIN_ASPECT_RATIO, targetAspectRatio);
|
|
2822
3114
|
return isAudio && (aspectRatio = props.forceAspectRatio ? (
|
|
2823
3115
|
// Make it wider when forcing aspect ratio to balance with videos' rendering height (audio players overflow a bit)
|
|
@@ -2833,7 +3125,7 @@ function VideoPlayer({
|
|
|
2833
3125
|
...isAudio && { display: "flex", alignItems: "flex-end" }
|
|
2834
3126
|
},
|
|
2835
3127
|
children: [
|
|
2836
|
-
|
|
3128
|
+
src && poster && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2837
3129
|
isAudio && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2838
3130
|
AudioIcon,
|
|
2839
3131
|
{
|
|
@@ -2848,34 +3140,36 @@ function VideoPlayer({
|
|
|
2848
3140
|
}
|
|
2849
3141
|
}
|
|
2850
3142
|
),
|
|
2851
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
3143
|
+
/* @__PURE__ */ jsxRuntime.jsxs(React.Suspense, { fallback: null, children: [
|
|
3144
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3145
|
+
MuxPlayer__default.default,
|
|
3146
|
+
{
|
|
3147
|
+
poster: isAudio ? void 0 : poster,
|
|
3148
|
+
ref: muxPlayer,
|
|
3149
|
+
...props,
|
|
3150
|
+
playsInline: !0,
|
|
3151
|
+
playbackId,
|
|
3152
|
+
tokens,
|
|
3153
|
+
preload: "metadata",
|
|
3154
|
+
crossOrigin: "anonymous",
|
|
3155
|
+
metadata: {
|
|
3156
|
+
player_name: "Sanity Admin Dashboard",
|
|
3157
|
+
player_version: "2.16.0",
|
|
3158
|
+
page_type: "Preview Player"
|
|
3159
|
+
},
|
|
3160
|
+
audio: isAudio,
|
|
3161
|
+
_hlsConfig: hlsConfig,
|
|
3162
|
+
style: {
|
|
3163
|
+
...!isAudio && { height: "100%" },
|
|
3164
|
+
width: "100%",
|
|
3165
|
+
display: "block",
|
|
3166
|
+
objectFit: "contain",
|
|
3167
|
+
...isAudio && { alignSelf: "end" }
|
|
3168
|
+
}
|
|
2875
3169
|
}
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
3170
|
+
),
|
|
3171
|
+
children
|
|
3172
|
+
] })
|
|
2879
3173
|
] }),
|
|
2880
3174
|
error ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2881
3175
|
"div",
|
|
@@ -3167,6 +3461,7 @@ function getVideoMetadata(doc) {
|
|
|
3167
3461
|
playbackId: doc.playbackId,
|
|
3168
3462
|
createdAt: date,
|
|
3169
3463
|
duration: doc.data?.duration ? formatSeconds(doc.data?.duration) : void 0,
|
|
3464
|
+
playback_ids: doc.data?.playback_ids,
|
|
3170
3465
|
aspect_ratio: doc.data?.aspect_ratio,
|
|
3171
3466
|
max_stored_resolution: doc.data?.max_stored_resolution,
|
|
3172
3467
|
max_stored_frame_rate: doc.data?.max_stored_frame_rate,
|
|
@@ -3176,7 +3471,10 @@ function getVideoMetadata(doc) {
|
|
|
3176
3471
|
function useVideoDetails(props) {
|
|
3177
3472
|
const documentStore = sanity.useDocumentStore(), toast = ui.useToast(), client = useClient(), [references, referencesLoading] = useDocReferences(
|
|
3178
3473
|
React.useMemo(() => ({ documentStore, id: props.asset._id }), [documentStore, props.asset._id])
|
|
3179
|
-
), [originalAsset, setOriginalAsset] = React.useState(() => props.asset), [filename, setFilename] = React.useState(props.asset.filename), modified = filename !== originalAsset.filename, displayInfo = getVideoMetadata({ ...props.asset, filename }), [state, setState] = React.useState("idle");
|
|
3474
|
+
), [originalAsset, setOriginalAsset] = React.useState(() => props.asset), [filename, setFilename] = React.useState(props.asset.filename), modified = filename !== originalAsset.filename, displayInfo = getVideoMetadata({ ...props.asset, filename }), [state, setState] = React.useState("idle"), { resyncAsset, isResyncing } = useResyncAsset({ showToast: !0 });
|
|
3475
|
+
async function handleResync() {
|
|
3476
|
+
state === "idle" && (setState("resyncing"), await resyncAsset(props.asset), setState("idle"));
|
|
3477
|
+
}
|
|
3180
3478
|
function handleClose() {
|
|
3181
3479
|
if (state === "idle") {
|
|
3182
3480
|
if (modified) {
|
|
@@ -3219,7 +3517,9 @@ function useVideoDetails(props) {
|
|
|
3219
3517
|
setState,
|
|
3220
3518
|
handleClose,
|
|
3221
3519
|
confirmClose,
|
|
3222
|
-
saveChanges
|
|
3520
|
+
saveChanges,
|
|
3521
|
+
handleResync,
|
|
3522
|
+
isResyncing
|
|
3223
3523
|
};
|
|
3224
3524
|
}
|
|
3225
3525
|
const AssetInput = (props) => /* @__PURE__ */ jsxRuntime.jsx(FormField$1, { title: props.label, description: props.description, inputId: props.label, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -3243,7 +3543,9 @@ const AssetInput = (props) => /* @__PURE__ */ jsxRuntime.jsx(FormField$1, { titl
|
|
|
3243
3543
|
setState,
|
|
3244
3544
|
handleClose,
|
|
3245
3545
|
confirmClose,
|
|
3246
|
-
saveChanges
|
|
3546
|
+
saveChanges,
|
|
3547
|
+
handleResync,
|
|
3548
|
+
isResyncing
|
|
3247
3549
|
} = useVideoDetails(props), isSaving = state === "saving", [containerHeight, setContainerHeight] = React.useState(null), contentsRef = React__default.default.useRef(null);
|
|
3248
3550
|
return React.useEffect(() => {
|
|
3249
3551
|
!contentsRef.current || !("getBoundingClientRect" in contentsRef.current) || setContainerHeight(contentsRef.current.getBoundingClientRect().height);
|
|
@@ -3259,19 +3561,35 @@ const AssetInput = (props) => /* @__PURE__ */ jsxRuntime.jsx(FormField$1, { titl
|
|
|
3259
3561
|
width: 2,
|
|
3260
3562
|
position: "fixed",
|
|
3261
3563
|
footer: /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { padding: 3, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { justify: "space-between", align: "center", children: [
|
|
3262
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3564
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gap: 2, children: [
|
|
3565
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3566
|
+
ui.Button,
|
|
3567
|
+
{
|
|
3568
|
+
icon: icons.TrashIcon,
|
|
3569
|
+
fontSize: 2,
|
|
3570
|
+
padding: 3,
|
|
3571
|
+
mode: "bleed",
|
|
3572
|
+
text: "Delete",
|
|
3573
|
+
tone: "critical",
|
|
3574
|
+
onClick: () => setState("deleting"),
|
|
3575
|
+
disabled: isSaving || isResyncing
|
|
3576
|
+
}
|
|
3577
|
+
),
|
|
3578
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3579
|
+
ui.Button,
|
|
3580
|
+
{
|
|
3581
|
+
icon: icons.SyncIcon,
|
|
3582
|
+
fontSize: 2,
|
|
3583
|
+
padding: 3,
|
|
3584
|
+
mode: "bleed",
|
|
3585
|
+
text: "Resync",
|
|
3586
|
+
tone: "primary",
|
|
3587
|
+
onClick: handleResync,
|
|
3588
|
+
disabled: isSaving || isResyncing,
|
|
3589
|
+
iconRight: isResyncing && ui.Spinner
|
|
3590
|
+
}
|
|
3591
|
+
)
|
|
3592
|
+
] }),
|
|
3275
3593
|
modified && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3276
3594
|
ui.Button,
|
|
3277
3595
|
{
|
|
@@ -3283,7 +3601,7 @@ const AssetInput = (props) => /* @__PURE__ */ jsxRuntime.jsx(FormField$1, { titl
|
|
|
3283
3601
|
tone: "positive",
|
|
3284
3602
|
onClick: saveChanges,
|
|
3285
3603
|
iconRight: isSaving && ui.Spinner,
|
|
3286
|
-
disabled: isSaving
|
|
3604
|
+
disabled: isSaving || isResyncing
|
|
3287
3605
|
}
|
|
3288
3606
|
)
|
|
3289
3607
|
] }) }),
|
|
@@ -3469,14 +3787,7 @@ const AssetInput = (props) => /* @__PURE__ */ jsxRuntime.jsx(FormField$1, { titl
|
|
|
3469
3787
|
),
|
|
3470
3788
|
/* @__PURE__ */ jsxRuntime.jsx(IconInfo, { text: `Mux ID:
|
|
3471
3789
|
${displayInfo.id}`, icon: icons.TagIcon, size: 2 }),
|
|
3472
|
-
|
|
3473
|
-
IconInfo,
|
|
3474
|
-
{
|
|
3475
|
-
text: `Playback ID: ${displayInfo.playbackId}`,
|
|
3476
|
-
icon: icons.TagIcon,
|
|
3477
|
-
size: 2
|
|
3478
|
-
}
|
|
3479
|
-
)
|
|
3790
|
+
/* @__PURE__ */ jsxRuntime.jsx(PlaybackIds, { playback_ids: displayInfo.playback_ids })
|
|
3480
3791
|
] })
|
|
3481
3792
|
] })
|
|
3482
3793
|
}
|
|
@@ -3499,9 +3810,28 @@ ${displayInfo.id}`, icon: icons.TagIcon, size: 2 }),
|
|
|
3499
3810
|
]
|
|
3500
3811
|
}
|
|
3501
3812
|
);
|
|
3502
|
-
},
|
|
3503
|
-
|
|
3504
|
-
|
|
3813
|
+
}, PlaybackIds = ({ playback_ids }) => playback_ids ? playback_ids.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3814
|
+
IconInfo,
|
|
3815
|
+
{
|
|
3816
|
+
text: `Playback ID [${policyToText(entry.policy)}]: ${entry.id}`,
|
|
3817
|
+
icon: icons.TagIcon,
|
|
3818
|
+
size: 2
|
|
3819
|
+
},
|
|
3820
|
+
entry.id
|
|
3821
|
+
)) : /* @__PURE__ */ jsxRuntime.jsx(IconInfo, { text: "No Playback ID", icon: icons.TagIcon, size: 2 }), policyToText = (policy) => {
|
|
3822
|
+
switch (policy) {
|
|
3823
|
+
case "drm":
|
|
3824
|
+
return "DRM";
|
|
3825
|
+
case "signed":
|
|
3826
|
+
return "Signed";
|
|
3827
|
+
case "public":
|
|
3828
|
+
return "Public";
|
|
3829
|
+
default:
|
|
3830
|
+
return policy;
|
|
3831
|
+
}
|
|
3832
|
+
}, VideoMetadata = (props) => {
|
|
3833
|
+
if (!props.asset)
|
|
3834
|
+
return null;
|
|
3505
3835
|
const displayInfo = getVideoMetadata(props.asset);
|
|
3506
3836
|
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, children: [
|
|
3507
3837
|
displayInfo.title && /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -3592,10 +3922,12 @@ function VideoInBrowser({
|
|
|
3592
3922
|
onEdit,
|
|
3593
3923
|
asset
|
|
3594
3924
|
}) {
|
|
3595
|
-
const [renderVideo, setRenderVideo] = React.useState(!1), select = React__default.default.useCallback(() => onSelect?.(asset), [onSelect, asset]), edit = React__default.default.useCallback(() => onEdit?.(asset), [onEdit, asset]);
|
|
3925
|
+
const [renderVideo, setRenderVideo] = React.useState(!1), select = React__default.default.useCallback(() => onSelect?.(asset), [onSelect, asset]), edit = React__default.default.useCallback(() => onEdit?.(asset), [onEdit, asset]), { hasShownWarning } = useDrmPlaybackWarningContext();
|
|
3596
3926
|
if (!asset)
|
|
3597
3927
|
return null;
|
|
3598
|
-
const playbackPolicy = getPlaybackPolicy(asset)
|
|
3928
|
+
const playbackPolicy = getPlaybackPolicy(asset), onClickPlay = () => {
|
|
3929
|
+
playbackPolicy?.policy === "drm" && !hasShownWarning ? setRenderVideo("pre-render-warn") : setRenderVideo("render-video");
|
|
3930
|
+
};
|
|
3599
3931
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3600
3932
|
ui.Card,
|
|
3601
3933
|
{
|
|
@@ -3607,7 +3939,7 @@ function VideoInBrowser({
|
|
|
3607
3939
|
position: "relative"
|
|
3608
3940
|
},
|
|
3609
3941
|
children: [
|
|
3610
|
-
playbackPolicy === "signed" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3942
|
+
playbackPolicy?.policy === "signed" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3611
3943
|
ui.Tooltip,
|
|
3612
3944
|
{
|
|
3613
3945
|
animate: !0,
|
|
@@ -3624,7 +3956,7 @@ function VideoInBrowser({
|
|
|
3624
3956
|
position: "absolute",
|
|
3625
3957
|
left: "1em",
|
|
3626
3958
|
top: "1em",
|
|
3627
|
-
zIndex:
|
|
3959
|
+
zIndex: 11
|
|
3628
3960
|
},
|
|
3629
3961
|
padding: 2,
|
|
3630
3962
|
border: !0,
|
|
@@ -3633,6 +3965,32 @@ function VideoInBrowser({
|
|
|
3633
3965
|
)
|
|
3634
3966
|
}
|
|
3635
3967
|
),
|
|
3968
|
+
playbackPolicy?.policy === "drm" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3969
|
+
ui.Tooltip,
|
|
3970
|
+
{
|
|
3971
|
+
animate: !0,
|
|
3972
|
+
content: /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { padding: 2, radius: 2, children: /* @__PURE__ */ jsxRuntime.jsx(IconInfo, { icon: icons.LockIcon, text: "DRM playback policy", size: 2 }) }),
|
|
3973
|
+
placement: "right",
|
|
3974
|
+
fallbackPlacements: ["top", "bottom"],
|
|
3975
|
+
portal: !0,
|
|
3976
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3977
|
+
ui.Card,
|
|
3978
|
+
{
|
|
3979
|
+
tone: "caution",
|
|
3980
|
+
style: {
|
|
3981
|
+
borderRadius: "0.25rem",
|
|
3982
|
+
position: "absolute",
|
|
3983
|
+
left: "1em",
|
|
3984
|
+
top: "1em",
|
|
3985
|
+
zIndex: 11
|
|
3986
|
+
},
|
|
3987
|
+
padding: 2,
|
|
3988
|
+
border: !0,
|
|
3989
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { muted: !0, size: 1, weight: "semibold", style: { color: "var(--card-icon-color)" }, children: "DRM" })
|
|
3990
|
+
}
|
|
3991
|
+
)
|
|
3992
|
+
}
|
|
3993
|
+
),
|
|
3636
3994
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3637
3995
|
ui.Stack,
|
|
3638
3996
|
{
|
|
@@ -3642,7 +4000,15 @@ function VideoInBrowser({
|
|
|
3642
4000
|
gridTemplateRows: "min-content min-content 1fr"
|
|
3643
4001
|
},
|
|
3644
4002
|
children: [
|
|
3645
|
-
renderVideo
|
|
4003
|
+
renderVideo === "pre-render-warn" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4004
|
+
DRMWarningDialog,
|
|
4005
|
+
{
|
|
4006
|
+
onClose: () => {
|
|
4007
|
+
setRenderVideo("render-video");
|
|
4008
|
+
}
|
|
4009
|
+
}
|
|
4010
|
+
),
|
|
4011
|
+
renderVideo === "render-video" ? /* @__PURE__ */ jsxRuntime.jsx(VideoPlayer, { asset, autoPlay: !0, forceAspectRatio: THUMBNAIL_ASPECT_RATIO }) : /* @__PURE__ */ jsxRuntime.jsxs(PlayButton, { onClick: onClickPlay, children: [
|
|
3646
4012
|
/* @__PURE__ */ jsxRuntime.jsx("div", { "data-play": !0, children: /* @__PURE__ */ jsxRuntime.jsx(icons.PlayIcon, {}) }),
|
|
3647
4013
|
assetIsAudio(asset) ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3648
4014
|
"div",
|
|
@@ -3704,12 +4070,12 @@ function VideoInBrowser({
|
|
|
3704
4070
|
}
|
|
3705
4071
|
);
|
|
3706
4072
|
}
|
|
3707
|
-
function VideosBrowser({ onSelect }) {
|
|
4073
|
+
function VideosBrowser({ onSelect, config }) {
|
|
3708
4074
|
const { assets, isLoading, searchQuery, setSearchQuery, setSort, sort } = useAssets(), [page, setPage] = React.useState(0), pageLimit = 20, pageTotal = Math.floor(assets.length / pageLimit) + 1, [editedAsset, setEditedAsset] = React.useState(null), freshEditedAsset = React.useMemo(
|
|
3709
4075
|
() => assets.find((a2) => a2._id === editedAsset?._id) || editedAsset,
|
|
3710
4076
|
[editedAsset, assets]
|
|
3711
4077
|
), pageStart = page * pageLimit, pageEnd = pageStart + pageLimit;
|
|
3712
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4078
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(DrmPlaybackWarningContextProvider, { config, children: [
|
|
3713
4079
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { padding: 4, space: 4, style: { minHeight: "50vh" }, children: [
|
|
3714
4080
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { justify: "space-between", align: "center", children: [
|
|
3715
4081
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 3, children: [
|
|
@@ -3723,7 +4089,7 @@ function VideosBrowser({ onSelect }) {
|
|
|
3723
4089
|
}
|
|
3724
4090
|
),
|
|
3725
4091
|
/* @__PURE__ */ jsxRuntime.jsx(SelectSortOptions, { setSort, sort }),
|
|
3726
|
-
/* @__PURE__ */ jsxRuntime.jsx(PageSelector, { page, setPage, total: pageTotal
|
|
4092
|
+
/* @__PURE__ */ jsxRuntime.jsx(PageSelector, { page, setPage, total: pageTotal })
|
|
3727
4093
|
] }),
|
|
3728
4094
|
(onSelect ? "input" : "tool") == "tool" && /* @__PURE__ */ jsxRuntime.jsxs(ui.Inline, { space: 2, children: [
|
|
3729
4095
|
/* @__PURE__ */ jsxRuntime.jsx(ImportVideosFromMux, {}),
|
|
@@ -3764,7 +4130,7 @@ function VideosBrowser({ onSelect }) {
|
|
|
3764
4130
|
freshEditedAsset && /* @__PURE__ */ jsxRuntime.jsx(VideoDetails, { closeDialog: () => setEditedAsset(null), asset: freshEditedAsset })
|
|
3765
4131
|
] });
|
|
3766
4132
|
}
|
|
3767
|
-
const StudioTool = () => /* @__PURE__ */ jsxRuntime.jsx(VideosBrowser, {}), DEFAULT_TOOL_CONFIG = {
|
|
4133
|
+
const StudioTool = (config) => /* @__PURE__ */ jsxRuntime.jsx(VideosBrowser, { config }), DEFAULT_TOOL_CONFIG = {
|
|
3768
4134
|
icon: ToolIcon,
|
|
3769
4135
|
title: "Videos"
|
|
3770
4136
|
};
|
|
@@ -4122,6 +4488,9 @@ function isValidUrl(url) {
|
|
|
4122
4488
|
return !1;
|
|
4123
4489
|
}
|
|
4124
4490
|
}
|
|
4491
|
+
function isServerError(error) {
|
|
4492
|
+
return "statusCode" in error && typeof error.statusCode == "number" && 500 <= error.statusCode && error.statusCode <= 600;
|
|
4493
|
+
}
|
|
4125
4494
|
function extractDroppedFiles(dataTransfer) {
|
|
4126
4495
|
const files = Array.from(dataTransfer.files || []), items = Array.from(dataTransfer.items || []);
|
|
4127
4496
|
return files && files.length > 0 ? Promise.resolve(files) : normalizeItems(items).then((arr) => arr.flat());
|
|
@@ -4163,7 +4532,12 @@ function walk(entry) {
|
|
|
4163
4532
|
}
|
|
4164
4533
|
return Promise.resolve([]);
|
|
4165
4534
|
}
|
|
4166
|
-
function SelectAssets({
|
|
4535
|
+
function SelectAssets({
|
|
4536
|
+
asset: selectedAsset,
|
|
4537
|
+
onChange,
|
|
4538
|
+
setDialogState,
|
|
4539
|
+
config
|
|
4540
|
+
}) {
|
|
4167
4541
|
const handleSelect = React.useCallback(
|
|
4168
4542
|
(chosenAsset) => {
|
|
4169
4543
|
chosenAsset?._id || onChange(sanity.PatchEvent.from([sanity.unset(["asset"])])), chosenAsset._id !== selectedAsset?._id && onChange(
|
|
@@ -4175,7 +4549,7 @@ function SelectAssets({ asset: selectedAsset, onChange, setDialogState }) {
|
|
|
4175
4549
|
},
|
|
4176
4550
|
[onChange, setDialogState, selectedAsset]
|
|
4177
4551
|
);
|
|
4178
|
-
return /* @__PURE__ */ jsxRuntime.jsx(VideosBrowser, { onSelect: handleSelect });
|
|
4552
|
+
return /* @__PURE__ */ jsxRuntime.jsx(VideosBrowser, { onSelect: handleSelect, config });
|
|
4179
4553
|
}
|
|
4180
4554
|
const StyledDialog = styledComponents.styled(ui.Dialog)`
|
|
4181
4555
|
> div[data-ui='DialogCard'] > div[data-ui='Card'] {
|
|
@@ -4185,7 +4559,8 @@ const StyledDialog = styledComponents.styled(ui.Dialog)`
|
|
|
4185
4559
|
function InputBrowser({
|
|
4186
4560
|
setDialogState,
|
|
4187
4561
|
asset,
|
|
4188
|
-
onChange
|
|
4562
|
+
onChange,
|
|
4563
|
+
config
|
|
4189
4564
|
}) {
|
|
4190
4565
|
const id = `InputBrowser${React.useId()}`, handleClose = React.useCallback(() => setDialogState(!1), [setDialogState]);
|
|
4191
4566
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -4196,7 +4571,15 @@ function InputBrowser({
|
|
|
4196
4571
|
id,
|
|
4197
4572
|
onClose: handleClose,
|
|
4198
4573
|
width: 2,
|
|
4199
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4574
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4575
|
+
SelectAssets,
|
|
4576
|
+
{
|
|
4577
|
+
config,
|
|
4578
|
+
asset,
|
|
4579
|
+
onChange,
|
|
4580
|
+
setDialogState
|
|
4581
|
+
}
|
|
4582
|
+
)
|
|
4200
4583
|
}
|
|
4201
4584
|
);
|
|
4202
4585
|
}
|
|
@@ -4412,7 +4795,9 @@ const FileButton = styledComponents.styled(ui.MenuItem)(({ theme }) => {
|
|
|
4412
4795
|
color: white;
|
|
4413
4796
|
`, isVideoAsset = (asset) => asset._type === "mux.videoAsset";
|
|
4414
4797
|
function PlayerActionsMenu(props) {
|
|
4415
|
-
const { asset, readOnly, dialogState, setDialogState, onChange, onSelect, accept } = props, [open, setOpen] = React.useState(!1), [menuElement, setMenuRef] = React.useState(null), isSigned = React.useMemo(() => getPlaybackPolicy(asset) === "signed", [asset]), { hasConfigAccess } = useAccessControl(props.config), onReset = React.useCallback(() => onChange(sanity.PatchEvent.from(sanity.unset([]))), [onChange])
|
|
4798
|
+
const { asset, readOnly, dialogState, setDialogState, onChange, onSelect, accept } = props, [open, setOpen] = React.useState(!1), [menuElement, setMenuRef] = React.useState(null), isSigned = React.useMemo(() => getPlaybackPolicy(asset)?.policy === "signed", [asset]), { hasConfigAccess } = useAccessControl(props.config), { resyncAsset, isResyncing } = useResyncAsset({ showToast: !0 }), onReset = React.useCallback(() => onChange(sanity.PatchEvent.from(sanity.unset([]))), [onChange]), handleResync = React.useCallback(async () => {
|
|
4799
|
+
setOpen(!1), await resyncAsset(asset);
|
|
4800
|
+
}, [resyncAsset, asset]);
|
|
4416
4801
|
return React.useEffect(() => {
|
|
4417
4802
|
open && dialogState && setOpen(!1);
|
|
4418
4803
|
}, [dialogState, open]), ui.useClickOutsideEvent(
|
|
@@ -4470,6 +4855,15 @@ function PlayerActionsMenu(props) {
|
|
|
4470
4855
|
text: "Captions",
|
|
4471
4856
|
onClick: () => setDialogState("edit-captions")
|
|
4472
4857
|
}
|
|
4858
|
+
),
|
|
4859
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4860
|
+
ui.MenuItem,
|
|
4861
|
+
{
|
|
4862
|
+
icon: icons.SyncIcon,
|
|
4863
|
+
text: "Resync from Mux",
|
|
4864
|
+
onClick: handleResync,
|
|
4865
|
+
disabled: readOnly || isResyncing
|
|
4866
|
+
}
|
|
4473
4867
|
)
|
|
4474
4868
|
] }),
|
|
4475
4869
|
/* @__PURE__ */ jsxRuntime.jsx(ui.MenuDivider, {}),
|
|
@@ -4513,6 +4907,79 @@ function PlayerActionsMenu(props) {
|
|
|
4513
4907
|
] });
|
|
4514
4908
|
}
|
|
4515
4909
|
var PlayerActionsMenu$1 = React.memo(PlayerActionsMenu);
|
|
4910
|
+
function useFetchFileSize(stagedUpload, maxFileSize) {
|
|
4911
|
+
const [fileSize, setFileSize] = React.useState(null), [isLoadingFileSize, setIsLoadingFileSize] = React.useState(!1), [canSkipFileSizeValidation, setCanSkipFileSizeValidation] = React.useState(!1);
|
|
4912
|
+
return React.useEffect(() => {
|
|
4913
|
+
if (stagedUpload.type === "url") {
|
|
4914
|
+
setIsLoadingFileSize(!1), setCanSkipFileSizeValidation(!1), setFileSize(null);
|
|
4915
|
+
const url = stagedUpload.url;
|
|
4916
|
+
(async () => {
|
|
4917
|
+
setIsLoadingFileSize(!0);
|
|
4918
|
+
try {
|
|
4919
|
+
const contentLength = (await fetch(url, { method: "HEAD" })).headers.get("content-length"), newFileSize = contentLength ? parseInt(contentLength, 10) : null;
|
|
4920
|
+
setIsLoadingFileSize(!1), newFileSize && setFileSize(newFileSize), newFileSize === null && maxFileSize !== void 0 && setCanSkipFileSizeValidation(!0);
|
|
4921
|
+
} catch {
|
|
4922
|
+
console.warn("Could not validate file size from URL"), setCanSkipFileSizeValidation(!0), setIsLoadingFileSize(!1);
|
|
4923
|
+
}
|
|
4924
|
+
})();
|
|
4925
|
+
}
|
|
4926
|
+
stagedUpload.type === "file" && setFileSize(stagedUpload.files[0].size);
|
|
4927
|
+
}, [maxFileSize, stagedUpload, stagedUpload.type]), {
|
|
4928
|
+
fileSize,
|
|
4929
|
+
isLoadingFileSize,
|
|
4930
|
+
canSkipFileSizeValidation
|
|
4931
|
+
};
|
|
4932
|
+
}
|
|
4933
|
+
function useMediaMetadata(stagedUpload) {
|
|
4934
|
+
const [videoAssetMetadata, setVideoAssetMetadata] = React.useState(null), [isLoadingMetadata, setIsLoadingMetadata] = React.useState(!1);
|
|
4935
|
+
return React.useEffect(() => {
|
|
4936
|
+
let videoSrc = null;
|
|
4937
|
+
if (stagedUpload.type === "file") {
|
|
4938
|
+
const file = stagedUpload.files[0];
|
|
4939
|
+
videoSrc = URL.createObjectURL(file);
|
|
4940
|
+
}
|
|
4941
|
+
if (stagedUpload.type === "url" && (videoSrc = stagedUpload.url), setVideoAssetMetadata((old) => ({
|
|
4942
|
+
...old,
|
|
4943
|
+
duration: void 0,
|
|
4944
|
+
width: void 0,
|
|
4945
|
+
height: void 0
|
|
4946
|
+
})), !videoSrc) return () => null;
|
|
4947
|
+
setIsLoadingMetadata(!0);
|
|
4948
|
+
const videoElement = document.createElement("video");
|
|
4949
|
+
videoElement.preload = "metadata";
|
|
4950
|
+
const metadataListeners = [
|
|
4951
|
+
() => {
|
|
4952
|
+
setIsLoadingMetadata(!1);
|
|
4953
|
+
},
|
|
4954
|
+
() => {
|
|
4955
|
+
const duration = videoElement.duration, width = videoElement.videoWidth, height = videoElement.videoHeight, isAudioOnly = width <= 0 && height <= 0;
|
|
4956
|
+
setVideoAssetMetadata((old) => ({
|
|
4957
|
+
...old,
|
|
4958
|
+
duration,
|
|
4959
|
+
width,
|
|
4960
|
+
height,
|
|
4961
|
+
isAudioOnly
|
|
4962
|
+
}));
|
|
4963
|
+
}
|
|
4964
|
+
], cleanupVideo = (videoEl) => {
|
|
4965
|
+
const currentVideoSrc = videoEl?.src;
|
|
4966
|
+
videoEl && (metadataListeners.forEach(
|
|
4967
|
+
(listener) => videoEl.removeEventListener("loadedmetadata", listener)
|
|
4968
|
+
), videoEl.onerror = null, videoEl.src = "", videoEl.load()), currentVideoSrc?.startsWith("blob:") && URL.revokeObjectURL(currentVideoSrc);
|
|
4969
|
+
};
|
|
4970
|
+
return metadataListeners.push(() => setTimeout(() => cleanupVideo(videoElement), 0)), videoElement.onerror = () => {
|
|
4971
|
+
setIsLoadingMetadata(!1), console.warn("Could not read video metadata for validation"), cleanupVideo(videoElement);
|
|
4972
|
+
}, metadataListeners.forEach(
|
|
4973
|
+
(listener) => videoElement.addEventListener("loadedmetadata", listener)
|
|
4974
|
+
), videoElement.src = videoSrc, () => {
|
|
4975
|
+
cleanupVideo(videoElement);
|
|
4976
|
+
};
|
|
4977
|
+
}, [stagedUpload.type, stagedUpload]), {
|
|
4978
|
+
videoAssetMetadata,
|
|
4979
|
+
setVideoAssetMetadata,
|
|
4980
|
+
isLoadingMetadata
|
|
4981
|
+
};
|
|
4982
|
+
}
|
|
4516
4983
|
function formatBytes(bytes, si = !1, dp = 1) {
|
|
4517
4984
|
const thresh = si ? 1e3 : 1024;
|
|
4518
4985
|
if (Math.abs(bytes) < thresh)
|
|
@@ -4602,13 +5069,14 @@ function PlaybackPolicyOption({
|
|
|
4602
5069
|
optionName,
|
|
4603
5070
|
description,
|
|
4604
5071
|
dispatch,
|
|
4605
|
-
action
|
|
5072
|
+
action,
|
|
5073
|
+
disabled
|
|
4606
5074
|
}) {
|
|
4607
5075
|
const [scale, setScale] = React.useState(1), boxStyle = {
|
|
4608
5076
|
outline: "0.01rem solid grey",
|
|
4609
5077
|
transform: `scale(${scale})`,
|
|
4610
5078
|
transition: "transform 0.1s ease-in-out",
|
|
4611
|
-
cursor: "pointer",
|
|
5079
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
4612
5080
|
borderRadius: "0.25rem"
|
|
4613
5081
|
}, triggerAnimation = () => {
|
|
4614
5082
|
setScale(0.98), setTimeout(() => {
|
|
@@ -4616,15 +5084,24 @@ function PlaybackPolicyOption({
|
|
|
4616
5084
|
}, 100);
|
|
4617
5085
|
};
|
|
4618
5086
|
return /* @__PURE__ */ jsxRuntime.jsx("label", { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gap: 3, padding: 3, style: boxStyle, children: [
|
|
4619
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
5087
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5088
|
+
ui.Checkbox,
|
|
5089
|
+
{
|
|
5090
|
+
id,
|
|
5091
|
+
required: !0,
|
|
5092
|
+
checked,
|
|
5093
|
+
onChange: () => {
|
|
5094
|
+
action && (triggerAnimation(), dispatch({
|
|
5095
|
+
action,
|
|
5096
|
+
value: !checked
|
|
5097
|
+
}));
|
|
5098
|
+
},
|
|
5099
|
+
disabled
|
|
5100
|
+
}
|
|
5101
|
+
),
|
|
4625
5102
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Grid, { gap: 3, children: [
|
|
4626
5103
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 3, weight: "bold", children: optionName }),
|
|
4627
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, muted: !0, children: description })
|
|
5104
|
+
typeof description == "string" ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, muted: !0, children: description }) : description
|
|
4628
5105
|
] })
|
|
4629
5106
|
] }) });
|
|
4630
5107
|
}
|
|
@@ -4649,7 +5126,7 @@ function PlaybackPolicy({
|
|
|
4649
5126
|
secrets,
|
|
4650
5127
|
dispatch
|
|
4651
5128
|
}) {
|
|
4652
|
-
const noPolicySelected = !(config.public_policy || config.signed_policy);
|
|
5129
|
+
const noPolicySelected = !(config.public_policy || config.signed_policy || config.drm_policy), drmPolicyDisabled = !secrets.drmConfigId;
|
|
4653
5130
|
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Grid, { gap: 3, children: [
|
|
4654
5131
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", children: "Advanced Playback Policies" }),
|
|
4655
5132
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -4658,7 +5135,10 @@ function PlaybackPolicy({
|
|
|
4658
5135
|
id: `${id}--public`,
|
|
4659
5136
|
checked: config.public_policy,
|
|
4660
5137
|
optionName: "Public",
|
|
4661
|
-
description:
|
|
5138
|
+
description: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5139
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, muted: !0, children: "Playback IDs are accessible by constructing an HLS URL like" }),
|
|
5140
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Code, { children: "https://stream.mux.com/{PLAYBACK_ID}" })
|
|
5141
|
+
] }),
|
|
4662
5142
|
dispatch,
|
|
4663
5143
|
action: "public_policy"
|
|
4664
5144
|
}
|
|
@@ -4669,24 +5149,146 @@ function PlaybackPolicy({
|
|
|
4669
5149
|
id: `${id}--signed`,
|
|
4670
5150
|
checked: config.signed_policy,
|
|
4671
5151
|
optionName: "Signed",
|
|
4672
|
-
description:
|
|
4673
|
-
|
|
5152
|
+
description: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5153
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, muted: !0, children: "Playback IDs should be used with tokens" }),
|
|
5154
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Code, { children: "https://stream.mux.com/{PLAYBACK_ID}?token={TOKEN}" }),
|
|
5155
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: 2, muted: !0, children: [
|
|
5156
|
+
"See",
|
|
5157
|
+
" ",
|
|
5158
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5159
|
+
"a",
|
|
5160
|
+
{
|
|
5161
|
+
href: "https://www.mux.com/docs/guides/secure-video-playback",
|
|
5162
|
+
target: "_blank",
|
|
5163
|
+
rel: "noopener noreferrer",
|
|
5164
|
+
children: "Secure video playback"
|
|
5165
|
+
}
|
|
5166
|
+
),
|
|
5167
|
+
" ",
|
|
5168
|
+
"for details about creating tokens."
|
|
5169
|
+
] })
|
|
5170
|
+
] }),
|
|
4674
5171
|
dispatch,
|
|
4675
5172
|
action: "signed_policy"
|
|
4676
5173
|
}
|
|
4677
5174
|
),
|
|
5175
|
+
drmPolicyDisabled ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
5176
|
+
PlaybackPolicyOption,
|
|
5177
|
+
{
|
|
5178
|
+
id: `${id}--drm`,
|
|
5179
|
+
checked: !1,
|
|
5180
|
+
optionName: "DRM - Disabled",
|
|
5181
|
+
description: /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: 2, muted: !0, children: [
|
|
5182
|
+
"To enable DRM add your DRM Configuration Id to your plugin configuration in the API Credentials view.",
|
|
5183
|
+
" ",
|
|
5184
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5185
|
+
"a",
|
|
5186
|
+
{
|
|
5187
|
+
href: "https://www.mux.com/support/human",
|
|
5188
|
+
target: "_blank",
|
|
5189
|
+
rel: "noopener noreferrer",
|
|
5190
|
+
children: "Contact us"
|
|
5191
|
+
}
|
|
5192
|
+
),
|
|
5193
|
+
" ",
|
|
5194
|
+
"to get started using DRM."
|
|
5195
|
+
] }) }),
|
|
5196
|
+
dispatch,
|
|
5197
|
+
disabled: !0
|
|
5198
|
+
}
|
|
5199
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
5200
|
+
PlaybackPolicyOption,
|
|
5201
|
+
{
|
|
5202
|
+
id: `${id}--drm`,
|
|
5203
|
+
checked: config.drm_policy,
|
|
5204
|
+
optionName: "DRM",
|
|
5205
|
+
description: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5206
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, muted: !0, children: "Playback IDs should be used with tokens as with Signed playback, but require extra configuration." }),
|
|
5207
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Code, { children: "https://stream.mux.com/{PLAYBACK_ID}?token={TOKEN}" }),
|
|
5208
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: 2, muted: !0, children: [
|
|
5209
|
+
"See",
|
|
5210
|
+
" ",
|
|
5211
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5212
|
+
"a",
|
|
5213
|
+
{
|
|
5214
|
+
href: "https://www.mux.com/docs/guides/protect-videos-with-drm#play-drm-protected-videos",
|
|
5215
|
+
target: "_blank",
|
|
5216
|
+
rel: "noopener noreferrer",
|
|
5217
|
+
children: "Protect videos with DRM"
|
|
5218
|
+
}
|
|
5219
|
+
),
|
|
5220
|
+
" ",
|
|
5221
|
+
"for details about configuring your player for DRM playback and",
|
|
5222
|
+
" ",
|
|
5223
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5224
|
+
"a",
|
|
5225
|
+
{
|
|
5226
|
+
href: "https://www.mux.com/docs/guides/secure-video-playback",
|
|
5227
|
+
target: "_blank",
|
|
5228
|
+
rel: "noopener noreferrer",
|
|
5229
|
+
children: "Secure video playback"
|
|
5230
|
+
}
|
|
5231
|
+
),
|
|
5232
|
+
" ",
|
|
5233
|
+
"for details about creating tokens."
|
|
5234
|
+
] })
|
|
5235
|
+
] }),
|
|
5236
|
+
dispatch,
|
|
5237
|
+
action: "drm_policy"
|
|
5238
|
+
}
|
|
5239
|
+
),
|
|
4678
5240
|
noPolicySelected && /* @__PURE__ */ jsxRuntime.jsx(PlaybackPolicyWarning, {})
|
|
4679
5241
|
] });
|
|
4680
5242
|
}
|
|
4681
|
-
const
|
|
4682
|
-
{ value: "basic", label: "Basic" },
|
|
4683
|
-
{ value: "plus", label: "Plus" },
|
|
4684
|
-
{ value: "premium", label: "Premium" }
|
|
4685
|
-
], RESOLUTION_TIERS = [
|
|
5243
|
+
const RESOLUTION_TIERS = [
|
|
4686
5244
|
{ value: "1080p", label: "1080p" },
|
|
4687
5245
|
{ value: "1440p", label: "1440p (2k)" },
|
|
4688
5246
|
{ value: "2160p", label: "2160p (4k)" }
|
|
4689
|
-
],
|
|
5247
|
+
], ResolutionTierSelector = ({
|
|
5248
|
+
id,
|
|
5249
|
+
config,
|
|
5250
|
+
dispatch,
|
|
5251
|
+
maxSupportedResolution
|
|
5252
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
5253
|
+
sanity.FormField,
|
|
5254
|
+
{
|
|
5255
|
+
title: "Resolution Tier",
|
|
5256
|
+
description: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5257
|
+
"The maximum",
|
|
5258
|
+
" ",
|
|
5259
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5260
|
+
"a",
|
|
5261
|
+
{
|
|
5262
|
+
href: "https://docs.mux.com/api-reference#video/operation/create-direct-upload",
|
|
5263
|
+
target: "_blank",
|
|
5264
|
+
rel: "noopener noreferrer",
|
|
5265
|
+
children: "resolution_tier"
|
|
5266
|
+
}
|
|
5267
|
+
),
|
|
5268
|
+
" ",
|
|
5269
|
+
"your asset is encoded, stored, and streamed at."
|
|
5270
|
+
] }),
|
|
5271
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { gap: 3, wrap: "wrap", children: RESOLUTION_TIERS.map(({ value, label }, index) => {
|
|
5272
|
+
const inputId = `${id}--type-${value}`;
|
|
5273
|
+
return index > maxSupportedResolution ? null : /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, children: [
|
|
5274
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5275
|
+
ui.Radio,
|
|
5276
|
+
{
|
|
5277
|
+
checked: config.max_resolution_tier === value,
|
|
5278
|
+
name: "asset-resolutiontier",
|
|
5279
|
+
onChange: (e) => dispatch({
|
|
5280
|
+
action: "max_resolution_tier",
|
|
5281
|
+
value: e.currentTarget.value
|
|
5282
|
+
}),
|
|
5283
|
+
value,
|
|
5284
|
+
id: inputId
|
|
5285
|
+
}
|
|
5286
|
+
),
|
|
5287
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: inputId, children: label })
|
|
5288
|
+
] }, value);
|
|
5289
|
+
}) })
|
|
5290
|
+
}
|
|
5291
|
+
), ADVANCED_RESOLUTIONS = [
|
|
4690
5292
|
{ value: "270p", label: "270p" },
|
|
4691
5293
|
{ value: "360p", label: "360p" },
|
|
4692
5294
|
{ value: "480p", label: "480p" },
|
|
@@ -4695,6 +5297,130 @@ const VIDEO_QUALITY_LEVELS = [
|
|
|
4695
5297
|
{ value: "1080p", label: "1080p" },
|
|
4696
5298
|
{ value: "1440p", label: "1440p" },
|
|
4697
5299
|
{ value: "2160p", label: "2160p" }
|
|
5300
|
+
], StaticRenditionSelector = ({
|
|
5301
|
+
id,
|
|
5302
|
+
config,
|
|
5303
|
+
dispatch
|
|
5304
|
+
}) => {
|
|
5305
|
+
const isAdvancedMode = React.useMemo(() => config.static_renditions.filter(
|
|
5306
|
+
(r) => r !== "highest" && r !== "audio-only"
|
|
5307
|
+
).length > 0, [config.static_renditions]), [renditionMode, setRenditionMode] = React.useState(
|
|
5308
|
+
isAdvancedMode ? "advanced" : "standard"
|
|
5309
|
+
), toggleRendition = (rendition) => {
|
|
5310
|
+
const current = config.static_renditions, hasRendition = current.includes(rendition);
|
|
5311
|
+
dispatch(hasRendition ? {
|
|
5312
|
+
action: "static_renditions",
|
|
5313
|
+
value: current.filter((r) => r !== rendition)
|
|
5314
|
+
} : {
|
|
5315
|
+
action: "static_renditions",
|
|
5316
|
+
value: [...current, rendition]
|
|
5317
|
+
});
|
|
5318
|
+
}, handleModeChange = (mode) => {
|
|
5319
|
+
setRenditionMode(mode), dispatch(mode === "standard" ? {
|
|
5320
|
+
action: "static_renditions",
|
|
5321
|
+
value: config.static_renditions.filter((r) => r === "highest" || r === "audio-only")
|
|
5322
|
+
} : {
|
|
5323
|
+
action: "static_renditions",
|
|
5324
|
+
value: config.static_renditions.filter((r) => r !== "highest")
|
|
5325
|
+
});
|
|
5326
|
+
};
|
|
5327
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 3, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5328
|
+
sanity.FormField,
|
|
5329
|
+
{
|
|
5330
|
+
title: "Static Renditions",
|
|
5331
|
+
description: "Generate downloadable MP4 or M4A files. Note: Mux will not upscale to produce MP4 renditions - renditions that would cause upscaling are skipped.",
|
|
5332
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
|
|
5333
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gap: 3, children: [
|
|
5334
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, children: [
|
|
5335
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5336
|
+
ui.Radio,
|
|
5337
|
+
{
|
|
5338
|
+
checked: renditionMode === "standard",
|
|
5339
|
+
name: "rendition-mode",
|
|
5340
|
+
onChange: () => handleModeChange("standard"),
|
|
5341
|
+
value: "standard",
|
|
5342
|
+
id: `${id}--mode-standard`
|
|
5343
|
+
}
|
|
5344
|
+
),
|
|
5345
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: `${id}--mode-standard`, children: "Standard" })
|
|
5346
|
+
] }),
|
|
5347
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, children: [
|
|
5348
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5349
|
+
ui.Radio,
|
|
5350
|
+
{
|
|
5351
|
+
checked: renditionMode === "advanced",
|
|
5352
|
+
name: "rendition-mode",
|
|
5353
|
+
onChange: () => handleModeChange("advanced"),
|
|
5354
|
+
value: "advanced",
|
|
5355
|
+
id: `${id}--mode-advanced`
|
|
5356
|
+
}
|
|
5357
|
+
),
|
|
5358
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: `${id}--mode-advanced`, children: "Advanced" })
|
|
5359
|
+
] })
|
|
5360
|
+
] }),
|
|
5361
|
+
renditionMode === "standard" && /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, children: [
|
|
5362
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, padding: [0, 2], children: [
|
|
5363
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5364
|
+
ui.Checkbox,
|
|
5365
|
+
{
|
|
5366
|
+
id: `${id}--highest`,
|
|
5367
|
+
style: { display: "block" },
|
|
5368
|
+
checked: config.static_renditions.includes("highest"),
|
|
5369
|
+
onChange: () => toggleRendition("highest")
|
|
5370
|
+
}
|
|
5371
|
+
),
|
|
5372
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: `${id}--highest`, children: "Highest Resolution (up to 4K)" })
|
|
5373
|
+
] }),
|
|
5374
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, padding: [0, 2], children: [
|
|
5375
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5376
|
+
ui.Checkbox,
|
|
5377
|
+
{
|
|
5378
|
+
id: `${id}--audio-only-standard`,
|
|
5379
|
+
style: { display: "block" },
|
|
5380
|
+
checked: config.static_renditions.includes("audio-only"),
|
|
5381
|
+
onChange: () => toggleRendition("audio-only")
|
|
5382
|
+
}
|
|
5383
|
+
),
|
|
5384
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: `${id}--audio-only-standard`, children: "Audio Only (M4A)" })
|
|
5385
|
+
] })
|
|
5386
|
+
] }),
|
|
5387
|
+
renditionMode === "advanced" && /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, children: [
|
|
5388
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: 1, muted: !0, children: "Select specific resolutions:" }),
|
|
5389
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { gap: 2, wrap: "wrap", children: ADVANCED_RESOLUTIONS.map(({ value, label }) => {
|
|
5390
|
+
const inputId = `${id}--resolution-${value}`;
|
|
5391
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, children: [
|
|
5392
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5393
|
+
ui.Checkbox,
|
|
5394
|
+
{
|
|
5395
|
+
id: inputId,
|
|
5396
|
+
style: { display: "block" },
|
|
5397
|
+
checked: config.static_renditions.includes(value),
|
|
5398
|
+
onChange: () => toggleRendition(value)
|
|
5399
|
+
}
|
|
5400
|
+
),
|
|
5401
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: inputId, size: 1, children: label })
|
|
5402
|
+
] }, value);
|
|
5403
|
+
}) }),
|
|
5404
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, padding: [2, 2, 0, 2], children: [
|
|
5405
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5406
|
+
ui.Checkbox,
|
|
5407
|
+
{
|
|
5408
|
+
id: `${id}--audio-only-advanced`,
|
|
5409
|
+
style: { display: "block" },
|
|
5410
|
+
checked: config.static_renditions.includes("audio-only"),
|
|
5411
|
+
onChange: () => toggleRendition("audio-only")
|
|
5412
|
+
}
|
|
5413
|
+
),
|
|
5414
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: `${id}--audio-only-advanced`, children: "Audio Only (M4A)" })
|
|
5415
|
+
] })
|
|
5416
|
+
] })
|
|
5417
|
+
] })
|
|
5418
|
+
}
|
|
5419
|
+
) });
|
|
5420
|
+
}, VIDEO_QUALITY_LEVELS = [
|
|
5421
|
+
{ value: "basic", label: "Basic" },
|
|
5422
|
+
{ value: "plus", label: "Plus" },
|
|
5423
|
+
{ value: "premium", label: "Premium" }
|
|
4698
5424
|
];
|
|
4699
5425
|
function sanitizeStaticRenditions(renditions) {
|
|
4700
5426
|
const hasHighest = renditions.includes("highest"), hasSpecificResolutions = renditions.some((r) => r !== "highest" && r !== "audio-only");
|
|
@@ -4726,7 +5452,8 @@ function UploadConfiguration({
|
|
|
4726
5452
|
max_resolution_tier: "1080p",
|
|
4727
5453
|
text_tracks: prev.text_tracks?.filter(({ type }) => type !== "autogenerated"),
|
|
4728
5454
|
public_policy: !0,
|
|
4729
|
-
signed_policy: !1
|
|
5455
|
+
signed_policy: !1,
|
|
5456
|
+
drm_policy: !1
|
|
4730
5457
|
}) : Object.assign({}, prev, {
|
|
4731
5458
|
video_quality: action.value,
|
|
4732
5459
|
static_renditions: sanitizeStaticRenditions(pluginConfig.static_renditions || []),
|
|
@@ -4740,6 +5467,8 @@ function UploadConfiguration({
|
|
|
4740
5467
|
return Object.assign({}, prev, { [action.action]: action.value });
|
|
4741
5468
|
case "public_policy":
|
|
4742
5469
|
return Object.assign({}, prev, { [action.action]: action.value });
|
|
5470
|
+
case "drm_policy":
|
|
5471
|
+
return Object.assign({}, prev, { [action.action]: action.value });
|
|
4743
5472
|
// Updating individual tracks
|
|
4744
5473
|
case "track": {
|
|
4745
5474
|
const text_tracks = [...prev.text_tracks], target_track_i = text_tracks.findIndex(({ _id: _id2 }) => _id2 === action.id);
|
|
@@ -4775,75 +5504,41 @@ function UploadConfiguration({
|
|
|
4775
5504
|
static_renditions: sanitizeStaticRenditions(pluginConfig.static_renditions || []),
|
|
4776
5505
|
signed_policy: secrets.enableSignedUrls && pluginConfig.defaultSigned,
|
|
4777
5506
|
public_policy: pluginConfig.defaultPublic,
|
|
5507
|
+
drm_policy: pluginConfig.defaultDrm && !!secrets.drmConfigId,
|
|
4778
5508
|
normalize_audio: pluginConfig.normalize_audio,
|
|
4779
5509
|
text_tracks: autoTextTracks
|
|
4780
5510
|
}
|
|
4781
|
-
),
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
), [videoDuration, setVideoDuration] = React.useState(null), [urlFileSize, setUrlFileSize] = React.useState(null), [isLoadingDuration, setIsLoadingDuration] = React.useState(!1), [isLoadingFileSize, setIsLoadingFileSize] = React.useState(!1), [validationError, setValidationError] = React.useState(null), [canSkipFileSizeValidation, setCanSkipFileSizeValidation] = React.useState(!1), MAX_FILE_SIZE = pluginConfig.maxAssetFileSize, MAX_DURATION_SECONDS = pluginConfig.maxAssetDuration;
|
|
5511
|
+
), [validationError, setValidationError] = React.useState(null), MAX_FILE_SIZE = pluginConfig.maxAssetFileSize, MAX_DURATION_SECONDS = pluginConfig.maxAssetDuration, { fileSize, isLoadingFileSize, canSkipFileSizeValidation } = useFetchFileSize(
|
|
5512
|
+
stagedUpload,
|
|
5513
|
+
MAX_FILE_SIZE
|
|
5514
|
+
), { videoAssetMetadata, setVideoAssetMetadata, isLoadingMetadata } = useMediaMetadata(stagedUpload);
|
|
4786
5515
|
React.useEffect(() => {
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
const
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
!MAX_DURATION_SECONDS || MAX_DURATION_SECONDS <= 0 || (setIsLoadingDuration(!0), videoElement = document.createElement("video"), videoElement.preload = "metadata", currentVideoSrc = videoSrc, videoElement.onloadedmetadata = () => {
|
|
4793
|
-
const duration = videoElement.duration;
|
|
4794
|
-
setVideoDuration(duration), setIsLoadingDuration(!1), duration > MAX_DURATION_SECONDS && setValidationError(
|
|
4795
|
-
`Video duration (${formatSeconds(duration)}) exceeds maximum allowed duration of ${formatSeconds(MAX_DURATION_SECONDS)}`
|
|
4796
|
-
), cleanupVideo(shouldRevokeUrl);
|
|
4797
|
-
}, videoElement.onerror = () => {
|
|
4798
|
-
setIsLoadingDuration(!1), console.warn("Could not read video metadata for validation"), cleanupVideo(shouldRevokeUrl);
|
|
4799
|
-
}, videoElement.src = videoSrc);
|
|
4800
|
-
}, validateFileSize = (size) => MAX_FILE_SIZE === void 0 || size <= MAX_FILE_SIZE ? !0 : (setValidationError(
|
|
5516
|
+
fileSize && setVideoAssetMetadata((old) => ({ ...old, size: fileSize }));
|
|
5517
|
+
}, [fileSize, setVideoAssetMetadata]), React.useEffect(() => {
|
|
5518
|
+
const validateDuration = (duration) => MAX_DURATION_SECONDS && duration > MAX_DURATION_SECONDS ? (setValidationError(
|
|
5519
|
+
`Video duration (${formatSeconds(duration)}) exceeds maximum allowed duration of ${formatSeconds(MAX_DURATION_SECONDS)}`
|
|
5520
|
+
), !1) : !0, validateFileSize = (size) => MAX_FILE_SIZE === void 0 || size <= MAX_FILE_SIZE ? !0 : (setValidationError(
|
|
4801
5521
|
`File size (${formatBytes(size)}) exceeds maximum allowed size of ${formatBytes(MAX_FILE_SIZE)}`
|
|
4802
|
-
), !1);
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
}
|
|
4819
|
-
})();
|
|
4820
|
-
}
|
|
4821
|
-
return () => {
|
|
4822
|
-
cleanupVideo(!0);
|
|
4823
|
-
};
|
|
4824
|
-
}, [stagedUpload, MAX_FILE_SIZE, MAX_DURATION_SECONDS]);
|
|
4825
|
-
const toggleRendition = (rendition) => {
|
|
4826
|
-
const current = config.static_renditions, hasRendition = current.includes(rendition);
|
|
4827
|
-
dispatch(hasRendition ? {
|
|
4828
|
-
action: "static_renditions",
|
|
4829
|
-
value: current.filter((r) => r !== rendition)
|
|
4830
|
-
} : {
|
|
4831
|
-
action: "static_renditions",
|
|
4832
|
-
value: [...current, rendition]
|
|
4833
|
-
});
|
|
4834
|
-
}, handleModeChange = (mode) => {
|
|
4835
|
-
setRenditionMode(mode), dispatch(mode === "standard" ? {
|
|
4836
|
-
action: "static_renditions",
|
|
4837
|
-
value: config.static_renditions.filter((r) => r === "highest" || r === "audio-only")
|
|
4838
|
-
} : {
|
|
4839
|
-
action: "static_renditions",
|
|
4840
|
-
value: config.static_renditions.filter((r) => r !== "highest")
|
|
4841
|
-
});
|
|
4842
|
-
}, { disableTextTrackConfig, disableUploadConfig } = pluginConfig, skipConfig = disableTextTrackConfig && disableUploadConfig;
|
|
5522
|
+
), !1), validateDrmAvailability = (isAudioOnly) => config.drm_policy && isAudioOnly ? (setValidationError("Audio-only asset cannot be DRM protected"), !1) : !0;
|
|
5523
|
+
let valid = !0;
|
|
5524
|
+
videoAssetMetadata?.size && (valid = valid && (canSkipFileSizeValidation || validateFileSize(videoAssetMetadata.size))), videoAssetMetadata?.duration && (valid = valid && validateDuration(videoAssetMetadata.duration)), videoAssetMetadata?.isAudioOnly != null && (valid = valid && validateDrmAvailability(videoAssetMetadata.isAudioOnly)), valid && setValidationError(null);
|
|
5525
|
+
}, [
|
|
5526
|
+
MAX_FILE_SIZE,
|
|
5527
|
+
MAX_DURATION_SECONDS,
|
|
5528
|
+
canSkipFileSizeValidation,
|
|
5529
|
+
videoAssetMetadata?.duration,
|
|
5530
|
+
videoAssetMetadata?.size,
|
|
5531
|
+
videoAssetMetadata?.height,
|
|
5532
|
+
videoAssetMetadata?.width,
|
|
5533
|
+
videoAssetMetadata,
|
|
5534
|
+
config.drm_policy,
|
|
5535
|
+
validationError
|
|
5536
|
+
]);
|
|
5537
|
+
const { disableTextTrackConfig, disableUploadConfig } = pluginConfig, skipConfig = disableTextTrackConfig && disableUploadConfig;
|
|
4843
5538
|
if (React.useEffect(() => {
|
|
4844
|
-
skipConfig && startUpload(formatUploadConfig(config));
|
|
5539
|
+
skipConfig && startUpload(formatUploadConfig(config, secrets));
|
|
4845
5540
|
}, []), skipConfig) return null;
|
|
4846
|
-
const basicConfig = config.video_quality !== "plus" && config.video_quality !== "premium", maxSupportedResolution = RESOLUTION_TIERS.findIndex(
|
|
5541
|
+
const basicConfig = config.video_quality !== "plus" && config.video_quality !== "premium", playbackPolicySelected = config.public_policy || config.signed_policy || config.drm_policy, maxSupportedResolution = RESOLUTION_TIERS.findIndex(
|
|
4847
5542
|
(rt) => rt.value === pluginConfig.max_resolution_tier
|
|
4848
5543
|
);
|
|
4849
5544
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -4877,12 +5572,12 @@ function UploadConfiguration({
|
|
|
4877
5572
|
/* @__PURE__ */ jsxRuntime.jsx(icons.DocumentVideoIcon, { fontSize: "2em" }),
|
|
4878
5573
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, children: [
|
|
4879
5574
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { textOverflow: "ellipsis", as: "h2", size: 3, children: stagedUpload.type === "file" ? stagedUpload.files[0].name : stagedUpload.url }),
|
|
4880
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "p", size: 1, muted: !0, children: stagedUpload.type === "file" ? `Direct File Upload (${formatBytes(stagedUpload.files[0].size)})` :
|
|
5575
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "p", size: 1, muted: !0, children: stagedUpload.type === "file" ? `Direct File Upload (${formatBytes(stagedUpload.files[0].size)})` : videoAssetMetadata?.size ? `File From URL (${formatBytes(videoAssetMetadata.size)})` : isLoadingFileSize ? "File From URL (Loading size...)" : "File From URL (Unknown size)" }),
|
|
4881
5576
|
stagedUpload.type === "file" && /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 1, children: [
|
|
4882
|
-
|
|
4883
|
-
|
|
5577
|
+
isLoadingMetadata && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "p", size: 1, muted: !0, children: "Reading video metadata..." }),
|
|
5578
|
+
videoAssetMetadata?.duration && !validationError && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { as: "p", size: 1, muted: !0, children: [
|
|
4884
5579
|
"Duration: ",
|
|
4885
|
-
formatSeconds(
|
|
5580
|
+
formatSeconds(videoAssetMetadata.duration)
|
|
4886
5581
|
] })
|
|
4887
5582
|
] })
|
|
4888
5583
|
] })
|
|
@@ -4928,160 +5623,37 @@ function UploadConfiguration({
|
|
|
4928
5623
|
}) })
|
|
4929
5624
|
}
|
|
4930
5625
|
),
|
|
4931
|
-
!basicConfig && maxSupportedResolution > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4932
|
-
sanity.FormField,
|
|
4933
|
-
{
|
|
4934
|
-
title: "Resolution Tier",
|
|
4935
|
-
description: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
4936
|
-
"The maximum",
|
|
4937
|
-
" ",
|
|
4938
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4939
|
-
"a",
|
|
4940
|
-
{
|
|
4941
|
-
href: "https://docs.mux.com/api-reference#video/operation/create-direct-upload",
|
|
4942
|
-
target: "_blank",
|
|
4943
|
-
rel: "noopener noreferrer",
|
|
4944
|
-
children: "resolution_tier"
|
|
4945
|
-
}
|
|
4946
|
-
),
|
|
4947
|
-
" ",
|
|
4948
|
-
"your asset is encoded, stored, and streamed at."
|
|
4949
|
-
] }),
|
|
4950
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { gap: 3, wrap: "wrap", children: RESOLUTION_TIERS.map(({ value, label }, index) => {
|
|
4951
|
-
const inputId = `${id}--type-${value}`;
|
|
4952
|
-
return index > maxSupportedResolution ? null : /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, children: [
|
|
4953
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4954
|
-
ui.Radio,
|
|
4955
|
-
{
|
|
4956
|
-
checked: config.max_resolution_tier === value,
|
|
4957
|
-
name: "asset-resolutiontier",
|
|
4958
|
-
onChange: (e) => dispatch({
|
|
4959
|
-
action: "max_resolution_tier",
|
|
4960
|
-
value: e.currentTarget.value
|
|
4961
|
-
}),
|
|
4962
|
-
value,
|
|
4963
|
-
id: inputId
|
|
4964
|
-
}
|
|
4965
|
-
),
|
|
4966
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: inputId, children: label })
|
|
4967
|
-
] }, value);
|
|
4968
|
-
}) })
|
|
4969
|
-
}
|
|
4970
|
-
),
|
|
4971
5626
|
!basicConfig && /* @__PURE__ */ jsxRuntime.jsx(sanity.FormField, { title: "Additional Configuration", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
|
|
4972
5627
|
/* @__PURE__ */ jsxRuntime.jsx(PlaybackPolicy, { id, config, secrets, dispatch }),
|
|
4973
|
-
|
|
4974
|
-
|
|
5628
|
+
maxSupportedResolution > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5629
|
+
ResolutionTierSelector,
|
|
4975
5630
|
{
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, children: [
|
|
4981
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4982
|
-
ui.Radio,
|
|
4983
|
-
{
|
|
4984
|
-
checked: renditionMode === "standard",
|
|
4985
|
-
name: "rendition-mode",
|
|
4986
|
-
onChange: () => handleModeChange("standard"),
|
|
4987
|
-
value: "standard",
|
|
4988
|
-
id: `${id}--mode-standard`
|
|
4989
|
-
}
|
|
4990
|
-
),
|
|
4991
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: `${id}--mode-standard`, children: "Standard" })
|
|
4992
|
-
] }),
|
|
4993
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, children: [
|
|
4994
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4995
|
-
ui.Radio,
|
|
4996
|
-
{
|
|
4997
|
-
checked: renditionMode === "advanced",
|
|
4998
|
-
name: "rendition-mode",
|
|
4999
|
-
onChange: () => handleModeChange("advanced"),
|
|
5000
|
-
value: "advanced",
|
|
5001
|
-
id: `${id}--mode-advanced`
|
|
5002
|
-
}
|
|
5003
|
-
),
|
|
5004
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: `${id}--mode-advanced`, children: "Advanced" })
|
|
5005
|
-
] })
|
|
5006
|
-
] }),
|
|
5007
|
-
renditionMode === "standard" && /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, children: [
|
|
5008
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, padding: [0, 2], children: [
|
|
5009
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5010
|
-
ui.Checkbox,
|
|
5011
|
-
{
|
|
5012
|
-
id: `${id}--highest`,
|
|
5013
|
-
style: { display: "block" },
|
|
5014
|
-
checked: config.static_renditions.includes("highest"),
|
|
5015
|
-
onChange: () => toggleRendition("highest")
|
|
5016
|
-
}
|
|
5017
|
-
),
|
|
5018
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: `${id}--highest`, children: "Highest Resolution (up to 4K)" })
|
|
5019
|
-
] }),
|
|
5020
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, padding: [0, 2], children: [
|
|
5021
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5022
|
-
ui.Checkbox,
|
|
5023
|
-
{
|
|
5024
|
-
id: `${id}--audio-only-standard`,
|
|
5025
|
-
style: { display: "block" },
|
|
5026
|
-
checked: config.static_renditions.includes("audio-only"),
|
|
5027
|
-
onChange: () => toggleRendition("audio-only")
|
|
5028
|
-
}
|
|
5029
|
-
),
|
|
5030
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: `${id}--audio-only-standard`, children: "Audio Only (M4A)" })
|
|
5031
|
-
] })
|
|
5032
|
-
] }),
|
|
5033
|
-
renditionMode === "advanced" && /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, children: [
|
|
5034
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: 1, muted: !0, children: "Select specific resolutions:" }),
|
|
5035
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { gap: 2, wrap: "wrap", children: ADVANCED_RESOLUTIONS.map(({ value, label }) => {
|
|
5036
|
-
const inputId = `${id}--resolution-${value}`;
|
|
5037
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, children: [
|
|
5038
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5039
|
-
ui.Checkbox,
|
|
5040
|
-
{
|
|
5041
|
-
id: inputId,
|
|
5042
|
-
style: { display: "block" },
|
|
5043
|
-
checked: config.static_renditions.includes(value),
|
|
5044
|
-
onChange: () => toggleRendition(value)
|
|
5045
|
-
}
|
|
5046
|
-
),
|
|
5047
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: inputId, size: 1, children: label })
|
|
5048
|
-
] }, value);
|
|
5049
|
-
}) }),
|
|
5050
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, padding: [2, 2, 0, 2], children: [
|
|
5051
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5052
|
-
ui.Checkbox,
|
|
5053
|
-
{
|
|
5054
|
-
id: `${id}--audio-only-advanced`,
|
|
5055
|
-
style: { display: "block" },
|
|
5056
|
-
checked: config.static_renditions.includes("audio-only"),
|
|
5057
|
-
onChange: () => toggleRendition("audio-only")
|
|
5058
|
-
}
|
|
5059
|
-
),
|
|
5060
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: `${id}--audio-only-advanced`, children: "Audio Only (M4A)" })
|
|
5061
|
-
] })
|
|
5062
|
-
] })
|
|
5063
|
-
] })
|
|
5631
|
+
id,
|
|
5632
|
+
config,
|
|
5633
|
+
dispatch,
|
|
5634
|
+
maxSupportedResolution
|
|
5064
5635
|
}
|
|
5065
|
-
)
|
|
5636
|
+
),
|
|
5637
|
+
/* @__PURE__ */ jsxRuntime.jsx(StaticRenditionSelector, { id, config, dispatch }),
|
|
5638
|
+
!disableTextTrackConfig && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5639
|
+
TextTracksEditor,
|
|
5640
|
+
{
|
|
5641
|
+
tracks: config.text_tracks,
|
|
5642
|
+
dispatch,
|
|
5643
|
+
defaultLang: pluginConfig.defaultAutogeneratedSubtitleLang
|
|
5644
|
+
}
|
|
5645
|
+
)
|
|
5066
5646
|
] }) })
|
|
5067
5647
|
] }),
|
|
5068
|
-
!disableTextTrackConfig && !basicConfig && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5069
|
-
TextTracksEditor,
|
|
5070
|
-
{
|
|
5071
|
-
tracks: config.text_tracks,
|
|
5072
|
-
dispatch,
|
|
5073
|
-
defaultLang: pluginConfig.defaultAutogeneratedSubtitleLang
|
|
5074
|
-
}
|
|
5075
|
-
),
|
|
5076
5648
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Box, { marginTop: 4, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5077
5649
|
ui.Button,
|
|
5078
5650
|
{
|
|
5079
|
-
disabled: !basicConfig && !
|
|
5651
|
+
disabled: !basicConfig && !playbackPolicySelected || validationError !== null || isLoadingMetadata || isLoadingFileSize && !canSkipFileSizeValidation,
|
|
5080
5652
|
icon: icons.UploadIcon,
|
|
5081
5653
|
text: "Upload",
|
|
5082
5654
|
tone: "positive",
|
|
5083
5655
|
onClick: () => {
|
|
5084
|
-
validationError || startUpload(formatUploadConfig(config));
|
|
5656
|
+
validationError || startUpload(formatUploadConfig(config, secrets));
|
|
5085
5657
|
}
|
|
5086
5658
|
}
|
|
5087
5659
|
) })
|
|
@@ -5089,11 +5661,14 @@ function UploadConfiguration({
|
|
|
5089
5661
|
}
|
|
5090
5662
|
);
|
|
5091
5663
|
}
|
|
5092
|
-
function
|
|
5093
|
-
const
|
|
5094
|
-
return config.public_policy &&
|
|
5664
|
+
function setAdvancedPlaybackPolicy(config, secrets) {
|
|
5665
|
+
const advanced_playback_policies = [];
|
|
5666
|
+
return config.public_policy && advanced_playback_policies.push({ policy: "public" }), config.signed_policy && advanced_playback_policies.push({ policy: "signed" }), config.drm_policy && (secrets.drmConfigId ? advanced_playback_policies.push({
|
|
5667
|
+
policy: "drm",
|
|
5668
|
+
drm_configuration_id: secrets.drmConfigId ?? void 0
|
|
5669
|
+
}) : console.error("Selected DRM Policy but missing DRM Configuration Id")), advanced_playback_policies;
|
|
5095
5670
|
}
|
|
5096
|
-
function formatUploadConfig(config) {
|
|
5671
|
+
function formatUploadConfig(config, secrets) {
|
|
5097
5672
|
const generated_subtitles = config.text_tracks.filter(isAutogeneratedTrack).map((track) => ({
|
|
5098
5673
|
name: track.name,
|
|
5099
5674
|
language_code: track.language_code
|
|
@@ -5117,7 +5692,7 @@ function formatUploadConfig(config) {
|
|
|
5117
5692
|
)
|
|
5118
5693
|
],
|
|
5119
5694
|
static_renditions: config.static_renditions.length > 0 ? config.static_renditions.map((resolution) => ({ resolution })) : void 0,
|
|
5120
|
-
|
|
5695
|
+
advanced_playback_policies: setAdvancedPlaybackPolicy(config, secrets),
|
|
5121
5696
|
max_resolution_tier: config.max_resolution_tier,
|
|
5122
5697
|
video_quality: config.video_quality,
|
|
5123
5698
|
normalize_audio: config.normalize_audio
|
|
@@ -5329,8 +5904,13 @@ function Uploader(props) {
|
|
|
5329
5904
|
case "reset":
|
|
5330
5905
|
case "complete":
|
|
5331
5906
|
return uploadRef.current?.unsubscribe(), uploadRef.current = null, uploadingDocumentId.current = null, INITIAL_STATE;
|
|
5332
|
-
case "error":
|
|
5333
|
-
|
|
5907
|
+
case "error": {
|
|
5908
|
+
uploadRef.current?.unsubscribe(), uploadRef.current = null, uploadingDocumentId.current = null;
|
|
5909
|
+
let error = action.error;
|
|
5910
|
+
return isServerError(action.error) && hasPlaybackPolicy(action.settings, "drm") && (error = new Error(
|
|
5911
|
+
"Unknown Error while uploading DRM protected content. Make sure your DRM configuration ID is valid and set correctly"
|
|
5912
|
+
)), Object.assign({}, INITIAL_STATE, { error });
|
|
5913
|
+
}
|
|
5334
5914
|
default:
|
|
5335
5915
|
return prev;
|
|
5336
5916
|
}
|
|
@@ -5409,7 +5989,7 @@ function Uploader(props) {
|
|
|
5409
5989
|
}
|
|
5410
5990
|
},
|
|
5411
5991
|
complete: () => dispatch({ action: "complete" }),
|
|
5412
|
-
error: (error) => dispatch({ action: "error", error })
|
|
5992
|
+
error: (error) => dispatch({ action: "error", error, settings })
|
|
5413
5993
|
});
|
|
5414
5994
|
}, invalidFileToast = React.useCallback(() => {
|
|
5415
5995
|
toast.push({
|
|
@@ -5460,11 +6040,11 @@ function Uploader(props) {
|
|
|
5460
6040
|
idx > -1 && dragEnteredEls.current.splice(idx, 1), dragEnteredEls.current.length === 0 && setDragState(null);
|
|
5461
6041
|
};
|
|
5462
6042
|
if (state.error !== null) {
|
|
5463
|
-
const error =
|
|
6043
|
+
const error = state.error;
|
|
5464
6044
|
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gap: 3, direction: "column", justify: "center", align: "center", children: [
|
|
5465
6045
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 5, muted: !0, children: /* @__PURE__ */ jsxRuntime.jsx(icons.ErrorOutlineIcon, {}) }),
|
|
5466
6046
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: "Something went wrong" }),
|
|
5467
|
-
error instanceof Error && error.message && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, muted: !0, children: error.message }),
|
|
6047
|
+
error instanceof Error && error.message && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, muted: !0, weight: "semibold", style: { textAlign: "center" }, children: error.message }),
|
|
5468
6048
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { text: "Upload another file", onClick: () => dispatch({ action: "reset" }) })
|
|
5469
6049
|
] });
|
|
5470
6050
|
}
|
|
@@ -5549,6 +6129,7 @@ function Uploader(props) {
|
|
|
5549
6129
|
props.dialogState === "select-video" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5550
6130
|
InputBrowser,
|
|
5551
6131
|
{
|
|
6132
|
+
config: props.config,
|
|
5552
6133
|
asset: props.asset,
|
|
5553
6134
|
onChange: props.onChange,
|
|
5554
6135
|
setDialogState: props.setDialogState
|
|
@@ -5633,7 +6214,12 @@ const muxVideoSchema = {
|
|
|
5633
6214
|
{ type: "number", name: "max_width" },
|
|
5634
6215
|
{ type: "number", name: "max_frame_rate" },
|
|
5635
6216
|
{ type: "number", name: "duration" },
|
|
5636
|
-
{ type: "number", name: "max_height" }
|
|
6217
|
+
{ type: "number", name: "max_height" },
|
|
6218
|
+
{ type: "string", name: "language_code" },
|
|
6219
|
+
{ type: "string", name: "name" },
|
|
6220
|
+
{ type: "string", name: "status" },
|
|
6221
|
+
{ type: "string", name: "text_source" },
|
|
6222
|
+
{ type: "string", name: "text_type" }
|
|
5637
6223
|
]
|
|
5638
6224
|
}, muxPlaybackId = {
|
|
5639
6225
|
name: "mux.playbackId",
|
|
@@ -5795,6 +6381,7 @@ const muxVideoSchema = {
|
|
|
5795
6381
|
normalize_audio: !1,
|
|
5796
6382
|
defaultPublic: !0,
|
|
5797
6383
|
defaultSigned: !1,
|
|
6384
|
+
defaultDrm: !1,
|
|
5798
6385
|
tool: DEFAULT_TOOL_CONFIG,
|
|
5799
6386
|
allowedRolesForConfiguration: [],
|
|
5800
6387
|
acceptedMimeTypes: ["video/*", "audio/*"]
|