diff --git a/README.md b/README.md index bf33934..961de6f 100644 --- a/README.md +++ b/README.md @@ -4,20 +4,20 @@ MMTL Logo

-

适合 NAS、家庭共享和多端播放的私人媒体中心

+

面向 NAS 与家庭影音场景的私人媒体中心

- Docker 一键部署 · PostgreSQL 主库 · Redis 热缓存 · OpenSearch 搜索增强 · Emby 协议兼容 · Bot 通知 + 媒体库 · 刮削整理 · 网盘 STRM · Emby 协议 · 远程 Emby 挂载 · 多用户权限 · Docker 一键部署

+ 项目简介 · 快速开始 · - 三挡部署 · - 路径映射 · - 旧 SQLite 迁移 · + 部署档位 · + 鸣谢 · 开发构建 · - 贡献规范 · - 在线演示 + English · + 贡献规范

@@ -31,400 +31,223 @@ ## 项目简介 -MMTL 是一个自托管媒体管理系统,面向 NAS、小主机、家庭影音和多用户共享场景。它把媒体库、刮削、下载整理、订阅、网盘播放、Emby 协议兼容、用户权限和 Bot 通知放在一个后台里,目标是让用户只维护一套服务,就能给网页端、手机端、电视端和第三方播放器使用。 +**MMTL** 是一个自托管私人媒体管理系统,适合 NAS、小主机、家庭共享和多端播放场景。本项目由 [MediaStationGo](https://github.com/ShukeBta/MediaStationGo) fork 并持续二开维护,在保留「一套服务覆盖网页、手机、电视与第三方播放器」思路的同时,围绕网盘播放、任务队列、远程挂载和权限体系做了大量增强。 -核心能力: +你可以把 MMTL 理解为: + +- 一个带现代 Web UI 的**媒体库后台** +- 一个兼容 Emby/Jellyfin 客户端的**协议网关** +- 一个连接本地硬盘、下载目录与网盘存储的**整理与播放入口** + +### 核心能力 + +| 模块 | 说明 | +| --- | --- | +| **媒体库** | 电影、电视剧、动漫、综艺、音乐与自定义库;多根目录、扫库、海报墙、继续观看 | +| **元数据刮削** | TMDb、Bangumi、Douban、TheTVDB、Fanart 等;支持 NFO、手动匹配、刮削队列 | +| **播放** | 网页播放器、HLS 转码、弹幕、字幕、播放配置档、观看历史与收藏 | +| **Emby 协议** | Infuse、SenPlayer、Fileball 等客户端可直接添加本服务,使用 MMTL 账号登录 | +| **远程 Emby 挂载** | 将远程 Emby 媒体库挂载到本地界面统一浏览(无需单独开 Emby 客户端) | +| **网盘与 STRM** | OpenList、CloudDrive2、115、WebDAV 等;STRM 同步、上传/下载队列、直链/302 播放 | +| **下载与整理** | qBittorrent 接入、站点搜索与订阅、下载后自动整理、文件管理器(复制/移动/硬链/软链) | +| **用户与权限** | 管理员/普通用户、有效期、成人内容开关、播放配置 PIN、细粒度操作权限 | +| **运维能力** | 统一任务队列、回收站、存储统计、DLNA 投屏、系统设置与日志 | + +### 技术栈 + +- **后端**:Go · Gin · GORM · SQLite / PostgreSQL · 可选 Redis · 可选 OpenSearch +- **前端**:React 18 · Vite · TypeScript · Tailwind CSS · Zustand +- **部署**:Docker Compose 多档模板,支持 amd64 / arm64 镜像与单文件可执行发布 + +--- -- **媒体库管理**:电影、电视剧、动漫、综艺、音乐和自定义媒体库统一管理。 -- **Emby 协议兼容**:Infuse、VidHub、SenPlayer、Fileball 等客户端可按 Emby/Jellyfin 方式添加服务器。 -- **本地 + 网盘**:支持本地硬盘、下载目录、OpenList、CloudDrive2、WebDAV、STRMURL 和 302 反代播放。 -- **订阅下载入库**:连接 qBittorrent 后支持搜索、订阅、下载完成整理、刮削和入库通知。 -- **多用户与权限**:管理员/普通用户、有效期、成人内容开关、设备管理、注册码和 Telegram Bot 绑定。 -- **灵活部署**:单镜像 SQLite 一键起步,或按规模选择 PostgreSQL、Redis、OpenSearch,低配 NAS 到大库检索都能覆盖。 ## 快速开始 -最推荐使用 Docker Compose。仓库提供四份独立完整模板,全部不依赖 `.env`。想最省心就下载单镜像档(SQLite,只有一个镜像);只需要按需修改访问端口、媒体目录、下载目录和可选硬件设备。需要多用户/高并发再选第一档起的 PostgreSQL 档位。 +推荐使用 Docker Compose。仓库提供四份**互相独立**的完整模板,无需 `.env` 即可起步。 ```bash -mkdir -p MMTL -cd MMTL -# 最省心:单镜像 + SQLite,只启动一个容器 +mkdir -p MMTL && cd MMTL + +# 最省心:单镜像 + 内置 SQLite curl -fsSL https://raw.githubusercontent.com/truewhile/MMTL/main/docker-compose.simple.yml -o docker-compose.yml -# 或第一档:PostgreSQL(多用户/高并发更稳) + +# 或多用户场景:PostgreSQL 第一档 # curl -fsSL https://raw.githubusercontent.com/truewhile/MMTL/main/docker-compose.yml -o docker-compose.yml + docker compose up -d ``` -启动后访问: +浏览器访问: ```text http://服务器IP:18080 ``` -默认账号: - -```text -admin / admin123 -``` - -首次登录后请立刻修改管理员密码。 +默认账号:`admin` / `admin123`(首次登录后请立即修改密码) 镜像地址: ```text -GHCR:ghcr.io/truewhile/mmtl:latest +ghcr.io/truewhile/mmtl:latest ``` +--- + ## 部署档位 -MMTL 推荐按机器资源和用户规模选择部署档位。每份 Compose 文件都是完整文件,不需要再叠加多个 `-f`。想一个镜像跑起来就选单镜像档(SQLite);需要多用户 / 高并发时再用 PostgreSQL 三档。Redis 和 OpenSearch 是增强组件,不替代 PostgreSQL。 +按机器资源选择档位。每份 Compose 文件均可单独使用,**不要**叠加多个 `-f`。 -| 档位 | 完整配置文件 | 组件 | 适合场景 | +| 档位 | 配置文件 | 组件 | 适合场景 | | --- | --- | --- | --- | -| 单镜像档 | `docker-compose.simple.yml` | MMTL + 内置 SQLite | 新手、单人使用、只想一个镜像跑起来的低配机器 | -| 第一档 | `docker-compose.yml` | MMTL + PostgreSQL | 大多数 NAS、个人/家庭使用、低内存机器 | -| 第二档 | `docker-compose.standard.yml` | MMTL + PostgreSQL + Redis | 多用户、Emby 客户端频繁刷新、首页/媒体列表访问较多 | -| 第三档 | `docker-compose.search.yml` | MMTL + PostgreSQL + Redis + OpenSearch | 超大媒体库、复杂全文搜索、后续需要独立搜索索引 | +| 单镜像档 | `docker-compose.simple.yml` | MMTL + SQLite | 新手、单人、低配 NAS,只想一个容器跑起来 | +| 第一档 | `docker-compose.yml` | MMTL + PostgreSQL | 大多数家庭 NAS,多用户更稳 | +| 第二档 | `docker-compose.standard.yml` | + Redis | 多用户、Emby 客户端频繁刷新、首页/列表访问多 | +| 第三档 | `docker-compose.search.yml` | + OpenSearch | 超大媒体库、复杂全文搜索(内存占用更高) | -### 单镜像档:SQLite(最省心) +### 单镜像档要点 -只启动 MMTL 一个镜像,主数据库用内置 SQLite,不需要 PostgreSQL / Redis / `.env`。变量最少、资源占用最低,适合新手和单人使用。日后需要多用户或更高并发时,保留 `./data` 后切换到第一档的 PostgreSQL 即可。 - -```bash -mkdir -p MMTL -cd MMTL -curl -fsSL https://raw.githubusercontent.com/truewhile/MMTL/main/docker-compose.simple.yml -o docker-compose.yml -docker compose up -d -``` - -第一次部署通常只需要改 `docker-compose.yml` 里的这几处: +- 只启动 **一个** MMTL 容器,数据在 `./data/mmtl.db` +- 通常只需改端口与媒体目录挂载 +- **不要**设置 `MMTL_DATABASE_DSN`,否则会切到 PostgreSQL ```yaml ports: - - "18080:8080" # 改左边 18080 即可 + - "18080:8080" volumes: - ./data:/data # 必须备份 - - ./media:/media # 改左边为你的媒体目录,例如 /vol1/1000/Media:/media - # - /dev/dri:/dev/dri # Intel 核显硬解需要时取消注释 + - ./cache:/cache # 可重建 + - ./media:/media # 改成你的媒体目录 ``` -网页后台添加媒体库时填写容器内路径: +网页添加媒体库时填写容器内路径,例如 `/media`、`/media/电影`。 -```text -/media -/media/电影 -/media/电视剧 -``` +### PostgreSQL 档位要点 -关键数据目录: - -```text -./data JWT 密钥、运行配置、SQLite 主数据库(mmtl.db)——必须备份 -./cache 海报/临时缓存,可重建 -./media 媒体库 -``` - -> 单镜像模式请不要配置 `MMTL_DATABASE_DSN`;一旦填了 DSN 就会切回 PostgreSQL。 - -### 第一档:PostgreSQL - -第一档是默认推荐部署。它只启动主服务和 PostgreSQL,资源占用最低,适合绝大多数 NAS。 - -```bash -mkdir -p MMTL -cd MMTL -curl -fsSL https://raw.githubusercontent.com/truewhile/MMTL/main/docker-compose.yml -o docker-compose.yml -docker compose up -d -``` - -关键数据目录: - -```text -./postgres PostgreSQL 主数据库,必须备份 -./data JWT 密钥、运行配置、旧 SQLite 迁移源 -./cache 海报、临时文件、转码缓存,可删除重建 -``` - -### 第二档:PostgreSQL + Redis - -第二档是独立完整文件,包含第一档全部配置并额外启用 Redis。Redis 用作热缓存,能减轻多用户和 Emby 客户端频繁刷新时的数据库压力。 - -```bash -mkdir -p MMTL -cd MMTL -curl -fsSL https://raw.githubusercontent.com/truewhile/MMTL/main/docker-compose.standard.yml -o docker-compose.yml -docker compose up -d -``` - -Redis 数据目录是 `./redis`。它主要保存缓存,通常可重建;真正需要备份的仍然是 `./postgres` 和 `./data`。 - -### 第三档:PostgreSQL + Redis + OpenSearch - -第三档是独立完整文件,包含第二档全部配置并额外启用 OpenSearch,用于大库全文搜索和独立搜索索引。OpenSearch 常驻内存明显更高,低配 NAS 不建议开启。 - -```bash -mkdir -p MMTL -cd MMTL -curl -fsSL https://raw.githubusercontent.com/truewhile/MMTL/main/docker-compose.search.yml -o docker-compose.yml -docker compose up -d -``` - -OpenSearch 数据目录是 `./opensearch`。搜索索引可重建,但重建大库索引会花时间;机器资源足够时再开启第三档。 - -## 配置示例 - -仓库内提供四份推荐 Compose 文件: - -```text -docker-compose.simple.yml 单镜像档:MMTL + 内置 SQLite -docker-compose.yml 第一档:MMTL + PostgreSQL -docker-compose.standard.yml 第二档:MMTL + PostgreSQL + Redis -docker-compose.search.yml 第三档:MMTL + PostgreSQL + Redis + OpenSearch -``` - -仓库只保留面向用户部署和项目维护的必要文件。旧的本地部署脚本、发包脚本、开发机辅助脚本、`.env` 示例和旧高级 Compose 模板已经移除;Linux / Docker 用户按上面四个 Compose 文件部署即可。开发者本地生成的 `bin/`、`data/`、`cache/`、`logs/`、`.tmp/`、`tools/` 等目录已列入 `.gitignore`,不应提交到仓库。 - -如果直接下载为 `docker-compose.yml`,启动命令统一是: - -```bash -docker compose up -d -``` - -如果保留原始文件名,也可以这样启动: - -```bash -docker compose -f docker-compose.simple.yml up -d -docker compose -f docker-compose.standard.yml up -d -docker compose -f docker-compose.search.yml up -d -``` - -常用配置片段如下,注释保留为中文,方便直接复制到 NAS 上调整: +- 主库在 `./postgres`,配置与密钥在 `./data` +- 若存在旧版 `./data/mmtl.db`,首次启动会自动迁移到 PostgreSQL +- 迁移完成后可将 `MMTL_DATABASE_DB_PATH` 改为不存在路径,避免重复检查: ```yaml -services: - mmtl: - image: ghcr.io/truewhile/MMTL:latest - ports: - # 左边是宿主机访问端口,右边是容器内端口。 - - "18080:8080" - volumes: - # 运行数据:JWT 密钥、配置、旧 SQLite 迁移源。 - - ./data:/data - - # 缓存目录:海报、临时文件、转码缓存,可删除重建。 - - ./cache:/cache - - # 媒体库目录:自动整理/重命名/入库需要写权限。 - - /vol1/1000/Media:/media - environment: - TZ: Asia/Shanghai - - # PostgreSQL 主数据库。 - MMTL_DATABASE_TYPE: postgres - MMTL_DATABASE_DSN: postgres://mmtl:mmtl@postgres:5432/mmtl?sslmode=disable - - # 旧 SQLite 迁移源:只在从旧版 data/mmtl.db 导入时使用。 - MMTL_DATABASE_DB_PATH: /data/mmtl.db - - # 路径换算:宿主机路径和容器路径必须一一对应。 - MMTL_MEDIA_DIR: /vol1/1000/Media - MMTL_MEDIA_CONTAINER_DIR: /media - MMTL_DOWNLOAD_DIR: /vol1/1000/Downloads - MMTL_DOWNLOAD_CONTAINER_DIR: /downloads +MMTL_DATABASE_DB_PATH: /data/no-sqlite-migration.db ``` -## 路径映射 +### 必须备份与可重建 -路径映射是 Docker 部署里最容易填错的地方。原则是:`volumes` 左边是宿主机真实路径,右边是容器内路径;环境变量里也要保持对应关系。 +| 路径 | 说明 | +| --- | --- | +| `./data` | JWT 密钥、运行配置、SQLite 主库或迁移源 | +| `./postgres` | PostgreSQL 主库(PG 档位) | +| `./cache` | 海报/转码缓存,可重建 | +| `./redis` | 热缓存,可重建 | +| `./opensearch` | 搜索索引,可重建 | -NAS 示例: - -```yaml -volumes: - - /vol1/1000/Docker/moviepilot-v2/media:/vol1/1000/Docker/moviepilot-v2/media - - /vol1/1000/qBittorrent/downloads:/vol1/1000/qBittorrent/downloads -environment: - MMTL_MEDIA_DIR: /vol1/1000/Docker/moviepilot-v2/media - MMTL_MEDIA_CONTAINER_DIR: /vol1/1000/Docker/moviepilot-v2/media - MMTL_DOWNLOAD_DIR: /vol1/1000/qBittorrent/downloads - MMTL_DOWNLOAD_CONTAINER_DIR: /vol1/1000/qBittorrent/downloads -``` - -Windows Docker Desktop 示例: - -```yaml -volumes: - - D:/Media:/media -environment: - MMTL_MEDIA_DIR: D:/Media - MMTL_MEDIA_CONTAINER_DIR: /media -``` - -如果后台添加媒体库时填的是 `/vol1/...`,Compose 里也建议把同一个 `/vol1/...` 挂进容器,避免自动整理和下载入库时路径不可访问。 - -## 旧 SQLite 迁移 - -新版推荐 PostgreSQL 作为主数据库。`MMTL_DATABASE_DB_PATH` 不是主库路径,而是旧 SQLite 数据的迁移源。 - -迁移步骤: - -1. 把旧版 `mmtl.db` 放到 `./data/mmtl.db`。 -2. 保持 `MMTL_DATABASE_DB_PATH: /data/mmtl.db`。 -3. 启动一次,确认日志显示迁移完成,网页数据正常。 -4. 备份 `./postgres` 和 `./data`。 -5. 确认不再需要 SQLite 后,把迁移源改成不存在的路径,例如: - -```yaml -environment: - # 已完成 SQLite 迁移后,建议改成不存在的路径,避免下次启动重复检查旧库。 - MMTL_DATABASE_DB_PATH: /data/no-sqlite-migration.db -``` - -不要删除 `./postgres`。PostgreSQL 已经是主数据库,删除它会丢失账号、媒体库、订阅、配置和历史数据。 - -## 日志与 STRM 路径 - -Compose 模板默认把完整应用日志写入 `./data/logs/app.log`,同时拆分 `./data/logs/warn.log` 和 `./data/logs/error.log`。Docker 自身日志也会保留 10 个 50MB 文件: - -```bash -docker compose logs -f mmtl -tail -f ./data/logs/app.log -tail -f ./data/logs/error.log -``` - -如果要排查订阅、站点搜索、自动整理或 STRM 生成问题,保持 `MMTL_LOGGING_LEVEL: info`;需要更细日志时临时改成 `debug`,确认后再改回 `info`。 - -STRM 输出目录请使用容器内可写路径,例如 `/data/strm`,或你已经挂载进容器的媒体目录。旧版本保存过 `/app/data/strm` 的部署会在生成时自动迁移到当前 `MMTL_APP_DATA_DIR`,默认就是 `/data`。 - -## 更新与备份 - -更新镜像: +### 更新镜像 ```bash docker compose pull mmtl docker compose up -d --no-deps mmtl ``` -不要执行裸 `docker compose pull` 做日常更新。PostgreSQL / Redis / OpenSearch 是数据与缓存基础组件,compose 已设置为 `pull_policy: missing`,首次部署缺镜像时会拉取,日常更新只建议拉取 `mmtl`。需要升级这些基础组件时,请先备份 `./postgres`,再手动修改镜像版本并单独拉取。 +日常更新只拉 `mmtl` 服务即可,不要随意 `docker compose pull` 升级 PostgreSQL/Redis/OpenSearch 基础镜像。 -如果第二档或第三档保留了原始文件名,更新时指定对应完整文件: +--- -```bash -# 第二档 -docker compose -f docker-compose.standard.yml pull mmtl -docker compose -f docker-compose.standard.yml up -d --no-deps mmtl +## 路径映射 -# 第三档 -docker compose -f docker-compose.search.yml pull mmtl -docker compose -f docker-compose.search.yml up -d --no-deps mmtl +Docker 部署最常见的问题是路径填错。记住: + +- `volumes` **左侧**是宿主机真实路径,**右侧**是容器内路径 +- 网页后台添加媒体库时,应填写**容器内**路径(如 `/media/电影`) +- 若使用自动整理/下载入库,`MMTL_MEDIA_DIR` 与 `MMTL_DOWNLOAD_DIR` 需与挂载一致 + +NAS 示例: + +```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 ``` -必须备份: +--- -```text -./postgres PostgreSQL 主数据库 -./data JWT 密钥、运行配置、旧 SQLite 迁移源 -``` +## 首次使用建议 -可重建: +1. **创建媒体库** → 填写 `/media/...` → 执行扫库 +2. **配置元数据源** → 系统设置中添加 TMDb、Bangumi 等 API +3. **(可选)连接 qBittorrent** → 下载客户端设置,宿主机可用 `http://host.docker.internal:8085` +4. **(可选)配置网盘账号** → STRM 管理中添加 OpenList / 115 / WebDAV 等 +5. **第三方播放器** → 以 Emby 服务器添加 `http://服务器IP:18080`,使用 MMTL 账号登录 -```text -./cache 图片缓存、临时文件、转码缓存 -./redis Redis 热缓存 -./opensearch 搜索索引 -``` +--- ## 常见问题 -**启动后还是反复迁移 SQLite?** +**扫库或入库很慢?** +先确认路径映射与数据库档位。网盘扫描还受接口限速与目录规模影响;大库可考虑第二档 Redis 或第三档 OpenSearch。 -确认旧数据已经迁移成功后,把 `MMTL_DATABASE_DB_PATH` 改成不存在的路径,例如 `/data/no-sqlite-migration.db`,然后重启容器。 +**qBittorrent 下载后无法整理?** +确认下载目录已通过 `volumes` 挂进容器,且 `MMTL_DOWNLOAD_*` 环境变量对应正确。 -**扫库或入库速度很慢?** +**硬链接失败(cross-device link)?** +硬链接要求源与目标在同一文件系统/子卷;跨盘、跨 btrfs 子卷或网盘挂载时请改用复制或软链接。 -先确认数据库档位和路径映射正确。第一档已经足够大多数场景;第二档 Redis 能缓解频繁刷新造成的数据库压力;第三档主要增强搜索,不会替代媒体扫描本身。网盘扫描还会受网盘接口响应、目录数量和网络质量影响。 +**第三方播放器连不上?** +确认地址为 `http://IP:18080`,使用 MMTL 用户账号;反代部署需正确配置外部 URL 与 HTTPS 头。 -**qBittorrent 下载完成后无法整理?** - -确认 qBittorrent 保存路径已经通过 `volumes` 挂载进 MMTL 容器,并且 `MMTL_DOWNLOAD_DIR` 与 `MMTL_DOWNLOAD_CONTAINER_DIR` 对应正确。 - -**硬链接目录在 Docker / NAS 上看不到内容?** - -硬链接不能直接链接“目录”本身,只能链接目录里的文件。文件管理器执行目录硬链接时会递归创建目标目录结构,并为每个文件创建硬链接。硬链接还要求源文件和目标文件在容器内属于同一个文件系统/子卷;如果下载目录和媒体目录是两个独立 bind mount、不同硬盘、不同 btrfs 子卷或网盘挂载,系统会返回 `invalid cross-device link`,此时请选择“复制”或“软链接”。 - -**第三方播放器无法连接?** - -确认播放器填写的是 `http://服务器IP:18080`,账号密码使用 MMTL 用户账号。反代部署时需要正确设置外部访问地址和 HTTPS 头。 +--- ## 开发构建 -本地开发需要 Go、Node.js 和 npm。 - -后端会将 `web/dist` 通过 `go:embed` 编进二进制,因此**在编译 / 运行后端之前要先构建前端**,否则 `web` 包会因为缺少嵌入资源而编译失败。 +后端通过 `go:embed` 嵌入 `web/dist`,**编译前必须先构建前端**。 ```bash -# 前端依赖与构建(必须先做,产物被 go:embed 打进二进制) npm --prefix web ci npm --prefix web run build -# 后端测试 go test ./... - -# 本地运行后端(二进制自带前端界面,无需额外 web 目录) -go run ./cmd/server - -# 本地运行前端开发服务器 -npm --prefix web run dev +go run ./cmd/server # http://127.0.0.1:8080 +npm --prefix web run dev # http://127.0.0.1:3000 ``` -前端开发服务器默认访问: +CI 会在 Release 中提供 Windows / Linux / macOS 的 amd64、arm64 单文件可执行程序。 -```text -http://127.0.0.1:3000 -``` +--- -后端健康检查: +## 鸣谢 -```text -http://127.0.0.1:8080/api/health -``` +MMTL 在 [MediaStationGo](https://github.com/ShukeBta/MediaStationGo) 的基础上 fork 并持续演进。感谢上游项目在媒体库架构、Emby 协议兼容和自托管体验上的奠基工作。 -### 交叉编译单文件发布物 +项目中许多网盘同步、STRM 与媒体整理相关的设计与实现,也参考了 [qmediasync](https://github.com/qicfan/qmediasync)。感谢该项目的思路与实践经验。 -CI(`.github/workflows/Auto-docker-publish.yml`)每次发布会自动为 Windows / Linux(含 Debian) / macOS 交叉编译 amd64 + arm64 的单文件可执行程序,并上传到对应的 GitHub Release。你可以在 Releases 页面下载 `.zip`(Windows)或 `.tar.gz`(Linux / macOS)附件,解压后直接运行其中的 `mmtl`(Windows 为 `mmtl.exe`),无需额外携带前端目录。 - -本地手动交叉编译某个平台: - -```bash -# 先构建前端 -npm --prefix web ci && npm --prefix web run build - -# 例如:构建 Linux amd64 单文件 -CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -trimpath -ldflags="-s -w" -o mmtl-linux-amd64 ./cmd/server -``` +--- ## 贡献与反馈 -提交 Bug、功能建议或 Pull Request 前,请先阅读 [贡献规范](CONTRIBUTING.md)。 +提交 Issue 或 Pull Request 前,请阅读 [贡献规范](CONTRIBUTING.md) 与 [安全策略](SECURITY.md)。 -- Bug 反馈请使用 Issue 模板,并提供部署方式、复现步骤、日志和关键配置。 -- 功能建议请说明使用场景、期望行为和可接受的替代方案。 -- 安全漏洞请不要公开发 Issue,按 [安全策略](SECURITY.md) 使用私密渠道报告。 -- Pull Request 请从独立分支或 fork 分支发起,不要直接向 `main` 推送。 -- 分支名建议使用 `fix/...`、`feat/...`、`docs/...` 或 `test/...`,例如 `docs/contribution-guidelines`。 -- 提交前按改动范围运行 `go test ./...`、`npm --prefix web run build` 或定向测试,并在 PR 中说明验证结果。 +- Bug 请附部署方式、复现步骤与相关日志 +- 功能建议请说明使用场景与期望行为 +- PR 请从独立分支发起,提交前运行 `go test ./...` 与 `npm --prefix web run build` + +--- ## Star History - + - - - Star History Chart + + + Star History Chart +--- + ## 许可证 -本项目使用 GPL-3.0 License。详见 [LICENSE](LICENSE)。 +本项目采用 [GPL-3.0](LICENSE) 许可证。 diff --git a/README_EN.md b/README_EN.md index 9ef5096..1927f58 100644 --- a/README_EN.md +++ b/README_EN.md @@ -4,18 +4,19 @@ MMTL Logo

-

A lightweight, polished, NAS-friendly private media center

+

A self-hosted media center for NAS and home theater

- Docker-first setup · Multi-user management · Media library · Metadata · Downloads · Emby-protocol clients · Cloud playback + Libraries · Metadata · Cloud STRM · Emby protocol · Remote Emby mounts · Multi-user · Docker-first

中文 · + Overview · Quick Start · - Docker Compose · - FAQ · - Live Demo + Deployment · + Acknowledgements · + Development

@@ -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: -# 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: -- 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. -WeChat Donation QR +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 - + - - - Star History Chart + + + Star History Chart --- -## 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. - ---- - -

Made with ❤️ by ShukeBta

+This project is licensed under [GPL-3.0](LICENSE).