mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-06 05:16:38 +08:00
Rewrite README for truewhile fork and current feature set
- Clarify fork lineage from MediaStationGo - Document current capabilities: Emby mounts, STRM queues, task queue, etc. - Add acknowledgements for MediaStationGo and qmediasync - Update repo/image URLs to truewhile/MMTL - Streamline deployment docs while keeping essential guidance Co-authored-by: truewhile <truewhile@users.noreply.github.com>
This commit is contained in:
+116
-472
@@ -4,18 +4,19 @@
|
||||
<img src="web/public/brand/logo-192.png" width="96" height="96" alt="MMTL Logo" />
|
||||
</p>
|
||||
|
||||
<h3 align="center">A lightweight, polished, NAS-friendly private media center</h3>
|
||||
<h3 align="center">A self-hosted media center for NAS and home theater</h3>
|
||||
|
||||
<p align="center">
|
||||
<strong>Docker-first setup · Multi-user management · Media library · Metadata · Downloads · Emby-protocol clients · Cloud playback</strong>
|
||||
<strong>Libraries · Metadata · Cloud STRM · Emby protocol · Remote Emby mounts · Multi-user · Docker-first</strong>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="README.md">中文</a> ·
|
||||
<a href="#overview">Overview</a> ·
|
||||
<a href="#quick-start">Quick Start</a> ·
|
||||
<a href="#docker-compose-recommended">Docker Compose</a> ·
|
||||
<a href="#faq">FAQ</a> ·
|
||||
<a href="https://mgo.3jzs.com">Live Demo</a>
|
||||
<a href="#deployment-tiers">Deployment</a> ·
|
||||
<a href="#acknowledgements">Acknowledgements</a> ·
|
||||
<a href="#development">Development</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@@ -27,78 +28,51 @@
|
||||
|
||||
---
|
||||
|
||||
## What is it?
|
||||
## Overview
|
||||
|
||||
MMTL is a self-hosted media center for personal libraries, home NAS, and home-theater users.
|
||||
**MMTL** is a self-hosted private media management system for NAS, mini PCs, family sharing, and multi-device playback. This repository is a maintained fork of [MediaStationGo](https://github.com/ShukeBta/MediaStationGo), extended with stronger cloud playback, task queues, remote mounts, and permission controls.
|
||||
|
||||
It helps you:
|
||||
In practice, MMTL gives you:
|
||||
|
||||
- Manage movies, TV shows, anime, variety shows, music, and adult libraries.
|
||||
- Create multiple user accounts for family members, friends, or different devices.
|
||||
- Scan files and enrich posters, summaries, years, seasons, and episodes.
|
||||
- Play in the web UI, or log in with a MMTL account from Emby-protocol apps such as Infuse, VidHub, SenPlayer, and Emby clients.
|
||||
- Connect qBittorrent for search, subscriptions, downloads, and post-download organization.
|
||||
- Connect OpenList, CloudDrive2, WebDAV, and other storage backends with STRMURL or 302 redirect playback.
|
||||
- Run on NAS, mini PCs, VPS, Linux, Windows Docker Desktop, or any Docker-friendly host.
|
||||
- A modern **web media library**
|
||||
- An **Emby/Jellyfin-compatible protocol gateway** for third-party players
|
||||
- A single panel for **local disks, download folders, and cloud storage**
|
||||
|
||||
> The project is moving fast. With the default PostgreSQL deployment, back up both `data/` and `postgres/`.
|
||||
### Key capabilities
|
||||
|
||||
---
|
||||
| Area | Highlights |
|
||||
| --- | --- |
|
||||
| **Libraries** | Movies, TV, anime, variety, music, custom libraries; multi-root scanning; poster wall; continue watching |
|
||||
| **Metadata** | TMDb, Bangumi, Douban, TheTVDB, Fanart, NFO import, manual matching, scrape queue |
|
||||
| **Playback** | Web player, HLS transcoding, danmaku, subtitles, play profiles, history and favourites |
|
||||
| **Emby protocol** | Add MMTL in Infuse, SenPlayer, Fileball, etc. and sign in with MMTL accounts |
|
||||
| **Remote Emby mounts** | Browse remote Emby libraries inside MMTL without a separate Emby client |
|
||||
| **Cloud & STRM** | OpenList, CloudDrive2, 115, WebDAV; STRM sync; upload/download queues; direct or 302 playback |
|
||||
| **Downloads & organize** | qBittorrent, site search/subscriptions, post-download organization, file manager |
|
||||
| **Users & permissions** | Admin/regular users, expiry, NSFW toggle, play-profile PIN, granular permissions |
|
||||
| **Operations** | Unified task queue, recycle bin, storage stats, DLNA casting, settings and logs |
|
||||
|
||||
## Key Highlights
|
||||
### Tech stack
|
||||
|
||||
- **One server, many clients**: deploy MMTL once; you do not need to run a separate Emby server.
|
||||
- **Emby-protocol compatibility**: add the server in third-party players as an Emby/Jellyfin-compatible server, then log in with your MMTL username and password.
|
||||
- **Multi-user management**: supports admins, regular users, account enable/disable, expiry dates, device management, Bot registration, and redeem codes.
|
||||
- **Local + cloud media in one place**: manage local disks, download folders, OpenList, CloudDrive2, WebDAV, and other storage backends from one panel.
|
||||
- **Download-to-library workflow**: connect qBittorrent for search, subscriptions, download completion organization, and metadata matching.
|
||||
- **NAS-friendly**: simple Docker Compose deployment. The primary database lives under `postgres/`, while runtime secrets and files live under `data/`.
|
||||
|
||||
---
|
||||
|
||||
## Who is it for?
|
||||
|
||||
- **Beginners** who want to edit one `docker-compose.yml` and start the service.
|
||||
- **NAS users** who want a low-resource media center for local disks and cloud storage.
|
||||
- **PT/download users** who want downloads, organization, metadata, and playback in one panel.
|
||||
- **External-player users** who want to log in to Emby-protocol third-party apps with one MMTL account.
|
||||
- **Family-sharing users** who want separate user accounts without deploying a separate media server for each person.
|
||||
- **Developers** who want to study or extend a Go + React self-hosted media app.
|
||||
|
||||
---
|
||||
|
||||
## Live Demo
|
||||
|
||||
- URL: [https://mgo.3jzs.com](https://mgo.3jzs.com)
|
||||
- Username: `admin`
|
||||
- Password: `admin123`
|
||||
|
||||
> The demo is for feature preview only. Do not save private API keys, tracker cookies, or personal data there.
|
||||
- **Backend**: Go, Gin, GORM, SQLite or PostgreSQL, optional Redis and OpenSearch
|
||||
- **Frontend**: React 18, Vite, TypeScript, Tailwind CSS, Zustand
|
||||
- **Deployment**: Standalone Docker Compose templates, amd64/arm64 images, single-binary releases
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
Docker Compose is the recommended path. Beginners do not need `.env`, bare-metal binaries, or source builds. Use the single-image SQLite template if you want the smallest possible setup.
|
||||
Docker Compose is the recommended path. The repo ships four **standalone** templates; no `.env` is required.
|
||||
|
||||
```bash
|
||||
mkdir -p MMTL
|
||||
cd MMTL
|
||||
# Simplest option: one MMTL container + SQLite
|
||||
curl -fsSL https://raw.githubusercontent.com/ShukeBta/MMTL/main/docker-compose.simple.yml -o docker-compose.yml
|
||||
# Or tier 1: MMTL + PostgreSQL
|
||||
# curl -fsSL https://raw.githubusercontent.com/ShukeBta/MMTL/main/docker-compose.yml -o docker-compose.yml
|
||||
```
|
||||
mkdir -p MMTL && cd MMTL
|
||||
|
||||
Edit `docker-compose.yml`:
|
||||
# Simplest: one container with built-in SQLite
|
||||
curl -fsSL https://raw.githubusercontent.com/truewhile/MMTL/main/docker-compose.simple.yml -o docker-compose.yml
|
||||
|
||||
```bash
|
||||
vi docker-compose.yml
|
||||
```
|
||||
# Or PostgreSQL tier for multi-user setups
|
||||
# curl -fsSL https://raw.githubusercontent.com/truewhile/MMTL/main/docker-compose.yml -o docker-compose.yml
|
||||
|
||||
Start:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
@@ -108,295 +82,46 @@ Open:
|
||||
http://SERVER_IP:18080
|
||||
```
|
||||
|
||||
Default login:
|
||||
Default login: `admin` / `admin123` — change the password immediately.
|
||||
|
||||
Image:
|
||||
|
||||
```text
|
||||
Username: admin
|
||||
Password: admin123
|
||||
ghcr.io/truewhile/mmtl:latest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Docker Compose Recommended
|
||||
## Deployment tiers
|
||||
|
||||
The repository `docker-compose.yml` is the lightweight recommended template: no `.env` required, and by default it only starts `MMTL + PostgreSQL`. This is the best starting point for most NAS users.
|
||||
Pick one compose file. Do **not** stack multiple `-f` files.
|
||||
|
||||
If you already have an older `./data/mmtl.db`, the first start with the new compose file automatically imports it into PostgreSQL. Keep `./data`; it still stores the JWT secret, runtime data, and the old SQLite migration source.
|
||||
| Tier | File | Stack | Best for |
|
||||
| --- | --- | --- | --- |
|
||||
| Single image | `docker-compose.simple.yml` | MMTL + SQLite | Beginners, single-user, low-resource NAS |
|
||||
| Tier 1 | `docker-compose.yml` | MMTL + PostgreSQL | Most home NAS deployments |
|
||||
| Tier 2 | `docker-compose.standard.yml` | + Redis | Multi-user, frequent Emby client refreshes |
|
||||
| Tier 3 | `docker-compose.search.yml` | + OpenSearch | Very large libraries, advanced full-text search |
|
||||
|
||||
### Deployment modes
|
||||
### Single-image notes
|
||||
|
||||
| Mode | Command | Best for |
|
||||
| --- | --- | --- |
|
||||
| Single image: SQLite | `docker compose -f docker-compose.simple.yml up -d` | Beginners and single-user setups that want one image only, no PostgreSQL/Redis |
|
||||
| Lightweight: PG only | `docker compose up -d` | Most NAS devices, lowest resource use |
|
||||
| Standard: PG + Redis | `docker compose -f docker-compose.standard.yml up -d` | Multi-user use and frequent Emby client refreshes |
|
||||
| Search enhanced: PG + Redis + OpenSearch | `docker compose -f docker-compose.search.yml up -d` | Huge libraries and future standalone search indexing |
|
||||
- Only one MMTL container; database lives in `./data/mmtl.db`
|
||||
- Do **not** set `MMTL_DATABASE_DSN` or it switches to PostgreSQL
|
||||
- Back up `./data`; `./cache` can be rebuilt
|
||||
|
||||
Each compose file is standalone. Do not stack multiple `-f` files together.
|
||||
### PostgreSQL notes
|
||||
|
||||
The single-image `docker-compose.simple.yml` runs only MMTL with a built-in SQLite database — the simplest starting point. Do not set `MMTL_DATABASE_DSN` there, or it switches back to PostgreSQL. Move up to the PostgreSQL modes for multi-user or high-concurrency use (keep `./data` when you switch). Redis and OpenSearch are enhancement layers, not source databases. Do not enable OpenSearch by default on low-memory NAS devices.
|
||||
- Primary DB: `./postgres`; secrets and runtime files: `./data`
|
||||
- Existing `./data/mmtl.db` migrates automatically on first start
|
||||
- After migration, point `MMTL_DATABASE_DB_PATH` at a non-existent file to disable re-checks
|
||||
|
||||
### Database Choice And Disabling SQLite
|
||||
### Backup
|
||||
|
||||
The current Docker Compose setup uses PostgreSQL by default. SQLite is no longer the primary database in the recommended Docker deployment. The runtime database is controlled by:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
MMTL_DATABASE_TYPE: postgres
|
||||
MMTL_DATABASE_DSN: postgres://mmtl:mmtl@postgres:5432/mmtl?sslmode=disable
|
||||
```
|
||||
|
||||
`MMTL_DATABASE_DB_PATH` is only used as a one-time migration source for old SQLite data:
|
||||
|
||||
- Fresh installs: `docker compose up -d` uses PostgreSQL and does not create a new SQLite primary database.
|
||||
- Upgrades: if `./data/mmtl.db` exists, the first start with the new compose file imports it into PostgreSQL.
|
||||
- Migration fills missing rows by primary key and skips rows that already exist. If it fails partway through, a later start continues the remaining tables.
|
||||
- After a successful import, PostgreSQL gets a completion marker in the `settings` table, so the old SQLite file is not imported again.
|
||||
- Redis is a hot cache and OpenSearch is a search index; neither is a source database.
|
||||
|
||||
Recommended SQLite to PostgreSQL upgrade flow:
|
||||
|
||||
```bash
|
||||
docker compose pull mmtl
|
||||
docker compose up -d --no-deps mmtl
|
||||
docker compose logs -f mmtl
|
||||
```
|
||||
|
||||
After you see `sqlite data migrated to postgres`, or after the web UI shows your users, libraries, and settings correctly, you can stop using the old SQLite file as a migration source.
|
||||
|
||||
To make the deployment PostgreSQL-only after migration, keep PostgreSQL selected and point the old SQLite migration path at a non-existent file:
|
||||
|
||||
> Only do this after the web UI confirms that users, libraries, settings, and media rows are already present in PostgreSQL.
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
MMTL_DATABASE_TYPE: postgres
|
||||
MMTL_DATABASE_DSN: postgres://mmtl:mmtl@postgres:5432/mmtl?sslmode=disable
|
||||
MMTL_DATABASE_DB_PATH: /data/disabled-sqlite-migration.db
|
||||
```
|
||||
|
||||
Then rename or move the old host-side SQLite file as an offline backup:
|
||||
|
||||
```bash
|
||||
mv data/mmtl.db data/mmtl.sqlite.bak
|
||||
```
|
||||
|
||||
For bare-metal or custom `config.yaml` deployments, use the same idea:
|
||||
|
||||
```yaml
|
||||
database:
|
||||
type: postgres
|
||||
dsn: postgres://mmtl:mmtl@127.0.0.1:5432/mmtl?sslmode=disable
|
||||
db_path: ""
|
||||
```
|
||||
|
||||
Do not delete `./postgres`. After migration, it is the real primary database. Keep `./data` too, because it stores the JWT secret and runtime files.
|
||||
|
||||
### Choose an image source
|
||||
|
||||
Both image sources are supported. Pick one and put it in `image:`:
|
||||
|
||||
| Source | Image | Best for |
|
||||
| --- | --- | --- |
|
||||
| GitHub Container Registry (GHCR) | `ghcr.io/shukebta/mmtl:latest` | Recommended default, follows repository releases |
|
||||
| Docker Hub | `shukbet/mmtl:latest` | Backup source when GHCR is slow or unavailable |
|
||||
|
||||
To pin a version, first confirm the tag exists on the repository Packages page. Use this format:
|
||||
|
||||
```yaml
|
||||
image: ghcr.io/shukebta/mmtl:<version-tag>
|
||||
# If GHCR does not have that tag, use Docker Hub as the backup:
|
||||
# image: shukbet/mmtl:MMTL-v0.0.72
|
||||
```
|
||||
|
||||
For the simplest setup, keep GHCR `latest`.
|
||||
|
||||
Manual pull examples:
|
||||
|
||||
```bash
|
||||
# GitHub Container Registry
|
||||
docker pull ghcr.io/shukebta/mmtl:latest
|
||||
|
||||
# Docker Hub backup
|
||||
docker pull shukbet/mmtl:latest
|
||||
```
|
||||
|
||||
Focus on this part:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- ./data:/data
|
||||
- ./cache:/cache
|
||||
- ./media:/media
|
||||
- ./downloads:/downloads
|
||||
```
|
||||
|
||||
Meaning:
|
||||
|
||||
| Host path | Container path | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `./data` | app `/data` | Settings, JWT secret, old SQLite migration source; the primary DB is under `./postgres` |
|
||||
| `./cache` | app `/cache` | Cache; safe to clean when needed |
|
||||
| `./media` | `/media` | Media libraries; use `/media/...` in the web UI |
|
||||
| `./downloads` | `/downloads` | Download directory and organization source |
|
||||
| `./postgres` | PostgreSQL `/var/lib/postgresql/data` | New default primary database; back this up |
|
||||
| `./redis` | Redis `/data` | Used only in standard mode; hot cache, rebuildable |
|
||||
| `./opensearch` | OpenSearch `/usr/share/opensearch/data` | Used only in search-enhanced mode; higher memory use |
|
||||
|
||||
If your NAS paths are:
|
||||
|
||||
```text
|
||||
/vol1/1000/Media
|
||||
/vol1/1000/Downloads
|
||||
```
|
||||
|
||||
change the compose file to:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- ./data:/data
|
||||
- ./cache:/cache
|
||||
- /vol1/1000/Media:/media
|
||||
- /vol1/1000/Downloads:/downloads
|
||||
|
||||
environment:
|
||||
MMTL_MEDIA_DIR: /vol1/1000/Media
|
||||
MMTL_DOWNLOAD_DIR: /vol1/1000/Downloads
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- The left side of `volumes` is the real path on your host/NAS.
|
||||
- The right side is the container path. Keep `/media` and `/downloads` unless you know why you are changing them.
|
||||
- In the web UI, create libraries with container paths such as `/media/Movies` or `/media/TV`.
|
||||
- Do not write NAS absolute paths as `./vol1/...`; `./` means a folder under the current compose directory.
|
||||
- On Windows Docker Desktop, paths like `D:/Media:/media` and `D:/Downloads:/downloads` are fine.
|
||||
- If you only scan/play existing media and never organize into the library, you may add `:ro`; if you use organize/rename/ingest, the media mount must stay writable.
|
||||
|
||||
### Minimal compose example
|
||||
|
||||
The root `docker-compose.yml` follows this style:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
mmtl:
|
||||
# Pick one image source:
|
||||
# GitHub Container Registry (GHCR):
|
||||
image: ghcr.io/shukebta/mmtl:latest
|
||||
# Docker Hub backup:
|
||||
# image: shukbet/mmtl:latest
|
||||
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
||||
# Browser: http://SERVER_IP:18080
|
||||
ports:
|
||||
- "18080:8080"
|
||||
|
||||
# Let the container reach qBittorrent running on the host:
|
||||
# qB URL example: http://host.docker.internal:8085
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
|
||||
volumes:
|
||||
# Application data. Back this up before upgrades.
|
||||
- ./data:/data
|
||||
- ./cache:/cache
|
||||
|
||||
# Beginners can create ./media and ./downloads.
|
||||
# NAS users should replace source with real absolute paths.
|
||||
# create_host_path=false prevents Docker from silently creating an empty
|
||||
# folder when the host path is wrong.
|
||||
- type: bind
|
||||
source: ./media
|
||||
target: /media
|
||||
bind:
|
||||
create_host_path: false
|
||||
- type: bind
|
||||
source: ./downloads
|
||||
target: /downloads
|
||||
bind:
|
||||
create_host_path: false
|
||||
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
PUID: "1000"
|
||||
PGID: "1000"
|
||||
|
||||
MMTL_APP_HOST: 0.0.0.0
|
||||
MMTL_APP_PORT: 8080
|
||||
MMTL_APP_WEB_DIR: /app/web/dist
|
||||
MMTL_APP_DATA_DIR: /data
|
||||
|
||||
# Lightweight mode uses PostgreSQL by default.
|
||||
# Old SQLite data migrates from this path on first start.
|
||||
MMTL_DATABASE_TYPE: postgres
|
||||
MMTL_DATABASE_DSN: postgres://mmtl:mmtl@postgres:5432/mmtl?sslmode=disable
|
||||
# After migration, change this to /data/disabled-sqlite-migration.db to disable the SQLite migration source.
|
||||
MMTL_DATABASE_DB_PATH: /data/mmtl.db
|
||||
MMTL_CACHE_CACHE_DIR: /cache
|
||||
|
||||
# Use /media and /downloads in the web UI and downloader by default.
|
||||
# Only set MMTL_*_DIR to real host paths when migrating old
|
||||
# libraries/tasks that already stored host paths.
|
||||
MMTL_MEDIA_DIR: /media
|
||||
MMTL_MEDIA_CONTAINER_DIR: /media
|
||||
MMTL_DOWNLOAD_DIR: /downloads
|
||||
MMTL_DOWNLOAD_CONTAINER_DIR: /downloads
|
||||
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: mmtl
|
||||
POSTGRES_USER: mmtl
|
||||
POSTGRES_PASSWORD: mmtl
|
||||
volumes:
|
||||
- ./postgres:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -U mmtl -d mmtl"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
```
|
||||
|
||||
> Note: PostgreSQL is the primary database. Lightweight mode still has short in-process caching. Redis is a cross-process hot cache, and OpenSearch is a search enhancement layer; neither is a source database.
|
||||
|
||||
---
|
||||
|
||||
## First-time Setup
|
||||
|
||||
1. **Create a library**
|
||||
- Go to the library page.
|
||||
- Use a container path such as `/media/Movies`.
|
||||
- Start a scan.
|
||||
|
||||
2. **Connect qBittorrent**
|
||||
- Go to download client settings.
|
||||
- If qBittorrent runs on the host, try `http://host.docker.internal:8085`.
|
||||
|
||||
3. **Configure metadata providers**
|
||||
- Go to system settings / external APIs.
|
||||
- Add TMDb, Bangumi, TheTVDB, Fanart, Douban, or other providers when needed.
|
||||
|
||||
4. **Use external players**
|
||||
- Add the server as an Emby/Jellyfin-compatible server.
|
||||
- Server URL: `http://SERVER_IP:18080`.
|
||||
- Use the username and password created in MMTL. No separate Emby server is required.
|
||||
- Admins can create regular users in the web UI or Bot so each person can log in with their own account.
|
||||
|
||||
5. **Use cloud playback**
|
||||
- Configure OpenList, CloudDrive2, WebDAV, or another provider in storage settings.
|
||||
- Choose STRMURL or 302 redirect playback in the admin settings.
|
||||
- The enabled option takes priority. If both are disabled, playback falls back to the normal server playback path.
|
||||
|
||||
---
|
||||
|
||||
## Update, Backup, Logs
|
||||
| Path | Notes |
|
||||
| --- | --- |
|
||||
| `./data` | JWT secret, config, SQLite DB or migration source |
|
||||
| `./postgres` | PostgreSQL primary DB |
|
||||
| `./cache`, `./redis`, `./opensearch` | Rebuildable |
|
||||
|
||||
### Update
|
||||
|
||||
@@ -405,181 +130,100 @@ docker compose pull mmtl
|
||||
docker compose up -d --no-deps mmtl
|
||||
```
|
||||
|
||||
### Logs
|
||||
---
|
||||
|
||||
```bash
|
||||
docker compose logs -f mmtl
|
||||
tail -f ./data/logs/app.log
|
||||
tail -f ./data/logs/error.log
|
||||
## Path mapping
|
||||
|
||||
The most common Docker mistake is mixing host paths with container paths.
|
||||
|
||||
- Left side of `volumes` = real host/NAS path
|
||||
- Right side = container path; use `/media/...` in the web UI
|
||||
- Keep `MMTL_MEDIA_DIR` / `MMTL_DOWNLOAD_DIR` aligned with mounts when organizing or ingesting downloads
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- /vol1/1000/Media:/media
|
||||
- /vol1/1000/Downloads:/downloads
|
||||
environment:
|
||||
MMTL_MEDIA_DIR: /vol1/1000/Media
|
||||
MMTL_MEDIA_CONTAINER_DIR: /media
|
||||
MMTL_DOWNLOAD_DIR: /vol1/1000/Downloads
|
||||
MMTL_DOWNLOAD_CONTAINER_DIR: /downloads
|
||||
```
|
||||
|
||||
The compose templates keep full application logs in `./data/logs/app.log` and split warnings/errors into `warn.log` and `error.log`. Keep `MMTL_LOGGING_LEVEL=info` while diagnosing subscription, site search, organizer, or STRM generation issues; temporarily switch to `debug` only when deeper tracing is needed.
|
||||
---
|
||||
|
||||
Use a writable container path for STRM output, such as `/data/strm` or a mounted media path. Deployments that previously saved `/app/data/strm` are migrated automatically to the configured `MMTL_APP_DATA_DIR`, which defaults to `/data`.
|
||||
## First-time setup
|
||||
|
||||
### Backup
|
||||
|
||||
For the default PostgreSQL deployment, back up:
|
||||
|
||||
```text
|
||||
data/
|
||||
postgres/
|
||||
```
|
||||
|
||||
`postgres/` is the primary database and contains users, libraries, settings, and media metadata. `data/` stores the JWT secret, runtime files, and optional old SQLite migration source.
|
||||
|
||||
If you enabled the extended modes, these are optional:
|
||||
|
||||
```text
|
||||
redis/ # hot cache, safe to rebuild
|
||||
opensearch/ # search index, rebuildable; backing it up can save reindex time on huge libraries
|
||||
```
|
||||
|
||||
`cache/` is usually not important. If you explicitly still use `database.type=sqlite`, the primary database remains `data/mmtl.db`.
|
||||
|
||||
### Stop
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
1. Create a library with a container path such as `/media/Movies`, then scan
|
||||
2. Add metadata providers (TMDb, Bangumi, etc.) in system settings
|
||||
3. Optionally connect qBittorrent (`http://host.docker.internal:8085` when qB runs on the host)
|
||||
4. Optionally configure cloud accounts under STRM management
|
||||
5. Add the server in Emby-compatible players at `http://SERVER_IP:18080` using MMTL credentials
|
||||
|
||||
---
|
||||
|
||||
## FAQ
|
||||
|
||||
### 1. The web page does not open
|
||||
**Library scan is slow**
|
||||
Check path mapping and DB tier. Cloud scans also depend on API limits and folder size.
|
||||
|
||||
Check the container:
|
||||
**qBittorrent downloads are not organized**
|
||||
Ensure the download directory is mounted into the container and env vars match.
|
||||
|
||||
```bash
|
||||
docker ps
|
||||
docker compose logs --tail=100 mmtl
|
||||
```
|
||||
**Hardlink fails with cross-device link**
|
||||
Hardlinks require the same filesystem/subvolume; use copy or symlink across disks or cloud mounts.
|
||||
|
||||
Then open:
|
||||
|
||||
```text
|
||||
http://SERVER_IP:18080
|
||||
```
|
||||
|
||||
### 2. The library cannot find files
|
||||
|
||||
Most cases are path mistakes.
|
||||
|
||||
- Docker maps media to `/media`.
|
||||
- In the web UI, use `/media/Movies`, not the original NAS path.
|
||||
- Docker maps downloads to `/downloads`; use `/downloads` as the organization source when possible.
|
||||
|
||||
### 3. qBittorrent cannot connect
|
||||
|
||||
If qBittorrent is on the host, try:
|
||||
|
||||
```text
|
||||
http://host.docker.internal:8085
|
||||
```
|
||||
|
||||
If qBittorrent is on another machine, use that machine's LAN IP.
|
||||
|
||||
### 4. NAS CPU usage is high
|
||||
|
||||
Suggested settings:
|
||||
|
||||
- Set `ffprobe.max_concurrent` to `1`.
|
||||
- Enable automatic organization, scrape-after-scan, and boot cloud scan only when you really need them.
|
||||
- Avoid frequent full-library scans on large libraries. Prefer manual scan or scheduled night sync.
|
||||
|
||||
### 5. Should I use `.env`?
|
||||
|
||||
Beginners should not. Editing `docker-compose.yml` directly is easier to understand.
|
||||
|
||||
`.env` is not required by the provided deployment templates. For the single-image template, edit `docker-compose.simple.yml` directly and only adjust the port, volume paths, and optional hardware device mapping.
|
||||
**External player cannot connect**
|
||||
Use `http://IP:18080` and a MMTL user account; reverse proxies need correct external URL and HTTPS headers.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
| Area | Features |
|
||||
| --- | --- |
|
||||
| Libraries | Movies, TV shows, anime, variety, music, adult content |
|
||||
| Metadata | NFO, local artwork, TMDb, TheTVDB, Bangumi, Douban, Fanart, JavBus/JavDB |
|
||||
| Playback | Web playback, HTTP Range, HLS transcoding, direct links, STRMURL, 302 redirect |
|
||||
| External clients | Emby-protocol compatible APIs; MMTL accounts can log in to third-party players |
|
||||
| User management | Multi-user accounts, admin/regular users, expiry dates, device management, Bot registration and redeem codes |
|
||||
| Downloads | qBittorrent, site search, subscriptions, post-download organization |
|
||||
| File manager | Browse, organize, copy, move, hardlink, symlink |
|
||||
| Operations | Task queue, recycle bin, duplicate files, notifications, logs |
|
||||
| AI | OpenAI-compatible API, AI search, recommendations, assistant |
|
||||
|
||||
Directory hardlinks are handled by recreating the directory tree and hardlinking
|
||||
each contained file. Linux cannot hardlink a directory itself. Hardlinks still
|
||||
require the source and target files to be on the same filesystem/subvolume from
|
||||
inside the container; if media and downloads are separate bind mounts, disks,
|
||||
btrfs subvolumes, or cloud mounts, use copy or symlink instead.
|
||||
|
||||
## Development
|
||||
|
||||
Regular users should use Docker. Developers can run:
|
||||
The backend embeds `web/dist` via `go:embed`. Build the frontend first.
|
||||
|
||||
```bash
|
||||
go run ./cmd/server
|
||||
```
|
||||
npm --prefix web ci
|
||||
npm --prefix web run build
|
||||
|
||||
Frontend:
|
||||
|
||||
```bash
|
||||
cd web
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Tests:
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
cd web && npm run build
|
||||
go run ./cmd/server
|
||||
npm --prefix web run dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Community and Friends
|
||||
|
||||
- Telegram group: <https://t.me/MMTL>
|
||||
- NodeSeek: [https://www.nodeseek.com/](https://www.nodeseek.com/)
|
||||
- LINUX DO: [https://linux.do/](https://linux.do/)
|
||||
Release builds ship single-file binaries for Windows, Linux, and macOS on amd64 and arm64.
|
||||
|
||||
---
|
||||
|
||||
## Donation
|
||||
## Acknowledgements
|
||||
|
||||
If MMTL saves you time, feel free to buy the author a bowl of noodles.
|
||||
MMTL is forked from and continues to evolve [MediaStationGo](https://github.com/ShukeBta/MediaStationGo). Thank you to the upstream project for the media-library architecture, Emby-protocol compatibility, and self-hosted foundation.
|
||||
|
||||
<img width="200" height="200" alt="WeChat Donation QR" src="https://github.com/user-attachments/assets/d6077de5-8305-400d-8b82-470ef05d926e" />
|
||||
Many cloud sync, STRM, and media-organization ideas in this project were also informed by [qmediasync](https://github.com/qicfan/qmediasync). Thank you for the reference implementation and design patterns.
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md) before opening issues or pull requests.
|
||||
|
||||
---
|
||||
|
||||
## Star History
|
||||
|
||||
<a href="https://www.star-history.com/?repos=ShukeBta%2FMMTL&type=date&legend=top-left">
|
||||
<a href="https://www.star-history.com/?repos=truewhile%2FMMTL&type=date&legend=top-left">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=ShukeBta/MMTL&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=ShukeBta/MMTL&type=date&legend=top-left" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=ShukeBta/MMTL&type=date&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=truewhile/MMTL&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=truewhile/MMTL&type=date&legend=top-left" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=truewhile/MMTL&type=date&legend=top-left" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
---
|
||||
|
||||
## License and Non-Commercial Statement
|
||||
## License
|
||||
|
||||
This project uses `GPL-3.0` as its base license. See [LICENSE](LICENSE).
|
||||
|
||||
The maintainers also state and request:
|
||||
|
||||
- The project is intended for personal learning, home NAS, self-hosted media, non-commercial research, and community collaboration.
|
||||
- Without explicit written permission from the author, do not use this project or derivative versions for commercial resale, paid hosting, paid SaaS, pre-installed commercial devices, closed-source redistribution, or other profit-oriented commercial use.
|
||||
- For commercial cooperation, enterprise deployment, custom development, integrated redistribution, or commercial authorization, contact the author first.
|
||||
- If there is any interpretive difference between this README and the formal `GPL-3.0` license text, the code license is governed by [LICENSE](LICENSE); commercial usage should additionally obtain author permission.
|
||||
|
||||
---
|
||||
|
||||
<p align="center">Made with ❤️ by ShukeBta</p>
|
||||
This project is licensed under [GPL-3.0](LICENSE).
|
||||
|
||||
Reference in New Issue
Block a user