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