This commit is contained in:
douboer 2025-09-07 13:10:52 +08:00
parent 2f26e95052
commit 227c7f806f
1 changed files with 64 additions and 64 deletions

128
readme.md
View File

@ -41,9 +41,33 @@
--- ---
## 3. 主要数据结构 ## 3. UML 图Mermaid
### 3.1 booksnote > 注:使用 Mermaid 语法,支持在支持渲染的 Markdown 查看。类之间仅展示主要依赖/调用,非完整字段集合。
### 3.1 类图(核心模块)
![img](uml/iShot_2025-09-07_12.55.41.png)
### 3.2 时序图:应用启动
![img](uml/iShot_2025-09-07_12.56.27.png)
### 3.3 时序图:选择书籍 + AI 简评
![img](uml/iShot_2025-09-07_12.56.00.png)
### 3.4 时序图:导出 Markdown
![img](uml/iShot_2025-09-07_12.57.06.png)
### 3.5 时序图:已读书籍网格刷新
![img](uml/iShot_2025-09-07_12.57.19.png)
### 3.6 时序图:点击已读封面跳转
![img](uml/iShot_2025-09-07_12.57.28.png)
---
## 4. 主要数据结构
### 4.1 booksnote
```python ```python
booksnote = { booksnote = {
@ -63,20 +87,20 @@ booksnote = {
--- ---
## 4. 主要流程 ## 5. 主要流程
### 4.1 数据同步 ### 5.1 数据同步
- 自动将 iBooks 的数据库和 plist 文件复制到本地 `data/` 目录,便于后续处理。 - 自动将 iBooks 的数据库和 plist 文件复制到本地 `data/` 目录,便于后续处理。
### 4.2 构建 booksnote ### 5.2 构建 booksnote
- 通过 `get_annotations` 解析 SQLite 笔记数据库,获取所有笔记。 - 通过 `get_annotations` 解析 SQLite 笔记数据库,获取所有笔记。
- 通过 `parse_books_plist` 解析书籍元数据,获取书名、路径等信息。 - 通过 `parse_books_plist` 解析书籍元数据,获取书名、路径等信息。
- 遍历每本书的所有笔记结合OPF、NCX文件和HTML 文件,定位章节名。 - 遍历每本书的所有笔记结合OPF、NCX文件和HTML 文件,定位章节名。
- 若无法通过目录文件定位章节,则尝试通过笔记选中文本在 HTML 文件中查找章节,否则标记为“未找到章节”。 - 若无法通过目录文件定位章节,则尝试通过笔记选中文本在 HTML 文件中查找章节,否则标记为“未找到章节”。
### 4.3 交互式选择书籍 ### 5.3 交互式选择书籍
- 读取 Books.plist 获取所有书籍元数据。 - 读取 Books.plist 获取所有书籍元数据。
- 读取 BKLibrary.sqlite获取每本书的最近打开时间last_open苹果时间戳基准2001-01-01 - 读取 BKLibrary.sqlite获取每本书的最近打开时间last_open苹果时间戳基准2001-01-01
@ -84,7 +108,7 @@ booksnote = {
- 按 last_open 时间戳降序排列,菜单显示“书名 [时间戳]”,时间戳为 last_open 字段。 - 按 last_open 时间戳降序排列,菜单显示“书名 [时间戳]”,时间戳为 last_open 字段。
- 使用 InquirerPy 提供模糊搜索交互界面,供用户选择要导出的书籍。 - 使用 InquirerPy 提供模糊搜索交互界面,供用户选择要导出的书籍。
### 4.4 导出 Markdown ### 5.4 导出 Markdown
- 仅导出用户选择的书籍。 - 仅导出用户选择的书籍。
- Markdown 格式如下: - Markdown 格式如下:
@ -101,15 +125,15 @@ booksnote = {
--- ---
## 5. 关键函数说明 ## 6. 关键函数说明
### 5.1 build_booksnote ### 6.1 build_booksnote
- 输入:注释数据库路径、书籍 plist 路径 - 输入:注释数据库路径、书籍 plist 路径
- 输出:结构化的 booksnote 字典 - 输出:结构化的 booksnote 字典
- 逻辑:遍历所有笔记,结合书籍元数据和目录信息,归类到章节下 - 逻辑:遍历所有笔记,结合书籍元数据和目录信息,归类到章节下
### 5.2 export_booksnote_to_md ### 6.2 export_booksnote_to_md
- 输入booksnote、booksinfo、导出路径 - 输入booksnote、booksinfo、导出路径
- 输出Markdown 字符串,并写入文件 - 输出Markdown 字符串,并写入文件
@ -117,7 +141,7 @@ booksnote = {
--- ---
## 6. 交互与用户体验 ## 7. 交互与用户体验
- 通过命令行交互,用户可模糊搜索并选择要导出的书籍。 - 通过命令行交互,用户可模糊搜索并选择要导出的书籍。
- 若无可导出的笔记,程序自动退出并提示。 - 若无可导出的笔记,程序自动退出并提示。
@ -125,9 +149,9 @@ booksnote = {
--- ---
## 7. 代码片段示例 ## 8. 代码片段示例
### 7.1 书名处理逻辑 ### 8.1 书名处理逻辑
```python ```python
name = info.get('displayname') or info.get('itemname') or assetid name = info.get('displayname') or info.get('itemname') or assetid
@ -135,7 +159,7 @@ name = info.get('displayname') or info.get('itemname') or assetid
if '-' in name: name = name.split('-', 1)[0].strip() if '-' in name: name = name.split('-', 1)[0].strip()
``` ```
### 7.2 交互式选择与排序 ### 8.2 交互式选择与排序
```python ```python
from booklist_parse import get_books_last_open from booklist_parse import get_books_last_open
@ -156,7 +180,7 @@ answer = inquirer.fuzzy(
--- ---
## 8. 依赖说明 ## 9. 依赖说明
- Python 3 - Python 3
- 主要依赖库:`InquirerPy`, `bs4`, `shutil`, `os`, `datetime`, `sqlite3` - 主要依赖库:`InquirerPy`, `bs4`, `shutil`, `os`, `datetime`, `sqlite3`
@ -164,7 +188,7 @@ answer = inquirer.fuzzy(
--- ---
## 9. 目录结构 ## 10. 目录结构
- `data/`:存放同步下来的数据库和 plist 文件(含 AEAnnotation.sqlite、Books.plist、BKLibrary.sqlite 等) - `data/`:存放同步下来的数据库和 plist 文件(含 AEAnnotation.sqlite、Books.plist、BKLibrary.sqlite 等)
- `notes/`:导出的 Markdown 文件 - `notes/`:导出的 Markdown 文件
@ -172,9 +196,9 @@ answer = inquirer.fuzzy(
--- ---
## 10. 主要代码文件说明(细化) ## 11. 主要代码文件说明(细化)
### 10.1 exportbooknotes.py ### 11.1 exportbooknotes.py
- 采用 OOP 设计,核心类为 `BookNotesExporter` - 采用 OOP 设计,核心类为 `BookNotesExporter`
- `build_booksnote(bookid=None)`:构建结构化笔记数据。 - `build_booksnote(bookid=None)`:构建结构化笔记数据。
@ -185,7 +209,7 @@ answer = inquirer.fuzzy(
- 只处理用户选中书籍的笔记,按章节分组导出 Markdown。 - 只处理用户选中书籍的笔记,按章节分组导出 Markdown。
- 依赖核心解析模块,负责主流程调度。 - 依赖核心解析模块,负责主流程调度。
### 10.2 annotationdata.py ### 11.2 annotationdata.py
- OOP 设计,核心类为 `AnnotationManager` - OOP 设计,核心类为 `AnnotationManager`
- `get_annotations(bookid=None)`:返回所有或指定 assetid 的笔记。 - `get_annotations(bookid=None)`:返回所有或指定 assetid 的笔记。
@ -193,7 +217,7 @@ answer = inquirer.fuzzy(
- 解析 AEAnnotation.sqlite提取所有或指定 assetid 的笔记。 - 解析 AEAnnotation.sqlite提取所有或指定 assetid 的笔记。
- 支持苹果时间戳转换,结构化输出。 - 支持苹果时间戳转换,结构化输出。
### 10.3 booklist_parse.py ### 11.3 booklist_parse.py
- OOP 设计,核心类为 `BookListManager` - OOP 设计,核心类为 `BookListManager`
- `get_books_info()`:获取书籍元数据。 - `get_books_info()`:获取书籍元数据。
@ -201,13 +225,13 @@ answer = inquirer.fuzzy(
- 解析 Books.plist获取书籍元数据书名、作者、路径、时间等 - 解析 Books.plist获取书籍元数据书名、作者、路径、时间等
- 解析 BKLibrary.sqlite获取每本书的最近打开时间。 - 解析 BKLibrary.sqlite获取每本书的最近打开时间。
### 10.4 opf_parse.py ### 11.4 opf_parse.py
- OOP 设计,核心类为 `OPFParser` - OOP 设计,核心类为 `OPFParser`
- `parse_opf(filepath)`:静态方法,返回 id->href 映射。 - `parse_opf(filepath)`:静态方法,返回 id->href 映射。
- 解析 epub 的 OPF 文件获取章节与文件映射关系idref -> href - 解析 epub 的 OPF 文件获取章节与文件映射关系idref -> href
### 10.5 toc_parse.py ### 11.5 toc_parse.py
- OOP 设计,核心类为 `TOCParser` - OOP 设计,核心类为 `TOCParser`
- `parse_navpoints(navpoints)`:递归解析 navPoint 节点。 - `parse_navpoints(navpoints)`:递归解析 navPoint 节点。
@ -216,13 +240,13 @@ answer = inquirer.fuzzy(
- `parse_html_title(html_path)`:解析 html 文件标题。 - `parse_html_title(html_path)`:解析 html 文件标题。
- 解析 NCX 目录文件,递归构建章节树结构。 - 解析 NCX 目录文件,递归构建章节树结构。
### 10.6 backup/booksnote.py ### 11.6 backup/booksnote.py
- 历史/备份脚本,辅助数据迁移或格式转换。 - 历史/备份脚本,辅助数据迁移或格式转换。
--- ---
## 11. 扩展与维护建议 ## 12. 扩展与维护建议
- 可扩展支持多本书批量导出 - 可扩展支持多本书批量导出
- 可增加导出格式(如 HTML、PDF - 可增加导出格式(如 HTML、PDF
@ -235,9 +259,9 @@ answer = inquirer.fuzzy(
--- ---
## 12. GUI 架构与模块调用关系2025 拆分后更新) ## 13. GUI 架构与模块调用关系2025 拆分后更新)
### 12.1 模块职责概览 ### 13.1 模块职责概览
| 模块 | 主要类/函数 | 职责 | 关键依赖 | | 模块 | 主要类/函数 | 职责 | 关键依赖 |
|------|-------------|------|----------| |------|-------------|------|----------|
@ -252,7 +276,7 @@ answer = inquirer.fuzzy(
| `opf_parse.py` | `OPFParser` | 解析 OPF 获取 manifest 映射 | OPF XML | | `opf_parse.py` | `OPFParser` | 解析 OPF 获取 manifest 映射 | OPF XML |
| `charts.py` | 图表组件 | 周 / 月 / 年 / 气泡指标可视化 | `BookListManager` 汇总数据 | | `charts.py` | 图表组件 | 周 / 月 / 年 / 气泡指标可视化 | `BookListManager` 汇总数据 |
### 12.2 运行时对象关系(简化 UML 文本表示) ### 13.2 运行时对象关系(简化 UML 文本表示)
``` ```
IBookExportApp IBookExportApp
├── BookListManager (数据/统计) ├── BookListManager (数据/统计)
@ -268,7 +292,7 @@ IBookExportApp
2. 网格构建 -> `FinishedBooksMixin._populate_finished_books_grid` 2. 网格构建 -> `FinishedBooksMixin._populate_finished_books_grid`
3. AI 简评 -> `BookReviewWorker` 发起,完成后回调 `_on_review_finished` 3. AI 简评 -> `BookReviewWorker` 发起,完成后回调 `_on_review_finished`
### 12.3 启动序列Startup Sequence ### 13.3 启动序列Startup Sequence
1. 用户运行 `ibook_export_app.py` → 创建 `QApplication` 1. 用户运行 `ibook_export_app.py` → 创建 `QApplication`
2. `IBookExportApp.__init__` 2. `IBookExportApp.__init__`
- 加载 `.ui` - 加载 `.ui`
@ -281,14 +305,14 @@ IBookExportApp
- 安排延迟 `_relayout_finished_grid()` 确保初次布局正确 - 安排延迟 `_relayout_finished_grid()` 确保初次布局正确
3. 主窗口显示;用户可交互。 3. 主窗口显示;用户可交互。
### 12.4 书籍切换流程Selecting a Book ### 13.4 书籍切换流程Selecting a Book
1. 用户在列表中选中条目 → `currentRowChanged``update_book_info(row)` 1. 用户在列表中选中条目 → `currentRowChanged``update_book_info(row)`
2. 刷新右侧三张封面(当前 + 后两本轮播预览) 2. 刷新右侧三张封面(当前 + 后两本轮播预览)
3. 构建基础信息 `_base_info_cache` 3. 构建基础信息 `_base_info_cache`
4. 若 `bookintro.json` 已有简评 → 直接渲染;否则启动新 `BookReviewWorker` → 占位“简评获取中...” 4. 若 `bookintro.json` 已有简评 → 直接渲染;否则启动新 `BookReviewWorker` → 占位“简评获取中...”
5. 线程完成 → 通过信号调用 `_on_review_finished` → 更新 HTML。 5. 线程完成 → 通过信号调用 `_on_review_finished` → 更新 HTML。
### 12.5 AI 简评线程生命周期 ### 13.5 AI 简评线程生命周期
1. 实例化 `BookReviewWorker(bookname, prompt, json_path)` 1. 实例化 `BookReviewWorker(bookname, prompt, json_path)`
2. 连接 `finished` 信号到: 2. 连接 `finished` 信号到:
- `_on_review_finished` - `_on_review_finished`
@ -296,7 +320,7 @@ IBookExportApp
3. `worker.start()` → 线程内部:调用 `DashScopeChatClient.ask()`;写入/更新 `bookintro.json`;发送 `finished` 信号。 3. `worker.start()` → 线程内部:调用 `DashScopeChatClient.ask()`;写入/更新 `bookintro.json`;发送 `finished` 信号。
4. 主线程根据 `_current_bookname` 校验是否仍是当前书,防止串写。 4. 主线程根据 `_current_bookname` 校验是否仍是当前书,防止串写。
### 12.6 已读书籍网格刷新逻辑 ### 13.6 已读书籍网格刷新逻辑
事件触发: 事件触发:
1. 程序启动初次调用 `_populate_finished_books_grid()` 1. 程序启动初次调用 `_populate_finished_books_grid()`
2. Tab 切换到“已读书籍”标签 → `_on_main_tab_changed()` → 若命中,立即 `_relayout_finished_grid()` 并延迟一次;(后续可扩展为定期重新查询) 2. Tab 切换到“已读书籍”标签 → `_on_main_tab_changed()` → 若命中,立即 `_relayout_finished_grid()` 并延迟一次;(后续可扩展为定期重新查询)
@ -307,26 +331,26 @@ IBookExportApp
- 将 Apple epoch(2001) 秒转为 `datetime`,过滤 `year==当前年` - 将 Apple epoch(2001) 秒转为 `datetime`,过滤 `year==当前年`
- 返回列表后排序(时间倒序) - 返回列表后排序(时间倒序)
### 12.7 封面加载与缩放流程 ### 13.7 封面加载与缩放流程
1. `_load_initial()` / `update_book_info()` 内调用 `find_book_cover()` 获取路径 1. `_load_initial()` / `update_book_info()` 内调用 `find_book_cover()` 获取路径
2. 原图 QPixmap 存入 `_cover_pixmaps_original` 2. 原图 QPixmap 存入 `_cover_pixmaps_original`
3. `_apply_cover_scale()` 使用当前 `cover_ratio`(默认 1.2)和弹性策略计算目标高度 3. `_apply_cover_scale()` 使用当前 `cover_ratio`(默认 1.2)和弹性策略计算目标高度
4. 固定宽 180px受硬上限 400px 与(可选)文本区 45% 限制 4. 固定宽 180px受硬上限 400px 与(可选)文本区 45% 限制
5. 非弹性模式忽略文本高度,仅 ratio + 硬上限。 5. 非弹性模式忽略文本高度,仅 ratio + 硬上限。
### 12.8 导出流程Export Notes ### 13.8 导出流程Export Notes
1. 用户点击“导出”按钮 → `export_notes()` 1. 用户点击“导出”按钮 → `export_notes()`
2. 取当前行 assetid → `BookNotesExporter.build_booksnote(bookid)` 2. 取当前行 assetid → `BookNotesExporter.build_booksnote(bookid)`
3. 组装单书字典 → `export_booksnote_to_md()` 输出 Markdown 到 `notes/` 目录 3. 组装单书字典 → `export_booksnote_to_md()` 输出 Markdown 到 `notes/` 目录
4. 弹窗提示路径。 4. 弹窗提示路径。
### 12.9 统计图表流程 ### 13.9 统计图表流程
1. 启动后调用 `_init_charts()`(懒加载) 1. 启动后调用 `_init_charts()`(懒加载)
2. 获取周 / 月 / 年聚合数据及总指标 2. 获取周 / 月 / 年聚合数据及总指标
3. 构造原生自绘组件 `BarChartWidget` / `ScatterChartWidget` / `BubbleMetricsWidget` 3. 构造原生自绘组件 `BarChartWidget` / `ScatterChartWidget` / `BubbleMetricsWidget`
4. 添加到对应 Layout。 4. 添加到对应 Layout。
### 12.10 关键调用关系(摘要) ### 13.10 关键调用关系(摘要)
``` ```
update_book_info -> find_book_cover (CoverMixin) update_book_info -> find_book_cover (CoverMixin)
update_book_info -> BookReviewWorker.start -> _on_review_finished update_book_info -> BookReviewWorker.start -> _on_review_finished
@ -336,7 +360,7 @@ export_notes -> BookNotesExporter.build_booksnote -> export_booksnote_to_md
_init_charts -> manager.get_total_readtime* 系列函数 _init_charts -> manager.get_total_readtime* 系列函数
``` ```
### 12.11 数据流摘要 ### 13.11 数据流摘要
``` ```
iBooks 原始文件 -> sync_source_files -> data/*.sqlite / Books.plist iBooks 原始文件 -> sync_source_files -> data/*.sqlite / Books.plist
└─ BookListManager 载入 -> booksinfo / open_times / 阅读统计 └─ BookListManager 载入 -> booksinfo / open_times / 阅读统计
@ -349,7 +373,7 @@ iBooks 原始文件 -> sync_source_files -> data/*.sqlite / Books.plist
AI 请求 -> BookReviewWorker -> DashScopeChatClient.ask -> bookintro.json -> _on_review_finished 渲染 AI 请求 -> BookReviewWorker -> DashScopeChatClient.ask -> bookintro.json -> _on_review_finished 渲染
``` ```
### 12.12 扩展点与建议 ### 13.12 扩展点与建议
1. 已读书籍:增加“显示全部年份 / 仅今年”开关;提供手动“刷新”按钮。 1. 已读书籍:增加“显示全部年份 / 仅今年”开关;提供手动“刷新”按钮。
2. 封面缓存:引入 LRU (assetid -> QPixmap) 降低重复磁盘扫描。 2. 封面缓存:引入 LRU (assetid -> QPixmap) 降低重复磁盘扫描。
3. AI 简评:加速策略(本地缓存 TTL、批量预取前 N 本)。 3. AI 简评:加速策略(本地缓存 TTL、批量预取前 N 本)。
@ -361,7 +385,7 @@ AI 请求 -> BookReviewWorker -> DashScopeChatClient.ask -> bookintro.json -> _o
6. 性能:大书量时(>1000列表初始化可用分页或懒加载。 6. 性能:大书量时(>1000列表初始化可用分页或懒加载。
7. 打包:后续可用 `PyInstaller`将可执行与资源icons、ui整合。 7. 打包:后续可用 `PyInstaller`将可执行与资源icons、ui整合。
### 12.13 风险与缓解 ### 13.13 风险与缓解
| 风险 | 描述 | 缓解 | | 风险 | 描述 | 缓解 |
|------|------|------| |------|------|------|
| 数据不同步 | data/ 下 sqlite 未更新导致已读缺失 | 提供“重新同步”按钮;比对文件修改时间 | | 数据不同步 | data/ 下 sqlite 未更新导致已读缺失 | 提供“重新同步”按钮;比对文件修改时间 |
@ -373,32 +397,6 @@ AI 请求 -> BookReviewWorker -> DashScopeChatClient.ask -> bookintro.json -> _o
(本节为 2025-09 结构化重构新增) (本节为 2025-09 结构化重构新增)
## 13. UML 图Mermaid
> 注:使用 Mermaid 语法,支持在支持渲染的 Markdown 查看。类之间仅展示主要依赖/调用,非完整字段集合。
### 13.1 类图(核心模块)
![img](uml/iShot_2025-09-07_12.55.41.png)
### 13.2 时序图:应用启动
![img](uml/iShot_2025-09-07_12.56.27.png)
### 13.3 时序图:选择书籍 + AI 简评
![img](uml/iShot_2025-09-07_12.56.00.png)
### 13.4 时序图:导出 Markdown
![img](uml/iShot_2025-09-07_12.57.06.png)
### 13.5 时序图:已读书籍网格刷新
![img](uml/iShot_2025-09-07_12.57.19.png)
### 13.6 时序图:点击已读封面跳转
![img](uml/iShot_2025-09-07_12.57.28.png)
---
## 14. 书籍阅读时长统计与可视化 ## 14. 书籍阅读时长统计与可视化
### 14.1 阅读时长统计逻辑 ### 14.1 阅读时长统计逻辑
@ -470,3 +468,5 @@ AI 请求 -> BookReviewWorker -> DashScopeChatClient.ask -> bookintro.json -> _o
- 气泡图支持动画过渡或改为雷达/仪表盘形式;“已读”气泡可按年份切换(未来提供年份选择器)。 - 气泡图支持动画过渡或改为雷达/仪表盘形式;“已读”气泡可按年份切换(未来提供年份选择器)。
- 增加刷新按钮与 Esc 退出全屏逻辑。 - 增加刷新按钮与 Esc 退出全屏逻辑。
---