novel-downloader 1.2.2__tar.gz → 1.3.1__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.2 → novel_downloader-1.3.1}/PKG-INFO +14 -17
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/README.md +8 -8
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/__init__.py +1 -2
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/cli/__init__.py +0 -1
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/cli/clean.py +2 -10
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/cli/download.py +16 -22
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/cli/interactive.py +0 -1
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/cli/main.py +1 -3
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/cli/settings.py +8 -8
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/config/__init__.py +0 -1
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/config/adapter.py +32 -27
- novel_downloader-1.3.1/novel_downloader/config/loader.py +185 -0
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/config/models.py +35 -29
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/config/site_rules.py +2 -4
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/core/__init__.py +0 -1
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/core/downloaders/__init__.py +4 -4
- novel_downloader-1.3.1/novel_downloader/core/downloaders/base/__init__.py +14 -0
- novel_downloader-1.2.2/novel_downloader/core/downloaders/base_async_downloader.py → novel_downloader-1.3.1/novel_downloader/core/downloaders/base/base_async.py +49 -53
- novel_downloader-1.2.2/novel_downloader/core/downloaders/base_downloader.py → novel_downloader-1.3.1/novel_downloader/core/downloaders/base/base_sync.py +64 -43
- novel_downloader-1.3.1/novel_downloader/core/downloaders/biquge/__init__.py +12 -0
- novel_downloader-1.3.1/novel_downloader/core/downloaders/biquge/biquge_sync.py +25 -0
- novel_downloader-1.3.1/novel_downloader/core/downloaders/common/__init__.py +14 -0
- novel_downloader-1.2.2/novel_downloader/core/downloaders/common_asynb_downloader.py → novel_downloader-1.3.1/novel_downloader/core/downloaders/common/common_async.py +42 -33
- novel_downloader-1.2.2/novel_downloader/core/downloaders/common_downloader.py → novel_downloader-1.3.1/novel_downloader/core/downloaders/common/common_sync.py +33 -21
- novel_downloader-1.3.1/novel_downloader/core/downloaders/qidian/__init__.py +10 -0
- novel_downloader-1.2.2/novel_downloader/core/downloaders/qidian_downloader.py → novel_downloader-1.3.1/novel_downloader/core/downloaders/qidian/qidian_sync.py +79 -62
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/core/factory/__init__.py +4 -5
- novel_downloader-1.2.2/novel_downloader/core/factory/downloader_factory.py → novel_downloader-1.3.1/novel_downloader/core/factory/downloader.py +25 -26
- novel_downloader-1.2.2/novel_downloader/core/factory/parser_factory.py → novel_downloader-1.3.1/novel_downloader/core/factory/parser.py +12 -14
- novel_downloader-1.2.2/novel_downloader/core/factory/requester_factory.py → novel_downloader-1.3.1/novel_downloader/core/factory/requester.py +29 -16
- novel_downloader-1.2.2/novel_downloader/core/factory/saver_factory.py → novel_downloader-1.3.1/novel_downloader/core/factory/saver.py +4 -9
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/core/interfaces/__init__.py +8 -9
- novel_downloader-1.2.2/novel_downloader/core/interfaces/async_downloader_protocol.py → novel_downloader-1.3.1/novel_downloader/core/interfaces/async_downloader.py +4 -5
- novel_downloader-1.2.2/novel_downloader/core/interfaces/async_requester_protocol.py → novel_downloader-1.3.1/novel_downloader/core/interfaces/async_requester.py +23 -12
- novel_downloader-1.2.2/novel_downloader/core/interfaces/parser_protocol.py → novel_downloader-1.3.1/novel_downloader/core/interfaces/parser.py +11 -6
- novel_downloader-1.2.2/novel_downloader/core/interfaces/saver_protocol.py → novel_downloader-1.3.1/novel_downloader/core/interfaces/saver.py +2 -3
- novel_downloader-1.2.2/novel_downloader/core/interfaces/downloader_protocol.py → novel_downloader-1.3.1/novel_downloader/core/interfaces/sync_downloader.py +6 -7
- novel_downloader-1.2.2/novel_downloader/core/interfaces/requester_protocol.py → novel_downloader-1.3.1/novel_downloader/core/interfaces/sync_requester.py +31 -17
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/core/parsers/__init__.py +5 -4
- novel_downloader-1.2.2/novel_downloader/core/parsers/base_parser.py → novel_downloader-1.3.1/novel_downloader/core/parsers/base.py +18 -9
- novel_downloader-1.3.1/novel_downloader/core/parsers/biquge/__init__.py +10 -0
- novel_downloader-1.3.1/novel_downloader/core/parsers/biquge/main_parser.py +126 -0
- {novel_downloader-1.2.2/novel_downloader/core/parsers/common_parser → novel_downloader-1.3.1/novel_downloader/core/parsers/common}/__init__.py +2 -3
- {novel_downloader-1.2.2/novel_downloader/core/parsers/common_parser → novel_downloader-1.3.1/novel_downloader/core/parsers/common}/helper.py +13 -13
- {novel_downloader-1.2.2/novel_downloader/core/parsers/common_parser → novel_downloader-1.3.1/novel_downloader/core/parsers/common}/main_parser.py +15 -9
- {novel_downloader-1.2.2/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.1/novel_downloader/core/parsers/qidian}/__init__.py +2 -3
- {novel_downloader-1.2.2/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.1/novel_downloader/core/parsers/qidian}/browser/__init__.py +2 -3
- {novel_downloader-1.2.2/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.1/novel_downloader/core/parsers/qidian}/browser/chapter_encrypted.py +40 -48
- {novel_downloader-1.2.2/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.1/novel_downloader/core/parsers/qidian}/browser/chapter_normal.py +17 -21
- {novel_downloader-1.2.2/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.1/novel_downloader/core/parsers/qidian}/browser/chapter_router.py +10 -9
- {novel_downloader-1.2.2/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.1/novel_downloader/core/parsers/qidian}/browser/main_parser.py +14 -10
- {novel_downloader-1.2.2/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.1/novel_downloader/core/parsers/qidian}/session/__init__.py +2 -3
- {novel_downloader-1.2.2/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.1/novel_downloader/core/parsers/qidian}/session/chapter_encrypted.py +36 -44
- {novel_downloader-1.2.2/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.1/novel_downloader/core/parsers/qidian}/session/chapter_normal.py +19 -23
- {novel_downloader-1.2.2/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.1/novel_downloader/core/parsers/qidian}/session/chapter_router.py +10 -9
- {novel_downloader-1.2.2/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.1/novel_downloader/core/parsers/qidian}/session/main_parser.py +14 -10
- {novel_downloader-1.2.2/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.1/novel_downloader/core/parsers/qidian}/session/node_decryptor.py +7 -10
- {novel_downloader-1.2.2/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.1/novel_downloader/core/parsers/qidian}/shared/__init__.py +2 -3
- {novel_downloader-1.2.2/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.1/novel_downloader/core/parsers/qidian}/shared/book_info_parser.py +5 -6
- {novel_downloader-1.2.2/novel_downloader/core/parsers/qidian_parser → novel_downloader-1.3.1/novel_downloader/core/parsers/qidian}/shared/helpers.py +7 -8
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/core/requesters/__init__.py +9 -5
- novel_downloader-1.3.1/novel_downloader/core/requesters/base/__init__.py +16 -0
- novel_downloader-1.2.2/novel_downloader/core/requesters/base_async_session.py → novel_downloader-1.3.1/novel_downloader/core/requesters/base/async_session.py +177 -73
- novel_downloader-1.3.1/novel_downloader/core/requesters/base/browser.py +340 -0
- novel_downloader-1.3.1/novel_downloader/core/requesters/base/session.py +364 -0
- novel_downloader-1.3.1/novel_downloader/core/requesters/biquge/__init__.py +12 -0
- novel_downloader-1.3.1/novel_downloader/core/requesters/biquge/session.py +90 -0
- {novel_downloader-1.2.2/novel_downloader/core/requesters/common_requester → novel_downloader-1.3.1/novel_downloader/core/requesters/common}/__init__.py +4 -5
- novel_downloader-1.3.1/novel_downloader/core/requesters/common/async_session.py +96 -0
- novel_downloader-1.3.1/novel_downloader/core/requesters/common/session.py +113 -0
- novel_downloader-1.3.1/novel_downloader/core/requesters/qidian/__init__.py +21 -0
- novel_downloader-1.3.1/novel_downloader/core/requesters/qidian/broswer.py +307 -0
- novel_downloader-1.3.1/novel_downloader/core/requesters/qidian/session.py +287 -0
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/core/savers/__init__.py +5 -3
- novel_downloader-1.2.2/novel_downloader/core/savers/base_saver.py → novel_downloader-1.3.1/novel_downloader/core/savers/base.py +12 -13
- novel_downloader-1.3.1/novel_downloader/core/savers/biquge.py +25 -0
- {novel_downloader-1.2.2/novel_downloader/core/savers/common_saver → novel_downloader-1.3.1/novel_downloader/core/savers/common}/__init__.py +2 -3
- novel_downloader-1.2.2/novel_downloader/core/savers/common_saver/common_epub.py → novel_downloader-1.3.1/novel_downloader/core/savers/common/epub.py +23 -51
- {novel_downloader-1.2.2/novel_downloader/core/savers/common_saver → novel_downloader-1.3.1/novel_downloader/core/savers/common}/main_saver.py +43 -9
- novel_downloader-1.2.2/novel_downloader/core/savers/common_saver/common_txt.py → novel_downloader-1.3.1/novel_downloader/core/savers/common/txt.py +16 -46
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/core/savers/epub_utils/__init__.py +0 -1
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/core/savers/epub_utils/css_builder.py +13 -7
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/core/savers/epub_utils/initializer.py +4 -5
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/core/savers/epub_utils/text_to_html.py +2 -3
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/core/savers/epub_utils/volume_intro.py +1 -3
- novel_downloader-1.2.2/novel_downloader/core/savers/qidian_saver.py → novel_downloader-1.3.1/novel_downloader/core/savers/qidian.py +12 -6
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/locales/en.json +8 -4
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/locales/zh.json +5 -1
- novel_downloader-1.3.1/novel_downloader/resources/config/settings.toml +88 -0
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/cache.py +2 -2
- novel_downloader-1.3.1/novel_downloader/utils/chapter_storage.py +340 -0
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/constants.py +6 -4
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/crypto_utils.py +3 -3
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/file_utils/__init__.py +0 -1
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/file_utils/io.py +12 -17
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/file_utils/normalize.py +1 -3
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/file_utils/sanitize.py +2 -9
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/fontocr/__init__.py +0 -1
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/fontocr/ocr_v1.py +19 -22
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/fontocr/ocr_v2.py +147 -60
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/hash_store.py +19 -20
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/hash_utils.py +0 -1
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/i18n.py +3 -4
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/logger.py +5 -6
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/model_loader.py +5 -8
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/network.py +9 -10
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/state.py +6 -7
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/text_utils/__init__.py +0 -1
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/text_utils/chapter_formatting.py +2 -7
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/text_utils/diff_display.py +0 -1
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/text_utils/font_mapping.py +1 -4
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/text_utils/text_cleaning.py +0 -1
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/time_utils/__init__.py +0 -1
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/time_utils/datetime_utils.py +8 -10
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/time_utils/sleep_utils.py +1 -3
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader.egg-info/PKG-INFO +14 -17
- novel_downloader-1.3.1/novel_downloader.egg-info/SOURCES.txt +130 -0
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader.egg-info/requires.txt +3 -4
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/pyproject.toml +20 -14
- novel_downloader-1.2.2/novel_downloader/config/loader.py +0 -177
- novel_downloader-1.2.2/novel_downloader/core/requesters/base_browser.py +0 -214
- novel_downloader-1.2.2/novel_downloader/core/requesters/base_session.py +0 -246
- novel_downloader-1.2.2/novel_downloader/core/requesters/common_requester/common_async_session.py +0 -98
- novel_downloader-1.2.2/novel_downloader/core/requesters/common_requester/common_session.py +0 -126
- novel_downloader-1.2.2/novel_downloader/core/requesters/qidian_requester/__init__.py +0 -22
- novel_downloader-1.2.2/novel_downloader/core/requesters/qidian_requester/qidian_broswer.py +0 -396
- novel_downloader-1.2.2/novel_downloader/core/requesters/qidian_requester/qidian_session.py +0 -202
- novel_downloader-1.2.2/novel_downloader/resources/config/settings.yaml +0 -76
- novel_downloader-1.2.2/novel_downloader.egg-info/SOURCES.txt +0 -118
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/LICENSE +0 -0
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/resources/config/rules.toml +0 -0
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/resources/css_styles/main.css +0 -0
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/resources/css_styles/volume-intro.css +0 -0
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/resources/images/volume_border.png +0 -0
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/resources/js_scripts/qidian_decrypt_node.js +0 -0
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/resources/json/replace_word_map.json +0 -0
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/resources/text/blacklist.txt +0 -0
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader/utils/__init__.py +0 -0
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader.egg-info/dependency_links.txt +0 -0
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader.egg-info/entry_points.txt +0 -0
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/novel_downloader.egg-info/top_level.txt +0 -0
- {novel_downloader-1.2.2 → novel_downloader-1.3.1}/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.1
|
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.1"
|
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
|
|
@@ -69,7 +68,7 @@ def download_cli(ctx: Context, book_ids: List[str], site: str) -> None:
|
|
69
68
|
|
70
69
|
# Filter out placeholder/example IDs
|
71
70
|
invalid_ids = {"0000000000"}
|
72
|
-
valid_book_ids =
|
71
|
+
valid_book_ids = set(book_ids) - invalid_ids
|
73
72
|
|
74
73
|
if not book_ids:
|
75
74
|
click.echo(t("download_no_ids"))
|
@@ -82,21 +81,20 @@ def download_cli(ctx: Context, book_ids: List[str], site: str) -> None:
|
|
82
81
|
|
83
82
|
# Initialize the requester, parser, saver, and downloader components
|
84
83
|
if downloader_cfg.mode == "async":
|
85
|
-
import asyncio
|
86
|
-
|
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
84
|
setup_logging()
|
91
|
-
async_downloader = get_async_downloader(
|
92
|
-
requester=async_requester,
|
93
|
-
parser=async_parser,
|
94
|
-
saver=async_saver,
|
95
|
-
site=site,
|
96
|
-
config=downloader_cfg,
|
97
|
-
)
|
98
85
|
|
99
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
|
+
|
100
98
|
prepare = getattr(async_downloader, "prepare", None)
|
101
99
|
if prepare and asyncio.iscoroutinefunction(prepare):
|
102
100
|
await prepare()
|
@@ -105,9 +103,7 @@ def download_cli(ctx: Context, book_ids: List[str], site: str) -> None:
|
|
105
103
|
click.echo(t("download_downloading", book_id=book_id, site=site))
|
106
104
|
await async_downloader.download_one(book_id)
|
107
105
|
|
108
|
-
|
109
|
-
input(t("download_prompt_parse"))
|
110
|
-
await async_requester.shutdown()
|
106
|
+
await async_requester.close()
|
111
107
|
|
112
108
|
asyncio.run(async_download_all())
|
113
109
|
else:
|
@@ -127,8 +123,6 @@ def download_cli(ctx: Context, book_ids: List[str], site: str) -> None:
|
|
127
123
|
click.echo(t("download_downloading", book_id=book_id, site=site))
|
128
124
|
sync_downloader.download_one(book_id)
|
129
125
|
|
130
|
-
|
131
|
-
input(t("download_prompt_parse"))
|
132
|
-
sync_requester.shutdown()
|
126
|
+
sync_requester.close()
|
133
127
|
|
134
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,
|
@@ -31,7 +32,7 @@ class ConfigAdapter:
|
|
31
32
|
Adapter to map a raw config dict + site name into structured dataclass configs.
|
32
33
|
"""
|
33
34
|
|
34
|
-
def __init__(self, config:
|
35
|
+
def __init__(self, config: dict[str, Any], site: str):
|
35
36
|
"""
|
36
37
|
:param config: 完整加载的配置 dict
|
37
38
|
:param site: 当前站点名称 (e.g. "qidian")
|
@@ -40,33 +41,33 @@ class ConfigAdapter:
|
|
40
41
|
self._site = site
|
41
42
|
|
42
43
|
site_rules = load_site_rules() # -> Dict[str, SiteRules]
|
43
|
-
self._supported_sites = set(site_rules.keys())
|
44
|
+
self._supported_sites = set(site_rules.keys()) | SUPPORTED_SITES
|
44
45
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
50
53
|
|
51
|
-
def _get_site_cfg(self) ->
|
54
|
+
def _get_site_cfg(self, site: str | None = None) -> dict[str, Any]:
|
52
55
|
"""
|
53
|
-
|
56
|
+
获取指定站点的配置 (默认为当前适配站点)
|
54
57
|
|
55
|
-
1.
|
56
|
-
2.
|
58
|
+
1. 如果有 site-specific 配置, 优先返回它
|
59
|
+
2. 否则, 如果该站点在支持站点中, 尝试返回 'common' 配置
|
57
60
|
3. 否则返回空 dict
|
58
61
|
"""
|
62
|
+
site = site or self._site
|
59
63
|
sites_cfg = self._config.get("sites", {}) or {}
|
60
64
|
|
61
|
-
|
62
|
-
|
63
|
-
return sites_cfg[self._site] or {}
|
65
|
+
if site in sites_cfg:
|
66
|
+
return sites_cfg[site] or {}
|
64
67
|
|
65
|
-
|
66
|
-
if self._site in self._supported_sites:
|
68
|
+
if site in self._supported_sites:
|
67
69
|
return sites_cfg.get("common", {}) or {}
|
68
70
|
|
69
|
-
# 3. completely unsupported site
|
70
71
|
return {}
|
71
72
|
|
72
73
|
def get_requester_config(self) -> RequesterConfig:
|
@@ -77,10 +78,11 @@ class ConfigAdapter:
|
|
77
78
|
req = self._config.get("requests", {})
|
78
79
|
site_cfg = self._get_site_cfg()
|
79
80
|
return RequesterConfig(
|
80
|
-
wait_time=req.get("wait_time", 5),
|
81
81
|
retry_times=req.get("retry_times", 3),
|
82
|
-
|
83
|
-
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),
|
84
86
|
headless=req.get("headless", True),
|
85
87
|
user_data_folder=req.get("user_data_folder", "./user_data"),
|
86
88
|
profile_name=req.get("profile_name", "Profile_1"),
|
@@ -88,7 +90,6 @@ class ConfigAdapter:
|
|
88
90
|
disable_images=req.get("disable_images", True),
|
89
91
|
mute_audio=req.get("mute_audio", True),
|
90
92
|
mode=site_cfg.get("mode", "session"),
|
91
|
-
max_rps=site_cfg.get("max_rps", None),
|
92
93
|
)
|
93
94
|
|
94
95
|
def get_downloader_config(self) -> DownloaderConfig:
|
@@ -100,9 +101,9 @@ class ConfigAdapter:
|
|
100
101
|
debug = gen.get("debug", {})
|
101
102
|
site_cfg = self._get_site_cfg()
|
102
103
|
return DownloaderConfig(
|
103
|
-
request_interval=gen.get("request_interval", 5),
|
104
|
+
request_interval=gen.get("request_interval", 5.0),
|
104
105
|
raw_data_dir=gen.get("raw_data_dir", "./raw_data"),
|
105
|
-
cache_dir=gen.get("cache_dir", "./
|
106
|
+
cache_dir=gen.get("cache_dir", "./novel_cache"),
|
106
107
|
download_workers=gen.get("download_workers", 4),
|
107
108
|
parser_workers=gen.get("parser_workers", 4),
|
108
109
|
use_process_pool=gen.get("use_process_pool", True),
|
@@ -110,6 +111,8 @@ class ConfigAdapter:
|
|
110
111
|
login_required=site_cfg.get("login_required", False),
|
111
112
|
save_html=debug.get("save_html", False),
|
112
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),
|
113
116
|
)
|
114
117
|
|
115
118
|
def get_parser_config(self) -> ParserConfig:
|
@@ -121,7 +124,7 @@ class ConfigAdapter:
|
|
121
124
|
font_ocr = gen.get("font_ocr", {})
|
122
125
|
site_cfg = self._get_site_cfg()
|
123
126
|
return ParserConfig(
|
124
|
-
cache_dir=gen.get("cache_dir", "./
|
127
|
+
cache_dir=gen.get("cache_dir", "./novel_cache"),
|
125
128
|
decode_font=font_ocr.get("decode_font", False),
|
126
129
|
use_freq=font_ocr.get("use_freq", False),
|
127
130
|
use_ocr=font_ocr.get("use_ocr", True),
|
@@ -149,6 +152,7 @@ class ConfigAdapter:
|
|
149
152
|
return SaverConfig(
|
150
153
|
raw_data_dir=gen.get("raw_data_dir", "./raw_data"),
|
151
154
|
output_dir=gen.get("output_dir", "./downloads"),
|
155
|
+
storage_backend=gen.get("storage_backend", "json"),
|
152
156
|
clean_text=out.get("clean_text", True),
|
153
157
|
make_txt=fmt.get("make_txt", True),
|
154
158
|
make_epub=fmt.get("make_epub", False),
|
@@ -158,9 +162,10 @@ class ConfigAdapter:
|
|
158
162
|
filename_template=naming.get("filename_template", "{title}_{author}"),
|
159
163
|
include_cover=epub_opts.get("include_cover", True),
|
160
164
|
include_toc=epub_opts.get("include_toc", False),
|
165
|
+
include_picture=epub_opts.get("include_picture", False),
|
161
166
|
)
|
162
167
|
|
163
|
-
def get_book_ids(self) ->
|
168
|
+
def get_book_ids(self) -> list[str]:
|
164
169
|
"""
|
165
170
|
从 config["sites"][site]["book_ids"] 中提取目标书籍列表
|
166
171
|
"""
|