novel-downloader 1.4.1__tar.gz → 1.4.3__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.4.1 → novel_downloader-1.4.3}/PKG-INFO +5 -2
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/README.md +3 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/__init__.py +1 -1
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/cli/download.py +70 -11
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/config/adapter.py +43 -9
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/__init__.py +19 -1
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/downloaders/base.py +26 -29
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/downloaders/biquge.py +1 -3
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/downloaders/common.py +41 -7
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/downloaders/esjzone.py +1 -3
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/downloaders/linovelib.py +1 -3
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/downloaders/qianbi.py +1 -3
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/downloaders/qidian.py +61 -37
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/downloaders/sfacg.py +1 -3
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/downloaders/yamibo.py +1 -3
- novel_downloader-1.4.3/novel_downloader/core/exporters/common/epub.py +277 -0
- novel_downloader-1.4.3/novel_downloader/core/exporters/epub_util.py +1358 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/exporters/linovelib/epub.py +147 -190
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/factory/downloader.py +3 -6
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/base/browser.py +32 -12
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/esjzone/browser.py +8 -6
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/qidian/browser.py +62 -10
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/yamibo/browser.py +3 -3
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/interfaces/downloader.py +13 -12
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/qidian/chapter_encrypted.py +11 -2
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/qidian/chapter_normal.py +8 -1
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/qidian/main_parser.py +7 -2
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/qidian/utils/__init__.py +2 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/qidian/utils/helpers.py +9 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/locales/en.json +2 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/locales/zh.json +2 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/models/__init__.py +2 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/models/config.py +9 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/resources/config/settings.toml +1 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/tui/screens/home.py +13 -6
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/constants.py +0 -29
- {novel_downloader-1.4.1/novel_downloader/utils → novel_downloader-1.4.3/novel_downloader/utils/fontocr}/model_loader.py +2 -2
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/fontocr/ocr_v1.py +2 -1
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/fontocr/ocr_v2.py +2 -1
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/text_utils/__init__.py +8 -1
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/text_utils/text_cleaning.py +51 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader.egg-info/PKG-INFO +5 -2
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader.egg-info/SOURCES.txt +2 -7
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader.egg-info/requires.txt +1 -1
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/pyproject.toml +3 -3
- novel_downloader-1.4.1/novel_downloader/core/exporters/common/epub.py +0 -192
- novel_downloader-1.4.1/novel_downloader/core/exporters/epub_utils/__init__.py +0 -40
- novel_downloader-1.4.1/novel_downloader/core/exporters/epub_utils/css_builder.py +0 -75
- novel_downloader-1.4.1/novel_downloader/core/exporters/epub_utils/image_loader.py +0 -131
- novel_downloader-1.4.1/novel_downloader/core/exporters/epub_utils/initializer.py +0 -100
- novel_downloader-1.4.1/novel_downloader/core/exporters/epub_utils/text_to_html.py +0 -178
- novel_downloader-1.4.1/novel_downloader/core/exporters/epub_utils/volume_intro.py +0 -60
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/LICENSE +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/cli/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/cli/clean.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/cli/config.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/cli/export.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/cli/main.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/config/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/config/loader.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/config/site_rules.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/downloaders/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/exporters/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/exporters/base.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/exporters/biquge.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/exporters/common/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/exporters/common/main_exporter.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/exporters/common/txt.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/exporters/esjzone.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/exporters/linovelib/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/exporters/linovelib/main_exporter.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/exporters/linovelib/txt.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/exporters/qianbi.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/exporters/qidian.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/exporters/sfacg.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/exporters/yamibo.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/factory/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/factory/exporter.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/factory/fetcher.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/factory/parser.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/base/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/base/rate_limiter.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/base/session.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/biquge/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/biquge/browser.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/biquge/session.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/common/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/common/browser.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/common/session.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/esjzone/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/esjzone/session.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/linovelib/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/linovelib/browser.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/linovelib/session.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/qianbi/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/qianbi/browser.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/qianbi/session.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/qidian/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/qidian/session.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/sfacg/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/sfacg/browser.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/sfacg/session.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/yamibo/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/fetchers/yamibo/session.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/interfaces/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/interfaces/exporter.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/interfaces/fetcher.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/interfaces/parser.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/base.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/biquge/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/biquge/main_parser.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/common/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/common/helper.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/common/main_parser.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/esjzone/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/esjzone/main_parser.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/linovelib/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/linovelib/main_parser.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/qianbi/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/qianbi/main_parser.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/qidian/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/qidian/book_info_parser.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/qidian/chapter_router.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/qidian/utils/decryptor_fetcher.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/qidian/utils/node_decryptor.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/sfacg/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/sfacg/main_parser.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/yamibo/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/parsers/yamibo/main_parser.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/models/browser.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/models/chapter.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/models/login.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/models/site_rules.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/models/tasks.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/models/types.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/resources/css_styles/main.css +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/resources/css_styles/volume-intro.css +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/resources/images/volume_border.png +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/resources/js_scripts/qidian_decrypt_node.js +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/resources/json/linovelib_font_map.json +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/resources/json/replace_word_map.json +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/resources/text/blacklist.txt +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/tui/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/tui/app.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/tui/main.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/tui/screens/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/tui/screens/login.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/tui/styles/home_layout.tcss +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/tui/widgets/richlog_handler.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/cache.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/chapter_storage.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/cookies.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/crypto_utils.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/file_utils/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/file_utils/io.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/file_utils/normalize.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/file_utils/sanitize.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/fontocr/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/hash_store.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/hash_utils.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/i18n.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/logger.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/network.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/state.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/text_utils/chapter_formatting.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/text_utils/diff_display.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/text_utils/font_mapping.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/time_utils/__init__.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/time_utils/datetime_utils.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/utils/time_utils/sleep_utils.py +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader.egg-info/dependency_links.txt +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader.egg-info/entry_points.txt +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader.egg-info/top_level.txt +0 -0
- {novel_downloader-1.4.1 → novel_downloader-1.4.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: novel-downloader
|
3
|
-
Version: 1.4.
|
3
|
+
Version: 1.4.3
|
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
|
@@ -46,7 +46,6 @@ Requires-Dist: aiohttp
|
|
46
46
|
Requires-Dist: playwright
|
47
47
|
Requires-Dist: lxml
|
48
48
|
Requires-Dist: platformdirs
|
49
|
-
Requires-Dist: ebooklib
|
50
49
|
Provides-Extra: dev
|
51
50
|
Requires-Dist: black; extra == "dev"
|
52
51
|
Requires-Dist: mypy; extra == "dev"
|
@@ -55,6 +54,7 @@ Requires-Dist: pytest; extra == "dev"
|
|
55
54
|
Requires-Dist: pytest-cov; extra == "dev"
|
56
55
|
Requires-Dist: pytest-mock; extra == "dev"
|
57
56
|
Requires-Dist: types-requests; extra == "dev"
|
57
|
+
Requires-Dist: types-lxml; extra == "dev"
|
58
58
|
Requires-Dist: types-PyYAML; extra == "dev"
|
59
59
|
Requires-Dist: pre-commit; extra == "dev"
|
60
60
|
Requires-Dist: commitizen; extra == "dev"
|
@@ -109,6 +109,8 @@ playwright install
|
|
109
109
|
pip install novel-downloader[font-recovery]
|
110
110
|
```
|
111
111
|
|
112
|
+
- 详细可见: [安装](https://github.com/BowenZ217/novel-downloader/blob/main/docs/1-installation.md)
|
113
|
+
|
112
114
|
---
|
113
115
|
|
114
116
|
### CLI 模式
|
@@ -180,6 +182,7 @@ pip install .
|
|
180
182
|
- [CLI 使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-cli-usage-examples.md)
|
181
183
|
- [复制 Cookies](https://github.com/BowenZ217/novel-downloader/blob/main/docs/copy-cookies.md)
|
182
184
|
- [文件保存](https://github.com/BowenZ217/novel-downloader/blob/main/docs/file-saving.md)
|
185
|
+
- [模块与接口文档](https://github.com/BowenZ217/novel-downloader/blob/main/docs/api/README.md)
|
183
186
|
- [TODO](https://github.com/BowenZ217/novel-downloader/blob/main/docs/todo.md)
|
184
187
|
- [开发](https://github.com/BowenZ217/novel-downloader/blob/main/docs/develop.md)
|
185
188
|
- [项目说明](#项目说明)
|
@@ -38,6 +38,8 @@ playwright install
|
|
38
38
|
pip install novel-downloader[font-recovery]
|
39
39
|
```
|
40
40
|
|
41
|
+
- 详细可见: [安装](https://github.com/BowenZ217/novel-downloader/blob/main/docs/1-installation.md)
|
42
|
+
|
41
43
|
---
|
42
44
|
|
43
45
|
### CLI 模式
|
@@ -109,6 +111,7 @@ pip install .
|
|
109
111
|
- [CLI 使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-cli-usage-examples.md)
|
110
112
|
- [复制 Cookies](https://github.com/BowenZ217/novel-downloader/blob/main/docs/copy-cookies.md)
|
111
113
|
- [文件保存](https://github.com/BowenZ217/novel-downloader/blob/main/docs/file-saving.md)
|
114
|
+
- [模块与接口文档](https://github.com/BowenZ217/novel-downloader/blob/main/docs/api/README.md)
|
112
115
|
- [TODO](https://github.com/BowenZ217/novel-downloader/blob/main/docs/todo.md)
|
113
116
|
- [开发](https://github.com/BowenZ217/novel-downloader/blob/main/docs/develop.md)
|
114
117
|
- [项目说明](#项目说明)
|
@@ -9,6 +9,7 @@ Download novels from supported sites via CLI.
|
|
9
9
|
import asyncio
|
10
10
|
import getpass
|
11
11
|
from argparse import Namespace, _SubParsersAction
|
12
|
+
from collections.abc import Iterable
|
12
13
|
from dataclasses import asdict
|
13
14
|
from pathlib import Path
|
14
15
|
from typing import Any
|
@@ -21,7 +22,7 @@ from novel_downloader.core.factory import (
|
|
21
22
|
get_parser,
|
22
23
|
)
|
23
24
|
from novel_downloader.core.interfaces import FetcherProtocol
|
24
|
-
from novel_downloader.models import LoginField
|
25
|
+
from novel_downloader.models import BookConfig, LoginField
|
25
26
|
from novel_downloader.utils.cookies import resolve_cookies
|
26
27
|
from novel_downloader.utils.i18n import t
|
27
28
|
from novel_downloader.utils.logger import setup_logging
|
@@ -36,6 +37,9 @@ def register_download_subcommand(subparsers: _SubParsersAction) -> None: # type
|
|
36
37
|
)
|
37
38
|
parser.add_argument("--config", type=str, help=t("help_config"))
|
38
39
|
|
40
|
+
parser.add_argument("--start", type=str, help=t("download_option_start"))
|
41
|
+
parser.add_argument("--end", type=str, help=t("download_option_end"))
|
42
|
+
|
39
43
|
parser.set_defaults(func=handle_download)
|
40
44
|
|
41
45
|
|
@@ -44,7 +48,11 @@ def handle_download(args: Namespace) -> None:
|
|
44
48
|
|
45
49
|
site: str = args.site
|
46
50
|
config_path: Path | None = Path(args.config) if args.config else None
|
47
|
-
book_ids: list[
|
51
|
+
book_ids: list[BookConfig] = _cli_args_to_book_configs(
|
52
|
+
args.book_ids,
|
53
|
+
args.start,
|
54
|
+
args.end,
|
55
|
+
)
|
48
56
|
|
49
57
|
print(t("download_site_info", site=site))
|
50
58
|
|
@@ -63,25 +71,73 @@ def handle_download(args: Namespace) -> None:
|
|
63
71
|
print(t("download_fail_get_ids", err=str(e)))
|
64
72
|
return
|
65
73
|
|
66
|
-
|
67
|
-
valid_book_ids = set(book_ids) - invalid_ids
|
74
|
+
valid_books = _filter_valid_book_configs(book_ids)
|
68
75
|
|
69
76
|
if not book_ids:
|
70
77
|
print(t("download_no_ids"))
|
71
78
|
return
|
72
79
|
|
73
|
-
if not
|
80
|
+
if not valid_books:
|
74
81
|
print(t("download_only_example", example="0000000000"))
|
75
82
|
print(t("download_edit_config"))
|
76
83
|
return
|
77
84
|
|
78
|
-
asyncio.run(_download(adapter, site,
|
85
|
+
asyncio.run(_download(adapter, site, valid_books))
|
86
|
+
|
87
|
+
|
88
|
+
def _cli_args_to_book_configs(
|
89
|
+
book_ids: list[str],
|
90
|
+
start_id: str | None,
|
91
|
+
end_id: str | None,
|
92
|
+
) -> list[BookConfig]:
|
93
|
+
"""
|
94
|
+
Convert CLI book_ids and optional --start/--end into a list of BookConfig.
|
95
|
+
Only the first book_id uses start/end; others are minimal.
|
96
|
+
"""
|
97
|
+
if not book_ids:
|
98
|
+
return []
|
99
|
+
|
100
|
+
result: list[BookConfig] = []
|
101
|
+
|
102
|
+
first: BookConfig = {"book_id": book_ids[0]}
|
103
|
+
if start_id:
|
104
|
+
first["start_id"] = start_id
|
105
|
+
if end_id:
|
106
|
+
first["end_id"] = end_id
|
107
|
+
result.append(first)
|
108
|
+
|
109
|
+
for book_id in book_ids[1:]:
|
110
|
+
result.append({"book_id": book_id})
|
111
|
+
|
112
|
+
return result
|
113
|
+
|
114
|
+
|
115
|
+
def _filter_valid_book_configs(
|
116
|
+
books: list[BookConfig],
|
117
|
+
invalid_ids: Iterable[str] = ("0000000000",),
|
118
|
+
) -> list[BookConfig]:
|
119
|
+
"""
|
120
|
+
Filter a list of BookConfig:
|
121
|
+
- Removes entries with invalid or placeholder book_ids
|
122
|
+
- Deduplicates based on book_id while preserving order
|
123
|
+
"""
|
124
|
+
seen = set(invalid_ids)
|
125
|
+
result: list[BookConfig] = []
|
126
|
+
|
127
|
+
for book in books:
|
128
|
+
book_id = book["book_id"]
|
129
|
+
if book_id in seen:
|
130
|
+
continue
|
131
|
+
seen.add(book_id)
|
132
|
+
result.append(book)
|
133
|
+
|
134
|
+
return result
|
79
135
|
|
80
136
|
|
81
137
|
async def _download(
|
82
138
|
adapter: ConfigAdapter,
|
83
139
|
site: str,
|
84
|
-
|
140
|
+
valid_books: list[BookConfig],
|
85
141
|
) -> None:
|
86
142
|
downloader_cfg = adapter.get_downloader_config()
|
87
143
|
fetcher_cfg = adapter.get_fetcher_config()
|
@@ -105,14 +161,17 @@ async def _download(
|
|
105
161
|
downloader = get_downloader(
|
106
162
|
fetcher=fetcher,
|
107
163
|
parser=parser,
|
108
|
-
exporter=exporter,
|
109
164
|
site=site,
|
110
165
|
config=downloader_cfg,
|
111
166
|
)
|
112
167
|
|
113
|
-
for
|
114
|
-
print(t("download_downloading", book_id=book_id, site=site))
|
115
|
-
await downloader.download(
|
168
|
+
for book in valid_books:
|
169
|
+
print(t("download_downloading", book_id=book["book_id"], site=site))
|
170
|
+
await downloader.download(
|
171
|
+
book,
|
172
|
+
progress_hook=_print_progress,
|
173
|
+
)
|
174
|
+
await asyncio.to_thread(exporter.export, book["book_id"])
|
116
175
|
|
117
176
|
if downloader_cfg.login_required and fetcher.is_logged_in:
|
118
177
|
await fetcher.save_state()
|
@@ -10,6 +10,7 @@ site name into structured dataclass-based config models.
|
|
10
10
|
from typing import Any
|
11
11
|
|
12
12
|
from novel_downloader.models import (
|
13
|
+
BookConfig,
|
13
14
|
DownloaderConfig,
|
14
15
|
ExporterConfig,
|
15
16
|
FetcherConfig,
|
@@ -126,6 +127,7 @@ class ConfigAdapter:
|
|
126
127
|
site_cfg = self._get_site_cfg()
|
127
128
|
return ParserConfig(
|
128
129
|
cache_dir=gen.get("cache_dir", "./novel_cache"),
|
130
|
+
use_truncation=site_cfg.get("use_truncation", True),
|
129
131
|
decode_font=font_ocr.get("decode_font", False),
|
130
132
|
use_freq=font_ocr.get("use_freq", False),
|
131
133
|
use_ocr=font_ocr.get("use_ocr", True),
|
@@ -169,22 +171,54 @@ class ConfigAdapter:
|
|
169
171
|
split_mode=site_cfg.get("split_mode", "book"),
|
170
172
|
)
|
171
173
|
|
172
|
-
def get_book_ids(self) -> list[
|
174
|
+
def get_book_ids(self) -> list[BookConfig]:
|
173
175
|
"""
|
174
176
|
从 config["sites"][site]["book_ids"] 中提取目标书籍列表
|
175
177
|
"""
|
176
178
|
site_cfg = self._get_site_cfg()
|
177
|
-
|
179
|
+
raw = site_cfg.get("book_ids", [])
|
178
180
|
|
179
|
-
if isinstance(
|
180
|
-
return [
|
181
|
+
if isinstance(raw, str | int):
|
182
|
+
return [{"book_id": str(raw)}]
|
181
183
|
|
182
|
-
if isinstance(
|
183
|
-
return [
|
184
|
+
if isinstance(raw, dict):
|
185
|
+
return [self._dict_to_book_config(raw)]
|
184
186
|
|
185
|
-
if not isinstance(
|
187
|
+
if not isinstance(raw, list):
|
186
188
|
raise ValueError(
|
187
|
-
f"book_ids must be a list or string, got {type(
|
189
|
+
f"book_ids must be a list or string, got {type(raw).__name__}"
|
188
190
|
)
|
189
191
|
|
190
|
-
|
192
|
+
result: list[BookConfig] = []
|
193
|
+
for item in raw:
|
194
|
+
try:
|
195
|
+
if isinstance(item, str | int):
|
196
|
+
result.append({"book_id": str(item)})
|
197
|
+
elif isinstance(item, dict):
|
198
|
+
result.append(self._dict_to_book_config(item))
|
199
|
+
except ValueError:
|
200
|
+
continue
|
201
|
+
|
202
|
+
return result
|
203
|
+
|
204
|
+
@staticmethod
|
205
|
+
def _dict_to_book_config(data: dict[str, Any]) -> BookConfig:
|
206
|
+
"""
|
207
|
+
Converts a dict to BookConfig with type normalization.
|
208
|
+
Raises ValueError if 'book_id' is missing.
|
209
|
+
"""
|
210
|
+
if "book_id" not in data:
|
211
|
+
raise ValueError("Missing required field 'book_id'")
|
212
|
+
|
213
|
+
result: BookConfig = {"book_id": str(data["book_id"])}
|
214
|
+
|
215
|
+
if "start_id" in data:
|
216
|
+
result["start_id"] = str(data["start_id"])
|
217
|
+
|
218
|
+
if "end_id" in data:
|
219
|
+
result["end_id"] = str(data["end_id"])
|
220
|
+
|
221
|
+
if "ignore_ids" in data:
|
222
|
+
result["ignore_ids"] = [str(x) for x in data["ignore_ids"]]
|
223
|
+
|
224
|
+
return result
|
@@ -14,8 +14,26 @@ downloading and processing online novel content, including:
|
|
14
14
|
- Exporter: Responsible for exporting downloaded data into various output formats.
|
15
15
|
"""
|
16
16
|
|
17
|
-
from .factory import
|
17
|
+
from .factory import (
|
18
|
+
get_downloader,
|
19
|
+
get_exporter,
|
20
|
+
get_fetcher,
|
21
|
+
get_parser,
|
22
|
+
)
|
23
|
+
from .interfaces import (
|
24
|
+
DownloaderProtocol,
|
25
|
+
ExporterProtocol,
|
26
|
+
FetcherProtocol,
|
27
|
+
ParserProtocol,
|
28
|
+
)
|
18
29
|
|
19
30
|
__all__ = [
|
31
|
+
"get_downloader",
|
32
|
+
"get_exporter",
|
33
|
+
"get_fetcher",
|
20
34
|
"get_parser",
|
35
|
+
"DownloaderProtocol",
|
36
|
+
"ExporterProtocol",
|
37
|
+
"FetcherProtocol",
|
38
|
+
"ParserProtocol",
|
21
39
|
]
|
@@ -15,11 +15,10 @@ from typing import Any
|
|
15
15
|
|
16
16
|
from novel_downloader.core.interfaces import (
|
17
17
|
DownloaderProtocol,
|
18
|
-
ExporterProtocol,
|
19
18
|
FetcherProtocol,
|
20
19
|
ParserProtocol,
|
21
20
|
)
|
22
|
-
from novel_downloader.models import DownloaderConfig
|
21
|
+
from novel_downloader.models import BookConfig, DownloaderConfig
|
23
22
|
|
24
23
|
|
25
24
|
class BaseDownloader(DownloaderProtocol, abc.ABC):
|
@@ -34,13 +33,11 @@ class BaseDownloader(DownloaderProtocol, abc.ABC):
|
|
34
33
|
self,
|
35
34
|
fetcher: FetcherProtocol,
|
36
35
|
parser: ParserProtocol,
|
37
|
-
exporter: ExporterProtocol,
|
38
36
|
config: DownloaderConfig,
|
39
37
|
site: str,
|
40
38
|
):
|
41
39
|
self._fetcher = fetcher
|
42
40
|
self._parser = parser
|
43
|
-
self._exporter = exporter
|
44
41
|
self._config = config
|
45
42
|
self._site = site
|
46
43
|
|
@@ -53,7 +50,7 @@ class BaseDownloader(DownloaderProtocol, abc.ABC):
|
|
53
50
|
|
54
51
|
async def download_many(
|
55
52
|
self,
|
56
|
-
|
53
|
+
books: list[BookConfig],
|
57
54
|
*,
|
58
55
|
progress_hook: Callable[[int, int], Awaitable[None]] | None = None,
|
59
56
|
**kwargs: Any,
|
@@ -61,33 +58,34 @@ class BaseDownloader(DownloaderProtocol, abc.ABC):
|
|
61
58
|
"""
|
62
59
|
Download multiple books with pre-download hook and error handling.
|
63
60
|
|
64
|
-
:param
|
65
|
-
:param progress_hook:
|
61
|
+
:param books: List of BookConfig entries.
|
62
|
+
:param progress_hook: Optional async callback after each chapter.
|
66
63
|
args: completed_count, total_count.
|
67
64
|
"""
|
68
65
|
if not await self._ensure_ready():
|
66
|
+
book_ids = [b["book_id"] for b in books]
|
69
67
|
self.logger.warning(
|
70
|
-
"[%s] login failed, skipping download of %s",
|
68
|
+
"[%s] login failed, skipping download of books: %s",
|
71
69
|
self._site,
|
72
|
-
book_ids,
|
70
|
+
", ".join(book_ids) or "<none>",
|
73
71
|
)
|
74
72
|
return
|
75
73
|
|
76
|
-
for
|
74
|
+
for book in books:
|
77
75
|
try:
|
78
76
|
await self._download_one(
|
79
|
-
|
77
|
+
book,
|
80
78
|
progress_hook=progress_hook,
|
81
79
|
**kwargs,
|
82
80
|
)
|
83
81
|
except Exception as e:
|
84
|
-
self._handle_download_exception(
|
82
|
+
self._handle_download_exception(book, e)
|
85
83
|
|
86
84
|
await self._finalize()
|
87
85
|
|
88
86
|
async def download(
|
89
87
|
self,
|
90
|
-
|
88
|
+
book: BookConfig,
|
91
89
|
*,
|
92
90
|
progress_hook: Callable[[int, int], Awaitable[None]] | None = None,
|
93
91
|
**kwargs: Any,
|
@@ -95,33 +93,34 @@ class BaseDownloader(DownloaderProtocol, abc.ABC):
|
|
95
93
|
"""
|
96
94
|
Download a single book with pre-download hook and error handling.
|
97
95
|
|
98
|
-
:param
|
99
|
-
:param progress_hook:
|
96
|
+
:param book: BookConfig with at least 'book_id'.
|
97
|
+
:param progress_hook: Optional async callback after each chapter.
|
100
98
|
args: completed_count, total_count.
|
101
99
|
"""
|
102
100
|
if not await self._ensure_ready():
|
103
101
|
self.logger.warning(
|
104
|
-
"[%s] login failed, skipping download of %s",
|
102
|
+
"[%s] login failed, skipping download of book: %s (%s-%s)",
|
105
103
|
self._site,
|
106
|
-
book_id,
|
104
|
+
book["book_id"],
|
105
|
+
book.get("start_id", "-"),
|
106
|
+
book.get("end_id", "-"),
|
107
107
|
)
|
108
|
-
return
|
109
108
|
|
110
109
|
try:
|
111
110
|
await self._download_one(
|
112
|
-
|
111
|
+
book,
|
113
112
|
progress_hook=progress_hook,
|
114
113
|
**kwargs,
|
115
114
|
)
|
116
115
|
except Exception as e:
|
117
|
-
self._handle_download_exception(
|
116
|
+
self._handle_download_exception(book, e)
|
118
117
|
|
119
118
|
await self._finalize()
|
120
119
|
|
121
120
|
@abc.abstractmethod
|
122
121
|
async def _download_one(
|
123
122
|
self,
|
124
|
-
|
123
|
+
book: BookConfig,
|
125
124
|
*,
|
126
125
|
progress_hook: Callable[[int, int], Awaitable[None]] | None = None,
|
127
126
|
**kwargs: Any,
|
@@ -156,10 +155,6 @@ class BaseDownloader(DownloaderProtocol, abc.ABC):
|
|
156
155
|
def parser(self) -> ParserProtocol:
|
157
156
|
return self._parser
|
158
157
|
|
159
|
-
@property
|
160
|
-
def exporter(self) -> ExporterProtocol:
|
161
|
-
return self._exporter
|
162
|
-
|
163
158
|
@property
|
164
159
|
def config(self) -> DownloaderConfig:
|
165
160
|
return self._config
|
@@ -208,19 +203,21 @@ class BaseDownloader(DownloaderProtocol, abc.ABC):
|
|
208
203
|
def download_workers(self) -> int:
|
209
204
|
return self._config.download_workers
|
210
205
|
|
211
|
-
def _handle_download_exception(self,
|
206
|
+
def _handle_download_exception(self, book: BookConfig, error: Exception) -> None:
|
212
207
|
"""
|
213
208
|
Handle download errors in a consistent way.
|
214
209
|
|
215
210
|
This method can be overridden or extended to implement retry logic, etc.
|
216
211
|
|
217
|
-
:param
|
212
|
+
:param book: The book that failed.
|
218
213
|
:param error: The exception raised during download.
|
219
214
|
"""
|
220
215
|
self.logger.warning(
|
221
|
-
"[%s] Failed to download
|
216
|
+
"[%s] Failed to download (book_id=%s, start=%s, end=%s): %s",
|
222
217
|
self.__class__.__name__,
|
223
|
-
book_id,
|
218
|
+
book.get("book_id", "<unknown>"),
|
219
|
+
book.get("start_id", "-"),
|
220
|
+
book.get("end_id", "-"),
|
224
221
|
error,
|
225
222
|
)
|
226
223
|
|
{novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/downloaders/biquge.py
RENAMED
@@ -7,7 +7,6 @@ novel_downloader.core.downloaders.biquge
|
|
7
7
|
|
8
8
|
from novel_downloader.core.downloaders.common import CommonDownloader
|
9
9
|
from novel_downloader.core.interfaces import (
|
10
|
-
ExporterProtocol,
|
11
10
|
FetcherProtocol,
|
12
11
|
ParserProtocol,
|
13
12
|
)
|
@@ -21,7 +20,6 @@ class BiqugeDownloader(CommonDownloader):
|
|
21
20
|
self,
|
22
21
|
fetcher: FetcherProtocol,
|
23
22
|
parser: ParserProtocol,
|
24
|
-
exporter: ExporterProtocol,
|
25
23
|
config: DownloaderConfig,
|
26
24
|
):
|
27
|
-
super().__init__(fetcher, parser,
|
25
|
+
super().__init__(fetcher, parser, config, "biquge")
|
{novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/downloaders/common.py
RENAMED
@@ -12,7 +12,13 @@ from contextlib import suppress
|
|
12
12
|
from typing import Any, cast
|
13
13
|
|
14
14
|
from novel_downloader.core.downloaders.base import BaseDownloader
|
15
|
-
from novel_downloader.models import
|
15
|
+
from novel_downloader.models import (
|
16
|
+
BookConfig,
|
17
|
+
ChapterDict,
|
18
|
+
CidTask,
|
19
|
+
HtmlTask,
|
20
|
+
RestoreTask,
|
21
|
+
)
|
16
22
|
from novel_downloader.utils.chapter_storage import ChapterStorage
|
17
23
|
from novel_downloader.utils.file_utils import save_as_json, save_as_txt
|
18
24
|
from novel_downloader.utils.time_utils import (
|
@@ -28,7 +34,7 @@ class CommonDownloader(BaseDownloader):
|
|
28
34
|
|
29
35
|
async def _download_one(
|
30
36
|
self,
|
31
|
-
|
37
|
+
book: BookConfig,
|
32
38
|
*,
|
33
39
|
progress_hook: Callable[[int, int], Awaitable[None]] | None = None,
|
34
40
|
**kwargs: Any,
|
@@ -36,9 +42,13 @@ class CommonDownloader(BaseDownloader):
|
|
36
42
|
"""
|
37
43
|
The full download logic for a single book.
|
38
44
|
|
39
|
-
:param
|
45
|
+
:param book: BookConfig with at least 'book_id'.
|
40
46
|
"""
|
41
47
|
TAG = "[Downloader]"
|
48
|
+
book_id = book["book_id"]
|
49
|
+
start_id = book.get("start_id")
|
50
|
+
end_id = book.get("end_id")
|
51
|
+
ignore_set = set(book.get("ignore_ids", []))
|
42
52
|
|
43
53
|
raw_base = self.raw_data_dir / book_id
|
44
54
|
cache_base = self.cache_dir / book_id
|
@@ -145,6 +155,10 @@ class CommonDownloader(BaseDownloader):
|
|
145
155
|
cid_queue.task_done()
|
146
156
|
continue
|
147
157
|
|
158
|
+
if cid in ignore_set:
|
159
|
+
cid_queue.task_done()
|
160
|
+
continue
|
161
|
+
|
148
162
|
try:
|
149
163
|
async with semaphore:
|
150
164
|
html_list = await self.fetcher.get_book_chapter(book_id, cid)
|
@@ -369,15 +383,33 @@ class CommonDownloader(BaseDownloader):
|
|
369
383
|
)
|
370
384
|
)
|
371
385
|
|
386
|
+
found_start = start_id is None
|
387
|
+
stop_early = False
|
372
388
|
last_cid: str | None = None
|
389
|
+
|
373
390
|
for vol_idx, vol in enumerate(vols):
|
374
391
|
chapters = vol.get("chapters", [])
|
375
392
|
for chap_idx, chap in enumerate(chapters):
|
393
|
+
if stop_early:
|
394
|
+
break
|
395
|
+
|
376
396
|
cid = chap.get("chapterId")
|
397
|
+
|
398
|
+
# Skip until reaching start_id
|
399
|
+
if not found_start:
|
400
|
+
if cid == start_id:
|
401
|
+
found_start = True
|
402
|
+
else:
|
403
|
+
completed_count += 1
|
404
|
+
last_cid = cid
|
405
|
+
continue
|
406
|
+
|
407
|
+
# Stop when reaching end_id
|
408
|
+
if end_id is not None and cid == end_id:
|
409
|
+
stop_early = True
|
410
|
+
|
377
411
|
if cid and normal_cs.exists(cid) and self.skip_existing:
|
378
412
|
completed_count += 1
|
379
|
-
if progress_hook:
|
380
|
-
await progress_hook(completed_count, total_chapters)
|
381
413
|
last_cid = cid
|
382
414
|
continue
|
383
415
|
|
@@ -389,8 +421,12 @@ class CommonDownloader(BaseDownloader):
|
|
389
421
|
prev_cid=last_cid,
|
390
422
|
)
|
391
423
|
)
|
424
|
+
|
392
425
|
last_cid = cid
|
393
426
|
|
427
|
+
if stop_early:
|
428
|
+
break
|
429
|
+
|
394
430
|
await restore_queue.join()
|
395
431
|
await cid_queue.join()
|
396
432
|
await html_queue.join()
|
@@ -404,8 +440,6 @@ class CommonDownloader(BaseDownloader):
|
|
404
440
|
normal_cs.close()
|
405
441
|
save_as_json(book_info, info_path)
|
406
442
|
|
407
|
-
await asyncio.to_thread(self.exporter.export, book_id)
|
408
|
-
|
409
443
|
self.logger.info(
|
410
444
|
"%s Novel '%s' download completed.",
|
411
445
|
TAG,
|
{novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/downloaders/esjzone.py
RENAMED
@@ -7,7 +7,6 @@ novel_downloader.core.downloaders.esjzone
|
|
7
7
|
|
8
8
|
from novel_downloader.core.downloaders.common import CommonDownloader
|
9
9
|
from novel_downloader.core.interfaces import (
|
10
|
-
ExporterProtocol,
|
11
10
|
FetcherProtocol,
|
12
11
|
ParserProtocol,
|
13
12
|
)
|
@@ -21,7 +20,6 @@ class EsjzoneDownloader(CommonDownloader):
|
|
21
20
|
self,
|
22
21
|
fetcher: FetcherProtocol,
|
23
22
|
parser: ParserProtocol,
|
24
|
-
exporter: ExporterProtocol,
|
25
23
|
config: DownloaderConfig,
|
26
24
|
):
|
27
|
-
super().__init__(fetcher, parser,
|
25
|
+
super().__init__(fetcher, parser, config, "esjzone")
|
{novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/downloaders/linovelib.py
RENAMED
@@ -7,7 +7,6 @@ novel_downloader.core.downloaders.linovelib
|
|
7
7
|
|
8
8
|
from novel_downloader.core.downloaders.common import CommonDownloader
|
9
9
|
from novel_downloader.core.interfaces import (
|
10
|
-
ExporterProtocol,
|
11
10
|
FetcherProtocol,
|
12
11
|
ParserProtocol,
|
13
12
|
)
|
@@ -21,7 +20,6 @@ class LinovelibDownloader(CommonDownloader):
|
|
21
20
|
self,
|
22
21
|
fetcher: FetcherProtocol,
|
23
22
|
parser: ParserProtocol,
|
24
|
-
exporter: ExporterProtocol,
|
25
23
|
config: DownloaderConfig,
|
26
24
|
):
|
27
|
-
super().__init__(fetcher, parser,
|
25
|
+
super().__init__(fetcher, parser, config, "linovelib")
|
{novel_downloader-1.4.1 → novel_downloader-1.4.3}/novel_downloader/core/downloaders/qianbi.py
RENAMED
@@ -7,7 +7,6 @@ novel_downloader.core.downloaders.qianbi
|
|
7
7
|
|
8
8
|
from novel_downloader.core.downloaders.common import CommonDownloader
|
9
9
|
from novel_downloader.core.interfaces import (
|
10
|
-
ExporterProtocol,
|
11
10
|
FetcherProtocol,
|
12
11
|
ParserProtocol,
|
13
12
|
)
|
@@ -21,7 +20,6 @@ class QianbiDownloader(CommonDownloader):
|
|
21
20
|
self,
|
22
21
|
fetcher: FetcherProtocol,
|
23
22
|
parser: ParserProtocol,
|
24
|
-
exporter: ExporterProtocol,
|
25
23
|
config: DownloaderConfig,
|
26
24
|
):
|
27
|
-
super().__init__(fetcher, parser,
|
25
|
+
super().__init__(fetcher, parser, config, "qianbi")
|