開発book-knowledge-base

朝、未コミットの作業を見たら昨日のyomitoku-kindleがStep 10まで書きかけで止まっていた。今日は「URLを渡したら最後まで通す」をゴールに据えて手を動かした。最終的に、Chrome拡張でKindle Cloud Readerを巡回スクショ→yomitokuでOCR→Turso book-knowledge-base DBに投入、最後に/restructure-bookで章節整形まで通る/yomitoku-kindleスラッシュコマンドが完成した。

やったこと

  • /yomitoku-kindle <ASIN> で巡回スクショ→OCR→Turso投入→/restructure-bookチェーン実行までを1コマンドで完走させた
  • Chrome拡張のUIをポップアップからFloating Panelに移し、拡張アイコンを毎回クリックする運用を捨てた
  • 撮影の瞬間だけFloating Panelをdisplay:noneでDOMから外し、OCRノイズを消した
  • read.amazon.co.jp/notebookの内部APIからハイライトとメモを取得し、kindle_library / kindle_highlights テーブルにUPSERTで投入した
  • KU(Kindle Unlimited)絞り込み27件をkindle_libraryに反映した
  • コミック157件にcomicタグを付与し、星評価ランキングから除外運用に切り替えた
  • ビューアー側に表紙画像を追加、グローバルナビでbooks / shelves / highlightsへ全ページから1クリック遷移できるようにした

Floating Panel化で「拡張アイコンを毎回クリックする」運用を捨てる

最初の壁はChrome拡張のUI形態だった。MV3のポップアップはアイコンクリックがないと開かないので、自動化と相性が悪い。content.jsの中でKindleページにfloating panelを直接埋め込む構造に書き換えた。これでClaude Codeからevaluate_script#kindle-capturer-startclick()するだけで巡回が始まる。

ところがpanel経由で起動した瞬間、captureVisibleTabEither the '<all_urls>' or 'activeTab' permission is required.で落ちた。MV3のactiveTabはユーザーのアイコンクリック起点でしか降りない仕様だった。manifestの権限を<all_urls>を持つ形に切り替えて回避した。昨日まで動いていたのに今朝動かなかったのは、起動経路がポップアップからfloating panelに変わったからだった。

撮影瞬間のpanel非表示——OCRノイズを消す

巡回を回しはじめて、ユーザーから「panelが画面に映り込んでる」と指摘された。captureVisibleTabはChromeのビューポート全体を撮るのでFloating Panel自体が黒背景の上に重なって入る。OCRノイズになる。

最初はvisibility:hiddenで逃がしたが、それでも領域が残る。完全に消すためにdisplay:noneに切り替え、撮影直後に復元する方式に変えた。さらに、keepalive用のsetIntervalが撮影中に新しいpanelを再注入する競合があったので、巡回中はkeepalive自体を止めた。134枚撮り終えて確認したら、Floating Panelの映り込みは一切なし。OCR精度の文脈で「白背景+黒文字」を意識してKindleのReader設定を反転させる運用も追加した。

// 撮影直前にpanelをDOMから外す
const panel = document.getElementById('kindle-capturer-panel')
const parent = panel?.parentNode
panel?.remove()
await chrome.runtime.sendMessage({ type: 'CAPTURE_VISIBLE_TAB' })
if (panel && parent) parent.appendChild(panel)

/restructure-bookのチェーン実行を組み込む

/yomitoku-kindleの末尾で/restructure-bookを必ず呼ぶように直した。マークダウンのスラッシュコマンドはチェーン実行を明示的に書かないと走らないので、Step 16として/restructure-book <book_id>をinline実行する手順を末尾に追加した。コミット履歴で追えるよう、book-knowledge-basechrome-extension-kindle~/.claudeの3リポジトリそれぞれにコミットを切った。

「目次照準が逆」バグ——画像順を反転して再OCR

別の本(東洋経済の和書)を流したら、章番号が第7章→第1章の降順で並ぶ妙な構造になった。最初は「目次ページがある変な本」と思って章名割り当てで凌いだが、ユーザーから「いや、照準なんでこれ逆なんですか」と一言。原因はKindleメタのpage-progression-direction: rtl。横書きビジネス書でもKindle側は「和書」扱いで、ページ送りキーがltrと逆向きに作用していた。

ここで「再撮影せず、画像の順序をプログラムで反転して再OCRすればいい」とユーザーから案を貰った。page_0001.pngpage_0124.pngをリネームで逆順に並べ替え、yomitokuに再投入。DB側の旧チャンクを削除して再投入したら、第1章→第7章の論理順に並んだ。/restructure-bookもセクション単位で全部走らせ、87→68チャンクに統合できた。

この学びは/yomitoku-kindleのStep 5「方向判定」と、末尾の「方向誤判定リカバリ」セクションに書き残した。次から「論理順がおかしい」と気づいた瞬間、撮り直しではなくスクリプトで反転すれば数分で復旧できる。

「画像が全部Xタブだった」事件——別ウィンドウ分離

別の本(新書)で巡回を回したら、撮れていた100枚が全部XのタブUIだった。原因は単純で、撮影中に元ウィンドウでX(Twitter)タブをアクティブにしたから。captureVisibleTab(windowId)は「指定ウィンドウのアクティブタブ」を撮るので、別タブに切り替えた瞬間そっちが入る。

対策として、background.jsに「Kindleタブを新ウィンドウに分離して撮影する」ハンドラを追加し、content.jsには**「別ウィンドウで開始」ボタン**を新設した。これで撮影専用ウィンドウは独立して動き、元ウィンドウではTwitterでも調べ物でも自由にできる。新書98ページを再撮影したら、まえがき→第1章→と論理順で揃った。

Kindleノートブックの内部API——2,801件のハイライトを取り込む

別セッションでread.amazon.co.jp/notebookの内部APIを観察した。/notebook?asin=...&contentLimitState= がハイライト本体(HTML 116KB)を返し、annotation IDと位置・色・本文・メモが全部DOMから取れる。Chrome DevTools MCP経由でログイン済みChromeから叩いて、ASIN 196冊分のハイライトをfetchしてTursoのkindle_library / kindle_highlightsテーブルにUPSERTで投入した。

最初の取り込みでcp932エンコーディングのprintで落ちて0件になったが、出力をUTF-8固定&書籍ごとcommitに変えて、196冊・2,801件のハイライトをDBに収めた。

-- kindle_highlights テーブル設計(差分判定用にlast_annotated_atを持つ)
CREATE TABLE kindle_highlights (
  annotation_id TEXT PRIMARY KEY,
  asin TEXT NOT NULL,
  location INTEGER,
  color TEXT,
  highlight TEXT,
  note TEXT,
  annotated_at TEXT
);

ビューアー側——表紙画像とグローバルナビ

mdx-playgroundではなくbook-knowledge-baseのNuxt側ビューアーに、amazon_metadata.image_url_largeから表紙画像を引っ張る修正を入れた。258冊中233冊に画像URLがあったので、/booksの4カラムグリッドが一気に華やかになった。

そのあとユーザーから「ヘッダーのどこかからbooks/shelves/highlightsに直接遷移できるようにして」と指示。Nuxtのレイアウトで全ページ共通のグローバルナビバーを追加し、Kindleハイライトページの独自topbarが重ならないようtop: 100pxにずらした。

コミック除外

KU・全468冊の星評価を取り終わって★順ランキングを見たら、トップ10がほぼコミックで埋まっていた。ユーザーから「コミックは星が高くなりがちだからcomicタグで除外して」と指示。タイトル・著者・カテゴリ・amazon_categoriesからヒューリスティック判定して157件にcomicタグを付与した。誤判定1件(橘玲の評論本がWPB eBooksレーベルでヒット)はユーザー目視で除外。

学びメモ

  • MV3のactiveTabは起動経路で挙動が変わる。Floating Panel化で動かなくなった原因はここだった。ポップアップ起動と同じ感覚で進めると黙って落ちる
  • OCR元画像にUI要素を映さないdisplay:none+keepalive停止までやって、ようやく黒背景に本文だけの画像が撮れる
  • 論理順が逆だと気づいたら撮り直さない。リネームで反転して再OCRすれば数分で済む。今回これに30分かけたが、次は5分で復旧できる
  • captureVisibleTabはアクティブタブを撮る。撮影専用ウィンドウを別に分離するのが唯一の正解。撮影中は元ウィンドウで自由に作業できるメリットも大きい
  • チェーン実行はマークダウンに書き残す/yomitoku-kindle末尾に「最後に/restructure-bookを必ず呼ぶ」を明文化して、未来の自分が忘れない構造にした

明日やること

  • kindle_libraryの467冊から、まだOCR未取り込みの本を優先順位順にOCR投入する運用フローを書く
  • ハイライトとOCR本文を横断検索できる/kindle-searchコマンドを設計する
  • /yomitoku-kindleの巡回中に「Oops... Something Went Wrong」が出たら自動でdone.jsonにerror記録する処理を実機で確認する