6.4 KiB
6.4 KiB
iBooks 笔记导出工具 详细设计文档
1. 概述
本工具用于从 macOS iBooks(Apple Books)应用的数据文件中提取用户的书籍笔记,并以 Markdown 格式导出。支持从 iBooks 的数据库和 plist 文件自动同步数据,支持交互式选择书籍导出,导出内容结构清晰,便于后续整理和阅读。 支持按最近打开时间排序书籍,菜单显示书名与时间戳,导出流程高效。
2. 主要功能
- 自动同步 iBooks 数据库和书籍信息文件到本地
./data
目录。 - 解析 iBooks 笔记数据库,构建结构化的
booksnote
数据。 - 解析书籍元数据(如书名、路径等)。
- 支持交互式模糊搜索选择要导出的书籍。
- 按章节导出所选书籍的所有笔记,格式为 Markdown。
- 书名中如含有“-xxxx”后缀,仅保留“-”前的主书名。
- 书籍选择菜单按最近打开时间(last_open)降序排序,显示格式为“书名 [时间戳]”。
3. 主要数据结构
3.1 booksnote
booksnote = {
assetid: { label_path: { uuid: {
'creationdate': '2023/7/12',
'filepos': None,
'idref': '008.xhtml',
'note': None,
'selectedtext': '這就是宣傳的恐怖之處'
}}}
}
assetid
:书籍唯一标识label_path
:章节名uuid
:笔记唯一标识- 其余字段为笔记内容及元数据
4. 主要流程
4.1 数据同步
- 自动将 iBooks 的数据库和 plist 文件复制到本地
data/
目录,便于后续处理。
4.2 构建 booksnote
- 通过
get_annotations
解析 SQLite 笔记数据库,获取所有笔记。 - 通过
parse_books_plist
解析书籍元数据,获取书名、路径等信息。 - 遍历每本书的所有笔记,结合OPF、NCX文件和HTML 文件,定位章节名。
- 若无法通过目录文件定位章节,则尝试通过笔记选中文本在 HTML 文件中查找章节,否则标记为“未找到章节”。
4.3 交互式选择书籍
- 读取 Books.plist 获取所有书籍元数据。
- 读取 BKLibrary.sqlite,获取每本书的最近打开时间(last_open,苹果时间戳,基准2001-01-01)。
- 生成书名列表(优先
displayname
,其次itemname
,否则用assetid
),并去除“-xxxx”后缀。 - 按 last_open 时间戳降序排列,菜单显示“书名 [时间戳]”,时间戳为 last_open 字段。
- 使用 InquirerPy 提供模糊搜索交互界面,供用户选择要导出的书籍。
4.4 导出 Markdown
-
仅导出用户选择的书籍。
-
Markdown 格式如下:
# 笔记导出 2025-08-06 12:00 ## 书名 ### 章节名 选中文本 > 笔记内容
-
每条笔记独立分行,章节分组。
5. 关键函数说明
5.1 build_booksnote
- 输入:注释数据库路径、书籍 plist 路径
- 输出:结构化的 booksnote 字典
- 逻辑:遍历所有笔记,结合书籍元数据和目录信息,归类到章节下
5.2 export_booksnote_to_md
- 输入:booksnote、booksinfo、导出路径
- 输出:Markdown 字符串,并写入文件
- 逻辑:遍历每本书、每个章节、每条笔记,按格式输出
6. 交互与用户体验
- 通过命令行交互,用户可模糊搜索并选择要导出的书籍。
- 若无可导出的笔记,程序自动退出并提示。
- 导出后,显示导出文件路径和书名。
7. 代码片段示例
7.1 书名处理逻辑
name = info.get('displayname') or info.get('itemname') or assetid
# 如果书名中包含“-”,只取“-”前面的部分
if '-' in name: name = name.split('-', 1)[0].strip()
7.2 交互式选择与排序
from booklist_parse import get_books_last_open
last_open_times = get_books_last_open('data/BKLibrary.sqlite')
for assetid, info in booksinfo.items():
...
ts = last_open_times.get(assetid, {}).get('last_open', 0)
assetid2lastopen[assetid] = ts
sorted_assetids = sorted(assetid2name.keys(), key=lambda aid: assetid2lastopen[aid], reverse=True)
choices = [f"{assetid2name[aid]} [{assetid2lastopen[aid]}]" for aid in sorted_assetids]
answer = inquirer.fuzzy(
message="请选择要导出的书名(支持模糊搜索):",
choices=choices,
multiselect=False,
instruction="上下键选择,输入可模糊筛选,回车确定"
).execute()
8. 依赖说明
- Python 3
- 主要依赖库:
InquirerPy
,bs4
,shutil
,os
,datetime
,sqlite3
- 需有 iBooks 数据库、plist 文件和 BKLibrary.sqlite 的本地访问权限
9. 目录结构
data/
:存放同步下来的数据库和 plist 文件(含 AEAnnotation.sqlite、Books.plist、BKLibrary.sqlite 等)export_notes/
:导出的 Markdown 文件examples/
:epub 示例文件夹
9.1 主要代码文件说明(细化)
-
exportbooknotes.py
- 数据同步:自动复制 iBooks 数据库和元数据到本地。
- 菜单交互:按最近打开时间戳排序,显示“书名 [时间戳]”,支持模糊搜索。
- 只处理用户选中书籍的笔记,按章节分组导出 Markdown。
- 依赖核心解析模块,负责主流程调度。
-
annotationdata.py
- 解析 AEAnnotation.sqlite,提取所有或指定 assetid 的笔记。
- 支持苹果时间戳转换,结构化输出。
- parse_location 辅助函数,统一解析笔记定位信息。
-
booklist_parse.py
- 解析 Books.plist,获取书籍元数据(书名、作者、路径、时间等)。
- 解析 BKLibrary.sqlite,获取每本书的最近打开时间(zlastopendate,苹果时间戳)。
- 提供统一数据接口,便于主流程排序和展示。
-
opf_parse.py
- 解析 epub 的 OPF 文件,获取章节与文件映射关系(idref -> href)。
- 支持多种 epub 目录结构。
-
toc_parse.py
- 解析 NCX 目录文件,递归构建章节树结构。
- find_label_path:支持通过 ref 和 filepos 查找完整 label 路径。
- find_section_by_selectedtext:通过选中文本在 html 文件中定位章节标题。
- parse_html_title:解析 html 文件标题。
-
backup/booksnote.py
- 历史/备份脚本,辅助数据迁移或格式转换。
10. 扩展与维护建议
- 可扩展支持多本书批量导出
- 可增加导出格式(如 HTML、PDF)
- 可优化章节定位算法,提升准确率
- 可增加 GUI 交互界面
如需进一步细化某一部分设计,请告知!