← 返回 Skill 列表
extension
分类: 数据与分析无需 API Key

大小电影刮削海报墙

把本地影视/视频文件夹整理成可浏览的海报墙网页(单文件 HTML:网格封面、搜索、按分组与来源库筛选、排序、详情弹窗、一键复制路径、点击播放)。提供两个入口——build.py 从只有视频文件的目录全新建立;complete.py 对已建过一部分的库补缺失的封面/标题/评分/中字标记。当用户说"整理电影文件夹""做个海报墙""把影片库做成索引页""movie wall / poster wall / media library""补全海报墙"时使用。 可以处理小姐姐的视频 给小姐姐一个别墅 支持Emby

person作者: user_493c1b82hubcommunity

本地影视库海报墙

把本地视频目录变成一张可搜索、可筛选的海报墙网页。

两个入口,按场景选一个

| 场景 | 入口 | 说明 | |---|---|---| | 全新建立:目录里只有视频文件(可能没有 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 随海报墙产物一起生成:

  1. 双击产物目录里的 register_play_protocol.reg 导入(只写 HKEY_CURRENT_USER,不需要管理员)
  2. 之后页面里的「播放」按钮即可直接调起本地播放器
  3. 不想要了,导入 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)—— 两个铁律

  1. 只能 keep-alive 单连接串行下载:多线程会被 CDN 限速到 ~17 KB/s 并整批卡死,单连接复用约 200 KB/s。
  2. 换图必须做「跨影片 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

两个入口脚本已内置这个顺序,直接用入口就不会踩。


关键设计(改动前务必理解)

  1. 封面按源图实际宽高比决定裁切锚点。横版源图(宽>=高)靠右裁切—— 横版剧照的主体通常偏右,居中裁会把人切掉一半;竖版源图居中裁。 判断依据必须是源图真实比例,不能按"它存在哪个字段"来猜(官方封面常是横版却放在 poster 字段)。

  2. 封面命名跟「视频文件名 stem」走,不是纯编号。 Emby 优先级:<视频名>-poster.jpg > poster.jpg > folder.jpg > movie-poster.jpg。 写入时统一用 <stem>-poster.jpg,它优先级最高最稳。

  3. pick_image 的大小写坑(已修,别改回去)。 文件名清单用 f.lower() 做键,候选却保留原始大小写,会导致 START-383-C-poster.jpg 这类含大写的一律匹配失败(曾造成 413 条封面识别不到,被迫用横版缩略图兜底)。 必须 low.get(cand.lower())。验证是否生效:看 build_thumbs 输出的锚点分布, 横图才靠右,若出现大量"靠右"说明还在用横版兜底图。

  4. -C 后缀 = 中文字幕 → subtitle 字段,卡片左上角显示「中字」徽章。 正则要求 -C 后接空白/./_/[/( 或结束,否则 ABC-123-UC 会被误判。

  5. 评分是 5 分制,带 <votes> 评价人数。enrich.py 必须同时读 rating 与 votes, 且 rating 保留 2 位小数;早年只读 rating 且 round 到 1 位,一重扫票数就整批丢失。 时长也要容错:runtime 可能是 120,也可能是 120 min / 2h 5min, 只认 isdigit() 会把带单位的整批归零。

  6. 封面的分辨率天花板是 358~379 宽。源站最大只给 800×538 横图,裁成 2:3 竖版理论极限 358。 所以重下只对宽度明显低于 358 的条目有效,全量重下是浪费请求。 需要更高清请直接用 hires.py(DMM 官方 CDN,可达 1032×1468)。

  7. 联网取的封面必须校验是不是占位图(踩过坑)。站点常对缺封面的作品返回一张共用的 「图片准备中」占位图(曾遇到 533×800 / 16 KB、11 部作品 md5 完全相同)。 它比真实的小封面大,若只比分辨率就会被当成"提升"替换进去 —— 那是把真图换成假图。 判定可疑图最可靠的办法是按 md5 分组看有没有被大量作品共用(真实封面各不相同)。

    通用版的 covers.py 默认不联网(fetch_online() 直接返回 None), 所以这条防护不在通用版生效;如果你自己接了图源,务必按上面加体积阈值 + md5 指纹两道拦截。 hires.py 已内置跨影片 md5 唯一性校验,走它就安全。

  8. 扫描时写入的字段,就地补不了。subtitle、rating、votes 都由 scan/enrich 生成, 重扫才会更新;complete.py 不加 --libs 时只补封面之类的就地可改项。

  9. 花絮不进海报墙。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。