sanity-plugin-mux-input 2.16.0 → 2.18.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 +916 -78
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +916 -78
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/actions/upload.ts +47 -7
- package/src/components/AddCaptionDialog.tsx +28 -9
- package/src/components/DraggableWatermark.tsx +877 -0
- package/src/components/EditCaptionDialog.tsx +4 -2
- package/src/components/UploadConfiguration.tsx +259 -59
- package/src/components/Uploader.tsx +7 -1
- package/src/components/VideoPlayer.tsx +2 -0
- package/src/hooks/useMediaMetadata.ts +3 -0
- package/src/util/convertWatermarkToMux.ts +160 -0
- package/src/util/formatDriveShareLink.ts +64 -0
- package/src/util/roundPxString.ts +16 -0
- package/src/util/types.ts +43 -1
package/dist/index.mjs
CHANGED
|
@@ -973,7 +973,7 @@ function tryWithSuspend(block, onError) {
|
|
|
973
973
|
return onError ? onError(errorOrPromise) : void 0;
|
|
974
974
|
}
|
|
975
975
|
}
|
|
976
|
-
const Image = styled.img`
|
|
976
|
+
const Image$1 = styled.img`
|
|
977
977
|
transition: opacity 0.175s ease-out 0s;
|
|
978
978
|
display: block;
|
|
979
979
|
width: 100%;
|
|
@@ -1051,7 +1051,7 @@ function VideoThumbnail({
|
|
|
1051
1051
|
}
|
|
1052
1052
|
),
|
|
1053
1053
|
/* @__PURE__ */ jsx(
|
|
1054
|
-
Image,
|
|
1054
|
+
Image$1,
|
|
1055
1055
|
{
|
|
1056
1056
|
src: thumbnailSrc ?? void 0,
|
|
1057
1057
|
alt: `Preview for ${staticImage ? "image" : "video"} ${asset.filename || asset.assetId}`,
|
|
@@ -1887,7 +1887,15 @@ function AddCaptionDialog({ asset, onAdd, onClose }) {
|
|
|
1887
1887
|
null
|
|
1888
1888
|
), [name2, setName] = useState(""), [isSubmitting, setIsSubmitting] = useState(!1), [selectedFile, setSelectedFile] = useState(null), fileInputRef = useRef(null), uploadVttFile = async (file) => (await client.assets.upload("file", file, {
|
|
1889
1889
|
filename: file.name
|
|
1890
|
-
})).url,
|
|
1890
|
+
})).url, refreshAssetData = async () => {
|
|
1891
|
+
if (!(!asset._id || !asset.assetId))
|
|
1892
|
+
try {
|
|
1893
|
+
const latestAssetData = await getAsset(client, asset.assetId), dataWithKeys = addKeysToMuxData(latestAssetData.data);
|
|
1894
|
+
await client.patch(asset._id).set({ data: dataWithKeys, status: latestAssetData.data.status }).commit({ returnDocuments: !1 });
|
|
1895
|
+
} catch (refreshError) {
|
|
1896
|
+
console.error("Failed to refresh asset data:", refreshError);
|
|
1897
|
+
}
|
|
1898
|
+
}, handleAddTrackFromUrl = async () => {
|
|
1891
1899
|
if (!asset.assetId)
|
|
1892
1900
|
throw new Error("Asset ID is required");
|
|
1893
1901
|
const trimmedName = name2.trim(), trimmedLanguageCode = languageCode.trim();
|
|
@@ -1897,9 +1905,9 @@ function AddCaptionDialog({ asset, onAdd, onClose }) {
|
|
|
1897
1905
|
vttUrlToUse = await uploadVttFile(selectedFile);
|
|
1898
1906
|
} catch (uploadError) {
|
|
1899
1907
|
throw toast.push({
|
|
1900
|
-
title: "Failed to upload
|
|
1908
|
+
title: "Failed to upload caption file",
|
|
1901
1909
|
status: "error",
|
|
1902
|
-
description: "Could not upload the
|
|
1910
|
+
description: "Could not upload the caption file to Sanity. Please try again."
|
|
1903
1911
|
}), setIsSubmitting(!1), uploadError;
|
|
1904
1912
|
}
|
|
1905
1913
|
await addTextTrackFromUrl(client, asset.assetId, vttUrlToUse, {
|
|
@@ -1912,13 +1920,13 @@ function AddCaptionDialog({ asset, onAdd, onClose }) {
|
|
|
1912
1920
|
assetId: asset.assetId,
|
|
1913
1921
|
trackName: trimmedName,
|
|
1914
1922
|
trackLanguageCode: trimmedLanguageCode,
|
|
1915
|
-
onTrackErrored: (track) => {
|
|
1923
|
+
onTrackErrored: async (track) => {
|
|
1916
1924
|
const errorMessage = track.error?.messages?.[0] || track.error?.type || "The track failed to download from the provided URL";
|
|
1917
1925
|
toast.push({
|
|
1918
1926
|
title: "Caption track failed",
|
|
1919
1927
|
status: "error",
|
|
1920
1928
|
description: errorMessage
|
|
1921
|
-
}), onAdd(track), onClose();
|
|
1929
|
+
}), await refreshAssetData(), onAdd(track), onClose();
|
|
1922
1930
|
}
|
|
1923
1931
|
});
|
|
1924
1932
|
if (!result.found || !result.track) {
|
|
@@ -1935,10 +1943,10 @@ function AddCaptionDialog({ asset, onAdd, onClose }) {
|
|
|
1935
1943
|
title: "Caption track is processing",
|
|
1936
1944
|
status: "info",
|
|
1937
1945
|
description: "The track was created and is being processed. It will appear in the list shortly."
|
|
1938
|
-
}), onAdd(result.track), onClose();
|
|
1946
|
+
}), await refreshAssetData(), onAdd(result.track), onClose();
|
|
1939
1947
|
return;
|
|
1940
1948
|
}
|
|
1941
|
-
toast.push({
|
|
1949
|
+
await refreshAssetData(), toast.push({
|
|
1942
1950
|
title: "Caption track added",
|
|
1943
1951
|
status: "success",
|
|
1944
1952
|
description: "Caption track added successfully"
|
|
@@ -1976,9 +1984,9 @@ function AddCaptionDialog({ asset, onAdd, onClose }) {
|
|
|
1976
1984
|
if (!isAutogenerated) {
|
|
1977
1985
|
if (!selectedFile && !vttUrl.trim()) {
|
|
1978
1986
|
toast.push({
|
|
1979
|
-
title: "
|
|
1987
|
+
title: "Caption file or URL required",
|
|
1980
1988
|
status: "error",
|
|
1981
|
-
description: "Please select a VTT file or enter a
|
|
1989
|
+
description: "Please select a VTT or SRT file or enter a caption file URL"
|
|
1982
1990
|
});
|
|
1983
1991
|
return;
|
|
1984
1992
|
}
|
|
@@ -1989,7 +1997,7 @@ function AddCaptionDialog({ asset, onAdd, onClose }) {
|
|
|
1989
1997
|
toast.push({
|
|
1990
1998
|
title: "Invalid URL",
|
|
1991
1999
|
status: "error",
|
|
1992
|
-
description: "Please enter a valid URL (e.g., https://example.com/subtitles.vtt)"
|
|
2000
|
+
description: "Please enter a valid URL (e.g., https://example.com/subtitles.vtt or subtitles.srt)"
|
|
1993
2001
|
});
|
|
1994
2002
|
return;
|
|
1995
2003
|
}
|
|
@@ -2071,7 +2079,7 @@ function AddCaptionDialog({ asset, onAdd, onClose }) {
|
|
|
2071
2079
|
{
|
|
2072
2080
|
ref: fileInputRef,
|
|
2073
2081
|
type: "file",
|
|
2074
|
-
accept: ".vtt,text/vtt",
|
|
2082
|
+
accept: ".vtt,text/vtt,.srt,application/x-subrip",
|
|
2075
2083
|
style: { display: "none" },
|
|
2076
2084
|
onChange: (e) => {
|
|
2077
2085
|
e.target.files && e.target.files.length > 0 && !isSubmitting && (setSelectedFile(e.target.files[0]), setVttUrl(""));
|
|
@@ -2079,9 +2087,9 @@ function AddCaptionDialog({ asset, onAdd, onClose }) {
|
|
|
2079
2087
|
}
|
|
2080
2088
|
)
|
|
2081
2089
|
] }),
|
|
2082
|
-
/* @__PURE__ */ jsx(Text, { size: 1, muted: !0, style: { textAlign: "center" }, children: "Or enter the
|
|
2090
|
+
/* @__PURE__ */ jsx(Text, { size: 1, muted: !0, style: { textAlign: "center" }, children: "Or enter the caption file URL" }),
|
|
2083
2091
|
/* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
2084
|
-
/* @__PURE__ */ jsx(Label$1, { htmlFor: "vtt-url", children: "
|
|
2092
|
+
/* @__PURE__ */ jsx(Label$1, { htmlFor: "vtt-url", children: "Caption File URL (.vtt or .srt)" }),
|
|
2085
2093
|
/* @__PURE__ */ jsx(
|
|
2086
2094
|
TextInput,
|
|
2087
2095
|
{
|
|
@@ -2214,8 +2222,8 @@ function EditCaptionDialog({ asset, track, onUpdate, onClose }) {
|
|
|
2214
2222
|
})).url, refreshAssetData = async () => {
|
|
2215
2223
|
if (!(!asset._id || !asset.assetId))
|
|
2216
2224
|
try {
|
|
2217
|
-
const latestAssetData = await getAsset(client, asset.assetId);
|
|
2218
|
-
await client.patch(asset._id).set({ data:
|
|
2225
|
+
const latestAssetData = await getAsset(client, asset.assetId), dataWithKeys = addKeysToMuxData(latestAssetData.data);
|
|
2226
|
+
await client.patch(asset._id).set({ data: dataWithKeys, status: latestAssetData.data.status }).commit({ returnDocuments: !1 });
|
|
2219
2227
|
} catch (refreshError) {
|
|
2220
2228
|
console.error("Failed to refresh asset data:", refreshError);
|
|
2221
2229
|
}
|
|
@@ -3059,7 +3067,7 @@ function VideoPlayer({
|
|
|
3059
3067
|
hlsConfig,
|
|
3060
3068
|
...props
|
|
3061
3069
|
}) {
|
|
3062
|
-
const client = useClient(), { dialogState } = useDialogStateContext(), isAudio = assetIsAudio(asset), muxPlayer = useRef(null), [error, setError] = useState(), playbackId = useMemo(() => {
|
|
3070
|
+
const client = useClient(), { dialogState } = useDialogStateContext(), isAudio = assetIsAudio(asset), muxPlayer = useRef(null), playerContainerRef = useRef(null), [error, setError] = useState(), playbackId = useMemo(() => {
|
|
3063
3071
|
try {
|
|
3064
3072
|
return getPlaybackId(asset, ["public", "signed", "drm"]);
|
|
3065
3073
|
} catch {
|
|
@@ -3117,6 +3125,7 @@ function VideoPlayer({
|
|
|
3117
3125
|
/* @__PURE__ */ jsxs(
|
|
3118
3126
|
Card,
|
|
3119
3127
|
{
|
|
3128
|
+
ref: playerContainerRef,
|
|
3120
3129
|
tone: "transparent",
|
|
3121
3130
|
style: {
|
|
3122
3131
|
aspectRatio,
|
|
@@ -3153,7 +3162,7 @@ function VideoPlayer({
|
|
|
3153
3162
|
crossOrigin: "anonymous",
|
|
3154
3163
|
metadata: {
|
|
3155
3164
|
player_name: "Sanity Admin Dashboard",
|
|
3156
|
-
player_version: "2.
|
|
3165
|
+
player_version: "2.18.0",
|
|
3157
3166
|
page_type: "Preview Player"
|
|
3158
3167
|
},
|
|
3159
3168
|
audio: isAudio,
|
|
@@ -4315,6 +4324,56 @@ function createUpChunkObservable(uuid2, uploadUrl2, source) {
|
|
|
4315
4324
|
return upchunk.on("success", successHandler), upchunk.on("error", errorHandler), upchunk.on("progress", progressHandler), upchunk.on("offline", offlineHandler), upchunk.on("online", onlineHandler), () => upchunk.abort();
|
|
4316
4325
|
});
|
|
4317
4326
|
}
|
|
4327
|
+
function formatDriveShareLink(url) {
|
|
4328
|
+
const formatExportLink = (id) => `https://drive.google.com/uc?export=download&id=${id}`;
|
|
4329
|
+
try {
|
|
4330
|
+
const trimmed = url.trim(), parsed = new URL(trimmed);
|
|
4331
|
+
if (parsed.hostname !== "drive.google.com")
|
|
4332
|
+
throw new Error("URL is not from Google Drive.");
|
|
4333
|
+
const id = parsed.searchParams.get("id") || "";
|
|
4334
|
+
if (id.length)
|
|
4335
|
+
return formatExportLink(id);
|
|
4336
|
+
const path2 = parsed.pathname.split("/") || [];
|
|
4337
|
+
if (path2.includes("file") && path2.includes("d")) {
|
|
4338
|
+
const index = path2.findIndex((value) => value === "d") + 1, fileId = path2.at(index) || "";
|
|
4339
|
+
return formatExportLink(fileId);
|
|
4340
|
+
}
|
|
4341
|
+
if (path2.includes("folders")) {
|
|
4342
|
+
const index = path2.findIndex((value) => value === "folders") + 1, folderId = path2.at(index) || "";
|
|
4343
|
+
return formatExportLink(folderId);
|
|
4344
|
+
}
|
|
4345
|
+
throw new Error("URL was not recognized.");
|
|
4346
|
+
} catch {
|
|
4347
|
+
return url;
|
|
4348
|
+
}
|
|
4349
|
+
}
|
|
4350
|
+
function roundPxString(value) {
|
|
4351
|
+
if (typeof value != "string") return;
|
|
4352
|
+
const trimmed = value.trim();
|
|
4353
|
+
if (!trimmed.endsWith("px")) return;
|
|
4354
|
+
const n = Number(trimmed.slice(0, -2));
|
|
4355
|
+
if (!Number.isFinite(n)) return;
|
|
4356
|
+
let rounded = Math.round(n);
|
|
4357
|
+
return rounded === 0 && (rounded = n < 0 ? -1 : 1), `${rounded}px`;
|
|
4358
|
+
}
|
|
4359
|
+
function sanitizeOverlaySettingsInPlace(settings) {
|
|
4360
|
+
const inputs = settings.input;
|
|
4361
|
+
if (inputs)
|
|
4362
|
+
for (const input of inputs) {
|
|
4363
|
+
const overlay = input.overlay_settings;
|
|
4364
|
+
if (!overlay) continue;
|
|
4365
|
+
const hm = roundPxString(overlay.horizontal_margin), vm = roundPxString(overlay.vertical_margin), w = roundPxString(overlay.width);
|
|
4366
|
+
hm && (overlay.horizontal_margin = hm), vm && (overlay.vertical_margin = vm), w && (overlay.width = w);
|
|
4367
|
+
}
|
|
4368
|
+
}
|
|
4369
|
+
function sanitizePxStringsInJson(json) {
|
|
4370
|
+
return json.replace(/"(-?\d+(?:\.\d+)?)px"/g, (_match, num) => {
|
|
4371
|
+
const n = Number(num);
|
|
4372
|
+
if (!Number.isFinite(n)) return _match;
|
|
4373
|
+
let rounded = Math.round(n);
|
|
4374
|
+
return rounded === 0 && (rounded = n < 0 ? -1 : 1), `"${rounded}px"`;
|
|
4375
|
+
});
|
|
4376
|
+
}
|
|
4318
4377
|
function cancelUpload(client, uuid2) {
|
|
4319
4378
|
return client.observable.request({
|
|
4320
4379
|
url: `/addons/mux/uploads/${client.config().dataset}/${uuid2}`,
|
|
@@ -4325,7 +4384,8 @@ function cancelUpload(client, uuid2) {
|
|
|
4325
4384
|
function uploadUrl({
|
|
4326
4385
|
url,
|
|
4327
4386
|
settings,
|
|
4328
|
-
client
|
|
4387
|
+
client,
|
|
4388
|
+
watermark
|
|
4329
4389
|
}) {
|
|
4330
4390
|
return testUrl(url).pipe(
|
|
4331
4391
|
switchMap((validUrl) => concat(
|
|
@@ -4335,9 +4395,9 @@ function uploadUrl({
|
|
|
4335
4395
|
if (!json || !json.status)
|
|
4336
4396
|
return throwError(new Error("Invalid credentials"));
|
|
4337
4397
|
const uuid$1 = uuid(), muxBody = settings;
|
|
4338
|
-
muxBody.input || (muxBody.input = [{ type: "video" }]), muxBody.input[0].url = validUrl;
|
|
4398
|
+
muxBody.input || (muxBody.input = [{ type: "video" }]), muxBody.input[0].url = validUrl, sanitizeOverlaySettingsInPlace(muxBody);
|
|
4339
4399
|
const query = {
|
|
4340
|
-
muxBody: JSON.stringify(muxBody),
|
|
4400
|
+
muxBody: sanitizePxStringsInJson(JSON.stringify(muxBody)),
|
|
4341
4401
|
filename: validUrl.split("/").slice(-1)[0]
|
|
4342
4402
|
}, dataset = client.config().dataset;
|
|
4343
4403
|
return defer(
|
|
@@ -4365,7 +4425,8 @@ function uploadUrl({
|
|
|
4365
4425
|
function uploadFile({
|
|
4366
4426
|
settings,
|
|
4367
4427
|
client,
|
|
4368
|
-
file
|
|
4428
|
+
file,
|
|
4429
|
+
watermark
|
|
4369
4430
|
}) {
|
|
4370
4431
|
return testFile(file).pipe(
|
|
4371
4432
|
switchMap((fileOptions) => concat(
|
|
@@ -4375,7 +4436,7 @@ function uploadFile({
|
|
|
4375
4436
|
if (!json || !json.status)
|
|
4376
4437
|
return throwError(() => new Error("Invalid credentials"));
|
|
4377
4438
|
const uuid$1 = uuid(), body = settings;
|
|
4378
|
-
return concat(
|
|
4439
|
+
return sanitizeOverlaySettingsInPlace(body), concat(
|
|
4379
4440
|
of({ type: "uuid", uuid: uuid$1 }),
|
|
4380
4441
|
defer(
|
|
4381
4442
|
() => client.observable.request({
|
|
@@ -4429,7 +4490,7 @@ function pollUpload(client, uuid2) {
|
|
|
4429
4490
|
}, 2e3);
|
|
4430
4491
|
});
|
|
4431
4492
|
}
|
|
4432
|
-
async function updateAssetDocumentFromUpload(client, uuid2) {
|
|
4493
|
+
async function updateAssetDocumentFromUpload(client, uuid2, _watermark) {
|
|
4433
4494
|
let upload, asset;
|
|
4434
4495
|
try {
|
|
4435
4496
|
upload = await pollUpload(client, uuid2);
|
|
@@ -4463,14 +4524,15 @@ function testUrl(url) {
|
|
|
4463
4524
|
const error = new Error("Invalid URL");
|
|
4464
4525
|
if (typeof url != "string")
|
|
4465
4526
|
return throwError(error);
|
|
4466
|
-
|
|
4527
|
+
let formattedUrl = url.trim();
|
|
4528
|
+
formattedUrl = formatDriveShareLink(formattedUrl);
|
|
4467
4529
|
let parsed;
|
|
4468
4530
|
try {
|
|
4469
|
-
parsed = new URL(
|
|
4531
|
+
parsed = new URL(formattedUrl);
|
|
4470
4532
|
} catch {
|
|
4471
4533
|
return throwError(error);
|
|
4472
4534
|
}
|
|
4473
|
-
return parsed && !parsed.protocol.match(/http:|https:/) ? throwError(error) : of(
|
|
4535
|
+
return parsed && !parsed.protocol.match(/http:|https:/) ? throwError(error) : of(formattedUrl);
|
|
4474
4536
|
}
|
|
4475
4537
|
function optionsFromFile(opts, file) {
|
|
4476
4538
|
if (!(typeof window > "u" || !(file instanceof window.File)))
|
|
@@ -4951,13 +5013,14 @@ function useMediaMetadata(stagedUpload) {
|
|
|
4951
5013
|
setIsLoadingMetadata(!1);
|
|
4952
5014
|
},
|
|
4953
5015
|
() => {
|
|
4954
|
-
const duration = videoElement.duration, width = videoElement.videoWidth, height = videoElement.videoHeight, isAudioOnly = width <= 0 && height <= 0;
|
|
5016
|
+
const duration = videoElement.duration, width = videoElement.videoWidth, height = videoElement.videoHeight, isAudioOnly = width <= 0 && height <= 0, aspectRatio = width / height;
|
|
4955
5017
|
setVideoAssetMetadata((old) => ({
|
|
4956
5018
|
...old,
|
|
4957
5019
|
duration,
|
|
4958
5020
|
width,
|
|
4959
5021
|
height,
|
|
4960
|
-
isAudioOnly
|
|
5022
|
+
isAudioOnly,
|
|
5023
|
+
aspectRatio
|
|
4961
5024
|
}));
|
|
4962
5025
|
}
|
|
4963
5026
|
], cleanupVideo = (videoEl) => {
|
|
@@ -4979,6 +5042,51 @@ function useMediaMetadata(stagedUpload) {
|
|
|
4979
5042
|
isLoadingMetadata
|
|
4980
5043
|
};
|
|
4981
5044
|
}
|
|
5045
|
+
function convertWatermarkToMuxOverlay(watermark, options) {
|
|
5046
|
+
if (!watermark.enabled || !watermark.imageUrl)
|
|
5047
|
+
return null;
|
|
5048
|
+
const size = watermark.size || 20, opacity = watermark.opacity ?? 0.7, toPxString = (valuePercent, axis) => {
|
|
5049
|
+
const videoAspectRatio2 = options?.videoAspectRatio ?? 1.7777777777777777, isVertical = videoAspectRatio2 > 0 && videoAspectRatio2 < 1, base = axis === "x" ? isVertical ? 1080 : 1920 : isVertical ? 1920 : 1080, px = valuePercent / 100 * base;
|
|
5050
|
+
let rounded = Math.round(px);
|
|
5051
|
+
return rounded === 0 && (rounded = px < 0 ? -1 : 1), `${rounded}px`;
|
|
5052
|
+
}, normalizeToPixels = (value, axis) => {
|
|
5053
|
+
if (!value) return value;
|
|
5054
|
+
const trimmed = value.trim();
|
|
5055
|
+
if (trimmed.endsWith("px"))
|
|
5056
|
+
return roundPxString(trimmed);
|
|
5057
|
+
if (trimmed.endsWith("%")) {
|
|
5058
|
+
const n = Number(trimmed.slice(0, -1));
|
|
5059
|
+
return Number.isFinite(n) ? toPxString(n, axis) : value;
|
|
5060
|
+
}
|
|
5061
|
+
return value;
|
|
5062
|
+
};
|
|
5063
|
+
if (watermark.overlay_settings) {
|
|
5064
|
+
const widthValue = watermark.overlay_settings.width, widthNormalized = options?.units === "px" ? normalizeToPixels(widthValue, "x") : widthValue;
|
|
5065
|
+
return {
|
|
5066
|
+
...watermark.overlay_settings,
|
|
5067
|
+
horizontal_margin: options?.units === "px" ? normalizeToPixels(watermark.overlay_settings.horizontal_margin, "x") ?? watermark.overlay_settings.horizontal_margin : watermark.overlay_settings.horizontal_margin,
|
|
5068
|
+
vertical_margin: options?.units === "px" ? normalizeToPixels(watermark.overlay_settings.vertical_margin, "y") ?? watermark.overlay_settings.vertical_margin : watermark.overlay_settings.vertical_margin,
|
|
5069
|
+
width: widthNormalized ?? `${size}%`,
|
|
5070
|
+
opacity: watermark.overlay_settings.opacity ?? `${Math.round(opacity * 100)}%`
|
|
5071
|
+
};
|
|
5072
|
+
}
|
|
5073
|
+
const position = watermark.position || { x: 50, y: 50 }, clampPercent = (value) => Math.max(-100, Math.min(100, value)), toPercentString = (value) => `${value === 0 || Object.is(value, -0) || Math.abs(value) < 1e-9 ? 0.01 : value}%`, watermarkWidthPercentOfVideoWidth = size, videoAspectRatio = options?.videoAspectRatio ?? 16 / 9, imageAspectRatio = watermark.imageAspectRatio ?? 1, watermarkHeightPercentOfVideoHeight = Math.max(
|
|
5074
|
+
0,
|
|
5075
|
+
Math.min(100, size * videoAspectRatio / imageAspectRatio)
|
|
5076
|
+
), halfWidth = watermarkWidthPercentOfVideoWidth / 2, halfHeight = watermarkHeightPercentOfVideoHeight / 2, leftMargin = clampPercent(
|
|
5077
|
+
Math.min(position.x - halfWidth, 100 - watermarkWidthPercentOfVideoWidth)
|
|
5078
|
+
), topMargin = clampPercent(
|
|
5079
|
+
Math.min(position.y - halfHeight, 100 - watermarkHeightPercentOfVideoHeight)
|
|
5080
|
+
), units = options?.units ?? "%", marginX = units === "px" ? toPxString(leftMargin, "x") : toPercentString(leftMargin), marginY = units === "px" ? toPxString(topMargin, "y") : toPercentString(topMargin), width = units === "px" ? toPxString(size, "x") : `${size}%`;
|
|
5081
|
+
return {
|
|
5082
|
+
vertical_align: "top",
|
|
5083
|
+
vertical_margin: marginY,
|
|
5084
|
+
horizontal_align: "left",
|
|
5085
|
+
horizontal_margin: marginX,
|
|
5086
|
+
width,
|
|
5087
|
+
opacity: `${Math.round(opacity * 100)}%`
|
|
5088
|
+
};
|
|
5089
|
+
}
|
|
4982
5090
|
function formatBytes(bytes, si = !1, dp = 1) {
|
|
4983
5091
|
const thresh = si ? 1e3 : 1024;
|
|
4984
5092
|
if (Math.abs(bytes) < thresh)
|
|
@@ -4991,6 +5099,566 @@ function formatBytes(bytes, si = !1, dp = 1) {
|
|
|
4991
5099
|
while (Math.round(Math.abs(bytes) * r) / r >= thresh && u2 < units.length - 1);
|
|
4992
5100
|
return bytes.toFixed(dp) + " " + units[u2];
|
|
4993
5101
|
}
|
|
5102
|
+
const RangeInput = styled.input`
|
|
5103
|
+
width: 100%;
|
|
5104
|
+
height: 4px;
|
|
5105
|
+
border-radius: 2px;
|
|
5106
|
+
background: var(--card-border-color);
|
|
5107
|
+
outline: none;
|
|
5108
|
+
-webkit-appearance: none;
|
|
5109
|
+
appearance: none;
|
|
5110
|
+
|
|
5111
|
+
&::-webkit-slider-thumb {
|
|
5112
|
+
-webkit-appearance: none;
|
|
5113
|
+
appearance: none;
|
|
5114
|
+
width: 16px;
|
|
5115
|
+
height: 16px;
|
|
5116
|
+
border-radius: 50%;
|
|
5117
|
+
background: var(--card-focus-ring-color, #2276fc);
|
|
5118
|
+
cursor: pointer;
|
|
5119
|
+
border: 2px solid white;
|
|
5120
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
|
5121
|
+
}
|
|
5122
|
+
|
|
5123
|
+
&::-moz-range-thumb {
|
|
5124
|
+
width: 16px;
|
|
5125
|
+
height: 16px;
|
|
5126
|
+
border-radius: 50%;
|
|
5127
|
+
background: var(--card-focus-ring-color, #2276fc);
|
|
5128
|
+
cursor: pointer;
|
|
5129
|
+
border: 2px solid white;
|
|
5130
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
|
5131
|
+
}
|
|
5132
|
+
|
|
5133
|
+
&:hover::-webkit-slider-thumb {
|
|
5134
|
+
background: var(--card-focus-ring-color, #1a5fc7);
|
|
5135
|
+
}
|
|
5136
|
+
|
|
5137
|
+
&:hover::-moz-range-thumb {
|
|
5138
|
+
background: var(--card-focus-ring-color, #1a5fc7);
|
|
5139
|
+
}
|
|
5140
|
+
`, WatermarkOverlay = styled.div`
|
|
5141
|
+
position: absolute;
|
|
5142
|
+
max-width: 200px;
|
|
5143
|
+
opacity: ${(props) => props.$opacity};
|
|
5144
|
+
cursor: move;
|
|
5145
|
+
user-select: none;
|
|
5146
|
+
z-index: 10;
|
|
5147
|
+
pointer-events: auto;
|
|
5148
|
+
|
|
5149
|
+
img {
|
|
5150
|
+
width: 100%;
|
|
5151
|
+
height: auto;
|
|
5152
|
+
display: block;
|
|
5153
|
+
pointer-events: none;
|
|
5154
|
+
}
|
|
5155
|
+
|
|
5156
|
+
&:hover {
|
|
5157
|
+
outline: 2px dashed rgba(255, 255, 255, 0.8);
|
|
5158
|
+
outline-offset: 4px;
|
|
5159
|
+
}
|
|
5160
|
+
`;
|
|
5161
|
+
function DraggableWatermark({
|
|
5162
|
+
watermark,
|
|
5163
|
+
onChange,
|
|
5164
|
+
containerRef,
|
|
5165
|
+
videoElementRef
|
|
5166
|
+
}) {
|
|
5167
|
+
const [isDragging, setIsDragging] = useState(!1), [dragStart, setDragStart] = useState({ x: 0, y: 0 }), [startPosition, setStartPosition] = useState({ x: 0, y: 0 }), watermarkRef = useRef(null), debounceTimeoutRef = useRef(null), [localPosition, setLocalPosition] = useState(watermark.position || { x: 50, y: 50 }), position = localPosition, size = watermark.size || 20, opacity = watermark.opacity ?? 0.7, parseOpacityPercent = (value) => {
|
|
5168
|
+
if (!value) return null;
|
|
5169
|
+
const trimmed = value.trim();
|
|
5170
|
+
if (!trimmed.endsWith("%")) return null;
|
|
5171
|
+
const num = Number(trimmed.slice(0, -1));
|
|
5172
|
+
return Number.isFinite(num) ? Math.max(0, Math.min(1, num / 100)) : null;
|
|
5173
|
+
}, getVideoContentBox = useCallback(() => {
|
|
5174
|
+
const container = containerRef?.current;
|
|
5175
|
+
if (!container) return { x: 0, y: 0, width: 0, height: 0 };
|
|
5176
|
+
const rect = container.getBoundingClientRect(), containerW = rect.width, containerH = rect.height, videoEl = videoElementRef?.current, videoW = videoEl?.videoWidth || 0, videoH = videoEl?.videoHeight || 0;
|
|
5177
|
+
if (!videoW || !videoH || !containerW || !containerH)
|
|
5178
|
+
return { x: 0, y: 0, width: containerW, height: containerH };
|
|
5179
|
+
const scale = Math.min(containerW / videoW, containerH / videoH), contentW = videoW * scale, contentH = videoH * scale, offsetX = (containerW - contentW) / 2, offsetY = (containerH - contentH) / 2;
|
|
5180
|
+
return { x: offsetX, y: offsetY, width: contentW, height: contentH };
|
|
5181
|
+
}, [containerRef, videoElementRef]), parseOverlayValue = (value) => {
|
|
5182
|
+
if (!value) return null;
|
|
5183
|
+
const trimmed = value.trim(), px = trimmed.endsWith("px"), pct = trimmed.endsWith("%"), num = Number(trimmed.replace(/px|%/g, ""));
|
|
5184
|
+
return Number.isFinite(num) ? px ? { n: num, unit: "px" } : pct ? { n: num, unit: "%" } : null : null;
|
|
5185
|
+
}, computeManualStyle = (overlay) => {
|
|
5186
|
+
const rect = containerRef?.current?.getBoundingClientRect(), w = rect?.width ?? 0, h = rect?.height ?? 0, isVertical = h > w, baseW = isVertical ? 1080 : 1920, baseH = isVertical ? 1920 : 1080, hm = parseOverlayValue(overlay.horizontal_margin), vm = parseOverlayValue(overlay.vertical_margin), ww = parseOverlayValue(overlay.width), manualOpacity = parseOpacityPercent(overlay.opacity), toCss = (v, axis) => {
|
|
5187
|
+
if (v)
|
|
5188
|
+
return v.unit === "%" ? `${v.n}%` : axis === "x" ? `${v.n * w / baseW}px` : `${v.n * h / baseH}px`;
|
|
5189
|
+
}, computeHorizontalStyle = () => overlay.horizontal_align === "left" ? { left: toCss(hm, "x"), right: void 0, transform: "translate(0, 0)" } : overlay.horizontal_align === "right" ? { right: toCss(hm, "x"), left: void 0, transform: "translate(0, 0)" } : { left: "50%", right: void 0, transform: "translate(-50%, 0)" }, computeVerticalStyle = () => overlay.vertical_align === "top" ? { top: toCss(vm, "y"), bottom: void 0 } : overlay.vertical_align === "bottom" ? { bottom: toCss(vm, "y"), top: void 0 } : { top: "50%", bottom: void 0 }, hStyle = computeHorizontalStyle(), vStyle = computeVerticalStyle();
|
|
5190
|
+
let transform = hStyle.transform;
|
|
5191
|
+
return overlay.vertical_align === "middle" && (transform = overlay.horizontal_align === "center" ? "translate(-50%, -50%)" : "translate(0, -50%)"), {
|
|
5192
|
+
position: "absolute",
|
|
5193
|
+
...hStyle,
|
|
5194
|
+
...vStyle,
|
|
5195
|
+
transform,
|
|
5196
|
+
width: ww ? toCss(ww, "x") : `${size}%`,
|
|
5197
|
+
opacity: manualOpacity ?? opacity,
|
|
5198
|
+
cursor: "default"
|
|
5199
|
+
};
|
|
5200
|
+
}, debouncedOnChange = useCallback(
|
|
5201
|
+
(newWatermark) => {
|
|
5202
|
+
debounceTimeoutRef.current && clearTimeout(debounceTimeoutRef.current), debounceTimeoutRef.current = setTimeout(() => {
|
|
5203
|
+
onChange(newWatermark);
|
|
5204
|
+
}, 300);
|
|
5205
|
+
},
|
|
5206
|
+
[onChange]
|
|
5207
|
+
);
|
|
5208
|
+
useEffect(() => () => {
|
|
5209
|
+
debounceTimeoutRef.current && clearTimeout(debounceTimeoutRef.current);
|
|
5210
|
+
}, []), useEffect(() => {
|
|
5211
|
+
!isDragging && watermark.position && setLocalPosition(watermark.position);
|
|
5212
|
+
}, [watermark.position, isDragging]);
|
|
5213
|
+
const handleMouseDown = useCallback(
|
|
5214
|
+
(e) => {
|
|
5215
|
+
e.preventDefault(), setIsDragging(!0), setDragStart({ x: e.clientX, y: e.clientY }), setStartPosition({ x: position.x, y: position.y });
|
|
5216
|
+
},
|
|
5217
|
+
[position]
|
|
5218
|
+
), handleMouseMove = useCallback(
|
|
5219
|
+
(e) => {
|
|
5220
|
+
if (!isDragging || !containerRef?.current) return;
|
|
5221
|
+
const rect = containerRef.current.getBoundingClientRect(), content = getVideoContentBox(), contentW = content.width || rect.width, contentH = content.height || rect.height, dx = e.clientX - dragStart.x, dy = e.clientY - dragStart.y, deltaXPercent = dx / contentW * 100, deltaYPercent = dy / contentH * 100;
|
|
5222
|
+
let newX = startPosition.x + deltaXPercent, newY = startPosition.y + deltaYPercent;
|
|
5223
|
+
newX = Math.max(0, Math.min(100, newX)), newY = Math.max(0, Math.min(100, newY)), setLocalPosition({ x: newX, y: newY }), debouncedOnChange({
|
|
5224
|
+
...watermark,
|
|
5225
|
+
position: { x: newX, y: newY }
|
|
5226
|
+
});
|
|
5227
|
+
},
|
|
5228
|
+
[
|
|
5229
|
+
isDragging,
|
|
5230
|
+
dragStart,
|
|
5231
|
+
startPosition,
|
|
5232
|
+
containerRef,
|
|
5233
|
+
watermark,
|
|
5234
|
+
debouncedOnChange,
|
|
5235
|
+
getVideoContentBox
|
|
5236
|
+
]
|
|
5237
|
+
), handleMouseUp = useCallback(() => {
|
|
5238
|
+
setIsDragging(!1), debounceTimeoutRef.current && (clearTimeout(debounceTimeoutRef.current), debounceTimeoutRef.current = null), onChange({
|
|
5239
|
+
...watermark,
|
|
5240
|
+
position: localPosition
|
|
5241
|
+
});
|
|
5242
|
+
}, [watermark, localPosition, onChange]);
|
|
5243
|
+
if (useEffect(() => {
|
|
5244
|
+
if (isDragging)
|
|
5245
|
+
return document.addEventListener("mousemove", handleMouseMove), document.addEventListener("mouseup", handleMouseUp), () => {
|
|
5246
|
+
document.removeEventListener("mousemove", handleMouseMove), document.removeEventListener("mouseup", handleMouseUp);
|
|
5247
|
+
};
|
|
5248
|
+
}, [isDragging, handleMouseMove, handleMouseUp]), !watermark.imageUrl)
|
|
5249
|
+
return null;
|
|
5250
|
+
const hasManualOverlay = !!watermark.overlay_settings, opacityForRender = hasManualOverlay ? parseOpacityPercent(watermark.overlay_settings?.opacity) ?? opacity : opacity, contentBox = getVideoContentBox(), hasContentBox = contentBox.width > 0 && contentBox.height > 0;
|
|
5251
|
+
return /* @__PURE__ */ jsx(
|
|
5252
|
+
WatermarkOverlay,
|
|
5253
|
+
{
|
|
5254
|
+
ref: watermarkRef,
|
|
5255
|
+
$opacity: opacityForRender,
|
|
5256
|
+
onMouseDown: hasManualOverlay ? void 0 : handleMouseDown,
|
|
5257
|
+
style: hasManualOverlay ? computeManualStyle(watermark.overlay_settings) : hasContentBox ? {
|
|
5258
|
+
left: `${contentBox.x + position.x / 100 * contentBox.width}px`,
|
|
5259
|
+
top: `${contentBox.y + position.y / 100 * contentBox.height}px`,
|
|
5260
|
+
transform: "translate(-50%, -50%)",
|
|
5261
|
+
width: `${Math.max(1, size / 100 * contentBox.width)}px`,
|
|
5262
|
+
cursor: isDragging ? "grabbing" : "grab"
|
|
5263
|
+
} : {
|
|
5264
|
+
left: `${position.x}%`,
|
|
5265
|
+
top: `${position.y}%`,
|
|
5266
|
+
transform: "translate(-50%, -50%)",
|
|
5267
|
+
width: `${size}%`,
|
|
5268
|
+
cursor: isDragging ? "grabbing" : "grab"
|
|
5269
|
+
},
|
|
5270
|
+
children: /* @__PURE__ */ jsx("img", { src: watermark.imageUrl, alt: "Watermark", draggable: !1 })
|
|
5271
|
+
}
|
|
5272
|
+
);
|
|
5273
|
+
}
|
|
5274
|
+
function WatermarkControls({
|
|
5275
|
+
watermark,
|
|
5276
|
+
onChange,
|
|
5277
|
+
onValidationChange,
|
|
5278
|
+
previewContainerRef,
|
|
5279
|
+
previewVideoRef
|
|
5280
|
+
}) {
|
|
5281
|
+
const [urlInput, setUrlInput] = useState(watermark.imageUrl || ""), [urlError, setUrlError] = useState(null), [isValidating, setIsValidating] = useState(!1), [isValid, setIsValid] = useState(null), validationTimeoutRef = useRef(null), [mode, setMode] = useState(
|
|
5282
|
+
watermark.overlay_settings ? "manual" : "canvas"
|
|
5283
|
+
), isUpdatingRef = useRef(!1), isValidExtension = (extension) => extension.endsWith(".png") || extension.endsWith(".jpg") || extension.endsWith(".jpeg"), validateUrl = useCallback(
|
|
5284
|
+
(url) => {
|
|
5285
|
+
if (validationTimeoutRef.current && clearTimeout(validationTimeoutRef.current), !url) {
|
|
5286
|
+
setUrlError(null), setIsValid(null), setIsValidating(!1), onValidationChange?.(null), isUpdatingRef.current = !0, onChange({
|
|
5287
|
+
...watermark,
|
|
5288
|
+
enabled: !1,
|
|
5289
|
+
imageUrl: void 0,
|
|
5290
|
+
overlay_settings: void 0
|
|
5291
|
+
});
|
|
5292
|
+
return;
|
|
5293
|
+
}
|
|
5294
|
+
setIsValidating(!0), setIsValid(null), setUrlError(null), validationTimeoutRef.current = setTimeout(() => {
|
|
5295
|
+
try {
|
|
5296
|
+
const pathname = new URL(url).pathname.toLowerCase();
|
|
5297
|
+
if (isValidExtension(pathname)) {
|
|
5298
|
+
setIsValid(!0), setUrlError(null), onValidationChange?.(null);
|
|
5299
|
+
const img = new Image();
|
|
5300
|
+
img.onload = () => {
|
|
5301
|
+
const imageAspectRatio = img.naturalWidth && img.naturalHeight ? img.naturalWidth / img.naturalHeight : 1;
|
|
5302
|
+
isUpdatingRef.current = !0, onChange({
|
|
5303
|
+
...watermark,
|
|
5304
|
+
enabled: !0,
|
|
5305
|
+
imageUrl: url,
|
|
5306
|
+
imageAspectRatio
|
|
5307
|
+
});
|
|
5308
|
+
}, img.onerror = () => {
|
|
5309
|
+
isUpdatingRef.current = !0, onChange({
|
|
5310
|
+
...watermark,
|
|
5311
|
+
enabled: !0,
|
|
5312
|
+
imageUrl: url,
|
|
5313
|
+
imageAspectRatio: watermark.imageAspectRatio
|
|
5314
|
+
});
|
|
5315
|
+
}, img.src = url;
|
|
5316
|
+
} else {
|
|
5317
|
+
const errorMsg = "Mux only supports PNG and JPG watermark images. Please use a .png or .jpg file.";
|
|
5318
|
+
setIsValid(!1), setUrlError(errorMsg), onValidationChange?.(errorMsg), isUpdatingRef.current = !0, onChange({
|
|
5319
|
+
...watermark,
|
|
5320
|
+
enabled: !1,
|
|
5321
|
+
imageUrl: void 0,
|
|
5322
|
+
imageAspectRatio: void 0,
|
|
5323
|
+
overlay_settings: void 0
|
|
5324
|
+
});
|
|
5325
|
+
}
|
|
5326
|
+
} catch {
|
|
5327
|
+
setIsValid(!1);
|
|
5328
|
+
const errorMsg = "Please enter a valid URL (e.g., https://example.com/watermark.png)";
|
|
5329
|
+
setUrlError(errorMsg), onValidationChange?.(errorMsg), isUpdatingRef.current = !0, onChange({
|
|
5330
|
+
...watermark,
|
|
5331
|
+
enabled: !1,
|
|
5332
|
+
imageUrl: void 0,
|
|
5333
|
+
imageAspectRatio: void 0,
|
|
5334
|
+
overlay_settings: void 0
|
|
5335
|
+
});
|
|
5336
|
+
} finally {
|
|
5337
|
+
setIsValidating(!1);
|
|
5338
|
+
}
|
|
5339
|
+
}, 500);
|
|
5340
|
+
},
|
|
5341
|
+
[watermark, onChange, onValidationChange]
|
|
5342
|
+
);
|
|
5343
|
+
useEffect(() => () => {
|
|
5344
|
+
validationTimeoutRef.current && clearTimeout(validationTimeoutRef.current);
|
|
5345
|
+
}, []), useEffect(() => {
|
|
5346
|
+
setMode(watermark.overlay_settings ? "manual" : "canvas");
|
|
5347
|
+
}, [watermark.overlay_settings]);
|
|
5348
|
+
const handleUrlChange = (e) => {
|
|
5349
|
+
const url = e.target.value;
|
|
5350
|
+
setUrlInput(url), watermark.imageUrl && url !== watermark.imageUrl && (isUpdatingRef.current = !0, onChange({
|
|
5351
|
+
...watermark,
|
|
5352
|
+
enabled: !1,
|
|
5353
|
+
imageUrl: void 0,
|
|
5354
|
+
imageAspectRatio: void 0,
|
|
5355
|
+
overlay_settings: void 0
|
|
5356
|
+
})), validateUrl(url);
|
|
5357
|
+
}, normalizeZeroPercent = (value) => {
|
|
5358
|
+
if (!value) return value;
|
|
5359
|
+
const trimmed = value.trim();
|
|
5360
|
+
if (!trimmed.endsWith("%")) return value;
|
|
5361
|
+
const n = Number(trimmed.slice(0, -1));
|
|
5362
|
+
return Number.isFinite(n) ? n === 0 || Object.is(n, -0) || Math.abs(n) < 1e-9 ? "0.01%" : `${n}%` : value;
|
|
5363
|
+
}, updateOverlaySettings = (next) => {
|
|
5364
|
+
const merged = {
|
|
5365
|
+
...watermark.overlay_settings ?? {
|
|
5366
|
+
vertical_align: "bottom",
|
|
5367
|
+
vertical_margin: "2%",
|
|
5368
|
+
horizontal_align: "right",
|
|
5369
|
+
horizontal_margin: "2%",
|
|
5370
|
+
width: `${watermark.size ?? 20}%`,
|
|
5371
|
+
opacity: `${Math.round((watermark.opacity ?? 0.7) * 100)}%`
|
|
5372
|
+
},
|
|
5373
|
+
...next
|
|
5374
|
+
};
|
|
5375
|
+
onChange({
|
|
5376
|
+
...watermark,
|
|
5377
|
+
enabled: !0,
|
|
5378
|
+
overlay_settings: {
|
|
5379
|
+
...merged,
|
|
5380
|
+
horizontal_margin: normalizeZeroPercent(merged.horizontal_margin) || merged.horizontal_margin,
|
|
5381
|
+
vertical_margin: normalizeZeroPercent(merged.vertical_margin) || merged.vertical_margin
|
|
5382
|
+
}
|
|
5383
|
+
});
|
|
5384
|
+
}, getVideoContentBox = () => {
|
|
5385
|
+
const container = previewContainerRef?.current;
|
|
5386
|
+
if (!container) return { x: 0, y: 0, width: 0, height: 0 };
|
|
5387
|
+
const rect = container.getBoundingClientRect(), containerW = rect.width, containerH = rect.height, videoEl = previewVideoRef?.current, videoW = videoEl?.videoWidth || 0, videoH = videoEl?.videoHeight || 0;
|
|
5388
|
+
if (!videoW || !videoH || !containerW || !containerH)
|
|
5389
|
+
return { x: 0, y: 0, width: containerW, height: containerH };
|
|
5390
|
+
const scale = Math.min(containerW / videoW, containerH / videoH), contentW = videoW * scale, contentH = videoH * scale, offsetX = (containerW - contentW) / 2, offsetY = (containerH - contentH) / 2;
|
|
5391
|
+
return { x: offsetX, y: offsetY, width: contentW, height: contentH };
|
|
5392
|
+
};
|
|
5393
|
+
return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
5394
|
+
/* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
5395
|
+
/* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: "Watermark Image URL" }),
|
|
5396
|
+
/* @__PURE__ */ jsx(Text, { size: 0, muted: !0, children: "Enter a URL to a PNG or JPG image. Mux will download this image and overlay it on your video." }),
|
|
5397
|
+
/* @__PURE__ */ jsxs(Box, { style: { position: "relative", width: "100%" }, children: [
|
|
5398
|
+
/* @__PURE__ */ jsx(
|
|
5399
|
+
"input",
|
|
5400
|
+
{
|
|
5401
|
+
type: "url",
|
|
5402
|
+
value: urlInput,
|
|
5403
|
+
onChange: handleUrlChange,
|
|
5404
|
+
placeholder: "https://example.com/watermark.png",
|
|
5405
|
+
style: {
|
|
5406
|
+
padding: "8px 12px",
|
|
5407
|
+
paddingRight: urlInput ? "96px" : isValid !== null ? "36px" : "12px",
|
|
5408
|
+
border: urlError || isValid === !1 ? "1px solid #e74c3c" : isValid === !0 ? "1px solid #4caf50" : "1px solid #ccc",
|
|
5409
|
+
borderRadius: "4px",
|
|
5410
|
+
width: "100%",
|
|
5411
|
+
maxWidth: "100%",
|
|
5412
|
+
boxSizing: "border-box",
|
|
5413
|
+
fontSize: "14px"
|
|
5414
|
+
}
|
|
5415
|
+
}
|
|
5416
|
+
),
|
|
5417
|
+
(urlInput || isValidating || isValid !== null) && /* @__PURE__ */ jsxs(
|
|
5418
|
+
Box,
|
|
5419
|
+
{
|
|
5420
|
+
style: {
|
|
5421
|
+
position: "absolute",
|
|
5422
|
+
right: "8px",
|
|
5423
|
+
top: "50%",
|
|
5424
|
+
transform: "translateY(-50%)",
|
|
5425
|
+
display: "flex",
|
|
5426
|
+
alignItems: "center",
|
|
5427
|
+
gap: "4px"
|
|
5428
|
+
},
|
|
5429
|
+
children: [
|
|
5430
|
+
urlInput && /* @__PURE__ */ jsx(
|
|
5431
|
+
Button,
|
|
5432
|
+
{
|
|
5433
|
+
text: "Clear",
|
|
5434
|
+
mode: "bleed",
|
|
5435
|
+
tone: "critical",
|
|
5436
|
+
onClick: () => {
|
|
5437
|
+
setUrlInput(""), validateUrl("");
|
|
5438
|
+
},
|
|
5439
|
+
disabled: isValidating,
|
|
5440
|
+
style: { fontSize: "11px", height: "24px" }
|
|
5441
|
+
}
|
|
5442
|
+
),
|
|
5443
|
+
isValidating && /* @__PURE__ */ jsx(Text, { size: 0, muted: !0, children: "Validating..." }),
|
|
5444
|
+
isValid === !0 && !isValidating && /* @__PURE__ */ jsx(CheckmarkCircleIcon, { style: { color: "#4caf50", fontSize: "18px" } }),
|
|
5445
|
+
isValid === !1 && !isValidating && /* @__PURE__ */ jsx(ErrorOutlineIcon, { style: { color: "#e74c3c", fontSize: "18px" } })
|
|
5446
|
+
]
|
|
5447
|
+
}
|
|
5448
|
+
)
|
|
5449
|
+
] }),
|
|
5450
|
+
urlError && /* @__PURE__ */ jsx(Card, { padding: 2, tone: "critical", radius: 2, children: /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
|
|
5451
|
+
/* @__PURE__ */ jsx(ErrorOutlineIcon, { style: { color: "#e74c3c", flexShrink: 0 } }),
|
|
5452
|
+
/* @__PURE__ */ jsx(Text, { size: 0, style: { color: "#e74c3c" }, children: urlError })
|
|
5453
|
+
] }) })
|
|
5454
|
+
] }),
|
|
5455
|
+
watermark.imageUrl && /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
5456
|
+
/* @__PURE__ */ jsx(Card, { padding: 3, tone: "transparent", border: !0, radius: 2, children: /* @__PURE__ */ jsxs(
|
|
5457
|
+
Flex,
|
|
5458
|
+
{
|
|
5459
|
+
align: "center",
|
|
5460
|
+
justify: "space-between",
|
|
5461
|
+
gap: 3,
|
|
5462
|
+
style: { flexWrap: "wrap", alignItems: "flex-start" },
|
|
5463
|
+
children: [
|
|
5464
|
+
/* @__PURE__ */ jsxs(Stack, { space: 2, style: { minWidth: 240, flex: 1 }, children: [
|
|
5465
|
+
/* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: "Positioning mode" }),
|
|
5466
|
+
/* @__PURE__ */ jsxs(Text, { size: 0, muted: !0, children: [
|
|
5467
|
+
"Choose between dragging on the canvas or manually editing the Mux",
|
|
5468
|
+
" ",
|
|
5469
|
+
/* @__PURE__ */ jsx("code", { children: "overlay_settings" }),
|
|
5470
|
+
" fields (as in",
|
|
5471
|
+
" ",
|
|
5472
|
+
/* @__PURE__ */ jsx(
|
|
5473
|
+
"a",
|
|
5474
|
+
{
|
|
5475
|
+
href: "https://www.mux.com/docs/guides/add-watermarks-to-your-videos",
|
|
5476
|
+
target: "_blank",
|
|
5477
|
+
rel: "noopener noreferrer",
|
|
5478
|
+
children: "the docs"
|
|
5479
|
+
}
|
|
5480
|
+
),
|
|
5481
|
+
")."
|
|
5482
|
+
] })
|
|
5483
|
+
] }),
|
|
5484
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, style: { flexWrap: "wrap" }, children: [
|
|
5485
|
+
/* @__PURE__ */ jsx(
|
|
5486
|
+
Button,
|
|
5487
|
+
{
|
|
5488
|
+
text: "Canvas",
|
|
5489
|
+
mode: mode === "canvas" ? "default" : "ghost",
|
|
5490
|
+
onClick: () => {
|
|
5491
|
+
setMode("canvas"), onChange({ ...watermark, enabled: !0, overlay_settings: void 0 });
|
|
5492
|
+
}
|
|
5493
|
+
}
|
|
5494
|
+
),
|
|
5495
|
+
/* @__PURE__ */ jsx(
|
|
5496
|
+
Button,
|
|
5497
|
+
{
|
|
5498
|
+
text: "Manual",
|
|
5499
|
+
mode: mode === "manual" ? "default" : "ghost",
|
|
5500
|
+
onClick: () => {
|
|
5501
|
+
setMode("manual");
|
|
5502
|
+
const overlay = convertWatermarkToMuxOverlay({ ...watermark, enabled: !0 });
|
|
5503
|
+
updateOverlaySettings(overlay ?? {});
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
)
|
|
5507
|
+
] })
|
|
5508
|
+
]
|
|
5509
|
+
}
|
|
5510
|
+
) }),
|
|
5511
|
+
mode === "manual" && /* @__PURE__ */ jsx(Card, { padding: 3, tone: "transparent", border: !0, radius: 2, children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
5512
|
+
/* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: "Mux overlay_settings" }),
|
|
5513
|
+
/* @__PURE__ */ jsxs(Grid, { columns: [1, 2], gap: 3, style: { width: "100%" }, children: [
|
|
5514
|
+
/* @__PURE__ */ jsxs(Stack, { space: 2, style: { minWidth: 0 }, children: [
|
|
5515
|
+
/* @__PURE__ */ jsx(Text, { size: 0, muted: !0, children: "horizontal_align" }),
|
|
5516
|
+
/* @__PURE__ */ jsxs(
|
|
5517
|
+
"select",
|
|
5518
|
+
{
|
|
5519
|
+
value: watermark.overlay_settings?.horizontal_align || "right",
|
|
5520
|
+
onChange: (e) => updateOverlaySettings({
|
|
5521
|
+
horizontal_align: e.target.value || "right"
|
|
5522
|
+
}),
|
|
5523
|
+
style: {
|
|
5524
|
+
width: "100%",
|
|
5525
|
+
padding: "8px 10px",
|
|
5526
|
+
border: "1px solid #ccc",
|
|
5527
|
+
borderRadius: 4
|
|
5528
|
+
},
|
|
5529
|
+
children: [
|
|
5530
|
+
/* @__PURE__ */ jsx("option", { value: "left", children: "left" }),
|
|
5531
|
+
/* @__PURE__ */ jsx("option", { value: "center", children: "center" }),
|
|
5532
|
+
/* @__PURE__ */ jsx("option", { value: "right", children: "right" })
|
|
5533
|
+
]
|
|
5534
|
+
}
|
|
5535
|
+
)
|
|
5536
|
+
] }),
|
|
5537
|
+
/* @__PURE__ */ jsxs(Stack, { space: 2, style: { minWidth: 0 }, children: [
|
|
5538
|
+
/* @__PURE__ */ jsx(Text, { size: 0, muted: !0, children: "horizontal_margin (e.g. 2% or 40px)" }),
|
|
5539
|
+
/* @__PURE__ */ jsx(
|
|
5540
|
+
TextInput,
|
|
5541
|
+
{
|
|
5542
|
+
value: watermark.overlay_settings?.horizontal_margin || "2%",
|
|
5543
|
+
onChange: (e) => updateOverlaySettings({ horizontal_margin: e.currentTarget.value })
|
|
5544
|
+
}
|
|
5545
|
+
)
|
|
5546
|
+
] }),
|
|
5547
|
+
/* @__PURE__ */ jsxs(Stack, { space: 2, style: { minWidth: 0 }, children: [
|
|
5548
|
+
/* @__PURE__ */ jsx(Text, { size: 0, muted: !0, children: "vertical_align" }),
|
|
5549
|
+
/* @__PURE__ */ jsxs(
|
|
5550
|
+
"select",
|
|
5551
|
+
{
|
|
5552
|
+
value: watermark.overlay_settings?.vertical_align || "bottom",
|
|
5553
|
+
onChange: (e) => updateOverlaySettings({
|
|
5554
|
+
vertical_align: e.target.value || "bottom"
|
|
5555
|
+
}),
|
|
5556
|
+
style: {
|
|
5557
|
+
width: "100%",
|
|
5558
|
+
padding: "8px 10px",
|
|
5559
|
+
border: "1px solid #ccc",
|
|
5560
|
+
borderRadius: 4
|
|
5561
|
+
},
|
|
5562
|
+
children: [
|
|
5563
|
+
/* @__PURE__ */ jsx("option", { value: "top", children: "top" }),
|
|
5564
|
+
/* @__PURE__ */ jsx("option", { value: "middle", children: "middle" }),
|
|
5565
|
+
/* @__PURE__ */ jsx("option", { value: "bottom", children: "bottom" })
|
|
5566
|
+
]
|
|
5567
|
+
}
|
|
5568
|
+
)
|
|
5569
|
+
] }),
|
|
5570
|
+
/* @__PURE__ */ jsxs(Stack, { space: 2, style: { minWidth: 0 }, children: [
|
|
5571
|
+
/* @__PURE__ */ jsx(Text, { size: 0, muted: !0, children: "vertical_margin (e.g. 2% or 40px)" }),
|
|
5572
|
+
/* @__PURE__ */ jsx(
|
|
5573
|
+
TextInput,
|
|
5574
|
+
{
|
|
5575
|
+
value: watermark.overlay_settings?.vertical_margin || "2%",
|
|
5576
|
+
onChange: (e) => updateOverlaySettings({ vertical_margin: e.currentTarget.value })
|
|
5577
|
+
}
|
|
5578
|
+
)
|
|
5579
|
+
] }),
|
|
5580
|
+
/* @__PURE__ */ jsxs(Stack, { space: 2, style: { minWidth: 0 }, children: [
|
|
5581
|
+
/* @__PURE__ */ jsx(Text, { size: 0, muted: !0, children: "width (e.g. 25% or 80px)" }),
|
|
5582
|
+
/* @__PURE__ */ jsx(
|
|
5583
|
+
TextInput,
|
|
5584
|
+
{
|
|
5585
|
+
value: watermark.overlay_settings?.width || `${watermark.size ?? 20}%`,
|
|
5586
|
+
onChange: (e) => updateOverlaySettings({ width: e.currentTarget.value })
|
|
5587
|
+
}
|
|
5588
|
+
)
|
|
5589
|
+
] }),
|
|
5590
|
+
/* @__PURE__ */ jsxs(Stack, { space: 2, style: { minWidth: 0 }, children: [
|
|
5591
|
+
/* @__PURE__ */ jsx(Text, { size: 0, muted: !0, children: "opacity (e.g. 90%)" }),
|
|
5592
|
+
/* @__PURE__ */ jsx(
|
|
5593
|
+
TextInput,
|
|
5594
|
+
{
|
|
5595
|
+
value: watermark.overlay_settings?.opacity || `${Math.round((watermark.opacity ?? 0.7) * 100)}%`,
|
|
5596
|
+
onChange: (e) => updateOverlaySettings({ opacity: e.currentTarget.value })
|
|
5597
|
+
}
|
|
5598
|
+
)
|
|
5599
|
+
] })
|
|
5600
|
+
] }),
|
|
5601
|
+
/* @__PURE__ */ jsx(Text, { size: 0, muted: !0, children: "Margins and width accept either percentages or pixels, per the Mux guide." })
|
|
5602
|
+
] }) }),
|
|
5603
|
+
mode === "canvas" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5604
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
5605
|
+
/* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: (() => {
|
|
5606
|
+
const sizePct = watermark.size || 20, contentW = getVideoContentBox().width;
|
|
5607
|
+
return contentW ? `Size: ${Math.max(1, Math.round(sizePct / 100 * contentW))}px` : `Size: ${sizePct}%`;
|
|
5608
|
+
})() }),
|
|
5609
|
+
/* @__PURE__ */ jsx(
|
|
5610
|
+
RangeInput,
|
|
5611
|
+
{
|
|
5612
|
+
type: "range",
|
|
5613
|
+
value: (() => {
|
|
5614
|
+
const sizePct = watermark.size || 20, contentW = getVideoContentBox().width;
|
|
5615
|
+
return contentW ? Math.max(1, Math.round(sizePct / 100 * contentW)) : sizePct;
|
|
5616
|
+
})(),
|
|
5617
|
+
min: (() => {
|
|
5618
|
+
const contentW = getVideoContentBox().width;
|
|
5619
|
+
return contentW ? Math.max(1, Math.round(contentW * 0.05)) : 5;
|
|
5620
|
+
})(),
|
|
5621
|
+
max: (() => {
|
|
5622
|
+
const contentW = getVideoContentBox().width;
|
|
5623
|
+
return contentW ? Math.max(1, Math.round(contentW * 0.5)) : 50;
|
|
5624
|
+
})(),
|
|
5625
|
+
step: 1,
|
|
5626
|
+
onChange: (e) => {
|
|
5627
|
+
const raw = Number(e.target.value), contentW = getVideoContentBox().width, nextPct = contentW ? raw / contentW * 100 : raw, clampedPct = Math.max(5, Math.min(50, nextPct));
|
|
5628
|
+
onChange({
|
|
5629
|
+
...watermark,
|
|
5630
|
+
size: clampedPct
|
|
5631
|
+
});
|
|
5632
|
+
}
|
|
5633
|
+
}
|
|
5634
|
+
)
|
|
5635
|
+
] }),
|
|
5636
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
5637
|
+
/* @__PURE__ */ jsxs(Text, { size: 1, weight: "medium", children: [
|
|
5638
|
+
"Opacity: ",
|
|
5639
|
+
Math.round((watermark.opacity ?? 0.7) * 100),
|
|
5640
|
+
"%"
|
|
5641
|
+
] }),
|
|
5642
|
+
/* @__PURE__ */ jsx(
|
|
5643
|
+
RangeInput,
|
|
5644
|
+
{
|
|
5645
|
+
type: "range",
|
|
5646
|
+
value: watermark.opacity ?? 0.7,
|
|
5647
|
+
min: 0,
|
|
5648
|
+
max: 1,
|
|
5649
|
+
step: 0.05,
|
|
5650
|
+
onChange: (e) => onChange({
|
|
5651
|
+
...watermark,
|
|
5652
|
+
opacity: Number(e.target.value)
|
|
5653
|
+
})
|
|
5654
|
+
}
|
|
5655
|
+
)
|
|
5656
|
+
] })
|
|
5657
|
+
] }),
|
|
5658
|
+
/* @__PURE__ */ jsx(Card, { padding: 2, tone: "transparent", border: !0, radius: 2, children: /* @__PURE__ */ jsx(Text, { size: 0, muted: !0, children: mode === "manual" ? "Manual mode: edit the overlay_settings fields above" : "\u{1F4A1} Drag the watermark on the preview to position it" }) })
|
|
5659
|
+
] })
|
|
5660
|
+
] });
|
|
5661
|
+
}
|
|
4994
5662
|
const ALL_LANGUAGE_CODES = LanguagesList.getAllCodes().map((code) => ({
|
|
4995
5663
|
value: code,
|
|
4996
5664
|
label: LanguagesList.getNativeName(code)
|
|
@@ -5432,7 +6100,7 @@ function UploadConfiguration({
|
|
|
5432
6100
|
startUpload,
|
|
5433
6101
|
onClose
|
|
5434
6102
|
}) {
|
|
5435
|
-
const id = useId(), autoTextTracks = useRef(
|
|
6103
|
+
const id = useId(), [watermarkValidationError, setWatermarkValidationError] = useState(null), watermarkPreviewContainerRef = useRef(null), watermarkPreviewVideoRef = useRef(null), autoTextTracks = useRef(
|
|
5436
6104
|
pluginConfig.video_quality === "plus" && pluginConfig.defaultAutogeneratedSubtitleLang ? [
|
|
5437
6105
|
{
|
|
5438
6106
|
_id: uuid(),
|
|
@@ -5468,6 +6136,8 @@ function UploadConfiguration({
|
|
|
5468
6136
|
return Object.assign({}, prev, { [action.action]: action.value });
|
|
5469
6137
|
case "drm_policy":
|
|
5470
6138
|
return Object.assign({}, prev, { [action.action]: action.value });
|
|
6139
|
+
case "watermark":
|
|
6140
|
+
return Object.assign({}, prev, { watermark: action.value });
|
|
5471
6141
|
// Updating individual tracks
|
|
5472
6142
|
case "track": {
|
|
5473
6143
|
const text_tracks = [...prev.text_tracks], target_track_i = text_tracks.findIndex(({ _id: _id2 }) => _id2 === action.id);
|
|
@@ -5535,7 +6205,12 @@ function UploadConfiguration({
|
|
|
5535
6205
|
]);
|
|
5536
6206
|
const { disableTextTrackConfig, disableUploadConfig } = pluginConfig, skipConfig = disableTextTrackConfig && disableUploadConfig;
|
|
5537
6207
|
if (useEffect(() => {
|
|
5538
|
-
|
|
6208
|
+
if (skipConfig) {
|
|
6209
|
+
const { settings, watermark } = formatUploadConfig(config, secrets, {
|
|
6210
|
+
videoAspectRatio: videoAssetMetadata?.aspectRatio
|
|
6211
|
+
});
|
|
6212
|
+
startUpload(settings, watermark);
|
|
6213
|
+
}
|
|
5539
6214
|
}, []), skipConfig) return null;
|
|
5540
6215
|
const basicConfig = config.video_quality !== "plus" && config.video_quality !== "premium", playbackPolicySelected = config.public_policy || config.signed_policy || config.drm_policy, maxSupportedResolution = RESOLUTION_TIERS.findIndex(
|
|
5541
6216
|
(rt) => rt.value === pluginConfig.max_resolution_tier
|
|
@@ -5551,11 +6226,11 @@ function UploadConfiguration({
|
|
|
5551
6226
|
header: "Configure Mux Upload",
|
|
5552
6227
|
onClose,
|
|
5553
6228
|
children: /* @__PURE__ */ jsxs(Stack, { padding: 4, space: 2, children: [
|
|
5554
|
-
validationError && /* @__PURE__ */ jsx(Card, { padding: 3, tone: "critical", radius: 2, marginBottom: 2, children: /* @__PURE__ */ jsxs(Flex, { gap: 2, align: "flex-start", children: [
|
|
6229
|
+
(validationError || watermarkValidationError) && /* @__PURE__ */ jsx(Card, { padding: 3, tone: "critical", radius: 2, marginBottom: 2, children: /* @__PURE__ */ jsxs(Flex, { gap: 2, align: "flex-start", children: [
|
|
5555
6230
|
/* @__PURE__ */ jsx(ErrorOutlineIcon, { width: 20, height: 20 }),
|
|
5556
6231
|
/* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
5557
6232
|
/* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: "Validation Error" }),
|
|
5558
|
-
/* @__PURE__ */ jsx(Text, { size: 1, children: validationError })
|
|
6233
|
+
/* @__PURE__ */ jsx(Text, { size: 1, children: validationError || watermarkValidationError })
|
|
5559
6234
|
] })
|
|
5560
6235
|
] }) }),
|
|
5561
6236
|
/* @__PURE__ */ jsx(Label$1, { size: 3, children: "FILE TO UPLOAD" }),
|
|
@@ -5622,27 +6297,41 @@ function UploadConfiguration({
|
|
|
5622
6297
|
}) })
|
|
5623
6298
|
}
|
|
5624
6299
|
),
|
|
5625
|
-
!basicConfig && /* @__PURE__ */
|
|
5626
|
-
/* @__PURE__ */ jsx(
|
|
5627
|
-
|
|
5628
|
-
|
|
6300
|
+
!basicConfig && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6301
|
+
/* @__PURE__ */ jsx(FormField$2, { title: "Additional Configuration", children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
6302
|
+
/* @__PURE__ */ jsx(PlaybackPolicy, { id, config, secrets, dispatch }),
|
|
6303
|
+
maxSupportedResolution > 0 && /* @__PURE__ */ jsx(
|
|
6304
|
+
ResolutionTierSelector,
|
|
6305
|
+
{
|
|
6306
|
+
id,
|
|
6307
|
+
config,
|
|
6308
|
+
dispatch,
|
|
6309
|
+
maxSupportedResolution
|
|
6310
|
+
}
|
|
6311
|
+
),
|
|
6312
|
+
/* @__PURE__ */ jsx(StaticRenditionSelector, { id, config, dispatch }),
|
|
6313
|
+
!disableTextTrackConfig && /* @__PURE__ */ jsx(
|
|
6314
|
+
TextTracksEditor,
|
|
6315
|
+
{
|
|
6316
|
+
tracks: config.text_tracks,
|
|
6317
|
+
dispatch,
|
|
6318
|
+
defaultLang: pluginConfig.defaultAutogeneratedSubtitleLang
|
|
6319
|
+
}
|
|
6320
|
+
)
|
|
6321
|
+
] }) }),
|
|
6322
|
+
/* @__PURE__ */ jsx(
|
|
6323
|
+
WatermarkSection,
|
|
5629
6324
|
{
|
|
5630
|
-
id,
|
|
5631
6325
|
config,
|
|
5632
6326
|
dispatch,
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
TextTracksEditor,
|
|
5639
|
-
{
|
|
5640
|
-
tracks: config.text_tracks,
|
|
5641
|
-
dispatch,
|
|
5642
|
-
defaultLang: pluginConfig.defaultAutogeneratedSubtitleLang
|
|
6327
|
+
stagedUpload,
|
|
6328
|
+
videoAssetMetadata,
|
|
6329
|
+
watermarkPreviewContainerRef,
|
|
6330
|
+
watermarkPreviewVideoRef,
|
|
6331
|
+
onValidationChange: setWatermarkValidationError
|
|
5643
6332
|
}
|
|
5644
6333
|
)
|
|
5645
|
-
] })
|
|
6334
|
+
] })
|
|
5646
6335
|
] }),
|
|
5647
6336
|
/* @__PURE__ */ jsx(Box, { marginTop: 4, children: /* @__PURE__ */ jsx(
|
|
5648
6337
|
Button,
|
|
@@ -5652,7 +6341,12 @@ function UploadConfiguration({
|
|
|
5652
6341
|
text: "Upload",
|
|
5653
6342
|
tone: "positive",
|
|
5654
6343
|
onClick: () => {
|
|
5655
|
-
|
|
6344
|
+
if (!validationError) {
|
|
6345
|
+
const { settings, watermark } = formatUploadConfig(config, secrets, {
|
|
6346
|
+
videoAspectRatio: videoAssetMetadata?.aspectRatio
|
|
6347
|
+
});
|
|
6348
|
+
startUpload(settings, watermark);
|
|
6349
|
+
}
|
|
5656
6350
|
}
|
|
5657
6351
|
}
|
|
5658
6352
|
) })
|
|
@@ -5667,36 +6361,178 @@ function setAdvancedPlaybackPolicy(config, secrets) {
|
|
|
5667
6361
|
drm_configuration_id: secrets.drmConfigId ?? void 0
|
|
5668
6362
|
}) : console.error("Selected DRM Policy but missing DRM Configuration Id")), advanced_playback_policies;
|
|
5669
6363
|
}
|
|
5670
|
-
function formatUploadConfig(config, secrets) {
|
|
6364
|
+
function formatUploadConfig(config, secrets, options) {
|
|
5671
6365
|
const generated_subtitles = config.text_tracks.filter(isAutogeneratedTrack).map((track) => ({
|
|
5672
6366
|
name: track.name,
|
|
5673
6367
|
language_code: track.language_code
|
|
5674
|
-
}))
|
|
6368
|
+
})), inputs = [
|
|
6369
|
+
{
|
|
6370
|
+
type: "video",
|
|
6371
|
+
generated_subtitles: generated_subtitles.length > 0 ? generated_subtitles : void 0
|
|
6372
|
+
},
|
|
6373
|
+
...config.text_tracks.filter(isCustomTextTrack).reduce(
|
|
6374
|
+
(acc, track) => (track.language_code && track.file && track.name && acc.push({
|
|
6375
|
+
url: track.file.contents,
|
|
6376
|
+
type: "text",
|
|
6377
|
+
text_type: track.type === "subtitles" ? "subtitles" : void 0,
|
|
6378
|
+
language_code: track.language_code,
|
|
6379
|
+
name: track.name,
|
|
6380
|
+
closed_captions: track.type === "captions"
|
|
6381
|
+
}), acc),
|
|
6382
|
+
[]
|
|
6383
|
+
)
|
|
6384
|
+
];
|
|
6385
|
+
if (config.watermark?.imageUrl) {
|
|
6386
|
+
const watermarkForMux = { ...config.watermark, enabled: !0 }, overlaySettings = convertWatermarkToMuxOverlay(watermarkForMux, {
|
|
6387
|
+
videoAspectRatio: options?.videoAspectRatio ?? void 0,
|
|
6388
|
+
units: "px"
|
|
6389
|
+
});
|
|
6390
|
+
overlaySettings && inputs.push({
|
|
6391
|
+
url: config.watermark.imageUrl,
|
|
6392
|
+
overlay_settings: overlaySettings
|
|
6393
|
+
});
|
|
6394
|
+
}
|
|
5675
6395
|
return {
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
text_type: track.type === "subtitles" ? "subtitles" : void 0,
|
|
5686
|
-
language_code: track.language_code,
|
|
5687
|
-
name: track.name,
|
|
5688
|
-
closed_captions: track.type === "captions"
|
|
5689
|
-
}), acc),
|
|
5690
|
-
[]
|
|
5691
|
-
)
|
|
5692
|
-
],
|
|
5693
|
-
static_renditions: config.static_renditions.length > 0 ? config.static_renditions.map((resolution) => ({ resolution })) : void 0,
|
|
5694
|
-
advanced_playback_policies: setAdvancedPlaybackPolicy(config, secrets),
|
|
5695
|
-
max_resolution_tier: config.max_resolution_tier,
|
|
5696
|
-
video_quality: config.video_quality,
|
|
5697
|
-
normalize_audio: config.normalize_audio
|
|
6396
|
+
settings: {
|
|
6397
|
+
input: inputs,
|
|
6398
|
+
static_renditions: config.static_renditions.length > 0 ? config.static_renditions.map((resolution) => ({ resolution })) : void 0,
|
|
6399
|
+
advanced_playback_policies: setAdvancedPlaybackPolicy(config, secrets),
|
|
6400
|
+
max_resolution_tier: config.max_resolution_tier,
|
|
6401
|
+
video_quality: config.video_quality,
|
|
6402
|
+
normalize_audio: config.normalize_audio
|
|
6403
|
+
},
|
|
6404
|
+
watermark: config.watermark?.imageUrl ? { ...config.watermark, enabled: !0 } : void 0
|
|
5698
6405
|
};
|
|
5699
6406
|
}
|
|
6407
|
+
function WatermarkSection({
|
|
6408
|
+
config,
|
|
6409
|
+
dispatch,
|
|
6410
|
+
stagedUpload,
|
|
6411
|
+
videoAssetMetadata,
|
|
6412
|
+
watermarkPreviewContainerRef,
|
|
6413
|
+
watermarkPreviewVideoRef,
|
|
6414
|
+
onValidationChange
|
|
6415
|
+
}) {
|
|
6416
|
+
return videoAssetMetadata?.isAudioOnly !== !1 ? null : /* @__PURE__ */ jsx(
|
|
6417
|
+
FormField$2,
|
|
6418
|
+
{
|
|
6419
|
+
title: "Watermark",
|
|
6420
|
+
description: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6421
|
+
"Add a watermark overlay to your video using Mux's native watermark support.",
|
|
6422
|
+
" ",
|
|
6423
|
+
/* @__PURE__ */ jsx(
|
|
6424
|
+
"a",
|
|
6425
|
+
{
|
|
6426
|
+
href: "https://www.mux.com/docs/guides/add-watermarks-to-your-videos",
|
|
6427
|
+
target: "_blank",
|
|
6428
|
+
rel: "noopener noreferrer",
|
|
6429
|
+
children: "Learn more about Mux watermarks."
|
|
6430
|
+
}
|
|
6431
|
+
)
|
|
6432
|
+
] }),
|
|
6433
|
+
children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
6434
|
+
/* @__PURE__ */ jsx(
|
|
6435
|
+
WatermarkControls,
|
|
6436
|
+
{
|
|
6437
|
+
watermark: config.watermark || { enabled: !1 },
|
|
6438
|
+
onChange: (watermark) => {
|
|
6439
|
+
dispatch({ action: "watermark", value: watermark });
|
|
6440
|
+
},
|
|
6441
|
+
onValidationChange,
|
|
6442
|
+
previewContainerRef: watermarkPreviewContainerRef,
|
|
6443
|
+
previewVideoRef: watermarkPreviewVideoRef
|
|
6444
|
+
}
|
|
6445
|
+
),
|
|
6446
|
+
config.watermark?.imageUrl && stagedUpload.type === "file" && // Canvas preview is only shown in "Canvas" mode (no explicit overlay_settings)
|
|
6447
|
+
!config.watermark.overlay_settings && /* @__PURE__ */ jsx(
|
|
6448
|
+
WatermarkPreview,
|
|
6449
|
+
{
|
|
6450
|
+
stagedUpload,
|
|
6451
|
+
watermark: config.watermark,
|
|
6452
|
+
videoAspectRatio: videoAssetMetadata.aspectRatio,
|
|
6453
|
+
onWatermarkChange: (watermark) => {
|
|
6454
|
+
dispatch({ action: "watermark", value: watermark });
|
|
6455
|
+
},
|
|
6456
|
+
previewContainerRef: watermarkPreviewContainerRef,
|
|
6457
|
+
videoRef: watermarkPreviewVideoRef
|
|
6458
|
+
}
|
|
6459
|
+
)
|
|
6460
|
+
] })
|
|
6461
|
+
}
|
|
6462
|
+
);
|
|
6463
|
+
}
|
|
6464
|
+
const WatermarkPreview = memo(function({
|
|
6465
|
+
stagedUpload,
|
|
6466
|
+
watermark,
|
|
6467
|
+
onWatermarkChange,
|
|
6468
|
+
videoAspectRatio,
|
|
6469
|
+
previewContainerRef,
|
|
6470
|
+
videoRef
|
|
6471
|
+
}) {
|
|
6472
|
+
useEffect(() => {
|
|
6473
|
+
if (videoRef.current && stagedUpload.type === "file") {
|
|
6474
|
+
const file = stagedUpload.files[0], url = URL.createObjectURL(file);
|
|
6475
|
+
return videoRef.current.src = url, () => {
|
|
6476
|
+
URL.revokeObjectURL(url);
|
|
6477
|
+
};
|
|
6478
|
+
}
|
|
6479
|
+
}, [stagedUpload, videoRef]);
|
|
6480
|
+
const isVertical = videoAspectRatio != null && videoAspectRatio < 1;
|
|
6481
|
+
return /* @__PURE__ */ jsx(
|
|
6482
|
+
Card,
|
|
6483
|
+
{
|
|
6484
|
+
tone: "transparent",
|
|
6485
|
+
border: !0,
|
|
6486
|
+
style: {
|
|
6487
|
+
overflow: "hidden",
|
|
6488
|
+
// For vertical videos, center the preview and limit its width
|
|
6489
|
+
display: "flex",
|
|
6490
|
+
justifyContent: "center"
|
|
6491
|
+
},
|
|
6492
|
+
children: /* @__PURE__ */ jsxs(
|
|
6493
|
+
"div",
|
|
6494
|
+
{
|
|
6495
|
+
ref: previewContainerRef,
|
|
6496
|
+
style: {
|
|
6497
|
+
position: "relative",
|
|
6498
|
+
// For vertical videos: limit width so the preview doesn't get too tall
|
|
6499
|
+
// For horizontal videos: use full width
|
|
6500
|
+
width: isVertical ? "auto" : "100%",
|
|
6501
|
+
aspectRatio: videoAspectRatio ? String(videoAspectRatio) : "16/9",
|
|
6502
|
+
...isVertical ? { height: "400px", maxHeight: "50vh" } : { minHeight: "200px" },
|
|
6503
|
+
overflow: "hidden"
|
|
6504
|
+
},
|
|
6505
|
+
children: [
|
|
6506
|
+
/* @__PURE__ */ jsx(
|
|
6507
|
+
"video",
|
|
6508
|
+
{
|
|
6509
|
+
ref: videoRef,
|
|
6510
|
+
style: {
|
|
6511
|
+
position: "absolute",
|
|
6512
|
+
top: 0,
|
|
6513
|
+
left: 0,
|
|
6514
|
+
width: "100%",
|
|
6515
|
+
height: "100%",
|
|
6516
|
+
objectFit: "fill",
|
|
6517
|
+
display: "block"
|
|
6518
|
+
}
|
|
6519
|
+
}
|
|
6520
|
+
),
|
|
6521
|
+
/* @__PURE__ */ jsx(
|
|
6522
|
+
DraggableWatermark,
|
|
6523
|
+
{
|
|
6524
|
+
watermark,
|
|
6525
|
+
onChange: onWatermarkChange,
|
|
6526
|
+
containerRef: previewContainerRef,
|
|
6527
|
+
videoElementRef: videoRef
|
|
6528
|
+
}
|
|
6529
|
+
)
|
|
6530
|
+
]
|
|
6531
|
+
}
|
|
6532
|
+
)
|
|
6533
|
+
}
|
|
6534
|
+
);
|
|
6535
|
+
});
|
|
5700
6536
|
function withFocusRing(component) {
|
|
5701
6537
|
return styled(component)((props) => {
|
|
5702
6538
|
const border = {
|
|
@@ -5935,7 +6771,7 @@ function Uploader(props) {
|
|
|
5935
6771
|
window.removeEventListener("beforeunload", handleBeforeUnload), window.removeEventListener("pagehide", handleBeforeUnload), cleanup();
|
|
5936
6772
|
};
|
|
5937
6773
|
}, [props.client, props.asset?._id]);
|
|
5938
|
-
const startUpload = (settings) => {
|
|
6774
|
+
const startUpload = (settings, watermark) => {
|
|
5939
6775
|
const { stagedUpload } = state;
|
|
5940
6776
|
if (!stagedUpload || uploadRef.current) return;
|
|
5941
6777
|
dispatch({ action: "commitUpload" });
|
|
@@ -5945,14 +6781,16 @@ function Uploader(props) {
|
|
|
5945
6781
|
uploadObservable = uploadUrl({
|
|
5946
6782
|
client: props.client,
|
|
5947
6783
|
url: stagedUpload.url,
|
|
5948
|
-
settings
|
|
6784
|
+
settings,
|
|
6785
|
+
watermark
|
|
5949
6786
|
});
|
|
5950
6787
|
break;
|
|
5951
6788
|
case "file":
|
|
5952
6789
|
uploadObservable = uploadFile({
|
|
5953
6790
|
client: props.client,
|
|
5954
6791
|
file: stagedUpload.files[0],
|
|
5955
|
-
settings
|
|
6792
|
+
settings,
|
|
6793
|
+
watermark
|
|
5956
6794
|
}).pipe(
|
|
5957
6795
|
takeUntil(
|
|
5958
6796
|
cancelUploadButton.observable.pipe(
|