iBook/mreadme.md

13 KiB
Raw Blame History

iBooks笔记导出

epub结构

sqlite3 ~/Library/Containers/com.apple.iBooksX/Data/Documents/AEAnnotation/AEAnnotation_v10312011_1727_local.sqlite sqlite> .tables ZAEANNOTATION Z_METADATA Z_PRIMARYKEY
ZBCCLOUDSYNCVERSIONS Z_MODELCACHE

ZAEANNOTATION表:

字段名 含义描述推测
Z_PK 4662 可能是该条记录在数据库中的主键Primary Key用于唯一标识这条批注数据
Z_ENT 1 可能代表实体类型Entity Type用于区分数据库中不同类型的记录此处可能标识为“批注”类实体
Z_OPT 1 可能是版本号或优化字段Optimization用于记录数据的更新次数或状态标识
ZANNOTATIONDELETED 0 标识批注是否被删除0表示未删除1可能表示已删除
ZANNOTATIONISUNDERLINE 1 标识批注是否为下划线样式1表示该批注是下划线标注0可能表示非下划线
ZANNOTATIONSTYLE 0 可能表示批注的样式属性如颜色、粗细等0可能对应默认样式
ZANNOTATIONTYPE 2 表示批注的类型可能用于区分是下划线、高亮、注释等不同类型2此处可能对应下划线批注
ZPLABSOLUTEPHYSICALLOCATION 0 可能是批注在文档中的绝对物理位置信息,具体含义需结合文档存储格式判断
ZPLLOCATIONRANGEEND 0 批注内容在文档中的位置范围终点,可能是字符偏移量或段落索引等
ZPLLOCATIONRANGESTART 9 批注内容在文档中的位置范围起点,可能是字符偏移量或段落索引等,与终点配合确定批注选中的文本范围
ZANNOTATIONCREATIONDATE 774064827.1 批注的创建时间,可能是时间戳格式(需转换为具体日期时间)
ZANNOTATIONMODIFICATIONDATE 774064829.2 批注的最后修改时间,时间戳格式,记录批注内容或属性的更新时间
ZANNOTATIONASSETID 768E1CD0B3086166F791683869B12425 可能是该批注所属文档的唯一标识Asset ID用于关联批注对应的电子书或文档
ZANNOTATIONCREATORIDENTIFIER com~apple~iBooks 标识创建该批注的应用程序此处表明是苹果的iBooks应用创建的批注
ZANNOTATIONLOCATION epubcfi(/6/20[id155]!/4/412/1,:0,:97) 采用EPUB格式的CFIContent Fragment Identifier精确标识批注在电子书文档中的位置
ZANNOTATIONNOTE 批注的备注内容,此处为空表示该批注没有额外添加的文字备注
ZANNOTATIONREPRESENTATIVETEXT 在听完这个充满暴力、腐败和表里不一的悲剧故事之后,希维格和我都已精疲力尽。我们不知道到底谁比较可恶:是罪不可逭的席格和犹太人防卫联盟,还是恶意侵犯席格的宪法基本人权、并且否认曾许下承诺的政府官员。 可能是批注所关联的代表性文本,通常是包含批注选中内容的上下文文本
ZANNOTATIONSELECTEDTEXT 在听完这个充满暴力、腐败和表里不一的悲剧故事之后,希维格和我都已精疲力尽。我们不知道到底谁比较可恶:是罪不可逭的席格和犹太人防卫联盟,还是恶意侵犯席格的宪法基本人权、并且否认曾许下承诺的政府官员 被用户选中并添加批注(下划线)的具体文本内容,优先使用该字段作为内容定位依据
ZANNOTATIONUUID 7097EAE0-0EDB-4552-9AE0-FA6AB390B90B 批注的唯一标识符UUID用于在系统中唯一标识这条批注避免重复
ZFUTUREPROOFING1 预留字段,可能用于未来功能扩展,目前未使用
ZFUTUREPROOFING10 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING11 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING12 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING2 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING3 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING4 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING5 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING6 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING7 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING8 预留字段,用于未来功能扩展,目前未使用
ZFUTUREPROOFING9 预留字段,用于未来功能扩展,目前未使用
ZPLSTORAGEUUID 774064829.2 可能是与存储相关的唯一标识符,用于关联批注在存储系统中的位置或状态
ZPLUSERDATA 可能用于存储与用户相关的自定义数据,此处为空表示无额外用户数据

笔记数据

书籍元数据

PLIST_PATH = os.path.expanduser("~/Library/Containers/com.apple.BKAgentService/Data/Documents/iBooks/Books/Books.plist")

{ "Books" => [

……

603 => {
  "artistName" => "(美)德肖维茨"
  "BKAllocatedSize" => 1388544
  "BKBookType" => "epub"
  "BKDisplayName" => "最好的辩护-德肖维茨"
  "BKGeneratedItemId" => "768E1CD0B3086166F791683869B12425"
  "BKGenerationCount" => 1
  "BKInsertionDate" => 773817769
  "BKPercentComplete" => 1
  "book-info" => {
    "package-file-hash" => "768E1CD0B3086166F791683869B12425"
  }
  "genre" => "法律"
  "isPreview" => 0
  "itemName" => "最好的辩护"
  "path" => "/Users/gavin/Library/Mobile Documents/iCloud~com~apple~iBooks/Documents/最好的辩护.epub"
  "sourcePath" => "/Users/gavin/Library/Containers/com.apple.iBooksX/Data/Library/Caches/Inbox/最好的辩护-德肖维茨.epub"
  "updateDate" => 2025-07-10 05:22:49 +0000
}
……

]

笔记数据库

DB_PATH = os.path.expanduser("~/Library/Containers/com.apple.iBooksX/Data/Documents/AEAnnotation/AEAnnotation_v10312011_1727_local.sqlite")

books

~/Library/Containers/com.apple.iBooksX/Data/Documents/BKLibrary/BKLibrary-1-091020131601.sqlite gavin@GavinsMAC BKLibrary % sqlite3 ~/Library/Containers/com.apple.iBooksX/Data/Documents/BKLibrary/BKLibrary-1-091020131601.sqlite sqlite> .tables ZBCCLOUDSYNCVERSIONS ZBKJALISCOSTATUS Z_MODELCACHE
ZBKCOLLECTION ZBKLIBRARYASSET Z_PRIMARYKEY
ZBKCOLLECTIONMEMBER Z_METADATA

问题

20250729

从epubcfi如epubcfi(/6/20[id155]!/4/412/1,:0,:97)去反推定位内容,没法实现,浪费时间。

解决思路

  1. 基本信息
  • 笔记数据在这个表中:~/Library/Containers/com.apple.iBooksX/Data/Documents/AEAnnotation/AEAnnotation_v10312011_1727_local.sqlite
  • 书籍清单在这个表中:~/Library/Containers/com.apple.iBooksX/Data/Documents/BKLibrary/BKLibrary-1-091020131601.sqlite
  • 书籍元数据在这里:~/Library/Containers/com.apple.BKAgentService/Data/Documents/iBooks/Books/Books.plist
  • 书籍在这个directory中(epub为文件夹非压缩文件)~/Library/Mobile Documents/iCloud~com~apple~iBooks/Documents
  1. copy以上前三个文件到./data目录下分别命名为AEAnnotation.sqlite、BKLibrary.sqlite、Books.plist BKLibrary.sqlite暂不使用使用Books.plist

  2. 解析Books.plist存在名为booksinfo的defaultdict(dict)中。booksinfo数据结构如下 booksinfo = { "768E1CD0B3086166F791683869B12425": { "BKDisplayName" : "最好的辩护-德肖维茨", "artistName" : "(美)德肖维茨", "BKBookType" : "epub", "BKGeneratedItemId" : "768E1CD0B3086166F791683869B12425", "itemName" : "最好的辩护", "path" : "/Users/gavin/Library/Mobile Documents/iCloud~com~apple~iBooks/Documents/最好的辩护.epub" } …… } 其中768E1CD0B3086166F791683869B12425为BKGeneratedItemId。

  3. 查询AEAnnotation.sqlite获取ZANNOTATIONCREATIONDATE ZANNOTATIONASSETID ZANNOTATIONLOCATION ZANNOTATIONSELECTEDTEXT ZANNOTATIONUUID 如: ZANNOTATIONCREATIONDATE 746202137.8 #需要转化为2025/7/30这样的格式。 ZANNOTATIONASSETID 5F2C1C40566C61A2267907E621A664A4 ZANNOTATIONLOCATION epubcfi(/6/98[id61]!/4[1CQAE0-902f5dcbb4a04a858d573ec5ee66e862]/58/1,:28,:226) #需要获取idref = id61filepos=1CQAE0-902f5dcbb4a04a858d573ec5ee66e862 ZANNOTATIONSELECTEDTEXT 这才是对人而言的自由 ZANNOTATIONUUID 0456103A-7161-451A-B762-1305ECBECBDB 并把数据存到名为annotations的defaultdict(dict)中。annotations的数据结构如下 annotations = { 'CB9A605DCD687C4FA544DD4BCCD00D43': { '326CA2CF-3298-45D5-A93F-440E6F2A0B33': { 'creationdate': '2023/7/12', 'filepos': None, 'idref': '008.xhtml', 'note': None, 'selectedtext': '這就是宣傳的恐怖之處'}, '2BD61C93-D1F1-4553-90CB-043A6E06DBA1': { 'creationdate': '2023/7/12', 'filepos': None, 'idref': '008.xhtml', 'note': None, 'selectedtext': '應該有一些系統性的表達機制'}, } …… }

  4. 需要导出的书名作为参数,比如"最好的辩护"书名做从booksinfo中做模糊查询如匹配到多本在shell中提示选择哪一本书。 从booksinfo获取本书的uuid和这本书epub的路径path进而从annotations中获取这本书的笔记。

  5. 依据这本书的路径path解析epub文件夹下的文件。 假定epub目录为epubdirxx.opf(package.opf|standard.opf|content.opf)和toc.ncx文件可能的位置在 epubdir/content.opf epubdir/toc.ncx epubdir/OEBPS/content.opf epubdir/OEBPS/toc.ncx epubdir/EPUB/package.opf epubdir/EPUB/toc.ncx epubdir/item/standard.opf epubdir/item/toc.ncx

  • 解析opf文件存入default(dict)数据结构contentopf: contentopf = { "768E1CD0B3086166F791683869B12425": { id163 : index_split_000.html, # idref: ref id162 : index_split_001.html, id161 : index_split_002.html, id160 : index_split_003.html, id159 : index_split_004.html, id158 : index_split_005.html …… } }

  • 解析toc.ncx文件存入default(dict)数据结构contenttoc: contenttoc = { "768E1CD0B3086166F791683869B12425": { { 'num_1': {'filepos': None, 'label': '\n最好的辩护\n', 'ref': 'index_split_000.html'}, 'num_100': { 'filepos': None, 'label': '\n第八章父亲的罪\n', 'num_101': { 'filepos': 'filepos698889', 'label': '\n没有父亲的生活\n', 'ref': 'index_split_016.html'}, 'num_102': {'filepos': 'filepos706518', 'label': '\n越狱\n', 'ref': 'index_split_016.html'}, …… } } }

  1. 使用annotationdata.py获取annotations把annotations扩充label_path 通过annotations的assetid从bookinfo获取path路径在path下找到.opf和.ncx使用opf_parse.py和toc_parse.py获取label_path

booksnote = { assetid: { label_path: { uuid: { 'creationdate': '2023/7/12', 'filepos': None, 'idref': '008.xhtml', 'note': None, 'selectedtext': '這就是宣傳的恐怖之處' }}}}

应该从epubcfi中的id155去找到html文件在从笔记内容去匹配对应文件中的位置。 需要有个函数吧html的数据结构dict化很快可以从内容找到title。

从epubcfi的ZANNOTATIONASSETID找到bookid 从ZANNOTATIONASSETID 依据bookid => epub路径 解析book到dict中。 解析content.opf中的[id,href] 关联idtitle章节内容 => bookdict = [id,title,[内容]] 从ZAEANNOTATION的笔记内容遍历bookdict找到内容找到title 最后按booknametitle归并笔记

202500906

  1. 增加QTUI
  2. 增加统计和展示 统计
  • 周活跃 - 30天每天的阅读时长柱状图 某本书根据每条笔记note中ZANNOTATIONCREATIONDATE如果某天没有note则阅读时间为0如果只有一条note阅读时间为READ_TIME_DAY=60(在config.py中配置)如果note超过1条计算第一条和最后一条的时间差作为阅读时长。放在readtime30d这个list中。
  • 月活跃 - 30天每天的阅读时长柱状图 每本书ZANNOTATIONCREATIONDATE落在30天前到今天的天数*60min(60mins为每天阅读时间可配置
  • 已阅读的书籍: 每本平均阅读时长。所有书籍:总阅读时长,年阅读时长,年平均每日阅读时长,累计阅读天数。用气泡图表示。 如果已读完表ZBKLIBRARYASSET的ZISFINISHED字段为1 表ZBKLIBRARYASSET的ZDATEFINISHED读完时间可以统计今年读完的书籍 表ZBKLIBRARYASSET的(ZDATEFINISHED - ZCREATIONDATE),得出书本创建到读完的阅读周期,并非真正的阅读时间。 表ZAEANNOTATION的ZANNOTATIONCREATIONDATE找出最早的一条批注创建时间(ZDATEFINISHED-ZANNOTATIONCREATIONDATE)大约周期read_days。近似的用read_days60min(60mins为每天阅读时间可配置
  • 本年度已阅读书籍封面图片橱窗6xn展示图片西面注上书名支持把6xn导出成一张图片。 读完时间依据ZISFINISHED为1ZDATEFINISHED计算是否今年读完。 封面图片通过在IBOOKS_BOOKS_DIR对应的书籍下查找cover.jpg/png/jpeg文件如果没有查找cover*html文件解析该文件获取图片路径(相对于html文件的路径)。如: ,取出../Images/data-url-image.jpeg 取出Image00007.jpg

202500907

  1. 使用GPT5生成ipad app版本
    • vscode 下载插件swift
    • 安装Command Line Tools for Xcode。MacBook一直提醒我安装正好更新了。