Skip to content

Commit e91665d

Browse files
committed
fix: CEP/UXP correctness batch — job dedup, NLP confirm, UXP await fixes
CEP: - Job history dedupe now compares by job_id (or type+createdAt fallback) instead of (type, status), preventing collapse of distinct runs - NLP auto-execute: raise confidence threshold from 0.6 to 0.8 and add confirm() before executing to prevent misrouted commands UXP: - Fix await precedence: (await getOutPoint?.())?.seconds (line 906) - Add missing await on getFirstMarkerAtTime so marker name/color apply - Guard Enter-key handlers with hasActiveJob() to prevent duplicate submissions during active jobs - Change ok-without-job_id else branches to else-if-!r.ok across 10 handlers so synchronous success responses aren't reported as failures Backend: - captions.py: use _schedule_temp_cleanup when immediate WAV unlink fails (Windows PermissionError from timed-out Whisper thread)
1 parent 55ebb35 commit e91665d

3 files changed

Lines changed: 30 additions & 23 deletions

File tree

extension/com.opencut.panel/client/main.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9901,6 +9901,7 @@
99019901
function addJobHistory(job) {
99029902
if (!job || !job.type) return;
99039903
jobHistoryList.unshift({
9904+
job_id: job.job_id || "",
99049905
type: job.type,
99059906
status: job.status || "complete",
99069907
message: job.message || "",
@@ -10172,13 +10173,17 @@
1017210173
// Avoid duplicating entries already in the client-side list
1017310174
var alreadyHas = false;
1017410175
for (var k = 0; k < jobHistoryList.length; k++) {
10175-
if (jobHistoryList[k].type === j.type && jobHistoryList[k].status === j.status) {
10176+
if (j.job_id && jobHistoryList[k].job_id === j.job_id) {
10177+
alreadyHas = true; break;
10178+
}
10179+
if (!j.job_id && jobHistoryList[k].type === j.type && jobHistoryList[k].createdAt === (j.created || 0)) {
1017610180
alreadyHas = true; break;
1017710181
}
1017810182
}
1017910183
if (!alreadyHas) {
1018010184
var created = j.created ? new Date(j.created * 1000).toLocaleTimeString() : "";
1018110185
jobHistoryList.push({
10186+
job_id: j.job_id || "",
1018210187
type: j.type || "unknown",
1018310188
status: j.status || "complete",
1018410189
message: j.message || "",
@@ -15359,9 +15364,11 @@
1535915364
if (confEl) confEl.textContent = t("nlp.confidence_label", "Confidence: {percent}%")
1536015365
.replace("{percent}", safeFixed((data.confidence || 0) * 100, 0));
1536115366
if (outEl) outEl.textContent = data.result ? JSON.stringify(data.result, null, 2) : "";
15362-
// Auto-execute matched route if high confidence — uses snapshot from command time
15363-
if (data.route && data.confidence > 0.6 && data.params) {
15364-
startJob(data.route, Object.assign({ filepath: snapPath, output_dir: snapFolder }, data.params));
15367+
if (data.route && data.confidence > 0.8 && data.params) {
15368+
var routeName = data.route.replace(/^\//, "");
15369+
if (confirm("Execute " + routeName + "?")) {
15370+
startJob(data.route, Object.assign({ filepath: snapPath, output_dir: snapFolder }, data.params));
15371+
}
1536515372
}
1536615373
});
1536715374
}

extension/com.opencut.uxp/main.js

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,7 @@ const PProBridge = (() => {
736736
await markerList.createMarker(m.time);
737737
// Marker label/color API may vary — wrap each in try/catch
738738
try {
739-
const created = markerList.getFirstMarkerAtTime(m.time);
739+
const created = await markerList.getFirstMarkerAtTime(m.time);
740740
if (created) {
741741
await created.setName(m.label || "Beat");
742742
if (m.color) await created.setColorIndex(colorNameToIndex(m.color));
@@ -903,7 +903,7 @@ const PProBridge = (() => {
903903
const mediaPath = await child.getMediaPath?.() ?? "";
904904
if (mediaPath) {
905905
const name = await child.getName?.() ?? "";
906-
const duration = await child.getOutPoint?.()?.seconds ?? 0;
906+
const duration = (await child.getOutPoint?.())?.seconds ?? 0;
907907
items.push({ name, path: mediaPath, duration });
908908
}
909909
}
@@ -5422,7 +5422,7 @@ async function runBrollGenerate() {
54225422
"success",
54235423
);
54245424
UIController.setStatus(formatI18n("uxp.video.runtime.broll_generated_status", "B-roll generation complete: {output}", { output }));
5425-
} else {
5425+
} else if (!r.ok) {
54265426
const error = r.error || r.data?.error || t("common.unknown", "unknown");
54275427
UIController.showToast(formatI18n("uxp.video.runtime.broll_generation_failed", "B-roll generation failed: {error}", { error }), "error");
54285428
}
@@ -5462,7 +5462,7 @@ async function runMultimodalDiarize() {
54625462
UIController.showToast(formatI18n("uxp.video.runtime.diarization_complete_summary", "Diarization complete: {summary}", { summary }), "success");
54635463
UIController.setStatus(summary);
54645464
}
5465-
} else {
5465+
} else if (!r.ok) {
54665466
const error = r.error || r.data?.error || t("common.unknown", "unknown");
54675467
UIController.showToast(formatI18n("uxp.video.runtime.diarization_failed", "Diarization failed: {error}", { error }), "error");
54685468
}
@@ -5496,7 +5496,7 @@ async function runSocialUpload() {
54965496
} else if (result) {
54975497
UIController.showToast(formatI18n("uxp.video.runtime.uploaded_to_platform", "Uploaded to {platform}.", { platform }), "success");
54985498
}
5499-
} else {
5499+
} else if (!r.ok) {
55005500
const error = r.error || r.data?.error || t("common.unknown", "unknown");
55015501
UIController.showToast(formatI18n("uxp.video.runtime.upload_failed", "Upload failed: {error}", { error }), "error");
55025502
}
@@ -5998,10 +5998,10 @@ function bindEvents() {
59985998
}
59995999
});
60006000
document.getElementById("searchQuery")?.addEventListener("keydown", (e) => {
6001-
if (e.key === "Enter") runFootageSearch();
6001+
if (e.key === "Enter" && !hasActiveJob()) runFootageSearch();
60026002
});
60036003
document.getElementById("nlpCommand")?.addEventListener("keydown", (e) => {
6004-
if (e.key === "Enter") runNlpCommand();
6004+
if (e.key === "Enter" && !hasActiveJob()) runNlpCommand();
60056005
});
60066006
document.querySelectorAll("[data-fill-target]").forEach((button) => {
60076007
button.addEventListener("click", () => fillFieldFromSuggestion(button));
@@ -6038,7 +6038,7 @@ function bindEvents() {
60386038
// ── Chat Editor ──
60396039
document.getElementById("chatSendBtnUxp")?.addEventListener("click", sendChatMessage);
60406040
document.getElementById("chatInputUxp")?.addEventListener("keydown", (e) => {
6041-
if (e.key === "Enter") sendChatMessage();
6041+
if (e.key === "Enter" && !hasActiveJob()) sendChatMessage();
60426042
});
60436043

60446044
// ── AI B-Roll Generation ──
@@ -6124,7 +6124,7 @@ async function runDepthEffect() {
61246124
}
61256125
UIController.hideProcessing();
61266126
UIController.setButtonLoading("runDepthBtnUxp", false);
6127-
} else {
6127+
} else if (!r.ok) {
61286128
UIController.setButtonLoading("runDepthBtnUxp", false);
61296129
const error = r.error || r.data?.error || t("uxp.video.runtime.failed_to_start_depth_effect", "Failed to start depth effect");
61306130
UIController.showToast(formatI18n("uxp.video.runtime.depth_effect_start_error", "Error: {error}", { error }), "error");
@@ -6155,7 +6155,7 @@ async function runEmotionHighlights() {
61556155
}
61566156
UIController.hideProcessing();
61576157
UIController.setButtonLoading("runEmotionBtnUxp", false);
6158-
} else {
6158+
} else if (!r.ok) {
61596159
UIController.setButtonLoading("runEmotionBtnUxp", false);
61606160
const error = r.error || r.data?.error || t("uxp.video.runtime.failed_to_start_emotion_analysis", "Failed to start emotion analysis");
61616161
UIController.showToast(formatI18n("uxp.video.runtime.emotion_analysis_start_error", "Error: {error}", { error }), "error");
@@ -6186,7 +6186,7 @@ async function runBrollAnalysis() {
61866186
}
61876187
UIController.hideProcessing();
61886188
UIController.setButtonLoading("runBrollPlanBtnUxp", false);
6189-
} else {
6189+
} else if (!r.ok) {
61906190
UIController.setButtonLoading("runBrollPlanBtnUxp", false);
61916191
const error = r.error || r.data?.error || t("uxp.video.runtime.failed_to_start_broll_analysis", "Failed to start B-roll analysis");
61926192
UIController.showToast(formatI18n("uxp.video.runtime.broll_analysis_start_error", "Error: {error}", { error }), "error");
@@ -6876,7 +6876,7 @@ async function runUpscaleUxp() {
68766876
const result = await JobPoller.poll(r.data.job_id);
68776877
const output = result?.output_path || t("uxp.video.runtime.done", "done");
68786878
UIController.showToast(formatI18n("uxp.video.runtime.upscaled_output", "Upscaled: {output}", { output }), "success");
6879-
} else {
6879+
} else if (!r.ok) {
68806880
const error = r.data?.error || r.error || t("uxp.video.runtime.upscale_failed_default", "Upscale failed.");
68816881
UIController.showToast(formatI18n("uxp.video.runtime.upscale_failed", "Upscale failed: {error}", { error }), "error");
68826882
}
@@ -6905,7 +6905,7 @@ async function runSceneDetectUxp() {
69056905
const result = await JobPoller.poll(r.data.job_id);
69066906
const count = result?.scenes?.length || result?.total_scenes || 0;
69076907
UIController.showToast(formatI18n("uxp.video.runtime.scene_boundaries_found", "Found {count} scene boundaries.", { count }), "success");
6908-
} else {
6908+
} else if (!r.ok) {
69096909
const error = r.data?.error || r.error || t("uxp.video.runtime.scene_detection_failed_default", "Scene detection failed.");
69106910
UIController.showToast(formatI18n("uxp.video.runtime.scene_detection_failed", "Scene detection failed: {error}", { error }), "error");
69116911
}
@@ -6934,7 +6934,7 @@ async function runStyleTransferUxp() {
69346934
const result = await JobPoller.poll(r.data.job_id);
69356935
const output = result?.output_path || t("uxp.video.runtime.done", "done");
69366936
UIController.showToast(formatI18n("uxp.video.runtime.style_applied_output", "Style applied: {output}", { output }), "success");
6937-
} else {
6937+
} else if (!r.ok) {
69386938
const error = r.data?.error || r.error || t("uxp.video.runtime.style_transfer_failed_default", "Style transfer failed.");
69396939
UIController.showToast(formatI18n("uxp.video.runtime.style_transfer_failed", "Style transfer failed: {error}", { error }), "error");
69406940
}
@@ -7199,7 +7199,7 @@ async function renderApprovedShortsUxp() {
71997199
const count = result?.total_clips || result?.clips?.length || 0;
72007200
renderShortsBundleSummaryUxp(result);
72017201
UIController.showToast(formatI18n("uxp.video.runtime.rendered_approved_shorts", "Rendered {count} approved short-form clip(s).", { count }), "success");
7202-
} else {
7202+
} else if (!r.ok) {
72037203
const error = r.data?.error || r.error || t("uxp.video.runtime.approved_render_failed_default", "Approved render failed.");
72047204
UIController.showToast(formatI18n("uxp.video.runtime.approved_render_failed", "Approved render failed: {error}", { error }), "error");
72057205
}
@@ -7225,7 +7225,7 @@ async function runShortsPipelineUxp() {
72257225
const count = result?.total_clips || result?.clips?.length || 0;
72267226
renderShortsBundleSummaryUxp(result);
72277227
UIController.showToast(formatI18n("uxp.video.runtime.generated_short_form_clips", "Generated {count} short-form clip(s).", { count }), "success");
7228-
} else {
7228+
} else if (!r.ok) {
72297229
const error = r.data?.error || r.error || t("uxp.video.runtime.shorts_pipeline_failed_default", "Shorts pipeline failed.");
72307230
UIController.showToast(formatI18n("uxp.video.runtime.shorts_pipeline_failed", "Shorts pipeline failed: {error}", { error }), "error");
72317231
}

opencut/core/captions.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -590,12 +590,12 @@ def _do_transcribe():
590590
logger.warning("Transcript cache write failed for %s: %s", filepath, exc)
591591
return result
592592
finally:
593-
# Cleanup temp wav
594593
if os.path.exists(wav_path) and wav_path.startswith(tempfile.gettempdir()):
595594
try:
596595
os.unlink(wav_path)
597-
except Exception:
598-
pass
596+
except OSError:
597+
from opencut.helpers import _schedule_temp_cleanup
598+
_schedule_temp_cleanup(wav_path)
599599

600600

601601
def transcribe_audio(

0 commit comments

Comments
 (0)