English | 简体中文
MT-Engine is a torrent search, freeleech monitor, automated download, and qBittorrent dashboard tool for M-Team.
- RADAR: M-Team torrent search with filters for channel, category, country/region, resolution, video codec, audio codec, discount type, and sorting.
- HOME: A family-oriented 4K media radar that surfaces read-only entries for English-language TV, foreign-language films, Japanese/Korean dramas, Chinese-language series, and classic collections.
- SONAR: Freeleech monitor that shows the real cached Free torrents, with filters for size, seeders, remaining time, status, channel, and pagination.
- PILOT: Automated download and cleanup rules built around a seeding strategy of "high download demand, low upload supply, enough FREE time, suitable size", with budget control and cleanup.
- PANEL: A qBittorrent dashboard showing upload/download, share ratio, active torrents, trend charts, and a torrent monitor table.
Current version: 6.6.2
6.6.2 continues to refine the family-oriented 4K media radar with wider candidate windows, tighter regional rails, read-only Home behavior, and improved poster fallback coverage.
See CHANGELOG.md for the full history.
- Python 3.12+
- FastAPI
- httpx
- SQLite
- Pydantic
- Next.js 16.2.6 App Router
- Node.js 22 LTS
- React 19.2.3
- TypeScript
- Tailwind CSS 4
- Coss UI registry/style
- Base UI primitives
- Recharts 2.15.4
- SWR
- Sonner
- Lucide React
- Docker
- Docker Compose
- Single-container deployment: FastAPI serves the API and also hosts the Next.js static export.
- Docker and Docker Compose
- An M-Team API Token
- An M-Team User ID (optional, used to show seeding/download status)
- qBittorrent Web UI (optional, used for downloads, auto cleanup, and PANEL)
git clone https://github.com/kevinplus66/mt-engine.git
cd mt-enginecp .env.example .envEdit .env:
PUID=1000
PGID=1000
MT_ENGINE_COMMIT=
MT_ENGINE_BIND_HOST=0.0.0.0
MT_TOKEN=your_api_token_here
MT_USER_ID=
MT_SITE_URL=https://kp.m-team.cc
REFRESH_INTERVAL=900
FREE_REFRESH_FAILURE_BACKOFF_SECONDS=7200
PANEL_COLLECT_INTERVAL=60
MEDIA_WALL_REFRESH_INTERVAL=21600
MEDIA_WALL_REFRESH_FAILURE_BACKOFF_SECONDS=7200
MEDIA_WALL_STARTUP_DELAY=420
MEDIA_WALL_METADATA_TTL=604800
MEDIA_WALL_MAX_METADATA_FETCHES=40
MEDIA_WALL_DOUBAN_POSTER_FETCHES=3
API_DELAY=6
MTEAM_PROACTIVE_ENABLED=true
DEBUG=false
PUSHPLUS_TOKEN=
QBITTORRENT_URL=http://<QB_HOST_IP>:8080
QBITTORRENT_USER=
QBITTORRENT_PASSWORD=
DOWNLOADS_PATH=/volume1/downloads
PILOT_SAVE_PATH=/downloads/mt_free_farmFor Docker deployments, QBITTORRENT_URL must use the actual LAN IP of your NAS or host — not localhost. qBittorrent 4.6.7 is known-good; qBittorrent 5 is supported through the compatibility layer that negotiates the pause/resume endpoint family. For NAS deployments, pin your qBittorrent image tag until you intentionally upgrade.
If you configure qBittorrent, use your own Web UI username and password; do not reuse an empty password, a default password, or a temporary setup password. Deployment secrets are direct environment variables: set MT_TOKEN, optional QBITTORRENT_PASSWORD, and optional PUSHPLUS_TOKEN in .env; do not commit real values.
MT-Engine has no built-in public authentication. Keep it LAN-only, or put public access behind external auth/reverse proxy.
To avoid M-Team rate limiting, FREE auto-refresh reuses the current cache during a refresh interval and backs off after M-Team failures. PILOT keeps qB task identity conservative so managed cleanup avoids unmanaged torrents.
Media-wall refresh still rotates sources on a stagger, refreshing only one source at a time; when an M-Team source refresh fails it keeps serving the old cache and retries after a MEDIA_WALL_REFRESH_FAILURE_BACKOFF_SECONDS cooldown to avoid hammering the API while rate-limited.
export MT_ENGINE_COMMIT="$(git rev-parse --short HEAD)"
# Optional: use a prebuilt or NAS-loaded image instead of the default mt-engine:<commit>
# export MT_ENGINE_IMAGE="mt-engine:${MT_ENGINE_COMMIT}"
docker compose up -d --buildThe first build installs the front-end and back-end dependencies and builds the Next.js static output. Docker builds use the hardcoded node:22-alpine and python:3.12-slim base images; keep qBittorrent on a pinned NAS image tag until you intentionally upgrade it.
http://<NAS-IP>:5050
By default Compose binds the port to 0.0.0.0:5050, suitable for direct LAN access on a NAS. To allow host-local access only, set MT_ENGINE_BIND_HOST=127.0.0.1 in .env. MT-Engine has no built-in public authentication; keep it LAN-only, or put public access behind external auth/reverse proxy.
docker compose ps
./scripts/verify-deploy.sh --base-url http://127.0.0.1:5050scripts/verify-deploy.sh is read-only: it only calls GET /health, /api/status, /api/home/media-wall, /api/auto-delete/status, /api/pilot/stats, and optionally /api/pilot/dry-run with --pilot-dry-run.
Example health response:
{"status":"ok","timestamp":"2026-05-26T01:34:01.425811+08:00","torrents_count":400}The status endpoint returns runtime version, commit, cache state, dependency status, and non-sensitive config, which can be used for deployment acceptance:
curl http://localhost:5050/api/statusIf you use PILOT auto-download, also confirm:
enabledin/api/auto-delete/statusistrueor matches your expectation.is_runningin/api/pilot/statsistrue, unless you have explicitly disabled the download and cleanup strategy./api/pilot/dry-runreturns a read-only preview of candidates and disk budget without mutating qB.warningsin/api/statusis empty, and both the qBittorrent and M-Team dependencies are healthy. qB 4.6 and qB 5 are supported by fallback between qB Web API endpoint families./api/home/media-wallreturnsrails; if it is briefly empty right after the first deploy, just wait for the first background refresh to finish.
| Variable | Required | Description | Default |
|---|---|---|---|
PUID |
No | Container run user ID, used for NAS file permissions | 1000 |
PGID |
No | Container run group ID | 1000 |
MT_ENGINE_COMMIT |
No | Build metadata (recommended: git rev-parse --short HEAD) |
local |
MT_ENGINE_BIND_HOST |
No | Web service bind address; LAN-accessible by default on NAS, set to 127.0.0.1 for host-only access |
0.0.0.0 |
MT_TOKEN |
Yes | M-Team API Token, set directly in .env |
- |
MT_USER_ID |
No | M-Team User ID, used for user seeding/download status | - |
MT_SITE_URL |
No | M-Team site URL | https://kp.m-team.cc |
REFRESH_INTERVAL |
No | SONAR background refresh interval, in seconds; current production steady-state is 15 minutes, not recommended below 300 | 900 |
FREE_REFRESH_FAILURE_BACKOFF_SECONDS |
No | Backoff after a FREE refresh failure, in seconds; keeps the old cache and retries after cooldown | 7200 |
API_DELAY |
No | M-Team API request interval, clamped to 3-10 seconds; default is 6 seconds, below 3 seconds tends to trigger dynamic rate limiting | 6 |
MTEAM_PROACTIVE_ENABLED |
No | Whether system-owned background jobs may call M-Team; set false for a manual-only instance while keeping user-initiated RADAR/search/download available |
true |
PANEL_COLLECT_INTERVAL |
No | PANEL data collection interval, in seconds | 60 |
MEDIA_WALL_REFRESH_INTERVAL |
No | HOME media-wall per-source background refresh interval, in seconds; defaults to 6 hours, not allowed below 21600; missing sources bootstrap no more than 90 minutes apart before normal staggering resumes | 21600 |
MEDIA_WALL_REFRESH_FAILURE_BACKOFF_SECONDS |
No | Backoff after a HOME media-wall source refresh failure, in seconds; keeps the old cache and retries after cooldown | 7200 |
MEDIA_WALL_STARTUP_DELAY |
No | Delay before the media wall's first refresh after container start, to avoid overlapping SONAR's first refresh | 420 |
MEDIA_WALL_METADATA_TTL |
No | Cache TTL for M-Team media metadata such as posters, year, and summary, in seconds | 604800 |
MEDIA_WALL_MAX_METADATA_FETCHES |
No | Metadata backfill budget per full rotation cycle; a single source refresh uses about 1/4 and prioritizes media never enriched before refreshing old entries | 40 |
MEDIA_WALL_DOUBAN_POSTER_FETCHES |
No | Max number of Douban-page posters fetched at low frequency per media-wall source refresh; used only when M-Team metadata lacks a poster | 3 |
PUSHPLUS_TOKEN |
No | PushPlus WeChat push Token | - |
QBITTORRENT_URL |
No | qBittorrent Web UI URL; use a pinned qB image tag on NAS until intentionally upgrading | - |
QBITTORRENT_USER |
No | qBittorrent Web UI username | - |
QBITTORRENT_PASSWORD |
No | qBittorrent Web UI password | - |
DOWNLOADS_PATH |
No | Download directory on the host, mounted read-only as /downloads in the container; PILOT fails closed if this path is missing or unreadable |
/downloads |
PILOT_SAVE_PATH |
No | PILOT save path, usually a subdirectory under /downloads |
/downloads/mt_free_farm |
DEBUG |
No | Local debug switch; suitable only for LAN/development environments | false |
MT_ENGINE_IMAGE |
No | Compose image tag to run; useful for prebuilt/offline images with docker compose up -d --no-build |
mt-engine:${MT_ENGINE_COMMIT:-local} |
- Omitting
delete_fileson/api/panel/torrents/deletepreserves downloaded files; files are removed only when the request explicitly setsdelete_files: true. - If
/downloadsor the configured save path cannot be measured, PILOT fails closed and skips downloads instead of guessing available space. - RADAR/PILOT managed cleanup avoids unmanaged torrents.
disk_pressure_elimination_ratiois an opt-in PILOT cleanup guard for fixed-size disks: when the remaining download budget is smaller than the configuredmin_size_gb, dry-run can preview and cleanup can remove the lowest-scored mature seed; the default is0, so existing deployments keep their current cleanup policy until explicitly enabled.
pip install -r requirements-dev.txt
uvicorn app.main:app --host 0.0.0.0 --port 5050 --reloadFront-end local development uses Node.js 22 LTS.
cd frontend
npm install
npm run devDefault URL:
http://localhost:3000
If 3000 is in use:
npm run dev -- -p 3001In dev mode the front end proxies the API via the rewrites in frontend/next.config.ts, proxying to http://localhost:5050 by default. To connect to a different backend, set this in frontend/.env.local:
NEXT_PUBLIC_API_URL=http://127.0.0.1:5051If the browser can reach the NAS IP but the Next dev proxy reports EHOSTUNREACH, the Node process cannot connect to that address directly; use a locally reachable bridge proxy address such as 127.0.0.1:5051.
mt-engine/
├── app/ # FastAPI backend
│ ├── main.py # Application entry point
│ ├── config.py # Configuration and version reading
│ ├── core/ # Background tasks, PILOT, rules, and alerts
│ ├── routes/ # API routes
│ └── services/ # M-Team, qBittorrent, PushPlus, PANEL DB
├── frontend/ # Next.js frontend
│ ├── app/ # App Router pages
│ ├── components/ # Page components and Coss/Base UI components
│ ├── hooks/ # SWR and interaction hooks
│ ├── lib/ # API client, types, sorting, and utilities
│ └── providers/ # Theme/SWR providers
├── data/ # SQLite and runtime config data
├── Dockerfile # Multi-stage image build
├── docker-compose.yml # Compose deployment config
├── scripts/ # Deployment and maintenance scripts
├── AGENT_DEPLOY.md # AI-agent-assisted deployment guide
├── CHANGELOG.md # Version history
├── requirements.txt # Python runtime dependencies
└── requirements-dev.txt # Python local test dependencies
# Start or update
export MT_ENGINE_COMMIT="$(git rev-parse --short HEAD)"
docker compose up -d --build
# Stop
docker compose down
# View logs
docker compose logs -f
# Restart
docker compose restart
# Health checks
curl -sf http://localhost:5050/health
curl -sf http://localhost:5050/api/status
curl -sf http://localhost:5050/api/home/media-wall
curl -sf http://localhost:5050/api/auto-delete/status
curl -sf http://localhost:5050/api/pilot/stats
curl -sf http://localhost:5050/api/pilot/dry-runcd /path/to/mt-engine
cp .env.example .env
# Edit .env and fill in required fields such as MT_TOKEN
export MT_ENGINE_COMMIT="$(git rev-parse --short HEAD)"
docker compose up -d --build
docker compose ps
curl -sf http://localhost:5050/health
curl -sf http://localhost:5050/api/status
curl -sf http://localhost:5050/api/home/media-wall
curl -sf http://localhost:5050/api/auto-delete/status
curl -sf http://localhost:5050/api/pilot/stats
curl -sf http://localhost:5050/api/pilot/dry-runIf the NAS cannot access GitHub non-interactively, you can deploy from your local machine using the bundle script:
NAS_HOST="<NAS_IP>" NAS_USER="<SSH_USER>" NAS_PATH="<INSTALL_PATH>" ./scripts/deploy-nas.shIf the NAS cannot pull Docker Hub base images or should not build at all, create a Docker image archive on a local machine, then pass it to the same deploy script:
PREBUILT_IMAGE_ARCHIVE="/path/to/mt-engine.tar" NAS_HOST="<NAS_IP>" NAS_USER="<SSH_USER>" NAS_PATH="<INSTALL_PATH>" ./scripts/deploy-nas.shRequired env vars are NAS_HOST, NAS_USER, and NAS_PATH; DEPLOY_REF, VERIFY_DEPLOY, and VERIFY_PILOT_DRY_RUN stay optional. With PREBUILT_IMAGE_ARCHIVE, scripts/deploy-nas.sh uploads and loads the archive on the NAS, sets MT_ENGINE_IMAGE, and starts Compose without building on the NAS.
cd /path/to/mt-engine
git log --oneline -n 5
git checkout <last-known-good-commit>
export MT_ENGINE_COMMIT="$(git rev-parse --short HEAD)"
docker compose up -d --build
docker compose ps
curl -sf http://localhost:5050/health
curl -sf http://localhost:5050/api/status
curl -sf http://localhost:5050/api/home/media-wall
curl -sf http://localhost:5050/api/auto-delete/status
curl -sf http://localhost:5050/api/pilot/stats
curl -sf http://localhost:5050/api/pilot/dry-runBefore rolling back, back up the current .env and data/; do not delete the user's download directory or qBittorrent data.
- Check container status:
docker compose ps - Check the port:
lsof -nP -iTCP:5050 -sTCP:LISTEN - Check logs:
docker compose logs -f - Compose listens on
0.0.0.0:5050by default; if the LAN cannot reach it, check whetherMT_ENGINE_BIND_HOSTin.envwas changed to127.0.0.1, then check firewall and reverse-proxy rules.
- Confirm the
.envfile exists. - Confirm
MT_TOKENis filled in. - Restart the container:
docker compose restart.
Set API_DELAY in .env to 6 or higher and set FREE_REFRESH_FAILURE_BACKOFF_SECONDS=7200, then restart. The program raises any API_DELAY below 3 up to 3:
docker compose restartDo not use localhost in a Docker environment. Set QBITTORRENT_URL to a LAN address reachable from inside the container for your NAS or host, and fill in your own qBittorrent Web UI username and password, for example:
QBITTORRENT_URL=http://<QB_HOST_IP>:8080Read-only check of auto-delete status, the PILOT loop, and the qB task mapping:
curl -sf http://localhost:5050/api/auto-delete/status
curl -sf http://localhost:5050/api/pilot/stats
curl -sf 'http://localhost:5050/api/panel/torrents?tag=PILOT'If you rely on PILOT auto-download, keep auto-delete enabled and confirm that PILOT tasks resolve an mteam_id. While troubleshooting, do not call /api/pilot/run-download, /api/pilot/run-cleanup, /api/panel/torrents/delete, /api/panel/torrents/pause, /api/panel/torrents/resume, or /api/auto-delete/toggle unless you explicitly intend to perform those actions.
Look up your user and group on the host:
id -u
id -gWrite them into .env:
PUID=1000
PGID=1000Fix data-directory permissions if needed:
sudo chown -R $(id -u):$(id -g) ./data
docker compose restartgit pull --ff-only
export MT_ENGINE_COMMIT="$(git rev-parse --short HEAD)"
docker compose up -d --build
docker compose ps
curl -sf http://localhost:5050/health
curl -sf http://localhost:5050/api/status
curl -sf http://localhost:5050/api/home/media-wall
curl -sf http://localhost:5050/api/auto-delete/status
curl -sf http://localhost:5050/api/pilot/statsAfter upgrading, confirm the current steady-state values exist in .env; if REFRESH_INTERVAL is already higher than 900, you can keep the longer interval:
REFRESH_INTERVAL=900
FREE_REFRESH_FAILURE_BACKOFF_SECONDS=7200
API_DELAY=6
MTEAM_PROACTIVE_ENABLED=true
MEDIA_WALL_REFRESH_INTERVAL=21600
MEDIA_WALL_REFRESH_FAILURE_BACKOFF_SECONDS=7200
MEDIA_WALL_DOUBAN_POSTER_FETCHES=3For local checks and contribution workflow, see CONTRIBUTING.md.
This project is licensed under the MIT License.
This project is for personal use, study, and research within the bounds of authorized use only. Users must supply their own credentials such as the M-Team API Token and qBittorrent username/password, and are responsible for credential security, downloaded content, and how they use the software.
This project has no affiliation, partnership, or endorsement relationship with any third-party site, service, or project, including M-Team, Douban, qBittorrent, PushPlus, or Next.js/Vercel.
Users must comply with the relevant site rules, third-party terms of service, copyright law, and the laws and regulations of their jurisdiction. The software is provided "as is" without warranty of any kind, express or implied; the MIT License already includes the legal disclaimer, and this section is operational guidance only.