novel-downloader 2.0.1__tar.gz → 2.0.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-2.0.1/src/novel_downloader.egg-info → novel_downloader-2.0.2}/PKG-INFO +4 -1
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/README.md +3 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/pyproject.toml +1 -1
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/__init__.py +1 -1
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/cli/download.py +11 -8
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/cli/export.py +17 -17
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/cli/ui.py +28 -1
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/config/adapter.py +27 -1
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/archived/deqixs/fetcher.py +1 -28
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/downloaders/__init__.py +2 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/downloaders/base.py +34 -85
- novel_downloader-2.0.2/src/novel_downloader/core/downloaders/common.py +257 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/downloaders/qianbi.py +30 -64
- novel_downloader-2.0.2/src/novel_downloader/core/downloaders/qidian.py +309 -0
- novel_downloader-2.0.2/src/novel_downloader/core/downloaders/qqbook.py +292 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/downloaders/registry.py +2 -2
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/exporters/__init__.py +2 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/exporters/base.py +37 -59
- novel_downloader-2.0.2/src/novel_downloader/core/exporters/common.py +620 -0
- novel_downloader-2.0.2/src/novel_downloader/core/exporters/linovelib.py +47 -0
- novel_downloader-2.0.2/src/novel_downloader/core/exporters/qidian.py +61 -0
- novel_downloader-2.0.2/src/novel_downloader/core/exporters/qqbook.py +28 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/exporters/registry.py +2 -2
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/__init__.py +4 -2
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/aaatxt.py +2 -22
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/b520.py +3 -23
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/base.py +80 -105
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/biquyuedu.py +2 -22
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/dxmwx.py +10 -22
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/esjzone.py +6 -29
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/guidaye.py +2 -22
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/hetushu.py +9 -29
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/i25zw.py +2 -16
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/ixdzs8.py +2 -16
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/jpxs123.py +2 -16
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/lewenn.py +2 -22
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/linovelib.py +4 -20
- novel_downloader-2.0.1/src/novel_downloader/core/fetchers/eightnovel.py → novel_downloader-2.0.2/src/novel_downloader/core/fetchers/n8novel.py +12 -40
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/piaotia.py +2 -16
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/qbtr.py +2 -16
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/qianbi.py +1 -20
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/qidian.py +7 -33
- novel_downloader-2.0.2/src/novel_downloader/core/fetchers/qqbook.py +177 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/quanben5.py +9 -29
- novel_downloader-2.0.2/src/novel_downloader/core/fetchers/rate_limiter.py +55 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/sfacg.py +3 -16
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/shencou.py +2 -16
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/shuhaige.py +2 -22
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/tongrenquan.py +2 -22
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/ttkan.py +3 -14
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/wanbengo.py +2 -22
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/xiaoshuowu.py +2 -16
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/xiguashuwu.py +4 -20
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/xs63b.py +3 -15
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/xshbook.py +2 -22
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/yamibo.py +4 -28
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/yibige.py +13 -26
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/interfaces/exporter.py +19 -7
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/interfaces/fetcher.py +21 -47
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/__init__.py +4 -2
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/b520.py +2 -2
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/base.py +4 -39
- novel_downloader-2.0.1/src/novel_downloader/core/parsers/eightnovel.py → novel_downloader-2.0.2/src/novel_downloader/core/parsers/n8novel.py +5 -5
- novel_downloader-2.0.1/src/novel_downloader/core/parsers/qidian/main_parser.py → novel_downloader-2.0.2/src/novel_downloader/core/parsers/qidian.py +147 -266
- novel_downloader-2.0.2/src/novel_downloader/core/parsers/qqbook.py +709 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/xiguashuwu.py +3 -4
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/__init__.py +2 -2
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/b520.py +1 -1
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/base.py +2 -2
- novel_downloader-2.0.1/src/novel_downloader/core/searchers/eightnovel.py → novel_downloader-2.0.2/src/novel_downloader/core/searchers/n8novel.py +5 -5
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/models/__init__.py +2 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/models/book.py +1 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/models/config.py +12 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/resources/config/settings.toml +23 -5
- novel_downloader-2.0.2/src/novel_downloader/resources/js_scripts/expr_to_json.js +14 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/resources/js_scripts/qidian_decrypt_node.js +21 -16
- novel_downloader-2.0.2/src/novel_downloader/resources/js_scripts/qq_decrypt_node.js +92 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/constants.py +6 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/crypto_utils/aes_util.py +1 -1
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/epub/constants.py +1 -6
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/fontocr/core.py +2 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/fontocr/loader.py +10 -8
- novel_downloader-2.0.2/src/novel_downloader/utils/node_decryptor/__init__.py +13 -0
- novel_downloader-2.0.2/src/novel_downloader/utils/node_decryptor/decryptor.py +342 -0
- {novel_downloader-2.0.1/src/novel_downloader/core/parsers/qidian/utils → novel_downloader-2.0.2/src/novel_downloader/utils/node_decryptor}/decryptor_fetcher.py +5 -6
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/web/pages/download.py +1 -1
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/web/pages/search.py +1 -1
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/web/services/task_manager.py +2 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2/src/novel_downloader.egg-info}/PKG-INFO +4 -1
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader.egg-info/SOURCES.txt +15 -18
- novel_downloader-2.0.1/src/novel_downloader/core/downloaders/common.py +0 -281
- novel_downloader-2.0.1/src/novel_downloader/core/downloaders/qidian.py +0 -336
- novel_downloader-2.0.1/src/novel_downloader/core/exporters/common/__init__.py +0 -11
- novel_downloader-2.0.1/src/novel_downloader/core/exporters/common/epub.py +0 -198
- novel_downloader-2.0.1/src/novel_downloader/core/exporters/common/main_exporter.py +0 -64
- novel_downloader-2.0.1/src/novel_downloader/core/exporters/common/txt.py +0 -146
- novel_downloader-2.0.1/src/novel_downloader/core/exporters/epub_util.py +0 -215
- novel_downloader-2.0.1/src/novel_downloader/core/exporters/linovelib/__init__.py +0 -11
- novel_downloader-2.0.1/src/novel_downloader/core/exporters/linovelib/epub.py +0 -349
- novel_downloader-2.0.1/src/novel_downloader/core/exporters/linovelib/main_exporter.py +0 -66
- novel_downloader-2.0.1/src/novel_downloader/core/exporters/linovelib/txt.py +0 -139
- novel_downloader-2.0.1/src/novel_downloader/core/exporters/qidian.py +0 -32
- novel_downloader-2.0.1/src/novel_downloader/core/exporters/txt_util.py +0 -67
- novel_downloader-2.0.1/src/novel_downloader/core/fetchers/rate_limiter.py +0 -86
- novel_downloader-2.0.1/src/novel_downloader/core/parsers/qidian/__init__.py +0 -10
- novel_downloader-2.0.1/src/novel_downloader/core/parsers/qidian/utils/__init__.py +0 -11
- novel_downloader-2.0.1/src/novel_downloader/core/parsers/qidian/utils/node_decryptor.py +0 -175
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/LICENSE +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/setup.cfg +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/cli/__init__.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/cli/clean.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/cli/config.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/cli/main.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/cli/search.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/config/__init__.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/config/file_io.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/__init__.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/archived/deqixs/parser.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/archived/deqixs/searcher.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/archived/qidian/searcher.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/archived/wanbengo/searcher.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/archived/xshbook/searcher.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/downloaders/signals.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/fetchers/registry.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/interfaces/__init__.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/interfaces/downloader.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/interfaces/parser.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/interfaces/searcher.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/aaatxt.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/biquyuedu.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/dxmwx.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/esjzone.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/guidaye.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/hetushu.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/i25zw.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/ixdzs8.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/jpxs123.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/lewenn.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/linovelib.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/piaotia.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/qbtr.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/qianbi.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/quanben5.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/registry.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/sfacg.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/shencou.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/shuhaige.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/tongrenquan.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/ttkan.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/wanbengo.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/xiaoshuowu.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/xs63b.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/xshbook.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/yamibo.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/parsers/yibige.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/aaatxt.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/dxmwx.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/esjzone.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/hetushu.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/i25zw.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/ixdzs8.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/jpxs123.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/piaotia.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/qbtr.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/qianbi.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/quanben5.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/registry.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/shuhaige.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/tongrenquan.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/ttkan.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/xiaoshuowu.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/xiguashuwu.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/searchers/xs63b.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/locales/en.json +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/locales/zh.json +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/models/login.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/models/search.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/resources/css_styles/intro.css +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/resources/css_styles/main.css +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/resources/images/volume_border.png +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/resources/json/linovelib_font_map.json +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/resources/json/xiguashuwu.json +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/__init__.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/chapter_storage.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/cookies.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/crypto_utils/__init__.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/crypto_utils/aes_v1.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/crypto_utils/aes_v2.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/crypto_utils/rc4.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/epub/__init__.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/epub/builder.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/epub/documents.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/epub/models.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/epub/utils.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/file_utils/__init__.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/file_utils/io.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/file_utils/normalize.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/file_utils/sanitize.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/fontocr/__init__.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/i18n.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/logger.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/network.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/state.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/text_utils/__init__.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/text_utils/diff_display.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/text_utils/numeric_conversion.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/text_utils/text_cleaner.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/text_utils/truncate_utils.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/time_utils/__init__.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/time_utils/datetime_utils.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/utils/time_utils/sleep_utils.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/web/__init__.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/web/components/__init__.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/web/components/navigation.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/web/main.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/web/pages/__init__.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/web/pages/progress.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/web/services/__init__.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/web/services/client_dialog.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/web/services/cred_broker.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/web/services/cred_models.py +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader.egg-info/dependency_links.txt +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader.egg-info/entry_points.txt +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader.egg-info/requires.txt +0 -0
- {novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: novel-downloader
|
3
|
-
Version: 2.0.
|
3
|
+
Version: 2.0.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
|
@@ -123,6 +123,7 @@ novel-cli download 123456
|
|
123
123
|
|
124
124
|
* 支持站点见: [支持站点列表](https://github.com/saudadez21/novel-downloader/blob/main/docs/4-supported-sites.md)
|
125
125
|
* 更多示例见: [CLI 使用示例](https://github.com/saudadez21/novel-downloader/blob/main/docs/5-cli-usage-examples.md)
|
126
|
+
* 运行中可使用 `CTRL+C` 取消任务
|
126
127
|
|
127
128
|
### 3. 图形界面 (GUI / Web)
|
128
129
|
|
@@ -134,6 +135,8 @@ novel-web
|
|
134
135
|
# novel-web --listen public
|
135
136
|
```
|
136
137
|
|
138
|
+
* 运行中可使用 `CTRL+C` 停止服务
|
139
|
+
|
137
140
|
---
|
138
141
|
|
139
142
|
## 从源码安装 (开发版)
|
@@ -56,6 +56,7 @@ novel-cli download 123456
|
|
56
56
|
|
57
57
|
* 支持站点见: [支持站点列表](https://github.com/saudadez21/novel-downloader/blob/main/docs/4-supported-sites.md)
|
58
58
|
* 更多示例见: [CLI 使用示例](https://github.com/saudadez21/novel-downloader/blob/main/docs/5-cli-usage-examples.md)
|
59
|
+
* 运行中可使用 `CTRL+C` 取消任务
|
59
60
|
|
60
61
|
### 3. 图形界面 (GUI / Web)
|
61
62
|
|
@@ -67,6 +68,8 @@ novel-web
|
|
67
68
|
# novel-web --listen public
|
68
69
|
```
|
69
70
|
|
71
|
+
* 运行中可使用 `CTRL+C` 停止服务
|
72
|
+
|
70
73
|
---
|
71
74
|
|
72
75
|
## 从源码安装 (开发版)
|
@@ -178,7 +178,14 @@ async def _download(
|
|
178
178
|
|
179
179
|
for book in valid_books:
|
180
180
|
ui.info(t("download_downloading", book_id=book["book_id"], site=site))
|
181
|
-
|
181
|
+
|
182
|
+
hook, close = ui.create_progress_hook(
|
183
|
+
prefix=t("download_progress_prefix"), unit="chapters"
|
184
|
+
)
|
185
|
+
try:
|
186
|
+
await downloader.download(book, progress_hook=hook)
|
187
|
+
finally:
|
188
|
+
close()
|
182
189
|
|
183
190
|
if not no_export and exporter is not None:
|
184
191
|
await asyncio.to_thread(exporter.export, book["book_id"])
|
@@ -186,6 +193,9 @@ async def _download(
|
|
186
193
|
if downloader_cfg.login_required and fetcher.is_logged_in:
|
187
194
|
await fetcher.save_state()
|
188
195
|
|
196
|
+
if exporter is not None:
|
197
|
+
exporter.close()
|
198
|
+
|
189
199
|
|
190
200
|
async def _prompt_login_fields(
|
191
201
|
fields: list[LoginField],
|
@@ -235,10 +245,3 @@ async def _prompt_login_fields(
|
|
235
245
|
result[field.name] = value
|
236
246
|
|
237
247
|
return result
|
238
|
-
|
239
|
-
|
240
|
-
async def _print_progress(done: int, total: int) -> None:
|
241
|
-
"""Progress hook passed into the downloader."""
|
242
|
-
ui.print_progress(
|
243
|
-
done, total, prefix=t("download_progress_prefix"), unit="chapters"
|
244
|
-
)
|
@@ -55,22 +55,22 @@ def handle_export(args: Namespace) -> None:
|
|
55
55
|
adapter = ConfigAdapter(config=config_data, site=site)
|
56
56
|
exporter_cfg = adapter.get_exporter_config()
|
57
57
|
log_level = adapter.get_log_level()
|
58
|
-
exporter = get_exporter(site, exporter_cfg)
|
59
58
|
setup_logging(console_level=log_level)
|
60
59
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
60
|
+
with get_exporter(site, exporter_cfg) as exporter:
|
61
|
+
for book_id in book_ids:
|
62
|
+
ui.info(t("export_processing", book_id=book_id, format=export_format))
|
63
|
+
|
64
|
+
if export_format in {"txt", "all"}:
|
65
|
+
try:
|
66
|
+
exporter.export_as_txt(book_id)
|
67
|
+
ui.success(t("export_success_txt", book_id=book_id))
|
68
|
+
except Exception as e:
|
69
|
+
ui.error(t("export_failed_txt", book_id=book_id, err=str(e)))
|
70
|
+
|
71
|
+
if export_format in {"epub", "all"}:
|
72
|
+
try:
|
73
|
+
exporter.export_as_epub(book_id)
|
74
|
+
ui.success(t("export_success_epub", book_id=book_id))
|
75
|
+
except Exception as e:
|
76
|
+
ui.error(t("export_failed_epub", book_id=book_id, err=str(e)))
|
@@ -17,9 +17,10 @@ Public API:
|
|
17
17
|
|
18
18
|
from __future__ import annotations
|
19
19
|
|
20
|
-
from collections.abc import Iterable, Sequence
|
20
|
+
from collections.abc import Awaitable, Callable, Iterable, Sequence
|
21
21
|
|
22
22
|
from rich.console import Console
|
23
|
+
from rich.progress import Progress, TaskID
|
23
24
|
from rich.prompt import Confirm, Prompt
|
24
25
|
from rich.table import Table
|
25
26
|
|
@@ -154,3 +155,29 @@ def print_progress(
|
|
154
155
|
total = max(1, total)
|
155
156
|
pct = done / total * 100.0
|
156
157
|
_CONSOLE.print(f"[dim]{prefix}[/] {done}/{total} {unit} ({pct:.2f}%)")
|
158
|
+
|
159
|
+
|
160
|
+
def create_progress_hook(
|
161
|
+
prefix: str = "Progress",
|
162
|
+
unit: str = "item",
|
163
|
+
) -> tuple[Callable[[int, int], Awaitable[None]], Callable[[], None]]:
|
164
|
+
progress = Progress(console=_CONSOLE)
|
165
|
+
task_id: TaskID | None = None
|
166
|
+
|
167
|
+
async def hook(done: int, total: int) -> None:
|
168
|
+
nonlocal task_id
|
169
|
+
if task_id is None:
|
170
|
+
progress.start()
|
171
|
+
task_id = progress.add_task(f"[cyan]{prefix}[/]", total=max(1, total))
|
172
|
+
|
173
|
+
progress.update(
|
174
|
+
task_id,
|
175
|
+
completed=done,
|
176
|
+
total=max(1, total),
|
177
|
+
description=f"{prefix} ({done}/{total} {unit})",
|
178
|
+
)
|
179
|
+
|
180
|
+
def close() -> None:
|
181
|
+
progress.stop()
|
182
|
+
|
183
|
+
return hook, close
|
@@ -17,6 +17,7 @@ from novel_downloader.models import (
|
|
17
17
|
DownloaderConfig,
|
18
18
|
ExporterConfig,
|
19
19
|
FetcherConfig,
|
20
|
+
FontOCRConfig,
|
20
21
|
ParserConfig,
|
21
22
|
TextCleanerConfig,
|
22
23
|
)
|
@@ -97,13 +98,16 @@ class ConfigAdapter:
|
|
97
98
|
"""
|
98
99
|
g = self._gen_cfg
|
99
100
|
s = self._site_cfg
|
100
|
-
|
101
|
+
g_font = g.get("font_ocr") or {}
|
102
|
+
s_font = s.get("font_ocr") or {}
|
103
|
+
font_ocr: dict[str, Any] = {**g_font, **s_font}
|
101
104
|
return ParserConfig(
|
102
105
|
cache_dir=g.get("cache_dir", "./novel_cache"),
|
103
106
|
use_truncation=bool(s.get("use_truncation", True)),
|
104
107
|
decode_font=bool(font_ocr.get("decode_font", False)),
|
105
108
|
save_font_debug=bool(font_ocr.get("save_font_debug", False)),
|
106
109
|
batch_size=int(font_ocr.get("batch_size", 32)),
|
110
|
+
fontocr_cfg=self._dict_to_fontocr_cfg(font_ocr),
|
107
111
|
)
|
108
112
|
|
109
113
|
def get_exporter_config(self) -> ExporterConfig:
|
@@ -283,6 +287,28 @@ class ConfigAdapter:
|
|
283
287
|
out["ignore_ids"] = [str(x) for x in data["ignore_ids"]]
|
284
288
|
return out
|
285
289
|
|
290
|
+
@staticmethod
|
291
|
+
def _dict_to_fontocr_cfg(data: dict[str, Any]) -> FontOCRConfig:
|
292
|
+
"""
|
293
|
+
Convert a raw ``font_ocr`` dict into a :class:`FontOCRConfig`.
|
294
|
+
"""
|
295
|
+
if not isinstance(data, dict):
|
296
|
+
return FontOCRConfig()
|
297
|
+
|
298
|
+
ishape = data.get("input_shape")
|
299
|
+
if isinstance(ishape, list):
|
300
|
+
ishape = tuple(ishape) # [C, H, W] -> (C, H, W)
|
301
|
+
|
302
|
+
return FontOCRConfig(
|
303
|
+
model_name=data.get("model_name"),
|
304
|
+
model_dir=data.get("model_dir"),
|
305
|
+
input_shape=ishape,
|
306
|
+
device=data.get("device"),
|
307
|
+
precision=data.get("precision", "fp32"),
|
308
|
+
cpu_threads=data.get("cpu_threads", 10),
|
309
|
+
enable_hpi=data.get("enable_hpi", False),
|
310
|
+
)
|
311
|
+
|
286
312
|
@classmethod
|
287
313
|
def _dict_to_cleaner_cfg(cls, cfg: dict[str, Any]) -> TextCleanerConfig:
|
288
314
|
"""
|
@@ -8,8 +8,6 @@ novel_downloader.core.archived.deqixs.fetcher
|
|
8
8
|
from typing import Any
|
9
9
|
|
10
10
|
from novel_downloader.core.fetchers.base import BaseSession
|
11
|
-
from novel_downloader.models import FetcherConfig
|
12
|
-
from novel_downloader.utils import async_jitter_sleep
|
13
11
|
|
14
12
|
# from novel_downloader.core.fetchers.registry import register_fetcher
|
15
13
|
|
@@ -26,25 +24,11 @@ class DeqixsSession(BaseSession):
|
|
26
24
|
BOOK_INFO_URL = "https://www.deqixs.com/xiaoshuo/{book_id}/"
|
27
25
|
CHAPTER_URL = "https://www.deqixs.com/xiaoshuo/{book_id}/{chapter_id}.html"
|
28
26
|
|
29
|
-
def __init__(
|
30
|
-
self,
|
31
|
-
config: FetcherConfig,
|
32
|
-
cookies: dict[str, str] | None = None,
|
33
|
-
**kwargs: Any,
|
34
|
-
) -> None:
|
35
|
-
super().__init__("deqixs", config, cookies, **kwargs)
|
36
|
-
|
37
27
|
async def get_book_info(
|
38
28
|
self,
|
39
29
|
book_id: str,
|
40
30
|
**kwargs: Any,
|
41
31
|
) -> list[str]:
|
42
|
-
"""
|
43
|
-
Fetch the raw HTML of the book info page asynchronously.
|
44
|
-
|
45
|
-
:param book_id: The book identifier.
|
46
|
-
:return: The page content as a string.
|
47
|
-
"""
|
48
32
|
url = self.book_info_url(book_id=book_id)
|
49
33
|
return [await self.fetch(url, **kwargs)]
|
50
34
|
|
@@ -54,13 +38,6 @@ class DeqixsSession(BaseSession):
|
|
54
38
|
chapter_id: str,
|
55
39
|
**kwargs: Any,
|
56
40
|
) -> list[str]:
|
57
|
-
"""
|
58
|
-
Fetch the raw HTML of a single chapter asynchronously.
|
59
|
-
|
60
|
-
:param book_id: The book identifier.
|
61
|
-
:param chapter_id: The chapter identifier.
|
62
|
-
:return: The chapter content as a string.
|
63
|
-
"""
|
64
41
|
html_pages: list[str] = []
|
65
42
|
idx = 1
|
66
43
|
|
@@ -85,11 +62,7 @@ class DeqixsSession(BaseSession):
|
|
85
62
|
|
86
63
|
html_pages.append(html)
|
87
64
|
idx += 1
|
88
|
-
await
|
89
|
-
self.request_interval,
|
90
|
-
mul_spread=1.1,
|
91
|
-
max_sleep=self.request_interval + 2,
|
92
|
-
)
|
65
|
+
await self._sleep()
|
93
66
|
|
94
67
|
return html_pages
|
95
68
|
|
{novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/downloaders/__init__.py
RENAMED
@@ -11,9 +11,11 @@ __all__ = [
|
|
11
11
|
"CommonDownloader",
|
12
12
|
"QianbiDownloader",
|
13
13
|
"QidianDownloader",
|
14
|
+
"QqbookDownloader",
|
14
15
|
]
|
15
16
|
|
16
17
|
from .common import CommonDownloader
|
17
18
|
from .qianbi import QianbiDownloader
|
18
19
|
from .qidian import QidianDownloader
|
20
|
+
from .qqbook import QqbookDownloader
|
19
21
|
from .registry import get_downloader
|
{novel_downloader-2.0.1 → novel_downloader-2.0.2}/src/novel_downloader/core/downloaders/base.py
RENAMED
@@ -10,15 +10,11 @@ import abc
|
|
10
10
|
import asyncio
|
11
11
|
import json
|
12
12
|
import logging
|
13
|
-
from collections.abc import
|
13
|
+
from collections.abc import Awaitable, Callable, Sequence
|
14
14
|
from pathlib import Path
|
15
|
-
from typing import Any, cast
|
15
|
+
from typing import Any, ClassVar, cast
|
16
16
|
|
17
|
-
from novel_downloader.core.interfaces import
|
18
|
-
DownloaderProtocol,
|
19
|
-
FetcherProtocol,
|
20
|
-
ParserProtocol,
|
21
|
-
)
|
17
|
+
from novel_downloader.core.interfaces import FetcherProtocol, ParserProtocol
|
22
18
|
from novel_downloader.models import (
|
23
19
|
BookConfig,
|
24
20
|
BookInfoDict,
|
@@ -28,7 +24,7 @@ from novel_downloader.models import (
|
|
28
24
|
from novel_downloader.utils import time_diff
|
29
25
|
|
30
26
|
|
31
|
-
class BaseDownloader(
|
27
|
+
class BaseDownloader(abc.ABC):
|
32
28
|
"""
|
33
29
|
Abstract base class for novel downloaders.
|
34
30
|
|
@@ -39,8 +35,8 @@ class BaseDownloader(DownloaderProtocol, abc.ABC):
|
|
39
35
|
a single book, using the provided fetcher and parser components.
|
40
36
|
"""
|
41
37
|
|
42
|
-
DEFAULT_SOURCE_ID = 0
|
43
|
-
PRIORITIES_MAP = {
|
38
|
+
DEFAULT_SOURCE_ID: ClassVar[int] = 0
|
39
|
+
PRIORITIES_MAP: ClassVar[dict[int, int]] = {
|
44
40
|
DEFAULT_SOURCE_ID: 0,
|
45
41
|
}
|
46
42
|
|
@@ -61,15 +57,23 @@ class BaseDownloader(DownloaderProtocol, abc.ABC):
|
|
61
57
|
"""
|
62
58
|
self._fetcher = fetcher
|
63
59
|
self._parser = parser
|
64
|
-
self._config = config
|
65
60
|
self._site = site
|
66
61
|
|
62
|
+
self._save_html = config.save_html
|
63
|
+
self._skip_existing = config.skip_existing
|
64
|
+
self._login_required = config.login_required
|
65
|
+
self._request_interval = config.request_interval
|
66
|
+
self._retry_times = config.retry_times
|
67
|
+
self._backoff_factor = config.backoff_factor
|
68
|
+
self._workers = config.workers
|
69
|
+
self._storage_batch_size = max(1, config.storage_batch_size)
|
70
|
+
|
67
71
|
self._raw_data_dir = Path(config.raw_data_dir) / site
|
68
72
|
self._raw_data_dir.mkdir(parents=True, exist_ok=True)
|
69
73
|
self._debug_dir = Path.cwd() / "debug" / site
|
70
74
|
self._debug_dir.mkdir(parents=True, exist_ok=True)
|
71
75
|
|
72
|
-
self.logger = logging.getLogger(f"{self.__class__.__name__}")
|
76
|
+
self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
|
73
77
|
|
74
78
|
async def download_many(
|
75
79
|
self,
|
@@ -87,7 +91,7 @@ class BaseDownloader(DownloaderProtocol, abc.ABC):
|
|
87
91
|
args: completed_count, total_count.
|
88
92
|
:param cancel_event: Optional asyncio.Event to allow cancellation.
|
89
93
|
"""
|
90
|
-
if not
|
94
|
+
if not self._check_login():
|
91
95
|
book_ids = [b["book_id"] for b in books]
|
92
96
|
self.logger.warning(
|
93
97
|
"[%s] login failed, skipping download of books: %s",
|
@@ -116,8 +120,6 @@ class BaseDownloader(DownloaderProtocol, abc.ABC):
|
|
116
120
|
except Exception as e:
|
117
121
|
self._handle_download_exception(book, e)
|
118
122
|
|
119
|
-
await self._finalize()
|
120
|
-
|
121
123
|
async def download(
|
122
124
|
self,
|
123
125
|
book: BookConfig,
|
@@ -134,7 +136,7 @@ class BaseDownloader(DownloaderProtocol, abc.ABC):
|
|
134
136
|
args: completed_count, total_count.
|
135
137
|
:param cancel_event: Optional asyncio.Event to allow cancellation.
|
136
138
|
"""
|
137
|
-
if not
|
139
|
+
if not self._check_login():
|
138
140
|
self.logger.warning(
|
139
141
|
"[%s] login failed, skipping download of book: %s (%s-%s)",
|
140
142
|
self._site,
|
@@ -142,6 +144,7 @@ class BaseDownloader(DownloaderProtocol, abc.ABC):
|
|
142
144
|
book.get("start_id", "-"),
|
143
145
|
book.get("end_id", "-"),
|
144
146
|
)
|
147
|
+
return
|
145
148
|
|
146
149
|
# if already cancelled before starting
|
147
150
|
if cancel_event and cancel_event.is_set():
|
@@ -162,8 +165,6 @@ class BaseDownloader(DownloaderProtocol, abc.ABC):
|
|
162
165
|
except Exception as e:
|
163
166
|
self._handle_download_exception(book, e)
|
164
167
|
|
165
|
-
await self._finalize()
|
166
|
-
|
167
168
|
async def load_book_info(
|
168
169
|
self,
|
169
170
|
book_id: str,
|
@@ -200,23 +201,6 @@ class BaseDownloader(DownloaderProtocol, abc.ABC):
|
|
200
201
|
"""
|
201
202
|
...
|
202
203
|
|
203
|
-
async def _prepare(self) -> None:
|
204
|
-
"""
|
205
|
-
Optional hook called before downloading.
|
206
|
-
|
207
|
-
Subclasses can override this method to perform pre-download setup.
|
208
|
-
"""
|
209
|
-
return
|
210
|
-
|
211
|
-
async def _finalize(self) -> None:
|
212
|
-
"""
|
213
|
-
Optional hook called after downloading is complete.
|
214
|
-
|
215
|
-
Subclasses can override this method to perform post-download tasks,
|
216
|
-
such as saving state or releasing resources.
|
217
|
-
"""
|
218
|
-
return
|
219
|
-
|
220
204
|
def _load_book_info(
|
221
205
|
self,
|
222
206
|
book_id: str,
|
@@ -283,25 +267,22 @@ class BaseDownloader(DownloaderProtocol, abc.ABC):
|
|
283
267
|
:param filename: used as filename prefix
|
284
268
|
:param html_list: list of HTML strings to save
|
285
269
|
"""
|
286
|
-
if not self.
|
270
|
+
if not self._save_html:
|
287
271
|
return
|
288
|
-
|
289
272
|
html_dir.mkdir(parents=True, exist_ok=True)
|
290
273
|
for i, html in enumerate(html_list):
|
291
|
-
|
292
|
-
file_path.write_text(html, encoding="utf-8")
|
274
|
+
(html_dir / f"{filename}_{i}.html").write_text(html, encoding="utf-8")
|
293
275
|
|
294
276
|
@staticmethod
|
295
|
-
|
296
|
-
|
277
|
+
def _planned_chapter_ids(
|
278
|
+
vols: list[VolumeInfoDict],
|
297
279
|
start_id: str | None,
|
298
280
|
end_id: str | None,
|
299
|
-
|
300
|
-
|
301
|
-
Yield each chapterId in order, respecting start/end bounds.
|
302
|
-
"""
|
281
|
+
ignore: set[str],
|
282
|
+
) -> list[str]:
|
303
283
|
seen_start = start_id is None
|
304
|
-
|
284
|
+
out: list[str] = []
|
285
|
+
for vol in vols:
|
305
286
|
for chap in vol["chapters"]:
|
306
287
|
cid = chap.get("chapterId")
|
307
288
|
if not cid:
|
@@ -311,9 +292,11 @@ class BaseDownloader(DownloaderProtocol, abc.ABC):
|
|
311
292
|
seen_start = True
|
312
293
|
else:
|
313
294
|
continue
|
314
|
-
|
295
|
+
if cid not in ignore:
|
296
|
+
out.append(cid)
|
315
297
|
if end_id is not None and cid == end_id:
|
316
|
-
return
|
298
|
+
return out
|
299
|
+
return out
|
317
300
|
|
318
301
|
@property
|
319
302
|
def fetcher(self) -> FetcherProtocol:
|
@@ -323,38 +306,6 @@ class BaseDownloader(DownloaderProtocol, abc.ABC):
|
|
323
306
|
def parser(self) -> ParserProtocol:
|
324
307
|
return self._parser
|
325
308
|
|
326
|
-
@property
|
327
|
-
def save_html(self) -> bool:
|
328
|
-
return self._config.save_html
|
329
|
-
|
330
|
-
@property
|
331
|
-
def skip_existing(self) -> bool:
|
332
|
-
return self._config.skip_existing
|
333
|
-
|
334
|
-
@property
|
335
|
-
def login_required(self) -> bool:
|
336
|
-
return self._config.login_required
|
337
|
-
|
338
|
-
@property
|
339
|
-
def request_interval(self) -> float:
|
340
|
-
return self._config.request_interval
|
341
|
-
|
342
|
-
@property
|
343
|
-
def retry_times(self) -> int:
|
344
|
-
return self._config.retry_times
|
345
|
-
|
346
|
-
@property
|
347
|
-
def backoff_factor(self) -> float:
|
348
|
-
return self._config.backoff_factor
|
349
|
-
|
350
|
-
@property
|
351
|
-
def workers(self) -> int:
|
352
|
-
return self._config.workers
|
353
|
-
|
354
|
-
@property
|
355
|
-
def storage_batch_size(self) -> int:
|
356
|
-
return max(1, self._config.storage_batch_size)
|
357
|
-
|
358
309
|
def _handle_download_exception(self, book: BookConfig, error: Exception) -> None:
|
359
310
|
"""
|
360
311
|
Handle download errors in a consistent way.
|
@@ -373,10 +324,8 @@ class BaseDownloader(DownloaderProtocol, abc.ABC):
|
|
373
324
|
error,
|
374
325
|
)
|
375
326
|
|
376
|
-
|
327
|
+
def _check_login(self) -> bool:
|
377
328
|
"""
|
378
|
-
|
329
|
+
Check login if needed.
|
379
330
|
"""
|
380
|
-
|
381
|
-
|
382
|
-
return self.fetcher.is_logged_in if self.login_required else True
|
331
|
+
return self.fetcher.is_logged_in if self._login_required else True
|