sanity-plugin-mux-input 2.4.0 → 2.5.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/LICENSE +1 -1
- package/README.md +57 -55
- package/dist/index.js +223 -88
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +227 -92
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -17
- package/src/components/EditThumbnailDialog.tsx +122 -0
- package/src/components/PlayerActionsMenu.tsx +13 -0
- package/src/components/Uploader.tsx +21 -15
- package/src/components/VideoPlayer.tsx +66 -49
- package/src/components/VideoThumbnail.tsx +15 -8
- package/src/context/DialogStateContext.tsx +36 -0
- package/src/hooks/useAssets.ts +29 -23
- package/src/util/createUrlParamsObject.ts +25 -0
- package/src/util/formatSeconds.ts +28 -1
- package/src/util/getAnimatedPosterSrc.ts +5 -13
- package/src/util/getPosterSrc.ts +10 -15
- package/src/util/getVideoMetadata.ts +1 -1
- package/src/util/types.ts +4 -0
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { useClient as useClient$1,
|
|
1
|
+
import { useClient as useClient$1, useDocumentStore, collate, useDocumentValues, createHookFromObservableFactory, truncateString, useFormattedDuration, SanityDefaultPreview, useTimeAgo, TextWithTone, isRecord, getPreviewStateObservable, getPreviewValueWithFallback, DocumentPreviewPresence, useDocumentPreviewStore, useSchema, useDocumentPresence, PreviewCard, 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
|
-
import { ErrorOutlineIcon,
|
|
4
|
-
import { Card, Box, Spinner, Stack, Text, Checkbox, Button, Dialog, Flex, Heading, Code, MenuButton, Menu, MenuItem, Tooltip, Inline, useToast, TabList, Tab, TabPanel,
|
|
5
|
-
import React, { useState, useMemo, useEffect, useRef, useId, memo, isValidElement, useCallback, useReducer, PureComponent, createElement, forwardRef, Suspense } from "react";
|
|
3
|
+
import { ErrorOutlineIcon, RetryIcon, CheckmarkCircleIcon, RetrieveIcon, SortIcon, WarningOutlineIcon, EditIcon, PublishIcon, DocumentIcon, TrashIcon, RevertIcon, SearchIcon, ClockIcon, CropIcon, CalendarIcon, TagIcon, CheckmarkIcon, LockIcon, PlayIcon, PlugIcon, EllipsisHorizontalIcon, UploadIcon, ImageIcon, ResetIcon, TranslateIcon, DocumentVideoIcon } from "@sanity/icons";
|
|
4
|
+
import { Card, Box, Spinner, Stack, Text, Checkbox, Button, Dialog, Flex, Heading, Code, MenuButton, Menu, MenuItem, TextInput, Tooltip, Inline, useToast, TabList, Tab, TabPanel, Label as Label$1, Grid, useClickOutsideEvent, Popover, MenuDivider, Autocomplete, Radio, rem } from "@sanity/ui";
|
|
5
|
+
import React, { useState, useMemo, useEffect, useRef, useId, memo, createContext, useContext, isValidElement, useCallback, useReducer, PureComponent, createElement, forwardRef, Suspense } from "react";
|
|
6
|
+
import { useObservable } from "react-rx";
|
|
6
7
|
import compact from "lodash/compact.js";
|
|
7
8
|
import toLower from "lodash/toLower.js";
|
|
8
9
|
import trim from "lodash/trim.js";
|
|
@@ -18,7 +19,6 @@ import { usePaneRouter } from "sanity/desk";
|
|
|
18
19
|
import { IntentLink } from "sanity/router";
|
|
19
20
|
import isNumber from "lodash/isNumber.js";
|
|
20
21
|
import isString from "lodash/isString.js";
|
|
21
|
-
import { useObservable } from "react-rx";
|
|
22
22
|
import useSWR from "swr";
|
|
23
23
|
import scrollIntoView from "scroll-into-view-if-needed";
|
|
24
24
|
import { UpChunk } from "@mux/upchunk";
|
|
@@ -71,31 +71,38 @@ const ASSET_SORT_OPTIONS = {
|
|
|
71
71
|
createdAsc: { groq: "_createdAt asc", label: "First created (oldest)" },
|
|
72
72
|
filenameAsc: { groq: "filename asc", label: "By filename (A-Z)" },
|
|
73
73
|
filenameDesc: { groq: "filename desc", label: "By filename (Z-A)" }
|
|
74
|
-
}, useAssetDocuments =
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
74
|
+
}, useAssetDocuments = ({
|
|
75
|
+
documentStore,
|
|
76
|
+
sort,
|
|
77
|
+
searchQuery
|
|
78
|
+
}) => {
|
|
79
|
+
const memoizedObservable = useMemo(() => {
|
|
80
|
+
const search = createSearchFilter(searchQuery), filter = ['_type == "mux.videoAsset"', ...search.filter].filter(Boolean).join(" && "), sortFragment = ASSET_SORT_OPTIONS[sort].groq;
|
|
81
|
+
return documentStore.listenQuery(
|
|
82
|
+
/* groq */
|
|
83
|
+
`*[${filter}] | order(${sortFragment})`,
|
|
84
|
+
search.params,
|
|
85
|
+
{
|
|
86
|
+
apiVersion: SANITY_API_VERSION
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
}, [documentStore, sort, searchQuery]);
|
|
90
|
+
return useObservable(memoizedObservable, void 0);
|
|
91
|
+
};
|
|
85
92
|
function useAssets() {
|
|
86
|
-
const documentStore = useDocumentStore(), [sort, setSort] = useState("createdDesc"), [searchQuery, setSearchQuery] = useState(""),
|
|
93
|
+
const documentStore = useDocumentStore(), [sort, setSort] = useState("createdDesc"), [searchQuery, setSearchQuery] = useState(""), assetDocumentsObservable = useAssetDocuments({ documentStore, sort, searchQuery }), isLoading = assetDocumentsObservable === void 0;
|
|
87
94
|
return {
|
|
88
95
|
assets: useMemo(
|
|
89
96
|
() => (
|
|
90
97
|
// Avoid displaying both drafts & published assets by collating them together and giving preference to drafts
|
|
91
|
-
collate(
|
|
98
|
+
collate(assetDocumentsObservable ?? []).map(
|
|
92
99
|
(collated) => ({
|
|
93
100
|
...collated.draft || collated.published || {},
|
|
94
101
|
_id: collated.id
|
|
95
102
|
})
|
|
96
103
|
)
|
|
97
104
|
),
|
|
98
|
-
[
|
|
105
|
+
[assetDocumentsObservable]
|
|
99
106
|
),
|
|
100
107
|
isLoading,
|
|
101
108
|
sort,
|
|
@@ -343,6 +350,17 @@ function getPlaybackId(asset) {
|
|
|
343
350
|
function getPlaybackPolicy(asset) {
|
|
344
351
|
return asset.data?.playback_ids?.find((playbackId) => asset.playbackId === playbackId.id)?.policy ?? "public";
|
|
345
352
|
}
|
|
353
|
+
function createUrlParamsObject(client, asset, params, audience) {
|
|
354
|
+
const playbackId = getPlaybackId(asset);
|
|
355
|
+
let searchParams = new URLSearchParams(
|
|
356
|
+
JSON.parse(JSON.stringify(params, (_, v) => v ?? void 0))
|
|
357
|
+
);
|
|
358
|
+
if (getPlaybackPolicy(asset) === "signed") {
|
|
359
|
+
const token = generateJwt(client, playbackId, audience, params);
|
|
360
|
+
searchParams = new URLSearchParams({ token });
|
|
361
|
+
}
|
|
362
|
+
return { playbackId, searchParams };
|
|
363
|
+
}
|
|
346
364
|
function getAnimatedPosterSrc({
|
|
347
365
|
asset,
|
|
348
366
|
client,
|
|
@@ -352,16 +370,22 @@ function getAnimatedPosterSrc({
|
|
|
352
370
|
end = start + 5,
|
|
353
371
|
fps = 15
|
|
354
372
|
}) {
|
|
355
|
-
const params = { height, width, start, end, fps }, playbackId =
|
|
356
|
-
let searchParams = new URLSearchParams(
|
|
357
|
-
JSON.parse(JSON.stringify(params, (_, v) => v ?? void 0))
|
|
358
|
-
);
|
|
359
|
-
if (getPlaybackPolicy(asset) === "signed") {
|
|
360
|
-
const token = generateJwt(client, playbackId, "g", params);
|
|
361
|
-
searchParams = new URLSearchParams({ token });
|
|
362
|
-
}
|
|
373
|
+
const params = { height, width, start, end, fps }, { playbackId, searchParams } = createUrlParamsObject(client, asset, params, "g");
|
|
363
374
|
return `https://image.mux.com/${playbackId}/animated.gif?${searchParams}`;
|
|
364
375
|
}
|
|
376
|
+
function getPosterSrc({
|
|
377
|
+
asset,
|
|
378
|
+
client,
|
|
379
|
+
fit_mode,
|
|
380
|
+
height,
|
|
381
|
+
time = asset.thumbTime ?? void 0,
|
|
382
|
+
width
|
|
383
|
+
}) {
|
|
384
|
+
const params = { fit_mode, height, width };
|
|
385
|
+
time && (params.time = time);
|
|
386
|
+
const { playbackId, searchParams } = createUrlParamsObject(client, asset, params, "t");
|
|
387
|
+
return `https://image.mux.com/${playbackId}/thumbnail.png?${searchParams}`;
|
|
388
|
+
}
|
|
365
389
|
const Image = styled.img`
|
|
366
390
|
transition: opacity 0.175s ease-out 0s;
|
|
367
391
|
display: block;
|
|
@@ -376,16 +400,18 @@ const Image = styled.img`
|
|
|
376
400
|
};
|
|
377
401
|
function VideoThumbnail({
|
|
378
402
|
asset,
|
|
379
|
-
width
|
|
403
|
+
width,
|
|
404
|
+
staticImage = !1
|
|
380
405
|
}) {
|
|
381
|
-
const { inView, ref } = useInView(), posterWidth = width || 250, [status, setStatus] = useState("loading"), client = useClient(),
|
|
406
|
+
const { inView, ref } = useInView(), posterWidth = width || 250, [status, setStatus] = useState("loading"), client = useClient(), src = useMemo(() => {
|
|
382
407
|
try {
|
|
383
|
-
|
|
408
|
+
let thumbnail;
|
|
409
|
+
return staticImage ? thumbnail = getPosterSrc({ asset, client, width: posterWidth }) : thumbnail = getAnimatedPosterSrc({ asset, client, width: posterWidth }), thumbnail;
|
|
384
410
|
} catch {
|
|
385
411
|
status !== "error" && setStatus("error");
|
|
386
412
|
return;
|
|
387
413
|
}
|
|
388
|
-
}, [asset, client, posterWidth, status]);
|
|
414
|
+
}, [asset, client, posterWidth, status, staticImage]);
|
|
389
415
|
function handleLoad() {
|
|
390
416
|
setStatus("loaded");
|
|
391
417
|
}
|
|
@@ -440,8 +466,8 @@ function VideoThumbnail({
|
|
|
440
466
|
/* @__PURE__ */ jsx(
|
|
441
467
|
Image,
|
|
442
468
|
{
|
|
443
|
-
src
|
|
444
|
-
alt: `Preview for video ${asset.filename || asset.assetId}`,
|
|
469
|
+
src,
|
|
470
|
+
alt: `Preview for ${staticImage ? "image" : "video"} ${asset.filename || asset.assetId}`,
|
|
445
471
|
onLoad: handleLoad,
|
|
446
472
|
onError: handleError,
|
|
447
473
|
style: {
|
|
@@ -754,6 +780,14 @@ function StopWatchIcon(props) {
|
|
|
754
780
|
}
|
|
755
781
|
);
|
|
756
782
|
}
|
|
783
|
+
const DialogStateContext = createContext({
|
|
784
|
+
dialogState: !1,
|
|
785
|
+
setDialogState: () => null
|
|
786
|
+
}), DialogStateProvider = ({
|
|
787
|
+
dialogState,
|
|
788
|
+
setDialogState,
|
|
789
|
+
children
|
|
790
|
+
}) => /* @__PURE__ */ jsx(DialogStateContext.Provider, { value: { dialogState, setDialogState }, children }), useDialogStateContext = () => useContext(DialogStateContext);
|
|
757
791
|
function getVideoSrc({ asset, client }) {
|
|
758
792
|
const playbackId = getPlaybackId(asset), searchParams = new URLSearchParams();
|
|
759
793
|
if (getPlaybackPolicy(asset) === "signed") {
|
|
@@ -762,12 +796,100 @@ function getVideoSrc({ asset, client }) {
|
|
|
762
796
|
}
|
|
763
797
|
return `https://stream.mux.com/${playbackId}.m3u8?${searchParams}`;
|
|
764
798
|
}
|
|
799
|
+
function getDevicePixelRatio(options) {
|
|
800
|
+
const {
|
|
801
|
+
defaultDpr = 1,
|
|
802
|
+
maxDpr = 3,
|
|
803
|
+
round = !0
|
|
804
|
+
} = options || {}, dpr = typeof window < "u" && typeof window.devicePixelRatio == "number" ? window.devicePixelRatio : defaultDpr;
|
|
805
|
+
return Math.min(Math.max(1, round ? Math.floor(dpr) : dpr), maxDpr);
|
|
806
|
+
}
|
|
807
|
+
function formatSeconds(seconds) {
|
|
808
|
+
if (typeof seconds != "number" || Number.isNaN(seconds))
|
|
809
|
+
return "";
|
|
810
|
+
const hrs = ~~(seconds / 3600), mins = ~~(seconds % 3600 / 60), secs = ~~seconds % 60;
|
|
811
|
+
let ret = "";
|
|
812
|
+
return hrs > 0 && (ret += "" + hrs + ":" + (mins < 10 ? "0" : "")), ret += "" + mins + ":" + (secs < 10 ? "0" : ""), ret += "" + secs, ret;
|
|
813
|
+
}
|
|
814
|
+
function formatSecondsToHHMMSS(seconds) {
|
|
815
|
+
const hrs = Math.floor(seconds / 3600).toString().padStart(2, "0"), mins = Math.floor(seconds % 3600 / 60).toString().padStart(2, "0"), secs = Math.floor(seconds % 60).toString().padStart(2, "0");
|
|
816
|
+
return `${hrs}:${mins}:${secs}`;
|
|
817
|
+
}
|
|
818
|
+
function isValidTimeFormat(time) {
|
|
819
|
+
return /^([0-1]?[0-9]|2[0-3]):([0-5]?[0-9]):([0-5]?[0-9])$/.test(time) || time === "";
|
|
820
|
+
}
|
|
821
|
+
function getSecondsFromTimeFormat(time) {
|
|
822
|
+
const [hh = 0, mm = 0, ss = 0] = time.split(":").map(Number);
|
|
823
|
+
return hh * 3600 + mm * 60 + ss;
|
|
824
|
+
}
|
|
825
|
+
function EditThumbnailDialog({ asset, currentTime = 0 }) {
|
|
826
|
+
const client = useClient(), { setDialogState } = useDialogStateContext(), dialogId = `EditThumbnailDialog${useId()}`, [timeFormatted, setTimeFormatted] = useState(
|
|
827
|
+
() => formatSecondsToHHMMSS(currentTime)
|
|
828
|
+
), [nextTime, setNextTime] = useState(currentTime), [inputError, setInputError] = useState(""), assetWithNewThumbnail = useMemo(() => ({ ...asset, thumbTime: nextTime }), [asset, nextTime]), [saving, setSaving] = useState(!1), [saveThumbnailError, setSaveThumbnailError] = useState(null), handleSave = () => {
|
|
829
|
+
setSaving(!0), client.patch(asset._id).set({ thumbTime: nextTime }).commit({ returnDocuments: !1 }).then(() => void setDialogState(!1)).catch(setSaveThumbnailError).finally(() => void setSaving(!1));
|
|
830
|
+
}, width = 300 * getDevicePixelRatio({ maxDpr: 2 });
|
|
831
|
+
if (saveThumbnailError)
|
|
832
|
+
throw saveThumbnailError;
|
|
833
|
+
return /* @__PURE__ */ jsx(
|
|
834
|
+
Dialog,
|
|
835
|
+
{
|
|
836
|
+
id: dialogId,
|
|
837
|
+
header: "Edit thumbnail",
|
|
838
|
+
onClose: () => setDialogState(!1),
|
|
839
|
+
footer: /* @__PURE__ */ jsx(Stack, { padding: 3, children: /* @__PURE__ */ jsx(
|
|
840
|
+
Button,
|
|
841
|
+
{
|
|
842
|
+
disabled: inputError !== "",
|
|
843
|
+
mode: "ghost",
|
|
844
|
+
tone: "primary",
|
|
845
|
+
loading: saving,
|
|
846
|
+
onClick: handleSave,
|
|
847
|
+
text: "Set new thumbnail"
|
|
848
|
+
},
|
|
849
|
+
"thumbnail"
|
|
850
|
+
) }),
|
|
851
|
+
children: /* @__PURE__ */ jsxs(Stack, { space: 3, padding: 3, children: [
|
|
852
|
+
/* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
853
|
+
/* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: "Current:" }),
|
|
854
|
+
/* @__PURE__ */ jsx(VideoThumbnail, { asset, width, staticImage: !0 })
|
|
855
|
+
] }),
|
|
856
|
+
/* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
857
|
+
/* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: "New:" }),
|
|
858
|
+
/* @__PURE__ */ jsx(VideoThumbnail, { asset: assetWithNewThumbnail, width, staticImage: !0 })
|
|
859
|
+
] }),
|
|
860
|
+
/* @__PURE__ */ jsx(Stack, { space: 2, children: /* @__PURE__ */ jsx(Flex, { align: "center", justify: "center", children: /* @__PURE__ */ jsx(Text, { size: 5, weight: "semibold", children: "Or" }) }) }),
|
|
861
|
+
/* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
862
|
+
/* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: "Selected time for thumbnail (hh:mm:ss):" }),
|
|
863
|
+
/* @__PURE__ */ jsx(
|
|
864
|
+
TextInput,
|
|
865
|
+
{
|
|
866
|
+
size: 1,
|
|
867
|
+
value: timeFormatted,
|
|
868
|
+
placeholder: "hh:mm:ss",
|
|
869
|
+
onChange: (event) => {
|
|
870
|
+
const value = event.currentTarget.value;
|
|
871
|
+
if (setTimeFormatted(value), isValidTimeFormat(value)) {
|
|
872
|
+
setInputError("");
|
|
873
|
+
const totalSeconds = getSecondsFromTimeFormat(value);
|
|
874
|
+
setNextTime(totalSeconds);
|
|
875
|
+
} else
|
|
876
|
+
setInputError("Invalid time format");
|
|
877
|
+
},
|
|
878
|
+
customValidity: inputError
|
|
879
|
+
}
|
|
880
|
+
)
|
|
881
|
+
] })
|
|
882
|
+
] })
|
|
883
|
+
}
|
|
884
|
+
);
|
|
885
|
+
}
|
|
765
886
|
function VideoPlayer({
|
|
766
887
|
asset,
|
|
888
|
+
thumbnailWidth = 250,
|
|
767
889
|
children,
|
|
768
890
|
...props
|
|
769
891
|
}) {
|
|
770
|
-
const client = useClient(), isAudio = assetIsAudio(asset), { src: videoSrc, error } = useMemo(() => {
|
|
892
|
+
const client = useClient(), { dialogState } = useDialogStateContext(), isAudio = assetIsAudio(asset), muxPlayer = useRef(null), thumbnail = getPosterSrc({ asset, client, width: thumbnailWidth }), { src: videoSrc, error } = useMemo(() => {
|
|
771
893
|
try {
|
|
772
894
|
const src = asset?.playbackId && getVideoSrc({ client, asset });
|
|
773
895
|
return src ? { src } : { error: new TypeError("Asset has no playback ID") };
|
|
@@ -785,49 +907,54 @@ function VideoPlayer({
|
|
|
785
907
|
return isAudio && (aspectRatio = props.forceAspectRatio ? (
|
|
786
908
|
// Make it wider when forcing aspect ratio to balance with videos' rendering height (audio players overflow a bit)
|
|
787
909
|
props.forceAspectRatio * 1.2
|
|
788
|
-
) : AUDIO_ASPECT_RATIO), /* @__PURE__ */ jsxs(
|
|
789
|
-
|
|
790
|
-
/* @__PURE__ */
|
|
791
|
-
|
|
910
|
+
) : AUDIO_ASPECT_RATIO), /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
911
|
+
/* @__PURE__ */ jsxs(Card, { tone: "transparent", style: { aspectRatio, position: "relative" }, children: [
|
|
912
|
+
videoSrc && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
913
|
+
/* @__PURE__ */ jsx(
|
|
914
|
+
MuxPlayer,
|
|
915
|
+
{
|
|
916
|
+
poster: thumbnail,
|
|
917
|
+
ref: muxPlayer,
|
|
918
|
+
...props,
|
|
919
|
+
playsInline: !0,
|
|
920
|
+
playbackId: asset.playbackId,
|
|
921
|
+
tokens: signedToken ? { playback: signedToken, thumbnail: signedToken, storyboard: signedToken } : void 0,
|
|
922
|
+
preload: "metadata",
|
|
923
|
+
crossOrigin: "anonymous",
|
|
924
|
+
metadata: {
|
|
925
|
+
player_name: "Sanity Admin Dashboard",
|
|
926
|
+
player_version: "2.5.0",
|
|
927
|
+
page_type: "Preview Player"
|
|
928
|
+
},
|
|
929
|
+
audio: isAudio,
|
|
930
|
+
style: {
|
|
931
|
+
height: "100%",
|
|
932
|
+
width: "100%",
|
|
933
|
+
display: "block",
|
|
934
|
+
objectFit: "contain"
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
),
|
|
938
|
+
children
|
|
939
|
+
] }),
|
|
940
|
+
error ? /* @__PURE__ */ jsx(
|
|
941
|
+
"div",
|
|
792
942
|
{
|
|
793
|
-
...props,
|
|
794
|
-
playsInline: !0,
|
|
795
|
-
playbackId: asset.playbackId,
|
|
796
|
-
tokens: signedToken ? { playback: signedToken, thumbnail: signedToken, storyboard: signedToken } : void 0,
|
|
797
|
-
preload: "metadata",
|
|
798
|
-
crossOrigin: "anonymous",
|
|
799
|
-
metadata: {
|
|
800
|
-
player_name: "Sanity Admin Dashboard",
|
|
801
|
-
player_version: "2.4.0",
|
|
802
|
-
page_type: "Preview Player"
|
|
803
|
-
},
|
|
804
|
-
audio: isAudio,
|
|
805
943
|
style: {
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
}
|
|
944
|
+
position: "absolute",
|
|
945
|
+
top: "50%",
|
|
946
|
+
left: "50%",
|
|
947
|
+
transform: "translate(-50%, -50%)"
|
|
948
|
+
},
|
|
949
|
+
children: /* @__PURE__ */ jsxs(Text, { muted: !0, children: [
|
|
950
|
+
/* @__PURE__ */ jsx(ErrorOutlineIcon, { style: { marginRight: "0.15em" } }),
|
|
951
|
+
typeof error == "object" && "message" in error && typeof error.message == "string" ? error.message : "Error loading video"
|
|
952
|
+
] })
|
|
811
953
|
}
|
|
812
|
-
),
|
|
954
|
+
) : null,
|
|
813
955
|
children
|
|
814
956
|
] }),
|
|
815
|
-
|
|
816
|
-
"div",
|
|
817
|
-
{
|
|
818
|
-
style: {
|
|
819
|
-
position: "absolute",
|
|
820
|
-
top: "50%",
|
|
821
|
-
left: "50%",
|
|
822
|
-
transform: "translate(-50%, -50%)"
|
|
823
|
-
},
|
|
824
|
-
children: /* @__PURE__ */ jsxs(Text, { muted: !0, children: [
|
|
825
|
-
/* @__PURE__ */ jsx(ErrorOutlineIcon, { style: { marginRight: "0.15em" } }),
|
|
826
|
-
typeof error == "object" && "message" in error && typeof error.message == "string" ? error.message : "Error loading video"
|
|
827
|
-
] })
|
|
828
|
-
}
|
|
829
|
-
) : null,
|
|
830
|
-
children
|
|
957
|
+
dialogState === "edit-thumbnail" && /* @__PURE__ */ jsx(EditThumbnailDialog, { asset, currentTime: muxPlayer?.current?.currentTime })
|
|
831
958
|
] });
|
|
832
959
|
}
|
|
833
960
|
function assetIsAudio(asset) {
|
|
@@ -1150,13 +1277,6 @@ const useDocReferences = createHookFromObservableFactory(({ documentStore, id })
|
|
|
1150
1277
|
apiVersion: SANITY_API_VERSION
|
|
1151
1278
|
}
|
|
1152
1279
|
));
|
|
1153
|
-
function formatSeconds(seconds) {
|
|
1154
|
-
if (typeof seconds != "number" || Number.isNaN(seconds))
|
|
1155
|
-
return "";
|
|
1156
|
-
const hrs = ~~(seconds / 3600), mins = ~~(seconds % 3600 / 60), secs = ~~seconds % 60;
|
|
1157
|
-
let ret = "";
|
|
1158
|
-
return hrs > 0 && (ret += "" + hrs + ":" + (mins < 10 ? "0" : "")), ret += "" + mins + ":" + (secs < 10 ? "0" : ""), ret += "" + secs, ret;
|
|
1159
|
-
}
|
|
1160
1280
|
function getVideoMetadata(doc) {
|
|
1161
1281
|
const id = doc.assetId || doc._id || "", date = doc.data?.created_at ? new Date(Number(doc.data.created_at) * 1e3) : new Date(doc._createdAt || doc._updatedAt || Date.now());
|
|
1162
1282
|
return {
|
|
@@ -3279,7 +3399,7 @@ const FileButton = styled(MenuItem)(({ theme }) => {
|
|
|
3279
3399
|
`, LockButton = styled(Button)`
|
|
3280
3400
|
background: transparent;
|
|
3281
3401
|
color: white;
|
|
3282
|
-
|
|
3402
|
+
`, isVideoAsset = (asset) => asset._type === "mux.videoAsset";
|
|
3283
3403
|
function PlayerActionsMenu(props) {
|
|
3284
3404
|
const { asset, readOnly, dialogState, setDialogState, onChange, onSelect } = props, [open, setOpen] = useState(!1), [menuElement, setMenuRef] = useState(null), isSigned = useMemo(() => getPlaybackPolicy(asset) === "signed", [asset]), onReset = useCallback(() => onChange(PatchEvent.from(unset([]))), [onChange]);
|
|
3285
3405
|
return useEffect(() => {
|
|
@@ -3323,6 +3443,14 @@ function PlayerActionsMenu(props) {
|
|
|
3323
3443
|
onClick: () => setDialogState("select-video")
|
|
3324
3444
|
}
|
|
3325
3445
|
),
|
|
3446
|
+
isVideoAsset(asset) && /* @__PURE__ */ jsx(
|
|
3447
|
+
MenuItem,
|
|
3448
|
+
{
|
|
3449
|
+
icon: ImageIcon,
|
|
3450
|
+
text: "Thumbnail",
|
|
3451
|
+
onClick: () => setDialogState("edit-thumbnail")
|
|
3452
|
+
}
|
|
3453
|
+
),
|
|
3326
3454
|
/* @__PURE__ */ jsx(MenuDivider, {}),
|
|
3327
3455
|
/* @__PURE__ */ jsx(
|
|
3328
3456
|
MenuItem,
|
|
@@ -3941,7 +4069,7 @@ function Uploader(props) {
|
|
|
3941
4069
|
case "commitUpload":
|
|
3942
4070
|
return Object.assign({}, prev, { uploadStatus: { progress: 0 } });
|
|
3943
4071
|
case "progressInfo": {
|
|
3944
|
-
const {
|
|
4072
|
+
const { ...payload } = action;
|
|
3945
4073
|
return Object.assign({}, prev, {
|
|
3946
4074
|
uploadStatus: {
|
|
3947
4075
|
...prev.uploadStatus,
|
|
@@ -4060,7 +4188,7 @@ function Uploader(props) {
|
|
|
4060
4188
|
idx > -1 && dragEnteredEls.current.splice(idx, 1), dragEnteredEls.current.length === 0 && setDragState(null);
|
|
4061
4189
|
};
|
|
4062
4190
|
if (state.error !== null) {
|
|
4063
|
-
const error = {
|
|
4191
|
+
const error = {};
|
|
4064
4192
|
return /* @__PURE__ */ jsxs(Flex, { gap: 3, direction: "column", justify: "center", align: "center", children: [
|
|
4065
4193
|
/* @__PURE__ */ jsx(Text, { size: 5, muted: !0, children: /* @__PURE__ */ jsx(ErrorOutlineIcon, {}) }),
|
|
4066
4194
|
/* @__PURE__ */ jsx(Text, { children: "Something went wrong" }),
|
|
@@ -4103,20 +4231,27 @@ function Uploader(props) {
|
|
|
4103
4231
|
onPaste: handlePaste,
|
|
4104
4232
|
ref: containerRef,
|
|
4105
4233
|
children: props.asset ? /* @__PURE__ */ jsx(
|
|
4106
|
-
|
|
4234
|
+
DialogStateProvider,
|
|
4107
4235
|
{
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
PlayerActionsMenu$1,
|
|
4236
|
+
dialogState: props.dialogState,
|
|
4237
|
+
setDialogState: props.setDialogState,
|
|
4238
|
+
children: /* @__PURE__ */ jsx(
|
|
4239
|
+
Player,
|
|
4113
4240
|
{
|
|
4241
|
+
readOnly: props.readOnly,
|
|
4114
4242
|
asset: props.asset,
|
|
4115
|
-
dialogState: props.dialogState,
|
|
4116
|
-
setDialogState: props.setDialogState,
|
|
4117
4243
|
onChange: props.onChange,
|
|
4118
|
-
|
|
4119
|
-
|
|
4244
|
+
buttons: /* @__PURE__ */ jsx(
|
|
4245
|
+
PlayerActionsMenu$1,
|
|
4246
|
+
{
|
|
4247
|
+
asset: props.asset,
|
|
4248
|
+
dialogState: props.dialogState,
|
|
4249
|
+
setDialogState: props.setDialogState,
|
|
4250
|
+
onChange: props.onChange,
|
|
4251
|
+
onSelect: handleUpload,
|
|
4252
|
+
readOnly: props.readOnly
|
|
4253
|
+
}
|
|
4254
|
+
)
|
|
4120
4255
|
}
|
|
4121
4256
|
)
|
|
4122
4257
|
}
|