カバディ専用のタイマーアプリの不具合を修正しました

先日公開したカバティタイマーなのですが、今日練習試合で利用するとわりと不具合が判明したので諸々修正しました。 あとついでにいくつかの調整を行いました。ご迷惑おかけしました。

dominion525.hatenablog.jp

v0.1.1 アップデート内容

重要な不具合の修正

  • カウントダウンが適切に行われない問題

タイマーのカウントダウンが正確に動作しないケースがありました。 内部的にはサーバとの同期を諦め端末側での高精度な相対時間を用いるように変更し、おそらく問題がなくなったと思います。 (端末間の誤差が発生する可能性はあります)

  • QRコードが再表示されない問題

一度閉じたQRコードモーダルを再度開くと、QRコードが表示されない問題がありました。 開き直してもQRコードが正しく表示されるようになりました。

仕様の変更

スコアリセットボタンの削除とレイドカウントの表示

  • Do or Die の仕様変更

カバディのルール上、Do or Die状態以外がプレイヤーに認知されてはいけないそうなので、インジケータ表示を変更しました。 レイド回数は操作画面に表示されています。

  • スコアリセットボタンを削除

誤って押してしまいやすかったスコアリセットボタンをシンプルモードから削除しました。スコアをリセットしたい場合は、通常モードに切り替えてご利用ください。

その他の改善

  • ボタン配置の改善

モバイル端末の非シンプルモードで全体コントロールを画面最上部に移動しました。

リセットとコートチェンジを上部に

  • モバイル端末で初めて開いた時はシンプルモード

スマートフォンで初めてタイマーを開いた時、シンプルモードの画面が表示されるようになりました。

  • デスクトップ版のボタンサイズ調整

+1/-1などの操作ボタンをよりコンパクトなサイズに調整ました。

利用はこちらから

kabaddi.dominion525.com

リンク

github.com

kindle の蔵書一覧を取得するツール kindle-tilte-exporter を作りました(Mac専用)

tl;dr

  • Kindle for Macの蔵書情報をCSV/JSONでエクスポートするCLIツールを作りました
  • npmパッケージとして公開、MITライセンス
  • 下記でインストール不要で使えます。
npx kindle-title-exporter > books.csv 
  • メタデータのみ取得、コンテンツ本体には関与しません。
  • Mac専用です。

背景

Kindleで本を買い続けていると、シリーズ本の購入状況を把握するのが難しくなってきます。Kindleアプリで一覧は見られますが、スクロールして目視確認するしかありません。もちろん集計やソート、フィルタリングもできません。

Amazonの「コンテンツと端末の管理」ページにも一覧はありますが、ページネーションで少しずつしか見られず、エクスポート機能もありません。

で今、主に漫画なんですが、現在ざっくり3500冊、600シリーズくらいあるんですよ。なお、継続購入してるシリーズが100件くらい。結構つらい。

そういえば Kidle for Macを使っていればローカルに蔵書情報が保存されているので、それを読めばいいじゃない、と言うことで、CSV/JSON形式で出力するツールを作りました。

準備

Kindle

Kindle

  • AMZN Mobile LLC
  • ブック
  • 無料
apps.apple.com

Kindle for Mac をセットアップして、蔵書リストを同期しておいて下さい(普通に本が読めるようにするだけ)。

使い方

インストール不要で使えます:

  npx kindle-title-exporter > books.csv

CSV形式で標準出力に出力されるので、リダイレクトでファイルに保存します。

JSON形式での出力も可能です:

  npx kindle-title-exporter -f json > books.json

出力される項目は12個です:

フィールド名 説明
bookId 書籍ID(例: A:B009DEMC8W-0)
asin 純粋なASIN(例: B009DEMC8W)
title タイトル
author 著者名
seriesName シリーズ名
seriesNumber シリーズ番号
publisher 出版社名
publicationDate 出版日(ISO 8601形式)
purchaseDate 購入日時(ISO 8601形式)
contentTags コンテンツタグ(配列)
language 言語コード(例: ja, en)
sortTitle ソート用タイトル(カタカナ表記など)

例えば、Googleスプレッドシートにインポートすれば、フィルタリングやソートが自由にできます。 シリーズ名でフィルタリングすることで、購入していない巻も分かりやすくなります。 購入日でソートすると、購入頻度の傾向も見えたりしますね。

いろいろ二次利用が捗るかと思います。

出力例

"bookId","asin","title","author","seriesName","seriesNumber","publisher","publicationDate","purchaseDate","contentTags","language","sortTitle"
"A:B0FHGY4T96-0","B0FHGY4T96","フラジャイル(30) (アフタヌーンコミックス)","恵三朗,草水敏","フラジャイル 病理医岸京一郎の所見","30","講談社","2025-07-23T00:00:00.000Z","2025-08-07T06:23:51+0000","MANGA","Unknown","フラジャイル030 (アフタヌーンコミックス)"
"A:B00AQY85PM-0","B00AQY85PM","フランケン・ふらん 1 (チャンピオンREDコミックス)","木々津克久","フランケン・ふらん","1","秋田書店","2007-11-01T00:00:00.000Z","2016-10-12T19:02:15+0000","MANGA","Unknown","フランケンフラン001  フランケンフラン (チャンピオンレッドコミックス)"
"A:B00AQY85U2-0","B00AQY85U2","フランケン・ふらん 2 (チャンピオンREDコミックス)","木々津克久","フランケン・ふらん","2","秋田書店","2008-07-01T00:00:00.000Z","2016-10-12T19:02:14+0000","MANGA","Unknown","フランケンフラン002  フランケンフラン (チャンピオンレッドコミックス)"
"A:B00AQY85UW-0","B00AQY85UW","フランケン・ふらん 3 (チャンピオンREDコミックス)","木々津克久","フランケン・ふらん","3","秋田書店","2009-02-01T00:00:00.000Z","2016-10-12T19:02:19+0000","MANGA","Unknown","フランケンフラン003  フランケンフラン (チャンピオンレッドコミックス)"
[
  {
    "bookId": "A:B00J919VBU-0",
    "asin": "B00J919VBU",
    "title": "りんたとさじ",
    "author": "オガツカヅオ",
    "seriesName": null,
    "seriesNumber": null,
    "publisher": "朝日新聞出版",
    "publicationDate": "2014-03-31T00:00:00.000Z",
    "purchaseDate": "2016-11-21T11:03:53+0000",
    "contentTags": [
      "MANGA",
      "COMICS"
    ],
    "language": "Unknown",
    "sortTitle": "リンタトサジ"
  },
  {
    "bookId": "A:B09C1WWXQF-0",
    "asin": "B09C1WWXQF",
    "title": "るなしい(1) (小説現代コミックス)",
    "author": "意志強ナツ子",
    "seriesName": "るなしい",
    "seriesNumber": "1",
    "publisher": "講談社",
    "publicationDate": "2021-08-23T00:00:00.000Z",
    "purchaseDate": "2024-06-03T02:28:53+0000",
    "contentTags": "MANGA",
    "language": "Unknown",
    "sortTitle": "ルナシイ001 (ショウセツゲンダイコミックス)"
  }
]

あとはjqなりで適宜加工やフィルタリングしましょう。

技術的な実装

データソース

Kindle for Macはデータをローカルに保存していますので、これを読むだけです。

~/Library/Containers/com.amazon.Lassen/Data/Library/Protected/BookData.sqlite

SQLiteファイルなので直接読めます。読むだけなら多分壊れないと思います。Kindle for Mac と同時起動はしないほうがいいかもしれません。

公式のドキュメントはないため、テーブル構造やフィールドの意味は独自調査です。 データベース構造の詳細は別記事にまとめました。 記事は細かく分割されていますが、読むべき必要があるのは一部のみなので大丈夫でしょう。

dominion525.hatenablog.jp

実装方針

書籍のメタデータ(タイトル、著者、購入日など)のみを取得します。本文やコンテンツには触りません。DRM回避でもありません。単なる蔵書管理ツールです。

ふつうの技術をふつうに使うことを心がけたので、特別な工夫はしていません。だってSQLiteをダンプするだけなんだし。

小さめのモジュールに分割して、各機能を独立してテストしやすくしました:

src/
├── db/reader.ts          # SQLite読み込み
├── converters/plist.ts   # plistデコード
├── converters/mapper.ts  # フィールドマッピング
├── formatters/csv.ts     # CSV出力(RFC 4180準拠)
└── formatters/json.ts    # JSON出力

テストカバレッジは今のところ90%くらいです。

指標 カバレッジ
全体 89.51%
Statements 89.51%
Branches 89.02%
Functions 93.75%
Lines 89.51%

なお、ストリーム処理とかもしてなくて、単にSQLiteから全件取得してガッと出力するだけです。せいぜい数千-数万件程度のテキストデータなのでまあいいかと。3500件のCSV出力でも秒だったし。

実装支援

本ツールは大部分Claude Codeの支援によって作成されています。 2.0(sonet4.5)以降、あんまりトークン使わない印象があるので、20Xじゃなくでもいいかもって気持ちにはなってきた。

npmパッケージ

npmに公開しました。

https://www.npmjs.com/package/kindle-title-exporter

グローバルインストールも可能ですが、しなくていいと思います。

なお、Github レポジトリは下記です。

github.com

免責

本ツールは非公式であり、AmazonまたはKindleとは一切関係がなく、承認や推奨を受けたものではありません。

「Kindle」はAmazon.com, Inc.の登録商標です。本ソフトウェアは、機能説明のためにのみ(Kindle for Macのデータベースファイルを読み取る)「Kindle」という名称を使用しています(nominative fair use)。本ツールは、デジタル著作権管理(DRM)やコピープロテクション機構を改変、回避、妨害するものではありません。

リンク

Kindle の蔵書一覧を取得する(5) / シリーズ情報の取り扱い

ようやくほぼ最後です。 シリーズの取り扱いについてです。基本的には便利なんですが、けっこうシリーズが設定されていないシリーズものがありそこそこ困ります。AIとか使いながらヒューリスティックに対応しないといけない雰囲気があるのですが、まあ、それはそれで別のお話。ひとまず、標準で持っているシリーズのお話です。

とはいえ、普通にLEFT JOINしようなってだけです。

シリーズ情報の取得方法

書籍のシリーズ情報を取得する方法を説明します。シリーズ情報はZGROUPZGROUPITEMZBOOKの3つのテーブルを使用して管理されています。

関連テーブルの構造

ZGROUP(シリーズマスタ)

シリーズの基本情報を格納するマスタテーブルです。

フィールド 説明
Z_PK INTEGER プライマリキー 123
ZGROUPID VARCHAR グループID(文字列) "B01FU3MY2S"
ZDISPLAYNAME VARCHAR シリーズ名🔥 "ワンパンマン"
ZDISPLAYAUTHOR VARCHAR シリーズ著者 "ONE/村田雄介"
ZSERIESTYPE VARCHAR シリーズタイプ "series"
ZRAWPUBLICATIONDATE INTEGER 出版日 1234567890
ZLASTACCESSTIME INTEGER 最終アクセス時刻 1234567890
ZSERIESCOVERIMAGE INTEGER 表紙画像への外部キー 456

重要フィールド: - ZDISPLAYNAME: ユーザーに表示するシリーズ名

ZGROUPITEM(書籍-シリーズ中間テーブル)

書籍とシリーズを紐付ける中間テーブルです。

フィールド 説明
Z_PK INTEGER プライマリキー 789
ZBOOK INTEGER 書籍への外部キー🔥 2940
ZPARENTCONTAINER INTEGER シリーズへの外部キー 123
ZPOSITION INTEGER シリーズ内位置(0始まり)🔥 0, 14, 33
ZPOSITIONLABEL VARCHAR 位置ラベル🔥 "1", "15", "34"
ZITEMID VARCHAR アイテムID "B01FU3MY2S"
ZRAWITEMCOLLECTIONTYPE VARCHAR アイテムコレクションタイプ "series"

重要フィールド: - ZBOOK: ZBOOK.Z_PKへの外部キー - ZPARENTCONTAINER: ZGROUP.Z_PKへの外部キー - ZPOSITION: シリーズ内の順序(0始まり) - ZPOSITIONLABEL: ユーザーに表示する位置("1"始まり)

ZBOOK(書籍テーブル)

書籍情報を格納するメインテーブル。シリーズ関連では以下のフィールドを持ちます。

フィールド 説明
Z_PK INTEGER プライマリキー 2940
ZGROUPID VARCHAR グループID "B01FU3MY2S"
ZDISPLAYTITLE VARCHAR タイトル "ワンパンマン 1"

注意: ZGROUPIDフィールドはありますが、実際のシリーズ情報取得にはZGROUPITEMを経由する必要があります。

リレーション構造

テーブル間の関係

ZBOOK (Z_PK)
  ↓
  ← ZGROUPITEM.ZBOOK (外部キー)
  ↓
ZGROUPITEM (ZPOSITION, ZPOSITIONLABEL)
  ↓
ZGROUPITEM.ZPARENTCONTAINER → ZGROUP.Z_PK (外部キー)
  ↓
ZGROUP (ZDISPLAYNAME)

関係の特徴

  1. 1対多の関係:

    • 1つのシリーズ(ZGROUP)は複数の書籍(ZBOOK)を持つ
    • 1つの書籍は1つのシリーズにのみ属する(または属さない)
  2. 中間テーブルの役割:

    • ZGROUPITEMが書籍とシリーズを紐付け
    • シリーズ内の位置情報も保持
  3. NULL値の意味:

    • シリーズ情報が未設定の書籍は、LEFT JOINの結果としてNULLとなる
    • 注意: 実際にはシリーズ作品でもデータベースに登録されていない場合がある

シリーズ情報の取得SQL

基本クエリ

SELECT
  ZBOOK.Z_PK,
  ZBOOK.ZDISPLAYTITLE,
  ZGROUP.ZDISPLAYNAME as series_name,
  ZGROUPITEM.ZPOSITION as series_position,
  ZGROUPITEM.ZPOSITIONLABEL as series_position_label
FROM ZBOOK
LEFT JOIN ZGROUPITEM ON ZBOOK.Z_PK = ZGROUPITEM.ZBOOK
LEFT JOIN ZGROUP ON ZGROUPITEM.ZPARENTCONTAINER = ZGROUP.Z_PK
ORDER BY ZBOOK.ZSORTTITLE ASC;

クエリの説明

  1. LEFT JOINの理由:

    • シリーズに属さない書籍も取得するため
    • INNER JOINだとシリーズに属する書籍のみになる
  2. 結合条件:

    • ZBOOK.Z_PK = ZGROUPITEM.ZBOOK: 書籍と中間テーブルを紐付け
    • ZGROUPITEM.ZPARENTCONTAINER = ZGROUP.Z_PK: 中間テーブルとシリーズマスタを紐付け
  3. 取得フィールド:

    • series_name: シリーズ名(ZGROUP.ZDISPLAYNAME
    • series_position: シリーズ内位置(0始まり、ZGROUPITEM.ZPOSITION
    • series_position_label: 位置ラベル("1"始まり、ZGROUPITEM.ZPOSITIONLABEL

全フィールドを含むクエリ

SELECT
  ZBOOK.*,
  ZGROUP.ZDISPLAYNAME as series_name,
  ZGROUPITEM.ZPOSITION as series_position,
  ZGROUPITEM.ZPOSITIONLABEL as series_position_label
FROM ZBOOK
LEFT JOIN ZGROUPITEM ON ZBOOK.Z_PK = ZGROUPITEM.ZBOOK
LEFT JOIN ZGROUP ON ZGROUPITEM.ZPARENTCONTAINER = ZGROUP.Z_PK
ORDER BY ZBOOK.ZSORTTITLE ASC;

シリーズのみを取得するクエリ

SELECT
  ZBOOK.ZDISPLAYTITLE,
  ZGROUP.ZDISPLAYNAME as series_name,
  ZGROUPITEM.ZPOSITIONLABEL as series_position_label
FROM ZBOOK
INNER JOIN ZGROUPITEM ON ZBOOK.Z_PK = ZGROUPITEM.ZBOOK
INNER JOIN ZGROUP ON ZGROUPITEM.ZPARENTCONTAINER = ZGROUP.Z_PK
WHERE ZGROUP.ZDISPLAYNAME IS NOT NULL
ORDER BY ZGROUP.ZDISPLAYNAME, ZGROUPITEM.ZPOSITION;

フィールド詳細

series_name(ZGROUP.ZDISPLAYNAME)

  • : VARCHAR
  • 説明: シリーズの表示名
  • : "ワンパンマン", "LIAR GAME", "明日の敵と今日の握手を【電子単行本】"
  • NULL値: シリーズに属さない書籍の場合

series_position(ZGROUPITEM.ZPOSITION)

  • : INTEGER
  • 説明: シリーズ内の位置(0始まり)
  • : 0, 1, 14, 33
  • NULL値: シリーズに属さない書籍の場合

series_position_label(ZGROUPITEM.ZPOSITIONLABEL)

  • : VARCHAR
  • 説明: ユーザー表示用の位置ラベル
  • : "1", "15", "34"
  • 特徴: series_position + 1の文字列表現(多くの場合)
  • NULL値: シリーズに属さない書籍の場合
  • 手元で確認した限りにおいては、すべていわゆる巻数と一致していました。

位置とラベルの関係

series_position series_position_label 意味
0 "1" シリーズ第1巻
14 "15" シリーズ第15巻
33 "34" シリーズ第34巻
NULL NULL シリーズなし

実例

ワンパンマンシリーズ

SELECT
  ZBOOK.ZDISPLAYTITLE,
  ZGROUP.ZDISPLAYNAME as series_name,
  ZGROUPITEM.ZPOSITION,
  ZGROUPITEM.ZPOSITIONLABEL
FROM ZBOOK
INNER JOIN ZGROUPITEM ON ZBOOK.Z_PK = ZGROUPITEM.ZBOOK
INNER JOIN ZGROUP ON ZGROUPITEM.ZPARENTCONTAINER = ZGROUP.Z_PK
WHERE ZGROUP.ZDISPLAYNAME = 'ワンパンマン'
ORDER BY ZGROUPITEM.ZPOSITION;

結果例:

タイトル                                    | series_name | ZPOSITION | ZPOSITIONLABEL
-------------------------------------------|-------------|-----------|---------------
ワンパンマン 1 (ジャンプコミックスDIGITAL)  | ワンパンマン | 0         | 1
ワンパンマン 2 (ジャンプコミックスDIGITAL)  | ワンパンマン | 1         | 2
...
ワンパンマン 34 (ジャンプコミックスDIGITAL) | ワンパンマン | 33        | 34

LIAR GAMEシリーズ

タイトル                                   | series_name | ZPOSITION | ZPOSITIONLABEL
------------------------------------------|-------------|-----------|---------------
LIAR GAME 9 (ヤングジャンプコミックスDIGITAL)  | LIAR GAME   | 8         | 9
LIAR GAME 10 (ヤングジャンプコミックスDIGITAL) | LIAR GAME   | 9         | 10
LIAR GAME 15 (ヤングジャンプコミックスDIGITAL) | LIAR GAME   | 14        | 15
LIAR GAME 17 (ヤングジャンプコミックスDIGITAL) | LIAR GAME   | 16        | 17

シリーズなしの書籍

タイトル                          | series_name | ZPOSITION | ZPOSITIONLABEL
---------------------------------|-------------|-----------|---------------
一九八四年 (ハヤカワepi文庫)      | NULL        | NULL      | NULL
アンチマン 岡田索雲短編集        | NULL        | NULL      | NULL

関連テーブル

ZSERIESAUTHOR(シリーズ著者情報)

シリーズの著者情報を格納します(複数著者対応)。

SELECT
  ZGROUP.ZDISPLAYNAME,
  ZSERIESAUTHOR.ZAUTHORNAME,
  ZSERIESAUTHOR.ZAUTHORPRONUNCIATION
FROM ZGROUP
LEFT JOIN ZSERIESAUTHOR ON ZGROUP.Z_PK = ZSERIESAUTHOR.ZSERIES
WHERE ZGROUP.ZDISPLAYNAME = 'ワンパンマン';

ZSERIESIMAGE(シリーズ画像情報)

シリーズの表紙画像情報を格納します。

SELECT
  ZGROUP.ZDISPLAYNAME,
  ZSERIESIMAGE.ZIMAGEID,
  ZSERIESIMAGE.ZEXTENSION
FROM ZGROUP
LEFT JOIN ZSERIESIMAGE ON ZGROUP.Z_PK = ZSERIESIMAGE.ZSERIES
WHERE ZGROUP.ZDISPLAYNAME = 'ワンパンマン';

注意点

NULL値の扱い

  • シリーズ情報が未設定の書籍はseries_nameNULL
  • 注意: 実際にはシリーズ作品でもデータベースに登録されていない場合がある

位置とラベルの関係

  • 常にseries_position + 1 = series_position_labelの関係が成立すると思われる
  • そのため表示にはseries_position_labelを使った方が分かりやすい

免責

  • 本情報は、筆者が独自に調査・検証した非公式情報であり、AmazonまたはKindleとは一切関係ありません。
  • 正確性の保証はありません。誤りや不適切な認識が含まれる可能性があります。
  • 特定の環境・バージョンでの検証結果であり、すべての環境での動作を保証するものではありません。
  • 本記事の内容を利用した結果生じたいかなる損害についても、筆者は一切の責任を負いません。
  • 「Kindle」はAmazon.com, Inc.の登録商標です。本情報は、私的利用の範囲において自身の蔵書情報を参照するものであり、デジタル著作権管理(DRM)やコピープロテクション機構を改変、回避、妨害するものではありません。

Kindle の蔵書一覧を取得する(4) / plist 形式の取り扱い

続きです。今回はplist形式の取り扱いだけど、一般的なものなので知らなくても多分大丈夫ですよ。

ZSYNCMETADATAATTRIBUTES(plist)について

plistとは

  • フルネーム: Property List(プロパティリスト)
  • 形式: NSKeyedArchiver形式のバイナリプロパティリスト
  • プラットフォーム: macOS/iOSで一般的なデータシリアライゼーション形式
  • 用途: 書籍のメタデータを構造化して格納
  • サイズ: 通常1,000-2,000 bytes程度

詳しくはこちらを読んでください。

ja.wikipedia.org

なぜplistが重要か

以下の情報はplistからしか取得できません

  1. 著者名(人間が読める形式)⭐
    • ZBOOKのZDISPLAYAUTHORはハッシュ値/ID
    • plistのauthors.authorが実際の著者名
  2. 購入日時(ISO 8601形式)⭐
    • ZBOOKには購入日フィールドがない
  3. 純粋なASIN(プレフィックスなし)
    • ZBOOKのZBOOKIDは"A:B009DEMC8W-0"
    • plistのASINは"B009DEMC8W"

plistの構造

NSKeyedArchiveの階層

バイナリplistは以下の階層構造を持ちます:

{
  "$version": 100000,
  "$archiver": "NSKeyedArchiver",
  "$top": {
    "root": {
      "UID": 1
    }
  },
  "$objects": [
    "$null",
    {書籍メタデータのルートオブジェクト},
    {属性辞書},
    ...
  ]
}

UID参照の仕組み

  • $objects配列にすべてのオブジェクトが格納
  • 各オブジェクトは{"UID": n}形式で他のオブジェクトを参照
  • 再帰的に辿ることで完全なデータ構造を復元

辞書と配列の表現

辞書(NSDictionary):

{
  "NS.keys": [{"UID": 3}, {"UID": 4}],
  "NS.objects": [{"UID": 5}, {"UID": 6}],
  "$class": {"UID": 7}
}

配列(NSArray):

{
  "NS.objects": [{"UID": 8}, {"UID": 9}],
  "$class": {"UID": 10}
}

含まれる情報の完全リスト

基本情報

フィールド 説明 ZBOOKとの関係
ASIN 文字列 純粋なASIN "B009DEMC8W" ZBOOKIDから抽出可能だがプレフィックス付き
title 文字列 タイトル "一九八四年 (ハヤカワepi文庫)" ZDISPLAYTITLEと完全一致
authors.author 文字列/配列 著者 ["ONE", "村田雄介"] ⭐plistのみ、ZDISPLAYAUTHORはハッシュ値
publishers.publisher 文字列 出版社 "早川書房" ZRAWPUBLISHERと完全一致
purchase_date 文字列 購入日時 "2018-09-24T04:45:07+0000" ⭐plistのみ

コンテンツ情報

フィールド 説明 ZBOOKとの関係
content_type 文字列 MIMEタイプ "application/x-mobipocket-ebook" ZMIMETYPEと同じ
content_tags.tag 配列 コンテンツタグ ["DICT", "FREE_DICT"] ZCONTENTTAGSは";DICT;FREE_DICT"形式
cde_contenttype 文字列 CDEコンテンツタイプ "EBOK" plistのみ
content_size 文字列 ファイルサイズ "18755584" ZRAWFILESIZEは数値
target_language 文字列 対象言語 "en", "ja" ZLANGUAGEと類似

入手元情報

フィールド 説明
origins.origin.type 文字列 入手元タイプ "KindleDictionary", "Purchase"
origins.origin.id 文字列 入手元ID "Freebie", ASIN

その他の情報

フィールド 説明
short_item_name 文字列 短縮名 "Hindi-English"
accessibility_description 文字列 アクセシビリティ説明 "Hindi to English"
bisac_subject_description_code.code 文字列/配列 BISAC分類コード ["BUS000000", "BUS010000"]
default_dict_for_locales 文字列 辞書のデフォルトロケール "hi_IN", "es_ES, es_MX"

ZBOOKフィールドとの比較

plistのみに存在する情報

  1. 著者名(文字列形式)

    • plist: authors.author"ジョージ・オーウェル" または ["ONE", "村田雄介"]
    • ZBOOK: ZDISPLAYAUTHOR → ハッシュ値(6946abd8...
  2. 購入日時

    • plist: purchase_date"2018-09-24T04:45:07+0000"
    • ZBOOK: 該当フィールドなし
  3. 純粋なASIN

    • plist: ASIN"B009DEMC8W"
    • ZBOOK: ZBOOKID"A:B009DEMC8W-0"(プレフィックス・サフィックス付き)

完全一致する情報

  1. タイトル

    • plist: title"一九八四年 (ハヤカワepi文庫)"
    • ZBOOK: ZDISPLAYTITLE"一九八四年 (ハヤカワepi文庫)"
    • 差異: 0件/3,833件(100%一致)
  2. 出版社

    • plist: publishers.publisher"早川書房"
    • ZBOOK: ZRAWPUBLISHER"早川書房"
    • 差異: 0件/3,833件(100%一致)

形式が異なる情報

  1. コンテンツタグ

    • plist: content_tags.tag["DICT", "FREE_DICT"](配列)
    • ZBOOK: ZCONTENTTAGS";DICT;FREE_DICT"(セミコロン区切り文字列)
    • 先頭のセミコロンの有無が異なる
  2. ファイルサイズ

    • plist: content_size"18755584"(文字列)
    • ZBOOK: ZRAWFILESIZE18755584(整数)

著者情報の詳細

ZDISPLAYAUTHORとの違い

ZDISPLAYAUTHOR(BLOB): - ハッシュ値またはID - サイズ: 16-48 bytes - 例: 6946abd8db4a0ba12ca67a5ded9830876eae4caa1b46c47e044bdd79535aef0b - 人間が読めない

plist authors.author: - 実際の著者名 - 文字列または配列 - 例: "ジョージ・オーウェル" または ["ONE", "村田雄介"] - 人間が読める

単一著者の場合

データ型: 文字列

:

{
  "authors": {
    "author": "ジョージ・オーウェル"
  }
}

実例: - 一九八四年: "ジョージ・オーウェル" - LIAR GAME 15: "甲斐谷忍" - 路傍のフジイ(5): "鍋倉夫"

複数著者の場合

データ型: 配列

:

{
  "authors": {
    "author": ["ONE", "村田雄介"]
  }
}

実例: - ワンパンマン 34: ["ONE", "村田雄介"] - 株式会社マジルミエ 18: ["岩田雪花", "青木 裕"] - ケーキの切れない非行少年たち 11巻: ["宮口幸治", "鈴木マサカズ"] - 明日の敵と今日の握手を 7: ["フクダイクミ", "カルロ・ゼン"] - 怪談和尚 妖異の声: ["三木大雲", "森野達弥"]

デコード方法

NSKeyedArchive形式のバイナリplistは、bplist-parserなどのライブラリを使用してデコードできます。

主なライブラリ: - Node.js: bplist-parser (npm) - Python: biplist (PyPI)

実データ例

例1: 単一著者(一九八四年)

{
  "ASIN": "B009DEMC8W",
  "title": "一九八四年 (ハヤカワepi文庫)",
  "authors": {
    "author": "ジョージ・オーウェル"
  },
  "publishers": {
    "publisher": "早川書房"
  },
  "purchase_date": "2018-09-24T04:45:07+0000",
  "content_type": "application/x-mobipocket-ebook",
  "content_size": "2746368"
}

例2: 複数著者(ワンパンマン 34)

{
  "ASIN": "B0FGBZ6BLM",
  "title": "ワンパンマン 34 (ジャンプコミックスDIGITAL)",
  "authors": {
    "author": ["ONE", "村田雄介"]
  },
  "publishers": {
    "publisher": "集英社"
  },
  "purchase_date": "2025-09-03T15:04:26+0000",
  "content_type": "application/x-mobipocket-ebook",
  "content_tags": {
    "tag": ["MANGA"]
  },
  "content_size": "15350784"
}

例3: 辞書(Prabhat Advanced Hindi English Dictionary)

{
  "ASIN": "B06W9KK63F",
  "title": "Prabhat Advanced Hindi English Dictionary (Hindi Edition)",
  "authors": {
    "author": "Prabhat Prakashan"
  },
  "publishers": {
    "publisher": "Amazon Dictionaries"
  },
  "purchase_date": "2017-12-08T17:56:30+0000",
  "target_language": "en",
  "content_tags": {
    "tag": ["DICT", "FREE_DICT"]
  },
  "origins": {
    "origin": {
      "type": "KindleDictionary",
      "id": "Freebie"
    }
  },
  "content_type": "application/x-mobipocket-ebook",
  "short_item_name": "Hindi-English",
  "accessibility_description": "Hindi to English",
  "bisac_subject_description_code": {
    "code": ["BUS000000", "BUS010000"]
  },
  "content_size": "18755584",
  "default_dict_for_locales": "hi_IN"
}

注意点

著者フィールドの型

authors.author文字列または配列のどちらかです: - 単一著者: "ジョージ・オーウェル" (文字列) - 複数著者: ["ONE", "村田雄介"] (配列)

免責

  • 本情報は、筆者が独自に調査・検証した非公式情報であり、AmazonまたはKindleとは一切関係ありません。
  • 正確性の保証はありません。誤りや不適切な認識が含まれる可能性があります。
  • 特定の環境・バージョンでの検証結果であり、すべての環境での動作を保証するものではありません。
  • 本記事の内容を利用した結果生じたいかなる損害についても、筆者は一切の責任を負いません。
  • 「Kindle」はAmazon.com, Inc.の登録商標です。本情報は、私的利用の範囲において自身の蔵書情報を参照するものであり、デジタル著作権管理(DRM)やコピープロテクション機構を改変、回避、妨害するものではありません。

Kindle の蔵書一覧を取得する(3) / ZBOOKテーブルの解説

前回の続きで、一番のメインになるZBOOKです。 とは言っても役に立つ範囲は少ないんですが。

ざっくり解説

  • ほとんどの項目が使えないので、結構残念です。
    • 使えるのは下記くらいかな。
      • ZBOOKID(ASIN風ID)
      • ZDISPLAYTITLE(書名)
      • ZSORTTITLE(ソート用タイトル/カタカナ)
      • ZRAWPUBLISHER(出版社)
      • ZRAWPUBLICATIONDATE(出版日)
    • あとギリでZCONTENTTAGS(コンテンツタグ)くらいかな
  • ASINもZBOOKIDだと謎のプレフィクス/サフィックスがついているので、plist見た方が堅い気がする
    • A:B0BX2GTPKY-0 みたいな感じでA-{ASIN}-0みたいになってる。
  • 著者名とかはZSYNCMETADATAATTRIBUTESにあるけど、plistになってるから別の記事を参照して下さい。

dominion525.hatenablog.jp

↓ 以下個別解説

フィールド一覧(カテゴリ別)

システム項目(3フィールド)

実質使いません。

Z_ENT

  • : INTEGER
  • 説明: エンティティタイプ識別子
  • : 通常 2
  • 用途: システム内部で使用

Z_OPT

  • : INTEGER
  • 説明: 最適化・バージョン管理用
  • : 通常 1
  • 用途: システム内部で使用

Z_PK

  • : INTEGER PRIMARY KEY
  • 説明: プライマリキー
  • : 1から始まる連番
  • 用途: レコードの一意識別
  • : 2940

識別情報(5フィールド)

🔥ZBOOKID

  • : VARCHAR
  • 説明: Book ID / ASIN(プレフィックス・サフィックス付き)
  • 形式: "A:{ASIN}-0"
  • : "A:B009DEMC8W-0", "A:B01FU3MY2S-0"
  • : 純粋なASINはplistのASINフィールドにある

ZPARENTASIN

  • : VARCHAR
  • 説明: 親ASIN(シリーズやバンドルの親書籍)
  • : ASINまたはNULL
  • : NULL(多くの書籍)

ZPERASINGUID

  • : VARCHAR
  • 説明: ASIN別のGUID(グローバル一意識別子)
  • : GUIDまたはNULL
  • : NULL(多くの書籍)

ZMANIFESTGUID

  • : VARCHAR
  • 説明: マニフェストファイルのGUID
  • : GUIDまたはNULL
  • : NULL(多くの書籍)

ZGROUPID

  • : VARCHAR
  • 説明: グループID(シリーズID)
  • : シリーズIDまたはNULL
  • : NULL(シリーズに属さない書籍)
  • ** シリーズの扱いは別の記事で。

書誌情報(8フィールド)

🔥ZDISPLAYTITLE

  • : VARCHAR
  • 説明: 表示用タイトル
  • : "一九八四年 (ハヤカワepi文庫)"
  • : plistのtitleと完全一致

🔥ZSORTTITLE

  • : VARCHAR
  • 説明: ソート用タイトル(カタカナ表記など)
  • : "センキュウヒャクハチジュウヨネン" (一九八四年のソート用)
  • 用途: 書籍のソート順決定

ZALTERNATESORTTITLE

  • : VARCHAR
  • 説明: 代替ソートタイトル
  • : "一九八四年"
  • : 多くの場合ZDISPLAYTITLEと同じ、またはNULLなので、使わなくていいと思う。

🔥ZRAWPUBLISHER

  • : VARCHAR
  • 説明: 出版社名
  • : "早川書房", "集英社", "Amazon Dictionaries"
  • : plistのpublishers.publisherと完全一致

ZLANGUAGE

  • : VARCHAR
  • 説明: 書籍の言語コード
  • : ISO 639言語コードまたは"Unknown"
  • : "ja", "en", "Unknown"
  • けっこう雑いのであまり当てにならない

ZRAWPUBLICATIONDATE

  • : INTEGER
  • 説明: 出版日
  • 形式: Unix タイムスタンプ(1970-01-01 00:00:00 UTCからの秒数)
  • : 1353283200 (2012-11-19)
  • : 0は「データなし」を意味する

ZCONTENTTAGS

  • : VARCHAR
  • 説明: コンテンツタグ(セミコロン区切り)
  • 形式: ;TAG1;TAG2;TAG3
  • : ";DICT;FREE_DICT", ";MANGA", ""(空文字列)
  • コミックは大抵、";MANGA"";MANGA;COMIC"、稀に単体の";COMIC"
  • plist の方が配列形式なので使いやすいと思う。

🔥ZGROUPID

  • : VARCHAR
  • 説明: グループID(シリーズを識別)
  • : グループIDまたはNULL
  • 用途: ZGROUPテーブルとの紐付け

ファイル情報(4フィールド)

実質使わないです。

ZPATH

  • : VARCHAR
  • 説明: 書籍ファイルのパス(相対パス)
  • 形式: Library/eBooks/{ASIN}/{GUID}
  • : "Library/eBooks/B00P6OA1MC/0889F48F-DE39-46E2-AAF9-FA825EB14..."

ZBUNDLEPATH

  • : VARCHAR
  • 説明: バンドルファイルのパス
  • : 多くの場合NULL
  • : NULL

ZMIMETYPE

  • : VARCHAR
  • 説明: ファイルのMIMEタイプ
  • : "application/x-mobipocket-ebook", "application/pdf"など
  • : "application/x-mobipocket-ebook"
  • : plistのcontent_typeと同じ

ZRAWFILESIZE

  • : INTEGER
  • 説明: ファイルサイズ(バイト単位)
  • : 2746368 (約2.6MB)

読書進捗(5フィールド)

確認してないですね。

ZRAWCURRENTPOSITION

  • : INTEGER
  • 説明: 現在の読書位置
  • : 位置またはNULL(未読の場合)
  • : NULL, 0, 523

ZRAWMAXPOSITION

  • : INTEGER
  • 説明: 書籍の最大位置
  • : 0, 1000, 5280

ZLONGCURRENTPOSITION

  • : VARCHAR
  • 説明: 現在位置(長整数として文字列保存)
  • : 大きな値の場合に使用、通常NULL
  • : NULL

ZLONGMAXPOSITION

  • : VARCHAR
  • 説明: 最大位置(長整数として文字列保存)
  • : 大きな値の場合に使用、通常NULL
  • : NULL

ZRAWMAXLOCATION

  • : INTEGER
  • 説明: 最大ロケーション
  • : 0

状態・フラグ(10フィールド)

ZRAWISUNREAD

  • : INTEGER
  • 説明: 未読フラグ
  • : 1 = 未読, 0 = 既読, NULL
  • : 1(未読)
  • のはずなんだけど、有意な値が入っていないぽさがあった。

ZRAWREADSTATE

  • : INTEGER
  • 説明: 読書状態
  • : 0, 1, 2など(詳細不明)
  • : 0

ZRAWBOOKSTATE

  • : INTEGER
  • 説明: 書籍の状態
  • : 0, 1, 2など(詳細不明)
  • : 0

ZRAWISKEPT

  • : INTEGER
  • 説明: 保持フラグ(デバイスに保持するかどうか)
  • : 1 = 保持, NULL = デフォルト
  • : NULL

ZRAWISHIDDEN

  • : INTEGER
  • 説明: 非表示フラグ(システムによる非表示)
  • : 1 = 非表示, 0 = 表示
  • : 0

ZISHIDDENBYUSER

  • : INTEGER
  • 説明: ユーザーによる非表示フラグ
  • : 1 = 非表示, 0 = 表示
  • : 0

ZRAWISARCHIVABLE

  • : INTEGER
  • 説明: アーカイブ可能フラグ
  • : 1 = 可能, 0 = 不可
  • : 1

ZRAWISDICTIONARY

  • : INTEGER
  • 説明: 辞書フラグ
  • : 1 = 辞書, NULL = 通常書籍
  • : NULL(通常書籍), 1(辞書)

ZRAWISMULTIMEDIA

  • : INTEGER
  • 説明: マルチメディアフラグ
  • : 1 = マルチメディア, 0 = テキスト
  • : 0

ZRAWISENCRYPTED

  • : INTEGER
  • 説明: 暗号化フラグ
  • : 1 = 暗号化, 0 = 非暗号化
  • : 0

時刻情報(2フィールド)

ZRAWLASTACCESSTIME

  • : INTEGER
  • 説明: 最終アクセス日時(Unix timestamp)
  • 形式: 1970-01-01 00:00:00 UTCからの秒数
  • : 1512755790 (2017-12-08 17:56:30 UTC)
  • 変換: JavaScript Date = new Date(unixTimestamp * 1000)

ZRAWTIMEDURATIONRESTRICTIONENDDATE

  • : TIMESTAMP
  • 説明: 時間制限の終了日
  • : 多くの場合NULL
  • : NULL

書籍種別(1フィールド)

ZRAWBOOKTYPE

  • : INTEGER
  • 説明: 書籍のタイプ
  • : 10 = 通常書籍、その他の値は用途不明
  • : 10

その他(3フィールド)

ZSHELF

  • : VARCHAR
  • 説明: 本棚情報
  • : 多くの場合NULL
  • : NULL

ZWATERMARK

  • : VARCHAR
  • 説明: 透かし情報
  • : 多くの場合NULL
  • : NULL

ZRAWADS

  • : VARCHAR
  • 説明: 広告情報
  • : 多くの場合NULL
  • : NULL

辞書専用項目(5フィールド)

ZDICTIONARYLOCALEID

  • : VARCHAR
  • 説明: 辞書のロケールID
  • : NULL(通常書籍), "hi_IN"(ヒンディー語辞書)

ZDICTIONARYSHORTTITLE

  • : VARCHAR
  • 説明: 辞書の短縮タイトル
  • : NULL(通常書籍)

ZDICTIONARYSOURCELANG

  • : VARCHAR
  • 説明: 辞書のソース言語
  • : NULL(通常書籍)

ZRAWISTRANSLATIONDICTIONARY

  • : INTEGER
  • 説明: 翻訳辞書フラグ
  • : 1 = 翻訳辞書, NULL = その他
  • : NULL

ZRAWISSMDCREATEDDICTIONARY

  • : INTEGER
  • 説明: SMD作成辞書フラグ(詳細不明)
  • : 1 = SMD作成, NULL = その他
  • : NULL

コンパニオン関連(2フィールド)

ZCOMPANION

  • : INTEGER
  • 説明: コンパニオンID(外部キー)
  • : 外部キーまたはNULL
  • : NULL

ZRAWHASCOMPANION

  • : INTEGER
  • 説明: コンパニオン有無フラグ
  • : 1 = あり, NULL = なし
  • : NULL

読書・表示モード(3フィールド)

ZRAWREADINGMODE

  • : INTEGER
  • 説明: 読書モード
  • : 0, 1, 2など(詳細不明)
  • : 0

ZRAWLASTVIEW

  • : INTEGER
  • 説明: 最終表示モード
  • : 0, 1, 2など(詳細不明)
  • : 0

ZRAWLASTOPENSUCCEEDED

  • : INTEGER
  • 説明: 最終オープン成功フラグ
  • : 1 = 成功, 0 = 失敗
  • : 1

アップグレード・検証(2フィールド)

ZRAWBOOKUPGRADESNEEDED

  • : INTEGER
  • 説明: アップグレード必要フラグ
  • : 1 = 必要, NULL = 不要
  • : NULL

ZRAWISPENDINGVERIFICATION

  • : INTEGER
  • 説明: 検証待ちフラグ
  • : 1 = 検証待ち, 0 = 検証済み
  • : 0

自動整理・分類(3フィールド)

ZRAWAUTOSHELVE

  • : INTEGER
  • 説明: 自動本棚配置フラグ
  • : 1 = 自動配置, NULL = 手動
  • : NULL

ZRAWPROGRESSDOTCATEGORY

  • : INTEGER
  • 説明: 進捗ドットカテゴリ
  • : 数値(用途不明)
  • : 32767

ZRAWUSERVISIBLELABELING

  • : INTEGER
  • 説明: ユーザー表示ラベリング
  • : 0, 1など(詳細不明)
  • : 0

技術的フラグ(5フィールド)

ZRAWERL

  • : INTEGER
  • 説明: ERL(詳細不明)
  • : 通常-1
  • : -1

ZRAWHASFIXEDMOPHIGHLIGHTS

  • : INTEGER
  • 説明: 固定MOPハイライトフラグ(詳細不明)
  • : 1 = あり, NULL = なし
  • : NULL

ZRAWINBOOKCOVERCHECKED

  • : INTEGER
  • 説明: ブック内カバーチェック済みフラグ
  • : 1 = チェック済み, 0 = 未チェック
  • : 0

ZRAWNCXINFOSTORED

  • : INTEGER
  • 説明: NCX情報格納済みフラグ(ePubのナビゲーション)
  • : 1 = 格納済み, 0 = 未格納
  • : 0

ZRAWREADSTATEORIGIN

  • : INTEGER
  • 説明: 読書状態の起源(同期元など)
  • : 0, 1, 2など(詳細不明)
  • : 0

BLOB項目(7フィールド)

これらのフィールドはバイナリデータとして保存されており、直接読み取ることができません。

ZDISPLAYAUTHOR

  • : BLOB
  • 説明: 表示著者のハッシュ値またはID
  • サイズ: 16-48 bytes
    • 単一著者: 16 bytes
    • 複数著者: 32-48 bytes
  • : 6946abd8db4a0ba12ca67a5ded9830876eae4caa1b46c47e044bdd79535aef0b(16進数)
  • : 人間が読める著者名はplistのauthors.authorにあり

ZSORTAUTHOR

  • : BLOB
  • 説明: ソート用著者情報
  • サイズ: 16-48 bytes
  • 用途: 著者名によるソート
  • 実質使えない

ZALTERNATESORTAUTHOR

  • : BLOB
  • 説明: 代替ソート著者情報
  • サイズ: 16-48 bytes

ZEXTENDEDMETADATA

  • : BLOB
  • 説明: 拡張メタデータ
  • : 多くの場合NULL
  • : NULL

ZORIGINS

  • : BLOB
  • 説明: 入手元情報
  • サイズ: 251 bytes程度
  • : バイナリデータ

ZSYNCMETADATAATTRIBUTES

  • : BLOB
  • 説明: 同期メタデータ属性(NSKeyedArchiver形式のplist)⭐最重要
  • サイズ: 1,000-2,000 bytes程度
  • 含まれる情報:
    • 著者名(文字列または配列)
    • 購入日時(ISO 8601形式)
    • 純粋なASIN
    • 出版社
    • コンテンツタイプ
    • その他多数
  • デコード: bplist-parserなどのツールが必要
  • **別記事参照

ZRAWTITLEDETAILSJSON

  • : BLOB
  • 説明: タイトル詳細のJSON
  • : 多くの場合NULL
  • : NULL

実データ例

例1: 通常書籍(一九八四年)

Z_PK: 2940
ZBOOKID: A:B00P6OA1MC-0
ZDISPLAYTITLE: 一九八四年
ZSORTTITLE: センキュウヒャクハチジュウヨネン
ZRAWPUBLISHER: オープンシェルフパブリッシング
ZLANGUAGE: Unknown
ZRAWPUBLICATIONDATE: 1414863211
ZMIMETYPE: application/x-mobipocket-ebook
ZRAWFILESIZE: 2746368
ZRAWISUNREAD: 1
ZRAWISDICTIONARY: NULL
ZGROUPID: NULL

例2: シリーズ書籍(ワンパンマン 34)

Z_PK: 1234
ZBOOKID: A:B0FGBZ6BLM-0
ZDISPLAYTITLE: ワンパンマン 34 (ジャンプコミックスDIGITAL)
ZRAWPUBLISHER: 集英社
ZCONTENTTAGS: ;MANGA
ZGROUPID: (シリーズID)

シリーズ情報の取得方法は別記事を参照。


フィールド使用上の注意点

タイムスタンプの変換

ZRAWPUBLICATIONDATEなどのタイムスタンプフィールドは、Unix タイムスタンプ(1970-01-01 00:00:00 UTC からの秒数)形式です。

const date = new Date(unixTimestamp * 1000);

注意: 0の値は「データなし」を意味する。

BLOB項目の扱い

BLOB項目は基本的に直接読み取ることができません。ただし、一部のBLOBフィールド(ZSYNCMETADATAATTRIBUTESなど)はplist形式であることが判明しており、plistデコーダーを使用することで内容を取得できます。

dominion525.hatenablog.jp

NULL値の意味

  • NULLは「未設定」「該当なし」「デフォルト値」を意味します
  • 多くのフラグフィールドでは、NULLが最も一般的な値です
  • シリーズやコレクションに属さない書籍では、関連フィールドがNULLになります

免責

  • 本情報は、筆者が独自に調査・検証した非公式情報であり、AmazonまたはKindleとは一切関係ありません。
  • 正確性の保証はありません。誤りや不適切な認識が含まれる可能性があります。
  • 特定の環境・バージョンでの検証結果であり、すべての環境での動作を保証するものではありません。
  • 本記事の内容を利用した結果生じたいかなる損害についても、筆者は一切の責任を負いません。
  • 「Kindle」はAmazon.com, Inc.の登録商標です。本情報は、私的利用の範囲において自身の蔵書情報を参照するものであり、デジタル著作権管理(DRM)やコピープロテクション機構を改変、回避、妨害するものではありません。

Kindle の蔵書一覧を取得する(2) / テーブルの概要

前の記事 の続きです。データベース全体をざっくりみていきましょう。


ER図

定義はこちら→ Kindle for Mac が使っているデータベース(BookData.sqlite.sql)のschema · GitHub

erDiagram
    ZBOOK ||--o| ZCOMPANION : "has"
    ZBOOK ||--o{ ZCOLLECTIONITEM : "belongs to"
    ZBOOK ||--o{ ZGROUPITEM : "belongs to"
    ZBOOK ||--o| ZBOOKEXT : "extended by"
    ZBOOK ||--o| ZBOOKUPDATE : "has update"

    ZCOLLECTIONV2 ||--o{ ZCOLLECTIONITEM : "contains"

    ZGROUP ||--o{ ZGROUPITEM : "contains"
    ZGROUP ||--o| ZSERIESIMAGE : "has cover"
    ZGROUP ||--o{ ZSERIESAUTHOR : "has authors"

    ZARTICLE }o--|| ZBOOK : "raw articles"

    ZBOOK {
        int Z_PK PK
        varchar ZBOOKID "Book ID"
        varchar ZDISPLAYTITLE "Title"
        blob ZDISPLAYAUTHOR "Author"
        varchar ZPATH "File Path"
        varchar ZPARENTASIN "ASIN"
        varchar ZRAWPUBLISHER "Publisher"
        varchar ZLANGUAGE "Language"
        int ZRAWPUBLICATIONDATE "Publication Date"
        int ZRAWBOOKSTATE "Book State"
        int ZRAWCURRENTPOSITION "Reading Position"
        int ZRAWMAXPOSITION "Max Position"
        int ZRAWLASTACCESSTIME "Last Access"
        int ZRAWISUNREAD "Is Unread"
        blob ZEXTENDEDMETADATA "Extended Metadata"
    }

    ZBOOKEXT {
        int Z_PK PK
        varchar ZBOOKID FK
        varchar ZDOWNLOADORIGIN "Download Origin"
    }

    ZBOOKUPDATE {
        int Z_PK PK
        varchar ZBOOKID FK
        int ZRAWUPDATESTATE "Update State"
        timestamp ZLASTUPDATEATTEMPT "Last Update"
    }

    ZCOLLECTIONITEM {
        int Z_PK PK
        int ZBOOK FK
        int ZCOLLECTION FK
        float ZRAWORDER "Order"
        varchar ZCOLLECTIONID "Collection ID"
        varchar ZITEMID "Item ID"
    }

    ZCOLLECTIONV2 {
        int Z_PK PK
        varchar ZCOLLECTIONID "Collection ID"
        varchar ZNAME "Collection Name"
        varchar ZCONTENTTYPE "Content Type"
        int ZRAWLASTACCESSTIME "Last Access"
    }

    ZGROUP {
        int Z_PK PK
        varchar ZGROUPID "Group/Series ID"
        varchar ZDISPLAYNAME "Series Name"
        varchar ZDISPLAYAUTHOR "Author"
        varchar ZSERIESTYPE "Series Type"
        int ZLASTACCESSTIME "Last Access"
        int ZRAWPUBLICATIONDATE "Publication Date"
    }

    ZGROUPITEM {
        int Z_PK PK
        int ZBOOK FK
        int ZPARENTCONTAINER FK
        int ZPOSITION "Position in Series"
        varchar ZPOSITIONLABEL "Position Label"
    }

    ZSERIESAUTHOR {
        int Z_PK PK
        int ZSERIES FK
        varchar ZAUTHORNAME "Author Name"
        varchar ZAUTHORPRONUNCIATION "Pronunciation"
    }

    ZSERIESIMAGE {
        int Z_PK PK
        int ZSERIES FK
        varchar ZIMAGEID "Image ID"
        varchar ZEXTENSION "File Extension"
    }

    ZARTICLE {
        int Z_PK PK
        int Z2RAWARTICLES FK
        int ZRAWINDEX "Index"
        int ZRAWISUNREAD "Is Unread"
    }

ざっくり解説

  • ZBOOK(書籍テーブル)だけ見ればまずはOKです。
    • 書名だけならこれでなんとかなります。
    • 一部バイナリ情報(plist形式)があるので注意
    • ASINはZBOOKIDを加工すれば抽出できるけど、本来の形式はplistの中に書かれてます。
  • シリーズ情報はZGROUP(シリーズマスタ)、ZGROUPITEM(書籍-シリーズ中間テーブル)にあります。
    • ただし明らかに続刊なのにシリーズに含まれてない場合もあるので困りますね。
  • 他のテーブルは値が入ってなかったり、不十分だったりするのでほぼ意味ないです。
    • コレクションとかはほしい人いるかもしれないけど、ぼくは使っていないので…。

↓以下は資料みがある記述。

データベースの基本情報

  • データベース種別: SQLite
  • 保存場所: ~/Library/Containers/com.amazon.Lassen/Data/Library/Protected/BookData.sqlite
  • 主要テーブル数: 10テーブル + システムテーブル3つ
  • 文字エンコーディング: UTF-8

テーブル一覧

なお、役割は列名などからの推測です。 あと、けっこう空の情報も多くて、ほとんどの場合期待する機能を果たしてくれません。(例えば未読管理が意味をなしてないとか思ってなかった…。) ので、🔥のマークつけたテーブル以外は見る必要ないと思う。

書籍関連テーブル

🔥ZBOOK(書籍メインテーブル)

  • 役割: 書籍の主要情報を格納する中心テーブル
  • レコード数: 蔵書数に応じて変動
  • 主要フィールド:
    • タイトル(ZDISPLAYTITLE)
    • 出版社(ZRAWPUBLISHER)
    • 言語(ZLANGUAGE)
    • plist形式メタデータ(ZSYNCMETADATAATTRIBUTES)
  • 詳細は別記事に書かれています。 dominion525.hatenablog.jp

ZBOOKEXT(書籍拡張情報)

  • 役割: 書籍の補助情報を格納
  • 主要フィールド:
    • 書籍ID(ZBOOKID)
    • ダウンロード元(ZDOWNLOADORIGIN)

ZBOOKUPDATE(書籍アップデート情報)

  • 役割: 書籍のアップデート状態を管理
  • 主要フィールド:
    • 書籍ID(ZBOOKID)
    • 更新状態(ZRAWUPDATESTATE)
    • 最終更新試行日時(ZLASTUPDATEATTEMPT)

ZARTICLE(記事情報)

  • 役割: 書籍内の記事・章情報を格納
  • 主要フィールド:
    • インデックス(ZRAWINDEX)
    • 未読フラグ(ZRAWISUNREAD)
  • リレーション: ZBOOK.Z_PK = ZARTICLE.Z2RAWARTICLES

シリーズ関連テーブル

🔥ZGROUP(シリーズマスタ)

  • 役割: シリーズ情報を格納
  • 主要フィールド:
    • シリーズID(ZGROUPID)
    • シリーズ名(ZDISPLAYNAME)
    • シリーズ著者(ZDISPLAYAUTHOR)
    • シリーズタイプ(ZSERIESTYPE)
    • 出版日(ZRAWPUBLICATIONDATE)
  • 詳細は別記事で

🔥ZGROUPITEM(書籍-シリーズ中間テーブル)

  • 役割: 書籍とシリーズを紐付ける
  • 主要フィールド:
    • 書籍への外部キー(ZBOOK)
    • シリーズへの外部キー(ZPARENTCONTAINER)
    • シリーズ内巻数(ZPOSITION)
    • 巻数表示ラベル(ZPOSITIONLABEL)
  • リレーション: ZBOOK ←→ ZGROUPITEM ←→ ZGROUP
  • 詳細は別記事で

ZSERIESAUTHOR(シリーズ著者情報)

  • 役割: シリーズの著者情報を格納(複数著者対応)
  • 主要フィールド:
    • シリーズへの外部キー(ZSERIES)
    • 著者名(ZAUTHORNAME)
    • 著者名読み(ZAUTHORPRONUNCIATION)
  • リレーション: ZGROUP ←→ ZSERIESAUTHOR
  • ハッシュ化されて読めない

ZSERIESIMAGE(シリーズ画像情報)

  • 役割: シリーズの表紙画像情報を格納
  • 主要フィールド:
    • シリーズへの外部キー(ZSERIES)
    • 画像ID(ZIMAGEID)
    • 拡張子(ZEXTENSION)
  • リレーション: ZGROUP ←→ ZSERIESIMAGE

コレクション関連テーブル

ZCOLLECTIONV2(コレクションマスタ)

  • 役割: ユーザーが作成したコレクション情報を格納
  • 主要フィールド:
    • コレクションID(ZCOLLECTIONID)
    • コレクション名(ZNAME)
    • コンテンツタイプ(ZCONTENTTYPE)
    • 最終アクセス時刻(ZRAWLASTACCESSTIME)

ZCOLLECTIONITEM(書籍-コレクション中間テーブル)

  • 役割: 書籍とコレクションを紐付ける
  • 主要フィールド:
    • 書籍への外部キー(ZBOOK)
    • コレクションへの外部キー(ZCOLLECTION)
    • 表示順(ZRAWORDER)
    • コレクションID(ZCOLLECTIONID)
    • アイテムID(ZITEMID)
  • リレーション: ZBOOK ←→ ZCOLLECTIONITEM ←→ ZCOLLECTIONV2

システムテーブル

Z_PRIMARYKEY

  • 役割: 各テーブルのプライマリキー最大値を管理
  • 構造: エンティティタイプごとの最大PK値を保持

Z_METADATA

  • 役割: Core Dataのメタデータを格納?
  • 主要フィールド:
    • バージョン(Z_VERSION)
    • UUID(Z_UUID)
    • メタデータplist(Z_PLIST)

Z_MODELCACHE

  • 役割: Core Dataモデルのキャッシュ?
  • 構造: モデル情報をBLOBで格納

免責

  • 本情報は、筆者が独自に調査・検証した非公式情報であり、AmazonまたはKindleとは一切関係ありません。
  • 正確性の保証はありません。誤りや不適切な認識が含まれる可能性があります。
  • 特定の環境・バージョンでの検証結果であり、すべての環境での動作を保証するものではありません。
  • 本記事の内容を利用した結果生じたいかなる損害についても、筆者は一切の責任を負いません。
  • 「Kindle」はAmazon.com, Inc.の登録商標です。本情報は、私的利用の範囲において自身の蔵書情報を参照するものであり、デジタル著作権管理(DRM)やコピープロテクション機構を改変、回避、妨害するものではありません。

Kindle の蔵書一覧を取得する(1)

追記

npxコマンド一発で蔵書リストが取れるツール作ったので、結果だけが欲しい人はそっち使う方がいいです。

npx kindle-title-exporter > books.csv 

dominion525.hatenablog.jp

背景

Kindleの蔵書、主に漫画なんですが、現在ざっくり3500冊、600シリーズくらいあるんですよ。 なお、継続購入してるシリーズが100件くらい。

こうなるとあんまり上手く把握できなくなっちゃうからなんとかしたいなーと思ってたわけです。 しかもAmazonは公式なエクスポート機能を提供してくれないし…。

で、以前調べた時は下記くらいが挙がってたものの、ちょっとなんだかなあと思ってたんですよ。

が、最近調べていたらわりとなんとかなりそうでした。

目次

比較的新しい方法(ただしMacOSのみ)

Kindle

Kindle

  • AMZN Mobile LLC
  • ブック
  • 無料
apps.apple.com

Macで使えるネイティブアプリ、Kindle for Macがあるんですが、これが内部的にSQLiteを使ってるんです!

はい、ここまでで全部わかっちゃった人はもう十分ですね。 単なるSQLiteなんで普通にクライアントから接続すればいいだけ。 もう、あと読まなくていいかと思います。

DBの場所

各人のホームディレクトリの下記の場所にあります。

~/Library/Containers/com.amazon.Lassen/Data/Library/Protected/BookData.sqlite

なので、任意のSQLiteクライアントがあれば簡単に参照できます。

# インストールされてなければ
brew install sqlite

# 接続
sqlite3 ~/Library/Containers/com.amazon.Lassen/Data/Library/Protected/BookData.sqlite
 

あとは普通にSQL投げるだけです。

DBの仕様

  • 形式: SQLiteデータベース
  • 文字エンコーディング: UTF-8
  • 主要テーブル数: 10テーブル + システムテーブル3つ

DB schema

エクスポートしたやつを貼っておきました。Table Plusは便利だなあという感じです。

Kindle for Mac が使っているデータベース(BookData.sqlite.sql)のschema · GitHub

細かいことは別の記事で書こうと思います。

クイックスタート

1. データベースの確認

# データベースファイルの存在確認
ls -lh ~/Library/Containers/com.amazon.Lassen/Data/Library/Protected/BookData.sqlite

# テーブル一覧を表示
sqlite3 ~/Library/Containers/com.amazon.Lassen/Data/Library/Protected/BookData.sqlite ".tables"

2. 書籍一覧の取得

SELECT ZDISPLAYTITLE, ZRAWPUBLISHER
FROM ZBOOK;
EOF

3. シリーズ情報付きで取得

いくつかのテーブルをLEFT JOINします。

SELECT
  ZBOOK.ZDISPLAYTITLE,
  ZGROUP.ZDISPLAYNAME as series_name,
  ZGROUPITEM.ZPOSITIONLABEL as series_position
FROM ZBOOK
LEFT JOIN ZGROUPITEM ON ZBOOK.Z_PK = ZGROUPITEM.ZBOOK
LEFT JOIN ZGROUP ON ZGROUPITEM.ZPARENTCONTAINER = ZGROUP.Z_PK;
  • あと全ての作品にシリーズ情報が入っているわけでもないので割と困りますね。
  • 詳細は別の記事で書きます。

FAQ

Q. 著者名を取得するには?

A: plist(ZSYNCMETADATAATTRIBUTES)からデコードする必要があります。

  • ZDISPLAYAUTHORはハッシュ値で人間が読めません。
  • 詳細は別の記事で書きます。

Q. 購入日時を取得するには?

A: plistのpurchase_dateフィールドから取得します。

  • ZBOOKテーブルには購入日フィールドがありません
  • 詳細は別の記事で書きます。

Q. タイムスタンプの形式は?

A: Unix timestamp(1970-01-01からの秒数)です。

: - Unix timestamp: 1353283200 - 日時: 2012-11-19T00:00:00.000Z

  • SQLite って日付型、日時型のカラムがなくて、ぜんぶ文字列として保持するんですよね。

Q. このデータベースを直接編集してもいい?

A: やめましょう。

  • データベースの破損リスクがあります。
  • 読み取り専用にした方が堅いです。

詳細解説

わりといろいろあるので別記事で解説します。

免責

  • 本情報は、筆者が独自に調査・検証した非公式情報であり、AmazonまたはKindleとは一切関係ありません。
  • 正確性の保証はありません。誤りや不適切な認識が含まれる可能性があります。
  • 特定の環境・バージョンでの検証結果であり、すべての環境での動作を保証するものではありません。
  • 本記事の内容を利用した結果生じたいかなる損害についても、筆者は一切の責任を負いません。
  • 「Kindle」はAmazon.com, Inc.の登録商標です。本情報は、私的利用の範囲において自身の蔵書情報を参照するものであり、デジタル著作権管理(DRM)やコピープロテクション機構を改変、回避、妨害するものではありません。