Skip to content

Repository files navigation

MT-Engine

English | 简体中文

Version License Python Next.js React Docker

MT-Engine is a torrent search, freeleech monitor, automated download, and qBittorrent dashboard tool for M-Team.

Modules

  • 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

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.

Tech Stack

Backend

  • Python 3.12+
  • FastAPI
  • httpx
  • SQLite
  • Pydantic

Frontend

  • 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

Deployment

  • Docker
  • Docker Compose
  • Single-container deployment: FastAPI serves the API and also hosts the Next.js static export.

Quick Start

Prerequisites

  • 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)

1. Clone the repository

git clone https://github.com/kevinplus66/mt-engine.git
cd mt-engine

2. Create the config file

cp .env.example .env

Edit .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_farm

For 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.

3. Start

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 --build

The 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.

4. Access

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.

5. Verify

docker compose ps
./scripts/verify-deploy.sh --base-url http://127.0.0.1:5050

scripts/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/status

If you use PILOT auto-download, also confirm:

  • enabled in /api/auto-delete/status is true or matches your expectation.
  • is_running in /api/pilot/stats is true, unless you have explicitly disabled the download and cleanup strategy.
  • /api/pilot/dry-run returns a read-only preview of candidates and disk budget without mutating qB.
  • warnings in /api/status is 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-wall returns rails; if it is briefly empty right after the first deploy, just wait for the first background refresh to finish.

Environment Variables

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}

PILOT and Safety Semantics

  • Omitting delete_files on /api/panel/torrents/delete preserves downloaded files; files are removed only when the request explicitly sets delete_files: true.
  • If /downloads or 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_ratio is an opt-in PILOT cleanup guard for fixed-size disks: when the remaining download budget is smaller than the configured min_size_gb, dry-run can preview and cleanup can remove the lowest-scored mature seed; the default is 0, so existing deployments keep their current cleanup policy until explicitly enabled.

Local Development

Backend

pip install -r requirements-dev.txt
uvicorn app.main:app --host 0.0.0.0 --port 5050 --reload

Frontend

Front-end local development uses Node.js 22 LTS.

cd frontend
npm install
npm run dev

Default URL:

http://localhost:3000

If 3000 is in use:

npm run dev -- -p 3001

In 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:5051

If 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.

Project Structure

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

Common Commands

# 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-run

NAS Standard Deployment and Rollback

Standard deployment

cd /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-run

If 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.sh

If 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.sh

Required 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.

Standard rollback

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-run

Before rolling back, back up the current .env and data/; do not delete the user's download directory or qBittorrent data.

Troubleshooting

Page unreachable

  1. Check container status: docker compose ps
  2. Check the port: lsof -nP -iTCP:5050 -sTCP:LISTEN
  3. Check logs: docker compose logs -f
  4. Compose listens on 0.0.0.0:5050 by default; if the LAN cannot reach it, check whether MT_ENGINE_BIND_HOST in .env was changed to 127.0.0.1, then check firewall and reverse-proxy rules.

Page says MT_TOKEN is not configured

  1. Confirm the .env file exists.
  2. Confirm MT_TOKEN is filled in.
  3. Restart the container: docker compose restart.

M-Team API says requests are too frequent

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 restart

qBittorrent cannot connect

Do 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>:8080

PILOT auto-delete status is uncertain

Read-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.

Data directory permission errors

Look up your user and group on the host:

id -u
id -g

Write them into .env:

PUID=1000
PGID=1000

Fix data-directory permissions if needed:

sudo chown -R $(id -u):$(id -g) ./data
docker compose restart

Update

git 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/stats

After 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=3

Documentation and Version Maintenance

For local checks and contribution workflow, see CONTRIBUTING.md.

License

This project is licensed under the MIT License.

Disclaimer

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.

About

M-Team automation engine for Kevin home/NAS workflows

Topics

Resources

Contributing

Security policy

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages