minecraft-wiki-mdifier 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. minecraft_wiki_mdifier-0.1.0/.github/workflows/ci.yml +37 -0
  2. minecraft_wiki_mdifier-0.1.0/.gitignore +58 -0
  3. minecraft_wiki_mdifier-0.1.0/.pre-commit-config.yaml +12 -0
  4. minecraft_wiki_mdifier-0.1.0/LICENSE +21 -0
  5. minecraft_wiki_mdifier-0.1.0/PKG-INFO +579 -0
  6. minecraft_wiki_mdifier-0.1.0/README-en.md +404 -0
  7. minecraft_wiki_mdifier-0.1.0/README-ja.md +411 -0
  8. minecraft_wiki_mdifier-0.1.0/README.md +554 -0
  9. minecraft_wiki_mdifier-0.1.0/pyproject.toml +70 -0
  10. minecraft_wiki_mdifier-0.1.0/src/minecraft_wiki_mdifier/__init__.py +26 -0
  11. minecraft_wiki_mdifier-0.1.0/src/minecraft_wiki_mdifier/_session.py +37 -0
  12. minecraft_wiki_mdifier-0.1.0/src/minecraft_wiki_mdifier/_validators.py +26 -0
  13. minecraft_wiki_mdifier-0.1.0/src/minecraft_wiki_mdifier/cache.py +135 -0
  14. minecraft_wiki_mdifier-0.1.0/src/minecraft_wiki_mdifier/cli.py +469 -0
  15. minecraft_wiki_mdifier-0.1.0/src/minecraft_wiki_mdifier/converter.py +464 -0
  16. minecraft_wiki_mdifier-0.1.0/src/minecraft_wiki_mdifier/exceptions.py +46 -0
  17. minecraft_wiki_mdifier-0.1.0/src/minecraft_wiki_mdifier/formatters.py +81 -0
  18. minecraft_wiki_mdifier-0.1.0/src/minecraft_wiki_mdifier/lib.py +273 -0
  19. minecraft_wiki_mdifier-0.1.0/src/minecraft_wiki_mdifier/parser.py +354 -0
  20. minecraft_wiki_mdifier-0.1.0/src/minecraft_wiki_mdifier/template_expander.py +787 -0
  21. minecraft_wiki_mdifier-0.1.0/src/minecraft_wiki_mdifier/wiki.py +306 -0
  22. minecraft_wiki_mdifier-0.1.0/templates_list.txt +1748 -0
  23. minecraft_wiki_mdifier-0.1.0/tests/__init__.py +0 -0
  24. minecraft_wiki_mdifier-0.1.0/tests/conftest.py +43 -0
  25. minecraft_wiki_mdifier-0.1.0/tests/test_cache.py +97 -0
  26. minecraft_wiki_mdifier-0.1.0/tests/test_cli.py +226 -0
  27. minecraft_wiki_mdifier-0.1.0/tests/test_converter.py +241 -0
  28. minecraft_wiki_mdifier-0.1.0/tests/test_formatter.py +46 -0
  29. minecraft_wiki_mdifier-0.1.0/tests/test_lib_batch.py +132 -0
  30. minecraft_wiki_mdifier-0.1.0/tests/test_parser.py +90 -0
  31. minecraft_wiki_mdifier-0.1.0/tests/test_template_expander.py +307 -0
  32. minecraft_wiki_mdifier-0.1.0/tests/test_wiki.py +285 -0
@@ -0,0 +1,37 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.13"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+ cache: pip
24
+
25
+ - name: Install dependencies
26
+ run: |
27
+ python -m pip install --upgrade pip
28
+ pip install -e ".[dev]"
29
+
30
+ - name: Lint (ruff)
31
+ run: ruff check .
32
+
33
+ - name: Format check
34
+ run: ruff format --check .
35
+
36
+ - name: Test
37
+ run: pytest tests/ -v
@@ -0,0 +1,58 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ .venv/
25
+ venv/
26
+ ENV/
27
+ env/
28
+
29
+ # IDE
30
+ .idea/
31
+ .vscode/
32
+ *.swp
33
+ *.swo
34
+ *~
35
+ .DS_Store
36
+
37
+ # pytest
38
+ .pytest_cache/
39
+ htmlcov/
40
+ .coverage
41
+ .coverage.*
42
+ .cache
43
+ nosetests.xml
44
+ coverage.xml
45
+ *.cover
46
+
47
+ # mypy
48
+ .mypy_cache/
49
+
50
+ # 本地测试产物
51
+ *.test
52
+ *.out
53
+
54
+ # 测试 Python 缓存
55
+ tests/__pycache__/
56
+
57
+ # Claude Code 本地配置
58
+ .claude/
@@ -0,0 +1,12 @@
1
+ # pre-commit 配置:https://pre-commit.com/
2
+ # 安装:pip install pre-commit
3
+ # 启用:pre-commit install
4
+ # 手动运行:pre-commit run --all-files
5
+
6
+ repos:
7
+ - repo: https://github.com/astral-sh/ruff-pre-commit
8
+ rev: v0.15.16
9
+ hooks:
10
+ - id: ruff
11
+ args: [--fix]
12
+ - id: ruff-format
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 stone-brick
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,579 @@
1
+ Metadata-Version: 2.4
2
+ Name: minecraft-wiki-mdifier
3
+ Version: 0.1.0
4
+ Summary: 将Minecraft Wiki页面转换为AI助手易读的Markdown格式
5
+ Author: stone-brick
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Keywords: converter,markdown,minecraft,wiki
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Topic :: Text Processing :: Markup
14
+ Requires-Python: >=3.13
15
+ Requires-Dist: beautifulsoup4>=4.12.0
16
+ Requires-Dist: click>=8.1.0
17
+ Requires-Dist: markdownify>=1.2.0
18
+ Requires-Dist: requests>=2.31.0
19
+ Provides-Extra: dev
20
+ Requires-Dist: pre-commit>=3.5.0; extra == 'dev'
21
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
22
+ Requires-Dist: ruff>=0.15.0; extra == 'dev'
23
+ Requires-Dist: tqdm>=4.66.0; extra == 'dev'
24
+ Description-Content-Type: text/markdown
25
+
26
+ <div align="center">
27
+
28
+ # ⚡ Minecraft Wiki MDifier
29
+
30
+ 将 Minecraft Wiki 页面转换为 AI 助手易读的 Markdown 格式
31
+
32
+ [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
33
+ [![Python](https://img.shields.io/badge/Python-3.13+-green.svg)](https://python.org)
34
+
35
+ **[English](./README-en.md)** · **[日本語](./README-ja.md)**
36
+
37
+ </div>
38
+
39
+ ## 安装
40
+
41
+ **需要 Python >= 3.13**,依赖:`requests`, `beautifulsoup4`, `click`, `markdownify`。
42
+
43
+ ```bash
44
+ pip install -e .
45
+ ```
46
+
47
+ ### PATH 设置
48
+
49
+ `mdifier` 命令装在 Python 的 Scripts 目录。如果终端找不到命令:
50
+
51
+ **Windows (Git Bash / PowerShell)**:
52
+ ```bash
53
+ # 找到 Scripts 路径(一般输出形如 C:\Program Files\Python\Python313\Scripts)
54
+ python -c "import sysconfig; print(sysconfig.get_paths()['scripts'])"
55
+ # 临时加 PATH(替换上面输出的实际路径,Git Bash 用正斜杠)
56
+ export PATH="$PATH:/d/Program\ Files/Python/Python313/Scripts"
57
+ # 永久加:把上述加到 ~/.bashrc
58
+ ```
59
+
60
+ **macOS / Linux**:
61
+ ```bash
62
+ # 通常 pip install 会自动装到 ~/.local/bin
63
+ export PATH="$PATH:$HOME/.local/bin"
64
+ # 或:python -m mdifier.cli(跨平台等价)
65
+ ```
66
+
67
+ 验证:
68
+ ```bash
69
+ mdifier --version
70
+ # 如不行:python -m mdifier.cli --version
71
+ ```
72
+
73
+ ### 路径最佳实践(AI 助手必看)
74
+
75
+ `-o` 和 `-i` 参数的路径处理**因 shell 而异**。为避免混乱,**推荐使用相对路径**:
76
+
77
+ | 路径形式 | PowerShell | Git Bash | 推荐度 |
78
+ |----------|------------|----------|--------|
79
+ | `output/x.md`(相对) | cwd | cwd | 🥇 跨 shell 一致 |
80
+ | `D:/tests/x.md`(Windows 绝对) | D:\tests\x.md | D:\tests\x.md | 🥈 两 shell 都认 |
81
+ | `/tests/x.md`(Unix 绝对) | **D:\tests\x.md**(字面)| D:\Program Files\Git\tests(MSYS 翻译) | ❌ 行为不一致 |
82
+
83
+ **为什么 PowerShell 把 `/tests/x.md` 写到 D:\tests\?** PowerShell 不会做 MSYS 翻译,按字面理解路径,相当于 `D:\tests\x.md`(D: 是当前盘符)。
84
+
85
+ **为什么 Git Bash 把 `/tests/x.md` 翻译到 D:\Program Files\Git\tests\?** Git Bash 启动时把 `/` 映射到 Git 安装目录(MSYS 机制),这跟系统根目录不是一回事。
86
+
87
+ **推荐写法(AI 助手)**:
88
+ ```bash
89
+ # 用相对路径(自动基于当前工作目录)
90
+ cd ~/wiki && mdifier convert "铁锭" -o output/iron.md
91
+
92
+ # 批量时先建子目录
93
+ mkdir -p output && mdifier batch -t 钻石 -t 铁锭 -o output/
94
+ ```
95
+
96
+ 保存路径**会显示为绝对路径**(避免你猜测它在哪)。
97
+
98
+ ### CLI 退出码(BSD sysexits)
99
+
100
+ | 退出码 | 名称 | 含义 |
101
+ |--------|------|------|
102
+ | 0 | 成功 | 全部 OK |
103
+ | 64 (`EX_USAGE`) | 命令行参数错 | lang、--marker-format 格式 |
104
+ | 65 (`EX_DATAERR`) | 数据错 | 页面不存在、批量部分失败 |
105
+ | 70 (`EX_SOFTWARE`) | 内部软件错 | 未预期异常 |
106
+ | 74 (`EX_IOERR`) | 本地 I/O 错 | 写入文件失败、目录创建失败 |
107
+ | 75 (`EX_TEMPFAIL`) | 网络临时失败 | Wiki API 连不上(可重试)|
108
+ | 77 (`EX_NOPERM`) | 权限错 | 无写权限 |
109
+
110
+ 错误消息用 `click.secho(..., fg='red')` 染色(管道/非 tty 自动失效)。
111
+
112
+ ## 使用方式
113
+
114
+ ### CLI
115
+
116
+ ```bash
117
+ # 转换页面(中文 wiki 默认)
118
+ mdifier convert "铁锭"
119
+
120
+ # 英文 wiki
121
+ mdifier convert "Iron Ingot" --lang en -o iron.md
122
+
123
+ # 输出到文件
124
+ mdifier convert "铁锭" -o iron_ingot.md
125
+
126
+ # 完整 JSON 输出(含 templates)
127
+ mdifier convert "铁锭" --detail
128
+
129
+ # 使用 URL(自动识别语言)
130
+ mdifier convert "https://zh.minecraft.wiki/铁锭"
131
+ mdifier convert "https://minecraft.wiki/wiki/Iron_Ingot"
132
+
133
+ # 搜索页面
134
+ mdifier search "钻石"
135
+ mdifier search "diamond" --lang en
136
+ mdifier search "钻石" -n 20 # 返回结果数(默认 10)
137
+
138
+ # 批量转换:多个标题 → 独立 .md 文件
139
+ mdifier batch -t 钻石 -t 铁锭 -t 附魔台 -o ./out
140
+ mdifier batch -t Iron_Ingot -t Diamond --lang en -o ./en_out
141
+
142
+ # 批量转换:从文件读取标题列表(每行一个,# 开头为注释,空行跳过)
143
+ mdifier batch -i pages.txt -o ./out --workers 8
144
+
145
+ # 批量转换:从搜索结果中取前 N 个(--search-limit 默认 20)
146
+ mdifier batch --from-search "红石" --search-limit 30 -o ./out
147
+
148
+ # 批量:禁用进度条
149
+ mdifier batch -i pages.txt -o ./out --no-progress
150
+
151
+ # 缓存管理
152
+ mdifier cache info # 查看缓存状态
153
+ mdifier cache clear # 强制清空缓存(默认会交互确认;加 -y 跳过)
154
+ mdifier cache prune # 仅清理过期条目(保留未过期)
155
+
156
+ # 自定义模板标记(喂给不同 LLM prompt 风格)
157
+ # 格式:open/close,用单个 / 分隔,{name} 是模板类名占位符
158
+ mdifier batch -t 钻石 --marker-format '<details><summary>{name}</summary>/</details>'
159
+ mdifier batch -t Iron_Ingot --marker-format '<template:{name} start>/<template:{name} end>'
160
+ ```
161
+
162
+ 没装 pip 或找不到 `mdifier` 命令时,直接用 `python -m` 运行模块:
163
+
164
+ ```bash
165
+ python -m mdifier.cli convert "铁锭"
166
+ python -m mdifier.cli search "钻石"
167
+ ```
168
+
169
+ ### Python 库
170
+
171
+ ```python
172
+ from mdifier import convert, convert_detailed, convert_many, search, BatchConvertResult, ConvertResult
173
+
174
+ # 简单转换(中文默认)
175
+ md = convert("铁锭")
176
+ print(md)
177
+
178
+ # 英文 wiki
179
+ md_en = convert("Iron Ingot", lang="en")
180
+
181
+ # 详细模式返回 ConvertResult(带 title、source、templates)
182
+ # CLI 可用 --detail 等价:mdifier convert "铁锭" --detail
183
+ result: ConvertResult = convert_detailed("铁锭")
184
+ print(f"标题: {result.title}")
185
+ print(f"来源: {result.source}") # "api" 或 "html"
186
+ print(f"模板: {result.templates}") # 非空 dict,含所有展开后的模板数据
187
+ print(f"Markdown 长度: {len(result.markdown)}")
188
+
189
+ # 跨调用共享缓存(同一进程内多次 convert 不重复请求模板)
190
+ shared_cache = {}
191
+ convert("钻石", template_cache=shared_cache)
192
+ convert("铁锭", template_cache=shared_cache) # 共享已展开的模板
193
+
194
+ # 批量转换
195
+ result = convert_many(["钻石", "铁锭", "附魔台"], max_workers=4)
196
+ for r in result.results:
197
+ print(f"=== {r.title} ===")
198
+ print(r.markdown)
199
+ if result.failed:
200
+ print(f"失败: {result.failed}")
201
+ if result.unresolved:
202
+ print(f"未展开模板: {result.unresolved}")
203
+
204
+ # 跨语言批量:混合 URL + 纯标题,内部按 lang 分组
205
+ items = [
206
+ "钻石", # zh
207
+ "https://minecraft.wiki/wiki/Diamond", # en(URL 识别)
208
+ "Iron Ingot", # 使用 --lang 默认值
209
+ "https://zh.minecraft.wiki/wiki/工作台", # zh
210
+ ]
211
+ result = convert_many(items, lang="zh")
212
+
213
+ # 进度回调
214
+ def on_progress(done, total, title):
215
+ print(f"[{done}/{total}] {title}")
216
+ result = convert_many(["钻石", "铁锭", "附魔台"], on_progress=on_progress)
217
+
218
+ # 搜索
219
+ results = search("diamond", lang="en")
220
+ for r in results[:5]:
221
+ print(f"{r['title']}: {r['description']} ({r['url']})")
222
+ ```
223
+
224
+ ### URL 自动识别
225
+
226
+ | URL 模式 | 识别为 |
227
+ |----------|--------|
228
+ | `https://zh.minecraft.wiki/wiki/铁锭` | zh |
229
+ | `https://zh.minecraft.wiki/铁锭`(省略 `/wiki/`) | zh |
230
+ | `https://minecraft.wiki/wiki/Iron_Ingot` | en |
231
+ | `https://en.minecraft.wiki/wiki/Diamond` | en |
232
+ | `钻石`(纯标题) | 使用 `--lang` 默认值 |
233
+
234
+ ### 错误处理
235
+
236
+ ```python
237
+ # 单页 convert 抛 InvalidInputError(继承自 ValueError)
238
+ try:
239
+ md = convert("nonexistent_xyz_123")
240
+ except InvalidInputError as e:
241
+ print(f"失败: {e}") # "无法获取页面: nonexistent_xyz_123"
242
+
243
+ # 批量 convert_many 不抛,仅聚合到 result.failed
244
+ result = convert_many(["钻石", "nonexistent_xyz_123"])
245
+ for t, err in result.failed:
246
+ print(f" 失败: {t}: {err}")
247
+ # CLI 模式下 result.failed 非空时 exit code = 65 (EX_DATAERR)
248
+
249
+ # 语言不支持抛 InvalidInputError
250
+ try:
251
+ convert("X", lang="xx")
252
+ except InvalidInputError as e:
253
+ print(e) # "Unsupported language: xx. Available: ['zh', 'en']"
254
+ ```
255
+
256
+ **自定义异常层级**:
257
+
258
+ | 异常 | 父类 | 含义 |
259
+ |------|------|------|
260
+ | `MdifierError` | `Exception` | 基类 |
261
+ | `InvalidInputError` | `MdifierError`, `ValueError` | 用户输入错误 |
262
+ | `FetchError` | `MdifierError`, `requests.RequestException` | 网络错误基类 |
263
+ | `NetworkError` | `FetchError` | 连接失败/超时 |
264
+ | `WikiAPIError` | `FetchError` | API 异常结构 |
265
+ | `PageNotFoundError` | `FetchError` | 页面不存在 |
266
+ | `CacheError` | `MdifierError`, `OSError` | 缓存读写失败 |
267
+
268
+ ## 功能特点
269
+
270
+ - **双模式**:CLI(`mdifier`)+ Python 库
271
+ - **多语言支持**:内置 `zh`(zh.minecraft.wiki)、`en`(minecraft.wiki)和 `ja`(ja.minecraft.wiki)
272
+ - **批量转换**:`mdifier batch` 子命令支持 -t / -i / --from-search
273
+ - **跨语言批量**:标题列表可混合 zh/en/ja 页面,内部自动按语言分组
274
+ - **持久化模板缓存**:相同模板只请求一次,跨运行共享(**5.4x 加速**)
275
+ - **缓存管理**:`mdifier cache info/clear/prune` 子命令组
276
+ - **自动 PascalCase**:仅对全小写、无空格/连字符的纯字母名生效(如 `for` → `For`、`id table` → `Id Table`)
277
+ - **未展开报告**:批量结束时报告缺失的模板名
278
+ - **模板标记可配置**:可自定义 `<template:xxx>` 标记格式
279
+ - **批量可取消**:`MarkdownConverter.cancel()` 中断大批量任务
280
+ - **智能获取**:优先 MediaWiki API,HTML 降级抓取
281
+ - **网络重试**:HTTP GET 请求自动重试 3 次(指数退避 0.5s/1s/2s),应对瞬时 5xx/429
282
+ - **模板适配**:合成表、物品信息框、战利品表等 30+ 常见模板自动展开
283
+ - **mcui 解析**:合成台、熔炉、织布机、锻造台的图片化 UI 转语义化文本
284
+ - **颜色代码**:Minecraft `&e` `&r` 等格式代码转为 `[yellow]` `[reset]` 等语义标签
285
+ - **并发优化**:模板展开使用线程池,单页 4.6x 加速
286
+
287
+ ## 模板处理
288
+
289
+ 模板被包裹在 `<template:xxx>` 标记中,内容按格式分类型处理:
290
+
291
+ | 模板 | 输出 |
292
+ |------|------|
293
+ | `Infobox`(物品信息框) | 两列 Markdown 表格 |
294
+ | `Crafting`(合成表) | 三列:材料 / 配方 / 描述 |
295
+ | `LootChest`(战利品表) | 六列:物品 / 来源 / 数量 / 概率等 |
296
+ | `mcui`(合成台/熔炉/织布机/锻造台) | 3x3 网格文本 + 物品描述 |
297
+ | `Hatnote`、`Quote` | 用 markdownify 转为 Markdown 格式 |
298
+ | 其他未识别模板 | 通用 markdownify 转换 |
299
+ | 展开失败(API 异常) | 回退文本 `[模板名: k=v]`,标记为 `class="error"` |
300
+
301
+ **输出示例**:
302
+
303
+ ```markdown
304
+ ### 合成
305
+
306
+ <template:wikitable start>
307
+ | 材料 | 合成 配方 |
308
+ | --- | --- |
309
+ | 钻石块 | [_|_|_ / _|_|钻石块|_ / _|_|_] -> 钻石x9 |
310
+ <template:wikitable end>
311
+ ```
312
+
313
+ ## 性能与缓存
314
+
315
+ ### 缓存机制
316
+
317
+ 模板展开结果自动持久化到磁盘:
318
+
319
+ - **位置**:`~/.cache/mdifier/templates.json`(Windows: `C:\Users\<user>\.cache\mdifier\`)
320
+ - **大小**:~1 MB / 1000 模板
321
+ - **TTL**:7 天(过期自动失效)
322
+ - **共享**:跨进程、跨运行、跨项目
323
+
324
+ ### 性能数据
325
+
326
+ | 场景 | 耗时 |
327
+ |------|------|
328
+ | 首次运行(建立缓存) | ~6s |
329
+ | 二次运行(命中缓存) | ~1s |
330
+ | **加速比** | **5.4x** |
331
+
332
+ ### 自定义模板标记
333
+
334
+ 可改为 HTML `details` 等风格。`open` 和 `close` 各自可独立配置:
335
+
336
+ ```python
337
+ from mdifier.converter import MarkdownConverter
338
+
339
+ c = MarkdownConverter()
340
+ c.template_marker_open = ":::{name}"
341
+ c.template_marker_close = ":::"
342
+
343
+ # 输出示例:
344
+ # :::infobox
345
+ # ...内容...
346
+ # :::
347
+
348
+ # 也可以 HTML 风格
349
+ c.template_marker_open = "<details><summary>{name}</summary>"
350
+ c.template_marker_close = "</details>"
351
+ ```
352
+
353
+ CLI 端用 `--marker-format`(格式:`open/close`,用单个 `/` 分隔,`{name}` 是模板类名占位符):
354
+
355
+ ```bash
356
+ mdifier batch -t 钻石 --marker-format '<details><summary>{name}</summary>/</details>'
357
+ mdifier batch -i pages.txt --marker-format '<template:{name} start>/<template:{name} end>'
358
+ ```
359
+
360
+ ### 批量取消(API 用户)
361
+
362
+ 通过 `converter_factory` 参数获得 converter 引用,从其他线程调用 `cancel()`:
363
+
364
+ ```python
365
+ import threading
366
+ from mdifier import convert_many
367
+ from mdifier.converter import MarkdownConverter
368
+
369
+ c = MarkdownConverter(lang='zh')
370
+ threading.Timer(0.5, c.cancel).start()
371
+ # 0.5 秒后自动取消批量任务
372
+ convert_many(['钻石', '铁锭', '附魔台'],
373
+ converter_factory=lambda l, cache: c)
374
+
375
+ # 取消后可检查状态和未展开模板
376
+ print(c.is_cancelled()) # True
377
+ print(c.unresolved_templates) # frozenset({'HistoryTable', ...})
378
+ ```
379
+
380
+ ### 批量输出文件命名
381
+
382
+ 批量输出文件(`-o out_dir/`)按以下规则生成文件名:
383
+
384
+ - 页面标题经 `_slug()` 处理:非法文件名字符(`\ / : * ? " < > |`)→ `_`;空格→`_`;emoji 等高位 Unicode → 移除
385
+ - 标题为空时回退为 `untitled`
386
+ - 同名冲突:自动加 `-2`、`-3` 后缀,超过 999 次冲突则用 uuid 前 6 位
387
+
388
+ ### 输入文件格式(`-i`)
389
+
390
+ ```
391
+ # 注释行(# 开头)
392
+ 钻石
393
+ 铁锭
394
+ # 空行自动跳过
395
+ Iron Ingot
396
+ ```
397
+
398
+ ### 跨调用共享缓存(不持久化)
399
+
400
+ 单页 `convert` 也支持传入 `template_cache`,同一进程内多次转换共享模板展开结果:
401
+
402
+ ```python
403
+ from mdifier import convert
404
+
405
+ shared = {}
406
+ convert("钻石", template_cache=shared) # 24 条模板展开
407
+ convert("铁锭", template_cache=shared) # 增量 17 条,24 条共享
408
+ ```
409
+
410
+ **与磁盘缓存的区别**:
411
+ - `template_cache` 参数:进程内共享,不写盘
412
+ - 磁盘缓存(`~/.cache/mdifier/`):跨进程、跨运行共享
413
+ - `convert_many()` 内部使用磁盘缓存;不写单页 `convert` 的中间缓存
414
+
415
+ ### 缓存管理命令对比
416
+
417
+ | 命令 | 行为 | 适用场景 |
418
+ |------|------|----------|
419
+ | `mdifier cache info` | 显示统计(只读) | 查看缓存状态 |
420
+ | `mdifier cache clear` | 删除整个缓存文件 | wiki 大改,强制重建 |
421
+ | `mdifier cache prune` | 保留未过期(< 7 天),仅删除过期 | 日常维护 |
422
+
423
+ 注:`cache clear` 默认会交互确认(除非 `-y`)。
424
+
425
+ **Python API 等效**:
426
+
427
+ ```python
428
+ from mdifier.cache import cache_info, clear_cache, save_cache
429
+
430
+ # 手动将进程内缓存写入磁盘(convert_many 自动调用)
431
+ save_cache(my_cache_dict)
432
+
433
+ # 等价于 cache clear
434
+ clear_cache()
435
+ ```
436
+
437
+ ### `cache_info()` 返回字段
438
+
439
+ | 字段 | 类型 | 含义 |
440
+ |------|------|------|
441
+ | `path` | str | 缓存文件路径 |
442
+ | `exists` | bool | 是否存在 |
443
+ | `size_bytes` | int | 字节数 |
444
+ | `size_mb` | float | MB(保留 2 位小数)|
445
+ | `entries` | int | 总条目数 |
446
+ | `fresh_entries` | int | 未过期条目数 |
447
+ | `expired_entries` | int | 已过期条目数 |
448
+ | `oldest_ts` | str | 最早时间戳(ISO 格式)|
449
+ | `newest_ts` | str | 最新时间戳(ISO 格式)|
450
+
451
+ Python 等价:
452
+
453
+ ```python
454
+ from mdifier.cache import cache_info, clear_cache
455
+
456
+ info = cache_info()
457
+ if info["exists"] and info["size_mb"] > 100:
458
+ clear_cache() # 清理
459
+ ```
460
+
461
+ ## 项目结构
462
+
463
+ ```
464
+ src/mdifier/
465
+ ├── __init__.py # 包初始化,导出 convert/convert_detailed/convert_many/search
466
+ ├── lib.py # 库模式 API(含 convert_many)
467
+ ├── cli.py # CLI 入口(click,含 batch/cache 子命令)
468
+ ├── convert.py # 独立转换脚本(命令行直接运行)
469
+ ├── search.py # 独立搜索脚本
470
+ ├── wiki.py # MediaWiki API 获取 + HTML 降级
471
+ ├── parser.py # Wikitext 解析器(模板/链接/标题)
472
+ ├── template_expander.py # 模板展开:action=bucket 或 action=parse + 格式检测
473
+ ├── formatters.py # Minecraft 颜色代码 → 语义化标签
474
+ ├── converter.py # Markdown 生成:dict dispatch 渲染
475
+ └── cache.py # 模板展开缓存持久化
476
+ ```
477
+
478
+ ### 数据流
479
+
480
+ 1. `WikiFetcher` → MediaWiki API 获取 wikitext
481
+ 2. `WikiParser` → 解析 AST,提取模板到 `templates` 字典
482
+ 3. `TemplateExpander` → 对 Lua 数据查询模板(Trade uses 等)优先尝试 `action=bucket`,失败则降级到 `action=parse`
483
+ 4. `MarkdownConverter` → 按格式分发到对应渲染器,生成最终 Markdown
484
+ 5. 跨运行:`get_or_load_persistent_cache()` 模块级单例懒加载磁盘缓存;批量结束仅 `save_cache()` 一次
485
+
486
+ ## 开发
487
+
488
+ ### 安装开发依赖
489
+
490
+ ```bash
491
+ pip install -e ".[dev]"
492
+ pre-commit install
493
+ ```
494
+
495
+ ### 运行 ruff 检查
496
+
497
+ ```bash
498
+ ruff check .
499
+ ```
500
+
501
+ 或通过 pre-commit 自动触发(提交时自动运行):
502
+
503
+ ```bash
504
+ pre-commit run --all-files
505
+ ```
506
+
507
+ ### 手动测试
508
+
509
+ ```bash
510
+ # 转换单页
511
+ mdifier convert "钻石" -o diamond.md
512
+
513
+ # 多页批量
514
+ for page in 钻石 铁锭 附魔台; do
515
+ mdifier convert "$page" -o "${page}.md"
516
+ done
517
+
518
+ # 缓存管理
519
+ mdifier cache info
520
+ mdifier cache clear -y
521
+ ```
522
+
523
+ ## 高级选项
524
+
525
+ ### `MarkdownConverter` 构造参数
526
+
527
+ ```python
528
+ from mdifier.converter import MarkdownConverter
529
+
530
+ c = MarkdownConverter(
531
+ lang="zh", # 语言:zh / en / ja
532
+ max_workers=10, # 模板展开线程池大小
533
+ template_cache={}, # 跨调用共享缓存(None 则新建)
534
+ use_persistent_cache=True, # 是否加载磁盘缓存(默认 True)
535
+ )
536
+ ```
537
+
538
+ ### `BatchConvertResult.results` 顺序说明
539
+
540
+ `result.results` **仅含成功项**,顺序与输入**不严格一致**(`convert_many` 按 lang 分组、按 future 完成顺序填充)。如需保持输入顺序,可自行构建 `dict[title, result]`:
541
+
542
+ ```python
543
+ result = convert_many(["钻石", "铁锭", "附魔台"], lang="zh")
544
+ by_title = {r.title: r for r in result.results}
545
+ md_diamond = by_title.get("钻石")
546
+ ```
547
+
548
+ ### `MinecraftColorFormatter` 独立 API
549
+
550
+ 低层颜色规范类,可独立使用或子类化:
551
+
552
+ ```python
553
+ from mdifier.formatters import MinecraftColorFormatter
554
+
555
+ f = MinecraftColorFormatter()
556
+ md_text = f.clean("&e黄色&r重置") # '[yellow]黄色[reset]重置'
557
+
558
+ # 自定义颜色规范(未来扩展)
559
+ class HtmlColorFormatter(MinecraftColorFormatter):
560
+ COLORS = {"red": "#ff0000", "blue": "#0000ff"}
561
+ # ...
562
+ ```
563
+
564
+ ## 依赖
565
+
566
+ ### 必需
567
+
568
+ - `requests` — MediaWiki API HTTP 客户端
569
+ - `beautifulsoup4` — HTML 解析
570
+ - `click` — CLI 框架
571
+ - `markdownify` — 通用 HTML → Markdown 转换
572
+
573
+ ### 可选
574
+
575
+ - `tqdm` — `mdifier batch` 进度条;缺则降级为 stderr 文本
576
+
577
+ ## License
578
+
579
+ MIT