novel-downloader 1.4.5__py3-none-any.whl → 2.0.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/__init__.py +2 -4
- novel_downloader/cli/clean.py +21 -88
- novel_downloader/cli/config.py +27 -104
- novel_downloader/cli/download.py +78 -66
- novel_downloader/cli/export.py +20 -21
- novel_downloader/cli/main.py +3 -1
- novel_downloader/cli/search.py +120 -0
- novel_downloader/cli/ui.py +156 -0
- novel_downloader/config/__init__.py +10 -14
- novel_downloader/config/adapter.py +195 -99
- novel_downloader/config/{loader.py → file_io.py} +53 -27
- novel_downloader/core/__init__.py +14 -13
- novel_downloader/core/archived/deqixs/fetcher.py +115 -0
- novel_downloader/core/archived/deqixs/parser.py +132 -0
- novel_downloader/core/archived/deqixs/searcher.py +89 -0
- novel_downloader/core/archived/qidian/searcher.py +79 -0
- novel_downloader/core/archived/wanbengo/searcher.py +98 -0
- novel_downloader/core/archived/xshbook/searcher.py +93 -0
- novel_downloader/core/downloaders/__init__.py +8 -30
- novel_downloader/core/downloaders/base.py +182 -30
- novel_downloader/core/downloaders/common.py +217 -384
- novel_downloader/core/downloaders/qianbi.py +332 -4
- novel_downloader/core/downloaders/qidian.py +250 -290
- novel_downloader/core/downloaders/registry.py +69 -0
- novel_downloader/core/downloaders/signals.py +46 -0
- novel_downloader/core/exporters/__init__.py +8 -26
- novel_downloader/core/exporters/base.py +107 -31
- novel_downloader/core/exporters/common/__init__.py +3 -4
- novel_downloader/core/exporters/common/epub.py +92 -171
- novel_downloader/core/exporters/common/main_exporter.py +14 -67
- novel_downloader/core/exporters/common/txt.py +90 -86
- novel_downloader/core/exporters/epub_util.py +184 -1327
- novel_downloader/core/exporters/linovelib/__init__.py +3 -2
- novel_downloader/core/exporters/linovelib/epub.py +165 -222
- novel_downloader/core/exporters/linovelib/main_exporter.py +10 -71
- novel_downloader/core/exporters/linovelib/txt.py +76 -66
- novel_downloader/core/exporters/qidian.py +15 -11
- novel_downloader/core/exporters/registry.py +55 -0
- novel_downloader/core/exporters/txt_util.py +67 -0
- novel_downloader/core/fetchers/__init__.py +57 -56
- novel_downloader/core/fetchers/aaatxt.py +83 -0
- novel_downloader/core/fetchers/{biquge/session.py → b520.py} +10 -10
- novel_downloader/core/fetchers/{base/session.py → base.py} +63 -47
- novel_downloader/core/fetchers/biquyuedu.py +83 -0
- novel_downloader/core/fetchers/dxmwx.py +110 -0
- novel_downloader/core/fetchers/eightnovel.py +139 -0
- novel_downloader/core/fetchers/{esjzone/session.py → esjzone.py} +23 -11
- novel_downloader/core/fetchers/guidaye.py +85 -0
- novel_downloader/core/fetchers/hetushu.py +92 -0
- novel_downloader/core/fetchers/{qianbi/browser.py → i25zw.py} +22 -26
- novel_downloader/core/fetchers/ixdzs8.py +113 -0
- novel_downloader/core/fetchers/jpxs123.py +101 -0
- novel_downloader/core/fetchers/{biquge/browser.py → lewenn.py} +15 -15
- novel_downloader/core/fetchers/{linovelib/session.py → linovelib.py} +16 -12
- novel_downloader/core/fetchers/piaotia.py +105 -0
- novel_downloader/core/fetchers/qbtr.py +101 -0
- novel_downloader/core/fetchers/{qianbi/session.py → qianbi.py} +9 -9
- novel_downloader/core/fetchers/{qidian/session.py → qidian.py} +55 -40
- novel_downloader/core/fetchers/quanben5.py +92 -0
- novel_downloader/core/fetchers/{base/rate_limiter.py → rate_limiter.py} +2 -2
- novel_downloader/core/fetchers/registry.py +60 -0
- novel_downloader/core/fetchers/{sfacg/session.py → sfacg.py} +11 -9
- novel_downloader/core/fetchers/shencou.py +106 -0
- novel_downloader/core/fetchers/{common/browser.py → shuhaige.py} +24 -19
- novel_downloader/core/fetchers/tongrenquan.py +84 -0
- novel_downloader/core/fetchers/ttkan.py +95 -0
- novel_downloader/core/fetchers/{common/session.py → wanbengo.py} +21 -17
- novel_downloader/core/fetchers/xiaoshuowu.py +106 -0
- novel_downloader/core/fetchers/xiguashuwu.py +177 -0
- novel_downloader/core/fetchers/xs63b.py +171 -0
- novel_downloader/core/fetchers/xshbook.py +85 -0
- novel_downloader/core/fetchers/{yamibo/session.py → yamibo.py} +23 -11
- novel_downloader/core/fetchers/yibige.py +114 -0
- novel_downloader/core/interfaces/__init__.py +8 -14
- novel_downloader/core/interfaces/downloader.py +6 -2
- novel_downloader/core/interfaces/exporter.py +7 -7
- novel_downloader/core/interfaces/fetcher.py +4 -17
- novel_downloader/core/interfaces/parser.py +5 -6
- novel_downloader/core/interfaces/searcher.py +26 -0
- novel_downloader/core/parsers/__init__.py +58 -22
- novel_downloader/core/parsers/aaatxt.py +132 -0
- novel_downloader/core/parsers/b520.py +116 -0
- novel_downloader/core/parsers/base.py +63 -12
- novel_downloader/core/parsers/biquyuedu.py +133 -0
- novel_downloader/core/parsers/dxmwx.py +162 -0
- novel_downloader/core/parsers/eightnovel.py +224 -0
- novel_downloader/core/parsers/{esjzone/main_parser.py → esjzone.py} +67 -67
- novel_downloader/core/parsers/guidaye.py +128 -0
- novel_downloader/core/parsers/hetushu.py +139 -0
- novel_downloader/core/parsers/i25zw.py +137 -0
- novel_downloader/core/parsers/ixdzs8.py +186 -0
- novel_downloader/core/parsers/jpxs123.py +137 -0
- novel_downloader/core/parsers/lewenn.py +142 -0
- novel_downloader/core/parsers/{linovelib/main_parser.py → linovelib.py} +54 -65
- novel_downloader/core/parsers/piaotia.py +189 -0
- novel_downloader/core/parsers/qbtr.py +136 -0
- novel_downloader/core/parsers/{qianbi/main_parser.py → qianbi.py} +54 -51
- novel_downloader/core/parsers/qidian/__init__.py +2 -2
- novel_downloader/core/parsers/qidian/book_info_parser.py +58 -59
- novel_downloader/core/parsers/qidian/chapter_encrypted.py +290 -346
- novel_downloader/core/parsers/qidian/chapter_normal.py +25 -56
- novel_downloader/core/parsers/qidian/main_parser.py +19 -57
- novel_downloader/core/parsers/qidian/utils/__init__.py +12 -11
- novel_downloader/core/parsers/qidian/utils/decryptor_fetcher.py +6 -7
- novel_downloader/core/parsers/qidian/utils/fontmap_recover.py +143 -0
- novel_downloader/core/parsers/qidian/utils/helpers.py +0 -4
- novel_downloader/core/parsers/qidian/utils/node_decryptor.py +2 -2
- novel_downloader/core/parsers/quanben5.py +103 -0
- novel_downloader/core/parsers/registry.py +57 -0
- novel_downloader/core/parsers/{sfacg/main_parser.py → sfacg.py} +46 -48
- novel_downloader/core/parsers/shencou.py +215 -0
- novel_downloader/core/parsers/shuhaige.py +111 -0
- novel_downloader/core/parsers/tongrenquan.py +116 -0
- novel_downloader/core/parsers/ttkan.py +132 -0
- novel_downloader/core/parsers/wanbengo.py +191 -0
- novel_downloader/core/parsers/xiaoshuowu.py +173 -0
- novel_downloader/core/parsers/xiguashuwu.py +435 -0
- novel_downloader/core/parsers/xs63b.py +161 -0
- novel_downloader/core/parsers/xshbook.py +134 -0
- novel_downloader/core/parsers/yamibo.py +155 -0
- novel_downloader/core/parsers/yibige.py +166 -0
- novel_downloader/core/searchers/__init__.py +51 -0
- novel_downloader/core/searchers/aaatxt.py +107 -0
- novel_downloader/core/searchers/b520.py +84 -0
- novel_downloader/core/searchers/base.py +168 -0
- novel_downloader/core/searchers/dxmwx.py +105 -0
- novel_downloader/core/searchers/eightnovel.py +84 -0
- novel_downloader/core/searchers/esjzone.py +102 -0
- novel_downloader/core/searchers/hetushu.py +92 -0
- novel_downloader/core/searchers/i25zw.py +93 -0
- novel_downloader/core/searchers/ixdzs8.py +107 -0
- novel_downloader/core/searchers/jpxs123.py +107 -0
- novel_downloader/core/searchers/piaotia.py +100 -0
- novel_downloader/core/searchers/qbtr.py +106 -0
- novel_downloader/core/searchers/qianbi.py +165 -0
- novel_downloader/core/searchers/quanben5.py +144 -0
- novel_downloader/core/searchers/registry.py +79 -0
- novel_downloader/core/searchers/shuhaige.py +124 -0
- novel_downloader/core/searchers/tongrenquan.py +110 -0
- novel_downloader/core/searchers/ttkan.py +92 -0
- novel_downloader/core/searchers/xiaoshuowu.py +122 -0
- novel_downloader/core/searchers/xiguashuwu.py +95 -0
- novel_downloader/core/searchers/xs63b.py +104 -0
- novel_downloader/locales/en.json +36 -79
- novel_downloader/locales/zh.json +37 -80
- novel_downloader/models/__init__.py +23 -50
- novel_downloader/models/book.py +44 -0
- novel_downloader/models/config.py +16 -43
- novel_downloader/models/login.py +1 -1
- novel_downloader/models/search.py +21 -0
- novel_downloader/resources/config/settings.toml +39 -74
- novel_downloader/resources/css_styles/intro.css +83 -0
- novel_downloader/resources/css_styles/main.css +30 -89
- novel_downloader/resources/json/xiguashuwu.json +718 -0
- novel_downloader/utils/__init__.py +43 -0
- novel_downloader/utils/chapter_storage.py +247 -226
- novel_downloader/utils/constants.py +5 -50
- novel_downloader/utils/cookies.py +6 -18
- novel_downloader/utils/crypto_utils/__init__.py +13 -0
- novel_downloader/utils/crypto_utils/aes_util.py +90 -0
- novel_downloader/utils/crypto_utils/aes_v1.py +619 -0
- novel_downloader/utils/crypto_utils/aes_v2.py +1143 -0
- novel_downloader/utils/{crypto_utils.py → crypto_utils/rc4.py} +3 -10
- novel_downloader/utils/epub/__init__.py +34 -0
- novel_downloader/utils/epub/builder.py +377 -0
- novel_downloader/utils/epub/constants.py +118 -0
- novel_downloader/utils/epub/documents.py +297 -0
- novel_downloader/utils/epub/models.py +120 -0
- novel_downloader/utils/epub/utils.py +179 -0
- novel_downloader/utils/file_utils/__init__.py +5 -30
- novel_downloader/utils/file_utils/io.py +9 -150
- novel_downloader/utils/file_utils/normalize.py +2 -2
- novel_downloader/utils/file_utils/sanitize.py +2 -7
- novel_downloader/utils/fontocr.py +207 -0
- novel_downloader/utils/i18n.py +2 -0
- novel_downloader/utils/logger.py +10 -16
- novel_downloader/utils/network.py +111 -252
- novel_downloader/utils/state.py +5 -90
- novel_downloader/utils/text_utils/__init__.py +16 -21
- novel_downloader/utils/text_utils/diff_display.py +6 -9
- novel_downloader/utils/text_utils/numeric_conversion.py +253 -0
- novel_downloader/utils/text_utils/text_cleaner.py +179 -0
- novel_downloader/utils/text_utils/truncate_utils.py +62 -0
- novel_downloader/utils/time_utils/__init__.py +6 -12
- novel_downloader/utils/time_utils/datetime_utils.py +23 -33
- novel_downloader/utils/time_utils/sleep_utils.py +5 -10
- novel_downloader/web/__init__.py +13 -0
- novel_downloader/web/components/__init__.py +11 -0
- novel_downloader/web/components/navigation.py +35 -0
- novel_downloader/web/main.py +66 -0
- novel_downloader/web/pages/__init__.py +17 -0
- novel_downloader/web/pages/download.py +78 -0
- novel_downloader/web/pages/progress.py +147 -0
- novel_downloader/web/pages/search.py +329 -0
- novel_downloader/web/services/__init__.py +17 -0
- novel_downloader/web/services/client_dialog.py +164 -0
- novel_downloader/web/services/cred_broker.py +113 -0
- novel_downloader/web/services/cred_models.py +35 -0
- novel_downloader/web/services/task_manager.py +264 -0
- novel_downloader-2.0.0.dist-info/METADATA +171 -0
- novel_downloader-2.0.0.dist-info/RECORD +210 -0
- {novel_downloader-1.4.5.dist-info → novel_downloader-2.0.0.dist-info}/entry_points.txt +1 -1
- novel_downloader/config/site_rules.py +0 -94
- novel_downloader/core/downloaders/biquge.py +0 -25
- novel_downloader/core/downloaders/esjzone.py +0 -25
- novel_downloader/core/downloaders/linovelib.py +0 -25
- novel_downloader/core/downloaders/sfacg.py +0 -25
- novel_downloader/core/downloaders/yamibo.py +0 -25
- novel_downloader/core/exporters/biquge.py +0 -25
- novel_downloader/core/exporters/esjzone.py +0 -25
- novel_downloader/core/exporters/qianbi.py +0 -25
- novel_downloader/core/exporters/sfacg.py +0 -25
- novel_downloader/core/exporters/yamibo.py +0 -25
- novel_downloader/core/factory/__init__.py +0 -20
- novel_downloader/core/factory/downloader.py +0 -73
- novel_downloader/core/factory/exporter.py +0 -58
- novel_downloader/core/factory/fetcher.py +0 -96
- novel_downloader/core/factory/parser.py +0 -86
- novel_downloader/core/fetchers/base/__init__.py +0 -14
- novel_downloader/core/fetchers/base/browser.py +0 -403
- novel_downloader/core/fetchers/biquge/__init__.py +0 -14
- novel_downloader/core/fetchers/common/__init__.py +0 -14
- novel_downloader/core/fetchers/esjzone/__init__.py +0 -14
- novel_downloader/core/fetchers/esjzone/browser.py +0 -204
- novel_downloader/core/fetchers/linovelib/__init__.py +0 -14
- novel_downloader/core/fetchers/linovelib/browser.py +0 -193
- novel_downloader/core/fetchers/qianbi/__init__.py +0 -14
- novel_downloader/core/fetchers/qidian/__init__.py +0 -14
- novel_downloader/core/fetchers/qidian/browser.py +0 -318
- novel_downloader/core/fetchers/sfacg/__init__.py +0 -14
- novel_downloader/core/fetchers/sfacg/browser.py +0 -189
- novel_downloader/core/fetchers/yamibo/__init__.py +0 -14
- novel_downloader/core/fetchers/yamibo/browser.py +0 -229
- novel_downloader/core/parsers/biquge/__init__.py +0 -10
- novel_downloader/core/parsers/biquge/main_parser.py +0 -134
- novel_downloader/core/parsers/common/__init__.py +0 -13
- novel_downloader/core/parsers/common/helper.py +0 -323
- novel_downloader/core/parsers/common/main_parser.py +0 -106
- novel_downloader/core/parsers/esjzone/__init__.py +0 -10
- novel_downloader/core/parsers/linovelib/__init__.py +0 -10
- novel_downloader/core/parsers/qianbi/__init__.py +0 -10
- novel_downloader/core/parsers/sfacg/__init__.py +0 -10
- novel_downloader/core/parsers/yamibo/__init__.py +0 -10
- novel_downloader/core/parsers/yamibo/main_parser.py +0 -194
- novel_downloader/models/browser.py +0 -21
- novel_downloader/models/chapter.py +0 -25
- novel_downloader/models/site_rules.py +0 -99
- novel_downloader/models/tasks.py +0 -33
- novel_downloader/models/types.py +0 -15
- novel_downloader/resources/css_styles/volume-intro.css +0 -56
- novel_downloader/resources/json/replace_word_map.json +0 -4
- novel_downloader/resources/text/blacklist.txt +0 -22
- novel_downloader/tui/__init__.py +0 -7
- novel_downloader/tui/app.py +0 -32
- novel_downloader/tui/main.py +0 -17
- novel_downloader/tui/screens/__init__.py +0 -14
- novel_downloader/tui/screens/home.py +0 -198
- novel_downloader/tui/screens/login.py +0 -74
- novel_downloader/tui/styles/home_layout.tcss +0 -79
- novel_downloader/tui/widgets/richlog_handler.py +0 -24
- novel_downloader/utils/cache.py +0 -24
- novel_downloader/utils/fontocr/__init__.py +0 -22
- novel_downloader/utils/fontocr/model_loader.py +0 -69
- novel_downloader/utils/fontocr/ocr_v1.py +0 -303
- novel_downloader/utils/fontocr/ocr_v2.py +0 -752
- novel_downloader/utils/hash_store.py +0 -279
- novel_downloader/utils/hash_utils.py +0 -103
- novel_downloader/utils/text_utils/chapter_formatting.py +0 -46
- novel_downloader/utils/text_utils/font_mapping.py +0 -28
- novel_downloader/utils/text_utils/text_cleaning.py +0 -107
- novel_downloader-1.4.5.dist-info/METADATA +0 -196
- novel_downloader-1.4.5.dist-info/RECORD +0 -165
- {novel_downloader-1.4.5.dist-info → novel_downloader-2.0.0.dist-info}/WHEEL +0 -0
- {novel_downloader-1.4.5.dist-info → novel_downloader-2.0.0.dist-info}/licenses/LICENSE +0 -0
- {novel_downloader-1.4.5.dist-info → novel_downloader-2.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,95 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
novel_downloader.core.searchers.xiguashuwu
|
4
|
+
------------------------------------------
|
5
|
+
|
6
|
+
"""
|
7
|
+
|
8
|
+
import logging
|
9
|
+
|
10
|
+
from lxml import html
|
11
|
+
|
12
|
+
from novel_downloader.core.searchers.base import BaseSearcher
|
13
|
+
from novel_downloader.core.searchers.registry import register_searcher
|
14
|
+
from novel_downloader.models import SearchResult
|
15
|
+
|
16
|
+
logger = logging.getLogger(__name__)
|
17
|
+
|
18
|
+
|
19
|
+
@register_searcher(
|
20
|
+
site_keys=["xiguashuwu"],
|
21
|
+
)
|
22
|
+
class XiguashuwuSearcher(BaseSearcher):
|
23
|
+
site_name = "xiguashuwu"
|
24
|
+
priority = 500
|
25
|
+
BASE_URL = "https://www.xiguashuwu.com"
|
26
|
+
SEARCH_URL = "https://www.xiguashuwu.com/search/{query}"
|
27
|
+
|
28
|
+
@classmethod
|
29
|
+
async def _fetch_html(cls, keyword: str) -> str:
|
30
|
+
url = cls.SEARCH_URL.format(query=cls._quote(keyword))
|
31
|
+
headers = {
|
32
|
+
"Referer": "https://www.xiguashuwu.com/search/",
|
33
|
+
}
|
34
|
+
try:
|
35
|
+
async with (await cls._http_get(url, headers=headers)) as resp:
|
36
|
+
return await cls._response_to_str(resp)
|
37
|
+
except Exception:
|
38
|
+
logger.error(
|
39
|
+
"Failed to fetch HTML for keyword '%s' from '%s'",
|
40
|
+
keyword,
|
41
|
+
cls.SEARCH_URL,
|
42
|
+
)
|
43
|
+
return ""
|
44
|
+
|
45
|
+
@classmethod
|
46
|
+
def _parse_html(cls, html_str: str, limit: int | None = None) -> list[SearchResult]:
|
47
|
+
doc = html.fromstring(html_str)
|
48
|
+
rows = doc.xpath('//div[@class="SHsectionThree-middle"]/p')
|
49
|
+
results: list[SearchResult] = []
|
50
|
+
|
51
|
+
for idx, row in enumerate(rows):
|
52
|
+
href = cls._first_str(
|
53
|
+
row.xpath(".//a[starts-with(@href,'/book/')][1]/@href")
|
54
|
+
) or cls._first_str(row.xpath(".//a[1]/@href"))
|
55
|
+
if not href:
|
56
|
+
continue
|
57
|
+
|
58
|
+
if limit is not None and idx >= limit:
|
59
|
+
break
|
60
|
+
|
61
|
+
# '/book/184974/iszip/0/' -> "184974"
|
62
|
+
book_id = href.split("/book/")[-1].split("/")[0]
|
63
|
+
book_url = cls._abs_url(href)
|
64
|
+
|
65
|
+
title = (
|
66
|
+
cls._first_str(
|
67
|
+
row.xpath(".//a[starts-with(@href,'/book/')][1]//text()")
|
68
|
+
)
|
69
|
+
or cls._first_str(row.xpath(".//a[1]//text()"))
|
70
|
+
or "-"
|
71
|
+
)
|
72
|
+
|
73
|
+
author = (
|
74
|
+
cls._first_str(
|
75
|
+
row.xpath(".//a[starts-with(@href,'/writer/')][1]//text()")
|
76
|
+
)
|
77
|
+
or cls._first_str(row.xpath(".//a[2]//text()"))
|
78
|
+
or "-"
|
79
|
+
)
|
80
|
+
|
81
|
+
results.append(
|
82
|
+
SearchResult(
|
83
|
+
site=cls.site_name,
|
84
|
+
book_id=book_id,
|
85
|
+
book_url=book_url,
|
86
|
+
cover_url="-",
|
87
|
+
title=title,
|
88
|
+
author=author,
|
89
|
+
latest_chapter="-",
|
90
|
+
update_date="-",
|
91
|
+
word_count="-",
|
92
|
+
priority=cls.priority + idx,
|
93
|
+
)
|
94
|
+
)
|
95
|
+
return results
|
@@ -0,0 +1,104 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
novel_downloader.core.searchers.xs63b
|
4
|
+
-------------------------------------
|
5
|
+
|
6
|
+
"""
|
7
|
+
|
8
|
+
import logging
|
9
|
+
|
10
|
+
from lxml import html
|
11
|
+
|
12
|
+
from novel_downloader.core.searchers.base import BaseSearcher
|
13
|
+
from novel_downloader.core.searchers.registry import register_searcher
|
14
|
+
from novel_downloader.models import SearchResult
|
15
|
+
|
16
|
+
logger = logging.getLogger(__name__)
|
17
|
+
|
18
|
+
|
19
|
+
@register_searcher(
|
20
|
+
site_keys=["xs63b"],
|
21
|
+
)
|
22
|
+
class Xs63bSearcher(BaseSearcher):
|
23
|
+
site_name = "xs63b"
|
24
|
+
priority = 30
|
25
|
+
BASE_URL = "https://www.xs63b.com"
|
26
|
+
SEARCH_URL = "https://www.xs63b.com/search/"
|
27
|
+
|
28
|
+
@classmethod
|
29
|
+
async def _fetch_html(cls, keyword: str) -> str:
|
30
|
+
headers = {
|
31
|
+
"Host": "www.xs63b.com",
|
32
|
+
"Origin": "https://www.xs63b.com",
|
33
|
+
"Referer": "https://www.xs63b.com/",
|
34
|
+
}
|
35
|
+
try:
|
36
|
+
async with (await cls._http_get(cls.BASE_URL, headers=headers)) as resp:
|
37
|
+
base_html = await cls._response_to_str(resp)
|
38
|
+
data = {
|
39
|
+
"_token": cls._parse_token(base_html),
|
40
|
+
"kw": keyword,
|
41
|
+
}
|
42
|
+
async with (
|
43
|
+
await cls._http_post(cls.SEARCH_URL, data=data, headers=headers)
|
44
|
+
) as resp:
|
45
|
+
return await cls._response_to_str(resp)
|
46
|
+
except Exception:
|
47
|
+
logger.error(
|
48
|
+
"Failed to fetch HTML for keyword '%s' from '%s'",
|
49
|
+
keyword,
|
50
|
+
cls.SEARCH_URL,
|
51
|
+
)
|
52
|
+
return ""
|
53
|
+
|
54
|
+
@classmethod
|
55
|
+
def _parse_html(cls, html_str: str, limit: int | None = None) -> list[SearchResult]:
|
56
|
+
doc = html.fromstring(html_str)
|
57
|
+
rows = doc.xpath("//div[@class='toplist']/ul/li")
|
58
|
+
results: list[SearchResult] = []
|
59
|
+
|
60
|
+
for idx, row in enumerate(rows):
|
61
|
+
href = cls._first_str(row.xpath(".//p[@class='s1']/a[1]/@href"))
|
62
|
+
if not href:
|
63
|
+
continue
|
64
|
+
|
65
|
+
if limit is not None and idx >= limit:
|
66
|
+
break
|
67
|
+
|
68
|
+
# 'https://www.xs63b.com/{catalog}/{name}/' -> "{catalog}-{name}"
|
69
|
+
book_id = href.split("xs63b.com", 1)[-1].strip(" /").replace("/", "-")
|
70
|
+
book_url = cls._abs_url(href)
|
71
|
+
|
72
|
+
title = "".join(row.xpath(".//p[@class='s1']//a//text()"))
|
73
|
+
|
74
|
+
latest_chapter = (
|
75
|
+
cls._first_str(row.xpath(".//p[@class='s2']//a/text()")) or "-"
|
76
|
+
)
|
77
|
+
author = cls._first_str(row.xpath(".//p[@class='s3']/text()")) or "-"
|
78
|
+
word_count = cls._first_str(row.xpath(".//p[@class='s4']/text()")) or "-"
|
79
|
+
update_date = cls._first_str(row.xpath(".//p[@class='s6']/text()")) or "-"
|
80
|
+
|
81
|
+
# Compute priority
|
82
|
+
prio = cls.priority + idx
|
83
|
+
|
84
|
+
results.append(
|
85
|
+
SearchResult(
|
86
|
+
site=cls.site_name,
|
87
|
+
book_id=book_id,
|
88
|
+
book_url=book_url,
|
89
|
+
cover_url="",
|
90
|
+
title=title,
|
91
|
+
author=author,
|
92
|
+
latest_chapter=latest_chapter,
|
93
|
+
update_date=update_date,
|
94
|
+
word_count=word_count,
|
95
|
+
priority=prio,
|
96
|
+
)
|
97
|
+
)
|
98
|
+
return results
|
99
|
+
|
100
|
+
@staticmethod
|
101
|
+
def _parse_token(html_str: str) -> str:
|
102
|
+
doc = html.fromstring(html_str)
|
103
|
+
vals = doc.xpath("//div[@id='search']//input[@name='_token']/@value")
|
104
|
+
return vals[0].strip() if vals else ""
|
novel_downloader/locales/en.json
CHANGED
@@ -1,111 +1,59 @@
|
|
1
1
|
{
|
2
|
-
"
|
2
|
+
"help_cli": "Novel Downloader CLI tool.",
|
3
3
|
"help_config": "Path to config file",
|
4
|
-
"help_download": "Download novels",
|
4
|
+
"help_download": "Download full novels by book IDs.",
|
5
5
|
"help_clean": "Clean cache and configuration files",
|
6
6
|
"help_export": "Export downloaded novels",
|
7
|
+
"help_search": "search for a book on one or more sites",
|
7
8
|
|
8
|
-
"
|
9
|
-
"
|
10
|
-
"
|
11
|
-
"
|
12
|
-
"
|
13
|
-
"
|
14
|
-
"
|
15
|
-
"
|
16
|
-
"
|
17
|
-
"
|
18
|
-
"
|
19
|
-
"
|
20
|
-
"
|
21
|
-
"settings_set_config": "Configuration file saved from {path}",
|
22
|
-
"settings_set_config_fail": "Failed to save config file: {err}",
|
23
|
-
"settings_update_rules": "Site rules updated from {path}",
|
24
|
-
"settings_update_rules_fail": "Failed to update site rules: {err}",
|
25
|
-
"settings_set_cookies_help": "Set cookies for a specific site.",
|
26
|
-
"settings_set_cookies_prompt_site": "Site identifier (e.g. 'qidian')",
|
27
|
-
"settings_set_cookies_prompt_payload": "Cookie payload (JSON or 'k=v; k2=v2')",
|
28
|
-
"settings_set_cookies_success": "Cookies for site '{site}' have been updated.",
|
29
|
-
"settings_set_cookies_fail": "Failed to set cookies: {err}",
|
30
|
-
"settings_add_hash_help": "Add image hashes manually or from file",
|
31
|
-
"settings_add_hash_path_help": "JSON path mapping image to label",
|
32
|
-
"settings_add_hash_prompt_img": "Image path",
|
33
|
-
"settings_add_hash_prompt_label": "Label for image",
|
34
|
-
"settings_add_hash_prompt_tip": "Enter image path and label. Leave blank or type 'exit' to finish.",
|
35
|
-
"settings_add_hash_path_invalid": "Path does not exist. Try again.",
|
36
|
-
"settings_add_hash_added": "Added image '{img}' under label '{label}'",
|
37
|
-
"settings_add_hash_failed": "Failed to add image: {err}",
|
38
|
-
"settings_add_hash_loaded": "Added images from mapping file: {path}",
|
39
|
-
"settings_add_hash_load_fail": "Failed to load image map: {err}",
|
40
|
-
"settings_add_hash_saved": "Image hash store saved",
|
9
|
+
"config_set_lang_help": "Switch language between Chinese and English.",
|
10
|
+
"config_set_config_help": "Set and save a custom YAML configuration file.",
|
11
|
+
"config_init_help": "Initialize default config and rule files in the current directory.",
|
12
|
+
"config_init_force_help": "Force overwrite if file already exists.",
|
13
|
+
"config_init_exists": "File already exists: {filename}",
|
14
|
+
"config_init_confirm_overwrite": "Do you want to overwrite {filename}?",
|
15
|
+
"config_init_skip": "Skipped: {filename}",
|
16
|
+
"config_init_copy": "Copied: {filename}",
|
17
|
+
"config_init_overwrite": "Overwriting existing file: {filename}",
|
18
|
+
"config_init_error": "Failed to copy {filename}: {err}",
|
19
|
+
"config_set_lang": "Language switched to {lang}",
|
20
|
+
"config_set_config": "Configuration file saved from {path}",
|
21
|
+
"config_set_config_fail": "Failed to save config file: {err}",
|
41
22
|
|
42
|
-
"
|
43
|
-
"interactive_option_download": "Download novels",
|
44
|
-
"interactive_browse_help": "Browse available novels interactively.",
|
45
|
-
"interactive_prompt_book_ids": "Enter book IDs to download, separated by spaces",
|
46
|
-
"interactive_preview_help": "Preview chapters before downloading.",
|
47
|
-
"interactive_no_sub": "No subcommand provided. Please select an action:",
|
48
|
-
"interactive_option_browse": "Browse novels",
|
49
|
-
"interactive_option_preview": "Preview chapters",
|
50
|
-
"interactive_option_exit": "Exit",
|
51
|
-
"interactive_prompt_choice": "Enter your choice",
|
52
|
-
"interactive_browse_start": "Starting interactive browser...",
|
53
|
-
"interactive_preview_start": "Previewing chapters...",
|
54
|
-
"interactive_exit": "Exiting.",
|
55
|
-
|
56
|
-
"download_help": "Download full novels by book IDs.",
|
57
|
-
"download_short_help": "Download novels",
|
23
|
+
"download_progress_prefix": "Download progress",
|
58
24
|
"download_option_site": "Website source, default is '{default}'.",
|
59
|
-
"download_using_config": "Using config: {path}",
|
60
25
|
"download_site_info": "Site: {site}",
|
61
|
-
"download_site_mode": "Mode: {mode}",
|
62
26
|
"download_no_ids": "No book IDs provided. Exiting.",
|
63
27
|
"download_fail_get_ids": "Failed to get book IDs from config: {err}",
|
28
|
+
"download_config_load_fail": "Failed to load config: {err}",
|
64
29
|
"download_only_example": "Only example book IDs found (e.g. '{example}').",
|
65
30
|
"download_edit_config": "Please edit your config and replace them with real book IDs.",
|
66
31
|
"download_downloading": "Downloading book {book_id} from {site}...",
|
67
|
-
"
|
32
|
+
"download_login_failed": "Download login failed: please check your cookies or account credentials and try again.",
|
68
33
|
"download_book_ids": "One or more book IDs to process",
|
69
34
|
"download_option_start": "Start chapter ID (applies to the first book ID only)",
|
70
35
|
"download_option_end": "End chapter ID (applies to the first book ID only)",
|
36
|
+
"download_option_no_export": "Do not export after download; download only.",
|
37
|
+
"download_export_skipped": "Download-only mode: export step will be skipped.",
|
38
|
+
|
71
39
|
"login_description": "Description",
|
72
40
|
"login_hint": "Hint",
|
73
|
-
"login_manual_prompt": ">> Please complete login in your browser and press Enter to continue...",
|
74
41
|
"login_use_config": "Using value from config.",
|
75
42
|
"login_enter_password": "Enter password: ",
|
76
43
|
"login_enter_cookie": "Enter cookies: ",
|
77
44
|
"login_enter_value": "Enter value: ",
|
78
45
|
"login_required_field": "This field is required. Please enter a value.",
|
79
46
|
|
80
|
-
"login_prompt_intro": "Manual login is required. Please switch to the browser and log in.",
|
81
|
-
"login_prompt_press_enter": "Attempt {attempt}/{max_retries}: Press Enter after completing login in the browser...",
|
82
|
-
|
83
|
-
"session_login_prompt_intro": "Failed to restore login from saved cookies. Please log in via browser, then paste the cookie string below.",
|
84
|
-
"session_login_prompt_paste_cookie": "Attempt {attempt}/{max_retries}: Paste your browser cookie string and press Enter:",
|
85
|
-
"session_login_prompt_invalid_cookie": "Invalid cookie. Please copy and paste again.",
|
86
|
-
"session_login_failed": "Login to {site} failed. Please check your credentials or try again later.",
|
87
|
-
|
88
47
|
"clean_logs": "Clean log directory",
|
89
48
|
"clean_cache": "Clean scripts and browser cache",
|
90
|
-
"
|
91
|
-
"
|
92
|
-
"clean_config": "Clean config files (e.g. settings.toml, site rules)",
|
93
|
-
"clean_models": "Clean downloaded model cache",
|
94
|
-
"clean_hf_cache": "Clear Hugging Face model cache",
|
95
|
-
"clean_hf_cache_done": "Hugging Face cache cleared",
|
96
|
-
"clean_hf_cache_fail": "Failed to clear Hugging Face cache: {err}",
|
97
|
-
"clean_hf_model": "Clear cached model from Hugging Face",
|
98
|
-
"clean_hf_model_done": "Cache for model '{repo}' has been cleared",
|
99
|
-
"clean_hf_model_not_found": "No local cache found for model '{repo}'",
|
100
|
-
"clean_hf_model_fail": "Failed to clear model cache: {err}",
|
101
|
-
"clean_hf_cache_all": "Clear all Hugging Face model and dataset cache",
|
102
|
-
"clean_hf_cache_all_done": "All Hugging Face cache cleared",
|
103
|
-
"clean_hf_cache_all_fail": "Failed to clear Hugging Face cache: {err}",
|
49
|
+
"clean_data": "Clean data files",
|
50
|
+
"clean_config": "Clean config files",
|
104
51
|
"clean_all": "Clean all settings, cache, and state files",
|
105
52
|
"clean_yes": "Skip confirmation prompt",
|
106
|
-
"clean_confirm": "Are you sure you want to delete all local data?
|
53
|
+
"clean_confirm": "Are you sure you want to delete all local data?",
|
107
54
|
"clean_nothing": "No clean option specified",
|
108
55
|
"clean_deleted": "Deleted",
|
56
|
+
"clean_failed": "Failed to delete: {path}",
|
109
57
|
"clean_not_found": "Not found",
|
110
58
|
"clean_cancelled": "Clean operation cancelled",
|
111
59
|
|
@@ -114,5 +62,14 @@
|
|
114
62
|
"export_success_txt": "Successfully exported {book_id} as TXT.",
|
115
63
|
"export_failed_txt": "Failed to export {book_id} as TXT: {err}",
|
116
64
|
"export_success_epub": "Successfully exported {book_id} as EPUB.",
|
117
|
-
"export_failed_epub": "Failed to export {book_id} as EPUB: {err}"
|
65
|
+
"export_failed_epub": "Failed to export {book_id} as EPUB: {err}",
|
66
|
+
|
67
|
+
"search_sites_help": "which site keys to search (default: all)",
|
68
|
+
"search_keyword_help": "keyword to look for",
|
69
|
+
"search_limit_help": "Maximum number of search results",
|
70
|
+
"search_site_limit_help": "Maximum number of search results per site",
|
71
|
+
"search_timeout_help": "Per-request timeout in seconds (default: {secs})",
|
72
|
+
|
73
|
+
"no_results": "No results found.",
|
74
|
+
"prompt_select_index": "Select a book by number (or press Enter to cancel)"
|
118
75
|
}
|
novel_downloader/locales/zh.json
CHANGED
@@ -1,109 +1,57 @@
|
|
1
1
|
{
|
2
|
-
"
|
2
|
+
"help_cli": "小说下载器 CLI 工具",
|
3
3
|
"help_config": "配置文件路径",
|
4
|
-
"help_download": "下载小说",
|
4
|
+
"help_download": "按书籍 ID 下载小说",
|
5
5
|
"help_clean": "清理缓存和配置文件",
|
6
6
|
"help_export": "导出已下载的小说",
|
7
|
+
"help_search": "在一个或多个站点搜索书籍",
|
7
8
|
|
8
|
-
"
|
9
|
-
"
|
10
|
-
"
|
11
|
-
"
|
12
|
-
"
|
13
|
-
"
|
14
|
-
"
|
15
|
-
"
|
16
|
-
"
|
17
|
-
"
|
18
|
-
"
|
19
|
-
"
|
20
|
-
"
|
21
|
-
"settings_set_config": "已从 {path} 保存配置文件",
|
22
|
-
"settings_set_config_fail": "保存配置文件失败: {err}",
|
23
|
-
"settings_update_rules": "已从 {path} 更新站点规则",
|
24
|
-
"settings_update_rules_fail": "更新站点规则失败: {err}",
|
25
|
-
"settings_set_cookies_help": "为特定站点设置 Cookie",
|
26
|
-
"settings_set_cookies_prompt_site": "站点标识 (例如 'qidian')",
|
27
|
-
"settings_set_cookies_prompt_payload": "Cookie 内容 (JSON 或 'k=v; k2=v2')",
|
28
|
-
"settings_set_cookies_success": "已更新站点 '{site}' 的 Cookie",
|
29
|
-
"settings_set_cookies_fail": "设置 Cookie 失败: {err}",
|
30
|
-
"settings_add_hash_help": "手动或通过文件添加图像哈希",
|
31
|
-
"settings_add_hash_path_help": "映射图片路径到标签的 JSON 文件路径",
|
32
|
-
"settings_add_hash_prompt_img": "图片路径",
|
33
|
-
"settings_add_hash_prompt_label": "该图片对应的标签",
|
34
|
-
"settings_add_hash_prompt_tip": "请输入图片路径和标签, 输入空行或 'exit' 退出",
|
35
|
-
"settings_add_hash_path_invalid": "路径不存在, 请重试",
|
36
|
-
"settings_add_hash_added": "已添加图片 '{img}', 标签为 '{label}'",
|
37
|
-
"settings_add_hash_failed": "添加图片失败: {err}",
|
38
|
-
"settings_add_hash_loaded": "已从映射文件导入图片: {path}",
|
39
|
-
"settings_add_hash_load_fail": "加载映射文件失败: {err}",
|
40
|
-
"settings_add_hash_saved": "图像哈希库已保存",
|
9
|
+
"config_set_lang_help": "在中文和英文之间切换语言",
|
10
|
+
"config_set_config_help": "设置并保存自定义 YAML 配置文件",
|
11
|
+
"config_init_help": "在当前目录初始化默认配置和规则文件",
|
12
|
+
"config_init_force_help": "如果文件已存在则强制覆盖",
|
13
|
+
"config_init_exists": "文件已存在: {filename}",
|
14
|
+
"config_init_confirm_overwrite": "你确定要覆盖 {filename} 吗?",
|
15
|
+
"config_init_skip": "文件已存在: {filename}, 已跳过",
|
16
|
+
"config_init_copy": "已复制默认文件: {filename}",
|
17
|
+
"config_init_overwrite": "已覆盖现有文件: {filename}",
|
18
|
+
"config_init_error": "复制 {filename} 失败: {err}",
|
19
|
+
"config_set_lang": "语言已切换为 {lang}",
|
20
|
+
"config_set_config": "已从 {path} 保存配置文件",
|
21
|
+
"config_set_config_fail": "保存配置文件失败: {err}",
|
41
22
|
|
42
|
-
"
|
43
|
-
"interactive_option_download": "下载小说",
|
44
|
-
"interactive_browse_help": "以交互方式浏览可用小说",
|
45
|
-
"interactive_prompt_book_ids": "请输入要下载的书籍 ID, 用空格分隔",
|
46
|
-
"interactive_preview_help": "下载前预览章节",
|
47
|
-
"interactive_no_sub": "未提供子命令请选择一项操作:",
|
48
|
-
"interactive_option_browse": "浏览小说",
|
49
|
-
"interactive_option_preview": "预览章节",
|
50
|
-
"interactive_option_exit": "退出",
|
51
|
-
"interactive_prompt_choice": "请输入你的选择",
|
52
|
-
"interactive_browse_start": "启动交互式浏览器...",
|
53
|
-
"interactive_preview_start": "正在预览章节...",
|
54
|
-
"interactive_exit": "正在退出",
|
55
|
-
|
56
|
-
"download_help": "按书籍 ID 下载完整小说",
|
57
|
-
"download_short_help": "下载小说",
|
23
|
+
"download_progress_prefix": "下载进度",
|
58
24
|
"download_option_site": "网站来源, 默认为 '{default}'",
|
59
|
-
"download_using_config": "使用配置: {path}",
|
60
25
|
"download_site_info": "站点: {site}",
|
61
|
-
"download_site_mode": "使用模式: {mode}",
|
62
26
|
"download_no_ids": "未提供书籍 ID, 正在退出",
|
63
27
|
"download_fail_get_ids": "从配置获取书籍 ID 失败: {err}",
|
28
|
+
"download_config_load_fail": "加载配置失败: {err}",
|
64
29
|
"download_only_example": "只发现示例书籍 ID (例如 '{example}')",
|
65
30
|
"download_edit_config": "请编辑配置并将示例 ID 替换为真实书籍 ID",
|
66
31
|
"download_downloading": "正在从 {site} 下载书籍 {book_id}...",
|
67
|
-
"
|
32
|
+
"download_login_failed": "登录失败: 请检查您的 Cookie 或账户信息后重试",
|
68
33
|
"download_book_ids": "要处理的一个或多个小说 ID",
|
69
34
|
"download_option_start": "起始章节 ID (仅用于第一个书籍 ID)",
|
70
35
|
"download_option_end": "结束章节 ID (仅用于第一个书籍 ID)",
|
36
|
+
"download_option_no_export": "仅下载 (不进行导出)",
|
37
|
+
"download_export_skipped": "仅下载模式: 将跳过导出步骤",
|
38
|
+
|
71
39
|
"login_description": "说明",
|
72
40
|
"login_hint": "提示",
|
73
|
-
"login_manual_prompt": ">> 请在浏览器中完成登录后按回车继续...",
|
74
41
|
"login_use_config": "使用配置中的默认值",
|
75
42
|
"login_enter_password": "请输入密码: ",
|
76
43
|
"login_enter_cookie": "请输入 Cookie: ",
|
77
44
|
"login_enter_value": "请输入值: ",
|
78
45
|
"login_required_field": "该字段是必填项, 请重新输入",
|
79
46
|
|
80
|
-
"
|
81
|
-
"login_prompt_press_enter": "第 {attempt}/{max_retries} 次尝试: 请在浏览器中完成登录后按回车键...",
|
82
|
-
|
83
|
-
"session_login_prompt_intro": "尝试使用历史 Cookie 恢复登录失败, 请在浏览器登录后从开发者工具复制 Cookie 粘贴至下方",
|
84
|
-
"session_login_prompt_paste_cookie": "第 {attempt}/{max_retries} 次尝试, 请粘贴 Cookie 字符串并回车:",
|
85
|
-
"session_login_prompt_invalid_cookie": "Cookie 格式不正确, 请重新复制粘贴",
|
86
|
-
"session_login_failed": "登录 {site} 失败, 请检查账号或稍后再试",
|
87
|
-
|
47
|
+
"clean_failed": "删除失败: {path}",
|
88
48
|
"clean_logs": "清理日志目录",
|
89
49
|
"clean_cache": "清理脚本和浏览器缓存",
|
90
|
-
"
|
91
|
-
"
|
92
|
-
"clean_config": "清理配置文件 (如 settings.toml 和规则)",
|
93
|
-
"clean_models": "清理模型缓存目录",
|
94
|
-
"clean_hf_cache": "清理 Hugging Face 下载缓存",
|
95
|
-
"clean_hf_cache_done": "已清除 Hugging Face 本地缓存",
|
96
|
-
"clean_hf_cache_fail": "清除 Hugging Face 缓存失败: {err}",
|
97
|
-
"clean_hf_model": "清理 Hugging Face 中模型缓存",
|
98
|
-
"clean_hf_model_done": "已清除模型 '{repo}' 的缓存",
|
99
|
-
"clean_hf_model_not_found": "未发现模型 '{repo}' 的本地缓存",
|
100
|
-
"clean_hf_model_fail": "清理模型缓存失败: {err}",
|
101
|
-
"clean_hf_cache_all": "清理所有 Hugging Face 模型与数据集缓存",
|
102
|
-
"clean_hf_cache_all_done": "已清除所有 Hugging Face 缓存",
|
103
|
-
"clean_hf_cache_all_fail": "清除 Hugging Face 缓存失败: {err}",
|
50
|
+
"clean_data": "清理数据文件",
|
51
|
+
"clean_config": "清理配置文件",
|
104
52
|
"clean_all": "清理所有配置, 缓存与状态",
|
105
53
|
"clean_yes": "跳过确认提示",
|
106
|
-
"clean_confirm": "确认要删除所有本地数据?
|
54
|
+
"clean_confirm": "确认要删除所有本地数据?",
|
107
55
|
"clean_nothing": "未指定任何要清理的内容",
|
108
56
|
"clean_deleted": "已删除",
|
109
57
|
"clean_not_found": "未找到",
|
@@ -111,8 +59,17 @@
|
|
111
59
|
|
112
60
|
"export_format_help": "导出格式: txt、epub 或 all",
|
113
61
|
"export_processing": "正在导出小说 {book_id}, 格式: {format}",
|
114
|
-
"export_success_txt": "成功将 {book_id} 导出为 TXT
|
62
|
+
"export_success_txt": "成功将 {book_id} 导出为 TXT",
|
115
63
|
"export_failed_txt": "导出 {book_id} 为 TXT 失败: {err}",
|
116
64
|
"export_success_epub": "成功将 {book_id} 导出为 EPUB",
|
117
|
-
"export_failed_epub": "导出 {book_id} 为 EPUB 失败: {err}"
|
65
|
+
"export_failed_epub": "导出 {book_id} 为 EPUB 失败: {err}",
|
66
|
+
|
67
|
+
"search_sites_help": "要搜索的站点键 (默认为全部)",
|
68
|
+
"search_keyword_help": "要搜索的关键字",
|
69
|
+
"search_limit_help": "总体搜索结果数量上限",
|
70
|
+
"search_site_limit_help": "单站点搜索结果数量上限",
|
71
|
+
"search_timeout_help": "每次请求的超时时间 (秒) (默认:{secs})",
|
72
|
+
|
73
|
+
"no_results": "未找到结果",
|
74
|
+
"prompt_select_index": "通过编号选择书籍 (或按回车取消)"
|
118
75
|
}
|
@@ -3,64 +3,37 @@
|
|
3
3
|
novel_downloader.models
|
4
4
|
-----------------------
|
5
5
|
|
6
|
+
Data models and configuration classes.
|
6
7
|
"""
|
7
8
|
|
8
|
-
from .browser import NewContextOptions
|
9
|
-
from .chapter import ChapterDict
|
10
|
-
from .config import (
|
11
|
-
BookConfig,
|
12
|
-
DownloaderConfig,
|
13
|
-
ExporterConfig,
|
14
|
-
FetcherConfig,
|
15
|
-
ParserConfig,
|
16
|
-
)
|
17
|
-
from .login import LoginField
|
18
|
-
from .site_rules import (
|
19
|
-
BookInfoRules,
|
20
|
-
FieldRules,
|
21
|
-
RuleStep,
|
22
|
-
SiteProfile,
|
23
|
-
SiteRules,
|
24
|
-
SiteRulesDict,
|
25
|
-
VolumesRules,
|
26
|
-
)
|
27
|
-
from .tasks import (
|
28
|
-
CidTask,
|
29
|
-
HtmlTask,
|
30
|
-
RestoreTask,
|
31
|
-
)
|
32
|
-
from .types import (
|
33
|
-
BrowserType,
|
34
|
-
LogLevel,
|
35
|
-
ModeType,
|
36
|
-
SaveMode,
|
37
|
-
SplitMode,
|
38
|
-
StorageBackend,
|
39
|
-
)
|
40
|
-
|
41
9
|
__all__ = [
|
42
|
-
"NewContextOptions",
|
43
10
|
"BookConfig",
|
44
11
|
"DownloaderConfig",
|
45
12
|
"ParserConfig",
|
46
13
|
"FetcherConfig",
|
47
14
|
"ExporterConfig",
|
15
|
+
"TextCleanerConfig",
|
16
|
+
"BookInfoDict",
|
48
17
|
"ChapterDict",
|
18
|
+
"ChapterInfoDict",
|
19
|
+
"VolumeInfoDict",
|
49
20
|
"LoginField",
|
50
|
-
"
|
51
|
-
"ModeType",
|
52
|
-
"SaveMode",
|
53
|
-
"StorageBackend",
|
54
|
-
"SplitMode",
|
55
|
-
"LogLevel",
|
56
|
-
"BookInfoRules",
|
57
|
-
"FieldRules",
|
58
|
-
"RuleStep",
|
59
|
-
"SiteProfile",
|
60
|
-
"SiteRules",
|
61
|
-
"SiteRulesDict",
|
62
|
-
"VolumesRules",
|
63
|
-
"CidTask",
|
64
|
-
"HtmlTask",
|
65
|
-
"RestoreTask",
|
21
|
+
"SearchResult",
|
66
22
|
]
|
23
|
+
|
24
|
+
from .book import (
|
25
|
+
BookInfoDict,
|
26
|
+
ChapterDict,
|
27
|
+
ChapterInfoDict,
|
28
|
+
VolumeInfoDict,
|
29
|
+
)
|
30
|
+
from .config import (
|
31
|
+
BookConfig,
|
32
|
+
DownloaderConfig,
|
33
|
+
ExporterConfig,
|
34
|
+
FetcherConfig,
|
35
|
+
ParserConfig,
|
36
|
+
TextCleanerConfig,
|
37
|
+
)
|
38
|
+
from .login import LoginField
|
39
|
+
from .search import SearchResult
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
novel_downloader.models.book
|
4
|
+
----------------------------
|
5
|
+
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Any, NotRequired, TypedDict
|
9
|
+
|
10
|
+
|
11
|
+
class ChapterDict(TypedDict):
|
12
|
+
id: str
|
13
|
+
title: str
|
14
|
+
content: str
|
15
|
+
extra: dict[str, Any]
|
16
|
+
|
17
|
+
|
18
|
+
class ChapterInfoDict(TypedDict):
|
19
|
+
title: str
|
20
|
+
url: str
|
21
|
+
chapterId: str
|
22
|
+
|
23
|
+
|
24
|
+
class VolumeInfoDict(TypedDict):
|
25
|
+
volume_name: str
|
26
|
+
volume_cover: NotRequired[str]
|
27
|
+
update_time: NotRequired[str]
|
28
|
+
word_count: NotRequired[str]
|
29
|
+
volume_intro: NotRequired[str]
|
30
|
+
chapters: list[ChapterInfoDict]
|
31
|
+
|
32
|
+
|
33
|
+
class BookInfoDict(TypedDict):
|
34
|
+
book_name: str
|
35
|
+
author: str
|
36
|
+
cover_url: str
|
37
|
+
update_time: str
|
38
|
+
summary: str
|
39
|
+
extra: dict[str, Any]
|
40
|
+
volumes: list[VolumeInfoDict]
|
41
|
+
tags: NotRequired[list[str]]
|
42
|
+
word_count: NotRequired[str]
|
43
|
+
serial_status: NotRequired[str]
|
44
|
+
summary_brief: NotRequired[str]
|