本地影视库海报墙
把本地视频目录变成一张可搜索、可筛选的海报墙网页。
两个入口,按场景选一个
| 场景 | 入口 | 说明 |
|---|---|---|
| 全新建立:目录里只有视频文件(可能没有 nfo、没有封面) | build.py | 从零扫描 → 补全元数据与封面 → 生成海报墙 |
| 补全:已经建过一部分,某些条目缺封面 / 封面不是竖版 / 缺标题 / 缺评分 / 缺中字标记 | complete.py | 先体检报告缺什么,再只补缺失的部分 |
两个入口都在当前工作目录产出(movies.json、posters/、thumbs/、index.html),
建议先建一个输出目录并 cd 进去,或用 --dir 指定。
功能一:全新建立 build.py
python build.py "E:\Movies" "D:\See" # 扫描这些库并建立
python build.py "E:\Movies" --no-net # 不联网,只用本地 nfo 与已有图片
python build.py "E:\Movies" --dir ./out # 指定产物目录
python build.py "E:\Movies" --sync-back # 额外把结果回写进源 .nfo
内部按顺序调用(任何一步失败即中止,可选步骤失败会跳过):
1/6 scan.py 扫描视频 -> movies.json
2/6 enrich.py 读本地 nfo -> 标题/简介/评分/票数/中字
3/6 apply.py 合并中文译名与元数据缓存(zh_titles.json / meta_cache.json)
4/6 (在线源) 通用版未内置,跳过 —— 用本地 nfo 代替
5/6 covers.py 补齐竖版封面(本地裁切,必要时联网)
6/6 build_thumbs.py 生成缩略图 → build_html.py 生成 index.html
sync_back.py 可选:回写源 nfo(--sync-back)
产物:index.html(单文件,双击即可浏览)、movies.json、posters/、thumbs/、
register_play_protocol.reg + play_handler.py(播放按钮用,见下方「播放」)。
中文译名怎么生效:把
{"ABC-123": "中文标题"}存成产物目录下的zh_titles.json(或zh_extra.json,值可带actor字段),build.py/complete.py的第 3 步会自动合并, 不需要手动跑apply.py。
功能二:补全 complete.py
python complete.py # 体检 + 就地补全(不联网)
python complete.py --online # 允许联网重下更高清封面
python complete.py --libs "E:\Movies" # 先重扫刷新字段(补中字/评分时用)
python complete.py --dir ./out # 指定产物目录
python complete.py --sync-back # 补完后回写源 .nfo
先打印体检结果,再动手,最后复检对比:
体检结果(补全前):
总数 827
缺封面 0
封面非竖版 74 ← Emby 会显示不出竖版海报
缺中文标题 0
缺评分 216
缺中字标记 0
缺中字标记 / 评分不对时用 --libs 重扫——这两项是 scan.py 与 enrich.py
在扫描阶段写入的字段,就地补不了。
工具集(按用途分类)
名称与元数据
| 脚本 | 用途 | 用法 |
|---|---|---|
| rename.py | 把演员名改成中文(演员级 + 影片级目录一起改,会重命名磁盘目录) | python rename.py --dir ./out(预演)/加 --apply 执行/--undo 撤回 |
| sync_back.py | 把结果回写进源 .nfo(会先备份原 nfo) | python sync_back.py --apply |
| cleanup.py | 收尾:修失效路径 + 删空文件夹(送回收站,可还原) | python cleanup.py --apply |
rename.py只改磁盘目录名;movies.json里的actor字段由scan.py重扫时统一 (别名表actor_alias.json),所以不需要再单独跑"修演员名"这类脚本。 它内置两道安全阀:只对含日文假名的目录段做映射;从旧 nfo 备份反推的映射必须多数一致才采纳 (movies.json重扫后索引会错位,否则会把中文名改成错的)。
封面
| 脚本 | 用途 | 用法 |
|---|---|---|
| covers.py | 补齐 / 修正封面(本地裁切;--online 需自行接入图源,通用版默认不联网) | python covers.py |
| hires.py | 高清封面升级:从 DMM 官方 CDN 换更大的海报 / 横图 | 见下方「高清封面升级」 |
清晰度排查
| 脚本 | 用途 | 用法 |
|---|---|---|
| lowres.py | 低分辨率影片 → 找高清替代资源(scan / check / report 三个子命令) | 见下方「低分辨率影片」 |
播放
播放按钮用 play:// 自定义协议唤起系统默认播放器(浏览器不允许网页直接启动本地程序)。
register_play_protocol.reg 与 play_handler.py 不在 skills 目录内,
而是由 build_html.py 随海报墙产物一起生成:
- 双击产物目录里的
register_play_protocol.reg导入(只写HKEY_CURRENT_USER,不需要管理员) - 之后页面里的「播放」按钮即可直接调起本地播放器
- 不想要了,导入
unregister_play_protocol.reg注销
未导入时按钮无反应属预期现象,不是 bug。
低分辨率影片 → 找高清替代资源(三步)
媒体库里总有几部是 480p 的老片。用 lowres.py 一条龙:
# ① 扫出低分辨率清单(读 Emby 库秒出;没装 Emby 用 --libs + --ffprobe)
python lowres.py scan --emby-db "D:/programdata/data/library.db" --out low_res.json
# ② 逐个番号查 JavDB 资源区(清晰度 / 体积 / 磁力链接)
python lowres.py check --list low_res.json --out javdb_hires_check.json
# ③ 生成图文并排的报告,人工判断要不要替换
python lowres.py report --check javdb_hires_check.json \
--movies movie-wall/movies.json --thumbs movie-wall/posters --out 替代方案.html
关键规则与坑:
- 中字优先:本地是中文字幕版(番号带
-C)时,候选池只取带中文字幕的资源;找不到就判「保留」, 绝不用无字幕原盘顶替,否则字幕就丢了。 - 资源可有两个标签:「高清」和「字幕」可能同时存在(
<div class="tags">内有多个 span), 只取第一个标签会把中字高清资源漏掉。 - JavDB 限流很紧:必须串行 + 每次请求间隔 ≥1.4 秒,否则跑一半后全部拿不到结果。
- 番号命中要用详情页的「番號」字段校验,搜索结果第一条未必是目标影片。
- 本地时长与 JavDB 官方时长并列显示,方便判断本地是不是残缺版。
- 留意资源名含
-U/「无码破解」的是第三方破解版,并非原厂高清化。
高清封面升级(hires.py)—— 两个铁律
- 只能 keep-alive 单连接串行下载:多线程会被 CDN 限速到 ~17 KB/s 并整批卡死,单连接复用约 200 KB/s。
- 换图必须做「跨影片 md5 唯一性」校验:只校验尺寸和方差会被兜底图/错误 cid 欺骗。
动手前读 references/dmm-hires.md(含素材地址、cid 形式、SOD 系 1 前缀、原子替换写法)。
环境依赖
- Pillow 必须有(
build.py/covers.py/build_thumbs.py都要)。若报缺 Pillow,换带 Pillow 的解释器:C:\Users\<你>\.workbuddy\binaries\python\envs\default\Scripts\python.exe - 其余步骤只用标准库
- Windows 路径含中文没问题;脚本内部统一转成
/存储
⚠ 顺序陷阱
scan.py 会重写 movies.json,并清空 build_thumbs.py 写入的封面字段 p/t。
只跑 scan 就直接看页面,封面会全线变占位块。正确顺序永远是:
scan.py → enrich.py → build_thumbs.py → build_html.py
两个入口脚本已内置这个顺序,直接用入口就不会踩。
关键设计(改动前务必理解)
-
封面按源图实际宽高比决定裁切锚点。横版源图(
宽>=高)靠右裁切—— 横版剧照的主体通常偏右,居中裁会把人切掉一半;竖版源图居中裁。 判断依据必须是源图真实比例,不能按"它存在哪个字段"来猜(官方封面常是横版却放在 poster 字段)。 -
封面命名跟「视频文件名 stem」走,不是纯编号。 Emby 优先级:
<视频名>-poster.jpg>poster.jpg>folder.jpg>movie-poster.jpg。 写入时统一用<stem>-poster.jpg,它优先级最高最稳。 -
pick_image的大小写坑(已修,别改回去)。 文件名清单用f.lower()做键,候选却保留原始大小写,会导致START-383-C-poster.jpg这类含大写的一律匹配失败(曾造成 413 条封面识别不到,被迫用横版缩略图兜底)。 必须low.get(cand.lower())。验证是否生效:看build_thumbs输出的锚点分布, 横图才靠右,若出现大量"靠右"说明还在用横版兜底图。 -
-C后缀 = 中文字幕 →subtitle字段,卡片左上角显示「中字」徽章。 正则要求-C后接空白/./_/[/(或结束,否则ABC-123-UC会被误判。 -
评分是 5 分制,带
<votes>评价人数。enrich.py必须同时读rating与votes, 且rating保留 2 位小数;早年只读 rating 且 round 到 1 位,一重扫票数就整批丢失。 时长也要容错:runtime可能是120,也可能是120 min/2h 5min, 只认isdigit()会把带单位的整批归零。 -
封面的分辨率天花板是 358~379 宽。源站最大只给 800×538 横图,裁成 2:3 竖版理论极限 358。 所以重下只对宽度明显低于 358 的条目有效,全量重下是浪费请求。 需要更高清请直接用
hires.py(DMM 官方 CDN,可达 1032×1468)。 -
联网取的封面必须校验是不是占位图(踩过坑)。站点常对缺封面的作品返回一张共用的 「图片准备中」占位图(曾遇到 533×800 / 16 KB、11 部作品 md5 完全相同)。 它比真实的小封面大,若只比分辨率就会被当成"提升"替换进去 —— 那是把真图换成假图。 判定可疑图最可靠的办法是按 md5 分组看有没有被大量作品共用(真实封面各不相同)。
通用版的
covers.py默认不联网(fetch_online()直接返回None), 所以这条防护不在通用版生效;如果你自己接了图源,务必按上面加体积阈值 + md5 指纹两道拦截。hires.py已内置跨影片 md5 唯一性校验,走它就安全。 -
扫描时写入的字段,就地补不了。
subtitle、rating、votes都由 scan/enrich 生成, 重扫才会更新;complete.py不加--libs时只补封面之类的就地可改项。 -
花絮不进海报墙。
behind the scenes/特典等归为 extras,不占海报位。
安全约束
scan.py是只读的,不动用户文件- 凡是要改磁盘的(
rename.py、cleanup.py、sync_back.py)默认都是预演, 必须显式加--apply才执行;预演不落盘(movies.json也不回写) cleanup.py删目录走回收站,不永久删除;非 Windows 平台直接拒绝执行而不是崩溃sync_back.py改 nfo 前会备份到backup_nfo/<日期>/- 三个改盘脚本都内置
within(路径, 库根)包含校验:movies.json是可以随手分享的 文件,dir/lib/path全由它驱动;没有这道校验,投毒一个字段就能让脚本去库外 改名、移动或删目录。越界条目一律跳过并在输出里点名 build_html.py把movies.json注入<script>前会把<转义成\u003c, 防止标题/简介里的</script>逃逸执行脚本(否则页面一经分享/托管就是完整 XSS)- 联网客户端(
hires.py/lowres.py/covers.py)保持 TLS 默认证书校验, 不放宽CERT_NONE:下载的图片字节会被 Pillow 解码,中间人篡改即是真实攻击面
环境坑(Windows + 本沙箱)
- Bash 的
head/tail/ls/rm/dirname/del都不可用,一律用 Python 或文件工具代替 build_html.py等依赖os.getcwd(),必须在产物目录里运行(入口脚本已处理 cwd)- 剪贴板:别直接用
navigator.clipboard,非安全上下文里它是undefined, 调用会同步抛错导致.then()根本不执行。页面已实现三级回退(clipboard → execCommand → 手动复制框) SHFileOperationW在部分盘返回 rc=2 但实际已删除,判断是否成功要用os.path.exists复查
脚本清单
入口: build.py 全新建立
complete.py 补全
rename.py 演员名统一(磁盘目录;--apply 执行 / --undo 撤回 / --dir 指定数据目录)
封面: covers.py 补齐、修正封面(本地裁切)
hires.py DMM 官方大图升级(probe / fetch)
清晰度: lowres.py 低分辨率排查(scan / check / report)
收尾: sync_back.py 回写源 nfo
cleanup.py 修失效路径 + 删空目录(回收站)
流水线: scan.py enrich.py apply.py build_thumbs.py build_html.py
(由 build.py / complete.py 编排调用,一般不直接跑)
播放: play_handler.py + register_play_protocol.reg(由 build_html.py 随产物生成)
数据: actor_alias.json(演员中文别名表,只维护这一份)
变更履历见 CHANGELOG.md。
微信扫一扫