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.
Files changed (129) hide show
  1. novel_downloader/__init__.py +1 -2
  2. novel_downloader/cli/__init__.py +0 -1
  3. novel_downloader/cli/clean.py +2 -10
  4. novel_downloader/cli/download.py +18 -22
  5. novel_downloader/cli/interactive.py +0 -1
  6. novel_downloader/cli/main.py +1 -3
  7. novel_downloader/cli/settings.py +8 -8
  8. novel_downloader/config/__init__.py +0 -1
  9. novel_downloader/config/adapter.py +48 -18
  10. novel_downloader/config/loader.py +116 -108
  11. novel_downloader/config/models.py +41 -32
  12. novel_downloader/config/site_rules.py +2 -4
  13. novel_downloader/core/__init__.py +0 -1
  14. novel_downloader/core/downloaders/__init__.py +4 -4
  15. novel_downloader/core/downloaders/base/__init__.py +14 -0
  16. novel_downloader/core/downloaders/{base_async_downloader.py → base/base_async.py} +49 -53
  17. novel_downloader/core/downloaders/{base_downloader.py → base/base_sync.py} +64 -43
  18. novel_downloader/core/downloaders/biquge/__init__.py +12 -0
  19. novel_downloader/core/downloaders/biquge/biquge_sync.py +25 -0
  20. novel_downloader/core/downloaders/common/__init__.py +14 -0
  21. novel_downloader/core/downloaders/{common_asynb_downloader.py → common/common_async.py} +42 -33
  22. novel_downloader/core/downloaders/{common_downloader.py → common/common_sync.py} +34 -23
  23. novel_downloader/core/downloaders/qidian/__init__.py +10 -0
  24. novel_downloader/core/downloaders/{qidian_downloader.py → qidian/qidian_sync.py} +80 -64
  25. novel_downloader/core/factory/__init__.py +4 -5
  26. novel_downloader/core/factory/{downloader_factory.py → downloader.py} +36 -35
  27. novel_downloader/core/factory/{parser_factory.py → parser.py} +12 -14
  28. novel_downloader/core/factory/{requester_factory.py → requester.py} +29 -16
  29. novel_downloader/core/factory/{saver_factory.py → saver.py} +4 -9
  30. novel_downloader/core/interfaces/__init__.py +8 -9
  31. novel_downloader/core/interfaces/{async_downloader_protocol.py → async_downloader.py} +4 -5
  32. novel_downloader/core/interfaces/{async_requester_protocol.py → async_requester.py} +26 -12
  33. novel_downloader/core/interfaces/{parser_protocol.py → parser.py} +11 -6
  34. novel_downloader/core/interfaces/{saver_protocol.py → saver.py} +2 -3
  35. novel_downloader/core/interfaces/{downloader_protocol.py → sync_downloader.py} +6 -7
  36. novel_downloader/core/interfaces/{requester_protocol.py → sync_requester.py} +34 -17
  37. novel_downloader/core/parsers/__init__.py +5 -4
  38. novel_downloader/core/parsers/{base_parser.py → base.py} +20 -11
  39. novel_downloader/core/parsers/biquge/__init__.py +10 -0
  40. novel_downloader/core/parsers/biquge/main_parser.py +126 -0
  41. novel_downloader/core/parsers/{common_parser → common}/__init__.py +2 -3
  42. novel_downloader/core/parsers/{common_parser → common}/helper.py +20 -18
  43. novel_downloader/core/parsers/{common_parser → common}/main_parser.py +15 -9
  44. novel_downloader/core/parsers/{qidian_parser → qidian}/__init__.py +2 -3
  45. novel_downloader/core/parsers/{qidian_parser → qidian}/browser/__init__.py +2 -3
  46. novel_downloader/core/parsers/{qidian_parser → qidian}/browser/chapter_encrypted.py +41 -49
  47. novel_downloader/core/parsers/{qidian_parser → qidian}/browser/chapter_normal.py +17 -21
  48. novel_downloader/core/parsers/{qidian_parser → qidian}/browser/chapter_router.py +10 -9
  49. novel_downloader/core/parsers/{qidian_parser → qidian}/browser/main_parser.py +16 -12
  50. novel_downloader/core/parsers/{qidian_parser → qidian}/session/__init__.py +2 -3
  51. novel_downloader/core/parsers/{qidian_parser → qidian}/session/chapter_encrypted.py +37 -45
  52. novel_downloader/core/parsers/{qidian_parser → qidian}/session/chapter_normal.py +19 -23
  53. novel_downloader/core/parsers/{qidian_parser → qidian}/session/chapter_router.py +10 -9
  54. novel_downloader/core/parsers/{qidian_parser → qidian}/session/main_parser.py +16 -12
  55. novel_downloader/core/parsers/{qidian_parser → qidian}/session/node_decryptor.py +7 -10
  56. novel_downloader/core/parsers/{qidian_parser → qidian}/shared/__init__.py +2 -3
  57. novel_downloader/core/parsers/qidian/shared/book_info_parser.py +150 -0
  58. novel_downloader/core/parsers/{qidian_parser → qidian}/shared/helpers.py +9 -10
  59. novel_downloader/core/requesters/__init__.py +9 -5
  60. novel_downloader/core/requesters/base/__init__.py +16 -0
  61. novel_downloader/core/requesters/{base_async_session.py → base/async_session.py} +180 -73
  62. novel_downloader/core/requesters/base/browser.py +340 -0
  63. novel_downloader/core/requesters/base/session.py +364 -0
  64. novel_downloader/core/requesters/biquge/__init__.py +12 -0
  65. novel_downloader/core/requesters/biquge/session.py +90 -0
  66. novel_downloader/core/requesters/{common_requester → common}/__init__.py +4 -5
  67. novel_downloader/core/requesters/common/async_session.py +96 -0
  68. novel_downloader/core/requesters/common/session.py +113 -0
  69. novel_downloader/core/requesters/qidian/__init__.py +21 -0
  70. novel_downloader/core/requesters/qidian/broswer.py +306 -0
  71. novel_downloader/core/requesters/qidian/session.py +287 -0
  72. novel_downloader/core/savers/__init__.py +5 -3
  73. novel_downloader/core/savers/{base_saver.py → base.py} +12 -13
  74. novel_downloader/core/savers/biquge.py +25 -0
  75. novel_downloader/core/savers/{common_saver → common}/__init__.py +2 -3
  76. novel_downloader/core/savers/{common_saver/common_epub.py → common/epub.py} +24 -52
  77. novel_downloader/core/savers/{common_saver → common}/main_saver.py +43 -9
  78. novel_downloader/core/savers/{common_saver/common_txt.py → common/txt.py} +16 -46
  79. novel_downloader/core/savers/epub_utils/__init__.py +0 -1
  80. novel_downloader/core/savers/epub_utils/css_builder.py +13 -7
  81. novel_downloader/core/savers/epub_utils/initializer.py +4 -5
  82. novel_downloader/core/savers/epub_utils/text_to_html.py +2 -3
  83. novel_downloader/core/savers/epub_utils/volume_intro.py +1 -3
  84. novel_downloader/core/savers/{qidian_saver.py → qidian.py} +12 -6
  85. novel_downloader/locales/en.json +12 -4
  86. novel_downloader/locales/zh.json +9 -1
  87. novel_downloader/resources/config/settings.toml +88 -0
  88. novel_downloader/utils/cache.py +2 -2
  89. novel_downloader/utils/chapter_storage.py +340 -0
  90. novel_downloader/utils/constants.py +8 -5
  91. novel_downloader/utils/crypto_utils.py +3 -3
  92. novel_downloader/utils/file_utils/__init__.py +0 -1
  93. novel_downloader/utils/file_utils/io.py +12 -17
  94. novel_downloader/utils/file_utils/normalize.py +1 -3
  95. novel_downloader/utils/file_utils/sanitize.py +2 -9
  96. novel_downloader/utils/fontocr/__init__.py +0 -1
  97. novel_downloader/utils/fontocr/ocr_v1.py +19 -22
  98. novel_downloader/utils/fontocr/ocr_v2.py +147 -60
  99. novel_downloader/utils/hash_store.py +19 -20
  100. novel_downloader/utils/hash_utils.py +0 -1
  101. novel_downloader/utils/i18n.py +3 -4
  102. novel_downloader/utils/logger.py +5 -6
  103. novel_downloader/utils/model_loader.py +5 -8
  104. novel_downloader/utils/network.py +9 -10
  105. novel_downloader/utils/state.py +6 -7
  106. novel_downloader/utils/text_utils/__init__.py +0 -1
  107. novel_downloader/utils/text_utils/chapter_formatting.py +2 -7
  108. novel_downloader/utils/text_utils/diff_display.py +0 -1
  109. novel_downloader/utils/text_utils/font_mapping.py +1 -4
  110. novel_downloader/utils/text_utils/text_cleaning.py +0 -1
  111. novel_downloader/utils/time_utils/__init__.py +0 -1
  112. novel_downloader/utils/time_utils/datetime_utils.py +9 -11
  113. novel_downloader/utils/time_utils/sleep_utils.py +27 -13
  114. {novel_downloader-1.2.1.dist-info → novel_downloader-1.3.0.dist-info}/METADATA +14 -17
  115. novel_downloader-1.3.0.dist-info/RECORD +127 -0
  116. {novel_downloader-1.2.1.dist-info → novel_downloader-1.3.0.dist-info}/WHEEL +1 -1
  117. novel_downloader/core/parsers/qidian_parser/shared/book_info_parser.py +0 -95
  118. novel_downloader/core/requesters/base_browser.py +0 -210
  119. novel_downloader/core/requesters/base_session.py +0 -243
  120. novel_downloader/core/requesters/common_requester/common_async_session.py +0 -98
  121. novel_downloader/core/requesters/common_requester/common_session.py +0 -126
  122. novel_downloader/core/requesters/qidian_requester/__init__.py +0 -22
  123. novel_downloader/core/requesters/qidian_requester/qidian_broswer.py +0 -377
  124. novel_downloader/core/requesters/qidian_requester/qidian_session.py +0 -202
  125. novel_downloader/resources/config/settings.yaml +0 -76
  126. novel_downloader-1.2.1.dist-info/RECORD +0 -115
  127. {novel_downloader-1.2.1.dist-info → novel_downloader-1.3.0.dist-info}/entry_points.txt +0 -0
  128. {novel_downloader-1.2.1.dist-info → novel_downloader-1.3.0.dist-info}/licenses/LICENSE +0 -0
  129. {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.2.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
- Requires-Python: >=3.8
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: pyyaml
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
- # 如需启用异步抓取模式 (mode=async), 请使用:
98
- # pip install novel-downloader[async]
99
-
100
- # 初始化默认配置 (生成 settings.yaml)
94
+ # 初始化默认配置 (生成 settings.toml)
101
95
  novel-cli settings init
102
96
 
103
- # 编辑 ./settings.yaml 完成 site/book_ids 等
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
- - 自动整合所有章节并输出为完整的 TXT 文件
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.yaml 配置说明](https://github.com/BowenZ217/novel-downloader/blob/main/docs/4-settings-schema.md)
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,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.4.0)
2
+ Generator: setuptools (80.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -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()