novel-downloader 1.2.2__py3-none-any.whl → 1.3.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.
Files changed (128) hide show
  1. novel_downloader/__init__.py +1 -2
  2. novel_downloader/cli/__init__.py +0 -1
  3. novel_downloader/cli/clean.py +2 -10
  4. novel_downloader/cli/download.py +16 -22
  5. novel_downloader/cli/interactive.py +0 -1
  6. novel_downloader/cli/main.py +1 -3
  7. novel_downloader/cli/settings.py +8 -8
  8. novel_downloader/config/__init__.py +0 -1
  9. novel_downloader/config/adapter.py +32 -27
  10. novel_downloader/config/loader.py +116 -108
  11. novel_downloader/config/models.py +35 -29
  12. novel_downloader/config/site_rules.py +2 -4
  13. novel_downloader/core/__init__.py +0 -1
  14. novel_downloader/core/downloaders/__init__.py +4 -4
  15. novel_downloader/core/downloaders/base/__init__.py +14 -0
  16. novel_downloader/core/downloaders/{base_async_downloader.py → base/base_async.py} +49 -53
  17. novel_downloader/core/downloaders/{base_downloader.py → base/base_sync.py} +64 -43
  18. novel_downloader/core/downloaders/biquge/__init__.py +12 -0
  19. novel_downloader/core/downloaders/biquge/biquge_sync.py +25 -0
  20. novel_downloader/core/downloaders/common/__init__.py +14 -0
  21. novel_downloader/core/downloaders/{common_asynb_downloader.py → common/common_async.py} +42 -33
  22. novel_downloader/core/downloaders/{common_downloader.py → common/common_sync.py} +33 -21
  23. novel_downloader/core/downloaders/qidian/__init__.py +10 -0
  24. novel_downloader/core/downloaders/{qidian_downloader.py → qidian/qidian_sync.py} +79 -62
  25. novel_downloader/core/factory/__init__.py +4 -5
  26. novel_downloader/core/factory/{downloader_factory.py → downloader.py} +25 -26
  27. novel_downloader/core/factory/{parser_factory.py → parser.py} +12 -14
  28. novel_downloader/core/factory/{requester_factory.py → requester.py} +29 -16
  29. novel_downloader/core/factory/{saver_factory.py → saver.py} +4 -9
  30. novel_downloader/core/interfaces/__init__.py +8 -9
  31. novel_downloader/core/interfaces/{async_downloader_protocol.py → async_downloader.py} +4 -5
  32. novel_downloader/core/interfaces/{async_requester_protocol.py → async_requester.py} +23 -12
  33. novel_downloader/core/interfaces/{parser_protocol.py → parser.py} +11 -6
  34. novel_downloader/core/interfaces/{saver_protocol.py → saver.py} +2 -3
  35. novel_downloader/core/interfaces/{downloader_protocol.py → sync_downloader.py} +6 -7
  36. novel_downloader/core/interfaces/{requester_protocol.py → sync_requester.py} +31 -17
  37. novel_downloader/core/parsers/__init__.py +5 -4
  38. novel_downloader/core/parsers/{base_parser.py → base.py} +18 -9
  39. novel_downloader/core/parsers/biquge/__init__.py +10 -0
  40. novel_downloader/core/parsers/biquge/main_parser.py +126 -0
  41. novel_downloader/core/parsers/{common_parser → common}/__init__.py +2 -3
  42. novel_downloader/core/parsers/{common_parser → common}/helper.py +13 -13
  43. novel_downloader/core/parsers/{common_parser → common}/main_parser.py +15 -9
  44. novel_downloader/core/parsers/{qidian_parser → qidian}/__init__.py +2 -3
  45. novel_downloader/core/parsers/{qidian_parser → qidian}/browser/__init__.py +2 -3
  46. novel_downloader/core/parsers/{qidian_parser → qidian}/browser/chapter_encrypted.py +40 -48
  47. novel_downloader/core/parsers/{qidian_parser → qidian}/browser/chapter_normal.py +17 -21
  48. novel_downloader/core/parsers/{qidian_parser → qidian}/browser/chapter_router.py +10 -9
  49. novel_downloader/core/parsers/{qidian_parser → qidian}/browser/main_parser.py +14 -10
  50. novel_downloader/core/parsers/{qidian_parser → qidian}/session/__init__.py +2 -3
  51. novel_downloader/core/parsers/{qidian_parser → qidian}/session/chapter_encrypted.py +36 -44
  52. novel_downloader/core/parsers/{qidian_parser → qidian}/session/chapter_normal.py +19 -23
  53. novel_downloader/core/parsers/{qidian_parser → qidian}/session/chapter_router.py +10 -9
  54. novel_downloader/core/parsers/{qidian_parser → qidian}/session/main_parser.py +14 -10
  55. novel_downloader/core/parsers/{qidian_parser → qidian}/session/node_decryptor.py +7 -10
  56. novel_downloader/core/parsers/{qidian_parser → qidian}/shared/__init__.py +2 -3
  57. novel_downloader/core/parsers/{qidian_parser → qidian}/shared/book_info_parser.py +5 -6
  58. novel_downloader/core/parsers/{qidian_parser → qidian}/shared/helpers.py +7 -8
  59. novel_downloader/core/requesters/__init__.py +9 -5
  60. novel_downloader/core/requesters/base/__init__.py +16 -0
  61. novel_downloader/core/requesters/{base_async_session.py → base/async_session.py} +177 -73
  62. novel_downloader/core/requesters/base/browser.py +340 -0
  63. novel_downloader/core/requesters/base/session.py +364 -0
  64. novel_downloader/core/requesters/biquge/__init__.py +12 -0
  65. novel_downloader/core/requesters/biquge/session.py +90 -0
  66. novel_downloader/core/requesters/{common_requester → common}/__init__.py +4 -5
  67. novel_downloader/core/requesters/common/async_session.py +96 -0
  68. novel_downloader/core/requesters/common/session.py +113 -0
  69. novel_downloader/core/requesters/qidian/__init__.py +21 -0
  70. novel_downloader/core/requesters/qidian/broswer.py +306 -0
  71. novel_downloader/core/requesters/qidian/session.py +287 -0
  72. novel_downloader/core/savers/__init__.py +5 -3
  73. novel_downloader/core/savers/{base_saver.py → base.py} +12 -13
  74. novel_downloader/core/savers/biquge.py +25 -0
  75. novel_downloader/core/savers/{common_saver → common}/__init__.py +2 -3
  76. novel_downloader/core/savers/{common_saver/common_epub.py → common/epub.py} +23 -51
  77. novel_downloader/core/savers/{common_saver → common}/main_saver.py +43 -9
  78. novel_downloader/core/savers/{common_saver/common_txt.py → common/txt.py} +16 -46
  79. novel_downloader/core/savers/epub_utils/__init__.py +0 -1
  80. novel_downloader/core/savers/epub_utils/css_builder.py +13 -7
  81. novel_downloader/core/savers/epub_utils/initializer.py +4 -5
  82. novel_downloader/core/savers/epub_utils/text_to_html.py +2 -3
  83. novel_downloader/core/savers/epub_utils/volume_intro.py +1 -3
  84. novel_downloader/core/savers/{qidian_saver.py → qidian.py} +12 -6
  85. novel_downloader/locales/en.json +8 -4
  86. novel_downloader/locales/zh.json +5 -1
  87. novel_downloader/resources/config/settings.toml +88 -0
  88. novel_downloader/utils/cache.py +2 -2
  89. novel_downloader/utils/chapter_storage.py +340 -0
  90. novel_downloader/utils/constants.py +6 -4
  91. novel_downloader/utils/crypto_utils.py +3 -3
  92. novel_downloader/utils/file_utils/__init__.py +0 -1
  93. novel_downloader/utils/file_utils/io.py +12 -17
  94. novel_downloader/utils/file_utils/normalize.py +1 -3
  95. novel_downloader/utils/file_utils/sanitize.py +2 -9
  96. novel_downloader/utils/fontocr/__init__.py +0 -1
  97. novel_downloader/utils/fontocr/ocr_v1.py +19 -22
  98. novel_downloader/utils/fontocr/ocr_v2.py +147 -60
  99. novel_downloader/utils/hash_store.py +19 -20
  100. novel_downloader/utils/hash_utils.py +0 -1
  101. novel_downloader/utils/i18n.py +3 -4
  102. novel_downloader/utils/logger.py +5 -6
  103. novel_downloader/utils/model_loader.py +5 -8
  104. novel_downloader/utils/network.py +9 -10
  105. novel_downloader/utils/state.py +6 -7
  106. novel_downloader/utils/text_utils/__init__.py +0 -1
  107. novel_downloader/utils/text_utils/chapter_formatting.py +2 -7
  108. novel_downloader/utils/text_utils/diff_display.py +0 -1
  109. novel_downloader/utils/text_utils/font_mapping.py +1 -4
  110. novel_downloader/utils/text_utils/text_cleaning.py +0 -1
  111. novel_downloader/utils/time_utils/__init__.py +0 -1
  112. novel_downloader/utils/time_utils/datetime_utils.py +8 -10
  113. novel_downloader/utils/time_utils/sleep_utils.py +1 -3
  114. {novel_downloader-1.2.2.dist-info → novel_downloader-1.3.0.dist-info}/METADATA +14 -17
  115. novel_downloader-1.3.0.dist-info/RECORD +127 -0
  116. {novel_downloader-1.2.2.dist-info → novel_downloader-1.3.0.dist-info}/WHEEL +1 -1
  117. novel_downloader/core/requesters/base_browser.py +0 -214
  118. novel_downloader/core/requesters/base_session.py +0 -246
  119. novel_downloader/core/requesters/common_requester/common_async_session.py +0 -98
  120. novel_downloader/core/requesters/common_requester/common_session.py +0 -126
  121. novel_downloader/core/requesters/qidian_requester/__init__.py +0 -22
  122. novel_downloader/core/requesters/qidian_requester/qidian_broswer.py +0 -396
  123. novel_downloader/core/requesters/qidian_requester/qidian_session.py +0 -202
  124. novel_downloader/resources/config/settings.yaml +0 -76
  125. novel_downloader-1.2.2.dist-info/RECORD +0 -115
  126. {novel_downloader-1.2.2.dist-info → novel_downloader-1.3.0.dist-info}/entry_points.txt +0 -0
  127. {novel_downloader-1.2.2.dist-info → novel_downloader-1.3.0.dist-info}/licenses/LICENSE +0 -0
  128. {novel_downloader-1.2.2.dist-info → novel_downloader-1.3.0.dist-info}/top_level.txt +0 -0
@@ -1,202 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- novel_downloader.core.requesters.qidian_requester.qidian_session
5
- ----------------------------------------------------------------
6
-
7
- This module defines the QidianRequester class for interacting with
8
- the Qidian website.
9
- It extends the BaseSession by adding methods for logging in and
10
- retrieving book information.
11
- """
12
-
13
- from __future__ import annotations
14
-
15
- import base64
16
- import logging
17
- import time
18
- from typing import Any, Dict, Optional
19
-
20
- from requests import Response
21
-
22
- from novel_downloader.config.models import RequesterConfig
23
- from novel_downloader.core.requesters.base_session import BaseSession
24
- from novel_downloader.utils.crypto_utils import patch_qd_payload_token
25
- from novel_downloader.utils.state import state_mgr
26
- from novel_downloader.utils.time_utils import sleep_with_random_delay
27
-
28
- logger = logging.getLogger(__name__)
29
-
30
-
31
- class QidianSession(BaseSession):
32
- """
33
- A concrete :class:`BaseSession` for the Qidian site. Besides the usual
34
- ``get``/``post`` helpers provided by the base class, this subclass adds:
35
-
36
- * URL builders for book info / chapter / bookcase pages
37
- * High-level convenience wrappers that:
38
- 1. sleep a configurable (jittered) amount of time;
39
- 2. retry on failures;
40
- 3. automatically persist fresh cookies to :pydata:`state_mgr`
41
- so that the next run can reuse them.
42
- """
43
-
44
- DEFAULT_SCHEME = "https:"
45
- QIDIAN_BASE_URL = "www.qidian.com"
46
- QIDIAN_BOOKCASE_URL = f"{DEFAULT_SCHEME}//my.qidian.com/bookcase/"
47
- QIDIAN_BOOK_INFO_URL_1 = f"{DEFAULT_SCHEME}//www.qidian.com/book"
48
- QIDIAN_BOOK_INFO_URL_2 = f"{DEFAULT_SCHEME}//book.qidian.com/info"
49
- QIDIAN_CHAPTER_URL = f"{DEFAULT_SCHEME}//www.qidian.com/chapter"
50
-
51
- def __init__(self, config: RequesterConfig):
52
- """
53
- Initialise the underlying :class:`requests.Session`.
54
- """
55
- self._init_session(config=config)
56
-
57
- def get(
58
- self,
59
- url: str,
60
- params: Optional[Dict[str, Any]] = None,
61
- **kwargs: Any,
62
- ) -> Response:
63
- """
64
- Same as :py:meth:`BaseSession.get`, but transparently refreshes
65
- a cookie-based token used for request validation.
66
-
67
- The method:
68
- 1. Reads the existing cookie (if any);
69
- 2. Generates a new value tied to *url*;
70
- 3. Updates both the live ``requests.Session`` and the internal cache;
71
- 4. Delegates the actual request to ``super().get``.
72
- """
73
- if self._session is None: # defensive - mirrors BaseSession check
74
- raise RuntimeError("Session is not initialized or has been shut down.")
75
-
76
- # ---- 1. refresh token cookie --------------------------------------
77
- cookie_key = base64.b64decode("d190c2Zw").decode()
78
- old_token = self._session.cookies.get(cookie_key, "")
79
-
80
- if old_token:
81
- refreshed_token = patch_qd_payload_token(old_token, url)
82
- self._session.cookies.set(cookie_key, refreshed_token)
83
- self._cookies[cookie_key] = refreshed_token
84
-
85
- # ---- 2. perform the real GET --------------------------------------------
86
- resp: Response = super().get(url, params=params, **kwargs)
87
-
88
- # ---- 3. persist any server-set cookies (optional) --------------
89
- self.update_cookies(self._session.cookies.get_dict(), overwrite=True)
90
- state_mgr.set_cookies("qidian", self._cookies)
91
-
92
- return resp
93
-
94
- def login(self, max_retries: int = 3, manual_login: bool = False) -> bool:
95
- """
96
- Restore cookies persisted by the browser-based workflow.
97
- """
98
- cookies: Dict[str, str] = state_mgr.get_cookies("qidian")
99
- if not cookies:
100
- logger.info(
101
- "[session] No stored cookies found: session remains unauthenticated."
102
- )
103
- return False
104
-
105
- # Merge cookies into both the internal cache and the live session
106
- self.update_cookies(cookies, overwrite=True)
107
- logger.info("[session] Loaded %d cookie(s) from state.", len(cookies))
108
- self.get("https://www.qidian.com")
109
- return True
110
-
111
- def get_book_info(self, book_id: str, wait_time: Optional[float] = None) -> str:
112
- """
113
- Fetch the raw HTML of the book info page.
114
-
115
- :param book_id: The book identifier.
116
- :param wait_time: Base number of seconds to wait before returning content.
117
- :return: The page content as a string.
118
- """
119
- url = f"{self.QIDIAN_BOOK_INFO_URL_2}/{book_id}/"
120
- base_delay = wait_time or self._config.wait_time
121
-
122
- for attempt in range(1, self.retry_times + 1):
123
- try:
124
- resp = self.get(url)
125
- resp.raise_for_status()
126
- sleep_with_random_delay(base_delay, mul_spread=1.2)
127
- return resp.text
128
- except Exception as exc:
129
- logger.warning(
130
- "[session] get_book_info(%s) attempt %s/%s failed: %s",
131
- book_id,
132
- attempt,
133
- self.retry_times,
134
- exc,
135
- )
136
- if attempt == self.retry_times:
137
- raise
138
- time.sleep(self.retry_interval)
139
-
140
- raise RuntimeError("Unexpected fall-through in get_book_info")
141
-
142
- def get_book_chapter(
143
- self, book_id: str, chapter_id: str, wait_time: Optional[float] = None
144
- ) -> str:
145
- """
146
- Fetch the HTML of a single chapter.
147
-
148
- :param book_id: The book identifier.
149
- :param chapter_id: The chapter identifier.
150
- :param wait_time: Base number of seconds to wait before returning content.
151
- :return: The chapter content as a string.
152
- """
153
- url = f"{self.QIDIAN_CHAPTER_URL}/{book_id}/{chapter_id}/"
154
- base_delay = wait_time or self._config.wait_time
155
-
156
- for attempt in range(1, self.retry_times + 1):
157
- try:
158
- resp = self.get(url)
159
- resp.raise_for_status()
160
- sleep_with_random_delay(base_delay, mul_spread=1.2)
161
- return resp.text
162
- except Exception as exc:
163
- logger.warning(
164
- "[session] get_book_chapter(%s, %s) attempt %s/%s failed: %s",
165
- book_id,
166
- chapter_id,
167
- attempt,
168
- self.retry_times,
169
- exc,
170
- )
171
- if attempt == self.retry_times:
172
- raise
173
- time.sleep(self.retry_interval)
174
-
175
- raise RuntimeError("Unexpected fall-through in get_book_chapter")
176
-
177
- def get_bookcase(self, wait_time: Optional[float] = None) -> str:
178
- """
179
- Retrieve the user's *bookcase* page.
180
-
181
- :param wait_time: Base number of seconds to wait before returning content.
182
- :return: The HTML markup of the bookcase page.
183
- """
184
- base_delay = wait_time or self._config.wait_time
185
- for attempt in range(1, self.retry_times + 1):
186
- try:
187
- resp = self.get(self.QIDIAN_BOOKCASE_URL, allow_redirects=True)
188
- resp.raise_for_status()
189
- sleep_with_random_delay(base_delay, mul_spread=1.2)
190
- return resp.text
191
- except Exception as exc:
192
- logger.warning(
193
- "[session] get_bookcase attempt %s/%s failed: %s",
194
- attempt,
195
- self.retry_times,
196
- exc,
197
- )
198
- if attempt == self.retry_times:
199
- raise
200
- time.sleep(self.retry_interval)
201
-
202
- raise RuntimeError("Unexpected fall-through in get_bookcase")
@@ -1,76 +0,0 @@
1
- # 网络请求层设置
2
- requests:
3
- wait_time: 5.0 # 每次请求等待时间 (秒)
4
- retry_times: 3 # 请求失败重试次数
5
- retry_interval: 5.0
6
- timeout: 30.0 # 页面加载超时时间 (秒)
7
- max_rps: null # 最大请求速率 (requests per second), 为 null 则不限制
8
- # DrissionPage 专用设置
9
- headless: false # 是否以无头模式启动浏览器
10
- user_data_folder: "" # 浏览器用户数据目录: 为空则使用默认目录
11
- profile_name: "" # 使用的用户配置名称: 为空则使用默认配置
12
- auto_close: true # 页面抓取完是否自动关闭浏览器
13
- disable_images: false # 是否禁用图片加载 (加速)
14
- mute_audio: true # 是否静音
15
-
16
- # 全局通用设置
17
- general:
18
- request_interval: 5.0 # 同一本书各章节请求间隔 (秒)
19
- raw_data_dir: "./raw_data" # 原始章节 HTML/JSON 存放目录
20
- output_dir: "./downloads" # 最终输出文件存放目录
21
- cache_dir: "./novel_cache" # 本地缓存目录 (字体 / 图片等)
22
- download_workers: 4 # 并发下载线程数
23
- parser_workers: 4 # 并发解析线程数
24
- use_process_pool: false # 是否使用多进程池来处理任务
25
- skip_existing: true # 是否跳过已存在章节
26
- debug:
27
- save_html: false # 是否将抓取到的原始 HTML 保留到磁盘
28
- log_level: "INFO" # 日志级别: DEBUG, INFO, WARNING, ERROR
29
- font_ocr:
30
- decode_font: false # 是否尝试本地解码混淆字体
31
- use_freq: false # 是否使用频率分析
32
- ocr_version: "v2.0" # "v1.0" / "v2.0"
33
- use_ocr: true # 是否使用 OCR 辅助识别文本
34
- use_vec: false # 是否使用 Vector 辅助识别文本
35
- save_font_debug: false # 是否保存字体解码调试数据
36
- batch_size: 32
37
- gpu_mem: 500 # GPU 显存限制 (MB)
38
- gpu_id: null # 使用哪个 GPU
39
- ocr_weight: 0.6
40
- vec_weight: 0.4
41
-
42
- # 各站点的特定配置
43
- sites:
44
- qidian:
45
- # 小说 ID 列表
46
- # 例如: 访问 https://www.qidian.com/book/1010868264/
47
- # 该小说的 ID 就是 1010868264
48
- book_ids:
49
- - "0000000000"
50
- - "0000000000"
51
- mode: "browser" # browser / session
52
- login_required: true # 是否需要登录才能访问
53
- #
54
- sample_site:
55
- book_ids:
56
- - "ABCDEF"
57
- api_key: "your_api_key"
58
- special_flag: 42
59
- common:
60
- mode: "session" # async / session
61
- login_required: false # 是否需要登录才能访问
62
-
63
- # 输出文件格式及相关选项
64
- output:
65
- clean_text: true # 是否对章节文本做清理
66
- formats:
67
- make_txt: true # 是否生成完整 TXT 文件
68
- make_epub: false # 是否生成 EPUB
69
- make_md: false # 是否生成 Markdown (未实现)
70
- make_pdf: false # 可能支持 PDF 输出 (未实现)
71
- naming:
72
- append_timestamp: false # 在文件名中追加时间戳
73
- filename_template: "{title}_{author}" # 文件命名规则
74
- epub:
75
- include_cover: true # 是否在 EPUB 中包含封面
76
- include_toc: false # 是否自动生成目录
@@ -1,115 +0,0 @@
1
- novel_downloader/__init__.py,sha256=LCdmt0Int1WSZ3n25_tdJ7Mm-gknw2F-Sg4xKyMLWJs,242
2
- novel_downloader/cli/__init__.py,sha256=ocGwOO4kmkby8VNol92UikMI1RPUJLv9i5xmB7wbpmw,198
3
- novel_downloader/cli/clean.py,sha256=9_hOrxKg8nY7q6cyR8iNech0vSREGagPBmdB4k8Te2U,3937
4
- novel_downloader/cli/download.py,sha256=WZLnf8cckAGe9t7hI6WWgjrm_KRa67pTwEC-YJBAmFc,4223
5
- novel_downloader/cli/interactive.py,sha256=6vROwPsvupb_TWH1dd_78FDqvtAaiPfyEBvQVai9E9c,2154
6
- novel_downloader/cli/main.py,sha256=km1MwHzIVZFcxUlKLRiiMctJlGHWKZNjRKrgAGQjkMs,1183
7
- novel_downloader/cli/settings.py,sha256=bV3Hgg502V9goeP3g2xSiF-PMQB9G32qGmjb8ncTENA,6522
8
- novel_downloader/config/__init__.py,sha256=tJ2k7nwZbxgqw1kKgJM4g1yu5-2fsx2LXU3VTadrTJ4,1129
9
- novel_downloader/config/adapter.py,sha256=R1y1g7uDBxtDz_sVsDGl2-iasQNAtWSmYdSk-mcFoAA,6698
10
- novel_downloader/config/loader.py,sha256=_rm9rp1lmHYg-A7F_0PQETWjlXbvtyJYaqQD5oI-1O0,5690
11
- novel_downloader/config/models.py,sha256=i7i1yQzY5lAZWDBhi5ZXqzthlanhvhAc3oViMMoJFyw,5139
12
- novel_downloader/config/site_rules.py,sha256=WRw12Tfue-ErAPGKq506gRIqKOxWU-u96kay3JDgTNc,3031
13
- novel_downloader/core/__init__.py,sha256=D-ACiIqP0rdARZmjBnF6WMKGvvjVtxGRIM7GhOS9kh4,779
14
- novel_downloader/core/downloaders/__init__.py,sha256=Qp0q4p7zTy7lReQQF0hDP7ALUQnNflSNNIl4F7iPGz0,601
15
- novel_downloader/core/downloaders/base_async_downloader.py,sha256=8lMSVLU-VtGIdEMGkS0s_rEJpqCgu2WaljBvsEDyPN4,4281
16
- novel_downloader/core/downloaders/base_downloader.py,sha256=kFw_yn3QRbWqU9jXJni4IGA8P3AxZf9gfjgfu01TauY,5371
17
- novel_downloader/core/downloaders/common_asynb_downloader.py,sha256=u1ODvh_n13CSGWwjkBIMoThTbCeACX5mOhv5ub2Cd0c,7120
18
- novel_downloader/core/downloaders/common_downloader.py,sha256=-HT7pi8bCSAdIcrU9CFHz26iqgtPhlUTI2MoE7ldCfY,6506
19
- novel_downloader/core/downloaders/qidian_downloader.py,sha256=KH-rPvwhWsZ9hyuvZxhQ9Q42LDFiAABpMlgReeX2nzI,7328
20
- novel_downloader/core/factory/__init__.py,sha256=qGqeROj8Dp_5iNtgWytkrUNI0ICab7SCNK3lba3H_NU,743
21
- novel_downloader/core/factory/downloader_factory.py,sha256=ldWFZ3189rxW2cWTNcwidiPisJNuaxD8OYB5YyjOXRU,5110
22
- novel_downloader/core/factory/parser_factory.py,sha256=4PxiagtSKY58azFsmEWfq2f5vhVbtMFm5gAXS3oQF08,1828
23
- novel_downloader/core/factory/requester_factory.py,sha256=OEK2S-rj8vw4IdDTMTEWcb7k7lRmmWBnfijhYnlOCc0,3173
24
- novel_downloader/core/factory/saver_factory.py,sha256=OgZPDOWVIfhxLFiVBKI5jaNOEKmzP9f3YWDOnw63Hfc,1275
25
- novel_downloader/core/interfaces/__init__.py,sha256=jeT8BmEEjIazVyX80ZdzQXgTccEj-ktG6Bbjs9uAVUM,843
26
- novel_downloader/core/interfaces/async_downloader_protocol.py,sha256=QWjdhNc39hC3bD8Q1lUpBv2GqX3roxVxzKWh6cgwLhk,1002
27
- novel_downloader/core/interfaces/async_requester_protocol.py,sha256=Jd54mJWnNt_BE2RRGYGmGDe93MM0I4xBLL4m6u6V_Is,2344
28
- novel_downloader/core/interfaces/downloader_protocol.py,sha256=YJdSAE9uBWF7wNLWmlKsYd3J7M4rXOjqDV5m9O7kfio,947
29
- novel_downloader/core/interfaces/parser_protocol.py,sha256=A2wIe7shEGdeKsNDFpMuPI8HFrK_H34HOseVAzqcnTo,1280
30
- novel_downloader/core/interfaces/requester_protocol.py,sha256=Zskzwom104GWjB0LrV-VvAR7vfTB3LAcHvV110ISdo0,2058
31
- novel_downloader/core/interfaces/saver_protocol.py,sha256=_3ha4koF-xBrogJwvCOsQM3GEXfLn3_aYZHvmRd1ZR4,1623
32
- novel_downloader/core/parsers/__init__.py,sha256=TSarXiYxthyjFgruRFkpMEucqmylrCtR_4Y1C1RVMhk,544
33
- novel_downloader/core/parsers/base_parser.py,sha256=mRv6tmbWIuX5XnXYnSP9JMsXECgykZymq4zWNNk7wDQ,2936
34
- novel_downloader/core/parsers/common_parser/__init__.py,sha256=GmV7Yj57zbLK0-WYmPtv6x1AhKwa3lzKlqsU7mc3lzg,379
35
- novel_downloader/core/parsers/common_parser/helper.py,sha256=gY3HHYnXGHHiCaig59T7IlI7Fa7XEqlmAAKWGmeSmXI,12090
36
- novel_downloader/core/parsers/common_parser/main_parser.py,sha256=ANvkZMYACZEwaYLz56lLyBz9dc_XqZMvWp2_jWWoF3g,2868
37
- novel_downloader/core/parsers/qidian_parser/__init__.py,sha256=mYbZvWMn4oFFX3qGhGx6Qo6oqvE5I71b-jbanWsTOSI,522
38
- novel_downloader/core/parsers/qidian_parser/browser/__init__.py,sha256=E8kMkQW_LdmVFYpHbbYtJTLNTYOy9XItQf_h7KcnGIU,353
39
- novel_downloader/core/parsers/qidian_parser/browser/chapter_encrypted.py,sha256=v-sCfQYyRNOdZ12Qlb9b5Tqq69rcOS_CKBUG5XG9WUs,17713
40
- novel_downloader/core/parsers/qidian_parser/browser/chapter_normal.py,sha256=SZ9Ncw6yLCRo4gJNEtv4V8N2WfONvEAc8lftJREsBTY,3190
41
- novel_downloader/core/parsers/qidian_parser/browser/chapter_router.py,sha256=qjN10SpQCUMjFcCaWnqIZhcLDx5sN5jzDfWIrBSbnyo,2101
42
- novel_downloader/core/parsers/qidian_parser/browser/main_parser.py,sha256=8nmmTlhgina3JNrKoIMkFoNF6d9ztki7kAcCyGysy4g,3856
43
- novel_downloader/core/parsers/qidian_parser/session/__init__.py,sha256=Rs2Sz1vNn1-UdpY0O_reECBN4kgb3JYHQZoZ20P7lHU,358
44
- novel_downloader/core/parsers/qidian_parser/session/chapter_encrypted.py,sha256=XbQV0MQVsiumyrwGddAUaK9JrZpWZlfbZ0_2vBhGupU,16016
45
- novel_downloader/core/parsers/qidian_parser/session/chapter_normal.py,sha256=ySQ7vUs4dLCkHv_nPSifDnH3xq3wqxa9FWGy_ETX-uw,3875
46
- novel_downloader/core/parsers/qidian_parser/session/chapter_router.py,sha256=ob8ULDhNdnJgU3rlA-tLy0w0PqbC20vi8auFqQipJww,1978
47
- novel_downloader/core/parsers/qidian_parser/session/main_parser.py,sha256=fL-IYhd99Rbb2eHCOy0DmYpmHeIOSe3DIFbtQKCj53s,3947
48
- novel_downloader/core/parsers/qidian_parser/session/node_decryptor.py,sha256=7ZuneGzL7HX1g8taMzn-2qkJXLDHgrVee-FDkMZtIIw,5755
49
- novel_downloader/core/parsers/qidian_parser/shared/__init__.py,sha256=K5HX7pgiRiJuTLdbQDbtm60mO-sXgr6bo5Ft8H1-JLs,978
50
- novel_downloader/core/parsers/qidian_parser/shared/book_info_parser.py,sha256=CGoJXRrf65CjJU3wPjZ8FRy96zV2rkYyWSMEyEFUrzE,4853
51
- novel_downloader/core/parsers/qidian_parser/shared/helpers.py,sha256=zQqcts8f_JddtKvQjvVJBJQvN5HjeaqlP2I3ztwO75o,4306
52
- novel_downloader/core/requesters/__init__.py,sha256=U2jDvt5RxF5P9yL2lwyZ-cRueJBZgRnjil3_5TvAh3Y,798
53
- novel_downloader/core/requesters/base_async_session.py,sha256=xojNO_TCLUHmkuk379ttoiScHwmLCNhQAFzQKSpk5AM,10635
54
- novel_downloader/core/requesters/base_browser.py,sha256=HceDLOkl86K_nAkZR1XGIzSa-4KyFGt0dG5PyhefJ3c,6899
55
- novel_downloader/core/requesters/base_session.py,sha256=0yl3wR-myu-r9NrtwnGWzEFMzWPxcD3ROT5x9xeX6zQ,8328
56
- novel_downloader/core/requesters/common_requester/__init__.py,sha256=kVKZyrS7PVlUnaV1xGsZdoW2J9XuyQ11A4oMV9Cc64Q,523
57
- novel_downloader/core/requesters/common_requester/common_async_session.py,sha256=Bt_pgiJdxfoSDswMANeiZxaUQ4E5YitqEmcTlL4ImLY,3556
58
- novel_downloader/core/requesters/common_requester/common_session.py,sha256=hzah5pvRqFBTzmz1J5d7GE5jbpKSOZWBPz-lh18cTv4,4686
59
- novel_downloader/core/requesters/qidian_requester/__init__.py,sha256=s0ldqNvfqUsEnm_biM_bXEGN7gz88Z5IAx1OBvGW1lY,682
60
- novel_downloader/core/requesters/qidian_requester/qidian_broswer.py,sha256=BkweHqf-TEC2VCGKFxdUp24lPDuQPUepfHPFUXUgTh0,15146
61
- novel_downloader/core/requesters/qidian_requester/qidian_session.py,sha256=R-B6Hc61v5C-KGdEckDJWBt7Bnn_dbpW5ukQgh3x--Q,7664
62
- novel_downloader/core/savers/__init__.py,sha256=p9O6p8ZUblrSheDVJoTSuDr6s1mJpQi8mz3QmQ16nHs,391
63
- novel_downloader/core/savers/base_saver.py,sha256=VocVl8go80IkzAp9qY4dgZjmLbK8TVkg48Ugl53pxrc,5513
64
- novel_downloader/core/savers/qidian_saver.py,sha256=MVAcWdM-IX_qsRW5It2aIkx9QPdRCLcZGcD3ihfm3gU,627
65
- novel_downloader/core/savers/common_saver/__init__.py,sha256=Pg52cjAwG9fgT5qWgVer5oLMACU-duNFNbtfcq4t5xA,292
66
- novel_downloader/core/savers/common_saver/common_epub.py,sha256=80a-ibhhTy08wsZr7uiqrlvmITcsd1MH-z1OAj1Dl3c,7182
67
- novel_downloader/core/savers/common_saver/common_txt.py,sha256=fgBoKPApmODeUoxCi54txoD322G7mlU9o8HEM5X5atk,5765
68
- novel_downloader/core/savers/common_saver/main_saver.py,sha256=scjERGgC7X2R3f0KuLwf462VHkFJ2D2duod53aWlMkg,2765
69
- novel_downloader/core/savers/epub_utils/__init__.py,sha256=rqWUMkdebZ2dO-aZRYSutZ-w5uKgFVwe83nBJbYx40E,738
70
- novel_downloader/core/savers/epub_utils/css_builder.py,sha256=ZiYEAuC4_u3BWBcf6-kILrKH8YHDcserzBP4luxmOro,2028
71
- novel_downloader/core/savers/epub_utils/initializer.py,sha256=QuPWuChq2dcFlGp-PeZBrhfqSwoM7Nko-zHe9beluZw,3255
72
- novel_downloader/core/savers/epub_utils/text_to_html.py,sha256=UW70pMmc9HdgSCMKVEgukONFdlNCmQIKs9PCDLNAFQU,4117
73
- novel_downloader/core/savers/epub_utils/volume_intro.py,sha256=1NhnLKRL_ieoDgXTRt3vTNeENGvUj7JTZxTME4TmYm8,1820
74
- novel_downloader/locales/en.json,sha256=4Zn2cD2vGkHHIam2j_Ig-gpicD14L3ufKX_lq0k0yR4,5531
75
- novel_downloader/locales/zh.json,sha256=nKmxyb84gmq9tg9DFLSx1r0MWLmo4Jwzf27S4ggDzV4,5420
76
- novel_downloader/resources/config/rules.toml,sha256=hrED6h3Z3cjSY5hRPQhp4TFAU5QXnN9xHfVABOJQNrM,4979
77
- novel_downloader/resources/config/settings.yaml,sha256=NxtYrf2HWJQyGwIuj3f3JKA72Do5WPvSghonPM0Q7SM,3493
78
- novel_downloader/resources/css_styles/main.css,sha256=WM6GePwdOGgM86fbbOxQ0_0oerTBDZeQHt8zRVfcJp8,1617
79
- novel_downloader/resources/css_styles/volume-intro.css,sha256=6gaUnNKkrb2w8tYJRq1BGD1FwbhT1I5W2GI_Zelo9G4,1156
80
- novel_downloader/resources/images/volume_border.png,sha256=2dEVimnTHKOfLMhi7bhkh_5joWNnrqg8duomLSNOZx4,28613
81
- novel_downloader/resources/js_scripts/qidian_decrypt_node.js,sha256=spNrk_gXI7pPW9abr4XGc2LASMe1UuN4BUe4cH24L8s,2195
82
- novel_downloader/resources/json/replace_word_map.json,sha256=ptL9sGO9aK7rnnAaOIyZ0OiH7gaT0BhFzficzYZSDks,55
83
- novel_downloader/resources/text/blacklist.txt,sha256=sovK9JgARZP3lud5b1EZgvv8LSVKPthf4ADpCSZZgQ8,154
84
- novel_downloader/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
- novel_downloader/utils/cache.py,sha256=NB5j7CWNscfE4eFA0A9O5mYR1eW-216M-ljlMo0LDqE,615
86
- novel_downloader/utils/constants.py,sha256=geLu9ufkM1plktIbF2VRzS5N5_N2XuHn0T03Kc0lGCQ,5520
87
- novel_downloader/utils/crypto_utils.py,sha256=whGgir2oi_17pNteiIRztiMNaB-ZP63GMP3KPJkXA80,4178
88
- novel_downloader/utils/hash_store.py,sha256=rpr61GsvZ9wT_1fEn4_83JZ-nWc1KLtcvb56ZqHawdk,9826
89
- novel_downloader/utils/hash_utils.py,sha256=6S4-Q_uLNzdEDkBOUG9QEcflbuPFNDAYe6Gx718AOo4,2998
90
- novel_downloader/utils/i18n.py,sha256=9vwBEuwk37ED29WI321-FWmA1_K4yv_qw84XUNbSr1s,1068
91
- novel_downloader/utils/logger.py,sha256=Jk65uXuF4lyuygQHcnlJjIOCMgqn-in-Dz-K3mSItlE,3439
92
- novel_downloader/utils/model_loader.py,sha256=zJETCQULf8GdW1eDV-Ez9W0GxFsQaqbCg9CTtr8G9LU,2079
93
- novel_downloader/utils/network.py,sha256=5XXDTnKfpL2IQFY6y1N23rBsxn5NhgwrArcmn0gsbHo,8705
94
- novel_downloader/utils/state.py,sha256=9ePYYSRMB43ob-OZ5KVUaq-6OlSsqTxQ2ItOw-47cvQ,5175
95
- novel_downloader/utils/file_utils/__init__.py,sha256=L0xvZDnnskCITPFzU8PUUbGjw__5wJf1OqKhZUOamaY,1183
96
- novel_downloader/utils/file_utils/io.py,sha256=Q1noPQ-MofSXFK5BwmBfgZqKctBRqzOQFlmI6lSkrAQ,7598
97
- novel_downloader/utils/file_utils/normalize.py,sha256=7lqtVAMQO6z1JoCihLo-cf9PYniz6jU97Cf3zpkU8tQ,2078
98
- novel_downloader/utils/file_utils/sanitize.py,sha256=dRgdtJx33G6DTFgEK0f1CFyTGUBKG5jxej2A-f7grQU,2324
99
- novel_downloader/utils/fontocr/__init__.py,sha256=j8IrSs7wUPX8shg18wafo15hgQmqwUaqrfExbHJAaMY,567
100
- novel_downloader/utils/fontocr/ocr_v1.py,sha256=hWB7cv6qQATpmrM0mVtRPlhBRmYKxJ9xNMiBrmo6__U,11351
101
- novel_downloader/utils/fontocr/ocr_v2.py,sha256=RSmHfrilrRudExAjzkBm-_PknDNT9blp_2Eu2wCw54M,24457
102
- novel_downloader/utils/text_utils/__init__.py,sha256=Iq6ZuS93J_M54nv1zPECryuPXJ-a0b3OE2wnyWqo-qs,830
103
- novel_downloader/utils/text_utils/chapter_formatting.py,sha256=NeRKvZFspA5fcBrxhgqf4ZP-zq1vr8zNZ3viNOXhwhM,1347
104
- novel_downloader/utils/text_utils/diff_display.py,sha256=cLjpeAOtpLOVMHnjgq2yv91fHvH0LvL1lMB80v3bPjQ,2552
105
- novel_downloader/utils/text_utils/font_mapping.py,sha256=lai3lZSaxvSL1BZWgD6JLWdI56aWlZDeZ3zUYQS8pkc,916
106
- novel_downloader/utils/text_utils/text_cleaning.py,sha256=1yuaDeUBHqBRkkWhw43rV1i8TL5rx1yK0I78FyEwcTw,1656
107
- novel_downloader/utils/time_utils/__init__.py,sha256=bRpO14eorfH5C5xfqvW7QwSe3fQHhpr34j4O3qY5cGc,580
108
- novel_downloader/utils/time_utils/datetime_utils.py,sha256=BX6W2dWpqEDMpAXF0pm2bPT7xZsy_Q_TZAiwd3dsiiE,4883
109
- novel_downloader/utils/time_utils/sleep_utils.py,sha256=xClrFcjn4DdOO5KskO5WkGKt6LKZx0Cb8SMH8mQrlT0,1864
110
- novel_downloader-1.2.2.dist-info/licenses/LICENSE,sha256=XgmnH0mBf-qEiizoVAfJQAKzPB9y3rBa-ni7M0Aqv4A,1066
111
- novel_downloader-1.2.2.dist-info/METADATA,sha256=IjqO-vr2DU20Tdz3JEHo5hSWI6-3qDuXsbFAW8Af68c,6291
112
- novel_downloader-1.2.2.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
113
- novel_downloader-1.2.2.dist-info/entry_points.txt,sha256=v23QrJrfrAcYpxUYslCVxubOVRRTaTw7vlG_tfMsFP8,65
114
- novel_downloader-1.2.2.dist-info/top_level.txt,sha256=hP4jYWM2LTm1jxsW4hqEB8N0dsRvldO2QdhggJT917I,17
115
- novel_downloader-1.2.2.dist-info/RECORD,,