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.
Files changed (276) hide show
  1. novel_downloader/__init__.py +1 -1
  2. novel_downloader/cli/__init__.py +2 -4
  3. novel_downloader/cli/clean.py +21 -88
  4. novel_downloader/cli/config.py +27 -104
  5. novel_downloader/cli/download.py +78 -66
  6. novel_downloader/cli/export.py +20 -21
  7. novel_downloader/cli/main.py +3 -1
  8. novel_downloader/cli/search.py +120 -0
  9. novel_downloader/cli/ui.py +156 -0
  10. novel_downloader/config/__init__.py +10 -14
  11. novel_downloader/config/adapter.py +195 -99
  12. novel_downloader/config/{loader.py → file_io.py} +53 -27
  13. novel_downloader/core/__init__.py +14 -13
  14. novel_downloader/core/archived/deqixs/fetcher.py +115 -0
  15. novel_downloader/core/archived/deqixs/parser.py +132 -0
  16. novel_downloader/core/archived/deqixs/searcher.py +89 -0
  17. novel_downloader/core/archived/qidian/searcher.py +79 -0
  18. novel_downloader/core/archived/wanbengo/searcher.py +98 -0
  19. novel_downloader/core/archived/xshbook/searcher.py +93 -0
  20. novel_downloader/core/downloaders/__init__.py +8 -30
  21. novel_downloader/core/downloaders/base.py +182 -30
  22. novel_downloader/core/downloaders/common.py +217 -384
  23. novel_downloader/core/downloaders/qianbi.py +332 -4
  24. novel_downloader/core/downloaders/qidian.py +250 -290
  25. novel_downloader/core/downloaders/registry.py +69 -0
  26. novel_downloader/core/downloaders/signals.py +46 -0
  27. novel_downloader/core/exporters/__init__.py +8 -26
  28. novel_downloader/core/exporters/base.py +107 -31
  29. novel_downloader/core/exporters/common/__init__.py +3 -4
  30. novel_downloader/core/exporters/common/epub.py +92 -171
  31. novel_downloader/core/exporters/common/main_exporter.py +14 -67
  32. novel_downloader/core/exporters/common/txt.py +90 -86
  33. novel_downloader/core/exporters/epub_util.py +184 -1327
  34. novel_downloader/core/exporters/linovelib/__init__.py +3 -2
  35. novel_downloader/core/exporters/linovelib/epub.py +165 -222
  36. novel_downloader/core/exporters/linovelib/main_exporter.py +10 -71
  37. novel_downloader/core/exporters/linovelib/txt.py +76 -66
  38. novel_downloader/core/exporters/qidian.py +15 -11
  39. novel_downloader/core/exporters/registry.py +55 -0
  40. novel_downloader/core/exporters/txt_util.py +67 -0
  41. novel_downloader/core/fetchers/__init__.py +57 -56
  42. novel_downloader/core/fetchers/aaatxt.py +83 -0
  43. novel_downloader/core/fetchers/{biquge/session.py → b520.py} +10 -10
  44. novel_downloader/core/fetchers/{base/session.py → base.py} +63 -47
  45. novel_downloader/core/fetchers/biquyuedu.py +83 -0
  46. novel_downloader/core/fetchers/dxmwx.py +110 -0
  47. novel_downloader/core/fetchers/eightnovel.py +139 -0
  48. novel_downloader/core/fetchers/{esjzone/session.py → esjzone.py} +23 -11
  49. novel_downloader/core/fetchers/guidaye.py +85 -0
  50. novel_downloader/core/fetchers/hetushu.py +92 -0
  51. novel_downloader/core/fetchers/{qianbi/browser.py → i25zw.py} +22 -26
  52. novel_downloader/core/fetchers/ixdzs8.py +113 -0
  53. novel_downloader/core/fetchers/jpxs123.py +101 -0
  54. novel_downloader/core/fetchers/{biquge/browser.py → lewenn.py} +15 -15
  55. novel_downloader/core/fetchers/{linovelib/session.py → linovelib.py} +16 -12
  56. novel_downloader/core/fetchers/piaotia.py +105 -0
  57. novel_downloader/core/fetchers/qbtr.py +101 -0
  58. novel_downloader/core/fetchers/{qianbi/session.py → qianbi.py} +9 -9
  59. novel_downloader/core/fetchers/{qidian/session.py → qidian.py} +55 -40
  60. novel_downloader/core/fetchers/quanben5.py +92 -0
  61. novel_downloader/core/fetchers/{base/rate_limiter.py → rate_limiter.py} +2 -2
  62. novel_downloader/core/fetchers/registry.py +60 -0
  63. novel_downloader/core/fetchers/{sfacg/session.py → sfacg.py} +11 -9
  64. novel_downloader/core/fetchers/shencou.py +106 -0
  65. novel_downloader/core/fetchers/{common/browser.py → shuhaige.py} +24 -19
  66. novel_downloader/core/fetchers/tongrenquan.py +84 -0
  67. novel_downloader/core/fetchers/ttkan.py +95 -0
  68. novel_downloader/core/fetchers/{common/session.py → wanbengo.py} +21 -17
  69. novel_downloader/core/fetchers/xiaoshuowu.py +106 -0
  70. novel_downloader/core/fetchers/xiguashuwu.py +177 -0
  71. novel_downloader/core/fetchers/xs63b.py +171 -0
  72. novel_downloader/core/fetchers/xshbook.py +85 -0
  73. novel_downloader/core/fetchers/{yamibo/session.py → yamibo.py} +23 -11
  74. novel_downloader/core/fetchers/yibige.py +114 -0
  75. novel_downloader/core/interfaces/__init__.py +8 -14
  76. novel_downloader/core/interfaces/downloader.py +6 -2
  77. novel_downloader/core/interfaces/exporter.py +7 -7
  78. novel_downloader/core/interfaces/fetcher.py +4 -17
  79. novel_downloader/core/interfaces/parser.py +5 -6
  80. novel_downloader/core/interfaces/searcher.py +26 -0
  81. novel_downloader/core/parsers/__init__.py +58 -22
  82. novel_downloader/core/parsers/aaatxt.py +132 -0
  83. novel_downloader/core/parsers/b520.py +116 -0
  84. novel_downloader/core/parsers/base.py +63 -12
  85. novel_downloader/core/parsers/biquyuedu.py +133 -0
  86. novel_downloader/core/parsers/dxmwx.py +162 -0
  87. novel_downloader/core/parsers/eightnovel.py +224 -0
  88. novel_downloader/core/parsers/{esjzone/main_parser.py → esjzone.py} +67 -67
  89. novel_downloader/core/parsers/guidaye.py +128 -0
  90. novel_downloader/core/parsers/hetushu.py +139 -0
  91. novel_downloader/core/parsers/i25zw.py +137 -0
  92. novel_downloader/core/parsers/ixdzs8.py +186 -0
  93. novel_downloader/core/parsers/jpxs123.py +137 -0
  94. novel_downloader/core/parsers/lewenn.py +142 -0
  95. novel_downloader/core/parsers/{linovelib/main_parser.py → linovelib.py} +54 -65
  96. novel_downloader/core/parsers/piaotia.py +189 -0
  97. novel_downloader/core/parsers/qbtr.py +136 -0
  98. novel_downloader/core/parsers/{qianbi/main_parser.py → qianbi.py} +54 -51
  99. novel_downloader/core/parsers/qidian/__init__.py +2 -2
  100. novel_downloader/core/parsers/qidian/book_info_parser.py +58 -59
  101. novel_downloader/core/parsers/qidian/chapter_encrypted.py +290 -346
  102. novel_downloader/core/parsers/qidian/chapter_normal.py +25 -56
  103. novel_downloader/core/parsers/qidian/main_parser.py +19 -57
  104. novel_downloader/core/parsers/qidian/utils/__init__.py +12 -11
  105. novel_downloader/core/parsers/qidian/utils/decryptor_fetcher.py +6 -7
  106. novel_downloader/core/parsers/qidian/utils/fontmap_recover.py +143 -0
  107. novel_downloader/core/parsers/qidian/utils/helpers.py +0 -4
  108. novel_downloader/core/parsers/qidian/utils/node_decryptor.py +2 -2
  109. novel_downloader/core/parsers/quanben5.py +103 -0
  110. novel_downloader/core/parsers/registry.py +57 -0
  111. novel_downloader/core/parsers/{sfacg/main_parser.py → sfacg.py} +46 -48
  112. novel_downloader/core/parsers/shencou.py +215 -0
  113. novel_downloader/core/parsers/shuhaige.py +111 -0
  114. novel_downloader/core/parsers/tongrenquan.py +116 -0
  115. novel_downloader/core/parsers/ttkan.py +132 -0
  116. novel_downloader/core/parsers/wanbengo.py +191 -0
  117. novel_downloader/core/parsers/xiaoshuowu.py +173 -0
  118. novel_downloader/core/parsers/xiguashuwu.py +435 -0
  119. novel_downloader/core/parsers/xs63b.py +161 -0
  120. novel_downloader/core/parsers/xshbook.py +134 -0
  121. novel_downloader/core/parsers/yamibo.py +155 -0
  122. novel_downloader/core/parsers/yibige.py +166 -0
  123. novel_downloader/core/searchers/__init__.py +51 -0
  124. novel_downloader/core/searchers/aaatxt.py +107 -0
  125. novel_downloader/core/searchers/b520.py +84 -0
  126. novel_downloader/core/searchers/base.py +168 -0
  127. novel_downloader/core/searchers/dxmwx.py +105 -0
  128. novel_downloader/core/searchers/eightnovel.py +84 -0
  129. novel_downloader/core/searchers/esjzone.py +102 -0
  130. novel_downloader/core/searchers/hetushu.py +92 -0
  131. novel_downloader/core/searchers/i25zw.py +93 -0
  132. novel_downloader/core/searchers/ixdzs8.py +107 -0
  133. novel_downloader/core/searchers/jpxs123.py +107 -0
  134. novel_downloader/core/searchers/piaotia.py +100 -0
  135. novel_downloader/core/searchers/qbtr.py +106 -0
  136. novel_downloader/core/searchers/qianbi.py +165 -0
  137. novel_downloader/core/searchers/quanben5.py +144 -0
  138. novel_downloader/core/searchers/registry.py +79 -0
  139. novel_downloader/core/searchers/shuhaige.py +124 -0
  140. novel_downloader/core/searchers/tongrenquan.py +110 -0
  141. novel_downloader/core/searchers/ttkan.py +92 -0
  142. novel_downloader/core/searchers/xiaoshuowu.py +122 -0
  143. novel_downloader/core/searchers/xiguashuwu.py +95 -0
  144. novel_downloader/core/searchers/xs63b.py +104 -0
  145. novel_downloader/locales/en.json +36 -79
  146. novel_downloader/locales/zh.json +37 -80
  147. novel_downloader/models/__init__.py +23 -50
  148. novel_downloader/models/book.py +44 -0
  149. novel_downloader/models/config.py +16 -43
  150. novel_downloader/models/login.py +1 -1
  151. novel_downloader/models/search.py +21 -0
  152. novel_downloader/resources/config/settings.toml +39 -74
  153. novel_downloader/resources/css_styles/intro.css +83 -0
  154. novel_downloader/resources/css_styles/main.css +30 -89
  155. novel_downloader/resources/json/xiguashuwu.json +718 -0
  156. novel_downloader/utils/__init__.py +43 -0
  157. novel_downloader/utils/chapter_storage.py +247 -226
  158. novel_downloader/utils/constants.py +5 -50
  159. novel_downloader/utils/cookies.py +6 -18
  160. novel_downloader/utils/crypto_utils/__init__.py +13 -0
  161. novel_downloader/utils/crypto_utils/aes_util.py +90 -0
  162. novel_downloader/utils/crypto_utils/aes_v1.py +619 -0
  163. novel_downloader/utils/crypto_utils/aes_v2.py +1143 -0
  164. novel_downloader/utils/{crypto_utils.py → crypto_utils/rc4.py} +3 -10
  165. novel_downloader/utils/epub/__init__.py +34 -0
  166. novel_downloader/utils/epub/builder.py +377 -0
  167. novel_downloader/utils/epub/constants.py +118 -0
  168. novel_downloader/utils/epub/documents.py +297 -0
  169. novel_downloader/utils/epub/models.py +120 -0
  170. novel_downloader/utils/epub/utils.py +179 -0
  171. novel_downloader/utils/file_utils/__init__.py +5 -30
  172. novel_downloader/utils/file_utils/io.py +9 -150
  173. novel_downloader/utils/file_utils/normalize.py +2 -2
  174. novel_downloader/utils/file_utils/sanitize.py +2 -7
  175. novel_downloader/utils/fontocr.py +207 -0
  176. novel_downloader/utils/i18n.py +2 -0
  177. novel_downloader/utils/logger.py +10 -16
  178. novel_downloader/utils/network.py +111 -252
  179. novel_downloader/utils/state.py +5 -90
  180. novel_downloader/utils/text_utils/__init__.py +16 -21
  181. novel_downloader/utils/text_utils/diff_display.py +6 -9
  182. novel_downloader/utils/text_utils/numeric_conversion.py +253 -0
  183. novel_downloader/utils/text_utils/text_cleaner.py +179 -0
  184. novel_downloader/utils/text_utils/truncate_utils.py +62 -0
  185. novel_downloader/utils/time_utils/__init__.py +6 -12
  186. novel_downloader/utils/time_utils/datetime_utils.py +23 -33
  187. novel_downloader/utils/time_utils/sleep_utils.py +5 -10
  188. novel_downloader/web/__init__.py +13 -0
  189. novel_downloader/web/components/__init__.py +11 -0
  190. novel_downloader/web/components/navigation.py +35 -0
  191. novel_downloader/web/main.py +66 -0
  192. novel_downloader/web/pages/__init__.py +17 -0
  193. novel_downloader/web/pages/download.py +78 -0
  194. novel_downloader/web/pages/progress.py +147 -0
  195. novel_downloader/web/pages/search.py +329 -0
  196. novel_downloader/web/services/__init__.py +17 -0
  197. novel_downloader/web/services/client_dialog.py +164 -0
  198. novel_downloader/web/services/cred_broker.py +113 -0
  199. novel_downloader/web/services/cred_models.py +35 -0
  200. novel_downloader/web/services/task_manager.py +264 -0
  201. novel_downloader-2.0.0.dist-info/METADATA +171 -0
  202. novel_downloader-2.0.0.dist-info/RECORD +210 -0
  203. {novel_downloader-1.4.5.dist-info → novel_downloader-2.0.0.dist-info}/entry_points.txt +1 -1
  204. novel_downloader/config/site_rules.py +0 -94
  205. novel_downloader/core/downloaders/biquge.py +0 -25
  206. novel_downloader/core/downloaders/esjzone.py +0 -25
  207. novel_downloader/core/downloaders/linovelib.py +0 -25
  208. novel_downloader/core/downloaders/sfacg.py +0 -25
  209. novel_downloader/core/downloaders/yamibo.py +0 -25
  210. novel_downloader/core/exporters/biquge.py +0 -25
  211. novel_downloader/core/exporters/esjzone.py +0 -25
  212. novel_downloader/core/exporters/qianbi.py +0 -25
  213. novel_downloader/core/exporters/sfacg.py +0 -25
  214. novel_downloader/core/exporters/yamibo.py +0 -25
  215. novel_downloader/core/factory/__init__.py +0 -20
  216. novel_downloader/core/factory/downloader.py +0 -73
  217. novel_downloader/core/factory/exporter.py +0 -58
  218. novel_downloader/core/factory/fetcher.py +0 -96
  219. novel_downloader/core/factory/parser.py +0 -86
  220. novel_downloader/core/fetchers/base/__init__.py +0 -14
  221. novel_downloader/core/fetchers/base/browser.py +0 -403
  222. novel_downloader/core/fetchers/biquge/__init__.py +0 -14
  223. novel_downloader/core/fetchers/common/__init__.py +0 -14
  224. novel_downloader/core/fetchers/esjzone/__init__.py +0 -14
  225. novel_downloader/core/fetchers/esjzone/browser.py +0 -204
  226. novel_downloader/core/fetchers/linovelib/__init__.py +0 -14
  227. novel_downloader/core/fetchers/linovelib/browser.py +0 -193
  228. novel_downloader/core/fetchers/qianbi/__init__.py +0 -14
  229. novel_downloader/core/fetchers/qidian/__init__.py +0 -14
  230. novel_downloader/core/fetchers/qidian/browser.py +0 -318
  231. novel_downloader/core/fetchers/sfacg/__init__.py +0 -14
  232. novel_downloader/core/fetchers/sfacg/browser.py +0 -189
  233. novel_downloader/core/fetchers/yamibo/__init__.py +0 -14
  234. novel_downloader/core/fetchers/yamibo/browser.py +0 -229
  235. novel_downloader/core/parsers/biquge/__init__.py +0 -10
  236. novel_downloader/core/parsers/biquge/main_parser.py +0 -134
  237. novel_downloader/core/parsers/common/__init__.py +0 -13
  238. novel_downloader/core/parsers/common/helper.py +0 -323
  239. novel_downloader/core/parsers/common/main_parser.py +0 -106
  240. novel_downloader/core/parsers/esjzone/__init__.py +0 -10
  241. novel_downloader/core/parsers/linovelib/__init__.py +0 -10
  242. novel_downloader/core/parsers/qianbi/__init__.py +0 -10
  243. novel_downloader/core/parsers/sfacg/__init__.py +0 -10
  244. novel_downloader/core/parsers/yamibo/__init__.py +0 -10
  245. novel_downloader/core/parsers/yamibo/main_parser.py +0 -194
  246. novel_downloader/models/browser.py +0 -21
  247. novel_downloader/models/chapter.py +0 -25
  248. novel_downloader/models/site_rules.py +0 -99
  249. novel_downloader/models/tasks.py +0 -33
  250. novel_downloader/models/types.py +0 -15
  251. novel_downloader/resources/css_styles/volume-intro.css +0 -56
  252. novel_downloader/resources/json/replace_word_map.json +0 -4
  253. novel_downloader/resources/text/blacklist.txt +0 -22
  254. novel_downloader/tui/__init__.py +0 -7
  255. novel_downloader/tui/app.py +0 -32
  256. novel_downloader/tui/main.py +0 -17
  257. novel_downloader/tui/screens/__init__.py +0 -14
  258. novel_downloader/tui/screens/home.py +0 -198
  259. novel_downloader/tui/screens/login.py +0 -74
  260. novel_downloader/tui/styles/home_layout.tcss +0 -79
  261. novel_downloader/tui/widgets/richlog_handler.py +0 -24
  262. novel_downloader/utils/cache.py +0 -24
  263. novel_downloader/utils/fontocr/__init__.py +0 -22
  264. novel_downloader/utils/fontocr/model_loader.py +0 -69
  265. novel_downloader/utils/fontocr/ocr_v1.py +0 -303
  266. novel_downloader/utils/fontocr/ocr_v2.py +0 -752
  267. novel_downloader/utils/hash_store.py +0 -279
  268. novel_downloader/utils/hash_utils.py +0 -103
  269. novel_downloader/utils/text_utils/chapter_formatting.py +0 -46
  270. novel_downloader/utils/text_utils/font_mapping.py +0 -28
  271. novel_downloader/utils/text_utils/text_cleaning.py +0 -107
  272. novel_downloader-1.4.5.dist-info/METADATA +0 -196
  273. novel_downloader-1.4.5.dist-info/RECORD +0 -165
  274. {novel_downloader-1.4.5.dist-info → novel_downloader-2.0.0.dist-info}/WHEEL +0 -0
  275. {novel_downloader-1.4.5.dist-info → novel_downloader-2.0.0.dist-info}/licenses/LICENSE +0 -0
  276. {novel_downloader-1.4.5.dist-info → novel_downloader-2.0.0.dist-info}/top_level.txt +0 -0
@@ -16,19 +16,9 @@ from platformdirs import user_config_path
16
16
  # -----------------------------------------------------------------------------
17
17
  PACKAGE_NAME = "novel_downloader" # Python package name
18
18
  APP_NAME = "NovelDownloader" # Display name
19
- APP_DIR_NAME = "novel_downloader" # Directory name for platformdirs
19
+ APP_DIR_NAME = PACKAGE_NAME # Directory name for platformdirs
20
20
  LOGGER_NAME = PACKAGE_NAME # Root logger name
21
21
 
22
- SUPPORTED_SITES = {
23
- "biquge",
24
- "esjzone",
25
- "linovelib",
26
- "qianbi",
27
- "qidian",
28
- "sfacg",
29
- "yamibo",
30
- }
31
-
32
22
  # -----------------------------------------------------------------------------
33
23
  # Base directories
34
24
  # -----------------------------------------------------------------------------
@@ -43,22 +33,17 @@ LOGGER_DIR = WORK_DIR / "logs"
43
33
  JS_SCRIPT_DIR = BASE_CONFIG_DIR / "scripts"
44
34
  DATA_DIR = BASE_CONFIG_DIR / "data"
45
35
  CONFIG_DIR = BASE_CONFIG_DIR / "config"
46
- MODEL_CACHE_DIR = BASE_CONFIG_DIR / "models"
47
36
 
48
37
  # -----------------------------------------------------------------------------
49
38
  # Default file paths
50
39
  # -----------------------------------------------------------------------------
51
40
  STATE_FILE = DATA_DIR / "state.json"
52
- HASH_STORE_FILE = DATA_DIR / "image_hashes.json"
53
41
  SETTING_FILE = CONFIG_DIR / "settings.json"
54
- SITE_RULES_FILE = CONFIG_DIR / "site_rules.json"
55
- DEFAULT_USER_DATA_DIR = DATA_DIR / "browser_data"
56
42
 
57
43
 
58
44
  # -----------------------------------------------------------------------------
59
45
  # Default preferences & headers
60
46
  # -----------------------------------------------------------------------------
61
- DEFAULT_USER_PROFILE_NAME = "Profile_1"
62
47
  DEFAULT_IMAGE_SUFFIX = ".jpg"
63
48
 
64
49
  DEFAULT_USER_AGENT = (
@@ -91,9 +76,7 @@ DEFAULT_SETTINGS_PATHS = [
91
76
 
92
77
  # CSS Styles
93
78
  CSS_MAIN_PATH = files("novel_downloader.resources.css_styles").joinpath("main.css")
94
- CSS_VOLUME_INTRO_PATH = files("novel_downloader.resources.css_styles").joinpath(
95
- "volume-intro.css"
96
- )
79
+ CSS_INTRO_PATH = files("novel_downloader.resources.css_styles").joinpath("intro.css")
97
80
 
98
81
  # Images
99
82
  VOLUME_BORDER_IMAGE_PATH = files("novel_downloader.resources.images").joinpath(
@@ -101,42 +84,14 @@ VOLUME_BORDER_IMAGE_PATH = files("novel_downloader.resources.images").joinpath(
101
84
  )
102
85
 
103
86
  # JSON
104
- REPLACE_WORD_MAP_PATH = files("novel_downloader.resources.json").joinpath(
105
- "replace_word_map.json"
106
- )
107
87
  LINOVELIB_FONT_MAP_PATH = files("novel_downloader.resources.json").joinpath(
108
88
  "linovelib_font_map.json"
109
89
  )
90
+ XIGUASHUWU_FONT_MAP_PATH = files("novel_downloader.resources.json").joinpath(
91
+ "xiguashuwu.json"
92
+ )
110
93
 
111
94
  # JavaScript
112
95
  QD_DECRYPT_SCRIPT_PATH = files("novel_downloader.resources.js_scripts").joinpath(
113
96
  "qidian_decrypt_node.js"
114
97
  )
115
-
116
- # Text Files
117
- BLACKLIST_PATH = files("novel_downloader.resources.text").joinpath("blacklist.txt")
118
-
119
- # ---------------------------------------------------------------------
120
- # Pretrained model registry (e.g. used in font recovery or OCR)
121
- # ---------------------------------------------------------------------
122
-
123
- # Hugging Face model repo for character recognition
124
- REC_CHAR_MODEL_REPO = "saudadez/rec_chinese_char"
125
-
126
- # Required files to be downloaded for the model
127
- REC_CHAR_MODEL_FILES = [
128
- "inference.pdmodel",
129
- "inference.pdiparams",
130
- "rec_custom_keys.txt",
131
- "char_freq.json",
132
- ]
133
-
134
- REC_CHAR_VECTOR_FILES = [
135
- "char_vectors.npy",
136
- "char_vectors.txt",
137
- ]
138
-
139
- REC_IMAGE_SHAPE_MAP = {
140
- "v1.0": "3,32,32",
141
- "v2.0": "3,48,48",
142
- }
@@ -6,20 +6,21 @@ novel_downloader.utils.cookies
6
6
  Utility for normalizing cookie input from user configuration.
7
7
  """
8
8
 
9
+ __all__ = ["parse_cookies", "get_cookie_value"]
10
+
9
11
  import json
10
12
  from collections.abc import Mapping
11
- from email.utils import parsedate_to_datetime
12
13
  from http.cookies import SimpleCookie
13
14
  from pathlib import Path
14
15
 
15
16
 
16
- def resolve_cookies(cookies: str | Mapping[str, str]) -> dict[str, str]:
17
+ def parse_cookies(cookies: str | Mapping[str, str]) -> dict[str, str]:
17
18
  """
18
19
  Parse cookies from a string or dictionary into a standard dictionary.
19
20
 
20
21
  Supports input like:
21
- - "key1=value1; key2=value2"
22
- - {"key1": "value1", "key2": "value2"}
22
+ * `"key1=value1; key2=value2"`
23
+ * `{"key1": "value1", "key2": "value2"}`
23
24
 
24
25
  :param cookies: Cookie string or dict-like object (e.g., from config)
25
26
  :return: A normalized cookie dictionary (key -> value)
@@ -35,20 +36,7 @@ def resolve_cookies(cookies: str | Mapping[str, str]) -> dict[str, str]:
35
36
  raise TypeError("Unsupported cookie format: must be str or dict-like")
36
37
 
37
38
 
38
- def parse_cookie_expires(value: str | None) -> int:
39
- if not value:
40
- return -1
41
- try:
42
- return int(value)
43
- except (ValueError, TypeError):
44
- try:
45
- dt = parsedate_to_datetime(value)
46
- return int(dt.timestamp())
47
- except Exception:
48
- return -1
49
-
50
-
51
- def find_cookie_value(state_files: list[Path], key: str) -> str:
39
+ def get_cookie_value(state_files: list[Path], key: str) -> str:
52
40
  for state_file in state_files:
53
41
  try:
54
42
  with state_file.open("r", encoding="utf-8") as f:
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ novel_downloader.utils.crypto_utils
4
+ -----------------------------------
5
+
6
+ Generic cryptographic utilities
7
+ """
8
+
9
+ __all__ = [
10
+ "rc4_crypt",
11
+ ]
12
+
13
+ from .rc4 import rc4_crypt
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ novel_downloader.utils.crypto_utils.aes_util
4
+ --------------------------------------------
5
+
6
+ AES decrypt functions.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ __all__ = ["aes_cbc_decrypt"]
12
+
13
+ from typing import Any
14
+
15
+ _BLOCK = 16
16
+ _VALID_KEY_SIZES = (16, 24, 32)
17
+
18
+
19
+ def _as_bytes(name: str, b: Any) -> bytes:
20
+ if isinstance(b, bytes):
21
+ return b
22
+ if isinstance(b, bytearray | memoryview):
23
+ return bytes(b)
24
+ raise TypeError(f"{name} must be bytes-like, got {type(b).__name__}")
25
+
26
+
27
+ def _validate_inputs(key: bytes, iv: bytes, data: bytes) -> None:
28
+ if len(iv) != _BLOCK:
29
+ raise ValueError(f"iv must be {_BLOCK} bytes, got {len(iv)}")
30
+ if len(key) not in _VALID_KEY_SIZES:
31
+ raise ValueError(
32
+ f"key length must be one of {_VALID_KEY_SIZES} bytes, got {len(key)}"
33
+ )
34
+ if len(data) % _BLOCK != 0:
35
+ raise ValueError(
36
+ f"ciphertext length must be a multiple of {_BLOCK} bytes, got {len(data)}"
37
+ )
38
+
39
+
40
+ try:
41
+ from Crypto.Cipher import AES as _PyAES
42
+ from Crypto.Util.Padding import unpad as _py_unpad
43
+
44
+ def aes_cbc_decrypt(
45
+ key: bytes, iv: bytes, data: bytes, block_size: int = _BLOCK
46
+ ) -> bytes:
47
+ """
48
+ AES-CBC decrypt + PKCS#7 unpad (PyCryptodome).
49
+
50
+ :param key: AES key (16/24/32 bytes)
51
+ :param iv: Initialization vector (16 bytes)
52
+ :param data: Ciphertext, length multiple of 16
53
+ :return: Plaintext bytes (unpadded)
54
+ :raises TypeError, ValueError: on invalid inputs
55
+ """
56
+ key_b = _as_bytes("key", key)
57
+ iv_b = _as_bytes("iv", iv)
58
+ data_b = _as_bytes("data", data)
59
+ if not data_b:
60
+ return b""
61
+ _validate_inputs(key_b, iv_b, data_b)
62
+ pt = _PyAES.new(key_b, _PyAES.MODE_CBC, iv_b).decrypt(data_b)
63
+ return _py_unpad(pt, block_size, style="pkcs7") # type: ignore[no-any-return]
64
+
65
+ except ImportError:
66
+ print(
67
+ "[crypto_utils] Falling back to pure-Python AES_CBC.\n"
68
+ "Tip: pip install pycryptodome for ~800x faster speed."
69
+ )
70
+ from novel_downloader.utils.crypto_utils.aes_v2 import AES_CBC
71
+
72
+ def aes_cbc_decrypt(
73
+ key: bytes, iv: bytes, data: bytes, block_size: int = _BLOCK
74
+ ) -> bytes:
75
+ """
76
+ AES-CBC decrypt + PKCS#7 unpad (handled by AES_CBC internally).
77
+
78
+ :param key: AES key (16/24/32 bytes)
79
+ :param iv: Initialization vector (16 bytes)
80
+ :param data: Ciphertext, length multiple of 16
81
+ :return: Plaintext bytes (unpadded)
82
+ :raises TypeError, ValueError: on invalid inputs
83
+ """
84
+ key_b = _as_bytes("key", key)
85
+ iv_b = _as_bytes("iv", iv)
86
+ data_b = _as_bytes("data", data)
87
+ if not data_b:
88
+ return b""
89
+ _validate_inputs(key_b, iv_b, data_b)
90
+ return AES_CBC(key_b, iv_b).decrypt_padded(data_b, block_size)