feat: use customer video key for demo access
This commit is contained in:
@@ -14,6 +14,7 @@ const LEGACY_GROUP_KEY = "yingxiangli.selectedAssetGroup";
|
||||
const SCENE_ROLES_KEY = "yingxiangli.sceneRoles.v1";
|
||||
const CHARACTER_LIBRARY_KEY = "yingxiangli.characterLibrary.v1";
|
||||
const DRAMA_PROJECT_KEY = "yingxiangli.dramaProject.v1";
|
||||
const VIDEO_KEY_STORAGE = "yingxiangli.videoKey.v1";
|
||||
const AUDIT_CLIENT_KEY = "yingxiangli.auditClientId.v1";
|
||||
const AUDIT_SESSION_KEY = "yingxiangli.auditSessionId.v1";
|
||||
const VIEW_LABELS = ["正面", "侧面", "背面"];
|
||||
@@ -53,6 +54,7 @@ const el = Object.fromEntries([
|
||||
"referenceVideoInputs", "addExtraImageButton", "addReferenceVideoButton", "inputQuota",
|
||||
"addShotButton", "shotList", "timelineSummary", "shotProgressBar", "continuityMode", "versionCount",
|
||||
"versionList", "assemblyPanel", "assemblyList", "assemblyHint", "closeAssemblyButton",
|
||||
"videoKeyGate", "videoKeyForm", "videoKeyInput", "videoKeyError", "changeVideoKeyButton", "cancelVideoKeyButton",
|
||||
].map((id) => [id, document.getElementById(id)]));
|
||||
|
||||
const reviewRows = [el.reviewFront, el.reviewSide, el.reviewBack];
|
||||
@@ -63,6 +65,7 @@ const auditInputTimers = new Map();
|
||||
let auditFlushTimer = null;
|
||||
|
||||
setupClientAudit();
|
||||
setupVideoKeyGate();
|
||||
init();
|
||||
|
||||
document.querySelectorAll(".module-tab").forEach((button) => {
|
||||
@@ -76,12 +79,13 @@ async function init() {
|
||||
restoreCharacterLibrary();
|
||||
restoreDramaProject();
|
||||
renderConnection();
|
||||
if (!state.config.mockMode && !currentVideoKey()) showVideoKeyGate(false);
|
||||
await loadAssets();
|
||||
loadActiveShotIntoForm();
|
||||
renderScene();
|
||||
renderStoryboard();
|
||||
resumePendingReview();
|
||||
resumePendingVideo();
|
||||
if (state.config.mockMode || currentVideoKey()) resumePendingVideo();
|
||||
} catch (error) {
|
||||
showToast(error.message, true);
|
||||
el.connectionState.textContent = "本地服务连接失败";
|
||||
@@ -89,6 +93,42 @@ async function init() {
|
||||
}
|
||||
}
|
||||
|
||||
function setupVideoKeyGate() {
|
||||
el.videoKeyForm.addEventListener("submit", (event) => {
|
||||
event.preventDefault();
|
||||
const key = el.videoKeyInput.value.trim();
|
||||
if (!/^sk-[A-Za-z0-9_-]{8,}$/.test(key)) {
|
||||
el.videoKeyError.textContent = "请输入正确格式的生视频 Key(sk-...)";
|
||||
el.videoKeyError.classList.remove("hidden");
|
||||
return;
|
||||
}
|
||||
localStorage.setItem(VIDEO_KEY_STORAGE, key);
|
||||
logClientOperation("video_key.saved", { keySuffix: key.slice(-4), keyLength: key.length });
|
||||
location.reload();
|
||||
});
|
||||
el.changeVideoKeyButton.addEventListener("click", () => showVideoKeyGate(true));
|
||||
el.cancelVideoKeyButton.addEventListener("click", hideVideoKeyGate);
|
||||
}
|
||||
|
||||
function currentVideoKey() {
|
||||
try { return localStorage.getItem(VIDEO_KEY_STORAGE)?.trim() || ""; }
|
||||
catch { return ""; }
|
||||
}
|
||||
|
||||
function showVideoKeyGate(canCancel = Boolean(currentVideoKey())) {
|
||||
el.videoKeyInput.value = "";
|
||||
el.videoKeyError.textContent = "";
|
||||
el.videoKeyError.classList.add("hidden");
|
||||
el.cancelVideoKeyButton.classList.toggle("hidden", !canCancel);
|
||||
el.videoKeyGate.classList.remove("hidden");
|
||||
requestAnimationFrame(() => el.videoKeyInput.focus());
|
||||
}
|
||||
|
||||
function hideVideoKeyGate() {
|
||||
if (!currentVideoKey() && !state.config?.mockMode) return;
|
||||
el.videoKeyGate.classList.add("hidden");
|
||||
}
|
||||
|
||||
el.addRoleButton.addEventListener("click", () => {
|
||||
if (state.roles.length >= 3) return showToast("当前 Demo 最多支持三个场景角色,人物图片总数不超过 9 张", true);
|
||||
const role = createRole(`角色 ${ROLE_LETTERS[state.roles.length]}`);
|
||||
@@ -1403,6 +1443,9 @@ async function resumePendingVideo() {
|
||||
}
|
||||
|
||||
function renderConnection() {
|
||||
const videoKey = currentVideoKey();
|
||||
el.changeVideoKeyButton.classList.toggle("hidden", state.config.mockMode || !videoKey);
|
||||
if (videoKey) el.changeVideoKeyButton.textContent = `更换视频 Key · ${videoKey.slice(-4)}`;
|
||||
if (state.config.mockMode) {
|
||||
el.connectionState.textContent = "Mock 模式 · 可评审完整流程";
|
||||
el.connectionState.className = "connection mock";
|
||||
@@ -1414,7 +1457,7 @@ function renderConnection() {
|
||||
el.reviewButton.textContent = "提交真实素材审核";
|
||||
el.mockHint.classList.add("hidden");
|
||||
} else if (state.config.assetReady) {
|
||||
el.connectionState.textContent = "素材审核 Live · 视频 Key 待配置";
|
||||
el.connectionState.textContent = "请输入视频 Key";
|
||||
el.connectionState.className = "connection live";
|
||||
el.reviewButton.textContent = "提交真实素材审核";
|
||||
el.mockHint.classList.add("hidden");
|
||||
@@ -1663,11 +1706,15 @@ async function api(path, options = {}) {
|
||||
const startedAt = Date.now();
|
||||
const method = options.method || "GET";
|
||||
const shouldLog = shouldLogClientApi(path);
|
||||
const videoKey = currentVideoKey();
|
||||
const videoHeaders = videoKey && (/^\/api\/videos(?:\/|$)/.test(path) || path === "/api/config")
|
||||
? { "X-Video-Key": videoKey }
|
||||
: {};
|
||||
if (shouldLog) logClientOperation("api.request", { method, path });
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}${path}`, {
|
||||
...options,
|
||||
headers: { "Content-Type": "application/json", "X-Session-Id": auditSessionId, ...(options.headers || {}) },
|
||||
headers: { "Content-Type": "application/json", "X-Session-Id": auditSessionId, ...videoHeaders, ...(options.headers || {}) },
|
||||
});
|
||||
const result = await response.json().catch(() => ({ error: "服务返回了无法解析的内容" }));
|
||||
if (shouldLog || !response.ok) {
|
||||
@@ -1683,6 +1730,10 @@ async function api(path, options = {}) {
|
||||
const error = new Error(extractApiError(result) || `请求失败(${response.status})`);
|
||||
error.payload = result;
|
||||
error.status = response.status;
|
||||
if (response.status === 401 && /^\/api\/videos(?:\/|$)/.test(path)) {
|
||||
localStorage.removeItem(VIDEO_KEY_STORAGE);
|
||||
showVideoKeyGate(false);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return result;
|
||||
|
||||
Reference in New Issue
Block a user