feat: Reddit 수집, 쇼츠 텔레그램 미리보기, 코너 9개 체계 정비

- Reddit 트렌딩 수집기 추가 (/reddit collect, /pick 명령어)
- 쇼츠 영상 텔레그램 미리보기 후 승인 기반 YouTube 업로드
- 코너 9개로 통합 (앱추천→제품리뷰, 재테크절약→재테크, TV로보는세상/건강정보 추가)
- RSS 피드 73개로 확대 (9개 코너 전체 커버)
- 블로그 중복 검토 알림 수정, 글 잘림 방지 (max_tokens 8192)
- 제품리뷰 다중 이미지 지원, 저품질 이미지 필터링 강화
- HookOptimizer LLM 연동, 인스타/X/틱톡 스케줄러 비활성화

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
JOUNGWOOK KWON
2026-04-07 13:56:20 +09:00
parent 93b2d3a264
commit 726c593e85
15 changed files with 1357 additions and 190 deletions
+23 -3
View File
@@ -143,7 +143,8 @@ def _is_converted(article_id: str) -> bool:
# ─── 파이프라인 ───────────────────────────────────────────────
def produce(article: dict, dry_run: bool = False, cfg: Optional[dict] = None) -> ShortsResult:
def produce(article: dict, dry_run: bool = False, cfg: Optional[dict] = None,
skip_upload: bool = False) -> ShortsResult:
"""
블로그 글 → 쇼츠 영상 생산 + (선택) YouTube 업로드.
@@ -151,6 +152,7 @@ def produce(article: dict, dry_run: bool = False, cfg: Optional[dict] = None) ->
article: article dict
dry_run: True이면 렌더링까지만 (업로드 생략)
cfg: shorts_config.json dict (None이면 자동 로드)
skip_upload: True이면 영상 렌더링까지만 (업로드는 별도 승인 후 진행)
Returns:
ShortsResult
@@ -193,10 +195,23 @@ def produce(article: dict, dry_run: bool = False, cfg: Optional[dict] = None) ->
manifest = resolve(article, script=script, cfg=cfg)
result.steps_completed.append('script_extract')
# ── STEP 1.5: Hook Optimization ─────────────────────────
# ── STEP 1.5: Hook Optimization (LLM 연동) ──────────────
hook_optimizer = HookOptimizer(threshold=70)
original_hook = script.get('hook', '')
optimized_hook = hook_optimizer.optimize(original_hook, article)
# LLM 함수 생성 — 기존 엔진 로더 활용
llm_fn = None
try:
from engine_loader import EngineLoader
writer = EngineLoader().get_writer()
if writer:
def _hook_llm(prompt: str) -> str:
return writer.write(prompt).strip()
llm_fn = _hook_llm
except Exception as e:
logger.warning(f'[{article_id}] 훅 LLM 로드 실패 (규칙 기반으로 진행): {e}')
optimized_hook = hook_optimizer.optimize(original_hook, article, llm_fn=llm_fn)
if optimized_hook != original_hook:
script['hook'] = optimized_hook
logger.info(f'[{article_id}] 훅 최적화: "{original_hook[:20]}""{optimized_hook[:20]}"')
@@ -256,6 +271,11 @@ def produce(article: dict, dry_run: bool = False, cfg: Optional[dict] = None) ->
result.success = True
return result
if skip_upload:
logger.info(f'[{article_id}] STEP 6: 건너뜀 (승인 대기 — skip_upload)')
result.success = True
return result
logger.info(f'[{article_id}] STEP 6: YouTube Upload')
from shorts.youtube_uploader import upload
upload_record = upload(video_path, article, script, ts, cfg=cfg)