KmoeFix — Kmoe EPUB 顺序与脏标签一键修复 / One-Click Fix for Kmoe EPUB Ordering & Dirty Tags
8000 div>修复 Kmoe 导出 EPUB 乱序与脏标签的一键工具 — 按
第X话重排章节、重命名资源、清除冗余标签,输出符合规范的*_修正版.epub。
English | 中文
KmoeFix v1.0.0 是一款面向 Kmoe 漫画 EPUB 的离线修复工具。
Kmoe 导出的 EPUB 常见两类问题:
- 章节乱序 —
vol.opf中<spine>的<itemref>顺序与实际话数不一致,html/page-*.html与image/*.jpg文件名也未按话数对齐,导致阅读器(尤其按 spine 顺序渲染的阅读器)出现“第 3 话在第 1 话前面”等错乱。 - 脏标签残留 — HTML 中残留
kmoetag/kimageraw/raw等自定义属性,影响校验、增加体积、部分阅读器解析异常;mimetype未按 EPUB 规范以ZIP_STORED置于 ZIP 首条,部分严格校验器直接报错。
KmoeFix 的做法是离线重建 ZIP:解析每个 HTML 的 <title>第X话</title> 提取话数,按话数重排 spine、重命名 html/ 与 image/ 资源、同步重写 vol.opf / xml/vol.nav 引用、剥离脏属性,并以规范的 ZIP 结构回写,最后回读校验确保页序连续才落盘。
适用对象:从 Kmoe 批量导出后需要本地整理、再用 NeeView / 其它 EPUB 阅读器阅读的用户。
- 按话数智能重排 — 正则
TITLE_RE = <title>\s*第\s*(\d+)\s*话</title>主匹配 +TITLE_FALLBACK兜底,cover.html固定为 0、theend.html自动置尾,缺失话数也会按实际最大值补齐宽度。 - 资源零填充重命名 — 统一重命名为
html/page-001.html/image/001.jpg(宽度max(3, len(str(max_page)))),cover.html/theend保持特殊命名,避免排序歧义。 - 脏标签一键清除 — 正则剥离
kmoetag="..."与kimageraw="..."/raw="...",同步修正 HTML 内src="../image/..."引用。 - EPUB 规范重打包 —
mimetype始终为 ZIP 第一条且ZIP_STORED不压缩,其余条目ZIP_DEFLATED;同步重写vol.opf的<manifest>与xml/vol.nav的src。 - 回读 spine 校验 + 防覆盖 — 写后立即回读
vol.opf解析 spine 页码,校验nums == [1..max],失败则删除临时文件并抛出回读校验失败;输出经get_unique_dst()自动生成*_修正版.epub/*_修正版 (1).epub,永不覆盖原文件。 - GUI 拖拽批量 + CLI 批量 + NeeView 联动 — Tkinter +
tkinterdnd2拖拽ZIP/EPUB/CBZ批量处理,后台threading + queue不卡界面;支持 CLI 批量与“完成后用 NeeView 打开最后一项”。
以一个 3 话样本为例,修复前后 vol.opf 的关键差异:
修复前(Kmoe 原始导出,spine 乱序)
<!-- vol.opf -->
<manifest>
<item id="p3" href="html/page-003.html" media-type="application/xhtml+xml" />
<item id="p1" href="html/page-001.html" media-type="application/xhtml+xml" />
<item id="p2" href="html/page-002.html" media-type="application/xhtml+xml" />
<item id="img3" href="image/003.jpg" media-type="image/jpeg" />
<item id="img1" href="image/001.jpg" media-type="image/jpeg" />
</manifest>
<spine toc="ncx">
<itemref idref="p3" />
<itemref idref="p1" />
<itemref idref="p2" />
</spine><!-- html/page-003.html 片段 -->
<title>第3话</title>
<img src="../image/003.jpg" kmoetag="xxx" kimageraw="xxx" />ZIP 结构:mimetype 非首条或被压缩,部分阅读器报 mimetype must be first and uncompressed。
修复后(KmoeFix 输出 *_修正版.epub)
<!-- vol.opf -->
<manifest>
<item id="p1" href="html/page-001.html" media-type="application/xhtml+xml" />
<item id="p2" href="html/page-002.html" media-type="application/xhtml+xml" />
<item id="p3" href="html/page-003.html" media-type="application/xhtml+xml" />
<item id="img1" href="image/001.jpg" media-type="image/jpeg" />
<item id="img2" href="image/002.jpg" media-type="image/jpeg" />
<item id="img3" href="image/003.jpg" media-type="image/jpeg" />
</manifest>
<spine toc="ncx">
<itemref idref="p1" />
<itemref idref="p2" />
<itemref idref="p3" />
</spine><!-- html/page-001.html 片段 -->
<title>第1话</title>
<img src="../image/001.jpg" />ZIP 结构(zipinfo):
mimetype ZIP_STORED (首条,未压缩)
vol.opf ZIP_DEFLATED
xml/vol.nav ZIP_DEFLATED
html/page-001.html ZIP_DEFLATED
html/page-002.html ZIP_DEFLATED
image/001.jpg ZIP_DEFLATED
...
校验逻辑:回读
vol.opf→ 提取 spine 对应 HTML 的<title>页码 → 断言nums == list(range(1, max+1)),否则回滚。
- 前往 Releases 下载
KmoeFix.exe(约 12 MB,PyInstaller 单文件,Windows x64)。 - 双击运行即可,无需安装 Python。
提示:首次运行若被 Windows SmartScreen 拦截,点击“更多信息 → 仍要运行”。
要求:Python 3.10+,Windows 10/11 推荐。
# 1. 克隆仓库
git clone https://github.com/KmoeFix/KmoeFix.git
cd KmoeFix
# 2. (可选)创建虚拟环境
python -m venv .venv
# Windows PowerShell
.venv\Scripts\Activate.ps1
# Windows CMD
.venv\Scripts\activate.bat
# 3. 安装依赖
pip install -r requirements.txt
# 或
pip install -e .
# 4. 启动 GUI
python -m src.kmoe_fix
# 安装后也可用命令
kmoefix依赖仅一项(见 requirements.txt / pyproject.toml):
tkinterdnd2>=0.1.3
其余均为标准库:zipfile / re / os / sys / json / threading / queue / subprocess / tkinter。
- 运行
KmoeFix.exe或python -m src.kmoe_fix打开主窗口(720×560)。 - 拖拽
*.zip/*.epub/*.cbz文件或整个文件夹到列表;或点击“添加文件…”选择。 - 可选勾选“完成后用 NeeView 打开(仅打开最后成功项)”并在下方填入
NeeView.exe路径(支持“浏览…”)。 - 点击 开始处理,日志区实时显示
▶ 处理 / ✔ 完成 / ✘ 失败,进度条滚动;处理在后台线程执行,界面不卡死。 - 输出位于原文件同目录,命名
原名_修正版.epub(已存在则原名_修正版 (1).epub),原文件不变。
输入: D:\Manga\某漫画.epub
输出: D:\Manga\某漫画_修正版.epub
配置会持久化到 kmoe_fix_config.json(src/ 或 exe 同级,字段 neeview_path / open_with_neeview,见 src/config.py)。
# 单文件
python -m src.kmoe_fix "D:\Manga\某漫画.epub"
# 多文件批量
python -m src.kmoe_fix a.epub b.epub "C:\Manga\*.epub"
# 通配(PowerShell)
python -m src.kmoe_fix (Get-ChildItem D:\Manga\*.epub | ForEach-Object FullName)输出:
[OK] D:\Manga\某漫画.epub -> D:\Manga\某漫画_修正版.epub
[FAIL] D:\Manga\broken.epub: 未找到 vol.opf
Windows GBK 控制台已做
_safe_print容错,不会因✔/✘乱码崩溃。
- GUI 中勾选后,处理完成会自动
subprocess.Popen([neeview_path, last_ok])打开最后成功的一项。 - 路径不存在时会弹窗确认是否仍继续(仅完成处理,不启动)。
- CLI 模式不自动启动 NeeView(仅 GUI 支持)。
- 手动编辑配置:打开
kmoe_fix_config.json:
{
"neeview_path": "C:\\Tools\\NeeView\\NeeView.exe",
"open_with_neeview": false
}从源码本地构建与 Release 一致的 dist/KmoeFix.exe:
pip install pyinstaller==6.12 tkinterdnd2
pyinstaller KmoeFix.spec
# 或
pyinstaller --clean --noconfirm KmoeFix.spec产物:dist/KmoeFix.exe(onefile + windowed,console=False,hiddenimports=tkinterdnd2)。
KmoeFix.spec 要点(见文件头部注释):
# 打包入口 src/kmoe_fix.py,图标自动取 assets/icon.ico(若存在)
a = Analysis(["src/kmoe_fix.py"], hiddenimports=["tkinterdnd2"], ...)
exe = EXE(pyz, a.scripts, a.binaries, a.datas, name="KmoeFix",
console=False, upx=True, icon=icon)pyproject.toml 已配置 kmoefix = "src.kmoe_fix:run_gui",pip install -e . 后可直接 kmoefix 启动。
KmoeFix/ # 仓库根,Public
├── src/
│ ├── __init__.py # 包标识
│ ├── config.py # 配置:APP_NAME/VERSION/OUT_SUFFIX,
│ │ # get_config_path/load_config/save_config
│ ├── core.py # 核心:fix_one(src, dst, log) / get_unique_dst
│ │ # TITLE_RE / MANIFEST_RE / SPINE_RE 等正则
│ ├── gui.py # 界面:run_gui() — Tkinter + tkinterdnd2
│ └── kmoe_fix.py # 入口 shim:GUI 与 CLI 分发,兼容 from src.kmoe_fix import *
├── tests/
│ └── test_fix_one.py # 单测:sorted/shuffled/get_unique_dst/shim
├── assets/
│ └── icon.ico # (可选)EXE 图标
├── KmoeFix.spec # PyInstaller 6.12 单文件打包配置
├── pyproject.toml # 项目元数据 + ruff/black 配置
├── requirements.txt # tkinterdnd2>=0.1.3
├── LICENSE # MIT
├── README.md # 本文
└── KmoeFix开源到GitHub分步计划.md # 开发与发布分步计划
核心调用链:
from src.core import fix_one
# 最简调用
out = fix_one("input.epub") # 自动生成 *_修正版.epub
out = fix_one("input.epub", dst="out.epub", log=print)这是回读校验触发的保护:输入本身的 <title> 页码不连续或标题缺失导致重排后仍非 1..N 连续。KmoeFix 会删除临时文件并回滚,不会污染输出。请检查原 EPUB 是否为 Kmoe 导出、是否混入非漫画 HTML(如广告页无 第X话 标题),或用解压工具查看 html/*.html 的 <title> 是否缺失。确认无误后可尝试重新导出再修复。
PyInstaller 的 onefile 单文件 EXE 因自解压行为易被启发式误报,属常见误报。可:① 在 Release 页核对 SHA256;② 将 KmoeFix.exe 加入白名单;③ 自行源码构建 pyinstaller KmoeFix.spec 得到本机可信 EXE;④ 改用 pyinstaller --onedir 目录模式分发(需自行修改 spec 的 console/onefile 配置)。
不会。项目全程 UTF-8(src/*.py 含 # -*- coding: utf-8 -*-,读写均 encoding="utf-8"),第X话 / _修正版 等已做 UTF-8 还原。但需注意:① 控制台为 GBK 时日志已做 _safe_print 容错;② 勿用 GBK 编辑器保存源码,否则中文会再次乱码;③ 输出路径经 get_unique_dst 处理,中文与空格均支持。
- 拖拽无反应:
tkinterdnd2未安装或版本过低,执行pip install -U tkinterdnd2后重试;文件夹拖拽仅扫描一层*.zip/*.epub/*.cbz,深层嵌套请直接拖文件。 - 未找到 vol.opf:输入非 Kmoe 结构 EPUB(Kmoe 固定含
vol.opf或*/vol.opf),请确认文件来源。 - spine 为空:
vol.opf中<spine>无<itemref>,属文件损坏或非标准 EPUB,无法修复。
更多问题请提 Issue,附上脱敏后的 vol.opf 片段与日志即可。
欢迎 Issue / PR!
# 开发流程
git clone https://github.com/KmoeFix/KmoeFix.git
cd KmoeFix
pip install -r requirements.txt
# 代码风格:ruff + black,行宽 100,py310
# 已在 pyproject.toml 配置 [tool.ruff] / [tool.black]
pip install ruff black
ruff check src/
black --check src/
# 单测(pytest 可选,仓库提供最小 stub 兼容)
python -m pytest tests/ -v
# 或直接
python -m py_compile src/*.py提交前请确保:
- 全程 UTF-8,勿引入 GBK;
- 不提交含版权的 EPUB 原文件,测试用
tests/内脚本生成的最小 ZIP; DEFAULT_NEEVIEW保持"",不写死个人路径;- 涉及
fix_one行为变更需同步更新“效果对比”与 FAQ。
详见 CONTRIBUTING.md / CODE_OF_CONDUCT.md(如有)。
MIT License — Copyright (c) 2026 KmoeFix Contributors
可自由使用、复制、修改、合并、发布、分发、再许可及销售本软件副本,需在副本中保留上述版权与许可声明。软件按“现状”提供,不附任何明示或暗示保证。
- Kmoe 漫画工具的原始导出能力
- NeeView — 轻量高性能的漫画/图片浏览器
- tkinterdnd2 — 让 Tkinter 支持文件拖拽
- PyInstaller 6.12 — 将 Python 打包为单文件 EXE
- 所有测试 EPUB 脱敏样本的贡献者
详细开发与发布流程见 KmoeFix开源到GitHub分步计划.md。