novel-downloader 1.2.1__tar.gz → 1.3.0__tar.gz
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-1.2.1 → novel_downloader-1.3.0}/PKG-INFO +14 -17
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/README.md +8 -8
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/__init__.py +1 -2
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/cli/__init__.py +0 -1
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/cli/clean.py +2 -10
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/cli/download.py +18 -22
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/cli/interactive.py +0 -1
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/cli/main.py +1 -3
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/cli/settings.py +8 -8
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/config/__init__.py +0 -1
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/config/adapter.py +48 -18
- novel_downloader-1.3.0/novel_downloader/config/loader.py +185 -0
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/config/models.py +41 -32
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/config/site_rules.py +2 -4
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/core/__init__.py +0 -1
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/core/downloaders/__init__.py +4 -4
- novel_downloader-1.3.0/novel_downloader/core/downloaders/base/__init__.py +14 -0
- novel_downloader-1.2.1/novel_downloader/core/downloaders/base_async_downloader.py → novel_downloader-1.3.0/novel_downloader/core/downloaders/base/base_async.py +49 -53
- novel_downloader-1.2.1/novel_downloader/core/downloaders/base_downloader.py → novel_downloader-1.3.0/novel_downloader/core/downloaders/base/base_sync.py +64 -43
- novel_downloader-1.3.0/novel_downloader/core/downloaders/biquge/__init__.py +12 -0
- novel_downloader-1.3.0/novel_downloader/core/downloaders/biquge/biquge_sync.py +25 -0
- novel_downloader-1.3.0/novel_downloader/core/downloaders/common/__init__.py +14 -0
- novel_downloader-1.2.1/novel_downloader/core/downloaders/common_asynb_downloader.py → novel_downloader-1.3.0/novel_downloader/core/downloaders/common/common_async.py +42 -33
- novel_downloader-1.2.1/novel_downloader/core/downloaders/common_downloader.py → novel_downloader-1.3.0/novel_downloader/core/downloaders/common/common_sync.py +34 -23
- novel_downloader-1.3.0/novel_downloader/core/downloaders/qidian/__init__.py +10 -0
- novel_downloader-1.2.1/novel_downloader/core/downloaders/qidian_downloader.py → novel_downloader-1.3.0/novel_downloader/core/downloaders/qidian/qidian_sync.py +80 -64
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/core/factory/__init__.py +4 -5
- novel_downloader-1.2.1/novel_downloader/core/factory/downloader_factory.py → novel_downloader-1.3.0/novel_downloader/core/factory/downloader.py +36 -35
- novel_downloader-1.2.1/novel_downloader/core/factory/parser_factory.py → novel_downloader-1.3.0/novel_downloader/core/factory/parser.py +12 -14
- novel_downloader-1.2.1/novel_downloader/core/factory/requester_factory.py → novel_downloader-1.3.0/novel_downloader/core/factory/requester.py +29 -16
- novel_downloader-1.2.1/novel_downloader/core/factory/saver_factory.py → novel_downloader-1.3.0/novel_downloader/core/factory/saver.py +4 -9
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/core/interfaces/__init__.py +8 -9
- novel_downloader-1.2.1/novel_downloader/core/interfaces/async_downloader_protocol.py → novel_downloader-1.3.0/novel_downloader/core/interfaces/async_downloader.py +4 -5
- novel_downloader-1.2.1/novel_downloader/core/interfaces/async_requester_protocol.py → novel_downloader-1.3.0/novel_downloader/core/interfaces/async_requester.py +26 -12
- novel_downloader-1.2.1/novel_downloader/core/interfaces/parser_protocol.py → novel_downloader-1.3.0/novel_downloader/core/interfaces/parser.py +11 -6
- novel_downloader-1.2.1/novel_downloader/core/interfaces/saver_protocol.py → novel_downloader-1.3.0/novel_downloader/core/interfaces/saver.py +2 -3
- novel_downloader-1.2.1/novel_downloader/core/interfaces/downloader_protocol.py → novel_downloader-1.3.0/novel_downloader/core/interfaces/sync_downloader.py +6 -7
- novel_downloader-1.2.1/novel_downloader/core/interfaces/requester_protocol.py → novel_downloader-1.3.0/novel_downloader/core/interfaces/sync_requester.py +34 -17
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/core/parsers/__init__.py +5 -4
- novel_downloader-1.2.1/novel_downloader/core/parsers/base_parser.py → novel_downloader-1.3.0/novel_downloader/core/parsers/base.py +20 -11
- novel_downloader-1.3.0/novel_downloader/core/parsers/biquge/__init__.py +10 -0
- novel_downloader-1.3.0/novel_downloader/core/parsers/biquge/main_parser.py +126 -0
- {novel_downloader-1.2.1/novel_downloader/core/parsers/common_parser → novel_downloader-1.3.0/novel_downloader/core/parsers/common}/__init__.py +2 -3
- {novel_downloader-1.2.1/novel_downloader/core/parsers/common_parser → novel_downloader-1.3.0/novel_downloader/core/parsers/common}/helper.py +20 -18
- {novel_downloader-1.2.1/novel_downloader/core/parsers/common_parser → novel_downloader-1.3.0/novel_downloader/core/parsers/common}/main_parser.py +15 -9
- {novel_downloader-1.2.1/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.0/novel_downloader/core/parsers/qidian}/__init__.py +2 -3
- {novel_downloader-1.2.1/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.0/novel_downloader/core/parsers/qidian}/browser/__init__.py +2 -3
- {novel_downloader-1.2.1/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.0/novel_downloader/core/parsers/qidian}/browser/chapter_encrypted.py +41 -49
- {novel_downloader-1.2.1/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.0/novel_downloader/core/parsers/qidian}/browser/chapter_normal.py +17 -21
- {novel_downloader-1.2.1/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.0/novel_downloader/core/parsers/qidian}/browser/chapter_router.py +10 -9
- {novel_downloader-1.2.1/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.0/novel_downloader/core/parsers/qidian}/browser/main_parser.py +16 -12
- {novel_downloader-1.2.1/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.0/novel_downloader/core/parsers/qidian}/session/__init__.py +2 -3
- {novel_downloader-1.2.1/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.0/novel_downloader/core/parsers/qidian}/session/chapter_encrypted.py +37 -45
- {novel_downloader-1.2.1/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.0/novel_downloader/core/parsers/qidian}/session/chapter_normal.py +19 -23
- {novel_downloader-1.2.1/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.0/novel_downloader/core/parsers/qidian}/session/chapter_router.py +10 -9
- {novel_downloader-1.2.1/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.0/novel_downloader/core/parsers/qidian}/session/main_parser.py +16 -12
- {novel_downloader-1.2.1/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.0/novel_downloader/core/parsers/qidian}/session/node_decryptor.py +7 -10
- {novel_downloader-1.2.1/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.0/novel_downloader/core/parsers/qidian}/shared/__init__.py +2 -3
- novel_downloader-1.3.0/novel_downloader/core/parsers/qidian/shared/book_info_parser.py +150 -0
- {novel_downloader-1.2.1/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.0/novel_downloader/core/parsers/qidian}/shared/helpers.py +9 -10
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/core/requesters/__init__.py +9 -5
- novel_downloader-1.3.0/novel_downloader/core/requesters/base/__init__.py +16 -0
- novel_downloader-1.2.1/novel_downloader/core/requesters/base_async_session.py → novel_downloader-1.3.0/novel_downloader/core/requesters/base/async_session.py +180 -73
- novel_downloader-1.3.0/novel_downloader/core/requesters/base/browser.py +340 -0
- novel_downloader-1.3.0/novel_downloader/core/requesters/base/session.py +364 -0
- novel_downloader-1.3.0/novel_downloader/core/requesters/biquge/__init__.py +12 -0
- novel_downloader-1.3.0/novel_downloader/core/requesters/biquge/session.py +90 -0
- {novel_downloader-1.2.1/novel_downloader/core/requesters/common_requester → novel_downloader-1.3.0/novel_downloader/core/requesters/common}/__init__.py +4 -5
- novel_downloader-1.3.0/novel_downloader/core/requesters/common/async_session.py +96 -0
- novel_downloader-1.3.0/novel_downloader/core/requesters/common/session.py +113 -0
- novel_downloader-1.3.0/novel_downloader/core/requesters/qidian/__init__.py +21 -0
- novel_downloader-1.3.0/novel_downloader/core/requesters/qidian/broswer.py +306 -0
- novel_downloader-1.3.0/novel_downloader/core/requesters/qidian/session.py +287 -0
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/core/savers/__init__.py +5 -3
- novel_downloader-1.2.1/novel_downloader/core/savers/base_saver.py → novel_downloader-1.3.0/novel_downloader/core/savers/base.py +12 -13
- novel_downloader-1.3.0/novel_downloader/core/savers/biquge.py +25 -0
- {novel_downloader-1.2.1/novel_downloader/core/savers/common_saver → novel_downloader-1.3.0/novel_downloader/core/savers/common}/__init__.py +2 -3
- novel_downloader-1.2.1/novel_downloader/core/savers/common_saver/common_epub.py → novel_downloader-1.3.0/novel_downloader/core/savers/common/epub.py +24 -52
- {novel_downloader-1.2.1/novel_downloader/core/savers/common_saver → novel_downloader-1.3.0/novel_downloader/core/savers/common}/main_saver.py +43 -9
- novel_downloader-1.2.1/novel_downloader/core/savers/common_saver/common_txt.py → novel_downloader-1.3.0/novel_downloader/core/savers/common/txt.py +16 -46
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/core/savers/epub_utils/__init__.py +0 -1
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/core/savers/epub_utils/css_builder.py +13 -7
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/core/savers/epub_utils/initializer.py +4 -5
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/core/savers/epub_utils/text_to_html.py +2 -3
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/core/savers/epub_utils/volume_intro.py +1 -3
- novel_downloader-1.2.1/novel_downloader/core/savers/qidian_saver.py → novel_downloader-1.3.0/novel_downloader/core/savers/qidian.py +12 -6
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/locales/en.json +12 -4
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/locales/zh.json +9 -1
- novel_downloader-1.3.0/novel_downloader/resources/config/settings.toml +88 -0
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/cache.py +2 -2
- novel_downloader-1.3.0/novel_downloader/utils/chapter_storage.py +340 -0
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/constants.py +8 -5
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/crypto_utils.py +3 -3
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/file_utils/__init__.py +0 -1
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/file_utils/io.py +12 -17
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/file_utils/normalize.py +1 -3
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/file_utils/sanitize.py +2 -9
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/fontocr/__init__.py +0 -1
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/fontocr/ocr_v1.py +19 -22
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/fontocr/ocr_v2.py +147 -60
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/hash_store.py +19 -20
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/hash_utils.py +0 -1
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/i18n.py +3 -4
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/logger.py +5 -6
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/model_loader.py +5 -8
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/network.py +9 -10
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/state.py +6 -7
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/text_utils/__init__.py +0 -1
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/text_utils/chapter_formatting.py +2 -7
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/text_utils/diff_display.py +0 -1
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/text_utils/font_mapping.py +1 -4
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/text_utils/text_cleaning.py +0 -1
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/time_utils/__init__.py +0 -1
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/time_utils/datetime_utils.py +9 -11
- novel_downloader-1.3.0/novel_downloader/utils/time_utils/sleep_utils.py +63 -0
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader.egg-info/PKG-INFO +14 -17
- novel_downloader-1.3.0/novel_downloader.egg-info/SOURCES.txt +130 -0
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader.egg-info/requires.txt +3 -4
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/pyproject.toml +20 -14
- novel_downloader-1.2.1/novel_downloader/config/loader.py +0 -177
- novel_downloader-1.2.1/novel_downloader/core/parsers/qidian_parser/shared/book_info_parser.py +0 -95
- novel_downloader-1.2.1/novel_downloader/core/requesters/base_browser.py +0 -210
- novel_downloader-1.2.1/novel_downloader/core/requesters/base_session.py +0 -243
- novel_downloader-1.2.1/novel_downloader/core/requesters/common_requester/common_async_session.py +0 -98
- novel_downloader-1.2.1/novel_downloader/core/requesters/common_requester/common_session.py +0 -126
- novel_downloader-1.2.1/novel_downloader/core/requesters/qidian_requester/__init__.py +0 -22
- novel_downloader-1.2.1/novel_downloader/core/requesters/qidian_requester/qidian_broswer.py +0 -377
- novel_downloader-1.2.1/novel_downloader/core/requesters/qidian_requester/qidian_session.py +0 -202
- novel_downloader-1.2.1/novel_downloader/resources/config/settings.yaml +0 -76
- novel_downloader-1.2.1/novel_downloader/utils/time_utils/sleep_utils.py +0 -49
- novel_downloader-1.2.1/novel_downloader.egg-info/SOURCES.txt +0 -118
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/LICENSE +0 -0
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/resources/config/rules.toml +0 -0
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/resources/css_styles/main.css +0 -0
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/resources/css_styles/volume-intro.css +0 -0
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/resources/images/volume_border.png +0 -0
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/resources/js_scripts/qidian_decrypt_node.js +0 -0
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/resources/json/replace_word_map.json +0 -0
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/resources/text/blacklist.txt +0 -0
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader/utils/__init__.py +0 -0
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader.egg-info/dependency_links.txt +0 -0
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader.egg-info/entry_points.txt +0 -0
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/novel_downloader.egg-info/top_level.txt +0 -0
- {novel_downloader-1.2.1 → novel_downloader-1.3.0}/setup.cfg +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)
|
@@ -22,13 +22,10 @@ pip install novel-downloader
|
|
22
22
|
# 如需支持字体解密功能 (decode_font), 请使用:
|
23
23
|
# pip install novel-downloader[font-recovery]
|
24
24
|
|
25
|
-
#
|
26
|
-
# pip install novel-downloader[async]
|
27
|
-
|
28
|
-
# 初始化默认配置 (生成 settings.yaml)
|
25
|
+
# 初始化默认配置 (生成 settings.toml)
|
29
26
|
novel-cli settings init
|
30
27
|
|
31
|
-
# 编辑 ./settings.
|
28
|
+
# 编辑 ./settings.toml 完成 site/book_ids 等
|
32
29
|
# 可查看 docs/4-settings-schema.md
|
33
30
|
|
34
31
|
# 运行下载
|
@@ -45,7 +42,6 @@ cd novel-downloader
|
|
45
42
|
pip install .
|
46
43
|
# 或安装带可选功能:
|
47
44
|
# pip install .[font-recovery]
|
48
|
-
# pip install .[async]
|
49
45
|
```
|
50
46
|
|
51
47
|
更多使用方法, 查看 [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-usage-examples.md)
|
@@ -55,7 +51,10 @@ pip install .
|
|
55
51
|
## 功能特性
|
56
52
|
|
57
53
|
- 爬取起点中文网的小说章节内容 (支持免费与已订阅章节)
|
58
|
-
-
|
54
|
+
- 断点续爬
|
55
|
+
- 自动整合所有章节并导出为
|
56
|
+
- TXT
|
57
|
+
- EPUB
|
59
58
|
- 支持活动广告过滤:
|
60
59
|
- [x] 章节标题
|
61
60
|
- [ ] 章节正文
|
@@ -69,8 +68,9 @@ pip install .
|
|
69
68
|
- [安装](https://github.com/BowenZ217/novel-downloader/blob/main/docs/1-installation.md)
|
70
69
|
- [环境准备](https://github.com/BowenZ217/novel-downloader/blob/main/docs/2-environment-setup.md)
|
71
70
|
- [配置](https://github.com/BowenZ217/novel-downloader/blob/main/docs/3-configuration.md)
|
72
|
-
- [settings.
|
71
|
+
- [settings.toml 配置说明](https://github.com/BowenZ217/novel-downloader/blob/main/docs/4-settings-schema.md)
|
73
72
|
- [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-usage-examples.md)
|
73
|
+
- [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-supported-sites.md)
|
74
74
|
- [文件保存](https://github.com/BowenZ217/novel-downloader/blob/main/docs/file-saving.md)
|
75
75
|
- [TODO](https://github.com/BowenZ217/novel-downloader/blob/main/docs/todo.md)
|
76
76
|
- [开发](https://github.com/BowenZ217/novel-downloader/blob/main/docs/develop.md)
|
@@ -1,5 +1,4 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
|
-
# -*- coding: utf-8 -*-
|
3
2
|
"""
|
4
3
|
novel_downloader
|
5
4
|
----------------
|
@@ -7,7 +6,7 @@ novel_downloader
|
|
7
6
|
Core package for the Novel Downloader project.
|
8
7
|
"""
|
9
8
|
|
10
|
-
__version__ = "1.
|
9
|
+
__version__ = "1.3.0"
|
11
10
|
|
12
11
|
__author__ = "Saudade Z"
|
13
12
|
__email__ = "saudadez217@gmail.com"
|
@@ -1,5 +1,4 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
|
-
# -*- coding: utf-8 -*-
|
3
2
|
"""
|
4
3
|
novel_downloader.cli.clean
|
5
4
|
-----------------------------
|
@@ -8,7 +7,6 @@ novel_downloader.cli.clean
|
|
8
7
|
|
9
8
|
import shutil
|
10
9
|
from pathlib import Path
|
11
|
-
from typing import List, Optional
|
12
10
|
|
13
11
|
import click
|
14
12
|
|
@@ -19,7 +17,6 @@ from novel_downloader.utils.constants import (
|
|
19
17
|
LOGGER_DIR,
|
20
18
|
MODEL_CACHE_DIR,
|
21
19
|
REC_CHAR_MODEL_REPO,
|
22
|
-
STATE_DIR,
|
23
20
|
)
|
24
21
|
from novel_downloader.utils.i18n import t
|
25
22
|
|
@@ -35,7 +32,7 @@ def delete_path(p: Path) -> None:
|
|
35
32
|
click.echo(f"[clean] {t('clean_not_found')}: {p}")
|
36
33
|
|
37
34
|
|
38
|
-
def clean_model_repo_cache(repo_id:
|
35
|
+
def clean_model_repo_cache(repo_id: str | None = None, all: bool = False) -> bool:
|
39
36
|
"""
|
40
37
|
Delete Hugging Face cache for a specific repo.
|
41
38
|
"""
|
@@ -61,7 +58,6 @@ def clean_model_repo_cache(repo_id: Optional[str] = None, all: bool = False) ->
|
|
61
58
|
@click.command(name="clean", help=t("help_clean")) # type: ignore
|
62
59
|
@click.option("--logs", is_flag=True, help=t("clean_logs")) # type: ignore
|
63
60
|
@click.option("--cache", is_flag=True, help=t("clean_cache")) # type: ignore
|
64
|
-
@click.option("--state", is_flag=True, help=t("clean_state")) # type: ignore
|
65
61
|
@click.option("--data", is_flag=True, help=t("clean_data")) # type: ignore
|
66
62
|
@click.option("--config", is_flag=True, help=t("clean_config")) # type: ignore
|
67
63
|
@click.option("--models", is_flag=True, help=t("clean_models")) # type: ignore
|
@@ -72,7 +68,6 @@ def clean_model_repo_cache(repo_id: Optional[str] = None, all: bool = False) ->
|
|
72
68
|
def clean_cli(
|
73
69
|
logs: bool,
|
74
70
|
cache: bool,
|
75
|
-
state: bool,
|
76
71
|
data: bool,
|
77
72
|
config: bool,
|
78
73
|
models: bool,
|
@@ -81,7 +76,7 @@ def clean_cli(
|
|
81
76
|
all: bool,
|
82
77
|
yes: bool,
|
83
78
|
) -> None:
|
84
|
-
targets:
|
79
|
+
targets: list[Path] = []
|
85
80
|
|
86
81
|
if all:
|
87
82
|
if not yes:
|
@@ -92,7 +87,6 @@ def clean_cli(
|
|
92
87
|
targets = [
|
93
88
|
LOGGER_DIR,
|
94
89
|
JS_SCRIPT_DIR,
|
95
|
-
STATE_DIR,
|
96
90
|
DATA_DIR,
|
97
91
|
CONFIG_DIR,
|
98
92
|
MODEL_CACHE_DIR,
|
@@ -102,8 +96,6 @@ def clean_cli(
|
|
102
96
|
targets.append(LOGGER_DIR)
|
103
97
|
if cache:
|
104
98
|
targets.append(JS_SCRIPT_DIR)
|
105
|
-
if state:
|
106
|
-
targets.append(STATE_DIR)
|
107
99
|
if data:
|
108
100
|
targets.append(DATA_DIR)
|
109
101
|
if config:
|
@@ -1,5 +1,4 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
|
-
# -*- coding: utf-8 -*-
|
3
2
|
"""
|
4
3
|
novel_downloader.cli.download
|
5
4
|
-----------------------------
|
@@ -8,7 +7,7 @@ Download full novels by book IDs
|
|
8
7
|
(supports config files, site switching, and localization prompts).
|
9
8
|
"""
|
10
9
|
|
11
|
-
|
10
|
+
import asyncio
|
12
11
|
|
13
12
|
import click
|
14
13
|
from click import Context
|
@@ -41,7 +40,7 @@ from novel_downloader.utils.logger import setup_logging
|
|
41
40
|
help=t("download_option_site", default="qidian"),
|
42
41
|
) # type: ignore
|
43
42
|
@click.pass_context # type: ignore
|
44
|
-
def download_cli(ctx: Context, book_ids:
|
43
|
+
def download_cli(ctx: Context, book_ids: list[str], site: str) -> None:
|
45
44
|
"""Download full novels by book IDs."""
|
46
45
|
config_path = ctx.obj.get("config_path")
|
47
46
|
|
@@ -57,6 +56,8 @@ def download_cli(ctx: Context, book_ids: List[str], site: str) -> None:
|
|
57
56
|
parser_cfg = adapter.get_parser_config()
|
58
57
|
saver_cfg = adapter.get_saver_config()
|
59
58
|
|
59
|
+
click.echo(t("download_site_mode", mode=downloader_cfg.mode))
|
60
|
+
|
60
61
|
# If no book_ids provided on the command line, try to load them from config
|
61
62
|
if not book_ids:
|
62
63
|
try:
|
@@ -67,7 +68,7 @@ def download_cli(ctx: Context, book_ids: List[str], site: str) -> None:
|
|
67
68
|
|
68
69
|
# Filter out placeholder/example IDs
|
69
70
|
invalid_ids = {"0000000000"}
|
70
|
-
valid_book_ids =
|
71
|
+
valid_book_ids = set(book_ids) - invalid_ids
|
71
72
|
|
72
73
|
if not book_ids:
|
73
74
|
click.echo(t("download_no_ids"))
|
@@ -80,21 +81,20 @@ def download_cli(ctx: Context, book_ids: List[str], site: str) -> None:
|
|
80
81
|
|
81
82
|
# Initialize the requester, parser, saver, and downloader components
|
82
83
|
if downloader_cfg.mode == "async":
|
83
|
-
import asyncio
|
84
|
-
|
85
|
-
async_requester = get_async_requester(site, requester_cfg)
|
86
|
-
async_parser = get_parser(site, parser_cfg)
|
87
|
-
async_saver = get_saver(site, saver_cfg)
|
88
84
|
setup_logging()
|
89
|
-
async_downloader = get_async_downloader(
|
90
|
-
requester=async_requester,
|
91
|
-
parser=async_parser,
|
92
|
-
saver=async_saver,
|
93
|
-
site=site,
|
94
|
-
config=downloader_cfg,
|
95
|
-
)
|
96
85
|
|
97
86
|
async def async_download_all() -> None:
|
87
|
+
async_requester = get_async_requester(site, requester_cfg)
|
88
|
+
async_parser = get_parser(site, parser_cfg)
|
89
|
+
async_saver = get_saver(site, saver_cfg)
|
90
|
+
async_downloader = get_async_downloader(
|
91
|
+
requester=async_requester,
|
92
|
+
parser=async_parser,
|
93
|
+
saver=async_saver,
|
94
|
+
site=site,
|
95
|
+
config=downloader_cfg,
|
96
|
+
)
|
97
|
+
|
98
98
|
prepare = getattr(async_downloader, "prepare", None)
|
99
99
|
if prepare and asyncio.iscoroutinefunction(prepare):
|
100
100
|
await prepare()
|
@@ -103,9 +103,7 @@ def download_cli(ctx: Context, book_ids: List[str], site: str) -> None:
|
|
103
103
|
click.echo(t("download_downloading", book_id=book_id, site=site))
|
104
104
|
await async_downloader.download_one(book_id)
|
105
105
|
|
106
|
-
|
107
|
-
input(t("download_prompt_parse"))
|
108
|
-
await async_requester.shutdown()
|
106
|
+
await async_requester.close()
|
109
107
|
|
110
108
|
asyncio.run(async_download_all())
|
111
109
|
else:
|
@@ -125,8 +123,6 @@ def download_cli(ctx: Context, book_ids: List[str], site: str) -> None:
|
|
125
123
|
click.echo(t("download_downloading", book_id=book_id, site=site))
|
126
124
|
sync_downloader.download_one(book_id)
|
127
125
|
|
128
|
-
|
129
|
-
input(t("download_prompt_parse"))
|
130
|
-
sync_requester.shutdown()
|
126
|
+
sync_requester.close()
|
131
127
|
|
132
128
|
return
|
@@ -1,5 +1,4 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
|
-
# -*- coding: utf-8 -*-
|
3
2
|
"""
|
4
3
|
novel_downloader.cli.main
|
5
4
|
--------------------------
|
@@ -7,7 +6,6 @@ novel_downloader.cli.main
|
|
7
6
|
Unified CLI entry point. Parses arguments and delegates to parser or interactive.
|
8
7
|
"""
|
9
8
|
|
10
|
-
from typing import Optional
|
11
9
|
|
12
10
|
import click
|
13
11
|
from click import Context
|
@@ -24,7 +22,7 @@ from novel_downloader.utils.i18n import t
|
|
24
22
|
help=t("help_config"),
|
25
23
|
) # type: ignore
|
26
24
|
@click.pass_context # type: ignore
|
27
|
-
def cli_main(ctx: Context, config:
|
25
|
+
def cli_main(ctx: Context, config: str | None) -> None:
|
28
26
|
"""Novel Downloader CLI."""
|
29
27
|
ctx.ensure_object(dict)
|
30
28
|
ctx.obj["config_path"] = config
|
@@ -1,5 +1,4 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
|
-
# -*- coding: utf-8 -*-
|
3
2
|
"""
|
4
3
|
novel_downloader.cli.settings
|
5
4
|
-----------------------------
|
@@ -10,7 +9,6 @@ Commands to configure novel downloader settings.
|
|
10
9
|
import shutil
|
11
10
|
from importlib.resources import as_file
|
12
11
|
from pathlib import Path
|
13
|
-
from typing import Optional
|
14
12
|
|
15
13
|
import click
|
16
14
|
from click import Context
|
@@ -61,7 +59,7 @@ def init_settings(force: bool) -> None:
|
|
61
59
|
except Exception as e:
|
62
60
|
raise click.ClickException(
|
63
61
|
t("settings_init_error", filename=resource.name, err=e)
|
64
|
-
)
|
62
|
+
) from e
|
65
63
|
|
66
64
|
|
67
65
|
@settings_cli.command(name="set-lang", help=t("settings_set_lang_help")) # type: ignore
|
@@ -81,7 +79,7 @@ def set_config(path: str) -> None:
|
|
81
79
|
save_config_file(path)
|
82
80
|
click.echo(t("settings_set_config", path=path))
|
83
81
|
except Exception as e:
|
84
|
-
raise click.ClickException(t("settings_set_config_fail", err=e))
|
82
|
+
raise click.ClickException(t("settings_set_config_fail", err=e)) from e
|
85
83
|
|
86
84
|
|
87
85
|
@settings_cli.command(name="update-rules", help=t("settings_update_rules_help")) # type: ignore
|
@@ -92,7 +90,7 @@ def update_rules(path: str) -> None:
|
|
92
90
|
save_rules_as_json(path)
|
93
91
|
click.echo(t("settings_update_rules", path=path))
|
94
92
|
except Exception as e:
|
95
|
-
raise click.ClickException(t("settings_update_rules_fail", err=e))
|
93
|
+
raise click.ClickException(t("settings_update_rules_fail", err=e)) from e
|
96
94
|
|
97
95
|
|
98
96
|
@settings_cli.command(
|
@@ -120,7 +118,7 @@ def set_cookies(ctx: Context, site: str, cookies: str) -> None:
|
|
120
118
|
state_mgr.set_cookies(site, cookies)
|
121
119
|
click.echo(t("settings_set_cookies_success", site=site))
|
122
120
|
except Exception as e:
|
123
|
-
raise click.ClickException(t("settings_set_cookies_fail", err=e))
|
121
|
+
raise click.ClickException(t("settings_set_cookies_fail", err=e)) from e
|
124
122
|
|
125
123
|
|
126
124
|
@settings_cli.command(name="add-hash", help=t("settings_add_hash_help")) # type: ignore
|
@@ -129,7 +127,7 @@ def set_cookies(ctx: Context, site: str, cookies: str) -> None:
|
|
129
127
|
type=click.Path(exists=True, dir_okay=False),
|
130
128
|
help=t("settings_add_hash_path_help"),
|
131
129
|
) # type: ignore
|
132
|
-
def add_image_hashes(path:
|
130
|
+
def add_image_hashes(path: str | None) -> None:
|
133
131
|
"""
|
134
132
|
Add image hashes to internal store for matching.
|
135
133
|
Can be run in interactive mode (no --path), or with a JSON file.
|
@@ -142,7 +140,9 @@ def add_image_hashes(path: Optional[str]) -> None:
|
|
142
140
|
img_hash_store.save()
|
143
141
|
click.echo(t("settings_add_hash_loaded", path=path))
|
144
142
|
except Exception as e:
|
145
|
-
raise click.ClickException(
|
143
|
+
raise click.ClickException(
|
144
|
+
t("settings_add_hash_load_fail", err=str(e))
|
145
|
+
) from e
|
146
146
|
else:
|
147
147
|
click.echo(t("settings_add_hash_prompt_tip"))
|
148
148
|
while True:
|
@@ -1,5 +1,4 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
|
-
# -*- coding: utf-8 -*-
|
3
2
|
"""
|
4
3
|
novel_downloader.config.adapter
|
5
4
|
-------------------------------
|
@@ -15,7 +14,9 @@ Supported mappings:
|
|
15
14
|
- sites[site] -> book_ids list
|
16
15
|
"""
|
17
16
|
|
18
|
-
from typing import Any
|
17
|
+
from typing import Any
|
18
|
+
|
19
|
+
from novel_downloader.utils.constants import SUPPORTED_SITES
|
19
20
|
|
20
21
|
from .models import (
|
21
22
|
DownloaderConfig,
|
@@ -23,6 +24,7 @@ from .models import (
|
|
23
24
|
RequesterConfig,
|
24
25
|
SaverConfig,
|
25
26
|
)
|
27
|
+
from .site_rules import load_site_rules
|
26
28
|
|
27
29
|
|
28
30
|
class ConfigAdapter:
|
@@ -30,7 +32,7 @@ class ConfigAdapter:
|
|
30
32
|
Adapter to map a raw config dict + site name into structured dataclass configs.
|
31
33
|
"""
|
32
34
|
|
33
|
-
def __init__(self, config:
|
35
|
+
def __init__(self, config: dict[str, Any], site: str):
|
34
36
|
"""
|
35
37
|
:param config: 完整加载的配置 dict
|
36
38
|
:param site: 当前站点名称 (e.g. "qidian")
|
@@ -38,11 +40,35 @@ class ConfigAdapter:
|
|
38
40
|
self._config = config
|
39
41
|
self._site = site
|
40
42
|
|
41
|
-
|
43
|
+
site_rules = load_site_rules() # -> Dict[str, SiteRules]
|
44
|
+
self._supported_sites = set(site_rules.keys()) | SUPPORTED_SITES
|
45
|
+
|
46
|
+
@property
|
47
|
+
def site(self) -> str:
|
48
|
+
return self._site
|
49
|
+
|
50
|
+
@site.setter
|
51
|
+
def site(self, value: str) -> None:
|
52
|
+
self._site = value
|
53
|
+
|
54
|
+
def _get_site_cfg(self, site: str | None = None) -> dict[str, Any]:
|
42
55
|
"""
|
43
|
-
|
56
|
+
获取指定站点的配置 (默认为当前适配站点)
|
57
|
+
|
58
|
+
1. 如果有 site-specific 配置, 优先返回它
|
59
|
+
2. 否则, 如果该站点在支持站点中, 尝试返回 'common' 配置
|
60
|
+
3. 否则返回空 dict
|
44
61
|
"""
|
45
|
-
|
62
|
+
site = site or self._site
|
63
|
+
sites_cfg = self._config.get("sites", {}) or {}
|
64
|
+
|
65
|
+
if site in sites_cfg:
|
66
|
+
return sites_cfg[site] or {}
|
67
|
+
|
68
|
+
if site in self._supported_sites:
|
69
|
+
return sites_cfg.get("common", {}) or {}
|
70
|
+
|
71
|
+
return {}
|
46
72
|
|
47
73
|
def get_requester_config(self) -> RequesterConfig:
|
48
74
|
"""
|
@@ -50,12 +76,13 @@ class ConfigAdapter:
|
|
50
76
|
返回 RequesterConfig 实例
|
51
77
|
"""
|
52
78
|
req = self._config.get("requests", {})
|
53
|
-
site_cfg = self.
|
79
|
+
site_cfg = self._get_site_cfg()
|
54
80
|
return RequesterConfig(
|
55
|
-
wait_time=req.get("wait_time", 5),
|
56
81
|
retry_times=req.get("retry_times", 3),
|
57
|
-
|
58
|
-
timeout=req.get("timeout", 30),
|
82
|
+
backoff_factor=req.get("backoff_factor", 2.0),
|
83
|
+
timeout=req.get("timeout", 30.0),
|
84
|
+
max_connections=req.get("max_connections", 10),
|
85
|
+
max_rps=req.get("max_rps", None),
|
59
86
|
headless=req.get("headless", True),
|
60
87
|
user_data_folder=req.get("user_data_folder", "./user_data"),
|
61
88
|
profile_name=req.get("profile_name", "Profile_1"),
|
@@ -63,7 +90,6 @@ class ConfigAdapter:
|
|
63
90
|
disable_images=req.get("disable_images", True),
|
64
91
|
mute_audio=req.get("mute_audio", True),
|
65
92
|
mode=site_cfg.get("mode", "session"),
|
66
|
-
max_rps=site_cfg.get("max_rps", None),
|
67
93
|
)
|
68
94
|
|
69
95
|
def get_downloader_config(self) -> DownloaderConfig:
|
@@ -73,11 +99,11 @@ class ConfigAdapter:
|
|
73
99
|
"""
|
74
100
|
gen = self._config.get("general", {})
|
75
101
|
debug = gen.get("debug", {})
|
76
|
-
site_cfg = self.
|
102
|
+
site_cfg = self._get_site_cfg()
|
77
103
|
return DownloaderConfig(
|
78
|
-
request_interval=gen.get("request_interval", 5),
|
104
|
+
request_interval=gen.get("request_interval", 5.0),
|
79
105
|
raw_data_dir=gen.get("raw_data_dir", "./raw_data"),
|
80
|
-
cache_dir=gen.get("cache_dir", "./
|
106
|
+
cache_dir=gen.get("cache_dir", "./novel_cache"),
|
81
107
|
download_workers=gen.get("download_workers", 4),
|
82
108
|
parser_workers=gen.get("parser_workers", 4),
|
83
109
|
use_process_pool=gen.get("use_process_pool", True),
|
@@ -85,6 +111,8 @@ class ConfigAdapter:
|
|
85
111
|
login_required=site_cfg.get("login_required", False),
|
86
112
|
save_html=debug.get("save_html", False),
|
87
113
|
mode=site_cfg.get("mode", "session"),
|
114
|
+
storage_backend=gen.get("storage_backend", "json"),
|
115
|
+
storage_batch_size=gen.get("storage_batch_size", 1),
|
88
116
|
)
|
89
117
|
|
90
118
|
def get_parser_config(self) -> ParserConfig:
|
@@ -94,9 +122,9 @@ class ConfigAdapter:
|
|
94
122
|
"""
|
95
123
|
gen = self._config.get("general", {})
|
96
124
|
font_ocr = gen.get("font_ocr", {})
|
97
|
-
site_cfg = self.
|
125
|
+
site_cfg = self._get_site_cfg()
|
98
126
|
return ParserConfig(
|
99
|
-
cache_dir=gen.get("cache_dir", "./
|
127
|
+
cache_dir=gen.get("cache_dir", "./novel_cache"),
|
100
128
|
decode_font=font_ocr.get("decode_font", False),
|
101
129
|
use_freq=font_ocr.get("use_freq", False),
|
102
130
|
use_ocr=font_ocr.get("use_ocr", True),
|
@@ -124,6 +152,7 @@ class ConfigAdapter:
|
|
124
152
|
return SaverConfig(
|
125
153
|
raw_data_dir=gen.get("raw_data_dir", "./raw_data"),
|
126
154
|
output_dir=gen.get("output_dir", "./downloads"),
|
155
|
+
storage_backend=gen.get("storage_backend", "json"),
|
127
156
|
clean_text=out.get("clean_text", True),
|
128
157
|
make_txt=fmt.get("make_txt", True),
|
129
158
|
make_epub=fmt.get("make_epub", False),
|
@@ -133,13 +162,14 @@ class ConfigAdapter:
|
|
133
162
|
filename_template=naming.get("filename_template", "{title}_{author}"),
|
134
163
|
include_cover=epub_opts.get("include_cover", True),
|
135
164
|
include_toc=epub_opts.get("include_toc", False),
|
165
|
+
include_picture=epub_opts.get("include_picture", False),
|
136
166
|
)
|
137
167
|
|
138
|
-
def get_book_ids(self) ->
|
168
|
+
def get_book_ids(self) -> list[str]:
|
139
169
|
"""
|
140
170
|
从 config["sites"][site]["book_ids"] 中提取目标书籍列表
|
141
171
|
"""
|
142
|
-
site_cfg = self.
|
172
|
+
site_cfg = self._get_site_cfg()
|
143
173
|
raw_ids = site_cfg.get("book_ids", [])
|
144
174
|
|
145
175
|
if isinstance(raw_ids, str):
|