sanity-plugin-r2-video 0.1.8 → 0.1.9
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/studio/index.js +184 -188
- package/package.json +1 -1
package/dist/studio/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { resolveRenditionPath } from '../chunk-D5VS4ED7.js';
|
|
2
2
|
export { resolveRenditionPath } from '../chunk-D5VS4ED7.js';
|
|
3
|
-
import {
|
|
4
|
-
import { defineType, defineField, definePlugin,
|
|
5
|
-
import { createContext, useState, useCallback, useEffect, useRef, useMemo, useContext, useId } from 'react';
|
|
3
|
+
import { createContext, useState, useCallback, useEffect, useRef, useMemo, useId, useContext } from 'react';
|
|
4
|
+
import { defineType, defineField, definePlugin, set, useClient } from 'sanity';
|
|
6
5
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
|
+
import { PlayIcon } from '@sanity/icons/Play';
|
|
7
7
|
import { UploadIcon } from '@sanity/icons/Upload';
|
|
8
|
-
import { Stack, Flex, Box, Button, TextInput,
|
|
8
|
+
import { Stack, Flex, Box, Button, TextInput, Card, Text, Grid, Dialog, Spinner, Select, Switch } from '@sanity/ui';
|
|
9
9
|
import { AccessDeniedIcon } from '@sanity/icons/AccessDenied';
|
|
10
10
|
import { canEncodeVideo } from 'mediabunny';
|
|
11
11
|
import { WarningOutlineIcon } from '@sanity/icons/WarningOutline';
|
|
@@ -107,6 +107,24 @@ var deleteVideoAsset = async (client, config, asset) => {
|
|
|
107
107
|
renditions.map((rendition) => rendition.key)
|
|
108
108
|
);
|
|
109
109
|
};
|
|
110
|
+
var R2VideoConfigContext = createContext(null);
|
|
111
|
+
var R2VideoConfigProvider = ({ config, children }) => {
|
|
112
|
+
return /* @__PURE__ */ jsx(R2VideoConfigContext.Provider, { value: config, children });
|
|
113
|
+
};
|
|
114
|
+
var useR2VideoConfig = () => {
|
|
115
|
+
const config = useContext(R2VideoConfigContext);
|
|
116
|
+
if (!config) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
"Missing R2 video config. Add `r2Video` to the Studio's plugin list."
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return config;
|
|
122
|
+
};
|
|
123
|
+
var useR2VideoClient = () => {
|
|
124
|
+
const config = useR2VideoConfig();
|
|
125
|
+
const client = useClient({ apiVersion: config.apiVersion });
|
|
126
|
+
return { config, client };
|
|
127
|
+
};
|
|
110
128
|
|
|
111
129
|
// studio/folders.ts
|
|
112
130
|
var QUERY_FOLDERS = `*[_type == $folderType]{
|
|
@@ -150,18 +168,13 @@ var resolvePosterFolder = async (client, folderType, name) => {
|
|
|
150
168
|
const created = await client.create({ _type: folderType, name });
|
|
151
169
|
return created._id;
|
|
152
170
|
};
|
|
153
|
-
var
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
throw new Error(
|
|
161
|
-
"Missing R2 video config. Add `r2Video` to the Studio's plugin list."
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
|
-
return config;
|
|
171
|
+
var useFolders = () => {
|
|
172
|
+
const { config, client } = useR2VideoClient();
|
|
173
|
+
const [folders, setFolders] = useState([]);
|
|
174
|
+
useEffect(() => {
|
|
175
|
+
fetchFolders(client, config.folders.type).then(setFolders);
|
|
176
|
+
}, [client, config.folders.type]);
|
|
177
|
+
return { folders, paths: resolveFolderPaths(folders) };
|
|
165
178
|
};
|
|
166
179
|
|
|
167
180
|
// studio/defaults.ts
|
|
@@ -204,6 +217,15 @@ var formatSize = (bytes) => {
|
|
|
204
217
|
var formatDuration = (seconds) => {
|
|
205
218
|
return `${seconds.toFixed(1)}s`;
|
|
206
219
|
};
|
|
220
|
+
var totalSize = (renditions) => {
|
|
221
|
+
return renditions.reduce((total, rendition) => total + rendition.size, 0);
|
|
222
|
+
};
|
|
223
|
+
var pluralize = (count, noun) => {
|
|
224
|
+
return `${count} ${noun}${count === 1 ? "" : "s"}`;
|
|
225
|
+
};
|
|
226
|
+
var toMessage = (error) => {
|
|
227
|
+
return error instanceof Error ? error.message : String(error);
|
|
228
|
+
};
|
|
207
229
|
var formatBitrate = (bytes, seconds) => {
|
|
208
230
|
if (!seconds) {
|
|
209
231
|
return "-";
|
|
@@ -333,6 +355,57 @@ var transcodeVideo = (request, progressed) => {
|
|
|
333
355
|
worker.postMessage(request);
|
|
334
356
|
});
|
|
335
357
|
};
|
|
358
|
+
var Notice = ({ tone, title, icon, children }) => {
|
|
359
|
+
return /* @__PURE__ */ jsx(Card, { padding: 4, radius: 2, tone, children: /* @__PURE__ */ jsxs(Flex, { align: "flex-start", gap: 3, children: [
|
|
360
|
+
icon && /* @__PURE__ */ jsx(Text, { size: 2, children: icon }),
|
|
361
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 3, children: [
|
|
362
|
+
title && /* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: title }),
|
|
363
|
+
typeof children === "string" ? /* @__PURE__ */ jsx(Text, { muted: Boolean(title), size: 1, children }) : children
|
|
364
|
+
] })
|
|
365
|
+
] }) });
|
|
366
|
+
};
|
|
367
|
+
var Field = ({ label, id, description, children }) => {
|
|
368
|
+
return /* @__PURE__ */ jsxs(Stack, { gap: 3, children: [
|
|
369
|
+
/* @__PURE__ */ jsx(Text, { as: id ? "label" : void 0, htmlFor: id, size: 1, weight: "medium", children: label }),
|
|
370
|
+
children,
|
|
371
|
+
description && /* @__PURE__ */ jsx(Text, { muted: true, size: 0, children: description })
|
|
372
|
+
] });
|
|
373
|
+
};
|
|
374
|
+
var DialogActions = ({
|
|
375
|
+
aside,
|
|
376
|
+
cancel,
|
|
377
|
+
confirm
|
|
378
|
+
}) => {
|
|
379
|
+
return /* @__PURE__ */ jsx(Card, { borderTop: true, padding: 2, children: /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
|
380
|
+
aside,
|
|
381
|
+
/* @__PURE__ */ jsx(Box, { flex: 1, children: /* @__PURE__ */ jsx(
|
|
382
|
+
Button,
|
|
383
|
+
{
|
|
384
|
+
disabled: cancel.disabled,
|
|
385
|
+
mode: "ghost",
|
|
386
|
+
text: cancel.text,
|
|
387
|
+
width: "fill",
|
|
388
|
+
onClick: cancel.onClick
|
|
389
|
+
}
|
|
390
|
+
) }),
|
|
391
|
+
confirm && /* @__PURE__ */ jsx(Box, { flex: 1, children: /* @__PURE__ */ jsx(
|
|
392
|
+
Button,
|
|
393
|
+
{
|
|
394
|
+
disabled: confirm.disabled,
|
|
395
|
+
text: confirm.text,
|
|
396
|
+
tone: confirm.tone,
|
|
397
|
+
width: "fill",
|
|
398
|
+
onClick: confirm.onClick
|
|
399
|
+
}
|
|
400
|
+
) })
|
|
401
|
+
] }) });
|
|
402
|
+
};
|
|
403
|
+
var Loading = ({ children }) => {
|
|
404
|
+
return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 3, children: [
|
|
405
|
+
/* @__PURE__ */ jsx(Spinner, { muted: true }),
|
|
406
|
+
/* @__PURE__ */ jsx(Text, { muted: true, size: 1, children })
|
|
407
|
+
] });
|
|
408
|
+
};
|
|
336
409
|
var PreviewEncode = ({ file, keepAudio, encoding }) => {
|
|
337
410
|
const [preview, setPreview] = useState(null);
|
|
338
411
|
const [isEncoding, setIsEncoding] = useState(false);
|
|
@@ -374,7 +447,7 @@ var PreviewEncode = ({ file, keepAudio, encoding }) => {
|
|
|
374
447
|
duration: result.duration
|
|
375
448
|
});
|
|
376
449
|
} catch (caught) {
|
|
377
|
-
setError(
|
|
450
|
+
setError(toMessage(caught));
|
|
378
451
|
}
|
|
379
452
|
setIsEncoding(false);
|
|
380
453
|
};
|
|
@@ -394,15 +467,12 @@ var PreviewEncode = ({ file, keepAudio, encoding }) => {
|
|
|
394
467
|
}
|
|
395
468
|
)
|
|
396
469
|
] }),
|
|
397
|
-
isEncoding && /* @__PURE__ */ jsxs(
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
Math.round(progress * 100),
|
|
402
|
-
"%"
|
|
403
|
-
] })
|
|
470
|
+
isEncoding && /* @__PURE__ */ jsxs(Loading, { children: [
|
|
471
|
+
"Encoding top tier - ",
|
|
472
|
+
Math.round(progress * 100),
|
|
473
|
+
"%"
|
|
404
474
|
] }),
|
|
405
|
-
error && /* @__PURE__ */ jsx(
|
|
475
|
+
error && /* @__PURE__ */ jsx(Notice, { tone: "critical", children: error }),
|
|
406
476
|
preview && !isEncoding && /* @__PURE__ */ jsxs(Stack, { gap: 3, children: [
|
|
407
477
|
/* @__PURE__ */ jsx(
|
|
408
478
|
"video",
|
|
@@ -439,20 +509,15 @@ var PreviewEncode = ({ file, keepAudio, encoding }) => {
|
|
|
439
509
|
] }) });
|
|
440
510
|
};
|
|
441
511
|
var UnsupportedBrowser = () => {
|
|
442
|
-
return /* @__PURE__ */ jsx(
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
/* @__PURE__ */ jsx(
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
return /* @__PURE__ */ jsxs(Stack, { gap: 3, children: [
|
|
452
|
-
/* @__PURE__ */ jsx(Text, { as: "label", htmlFor: id, size: 1, weight: "medium", children: label }),
|
|
453
|
-
children,
|
|
454
|
-
/* @__PURE__ */ jsx(Text, { muted: true, size: 0, children: description })
|
|
455
|
-
] });
|
|
512
|
+
return /* @__PURE__ */ jsx(
|
|
513
|
+
Notice,
|
|
514
|
+
{
|
|
515
|
+
icon: /* @__PURE__ */ jsx(WarningOutlineIcon, {}),
|
|
516
|
+
title: "This browser can't encode video",
|
|
517
|
+
tone: "caution",
|
|
518
|
+
children: "Uploading needs WebCodecs h264 encoding, which this browser doesn't provide. Open the Studio in Chrome to add videos. Everything already in the library still works here."
|
|
519
|
+
}
|
|
520
|
+
);
|
|
456
521
|
};
|
|
457
522
|
var ToggleRow = ({ id, label, description, children }) => {
|
|
458
523
|
return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 3, children: [
|
|
@@ -702,9 +767,6 @@ var describe = (item) => {
|
|
|
702
767
|
const { stage, progress, label } = item.progress;
|
|
703
768
|
return `${STAGE_LABELS[stage]} ${label} \xB7 ${Math.round(progress * 100)}%`;
|
|
704
769
|
};
|
|
705
|
-
var countLabel = (count) => {
|
|
706
|
-
return count === 1 ? "1 video" : `${count} video(s)`;
|
|
707
|
-
};
|
|
708
770
|
var toItem = (file) => ({
|
|
709
771
|
id: crypto.randomUUID(),
|
|
710
772
|
file,
|
|
@@ -718,10 +780,9 @@ var DialogUpload = ({
|
|
|
718
780
|
onUploaded,
|
|
719
781
|
onClose
|
|
720
782
|
}) => {
|
|
721
|
-
const config =
|
|
722
|
-
const
|
|
783
|
+
const { config, client } = useR2VideoClient();
|
|
784
|
+
const { paths } = useFolders();
|
|
723
785
|
const [canEncode, setCanEncode] = useState(null);
|
|
724
|
-
const [folders, setFolders] = useState([]);
|
|
725
786
|
const [targetFolder, setTargetFolder] = useState(folderId);
|
|
726
787
|
const [keepAudio, setKeepAudio] = useState(false);
|
|
727
788
|
const [quality, setQuality] = useState(config.encoding.quality);
|
|
@@ -736,13 +797,9 @@ var DialogUpload = ({
|
|
|
736
797
|
useEffect(() => {
|
|
737
798
|
canEncodeLadder(config.encoding.videoCodec).then(setCanEncode);
|
|
738
799
|
}, [config.encoding.videoCodec]);
|
|
739
|
-
useEffect(() => {
|
|
740
|
-
fetchFolders(client, config.folders.type).then(setFolders);
|
|
741
|
-
}, [client, config.folders.type]);
|
|
742
800
|
const encoding = useMemo(() => {
|
|
743
801
|
return { ...config.encoding, quality, preferBitrate };
|
|
744
802
|
}, [config.encoding, quality, preferBitrate]);
|
|
745
|
-
const paths = resolveFolderPaths(folders);
|
|
746
803
|
const pending = items.filter((item) => item.status === "pending");
|
|
747
804
|
const isBusy = items.some((item) => item.status === "working");
|
|
748
805
|
const canStart = pending.length > 0 && !isBusy && canEncode !== false;
|
|
@@ -776,7 +833,7 @@ var DialogUpload = ({
|
|
|
776
833
|
update(item.id, {
|
|
777
834
|
status: "failed",
|
|
778
835
|
progress: null,
|
|
779
|
-
error:
|
|
836
|
+
error: toMessage(error)
|
|
780
837
|
});
|
|
781
838
|
}
|
|
782
839
|
}
|
|
@@ -817,28 +874,22 @@ var DialogUpload = ({
|
|
|
817
874
|
id: "r2-video-upload",
|
|
818
875
|
width: 1,
|
|
819
876
|
onClose: isBusy ? void 0 : onClose,
|
|
820
|
-
footer: /* @__PURE__ */ jsx(
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
{
|
|
824
|
-
disabled: isBusy,
|
|
825
|
-
mode: "ghost",
|
|
877
|
+
footer: /* @__PURE__ */ jsx(
|
|
878
|
+
DialogActions,
|
|
879
|
+
{
|
|
880
|
+
cancel: {
|
|
826
881
|
text: items.length > 0 && !canStart ? "Done" : "Cancel",
|
|
827
|
-
|
|
882
|
+
disabled: isBusy,
|
|
828
883
|
onClick: onClose
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
Button,
|
|
833
|
-
{
|
|
834
|
-
disabled: !canStart,
|
|
835
|
-
text: isBusy ? "Encoding\u2026" : `Upload ${countLabel(pending.length)}`,
|
|
884
|
+
},
|
|
885
|
+
confirm: {
|
|
886
|
+
text: isBusy ? "Encoding\u2026" : `Upload ${pluralize(pending.length, "video")}`,
|
|
836
887
|
tone: "primary",
|
|
837
|
-
|
|
888
|
+
disabled: !canStart,
|
|
838
889
|
onClick: confirmed
|
|
839
890
|
}
|
|
840
|
-
|
|
841
|
-
|
|
891
|
+
}
|
|
892
|
+
),
|
|
842
893
|
children: /* @__PURE__ */ jsx(Card, { padding: 4, children: /* @__PURE__ */ jsxs(Stack, { gap: 5, children: [
|
|
843
894
|
canEncode === false && /* @__PURE__ */ jsx(UnsupportedBrowser, {}),
|
|
844
895
|
/* @__PURE__ */ jsx(
|
|
@@ -883,7 +934,7 @@ var DialogUpload = ({
|
|
|
883
934
|
}
|
|
884
935
|
),
|
|
885
936
|
items.length > 0 && /* @__PURE__ */ jsxs(Stack, { gap: 3, children: [
|
|
886
|
-
/* @__PURE__ */ jsx(Text, { muted: true, size: 1, weight: "medium", children:
|
|
937
|
+
/* @__PURE__ */ jsx(Text, { muted: true, size: 1, weight: "medium", children: pluralize(items.length, "video") }),
|
|
887
938
|
/* @__PURE__ */ jsx(Stack, { gap: 2, children: items.map((item) => /* @__PURE__ */ jsx(
|
|
888
939
|
Card,
|
|
889
940
|
{
|
|
@@ -976,7 +1027,7 @@ var InputVideo = (props) => {
|
|
|
976
1027
|
};
|
|
977
1028
|
return /* @__PURE__ */ jsxs(Stack, { gap: 3, style: { position: "relative" }, ...dropProps, children: [
|
|
978
1029
|
isDragging && /* @__PURE__ */ jsx(DropToUpload, { isRejected }),
|
|
979
|
-
/* @__PURE__ */ jsxs(Flex, { gap:
|
|
1030
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 1, children: [
|
|
980
1031
|
/* @__PURE__ */ jsx(Box, { flex: 1, children: renderDefault({
|
|
981
1032
|
...props,
|
|
982
1033
|
renderField: (field) => field.children
|
|
@@ -1015,7 +1066,7 @@ var VideoPreview = ({ renditions, posterUrl }) => {
|
|
|
1015
1066
|
const config = useR2VideoConfig();
|
|
1016
1067
|
const rendition = resolvePreviewRendition(renditions);
|
|
1017
1068
|
if (!rendition) {
|
|
1018
|
-
return /* @__PURE__ */ jsx(
|
|
1069
|
+
return /* @__PURE__ */ jsx(Notice, { tone: "caution", children: "This video has no renditions to play." });
|
|
1019
1070
|
}
|
|
1020
1071
|
return /* @__PURE__ */ jsx(
|
|
1021
1072
|
"video",
|
|
@@ -1054,9 +1105,6 @@ var VideoSummary = ({
|
|
|
1054
1105
|
hasAudio,
|
|
1055
1106
|
uploadedAt
|
|
1056
1107
|
}) => {
|
|
1057
|
-
const totalBytes = renditions.reduce((total, rendition) => {
|
|
1058
|
-
return total + rendition.size;
|
|
1059
|
-
}, 0);
|
|
1060
1108
|
const ordered = [...renditions].sort((a, b) => b.height - a.height);
|
|
1061
1109
|
const largest = ordered[0];
|
|
1062
1110
|
return /* @__PURE__ */ jsxs(Stack, { gap: 5, children: [
|
|
@@ -1070,7 +1118,7 @@ var VideoSummary = ({
|
|
|
1070
1118
|
),
|
|
1071
1119
|
/* @__PURE__ */ jsx(Fact, { label: "Duration", value: formatDuration(duration) }),
|
|
1072
1120
|
/* @__PURE__ */ jsx(Fact, { label: "Audio", value: hasAudio ? "Kept" : "Stripped" }),
|
|
1073
|
-
/* @__PURE__ */ jsx(Fact, { label: "Total", value: formatSize(
|
|
1121
|
+
/* @__PURE__ */ jsx(Fact, { label: "Total", value: formatSize(totalSize(renditions)) })
|
|
1074
1122
|
] }) }),
|
|
1075
1123
|
/* @__PURE__ */ jsxs(Stack, { gap: 3, children: [
|
|
1076
1124
|
/* @__PURE__ */ jsx(Text, { muted: true, size: 1, weight: "medium", children: "Renditions" }),
|
|
@@ -1277,8 +1325,7 @@ var SCHEMA_R2_VIDEO = defineType({
|
|
|
1277
1325
|
}
|
|
1278
1326
|
});
|
|
1279
1327
|
var DialogDelete = ({ asset, onDeleted, onClose }) => {
|
|
1280
|
-
const config =
|
|
1281
|
-
const client = useClient({ apiVersion: config.apiVersion });
|
|
1328
|
+
const { config, client } = useR2VideoClient();
|
|
1282
1329
|
const [blockers, setBlockers] = useState(null);
|
|
1283
1330
|
const [isDeleting, setIsDeleting] = useState(false);
|
|
1284
1331
|
const [error, setError] = useState(null);
|
|
@@ -1294,13 +1341,10 @@ var DialogDelete = ({ asset, onDeleted, onClose }) => {
|
|
|
1294
1341
|
await deleteVideoAsset(client, config, asset);
|
|
1295
1342
|
onDeleted();
|
|
1296
1343
|
} catch (caught) {
|
|
1297
|
-
setError(
|
|
1344
|
+
setError(toMessage(caught));
|
|
1298
1345
|
setIsDeleting(false);
|
|
1299
1346
|
}
|
|
1300
1347
|
};
|
|
1301
|
-
const totalBytes = asset.renditions.reduce((total, rendition) => {
|
|
1302
|
-
return total + rendition.size;
|
|
1303
|
-
}, 0);
|
|
1304
1348
|
const isBlocked = blockers !== null && blockers.length > 0;
|
|
1305
1349
|
return /* @__PURE__ */ jsx(
|
|
1306
1350
|
Dialog,
|
|
@@ -1309,28 +1353,18 @@ var DialogDelete = ({ asset, onDeleted, onClose }) => {
|
|
|
1309
1353
|
id: "r2-video-delete",
|
|
1310
1354
|
width: 1,
|
|
1311
1355
|
onClose: isDeleting ? void 0 : onClose,
|
|
1312
|
-
footer: /* @__PURE__ */ jsx(
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
{
|
|
1316
|
-
|
|
1317
|
-
mode: "ghost",
|
|
1318
|
-
text: "Cancel",
|
|
1319
|
-
width: "fill",
|
|
1320
|
-
onClick: onClose
|
|
1321
|
-
}
|
|
1322
|
-
) }),
|
|
1323
|
-
/* @__PURE__ */ jsx(Box, { flex: 1, children: /* @__PURE__ */ jsx(
|
|
1324
|
-
Button,
|
|
1325
|
-
{
|
|
1326
|
-
disabled: isDeleting || isBlocked || blockers === null,
|
|
1356
|
+
footer: /* @__PURE__ */ jsx(
|
|
1357
|
+
DialogActions,
|
|
1358
|
+
{
|
|
1359
|
+
cancel: { text: "Cancel", disabled: isDeleting, onClick: onClose },
|
|
1360
|
+
confirm: {
|
|
1327
1361
|
text: isDeleting ? "Deleting\u2026" : "Delete",
|
|
1328
1362
|
tone: "critical",
|
|
1329
|
-
|
|
1363
|
+
disabled: isDeleting || isBlocked || blockers === null,
|
|
1330
1364
|
onClick: confirmed
|
|
1331
1365
|
}
|
|
1332
|
-
|
|
1333
|
-
|
|
1366
|
+
}
|
|
1367
|
+
),
|
|
1334
1368
|
children: /* @__PURE__ */ jsx(Card, { padding: 4, children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
1335
1369
|
/* @__PURE__ */ jsxs(Text, { size: 1, children: [
|
|
1336
1370
|
asset.filename,
|
|
@@ -1338,15 +1372,11 @@ var DialogDelete = ({ asset, onDeleted, onClose }) => {
|
|
|
1338
1372
|
asset.renditions.length,
|
|
1339
1373
|
" renditions,",
|
|
1340
1374
|
" ",
|
|
1341
|
-
formatSize(
|
|
1375
|
+
formatSize(totalSize(asset.renditions)),
|
|
1342
1376
|
", plus its poster."
|
|
1343
1377
|
] }),
|
|
1344
|
-
blockers === null && !error && /* @__PURE__ */
|
|
1345
|
-
|
|
1346
|
-
/* @__PURE__ */ jsx(Text, { muted: true, size: 1, children: "Checking where it's used\u2026" })
|
|
1347
|
-
] }),
|
|
1348
|
-
isBlocked && /* @__PURE__ */ jsx(Card, { padding: 3, radius: 2, tone: "caution", children: /* @__PURE__ */ jsxs(Stack, { gap: 3, children: [
|
|
1349
|
-
/* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: "Still in use" }),
|
|
1378
|
+
blockers === null && !error && /* @__PURE__ */ jsx(Loading, { children: "Checking where it's used\u2026" }),
|
|
1379
|
+
isBlocked && /* @__PURE__ */ jsx(Notice, { title: "Still in use", tone: "caution", children: /* @__PURE__ */ jsxs(Stack, { gap: 3, children: [
|
|
1350
1380
|
/* @__PURE__ */ jsx(Text, { muted: true, size: 1, children: "Remove it from these documents first:" }),
|
|
1351
1381
|
blockers.map((blocker) => /* @__PURE__ */ jsxs(Text, { muted: true, size: 1, children: [
|
|
1352
1382
|
blocker.title || blocker._id,
|
|
@@ -1356,36 +1386,25 @@ var DialogDelete = ({ asset, onDeleted, onClose }) => {
|
|
|
1356
1386
|
] }, blocker._id))
|
|
1357
1387
|
] }) }),
|
|
1358
1388
|
blockers !== null && blockers.length === 0 && /* @__PURE__ */ jsx(Text, { muted: true, size: 1, children: "Nothing references it. This can't be undone." }),
|
|
1359
|
-
error && /* @__PURE__ */ jsx(
|
|
1389
|
+
error && /* @__PURE__ */ jsx(Notice, { tone: "critical", children: error })
|
|
1360
1390
|
] }) })
|
|
1361
1391
|
}
|
|
1362
1392
|
);
|
|
1363
1393
|
};
|
|
1364
|
-
var Field2 = ({ label, children }) => {
|
|
1365
|
-
return /* @__PURE__ */ jsxs(Stack, { gap: 3, children: [
|
|
1366
|
-
/* @__PURE__ */ jsx(Text, { muted: true, size: 1, weight: "medium", children: label }),
|
|
1367
|
-
children
|
|
1368
|
-
] });
|
|
1369
|
-
};
|
|
1370
1394
|
var DialogDetails = ({
|
|
1371
1395
|
asset,
|
|
1372
1396
|
onChanged,
|
|
1373
1397
|
onDelete,
|
|
1374
1398
|
onClose
|
|
1375
1399
|
}) => {
|
|
1376
|
-
const
|
|
1377
|
-
const
|
|
1378
|
-
const [folders, setFolders] = useState([]);
|
|
1400
|
+
const { client } = useR2VideoClient();
|
|
1401
|
+
const { paths } = useFolders();
|
|
1379
1402
|
const [folderId, setFolderId] = useState(
|
|
1380
1403
|
asset.folder ? asset.folder._ref : ""
|
|
1381
1404
|
);
|
|
1382
1405
|
const [filename, setFilename] = useState(asset.filename);
|
|
1383
1406
|
const [isSaving, setIsSaving] = useState(false);
|
|
1384
1407
|
const [error, setError] = useState(null);
|
|
1385
|
-
useEffect(() => {
|
|
1386
|
-
fetchFolders(client, config.folders.type).then(setFolders);
|
|
1387
|
-
}, [client, config.folders.type]);
|
|
1388
|
-
const paths = resolveFolderPaths(folders);
|
|
1389
1408
|
const isBroken = asset.renditions.length === 0;
|
|
1390
1409
|
const trimmed = filename.trim();
|
|
1391
1410
|
const savedFolderId = asset.folder ? asset.folder._ref : "";
|
|
@@ -1426,7 +1445,7 @@ var DialogDetails = ({
|
|
|
1426
1445
|
onChanged();
|
|
1427
1446
|
} catch (caught) {
|
|
1428
1447
|
reset();
|
|
1429
|
-
setError(
|
|
1448
|
+
setError(toMessage(caught));
|
|
1430
1449
|
}
|
|
1431
1450
|
setIsSaving(false);
|
|
1432
1451
|
};
|
|
@@ -1437,40 +1456,34 @@ var DialogDetails = ({
|
|
|
1437
1456
|
id: "r2-video-details",
|
|
1438
1457
|
width: 1,
|
|
1439
1458
|
onClose: isSaving ? void 0 : onClose,
|
|
1440
|
-
footer: /* @__PURE__ */ jsx(
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
{
|
|
1455
|
-
disabled: isSaving,
|
|
1456
|
-
mode: "ghost",
|
|
1459
|
+
footer: /* @__PURE__ */ jsx(
|
|
1460
|
+
DialogActions,
|
|
1461
|
+
{
|
|
1462
|
+
aside: /* @__PURE__ */ jsx(
|
|
1463
|
+
Button,
|
|
1464
|
+
{
|
|
1465
|
+
"aria-label": "Delete video",
|
|
1466
|
+
disabled: isSaving,
|
|
1467
|
+
icon: TrashIcon,
|
|
1468
|
+
mode: "ghost",
|
|
1469
|
+
tone: "critical",
|
|
1470
|
+
onClick: onDelete
|
|
1471
|
+
}
|
|
1472
|
+
),
|
|
1473
|
+
cancel: {
|
|
1457
1474
|
text: isDirty ? "Discard" : "Close",
|
|
1458
|
-
|
|
1475
|
+
disabled: isSaving,
|
|
1459
1476
|
onClick: isDirty ? reset : onClose
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
!isBroken && /* @__PURE__ */ jsx(Box, { flex: 1, children: /* @__PURE__ */ jsx(
|
|
1463
|
-
Button,
|
|
1464
|
-
{
|
|
1465
|
-
disabled: isSaving || !isDirty,
|
|
1477
|
+
},
|
|
1478
|
+
confirm: isBroken ? void 0 : {
|
|
1466
1479
|
text: isSaving ? "Saving\u2026" : "Save changes",
|
|
1467
1480
|
tone: "primary",
|
|
1468
|
-
|
|
1481
|
+
disabled: isSaving || !isDirty,
|
|
1469
1482
|
onClick: save
|
|
1470
1483
|
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
children: /* @__PURE__ */ jsx(Card, { padding: 4, children: isBroken ? /* @__PURE__ */ jsx(
|
|
1484
|
+
}
|
|
1485
|
+
),
|
|
1486
|
+
children: /* @__PURE__ */ jsx(Card, { padding: 4, children: isBroken ? /* @__PURE__ */ jsx(Notice, { tone: "caution", children: "This video was never finished uploading, so it has no renditions and nothing stored behind it. Delete it." }) : /* @__PURE__ */ jsxs(Stack, { gap: 5, children: [
|
|
1474
1487
|
/* @__PURE__ */ jsx(
|
|
1475
1488
|
VideoPreview,
|
|
1476
1489
|
{
|
|
@@ -1479,7 +1492,7 @@ var DialogDetails = ({
|
|
|
1479
1492
|
}
|
|
1480
1493
|
),
|
|
1481
1494
|
/* @__PURE__ */ jsxs(Flex, { gap: 3, children: [
|
|
1482
|
-
/* @__PURE__ */ jsx(Box, { flex: 1, children: /* @__PURE__ */ jsx(
|
|
1495
|
+
/* @__PURE__ */ jsx(Box, { flex: 1, children: /* @__PURE__ */ jsx(Field, { label: "Filename", children: /* @__PURE__ */ jsx(
|
|
1483
1496
|
TextInput,
|
|
1484
1497
|
{
|
|
1485
1498
|
"aria-label": "Filename",
|
|
@@ -1493,7 +1506,7 @@ var DialogDetails = ({
|
|
|
1493
1506
|
}
|
|
1494
1507
|
}
|
|
1495
1508
|
) }) }),
|
|
1496
|
-
/* @__PURE__ */ jsx(Box, { flex: 1, children: /* @__PURE__ */ jsx(
|
|
1509
|
+
/* @__PURE__ */ jsx(Box, { flex: 1, children: /* @__PURE__ */ jsx(Field, { label: "Folder", children: /* @__PURE__ */ jsxs(
|
|
1497
1510
|
Select,
|
|
1498
1511
|
{
|
|
1499
1512
|
"aria-label": "Folder",
|
|
@@ -1507,7 +1520,7 @@ var DialogDetails = ({
|
|
|
1507
1520
|
}
|
|
1508
1521
|
) }) })
|
|
1509
1522
|
] }),
|
|
1510
|
-
error && /* @__PURE__ */ jsx(
|
|
1523
|
+
error && /* @__PURE__ */ jsx(Notice, { tone: "critical", children: error }),
|
|
1511
1524
|
/* @__PURE__ */ jsx(
|
|
1512
1525
|
VideoSummary,
|
|
1513
1526
|
{
|
|
@@ -1556,19 +1569,15 @@ var removeOrphans = async (client, config, orphans) => {
|
|
|
1556
1569
|
}
|
|
1557
1570
|
}
|
|
1558
1571
|
};
|
|
1559
|
-
var countLabel2 = (count, noun) => {
|
|
1560
|
-
return `${count} ${noun}${count === 1 ? "" : "s"}`;
|
|
1561
|
-
};
|
|
1562
1572
|
var DialogOrphans = ({ onCleaned, onClose }) => {
|
|
1563
|
-
const config =
|
|
1564
|
-
const client = useClient({ apiVersion: config.apiVersion });
|
|
1573
|
+
const { config, client } = useR2VideoClient();
|
|
1565
1574
|
const [orphans, setOrphans] = useState(null);
|
|
1566
1575
|
const [isRemoving, setIsRemoving] = useState(false);
|
|
1567
1576
|
const [isDone, setIsDone] = useState(false);
|
|
1568
1577
|
const [error, setError] = useState(null);
|
|
1569
1578
|
useEffect(() => {
|
|
1570
1579
|
findOrphans(client, config).then(setOrphans).catch((caught) => {
|
|
1571
|
-
setError(
|
|
1580
|
+
setError(toMessage(caught));
|
|
1572
1581
|
});
|
|
1573
1582
|
}, [client, config]);
|
|
1574
1583
|
const confirmed = async () => {
|
|
@@ -1582,7 +1591,7 @@ var DialogOrphans = ({ onCleaned, onClose }) => {
|
|
|
1582
1591
|
setIsDone(true);
|
|
1583
1592
|
onCleaned();
|
|
1584
1593
|
} catch (caught) {
|
|
1585
|
-
setError(
|
|
1594
|
+
setError(toMessage(caught));
|
|
1586
1595
|
}
|
|
1587
1596
|
setIsRemoving(false);
|
|
1588
1597
|
};
|
|
@@ -1594,46 +1603,37 @@ var DialogOrphans = ({ onCleaned, onClose }) => {
|
|
|
1594
1603
|
id: "r2-video-orphans",
|
|
1595
1604
|
width: 1,
|
|
1596
1605
|
onClose: isRemoving ? void 0 : onClose,
|
|
1597
|
-
footer: /* @__PURE__ */ jsx(
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
{
|
|
1601
|
-
disabled: isRemoving,
|
|
1602
|
-
mode: "ghost",
|
|
1606
|
+
footer: /* @__PURE__ */ jsx(
|
|
1607
|
+
DialogActions,
|
|
1608
|
+
{
|
|
1609
|
+
cancel: {
|
|
1603
1610
|
text: isDone ? "Done" : "Cancel",
|
|
1604
|
-
|
|
1611
|
+
disabled: isRemoving,
|
|
1605
1612
|
onClick: onClose
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
|
-
/* @__PURE__ */ jsx(Box, { flex: 1, children: /* @__PURE__ */ jsx(
|
|
1609
|
-
Button,
|
|
1610
|
-
{
|
|
1611
|
-
disabled: isRemoving || isDone || total === 0,
|
|
1613
|
+
},
|
|
1614
|
+
confirm: {
|
|
1612
1615
|
text: isRemoving ? "Removing\u2026" : "Remove",
|
|
1613
1616
|
tone: "critical",
|
|
1614
|
-
|
|
1617
|
+
disabled: isRemoving || isDone || total === 0,
|
|
1615
1618
|
onClick: confirmed
|
|
1616
1619
|
}
|
|
1617
|
-
|
|
1618
|
-
|
|
1620
|
+
}
|
|
1621
|
+
),
|
|
1619
1622
|
children: /* @__PURE__ */ jsx(Card, { padding: 4, children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
1620
|
-
!orphans && !error && /* @__PURE__ */
|
|
1621
|
-
|
|
1622
|
-
/* @__PURE__ */ jsx(Text, { muted: true, size: 1, children: "Comparing storage against the library\u2026" })
|
|
1623
|
-
] }),
|
|
1624
|
-
isDone && /* @__PURE__ */ jsx(Card, { padding: 3, radius: 2, tone: "positive", children: /* @__PURE__ */ jsx(Text, { size: 1, children: "Removed." }) }),
|
|
1623
|
+
!orphans && !error && /* @__PURE__ */ jsx(Loading, { children: "Comparing storage against the library\u2026" }),
|
|
1624
|
+
isDone && /* @__PURE__ */ jsx(Notice, { tone: "positive", children: "Removed." }),
|
|
1625
1625
|
orphans && !isDone && total === 0 && /* @__PURE__ */ jsx(Text, { muted: true, size: 1, children: "Nothing to clean up - every stored file belongs to a video." }),
|
|
1626
1626
|
orphans && !isDone && total > 0 && /* @__PURE__ */ jsxs(Stack, { gap: 3, children: [
|
|
1627
1627
|
/* @__PURE__ */ jsxs(Text, { size: 1, children: [
|
|
1628
1628
|
"Found ",
|
|
1629
|
-
|
|
1629
|
+
pluralize(orphans.keys.length, "orphaned file"),
|
|
1630
1630
|
" in R2 and ",
|
|
1631
|
-
|
|
1631
|
+
pluralize(orphans.posterIds.length, "unused poster"),
|
|
1632
1632
|
" in Sanity."
|
|
1633
1633
|
] }),
|
|
1634
1634
|
/* @__PURE__ */ jsx(Text, { muted: true, size: 1, children: "Nothing references any of them, so removing them can't affect the site. This can't be undone." })
|
|
1635
1635
|
] }),
|
|
1636
|
-
error && /* @__PURE__ */ jsx(
|
|
1636
|
+
error && /* @__PURE__ */ jsx(Notice, { tone: "critical", children: error })
|
|
1637
1637
|
] }) })
|
|
1638
1638
|
}
|
|
1639
1639
|
);
|
|
@@ -1738,8 +1738,7 @@ var matches = (asset, search) => {
|
|
|
1738
1738
|
return asset.filename.toLowerCase().includes(term) || folderName.toLowerCase().includes(term);
|
|
1739
1739
|
};
|
|
1740
1740
|
var ToolVideoLibrary = () => {
|
|
1741
|
-
const config =
|
|
1742
|
-
const client = useClient({ apiVersion: config.apiVersion });
|
|
1741
|
+
const { config, client } = useR2VideoClient();
|
|
1743
1742
|
const [assets, setAssets] = useState(null);
|
|
1744
1743
|
const [folders, setFolders] = useState([]);
|
|
1745
1744
|
const [search, setSearch] = useState("");
|
|
@@ -1826,10 +1825,7 @@ var ToolVideoLibrary = () => {
|
|
|
1826
1825
|
}
|
|
1827
1826
|
)
|
|
1828
1827
|
] }),
|
|
1829
|
-
assets === null && /* @__PURE__ */
|
|
1830
|
-
/* @__PURE__ */ jsx(Spinner, { muted: true }),
|
|
1831
|
-
/* @__PURE__ */ jsx(Text, { muted: true, size: 1, children: "Loading library\u2026" })
|
|
1832
|
-
] }),
|
|
1828
|
+
assets === null && /* @__PURE__ */ jsx(Box, { padding: 4, children: /* @__PURE__ */ jsx(Loading, { children: "Loading library\u2026" }) }),
|
|
1833
1829
|
assets !== null && visible.length === 0 && /* @__PURE__ */ jsx(Card, { padding: 5, radius: 2, tone: "transparent", children: /* @__PURE__ */ jsx(Text, { align: "center", muted: true, size: 1, children: "No videos here yet." }) }),
|
|
1834
1830
|
/* @__PURE__ */ jsx(Grid, { gridTemplateColumns: [1, 2, 3, 4], gap: 3, children: visible.map((asset) => /* @__PURE__ */ jsx(
|
|
1835
1831
|
Card,
|