sanity-plugin-mux-input 2.14.0 → 2.15.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 +771 -351
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +773 -353
- 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 +1 -1
- package/src/components/SelectAsset.tsx +9 -3
- package/src/components/StudioTool.tsx +2 -2
- package/src/components/UploadConfiguration.tsx +104 -343
- package/src/components/Uploader.tsx +18 -7
- package/src/components/VideoDetails/VideoDetails.tsx +28 -8
- 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/useSaveSecrets.ts +10 -3
- package/src/hooks/useSecretsDocumentValues.ts +9 -1
- package/src/hooks/useSecretsFormState.ts +6 -3
- 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,7 +1316,7 @@ 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
|
)
|
|
@@ -1548,9 +1680,9 @@ async function downloadVttFile(client, asset, track) {
|
|
|
1548
1680
|
const playbackId = getPlaybackId(asset);
|
|
1549
1681
|
if (!playbackId)
|
|
1550
1682
|
throw new Error("Playback ID is required");
|
|
1551
|
-
const playbackPolicy = getPlaybackPolicy(asset);
|
|
1683
|
+
const playbackPolicy = getPlaybackPolicy(asset)?.policy;
|
|
1552
1684
|
let downloadUrl = `https://stream.mux.com/${playbackId}/text/${track.id}.vtt`;
|
|
1553
|
-
if (playbackPolicy === "signed") {
|
|
1685
|
+
if (playbackPolicy === "signed" || playbackPolicy === "drm") {
|
|
1554
1686
|
const token = generateJwt(client, playbackId, "v");
|
|
1555
1687
|
downloadUrl += `?token=${token}`;
|
|
1556
1688
|
}
|
|
@@ -2028,7 +2160,7 @@ function EditCaptionDialog({ asset, track, onUpdate, onClose }) {
|
|
|
2028
2160
|
const playbackId = getPlaybackId(asset);
|
|
2029
2161
|
if (!playbackId) return "";
|
|
2030
2162
|
let url = `https://stream.mux.com/${playbackId}/text/${track.id}.vtt`;
|
|
2031
|
-
if (getPlaybackPolicy(asset) === "signed") {
|
|
2163
|
+
if (getPlaybackPolicy(asset)?.policy === "signed") {
|
|
2032
2164
|
const token = generateJwt(client, playbackId, "v");
|
|
2033
2165
|
url += `?token=${token}`;
|
|
2034
2166
|
}
|
|
@@ -2684,13 +2816,13 @@ const DialogStateContext = React.createContext({
|
|
|
2684
2816
|
setDialogState,
|
|
2685
2817
|
children
|
|
2686
2818
|
}) => /* @__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,
|
|
2819
|
+
function getVideoSrc({ client, muxPlaybackId: muxPlaybackId2 }) {
|
|
2820
|
+
const searchParams = new URLSearchParams();
|
|
2821
|
+
if (muxPlaybackId2.policy === "signed" || muxPlaybackId2.policy === "drm") {
|
|
2822
|
+
const token = generateJwt(client, muxPlaybackId2.id, "v");
|
|
2691
2823
|
searchParams.set("token", token);
|
|
2692
2824
|
}
|
|
2693
|
-
return `https://stream.mux.com/${
|
|
2825
|
+
return `https://stream.mux.com/${muxPlaybackId2.id}.m3u8?${searchParams}`;
|
|
2694
2826
|
}
|
|
2695
2827
|
function CaptionsDialog({ asset }) {
|
|
2696
2828
|
const { setDialogState } = useDialogStateContext(), dialogId = `CaptionsDialog${React.useId()}`;
|
|
@@ -2800,24 +2932,56 @@ function VideoPlayer({
|
|
|
2800
2932
|
hlsConfig,
|
|
2801
2933
|
...props
|
|
2802
2934
|
}) {
|
|
2803
|
-
const client = useClient(), { dialogState } = useDialogStateContext(), isAudio = assetIsAudio(asset), muxPlayer = React.useRef(null), {
|
|
2804
|
-
src: videoSrc,
|
|
2805
|
-
thumbnail: thumbnailSrc,
|
|
2806
|
-
error
|
|
2807
|
-
} = React.useMemo(() => {
|
|
2935
|
+
const client = useClient(), { dialogState } = useDialogStateContext(), isAudio = assetIsAudio(asset), muxPlayer = React.useRef(null), [error, setError] = React.useState(), playbackId = React.useMemo(() => {
|
|
2808
2936
|
try {
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
return
|
|
2937
|
+
return getPlaybackId(asset, ["public", "signed", "drm"]);
|
|
2938
|
+
} catch {
|
|
2939
|
+
setError(new TypeError("Asset has no playback ID"));
|
|
2940
|
+
return;
|
|
2813
2941
|
}
|
|
2814
|
-
}, [asset
|
|
2942
|
+
}, [asset]), muxPlaybackId2 = React.useMemo(() => {
|
|
2943
|
+
if (playbackId)
|
|
2944
|
+
return getPlaybackPolicyById(asset, playbackId);
|
|
2945
|
+
}, [asset, playbackId]), src = React.useMemo(() => {
|
|
2946
|
+
if (playbackId && muxPlaybackId2)
|
|
2947
|
+
return tryWithSuspend(
|
|
2948
|
+
() => getVideoSrc({ muxPlaybackId: muxPlaybackId2, client }),
|
|
2949
|
+
(e) => {
|
|
2950
|
+
setError(e);
|
|
2951
|
+
}
|
|
2952
|
+
);
|
|
2953
|
+
}, [muxPlaybackId2, playbackId, client]), poster = React.useMemo(() => tryWithSuspend(
|
|
2954
|
+
() => getPosterSrc({ asset, client, width: thumbnailWidth }),
|
|
2955
|
+
(e) => {
|
|
2956
|
+
setError(e);
|
|
2957
|
+
}
|
|
2958
|
+
), [asset, client, thumbnailWidth]), signedToken = React.useMemo(() => {
|
|
2815
2959
|
try {
|
|
2816
|
-
return new URL(
|
|
2960
|
+
return new URL(src).searchParams.get("token");
|
|
2817
2961
|
} catch {
|
|
2818
|
-
return
|
|
2962
|
+
return;
|
|
2819
2963
|
}
|
|
2820
|
-
}, [
|
|
2964
|
+
}, [src]), drmToken = React.useMemo(() => {
|
|
2965
|
+
if (playbackId && muxPlaybackId2?.policy === "drm")
|
|
2966
|
+
return tryWithSuspend(
|
|
2967
|
+
() => generateJwt(client, playbackId, "d"),
|
|
2968
|
+
(e) => {
|
|
2969
|
+
setError(e);
|
|
2970
|
+
}
|
|
2971
|
+
);
|
|
2972
|
+
}, [client, muxPlaybackId2?.policy, playbackId]), tokens = React.useMemo(() => {
|
|
2973
|
+
try {
|
|
2974
|
+
const partialTokens = {
|
|
2975
|
+
playback: void 0,
|
|
2976
|
+
thumbnail: void 0,
|
|
2977
|
+
storyboard: void 0,
|
|
2978
|
+
drm: void 0
|
|
2979
|
+
};
|
|
2980
|
+
return signedToken && (partialTokens.playback = signedToken, partialTokens.thumbnail = signedToken, partialTokens.storyboard = signedToken), drmToken && (partialTokens.drm = drmToken), { ...partialTokens };
|
|
2981
|
+
} catch {
|
|
2982
|
+
return;
|
|
2983
|
+
}
|
|
2984
|
+
}, [signedToken, drmToken]), [width, height] = (asset?.data?.aspect_ratio ?? "16:9").split(":").map(Number), targetAspectRatio = props.forceAspectRatio || (Number.isNaN(width) ? 16 / 9 : width / height);
|
|
2821
2985
|
let aspectRatio = Math.max(MIN_ASPECT_RATIO, targetAspectRatio);
|
|
2822
2986
|
return isAudio && (aspectRatio = props.forceAspectRatio ? (
|
|
2823
2987
|
// Make it wider when forcing aspect ratio to balance with videos' rendering height (audio players overflow a bit)
|
|
@@ -2833,7 +2997,7 @@ function VideoPlayer({
|
|
|
2833
2997
|
...isAudio && { display: "flex", alignItems: "flex-end" }
|
|
2834
2998
|
},
|
|
2835
2999
|
children: [
|
|
2836
|
-
|
|
3000
|
+
src && poster && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2837
3001
|
isAudio && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2838
3002
|
AudioIcon,
|
|
2839
3003
|
{
|
|
@@ -2848,34 +3012,36 @@ function VideoPlayer({
|
|
|
2848
3012
|
}
|
|
2849
3013
|
}
|
|
2850
3014
|
),
|
|
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
|
-
|
|
3015
|
+
/* @__PURE__ */ jsxRuntime.jsxs(React.Suspense, { fallback: null, children: [
|
|
3016
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3017
|
+
MuxPlayer__default.default,
|
|
3018
|
+
{
|
|
3019
|
+
poster: isAudio ? void 0 : poster,
|
|
3020
|
+
ref: muxPlayer,
|
|
3021
|
+
...props,
|
|
3022
|
+
playsInline: !0,
|
|
3023
|
+
playbackId,
|
|
3024
|
+
tokens,
|
|
3025
|
+
preload: "metadata",
|
|
3026
|
+
crossOrigin: "anonymous",
|
|
3027
|
+
metadata: {
|
|
3028
|
+
player_name: "Sanity Admin Dashboard",
|
|
3029
|
+
player_version: "2.15.0",
|
|
3030
|
+
page_type: "Preview Player"
|
|
3031
|
+
},
|
|
3032
|
+
audio: isAudio,
|
|
3033
|
+
_hlsConfig: hlsConfig,
|
|
3034
|
+
style: {
|
|
3035
|
+
...!isAudio && { height: "100%" },
|
|
3036
|
+
width: "100%",
|
|
3037
|
+
display: "block",
|
|
3038
|
+
objectFit: "contain",
|
|
3039
|
+
...isAudio && { alignSelf: "end" }
|
|
3040
|
+
}
|
|
2875
3041
|
}
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
3042
|
+
),
|
|
3043
|
+
children
|
|
3044
|
+
] })
|
|
2879
3045
|
] }),
|
|
2880
3046
|
error ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2881
3047
|
"div",
|
|
@@ -3167,6 +3333,7 @@ function getVideoMetadata(doc) {
|
|
|
3167
3333
|
playbackId: doc.playbackId,
|
|
3168
3334
|
createdAt: date,
|
|
3169
3335
|
duration: doc.data?.duration ? formatSeconds(doc.data?.duration) : void 0,
|
|
3336
|
+
playback_ids: doc.data?.playback_ids,
|
|
3170
3337
|
aspect_ratio: doc.data?.aspect_ratio,
|
|
3171
3338
|
max_stored_resolution: doc.data?.max_stored_resolution,
|
|
3172
3339
|
max_stored_frame_rate: doc.data?.max_stored_frame_rate,
|
|
@@ -3469,14 +3636,7 @@ const AssetInput = (props) => /* @__PURE__ */ jsxRuntime.jsx(FormField$1, { titl
|
|
|
3469
3636
|
),
|
|
3470
3637
|
/* @__PURE__ */ jsxRuntime.jsx(IconInfo, { text: `Mux ID:
|
|
3471
3638
|
${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
|
-
)
|
|
3639
|
+
/* @__PURE__ */ jsxRuntime.jsx(PlaybackIds, { playback_ids: displayInfo.playback_ids })
|
|
3480
3640
|
] })
|
|
3481
3641
|
] })
|
|
3482
3642
|
}
|
|
@@ -3499,6 +3659,25 @@ ${displayInfo.id}`, icon: icons.TagIcon, size: 2 }),
|
|
|
3499
3659
|
]
|
|
3500
3660
|
}
|
|
3501
3661
|
);
|
|
3662
|
+
}, PlaybackIds = ({ playback_ids }) => playback_ids ? playback_ids.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3663
|
+
IconInfo,
|
|
3664
|
+
{
|
|
3665
|
+
text: `Playback ID [${policyToText(entry.policy)}]: ${entry.id}`,
|
|
3666
|
+
icon: icons.TagIcon,
|
|
3667
|
+
size: 2
|
|
3668
|
+
},
|
|
3669
|
+
entry.id
|
|
3670
|
+
)) : /* @__PURE__ */ jsxRuntime.jsx(IconInfo, { text: "No Playback ID", icon: icons.TagIcon, size: 2 }), policyToText = (policy) => {
|
|
3671
|
+
switch (policy) {
|
|
3672
|
+
case "drm":
|
|
3673
|
+
return "DRM";
|
|
3674
|
+
case "signed":
|
|
3675
|
+
return "Signed";
|
|
3676
|
+
case "public":
|
|
3677
|
+
return "Public";
|
|
3678
|
+
default:
|
|
3679
|
+
return policy;
|
|
3680
|
+
}
|
|
3502
3681
|
}, VideoMetadata = (props) => {
|
|
3503
3682
|
if (!props.asset)
|
|
3504
3683
|
return null;
|
|
@@ -3592,10 +3771,12 @@ function VideoInBrowser({
|
|
|
3592
3771
|
onEdit,
|
|
3593
3772
|
asset
|
|
3594
3773
|
}) {
|
|
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]);
|
|
3774
|
+
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
3775
|
if (!asset)
|
|
3597
3776
|
return null;
|
|
3598
|
-
const playbackPolicy = getPlaybackPolicy(asset)
|
|
3777
|
+
const playbackPolicy = getPlaybackPolicy(asset), onClickPlay = () => {
|
|
3778
|
+
playbackPolicy?.policy === "drm" && !hasShownWarning ? setRenderVideo("pre-render-warn") : setRenderVideo("render-video");
|
|
3779
|
+
};
|
|
3599
3780
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3600
3781
|
ui.Card,
|
|
3601
3782
|
{
|
|
@@ -3607,7 +3788,7 @@ function VideoInBrowser({
|
|
|
3607
3788
|
position: "relative"
|
|
3608
3789
|
},
|
|
3609
3790
|
children: [
|
|
3610
|
-
playbackPolicy === "signed" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3791
|
+
playbackPolicy?.policy === "signed" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3611
3792
|
ui.Tooltip,
|
|
3612
3793
|
{
|
|
3613
3794
|
animate: !0,
|
|
@@ -3624,7 +3805,7 @@ function VideoInBrowser({
|
|
|
3624
3805
|
position: "absolute",
|
|
3625
3806
|
left: "1em",
|
|
3626
3807
|
top: "1em",
|
|
3627
|
-
zIndex:
|
|
3808
|
+
zIndex: 11
|
|
3628
3809
|
},
|
|
3629
3810
|
padding: 2,
|
|
3630
3811
|
border: !0,
|
|
@@ -3633,6 +3814,32 @@ function VideoInBrowser({
|
|
|
3633
3814
|
)
|
|
3634
3815
|
}
|
|
3635
3816
|
),
|
|
3817
|
+
playbackPolicy?.policy === "drm" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3818
|
+
ui.Tooltip,
|
|
3819
|
+
{
|
|
3820
|
+
animate: !0,
|
|
3821
|
+
content: /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { padding: 2, radius: 2, children: /* @__PURE__ */ jsxRuntime.jsx(IconInfo, { icon: icons.LockIcon, text: "DRM playback policy", size: 2 }) }),
|
|
3822
|
+
placement: "right",
|
|
3823
|
+
fallbackPlacements: ["top", "bottom"],
|
|
3824
|
+
portal: !0,
|
|
3825
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3826
|
+
ui.Card,
|
|
3827
|
+
{
|
|
3828
|
+
tone: "caution",
|
|
3829
|
+
style: {
|
|
3830
|
+
borderRadius: "0.25rem",
|
|
3831
|
+
position: "absolute",
|
|
3832
|
+
left: "1em",
|
|
3833
|
+
top: "1em",
|
|
3834
|
+
zIndex: 11
|
|
3835
|
+
},
|
|
3836
|
+
padding: 2,
|
|
3837
|
+
border: !0,
|
|
3838
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { muted: !0, size: 1, weight: "semibold", style: { color: "var(--card-icon-color)" }, children: "DRM" })
|
|
3839
|
+
}
|
|
3840
|
+
)
|
|
3841
|
+
}
|
|
3842
|
+
),
|
|
3636
3843
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3637
3844
|
ui.Stack,
|
|
3638
3845
|
{
|
|
@@ -3642,7 +3849,15 @@ function VideoInBrowser({
|
|
|
3642
3849
|
gridTemplateRows: "min-content min-content 1fr"
|
|
3643
3850
|
},
|
|
3644
3851
|
children: [
|
|
3645
|
-
renderVideo
|
|
3852
|
+
renderVideo === "pre-render-warn" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3853
|
+
DRMWarningDialog,
|
|
3854
|
+
{
|
|
3855
|
+
onClose: () => {
|
|
3856
|
+
setRenderVideo("render-video");
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
3859
|
+
),
|
|
3860
|
+
renderVideo === "render-video" ? /* @__PURE__ */ jsxRuntime.jsx(VideoPlayer, { asset, autoPlay: !0, forceAspectRatio: THUMBNAIL_ASPECT_RATIO }) : /* @__PURE__ */ jsxRuntime.jsxs(PlayButton, { onClick: onClickPlay, children: [
|
|
3646
3861
|
/* @__PURE__ */ jsxRuntime.jsx("div", { "data-play": !0, children: /* @__PURE__ */ jsxRuntime.jsx(icons.PlayIcon, {}) }),
|
|
3647
3862
|
assetIsAudio(asset) ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3648
3863
|
"div",
|
|
@@ -3704,12 +3919,12 @@ function VideoInBrowser({
|
|
|
3704
3919
|
}
|
|
3705
3920
|
);
|
|
3706
3921
|
}
|
|
3707
|
-
function VideosBrowser({ onSelect }) {
|
|
3922
|
+
function VideosBrowser({ onSelect, config }) {
|
|
3708
3923
|
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
3924
|
() => assets.find((a2) => a2._id === editedAsset?._id) || editedAsset,
|
|
3710
3925
|
[editedAsset, assets]
|
|
3711
3926
|
), pageStart = page * pageLimit, pageEnd = pageStart + pageLimit;
|
|
3712
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3927
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(DrmPlaybackWarningContextProvider, { config, children: [
|
|
3713
3928
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { padding: 4, space: 4, style: { minHeight: "50vh" }, children: [
|
|
3714
3929
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { justify: "space-between", align: "center", children: [
|
|
3715
3930
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 3, children: [
|
|
@@ -3723,7 +3938,7 @@ function VideosBrowser({ onSelect }) {
|
|
|
3723
3938
|
}
|
|
3724
3939
|
),
|
|
3725
3940
|
/* @__PURE__ */ jsxRuntime.jsx(SelectSortOptions, { setSort, sort }),
|
|
3726
|
-
/* @__PURE__ */ jsxRuntime.jsx(PageSelector, { page, setPage, total: pageTotal
|
|
3941
|
+
/* @__PURE__ */ jsxRuntime.jsx(PageSelector, { page, setPage, total: pageTotal })
|
|
3727
3942
|
] }),
|
|
3728
3943
|
(onSelect ? "input" : "tool") == "tool" && /* @__PURE__ */ jsxRuntime.jsxs(ui.Inline, { space: 2, children: [
|
|
3729
3944
|
/* @__PURE__ */ jsxRuntime.jsx(ImportVideosFromMux, {}),
|
|
@@ -3764,7 +3979,7 @@ function VideosBrowser({ onSelect }) {
|
|
|
3764
3979
|
freshEditedAsset && /* @__PURE__ */ jsxRuntime.jsx(VideoDetails, { closeDialog: () => setEditedAsset(null), asset: freshEditedAsset })
|
|
3765
3980
|
] });
|
|
3766
3981
|
}
|
|
3767
|
-
const StudioTool = () => /* @__PURE__ */ jsxRuntime.jsx(VideosBrowser, {}), DEFAULT_TOOL_CONFIG = {
|
|
3982
|
+
const StudioTool = (config) => /* @__PURE__ */ jsxRuntime.jsx(VideosBrowser, { config }), DEFAULT_TOOL_CONFIG = {
|
|
3768
3983
|
icon: ToolIcon,
|
|
3769
3984
|
title: "Videos"
|
|
3770
3985
|
};
|
|
@@ -4122,6 +4337,9 @@ function isValidUrl(url) {
|
|
|
4122
4337
|
return !1;
|
|
4123
4338
|
}
|
|
4124
4339
|
}
|
|
4340
|
+
function isServerError(error) {
|
|
4341
|
+
return "statusCode" in error && typeof error.statusCode == "number" && 500 <= error.statusCode && error.statusCode <= 600;
|
|
4342
|
+
}
|
|
4125
4343
|
function extractDroppedFiles(dataTransfer) {
|
|
4126
4344
|
const files = Array.from(dataTransfer.files || []), items = Array.from(dataTransfer.items || []);
|
|
4127
4345
|
return files && files.length > 0 ? Promise.resolve(files) : normalizeItems(items).then((arr) => arr.flat());
|
|
@@ -4163,7 +4381,12 @@ function walk(entry) {
|
|
|
4163
4381
|
}
|
|
4164
4382
|
return Promise.resolve([]);
|
|
4165
4383
|
}
|
|
4166
|
-
function SelectAssets({
|
|
4384
|
+
function SelectAssets({
|
|
4385
|
+
asset: selectedAsset,
|
|
4386
|
+
onChange,
|
|
4387
|
+
setDialogState,
|
|
4388
|
+
config
|
|
4389
|
+
}) {
|
|
4167
4390
|
const handleSelect = React.useCallback(
|
|
4168
4391
|
(chosenAsset) => {
|
|
4169
4392
|
chosenAsset?._id || onChange(sanity.PatchEvent.from([sanity.unset(["asset"])])), chosenAsset._id !== selectedAsset?._id && onChange(
|
|
@@ -4175,7 +4398,7 @@ function SelectAssets({ asset: selectedAsset, onChange, setDialogState }) {
|
|
|
4175
4398
|
},
|
|
4176
4399
|
[onChange, setDialogState, selectedAsset]
|
|
4177
4400
|
);
|
|
4178
|
-
return /* @__PURE__ */ jsxRuntime.jsx(VideosBrowser, { onSelect: handleSelect });
|
|
4401
|
+
return /* @__PURE__ */ jsxRuntime.jsx(VideosBrowser, { onSelect: handleSelect, config });
|
|
4179
4402
|
}
|
|
4180
4403
|
const StyledDialog = styledComponents.styled(ui.Dialog)`
|
|
4181
4404
|
> div[data-ui='DialogCard'] > div[data-ui='Card'] {
|
|
@@ -4185,7 +4408,8 @@ const StyledDialog = styledComponents.styled(ui.Dialog)`
|
|
|
4185
4408
|
function InputBrowser({
|
|
4186
4409
|
setDialogState,
|
|
4187
4410
|
asset,
|
|
4188
|
-
onChange
|
|
4411
|
+
onChange,
|
|
4412
|
+
config
|
|
4189
4413
|
}) {
|
|
4190
4414
|
const id = `InputBrowser${React.useId()}`, handleClose = React.useCallback(() => setDialogState(!1), [setDialogState]);
|
|
4191
4415
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -4196,7 +4420,15 @@ function InputBrowser({
|
|
|
4196
4420
|
id,
|
|
4197
4421
|
onClose: handleClose,
|
|
4198
4422
|
width: 2,
|
|
4199
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4423
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4424
|
+
SelectAssets,
|
|
4425
|
+
{
|
|
4426
|
+
config,
|
|
4427
|
+
asset,
|
|
4428
|
+
onChange,
|
|
4429
|
+
setDialogState
|
|
4430
|
+
}
|
|
4431
|
+
)
|
|
4200
4432
|
}
|
|
4201
4433
|
);
|
|
4202
4434
|
}
|
|
@@ -4412,7 +4644,7 @@ const FileButton = styledComponents.styled(ui.MenuItem)(({ theme }) => {
|
|
|
4412
4644
|
color: white;
|
|
4413
4645
|
`, isVideoAsset = (asset) => asset._type === "mux.videoAsset";
|
|
4414
4646
|
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]);
|
|
4647
|
+
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), onReset = React.useCallback(() => onChange(sanity.PatchEvent.from(sanity.unset([]))), [onChange]);
|
|
4416
4648
|
return React.useEffect(() => {
|
|
4417
4649
|
open && dialogState && setOpen(!1);
|
|
4418
4650
|
}, [dialogState, open]), ui.useClickOutsideEvent(
|
|
@@ -4513,6 +4745,79 @@ function PlayerActionsMenu(props) {
|
|
|
4513
4745
|
] });
|
|
4514
4746
|
}
|
|
4515
4747
|
var PlayerActionsMenu$1 = React.memo(PlayerActionsMenu);
|
|
4748
|
+
function useFetchFileSize(stagedUpload, maxFileSize) {
|
|
4749
|
+
const [fileSize, setFileSize] = React.useState(null), [isLoadingFileSize, setIsLoadingFileSize] = React.useState(!1), [canSkipFileSizeValidation, setCanSkipFileSizeValidation] = React.useState(!1);
|
|
4750
|
+
return React.useEffect(() => {
|
|
4751
|
+
if (stagedUpload.type === "url") {
|
|
4752
|
+
setIsLoadingFileSize(!1), setCanSkipFileSizeValidation(!1), setFileSize(null);
|
|
4753
|
+
const url = stagedUpload.url;
|
|
4754
|
+
(async () => {
|
|
4755
|
+
setIsLoadingFileSize(!0);
|
|
4756
|
+
try {
|
|
4757
|
+
const contentLength = (await fetch(url, { method: "HEAD" })).headers.get("content-length"), newFileSize = contentLength ? parseInt(contentLength, 10) : null;
|
|
4758
|
+
setIsLoadingFileSize(!1), newFileSize && setFileSize(newFileSize), newFileSize === null && maxFileSize !== void 0 && setCanSkipFileSizeValidation(!0);
|
|
4759
|
+
} catch {
|
|
4760
|
+
console.warn("Could not validate file size from URL"), setCanSkipFileSizeValidation(!0), setIsLoadingFileSize(!1);
|
|
4761
|
+
}
|
|
4762
|
+
})();
|
|
4763
|
+
}
|
|
4764
|
+
stagedUpload.type === "file" && setFileSize(stagedUpload.files[0].size);
|
|
4765
|
+
}, [maxFileSize, stagedUpload, stagedUpload.type]), {
|
|
4766
|
+
fileSize,
|
|
4767
|
+
isLoadingFileSize,
|
|
4768
|
+
canSkipFileSizeValidation
|
|
4769
|
+
};
|
|
4770
|
+
}
|
|
4771
|
+
function useMediaMetadata(stagedUpload) {
|
|
4772
|
+
const [videoAssetMetadata, setVideoAssetMetadata] = React.useState(null), [isLoadingMetadata, setIsLoadingMetadata] = React.useState(!1);
|
|
4773
|
+
return React.useEffect(() => {
|
|
4774
|
+
let videoSrc = null;
|
|
4775
|
+
if (stagedUpload.type === "file") {
|
|
4776
|
+
const file = stagedUpload.files[0];
|
|
4777
|
+
videoSrc = URL.createObjectURL(file);
|
|
4778
|
+
}
|
|
4779
|
+
if (stagedUpload.type === "url" && (videoSrc = stagedUpload.url), setVideoAssetMetadata((old) => ({
|
|
4780
|
+
...old,
|
|
4781
|
+
duration: void 0,
|
|
4782
|
+
width: void 0,
|
|
4783
|
+
height: void 0
|
|
4784
|
+
})), !videoSrc) return () => null;
|
|
4785
|
+
setIsLoadingMetadata(!0);
|
|
4786
|
+
const videoElement = document.createElement("video");
|
|
4787
|
+
videoElement.preload = "metadata";
|
|
4788
|
+
const metadataListeners = [
|
|
4789
|
+
() => {
|
|
4790
|
+
setIsLoadingMetadata(!1);
|
|
4791
|
+
},
|
|
4792
|
+
() => {
|
|
4793
|
+
const duration = videoElement.duration, width = videoElement.videoWidth, height = videoElement.videoHeight, isAudioOnly = width <= 0 && height <= 0;
|
|
4794
|
+
setVideoAssetMetadata((old) => ({
|
|
4795
|
+
...old,
|
|
4796
|
+
duration,
|
|
4797
|
+
width,
|
|
4798
|
+
height,
|
|
4799
|
+
isAudioOnly
|
|
4800
|
+
}));
|
|
4801
|
+
}
|
|
4802
|
+
], cleanupVideo = (videoEl) => {
|
|
4803
|
+
const currentVideoSrc = videoEl?.src;
|
|
4804
|
+
videoEl && (metadataListeners.forEach(
|
|
4805
|
+
(listener) => videoEl.removeEventListener("loadedmetadata", listener)
|
|
4806
|
+
), videoEl.onerror = null, videoEl.src = "", videoEl.load()), currentVideoSrc?.startsWith("blob:") && URL.revokeObjectURL(currentVideoSrc);
|
|
4807
|
+
};
|
|
4808
|
+
return metadataListeners.push(() => setTimeout(() => cleanupVideo(videoElement), 0)), videoElement.onerror = () => {
|
|
4809
|
+
setIsLoadingMetadata(!1), console.warn("Could not read video metadata for validation"), cleanupVideo(videoElement);
|
|
4810
|
+
}, metadataListeners.forEach(
|
|
4811
|
+
(listener) => videoElement.addEventListener("loadedmetadata", listener)
|
|
4812
|
+
), videoElement.src = videoSrc, () => {
|
|
4813
|
+
cleanupVideo(videoElement);
|
|
4814
|
+
};
|
|
4815
|
+
}, [stagedUpload.type, stagedUpload]), {
|
|
4816
|
+
videoAssetMetadata,
|
|
4817
|
+
setVideoAssetMetadata,
|
|
4818
|
+
isLoadingMetadata
|
|
4819
|
+
};
|
|
4820
|
+
}
|
|
4516
4821
|
function formatBytes(bytes, si = !1, dp = 1) {
|
|
4517
4822
|
const thresh = si ? 1e3 : 1024;
|
|
4518
4823
|
if (Math.abs(bytes) < thresh)
|
|
@@ -4602,13 +4907,14 @@ function PlaybackPolicyOption({
|
|
|
4602
4907
|
optionName,
|
|
4603
4908
|
description,
|
|
4604
4909
|
dispatch,
|
|
4605
|
-
action
|
|
4910
|
+
action,
|
|
4911
|
+
disabled
|
|
4606
4912
|
}) {
|
|
4607
4913
|
const [scale, setScale] = React.useState(1), boxStyle = {
|
|
4608
4914
|
outline: "0.01rem solid grey",
|
|
4609
4915
|
transform: `scale(${scale})`,
|
|
4610
4916
|
transition: "transform 0.1s ease-in-out",
|
|
4611
|
-
cursor: "pointer",
|
|
4917
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
4612
4918
|
borderRadius: "0.25rem"
|
|
4613
4919
|
}, triggerAnimation = () => {
|
|
4614
4920
|
setScale(0.98), setTimeout(() => {
|
|
@@ -4616,15 +4922,24 @@ function PlaybackPolicyOption({
|
|
|
4616
4922
|
}, 100);
|
|
4617
4923
|
};
|
|
4618
4924
|
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
|
-
|
|
4925
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4926
|
+
ui.Checkbox,
|
|
4927
|
+
{
|
|
4928
|
+
id,
|
|
4929
|
+
required: !0,
|
|
4930
|
+
checked,
|
|
4931
|
+
onChange: () => {
|
|
4932
|
+
action && (triggerAnimation(), dispatch({
|
|
4933
|
+
action,
|
|
4934
|
+
value: !checked
|
|
4935
|
+
}));
|
|
4936
|
+
},
|
|
4937
|
+
disabled
|
|
4938
|
+
}
|
|
4939
|
+
),
|
|
4625
4940
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Grid, { gap: 3, children: [
|
|
4626
4941
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 3, weight: "bold", children: optionName }),
|
|
4627
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, muted: !0, children: description })
|
|
4942
|
+
typeof description == "string" ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, muted: !0, children: description }) : description
|
|
4628
4943
|
] })
|
|
4629
4944
|
] }) });
|
|
4630
4945
|
}
|
|
@@ -4649,7 +4964,7 @@ function PlaybackPolicy({
|
|
|
4649
4964
|
secrets,
|
|
4650
4965
|
dispatch
|
|
4651
4966
|
}) {
|
|
4652
|
-
const noPolicySelected = !(config.public_policy || config.signed_policy);
|
|
4967
|
+
const noPolicySelected = !(config.public_policy || config.signed_policy || config.drm_policy), drmPolicyDisabled = !secrets.drmConfigId;
|
|
4653
4968
|
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Grid, { gap: 3, children: [
|
|
4654
4969
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", children: "Advanced Playback Policies" }),
|
|
4655
4970
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -4658,7 +4973,10 @@ function PlaybackPolicy({
|
|
|
4658
4973
|
id: `${id}--public`,
|
|
4659
4974
|
checked: config.public_policy,
|
|
4660
4975
|
optionName: "Public",
|
|
4661
|
-
description:
|
|
4976
|
+
description: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
4977
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, muted: !0, children: "Playback IDs are accessible by constructing an HLS URL like" }),
|
|
4978
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Code, { children: "https://stream.mux.com/{PLAYBACK_ID}" })
|
|
4979
|
+
] }),
|
|
4662
4980
|
dispatch,
|
|
4663
4981
|
action: "public_policy"
|
|
4664
4982
|
}
|
|
@@ -4669,24 +4987,146 @@ function PlaybackPolicy({
|
|
|
4669
4987
|
id: `${id}--signed`,
|
|
4670
4988
|
checked: config.signed_policy,
|
|
4671
4989
|
optionName: "Signed",
|
|
4672
|
-
description:
|
|
4673
|
-
|
|
4990
|
+
description: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
4991
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 2, muted: !0, children: "Playback IDs should be used with tokens" }),
|
|
4992
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Code, { children: "https://stream.mux.com/{PLAYBACK_ID}?token={TOKEN}" }),
|
|
4993
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: 2, muted: !0, children: [
|
|
4994
|
+
"See",
|
|
4995
|
+
" ",
|
|
4996
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4997
|
+
"a",
|
|
4998
|
+
{
|
|
4999
|
+
href: "https://www.mux.com/docs/guides/secure-video-playback",
|
|
5000
|
+
target: "_blank",
|
|
5001
|
+
rel: "noopener noreferrer",
|
|
5002
|
+
children: "Secure video playback"
|
|
5003
|
+
}
|
|
5004
|
+
),
|
|
5005
|
+
" ",
|
|
5006
|
+
"for details about creating tokens."
|
|
5007
|
+
] })
|
|
5008
|
+
] }),
|
|
4674
5009
|
dispatch,
|
|
4675
5010
|
action: "signed_policy"
|
|
4676
5011
|
}
|
|
4677
5012
|
),
|
|
5013
|
+
drmPolicyDisabled ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
5014
|
+
PlaybackPolicyOption,
|
|
5015
|
+
{
|
|
5016
|
+
id: `${id}--drm`,
|
|
5017
|
+
checked: !1,
|
|
5018
|
+
optionName: "DRM - Disabled",
|
|
5019
|
+
description: /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: 2, muted: !0, children: [
|
|
5020
|
+
"To enable DRM add your DRM Configuration Id to your plugin configuration in the API Credentials view.",
|
|
5021
|
+
" ",
|
|
5022
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5023
|
+
"a",
|
|
5024
|
+
{
|
|
5025
|
+
href: "https://www.mux.com/support/human",
|
|
5026
|
+
target: "_blank",
|
|
5027
|
+
rel: "noopener noreferrer",
|
|
5028
|
+
children: "Contact us"
|
|
5029
|
+
}
|
|
5030
|
+
),
|
|
5031
|
+
" ",
|
|
5032
|
+
"to get started using DRM."
|
|
5033
|
+
] }) }),
|
|
5034
|
+
dispatch,
|
|
5035
|
+
disabled: !0
|
|
5036
|
+
}
|
|
5037
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
5038
|
+
PlaybackPolicyOption,
|
|
5039
|
+
{
|
|
5040
|
+
id: `${id}--drm`,
|
|
5041
|
+
checked: config.drm_policy,
|
|
5042
|
+
optionName: "DRM",
|
|
5043
|
+
description: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5044
|
+
/* @__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." }),
|
|
5045
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Code, { children: "https://stream.mux.com/{PLAYBACK_ID}?token={TOKEN}" }),
|
|
5046
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: 2, muted: !0, children: [
|
|
5047
|
+
"See",
|
|
5048
|
+
" ",
|
|
5049
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5050
|
+
"a",
|
|
5051
|
+
{
|
|
5052
|
+
href: "https://www.mux.com/docs/guides/protect-videos-with-drm#play-drm-protected-videos",
|
|
5053
|
+
target: "_blank",
|
|
5054
|
+
rel: "noopener noreferrer",
|
|
5055
|
+
children: "Protect videos with DRM"
|
|
5056
|
+
}
|
|
5057
|
+
),
|
|
5058
|
+
" ",
|
|
5059
|
+
"for details about configuring your player for DRM playback and",
|
|
5060
|
+
" ",
|
|
5061
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5062
|
+
"a",
|
|
5063
|
+
{
|
|
5064
|
+
href: "https://www.mux.com/docs/guides/secure-video-playback",
|
|
5065
|
+
target: "_blank",
|
|
5066
|
+
rel: "noopener noreferrer",
|
|
5067
|
+
children: "Secure video playback"
|
|
5068
|
+
}
|
|
5069
|
+
),
|
|
5070
|
+
" ",
|
|
5071
|
+
"for details about creating tokens."
|
|
5072
|
+
] })
|
|
5073
|
+
] }),
|
|
5074
|
+
dispatch,
|
|
5075
|
+
action: "drm_policy"
|
|
5076
|
+
}
|
|
5077
|
+
),
|
|
4678
5078
|
noPolicySelected && /* @__PURE__ */ jsxRuntime.jsx(PlaybackPolicyWarning, {})
|
|
4679
5079
|
] });
|
|
4680
5080
|
}
|
|
4681
|
-
const
|
|
4682
|
-
{ value: "basic", label: "Basic" },
|
|
4683
|
-
{ value: "plus", label: "Plus" },
|
|
4684
|
-
{ value: "premium", label: "Premium" }
|
|
4685
|
-
], RESOLUTION_TIERS = [
|
|
5081
|
+
const RESOLUTION_TIERS = [
|
|
4686
5082
|
{ value: "1080p", label: "1080p" },
|
|
4687
5083
|
{ value: "1440p", label: "1440p (2k)" },
|
|
4688
5084
|
{ value: "2160p", label: "2160p (4k)" }
|
|
4689
|
-
],
|
|
5085
|
+
], ResolutionTierSelector = ({
|
|
5086
|
+
id,
|
|
5087
|
+
config,
|
|
5088
|
+
dispatch,
|
|
5089
|
+
maxSupportedResolution
|
|
5090
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
5091
|
+
sanity.FormField,
|
|
5092
|
+
{
|
|
5093
|
+
title: "Resolution Tier",
|
|
5094
|
+
description: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5095
|
+
"The maximum",
|
|
5096
|
+
" ",
|
|
5097
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5098
|
+
"a",
|
|
5099
|
+
{
|
|
5100
|
+
href: "https://docs.mux.com/api-reference#video/operation/create-direct-upload",
|
|
5101
|
+
target: "_blank",
|
|
5102
|
+
rel: "noopener noreferrer",
|
|
5103
|
+
children: "resolution_tier"
|
|
5104
|
+
}
|
|
5105
|
+
),
|
|
5106
|
+
" ",
|
|
5107
|
+
"your asset is encoded, stored, and streamed at."
|
|
5108
|
+
] }),
|
|
5109
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { gap: 3, wrap: "wrap", children: RESOLUTION_TIERS.map(({ value, label }, index) => {
|
|
5110
|
+
const inputId = `${id}--type-${value}`;
|
|
5111
|
+
return index > maxSupportedResolution ? null : /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, children: [
|
|
5112
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5113
|
+
ui.Radio,
|
|
5114
|
+
{
|
|
5115
|
+
checked: config.max_resolution_tier === value,
|
|
5116
|
+
name: "asset-resolutiontier",
|
|
5117
|
+
onChange: (e) => dispatch({
|
|
5118
|
+
action: "max_resolution_tier",
|
|
5119
|
+
value: e.currentTarget.value
|
|
5120
|
+
}),
|
|
5121
|
+
value,
|
|
5122
|
+
id: inputId
|
|
5123
|
+
}
|
|
5124
|
+
),
|
|
5125
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: inputId, children: label })
|
|
5126
|
+
] }, value);
|
|
5127
|
+
}) })
|
|
5128
|
+
}
|
|
5129
|
+
), ADVANCED_RESOLUTIONS = [
|
|
4690
5130
|
{ value: "270p", label: "270p" },
|
|
4691
5131
|
{ value: "360p", label: "360p" },
|
|
4692
5132
|
{ value: "480p", label: "480p" },
|
|
@@ -4695,6 +5135,130 @@ const VIDEO_QUALITY_LEVELS = [
|
|
|
4695
5135
|
{ value: "1080p", label: "1080p" },
|
|
4696
5136
|
{ value: "1440p", label: "1440p" },
|
|
4697
5137
|
{ value: "2160p", label: "2160p" }
|
|
5138
|
+
], StaticRenditionSelector = ({
|
|
5139
|
+
id,
|
|
5140
|
+
config,
|
|
5141
|
+
dispatch
|
|
5142
|
+
}) => {
|
|
5143
|
+
const isAdvancedMode = React.useMemo(() => config.static_renditions.filter(
|
|
5144
|
+
(r) => r !== "highest" && r !== "audio-only"
|
|
5145
|
+
).length > 0, [config.static_renditions]), [renditionMode, setRenditionMode] = React.useState(
|
|
5146
|
+
isAdvancedMode ? "advanced" : "standard"
|
|
5147
|
+
), toggleRendition = (rendition) => {
|
|
5148
|
+
const current = config.static_renditions, hasRendition = current.includes(rendition);
|
|
5149
|
+
dispatch(hasRendition ? {
|
|
5150
|
+
action: "static_renditions",
|
|
5151
|
+
value: current.filter((r) => r !== rendition)
|
|
5152
|
+
} : {
|
|
5153
|
+
action: "static_renditions",
|
|
5154
|
+
value: [...current, rendition]
|
|
5155
|
+
});
|
|
5156
|
+
}, handleModeChange = (mode) => {
|
|
5157
|
+
setRenditionMode(mode), dispatch(mode === "standard" ? {
|
|
5158
|
+
action: "static_renditions",
|
|
5159
|
+
value: config.static_renditions.filter((r) => r === "highest" || r === "audio-only")
|
|
5160
|
+
} : {
|
|
5161
|
+
action: "static_renditions",
|
|
5162
|
+
value: config.static_renditions.filter((r) => r !== "highest")
|
|
5163
|
+
});
|
|
5164
|
+
};
|
|
5165
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 3, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5166
|
+
sanity.FormField,
|
|
5167
|
+
{
|
|
5168
|
+
title: "Static Renditions",
|
|
5169
|
+
description: "Generate downloadable MP4 or M4A files. Note: Mux will not upscale to produce MP4 renditions - renditions that would cause upscaling are skipped.",
|
|
5170
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
|
|
5171
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gap: 3, children: [
|
|
5172
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, children: [
|
|
5173
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5174
|
+
ui.Radio,
|
|
5175
|
+
{
|
|
5176
|
+
checked: renditionMode === "standard",
|
|
5177
|
+
name: "rendition-mode",
|
|
5178
|
+
onChange: () => handleModeChange("standard"),
|
|
5179
|
+
value: "standard",
|
|
5180
|
+
id: `${id}--mode-standard`
|
|
5181
|
+
}
|
|
5182
|
+
),
|
|
5183
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: `${id}--mode-standard`, children: "Standard" })
|
|
5184
|
+
] }),
|
|
5185
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, children: [
|
|
5186
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5187
|
+
ui.Radio,
|
|
5188
|
+
{
|
|
5189
|
+
checked: renditionMode === "advanced",
|
|
5190
|
+
name: "rendition-mode",
|
|
5191
|
+
onChange: () => handleModeChange("advanced"),
|
|
5192
|
+
value: "advanced",
|
|
5193
|
+
id: `${id}--mode-advanced`
|
|
5194
|
+
}
|
|
5195
|
+
),
|
|
5196
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: `${id}--mode-advanced`, children: "Advanced" })
|
|
5197
|
+
] })
|
|
5198
|
+
] }),
|
|
5199
|
+
renditionMode === "standard" && /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, children: [
|
|
5200
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, padding: [0, 2], children: [
|
|
5201
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5202
|
+
ui.Checkbox,
|
|
5203
|
+
{
|
|
5204
|
+
id: `${id}--highest`,
|
|
5205
|
+
style: { display: "block" },
|
|
5206
|
+
checked: config.static_renditions.includes("highest"),
|
|
5207
|
+
onChange: () => toggleRendition("highest")
|
|
5208
|
+
}
|
|
5209
|
+
),
|
|
5210
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: `${id}--highest`, children: "Highest Resolution (up to 4K)" })
|
|
5211
|
+
] }),
|
|
5212
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, padding: [0, 2], children: [
|
|
5213
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5214
|
+
ui.Checkbox,
|
|
5215
|
+
{
|
|
5216
|
+
id: `${id}--audio-only-standard`,
|
|
5217
|
+
style: { display: "block" },
|
|
5218
|
+
checked: config.static_renditions.includes("audio-only"),
|
|
5219
|
+
onChange: () => toggleRendition("audio-only")
|
|
5220
|
+
}
|
|
5221
|
+
),
|
|
5222
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: `${id}--audio-only-standard`, children: "Audio Only (M4A)" })
|
|
5223
|
+
] })
|
|
5224
|
+
] }),
|
|
5225
|
+
renditionMode === "advanced" && /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, children: [
|
|
5226
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: 1, muted: !0, children: "Select specific resolutions:" }),
|
|
5227
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { gap: 2, wrap: "wrap", children: ADVANCED_RESOLUTIONS.map(({ value, label }) => {
|
|
5228
|
+
const inputId = `${id}--resolution-${value}`;
|
|
5229
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, children: [
|
|
5230
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5231
|
+
ui.Checkbox,
|
|
5232
|
+
{
|
|
5233
|
+
id: inputId,
|
|
5234
|
+
style: { display: "block" },
|
|
5235
|
+
checked: config.static_renditions.includes(value),
|
|
5236
|
+
onChange: () => toggleRendition(value)
|
|
5237
|
+
}
|
|
5238
|
+
),
|
|
5239
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: inputId, size: 1, children: label })
|
|
5240
|
+
] }, value);
|
|
5241
|
+
}) }),
|
|
5242
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, padding: [2, 2, 0, 2], children: [
|
|
5243
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5244
|
+
ui.Checkbox,
|
|
5245
|
+
{
|
|
5246
|
+
id: `${id}--audio-only-advanced`,
|
|
5247
|
+
style: { display: "block" },
|
|
5248
|
+
checked: config.static_renditions.includes("audio-only"),
|
|
5249
|
+
onChange: () => toggleRendition("audio-only")
|
|
5250
|
+
}
|
|
5251
|
+
),
|
|
5252
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "label", htmlFor: `${id}--audio-only-advanced`, children: "Audio Only (M4A)" })
|
|
5253
|
+
] })
|
|
5254
|
+
] })
|
|
5255
|
+
] })
|
|
5256
|
+
}
|
|
5257
|
+
) });
|
|
5258
|
+
}, VIDEO_QUALITY_LEVELS = [
|
|
5259
|
+
{ value: "basic", label: "Basic" },
|
|
5260
|
+
{ value: "plus", label: "Plus" },
|
|
5261
|
+
{ value: "premium", label: "Premium" }
|
|
4698
5262
|
];
|
|
4699
5263
|
function sanitizeStaticRenditions(renditions) {
|
|
4700
5264
|
const hasHighest = renditions.includes("highest"), hasSpecificResolutions = renditions.some((r) => r !== "highest" && r !== "audio-only");
|
|
@@ -4726,7 +5290,8 @@ function UploadConfiguration({
|
|
|
4726
5290
|
max_resolution_tier: "1080p",
|
|
4727
5291
|
text_tracks: prev.text_tracks?.filter(({ type }) => type !== "autogenerated"),
|
|
4728
5292
|
public_policy: !0,
|
|
4729
|
-
signed_policy: !1
|
|
5293
|
+
signed_policy: !1,
|
|
5294
|
+
drm_policy: !1
|
|
4730
5295
|
}) : Object.assign({}, prev, {
|
|
4731
5296
|
video_quality: action.value,
|
|
4732
5297
|
static_renditions: sanitizeStaticRenditions(pluginConfig.static_renditions || []),
|
|
@@ -4740,6 +5305,8 @@ function UploadConfiguration({
|
|
|
4740
5305
|
return Object.assign({}, prev, { [action.action]: action.value });
|
|
4741
5306
|
case "public_policy":
|
|
4742
5307
|
return Object.assign({}, prev, { [action.action]: action.value });
|
|
5308
|
+
case "drm_policy":
|
|
5309
|
+
return Object.assign({}, prev, { [action.action]: action.value });
|
|
4743
5310
|
// Updating individual tracks
|
|
4744
5311
|
case "track": {
|
|
4745
5312
|
const text_tracks = [...prev.text_tracks], target_track_i = text_tracks.findIndex(({ _id: _id2 }) => _id2 === action.id);
|
|
@@ -4775,75 +5342,41 @@ function UploadConfiguration({
|
|
|
4775
5342
|
static_renditions: sanitizeStaticRenditions(pluginConfig.static_renditions || []),
|
|
4776
5343
|
signed_policy: secrets.enableSignedUrls && pluginConfig.defaultSigned,
|
|
4777
5344
|
public_policy: pluginConfig.defaultPublic,
|
|
5345
|
+
drm_policy: pluginConfig.defaultDrm && !!secrets.drmConfigId,
|
|
4778
5346
|
normalize_audio: pluginConfig.normalize_audio,
|
|
4779
5347
|
text_tracks: autoTextTracks
|
|
4780
5348
|
}
|
|
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;
|
|
5349
|
+
), [validationError, setValidationError] = React.useState(null), MAX_FILE_SIZE = pluginConfig.maxAssetFileSize, MAX_DURATION_SECONDS = pluginConfig.maxAssetDuration, { fileSize, isLoadingFileSize, canSkipFileSizeValidation } = useFetchFileSize(
|
|
5350
|
+
stagedUpload,
|
|
5351
|
+
MAX_FILE_SIZE
|
|
5352
|
+
), { videoAssetMetadata, setVideoAssetMetadata, isLoadingMetadata } = useMediaMetadata(stagedUpload);
|
|
4786
5353
|
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(
|
|
5354
|
+
fileSize && setVideoAssetMetadata((old) => ({ ...old, size: fileSize }));
|
|
5355
|
+
}, [fileSize, setVideoAssetMetadata]), React.useEffect(() => {
|
|
5356
|
+
const validateDuration = (duration) => MAX_DURATION_SECONDS && duration > MAX_DURATION_SECONDS ? (setValidationError(
|
|
5357
|
+
`Video duration (${formatSeconds(duration)}) exceeds maximum allowed duration of ${formatSeconds(MAX_DURATION_SECONDS)}`
|
|
5358
|
+
), !1) : !0, validateFileSize = (size) => MAX_FILE_SIZE === void 0 || size <= MAX_FILE_SIZE ? !0 : (setValidationError(
|
|
4801
5359
|
`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;
|
|
5360
|
+
), !1), validateDrmAvailability = (isAudioOnly) => config.drm_policy && isAudioOnly ? (setValidationError("Audio-only asset cannot be DRM protected"), !1) : !0;
|
|
5361
|
+
let valid = !0;
|
|
5362
|
+
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);
|
|
5363
|
+
}, [
|
|
5364
|
+
MAX_FILE_SIZE,
|
|
5365
|
+
MAX_DURATION_SECONDS,
|
|
5366
|
+
canSkipFileSizeValidation,
|
|
5367
|
+
videoAssetMetadata?.duration,
|
|
5368
|
+
videoAssetMetadata?.size,
|
|
5369
|
+
videoAssetMetadata?.height,
|
|
5370
|
+
videoAssetMetadata?.width,
|
|
5371
|
+
videoAssetMetadata,
|
|
5372
|
+
config.drm_policy,
|
|
5373
|
+
validationError
|
|
5374
|
+
]);
|
|
5375
|
+
const { disableTextTrackConfig, disableUploadConfig } = pluginConfig, skipConfig = disableTextTrackConfig && disableUploadConfig;
|
|
4843
5376
|
if (React.useEffect(() => {
|
|
4844
|
-
skipConfig && startUpload(formatUploadConfig(config));
|
|
5377
|
+
skipConfig && startUpload(formatUploadConfig(config, secrets));
|
|
4845
5378
|
}, []), skipConfig) return null;
|
|
4846
|
-
const basicConfig = config.video_quality !== "plus" && config.video_quality !== "premium", maxSupportedResolution = RESOLUTION_TIERS.findIndex(
|
|
5379
|
+
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
5380
|
(rt) => rt.value === pluginConfig.max_resolution_tier
|
|
4848
5381
|
);
|
|
4849
5382
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -4877,12 +5410,12 @@ function UploadConfiguration({
|
|
|
4877
5410
|
/* @__PURE__ */ jsxRuntime.jsx(icons.DocumentVideoIcon, { fontSize: "2em" }),
|
|
4878
5411
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, children: [
|
|
4879
5412
|
/* @__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)})` :
|
|
5413
|
+
/* @__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
5414
|
stagedUpload.type === "file" && /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 1, children: [
|
|
4882
|
-
|
|
4883
|
-
|
|
5415
|
+
isLoadingMetadata && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "p", size: 1, muted: !0, children: "Reading video metadata..." }),
|
|
5416
|
+
videoAssetMetadata?.duration && !validationError && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { as: "p", size: 1, muted: !0, children: [
|
|
4884
5417
|
"Duration: ",
|
|
4885
|
-
formatSeconds(
|
|
5418
|
+
formatSeconds(videoAssetMetadata.duration)
|
|
4886
5419
|
] })
|
|
4887
5420
|
] })
|
|
4888
5421
|
] })
|
|
@@ -4928,160 +5461,37 @@ function UploadConfiguration({
|
|
|
4928
5461
|
}) })
|
|
4929
5462
|
}
|
|
4930
5463
|
),
|
|
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
5464
|
!basicConfig && /* @__PURE__ */ jsxRuntime.jsx(sanity.FormField, { title: "Additional Configuration", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
|
|
4972
5465
|
/* @__PURE__ */ jsxRuntime.jsx(PlaybackPolicy, { id, config, secrets, dispatch }),
|
|
4973
|
-
|
|
4974
|
-
|
|
5466
|
+
maxSupportedResolution > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5467
|
+
ResolutionTierSelector,
|
|
4975
5468
|
{
|
|
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
|
-
] })
|
|
5469
|
+
id,
|
|
5470
|
+
config,
|
|
5471
|
+
dispatch,
|
|
5472
|
+
maxSupportedResolution
|
|
5064
5473
|
}
|
|
5065
|
-
)
|
|
5474
|
+
),
|
|
5475
|
+
/* @__PURE__ */ jsxRuntime.jsx(StaticRenditionSelector, { id, config, dispatch }),
|
|
5476
|
+
!disableTextTrackConfig && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5477
|
+
TextTracksEditor,
|
|
5478
|
+
{
|
|
5479
|
+
tracks: config.text_tracks,
|
|
5480
|
+
dispatch,
|
|
5481
|
+
defaultLang: pluginConfig.defaultAutogeneratedSubtitleLang
|
|
5482
|
+
}
|
|
5483
|
+
)
|
|
5066
5484
|
] }) })
|
|
5067
5485
|
] }),
|
|
5068
|
-
!disableTextTrackConfig && !basicConfig && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5069
|
-
TextTracksEditor,
|
|
5070
|
-
{
|
|
5071
|
-
tracks: config.text_tracks,
|
|
5072
|
-
dispatch,
|
|
5073
|
-
defaultLang: pluginConfig.defaultAutogeneratedSubtitleLang
|
|
5074
|
-
}
|
|
5075
|
-
),
|
|
5076
5486
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Box, { marginTop: 4, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5077
5487
|
ui.Button,
|
|
5078
5488
|
{
|
|
5079
|
-
disabled: !basicConfig && !
|
|
5489
|
+
disabled: !basicConfig && !playbackPolicySelected || validationError !== null || isLoadingMetadata || isLoadingFileSize && !canSkipFileSizeValidation,
|
|
5080
5490
|
icon: icons.UploadIcon,
|
|
5081
5491
|
text: "Upload",
|
|
5082
5492
|
tone: "positive",
|
|
5083
5493
|
onClick: () => {
|
|
5084
|
-
validationError || startUpload(formatUploadConfig(config));
|
|
5494
|
+
validationError || startUpload(formatUploadConfig(config, secrets));
|
|
5085
5495
|
}
|
|
5086
5496
|
}
|
|
5087
5497
|
) })
|
|
@@ -5089,11 +5499,14 @@ function UploadConfiguration({
|
|
|
5089
5499
|
}
|
|
5090
5500
|
);
|
|
5091
5501
|
}
|
|
5092
|
-
function
|
|
5093
|
-
const
|
|
5094
|
-
return config.public_policy &&
|
|
5502
|
+
function setAdvancedPlaybackPolicy(config, secrets) {
|
|
5503
|
+
const advanced_playback_policies = [];
|
|
5504
|
+
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({
|
|
5505
|
+
policy: "drm",
|
|
5506
|
+
drm_configuration_id: secrets.drmConfigId ?? void 0
|
|
5507
|
+
}) : console.error("Selected DRM Policy but missing DRM Configuration Id")), advanced_playback_policies;
|
|
5095
5508
|
}
|
|
5096
|
-
function formatUploadConfig(config) {
|
|
5509
|
+
function formatUploadConfig(config, secrets) {
|
|
5097
5510
|
const generated_subtitles = config.text_tracks.filter(isAutogeneratedTrack).map((track) => ({
|
|
5098
5511
|
name: track.name,
|
|
5099
5512
|
language_code: track.language_code
|
|
@@ -5117,7 +5530,7 @@ function formatUploadConfig(config) {
|
|
|
5117
5530
|
)
|
|
5118
5531
|
],
|
|
5119
5532
|
static_renditions: config.static_renditions.length > 0 ? config.static_renditions.map((resolution) => ({ resolution })) : void 0,
|
|
5120
|
-
|
|
5533
|
+
advanced_playback_policies: setAdvancedPlaybackPolicy(config, secrets),
|
|
5121
5534
|
max_resolution_tier: config.max_resolution_tier,
|
|
5122
5535
|
video_quality: config.video_quality,
|
|
5123
5536
|
normalize_audio: config.normalize_audio
|
|
@@ -5329,8 +5742,13 @@ function Uploader(props) {
|
|
|
5329
5742
|
case "reset":
|
|
5330
5743
|
case "complete":
|
|
5331
5744
|
return uploadRef.current?.unsubscribe(), uploadRef.current = null, uploadingDocumentId.current = null, INITIAL_STATE;
|
|
5332
|
-
case "error":
|
|
5333
|
-
|
|
5745
|
+
case "error": {
|
|
5746
|
+
uploadRef.current?.unsubscribe(), uploadRef.current = null, uploadingDocumentId.current = null;
|
|
5747
|
+
let error = action.error;
|
|
5748
|
+
return isServerError(action.error) && hasPlaybackPolicy(action.settings, "drm") && (error = new Error(
|
|
5749
|
+
"Unknown Error while uploading DRM protected content. Make sure your DRM configuration ID is valid and set correctly"
|
|
5750
|
+
)), Object.assign({}, INITIAL_STATE, { error });
|
|
5751
|
+
}
|
|
5334
5752
|
default:
|
|
5335
5753
|
return prev;
|
|
5336
5754
|
}
|
|
@@ -5409,7 +5827,7 @@ function Uploader(props) {
|
|
|
5409
5827
|
}
|
|
5410
5828
|
},
|
|
5411
5829
|
complete: () => dispatch({ action: "complete" }),
|
|
5412
|
-
error: (error) => dispatch({ action: "error", error })
|
|
5830
|
+
error: (error) => dispatch({ action: "error", error, settings })
|
|
5413
5831
|
});
|
|
5414
5832
|
}, invalidFileToast = React.useCallback(() => {
|
|
5415
5833
|
toast.push({
|
|
@@ -5460,11 +5878,11 @@ function Uploader(props) {
|
|
|
5460
5878
|
idx > -1 && dragEnteredEls.current.splice(idx, 1), dragEnteredEls.current.length === 0 && setDragState(null);
|
|
5461
5879
|
};
|
|
5462
5880
|
if (state.error !== null) {
|
|
5463
|
-
const error =
|
|
5881
|
+
const error = state.error;
|
|
5464
5882
|
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gap: 3, direction: "column", justify: "center", align: "center", children: [
|
|
5465
5883
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 5, muted: !0, children: /* @__PURE__ */ jsxRuntime.jsx(icons.ErrorOutlineIcon, {}) }),
|
|
5466
5884
|
/* @__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 }),
|
|
5885
|
+
error instanceof Error && error.message && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, muted: !0, weight: "semibold", style: { textAlign: "center" }, children: error.message }),
|
|
5468
5886
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { text: "Upload another file", onClick: () => dispatch({ action: "reset" }) })
|
|
5469
5887
|
] });
|
|
5470
5888
|
}
|
|
@@ -5549,6 +5967,7 @@ function Uploader(props) {
|
|
|
5549
5967
|
props.dialogState === "select-video" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5550
5968
|
InputBrowser,
|
|
5551
5969
|
{
|
|
5970
|
+
config: props.config,
|
|
5552
5971
|
asset: props.asset,
|
|
5553
5972
|
onChange: props.onChange,
|
|
5554
5973
|
setDialogState: props.setDialogState
|
|
@@ -5795,6 +6214,7 @@ const muxVideoSchema = {
|
|
|
5795
6214
|
normalize_audio: !1,
|
|
5796
6215
|
defaultPublic: !0,
|
|
5797
6216
|
defaultSigned: !1,
|
|
6217
|
+
defaultDrm: !1,
|
|
5798
6218
|
tool: DEFAULT_TOOL_CONFIG,
|
|
5799
6219
|
allowedRolesForConfiguration: [],
|
|
5800
6220
|
acceptedMimeTypes: ["video/*", "audio/*"]
|