chore remove unused docs and assets

This commit is contained in:
ShukeBta
2026-07-02 02:11:10 +08:00
parent 1b56469beb
commit 204f7fb676
56 changed files with 0 additions and 2697 deletions
-19
View File
@@ -493,25 +493,6 @@ Beginners should not. Editing `docker-compose.yml` directly is easier to underst
| Operations | Task queue, recycle bin, duplicate files, notifications, logs |
| AI | OpenAI-compatible API, AI search, recommendations, assistant |
---
## Screenshots
<details open>
<summary><strong>Preview</strong></summary>
| Login | Home |
| --- | --- |
| <img src="docs/screenshots/00-login.jpg" alt="Login" width="100%"> | <img src="docs/screenshots/01-home.jpg" alt="Home" width="100%"> |
| Libraries | Player |
| --- | --- |
| <img src="docs/screenshots/02-libraries.jpg" alt="Libraries" width="100%"> | <img src="docs/screenshots/06-player.jpg" alt="Player" width="100%"> |
</details>
---
## Development
Regular users should use Docker. Developers can run:
-456
View File
@@ -1,456 +0,0 @@
# MediaStationGo 当前实现分析报告
> **项目路径**: `D:\项目\MediaStationGo`
> **技术栈**: Go 1.25 + Gin + GORM + SQLite (WAL) + Viper + Zap + JWT (后端) | React 18 + Vite + TailwindCSS + Zustand + HLS.js (前端)
> **分析日期**: 2026-02-04
> **分析者**: Architect (Bob)
---
## 一、总体架构概览
### 后端架构
```
cmd/server/main.go # 应用入口
├── internal/config/config.go # 分层配置(默认值/YAML/环境变量)
├── internal/model/model.go # GORM 数据模型(12个实体)
├── internal/repository/ # 数据访问层(12个Repository)
├── internal/service/ # 业务逻辑层(28个服务文件)
├── internal/handler/ # HTTP 路由处理层(19个Handler文件)
├── internal/middleware/ # 中间件(日志/CORS/JWT/Admin)
├── internal/database/ # 数据库初始化与迁移
```
### 前端架构
```
web/src/
├── App.tsx # 路由定义(18个页面路由)
├── main.tsx # React 入口
├── api/ # API 调用层(15个模块)
│ ├── client.ts # Axios 实例 + 拦截器
│ ├── auth.ts, library.ts, playback.ts, downloads.ts, ...
│ └── ...
├── pages/ # 页面组件(18个页面)
│ ├── HomePage, LoginPage, LibraryPage, SearchPage, ...
│ └── ...
├── components/ # 公共组件(4个)
│ ├── Layout.tsx, MediaCard.tsx, RequireAuth.tsx, GlobalEvents.tsx
├── stores/auth.ts # Zustand 认证状态管理
└── types/index.ts # TypeScript 类型定义(14个接口)
```
### 数据模型(12个实体)
| 实体 | 说明 |
|------|------|
| User | 用户账户(角色: admin/user) |
| Library | 媒体库根目录(类型: movie/tv/anime/music) |
| Media | 单个可播放媒体项 |
| Series | 电视剧集分组 |
| PlaybackHistory | 播放进度记录 |
| Favorite | 收藏标记 |
| Playlist / PlaylistItem | 用户播放列表 |
| DownloadTask | 下载任务(qBittorrent) |
| Subscription | RSS订阅规则 |
| Setting | 系统键值配置 |
| AccessLog | 操作审计日志 |
---
## 二、功能模块详细分析
### 1. 认证与用户系统 ✅ 已完整实现
#### 后端实现
| 功能 | 状态 | 文件位置 |
|------|------|----------|
| 用户注册(首用户自动提升为admin) | ✅ | `service/auth.go` - `Register()` |
| 用户登录(JWT签发,24h有效期) | ✅ | `service/auth.go` - `Login()` |
| 密码修改(验证旧密码) | ✅ | `service/auth.go` - `ChangePassword()` |
| 初始化Admin种子用户 | ✅ | `service/auth.go` - `SeedAdmin()` |
| JWT中间件认证 | ✅ | `middleware/middleware.go` - `AuthRequired()` |
| Admin权限守卫 | ✅ | `middleware/middleware.go` - `AdminRequired()` |
| CORS跨域支持 | ✅ | `middleware/middleware.go` - `CORS()` |
| 用户列表/删除(管理员) | ✅ | `handler/admin.go`, `handler/profile.go` |
| 角色更新(管理员) | ✅ | `handler/profile.go` - `adminUpdateRoleHandler` |
| 个人资料更新 | ✅ | `service/profile.go`, `handler/profile.go` |
**API端点**:
- `POST /api/auth/login` - 登录
- `POST /api/auth/register` - 注册
- `GET /api/me` - 获取当前用户
- `PATCH /api/me` - 更新资料
- `POST /api/me/password` - 修改密码
- `GET /api/admin/users` - 用户列表(管理员)
- `PATCH /api/admin/users/:id/role` - 更新角色(管理员)
- `DELETE /api/admin/users/:id` - 删除用户(管理员)
#### 前端实现
| 功能 | 状态 | 文件位置 |
|------|------|----------|
| 登录页面 | ✅ | `pages/LoginPage.tsx` |
| 注册入口 | ✅ | 登录页集成 |
| JWT状态管理 | ✅ | `stores/auth.ts` (Zustand) |
| 路由守卫 | ✅ | `components/RequireAuth.tsx` |
| 自动401跳转登录 | ✅ | `api/client.ts` 拦截器 |
| Profile页面 | ✅ | `pages/ProfilePage.tsx` |
| Admin管理页面 | ✅ | `pages/AdminPage.tsx` |
---
### 2. 媒体库管理 ✅ 已完整实现
#### 后端实现
| 功能 | 状态 | 文件位置 |
|------|------|----------|
| 创建媒体库 | ✅ | `service/media.go` - `CreateLibrary()` |
| 列出所有媒体库 | ✅ | `service/media.go` - `ListLibraries()` |
| 删除媒体库(级联删除媒体) | ✅ | `service/media.go` - `DeleteLibrary()` |
| 扫描媒体库(发现视频文件) | ✅ | `service/scanner.go` - `ScanLibrary()` |
| FFprobe元数据提取 | ✅ | `service/ffprobe.go` |
| 文件系统监控自动扫描 | ✅ | `service/watcher.go` (fsnotify) |
| 剧集季/集解析 | ✅ | `service/episode_parser.go` - `ParseEpisode()` |
| 媒体分页查询 | ✅ | `service/media.go` - `ListMedia()` |
| 媒体搜索(LIKE模糊匹配) | ✅ | `service/media.go` - `SearchMedia()` |
| 媒体详情查询 | ✅ | `service/media.go` - `GetMedia()` |
| TV剧按季分组API | ✅ | `handler/series.go` - `listSeasonsHandler` |
| 软删除/恢复/永久删除 | ✅ | `service/media.go` (回收站功能) |
**支持的视频格式**: `.mkv`, `.mp4`, `.m4v`, `.avi`, `.mov`, `.webm`, `.ts`, `.rmvb`, `.rm`, `.3gp`, `.mpg`, `.mpeg`, `.strm`
**API端点**:
- `GET /api/libraries` - 列表
- `POST /api/libraries` - 创建(需管理员)
- `DELETE /api/libraries/:id` - 删除(需管理员)
- `POST /api/libraries/:id/scan` - 扫描(需管理员)
- `POST /api/libraries/:id/scrape` - 刮削(需管理员)
- `GET /api/libraries/:id/media` - 媒体列表(分页)
- `GET /api/libraries/:id/seasons` - 按季分组
- `GET /api/media/:id` - 媒体详情
- `GET /api/media?q=` - 搜索
- `DELETE /api/media/:id` - 软删除
- `POST /api/media/:id/restore` - 恢复
- `DELETE /api/media/:id/purge` - 永久删除
- `POST /api/media/:id/probe` - 重新探测
#### 前端实现
| 功能 | 状态 | 文件位置 |
|------|------|----------|
| 首页(继续观看+最近添加) | ✅ | `pages/HomePage.tsx` |
| 媒体库详情页 | ✅ | `pages/LibraryPage.tsx` |
| 媒体详情页 | ✅ | `pages/MediaDetailPage.tsx` |
| 搜索页面 | ✅ | `pages/SearchPage.tsx` |
| 媒体卡片组件 | ✅ | `components/MediaCard.tsx` |
| 回收站页面 | ✅ | `pages/RecycleBinPage.tsx` |
---
### 3. 刮削系统 ✅ 已完整实现(多数据源)
#### 后端实现
| 功能 | 状态 | 文件位置 |
|------|------|----------|
| TMDb电影刮削 | ✅ | `service/tmdb.go` - `SearchMovie()` |
| Bangumi动漫刮削 | ✅ | `service/bangumi.go` - `Search()` |
| TheTVDB电视剧刮削 | 🔶 | `service/thetvdb.go` (结构存在,需确认实现完整性) |
| Fanart.tv封面升级 | 🔶 | `service/fanart.go` (结构存在,需确认实现完整性) |
| 文件名智能清洗 | ✅ | `service/scraper.go` - `CleanQuery()` |
| 年份提取 | ✅ | 正则 `yearPattern` |
| 噪声词过滤 | ✅ | 35+噪声词(分辨率、编码、字幕组等) |
| 季/集号正则提取 | ✅ | `service/episode_parser.go` |
| 单个媒体刮削 | ✅ | `service/scraper.go` - `EnrichOne()` |
| 批量库刮削(后台执行,4 RPS限流) | ✅ | `service/scraper.go` - `EnrichLibrary()` |
| 刮削进度WebSocket推送 | ✅ | 通过WSHub发布"scrape"事件 |
| TMDb代理支持(GFW穿透) | ✅ | 配置 `tmdb_api_proxy` |
| 图片CDN代理 | ✅ | 配置 `tmdb_image_proxy` |
| NFO导出(Kodi/Jellyfin兼容) | ✅ | `service/nfo.go` |
**刮削策略链**:
```
library.type == "anime" → Bangumi (fallback: TMDb)
library.type == "tv" → TheTVDB (fallback: TMDb)
default → TMDb
匹配后可选 Fanart.tv 封面升级
```
**API端点**:
- `POST /api/media/:id/scrape` - 单个刮削(需管理员)
- `POST /api/libraries/:id/scrape` - 批量刮削(需管理员,异步)
- `POST /api/media/:id/nfo` - 导出NFO(需管理员)
- `POST /api/libraries/:id/nfo` - 批量导出NFO(需管理员)
- `GET /api/img?url=...` - 图片代理
---
### 4. 播放与转码 ✅ 已完整实现
#### 后端实现
| 功能 | 状态 | 文件位置 |
|------|------|----------|
| 直接播放(HTTP Range支持) | ✅ | `service/stream.go` - `ServeFile()` |
| HLS转码播放 | ✅ | `service/transcoder.go` |
| HLS M3U8播放列表服务 | ✅ | `service/stream.go` - `ServeHLSPlaylist()` |
| HLS分段服务 | ✅ | `service/stream.go` - `ServeHLSSegment()` |
| 转码任务管理 | ✅ | `TranscoderService` (启动/停止/活跃列表) |
| 软件编码 (libx264) | ✅ | 默认编码器 |
| NVIDIA NVENC硬件加速 | ✅ | encoder = "nvenc" |
| Intel QSV硬件加速 | ✅ | encoder = "qsv" |
| VAAPI硬件加速 | ✅ | encoder = "vaapi" |
| FFprobe媒体信息探测 | ✅ | `service/ffprobe.go` |
| 字幕发现(同目录/subs/子目录) | ✅ | `service/subtitle.go` - `Discover()` |
| SRT→WebVTT转换 | ✅ | `service/subtitle.go` - `srtToVTT()` |
| ASS/SSA→WebVTT转换 | ✅ | `service/subtitle.go` - `assToVTT()` |
| 字幕语言检测 | ✅ | 正则语言标签识别 |
| 转码进度WebSocket推送 | ✅ | 通过WSHub发布"transcode"事件 |
**转码参数**(可配置):
- 视频码率: 1500k(默认)
- 最大码率: 1800k
- 缓冲区: 3000k
- 最大高度: 720p(默认)
- 分段时长: 4秒(默认)
- 音频: AAC 128kHz 立体声
**API端点**:
- `GET /api/stream/:id` - 直接播放
- `GET /api/hls/:id/index.m3u8` - HLS播放列表
- `GET /api/hls/:id/:seg` - HLS分段
- `DELETE /api/hls/:id` - 停止转码
- `GET /api/media/:id/subtitles` - 字幕列表
- `GET /api/subtitles/:id?path=...` - 字幕内容
#### 前端实现
| 功能 | 状态 | 文件位置 |
|------|------|----------|
| 播放器页面 | ✅ | `pages/PlayerPage.tsx` |
| HLS.js集成 | ✅ | 通过HLS.js播放m3u8 |
| 直接播放回退 | ✅ | `<video>` 标签直接播放 |
| 字幕轨道加载 | ✅ | `<track>` 元素 |
| 全局事件处理 | ✅ | `components/GlobalEvents.tsx` |
---
### 5. 下载管理 ✅ 已完整实现
#### 后端实现
| 功能 | 状态 | 文件位置 |
|------|------|----------|
| 添加下载任务(磁力链接/URL) | ✅ | `service/downloads.go` - `AddDownload()` |
| qBittorrent集成 | ✅ | `service/qbittorrent.go` - `QBitClient` |
| 下载列表(数据库+实时状态) | ✅ | `service/downloads.go` - `List()` |
| 删除下载(可选删文件) | ✅ | `service/downloads.go` - `Delete()` |
| 下载配置热重载 | ✅ | `service/downloads.go` - `ReloadConfig()` |
| 后台轮询进度(5s间隔) | ✅ | `service/downloads.go` - `poll()` |
| 进度WebSocket推送 | ✅ | 通过WSHub发布"download"事件 |
**qBittorrent设置**(通过Setting表动态配置):
- `qbittorrent.url` - WebUI地址
- `qbittorrent.username` - 用户名
- `qbittorrent.password` - 密码
- `qbittorrent.savepath` - 默认保存目录
**API端点**:
- `GET /api/downloads` - 下载列表
- `POST /api/downloads` - 添加下载
- `DELETE /api/downloads/:hash?delete_files=true` - 删除下载
- `POST /api/downloads/reload` - 重载配置(需管理员)
#### 前端实现
| 功能 | 状态 | 文件位置 |
|------|------|----------|
| 下载管理页面 | ✅ | `pages/DownloadsPage.tsx` |
| 实时进度显示 | ✅ | WebSocket + REST 双通道 |
---
### 6. RSS 订阅 ✅ 已完整实现
#### 后端实现
| 功能 | 状态 | 文件位置 |
|------|------|----------|
| 创建订阅规则 | ✅ | `service/subscription.go` - `Create()` |
| 订阅列表 | ✅ | `service/subscription.go` - `List()` |
| 删除订阅 | ✅ | `service/subscription.go` - `Delete()` |
| RSS/Atom Feed解析 | ✅ | `service/subscription.go` - `fetch()` |
| 正则过滤规则 | ✅ | `service/subscription.go` - `compileFilter()` |
| GUID去重(防重复下载) | ✅ | Setting存储已见GUID列表(最近200条) |
| 自动轮询(10分钟间隔) | ✅ | `service/subscription.go` - `loop()` |
| 启动后首次快速运行(30s) | ✅ | Timer机制 |
| 手动触发运行 | ✅ | `service/subscription.go` - `RunNow()` |
| 匹配项自动入队下载 | ✅ | 调用DownloadService.AddDownload() |
**API端点**:
- `GET /api/subscriptions` - 订阅列表
- `POST /api/subscriptions` - 创建订阅
- `DELETE /api/subscriptions/:id` - 删除订阅
- `POST /api/subscriptions/:id/run` - 手动触发
#### 前端实现
| 功能 | 状态 | 文件位置 |
|------|------|----------|
| 订阅管理页面 | ✅ | `pages/SubscriptionsPage.tsx` |
---
### 7. AI 功能 ✅ 已完整实现
#### 后端实现
| 功能 | 状态 | 文件位置 |
|------|------|----------|
| AI智能搜索(自然语言→结构化查询) | ✅ | `service/ai.go` - `SmartSearch()` |
| AI推荐(基于历史观看记录) | ✅ | `service/ai.go` - `Recommend()` |
| OpenAI兼容API接入 | ✅ | `/v1/chat/completions` |
| 多Provider支持 | ✅ | OpenAI/DeepSeek/Qwen/Ollama等 |
| AI状态检查 | ✅ | `ai.go` - `Enabled()` |
| 降级处理(AI不可用时返回原始查询) | ✅ | SmartSearch的fallback逻辑 |
**AI能力**:
- **SmartSearch**: 将中英文自然语言查询转换为结构化搜索意图(query/year/genre/type/sort/language)
- **Recommend**: 根据最近观看标题生成推荐片单
**配置项** (`config.AIConfig`):
- `enabled` - 开关
- `provider` - 提供商标识
- `api_base` - API地址
- `api_key` - API密钥
- `model` - 模型名称(默认 gpt-4o-mini)
- `timeout` - 超时时间(默认30s)
- `max_concurrent` - 最大并发数
**API端点**:
- `GET /api/ai/status` - AI状态
- `POST /api/ai/search` - AI智能搜索
- `GET /api/ai/recommend` - AI推荐
#### 前端实现
| 功能 | 状态 | 文件位置 |
|------|------|----------|
| AI搜索集成 | ✅ | `api/ai.ts` |
| 推荐展示 | ✅ | HomePage集成 |
---
### 8. 系统运维 ✅ 已完整实现
#### 后端实现
| 功能 | 状态 | 文件位置 |
|------|------|----------|
| 统计仪表盘 | ✅ | `service/stats.go` - `Compute()` |
| CPU/内存/磁盘监控 | ✅ | `gopsutil` 库 |
| Go运行时指标 | ✅ | goroutine数量/版本号 |
| 系统设置CRUD | ✅ | `repository/setting.go` |
| 操作审计日志 | ✅ | `service/audit.go` |
| 日志查询 | ✅ | `handler/admin.go` - `recentLogsHandler` |
| 活跃任务面板 | ✅ | `handler/tasks.go` (转码+种子) |
| 健康检查 | ✅ | `handler/handler.go` - `healthCheck` |
| 版本信息 | ✅ | `handler/handler.go` - `versionInfo` |
| 优雅关闭 | ✅ | `main.go` (SIGINT/SIGTERM处理) |
| SPA静态文件服务 | ✅ | `main.go` - `serveSPA()` |
| 图片代理(CORS/GFW穿透) | ✅ | `service/image_proxy.go` |
| WebSocket实时推送中心 | ✅ | `service/ws_hub.go` (Hub发布/订阅) |
**统计快照包含**:
- 媒体库总数 / 媒体总数 / 用户总数
- 总磁盘占用 / 总时长
- 最近添加的12条媒体
- 硬件信息(CPU%/内存/磁盘/Go版本/goroutines数)
**API端点**:
- `GET /api/stats` - 统计数据
- `GET /api/tasks` - 活跃任务(管理员)
- `GET /api/settings` - 设置列表(管理员)
- `PUT /api/settings` - 更新设置(管理员)
- `GET /api/logs` - 审计日志(管理员)
- `GET /api/health` - 健康检查
- `GET /api/version` - 版本信息
- `GET /api/ws` - WebSocket连接
- `GET /api/discover/trending` - TMDb热门趋势
- `GET /api/discover/popular` - TMDb热门影片
- `GET /api/recycle` - 回收站(管理员)
#### 前端实现
| 功能 | 状态 | 文件位置 |
|------|------|----------|
| Stats统计页面 | ✅ | `pages/StatsPage.tsx` |
| Tasks任务页面 | ✅ | `pages/TasksPage.tsx` |
| Admin管理面板 | ✅ | `pages/AdminPage.tsx` |
| Discover发现页 | ✅ | `pages/DiscoverPage.tsx` |
| RecycleBin回收站 | ✅ | `pages/RecycleBinPage.tsx` |
| Layout布局组件 | ✅ | `components/Layout.tsx` (导航/侧边栏) |
---
## 三、前端页面清单(18个路由)
| 路径 | 页面 | 认证要求 | 状态 |
|------|------|----------|------|
| `/login` | LoginPage | 公开 | ✅ |
| `/` | HomePage | 登录 | ✅ |
| `/library/:id` | LibraryPage | 登录 | ✅ |
| `/discover` | DiscoverPage | 登录 | ✅ |
| `/search` | SearchPage | 登录 | ✅ |
| `/favourites` | FavouritesPage | 登录 | ✅ |
| `/playlists` | PlaylistsPage | 登录 | ✅ |
| `/playlist/:id` | PlaylistDetailPage | 登录 | ✅ |
| `/media/:id` | MediaDetailPage | 登录 | ✅ |
| `/play/:id` | PlayerPage | 登录 | ✅ |
| `/downloads` | DownloadsPage | 登录 | ✅ |
| `/subscriptions` | SubscriptionsPage | 登录 | ✅ |
| `/profile` | ProfilePage | 登录 | ✅ |
| `/tasks` | TasksPage | 管理员 | ✅ |
| `/recycle` | RecycleBinPage | 管理员 | ✅ |
| `/stats` | StatsPage | 管理员 | ✅ |
| `/admin` | AdminPage | 管理员 | ✅ |
| `*` | 重定向到首页 | - | ✅ |
## 四、前端API模块清单(15个)
| 模块 | 文件 | 功能覆盖 |
|------|------|----------|
| client.ts | Axios实例 | 基础HTTP/JWT拦截/流媒体URL构建/图片代理URL |
| auth.ts | 认证API | login/register/getMe/updateProfile/changePassword |
| library.ts | 媒体库API | list/create/delete/scan/scrape/listMedia/listSeasons |
| playback.ts | 播放API | history/favourites/playlists CRUD |
| downloads.ts | 下载API | list/add/delete/reloadConfig |
| subscriptions.ts | 订阅API | list/create/delete/runNow |
| ai.ts | AI API | status/smartSearch/recommend |
| discover.ts | 发现API | trending/popular |
| admin.ts | 管理API | users/roles/settings/logs |
| profile.ts | 资料API | update |
| recycle.ts | 回收站API | list/restore/purge/softDelete |
| series.ts | 剧集API | seasons |
| stats.ts | 统计API | getStats |
| subtitles.ts | 字幕API | list/serve |
| tasks.ts | 任务API | getTasks |
## 五、依赖清单
### Go后端核心依赖
| 依赖 | 版本 | 用途 |
|------|------|------|
| github.com/gin-gonic/gin | v1.9.1 | HTTP框架 |
| github.com/glebarez/sqlite | v1.11.0 | SQLite驱动(CGo-free) |
| gorm.io/gorm | v1.25.7 | ORM |
| github.com/golang-jwt/jwt/v5 | v5.2.0 | JWT认证 |
| github.com/spf13/viper | v1.18.2 | 配置管理 |
| go.uber.org/zap | v1.27.0 | 结构化日志 |
| github.com/gorilla/websocket | v1.5.3 | WebSocket |
| github.com/fsnotify/fsnotify | v1.7.0 | 文件系统监控 |
| github.com/shirou/gopsutil/v3 | v3.24.5 | 系统监控 |
| github.com/google/uuid | v1.6.0 | UUID生成 |
| golang.org/x/crypto | v0.21.0 | bcrypt密码哈希 |
## 六、功能完成度总结
| 功能模块 | 完成度 | 备注 |
|----------|--------|------|
| 认证与用户系统 | ✅ 100% | JWT + 角色 + 种子Admin + 审计 |
| 媒体库管理 | ✅ 100% | CRUD + 扫描 + 监控 + 搜索 + 回收站 |
| 刮削系统 | ✅ 100% | TMDb + Bangumi + TheTVDB + Fanart + NFO |
| 播放与转码 | ✅ 100% | 直播/HLS/4种硬件加速/字幕 |
| 下载管理 | ✅ 100% | qBittorrent + 实时进度 + 热重载 |
| RSS订阅 | ✅ 100% | RSS解析 + 过滤 + 去重 + 自动入队 |
| AI功能 | ✅ 100% | 智能搜索 + 推荐 + 多Provider |
| 系统运维 | ✅ 100% | 统计/审计/日志/任务/WS/健康检查 |
| 前端页面 | ✅ 100% | 18个路由全部定义 + API全覆盖 |
**整体评估**: MediaStationGo 的代码库是一个**功能完整的媒体服务器实现**,后端约30个Go源文件(~5000行),前端18个页面+15个API模块。所有核心功能模块均有对应的 Handler → Service → Repository 三层实现。项目采用了生产级的架构模式(分层配置、优雅关闭、WebSocket实时推送、硬件加速转码等)。
-298
View File
@@ -1,298 +0,0 @@
classDiagram
direction TB
class Base {
+ID: string
+CreatedAt: time.Time
+UpdatedAt: time.Time
+DeletedAt: gorm.DeletedAt
+BeforeCreate(db: *gorm.DB) error
}
class User {
-Base
+Username: string
+PasswordHash: string
+Role: string
+Tier: string
+Email: string
+AvatarURL: string
+Nickname: string
+IsActive: bool
+ForcePasswordReset: bool
+LastLoginAt: *time.Time
}
class UserPermission {
+ID: string
+UserID: string
+CanViewDashboard: bool
+CanPlayMedia: bool
+CanCast: bool
+CanExternalPlayer: bool
+CanFavorite: bool
+CanViewHistory: bool
+CanEditMedia: bool
+CanRescrape: bool
+CanUseAI: bool
+CanCaptureFrames: bool
+CanManageDownloads: bool
+CanViewDiscover: bool
+CanManageSubscriptions: bool
+CanManageSites: bool
+CanUseAIAssistant: bool
+CanManageUsers: bool
+CanManageFiles: bool
+CanManageStrm: bool
+CanAccessSettings: bool
}
class RefreshToken {
+ID: string
+UserID: string
+TokenHash: string
+ExpiresAt: time.Time
+CreatedAt: time.Time
+Revoked: bool
}
class Library {
-Base
+Name: string
+Path: string
+Type: string
+Enabled: bool
+ScanIntervalMin: int
+MetadataLanguage: string
+AdultContent: bool
+PreferNFO: bool
+EnableWatch: bool
+MinFileSizeMB: int
}
class Media {
-Base
+LibraryID: string
+SeriesID: string
+Title: string
+OriginalName: string
+Path: string
+SizeBytes: int64
+DurationSec: int
+Width: int
+Height: int
+VideoCodec: string
+AudioCodec: string
+Container: string
+PosterURL: string
+BackdropURL: string
+Overview: string
+Rating: float32
+Year: int
+SeasonNum: int
+EpisodeNum: int
+ScrapeStatus: string
+TMDbID: int
+BangumiID: int
+DoubanID: int
+NSFW: bool
+FileHash: string
+IsDuplicate: bool
+DuplicateOfID: string
+STRMURL: string
+Resolution: string
+AudioChannels: int
+HdrFormat: string
+FrameRate: float32
+ColorSpace: string
+BitDepth: int
+Genres: string
}
class Series {
-Base
+LibraryID: string
+Title: string
+PosterURL: string
+BackdropURL: string
+Overview: string
+Rating: float32
+Year: int
+TMDbID: int
+BangumiID: int
}
class SubtitleTrack {
-Base
+MediaID: string
+Language: string
+LanguageName: string
+Path: string
+Source: string
+Codec: string
+IsInternal: bool
+StreamIdx: int
}
class PlaybackHistory {
-Base
+UserID: string
+MediaID: string
+PositionMs: int64
+DurationMs: int64
+WatchedAt: time.Time
+PlayedAt: time.Time
+Completed: bool
+DeviceType: string
+IPAddress: string
}
class Favorite {
-Base
+UserID: string
+MediaID: string
}
class Playlist {
-Base
+UserID: string
+Name: string
+Description: string
+IsPublic: bool
+CoverURL: string
}
class PlaylistItem {
-Base
+PlaylistID: string
+MediaID: string
+Position: int
}
class DownloadClient {
-Base
+Name: string
+Type: string
+Host: string
+Port: int
+Username: string
+Password: string
+Enabled: bool
+Category: string
}
class DownloadTask {
-Base
+ClientID: string
+UserID: string
+Source: string
+URL: string
+SavePath: string
+InfoHash: string
+Status: string
+Progress: float32
+TotalSize: int64
+SpeedDown: int64
+SpeedUp: int64
+Message: string
}
class Site {
-Base
+Name: string
+BaseURL: string
+SiteType: string
+AuthType: string
+Cookie: string
+APIKey: string
+AuthHeader: string
+UserAgent: string
+RSSURL: string
+TimeoutSec: int
+Priority: int
+UseProxy: bool
+RateLimit: int
+Enabled: bool
+LoginStatus: string
+UploadBytes: int64
+DownloadBytes: int64
}
class Subscription {
-Base
+UserID: string
+Name: string
+FeedURL: string
+Filter: string
+Enabled: bool
+LastRunAt: *time.Time
+TMDbID: int
+MediaType: string
+Year: int
+QualityFilter: []byte
+MinSizeMB: int
+MaxSizeMB: int
+ExcludeKeys: string
+IncludeKeys: string
+TotalDownloaded: int
+Status: string
}
class NotifyChannel {
-Base
+Name: string
+ChannelType: string
+Enabled: bool
+Config: string
+EncryptedConfig: string
+Events: string
}
class ApiConfig {
+ID: string
+Provider: string
+APIKey: string
+BaseURL: string
+Extra: string
+Enabled: bool
+Description: string
+LastTestedAt: *time.Time
+TestResult: string
+UpdatedAt: time.Time
}
class STRMRecord {
-Base
+MediaID: string
+URL: string
+Protocol: string
}
class Setting {
+Key: string
+Value: string
+UpdatedAt: time.Time
}
class AccessLog {
-Base
+UserID: string
+Action: string
+Target: string
+IP: string
+Detail: string
}
User "1" -- "1..1" --> UserPermission : has
User "1" -- "*" --> RefreshToken : issues
Library "1" -- "1..*" --> Media : contains
Media "*..*" -- "..1" --> Series : belongs_to
Media "1" -- "1..*" --> SubtitleTrack : has_tracks
User "1" -- "0..*" --> PlaybackHistory : records
User "1" -- "0..*" --> Favorite : marks
User "1" -- "0..*" --> Playlist : owns
Playlist "1" -- "1..*" --> PlaylistItem : contains
DownloadClient "1" -- "0..*" --> DownloadTask : manages
Site "1" -- "0..*" --> Subscription : feeds_into
Subscription "1" -- "0..*" --> DownloadTask : queues
Media "1" -- "0..1" --> STRMRecord : has_strm
-982
View File
@@ -1,982 +0,0 @@
# MediaStation 原版完整功能迁移清单
> 基于 `MediaStation-py`(Python/FastAPI + Vue 3)源代码分析,供 MediaStationGo(Go/Gin + React)重写参考。
>
> 分析日期:2025-07-09
---
## 目录
- [1. 项目概览](#1-项目概览)
- [2. 后端功能清单](#2-后端功能清单)
- [2.1 用户与认证模块](#21-用户与认证模块)
- [2.2 媒体库模块](#22-媒体库模块)
- [2.3 播放模块](#23-播放模块)
- [2.4 下载模块](#24-下载模块)
- [2.5 订阅与站点模块](#25-订阅与站点模块)
- [2.6 系统模块](#26-系统模块)
- [2.7 管理后台模块](#27-管理后台模块)
- [2.8 统计模块](#28-统计模块)
- [2.9 播放列表模块](#29-播放列表模块)
- [2.10 STRM 文件支持模块](#210-strm-文件支持模块)
- [2.11 DLNA/投屏模块](#211-dlna投屏模块)
- [2.12 授权管理模块](#212-授权管理模块)
- [2.13 Emby API 兼容层](#213-emby-api-兼容层)
- [2.14 发现/探索模块](#214-发现探索模块)
- [3. 数据模型清单](#3-数据模型清单)
- [4. 前端功能清单](#4-前端功能清单)
- [5. 部署配置清单](#5-部署配置清单)
- [6. 中间件与基础设施](#6-中间件与基础设施)
- [7. 配置系统](#7-配置系统)
- [8. 技术栈对照表](#8-技术栈对照表)
---
## 1. 项目概览
### 原版架构
- **后端**: Python 3.11+ / FastAPI / SQLAlchemy (async) / APScheduler
- **前端**: Vue 3 + Pinia + Vue Router + TypeScript
- **数据库**: SQLite(默认)/ PostgreSQL(可选)
- **部署**: Docker Compose / Nginx 反向代理
### 核心定位
MediaStation 是一个轻量级家庭媒体服务器,融合 **媒体播放 + 自动化订阅下载 + 多平台资源聚合**。
---
## 2. 后端功能清单
### 2.1 用户与认证模块
> 源文件:`backend/app/user/` (router.py, service.py, repository.py, auth.py, models.py, schemas.py)
> 依赖注入:`backend/app/deps.py`
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 用户登录 | POST | `/api/auth/login` | 用户名+密码登录,返回 JWT access_token + refresh_token | 公开 |
| Token 刷新 | POST | `/api/auth/refresh` | 通过 refresh_token 获取新的 access_token | 公开 |
| 获取当前用户 | GET | `/api/auth/me` | 返回当前登录用户信息 | 登录 |
| 修改密码 | POST | `/api/auth/change-password` | 当前用户修改自己的密码 | 登录 |
| 更新资料 | PATCH | `/api/auth/profile` | 更新头像等个人资料 | 登录 |
| 获取权限 | GET | `/api/auth/permissions` | 获取当前用户的功能权限列表 | 登录 |
| 用户列表 | GET | `/api/users` | 获取所有用户列表 | 管理员 |
| 创建用户 | POST | `/api/users` | 创建用户(免费版限30人) | 管理员 |
| 更新用户 | PUT | `/api/users/{id}` | 更新用户信息 | 管理员 |
| 删除用户 | DELETE | `/api/users/{id}` | 删除用户 | 管理员 |
| 获取用户权限 | GET | `/api/users/{id}/permissions` | 获取指定用户的功能权限 | 管理员 |
| 更新用户权限 | PUT | `/api/users/{id}/permissions` | 更新指定用户的功能权限 | 管理员 |
| 重置用户权限 | POST | `/api/users/{id}/permissions/reset` | 重置为默认权限 | 管理员 |
| 系统配置(用户) | GET | `/api/system/config` | 获取系统级用户配置(FREE/PLUS) | 管理员 |
| 更新系统配置 | PUT | `/api/system/config` | 更新系统配置(tier/最大用户数) | 管理员 |
| 观看历史统计 | GET | `/api/watch-history/stats` | 获取当前用户观看历史统计 | 登录 |
| 观看历史列表 | GET | `/api/watch-history` | 分页获取观看历史 | 登录 |
| 继续观看列表 | GET | `/api/watch-history/continue` | 获取未看完的媒体列表 | 登录 |
| 删除单条历史 | DELETE | `/api/watch-history/{id}` | 删除单条历史(管理员可删任何人的) | 登录 |
| 清空历史 | DELETE | `/api/watch-history` | 清空历史(可指定媒体ID) | 登录 |
**认证机制细节**:
- JWT (HS256) access_token(60分钟)+ refresh_token(30天)
- 密码哈希:pbkdf2_sha256
- 依赖注入:`get_current_user`, `require_admin`, `require_permission(permission_field)`, `get_user_permissions`
**权限系统(19 项细粒度权限)**:
- 基础权限(默认开启):`can_view_dashboard`, `can_play_media`, `can_cast`, `can_external_player`, `can_favorite`, `can_view_history`
- 受限功能(默认关闭):`can_edit_media`, `can_rescrape`, `can_use_ai`, `can_capture_frames`, `can_manage_downloads`, `can_view_discover`, `can_manage_subscriptions`, `can_manage_sites`, `can_use_ai_assistant`, `can_manage_users`, `can_manage_files`, `can_manage_strm`, `can_access_settings`
- Plus 用户(tier=plus)自动获得所有权限
- 管理员(role=admin)自动获得所有权限
**用户角色与层级**:
- 角色:admin / user
- 层级:free / plus(免费版限30用户,Plus 无限)
---
### 2.2 媒体库模块
> 源文件:`backend/app/media/` (router.py, service.py, repository.py, models.py, schemas.py, scanner.py, scraper.py, organizer.py, watcher.py, subtitle_service.py, duplicate.py, image_proxy.py, bangumi_scraper.py, douban_scraper.py, parse_code.py, providers/)
#### 2.2.1 媒体库管理
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 媒体库列表 | GET | `/api/libraries` | 获取所有媒体库 | 登录 |
| 创建媒体库 | POST | `/api/libraries` | 创建新媒体库 | 管理员 |
| 扫描媒体库 | POST | `/api/libraries/{id}/scan` | 触发媒体库扫描+自动刮削 | 管理员 |
| 更新媒体库 | PUT | `/api/libraries/{id}` | 更新媒体库配置 | 管理员 |
| 删除媒体库 | DELETE | `/api/libraries/{id}` | 删除媒体库 | 管理员 |
#### 2.2.2 媒体条目管理
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 媒体列表 | GET | `/api/media` | 分页+多维筛选(类型/类型/年份/评分/排序) | 登录 |
| 最近添加 | GET | `/api/media/recent` | 获取最近添加的媒体 | 登录 |
| 媒体统计 | GET | `/api/media/stats` | 获取媒体数量统计 | 登录 |
| 媒体详情 | GET | `/api/media/{id}` | 获取媒体详情(含季/集/字幕) | 登录 |
| 删除媒体 | DELETE | `/api/media/{id}` | 删除媒体条目 | 管理员 |
| 更新媒体 | PUT | `/api/media/{id}` | 手动编辑媒体元数据 | 管理员 |
| 视频截帧 | GET | `/api/media/{id}/thumbnail` | FFmpeg 视频截帧(缩略图) | 登录 |
#### 2.2.3 元数据刮削
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 刮削媒体 | POST | `/api/media/{id}/scrape` | 手动触发刮削(可指定 TMDb ID) | 管理员 |
| 搜索 TMDb | GET | `/api/search/tmdb` | 搜索 TMDb 数据库 | 登录 |
| 搜索豆瓣 | GET | `/api/search/douban` | 搜索豆瓣影视 | 登录 |
| 搜索 Bangumi | GET | `/api/search/bangumi` | 搜索 Bangumi 动漫数据库 | 登录 |
| Adult 刮削测试 | POST | `/api/media/scrape/test` | 测试 Adult Provider 刮削 | 管理员 |
**元数据 Provider Chain(多源聚合)**:
- `TMDbProvider` — TMDb 主数据源(电影/剧集)
- `DoubanProvider` — 豆瓣中文元数据补充
- `BangumiProvider` — Bangumi 番剧/动画数据源
- `AdultProvider` — 18+ 番号刮削(多层 Fallback:JavBus → JavDB → 微服务)
- Provider Chain 支持优先级调度和自动降级
#### 2.2.4 搜索功能
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 全局搜索 | GET | `/api/search` | 搜索本地媒体库 | 登录 |
| 高级搜索 | GET | `/api/search/advanced` | 多条件组合搜索(标题/类型/年份/评分/分辨率/字幕) | 登录 |
| 混合搜索 | GET | `/api/search/mixed` | 并发搜索本地+TMDb | 登录 |
#### 2.2.5 推荐系统
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 智能推荐 | GET | `/api/recommend` | 基于高评分+热度的推荐 | 登录 |
| 相似推荐 | GET | `/api/recommend/similar/{id}` | 基于同类型/标签/年代的相似内容 | 登录 |
#### 2.2.6 字幕管理
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 字幕列表 | GET | `/api/media/{id}/subtitles` | 获取媒体字幕列表 | 登录 |
| 扫描外挂字幕 | POST | `/api/media/{id}/subtitles/scan` | 扫描外挂字幕文件 | 管理员 |
| 检测内嵌字幕 | POST | `/api/media/{id}/subtitles/extract` | 检测内嵌字幕流 | 管理员 |
| 提取内嵌字幕 | POST | `/api/media/{id}/subtitles/extract/{idx}` | 提取内嵌字幕为 SRT | 管理员 |
| 上传字幕 | POST | `/api/media/{id}/subtitles/upload` | 上传字幕文件 | 管理员 |
| 获取字幕内容 | GET | `/api/subtitles/{id}/content` | 获取字幕文件内容 | 登录 |
| 删除字幕 | DELETE | `/api/subtitles/{id}` | 删除字幕(可选删除文件) | 管理员 |
#### 2.2.7 收藏功能
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 添加收藏 | POST | `/api/media/{id}/favorite` | 添加收藏 | 登录 |
| 取消收藏 | DELETE | `/api/media/{id}/favorite` | 取消收藏 | 登录 |
| 检查收藏状态 | GET | `/api/media/{id}/favorite/status` | 检查是否已收藏 | 登录 |
| 收藏列表 | GET | `/api/favorites` | 分页获取收藏列表 | 登录 |
#### 2.2.8 重复检测
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 计算文件哈希 | POST | `/api/libraries/{id}/duplicates/hash` | 计算文件哈希(重复检测前置) | 管理员 |
| 检测重复 | POST | `/api/libraries/{id}/duplicates/scan` | 检测并标记重复文件 | 管理员 |
| 重复文件列表 | GET | `/api/libraries/{id}/duplicates` | 获取重复文件列表 | 登录 |
| 取消重复标记 | DELETE | `/api/libraries/{id}/duplicates` | 取消所有重复标记 | 管理员 |
| 取消单项重复标记 | POST | `/api/media/{id}/duplicate/unmark` | 取消单个条目重复标记 | 管理员 |
#### 2.2.9 文件整理
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 整理文件 | POST | `/api/media/organize` | 手动触发文件整理到媒体库 | 管理员 |
#### 2.2.10 图片代理
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 本地图片访问 | GET | `/api/media/image/{filename}` | 访问本地保存的图片(防路径遍历) | 登录 |
| 图片代理 | GET | `/api/media/proxy-image` | 代理外部图片(绕过防盗链) | 登录 |
---
### 2.3 播放模块
> 源文件:`backend/app/playback/` (router.py, external.py, service.py, transcoder.py, models.py)
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 播放信息 | GET | `/api/playback/{id}/info` | 获取媒体播放信息 | 登录 |
| 视频流 | GET | `/api/playback/{id}/stream` | 直接视频流(支持 Range 断点续传 + query token) | 登录 |
| 外部播放器直链 | GET | `/api/playback/{id}/external-url` | 生成带 token 的外部播放直链 | 登录 |
| 外部播放器协议 | GET | `/api/playback/{id}/external-players` | 生成各播放器协议直链(PotPlayer/VLC/IINA/Infuse/NPlayer/MX/MPV/MPC-HC) | 登录 |
| 外部播放流 | GET | `/api/playback/{id}/external-stream` | 外部播放器流式传输(支持 Range) | Token |
| HLS 播放列表 | GET | `/api/playback/hls/{job}/playlist.m3u8` | HLS m3u8 播放列表 | Token |
| HLS 分片 | GET | `/api/playback/hls/{job}/{segment}` | HLS ts 分片 | Token |
| 转码状态 | GET | `/api/playback/transcode/{job}/status` | 获取转码任务状态 | 登录 |
| 字幕文件 | GET | `/api/playback/subtitles/{id}` | 获取字幕文件流 | 登录 |
| 上报进度 | POST | `/api/playback/{id}/progress` | 上报播放进度 | 登录 |
**播放功能特性**:
- HTTP Range 断点续传(206 Partial Content)
- 多种认证方式:Bearer Token / Query Token / 一次性票据
- 硬件加速转码(auto/qsv/vaapi/nvenc/videotoolbox/none)
- HLS 转码输出
- 外部播放器协议直链(8种播放器)
- 转码并发控制(max_transcode_jobs)
- 转码缓存自动清理
---
### 2.4 下载模块
> 源文件:`backend/app/download/` (router.py, service.py, clients.py, models.py, schemas.py)
#### 2.4.1 下载客户端管理
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 客户端列表 | GET | `/api/download/clients` | 获取所有下载客户端 | 登录 |
| 创建客户端 | POST | `/api/download/clients` | 添加下载客户端 | 管理员 |
| 获取客户端 | GET | `/api/download/clients/{id}` | 获取客户端详情 | 登录 |
| 更新客户端 | PUT | `/api/download/clients/{id}` | 更新客户端配置 | 管理员 |
| 删除客户端 | DELETE | `/api/download/clients/{id}` | 删除客户端 | 管理员 |
| 测试连接 | POST | `/api/download/clients/{id}/test` | 测试客户端连接 | 管理员 |
#### 2.4.2 下载任务管理
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 任务列表 | GET | `/api/download/tasks` | 分页获取下载任务 | 登录 |
| 添加任务 | POST | `/api/download/add` | 添加下载任务 | 登录 |
| 暂停任务 | POST | `/api/download/{id}/pause` | 暂停下载 | 登录 |
| 恢复任务 | POST | `/api/download/{id}/resume` | 恢复下载 | 登录 |
| 删除任务 | DELETE | `/api/download/{id}` | 删除任务(可选删除文件) | 登录 |
| 同步状态 | POST | `/api/download/sync` | 手动同步下载状态 | 管理员 |
| 自动同步 | POST | `/api/download/start-auto-sync` | 启动后台自动进度同步(5秒间隔) | 登录 |
#### 2.4.3 Aria2 扩展
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| Aria2 统计 | GET | `/api/download/aria2/stats` | Aria2 全局统计(活跃/等待/停止/速度) | 登录 |
#### 2.4.4 整理入库
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 批量整理 | POST | `/api/download/organize` | 手动触发所有已完成任务整理入库 | 管理员 |
| 单个整理 | POST | `/api/download/{id}/organize` | 手动整理单个下载任务 | 管理员 |
**下载客户端适配器**:
- qBittorrent(WebUI API)
- Transmission(RPC API)
- Aria2(JSON-RPC)
---
### 2.5 订阅与站点模块
> 源文件:`backend/app/subscribe/` (router.py, service.py, site_adapter.py, notifier.py, models.py, schemas.py)
#### 2.5.1 站点管理
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 站点列表 | GET | `/api/sites` | 获取所有站点配置 | 登录 |
| 创建站点 | POST | `/api/sites` | 添加站点 | 管理员 |
| 更新站点 | PUT | `/api/sites/{id}` | 更新站点配置 | 管理员 |
| 删除站点 | DELETE | `/api/sites/{id}` | 删除站点 | 管理员 |
| 测试站点 | POST | `/api/sites/{id}/test` | 测试站点连接 | 管理员 |
| 浏览站点资源 | GET | `/api/sites/{id}/resource` | 分页浏览站点资源列表 | 登录 |
| 刷新用户数据 | GET | `/api/sites/{id}/userdata` | 获取站点用户数据(上传/下载量等) | 管理员 |
**支持站点类型**:
- **NexusPHP** — 国内绝大多数 PT 站
- **Gazelle/Luminance** — HDBits/OPS 等
- **UNIT3D** — BeyondHD/BluTopia 等
- **MTeam** — 馒头专用 REST API
- **Discuz** — 论坛型资源站
- **Custom RSS** — 自定义 RSS
**认证方式**:
- Cookie / API Key / Authorization Header
#### 2.5.2 资源搜索(跨站聚合)
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 跨站搜索 | GET | `/api/search/sites` | 多站点资源聚合搜索 | 登录 |
#### 2.5.3 订阅管理
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 订阅列表 | GET | `/api/subscriptions` | 获取订阅列表 | 登录 |
| 创建订阅 | POST | `/api/subscriptions` | 创建订阅 | 登录 |
| 更新订阅 | PUT | `/api/subscriptions/{id}` | 更新订阅 | 登录 |
| 删除订阅 | DELETE | `/api/subscriptions/{id}` | 删除订阅 | 管理员 |
| 按媒体查订阅 | GET | `/api/subscriptions/media/{mediaid}` | 支持 tmdb:/douban:/bangumi: 前缀 | 登录 |
| 触发搜索 | POST | `/api/subscriptions/{id}/search` | 手动触发订阅搜索 | 登录 |
| 分享订阅 | POST | `/api/subscriptions/{id}/share` | 创建订阅分享 | 登录 |
| 复制订阅 | POST | `/api/subscriptions/{id}/fork` | 从分享链接复制订阅 | 登录 |
**订阅过滤条件**:
- 画质优先级列表
- 最小/最大文件大小
- 包含/排除关键词
#### 2.5.4 通知渠道
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 渠道列表 | GET | `/api/notify/channels` | 获取通知渠道列表 | 登录 |
| 创建渠道 | POST | `/api/notify/channels` | 创建通知渠道 | 管理员 |
| 更新渠道 | PUT | `/api/notify/channels/{id}` | 更新通知渠道 | 管理员 |
| 删除渠道 | DELETE | `/api/notify/channels/{id}` | 删除通知渠道 | 管理员 |
| 测试渠道 | POST | `/api/notify/channels/{id}/test` | 发送测试通知 | 管理员 |
**通知渠道类型**:
- Telegram
- 微信(Server酱)
- Bark (iOS)
- Webhook
- Email
#### 2.5.5 RSS
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 拉取 RSS | POST | `/api/rss/pull` | 手动拉取所有站点 RSS | 管理员 |
---
### 2.6 系统模块
> 源文件:`backend/app/system/` (router.py, settings_router.py, settings_service.py, api_config_router.py, api_config_service.py, api_config_models.py, scheduler.py, events.py, crypto.py, models.py)
#### 2.6.1 系统信息
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 健康检查 | GET | `/api/health` | 健康检查端点 | 公开 |
| 系统信息 | GET | `/api/system/info` | 获取系统详细信息 | 登录 |
| 系统状态 | GET | `/api/system/status` | CPU/内存/磁盘使用率 | 登录 |
| 系统配置 | GET | `/api/system/config` | 获取可编辑系统配置(密钥掩码) | 管理员 |
| 更新系统配置 | PATCH | `/api/system/config` | 更新系统配置(写入 .env) | 管理员 |
#### 2.6.2 SSE 实时事件
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 获取 SSE 票据 | GET | `/api/system/events/ticket` | 生成一次性 SSE 票据(OTP,10秒有效) | 登录 |
| SSE 事件流 | GET | `/api/system/events` | SSE 实时事件推送 | 登录 |
**SSE 安全机制**:
- 一次性票据(OTP)认证(推荐,防 Nginx 日志泄露 JWT)
- 兼容 Authorization Header 认证
- 兼容 URL query token 认证(旧版)
**事件类型**:下载进度、扫描进度、通知消息等
#### 2.6.3 定时任务
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 调度器信息 | GET | `/api/system/scheduler` | 获取定时任务列表 | 管理员 |
| 触发任务 | POST | `/api/system/scheduler/{id}/trigger` | 手动触发定时任务 | 管理员 |
**内置定时任务**:
| 任务 ID | 名称 | 间隔 | 说明 |
|---------|------|------|------|
| `media_scan` | 媒体库扫描 | 60分钟 | 扫描+增量刮削 |
| `subscription_search` | 订阅搜索 | 60分钟 | 处理所有订阅 |
| `download_sync` | 下载状态同步 | 30秒 | 同步下载进度 |
| `rss_pull` | RSS 拉取 | 30分钟 | 拉取所有站点 RSS |
| `cache_cleanup` | 转码缓存清理 | 每天3:00 | 清理24小时以上的转码缓存 |
| `download_complete` | 下载完成整理 | 5分钟 | 检测完成并自动整理入库 |
#### 2.6.4 整理与刮削配置
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 获取所有配置 | GET | `/api/settings` | 获取所有整理/刮削配置 | 管理员 |
| 配置 Schema | GET | `/api/settings/schema` | 获取配置表单 Schema | 管理员 |
| 获取单个配置 | GET | `/api/settings/{key}` | 获取单个配置 | 管理员 |
| 更新单个配置 | PUT | `/api/settings/{key}` | 更新单个配置 | 管理员 |
| 批量更新 | PATCH | `/api/settings` | 批量更新配置 | 管理员 |
| 重置配置 | DELETE | `/api/settings/{key}` | 重置为默认值 | 管理员 |
| 重置所有 | DELETE | `/api/settings` | 重置所有配置 | 管理员 |
#### 2.6.5 API 配置管理
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 配置列表 | GET | `/api/api-config` | 获取所有 API 配置 | 管理员 |
| 获取配置 | GET | `/api/api-config/{provider}` | 获取指定 Provider 配置 | 管理员 |
| 获取生效配置 | GET | `/api/api-config/{provider}/effective` | 获取合并后的生效配置 | 管理员 |
| 更新配置 | POST | `/api/api-config/{provider}` | 更新 API 配置 | 管理员 |
| 清除配置 | DELETE | `/api/api-config/{provider}` | 清除 API Key | 管理员 |
| 测试连接 | POST | `/api/api-config/{provider}/test` | 测试 API 连接 | 管理员 |
| Provider 列表 | GET | `/api/api-config/providers/list` | 获取支持的 Provider 列表 | 管理员 |
**预置 Provider**:
- `tmdb` — TMDb API
- `douban` — 豆瓣
- `bangumi` — Bangumi
- `thetvdb` — TheTVDB
- `fanart` — Fanart.tv
- `openai` — OpenAI 兼容 API
- `siliconflow` — 硅基流动
- `deepseek` — DeepSeek
- `adult` — Adult Provider (JavBus/JavDB)
#### 2.6.6 敏感数据加密
> 源文件:`backend/app/system/crypto.py`
- 使用 Fernet (AES-128-CBC) 加密存储 API Key、Passkey 等敏感字段
- 基于 APP_SECRET_KEY 派生加密密钥
- 加密数据前缀标识 `enc:v1:`,兼容旧版明文迁移
---
### 2.7 管理后台模块
> 源文件:`backend/app/admin/` (router.py, service.py, schemas.py, backup_service.py)
#### 2.7.1 定时任务管理
| 功能 | HTTP 方法 | 端点 | 说明 |
|------|----------|------|------|
| 定时任务列表 | GET | `/api/admin/scheduler/tasks` | 获取可管理的定时任务 |
| 创建定时任务 | POST | `/api/admin/scheduler/tasks` | 创建自定义定时任务 |
| 更新定时任务 | PUT | `/api/admin/scheduler/tasks/{id}` | 更新定时任务 |
| 删除定时任务 | DELETE | `/api/admin/scheduler/tasks/{id}` | 删除定时任务 |
#### 2.7.2 批量操作
| 功能 | HTTP 方法 | 端点 | 说明 |
|------|----------|------|------|
| 批量扫描 | POST | `/api/admin/media/batch/scan` | 批量扫描媒体库 |
| 批量刮削 | POST | `/api/admin/media/batch/scrape` | 批量刮削媒体 |
| 批量删除 | POST | `/api/admin/media/batch/delete` | 批量删除媒体 |
| 批量移动 | POST | `/api/admin/media/batch/move` | 批量移动媒体到其他库 |
| 批量收藏 | POST | `/api/admin/media/batch/favorite` | 批量收藏 |
| 批量标记已看 | POST | `/api/admin/media/batch/watched` | 批量标记为已看 |
| 批量重命名 | POST | `/api/admin/media/batch/rename` | 批量重命名文件 |
| AI 重命名 | POST | `/api/admin/media/batch/ai-rename` | AI 智能重命名 |
#### 2.7.3 内容分级
| 功能 | HTTP 方法 | 端点 | 说明 |
|------|----------|------|------|
| 获取分级 | GET | `/api/admin/content-rating` | 获取内容分级配置 |
| 更新分级 | PUT | `/api/admin/content-rating` | 更新内容分级 |
#### 2.7.4 文件管理
| 功能 | HTTP 方法 | 端点 | 说明 |
|------|----------|------|------|
| 浏览文件 | GET | `/api/admin/files/browse` | 浏览文件目录 |
| 文件操作 | POST | `/api/admin/files/operation` | 文件操作(移动/复制/删除) |
| 重命名预览 | GET | `/api/admin/files/rename/preview` | 重命名预览 |
| 批量重命名预览 | POST | `/api/admin/files/rename/batch-preview` | 批量重命名预览 |
| 执行重命名 | POST | `/api/admin/files/rename/execute` | 执行重命名 |
| 创建文件夹 | POST | `/api/admin/files/folder` | 创建文件夹 |
| 重命名文件夹 | PUT | `/api/admin/files/folder/{path}` | 重命名文件夹 |
| 删除文件夹 | DELETE | `/api/admin/files/folder/{path}` | 删除文件夹 |
#### 2.7.5 系统管理
| 功能 | HTTP 方法 | 端点 | 说明 |
|------|----------|------|------|
| 系统设置 | GET/PUT | `/api/admin/settings` | 获取/更新系统设置 |
| 系统统计 | GET | `/api/admin/stats` | 获取系统统计信息 |
| 系统备份 | POST | `/api/admin/backup` | 触发系统备份 |
| 备份列表 | GET | `/api/admin/backup/list` | 获取备份列表 |
| 恢复备份 | POST | `/api/admin/backup/restore` | 从备份恢复 |
---
### 2.8 统计模块
> 源文件:`backend/app/stats/` (router.py, service.py, schemas.py)
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 概览统计 | GET | `/api/stats/overview` | 媒体总数/电影/剧集/大小/用户/播放次数 | 公开 |
| 播放趋势 | GET | `/api/stats/trend` | 按小时/天/周的播放趋势 | 公开 |
| 热门内容 | GET | `/api/stats/top-content` | 播放次数最多的媒体 | 公开 |
| 活跃用户 | GET | `/api/stats/top-users` | 播放次数最多的用户 | 公开 |
| 媒体库统计 | GET | `/api/stats/libraries` | 各媒体库统计 | 管理员 |
| 系统监控 | GET | `/api/stats/monitor` | CPU/内存/磁盘/网络监控 | 管理员 |
| 用户统计 | GET | `/api/stats/user/{id}` | 用户播放统计 | 管理员 |
| 记录播放 | POST | `/api/stats/play` | 记录播放事件 | 登录 |
---
### 2.9 播放列表模块
> 源文件:`backend/app/playlist/` (router.py, service.py, models.py, schemas.py)
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 播放列表列表 | GET | `/api/playlists` | 获取用户播放列表 | 登录 |
| 播放列表详情 | GET | `/api/playlists/{id}` | 获取列表详情(含媒体项) | 登录 |
| 创建播放列表 | POST | `/api/playlists` | 创建播放列表 | 登录 |
| 更新播放列表 | PUT | `/api/playlists/{id}` | 更新播放列表 | 登录 |
| 删除播放列表 | DELETE | `/api/playlists/{id}` | 删除播放列表 | 登录 |
| 添加项目 | POST | `/api/playlists/{id}/items` | 添加媒体到播放列表 | 登录 |
| 移除项目 | DELETE | `/api/playlists/{id}/items/{item_id}` | 从播放列表移除 | 登录 |
| 重新排序 | PUT | `/api/playlists/{id}/reorder` | 重新排序播放列表 | 登录 |
---
### 2.10 STRM 文件支持模块
> 源文件:`backend/app/strm/` (router.py, schemas.py, __init__.py)
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| STRM 配置 | GET | `/api/admin/strm/config` | 获取 STRM 配置 | 管理员 |
| 更新 STRM 配置 | PUT | `/api/admin/strm/config` | 更新 STRM 配置 | 管理员 |
| 获取 STRM URL | GET | `/api/admin/strm/media/{id}` | 获取媒体 STRM URL | 管理员 |
| 设置 STRM URL | PUT | `/api/admin/strm/media/{id}` | 设置媒体 STRM URL(协议白名单校验) | 管理员 |
| 清除 STRM URL | DELETE | `/api/admin/strm/media/{id}` | 清除 STRM URL | 管理员 |
| Emby STRM 播放信息 | GET | `/api/admin/strm/emby/Items/{id}/PlaybackInfo` | Emby 兼容 STRM 播放 | 公开 |
**STRM 功能**:将外部存储(WebDAV/Alist/S3/HTTP 直链)以"文件"形式加入媒体库,播放时直接访问远程 URL。
---
### 2.11 DLNA/投屏模块
> 源文件:`backend/app/dlna/__init__.py`(当前为 stub 实现)
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 发现设备 | GET | `/api/dlna/devices` | 发现 DLNA 设备 | 登录 |
| 获取设备 | GET | `/api/dlna/devices/{id}` | 获取设备信息 | 登录 |
| 投屏 | POST | `/api/dlna/cast` | 投屏媒体到设备 | 登录 |
> **注意**:当前 DLNA 为 stub 实现,返回空列表。Go 版可考虑完整实现。
---
### 2.12 授权管理模块
> 源文件:`backend/app/license/` (router.py, schemas.py, __init__.py)
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 授权信息 | GET | `/api/license/info` | 获取基本授权信息 | 登录 |
| 授权状态 | GET | `/api/license/status` | 获取详细授权状态 | 登录 |
| 激活授权 | POST | `/api/license/activate` | 通过授权码激活 Plus | 登录 |
| 解绑授权 | POST | `/api/license/unbind` | 解绑当前设备 | 登录 |
| 授权配置 | GET/POST | `/api/license/config` | 获取/更新授权配置 | 管理员 |
| 测试连接 | POST | `/api/license/config/test` | 测试授权服务器连接 | 管理员 |
| 心跳状态 | GET | `/api/license/heartbeat-status` | 获取心跳状态 | 登录 |
| 刷新授权 | POST | `/api/license/refresh` | 刷新授权状态 | 登录 |
| 生成授权码 | POST | `/api/license/generate` | 生成授权码 | 管理员 |
| 授权码列表 | GET | `/api/license/list` | 获取授权码列表 | 管理员 |
| 激活记录 | GET | `/api/license/{id}/activations` | 获取激活记录 | 管理员 |
| 吊销授权码 | POST | `/api/license/{id}/revoke` | 吊销授权码 | 管理员 |
| 解绑设备 | POST | `/api/license/activation/{id}/unbind` | 解绑指定设备 | 管理员 |
**Plus 版特性**:
- 无用户数量限制(免费版限30人)
- Plus 用户自动获得所有功能权限
- 授权码格式:`MS-XXXX-XXXX-XXXX-XXXX`
- 验证模式:本地验证 / 在线服务器验证
---
### 2.13 Emby API 兼容层
> 源文件:`backend/app/emby_api.py`(~1800 行,完整的 Emby Server API v3 兼容)
提供 Emby API 子集,让 **Infuse、Kodi、Fileball** 等客户端可以直接连接 MediaStation。
**核心 Emby 端点**(仅列出关键部分,实际约 50+ 端点):
| 功能 | HTTP 方法 | 端点 | 说明 |
|------|----------|------|------|
| Emby 认证 | POST | `/api/emby/Users/AuthenticateByName` | Emby 客户端认证 |
| 系统信息 | GET | `/api/emby/System/Info` | Emby 系统信息 |
| 媒体库列表 | GET | `/api/emby/Library/VirtualFolders` | 虚拟文件夹(媒体库) |
| 媒体列表 | GET | `/api/emby/Items` | 媒体条目列表 |
| 媒体详情 | GET | `/api/emby/Users/{uid}/Items/{id}` | 媒体详情 |
| 搜索 | GET | `/api/emby/Items?searchTerm=` | 媒体搜索 |
| 播放信息 | GET | `/api/emby/Items/{id}/PlaybackInfo` | 获取播放流信息 |
| 视频流 | GET | `/api/emby/Videos/{id}/stream` | 视频流直链 |
| 字幕流 | GET | `/api/emby/Videos/{id}/Subtitles/{sid}/Stream` | 字幕流 |
| 播放进度上报 | POST | `/api/emby/Sessions/Playing` | 上报播放进度 |
| 播放停止 | POST | `/api/emby/Sessions/Playing/Stopped` | 播放停止上报 |
| 最新添加 | GET | `/api/emby/Users/{uid}/Items/Latest` | 最新添加的媒体 |
| 继续观看 | GET | `/api/emby/Users/{uid}/Items/Resume` | 继续观看列表 |
**Emby 认证方式**:
- X-Emby-Token Header
- Authorization: Bearer Token
- Emby 用户名/密码认证
---
### 2.14 发现/探索模块
> 源文件:`backend/app/media/discover_router.py`
| 功能 | HTTP 方法 | 端点 | 说明 | 权限 |
|------|----------|------|------|------|
| 可用区块列表 | GET | `/api/discover/sections` | 获取所有推荐区块(含可用状态) | 登录 |
| 聚合发现页 | GET | `/api/discover/feed` | 聚合各数据源推荐内容 | 登录 |
| 图片代理 | GET | `/api/discover/image-proxy` | 代理外部图片(绕过豆瓣防盗链) | 登录 |
**推荐区块(12 个)**:
| Key | 标签 | 数据源 |
|-----|------|--------|
| `recent_movies` | 最近添加电影 | 本地 |
| `recent_tv` | 最近添加剧集 | 本地 |
| `top_rated` | 评分最高 | 本地 |
| `anime` | 动漫推荐 | 本地 |
| `tmdb_trending` | 流行趋势 | TMDb |
| `tmdb_now_playing` | 正在热映 | TMDb |
| `tmdb_popular_movies` | TMDB 热门电影 | TMDb |
| `tmdb_popular_tv` | TMDB 热门电视剧 | TMDb |
| `douban_hot_movies` | 豆瓣热门电影 | 豆瓣 |
| `douban_hot_tv` | 豆瓣热门电视剧 | 豆瓣 |
| `douban_hot_anime` | 豆瓣热门动漫 | 豆瓣 |
| `douban_top250` | 豆瓣 TOP250 | 豆瓣 |
| `bangumi_daily` | Bangumi 每日放送 | Bangumi |
---
## 3. 数据模型清单
> 源文件:`backend/app/base_models.py`, 各模块 `models.py`
### 公共基类
| 模型 | 说明 |
|------|------|
| `Base` | SQLAlchemy 声明基类 |
| `TimestampMixin` | 时间戳混入(created_at, updated_at) |
### 用户模块
| 表名 | 模型 | 关键字段 | 说明 |
|------|------|---------|------|
| `users` | User | id, username, password_hash, role, tier, avatar, nickname, is_active, last_login | 用户表 |
| `user_permissions` | UserPermission | id, user_id, can_* (19个权限字段) | 用户功能权限表 |
| `system_config` | SystemConfig | id, key, value, value_type | 系统配置表 |
| `watch_history` | WatchHistory | id, user_id, media_item_id, episode_id, progress, duration, completed, last_watched | 观看历史 |
### 媒体模块
| 表名 | 模型 | 关键字段 | 说明 |
|------|------|---------|------|
| `media_libraries` | MediaLibrary | id, name, path, media_type, scan_interval, enabled, min_file_size, metadata_language, adult_content, prefer_nfo, enable_watch | 媒体库 |
| `media_items` | MediaItem | id, library_id, tmdb_id, douban_id, bangumi_id, title, original_title, year, overview, poster_url, backdrop_url, media_type, rating, genres, file_path, file_size, duration, video/audio_codec, resolution, strm_url, hdr_format, audio_channels, frame_rate, color_space, bit_depth, is_duplicate, duplicate_of, file_hash | 媒体条目 |
| `media_seasons` | MediaSeason | id, media_item_id, season_number, name, poster_url | 季 |
| `media_episodes` | MediaEpisode | id, season_id, episode_number, title, file_path, file_size, duration, air_date, video/audio_codec | 集 |
| `subtitles` | Subtitle | id, media_item_id, episode_id, language, language_name, path, source | 字幕 |
| `favorites` | Favorite | id, user_id, media_item_id (unique) | 收藏 |
### 下载模块
| 表名 | 模型 | 关键字段 | 说明 |
|------|------|---------|------|
| `download_clients` | DownloadClient | id, name, client_type, host, port, username, password, enabled, category | 下载客户端 |
| `download_tasks` | DownloadTask | id, client_id, subscription_id, media_id, torrent_name, torrent_url, info_hash, save_path, status, progress, total_size, downloaded, speed, seeders, eta, message | 下载任务 |
### 订阅模块
| 表名 | 模型 | 关键字段 | 说明 |
|------|------|---------|------|
| `sites` | Site | id, name, base_url, site_type, auth_type, cookie, api_key, auth_header, user_agent, rss_url, timeout, priority, use_proxy, rate_limit, browser_emulation, enabled, login_status, upload/download_bytes, downloader | 站点配置 |
| `subscriptions` | Subscription | id, name, original_name, tmdb_id, media_type, year, quality_filter, min/max_size, exclude/include_keywords, status, last_search, total_downloaded | 订阅 |
| `subscription_logs` | SubscriptionLog | id, subscription_id, action, resource_title, message | 订阅日志 |
| `notify_channels` | NotifyChannel | id, name, channel_type, config, enabled, events | 通知渠道 |
### 播放模块
| 表名 | 模型 | 关键字段 | 说明 |
|------|------|---------|------|
| `play_history` | PlayHistory | id, user_id, media_item_id, played_at, duration, device_type, ip_address | 播放历史 |
| `playlists` | Playlist | id, user_id, name, description, cover_url, is_public | 播放列表 |
| `playlist_items` | PlaylistItem | id, playlist_id, media_item_id, position, added_at | 播放列表项 |
### 系统模块
| 表名 | 模型 | 关键字段 | 说明 |
|------|------|---------|------|
| `settings` | SettingsKV | id, key, value | KV 设置表 |
| `api_configs` | ApiConfig | id, provider, api_key, base_url, extra, enabled, description | API 配置表 |
---
## 4. 前端功能清单
> 源文件:`frontend/src/`
### 4.1 页面/视图
| 路由 | 视图文件 | 功能 | 权限 |
|------|---------|------|------|
| `/login` | LoginView.vue | 登录页 | 公开 |
| `/` | DashboardView.vue | 仪表盘(继续观看/最近添加/统计数据) | can_view_dashboard |
| `/media` | MediaLibraryView.vue | 媒体库浏览(列表/海报墙切换) | can_play_media |
| `/poster-wall` | PosterWallView.vue | 海报墙视图 | can_play_media |
| `/favorites` | FavoritesView.vue | 收藏列表 | can_favorite |
| `/tv/:id` | TvSeasonView.vue | 剧集季详情 | can_play_media |
| `/media/:id` | MediaDetailView.vue | 媒体详情页 | can_play_media |
| `/player/:id` | PlayerView.vue | 视频播放器 | can_play_media |
| `/downloads` | DownloadView.vue | 下载管理 | can_manage_downloads |
| `/discover` | DiscoverView.vue | 发现/探索页(多源聚合) | can_view_discover |
| `/search` | SearchResultView.vue | 搜索结果页 | 登录 |
| `/subscriptions` | SubscribeView.vue | 订阅管理 | can_manage_subscriptions |
| `/sites` | SitesView.vue | 站点管理 | can_manage_sites |
| `/site-search` | SiteSearchView.vue | 跨站资源搜索 | can_manage_sites |
| `/settings` | SettingsView.vue | 系统设置(多 Tab) | can_access_settings |
| `/history` | WatchHistoryView.vue | 观看历史 | can_view_history |
| `/profile` | ProfileView.vue | 个人资料 | 登录 |
| `/files` | FileManagerView.vue | 文件管理器 | can_manage_files |
| `/playlists` | PlaylistView.vue | 播放列表 | 登录 |
| `/playlists/:id` | PlaylistDetailView.vue | 播放列表详情 | 登录 |
| `/ai-assistant` | AIAssistantView.vue | AI 助手 | can_use_ai_assistant |
| `/profiles-management` | ProfileManagementView.vue | 用户管理 | 管理员 |
| `/storage` | StorageView.vue | 存储管理 | 管理员 |
| `/strm` | StrmView.vue | STRM 文件管理 | can_manage_strm |
| `/dlna` | DlnaView.vue | DLNA 投屏 | can_cast |
### 4.2 组件
| 组件 | 说明 |
|------|------|
| AppEmpty.vue | 空状态占位组件 |
| AppModal.vue | 通用模态框 |
| AppToast.vue | 消息提示 |
| BackendStatus.vue | 后端状态检测 |
| FileTree.vue / FileTreeNode.vue | 文件树组件 |
| settings/GeneralTab.vue | 通用设置 Tab |
| settings/AccountTab.vue | 账户设置 Tab |
| settings/UsersTab.vue | 用户管理 Tab |
| settings/LibrariesTab.vue | 媒体库设置 Tab |
| settings/OrganizeScrapeTab.vue | 整理与刮削设置 Tab |
| settings/DownloadTab.vue | 下载设置 Tab |
| settings/NotifyTab.vue | 通知设置 Tab |
| settings/SchedulerTab.vue | 定时任务设置 Tab |
| settings/SystemTab.vue | 系统设置 Tab |
| settings/ApiConfigTab.vue | API 配置 Tab |
| settings/LicenseTab.vue | 授权管理 Tab |
| settings/AdultTab.vue | Adult Provider 设置 Tab |
| settings/ConfigGroup.vue / ConfigRow.vue | 配置表单通用组件 |
### 4.3 状态管理(Pinia Stores)
| Store | 文件 | 说明 |
|-------|------|------|
| auth | stores/auth.ts | 认证状态 + 用户权限 |
| player | stores/player.ts | 播放器状态 |
### 4.4 API 调用模块
| 模块 | 文件 | 说明 |
|------|------|------|
| auth | api/auth.ts | 认证相关 API |
| media | api/media.ts | 媒体库 API |
| playback | api/playback.ts | 播放 API |
| download | api/download.ts | 下载 API |
| subscribe | api/subscribe.ts | 订阅 API |
| system | api/system.ts | 系统 API |
| settings | api/settings.ts | 设置 API |
| config | api/config.ts | 配置 API |
| admin | api/admin.ts | 管理后台 API |
| license | api/license.ts | 授权 API |
| profiles | api/profiles.ts | 用户配置 API |
| playlist | api/playlist.ts | 播放列表 API |
| strm | api/strm.ts | STRM API |
| dlna | api/dlna.ts | DLNA API |
| client | api/client.ts | HTTP 客户端封装 |
### 4.5 Composables
| 模块 | 说明 |
|------|------|
| useFormat.ts | 格式化工具(文件大小、时长等) |
| useImageError.ts | 图片加载错误处理(默认占位图) |
| useSSE.ts | SSE 实时事件连接 |
| useToast.ts | 消息提示封装 |
### 4.6 前端路由守卫
- 认证检查(requiresAuth)
- 游客页面重定向(guest)
- 管理员权限检查(adminOnly)
- 功能权限检查(requiredPermission)— 与后端 19 项权限对齐
---
## 5. 部署配置清单
### 5.1 Docker
| 文件 | 说明 |
|------|------|
| `docker/Dockerfile` | 多阶段构建(前端构建 + Python 运行时) |
| `docker/docker-compose.yml` | Docker Compose 编排 |
| `docker/docker-compose.template.yml` | 模板版本 |
| `docker/.env.template` | 环境变量模板 |
| `docker/deploy-docker.sh` | Linux 部署脚本 |
| `docker/deploy-docker.ps1` | Windows 部署脚本 |
| `docker/check-image-security.sh` | 镜像安全检查 |
| `docker-compose.example.yml` | 根目录示例 |
### 5.2 部署模板
| 文件/目录 | 说明 |
|-----------|------|
| `docker-compose.simple.yml` | 单镜像 SQLite 部署 |
| `docker-compose.yml` | PostgreSQL 第一档部署 |
| `docker-compose.standard.yml` | PostgreSQL + Redis 第二档部署 |
| `docker-compose.search.yml` | PostgreSQL + Redis + OpenSearch 第三档部署 |
| `README.md` | Docker Compose 部署说明 |
---
## 6. 中间件与基础设施
| 功能 | 源文件 | 说明 |
|------|--------|------|
| CORS 中间件 | main.py | 可配置 origins,支持凭证 |
| 全局异常处理 | main.py | AppError 层级 + 422/500 兜底 |
| SPA 路由回退 | main.py | 非API请求返回 index.html |
| 路径遍历防护 | main.py, image_proxy.py | resolve() 后校验 |
| SQLite WAL 模式 | database.py | 预设 WAL + NORMAL 同步 |
| SQLite busy_timeout | database.py | 5000ms 忙等待 |
| PostgreSQL 连接池 | database.py | pool_size=10, max_overflow=20 |
| JWT 认证 | deps.py, user/auth.py | HS256, access + refresh token |
| 权限检查 | deps.py | require_permission() 工厂函数 |
| 敏感数据加密 | system/crypto.py | Fernet (AES-128-CBC) |
| SSE 事件总线 | system/events.py | 僵尸队列检测 + 心跳 + 自动清理 |
| 文件监控 | media/watcher.py | 文件系统实时监控 |
| 后台任务调度 | system/scheduler.py | APScheduler (AsyncIO) |
### 异常层级
| 异常类 | HTTP 状态码 | 说明 |
|--------|-----------|------|
| AppError | 500 | 基础业务异常 |
| NotFoundError | 404 | 资源不存在 |
| ValidationError | 422 | 参数校验失败 |
| UnauthorizedError | 401 | 未认证 |
| ForbiddenError | 403 | 无权限 |
| ConflictError | 409 | 资源冲突 |
| ExternalServiceError | 502 | 外部服务错误 |
| ScraperError | 404 | 刮削失败 |
| TranscodeError | 500 | 转码失败 |
| DownloadClientError | 502 | 下载客户端错误 |
| SiteError | 502 | 站点错误 |
---
## 7. 配置系统
> 源文件:`backend/app/config.py`
### 环境变量配置
| 分类 | 变量 | 默认值 | 说明 |
|------|------|--------|------|
| **应用** | APP_NAME | MediaStation | 应用名 |
| | APP_PORT | 3001 | 端口 |
| | APP_DEBUG | false | 调试模式 |
| | APP_SECRET_KEY | AUTO_GENERATE | JWT 密钥(自动生成警告) |
| | DATA_DIR | ./data | 数据目录 |
| | SERVER_URL | "" | 服务器地址(外部播放器用) |
| **数据库** | DATABASE_URL | "" | 留空用 SQLite |
| **TMDb** | TMDB_API_KEY | "" | TMDb API Key |
| | TMDB_LANGUAGE | zh-CN | TMDb 语言 |
| | TMDB_BASE_URL | https://api.themoviedb.org/3 | TMDb API 地址 |
| **豆瓣** | DOUBAN_COOKIE | "" | 豆瓣 Cookie |
| **Bangumi** | BANGUMI_TOKEN | "" | Bangumi Token |
| **qBittorrent** | QB_HOST | "" | qBittorrent 地址 |
| | QB_USERNAME | admin | 用户名 |
| | QB_PASSWORD | adminadmin | 密码 |
| **Transmission** | TR_HOST | "" | Transmission 地址 |
| | TR_USERNAME | "" | 用户名 |
| | TR_PASSWORD | "" | 密码 |
| **Telegram** | TELEGRAM_BOT_TOKEN | "" | Bot Token |
| | TELEGRAM_CHAT_ID | "" | Chat ID |
| **微信** | WECHAT_SENDKEY | "" | Server酱 SendKey |
| **Bark** | BARK_SERVER | "" | Bark 服务器 |
| | BARK_KEY | "" | Bark Key |
| **AI** | OPENAI_API_KEY | "" | OpenAI API Key |
| | OPENAI_BASE_URL | https://api.openai.com/v1 | API 地址 |
| | OPENAI_MODEL | gpt-4o-mini | 模型 |
| **FFmpeg** | FFMPEG_PATH | ffmpeg | FFmpeg 路径 |
| | FFPROBE_PATH | ffprobe | FFprobe 路径 |
| | HW_ACCEL | auto | 硬件加速 (auto/qsv/vaapi/nvenc/videotoolbox/none) |
| | MAX_TRANSCODE_JOBS | 2 | 最大并发转码 |
| | TRANSCODE_ENABLED | false | 默认关闭转码 |
| **媒体目录** | MOVIES_DIR | "" | 电影目录 |
| | TV_DIR | "" | 剧集目录 |
| | ANIME_DIR | "" | 动漫目录 |
| **JWT** | JWT_ACCESS_EXPIRE_MINUTES | 60 | Access Token 有效期 |
| | JWT_REFRESH_EXPIRE_DAYS | 30 | Refresh Token 有效期 |
| **安全** | VERIFY_CLIENT_SSL | true | 下载客户端 SSL 校验 |
| **CORS** | CORS_ORIGINS | "" | 逗号分隔的允许源 |
### 数据库存储配置(settings 表)
整理/刮削相关配置通过 `SettingsKV` 表存储,通过 `/api/settings` 端点管理。
### API 配置(api_configs 表)
各数据源 API Key 通过 `ApiConfig` 表存储,支持加密,通过 `/api/api-config` 端点管理。
---
## 8. 技术栈对照表
| 层次 | 原版 (Python) | 目标 (Go) |
|------|--------------|-----------|
| **Web 框架** | FastAPI | Gin |
| **ORM** | SQLAlchemy (async) | GORM |
| **数据库** | SQLite / PostgreSQL | SQLite / PostgreSQL |
| **认证** | python-jose (JWT) + passlib | golang-jwt + bcrypt |
| **任务调度** | APScheduler | robfig/cron 或类似 |
| **SSE** | sse-starlette | 原生实现 |
| **HTTP 客户端** | httpx | net/http |
| **模板引擎** | 无(SPA) | 无(SPA) |
| **前端** | Vue 3 + Pinia + Vue Router | React + Zustand + React Router |
| **UI 框架** | 未明确(推测自定义/Vuetify) | MUI + Tailwind CSS |
| **构建工具** | Vite | Vite |
| **加密** | cryptography (Fernet) | crypto/aes |
| **视频处理** | FFmpeg (subprocess) | FFmpeg (exec) |
| **容器化** | Docker Compose | Docker Compose |
| **反向代理** | Nginx | Nginx |
---
## 附录:API 端点总数统计
| 模块 | 端点数量 |
|------|---------|
| 用户与认证 | 19 |
| 媒体库 | 31 |
| 播放 | 11 |
| 下载 | 12 |
| 订阅与站点 | 20 |
| 系统 | 15 |
| 管理后台 | ~20 |
| 统计 | 8 |
| 播放列表 | 8 |
| STRM | 6 |
| DLNA | 3 |
| 授权管理 | 13 |
| Emby 兼容层 | ~50 |
| 发现/探索 | 3 |
| **总计** | **~220** |
---
> **文档版本**: v1.0 | **分析范围**: `MediaStation-py` 全量源代码
-834
View File
@@ -1,834 +0,0 @@
# MediaStationGo 重构架构设计方案
> **版本**: v1.0 | **日期**: 2026-02-04 | **作者**: Architect (Bob)
>
> 本文档基于旧版 Python 实现(~220 API)与当前 Go 版实现(~50+ API)的差距分析,
> 设计完整的重构架构方案,涵盖数据模型、文件结构、依赖、任务分解和跨模块约定。
---
## 一、差距总览
| 维度 | 原版 (Python) | 当前 Go 版 | 差距 | 优先级 |
|------|---------------|-----------|------|--------|
| **API 端点总数** | ~220 | ~50+ | **~170 个缺失** | - |
| **认证** | access_token + refresh_token | 单一 JWT(24h) | 缺 Token 刷新 + 细粒度权限 | P0 |
| **用户系统** | 19 项细粒度权限 | admin/user 两角色 | 缺完整 RBAC | P0 |
| **Emby 兼容层** | ~50 端点 | 无 | **完全缺失** | P0 |
| **站点管理** | 6 种 PT 站类型 + 聚合搜索 | 仅 RSS 订阅 | 完全缺失站点抽象层 | P0 |
| **通知渠道** | Telegram/微信/Bark/Webhook/Email | 无 | **完全缺失** | P0 |
| **下载客户端** | qBittorrent + Transmission + Aria2 | 仅 qBittorrent | 缺 2 个适配器 | P0 |
| **统计端点** | 8 个 | 1 个 (/api/stats) | 缺趋势/热门/监控等 | P1 |
| **批量操作** | 8 个 | 无 | **完全缺失** | P1 |
| **文件管理器** | 浏览/移动/复制/删除/重命名 | 无 | **完全缺失** | P1 |
| **API 配置管理** | 9 端点 + 加密存储 | 散落在 Config/Setting | 需统一 ApiConfig 表 | P1 |
| **发现页** | 12 个推荐区块 | 2 个 (trending + popular) | 缺 10 个区块 | P1 |
| **DLNA 投屏** | stub (3 端点) | 无 | 可延后 | P2 |
| **授权/Plus** | 13 端点 | 无 | 可延后 | P2 |
---
## 二、新增/修改的数据模型
### 2.1 新增模型定义
```go
// ═══════════════════════════════════════════
// 1. 用户权限模型 (替代 admin/user 二元角色)
// ═══════════════════════════════════════════
// UserPermission stores 19 fine-grained permission flags per user.
// Admin users implicitly have all permissions; Plus tier users too.
type UserPermission struct {
ID string `gorm:"primaryKey;size:36)" json:"id"`
UserID string `gorm:"uniqueIndex;size:36;not null" json:"user_id"`
// Default-on permissions (granted to new users)
CanViewDashboard bool `gorm:"default:true" json:"can_view_dashboard"`
CanPlayMedia bool `gorm:"default:true" json:"can_play_media"`
CanCast bool `gorm:"default:true" json:"can_cast"`
CanExternalPlayer bool `gorm:"default:true" json:"can_external_player"`
CanFavorite bool `gorm:"default:true" json:"can_favorite"`
CanViewHistory bool `gorm:"default:true" json:"can_view_history"`
// Restricted permissions (admin-only by default)
CanEditMedia bool `gorm:"default:false" json:"can_edit_media"`
CanRescrape bool `gorm:"default:false" json:"can_rescrape"`
CanUseAI bool `gorm:"default:false" json:"can_use_ai"`
CanCaptureFrames bool `gorm:"default:false" json:"can_capture_frames"`
CanManageDownloads bool `gorm:"default:false" json:"can_manage_downloads"`
CanViewDiscover bool `gorm:"default:false" json:"can_view_discover"`
CanManageSubscriptions bool `gorm:"default:false" json:"can_manage_subscriptions"`
CanManageSites bool `gorm:"default:false" json:"can_manage_sites"`
CanUseAIAssistant bool `gorm:"default:false" json:"can_use_ai_assistant"`
CanManageUsers bool `gorm:"default:false" json:"can_manage_users"`
CanManageFiles bool `gorm:"default:false" json:"can_manage_files"`
CanManageStrm bool `gorm:"default:false" json:"can_manage_strm"`
CanAccessSettings bool `gorm:"default:false" json:"can_access_settings"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
```
```go
// ═══════════════════════════════════════════
// 2. Refresh Token 模型
// ═══════════════════════════════════════════
// RefreshToken stores long-lived refresh tokens for token rotation.
type RefreshToken struct {
ID string `gorm:"primaryKey;size:36)" json:"id"`
UserID string `gorm:"index;size:36;not null" json:"user_id"`
TokenHash string `gorm:"uniqueIndex;size:128;not null" json:"-"`
ExpiresAt time.Time `gorm:"index" json:"expires_at"`
CreatedAt time.Time `json:"created_at"`
Revoked bool `gorm:"default:false" json:"revoked"`
}
```
```go
// ═══════════════════════════════════════════
// 3. 下载客户端模型 (多客户端支持)
// ═══════════════════════════════════════════
// DownloadClient represents a configured download client (qBittorrent / Transmission / Aria2).
type DownloadClient struct {
Base
Name string `gorm:"size:128;not null" json:"name"`
Type string `gorm:"size:32;not null" json:"type"` // qbittorrent / transmission / aria2
Host string `gorm:"size:512;not null" json:"host"` // http://host:port
Port int `json:"port"` // 0 = included in host
Username string `gorm:"size:255" json:"username,omitempty"`
Password string `gorm:"size:255" json:"-"` // stored encrypted or plain
Enabled bool `gorm:"default:true" json:"enabled"`
Category string `gorm:"size:255" json:"category,omitempty"` // default save category
}
// DownloadTask — MODIFY existing: add ClientID, InfoHash, and more fields.
// New fields to ADD to existing model:
// ClientID string `gorm:"size:36" json:"client_id"` // FK -> DownloadClient.ID
// InfoHash string `gorm:"size:64;index" json:"info_hash,omitempty"`
// TotalSize int64 `json:"total_size,omitempty"`
// Progress float32 `json:"progress"` // 0.0-1.0
// SpeedDown int64 `json:"speed_down,omitempty"` // bytes/sec
// SpeedUp int64 `json:"speed_up,omitempty"` // bytes/sec
// Message string `gorm:"size:512" json:"message,omitempty"`
```
```go
// ═══════════════════════════════════════════
// 4. 站点管理模型 (PT 站 / RSS)
// ═══════════════════════════════════════════
// Site represents a PT site or custom RSS source configuration.
type Site struct {
Base
Name string `gorm:"size:128;not null" json:"name"`
BaseURL string `gorm:"size:1024" json:"base_url"`
SiteType string `gorm:"size:32;not null" json:"site_type"` // nexusphp / gazelle / unit3d / mteam / discuz / custom_rss
AuthType string `gorm:"size:16;not null" json:"auth_type"` // cookie / api_key / auth_header
Cookie string `gorm:"type:text" json:"-"` // encrypted at rest
APIKey string `gorm:"size:512" json:"-"` // encrypted at rest
AuthHeader string `gorm:"size:1024" json:"-"` // encrypted at rest
UserAgent string `gorm:"size:512" json:"user_agent,omitempty"`
RSSURL string `gorm:"size:2048" json:"rss_url,omitempty"`
TimeoutSec int `gorm:"default:30" json:"timeout_sec"`
Priority int `gorm:"default:0" json:"priority"`
UseProxy bool `gorm:"default:false" json:"use_proxy"`
RateLimit int `gorm:"default:0" json:"rate_limit"` // 0 = unlimited
Enabled bool `gorm:"default:true" json:"enabled"`
// Runtime status (not persisted to DB on every request, updated by poller)
LoginStatus *string `gorm:"size:32" json:"login_status,omitempty"` // ok / failed / untested
UploadBytes int64 `json:"upload_bytes,omitempty"`
DownloadBytes int64 `json:"download_bytes,omitempty"`
}
```
```go
// ═══════════════════════════════════════════
// 5. 通知渠道模型
// ═══════════════════════════════════════════
// NotifyChannel represents a notification delivery channel.
type NotifyChannel struct {
Base
Name string `gorm:"size:128;not null" json:"name"`
ChannelType string `gorm:"size:32;not null" json:"channel_type"` // telegram / wechat_bark / webhook / email
Enabled bool `gorm:"default:true" json:"enabled"`
// Type-specific config stored as JSON blob
// Telegram: { "bot_token": "...", "chat_id": "..." }
// WeChat: { "sendkey": "..." }
// Bark: { "server": "...", "key": "..." }
// Webhook: { "url": "...", "method": "POST", "headers": {} }
// Email: { "smtp_host", "smtp_port", "username", "password", "to_address", "from_name" }
Config string `gorm:"type:text" json:"config,omitempty"`
EncryptedConfig string `gorm:"type:text" json:"-"` // encrypted sensitive fields
// Which events trigger this channel (comma-separated or JSON array)
Events string `gorm:"type:text" json:"events"` // subscription_hit, download_complete, scrape_failed, system_alert
}
```
```go
// ═══════════════════════════════════════════
// 6. API 配置管理模型 (统一 Provider Key 管理)
// ═══════════════════════════════════════════
// ApiConfig stores external API credentials with optional encryption.
type ApiConfig struct {
ID string `gorm:"primaryKey;size:36)" json:"id"`
Provider string `gorm:"size:64;uniqueIndex;not null" json:"provider"` // tmdb / douban / bangumi / thetvdb / fanart / openai / deepseek / siliconflow / adult
APIKey string `gorm:"size:512" json:"-"` // encrypted
BaseURL string `gorm:"size:512" json:"base_url,omitempty"`
Extra string `gorm:"type:text" json:"extra,omitempty"` // provider-specific extra config (JSON)
Enabled bool `gorm:"default:true" json:"enabled"`
Description string `gorm:"size:255" json:"description,omitempty"`
LastTestedAt *time.Time `json:"last_tested_at,omitempty"`
TestResult string `gorm:"size:32" json:"test_result,omitempty"` // success / failure / untested
UpdatedAt time.Time `json:"updated_at"`
}
```
```go
// ═══════════════════════════════════════════
// 7. STRM 文件模型 (外部存储支持)
// ═══════════════════════════════════════════
// STRMRecord maps a Media item to an external storage URL.
// When Media.Path is a .strm file, its content is read as the real URL.
type STRMRecord struct {
Base
MediaID string `gorm:"uniqueIndex;size:36;not null" json:"media_id"`
URL string `gorm:"size:2048;not null" json:"url"` // actual remote URL
Protocol string `gorm:"size:32" json:"protocol"` // webdav / alist / s3 / http / https
}
```
```go
// ═══════════════════════════════════════════
// 8. 增强订阅模型 (扩展现有 Subscription)
// ═══════════════════════════════════════════
// Add to existing Subscription:
// TMDbID int `json:"tmdb_id,omitempty"` // link to scraped media
// MediaType string `gorm:"size:32" json:"media_type,omitempty"` // movie / tv / anime
// Year int `json:"year,omitempty"`
// QualityFilter []byte `json:"quality_filter,omitempty"` // ordered priority list (JSON)
// MinSizeMB int `json:"min_size_mb,omitempty"`
// MaxSizeMB int `json:"max_size_mb,omitempty"`
// ExcludeKeys string `gorm:"size:1024" json:"exclude_keywords,omitempty"`
// IncludeKeys string `gorm:"size:1024" json:"include_keywords,omitempty"`
// TotalDownloaded int `json:"total_downloaded,omitempty"`
// Status string `gorm:"size:32;default:active" json:"status"` // active / paused / disabled
```
```go
// ═══════════════════════════════════════════
// 9. 字幕模型 (独立实体,从 Media 中分离)
// ═══════════════════════════════════════════
// SubtitleTrack represents a subtitle file associated with media.
type SubtitleTrack struct {
Base
MediaID string `gorm:"index;size:36;not null" json:"media_id"`
Language string `gorm:"size:16" json:"language"` // zh / en / und
LanguageName string `gorm:"size:128" json:"language_name,omitempty"`
Path string `gorm:"size:1024;not null" json:"path"`
Source string `gorm:"size:32" json:"source"` // external / embedded / uploaded
Codec string `gorm:"size:16" json:"codec"` // srt / ass / vtt / ssa
IsInternal bool `gorm:"default:false" json:"is_internal"` // embedded in container
StreamIdx int `json:"stream_idx,omitempty"` // for embedded subs
}
```
### 2.2 需修改的现有模型
| 模型 | 修改内容 |
|------|----------|
| **User** | 新增字段:`Tier` (free/plus), `Nickname`, `IsActive`, `AvatarURL`(保留), `LastLoginAt`(保留) |
| **Media** | 新增字段:`DoubanID`, `FileHash`(SHA256), `IsDuplicate`, `DuplicateOfID`, `STRMURL`, `Resolution`, `AudioChannels`, `HdrFormat`, `FrameRate`, `ColorSpace`, `BitDepth`, `Genres`(JSON) |
| **Library** | 新增字段:`ScanIntervalMin`, `MetadataLanguage`, `AdultContent`, `PreferNFO`, `EnableWatch`, `MinFileSizeMB` |
| **Playlist** | 新增字段:`Description`, `CoverURL` |
| **PlaybackHistory** | 新增字段:`DeviceType`, `IPAddress`, `PlayedAt`(保留 WatchedAt) |
---
## 三、新增/修改的文件清单(完整路径)
### 3.1 后端新增文件
```
internal/
├── model/
│ ├── permission.go # UserPermission model
│ ├── refresh_token.go # RefreshToken model
│ ├── download_client.go # DownloadClient model
│ ├── site.go # Site model (PT sites)
│ ├── notify_channel.go # NotifyChannel model
│ ├── api_config.go # ApiConfig model
│ ├── strm.go # STRMRecord model
│ └── subtitle_track.go # SubtitleTrack model
│
├── repository/
│ ├── permission_repo.go # UserPermission CRUD
│ ├── refresh_token_repo.go # RefreshToken CRUD
│ ├── download_client_repo.go # DownloadClient CRUD
│ ├── site_repo.go # Site CRUD
│ ├── notify_channel_repo.go # NotifyChannel CRUD
│ ├── api_config_repo.go # ApiConfig CRUD
│ ├── strm_repo.go # STRMRecord CRUD
│ └── subtitle_track_repo.go # SubtitleTrack CRUD
│
├── middleware/
│ ├── permission.go # RequirePermission(permissionKey) middleware
│ └── emby_auth.go # Emby auth (X-Emby-Token / Bearer / username+password)
│
├── service/
│ ├── permission_svc.go # Permission business logic (defaults, checks, grant/revoke)
│ ├── token_svc.go # JWT pair issuance, refresh rotation, revocation
│ ├── download_adapter.go # DownloadClient adapter interface + registry
│ ├── qbittorrent_adp.go # qBittorrent adapter (refactor from qbittorrent.go)
│ ├── transmission_adp.go # Transmission RPC adapter [NEW]
│ ├── aria2_adp.go # Aria2 JSON-RPC adapter [NEW]
│ ├── download_manager_svc.go # Multi-client orchestration (dispatch by client_type)
│ ├── site_svc.go # Site CRUD + test connection + browse resources
│ ├── site_adapter.go # SiteAdapter interface + NexusPHP/Gazelle/UNIT3D/MTeam/Discuz adapters
│ ├── site_search_svc.go # Cross-site aggregated search
│ ├── notify_svc.go # Notification dispatch engine (event → channels)
│ ├── notify_telegram.go # Telegram sender
│ ├── notify_wechat.go # Server酱 (WeChat) sender
│ ├── notify_bark.go # Bark sender
│ ├── notify_webhook.go # Webhook sender
│ ├── notify_email.go # Email (SMTP) sender
│ ├── api_config_svc.go # ApiConfig CRUD + encryption + test connection
│ ├── crypto_svc.go # AES-128-CBC encrypt/decrypt for sensitive data
│ ├── strm_svc.go # STRM file management + protocol whitelist
│ ├── emby_handler_svc.go # Emby API handler (~50 endpoints)
│ ├── douban_scraper.go # Douban metadata provider [NEW]
│ ├── discover_feed_svc.go # Multi-source discovery feed (12 sections)
│ ├── batch_svc.go # Batch operations (scan/scrape/delete/move/favorite/watched/rename/ai-rename)
│ ├── filemanager_svc.go # File browser + move/copy/delete/mkdir/rename
│ ├── duplicate_svc.go # File hash-based duplicate detection
│ ├── stats_enhanced_svc.go # Trend/top-content/top-users/monitor/user-stats
│ ├── sse_hub.go # SSE (Server-Sent Events) event stream hub
│ ├── external_player_svc.go # External player protocol URLs (8 players)
│ ├── thumbnail_svc.go # FFmpeg video frame capture (thumbnail)
│ ├── scheduler_svc.go # Cron-based task scheduler (robfig/cron)
│ └── backup_svc.go # System backup/restore
│
├── handler/
│ ├── permission_handler.go # GET/PUT user permissions, POST reset
│ ├── refresh_handler.go # POST /api/auth/refresh
│ ├── download_client_handler.go # CRUD + test for download clients
│ ├── site_handler.go # Site CRUD + test + browse + userdata
│ ├── site_search_handler.go # GET /api/search/sites (cross-site search)
│ ├── notify_handler.go # NotifyChannel CRUD + test
│ ├── api_config_handler.go # ApiConfig CRUD + test + providers list
│ ├── strm_handler.go # STRM config + media-level STRM URL management
│ ├── emby_router.go # All Emby-compatible routes (~50 routes)
│ ├── douban_search_handler.go # GET /api/search/douban
│ ├── discover_feed_handler.go # GET /api/discover/feed, GET /api/discover/sections
│ ├── batch_handler.go # 8 batch operation endpoints
│ ├── filemanager_handler.go # File browser + operations
│ ├── duplicate_handler.go # Hash compute + scan + list + unmark
│ ├── stats_enhanced_handler.go # 7 new stats endpoints
│ ├── sse_handler.go # GET /api/system/events (SSE stream)
│ ├── external_player_handler.go# External URLs + protocols
│ ├── thumbnail_handler.go # GET /api/media/:id/thumbnail
│ ├── subtitle_enhanced_handler.go # Upload/scan/extract/manage subtitles
│ ├── scheduler_handler.go # Scheduler management
│ └── backup_handler.go # Backup/restore
│
└── emby_types.go # Emby-specific DTOs (User, Item, Session, etc.)
```
### 3.2 后端需修改的文件
| 文件 | 修改内容 |
|------|----------|
| `model/model.go` | 添加新模型引用到 AllModels();修改 User/Media/Library/Playlist/PlaybackHistory 结构体 |
| `repository/repository.go` | Container 添加新 Repository 字段 |
| `middleware/middleware.go` | 添加 RequirePermission 构造函数 |
| `service/service.go` | Container 添加新 Service 字段;Boot() 启动新后台服务 |
| `service/auth.go` | 支持 Tier 字段;签发 access_token + refresh_token 对;集成权限检查 |
| `handler/handler.go` | Register() 注册所有新路由分组 |
| `config/config.go` | 新增通知配置段、DLNA 配置段、调度器配置段 |
| `cmd/server/main.go` | 初始化新服务 |
### 3.3 前端新增文件
```
web/src/
├── pages/
│ ├── SitesPage.tsx # 站点管理页面
│ ├── SiteSearchPage.tsx # 跨站资源搜索页面
│ ├── FileManagerPage.tsx # 文件管理器页面
│ ├── SettingsPage.tsx # 系统设置页面(多 Tab)
│ │ # 内含子组件:
│ │ ├── components/settings/
│ │ │ ├── GeneralTab.tsx # 通用设置 Tab
│ │ │ ├── AccountTab.tsx # 账户设置 Tab
│ │ │ ├── UsersTab.tsx # 用户管理 Tab
│ │ │ ├── LibrariesTab.tsx # 媒体库设置 Tab
│ │ │ ├── ScrapeOrganizeTab.tsx# 整理与刮削设置 Tab
│ │ │ ├── DownloadTab.tsx # 下载客户端设置 Tab
│ │ │ ├── NotifyTab.tsx # 通知渠道设置 Tab
│ │ │ ├── SchedulerTab.tsx # 定时任务设置 Tab
│ │ │ ├── SystemTab.tsx # 系统设置 Tab
│ │ │ ├── ApiConfigTab.tsx # API 配置 Tab
│ │ │ ├── LicenseTab.tsx # 授权管理 Tab
│ │ │ └── AdultTab.tsx # Adult Provider 设置 Tab
│ │ ├── ConfigGroup.tsx # 配置表单通用组件
│ │ └── ConfigRow.tsx # 配置行组件
│ ├── StatsEnhancedPage.tsx # 增强统计仪表盘
│ ├── StrmPage.tsx # STRM 文件管理页面
│ ├── HistoryPage.tsx # 观看历史页面
│ ├── DlnaPage.tsx # DLNA 投屏页面
│ ├── AiAssistantPage.tsx # AI 助手对话页面
│ ├── PosterWallPage.tsx # 海报墙视图
│ └── LicensePage.tsx # 授权管理页面
│
├── api/
│ ├── permission.ts # 权限 API (get/update/reset)
│ ├── refresh.ts # Token 刷新 API
│ ├── downloadClient.ts # 下载客户端 API
│ ├── site.ts # 站点 API
│ ├── siteSearch.ts # 跨站搜索 API
│ ├── notify.ts # 通知渠道 API
│ ├── apiConfig.ts # API 配置管理 API
│ ├── strm.ts # STRM API
│ ├── douban.ts # 豆瓣搜索 API
│ ├── discoverFeed.ts # 发现聚合 API (多源 feed)
│ ├── batch.ts # 批量操作 API
│ ├── filemanager.ts # 文件管理器 API
│ ├── duplicate.ts # 重复检测 API
│ ├── statsEnhanced.ts # 增强统计 API
│ ├── sse.ts # SSE 连接工具
│ ├── externalPlayer.ts # 外部播放器 API
│ ├── thumbnail.ts # 截图 API
│ ├── subtitleEnhanced.ts # 增强字幕 API (上传/提取/删除)
│ ├── scheduler.ts # 定时任务 API
│ ├── backup.ts # 备份/恢复 API
│ ├── license.ts # 授权 API
│ └── dlna.ts # DLNA API
│
├── stores/
│ ├── permissions.ts # 权限状态 store (Zustand)
│ ├── settings.ts # 系统设置 store (Zustand)
│ ├── notifications.ts # 通知消息 store (Zustand)
│ └── sse.ts # SSE 连接 store (Zustand)
│
├── hooks/
│ ├── usePermission.ts # usePermission(key) hook
│ ├── useSSE.ts # SSE 事件流 hook
│ └── useExternalPlayer.ts # 外部播放器协议生成 hook
│
├── components/
│ ├── settings/ # 设置页子组件目录
│ ├── FileTree.tsx # 文件树组件
│ ├── FileTreeNode.tsx # 文件树节点组件
│ ├── PermissionGuard.tsx # 权限守卫组件 (<PermissionGuard permission="can_play_media">)
│ ├── DiscoverSection.tsx # 发现页区块组件
│ ├── SiteCard.tsx # 站点卡片组件
│ ├── DownloadClientCard.tsx # 下载客户端卡片组件
│ ├── NotifyChannelCard.tsx # 通知渠道卡片组件
│ ├── ApiConfigCard.tsx # API 配置卡片组件
│ ├── BatchOperationBar.tsx # 批量操作栏组件
│ ├── PlayerProtocolList.tsx # 播放器协议列表组件
│ ├── StatsChart.tsx # 统计图表组件
│ └── AppEmpty.tsx # 空状态占位组件
│
└── types/
└── index.ts # 扩展: 添加新类型定义
```
### 3.4 前端需修改的文件
| 文件 | 修改内容 |
|------|----------|
| `App.tsx` | 新增 ~15 个路由 (Sites/FileManager/Settings/StatsEnhanced/Strm/History/DLNA/AiAssistant/PosterWall/License);添加 `<PermissionGuard>` 路由级守卫 |
| `stores/auth.ts` | 新增 permissions 对象、tokenRefresh() 方法、tier 字段 |
| `components/RequireAuth.tsx` | 集成权限检查逻辑 |
| `components/Layout.tsx` | 侧边栏新增菜单项(站点管理/文件管理/STRM/DLNA/统计/设置) |
| `types/index.ts` | 新增所有新模型的 TypeScript 类型 |
---
## 四、依赖包列表
### 4.1 Go 新增依赖
```
# 已有依赖保持不变,新增:
github.com/robfig/cron/v3 v3.0.1 # 定时任务调度器 (APScheduler 替代)
golang.org/x/crypto/v0 latest # crypto/aes (AES-128-CBC 加密)
github.com/go-resty/resty/v3 v3.10.0 # HTTP client (Transmission/Aria2/Site 调用)
github.com/gabriel-vasile/mimetype v1.4.2 # MIME 类型检测 (字幕格式识别)
github.com/disintegration/imaging v4.0.0 # 图片处理 (缩略图截取/缩放)
```
### 4.2 npm 新增依赖
```
# 已有依赖保持不变,新增:
@mui/icons-material ^5.14.0 # Material Design 图标库(设置页需要大量图标)
recharts ^2.5.0 # React 图表库(统计图表)
react-virtuoso ^4.6.0 # 虚拟滚动(大列表性能)
dayjs ^1.11.10 # 轻量日期库(替代 moment.js)
framer-motion ^11.0.0 # 动画库(发现页过渡动画)
```
---
## 五、任务分解(按实现顺序)
### T01: 项目基础设施增强(认证+权限+加密+SSE 核心)
**优先级**: P0 | **依赖**: 无 | **预估代码量**: ~2000 行
| 类别 | 文件 |
|------|------|
| **新模型** | `model/permission.go`, `model/refresh_token.go`, `model/api_config.go` |
| **Repository** | `permission_repo.go`, `refresh_token_repo.go`, `api_config_repo.go` |
| **Service** | `service/permission_svc.go`, `service/token_svc.go`, `service/crypto_svc.go`, `service/sse_hub.go` |
| **Middleware** | `middleware/permission.go` |
| **Handler** | `handler/permission_handler.go`, `handler/refresh_handler.go`, `handler/api_config_handler.go` |
| **Handler (mod)** | `handler/handler.go` (注册新路由) |
| **Service (mod)** | `service/auth.go` (支持 token 对 + tier), `service/service.go` (Container 扩展) |
| **Model (mod)** | `model/model.go` (AllModels 扩展, User 字段扩展) |
| **前端** | `stores/auth.ts` (permissions + refresh), `stores/sse.ts`, `stores/permissions.ts`, `hooks/usePermission.ts`, `hooks/useSSE.ts`, `components/PermissionGuard.tsx`, `types/index.ts` (扩展), `api/permission.ts`, `api/refresh.ts`, `api/apiConfig.ts` |
**核心交付物**:
- 19 项细粒度权限系统的完整链路(模型→仓库→中间件→服务→Handler→前端守卫)
- Access Token (60min) + Refresh Token (30天) 双令牌机制
- AES-128-CBC 加密服务(敏感数据加解密,兼容明文迁移)
- SSE 事件流 Hub(替代 WebSocket 的备选实时通道)
- ApiConfig 表 + 9 个管理端点(统一 API Key 管理 + 加密存储 + 测试连接)
---
### T02: 多下载客户端 + 通知渠道 + 定时任务
**优先级**: P0 | **依赖**: T01(加密服务用于密码加密) | **预估代码量**: ~3000 行
| 类别 | 文件 |
|------|------|
| **新模型** | `model/download_client.go` |
| **Repository** | `download_client_repo.go`, `notify_channel_repo.go` |
| **Service** | `service/download_adapter.go` (接口), `service/qbittorrent_adp.go` (重构), `service/transmission_adp.go`, `service/aria2_adp.go`, `service/download_manager_svc.go`, `service/notify_svc.go`, `service/notify_telegram.go`, `service/notify_wechat.go`, `service/notify_bark.go`, `service/notify_webhook.go`, `service/notify_email.go`, `service/scheduler_svc.go` |
| **Handler** | `handler/download_client_handler.go`, `handler/notify_handler.go`, `handler/scheduler_handler.go` |
| **Handler (mod)** | `handler/handler.go`, `handler/downloads.go` (改为通过 DownloadManager) |
| **Service (mod)** | `service/downloads.go` (重构为多客户端分发), `service/service.go` (Boot 启动 scheduler + notifier) |
| **前端** | `api/downloadClient.ts`, `api/notify.ts`, `api/scheduler.ts`, `components/DownloadClientCard.tsx`, `components/NotifyChannelCard.tsx` |
**核心交付物**:
- DownloadClient 适配器模式(Interface + qBittorrent/Transmission/Aria2 三实现)
- 5 种通知渠道(Telegram/Server酱/Bark/Webhook/Email)完整发送链路
- 事件驱动通知引擎(subscription_hit / download_complete / scrape_failed / system_alert)
- 基于 robfig/cron 的定时任务调度器(替代 APScheduler),内置 6 个预配置任务
- 下载客户端热插拔(运行时添加/移除/测试连接)
---
### T03: 站点管理 + Emby API 兼容层
**优先级**: P0 | **依赖**: T01(加密服务用于 Cookie/APIKey 存储) | **预估代码量**: ~4000 行
| 类别 | 文件 |
|------|------|
| **新模型** | `model/site.go` |
| **Repository** | `site_repo.go` |
| **Service** | `service/site_svc.go`, `service/site_adapter.go` (接口+6种适配器), `service/site_search_svc.go`, `service/emby_handler_svc.go`, `service/strm_svc.go` |
| **Model** | `model/strm.go`, `emby_types.go` |
| **Handler** | `handler/site_handler.go`, `handler/site_search_handler.go`, `handler/emby_router.go` (~50 路由), `handler/strm_handler.go` |
| **Middleware** | `middleware/emby_auth.go` |
| **前端** | `api/site.ts`, `api/siteSearch.ts`, `api/strm.ts`, `pages/SitesPage.tsx`, `pages/SiteSearchPage.tsx`, `pages/StrmPage.tsx`, `components/SiteCard.tsx` |
**核心交付物**:
- 6 种 PT 站点类型适配器(NexusPHP / Gazelle / UNIT3D / MTeam / Discuz / Custom RSS)
- 3 种认证方式(Cookie / API Key / Authorization Header),全部加密存储
- 跨站聚合搜索(并发搜索多个站点,合并去重排序)
- 站点资源浏览器(分页浏览种子列表)
- **Emby API 兼容层**(~50 端点):认证 → 系统信息 → 媒体库 → Items → PlaybackInfo → 流代理 → 字幕 → 进度上报
- STRM 文件管理(外部存储以"文件"形式入库,WebDAV/Alist/S3/HTTP 协议白名单校验)
- Emby 认证中间件(X-Emby-Token / Bearer / Username+Password 三种方式)
---
### T04: 增强功能集(发现页+统计+批量操作+文件管理+播放增强+刮削增强)
**优先级**: P1 | **依赖**: T01, T02, T03 | **预估代码量**: ~3500 行
| 类别 | 文件 |
|------|------|
| **Service** | `service/discover_feed_svc.go` (12 区块), `service/stats_enhanced_svc.go`, `service/batch_svc.go`, `service/filemanager_svc.go`, `service/duplicate_svc.go`, `service/douban_scraper.go`, `service/external_player_svc.go`, `service/thumbnail_svc.go`, `service/backup_svc.go` |
| **Model** | `model/subtitle_track.go` |
| **Repository** | `subtitle_track_repo.go`, `strm_repo.go` |
| **Handler** | `handler/discover_feed_handler.go`, `handler/stats_enhanced_handler.go`, `handler/batch_handler.go`, `handler/filemanager_handler.go`, `handler/duplicate_handler.go`, `handler/douban_search_handler.go`, `handler/external_player_handler.go`, `handler/thumbnail_handler.go`, `handler/subtitle_enhanced_handler.go`, `handler/backup_handler.go`, `handler/recycle.go` (扩展) |
| **前端 (pages)** | `pages/SettingsPage.tsx` (+12个子Tab组件), `pages/StatsEnhancedPage.tsx`, `pages/FileManagerPage.tsx`, `pages/HistoryPage.tsx`, `pages/PosterWallPage.tsx`, `pages/AiAssistantPage.tsx`, `pages/DlnaPage.tsx`, `pages/LicensePage.tsx` |
| **前端 (api)** | `api/douban.ts`, `api/discoverFeed.ts`, `api/batch.ts`, `api/filemanager.ts`, `api/duplicate.ts`, `api/statsEnhanced.ts`, `api/externalPlayer.ts`, `api/thumbnail.ts`, `api/subtitleEnhanced.ts`, `api/backup.ts`, `api/license.ts`, `api/dlna.ts`, `api/sse.ts` |
| **前端 (components)** | `components/settings/*` (12个Tab), `components/FileTree.tsx`, `components/FileTreeNode.tsx`, `components/DiscoverSection.tsx`, `components/BatchOperationBar.tsx`, `components/PlayerProtocolList.tsx`, `components/StatsChart.tsx`, `components/AppEmpty.tsx` |
| **前端 (stores)** | `stores/settings.ts`, `stores/notifications.ts` |
| **前端 (hooks)** | `hooks/useExternalPlayer.ts` |
| **前端 (mod)** | `App.tsx` (15个新路由 + 权限守卫), `Layout.tsx` (侧边栏扩展), `types/index.ts` (类型扩展) |
**核心交付物**:
- **增强发现页**(12 个推荐区块:本地最近电影/剧集 + TMDb 4 区块 + 豆瓣 4 区块 + Bangumi 每日)
- **豆瓣刮削器**(DoubanProvider,补充中文元数据,Cookie 认证)
- **增强统计**(8 端点:概览/播放趋势/热门内容/活跃用户/媒体库统计/系统监控/用户统计/记录播放)
- **批量操作**(8 个端点:扫描/刮削/删除/移动/收藏/标记已看/重命名/AI重命名)
- **文件管理器**(目录浏览 + 移动/复制/删除/创建文件夹/重命名/重命名预览)
- **外部播放器直链**(8 种协议:PotPlayer/VLC/IINA/Infuse/NPlayer/MX/MPV/MPC-HC)
- **视频截帧**(FFmpeg thumbnail 提取,用于卡片封面)
- **增强字幕管理**(上传/扫描外挂/检测内嵌/提取内嵌/删除)
- **文件哈希重复检测**(SHA256 哈希计算 + 重复标记/取消)
- **系统备份/恢复**(SQLite 数据库备份)
- DLNA stub(设备发现/投屏框架,可后续完善)
- 授权/Plus 系统(13 端点框架,可后续接入验证服务器)
---
### T05: 集成优化 + 测试 + 文档
**优先级**: P1 | **依赖**: T01-T04 全部完成 | **预估代码量**: ~1000 行
| 类别 | 文件 |
|------|------|
| **配置** | `config/config.go` (新增段: Notify, DLNA, Scheduler, License) |
| **入口** | `cmd/server/main.go` (初始化所有新服务) |
| **集成** | `service/service.go` (最终 Container + Boot + Close) |
| **路由整合** | `handler/handler.go` (最终全部路由注册) |
| **文档** | `docs/refactor-migration-guide.md` (迁移指南) |
| **测试** | 各 service 对应的 `_test.go` 补充 |
**核心交付物**:
- 所有新服务的启动/关闭生命周期整合
- 最终路由注册(目标 ~220 个端点)
- 配置文件 Schema 更新(新增 Notify/DLNA/Scheduler/License 段)
- 迁移指南文档(从旧版升级的数据变更说明)
---
## 六、共享知识(跨文件约定)
### 6.1 错误码规范
```go
// 应用错误码常量 (package apperr)
const (
ErrOK = 0
ErrInvalidParams = 40001
ErrUnauthorized = 40101
ErrForbidden = 40301
ErrNotFound = 40401
ErrConflict = 40901
ErrRateLimit = 42901
ErrInternal = 50001
ErrExternalService = 50201 // 第三方服务不可达
ErrScraperFailed = 50401 // 刮削超时/失败
ErrTranscodeFailed = 50501 // FFmpeg 转码失败
ErrDownloadClient = 50601 // 下载客户端错误
ErrSiteAuthFailed = 50701 // 站点认证失败
ErrEncryptFailed = 50801 // 加密操作失败
)
// 统一错误响应格式
type APIResponse struct {
Code int `json:"code"` // 业务错误码
Data any `json:"data,omitempty"` // 成功时的 payload
Message string `json:"message,omitempty"` // 人可读错误信息
}
```
### 6.2 API 响应格式约定
```
成功响应 (2xx):
{
"code": 0,
"data": { ... },
"message": "ok"
}
分页列表响应:
{
"code": 0,
"data": {
"items": [...],
"total": 150,
"page": 1,
"page_size": 50
}
}
错误响应 (non-2xx):
{
"code": 40101,
"data": null,
"message": "token expired"
}
```
### 6.3 权限检查约定
```go
// 后端中间件用法
r.POST("/media/:id/scrape",
middleware.AuthRequired(secret),
middleware.RequirePermission("can_rescrape"), // NEW
middleware.AdminRequired(), // still works (implies all permissions)
scrapeOneHandler(svc),
)
// 权限解析优先级:
// 1. role == "admin" → 自动拥有所有权限(跳过 DB 查询)
// 2. tier == "plus" → 自动拥有所有权限(跳过 DB 查询)
// 3. 普通 user → 查询 user_permissions 表,逐字段判断
```
```tsx
// 前端路由守卫用法
<Route path="discover" element={
<PermissionGuard permission="can_view_discover">
<DiscoverPage />
</PermissionGuard>
} />
```
### 6.4 敏感数据加密约定
```go
// 加密标识前缀
const EncryptPrefix = "enc:v1:"
// 加密流程:
// 1. 检查值是否已以 enc:v1: 开头 → 是则跳过(避免双重加密)
// 2. 使用 AES-128-CBC 加密,密钥从 APP_SECRET_KEY 派生 (PBKDF2 + SHA256)
// 3. 存储为 enc:v1:<base64_ciphertext>
// 解密流程:
// 1. 检查是否以 enc:v1: 开头 → 否则返回原文(明文兼容)
// 2. AES-128-CBC 解密
// 3. 返回明文
// 适用字段: Site.Cookie/Site.APIKey/Site.AuthHeader /
// DownloadClient.Password / NotifyChannel.EncryptedConfig /
// ApiConfig.APIKey / ApiConfig.Extra
```
### 6.5 Emby API 兼容层约定
```
路由前缀: /emby/
认证方式 (按优先级):
1. X-Emby-Token: <token> (Emby 标准头)
2. Authorization: Bearer <token> (标准 JWT)
3. query: ?token=<token> (URL 参数,用于<video> src)
4. POST body: {Username, Password} (Emby AuthenticateByName)
Emby Token 与内部 JWT 的映射关系:
- Emby 认证成功后,返回 EmbyUserId + AccessToken (即内部 JWT)
- 后续请求通过映射表查找对应用户
```
### 6.6 事件系统约定
```go
// 支持两种推送通道:
// 1. WebSocket (已有) — 用于需要双向通信的场景 (scan progress, transcode status)
// Topics: scan | scrape | transcode | download
// 2. SSE (新增) — 用于服务端单向推送场景 (notification, task progress)
// Events: notification | task_progress | alert
// 认证: 一次性 OTP ticket (GET /api/system/events/ticket → 10s 有效)
// 或 Authorization Bearer (降级方案)
// 通知事件类型:
const (
EventSubscriptionHit = "subscription_hit" // 订阅命中新资源
EventDownloadComplete = "download_complete" // 下载完成
EventScrapeFailed = "scrape_failed" // 刮削失败
EventSystemAlert = "system_alert" // 系统告警 (磁盘满/CPU高等)
)
```
### 6.7 下载客户端适配器接口
```go
type DownloadAdapter interface {
// 生命周期
Initialize(ctx context.Context, cfg DownloadClientConfig) error
Ping(ctx context.Context) error
// 任务操作
AddTorrent(ctx context.Context, url, savePath string) (string, error) // returns hash/id
Pause(ctx context.Context, hash string) error
Resume(ctx context.Context, hash string) error
Remove(ctx context.Context, hash string, deleteFiles bool) error
// 状态查询
List(ctx context.Context, filter string) ([]TorrentInfo, error)
GetGlobalStats(ctx context.Context) (*GlobalStats, error) // Aria2 only
}
```
---
## 七、任务依赖关系图
```mermaid
graph TD
T01[T01: 基础设施<br/>认证+权限+加密+SSE] --> T03
T01 --> T02
T02[T02: 多下载客户端<br/>+通知+定时任务] --> T04
T03[T03: 站点管理<br/>+Emby兼容层] --> T04
T04[T04: 增强功能集<br/>发现+统计+批量+文件管理+播放增强] --> T05
T05[T05: 集成优化+测试+文档]
style T01 fill:#e1f5fe
style T02 fill:#fff3e0
style T03 fill:#fce4ec
style T04 fill:#f3e5f5
style T05 fill:#e8f5e9
```
---
## 八、预估代码规模汇总
| 任务 | 新增后端文件 | 新增前端文件 | 修改文件 | 预估行数 |
|------|------------|------------|---------|---------|
| T01 | ~18 | ~14 | ~8 | ~2000 |
| T02 | ~16 | ~8 | ~4 | ~3000 |
| T03 | ~14 | ~8 | ~4 | ~4000 |
| T04 | ~20 | ~35 | ~5 | ~3500 |
| T05 | ~2 | ~2 | ~5 | ~1000 |
| **合计** | **~70** | **~67** | **~26** | **~13500** |
对比当前代码库(Go ~5000 行,前端 ~3000 行),本次重构将使代码总量增长约 **2.5 倍**。
---
## 九、实施建议
### 分期策略
1. **Phase 1(必须做)**: T01 + T02 → 核心基础设施和多客户端/通知,解决最大痛点
2. **Phase 2(应该做)**: T03 → Emby 兼容层是差异化竞争力(让 Infuse/Kodi 直连)
3. **Phase 3(最好做)**: T04 → 功能完整性(对标原版 ~220 API)
4. **Phase 4(收尾)**: T05 → 打磨质量
### 可并行化的独立模块
- Emby 兼容层与站点管理可以独立开发(仅共享基础模型/T01)
- 通知渠道之间完全独立(每种渠道一个文件)
- 下载客户端适配器之间完全独立
- 前端各页面可以并行开发
### 风险控制
- **Emby 兼容层复杂度最高**:建议先实现核心 15 个端点(认证+媒体列表+播放+进度),再逐步补全
- **PT 站适配器**:每个站点类型的 HTML 解析差异大,建议先实现 NexusPHP(覆盖最广),其他用通用 RSS 替代
- **加密迁移**:必须保证旧明文数据的读取兼容(detect prefix 策略)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

-12
View File
@@ -1,12 +0,0 @@
graph TD
T01[T01: 基础设施增强 - 认证+权限+加密+SSE核心] -->|依赖: 加密服务| T03
T01 -->|依赖: 权限系统| T02
T02[T02: 多下载客户端 + 通知渠道 + 定时任务] -->|依赖: 事件引擎/多客户端| T04
T03[T03: 站点管理 + Emby API兼容层] -->|依赖: 站点适配器/Emby认证| T04
T04[T04: 增强功能集 - 发现页+统计+批量操作+文件管理+播放增强+刮削增强] --> T05[T05: 集成优化 + 测试 + 文档]
style T01 fill:#e1f5fe,stroke:#0277bd,color:#01579b
style T02 fill:#fff3e0,stroke:#e65100,color:#bf360c
style T03 fill:#fce4ec,stroke:#c2185b,color:#880e4f
style T04 fill:#f3e5f5,stroke:#6a1b9a,color:#4a148c
style T05 fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20
Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

-20
View File
@@ -1,20 +0,0 @@
<svg width="920" height="160" viewBox="0 0 460 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Logo Mark -->
<rect x="10" y="15" width="50" height="50" rx="14" fill="url(#horizonPurpleGrad)" />
<path d="M26 30 L43 40 L26 50" stroke="white" stroke-width="4" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
<path d="M26 43 L26 54 L34 48" stroke="white" stroke-width="4" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
<circle cx="46" cy="28" r="3.5" fill="#06B6D4" />
<!-- Typography -->
<text x="75" y="47" font-family="'Plus Jakarta Sans', system-ui, sans-serif" font-weight="800" font-size="28" fill="#111827" letter-spacing="-0.5">Mgo-Emby</text>
<text x="238" y="47" font-family="'Plus Jakarta Sans', system-ui, sans-serif" font-weight="300" font-size="28" fill="#9CA3AF" letter-spacing="-0.5"> 社区</text>
<rect x="315" y="26" width="48" height="22" rx="6" fill="#06B6D4" fill-opacity="0.1" stroke="#06B6D4" stroke-width="1" />
<text x="339" y="41" font-family="'JetBrains Mono', monospace" font-weight="700" font-size="11" fill="#06B6D4" text-anchor="middle">BBS</text>
<defs>
<linearGradient id="horizonPurpleGrad" x1="10" y1="15" x2="60" y2="65" gradientUnits="userSpaceOnUse">
<stop stop-color="#7C3AED" />
<stop offset="1" stop-color="#5B21B6" />
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

-6
View File
@@ -1,6 +0,0 @@
<svg width="200" height="200" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="5" y="5" width="90" height="90" rx="24" stroke="currentColor" stroke-width="6" />
<path d="M35 30 L65 47 L35 64" stroke="currentColor" stroke-width="8" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
<path d="M35 51 L35 72 L48 64" stroke="currentColor" stroke-width="8" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
<circle cx="69" cy="29" r="6" fill="currentColor" />
</svg>

Before

Width:  |  Height:  |  Size: 525 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

-16
View File
@@ -1,16 +0,0 @@
<svg width="400" height="480" viewBox="0 0 200 240" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="40" y="20" width="120" height="120" rx="32" fill="url(#primaryPurpleGrad)" />
<path d="M80 55 L120 80 L80 105" stroke="white" stroke-width="9" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
<path d="M80 87 L80 114 L100 101" stroke="white" stroke-width="9" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
<circle cx="128" cy="52" r="8" fill="#06B6D4" />
<text x="100" y="185" font-family="'Plus Jakarta Sans', system-ui, sans-serif" font-weight="800" font-size="24" fill="#111827" text-anchor="middle" letter-spacing="1">Mgo-Emby</text>
<text x="100" y="215" font-family="'Inter', system-ui, sans-serif" font-weight="500" font-size="14" fill="#06B6D4" text-anchor="middle" letter-spacing="4">社区</text>
<defs>
<linearGradient id="primaryPurpleGrad" x1="40" y1="20" x2="160" y2="140" gradientUnits="userSpaceOnUse">
<stop stop-color="#7C3AED" />
<stop offset="1" stop-color="#5B21B6" />
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

-20
View File
@@ -1,20 +0,0 @@
<svg width="460" height="80" viewBox="0 0 460 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Logo Mark -->
<rect x="10" y="15" width="50" height="50" rx="14" fill="url(#horizonPurpleGrad)" />
<path d="M26 30 L43 40 L26 50" stroke="white" stroke-width="4" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
<path d="M26 43 L26 54 L34 48" stroke="white" stroke-width="4" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
<circle cx="46" cy="28" r="3.5" fill="#06B6D4" />
<!-- Typography -->
<text x="75" y="47" font-family="'Plus Jakarta Sans', system-ui, sans-serif" font-weight="800" font-size="28" fill="#111827" letter-spacing="-0.5">Mgo-Emby</text>
<text x="238" y="47" font-family="'Plus Jakarta Sans', system-ui, sans-serif" font-weight="300" font-size="28" fill="#9CA3AF" letter-spacing="-0.5"> 社区</text>
<rect x="315" y="26" width="48" height="22" rx="6" fill="#06B6D4" fill-opacity="0.1" stroke="#06B6D4" stroke-width="1" />
<text x="339" y="41" font-family="'JetBrains Mono', monospace" font-weight="700" font-size="11" fill="#06B6D4" text-anchor="middle">BBS</text>
<defs>
<linearGradient id="horizonPurpleGrad" x1="10" y1="15" x2="60" y2="65" gradientUnits="userSpaceOnUse">
<stop stop-color="#7C3AED" />
<stop offset="1" stop-color="#5B21B6" />
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 981 B

-12
View File
@@ -1,12 +0,0 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="32" height="32" rx="8" fill="url(#mgoPurpleGrad)" />
<path d="M11 10 L21 16 L11 22" stroke="white" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
<path d="M11 18 L11 24 L16 21" stroke="white" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
<circle cx="23" cy="9" r="2" fill="#06B6D4" />
<defs>
<linearGradient id="mgoPurpleGrad" x1="0" y1="0" x2="32" y2="32" gradientUnits="userSpaceOnUse">
<stop stop-color="#7C3AED" />
<stop offset="1" stop-color="#5B21B6" />
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 705 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

@@ -1,6 +0,0 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="5" y="5" width="90" height="90" rx="24" stroke="currentColor" stroke-width="6" />
<path d="M35 30 L65 47 L35 64" stroke="currentColor" stroke-width="8" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
<path d="M35 51 L35 72 L48 64" stroke="currentColor" stroke-width="8" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
<circle cx="69" cy="29" r="6" fill="currentColor" />
</svg>

Before

Width:  |  Height:  |  Size: 525 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

-16
View File
@@ -1,16 +0,0 @@
<svg width="200" height="240" viewBox="0 0 200 240" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="40" y="20" width="120" height="120" rx="32" fill="url(#primaryPurpleGrad)" />
<path d="M80 55 L120 80 L80 105" stroke="white" stroke-width="9" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
<path d="M80 87 L80 114 L100 101" stroke="white" stroke-width="9" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
<circle cx="128" cy="52" r="8" fill="#06B6D4" />
<text x="100" y="185" font-family="'Plus Jakarta Sans', system-ui, sans-serif" font-weight="800" font-size="24" fill="#111827" text-anchor="middle" letter-spacing="1">Mgo-Emby</text>
<text x="100" y="215" font-family="'Inter', system-ui, sans-serif" font-weight="500" font-size="14" fill="#06B6D4" text-anchor="middle" letter-spacing="4">社区</text>
<defs>
<linearGradient id="primaryPurpleGrad" x1="40" y1="20" x2="160" y2="140" gradientUnits="userSpaceOnUse">
<stop stop-color="#7C3AED" />
<stop offset="1" stop-color="#5B21B6" />
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB