novel-downloader 1.3.1__tar.gz → 1.3.2__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.3.1 → novel_downloader-1.3.2}/PKG-INFO +25 -20
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/README.md +24 -19
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/__init__.py +1 -1
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/cli/download.py +1 -1
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/config/adapter.py +3 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/config/models.py +3 -0
- novel_downloader-1.3.2/novel_downloader/core/downloaders/__init__.py +44 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/biquge/__init__.py +2 -0
- novel_downloader-1.3.2/novel_downloader/core/downloaders/biquge/biquge_async.py +27 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/biquge/biquge_sync.py +5 -3
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/common/common_async.py +5 -3
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/common/common_sync.py +18 -10
- novel_downloader-1.3.2/novel_downloader/core/downloaders/esjzone/__init__.py +14 -0
- novel_downloader-1.3.2/novel_downloader/core/downloaders/esjzone/esjzone_async.py +27 -0
- novel_downloader-1.3.2/novel_downloader/core/downloaders/esjzone/esjzone_sync.py +27 -0
- novel_downloader-1.3.2/novel_downloader/core/downloaders/qianbi/__init__.py +14 -0
- novel_downloader-1.3.2/novel_downloader/core/downloaders/qianbi/qianbi_async.py +27 -0
- novel_downloader-1.3.2/novel_downloader/core/downloaders/qianbi/qianbi_sync.py +27 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/qidian/qidian_sync.py +9 -6
- novel_downloader-1.3.2/novel_downloader/core/downloaders/sfacg/__init__.py +14 -0
- novel_downloader-1.3.2/novel_downloader/core/downloaders/sfacg/sfacg_async.py +27 -0
- novel_downloader-1.3.2/novel_downloader/core/downloaders/sfacg/sfacg_sync.py +27 -0
- novel_downloader-1.3.2/novel_downloader/core/downloaders/yamibo/__init__.py +14 -0
- novel_downloader-1.3.2/novel_downloader/core/downloaders/yamibo/yamibo_async.py +27 -0
- novel_downloader-1.3.2/novel_downloader/core/downloaders/yamibo/yamibo_sync.py +27 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/factory/downloader.py +35 -7
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/factory/parser.py +23 -2
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/factory/requester.py +32 -7
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/factory/saver.py +14 -2
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/interfaces/async_requester.py +3 -3
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/interfaces/parser.py +7 -2
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/interfaces/sync_requester.py +3 -3
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/__init__.py +15 -5
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/base.py +7 -2
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/biquge/main_parser.py +13 -4
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/common/main_parser.py +13 -4
- novel_downloader-1.3.2/novel_downloader/core/parsers/esjzone/__init__.py +10 -0
- novel_downloader-1.3.2/novel_downloader/core/parsers/esjzone/main_parser.py +219 -0
- novel_downloader-1.3.2/novel_downloader/core/parsers/qianbi/__init__.py +10 -0
- novel_downloader-1.3.2/novel_downloader/core/parsers/qianbi/main_parser.py +142 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/browser/main_parser.py +13 -4
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/session/main_parser.py +13 -4
- novel_downloader-1.3.2/novel_downloader/core/parsers/sfacg/__init__.py +10 -0
- novel_downloader-1.3.2/novel_downloader/core/parsers/sfacg/main_parser.py +166 -0
- novel_downloader-1.3.2/novel_downloader/core/parsers/yamibo/__init__.py +10 -0
- novel_downloader-1.3.2/novel_downloader/core/parsers/yamibo/main_parser.py +194 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/__init__.py +33 -3
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/base/async_session.py +14 -10
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/base/browser.py +4 -7
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/base/session.py +25 -11
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/biquge/__init__.py +2 -0
- novel_downloader-1.3.2/novel_downloader/core/requesters/biquge/async_session.py +71 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/biquge/session.py +6 -6
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/common/async_session.py +4 -4
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/common/session.py +6 -6
- novel_downloader-1.3.2/novel_downloader/core/requesters/esjzone/__init__.py +13 -0
- novel_downloader-1.3.2/novel_downloader/core/requesters/esjzone/async_session.py +211 -0
- novel_downloader-1.3.2/novel_downloader/core/requesters/esjzone/session.py +235 -0
- novel_downloader-1.3.2/novel_downloader/core/requesters/qianbi/__init__.py +13 -0
- novel_downloader-1.3.2/novel_downloader/core/requesters/qianbi/async_session.py +96 -0
- novel_downloader-1.3.2/novel_downloader/core/requesters/qianbi/session.py +125 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/qidian/broswer.py +9 -9
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/qidian/session.py +14 -11
- novel_downloader-1.3.2/novel_downloader/core/requesters/sfacg/__init__.py +13 -0
- novel_downloader-1.3.2/novel_downloader/core/requesters/sfacg/async_session.py +204 -0
- novel_downloader-1.3.2/novel_downloader/core/requesters/sfacg/session.py +242 -0
- novel_downloader-1.3.2/novel_downloader/core/requesters/yamibo/__init__.py +13 -0
- novel_downloader-1.3.2/novel_downloader/core/requesters/yamibo/async_session.py +211 -0
- novel_downloader-1.3.2/novel_downloader/core/requesters/yamibo/session.py +237 -0
- novel_downloader-1.3.2/novel_downloader/core/savers/__init__.py +34 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/base.py +1 -0
- novel_downloader-1.3.2/novel_downloader/core/savers/esjzone.py +25 -0
- novel_downloader-1.3.2/novel_downloader/core/savers/qianbi.py +25 -0
- novel_downloader-1.3.2/novel_downloader/core/savers/sfacg.py +25 -0
- novel_downloader-1.3.2/novel_downloader/core/savers/yamibo.py +25 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/locales/en.json +1 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/locales/zh.json +1 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/resources/config/settings.toml +40 -4
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/time_utils/__init__.py +2 -1
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/time_utils/datetime_utils.py +3 -1
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/time_utils/sleep_utils.py +43 -1
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader.egg-info/PKG-INFO +25 -20
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader.egg-info/SOURCES.txt +38 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/pyproject.toml +1 -1
- novel_downloader-1.3.1/novel_downloader/core/downloaders/__init__.py +0 -22
- novel_downloader-1.3.1/novel_downloader/core/savers/__init__.py +0 -22
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/LICENSE +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/cli/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/cli/clean.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/cli/interactive.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/cli/main.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/cli/settings.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/config/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/config/loader.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/config/site_rules.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/base/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/base/base_async.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/base/base_sync.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/common/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/qidian/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/factory/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/interfaces/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/interfaces/async_downloader.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/interfaces/saver.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/interfaces/sync_downloader.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/biquge/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/common/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/common/helper.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/browser/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/browser/chapter_encrypted.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/browser/chapter_normal.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/browser/chapter_router.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/session/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/session/chapter_encrypted.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/session/chapter_normal.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/session/chapter_router.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/session/node_decryptor.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/shared/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/shared/book_info_parser.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/shared/helpers.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/base/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/common/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/qidian/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/biquge.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/common/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/common/epub.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/common/main_saver.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/common/txt.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/epub_utils/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/epub_utils/css_builder.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/epub_utils/initializer.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/epub_utils/text_to_html.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/epub_utils/volume_intro.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/qidian.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/resources/config/rules.toml +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/resources/css_styles/main.css +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/resources/css_styles/volume-intro.css +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/resources/images/volume_border.png +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/resources/js_scripts/qidian_decrypt_node.js +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/resources/json/replace_word_map.json +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/resources/text/blacklist.txt +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/cache.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/chapter_storage.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/constants.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/crypto_utils.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/file_utils/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/file_utils/io.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/file_utils/normalize.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/file_utils/sanitize.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/fontocr/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/fontocr/ocr_v1.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/fontocr/ocr_v2.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/hash_store.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/hash_utils.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/i18n.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/logger.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/model_loader.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/network.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/state.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/text_utils/__init__.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/text_utils/chapter_formatting.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/text_utils/diff_display.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/text_utils/font_mapping.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/text_utils/text_cleaning.py +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader.egg-info/dependency_links.txt +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader.egg-info/entry_points.txt +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader.egg-info/requires.txt +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader.egg-info/top_level.txt +0 -0
- {novel_downloader-1.3.1 → novel_downloader-1.3.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: novel-downloader
|
3
|
-
Version: 1.3.
|
3
|
+
Version: 1.3.2
|
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
|
@@ -80,9 +80,26 @@ Dynamic: license-file
|
|
80
80
|
- 对于起点中文网 (Qidian), 可在配置中选择:
|
81
81
|
- `mode: session` : 纯 Requests 模式
|
82
82
|
- `mode: browser` : 基于 DrissionPage 驱动 Chrome 的浏览器模式 (可处理更复杂的 JS/加密)。
|
83
|
-
-
|
83
|
+
- 若配置 `login_required: true`, 程序会在运行时自动检查登录状态, 支持自动重用历史 Cookie, 仅在首次登录或 Cookie 失效时需要人工介入:
|
84
|
+
- 若使用 `browser` 模式, 请在程序打开的浏览器窗口登录, 登录后回车继续
|
85
|
+
- 若使用 `session` 模式, 请根据程序提示粘贴浏览器中登录成功后的 Cookie ([复制 Cookies](https://github.com/BowenZ217/novel-downloader/blob/main/docs/copy-cookies.md))
|
84
86
|
|
85
|
-
|
87
|
+
## 功能特性
|
88
|
+
|
89
|
+
- 爬取起点中文网的小说章节内容 (支持免费与已订阅章节)
|
90
|
+
- 爬取部分小说网站
|
91
|
+
- 断点续爬
|
92
|
+
- 自动整合所有章节并导出为
|
93
|
+
- TXT
|
94
|
+
- EPUB
|
95
|
+
- 支持活动广告过滤:
|
96
|
+
- [x] 章节标题
|
97
|
+
- [ ] 章节正文
|
98
|
+
- [ ] 作者说
|
99
|
+
|
100
|
+
---
|
101
|
+
|
102
|
+
## 快速开始
|
86
103
|
|
87
104
|
```bash
|
88
105
|
# 克隆 + 安装
|
@@ -101,7 +118,10 @@ novel-cli settings init
|
|
101
118
|
novel-cli download 123456
|
102
119
|
```
|
103
120
|
|
104
|
-
|
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)
|
123
|
+
|
124
|
+
## 从 GitHub 安装 (开发版)
|
105
125
|
|
106
126
|
如需体验开发中的最新功能, 可通过 GitHub 安装:
|
107
127
|
|
@@ -113,22 +133,6 @@ pip install .
|
|
113
133
|
# pip install .[font-recovery]
|
114
134
|
```
|
115
135
|
|
116
|
-
更多使用方法, 查看 [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-usage-examples.md)
|
117
|
-
|
118
|
-
---
|
119
|
-
|
120
|
-
## 功能特性
|
121
|
-
|
122
|
-
- 爬取起点中文网的小说章节内容 (支持免费与已订阅章节)
|
123
|
-
- 断点续爬
|
124
|
-
- 自动整合所有章节并导出为
|
125
|
-
- TXT
|
126
|
-
- EPUB
|
127
|
-
- 支持活动广告过滤:
|
128
|
-
- [x] 章节标题
|
129
|
-
- [ ] 章节正文
|
130
|
-
- [ ] 作者说
|
131
|
-
|
132
136
|
---
|
133
137
|
|
134
138
|
## 文档结构
|
@@ -140,6 +144,7 @@ pip install .
|
|
140
144
|
- [settings.toml 配置说明](https://github.com/BowenZ217/novel-downloader/blob/main/docs/4-settings-schema.md)
|
141
145
|
- [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-usage-examples.md)
|
142
146
|
- [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-supported-sites.md)
|
147
|
+
- [复制 Cookies](https://github.com/BowenZ217/novel-downloader/blob/main/docs/copy-cookies.md)
|
143
148
|
- [文件保存](https://github.com/BowenZ217/novel-downloader/blob/main/docs/file-saving.md)
|
144
149
|
- [TODO](https://github.com/BowenZ217/novel-downloader/blob/main/docs/todo.md)
|
145
150
|
- [开发](https://github.com/BowenZ217/novel-downloader/blob/main/docs/develop.md)
|
@@ -11,9 +11,26 @@
|
|
11
11
|
- 对于起点中文网 (Qidian), 可在配置中选择:
|
12
12
|
- `mode: session` : 纯 Requests 模式
|
13
13
|
- `mode: browser` : 基于 DrissionPage 驱动 Chrome 的浏览器模式 (可处理更复杂的 JS/加密)。
|
14
|
-
-
|
14
|
+
- 若配置 `login_required: true`, 程序会在运行时自动检查登录状态, 支持自动重用历史 Cookie, 仅在首次登录或 Cookie 失效时需要人工介入:
|
15
|
+
- 若使用 `browser` 模式, 请在程序打开的浏览器窗口登录, 登录后回车继续
|
16
|
+
- 若使用 `session` 模式, 请根据程序提示粘贴浏览器中登录成功后的 Cookie ([复制 Cookies](https://github.com/BowenZ217/novel-downloader/blob/main/docs/copy-cookies.md))
|
15
17
|
|
16
|
-
|
18
|
+
## 功能特性
|
19
|
+
|
20
|
+
- 爬取起点中文网的小说章节内容 (支持免费与已订阅章节)
|
21
|
+
- 爬取部分小说网站
|
22
|
+
- 断点续爬
|
23
|
+
- 自动整合所有章节并导出为
|
24
|
+
- TXT
|
25
|
+
- EPUB
|
26
|
+
- 支持活动广告过滤:
|
27
|
+
- [x] 章节标题
|
28
|
+
- [ ] 章节正文
|
29
|
+
- [ ] 作者说
|
30
|
+
|
31
|
+
---
|
32
|
+
|
33
|
+
## 快速开始
|
17
34
|
|
18
35
|
```bash
|
19
36
|
# 克隆 + 安装
|
@@ -32,7 +49,10 @@ novel-cli settings init
|
|
32
49
|
novel-cli download 123456
|
33
50
|
```
|
34
51
|
|
35
|
-
|
52
|
+
- 详细可见: [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-supported-sites.md)
|
53
|
+
- 更多使用方法, 查看 [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-usage-examples.md)
|
54
|
+
|
55
|
+
## 从 GitHub 安装 (开发版)
|
36
56
|
|
37
57
|
如需体验开发中的最新功能, 可通过 GitHub 安装:
|
38
58
|
|
@@ -44,22 +64,6 @@ pip install .
|
|
44
64
|
# pip install .[font-recovery]
|
45
65
|
```
|
46
66
|
|
47
|
-
更多使用方法, 查看 [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-usage-examples.md)
|
48
|
-
|
49
|
-
---
|
50
|
-
|
51
|
-
## 功能特性
|
52
|
-
|
53
|
-
- 爬取起点中文网的小说章节内容 (支持免费与已订阅章节)
|
54
|
-
- 断点续爬
|
55
|
-
- 自动整合所有章节并导出为
|
56
|
-
- TXT
|
57
|
-
- EPUB
|
58
|
-
- 支持活动广告过滤:
|
59
|
-
- [x] 章节标题
|
60
|
-
- [ ] 章节正文
|
61
|
-
- [ ] 作者说
|
62
|
-
|
63
67
|
---
|
64
68
|
|
65
69
|
## 文档结构
|
@@ -71,6 +75,7 @@ pip install .
|
|
71
75
|
- [settings.toml 配置说明](https://github.com/BowenZ217/novel-downloader/blob/main/docs/4-settings-schema.md)
|
72
76
|
- [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-usage-examples.md)
|
73
77
|
- [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-supported-sites.md)
|
78
|
+
- [复制 Cookies](https://github.com/BowenZ217/novel-downloader/blob/main/docs/copy-cookies.md)
|
74
79
|
- [文件保存](https://github.com/BowenZ217/novel-downloader/blob/main/docs/file-saving.md)
|
75
80
|
- [TODO](https://github.com/BowenZ217/novel-downloader/blob/main/docs/todo.md)
|
76
81
|
- [开发](https://github.com/BowenZ217/novel-downloader/blob/main/docs/develop.md)
|
@@ -119,7 +119,7 @@ def download_cli(ctx: Context, book_ids: list[str], site: str) -> None:
|
|
119
119
|
config=downloader_cfg,
|
120
120
|
)
|
121
121
|
|
122
|
-
for book_id in
|
122
|
+
for book_id in valid_book_ids:
|
123
123
|
click.echo(t("download_downloading", book_id=book_id, site=site))
|
124
124
|
sync_downloader.download_one(book_id)
|
125
125
|
|
@@ -90,6 +90,8 @@ class ConfigAdapter:
|
|
90
90
|
disable_images=req.get("disable_images", True),
|
91
91
|
mute_audio=req.get("mute_audio", True),
|
92
92
|
mode=site_cfg.get("mode", "session"),
|
93
|
+
username=site_cfg.get("username", ""),
|
94
|
+
password=site_cfg.get("password", ""),
|
93
95
|
)
|
94
96
|
|
95
97
|
def get_downloader_config(self) -> DownloaderConfig:
|
@@ -150,6 +152,7 @@ class ConfigAdapter:
|
|
150
152
|
naming = out.get("naming", {})
|
151
153
|
epub_opts = out.get("epub", {})
|
152
154
|
return SaverConfig(
|
155
|
+
cache_dir=gen.get("cache_dir", "./novel_cache"),
|
153
156
|
raw_data_dir=gen.get("raw_data_dir", "./raw_data"),
|
154
157
|
output_dir=gen.get("output_dir", "./downloads"),
|
155
158
|
storage_backend=gen.get("storage_backend", "json"),
|
@@ -38,6 +38,8 @@ class RequesterConfig:
|
|
38
38
|
mode: ModeType = "session"
|
39
39
|
max_connections: int = 10
|
40
40
|
max_rps: float | None = None # Maximum requests per second
|
41
|
+
username: str = ""
|
42
|
+
password: str = ""
|
41
43
|
|
42
44
|
|
43
45
|
# === Downloaders ===
|
@@ -78,6 +80,7 @@ class ParserConfig:
|
|
78
80
|
# === Savers ===
|
79
81
|
@dataclass
|
80
82
|
class SaverConfig:
|
83
|
+
cache_dir: str = "./novel_cache"
|
81
84
|
raw_data_dir: str = "./raw_data"
|
82
85
|
output_dir: str = "./downloads"
|
83
86
|
storage_backend: StorageBackend = "json"
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
novel_downloader.core.downloaders
|
4
|
+
---------------------------------
|
5
|
+
|
6
|
+
This subpackage contains concrete downloader implementations for
|
7
|
+
specific novel platforms.
|
8
|
+
|
9
|
+
Each downloader is responsible for orchestrating the full lifecycle
|
10
|
+
of retrieving, parsing, and saving novel content for a given source.
|
11
|
+
|
12
|
+
Currently supported platforms:
|
13
|
+
- biquge (笔趣阁)
|
14
|
+
- esjzone (ESJ Zone)
|
15
|
+
- qianbi (铅笔小说)
|
16
|
+
- qidian (起点中文网)
|
17
|
+
- sfacg (SF轻小说)
|
18
|
+
- yamibo (百合会)
|
19
|
+
- common (通用架构)
|
20
|
+
"""
|
21
|
+
|
22
|
+
from .biquge import BiqugeAsyncDownloader, BiqugeDownloader
|
23
|
+
from .common import CommonAsyncDownloader, CommonDownloader
|
24
|
+
from .esjzone import EsjzoneAsyncDownloader, EsjzoneDownloader
|
25
|
+
from .qianbi import QianbiAsyncDownloader, QianbiDownloader
|
26
|
+
from .qidian import QidianDownloader
|
27
|
+
from .sfacg import SfacgAsyncDownloader, SfacgDownloader
|
28
|
+
from .yamibo import YamiboAsyncDownloader, YamiboDownloader
|
29
|
+
|
30
|
+
__all__ = [
|
31
|
+
"BiqugeAsyncDownloader",
|
32
|
+
"BiqugeDownloader",
|
33
|
+
"CommonAsyncDownloader",
|
34
|
+
"CommonDownloader",
|
35
|
+
"EsjzoneAsyncDownloader",
|
36
|
+
"EsjzoneDownloader",
|
37
|
+
"QianbiAsyncDownloader",
|
38
|
+
"QianbiDownloader",
|
39
|
+
"QidianDownloader",
|
40
|
+
"SfacgAsyncDownloader",
|
41
|
+
"SfacgDownloader",
|
42
|
+
"YamiboAsyncDownloader",
|
43
|
+
"YamiboDownloader",
|
44
|
+
]
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
novel_downloader.core.downloaders.biquge.biquge_async
|
4
|
+
-----------------------------------------------------
|
5
|
+
|
6
|
+
"""
|
7
|
+
|
8
|
+
from novel_downloader.config.models import DownloaderConfig
|
9
|
+
from novel_downloader.core.downloaders.common import CommonAsyncDownloader
|
10
|
+
from novel_downloader.core.interfaces import (
|
11
|
+
AsyncRequesterProtocol,
|
12
|
+
ParserProtocol,
|
13
|
+
SaverProtocol,
|
14
|
+
)
|
15
|
+
|
16
|
+
|
17
|
+
class BiqugeAsyncDownloader(CommonAsyncDownloader):
|
18
|
+
""""""
|
19
|
+
|
20
|
+
def __init__(
|
21
|
+
self,
|
22
|
+
requester: AsyncRequesterProtocol,
|
23
|
+
parser: ParserProtocol,
|
24
|
+
saver: SaverProtocol,
|
25
|
+
config: DownloaderConfig,
|
26
|
+
):
|
27
|
+
super().__init__(requester, parser, saver, config, "biquge")
|
@@ -7,9 +7,11 @@ novel_downloader.core.downloaders.biquge.biquge_sync
|
|
7
7
|
|
8
8
|
from novel_downloader.config.models import DownloaderConfig
|
9
9
|
from novel_downloader.core.downloaders.common import CommonDownloader
|
10
|
-
from novel_downloader.core.interfaces
|
11
|
-
|
12
|
-
|
10
|
+
from novel_downloader.core.interfaces import (
|
11
|
+
ParserProtocol,
|
12
|
+
SaverProtocol,
|
13
|
+
SyncRequesterProtocol,
|
14
|
+
)
|
13
15
|
|
14
16
|
|
15
17
|
class BiqugeDownloader(CommonDownloader):
|
@@ -48,7 +48,7 @@ class CommonAsyncDownloader(BaseAsyncDownloader):
|
|
48
48
|
Perform login
|
49
49
|
"""
|
50
50
|
if self.login_required and not self._is_logged_in:
|
51
|
-
success = await self.requester.login(
|
51
|
+
success = await self.requester.login()
|
52
52
|
if not success:
|
53
53
|
raise RuntimeError("Login failed")
|
54
54
|
self._is_logged_in = True
|
@@ -60,6 +60,7 @@ class CommonAsyncDownloader(BaseAsyncDownloader):
|
|
60
60
|
:param book_id: The identifier of the book to download.
|
61
61
|
"""
|
62
62
|
assert isinstance(self.requester, AsyncRequesterProtocol)
|
63
|
+
await self.prepare()
|
63
64
|
|
64
65
|
TAG = "[AsyncDownloader]"
|
65
66
|
wait_time = self.config.request_interval
|
@@ -95,7 +96,8 @@ class CommonAsyncDownloader(BaseAsyncDownloader):
|
|
95
96
|
if re_fetch:
|
96
97
|
info_html = await self.requester.get_book_info(book_id)
|
97
98
|
if self.save_html:
|
98
|
-
|
99
|
+
for i, html in enumerate(info_html):
|
100
|
+
save_as_txt(html, chapters_html_dir / f"info_{i}.html")
|
99
101
|
book_info = self.parser.parse_book_info(info_html)
|
100
102
|
if book_info.get("book_name") != "未找到书名":
|
101
103
|
save_as_json(book_info, info_path)
|
@@ -114,7 +116,7 @@ class CommonAsyncDownloader(BaseAsyncDownloader):
|
|
114
116
|
|
115
117
|
# setup queue, semaphore, executor
|
116
118
|
semaphore = asyncio.Semaphore(self.download_workers)
|
117
|
-
queue: asyncio.Queue[tuple[str, str]] = asyncio.Queue()
|
119
|
+
queue: asyncio.Queue[tuple[str, list[str]]] = asyncio.Queue()
|
118
120
|
save_queue: asyncio.Queue[ChapterDict] = asyncio.Queue()
|
119
121
|
loop = asyncio.get_running_loop()
|
120
122
|
executor = (
|
@@ -52,6 +52,17 @@ class CommonDownloader(BaseDownloader):
|
|
52
52
|
"""
|
53
53
|
super().__init__(requester, parser, saver, config, site)
|
54
54
|
self._site = site
|
55
|
+
self._is_logged_in = False
|
56
|
+
|
57
|
+
def prepare(self) -> None:
|
58
|
+
"""
|
59
|
+
Perform login
|
60
|
+
"""
|
61
|
+
if self.login_required and not self._is_logged_in:
|
62
|
+
success = self.requester.login()
|
63
|
+
if not success:
|
64
|
+
raise RuntimeError("Login failed")
|
65
|
+
self._is_logged_in = True
|
55
66
|
|
56
67
|
def download_one(self, book_id: str) -> None:
|
57
68
|
"""
|
@@ -59,6 +70,8 @@ class CommonDownloader(BaseDownloader):
|
|
59
70
|
|
60
71
|
:param book_id: The identifier of the book to download.
|
61
72
|
"""
|
73
|
+
self.prepare()
|
74
|
+
|
62
75
|
TAG = "[Downloader]"
|
63
76
|
save_html = self.config.save_html
|
64
77
|
skip_existing = self.config.skip_existing
|
@@ -96,8 +109,8 @@ class CommonDownloader(BaseDownloader):
|
|
96
109
|
except Exception:
|
97
110
|
info_html = self.requester.get_book_info(book_id)
|
98
111
|
if save_html:
|
99
|
-
|
100
|
-
|
112
|
+
for i, html in enumerate(info_html):
|
113
|
+
save_as_txt(html, chapters_html_dir / f"info_{i}.html")
|
101
114
|
book_info = self.parser.parse_book_info(info_html)
|
102
115
|
if (
|
103
116
|
book_info.get("book_name", "") != "未找到书名"
|
@@ -138,14 +151,9 @@ class CommonDownloader(BaseDownloader):
|
|
138
151
|
chap_html = self.requester.get_book_chapter(book_id, cid)
|
139
152
|
|
140
153
|
if save_html:
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
"%s Saved raw HTML for chapter %s to %s",
|
145
|
-
TAG,
|
146
|
-
cid,
|
147
|
-
html_path,
|
148
|
-
)
|
154
|
+
for i, html in enumerate(chap_html):
|
155
|
+
html_path = chapters_html_dir / f"{cid}_{i}.html"
|
156
|
+
save_as_txt(html, html_path, on_exist="skip")
|
149
157
|
|
150
158
|
chap_json = self.parser.parse_chapter(chap_html, cid)
|
151
159
|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
novel_downloader.core.downloaders.esjzone
|
4
|
+
-----------------------------------------
|
5
|
+
|
6
|
+
"""
|
7
|
+
|
8
|
+
from .esjzone_async import EsjzoneAsyncDownloader
|
9
|
+
from .esjzone_sync import EsjzoneDownloader
|
10
|
+
|
11
|
+
__all__ = [
|
12
|
+
"EsjzoneAsyncDownloader",
|
13
|
+
"EsjzoneDownloader",
|
14
|
+
]
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
novel_downloader.core.downloaders.esjzone.esjzone_async
|
4
|
+
-------------------------------------------------------
|
5
|
+
|
6
|
+
"""
|
7
|
+
|
8
|
+
from novel_downloader.config.models import DownloaderConfig
|
9
|
+
from novel_downloader.core.downloaders.common import CommonAsyncDownloader
|
10
|
+
from novel_downloader.core.interfaces import (
|
11
|
+
AsyncRequesterProtocol,
|
12
|
+
ParserProtocol,
|
13
|
+
SaverProtocol,
|
14
|
+
)
|
15
|
+
|
16
|
+
|
17
|
+
class EsjzoneAsyncDownloader(CommonAsyncDownloader):
|
18
|
+
""""""
|
19
|
+
|
20
|
+
def __init__(
|
21
|
+
self,
|
22
|
+
requester: AsyncRequesterProtocol,
|
23
|
+
parser: ParserProtocol,
|
24
|
+
saver: SaverProtocol,
|
25
|
+
config: DownloaderConfig,
|
26
|
+
):
|
27
|
+
super().__init__(requester, parser, saver, config, "esjzone")
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
novel_downloader.core.downloaders.esjzone.esjzone_sync
|
4
|
+
------------------------------------------------------
|
5
|
+
|
6
|
+
"""
|
7
|
+
|
8
|
+
from novel_downloader.config.models import DownloaderConfig
|
9
|
+
from novel_downloader.core.downloaders.common import CommonDownloader
|
10
|
+
from novel_downloader.core.interfaces import (
|
11
|
+
ParserProtocol,
|
12
|
+
SaverProtocol,
|
13
|
+
SyncRequesterProtocol,
|
14
|
+
)
|
15
|
+
|
16
|
+
|
17
|
+
class EsjzoneDownloader(CommonDownloader):
|
18
|
+
""""""
|
19
|
+
|
20
|
+
def __init__(
|
21
|
+
self,
|
22
|
+
requester: SyncRequesterProtocol,
|
23
|
+
parser: ParserProtocol,
|
24
|
+
saver: SaverProtocol,
|
25
|
+
config: DownloaderConfig,
|
26
|
+
):
|
27
|
+
super().__init__(requester, parser, saver, config, "esjzone")
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
novel_downloader.core.downloaders.qianbi
|
4
|
+
----------------------------------------
|
5
|
+
|
6
|
+
"""
|
7
|
+
|
8
|
+
from .qianbi_async import QianbiAsyncDownloader
|
9
|
+
from .qianbi_sync import QianbiDownloader
|
10
|
+
|
11
|
+
__all__ = [
|
12
|
+
"QianbiAsyncDownloader",
|
13
|
+
"QianbiDownloader",
|
14
|
+
]
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
novel_downloader.core.downloaders.qianbi.qianbi_async
|
4
|
+
-----------------------------------------------------
|
5
|
+
|
6
|
+
"""
|
7
|
+
|
8
|
+
from novel_downloader.config.models import DownloaderConfig
|
9
|
+
from novel_downloader.core.downloaders.common import CommonAsyncDownloader
|
10
|
+
from novel_downloader.core.interfaces import (
|
11
|
+
AsyncRequesterProtocol,
|
12
|
+
ParserProtocol,
|
13
|
+
SaverProtocol,
|
14
|
+
)
|
15
|
+
|
16
|
+
|
17
|
+
class QianbiAsyncDownloader(CommonAsyncDownloader):
|
18
|
+
""""""
|
19
|
+
|
20
|
+
def __init__(
|
21
|
+
self,
|
22
|
+
requester: AsyncRequesterProtocol,
|
23
|
+
parser: ParserProtocol,
|
24
|
+
saver: SaverProtocol,
|
25
|
+
config: DownloaderConfig,
|
26
|
+
):
|
27
|
+
super().__init__(requester, parser, saver, config, "qianbi")
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
novel_downloader.core.downloaders.qianbi.qianbi_sync
|
4
|
+
----------------------------------------------------
|
5
|
+
|
6
|
+
"""
|
7
|
+
|
8
|
+
from novel_downloader.config.models import DownloaderConfig
|
9
|
+
from novel_downloader.core.downloaders.common import CommonDownloader
|
10
|
+
from novel_downloader.core.interfaces import (
|
11
|
+
ParserProtocol,
|
12
|
+
SaverProtocol,
|
13
|
+
SyncRequesterProtocol,
|
14
|
+
)
|
15
|
+
|
16
|
+
|
17
|
+
class QianbiDownloader(CommonDownloader):
|
18
|
+
""""""
|
19
|
+
|
20
|
+
def __init__(
|
21
|
+
self,
|
22
|
+
requester: SyncRequesterProtocol,
|
23
|
+
parser: ParserProtocol,
|
24
|
+
saver: SaverProtocol,
|
25
|
+
config: DownloaderConfig,
|
26
|
+
):
|
27
|
+
super().__init__(requester, parser, saver, config, "qianbi")
|
@@ -100,9 +100,9 @@ class QidianDownloader(BaseDownloader):
|
|
100
100
|
raise FileNotFoundError # trigger re-fetch
|
101
101
|
except Exception:
|
102
102
|
info_html = self.requester.get_book_info(book_id)
|
103
|
-
if save_html:
|
103
|
+
if save_html and info_html:
|
104
104
|
info_html_path = chapters_html_dir / "info.html"
|
105
|
-
save_as_txt(info_html, info_html_path)
|
105
|
+
save_as_txt(info_html[0], info_html_path)
|
106
106
|
book_info = self.parser.parse_book_info(info_html)
|
107
107
|
if (
|
108
108
|
book_info.get("book_name", "") != "未找到书名"
|
@@ -140,6 +140,9 @@ class QidianDownloader(BaseDownloader):
|
|
140
140
|
chap_title = chap.get("title", "")
|
141
141
|
self.logger.info("%s Fetching chapter: %s (%s)", TAG, chap_title, cid)
|
142
142
|
chap_html = self.requester.get_book_chapter(book_id, cid)
|
143
|
+
if not chap_html:
|
144
|
+
continue
|
145
|
+
|
143
146
|
if scroll:
|
144
147
|
self.requester.scroll_page(wait_time * 2) # type: ignore[attr-defined]
|
145
148
|
else:
|
@@ -147,7 +150,7 @@ class QidianDownloader(BaseDownloader):
|
|
147
150
|
wait_time, mul_spread=1.1, max_sleep=wait_time + 2
|
148
151
|
)
|
149
152
|
|
150
|
-
is_encrypted = self.parser.is_encrypted(chap_html) # type: ignore[attr-defined]
|
153
|
+
is_encrypted = self.parser.is_encrypted(chap_html[0]) # type: ignore[attr-defined]
|
151
154
|
|
152
155
|
if is_encrypted and encrypted_cs.exists(cid) and skip_existing:
|
153
156
|
self.logger.debug(
|
@@ -157,18 +160,18 @@ class QidianDownloader(BaseDownloader):
|
|
157
160
|
)
|
158
161
|
continue
|
159
162
|
|
160
|
-
if save_html and not is_vip(chap_html):
|
163
|
+
if save_html and chap_html and not is_vip(chap_html[0]):
|
161
164
|
folder = chapters_html_dir / (
|
162
165
|
"html_encrypted" if is_encrypted else "html_plain"
|
163
166
|
)
|
164
167
|
html_path = folder / f"{cid}.html"
|
165
|
-
save_as_txt(chap_html, html_path, on_exist="skip")
|
168
|
+
save_as_txt(chap_html[0], html_path, on_exist="skip")
|
166
169
|
self.logger.debug(
|
167
170
|
"%s Saved raw HTML for chapter %s to %s", TAG, cid, html_path
|
168
171
|
)
|
169
172
|
|
170
173
|
chap_json = self.parser.parse_chapter(chap_html, cid)
|
171
|
-
if not chap_json:
|
174
|
+
if not chap_json or not chap_json.get("content"):
|
172
175
|
self.logger.warning(
|
173
176
|
"%s Parsed chapter json is empty, skipping: %s (%s)",
|
174
177
|
TAG,
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
novel_downloader.core.downloaders.sfacg
|
4
|
+
---------------------------------------
|
5
|
+
|
6
|
+
"""
|
7
|
+
|
8
|
+
from .sfacg_async import SfacgAsyncDownloader
|
9
|
+
from .sfacg_sync import SfacgDownloader
|
10
|
+
|
11
|
+
__all__ = [
|
12
|
+
"SfacgAsyncDownloader",
|
13
|
+
"SfacgDownloader",
|
14
|
+
]
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
novel_downloader.core.downloaders.sfacg.sfacg_async
|
4
|
+
---------------------------------------------------
|
5
|
+
|
6
|
+
"""
|
7
|
+
|
8
|
+
from novel_downloader.config.models import DownloaderConfig
|
9
|
+
from novel_downloader.core.downloaders.common import CommonAsyncDownloader
|
10
|
+
from novel_downloader.core.interfaces import (
|
11
|
+
AsyncRequesterProtocol,
|
12
|
+
ParserProtocol,
|
13
|
+
SaverProtocol,
|
14
|
+
)
|
15
|
+
|
16
|
+
|
17
|
+
class SfacgAsyncDownloader(CommonAsyncDownloader):
|
18
|
+
""""""
|
19
|
+
|
20
|
+
def __init__(
|
21
|
+
self,
|
22
|
+
requester: AsyncRequesterProtocol,
|
23
|
+
parser: ParserProtocol,
|
24
|
+
saver: SaverProtocol,
|
25
|
+
config: DownloaderConfig,
|
26
|
+
):
|
27
|
+
super().__init__(requester, parser, saver, config, "sfacg")
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
novel_downloader.core.downloaders.sfacg.sfacg_sync
|
4
|
+
--------------------------------------------------
|
5
|
+
|
6
|
+
"""
|
7
|
+
|
8
|
+
from novel_downloader.config.models import DownloaderConfig
|
9
|
+
from novel_downloader.core.downloaders.common import CommonDownloader
|
10
|
+
from novel_downloader.core.interfaces import (
|
11
|
+
ParserProtocol,
|
12
|
+
SaverProtocol,
|
13
|
+
SyncRequesterProtocol,
|
14
|
+
)
|
15
|
+
|
16
|
+
|
17
|
+
class SfacgDownloader(CommonDownloader):
|
18
|
+
""""""
|
19
|
+
|
20
|
+
def __init__(
|
21
|
+
self,
|
22
|
+
requester: SyncRequesterProtocol,
|
23
|
+
parser: ParserProtocol,
|
24
|
+
saver: SaverProtocol,
|
25
|
+
config: DownloaderConfig,
|
26
|
+
):
|
27
|
+
super().__init__(requester, parser, saver, config, "sfacg")
|