chore remove unused docs and assets
@@ -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:
|
||||
|
||||
@@ -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实时推送、硬件加速转码等)。
|
||||
@@ -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
|
||||
@@ -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` 全量源代码
|
||||
@@ -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 策略)
|
||||
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 26 KiB |
@@ -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
|
||||
|
Before Width: | Height: | Size: 14 KiB |
@@ -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 |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
@@ -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 |
|
Before Width: | Height: | Size: 14 KiB |
@@ -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 |
|
Before Width: | Height: | Size: 6.0 KiB |
@@ -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 |
|
Before Width: | Height: | Size: 981 B |
@@ -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 |
|
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 |
|
Before Width: | Height: | Size: 6.6 KiB |
@@ -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 |