sanity-plugin-mux-input
Version:
An input component that integrates Sanity Studio with Mux video encoding/hosting service.
1,325 lines • 340 kB
JavaScript
import { DocumentPreviewPresence, FormField, LinearProgress, PatchEvent, PreviewCard, SanityDefaultPreview, TextWithTone, collate, createHookFromObservableFactory, definePlugin, getPreviewStateObservable, getPreviewValueWithFallback, isRecord, isReference, set, setIfMissing, truncateString, unset, useClient, useCurrentUser, useDataset, useDocumentPresence, useDocumentPreviewStore, useDocumentStore, useDocumentValues, useFormattedDuration, useProjectId, useSchema, useTimeAgo } from "sanity";
import { c } from "react/compiler-runtime";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import { SearchIcon } from "@sanity/icons/Search";
import { Autocomplete, Box, Button, Card, Checkbox, Code, Dialog, Flex, Grid, Heading, Inline, Label, Menu, MenuButton, MenuDivider, MenuItem, Popover, Radio, Spinner, Stack, Tab, TabList, TabPanel, Text, TextInput, Tooltip, rem, useClickOutsideEvent, useTheme_v2, useToast } from "@sanity/ui";
import { Suspense, createContext, isValidElement, memo, useCallback, useContext, useEffect, useId, useMemo, useReducer, useRef, useState } from "react";
import { compact, isNumber, isString, toLower, trim, uniq, words } from "lodash";
import { clear, preload, suspend } from "suspend-react";
import { Observable, Subject, concat, defer, from, of, throwError, timer } from "rxjs";
import { css, styled } from "styled-components";
import { CheckmarkCircleIcon } from "@sanity/icons/CheckmarkCircle";
import { ErrorOutlineIcon } from "@sanity/icons/ErrorOutline";
import { InfoOutlineIcon } from "@sanity/icons/InfoOutline";
import { RetrieveIcon } from "@sanity/icons/Retrieve";
import { RetryIcon } from "@sanity/icons/Retry";
import { uuid } from "@sanity/uuid";
import { catchError, concatMap, expand, mergeMap, mergeMapTo, switchMap, takeUntil, tap } from "rxjs/operators";
import { ChevronLeftIcon } from "@sanity/icons/ChevronLeft";
import { ChevronRightIcon } from "@sanity/icons/ChevronRight";
import { SyncIcon } from "@sanity/icons/Sync";
import { SortIcon } from "@sanity/icons/Sort";
import { CalendarIcon } from "@sanity/icons/Calendar";
import { CheckmarkIcon } from "@sanity/icons/Checkmark";
import { ClockIcon } from "@sanity/icons/Clock";
import { CropIcon } from "@sanity/icons/Crop";
import { EditIcon } from "@sanity/icons/Edit";
import { RevertIcon } from "@sanity/icons/Revert";
import { TagIcon } from "@sanity/icons/Tag";
import { TrashIcon } from "@sanity/icons/Trash";
import { DownloadIcon } from "@sanity/icons/Download";
import { AddIcon } from "@sanity/icons/Add";
import { ChevronDownIcon } from "@sanity/icons/ChevronDown";
import { ChevronUpIcon } from "@sanity/icons/ChevronUp";
import { TranslateIcon } from "@sanity/icons/Translate";
import { UploadIcon } from "@sanity/icons/Upload";
import LanguagesList from "iso-639-1";
import "@mux/mux-player-react";
import MuxPlayer from "@mux/mux-player-react/lazy";
import { getDevicePixelRatio } from "use-device-pixel-ratio";
import { DocumentIcon } from "@sanity/icons/Document";
import { IntentLink } from "sanity/router";
import { WarningOutlineIcon } from "@sanity/icons/WarningOutline";
import { useObservable } from "react-rx";
import { PublishIcon } from "@sanity/icons/Publish";
import { LockIcon } from "@sanity/icons/Lock";
import { PlayIcon } from "@sanity/icons/Play";
import useSWR from "swr";
import scrollIntoView from "scroll-into-view-if-needed";
import { useErrorBoundary } from "use-error-boundary";
import { PlugIcon } from "@sanity/icons/Plug";
import { UpChunk } from "@mux/upchunk";
import { EllipsisHorizontalIcon } from "@sanity/icons/EllipsisHorizontal";
import { ImageIcon } from "@sanity/icons/Image";
import { ResetIcon } from "@sanity/icons/Reset";
import { isValidElementType } from "react-is";
import { DocumentVideoIcon } from "@sanity/icons/DocumentVideo";
import { WarningFilledIcon } from "@sanity/icons/WarningFilled";
/**
* Icon of a monitor with a play button.
* Credits: material design icons & react-icons
*/
const ToolIcon = () => {
let $ = c(1), t0;
return $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = /* @__PURE__ */ jsx("svg", {
stroke: "currentColor",
fill: "currentColor",
strokeWidth: "0",
viewBox: "0 0 24 24",
height: "1em",
width: "1em",
xmlns: "http://www.w3.org/2000/svg",
children: /* @__PURE__ */ jsx("path", { d: "M21 3H3c-1.11 0-2 .89-2 2v12c0 1.1.89 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.11-.9-2-2-2zm0 14H3V5h18v12zm-5-6l-7 4V7z" })
}), $[0] = t0) : t0 = $[0], t0;
}, SUPPORTED_MUX_LANGUAGES = [
{
label: "English",
code: "en",
state: "Stable"
},
{
label: "Spanish",
code: "es",
state: "Stable"
},
{
label: "Italian",
code: "it",
state: "Stable"
},
{
label: "Portuguese",
code: "pt",
state: "Stable"
},
{
label: "German",
code: "de",
state: "Stable"
},
{
label: "French",
code: "fr",
state: "Stable"
},
{
label: "Polish",
code: "pl",
state: "Beta"
},
{
label: "Russian",
code: "ru",
state: "Beta"
},
{
label: "Dutch",
code: "nl",
state: "Beta"
},
{
label: "Catalan",
code: "ca",
state: "Beta"
},
{
label: "Turkish",
code: "tr",
state: "Beta"
},
{
label: "Swedish",
code: "sv",
state: "Beta"
},
{
label: "Ukrainian",
code: "uk",
state: "Beta"
},
{
label: "Norwegian",
code: "no",
state: "Beta"
},
{
label: "Finnish",
code: "fi",
state: "Beta"
},
{
label: "Slovak",
code: "sk",
state: "Beta"
},
{
label: "Greek",
code: "el",
state: "Beta"
},
{
label: "Czech",
code: "cs",
state: "Beta"
},
{
label: "Croatian",
code: "hr",
state: "Beta"
},
{
label: "Danish",
code: "da",
state: "Beta"
},
{
label: "Romanian",
code: "ro",
state: "Beta"
},
{
label: "Bulgarian",
code: "bg",
state: "Beta"
}
];
SUPPORTED_MUX_LANGUAGES.map((l) => l.code);
function isCustomTextTrack(track) {
return track.type !== "autogenerated";
}
function isAutogeneratedTrack(track) {
return track.type === "autogenerated";
}
const LOCAL_STORAGE_HAS_SHOWN_WARNING_KEY = "mux-plugin-has-shown-drm-playback-warning", DrmPlaybackWarningContext = createContext({
hasShownWarning: !1,
setHasWarnedAboutDrmPlayback: () => null
}), DrmPlaybackWarningContextProvider = (t0) => {
let $ = c(8), { config, children } = t0, warningDisabled = config?.disableDrmPlaybackWarning ?? !1, t1;
$[0] === warningDisabled ? t1 = $[1] : (t1 = warningDisabled || window.localStorage.getItem(LOCAL_STORAGE_HAS_SHOWN_WARNING_KEY) === "true", $[0] = warningDisabled, $[1] = t1);
let [hasWarnedAboutDrmPlayback, setHasWarnedAboutDrmPlayback] = useState(t1), t2;
$[2] === Symbol.for("react.memo_cache_sentinel") ? (t2 = (b) => {
window.localStorage.setItem(LOCAL_STORAGE_HAS_SHOWN_WARNING_KEY, b.toString()), setHasWarnedAboutDrmPlayback(b);
}, $[2] = t2) : t2 = $[2];
let setHasShownWarning = t2, t3;
$[3] === hasWarnedAboutDrmPlayback ? t3 = $[4] : (t3 = {
hasShownWarning: hasWarnedAboutDrmPlayback,
setHasWarnedAboutDrmPlayback: setHasShownWarning
}, $[3] = hasWarnedAboutDrmPlayback, $[4] = t3);
let value = t3, t4;
return $[5] !== children || $[6] !== value ? (t4 = /* @__PURE__ */ jsx(DrmPlaybackWarningContext.Provider, {
value,
children
}), $[5] = children, $[6] = value, $[7] = t4) : t4 = $[7], t4;
}, useDrmPlaybackWarningContext = () => useContext(DrmPlaybackWarningContext), DRMWarningDialog = (t0) => {
let $ = c(12), { onClose } = t0, { setHasWarnedAboutDrmPlayback } = useDrmPlaybackWarningContext(), t1;
$[0] !== onClose || $[1] !== setHasWarnedAboutDrmPlayback ? (t1 = () => {
setHasWarnedAboutDrmPlayback(!0), onClose();
}, $[0] = onClose, $[1] = setHasWarnedAboutDrmPlayback, $[2] = t1) : t1 = $[2];
let _onClose = t1, t2;
$[3] === _onClose ? t2 = $[4] : (t2 = /* @__PURE__ */ jsx(Stack, {
padding: 3,
children: /* @__PURE__ */ jsx(Button, {
mode: "ghost",
tone: "primary",
onClick: _onClose,
text: "Ok"
})
}), $[3] = _onClose, $[4] = t2);
let t3;
$[5] === Symbol.for("react.memo_cache_sentinel") ? (t3 = [
3,
3,
3
], $[5] = t3) : t3 = $[5];
let t4;
$[6] === Symbol.for("react.memo_cache_sentinel") ? (t4 = /* @__PURE__ */ jsx(Card, {
padding: t3,
radius: 2,
children: /* @__PURE__ */ jsx(Stack, {
space: 3,
children: /* @__PURE__ */ jsx(Text, {
size: 1,
weight: "semibold",
children: "DRM-protected playback will generate a license with a small associated cost. The plugin will attempt to play signed or public playback IDs instead whenever possible."
})
})
}), $[6] = t4) : t4 = $[6];
let t5;
$[7] === Symbol.for("react.memo_cache_sentinel") ? (t5 = [
3,
3,
3
], $[7] = t5) : t5 = $[7];
let t6;
$[8] === Symbol.for("react.memo_cache_sentinel") ? (t6 = /* @__PURE__ */ jsxs(Stack, {
space: 3,
padding: 3,
children: [t4, /* @__PURE__ */ jsx(Card, {
padding: t5,
radius: 2,
tone: "suggest",
children: /* @__PURE__ */ jsx(Stack, {
space: 3,
children: /* @__PURE__ */ jsx(Text, {
size: 1,
weight: "semibold",
children: "This is a one time warning. If it persists, you can disable it from your plugin configuration."
})
})
})]
}), $[8] = t6) : t6 = $[8];
let t7;
return $[9] !== _onClose || $[10] !== t2 ? (t7 = /* @__PURE__ */ jsx(Dialog, {
open: !0,
id: "drm-playback-warn",
onClose: _onClose,
header: "DRM Playback Warning",
footer: t2,
children: t6
}), $[9] = _onClose, $[10] = t2, $[11] = t7) : t7 = $[11], t7;
}, SANITY_API_VERSION = "2024-03-05";
function useClient$1() {
let $ = c(1), t0;
return $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = { apiVersion: SANITY_API_VERSION }, $[0] = t0) : t0 = $[0], useClient(t0);
}
const SPECIAL_CHARS = /([^!@#$%^&*(),\\/?";:{}|[\]+<>\s-])+/g, STRIP_EDGE_CHARS = /(^[.]+)|([.]+$)/;
function tokenize(string) {
return (string.match(SPECIAL_CHARS) || []).map((token) => token.replace(STRIP_EDGE_CHARS, ""));
}
function toGroqParams(terms) {
return terms.reduce((acc, term, i) => (acc[`t${i}`] = `*${term}*`, acc), {});
}
/**
* Convert a string into an array of tokenized terms.
*
* Any (multi word) text wrapped in double quotes will be treated as "phrases", or separate tokens that
* will not have its special characters removed.
* E.g.`"the" "fantastic mr" fox fox book` =\> ["the", `"fantastic mr"`, "fox", "book"]
*
* Phrases wrapped in quotes are assigned relevance scoring differently from regular words.
*
* @internal
*/
function extractTermsFromQuery(query) {
let quotedQueries = [], unquotedQuery = query.replace(/("[^"]*")/g, (match) => words(match).length > 1 ? (quotedQueries.push(match), "") : match), quotedTerms = quotedQueries.map((str) => trim(toLower(str))), remainingTerms = uniq(compact(tokenize(toLower(unquotedQuery))));
return [...quotedTerms, ...remainingTerms];
}
/**
* Create GROQ constraints, given search terms and the full spec of available document types and fields.
* Essentially a large list of all possible fields (joined by logical OR) to match our search terms against.
*/
function createConstraints(terms, includeAssetId) {
let searchPaths = includeAssetId ? ["filename", "assetId"] : ["filename"];
return terms.map((_term, i) => searchPaths.map((joinedPath) => `${joinedPath} match $t${i}`)).filter((constraint) => constraint.length > 0).map((constraint) => `(${constraint.join(" || ")})`);
}
function createSearchFilter(query) {
let terms = extractTermsFromQuery(query);
return {
filter: createConstraints(terms, query.length >= 8),
params: { ...toGroqParams(terms) }
};
}
const ASSET_SORT_OPTIONS = {
createdDesc: {
groq: "_createdAt desc",
label: "Newest first"
},
createdAsc: {
groq: "_createdAt asc",
label: "First created (oldest)"
},
filenameAsc: {
groq: "filename asc",
label: "By filename (A-Z)"
},
filenameDesc: {
groq: "filename desc",
label: "By filename (Z-A)"
}
}, useAssetDocuments = createHookFromObservableFactory(({ documentStore, sort, searchQuery }) => {
let search = createSearchFilter(searchQuery), filter = ["_type == \"mux.videoAsset\"", ...search.filter].filter(Boolean).join(" && "), sortFragment = ASSET_SORT_OPTIONS[sort].groq;
return documentStore.listenQuery(`*[${filter}] | order(${sortFragment})`, search.params, { apiVersion: SANITY_API_VERSION });
});
function useAssets() {
let $ = c(13), documentStore = useDocumentStore(), [sort, setSort] = useState("createdDesc"), [searchQuery, setSearchQuery] = useState(""), t0;
$[0] !== documentStore || $[1] !== searchQuery || $[2] !== sort ? (t0 = {
documentStore,
sort,
searchQuery
}, $[0] = documentStore, $[1] = searchQuery, $[2] = sort, $[3] = t0) : t0 = $[3];
let [t1, isLoading] = useAssetDocuments(t0), t2;
$[4] === t1 ? t2 = $[5] : (t2 = t1 === void 0 ? [] : t1, $[4] = t1, $[5] = t2);
let assetDocuments = t2, t3;
$[6] === assetDocuments ? t3 = $[7] : (t3 = collate(assetDocuments).map(_temp$9), $[6] = assetDocuments, $[7] = t3);
let assets = t3, t4;
return $[8] !== assets || $[9] !== isLoading || $[10] !== searchQuery || $[11] !== sort ? (t4 = {
assets,
isLoading,
sort,
searchQuery,
setSort,
setSearchQuery
}, $[8] = assets, $[9] = isLoading, $[10] = searchQuery, $[11] = sort, $[12] = t4) : t4 = $[12], t4;
}
function _temp$9(collated) {
return {
...collated.draft || collated.published,
_id: collated.id
};
}
function useDialogState() {
return useState(!1);
}
const PLUGIN_VERSION_QUERY = { sanityVersion: "5.0.7" };
function saveSecrets(client, token, secretKey, enableSignedUrls, signingKeyId, signingKeyPrivate, drmConfigId) {
let doc = {
_id: "secrets.mux",
_type: "mux.apiKey",
token,
secretKey,
enableSignedUrls,
signingKeyId,
signingKeyPrivate,
drmConfigId
};
return doc.signingKeyId = enableSignedUrls ? signingKeyId : "", doc.signingKeyPrivate = enableSignedUrls ? signingKeyPrivate : "", client.createOrReplace(doc);
}
async function createSigningKeys(client) {
try {
let { dataset } = client.config();
return await client.request({
url: `/addons/mux/signing-keys/${dataset}`,
withCredentials: !0,
method: "POST",
query: PLUGIN_VERSION_QUERY
});
} catch (error) {
console.error("Error creating signing keys", error);
let message = error.response?.statusCode === 401 ? "Unauthorized - Failed to create the Signing Key. Please ensure that the token has \"System\" permissions" : error.message;
throw Error(message, { cause: error });
}
}
function testSecrets(client) {
let { dataset } = client.config();
return client.request({
url: `/addons/mux/secrets/${dataset}/test`,
withCredentials: !0,
method: "GET",
query: PLUGIN_VERSION_QUERY
});
}
async function haveValidSigningKeys(client, signingKeyId, signingKeyPrivate) {
if (!(signingKeyId && signingKeyPrivate)) return !1;
let { dataset } = client.config();
try {
let res = await client.request({
url: `/addons/mux/signing-keys/${dataset}/${signingKeyId}`,
withCredentials: !0,
method: "GET",
query: PLUGIN_VERSION_QUERY
});
return !!(res.data && res.data.id);
} catch (e) {
return console.error("Error fetching signingKeyId", signingKeyId, "assuming it is not valid", e), !1;
}
}
function testSecretsObservable(client) {
let { dataset } = client.config();
return defer(() => client.observable.request({
url: `/addons/mux/secrets/${dataset}/test`,
withCredentials: !0,
method: "GET",
query: PLUGIN_VERSION_QUERY
}));
}
const useSaveSecrets = (client, secrets) => useCallback(async ({ token, secretKey, enableSignedUrls, drmConfigId }) => {
let { signingKeyId, signingKeyPrivate } = secrets;
try {
if (await saveSecrets(client, token, secretKey, enableSignedUrls, signingKeyId, signingKeyPrivate, drmConfigId), !(await testSecrets(client))?.status && token && secretKey) throw Error("Invalid secrets");
} catch (err) {
throw console.error("Error while trying to save secrets:", err), err;
}
if (enableSignedUrls && !await haveValidSigningKeys(client, signingKeyId, signingKeyPrivate)) try {
let { data } = await createSigningKeys(client);
signingKeyId = data.id, signingKeyPrivate = data.private_key, await saveSecrets(client, token, secretKey, enableSignedUrls, signingKeyId, signingKeyPrivate, drmConfigId ?? "");
} catch (err_0) {
throw console.log("Error while creating and saving signing key:", err_0?.message), err_0;
}
return {
token,
secretKey,
enableSignedUrls,
signingKeyId,
signingKeyPrivate,
drmConfigId
};
}, [client, secrets]), name = "mux-input", cacheNs = "sanity-plugin-mux-input", DIALOGS_Z_INDEX = 6e4, THUMBNAIL_ASPECT_RATIO = 16 / 9, path$1 = [
"token",
"secretKey",
"enableSignedUrls",
"signingKeyId",
"signingKeyPrivate",
"drmConfigId"
], useSecretsDocumentValues = () => {
let $ = c(15), { error, isLoading, value } = useDocumentValues("secrets.mux", path$1), exists = !!value, t0 = value?.token || null, t1 = value?.secretKey || null, t2 = value?.enableSignedUrls || !1, t3 = value?.signingKeyId || null, t4 = value?.signingKeyPrivate || null, t5 = value?.drmConfigId || null, t6;
$[0] !== t0 || $[1] !== t1 || $[2] !== t2 || $[3] !== t3 || $[4] !== t4 || $[5] !== t5 ? (t6 = {
token: t0,
secretKey: t1,
enableSignedUrls: t2,
signingKeyId: t3,
signingKeyPrivate: t4,
drmConfigId: t5
}, $[0] = t0, $[1] = t1, $[2] = t2, $[3] = t3, $[4] = t4, $[5] = t5, $[6] = t6) : t6 = $[6];
let secrets = t6, t7 = !exists, t8 = !secrets?.token || !secrets?.secretKey, t9;
$[7] !== secrets || $[8] !== t7 || $[9] !== t8 ? (t9 = {
isInitialSetup: t7,
needsSetup: t8,
secrets
}, $[7] = secrets, $[8] = t7, $[9] = t8, $[10] = t9) : t9 = $[10];
let cache = t9, t10;
return $[11] !== cache || $[12] !== error || $[13] !== isLoading ? (t10 = {
error,
isLoading,
value: cache
}, $[11] = cache, $[12] = error, $[13] = isLoading, $[14] = t10) : t10 = $[14], t10;
};
function init({ token, secretKey, enableSignedUrls, drmConfigId }) {
return {
submitting: !1,
error: null,
token: token ?? "",
secretKey: secretKey ?? "",
enableSignedUrls: enableSignedUrls ?? !1,
drmConfigId: drmConfigId ?? ""
};
}
function reducer(state, action) {
switch (action?.type) {
case "submit": return {
...state,
submitting: !0,
error: null
};
case "error": return {
...state,
submitting: !1,
error: action.payload
};
case "reset": return init(action.payload);
case "change": return {
...state,
[action.payload.name]: action.payload.value
};
default: throw Error(`Unknown action type: ${action?.type}`);
}
}
const useSecretsFormState = (secrets) => useReducer(reducer, secrets, init), _id = "secrets.mux";
function readSecrets(client) {
let { projectId, dataset } = client.config();
return suspend(async () => {
let data = await client.fetch("*[_id == $_id][0]{\n token,\n secretKey,\n enableSignedUrls,\n signingKeyId,\n signingKeyPrivate,\n drmConfigId\n }", { _id });
return {
token: data?.token || null,
secretKey: data?.secretKey || null,
enableSignedUrls: !!data?.enableSignedUrls || !1,
signingKeyId: data?.signingKeyId || null,
signingKeyPrivate: data?.signingKeyPrivate || null,
drmConfigId: data?.drmConfigId || null
};
}, [
cacheNs,
_id,
projectId,
dataset
]);
}
function MuxLogo(t0) {
let $ = c(12), { height: t1 } = t0, height = t1 === void 0 ? 26 : t1, id = useId(), fillColor = useTheme_v2().color._dark ? "white" : "black", titleId = `${id}-title`, t2;
$[0] === Symbol.for("react.memo_cache_sentinel") ? (t2 = { fillRule: "nonzero" }, $[0] = t2) : t2 = $[0];
let pathStyle = t2, t3 = `${height}px`, t4;
$[1] === t3 ? t4 = $[2] : (t4 = { height: t3 }, $[1] = t3, $[2] = t4);
let t5, t6, t7;
$[3] === Symbol.for("react.memo_cache_sentinel") ? (t5 = /* @__PURE__ */ jsx("path", {
d: "M994.287,93.486c-17.121,-0 -31,-13.879 -31,-31c0,-17.121 13.879,-31 31,-31c17.121,-0 31,13.879 31,31c0,17.121 -13.879,31 -31,31m0,-93.486c-34.509,-0 -62.484,27.976 -62.484,62.486l0,187.511c0,68.943 -56.09,125.033 -125.032,125.033c-68.942,-0 -125.03,-56.09 -125.03,-125.033l0,-187.511c0,-34.51 -27.976,-62.486 -62.485,-62.486c-34.509,-0 -62.484,27.976 -62.484,62.486l0,187.511c0,137.853 112.149,250.003 249.999,250.003c137.851,-0 250.001,-112.15 250.001,-250.003l0,-187.511c0,-34.51 -27.976,-62.486 -62.485,-62.486",
style: pathStyle
}), t6 = /* @__PURE__ */ jsx("path", {
d: "M1537.51,468.511c-17.121,-0 -31,-13.879 -31,-31c0,-17.121 13.879,-31 31,-31c17.121,-0 31,13.879 31,31c0,17.121 -13.879,31 -31,31m-275.883,-218.509l-143.33,143.329c-24.402,24.402 -24.402,63.966 0,88.368c24.402,24.402 63.967,24.402 88.369,-0l143.33,-143.329l143.328,143.329c24.402,24.4 63.967,24.402 88.369,-0c24.403,-24.402 24.403,-63.966 0.001,-88.368l-143.33,-143.329l0.001,-0.004l143.329,-143.329c24.402,-24.402 24.402,-63.965 0,-88.367c-24.402,-24.402 -63.967,-24.402 -88.369,-0l-143.329,143.328l-143.329,-143.328c-24.402,-24.401 -63.967,-24.402 -88.369,-0c-24.402,24.402 -24.402,63.965 0,88.367l143.329,143.329l0,0.004Z",
style: pathStyle
}), t7 = /* @__PURE__ */ jsx("path", {
d: "M437.511,468.521c-17.121,-0 -31,-13.879 -31,-31c0,-17.121 13.879,-31 31,-31c17.121,-0 31,13.879 31,31c0,17.121 -13.879,31 -31,31m23.915,-463.762c-23.348,-9.672 -50.226,-4.327 -68.096,13.544l-143.331,143.329l-143.33,-143.329c-17.871,-17.871 -44.747,-23.216 -68.096,-13.544c-23.349,9.671 -38.574,32.455 -38.574,57.729l0,375.026c0,34.51 27.977,62.486 62.487,62.486c34.51,-0 62.486,-27.976 62.486,-62.486l0,-224.173l80.843,80.844c24.404,24.402 63.965,24.402 88.369,-0l80.843,-80.844l0,224.173c0,34.51 27.976,62.486 62.486,62.486c34.51,-0 62.486,-27.976 62.486,-62.486l0,-375.026c0,-25.274 -15.224,-48.058 -38.573,-57.729",
style: pathStyle
}), $[3] = t5, $[4] = t6, $[5] = t7) : (t5 = $[3], t6 = $[4], t7 = $[5]);
let t8;
$[6] === fillColor ? t8 = $[7] : (t8 = /* @__PURE__ */ jsxs("g", {
id: "Layer-1",
fill: fillColor,
children: [
t5,
t6,
t7
]
}), $[6] = fillColor, $[7] = t8);
let t9;
return $[8] !== t4 || $[9] !== t8 || $[10] !== titleId ? (t9 = /* @__PURE__ */ jsx("svg", {
"aria-labelledby": titleId,
style: t4,
viewBox: "0 0 1600 500",
version: "1.1",
xmlns: "http://www.w3.org/2000/svg",
xmlSpace: "preserve",
children: t8
}), $[8] = t4, $[9] = t8, $[10] = titleId, $[11] = t9) : t9 = $[11], t9;
}
const Logo = styled.span.withConfig({
displayName: "Logo",
componentId: "sc-1qgvus-0"
})`display:inline-block;height:0.8em;margin-right:1em;transform:translate(0.3em,-0.2em);`, Header = () => {
let $ = c(1), t0;
return $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Logo, { children: /* @__PURE__ */ jsx(MuxLogo, { height: 13 }) }), "API Credentials"] }), $[0] = t0) : t0 = $[0], t0;
};
function FormField$1(props) {
let $ = c(15), { children, title, description, inputId } = props, t0;
$[0] === title ? t0 = $[1] : (t0 = title || /* @__PURE__ */ jsx("em", { children: "Untitled" }), $[0] = title, $[1] = t0);
let t1;
$[2] !== inputId || $[3] !== t0 ? (t1 = /* @__PURE__ */ jsx(Text, {
as: "label",
htmlFor: inputId,
weight: "semibold",
size: 1,
children: t0
}), $[2] = inputId, $[3] = t0, $[4] = t1) : t1 = $[4];
let t2;
$[5] === description ? t2 = $[6] : (t2 = description && /* @__PURE__ */ jsx(Text, {
muted: !0,
size: 1,
children: description
}), $[5] = description, $[6] = t2);
let t3;
$[7] !== t1 || $[8] !== t2 ? (t3 = /* @__PURE__ */ jsx(Flex, {
align: "flex-end",
children: /* @__PURE__ */ jsx(Box, {
flex: 1,
paddingY: 2,
children: /* @__PURE__ */ jsxs(Stack, {
space: 2,
children: [t1, t2]
})
})
}), $[7] = t1, $[8] = t2, $[9] = t3) : t3 = $[9];
let t4;
$[10] === children ? t4 = $[11] : (t4 = /* @__PURE__ */ jsx("div", { children }), $[10] = children, $[11] = t4);
let t5;
return $[12] !== t3 || $[13] !== t4 ? (t5 = /* @__PURE__ */ jsxs(Stack, {
space: 1,
children: [t3, t4]
}), $[12] = t3, $[13] = t4, $[14] = t5) : t5 = $[14], t5;
}
var FormField_default = memo(FormField$1);
const fieldNames = [
"token",
"secretKey",
"enableSignedUrls",
"drmConfigId"
];
function ConfigureApiDialog(t0) {
let $ = c(91), { secrets, setDialogState } = t0, client = useClient$1(), [state, dispatch] = useSecretsFormState(secrets), hasSecretsInitially = secrets.token && secrets.secretKey, t1;
$[0] === setDialogState ? t1 = $[1] : (t1 = () => setDialogState(!1), $[0] = setDialogState, $[1] = t1);
let handleClose = t1, dirty = secrets.token !== state.token || secrets.secretKey !== state.secretKey || secrets.enableSignedUrls !== state.enableSignedUrls || secrets.drmConfigId !== state.drmConfigId, id = `ConfigureApi${useId()}`, t2;
$[2] === id ? t2 = $[3] : (t2 = fieldNames.map((field) => `${id}-${field}`), $[2] = id, $[3] = t2);
let [tokenId, secretKeyId, enableSignedUrlsId, drmConfigIdId] = t2, firstField = useRef(null), handleSaveSecrets = useSaveSecrets(client, secrets), saving = useRef(!1), t3;
$[4] !== client || $[5] !== dispatch || $[6] !== handleSaveSecrets || $[7] !== setDialogState || $[8] !== state ? (t3 = (event) => {
if (event.preventDefault(), !saving.current && event.currentTarget.reportValidity()) {
saving.current = !0, dispatch({ type: "submit" });
let { token, secretKey, enableSignedUrls, drmConfigId } = state;
handleSaveSecrets({
token,
secretKey,
enableSignedUrls,
drmConfigId
}).then((savedSecrets) => {
let { projectId, dataset } = client.config();
clear([
cacheNs,
_id,
projectId,
dataset
]), preload(() => Promise.resolve(savedSecrets), [
cacheNs,
_id,
projectId,
dataset
]), setDialogState(!1);
}).catch((err) => dispatch({
type: "error",
payload: err.message
})).finally(() => {
saving.current = !1;
});
}
}, $[4] = client, $[5] = dispatch, $[6] = handleSaveSecrets, $[7] = setDialogState, $[8] = state, $[9] = t3) : t3 = $[9];
let handleSubmit = t3, t4;
$[10] === dispatch ? t4 = $[11] : (t4 = (event_0) => {
dispatch({
type: "change",
payload: {
name: "token",
value: event_0.currentTarget.value
}
});
}, $[10] = dispatch, $[11] = t4);
let handleChangeToken = t4, t5;
$[12] === dispatch ? t5 = $[13] : (t5 = (event_1) => {
dispatch({
type: "change",
payload: {
name: "secretKey",
value: event_1.currentTarget.value
}
});
}, $[12] = dispatch, $[13] = t5);
let handleChangeSecretKey = t5, t6;
$[14] === dispatch ? t6 = $[15] : (t6 = (event_2) => {
dispatch({
type: "change",
payload: {
name: "enableSignedUrls",
value: event_2.currentTarget.checked
}
});
}, $[14] = dispatch, $[15] = t6);
let handleChangeEnableSignedUrls = t6, t7;
$[16] === dispatch ? t7 = $[17] : (t7 = (event_3) => {
dispatch({
type: "change",
payload: {
name: "drmConfigId",
value: event_3.currentTarget.value
}
});
}, $[16] = dispatch, $[17] = t7);
let handleChangeDrmConfigId = t7, t8, t9;
$[18] === Symbol.for("react.memo_cache_sentinel") ? (t8 = () => {
firstField.current && firstField.current.focus();
}, t9 = [firstField], $[18] = t8, $[19] = t9) : (t8 = $[18], t9 = $[19]), useEffect(t8, t9);
let t10;
$[20] === Symbol.for("react.memo_cache_sentinel") ? (t10 = /* @__PURE__ */ jsx(Header, {}), $[20] = t10) : t10 = $[20];
let t11;
$[21] === hasSecretsInitially ? t11 = $[22] : (t11 = !hasSecretsInitially && /* @__PURE__ */ jsx(Card, {
padding: [
3,
3,
3
],
radius: 2,
shadow: 1,
tone: "primary",
children: /* @__PURE__ */ jsxs(Stack, {
space: 3,
children: [/* @__PURE__ */ jsxs(Text, {
size: 1,
children: [
"To set up a new access token, go to your",
" ",
/* @__PURE__ */ jsx("a", {
href: "https://dashboard.mux.com/settings/access-tokens",
target: "_blank",
rel: "noreferrer noopener",
children: "account on mux.com"
}),
"."
]
}), /* @__PURE__ */ jsxs(Text, {
size: 1,
children: [
"The access token needs permissions: ",
/* @__PURE__ */ jsx("strong", { children: "Mux Video " }),
"(Full Access) and ",
/* @__PURE__ */ jsx("strong", { children: "Mux Data" }),
" (Read)",
/* @__PURE__ */ jsx("br", {}),
"To use Signed URLs, the token must also have System permissions.",
/* @__PURE__ */ jsx("br", {}),
"The credentials will be stored safely in a hidden document only available to editors."
]
})]
})
}), $[21] = hasSecretsInitially, $[22] = t11);
let t12 = state.token ?? "", t13 = !!state.secretKey || state.enableSignedUrls, t14;
$[23] !== handleChangeToken || $[24] !== t12 || $[25] !== t13 || $[26] !== tokenId ? (t14 = /* @__PURE__ */ jsx(TextInput, {
id: tokenId,
ref: firstField,
onChange: handleChangeToken,
type: "text",
value: t12,
required: t13
}), $[23] = handleChangeToken, $[24] = t12, $[25] = t13, $[26] = tokenId, $[27] = t14) : t14 = $[27];
let t15;
$[28] !== t14 || $[29] !== tokenId ? (t15 = /* @__PURE__ */ jsx(FormField_default, {
title: "Access Token",
inputId: tokenId,
children: t14
}), $[28] = t14, $[29] = tokenId, $[30] = t15) : t15 = $[30];
let t16 = state.secretKey ?? "", t17 = !!state.token || state.enableSignedUrls, t18;
$[31] !== handleChangeSecretKey || $[32] !== secretKeyId || $[33] !== t16 || $[34] !== t17 ? (t18 = /* @__PURE__ */ jsx(TextInput, {
id: secretKeyId,
onChange: handleChangeSecretKey,
type: "text",
value: t16,
required: t17
}), $[31] = handleChangeSecretKey, $[32] = secretKeyId, $[33] = t16, $[34] = t17, $[35] = t18) : t18 = $[35];
let t19;
$[36] !== secretKeyId || $[37] !== t18 ? (t19 = /* @__PURE__ */ jsx(FormField_default, {
title: "Secret Key",
inputId: secretKeyId,
children: t18
}), $[36] = secretKeyId, $[37] = t18, $[38] = t19) : t19 = $[38];
let t20;
$[39] === Symbol.for("react.memo_cache_sentinel") ? (t20 = { display: "block" }, $[39] = t20) : t20 = $[39];
let t21;
$[40] !== enableSignedUrlsId || $[41] !== handleChangeEnableSignedUrls || $[42] !== state.enableSignedUrls ? (t21 = /* @__PURE__ */ jsx(Checkbox, {
id: enableSignedUrlsId,
onChange: handleChangeEnableSignedUrls,
checked: state.enableSignedUrls,
style: t20
}), $[40] = enableSignedUrlsId, $[41] = handleChangeEnableSignedUrls, $[42] = state.enableSignedUrls, $[43] = t21) : t21 = $[43];
let t22;
$[44] === enableSignedUrlsId ? t22 = $[45] : (t22 = /* @__PURE__ */ jsx(Box, {
flex: 1,
paddingLeft: 3,
children: /* @__PURE__ */ jsx(Text, { children: /* @__PURE__ */ jsx("label", {
htmlFor: enableSignedUrlsId,
children: "Enable Signed Urls"
}) })
}), $[44] = enableSignedUrlsId, $[45] = t22);
let t23;
$[46] !== t21 || $[47] !== t22 ? (t23 = /* @__PURE__ */ jsxs(Flex, {
align: "center",
children: [t21, t22]
}), $[46] = t21, $[47] = t22, $[48] = t23) : t23 = $[48];
let t24;
$[49] !== secrets.signingKeyId || $[50] !== state.enableSignedUrls ? (t24 = secrets.signingKeyId && state.enableSignedUrls ? /* @__PURE__ */ jsx(Card, {
padding: [
3,
3,
3
],
radius: 2,
shadow: 1,
tone: "caution",
children: /* @__PURE__ */ jsxs(Stack, {
space: 3,
children: [
/* @__PURE__ */ jsx(Text, {
size: 1,
children: "The signing key ID that Sanity will use is:"
}),
/* @__PURE__ */ jsx(Code, {
size: 1,
children: secrets.signingKeyId
}),
/* @__PURE__ */ jsxs(Text, {
size: 1,
children: [
"This key is only used for previewing content in the Sanity UI.",
/* @__PURE__ */ jsx("br", {}),
"You should generate a different key to use in your application server."
]
})
]
})
}) : null, $[49] = secrets.signingKeyId, $[50] = state.enableSignedUrls, $[51] = t24) : t24 = $[51];
let t25;
$[52] !== t23 || $[53] !== t24 ? (t25 = /* @__PURE__ */ jsxs(Stack, {
space: 4,
children: [t23, t24]
}), $[52] = t23, $[53] = t24, $[54] = t25) : t25 = $[54];
let t26 = state.drmConfigId ?? "", t27;
$[55] !== drmConfigIdId || $[56] !== handleChangeDrmConfigId || $[57] !== t26 ? (t27 = /* @__PURE__ */ jsx(TextInput, {
id: drmConfigIdId,
onChange: handleChangeDrmConfigId,
type: "text",
value: t26,
required: !1
}), $[55] = drmConfigIdId, $[56] = handleChangeDrmConfigId, $[57] = t26, $[58] = t27) : t27 = $[58];
let t28;
$[59] !== drmConfigIdId || $[60] !== t27 ? (t28 = /* @__PURE__ */ jsx(FormField_default, {
title: "DRM Configuration ID",
inputId: drmConfigIdId,
children: t27
}), $[59] = drmConfigIdId, $[60] = t27, $[61] = t28) : t28 = $[61];
let t29;
$[62] === Symbol.for("react.memo_cache_sentinel") ? (t29 = [
3,
3,
3
], $[62] = t29) : t29 = $[62];
let t30;
$[63] === Symbol.for("react.memo_cache_sentinel") ? (t30 = /* @__PURE__ */ jsxs(Text, {
size: 1,
children: [
"DRM (Digital Rights Management) provides an extra layer of content security for video content streamed from Mux. For additional information check out our",
" ",
/* @__PURE__ */ jsx("a", {
href: "https://www.mux.com/docs/guides/protect-videos-with-drm#play-drm-protected-videos",
target: "_blank",
rel: "noopener noreferrer",
children: "DRM Guide"
}),
"."
]
}), $[63] = t30) : t30 = $[63];
let t31;
$[64] === Symbol.for("react.memo_cache_sentinel") ? (t31 = /* @__PURE__ */ jsx(Card, {
padding: t29,
radius: 2,
shadow: 1,
tone: "neutral",
children: /* @__PURE__ */ jsxs(Stack, {
space: 3,
children: [t30, /* @__PURE__ */ jsxs(Text, {
size: 1,
children: [
/* @__PURE__ */ jsx("a", {
href: "https://www.mux.com/support/human",
target: "_blank",
rel: "noopener noreferrer",
children: "Contact us"
}),
" ",
"to get started using DRM."
]
})]
})
}), $[64] = t31) : t31 = $[64];
let t32 = !dirty, t33;
$[65] !== state.submitting || $[66] !== t32 ? (t33 = /* @__PURE__ */ jsx(Button, {
text: "Save",
disabled: t32,
loading: state.submitting,
tone: "primary",
mode: "default",
type: "submit"
}), $[65] = state.submitting, $[66] = t32, $[67] = t33) : t33 = $[67];
let t34;
$[68] !== handleClose || $[69] !== state.submitting ? (t34 = /* @__PURE__ */ jsx(Button, {
disabled: state.submitting,
text: "Cancel",
mode: "bleed",
onClick: handleClose
}), $[68] = handleClose, $[69] = state.submitting, $[70] = t34) : t34 = $[70];
let t35;
$[71] !== t33 || $[72] !== t34 ? (t35 = /* @__PURE__ */ jsxs(Inline, {
space: 2,
children: [t33, t34]
}), $[71] = t33, $[72] = t34, $[73] = t35) : t35 = $[73];
let t36;
$[74] === state.error ? t36 = $[75] : (t36 = state.error && /* @__PURE__ */ jsx(Card, {
padding: [
3,
3,
3
],
radius: 2,
shadow: 1,
tone: "critical",
children: /* @__PURE__ */ jsx(Text, { children: state.error })
}), $[74] = state.error, $[75] = t36);
let t37;
$[76] !== t11 || $[77] !== t15 || $[78] !== t19 || $[79] !== t25 || $[80] !== t28 || $[81] !== t35 || $[82] !== t36 ? (t37 = /* @__PURE__ */ jsxs(Stack, {
space: 4,
children: [
t11,
t15,
t19,
t25,
t28,
t31,
t35,
t36
]
}), $[76] = t11, $[77] = t15, $[78] = t19, $[79] = t25, $[80] = t28, $[81] = t35, $[82] = t36, $[83] = t37) : t37 = $[83];
let t38;
$[84] !== handleSubmit || $[85] !== t37 ? (t38 = /* @__PURE__ */ jsx(Box, {
padding: 3,
children: /* @__PURE__ */ jsx("form", {
onSubmit: handleSubmit,
noValidate: !0,
children: t37
})
}), $[84] = handleSubmit, $[85] = t37, $[86] = t38) : t38 = $[86];
let t39;
return $[87] !== handleClose || $[88] !== id || $[89] !== t38 ? (t39 = /* @__PURE__ */ jsx(Dialog, {
animate: !0,
id,
onClose: handleClose,
onClickOutside: handleClose,
header: t10,
zOffset: DIALOGS_Z_INDEX,
position: "fixed",
width: 1,
children: t38
}), $[87] = handleClose, $[88] = id, $[89] = t38, $[90] = t39) : t39 = $[90], t39;
}
function ConfigureApi() {
let $ = c(7), [dialogOpen, setDialogOpen] = useDialogState(), secretDocumentValues = useSecretsDocumentValues(), t0;
$[0] === setDialogOpen ? t0 = $[1] : (t0 = () => setDialogOpen("secrets"), $[0] = setDialogOpen, $[1] = t0);
let openDialog = t0;
if (dialogOpen === "secrets") {
let t1;
return $[2] !== secretDocumentValues.value.secrets || $[3] !== setDialogOpen ? (t1 = /* @__PURE__ */ jsx(ConfigureApiDialog, {
secrets: secretDocumentValues.value.secrets,
setDialogState: setDialogOpen
}), $[2] = secretDocumentValues.value.secrets, $[3] = setDialogOpen, $[4] = t1) : t1 = $[4], t1;
}
let t1;
return $[5] === openDialog ? t1 = $[6] : (t1 = /* @__PURE__ */ jsx(Button, {
mode: "bleed",
text: "Configure plugin",
onClick: openDialog
}), $[5] = openDialog, $[6] = t1), t1;
}
/**
* Generates a placeholder title for a Mux asset when no title is available.
* This format is used when importing assets that don't have a title in Mux.
*
* @param assetId - The Mux asset ID
* @returns A placeholder title in the format "Asset #[truncated-id]"
*/
function generateAssetPlaceholder(assetId) {
return `Asset #${truncateString(assetId, 15)}`;
}
/**
* Checks if a filename is empty or has the placeholder format.
* This is used to determine if a video title should be updated during metadata sync.
*
* @param filename - The current filename/title of the video
* @param assetId - The Mux asset ID to check against
* @returns true if the filename is empty or matches the placeholder format
*/
function isEmptyOrPlaceholderTitle(filename, assetId) {
return !filename || filename.trim() === "" || filename === generateAssetPlaceholder(assetId);
}
function parseMuxDate(date) {
return /* @__PURE__ */ new Date(Number(date) * 1e3);
}
function deleteAssetOnMux(client, assetId) {
let { dataset } = client.config();
return client.request({
url: `/addons/mux/assets/${dataset}/${assetId}`,
withCredentials: !0,
method: "DELETE",
query: PLUGIN_VERSION_QUERY
});
}
async function deleteAsset({ client, asset, deleteOnMux }) {
if (!asset?._id) return !0;
try {
await client.delete(asset._id);
} catch {
return "failed-sanity";
}
if (deleteOnMux && asset?.assetId) try {
await deleteAssetOnMux(client, asset.assetId);
} catch {
return "failed-mux";
}
return !0;
}
function getAsset(client, assetId) {
let { dataset } = client.config();
return client.request({
url: `/addons/mux/assets/${dataset}/data/${assetId}`,
withCredentials: !0,
method: "GET",
query: PLUGIN_VERSION_QUERY
});
}
function listAssets(client, options) {
let { dataset } = client.config(), query = {};
return options.limit && (query.limit = options.limit.toString()), options.cursor && (query.cursor = options.cursor), client.request({
url: `/addons/mux/assets/${dataset}/data/list`,
withCredentials: !0,
method: "GET",
query: {
...query,
...PLUGIN_VERSION_QUERY
}
});
}
/**
* Adds a new text track to an existing asset using a VTT file URL
*/
function addTextTrackFromUrl(client, assetId, vttUrl, options) {
let { dataset } = client.config();
return client.request({
url: `/addons/mux/assets/${dataset}/${assetId}/tracks`,
withCredentials: !0,
method: "POST",
body: {
url: vttUrl,
type: "text",
language_code: options.language_code,
name: options.name,
text_type: options.text_type || "subtitles"
},
headers: { "Content-Type": "application/json" },
query: PLUGIN_VERSION_QUERY
});
}
/**
* Generates subtitles automatically for an audio track
*/
function generateSubtitles(client, assetId, audioTrackId, options) {
let { dataset } = client.config();
return client.request({
url: `/addons/mux/assets/${dataset}/${assetId}/tracks/${audioTrackId}/generate-subtitles`,
withCredentials: !0,
method: "POST",
body: { generated_subtitles: [{
language_code: options.language_code,
name: options.name
}] },
headers: { "Content-Type": "application/json" },
query: PLUGIN_VERSION_QUERY
});
}
/**
* Deletes a text track from an asset
*/
function deleteTextTrack(client, assetId, trackId) {
let { dataset } = client.config();
return client.request({
url: `/addons/mux/assets/${dataset}/${assetId}/tracks/${trackId}`,
withCredentials: !0,
method: "DELETE",
query: PLUGIN_VERSION_QUERY
});
}
/**
* Updates master access (the "mezzanine" file in Mux's docs) on a Mux asset, via the addon proxy.
* @see {@link https://docs.mux.com/api-reference/video/assets/update-asset-master-access}
*/
function updateMasterAccess(client, assetId, masterAccess) {
let { dataset } = client.config();
return client.request({
url: `/addons/mux/assets/${dataset}/${assetId}/master-access`,
withCredentials: !0,
method: "PUT",
body: { master_access: masterAccess },
headers: { "Content-Type": "application/json" },
query: PLUGIN_VERSION_QUERY
});
}
/**
* @docs {@link https://docs.mux.com/api-reference#video/operation/list-assets}
*/
async function fetchMuxAssetsPage(client, cursor) {
try {
let response = await listAssets(client, {
limit: 100,
cursor
});
return {
cursor,
data: response.data,
next_cursor: response.next_cursor || null
};
} catch {
return {
cursor,
error: { _tag: "FetchError" }
};
}
}
function accumulateIntermediateState(currentState, pageResult) {
let currentData = "data" in currentState && currentState.data || [], { validAssets, skippedInThisPage } = ("data" in pageResult && pageResult.data || []).reduce((acc, asset) => {
let hasPlaybackIds = asset.playback_ids && asset.playback_ids.length > 0, isDuplicate = currentData.some((a) => a.id === asset.id);
return hasPlaybackIds || (acc.skippedInThisPage = !0), hasPlaybackIds && !isDuplicate && acc.validAssets.push(asset), acc;
}, {
validAssets: [],
skippedInThisPage: !1
});
return {
...currentState,
data: [...currentData, ...validAssets],
error: "error" in pageResult ? pageResult.error : void 0,
cursor: "next_cursor" in pageResult ? pageResult.next_cursor : pageResult.cursor,
loading: !0,
hasSkippedAssetsWithoutPlayback: currentState.hasSkippedAssetsWithoutPlayback || skippedInThisPage
};
}
function hasMorePages(pageResult) {
return typeof pageResult == "object" && "next_cursor" in pageResult && pageResult.next_cursor !== null;
}
/**
* Fetches all assets from a Mux environment. Rules:
* - One page at a time
* - Uses cursor-based pagination
* - We've finished fetching when `next_cursor` is null
* - Rate limiting to one request per 2 seconds
* - Update state while still fetching to give feedback to users
*/
function useMuxAssets(t0) {
let $ = c(7), { client, enabled } = t0, t1;
$[0] === Symbol.for("react.memo_cache_sentinel") ? (t1 = {
loading: !0,
cursor: null
}, $[0] = t1) : t1 = $[0];
let [state, setState] = useState(t1), t2;
$[1] !== client || $[2] !== enabled || $[3] !== state ? (t2 = () => {
if (!enabled) return;
let subscription = defer(() => fetchMuxAssetsPage(client, "data" in state && state.data && state.data.length > 0 && !state.error ? state.cursor : null)).pipe(expand((pageResult) => hasMorePages(pageResult) ? timer(2e3).pipe(concatMap(() => defer(() => fetchMuxAssetsPage(client, "next_cursor" in pageResult ? pageResult.next_cursor : null)))) : of()), tap((pageResult_0) => setState((prevState) => accumulateIntermediateState(prevState, pageResult_0)))).subscribe({ complete: () => {
setState(_temp$8);
} });
return () => subscription.unsubscribe();
}, $[1] = client, $[2] = enabled, $[3] = state, $[4] = t2) : t2 = $[4];
let t3;
return $[5] === enabled ? t3 = $[6] : (t3 = [enabled], $[5] = enabled, $[6] = t3), useEffect(t2, t3), state;
}
function _temp$8(prev) {
return {
...prev,
loading: !1
};
}
function useImportMuxAssets() {
let $ = c(27), documentStore = useDocumentStore(), t0;
$[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = { apiVersion: SANITY_API_VERSION }, $[0] = t0) : t0 = $[0];
let client = useClient(t0), [assetsInSanity, assetsInSanityLoading] = useAssetsInSanity(documentStore), hasSecrets = !!useSecretsDocumentValues().value.secrets?.secretKey, [importError, setImportError] = useState(), [importState, setImportState] = useState("closed"), dialogOpen = importState !== "closed", t1 = hasSecrets && dialogOpen, t2;
$[1] !== client || $[2] !== t1 ? (t2 = {
client,
enabled: t1
}, $[1] = client, $[2] = t1, $[3] = t2) : t2 = $[3];
let muxAssets = useMuxAssets(t2), t3;
$[4] !== assetsInSanity || $[5] !== muxAssets.data ? (t3 = assetsInSanity && muxAssets.data ? muxAssets.data.filter((a) => !assetExistsInSanity(a, assetsInSanity)) : void 0, $[4] = assetsInSanity, $[5] = muxAssets.data, $[6] = t3) : t3 = $[6];
let missingAssets = t3, t4;
$[7] === Symbol.for("react.memo_cache_sentinel") ? (t4 = [], $[7] = t4) : t4 = $[7];
let [selectedAssets, setSelectedAssets] = useState(t4), t5;
$[8] === importState ? t5 = $[9] : (t5 = () => {
importState !== "importing" && setImportState("closed");
}, $[8] = importState, $[9] = t5);
let closeDialog = t5, t6;
$[10] === importState ? t6 = $[11] : (t6 = () => {
importState === "closed" && setImportState("idle");
}, $[10] = importState, $[11] = t6);
let openDialog = t6, t7;
$[12] !== client || $[13] !== selectedAssets ? (t7 = async function importAssets() {
setImportState("importing");
let documents = selectedAssets.flatMap(_temp$7), tx = client.transaction();
documents.forEach((doc) => tx.create(doc));
try {
await tx.commit({ returnDocuments: !1 }), setSelectedAssets([]), setImportState("done");
} catch (t8) {
let error = t8;
setImportState("error"), setImportError(error);
}
}, $[12] = client, $[13] = selectedAssets, $[14] = t7) : t7 = $[14];
let importAssets = t7, t8;
return $[15] !== assetsInSanityLoading || $[16] !== closeDialog || $[17] !== dialogOpen || $[18] !== hasSecrets || $[19] !== importAssets || $[20] !== importError || $[21] !== importState || $[22] !== missingAssets || $[23] !== muxAssets || $[24] !== openDialog || $[25] !== selectedAssets ? (t8 = {
assetsInSanityLoading,
closeDialog,
dialogOpen,
importState,
importError,
hasSecrets,
importAssets,
missingAssets,
muxAssets,
openDialog,
selectedAssets,
setSelectedAssets
}, $[15] = assetsInSanityLoading, $[16] = closeDialog, $[17] = dialogOpen, $[18] = hasSecrets, $[19] = importAssets, $[20] = importError, $[21] = importState, $[22] = missingAssets, $[23] = muxAssets, $[24] = openDialog, $[25] = selectedAssets, $[26] = t8) : t8 = $[26], t8;
}
function _temp$7(asset) {
return muxAssetToSanityDocument(asset) || [];
}
function muxAssetToSanityDocument(asset) {
let playbackId = (asset.playback_ids || []).find((p) => p.id)?.id;
if (playbackId) return {
_id: uuid(),
_type: "mux.videoAsset",
_updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
_createdAt: parseMuxDate(asset.created_at).toISOString(),
assetId: asset.id,
playbackId,
filename: asset.meta?.title ?? generateAssetPlaceholder(asset.id),
status: asset.status,
data: asset
};
}
const useAssetsInSanity = createHookFromObservableFactory((documentStore) => documentStore.listenQuery("*[_type == \"mux.videoAsset\"] {\n \"uploadId\": coalesce(uploadId, data.upload_id),\n \"assetId\": coalesce(assetId, data.id),\n }", {}, { apiVersion: SANITY_API_VERSION }));
function assetExistsInSanity(asset, existingAssets) {
return asset.status === "ready" && existingAssets.some((existing) => existing.assetId === asset.id || existing.uploadId === asset.upload_id);
}
function useInView(ref, t0) {
let $ = c(6), t1;
$[0] === t0 ? t1 = $[1] : (t1 = t0 === void 0 ? {} : t0, $[0] = t0, $[1] = t1);
let options = t1, [inView, setInView] = useState(!1), t2, t3;
return $[2] !== options || $[3] !== ref ? (t2 = () => {
if (!ref.current) return;
let observer = new IntersectionObserver((t4, obs) => {
let [entry] = t4, nowInView = entry.isIntersecting && obs.thresholds.some((threshold) => entry.intersectionRatio >= threshold);
setInView(nowInView), options?.onChange?.(nowInView);
}, options), toObserve = ref.current;
return observer.observe(toObserve), () => {
toObserve && observer.unobserve(toObserve);
};
}, t3 = [options, ref], $[2] = options, $[3] = ref, $[4] = t2, $[5] = t3) : (t2 = $[4], t3 = $[5]), useEffect(t2, t3), inView;
}
function getPlaybackId(asset, priority = [
"drm",
"signed",
"public"
]) {
try {
if (!asset) throw TypeError("Tried to get playback Id with no asset");
let playbackIds = asset.data?.playback_ids;
if (playbackIds && playbackIds.length > 0) {
for (let policy of priority) {
let match = playbackIds.find((entry) => entry.policy === policy);
if (match) return match.id;
}
return playbackIds[0].id;
}
throw TypeError("Missing playbackId");
} catch (e) {
throw console.error("Asset is missing a playbackId", { asset }, e), e;
}
}
function getPlaybackPolicy(asset) {
return asset.data?.playback_ids?.find((playbackId) => getPlaybackId(asset, [
"drm",
"signed",
"public"
]) === playbackId.id) ?? {
id: "",
policy: "public"
};
}
function getPlaybackPolicyById(asset, playbackId) {
return asset.data?.playback_ids?.find((entry) => playbackId === entry.id);
}
function hasPlaybackPolicy(data, policy) {
return data.advanced_playback_policies && data.advanced_playback_policies.find((p) => p.policy === policy) || data.playback_policy?.find((p) => p === policy);
}
/**
* Uses suspend. Call this with {@link tryWithSuspend} or rethrow the Promise
*/
function generateJwt(client, playbackId, aud, payload) {
let { signingKeyId, signingKeyPrivate } = readSecrets(client);
if (!signingKeyId) throw TypeError("Missing `signingKeyId`.\n Check your plugin's configuration");
if (!signingKeyPrivate) throw TypeError("Missing `signingKeyPrivate`.\n Check your plugin's configuration");
let { default: sign } = suspend(() => import("jsonwebtoken-esm/sign"), ["jsonwebtoken-esm/sign"]);
return sign(payload ? JSON.parse(JSON.stringify(payload, (_, v) => v ?? void 0)) : {}, atob(signingKeyPrivate), {
algorithm: "RS256",
keyid: signingKeyId,
audience: aud,
subject: playbackId,
noTimestamp: !0,
expiresIn: "12h"
});
}
/**
* May throw a Promise. Call this with {@link tryWithSuspend} or rethrow the Promise
*/
function createUrlParamsObject(cli