xvai-loi 0.1.113 → 0.1.115

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.
Files changed (3) hide show
  1. package/co.txt +1 -1
  2. package/package.json +1 -1
  3. package/xvai-loi.js +79 -336
package/co.txt CHANGED
@@ -1 +1 @@
1
- xvaiLoi130036
1
+ xvaiLoi130151
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xvai-loi",
3
- "version": "0.1.113",
3
+ "version": "0.1.115",
4
4
  "description": "Loi dung video (Edit Video Tu Dong) nap vao tool Google Flow qua o cam engine/oCam.ts",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
package/xvai-loi.js CHANGED
@@ -2564,6 +2564,70 @@ function measuredSilences(pcm, offset = 0, rate = 16e3) {
2564
2564
  }
2565
2565
  return out;
2566
2566
  }
2567
+ function nanMoc(ws, pcm, o, sil) {
2568
+ const H2 = 0.02, hop4 = 320, n = Math.floor(pcm.length / hop4), r = new Float32Array(n), M = new Float32Array(n), A2 = new Float32Array(n), L = 60, T = [], D = [];
2569
+ for (let i = 0; i < n; i++) {
2570
+ let e = 0;
2571
+ for (let j = i * hop4; j < (i + 1) * hop4; j++) e += pcm[j] * pcm[j];
2572
+ r[i] = Math.sqrt(e / hop4);
2573
+ }
2574
+ const x = Array.from(r).sort((a, b) => a - b), g = Math.max(2e-3, (x[Math.floor(n * 0.8)] || 0) * 0.2);
2575
+ for (let i = 0; i < n; i++) M[i] = ((i > 0 && r[i - 1] >= g ? 1 : 0) + (r[i] >= g ? 1 : 0) + (i < n - 1 && r[i + 1] >= g ? 1 : 0)) / 3;
2576
+ for (const w of ws) {
2577
+ const a = Math.floor((w.t0 - o) / H2);
2578
+ for (let i = a; i < Math.max(a + 1, Math.floor((w.t1 - o) / H2)); i++) if (i >= 0 && i < n) A2[i] = 1;
2579
+ }
2580
+ for (let c = 3; c <= n * H2 - 3 + 1.5; c += 1.5) {
2581
+ const a = Math.max(Math.floor((c - 3) / H2), L), b = Math.min(Math.floor((c + 3) / H2), n - L);
2582
+ if (b - a < 50) continue;
2583
+ let s = 0;
2584
+ for (let i = a; i < b; i++) s += A2[i];
2585
+ if (s < 10) continue;
2586
+ const m = s / (b - a);
2587
+ let bs = -1e9, bd = 0;
2588
+ for (let d = -L; d <= L; d++) {
2589
+ let v = 0;
2590
+ for (let i = a; i < b; i++) v += (A2[i] - m) * M[i + d];
2591
+ if (v > bs) {
2592
+ bs = v;
2593
+ bd = d;
2594
+ }
2595
+ }
2596
+ T.push(c + o);
2597
+ D.push(bd * H2);
2598
+ }
2599
+ if (!T.length) return ws;
2600
+ const S = D.map((_, i) => {
2601
+ const v = D.slice(Math.max(0, i - 1), i + 2).sort((p, q) => p - q), h = v.length >> 1;
2602
+ return v.length % 2 ? v[h] : (v[h - 1] + v[h]) / 2;
2603
+ });
2604
+ const f2 = (t) => {
2605
+ if (t <= T[0]) return t + S[0];
2606
+ if (t >= T[T.length - 1]) return t + S[S.length - 1];
2607
+ let k = 1;
2608
+ while (T[k] < t) k++;
2609
+ return t + S[k - 1] + (S[k] - S[k - 1]) * (t - T[k - 1]) / (T[k] - T[k - 1]);
2610
+ };
2611
+ const out = ws.map((w) => {
2612
+ const t0 = f2(w.t0);
2613
+ return { ...w, t0, t1: Math.max(f2(w.t1), t0 + 0.04) };
2614
+ });
2615
+ for (let i = 1; i < out.length; i++) if (out[i].t0 < out[i - 1].t0) out[i].t0 = out[i - 1].t0 + 0.01;
2616
+ for (const w of out) {
2617
+ const c = (w.t0 + w.t1) / 2, d = w.t1 - w.t0;
2618
+ for (const q of sil) if (q.t0 < c && c < q.t1) {
2619
+ if (c - q.t0 < q.t1 - c) {
2620
+ w.t1 = q.t0;
2621
+ w.t0 = Math.min(w.t0, q.t0 - Math.min(d, 0.3));
2622
+ } else {
2623
+ w.t0 = q.t1;
2624
+ w.t1 = Math.max(w.t1, q.t1 + Math.min(d, 0.3));
2625
+ }
2626
+ break;
2627
+ }
2628
+ }
2629
+ return out;
2630
+ }
2567
2631
 
2568
2632
  // flow-src/engine/montageVoice.ts
2569
2633
  function naturalVoiceRanges(pcm, rate = 16e3) {
@@ -3937,206 +4001,6 @@ var scoreFilm = (project) => {
3937
4001
  return { state, issues, passes, summary };
3938
4002
  };
3939
4003
 
3940
- // flow-src/components/AiEditPanel.tsx
3941
- var AiEditPanel = ({ project, currentScene, currentItem, onUpdate }) => {
3942
- const [request, setRequest] = useState("");
3943
- const [style, setStyle] = useState("vibrant");
3944
- const [loading, setLoading] = useState(false);
3945
- const [error, setError] = useState(null);
3946
- const [draft, setDraft] = useState(null);
3947
- const [undoState, setUndoState] = useState(null);
3948
- const analysisSceneId = useRef(null);
3949
- const analysisItemSnapshot = useRef(null);
3950
- const extractJson = (text2) => {
3951
- const startIdx = text2.indexOf("{");
3952
- const endIdx = text2.lastIndexOf("}");
3953
- if (startIdx === -1 || endIdx === -1) throw new Error("AI kh\xF4ng tr\u1EA3 v\u1EC1 \u0111\xFAng \u0111\u1ECBnh d\u1EA1ng JSON.");
3954
- const json2 = JSON.parse(text2.substring(startIdx, endIdx + 1));
3955
- if (!json2.moments || !Array.isArray(json2.moments)) throw new Error("D\u1EEF li\u1EC7u thi\u1EBFu danh s\xE1ch th\u1EBB ch\u1EEF (moments).");
3956
- if (!currentItem) throw new Error("Kh\xF4ng c\xF3 th\xF4ng tin \u0111o\u1EA1n d\u1EF1ng \u0111\u1EC3 \u0111\u1ED1i so\xE1t.");
3957
- const start = currentItem.trimStart;
3958
- const end = currentItem.trimEnd;
3959
- if (!Number.isFinite(start) || !Number.isFinite(end)) throw new Error("V\xF9ng c\u1EAFt ngu\u1ED3n kh\xF4ng h\u1EE3p l\u1EC7.");
3960
- if (json2.moments.length === 0) return { ...json2, moments: [] };
3961
- const cleanMoments = json2.moments.map((m, idx) => {
3962
- const label = `Th\u1EBB ${idx + 1}: `;
3963
- const parseVal = (v) => typeof v === "number" ? v : typeof v === "string" && v.trim() !== "" ? Number(v) : NaN;
3964
- const t0 = parseVal(m.t0);
3965
- const t1 = parseVal(m.t1);
3966
- const txt = String(m.text || "").trim();
3967
- if (!Number.isFinite(t0) || !Number.isFinite(t1)) {
3968
- throw new Error(label + "m\u1ED1c th\u1EDDi gian kh\xF4ng ph\u1EA3i l\xE0 s\u1ED1 h\u1EEFu h\u1EA1n.");
3969
- }
3970
- if (t0 < start) {
3971
- throw new Error(label + `b\u1EAFt \u0111\u1EA7u (${t0.toFixed(2)}s) tr\u01B0\u1EDBc v\xF9ng c\u1EAFt c\u1EE7a clip (${start.toFixed(2)}s).`);
3972
- }
3973
- if (t1 > end) {
3974
- throw new Error(label + `k\u1EBFt th\xFAc (${t1.toFixed(2)}s) sau v\xF9ng c\u1EAFt c\u1EE7a clip (${end.toFixed(2)}s).`);
3975
- }
3976
- if (t1 <= t0) {
3977
- throw new Error(label + "m\u1ED1c k\u1EBFt th\xFAc kh\xF4ng l\u1EDBn h\u01A1n m\u1ED1c b\u1EAFt \u0111\u1EA7u.");
3978
- }
3979
- if (t1 - t0 < 0.2) {
3980
- throw new Error(label + "th\u1EDDi gian hi\u1EC3n th\u1ECB qu\xE1 ng\u1EAFn (t\u1ED1i thi\u1EC3u 0.2s).");
3981
- }
3982
- if (!txt) {
3983
- throw new Error(label + "n\u1ED9i dung ch\u1EEF tr\u1ED1ng.");
3984
- }
3985
- return {
3986
- t0,
3987
- t1,
3988
- text: txt.slice(0, 80),
3989
- pos: ["top", "bottom"].includes(m.pos) ? m.pos : "bottom",
3990
- effect: "pop",
3991
- sfx: ["pop-nhe", "whoosh"].includes(m.sfx) ? m.sfx : "",
3992
- kind: ["keyword", "sub"].includes(m.kind) ? m.kind : "keyword"
3993
- };
3994
- });
3995
- for (let i = 0; i < cleanMoments.length; i++) {
3996
- for (let j = i + 1; j < cleanMoments.length; j++) {
3997
- if (cleanMoments[i].t0 === cleanMoments[j].t0 && cleanMoments[i].t1 === cleanMoments[j].t1) {
3998
- throw new Error("Ph\xE1t hi\u1EC7n c\xE1c th\u1EBB ch\u1EEF tr\xF9ng l\u1EB7p m\u1ED1c th\u1EDDi gian.");
3999
- }
4000
- }
4001
- }
4002
- return { ...json2, moments: cleanMoments.slice(0, 5) };
4003
- };
4004
- const handleAnalyze = async () => {
4005
- if (!currentScene?.videoBase64 || !currentItem) return;
4006
- setLoading(true);
4007
- setError(null);
4008
- setDraft(null);
4009
- analysisSceneId.current = currentScene.id;
4010
- analysisItemSnapshot.current = {
4011
- id: currentItem.id,
4012
- trimStart: currentItem.trimStart,
4013
- trimEnd: currentItem.trimEnd,
4014
- videoBase64: currentScene.videoBase64
4015
- };
4016
- try {
4017
- const sys = `B\u1EA1n l\xE0 chuy\xEAn gia d\u1EF1ng phim AI. H\xE3y xem v\xE0 nghe video \u0111\u1EC3 \u0111\u1EC1 xu\u1EA5t th\u1EBB ch\u1EEF highlight.
4018
- PH\u1EA0M VI NGU\u1ED2N: Ch\u1EC9 \u0111\u1EC1 xu\u1EA5t trong kho\u1EA3ng [${currentItem.trimStart.toFixed(2)}s - ${currentItem.trimEnd.toFixed(2)}s].
4019
- Transcript: Ch\xE9p \u0111\xFAng l\u1EDDi tho\u1EA1i ti\u1EBFng Vi\u1EC7t nghe \u0111\u01B0\u1EE3c; n\u1EBFu im l\u1EB7ng ho\u1EB7c kh\xF4ng r\xF5 th\xEC \u0111\u1EC3 transcript r\u1ED7ng.
4020
- AI tr\u1EA3 JSON numbers kh\xF4ng quote v\xE0 moments r\u1ED7ng khi kh\xF4ng bi\u1EBFt m\u1ED1c.
4021
- M\u1ED1c th\u1EDDi gian t0, t1: B\u1EAET BU\u1ED8C l\xE0 JSON number th\u1EADp ph\xE2n (v\xED d\u1EE5 1.25), kh\xF4ng \u0111\u1EB7t trong d\u1EA5u nh\xE1y. M\u1ECDi m\u1ED1c ph\u1EA3i n\u1EB1m trong kho\u1EA3ng t\u1EEB ${currentItem.trimStart.toFixed(2)} \u0111\u1EBFn ${currentItem.trimEnd.toFixed(2)}.
4022
- Tr\u01B0\u1EDDng h\u1EE3p \u0111\u1EB7c bi\u1EC7t: N\u1EBFu kh\xF4ng x\xE1c \u0111\u1ECBnh \u0111\u01B0\u1EE3c m\u1ED1c ph\xF9 h\u1EE3p ho\u1EB7c video kh\xF4ng c\xF3 n\u1ED9i dung \u0111\xE1ng nh\u1EA5n, h\xE3y tr\u1EA3 moments l\xE0 m\u1EA3ng r\u1ED7ng []. N\u1EBFu transcript r\u1ED7ng, summary ph\u1EA3i trung t\xEDnh, kh\xF4ng n\xEAu c\xF4ng d\u1EE5ng/t\xE1c d\u1EE5ng b\u1ECBa \u0111\u1EB7t. Kh\xF4ng d\xF9ng d\u1EEF li\u1EC7u m\u1EABu.
4023
- TR\u1EA2 JSON: {summary, transcript:[{t0, t1, text}], moments:[{t0, t1, text, kind, pos, effect, sfx}]}.
4024
- Th\u1EBB ch\u1EEF: T\u1ED1i \u0111a 5 th\u1EBB, m\u1ED7i th\u1EBB 2-6 t\u1EEB. Kind: keyword ho\u1EB7c sub.
4025
- Phong c\xE1ch: ${style === "concise" ? "Ch\u1EC9 gi\u1EEF \xFD \u0111\u1EAFt gi\xE1." : "Sinh \u0111\u1ED9ng theo n\u0103ng l\u01B0\u1EE3ng."}
4026
- Y\xEAu c\u1EA7u: ${request || "Kh\xF4ng c\xF3"}`;
4027
- const res = await Flow.generate.text("Ph\xE2n t\xEDch video v\xE0 tr\u1EA3 v\u1EC1 k\u1ECBch b\u1EA3n d\u1EF1ng highlight.", {
4028
- systemInstruction: sys,
4029
- videos: [{ base64: currentScene.videoBase64, mimeType: currentScene.videoMimeType || "video/mp4" }],
4030
- thinkingLevel: "medium"
4031
- });
4032
- const parsed = extractJson(res.text);
4033
- setDraft({ ...parsed, request });
4034
- } catch (e) {
4035
- setError(e.message || "L\u1ED7i ph\xE2n t\xEDch video.");
4036
- setDraft(null);
4037
- } finally {
4038
- setLoading(false);
4039
- }
4040
- };
4041
- const handleApply = () => {
4042
- if (!draft || !currentScene || !currentItem) return;
4043
- const snap = analysisItemSnapshot.current;
4044
- const isStale = analysisSceneId.current !== currentScene.id || !snap || snap.id !== currentItem.id || snap.trimStart !== currentItem.trimStart || snap.trimEnd !== currentItem.trimEnd || snap.videoBase64 !== currentScene.videoBase64;
4045
- if (isStale) {
4046
- setError("Clip ho\u1EB7c m\u1ED1c c\u1EAFt \u0111\xE3 thay \u0111\u1ED5i so v\u1EDBi l\xFAc ph\xE2n t\xEDch. Vui l\xF2ng ph\xE2n t\xEDch l\u1EA1i.");
4047
- return;
4048
- }
4049
- setUndoState(currentScene.text_moments || []);
4050
- onUpdate({
4051
- text_moments: draft.moments,
4052
- aiEditAnalysis: {
4053
- summary: draft.summary,
4054
- transcript: draft.transcript,
4055
- request: draft.request
4056
- }
4057
- });
4058
- setError(null);
4059
- };
4060
- const handleLoadExisting = () => {
4061
- if (!currentScene || !currentItem) return;
4062
- const sceneWithAnalysis = currentScene;
4063
- setDraft({
4064
- summary: sceneWithAnalysis.aiEditAnalysis?.summary || "D\u1EEF li\u1EC7u hi\u1EC7n c\xF3",
4065
- transcript: sceneWithAnalysis.aiEditAnalysis?.transcript || [],
4066
- moments: currentScene.text_moments || [],
4067
- request: sceneWithAnalysis.aiEditAnalysis?.request || ""
4068
- });
4069
- analysisSceneId.current = currentScene.id;
4070
- analysisItemSnapshot.current = {
4071
- id: currentItem.id,
4072
- trimStart: currentItem.trimStart,
4073
- trimEnd: currentItem.trimEnd,
4074
- videoBase64: currentScene.videoBase64 || ""
4075
- };
4076
- setError(null);
4077
- };
4078
- const handleUndo = () => {
4079
- if (undoState) {
4080
- onUpdate({ text_moments: undoState });
4081
- setUndoState(null);
4082
- }
4083
- };
4084
- return /* @__PURE__ */ shim_react_default.createElement("div", { className: "bg-[#142A47] border border-[#2C4A73] rounded-xl overflow-hidden shadow-2xl flex flex-col max-h-[500px]" }, /* @__PURE__ */ shim_react_default.createElement("div", { className: "px-5 py-3 border-b border-[#2C4A73] bg-[#0E2036]/50 flex items-center justify-between" }, /* @__PURE__ */ shim_react_default.createElement("div", { className: "flex items-center gap-2 text-[#F6D178]" }, /* @__PURE__ */ shim_react_default.createElement("span", { className: "material-symbols-outlined text-[20px]" }, "auto_videocam"), /* @__PURE__ */ shim_react_default.createElement("h3", { className: "text-[13px] font-black uppercase tracking-widest" }, "AI D\u1EF0NG VIDEO C\xD3 S\u1EB4N")), undoState && /* @__PURE__ */ shim_react_default.createElement("button", { onClick: handleUndo, className: "text-[10px] font-bold text-[#A2B2C9] hover:text-[#F6D178] uppercase underline decoration-dotted" }, "Ho\xE0n t\xE1c \xE1p d\u1EE5ng")), /* @__PURE__ */ shim_react_default.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-5 space-y-5" }, !currentScene?.videoBase64 ? /* @__PURE__ */ shim_react_default.createElement("div", { className: "py-10 text-center text-[#2C4A73] italic text-[12px] font-bold uppercase tracking-widest" }, "Ch\u1ECDn clip \u0111\xE3 c\xF3 video \u0111\u1EC3 b\u1EAFt \u0111\u1EA7u") : /* @__PURE__ */ shim_react_default.createElement(shim_react_default.Fragment, null, /* @__PURE__ */ shim_react_default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ shim_react_default.createElement("label", { className: "text-[10px] font-bold text-[#A2B2C9] uppercase tracking-widest ml-1" }, "Y\xEAu c\u1EA7u d\u1EF1ng ri\xEAng"), /* @__PURE__ */ shim_react_default.createElement(
4085
- "textarea",
4086
- {
4087
- value: request,
4088
- onChange: (e) => setRequest(e.target.value),
4089
- placeholder: "VD: Nh\u1EA5n m\u1EA1nh \u0111o\u1EA1n th\u1EED m\xE1y, th\xEAm emoji, ch\u1EEF n\u1EB1m \u1EDF tr\xEAn...",
4090
- className: "w-full bg-[#1B3456] border border-[#2C4A73] rounded-[8px] p-3 text-[13px] text-[#F4F1E8] h-20 outline-none focus:border-[#F6D178] custom-scrollbar"
4091
- }
4092
- )), /* @__PURE__ */ shim_react_default.createElement("div", { className: "flex flex-col gap-3" }, /* @__PURE__ */ shim_react_default.createElement("div", { className: "flex items-center justify-between gap-4" }, /* @__PURE__ */ shim_react_default.createElement("div", { className: "flex bg-[#1B3456] rounded-lg p-1 border border-[#2C4A73]" }, [{ l: "Tinh g\u1ECDn", v: "concise" }, { l: "Sinh \u0111\u1ED9ng", v: "vibrant" }].map((opt) => /* @__PURE__ */ shim_react_default.createElement("button", { key: opt.v, onClick: () => setStyle(opt.v), className: `px-4 py-1.5 rounded-md text-[10px] font-bold uppercase transition-all ${style === opt.v ? "bg-[#F6D178] text-[#122238]" : "text-[#A2B2C9] hover:text-white"}` }, opt.l))), /* @__PURE__ */ shim_react_default.createElement(
4093
- "button",
4094
- {
4095
- onClick: handleAnalyze,
4096
- disabled: loading,
4097
- className: "flex-1 py-2.5 gold-button rounded-lg text-[11px] font-black uppercase shadow-lg flex items-center justify-center gap-2 disabled:opacity-50"
4098
- },
4099
- loading ? /* @__PURE__ */ shim_react_default.createElement("div", { className: "w-4 h-4 border-2 border-current border-t-transparent rounded-full animate-spin" }) : /* @__PURE__ */ shim_react_default.createElement("span", { className: "material-symbols-outlined text-[18px]" }, "analytics"),
4100
- "PH\xC2N T\xCDCH VIDEO"
4101
- )), /* @__PURE__ */ shim_react_default.createElement(
4102
- "button",
4103
- {
4104
- onClick: handleLoadExisting,
4105
- className: "w-full py-2 bg-[#1B3456] border border-[#2C4A73] hover:border-[#F6D178] rounded-lg text-[10px] font-bold uppercase text-[#A2B2C9] hover:text-[#F6D178] transition-all flex items-center justify-center gap-2"
4106
- },
4107
- /* @__PURE__ */ shim_react_default.createElement("span", { className: "material-symbols-outlined text-[16px]" }, "sync_alt"),
4108
- "N\u1EA0P TH\u1EBA \u0110ANG C\xD3"
4109
- )), error && /* @__PURE__ */ shim_react_default.createElement("div", { className: "p-3 bg-red-500/10 border border-red-500/30 rounded-lg text-red-400 text-[11px] font-bold uppercase leading-relaxed" }, error), draft && /* @__PURE__ */ shim_react_default.createElement("div", { className: "mt-4 space-y-6 animate-in slide-in-from-top duration-300" }, /* @__PURE__ */ shim_react_default.createElement("div", { className: "p-4 bg-[#0E2036]/50 rounded-xl border border-[#F6D178]/20 space-y-3" }, /* @__PURE__ */ shim_react_default.createElement("h4", { className: "text-[11px] font-black text-[#F6D178] uppercase tracking-widest" }, "B\u1EA2N NH\xC1P D\u1EF0NG"), /* @__PURE__ */ shim_react_default.createElement("p", { className: "text-[13px] text-[#F4F1E8] font-medium leading-relaxed italic" }, '"', draft.summary, '"'), /* @__PURE__ */ shim_react_default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ shim_react_default.createElement("label", { className: "text-[9px] font-bold text-[#A2B2C9] uppercase" }, "L\u1EDDi tho\u1EA1i nghe \u0111\u01B0\u1EE3c:"), /* @__PURE__ */ shim_react_default.createElement("p", { className: "text-[12px] text-[#A2B2C9] line-clamp-2" }, draft.transcript.map((t) => t.text).join(" ") || "(Kh\xF4ng nghe th\u1EA5y l\u1EDDi r\xF5 r\xE0ng)"))), /* @__PURE__ */ shim_react_default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ shim_react_default.createElement("label", { className: "text-[9px] font-black text-[#A2B2C9] uppercase tracking-widest ml-1" }, "\u0110O\u1EA0N GI\u1EEE (", draft.moments.length, ")"), draft.moments.length === 0 ? /* @__PURE__ */ shim_react_default.createElement("p", { className: "text-[12px] text-[#A2B2C9] italic p-4 bg-[#1B3456]/20 rounded-lg border border-dashed border-[#2C4A73]" }, "Kh\xF4ng c\xF3 th\u1EBB highlight \u0111\u01B0\u1EE3c \u0111\u1EC1 xu\u1EA5t cho \u0111o\u1EA1n n\xE0y.") : draft.moments.map((m, i) => /* @__PURE__ */ shim_react_default.createElement("div", { key: i, className: "p-4 bg-[#1B3456]/40 border border-[#2C4A73] rounded-xl flex flex-col gap-3 group relative" }, /* @__PURE__ */ shim_react_default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ shim_react_default.createElement("span", { className: "text-[9px] font-black text-[#F6D178] uppercase bg-[#F6D178]/10 px-2 py-0.5 rounded" }, "Th\u1EBB ", i + 1), /* @__PURE__ */ shim_react_default.createElement("button", { onClick: () => setDraft({ ...draft, moments: draft.moments.filter((_, idx) => idx !== i) }), className: "w-6 h-6 flex items-center justify-center bg-red-500/10 text-red-400 hover:bg-red-500 hover:text-white rounded-full transition-all" }, /* @__PURE__ */ shim_react_default.createElement(Icons.X, { className: "w-4 h-4" }))), /* @__PURE__ */ shim_react_default.createElement(
4110
- "input",
4111
- {
4112
- type: "text",
4113
- value: m.text,
4114
- onChange: (e) => {
4115
- const nm = [...draft.moments];
4116
- nm[i].text = e.target.value;
4117
- setDraft({ ...draft, moments: nm });
4118
- },
4119
- className: "bg-transparent border-b border-[#2C4A73] focus:border-[#F6D178] outline-none text-[14px] font-bold text-[#F4F1E8] py-1"
4120
- }
4121
- ), /* @__PURE__ */ shim_react_default.createElement("div", { className: "grid grid-cols-2 gap-4" }, /* @__PURE__ */ shim_react_default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ shim_react_default.createElement("span", { className: "text-[10px] text-[#A2B2C9] font-bold uppercase" }, "M\u1ED1c:"), /* @__PURE__ */ shim_react_default.createElement("span", { className: "font-mono text-[11px] text-[#F6D178]" }, m.t0.toFixed(2), " - ", m.t1.toFixed(2), "s")), /* @__PURE__ */ shim_react_default.createElement("div", { className: "flex items-center gap-2 justify-end" }, /* @__PURE__ */ shim_react_default.createElement("select", { value: m.pos, onChange: (e) => {
4122
- const nm = [...draft.moments];
4123
- nm[i].pos = e.target.value;
4124
- setDraft({ ...draft, moments: nm });
4125
- }, className: "bg-[#0E2036] text-[10px] font-bold text-[#A2B2C9] p-1 rounded outline-none" }, /* @__PURE__ */ shim_react_default.createElement("option", { value: "bottom" }, "D\u01AF\u1EDAI"), /* @__PURE__ */ shim_react_default.createElement("option", { value: "top" }, "TR\xCAN")), /* @__PURE__ */ shim_react_default.createElement("select", { value: m.sfx, onChange: (e) => {
4126
- const nm = [...draft.moments];
4127
- nm[i].sfx = e.target.value;
4128
- setDraft({ ...draft, moments: nm });
4129
- }, className: "bg-[#0E2036] text-[10px] font-bold text-[#A2B2C9] p-1 rounded outline-none" }, /* @__PURE__ */ shim_react_default.createElement("option", { value: "" }, "IM L\u1EB6NG"), /* @__PURE__ */ shim_react_default.createElement("option", { value: "pop-nhe" }, "POP"), /* @__PURE__ */ shim_react_default.createElement("option", { value: "whoosh" }, "WHOOSH"))))))), /* @__PURE__ */ shim_react_default.createElement(
4130
- "button",
4131
- {
4132
- onClick: handleApply,
4133
- disabled: loading || analysisSceneId.current !== currentScene.id,
4134
- className: "w-full py-4 bg-[#3FC08C] text-[#122238] rounded-lg text-[13px] font-black uppercase shadow-xl hover:brightness-110 active:scale-95 transition-all disabled:opacity-30"
4135
- },
4136
- "\xC1P D\u1EE4NG B\u1EA2N D\u1EF0NG"
4137
- )))));
4138
- };
4139
-
4140
4004
  // flow-src/engine/stabNen.ts
4141
4005
  var STAB_W = 192;
4142
4006
  var STAB_R = 6;
@@ -13302,6 +13166,13 @@ async function nhipKhung(M, ff, files, options, onLog) {
13302
13166
  // flow-src/engine/demBen.ts
13303
13167
  var DB = "xvai-dem";
13304
13168
  var KHO2 = "k";
13169
+ var lanh = () => {
13170
+ try {
13171
+ return localStorage.getItem("xvaiDemLanh") === "1";
13172
+ } catch (e) {
13173
+ return false;
13174
+ }
13175
+ };
13305
13176
  var tat = () => {
13306
13177
  try {
13307
13178
  return localStorage.getItem("xvaiDem") === "tat";
@@ -13343,7 +13214,7 @@ async function docTrong(k) {
13343
13214
  });
13344
13215
  }
13345
13216
  async function docDem(k) {
13346
- if (tat()) return null;
13217
+ if (tat() || lanh() && /^(nghe|ai|kb):/.test(k)) return null;
13347
13218
  const t = await docTrong(k);
13348
13219
  if (t) return t;
13349
13220
  const n = await hoiNgoai("lay", k);
@@ -14438,130 +14309,6 @@ var XemTheAI = ({ moments, currentTime, videoWidth, videoHeight, isPlaying = fal
14438
14309
  }));
14439
14310
  };
14440
14311
 
14441
- // flow-src/components/AiStoryPanel.tsx
14442
- var AiStoryPanel = ({ project, currentScene, currentItem, onApplyRanges }) => {
14443
- const [loading, setLoading] = useState(false);
14444
- const [error, setError] = useState(null);
14445
- const [draft, setDraft] = useState(null);
14446
- const [requestToken, setRequestToken] = useState("");
14447
- const requestRef = useRef(0);
14448
- const mountedRef = useRef(true);
14449
- const currentToken = useMemo(() => {
14450
- if (!currentItem || !currentScene?.videoBase64) return "";
14451
- return `P:${project.id}|S:${currentScene.id}|I:${currentItem.id}|T:${currentItem.trimStart}-${currentItem.trimEnd}|V:${currentScene.videoBase64}`;
14452
- }, [project.id, currentScene, currentItem]);
14453
- const latestTokenRef = useRef(currentToken);
14454
- latestTokenRef.current = currentToken;
14455
- useEffect(() => {
14456
- mountedRef.current = true;
14457
- return () => {
14458
- mountedRef.current = false;
14459
- };
14460
- }, []);
14461
- useEffect(() => {
14462
- setDraft(null);
14463
- setError(null);
14464
- setLoading(false);
14465
- setRequestToken("");
14466
- requestRef.current++;
14467
- }, [currentToken]);
14468
- const validateSchema = (data) => {
14469
- if (!data || typeof data !== "object") throw new Error("D\u1EEF li\u1EC7u kh\xF4ng ph\u1EA3i object.");
14470
- if (typeof data.summary !== "string") throw new Error("Tr\u01B0\u1EDDng summary ph\u1EA3i l\xE0 string.");
14471
- if (typeof data.script !== "string") throw new Error("Tr\u01B0\u1EDDng script ph\u1EA3i l\xE0 string.");
14472
- if (!Array.isArray(data.keepRanges)) throw new Error("Tr\u01B0\u1EDDng keepRanges ph\u1EA3i l\xE0 m\u1EA3ng.");
14473
- if (!Array.isArray(data.removed)) throw new Error("Tr\u01B0\u1EDDng removed ph\u1EA3i l\xE0 m\u1EA3ng.");
14474
- if (!Array.isArray(data.uncertainties)) throw new Error("Tr\u01B0\u1EDDng uncertainties ph\u1EA3i l\xE0 m\u1EA3ng.");
14475
- if (!data.uncertainties.every((x) => typeof x === "string")) throw new Error("M\u1ECDi ph\u1EA7n t\u1EED uncertainties ph\u1EA3i l\xE0 string.");
14476
- const keepRanges = data.keepRanges;
14477
- if (keepRanges.length === 0 || keepRanges.length > 20) throw new Error("S\u1ED1 l\u01B0\u1EE3ng \u0111o\u1EA1n gi\u1EEF ph\u1EA3i t\u1EEB 1 \u0111\u1EBFn 20.");
14478
- const trimS = currentItem?.trimStart ?? 0;
14479
- const trimE = currentItem?.trimEnd ?? 0;
14480
- if (!Number.isFinite(trimS) || !Number.isFinite(trimE) || trimS < 0 || trimE <= trimS) {
14481
- throw new Error("V\xF9ng c\u1EAFt c\u1EE7a clip kh\xF4ng h\u1EE3p l\u1EC7.");
14482
- }
14483
- keepRanges.forEach((r, i) => {
14484
- if (!r || typeof r !== "object") throw new Error(`\u0110o\u1EA1n gi\u1EEF ${i + 1} r\u1ED7ng.`);
14485
- if (!Number.isFinite(r.t0) || !Number.isFinite(r.t1)) throw new Error(`\u0110o\u1EA1n gi\u1EEF ${i + 1} sai \u0111\u1ECBnh d\u1EA1ng s\u1ED1.`);
14486
- if (r.t0 < trimS) throw new Error(`\u0110o\u1EA1n gi\u1EEF ${i + 1} b\u1EAFt \u0111\u1EA7u ngo\xE0i v\xF9ng c\u1EAFt (${r.t0.toFixed(2)} < ${trimS.toFixed(2)}).`);
14487
- if (r.t1 > trimE) throw new Error(`\u0110o\u1EA1n gi\u1EEF ${i + 1} k\u1EBFt th\xFAc ngo\xE0i v\xF9ng c\u1EAFt (${r.t1.toFixed(2)} > ${trimE.toFixed(2)}).`);
14488
- if (r.t1 - r.t0 < 0.3 - 1e-6) throw new Error(`\u0110o\u1EA1n gi\u1EEF ${i + 1} qu\xE1 ng\u1EAFn (t\u1ED1i thi\u1EC3u 0.3s).`);
14489
- if (i > 0 && r.t0 < keepRanges[i - 1].t1) throw new Error(`\u0110o\u1EA1n gi\u1EEF ${i + 1} b\u1ECB ch\u1ED3ng l\u1EA5n.`);
14490
- if (typeof r.reason !== "string") throw new Error(`\u0110o\u1EA1n gi\u1EEF ${i + 1} thi\u1EBFu l\xFD do (string).`);
14491
- });
14492
- data.removed.forEach((rem, i) => {
14493
- if (!rem || typeof rem !== "object") throw new Error(`\u0110o\u1EA1n b\u1ECF ${i + 1} r\u1ED7ng.`);
14494
- if (!Number.isFinite(rem.t0) || !Number.isFinite(rem.t1)) throw new Error(`\u0110o\u1EA1n b\u1ECF ${i + 1} sai \u0111\u1ECBnh d\u1EA1ng s\u1ED1.`);
14495
- if (typeof rem.reason !== "string") throw new Error(`\u0110o\u1EA1n b\u1ECF ${i + 1} thi\u1EBFu l\xFD do (string).`);
14496
- if (rem.t0 < trimS || rem.t1 > trimE || rem.t1 <= rem.t0) throw new Error(`\u0110o\u1EA1n b\u1ECF ${i + 1} n\u1EB1m ngo\xE0i ngu\u1ED3n ho\u1EB7c \u0111\u1EA3o m\u1ED1c.`);
14497
- });
14498
- return data;
14499
- };
14500
- const handleAnalyze = async () => {
14501
- if (!currentScene?.videoBase64 || !currentItem) return;
14502
- const requestId = ++requestRef.current;
14503
- const snapshot = currentToken;
14504
- setLoading(true);
14505
- setError(null);
14506
- try {
14507
- const sys = `B\u1EA1n l\xE0 bi\xEAn t\u1EADp vi\xEAn n\u1ED9i dung video chuy\xEAn nghi\u1EC7p. H\xE3y xem v\xE0 nghe k\u1EF9 video \u0111\u1EC3 \u0111\u1EC1 xu\u1EA5t c\xE1c \u0111o\u1EA1n c\u1EAFt tinh g\u1ECDn nh\u1EA5t.
14508
- GI\u1EDAI H\u1EA0N NGU\u1ED2N: Video hi\u1EC7n t\u1EA1i \u0111ang \u0111\u01B0\u1EE3c d\xF9ng t\u1EEB ${currentItem.trimStart.toFixed(2)}s \u0111\u1EBFn ${currentItem.trimEnd.toFixed(2)}s. Ch\u1EC9 \u0111\u1EC1 xu\u1EA5t c\xE1c m\u1ED1c gi\xE2y TRONG kho\u1EA3ng n\xE0y.
14509
- M\u1EE4C TI\xCAU: Gi\u1EEF tr\u1ECDn \xFD, ph\u1EE7 \u0111\u1ECBnh, s\u1ED1 v\xE0 CTA. Kh\xF4ng b\u1ECBa t\xEAn/th\xF4ng s\u1ED1/c\xF4ng d\u1EE5ng. Ch\u1EC9 b\u1ECF ph\u1EA7n d\u01B0 c\xF3 ch\u1EE9ng c\u1EE9; kh\xF4ng r\xF5 th\xEC gi\u1EEF v\xE0 ghi uncertainties. Gi\u1EEF nh\u1ECBp th\u1EDF t\u1EF1 nhi\xEAn v\xE0 thao t\xE1c im ti\u1EBFng c\xF3 gi\xE1 tr\u1ECB. M\u1ED7i \u0111o\u1EA1n gi\u1EEF >=0.3s, t\u1ED1i \u0111a20, t\u0103ng d\u1EA7n kh\xF4ng ch\u1ED3ng. Script l\xE0 l\u1EDDi \u0111\u1ECDc G\u1EE2I \xDD d\u1EF1a tr\xEAn ngu\u1ED3n, ch\u01B0a t\u1EA1o gi\u1ECDng.
14510
- TR\u1EA2 JSON: {
14511
- "summary": "T\xF3m t\u1EAFt ng\u1EAFn g\u1ECDn",
14512
- "keepRanges": [{"t0": s\u1ED1, "t1": s\u1ED1, "reason": "L\xFD do gi\u1EEF", "script": "L\u1EDDi tho\u1EA1i"}],
14513
- "removed": [{"t0": s\u1ED1, "t1": s\u1ED1, "reason": "L\xFD do b\u1ECF"}],
14514
- "script": "To\xE0n b\u1ED9 k\u1ECBch b\u1EA3n sau c\u1EAFt",
14515
- "uncertainties": ["C\xE1c ph\u1EA7n ch\u01B0a r\xF5 n\u1EBFu c\xF3"]
14516
- }`;
14517
- const res = await Flow.generate.text("\u0110\u1EC1 xu\u1EA5t k\u1ECBch b\u1EA3n c\u1EAFt d\u1EF1ng t\u1ED1i \u01B0u.", {
14518
- systemInstruction: sys,
14519
- videos: [{ base64: currentScene.videoBase64, mimeType: currentScene.videoMimeType || "video/mp4" }],
14520
- thinkingLevel: "medium"
14521
- });
14522
- if (!mountedRef.current || requestId !== requestRef.current || snapshot !== latestTokenRef.current) return;
14523
- const text2 = res.text || "";
14524
- const start = text2.indexOf("{");
14525
- const end = text2.lastIndexOf("}");
14526
- if (start === -1 || end === -1) throw new Error("AI kh\xF4ng tr\u1EA3 v\u1EC1 JSON h\u1EE3p l\u1EC7.");
14527
- const parsed = validateSchema(JSON.parse(text2.substring(start, end + 1)));
14528
- setDraft(parsed);
14529
- setRequestToken(snapshot);
14530
- } catch (e) {
14531
- if (mountedRef.current && requestId === requestRef.current && snapshot === latestTokenRef.current) {
14532
- setError(e.message || "L\u1ED7i ph\xE2n t\xEDch video.");
14533
- }
14534
- } finally {
14535
- if (mountedRef.current && requestId === requestRef.current && snapshot === latestTokenRef.current) {
14536
- setLoading(false);
14537
- }
14538
- }
14539
- };
14540
- const handleApply = () => {
14541
- if (!draft || requestToken !== latestTokenRef.current) {
14542
- setError("Ngu\u1ED3n clip \u0111\xE3 thay \u0111\u1ED5i. Vui l\xF2ng ph\xE2n t\xEDch l\u1EA1i.");
14543
- return;
14544
- }
14545
- try {
14546
- validateSchema(draft);
14547
- onApplyRanges(draft.keepRanges.map((r) => ({ t0: r.t0, t1: r.t1, script: r.script })), requestToken);
14548
- } catch (e) {
14549
- setError("B\u1EA3n th\u1EA3o kh\xF4ng h\u1EE3p l\u1EC7: " + e.message);
14550
- }
14551
- };
14552
- const totalKeepDur = draft?.keepRanges.reduce((acc, r) => acc + (r.t1 - r.t0), 0) || 0;
14553
- const sourceDur = currentItem ? currentItem.trimEnd - currentItem.trimStart : 0;
14554
- return /* @__PURE__ */ shim_react_default.createElement("div", { className: "bg-[#142A47] border border-[#2C4A73] rounded-xl overflow-hidden shadow-2xl flex flex-col max-h-[600px]" }, /* @__PURE__ */ shim_react_default.createElement("div", { className: "px-5 py-3 border-b border-[#2C4A73] bg-[#0E2036]/50 flex items-center justify-between" }, /* @__PURE__ */ shim_react_default.createElement("div", { className: "flex items-center gap-2 text-[#F6D178]" }, /* @__PURE__ */ shim_react_default.createElement("span", { className: "material-symbols-outlined text-[20px]" }, "content_cut"), /* @__PURE__ */ shim_react_default.createElement("h3", { className: "text-[13px] font-black uppercase tracking-widest" }, "AI C\u1EAET D\u1EF0NG & K\u1ECACH B\u1EA2N"))), /* @__PURE__ */ shim_react_default.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-5 space-y-5" }, !currentScene?.videoBase64 || !currentItem ? /* @__PURE__ */ shim_react_default.createElement("div", { className: "py-10 text-center text-[#2C4A73] italic text-[12px] font-bold uppercase tracking-widest" }, "Ch\u1ECDn m\u1ED9t clip tr\xEAn Timeline \u0111\u1EC3 b\u1EAFt \u0111\u1EA7u") : /* @__PURE__ */ shim_react_default.createElement(shim_react_default.Fragment, null, /* @__PURE__ */ shim_react_default.createElement("div", { className: "flex items-center justify-between p-3 bg-[#0E2036]/40 rounded-lg border border-[#2C4A73]/50" }, /* @__PURE__ */ shim_react_default.createElement("div", { className: "text-[10px] font-bold text-[#A2B2C9] uppercase" }, "Ngu\u1ED3n: ", sourceDur.toFixed(2), "s"), /* @__PURE__ */ shim_react_default.createElement("button", { onClick: handleAnalyze, disabled: loading, className: "px-6 py-2 gold-button rounded-lg text-[10px] font-black uppercase shadow-lg flex items-center gap-2 disabled:opacity-50" }, loading ? /* @__PURE__ */ shim_react_default.createElement("div", { className: "w-3.5 h-3.5 border-2 border-current border-t-transparent rounded-full animate-spin" }) : /* @__PURE__ */ shim_react_default.createElement("span", { className: "material-symbols-outlined text-[16px]" }, "psychology"), "T\xCCM \u0110O\u1EA0N T\u1ED0T")), error && /* @__PURE__ */ shim_react_default.createElement("div", { className: "p-3 bg-red-500/10 border border-red-500/30 rounded-lg text-red-400 text-[11px] font-bold uppercase" }, error), draft && /* @__PURE__ */ shim_react_default.createElement("div", { className: "space-y-6 animate-in slide-in-from-top duration-300" }, /* @__PURE__ */ shim_react_default.createElement("div", { className: "p-4 bg-[#0E2036]/50 rounded-xl border border-[#F6D178]/20 space-y-3" }, /* @__PURE__ */ shim_react_default.createElement("div", { className: "flex justify-between text-[10px] font-black text-[#F6D178] uppercase tracking-widest" }, /* @__PURE__ */ shim_react_default.createElement("span", null, "B\u1EA2N TH\u1EA2O C\u1EAET D\u1EF0NG"), /* @__PURE__ */ shim_react_default.createElement("span", { className: "text-[#3FC08C]" }, "GI\u1EA2M ", (sourceDur - totalKeepDur).toFixed(2), "s")), /* @__PURE__ */ shim_react_default.createElement("p", { className: "text-[13px] text-[#F4F1E8] font-medium leading-relaxed italic" }, '"', draft.summary, '"')), /* @__PURE__ */ shim_react_default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ shim_react_default.createElement("label", { className: "text-[9px] font-black text-[#A2B2C9] uppercase tracking-widest ml-1" }, "\u0110O\u1EA0N GI\u1EEE (", draft.keepRanges.length, ")"), draft.keepRanges.map((r, i) => /* @__PURE__ */ shim_react_default.createElement("div", { key: i, className: "p-3 bg-[#1B3456]/40 border border-[#2C4A73] rounded-xl flex flex-col gap-2" }, /* @__PURE__ */ shim_react_default.createElement("div", { className: "flex justify-between items-center text-[10px] font-bold" }, /* @__PURE__ */ shim_react_default.createElement("span", { className: "text-[#3FC08C] uppercase" }, "\u0110o\u1EA1n ", i + 1), /* @__PURE__ */ shim_react_default.createElement("span", { className: "text-[#A2B2C9] italic" }, r.reason)), /* @__PURE__ */ shim_react_default.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ shim_react_default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ shim_react_default.createElement("input", { type: "number", step: "0.1", value: r.t0, onChange: (e) => {
14555
- const nd = { ...draft };
14556
- nd.keepRanges[i].t0 = Number(e.target.value);
14557
- setDraft(nd);
14558
- }, className: "w-16 bg-black/30 text-[11px] font-mono text-[#F6D178] p-1 rounded border border-[#2C4A73]" }), /* @__PURE__ */ shim_react_default.createElement("span", { className: "text-[#2C4A73]" }, "\u2192"), /* @__PURE__ */ shim_react_default.createElement("input", { type: "number", step: "0.1", value: r.t1, onChange: (e) => {
14559
- const nd = { ...draft };
14560
- nd.keepRanges[i].t1 = Number(e.target.value);
14561
- setDraft(nd);
14562
- }, className: "w-16 bg-black/30 text-[11px] font-mono text-[#F6D178] p-1 rounded border border-[#2C4A73]" })), /* @__PURE__ */ shim_react_default.createElement("span", { className: "text-[11px] font-bold text-[#F4F1E8]" }, (r.t1 - r.t0).toFixed(2), "s"))))), draft.removed.length > 0 && /* @__PURE__ */ shim_react_default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ shim_react_default.createElement("label", { className: "text-[9px] font-black text-[#F0566E] uppercase tracking-widest ml-1 opacity-60" }, "\u0110\u1EC0 XU\u1EA4T B\u1ECE (", draft.removed.length, ")"), /* @__PURE__ */ shim_react_default.createElement("div", { className: "p-3 bg-red-500/5 border border-red-500/10 rounded-xl space-y-1" }, draft.removed.map((rem, i) => /* @__PURE__ */ shim_react_default.createElement("div", { key: i, className: "text-[11px] text-[#A2B2C9]" }, "\u2022 ", rem.t0.toFixed(2), "-", rem.t1.toFixed(2), "s: ", rem.reason)))), /* @__PURE__ */ shim_react_default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ shim_react_default.createElement("label", { className: "text-[9px] font-black text-[#A2B2C9] uppercase tracking-widest ml-1" }, "K\u1ECACH B\u1EA2N \u0110\u1ECCC (SAU C\u1EAET)"), /* @__PURE__ */ shim_react_default.createElement("textarea", { value: draft.script, onChange: (e) => setDraft({ ...draft, script: e.target.value }), className: "w-full bg-[#1B3456] border border-[#2C4A73] rounded-lg p-3 text-[13px] text-[#F4F1E8] h-24 outline-none focus:border-[#F6D178] custom-scrollbar leading-relaxed" })), draft.uncertainties.length > 0 && /* @__PURE__ */ shim_react_default.createElement("div", { className: "p-3 bg-[#E8894A]/10 border border-[#E8894A]/30 rounded-lg" }, /* @__PURE__ */ shim_react_default.createElement("label", { className: "text-[9px] font-bold text-[#E8894A] uppercase tracking-widest" }, "L\u01AF\u01AF \xDD:"), draft.uncertainties.map((u, i) => /* @__PURE__ */ shim_react_default.createElement("p", { key: i, className: "text-[11px] text-[#F4F1E8] mt-1" }, "\u2022 ", u))), /* @__PURE__ */ shim_react_default.createElement("button", { onClick: handleApply, disabled: loading || requestToken !== latestTokenRef.current, className: "w-full py-4 bg-[#3FC08C] text-[#122238] rounded-lg text-[13px] font-black uppercase shadow-xl hover:brightness-105 active:scale-95 transition-all disabled:opacity-30" }, "\xC1P D\u1EE4NG C\xC1C \u0110O\u1EA0N GI\u1EEE")))));
14563
- };
14564
-
14565
14312
  // flow-src/engine/autoNative.ts
14566
14313
  var norm = (s) => s.normalize("NFC").toLocaleLowerCase("vi").replace(/[^\p{L}\p{N}]+/gu, " ").trim();
14567
14314
  function validateCaptions(value, plan, chunks) {
@@ -15321,9 +15068,10 @@ async function analyzeSpeech(project, scene, item, request, onProgress, onCache,
15321
15068
  }
15322
15069
  }
15323
15070
  }
15324
- const valid = validateWords(words, item.trimStart, item.trimEnd);
15325
- if (!valid.length) throw new Error("Ch\u01B0a nghe \u0111\u01B0\u1EE3c l\u1EDDi r\xF5. Chuy\u1EC3n sang Video ch\u01B0a c\xF3 l\u1EDDi.");
15326
- if (!daVa) ghiDem("nghe:" + fingerprint, { parts, words: valid });
15071
+ const tho2 = validateWords(words, item.trimStart, item.trimEnd);
15072
+ if (!tho2.length) throw new Error("Ch\u01B0a nghe \u0111\u01B0\u1EE3c l\u1EDDi r\xF5. Chuy\u1EC3n sang Video ch\u01B0a c\xF3 l\u1EDDi.");
15073
+ const valid = nanMoc(tho2, pcm.subarray(Math.round(item.trimStart * 16e3), Math.round(item.trimEnd * 16e3)), item.trimStart, silences);
15074
+ if (!daVa) ghiDem("nghe:" + fingerprint, { parts, words: tho2 });
15327
15075
  onProgress("Ch\u1ECDn \u0111i\u1EC3m nh\u1EA5n t\u1EEB ch\xEDnh l\u1EDDi n\xF3i...");
15328
15076
  let parsedHl = { highlights: [], cta: "" }, highlightFallback = "";
15329
15077
  if (boNhan) {
@@ -18439,7 +18187,7 @@ async function kichBanDoan(ws) {
18439
18187
  return x.toString(36).slice(0, 5);
18440
18188
  })(), khoa2 = (cm ? "xvaiKbCm9" + hp + ":" : "xvaiKb3:") + h.toString(36);
18441
18189
  try {
18442
- const c = JSON.parse(localStorage.getItem(khoa2) || "null");
18190
+ const c = localStorage.getItem("xvaiDemLanh") === "1" ? null : JSON.parse(localStorage.getItem(khoa2) || "null");
18443
18191
  if (c && c.moc) {
18444
18192
  globalThis.__kbNguon = "dem";
18445
18193
  return c;
@@ -19638,12 +19386,7 @@ var TimelineEditor = ({ project, onUpdate, onExport, onOpenVoice }) => {
19638
19386
  const rect = e.currentTarget.getBoundingClientRect();
19639
19387
  const pos = (e.clientX - rect.left) / rect.width;
19640
19388
  handleScrub(activeIndex, currentItem ? currentItem.trimStart + pos * (currentItem.trimEnd - currentItem.trimStart) : 0);
19641
- }, className: "w-full h-1.5 bg-[#1B3456] rounded-full cursor-pointer overflow-hidden border border-[#2C4A73]" }, /* @__PURE__ */ shim_react_default.createElement("div", { className: "h-full bg-[#F6D178]", style: { width: `${totalDuration > 0 ? elapsed / totalDuration * 100 : 0}%` } })))), /* @__PURE__ */ shim_react_default.createElement("div", { className: "flex-1 min-w-0 space-y-4" }, /* @__PURE__ */ shim_react_default.createElement(StudioAudioPanel, { project, onUpdate, totalDuration, onOpenVoice }), /* @__PURE__ */ shim_react_default.createElement(ProEditPanel, { project, currentScene, currentItem, onUpdate: (data) => onUpdate((current) => current && current.id === project.id ? { ...current, ...data } : current), onExport }), /* @__PURE__ */ shim_react_default.createElement("details", { className: "group" }, /* @__PURE__ */ shim_react_default.createElement("summary", { className: "text-xs text-[#F6D178] cursor-pointer font-bold py-2 px-1 hover:underline" }, "AI D\u1EF0NG & C\u1EAET D\u1EF0NG (c\xF3 th\u1EC3 thu g\u1ECDn)"), /* @__PURE__ */ shim_react_default.createElement("div", { className: "grid grid-cols-1 xl:grid-cols-2 gap-4 mt-2" }, /* @__PURE__ */ shim_react_default.createElement(AiEditPanel, { project, currentScene, currentItem, onUpdate: (data) => {
19642
- if (!currentScene) return;
19643
- const nextScenes = project.scenes.map((s) => s.id === currentScene.id ? { ...s, ...data } : s);
19644
- onUpdate({ ...project, scenes: nextScenes });
19645
- } }), /* @__PURE__ */ shim_react_default.createElement(AiStoryPanel, { project, currentScene, currentItem, onApplyRanges: (r, t) => {
19646
- } }))))), /* @__PURE__ */ shim_react_default.createElement("div", { className: "w-full pt-2 border-t border-[#2C4A73]" }, /* @__PURE__ */ shim_react_default.createElement(TimelineTrack, { timeline, scenes: project.scenes, activeIndex, onSelect: (i) => {
19389
+ }, className: "w-full h-1.5 bg-[#1B3456] rounded-full cursor-pointer overflow-hidden border border-[#2C4A73]" }, /* @__PURE__ */ shim_react_default.createElement("div", { className: "h-full bg-[#F6D178]", style: { width: `${totalDuration > 0 ? elapsed / totalDuration * 100 : 0}%` } })))), /* @__PURE__ */ shim_react_default.createElement("div", { className: "flex-1 min-w-0 space-y-4" }, /* @__PURE__ */ shim_react_default.createElement(StudioAudioPanel, { project, onUpdate, totalDuration, onOpenVoice }), /* @__PURE__ */ shim_react_default.createElement(ProEditPanel, { project, currentScene, currentItem, onUpdate: (data) => onUpdate((current) => current && current.id === project.id ? { ...current, ...data } : current), onExport }))), /* @__PURE__ */ shim_react_default.createElement("div", { className: "w-full pt-2 border-t border-[#2C4A73]" }, /* @__PURE__ */ shim_react_default.createElement(TimelineTrack, { timeline, scenes: project.scenes, activeIndex, onSelect: (i) => {
19647
19390
  setIsPlaying(false);
19648
19391
  setActiveIndex(i);
19649
19392
  }, onUpdate: commitTimeline, onScrub: handleScrub, musicName: project.musicBed?.name, playheadTime: elapsed })), showQCPopover && /* @__PURE__ */ shim_react_default.createElement(shim_react_default.Fragment, null, /* @__PURE__ */ shim_react_default.createElement("div", { className: "fixed inset-0 z-[6999] bg-transparent", onClick: () => setShowQCPopover(false) }), /* @__PURE__ */ shim_react_default.createElement("div", { className: "fixed z-[7000] w-[340px] bg-[#142A47] border border-[#2C4A73] rounded-xl overflow-hidden shadow-[0_20px_50px_rgba(0,0,0,0.5)] animate-in fade-in zoom-in-95 duration-200", style: { top: popoverPos.top, right: popoverPos.right } }, /* @__PURE__ */ shim_react_default.createElement("div", { className: "px-4 py-3 border-b border-[#2C4A73] bg-[#0E2036]/50 flex justify-between items-center" }, /* @__PURE__ */ shim_react_default.createElement("h4", { className: "text-[10px] font-black text-[#F6D178] uppercase tracking-[0.2em]" }, "KI\u1EC2M PHIM"), /* @__PURE__ */ shim_react_default.createElement("button", { onClick: () => setShowQCPopover(false), className: "text-[#A2B2C9] hover:text-white transition-colors" }, /* @__PURE__ */ shim_react_default.createElement(Icons.X, { className: "w-4 h-4" }))), /* @__PURE__ */ shim_react_default.createElement("div", { className: "max-h-[70vh] overflow-y-auto custom-scrollbar p-5 space-y-5 bg-[#142A47]" }, qc.state === "empty" ? /* @__PURE__ */ shim_react_default.createElement("p", { className: "text-[12px] text-[#A2B2C9] leading-relaxed italic" }, qc.summary) : /* @__PURE__ */ shim_react_default.createElement(shim_react_default.Fragment, null, /* @__PURE__ */ shim_react_default.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ shim_react_default.createElement("h5", { className: "text-[10px] font-black text-[#E8894A] uppercase tracking-widest flex items-center gap-2" }, /* @__PURE__ */ shim_react_default.createElement("span", { className: "w-1 h-1 bg-[#E8894A] rounded-full" }), " C\u1EA6N XEM L\u1EA0I"), qc.issues.map((item, i) => /* @__PURE__ */ shim_react_default.createElement("div", { key: i, className: "flex gap-3 items-start" }, /* @__PURE__ */ shim_react_default.createElement("div", { className: "w-5 h-5 rounded-full flex-shrink-0 mt-0.5 bg-[#E8894A]/20 flex items-center justify-center border border-[#E8894A]/30" }, /* @__PURE__ */ shim_react_default.createElement("span", { className: "material-symbols-outlined text-[12px] text-[#E8894A] font-black" }, "priority_high")), /* @__PURE__ */ shim_react_default.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ shim_react_default.createElement("div", { className: "text-[12px] font-bold text-[#F4F1E8] uppercase tracking-wide leading-tight" }, item.label), /* @__PURE__ */ shim_react_default.createElement("div", { className: "text-[10px] text-[#A2B2C9] leading-relaxed mt-1" }, item.detail))))))))));
@@ -21007,7 +20750,7 @@ function datCauLenh(a) {
21007
20750
  on2 = true;
21008
20751
  window.addEventListener("message", async (ev) => {
21009
20752
  const d = ev.data;
21010
- if (!d || d.xvaiLoi130036 !== true) return;
20753
+ if (!d || d.xvaiLoi130151 !== true) return;
21011
20754
  const o = String(ev.origin || "");
21012
20755
  if (!(o.indexOf("https://flow.google.com") === 0 || o.indexOf("https://labs.google") === 0)) return;
21013
20756
  const tra = (kq) => {