给 AI Agent 写了个 Skill:扫描版电子书自动解压合并 + 加书签(附 PDG 格式逆向)

给 AI Agent 写了个 Skill:扫描版电子书自动解压合并 + 加书签(附 PDG 格式逆向)

估计很多朋友喜欢和我一样去网上下载电子书,一般都是超星格式的压缩包,也就是 ZIP 分卷包,里面是 PDG 页面文件,没目录。需要自己转换为 PDF,以前一本书要手工走完解压、按页序合并、查目录、算页码偏移、PdgCntEditor 粘目录调层级、保存、抽查,十几分钟一本。书一多就是体力活,于是写了个 Skill(SKILL.md + Python 脚本)交给 Agent 全流程跑。

Skill 下载见博客后面的下载链接,其中的配置请自行修改。

1. 素材长什么样

ZIP(未加密;加密的用固定密码表,逐个试),内层是 <SS码>/ 文件夹 + PDG 文件:

text 复制代码
14080995.zip
└── 14080995/
    ├── !00001.pdg ...      ← 目录等前置页
    ├── 000001.pdg ...      ← 正文
    ├── cov001/002.pdg    ← 封面 / 封底
    ├── bok001.pdg        ← 书名页
    ├── fow001.pdg        ← 前言
    └── leg001.pdg        ← 版权页

大部分 PDG 只是改了扩展名的 JPEG(文件头 FF D8 FF);少数是真容器格式,第 3 节细说。文件名前缀对应书的部位,这是后面排序的依据。

2. Skill 结构与流程

SKILL.md 写流程和触发条件,scripts/bmtool.py 承担全部确定性计算,config.json 放机器路径。分工原则:能写成脚本的不让 Agent 现场发挥,Agent 只做两件事------驱动书签查询软件的 GUI、异常时找人确认。

text 复制代码
zip ── bmtool merge ──▶ 无书签 PDF(输出页数/前置页定位/正文起始页 N)
     ── 书签软件查目录(Agent 填 SS 码,点"复制书签"读剪贴板)
     ── bmtool build(推层级)── bmtool write ── bmtool verify ──▶ 成品

bmtool 六个子命令:merge(解压合并)、toc(查现有书签)、build(生成书签文本)、write(写入 PDF)、verify(回读校验)、sheet(缩略图拼图,备用)。

四本书实测:

书 页数 书签数 验证
艾灸养生书 146 126 通过
REVIT 族设计手册 331 221 通过
空气调节设计手册(第3版) 1167 452 通过
暖通设计手册 1350 382 通过

3. PDG 容器:主图 JPEG 被剥掉了 SOI

第一版 merge 跑完发现 146 个 PDG 只合并出 121 页,静默丢了 25 页。查这 25 个文件,文件头是 48 48 02 4A(HH\x02J)------真 PDG 容器。十六进制逐段看,结构是:

text 复制代码
[PDG头 ~0x2588 字节,含 "SUPER STAR SSR EADER"]
[缩略图 JPEG:FF D8 ... FF D9,完整,但只有 160×153]
[IPTC / 8BIM 元数据块]
[主图 JPEG:从 APP1 直接开始 ... 文件尾 FF D9]   ← 没有 SOI

也就是说主图是完整 JPEG,唯独开头的 SOI 标记(FF D8)被剥掉了。全文件搜 FF D8 FF 只命中缩略图,按常规提取只能拿到 160×153 的小图,主图(1888×1808)全部丢失。

修复:在缩略图 EOI 之后找第一个 APPx 标记,补回 \xFF\xD8 前缀,并校验尺寸防止拿到缩略图:

python 复制代码
def pdg_main_image_bytes(data):
    j = data.find(b"\xff\xd8\xff", 1)          # 缩略图 SOI
    e = data.find(b"\xff\xd9", j)              # 缩略图 EOI
    for m in re.finditer(b"\xff[\xe0-\xee]", data[e+2:e+2+40000]):
        cand = b"\xff\xd8" + data[e+2+m.start():]
        im = Image.open(io.BytesIO(cand))
        if max(im.size) >= 800:                # 主图而非缩略图
            return cand
    return None

这次翻车后给脚本加了两条硬校验:解码失败的页面必须报警列出,不许静默跳过;合并页数必须等于包内 PDG 文件总数。

4. 页序与偏移 N

ZIP 条目顺序是乱的(cov/bok/fow/leg 全堆在目录末尾),不能直接当页序。按超星命名规范排:

text 复制代码
cov001(封面) < bok(书名) < leg(版权) < fow(前言) < !(目录) < 000001...(正文) < cov002(封底)

排完有个直接产出:正文第一页(000001.pdg)在 PDF 里的位置就是加书签要的偏移 N,不用再翻缩略图人工找。merge 输出:

text 复制代码
完成: 14314981 空气调节设计手册 第3版.pdf
共 1167 页, 241.0 MB
前置页定位: 封面=PDF第1页 书名=第2页 版权=第3页 前言=第4页 目录=第7页
正文第1页 = PDF第19页  (加书签时 N=19)

书签页码换算两条公式,从一本手工做好的成品书读回 110 条书签反推验证:

  • 正文(正数页码):PDF页 = N + 页码 − 1
  • 前置(零/负数页码):PDF页 = N + 页码

5. 为什么没用 PDG 保存

一开始走的就是 PdgCntEditor 路线:命令行带 PDF 路径启动、正文页文本区整体 set_value 都通了,卡在保存------工具栏按钮没有可访问性名称,图标位置不能猜;Ctrl+S 要窗口前台,Listary、spacedesk 的弹窗反复抢焦点;一次激活冲突直接把程序搞崩,未保存的编辑全丢。

改用 PyMuPDF 的 set_toc() 直写,和手工用 PdgCntEditor 做的成品逐条比对:110 条书签,层级、标题、页码零差异。等价就选脚本,自动化场景确定性优先。

顺带:原来配套的汇文馆客户端自带一键解压合并,登录服务器失联后整个客户端不可用。依赖第三方小工具的服务端说挂就挂,本地格式处理才是能长期持有的能力------这也是自己实现 merge 的原因。

6. 部署与配置

依赖:Python 3.10+,py -m pip install pymupdf pillow,7-Zip(官网下载)。把 pdf-bookmark/ 放进 Agent 的技能目录(如 ~/.agents/skills/)。本 Skill 完整代码可在此下载:下载链接。

改动集中在 config.json:

json 复制代码
{
  "archive_dir":   "D:\\Books\\PDG书",
  "output_dir":    "D:\\Books",
  "bookmark_tool": "D:\\Tools\\书签软件.exe",
  "pdgcnt_editor": "D:\\Tools\\PdgCntEditor.exe",
  "sevenzip":      "C:\\Program Files\\7-Zip\\7z.exe",
  "password_list": ""
}
  • sevenzip:必改;
  • archive_dir / output_dir:分卷包目录、成品输出目录;
  • bookmark_tool:按 SS 码联网查目录的小工具,本文不提供下载,请自行获取。没有它其余步骤照常------目录文本按 标题<TAB>页码 每行一条存 txt,从 bmtool build 接着跑;
  • pdgcnt_editor:可选,手工编辑书签才用;
  • password_list:可选,分卷包加密时放一个每行一个密码的文本文件。

也可以不改文件,用环境变量 PDFBK_7Z、PDFBK_PWLIST、PDFBK_ARCHIVE_DIR、PDFBK_OUTPUT_DIR 覆盖。

分卷包默认按文件名开头的 SS 码识别(如 14080995.zip);命名不带 SS 码就触发时直接报给 Agent。

7. 怎么用

两种用法,脚本可以完全脱离 Agent 单独跑。

方式一:直接用脚本 (不需要任何 Agent)。以 14080995.zip 为例:

bash 复制代码
# 1. 解压合并。输出会给出总页数、前置页位置和 N,先看一眼
py bmtool.py merge "D:\Books\PDG 书\14080995.zip" "D:\Books\14080995.pdf"
#   -> 正文第 1 页 = PDF 第 12 页  (加书签时 N=12)

# 2. 拿目录文本:书签软件填 SS 码查询、点"复制书签",粘贴存成 toc.txt
#    (每行格式:标题<TAB>纸书页码;手工整理的目录也行)

# 3. 生成书签文本。前置页相对码 = 该页 PDF 页码 - N,
#    上一步输出 目录=PDF 第 6 页、N=12,所以 目录=-6
py bmtool.py build toc.txt 12 --front "封面=-11,书名=-10,版权=-9,前言=-8,目录=-6" --out outline.txt

# 4. 写入书签并验证
py bmtool.py write "D:\Books\14080995.pdf" outline.txt 12
py bmtool.py verify "D:\Books\14080995.pdf" 12

只有第 2 步需要人(或 GUI 自动化)参与,其余全是本地命令。

方式二:交给 Agent 。配置好 config.json 后一句话触发,比如"PDG 书 有新包,做书",Agent 按 SKILL.md 自己跑完全流程------merge、驱动书签软件查目录、build/write、verify------最后汇报成品路径和验证结果。

8. 其他实现细节

  • verify 是硬闸门:首章节落点、页码单调、末条不超总页数,不过就回查偏移重做;
  • 已有书签的书默认拒绝覆盖,--force 才重建;标题全是"1、2、3..."的每页一条式垃圾书签(下载站自动生成的)直接替换;
  • 层级推断按标题规律:第N章/Part N→1 级、第X节/N.N→2 级、N.N.N/一、→3 级,章标题后紧跟的无编号小标题当子级。四种目录风格(章-节式、Part 式、纯编号式)实测排布正确。

9. 本 Skill 完整代码及下载

本 Skill 完整代码可在此下载:下载链接。

文中提到的所有第三方软件(书签软件、PdgCntEditor、7-Zip 等)均不提供下载,请自行获取。本文仅讨论本地已有文件的格式处理与流程自动化,不提供任何书籍获取渠道;请支持正版,尊重版权。

相关推荐
风花一世月1 小时前
[特殊字符] 我把一套分布式消防方案做成了能跑的 3D 沙盘(三架无人机共用一根水带,在线直接玩)
分布式·3d·ai·无人机
GalaxyMeteor1 小时前
Windows 本地部署 Sam3 模型笔记(含Demo)
ai
秋饼2 小时前
Java 后端企业级 LLM 网关实战:Spring Cloud Gateway 构建统一大模型接入层
java·ai·技术分享·后端开发
赛博仓鼠3 小时前
Vidu Q4 Preview实测:15张参考图+3段音频驱动,2K/4K输出,国产视频生成新标杆
ai·aigc·音视频
粥里有勺糖3 小时前
Windows 搭建 ComfyUI 环境 | 小白也能直接上手
ai·经验总结·comfyui
开开心心就好4 小时前
一元二次方程求解工具,答案带详细步骤
java·服务器·开发语言·算法·pdf·ocr·perl
闭包不眠5 小时前
网页转PDF字体出错不报错,我先看文件大小
图像处理·人工智能·计算机视觉·pdf
袖清暮雨5 小时前
机器学习之其他聚类(层次聚类+DBSCAN算法)
人工智能·机器学习·ai·聚类
ofoxcoding5 小时前
Gemini TTS 与 ElevenLabs 语音合成实测:同一段视频旁白效果对比
ai·ffmpeg·音视频