Authorized YTDL MCP
README.md
# YouTube Downloader MCP
ChatGPT 웹에서 YouTube 영상, 음원, 재생목록을 MP4/MP3로 준비하는 원격 MCP
App입니다. `~/Desktop/ytdl_cli`의
`yt-dlp + ffmpeg` 방식을 서버용으로 옮기고, ChatGPT 채팅 안에 실시간 진행 화면과
미디어 플레이어가 나타나는 MCP Apps 위젯을 추가했습니다.
## 제공 도구
- `inspect_youtube_url`: 영상/재생목록 자동 감지, 제목, 곡 수, 항목 목록 확인
- `start_download`: 단일 영상/음원의 비동기 작업 시작
- `start_playlist_download`: 재생목록 전체를 원래 순서대로 하나씩 다운로드
- `retry_download`: 재생목록 처리가 끝난 뒤 실패한 항목 하나를 다시 시도
- `connect_google_drive`: 브라우저에 암호화 저장된 Drive 연결 복원
- `load_drive_downloads`: Drive 전용 폴더의 최근 다운로드 내역 복구
- `load_drive_file`: 고유 파일명으로 Drive 파일 하나 복구
- `get_download_status`: 개별 항목 진행률 조회
- `get_batch_status`: 전체 및 모든 항목의 최신 상태 조회
- `render_download_widget`: 채팅 안에 실시간 진행 UI와 플레이어 표시
위젯은 1초마다 자동 갱신하며 각 항목의 받은 용량, 전체 예상 용량, 속도, ETA,
진행률과 상태를 보여줍니다. 배치·영상·마지막 진행 상태는 브라우저
`localStorage`에도 저장하므로 채팅을 다시 열 때 화면부터 즉시 복원합니다.
완료 파일은 Google Drive의 `YouTube Downloader MCP` 폴더에 한 개씩 업로드되고,
업로드 직후 Render 임시 파일은 삭제됩니다. 각 파일에는 고유 파일명이 붙으므로
서버 재시작 뒤에도 최근 내역이나 고유 파일명으로 다시 찾을 수 있습니다.
완료된 MP4/MP3는 위젯에서 개별 다운로드하거나
이동·크기 조절 가능한 미리보기로 재생할 수 있습니다. 모든 항목의 처리가 끝나면
성공한 파일 전체를 스트리밍 ZIP으로 받을 수 있고, 실패 항목을 재시도하는 동안에는
전체 다운로드가 다시 잠깁니다. 앱에서 정한 파일 용량 상한은 없으며, 다운로드는 서버 부담을 줄이기
위해 한 번에 1개씩 순서대로 처리합니다. Drive 인증 후 작업은 위젯 폴링과
분리된 백엔드 스레드에서 실행되므로 채팅을 닫아도 서버 프로세스가 살아 있는 동안
계속됩니다. 라이브 스트림은 지원하지 않습니다.
> 애플리케이션의 용량 제한을 없애도 호스팅 서버의 디스크·메모리·대역폭 한계는
> 없어지지 않습니다. 특히 무료 Render 인스턴스는 대용량 영상과 큰 재생목록을
> 안정적으로 처리하는 무제한 저장소가 아닙니다.
## 로컬 실행
Python 3.10 이상과 `ffmpeg`가 필요합니다.
```bash
cd ~/Desktop/ytdl_mcp
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
ALLOWED_HOSTS=localhost,127.0.0.1 \
.venv/bin/uvicorn server:app --host 127.0.0.1 --port 8000
```
- MCP URL: `http://127.0.0.1:8000/mcp`
- 상태 확인: `http://127.0.0.1:8000/health`
MCP Inspector에서는 전송 방식을 `Streamable HTTP`로 선택합니다.
```bash
npx @modelcontextprotocol/inspector
```
## Render 무료 배포
1. 이 폴더를 새 GitHub 저장소에 push합니다.
2. Render에서 **New → Blueprint**를 선택하고 저장소를 연결합니다.
3. 환경 변수를 다음처럼 설정합니다.
- `PUBLIC_BASE_URL=https://서비스이름.onrender.com`
- `ALLOWED_HOSTS=서비스이름.onrender.com`
- `MAX_CONCURRENT_JOBS=1`
- `JOB_TTL_SECONDS=1800`
- `YTDLP_COOKIE_FILE=/etc/secrets/youtube-cookies.txt`
- `GOOGLE_DRIVE_CLIENT_ID=Google OAuth 웹 클라이언트 ID`
- `GOOGLE_DRIVE_CLIENT_SECRET=Google OAuth 클라이언트 보안 비밀`
- `GOOGLE_DRIVE_SETUP_TOKEN=Render의 Generate로 만든 긴 임의 문자열`
- `GOOGLE_DRIVE_FOLDER_NAME=YouTube Downloader MCP`
- `SELF_KEEPALIVE_ENABLED=true`
- `SELF_KEEPALIVE_INTERVAL_SECONDS=420`
`GOOGLE_DRIVE_REFRESH_TOKEN`은 선택 사항입니다. 위젯 로그인 방식을 쓰면 비워
둬도 되고, 서버에 고정 연결할 때만 Render 비밀 환경 변수로 설정합니다.
4. YouTube가 서버 요청에 로그인을 요구한다면 Render의 **Environment → Secret
Files → Add Secret File**에서 다음 파일을 추가합니다.
- Filename: `youtube-cookies.txt`
- Contents: Netscape 형식으로 내보낸 `youtube.com` 쿠키 내용
쿠키 파일은 GitHub나 일반 환경 변수에 넣지 마세요. 서버는 Render의 읽기 전용
비밀 파일을 작업마다 별도의 임시 파일로 복사해 사용하며, 임시 파일은 작업
종료 시 삭제합니다. 배포 Docker 이미지에는 최신 YouTube JavaScript challenge
처리를 위한 Deno와 `yt-dlp-ejs`도 포함됩니다.
5. 배포 후 다음 주소를 확인합니다.
- 상태: `https://서비스이름.onrender.com/health`
- MCP: `https://서비스이름.onrender.com/mcp`
`/health`의 `youtube.cookies_configured`가 `true`, `youtube.js_runtime`이
`"deno"`이면 서버가 쿠키와 JavaScript 런타임을 인식한 것입니다.
### YouTube 쿠키 내보내기
YouTube 계정 쿠키는 비밀번호와 비슷한 비밀정보입니다. 필요한 경우에만 다음처럼
내보내세요.
1. 브라우저의 새 시크릿/비공개 창 하나만 열고 YouTube에 로그인합니다.
2. 같은 탭에서 `https://www.youtube.com/robots.txt`로 이동합니다.
3. 신뢰할 수 있는 로컬 전용 도구로 `youtube.com` 쿠키만 Netscape
`cookies.txt` 형식으로 내보냅니다.
4. 곧바로 시크릿/비공개 창을 닫고, 내보낸 내용을 Render Secret File에
`youtube-cookies.txt`라는 이름으로 붙여 넣습니다.
5. 로컬 쿠키 파일은 Render 설정 완료 후 안전하게 삭제합니다.
계정 쿠키를 사용하면 YouTube 계정이 일시적 또는 영구적으로 제한될 위험이
있습니다. 요청량을 낮게 유지하고 본계정 쿠키를 서버에 보관하는 것은 피하는 것이
안전합니다. 쿠키를 교체하면 Render에서 다시 배포한 뒤 상태 주소를 확인하세요.
활성 다운로드나 Drive 업로드가 있으면 서버가 7분마다 자신의 `/health`에 요청해
무료 인스턴스의 유휴 정지를 방지하려고 시도합니다. 작업이 없으면 자가 요청도
자동으로 멈춥니다. 다만 Render가 자가 요청을 항상 활동으로 인정한다고 보장하지
않고 무료 인스턴스를 임의로 재시작할 수도 있으므로, 실행 중 작업의 절대적인
지속성을 보장하는 장치는 아닙니다. 이미 Drive에 업로드된 파일은 영향을 받지
않습니다.
## Google Drive OAuth 설정
1. Google Cloud Console에서 프로젝트를 만들고 **Google Drive API**를
활성화합니다.
2. OAuth 동의 화면을 구성합니다. 앱이 테스트 모드라면 사용할 Google 계정을
테스트 사용자로 추가합니다.
3. **OAuth client ID → Web application**을 만듭니다.
4. 승인된 리디렉션 URI에 다음 주소를 정확히 추가합니다.
`https://서비스이름.onrender.com/drive/callback`
5. 발급된 Client ID와 Client secret을 Render 환경 변수에 입력하고 다시
배포합니다.
6. ChatGPT에서 다운로드를 처음 시작하면 위젯의 **Google 계정으로 로그인**을
누릅니다. 서버는 `drive.file` 범위만 요청하고, 로그인 성공 즉시
`YouTube Downloader MCP` 폴더를 검색해 없으면 자동 생성합니다.
로그인 후 브라우저에는 원본 OAuth refresh token이 아니라
`GOOGLE_DRIVE_SETUP_TOKEN`으로 암호화한 연결값만 저장됩니다. 새 채팅이나 오래된
채팅을 열면 위젯이 이 값을 이용해 Drive 연결과 최근 내역을 자동 복구합니다.
## ChatGPT 웹에 연결
ChatGPT 웹의 **Settings → Apps → Advanced settings → Developer mode**를 켠 뒤,
새 앱/커넥터의 원격 MCP URL로 `https://서비스이름.onrender.com/mcp`를 입력합니다.
계정 플랜과 워크스페이스 관리자 정책에 따라 메뉴와 허용 범위가 다를 수 있습니다.
예시 요청:
> 이 영상을 720p MP4로 준비해 줘:
> https://youtu.be/...
재생목록 예시:
> 이 재생목록을 각 항목 192kbps MP3로 준비하고 진행 화면을 보여줘:
> https://www.youtube.com/playlist?list=...
Drive 내역 예시:
> Google Drive에 저장된 최근 다운로드 20개를 진행 화면으로 보여줘.
고유 파일명으로 찾기:
> 고유 파일명 `AbCd123__example.mp4` 파일을 Google Drive에서 불러와줘.
ChatGPT는 시작 도구 호출 후 `render_download_widget`을 호출합니다. 이후 위젯이
`get_batch_status`를 1초마다 직접 호출하므로 ChatGPT 답변을 계속 새로 만들지
않고 카드 내부의 진행 상태만 자연스럽게 갱신합니다.
## 운영 전 필수 보안
현재 구성은 개인 개발 테스트용입니다. URL을 아는 누구나 작업을 만들 수 있으므로
공개 배포·공개 등록 전에는 OAuth 2.1 사용자 인증, 사용자별 할당량, 외부 작업
저장소/오브젝트 스토리지, 모니터링을 추가해야 합니다.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues