From 893cd06c2c99d9e240983111d87e176c04d51295 Mon Sep 17 00:00:00 2001 From: douboer Date: Sat, 6 Sep 2025 16:43:13 +0800 Subject: [PATCH] 'update' --- __pycache__/annotationdata.cpython-312.pyc | Bin 5789 -> 7918 bytes __pycache__/config.cpython-312.pyc | Bin 2253 -> 2368 bytes annotationdata.py | 96 +++++++++++- booklist_parse.py | 172 ++++++++++++++++++++- config.py | 5 +- kmanapp.png | Bin 18468 -> 0 bytes readme.md | 4 +- 7 files changed, 266 insertions(+), 11 deletions(-) delete mode 100644 kmanapp.png diff --git a/__pycache__/annotationdata.cpython-312.pyc b/__pycache__/annotationdata.cpython-312.pyc index e515272143cce82eaf43d3ae273312f082fa9c3f..7333ec6f0389f75f5328cac89dee3af77e96fa98 100644 GIT binary patch delta 3617 zcmai1dr(u^8NWC07fA>q;xg!EYrLdO1gtJLE4rwxxU%S2yB*o3nUH(c*g%+@WOUa! ziMt>{QE-onf{JgkU|@YBi2BD)XQ!R%y0tU9>CUvB=H7%(rwjXNr~5~z=iEzfBVc=n z`8el0-|PI&_kHK&jQ#Hw`rl+_X)yRD*|D9)3y$jZ@am)FmhfIRCc$Xs09t}yof^(G zlyU$U<~X(kqf{60IxVeTht(HB2_3E8l`pJjQd*bRc~=6Z^mPW_lA6}_Vw8r~L#m|> zkm}B4ShsZiP=*4~P+4hp9HaC{|CYbekY?1GD4a5Sa~gB%a&SR7n>IpArZdvC4A0M@ zO;iqL9?Tt(rlWsP@}9(Ixw!>@C<&KBW;aG-bvT99NhrKdN+~F53tlIq<&>-i6ERRg zz8vzEP*)kxu54=Z1zZ7-ujwsUlWPaf1iyopGz9!SYka<6_>+UjQnBG=?0mB80DG#R zU*SpWR$uDY#l+YVBnPhGh2g}=b*{6E?L5JL)RDS+kvrR!Jl4-e?{BUwSmUwuc+dm}4z#i;NeK?->0^XKw4&ydS@N#;aXsc}W z414tu+i_y9-68=#LNuskcK99Q3~{IiXR|A?lPK~Bm}0`$jIeZhZ4QDvKFl4Cv7>-H z(Uusyle#;^K4^i#PAnNanTlOXj6a0%xP49AJv)}vY+74cxNCW&UI@< zC*YcFDXs=z%HB3EfI<5$kVLRaJ(jl+ye@wl?u98S3JZIF?&x}2GC>g~#Q{9Ry8n7c zk)R0J>)l-J_B4cTqsf7Dse>P&6B{~^9J~V0P}miFAl#a1K8-U%MmSNrIoROd?%~ee zXYY5y!xW^Ud1xjvazpTtsU`+C8*8u6&F3l)HiQ@*S2#92!z98u(!-97Bt}M`^DbIO z(TS5SbRfty%{bLSu-QvHh$0Wg(A$fN?H(`P?DN}1C1e48b|TD+h!H)H6AT3&a$@ud zO55?nZ0p^`I1sR$!_>5ww5>G4(*TRMk~bbC&&LFd$-ywHPXU2hCPhZo9#rNYk}?PUSMFG0 zBOxI90uVecB6)eTm>{7zX#*pQ23!mBmCLh8l)AK#x<#N~IS>Cc2rA#iv%n+ed__ct z+7~EOS#Ex(d<}=`UZy5-4cMmMn5FV&?bC!*>##1o9s?h%3-CwaGxdCEcS8MBTnAQb zHb5)w8Z`c+rlMR$>0iPq!vHJ`^zAc;%)9KuIFnMwf$4i-pDBdxDg*nqWR5gsjNl!A z4VglgP;ST&GE&)KTx(TIWmrhD>qlThTaGUWg}T@9C7@nM)_k}d+aukE?Z$1U;Ci?R z5pY;kV*Lv}ZMuo^O+geB2T&edz^zi)L&2}{{IJ;d#BRwRXk}yfG7BZu_Dl9)KYOVK zwq~yT&P4YuE_yRHKFA%u0JVga$n1ofx}xLRMb-e@K1}M_4Nd5~soO1y(W@KQ!E|J= ze88PO4Xdoy*F>|?Q~#rRi~dlah?6->kMg(!VWGBT!_y#M?`B&^Pyvhq?raP6_|^R$ z(Mmc2_s-&=6_?*n2RxL6n0pzC1cNiz!-)O-N4goBZ$6YOK$8xFWTU<8*eKV1nLFJ_ z7SHKBZGoJwY@{YswQxEwRGan>%L%q0q?Or!U#KOxBjJtC2PkBmkT?HN$4`4{cYvk> z^qv6p7bYhn?dU!~(Z-z~Bwx6>eRHnN=FaAbinTD>=WkRykqF=K;VwqFzRN$Wtl|34 zqno35gl)YkfLCPS?eZ2AcK^;s8!Qy|`bqY5KNq)Zo z?zK_aliBg(VnE;HOBe$+2y3;&`j*4`6PrA)%giw* z7q%f`1$4{ooa}EM2ThY0!Mpow5qEdN*m45&^g^>V%ale z-lPgEw1HmJ%em#y;p7`Qrq1frv}-zaLX@AGYD0M-$>zT5{475uAahTuUv=GCo21RE=u`v_IfP*9UA{2peWK zBAXGZK|}=Vd)3ND9~Jb{tC($Y0o}v?FTm4WqHMDS&!55&oup)#Oc#CschdQj5`|nf vrN<1!6MbQ1{geWeYaVNge=oCtEddtG8vT?;D2B&1r5B3Of!CW_J^z8H)zU`^VNP3!}U_(GP8FFbi>mo|p@XY$Q|zW*}+ zIsbp=?uOw8{#TA;0Flf8eB6G`eUcBrr6*&%`#LFrtfPj3NXp&@BC!;)_TW%y@YI_vrS}Jn=i>LbF8|OW$>_jBl7?t$;M-eQPF15y z0~5rl^uZ^87p~m7dJ^|Ct$rpF)sJ9f+^jpYGODy$%BV>8ks{<80=kK`AL$MSb)l?^ zBo!T1Iop*}kWRNDHSU*L zJnnB?7q6t1b4L`N%`8lpA~zU3O?cg!Q52+-_SX_hHU>+M8c>JDK$JJisuZXgbBBL& z`zhIhr9hHjPS(n9(trY(6Fhh{AVCXu21RWr4M$rbGzQMlZeUn18XqxraeYSYg z7b~)V*!;5|KQ08l?3};8*fb-A%M^pp1`BO-;*u#baZ^l8iOHK{^0MRFo@=c)p5K0b z?WDN(G&Q|w@fWePv60~Uy7QaAZo1HPe(l(6WAb;0zd1bCexr8NP*d3gB8#TP6*t8d zV}VI=?eXT~wxQ&-;3;Y&jvLWcGtR(VbQyj(w8$L;-avU3uycdVapr`>DEd+;4r}pj zs5Y?0*R+wkzR|nA#{7#H5|piEw{a_Wg%|h99MMUWjEkD>MrwObs+_;PgYXxC%#)Yw zFu*(yMu(Lsfqw9Sg60jRPK)~<#hD`yXrh%3?O|kN(7w-~U;fL9Gz*yJPI`tF=`s0(eBTZqB< zAuCr2lxYazJ?xA4Ynvbp-vb7gU#Eb{Q9Sk=9h~87C;7UrctD8nG%qg~0}y<8vpXY~Z6cop_Yr0WhUp9`3{ec68}%EQCjVpt{OW$@CFkeXB^DIqq}F9R<>zM?>%&z{E@4fRerW?VOjG=pP>`#m zOMHl@uWP)2fU94;i(};Er>y4%1w5Vn{k?19lv$Mk delta 259 zcmX>gbXJh>G%qg~0}v=~+ntfaI+0I;vxAv|VLC$!L)1pY2ByjK%wo*EtC=RBV0uws z$)qV%8Vyv;m7JfKmYJ?sP|2kW16uh*7r}Npi&Zqrd&!)9M z*}d(>_VrKqwLETUd_H^K)4fxHjHf$xyln4(zP9saYwz>D>s~J0`M9Bp>m|rBez#as z5=#>> parse_location('epubcfi(/6/746[id509]!/4[abc]/10,/2/1:0,/7:8)') + ('id509', 'abc') """ idref = None filepos = None if not location: return idref, filepos + # 使用正则表达式提取[]内的内容 matches = re.findall(r'\[(.*?)\]', location) if location else [] idref = matches[0] if len(matches) > 0 else None filepos = matches[1] if len(matches) > 1 else None return idref, filepos def get_annotations(self, bookid=None): + """ + 从数据库获取笔记数据 + + 从iBooks的AEAnnotation.sqlite数据库中提取所有或指定书籍的笔记和高亮内容。 + 自动处理时间戳转换和位置信息解析。 + + Args: + bookid (str, optional): 书籍资产ID,如果为None则获取所有书籍的笔记 + + Returns: + dict: 笔记数据字典,结构为: + { + assetid: { + uuid: { + 'creationdate': '创建日期', + 'filepos': '文件位置', + 'idref': '章节标识', + 'note': '笔记内容', + 'selectedtext': '选中文本' + } + } + } + + Note: + - 会检查WAL模式相关文件(-wal, -shm)的存在性 + - 自动转换苹果时间戳格式(以2001-01-01为基准) + - 过滤掉既没有笔记也没有选中文本的空记录 + """ # 检查WAL模式相关文件 base = self.db_path.rsplit('.', 1)[0] wal_path = base + '.sqlite-wal' @@ -47,8 +102,11 @@ class AnnotationManager: for f in [self.db_path, wal_path, shm_path]: if not os.path.exists(f): print(f'警告: 缺少 {f},可能无法获取全部最新笔记') + # 连接数据库并执行查询 conn = sqlite3.connect(self.db_path) cursor = conn.cursor() + + # 根据是否指定bookid选择不同的查询语句 if bookid is not None: cursor.execute(''' SELECT ZANNOTATIONASSETID, ZANNOTATIONCREATIONDATE, ZANNOTATIONLOCATION, ZANNOTATIONNOTE, ZANNOTATIONSELECTEDTEXT, ZANNOTATIONUUID @@ -59,12 +117,16 @@ class AnnotationManager: SELECT ZANNOTATIONASSETID, ZANNOTATIONCREATIONDATE, ZANNOTATIONLOCATION, ZANNOTATIONNOTE, ZANNOTATIONSELECTEDTEXT, ZANNOTATIONUUID FROM ZAEANNOTATION ''') + rows = cursor.fetchall() annotations = defaultdict(dict) import datetime + + # 处理每一行数据 for row in rows: assetid, creationdate, location, note, selectedtext, uuid = row - # 转换 creationdate 格式,支持苹果时间戳(以2001-01-01为基准) + + # 转换 creationdate格式为'YYYY-MM-DD HH:MM:SS',支持苹果时间戳(以2001-01-01为基准) date_str = creationdate if creationdate: try: @@ -74,13 +136,20 @@ class AnnotationManager: elif isinstance(creationdate, str) and creationdate.replace('.', '', 1).isdigit(): dt = origin + datetime.timedelta(seconds=float(creationdate)) else: + # 支持原有格式'2025/9/6'等 dt = datetime.datetime.strptime(creationdate[:10], "%Y-%m-%d") - date_str = f"{dt.year}/{dt.month}/{dt.day}" + date_str = dt.strftime('%Y-%m-%d %H:%M:%S') except Exception: date_str = str(creationdate) + + # 解析位置信息 idref, filepos = self.parse_location(location) + + # 过滤空记录(既没有笔记也没有选中文本) if note is None and selectedtext is None: continue + + # 构建笔记数据结构 annotations[str(assetid)][uuid] = { 'creationdate': date_str, 'filepos': filepos, @@ -88,14 +157,26 @@ class AnnotationManager: 'note': note, 'selectedtext': selectedtext } + conn.close() + + # 根据查询类型返回相应结果 if bookid is not None: return {str(bookid): annotations.get(str(bookid), {})} return annotations if __name__ == "__main__": + """ + 测试模块功能 + + 包含两个测试用例: + 1. 测试parse_location方法解析各种格式的位置字符串 + 2. 测试get_annotations方法获取指定书籍的笔记数据 + """ manager = AnnotationManager() - # 测试 parse_location + + # 测试 parse_location 方法 + print("=== 测试位置解析功能 ===") test_locations = [ 'epubcfi(/6/746[id509]!/4[4MLOS0-27b363c65bfe41ad8429f530566a2737]/10,/2/1:0,/7:8', 'epubcfi(/6/22[id15]!/4/156/1,:21,:157)', @@ -105,7 +186,8 @@ if __name__ == "__main__": idref, filepos = manager.parse_location(loc) print(f"location: {loc}\n idref: {idref}\n filepos: {filepos}\n") - # 测试只获取特定 assetid 的笔记 + # 测试获取特定书籍的笔记 + print("=== 测试笔记获取功能 ===") test_bookid = "B18FCD9F90FD43C2373AE52BAEF9A77C" annotations = manager.get_annotations(bookid=test_bookid) from pprint import pprint diff --git a/booklist_parse.py b/booklist_parse.py index fb1c9ab..5080704 100644 --- a/booklist_parse.py +++ b/booklist_parse.py @@ -6,6 +6,7 @@ import os from collections import defaultdict class BookListManager: + def __init__(self, plist_path=None, db_path=None): self.plist_path = plist_path or config.LOCAL_BOOKS_PLIST self.db_path = db_path or config.LOCAL_LIBRARY_DB @@ -32,6 +33,93 @@ class BookListManager: 'date': book.get('BKInsertionDate',''), 'updatedate': book.get('updateDate','') } + # 统计每本书最近30天每天的阅读时长 + try: + from annotationdata import AnnotationManager + import datetime + # 每天最小阅读时长(有笔记) + READ_TIME_DAY = getattr(config, 'READ_TIME_DAY', 60) # 单位:分钟 + # 无笔记但当天有打开书籍时的阅读时长 + READ_TIME_OPEN_DAY = getattr(config, 'READ_TIME_OPEN_DAY', 30) # 单位:分钟 + today = datetime.datetime.now().date() + manager = AnnotationManager() + annotations = manager.get_annotations() + # 获取所有书籍的打开时间(ZLASTOPENDATE),单位为苹果时间戳 + books_open = self.get_books_last_open() + this_year = today.year + for bk_id in booksinfo: + notes = annotations.get(bk_id, {}) + day_notes = {} + # 收集每本书所有笔记的创建时间,按天分组 + for uuid, note in notes.items(): + raw_date = note.get('creationdate') + try: + dt = datetime.datetime.strptime(raw_date, '%Y-%m-%d %H:%M:%S') + day = dt.date() + if day not in day_notes: + day_notes[day] = [] + day_notes[day].append(dt) + except Exception: + pass + # 获取该书的打开时间戳(ZLASTOPENDATE),用于判断无笔记时是否有打开过书籍 + open_info = books_open.get(bk_id, {}) + last_open_ts = open_info.get('last_open') + # 生成最近30天的阅读时长列表 + readtime30d = [] + for i in range(30): + day = today - datetime.timedelta(days=i) + times = day_notes.get(day, []) + if not times: + opened = False + if last_open_ts: + open_dt = datetime.datetime(2001, 1, 1) + datetime.timedelta(seconds=last_open_ts) + if open_dt.date() == day: + opened = True + readtime = READ_TIME_OPEN_DAY if opened else 0 + elif len(times) == 1: + readtime = READ_TIME_DAY + else: + times_sorted = sorted(times) + total_minutes = 0 + for idx in range(1, len(times_sorted)): + delta = (times_sorted[idx] - times_sorted[idx-1]).total_seconds() / 60 + if 0 < delta <= 180: + total_minutes += int(delta) + readtime = total_minutes if total_minutes > 0 else READ_TIME_DAY + readtime30d.append(readtime) + booksinfo[bk_id]['readtime30d'] = readtime30d + + # 新增:统计今年每个月的阅读时长和年总阅读时长(遍历今年每一天) + readtime12m = [0] * 12 # 今年每月阅读时长 + readtime_year = 0 # 今年总阅读时长 + first_day = datetime.date(this_year, 1, 1) + days_in_year = (today - first_day).days + 1 + for i in range(days_in_year): + day = first_day + datetime.timedelta(days=i) + times = day_notes.get(day, []) + if not times: + opened = False + if last_open_ts: + open_dt = datetime.datetime(2001, 1, 1) + datetime.timedelta(seconds=last_open_ts) + if open_dt.date() == day: + opened = True + readtime = READ_TIME_OPEN_DAY if opened else 0 + elif len(times) == 1: + readtime = READ_TIME_DAY + else: + times_sorted = sorted(times) + total_minutes = 0 + for idx in range(1, len(times_sorted)): + delta = (times_sorted[idx] - times_sorted[idx-1]).total_seconds() / 60 + if 0 < delta <= 180: + total_minutes += int(delta) + readtime = total_minutes if total_minutes > 0 else READ_TIME_DAY + readtime12m[day.month-1] += readtime + readtime_year += readtime + booksinfo[bk_id]['readtime12m'] = readtime12m + booksinfo[bk_id]['readtime_year'] = readtime_year + except Exception as e: + print(f'警告: 统计readtime30d失败: {e}') self._booksinfo = booksinfo return booksinfo @@ -58,7 +146,45 @@ class BookListManager: self._books_open = books_open return books_open + def get_total_readtime(self, days=30): + """ + 获取最近days天每天所有书籍的总阅读时间(分钟),返回长度为days的列表。 + 列表第0项为今天,第1项为昨天,依次类推。 + """ + booksinfo = self.get_books_info() + total = [0] * days + for info in booksinfo.values(): + readtime30d = info.get('readtime30d', []) + for i in range(min(days, len(readtime30d))): + total[i] += readtime30d[i] + return total + + def get_total_readtime_year(self): + """ + 获取全年所有书的累计阅读时间(分钟)。 + """ + booksinfo = self.get_books_info() + total = 0 + for info in booksinfo.values(): + total += info.get('readtime_year', 0) + return total + + def get_total_readtime12m(self): + """ + 获取全年所有书的月度累计阅读时间(长度12的列表,单位:分钟)。 + """ + booksinfo = self.get_books_info() + total = [0] * 12 + for info in booksinfo.values(): + readtime12m = info.get('readtime12m', [0]*12) + for i in range(12): + total[i] += readtime12m[i] + return total + if __name__ == '__main__': + manager = BookListManager() + booksinfo = manager.get_books_info() + manager = BookListManager() booksinfo = manager.get_books_info() from pprint import pprint @@ -74,4 +200,48 @@ if __name__ == '__main__': for k, v in list(books_open.items())[:3]: ts = v['last_open'] dt = datetime.datetime(2001, 1, 1) + datetime.timedelta(seconds=ts) - print(f"{k}: {dt} (timestamp: {ts})") \ No newline at end of file + print(f"{k}: {dt} (timestamp: {ts})") + + # 展示最近5天有阅读行为的书籍的readtime30d信息 + print("\n【最近5天有阅读行为的书籍的readtime30d信息】") + books_with_recent_reading = [] + for book_id, book in booksinfo.items(): + readtime30d = book.get('readtime30d', []) + # 最近5天(含今天)有阅读行为 + if len(readtime30d) >= 5 and any(rt > 0 for rt in readtime30d[:5]): + books_with_recent_reading.append((book_id, book)) + for book_id, book in books_with_recent_reading: + print(f"书名: {book.get('displayname', book_id)}") + print(f"readtime30d: {book.get('readtime30d', [])}") + print('-' * 60) + + # 测试每本书今年每月和年总阅读时长 + print("\n【每本书今年每月阅读时长(分钟)和年总阅读时长】") + for k, v in booksinfo.items(): + print(f"书名: {v.get('displayname', k)}") + print(f"readtime12m: {v.get('readtime12m', [])}") + print(f"readtime_year: {v.get('readtime_year', 0)} 分钟") + print('-' * 60) + + # 测试get_total_readtime,天数可自定义 + print("\n【最近7天每天所有书籍总阅读时间(分钟)】") + total_readtime7d = manager.get_total_readtime(days=7) + for i, mins in enumerate(total_readtime7d): + if i == 0: + label = "今天" + elif i == 1: + label = "昨天" + else: + label = f"{i}天前" + print(f"{label}: {mins} 分钟") + + # 测试全年总阅读时间 + print("\n【全年所有书的累计阅读时间(分钟)】") + total_readtime_year = manager.get_total_readtime_year() + print(f"全年总阅读时间: {total_readtime_year} 分钟") + + # 测试全年每月累计阅读时间 + print("\n【全年所有书的月度累计阅读时间(分钟)】") + total_readtime12m = manager.get_total_readtime12m() + for month, mins in enumerate(total_readtime12m, start=1): + print(f"{month}月: {mins} 分钟") \ No newline at end of file diff --git a/config.py b/config.py index 569f962..140edd5 100644 --- a/config.py +++ b/config.py @@ -1,3 +1,5 @@ +# 统计用:无笔记但当天有打开书籍时的阅读时长(单位:分钟) +READ_TIME_OPEN_DAY = 15 """ config.py --------- @@ -29,5 +31,6 @@ LOCAL_ANNOTATION_WAL = os.path.join(DATA_DIR, 'AEAnnotation.sqlite-wal') LOCAL_LIBRARY_DB = os.path.join(DATA_DIR, 'BKLibrary.sqlite') LOCAL_BOOKS_PLIST = os.path.join(DATA_DIR, 'Books.plist') -# 其他可扩展配置项 +# 统计用:每日最小阅读时长(单位:秒) +READ_TIME_DAY = 60 diff --git a/kmanapp.png b/kmanapp.png deleted file mode 100644 index 6786cc16172d2419337905ca22bc847f04adade2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18468 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