afluakd9's picture
Initial commit without binary files
bbe872c
|
Raw History Blame Contribute Delete
5.68 kB
# AGENTS.md β€” ReClip Telegram Bot
Compact instruction file for OpenCode sessions working in this repository.
## Project Overview
Self-hosted Telegram bot that downloads media from YouTube, TikTok, Instagram, and 1000+ other sites. Built from four Dockerized Python services:
| Service | Tech | Port | Role |
|---------|------|------|------|
| `reclip/` | Flask + yt-dlp | 8899 | Download engine with REST API |
| `bot/` | python-telegram-bot + httpx | β€” | Telegram bot client |
| `dashboard/` | FastAPI + aiosqlite + Jinja2 | 8080 | Admin panel with stats |
| `telegram-bot-api` | aiogram/telegram-bot-api | 8081 | Self-hosted Bot API for 2GB uploads |
## Running Locally (No Docker)
Start services in this order β€” each needs its own terminal:
```bash
# 1. Reclip (download engine)
cd reclip
pip install flask yt-dlp
python app.py # listens on 8899
# 2. Dashboard (admin panel)
cd dashboard
pip install -r requirements.txt
ADMIN_PASSWORD=changeme DB_PATH=./reclip.db \
uvicorn main:create_app --factory --host 0.0.0.0 --port 8080
# 3. Bot (Telegram client)
cd bot
pip install -r requirements.txt
BOT_TOKEN=<token> RECLIP_URL=http://localhost:8899 \
DOWNLOADS_PATH=../reclip/downloads python bot.py
```
The bot waits up to 60s for the self-hosted Bot API server on startup. In Docker it talks to `http://telegram-bot-api:8081`; locally you need that container running too, or the bot will warn and start anyway (uploads >20MB will fail).
## Running Tests
```bash
# Bot tests
cd bot
pip install -r requirements.txt
pip install pytest pytest-asyncio
python -m pytest tests/ -v
# Dashboard tests
cd dashboard
pip install -r requirements.txt
pip install pytest pytest-asyncio httpx
python -m pytest tests/ -v
```
**Critical:** Dashboard tests must set `DB_PATH`, `ADMIN_PASSWORD`, and `SECRET_KEY` **before** importing `db.py` or `main.py`. The test files do this via `os.environ` at the top of the file. If you add new dashboard test files, copy that pattern or imports will create the DB in the wrong place.
## Architecture Notes
### Reclip API Surface (`reclip/app.py`)
- `POST /api/info` β€” returns title, thumbnail, duration, uploader, extractor, and available quality formats
- `POST /api/download` β€” spawns a threaded yt-dlp download, returns `job_id`
- `GET /api/status/<job_id>` β€” returns `status` (`downloading` | `done` | `error`), progress dict, file metadata
- `GET /api/file/<job_id>` β€” serves finished file as attachment
### Bot β†’ Dashboard Events (`bot/event_client.py`)
The bot sends fire-and-forget HTTP POSTs to `DASHBOARD_URL/api/events` with `type: download_start | progress | download_done | download_error`. These are best-effort; failures are logged at DEBUG and ignored.
### Download Flow
1. User sends URL β†’ bot calls `reclip_client.get_info()`
2. Bot shows thumbnail + format buttons (MP4 / MP3)
3. MP4 path shows quality picker; MP3 path starts audio download directly
4. Bot calls `start_download()` β†’ polls `poll_status()` every 2s
5. Progress updates edit the Telegram message in real time
6. On `status: done`, bot uploads the file via the self-hosted Bot API
### Post-Processing in Reclip
- yt-dlp downloads to `DOWNLOADS_PATH/<job_id>.<ext>`
- AV1/VP9/VP8 videos are transcoded to H264 via ffmpeg (Telegram compatibility)
- Other MP4s get `-movflags +faststart` via ffmpeg copy
- File duration/width/height extracted with ffprobe
### Concurrency & Limits
- Reclip uses `threading.Semaphore(MAX_CONCURRENT_DOWNLOADS)` (default 3)
- Each download has a hard timeout of `DOWNLOAD_TIMEOUT` seconds (default 900)
- Bot has a 10-minute in-memory session TTL (`STATE_TTL = 600`)
### Cleanup (`bot/cleanup.py`)
Background asyncio task that runs every `CLEANUP_INTERVAL_SECONDS` (default 300):
1. Deletes files older than `CLEANUP_MAX_AGE_HOURS` (default 1)
2. If disk usage exceeds `CLEANUP_MAX_DISK_MB` (default 5000), deletes oldest files first
## Environment Variables
Required:
- `BOT_TOKEN` β€” from @BotFather
- `TELEGRAM_API_ID` / `TELEGRAM_API_HASH` β€” from my.telegram.org
- `DASHBOARD_PASSWORD` β€” dashboard login password
Commonly overridden for local dev:
- `RECLIP_URL=http://localhost:8899`
- `TELEGRAM_BOT_API_URL=http://localhost:8081`
- `DOWNLOADS_PATH=./downloads`
- `DB_PATH=./reclip.db`
- `SECRET_KEY=change-me` (dashboard cookie signing)
See `.env.example` for the full list with defaults.
## Code Conventions
- **No linting / formatting / typechecking tooling is configured.** There is no black, ruff, mypy, or pre-commit setup.
- Imports use `sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))` in test files to reach parent modules.
- Dashboard DB uses SQLite with WAL mode (`PRAGMA journal_mode=WAL`).
- Auth is session-cookie based (itsdangerous URLSafeTimedSerializer), not JWT or OAuth.
- All event_client calls are fire-and-forget with a 2-second timeout; they must never crash the bot.
## Common Gotchas
- **Shared volume:** `downloads` is a Docker shared volume between reclip, bot, and dashboard. Locally, all three services must point to the same filesystem path.
- **Bot API server first startup:** The self-hosted Bot API downloads Telegram server data on first run. This can take 30–60 seconds. The bot polls `wait_for_bot_api()` for up to 60s.
- **Dashboard DB path:** `_db_path()` re-reads `DB_PATH` from env on every call so tests can override it dynamically.
- **Reclip job state is in-memory:** `jobs = {}` in `reclip/app.py` is not persistent. Restarting reclip loses active download state.
- **FFmpeg dependency:** The reclip Dockerfile installs ffmpeg. Locally you need it installed (`apt install ffmpeg` or equivalent).