Castopod 기반의 팟캐스트 자동화 실험 환경입니다. Traefik 리버스 프록시, MariaDB, Redis를 포함한 Docker Compose 스택으로 구성되며 로컬에서 빠르게 테스트할 수 있도록 설계되었습니다.
- Docker Desktop (또는 호환 가능한 Docker 엔진)
podcast-server/.env파일 내 도메인 및 비밀번호 값 확인- CLI 파이프라인 실행을 위해서는
yt-dlp,ffmpeg가 설치되어 있어야 합니다.pipeline패키지를 설치하면yt-dlp는 의존성으로 내려오며, macOS 기준brew install ffmpeg로 FFmpeg를 준비할 수 있습니다.
cd podcast-server
docker compose up -d- Traefik이
CP_HOST환경변수에 정의된 호스트 이름으로 라우팅합니다. 기본값은localhost입니다. - 로컬 개발 기본값 기준 접속 주소:
- 공개 웹:
https://localhost - 로그인:
https://localhost/cp-auth/login - 관리자 콘솔:
https://localhost/cp-admin - Traefik 대시보드(옵션):
http://localhost:8080/dashboard/#/(루트 경로는 404를 반환합니다)
- 공개 웹:
- 처음 HTTPS 접속 시 브라우저에서 자가 서명 인증서 경고가 표시될 수 있습니다. 아래 "로컬 HTTPS 인증서 신뢰화" 절차를 완료하면 경고 없이 접속할 수 있습니다.
- 커스텀 도메인을 사용하려면
.env파일의CP_HOST,CP_BASE_URL,CP_MEDIA_BASEURL값을 원하는 도메인으로 수정한 뒤 Traefik DNS/인증서 구성을 조정하세요. - 새 팟캐스트를 만든 직후에는 기본값이 비공개(
Locked)입니다. Dashboard → Podcasts → Settings → Access에서 공개(Public)로 전환하지 않으면 에피소드 URL이 404로 응답합니다.
macOS에서 Traefik이 제공하는 https://localhost 인증서를 신뢰시키려면 한 번만 아래 절차를 수행하면 됩니다.
brew install mkcert(필요 시 Firefox는brew install nss도 함께 설치).mkcert -install로 로컬 신뢰 루트 인증서를 키체인에 추가합니다.- 저장소 루트에서 다음 명령으로 localhost 인증서를 생성합니다.
mkcert \ -cert-file podcast-server/traefik/certs/localhost.pem \ -key-file podcast-server/traefik/certs/localhost-key.pem \ localhost 127.0.0.1 ::1
- 파일은
.gitignore에 포함되어 있어 레포지토리에 커밋되지 않습니다.
- 파일은
- Traefik을 재시작해 새 인증서를 로드합니다.
cd podcast-server docker compose up -d traefik --force-recreate - 브라우저에서
https://localhost접속 후 경고 없이 열리면 완료입니다. 경고가 남아 있다면 브라우저 캐시/인증서 저장소를 비우고 다시 시도하세요.
| Phase | 설명 | 핵심 컴포넌트 |
|---|---|---|
| 1. API & 파이프라인 | Automation Service(FastAPI + SQLite)에서 채널/플레이리스트/스케줄을 관리하고 pipeline-run이 설정을 읽어 다운로드/메타데이터 생성 |
automation-service, pipeline |
| 2. TUI 관리 도구 | SSH 환경에서도 CRUD·즉시 실행을 처리할 수 있는 Textual 기반 CLI | pipeline_client.tui |
| 3. 웹 프런트(6-3) | React + Chakra UI 대시보드로 현황을 시각화, 추후 CRUD·수동 실행·알림을 추가 예정 | web-frontend |
- Castopod 채널 생성 마법사: Automation Service에 채널 메타데이터와 YouTube 플레이리스트 URL을 입력받는 엔드포인트를 추가하고, 파이프라인이 전체 다운로드 후 Castopod API로 채널/에피소드를 자동 생성
- 증분 스케줄러: 스케줄 엔티티에 Castopod 채널 매핑을 보관하고, supercronic→pipeline-run 체인에서 신규 업로드만 다운로드하여 해당 채널에 업로드
- 통합 관리 UI: TUI/web에서 채널/플레이리스트/스케줄 CRUD, 수동 실행, 실행 로그 상세, 향후 WebSocket 기반 실시간 알림까지 제공
세부 계획과 진행 상황은 AI/podcast_automation_beginner_guide.md와 체크리스트를 참고하세요.
- 경로:
automation-service/ - 로컬 실행 예시:
cd automation-service
conda create -n podcast python=3.12 -y
conda activate podcast
pip install -e .
uvicorn automation_service.main:app --reload- 위 명령은 uvicorn(ASGI 서버)을 이용해 FastAPI 앱을 개발 모드로 실행합니다.
uvicorn automation_service.main:app --reload는 FastAPI 앱을 개발 모드로 실행하며 코드 변경 시 서버를 자동으로 재시작합니다.- 기본 DB는 같은 디렉터리의
automation_service.db이며AUTOMATION_DATABASE_URL로 변경할 수 있습니다. - 웹 프런트엔드와 같은 다른 오리진에서 접근하려면 CORS 허용 목록을 지정해야 합니다. 기본값은
http://localhost:5173/http://127.0.0.1:5173이며, 추가가 필요하면AUTOMATION_CORS_ALLOW_ORIGINS="https://example.com,https://foo.bar"처럼 쉼표로 구분해 설정하세요. - 추가 기술 설명은
AI/technical_notes.md에서 확인할 수 있습니다. - 테스트 실행:
conda activate podcast cd automation-service pytest - 파이프라인 클라이언트:
conda activate podcast cd pipeline python -m pip install -r requirements-dev.txt python -m pytest - TUI 실행:
(단축키:
conda activate podcast pipeline-tui
Ctrl+A채널 추가,Ctrl+P플레이리스트 추가,Ctrl+S스케줄 추가,Ctrl+E수정,Ctrl+X삭제,Ctrl+R새로고침,Ctrl+T즉시 실행 로그,Ctrl+Q종료) - 파이프라인 실행:
conda activate podcast pipeline-run --dry-run --download-dir downloads
--dry-run옵션을 제거하면 실제 다운로드가 수행됩니다. (FFmpeg가 필요합니다.)- 실행 결과는 Automation Service
/runs엔드포인트에서도 확인할 수 있습니다. - 각 플레이리스트 폴더에는
metadata/playlist.json이 생성되어 에피소드별 제목·설명·썸네일·오디오 파일 경로가 정리됩니다. - 정사각형 커버 이미지는
metadata/artwork/playlist_cover.jpg및metadata/artwork/episodes/<video_id>.jpg로 저장되며playlist.json의square_cover·thumbnail_square필드와 연결됩니다.
React + Vite + Chakra UI로 만든 경량 대시보드입니다.
cd web-frontend
cp .env.example .env # 필요시 API 주소 수정
npm install
npm run dev -- --host- Automation Service가 실행 중이어야 하며 기본 API 주소는
http://127.0.0.1:8000입니다. - 브라우저에서
http://localhost:5173로 접속하면 채널/플레이리스트/스케줄 CRUD, 실행 로그 필터/수동 실행, 자동 새로고침을 포함한 현황판을 사용할 수 있습니다. - 스케줄 관리: 각 플레이리스트 카드의 스케줄 섹션에서 추가 버튼으로 새 스케줄을 만들고, 항목 옆 편집/삭제 아이콘으로 기존 스케줄을 수정·제거할 수 있습니다(상단 글로벌 스케줄 버튼 제거).
- 스케줄이 실행되면 해당 플레이리스트 작업이 자동으로 큐에 추가되고
pipeline-run이 트리거되어 Castopod 업로드까지 처리합니다(동일 플레이리스트에queued/in_progressJob이 있으면 중복 생성 없이 기존 작업을 사용). - 큐 러너가 백그라운드에서 대기하고 있다가
queued작업이 존재하고 파이프라인이 비어 있으면 자동으로pipeline-run을 재실행하므로, 스케줄이 동시에 여러 개 쌓여도 순차 처리됩니다. - 새로 생성된 스케줄 작업은 대시보드 오른쪽 상단에 토스트로 “스케줄 실행 시작” 알림이 표시되어 현재 어떤 플레이리스트가 동작 중인지 바로 확인할 수 있습니다.
- 큐 패널 상단 다운로드 폴더 열기 버튼을 누르면 Automation Service의
/downloads-browser페이지가 새 탭으로 열려, 하위 폴더와 파일을 탐색하고 각 항목을 직접 다운로드할 수 있습니다. - 작업 큐: 플레이리스트 카드의 큐에 추가 버튼으로 Castopod 채널 정보와 함께 큐에 적재 → 큐 패널에서 제거/실행(필요 시 전체 삭제 버튼으로 한 번에 정리) → 실행 시 Automation Service
/jobs와/runs에 기록되므로pipeline-run이 후속 다운로드를 수행합니다.
- 환경 준비 (최초 1회)
conda activate podcast cd ~/WorkSpace/Dev/proj.podcastserver.python/pipeline python -m pip install -r requirements-dev.txt
- Automation Service 실행 (새 터미널)
conda activate podcast cd ~/WorkSpace/Dev/proj.podcastserver.python/automation-service uvicorn automation_service.main:app --reload
- Docker로 실행하려면 `automation-service/.env.docker`를 필요에 맞게 수정한 뒤 같은 디렉터리에서 `docker compose up -d --build`를 수행하면 됩니다. Automation Service는 `http://127.0.0.1:18800`으로, 웹 대시보드는 `http://127.0.0.1:18080`으로 접근할 수 있습니다. SQLite/로그/다운로드 파일은 `automation-service/data/`에 저장되며, MariaDB(Castopod)에 접속하려면 `.env.docker`의 DB DSN을 `host.docker.internal` 등으로 조정하세요.
3. 파이프라인 실행
- 드라이런:
```bash
conda activate podcast
cd ~/WorkSpace/Dev/proj.podcastserver.python/pipeline
pipeline-run --dry-run --download-dir downloads
```
- 실제 다운로드:
```bash
conda activate podcast
cd ~/WorkSpace/Dev/proj.podcastserver.python/pipeline
pipeline-run --download-dir downloads
```
4. 산출물 위치
~/WorkSpace/Dev/proj.podcastserver.python/pipeline/downloads/<채널>/<플레이리스트>/metadata/playlist.json ~/WorkSpace/Dev/proj.podcastserver.python/pipeline/downloads/<채널>/<플레이리스트>/metadata/artwork/playlist_cover.jpg ~/WorkSpace/Dev/proj.podcastserver.python/pipeline/downloads/<채널>/<플레이리스트>/metadata/artwork/episodes/<video_id>.jpg
- 주요 REST 엔드포인트: `/channels`, `/playlists`, `/schedules`, `/runs` (OpenAPI: `/docs`).
- Castopod 업로드를 자동화하려면 Castopod 관리자(UI)에서 발급 받은 OAuth Client ID/Secret, 업로드 대상 팟캐스트 UUID 등이 필요합니다. 민감한 값은 프로젝트 루트의 `credentials.example.md`를 참고해 별도의 비공개 파일에 정리하고 `.env` 또는 Docker secrets로 주입하세요.
## 자주 사용하는 명령어 모음
| 작업 | 명령 |
|------|------|
| Automation Service 서버 실행 |`conda activate podcast && cd automation-service && uvicorn automation_service.main:app --host 127.0.0.1 --port 8800 --reload` |
| Automation Service 테스트 | `conda run -n podcast bash -lc 'cd automation-service && pytest'` |
| 파이프라인 러너 실행(드라이런) | `conda activate podcast && cd pipeline && pipeline-run --dry-run --download-dir downloads` |
| 파이프라인 러너 실행(실제 다운로드) | `conda activate podcast && cd pipeline && pipeline-run --download-dir downloads` |
| 파이프라인 Textual TUI | `conda activate podcast && pipeline-tui` |
| 웹 대시보드 개발 서버 | `cd web-frontend && npm install && npm run dev -- --host` |
| Castopod Docker 스택 기동 | `cd podcast-server && docker compose up -d` |
| Castopod 서버 실행 | `conda activate podcast
cd ~/WorkSpace/Dev/proj.podcastserver.python/automation-service
uvicorn automation_service.main:app --host 127.0.0.1 --port 8800 --reload
` |
| Castopod DB에서 UUID 확인 | `docker compose exec mariadb mariadb \
-u"$CP_DB_USERNAME" -p"$CP_DB_PASSWORD" "$CP_DB_DATABASE" \
-e "SELECT id, guid AS uuid, title FROM cp_podcasts;"` |
| Castopod DB 접속(패스워드 외부 입력) | `cd podcast-server && docker compose exec mariadb mariadb -ucastouser -p castopod` |
## 에피소드 수동 업로드 가이드
1. 브라우저에서 `https://localhost/cp-auth/login`으로 이동하거나 공개 페이지 우측 상단의 사용자 아이콘을 눌러 로그인 화면을 연 뒤 관리자 계정으로 로그인합니다.
- 성공적으로 로그인하면 `https://localhost/cp-admin`(또는 우측 상단 프로필 메뉴 → **Dashboard**)에서 관리 화면을 이용할 수 있습니다.
2. 왼쪽 사이드바에서 **Podcasts** → 원하는 채널 선택(또는 새 채널 생성) → **Episodes**를 클릭합니다.
3. **New episode** 버튼을 누르고 제목, 설명 등 기본 메타데이터를 입력합니다.
4. **Upload media** 단계에서 변환된 mp3 파일(예: `AI/tmp/youtube_test.mp3`)을 업로드합니다.
5. 업로드가 완료되면 발행 시점(즉시/예약)을 설정하고 **Publish**로 마무리합니다.
6. 발행 후 채널 페이지에서 RSS 주소가 정상적으로 갱신됐는지 확인합니다.
## 최초 설정 체크리스트
1. Castopod 초기 마법사에서 관리자 계정 및 첫 채널을 생성합니다.
2. 대시보드에서 RSS 주소를 확인하고 자동화 파이프라인 테스트에 활용합니다.
추가 참고 자료와 작업 기록은 `AI/` 디렉터리에 정리되어 있습니다.
