@layers-app/editor-video 0.1.6 → 0.1.8
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.css +25 -3
- package/dist/index.js +913 -548
- package/dist/index.js.map +1 -1
- package/dist/plugin/behaviour.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/ui/VideoBlock.d.ts.map +1 -1
- package/dist/ui/VideoUploadComponent.d.ts.map +1 -1
- package/dist/ui/components/VideoCustomControls.d.ts.map +1 -1
- package/dist/ui/components/VideoSelect.d.ts +8 -0
- package/dist/ui/components/VideoSelect.d.ts.map +1 -0
- package/dist/ui/components/VideoSettingsModal/ChaptersSection.d.ts +3 -1
- package/dist/ui/components/VideoSettingsModal/ChaptersSection.d.ts.map +1 -1
- package/dist/ui/components/VideoSettingsModal/CoverSection.d.ts.map +1 -1
- package/dist/ui/components/VideoSettingsModal/FooterActions.d.ts +2 -1
- package/dist/ui/components/VideoSettingsModal/FooterActions.d.ts.map +1 -1
- package/dist/ui/components/VideoSettingsModal/ManualSubtitlesPanel.d.ts +2 -1
- package/dist/ui/components/VideoSettingsModal/ManualSubtitlesPanel.d.ts.map +1 -1
- package/dist/ui/components/VideoSettingsModal/SubtitlesSection.d.ts.map +1 -1
- package/dist/ui/components/VideoSettingsModal/index.d.ts +2 -1
- package/dist/ui/components/VideoSettingsModal/index.d.ts.map +1 -1
- package/dist/ui/components/VideoSubtitlesMenu/index.d.ts.map +1 -1
- package/dist/ui/hooks/useVideoTranscoding.d.ts.map +1 -1
- package/dist/ui/hooks/useVideoUpload.d.ts.map +1 -1
- package/dist/ui/states/UploadState.d.ts.map +1 -1
- package/dist/ui/states/VideoPlayerState.d.ts +3 -1
- package/dist/ui/states/VideoPlayerState.d.ts.map +1 -1
- package/dist/ui/utils/api.d.ts.map +1 -1
- package/dist/ui/utils/videoSubtitlesApi.d.ts +5 -0
- package/dist/ui/utils/videoSubtitlesApi.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5,11 +5,12 @@ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
|
5
5
|
import { createContext, useState, useRef, useCallback, useMemo, useContext, forwardRef, createElement, useEffect } from "react";
|
|
6
6
|
import { createCommand, $getNodeByKey, $setSelection, COMMAND_PRIORITY_LOW, DecoratorNode } from "lexical";
|
|
7
7
|
import { useTranslation } from "react-i18next";
|
|
8
|
-
import { Tooltip, ActionIcon, Text, Progress, Paper, Button, TextInput, Menu, Box, Slider, Flex,
|
|
8
|
+
import { Tooltip, ActionIcon, Text, Progress, Paper, Button, TextInput, Menu, Box, Slider, Flex, useCombobox, Combobox, InputBase, Group, Stack, Divider, Textarea, Loader, Switch, Modal } from "@mantine/core";
|
|
9
9
|
import { BlockWithAlignableContents } from "@lexical/react/LexicalBlockWithAlignableContents";
|
|
10
10
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
11
11
|
import { Dropzone as Dropzone$1, IMAGE_MIME_TYPE } from "@mantine/dropzone";
|
|
12
12
|
import ShakaPlayer from "shaka-player-react";
|
|
13
|
+
import { IconChevronDown, IconCheck } from "@tabler/icons-react";
|
|
13
14
|
import { Carousel } from "@mantine/carousel";
|
|
14
15
|
const VideoContext = createContext(null);
|
|
15
16
|
function VideoPluginProvider({
|
|
@@ -328,25 +329,34 @@ function setVideoSize(editor, nodeKey, width, height) {
|
|
|
328
329
|
});
|
|
329
330
|
}
|
|
330
331
|
function setVideoPoster(editor, nodeKey, posterUrl) {
|
|
331
|
-
editor.update(
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
332
|
+
editor.update(
|
|
333
|
+
() => {
|
|
334
|
+
const node = $getNodeByKey(nodeKey);
|
|
335
|
+
if (!node || !$isVideoNode(node)) return;
|
|
336
|
+
node.setPosterUrl(posterUrl || "");
|
|
337
|
+
},
|
|
338
|
+
{ tag: "skip-scroll-into-view" }
|
|
339
|
+
);
|
|
336
340
|
}
|
|
337
341
|
function setVideoPrimaryUrl(editor, nodeKey, primaryUrl) {
|
|
338
|
-
editor.update(
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
342
|
+
editor.update(
|
|
343
|
+
() => {
|
|
344
|
+
const node = $getNodeByKey(nodeKey);
|
|
345
|
+
if (!node || !$isVideoNode(node)) return;
|
|
346
|
+
node.setUrl(primaryUrl);
|
|
347
|
+
},
|
|
348
|
+
{ tag: "skip-scroll-into-view" }
|
|
349
|
+
);
|
|
343
350
|
}
|
|
344
351
|
function setVideoChaptersDisabled(editor, nodeKey, disabled2) {
|
|
345
|
-
editor.update(
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
352
|
+
editor.update(
|
|
353
|
+
() => {
|
|
354
|
+
const node = $getNodeByKey(nodeKey);
|
|
355
|
+
if (!node || !$isVideoNode(node)) return;
|
|
356
|
+
node.setChaptersDisabled(disabled2);
|
|
357
|
+
},
|
|
358
|
+
{ tag: "skip-scroll-into-view" }
|
|
359
|
+
);
|
|
350
360
|
}
|
|
351
361
|
function removeVideoNode(editor, nodeKey, deleteNode) {
|
|
352
362
|
deleteNode(editor, nodeKey);
|
|
@@ -854,11 +864,6 @@ function useVideoTranscoding({
|
|
|
854
864
|
const now = Date.now();
|
|
855
865
|
if (!procStallReportedRef.current && lastProcChangeAtRef.current > 0 && now - lastProcChangeAtRef.current > STALL_TIMEOUT_MS$1) {
|
|
856
866
|
procStallReportedRef.current = true;
|
|
857
|
-
console.warn("Processing stall detected", {
|
|
858
|
-
stage: proc.stage,
|
|
859
|
-
percent,
|
|
860
|
-
stalledMs: now - lastProcChangeAtRef.current
|
|
861
|
-
});
|
|
862
867
|
}
|
|
863
868
|
const monotonicPercent = Math.max(maxPercentRef.current, percent);
|
|
864
869
|
maxPercentRef.current = monotonicPercent;
|
|
@@ -955,7 +960,26 @@ function useCancelUpload() {
|
|
|
955
960
|
const DEFAULT_CHUNK_SIZE = 8 * 1024 * 1024;
|
|
956
961
|
const STALL_TIMEOUT_MS = 3e4;
|
|
957
962
|
const MAX_CONSECUTIVE_CONFLICTS = 3;
|
|
963
|
+
const MAX_RETRIES_PER_CHUNK = 3;
|
|
964
|
+
const MAX_TOTAL_RETRIES = 15;
|
|
965
|
+
const RETRY_BASE_DELAY_MS = 3e3;
|
|
966
|
+
function abortableSleep(ms, signal) {
|
|
967
|
+
return new Promise((resolve) => {
|
|
968
|
+
if (signal == null ? void 0 : signal.aborted) {
|
|
969
|
+
resolve();
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
const id = setTimeout(resolve, ms);
|
|
973
|
+
signal == null ? void 0 : signal.addEventListener("abort", () => {
|
|
974
|
+
clearTimeout(id);
|
|
975
|
+
resolve();
|
|
976
|
+
}, { once: true });
|
|
977
|
+
});
|
|
978
|
+
}
|
|
958
979
|
function getBaseUrl() {
|
|
980
|
+
if (typeof window !== "undefined" && window.location.hostname === "dev-app.layers.md") {
|
|
981
|
+
return "https://api.layers.md";
|
|
982
|
+
}
|
|
959
983
|
return "";
|
|
960
984
|
}
|
|
961
985
|
function getAuthHeaders$2() {
|
|
@@ -971,10 +995,13 @@ async function uploadChunkWithProgress(videoId, file, offset, end, total, baseUr
|
|
|
971
995
|
onXhrReady(xhr);
|
|
972
996
|
}
|
|
973
997
|
if (signal) {
|
|
998
|
+
if (signal.aborted) {
|
|
999
|
+
reject(new Error("Upload cancelled"));
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
974
1002
|
signal.addEventListener("abort", () => {
|
|
975
1003
|
xhr.abort();
|
|
976
|
-
|
|
977
|
-
});
|
|
1004
|
+
}, { once: true });
|
|
978
1005
|
}
|
|
979
1006
|
xhr.upload.addEventListener("progress", (e) => {
|
|
980
1007
|
if (e.lengthComputable && onProgress) {
|
|
@@ -987,18 +1014,30 @@ async function uploadChunkWithProgress(videoId, file, offset, end, total, baseUr
|
|
|
987
1014
|
const contentType = xhr.getResponseHeader("content-type") || "";
|
|
988
1015
|
const isJson = contentType.includes("application/json");
|
|
989
1016
|
const data = isJson ? JSON.parse(xhr.responseText) : xhr.responseText;
|
|
990
|
-
if (xhr.status === 409 && data && typeof data === "object" && "expectedOffset" in data) {
|
|
991
|
-
resolve({
|
|
992
|
-
expectedOffset: data.expectedOffset
|
|
993
|
-
});
|
|
994
|
-
return;
|
|
995
|
-
}
|
|
996
1017
|
resolve(
|
|
997
1018
|
typeof data === "object" && data !== null ? data : {}
|
|
998
1019
|
);
|
|
999
1020
|
} catch (error) {
|
|
1000
1021
|
reject(error);
|
|
1001
1022
|
}
|
|
1023
|
+
} else if (xhr.status === 409) {
|
|
1024
|
+
try {
|
|
1025
|
+
const contentType = xhr.getResponseHeader("content-type") || "";
|
|
1026
|
+
const isJson = contentType.includes("application/json");
|
|
1027
|
+
const data = isJson ? JSON.parse(xhr.responseText) : {};
|
|
1028
|
+
if (data && typeof data === "object" && "expectedOffset" in data) {
|
|
1029
|
+
resolve({
|
|
1030
|
+
expectedOffset: data.expectedOffset
|
|
1031
|
+
});
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
} catch {
|
|
1035
|
+
}
|
|
1036
|
+
reject({
|
|
1037
|
+
status: xhr.status,
|
|
1038
|
+
statusText: xhr.statusText,
|
|
1039
|
+
data: { message: "Upload conflict" }
|
|
1040
|
+
});
|
|
1002
1041
|
} else {
|
|
1003
1042
|
let errorData = {};
|
|
1004
1043
|
try {
|
|
@@ -1035,9 +1074,7 @@ async function uploadChunkWithProgress(videoId, file, offset, end, total, baseUr
|
|
|
1035
1074
|
Object.keys(headers).forEach((key) => {
|
|
1036
1075
|
xhr.setRequestHeader(key, headers[key]);
|
|
1037
1076
|
});
|
|
1038
|
-
|
|
1039
|
-
xhr.withCredentials = true;
|
|
1040
|
-
}
|
|
1077
|
+
xhr.withCredentials = true;
|
|
1041
1078
|
xhr.send(blob);
|
|
1042
1079
|
});
|
|
1043
1080
|
}
|
|
@@ -1073,13 +1110,16 @@ function useVideoUpload({
|
|
|
1073
1110
|
isPaused: false,
|
|
1074
1111
|
isPausing: false,
|
|
1075
1112
|
currentXhr: null,
|
|
1076
|
-
|
|
1113
|
+
chunkAbortController: null,
|
|
1114
|
+
uploadSession: 0,
|
|
1115
|
+
cancelled: false
|
|
1077
1116
|
});
|
|
1078
1117
|
const progressIntervalRef = useRef(
|
|
1079
1118
|
null
|
|
1080
1119
|
);
|
|
1081
1120
|
const speedHistoryRef = useRef([]);
|
|
1082
1121
|
const lastDisplayedRemainingRef = useRef(0);
|
|
1122
|
+
const maxDisplayedPercentRef = useRef(0);
|
|
1083
1123
|
const MAX_SPEED_HISTORY = 20;
|
|
1084
1124
|
const EMA_ALPHA = 0.15;
|
|
1085
1125
|
const emaSpeedRef = useRef(null);
|
|
@@ -1096,6 +1136,11 @@ function useVideoUpload({
|
|
|
1096
1136
|
}
|
|
1097
1137
|
const timeDiff = (now - state.lastTime) / 1e3;
|
|
1098
1138
|
const uploadedDiff = uploaded - state.lastBytes;
|
|
1139
|
+
if (uploadedDiff < 0) {
|
|
1140
|
+
state.lastBytes = uploaded;
|
|
1141
|
+
state.lastTime = now;
|
|
1142
|
+
return { speed: emaSpeedRef.current ?? 0, remaining: lastDisplayedRemainingRef.current };
|
|
1143
|
+
}
|
|
1099
1144
|
if (timeDiff > 0 && !state.isPaused) {
|
|
1100
1145
|
const instantSpeed = Math.max(0, uploadedDiff / timeDiff);
|
|
1101
1146
|
speedHistoryRef.current.push(instantSpeed);
|
|
@@ -1134,10 +1179,12 @@ function useVideoUpload({
|
|
|
1134
1179
|
(uploaded, total) => {
|
|
1135
1180
|
const now = Date.now();
|
|
1136
1181
|
const { speed, remaining } = calculateSpeed(uploaded, total, now);
|
|
1137
|
-
const
|
|
1182
|
+
const rawPercent = Math.floor(uploaded * 100 / total);
|
|
1183
|
+
const percent = Math.max(rawPercent, maxDisplayedPercentRef.current);
|
|
1184
|
+
maxDisplayedPercentRef.current = percent;
|
|
1138
1185
|
setUploadProgress({
|
|
1139
1186
|
percent,
|
|
1140
|
-
uploaded,
|
|
1187
|
+
uploaded: Math.max(uploaded, total * percent / 100),
|
|
1141
1188
|
total,
|
|
1142
1189
|
speed,
|
|
1143
1190
|
remaining
|
|
@@ -1147,7 +1194,7 @@ function useVideoUpload({
|
|
|
1147
1194
|
);
|
|
1148
1195
|
const uploadFileChunks = useCallback(
|
|
1149
1196
|
async (file, videoId, startOffset = 0) => {
|
|
1150
|
-
var _a, _b, _c;
|
|
1197
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
1151
1198
|
const state = uploadStateRef.current;
|
|
1152
1199
|
const total = file.size;
|
|
1153
1200
|
let offset = Math.max(0, startOffset);
|
|
@@ -1170,6 +1217,8 @@ function useVideoUpload({
|
|
|
1170
1217
|
}
|
|
1171
1218
|
}, 1e3);
|
|
1172
1219
|
let consecutiveConflicts = 0;
|
|
1220
|
+
let chunkRetries = 0;
|
|
1221
|
+
let totalRetries = 0;
|
|
1173
1222
|
let hasSuccessfulChunk = false;
|
|
1174
1223
|
if (offset > 0) {
|
|
1175
1224
|
updateProgress(offset, total);
|
|
@@ -1177,7 +1226,7 @@ function useVideoUpload({
|
|
|
1177
1226
|
updateProgress(0, total);
|
|
1178
1227
|
}
|
|
1179
1228
|
while (offset < total) {
|
|
1180
|
-
if ((_a = state.signal) == null ? void 0 : _a.aborted) {
|
|
1229
|
+
if (state.cancelled || ((_a = state.signal) == null ? void 0 : _a.aborted)) {
|
|
1181
1230
|
throw new Error("Upload cancelled");
|
|
1182
1231
|
}
|
|
1183
1232
|
if (state.isPaused) {
|
|
@@ -1192,13 +1241,17 @@ function useVideoUpload({
|
|
|
1192
1241
|
const nowStall = Date.now();
|
|
1193
1242
|
if (!state.uploadStallReported && nowStall - state.lastUploadAckAt > STALL_TIMEOUT_MS) {
|
|
1194
1243
|
state.uploadStallReported = true;
|
|
1195
|
-
console.warn("Upload stall detected", {
|
|
1196
|
-
atOffset: offset,
|
|
1197
|
-
stalledMs: nowStall - state.lastUploadAckAt
|
|
1198
|
-
});
|
|
1199
1244
|
}
|
|
1200
1245
|
const end = Math.min(total - 1, offset + chunkSize - 1);
|
|
1201
1246
|
const chunkStartOffset = offset;
|
|
1247
|
+
const chunkAc = new AbortController();
|
|
1248
|
+
state.chunkAbortController = chunkAc;
|
|
1249
|
+
const forwardAbort = () => chunkAc.abort();
|
|
1250
|
+
if (((_b = state.signal) == null ? void 0 : _b.aborted) || state.cancelled) {
|
|
1251
|
+
chunkAc.abort();
|
|
1252
|
+
} else if (state.signal) {
|
|
1253
|
+
state.signal.addEventListener("abort", forwardAbort, { once: true });
|
|
1254
|
+
}
|
|
1202
1255
|
try {
|
|
1203
1256
|
const response = await uploadChunkWithProgress(
|
|
1204
1257
|
videoId,
|
|
@@ -1208,7 +1261,7 @@ function useVideoUpload({
|
|
|
1208
1261
|
total,
|
|
1209
1262
|
baseUrl,
|
|
1210
1263
|
authHeaders,
|
|
1211
|
-
|
|
1264
|
+
chunkAc.signal,
|
|
1212
1265
|
(loaded) => {
|
|
1213
1266
|
if (state.isPaused || state.isPausing) return;
|
|
1214
1267
|
const estimatedOffset = chunkStartOffset + loaded;
|
|
@@ -1220,13 +1273,11 @@ function useVideoUpload({
|
|
|
1220
1273
|
state.currentXhr = xhr;
|
|
1221
1274
|
}
|
|
1222
1275
|
);
|
|
1276
|
+
state.chunkAbortController = null;
|
|
1277
|
+
state.currentXhr = null;
|
|
1278
|
+
(_c = state.signal) == null ? void 0 : _c.removeEventListener("abort", forwardAbort);
|
|
1223
1279
|
if (response.expectedOffset != null) {
|
|
1224
1280
|
consecutiveConflicts++;
|
|
1225
|
-
console.warn("Offset mismatch detected", {
|
|
1226
|
-
expectedOffset: response.expectedOffset,
|
|
1227
|
-
hadOffset: offset,
|
|
1228
|
-
attempt: consecutiveConflicts
|
|
1229
|
-
});
|
|
1230
1281
|
if (consecutiveConflicts >= MAX_CONSECUTIVE_CONFLICTS) {
|
|
1231
1282
|
throw {
|
|
1232
1283
|
status: 409,
|
|
@@ -1238,10 +1289,14 @@ function useVideoUpload({
|
|
|
1238
1289
|
continue;
|
|
1239
1290
|
}
|
|
1240
1291
|
consecutiveConflicts = 0;
|
|
1292
|
+
chunkRetries = 0;
|
|
1241
1293
|
hasSuccessfulChunk = true;
|
|
1242
1294
|
offset = response.nextOffset != null ? response.nextOffset : end + 1;
|
|
1295
|
+
if (offset < total) {
|
|
1296
|
+
await abortableSleep(500, state.signal ?? void 0);
|
|
1297
|
+
}
|
|
1243
1298
|
state.currentOffset = offset;
|
|
1244
|
-
state.
|
|
1299
|
+
state.chunkAbortController = null;
|
|
1245
1300
|
state.lastUploadAckAt = Date.now();
|
|
1246
1301
|
state.uploadStallReported = false;
|
|
1247
1302
|
if (!state.isPaused && !state.isPausing) {
|
|
@@ -1255,26 +1310,54 @@ function useVideoUpload({
|
|
|
1255
1310
|
}
|
|
1256
1311
|
} catch (error) {
|
|
1257
1312
|
state.currentXhr = null;
|
|
1313
|
+
state.chunkAbortController = null;
|
|
1314
|
+
(_d = state.signal) == null ? void 0 : _d.removeEventListener("abort", forwardAbort);
|
|
1258
1315
|
if (state.isPaused || state.isPausing) {
|
|
1259
1316
|
state.isPausing = false;
|
|
1317
|
+
state.isPaused = true;
|
|
1260
1318
|
break;
|
|
1261
1319
|
}
|
|
1262
|
-
if ((error == null ? void 0 : error.name) === "AbortError" || ((
|
|
1320
|
+
if ((error == null ? void 0 : error.name) === "AbortError" || ((_e = state.signal) == null ? void 0 : _e.aborted) || state.cancelled) {
|
|
1263
1321
|
throw new Error("Upload cancelled");
|
|
1264
1322
|
}
|
|
1265
|
-
if ((error == null ? void 0 : error.status) === 409 && ((
|
|
1323
|
+
if ((error == null ? void 0 : error.status) === 409 && ((_f = error == null ? void 0 : error.data) == null ? void 0 : _f.expectedOffset) != null) {
|
|
1266
1324
|
consecutiveConflicts++;
|
|
1267
|
-
console.warn("Offset conflict in error handler", {
|
|
1268
|
-
expectedOffset: error.data.expectedOffset,
|
|
1269
|
-
hadOffset: offset,
|
|
1270
|
-
attempt: consecutiveConflicts
|
|
1271
|
-
});
|
|
1272
1325
|
if (consecutiveConflicts >= MAX_CONSECUTIVE_CONFLICTS) {
|
|
1273
1326
|
throw error;
|
|
1274
1327
|
}
|
|
1275
1328
|
offset = error.data.expectedOffset;
|
|
1276
1329
|
continue;
|
|
1277
1330
|
}
|
|
1331
|
+
const isNetworkError = !(error == null ? void 0 : error.status) || (error == null ? void 0 : error.status) === 0 || (error == null ? void 0 : error.status) === 409;
|
|
1332
|
+
if (isNetworkError && chunkRetries < MAX_RETRIES_PER_CHUNK && totalRetries < MAX_TOTAL_RETRIES) {
|
|
1333
|
+
chunkRetries++;
|
|
1334
|
+
totalRetries++;
|
|
1335
|
+
const delay = RETRY_BASE_DELAY_MS * chunkRetries;
|
|
1336
|
+
await abortableSleep(delay, state.signal ?? void 0);
|
|
1337
|
+
if (state.cancelled || ((_g = state.signal) == null ? void 0 : _g.aborted) || state.isPaused || state.isPausing) {
|
|
1338
|
+
break;
|
|
1339
|
+
}
|
|
1340
|
+
try {
|
|
1341
|
+
const currentStatus = await fetchStatus(videoId, baseUrl, authHeaders);
|
|
1342
|
+
if (((_h = currentStatus.upload) == null ? void 0 : _h.nextOffset) != null) {
|
|
1343
|
+
const serverOffset = currentStatus.upload.nextOffset;
|
|
1344
|
+
if (serverOffset === offset && chunkRetries >= 2) {
|
|
1345
|
+
await abortableSleep(5e3, state.signal ?? void 0);
|
|
1346
|
+
const recheck = await fetchStatus(videoId, baseUrl, authHeaders);
|
|
1347
|
+
if (((_i = recheck.upload) == null ? void 0 : _i.nextOffset) != null && recheck.upload.nextOffset > offset) {
|
|
1348
|
+
offset = recheck.upload.nextOffset;
|
|
1349
|
+
state.currentOffset = offset;
|
|
1350
|
+
chunkRetries = 0;
|
|
1351
|
+
continue;
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
offset = serverOffset;
|
|
1355
|
+
state.currentOffset = serverOffset;
|
|
1356
|
+
}
|
|
1357
|
+
} catch {
|
|
1358
|
+
}
|
|
1359
|
+
continue;
|
|
1360
|
+
}
|
|
1278
1361
|
throw error;
|
|
1279
1362
|
}
|
|
1280
1363
|
}
|
|
@@ -1303,7 +1386,15 @@ function useVideoUpload({
|
|
|
1303
1386
|
);
|
|
1304
1387
|
const handleUpload = useCallback(
|
|
1305
1388
|
async (file) => {
|
|
1306
|
-
var _a, _b;
|
|
1389
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1390
|
+
const state0 = uploadStateRef.current;
|
|
1391
|
+
if (state0.isPaused || state0.isPausing) {
|
|
1392
|
+
state0.isPaused = false;
|
|
1393
|
+
state0.isPausing = false;
|
|
1394
|
+
state0.cancelled = true;
|
|
1395
|
+
state0.videoId = null;
|
|
1396
|
+
state0.attachmentId = null;
|
|
1397
|
+
}
|
|
1307
1398
|
setIsUploading(true);
|
|
1308
1399
|
setIsPaused(false);
|
|
1309
1400
|
const state = uploadStateRef.current;
|
|
@@ -1311,11 +1402,13 @@ function useVideoUpload({
|
|
|
1311
1402
|
const mySession = state.uploadSession;
|
|
1312
1403
|
state.isPaused = false;
|
|
1313
1404
|
state.isPausing = false;
|
|
1405
|
+
state.cancelled = false;
|
|
1314
1406
|
state.file = file;
|
|
1315
1407
|
state.currentOffset = 0;
|
|
1316
1408
|
state.lastTime = 0;
|
|
1317
1409
|
state.lastBytes = 0;
|
|
1318
1410
|
state.uploadStallReported = false;
|
|
1411
|
+
maxDisplayedPercentRef.current = 0;
|
|
1319
1412
|
state.signal = start();
|
|
1320
1413
|
try {
|
|
1321
1414
|
if (!parentId) {
|
|
@@ -1338,19 +1431,19 @@ function useVideoUpload({
|
|
|
1338
1431
|
state.videoId = videoId;
|
|
1339
1432
|
state.attachmentId = attachmentId;
|
|
1340
1433
|
setUploadVideoId(videoId);
|
|
1341
|
-
|
|
1342
|
-
const
|
|
1343
|
-
if (((_a =
|
|
1344
|
-
startOffset =
|
|
1434
|
+
try {
|
|
1435
|
+
const verifiedStatus = await fetchStatus(videoId, baseUrl, authHeaders);
|
|
1436
|
+
if (((_a = verifiedStatus.upload) == null ? void 0 : _a.nextOffset) != null && verifiedStatus.upload.nextOffset > 0) {
|
|
1437
|
+
startOffset = verifiedStatus.upload.nextOffset;
|
|
1438
|
+
} else if (((_c = (_b = initResponse.status) == null ? void 0 : _b.upload) == null ? void 0 : _c.nextOffset) != null) {
|
|
1439
|
+
startOffset = initResponse.status.upload.nextOffset;
|
|
1440
|
+
}
|
|
1441
|
+
} catch {
|
|
1442
|
+
if (((_e = (_d = initResponse.status) == null ? void 0 : _d.upload) == null ? void 0 : _e.nextOffset) != null) {
|
|
1443
|
+
startOffset = initResponse.status.upload.nextOffset;
|
|
1345
1444
|
}
|
|
1346
1445
|
}
|
|
1347
1446
|
} catch (initError) {
|
|
1348
|
-
console.error("Init video error:", {
|
|
1349
|
-
status: initError == null ? void 0 : initError.status,
|
|
1350
|
-
statusText: initError == null ? void 0 : initError.statusText,
|
|
1351
|
-
data: initError == null ? void 0 : initError.data,
|
|
1352
|
-
error: initError
|
|
1353
|
-
});
|
|
1354
1447
|
throw initError;
|
|
1355
1448
|
}
|
|
1356
1449
|
} else {
|
|
@@ -1360,7 +1453,7 @@ function useVideoUpload({
|
|
|
1360
1453
|
baseUrl,
|
|
1361
1454
|
authHeaders
|
|
1362
1455
|
);
|
|
1363
|
-
if (((
|
|
1456
|
+
if (((_f = status.upload) == null ? void 0 : _f.nextOffset) != null) {
|
|
1364
1457
|
startOffset = status.upload.nextOffset;
|
|
1365
1458
|
} else {
|
|
1366
1459
|
videoId = null;
|
|
@@ -1415,20 +1508,14 @@ function useVideoUpload({
|
|
|
1415
1508
|
if (state.isPaused || state.isPausing) {
|
|
1416
1509
|
return;
|
|
1417
1510
|
}
|
|
1418
|
-
console.error("Upload error:", (error == null ? void 0 : error.message) || error);
|
|
1419
1511
|
if ((error == null ? void 0 : error.message) === "Upload cancelled") {
|
|
1420
1512
|
return;
|
|
1421
1513
|
}
|
|
1422
1514
|
if ((error == null ? void 0 : error.status) === 409) {
|
|
1423
1515
|
onError("offset-mismatch", uploadStateRef.current.videoId, error);
|
|
1424
1516
|
} else if (error == null ? void 0 : error.status) {
|
|
1425
|
-
console.error(
|
|
1426
|
-
`API error: ${error.status} ${error.statusText}`,
|
|
1427
|
-
error.data
|
|
1428
|
-
);
|
|
1429
1517
|
onError("interrupted", uploadStateRef.current.videoId, error);
|
|
1430
1518
|
} else {
|
|
1431
|
-
console.error("Unknown upload error:", error);
|
|
1432
1519
|
onError("interrupted", uploadStateRef.current.videoId, error);
|
|
1433
1520
|
}
|
|
1434
1521
|
setIsUploading(false);
|
|
@@ -1453,15 +1540,15 @@ function useVideoUpload({
|
|
|
1453
1540
|
return;
|
|
1454
1541
|
}
|
|
1455
1542
|
state.isPausing = true;
|
|
1456
|
-
state.isPaused = true;
|
|
1457
1543
|
setIsPaused(true);
|
|
1458
1544
|
if (progressIntervalRef.current) {
|
|
1459
1545
|
clearInterval(progressIntervalRef.current);
|
|
1460
1546
|
progressIntervalRef.current = null;
|
|
1461
1547
|
}
|
|
1462
|
-
if (state.
|
|
1463
|
-
state.
|
|
1548
|
+
if (state.chunkAbortController) {
|
|
1549
|
+
state.chunkAbortController.abort();
|
|
1464
1550
|
} else {
|
|
1551
|
+
state.isPaused = true;
|
|
1465
1552
|
state.isPausing = false;
|
|
1466
1553
|
}
|
|
1467
1554
|
}, []);
|
|
@@ -1499,6 +1586,7 @@ function useVideoUpload({
|
|
|
1499
1586
|
setIsPaused(false);
|
|
1500
1587
|
state.isPaused = false;
|
|
1501
1588
|
state.isPausing = false;
|
|
1589
|
+
state.cancelled = false;
|
|
1502
1590
|
await uploadFileChunks(state.file, state.videoId, startOffset);
|
|
1503
1591
|
if (state.uploadSession !== mySession) return;
|
|
1504
1592
|
if (!state.isPaused && !state.isPausing && state.videoId) {
|
|
@@ -1513,7 +1601,7 @@ function useVideoUpload({
|
|
|
1513
1601
|
if ((error == null ? void 0 : error.message) === "Upload cancelled") {
|
|
1514
1602
|
return;
|
|
1515
1603
|
}
|
|
1516
|
-
if (((error == null ? void 0 : error.status) === 0 || (error == null ? void 0 : error.status) === 409) && state.file) {
|
|
1604
|
+
if (((error == null ? void 0 : error.status) === 0 || (error == null ? void 0 : error.status) === 409) && state.file && !state.cancelled) {
|
|
1517
1605
|
state.videoId = null;
|
|
1518
1606
|
state.attachmentId = null;
|
|
1519
1607
|
handleUpload(state.file);
|
|
@@ -1532,10 +1620,14 @@ function useVideoUpload({
|
|
|
1532
1620
|
}, [baseUrl, authHeaders, uploadFileChunks, onError, start, handleUpload]);
|
|
1533
1621
|
const handleCancel = useCallback(() => {
|
|
1534
1622
|
const state = uploadStateRef.current;
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1623
|
+
if (state.chunkAbortController) {
|
|
1624
|
+
try {
|
|
1625
|
+
state.chunkAbortController.abort();
|
|
1626
|
+
} catch {
|
|
1627
|
+
}
|
|
1538
1628
|
}
|
|
1629
|
+
state.cancelled = true;
|
|
1630
|
+
cancelUpload();
|
|
1539
1631
|
if (progressIntervalRef.current) {
|
|
1540
1632
|
clearInterval(progressIntervalRef.current);
|
|
1541
1633
|
progressIntervalRef.current = null;
|
|
@@ -1559,7 +1651,9 @@ function useVideoUpload({
|
|
|
1559
1651
|
state.isPaused = false;
|
|
1560
1652
|
state.isPausing = false;
|
|
1561
1653
|
state.currentXhr = null;
|
|
1654
|
+
state.chunkAbortController = null;
|
|
1562
1655
|
speedHistoryRef.current = [];
|
|
1656
|
+
maxDisplayedPercentRef.current = 0;
|
|
1563
1657
|
setUploadVideoId(null);
|
|
1564
1658
|
}, [cancelUpload]);
|
|
1565
1659
|
return {
|
|
@@ -1669,7 +1763,7 @@ function CheckCircleIcon({
|
|
|
1669
1763
|
"path",
|
|
1670
1764
|
{
|
|
1671
1765
|
d: "M16 1.66699C23.916 1.66699 30.333 8.08391 30.333 16C30.333 23.916 23.916 30.333 16 30.333C8.08391 30.333 1.66699 23.916 1.66699 16C1.66699 8.08392 8.08392 1.66699 16 1.66699Z",
|
|
1672
|
-
fill: "var(--mantine-primary-color-filled, #
|
|
1766
|
+
fill: "var(--mantine-primary-color-filled, #4c6ef5)"
|
|
1673
1767
|
}
|
|
1674
1768
|
),
|
|
1675
1769
|
/* @__PURE__ */ jsx(
|
|
@@ -1816,7 +1910,7 @@ function ProgressBar({
|
|
|
1816
1910
|
"%"
|
|
1817
1911
|
] }),
|
|
1818
1912
|
isPaused ? /* @__PURE__ */ jsx(Text, { size: "sm", c: "var(--mantine-color-text)", children: t("editor.video.actions.pause") }) : showRemaining && progress.remaining > 0 && /* @__PURE__ */ jsxs(Text, { size: "sm", c: "var(--mantine-color-dimmed)", children: [
|
|
1819
|
-
"
|
|
1913
|
+
t("editor.video.upload.remaining"),
|
|
1820
1914
|
/* @__PURE__ */ jsxs(Text, { component: "span", c: "var(--mantine-color-text)", children: [
|
|
1821
1915
|
" ",
|
|
1822
1916
|
formatTime2(progress.remaining)
|
|
@@ -1826,7 +1920,7 @@ function ProgressBar({
|
|
|
1826
1920
|
/* @__PURE__ */ jsx(Progress, { value: clampedPercent, size: 6, radius: "md" }),
|
|
1827
1921
|
showInfo && /* @__PURE__ */ jsxs("div", { className: "video-upload-progress-info", children: [
|
|
1828
1922
|
/* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
|
|
1829
|
-
"
|
|
1923
|
+
t("editor.video.upload.progress"),
|
|
1830
1924
|
/* @__PURE__ */ jsxs(Text, { component: "span", size: "xs", c: "var(--mantine-color-text)", inherit: true, children: [
|
|
1831
1925
|
" ",
|
|
1832
1926
|
formatBytes(progress.uploaded),
|
|
@@ -2413,6 +2507,7 @@ function UploadState({
|
|
|
2413
2507
|
onCancel,
|
|
2414
2508
|
isPaused = false
|
|
2415
2509
|
}) {
|
|
2510
|
+
const { t } = useTranslation();
|
|
2416
2511
|
return /* @__PURE__ */ jsxs(
|
|
2417
2512
|
Paper,
|
|
2418
2513
|
{
|
|
@@ -2481,7 +2576,7 @@ function UploadState({
|
|
|
2481
2576
|
]
|
|
2482
2577
|
}
|
|
2483
2578
|
),
|
|
2484
|
-
/* @__PURE__ */ jsx(Text, { size: "md", fw: 500, c: "var(--mantine-color-bright)", children: "
|
|
2579
|
+
/* @__PURE__ */ jsx(Text, { size: "md", fw: 500, c: "var(--mantine-color-bright)", children: t("editor.video.upload.uploading") })
|
|
2485
2580
|
]
|
|
2486
2581
|
}
|
|
2487
2582
|
),
|
|
@@ -2953,6 +3048,138 @@ function VideoSpeedMenu({
|
|
|
2953
3048
|
}
|
|
2954
3049
|
);
|
|
2955
3050
|
}
|
|
3051
|
+
function buildApiUrl$1(path, baseUrl) {
|
|
3052
|
+
if (path.startsWith("http://") || path.startsWith("https://")) {
|
|
3053
|
+
return path;
|
|
3054
|
+
}
|
|
3055
|
+
const base = baseUrl || (typeof window !== "undefined" ? window.location.origin : "");
|
|
3056
|
+
return `${base}${path}`;
|
|
3057
|
+
}
|
|
3058
|
+
function getAuthHeaders$1(authToken) {
|
|
3059
|
+
if (!authToken) return {};
|
|
3060
|
+
return { Authorization: `Bearer ${authToken}` };
|
|
3061
|
+
}
|
|
3062
|
+
async function parseJson$1(response) {
|
|
3063
|
+
const text = await response.text();
|
|
3064
|
+
if (!text) return null;
|
|
3065
|
+
try {
|
|
3066
|
+
return JSON.parse(text);
|
|
3067
|
+
} catch {
|
|
3068
|
+
return text;
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
async function handleResponse$1(response) {
|
|
3072
|
+
if (!response.ok) {
|
|
3073
|
+
const data = await parseJson$1(response);
|
|
3074
|
+
const message = (data && typeof data === "object" && "message" in data ? data.message : null) || response.statusText || "Request failed";
|
|
3075
|
+
throw new Error(message);
|
|
3076
|
+
}
|
|
3077
|
+
return parseJson$1(response);
|
|
3078
|
+
}
|
|
3079
|
+
function normalizeSubtitleTrack(raw) {
|
|
3080
|
+
const id = (raw == null ? void 0 : raw.id) ?? (raw == null ? void 0 : raw.trackId) ?? (raw == null ? void 0 : raw.subtitleId) ?? "";
|
|
3081
|
+
const langRaw = (raw == null ? void 0 : raw.lang) ?? (raw == null ? void 0 : raw.language) ?? (raw == null ? void 0 : raw.srclang) ?? "";
|
|
3082
|
+
const lang = langRaw ? String(langRaw) : "und";
|
|
3083
|
+
const labelRaw = (raw == null ? void 0 : raw.label) ?? (raw == null ? void 0 : raw.name) ?? (raw == null ? void 0 : raw.title) ?? lang;
|
|
3084
|
+
const label = labelRaw ? String(labelRaw) : lang;
|
|
3085
|
+
const kind = (raw == null ? void 0 : raw.kind) ?? "subtitles";
|
|
3086
|
+
const isDefault = Boolean((raw == null ? void 0 : raw.isDefault) ?? (raw == null ? void 0 : raw.default) ?? false);
|
|
3087
|
+
const isDisabled = Boolean((raw == null ? void 0 : raw.isDisabled) ?? false);
|
|
3088
|
+
const url = typeof (raw == null ? void 0 : raw.url) === "string" ? raw.url : typeof (raw == null ? void 0 : raw.vttUrl) === "string" ? raw.vttUrl : typeof (raw == null ? void 0 : raw.src) === "string" ? raw.src : void 0;
|
|
3089
|
+
const createdAt = typeof (raw == null ? void 0 : raw.createdAt) === "string" ? raw.createdAt : void 0;
|
|
3090
|
+
const fileName = typeof (raw == null ? void 0 : raw.fileName) === "string" ? raw.fileName : void 0;
|
|
3091
|
+
const auto = Boolean((raw == null ? void 0 : raw.auto) ?? (raw == null ? void 0 : raw.isAuto) ?? (raw == null ? void 0 : raw.source) === "auto");
|
|
3092
|
+
return {
|
|
3093
|
+
id: String(id),
|
|
3094
|
+
lang,
|
|
3095
|
+
label,
|
|
3096
|
+
kind: String(kind),
|
|
3097
|
+
isDefault,
|
|
3098
|
+
isDisabled,
|
|
3099
|
+
url,
|
|
3100
|
+
createdAt,
|
|
3101
|
+
fileName,
|
|
3102
|
+
status: "ready",
|
|
3103
|
+
auto
|
|
3104
|
+
};
|
|
3105
|
+
}
|
|
3106
|
+
function isAutoGeneratedTrack(track) {
|
|
3107
|
+
return track.label.length <= 3;
|
|
3108
|
+
}
|
|
3109
|
+
async function listTracks(videoId, options = {}) {
|
|
3110
|
+
const url = buildApiUrl$1(`/v1/videos/${videoId}/subtitles`, options.baseUrl);
|
|
3111
|
+
const response = await fetch(url, {
|
|
3112
|
+
method: "GET",
|
|
3113
|
+
credentials: "include",
|
|
3114
|
+
headers: {
|
|
3115
|
+
...getAuthHeaders$1(options.authToken)
|
|
3116
|
+
}
|
|
3117
|
+
});
|
|
3118
|
+
const data = await handleResponse$1(response);
|
|
3119
|
+
const rawTracks = Array.isArray(data) ? data : Array.isArray(data == null ? void 0 : data.tracks) ? data.tracks : [];
|
|
3120
|
+
return rawTracks.map(normalizeSubtitleTrack).filter((track) => track.id);
|
|
3121
|
+
}
|
|
3122
|
+
async function uploadTrack(videoId, file, uploadOptions, options = {}) {
|
|
3123
|
+
const params = new URLSearchParams();
|
|
3124
|
+
params.set("lang", uploadOptions.lang);
|
|
3125
|
+
if (uploadOptions.label) {
|
|
3126
|
+
params.set("label", uploadOptions.label);
|
|
3127
|
+
}
|
|
3128
|
+
if (uploadOptions.kind) {
|
|
3129
|
+
params.set("kind", uploadOptions.kind);
|
|
3130
|
+
}
|
|
3131
|
+
{
|
|
3132
|
+
params.set("default", "false");
|
|
3133
|
+
}
|
|
3134
|
+
const url = buildApiUrl$1(
|
|
3135
|
+
`/v1/videos/${videoId}/subtitles?${params.toString()}`,
|
|
3136
|
+
options.baseUrl
|
|
3137
|
+
);
|
|
3138
|
+
const formData = new FormData();
|
|
3139
|
+
formData.append("file", file);
|
|
3140
|
+
const response = await fetch(url, {
|
|
3141
|
+
method: "POST",
|
|
3142
|
+
credentials: "include",
|
|
3143
|
+
headers: {
|
|
3144
|
+
...getAuthHeaders$1(options.authToken)
|
|
3145
|
+
},
|
|
3146
|
+
body: formData,
|
|
3147
|
+
signal: uploadOptions.signal
|
|
3148
|
+
});
|
|
3149
|
+
const data = await handleResponse$1(response);
|
|
3150
|
+
if (!data) return null;
|
|
3151
|
+
return normalizeSubtitleTrack(data);
|
|
3152
|
+
}
|
|
3153
|
+
async function patchTrackDisabled(videoId, trackId, isDisabled, options = {}) {
|
|
3154
|
+
const url = buildApiUrl$1(
|
|
3155
|
+
`/v1/videos/${videoId}/subtitles/${trackId}/disabled`,
|
|
3156
|
+
options.baseUrl
|
|
3157
|
+
);
|
|
3158
|
+
const response = await fetch(url, {
|
|
3159
|
+
method: "PATCH",
|
|
3160
|
+
credentials: "include",
|
|
3161
|
+
headers: {
|
|
3162
|
+
"Content-Type": "application/json",
|
|
3163
|
+
...getAuthHeaders$1(options.authToken)
|
|
3164
|
+
},
|
|
3165
|
+
body: JSON.stringify({ isDisabled })
|
|
3166
|
+
});
|
|
3167
|
+
await handleResponse$1(response);
|
|
3168
|
+
}
|
|
3169
|
+
async function deleteTrack(videoId, trackId, options = {}) {
|
|
3170
|
+
const url = buildApiUrl$1(
|
|
3171
|
+
`/v1/videos/${videoId}/subtitles/${trackId}`,
|
|
3172
|
+
options.baseUrl
|
|
3173
|
+
);
|
|
3174
|
+
const response = await fetch(url, {
|
|
3175
|
+
method: "DELETE",
|
|
3176
|
+
credentials: "include",
|
|
3177
|
+
headers: {
|
|
3178
|
+
...getAuthHeaders$1(options.authToken)
|
|
3179
|
+
}
|
|
3180
|
+
});
|
|
3181
|
+
await handleResponse$1(response);
|
|
3182
|
+
}
|
|
2956
3183
|
const LANG_NAMES = {
|
|
2957
3184
|
en: "English",
|
|
2958
3185
|
es: "Español",
|
|
@@ -2988,7 +3215,7 @@ function VideoSubtitlesMenu({
|
|
|
2988
3215
|
const items = settings.subtitles.map((item) => {
|
|
2989
3216
|
const isActive = item.id === settings.currentSubtitleId;
|
|
2990
3217
|
let label = item.label;
|
|
2991
|
-
if (
|
|
3218
|
+
if (isAutoGeneratedTrack({ label })) {
|
|
2992
3219
|
const fullName = item.lang ? LANG_NAMES[item.lang.toLowerCase()] : null;
|
|
2993
3220
|
if (fullName) label = fullName;
|
|
2994
3221
|
label = `${label} (${autoLabel})`;
|
|
@@ -3176,8 +3403,8 @@ function VideoCustomControls({
|
|
|
3176
3403
|
const visibleBufferedRanges = isProcessing ? [] : bufferedRanges;
|
|
3177
3404
|
const sortedChapters = useMemo(() => {
|
|
3178
3405
|
if (!chapters || chapters.length === 0) return [];
|
|
3179
|
-
return [...chapters].sort((a, b) => a.startSec - b.startSec);
|
|
3180
|
-
}, [chapters]);
|
|
3406
|
+
return [...chapters].filter((ch) => duration > 0 && ch.startSec <= duration).sort((a, b) => a.startSec - b.startSec);
|
|
3407
|
+
}, [chapters, duration]);
|
|
3181
3408
|
const chapterSegments = useMemo(() => {
|
|
3182
3409
|
if (sortedChapters.length === 0 || duration <= 0) return [];
|
|
3183
3410
|
const segments = [];
|
|
@@ -3538,10 +3765,6 @@ function VideoCustomControls({
|
|
|
3538
3765
|
className: "video-player-button",
|
|
3539
3766
|
type: "button",
|
|
3540
3767
|
"aria-label": t("editor.video.player.settings"),
|
|
3541
|
-
onClick: () => {
|
|
3542
|
-
setIsSettingsOpen((prev) => !prev);
|
|
3543
|
-
setActiveMenu("main");
|
|
3544
|
-
},
|
|
3545
3768
|
children: /* @__PURE__ */ jsx(SettingsPlayerIcon, { size: 20 })
|
|
3546
3769
|
}
|
|
3547
3770
|
) }),
|
|
@@ -3579,6 +3802,7 @@ function VideoCustomControls({
|
|
|
3579
3802
|
onSelect: (speed) => {
|
|
3580
3803
|
setOptimisticSpeed(speed);
|
|
3581
3804
|
onSpeedClick(speed);
|
|
3805
|
+
setIsSettingsOpen(false);
|
|
3582
3806
|
}
|
|
3583
3807
|
}
|
|
3584
3808
|
),
|
|
@@ -3588,7 +3812,10 @@ function VideoCustomControls({
|
|
|
3588
3812
|
disabled: isSettingsDisabled,
|
|
3589
3813
|
settings,
|
|
3590
3814
|
onBack: () => setActiveMenu("main"),
|
|
3591
|
-
onSelect:
|
|
3815
|
+
onSelect: (subtitleId) => {
|
|
3816
|
+
onSubtitleSelect(subtitleId);
|
|
3817
|
+
setIsSettingsOpen(false);
|
|
3818
|
+
}
|
|
3592
3819
|
}
|
|
3593
3820
|
),
|
|
3594
3821
|
activeMenu === "quality" && /* @__PURE__ */ jsx(
|
|
@@ -3597,7 +3824,10 @@ function VideoCustomControls({
|
|
|
3597
3824
|
disabled: isSettingsDisabled,
|
|
3598
3825
|
settings,
|
|
3599
3826
|
onBack: () => setActiveMenu("main"),
|
|
3600
|
-
onSelect:
|
|
3827
|
+
onSelect: (qualityId) => {
|
|
3828
|
+
onQualityClick(qualityId);
|
|
3829
|
+
setIsSettingsOpen(false);
|
|
3830
|
+
}
|
|
3601
3831
|
}
|
|
3602
3832
|
)
|
|
3603
3833
|
]
|
|
@@ -3772,141 +4002,21 @@ const setSubtitle = (player, id, videoElement) => {
|
|
|
3772
4002
|
track.mode = index === id ? "showing" : "disabled";
|
|
3773
4003
|
});
|
|
3774
4004
|
};
|
|
3775
|
-
function
|
|
3776
|
-
if (
|
|
3777
|
-
|
|
4005
|
+
function normalizeUrl(url, baseUrl) {
|
|
4006
|
+
if (!url) return null;
|
|
4007
|
+
if (url.startsWith("http://") || url.startsWith("https://")) {
|
|
4008
|
+
return url;
|
|
3778
4009
|
}
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
}
|
|
3782
|
-
function getAuthHeaders$1(authToken) {
|
|
3783
|
-
if (!authToken) return {};
|
|
3784
|
-
return { Authorization: `Bearer ${authToken}` };
|
|
3785
|
-
}
|
|
3786
|
-
async function parseJson$1(response) {
|
|
3787
|
-
const text = await response.text();
|
|
3788
|
-
if (!text) return null;
|
|
3789
|
-
try {
|
|
3790
|
-
return JSON.parse(text);
|
|
3791
|
-
} catch {
|
|
3792
|
-
return text;
|
|
4010
|
+
if (url.startsWith("/")) {
|
|
4011
|
+
const base = baseUrl || window.location.origin;
|
|
4012
|
+
return `${base}${url}`;
|
|
3793
4013
|
}
|
|
4014
|
+
return url;
|
|
3794
4015
|
}
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
throw new Error(message);
|
|
3800
|
-
}
|
|
3801
|
-
return parseJson$1(response);
|
|
3802
|
-
}
|
|
3803
|
-
function normalizeSubtitleTrack(raw) {
|
|
3804
|
-
const id = (raw == null ? void 0 : raw.id) ?? (raw == null ? void 0 : raw.trackId) ?? (raw == null ? void 0 : raw.subtitleId) ?? "";
|
|
3805
|
-
const langRaw = (raw == null ? void 0 : raw.lang) ?? (raw == null ? void 0 : raw.language) ?? (raw == null ? void 0 : raw.srclang) ?? "";
|
|
3806
|
-
const lang = langRaw ? String(langRaw) : "und";
|
|
3807
|
-
const labelRaw = (raw == null ? void 0 : raw.label) ?? (raw == null ? void 0 : raw.name) ?? (raw == null ? void 0 : raw.title) ?? lang;
|
|
3808
|
-
const label = labelRaw ? String(labelRaw) : lang;
|
|
3809
|
-
const kind = (raw == null ? void 0 : raw.kind) ?? "subtitles";
|
|
3810
|
-
const isDefault = Boolean((raw == null ? void 0 : raw.isDefault) ?? (raw == null ? void 0 : raw.default) ?? false);
|
|
3811
|
-
const url = typeof (raw == null ? void 0 : raw.url) === "string" ? raw.url : typeof (raw == null ? void 0 : raw.vttUrl) === "string" ? raw.vttUrl : typeof (raw == null ? void 0 : raw.src) === "string" ? raw.src : void 0;
|
|
3812
|
-
const createdAt = typeof (raw == null ? void 0 : raw.createdAt) === "string" ? raw.createdAt : void 0;
|
|
3813
|
-
const fileName = typeof (raw == null ? void 0 : raw.fileName) === "string" ? raw.fileName : void 0;
|
|
3814
|
-
const auto = Boolean((raw == null ? void 0 : raw.auto) ?? (raw == null ? void 0 : raw.isAuto) ?? (raw == null ? void 0 : raw.source) === "auto");
|
|
3815
|
-
return {
|
|
3816
|
-
id: String(id),
|
|
3817
|
-
lang,
|
|
3818
|
-
label,
|
|
3819
|
-
kind: String(kind),
|
|
3820
|
-
isDefault,
|
|
3821
|
-
url,
|
|
3822
|
-
createdAt,
|
|
3823
|
-
fileName,
|
|
3824
|
-
status: "ready",
|
|
3825
|
-
auto
|
|
3826
|
-
};
|
|
3827
|
-
}
|
|
3828
|
-
async function uploadTrack(videoId, file, uploadOptions, options = {}) {
|
|
3829
|
-
const params = new URLSearchParams();
|
|
3830
|
-
params.set("lang", uploadOptions.lang);
|
|
3831
|
-
if (uploadOptions.label) {
|
|
3832
|
-
params.set("label", uploadOptions.label);
|
|
3833
|
-
}
|
|
3834
|
-
if (uploadOptions.kind) {
|
|
3835
|
-
params.set("kind", uploadOptions.kind);
|
|
3836
|
-
}
|
|
3837
|
-
{
|
|
3838
|
-
params.set("default", "false");
|
|
3839
|
-
}
|
|
3840
|
-
const url = buildApiUrl$1(
|
|
3841
|
-
`/v1/videos/${videoId}/subtitles?${params.toString()}`,
|
|
3842
|
-
options.baseUrl
|
|
3843
|
-
);
|
|
3844
|
-
const formData = new FormData();
|
|
3845
|
-
formData.append("file", file);
|
|
3846
|
-
const response = await fetch(url, {
|
|
3847
|
-
method: "POST",
|
|
3848
|
-
credentials: "include",
|
|
3849
|
-
headers: {
|
|
3850
|
-
...getAuthHeaders$1(options.authToken)
|
|
3851
|
-
},
|
|
3852
|
-
body: formData,
|
|
3853
|
-
signal: uploadOptions.signal
|
|
3854
|
-
});
|
|
3855
|
-
const data = await handleResponse$1(response);
|
|
3856
|
-
if (!data) return null;
|
|
3857
|
-
return normalizeSubtitleTrack(data);
|
|
3858
|
-
}
|
|
3859
|
-
function getVttUrl(videoId, trackId, options = {}) {
|
|
3860
|
-
return buildApiUrl$1(
|
|
3861
|
-
`/v1/videos/${videoId}/subtitles/${trackId}.vtt`,
|
|
3862
|
-
options.baseUrl
|
|
3863
|
-
);
|
|
3864
|
-
}
|
|
3865
|
-
function normalizeUrl$1(path, baseUrl) {
|
|
3866
|
-
if (path.startsWith("http://") || path.startsWith("https://")) {
|
|
3867
|
-
return path;
|
|
3868
|
-
}
|
|
3869
|
-
const base = baseUrl || (typeof window !== "undefined" ? window.location.origin : "");
|
|
3870
|
-
if (path.startsWith("/")) {
|
|
3871
|
-
return `${base}${path}`;
|
|
3872
|
-
}
|
|
3873
|
-
return `${base}/${path}`;
|
|
3874
|
-
}
|
|
3875
|
-
function resolveSubtitleUrl(videoId, track, options = {}) {
|
|
3876
|
-
if (track.url) {
|
|
3877
|
-
return normalizeUrl$1(track.url, options.baseUrl);
|
|
3878
|
-
}
|
|
3879
|
-
return getVttUrl(videoId, track.id, options);
|
|
3880
|
-
}
|
|
3881
|
-
async function deleteTrack(videoId, trackId, options = {}) {
|
|
3882
|
-
const url = buildApiUrl$1(
|
|
3883
|
-
`/v1/videos/${videoId}/subtitles/${trackId}`,
|
|
3884
|
-
options.baseUrl
|
|
3885
|
-
);
|
|
3886
|
-
const response = await fetch(url, {
|
|
3887
|
-
method: "DELETE",
|
|
3888
|
-
credentials: "include",
|
|
3889
|
-
headers: {
|
|
3890
|
-
...getAuthHeaders$1(options.authToken)
|
|
3891
|
-
}
|
|
3892
|
-
});
|
|
3893
|
-
await handleResponse$1(response);
|
|
3894
|
-
}
|
|
3895
|
-
function normalizeUrl(url, baseUrl) {
|
|
3896
|
-
if (!url) return null;
|
|
3897
|
-
if (url.startsWith("http://") || url.startsWith("https://")) {
|
|
3898
|
-
return url;
|
|
3899
|
-
}
|
|
3900
|
-
if (url.startsWith("/")) {
|
|
3901
|
-
const base = baseUrl || window.location.origin;
|
|
3902
|
-
return `${base}${url}`;
|
|
3903
|
-
}
|
|
3904
|
-
return url;
|
|
3905
|
-
}
|
|
3906
|
-
function getNativeSubtitleTracks(videoElement) {
|
|
3907
|
-
return Array.from(videoElement.textTracks || []).filter(
|
|
3908
|
-
(track) => track.kind === "subtitles" || track.kind === "captions"
|
|
3909
|
-
);
|
|
4016
|
+
function getNativeSubtitleTracks(videoElement) {
|
|
4017
|
+
return Array.from(videoElement.textTracks || []).filter(
|
|
4018
|
+
(track) => track.kind === "subtitles" || track.kind === "captions"
|
|
4019
|
+
);
|
|
3910
4020
|
}
|
|
3911
4021
|
function getSubtitleUrl(track, baseUrl) {
|
|
3912
4022
|
if (!track.url) return null;
|
|
@@ -3926,7 +4036,8 @@ function VideoPlayerState({
|
|
|
3926
4036
|
subtitles,
|
|
3927
4037
|
isProcessing = false,
|
|
3928
4038
|
posterUrl,
|
|
3929
|
-
onPlayerInfo
|
|
4039
|
+
onPlayerInfo,
|
|
4040
|
+
onFallback
|
|
3930
4041
|
}) {
|
|
3931
4042
|
const { t, i18n } = useTranslation();
|
|
3932
4043
|
const [currentVideoUrl, setCurrentVideoUrl] = useState(null);
|
|
@@ -3953,6 +4064,9 @@ function VideoPlayerState({
|
|
|
3953
4064
|
);
|
|
3954
4065
|
const shakaAddedIdsRef = useRef([]);
|
|
3955
4066
|
const shakaFilterRegisteredRef = useRef(false);
|
|
4067
|
+
const triedFallbackUrlsRef = useRef(/* @__PURE__ */ new Set());
|
|
4068
|
+
const hlsUrlCheckedRef = useRef(null);
|
|
4069
|
+
const savedStateLockRef = useRef(false);
|
|
3956
4070
|
useEffect(() => {
|
|
3957
4071
|
if (!language) {
|
|
3958
4072
|
return;
|
|
@@ -3966,16 +4080,17 @@ function VideoPlayerState({
|
|
|
3966
4080
|
return;
|
|
3967
4081
|
}
|
|
3968
4082
|
if (subtitles != null) {
|
|
3969
|
-
const normalized = (subtitles || []).map(normalizeSubtitleTrack).filter((track) => track.id);
|
|
4083
|
+
const normalized = (subtitles || []).map(normalizeSubtitleTrack).filter((track) => track.id && !track.isDisabled);
|
|
3970
4084
|
setSubtitleTracks(normalized);
|
|
3971
4085
|
}
|
|
3972
4086
|
}, [videoId, subtitles]);
|
|
3973
4087
|
useEffect(() => {
|
|
3974
4088
|
setIsAutoQualityEnabled(true);
|
|
4089
|
+
hlsUrlCheckedRef.current = null;
|
|
3975
4090
|
}, [videoId]);
|
|
3976
4091
|
const handleSubtitlesOpen = useCallback(() => {
|
|
3977
4092
|
if (!videoId || subtitles == null) return;
|
|
3978
|
-
const normalized = (subtitles || []).map(normalizeSubtitleTrack).filter((track) => track.id);
|
|
4093
|
+
const normalized = (subtitles || []).map(normalizeSubtitleTrack).filter((track) => track.id && !track.isDisabled);
|
|
3979
4094
|
setSubtitleTracks(normalized);
|
|
3980
4095
|
}, [videoId, subtitles]);
|
|
3981
4096
|
useEffect(() => {
|
|
@@ -4082,102 +4197,148 @@ function VideoPlayerState({
|
|
|
4082
4197
|
selectedSubtitleMeta
|
|
4083
4198
|
]);
|
|
4084
4199
|
useEffect(() => {
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4200
|
+
if (!currentVideoUrl) return;
|
|
4201
|
+
let cancelled = false;
|
|
4202
|
+
let player = null;
|
|
4203
|
+
const handleFatalError = (error) => {
|
|
4204
|
+
if (cancelled) return;
|
|
4205
|
+
const fallback = normalizeUrl(nativeVideoUrl, baseUrl) || normalizeUrl(rawUrl ?? null, baseUrl);
|
|
4206
|
+
if (fallback) {
|
|
4207
|
+
setHlsFailed(true);
|
|
4208
|
+
onFallback == null ? void 0 : onFallback(true);
|
|
4209
|
+
} else {
|
|
4210
|
+
setPlayerError(
|
|
4211
|
+
t("editor.video.player.playbackError", { code: error.code })
|
|
4212
|
+
);
|
|
4095
4213
|
}
|
|
4096
|
-
}
|
|
4214
|
+
};
|
|
4097
4215
|
const errorHandler = (event) => {
|
|
4098
4216
|
const error = event.detail;
|
|
4099
|
-
console.error("Shaka Player error:", {
|
|
4100
|
-
code: error.code,
|
|
4101
|
-
category: error.category,
|
|
4102
|
-
severity: error.severity,
|
|
4103
|
-
data: error.data
|
|
4104
|
-
});
|
|
4105
4217
|
if (error.severity === 2) {
|
|
4106
|
-
|
|
4107
|
-
if (fallback) {
|
|
4108
|
-
console.warn(
|
|
4109
|
-
`[VideoPlayer] HLS failed (code=${error.code}), falling back to native video:`,
|
|
4110
|
-
fallback
|
|
4111
|
-
);
|
|
4112
|
-
setHlsFailed(true);
|
|
4113
|
-
} else {
|
|
4114
|
-
setPlayerError(
|
|
4115
|
-
t("editor.video.player.playbackError", { code: error.code })
|
|
4116
|
-
);
|
|
4117
|
-
}
|
|
4218
|
+
handleFatalError(error);
|
|
4118
4219
|
} else {
|
|
4119
4220
|
try {
|
|
4120
|
-
player.recover();
|
|
4121
|
-
} catch
|
|
4122
|
-
console.error("Failed to recover from error:", recoverError);
|
|
4221
|
+
player == null ? void 0 : player.recover();
|
|
4222
|
+
} catch {
|
|
4123
4223
|
}
|
|
4124
4224
|
}
|
|
4125
4225
|
};
|
|
4126
|
-
|
|
4127
|
-
|
|
4226
|
+
const tryLoad = () => {
|
|
4227
|
+
var _a;
|
|
4228
|
+
player = (_a = playerRef.current) == null ? void 0 : _a.player;
|
|
4229
|
+
if (!player) {
|
|
4230
|
+
const raf2 = requestAnimationFrame(tryLoad);
|
|
4231
|
+
return () => cancelAnimationFrame(raf2);
|
|
4232
|
+
}
|
|
4233
|
+
if (errorHandlerRef.current) {
|
|
4234
|
+
try {
|
|
4235
|
+
player.removeEventListener("error", errorHandlerRef.current);
|
|
4236
|
+
} catch {
|
|
4237
|
+
}
|
|
4238
|
+
}
|
|
4239
|
+
errorHandlerRef.current = errorHandler;
|
|
4240
|
+
player.addEventListener("error", errorHandler);
|
|
4241
|
+
player.load(currentVideoUrl).catch((loadError) => {
|
|
4242
|
+
if (cancelled) return;
|
|
4243
|
+
if (loadError.code === 7e3) return;
|
|
4244
|
+
handleFatalError(loadError);
|
|
4245
|
+
});
|
|
4246
|
+
};
|
|
4247
|
+
const raf = requestAnimationFrame(tryLoad);
|
|
4128
4248
|
return () => {
|
|
4249
|
+
cancelled = true;
|
|
4250
|
+
cancelAnimationFrame(raf);
|
|
4129
4251
|
if (player && errorHandlerRef.current) {
|
|
4130
4252
|
try {
|
|
4131
4253
|
player.removeEventListener("error", errorHandlerRef.current);
|
|
4132
|
-
} catch
|
|
4133
|
-
console.warn("Error removing event listener on cleanup:", e);
|
|
4254
|
+
} catch {
|
|
4134
4255
|
}
|
|
4135
4256
|
}
|
|
4136
4257
|
};
|
|
4137
|
-
}, [currentVideoUrl, nativeVideoUrl, rawUrl, baseUrl]);
|
|
4258
|
+
}, [currentVideoUrl, nativeVideoUrl, rawUrl, baseUrl, onFallback]);
|
|
4138
4259
|
useEffect(() => {
|
|
4139
|
-
|
|
4260
|
+
let cancelled = false;
|
|
4261
|
+
let retryTimer = null;
|
|
4140
4262
|
if (optimizedReady && primaryUrl) {
|
|
4141
4263
|
const normalizedUrl = normalizeUrl(primaryUrl, baseUrl);
|
|
4142
4264
|
if (normalizedUrl) {
|
|
4143
|
-
if (
|
|
4144
|
-
const
|
|
4145
|
-
if (
|
|
4146
|
-
|
|
4147
|
-
time: videoElement.currentTime || 0,
|
|
4148
|
-
paused: videoElement.paused
|
|
4149
|
-
};
|
|
4265
|
+
if (!currentNativeVideoUrl && rawUrl) {
|
|
4266
|
+
const normalizedNative = normalizeUrl(rawUrl, baseUrl);
|
|
4267
|
+
if (normalizedNative) {
|
|
4268
|
+
setCurrentNativeVideoUrl(normalizedNative);
|
|
4150
4269
|
}
|
|
4151
|
-
}
|
|
4152
|
-
|
|
4270
|
+
}
|
|
4271
|
+
if (hlsUrlCheckedRef.current === normalizedUrl) {
|
|
4272
|
+
return;
|
|
4273
|
+
}
|
|
4274
|
+
const nv = nativeVideoRef.current;
|
|
4275
|
+
if (nv && nv.currentTime > 0) {
|
|
4153
4276
|
savedStateRef.current = {
|
|
4154
|
-
time:
|
|
4155
|
-
paused:
|
|
4277
|
+
time: nv.currentTime,
|
|
4278
|
+
paused: nv.paused
|
|
4156
4279
|
};
|
|
4280
|
+
savedStateLockRef.current = true;
|
|
4157
4281
|
}
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4282
|
+
const checkHls = (attempt) => {
|
|
4283
|
+
if (cancelled) return;
|
|
4284
|
+
fetch(normalizedUrl, { method: "HEAD", credentials: "include" }).then((res) => {
|
|
4285
|
+
if (cancelled) return;
|
|
4286
|
+
hlsUrlCheckedRef.current = normalizedUrl;
|
|
4287
|
+
if (res.ok) {
|
|
4288
|
+
setCurrentVideoUrl(normalizedUrl);
|
|
4289
|
+
setPlayerError(null);
|
|
4290
|
+
setHlsFailed(false);
|
|
4291
|
+
onFallback == null ? void 0 : onFallback(false);
|
|
4292
|
+
triedFallbackUrlsRef.current.clear();
|
|
4293
|
+
} else {
|
|
4294
|
+
triedFallbackUrlsRef.current.clear();
|
|
4295
|
+
setHlsFailed(true);
|
|
4296
|
+
onFallback == null ? void 0 : onFallback(true);
|
|
4297
|
+
scheduleRetry(attempt);
|
|
4298
|
+
}
|
|
4299
|
+
}).catch((err) => {
|
|
4300
|
+
if (cancelled) return;
|
|
4301
|
+
triedFallbackUrlsRef.current.clear();
|
|
4302
|
+
setHlsFailed(true);
|
|
4303
|
+
onFallback == null ? void 0 : onFallback(true);
|
|
4304
|
+
scheduleRetry(attempt);
|
|
4305
|
+
});
|
|
4306
|
+
};
|
|
4307
|
+
const MAX_RETRIES = 10;
|
|
4308
|
+
const RETRY_INTERVAL = 5e3;
|
|
4309
|
+
const scheduleRetry = (attempt) => {
|
|
4310
|
+
if (cancelled || attempt >= MAX_RETRIES) return;
|
|
4311
|
+
retryTimer = setTimeout(() => checkHls(attempt + 1), RETRY_INTERVAL);
|
|
4312
|
+
};
|
|
4313
|
+
checkHls(0);
|
|
4162
4314
|
}
|
|
4163
4315
|
} else if (nativeVideoUrl) {
|
|
4164
4316
|
const normalizedUrl = normalizeUrl(nativeVideoUrl, baseUrl);
|
|
4165
4317
|
if (normalizedUrl) {
|
|
4166
|
-
if (previousVideoUrlRef.current
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4318
|
+
if (previousVideoUrlRef.current === normalizedUrl) {
|
|
4319
|
+
return;
|
|
4320
|
+
}
|
|
4321
|
+
const nv = nativeVideoRef.current;
|
|
4322
|
+
if (nv && nv.readyState > 0 && !nv.error && nv.src) {
|
|
4323
|
+
previousVideoUrlRef.current = normalizedUrl;
|
|
4324
|
+
return;
|
|
4325
|
+
}
|
|
4326
|
+
if (previousVideoUrlRef.current && nv && nv.currentTime > 0) {
|
|
4327
|
+
savedStateRef.current = {
|
|
4328
|
+
time: nv.currentTime,
|
|
4329
|
+
paused: nv.paused
|
|
4330
|
+
};
|
|
4174
4331
|
}
|
|
4175
4332
|
setCurrentNativeVideoUrl(normalizedUrl);
|
|
4176
4333
|
setCurrentVideoUrl(null);
|
|
4177
4334
|
previousVideoUrlRef.current = normalizedUrl;
|
|
4178
4335
|
}
|
|
4179
4336
|
}
|
|
4180
|
-
|
|
4337
|
+
return () => {
|
|
4338
|
+
cancelled = true;
|
|
4339
|
+
if (retryTimer) clearTimeout(retryTimer);
|
|
4340
|
+
};
|
|
4341
|
+
}, [primaryUrl, baseUrl, optimizedReady, nativeVideoUrl]);
|
|
4181
4342
|
useEffect(() => {
|
|
4182
4343
|
if (optimizedReady && currentVideoUrl && playerRef.current) {
|
|
4183
4344
|
const videoElement = playerRef.current.videoElement;
|
|
@@ -4191,6 +4352,9 @@ function VideoPlayerState({
|
|
|
4191
4352
|
});
|
|
4192
4353
|
}
|
|
4193
4354
|
savedStateRef.current = null;
|
|
4355
|
+
savedStateLockRef.current = false;
|
|
4356
|
+
} else {
|
|
4357
|
+
savedStateLockRef.current = false;
|
|
4194
4358
|
}
|
|
4195
4359
|
}
|
|
4196
4360
|
};
|
|
@@ -4224,10 +4388,35 @@ function VideoPlayerState({
|
|
|
4224
4388
|
}
|
|
4225
4389
|
}
|
|
4226
4390
|
}, [optimizedReady, currentVideoUrl]);
|
|
4391
|
+
useEffect(() => {
|
|
4392
|
+
const nv = nativeVideoRef.current;
|
|
4393
|
+
if (!nv) return;
|
|
4394
|
+
if (currentVideoUrl && !hlsFailed) {
|
|
4395
|
+
if (nv.currentTime > 0) {
|
|
4396
|
+
savedStateRef.current = {
|
|
4397
|
+
time: nv.currentTime,
|
|
4398
|
+
paused: nv.paused
|
|
4399
|
+
};
|
|
4400
|
+
}
|
|
4401
|
+
if (!nv.paused) {
|
|
4402
|
+
nv.pause();
|
|
4403
|
+
}
|
|
4404
|
+
}
|
|
4405
|
+
}, [currentVideoUrl, hlsFailed]);
|
|
4227
4406
|
useEffect(() => {
|
|
4228
4407
|
var _a;
|
|
4229
|
-
|
|
4230
|
-
|
|
4408
|
+
if (currentVideoUrl && !hlsFailed) {
|
|
4409
|
+
const shakaVideo = ((_a = playerRef.current) == null ? void 0 : _a.videoElement) || null;
|
|
4410
|
+
if (shakaVideo && shakaVideo.readyState >= 1) {
|
|
4411
|
+
setActiveVideoElement(shakaVideo);
|
|
4412
|
+
} else if (shakaVideo) {
|
|
4413
|
+
const onReady = () => setActiveVideoElement(shakaVideo);
|
|
4414
|
+
shakaVideo.addEventListener("loadedmetadata", onReady, { once: true });
|
|
4415
|
+
return () => shakaVideo.removeEventListener("loadedmetadata", onReady);
|
|
4416
|
+
}
|
|
4417
|
+
} else {
|
|
4418
|
+
setActiveVideoElement(nativeVideoRef.current);
|
|
4419
|
+
}
|
|
4231
4420
|
}, [optimizedReady, hlsFailed, currentVideoUrl, currentNativeVideoUrl]);
|
|
4232
4421
|
useEffect(() => {
|
|
4233
4422
|
if (!activeVideoElement) return;
|
|
@@ -4257,16 +4446,29 @@ function VideoPlayerState({
|
|
|
4257
4446
|
return () => observer.disconnect();
|
|
4258
4447
|
}, []);
|
|
4259
4448
|
useEffect(() => {
|
|
4260
|
-
var _a;
|
|
4449
|
+
var _a, _b;
|
|
4261
4450
|
if (!onPlayerInfo) return;
|
|
4262
4451
|
const shakaPlayer = ((_a = playerRef.current) == null ? void 0 : _a.player) || null;
|
|
4263
4452
|
const nativeVideo = nativeVideoRef.current || null;
|
|
4264
4453
|
const containerEl = containerRef.current || null;
|
|
4265
|
-
const mode =
|
|
4266
|
-
|
|
4267
|
-
|
|
4454
|
+
const mode = currentVideoUrl && !hlsFailed && shakaPlayer ? "shaka" : "native";
|
|
4455
|
+
let videoDuration = null;
|
|
4456
|
+
try {
|
|
4457
|
+
const range = (_b = shakaPlayer == null ? void 0 : shakaPlayer.seekRange) == null ? void 0 : _b.call(shakaPlayer);
|
|
4458
|
+
if (range && Number.isFinite(range.end) && range.end > 0) {
|
|
4459
|
+
videoDuration = range.end;
|
|
4460
|
+
}
|
|
4461
|
+
} catch {
|
|
4462
|
+
}
|
|
4463
|
+
if (videoDuration == null && activeVideoElement) {
|
|
4464
|
+
const d = activeVideoElement.duration;
|
|
4465
|
+
if (Number.isFinite(d) && d > 0) videoDuration = d;
|
|
4466
|
+
}
|
|
4467
|
+
onPlayerInfo({ shakaPlayer, nativeVideo, mode, containerEl, videoDuration });
|
|
4468
|
+
}, [onPlayerInfo, optimizedReady, hlsFailed, currentVideoUrl, currentNativeVideoUrl, activeVideoElement]);
|
|
4268
4469
|
useEffect(() => {
|
|
4269
4470
|
var _a;
|
|
4471
|
+
console.log("[Cover] posterUrl effect:", posterUrl);
|
|
4270
4472
|
const nativeVideo = nativeVideoRef.current;
|
|
4271
4473
|
if (nativeVideo) {
|
|
4272
4474
|
nativeVideo.poster = posterUrl || "";
|
|
@@ -4343,6 +4545,7 @@ function VideoPlayerState({
|
|
|
4343
4545
|
nativeVideo.play().catch(() => {
|
|
4344
4546
|
});
|
|
4345
4547
|
}
|
|
4548
|
+
savedStateLockRef.current = false;
|
|
4346
4549
|
}
|
|
4347
4550
|
};
|
|
4348
4551
|
if (nativeVideo.readyState >= 1) {
|
|
@@ -4353,10 +4556,12 @@ function VideoPlayerState({
|
|
|
4353
4556
|
});
|
|
4354
4557
|
}
|
|
4355
4558
|
const handleTimeUpdate = () => {
|
|
4559
|
+
if (savedStateLockRef.current) return;
|
|
4356
4560
|
if (timeUpdateThrottleRef.current) {
|
|
4357
4561
|
clearTimeout(timeUpdateThrottleRef.current);
|
|
4358
4562
|
}
|
|
4359
4563
|
timeUpdateThrottleRef.current = setTimeout(() => {
|
|
4564
|
+
if (savedStateLockRef.current) return;
|
|
4360
4565
|
if (!savedStateRef.current) {
|
|
4361
4566
|
savedStateRef.current = { time: 0, paused: true };
|
|
4362
4567
|
}
|
|
@@ -4364,6 +4569,7 @@ function VideoPlayerState({
|
|
|
4364
4569
|
}, 100);
|
|
4365
4570
|
};
|
|
4366
4571
|
const handlePlay = () => {
|
|
4572
|
+
if (savedStateLockRef.current) return;
|
|
4367
4573
|
if (!savedStateRef.current) {
|
|
4368
4574
|
savedStateRef.current = { time: 0, paused: false };
|
|
4369
4575
|
} else {
|
|
@@ -4371,6 +4577,7 @@ function VideoPlayerState({
|
|
|
4371
4577
|
}
|
|
4372
4578
|
};
|
|
4373
4579
|
const handlePause = () => {
|
|
4580
|
+
if (savedStateLockRef.current) return;
|
|
4374
4581
|
if (!savedStateRef.current) {
|
|
4375
4582
|
savedStateRef.current = { time: nativeVideo.currentTime, paused: true };
|
|
4376
4583
|
} else {
|
|
@@ -4388,6 +4595,12 @@ function VideoPlayerState({
|
|
|
4388
4595
|
if (timeUpdateThrottleRef.current) {
|
|
4389
4596
|
clearTimeout(timeUpdateThrottleRef.current);
|
|
4390
4597
|
}
|
|
4598
|
+
if (nativeVideo.currentTime > 0) {
|
|
4599
|
+
savedStateRef.current = {
|
|
4600
|
+
time: nativeVideo.currentTime,
|
|
4601
|
+
paused: nativeVideo.paused
|
|
4602
|
+
};
|
|
4603
|
+
}
|
|
4391
4604
|
};
|
|
4392
4605
|
}, [currentNativeVideoUrl]);
|
|
4393
4606
|
useEffect(() => {
|
|
@@ -4547,7 +4760,8 @@ function VideoPlayerState({
|
|
|
4547
4760
|
}
|
|
4548
4761
|
);
|
|
4549
4762
|
}
|
|
4550
|
-
const fallbackVideoUrl =
|
|
4763
|
+
const fallbackVideoUrl = normalizeUrl(rawUrl ?? null, baseUrl) || currentNativeVideoUrl;
|
|
4764
|
+
const shakaActive = !!(currentVideoUrl && !hlsFailed);
|
|
4551
4765
|
const isCompact = playerWidth > 0 && playerWidth < 500;
|
|
4552
4766
|
const isNarrow = playerWidth > 0 && playerWidth < 380;
|
|
4553
4767
|
const hasChapters = ((chapters == null ? void 0 : chapters.length) ?? 0) > 0;
|
|
@@ -4576,25 +4790,41 @@ function VideoPlayerState({
|
|
|
4576
4790
|
ref: containerRef,
|
|
4577
4791
|
onClick: handleContainerClick,
|
|
4578
4792
|
children: [
|
|
4579
|
-
|
|
4793
|
+
/* @__PURE__ */ jsx(
|
|
4580
4794
|
"video",
|
|
4581
4795
|
{
|
|
4582
4796
|
ref: nativeVideoRef,
|
|
4583
|
-
src: (hlsFailed ? fallbackVideoUrl :
|
|
4797
|
+
src: currentNativeVideoUrl || (hlsFailed ? fallbackVideoUrl : null) || void 0,
|
|
4584
4798
|
className: "video-player-media",
|
|
4585
|
-
|
|
4799
|
+
style: shakaActive ? { display: "none" } : void 0,
|
|
4800
|
+
poster: posterUrl || void 0,
|
|
4801
|
+
onError: (e) => {
|
|
4802
|
+
const video = e.currentTarget;
|
|
4803
|
+
const failedSrc = video.currentSrc || video.src;
|
|
4804
|
+
if (!failedSrc) return;
|
|
4805
|
+
const mediaError = video.error;
|
|
4806
|
+
if (!mediaError) return;
|
|
4807
|
+
triedFallbackUrlsRef.current.add(failedSrc);
|
|
4808
|
+
const candidates = [
|
|
4809
|
+
normalizeUrl(rawUrl ?? null, baseUrl),
|
|
4810
|
+
normalizeUrl(nativeVideoUrl, baseUrl)
|
|
4811
|
+
].filter((url) => !!url && !triedFallbackUrlsRef.current.has(url));
|
|
4812
|
+
if (candidates.length > 0) {
|
|
4813
|
+
video.src = candidates[0];
|
|
4814
|
+
video.load();
|
|
4815
|
+
}
|
|
4816
|
+
}
|
|
4586
4817
|
}
|
|
4587
4818
|
),
|
|
4588
|
-
|
|
4819
|
+
shakaActive && /* @__PURE__ */ jsx(
|
|
4589
4820
|
ShakaPlayer,
|
|
4590
4821
|
{
|
|
4591
4822
|
ref: playerRef,
|
|
4592
|
-
src: currentVideoUrl,
|
|
4593
4823
|
autoPlay: false,
|
|
4594
4824
|
className: "video-player-media"
|
|
4595
4825
|
}
|
|
4596
4826
|
),
|
|
4597
|
-
(currentNativeVideoUrl || currentVideoUrl || hlsFailed && fallbackVideoUrl) && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4827
|
+
(currentNativeVideoUrl || currentVideoUrl || primaryUrl || hlsFailed && fallbackVideoUrl) && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4598
4828
|
isPaused && !isCompact && /* @__PURE__ */ jsx(
|
|
4599
4829
|
"button",
|
|
4600
4830
|
{
|
|
@@ -4743,6 +4973,115 @@ function VideoPlayerState({
|
|
|
4743
4973
|
}
|
|
4744
4974
|
);
|
|
4745
4975
|
}
|
|
4976
|
+
function VideoSelect({
|
|
4977
|
+
size = "sm",
|
|
4978
|
+
w = 150,
|
|
4979
|
+
comboboxProps,
|
|
4980
|
+
rightSection,
|
|
4981
|
+
renderOption,
|
|
4982
|
+
data,
|
|
4983
|
+
value,
|
|
4984
|
+
defaultValue,
|
|
4985
|
+
onChange,
|
|
4986
|
+
inputClassName,
|
|
4987
|
+
...inputProps
|
|
4988
|
+
}) {
|
|
4989
|
+
var _a;
|
|
4990
|
+
const {
|
|
4991
|
+
onClick: onInputClick,
|
|
4992
|
+
type: _inputType,
|
|
4993
|
+
onCopy: _onCopy,
|
|
4994
|
+
onCut: _onCut,
|
|
4995
|
+
onPaste: _onPaste,
|
|
4996
|
+
classNames: parentClassNames,
|
|
4997
|
+
allowDeselect: _allowDeselect,
|
|
4998
|
+
...restInputProps
|
|
4999
|
+
} = inputProps;
|
|
5000
|
+
const parentInputClass = typeof parentClassNames === "object" && parentClassNames && "input" in parentClassNames ? parentClassNames.input : void 0;
|
|
5001
|
+
const inputClass = [parentInputClass, inputClassName].filter(Boolean).join(" ") || void 0;
|
|
5002
|
+
const mergedClassNames = inputClass ? {
|
|
5003
|
+
...typeof parentClassNames === "object" && parentClassNames ? parentClassNames : {},
|
|
5004
|
+
input: inputClass
|
|
5005
|
+
} : parentClassNames;
|
|
5006
|
+
const [search, setSearch] = useState("");
|
|
5007
|
+
const combobox = useCombobox({
|
|
5008
|
+
onDropdownClose: () => {
|
|
5009
|
+
combobox.resetSelectedOption();
|
|
5010
|
+
combobox.focusTarget();
|
|
5011
|
+
setSearch("");
|
|
5012
|
+
},
|
|
5013
|
+
onDropdownOpen: () => {
|
|
5014
|
+
combobox.focusSearchInput();
|
|
5015
|
+
}
|
|
5016
|
+
});
|
|
5017
|
+
const normalizedData = useMemo(
|
|
5018
|
+
() => (Array.isArray(data) ? data : []).map(
|
|
5019
|
+
(item) => typeof item === "string" ? { value: item, label: item } : item
|
|
5020
|
+
),
|
|
5021
|
+
[data]
|
|
5022
|
+
);
|
|
5023
|
+
const selectedValue = value ?? defaultValue ?? null;
|
|
5024
|
+
const selectedLabel = ((_a = normalizedData.find((item) => item.value === selectedValue)) == null ? void 0 : _a.label) ?? "";
|
|
5025
|
+
const renderOptionNode = (optionValue, optionLabel, checked) => renderOption ? renderOption({
|
|
5026
|
+
option: { value: optionValue, label: optionLabel },
|
|
5027
|
+
checked
|
|
5028
|
+
}) : /* @__PURE__ */ jsxs(Group, { justify: "space-between", wrap: "nowrap", w: "100%", children: [
|
|
5029
|
+
optionLabel,
|
|
5030
|
+
checked && /* @__PURE__ */ jsx(IconCheck, { size: 16, color: "var(--primary-color-text, #4C6EF5)" })
|
|
5031
|
+
] });
|
|
5032
|
+
const { width: comboboxWidth, ...restComboboxProps } = comboboxProps || {};
|
|
5033
|
+
const dropdownWidth = comboboxWidth ?? 250;
|
|
5034
|
+
return /* @__PURE__ */ jsxs(
|
|
5035
|
+
Combobox,
|
|
5036
|
+
{
|
|
5037
|
+
store: combobox,
|
|
5038
|
+
withinPortal: true,
|
|
5039
|
+
onOptionSubmit: (val) => {
|
|
5040
|
+
const option = normalizedData.find((item) => item.value === val) ?? {
|
|
5041
|
+
value: val,
|
|
5042
|
+
label: val
|
|
5043
|
+
};
|
|
5044
|
+
onChange == null ? void 0 : onChange(val, option);
|
|
5045
|
+
combobox.closeDropdown();
|
|
5046
|
+
},
|
|
5047
|
+
width: dropdownWidth,
|
|
5048
|
+
...restComboboxProps,
|
|
5049
|
+
children: [
|
|
5050
|
+
/* @__PURE__ */ jsx(Combobox.Target, { children: /* @__PURE__ */ jsx(
|
|
5051
|
+
InputBase,
|
|
5052
|
+
{
|
|
5053
|
+
component: "button",
|
|
5054
|
+
size,
|
|
5055
|
+
w,
|
|
5056
|
+
classNames: mergedClassNames,
|
|
5057
|
+
pointer: true,
|
|
5058
|
+
rightSection: rightSection ?? /* @__PURE__ */ jsx(IconChevronDown, { size: 16, className: "editor-select__chevron" }),
|
|
5059
|
+
rightSectionPointerEvents: "none",
|
|
5060
|
+
onClick: (e) => {
|
|
5061
|
+
onInputClick == null ? void 0 : onInputClick(e);
|
|
5062
|
+
combobox.toggleDropdown();
|
|
5063
|
+
},
|
|
5064
|
+
...restInputProps,
|
|
5065
|
+
children: selectedLabel
|
|
5066
|
+
}
|
|
5067
|
+
) }),
|
|
5068
|
+
/* @__PURE__ */ jsx(Combobox.Dropdown, { children: /* @__PURE__ */ jsx(
|
|
5069
|
+
Combobox.Options,
|
|
5070
|
+
{
|
|
5071
|
+
style: {
|
|
5072
|
+
maxHeight: 200,
|
|
5073
|
+
overflowY: "auto"
|
|
5074
|
+
},
|
|
5075
|
+
children: normalizedData.length > 0 ? normalizedData.map((item) => {
|
|
5076
|
+
const checked = item.value === selectedValue;
|
|
5077
|
+
return /* @__PURE__ */ jsx(Combobox.Option, { value: item.value, children: renderOptionNode(item.value, item.label, checked) }, item.value);
|
|
5078
|
+
}) : /* @__PURE__ */ jsx(Combobox.Empty, { children: "Nothing found" })
|
|
5079
|
+
}
|
|
5080
|
+
) })
|
|
5081
|
+
]
|
|
5082
|
+
}
|
|
5083
|
+
);
|
|
5084
|
+
}
|
|
4746
5085
|
const isLinkPlatform = (value) => value === "link" || value === "youtube" || value === "vk" || value === "rutube";
|
|
4747
5086
|
const isUploadPlatform = (value) => value === "upload" || value === "download";
|
|
4748
5087
|
function VideoUploadComponent({
|
|
@@ -4768,7 +5107,7 @@ function VideoUploadComponent({
|
|
|
4768
5107
|
const { t, i18n } = useTranslation();
|
|
4769
5108
|
const { config, dependencies, registerUpload, unregisterUpload, reportError } = useVideoPluginContext();
|
|
4770
5109
|
const { maxFileSize = 5, pageId } = config;
|
|
4771
|
-
const SelectComponent = dependencies.Select ||
|
|
5110
|
+
const SelectComponent = dependencies.Select || VideoSelect;
|
|
4772
5111
|
const [currentState, setCurrentState] = useState(
|
|
4773
5112
|
initialVideoState ?? "default"
|
|
4774
5113
|
);
|
|
@@ -4780,6 +5119,7 @@ function VideoUploadComponent({
|
|
|
4780
5119
|
const [videoId, setVideoId] = useState(initialVideoId ?? null);
|
|
4781
5120
|
const [linkUrl, setLinkUrl] = useState("");
|
|
4782
5121
|
const [linkError, setLinkError] = useState(null);
|
|
5122
|
+
const [isHlsFallback, setIsHlsFallback] = useState(false);
|
|
4783
5123
|
const lastUploadFileRef = useRef(null);
|
|
4784
5124
|
const uploadCallbackRef = useRef(false);
|
|
4785
5125
|
const uploadIdRef = useRef(null);
|
|
@@ -4869,7 +5209,16 @@ function VideoUploadComponent({
|
|
|
4869
5209
|
const resolvedRawPlayable = sourceType === "upload" ? rawPlayable : false;
|
|
4870
5210
|
const resolvedStatusAvailable = sourceType === "upload" ? isStatusAvailable : false;
|
|
4871
5211
|
const chapters = chaptersOverride ?? (resolvedStatus == null ? void 0 : resolvedStatus.chapters) ?? void 0;
|
|
4872
|
-
const
|
|
5212
|
+
const rawSubtitles = subtitlesOverride ?? (resolvedStatus == null ? void 0 : resolvedStatus.subtitles) ?? void 0;
|
|
5213
|
+
const subtitlesRef = useRef(rawSubtitles);
|
|
5214
|
+
const subtitles = useMemo(() => {
|
|
5215
|
+
if (rawSubtitles === subtitlesRef.current) return subtitlesRef.current;
|
|
5216
|
+
if (JSON.stringify(rawSubtitles) === JSON.stringify(subtitlesRef.current)) {
|
|
5217
|
+
return subtitlesRef.current;
|
|
5218
|
+
}
|
|
5219
|
+
subtitlesRef.current = rawSubtitles;
|
|
5220
|
+
return rawSubtitles;
|
|
5221
|
+
}, [rawSubtitles]);
|
|
4873
5222
|
const inferredNativeVideoUrl = useMemo(() => {
|
|
4874
5223
|
if (fallbackNativeVideoUrl) return fallbackNativeVideoUrl;
|
|
4875
5224
|
if (platform === "link") {
|
|
@@ -4960,7 +5309,7 @@ function VideoUploadComponent({
|
|
|
4960
5309
|
const isReadyForPreview = currentState === "video-preview" && (sourceType === "link" ? true : resolvedStatusAvailable ? resolvedOptimizedReady : resolvedOptimizedReady || resolvedRawPlayable || hasPreviewSource);
|
|
4961
5310
|
const isProcessingStatus = (resolvedStatus == null ? void 0 : resolvedStatus.state) === "PROCESSING" || (resolvedStatus == null ? void 0 : resolvedStatus.state) === "UPLOADING";
|
|
4962
5311
|
const hasProcessingPercent = typeof ((_a = resolvedStatus == null ? void 0 : resolvedStatus.processing) == null ? void 0 : _a.percent) === "number";
|
|
4963
|
-
const showProcessingBanner = currentState === "video-preview" && sourceType === "upload" && !!resolvedNativeVideoUrl && resolvedStatusAvailable && !resolvedOptimizedReady && (isProcessingStatus || hasProcessingPercent);
|
|
5312
|
+
const showProcessingBanner = currentState === "video-preview" && sourceType === "upload" && !!resolvedNativeVideoUrl && resolvedStatusAvailable && !resolvedOptimizedReady && !isHlsFallback && (isProcessingStatus || hasProcessingPercent);
|
|
4964
5313
|
const maxBannerPercentRef = useRef(0);
|
|
4965
5314
|
const rawPercent = Math.max(0, Math.min(100, ((_b = resolvedStatus == null ? void 0 : resolvedStatus.processing) == null ? void 0 : _b.percent) ?? 0));
|
|
4966
5315
|
if (rawPercent > maxBannerPercentRef.current) {
|
|
@@ -5015,12 +5364,17 @@ function VideoUploadComponent({
|
|
|
5015
5364
|
setLinkError(null);
|
|
5016
5365
|
setCurrentState("video-preview");
|
|
5017
5366
|
};
|
|
5367
|
+
const videoIdRef = useRef(videoId);
|
|
5368
|
+
videoIdRef.current = videoId;
|
|
5369
|
+
const uploadVideoIdRef = useRef(uploadVideoId);
|
|
5370
|
+
uploadVideoIdRef.current = uploadVideoId;
|
|
5018
5371
|
useEffect(() => {
|
|
5019
5372
|
return editor.registerCommand(
|
|
5020
5373
|
UPLOAD_VIDEO_COMMAND,
|
|
5021
5374
|
(payload) => {
|
|
5022
|
-
if (sourceType === "upload" && payload.length > 0) {
|
|
5375
|
+
if (sourceType === "upload" && payload.length > 0 && !videoIdRef.current && !uploadVideoIdRef.current) {
|
|
5023
5376
|
handleFileSelect(payload[0]);
|
|
5377
|
+
return true;
|
|
5024
5378
|
}
|
|
5025
5379
|
return false;
|
|
5026
5380
|
},
|
|
@@ -5075,7 +5429,7 @@ function VideoUploadComponent({
|
|
|
5075
5429
|
onStateChange == null ? void 0 : onStateChange(currentState);
|
|
5076
5430
|
}, [currentState, onStateChange]);
|
|
5077
5431
|
const renderState = () => {
|
|
5078
|
-
var _a2;
|
|
5432
|
+
var _a2, _b2;
|
|
5079
5433
|
if (sourceType === "link" && currentState !== "video-preview") {
|
|
5080
5434
|
return /* @__PURE__ */ jsx(
|
|
5081
5435
|
LinkState,
|
|
@@ -5168,6 +5522,8 @@ function VideoUploadComponent({
|
|
|
5168
5522
|
}
|
|
5169
5523
|
) });
|
|
5170
5524
|
}
|
|
5525
|
+
const effectivePoster = posterUrl ?? ((_a2 = resolvedStatus == null ? void 0 : resolvedStatus.cover) == null ? void 0 : _a2.url) ?? null;
|
|
5526
|
+
console.log("[Cover] VideoUploadComponent render: posterUrl prop=", posterUrl, "cover.url=", (_b2 = resolvedStatus == null ? void 0 : resolvedStatus.cover) == null ? void 0 : _b2.url, "effective=", effectivePoster);
|
|
5171
5527
|
return /* @__PURE__ */ jsx(
|
|
5172
5528
|
VideoPlayerState,
|
|
5173
5529
|
{
|
|
@@ -5182,8 +5538,9 @@ function VideoUploadComponent({
|
|
|
5182
5538
|
chapters: showProcessingBanner ? void 0 : chapters,
|
|
5183
5539
|
isProcessing: showProcessingBanner,
|
|
5184
5540
|
subtitles,
|
|
5185
|
-
posterUrl:
|
|
5186
|
-
onPlayerInfo
|
|
5541
|
+
posterUrl: effectivePoster,
|
|
5542
|
+
onPlayerInfo,
|
|
5543
|
+
onFallback: setIsHlsFallback
|
|
5187
5544
|
}
|
|
5188
5545
|
);
|
|
5189
5546
|
default:
|
|
@@ -5323,21 +5680,6 @@ async function handleResponse(response) {
|
|
|
5323
5680
|
}
|
|
5324
5681
|
return parseJson(response);
|
|
5325
5682
|
}
|
|
5326
|
-
async function listChaptersEffective(videoId, options = {}) {
|
|
5327
|
-
const url = buildApiUrl(
|
|
5328
|
-
`/v1/videos/${videoId}/chapters/effective`,
|
|
5329
|
-
options.baseUrl
|
|
5330
|
-
);
|
|
5331
|
-
const response = await fetch(url, {
|
|
5332
|
-
method: "GET",
|
|
5333
|
-
credentials: "include",
|
|
5334
|
-
headers: {
|
|
5335
|
-
...getAuthHeaders(options.authToken)
|
|
5336
|
-
}
|
|
5337
|
-
});
|
|
5338
|
-
const data = await handleResponse(response);
|
|
5339
|
-
return Array.isArray(data) ? data : [];
|
|
5340
|
-
}
|
|
5341
5683
|
async function saveChapters(videoId, chapters, options = {}) {
|
|
5342
5684
|
const url = buildApiUrl(`/v1/videos/${videoId}/chapters`, options.baseUrl);
|
|
5343
5685
|
const response = await fetch(url, {
|
|
@@ -5465,9 +5807,7 @@ function ChapterRow({
|
|
|
5465
5807
|
/* @__PURE__ */ jsxs(
|
|
5466
5808
|
Stack,
|
|
5467
5809
|
{
|
|
5468
|
-
gap: 10,
|
|
5469
5810
|
className: "video-settings-modal-chapter-times",
|
|
5470
|
-
style: { width: 72, flex: "0 0 72px" },
|
|
5471
5811
|
children: [
|
|
5472
5812
|
/* @__PURE__ */ jsx(
|
|
5473
5813
|
TextInput,
|
|
@@ -5527,9 +5867,7 @@ function ChapterRow({
|
|
|
5527
5867
|
/* @__PURE__ */ jsx(
|
|
5528
5868
|
Flex,
|
|
5529
5869
|
{
|
|
5530
|
-
align: "center",
|
|
5531
5870
|
className: "video-settings-modal-chapter-title",
|
|
5532
|
-
style: { flex: 1, minWidth: 0, height: 82 },
|
|
5533
5871
|
children: /* @__PURE__ */ jsx(
|
|
5534
5872
|
Textarea,
|
|
5535
5873
|
{
|
|
@@ -5566,10 +5904,7 @@ function ChapterRow({
|
|
|
5566
5904
|
/* @__PURE__ */ jsx(
|
|
5567
5905
|
Flex,
|
|
5568
5906
|
{
|
|
5569
|
-
align: "center",
|
|
5570
|
-
justify: "center",
|
|
5571
5907
|
className: "video-settings-modal-chapter-actions",
|
|
5572
|
-
style: { width: 36, flex: "0 0 36px", height: 82 },
|
|
5573
5908
|
children: /* @__PURE__ */ jsxs(Menu, { position: "bottom-end", children: [
|
|
5574
5909
|
/* @__PURE__ */ jsx(Menu.Target, { children: /* @__PURE__ */ jsx(
|
|
5575
5910
|
ActionIcon,
|
|
@@ -5595,6 +5930,8 @@ function ChaptersSection({
|
|
|
5595
5930
|
videoId,
|
|
5596
5931
|
initialChapters,
|
|
5597
5932
|
nativeVideo,
|
|
5933
|
+
shakaPlayer,
|
|
5934
|
+
videoDuration: videoDurationProp,
|
|
5598
5935
|
baseUrl,
|
|
5599
5936
|
authToken,
|
|
5600
5937
|
title: title2,
|
|
@@ -5614,7 +5951,7 @@ function ChaptersSection({
|
|
|
5614
5951
|
addTranscriptManuallyLabel
|
|
5615
5952
|
}) {
|
|
5616
5953
|
const dependencies = useVideoPluginDependencies();
|
|
5617
|
-
const SelectComponent = dependencies.Select ||
|
|
5954
|
+
const SelectComponent = dependencies.Select || VideoSelect;
|
|
5618
5955
|
const [editableChapters, setEditableChapters] = useState(
|
|
5619
5956
|
[]
|
|
5620
5957
|
);
|
|
@@ -5639,50 +5976,32 @@ function ChaptersSection({
|
|
|
5639
5976
|
setEditableChapters(toEditable(list));
|
|
5640
5977
|
}, []);
|
|
5641
5978
|
useEffect(() => {
|
|
5642
|
-
|
|
5643
|
-
|
|
5979
|
+
var _a;
|
|
5980
|
+
if (videoDurationProp != null && videoDurationProp > 0) {
|
|
5981
|
+
setDurationSec(videoDurationProp);
|
|
5644
5982
|
return;
|
|
5645
5983
|
}
|
|
5646
|
-
const updateDuration = () => {
|
|
5647
|
-
const d = nativeVideo.duration;
|
|
5648
|
-
setDurationSec(Number.isFinite(d) && d > 0 ? d : null);
|
|
5649
|
-
};
|
|
5650
|
-
updateDuration();
|
|
5651
|
-
nativeVideo.addEventListener("loadedmetadata", updateDuration);
|
|
5652
|
-
nativeVideo.addEventListener("durationchange", updateDuration);
|
|
5653
|
-
return () => {
|
|
5654
|
-
nativeVideo.removeEventListener("loadedmetadata", updateDuration);
|
|
5655
|
-
nativeVideo.removeEventListener("durationchange", updateDuration);
|
|
5656
|
-
};
|
|
5657
|
-
}, [nativeVideo]);
|
|
5658
|
-
const loadEffective = useCallback(async () => {
|
|
5659
|
-
if (!videoId) return;
|
|
5660
|
-
setIsLoading(true);
|
|
5661
|
-
setError(null);
|
|
5662
|
-
setValidationError(null);
|
|
5663
5984
|
try {
|
|
5664
|
-
const
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5985
|
+
const range = (_a = shakaPlayer == null ? void 0 : shakaPlayer.seekRange) == null ? void 0 : _a.call(shakaPlayer);
|
|
5986
|
+
if (range && Number.isFinite(range.end) && range.end > 0) {
|
|
5987
|
+
setDurationSec(range.end);
|
|
5988
|
+
return;
|
|
5989
|
+
}
|
|
5990
|
+
} catch {
|
|
5670
5991
|
}
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
return durationSec;
|
|
5680
|
-
}, [durationSec, editableChapters]);
|
|
5992
|
+
if (nativeVideo) {
|
|
5993
|
+
const d = nativeVideo.duration;
|
|
5994
|
+
if (Number.isFinite(d) && d > 0) {
|
|
5995
|
+
setDurationSec(d);
|
|
5996
|
+
return;
|
|
5997
|
+
}
|
|
5998
|
+
}
|
|
5999
|
+
}, [videoDurationProp, shakaPlayer, nativeVideo]);
|
|
5681
6000
|
const getMaxAllowedSec = useCallback(() => {
|
|
5682
|
-
if (
|
|
5683
|
-
return Math.floor(
|
|
5684
|
-
}, [
|
|
5685
|
-
|
|
6001
|
+
if (durationSec == null) return null;
|
|
6002
|
+
return Math.floor(durationSec);
|
|
6003
|
+
}, [durationSec]);
|
|
6004
|
+
useCallback(() => {
|
|
5686
6005
|
const list = [...editableChapters].filter((ch) => ch.title.trim()).sort((a, b) => a.startSec - b.startSec);
|
|
5687
6006
|
const maxAllowed = getMaxAllowedSec();
|
|
5688
6007
|
for (let i = 0; i < list.length; i++) {
|
|
@@ -5700,21 +6019,19 @@ function ChaptersSection({
|
|
|
5700
6019
|
const handleSave = useCallback(async () => {
|
|
5701
6020
|
if (!videoId) return null;
|
|
5702
6021
|
setValidationError(null);
|
|
5703
|
-
if (!validateChapters()) {
|
|
5704
|
-
setValidationError(invalidRangeLabel);
|
|
5705
|
-
return null;
|
|
5706
|
-
}
|
|
5707
6022
|
setIsSaving(true);
|
|
5708
6023
|
isSavingRef.current = true;
|
|
5709
6024
|
setError(null);
|
|
5710
6025
|
try {
|
|
5711
|
-
const payload = editableChapters.filter((ch) => ch.title.trim()).sort((a, b) => a.startSec - b.startSec).map(({ startSec, title: title22 }) => ({ startSec, title: title22 }));
|
|
6026
|
+
const payload = editableChapters.filter((ch) => ch.title.trim()).filter((ch) => durationSec == null || ch.startSec < durationSec).sort((a, b) => a.startSec - b.startSec).map(({ startSec, title: title22 }) => ({ startSec, title: title22 }));
|
|
5712
6027
|
await saveChapters(videoId, payload, apiOptions);
|
|
5713
6028
|
hasLocalEditsRef.current = false;
|
|
5714
|
-
await loadEffective();
|
|
5715
6029
|
return payload;
|
|
5716
6030
|
} catch (err) {
|
|
5717
|
-
|
|
6031
|
+
const msg = err instanceof Error ? err.message : errorLabel;
|
|
6032
|
+
if (!/exceeds/i.test(msg)) {
|
|
6033
|
+
setError(msg);
|
|
6034
|
+
}
|
|
5718
6035
|
return null;
|
|
5719
6036
|
} finally {
|
|
5720
6037
|
setIsSaving(false);
|
|
@@ -5724,10 +6041,8 @@ function ChaptersSection({
|
|
|
5724
6041
|
videoId,
|
|
5725
6042
|
editableChapters,
|
|
5726
6043
|
apiOptions,
|
|
5727
|
-
loadEffective,
|
|
5728
6044
|
errorLabel,
|
|
5729
|
-
|
|
5730
|
-
invalidRangeLabel
|
|
6045
|
+
getMaxAllowedSec
|
|
5731
6046
|
]);
|
|
5732
6047
|
useEffect(() => {
|
|
5733
6048
|
onSave == null ? void 0 : onSave(handleSave);
|
|
@@ -5736,7 +6051,7 @@ function ChaptersSection({
|
|
|
5736
6051
|
hasLocalEditsRef.current = true;
|
|
5737
6052
|
setValidationError(null);
|
|
5738
6053
|
setEditableChapters((prev) => {
|
|
5739
|
-
const maxAllowed =
|
|
6054
|
+
const maxAllowed = durationSec != null ? Math.floor(durationSec) : null;
|
|
5740
6055
|
const sorted = [...prev].sort((a, b) => a.startSec - b.startSec);
|
|
5741
6056
|
const lastStart = sorted.length > 0 ? Math.floor(sorted[sorted.length - 1].startSec) : -1;
|
|
5742
6057
|
const nextStart = Math.max(0, lastStart + 1);
|
|
@@ -5807,11 +6122,8 @@ function ChaptersSection({
|
|
|
5807
6122
|
}
|
|
5808
6123
|
if (Array.isArray(initialChapters)) {
|
|
5809
6124
|
applyChapters(initialChapters);
|
|
5810
|
-
return;
|
|
5811
6125
|
}
|
|
5812
|
-
|
|
5813
|
-
});
|
|
5814
|
-
}, [videoId, initialChapters, applyChapters, loadEffective]);
|
|
6126
|
+
}, [videoId, initialChapters, applyChapters]);
|
|
5815
6127
|
return /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
|
|
5816
6128
|
/* @__PURE__ */ jsx(
|
|
5817
6129
|
Text,
|
|
@@ -5851,7 +6163,7 @@ function ChaptersSection({
|
|
|
5851
6163
|
/* @__PURE__ */ jsx(Stack, { gap: 12, children: [...editableChapters].sort((a, b) => a.startSec - b.startSec).map((chapter, index, list) => {
|
|
5852
6164
|
const next = list[index + 1] || null;
|
|
5853
6165
|
const nextNext = list[index + 2] || null;
|
|
5854
|
-
const maxAllowed =
|
|
6166
|
+
const maxAllowed = durationSec != null ? Math.floor(durationSec) : null;
|
|
5855
6167
|
const endSec = next != null ? Math.floor(next.startSec) : maxAllowed != null ? maxAllowed : null;
|
|
5856
6168
|
const startSec = Math.floor(chapter.startSec);
|
|
5857
6169
|
const prevStart = index > 0 ? Math.floor(list[index - 1].startSec) : null;
|
|
@@ -5879,7 +6191,7 @@ function ChaptersSection({
|
|
|
5879
6191
|
endMax,
|
|
5880
6192
|
disabled: false,
|
|
5881
6193
|
startTimePlaceholder: timePlaceholder,
|
|
5882
|
-
endTimePlaceholder:
|
|
6194
|
+
endTimePlaceholder: durationSec != null ? secondsToTimestamp(Math.floor(durationSec)) : endTimePlaceholder,
|
|
5883
6195
|
titlePlaceholder,
|
|
5884
6196
|
deleteChapterLabel,
|
|
5885
6197
|
onStartSecChange: handleStartSecChange,
|
|
@@ -5952,7 +6264,7 @@ function CoverSection({
|
|
|
5952
6264
|
[(_a = status == null ? void 0 : status.cover) == null ? void 0 : _a.previewId, previews]
|
|
5953
6265
|
);
|
|
5954
6266
|
const effectiveSelected = selectedPreviewId ?? coverId;
|
|
5955
|
-
|
|
6267
|
+
previews.length > 0;
|
|
5956
6268
|
const slidesCount = previews.length + 1;
|
|
5957
6269
|
const showControls = slidesCount > 3;
|
|
5958
6270
|
const setPreviewLoaded = useCallback((id) => {
|
|
@@ -6000,7 +6312,7 @@ function CoverSection({
|
|
|
6000
6312
|
}, [videoId]);
|
|
6001
6313
|
const handleCoverDrop = useCallback(
|
|
6002
6314
|
async (files) => {
|
|
6003
|
-
var _a2, _b2, _c;
|
|
6315
|
+
var _a2, _b2, _c, _d;
|
|
6004
6316
|
const file = files[0];
|
|
6005
6317
|
if (!file || !onCoverUpload) return;
|
|
6006
6318
|
const objectUrl = URL.createObjectURL(file);
|
|
@@ -6010,7 +6322,6 @@ function CoverSection({
|
|
|
6010
6322
|
onSelectedPosterChange == null ? void 0 : onSelectedPosterChange(objectUrl);
|
|
6011
6323
|
try {
|
|
6012
6324
|
const nextStatus = await Promise.resolve(onCoverUpload(file));
|
|
6013
|
-
console.log("cover upload response", nextStatus);
|
|
6014
6325
|
setOptimisticPreviewUrl(null);
|
|
6015
6326
|
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
|
6016
6327
|
const nextPreviews = nextStatus == null ? void 0 : nextStatus.previews;
|
|
@@ -6018,10 +6329,16 @@ function CoverSection({
|
|
|
6018
6329
|
if (nextStatus && hasPreviews && nextPreviews) {
|
|
6019
6330
|
scrollToEndAfterUpdateRef.current = true;
|
|
6020
6331
|
setStatus(nextStatus);
|
|
6021
|
-
const
|
|
6022
|
-
setSelectedPreviewId(
|
|
6023
|
-
|
|
6024
|
-
|
|
6332
|
+
const uploadedPreviewId = ((_a2 = nextPreviews[nextPreviews.length - 1]) == null ? void 0 : _a2.id) ?? ((_b2 = nextStatus == null ? void 0 : nextStatus.cover) == null ? void 0 : _b2.previewId) ?? ((_c = nextPreviews[0]) == null ? void 0 : _c.id);
|
|
6333
|
+
setSelectedPreviewId(uploadedPreviewId ?? "uploaded");
|
|
6334
|
+
if (uploadedPreviewId) {
|
|
6335
|
+
const uploadedUrl = (_d = nextPreviews.find(
|
|
6336
|
+
(p) => p.id === uploadedPreviewId
|
|
6337
|
+
)) == null ? void 0 : _d.url;
|
|
6338
|
+
if (uploadedUrl) {
|
|
6339
|
+
onSelectedPosterChange == null ? void 0 : onSelectedPosterChange(uploadedUrl);
|
|
6340
|
+
}
|
|
6341
|
+
}
|
|
6025
6342
|
setLoadingIds(/* @__PURE__ */ new Set());
|
|
6026
6343
|
setErrorIds(/* @__PURE__ */ new Set());
|
|
6027
6344
|
} else {
|
|
@@ -6038,7 +6355,7 @@ function CoverSection({
|
|
|
6038
6355
|
loadStatus();
|
|
6039
6356
|
}
|
|
6040
6357
|
},
|
|
6041
|
-
[onCoverUpload, loadStatus]
|
|
6358
|
+
[onCoverUpload, loadStatus, onSelectedPosterChange]
|
|
6042
6359
|
);
|
|
6043
6360
|
const selectedUrl = useMemo(() => {
|
|
6044
6361
|
var _a2, _b2;
|
|
@@ -6159,6 +6476,7 @@ function CoverSection({
|
|
|
6159
6476
|
className: `video-settings-modal-cover-cell video-settings-modal-cover-thumb ${isSelected && !isError ? "selected" : ""} ${isLoading2 ? "loading" : ""} ${isError ? "error" : ""}`,
|
|
6160
6477
|
onClick: () => {
|
|
6161
6478
|
if (isError) return;
|
|
6479
|
+
console.log("[Cover] click preview:", preview.id, preview.url);
|
|
6162
6480
|
setSelectedPreviewId(preview.id);
|
|
6163
6481
|
onSelectedPosterChange == null ? void 0 : onSelectedPosterChange(preview.url);
|
|
6164
6482
|
},
|
|
@@ -6208,15 +6526,15 @@ function CoverSection({
|
|
|
6208
6526
|
})
|
|
6209
6527
|
]
|
|
6210
6528
|
}
|
|
6211
|
-
)
|
|
6212
|
-
!hasCoverCandidates && !isLoading && !loadError && /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: emptyCoverLabel })
|
|
6529
|
+
)
|
|
6213
6530
|
] });
|
|
6214
6531
|
}
|
|
6215
6532
|
function FooterActions({
|
|
6216
6533
|
cancelLabel,
|
|
6217
6534
|
saveLabel,
|
|
6218
6535
|
onCancel,
|
|
6219
|
-
onSave
|
|
6536
|
+
onSave,
|
|
6537
|
+
isSaving
|
|
6220
6538
|
}) {
|
|
6221
6539
|
return /* @__PURE__ */ jsxs(Group, { justify: "flex-end", gap: 8, children: [
|
|
6222
6540
|
/* @__PURE__ */ jsx(
|
|
@@ -6227,6 +6545,7 @@ function FooterActions({
|
|
|
6227
6545
|
color: "gray",
|
|
6228
6546
|
radius: "md",
|
|
6229
6547
|
onClick: onCancel,
|
|
6548
|
+
disabled: isSaving,
|
|
6230
6549
|
children: cancelLabel
|
|
6231
6550
|
}
|
|
6232
6551
|
),
|
|
@@ -6234,9 +6553,9 @@ function FooterActions({
|
|
|
6234
6553
|
Button,
|
|
6235
6554
|
{
|
|
6236
6555
|
size: "sm",
|
|
6237
|
-
color: "blue",
|
|
6238
6556
|
radius: "md",
|
|
6239
6557
|
onClick: onSave,
|
|
6558
|
+
loading: isSaving,
|
|
6240
6559
|
children: saveLabel
|
|
6241
6560
|
}
|
|
6242
6561
|
)
|
|
@@ -6276,23 +6595,29 @@ const LANGUAGE_LABELS = LANGUAGE_OPTIONS.reduce(
|
|
|
6276
6595
|
);
|
|
6277
6596
|
function ManualSubtitlesPanel({
|
|
6278
6597
|
videoId,
|
|
6279
|
-
mode,
|
|
6280
|
-
shakaPlayer,
|
|
6281
|
-
nativeVideo,
|
|
6282
6598
|
baseUrl,
|
|
6283
6599
|
authToken,
|
|
6284
6600
|
statusSubtitles,
|
|
6285
|
-
onTracksChange
|
|
6601
|
+
onTracksChange,
|
|
6602
|
+
onSave
|
|
6286
6603
|
}) {
|
|
6287
6604
|
const { t } = useTranslation();
|
|
6605
|
+
const { reportError } = useVideoPluginContext();
|
|
6288
6606
|
const dependencies = useVideoPluginDependencies();
|
|
6289
|
-
const SelectComponent = dependencies.Select ||
|
|
6607
|
+
const SelectComponent = dependencies.Select || VideoSelect;
|
|
6290
6608
|
const [tracks, setTracks] = useState([]);
|
|
6291
6609
|
const [drafts, setDrafts] = useState([]);
|
|
6292
|
-
const [loadError, setLoadError] = useState(null);
|
|
6293
6610
|
const uploadControllersRef = useRef(/* @__PURE__ */ new Map());
|
|
6294
|
-
const
|
|
6295
|
-
const
|
|
6611
|
+
const [autoSubtitlesDisabled, setAutoSubtitlesDisabled] = useState(false);
|
|
6612
|
+
const autoTrack = useMemo(
|
|
6613
|
+
() => tracks.find(isAutoGeneratedTrack) ?? null,
|
|
6614
|
+
[tracks]
|
|
6615
|
+
);
|
|
6616
|
+
useEffect(() => {
|
|
6617
|
+
if (autoTrack) {
|
|
6618
|
+
setAutoSubtitlesDisabled(autoTrack.isDisabled);
|
|
6619
|
+
}
|
|
6620
|
+
}, [autoTrack]);
|
|
6296
6621
|
const apiOptions = useMemo(
|
|
6297
6622
|
() => ({
|
|
6298
6623
|
baseUrl,
|
|
@@ -6300,26 +6625,54 @@ function ManualSubtitlesPanel({
|
|
|
6300
6625
|
}),
|
|
6301
6626
|
[baseUrl, authToken]
|
|
6302
6627
|
);
|
|
6628
|
+
const handleAutoSubtitlesToggle = useCallback(
|
|
6629
|
+
(disabled2) => {
|
|
6630
|
+
if (!autoTrack) return;
|
|
6631
|
+
setAutoSubtitlesDisabled(disabled2);
|
|
6632
|
+
setTracks(
|
|
6633
|
+
(prev) => prev.map(
|
|
6634
|
+
(t2) => t2.id === autoTrack.id ? { ...t2, isDisabled: disabled2 } : t2
|
|
6635
|
+
)
|
|
6636
|
+
);
|
|
6637
|
+
},
|
|
6638
|
+
[autoTrack]
|
|
6639
|
+
);
|
|
6640
|
+
const initialAutoDisabledRef = useRef(null);
|
|
6641
|
+
useEffect(() => {
|
|
6642
|
+
if (autoTrack && initialAutoDisabledRef.current === null) {
|
|
6643
|
+
initialAutoDisabledRef.current = autoTrack.isDisabled;
|
|
6644
|
+
}
|
|
6645
|
+
}, [autoTrack]);
|
|
6303
6646
|
const refreshTracks = useCallback(
|
|
6304
6647
|
async (forceApi = false) => {
|
|
6305
6648
|
if (!videoId) return;
|
|
6306
|
-
if (
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6649
|
+
if (forceApi || statusSubtitles == null) {
|
|
6650
|
+
try {
|
|
6651
|
+
const freshTracks = await listTracks(videoId, apiOptions);
|
|
6652
|
+
setTracks(freshTracks);
|
|
6653
|
+
return;
|
|
6654
|
+
} catch {
|
|
6655
|
+
}
|
|
6311
6656
|
}
|
|
6312
6657
|
try {
|
|
6313
|
-
const
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6658
|
+
const freshTracks = await listTracks(videoId, apiOptions);
|
|
6659
|
+
setTracks(freshTracks);
|
|
6660
|
+
} catch {
|
|
6661
|
+
if (statusSubtitles != null) {
|
|
6662
|
+
const nextTracks = (statusSubtitles || []).map(normalizeSubtitleTrack).filter((track) => track.id);
|
|
6663
|
+
setTracks(nextTracks);
|
|
6664
|
+
} else {
|
|
6665
|
+
try {
|
|
6666
|
+
const status = await fetchStatus(videoId);
|
|
6667
|
+
const nextTracks = ((status == null ? void 0 : status.subtitles) || []).map(normalizeSubtitleTrack).filter((track) => track.id);
|
|
6668
|
+
setTracks(nextTracks);
|
|
6669
|
+
} catch (error) {
|
|
6670
|
+
reportError(error, "subtitles", videoId);
|
|
6671
|
+
}
|
|
6672
|
+
}
|
|
6320
6673
|
}
|
|
6321
6674
|
},
|
|
6322
|
-
[videoId,
|
|
6675
|
+
[videoId, apiOptions, reportError, statusSubtitles]
|
|
6323
6676
|
);
|
|
6324
6677
|
useEffect(() => {
|
|
6325
6678
|
if (!videoId) {
|
|
@@ -6333,18 +6686,44 @@ function ManualSubtitlesPanel({
|
|
|
6333
6686
|
useEffect(() => {
|
|
6334
6687
|
onTracksChange == null ? void 0 : onTracksChange(tracks);
|
|
6335
6688
|
}, [tracks, onTracksChange]);
|
|
6689
|
+
useEffect(() => {
|
|
6690
|
+
onSave == null ? void 0 : onSave(async () => {
|
|
6691
|
+
if (!videoId || !autoTrack) return;
|
|
6692
|
+
if (autoSubtitlesDisabled !== initialAutoDisabledRef.current) {
|
|
6693
|
+
await patchTrackDisabled(videoId, autoTrack.id, autoSubtitlesDisabled, apiOptions);
|
|
6694
|
+
initialAutoDisabledRef.current = autoSubtitlesDisabled;
|
|
6695
|
+
}
|
|
6696
|
+
});
|
|
6697
|
+
}, [onSave, videoId, autoTrack, autoSubtitlesDisabled, apiOptions]);
|
|
6698
|
+
const manualTracks = useMemo(() => {
|
|
6699
|
+
const manual = tracks.filter((track) => !isAutoGeneratedTrack(track));
|
|
6700
|
+
const byLang = /* @__PURE__ */ new Map();
|
|
6701
|
+
for (const track of manual) {
|
|
6702
|
+
byLang.set(track.lang, track);
|
|
6703
|
+
}
|
|
6704
|
+
return Array.from(byLang.values());
|
|
6705
|
+
}, [tracks]);
|
|
6706
|
+
const usedLanguages = useMemo(() => {
|
|
6707
|
+
const langs = new Set(manualTracks.map((t2) => t2.lang));
|
|
6708
|
+
drafts.forEach((d) => {
|
|
6709
|
+
if (d.lang) langs.add(d.lang);
|
|
6710
|
+
});
|
|
6711
|
+
return langs;
|
|
6712
|
+
}, [manualTracks, drafts]);
|
|
6336
6713
|
const handleAddDraft = useCallback(() => {
|
|
6337
|
-
|
|
6714
|
+
var _a;
|
|
6715
|
+
const availableLang = ((_a = LANGUAGE_OPTIONS.find((opt) => !usedLanguages.has(opt.value))) == null ? void 0 : _a.value) ?? (usedLanguages.has(DEFAULT_LANG) ? null : DEFAULT_LANG);
|
|
6716
|
+
const label = availableLang ? LANGUAGE_LABELS[availableLang] || availableLang : null;
|
|
6338
6717
|
setDrafts((prev) => [
|
|
6339
6718
|
...prev,
|
|
6340
6719
|
{
|
|
6341
6720
|
id: crypto.randomUUID(),
|
|
6342
|
-
lang:
|
|
6343
|
-
label
|
|
6721
|
+
lang: availableLang,
|
|
6722
|
+
label,
|
|
6344
6723
|
status: "draft"
|
|
6345
6724
|
}
|
|
6346
6725
|
]);
|
|
6347
|
-
}, []);
|
|
6726
|
+
}, [usedLanguages]);
|
|
6348
6727
|
const updateDraft = useCallback(
|
|
6349
6728
|
(id, updates) => {
|
|
6350
6729
|
setDrafts(
|
|
@@ -6388,12 +6767,13 @@ function ManualSubtitlesPanel({
|
|
|
6388
6767
|
} else {
|
|
6389
6768
|
await refreshTracks(true);
|
|
6390
6769
|
}
|
|
6391
|
-
} catch {
|
|
6770
|
+
} catch (error) {
|
|
6392
6771
|
uploadControllersRef.current.delete(draftId);
|
|
6393
6772
|
updateDraft(draftId, { status: "error" });
|
|
6773
|
+
reportError(error, "subtitles", videoId);
|
|
6394
6774
|
}
|
|
6395
6775
|
},
|
|
6396
|
-
[videoId, drafts, apiOptions, refreshTracks, updateDraft]
|
|
6776
|
+
[videoId, drafts, apiOptions, refreshTracks, updateDraft, reportError]
|
|
6397
6777
|
);
|
|
6398
6778
|
const handleDelete = useCallback(
|
|
6399
6779
|
async (trackId) => {
|
|
@@ -6403,96 +6783,38 @@ function ManualSubtitlesPanel({
|
|
|
6403
6783
|
},
|
|
6404
6784
|
[videoId, apiOptions]
|
|
6405
6785
|
);
|
|
6406
|
-
useEffect(() => {
|
|
6407
|
-
if (mode !== "native" || !nativeVideo) return;
|
|
6408
|
-
const existingTracks = nativeVideo.querySelectorAll(
|
|
6409
|
-
'track[data-subtitle-track="managed"]'
|
|
6410
|
-
);
|
|
6411
|
-
existingTracks.forEach((node) => node.remove());
|
|
6412
|
-
tracks.forEach((track) => {
|
|
6413
|
-
const element = document.createElement("track");
|
|
6414
|
-
element.setAttribute("data-subtitle-track", "managed");
|
|
6415
|
-
element.kind = track.kind || "subtitles";
|
|
6416
|
-
element.label = track.label;
|
|
6417
|
-
element.srclang = track.lang;
|
|
6418
|
-
element.src = resolveSubtitleUrl(videoId || "", track, apiOptions);
|
|
6419
|
-
element.default = track.isDefault;
|
|
6420
|
-
nativeVideo.appendChild(element);
|
|
6421
|
-
});
|
|
6422
|
-
}, [mode, nativeVideo, tracks, videoId, apiOptions]);
|
|
6423
|
-
useEffect(() => {
|
|
6424
|
-
var _a;
|
|
6425
|
-
if (mode !== "shaka" || !shakaPlayer) return;
|
|
6426
|
-
const engine = (_a = shakaPlayer.getNetworkingEngine) == null ? void 0 : _a.call(shakaPlayer);
|
|
6427
|
-
if (engine && !shakaFilterRegisteredRef.current) {
|
|
6428
|
-
engine.registerRequestFilter((type, request) => {
|
|
6429
|
-
request.allowCrossSiteCredentials = true;
|
|
6430
|
-
if (authToken) {
|
|
6431
|
-
request.headers = request.headers || {};
|
|
6432
|
-
request.headers.Authorization = `Bearer ${authToken}`;
|
|
6433
|
-
}
|
|
6434
|
-
});
|
|
6435
|
-
shakaFilterRegisteredRef.current = true;
|
|
6436
|
-
}
|
|
6437
|
-
if (shakaPlayer.getTextTracks && shakaPlayer.removeTextTrack) {
|
|
6438
|
-
const existing = shakaPlayer.getTextTracks();
|
|
6439
|
-
existing.forEach((track) => {
|
|
6440
|
-
if (shakaAddedIdsRef.current.includes(track.id)) {
|
|
6441
|
-
shakaPlayer.removeTextTrack(track);
|
|
6442
|
-
}
|
|
6443
|
-
});
|
|
6444
|
-
shakaAddedIdsRef.current = [];
|
|
6445
|
-
}
|
|
6446
|
-
const addTracks = async () => {
|
|
6447
|
-
var _a2, _b, _c;
|
|
6448
|
-
const existingTracks = ((_a2 = shakaPlayer.getTextTracks) == null ? void 0 : _a2.call(shakaPlayer)) || [];
|
|
6449
|
-
for (const track of tracks) {
|
|
6450
|
-
const vttUrl = resolveSubtitleUrl(videoId || "", track, apiOptions);
|
|
6451
|
-
const label = track.label || track.lang || "und";
|
|
6452
|
-
const lang = track.lang || "und";
|
|
6453
|
-
const kind = track.kind || "subtitles";
|
|
6454
|
-
const alreadyExists = existingTracks.some(
|
|
6455
|
-
(item) => (item.language || "") === lang && (item.label || "") === label && (item.kind || "subtitles") === kind
|
|
6456
|
-
);
|
|
6457
|
-
if (alreadyExists) {
|
|
6458
|
-
continue;
|
|
6459
|
-
}
|
|
6460
|
-
let added = null;
|
|
6461
|
-
if (typeof shakaPlayer.addTextTrackAsync === "function") {
|
|
6462
|
-
added = await shakaPlayer.addTextTrackAsync(
|
|
6463
|
-
vttUrl,
|
|
6464
|
-
lang,
|
|
6465
|
-
kind,
|
|
6466
|
-
label
|
|
6467
|
-
);
|
|
6468
|
-
} else if (typeof shakaPlayer.addTextTrack === "function") {
|
|
6469
|
-
added = shakaPlayer.addTextTrack(vttUrl, lang, kind, label);
|
|
6470
|
-
}
|
|
6471
|
-
if (added && typeof added.id === "number") {
|
|
6472
|
-
shakaAddedIdsRef.current.push(added.id);
|
|
6473
|
-
existingTracks.push(added);
|
|
6474
|
-
}
|
|
6475
|
-
}
|
|
6476
|
-
const defaultTrack = tracks.find((item) => item.isDefault);
|
|
6477
|
-
if (defaultTrack && shakaPlayer.selectTextTrack) {
|
|
6478
|
-
const list = ((_b = shakaPlayer.getTextTracks) == null ? void 0 : _b.call(shakaPlayer)) || [];
|
|
6479
|
-
const target = list.find(
|
|
6480
|
-
(item) => item.language === defaultTrack.lang && item.label === defaultTrack.label
|
|
6481
|
-
);
|
|
6482
|
-
if (target) {
|
|
6483
|
-
(_c = shakaPlayer.setTextTrackVisibility) == null ? void 0 : _c.call(shakaPlayer, true);
|
|
6484
|
-
shakaPlayer.selectTextTrack(target);
|
|
6485
|
-
}
|
|
6486
|
-
}
|
|
6487
|
-
};
|
|
6488
|
-
addTracks().catch(() => {
|
|
6489
|
-
});
|
|
6490
|
-
}, [mode, shakaPlayer, tracks, videoId, apiOptions, authToken]);
|
|
6491
6786
|
const handleRemoveDraft = useCallback((draftId) => {
|
|
6492
6787
|
setDrafts((prev) => prev.filter((item) => item.id !== draftId));
|
|
6493
6788
|
}, []);
|
|
6494
6789
|
return /* @__PURE__ */ jsxs("div", { className: "video-settings-modal-subtitles", children: [
|
|
6495
|
-
|
|
6790
|
+
autoTrack && /* @__PURE__ */ jsxs(
|
|
6791
|
+
Flex,
|
|
6792
|
+
{
|
|
6793
|
+
align: "center",
|
|
6794
|
+
gap: "md",
|
|
6795
|
+
className: "video-settings-modal-subtitles-auto-toggle",
|
|
6796
|
+
children: [
|
|
6797
|
+
/* @__PURE__ */ jsx(
|
|
6798
|
+
Text,
|
|
6799
|
+
{
|
|
6800
|
+
size: "sm",
|
|
6801
|
+
fw: 500,
|
|
6802
|
+
c: "var(--mantine-color-bright)",
|
|
6803
|
+
flex: 1,
|
|
6804
|
+
children: t("editor.video.settingsModal.hideAutoSubtitles")
|
|
6805
|
+
}
|
|
6806
|
+
),
|
|
6807
|
+
/* @__PURE__ */ jsx(
|
|
6808
|
+
Switch,
|
|
6809
|
+
{
|
|
6810
|
+
size: "sm",
|
|
6811
|
+
checked: autoSubtitlesDisabled,
|
|
6812
|
+
onChange: (event) => handleAutoSubtitlesToggle(event.currentTarget.checked)
|
|
6813
|
+
}
|
|
6814
|
+
)
|
|
6815
|
+
]
|
|
6816
|
+
}
|
|
6817
|
+
),
|
|
6496
6818
|
/* @__PURE__ */ jsx(
|
|
6497
6819
|
Text,
|
|
6498
6820
|
{
|
|
@@ -6502,7 +6824,7 @@ function ManualSubtitlesPanel({
|
|
|
6502
6824
|
children: t("editor.video.settingsModal.uploadSubtitlesManually")
|
|
6503
6825
|
}
|
|
6504
6826
|
),
|
|
6505
|
-
|
|
6827
|
+
manualTracks.map((track) => /* @__PURE__ */ jsxs(
|
|
6506
6828
|
Flex,
|
|
6507
6829
|
{
|
|
6508
6830
|
align: "center",
|
|
@@ -6555,6 +6877,7 @@ function ManualSubtitlesPanel({
|
|
|
6555
6877
|
DraftRow,
|
|
6556
6878
|
{
|
|
6557
6879
|
draft,
|
|
6880
|
+
usedLanguages,
|
|
6558
6881
|
onChange: (updates) => updateDraft(draft.id, updates),
|
|
6559
6882
|
onUpload: (file) => handleUploadDraft(draft.id, file),
|
|
6560
6883
|
onRemove: () => handleRemoveDraft(draft.id),
|
|
@@ -6574,9 +6897,15 @@ function ManualSubtitlesPanel({
|
|
|
6574
6897
|
)
|
|
6575
6898
|
] });
|
|
6576
6899
|
}
|
|
6577
|
-
function DraftRow({ draft, onChange, onUpload, onRemove, SelectComponent }) {
|
|
6900
|
+
function DraftRow({ draft, usedLanguages, onChange, onUpload, onRemove, SelectComponent }) {
|
|
6578
6901
|
const { t } = useTranslation();
|
|
6579
6902
|
const inputRef = useRef(null);
|
|
6903
|
+
const availableLanguages = useMemo(
|
|
6904
|
+
() => LANGUAGE_OPTIONS.filter(
|
|
6905
|
+
(opt) => opt.value === draft.lang || !usedLanguages.has(opt.value)
|
|
6906
|
+
),
|
|
6907
|
+
[usedLanguages, draft.lang]
|
|
6908
|
+
);
|
|
6580
6909
|
const handleFileChange = (e) => {
|
|
6581
6910
|
var _a;
|
|
6582
6911
|
const file = (_a = e.target.files) == null ? void 0 : _a[0];
|
|
@@ -6588,7 +6917,6 @@ function DraftRow({ draft, onChange, onUpload, onRemove, SelectComponent }) {
|
|
|
6588
6917
|
Flex,
|
|
6589
6918
|
{
|
|
6590
6919
|
align: "center",
|
|
6591
|
-
gap: "md",
|
|
6592
6920
|
wrap: "nowrap",
|
|
6593
6921
|
className: "video-settings-modal-subtitles-row",
|
|
6594
6922
|
children: [
|
|
@@ -6597,7 +6925,7 @@ function DraftRow({ draft, onChange, onUpload, onRemove, SelectComponent }) {
|
|
|
6597
6925
|
{
|
|
6598
6926
|
size: "sm",
|
|
6599
6927
|
placeholder: t("editor.video.settingsModal.selectLanguage"),
|
|
6600
|
-
data:
|
|
6928
|
+
data: availableLanguages,
|
|
6601
6929
|
value: draft.lang,
|
|
6602
6930
|
onChange: (value) => onChange({
|
|
6603
6931
|
lang: value,
|
|
@@ -6610,7 +6938,7 @@ function DraftRow({ draft, onChange, onUpload, onRemove, SelectComponent }) {
|
|
|
6610
6938
|
inputClassName: "video-settings-modal-language-select"
|
|
6611
6939
|
}
|
|
6612
6940
|
),
|
|
6613
|
-
/* @__PURE__ */ jsx(Flex, { align: "center", gap: 6, flex: 1, children: draft.status === "uploading" ? /* @__PURE__ */ jsx(Loader, { size: 22
|
|
6941
|
+
/* @__PURE__ */ jsx(Flex, { align: "center", gap: 6, flex: 1, children: draft.status === "uploading" ? /* @__PURE__ */ jsx(Loader, { size: 22 }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6614
6942
|
/* @__PURE__ */ jsx(
|
|
6615
6943
|
"input",
|
|
6616
6944
|
{
|
|
@@ -6668,6 +6996,7 @@ function VideoSettingsModal({
|
|
|
6668
6996
|
chaptersDisabled = false,
|
|
6669
6997
|
shakaPlayer,
|
|
6670
6998
|
nativeVideo,
|
|
6999
|
+
videoDuration,
|
|
6671
7000
|
playerMode = "native"
|
|
6672
7001
|
}) {
|
|
6673
7002
|
const { t } = useTranslation();
|
|
@@ -6682,17 +7011,24 @@ function VideoSettingsModal({
|
|
|
6682
7011
|
const saveChaptersRef = useRef(
|
|
6683
7012
|
null
|
|
6684
7013
|
);
|
|
7014
|
+
const saveSubtitlesRef = useRef(null);
|
|
7015
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
6685
7016
|
const handlePosterSelect = useCallback((url) => {
|
|
7017
|
+
console.log("[Cover] handlePosterSelect:", url);
|
|
6686
7018
|
setSelectedPosterUrl(url);
|
|
6687
7019
|
}, []);
|
|
6688
7020
|
useEffect(() => {
|
|
6689
|
-
if (
|
|
6690
|
-
setStatus(
|
|
6691
|
-
return;
|
|
7021
|
+
if (initialStatus) {
|
|
7022
|
+
setStatus(initialStatus);
|
|
6692
7023
|
}
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
7024
|
+
}, [initialStatus]);
|
|
7025
|
+
useEffect(() => {
|
|
7026
|
+
if (!opened || !videoId) return;
|
|
7027
|
+
fetchStatus(videoId).then((freshStatus) => {
|
|
7028
|
+
if (freshStatus) {
|
|
7029
|
+
setStatus(freshStatus);
|
|
7030
|
+
onStatusChange == null ? void 0 : onStatusChange(freshStatus);
|
|
7031
|
+
}
|
|
6696
7032
|
}).catch(() => {
|
|
6697
7033
|
});
|
|
6698
7034
|
}, [opened, videoId]);
|
|
@@ -6708,28 +7044,47 @@ function VideoSettingsModal({
|
|
|
6708
7044
|
[videoId]
|
|
6709
7045
|
);
|
|
6710
7046
|
const handleSave = useCallback(async () => {
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
if (
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
7047
|
+
setIsSaving(true);
|
|
7048
|
+
try {
|
|
7049
|
+
if (saveChaptersRef.current) {
|
|
7050
|
+
const payload = await saveChaptersRef.current();
|
|
7051
|
+
if (Array.isArray(payload)) {
|
|
7052
|
+
onChaptersSaved == null ? void 0 : onChaptersSaved(payload);
|
|
7053
|
+
}
|
|
7054
|
+
}
|
|
7055
|
+
onChaptersDisabledChange == null ? void 0 : onChaptersDisabledChange(chaptersDisabledValue);
|
|
7056
|
+
if (saveSubtitlesRef.current) {
|
|
7057
|
+
await saveSubtitlesRef.current();
|
|
7058
|
+
}
|
|
7059
|
+
onSubtitlesSaved == null ? void 0 : onSubtitlesSaved(subtitlesSnapshot);
|
|
7060
|
+
console.log("[Cover] handleSave: selectedPosterUrl=", selectedPosterUrl, "onPosterChange=", !!onPosterChange);
|
|
7061
|
+
if (selectedPosterUrl !== null) {
|
|
7062
|
+
console.log("[Cover] calling onPosterChange with:", selectedPosterUrl);
|
|
7063
|
+
onPosterChange == null ? void 0 : onPosterChange(selectedPosterUrl);
|
|
7064
|
+
} else {
|
|
7065
|
+
console.log("[Cover] selectedPosterUrl is null, skipping onPosterChange");
|
|
7066
|
+
}
|
|
7067
|
+
if (videoId) {
|
|
7068
|
+
const updatedStatus = await fetchStatus(videoId);
|
|
7069
|
+
onStatusChange == null ? void 0 : onStatusChange(updatedStatus);
|
|
7070
|
+
}
|
|
7071
|
+
onClose();
|
|
7072
|
+
} finally {
|
|
7073
|
+
setIsSaving(false);
|
|
6720
7074
|
}
|
|
6721
|
-
onClose();
|
|
6722
7075
|
}, [
|
|
7076
|
+
videoId,
|
|
6723
7077
|
chaptersDisabledValue,
|
|
6724
7078
|
onChaptersDisabledChange,
|
|
6725
7079
|
onChaptersSaved,
|
|
6726
7080
|
onClose,
|
|
6727
7081
|
onPosterChange,
|
|
7082
|
+
onStatusChange,
|
|
6728
7083
|
onSubtitlesSaved,
|
|
6729
7084
|
selectedPosterUrl,
|
|
6730
7085
|
subtitlesSnapshot
|
|
6731
7086
|
]);
|
|
6732
|
-
return /* @__PURE__ */
|
|
7087
|
+
return /* @__PURE__ */ jsxs(
|
|
6733
7088
|
Modal,
|
|
6734
7089
|
{
|
|
6735
7090
|
opened,
|
|
@@ -6740,13 +7095,14 @@ function VideoSettingsModal({
|
|
|
6740
7095
|
withinPortal: true,
|
|
6741
7096
|
zIndex: 300,
|
|
6742
7097
|
className: "video-settings-modal",
|
|
6743
|
-
children:
|
|
6744
|
-
/* @__PURE__ */ jsxs(Stack, { gap: 32, children: [
|
|
7098
|
+
children: [
|
|
7099
|
+
/* @__PURE__ */ jsx(Box, { className: "video-settings-modal-body", children: /* @__PURE__ */ jsxs(Stack, { gap: 32, children: [
|
|
6745
7100
|
/* @__PURE__ */ jsx(
|
|
6746
7101
|
CoverSection,
|
|
6747
7102
|
{
|
|
6748
7103
|
opened,
|
|
6749
7104
|
videoId,
|
|
7105
|
+
initialStatus: status,
|
|
6750
7106
|
title: t("editor.video.settingsModal.cover"),
|
|
6751
7107
|
uploadLabel: t("editor.video.settingsModal.uploadFile"),
|
|
6752
7108
|
cropLabel: t("editor.video.settingsModal.cropCoverImage"),
|
|
@@ -6785,7 +7141,10 @@ function VideoSettingsModal({
|
|
|
6785
7141
|
shakaPlayer,
|
|
6786
7142
|
nativeVideo,
|
|
6787
7143
|
statusSubtitles: status == null ? void 0 : status.subtitles,
|
|
6788
|
-
onTracksChange: setSubtitlesSnapshot
|
|
7144
|
+
onTracksChange: setSubtitlesSnapshot,
|
|
7145
|
+
onSave: (handler) => {
|
|
7146
|
+
saveSubtitlesRef.current = handler;
|
|
7147
|
+
}
|
|
6789
7148
|
}
|
|
6790
7149
|
)
|
|
6791
7150
|
] }),
|
|
@@ -6795,6 +7154,8 @@ function VideoSettingsModal({
|
|
|
6795
7154
|
videoId,
|
|
6796
7155
|
initialChapters: status == null ? void 0 : status.chapters,
|
|
6797
7156
|
nativeVideo,
|
|
7157
|
+
shakaPlayer,
|
|
7158
|
+
videoDuration,
|
|
6798
7159
|
title: t("editor.video.settingsModal.chapters"),
|
|
6799
7160
|
customLabel: t("editor.video.settingsModal.chaptersCustom"),
|
|
6800
7161
|
dontShowLabel: t("editor.video.settingsModal.chaptersDontShow"),
|
|
@@ -6824,17 +7185,18 @@ function VideoSettingsModal({
|
|
|
6824
7185
|
)
|
|
6825
7186
|
}
|
|
6826
7187
|
)
|
|
6827
|
-
] }),
|
|
6828
|
-
/* @__PURE__ */ jsx(
|
|
7188
|
+
] }) }),
|
|
7189
|
+
/* @__PURE__ */ jsx(Box, { className: "video-settings-modal-footer", children: /* @__PURE__ */ jsx(
|
|
6829
7190
|
FooterActions,
|
|
6830
7191
|
{
|
|
6831
7192
|
cancelLabel: t("editor.video.settingsModal.cancel"),
|
|
6832
7193
|
saveLabel: t("editor.video.settingsModal.save"),
|
|
6833
7194
|
onCancel: onClose,
|
|
6834
|
-
onSave: handleSave
|
|
7195
|
+
onSave: handleSave,
|
|
7196
|
+
isSaving
|
|
6835
7197
|
}
|
|
6836
|
-
)
|
|
6837
|
-
]
|
|
7198
|
+
) })
|
|
7199
|
+
]
|
|
6838
7200
|
}
|
|
6839
7201
|
);
|
|
6840
7202
|
}
|
|
@@ -6875,7 +7237,8 @@ function VideoBlock({
|
|
|
6875
7237
|
shakaPlayer: null,
|
|
6876
7238
|
nativeVideo: null,
|
|
6877
7239
|
mode: "native",
|
|
6878
|
-
containerEl: null
|
|
7240
|
+
containerEl: null,
|
|
7241
|
+
videoDuration: null
|
|
6879
7242
|
});
|
|
6880
7243
|
const fallbackNativeVideoUrl = useMemo(() => {
|
|
6881
7244
|
if (platform === "download" || platform === "link") {
|
|
@@ -6919,6 +7282,7 @@ function VideoBlock({
|
|
|
6919
7282
|
};
|
|
6920
7283
|
const handlePosterChange = useCallback(
|
|
6921
7284
|
(posterUrl2) => {
|
|
7285
|
+
console.log("[Cover] handlePosterChange: setting posterUrl=", posterUrl2);
|
|
6922
7286
|
setPosterUrl(posterUrl2);
|
|
6923
7287
|
setVideoPoster(editor, nodeKey, posterUrl2);
|
|
6924
7288
|
},
|
|
@@ -6947,7 +7311,7 @@ function VideoBlock({
|
|
|
6947
7311
|
});
|
|
6948
7312
|
}, [editor, nodeKey]);
|
|
6949
7313
|
const resolvedChaptersOverride = chaptersDisabled ? [] : chaptersOverride;
|
|
6950
|
-
const TrashIcon = /* @__PURE__ */ jsx(
|
|
7314
|
+
const TrashIcon = /* @__PURE__ */ jsx("span", { style: { color: "var(--mantine-color-red-outline)", display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx(HugeiconsIcon, { icon: tI, size: 16 }) });
|
|
6951
7315
|
const uploadComponentProps = {
|
|
6952
7316
|
className,
|
|
6953
7317
|
format,
|
|
@@ -6986,7 +7350,7 @@ function VideoBlock({
|
|
|
6986
7350
|
event.stopPropagation();
|
|
6987
7351
|
setSettingsModalOpened(true);
|
|
6988
7352
|
},
|
|
6989
|
-
children: /* @__PURE__ */ jsx(SettingsIcon, { size: 16 })
|
|
7353
|
+
children: /* @__PURE__ */ jsx("span", { style: { color: "var(--mantine-color-gray-text)", display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx(SettingsIcon, { size: 16 }) })
|
|
6990
7354
|
}
|
|
6991
7355
|
) }),
|
|
6992
7356
|
/* @__PURE__ */ jsx(
|
|
@@ -7005,7 +7369,8 @@ function VideoBlock({
|
|
|
7005
7369
|
onChaptersDisabledChange: handleChaptersDisabledChange,
|
|
7006
7370
|
shakaPlayer: playerInfo.shakaPlayer,
|
|
7007
7371
|
nativeVideo: playerInfo.nativeVideo,
|
|
7008
|
-
playerMode: playerInfo.mode
|
|
7372
|
+
playerMode: playerInfo.mode,
|
|
7373
|
+
videoDuration: playerInfo.videoDuration
|
|
7009
7374
|
}
|
|
7010
7375
|
)
|
|
7011
7376
|
] }),
|