novel-downloader 1.3.2__py3-none-any.whl → 1.4.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 (213) hide show
  1. novel_downloader/__init__.py +1 -1
  2. novel_downloader/cli/clean.py +97 -78
  3. novel_downloader/cli/config.py +177 -0
  4. novel_downloader/cli/download.py +132 -87
  5. novel_downloader/cli/export.py +77 -0
  6. novel_downloader/cli/main.py +21 -28
  7. novel_downloader/config/__init__.py +1 -25
  8. novel_downloader/config/adapter.py +32 -31
  9. novel_downloader/config/loader.py +3 -3
  10. novel_downloader/config/site_rules.py +1 -2
  11. novel_downloader/core/__init__.py +3 -6
  12. novel_downloader/core/downloaders/__init__.py +10 -13
  13. novel_downloader/core/downloaders/base.py +233 -0
  14. novel_downloader/core/downloaders/biquge.py +27 -0
  15. novel_downloader/core/downloaders/common.py +414 -0
  16. novel_downloader/core/downloaders/esjzone.py +27 -0
  17. novel_downloader/core/downloaders/linovelib.py +27 -0
  18. novel_downloader/core/downloaders/qianbi.py +27 -0
  19. novel_downloader/core/downloaders/qidian.py +352 -0
  20. novel_downloader/core/downloaders/sfacg.py +27 -0
  21. novel_downloader/core/downloaders/yamibo.py +27 -0
  22. novel_downloader/core/exporters/__init__.py +37 -0
  23. novel_downloader/core/{savers → exporters}/base.py +73 -44
  24. novel_downloader/core/exporters/biquge.py +25 -0
  25. novel_downloader/core/exporters/common/__init__.py +12 -0
  26. novel_downloader/core/{savers → exporters}/common/epub.py +40 -52
  27. novel_downloader/core/{savers/common/main_saver.py → exporters/common/main_exporter.py} +36 -39
  28. novel_downloader/core/{savers → exporters}/common/txt.py +20 -24
  29. novel_downloader/core/exporters/epub_utils/__init__.py +40 -0
  30. novel_downloader/core/{savers → exporters}/epub_utils/css_builder.py +2 -1
  31. novel_downloader/core/exporters/epub_utils/image_loader.py +131 -0
  32. novel_downloader/core/{savers → exporters}/epub_utils/initializer.py +6 -3
  33. novel_downloader/core/{savers → exporters}/epub_utils/text_to_html.py +49 -2
  34. novel_downloader/core/{savers → exporters}/epub_utils/volume_intro.py +2 -1
  35. novel_downloader/core/exporters/esjzone.py +25 -0
  36. novel_downloader/core/exporters/linovelib/__init__.py +10 -0
  37. novel_downloader/core/exporters/linovelib/epub.py +449 -0
  38. novel_downloader/core/exporters/linovelib/main_exporter.py +127 -0
  39. novel_downloader/core/exporters/linovelib/txt.py +129 -0
  40. novel_downloader/core/exporters/qianbi.py +25 -0
  41. novel_downloader/core/{savers → exporters}/qidian.py +8 -8
  42. novel_downloader/core/exporters/sfacg.py +25 -0
  43. novel_downloader/core/exporters/yamibo.py +25 -0
  44. novel_downloader/core/factory/__init__.py +5 -17
  45. novel_downloader/core/factory/downloader.py +24 -126
  46. novel_downloader/core/factory/exporter.py +58 -0
  47. novel_downloader/core/factory/fetcher.py +96 -0
  48. novel_downloader/core/factory/parser.py +17 -12
  49. novel_downloader/core/{requesters → fetchers}/__init__.py +22 -15
  50. novel_downloader/core/{requesters → fetchers}/base/__init__.py +2 -4
  51. novel_downloader/core/fetchers/base/browser.py +383 -0
  52. novel_downloader/core/fetchers/base/rate_limiter.py +86 -0
  53. novel_downloader/core/fetchers/base/session.py +419 -0
  54. novel_downloader/core/fetchers/biquge/__init__.py +14 -0
  55. novel_downloader/core/{requesters/biquge/async_session.py → fetchers/biquge/browser.py} +18 -6
  56. novel_downloader/core/{requesters → fetchers}/biquge/session.py +23 -30
  57. novel_downloader/core/fetchers/common/__init__.py +14 -0
  58. novel_downloader/core/fetchers/common/browser.py +79 -0
  59. novel_downloader/core/{requesters/common/async_session.py → fetchers/common/session.py} +8 -25
  60. novel_downloader/core/fetchers/esjzone/__init__.py +14 -0
  61. novel_downloader/core/fetchers/esjzone/browser.py +202 -0
  62. novel_downloader/core/{requesters/esjzone/async_session.py → fetchers/esjzone/session.py} +62 -42
  63. novel_downloader/core/fetchers/linovelib/__init__.py +14 -0
  64. novel_downloader/core/fetchers/linovelib/browser.py +178 -0
  65. novel_downloader/core/fetchers/linovelib/session.py +178 -0
  66. novel_downloader/core/fetchers/qianbi/__init__.py +14 -0
  67. novel_downloader/core/{requesters/qianbi/session.py → fetchers/qianbi/browser.py} +30 -48
  68. novel_downloader/core/{requesters/qianbi/async_session.py → fetchers/qianbi/session.py} +18 -6
  69. novel_downloader/core/fetchers/qidian/__init__.py +14 -0
  70. novel_downloader/core/fetchers/qidian/browser.py +266 -0
  71. novel_downloader/core/fetchers/qidian/session.py +326 -0
  72. novel_downloader/core/fetchers/sfacg/__init__.py +14 -0
  73. novel_downloader/core/fetchers/sfacg/browser.py +189 -0
  74. novel_downloader/core/{requesters/sfacg/async_session.py → fetchers/sfacg/session.py} +43 -73
  75. novel_downloader/core/fetchers/yamibo/__init__.py +14 -0
  76. novel_downloader/core/fetchers/yamibo/browser.py +229 -0
  77. novel_downloader/core/{requesters/yamibo/async_session.py → fetchers/yamibo/session.py} +62 -44
  78. novel_downloader/core/interfaces/__init__.py +8 -12
  79. novel_downloader/core/interfaces/downloader.py +54 -0
  80. novel_downloader/core/interfaces/{saver.py → exporter.py} +12 -12
  81. novel_downloader/core/interfaces/fetcher.py +162 -0
  82. novel_downloader/core/interfaces/parser.py +6 -7
  83. novel_downloader/core/parsers/__init__.py +5 -6
  84. novel_downloader/core/parsers/base.py +9 -13
  85. novel_downloader/core/parsers/biquge/main_parser.py +12 -13
  86. novel_downloader/core/parsers/common/helper.py +3 -3
  87. novel_downloader/core/parsers/common/main_parser.py +39 -34
  88. novel_downloader/core/parsers/esjzone/main_parser.py +24 -17
  89. novel_downloader/core/parsers/linovelib/__init__.py +10 -0
  90. novel_downloader/core/parsers/linovelib/main_parser.py +210 -0
  91. novel_downloader/core/parsers/qianbi/main_parser.py +21 -15
  92. novel_downloader/core/parsers/qidian/__init__.py +2 -11
  93. novel_downloader/core/parsers/qidian/book_info_parser.py +113 -0
  94. novel_downloader/core/parsers/qidian/{browser/chapter_encrypted.py → chapter_encrypted.py} +162 -135
  95. novel_downloader/core/parsers/qidian/chapter_normal.py +150 -0
  96. novel_downloader/core/parsers/qidian/{session/chapter_router.py → chapter_router.py} +15 -15
  97. novel_downloader/core/parsers/qidian/{browser/main_parser.py → main_parser.py} +49 -40
  98. novel_downloader/core/parsers/qidian/utils/__init__.py +27 -0
  99. novel_downloader/core/parsers/qidian/utils/decryptor_fetcher.py +145 -0
  100. novel_downloader/core/parsers/qidian/{shared → utils}/helpers.py +41 -68
  101. novel_downloader/core/parsers/qidian/{session → utils}/node_decryptor.py +64 -50
  102. novel_downloader/core/parsers/sfacg/main_parser.py +12 -12
  103. novel_downloader/core/parsers/yamibo/main_parser.py +10 -10
  104. novel_downloader/locales/en.json +18 -2
  105. novel_downloader/locales/zh.json +18 -2
  106. novel_downloader/models/__init__.py +64 -0
  107. novel_downloader/models/browser.py +21 -0
  108. novel_downloader/models/chapter.py +25 -0
  109. novel_downloader/models/config.py +100 -0
  110. novel_downloader/models/login.py +20 -0
  111. novel_downloader/models/site_rules.py +99 -0
  112. novel_downloader/models/tasks.py +33 -0
  113. novel_downloader/models/types.py +15 -0
  114. novel_downloader/resources/config/settings.toml +31 -25
  115. novel_downloader/resources/json/linovelib_font_map.json +3573 -0
  116. novel_downloader/tui/__init__.py +7 -0
  117. novel_downloader/tui/app.py +32 -0
  118. novel_downloader/tui/main.py +17 -0
  119. novel_downloader/tui/screens/__init__.py +14 -0
  120. novel_downloader/tui/screens/home.py +191 -0
  121. novel_downloader/tui/screens/login.py +74 -0
  122. novel_downloader/tui/styles/home_layout.tcss +79 -0
  123. novel_downloader/tui/widgets/richlog_handler.py +24 -0
  124. novel_downloader/utils/__init__.py +6 -0
  125. novel_downloader/utils/chapter_storage.py +25 -38
  126. novel_downloader/utils/constants.py +15 -5
  127. novel_downloader/utils/cookies.py +66 -0
  128. novel_downloader/utils/crypto_utils.py +1 -74
  129. novel_downloader/utils/file_utils/io.py +1 -1
  130. novel_downloader/utils/fontocr/ocr_v1.py +2 -1
  131. novel_downloader/utils/fontocr/ocr_v2.py +2 -2
  132. novel_downloader/utils/hash_store.py +10 -18
  133. novel_downloader/utils/hash_utils.py +3 -2
  134. novel_downloader/utils/logger.py +2 -3
  135. novel_downloader/utils/network.py +53 -39
  136. novel_downloader/utils/text_utils/chapter_formatting.py +6 -1
  137. novel_downloader/utils/text_utils/font_mapping.py +1 -1
  138. novel_downloader/utils/text_utils/text_cleaning.py +1 -1
  139. novel_downloader/utils/time_utils/datetime_utils.py +3 -3
  140. novel_downloader/utils/time_utils/sleep_utils.py +3 -3
  141. {novel_downloader-1.3.2.dist-info → novel_downloader-1.4.0.dist-info}/METADATA +72 -38
  142. novel_downloader-1.4.0.dist-info/RECORD +170 -0
  143. {novel_downloader-1.3.2.dist-info → novel_downloader-1.4.0.dist-info}/WHEEL +1 -1
  144. {novel_downloader-1.3.2.dist-info → novel_downloader-1.4.0.dist-info}/entry_points.txt +1 -0
  145. novel_downloader/cli/interactive.py +0 -66
  146. novel_downloader/cli/settings.py +0 -177
  147. novel_downloader/config/models.py +0 -187
  148. novel_downloader/core/downloaders/base/__init__.py +0 -14
  149. novel_downloader/core/downloaders/base/base_async.py +0 -153
  150. novel_downloader/core/downloaders/base/base_sync.py +0 -208
  151. novel_downloader/core/downloaders/biquge/__init__.py +0 -14
  152. novel_downloader/core/downloaders/biquge/biquge_async.py +0 -27
  153. novel_downloader/core/downloaders/biquge/biquge_sync.py +0 -27
  154. novel_downloader/core/downloaders/common/__init__.py +0 -14
  155. novel_downloader/core/downloaders/common/common_async.py +0 -218
  156. novel_downloader/core/downloaders/common/common_sync.py +0 -210
  157. novel_downloader/core/downloaders/esjzone/__init__.py +0 -14
  158. novel_downloader/core/downloaders/esjzone/esjzone_async.py +0 -27
  159. novel_downloader/core/downloaders/esjzone/esjzone_sync.py +0 -27
  160. novel_downloader/core/downloaders/qianbi/__init__.py +0 -14
  161. novel_downloader/core/downloaders/qianbi/qianbi_async.py +0 -27
  162. novel_downloader/core/downloaders/qianbi/qianbi_sync.py +0 -27
  163. novel_downloader/core/downloaders/qidian/__init__.py +0 -10
  164. novel_downloader/core/downloaders/qidian/qidian_sync.py +0 -227
  165. novel_downloader/core/downloaders/sfacg/__init__.py +0 -14
  166. novel_downloader/core/downloaders/sfacg/sfacg_async.py +0 -27
  167. novel_downloader/core/downloaders/sfacg/sfacg_sync.py +0 -27
  168. novel_downloader/core/downloaders/yamibo/__init__.py +0 -14
  169. novel_downloader/core/downloaders/yamibo/yamibo_async.py +0 -27
  170. novel_downloader/core/downloaders/yamibo/yamibo_sync.py +0 -27
  171. novel_downloader/core/factory/requester.py +0 -144
  172. novel_downloader/core/factory/saver.py +0 -56
  173. novel_downloader/core/interfaces/async_downloader.py +0 -36
  174. novel_downloader/core/interfaces/async_requester.py +0 -84
  175. novel_downloader/core/interfaces/sync_downloader.py +0 -36
  176. novel_downloader/core/interfaces/sync_requester.py +0 -82
  177. novel_downloader/core/parsers/qidian/browser/__init__.py +0 -12
  178. novel_downloader/core/parsers/qidian/browser/chapter_normal.py +0 -93
  179. novel_downloader/core/parsers/qidian/browser/chapter_router.py +0 -71
  180. novel_downloader/core/parsers/qidian/session/__init__.py +0 -12
  181. novel_downloader/core/parsers/qidian/session/chapter_encrypted.py +0 -443
  182. novel_downloader/core/parsers/qidian/session/chapter_normal.py +0 -115
  183. novel_downloader/core/parsers/qidian/session/main_parser.py +0 -128
  184. novel_downloader/core/parsers/qidian/shared/__init__.py +0 -37
  185. novel_downloader/core/parsers/qidian/shared/book_info_parser.py +0 -150
  186. novel_downloader/core/requesters/base/async_session.py +0 -410
  187. novel_downloader/core/requesters/base/browser.py +0 -337
  188. novel_downloader/core/requesters/base/session.py +0 -378
  189. novel_downloader/core/requesters/biquge/__init__.py +0 -14
  190. novel_downloader/core/requesters/common/__init__.py +0 -17
  191. novel_downloader/core/requesters/common/session.py +0 -113
  192. novel_downloader/core/requesters/esjzone/__init__.py +0 -13
  193. novel_downloader/core/requesters/esjzone/session.py +0 -235
  194. novel_downloader/core/requesters/qianbi/__init__.py +0 -13
  195. novel_downloader/core/requesters/qidian/__init__.py +0 -21
  196. novel_downloader/core/requesters/qidian/broswer.py +0 -307
  197. novel_downloader/core/requesters/qidian/session.py +0 -290
  198. novel_downloader/core/requesters/sfacg/__init__.py +0 -13
  199. novel_downloader/core/requesters/sfacg/session.py +0 -242
  200. novel_downloader/core/requesters/yamibo/__init__.py +0 -13
  201. novel_downloader/core/requesters/yamibo/session.py +0 -237
  202. novel_downloader/core/savers/__init__.py +0 -34
  203. novel_downloader/core/savers/biquge.py +0 -25
  204. novel_downloader/core/savers/common/__init__.py +0 -12
  205. novel_downloader/core/savers/epub_utils/__init__.py +0 -26
  206. novel_downloader/core/savers/esjzone.py +0 -25
  207. novel_downloader/core/savers/qianbi.py +0 -25
  208. novel_downloader/core/savers/sfacg.py +0 -25
  209. novel_downloader/core/savers/yamibo.py +0 -25
  210. novel_downloader/resources/config/rules.toml +0 -196
  211. novel_downloader-1.3.2.dist-info/RECORD +0 -165
  212. {novel_downloader-1.3.2.dist-info → novel_downloader-1.4.0.dist-info}/licenses/LICENSE +0 -0
  213. {novel_downloader-1.3.2.dist-info → novel_downloader-1.4.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.2
3
+ Version: 1.4.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,19 +34,18 @@ 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.11
37
38
  Classifier: Programming Language :: Python :: 3.12
38
39
  Classifier: Programming Language :: Python :: 3.13
39
- Requires-Python: >=3.12
40
+ Requires-Python: >=3.11
40
41
  Description-Content-Type: text/markdown
41
42
  License-File: LICENSE
43
+ Requires-Dist: textual
42
44
  Requires-Dist: requests
43
45
  Requires-Dist: aiohttp
44
- Requires-Dist: beautifulsoup4
45
- Requires-Dist: DrissionPage
46
- Requires-Dist: opencv-python
46
+ Requires-Dist: playwright
47
47
  Requires-Dist: lxml
48
48
  Requires-Dist: platformdirs
49
- Requires-Dist: click
50
49
  Requires-Dist: ebooklib
51
50
  Provides-Extra: dev
52
51
  Requires-Dist: black; extra == "dev"
@@ -55,6 +54,8 @@ Requires-Dist: ruff; extra == "dev"
55
54
  Requires-Dist: pytest; extra == "dev"
56
55
  Requires-Dist: pytest-cov; extra == "dev"
57
56
  Requires-Dist: pytest-mock; extra == "dev"
57
+ Requires-Dist: types-requests; extra == "dev"
58
+ Requires-Dist: types-PyYAML; extra == "dev"
58
59
  Requires-Dist: pre-commit; extra == "dev"
59
60
  Requires-Dist: commitizen; extra == "dev"
60
61
  Provides-Extra: font-recovery
@@ -63,63 +64,96 @@ Requires-Dist: numpy; extra == "font-recovery"
63
64
  Requires-Dist: tinycss2; extra == "font-recovery"
64
65
  Requires-Dist: fonttools; extra == "font-recovery"
65
66
  Requires-Dist: brotli; extra == "font-recovery"
67
+ Requires-Dist: opencv-python; extra == "font-recovery"
66
68
  Requires-Dist: pillow; extra == "font-recovery"
67
69
  Requires-Dist: huggingface_hub; extra == "font-recovery"
68
70
  Dynamic: license-file
69
71
 
70
72
  # novel-downloader
71
73
 
72
- 一个基于 [DrissionPage](https://www.drissionpage.cn) 和 [requests](https://github.com/psf/requests) 的小说下载器。
74
+ 一个基于 [playwright](https://playwright.dev/) 和 [aiohttp](https://github.com/aio-libs/aiohttp) 的小说下载工具/库。
73
75
 
74
- ---
75
-
76
- ## 项目简介
77
-
78
- **novel-downloader** 是一个通用的小说下载库 / CLI 工具,
79
- - 大多数支持的站点仅依赖 [`requests`](https://github.com/psf/requests) 进行 HTTP 抓取
80
- - 对于起点中文网 (Qidian), 可在配置中选择:
81
- - `mode: session` : 纯 Requests 模式
82
- - `mode: browser` : 基于 DrissionPage 驱动 Chrome 的浏览器模式 (可处理更复杂的 JS/加密)。
83
- - 若配置 `login_required: true`, 程序会在运行时自动检查登录状态, 支持自动重用历史 Cookie, 仅在首次登录或 Cookie 失效时需要人工介入:
84
- - 若使用 `browser` 模式, 请在程序打开的浏览器窗口登录, 登录后回车继续
85
- - 若使用 `session` 模式, 请根据程序提示粘贴浏览器中登录成功后的 Cookie ([复制 Cookies](https://github.com/BowenZ217/novel-downloader/blob/main/docs/copy-cookies.md))
76
+ > 本项目开发环境为 Python 3.12, 需确保运行环境为 Python 3.11 及以上版本
86
77
 
87
78
  ## 功能特性
88
79
 
89
- - 爬取起点中文网的小说章节内容 (支持免费与已订阅章节)
90
- - 爬取部分小说网站
91
- - 断点续爬
92
- - 自动整合所有章节并导出为
80
+ - 支持断点续爬, 自动续传未完成任务
81
+ - 自动整合所有章节并导出为:
93
82
  - TXT
94
- - EPUB
83
+ - EPUB (可选包含章节插图)
95
84
  - 支持活动广告过滤:
96
85
  - [x] 章节标题
97
86
  - [ ] 章节正文
98
- - [ ] 作者说
99
87
 
100
88
  ---
101
89
 
102
90
  ## 快速开始
103
91
 
92
+ ### 安装
93
+
94
+ 使用 `pip` 安装:
95
+
104
96
  ```bash
105
- # 克隆 + 安装
106
97
  pip install novel-downloader
98
+ ```
99
+
100
+ 如需使用浏览器模式 (即 `mode: browser`), 请确保已安装 Playwright 依赖:
107
101
 
108
- # 如需支持字体解密功能 (decode_font), 请使用:
109
- # pip install novel-downloader[font-recovery]
102
+ ```bash
103
+ playwright install
104
+ ```
110
105
 
106
+ 如需启用字体解密功能 (`decode_font`, 用于处理起点中文网对近一个月更新章节所采用的字体混淆技术), 请使用扩展安装方式:
107
+
108
+ ```bash
109
+ pip install novel-downloader[font-recovery]
110
+ ```
111
+
112
+ ---
113
+
114
+ ### CLI 模式
115
+
116
+ ```bash
111
117
  # 初始化默认配置 (生成 settings.toml)
112
- novel-cli settings init
118
+ novel-cli config init
113
119
 
114
120
  # 编辑 ./settings.toml 完成 site/book_ids 等
115
- # 可查看 docs/4-settings-schema.md
121
+ # 可查看 docs/3-settings-schema.md
116
122
 
117
- # 运行下载
123
+ # 执行下载任务
118
124
  novel-cli download 123456
119
125
  ```
120
126
 
121
- - 详细可见: [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-supported-sites.md)
122
- - 更多使用方法, 查看 [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-usage-examples.md)
127
+ - 详细可见: [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/4-supported-sites.md)
128
+ - 更多使用方法, 查看 [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-cli-usage-examples.md)
129
+
130
+ ---
131
+
132
+ ### TUI 模式 (终端用户界面)
133
+
134
+ **注意**: TUI 模式仍在开发中, 目前尚未实现登录和修改设置等功能。建议优先使用稳定的 CLI 模式。
135
+
136
+ ```bash
137
+ # 初始化默认配置 (生成 settings.toml)
138
+ novel-cli config init
139
+
140
+ # 编辑 ./settings.toml 修改网络配置
141
+ # 可查看 docs/3-settings-schema.md
142
+
143
+ # 启动 TUI 界面
144
+ novel-tui
145
+ ```
146
+
147
+ - 详细可见: [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/4-supported-sites.md)
148
+ - 更多使用方法, 查看 [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-tui-usage-examples.md)
149
+
150
+ ---
151
+
152
+ ### GUI 模式 (图形界面)
153
+
154
+ 尚未实现
155
+
156
+ ---
123
157
 
124
158
  ## 从 GitHub 安装 (开发版)
125
159
 
@@ -139,11 +173,11 @@ pip install .
139
173
 
140
174
  - [项目简介](#项目简介)
141
175
  - [安装](https://github.com/BowenZ217/novel-downloader/blob/main/docs/1-installation.md)
142
- - [环境准备](https://github.com/BowenZ217/novel-downloader/blob/main/docs/2-environment-setup.md)
143
- - [配置](https://github.com/BowenZ217/novel-downloader/blob/main/docs/3-configuration.md)
144
- - [settings.toml 配置说明](https://github.com/BowenZ217/novel-downloader/blob/main/docs/4-settings-schema.md)
145
- - [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-usage-examples.md)
146
- - [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-supported-sites.md)
176
+ - [配置](https://github.com/BowenZ217/novel-downloader/blob/main/docs/2-configuration.md)
177
+ - [settings.toml 配置说明](https://github.com/BowenZ217/novel-downloader/blob/main/docs/3-settings-schema.md)
178
+ - [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/4-supported-sites.md)
179
+ - [TUI 使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-tui-usage-examples.md)
180
+ - [CLI 使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-cli-usage-examples.md)
147
181
  - [复制 Cookies](https://github.com/BowenZ217/novel-downloader/blob/main/docs/copy-cookies.md)
148
182
  - [文件保存](https://github.com/BowenZ217/novel-downloader/blob/main/docs/file-saving.md)
149
183
  - [TODO](https://github.com/BowenZ217/novel-downloader/blob/main/docs/todo.md)
@@ -0,0 +1,170 @@
1
+ novel_downloader/__init__.py,sha256=TLa2eWlJhpY9lBjsolzhKpaZ7PWwhQj34Tj7RUSLlOY,218
2
+ novel_downloader/cli/__init__.py,sha256=-2HAut_U1e67MZGdvbpEJ1n5J-bRchzto6L4c-nWeXY,174
3
+ novel_downloader/cli/clean.py,sha256=hOk8SJQwBCw2oOObTdEI79wpnmZ25uB1s9LQK1-4LNU,4487
4
+ novel_downloader/cli/config.py,sha256=C6QLfegZLp4legmu8KenqyYKNdrk47bH0z86ujLP0pY,6509
5
+ novel_downloader/cli/download.py,sha256=HKCxufqnj4vDzCdM7soH_Y_eiVNRGpV17Mz08ADYwqQ,5279
6
+ novel_downloader/cli/export.py,sha256=x9uvyLuvkuaDZGoH212aHZ7XyPT9b2S78AmTN6rkAu4,2283
7
+ novel_downloader/cli/main.py,sha256=9J8KMuYwL01X6chIaXpQNeS5d3pHnwB9vA9XjKd8RrM,919
8
+ novel_downloader/config/__init__.py,sha256=2mnf33MQOUnLGCnL1NtNV_rHBejNxBNIbobIGN0tw4E,666
9
+ novel_downloader/config/adapter.py,sha256=UYtDlpKFcKmvBBN9ZOI3W7u0a72Fhrpk-DkZmKoKpqk,7281
10
+ novel_downloader/config/loader.py,sha256=jo_1rr3UKZRAFFYgO-oHpYLRhF431chmfx4fLGh0MKw,5743
11
+ novel_downloader/config/site_rules.py,sha256=CJksBSvVAC4sR6cEruf4pM1Jv0zTJb1lcHq0Yn6LPFM,2979
12
+ novel_downloader/core/__init__.py,sha256=zzrXjQfBUhfLmBD_95oHTjtTsR81NSRtHcxpYBxQlZ4,654
13
+ novel_downloader/core/downloaders/__init__.py,sha256=AK5zeetVXOn_irgHp-NORPYW65UJM4CsF8ysxf2vKD4,1064
14
+ novel_downloader/core/downloaders/base.py,sha256=H3Y9NnSv4lQA6ilKCd7HqAX24R79wXBMkLG5StK8znc,6420
15
+ novel_downloader/core/downloaders/biquge.py,sha256=PTm8eeaHVyw_nvtnuRpZ2hT7hLs2LEPV1Drd2O6idNM,651
16
+ novel_downloader/core/downloaders/common.py,sha256=1YNo-e5tuK5bGvuzX3eVmEJznD7f5JMo7a_abIdmEkM,15504
17
+ novel_downloader/core/downloaders/esjzone.py,sha256=GBFJ3fIPov483QxkOTaKBU2wd2f8T4GdvzSbRaU9hDI,655
18
+ novel_downloader/core/downloaders/linovelib.py,sha256=Cd43_70SKbKM8_C8c4qev3pLUgyziW9413qrA8EcStY,663
19
+ novel_downloader/core/downloaders/qianbi.py,sha256=ARuk1xABOGCjlD_ct5hOKMgWloU187rUJBbICJ2yvkY,651
20
+ novel_downloader/core/downloaders/qidian.py,sha256=YrX5HdJDopEjc1kvEJRjMc1S4iR049IGvzUNtgE-LPE,12590
21
+ novel_downloader/core/downloaders/sfacg.py,sha256=ShlkIIR6OW_x2FhNDi79S_3AL2CWId2upQmRkApee_E,647
22
+ novel_downloader/core/downloaders/yamibo.py,sha256=9L0m5Zq0o1y44tCQ4ZRHHqdO3YresJkRAVvCtsQwouk,651
23
+ novel_downloader/core/exporters/__init__.py,sha256=ATkkdh6RUIaM19mG1XjFiaMnGRgFFGT3ixsqVkU13Q0,867
24
+ novel_downloader/core/exporters/base.py,sha256=duIdLnj9kKNZ9r2aJV0Rzl-rnQCP8olk12uzduT9PtE,6146
25
+ novel_downloader/core/exporters/biquge.py,sha256=SJCChYtDLJKrOpwDCT8IeR0v1LoiUQuTTejfGmRnxX8,462
26
+ novel_downloader/core/exporters/esjzone.py,sha256=UP9gtTOv6komWjgvwXkttqDYJJPjjbJdiG3yz9RxHSQ,467
27
+ novel_downloader/core/exporters/qianbi.py,sha256=x841MNIxpS5eY8u91ODC0cpIhpT-ENnFqs_Yx6HwtdM,462
28
+ novel_downloader/core/exporters/qidian.py,sha256=ecaZcgqXyLvJtT2J5QXw4qS9gY3d72mgUnV-0frJvhQ,724
29
+ novel_downloader/core/exporters/sfacg.py,sha256=j4_H-ixvDxd_OJfIvD4Lbs24LaiFjaYqhvhZcks-ItI,457
30
+ novel_downloader/core/exporters/yamibo.py,sha256=QaY13llqpyxIgmMnMVFtR5yFMIpyMHHdCmVDW4Dq-bo,462
31
+ novel_downloader/core/exporters/common/__init__.py,sha256=qIOZ_TgnnQZ6p45YQsxpTcB69e0Jq9XMG3IY0NBVsTM,274
32
+ novel_downloader/core/exporters/common/epub.py,sha256=1BhaZCZLehX-v8Z7spatyizhHoMfCF08ZfZGpPfB5Kw,6331
33
+ novel_downloader/core/exporters/common/main_exporter.py,sha256=-MRZEpAdsfdRt3VxIv4krMaw2UJnrvCnyj5Jde93zuA,3926
34
+ novel_downloader/core/exporters/common/txt.py,sha256=b5cNvH0hrjaBO89Uddn1IUY1BFhkHJdTd7WjSWk4ndk,4719
35
+ novel_downloader/core/exporters/epub_utils/__init__.py,sha256=cPtuQDtuJZPgOHRf_T5sgkQE13jZnOyPoK9hHJeMEEo,993
36
+ novel_downloader/core/exporters/epub_utils/css_builder.py,sha256=qvVgHnyoILgqSUoVsHv-cHzIdtfsqxA8WHt9_89puic,2145
37
+ novel_downloader/core/exporters/epub_utils/image_loader.py,sha256=hbxoPvXkBIZnoZSIlHwIUx4zWR8QVppWiH1KotMrBXg,3953
38
+ novel_downloader/core/exporters/epub_utils/initializer.py,sha256=TGueFff1ydRLdJYBEJnqIkoVCV57ZiR3dGu_c5Hrp8g,3364
39
+ novel_downloader/core/exporters/epub_utils/text_to_html.py,sha256=VSYbm33PrwxFmcT-2f_U2j3kuHzdcSMR7PRqOWvZICU,5617
40
+ novel_downloader/core/exporters/epub_utils/volume_intro.py,sha256=cwHMVwJeK8TzXnOw4MYWXvbZGt4iWFjdesDm4n_V-Tk,1830
41
+ novel_downloader/core/exporters/linovelib/__init__.py,sha256=zD7A7OhluipwICssnp27c_oenYozevZH9g0Qj5WOMWY,195
42
+ novel_downloader/core/exporters/linovelib/epub.py,sha256=wxfNxOX6kxSaove-YlDlNw1cmsBJFwysk6bS6Gsk8Cw,14503
43
+ novel_downloader/core/exporters/linovelib/main_exporter.py,sha256=cHkAp_jdF6uji5Avu1l8z6mR0nSrWYgX6ETDEezkjoE,3700
44
+ novel_downloader/core/exporters/linovelib/txt.py,sha256=ALlZUl5nNtg4OmYlurMC0acjmTOBV7G8c13DPrxbG4w,4407
45
+ novel_downloader/core/factory/__init__.py,sha256=_IY3N35onhWD_nw_TyxKOxa6e7Uak9Cv0bp4pK9yb0M,464
46
+ novel_downloader/core/factory/downloader.py,sha256=hNCp3IlZQzeTSLBMuO_Y_EzrAn9_-8SLBHcUwMw8ijM,2060
47
+ novel_downloader/core/factory/exporter.py,sha256=CjDJGnWBDk-S1zYntIDAEo1hLM2q55tlJOjTXKn0hAI,1533
48
+ novel_downloader/core/factory/fetcher.py,sha256=stfRJnh5ZXLqRsDtQC1BDeTe0yaZI-mgm3Qx03nWUP4,2402
49
+ novel_downloader/core/factory/parser.py,sha256=0PXepJhlE6aGs9_t81vyho1eCw84-6XRBGb98_phvSQ,2237
50
+ novel_downloader/core/fetchers/__init__.py,sha256=C1OykEdCzj3fpLRRhVrvwClpzz-pzTzTilH306crgYg,1440
51
+ novel_downloader/core/fetchers/base/__init__.py,sha256=p9be-q2YjiHcQhv4_KMeZmHgaAYYyUDWoBo6Gvwughc,224
52
+ novel_downloader/core/fetchers/base/browser.py,sha256=oR3ZMw71_LJrgTZXduVtZuXEkr7PZ9nxlS5ZlYavN_0,11153
53
+ novel_downloader/core/fetchers/base/rate_limiter.py,sha256=zUYH_PjnKfUzJpcbUPtMkwXxIlF0SH-ZTFlbCUrq060,2724
54
+ novel_downloader/core/fetchers/base/session.py,sha256=Elfpov2cqojunCrLaaSL9ZgWLNVanUDZvgOoIfk5sSc,13251
55
+ novel_downloader/core/fetchers/biquge/__init__.py,sha256=9EW4eerGeob4QGoDr11A8Mv7xvcfWVFU57M3VT9vzPI,236
56
+ novel_downloader/core/fetchers/biquge/browser.py,sha256=vplaCRAz8ECxk-yXm7DxSxW3EyX8GCYJMeImUut53RI,2390
57
+ novel_downloader/core/fetchers/biquge/session.py,sha256=waxv_dPsBl2SeudaINjt-eUwpW-FcDP2zFN_1-NXhr8,2400
58
+ novel_downloader/core/fetchers/common/__init__.py,sha256=ur_zQHrmJdPsFvpyC8AWjsZptHLbBHLb3EuKLt6LRVc,236
59
+ novel_downloader/core/fetchers/common/browser.py,sha256=RgNOizfgi_59Xaee6lTlpfiCMGjz_4luNDlHLQqUpl0,2289
60
+ novel_downloader/core/fetchers/common/session.py,sha256=Ydtwun9lPN4VEIIEIA7Skm6_GOiF_9VDUPkjGQQTdtM,2299
61
+ novel_downloader/core/fetchers/esjzone/__init__.py,sha256=Cr30WpKEnCrG_vVqttfI9T0zdkwDsLFOnCxQz4EAQQA,242
62
+ novel_downloader/core/fetchers/esjzone/browser.py,sha256=SPvbKURw2DkUAlOBLbklmRCFvDoI4tAUDwmBRo2Kuc8,6224
63
+ novel_downloader/core/fetchers/esjzone/session.py,sha256=hwvS9LMWm5PYHTTZqYkBgWqkVqkrCfQcaFh6NBICt1Q,7218
64
+ novel_downloader/core/fetchers/linovelib/__init__.py,sha256=sMNXSBvn8gaZxNX5x4Ork8RzXxL7PhuigquWx6zQ6A4,254
65
+ novel_downloader/core/fetchers/linovelib/browser.py,sha256=pXasjPCG18CXmWkXFh7Uw24HNlTiocSup5tKK42YGck,5515
66
+ novel_downloader/core/fetchers/linovelib/session.py,sha256=y-R2FzKf0dpffUATI-xTKvq1S2wvQWA50H1avuqUrGM,5525
67
+ novel_downloader/core/fetchers/qianbi/__init__.py,sha256=h4Rve7fO1GcSJ-DlNC5zw7fjoldJy4chG9RZQf5DuCU,236
68
+ novel_downloader/core/fetchers/qianbi/browser.py,sha256=1EmrSwpqSYhEO_ID3RJbUaAOhcqvVnMnch6iOafXbTA,3162
69
+ novel_downloader/core/fetchers/qianbi/session.py,sha256=c3pJcgi9C1x9QYTBihvazHcgT7XTp2HBYfStTn6gSEg,3141
70
+ novel_downloader/core/fetchers/qidian/__init__.py,sha256=2LshlX82lFpWZMV6yujHsfue9KM0-F1O3HvMCopIv9M,236
71
+ novel_downloader/core/fetchers/qidian/browser.py,sha256=JuFncpirI4HLyfIkR0xZmHL6AZZy7tQOzkzsC24b9ss,8578
72
+ novel_downloader/core/fetchers/qidian/session.py,sha256=WrHsov15PoRDYbb1ZRaScJQGRZv6axz9_hVEC1Wt1PM,9746
73
+ novel_downloader/core/fetchers/sfacg/__init__.py,sha256=bQAIwERsX9XOKrP2LteFKX8Jlhw4oeUNwpZTHXn5RRg,230
74
+ novel_downloader/core/fetchers/sfacg/browser.py,sha256=15PVS75PxEKR5W7mQbqVxoN0d4V1XVYVF0l1yy_sv_Y,5681
75
+ novel_downloader/core/fetchers/sfacg/session.py,sha256=9K4emQCRq45vzYn-ZDX549tK2F92x2CBMp4ODohNOjc,5085
76
+ novel_downloader/core/fetchers/yamibo/__init__.py,sha256=5ds6DNNvpo6F6U5dboEaIsJoKSPorkPte_HWVnXMdXo,236
77
+ novel_downloader/core/fetchers/yamibo/browser.py,sha256=7YRrWbA8_cOcT_z-VjMWP6FUg30TwV6eLW4zJZ_UxSE,7249
78
+ novel_downloader/core/fetchers/yamibo/session.py,sha256=434EArdKgEYIBZkb1nMub3PQXdRTS-Ov2_1u9MESjOs,7212
79
+ novel_downloader/core/interfaces/__init__.py,sha256=hB1SjBzuN7qnZx_h3RV4w_roj3ZwShbIG3CV9jGMB14,602
80
+ novel_downloader/core/interfaces/downloader.py,sha256=XozFf-7OOtBQDlWNs3IRcBjwTPGFWwseFLGta-sZZmU,1560
81
+ novel_downloader/core/interfaces/exporter.py,sha256=zwIaJ5FXo_JmKYg2UZV9FTWX7xZ2e0QuL4RTNGRtI04,1610
82
+ novel_downloader/core/interfaces/fetcher.py,sha256=XlP3d-Q_xvuVextZCdLOxDf8BwBbx799L_WLJx0WTB8,4096
83
+ novel_downloader/core/interfaces/parser.py,sha256=iZeUgNl-zE-wsf1eW4GVCPjEaLSvQrNJi_mVEOU9ulY,1353
84
+ novel_downloader/core/parsers/__init__.py,sha256=lrpt5E1YMiI0WY_cco1lEDhBQZJ_ZEV4b5fR6Fs3ZwA,832
85
+ novel_downloader/core/parsers/base.py,sha256=GifJANFm0auYyzYuqYox9LFgDskW8G2vR0DaMrRcG1w,2962
86
+ novel_downloader/core/parsers/biquge/__init__.py,sha256=oaLgBLdMW7DcdsEGXW-S91Z6_p5XmBcBIp7TX79hmV4,173
87
+ novel_downloader/core/parsers/biquge/main_parser.py,sha256=mbrEjJ8iSvWZ7SU4RH8pHxs1jnEwwlag_KkhTYoU0sY,4572
88
+ novel_downloader/core/parsers/common/__init__.py,sha256=MzNUUxvf7jmeO0LvQ_FRW0QLKuYVXkkTgJ4CxWZKF-Y,341
89
+ novel_downloader/core/parsers/common/helper.py,sha256=U5SzhVJJ5lGRNhLoOdEQw9LfBuPv17dmyT_MbdgnCl0,12052
90
+ novel_downloader/core/parsers/common/main_parser.py,sha256=znuXTJC-TXsgBTB3Hyqi87L65bqyM90TYyjU1KEhswc,3229
91
+ novel_downloader/core/parsers/esjzone/__init__.py,sha256=RSsUdOvaiqv-rTaYVc-qO25jytRCj9X2EYErMltA_b8,177
92
+ novel_downloader/core/parsers/esjzone/main_parser.py,sha256=jF7WPAwN5S2uUOdXk3fCkM3PSS5DPhI-SzmP00GFItw,8520
93
+ novel_downloader/core/parsers/linovelib/__init__.py,sha256=t5xzMEb4Q9MZxOvedOnot2HR6nCtU5YnAxGUk2lJoMA,185
94
+ novel_downloader/core/parsers/linovelib/main_parser.py,sha256=LqC6W-Lk7sdbiqhYvqQAh2z31v23OtMzyp1kAelsugI,7565
95
+ novel_downloader/core/parsers/qianbi/__init__.py,sha256=CNmoER8U2u4-ix5S0DDq-pHTtkLR0IZf2SLaTTYXee4,173
96
+ novel_downloader/core/parsers/qianbi/main_parser.py,sha256=kMjGew_dmqjI9oIThyHgxThZZgS3IaESB_tNf1nkCKk,5069
97
+ novel_downloader/core/parsers/qidian/__init__.py,sha256=fWWFeyythX0gpDCJ-2AslrRl2hq4vW2bx9hHh1W7mAw,173
98
+ novel_downloader/core/parsers/qidian/book_info_parser.py,sha256=h5oAVJOOSZMV4U2GnSezVHPJOEUKUic_oD2gl94mEPQ,3721
99
+ novel_downloader/core/parsers/qidian/chapter_encrypted.py,sha256=VhPDERJq1ROJaBlaJuCXJGffisOxkeYFDUKXHo31HfU,18263
100
+ novel_downloader/core/parsers/qidian/chapter_normal.py,sha256=6mD86OalpxiCDhjzUjcfFYsgmnTRU_rMIgFN8_Ceu50,4453
101
+ novel_downloader/core/parsers/qidian/chapter_router.py,sha256=foVMlWtE-qUOvJD_4EDiuAVaNkFdeV_ZTCvS5IL7Orc,1957
102
+ novel_downloader/core/parsers/qidian/main_parser.py,sha256=SPU6sUO8e4Gp_1q2WypuUQYEyHhoXM7OiRsthhFT6SM,4396
103
+ novel_downloader/core/parsers/qidian/utils/__init__.py,sha256=HfjXrGAwH_ceRSE4m88Dl9qb7vHMGcPFxxTU3qrTld8,548
104
+ novel_downloader/core/parsers/qidian/utils/decryptor_fetcher.py,sha256=8ytJnAfiJIxj0wlke9UwYA6vngUyLxVZt8PbfkNUhss,4687
105
+ novel_downloader/core/parsers/qidian/utils/helpers.py,sha256=-7vd1BMu5pVFCRySfPWyrItVbZ2wrHPNY8iAQEP7T_8,3264
106
+ novel_downloader/core/parsers/qidian/utils/node_decryptor.py,sha256=gjqirr5RECScFw0C6DET7ZMaLTcqGcPOmlkwFUJbTHQ,5965
107
+ novel_downloader/core/parsers/sfacg/__init__.py,sha256=O2nscvtOweMXHMONdvySTsLSy1ulhv53WTp4r6J47tI,169
108
+ novel_downloader/core/parsers/sfacg/main_parser.py,sha256=yW18MALAZisJdFO-7peI1h-4XVloEDXeCJvXUz5hJ1A,5899
109
+ novel_downloader/core/parsers/yamibo/__init__.py,sha256=KJ_fCcakoQrsG36OOd0paXXB1cAIwMirKaA8ZLUKUKI,173
110
+ novel_downloader/core/parsers/yamibo/main_parser.py,sha256=9sNXsDR2iDVyTYupP_7PyE7BXf2po3sZLq1XEFA8DrI,6554
111
+ novel_downloader/locales/en.json,sha256=-bKxndkwJCTCt7ZZGRRKzNaulC7z8XB6Tu7T1SR-1S8,6840
112
+ novel_downloader/locales/zh.json,sha256=xzbUsKtkdQvQkehTgIURlQMPepOSWbQBgzTJ4SSJNZc,6699
113
+ novel_downloader/models/__init__.py,sha256=G5fYGdaFquJBcllFJ7-YgqkvP4vD3xVEI9MeYcrCr_s,1068
114
+ novel_downloader/models/browser.py,sha256=ly-jM7izQ77yTIG-oau51HJofDpBfrXpIJZJjoQyad8,435
115
+ novel_downloader/models/chapter.py,sha256=bdAQUDZIuuTVxoYjoOJrbS2u81b1B2mkuZkTSf0m2HQ,492
116
+ novel_downloader/models/config.py,sha256=d5G8sob9oNA8rr1N7BvDEJRVSCKJUtD_QBcvWZvGMyw,2792
117
+ novel_downloader/models/login.py,sha256=sY2Jom6PLpA9Z3Uy7plZKhda3Gq7awKOOIIaQ79PpWs,371
118
+ novel_downloader/models/site_rules.py,sha256=kzDB5F8lf4udAO0WVUrgBOR7ave3jsMBxt7cEoG0bnI,2721
119
+ novel_downloader/models/tasks.py,sha256=e4DYEXQQQewgQyCCHfc0UYnkPJ96LafmhG3oO5MQP0Q,465
120
+ novel_downloader/models/types.py,sha256=q1KDuGW0SVxQILKKoPXKRecfaKe2m8jUM0nyaeDJ6dE,394
121
+ novel_downloader/resources/config/settings.toml,sha256=A2B_EMh0zjTLi6UBCjhX__igz7xLCXnCqDzJMhiZn8E,4491
122
+ novel_downloader/resources/css_styles/main.css,sha256=WM6GePwdOGgM86fbbOxQ0_0oerTBDZeQHt8zRVfcJp8,1617
123
+ novel_downloader/resources/css_styles/volume-intro.css,sha256=6gaUnNKkrb2w8tYJRq1BGD1FwbhT1I5W2GI_Zelo9G4,1156
124
+ novel_downloader/resources/images/volume_border.png,sha256=2dEVimnTHKOfLMhi7bhkh_5joWNnrqg8duomLSNOZx4,28613
125
+ novel_downloader/resources/js_scripts/qidian_decrypt_node.js,sha256=spNrk_gXI7pPW9abr4XGc2LASMe1UuN4BUe4cH24L8s,2195
126
+ novel_downloader/resources/json/linovelib_font_map.json,sha256=F1IlEcvXGcagnZCu4Gw2vQ2NLnhy6cJOLhabukqnftw,67854
127
+ novel_downloader/resources/json/replace_word_map.json,sha256=ptL9sGO9aK7rnnAaOIyZ0OiH7gaT0BhFzficzYZSDks,55
128
+ novel_downloader/resources/text/blacklist.txt,sha256=sovK9JgARZP3lud5b1EZgvv8LSVKPthf4ADpCSZZgQ8,154
129
+ novel_downloader/tui/__init__.py,sha256=8RB8tBrPcoBzm1tpQlgZqnOZXrdHmlzMRxuk9wsN4m0,115
130
+ novel_downloader/tui/app.py,sha256=ytV1u15nGCRj_ff_GeAL3W1XlU6r5Lh_k3HBcAjPRx0,731
131
+ novel_downloader/tui/main.py,sha256=MBP8SrwEYTpGQm-V9W_4rKnTeslneORfkzFsU3Xj2yA,256
132
+ novel_downloader/tui/screens/__init__.py,sha256=QsUM5cUEKm7nluQh9acEt37xRWbkZU3vqIpBduepDCU,203
133
+ novel_downloader/tui/screens/home.py,sha256=ximy5vwHGrkUD88U6Gr9h3kT-wtOMMLBzB9aEZPuA5w,6716
134
+ novel_downloader/tui/screens/login.py,sha256=eEVmQFZKQX8mCt_qp96QK8t2pB15FeUrk_oV3jm_Mz4,2224
135
+ novel_downloader/tui/styles/home_layout.tcss,sha256=VNJs339qiwNUuqwwdK6VkYThCdsqlw-2mEoZVCspdrw,974
136
+ novel_downloader/tui/widgets/richlog_handler.py,sha256=bhFb0E7Z7-dRS07y_vMjxjPeCic9AD59MFPLhef_R5g,572
137
+ novel_downloader/utils/__init__.py,sha256=4iUXNUzxeAnGmpGWsB4K_jckUYEW0u_LqWp_OM7mtK8,78
138
+ novel_downloader/utils/cache.py,sha256=_jVEWAaMGY86kWVnAuuIvJA_hA8dH2ClI7o3p99mEvE,618
139
+ novel_downloader/utils/chapter_storage.py,sha256=wXXIzNjs1GfPPoqgYYPnQ9Two23McW6ZZNDmhfSC9aY,9831
140
+ novel_downloader/utils/constants.py,sha256=1emMOmGRuat0yX8ozZMM1pAUGdU0kYBhDVNW_6dDtps,5700
141
+ novel_downloader/utils/cookies.py,sha256=WuPBt_z7cF4n7QPIw1julATWt6dbDRCbNdgXHSikmjo,2042
142
+ novel_downloader/utils/crypto_utils.py,sha256=BRlVR9Nvbu8TQXocJb3po1-kD5HkbCV0yyT84nq_XtI,2052
143
+ novel_downloader/utils/hash_store.py,sha256=HfzthzcKbHbVaHNpqjaAs2wDeq7iIeY8Mzkt_4v80jQ,9388
144
+ novel_downloader/utils/hash_utils.py,sha256=7eC7WsO_kl25OnRYWzIXbCXsewxvCcvRCzetsf-wbTo,3023
145
+ novel_downloader/utils/i18n.py,sha256=pdAcSIA5Tp-uPEBwNByHL7C1NayTnpOsl7zFv9p2G1k,1033
146
+ novel_downloader/utils/logger.py,sha256=9h1iFS8_auiquNgOBd-Q2pbbcnAhAKL39yf3PKadu00,3339
147
+ novel_downloader/utils/model_loader.py,sha256=JKgRFrr4HlAW9zuDUBAuuo_Kk_T_g9dWiU8E3zYk0vo,1996
148
+ novel_downloader/utils/network.py,sha256=W0SVr55MSUjTmMPkUvvkH10SRgFx3GWrCN_fDavfk4A,9143
149
+ novel_downloader/utils/state.py,sha256=FcNJ85GvBu7uEIjy0QHGr4sXMbHPEMkCjwUKNg5EabI,5132
150
+ novel_downloader/utils/file_utils/__init__.py,sha256=zvOm2qSEmWd_mRGJceGBZb5MYMSDAlWYjS5MkVQNZgI,1159
151
+ novel_downloader/utils/file_utils/io.py,sha256=AZ3NUe6lifGsYt3iYyXyQ2BO41WV8j013LpZy4KSjJQ,7473
152
+ novel_downloader/utils/file_utils/normalize.py,sha256=MrsCq4FqmskKRkHRV_J0z0dmn69OerMum-9sqx2XOGM,2023
153
+ novel_downloader/utils/file_utils/sanitize.py,sha256=rE-u4vpDL10zH8FT8d9wqwWsz-7dR6PJ-LE45K8VaeE,2112
154
+ novel_downloader/utils/fontocr/__init__.py,sha256=fe-04om3xxBvFKt5BBCApXCzv-Z0K_AY7lv9IB1jEHM,543
155
+ novel_downloader/utils/fontocr/ocr_v1.py,sha256=bwkvqKXUcKsgBCrag8jHuIl8AOM_MbV28ZSQ_aO_X6s,11245
156
+ novel_downloader/utils/fontocr/ocr_v2.py,sha256=OJ_egORHa9KwVJX4kEABR25D7RRF0jVCRV4WgHUal7M,27307
157
+ novel_downloader/utils/text_utils/__init__.py,sha256=JQtEnJ22K9o_syY5PV4Bf_p4BKZonX5g9-A5eqlxpZs,806
158
+ novel_downloader/utils/text_utils/chapter_formatting.py,sha256=WAAEAcI7zI_uIeARDybZfXDdMvGio3VIkANFrK8-8Os,1378
159
+ novel_downloader/utils/text_utils/diff_display.py,sha256=aUvjMcYO-1_P8ZYiYbmYbJOByKo2bWoBV_ifRuAqwb8,2528
160
+ novel_downloader/utils/text_utils/font_mapping.py,sha256=Aos5skBhowDdPgnYmK0bpLtNm2hZg3RolNlTkxC9kO8,865
161
+ novel_downloader/utils/text_utils/text_cleaning.py,sha256=_Nahr8iv3341IyDXW-KpTn4XNG2hB-HajSM52pysPu8,1630
162
+ novel_downloader/utils/time_utils/__init__.py,sha256=725vY2PvqFhjbAz0hCOuIuhSCK8HrEqQ_k3YwvubmXo,624
163
+ novel_downloader/utils/time_utils/datetime_utils.py,sha256=1eyX8lTEqkQ-rEej_GrhbUpaIR5tsFdVSKT-q8s9X-g,4927
164
+ novel_downloader/utils/time_utils/sleep_utils.py,sha256=C4XYeAtxoVZC9Ju6vhhP9sbOrSpdZG2Nm-x1IYO_OFA,3233
165
+ novel_downloader-1.4.0.dist-info/licenses/LICENSE,sha256=XgmnH0mBf-qEiizoVAfJQAKzPB9y3rBa-ni7M0Aqv4A,1066
166
+ novel_downloader-1.4.0.dist-info/METADATA,sha256=iFXirg0_BDhczHRV37P5QgtbuUawakAx1kzY1aAfQeU,7027
167
+ novel_downloader-1.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
168
+ novel_downloader-1.4.0.dist-info/entry_points.txt,sha256=u1Ns5xI_QJyL4HAFCgJvJdib9ugu7M9I2tnQwZjJxrk,112
169
+ novel_downloader-1.4.0.dist-info/top_level.txt,sha256=hP4jYWM2LTm1jxsW4hqEB8N0dsRvldO2QdhggJT917I,17
170
+ novel_downloader-1.4.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.8.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
2
  novel-cli = novel_downloader.cli.main:cli_main
3
+ novel-tui = novel_downloader.tui.main:tui_main
@@ -1,66 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- novel_downloader.cli.interactive
4
- --------------------------------
5
-
6
- Interactive CLI mode for novel_downloader.
7
- Supports multilingual prompt, input validation, and quit control.
8
- """
9
-
10
- import click
11
- from click import Context
12
-
13
- from novel_downloader.cli.download import download_cli
14
- from novel_downloader.utils.i18n import t
15
-
16
-
17
- @click.group( # type: ignore
18
- name="interactive", help=t("interactive_help"), invoke_without_command=True
19
- )
20
- @click.pass_context # type: ignore
21
- def interactive_cli(ctx: Context) -> None:
22
- """Interactive mode for novel selection and preview."""
23
- if ctx.invoked_subcommand is None:
24
- click.echo(t("interactive_no_sub"))
25
-
26
- options = [
27
- t("interactive_option_download"),
28
- t("interactive_option_browse"),
29
- t("interactive_option_preview"),
30
- t("interactive_option_exit"),
31
- ]
32
- for idx, opt in enumerate(options, 1):
33
- click.echo(f"{idx}. {opt}")
34
-
35
- choice = click.prompt(t("interactive_prompt_choice"), type=int)
36
-
37
- if choice == 1:
38
- default_site = "qidian"
39
- site: str = click.prompt(
40
- t("download_option_site", default=default_site),
41
- default_site,
42
- )
43
- ids_input: str = click.prompt(t("interactive_prompt_book_ids"))
44
- book_ids = ids_input.strip().split()
45
- ctx.invoke(download_cli, book_ids=book_ids, site=site)
46
- elif choice == 2:
47
- ctx.invoke(browse)
48
- elif choice == 3:
49
- ctx.invoke(preview)
50
- else:
51
- click.echo(t("interactive_exit"))
52
- return
53
-
54
-
55
- @interactive_cli.command(help=t("interactive_browse_help")) # type: ignore
56
- @click.pass_context # type: ignore
57
- def browse(ctx: Context) -> None:
58
- """Browse available novels interactively."""
59
- click.echo(t("interactive_browse_start"))
60
-
61
-
62
- @interactive_cli.command(help=t("interactive_preview_help")) # type: ignore
63
- @click.pass_context # type: ignore
64
- def preview(ctx: Context) -> None:
65
- """Preview chapters before downloading."""
66
- click.echo(t("interactive_preview_start"))
@@ -1,177 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- novel_downloader.cli.settings
4
- -----------------------------
5
-
6
- Commands to configure novel downloader settings.
7
- """
8
-
9
- import shutil
10
- from importlib.resources import as_file
11
- from pathlib import Path
12
-
13
- import click
14
- from click import Context
15
-
16
- from novel_downloader.config import save_config_file, save_rules_as_json
17
- from novel_downloader.utils.constants import DEFAULT_SETTINGS_PATHS
18
- from novel_downloader.utils.i18n import t
19
- from novel_downloader.utils.logger import setup_logging
20
- from novel_downloader.utils.state import state_mgr
21
-
22
-
23
- @click.group(name="settings", help=t("settings_help")) # type: ignore
24
- def settings_cli() -> None:
25
- """Configure downloader settings."""
26
- setup_logging()
27
- pass
28
-
29
-
30
- @settings_cli.command(name="init", help=t("settings_init_help")) # type: ignore
31
- @click.option("--force", is_flag=True, help=t("settings_init_force_help")) # type: ignore
32
- def init_settings(force: bool) -> None:
33
- """Initialize default settings and rules in the current directory."""
34
- cwd = Path.cwd()
35
-
36
- for resource in DEFAULT_SETTINGS_PATHS:
37
- target_path = cwd / resource.name
38
- should_copy = True
39
-
40
- if target_path.exists():
41
- if force:
42
- should_copy = True
43
- click.echo(t("settings_init_overwrite", filename=resource.name))
44
- else:
45
- click.echo(t("settings_init_exists", filename=resource.name))
46
- should_copy = click.confirm(
47
- t("settings_init_confirm_overwrite", filename=resource.name),
48
- default=False,
49
- )
50
-
51
- if not should_copy:
52
- click.echo(t("settings_init_skip", filename=resource.name))
53
- continue
54
-
55
- try:
56
- with as_file(resource) as actual_path:
57
- shutil.copy(actual_path, target_path)
58
- click.echo(t("settings_init_copy", filename=resource.name))
59
- except Exception as e:
60
- raise click.ClickException(
61
- t("settings_init_error", filename=resource.name, err=e)
62
- ) from e
63
-
64
-
65
- @settings_cli.command(name="set-lang", help=t("settings_set_lang_help")) # type: ignore
66
- @click.argument("lang", type=click.Choice(["zh", "en"])) # type: ignore
67
- @click.pass_context # type: ignore
68
- def set_language(ctx: Context, lang: str) -> None:
69
- """Switch language between Chinese and English."""
70
- state_mgr.set_language(lang)
71
- click.echo(t("settings_set_lang", lang=lang))
72
-
73
-
74
- @settings_cli.command(name="set-config", help=t("settings_set_config_help")) # type: ignore
75
- @click.argument("path", type=click.Path(exists=True, dir_okay=False, resolve_path=True)) # type: ignore
76
- def set_config(path: str) -> None:
77
- """Set and save a custom YAML configuration file."""
78
- try:
79
- save_config_file(path)
80
- click.echo(t("settings_set_config", path=path))
81
- except Exception as e:
82
- raise click.ClickException(t("settings_set_config_fail", err=e)) from e
83
-
84
-
85
- @settings_cli.command(name="update-rules", help=t("settings_update_rules_help")) # type: ignore
86
- @click.argument("path", type=click.Path(exists=True, dir_okay=False, resolve_path=True)) # type: ignore
87
- def update_rules(path: str) -> None:
88
- """Update site rules from a TOML/YAML/JSON file."""
89
- try:
90
- save_rules_as_json(path)
91
- click.echo(t("settings_update_rules", path=path))
92
- except Exception as e:
93
- raise click.ClickException(t("settings_update_rules_fail", err=e)) from e
94
-
95
-
96
- @settings_cli.command(
97
- name="set-cookies", help=t("settings_set_cookies_help")
98
- ) # type: ignore
99
- @click.argument("site", required=False) # type: ignore
100
- @click.argument("cookies", required=False) # type: ignore
101
- @click.pass_context # type: ignore
102
- def set_cookies(ctx: Context, site: str, cookies: str) -> None:
103
- """
104
- Set or update cookies for a site.
105
-
106
- :param site: Site identifier (e.g. 'qidian', 'bqg').
107
- If omitted, you will be prompted to enter it.
108
- :param cookies: Cookie payload. Can be a JSON string (e.g. '{"k":"v"}')
109
- or a browser-style string 'k1=v1; k2=v2'.
110
- If omitted, you will be prompted to enter it.
111
- """
112
- if not site:
113
- site = click.prompt(t("settings_set_cookies_prompt_site"), type=str)
114
- if not cookies:
115
- cookies = click.prompt(t("settings_set_cookies_prompt_payload"), type=str)
116
-
117
- try:
118
- state_mgr.set_cookies(site, cookies)
119
- click.echo(t("settings_set_cookies_success", site=site))
120
- except Exception as e:
121
- raise click.ClickException(t("settings_set_cookies_fail", err=e)) from e
122
-
123
-
124
- @settings_cli.command(name="add-hash", help=t("settings_add_hash_help")) # type: ignore
125
- @click.option(
126
- "--path",
127
- type=click.Path(exists=True, dir_okay=False),
128
- help=t("settings_add_hash_path_help"),
129
- ) # type: ignore
130
- def add_image_hashes(path: str | None) -> None:
131
- """
132
- Add image hashes to internal store for matching.
133
- Can be run in interactive mode (no --path), or with a JSON file.
134
- """
135
- from novel_downloader.utils.hash_store import img_hash_store
136
-
137
- if path:
138
- try:
139
- img_hash_store.add_from_map(path)
140
- img_hash_store.save()
141
- click.echo(t("settings_add_hash_loaded", path=path))
142
- except Exception as e:
143
- raise click.ClickException(
144
- t("settings_add_hash_load_fail", err=str(e))
145
- ) from e
146
- else:
147
- click.echo(t("settings_add_hash_prompt_tip"))
148
- while True:
149
- img_path = click.prompt(
150
- t("settings_add_hash_prompt_img"),
151
- type=str,
152
- default="",
153
- show_default=False,
154
- ).strip()
155
- if not img_path or img_path.lower() in {"exit", "quit"}:
156
- break
157
- if not Path(img_path).exists():
158
- click.echo(t("settings_add_hash_path_invalid"))
159
- continue
160
-
161
- label = click.prompt(
162
- t("settings_add_hash_prompt_label"),
163
- type=str,
164
- default="",
165
- show_default=False,
166
- ).strip()
167
- if not label or label.lower() in {"exit", "quit"}:
168
- break
169
-
170
- try:
171
- img_hash_store.add_image(img_path, label)
172
- click.echo(t("settings_add_hash_added", img=img_path, label=label))
173
- except Exception as e:
174
- click.echo(t("settings_add_hash_failed", err=str(e)))
175
-
176
- img_hash_store.save()
177
- click.echo(t("settings_add_hash_saved"))