novel-downloader 1.2.1__py3-none-any.whl → 1.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- novel_downloader/__init__.py +1 -2
- novel_downloader/cli/__init__.py +0 -1
- novel_downloader/cli/clean.py +2 -10
- novel_downloader/cli/download.py +18 -22
- novel_downloader/cli/interactive.py +0 -1
- novel_downloader/cli/main.py +1 -3
- novel_downloader/cli/settings.py +8 -8
- novel_downloader/config/__init__.py +0 -1
- novel_downloader/config/adapter.py +48 -18
- novel_downloader/config/loader.py +116 -108
- novel_downloader/config/models.py +41 -32
- novel_downloader/config/site_rules.py +2 -4
- novel_downloader/core/__init__.py +0 -1
- novel_downloader/core/downloaders/__init__.py +4 -4
- novel_downloader/core/downloaders/base/__init__.py +14 -0
- novel_downloader/core/downloaders/{base_async_downloader.py → base/base_async.py} +49 -53
- novel_downloader/core/downloaders/{base_downloader.py → base/base_sync.py} +64 -43
- novel_downloader/core/downloaders/biquge/__init__.py +12 -0
- novel_downloader/core/downloaders/biquge/biquge_sync.py +25 -0
- novel_downloader/core/downloaders/common/__init__.py +14 -0
- novel_downloader/core/downloaders/{common_asynb_downloader.py → common/common_async.py} +42 -33
- novel_downloader/core/downloaders/{common_downloader.py → common/common_sync.py} +34 -23
- novel_downloader/core/downloaders/qidian/__init__.py +10 -0
- novel_downloader/core/downloaders/{qidian_downloader.py → qidian/qidian_sync.py} +80 -64
- novel_downloader/core/factory/__init__.py +4 -5
- novel_downloader/core/factory/{downloader_factory.py → downloader.py} +36 -35
- novel_downloader/core/factory/{parser_factory.py → parser.py} +12 -14
- novel_downloader/core/factory/{requester_factory.py → requester.py} +29 -16
- novel_downloader/core/factory/{saver_factory.py → saver.py} +4 -9
- novel_downloader/core/interfaces/__init__.py +8 -9
- novel_downloader/core/interfaces/{async_downloader_protocol.py → async_downloader.py} +4 -5
- novel_downloader/core/interfaces/{async_requester_protocol.py → async_requester.py} +26 -12
- novel_downloader/core/interfaces/{parser_protocol.py → parser.py} +11 -6
- novel_downloader/core/interfaces/{saver_protocol.py → saver.py} +2 -3
- novel_downloader/core/interfaces/{downloader_protocol.py → sync_downloader.py} +6 -7
- novel_downloader/core/interfaces/{requester_protocol.py → sync_requester.py} +34 -17
- novel_downloader/core/parsers/__init__.py +5 -4
- novel_downloader/core/parsers/{base_parser.py → base.py} +20 -11
- novel_downloader/core/parsers/biquge/__init__.py +10 -0
- novel_downloader/core/parsers/biquge/main_parser.py +126 -0
- novel_downloader/core/parsers/{common_parser → common}/__init__.py +2 -3
- novel_downloader/core/parsers/{common_parser → common}/helper.py +20 -18
- novel_downloader/core/parsers/{common_parser → common}/main_parser.py +15 -9
- novel_downloader/core/parsers/{qidian_parser → qidian}/__init__.py +2 -3
- novel_downloader/core/parsers/{qidian_parser → qidian}/browser/__init__.py +2 -3
- novel_downloader/core/parsers/{qidian_parser → qidian}/browser/chapter_encrypted.py +41 -49
- novel_downloader/core/parsers/{qidian_parser → qidian}/browser/chapter_normal.py +17 -21
- novel_downloader/core/parsers/{qidian_parser → qidian}/browser/chapter_router.py +10 -9
- novel_downloader/core/parsers/{qidian_parser → qidian}/browser/main_parser.py +16 -12
- novel_downloader/core/parsers/{qidian_parser → qidian}/session/__init__.py +2 -3
- novel_downloader/core/parsers/{qidian_parser → qidian}/session/chapter_encrypted.py +37 -45
- novel_downloader/core/parsers/{qidian_parser → qidian}/session/chapter_normal.py +19 -23
- novel_downloader/core/parsers/{qidian_parser → qidian}/session/chapter_router.py +10 -9
- novel_downloader/core/parsers/{qidian_parser → qidian}/session/main_parser.py +16 -12
- novel_downloader/core/parsers/{qidian_parser → qidian}/session/node_decryptor.py +7 -10
- novel_downloader/core/parsers/{qidian_parser → qidian}/shared/__init__.py +2 -3
- novel_downloader/core/parsers/qidian/shared/book_info_parser.py +150 -0
- novel_downloader/core/parsers/{qidian_parser → qidian}/shared/helpers.py +9 -10
- novel_downloader/core/requesters/__init__.py +9 -5
- novel_downloader/core/requesters/base/__init__.py +16 -0
- novel_downloader/core/requesters/{base_async_session.py → base/async_session.py} +180 -73
- novel_downloader/core/requesters/base/browser.py +340 -0
- novel_downloader/core/requesters/base/session.py +364 -0
- novel_downloader/core/requesters/biquge/__init__.py +12 -0
- novel_downloader/core/requesters/biquge/session.py +90 -0
- novel_downloader/core/requesters/{common_requester → common}/__init__.py +4 -5
- novel_downloader/core/requesters/common/async_session.py +96 -0
- novel_downloader/core/requesters/common/session.py +113 -0
- novel_downloader/core/requesters/qidian/__init__.py +21 -0
- novel_downloader/core/requesters/qidian/broswer.py +306 -0
- novel_downloader/core/requesters/qidian/session.py +287 -0
- novel_downloader/core/savers/__init__.py +5 -3
- novel_downloader/core/savers/{base_saver.py → base.py} +12 -13
- novel_downloader/core/savers/biquge.py +25 -0
- novel_downloader/core/savers/{common_saver → common}/__init__.py +2 -3
- novel_downloader/core/savers/{common_saver/common_epub.py → common/epub.py} +24 -52
- novel_downloader/core/savers/{common_saver → common}/main_saver.py +43 -9
- novel_downloader/core/savers/{common_saver/common_txt.py → common/txt.py} +16 -46
- novel_downloader/core/savers/epub_utils/__init__.py +0 -1
- novel_downloader/core/savers/epub_utils/css_builder.py +13 -7
- novel_downloader/core/savers/epub_utils/initializer.py +4 -5
- novel_downloader/core/savers/epub_utils/text_to_html.py +2 -3
- novel_downloader/core/savers/epub_utils/volume_intro.py +1 -3
- novel_downloader/core/savers/{qidian_saver.py → qidian.py} +12 -6
- novel_downloader/locales/en.json +12 -4
- novel_downloader/locales/zh.json +9 -1
- novel_downloader/resources/config/settings.toml +88 -0
- novel_downloader/utils/cache.py +2 -2
- novel_downloader/utils/chapter_storage.py +340 -0
- novel_downloader/utils/constants.py +8 -5
- novel_downloader/utils/crypto_utils.py +3 -3
- novel_downloader/utils/file_utils/__init__.py +0 -1
- novel_downloader/utils/file_utils/io.py +12 -17
- novel_downloader/utils/file_utils/normalize.py +1 -3
- novel_downloader/utils/file_utils/sanitize.py +2 -9
- novel_downloader/utils/fontocr/__init__.py +0 -1
- novel_downloader/utils/fontocr/ocr_v1.py +19 -22
- novel_downloader/utils/fontocr/ocr_v2.py +147 -60
- novel_downloader/utils/hash_store.py +19 -20
- novel_downloader/utils/hash_utils.py +0 -1
- novel_downloader/utils/i18n.py +3 -4
- novel_downloader/utils/logger.py +5 -6
- novel_downloader/utils/model_loader.py +5 -8
- novel_downloader/utils/network.py +9 -10
- novel_downloader/utils/state.py +6 -7
- novel_downloader/utils/text_utils/__init__.py +0 -1
- novel_downloader/utils/text_utils/chapter_formatting.py +2 -7
- novel_downloader/utils/text_utils/diff_display.py +0 -1
- novel_downloader/utils/text_utils/font_mapping.py +1 -4
- novel_downloader/utils/text_utils/text_cleaning.py +0 -1
- novel_downloader/utils/time_utils/__init__.py +0 -1
- novel_downloader/utils/time_utils/datetime_utils.py +9 -11
- novel_downloader/utils/time_utils/sleep_utils.py +27 -13
- {novel_downloader-1.2.1.dist-info → novel_downloader-1.3.0.dist-info}/METADATA +14 -17
- novel_downloader-1.3.0.dist-info/RECORD +127 -0
- {novel_downloader-1.2.1.dist-info → novel_downloader-1.3.0.dist-info}/WHEEL +1 -1
- novel_downloader/core/parsers/qidian_parser/shared/book_info_parser.py +0 -95
- novel_downloader/core/requesters/base_browser.py +0 -210
- novel_downloader/core/requesters/base_session.py +0 -243
- novel_downloader/core/requesters/common_requester/common_async_session.py +0 -98
- novel_downloader/core/requesters/common_requester/common_session.py +0 -126
- novel_downloader/core/requesters/qidian_requester/__init__.py +0 -22
- novel_downloader/core/requesters/qidian_requester/qidian_broswer.py +0 -377
- novel_downloader/core/requesters/qidian_requester/qidian_session.py +0 -202
- novel_downloader/resources/config/settings.yaml +0 -76
- novel_downloader-1.2.1.dist-info/RECORD +0 -115
- {novel_downloader-1.2.1.dist-info → novel_downloader-1.3.0.dist-info}/entry_points.txt +0 -0
- {novel_downloader-1.2.1.dist-info → novel_downloader-1.3.0.dist-info}/licenses/LICENSE +0 -0
- {novel_downloader-1.2.1.dist-info → novel_downloader-1.3.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: novel-downloader
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.3.0
|
4
4
|
Summary: A command-line tool for downloading Chinese web novels from Qidian and similar platforms.
|
5
5
|
Author-email: Saudade Z <saudadez217@gmail.com>
|
6
6
|
License: MIT License
|
@@ -34,18 +34,16 @@ Classifier: License :: OSI Approved :: MIT License
|
|
34
34
|
Classifier: Natural Language :: Chinese (Simplified)
|
35
35
|
Classifier: Topic :: Utilities
|
36
36
|
Classifier: Programming Language :: Python :: 3
|
37
|
-
Classifier: Programming Language :: Python :: 3.8
|
38
|
-
Classifier: Programming Language :: Python :: 3.9
|
39
|
-
Classifier: Programming Language :: Python :: 3.10
|
40
|
-
Classifier: Programming Language :: Python :: 3.11
|
41
37
|
Classifier: Programming Language :: Python :: 3.12
|
42
|
-
|
38
|
+
Classifier: Programming Language :: Python :: 3.13
|
39
|
+
Requires-Python: >=3.12
|
43
40
|
Description-Content-Type: text/markdown
|
44
41
|
License-File: LICENSE
|
45
42
|
Requires-Dist: requests
|
43
|
+
Requires-Dist: aiohttp
|
46
44
|
Requires-Dist: beautifulsoup4
|
47
45
|
Requires-Dist: DrissionPage
|
48
|
-
Requires-Dist:
|
46
|
+
Requires-Dist: opencv-python
|
49
47
|
Requires-Dist: lxml
|
50
48
|
Requires-Dist: platformdirs
|
51
49
|
Requires-Dist: click
|
@@ -64,10 +62,9 @@ Requires-Dist: scipy; extra == "font-recovery"
|
|
64
62
|
Requires-Dist: numpy; extra == "font-recovery"
|
65
63
|
Requires-Dist: tinycss2; extra == "font-recovery"
|
66
64
|
Requires-Dist: fonttools; extra == "font-recovery"
|
65
|
+
Requires-Dist: brotli; extra == "font-recovery"
|
67
66
|
Requires-Dist: pillow; extra == "font-recovery"
|
68
67
|
Requires-Dist: huggingface_hub; extra == "font-recovery"
|
69
|
-
Provides-Extra: async
|
70
|
-
Requires-Dist: aiohttp; extra == "async"
|
71
68
|
Dynamic: license-file
|
72
69
|
|
73
70
|
# novel-downloader
|
@@ -94,13 +91,10 @@ pip install novel-downloader
|
|
94
91
|
# 如需支持字体解密功能 (decode_font), 请使用:
|
95
92
|
# pip install novel-downloader[font-recovery]
|
96
93
|
|
97
|
-
#
|
98
|
-
# pip install novel-downloader[async]
|
99
|
-
|
100
|
-
# 初始化默认配置 (生成 settings.yaml)
|
94
|
+
# 初始化默认配置 (生成 settings.toml)
|
101
95
|
novel-cli settings init
|
102
96
|
|
103
|
-
# 编辑 ./settings.
|
97
|
+
# 编辑 ./settings.toml 完成 site/book_ids 等
|
104
98
|
# 可查看 docs/4-settings-schema.md
|
105
99
|
|
106
100
|
# 运行下载
|
@@ -117,7 +111,6 @@ cd novel-downloader
|
|
117
111
|
pip install .
|
118
112
|
# 或安装带可选功能:
|
119
113
|
# pip install .[font-recovery]
|
120
|
-
# pip install .[async]
|
121
114
|
```
|
122
115
|
|
123
116
|
更多使用方法, 查看 [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-usage-examples.md)
|
@@ -127,7 +120,10 @@ pip install .
|
|
127
120
|
## 功能特性
|
128
121
|
|
129
122
|
- 爬取起点中文网的小说章节内容 (支持免费与已订阅章节)
|
130
|
-
-
|
123
|
+
- 断点续爬
|
124
|
+
- 自动整合所有章节并导出为
|
125
|
+
- TXT
|
126
|
+
- EPUB
|
131
127
|
- 支持活动广告过滤:
|
132
128
|
- [x] 章节标题
|
133
129
|
- [ ] 章节正文
|
@@ -141,8 +137,9 @@ pip install .
|
|
141
137
|
- [安装](https://github.com/BowenZ217/novel-downloader/blob/main/docs/1-installation.md)
|
142
138
|
- [环境准备](https://github.com/BowenZ217/novel-downloader/blob/main/docs/2-environment-setup.md)
|
143
139
|
- [配置](https://github.com/BowenZ217/novel-downloader/blob/main/docs/3-configuration.md)
|
144
|
-
- [settings.
|
140
|
+
- [settings.toml 配置说明](https://github.com/BowenZ217/novel-downloader/blob/main/docs/4-settings-schema.md)
|
145
141
|
- [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-usage-examples.md)
|
142
|
+
- [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-supported-sites.md)
|
146
143
|
- [文件保存](https://github.com/BowenZ217/novel-downloader/blob/main/docs/file-saving.md)
|
147
144
|
- [TODO](https://github.com/BowenZ217/novel-downloader/blob/main/docs/todo.md)
|
148
145
|
- [开发](https://github.com/BowenZ217/novel-downloader/blob/main/docs/develop.md)
|
@@ -0,0 +1,127 @@
|
|
1
|
+
novel_downloader/__init__.py,sha256=eWGESJtMUUHTCWUQHmDXJ5aOFLJVc9qUXiG-D_qc7yY,218
|
2
|
+
novel_downloader/cli/__init__.py,sha256=-2HAut_U1e67MZGdvbpEJ1n5J-bRchzto6L4c-nWeXY,174
|
3
|
+
novel_downloader/cli/clean.py,sha256=yjPDEoJRZnP_Xq-y0vA2ZhyNP-GxCM1UosW4gVR3VBI,3687
|
4
|
+
novel_downloader/cli/download.py,sha256=Q6ySIHalzX5qRTs_R1VH2xWpFbw88_UIiqYy0olxqeg,3995
|
5
|
+
novel_downloader/cli/interactive.py,sha256=7VLwKpDnSlNrA9biRu8v1bzur-khT5509RtW7EzFNvU,2130
|
6
|
+
novel_downloader/cli/main.py,sha256=JJdIEhMyjo_W2M-Uo3tstnSWDbmip9-UqSTVN6-YV3I,1128
|
7
|
+
novel_downloader/cli/settings.py,sha256=WOpy6aIsL4g_Zn_1kL4GEpkKiyxmgnTVMBhwJFo9J8s,6532
|
8
|
+
novel_downloader/config/__init__.py,sha256=-d48yLzDISMNUTxNTcAJGNKPXiZCB6Jv6yQBNO28A-8,1105
|
9
|
+
novel_downloader/config/adapter.py,sha256=kbqOHXHpf6TYxOPgkAa-4EzSKcpJEvzfm48iWaVQhMs,6965
|
10
|
+
novel_downloader/config/loader.py,sha256=EeMJqM0erh8lV-eYbd9TyrWmPRi7VEEkSTFb8YMxYHU,5745
|
11
|
+
novel_downloader/config/models.py,sha256=FE58OXH70eJE2R-TGSOWz2OaLjI9uvcEsyq-5uHrj48,5231
|
12
|
+
novel_downloader/config/site_rules.py,sha256=Win_tfW9jlpktfg4PZCl8R_GKqJaSbwL_9zIFUsF0WE,2964
|
13
|
+
novel_downloader/core/__init__.py,sha256=lg9azoOEI0W1pO1FgPLIJ2fUWLSYyNdng2NNR7Jl_NI,755
|
14
|
+
novel_downloader/core/downloaders/__init__.py,sha256=YCDQDJNkk2zAhXF4y6XuXjGNTJobdpSUZ8Kc40xDCi8,580
|
15
|
+
novel_downloader/core/downloaders/base/__init__.py,sha256=lGbWlGjbtP2GeHr9BJ4s56QDhA13XT66PX-SNkWjCo8,257
|
16
|
+
novel_downloader/core/downloaders/base/base_async.py,sha256=_lgq0Wzf5HebMCFub1zgGDJ74gprXpz0OqID9m80UoQ,4200
|
17
|
+
novel_downloader/core/downloaders/base/base_sync.py,sha256=En_8TVQOwqseee0y0bLHkLJc3xpohG-oRccgbK1gf4M,5824
|
18
|
+
novel_downloader/core/downloaders/biquge/__init__.py,sha256=uZgwfQxpPotRSi7WILOyybl4Ztjnmas2SLR3tXLXm6I,196
|
19
|
+
novel_downloader/core/downloaders/biquge/biquge_sync.py,sha256=T_9flRTQ-CRYuzCpsLxeFvR_lMFkzQiSTLNKfZWfJb0,785
|
20
|
+
novel_downloader/core/downloaders/common/__init__.py,sha256=KZwtl9K7ZhN8Nnnk9BBOVWZ-QJ5GQBsRAoG7x2OlVYM,273
|
21
|
+
novel_downloader/core/downloaders/common/common_async.py,sha256=SNgxQWkAIBj0_d5b-ClwP-T9CjPJimzgdWJXXsIyv58,7456
|
22
|
+
novel_downloader/core/downloaders/common/common_sync.py,sha256=vIPoH6bCm1vCHg5rW_HxZg9vehuS0Cy1wr3B97G0qhY,6966
|
23
|
+
novel_downloader/core/downloaders/qidian/__init__.py,sha256=LCdrqLI2iTCGERmlAwKtr-PKtvDh_rs2vKD891-ixvo,189
|
24
|
+
novel_downloader/core/downloaders/qidian/qidian_sync.py,sha256=hGm24AnKRjkKEPDUUdXmdYH34coC3iwo21kRSBIcdeI,8065
|
25
|
+
novel_downloader/core/factory/__init__.py,sha256=q9eT93OcyyUDoZxwhKWdgb_kUTIJ8nGSisH0wuiuBSw,687
|
26
|
+
novel_downloader/core/factory/downloader.py,sha256=7uxGPIAKGwyJ-OfmyCM8a3ON0M4sHz0p6rMie2o7ts4,4878
|
27
|
+
novel_downloader/core/factory/parser.py,sha256=h3KZIDArl66Q1qeI_h778snglhmRYrkS6872B9YiQgA,1684
|
28
|
+
novel_downloader/core/factory/requester.py,sha256=oY1x8eMn982BG6oSlt3gSyYy_eT7bJgph9Uj_FgMvcI,3283
|
29
|
+
novel_downloader/core/factory/saver.py,sha256=v0wb7XlbxNRyczrfkU1DXtFmp_HCjd7u4wM27mkamfQ,1144
|
30
|
+
novel_downloader/core/interfaces/__init__.py,sha256=ym_dJH0x9UCMvqgIiy9__Tkek7hy_LS_i56KhpOKnXQ,791
|
31
|
+
novel_downloader/core/interfaces/async_downloader.py,sha256=uMEOpbgYAK0aAVSdnnRVCoCAiwhggovz9mEBV6uPEpo,961
|
32
|
+
novel_downloader/core/interfaces/async_requester.py,sha256=_LPbfWw2uJf7r3RbLjUdRRJWQJrSLRsUFHyrXC3XEds,2139
|
33
|
+
novel_downloader/core/interfaces/parser.py,sha256=THAlFhZ8MPYtH_Ulbv6Cw_nKwekQDbLfsytsMMt3NzE,1331
|
34
|
+
novel_downloader/core/interfaces/saver.py,sha256=BjXNeQLTC7Bg5mGyFoL9Yq7V1kC53POjXOWG9BFVBf4,1580
|
35
|
+
novel_downloader/core/interfaces/sync_downloader.py,sha256=1QRH0aQFd_xwliMXUyiEhog2k2tj2pMkJBjJyKbx7cs,917
|
36
|
+
novel_downloader/core/interfaces/sync_requester.py,sha256=sMtJVWdqwAgar_yMSE-ZcivdUCJgvHjtZWX_c2mX-XA,1926
|
37
|
+
novel_downloader/core/parsers/__init__.py,sha256=4bO2zhVFqg02G6xZ8fm-r-_AexQgwpQ2iQepQV5PESw,524
|
38
|
+
novel_downloader/core/parsers/base.py,sha256=lyS9Uv95ttQePr5_JXt-tmLbgQBJMr13EvSgBRGoYSQ,3099
|
39
|
+
novel_downloader/core/parsers/biquge/__init__.py,sha256=oaLgBLdMW7DcdsEGXW-S91Z6_p5XmBcBIp7TX79hmV4,173
|
40
|
+
novel_downloader/core/parsers/biquge/main_parser.py,sha256=Dx4Oilqi-lpoRd8O99BSwI0qqvdVT_RCLGjDcm5ZhTc,4408
|
41
|
+
novel_downloader/core/parsers/common/__init__.py,sha256=MzNUUxvf7jmeO0LvQ_FRW0QLKuYVXkkTgJ4CxWZKF-Y,341
|
42
|
+
novel_downloader/core/parsers/common/helper.py,sha256=SSNES1AlGu5LqPXN61Rp6SmVgR4oKaI2gvLJfEYBsF4,12054
|
43
|
+
novel_downloader/core/parsers/common/main_parser.py,sha256=SaUxkcHl5UHKPzDVlHAUwAOyV1F6X1tDQZJJpeDCse8,2987
|
44
|
+
novel_downloader/core/parsers/qidian/__init__.py,sha256=-KvMBzggUaj5zeZKFpVbbx5GRJdykwaFPfxJc4KzF-Q,484
|
45
|
+
novel_downloader/core/parsers/qidian/browser/__init__.py,sha256=6oRiuhptJHjNh9lhfe9bjh2_9fKv9h8f_mv-VvCs48o,315
|
46
|
+
novel_downloader/core/parsers/qidian/browser/chapter_encrypted.py,sha256=UyBX5NqfCPappbVhMP_mPCYUsTJesgkOs_VjrCjlfj4,17407
|
47
|
+
novel_downloader/core/parsers/qidian/browser/chapter_normal.py,sha256=1I6PkrGEOjHvz-VRdjm9PfXFA8uInifkVYgw597Xqto,3014
|
48
|
+
novel_downloader/core/parsers/qidian/browser/chapter_router.py,sha256=92pczn5_had3I5i67bmt2Qeg4pmOh5MquYtwJ3_ByzE,2124
|
49
|
+
novel_downloader/core/parsers/qidian/browser/main_parser.py,sha256=dwqEUG60Il5tWCpA8NCmazegm4fqmuXgV9oq8wNm0fw,3898
|
50
|
+
novel_downloader/core/parsers/qidian/session/__init__.py,sha256=hNxcEkTgD7mTZukHjCQm7L62HBiT7GUCOu6JZjPYwTY,308
|
51
|
+
novel_downloader/core/parsers/qidian/session/chapter_encrypted.py,sha256=kGL8mClK-fThEVNw8FQEnFTqJhn7kaoapFjK01NCpEo,15727
|
52
|
+
novel_downloader/core/parsers/qidian/session/chapter_normal.py,sha256=T3UOKRf0ET-ngz0oMfsSRU_24ODIaJ_wWdNo0CDcabU,3688
|
53
|
+
novel_downloader/core/parsers/qidian/session/chapter_router.py,sha256=dHumd9itE8Scai_h7pctI0o21f6HL4DtMpZpSZg2KDk,2001
|
54
|
+
novel_downloader/core/parsers/qidian/session/main_parser.py,sha256=h6sE0XByX1gbdUAi1sDEgmJ2xBANsicX5DB8T1IDPwo,3989
|
55
|
+
novel_downloader/core/parsers/qidian/session/node_decryptor.py,sha256=qKJ6BDkshtQNSHmrjnsbIVvAX08g4MmwIrQFJmakvYU,5629
|
56
|
+
novel_downloader/core/parsers/qidian/shared/__init__.py,sha256=9Yb0qCiszbIfuUUQ1gHKcW1xjItHGJug8aM2cSKLcY8,940
|
57
|
+
novel_downloader/core/parsers/qidian/shared/book_info_parser.py,sha256=kKkRuF1QUA97VlX7XO7zcwAjWhjIqYB5onx_uzvjj6g,4809
|
58
|
+
novel_downloader/core/parsers/qidian/shared/helpers.py,sha256=xdzWMHaQI_7K-SEV8iUcIj577ys2B2eU_IbOIC7Ilwk,4249
|
59
|
+
novel_downloader/core/requesters/__init__.py,sha256=bBUdZtuWqWnjdhUx8Hu7iajsBY3T18JLRFobh-u_45w,718
|
60
|
+
novel_downloader/core/requesters/base/__init__.py,sha256=lIwwdQWDf83NbAikZGFKUnkSa2jKX4jqKnSy0_zRRlg,296
|
61
|
+
novel_downloader/core/requesters/base/async_session.py,sha256=xayGshZd1tb98kfuMjC4KF8DQlU_SeoPk6m6vowLr9E,13000
|
62
|
+
novel_downloader/core/requesters/base/browser.py,sha256=FY_H6MLvUZllFyeYcfWsvBJnMhfouVrsQs4i5WYy9Tw,10609
|
63
|
+
novel_downloader/core/requesters/base/session.py,sha256=x7PAU8f2Lk3B7n6gIg58Ul6MqK5btZLQKkidEpKuETk,10526
|
64
|
+
novel_downloader/core/requesters/biquge/__init__.py,sha256=T1PQXdHUEhR0WRltFW4aTOCWIF3isWXUFx-WjUXU99M,184
|
65
|
+
novel_downloader/core/requesters/biquge/session.py,sha256=toSdptICqGWknk_J5Ocb0yQ8oD0zXQDCiA1qbpXzTzA,2563
|
66
|
+
novel_downloader/core/requesters/common/__init__.py,sha256=QRYbc4QEUszv_bgBdKCXHq82K8MIUhgYDMnm3iceAzI,465
|
67
|
+
novel_downloader/core/requesters/common/async_session.py,sha256=5RwGAXmtCdeLCuMkqT_ZyERi1lYgOpUE1gTTf_f2uck,3051
|
68
|
+
novel_downloader/core/requesters/common/session.py,sha256=bqr4viPytTaD6XRuXEcSjSe6CfI9zABqhbITFvyrx-Y,3430
|
69
|
+
novel_downloader/core/requesters/qidian/__init__.py,sha256=DyiuNMoBVAVxMJspdElgQ8fvpf4gKtCAc_nqW4qQ0rM,506
|
70
|
+
novel_downloader/core/requesters/qidian/broswer.py,sha256=stTywDGR7BdcLnZaNNgkPyo2PRERwollrAKeuOgrJR8,10642
|
71
|
+
novel_downloader/core/requesters/qidian/session.py,sha256=TsszpDnUQa5Lj0KpnQccowIS1APITnZ2v63OXbQmCtg,9036
|
72
|
+
novel_downloader/core/savers/__init__.py,sha256=Wnjy7te6uZtSbD_lo2wCBrMprATu8y-UAh-S-HEI9q4,427
|
73
|
+
novel_downloader/core/savers/base.py,sha256=7KCfDdE_Tq5nyoGGenXryd-nortwbJvZKteMsldCpi0,5514
|
74
|
+
novel_downloader/core/savers/biquge.py,sha256=8kZ6hcXuXmaizaKKytjy3LIGX5kMJRwgUJdhYzH5ajE,445
|
75
|
+
novel_downloader/core/savers/qidian.py,sha256=HtIz2SHLL-BZbVuahu6voKpgWtlli14_YYCA9qv7pMA,704
|
76
|
+
novel_downloader/core/savers/common/__init__.py,sha256=V5EbaRwmdXlQKi4Ce9SLstl0c9x9yTTRUB23rXFCvXw,256
|
77
|
+
novel_downloader/core/savers/common/epub.py,sha256=ZmTt3kAODa8lQKpgegbJY_7qcKQmoVQ8h5JPZYBSJbI,6370
|
78
|
+
novel_downloader/core/savers/common/main_saver.py,sha256=O-XFfDAlOun0kSBBHEIktwUcm47CsXgmFOkyK3fP_1M,3826
|
79
|
+
novel_downloader/core/savers/common/txt.py,sha256=nsYQp3-xti5uAj3r736ZKDg0gzrme2YoQ8bUTOG2jHk,4853
|
80
|
+
novel_downloader/core/savers/epub_utils/__init__.py,sha256=ZzOOsuHL9O0CsE9xl0vAcvxxlBz4AkDVOlTnsfNGN7Q,714
|
81
|
+
novel_downloader/core/savers/epub_utils/css_builder.py,sha256=sa8t72YNKkGeqC6dflXz1iefcpUqZ6t80VVZE91XfNw,2087
|
82
|
+
novel_downloader/core/savers/epub_utils/initializer.py,sha256=6qId5UTSjXnqSs9S9XrWGELAgq4fNut71CYIq7b6LQE,3199
|
83
|
+
novel_downloader/core/savers/epub_utils/text_to_html.py,sha256=omqob2yKFYru2dvHTQuGVQH9RH_VQXxMSopzwd_-6_I,4087
|
84
|
+
novel_downloader/core/savers/epub_utils/volume_intro.py,sha256=aJAdEU2YvYkX2sqnU5VkZ33XRfb5x083GdDmDXcQpUI,1771
|
85
|
+
novel_downloader/locales/en.json,sha256=Q7dkpaet_NyjMRIHiBaIu5JBz1pX3-VdffpcwYBZbRQ,5888
|
86
|
+
novel_downloader/locales/zh.json,sha256=Aq1zxm18-ijifsExK5kZeBLDV8drYRsHslsjJd9v9QU,5782
|
87
|
+
novel_downloader/resources/config/rules.toml,sha256=hrED6h3Z3cjSY5hRPQhp4TFAU5QXnN9xHfVABOJQNrM,4979
|
88
|
+
novel_downloader/resources/config/settings.toml,sha256=yLqI38FhY6d9_fiKUSsabIo_YRR3j8uRDTu85Bf70uQ,3769
|
89
|
+
novel_downloader/resources/css_styles/main.css,sha256=WM6GePwdOGgM86fbbOxQ0_0oerTBDZeQHt8zRVfcJp8,1617
|
90
|
+
novel_downloader/resources/css_styles/volume-intro.css,sha256=6gaUnNKkrb2w8tYJRq1BGD1FwbhT1I5W2GI_Zelo9G4,1156
|
91
|
+
novel_downloader/resources/images/volume_border.png,sha256=2dEVimnTHKOfLMhi7bhkh_5joWNnrqg8duomLSNOZx4,28613
|
92
|
+
novel_downloader/resources/js_scripts/qidian_decrypt_node.js,sha256=spNrk_gXI7pPW9abr4XGc2LASMe1UuN4BUe4cH24L8s,2195
|
93
|
+
novel_downloader/resources/json/replace_word_map.json,sha256=ptL9sGO9aK7rnnAaOIyZ0OiH7gaT0BhFzficzYZSDks,55
|
94
|
+
novel_downloader/resources/text/blacklist.txt,sha256=sovK9JgARZP3lud5b1EZgvv8LSVKPthf4ADpCSZZgQ8,154
|
95
|
+
novel_downloader/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
96
|
+
novel_downloader/utils/cache.py,sha256=_jVEWAaMGY86kWVnAuuIvJA_hA8dH2ClI7o3p99mEvE,618
|
97
|
+
novel_downloader/utils/chapter_storage.py,sha256=bbQ6mLvIzhWNUypIcgg6-nPo1_fIgGCOyQ3eejuvcK4,10100
|
98
|
+
novel_downloader/utils/constants.py,sha256=7qr6_w7bM-Xf-Pz6ZFgamsR9JNl7RgFNmHvSWN2fcgg,5507
|
99
|
+
novel_downloader/utils/crypto_utils.py,sha256=9uMNpg0rAWaoD3YsqDucFajPjoQuwEfeZDInUBISuSI,4166
|
100
|
+
novel_downloader/utils/hash_store.py,sha256=pFg2SzPF9sZqezNx-24jSDvYw4YMSBOpiIjyUbZx_r8,9739
|
101
|
+
novel_downloader/utils/hash_utils.py,sha256=oaXICmANsqImXW1qNSVmVQbMfNbx35FHe7EHL2VT2tM,2974
|
102
|
+
novel_downloader/utils/i18n.py,sha256=pdAcSIA5Tp-uPEBwNByHL7C1NayTnpOsl7zFv9p2G1k,1033
|
103
|
+
novel_downloader/utils/logger.py,sha256=n5xggOejtNdZv2X9f4Cq8weOaT1KG0n0-im2LIYycu0,3377
|
104
|
+
novel_downloader/utils/model_loader.py,sha256=JKgRFrr4HlAW9zuDUBAuuo_Kk_T_g9dWiU8E3zYk0vo,1996
|
105
|
+
novel_downloader/utils/network.py,sha256=2VX33jo11tzHSqvinpd0VoXDkXK_uxdxqaq50ndWXXg,8622
|
106
|
+
novel_downloader/utils/state.py,sha256=FcNJ85GvBu7uEIjy0QHGr4sXMbHPEMkCjwUKNg5EabI,5132
|
107
|
+
novel_downloader/utils/file_utils/__init__.py,sha256=zvOm2qSEmWd_mRGJceGBZb5MYMSDAlWYjS5MkVQNZgI,1159
|
108
|
+
novel_downloader/utils/file_utils/io.py,sha256=73uRrr41_Obqy9HMFBnvcOcApMZYBNi4KWT4DpskSk8,7472
|
109
|
+
novel_downloader/utils/file_utils/normalize.py,sha256=MrsCq4FqmskKRkHRV_J0z0dmn69OerMum-9sqx2XOGM,2023
|
110
|
+
novel_downloader/utils/file_utils/sanitize.py,sha256=rE-u4vpDL10zH8FT8d9wqwWsz-7dR6PJ-LE45K8VaeE,2112
|
111
|
+
novel_downloader/utils/fontocr/__init__.py,sha256=fe-04om3xxBvFKt5BBCApXCzv-Z0K_AY7lv9IB1jEHM,543
|
112
|
+
novel_downloader/utils/fontocr/ocr_v1.py,sha256=WreUf0E0n9oDP2C2JBDKVT2_Lsm46zKuFjYAySI64LY,11209
|
113
|
+
novel_downloader/utils/fontocr/ocr_v2.py,sha256=HpcHZYl3BCMICqeznghkUre0shHRO9KhhjrGSrtFbzE,27311
|
114
|
+
novel_downloader/utils/text_utils/__init__.py,sha256=JQtEnJ22K9o_syY5PV4Bf_p4BKZonX5g9-A5eqlxpZs,806
|
115
|
+
novel_downloader/utils/text_utils/chapter_formatting.py,sha256=9y5TodhpC-FNh5cTHNQNKDEMF9WLyA4pCxuGTLzY6KA,1279
|
116
|
+
novel_downloader/utils/text_utils/diff_display.py,sha256=aUvjMcYO-1_P8ZYiYbmYbJOByKo2bWoBV_ifRuAqwb8,2528
|
117
|
+
novel_downloader/utils/text_utils/font_mapping.py,sha256=ePpNLSrIEwIpqpnPZBMODj-gyNAPjLITyt6JQ50EfNw,867
|
118
|
+
novel_downloader/utils/text_utils/text_cleaning.py,sha256=KQhTGKiSX-eBVmjSpggxtf1hSjQLTu3cIjt65Ir4SWs,1632
|
119
|
+
novel_downloader/utils/time_utils/__init__.py,sha256=BwlvP0T9ABLdIxfcZXPmPv2yWO6F3-JiPwnBJioLouE,556
|
120
|
+
novel_downloader/utils/time_utils/datetime_utils.py,sha256=DfKobjxcrJlPdB32rqaP2ZCQ6ORohiwt6n2TRFrBraM,4815
|
121
|
+
novel_downloader/utils/time_utils/sleep_utils.py,sha256=FshjN9WW-y_pC5GJmN62J7uwXtRBzW3Wr82CEhg_UnU,1809
|
122
|
+
novel_downloader-1.3.0.dist-info/licenses/LICENSE,sha256=XgmnH0mBf-qEiizoVAfJQAKzPB9y3rBa-ni7M0Aqv4A,1066
|
123
|
+
novel_downloader-1.3.0.dist-info/METADATA,sha256=iOt-Pm4Bib-mlFWTwXCGxicG_yDbc8BL1NohQINm2LI,6154
|
124
|
+
novel_downloader-1.3.0.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
125
|
+
novel_downloader-1.3.0.dist-info/entry_points.txt,sha256=v23QrJrfrAcYpxUYslCVxubOVRRTaTw7vlG_tfMsFP8,65
|
126
|
+
novel_downloader-1.3.0.dist-info/top_level.txt,sha256=hP4jYWM2LTm1jxsW4hqEB8N0dsRvldO2QdhggJT917I,17
|
127
|
+
novel_downloader-1.3.0.dist-info/RECORD,,
|
@@ -1,95 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
# -*- coding: utf-8 -*-
|
3
|
-
"""
|
4
|
-
novel_downloader.core.parsers.qidian_parser.shared.book_info_parser
|
5
|
-
-------------------------------------------------------------------
|
6
|
-
|
7
|
-
This module provides parsing of Qidian book info pages.
|
8
|
-
|
9
|
-
It extracts metadata such as title, author, cover URL, update
|
10
|
-
time, status, word count, summary, and volume-chapter structure.
|
11
|
-
"""
|
12
|
-
|
13
|
-
import logging
|
14
|
-
import re
|
15
|
-
from typing import Any, Dict
|
16
|
-
|
17
|
-
from bs4.element import Tag
|
18
|
-
|
19
|
-
from .helpers import html_to_soup
|
20
|
-
|
21
|
-
logger = logging.getLogger(__name__)
|
22
|
-
|
23
|
-
|
24
|
-
def _chapter_url_to_id(url: str) -> str:
|
25
|
-
"""
|
26
|
-
Extract chapterId as the last non-empty segment of the URL.
|
27
|
-
"""
|
28
|
-
return url.rstrip("/").split("/")[-1]
|
29
|
-
|
30
|
-
|
31
|
-
def _get_volume_name(vol_div: Tag) -> str:
|
32
|
-
"""
|
33
|
-
Extracts the volume title from a <div class="volume"> element
|
34
|
-
"""
|
35
|
-
h3 = vol_div.select_one("h3")
|
36
|
-
if not h3:
|
37
|
-
return ""
|
38
|
-
for a in h3.find_all("a"):
|
39
|
-
a.decompose()
|
40
|
-
text: str = h3.get_text(strip=True)
|
41
|
-
return text.split(chr(183))[0].strip()
|
42
|
-
|
43
|
-
|
44
|
-
def parse_book_info(html_str: str) -> Dict[str, Any]:
|
45
|
-
"""
|
46
|
-
Extract metadata: title, author, cover_url, update_time, status,
|
47
|
-
word_count, summary, and volumes with chapters.
|
48
|
-
|
49
|
-
:param html_str: Raw HTML of the book info page.
|
50
|
-
:return: A dict containing book metadata.
|
51
|
-
"""
|
52
|
-
info: Dict[str, Any] = {}
|
53
|
-
try:
|
54
|
-
soup = html_to_soup(html_str)
|
55
|
-
info["book_name"] = soup.select_one("em#bookName").get_text(strip=True)
|
56
|
-
info["author"] = soup.select_one("a.writer").get_text(strip=True)
|
57
|
-
info["cover_url"] = soup.select_one("div.book-img img")["src"].strip()
|
58
|
-
info["update_time"] = (
|
59
|
-
soup.select_one("span.book-update-time")
|
60
|
-
.get_text(strip=True)
|
61
|
-
.replace("更新时间", "")
|
62
|
-
.strip()
|
63
|
-
)
|
64
|
-
info["serial_status"] = soup.select_one("span.blue").get_text(strip=True)
|
65
|
-
# word count via regex
|
66
|
-
match = re.search(
|
67
|
-
r"<em>([\d.]+)</em>\s*<cite>(.*?)字</cite>",
|
68
|
-
html_str,
|
69
|
-
)
|
70
|
-
if match:
|
71
|
-
info["word_count"] = match.group(1) + match.group(2) + "字"
|
72
|
-
else:
|
73
|
-
info["word_count"] = "Unknown"
|
74
|
-
info["summary"] = soup.select_one("div.book-intro p").get_text(
|
75
|
-
separator="\n", strip=True
|
76
|
-
)
|
77
|
-
# volumes
|
78
|
-
vols = []
|
79
|
-
for vol_div in soup.select("div.volume-wrap div.volume"):
|
80
|
-
name = _get_volume_name(vol_div)
|
81
|
-
chaps = []
|
82
|
-
for li in vol_div.select("li"):
|
83
|
-
a = li.select_one("a")
|
84
|
-
chaps.append(
|
85
|
-
{
|
86
|
-
"title": a.get_text(strip=True),
|
87
|
-
"url": a["href"].strip(),
|
88
|
-
"chapterId": _chapter_url_to_id(a["href"]),
|
89
|
-
}
|
90
|
-
)
|
91
|
-
vols.append({"volume_name": name, "chapters": chaps})
|
92
|
-
info["volumes"] = vols
|
93
|
-
except Exception as e:
|
94
|
-
logger.warning("[Parser] Error parsing book info: %s", e)
|
95
|
-
return info
|
@@ -1,210 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
# -*- coding: utf-8 -*-
|
3
|
-
"""
|
4
|
-
novel_downloader.core.requesters.base_browser
|
5
|
-
---------------------------------------------
|
6
|
-
|
7
|
-
This module defines the BaseBrowser class, which provides common functionalities
|
8
|
-
for browser operations. Derived classes can extend these methods for
|
9
|
-
specialized purposes.
|
10
|
-
"""
|
11
|
-
|
12
|
-
import abc
|
13
|
-
import logging
|
14
|
-
from typing import Any, Dict, Optional
|
15
|
-
|
16
|
-
from DrissionPage import Chromium, ChromiumOptions, ChromiumPage
|
17
|
-
|
18
|
-
from novel_downloader.config.models import RequesterConfig
|
19
|
-
from novel_downloader.core.interfaces import RequesterProtocol
|
20
|
-
from novel_downloader.utils.constants import (
|
21
|
-
DEFAULT_USER_AGENT,
|
22
|
-
DEFAULT_USER_DATA_DIR,
|
23
|
-
DEFAULT_USER_PROFILE_NAME,
|
24
|
-
)
|
25
|
-
|
26
|
-
logger = logging.getLogger(__name__)
|
27
|
-
|
28
|
-
|
29
|
-
def _is_valid(value: str) -> bool:
|
30
|
-
return bool(value and value.strip())
|
31
|
-
|
32
|
-
|
33
|
-
class BaseBrowser(RequesterProtocol, abc.ABC):
|
34
|
-
"""
|
35
|
-
BaseBrowser wraps basic browser operations using DrissionPage,
|
36
|
-
with full control over browser configuration, session profile,
|
37
|
-
retry and timeout behavior.
|
38
|
-
|
39
|
-
Attributes:
|
40
|
-
_options (ChromiumOptions): Configuration object for Chromium.
|
41
|
-
_browser (Chromium): Chromium instance.
|
42
|
-
_page (ChromiumPage): The active browser tab.
|
43
|
-
"""
|
44
|
-
|
45
|
-
def _init_browser(self, config: RequesterConfig) -> None:
|
46
|
-
"""
|
47
|
-
Initialize the browser with specified options from RequesterConfig.
|
48
|
-
|
49
|
-
:param config: Configuration settings for
|
50
|
-
browser behavior, profile, timeouts, etc.
|
51
|
-
"""
|
52
|
-
self._config = config
|
53
|
-
self._options = ChromiumOptions()
|
54
|
-
|
55
|
-
user_data_path = (
|
56
|
-
config.user_data_folder
|
57
|
-
if _is_valid(config.user_data_folder)
|
58
|
-
else DEFAULT_USER_DATA_DIR
|
59
|
-
)
|
60
|
-
if _is_valid(config.user_data_folder):
|
61
|
-
logger.warning(
|
62
|
-
"[browser] Using user_data_folder='%s'. "
|
63
|
-
"This may interfere with an active Chrome session. "
|
64
|
-
"Do NOT use this profile in both the browser and "
|
65
|
-
"this script at the same time.",
|
66
|
-
config.user_data_folder,
|
67
|
-
)
|
68
|
-
self._options.set_user_data_path(user_data_path)
|
69
|
-
|
70
|
-
profile_name = (
|
71
|
-
config.profile_name
|
72
|
-
if _is_valid(config.profile_name)
|
73
|
-
else DEFAULT_USER_PROFILE_NAME
|
74
|
-
)
|
75
|
-
self._options.set_user(profile_name)
|
76
|
-
|
77
|
-
self._options.headless(config.headless)
|
78
|
-
self._options.set_user_agent(DEFAULT_USER_AGENT)
|
79
|
-
self._options.set_timeouts(base=config.wait_time)
|
80
|
-
self._options.set_retry(
|
81
|
-
times=config.retry_times, interval=config.retry_interval
|
82
|
-
)
|
83
|
-
|
84
|
-
self._disable_images_orig = config.disable_images
|
85
|
-
if config.disable_images:
|
86
|
-
self._options.no_imgs(True)
|
87
|
-
if config.mute_audio:
|
88
|
-
self._options.mute(True)
|
89
|
-
|
90
|
-
# self._options.set_argument('--disable-blink-features', 'AutomationControlled')
|
91
|
-
# self._options.set_argument('--log-level', '3')
|
92
|
-
# self._options.set_argument('--disable-gpu')
|
93
|
-
# self._options.set_argument('no-sandbox')
|
94
|
-
|
95
|
-
self._setup()
|
96
|
-
|
97
|
-
def _setup(self) -> None:
|
98
|
-
"""
|
99
|
-
Set up the browser instance and open the default tab.
|
100
|
-
"""
|
101
|
-
self._browser = Chromium(self._options)
|
102
|
-
self._page = self._browser.get_tab()
|
103
|
-
|
104
|
-
def login(self, max_retries: int = 3, manual_login: bool = False) -> bool:
|
105
|
-
"""
|
106
|
-
Attempt to log in
|
107
|
-
"""
|
108
|
-
raise NotImplementedError(
|
109
|
-
"Login is not supported by this browser type. "
|
110
|
-
"Override login() in your subclass to enable it."
|
111
|
-
)
|
112
|
-
|
113
|
-
@abc.abstractmethod
|
114
|
-
def get_book_info(self, book_id: str, wait_time: Optional[float] = None) -> str:
|
115
|
-
"""
|
116
|
-
Fetch the raw HTML (or JSON) of the book info page.
|
117
|
-
|
118
|
-
:param book_id: The book identifier.
|
119
|
-
:param wait_time: Base number of seconds to wait before returning content.
|
120
|
-
:return: The page content as a string.
|
121
|
-
"""
|
122
|
-
...
|
123
|
-
|
124
|
-
@abc.abstractmethod
|
125
|
-
def get_book_chapter(
|
126
|
-
self, book_id: str, chapter_id: str, wait_time: Optional[float] = None
|
127
|
-
) -> str:
|
128
|
-
"""
|
129
|
-
Fetch the raw HTML (or JSON) of a single chapter.
|
130
|
-
|
131
|
-
:param book_id: The book identifier.
|
132
|
-
:param chapter_id: The chapter identifier.
|
133
|
-
:param wait_time: Base number of seconds to wait before returning content.
|
134
|
-
:return: The chapter content as a string.
|
135
|
-
"""
|
136
|
-
...
|
137
|
-
|
138
|
-
def get_bookcase(self, wait_time: Optional[float] = None) -> str:
|
139
|
-
"""
|
140
|
-
Optional: Retrieve the HTML content of the authenticated user's bookcase page.
|
141
|
-
|
142
|
-
Subclasses that support login+bookcase retrieval should override this.
|
143
|
-
|
144
|
-
:param wait_time: Base number of seconds to wait before returning content.
|
145
|
-
:return: The HTML markup of the bookcase page.
|
146
|
-
:raises NotImplementedError: If bookcase fetching is not supported.
|
147
|
-
"""
|
148
|
-
raise NotImplementedError(
|
149
|
-
"Bookcase fetching is not supported by this browser type. "
|
150
|
-
"Override get_bookcase() in your subclass to enable it."
|
151
|
-
)
|
152
|
-
|
153
|
-
@property
|
154
|
-
def page(self) -> ChromiumPage:
|
155
|
-
"""
|
156
|
-
Return the current Chromium page object.
|
157
|
-
|
158
|
-
:return: ChromiumPage instance of the current tab.
|
159
|
-
"""
|
160
|
-
return self._page
|
161
|
-
|
162
|
-
@property
|
163
|
-
def browser(self) -> Chromium:
|
164
|
-
"""
|
165
|
-
Return the Chromium browser instance.
|
166
|
-
|
167
|
-
:return: Chromium instance used by this browser.
|
168
|
-
"""
|
169
|
-
return self._browser
|
170
|
-
|
171
|
-
def _clear_browser_refs(self) -> None:
|
172
|
-
"""
|
173
|
-
Clear internal browser/page references without quitting.
|
174
|
-
"""
|
175
|
-
self._browser = None
|
176
|
-
self._page = None
|
177
|
-
|
178
|
-
def shutdown(self) -> None:
|
179
|
-
"""
|
180
|
-
Shutdown the browser session and release resources.
|
181
|
-
|
182
|
-
This quits the Chromium instance and clears references to browser and page.
|
183
|
-
"""
|
184
|
-
if self._browser:
|
185
|
-
self._browser.quit()
|
186
|
-
self._clear_browser_refs()
|
187
|
-
|
188
|
-
def __getstate__(self) -> Dict[str, Any]:
|
189
|
-
"""
|
190
|
-
Prepare object state for serialization (e.g., pickling).
|
191
|
-
|
192
|
-
Removes browser-related fields that cannot be pickled.
|
193
|
-
|
194
|
-
:return: A dict representing the serializable object state.
|
195
|
-
"""
|
196
|
-
state = self.__dict__.copy()
|
197
|
-
state.pop("_browser", None)
|
198
|
-
state.pop("_page", None)
|
199
|
-
return state
|
200
|
-
|
201
|
-
def __setstate__(self, state: Dict[str, Any]) -> None:
|
202
|
-
"""
|
203
|
-
Restore object state after deserialization.
|
204
|
-
|
205
|
-
Automatically reinitializes the browser setup.
|
206
|
-
|
207
|
-
:param state: The saved state dictionary.
|
208
|
-
"""
|
209
|
-
self.__dict__.update(state)
|
210
|
-
self._setup()
|