novel-downloader 1.3.3__py3-none-any.whl → 1.4.0__py3-none-any.whl
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/__init__.py +1 -1
- novel_downloader/cli/clean.py +97 -78
- novel_downloader/cli/config.py +177 -0
- novel_downloader/cli/download.py +132 -87
- novel_downloader/cli/export.py +77 -0
- novel_downloader/cli/main.py +21 -28
- novel_downloader/config/__init__.py +1 -25
- novel_downloader/config/adapter.py +32 -31
- novel_downloader/config/loader.py +3 -3
- novel_downloader/config/site_rules.py +1 -2
- novel_downloader/core/__init__.py +3 -6
- novel_downloader/core/downloaders/__init__.py +10 -13
- novel_downloader/core/downloaders/base.py +233 -0
- novel_downloader/core/downloaders/biquge.py +27 -0
- novel_downloader/core/downloaders/common.py +414 -0
- novel_downloader/core/downloaders/esjzone.py +27 -0
- novel_downloader/core/downloaders/linovelib.py +27 -0
- novel_downloader/core/downloaders/qianbi.py +27 -0
- novel_downloader/core/downloaders/qidian.py +352 -0
- novel_downloader/core/downloaders/sfacg.py +27 -0
- novel_downloader/core/downloaders/yamibo.py +27 -0
- novel_downloader/core/exporters/__init__.py +37 -0
- novel_downloader/core/{savers → exporters}/base.py +73 -39
- novel_downloader/core/exporters/biquge.py +25 -0
- novel_downloader/core/exporters/common/__init__.py +12 -0
- novel_downloader/core/{savers → exporters}/common/epub.py +22 -22
- novel_downloader/core/{savers/common/main_saver.py → exporters/common/main_exporter.py} +35 -40
- novel_downloader/core/{savers → exporters}/common/txt.py +20 -23
- novel_downloader/core/{savers → exporters}/epub_utils/__init__.py +8 -3
- novel_downloader/core/{savers → exporters}/epub_utils/css_builder.py +2 -2
- novel_downloader/core/{savers → exporters}/epub_utils/image_loader.py +46 -4
- novel_downloader/core/{savers → exporters}/epub_utils/initializer.py +6 -4
- novel_downloader/core/{savers → exporters}/epub_utils/text_to_html.py +3 -3
- novel_downloader/core/{savers → exporters}/epub_utils/volume_intro.py +2 -2
- novel_downloader/core/exporters/esjzone.py +25 -0
- novel_downloader/core/exporters/linovelib/__init__.py +10 -0
- novel_downloader/core/exporters/linovelib/epub.py +449 -0
- novel_downloader/core/exporters/linovelib/main_exporter.py +127 -0
- novel_downloader/core/exporters/linovelib/txt.py +129 -0
- novel_downloader/core/exporters/qianbi.py +25 -0
- novel_downloader/core/{savers → exporters}/qidian.py +8 -8
- novel_downloader/core/exporters/sfacg.py +25 -0
- novel_downloader/core/exporters/yamibo.py +25 -0
- novel_downloader/core/factory/__init__.py +5 -17
- novel_downloader/core/factory/downloader.py +24 -126
- novel_downloader/core/factory/exporter.py +58 -0
- novel_downloader/core/factory/fetcher.py +96 -0
- novel_downloader/core/factory/parser.py +17 -12
- novel_downloader/core/{requesters → fetchers}/__init__.py +22 -15
- novel_downloader/core/{requesters → fetchers}/base/__init__.py +2 -4
- novel_downloader/core/fetchers/base/browser.py +383 -0
- novel_downloader/core/fetchers/base/rate_limiter.py +86 -0
- novel_downloader/core/fetchers/base/session.py +419 -0
- novel_downloader/core/fetchers/biquge/__init__.py +14 -0
- novel_downloader/core/{requesters/biquge/async_session.py → fetchers/biquge/browser.py} +18 -6
- novel_downloader/core/{requesters → fetchers}/biquge/session.py +23 -30
- novel_downloader/core/fetchers/common/__init__.py +14 -0
- novel_downloader/core/fetchers/common/browser.py +79 -0
- novel_downloader/core/{requesters/common/async_session.py → fetchers/common/session.py} +8 -25
- novel_downloader/core/fetchers/esjzone/__init__.py +14 -0
- novel_downloader/core/fetchers/esjzone/browser.py +202 -0
- novel_downloader/core/{requesters/esjzone/async_session.py → fetchers/esjzone/session.py} +62 -42
- novel_downloader/core/fetchers/linovelib/__init__.py +14 -0
- novel_downloader/core/fetchers/linovelib/browser.py +178 -0
- novel_downloader/core/fetchers/linovelib/session.py +178 -0
- novel_downloader/core/fetchers/qianbi/__init__.py +14 -0
- novel_downloader/core/{requesters/qianbi/session.py → fetchers/qianbi/browser.py} +30 -48
- novel_downloader/core/{requesters/qianbi/async_session.py → fetchers/qianbi/session.py} +18 -6
- novel_downloader/core/fetchers/qidian/__init__.py +14 -0
- novel_downloader/core/fetchers/qidian/browser.py +266 -0
- novel_downloader/core/fetchers/qidian/session.py +326 -0
- novel_downloader/core/fetchers/sfacg/__init__.py +14 -0
- novel_downloader/core/fetchers/sfacg/browser.py +189 -0
- novel_downloader/core/{requesters/sfacg/async_session.py → fetchers/sfacg/session.py} +43 -73
- novel_downloader/core/fetchers/yamibo/__init__.py +14 -0
- novel_downloader/core/fetchers/yamibo/browser.py +229 -0
- novel_downloader/core/{requesters/yamibo/async_session.py → fetchers/yamibo/session.py} +62 -44
- novel_downloader/core/interfaces/__init__.py +8 -12
- novel_downloader/core/interfaces/downloader.py +54 -0
- novel_downloader/core/interfaces/{saver.py → exporter.py} +12 -12
- novel_downloader/core/interfaces/fetcher.py +162 -0
- novel_downloader/core/interfaces/parser.py +6 -7
- novel_downloader/core/parsers/__init__.py +5 -6
- novel_downloader/core/parsers/base.py +9 -13
- novel_downloader/core/parsers/biquge/main_parser.py +12 -13
- novel_downloader/core/parsers/common/helper.py +3 -3
- novel_downloader/core/parsers/common/main_parser.py +39 -34
- novel_downloader/core/parsers/esjzone/main_parser.py +20 -14
- novel_downloader/core/parsers/linovelib/__init__.py +10 -0
- novel_downloader/core/parsers/linovelib/main_parser.py +210 -0
- novel_downloader/core/parsers/qianbi/main_parser.py +21 -15
- novel_downloader/core/parsers/qidian/__init__.py +2 -11
- novel_downloader/core/parsers/qidian/book_info_parser.py +113 -0
- novel_downloader/core/parsers/qidian/{browser/chapter_encrypted.py → chapter_encrypted.py} +162 -135
- novel_downloader/core/parsers/qidian/chapter_normal.py +150 -0
- novel_downloader/core/parsers/qidian/{session/chapter_router.py → chapter_router.py} +15 -15
- novel_downloader/core/parsers/qidian/{browser/main_parser.py → main_parser.py} +49 -40
- novel_downloader/core/parsers/qidian/utils/__init__.py +27 -0
- novel_downloader/core/parsers/qidian/utils/decryptor_fetcher.py +145 -0
- novel_downloader/core/parsers/qidian/{shared → utils}/helpers.py +41 -68
- novel_downloader/core/parsers/qidian/{session → utils}/node_decryptor.py +64 -50
- novel_downloader/core/parsers/sfacg/main_parser.py +12 -12
- novel_downloader/core/parsers/yamibo/main_parser.py +10 -10
- novel_downloader/locales/en.json +18 -2
- novel_downloader/locales/zh.json +18 -2
- novel_downloader/models/__init__.py +64 -0
- novel_downloader/models/browser.py +21 -0
- novel_downloader/models/chapter.py +25 -0
- novel_downloader/models/config.py +100 -0
- novel_downloader/models/login.py +20 -0
- novel_downloader/models/site_rules.py +99 -0
- novel_downloader/models/tasks.py +33 -0
- novel_downloader/models/types.py +15 -0
- novel_downloader/resources/config/settings.toml +31 -25
- novel_downloader/resources/json/linovelib_font_map.json +3573 -0
- novel_downloader/tui/__init__.py +7 -0
- novel_downloader/tui/app.py +32 -0
- novel_downloader/tui/main.py +17 -0
- novel_downloader/tui/screens/__init__.py +14 -0
- novel_downloader/tui/screens/home.py +191 -0
- novel_downloader/tui/screens/login.py +74 -0
- novel_downloader/tui/styles/home_layout.tcss +79 -0
- novel_downloader/tui/widgets/richlog_handler.py +24 -0
- novel_downloader/utils/__init__.py +6 -0
- novel_downloader/utils/chapter_storage.py +25 -38
- novel_downloader/utils/constants.py +11 -5
- novel_downloader/utils/cookies.py +66 -0
- novel_downloader/utils/crypto_utils.py +1 -74
- novel_downloader/utils/fontocr/ocr_v1.py +2 -1
- novel_downloader/utils/fontocr/ocr_v2.py +2 -2
- novel_downloader/utils/hash_store.py +10 -18
- novel_downloader/utils/hash_utils.py +3 -2
- novel_downloader/utils/logger.py +2 -3
- novel_downloader/utils/network.py +2 -1
- novel_downloader/utils/text_utils/chapter_formatting.py +6 -1
- novel_downloader/utils/text_utils/font_mapping.py +1 -1
- novel_downloader/utils/text_utils/text_cleaning.py +1 -1
- novel_downloader/utils/time_utils/datetime_utils.py +3 -3
- novel_downloader/utils/time_utils/sleep_utils.py +1 -1
- {novel_downloader-1.3.3.dist-info → novel_downloader-1.4.0.dist-info}/METADATA +69 -35
- novel_downloader-1.4.0.dist-info/RECORD +170 -0
- {novel_downloader-1.3.3.dist-info → novel_downloader-1.4.0.dist-info}/WHEEL +1 -1
- {novel_downloader-1.3.3.dist-info → novel_downloader-1.4.0.dist-info}/entry_points.txt +1 -0
- novel_downloader/cli/interactive.py +0 -66
- novel_downloader/cli/settings.py +0 -177
- novel_downloader/config/models.py +0 -187
- novel_downloader/core/downloaders/base/__init__.py +0 -14
- novel_downloader/core/downloaders/base/base_async.py +0 -153
- novel_downloader/core/downloaders/base/base_sync.py +0 -208
- novel_downloader/core/downloaders/biquge/__init__.py +0 -14
- novel_downloader/core/downloaders/biquge/biquge_async.py +0 -27
- novel_downloader/core/downloaders/biquge/biquge_sync.py +0 -27
- novel_downloader/core/downloaders/common/__init__.py +0 -14
- novel_downloader/core/downloaders/common/common_async.py +0 -210
- novel_downloader/core/downloaders/common/common_sync.py +0 -202
- novel_downloader/core/downloaders/esjzone/__init__.py +0 -14
- novel_downloader/core/downloaders/esjzone/esjzone_async.py +0 -27
- novel_downloader/core/downloaders/esjzone/esjzone_sync.py +0 -27
- novel_downloader/core/downloaders/qianbi/__init__.py +0 -14
- novel_downloader/core/downloaders/qianbi/qianbi_async.py +0 -27
- novel_downloader/core/downloaders/qianbi/qianbi_sync.py +0 -27
- novel_downloader/core/downloaders/qidian/__init__.py +0 -10
- novel_downloader/core/downloaders/qidian/qidian_sync.py +0 -219
- novel_downloader/core/downloaders/sfacg/__init__.py +0 -14
- novel_downloader/core/downloaders/sfacg/sfacg_async.py +0 -27
- novel_downloader/core/downloaders/sfacg/sfacg_sync.py +0 -27
- novel_downloader/core/downloaders/yamibo/__init__.py +0 -14
- novel_downloader/core/downloaders/yamibo/yamibo_async.py +0 -27
- novel_downloader/core/downloaders/yamibo/yamibo_sync.py +0 -27
- novel_downloader/core/factory/requester.py +0 -144
- novel_downloader/core/factory/saver.py +0 -56
- novel_downloader/core/interfaces/async_downloader.py +0 -36
- novel_downloader/core/interfaces/async_requester.py +0 -84
- novel_downloader/core/interfaces/sync_downloader.py +0 -36
- novel_downloader/core/interfaces/sync_requester.py +0 -82
- novel_downloader/core/parsers/qidian/browser/__init__.py +0 -12
- novel_downloader/core/parsers/qidian/browser/chapter_normal.py +0 -93
- novel_downloader/core/parsers/qidian/browser/chapter_router.py +0 -71
- novel_downloader/core/parsers/qidian/session/__init__.py +0 -12
- novel_downloader/core/parsers/qidian/session/chapter_encrypted.py +0 -443
- novel_downloader/core/parsers/qidian/session/chapter_normal.py +0 -115
- novel_downloader/core/parsers/qidian/session/main_parser.py +0 -128
- novel_downloader/core/parsers/qidian/shared/__init__.py +0 -37
- novel_downloader/core/parsers/qidian/shared/book_info_parser.py +0 -150
- novel_downloader/core/requesters/base/async_session.py +0 -410
- novel_downloader/core/requesters/base/browser.py +0 -337
- novel_downloader/core/requesters/base/session.py +0 -378
- novel_downloader/core/requesters/biquge/__init__.py +0 -14
- novel_downloader/core/requesters/common/__init__.py +0 -17
- novel_downloader/core/requesters/common/session.py +0 -113
- novel_downloader/core/requesters/esjzone/__init__.py +0 -13
- novel_downloader/core/requesters/esjzone/session.py +0 -235
- novel_downloader/core/requesters/qianbi/__init__.py +0 -13
- novel_downloader/core/requesters/qidian/__init__.py +0 -21
- novel_downloader/core/requesters/qidian/broswer.py +0 -307
- novel_downloader/core/requesters/qidian/session.py +0 -290
- novel_downloader/core/requesters/sfacg/__init__.py +0 -13
- novel_downloader/core/requesters/sfacg/session.py +0 -242
- novel_downloader/core/requesters/yamibo/__init__.py +0 -13
- novel_downloader/core/requesters/yamibo/session.py +0 -237
- novel_downloader/core/savers/__init__.py +0 -34
- novel_downloader/core/savers/biquge.py +0 -25
- novel_downloader/core/savers/common/__init__.py +0 -12
- novel_downloader/core/savers/esjzone.py +0 -25
- novel_downloader/core/savers/qianbi.py +0 -25
- novel_downloader/core/savers/sfacg.py +0 -25
- novel_downloader/core/savers/yamibo.py +0 -25
- novel_downloader/resources/config/rules.toml +0 -196
- novel_downloader-1.3.3.dist-info/RECORD +0 -166
- {novel_downloader-1.3.3.dist-info → novel_downloader-1.4.0.dist-info}/licenses/LICENSE +0 -0
- {novel_downloader-1.3.3.dist-info → novel_downloader-1.4.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,170 @@
|
|
1
|
+
novel_downloader/__init__.py,sha256=TLa2eWlJhpY9lBjsolzhKpaZ7PWwhQj34Tj7RUSLlOY,218
|
2
|
+
novel_downloader/cli/__init__.py,sha256=-2HAut_U1e67MZGdvbpEJ1n5J-bRchzto6L4c-nWeXY,174
|
3
|
+
novel_downloader/cli/clean.py,sha256=hOk8SJQwBCw2oOObTdEI79wpnmZ25uB1s9LQK1-4LNU,4487
|
4
|
+
novel_downloader/cli/config.py,sha256=C6QLfegZLp4legmu8KenqyYKNdrk47bH0z86ujLP0pY,6509
|
5
|
+
novel_downloader/cli/download.py,sha256=HKCxufqnj4vDzCdM7soH_Y_eiVNRGpV17Mz08ADYwqQ,5279
|
6
|
+
novel_downloader/cli/export.py,sha256=x9uvyLuvkuaDZGoH212aHZ7XyPT9b2S78AmTN6rkAu4,2283
|
7
|
+
novel_downloader/cli/main.py,sha256=9J8KMuYwL01X6chIaXpQNeS5d3pHnwB9vA9XjKd8RrM,919
|
8
|
+
novel_downloader/config/__init__.py,sha256=2mnf33MQOUnLGCnL1NtNV_rHBejNxBNIbobIGN0tw4E,666
|
9
|
+
novel_downloader/config/adapter.py,sha256=UYtDlpKFcKmvBBN9ZOI3W7u0a72Fhrpk-DkZmKoKpqk,7281
|
10
|
+
novel_downloader/config/loader.py,sha256=jo_1rr3UKZRAFFYgO-oHpYLRhF431chmfx4fLGh0MKw,5743
|
11
|
+
novel_downloader/config/site_rules.py,sha256=CJksBSvVAC4sR6cEruf4pM1Jv0zTJb1lcHq0Yn6LPFM,2979
|
12
|
+
novel_downloader/core/__init__.py,sha256=zzrXjQfBUhfLmBD_95oHTjtTsR81NSRtHcxpYBxQlZ4,654
|
13
|
+
novel_downloader/core/downloaders/__init__.py,sha256=AK5zeetVXOn_irgHp-NORPYW65UJM4CsF8ysxf2vKD4,1064
|
14
|
+
novel_downloader/core/downloaders/base.py,sha256=H3Y9NnSv4lQA6ilKCd7HqAX24R79wXBMkLG5StK8znc,6420
|
15
|
+
novel_downloader/core/downloaders/biquge.py,sha256=PTm8eeaHVyw_nvtnuRpZ2hT7hLs2LEPV1Drd2O6idNM,651
|
16
|
+
novel_downloader/core/downloaders/common.py,sha256=1YNo-e5tuK5bGvuzX3eVmEJznD7f5JMo7a_abIdmEkM,15504
|
17
|
+
novel_downloader/core/downloaders/esjzone.py,sha256=GBFJ3fIPov483QxkOTaKBU2wd2f8T4GdvzSbRaU9hDI,655
|
18
|
+
novel_downloader/core/downloaders/linovelib.py,sha256=Cd43_70SKbKM8_C8c4qev3pLUgyziW9413qrA8EcStY,663
|
19
|
+
novel_downloader/core/downloaders/qianbi.py,sha256=ARuk1xABOGCjlD_ct5hOKMgWloU187rUJBbICJ2yvkY,651
|
20
|
+
novel_downloader/core/downloaders/qidian.py,sha256=YrX5HdJDopEjc1kvEJRjMc1S4iR049IGvzUNtgE-LPE,12590
|
21
|
+
novel_downloader/core/downloaders/sfacg.py,sha256=ShlkIIR6OW_x2FhNDi79S_3AL2CWId2upQmRkApee_E,647
|
22
|
+
novel_downloader/core/downloaders/yamibo.py,sha256=9L0m5Zq0o1y44tCQ4ZRHHqdO3YresJkRAVvCtsQwouk,651
|
23
|
+
novel_downloader/core/exporters/__init__.py,sha256=ATkkdh6RUIaM19mG1XjFiaMnGRgFFGT3ixsqVkU13Q0,867
|
24
|
+
novel_downloader/core/exporters/base.py,sha256=duIdLnj9kKNZ9r2aJV0Rzl-rnQCP8olk12uzduT9PtE,6146
|
25
|
+
novel_downloader/core/exporters/biquge.py,sha256=SJCChYtDLJKrOpwDCT8IeR0v1LoiUQuTTejfGmRnxX8,462
|
26
|
+
novel_downloader/core/exporters/esjzone.py,sha256=UP9gtTOv6komWjgvwXkttqDYJJPjjbJdiG3yz9RxHSQ,467
|
27
|
+
novel_downloader/core/exporters/qianbi.py,sha256=x841MNIxpS5eY8u91ODC0cpIhpT-ENnFqs_Yx6HwtdM,462
|
28
|
+
novel_downloader/core/exporters/qidian.py,sha256=ecaZcgqXyLvJtT2J5QXw4qS9gY3d72mgUnV-0frJvhQ,724
|
29
|
+
novel_downloader/core/exporters/sfacg.py,sha256=j4_H-ixvDxd_OJfIvD4Lbs24LaiFjaYqhvhZcks-ItI,457
|
30
|
+
novel_downloader/core/exporters/yamibo.py,sha256=QaY13llqpyxIgmMnMVFtR5yFMIpyMHHdCmVDW4Dq-bo,462
|
31
|
+
novel_downloader/core/exporters/common/__init__.py,sha256=qIOZ_TgnnQZ6p45YQsxpTcB69e0Jq9XMG3IY0NBVsTM,274
|
32
|
+
novel_downloader/core/exporters/common/epub.py,sha256=1BhaZCZLehX-v8Z7spatyizhHoMfCF08ZfZGpPfB5Kw,6331
|
33
|
+
novel_downloader/core/exporters/common/main_exporter.py,sha256=-MRZEpAdsfdRt3VxIv4krMaw2UJnrvCnyj5Jde93zuA,3926
|
34
|
+
novel_downloader/core/exporters/common/txt.py,sha256=b5cNvH0hrjaBO89Uddn1IUY1BFhkHJdTd7WjSWk4ndk,4719
|
35
|
+
novel_downloader/core/exporters/epub_utils/__init__.py,sha256=cPtuQDtuJZPgOHRf_T5sgkQE13jZnOyPoK9hHJeMEEo,993
|
36
|
+
novel_downloader/core/exporters/epub_utils/css_builder.py,sha256=qvVgHnyoILgqSUoVsHv-cHzIdtfsqxA8WHt9_89puic,2145
|
37
|
+
novel_downloader/core/exporters/epub_utils/image_loader.py,sha256=hbxoPvXkBIZnoZSIlHwIUx4zWR8QVppWiH1KotMrBXg,3953
|
38
|
+
novel_downloader/core/exporters/epub_utils/initializer.py,sha256=TGueFff1ydRLdJYBEJnqIkoVCV57ZiR3dGu_c5Hrp8g,3364
|
39
|
+
novel_downloader/core/exporters/epub_utils/text_to_html.py,sha256=VSYbm33PrwxFmcT-2f_U2j3kuHzdcSMR7PRqOWvZICU,5617
|
40
|
+
novel_downloader/core/exporters/epub_utils/volume_intro.py,sha256=cwHMVwJeK8TzXnOw4MYWXvbZGt4iWFjdesDm4n_V-Tk,1830
|
41
|
+
novel_downloader/core/exporters/linovelib/__init__.py,sha256=zD7A7OhluipwICssnp27c_oenYozevZH9g0Qj5WOMWY,195
|
42
|
+
novel_downloader/core/exporters/linovelib/epub.py,sha256=wxfNxOX6kxSaove-YlDlNw1cmsBJFwysk6bS6Gsk8Cw,14503
|
43
|
+
novel_downloader/core/exporters/linovelib/main_exporter.py,sha256=cHkAp_jdF6uji5Avu1l8z6mR0nSrWYgX6ETDEezkjoE,3700
|
44
|
+
novel_downloader/core/exporters/linovelib/txt.py,sha256=ALlZUl5nNtg4OmYlurMC0acjmTOBV7G8c13DPrxbG4w,4407
|
45
|
+
novel_downloader/core/factory/__init__.py,sha256=_IY3N35onhWD_nw_TyxKOxa6e7Uak9Cv0bp4pK9yb0M,464
|
46
|
+
novel_downloader/core/factory/downloader.py,sha256=hNCp3IlZQzeTSLBMuO_Y_EzrAn9_-8SLBHcUwMw8ijM,2060
|
47
|
+
novel_downloader/core/factory/exporter.py,sha256=CjDJGnWBDk-S1zYntIDAEo1hLM2q55tlJOjTXKn0hAI,1533
|
48
|
+
novel_downloader/core/factory/fetcher.py,sha256=stfRJnh5ZXLqRsDtQC1BDeTe0yaZI-mgm3Qx03nWUP4,2402
|
49
|
+
novel_downloader/core/factory/parser.py,sha256=0PXepJhlE6aGs9_t81vyho1eCw84-6XRBGb98_phvSQ,2237
|
50
|
+
novel_downloader/core/fetchers/__init__.py,sha256=C1OykEdCzj3fpLRRhVrvwClpzz-pzTzTilH306crgYg,1440
|
51
|
+
novel_downloader/core/fetchers/base/__init__.py,sha256=p9be-q2YjiHcQhv4_KMeZmHgaAYYyUDWoBo6Gvwughc,224
|
52
|
+
novel_downloader/core/fetchers/base/browser.py,sha256=oR3ZMw71_LJrgTZXduVtZuXEkr7PZ9nxlS5ZlYavN_0,11153
|
53
|
+
novel_downloader/core/fetchers/base/rate_limiter.py,sha256=zUYH_PjnKfUzJpcbUPtMkwXxIlF0SH-ZTFlbCUrq060,2724
|
54
|
+
novel_downloader/core/fetchers/base/session.py,sha256=Elfpov2cqojunCrLaaSL9ZgWLNVanUDZvgOoIfk5sSc,13251
|
55
|
+
novel_downloader/core/fetchers/biquge/__init__.py,sha256=9EW4eerGeob4QGoDr11A8Mv7xvcfWVFU57M3VT9vzPI,236
|
56
|
+
novel_downloader/core/fetchers/biquge/browser.py,sha256=vplaCRAz8ECxk-yXm7DxSxW3EyX8GCYJMeImUut53RI,2390
|
57
|
+
novel_downloader/core/fetchers/biquge/session.py,sha256=waxv_dPsBl2SeudaINjt-eUwpW-FcDP2zFN_1-NXhr8,2400
|
58
|
+
novel_downloader/core/fetchers/common/__init__.py,sha256=ur_zQHrmJdPsFvpyC8AWjsZptHLbBHLb3EuKLt6LRVc,236
|
59
|
+
novel_downloader/core/fetchers/common/browser.py,sha256=RgNOizfgi_59Xaee6lTlpfiCMGjz_4luNDlHLQqUpl0,2289
|
60
|
+
novel_downloader/core/fetchers/common/session.py,sha256=Ydtwun9lPN4VEIIEIA7Skm6_GOiF_9VDUPkjGQQTdtM,2299
|
61
|
+
novel_downloader/core/fetchers/esjzone/__init__.py,sha256=Cr30WpKEnCrG_vVqttfI9T0zdkwDsLFOnCxQz4EAQQA,242
|
62
|
+
novel_downloader/core/fetchers/esjzone/browser.py,sha256=SPvbKURw2DkUAlOBLbklmRCFvDoI4tAUDwmBRo2Kuc8,6224
|
63
|
+
novel_downloader/core/fetchers/esjzone/session.py,sha256=hwvS9LMWm5PYHTTZqYkBgWqkVqkrCfQcaFh6NBICt1Q,7218
|
64
|
+
novel_downloader/core/fetchers/linovelib/__init__.py,sha256=sMNXSBvn8gaZxNX5x4Ork8RzXxL7PhuigquWx6zQ6A4,254
|
65
|
+
novel_downloader/core/fetchers/linovelib/browser.py,sha256=pXasjPCG18CXmWkXFh7Uw24HNlTiocSup5tKK42YGck,5515
|
66
|
+
novel_downloader/core/fetchers/linovelib/session.py,sha256=y-R2FzKf0dpffUATI-xTKvq1S2wvQWA50H1avuqUrGM,5525
|
67
|
+
novel_downloader/core/fetchers/qianbi/__init__.py,sha256=h4Rve7fO1GcSJ-DlNC5zw7fjoldJy4chG9RZQf5DuCU,236
|
68
|
+
novel_downloader/core/fetchers/qianbi/browser.py,sha256=1EmrSwpqSYhEO_ID3RJbUaAOhcqvVnMnch6iOafXbTA,3162
|
69
|
+
novel_downloader/core/fetchers/qianbi/session.py,sha256=c3pJcgi9C1x9QYTBihvazHcgT7XTp2HBYfStTn6gSEg,3141
|
70
|
+
novel_downloader/core/fetchers/qidian/__init__.py,sha256=2LshlX82lFpWZMV6yujHsfue9KM0-F1O3HvMCopIv9M,236
|
71
|
+
novel_downloader/core/fetchers/qidian/browser.py,sha256=JuFncpirI4HLyfIkR0xZmHL6AZZy7tQOzkzsC24b9ss,8578
|
72
|
+
novel_downloader/core/fetchers/qidian/session.py,sha256=WrHsov15PoRDYbb1ZRaScJQGRZv6axz9_hVEC1Wt1PM,9746
|
73
|
+
novel_downloader/core/fetchers/sfacg/__init__.py,sha256=bQAIwERsX9XOKrP2LteFKX8Jlhw4oeUNwpZTHXn5RRg,230
|
74
|
+
novel_downloader/core/fetchers/sfacg/browser.py,sha256=15PVS75PxEKR5W7mQbqVxoN0d4V1XVYVF0l1yy_sv_Y,5681
|
75
|
+
novel_downloader/core/fetchers/sfacg/session.py,sha256=9K4emQCRq45vzYn-ZDX549tK2F92x2CBMp4ODohNOjc,5085
|
76
|
+
novel_downloader/core/fetchers/yamibo/__init__.py,sha256=5ds6DNNvpo6F6U5dboEaIsJoKSPorkPte_HWVnXMdXo,236
|
77
|
+
novel_downloader/core/fetchers/yamibo/browser.py,sha256=7YRrWbA8_cOcT_z-VjMWP6FUg30TwV6eLW4zJZ_UxSE,7249
|
78
|
+
novel_downloader/core/fetchers/yamibo/session.py,sha256=434EArdKgEYIBZkb1nMub3PQXdRTS-Ov2_1u9MESjOs,7212
|
79
|
+
novel_downloader/core/interfaces/__init__.py,sha256=hB1SjBzuN7qnZx_h3RV4w_roj3ZwShbIG3CV9jGMB14,602
|
80
|
+
novel_downloader/core/interfaces/downloader.py,sha256=XozFf-7OOtBQDlWNs3IRcBjwTPGFWwseFLGta-sZZmU,1560
|
81
|
+
novel_downloader/core/interfaces/exporter.py,sha256=zwIaJ5FXo_JmKYg2UZV9FTWX7xZ2e0QuL4RTNGRtI04,1610
|
82
|
+
novel_downloader/core/interfaces/fetcher.py,sha256=XlP3d-Q_xvuVextZCdLOxDf8BwBbx799L_WLJx0WTB8,4096
|
83
|
+
novel_downloader/core/interfaces/parser.py,sha256=iZeUgNl-zE-wsf1eW4GVCPjEaLSvQrNJi_mVEOU9ulY,1353
|
84
|
+
novel_downloader/core/parsers/__init__.py,sha256=lrpt5E1YMiI0WY_cco1lEDhBQZJ_ZEV4b5fR6Fs3ZwA,832
|
85
|
+
novel_downloader/core/parsers/base.py,sha256=GifJANFm0auYyzYuqYox9LFgDskW8G2vR0DaMrRcG1w,2962
|
86
|
+
novel_downloader/core/parsers/biquge/__init__.py,sha256=oaLgBLdMW7DcdsEGXW-S91Z6_p5XmBcBIp7TX79hmV4,173
|
87
|
+
novel_downloader/core/parsers/biquge/main_parser.py,sha256=mbrEjJ8iSvWZ7SU4RH8pHxs1jnEwwlag_KkhTYoU0sY,4572
|
88
|
+
novel_downloader/core/parsers/common/__init__.py,sha256=MzNUUxvf7jmeO0LvQ_FRW0QLKuYVXkkTgJ4CxWZKF-Y,341
|
89
|
+
novel_downloader/core/parsers/common/helper.py,sha256=U5SzhVJJ5lGRNhLoOdEQw9LfBuPv17dmyT_MbdgnCl0,12052
|
90
|
+
novel_downloader/core/parsers/common/main_parser.py,sha256=znuXTJC-TXsgBTB3Hyqi87L65bqyM90TYyjU1KEhswc,3229
|
91
|
+
novel_downloader/core/parsers/esjzone/__init__.py,sha256=RSsUdOvaiqv-rTaYVc-qO25jytRCj9X2EYErMltA_b8,177
|
92
|
+
novel_downloader/core/parsers/esjzone/main_parser.py,sha256=jF7WPAwN5S2uUOdXk3fCkM3PSS5DPhI-SzmP00GFItw,8520
|
93
|
+
novel_downloader/core/parsers/linovelib/__init__.py,sha256=t5xzMEb4Q9MZxOvedOnot2HR6nCtU5YnAxGUk2lJoMA,185
|
94
|
+
novel_downloader/core/parsers/linovelib/main_parser.py,sha256=LqC6W-Lk7sdbiqhYvqQAh2z31v23OtMzyp1kAelsugI,7565
|
95
|
+
novel_downloader/core/parsers/qianbi/__init__.py,sha256=CNmoER8U2u4-ix5S0DDq-pHTtkLR0IZf2SLaTTYXee4,173
|
96
|
+
novel_downloader/core/parsers/qianbi/main_parser.py,sha256=kMjGew_dmqjI9oIThyHgxThZZgS3IaESB_tNf1nkCKk,5069
|
97
|
+
novel_downloader/core/parsers/qidian/__init__.py,sha256=fWWFeyythX0gpDCJ-2AslrRl2hq4vW2bx9hHh1W7mAw,173
|
98
|
+
novel_downloader/core/parsers/qidian/book_info_parser.py,sha256=h5oAVJOOSZMV4U2GnSezVHPJOEUKUic_oD2gl94mEPQ,3721
|
99
|
+
novel_downloader/core/parsers/qidian/chapter_encrypted.py,sha256=VhPDERJq1ROJaBlaJuCXJGffisOxkeYFDUKXHo31HfU,18263
|
100
|
+
novel_downloader/core/parsers/qidian/chapter_normal.py,sha256=6mD86OalpxiCDhjzUjcfFYsgmnTRU_rMIgFN8_Ceu50,4453
|
101
|
+
novel_downloader/core/parsers/qidian/chapter_router.py,sha256=foVMlWtE-qUOvJD_4EDiuAVaNkFdeV_ZTCvS5IL7Orc,1957
|
102
|
+
novel_downloader/core/parsers/qidian/main_parser.py,sha256=SPU6sUO8e4Gp_1q2WypuUQYEyHhoXM7OiRsthhFT6SM,4396
|
103
|
+
novel_downloader/core/parsers/qidian/utils/__init__.py,sha256=HfjXrGAwH_ceRSE4m88Dl9qb7vHMGcPFxxTU3qrTld8,548
|
104
|
+
novel_downloader/core/parsers/qidian/utils/decryptor_fetcher.py,sha256=8ytJnAfiJIxj0wlke9UwYA6vngUyLxVZt8PbfkNUhss,4687
|
105
|
+
novel_downloader/core/parsers/qidian/utils/helpers.py,sha256=-7vd1BMu5pVFCRySfPWyrItVbZ2wrHPNY8iAQEP7T_8,3264
|
106
|
+
novel_downloader/core/parsers/qidian/utils/node_decryptor.py,sha256=gjqirr5RECScFw0C6DET7ZMaLTcqGcPOmlkwFUJbTHQ,5965
|
107
|
+
novel_downloader/core/parsers/sfacg/__init__.py,sha256=O2nscvtOweMXHMONdvySTsLSy1ulhv53WTp4r6J47tI,169
|
108
|
+
novel_downloader/core/parsers/sfacg/main_parser.py,sha256=yW18MALAZisJdFO-7peI1h-4XVloEDXeCJvXUz5hJ1A,5899
|
109
|
+
novel_downloader/core/parsers/yamibo/__init__.py,sha256=KJ_fCcakoQrsG36OOd0paXXB1cAIwMirKaA8ZLUKUKI,173
|
110
|
+
novel_downloader/core/parsers/yamibo/main_parser.py,sha256=9sNXsDR2iDVyTYupP_7PyE7BXf2po3sZLq1XEFA8DrI,6554
|
111
|
+
novel_downloader/locales/en.json,sha256=-bKxndkwJCTCt7ZZGRRKzNaulC7z8XB6Tu7T1SR-1S8,6840
|
112
|
+
novel_downloader/locales/zh.json,sha256=xzbUsKtkdQvQkehTgIURlQMPepOSWbQBgzTJ4SSJNZc,6699
|
113
|
+
novel_downloader/models/__init__.py,sha256=G5fYGdaFquJBcllFJ7-YgqkvP4vD3xVEI9MeYcrCr_s,1068
|
114
|
+
novel_downloader/models/browser.py,sha256=ly-jM7izQ77yTIG-oau51HJofDpBfrXpIJZJjoQyad8,435
|
115
|
+
novel_downloader/models/chapter.py,sha256=bdAQUDZIuuTVxoYjoOJrbS2u81b1B2mkuZkTSf0m2HQ,492
|
116
|
+
novel_downloader/models/config.py,sha256=d5G8sob9oNA8rr1N7BvDEJRVSCKJUtD_QBcvWZvGMyw,2792
|
117
|
+
novel_downloader/models/login.py,sha256=sY2Jom6PLpA9Z3Uy7plZKhda3Gq7awKOOIIaQ79PpWs,371
|
118
|
+
novel_downloader/models/site_rules.py,sha256=kzDB5F8lf4udAO0WVUrgBOR7ave3jsMBxt7cEoG0bnI,2721
|
119
|
+
novel_downloader/models/tasks.py,sha256=e4DYEXQQQewgQyCCHfc0UYnkPJ96LafmhG3oO5MQP0Q,465
|
120
|
+
novel_downloader/models/types.py,sha256=q1KDuGW0SVxQILKKoPXKRecfaKe2m8jUM0nyaeDJ6dE,394
|
121
|
+
novel_downloader/resources/config/settings.toml,sha256=A2B_EMh0zjTLi6UBCjhX__igz7xLCXnCqDzJMhiZn8E,4491
|
122
|
+
novel_downloader/resources/css_styles/main.css,sha256=WM6GePwdOGgM86fbbOxQ0_0oerTBDZeQHt8zRVfcJp8,1617
|
123
|
+
novel_downloader/resources/css_styles/volume-intro.css,sha256=6gaUnNKkrb2w8tYJRq1BGD1FwbhT1I5W2GI_Zelo9G4,1156
|
124
|
+
novel_downloader/resources/images/volume_border.png,sha256=2dEVimnTHKOfLMhi7bhkh_5joWNnrqg8duomLSNOZx4,28613
|
125
|
+
novel_downloader/resources/js_scripts/qidian_decrypt_node.js,sha256=spNrk_gXI7pPW9abr4XGc2LASMe1UuN4BUe4cH24L8s,2195
|
126
|
+
novel_downloader/resources/json/linovelib_font_map.json,sha256=F1IlEcvXGcagnZCu4Gw2vQ2NLnhy6cJOLhabukqnftw,67854
|
127
|
+
novel_downloader/resources/json/replace_word_map.json,sha256=ptL9sGO9aK7rnnAaOIyZ0OiH7gaT0BhFzficzYZSDks,55
|
128
|
+
novel_downloader/resources/text/blacklist.txt,sha256=sovK9JgARZP3lud5b1EZgvv8LSVKPthf4ADpCSZZgQ8,154
|
129
|
+
novel_downloader/tui/__init__.py,sha256=8RB8tBrPcoBzm1tpQlgZqnOZXrdHmlzMRxuk9wsN4m0,115
|
130
|
+
novel_downloader/tui/app.py,sha256=ytV1u15nGCRj_ff_GeAL3W1XlU6r5Lh_k3HBcAjPRx0,731
|
131
|
+
novel_downloader/tui/main.py,sha256=MBP8SrwEYTpGQm-V9W_4rKnTeslneORfkzFsU3Xj2yA,256
|
132
|
+
novel_downloader/tui/screens/__init__.py,sha256=QsUM5cUEKm7nluQh9acEt37xRWbkZU3vqIpBduepDCU,203
|
133
|
+
novel_downloader/tui/screens/home.py,sha256=ximy5vwHGrkUD88U6Gr9h3kT-wtOMMLBzB9aEZPuA5w,6716
|
134
|
+
novel_downloader/tui/screens/login.py,sha256=eEVmQFZKQX8mCt_qp96QK8t2pB15FeUrk_oV3jm_Mz4,2224
|
135
|
+
novel_downloader/tui/styles/home_layout.tcss,sha256=VNJs339qiwNUuqwwdK6VkYThCdsqlw-2mEoZVCspdrw,974
|
136
|
+
novel_downloader/tui/widgets/richlog_handler.py,sha256=bhFb0E7Z7-dRS07y_vMjxjPeCic9AD59MFPLhef_R5g,572
|
137
|
+
novel_downloader/utils/__init__.py,sha256=4iUXNUzxeAnGmpGWsB4K_jckUYEW0u_LqWp_OM7mtK8,78
|
138
|
+
novel_downloader/utils/cache.py,sha256=_jVEWAaMGY86kWVnAuuIvJA_hA8dH2ClI7o3p99mEvE,618
|
139
|
+
novel_downloader/utils/chapter_storage.py,sha256=wXXIzNjs1GfPPoqgYYPnQ9Two23McW6ZZNDmhfSC9aY,9831
|
140
|
+
novel_downloader/utils/constants.py,sha256=1emMOmGRuat0yX8ozZMM1pAUGdU0kYBhDVNW_6dDtps,5700
|
141
|
+
novel_downloader/utils/cookies.py,sha256=WuPBt_z7cF4n7QPIw1julATWt6dbDRCbNdgXHSikmjo,2042
|
142
|
+
novel_downloader/utils/crypto_utils.py,sha256=BRlVR9Nvbu8TQXocJb3po1-kD5HkbCV0yyT84nq_XtI,2052
|
143
|
+
novel_downloader/utils/hash_store.py,sha256=HfzthzcKbHbVaHNpqjaAs2wDeq7iIeY8Mzkt_4v80jQ,9388
|
144
|
+
novel_downloader/utils/hash_utils.py,sha256=7eC7WsO_kl25OnRYWzIXbCXsewxvCcvRCzetsf-wbTo,3023
|
145
|
+
novel_downloader/utils/i18n.py,sha256=pdAcSIA5Tp-uPEBwNByHL7C1NayTnpOsl7zFv9p2G1k,1033
|
146
|
+
novel_downloader/utils/logger.py,sha256=9h1iFS8_auiquNgOBd-Q2pbbcnAhAKL39yf3PKadu00,3339
|
147
|
+
novel_downloader/utils/model_loader.py,sha256=JKgRFrr4HlAW9zuDUBAuuo_Kk_T_g9dWiU8E3zYk0vo,1996
|
148
|
+
novel_downloader/utils/network.py,sha256=W0SVr55MSUjTmMPkUvvkH10SRgFx3GWrCN_fDavfk4A,9143
|
149
|
+
novel_downloader/utils/state.py,sha256=FcNJ85GvBu7uEIjy0QHGr4sXMbHPEMkCjwUKNg5EabI,5132
|
150
|
+
novel_downloader/utils/file_utils/__init__.py,sha256=zvOm2qSEmWd_mRGJceGBZb5MYMSDAlWYjS5MkVQNZgI,1159
|
151
|
+
novel_downloader/utils/file_utils/io.py,sha256=AZ3NUe6lifGsYt3iYyXyQ2BO41WV8j013LpZy4KSjJQ,7473
|
152
|
+
novel_downloader/utils/file_utils/normalize.py,sha256=MrsCq4FqmskKRkHRV_J0z0dmn69OerMum-9sqx2XOGM,2023
|
153
|
+
novel_downloader/utils/file_utils/sanitize.py,sha256=rE-u4vpDL10zH8FT8d9wqwWsz-7dR6PJ-LE45K8VaeE,2112
|
154
|
+
novel_downloader/utils/fontocr/__init__.py,sha256=fe-04om3xxBvFKt5BBCApXCzv-Z0K_AY7lv9IB1jEHM,543
|
155
|
+
novel_downloader/utils/fontocr/ocr_v1.py,sha256=bwkvqKXUcKsgBCrag8jHuIl8AOM_MbV28ZSQ_aO_X6s,11245
|
156
|
+
novel_downloader/utils/fontocr/ocr_v2.py,sha256=OJ_egORHa9KwVJX4kEABR25D7RRF0jVCRV4WgHUal7M,27307
|
157
|
+
novel_downloader/utils/text_utils/__init__.py,sha256=JQtEnJ22K9o_syY5PV4Bf_p4BKZonX5g9-A5eqlxpZs,806
|
158
|
+
novel_downloader/utils/text_utils/chapter_formatting.py,sha256=WAAEAcI7zI_uIeARDybZfXDdMvGio3VIkANFrK8-8Os,1378
|
159
|
+
novel_downloader/utils/text_utils/diff_display.py,sha256=aUvjMcYO-1_P8ZYiYbmYbJOByKo2bWoBV_ifRuAqwb8,2528
|
160
|
+
novel_downloader/utils/text_utils/font_mapping.py,sha256=Aos5skBhowDdPgnYmK0bpLtNm2hZg3RolNlTkxC9kO8,865
|
161
|
+
novel_downloader/utils/text_utils/text_cleaning.py,sha256=_Nahr8iv3341IyDXW-KpTn4XNG2hB-HajSM52pysPu8,1630
|
162
|
+
novel_downloader/utils/time_utils/__init__.py,sha256=725vY2PvqFhjbAz0hCOuIuhSCK8HrEqQ_k3YwvubmXo,624
|
163
|
+
novel_downloader/utils/time_utils/datetime_utils.py,sha256=1eyX8lTEqkQ-rEej_GrhbUpaIR5tsFdVSKT-q8s9X-g,4927
|
164
|
+
novel_downloader/utils/time_utils/sleep_utils.py,sha256=C4XYeAtxoVZC9Ju6vhhP9sbOrSpdZG2Nm-x1IYO_OFA,3233
|
165
|
+
novel_downloader-1.4.0.dist-info/licenses/LICENSE,sha256=XgmnH0mBf-qEiizoVAfJQAKzPB9y3rBa-ni7M0Aqv4A,1066
|
166
|
+
novel_downloader-1.4.0.dist-info/METADATA,sha256=iFXirg0_BDhczHRV37P5QgtbuUawakAx1kzY1aAfQeU,7027
|
167
|
+
novel_downloader-1.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
168
|
+
novel_downloader-1.4.0.dist-info/entry_points.txt,sha256=u1Ns5xI_QJyL4HAFCgJvJdib9ugu7M9I2tnQwZjJxrk,112
|
169
|
+
novel_downloader-1.4.0.dist-info/top_level.txt,sha256=hP4jYWM2LTm1jxsW4hqEB8N0dsRvldO2QdhggJT917I,17
|
170
|
+
novel_downloader-1.4.0.dist-info/RECORD,,
|
@@ -1,66 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
novel_downloader.cli.interactive
|
4
|
-
--------------------------------
|
5
|
-
|
6
|
-
Interactive CLI mode for novel_downloader.
|
7
|
-
Supports multilingual prompt, input validation, and quit control.
|
8
|
-
"""
|
9
|
-
|
10
|
-
import click
|
11
|
-
from click import Context
|
12
|
-
|
13
|
-
from novel_downloader.cli.download import download_cli
|
14
|
-
from novel_downloader.utils.i18n import t
|
15
|
-
|
16
|
-
|
17
|
-
@click.group( # type: ignore
|
18
|
-
name="interactive", help=t("interactive_help"), invoke_without_command=True
|
19
|
-
)
|
20
|
-
@click.pass_context # type: ignore
|
21
|
-
def interactive_cli(ctx: Context) -> None:
|
22
|
-
"""Interactive mode for novel selection and preview."""
|
23
|
-
if ctx.invoked_subcommand is None:
|
24
|
-
click.echo(t("interactive_no_sub"))
|
25
|
-
|
26
|
-
options = [
|
27
|
-
t("interactive_option_download"),
|
28
|
-
t("interactive_option_browse"),
|
29
|
-
t("interactive_option_preview"),
|
30
|
-
t("interactive_option_exit"),
|
31
|
-
]
|
32
|
-
for idx, opt in enumerate(options, 1):
|
33
|
-
click.echo(f"{idx}. {opt}")
|
34
|
-
|
35
|
-
choice = click.prompt(t("interactive_prompt_choice"), type=int)
|
36
|
-
|
37
|
-
if choice == 1:
|
38
|
-
default_site = "qidian"
|
39
|
-
site: str = click.prompt(
|
40
|
-
t("download_option_site", default=default_site),
|
41
|
-
default_site,
|
42
|
-
)
|
43
|
-
ids_input: str = click.prompt(t("interactive_prompt_book_ids"))
|
44
|
-
book_ids = ids_input.strip().split()
|
45
|
-
ctx.invoke(download_cli, book_ids=book_ids, site=site)
|
46
|
-
elif choice == 2:
|
47
|
-
ctx.invoke(browse)
|
48
|
-
elif choice == 3:
|
49
|
-
ctx.invoke(preview)
|
50
|
-
else:
|
51
|
-
click.echo(t("interactive_exit"))
|
52
|
-
return
|
53
|
-
|
54
|
-
|
55
|
-
@interactive_cli.command(help=t("interactive_browse_help")) # type: ignore
|
56
|
-
@click.pass_context # type: ignore
|
57
|
-
def browse(ctx: Context) -> None:
|
58
|
-
"""Browse available novels interactively."""
|
59
|
-
click.echo(t("interactive_browse_start"))
|
60
|
-
|
61
|
-
|
62
|
-
@interactive_cli.command(help=t("interactive_preview_help")) # type: ignore
|
63
|
-
@click.pass_context # type: ignore
|
64
|
-
def preview(ctx: Context) -> None:
|
65
|
-
"""Preview chapters before downloading."""
|
66
|
-
click.echo(t("interactive_preview_start"))
|
novel_downloader/cli/settings.py
DELETED
@@ -1,177 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
novel_downloader.cli.settings
|
4
|
-
-----------------------------
|
5
|
-
|
6
|
-
Commands to configure novel downloader settings.
|
7
|
-
"""
|
8
|
-
|
9
|
-
import shutil
|
10
|
-
from importlib.resources import as_file
|
11
|
-
from pathlib import Path
|
12
|
-
|
13
|
-
import click
|
14
|
-
from click import Context
|
15
|
-
|
16
|
-
from novel_downloader.config import save_config_file, save_rules_as_json
|
17
|
-
from novel_downloader.utils.constants import DEFAULT_SETTINGS_PATHS
|
18
|
-
from novel_downloader.utils.i18n import t
|
19
|
-
from novel_downloader.utils.logger import setup_logging
|
20
|
-
from novel_downloader.utils.state import state_mgr
|
21
|
-
|
22
|
-
|
23
|
-
@click.group(name="settings", help=t("settings_help")) # type: ignore
|
24
|
-
def settings_cli() -> None:
|
25
|
-
"""Configure downloader settings."""
|
26
|
-
setup_logging()
|
27
|
-
pass
|
28
|
-
|
29
|
-
|
30
|
-
@settings_cli.command(name="init", help=t("settings_init_help")) # type: ignore
|
31
|
-
@click.option("--force", is_flag=True, help=t("settings_init_force_help")) # type: ignore
|
32
|
-
def init_settings(force: bool) -> None:
|
33
|
-
"""Initialize default settings and rules in the current directory."""
|
34
|
-
cwd = Path.cwd()
|
35
|
-
|
36
|
-
for resource in DEFAULT_SETTINGS_PATHS:
|
37
|
-
target_path = cwd / resource.name
|
38
|
-
should_copy = True
|
39
|
-
|
40
|
-
if target_path.exists():
|
41
|
-
if force:
|
42
|
-
should_copy = True
|
43
|
-
click.echo(t("settings_init_overwrite", filename=resource.name))
|
44
|
-
else:
|
45
|
-
click.echo(t("settings_init_exists", filename=resource.name))
|
46
|
-
should_copy = click.confirm(
|
47
|
-
t("settings_init_confirm_overwrite", filename=resource.name),
|
48
|
-
default=False,
|
49
|
-
)
|
50
|
-
|
51
|
-
if not should_copy:
|
52
|
-
click.echo(t("settings_init_skip", filename=resource.name))
|
53
|
-
continue
|
54
|
-
|
55
|
-
try:
|
56
|
-
with as_file(resource) as actual_path:
|
57
|
-
shutil.copy(actual_path, target_path)
|
58
|
-
click.echo(t("settings_init_copy", filename=resource.name))
|
59
|
-
except Exception as e:
|
60
|
-
raise click.ClickException(
|
61
|
-
t("settings_init_error", filename=resource.name, err=e)
|
62
|
-
) from e
|
63
|
-
|
64
|
-
|
65
|
-
@settings_cli.command(name="set-lang", help=t("settings_set_lang_help")) # type: ignore
|
66
|
-
@click.argument("lang", type=click.Choice(["zh", "en"])) # type: ignore
|
67
|
-
@click.pass_context # type: ignore
|
68
|
-
def set_language(ctx: Context, lang: str) -> None:
|
69
|
-
"""Switch language between Chinese and English."""
|
70
|
-
state_mgr.set_language(lang)
|
71
|
-
click.echo(t("settings_set_lang", lang=lang))
|
72
|
-
|
73
|
-
|
74
|
-
@settings_cli.command(name="set-config", help=t("settings_set_config_help")) # type: ignore
|
75
|
-
@click.argument("path", type=click.Path(exists=True, dir_okay=False, resolve_path=True)) # type: ignore
|
76
|
-
def set_config(path: str) -> None:
|
77
|
-
"""Set and save a custom YAML configuration file."""
|
78
|
-
try:
|
79
|
-
save_config_file(path)
|
80
|
-
click.echo(t("settings_set_config", path=path))
|
81
|
-
except Exception as e:
|
82
|
-
raise click.ClickException(t("settings_set_config_fail", err=e)) from e
|
83
|
-
|
84
|
-
|
85
|
-
@settings_cli.command(name="update-rules", help=t("settings_update_rules_help")) # type: ignore
|
86
|
-
@click.argument("path", type=click.Path(exists=True, dir_okay=False, resolve_path=True)) # type: ignore
|
87
|
-
def update_rules(path: str) -> None:
|
88
|
-
"""Update site rules from a TOML/YAML/JSON file."""
|
89
|
-
try:
|
90
|
-
save_rules_as_json(path)
|
91
|
-
click.echo(t("settings_update_rules", path=path))
|
92
|
-
except Exception as e:
|
93
|
-
raise click.ClickException(t("settings_update_rules_fail", err=e)) from e
|
94
|
-
|
95
|
-
|
96
|
-
@settings_cli.command(
|
97
|
-
name="set-cookies", help=t("settings_set_cookies_help")
|
98
|
-
) # type: ignore
|
99
|
-
@click.argument("site", required=False) # type: ignore
|
100
|
-
@click.argument("cookies", required=False) # type: ignore
|
101
|
-
@click.pass_context # type: ignore
|
102
|
-
def set_cookies(ctx: Context, site: str, cookies: str) -> None:
|
103
|
-
"""
|
104
|
-
Set or update cookies for a site.
|
105
|
-
|
106
|
-
:param site: Site identifier (e.g. 'qidian', 'bqg').
|
107
|
-
If omitted, you will be prompted to enter it.
|
108
|
-
:param cookies: Cookie payload. Can be a JSON string (e.g. '{"k":"v"}')
|
109
|
-
or a browser-style string 'k1=v1; k2=v2'.
|
110
|
-
If omitted, you will be prompted to enter it.
|
111
|
-
"""
|
112
|
-
if not site:
|
113
|
-
site = click.prompt(t("settings_set_cookies_prompt_site"), type=str)
|
114
|
-
if not cookies:
|
115
|
-
cookies = click.prompt(t("settings_set_cookies_prompt_payload"), type=str)
|
116
|
-
|
117
|
-
try:
|
118
|
-
state_mgr.set_cookies(site, cookies)
|
119
|
-
click.echo(t("settings_set_cookies_success", site=site))
|
120
|
-
except Exception as e:
|
121
|
-
raise click.ClickException(t("settings_set_cookies_fail", err=e)) from e
|
122
|
-
|
123
|
-
|
124
|
-
@settings_cli.command(name="add-hash", help=t("settings_add_hash_help")) # type: ignore
|
125
|
-
@click.option(
|
126
|
-
"--path",
|
127
|
-
type=click.Path(exists=True, dir_okay=False),
|
128
|
-
help=t("settings_add_hash_path_help"),
|
129
|
-
) # type: ignore
|
130
|
-
def add_image_hashes(path: str | None) -> None:
|
131
|
-
"""
|
132
|
-
Add image hashes to internal store for matching.
|
133
|
-
Can be run in interactive mode (no --path), or with a JSON file.
|
134
|
-
"""
|
135
|
-
from novel_downloader.utils.hash_store import img_hash_store
|
136
|
-
|
137
|
-
if path:
|
138
|
-
try:
|
139
|
-
img_hash_store.add_from_map(path)
|
140
|
-
img_hash_store.save()
|
141
|
-
click.echo(t("settings_add_hash_loaded", path=path))
|
142
|
-
except Exception as e:
|
143
|
-
raise click.ClickException(
|
144
|
-
t("settings_add_hash_load_fail", err=str(e))
|
145
|
-
) from e
|
146
|
-
else:
|
147
|
-
click.echo(t("settings_add_hash_prompt_tip"))
|
148
|
-
while True:
|
149
|
-
img_path = click.prompt(
|
150
|
-
t("settings_add_hash_prompt_img"),
|
151
|
-
type=str,
|
152
|
-
default="",
|
153
|
-
show_default=False,
|
154
|
-
).strip()
|
155
|
-
if not img_path or img_path.lower() in {"exit", "quit"}:
|
156
|
-
break
|
157
|
-
if not Path(img_path).exists():
|
158
|
-
click.echo(t("settings_add_hash_path_invalid"))
|
159
|
-
continue
|
160
|
-
|
161
|
-
label = click.prompt(
|
162
|
-
t("settings_add_hash_prompt_label"),
|
163
|
-
type=str,
|
164
|
-
default="",
|
165
|
-
show_default=False,
|
166
|
-
).strip()
|
167
|
-
if not label or label.lower() in {"exit", "quit"}:
|
168
|
-
break
|
169
|
-
|
170
|
-
try:
|
171
|
-
img_hash_store.add_image(img_path, label)
|
172
|
-
click.echo(t("settings_add_hash_added", img=img_path, label=label))
|
173
|
-
except Exception as e:
|
174
|
-
click.echo(t("settings_add_hash_failed", err=str(e)))
|
175
|
-
|
176
|
-
img_hash_store.save()
|
177
|
-
click.echo(t("settings_add_hash_saved"))
|
@@ -1,187 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
novel_downloader.config.models
|
4
|
-
------------------------------
|
5
|
-
|
6
|
-
Defines structured configuration models using dataclasses for each
|
7
|
-
major component in the novel_downloader pipeline.
|
8
|
-
|
9
|
-
Each config section corresponds to a specific stage of the pipeline:
|
10
|
-
- RequesterConfig: network settings for requests and DrissionPage
|
11
|
-
- DownloaderConfig: chapter download behavior and local raw data paths
|
12
|
-
- ParserConfig: font decoding, cache handling, and debug options
|
13
|
-
- SaverConfig: output formatting, export formats, and filename templates
|
14
|
-
|
15
|
-
These models are used to map loaded YAML or JSON config data into
|
16
|
-
strongly typed Python objects for safer and cleaner access.
|
17
|
-
"""
|
18
|
-
|
19
|
-
from dataclasses import dataclass
|
20
|
-
from typing import Any, Literal, TypedDict
|
21
|
-
|
22
|
-
ModeType = Literal["browser", "session", "async"]
|
23
|
-
StorageBackend = Literal["json", "sqlite"]
|
24
|
-
|
25
|
-
|
26
|
-
# === Requesters ===
|
27
|
-
@dataclass
|
28
|
-
class RequesterConfig:
|
29
|
-
retry_times: int = 3
|
30
|
-
backoff_factor: float = 2.0
|
31
|
-
timeout: float = 30.0
|
32
|
-
headless: bool = True
|
33
|
-
user_data_folder: str = ""
|
34
|
-
profile_name: str = ""
|
35
|
-
auto_close: bool = True
|
36
|
-
disable_images: bool = True
|
37
|
-
mute_audio: bool = True
|
38
|
-
mode: ModeType = "session"
|
39
|
-
max_connections: int = 10
|
40
|
-
max_rps: float | None = None # Maximum requests per second
|
41
|
-
username: str = ""
|
42
|
-
password: str = ""
|
43
|
-
|
44
|
-
|
45
|
-
# === Downloaders ===
|
46
|
-
@dataclass
|
47
|
-
class DownloaderConfig:
|
48
|
-
request_interval: float = 5.0
|
49
|
-
raw_data_dir: str = "./raw_data"
|
50
|
-
cache_dir: str = "./novel_cache"
|
51
|
-
download_workers: int = 4
|
52
|
-
parser_workers: int = 4
|
53
|
-
use_process_pool: bool = False
|
54
|
-
skip_existing: bool = True
|
55
|
-
login_required: bool = False
|
56
|
-
save_html: bool = False
|
57
|
-
mode: ModeType = "session"
|
58
|
-
storage_backend: StorageBackend = "json"
|
59
|
-
storage_batch_size: int = 1
|
60
|
-
|
61
|
-
|
62
|
-
# === Parsers ===
|
63
|
-
@dataclass
|
64
|
-
class ParserConfig:
|
65
|
-
cache_dir: str = "./novel_cache"
|
66
|
-
decode_font: bool = False
|
67
|
-
use_freq: bool = False
|
68
|
-
use_ocr: bool = True
|
69
|
-
use_vec: bool = False
|
70
|
-
ocr_version: str = "v1.0"
|
71
|
-
batch_size: int = 32
|
72
|
-
gpu_mem: int = 500
|
73
|
-
gpu_id: int | None = None
|
74
|
-
ocr_weight: float = 0.6
|
75
|
-
vec_weight: float = 0.4
|
76
|
-
save_font_debug: bool = False
|
77
|
-
mode: ModeType = "session"
|
78
|
-
|
79
|
-
|
80
|
-
# === Savers ===
|
81
|
-
@dataclass
|
82
|
-
class SaverConfig:
|
83
|
-
cache_dir: str = "./novel_cache"
|
84
|
-
raw_data_dir: str = "./raw_data"
|
85
|
-
output_dir: str = "./downloads"
|
86
|
-
storage_backend: StorageBackend = "json"
|
87
|
-
clean_text: bool = True
|
88
|
-
make_txt: bool = True
|
89
|
-
make_epub: bool = False
|
90
|
-
make_md: bool = False
|
91
|
-
make_pdf: bool = False
|
92
|
-
append_timestamp: bool = True
|
93
|
-
filename_template: str = "{title}_{author}"
|
94
|
-
include_cover: bool = True
|
95
|
-
include_toc: bool = False
|
96
|
-
include_picture: bool = False
|
97
|
-
|
98
|
-
|
99
|
-
class RuleStep(TypedDict, total=False):
|
100
|
-
# —— 操作类型 —— #
|
101
|
-
type: Literal[
|
102
|
-
"attr",
|
103
|
-
"select_one",
|
104
|
-
"select",
|
105
|
-
"find",
|
106
|
-
"find_all",
|
107
|
-
"exclude",
|
108
|
-
"regex",
|
109
|
-
"text",
|
110
|
-
"strip",
|
111
|
-
"replace",
|
112
|
-
"split",
|
113
|
-
"join",
|
114
|
-
]
|
115
|
-
|
116
|
-
# —— BeautifulSoup 相关 —— #
|
117
|
-
selector: str | None # CSS 选择器, 用于 select/select_one/exclude
|
118
|
-
name: str | None # 标签名称, 用于 find/find_all
|
119
|
-
attrs: dict[str, Any] | None # 属性过滤, 用于 find/find_all
|
120
|
-
limit: int | None # find_all 的最大匹配数
|
121
|
-
attr: str | None # 从元素获取属性值 (select/select_one/select_all)
|
122
|
-
|
123
|
-
# —— 正则相关 —— #
|
124
|
-
pattern: str | None # 正则表达式
|
125
|
-
flags: int | None # re.I, re.M 等
|
126
|
-
group: int | None # 匹配结果中的第几个分组 (默认 0)
|
127
|
-
template: str | None # 自定义组合, 比如 "$1$2字"
|
128
|
-
|
129
|
-
# —— 文本处理 —— #
|
130
|
-
chars: str | None # strip 要去除的字符集
|
131
|
-
old: str | None # replace 中要被替换的子串
|
132
|
-
new: str | None # replace 中新的子串
|
133
|
-
count: int | None # replace 中的最大替换次数
|
134
|
-
sep: str | None # split/join 的分隔符
|
135
|
-
index: int | None # split/select_all/select 之后取第几个元素
|
136
|
-
|
137
|
-
|
138
|
-
class FieldRules(TypedDict):
|
139
|
-
steps: list[RuleStep]
|
140
|
-
|
141
|
-
|
142
|
-
class ChapterFieldRules(TypedDict):
|
143
|
-
key: str
|
144
|
-
steps: list[RuleStep]
|
145
|
-
|
146
|
-
|
147
|
-
class VolumesRulesOptional(TypedDict, total=False):
|
148
|
-
volume_selector: str # 有卷时选择 volume 块的 selector
|
149
|
-
volume_name_steps: list[RuleStep]
|
150
|
-
volume_mode: str # Optional: "normal" (default) or "mixed"
|
151
|
-
list_selector: str # Optional: If "mixed" mode, parent container selector
|
152
|
-
|
153
|
-
|
154
|
-
class VolumesRules(VolumesRulesOptional):
|
155
|
-
has_volume: bool # 是否存在卷,false=未分卷
|
156
|
-
chapter_selector: str # 选择 chapter 节点的 selector
|
157
|
-
chapter_steps: list[ChapterFieldRules] # 提取章节信息的步骤列表
|
158
|
-
|
159
|
-
|
160
|
-
class BookInfoRules(TypedDict, total=False):
|
161
|
-
book_name: FieldRules
|
162
|
-
author: FieldRules
|
163
|
-
cover_url: FieldRules
|
164
|
-
update_time: FieldRules
|
165
|
-
serial_status: FieldRules
|
166
|
-
word_count: FieldRules
|
167
|
-
summary: FieldRules
|
168
|
-
volumes: VolumesRules
|
169
|
-
|
170
|
-
|
171
|
-
class ChapterRules(TypedDict, total=False):
|
172
|
-
title: FieldRules
|
173
|
-
content: FieldRules
|
174
|
-
|
175
|
-
|
176
|
-
class SiteProfile(TypedDict):
|
177
|
-
book_info_url: str
|
178
|
-
chapter_url: str
|
179
|
-
|
180
|
-
|
181
|
-
class SiteRules(TypedDict):
|
182
|
-
profile: SiteProfile
|
183
|
-
book_info: BookInfoRules
|
184
|
-
chapter: ChapterRules
|
185
|
-
|
186
|
-
|
187
|
-
SiteRulesDict = dict[str, SiteRules]
|
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
novel_downloader.core.downloaders.base
|
4
|
-
--------------------------------------
|
5
|
-
|
6
|
-
"""
|
7
|
-
|
8
|
-
from .base_async import BaseAsyncDownloader
|
9
|
-
from .base_sync import BaseDownloader
|
10
|
-
|
11
|
-
__all__ = [
|
12
|
-
"BaseAsyncDownloader",
|
13
|
-
"BaseDownloader",
|
14
|
-
]
|