sanity-plugin-mux-input 2.15.0 → 2.16.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 +285 -118
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +286 -119
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/PlayerActionsMenu.tsx +14 -0
- package/src/components/ResyncMetadata.tsx +152 -73
- package/src/components/TextTracksManager.tsx +11 -55
- package/src/components/VideoDetails/VideoDetails.tsx +27 -11
- package/src/components/VideoDetails/useVideoDetails.ts +15 -1
- package/src/hooks/useResyncAsset.ts +110 -0
- package/src/hooks/useResyncMuxMetadata.ts +33 -0
- package/src/schema.ts +5 -0
- package/src/util/addKeysToMuxData.ts +30 -0
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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 { 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,
|
|
4
|
+
import { Dialog, Stack, Card, Text, Button, useTheme_v2, Flex, Box, TextInput, Checkbox, Code, Inline, Spinner, Heading, Label as Label$1, Radio, MenuButton, Menu, MenuItem, useToast, Autocomplete, Tooltip, TabList, Tab, TabPanel, Grid, useClickOutsideEvent, Popover, MenuDivider, rem } from "@sanity/ui";
|
|
5
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";
|
|
@@ -1321,6 +1321,26 @@ const PageSelector = (props) => {
|
|
|
1321
1321
|
)
|
|
1322
1322
|
] });
|
|
1323
1323
|
};
|
|
1324
|
+
function addKeysToMuxData(data) {
|
|
1325
|
+
return {
|
|
1326
|
+
...data,
|
|
1327
|
+
tracks: data.tracks?.map((track) => ({
|
|
1328
|
+
...track,
|
|
1329
|
+
_key: uuid()
|
|
1330
|
+
})),
|
|
1331
|
+
playback_ids: data.playback_ids?.map((playbackId) => ({
|
|
1332
|
+
...playbackId,
|
|
1333
|
+
_key: uuid()
|
|
1334
|
+
})),
|
|
1335
|
+
static_renditions: data.static_renditions ? {
|
|
1336
|
+
...data.static_renditions,
|
|
1337
|
+
files: data.static_renditions.files?.map((file) => ({
|
|
1338
|
+
...file,
|
|
1339
|
+
_key: uuid()
|
|
1340
|
+
}))
|
|
1341
|
+
} : void 0
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1324
1344
|
function useResyncMuxMetadata() {
|
|
1325
1345
|
const documentStore = useDocumentStore(), client = useClient$1({
|
|
1326
1346
|
apiVersion: SANITY_API_VERSION
|
|
@@ -1369,6 +1389,27 @@ function useResyncMuxMetadata() {
|
|
|
1369
1389
|
}
|
|
1370
1390
|
}
|
|
1371
1391
|
}
|
|
1392
|
+
async function syncFullData() {
|
|
1393
|
+
if (matchedAssets) {
|
|
1394
|
+
setResyncState("syncing");
|
|
1395
|
+
try {
|
|
1396
|
+
const tx = client.transaction();
|
|
1397
|
+
matchedAssets.forEach((matched) => {
|
|
1398
|
+
if (!matched.muxAsset) return;
|
|
1399
|
+
const dataWithKeys = addKeysToMuxData(matched.muxAsset);
|
|
1400
|
+
tx.patch(matched.sanityDoc._id, {
|
|
1401
|
+
set: {
|
|
1402
|
+
filename: matched.muxTitle || matched.currentTitle || "",
|
|
1403
|
+
status: matched.muxAsset.status,
|
|
1404
|
+
data: dataWithKeys
|
|
1405
|
+
}
|
|
1406
|
+
});
|
|
1407
|
+
}), await tx.commit({ returnDocuments: !1 }), setResyncState("done");
|
|
1408
|
+
} catch (error) {
|
|
1409
|
+
setResyncState("error"), setResyncError(error);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1372
1413
|
return {
|
|
1373
1414
|
sanityAssetsLoading,
|
|
1374
1415
|
closeDialog,
|
|
@@ -1378,6 +1419,7 @@ function useResyncMuxMetadata() {
|
|
|
1378
1419
|
hasSecrets,
|
|
1379
1420
|
syncAllVideos,
|
|
1380
1421
|
syncOnlyEmpty,
|
|
1422
|
+
syncFullData,
|
|
1381
1423
|
matchedAssets,
|
|
1382
1424
|
muxAssets,
|
|
1383
1425
|
openDialog
|
|
@@ -1393,22 +1435,78 @@ const useSanityAssets = createHookFromObservableFactory(
|
|
|
1393
1435
|
}
|
|
1394
1436
|
)
|
|
1395
1437
|
);
|
|
1438
|
+
function OptionCard({
|
|
1439
|
+
id,
|
|
1440
|
+
selected,
|
|
1441
|
+
onSelect,
|
|
1442
|
+
title,
|
|
1443
|
+
count,
|
|
1444
|
+
description,
|
|
1445
|
+
disabled
|
|
1446
|
+
}) {
|
|
1447
|
+
return /* @__PURE__ */ jsx(
|
|
1448
|
+
Card,
|
|
1449
|
+
{
|
|
1450
|
+
as: "label",
|
|
1451
|
+
padding: 3,
|
|
1452
|
+
radius: 2,
|
|
1453
|
+
border: !0,
|
|
1454
|
+
tone: selected ? "primary" : "default",
|
|
1455
|
+
style: {
|
|
1456
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
1457
|
+
opacity: disabled ? 0.5 : 1
|
|
1458
|
+
},
|
|
1459
|
+
children: /* @__PURE__ */ jsxs(Flex, { gap: 3, align: "flex-start", children: [
|
|
1460
|
+
/* @__PURE__ */ jsx(Box, { paddingTop: 1, children: /* @__PURE__ */ jsx(
|
|
1461
|
+
Radio,
|
|
1462
|
+
{
|
|
1463
|
+
checked: selected,
|
|
1464
|
+
onChange: () => onSelect(id),
|
|
1465
|
+
disabled,
|
|
1466
|
+
name: "sync-option"
|
|
1467
|
+
}
|
|
1468
|
+
) }),
|
|
1469
|
+
/* @__PURE__ */ jsxs(Stack, { space: 2, flex: 1, children: [
|
|
1470
|
+
/* @__PURE__ */ jsx(Flex, { align: "center", gap: 2, children: /* @__PURE__ */ jsxs(Text, { size: 2, weight: "semibold", children: [
|
|
1471
|
+
title,
|
|
1472
|
+
" (",
|
|
1473
|
+
count,
|
|
1474
|
+
")"
|
|
1475
|
+
] }) }),
|
|
1476
|
+
/* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: description })
|
|
1477
|
+
] })
|
|
1478
|
+
] })
|
|
1479
|
+
}
|
|
1480
|
+
);
|
|
1481
|
+
}
|
|
1396
1482
|
function ResyncMetadataDialog(props) {
|
|
1397
|
-
const { resyncState } = props,
|
|
1483
|
+
const { resyncState } = props, videosToUpdate = props.matchedAssets?.filter((m) => m.muxAsset).length || 0, videosWithEmptyOrPlaceholder = props.matchedAssets?.filter(
|
|
1398
1484
|
(m) => m.muxAsset && m.muxTitle && isEmptyOrPlaceholderTitle(m.currentTitle, m.muxAsset.id)
|
|
1399
|
-
).length || 0
|
|
1485
|
+
).length || 0, hasEmptyTitles = videosWithEmptyOrPlaceholder > 0, defaultOption = hasEmptyTitles ? "fillEmpty" : "syncTitles", [selectedOption, setSelectedOption] = useState(defaultOption), canTriggerResync = resyncState === "idle" || resyncState === "error", isResyncing = resyncState === "syncing", isDone = resyncState === "done", isLoading = props.muxAssets.loading || props.sanityAssetsLoading, handleSync = () => {
|
|
1486
|
+
switch (selectedOption) {
|
|
1487
|
+
case "fillEmpty":
|
|
1488
|
+
props.syncOnlyEmpty();
|
|
1489
|
+
break;
|
|
1490
|
+
case "syncTitles":
|
|
1491
|
+
props.syncAllVideos();
|
|
1492
|
+
break;
|
|
1493
|
+
case "fullResync":
|
|
1494
|
+
props.syncFullData();
|
|
1495
|
+
break;
|
|
1496
|
+
}
|
|
1497
|
+
};
|
|
1400
1498
|
return /* @__PURE__ */ jsx(
|
|
1401
1499
|
Dialog,
|
|
1402
1500
|
{
|
|
1403
1501
|
animate: !0,
|
|
1404
|
-
header: "
|
|
1502
|
+
header: "Sync with Mux",
|
|
1405
1503
|
zOffset: DIALOGS_Z_INDEX,
|
|
1406
1504
|
id: "resync-metadata-dialog",
|
|
1407
1505
|
onClose: props.closeDialog,
|
|
1408
1506
|
onClickOutside: props.closeDialog,
|
|
1409
1507
|
width: 1,
|
|
1410
1508
|
position: "fixed",
|
|
1411
|
-
footer: !isDone && /* @__PURE__ */ jsx(Card, { padding: 3, children: /* @__PURE__ */ jsxs(Flex, { justify: "
|
|
1509
|
+
footer: !isDone && /* @__PURE__ */ jsx(Card, { padding: 3, children: /* @__PURE__ */ jsxs(Flex, { justify: "flex-end", gap: 2, children: [
|
|
1412
1510
|
/* @__PURE__ */ jsx(
|
|
1413
1511
|
Button,
|
|
1414
1512
|
{
|
|
@@ -1416,97 +1514,104 @@ function ResyncMetadataDialog(props) {
|
|
|
1416
1514
|
padding: 3,
|
|
1417
1515
|
mode: "ghost",
|
|
1418
1516
|
text: "Cancel",
|
|
1419
|
-
tone: "critical",
|
|
1420
1517
|
onClick: props.closeDialog,
|
|
1421
1518
|
disabled: isResyncing
|
|
1422
1519
|
}
|
|
1423
1520
|
),
|
|
1424
|
-
/* @__PURE__ */
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
/* @__PURE__ */ jsx(
|
|
1438
|
-
Button,
|
|
1439
|
-
{
|
|
1440
|
-
icon: SyncIcon,
|
|
1441
|
-
fontSize: 2,
|
|
1442
|
-
padding: 3,
|
|
1443
|
-
mode: "ghost",
|
|
1444
|
-
text: `Update all (${videosToUpdate})`,
|
|
1445
|
-
tone: "positive",
|
|
1446
|
-
onClick: props.syncAllVideos,
|
|
1447
|
-
iconRight: isResyncing && Spinner,
|
|
1448
|
-
disabled: !canTriggerResync
|
|
1449
|
-
}
|
|
1450
|
-
)
|
|
1451
|
-
] })
|
|
1521
|
+
/* @__PURE__ */ jsx(
|
|
1522
|
+
Button,
|
|
1523
|
+
{
|
|
1524
|
+
icon: SyncIcon,
|
|
1525
|
+
fontSize: 2,
|
|
1526
|
+
padding: 3,
|
|
1527
|
+
text: "Run sync",
|
|
1528
|
+
tone: "primary",
|
|
1529
|
+
onClick: handleSync,
|
|
1530
|
+
iconRight: isResyncing && Spinner,
|
|
1531
|
+
disabled: !canTriggerResync || isLoading
|
|
1532
|
+
}
|
|
1533
|
+
)
|
|
1452
1534
|
] }) }),
|
|
1453
1535
|
children: /* @__PURE__ */ jsxs(Box, { padding: 4, children: [
|
|
1454
|
-
|
|
1536
|
+
isLoading && /* @__PURE__ */ jsx(Card, { tone: "primary", marginBottom: 4, padding: 3, border: !0, radius: 2, children: /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 4, children: [
|
|
1455
1537
|
/* @__PURE__ */ jsx(Spinner, { muted: !0, size: 4 }),
|
|
1456
1538
|
/* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
1457
1539
|
/* @__PURE__ */ jsx(Text, { size: 2, weight: "semibold", children: "Loading assets from Mux" }),
|
|
1458
|
-
/* @__PURE__ */ jsx(Text, { size: 1, children: "This may take a while." })
|
|
1540
|
+
/* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "This may take a while." })
|
|
1459
1541
|
] })
|
|
1460
1542
|
] }) }),
|
|
1461
|
-
props.muxAssets.error && /* @__PURE__ */ jsx(Card, { tone: "critical", marginBottom:
|
|
1543
|
+
props.muxAssets.error && /* @__PURE__ */ jsx(Card, { tone: "critical", marginBottom: 4, padding: 3, border: !0, radius: 2, children: /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
|
|
1462
1544
|
/* @__PURE__ */ jsx(ErrorOutlineIcon, { fontSize: 36 }),
|
|
1463
1545
|
/* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
1464
1546
|
/* @__PURE__ */ jsx(Text, { size: 2, weight: "semibold", children: "There was an error getting data from Mux" }),
|
|
1465
1547
|
/* @__PURE__ */ jsx(Text, { size: 1, children: "Please try again or contact a developer for help." })
|
|
1466
1548
|
] })
|
|
1467
1549
|
] }) }),
|
|
1468
|
-
resyncState === "syncing" && /* @__PURE__ */ jsx(Card, { tone: "primary", marginBottom:
|
|
1550
|
+
resyncState === "syncing" && /* @__PURE__ */ jsx(Card, { tone: "primary", marginBottom: 4, padding: 3, border: !0, radius: 2, children: /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 4, children: [
|
|
1469
1551
|
/* @__PURE__ */ jsx(Spinner, { muted: !0, size: 4 }),
|
|
1470
1552
|
/* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
1471
|
-
/* @__PURE__ */ jsx(Text, { size: 2, weight: "semibold", children: "
|
|
1472
|
-
/* @__PURE__ */ jsx(Text, { size: 1, children: "
|
|
1553
|
+
/* @__PURE__ */ jsx(Text, { size: 2, weight: "semibold", children: "Syncing metadata" }),
|
|
1554
|
+
/* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "Updating videos from Mux..." })
|
|
1473
1555
|
] })
|
|
1474
1556
|
] }) }),
|
|
1475
|
-
resyncState === "error" && /* @__PURE__ */ jsx(Card, { tone: "critical", marginBottom:
|
|
1557
|
+
resyncState === "error" && /* @__PURE__ */ jsx(Card, { tone: "critical", marginBottom: 4, padding: 3, border: !0, radius: 2, children: /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
|
|
1476
1558
|
/* @__PURE__ */ jsx(ErrorOutlineIcon, { fontSize: 36 }),
|
|
1477
1559
|
/* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
1478
1560
|
/* @__PURE__ */ jsx(Text, { size: 2, weight: "semibold", children: "There was an error syncing metadata" }),
|
|
1479
1561
|
/* @__PURE__ */ jsx(Text, { size: 1, children: props.resyncError ? `Error: ${props.resyncError}` : "Please try again or contact a developer for help." })
|
|
1480
1562
|
] })
|
|
1481
1563
|
] }) }),
|
|
1482
|
-
resyncState === "done" && /* @__PURE__ */ jsxs(Stack, { paddingY: 5,
|
|
1564
|
+
resyncState === "done" && /* @__PURE__ */ jsxs(Stack, { paddingY: 5, space: 3, style: { textAlign: "center" }, children: [
|
|
1483
1565
|
/* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(CheckmarkCircleIcon, { fontSize: 48 }) }),
|
|
1484
|
-
/* @__PURE__ */ jsx(Heading, { size: 2, children: "
|
|
1485
|
-
/* @__PURE__ */ jsx(Text, { size: 2, children: "
|
|
1566
|
+
/* @__PURE__ */ jsx(Heading, { size: 2, children: "Sync completed" }),
|
|
1567
|
+
/* @__PURE__ */ jsx(Text, { size: 2, muted: !0, children: "Videos have been updated from Mux." })
|
|
1486
1568
|
] }),
|
|
1487
|
-
|
|
1488
|
-
/* @__PURE__ */ jsxs(
|
|
1489
|
-
"
|
|
1490
|
-
videosToUpdate === 1 ? "is" : "are",
|
|
1491
|
-
" ",
|
|
1569
|
+
!isDone && !isLoading && !props.muxAssets.error && /* @__PURE__ */ jsxs(Stack, { space: 4, children: [
|
|
1570
|
+
/* @__PURE__ */ jsxs(Text, { size: 1, muted: !0, children: [
|
|
1571
|
+
"Found ",
|
|
1492
1572
|
videosToUpdate,
|
|
1493
1573
|
" video",
|
|
1494
1574
|
videosToUpdate === 1 ? "" : "s",
|
|
1495
|
-
"
|
|
1575
|
+
" linked to Mux."
|
|
1496
1576
|
] }),
|
|
1497
|
-
/* @__PURE__ */
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
"
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1577
|
+
/* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
1578
|
+
hasEmptyTitles && /* @__PURE__ */ jsx(
|
|
1579
|
+
OptionCard,
|
|
1580
|
+
{
|
|
1581
|
+
id: "fillEmpty",
|
|
1582
|
+
selected: selectedOption === "fillEmpty",
|
|
1583
|
+
onSelect: setSelectedOption,
|
|
1584
|
+
title: "Fill missing titles only",
|
|
1585
|
+
count: videosWithEmptyOrPlaceholder,
|
|
1586
|
+
description: "Updates only videos without a title or with placeholder titles (e.g., 'Asset #123') using the title from Mux.",
|
|
1587
|
+
disabled: isResyncing
|
|
1588
|
+
}
|
|
1589
|
+
),
|
|
1590
|
+
/* @__PURE__ */ jsx(
|
|
1591
|
+
OptionCard,
|
|
1592
|
+
{
|
|
1593
|
+
id: "syncTitles",
|
|
1594
|
+
selected: selectedOption === "syncTitles",
|
|
1595
|
+
onSelect: setSelectedOption,
|
|
1596
|
+
title: "Sync all titles",
|
|
1597
|
+
count: videosToUpdate,
|
|
1598
|
+
description: "Replaces the title in Sanity with the title from Mux for all videos.",
|
|
1599
|
+
disabled: isResyncing
|
|
1600
|
+
}
|
|
1601
|
+
),
|
|
1602
|
+
/* @__PURE__ */ jsx(
|
|
1603
|
+
OptionCard,
|
|
1604
|
+
{
|
|
1605
|
+
id: "fullResync",
|
|
1606
|
+
selected: selectedOption === "fullResync",
|
|
1607
|
+
onSelect: setSelectedOption,
|
|
1608
|
+
title: "Full resync",
|
|
1609
|
+
count: videosToUpdate,
|
|
1610
|
+
description: "Updates all fields from Mux including status, duration, tracks, captions, and renditions.",
|
|
1611
|
+
disabled: isResyncing
|
|
1612
|
+
}
|
|
1613
|
+
)
|
|
1614
|
+
] })
|
|
1510
1615
|
] })
|
|
1511
1616
|
] })
|
|
1512
1617
|
}
|
|
@@ -1515,7 +1620,7 @@ function ResyncMetadataDialog(props) {
|
|
|
1515
1620
|
function ResyncMetadata() {
|
|
1516
1621
|
const resyncMetadata = useResyncMuxMetadata();
|
|
1517
1622
|
if (resyncMetadata.hasSecrets)
|
|
1518
|
-
return resyncMetadata.dialogOpen ? /* @__PURE__ */ jsx(ResyncMetadataDialog, { ...resyncMetadata }) : /* @__PURE__ */ jsx(Button, { mode: "bleed", text: "
|
|
1623
|
+
return resyncMetadata.dialogOpen ? /* @__PURE__ */ jsx(ResyncMetadataDialog, { ...resyncMetadata }) : /* @__PURE__ */ jsx(Button, { mode: "bleed", text: "Sync with Mux", onClick: resyncMetadata.openDialog });
|
|
1519
1624
|
}
|
|
1520
1625
|
const CONTEXT_MENU_POPOVER_PROPS = {
|
|
1521
1626
|
constrainSize: !0,
|
|
@@ -1596,6 +1701,55 @@ function StopWatchIcon(props) {
|
|
|
1596
1701
|
}
|
|
1597
1702
|
);
|
|
1598
1703
|
}
|
|
1704
|
+
function useResyncAsset(options) {
|
|
1705
|
+
const client = useClient(), toast = useToast(), [resyncState, setResyncState] = useState("idle"), [resyncError, setResyncError] = useState(null), showToast = options?.showToast ?? !1, resyncAsset = useCallback(
|
|
1706
|
+
async (asset) => {
|
|
1707
|
+
if (!asset.assetId) {
|
|
1708
|
+
showToast && toast.push({
|
|
1709
|
+
title: "Cannot resync",
|
|
1710
|
+
description: "Asset has no Mux ID",
|
|
1711
|
+
status: "error"
|
|
1712
|
+
}), options?.onError?.(new Error("Asset has no Mux ID"));
|
|
1713
|
+
return;
|
|
1714
|
+
}
|
|
1715
|
+
if (!asset._id) {
|
|
1716
|
+
showToast && toast.push({
|
|
1717
|
+
title: "Cannot resync",
|
|
1718
|
+
description: "Asset has no document ID",
|
|
1719
|
+
status: "error"
|
|
1720
|
+
}), options?.onError?.(new Error("Asset has no document ID"));
|
|
1721
|
+
return;
|
|
1722
|
+
}
|
|
1723
|
+
setResyncState("syncing"), setResyncError(null);
|
|
1724
|
+
try {
|
|
1725
|
+
const muxData = (await getAsset(client, asset.assetId)).data, dataWithKeys = addKeysToMuxData(muxData);
|
|
1726
|
+
return await client.patch(asset._id).set({
|
|
1727
|
+
status: muxData.status,
|
|
1728
|
+
data: dataWithKeys,
|
|
1729
|
+
...muxData.meta?.title && { filename: muxData.meta.title }
|
|
1730
|
+
}).commit({ returnDocuments: !1 }), setResyncState("success"), showToast && toast.push({
|
|
1731
|
+
title: "Asset synced",
|
|
1732
|
+
description: "Data has been updated from Mux",
|
|
1733
|
+
status: "success"
|
|
1734
|
+
}), options?.onSuccess?.(muxData), muxData;
|
|
1735
|
+
} catch (error) {
|
|
1736
|
+
setResyncState("error"), setResyncError(error), console.error("Failed to refresh asset data:", error), showToast && toast.push({
|
|
1737
|
+
title: "Sync failed",
|
|
1738
|
+
description: "Could not sync asset from Mux",
|
|
1739
|
+
status: "error"
|
|
1740
|
+
}), options?.onError?.(error);
|
|
1741
|
+
return;
|
|
1742
|
+
}
|
|
1743
|
+
},
|
|
1744
|
+
[client, toast, options, showToast]
|
|
1745
|
+
);
|
|
1746
|
+
return {
|
|
1747
|
+
resyncState,
|
|
1748
|
+
resyncError,
|
|
1749
|
+
resyncAsset,
|
|
1750
|
+
isResyncing: resyncState === "syncing"
|
|
1751
|
+
};
|
|
1752
|
+
}
|
|
1599
1753
|
function extractErrorMessage(error, defaultMessage = "Failed to process request") {
|
|
1600
1754
|
let message = "";
|
|
1601
1755
|
if (error && typeof error == "object") {
|
|
@@ -2498,20 +2652,7 @@ function TextTracksManager({
|
|
|
2498
2652
|
tracks: propTracks,
|
|
2499
2653
|
collapseTracks = !1
|
|
2500
2654
|
}) {
|
|
2501
|
-
const client = useClient(), toast = useToast(), dialogId = `DeleteCaptionDialog${useId()}`, [downloadingTrackId, setDownloadingTrackId] = useState(null), [deletingTrackId, setDeletingTrackId] = useState(null), [addedTracks, setAddedTracks] = useState([]), [updatedTracks, setUpdatedTracks] = useState(/* @__PURE__ */ new Map()), [trackActivityOrder, setTrackActivityOrder] = useState(/* @__PURE__ */ new Map()), [autogeneratedTrackIds, setAutogeneratedTrackIds] = useState(/* @__PURE__ */ new Set()), [trackToDelete, setTrackToDelete] = useState(null), [trackToEdit, setTrackToEdit] = useState(null), [showAddDialog, setShowAddDialog] = useState(!1), [isExpanded, setIsExpanded] = useState(!1), MAX_VISIBLE_TRACKS = 4
|
|
2502
|
-
useEffect(() => {
|
|
2503
|
-
if (!asset.assetId || !asset._id) return;
|
|
2504
|
-
const assetId = asset.assetId, documentId = asset._id;
|
|
2505
|
-
(async () => {
|
|
2506
|
-
try {
|
|
2507
|
-
const response = await getAsset(client, assetId);
|
|
2508
|
-
await client.patch(documentId).set({ data: response.data, status: response.data.status }).commit();
|
|
2509
|
-
} catch (error) {
|
|
2510
|
-
console.error("Failed to refresh asset data:", error);
|
|
2511
|
-
}
|
|
2512
|
-
})();
|
|
2513
|
-
}, [asset.assetId, asset._id, client]);
|
|
2514
|
-
const activeTracks = (propTracks || asset.data?.tracks?.filter((track) => track.type === "text") || []).filter(
|
|
2655
|
+
const client = useClient(), toast = useToast(), dialogId = `DeleteCaptionDialog${useId()}`, { resyncAsset } = useResyncAsset(), [downloadingTrackId, setDownloadingTrackId] = useState(null), [deletingTrackId, setDeletingTrackId] = useState(null), [addedTracks, setAddedTracks] = useState([]), [updatedTracks, setUpdatedTracks] = useState(/* @__PURE__ */ new Map()), [trackActivityOrder, setTrackActivityOrder] = useState(/* @__PURE__ */ new Map()), [autogeneratedTrackIds, setAutogeneratedTrackIds] = useState(/* @__PURE__ */ new Set()), [trackToDelete, setTrackToDelete] = useState(null), [trackToEdit, setTrackToEdit] = useState(null), [showAddDialog, setShowAddDialog] = useState(!1), [isExpanded, setIsExpanded] = useState(!1), MAX_VISIBLE_TRACKS = 4, activeTracks = (propTracks || asset.data?.tracks?.filter((track) => track.type === "text") || []).filter(
|
|
2515
2656
|
(track) => track.id && (track.status === "ready" || track.status === "preparing" || track.status === "errored")
|
|
2516
2657
|
), allTracks = useMemo(() => {
|
|
2517
2658
|
const tracksWithUpdates = activeTracks.map((track) => updatedTracks.get(track.id) || track), isMockTrackReplaced = (mockTrack, realTracksList) => !mockTrack.id || !mockTrack.id.startsWith("generating-") ? !1 : realTracksList.some((realTrack) => {
|
|
@@ -2542,11 +2683,11 @@ function TextTracksManager({
|
|
|
2542
2683
|
}, [activeTracks, addedTracks]), useEffect(() => {
|
|
2543
2684
|
if (allTracks.filter((track) => track.status === "preparing").length === 0 || !asset.assetId || !asset._id)
|
|
2544
2685
|
return;
|
|
2545
|
-
const
|
|
2686
|
+
const interval = setInterval(async () => {
|
|
2546
2687
|
try {
|
|
2547
|
-
const
|
|
2548
|
-
|
|
2549
|
-
const fetchedTracks =
|
|
2688
|
+
const muxData = await resyncAsset(asset);
|
|
2689
|
+
if (!muxData) return;
|
|
2690
|
+
const fetchedTracks = muxData.tracks?.filter((track) => track.type === "text") || [], isMockTrackReplaced = (mockTrack, fetchedTracksList) => !mockTrack.id || !mockTrack.id.startsWith("generating-") ? !1 : fetchedTracksList.some((realTrack) => {
|
|
2550
2691
|
const nameMatches = realTrack.name === mockTrack.name, languageMatches = realTrack.language_code === mockTrack.language_code;
|
|
2551
2692
|
return !nameMatches || !languageMatches ? !1 : realTrack.status === "ready" ? realTrack.text_source === "generated_live" || realTrack.text_source === "generated_live_final" || realTrack.text_source === "generated_vod" : realTrack.status === "preparing";
|
|
2552
2693
|
}), newAutogeneratedIds = /* @__PURE__ */ new Set();
|
|
@@ -2583,7 +2724,7 @@ function TextTracksManager({
|
|
|
2583
2724
|
}
|
|
2584
2725
|
}, 3e3);
|
|
2585
2726
|
return () => clearInterval(interval);
|
|
2586
|
-
}, [allTracks, asset
|
|
2727
|
+
}, [allTracks, asset, resyncAsset]);
|
|
2587
2728
|
const visibleTracks = allTracks.filter(
|
|
2588
2729
|
(track) => track.status === "ready" || track.status === "preparing" || track.status === "errored"
|
|
2589
2730
|
).sort((a2, b) => {
|
|
@@ -2619,14 +2760,7 @@ function TextTracksManager({
|
|
|
2619
2760
|
try {
|
|
2620
2761
|
if (!asset.assetId)
|
|
2621
2762
|
throw new Error("Asset ID is required");
|
|
2622
|
-
|
|
2623
|
-
try {
|
|
2624
|
-
const response = await getAsset(client, asset.assetId);
|
|
2625
|
-
await client.patch(asset._id).set({ data: response.data, status: response.data.status }).commit();
|
|
2626
|
-
} catch (refreshError) {
|
|
2627
|
-
console.error("Failed to refresh asset data:", refreshError);
|
|
2628
|
-
}
|
|
2629
|
-
toast.push({
|
|
2763
|
+
await deleteTextTrack(client, asset.assetId, track.id), await resyncAsset(asset), toast.push({
|
|
2630
2764
|
title: "Successfully deleted caption track",
|
|
2631
2765
|
status: "success"
|
|
2632
2766
|
}), setAddedTracks((prev) => prev.filter((t) => t.id !== track.id)), setUpdatedTracks((prev) => {
|
|
@@ -2654,7 +2788,7 @@ function TextTracksManager({
|
|
|
2654
2788
|
return newMap.set(track.id, prev.size + 1), newMap;
|
|
2655
2789
|
}), setShowAddDialog(!1);
|
|
2656
2790
|
}, handleUpdateTrack = async (updatedTrack, oldTrackId) => {
|
|
2657
|
-
|
|
2791
|
+
oldTrackId && (setAddedTracks((prev) => prev.filter((t) => t.id !== oldTrackId)), setUpdatedTracks((prev) => {
|
|
2658
2792
|
const newMap = new Map(prev);
|
|
2659
2793
|
return newMap.delete(oldTrackId), newMap;
|
|
2660
2794
|
}), setTrackActivityOrder((prev) => {
|
|
@@ -2669,13 +2803,7 @@ function TextTracksManager({
|
|
|
2669
2803
|
}), setTrackActivityOrder((prev) => {
|
|
2670
2804
|
const newMap = new Map(prev);
|
|
2671
2805
|
return newMap.set(updatedTrack.id, prev.size + 1), newMap;
|
|
2672
|
-
}), setTrackToEdit(null),
|
|
2673
|
-
try {
|
|
2674
|
-
const response = await getAsset(client, asset.assetId);
|
|
2675
|
-
await client.patch(asset._id).set({ data: response.data, status: response.data.status }).commit();
|
|
2676
|
-
} catch (refreshError) {
|
|
2677
|
-
console.error("Failed to refresh asset data:", refreshError);
|
|
2678
|
-
}
|
|
2806
|
+
}), setTrackToEdit(null), await resyncAsset(asset);
|
|
2679
2807
|
}, getTrackSourceLabel = (track) => track.id && track.id.startsWith("generating-") || track.id && autogeneratedTrackIds.has(track.id) || track.text_source === "generated_live_final" || track.text_source === "generated_live" || track.text_source === "generated_vod" ? "Auto-generated" : track.text_source === "uploaded" ? "Uploaded" : "Custom";
|
|
2680
2808
|
if (visibleTracks.length === 0 && !showAddDialog)
|
|
2681
2809
|
return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
@@ -3025,7 +3153,7 @@ function VideoPlayer({
|
|
|
3025
3153
|
crossOrigin: "anonymous",
|
|
3026
3154
|
metadata: {
|
|
3027
3155
|
player_name: "Sanity Admin Dashboard",
|
|
3028
|
-
player_version: "2.
|
|
3156
|
+
player_version: "2.16.0",
|
|
3029
3157
|
page_type: "Preview Player"
|
|
3030
3158
|
},
|
|
3031
3159
|
audio: isAudio,
|
|
@@ -3342,7 +3470,10 @@ function getVideoMetadata(doc) {
|
|
|
3342
3470
|
function useVideoDetails(props) {
|
|
3343
3471
|
const documentStore = useDocumentStore(), toast = useToast(), client = useClient(), [references, referencesLoading] = useDocReferences(
|
|
3344
3472
|
useMemo(() => ({ documentStore, id: props.asset._id }), [documentStore, props.asset._id])
|
|
3345
|
-
), [originalAsset, setOriginalAsset] = useState(() => props.asset), [filename, setFilename] = useState(props.asset.filename), modified = filename !== originalAsset.filename, displayInfo = getVideoMetadata({ ...props.asset, filename }), [state, setState] = useState("idle");
|
|
3473
|
+
), [originalAsset, setOriginalAsset] = useState(() => props.asset), [filename, setFilename] = useState(props.asset.filename), modified = filename !== originalAsset.filename, displayInfo = getVideoMetadata({ ...props.asset, filename }), [state, setState] = useState("idle"), { resyncAsset, isResyncing } = useResyncAsset({ showToast: !0 });
|
|
3474
|
+
async function handleResync() {
|
|
3475
|
+
state === "idle" && (setState("resyncing"), await resyncAsset(props.asset), setState("idle"));
|
|
3476
|
+
}
|
|
3346
3477
|
function handleClose() {
|
|
3347
3478
|
if (state === "idle") {
|
|
3348
3479
|
if (modified) {
|
|
@@ -3385,7 +3516,9 @@ function useVideoDetails(props) {
|
|
|
3385
3516
|
setState,
|
|
3386
3517
|
handleClose,
|
|
3387
3518
|
confirmClose,
|
|
3388
|
-
saveChanges
|
|
3519
|
+
saveChanges,
|
|
3520
|
+
handleResync,
|
|
3521
|
+
isResyncing
|
|
3389
3522
|
};
|
|
3390
3523
|
}
|
|
3391
3524
|
const AssetInput = (props) => /* @__PURE__ */ jsx(FormField$1, { title: props.label, description: props.description, inputId: props.label, children: /* @__PURE__ */ jsx(
|
|
@@ -3409,7 +3542,9 @@ const AssetInput = (props) => /* @__PURE__ */ jsx(FormField$1, { title: props.la
|
|
|
3409
3542
|
setState,
|
|
3410
3543
|
handleClose,
|
|
3411
3544
|
confirmClose,
|
|
3412
|
-
saveChanges
|
|
3545
|
+
saveChanges,
|
|
3546
|
+
handleResync,
|
|
3547
|
+
isResyncing
|
|
3413
3548
|
} = useVideoDetails(props), isSaving = state === "saving", [containerHeight, setContainerHeight] = useState(null), contentsRef = React.useRef(null);
|
|
3414
3549
|
return useEffect(() => {
|
|
3415
3550
|
!contentsRef.current || !("getBoundingClientRect" in contentsRef.current) || setContainerHeight(contentsRef.current.getBoundingClientRect().height);
|
|
@@ -3425,19 +3560,35 @@ const AssetInput = (props) => /* @__PURE__ */ jsx(FormField$1, { title: props.la
|
|
|
3425
3560
|
width: 2,
|
|
3426
3561
|
position: "fixed",
|
|
3427
3562
|
footer: /* @__PURE__ */ jsx(Card, { padding: 3, children: /* @__PURE__ */ jsxs(Flex, { justify: "space-between", align: "center", children: [
|
|
3428
|
-
/* @__PURE__ */
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3563
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
|
3564
|
+
/* @__PURE__ */ jsx(
|
|
3565
|
+
Button,
|
|
3566
|
+
{
|
|
3567
|
+
icon: TrashIcon,
|
|
3568
|
+
fontSize: 2,
|
|
3569
|
+
padding: 3,
|
|
3570
|
+
mode: "bleed",
|
|
3571
|
+
text: "Delete",
|
|
3572
|
+
tone: "critical",
|
|
3573
|
+
onClick: () => setState("deleting"),
|
|
3574
|
+
disabled: isSaving || isResyncing
|
|
3575
|
+
}
|
|
3576
|
+
),
|
|
3577
|
+
/* @__PURE__ */ jsx(
|
|
3578
|
+
Button,
|
|
3579
|
+
{
|
|
3580
|
+
icon: SyncIcon,
|
|
3581
|
+
fontSize: 2,
|
|
3582
|
+
padding: 3,
|
|
3583
|
+
mode: "bleed",
|
|
3584
|
+
text: "Resync",
|
|
3585
|
+
tone: "primary",
|
|
3586
|
+
onClick: handleResync,
|
|
3587
|
+
disabled: isSaving || isResyncing,
|
|
3588
|
+
iconRight: isResyncing && Spinner
|
|
3589
|
+
}
|
|
3590
|
+
)
|
|
3591
|
+
] }),
|
|
3441
3592
|
modified && /* @__PURE__ */ jsx(
|
|
3442
3593
|
Button,
|
|
3443
3594
|
{
|
|
@@ -3449,7 +3600,7 @@ const AssetInput = (props) => /* @__PURE__ */ jsx(FormField$1, { title: props.la
|
|
|
3449
3600
|
tone: "positive",
|
|
3450
3601
|
onClick: saveChanges,
|
|
3451
3602
|
iconRight: isSaving && Spinner,
|
|
3452
|
-
disabled: isSaving
|
|
3603
|
+
disabled: isSaving || isResyncing
|
|
3453
3604
|
}
|
|
3454
3605
|
)
|
|
3455
3606
|
] }) }),
|
|
@@ -4643,7 +4794,9 @@ const FileButton = styled(MenuItem)(({ theme }) => {
|
|
|
4643
4794
|
color: white;
|
|
4644
4795
|
`, isVideoAsset = (asset) => asset._type === "mux.videoAsset";
|
|
4645
4796
|
function PlayerActionsMenu(props) {
|
|
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])
|
|
4797
|
+
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), { resyncAsset, isResyncing } = useResyncAsset({ showToast: !0 }), onReset = useCallback(() => onChange(PatchEvent.from(unset([]))), [onChange]), handleResync = useCallback(async () => {
|
|
4798
|
+
setOpen(!1), await resyncAsset(asset);
|
|
4799
|
+
}, [resyncAsset, asset]);
|
|
4647
4800
|
return useEffect(() => {
|
|
4648
4801
|
open && dialogState && setOpen(!1);
|
|
4649
4802
|
}, [dialogState, open]), useClickOutsideEvent(
|
|
@@ -4701,6 +4854,15 @@ function PlayerActionsMenu(props) {
|
|
|
4701
4854
|
text: "Captions",
|
|
4702
4855
|
onClick: () => setDialogState("edit-captions")
|
|
4703
4856
|
}
|
|
4857
|
+
),
|
|
4858
|
+
/* @__PURE__ */ jsx(
|
|
4859
|
+
MenuItem,
|
|
4860
|
+
{
|
|
4861
|
+
icon: SyncIcon,
|
|
4862
|
+
text: "Resync from Mux",
|
|
4863
|
+
onClick: handleResync,
|
|
4864
|
+
disabled: readOnly || isResyncing
|
|
4865
|
+
}
|
|
4704
4866
|
)
|
|
4705
4867
|
] }),
|
|
4706
4868
|
/* @__PURE__ */ jsx(MenuDivider, {}),
|
|
@@ -6051,7 +6213,12 @@ const muxVideoSchema = {
|
|
|
6051
6213
|
{ type: "number", name: "max_width" },
|
|
6052
6214
|
{ type: "number", name: "max_frame_rate" },
|
|
6053
6215
|
{ type: "number", name: "duration" },
|
|
6054
|
-
{ type: "number", name: "max_height" }
|
|
6216
|
+
{ type: "number", name: "max_height" },
|
|
6217
|
+
{ type: "string", name: "language_code" },
|
|
6218
|
+
{ type: "string", name: "name" },
|
|
6219
|
+
{ type: "string", name: "status" },
|
|
6220
|
+
{ type: "string", name: "text_source" },
|
|
6221
|
+
{ type: "string", name: "text_type" }
|
|
6055
6222
|
]
|
|
6056
6223
|
}, muxPlaybackId = {
|
|
6057
6224
|
name: "mux.playbackId",
|