feat: 숏폼 큐 스킬 - 저장 전 JSON 검증·따옴표 보정 단계 추가

Opal이 title_top 같은 값에 따옴표를 이스케이프 없이 뱉으면 파서의
Jackson readTree가 던져서 status=FAILED, clips=0이 된다. 같은 JSON이
캡컷 구간 JSON 붙여넣기로도 들어가므로 저장과 영상 생성이 둘 다 막힌다.

저장 전에 파싱을 시도하고, 깨졌으면 fix-opal-json.ps1로 문자열 값
안쪽 따옴표만 이스케이프한 뒤 재검증한다. 그래도 안 되면 저장하지
않고 PENDING으로 남긴다. 정상 JSON에는 아무 변화가 없다.

캡컷 방식 선택도 모드별로 갈랐다 - 1단계는 구간 JSON(wpaste),
풀버전은 오팔 JSON(paste).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
hehihoho3@gmail.com 2026-08-10 17:12:49 +09:00
parent be56ff39df
commit 7d8c6bc49b
2 changed files with 29 additions and 3 deletions

View File

@ -26,6 +26,7 @@ h-lab 서버가 꺼져 있으면 먼저 `.\gradlew.bat bootRun`(JAVA_HOME=D:\Dev
a. 캔버스의 **YouTube Video 노드 클릭** → 우측 패널 URL 입력창을 비우고(전체선택 ctrl+a 후 덮어쓰기) 작업의 youtubeUrl 입력 → **Apply** (썸네일이 해당 영상으로 바뀌는지 확인). a. 캔버스의 **YouTube Video 노드 클릭** → 우측 패널 URL 입력창을 비우고(전체선택 ctrl+a 후 덮어쓰기) 작업의 youtubeUrl 입력 → **Apply** (썸네일이 해당 영상으로 바뀌는지 확인).
b. Preview 패널의 새로고침(리셋) 아이콘 → **Start** 클릭. b. Preview 패널의 새로고침(리셋) 아이콘 → **Start** 클릭.
c. **완료 폴링****추출**: 모드별 절차(아래) 참조. c. **완료 폴링****추출**: 모드별 절차(아래) 참조.
d. **JSON 검증·보정** (1단계 모드에서 저장 전 필수): 추출 텍스트를 `ConvertFrom-Json`으로 파싱해본다. 실패하면 스킬 폴더의 `fix-opal-json.ps1`로 보정하고 다시 검증한다 — 통과하면 보정본으로 저장·전달하고, 여전히 깨지면 저장하지 말고(PENDING 유지) 원문과 파싱 오류를 보고한다. 보정 후 candidates가 5개인지도 확인. (풀버전은 `N=====` 구간 5개라 통짜 파싱 대상이 아님 — 저장 후 응답 clips 개수로 확인한다.)
e. **저장**: 브라우저 fetch는 HTTPS→HTTP라 차단됨 — 추출 텍스트를 파일로 옮겨 셸에서 `POST http://localhost:8088/api/shortform/jobs/{id}/result` body `{"rawText": "<Output 원문>", "step1Text": "<Step 1 원문>"}`. PowerShell은 UTF-8 바이트로 전송(`[Text.Encoding]::UTF8.GetBytes`), 응답 한글이 깨져 보이는 건 PS 5.1 표시 문제이므로 검증은 curl로. 응답의 clips가 5개 미만이면 원문과 함께 보고. e. **저장**: 브라우저 fetch는 HTTPS→HTTP라 차단됨 — 추출 텍스트를 파일로 옮겨 셸에서 `POST http://localhost:8088/api/shortform/jobs/{id}/result` body `{"rawText": "<Output 원문>", "step1Text": "<Step 1 원문>"}`. PowerShell은 UTF-8 바이트로 전송(`[Text.Encoding]::UTF8.GetBytes`), 응답 한글이 깨져 보이는 건 PS 5.1 표시 문제이므로 검증은 curl로. 응답의 clips가 5개 미만이면 원문과 함께 보고.
f. **capcut 에이전트 넘기기**: 저장이 DONE이면 아래 "capcut 에이전트 연동" 절차로 영상 생성을 **시작만** 하고, 완료를 기다리지 않고 즉시 다음 작업의 Opal 실행으로 넘어간다. f. **capcut 에이전트 넘기기**: 저장이 DONE이면 아래 "capcut 에이전트 연동" 절차로 영상 생성을 **시작만** 하고, 완료를 기다리지 않고 즉시 다음 작업의 Opal 실행으로 넘어간다.
4. **에러 처리**: 특정 Step 노드가 빨간 표시로 실패하면 Preview 리셋 → Start로 1회 재실행. 재실패 시 해당 작업은 건너뛰고 사유를 보고(저장하지 않음 — PENDING 유지). 프롬프트 노드 끝에 중복 YouTube Video 칩이 생겼는지 확인(2026-08-03에 이 원인으로 ID 3이 항상 실패했음). 4. **에러 처리**: 특정 Step 노드가 빨간 표시로 실패하면 Preview 리셋 → Start로 1회 재실행. 재실패 시 해당 작업은 건너뛰고 사유를 보고(저장하지 않음 — PENDING 유지). 프롬프트 노드 끝에 중복 YouTube Video 칩이 생겼는지 확인(2026-08-03에 이 원인으로 ID 3이 항상 실패했음).
@ -51,9 +52,9 @@ h-lab 서버가 꺼져 있으면 먼저 `.\gradlew.bat bootRun`(JAVA_HOME=D:\Dev
2. **작업마다 새 탭**을 연다(`http://127.0.0.1:8001`). 한 탭에서 두 번 실행하면 `analyze()`가 이전 실행의 화면 상태(`A`/`P`/`AUTO_AID`)를 초기화해 먼저 돌던 작업의 진행 화면이 깨진다. 2. **작업마다 새 탭**을 연다(`http://127.0.0.1:8001`). 한 탭에서 두 번 실행하면 `analyze()`가 이전 실행의 화면 상태(`A`/`P`/`AUTO_AID`)를 초기화해 먼저 돌던 작업의 진행 화면이 깨진다.
3. 탭에서 순서대로: 3. 탭에서 순서대로:
- `#tab-auto` 클릭 (🤖 자동 탭) - `#tab-auto` 클릭 (🤖 자동 탭)
- 방식에서 **📐 구간 JSON** 선택 — `input[name="amode"][value="wpaste"]` - 방식 선택 — 1단계 모드(기본)는 **📐 구간 JSON** `input[name="amode"][value="wpaste"]`, 풀버전은 **📋 오팔 JSON** `value="paste"`
- `#autoUrl` ← 작업의 `youtubeUrl` (구간 JSON에는 URL이 없어 필수) - `#autoUrl` ← 작업의 `youtubeUrl` (구간 JSON에는 URL이 없어 필수. 오팔 JSON 모드에서는 선택)
- `#apaste` ← 저장에 쓴 candidates JSON 원문 그대로 - `#apaste` ← 저장에 쓴 원문 그대로 (1단계=candidates JSON, 풀버전=Output 통짜)
- `#autoAutoRun`(끝까지 진행)이 **체크되어 있는지 확인**, 아니면 체크한다. 이게 켜져야 버튼이 "댓글 매칭 시작 → 영상까지 자동"이 되고 검토 화면에서 안 멈춘다. - `#autoAutoRun`(끝까지 진행)이 **체크되어 있는지 확인**, 아니면 체크한다. 이게 켜져야 버튼이 "댓글 매칭 시작 → 영상까지 자동"이 되고 검토 화면에서 안 멈춘다.
- `#autoRmsilence`(무음 제거)는 **사용자가 둔 상태 그대로 두고 건드리지 않는다**. - `#autoRmsilence`(무음 제거)는 **사용자가 둔 상태 그대로 두고 건드리지 않는다**.
- `#autoGo` 클릭 - `#autoGo` 클릭

View File

@ -0,0 +1,25 @@
# Opal 출력 JSON 보정 — 문자열 값 안의 미이스케이프 따옴표를 \" 로 바꾼다.
#
# Opal이 title_top 같은 값에 따옴표를 그대로 뱉는 일이 있다(예: "title_top": ""나 진짜 방장하기 싫어...""").
# 그대로 저장하면 ShortformOutputParser의 Jackson readTree가 던져서 status=FAILED, clips=0이 되고,
# 캡컷 에이전트의 구간 JSON 붙여넣기도 같이 막힌다.
#
# 한 줄에 필드 하나씩 나오는 Opal 출력 형태를 전제로, `"key": "value"` 꼴 줄의
# value 안쪽 따옴표만 이스케이프한다. 이미 정상인 JSON에는 아무 변화가 없다.
#
# 사용:
# $fixed = & .\fix-opal-json.ps1 -Text $raw
param([Parameter(Mandatory = $true)][AllowEmptyString()][string]$Text)
$lines = $Text -split "`r`n|`n|`r"
$out = foreach ($ln in $lines) {
if ($ln -match '^(\s*"[A-Za-z0-9_]+"\s*:\s*)"(.*)"(\s*,?)\s*$') {
$pre = $Matches[1]; $val = $Matches[2]; $tail = $Matches[3]
$val = [regex]::Replace($val, '(?<!\\)"', '\"')
$pre + '"' + $val + '"' + $tail
}
else {
$ln
}
}
$out -join "`r`n"