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.
Files changed (48) hide show
  1. package/README.md +25 -24
  2. package/dist/index.d.mts +13 -1
  3. package/dist/index.d.ts +13 -1
  4. package/dist/index.js +771 -351
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +773 -353
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +1 -1
  9. package/src/_exports/index.ts +1 -0
  10. package/src/actions/secrets.ts +6 -1
  11. package/src/actions/upload.ts +1 -1
  12. package/src/components/ConfigureApi.tsx +51 -5
  13. package/src/components/EditCaptionDialog.tsx +2 -2
  14. package/src/components/InputBrowser.tsx +8 -2
  15. package/src/components/PageSelector.tsx +4 -7
  16. package/src/components/Player.styled.tsx +7 -2
  17. package/src/components/PlayerActionsMenu.tsx +1 -1
  18. package/src/components/SelectAsset.tsx +9 -3
  19. package/src/components/StudioTool.tsx +2 -2
  20. package/src/components/UploadConfiguration.tsx +104 -343
  21. package/src/components/Uploader.tsx +18 -7
  22. package/src/components/VideoDetails/VideoDetails.tsx +28 -8
  23. package/src/components/VideoInBrowser.tsx +53 -6
  24. package/src/components/VideoPlayer.tsx +120 -47
  25. package/src/components/VideoThumbnail.tsx +84 -72
  26. package/src/components/VideosBrowser.tsx +7 -5
  27. package/src/components/uploadConfiguration/PlaybackPolicy.tsx +95 -6
  28. package/src/components/uploadConfiguration/PlaybackPolicyOption.tsx +26 -10
  29. package/src/components/uploadConfiguration/ResolutionTierSelector.tsx +71 -0
  30. package/src/components/uploadConfiguration/StaticRenditionSelector.tsx +179 -0
  31. package/src/context/DrmPlaybackWarningContext.tsx +93 -0
  32. package/src/hooks/useFetchFileSize.ts +54 -0
  33. package/src/hooks/useMediaMetadata.ts +100 -0
  34. package/src/hooks/useSaveSecrets.ts +10 -3
  35. package/src/hooks/useSecretsDocumentValues.ts +9 -1
  36. package/src/hooks/useSecretsFormState.ts +6 -3
  37. package/src/util/asserters.ts +14 -0
  38. package/src/util/createUrlParamsObject.ts +7 -3
  39. package/src/util/generateJwt.ts +11 -2
  40. package/src/util/getPlaybackPolicy.ts +63 -4
  41. package/src/util/getStoryboardSrc.ts +7 -3
  42. package/src/util/getVideoMetadata.ts +1 -0
  43. package/src/util/getVideoSrc.ts +9 -9
  44. package/src/util/readSecrets.ts +3 -1
  45. package/src/util/textTracks.ts +6 -3
  46. package/src/util/tryWithSuspend.ts +22 -0
  47. package/src/util/types.ts +27 -2
  48. package/src/util/getPlaybackId.ts +0 -9
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { useClient as useClient$1, createHookFromObservableFactory, useDocumentStore, collate, useDocumentValues, truncateString, useFormattedDuration, SanityDefaultPreview, useTimeAgo, TextWithTone, isRecord, getPreviewStateObservable, getPreviewValueWithFallback, DocumentPreviewPresence, useDocumentPreviewStore, useSchema, useDocumentPresence, PreviewCard, useCurrentUser, isReference, useProjectId, useDataset, PatchEvent, unset, setIfMissing, set, LinearProgress, FormField as FormField$2, definePlugin } from "sanity";
2
2
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
3
3
  import { ErrorOutlineIcon, InfoOutlineIcon, RetryIcon, CheckmarkCircleIcon, RetrieveIcon, ChevronLeftIcon, ChevronRightIcon, SyncIcon, SortIcon, UploadIcon, TranslateIcon, DownloadIcon, AddIcon, ChevronUpIcon, ChevronDownIcon, TrashIcon, EditIcon, WarningOutlineIcon, PublishIcon, DocumentIcon, RevertIcon, SearchIcon, ClockIcon, CropIcon, CalendarIcon, TagIcon, CheckmarkIcon, LockIcon, PlayIcon, PlugIcon, EllipsisHorizontalIcon, ImageIcon, ResetIcon, WarningFilledIcon, DocumentVideoIcon } from "@sanity/icons";
4
- import { useTheme_v2, Stack, Flex, Box, Text, Button, Dialog, Card, TextInput, Checkbox, Code, Inline, Spinner, Heading, Label as Label$1, MenuButton, Menu, MenuItem, useToast, Autocomplete, Tooltip, TabList, Tab, TabPanel, Grid, useClickOutsideEvent, Popover, MenuDivider, Radio, rem } from "@sanity/ui";
5
- import React, { useState, useMemo, useCallback, useReducer, useId, memo, useRef, useEffect, createContext, useContext, isValidElement, PureComponent, createElement, forwardRef, Suspense } from "react";
4
+ import { Dialog, Stack, Card, Text, Button, useTheme_v2, Flex, Box, TextInput, Checkbox, Code, Inline, Spinner, Heading, Label as Label$1, MenuButton, Menu, MenuItem, useToast, Autocomplete, Tooltip, TabList, Tab, TabPanel, Grid, useClickOutsideEvent, Popover, MenuDivider, Radio, rem } from "@sanity/ui";
5
+ import React, { createContext, useContext, useState, useMemo, useCallback, useReducer, useId, memo, useRef, useEffect, Suspense, isValidElement, PureComponent, createElement, forwardRef } from "react";
6
6
  import compact from "lodash/compact.js";
7
7
  import toLower from "lodash/toLower.js";
8
8
  import trim from "lodash/trim.js";
@@ -35,7 +35,45 @@ const ToolIcon = () => /* @__PURE__ */ jsx(
35
35
  xmlns: "http://www.w3.org/2000/svg",
36
36
  children: /* @__PURE__ */ 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" })
37
37
  }
38
- ), SANITY_API_VERSION = "2024-03-05";
38
+ ), LOCAL_STORAGE_HAS_SHOWN_WARNING_KEY = "mux-plugin-has-shown-drm-playback-warning", DrmPlaybackWarningContext = createContext({
39
+ hasShownWarning: !1,
40
+ setHasWarnedAboutDrmPlayback: () => null
41
+ }), DrmPlaybackWarningContextProvider = ({
42
+ config,
43
+ children
44
+ }) => {
45
+ const hasWarned = (config?.disableDrmPlaybackWarning ?? !1) || window.localStorage.getItem(LOCAL_STORAGE_HAS_SHOWN_WARNING_KEY) === "true", [hasWarnedAboutDrmPlayback, setHasWarnedAboutDrmPlayback] = useState(hasWarned), setHasShownWarning = (b) => {
46
+ window.localStorage.setItem(LOCAL_STORAGE_HAS_SHOWN_WARNING_KEY, b.toString()), setHasWarnedAboutDrmPlayback(b);
47
+ };
48
+ return /* @__PURE__ */ jsx(
49
+ DrmPlaybackWarningContext.Provider,
50
+ {
51
+ value: {
52
+ hasShownWarning: hasWarnedAboutDrmPlayback,
53
+ setHasWarnedAboutDrmPlayback: setHasShownWarning
54
+ },
55
+ children
56
+ }
57
+ );
58
+ }, useDrmPlaybackWarningContext = () => useContext(DrmPlaybackWarningContext), DRMWarningDialog = ({ onClose }) => {
59
+ const { setHasWarnedAboutDrmPlayback } = useDrmPlaybackWarningContext(), _onClose = () => {
60
+ setHasWarnedAboutDrmPlayback(!0), onClose();
61
+ };
62
+ return /* @__PURE__ */ jsx(
63
+ Dialog,
64
+ {
65
+ open: !0,
66
+ id: "drm-playback-warn",
67
+ onClose: _onClose,
68
+ header: "DRM Playback Warning",
69
+ footer: /* @__PURE__ */ jsx(Stack, { padding: 3, children: /* @__PURE__ */ jsx(Button, { mode: "ghost", tone: "primary", onClick: _onClose, text: "Ok" }) }),
70
+ children: /* @__PURE__ */ jsxs(Stack, { space: 3, padding: 3, children: [
71
+ /* @__PURE__ */ jsx(Card, { padding: [3, 3, 3], radius: 2, children: /* @__PURE__ */ jsx(Stack, { space: 3, children: /* @__PURE__ */ jsx(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." }) }) }),
72
+ /* @__PURE__ */ jsx(Card, { padding: [3, 3, 3], radius: 2, tone: "suggest", children: /* @__PURE__ */ jsx(Stack, { space: 3, children: /* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: "This is a one time warning. If it persists, you can disable it from your plugin configuration." }) }) })
73
+ ] })
74
+ }
75
+ );
76
+ }, SANITY_API_VERSION = "2024-03-05";
39
77
  function useClient() {
40
78
  return useClient$1({ apiVersion: SANITY_API_VERSION });
41
79
  }
@@ -108,7 +146,7 @@ function useAssets() {
108
146
  function useDialogState() {
109
147
  return useState(!1);
110
148
  }
111
- function saveSecrets(client, token, secretKey, enableSignedUrls, signingKeyId, signingKeyPrivate) {
149
+ function saveSecrets(client, token, secretKey, enableSignedUrls, signingKeyId, signingKeyPrivate, drmConfigId) {
112
150
  const doc = {
113
151
  _id: "secrets.mux",
114
152
  _type: "mux.apiKey",
@@ -116,9 +154,10 @@ function saveSecrets(client, token, secretKey, enableSignedUrls, signingKeyId, s
116
154
  secretKey,
117
155
  enableSignedUrls,
118
156
  signingKeyId,
119
- signingKeyPrivate
157
+ signingKeyPrivate,
158
+ drmConfigId
120
159
  };
121
- return client.createOrReplace(doc);
160
+ return doc.signingKeyId = enableSignedUrls ? signingKeyId : "", doc.signingKeyPrivate = enableSignedUrls ? signingKeyPrivate : "", client.createOrReplace(doc);
122
161
  }
123
162
  async function createSigningKeys(client) {
124
163
  try {
@@ -171,7 +210,8 @@ const useSaveSecrets = (client, secrets) => useCallback(
171
210
  async ({
172
211
  token,
173
212
  secretKey,
174
- enableSignedUrls
213
+ enableSignedUrls,
214
+ drmConfigId
175
215
  }) => {
176
216
  let { signingKeyId, signingKeyPrivate } = secrets;
177
217
  try {
@@ -181,7 +221,8 @@ const useSaveSecrets = (client, secrets) => useCallback(
181
221
  secretKey,
182
222
  enableSignedUrls,
183
223
  signingKeyId,
184
- signingKeyPrivate
224
+ signingKeyPrivate,
225
+ drmConfigId
185
226
  ), !(await testSecrets(client))?.status && token && secretKey)
186
227
  throw new Error("Invalid secrets");
187
228
  } catch (err) {
@@ -200,7 +241,8 @@ const useSaveSecrets = (client, secrets) => useCallback(
200
241
  secretKey,
201
242
  enableSignedUrls,
202
243
  signingKeyId,
203
- signingKeyPrivate
244
+ signingKeyPrivate,
245
+ drmConfigId ?? ""
204
246
  );
205
247
  } catch (err) {
206
248
  throw console.log("Error while creating and saving signing key:", err?.message), err;
@@ -210,11 +252,19 @@ const useSaveSecrets = (client, secrets) => useCallback(
210
252
  secretKey,
211
253
  enableSignedUrls,
212
254
  signingKeyId,
213
- signingKeyPrivate
255
+ signingKeyPrivate,
256
+ drmConfigId
214
257
  };
215
258
  },
216
259
  [client, secrets]
217
- ), 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 = ["token", "secretKey", "enableSignedUrls", "signingKeyId", "signingKeyPrivate"], useSecretsDocumentValues = () => {
260
+ ), 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
+ "token",
262
+ "secretKey",
263
+ "enableSignedUrls",
264
+ "signingKeyId",
265
+ "signingKeyPrivate",
266
+ "drmConfigId"
267
+ ], useSecretsDocumentValues = () => {
218
268
  const { error, isLoading, value } = useDocumentValues(
219
269
  muxSecretsDocumentId,
220
270
  path$1
@@ -224,7 +274,8 @@ const useSaveSecrets = (client, secrets) => useCallback(
224
274
  secretKey: value?.secretKey || null,
225
275
  enableSignedUrls: value?.enableSignedUrls || !1,
226
276
  signingKeyId: value?.signingKeyId || null,
227
- signingKeyPrivate: value?.signingKeyPrivate || null
277
+ signingKeyPrivate: value?.signingKeyPrivate || null,
278
+ drmConfigId: value?.drmConfigId || null
228
279
  };
229
280
  return {
230
281
  isInitialSetup: !exists,
@@ -234,7 +285,7 @@ const useSaveSecrets = (client, secrets) => useCallback(
234
285
  }, [value]);
235
286
  return { error, isLoading, value: cache };
236
287
  };
237
- function init({ token, secretKey, enableSignedUrls }) {
288
+ function init({ token, secretKey, enableSignedUrls, drmConfigId }) {
238
289
  return {
239
290
  submitting: !1,
240
291
  error: null,
@@ -242,7 +293,8 @@ function init({ token, secretKey, enableSignedUrls }) {
242
293
  // This ensures the `dirty` check works correctly
243
294
  token: token ?? "",
244
295
  secretKey: secretKey ?? "",
245
- enableSignedUrls: enableSignedUrls ?? !1
296
+ enableSignedUrls: enableSignedUrls ?? !1,
297
+ drmConfigId: drmConfigId ?? ""
246
298
  };
247
299
  }
248
300
  function reducer(state, action) {
@@ -270,7 +322,8 @@ function readSecrets(client) {
270
322
  secretKey,
271
323
  enableSignedUrls,
272
324
  signingKeyId,
273
- signingKeyPrivate
325
+ signingKeyPrivate,
326
+ drmConfigId
274
327
  }`,
275
328
  { _id }
276
329
  );
@@ -279,7 +332,8 @@ function readSecrets(client) {
279
332
  secretKey: data?.secretKey || null,
280
333
  enableSignedUrls: !!data?.enableSignedUrls || !1,
281
334
  signingKeyId: data?.signingKeyId || null,
282
- signingKeyPrivate: data?.signingKeyPrivate || null
335
+ signingKeyPrivate: data?.signingKeyPrivate || null,
336
+ drmConfigId: data?.drmConfigId || null
283
337
  };
284
338
  }, [cacheNs, _id, projectId, dataset]);
285
339
  }
@@ -342,20 +396,20 @@ function FormField(props) {
342
396
  ] });
343
397
  }
344
398
  var FormField$1 = memo(FormField);
345
- const fieldNames = ["token", "secretKey", "enableSignedUrls"];
399
+ const fieldNames = ["token", "secretKey", "enableSignedUrls", "drmConfigId"];
346
400
  function ConfigureApiDialog({ secrets, setDialogState }) {
347
401
  const client = useClient(), [state, dispatch] = useSecretsFormState(secrets), hasSecretsInitially = useMemo(() => secrets.token && secrets.secretKey, [secrets]), handleClose = useCallback(() => setDialogState(!1), [setDialogState]), dirty = useMemo(
348
- () => secrets.token !== state.token || secrets.secretKey !== state.secretKey || secrets.enableSignedUrls !== state.enableSignedUrls,
402
+ () => secrets.token !== state.token || secrets.secretKey !== state.secretKey || secrets.enableSignedUrls !== state.enableSignedUrls || secrets.drmConfigId !== state.drmConfigId,
349
403
  [secrets, state]
350
- ), id = `ConfigureApi${useId()}`, [tokenId, secretKeyId, enableSignedUrlsId] = useMemo(
404
+ ), id = `ConfigureApi${useId()}`, [tokenId, secretKeyId, enableSignedUrlsId, drmConfigIdId] = useMemo(
351
405
  () => fieldNames.map((field) => `${id}-${field}`),
352
406
  [id]
353
407
  ), firstField = useRef(null), handleSaveSecrets = useSaveSecrets(client, secrets), saving = useRef(!1), handleSubmit = useCallback(
354
408
  (event) => {
355
409
  if (event.preventDefault(), !saving.current && event.currentTarget.reportValidity()) {
356
410
  saving.current = !0, dispatch({ type: "submit" });
357
- const { token, secretKey, enableSignedUrls } = state;
358
- handleSaveSecrets({ token, secretKey, enableSignedUrls }).then((savedSecrets) => {
411
+ const { token, secretKey, enableSignedUrls, drmConfigId } = state;
412
+ handleSaveSecrets({ token, secretKey, enableSignedUrls, drmConfigId }).then((savedSecrets) => {
359
413
  const { projectId, dataset } = client.config();
360
414
  clear([cacheNs, _id, projectId, dataset]), preload(() => Promise.resolve(savedSecrets), [cacheNs, _id, projectId, dataset]), setDialogState(!1);
361
415
  }).catch((err) => dispatch({ type: "error", payload: err.message })).finally(() => {
@@ -388,6 +442,14 @@ function ConfigureApiDialog({ secrets, setDialogState }) {
388
442
  });
389
443
  },
390
444
  [dispatch]
445
+ ), handleChangeDrmConfigId = useCallback(
446
+ (event) => {
447
+ dispatch({
448
+ type: "change",
449
+ payload: { name: "drmConfigId", value: event.currentTarget.value }
450
+ });
451
+ },
452
+ [dispatch]
391
453
  );
392
454
  return useEffect(() => {
393
455
  firstField.current && firstField.current.focus();
@@ -474,6 +536,45 @@ function ConfigureApiDialog({ secrets, setDialogState }) {
474
536
  ] })
475
537
  ] }) }) : null
476
538
  ] }),
539
+ /* @__PURE__ */ jsx(FormField$1, { title: "DRM Configuration ID", inputId: drmConfigIdId, children: /* @__PURE__ */ jsx(
540
+ TextInput,
541
+ {
542
+ id: drmConfigIdId,
543
+ onChange: handleChangeDrmConfigId,
544
+ type: "text",
545
+ value: state.drmConfigId ?? "",
546
+ required: !1
547
+ }
548
+ ) }),
549
+ /* @__PURE__ */ jsx(Card, { padding: [3, 3, 3], radius: 2, shadow: 1, tone: "neutral", children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
550
+ /* @__PURE__ */ jsxs(Text, { size: 1, children: [
551
+ "DRM (Digital Rights Management) provides an extra layer of content security for video content streamed from Mux. For additional information check out our",
552
+ " ",
553
+ /* @__PURE__ */ jsx(
554
+ "a",
555
+ {
556
+ href: "https://www.mux.com/docs/guides/protect-videos-with-drm#play-drm-protected-videos",
557
+ target: "_blank",
558
+ rel: "noopener noreferrer",
559
+ children: "DRM Guide"
560
+ }
561
+ ),
562
+ "."
563
+ ] }),
564
+ /* @__PURE__ */ jsxs(Text, { size: 1, children: [
565
+ /* @__PURE__ */ jsx(
566
+ "a",
567
+ {
568
+ href: "https://www.mux.com/support/human",
569
+ target: "_blank",
570
+ rel: "noopener noreferrer",
571
+ children: "Contact us"
572
+ }
573
+ ),
574
+ " ",
575
+ "to get started using DRM."
576
+ ] })
577
+ ] }) }),
477
578
  /* @__PURE__ */ jsxs(Inline, { space: 2, children: [
478
579
  /* @__PURE__ */ jsx(
479
580
  Button,
@@ -777,6 +878,35 @@ function useInView(ref, options = {}) {
777
878
  };
778
879
  }, [options, ref]), inView;
779
880
  }
881
+ function getPlaybackId(asset, priority = ["drm", "signed", "public"]) {
882
+ try {
883
+ if (!asset)
884
+ throw new TypeError("Tried to get playback Id with no asset");
885
+ const playbackIds = asset.data?.playback_ids;
886
+ if (playbackIds && playbackIds.length > 0) {
887
+ for (const policy of priority) {
888
+ const match = playbackIds.find((entry) => entry.policy === policy);
889
+ if (match)
890
+ return match.id;
891
+ }
892
+ return playbackIds[0].id;
893
+ }
894
+ throw new TypeError("Missing playbackId");
895
+ } catch (e) {
896
+ throw console.error("Asset is missing a playbackId", { asset }, e), e;
897
+ }
898
+ }
899
+ function getPlaybackPolicy(asset) {
900
+ return asset.data?.playback_ids?.find(
901
+ (playbackId) => getPlaybackId(asset, ["drm", "signed", "public"]) === playbackId.id
902
+ ) ?? { id: "", policy: "public" };
903
+ }
904
+ function getPlaybackPolicyById(asset, playbackId) {
905
+ return asset.data?.playback_ids?.find((entry) => playbackId === entry.id);
906
+ }
907
+ function hasPlaybackPolicy(data, policy) {
908
+ return data.advanced_playback_policies && data.advanced_playback_policies.find((p) => p.policy === policy) || data.playback_policy?.find((p) => p === policy);
909
+ }
780
910
  function generateJwt(client, playbackId, aud, payload) {
781
911
  const { signingKeyId, signingKeyPrivate } = readSecrets(client);
782
912
  if (!signingKeyId)
@@ -797,20 +927,13 @@ function generateJwt(client, playbackId, aud, payload) {
797
927
  }
798
928
  );
799
929
  }
800
- function getPlaybackId(asset) {
801
- if (!asset?.playbackId)
802
- throw console.error("Asset is missing a playbackId", { asset }), new TypeError("Missing playbackId");
803
- return asset.playbackId;
804
- }
805
- function getPlaybackPolicy(asset) {
806
- return asset.data?.playback_ids?.find((playbackId) => asset.playbackId === playbackId.id)?.policy ?? "public";
807
- }
808
930
  function createUrlParamsObject(client, asset, params, audience) {
809
931
  const playbackId = getPlaybackId(asset);
810
932
  let searchParams = new URLSearchParams(
811
933
  JSON.parse(JSON.stringify(params, (_, v) => v ?? void 0))
812
934
  );
813
- if (getPlaybackPolicy(asset) === "signed") {
935
+ const playbackPolicy = getPlaybackPolicyById(asset, playbackId)?.policy;
936
+ if (playbackPolicy === "signed" || playbackPolicy === "drm") {
814
937
  const token = generateJwt(client, playbackId, audience, params);
815
938
  searchParams = new URLSearchParams({ token });
816
939
  }
@@ -841,6 +964,15 @@ function getPosterSrc({
841
964
  const { playbackId, searchParams } = createUrlParamsObject(client, asset, params, "t");
842
965
  return `https://image.mux.com/${playbackId}/thumbnail.png?${searchParams}`;
843
966
  }
967
+ function tryWithSuspend(block, onError) {
968
+ try {
969
+ return block();
970
+ } catch (errorOrPromise) {
971
+ if (errorOrPromise instanceof Promise)
972
+ throw errorOrPromise;
973
+ return onError ? onError(errorOrPromise) : void 0;
974
+ }
975
+ }
844
976
  const Image = styled.img`
845
977
  transition: opacity 0.175s ease-out 0s;
846
978
  display: block;
@@ -858,22 +990,22 @@ function VideoThumbnail({
858
990
  width,
859
991
  staticImage = !1
860
992
  }) {
861
- const ref = useRef(null), inView = useInView(ref), posterWidth = width || 250, [status, setStatus] = useState("loading"), client = useClient(), src = useMemo(() => {
862
- try {
993
+ const posterWidth = width || 250, client = useClient(), ref = useRef(null), inView = useInView(ref), [status, setStatus] = useState("loading"), [error, setError] = useState(null), thumbnailSrc = useMemo(() => tryWithSuspend(
994
+ () => {
863
995
  let thumbnail;
864
996
  return staticImage ? thumbnail = getPosterSrc({ asset, client, width: posterWidth }) : thumbnail = getAnimatedPosterSrc({ asset, client, width: posterWidth }), thumbnail;
865
- } catch {
866
- status !== "error" && setStatus("error");
867
- return;
997
+ },
998
+ (err) => {
999
+ handleError(err.message);
868
1000
  }
869
- }, [asset, client, posterWidth, status, staticImage]);
1001
+ ), [asset, client, posterWidth, staticImage]);
870
1002
  function handleLoad() {
871
1003
  setStatus("loaded");
872
1004
  }
873
- function handleError() {
874
- setStatus("error");
1005
+ function handleError(err) {
1006
+ setStatus("error"), setError(err || "Failed loading thumbnail");
875
1007
  }
876
- return /* @__PURE__ */ jsx(
1008
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("span", { children: "Preparing thumbnail" }), children: /* @__PURE__ */ jsx(
877
1009
  Card,
878
1010
  {
879
1011
  style: {
@@ -914,23 +1046,23 @@ function VideoThumbnail({
914
1046
  },
915
1047
  children: [
916
1048
  /* @__PURE__ */ jsx(Text, { size: 4, muted: !0, children: /* @__PURE__ */ jsx(ErrorOutlineIcon, { style: { fontSize: "1.75em" } }) }),
917
- /* @__PURE__ */ jsx(Text, { muted: !0, align: "center", children: "Failed loading thumbnail" })
1049
+ /* @__PURE__ */ jsx(Text, { muted: !0, align: "center", children: error })
918
1050
  ]
919
1051
  }
920
1052
  ),
921
1053
  /* @__PURE__ */ jsx(
922
1054
  Image,
923
1055
  {
924
- src,
1056
+ src: thumbnailSrc ?? void 0,
925
1057
  alt: `Preview for ${staticImage ? "image" : "video"} ${asset.filename || asset.assetId}`,
926
1058
  onLoad: handleLoad,
927
- onError: handleError,
1059
+ onError: () => handleError(),
928
1060
  style: { opacity: status === "loaded" ? 1 : 0 }
929
1061
  }
930
1062
  )
931
1063
  ] }) : null
932
1064
  }
933
- );
1065
+ ) });
934
1066
  }
935
1067
  const MissingAssetCheckbox = styled(Checkbox)`
936
1068
  position: static !important;
@@ -1164,7 +1296,7 @@ const PageSelector = (props) => {
1164
1296
  style: { cursor: "pointer" },
1165
1297
  disabled: page <= 0,
1166
1298
  onClick: () => {
1167
- setPage((page2) => Math.min(props.total - 1, Math.max(0, page2 - 1)));
1299
+ setPage((p) => Math.min(props.total - 1, Math.max(0, p - 1)));
1168
1300
  }
1169
1301
  }
1170
1302
  ),
@@ -1183,7 +1315,7 @@ const PageSelector = (props) => {
1183
1315
  style: { cursor: "pointer" },
1184
1316
  disabled: page >= props.total - 1,
1185
1317
  onClick: () => {
1186
- setPage((page2) => Math.min(props.total - 1, Math.max(0, page2 + 1)));
1318
+ setPage((p) => Math.min(props.total - 1, Math.max(0, p + 1)));
1187
1319
  }
1188
1320
  }
1189
1321
  )
@@ -1547,9 +1679,9 @@ async function downloadVttFile(client, asset, track) {
1547
1679
  const playbackId = getPlaybackId(asset);
1548
1680
  if (!playbackId)
1549
1681
  throw new Error("Playback ID is required");
1550
- const playbackPolicy = getPlaybackPolicy(asset);
1682
+ const playbackPolicy = getPlaybackPolicy(asset)?.policy;
1551
1683
  let downloadUrl = `https://stream.mux.com/${playbackId}/text/${track.id}.vtt`;
1552
- if (playbackPolicy === "signed") {
1684
+ if (playbackPolicy === "signed" || playbackPolicy === "drm") {
1553
1685
  const token = generateJwt(client, playbackId, "v");
1554
1686
  downloadUrl += `?token=${token}`;
1555
1687
  }
@@ -2027,7 +2159,7 @@ function EditCaptionDialog({ asset, track, onUpdate, onClose }) {
2027
2159
  const playbackId = getPlaybackId(asset);
2028
2160
  if (!playbackId) return "";
2029
2161
  let url = `https://stream.mux.com/${playbackId}/text/${track.id}.vtt`;
2030
- if (getPlaybackPolicy(asset) === "signed") {
2162
+ if (getPlaybackPolicy(asset)?.policy === "signed") {
2031
2163
  const token = generateJwt(client, playbackId, "v");
2032
2164
  url += `?token=${token}`;
2033
2165
  }
@@ -2683,13 +2815,13 @@ const DialogStateContext = createContext({
2683
2815
  setDialogState,
2684
2816
  children
2685
2817
  }) => /* @__PURE__ */ jsx(DialogStateContext.Provider, { value: { dialogState, setDialogState }, children }), useDialogStateContext = () => useContext(DialogStateContext);
2686
- function getVideoSrc({ asset, client }) {
2687
- const playbackId = getPlaybackId(asset), searchParams = new URLSearchParams();
2688
- if (getPlaybackPolicy(asset) === "signed") {
2689
- const token = generateJwt(client, playbackId, "v");
2818
+ function getVideoSrc({ client, muxPlaybackId: muxPlaybackId2 }) {
2819
+ const searchParams = new URLSearchParams();
2820
+ if (muxPlaybackId2.policy === "signed" || muxPlaybackId2.policy === "drm") {
2821
+ const token = generateJwt(client, muxPlaybackId2.id, "v");
2690
2822
  searchParams.set("token", token);
2691
2823
  }
2692
- return `https://stream.mux.com/${playbackId}.m3u8?${searchParams}`;
2824
+ return `https://stream.mux.com/${muxPlaybackId2.id}.m3u8?${searchParams}`;
2693
2825
  }
2694
2826
  function CaptionsDialog({ asset }) {
2695
2827
  const { setDialogState } = useDialogStateContext(), dialogId = `CaptionsDialog${useId()}`;
@@ -2799,24 +2931,56 @@ function VideoPlayer({
2799
2931
  hlsConfig,
2800
2932
  ...props
2801
2933
  }) {
2802
- const client = useClient(), { dialogState } = useDialogStateContext(), isAudio = assetIsAudio(asset), muxPlayer = useRef(null), {
2803
- src: videoSrc,
2804
- thumbnail: thumbnailSrc,
2805
- error
2806
- } = useMemo(() => {
2934
+ const client = useClient(), { dialogState } = useDialogStateContext(), isAudio = assetIsAudio(asset), muxPlayer = useRef(null), [error, setError] = useState(), playbackId = useMemo(() => {
2807
2935
  try {
2808
- const thumbnail = getPosterSrc({ asset, client, width: thumbnailWidth }), src = asset?.playbackId && getVideoSrc({ client, asset });
2809
- return src ? { src, thumbnail } : { error: new TypeError("Asset has no playback ID") };
2810
- } catch (error2) {
2811
- return { error: error2 };
2936
+ return getPlaybackId(asset, ["public", "signed", "drm"]);
2937
+ } catch {
2938
+ setError(new TypeError("Asset has no playback ID"));
2939
+ return;
2812
2940
  }
2813
- }, [asset, client, thumbnailWidth]), signedToken = useMemo(() => {
2941
+ }, [asset]), muxPlaybackId2 = useMemo(() => {
2942
+ if (playbackId)
2943
+ return getPlaybackPolicyById(asset, playbackId);
2944
+ }, [asset, playbackId]), src = useMemo(() => {
2945
+ if (playbackId && muxPlaybackId2)
2946
+ return tryWithSuspend(
2947
+ () => getVideoSrc({ muxPlaybackId: muxPlaybackId2, client }),
2948
+ (e) => {
2949
+ setError(e);
2950
+ }
2951
+ );
2952
+ }, [muxPlaybackId2, playbackId, client]), poster = useMemo(() => tryWithSuspend(
2953
+ () => getPosterSrc({ asset, client, width: thumbnailWidth }),
2954
+ (e) => {
2955
+ setError(e);
2956
+ }
2957
+ ), [asset, client, thumbnailWidth]), signedToken = useMemo(() => {
2814
2958
  try {
2815
- return new URL(videoSrc).searchParams.get("token");
2959
+ return new URL(src).searchParams.get("token");
2816
2960
  } catch {
2817
- return !1;
2961
+ return;
2818
2962
  }
2819
- }, [videoSrc]), [width, height] = (asset?.data?.aspect_ratio ?? "16:9").split(":").map(Number), targetAspectRatio = props.forceAspectRatio || (Number.isNaN(width) ? 16 / 9 : width / height);
2963
+ }, [src]), drmToken = useMemo(() => {
2964
+ if (playbackId && muxPlaybackId2?.policy === "drm")
2965
+ return tryWithSuspend(
2966
+ () => generateJwt(client, playbackId, "d"),
2967
+ (e) => {
2968
+ setError(e);
2969
+ }
2970
+ );
2971
+ }, [client, muxPlaybackId2?.policy, playbackId]), tokens = useMemo(() => {
2972
+ try {
2973
+ const partialTokens = {
2974
+ playback: void 0,
2975
+ thumbnail: void 0,
2976
+ storyboard: void 0,
2977
+ drm: void 0
2978
+ };
2979
+ return signedToken && (partialTokens.playback = signedToken, partialTokens.thumbnail = signedToken, partialTokens.storyboard = signedToken), drmToken && (partialTokens.drm = drmToken), { ...partialTokens };
2980
+ } catch {
2981
+ return;
2982
+ }
2983
+ }, [signedToken, drmToken]), [width, height] = (asset?.data?.aspect_ratio ?? "16:9").split(":").map(Number), targetAspectRatio = props.forceAspectRatio || (Number.isNaN(width) ? 16 / 9 : width / height);
2820
2984
  let aspectRatio = Math.max(MIN_ASPECT_RATIO, targetAspectRatio);
2821
2985
  return isAudio && (aspectRatio = props.forceAspectRatio ? (
2822
2986
  // Make it wider when forcing aspect ratio to balance with videos' rendering height (audio players overflow a bit)
@@ -2832,7 +2996,7 @@ function VideoPlayer({
2832
2996
  ...isAudio && { display: "flex", alignItems: "flex-end" }
2833
2997
  },
2834
2998
  children: [
2835
- videoSrc && /* @__PURE__ */ jsxs(Fragment, { children: [
2999
+ src && poster && /* @__PURE__ */ jsxs(Fragment, { children: [
2836
3000
  isAudio && /* @__PURE__ */ jsx(
2837
3001
  AudioIcon,
2838
3002
  {
@@ -2847,34 +3011,36 @@ function VideoPlayer({
2847
3011
  }
2848
3012
  }
2849
3013
  ),
2850
- /* @__PURE__ */ jsx(
2851
- MuxPlayer,
2852
- {
2853
- poster: isAudio ? void 0 : thumbnailSrc,
2854
- ref: muxPlayer,
2855
- ...props,
2856
- playsInline: !0,
2857
- playbackId: asset.playbackId,
2858
- tokens: signedToken ? { playback: signedToken, thumbnail: signedToken, storyboard: signedToken } : void 0,
2859
- preload: "metadata",
2860
- crossOrigin: "anonymous",
2861
- metadata: {
2862
- player_name: "Sanity Admin Dashboard",
2863
- player_version: "2.14.0",
2864
- page_type: "Preview Player"
2865
- },
2866
- audio: isAudio,
2867
- _hlsConfig: hlsConfig,
2868
- style: {
2869
- ...!isAudio && { height: "100%" },
2870
- width: "100%",
2871
- display: "block",
2872
- objectFit: "contain",
2873
- ...isAudio && { alignSelf: "end" }
3014
+ /* @__PURE__ */ jsxs(Suspense, { fallback: null, children: [
3015
+ /* @__PURE__ */ jsx(
3016
+ MuxPlayer,
3017
+ {
3018
+ poster: isAudio ? void 0 : poster,
3019
+ ref: muxPlayer,
3020
+ ...props,
3021
+ playsInline: !0,
3022
+ playbackId,
3023
+ tokens,
3024
+ preload: "metadata",
3025
+ crossOrigin: "anonymous",
3026
+ metadata: {
3027
+ player_name: "Sanity Admin Dashboard",
3028
+ player_version: "2.15.0",
3029
+ page_type: "Preview Player"
3030
+ },
3031
+ audio: isAudio,
3032
+ _hlsConfig: hlsConfig,
3033
+ style: {
3034
+ ...!isAudio && { height: "100%" },
3035
+ width: "100%",
3036
+ display: "block",
3037
+ objectFit: "contain",
3038
+ ...isAudio && { alignSelf: "end" }
3039
+ }
2874
3040
  }
2875
- }
2876
- ),
2877
- children
3041
+ ),
3042
+ children
3043
+ ] })
2878
3044
  ] }),
2879
3045
  error ? /* @__PURE__ */ jsx(
2880
3046
  "div",
@@ -3166,6 +3332,7 @@ function getVideoMetadata(doc) {
3166
3332
  playbackId: doc.playbackId,
3167
3333
  createdAt: date,
3168
3334
  duration: doc.data?.duration ? formatSeconds(doc.data?.duration) : void 0,
3335
+ playback_ids: doc.data?.playback_ids,
3169
3336
  aspect_ratio: doc.data?.aspect_ratio,
3170
3337
  max_stored_resolution: doc.data?.max_stored_resolution,
3171
3338
  max_stored_frame_rate: doc.data?.max_stored_frame_rate,
@@ -3468,14 +3635,7 @@ const AssetInput = (props) => /* @__PURE__ */ jsx(FormField$1, { title: props.la
3468
3635
  ),
3469
3636
  /* @__PURE__ */ jsx(IconInfo, { text: `Mux ID:
3470
3637
  ${displayInfo.id}`, icon: TagIcon, size: 2 }),
3471
- displayInfo?.playbackId && /* @__PURE__ */ jsx(
3472
- IconInfo,
3473
- {
3474
- text: `Playback ID: ${displayInfo.playbackId}`,
3475
- icon: TagIcon,
3476
- size: 2
3477
- }
3478
- )
3638
+ /* @__PURE__ */ jsx(PlaybackIds, { playback_ids: displayInfo.playback_ids })
3479
3639
  ] })
3480
3640
  ] })
3481
3641
  }
@@ -3498,6 +3658,25 @@ ${displayInfo.id}`, icon: TagIcon, size: 2 }),
3498
3658
  ]
3499
3659
  }
3500
3660
  );
3661
+ }, PlaybackIds = ({ playback_ids }) => playback_ids ? playback_ids.map((entry) => /* @__PURE__ */ jsx(
3662
+ IconInfo,
3663
+ {
3664
+ text: `Playback ID [${policyToText(entry.policy)}]: ${entry.id}`,
3665
+ icon: TagIcon,
3666
+ size: 2
3667
+ },
3668
+ entry.id
3669
+ )) : /* @__PURE__ */ jsx(IconInfo, { text: "No Playback ID", icon: TagIcon, size: 2 }), policyToText = (policy) => {
3670
+ switch (policy) {
3671
+ case "drm":
3672
+ return "DRM";
3673
+ case "signed":
3674
+ return "Signed";
3675
+ case "public":
3676
+ return "Public";
3677
+ default:
3678
+ return policy;
3679
+ }
3501
3680
  }, VideoMetadata = (props) => {
3502
3681
  if (!props.asset)
3503
3682
  return null;
@@ -3591,10 +3770,12 @@ function VideoInBrowser({
3591
3770
  onEdit,
3592
3771
  asset
3593
3772
  }) {
3594
- const [renderVideo, setRenderVideo] = useState(!1), select = React.useCallback(() => onSelect?.(asset), [onSelect, asset]), edit = React.useCallback(() => onEdit?.(asset), [onEdit, asset]);
3773
+ const [renderVideo, setRenderVideo] = useState(!1), select = React.useCallback(() => onSelect?.(asset), [onSelect, asset]), edit = React.useCallback(() => onEdit?.(asset), [onEdit, asset]), { hasShownWarning } = useDrmPlaybackWarningContext();
3595
3774
  if (!asset)
3596
3775
  return null;
3597
- const playbackPolicy = getPlaybackPolicy(asset);
3776
+ const playbackPolicy = getPlaybackPolicy(asset), onClickPlay = () => {
3777
+ playbackPolicy?.policy === "drm" && !hasShownWarning ? setRenderVideo("pre-render-warn") : setRenderVideo("render-video");
3778
+ };
3598
3779
  return /* @__PURE__ */ jsxs(
3599
3780
  Card,
3600
3781
  {
@@ -3606,7 +3787,7 @@ function VideoInBrowser({
3606
3787
  position: "relative"
3607
3788
  },
3608
3789
  children: [
3609
- playbackPolicy === "signed" && /* @__PURE__ */ jsx(
3790
+ playbackPolicy?.policy === "signed" && /* @__PURE__ */ jsx(
3610
3791
  Tooltip,
3611
3792
  {
3612
3793
  animate: !0,
@@ -3623,7 +3804,7 @@ function VideoInBrowser({
3623
3804
  position: "absolute",
3624
3805
  left: "1em",
3625
3806
  top: "1em",
3626
- zIndex: 10
3807
+ zIndex: 11
3627
3808
  },
3628
3809
  padding: 2,
3629
3810
  border: !0,
@@ -3632,6 +3813,32 @@ function VideoInBrowser({
3632
3813
  )
3633
3814
  }
3634
3815
  ),
3816
+ playbackPolicy?.policy === "drm" && /* @__PURE__ */ jsx(
3817
+ Tooltip,
3818
+ {
3819
+ animate: !0,
3820
+ content: /* @__PURE__ */ jsx(Card, { padding: 2, radius: 2, children: /* @__PURE__ */ jsx(IconInfo, { icon: LockIcon, text: "DRM playback policy", size: 2 }) }),
3821
+ placement: "right",
3822
+ fallbackPlacements: ["top", "bottom"],
3823
+ portal: !0,
3824
+ children: /* @__PURE__ */ jsx(
3825
+ Card,
3826
+ {
3827
+ tone: "caution",
3828
+ style: {
3829
+ borderRadius: "0.25rem",
3830
+ position: "absolute",
3831
+ left: "1em",
3832
+ top: "1em",
3833
+ zIndex: 11
3834
+ },
3835
+ padding: 2,
3836
+ border: !0,
3837
+ children: /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, weight: "semibold", style: { color: "var(--card-icon-color)" }, children: "DRM" })
3838
+ }
3839
+ )
3840
+ }
3841
+ ),
3635
3842
  /* @__PURE__ */ jsxs(
3636
3843
  Stack,
3637
3844
  {
@@ -3641,7 +3848,15 @@ function VideoInBrowser({
3641
3848
  gridTemplateRows: "min-content min-content 1fr"
3642
3849
  },
3643
3850
  children: [
3644
- renderVideo ? /* @__PURE__ */ jsx(VideoPlayer, { asset, autoPlay: !0, forceAspectRatio: THUMBNAIL_ASPECT_RATIO }) : /* @__PURE__ */ jsxs(PlayButton, { onClick: () => setRenderVideo(!0), children: [
3851
+ renderVideo === "pre-render-warn" && /* @__PURE__ */ jsx(
3852
+ DRMWarningDialog,
3853
+ {
3854
+ onClose: () => {
3855
+ setRenderVideo("render-video");
3856
+ }
3857
+ }
3858
+ ),
3859
+ renderVideo === "render-video" ? /* @__PURE__ */ jsx(VideoPlayer, { asset, autoPlay: !0, forceAspectRatio: THUMBNAIL_ASPECT_RATIO }) : /* @__PURE__ */ jsxs(PlayButton, { onClick: onClickPlay, children: [
3645
3860
  /* @__PURE__ */ jsx("div", { "data-play": !0, children: /* @__PURE__ */ jsx(PlayIcon, {}) }),
3646
3861
  assetIsAudio(asset) ? /* @__PURE__ */ jsx(
3647
3862
  "div",
@@ -3703,12 +3918,12 @@ function VideoInBrowser({
3703
3918
  }
3704
3919
  );
3705
3920
  }
3706
- function VideosBrowser({ onSelect }) {
3921
+ function VideosBrowser({ onSelect, config }) {
3707
3922
  const { assets, isLoading, searchQuery, setSearchQuery, setSort, sort } = useAssets(), [page, setPage] = useState(0), pageLimit = 20, pageTotal = Math.floor(assets.length / pageLimit) + 1, [editedAsset, setEditedAsset] = useState(null), freshEditedAsset = useMemo(
3708
3923
  () => assets.find((a2) => a2._id === editedAsset?._id) || editedAsset,
3709
3924
  [editedAsset, assets]
3710
3925
  ), pageStart = page * pageLimit, pageEnd = pageStart + pageLimit;
3711
- return /* @__PURE__ */ jsxs(Fragment, { children: [
3926
+ return /* @__PURE__ */ jsxs(DrmPlaybackWarningContextProvider, { config, children: [
3712
3927
  /* @__PURE__ */ jsxs(Stack, { padding: 4, space: 4, style: { minHeight: "50vh" }, children: [
3713
3928
  /* @__PURE__ */ jsxs(Flex, { justify: "space-between", align: "center", children: [
3714
3929
  /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 3, children: [
@@ -3722,7 +3937,7 @@ function VideosBrowser({ onSelect }) {
3722
3937
  }
3723
3938
  ),
3724
3939
  /* @__PURE__ */ jsx(SelectSortOptions, { setSort, sort }),
3725
- /* @__PURE__ */ jsx(PageSelector, { page, setPage, total: pageTotal, limit: pageLimit })
3940
+ /* @__PURE__ */ jsx(PageSelector, { page, setPage, total: pageTotal })
3726
3941
  ] }),
3727
3942
  (onSelect ? "input" : "tool") == "tool" && /* @__PURE__ */ jsxs(Inline, { space: 2, children: [
3728
3943
  /* @__PURE__ */ jsx(ImportVideosFromMux, {}),
@@ -3763,7 +3978,7 @@ function VideosBrowser({ onSelect }) {
3763
3978
  freshEditedAsset && /* @__PURE__ */ jsx(VideoDetails, { closeDialog: () => setEditedAsset(null), asset: freshEditedAsset })
3764
3979
  ] });
3765
3980
  }
3766
- const StudioTool = () => /* @__PURE__ */ jsx(VideosBrowser, {}), DEFAULT_TOOL_CONFIG = {
3981
+ const StudioTool = (config) => /* @__PURE__ */ jsx(VideosBrowser, { config }), DEFAULT_TOOL_CONFIG = {
3767
3982
  icon: ToolIcon,
3768
3983
  title: "Videos"
3769
3984
  };
@@ -4121,6 +4336,9 @@ function isValidUrl(url) {
4121
4336
  return !1;
4122
4337
  }
4123
4338
  }
4339
+ function isServerError(error) {
4340
+ return "statusCode" in error && typeof error.statusCode == "number" && 500 <= error.statusCode && error.statusCode <= 600;
4341
+ }
4124
4342
  function extractDroppedFiles(dataTransfer) {
4125
4343
  const files = Array.from(dataTransfer.files || []), items = Array.from(dataTransfer.items || []);
4126
4344
  return files && files.length > 0 ? Promise.resolve(files) : normalizeItems(items).then((arr) => arr.flat());
@@ -4162,7 +4380,12 @@ function walk(entry) {
4162
4380
  }
4163
4381
  return Promise.resolve([]);
4164
4382
  }
4165
- function SelectAssets({ asset: selectedAsset, onChange, setDialogState }) {
4383
+ function SelectAssets({
4384
+ asset: selectedAsset,
4385
+ onChange,
4386
+ setDialogState,
4387
+ config
4388
+ }) {
4166
4389
  const handleSelect = useCallback(
4167
4390
  (chosenAsset) => {
4168
4391
  chosenAsset?._id || onChange(PatchEvent.from([unset(["asset"])])), chosenAsset._id !== selectedAsset?._id && onChange(
@@ -4174,7 +4397,7 @@ function SelectAssets({ asset: selectedAsset, onChange, setDialogState }) {
4174
4397
  },
4175
4398
  [onChange, setDialogState, selectedAsset]
4176
4399
  );
4177
- return /* @__PURE__ */ jsx(VideosBrowser, { onSelect: handleSelect });
4400
+ return /* @__PURE__ */ jsx(VideosBrowser, { onSelect: handleSelect, config });
4178
4401
  }
4179
4402
  const StyledDialog = styled(Dialog)`
4180
4403
  > div[data-ui='DialogCard'] > div[data-ui='Card'] {
@@ -4184,7 +4407,8 @@ const StyledDialog = styled(Dialog)`
4184
4407
  function InputBrowser({
4185
4408
  setDialogState,
4186
4409
  asset,
4187
- onChange
4410
+ onChange,
4411
+ config
4188
4412
  }) {
4189
4413
  const id = `InputBrowser${useId()}`, handleClose = useCallback(() => setDialogState(!1), [setDialogState]);
4190
4414
  return /* @__PURE__ */ jsx(
@@ -4195,7 +4419,15 @@ function InputBrowser({
4195
4419
  id,
4196
4420
  onClose: handleClose,
4197
4421
  width: 2,
4198
- children: /* @__PURE__ */ jsx(SelectAssets, { asset, onChange, setDialogState })
4422
+ children: /* @__PURE__ */ jsx(
4423
+ SelectAssets,
4424
+ {
4425
+ config,
4426
+ asset,
4427
+ onChange,
4428
+ setDialogState
4429
+ }
4430
+ )
4199
4431
  }
4200
4432
  );
4201
4433
  }
@@ -4411,7 +4643,7 @@ const FileButton = styled(MenuItem)(({ theme }) => {
4411
4643
  color: white;
4412
4644
  `, isVideoAsset = (asset) => asset._type === "mux.videoAsset";
4413
4645
  function PlayerActionsMenu(props) {
4414
- const { asset, readOnly, dialogState, setDialogState, onChange, onSelect, accept } = props, [open, setOpen] = useState(!1), [menuElement, setMenuRef] = useState(null), isSigned = useMemo(() => getPlaybackPolicy(asset) === "signed", [asset]), { hasConfigAccess } = useAccessControl(props.config), onReset = useCallback(() => onChange(PatchEvent.from(unset([]))), [onChange]);
4646
+ const { asset, readOnly, dialogState, setDialogState, onChange, onSelect, accept } = props, [open, setOpen] = useState(!1), [menuElement, setMenuRef] = useState(null), isSigned = useMemo(() => getPlaybackPolicy(asset)?.policy === "signed", [asset]), { hasConfigAccess } = useAccessControl(props.config), onReset = useCallback(() => onChange(PatchEvent.from(unset([]))), [onChange]);
4415
4647
  return useEffect(() => {
4416
4648
  open && dialogState && setOpen(!1);
4417
4649
  }, [dialogState, open]), useClickOutsideEvent(
@@ -4512,6 +4744,79 @@ function PlayerActionsMenu(props) {
4512
4744
  ] });
4513
4745
  }
4514
4746
  var PlayerActionsMenu$1 = memo(PlayerActionsMenu);
4747
+ function useFetchFileSize(stagedUpload, maxFileSize) {
4748
+ const [fileSize, setFileSize] = useState(null), [isLoadingFileSize, setIsLoadingFileSize] = useState(!1), [canSkipFileSizeValidation, setCanSkipFileSizeValidation] = useState(!1);
4749
+ return useEffect(() => {
4750
+ if (stagedUpload.type === "url") {
4751
+ setIsLoadingFileSize(!1), setCanSkipFileSizeValidation(!1), setFileSize(null);
4752
+ const url = stagedUpload.url;
4753
+ (async () => {
4754
+ setIsLoadingFileSize(!0);
4755
+ try {
4756
+ const contentLength = (await fetch(url, { method: "HEAD" })).headers.get("content-length"), newFileSize = contentLength ? parseInt(contentLength, 10) : null;
4757
+ setIsLoadingFileSize(!1), newFileSize && setFileSize(newFileSize), newFileSize === null && maxFileSize !== void 0 && setCanSkipFileSizeValidation(!0);
4758
+ } catch {
4759
+ console.warn("Could not validate file size from URL"), setCanSkipFileSizeValidation(!0), setIsLoadingFileSize(!1);
4760
+ }
4761
+ })();
4762
+ }
4763
+ stagedUpload.type === "file" && setFileSize(stagedUpload.files[0].size);
4764
+ }, [maxFileSize, stagedUpload, stagedUpload.type]), {
4765
+ fileSize,
4766
+ isLoadingFileSize,
4767
+ canSkipFileSizeValidation
4768
+ };
4769
+ }
4770
+ function useMediaMetadata(stagedUpload) {
4771
+ const [videoAssetMetadata, setVideoAssetMetadata] = useState(null), [isLoadingMetadata, setIsLoadingMetadata] = useState(!1);
4772
+ return useEffect(() => {
4773
+ let videoSrc = null;
4774
+ if (stagedUpload.type === "file") {
4775
+ const file = stagedUpload.files[0];
4776
+ videoSrc = URL.createObjectURL(file);
4777
+ }
4778
+ if (stagedUpload.type === "url" && (videoSrc = stagedUpload.url), setVideoAssetMetadata((old) => ({
4779
+ ...old,
4780
+ duration: void 0,
4781
+ width: void 0,
4782
+ height: void 0
4783
+ })), !videoSrc) return () => null;
4784
+ setIsLoadingMetadata(!0);
4785
+ const videoElement = document.createElement("video");
4786
+ videoElement.preload = "metadata";
4787
+ const metadataListeners = [
4788
+ () => {
4789
+ setIsLoadingMetadata(!1);
4790
+ },
4791
+ () => {
4792
+ const duration = videoElement.duration, width = videoElement.videoWidth, height = videoElement.videoHeight, isAudioOnly = width <= 0 && height <= 0;
4793
+ setVideoAssetMetadata((old) => ({
4794
+ ...old,
4795
+ duration,
4796
+ width,
4797
+ height,
4798
+ isAudioOnly
4799
+ }));
4800
+ }
4801
+ ], cleanupVideo = (videoEl) => {
4802
+ const currentVideoSrc = videoEl?.src;
4803
+ videoEl && (metadataListeners.forEach(
4804
+ (listener) => videoEl.removeEventListener("loadedmetadata", listener)
4805
+ ), videoEl.onerror = null, videoEl.src = "", videoEl.load()), currentVideoSrc?.startsWith("blob:") && URL.revokeObjectURL(currentVideoSrc);
4806
+ };
4807
+ return metadataListeners.push(() => setTimeout(() => cleanupVideo(videoElement), 0)), videoElement.onerror = () => {
4808
+ setIsLoadingMetadata(!1), console.warn("Could not read video metadata for validation"), cleanupVideo(videoElement);
4809
+ }, metadataListeners.forEach(
4810
+ (listener) => videoElement.addEventListener("loadedmetadata", listener)
4811
+ ), videoElement.src = videoSrc, () => {
4812
+ cleanupVideo(videoElement);
4813
+ };
4814
+ }, [stagedUpload.type, stagedUpload]), {
4815
+ videoAssetMetadata,
4816
+ setVideoAssetMetadata,
4817
+ isLoadingMetadata
4818
+ };
4819
+ }
4515
4820
  function formatBytes(bytes, si = !1, dp = 1) {
4516
4821
  const thresh = si ? 1e3 : 1024;
4517
4822
  if (Math.abs(bytes) < thresh)
@@ -4601,13 +4906,14 @@ function PlaybackPolicyOption({
4601
4906
  optionName,
4602
4907
  description,
4603
4908
  dispatch,
4604
- action
4909
+ action,
4910
+ disabled
4605
4911
  }) {
4606
4912
  const [scale, setScale] = useState(1), boxStyle = {
4607
4913
  outline: "0.01rem solid grey",
4608
4914
  transform: `scale(${scale})`,
4609
4915
  transition: "transform 0.1s ease-in-out",
4610
- cursor: "pointer",
4916
+ cursor: disabled ? "not-allowed" : "pointer",
4611
4917
  borderRadius: "0.25rem"
4612
4918
  }, triggerAnimation = () => {
4613
4919
  setScale(0.98), setTimeout(() => {
@@ -4615,15 +4921,24 @@ function PlaybackPolicyOption({
4615
4921
  }, 100);
4616
4922
  };
4617
4923
  return /* @__PURE__ */ jsx("label", { children: /* @__PURE__ */ jsxs(Flex, { gap: 3, padding: 3, style: boxStyle, children: [
4618
- /* @__PURE__ */ jsx(Checkbox, { id, required: !0, checked, onChange: () => {
4619
- triggerAnimation(), dispatch({
4620
- action,
4621
- value: !checked
4622
- });
4623
- } }),
4924
+ /* @__PURE__ */ jsx(
4925
+ Checkbox,
4926
+ {
4927
+ id,
4928
+ required: !0,
4929
+ checked,
4930
+ onChange: () => {
4931
+ action && (triggerAnimation(), dispatch({
4932
+ action,
4933
+ value: !checked
4934
+ }));
4935
+ },
4936
+ disabled
4937
+ }
4938
+ ),
4624
4939
  /* @__PURE__ */ jsxs(Grid, { gap: 3, children: [
4625
4940
  /* @__PURE__ */ jsx(Text, { size: 3, weight: "bold", children: optionName }),
4626
- /* @__PURE__ */ jsx(Text, { size: 2, muted: !0, children: description })
4941
+ typeof description == "string" ? /* @__PURE__ */ jsx(Text, { size: 2, muted: !0, children: description }) : description
4627
4942
  ] })
4628
4943
  ] }) });
4629
4944
  }
@@ -4648,7 +4963,7 @@ function PlaybackPolicy({
4648
4963
  secrets,
4649
4964
  dispatch
4650
4965
  }) {
4651
- const noPolicySelected = !(config.public_policy || config.signed_policy);
4966
+ const noPolicySelected = !(config.public_policy || config.signed_policy || config.drm_policy), drmPolicyDisabled = !secrets.drmConfigId;
4652
4967
  return /* @__PURE__ */ jsxs(Grid, { gap: 3, children: [
4653
4968
  /* @__PURE__ */ jsx(Text, { weight: "bold", children: "Advanced Playback Policies" }),
4654
4969
  /* @__PURE__ */ jsx(
@@ -4657,7 +4972,10 @@ function PlaybackPolicy({
4657
4972
  id: `${id}--public`,
4658
4973
  checked: config.public_policy,
4659
4974
  optionName: "Public",
4660
- description: "Playback IDs are accessible by constructing an HLS URL like https://stream.mux.com/{PLAYBACK_ID}",
4975
+ description: /* @__PURE__ */ jsxs(Fragment, { children: [
4976
+ /* @__PURE__ */ jsx(Text, { size: 2, muted: !0, children: "Playback IDs are accessible by constructing an HLS URL like" }),
4977
+ /* @__PURE__ */ jsx(Code, { children: "https://stream.mux.com/{PLAYBACK_ID}" })
4978
+ ] }),
4661
4979
  dispatch,
4662
4980
  action: "public_policy"
4663
4981
  }
@@ -4668,24 +4986,146 @@ function PlaybackPolicy({
4668
4986
  id: `${id}--signed`,
4669
4987
  checked: config.signed_policy,
4670
4988
  optionName: "Signed",
4671
- description: `Playback IDs should be used with tokens https://stream.mux.com/{PLAYBACK_ID}?token={TOKEN}.
4672
- // See Secure video playback for details about creating tokens.`,
4989
+ description: /* @__PURE__ */ jsxs(Fragment, { children: [
4990
+ /* @__PURE__ */ jsx(Text, { size: 2, muted: !0, children: "Playback IDs should be used with tokens" }),
4991
+ /* @__PURE__ */ jsx(Code, { children: "https://stream.mux.com/{PLAYBACK_ID}?token={TOKEN}" }),
4992
+ /* @__PURE__ */ jsxs(Text, { size: 2, muted: !0, children: [
4993
+ "See",
4994
+ " ",
4995
+ /* @__PURE__ */ jsx(
4996
+ "a",
4997
+ {
4998
+ href: "https://www.mux.com/docs/guides/secure-video-playback",
4999
+ target: "_blank",
5000
+ rel: "noopener noreferrer",
5001
+ children: "Secure video playback"
5002
+ }
5003
+ ),
5004
+ " ",
5005
+ "for details about creating tokens."
5006
+ ] })
5007
+ ] }),
4673
5008
  dispatch,
4674
5009
  action: "signed_policy"
4675
5010
  }
4676
5011
  ),
5012
+ drmPolicyDisabled ? /* @__PURE__ */ jsx(
5013
+ PlaybackPolicyOption,
5014
+ {
5015
+ id: `${id}--drm`,
5016
+ checked: !1,
5017
+ optionName: "DRM - Disabled",
5018
+ description: /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Text, { size: 2, muted: !0, children: [
5019
+ "To enable DRM add your DRM Configuration Id to your plugin configuration in the API Credentials view.",
5020
+ " ",
5021
+ /* @__PURE__ */ jsx(
5022
+ "a",
5023
+ {
5024
+ href: "https://www.mux.com/support/human",
5025
+ target: "_blank",
5026
+ rel: "noopener noreferrer",
5027
+ children: "Contact us"
5028
+ }
5029
+ ),
5030
+ " ",
5031
+ "to get started using DRM."
5032
+ ] }) }),
5033
+ dispatch,
5034
+ disabled: !0
5035
+ }
5036
+ ) : /* @__PURE__ */ jsx(
5037
+ PlaybackPolicyOption,
5038
+ {
5039
+ id: `${id}--drm`,
5040
+ checked: config.drm_policy,
5041
+ optionName: "DRM",
5042
+ description: /* @__PURE__ */ jsxs(Fragment, { children: [
5043
+ /* @__PURE__ */ jsx(Text, { size: 2, muted: !0, children: "Playback IDs should be used with tokens as with Signed playback, but require extra configuration." }),
5044
+ /* @__PURE__ */ jsx(Code, { children: "https://stream.mux.com/{PLAYBACK_ID}?token={TOKEN}" }),
5045
+ /* @__PURE__ */ jsxs(Text, { size: 2, muted: !0, children: [
5046
+ "See",
5047
+ " ",
5048
+ /* @__PURE__ */ jsx(
5049
+ "a",
5050
+ {
5051
+ href: "https://www.mux.com/docs/guides/protect-videos-with-drm#play-drm-protected-videos",
5052
+ target: "_blank",
5053
+ rel: "noopener noreferrer",
5054
+ children: "Protect videos with DRM"
5055
+ }
5056
+ ),
5057
+ " ",
5058
+ "for details about configuring your player for DRM playback and",
5059
+ " ",
5060
+ /* @__PURE__ */ jsx(
5061
+ "a",
5062
+ {
5063
+ href: "https://www.mux.com/docs/guides/secure-video-playback",
5064
+ target: "_blank",
5065
+ rel: "noopener noreferrer",
5066
+ children: "Secure video playback"
5067
+ }
5068
+ ),
5069
+ " ",
5070
+ "for details about creating tokens."
5071
+ ] })
5072
+ ] }),
5073
+ dispatch,
5074
+ action: "drm_policy"
5075
+ }
5076
+ ),
4677
5077
  noPolicySelected && /* @__PURE__ */ jsx(PlaybackPolicyWarning, {})
4678
5078
  ] });
4679
5079
  }
4680
- const VIDEO_QUALITY_LEVELS = [
4681
- { value: "basic", label: "Basic" },
4682
- { value: "plus", label: "Plus" },
4683
- { value: "premium", label: "Premium" }
4684
- ], RESOLUTION_TIERS = [
5080
+ const RESOLUTION_TIERS = [
4685
5081
  { value: "1080p", label: "1080p" },
4686
5082
  { value: "1440p", label: "1440p (2k)" },
4687
5083
  { value: "2160p", label: "2160p (4k)" }
4688
- ], ADVANCED_RESOLUTIONS = [
5084
+ ], ResolutionTierSelector = ({
5085
+ id,
5086
+ config,
5087
+ dispatch,
5088
+ maxSupportedResolution
5089
+ }) => /* @__PURE__ */ jsx(
5090
+ FormField$2,
5091
+ {
5092
+ title: "Resolution Tier",
5093
+ description: /* @__PURE__ */ jsxs(Fragment, { children: [
5094
+ "The maximum",
5095
+ " ",
5096
+ /* @__PURE__ */ jsx(
5097
+ "a",
5098
+ {
5099
+ href: "https://docs.mux.com/api-reference#video/operation/create-direct-upload",
5100
+ target: "_blank",
5101
+ rel: "noopener noreferrer",
5102
+ children: "resolution_tier"
5103
+ }
5104
+ ),
5105
+ " ",
5106
+ "your asset is encoded, stored, and streamed at."
5107
+ ] }),
5108
+ children: /* @__PURE__ */ jsx(Flex, { gap: 3, wrap: "wrap", children: RESOLUTION_TIERS.map(({ value, label }, index) => {
5109
+ const inputId = `${id}--type-${value}`;
5110
+ return index > maxSupportedResolution ? null : /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
5111
+ /* @__PURE__ */ jsx(
5112
+ Radio,
5113
+ {
5114
+ checked: config.max_resolution_tier === value,
5115
+ name: "asset-resolutiontier",
5116
+ onChange: (e) => dispatch({
5117
+ action: "max_resolution_tier",
5118
+ value: e.currentTarget.value
5119
+ }),
5120
+ value,
5121
+ id: inputId
5122
+ }
5123
+ ),
5124
+ /* @__PURE__ */ jsx(Text, { as: "label", htmlFor: inputId, children: label })
5125
+ ] }, value);
5126
+ }) })
5127
+ }
5128
+ ), ADVANCED_RESOLUTIONS = [
4689
5129
  { value: "270p", label: "270p" },
4690
5130
  { value: "360p", label: "360p" },
4691
5131
  { value: "480p", label: "480p" },
@@ -4694,6 +5134,130 @@ const VIDEO_QUALITY_LEVELS = [
4694
5134
  { value: "1080p", label: "1080p" },
4695
5135
  { value: "1440p", label: "1440p" },
4696
5136
  { value: "2160p", label: "2160p" }
5137
+ ], StaticRenditionSelector = ({
5138
+ id,
5139
+ config,
5140
+ dispatch
5141
+ }) => {
5142
+ const isAdvancedMode = useMemo(() => config.static_renditions.filter(
5143
+ (r) => r !== "highest" && r !== "audio-only"
5144
+ ).length > 0, [config.static_renditions]), [renditionMode, setRenditionMode] = useState(
5145
+ isAdvancedMode ? "advanced" : "standard"
5146
+ ), toggleRendition = (rendition) => {
5147
+ const current = config.static_renditions, hasRendition = current.includes(rendition);
5148
+ dispatch(hasRendition ? {
5149
+ action: "static_renditions",
5150
+ value: current.filter((r) => r !== rendition)
5151
+ } : {
5152
+ action: "static_renditions",
5153
+ value: [...current, rendition]
5154
+ });
5155
+ }, handleModeChange = (mode) => {
5156
+ setRenditionMode(mode), dispatch(mode === "standard" ? {
5157
+ action: "static_renditions",
5158
+ value: config.static_renditions.filter((r) => r === "highest" || r === "audio-only")
5159
+ } : {
5160
+ action: "static_renditions",
5161
+ value: config.static_renditions.filter((r) => r !== "highest")
5162
+ });
5163
+ };
5164
+ return /* @__PURE__ */ jsx(Stack, { space: 3, children: /* @__PURE__ */ jsx(
5165
+ FormField$2,
5166
+ {
5167
+ title: "Static Renditions",
5168
+ description: "Generate downloadable MP4 or M4A files. Note: Mux will not upscale to produce MP4 renditions - renditions that would cause upscaling are skipped.",
5169
+ children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
5170
+ /* @__PURE__ */ jsxs(Flex, { gap: 3, children: [
5171
+ /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
5172
+ /* @__PURE__ */ jsx(
5173
+ Radio,
5174
+ {
5175
+ checked: renditionMode === "standard",
5176
+ name: "rendition-mode",
5177
+ onChange: () => handleModeChange("standard"),
5178
+ value: "standard",
5179
+ id: `${id}--mode-standard`
5180
+ }
5181
+ ),
5182
+ /* @__PURE__ */ jsx(Text, { as: "label", htmlFor: `${id}--mode-standard`, children: "Standard" })
5183
+ ] }),
5184
+ /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
5185
+ /* @__PURE__ */ jsx(
5186
+ Radio,
5187
+ {
5188
+ checked: renditionMode === "advanced",
5189
+ name: "rendition-mode",
5190
+ onChange: () => handleModeChange("advanced"),
5191
+ value: "advanced",
5192
+ id: `${id}--mode-advanced`
5193
+ }
5194
+ ),
5195
+ /* @__PURE__ */ jsx(Text, { as: "label", htmlFor: `${id}--mode-advanced`, children: "Advanced" })
5196
+ ] })
5197
+ ] }),
5198
+ renditionMode === "standard" && /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
5199
+ /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, padding: [0, 2], children: [
5200
+ /* @__PURE__ */ jsx(
5201
+ Checkbox,
5202
+ {
5203
+ id: `${id}--highest`,
5204
+ style: { display: "block" },
5205
+ checked: config.static_renditions.includes("highest"),
5206
+ onChange: () => toggleRendition("highest")
5207
+ }
5208
+ ),
5209
+ /* @__PURE__ */ jsx(Text, { as: "label", htmlFor: `${id}--highest`, children: "Highest Resolution (up to 4K)" })
5210
+ ] }),
5211
+ /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, padding: [0, 2], children: [
5212
+ /* @__PURE__ */ jsx(
5213
+ Checkbox,
5214
+ {
5215
+ id: `${id}--audio-only-standard`,
5216
+ style: { display: "block" },
5217
+ checked: config.static_renditions.includes("audio-only"),
5218
+ onChange: () => toggleRendition("audio-only")
5219
+ }
5220
+ ),
5221
+ /* @__PURE__ */ jsx(Text, { as: "label", htmlFor: `${id}--audio-only-standard`, children: "Audio Only (M4A)" })
5222
+ ] })
5223
+ ] }),
5224
+ renditionMode === "advanced" && /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
5225
+ /* @__PURE__ */ jsx(Label$1, { size: 1, muted: !0, children: "Select specific resolutions:" }),
5226
+ /* @__PURE__ */ jsx(Flex, { gap: 2, wrap: "wrap", children: ADVANCED_RESOLUTIONS.map(({ value, label }) => {
5227
+ const inputId = `${id}--resolution-${value}`;
5228
+ return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
5229
+ /* @__PURE__ */ jsx(
5230
+ Checkbox,
5231
+ {
5232
+ id: inputId,
5233
+ style: { display: "block" },
5234
+ checked: config.static_renditions.includes(value),
5235
+ onChange: () => toggleRendition(value)
5236
+ }
5237
+ ),
5238
+ /* @__PURE__ */ jsx(Text, { as: "label", htmlFor: inputId, size: 1, children: label })
5239
+ ] }, value);
5240
+ }) }),
5241
+ /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, padding: [2, 2, 0, 2], children: [
5242
+ /* @__PURE__ */ jsx(
5243
+ Checkbox,
5244
+ {
5245
+ id: `${id}--audio-only-advanced`,
5246
+ style: { display: "block" },
5247
+ checked: config.static_renditions.includes("audio-only"),
5248
+ onChange: () => toggleRendition("audio-only")
5249
+ }
5250
+ ),
5251
+ /* @__PURE__ */ jsx(Text, { as: "label", htmlFor: `${id}--audio-only-advanced`, children: "Audio Only (M4A)" })
5252
+ ] })
5253
+ ] })
5254
+ ] })
5255
+ }
5256
+ ) });
5257
+ }, VIDEO_QUALITY_LEVELS = [
5258
+ { value: "basic", label: "Basic" },
5259
+ { value: "plus", label: "Plus" },
5260
+ { value: "premium", label: "Premium" }
4697
5261
  ];
4698
5262
  function sanitizeStaticRenditions(renditions) {
4699
5263
  const hasHighest = renditions.includes("highest"), hasSpecificResolutions = renditions.some((r) => r !== "highest" && r !== "audio-only");
@@ -4725,7 +5289,8 @@ function UploadConfiguration({
4725
5289
  max_resolution_tier: "1080p",
4726
5290
  text_tracks: prev.text_tracks?.filter(({ type }) => type !== "autogenerated"),
4727
5291
  public_policy: !0,
4728
- signed_policy: !1
5292
+ signed_policy: !1,
5293
+ drm_policy: !1
4729
5294
  }) : Object.assign({}, prev, {
4730
5295
  video_quality: action.value,
4731
5296
  static_renditions: sanitizeStaticRenditions(pluginConfig.static_renditions || []),
@@ -4739,6 +5304,8 @@ function UploadConfiguration({
4739
5304
  return Object.assign({}, prev, { [action.action]: action.value });
4740
5305
  case "public_policy":
4741
5306
  return Object.assign({}, prev, { [action.action]: action.value });
5307
+ case "drm_policy":
5308
+ return Object.assign({}, prev, { [action.action]: action.value });
4742
5309
  // Updating individual tracks
4743
5310
  case "track": {
4744
5311
  const text_tracks = [...prev.text_tracks], target_track_i = text_tracks.findIndex(({ _id: _id2 }) => _id2 === action.id);
@@ -4774,75 +5341,41 @@ function UploadConfiguration({
4774
5341
  static_renditions: sanitizeStaticRenditions(pluginConfig.static_renditions || []),
4775
5342
  signed_policy: secrets.enableSignedUrls && pluginConfig.defaultSigned,
4776
5343
  public_policy: pluginConfig.defaultPublic,
5344
+ drm_policy: pluginConfig.defaultDrm && !!secrets.drmConfigId,
4777
5345
  normalize_audio: pluginConfig.normalize_audio,
4778
5346
  text_tracks: autoTextTracks
4779
5347
  }
4780
- ), isAdvancedMode = useMemo(() => config.static_renditions.filter(
4781
- (r) => r !== "highest" && r !== "audio-only"
4782
- ).length > 0, [config.static_renditions]), [renditionMode, setRenditionMode] = useState(
4783
- isAdvancedMode ? "advanced" : "standard"
4784
- ), [videoDuration, setVideoDuration] = useState(null), [urlFileSize, setUrlFileSize] = useState(null), [isLoadingDuration, setIsLoadingDuration] = useState(!1), [isLoadingFileSize, setIsLoadingFileSize] = useState(!1), [validationError, setValidationError] = useState(null), [canSkipFileSizeValidation, setCanSkipFileSizeValidation] = useState(!1), MAX_FILE_SIZE = pluginConfig.maxAssetFileSize, MAX_DURATION_SECONDS = pluginConfig.maxAssetDuration;
5348
+ ), [validationError, setValidationError] = useState(null), MAX_FILE_SIZE = pluginConfig.maxAssetFileSize, MAX_DURATION_SECONDS = pluginConfig.maxAssetDuration, { fileSize, isLoadingFileSize, canSkipFileSizeValidation } = useFetchFileSize(
5349
+ stagedUpload,
5350
+ MAX_FILE_SIZE
5351
+ ), { videoAssetMetadata, setVideoAssetMetadata, isLoadingMetadata } = useMediaMetadata(stagedUpload);
4785
5352
  useEffect(() => {
4786
- setVideoDuration(null), setUrlFileSize(null), setIsLoadingDuration(!1), setIsLoadingFileSize(!1), setValidationError(null), setCanSkipFileSizeValidation(!1);
4787
- let videoElement = null, currentVideoSrc = null;
4788
- const cleanupVideo = (shouldRevokeUrl) => {
4789
- videoElement && (videoElement.onloadedmetadata = null, videoElement.onerror = null, videoElement.src = "", videoElement.load(), videoElement = null), shouldRevokeUrl && currentVideoSrc?.startsWith("blob:") && URL.revokeObjectURL(currentVideoSrc), currentVideoSrc = null;
4790
- }, validateDuration = (videoSrc, shouldRevokeUrl = !1) => {
4791
- !MAX_DURATION_SECONDS || MAX_DURATION_SECONDS <= 0 || (setIsLoadingDuration(!0), videoElement = document.createElement("video"), videoElement.preload = "metadata", currentVideoSrc = videoSrc, videoElement.onloadedmetadata = () => {
4792
- const duration = videoElement.duration;
4793
- setVideoDuration(duration), setIsLoadingDuration(!1), duration > MAX_DURATION_SECONDS && setValidationError(
4794
- `Video duration (${formatSeconds(duration)}) exceeds maximum allowed duration of ${formatSeconds(MAX_DURATION_SECONDS)}`
4795
- ), cleanupVideo(shouldRevokeUrl);
4796
- }, videoElement.onerror = () => {
4797
- setIsLoadingDuration(!1), console.warn("Could not read video metadata for validation"), cleanupVideo(shouldRevokeUrl);
4798
- }, videoElement.src = videoSrc);
4799
- }, validateFileSize = (size) => MAX_FILE_SIZE === void 0 || size <= MAX_FILE_SIZE ? !0 : (setValidationError(
5353
+ fileSize && setVideoAssetMetadata((old) => ({ ...old, size: fileSize }));
5354
+ }, [fileSize, setVideoAssetMetadata]), useEffect(() => {
5355
+ const validateDuration = (duration) => MAX_DURATION_SECONDS && duration > MAX_DURATION_SECONDS ? (setValidationError(
5356
+ `Video duration (${formatSeconds(duration)}) exceeds maximum allowed duration of ${formatSeconds(MAX_DURATION_SECONDS)}`
5357
+ ), !1) : !0, validateFileSize = (size) => MAX_FILE_SIZE === void 0 || size <= MAX_FILE_SIZE ? !0 : (setValidationError(
4800
5358
  `File size (${formatBytes(size)}) exceeds maximum allowed size of ${formatBytes(MAX_FILE_SIZE)}`
4801
- ), !1);
4802
- if (stagedUpload.type === "file") {
4803
- const file = stagedUpload.files[0];
4804
- validateFileSize(file.size) && validateDuration(URL.createObjectURL(file), !0);
4805
- }
4806
- if (stagedUpload.type === "url") {
4807
- const url = stagedUpload.url;
4808
- (async () => {
4809
- setIsLoadingFileSize(!0);
4810
- try {
4811
- const contentLength = (await fetch(url, { method: "HEAD" })).headers.get("content-length"), fileSize = contentLength ? parseInt(contentLength, 10) : null;
4812
- setIsLoadingFileSize(!1), fileSize && setUrlFileSize(fileSize);
4813
- const shouldValidateDuration = MAX_FILE_SIZE === void 0 || fileSize === null || validateFileSize(fileSize);
4814
- fileSize === null && MAX_FILE_SIZE !== void 0 && setCanSkipFileSizeValidation(!0), shouldValidateDuration && validateDuration(url);
4815
- } catch {
4816
- setIsLoadingFileSize(!1), console.warn("Could not validate file size from URL"), setCanSkipFileSizeValidation(!0), validateDuration(url);
4817
- }
4818
- })();
4819
- }
4820
- return () => {
4821
- cleanupVideo(!0);
4822
- };
4823
- }, [stagedUpload, MAX_FILE_SIZE, MAX_DURATION_SECONDS]);
4824
- const toggleRendition = (rendition) => {
4825
- const current = config.static_renditions, hasRendition = current.includes(rendition);
4826
- dispatch(hasRendition ? {
4827
- action: "static_renditions",
4828
- value: current.filter((r) => r !== rendition)
4829
- } : {
4830
- action: "static_renditions",
4831
- value: [...current, rendition]
4832
- });
4833
- }, handleModeChange = (mode) => {
4834
- setRenditionMode(mode), dispatch(mode === "standard" ? {
4835
- action: "static_renditions",
4836
- value: config.static_renditions.filter((r) => r === "highest" || r === "audio-only")
4837
- } : {
4838
- action: "static_renditions",
4839
- value: config.static_renditions.filter((r) => r !== "highest")
4840
- });
4841
- }, { disableTextTrackConfig, disableUploadConfig } = pluginConfig, skipConfig = disableTextTrackConfig && disableUploadConfig;
5359
+ ), !1), validateDrmAvailability = (isAudioOnly) => config.drm_policy && isAudioOnly ? (setValidationError("Audio-only asset cannot be DRM protected"), !1) : !0;
5360
+ let valid = !0;
5361
+ 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);
5362
+ }, [
5363
+ MAX_FILE_SIZE,
5364
+ MAX_DURATION_SECONDS,
5365
+ canSkipFileSizeValidation,
5366
+ videoAssetMetadata?.duration,
5367
+ videoAssetMetadata?.size,
5368
+ videoAssetMetadata?.height,
5369
+ videoAssetMetadata?.width,
5370
+ videoAssetMetadata,
5371
+ config.drm_policy,
5372
+ validationError
5373
+ ]);
5374
+ const { disableTextTrackConfig, disableUploadConfig } = pluginConfig, skipConfig = disableTextTrackConfig && disableUploadConfig;
4842
5375
  if (useEffect(() => {
4843
- skipConfig && startUpload(formatUploadConfig(config));
5376
+ skipConfig && startUpload(formatUploadConfig(config, secrets));
4844
5377
  }, []), skipConfig) return null;
4845
- const basicConfig = config.video_quality !== "plus" && config.video_quality !== "premium", maxSupportedResolution = RESOLUTION_TIERS.findIndex(
5378
+ const basicConfig = config.video_quality !== "plus" && config.video_quality !== "premium", playbackPolicySelected = config.public_policy || config.signed_policy || config.drm_policy, maxSupportedResolution = RESOLUTION_TIERS.findIndex(
4846
5379
  (rt) => rt.value === pluginConfig.max_resolution_tier
4847
5380
  );
4848
5381
  return /* @__PURE__ */ jsx(
@@ -4876,12 +5409,12 @@ function UploadConfiguration({
4876
5409
  /* @__PURE__ */ jsx(DocumentVideoIcon, { fontSize: "2em" }),
4877
5410
  /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
4878
5411
  /* @__PURE__ */ jsx(Text, { textOverflow: "ellipsis", as: "h2", size: 3, children: stagedUpload.type === "file" ? stagedUpload.files[0].name : stagedUpload.url }),
4879
- /* @__PURE__ */ jsx(Text, { as: "p", size: 1, muted: !0, children: stagedUpload.type === "file" ? `Direct File Upload (${formatBytes(stagedUpload.files[0].size)})` : urlFileSize ? `File From URL (${formatBytes(urlFileSize)})` : isLoadingFileSize ? "File From URL (Loading size...)" : "File From URL (Unknown size)" }),
5412
+ /* @__PURE__ */ jsx(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)" }),
4880
5413
  stagedUpload.type === "file" && /* @__PURE__ */ jsxs(Stack, { space: 1, children: [
4881
- isLoadingDuration && /* @__PURE__ */ jsx(Text, { as: "p", size: 1, muted: !0, children: "Reading video metadata..." }),
4882
- videoDuration !== null && !validationError && /* @__PURE__ */ jsxs(Text, { as: "p", size: 1, muted: !0, children: [
5414
+ isLoadingMetadata && /* @__PURE__ */ jsx(Text, { as: "p", size: 1, muted: !0, children: "Reading video metadata..." }),
5415
+ videoAssetMetadata?.duration && !validationError && /* @__PURE__ */ jsxs(Text, { as: "p", size: 1, muted: !0, children: [
4883
5416
  "Duration: ",
4884
- formatSeconds(videoDuration)
5417
+ formatSeconds(videoAssetMetadata.duration)
4885
5418
  ] })
4886
5419
  ] })
4887
5420
  ] })
@@ -4927,160 +5460,37 @@ function UploadConfiguration({
4927
5460
  }) })
4928
5461
  }
4929
5462
  ),
4930
- !basicConfig && maxSupportedResolution > 0 && /* @__PURE__ */ jsx(
4931
- FormField$2,
4932
- {
4933
- title: "Resolution Tier",
4934
- description: /* @__PURE__ */ jsxs(Fragment, { children: [
4935
- "The maximum",
4936
- " ",
4937
- /* @__PURE__ */ jsx(
4938
- "a",
4939
- {
4940
- href: "https://docs.mux.com/api-reference#video/operation/create-direct-upload",
4941
- target: "_blank",
4942
- rel: "noopener noreferrer",
4943
- children: "resolution_tier"
4944
- }
4945
- ),
4946
- " ",
4947
- "your asset is encoded, stored, and streamed at."
4948
- ] }),
4949
- children: /* @__PURE__ */ jsx(Flex, { gap: 3, wrap: "wrap", children: RESOLUTION_TIERS.map(({ value, label }, index) => {
4950
- const inputId = `${id}--type-${value}`;
4951
- return index > maxSupportedResolution ? null : /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
4952
- /* @__PURE__ */ jsx(
4953
- Radio,
4954
- {
4955
- checked: config.max_resolution_tier === value,
4956
- name: "asset-resolutiontier",
4957
- onChange: (e) => dispatch({
4958
- action: "max_resolution_tier",
4959
- value: e.currentTarget.value
4960
- }),
4961
- value,
4962
- id: inputId
4963
- }
4964
- ),
4965
- /* @__PURE__ */ jsx(Text, { as: "label", htmlFor: inputId, children: label })
4966
- ] }, value);
4967
- }) })
4968
- }
4969
- ),
4970
5463
  !basicConfig && /* @__PURE__ */ jsx(FormField$2, { title: "Additional Configuration", children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
4971
5464
  /* @__PURE__ */ jsx(PlaybackPolicy, { id, config, secrets, dispatch }),
4972
- /* @__PURE__ */ jsx(Stack, { space: 3, children: /* @__PURE__ */ jsx(
4973
- FormField$2,
5465
+ maxSupportedResolution > 0 && /* @__PURE__ */ jsx(
5466
+ ResolutionTierSelector,
4974
5467
  {
4975
- title: "Static Renditions",
4976
- description: "Generate downloadable MP4 or M4A files. Note: Mux will not upscale to produce MP4 renditions - renditions that would cause upscaling are skipped.",
4977
- children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
4978
- /* @__PURE__ */ jsxs(Flex, { gap: 3, children: [
4979
- /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
4980
- /* @__PURE__ */ jsx(
4981
- Radio,
4982
- {
4983
- checked: renditionMode === "standard",
4984
- name: "rendition-mode",
4985
- onChange: () => handleModeChange("standard"),
4986
- value: "standard",
4987
- id: `${id}--mode-standard`
4988
- }
4989
- ),
4990
- /* @__PURE__ */ jsx(Text, { as: "label", htmlFor: `${id}--mode-standard`, children: "Standard" })
4991
- ] }),
4992
- /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
4993
- /* @__PURE__ */ jsx(
4994
- Radio,
4995
- {
4996
- checked: renditionMode === "advanced",
4997
- name: "rendition-mode",
4998
- onChange: () => handleModeChange("advanced"),
4999
- value: "advanced",
5000
- id: `${id}--mode-advanced`
5001
- }
5002
- ),
5003
- /* @__PURE__ */ jsx(Text, { as: "label", htmlFor: `${id}--mode-advanced`, children: "Advanced" })
5004
- ] })
5005
- ] }),
5006
- renditionMode === "standard" && /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
5007
- /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, padding: [0, 2], children: [
5008
- /* @__PURE__ */ jsx(
5009
- Checkbox,
5010
- {
5011
- id: `${id}--highest`,
5012
- style: { display: "block" },
5013
- checked: config.static_renditions.includes("highest"),
5014
- onChange: () => toggleRendition("highest")
5015
- }
5016
- ),
5017
- /* @__PURE__ */ jsx(Text, { as: "label", htmlFor: `${id}--highest`, children: "Highest Resolution (up to 4K)" })
5018
- ] }),
5019
- /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, padding: [0, 2], children: [
5020
- /* @__PURE__ */ jsx(
5021
- Checkbox,
5022
- {
5023
- id: `${id}--audio-only-standard`,
5024
- style: { display: "block" },
5025
- checked: config.static_renditions.includes("audio-only"),
5026
- onChange: () => toggleRendition("audio-only")
5027
- }
5028
- ),
5029
- /* @__PURE__ */ jsx(Text, { as: "label", htmlFor: `${id}--audio-only-standard`, children: "Audio Only (M4A)" })
5030
- ] })
5031
- ] }),
5032
- renditionMode === "advanced" && /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
5033
- /* @__PURE__ */ jsx(Label$1, { size: 1, muted: !0, children: "Select specific resolutions:" }),
5034
- /* @__PURE__ */ jsx(Flex, { gap: 2, wrap: "wrap", children: ADVANCED_RESOLUTIONS.map(({ value, label }) => {
5035
- const inputId = `${id}--resolution-${value}`;
5036
- return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
5037
- /* @__PURE__ */ jsx(
5038
- Checkbox,
5039
- {
5040
- id: inputId,
5041
- style: { display: "block" },
5042
- checked: config.static_renditions.includes(value),
5043
- onChange: () => toggleRendition(value)
5044
- }
5045
- ),
5046
- /* @__PURE__ */ jsx(Text, { as: "label", htmlFor: inputId, size: 1, children: label })
5047
- ] }, value);
5048
- }) }),
5049
- /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, padding: [2, 2, 0, 2], children: [
5050
- /* @__PURE__ */ jsx(
5051
- Checkbox,
5052
- {
5053
- id: `${id}--audio-only-advanced`,
5054
- style: { display: "block" },
5055
- checked: config.static_renditions.includes("audio-only"),
5056
- onChange: () => toggleRendition("audio-only")
5057
- }
5058
- ),
5059
- /* @__PURE__ */ jsx(Text, { as: "label", htmlFor: `${id}--audio-only-advanced`, children: "Audio Only (M4A)" })
5060
- ] })
5061
- ] })
5062
- ] })
5468
+ id,
5469
+ config,
5470
+ dispatch,
5471
+ maxSupportedResolution
5063
5472
  }
5064
- ) })
5473
+ ),
5474
+ /* @__PURE__ */ jsx(StaticRenditionSelector, { id, config, dispatch }),
5475
+ !disableTextTrackConfig && /* @__PURE__ */ jsx(
5476
+ TextTracksEditor,
5477
+ {
5478
+ tracks: config.text_tracks,
5479
+ dispatch,
5480
+ defaultLang: pluginConfig.defaultAutogeneratedSubtitleLang
5481
+ }
5482
+ )
5065
5483
  ] }) })
5066
5484
  ] }),
5067
- !disableTextTrackConfig && !basicConfig && /* @__PURE__ */ jsx(
5068
- TextTracksEditor,
5069
- {
5070
- tracks: config.text_tracks,
5071
- dispatch,
5072
- defaultLang: pluginConfig.defaultAutogeneratedSubtitleLang
5073
- }
5074
- ),
5075
5485
  /* @__PURE__ */ jsx(Box, { marginTop: 4, children: /* @__PURE__ */ jsx(
5076
5486
  Button,
5077
5487
  {
5078
- disabled: !basicConfig && !config.public_policy && !config.signed_policy || validationError !== null || isLoadingDuration || isLoadingFileSize && !canSkipFileSizeValidation,
5488
+ disabled: !basicConfig && !playbackPolicySelected || validationError !== null || isLoadingMetadata || isLoadingFileSize && !canSkipFileSizeValidation,
5079
5489
  icon: UploadIcon,
5080
5490
  text: "Upload",
5081
5491
  tone: "positive",
5082
5492
  onClick: () => {
5083
- validationError || startUpload(formatUploadConfig(config));
5493
+ validationError || startUpload(formatUploadConfig(config, secrets));
5084
5494
  }
5085
5495
  }
5086
5496
  ) })
@@ -5088,11 +5498,14 @@ function UploadConfiguration({
5088
5498
  }
5089
5499
  );
5090
5500
  }
5091
- function setPlaybackPolicy(config) {
5092
- const playback_policy = [];
5093
- return config.public_policy && playback_policy.push("public"), config.signed_policy && playback_policy.push("signed"), playback_policy;
5501
+ function setAdvancedPlaybackPolicy(config, secrets) {
5502
+ const advanced_playback_policies = [];
5503
+ 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({
5504
+ policy: "drm",
5505
+ drm_configuration_id: secrets.drmConfigId ?? void 0
5506
+ }) : console.error("Selected DRM Policy but missing DRM Configuration Id")), advanced_playback_policies;
5094
5507
  }
5095
- function formatUploadConfig(config) {
5508
+ function formatUploadConfig(config, secrets) {
5096
5509
  const generated_subtitles = config.text_tracks.filter(isAutogeneratedTrack).map((track) => ({
5097
5510
  name: track.name,
5098
5511
  language_code: track.language_code
@@ -5116,7 +5529,7 @@ function formatUploadConfig(config) {
5116
5529
  )
5117
5530
  ],
5118
5531
  static_renditions: config.static_renditions.length > 0 ? config.static_renditions.map((resolution) => ({ resolution })) : void 0,
5119
- playback_policy: setPlaybackPolicy(config),
5532
+ advanced_playback_policies: setAdvancedPlaybackPolicy(config, secrets),
5120
5533
  max_resolution_tier: config.max_resolution_tier,
5121
5534
  video_quality: config.video_quality,
5122
5535
  normalize_audio: config.normalize_audio
@@ -5328,8 +5741,13 @@ function Uploader(props) {
5328
5741
  case "reset":
5329
5742
  case "complete":
5330
5743
  return uploadRef.current?.unsubscribe(), uploadRef.current = null, uploadingDocumentId.current = null, INITIAL_STATE;
5331
- case "error":
5332
- return uploadRef.current?.unsubscribe(), uploadRef.current = null, uploadingDocumentId.current = null, Object.assign({}, INITIAL_STATE, { error: action.error });
5744
+ case "error": {
5745
+ uploadRef.current?.unsubscribe(), uploadRef.current = null, uploadingDocumentId.current = null;
5746
+ let error = action.error;
5747
+ return isServerError(action.error) && hasPlaybackPolicy(action.settings, "drm") && (error = new Error(
5748
+ "Unknown Error while uploading DRM protected content. Make sure your DRM configuration ID is valid and set correctly"
5749
+ )), Object.assign({}, INITIAL_STATE, { error });
5750
+ }
5333
5751
  default:
5334
5752
  return prev;
5335
5753
  }
@@ -5408,7 +5826,7 @@ function Uploader(props) {
5408
5826
  }
5409
5827
  },
5410
5828
  complete: () => dispatch({ action: "complete" }),
5411
- error: (error) => dispatch({ action: "error", error })
5829
+ error: (error) => dispatch({ action: "error", error, settings })
5412
5830
  });
5413
5831
  }, invalidFileToast = useCallback(() => {
5414
5832
  toast.push({
@@ -5459,11 +5877,11 @@ function Uploader(props) {
5459
5877
  idx > -1 && dragEnteredEls.current.splice(idx, 1), dragEnteredEls.current.length === 0 && setDragState(null);
5460
5878
  };
5461
5879
  if (state.error !== null) {
5462
- const error = {};
5880
+ const error = state.error;
5463
5881
  return /* @__PURE__ */ jsxs(Flex, { gap: 3, direction: "column", justify: "center", align: "center", children: [
5464
5882
  /* @__PURE__ */ jsx(Text, { size: 5, muted: !0, children: /* @__PURE__ */ jsx(ErrorOutlineIcon, {}) }),
5465
5883
  /* @__PURE__ */ jsx(Text, { children: "Something went wrong" }),
5466
- error instanceof Error && error.message && /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: error.message }),
5884
+ error instanceof Error && error.message && /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, weight: "semibold", style: { textAlign: "center" }, children: error.message }),
5467
5885
  /* @__PURE__ */ jsx(Button, { text: "Upload another file", onClick: () => dispatch({ action: "reset" }) })
5468
5886
  ] });
5469
5887
  }
@@ -5548,6 +5966,7 @@ function Uploader(props) {
5548
5966
  props.dialogState === "select-video" && /* @__PURE__ */ jsx(
5549
5967
  InputBrowser,
5550
5968
  {
5969
+ config: props.config,
5551
5970
  asset: props.asset,
5552
5971
  onChange: props.onChange,
5553
5972
  setDialogState: props.setDialogState
@@ -5794,6 +6213,7 @@ const muxVideoSchema = {
5794
6213
  normalize_audio: !1,
5795
6214
  defaultPublic: !0,
5796
6215
  defaultSigned: !1,
6216
+ defaultDrm: !1,
5797
6217
  tool: DEFAULT_TOOL_CONFIG,
5798
6218
  allowedRolesForConfiguration: [],
5799
6219
  acceptedMimeTypes: ["video/*", "audio/*"]