dsh-convert-core 0.1.0-alpha.6 → 0.1.0-alpha.7

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 (102) hide show
  1. package/README.md +34 -16
  2. package/bin/media-to-md.mjs +5 -5
  3. package/bin/tita-convert.mjs +1 -1
  4. package/docs/media-to-md.md +41 -16
  5. package/lib/adapters/iddoc.d.ts +3 -1
  6. package/lib/adapters/iddoc.d.ts.map +1 -1
  7. package/lib/adapters/iddoc.js +18 -6
  8. package/lib/adapters/iddoc.js.map +1 -1
  9. package/lib/adapters/index.d.ts +12 -5
  10. package/lib/adapters/index.d.ts.map +1 -1
  11. package/lib/adapters/index.js +14 -10
  12. package/lib/adapters/index.js.map +1 -1
  13. package/lib/adapters/scanned.d.ts +14 -6
  14. package/lib/adapters/scanned.d.ts.map +1 -1
  15. package/lib/adapters/scanned.js +30 -12
  16. package/lib/adapters/scanned.js.map +1 -1
  17. package/lib/adapters/types.d.ts +1 -1
  18. package/lib/adapters/types.d.ts.map +1 -1
  19. package/lib/adapters/web.d.ts +9 -12
  20. package/lib/adapters/web.d.ts.map +1 -1
  21. package/lib/adapters/web.js +30 -53
  22. package/lib/adapters/web.js.map +1 -1
  23. package/lib/contract.d.ts +55 -4
  24. package/lib/contract.d.ts.map +1 -1
  25. package/lib/contract.js +9 -2
  26. package/lib/contract.js.map +1 -1
  27. package/lib/http.d.ts.map +1 -1
  28. package/lib/http.js +85 -13
  29. package/lib/http.js.map +1 -1
  30. package/lib/media/convert.d.ts.map +1 -1
  31. package/lib/media/convert.js +68 -19
  32. package/lib/media/convert.js.map +1 -1
  33. package/lib/media/index.d.ts +2 -2
  34. package/lib/media/index.d.ts.map +1 -1
  35. package/lib/media/index.js +1 -1
  36. package/lib/media/index.js.map +1 -1
  37. package/lib/media/markdown.d.ts +20 -2
  38. package/lib/media/markdown.d.ts.map +1 -1
  39. package/lib/media/markdown.js +31 -5
  40. package/lib/media/markdown.js.map +1 -1
  41. package/lib/media/models.d.ts +67 -2
  42. package/lib/media/models.d.ts.map +1 -1
  43. package/lib/media/models.js +221 -13
  44. package/lib/media/models.js.map +1 -1
  45. package/lib/media/scanned.d.ts +35 -7
  46. package/lib/media/scanned.d.ts.map +1 -1
  47. package/lib/media/scanned.js +81 -38
  48. package/lib/media/scanned.js.map +1 -1
  49. package/lib/media/types.d.ts +56 -11
  50. package/lib/media/types.d.ts.map +1 -1
  51. package/lib/media/types.js.map +1 -1
  52. package/lib/service.d.ts +140 -8
  53. package/lib/service.d.ts.map +1 -1
  54. package/lib/service.js +294 -30
  55. package/lib/service.js.map +1 -1
  56. package/lib/web/convert.d.ts +11 -14
  57. package/lib/web/convert.d.ts.map +1 -1
  58. package/lib/web/convert.js +26 -125
  59. package/lib/web/convert.js.map +1 -1
  60. package/lib/web/fetch.js +1 -1
  61. package/lib/web/fetch.js.map +1 -1
  62. package/lib/web/index.d.ts +3 -1
  63. package/lib/web/index.d.ts.map +1 -1
  64. package/lib/web/index.js +3 -1
  65. package/lib/web/index.js.map +1 -1
  66. package/lib/web/types.d.ts +10 -22
  67. package/lib/web/types.d.ts.map +1 -1
  68. package/lib/web/types.js +3 -1
  69. package/lib/web/types.js.map +1 -1
  70. package/lib/web/url.d.ts +6 -0
  71. package/lib/web/url.d.ts.map +1 -1
  72. package/lib/web/url.js +7 -1
  73. package/lib/web/url.js.map +1 -1
  74. package/package.json +10 -6
  75. package/scripts/__pycache__/asr.cpython-312.pyc +0 -0
  76. package/scripts/asr.py +157 -10
  77. package/scripts/pdf_pages.py +102 -12
  78. package/src/adapters/iddoc.ts +18 -6
  79. package/src/adapters/index.ts +14 -9
  80. package/src/adapters/scanned.ts +30 -12
  81. package/src/adapters/types.ts +1 -1
  82. package/src/adapters/web.ts +32 -53
  83. package/src/contract.ts +56 -4
  84. package/src/http.ts +82 -11
  85. package/src/media/convert.ts +67 -21
  86. package/src/media/index.ts +17 -1
  87. package/src/media/markdown.ts +34 -6
  88. package/src/media/models.ts +229 -14
  89. package/src/media/scanned.ts +107 -40
  90. package/src/media/types.ts +58 -11
  91. package/src/service.ts +343 -31
  92. package/src/web/convert.ts +26 -138
  93. package/src/web/fetch.ts +2 -2
  94. package/src/web/index.ts +3 -1
  95. package/src/web/types.ts +10 -22
  96. package/src/web/url.ts +7 -1
  97. package/web/app.js +50 -389
  98. package/web/index.html +14 -212
  99. package/web/styles.css +248 -235
  100. package/scripts/vendor.mjs +0 -76
  101. package/web/vendor/icons.js +0 -25
  102. package/web/vendor/vue.global.prod.js +0 -14
package/README.md CHANGED
@@ -1,10 +1,14 @@
1
1
  # dsh-convert-core
2
2
 
3
3
  转换**内核**:把 Office / OpenDocument / RTF / EPUB / CSV / 文本层 PDF、音视频与图片、
4
- 扫描件、**网页链接(含微信公众号文章)** 转成 **知识就绪 Markdown + 本地图片**,
5
- 自带 HTTP API 和一个 Vue 3(Options API)可视化控制台。
4
+ 混合层 PDF、**另存下来的网页(含微信公众号文章)** 转成 **知识就绪 Markdown + 本地图片**,
5
+ 自带 HTTP API 和一个 React(`@tita-data-agent/ui`)可视化控制台。
6
6
  它可以**独立运行**,不依赖 DSH。
7
7
 
8
+ **单一职责:只做转换,不联网取内容。** 输入是磁盘上的一份文件(或一段内联文本),输出是
9
+ Markdown。需要浏览器才能读的页面(风控、验证码、JS 渲染、登录态)由宿主用自己的浏览器读下来
10
+ 另存为 `.html`,再按文件交给内核 —— 内核**没有**「粘一个网址让它去抓」这条路。
11
+
8
12
  ```bash
9
13
  npm install dsh-convert-core
10
14
  npx tita-convert --port 8787
@@ -59,17 +63,23 @@ const handler = createRequestHandler({ service, basePath: '/convert' })
59
63
  ## 产物契约
60
64
 
61
65
  ```
62
- convert-out/<taskId>/
66
+ <outRoot>/<YYYY-MM-DD>/<中文名>/ # 日期层由 bucketByDay 打开;叶子名见 deliveryDirName()
63
67
  index.md front matter + 正文,图片引用为 assets/img-<sha1_12>.<ext>
64
68
  assets/ 内容寻址的本地图片
65
69
  source/ 原始输入(0048):抓下来的 HTML、上传的那份文件
66
70
  manifest.json 来源、引擎与格式、warnings、content_hash、ingest 负载
67
71
  ```
68
72
 
73
+ 交付目录是**用户会打开**的东西,所以叶子名是文档的中文名(`output/2026-10-02/月度销售汇总/`),
74
+ 不是 `conv_<时间戳>_<随机>`:名字取标题,没有标题就用文件名(去掉扩展名),最多 25 个字,
75
+ 同一天同名让路成 `-2`、`-3`。让路结果记在任务的 `deliveryName` 上,所以重试与重启之后算出来的
76
+ 都是同一个目录;那份记录早于这个字段时回退到 task id。**工作目录不跟着改名**(仍按 task id):
77
+ 工地只要唯一,成品的位置要稳定。
78
+
69
79
  **`source/` 是底稿,不是资产**(0048)。抽取是有损的(正文外的元素、内嵌音视频都不在
70
80
  产物里),而工作目录在交付成功那一刻就被删掉 —— 不留底稿,事后想换个抽取规则重来一遍就
71
- 没有素材了。网页那档由适配器交出**真正被解析的那份 HTML**(`kind=url` 是抓下来的页面,
72
- `kind=file` 是读进来的文件);其他档由内核把暂存输入复制过来。`manifest.original` 记它的
81
+ 没有素材了。网页那档由适配器交出**真正被解析的那份 HTML**(就是宿主交进来的那份 `.html`);
82
+ 其他档由内核把暂存输入复制过来。`manifest.original` 记它的
73
83
  `rel` / `bytes` / `sha256`,`manifest.ingest.original` 让知识层也能记住它。内联文本没有独立
74
84
  底稿(`index.md` 就是它),所以不写这个键,而不是造一个空的。
75
85
 
@@ -87,14 +97,12 @@ source_type_source / source_type_candidates` 由内核如实填写。
87
97
  ## 范围
88
98
 
89
99
  文件类:14 种文档格式(含文本层 PDF)、本地音视频与图片、扫描件轻量 OCR、另存的页面(`.html/.htm`)。
90
- **网页(`kind=url` 链接)已落地**:静态抓取 + 正文抽取,公众号文章有专用路径。
91
100
  还没做:内核内的浏览器档(Playwright / CDP)、在线视频字幕/转写、版面/表格/公式级的高质量扫描档
92
101
  (docling / MinerU)。
93
102
 
94
- **风控墙怎么办**:静态抓取会被「需要真浏览器」的墙挡下(公众号重复访问会跳验证码页,
95
- `WECHAT_RATE_LIMITED`)。这时**不要**去骗验证码,而是换一条路:在浏览器里正常打开这一页
96
- (验证由用户自己的浏览器完成),另存为 HTML 后按 `.html` 提交 —— 本档会从 `og:url` 把原始
97
- 地址读回来,产物与直抓完全一致。
103
+ **抓取不在本包范围**(原 `kind=url` 已删除)。需要真浏览器才能读的页面(公众号重复访问会跳
104
+ 验证码页这类风控墙)本来就不是静态抓取能解决的,而宿主手里有真浏览器 —— 让它读下来另存为
105
+ `.html`,再按文件提交。本档会从 `og:url` / `canonical` 把原始地址读回来,产物与直抓一致。
98
106
 
99
107
  Agent 侧的同一件事已经写成 skill:**`browser-page-to-markdown`** —— 用 Kimi WebBridge 驱动
100
108
  用户的真实浏览器读一次页面、把渲染后的 HTML 落到本地,再交给本档(`kind=file` 的 `.html`)。
@@ -112,7 +120,7 @@ Agent 侧的同一件事已经写成 skill:**`browser-page-to-markdown`** —
112
120
  | 输入 | 适配器 | 说明 |
113
121
  | --- | --- | --- |
114
122
  | 14 种文档格式(含文本层 PDF) | `anydoc` | 见上;内嵌图片落地到 `assets/` |
115
- | 网页 / 微信公众号文章(`kind=url` 的链接,或 `kind=file` 的 `.html/.htm`) | `web` | 能力来自内核自带的网页管线 `src/web/`:抓取(或直接读一份已在手上的 HTML)→ 正文抽取(公众号走 `#js_content`,其他站点走 Readability)→ 图片按内容 sha1 落地 `assets/` → turndown 出 GFM Markdown。**只抓用户给的这一个地址、不跟进页面里的链接**;正文图片引用一律改成 `assets/…`,远程地址不会留在产物里。另存的 HTML 会从页面自带的 `og:url` / `canonical` 里把原始地址读回来(相对地址、`source_url`、幂等身份都靠它) |
123
+ | 另存下来的网页 / 微信公众号文章(`kind=file` 的 `.html/.htm`) | `web` | 能力来自内核自带的网页管线 `src/web/`:读入一份**已在手上**的 HTML → 正文抽取(公众号走 `#js_content`,其他站点走 Readability)→ 图片按内容 sha1 落地 `assets/` → turndown 出 GFM Markdown。**不抓页面、不跟进链接**(唯一的网络请求是正文图片);正文图片引用一律改成 `assets/…`,远程地址不会留在产物里。原始地址从页面自带的 `og:url` / `canonical` 读回来(相对地址、`source_url`、幂等身份都靠它) |
116
124
  | 音视频(`.mp4 .mov .mkv .webm …`、`.mp3 .m4a .wav …`)与图片(`.png .jpg .webp …`) | `iddoc` | 能力来自内核自带的本地媒体管线 [`src/media/`](docs/media-to-md.md):**字幕优先**,无字幕时才本地转写(默认**不**下载模型,`IDDOC_ASR=1` 才允许);图片默认只收录不做 OCR;视频关键帧(均匀采样 + 场景变化 + 内容去重)落地到 `assets/` |
117
125
  | 扫描件 / 纯图片页 PDF | `scanned` | 同一包的 `convertScannedDocument()`:`pypdfium2` 逐页渲染 + 本地 `RapidOCR` 识别,`page-0001.png …` 落地为 assets。默认作为 `anydoc` 报 `needsOcr` 时的**自动兜底**;`SCANNED_OCR=1` 是「跳过 anydoc 直接走 OCR」的显式出口。不做版面还原 |
118
126
  | `.md .markdown .txt .json .tsv .yml .log .adoc .rst .org .tex` | `text` | 直通 + 契约归一化(重写 front matter)|
@@ -159,12 +167,22 @@ curl -X POST "localhost:8787/api/tasks?kind=file" -F "file=@spec.docx"
159
167
 
160
168
  ## 控制台
161
169
 
162
- `web/index.html` + `web/app.js`,**Vue 3 Options API、零构建**:`vue.global.prod.js` 与
163
- lucide 图标集由 `scripts/vendor.mjs` 在构建时从 npm 包拷成静态资源(图标以纯 SVG map
164
- 注入,避免 `createIcons()` 在 Vue 背后替换 DOM 节点)。
170
+ `web/index.html` + `web/app.js` + `web/styles.css`,由 `build-console.mjs` 用 esbuild
171
+ 从 `console/` 打出来。界面是 React + `@tita-data-agent/ui`(仓库共享的 shadcn 底座),
172
+ 样式表直接拷 `packages/ui` 编译出的那一份 —— 控制台和三个插件穿同一套视觉,不再各造一套。
173
+
174
+ **运行时仍然零构建**:内核只负责把 `web/` 下这三个静态文件发出去,`npx tita-convert`
175
+ 不需要打包器;打包器只在**构建期**存在,产物自包含。代价是改控制台的类名之后要重编
176
+ `packages/ui`(`@source` 清单在那里),这也是三个插件一直以来的做法。
177
+
178
+ 界面结构是**两个视图**(右上角切换):
165
179
 
166
- 之所以不用 SPA 工具链:内核必须能被 `npx tita-convert` 直接拉起,并且
167
- DSH 插件要把它当静态页原样挂载。代价是没有 shadcn/Tailwind。
180
+ - **转换** —— 一个输入框收文件(拖进来,或点「浏览文件」)、
181
+ 依赖项清单(ffmpeg / anydoc / uvx / readability / whisper,名字 + 版本 + 它负责什么)、
182
+ 支持的格式(按谁负责它分组,共 72 种)。
183
+ - **任务列表** —— 四列的表:标题(第二行是它出了什么事)| 状态 | 时间 | 操作。
184
+ 失败的出路是行内一枚按钮,完整建议收进展开的「说明」活页。停下来的一行可以删除
185
+ (记录 + 交付目录一起删,删前确认)。
168
186
 
169
187
  ## 设计取舍
170
188
 
@@ -51,16 +51,16 @@ function usage() {
51
51
  ' --mode <档位> auto | subtitle-only | transcribe | frames-only(默认 auto)',
52
52
  ' --model <name|path> whisper 模型(默认 small;中文建议 large-v3-turbo;也可是本地模型目录)',
53
53
  ' --lang <code> 语言(默认自动检测;中文建议 zh)',
54
- ' --allow-model-download 允许在无字幕时下载转写模型(默认不允许,会直接报错并告知体积)',
54
+ ' --allow-model-download 允许在无字幕时下载转写模型(默认不允许:auto 档就不出逐字稿,transcribe 档直接报错并告知体积)',
55
55
  ' --samples <n> 均匀采样帧数(默认 12)',
56
56
  ' --max-frames <n> 关键帧上限(默认 40)',
57
57
  ' --scene <0..1> 场景切换阈值(默认 0.3)',
58
58
  ' --ocr-frames 对关键帧做画面文字 OCR(默认关闭)',
59
59
  ' --image-ocr 对图片输入做 OCR(默认关闭,图片直接收录)',
60
- ' --scanned-pdf 走扫描档:纯图片页 PDF → 逐页本地 OCR(默认关闭)',
61
- ' --dpi <n> 扫描档渲染 DPI(默认 150)',
62
- ' --max-pages <n> 扫描档最多处理页数(默认 50)',
63
- ' --no-page-images 扫描档不落地页图,只保留文字',
60
+ ' --scanned-pdf PDF 逐页混合档:有文本层的页用文本层,缺文本层的页走本地 OCR(默认关闭)',
61
+ ' --dpi <n> 需要 OCR 的页的渲染 DPI(默认 150)',
62
+ ' --max-pages <n> 最多处理页数(默认 50)',
63
+ ' --no-page-images 不落地页图,只保留文字',
64
64
  ' --title <text> 产物标题(默认文件名)',
65
65
  ' --uvx <path> / --python <v> 自定义 uvx 与 Python 版本',
66
66
  ' --keep-scratch 保留中间目录(排障)',
@@ -56,7 +56,7 @@ const HELP = `tita-convert — 多格式转换内核(独立运行)
56
56
  -v, --version 显示版本
57
57
 
58
58
  控制台
59
- 启动后打开 http://127.0.0.1:<port>/ 即可看到 Vue3 转换面板。
59
+ 启动后打开 http://127.0.0.1:<port>/ 即可看到转换控制台。
60
60
  `
61
61
 
62
62
  const options = parseArgs(process.argv.slice(2))
@@ -21,7 +21,16 @@
21
21
  | 画面文字 / 扫描页文字 → 文字 | `RapidOCR`(ONNX) | **不是 LLM**。OCR 小模型,几十 MB |
22
22
  | 拼装 Markdown | 本包模板 | 纯规则 |
23
23
 
24
- 模型只在首次使用时下载一次,之后完全离线。**是否允许下载由调用方显式决定**(`asr.allowModelDownload`,默认 `false`)—— 没字幕又没允许下载时,直接抛 `ASR_MODEL_REQUIRED` 并写清"要下多大"。
24
+ 模型只在首次使用时下载一次,之后完全离线。**是否允许下载由调用方显式决定**(`asr.allowModelDownload`,默认 `false`)。
25
+
26
+ 「允许下载」和「可以使用」是**两件事**,判据也分开:
27
+
28
+ - 本地已经有模型(调用方给的目录,或 huggingface_hub 缓存里的快照)→ **直接用**,不需要那个开关。
29
+ 原来的写法把两件事合成一个布尔值,于是模型已经躺在缓存里的机器照样被拒。
30
+ - 没字幕、本地也没有模型、又不允许下载:
31
+ - `mode: 'auto'`(默认)—— **不转写,也不失败**。关键帧、时长、编码信息照常交付,
32
+ 缺的那一块写成 `TRANSCRIPT_UNAVAILABLE` warning 与正文里的一句说明。
33
+ - `mode: 'transcribe'` —— 明确要逐字稿,拿不到就抛 `ASR_MODEL_REQUIRED`,并写清"要下多大"。
25
34
 
26
35
  ## 安装与构建
27
36
 
@@ -56,33 +65,43 @@ const result = await convertMedia({
56
65
 
57
66
  | mode | 字幕 | 转写 | 说明 |
58
67
  | --- | --- | --- | --- |
59
- | `auto`(默认) | 优先 | 仅当允许下载模型 | 有字幕就绝不碰模型 |
68
+ | `auto`(默认) | 优先 | 本地有模型就转;允许下载才下;两者都不成立就**只交付关键帧并写明原因**,不整单失败 | 有字幕就绝不碰模型 |
60
69
  | `subtitle-only` | 只用 | 永不 | 没字幕就是"没有逐字稿",不报错 |
61
- | `transcribe` | 跳过 | 强制 | 必须允许下载模型 |
70
+ | `transcribe` | 跳过 | 强制 | 拿不到逐字稿就抛 `ASR_MODEL_REQUIRED` |
62
71
  | `frames-only` | 跳过 | 跳过 | 只要关键帧 |
63
72
 
64
- ## 扫描件轻量档:`convertScannedDocument()`
73
+ ## PDF 逐页混合档:`convertScannedDocument()`
65
74
 
66
- 纯图片页 PDF(扫描件)走这条路 —— 它解决的**唯一**问题是"图片页里的字读不出来"。
75
+ **逐页**判断:有文本层的页取文本层(不渲染、不 OCR),缺文本层的页才渲染页图走本地 OCR。
76
+
77
+ 为什么是逐页而不是整份:混合 PDF(扫描封面 + 电子正文)在现实里很常见,而按整份判断
78
+ 两种方向都会出错 —— anydoc 那样"有一页是图片就整份放弃"会把好文字的页一起丢掉;
79
+ 反过来"见到图片页就整份 OCR"会把无损的文字换成有识别误差的版本。
67
80
 
68
81
  ```ts
69
82
  import { convertScannedDocument } from 'dsh-convert-core/media'
70
83
 
71
84
  const result = await convertScannedDocument({
72
- file: '/abs/scan.pdf',
73
- dpi: 150, // 渲染 DPI(默认 150)
74
- maxPages: 50, // 上限,超出写 warning,不静默截断
75
- pageImages: true, // 逐页图片落地为 assets/page-NNNN.png
85
+ file: '/abs/report.pdf',
86
+ dpi: 150,
87
+ maxPages: 50,
88
+ // 一页的非空白字符数低于它就当作没有文本层(默认 16)。
89
+ // 调高会更激进地走 OCR,适合"文本层是乱码的伪文本 PDF"。
90
+ ocrThreshold: 16,
76
91
  })
77
- // result.pages → [{ index, rel, width, height, text, ocrLines }]
92
+ // result.pages[i].source 是 'text' | 'ocr' —— 产物里也标着,读者据此知道哪一页可逐字引用。
78
93
  ```
79
94
 
95
+ 原来的 `convertScannedDocument()` 是"整份 OCR"档:纯图片页 PDF 走它没问题,但混合 PDF
96
+ 会被整份 OCR 一遍。现在它对两种输入都对。
97
+
80
98
  **它不做/不承诺什么**(这是刻意的):
81
99
 
82
100
  - **不做版面还原**:输出是"第 N 页 + 该页图片 + 该页文字",顺序 = 页序;多栏、表格、公式会乱;
83
- - **不引入 docling / PaddleOCR 框架 / MinerU**:那是 `issues/0017` 的"可选高质量档"(几 GB ~ 20 GB 依赖);
84
- - **不联网**:只有两个本地组件 —— `pypdfium2`(渲染)与 `RapidOCR`(识别);
85
- - **不判断该不该用**:是"这份 PDF 没有文本层"才走这里;默认路径应当先让文档引擎试文本层。
101
+ - **不引入 docling / PaddleOCR 框架 / MinerU**:那是 `docs/issue/0017` 的"可选高质量档"(几 GB ~ 20 GB 依赖);
102
+ - **不联网**:只有两个本地组件 —— `pypdfium2`(取文本 + 渲染)与 `RapidOCR`(识别);
103
+ - **不判断该不该用**:默认路径先让文档引擎试文本层,它报 `needsOcr` 时才换到本档
104
+ (内核自动换,见 `resolveFallbackAdapter`);`SCANNED_OCR=1` 可跳过那一步直接走本档。
86
105
 
87
106
  ## CLI
88
107
 
@@ -91,12 +110,12 @@ const result = await convertScannedDocument({
91
110
  node packages/dsh-convert-core/bin/media-to-md.mjs clip.mp4 --lang zh --model large-v3-turbo --allow-model-download
92
111
  # → clip.md-out/{index.md, assets/, transcript.srt}
93
112
 
94
- # 扫描件:逐页 OCR(无需任何系统依赖)
113
+ # PDF 逐页混合(有文本层用文本层,只有图片页走 OCR;无需任何系统依赖)
95
114
  node packages/dsh-convert-core/bin/media-to-md.mjs scan.pdf --scanned-pdf --dpi 200
96
115
  # → scan.md-out/{index.md, assets/page-0001.png …}
97
116
  ```
98
117
 
99
- 扫描档相关参数:`--scanned-pdf`、`--dpi <n>`、`--max-pages <n>`、`--no-page-images`。
118
+ PDF 档相关参数:`--scanned-pdf`(跳过文本层尝试,直接走本档)、`--dpi <n>`、`--max-pages <n>`、`--no-page-images`。
100
119
 
101
120
  ## 设计取舍
102
121
 
@@ -120,6 +139,12 @@ node packages/dsh-convert-core/bin/media-to-md.mjs scan.pdf --scanned-pdf --dpi
120
139
 
121
140
  - **`Could not create temporary file` / uv 缓存不可写**:把缓存重定向到可写目录:
122
141
  `UV_CACHE_DIR`、`UV_PYTHON_INSTALL_DIR`、`UV_TOOL_DIR`、`UV_TOOL_BIN_DIR`、`XDG_CACHE_HOME`。
142
+ - **`TypeError: open() got an unexpected keyword argument 'metadata_errors'`**:
143
+ `faster-whisper` 声明 `av>=11` 却**没有上界**,而 `av.open()` 的签名改过 —— av 19 删掉了
144
+ `metadata_errors`,**av 19 起就拒收这个参数**(实测 13~18 收得下)。转写时 `asr.py` 把
145
+ 16 kHz 单声道 WAV 自己解成波形再交给模型(`transcribe()` 收到 ndarray 就跳过 `decode_audio`),
146
+ 所以正常路径完全不碰 PyAV;只有输入不是这种 WAV、退回"传路径"时才可能撞上。
147
+ 临时解法:`uvx --python 3.12 --with faster-whisper --with "av<19" python scripts/asr.py <音频>`。
123
148
  - **模型下载失败(SSL / Server disconnected)**:Python 侧走 HuggingFace 下载不稳定时,用 curl 预先下好模型目录再把 `asr.model` 指向它:
124
149
 
125
150
  ```bash
@@ -140,4 +165,4 @@ node packages/dsh-convert-core/bin/media-to-md.mjs scan.pdf --scanned-pdf --dpi
140
165
 
141
166
  - 长音频未做 VAD 分片与取消后的分片保留(子进程会随 `AbortSignal` 结束)。
142
167
  - 说话人分离未接。
143
- - 网页链接与扫描件 OCR 不在本包范围(见 `issues/0017` 的 `web.ts` / `docling.ts`)。
168
+ - 网页链接与扫描件 OCR 不在本包范围(见 `docs/issue/0017` 的 `web.ts` / `docling.ts`)。
@@ -10,7 +10,9 @@
10
10
  *
11
11
  * 与文档档(anydoc)一样:引擎独立于 adapter,内核只管契约。
12
12
  *
13
- * 环境变量(都是**显式**开关,默认不下载模型、不对图片做 OCR):
13
+ * 环境变量(都是**显式**开关,默认不下载模型、不对图片做 OCR)。
14
+ * `request.media` 里同名的项**优先于**这些环境变量 —— 环境变量是这台机器的默认值,
15
+ * `media` 是这一次转换的选择(面板上的开关):
14
16
  * IDDOC_ASR=1 允许在无字幕时下载模型并本地转写
15
17
  * IDDOC_ASR_MODEL whisper 模型(默认 small;中文建议 large-v3-turbo)
16
18
  * IDDOC_ASR_LANG 语言代码(默认自动检测;中文建议 zh)
@@ -1 +1 @@
1
- {"version":3,"file":"iddoc.d.ts","sourceRoot":"","sources":["../../src/adapters/iddoc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAYH,OAAO,EAAgB,KAAK,OAAO,EAAyF,MAAM,YAAY,CAAA;AAE9I,eAAO,MAAM,sBAAsB,UAElC,CAAA;AACD,eAAO,MAAM,sBAAsB,UAElC,CAAA;AACD,eAAO,MAAM,sBAAsB,UAAmB,CAAA;AACtD,eAAO,MAAM,gBAAgB,UAAoF,CAAA;AAyHjH,eAAO,MAAM,YAAY,EAAE,OAQ1B,CAAA"}
1
+ {"version":3,"file":"iddoc.d.ts","sourceRoot":"","sources":["../../src/adapters/iddoc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAYH,OAAO,EAAgB,KAAK,OAAO,EAAyF,MAAM,YAAY,CAAA;AAE9I,eAAO,MAAM,sBAAsB,UAElC,CAAA;AACD,eAAO,MAAM,sBAAsB,UAElC,CAAA;AACD,eAAO,MAAM,sBAAsB,UAAmB,CAAA;AACtD,eAAO,MAAM,gBAAgB,UAAoF,CAAA;AAmIjH,eAAO,MAAM,YAAY,EAAE,OAQ1B,CAAA"}
@@ -10,7 +10,9 @@
10
10
  *
11
11
  * 与文档档(anydoc)一样:引擎独立于 adapter,内核只管契约。
12
12
  *
13
- * 环境变量(都是**显式**开关,默认不下载模型、不对图片做 OCR):
13
+ * 环境变量(都是**显式**开关,默认不下载模型、不对图片做 OCR)。
14
+ * `request.media` 里同名的项**优先于**这些环境变量 —— 环境变量是这台机器的默认值,
15
+ * `media` 是这一次转换的选择(面板上的开关):
14
16
  * IDDOC_ASR=1 允许在无字幕时下载模型并本地转写
15
17
  * IDDOC_ASR_MODEL whisper 模型(默认 small;中文建议 large-v3-turbo)
16
18
  * IDDOC_ASR_LANG 语言代码(默认自动检测;中文建议 zh)
@@ -63,8 +65,16 @@ async function probeAvailability() {
63
65
  return { available: true, versions: reported };
64
66
  }
65
67
  function toOptions(context, file) {
66
- const model = (process.env.IDDOC_ASR_MODEL ?? '').trim() || 'small';
67
- const language = (process.env.IDDOC_ASR_LANG ?? '').trim();
68
+ /*
69
+ * 优先级:**本次请求 > 环境变量 > 内置默认**。
70
+ *
71
+ * 环境变量是"这台机器/这个部署"的默认值(运维设一次),请求里的是"这一次转换"的
72
+ * 选择(用户在面板上点的那个开关)。两者都需要:没有前者,每个调用方都得自己写一遍;
73
+ * 没有后者,界面上就无处安放「允许下载模型」这个决定 —— 而那正是原来卡住用户的地方。
74
+ */
75
+ const media = context.request.media ?? {};
76
+ const model = media.asrModel?.trim() || (process.env.IDDOC_ASR_MODEL ?? '').trim() || 'small';
77
+ const language = media.asrLanguage?.trim() || (process.env.IDDOC_ASR_LANG ?? '').trim();
68
78
  const frames = {
69
79
  ...(envInt('IDDOC_SAMPLES') ? { samples: envInt('IDDOC_SAMPLES') } : {}),
70
80
  ...(envInt('IDDOC_MAX_FRAMES') ? { maxFrames: envInt('IDDOC_MAX_FRAMES') } : {}),
@@ -74,6 +84,8 @@ function toOptions(context, file) {
74
84
  ...(process.env.IDDOC_UVX?.trim() ? { uvxPath: process.env.IDDOC_UVX.trim() } : {}),
75
85
  ...(process.env.IDDOC_PYTHON?.trim() ? { pythonVersion: process.env.IDDOC_PYTHON.trim() } : {}),
76
86
  };
87
+ const ocrFrames = media.ocrFrames ?? envFlag('IDDOC_OCR_FRAMES');
88
+ const imageOcr = media.imageOcr ?? envFlag('IDDOC_IMAGE_OCR');
77
89
  return {
78
90
  file,
79
91
  ...(context.request.title?.trim() ? { title: context.request.title.trim() } : {}),
@@ -81,12 +93,12 @@ function toOptions(context, file) {
81
93
  asr: {
82
94
  model,
83
95
  ...(language ? { language } : {}),
84
- allowModelDownload: envFlag('IDDOC_ASR'),
96
+ allowModelDownload: media.allowModelDownload ?? envFlag('IDDOC_ASR'),
85
97
  },
86
98
  ...(Object.keys(frames).length > 0 ? { frames } : {}),
87
99
  ...(Object.keys(tools).length > 0 ? { tools } : {}),
88
- ...(envFlag('IDDOC_IMAGE_OCR') ? { imageOcr: true } : {}),
89
- ...(envFlag('IDDOC_OCR_FRAMES') ? { ocrFrames: true } : {}),
100
+ ...(imageOcr ? { imageOcr: true } : {}),
101
+ ...(ocrFrames ? { ocrFrames: true } : {}),
90
102
  ...(context.signal ? { signal: context.signal } : {}),
91
103
  onProgress: progress => context.report(progress.progress, progress.stage, progress.message),
92
104
  };
@@ -1 +1 @@
1
- {"version":3,"file":"iddoc.js","sourceRoot":"","sources":["../../src/adapters/iddoc.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"iddoc.js","sourceRoot":"","sources":["../../src/adapters/iddoc.ts"],"names":[],"mappings":"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"}
@@ -6,8 +6,10 @@
6
6
  * pass-through. Unknown extensions are only treated as text when the bytes are
7
7
  * actually textual — guessing there is how "converted successfully" becomes a lie.
8
8
  *
9
- * `.pdf` 默认仍走 anydoc(文本层最好、零依赖);它报 `needsOcr` 时由
10
- * {@link resolveFallbackAdapter} 当场改走 `scanned`(见那里的理由)。
9
+ * `.pdf` 默认仍走 anydoc(整份都是文本层时质量最好、零依赖);它报 `needsOcr` 时由
10
+ * {@link resolveFallbackAdapter} 当场改走 `scanned`(见那里的理由)。改走之后是**逐页**
11
+ * 判断:有文本层的页仍然用文本层,只有图片页才渲染 + OCR —— 所以"32 页里有 1 页是图片"
12
+ * 不会把另外 31 页的无损文字换成 OCR 结果。
11
13
  */
12
14
  import type { ConvertRequest } from '../contract.js';
13
15
  import { type Adapter, type AdapterAvailability } from './types.js';
@@ -33,7 +35,12 @@ export interface FallbackDecision {
33
35
  /**
34
36
  * 失败了要不要**当场换一个档再试一次**。
35
37
  *
36
- * 今天只有一条:anydoc 报 `needsOcr` 且输入是 `.pdf` → 改走 `scanned`(本地 OCR 档)。
38
+ * 今天只有一条:anydoc 报 `needsOcr` 且输入是 `.pdf` → 改走 `scanned`(PDF 逐页混合档)。
39
+ *
40
+ * **注意 anydoc 的判据是"整份文档"**:它只要发现**任意一页**需要 OCR 就整份放弃,
41
+ * 报出来的那句 `page 1 of 32 needs OCR` 说的其实是"第 1 页就是图片,我不做了",
42
+ * 而不是"32 页里只有第 1 页有问题"。所以换档不是"退而求其次",而是把 31 页本来
43
+ * 可以无损读出的文本层救回来 —— 换过去的档逐页判断,有文本层的页仍然走文本层。
37
44
  *
38
45
  * 为什么是自动的、而不是像 0017 原来写的那样要求用户显式指定 adapter:
39
46
  *
@@ -44,10 +51,10 @@ export interface FallbackDecision {
44
51
  * 2. **它不改变「不静默降级」这条底线。** 换档会写进任务消息(面板上看得到)与
45
52
  * `manifest.warnings`(`ADAPTER_FALLBACK`),并且产物里带 `SCANNED_TIER` 警告说明
46
53
  * 这一档不做版面还原。用户看到的是「换了什么、为什么换、代价是什么」,不是「假装成功」。
47
- * 3. **代价可控。** 扫描档全程本地(pypdfium2 + RapidOCR,不出网),只在 uvx 存在时才走;
54
+ * 3. **代价可控。** 这一档全程本地(pypdfium2 + RapidOCR,不出网),只在 uvx 存在时才走;
48
55
  * uvx 不在就原样抛出 anydoc 的错误。
49
56
  *
50
- * 仍然保留 `SCANNED_OCR=1` 那条显式出口:它管的是「跳过 anydoc 直接走 OCR」,
57
+ * 仍然保留 `SCANNED_OCR=1` 那条显式出口:它管的是「跳过 anydoc 直接走本档」,
51
58
  * 与这里「anydoc 试过、失败、再换」是两件事。
52
59
  */
53
60
  export declare function resolveFallbackAdapter(cause: unknown, request: ConvertRequest, filename: string, inputPath: string | undefined): Promise<FallbackDecision | undefined>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAMpD,OAAO,EAAgB,KAAK,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAEjF,eAAO,MAAM,QAAQ,EAAE,OAAO,EAA2E,CAAA;AAEzG,4CAA4C;AAC5C,eAAO,MAAM,cAAc,QAAQ,CAAA;AAEnC,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAE3D;AAED,mFAAmF;AACnF,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAiBjE;AAED;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,UAEhC,CAAA;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,OAAO,CAAC,OAAO,CAAC,CAmClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAA;IAChB,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAYvC;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,cAAc,CAAC,MAAM,CAAC,EAAE,CAAA;IAC/B,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,YAAY,EAAE,mBAAmB,CAAA;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,iBAAiB,EAAE,CAAA;IAC7B,yFAAyF;IACzF,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACjF,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,6CAA6C;IAC7C,UAAU,EAAE,MAAM,EAAE,CAAA;CACrB;AAED,gFAAgF;AAChF,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,gBAAgB,CAAC,CA0ClE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAMpD,OAAO,EAAgB,KAAK,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAEjF,eAAO,MAAM,QAAQ,EAAE,OAAO,EAA2E,CAAA;AAEzG,4CAA4C;AAC5C,eAAO,MAAM,cAAc,QAAQ,CAAA;AAEnC,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAE3D;AAED,mFAAmF;AACnF,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAiBjE;AAED;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,UAEhC,CAAA;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,OAAO,CAAC,OAAO,CAAC,CAkClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAA;IAChB,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAYvC;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,cAAc,CAAC,MAAM,CAAC,EAAE,CAAA;IAC/B,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,YAAY,EAAE,mBAAmB,CAAA;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,iBAAiB,EAAE,CAAA;IAC7B,yFAAyF;IACzF,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACjF,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,6CAA6C;IAC7C,UAAU,EAAE,MAAM,EAAE,CAAA;CACrB;AAED,gFAAgF;AAChF,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAyClE"}
@@ -6,8 +6,10 @@
6
6
  * pass-through. Unknown extensions are only treated as text when the bytes are
7
7
  * actually textual — guessing there is how "converted successfully" becomes a lie.
8
8
  *
9
- * `.pdf` 默认仍走 anydoc(文本层最好、零依赖);它报 `needsOcr` 时由
10
- * {@link resolveFallbackAdapter} 当场改走 `scanned`(见那里的理由)。
9
+ * `.pdf` 默认仍走 anydoc(整份都是文本层时质量最好、零依赖);它报 `needsOcr` 时由
10
+ * {@link resolveFallbackAdapter} 当场改走 `scanned`(见那里的理由)。改走之后是**逐页**
11
+ * 判断:有文本层的页仍然用文本层,只有图片页才渲染 + OCR —— 所以"32 页里有 1 页是图片"
12
+ * 不会把另外 31 页的无损文字换成 OCR 结果。
11
13
  */
12
14
  import { open } from 'node:fs/promises';
13
15
  import { extname } from 'node:path';
@@ -69,8 +71,6 @@ export async function resolveAdapter(request, filename, inputPath) {
69
71
  }
70
72
  if (request.kind === 'text')
71
73
  return textAdapter;
72
- if (request.kind === 'url')
73
- return webAdapter;
74
74
  const extension = extname(filename).toLowerCase();
75
75
  // 另存下来的页面按「网页」处理,而不是当纯文本直通 —— 否则 `.html` 会以源码形态入库。
76
76
  if (WEB_FILE_EXTENSIONS.includes(extension))
@@ -86,12 +86,17 @@ export async function resolveAdapter(request, filename, inputPath) {
86
86
  return textAdapter;
87
87
  if (inputPath && (await looksTextual(inputPath)))
88
88
  return textAdapter;
89
- throw new AdapterError('FORMAT_UNSUPPORTED', `不支持的输入格式:${extension || '(无扩展名)'}`, `本版做文件类:${ANYDOC_EXTENSIONS.join(' ')}(文档)、${SCANNED_EXTENSIONS.join(' ')}(扫描件 OCR 档)、${IDDOC_EXTENSIONS.join(' ')}(音视频/图片)、${WEB_FILE_EXTENSIONS.join(' ')}(另存的页面)以及 ${TEXT_EXTENSIONS.join(' ')}(直通);网页链接走 kind=url。`);
89
+ throw new AdapterError('FORMAT_UNSUPPORTED', `不支持的输入格式:${extension || '(无扩展名)'}`, `本版做文件类:${ANYDOC_EXTENSIONS.join(' ')}(文档)、${SCANNED_EXTENSIONS.join(' ')}(扫描件 OCR 档)、${IDDOC_EXTENSIONS.join(' ')}(音视频/图片)、${WEB_FILE_EXTENSIONS.join(' ')}(另存的页面)以及 ${TEXT_EXTENSIONS.join(' ')}(直通)。链接请先取回页面(浏览器另存为 HTML)再按文件提交。`);
90
90
  }
91
91
  /**
92
92
  * 失败了要不要**当场换一个档再试一次**。
93
93
  *
94
- * 今天只有一条:anydoc 报 `needsOcr` 且输入是 `.pdf` → 改走 `scanned`(本地 OCR 档)。
94
+ * 今天只有一条:anydoc 报 `needsOcr` 且输入是 `.pdf` → 改走 `scanned`(PDF 逐页混合档)。
95
+ *
96
+ * **注意 anydoc 的判据是"整份文档"**:它只要发现**任意一页**需要 OCR 就整份放弃,
97
+ * 报出来的那句 `page 1 of 32 needs OCR` 说的其实是"第 1 页就是图片,我不做了",
98
+ * 而不是"32 页里只有第 1 页有问题"。所以换档不是"退而求其次",而是把 31 页本来
99
+ * 可以无损读出的文本层救回来 —— 换过去的档逐页判断,有文本层的页仍然走文本层。
95
100
  *
96
101
  * 为什么是自动的、而不是像 0017 原来写的那样要求用户显式指定 adapter:
97
102
  *
@@ -102,10 +107,10 @@ export async function resolveAdapter(request, filename, inputPath) {
102
107
  * 2. **它不改变「不静默降级」这条底线。** 换档会写进任务消息(面板上看得到)与
103
108
  * `manifest.warnings`(`ADAPTER_FALLBACK`),并且产物里带 `SCANNED_TIER` 警告说明
104
109
  * 这一档不做版面还原。用户看到的是「换了什么、为什么换、代价是什么」,不是「假装成功」。
105
- * 3. **代价可控。** 扫描档全程本地(pypdfium2 + RapidOCR,不出网),只在 uvx 存在时才走;
110
+ * 3. **代价可控。** 这一档全程本地(pypdfium2 + RapidOCR,不出网),只在 uvx 存在时才走;
106
111
  * uvx 不在就原样抛出 anydoc 的错误。
107
112
  *
108
- * 仍然保留 `SCANNED_OCR=1` 那条显式出口:它管的是「跳过 anydoc 直接走 OCR」,
113
+ * 仍然保留 `SCANNED_OCR=1` 那条显式出口:它管的是「跳过 anydoc 直接走本档」,
109
114
  * 与这里「anydoc 试过、失败、再换」是两件事。
110
115
  */
111
116
  export async function resolveFallbackAdapter(cause, request, filename, inputPath) {
@@ -123,7 +128,7 @@ export async function resolveFallbackAdapter(cause, request, filename, inputPath
123
128
  return undefined;
124
129
  return {
125
130
  adapter: scannedAdapter,
126
- reason: '这份 PDF 没有文本层(纯图片页),anydoc 不做 OCR,已改用本地扫描档(逐页 OCR,不做版面还原)',
131
+ reason: '这份 PDF 里有图片页,anydoc 不做 OCR,已改用本地 PDF 档(逐页判断:有文本层的页仍用文本层,只有图片页走 OCR;不做版面还原)',
127
132
  };
128
133
  }
129
134
  /** Everything the console and the DSH settings panel need to tell the truth. */
@@ -158,7 +163,6 @@ export async function capabilityReport() {
158
163
  defaults: {
159
164
  document: anydoc.available ? 'anydoc' : '(不可用)',
160
165
  text: 'text',
161
- url: 'web',
162
166
  video: 'iddoc',
163
167
  audio: 'iddoc',
164
168
  image: 'iddoc',
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"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"}
@@ -1,28 +1,36 @@
1
1
  /**
2
- * scanned —— 扫描件 / 纯图片页 PDF 的**轻量 OCR 档**(issue 0017 的扫描件线,轻量版)。
2
+ * scanned —— PDF 的**逐页混合档**(issue 0017 的扫描件线)。
3
3
  *
4
4
  * 与 `anydoc` 的分工是明确的:
5
5
  *
6
- * - `.pdf` **默认仍走 anydoc**(文本层 PDF,零依赖、质量最好);
7
- * - anydoc 报 `needsOcr`(整份是图片页)时,错误建议会指向本档;
6
+ * - `.pdf` **默认仍走 anydoc**(整份都是文本层时零依赖、质量最好);
7
+ * - anydoc 报 `needsOcr`(文档里**有**图片页)时,内核自动换到本档(`resolveFallbackAdapter`);
8
8
  * - 设 `SCANNED_OCR=1` 可让 `.pdf` 直接走本档(显式 opt-in,不做静默降级)。
9
9
  *
10
+ * ## 本档的核心是"逐页"而不是"整份"
11
+ *
12
+ * anydoc 在**整份文档**这一层判断,所以"32 页里有 1 页是图片"会让 32 页一起失败。
13
+ * 本档逐页问"这页有文本层吗":有就取文本、不渲染;没有才渲染页图并 OCR。
14
+ * 于是混合 PDF(扫描封面 + 电子正文)既能拿到全部 32 页,又不会把 31 页无损文字
15
+ * 换成 OCR 结果。
16
+ *
10
17
  * 本档**不做**版面还原:输出是"第 N 页 + 该页图片 + 该页文字"。版面顺序、表格结构、
11
18
  * 公式属于 0017 的"可选高质量档"(docling / MinerU),不在这一档的承诺里。
12
19
  *
13
20
  * 能力来自同包 `src/media/` 的 `convertScannedDocument()`:
14
- * pypdfium2 渲染页图(无系统 poppler 依赖)+ RapidOCR 本地识别,全程不联网。
21
+ * pypdfium2 取文本 + 渲染页图(无系统 poppler 依赖)、RapidOCR 本地识别,全程不联网。
15
22
  *
16
23
  * 环境变量:
17
24
  * SCANNED_OCR=1 让 `.pdf` 直接走本档(默认 false:先让 anydoc 试文本层)
18
25
  * SCANNED_MAX_PAGES 最多处理页数(默认 50)
19
- * SCANNED_DPI 渲染 DPI(默认 150)
26
+ * SCANNED_DPI 渲染 DPI(默认 150),只影响需要 OCR 的页
27
+ * SCANNED_OCR_THRESHOLD 文本层非空白字符数低于它就当作图片页(默认 16)
20
28
  * SCANNED_NO_PAGE_IMAGES=1 只保留文字,不落地页图
21
29
  * SCANNED_UVX / SCANNED_PYTHON 自定义 uvx 与 Python 版本
22
30
  */
23
31
  import { type Adapter } from './types.js';
24
32
  export declare const SCANNED_EXTENSIONS: string[];
25
33
  export declare const scannedAdapter: Adapter;
26
- /** 路由用:是否让 `.pdf` 直接走扫描档(显式 opt-in)。 */
34
+ /** 路由用:是否让 `.pdf` 直接走本档(显式 opt-in)。 */
27
35
  export declare function scannedPreferred(): boolean;
28
36
  //# sourceMappingURL=scanned.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scanned.d.ts","sourceRoot":"","sources":["../../src/adapters/scanned.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAWH,OAAO,EAAgB,KAAK,OAAO,EAAyF,MAAM,YAAY,CAAA;AAE9I,eAAO,MAAM,kBAAkB,UAAW,CAAA;AA2F1C,eAAO,MAAM,cAAc,EAAE,OAQ5B,CAAA;AAED,wCAAwC;AACxC,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C"}
1
+ {"version":3,"file":"scanned.d.ts","sourceRoot":"","sources":["../../src/adapters/scanned.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAWH,OAAO,EAAgB,KAAK,OAAO,EAAyF,MAAM,YAAY,CAAA;AAE9I,eAAO,MAAM,kBAAkB,UAAW,CAAA;AAqG1C,eAAO,MAAM,cAAc,EAAE,OAQ5B,CAAA;AAED,uCAAuC;AACvC,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C"}