sanity-plugin-mux-input 2.12.1 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1483,10 +1483,21 @@ function EditThumbnailDialog({ asset, currentTime = 0 }) {
1483
1483
  }
1484
1484
  );
1485
1485
  }
1486
+ function AudioIcon(props) {
1487
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "1em", height: "1em", viewBox: "0 0 24 24", ...props, children: /* @__PURE__ */ jsxRuntime.jsx(
1488
+ "path",
1489
+ {
1490
+ fill: "currentColor",
1491
+ style: { opacity: "0.65" },
1492
+ d: "M10.75 19q.95 0 1.6-.65t.65-1.6V13h3v-2h-4v3.875q-.275-.2-.587-.288t-.663-.087q-.95 0-1.6.65t-.65 1.6t.65 1.6t1.6.65M6 22q-.825 0-1.412-.587T4 20V4q0-.825.588-1.412T6 2h8l6 6v12q0 .825-.587 1.413T18 22zm7-13V4H6v16h12V9zM6 4v5zv16z"
1493
+ }
1494
+ ) });
1495
+ }
1486
1496
  function VideoPlayer({
1487
1497
  asset,
1488
1498
  thumbnailWidth = 250,
1489
1499
  children,
1500
+ hlsConfig,
1490
1501
  ...props
1491
1502
  }) {
1492
1503
  const client = useClient(), { dialogState } = useDialogStateContext(), isAudio = assetIsAudio(asset), muxPlayer = React.useRef(null), {
@@ -1512,52 +1523,79 @@ function VideoPlayer({
1512
1523
  // Make it wider when forcing aspect ratio to balance with videos' rendering height (audio players overflow a bit)
1513
1524
  props.forceAspectRatio * 1.2
1514
1525
  ) : AUDIO_ASPECT_RATIO), /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1515
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Card, { tone: "transparent", style: { aspectRatio, position: "relative" }, children: [
1516
- videoSrc && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1517
- /* @__PURE__ */ jsxRuntime.jsx(
1518
- MuxPlayer__default.default,
1519
- {
1520
- poster: thumbnailSrc,
1521
- ref: muxPlayer,
1522
- ...props,
1523
- playsInline: !0,
1524
- playbackId: asset.playbackId,
1525
- tokens: signedToken ? { playback: signedToken, thumbnail: signedToken, storyboard: signedToken } : void 0,
1526
- preload: "metadata",
1527
- crossOrigin: "anonymous",
1528
- metadata: {
1529
- player_name: "Sanity Admin Dashboard",
1530
- player_version: "2.12.1",
1531
- page_type: "Preview Player"
1532
- },
1533
- audio: isAudio,
1534
- style: {
1535
- height: "100%",
1536
- width: "100%",
1537
- display: "block",
1538
- objectFit: "contain"
1526
+ /* @__PURE__ */ jsxRuntime.jsxs(
1527
+ ui.Card,
1528
+ {
1529
+ tone: "transparent",
1530
+ style: {
1531
+ aspectRatio,
1532
+ position: "relative",
1533
+ ...isAudio && { display: "flex", alignItems: "flex-end" }
1534
+ },
1535
+ children: [
1536
+ videoSrc && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1537
+ isAudio && /* @__PURE__ */ jsxRuntime.jsx(
1538
+ AudioIcon,
1539
+ {
1540
+ style: {
1541
+ padding: "0.5em",
1542
+ width: "2.2em",
1543
+ height: "2.2em",
1544
+ position: "absolute",
1545
+ top: 0,
1546
+ left: 0,
1547
+ zIndex: 1
1548
+ }
1549
+ }
1550
+ ),
1551
+ /* @__PURE__ */ jsxRuntime.jsx(
1552
+ MuxPlayer__default.default,
1553
+ {
1554
+ poster: isAudio ? void 0 : thumbnailSrc,
1555
+ ref: muxPlayer,
1556
+ ...props,
1557
+ playsInline: !0,
1558
+ playbackId: asset.playbackId,
1559
+ tokens: signedToken ? { playback: signedToken, thumbnail: signedToken, storyboard: signedToken } : void 0,
1560
+ preload: "metadata",
1561
+ crossOrigin: "anonymous",
1562
+ metadata: {
1563
+ player_name: "Sanity Admin Dashboard",
1564
+ player_version: "2.13.0",
1565
+ page_type: "Preview Player"
1566
+ },
1567
+ audio: isAudio,
1568
+ _hlsConfig: hlsConfig,
1569
+ style: {
1570
+ ...!isAudio && { height: "100%" },
1571
+ width: "100%",
1572
+ display: "block",
1573
+ objectFit: "contain",
1574
+ ...isAudio && { alignSelf: "end" }
1575
+ }
1576
+ }
1577
+ ),
1578
+ children
1579
+ ] }),
1580
+ error ? /* @__PURE__ */ jsxRuntime.jsx(
1581
+ "div",
1582
+ {
1583
+ style: {
1584
+ position: "absolute",
1585
+ top: "50%",
1586
+ left: "50%",
1587
+ transform: "translate(-50%, -50%)"
1588
+ },
1589
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { muted: !0, children: [
1590
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ErrorOutlineIcon, { style: { marginRight: "0.15em" } }),
1591
+ typeof error == "object" && "message" in error && typeof error.message == "string" ? error.message : "Error loading video"
1592
+ ] })
1539
1593
  }
1540
- }
1541
- ),
1542
- children
1543
- ] }),
1544
- error ? /* @__PURE__ */ jsxRuntime.jsx(
1545
- "div",
1546
- {
1547
- style: {
1548
- position: "absolute",
1549
- top: "50%",
1550
- left: "50%",
1551
- transform: "translate(-50%, -50%)"
1552
- },
1553
- children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { muted: !0, children: [
1554
- /* @__PURE__ */ jsxRuntime.jsx(icons.ErrorOutlineIcon, { style: { marginRight: "0.15em" } }),
1555
- typeof error == "object" && "message" in error && typeof error.message == "string" ? error.message : "Error loading video"
1556
- ] })
1557
- }
1558
- ) : null,
1559
- children
1560
- ] }),
1594
+ ) : null,
1595
+ children
1596
+ ]
1597
+ }
1598
+ ),
1561
1599
  dialogState === "edit-thumbnail" && /* @__PURE__ */ jsxRuntime.jsx(EditThumbnailDialog, { asset, currentTime: muxPlayer?.current?.currentTime })
1562
1600
  ] });
1563
1601
  }
@@ -2300,14 +2338,7 @@ function VideoInBrowser({
2300
2338
  alignItems: "center",
2301
2339
  justifyContent: "center"
2302
2340
  },
2303
- children: /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "3em", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx(
2304
- "path",
2305
- {
2306
- fill: "currentColor",
2307
- style: { opacity: "0.65" },
2308
- d: "M10.75 19q.95 0 1.6-.65t.65-1.6V13h3v-2h-4v3.875q-.275-.2-.587-.288t-.663-.087q-.95 0-1.6.65t-.65 1.6t.65 1.6t1.6.65M6 22q-.825 0-1.412-.587T4 20V4q0-.825.588-1.412T6 2h8l6 6v12q0 .825-.587 1.413T18 22zm7-13V4H6v16h12V9zM6 4v5zv16z"
2309
- }
2310
- ) })
2341
+ children: /* @__PURE__ */ jsxRuntime.jsx(AudioIcon, { width: "3em", height: "3em" })
2311
2342
  }
2312
2343
  ) : /* @__PURE__ */ jsxRuntime.jsx(VideoThumbnail, { asset })
2313
2344
  ] }),
@@ -2932,7 +2963,7 @@ const TopControls = styledComponents.styled.div`
2932
2963
  }
2933
2964
  ) : null
2934
2965
  ] }) });
2935
- }, Player = ({ asset, buttons, readOnly, onChange }) => {
2966
+ }, Player = ({ asset, buttons, readOnly, onChange, config }) => {
2936
2967
  const isLoading = React.useMemo(() => asset?.status === "preparing" ? "Preparing the video" : asset?.status === "waiting_for_upload" ? "Waiting for upload to start" : asset?.status === "waiting" ? "Processing upload" : !(asset?.status === "ready" || typeof asset?.status > "u"), [asset]), isPreparingStaticRenditions = React.useMemo(() => {
2937
2968
  if (asset?.data?.static_renditions?.status && asset?.data?.static_renditions?.status !== "disabled")
2938
2969
  return !1;
@@ -2953,7 +2984,7 @@ const TopControls = styledComponents.styled.div`
2953
2984
  text: isLoading !== !0 && isLoading || "Waiting for Mux to complete the upload",
2954
2985
  onCancel: readOnly ? void 0 : () => handleCancelUpload()
2955
2986
  }
2956
- ) : /* @__PURE__ */ jsxRuntime.jsxs(VideoPlayer, { asset, children: [
2987
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(VideoPlayer, { asset, hlsConfig: config?.hlsConfig, children: [
2957
2988
  buttons && /* @__PURE__ */ jsxRuntime.jsx(TopControls, { slot: "top-chrome", children: buttons }),
2958
2989
  isPreparingStaticRenditions && /* @__PURE__ */ jsxRuntime.jsx(
2959
2990
  ui.Card,
@@ -3065,7 +3096,7 @@ const FileButton = styledComponents.styled(ui.MenuItem)(({ theme }) => {
3065
3096
  color: white;
3066
3097
  `, isVideoAsset = (asset) => asset._type === "mux.videoAsset";
3067
3098
  function PlayerActionsMenu(props) {
3068
- const { asset, readOnly, dialogState, setDialogState, onChange, onSelect } = 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]);
3099
+ 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]);
3069
3100
  return React.useEffect(() => {
3070
3101
  open && dialogState && setOpen(!1);
3071
3102
  }, [dialogState, open]), ui.useClickOutsideEvent(
@@ -3091,7 +3122,7 @@ function PlayerActionsMenu(props) {
3091
3122
  /* @__PURE__ */ jsxRuntime.jsx(
3092
3123
  FileInputMenuItem,
3093
3124
  {
3094
- accept: "video/*",
3125
+ accept,
3095
3126
  icon: icons.UploadIcon,
3096
3127
  onSelect,
3097
3128
  text: "Upload",
@@ -3455,7 +3486,47 @@ function UploadConfiguration({
3455
3486
  (r) => r !== "highest" && r !== "audio-only"
3456
3487
  ).length > 0, [config.static_renditions]), [renditionMode, setRenditionMode] = React.useState(
3457
3488
  isAdvancedMode ? "advanced" : "standard"
3458
- ), toggleRendition = (rendition) => {
3489
+ ), [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;
3490
+ React.useEffect(() => {
3491
+ setVideoDuration(null), setUrlFileSize(null), setIsLoadingDuration(!1), setIsLoadingFileSize(!1), setValidationError(null), setCanSkipFileSizeValidation(!1);
3492
+ let videoElement = null, currentVideoSrc = null;
3493
+ const cleanupVideo = (shouldRevokeUrl) => {
3494
+ videoElement && (videoElement.onloadedmetadata = null, videoElement.onerror = null, videoElement.src = "", videoElement.load(), videoElement = null), shouldRevokeUrl && currentVideoSrc?.startsWith("blob:") && URL.revokeObjectURL(currentVideoSrc), currentVideoSrc = null;
3495
+ }, validateDuration = (videoSrc, shouldRevokeUrl = !1) => {
3496
+ !MAX_DURATION_SECONDS || MAX_DURATION_SECONDS <= 0 || (setIsLoadingDuration(!0), videoElement = document.createElement("video"), videoElement.preload = "metadata", currentVideoSrc = videoSrc, videoElement.onloadedmetadata = () => {
3497
+ const duration = videoElement.duration;
3498
+ setVideoDuration(duration), setIsLoadingDuration(!1), duration > MAX_DURATION_SECONDS && setValidationError(
3499
+ `Video duration (${formatSeconds(duration)}) exceeds maximum allowed duration of ${formatSeconds(MAX_DURATION_SECONDS)}`
3500
+ ), cleanupVideo(shouldRevokeUrl);
3501
+ }, videoElement.onerror = () => {
3502
+ setIsLoadingDuration(!1), console.warn("Could not read video metadata for validation"), cleanupVideo(shouldRevokeUrl);
3503
+ }, videoElement.src = videoSrc);
3504
+ }, validateFileSize = (size) => MAX_FILE_SIZE === void 0 || size <= MAX_FILE_SIZE ? !0 : (setValidationError(
3505
+ `File size (${formatBytes(size)}) exceeds maximum allowed size of ${formatBytes(MAX_FILE_SIZE)}`
3506
+ ), !1);
3507
+ if (stagedUpload.type === "file") {
3508
+ const file = stagedUpload.files[0];
3509
+ validateFileSize(file.size) && validateDuration(URL.createObjectURL(file), !0);
3510
+ }
3511
+ if (stagedUpload.type === "url") {
3512
+ const url = stagedUpload.url;
3513
+ (async () => {
3514
+ setIsLoadingFileSize(!0);
3515
+ try {
3516
+ const contentLength = (await fetch(url, { method: "HEAD" })).headers.get("content-length"), fileSize = contentLength ? parseInt(contentLength, 10) : null;
3517
+ setIsLoadingFileSize(!1), fileSize && setUrlFileSize(fileSize);
3518
+ const shouldValidateDuration = MAX_FILE_SIZE === void 0 || fileSize === null || validateFileSize(fileSize);
3519
+ fileSize === null && MAX_FILE_SIZE !== void 0 && setCanSkipFileSizeValidation(!0), shouldValidateDuration && validateDuration(url);
3520
+ } catch {
3521
+ setIsLoadingFileSize(!1), console.warn("Could not validate file size from URL"), setCanSkipFileSizeValidation(!0), validateDuration(url);
3522
+ }
3523
+ })();
3524
+ }
3525
+ return () => {
3526
+ cleanupVideo(!0);
3527
+ };
3528
+ }, [stagedUpload, MAX_FILE_SIZE, MAX_DURATION_SECONDS]);
3529
+ const toggleRendition = (rendition) => {
3459
3530
  const current = config.static_renditions, hasRendition = current.includes(rendition);
3460
3531
  dispatch(hasRendition ? {
3461
3532
  action: "static_renditions",
@@ -3490,6 +3561,13 @@ function UploadConfiguration({
3490
3561
  header: "Configure Mux Upload",
3491
3562
  onClose,
3492
3563
  children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { padding: 4, space: 2, children: [
3564
+ validationError && /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { padding: 3, tone: "critical", radius: 2, marginBottom: 2, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gap: 2, align: "flex-start", children: [
3565
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ErrorOutlineIcon, { width: 20, height: 20 }),
3566
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, children: [
3567
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, weight: "semibold", children: "Validation Error" }),
3568
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, children: validationError })
3569
+ ] })
3570
+ ] }) }),
3493
3571
  /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: 3, children: "FILE TO UPLOAD" }),
3494
3572
  /* @__PURE__ */ jsxRuntime.jsx(
3495
3573
  ui.Card,
@@ -3503,7 +3581,14 @@ function UploadConfiguration({
3503
3581
  /* @__PURE__ */ jsxRuntime.jsx(icons.DocumentVideoIcon, { fontSize: "2em" }),
3504
3582
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, children: [
3505
3583
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { textOverflow: "ellipsis", as: "h2", size: 3, children: stagedUpload.type === "file" ? stagedUpload.files[0].name : stagedUpload.url }),
3506
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "p", size: 1, muted: !0, children: stagedUpload.type === "file" ? `Direct File Upload (${formatBytes(stagedUpload.files[0].size)})` : "File From URL (Unknown size)" })
3584
+ /* @__PURE__ */ jsxRuntime.jsx(ui.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)" }),
3585
+ stagedUpload.type === "file" && /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 1, children: [
3586
+ isLoadingDuration && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "p", size: 1, muted: !0, children: "Reading video metadata..." }),
3587
+ videoDuration !== null && !validationError && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { as: "p", size: 1, muted: !0, children: [
3588
+ "Duration: ",
3589
+ formatSeconds(videoDuration)
3590
+ ] })
3591
+ ] })
3507
3592
  ] })
3508
3593
  ] })
3509
3594
  }
@@ -3695,11 +3780,13 @@ function UploadConfiguration({
3695
3780
  /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { marginTop: 4, children: /* @__PURE__ */ jsxRuntime.jsx(
3696
3781
  ui.Button,
3697
3782
  {
3698
- disabled: !basicConfig && !config.public_policy && !config.signed_policy,
3783
+ disabled: !basicConfig && !config.public_policy && !config.signed_policy || validationError !== null || isLoadingDuration || isLoadingFileSize && !canSkipFileSizeValidation,
3699
3784
  icon: icons.UploadIcon,
3700
3785
  text: "Upload",
3701
3786
  tone: "positive",
3702
- onClick: () => startUpload(formatUploadConfig(config))
3787
+ onClick: () => {
3788
+ validationError || startUpload(formatUploadConfig(config));
3789
+ }
3703
3790
  }
3704
3791
  ) })
3705
3792
  ] })
@@ -3822,7 +3909,7 @@ const ctrlKey = 17, cmdKey = 91, UploadCardWithFocusRing = withFocusRing(ui.Card
3822
3909
  /* @__PURE__ */ jsxRuntime.jsx(
3823
3910
  HiddenInput,
3824
3911
  {
3825
- accept: accept || "video/*",
3912
+ accept,
3826
3913
  ref: inputRef,
3827
3914
  tabIndex: 0,
3828
3915
  type: "file",
@@ -3843,8 +3930,11 @@ const ctrlKey = 17, cmdKey = 91, UploadCardWithFocusRing = withFocusRing(ui.Card
3843
3930
  )
3844
3931
  ] });
3845
3932
  };
3933
+ function formatAcceptString(accept) {
3934
+ return accept.split(",").map((type) => type.trim().replace("/*", "")).join(" or ");
3935
+ }
3846
3936
  function UploadPlaceholder(props) {
3847
- const { setDialogState, readOnly, onSelect, hovering, needsSetup } = props, handleBrowse = React.useCallback(() => setDialogState("select-video"), [setDialogState]), handleConfigureApi = React.useCallback(() => setDialogState("secrets"), [setDialogState]), { hasConfigAccess } = useAccessControl(props.config);
3937
+ const { setDialogState, readOnly, onSelect, hovering, needsSetup, accept } = props, handleBrowse = React.useCallback(() => setDialogState("select-video"), [setDialogState]), handleConfigureApi = React.useCallback(() => setDialogState("secrets"), [setDialogState]), { hasConfigAccess } = useAccessControl(props.config);
3848
3938
  return /* @__PURE__ */ jsxRuntime.jsx(
3849
3939
  ui.Card,
3850
3940
  {
@@ -3867,12 +3957,17 @@ function UploadPlaceholder(props) {
3867
3957
  children: [
3868
3958
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", justify: "flex-start", gap: 2, flex: 1, children: [
3869
3959
  /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { justify: "center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { muted: !0, children: /* @__PURE__ */ jsxRuntime.jsx(icons.DocumentVideoIcon, {}) }) }),
3870
- /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { justify: "center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, muted: !0, children: "Drag video or paste URL here" }) })
3960
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { justify: "center", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: 1, muted: !0, children: [
3961
+ "Drag ",
3962
+ formatAcceptString(accept),
3963
+ " file or paste URL here"
3964
+ ] }) })
3871
3965
  ] }),
3872
3966
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Inline, { space: 2, children: [
3873
3967
  /* @__PURE__ */ jsxRuntime.jsx(
3874
3968
  FileInputButton,
3875
3969
  {
3970
+ accept,
3876
3971
  mode: "bleed",
3877
3972
  tone: "default",
3878
3973
  icon: icons.UploadIcon,
@@ -4023,21 +4118,33 @@ function Uploader(props) {
4023
4118
  complete: () => dispatch({ action: "complete" }),
4024
4119
  error: (error) => dispatch({ action: "error", error })
4025
4120
  });
4026
- }, handleUpload = (files) => {
4027
- dispatch({
4121
+ }, invalidFileToast = React.useCallback(() => {
4122
+ toast.push({
4123
+ status: "error",
4124
+ title: `Invalid file type. Accepted types: ${props.config.acceptedMimeTypes?.join(", ")}`
4125
+ });
4126
+ }, [props.config.acceptedMimeTypes, toast]), isInvalidFile = (files) => Array.from(files).some((file) => !props.config.acceptedMimeTypes?.some((acceptedType) => {
4127
+ const pattern = `^${acceptedType.replace("*", ".*")}$`;
4128
+ return new RegExp(pattern).test(file.type);
4129
+ })), handleUpload = (files) => {
4130
+ isInvalidFile(files) || dispatch({
4028
4131
  action: "stageUpload",
4029
4132
  input: { type: "file", files }
4030
4133
  });
4031
4134
  }, handlePaste = (event) => {
4032
4135
  event.preventDefault(), event.stopPropagation();
4033
- const url = (event.clipboardData || window.clipboardData).getData("text")?.trim();
4136
+ const url = (event.clipboardData || window.clipboardData)?.getData("text")?.trim();
4034
4137
  if (!isValidUrl(url)) {
4035
4138
  toast.push({ status: "error", title: "Invalid URL for Mux video input." });
4036
4139
  return;
4037
4140
  }
4038
4141
  dispatch({ action: "stageUpload", input: { type: "url", url } });
4039
4142
  }, handleDrop = (event) => {
4040
- setDragState(null), event.preventDefault(), event.stopPropagation(), extractDroppedFiles(event.nativeEvent.dataTransfer).then((files) => {
4143
+ if (event.preventDefault(), event.stopPropagation(), dragState === "invalid") {
4144
+ invalidFileToast(), setDragState(null);
4145
+ return;
4146
+ }
4147
+ setDragState(null), extractDroppedFiles(event.nativeEvent.dataTransfer).then((files) => {
4041
4148
  dispatch({
4042
4149
  action: "stageUpload",
4043
4150
  input: { type: "file", files }
@@ -4047,8 +4154,11 @@ function Uploader(props) {
4047
4154
  event.preventDefault(), event.stopPropagation();
4048
4155
  }, handleDragEnter = (event) => {
4049
4156
  event.stopPropagation(), dragEnteredEls.current.push(event.target);
4050
- const type = event.dataTransfer.items?.[0]?.type;
4051
- setDragState(type?.startsWith("video/") ? "valid" : "invalid");
4157
+ const type = event.dataTransfer.items?.[0]?.type, isValidType = props.config.acceptedMimeTypes?.some((acceptedType) => {
4158
+ const pattern = `^${acceptedType.replace("*", ".*")}$`;
4159
+ return new RegExp(pattern).test(type);
4160
+ });
4161
+ setDragState(isValidType ? "valid" : "invalid");
4052
4162
  }, handleDragLeave = (event) => {
4053
4163
  event.stopPropagation();
4054
4164
  const idx = dragEnteredEls.current.indexOf(event.target);
@@ -4086,7 +4196,9 @@ function Uploader(props) {
4086
4196
  }
4087
4197
  );
4088
4198
  let tone;
4089
- return dragState && (tone = dragState === "valid" ? "positive" : "critical"), /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4199
+ dragState && (tone = dragState === "valid" ? "positive" : "critical");
4200
+ const acceptMimeString = props.config?.acceptedMimeTypes?.length ? props.config.acceptedMimeTypes.join(",") : "video/*, audio/*";
4201
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4090
4202
  /* @__PURE__ */ jsxRuntime.jsx(
4091
4203
  UploadCard,
4092
4204
  {
@@ -4108,9 +4220,11 @@ function Uploader(props) {
4108
4220
  readOnly: props.readOnly,
4109
4221
  asset: props.asset,
4110
4222
  onChange: props.onChange,
4223
+ config: props.config,
4111
4224
  buttons: /* @__PURE__ */ jsxRuntime.jsx(
4112
4225
  PlayerActionsMenu$1,
4113
4226
  {
4227
+ accept: acceptMimeString,
4114
4228
  asset: props.asset,
4115
4229
  dialogState: props.dialogState,
4116
4230
  setDialogState: props.setDialogState,
@@ -4126,6 +4240,7 @@ function Uploader(props) {
4126
4240
  ) : /* @__PURE__ */ jsxRuntime.jsx(
4127
4241
  UploadPlaceholder,
4128
4242
  {
4243
+ accept: acceptMimeString,
4129
4244
  hovering: dragState !== null,
4130
4245
  onSelect: handleUpload,
4131
4246
  readOnly: !!props.readOnly,
@@ -4386,7 +4501,8 @@ const muxVideoSchema = {
4386
4501
  defaultPublic: !0,
4387
4502
  defaultSigned: !1,
4388
4503
  tool: DEFAULT_TOOL_CONFIG,
4389
- allowedRolesForConfiguration: []
4504
+ allowedRolesForConfiguration: [],
4505
+ acceptedMimeTypes: ["video/*", "audio/*"]
4390
4506
  };
4391
4507
  function convertLegacyConfig(config) {
4392
4508
  return config.static_renditions && config.static_renditions.length > 0 ? { static_renditions: config.static_renditions } : config.mp4_support === "standard" ? { static_renditions: ["highest"] } : { static_renditions: [] };