capcut-agent/capcut_agent/prompts.py
hehihoho3@gmail.com bf1b387d6d chore: git 저장소 초기화 (기존 코드 스냅샷)
컷별 댓글 추천 작업을 태스크 단위로 되돌릴 수 있게 버전관리를 시작한다.
.gitignore 로 영상·캐시(.downloads 2.7G, .comments 72M, .media 28M)와
비밀키(.gemini_key)를 제외했다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 11:36:05 +09:00

103 lines
4.0 KiB
Python

"""프롬프트·설정 파일 관리 — 자동 탭(Gemini)용.
Step 3 지침 = 프로젝트 루트 `숏폼_편집_지침서_v13.7_capcut2연동판.md` (사용자 소유,
reset 대상 아님). Step 1 프롬프트·모델 설정 = `프롬프트/` 폴더(없으면 기본값 생성).
UI(⚙ 지침 수정)에서든 메모장에서든 고치면 다음 분석부터 반영된다.
"""
from __future__ import annotations
import json
import os
from typing import Optional
_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PROMPT_DIR = os.path.join(_ROOT, "프롬프트")
STEP1_PATH = os.path.join(PROMPT_DIR, "하이라이트_선정.md")
CONFIG_PATH = os.path.join(PROMPT_DIR, "설정.json")
STEP3_PATH = os.path.join(_ROOT, "숏폼_편집_지침서_v13.7_capcut2연동판.md")
# 오팔 Step 1 원문 이식. 원문 JSON 예시의 오타(start_time 중복, end_time 누락)는 수정.
DEFAULT_STEP1 = """당신은 유튜브 영상에서 쇼츠(Shorts)로 제작했을 때 가장 터질 만한 구간을 찾아내는 '바이럴 분석가'입니다. 제공된 영상을 분석해 아래 규칙에 따라 5개의 하이라이트 후보 구간을 선정하세요.
1. 각 구간의 길이는 최소 1분 30초에서 최대 3분 사이로 설정할 것. (최종 편집을 위한 원천 소스 구간임)
2. 시청자의 시선을 끌 수 있는 갈등, 웃음, 반전, 혹은 핵심 정보가 포함된 구간을 우선순위로 둘 것.
3. 결과물은 반드시 아래의 JSON 형식으로만 출력할 것. (다른 설명 금지)
{
"candidates": [
{
"id": 1,
"start_time": "MM:SS",
"end_time": "MM:SS",
"reason": "구간 선정 이유 요약"
},
... (총 5개)
]
}
"""
DEFAULT_CONFIG = {
"model": "gemini-3.5-flash",
"model_step1": "",
"fps_step1": 0.2,
"fps_step3": 1.0,
}
def load_step1() -> str:
if not os.path.isfile(STEP1_PATH):
os.makedirs(PROMPT_DIR, exist_ok=True)
with open(STEP1_PATH, "w", encoding="utf-8") as f:
f.write(DEFAULT_STEP1)
with open(STEP1_PATH, encoding="utf-8") as f:
return f.read()
def load_step3() -> str:
if not os.path.isfile(STEP3_PATH):
raise RuntimeError(f"Step 3 지침 파일이 없습니다: {STEP3_PATH}")
with open(STEP3_PATH, encoding="utf-8") as f:
return f.read()
def load_config() -> dict:
"""설정 로드. 파일이 없거나 깨졌으면 기본값(파일은 첫 save 때 생성)."""
cfg = dict(DEFAULT_CONFIG)
try:
with open(CONFIG_PATH, encoding="utf-8") as f:
loaded = json.load(f)
if isinstance(loaded, dict):
cfg.update(loaded)
except (OSError, json.JSONDecodeError, ValueError):
pass
return cfg
def save(*, step1: Optional[str] = None, step3: Optional[str] = None,
config: Optional[str] = None) -> None:
"""전달된 것만 저장. config 는 JSON 문자열 — 형식 오류는 예외로 올린다."""
os.makedirs(PROMPT_DIR, exist_ok=True)
if config is not None: # 검증 먼저(실패 시 아무것도 안 씀)
cfg = json.loads(config)
if not isinstance(cfg, dict):
raise ValueError("설정은 JSON 객체({…})여야 합니다.")
merged = dict(DEFAULT_CONFIG)
merged.update(cfg)
with open(CONFIG_PATH, "w", encoding="utf-8") as f:
json.dump(merged, f, ensure_ascii=False, indent=1)
if step1 is not None:
with open(STEP1_PATH, "w", encoding="utf-8") as f:
f.write(step1)
if step3 is not None:
with open(STEP3_PATH, "w", encoding="utf-8") as f:
f.write(step3)
def reset() -> None:
"""Step 1 프롬프트·설정을 기본값으로. Step 3(지침서)는 사용자 파일이라 불변."""
os.makedirs(PROMPT_DIR, exist_ok=True)
with open(STEP1_PATH, "w", encoding="utf-8") as f:
f.write(DEFAULT_STEP1)
with open(CONFIG_PATH, "w", encoding="utf-8") as f:
json.dump(DEFAULT_CONFIG, f, ensure_ascii=False, indent=1)