Commit Graph

463 Commits

Author SHA1 Message Date
1d4bff31c4 feat(packs-lab): DSM_VERIFY_SSL env — LAN IP + self-signed cert 환경 대응
운영 NAS에서 DSM_HOST=https://192.168.x.x:5001 같은 LAN IP 사용 시
DSM의 self-signed 인증서가 IP 주소에 매칭되지 않아 SSL 검증 실패
(SSL: CERTIFICATE_VERIFY_FAILED — IP address mismatch).

LAN 내부 통신이라 verify=False 허용 가능. 환경변수로 토글:
- DSM_VERIFY_SSL=true (default) — 도메인 + 정상 cert 환경
- DSM_VERIFY_SSL=false — LAN IP + self-signed 환경

dsm_client.py가 환경변수 읽어 httpx.AsyncClient(verify=...)에 전달.
docker-compose.yml + .env.example + CLAUDE.md에 신규 env 명시.
회귀 25/25 passing.
2026-05-11 03:31:15 +09:00
e31bf549a8 docs(spec/plan): packs-lab spec/plan 복구 + PACK_HOST_DIR/평면구조/SERVICES 화이트리스트 반영
dc92c3d에서 "완료된 spec/plan 제거"로 함께 정리됐던 두 파일을 복구하고,
이후 적용된 운영 변경사항을 반영해 문서-구현 추적성 회복:

- PACK_HOST_DIR 환경변수 도입 (NAS 호스트 절대경로, DSM·Supabase에 노출)
- 평면 저장 구조 (PACK_BASE_DIR/{filename}, tier 디렉토리 분기 제거 — tier는 filename 규칙으로)
- scripts/deploy-nas.sh의 SERVICES 화이트리스트에 packs-lab 추가 (누락 시 NAS 컨테이너 미등장)
- .env.example 환경변수 6+3 path (DSM 3 / HMAC / Supabase 2 / TTL / DATA_PATH / BASE_DIR / HOST_DIR)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 03:03:00 +09:00
aec0fdcd31 fix(packs-lab): tier 디렉토리 제거(평면 구조) + deployer SERVICES에 packs-lab 추가
문제 1: deploy-nas.sh의 SERVICES 화이트리스트에 packs-lab이 빠져 있어
NAS 운영 디렉토리에 소스 sync가 안 됐고 docker compose가 packs-lab을
빌드 못해 컨테이너가 안 떠 있었다.

문제 2: routes.py가 PACK_BASE_DIR/{tier}/{filename} 트리 구조로 저장 →
사용자 요청에 따라 평면 구조(PACK_BASE_DIR/{filename})로 변경. tier 구분은
filename 규칙(prefix 등)으로 admin이 관리.

- scripts/deploy-nas.sh: SERVICES에 packs-lab 추가 (10개 → 11개)
- routes.py: tier 디렉토리 제거 (target = PACK_BASE_DIR / filename, host_path = PACK_HOST_DIR / filename)
- tests: tier 분기 사용처 평면 구조로 보정 (size_mismatch / host_path_check)
- 25/25 passing

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 02:54:25 +09:00
f1f1dc98a6 fix(packs-lab): PACK_HOST_DIR 도입 — sign-link 시 DSM이 NAS 호스트경로 받도록
이전: upload가 컨테이너 경로(/app/data/packs/...)를 Supabase에 저장 →
sign-link 시 그 경로를 DSM에 전달 → DSM은 NAS 호스트 절대경로
(/volume1/.../media/packs/...) 기준이라 파일을 찾지 못함.

수정:
- routes.py: PACK_HOST_DIR 신규 (env, fallback=PACK_BASE_DIR)
  - upload 시 host_path = PACK_HOST_DIR/{tier}/{filename}을 Supabase에 INSERT
  - sign-link 시 PACK_HOST_DIR 기준 경로 검증
- docker-compose: PACK_HOST_DIR env 주입 (default=PACK_DATA_PATH)
- .env.example + CLAUDE.md: 환경변수 의미 분리 명시
- tests: 호스트경로 저장 검증 신규 (test_upload_stores_host_path_not_container_path)
- 25/25 passing

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 02:47:26 +09:00
8b5cb2c16a feat(music-lab): 랜덤 풀에 7개 장르 추가 + GET /api/music/genres 2026-05-10 23:53:35 +09:00
77b8d05ad7 feat(music-lab): 배치 음악 생성 endpoint + 자동 compile·video 파이프라인 오케스트레이터
- batch_generator.py: 장르별 N트랙 순차 Suno 생성 → 자동 compile → 자동 video pipeline
- main.py: POST/GET /api/music/generate-batch, GET /api/music/generate-batch/{id} 추가
- tests: 10개 endpoint 테스트 (검증·필터·404)
2026-05-10 18:57:23 +09:00
f0cb06268e feat(music-lab): music_batch_jobs 테이블 + 장르별 랜덤 풀 2026-05-10 18:52:07 +09:00
f074cbec2d docs: 배치 음악 생성 + 자동 영상 파이프라인 spec + plan 2026-05-10 18:49:16 +09:00
84548a326e feat(music-lab): cover 16:9 landscape 생성 + 메타데이터 프로페셔널화
- cover.py: DALL·E 3 → 1792x1024, gpt-image-1 → 1536x1024 (모델별 자동),
  prompt에 'cinematic landscape composition' 명시. OPENAI_IMAGE_SIZE env로 override 가능.
- metadata.py: prompt를 list+join 패턴으로 재구성 (인접 문자열/+ 충돌 해결)
  + lofi 채널 카피라이터 페르소나 부여. description 5-7섹션 구조 명시:
  후크/분위기/사용시나리오/챕터/시청권장/콜투액션/해시태그.
  mix vs single 분기 + tags 가이드 + 출력 JSON schema 명시.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 18:38:53 +09:00
5f5010ded4 fix(music-lab): video encoder timeout을 duration에 비례 (긴 mix 인코딩 지원) 2026-05-10 17:10:27 +09:00
755dea63f4 fix(music-lab): cache-buster query 제거 + DALL·E prompt에 background_keyword 활용
1. video.py _container_to_nas, orchestrator.py _local_path에서 path 변환 전 ?쿼리 strip
   — 이전 commit 20c5268의 cache-buster ?v=...가 Windows path로 그대로 전달되어 input_validation 실패하던 문제 픽스
2. cover.py _generate_with_dalle가 background_keyword를 prompt에 포함
   — 사용자가 PipelineStartModal에서 '배경 키워드' 입력 시 처음부터 원하는 분위기 cover 생성
2026-05-10 16:12:21 +09:00
20c5268def fix(music-lab): pipeline media URL에 cache-buster — regen 시 브라우저/텔레그램 캐시 우회 2026-05-10 15:50:42 +09:00
dc3f9cb6a9 fix(music-lab): compile job status='done'도 ready로 인식 (production convention) 2026-05-10 15:28:08 +09:00
262366bc1e test(music-lab): compile_job 기반 happy path 통합 테스트 2026-05-09 13:27:47 +09:00
5fc914cd8f feat(music-lab): POST /pipeline에 compile_job_id + visual_style/background 옵션 2026-05-09 13:20:38 +09:00
8f859274c4 feat(music-lab): video.py — Windows에 style/background_mode/tracks 전달 + orchestrator 파라미터 wiring 2026-05-09 13:17:49 +09:00
a347da075c feat(music-lab): metadata tracks 옵션 + YouTube 챕터 자동 형식 2026-05-09 13:15:30 +09:00
e754fb30f5 feat(music-lab): background.py — Pexels Video API + orchestrator video_loop 분기 2026-05-09 13:13:42 +09:00
f0c0c18beb feat(music-lab): cover.py Pexels 이미지 검색 분기 (image_source=pexels) 2026-05-09 13:10:49 +09:00
d11023decb feat(music-lab): orchestrator _resolve_input — track/compile_job 통합 입력 2026-05-09 13:08:53 +09:00
70a256bbe4 feat(music-lab): video_pipelines 4 컬럼 추가 + compile_jobs JOIN
- _add_column_if_missing 헬퍼 추가 (idempotent ALTER TABLE)
- video_pipelines에 compile_job_id, visual_style, background_mode, background_keyword 컬럼 추가
- track_id를 nullable로 변경 (compile_job_id 입력 모드 지원)
- create_pipeline에 compile_job_id XOR track_id 검증 추가
- get_pipeline / list_pipelines에 compile_jobs LEFT JOIN — compile_title 노출

Task 1 of 17: Essential Mix pipeline DB migration

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 13:04:23 +09:00
ebbfa6299a docs(plan): Essential Mix 파이프라인 — 17 task 구현 계획
DB 마이그레이션 → orchestrator _resolve_input → cover Pexels 분기 →
background.py 신규 → metadata tracks → video.py 파라미터 확장 →
main.py compile_job_id → Windows essential filter (showfreqs+ring+drawtext) →
server.py schema → 통합 테스트 → 배포 → 프론트(api.js, CompileTab,
PipelineStartModal, PipelineCard+DetailModal, SetupTab) → 프론트 푸시 → E2E.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 12:44:02 +09:00
d4fb485931 docs(spec): Essential Mix 파이프라인 설계
1시간+ mix 영상(컴파일 → 파이프라인) + essential 시각 스타일(배경 사진 + 중앙 방사형 막대 + 곡명 자막) + 진행 탭 산출물 미리보기 모달.

핵심 결정:
- 입력: track_id XOR compile_job_id
- 시각: single (기존) / essential (신규, default)
- 배경: static(사진) / video_loop(Pexels 영상)
- 배경 소스: AI 기본 + Pexels 폴백
- Mix 메타: 트랙 리스트 자동 챕터화 (YouTube 자동 인식)
- UX: PipelineCard mini 미리보기 + 클릭 시 상세 모달

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 11:55:24 +09:00
b6dffb4d42 chore(infra): GPU 영상 인코더 env 추가 (WINDOWS_VIDEO_ENCODER_URL, NAS_VIDEOS_ROOT, NAS_MUSIC_ROOT) 2026-05-09 02:03:26 +09:00
240bd38541 feat(music-lab): 영상 인코딩을 Windows GPU 서버로 오프로드
- pipeline/video.py 재작성: subprocess.run 제거, httpx로 Windows /encode_video 호출
- Windows 서버 다운 시 즉시 VideoGenerationError (NAS 로컬 폴백 X — 의도적 결정)
- /app/data/* → /volume1/docker/webpage/data/* 경로 변환 (_container_to_nas)
- 테스트는 respx mock 기반으로 교체 (6개)
2026-05-09 02:01:34 +09:00
bb0b0dff25 docs: GPU 영상 인코딩 오프로드 spec + plan
NAS 저성능 CPU(J4025) ffmpeg 5분 타임아웃 → Windows PC RTX 5070 Ti NVENC로
오프로드. 같은 music_ai 서버에 /encode_video endpoint 추가, NAS는 다운 시
즉시 실패 (로컬 폴백 X). LAN 무인증.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 01:52:34 +09:00
47e5315487 fix(music-lab): ffmpeg 인코딩 가속 + 타임아웃 확장 (저성능 CPU 대응)
- preset fast → ultrafast (5-10x 가속, J4025 같은 저성능 CPU에서 5분 내 완료)
- tune stillimage 추가 (정적 배경 + 파형 오버레이에 최적)
- threads 0 — 모든 CPU 코어 활용
- VIDEO_TIMEOUT_S 300 → 600 (안전 마진)
- subprocess.TimeoutExpired 캐치하여 명확한 에러 메시지
2026-05-09 01:01:03 +09:00
97b15cb985 fix(pipeline): premature state update + reject 재생성 알림
버그1: /feedback approve가 bg task 시작 전에 state를 next_pending으로 set →
polling이 빈 video_url로 알림 발송. bg task의 run_step이 state를 set하도록
일임 — 이중 update 제거.

버그2: reject 후 같은 *_pending 상태로 재생성됐을 때 dedupe에 막혀 알림이
안 감. dedupe 키에 feedback_count_per_step[step]을 포함 — 재생성마다
count가 증가하므로 키가 달라져 재알림 동작.
2026-05-08 23:08:24 +09:00
6d416aab78 fix(music-lab): pipeline 동기 작업을 asyncio.to_thread로 — 이벤트 루프 블로킹 해결
video.generate/thumb.generate/youtube.upload_video는 동기 함수로 ffmpeg subprocess(최대 5분)와
google-api-python-client(최대 10분)를 호출함. async run_step에서 직접 호출하면 이벤트 루프가
블로킹돼 후속 요청이 504로 타임아웃되고 텔레그램 폴링도 끊김.

asyncio.to_thread로 감싸 스레드 풀에서 실행 — 이벤트 루프 자유.
2026-05-08 22:57:33 +09:00
2c13e7cc85 fix(music-lab): pipeline 오디오 경로 + ffmpeg 에러 가시성
- orchestrator._run_video: track.file_path 우선 사용 (audio_url 변환 불필요)
- _local_path: /media/music/ → /app/data/ (마운트가 /app/data 직접이라 music 서브디렉토리 없음)
- video.py/thumb.py: stderr truncation [-800:]/[-500:] — 진짜 에러 보이게
2026-05-08 22:50:13 +09:00
4f67cd02fa fix(music-lab): pipeline 응답에 track_title 포함 (LEFT JOIN music_library) 2026-05-07 17:43:55 +09:00
868906b8c6 test(music-lab): 풀 파이프라인 통합 테스트 (mock) 2026-05-07 17:37:15 +09:00
bd97cc1e97 chore(infra): pipeline env (OPENAI/YOUTUBE_OAUTH_*) + 폰트
music-lab 서비스에 OpenAI/YouTube OAuth 환경변수 + Claude 모델 변수 추가.
agent-office에도 CLAUDE_HAIKU_MODEL/CLAUDE_SONNET_MODEL 노출.
Alpine 기반 music-lab 이미지에 ttf-dejavu + fontconfig 설치하고
PIL이 참조하는 Debian 스타일 경로(/usr/share/fonts/truetype/dejavu)로
심볼릭 링크 생성하여 썸네일/그라디언트 폰트 로딩 보장.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 17:33:44 +09:00
7552ce4263 feat(agent-office): youtube_publisher 에이전트 + 30s 폴링
- YoutubePublisherAgent: 음악 파이프라인의 *_pending 상태를 폴링하여
  텔레그램 단일 채널로 단계별 검토 요청 발송, reply 수신 시 의도 분류
  후 music-lab에 feedback POST
- service_proxy: pipeline list/get/feedback/telegram-msg/lookup-by-msg
  헬퍼 5종 추가 (MUSIC_LAB_URL 사용)
- scheduler: 30초 interval로 poll_state_changes 실행
- telegram webhook: reply_to_message 가 파이프라인 메시지면
  youtube_publisher 로 라우팅 (슬래시 명령보다 우선)
- 테스트 4종 추가 (4 PASS)
2026-05-07 17:20:21 +09:00
17034ea6ea feat(agent-office): 텔레그램 자연어 의도 분류 2026-05-07 17:15:24 +09:00
fe60c8d330 feat(music-lab): pipeline 오케스트레이터 + 14 엔드포인트 2026-05-07 17:11:29 +09:00
4755e34c14 feat(music-lab): YouTube OAuth + resumable 업로드 2026-05-07 17:05:12 +09:00
ad1c721ba8 feat(music-lab): pipeline 4축 AI 검토 + 휴리스틱 폴백 2026-05-07 17:01:17 +09:00
1c705b0ef3 feat(music-lab): pipeline 메타데이터 LLM 생성 + 폴백 2026-05-07 16:58:03 +09:00
68dec2e53d feat(music-lab): pipeline 영상·썸네일 생성 2026-05-07 16:53:57 +09:00
e33a2310af feat(music-lab): AI 커버 생성 + 그라데이션 폴백
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 16:50:28 +09:00
fceca88db4 feat(music-lab): pipeline 상태 머신 2026-05-07 16:40:31 +09:00
d66a321982 feat(music-lab): pipeline 5개 DB 테이블 + 헬퍼
YouTube 음악 파이프라인 Task 1 — 신규 5개 테이블과 헬퍼 함수 추가.

테이블:
- video_pipelines: 파이프라인 단위 상태 머신 + 메타/리뷰 JSON
- pipeline_jobs: 단계별 비동기 작업 상태/시간
- pipeline_feedback: 텔레그램 피드백 이력
- youtube_oauth_tokens: 채널 OAuth refresh/access 토큰
- youtube_setup: 단일 행 설정 (메타 템플릿/커버 프롬프트/리뷰 가중치/임계값/비주얼/공개정책)

헬퍼:
- create_pipeline / get_pipeline / update_pipeline_state / list_pipelines
- increment_feedback_count / record_feedback / get_feedback_history
- create_pipeline_job / update_pipeline_job / list_pipeline_jobs
- get_youtube_setup / update_youtube_setup
- upsert_oauth_token / get_oauth_token / delete_oauth_token

테스트:
- tests/test_pipeline_db.py: 7개 테스트 (생성/상태/피드백/잡/셋업/OAuth)
- tests/conftest.py: freezegun 기반 freezer fixture 추가
- requirements.txt: freezegun>=1.4 추가

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 16:37:11 +09:00
e03d074222 docs(plan): Music YouTube 파이프라인 구현 계획 — 16 task
스펙 2026-05-07-music-youtube-pipeline-design.md를 16개 task로 분해.
TDD 패턴: 각 task = 실패 테스트 → 구현 → 통과 → 커밋.

태스크 흐름:
1. DB 5개 테이블 + 헬퍼
2. 상태 머신
3. Storage + 커버 (DALL·E + 폴백)
4. 영상/썸네일 (FFmpeg)
5. 메타데이터 (Claude Haiku)
6. AI 검토 4축 (Claude Sonnet + 휴리스틱)
7. YouTube OAuth + 업로드
8. 오케스트레이터 + 13 엔드포인트
9. agent-office 자연어 의도 분류
10. youtube_publisher 에이전트 + 30s 폴링
11. web-ui api.js 헬퍼
12. SetupTab
13. PipelineTab + 카드
14. YoutubeTab 6 서브탭 + Library 트리거
15. docker-compose env + nginx
16. 통합 테스트 + 수동 E2E

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 16:23:46 +09:00
2eeb98a723 docs(spec): Music YouTube 파이프라인 단계별 승인 자동화 설계
트랙 → 영상 → 발행까지 단계별 텔레그램 승인 워크플로 설계.
- 6단계 진행 바: 커버/영상/썸네/메타/AI검토/발행
- 자연어 의도 분류 (화이트리스트 + LLM 폴백)
- 반려 시 사용자 피드백 반영 재생성 (5회 한도)
- AI 최종 검토 4축 가중평균 (메타/정책/시청/트렌드)
- music-lab 5개 신규 테이블 + 12개 엔드포인트
- agent-office youtube_publisher 에이전트 + scheduler 폴링
- web-ui SetupTab + PipelineTab 신규 + Library 트리거

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 16:13:29 +09:00
657ffdc55f fix(agent-office): 아침 브리핑 직전 뉴스 스크랩 트리거 — 어제 뉴스 송출 방지
기존: stock-lab cron 스크랩(08:00)이 stock 에이전트 브리핑(07:30)보다 늦어
어제 8시 스크랩 결과만 DB에 있어 어제 뉴스가 요약·전송되었음.

수정: 에이전트 on_schedule에서 summarize 직전 /api/stock/scrap을 호출해
DB를 오늘 새벽 뉴스로 갱신한 뒤 요약. 스크랩 실패 시 경고 로그만 남기고
이전 데이터로 진행(브리핑 자체가 무산되는 것은 방지).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 15:31:55 +09:00
f54da7d46a chore(harness): 프로젝트 settings.json — git/docker/pytest allowlist + 민감파일 deny
체크인되는 프로젝트 권한 설정. read-only 명령(status/diff/logs/ps 등)을
사전 승인하여 권한 프롬프트 감소. .env / *.pem / *.key / lotto.db / stock.db
deny로 비밀·DB 직접 읽기 차단.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 15:06:12 +09:00
dc92c3d42d docs: 완료된 spec/plan 제거 + lotto 프리미엄 로드맵 보존
운영 중인 기능에 대한 design/plan 문서 일괄 삭제(20개 spec + 14개 plan).
미구현 pet-lab만 보존. lotto-premium-roadmap.md 신규 추가
(Phase 3 구독 모델 미구현 — STATUS.md에서 참조).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 15:06:04 +09:00
24a57f2b69 docs: STATUS.md — 서비스 현황 + 향후 계획 정리
10개 컨테이너 운영 상태와 Phase별 향후 작업 인덱스. CLAUDE.md를 보완.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 14:57:28 +09:00
b9d3242341 Merge branch 'feat/packs-lab-infra'
packs-lab 인프라 통합 + admin mint-token 구현 — 9 task TDD

- conftest.py로 테스트 HMAC secret 통일
- POST /api/packs/admin/mint-token 라우트 (Vercel HMAC → 일회성 upload 토큰)
- 기존 4 라우트 회귀 테스트 + DSM client mock 테스트
- Supabase pack_files DDL + 활성/삭제 인덱스
- docker-compose 18950 + nginx /api/packs/ 5GB streaming + env 8개
- PACK_BASE_DIR 환경변수화 (마운트 경로와 정합성 확보)
- web-backend CLAUDE.md 5곳 + workspace CLAUDE.md 1줄 갱신
- 24 tests passing
2026-05-06 03:35:37 +09:00