novel-downloader 1.3.2__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 -44
- 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 +40 -52
- novel_downloader/core/{savers/common/main_saver.py → exporters/common/main_exporter.py} +36 -39
- novel_downloader/core/{savers → exporters}/common/txt.py +20 -24
- novel_downloader/core/exporters/epub_utils/__init__.py +40 -0
- novel_downloader/core/{savers → exporters}/epub_utils/css_builder.py +2 -1
- novel_downloader/core/exporters/epub_utils/image_loader.py +131 -0
- novel_downloader/core/{savers → exporters}/epub_utils/initializer.py +6 -3
- novel_downloader/core/{savers → exporters}/epub_utils/text_to_html.py +49 -2
- novel_downloader/core/{savers → exporters}/epub_utils/volume_intro.py +2 -1
- 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 +24 -17
- 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 +15 -5
- novel_downloader/utils/cookies.py +66 -0
- novel_downloader/utils/crypto_utils.py +1 -74
- novel_downloader/utils/file_utils/io.py +1 -1
- 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 +53 -39
- 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 +3 -3
- {novel_downloader-1.3.2.dist-info → novel_downloader-1.4.0.dist-info}/METADATA +72 -38
- novel_downloader-1.4.0.dist-info/RECORD +170 -0
- {novel_downloader-1.3.2.dist-info → novel_downloader-1.4.0.dist-info}/WHEEL +1 -1
- {novel_downloader-1.3.2.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 -218
- novel_downloader/core/downloaders/common/common_sync.py +0 -210
- 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 -227
- 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/epub_utils/__init__.py +0 -26
- 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.2.dist-info/RECORD +0 -165
- {novel_downloader-1.3.2.dist-info → novel_downloader-1.4.0.dist-info}/licenses/LICENSE +0 -0
- {novel_downloader-1.3.2.dist-info → novel_downloader-1.4.0.dist-info}/top_level.txt +0 -0
@@ -1,36 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
novel_downloader.core.interfaces.sync_downloader
|
4
|
-
------------------------------------------------
|
5
|
-
|
6
|
-
This module defines the SyncDownloaderProtocol, a structural interface
|
7
|
-
that outlines the expected behavior of any downloader class.
|
8
|
-
"""
|
9
|
-
|
10
|
-
from typing import Protocol
|
11
|
-
|
12
|
-
|
13
|
-
class SyncDownloaderProtocol(Protocol):
|
14
|
-
"""
|
15
|
-
Protocol for downloader classes.
|
16
|
-
|
17
|
-
Defines the expected interface for any downloader implementation,
|
18
|
-
including both batch and single book downloads,
|
19
|
-
as well as optional pre-download hooks.
|
20
|
-
"""
|
21
|
-
|
22
|
-
def download(self, book_ids: list[str]) -> None:
|
23
|
-
"""
|
24
|
-
Batch download entry point.
|
25
|
-
|
26
|
-
:param book_ids: List of book IDs to download.
|
27
|
-
"""
|
28
|
-
...
|
29
|
-
|
30
|
-
def download_one(self, book_id: str) -> None:
|
31
|
-
"""
|
32
|
-
Download logic for a single book.
|
33
|
-
|
34
|
-
:param book_id: The identifier of the book.
|
35
|
-
"""
|
36
|
-
...
|
@@ -1,82 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
novel_downloader.core.interfaces.sync_requester
|
4
|
-
-----------------------------------------------
|
5
|
-
|
6
|
-
Defines the RequesterProtocol interface for fetching raw HTML or JSON
|
7
|
-
for book info pages, individual chapters, managing request lifecycle,
|
8
|
-
and optionally retrieving a user's authenticated bookcase.
|
9
|
-
"""
|
10
|
-
|
11
|
-
from typing import Any, Literal, Protocol, runtime_checkable
|
12
|
-
|
13
|
-
|
14
|
-
@runtime_checkable
|
15
|
-
class SyncRequesterProtocol(Protocol):
|
16
|
-
"""
|
17
|
-
A requester must be able to fetch raw HTML/data for:
|
18
|
-
- a book's info page,
|
19
|
-
- a specific chapter page.
|
20
|
-
"""
|
21
|
-
|
22
|
-
def is_async(self) -> Literal[False]:
|
23
|
-
...
|
24
|
-
|
25
|
-
def login(
|
26
|
-
self,
|
27
|
-
username: str = "",
|
28
|
-
password: str = "",
|
29
|
-
manual_login: bool = False,
|
30
|
-
**kwargs: Any,
|
31
|
-
) -> bool:
|
32
|
-
"""
|
33
|
-
Attempt to log in
|
34
|
-
"""
|
35
|
-
...
|
36
|
-
|
37
|
-
def get_book_info(
|
38
|
-
self,
|
39
|
-
book_id: str,
|
40
|
-
**kwargs: Any,
|
41
|
-
) -> list[str]:
|
42
|
-
"""
|
43
|
-
Fetch the raw HTML (or JSON) of the book info page.
|
44
|
-
|
45
|
-
:param book_id: The book identifier.
|
46
|
-
:return: The page content as a string.
|
47
|
-
"""
|
48
|
-
...
|
49
|
-
|
50
|
-
def get_book_chapter(
|
51
|
-
self,
|
52
|
-
book_id: str,
|
53
|
-
chapter_id: str,
|
54
|
-
**kwargs: Any,
|
55
|
-
) -> list[str]:
|
56
|
-
"""
|
57
|
-
Fetch the raw HTML (or JSON) of a single chapter.
|
58
|
-
|
59
|
-
:param book_id: The book identifier.
|
60
|
-
:param chapter_id: The chapter identifier.
|
61
|
-
:return: The chapter content as a string.
|
62
|
-
"""
|
63
|
-
...
|
64
|
-
|
65
|
-
def get_bookcase(
|
66
|
-
self,
|
67
|
-
page: int = 1,
|
68
|
-
**kwargs: Any,
|
69
|
-
) -> list[str]:
|
70
|
-
"""
|
71
|
-
Optional: Retrieve the HTML content of the authenticated user's bookcase page.
|
72
|
-
|
73
|
-
:param page: Page idx
|
74
|
-
:return: The HTML markup of the bookcase page.
|
75
|
-
"""
|
76
|
-
...
|
77
|
-
|
78
|
-
def close(self) -> None:
|
79
|
-
"""
|
80
|
-
Shutdown and cleans up resources.
|
81
|
-
"""
|
82
|
-
...
|
@@ -1,12 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
novel_downloader.core.parsers.qidian.browser
|
4
|
-
--------------------------------------------
|
5
|
-
|
6
|
-
This package provides parsing components for handling Qidian
|
7
|
-
pages that have been rendered by a browser engine.
|
8
|
-
"""
|
9
|
-
|
10
|
-
from .main_parser import QidianBrowserParser
|
11
|
-
|
12
|
-
__all__ = ["QidianBrowserParser"]
|
@@ -1,93 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
novel_downloader.core.parsers.qidian.browser.chapter_normal
|
4
|
-
-----------------------------------------------------------
|
5
|
-
|
6
|
-
Parser logic for extracting readable text from Qidian chapters
|
7
|
-
that use plain (non-encrypted) browser-rendered HTML.
|
8
|
-
"""
|
9
|
-
|
10
|
-
import logging
|
11
|
-
|
12
|
-
from bs4 import BeautifulSoup
|
13
|
-
|
14
|
-
from novel_downloader.utils.chapter_storage import ChapterDict
|
15
|
-
|
16
|
-
from ..shared import (
|
17
|
-
extract_chapter_info,
|
18
|
-
find_ssr_page_context,
|
19
|
-
)
|
20
|
-
|
21
|
-
logger = logging.getLogger(__name__)
|
22
|
-
|
23
|
-
|
24
|
-
def parse_normal_chapter(
|
25
|
-
soup: BeautifulSoup,
|
26
|
-
chapter_id: str,
|
27
|
-
) -> ChapterDict | None:
|
28
|
-
"""
|
29
|
-
Extract and format the chapter text from a normal Qidian page.
|
30
|
-
Returns empty string if VIP/encrypted.
|
31
|
-
|
32
|
-
This method performs the following steps:
|
33
|
-
1. Parses HTML into soup.
|
34
|
-
2. Skips parsing if VIP or encrypted.
|
35
|
-
3. Locates main content container.
|
36
|
-
4. Extracts SSR-rendered chapter info (title, author note).
|
37
|
-
5. Removes review spans.
|
38
|
-
6. Extracts paragraph texts and formats them.
|
39
|
-
|
40
|
-
:param html_str: Raw HTML content of the chapter page.
|
41
|
-
:return: Formatted chapter text or empty string if not parsable.
|
42
|
-
"""
|
43
|
-
try:
|
44
|
-
main = soup.select_one("div#app div#reader-content main")
|
45
|
-
if not main:
|
46
|
-
logger.warning("[Parser] Main content not found for chapter")
|
47
|
-
return None
|
48
|
-
|
49
|
-
ssr_data = find_ssr_page_context(soup)
|
50
|
-
chapter_info = extract_chapter_info(ssr_data)
|
51
|
-
if not chapter_info:
|
52
|
-
logger.warning(
|
53
|
-
"[Parser] ssr_chapterInfo not found for chapter '%s'", chapter_id
|
54
|
-
)
|
55
|
-
return None
|
56
|
-
|
57
|
-
title = chapter_info.get("chapterName", "Untitled")
|
58
|
-
chapter_id = chapter_info.get("chapterId", "")
|
59
|
-
author_say = chapter_info.get("authorSay", "")
|
60
|
-
update_time = chapter_info.get("updateTime", "")
|
61
|
-
update_timestamp = chapter_info.get("updateTimestamp", 0)
|
62
|
-
modify_time = chapter_info.get("modifyTime", 0)
|
63
|
-
word_count = chapter_info.get("wordsCount", 0)
|
64
|
-
seq = chapter_info.get("seq", None)
|
65
|
-
volume = chapter_info.get("extra", {}).get("volumeName", "")
|
66
|
-
|
67
|
-
# remove review spans
|
68
|
-
for span in main.select("span.review"):
|
69
|
-
span.decompose()
|
70
|
-
|
71
|
-
paras = [p.get_text(strip=True) for p in main.find_all("p")]
|
72
|
-
chapter_text = "\n\n".join(paras)
|
73
|
-
|
74
|
-
return {
|
75
|
-
"id": str(chapter_id),
|
76
|
-
"title": title,
|
77
|
-
"content": chapter_text,
|
78
|
-
"extra": {
|
79
|
-
"author_say": author_say.strip() if author_say else "",
|
80
|
-
"updated_at": update_time,
|
81
|
-
"update_timestamp": update_timestamp,
|
82
|
-
"modify_time": modify_time,
|
83
|
-
"word_count": word_count,
|
84
|
-
"seq": seq,
|
85
|
-
"volume": volume,
|
86
|
-
},
|
87
|
-
}
|
88
|
-
|
89
|
-
except Exception as e:
|
90
|
-
logger.warning(
|
91
|
-
"[Parser] parse error for normal chapter '%s': %s", chapter_id, e
|
92
|
-
)
|
93
|
-
return None
|
@@ -1,71 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
novel_downloader.core.parsers.qidian.browser.chapter_router
|
4
|
-
-----------------------------------------------------------
|
5
|
-
|
6
|
-
Routing logic for selecting the correct chapter parser for Qidian browser pages.
|
7
|
-
|
8
|
-
This module acts as a dispatcher that analyzes a chapter's HTML content and
|
9
|
-
routes the parsing task to either the encrypted or normal chapter parser.
|
10
|
-
"""
|
11
|
-
|
12
|
-
from __future__ import annotations
|
13
|
-
|
14
|
-
import logging
|
15
|
-
from typing import TYPE_CHECKING
|
16
|
-
|
17
|
-
from novel_downloader.utils.chapter_storage import ChapterDict
|
18
|
-
|
19
|
-
from ..shared import (
|
20
|
-
can_view_chapter,
|
21
|
-
html_to_soup,
|
22
|
-
is_encrypted,
|
23
|
-
)
|
24
|
-
from .chapter_normal import parse_normal_chapter
|
25
|
-
|
26
|
-
if TYPE_CHECKING:
|
27
|
-
from .main_parser import QidianBrowserParser
|
28
|
-
|
29
|
-
logger = logging.getLogger(__name__)
|
30
|
-
|
31
|
-
|
32
|
-
def parse_chapter(
|
33
|
-
parser: QidianBrowserParser,
|
34
|
-
html_str: str,
|
35
|
-
chapter_id: str,
|
36
|
-
) -> ChapterDict | None:
|
37
|
-
"""
|
38
|
-
Extract and return the formatted textual content of chapter.
|
39
|
-
|
40
|
-
:param parser: Instance of QidianBrowserParser.
|
41
|
-
:param html_str: Raw HTML content of the chapter page.
|
42
|
-
:param chapter_id: Identifier of the chapter being parsed.
|
43
|
-
:return: Formatted chapter text or empty string if not parsable.
|
44
|
-
"""
|
45
|
-
try:
|
46
|
-
soup = html_to_soup(html_str)
|
47
|
-
|
48
|
-
if not can_view_chapter(soup):
|
49
|
-
logger.warning(
|
50
|
-
"[Parser] Chapter '%s' is not purchased or inaccessible.", chapter_id
|
51
|
-
)
|
52
|
-
return None
|
53
|
-
|
54
|
-
if is_encrypted(soup):
|
55
|
-
if not parser._decode_font:
|
56
|
-
return None
|
57
|
-
try:
|
58
|
-
from .chapter_encrypted import parse_encrypted_chapter
|
59
|
-
|
60
|
-
return parse_encrypted_chapter(parser, soup, chapter_id)
|
61
|
-
except ImportError:
|
62
|
-
logger.warning(
|
63
|
-
"[Parser] Encrypted chapter '%s' requires extra dependencies.",
|
64
|
-
chapter_id,
|
65
|
-
)
|
66
|
-
return None
|
67
|
-
|
68
|
-
return parse_normal_chapter(soup, chapter_id)
|
69
|
-
except Exception as e:
|
70
|
-
logger.warning("[Parser] parse error for chapter '%s': %s", chapter_id, e)
|
71
|
-
return None
|
@@ -1,12 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
novel_downloader.core.parsers.qidian.session
|
4
|
-
--------------------------------------------
|
5
|
-
|
6
|
-
This package provides parsing components for handling Qidian
|
7
|
-
pages that have been rendered by a session.
|
8
|
-
"""
|
9
|
-
|
10
|
-
from .main_parser import QidianSessionParser
|
11
|
-
|
12
|
-
__all__ = ["QidianSessionParser"]
|