-
Notifications
You must be signed in to change notification settings - Fork 0
Database Schema
项目使用两个 SQLite 数据库,均采用 WAL 模式 以获得并发读取性能。
由 Database 类管理(src/database.py)。通过自定义 ReadWriteLock 类(database.py 顶部定义,支持读写分离:多个读者并发、写者独占、写者优先防饥饿)保证线程安全。所有表在 _create_schema() 方法中创建。bridge.db 共 14 张表:10 张常规表、3 张 FTS5 虚拟表以及 1 张 subtitles 字幕表(由 init_subtitle_table() 独立创建)。
每个连接应用以下优化:
PRAGMA journal_mode=WAL; -- 写前日志,支持并发读取
PRAGMA busy_timeout=10000; -- 10 秒忙等待
PRAGMA synchronous=NORMAL; -- WAL 模式下安全与速度的平衡
PRAGMA cache_size=-64000; -- 64MB 页缓存
PRAGMA temp_store=MEMORY; -- 临时表在内存中
PRAGMA mmap_size=268435456; -- 256MB 内存映射 I/O只读连接额外设置:PRAGMA query_only=ON;
| 字段 | 类型 | 说明 |
|---|---|---|
local_path |
TEXT PRIMARY KEY | A 区本地绝对路径 |
webdav_path |
TEXT NOT NULL | 规范化后的 WebDAV 路径 |
parent_webdav_path |
TEXT NOT NULL | 父级 WebDAV 目录 |
updated_at |
REAL NOT NULL | 最后更新时间戳 |
索引:idx_a_strm_webdav_path(webdav_path)、idx_a_strm_updated_at(updated_at)
| 字段 | 类型 | 说明 |
|---|---|---|
local_path |
TEXT PRIMARY KEY | B 区本地绝对路径 |
webdav_path |
TEXT NOT NULL | 规范化后的 WebDAV 路径 |
parent_webdav_path |
TEXT NOT NULL | 父级 WebDAV 目录 |
source_a_path |
TEXT | 对应的 A 区源路径 |
fingerprint |
TEXT | SHA-256 指纹 |
status |
TEXT DEFAULT 'valid' | 状态:valid/duplicate/quarantined/invalid/ghost |
updated_at |
REAL NOT NULL | 更新时间戳 |
索引:idx_b_strm_webdav_path、idx_b_strm_fingerprint、idx_b_strm_status、idx_b_strm_updated_at
| 字段 | 类型 | 说明 |
|---|---|---|
fingerprint |
TEXT PRIMARY KEY | SHA-256 指纹 |
webdav_path |
TEXT NOT NULL | 规范化的 WebDAV 路径 |
source_a_path |
TEXT | 原始 A 区源路径 |
current_b_path |
TEXT | 当前 B 区路径(可能因改名而不同于 A 区) |
updated_at |
REAL NOT NULL | 更新时间戳 |
索引:idx_identity_webdav_path、idx_identity_current_b_path
| 字段 | 类型 | 说明 |
|---|---|---|
local_path |
TEXT PRIMARY KEY | C 区当前路径 |
webdav_path |
TEXT NOT NULL | 原始 WebDAV 路径 |
original_b_path |
TEXT NOT NULL | 迁移前的 B 区路径 |
ghost_root |
TEXT NOT NULL | 所在的 C 区根目录 |
moved_at |
REAL NOT NULL | 迁移时间戳 |
索引:idx_c_ghost_moved_at(moved_at)
| 字段 | 类型 | 说明 |
|---|---|---|
webdav_path |
TEXT PRIMARY KEY | 受保护的 WebDAV 路径 |
expire_time |
REAL NOT NULL | 保护过期时间戳 |
reason |
TEXT | 保护原因(如 user_delete) |
| 字段 | 类型 | 说明 |
|---|---|---|
folder_path |
TEXT PRIMARY KEY | WebDAV 文件夹路径 |
source |
TEXT | 发现来源 |
updated_at |
REAL NOT NULL | 最后发现时间 |
| 字段 | 类型 | 说明 |
|---|---|---|
root_path |
TEXT PRIMARY KEY | 引擎根 WebDAV 路径 |
trash_path |
TEXT NOT NULL | 对应的回收站路径 |
active |
INTEGER NOT NULL | 1=活跃,0=不活跃 |
updated_at |
REAL NOT NULL | 更新时间戳 |
| 字段 | 类型 | 说明 |
|---|---|---|
root_path |
TEXT PRIMARY KEY | 引擎根 WebDAV 路径 |
trash_path |
TEXT NOT NULL | 对应的回收站路径 |
updated_at |
REAL NOT NULL | 快照时间戳 |
| 字段 | 类型 | 说明 |
|---|---|---|
control_key |
TEXT PRIMARY KEY | 控制键名 |
control_value |
TEXT NOT NULL | 控制值(JSON 编码) |
updated_at |
REAL NOT NULL | 更新时间戳 |
| 字段 | 类型 | 说明 |
|---|---|---|
fingerprint |
TEXT PRIMARY KEY | SHA-256 指纹 |
source_media_name |
TEXT NOT NULL | 原始云端媒体名 |
current_media_name |
TEXT NOT NULL | 当前本地文件夹名 |
engine_entry_path |
TEXT NOT NULL | 对应的引擎入口路径 |
updated_at |
REAL NOT NULL | 时间戳 |
索引:idx_boundary_source_name、idx_boundary_current_name
bridge.db 中包含三张 FTS5 虚拟表,使用 simple 或 unicode61 分词器(取决于 simple.dll 是否加载成功):
| 虚拟表 | 索引基表 | 索引字段 |
|---|---|---|
a_strm_files_fts |
a_strm_files |
local_path、webdav_path
|
b_strm_files_fts |
b_strm_files |
local_path、webdav_path
|
c_ghost_files_fts |
c_ghost_files |
local_path、webdav_path
|
维护:_backfill_fts_if_empty(首次回填)和 _rebuild_fts_if_stale(孤儿清理,rowid 不一致时全量重建)。
由 init_subtitle_table() 单独创建,Database.__init__() 时调用。
| 字段 | 类型 | 说明 |
|---|---|---|
id |
INTEGER PRIMARY KEY AUTOINCREMENT | 自增 ID |
local_path |
TEXT NOT NULL UNIQUE | 原始字幕文件路径 |
target_path |
TEXT NOT NULL | B 区同步路径 |
fingerprint |
TEXT NOT NULL | 关联 STRM 的指纹 |
season |
INTEGER | 提取的季号(番剧) |
episode |
INTEGER | 提取的集号(番剧) |
lang_code |
TEXT | 检测的语言代码 |
status |
TEXT DEFAULT 'valid' | 处理状态 |
created_at |
TIMESTAMP DEFAULT CURRENT_TIMESTAMP | 创建时间 |
updated_at |
TIMESTAMP DEFAULT CURRENT_TIMESTAMP | 更新时间 |
索引:idx_subtitle_fingerprint(fingerprint)、idx_subtitle_target(target_path)
由 TmdbWatchlistDb 类管理(src/tmdb_watchlist_db.py)。tmdb_watchlist.db 共 6 张表:5 张常规表(movies、tv、meta、webui_config、tmdb_operation_log)以及 1 张 FTS5 虚拟表 tmdb_watchlist_fts。
本项目的全文搜索依赖 SQLite FTS5 虚拟表,并结合中文分词器 simple(cppjieba 封装,源于 wangfenjin/simple,当前内置版本见 src/tokenizers/simple/VERSION,约 v0.7.1)实现中文智能检索。
-
分词器加载:
database.py的_load_simple_tokenizer(在每次建立连接时通过conn.load_extension加载src/tokenizers/simple/simple.dll)与tmdb_watchlist_db.py的_load_simple_into采用相同逻辑。加载成功后记录实际分词器名(_fts_tokenizer = 'simple')并读取VERSION文件缓存版本到_simple_version;失败(dll 缺失或加载异常)时仅记录logging.warning软降级到 SQLite 内建的unicode61,不阻断启动。 -
降级风险:
unicode61对中文不产生有效的 token,因此当simple.dll缺失而降级时,中文搜索实际上会完全失效。即便存在前缀查询(如黑*),本项目的 FTS 查询转义逻辑(_escape_fts5_query)会移除*等通配符,所以前缀侥幸命中也不成立。换言之,simple分词器是中文搜索的硬依赖,部署时必须保证src/tokenizers/simple/simple.dll存在且可被load_extension加载。 -
FTS 虚拟表:bridge.db 中包含
a_strm_files_fts/b_strm_files_fts/c_ghost_files_fts,tmdb_watchlist.db 中包含tmdb_watchlist_fts。这些虚拟表使用上述选中的分词器(simple 优先,否则 unicode61),索引对应基表的local_path/webdav_path(或 tmdb 的title/original_title/overview)。 -
孤儿行清理:当基表发生增删改时,通过
_rebuild_fts_if_stale(及_backfill_fts_if_empty)比对 rowid 集合,删除 FTS 中已无对应基表行的孤儿记录,保证索引与数据一致。
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
id |
INTEGER PRIMARY KEY | TMDB 电影 ID | |
title |
TEXT | '' | 电影标题 |
original_title |
TEXT | '' | 原始标题 |
overview |
TEXT | '' | 剧情简介 |
poster_path |
TEXT | '' | 海报路径 |
backdrop_path |
TEXT | '' | 背景图路径 |
release_date |
TEXT | '' | 上映日期 |
vote_average |
REAL | 0.0 | TMDB 评分 |
vote_count |
INTEGER | 0 | 评分人数 |
genre_ids |
TEXT | '[]' | 类型 ID 数组(JSON) |
popularity |
REAL | 0.0 | 人气值 |
original_language |
TEXT | '' | 原始语言 |
video |
INTEGER | 0 | 是否为视频 |
adult |
INTEGER | 0 | 是否为成人内容 |
_media_type |
TEXT | 'movie' | 媒体类型 |
_synced_at |
REAL | 0 | 同步时间戳(NOT NULL) |
match_status |
TEXT | 'uncomputed' | 匹配状态 |
match_reason |
TEXT | '' | 匹配原因说明 |
match_updated_at |
REAL | 0 | 匹配状态最后更新时间 |
manual_override_at |
REAL | 0 | 手动覆盖时间 |
manual_override_by |
TEXT | '' | 手动覆盖操作者 |
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
id |
INTEGER PRIMARY KEY | TMDB 剧集 ID | |
name |
TEXT | '' | 剧集名称 |
original_name |
TEXT | '' | 原始名称 |
overview |
TEXT | '' | 剧情简介 |
poster_path |
TEXT | '' | 海报路径 |
backdrop_path |
TEXT | '' | 背景图路径 |
first_air_date |
TEXT | '' | 首播日期 |
vote_average |
REAL | 0.0 | 评分 |
vote_count |
INTEGER | 0 | 评分人数 |
genre_ids |
TEXT | '[]' | 类型 ID 数组(JSON) |
popularity |
REAL | 0.0 | 人气值 |
origin_country |
TEXT | '[]' | 产地国家(JSON 数组) |
original_language |
TEXT | '' | 原始语言 |
_season_count |
INTEGER | 0 | 季数 |
_episode_count |
INTEGER | 0 | 集数 |
_last_ep_season |
INTEGER | 0 | 最后一季号 |
_last_ep_episode |
INTEGER | 0 | 最后一集号 |
_media_type |
TEXT | 'tv' | 媒体类型 |
_synced_at |
REAL | 0 | 同步时间戳(NOT NULL) |
match_status |
TEXT | 'uncomputed' | 匹配状态 |
match_reason |
TEXT | '' | 匹配原因说明 |
match_updated_at |
REAL | 0 | 匹配状态最后更新时间 |
manual_override_at |
REAL | 0 | 手动覆盖时间 |
manual_override_by |
TEXT | '' | 手动覆盖操作者 |
| 字段 | 类型 | 说明 |
|---|---|---|
key |
TEXT PRIMARY KEY | 元数据键 |
value |
TEXT NOT NULL | 值(JSON 编码) |
| 字段 | 类型 | 说明 |
|---|---|---|
scope |
TEXT NOT NULL | 配置作用域(tmdb、openlist、ui、migration) |
key |
TEXT NOT NULL | 配置键名 |
value |
TEXT NOT NULL DEFAULT '' | 配置值(JSON 编码) |
updated_at |
REAL NOT NULL DEFAULT 0 | 更新时间戳 |
主键:(scope, key)
示例:('tmdb', 'access_token', 'eyJ...', 1700000000)、('ui', 'admin_password', 'salt$600000$hash', 1700000000)
| 字段 | 类型 | 说明 |
|---|---|---|
id |
INTEGER PRIMARY KEY AUTOINCREMENT | 自增 ID |
ts |
REAL NOT NULL | 时间戳 |
op |
TEXT NOT NULL | 操作类型(如 sync) |
level |
TEXT NOT NULL DEFAULT 'info' | 日志级别(info/success/warn/error) |
msg |
TEXT NOT NULL | 日志消息 |
detail |
TEXT | 详细信息 |
索引:idx_tmdb_log_ts(ts DESC)
为 TMDB 待看列表(电影 movies / 电视剧 tv)提供全文搜索能力,建表语句见 tmdb_watchlist_db.py 的 _init_schema 方法(注意:database.py 中对应的建表方法名为 _create_schema,两者不同)。该虚拟表使用与 bridge.db 相同的分词器选择逻辑(simple 优先,失败降级 unicode61,见上文「全文搜索与中文分词」)。
-
索引字段:
rowid(关联基表主键id)、title、original_title、overview,覆盖标题与简介的中英文检索。 -
维护:在电影/剧集写入、更新、删除时同步增删 FTS 行;通过
DELETE ... WHERE rowid NOT IN (SELECT rowid FROM movies/tv)清除孤儿行,保证待看列表检索结果与真实数据一致。
🏡 返回 Wiki 首页 • 💻 项目源码仓库 • 🐛 提交 Bug / 建议 • 📦 下载最新版本
🚨 安全与自保黄金法则(每页必读)
- 严禁随意重置 OpenList 令牌:播放签名(
?sign=)强依赖服务端密钥。一旦重置,B区所有.strm将瞬间失效报无权播放,只能清库重来!- 调试阶段切勿使用 DELETE:
DELETE会物理删除云端文件,极其危险!建议终身配置为action = "MOVE"(云端一比一树状回收站模式)。- 放心刮削,资产安全:空文件夹清理算法采用严格的零物理文件判定,含有海报图片、
.nfo、外部字幕的目录绝对不会被误删。
本项目遵循 MIT 开源协议。数据无价,请在充分测试后接入生产环境。