smovie-mcp
# smovie
A small download service for YouTube and TikTok, built to run all the time on the Mac mini.
You send it a URL, it adds a job to a queue (to run now, in turn, or at a set time), and
[yt-dlp](https://github.com/yt-dlp/yt-dlp) downloads the video. You follow progress on a
view-only dashboard, through the REST API, or through an MCP server that agents can use.
```
URL ──► POST /api/jobs ──► SQLite queue ──► worker (yt-dlp, N at a time) ──► data/downloads/<job>/
▲ │
dashboard (view) ──────┤ ▼
MCP server (agents) ───┘ GET /api/jobs/<id>/file
```
## Run
```bash
uv sync
uv run smovie # http://localhost:8787 (dashboard) /docs (API)
```
To install it on the mini so it starts at login and restarts if it crashes:
```bash
SM_API_TOKEN=change-me deploy/install-launchd.sh
```
Requires `ffmpeg` (`brew install ffmpeg`) to merge video and audio.
### Settings (environment variables)
| Var | Default | |
|---|---|---|
| `SM_DATA_DIR` | `./data` (launchd: `~/smovie-data`) | DB, downloads, log |
| `SM_PORT` | `8787` | |
| `SM_CONCURRENCY` | `2` | parallel downloads |
| `SM_API_TOKEN` | none | if set, API needs `Authorization: Bearer …` (dashboard: open `/?token=…` once) |
| `SM_COOKIES_FILE` | none | Netscape cookies.txt, for YouTube "confirm you're not a bot" errors |
| `SM_API_URL` | `http://127.0.0.1:8787` | where the MCP server finds the API |
## API
| Method | Path | |
|---|---|---|
| POST | `/api/jobs` | `{"url", "when": "now"\|"queue"\|"schedule", "run_at"?, "audio_only"?, "max_height"?, "purpose"?, "studio"?}` |
| GET | `/api/jobs?status=queued,downloading&limit=100` | list jobs |
| GET | `/api/jobs/{id}` | status, progress, speed, eta, title, error |
| GET | `/api/jobs/{id}/file` | download the finished file |
| POST | `/api/jobs/{id}/cancel` | cancel a scheduled, queued or running job |
| POST | `/api/jobs/{id}/retry` | requeue a failed or canceled job |
| DELETE | `/api/jobs/{id}?delete_file=true` | remove job (and its file) |
| GET | `/api/stats`, `/api/health` | |
`when=now` moves the job to the front of the queue (it still waits if all download slots are busy).
`run_at` without a timezone means the mini's local time.
```bash
curl -X POST localhost:8787/api/jobs -H 'content-type: application/json' \
-d '{"url":"https://www.tiktok.com/@user/video/123","when":"schedule","run_at":"2026-10-29T02:00"}'
curl -OJ localhost:8787/api/jobs/<id>/file
```
Jobs that were downloading when the service stopped go back to the queue when it restarts.
### Animation Studio (codex-3d)
`"purpose": "depth"` (or `"action"`) on a job hands the finished file to Animation Studio, which polls for done jobs once a minute and
takes each in once (hardlinked, so deleting the job keeps its copy). `"studio"` carries its fields: `id`, `name`, `rights`, and
optionally `window` [start, end] in seconds with `scene`, for a depth reference of that window. Depth and action downloads are video, capped at 1080.
```bash
curl -X POST localhost:8787/api/jobs -H 'content-type: application/json' \
-d '{"url":"https://youtu.be/…","purpose":"depth","studio":{"id":"got-olenna","name":"Olenna","rights":"non-commercial reference"}}'
```
## Agents
- **MCP:** `.mcp.json` registers `smovie-mcp` (stdio). The tools are `submit_video`, `wait_for_job`,
`save_video`, `list_jobs`, `get_job`, `cancel_job`, `retry_job` and `delete_job`. To use it from
another machine, set `SM_API_URL=http://<mini>.local:8787` (and `SM_API_TOKEN`).
- **Skill:** `.claude/skills/video-download/SKILL.md` covers the same flow with curl.
## Maintenance
YouTube and TikTok change often, so keep yt-dlp up to date. Running `deploy/update-ytdlp.sh` weekly is enough.
## Roadmap
- Trimming: add `trim: {start, end}` to the job options and run `ffmpeg -ss … -to … -c copy` after the download.
- Cloud: the same service in a container. Expect YouTube to block datacenter IPs, so plan on cookies or a residential proxy.
## License
MIT, see [LICENSE](LICENSE). Only download content you have the right to; downloading may break YouTube's and TikTok's terms of service.
TDQS
Scored across 8 tools
Each tool has a clearly distinct role across the job lifecycle: submit, inspect, list, wait, save, cancel, retry, and delete. The nearest pair, get_job and wait_for_job, is differentiated by wait_for_job blocking until a terminal state or timeout.
All eight tools follow a consistent snake_case verb_noun pattern (get_job, submit_video, list_jobs, wait_for_job, save_video, cancel_job, retry_job, delete_job). No mixed casing or inconsistent verb styles appear.
Eight tools is well-scoped for a video download queue service, covering submission, monitoring, waiting, file retrieval, and lifecycle management without bloat. Each tool earns its place in the workflow.
The surface covers the full job lifecycle: create/submit, get, list, wait, save finished files, cancel, retry, and delete. Scheduling and Animation Studio handoff are handled through submit_video, leaving no obvious dead ends for the stated purpose.