-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture Overview
代码库采用分层架构,各层职责明确:
┌──────────────────────────────────────────────────────────────┐
│ 入口层 (main.py) │
│ 加载配置 → 初始化 DB → 创建 Admin Client → 启动 AppService │
├──────────────────────────────────────────────────────────────┤
│ 编排层 (app_service_core.py) │
│ AppService — 生命周期、事件处理器、血统校验、清理 │
│ SyncService — A→B 同步管线 │
│ RefreshService — 周期性 WebDAV 刷新 │
│ SubtitleHandler — 字幕检测与归档 │
├──────────────────────────────────────────────────────────────┤
│ 领域服务层 (domain/) │
│ SubtitleHandler(domain/media/subtitle_handler.py) │
│ SyncService(domain/sync/sync_service.py) │
├──────────────────────────────────────────────────────────────┤
│ 基础设施层 │
│ Database — SQLite bridge.db 管理器 │
│ OpenListAdminClient — JWT 认证 + Admin API │
│ OpenlistWebDAV — WebDAV 协议客户端 │
│ TmdbClient — TMDB API v3 客户端 │
│ TmdbWatchlistDb — TMDB 待看列表 DB │
├──────────────────────────────────────────────────────────────┤
│ 工具层 (utils/) │
│ strm_utils(指纹/路径解析)、file_utils、webdav_utils │
├──────────────────────────────────────────────────────────────┤
│ WebUI 层 (webui/) │
│ server.py — HTTP 服务器 + 鉴权 + 路由分发 │
│ routes.py — 全部 API 处理器 │
│ modules/ — Vanilla JS SPA 前端 │
└──────────────────────────────────────────────────────────────┘
中央编排器。管理完整的同步生命周期:
- 环境准备与数据库初始化
- OpenList 引擎配置加载
- A/B/C 区 watchdog 事件处理器
- A→B 复制与血统校验
- B 区事件处理(创建/修改/删除/移动)
- 幽灵保护与冗余清理
- 文件损坏恢复
class AppService:
def __init__(self, config: AppConfig, db: Database,
admin_api: OpenListAdminClient) -> None:SQLite 数据库管理器,WAL 模式。通过自定义 ReadWriteLock 类保证线程安全(非 RLock)。
管理 bridge.db 中 14 张表(10 张普通表 + 3 张 FTS5 虚拟表 + subtitles),提供读写连接的上下文管理器。
JWT 认证的 OpenList Admin API 客户端:
- TOTP 2FA 支持(base32/base64 自适应)
- Token 缓存(24h TTL)
- 401 自动重试重新登录
- 大列表分页获取
管理 STRM 存储的发现与本地配置的验证。
类型化 dataclass 配置,嵌套配置段。从 config.toml 加载,数据库 webui_config 表可覆盖。
引擎使用严格的 6 级锁层次防止死锁(定义在 AppService.__init__ 方法中):
获取顺序(必须从小到大获取):
1. _path_locks_lock (获取 path_lock 时)
2. _path_locks[path] (单路径操作)
3. _dav_write_lock (WebDAV 写操作)
4. _cleanup_lock (延迟清理定时器管理)
5. _restoring_lock (恢复标记 / 引擎内部删除标记)
6. _lineage_log_lock (日志记录)
规则:只能按编号从小到大获取,释放时反向。禁止同时持有非相邻的锁。
附加锁:
-
_fingerprint_locks— 按指纹串行化 A→B 处理(防止 TOCTOU 竞态) -
get_path_lock(path)— 按文件路径锁定,使用Path(path).resolve()做 key -
get_webdav_lock(webdav_path)— WebDAV 路径锁,使用webdav:前缀命名空间隔离
配置按以下优先级解析(main.py 启动流程、config.py):
-
数据库(
tmdb_watchlist.db→webui_config表)— 最高优先级 - OpenList API — 动态 STRM 存储映射
- config.toml — 静态文件配置
- 默认值 — dataclass 定义中的硬编码默认值
首次启动时,config.toml 内容会被一次迁移到数据库(config.py:migrate_config_to_db),之后 DB 成为运行时配置的权威来源。
| 模式 | 用途 | 位置 |
|---|---|---|
| Dataclass 配置 | 所有配置段使用 @dataclass(slots=True);StrmStorageInfo 等不可变快照使用 frozen=True
|
config.py、app_service_core.py
|
| 上下文管理器 DB 连接 | with self.lock, self.connection() as conn: |
database.py |
| 指纹去重 |
make_strm_fingerprint() 对 WebDAV 路径做 SHA256 哈希 |
utils/strm_utils.py |
| 事件驱动文件监控 | watchdog Observer + 3 个事件处理器 |
area_watchers.py |
| 子服务委托 | AppService 创建 SyncService、SubtitleHandler、RefreshService | app_service_core.py |
| 渲染过时检测 | 前端 router 根据计数器判定渲染结果是否过时 | router.js |
项目的中文智能搜索建立在 SQLite FTS5 全文搜索虚拟表 之上,核心由基础设施层的 Database(src/database.py)与 TmdbWatchlistDb(src/tmdb_watchlist_db.py)负责。
-
分词器:中文使用
simple分词器(cppjieba 封装,源于 wangfenjin/simple,内置版本见src/tokenizers/simple/VERSION,约 v0.7.1)。database.py的_load_simple_tokenizer与tmdb_watchlist_db.py的_load_simple_into在连接建立时通过conn.load_extension加载src/tokenizers/simple/simple.dll;加载成功后切换为simple并记录版本,失败则软降级到内建unicode61(仅 warning,不阻断启动)。 -
降级风险:
unicode61不会对中文切分出有效 token,因此simple.dll缺失时中文搜索实际完全失效。本项目的 FTS 查询转义会移除*等通配符,前缀黑*之类的侥幸命中也不成立——simple是中文搜索的硬依赖。 -
FTS 虚拟表:bridge.db 的
a_strm_files_fts/b_strm_files_fts/c_ghost_files_fts索引 STRM/幽灵文件的local_path、webdav_path;tmdb_watchlist.db 的tmdb_watchlist_fts索引待看列表的title、original_title、overview。 -
一致性:
Database._rebuild_fts_if_stale/_backfill_fts_if_empty以及tmdb_watchlist_db.py中的孤儿清理,负责在基表变更后清理 FTS 中悬空的孤儿行,保证索引与基表一致。
为降低首次使用门槛,WebUI 提供 7 步新手引导,引导状态持久化在 tmdb_watchlist.db 的 webui_config 表中(scope='ui',键如 onboarding_completed)。
-
7 个步骤(定义于前端
src/webui/modules/pages/dashboard.js的 steps):password(确认管理员密码)、tmdb(配置 TMDB)、openlist(配置 OpenList)、main(启动主程序)、view_ab(查看 A/B 区)、tmdb_refresh(刷新 TMDB 待看列表)、tmdb_match(检测 TMDB 收录状态)。 -
单步完成:前端调用
POST /api/onboarding/complete-step手动标记某一步已完成。 -
整体完成 / 跳过:通过
POST /api/webui/config/ui写入{ onboarding_completed: '1' }标记引导结束;白名单键见routes.py的_UI_CONFIG_ALLOWED_KEYS。 -
状态读取:
GET /api/config/status等接口回传onboarding_completed等字段,驱动前端步骤卡片的「已完成 / 进行中」展示。
🏡 返回 Wiki 首页 • 💻 项目源码仓库 • 🐛 提交 Bug / 建议 • 📦 下载最新版本
🚨 安全与自保黄金法则(每页必读)
- 严禁随意重置 OpenList 令牌:播放签名(
?sign=)强依赖服务端密钥。一旦重置,B区所有.strm将瞬间失效报无权播放,只能清库重来!- 调试阶段切勿使用 DELETE:
DELETE会物理删除云端文件,极其危险!建议终身配置为action = "MOVE"(云端一比一树状回收站模式)。- 放心刮削,资产安全:空文件夹清理算法采用严格的零物理文件判定,含有海报图片、
.nfo、外部字幕的目录绝对不会被误删。
本项目遵循 MIT 开源协议。数据无价,请在充分测试后接入生产环境。