Kindle for PC(デスクトップ版)で開いた本を、ページ送りからスクリーンショット、OCR、Turso DB 投入まで一気通貫で流すパイプラインを組んだ。Cloud Reader で開けない本(固定レイアウトの学術書や、PC アプリにしか降りてこない本)に届かせるための回り道。
途中で C:\Users\numbe\.git を踏み抜く事故と、OpenAI API キーを env | grep で漏らす事故も同時に処理することになって、最後はだいぶ疲れた。
まず本文 DOM を直接取れないか足掻く
きっかけは「OCR の前に、Kindle アプリの中の本文テキストがそのまま取れたら無駄が省ける」という素朴な期待。アプリの裏側を Claude Code に掘らせた。
順番にこうなった。
- ネイティブ Windows アプリだから DOM は無いと最初に整理された
- UIA で覗くと
ReadingAreaの構造とフッターのPage X of Yまでは取れる。ただし本文テキストは降りてこない - Kindle for PC の中身を漁ったら
EBWebView/、appBundle.js、bridge.jsといったファイルが出てきた。一瞬「React/Webpack の Web アプリで、Chromium 系の埋め込みブラウザで動いてる」と確定したように見えた - そのあと
Qt5Core.dll/LibWebCore.dllが見つかり「Qt + WebKit 系か」と反転。さらにbridge.jsの中身で再反転 - 最終的に 旧 Kindle(Legacy)は Qt + WebKit + KRF(Kindle Reader Framework)、新 Kindle(MSIX)は React Native for Windows + XAML ネイティブ UI(Hermes bytecode のバンドル magic で確定) という結論に着地した
新 Kindle に至っては XAML ネイティブで描画していて、そもそも DOM が存在しない。WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS を設定して CDP に繋いだら旧 Kindle のライブラリ画面までは見えたが、リーダー領域は最後まで空のまま。
ここで OCR 路線に確定。Cloud 版で動かしている既存の「読み解く-Kindle」スキルと同じ出力形式(~/Downloads/kindle-captures/<ASIN>/page_NNNN.png + done.json)に揃える方針にした。
事故その1:C:\Users\numbe\.git がいた
ここで横やり。VS Code の Git タブが「1万件変更」と言ってきた。
調べていくと、犯人はホームディレクトリ直下の .git。いつ作ったか覚えがないが C:/Users/numbe/.git がいて、Git_repo/ 配下の各プロジェクトの .git/objects/ の中身が、親リポジトリから見ると未追跡ファイルとして10K件積み上がっていた、という構図。
最初の対処は素直に削除する方針で進めたが、PowerShell に -rf が無くてエラー。最終的に mv C:/Users/numbe/.git C:/Users/numbe/.git.bak でリネームして無効化。万一中身が必要になっても戻せる形にして、後始末を終わらせた。
ユーザー(自分)がやったのは「VS Code が 1 万件と言ってるんだけど」と違和感を投げたことと、mv を一発打ったこと。原因の特定と回避手順は Claude Code に組み立てさせた。
事故その2:OpenAI API キーが env | grep で漏れた
OCR 路線が決まった直後、Qt WebKit Inspector を起動できないか調査していたとき、Claude が env | grep -iE "qt|..." を打った。qt が 2 文字なので、文字列の中にたまたま qt を含む環境変数の値まで全部マッチする。 OpenAI API キーが画面に流れた。
Claude 側から「APIキー漏洩しました」と即座に申告が出た。LINE の通知トークンも一緒に流れていた。
- OpenAI 側はキーをリボーク済み。
- LINE は通知トークンの単体漏れなので、できることが限定的(自分のチャネルへの通知が打てる程度)。実害評価だけ済ませて止めた。
- 後始末として、レジストリから
WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTSも完全削除させた。
grep の 2 文字パターンは危ない、というのを身体で覚えた。次から grep -iE "qt[a-z_]*key|qt[a-z_]*url" のように、隣接する文字も含めて絞らせる癖をつけたい。
OCR 路線:矢印キー送信から「物理マウスクリック」へ
ここから本筋の実装に戻る。
最初の設計は素直なもの。「ユーザーがスタートを押す → 自動で先頭ページに戻る → 右矢印キーを送ってページ送り → 末尾までキャプチャ → done.json 書き出し」。CLI と GUI の両方で動くようにした。
詰まったのは新 Kindle 側のページ送り。
- 新 Kindle MSIX は SendInput でのキー入力を完全にブロックしてくる。
SetForegroundWindow FAILED、SendInput VK_RIGHT returned: 0 PostMessage WM_KEYDOWN/SendMessageも無視。DoDefaultAction()も効かない- マウスの方も
mouse_event(旧 API)は無視。SendInputでマウスイベントを送る方式に切り替えてもダメに見えた - 唯一効いたのが、ReadingArea 右端 100px の
page-chevron-container-right領域への物理クリック
ここで切り分けに苦労した。「クリックは効いているのにページが進まない」と思い込んだ瞬間が何度かあったが、実際には UIA キャッシュが古い値を返していて、ページは進んでいた。スクショを撮って目視で Location を確認したら、自分の read_page_status が返す値より先に進んでいた、という瞬間がブレイクスルーになった。
最終的にこういう構図に落ち着いた。
- スタートと書籍切り替えは自分(人間)がやる。新 Kindle はキーもクリックも synthetic input を弾いてくるので、Library 画面の本表紙クリックや Aa 設定(1列・Light・先頭)は手で済ませる
- キャプチャ開始のボタンを押したら、あとは Python スクリプトが ReadingArea 右端をクリックしてページを送り続ける
- フッターの
Page X of Y/Location X of Yを UIA で読み、末尾に達するか同じ位置で 5 回連続止まったら停止 ~/Downloads/kindle-captures/<ASIN>/page_NNNN.pngを吐いて、最後にdone.jsonを書く
ページ送りの間隔は 1.5 秒で始めて、ユーザー判断で 1.0 秒まで縮めた。固定レイアウト本は 1.0 秒で問題なく走ったが、リフロー本は 1.0 秒だとレンダリングが追いつかず no_advance で即停止することがあって、停止判定を「3 回連続で位置変化なし」に緩めた。
ローマ数字ページの罠
何冊か撮っているうちに、税理士向けの実用書系(前付に i, ii, iii, iv... のローマ数字、本文に 1, 2, 3... のアラビア数字)で詰まった。
_PAGE_PATTERN_ENがPage\s+\d+\s+of\s+\d+でアラビア数字しか拾わない- 前付の i, ii を取れないまま「進んでないと判定して停止」していた
修正は単純で、ローマ数字(大文字/小文字両対応)も Page X of Y と同じ枠で拾えるようにしてから、停止判定はアラビア数字と location 形式の本文ページに限定することにした。ローマ数字のページは「撮るだけ、止めない」。
結果、修正後の走行で「前付 i〜viii + 本文 1〜299 = 307 枚、11.5 分、status: completed」が出た。
役割分担:自分が判断する係、Python と Claude が回す係
今回はっきり線が引けた。
- 自分: Kindle で本を開く、Aa で 1 列・Light・先頭ページを揃える、GUI の START を押す、撮れた PNG を 3 枚ほど目視で確認する、
B009U4ZX1I池上彰のお金の学校みたいに「PC アプリでも開けない本」を発見してフラグを足す指示を出す - Python スクリプト: ReadingArea 右端を物理クリックしてページを送り、UIA でフッターを読んで終端判定、PNG と done.json を吐く
- Claude Code: 仕様変更(停止判定の緩和、ローマ数字対応、座標調整)、OCR を yomitoku で回し、md と figures を整理して Turso の
kindle_highlights/book_chunks系に投入、FTS の検索テストまで一気通貫
「人間が判断する係、AI が実行する係」の構図そのまま。ページ送りが効いてないように見える瞬間や、座標がズレた瞬間に違和感を拾うのは自分の役目で、修正の手は Claude が動かす。
サンプル本 16 冊と「Desktop App で開ける本」のフラグ整理
撮影パイプラインが安定したあと、Turso の kindle_library テーブルで「Cloud Reader で開けなかった本」を洗い直した。excluded_reason 列に過去の Cloud Reader 失敗理由が積んであって、これが Desktop App ルートの取り込み候補リストになる。
is_sample = 1のサンプル本 16 冊が混ざっていたので、これは買い直さない限り対象外として除外- Kindle Unlimited で借りているだけの本(
is_kindle_unlimited = 1)も、返却済み/返却予定があるので原則除外 - Cloud Reader 不可・購入済み・未処理の本 16 冊 → 14 冊 → 7 冊と絞り込んだ
- 池上彰のお金の学校(
B009U4ZX1I)は PC アプリでも開けなかった。新しいexcluded_reasonタグ「Desktop App でも未サポート(モバイル専用)」を立てた
撮影計画書は book-knowledge-base/memo/2026-06-23/kindle-desktop-bulk-capture-plan.md に固定して、明日以降の自分への引き継ぎはこのファイルに集約する形にした。
今日撮れた本
実走行できたのはこのあたり。
- システムの科学 第3版(ハーバート・A・サイモン): 415 ページ、140 枚、6 分 20 秒。最初の本格走行。1 クリックで Kindle 内部のページカウンタが 2〜3 進む現象に気づいて、1 スプレッド ≒ 1 内容で問題ないと結論づけた
- 税理士のためのプログラミング: 221 ページ、220 枚、11 分。固定レイアウト本のリファレンス挙動として安定
- 新・現代会計入門 第3版: リフロー本。Location 10689 のうち 377 枚を撮影。
no_advanceの停止判定を 5 回 → 3 回に緩めた修正と、座標をright - 30(chevron 領域内)に戻した修正がここで効いた - ソフトウェア開発失敗集(B0CW1KZ5N3): 425 ページ、206 枚、11.85 分。
is_kindle_unlimited = 1と判明したので扱いを継続検討に回した
OCR と DB 投入は最初の「システムの科学」だけ通して、Step C(mv → yomitoku OCR → DB 投入 → FTS 確認 → restructure-book サブエージェント)が動くことを確認した。残りはまとめて OCR バッチで流す予定。
一日の終わりに残ったもの
- Kindle for PC(旧/新両方)の中身がどうなっているか、UIA/CDP/物理マウスのどれが効くかが頭に入った
- ローマ数字ページや、Location 形式のフッター(リフロー本)も扱える形になった
- ホームディレクトリ直下の
.gitという地雷を1個踏み抜いて回避手順を学んだ - 環境変数を雑に grep してはいけない、という痛い教訓を 1 件追加した
- 翌日に積む候補のリストが計画書に整理された
memo/ に過去経緯の HTML、book-knowledge-base/memo/2026-06-23/ に撮影計画書を残して終わり。明日は撮影フェーズの残り 6 冊を片付けてから、まとめて yomitoku を走らせる。