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
@@ -0,0 +1,127 @@
1
+ novel_downloader/__init__.py,sha256=eWGESJtMUUHTCWUQHmDXJ5aOFLJVc9qUXiG-D_qc7yY,218
2
+ novel_downloader/cli/__init__.py,sha256=-2HAut_U1e67MZGdvbpEJ1n5J-bRchzto6L4c-nWeXY,174
3
+ novel_downloader/cli/clean.py,sha256=yjPDEoJRZnP_Xq-y0vA2ZhyNP-GxCM1UosW4gVR3VBI,3687
4
+ novel_downloader/cli/download.py,sha256=Q6ySIHalzX5qRTs_R1VH2xWpFbw88_UIiqYy0olxqeg,3995
5
+ novel_downloader/cli/interactive.py,sha256=7VLwKpDnSlNrA9biRu8v1bzur-khT5509RtW7EzFNvU,2130
6
+ novel_downloader/cli/main.py,sha256=JJdIEhMyjo_W2M-Uo3tstnSWDbmip9-UqSTVN6-YV3I,1128
7
+ novel_downloader/cli/settings.py,sha256=WOpy6aIsL4g_Zn_1kL4GEpkKiyxmgnTVMBhwJFo9J8s,6532
8
+ novel_downloader/config/__init__.py,sha256=-d48yLzDISMNUTxNTcAJGNKPXiZCB6Jv6yQBNO28A-8,1105
9
+ novel_downloader/config/adapter.py,sha256=kbqOHXHpf6TYxOPgkAa-4EzSKcpJEvzfm48iWaVQhMs,6965
10
+ novel_downloader/config/loader.py,sha256=EeMJqM0erh8lV-eYbd9TyrWmPRi7VEEkSTFb8YMxYHU,5745
11
+ novel_downloader/config/models.py,sha256=FE58OXH70eJE2R-TGSOWz2OaLjI9uvcEsyq-5uHrj48,5231
12
+ novel_downloader/config/site_rules.py,sha256=Win_tfW9jlpktfg4PZCl8R_GKqJaSbwL_9zIFUsF0WE,2964
13
+ novel_downloader/core/__init__.py,sha256=lg9azoOEI0W1pO1FgPLIJ2fUWLSYyNdng2NNR7Jl_NI,755
14
+ novel_downloader/core/downloaders/__init__.py,sha256=YCDQDJNkk2zAhXF4y6XuXjGNTJobdpSUZ8Kc40xDCi8,580
15
+ novel_downloader/core/downloaders/base/__init__.py,sha256=lGbWlGjbtP2GeHr9BJ4s56QDhA13XT66PX-SNkWjCo8,257
16
+ novel_downloader/core/downloaders/base/base_async.py,sha256=_lgq0Wzf5HebMCFub1zgGDJ74gprXpz0OqID9m80UoQ,4200
17
+ novel_downloader/core/downloaders/base/base_sync.py,sha256=En_8TVQOwqseee0y0bLHkLJc3xpohG-oRccgbK1gf4M,5824
18
+ novel_downloader/core/downloaders/biquge/__init__.py,sha256=uZgwfQxpPotRSi7WILOyybl4Ztjnmas2SLR3tXLXm6I,196
19
+ novel_downloader/core/downloaders/biquge/biquge_sync.py,sha256=T_9flRTQ-CRYuzCpsLxeFvR_lMFkzQiSTLNKfZWfJb0,785
20
+ novel_downloader/core/downloaders/common/__init__.py,sha256=KZwtl9K7ZhN8Nnnk9BBOVWZ-QJ5GQBsRAoG7x2OlVYM,273
21
+ novel_downloader/core/downloaders/common/common_async.py,sha256=SNgxQWkAIBj0_d5b-ClwP-T9CjPJimzgdWJXXsIyv58,7456
22
+ novel_downloader/core/downloaders/common/common_sync.py,sha256=vIPoH6bCm1vCHg5rW_HxZg9vehuS0Cy1wr3B97G0qhY,6966
23
+ novel_downloader/core/downloaders/qidian/__init__.py,sha256=LCdrqLI2iTCGERmlAwKtr-PKtvDh_rs2vKD891-ixvo,189
24
+ novel_downloader/core/downloaders/qidian/qidian_sync.py,sha256=hGm24AnKRjkKEPDUUdXmdYH34coC3iwo21kRSBIcdeI,8065
25
+ novel_downloader/core/factory/__init__.py,sha256=q9eT93OcyyUDoZxwhKWdgb_kUTIJ8nGSisH0wuiuBSw,687
26
+ novel_downloader/core/factory/downloader.py,sha256=7uxGPIAKGwyJ-OfmyCM8a3ON0M4sHz0p6rMie2o7ts4,4878
27
+ novel_downloader/core/factory/parser.py,sha256=h3KZIDArl66Q1qeI_h778snglhmRYrkS6872B9YiQgA,1684
28
+ novel_downloader/core/factory/requester.py,sha256=oY1x8eMn982BG6oSlt3gSyYy_eT7bJgph9Uj_FgMvcI,3283
29
+ novel_downloader/core/factory/saver.py,sha256=v0wb7XlbxNRyczrfkU1DXtFmp_HCjd7u4wM27mkamfQ,1144
30
+ novel_downloader/core/interfaces/__init__.py,sha256=ym_dJH0x9UCMvqgIiy9__Tkek7hy_LS_i56KhpOKnXQ,791
31
+ novel_downloader/core/interfaces/async_downloader.py,sha256=uMEOpbgYAK0aAVSdnnRVCoCAiwhggovz9mEBV6uPEpo,961
32
+ novel_downloader/core/interfaces/async_requester.py,sha256=_LPbfWw2uJf7r3RbLjUdRRJWQJrSLRsUFHyrXC3XEds,2139
33
+ novel_downloader/core/interfaces/parser.py,sha256=THAlFhZ8MPYtH_Ulbv6Cw_nKwekQDbLfsytsMMt3NzE,1331
34
+ novel_downloader/core/interfaces/saver.py,sha256=BjXNeQLTC7Bg5mGyFoL9Yq7V1kC53POjXOWG9BFVBf4,1580
35
+ novel_downloader/core/interfaces/sync_downloader.py,sha256=1QRH0aQFd_xwliMXUyiEhog2k2tj2pMkJBjJyKbx7cs,917
36
+ novel_downloader/core/interfaces/sync_requester.py,sha256=sMtJVWdqwAgar_yMSE-ZcivdUCJgvHjtZWX_c2mX-XA,1926
37
+ novel_downloader/core/parsers/__init__.py,sha256=4bO2zhVFqg02G6xZ8fm-r-_AexQgwpQ2iQepQV5PESw,524
38
+ novel_downloader/core/parsers/base.py,sha256=lyS9Uv95ttQePr5_JXt-tmLbgQBJMr13EvSgBRGoYSQ,3099
39
+ novel_downloader/core/parsers/biquge/__init__.py,sha256=oaLgBLdMW7DcdsEGXW-S91Z6_p5XmBcBIp7TX79hmV4,173
40
+ novel_downloader/core/parsers/biquge/main_parser.py,sha256=Dx4Oilqi-lpoRd8O99BSwI0qqvdVT_RCLGjDcm5ZhTc,4408
41
+ novel_downloader/core/parsers/common/__init__.py,sha256=MzNUUxvf7jmeO0LvQ_FRW0QLKuYVXkkTgJ4CxWZKF-Y,341
42
+ novel_downloader/core/parsers/common/helper.py,sha256=SSNES1AlGu5LqPXN61Rp6SmVgR4oKaI2gvLJfEYBsF4,12054
43
+ novel_downloader/core/parsers/common/main_parser.py,sha256=SaUxkcHl5UHKPzDVlHAUwAOyV1F6X1tDQZJJpeDCse8,2987
44
+ novel_downloader/core/parsers/qidian/__init__.py,sha256=-KvMBzggUaj5zeZKFpVbbx5GRJdykwaFPfxJc4KzF-Q,484
45
+ novel_downloader/core/parsers/qidian/browser/__init__.py,sha256=6oRiuhptJHjNh9lhfe9bjh2_9fKv9h8f_mv-VvCs48o,315
46
+ novel_downloader/core/parsers/qidian/browser/chapter_encrypted.py,sha256=UyBX5NqfCPappbVhMP_mPCYUsTJesgkOs_VjrCjlfj4,17407
47
+ novel_downloader/core/parsers/qidian/browser/chapter_normal.py,sha256=1I6PkrGEOjHvz-VRdjm9PfXFA8uInifkVYgw597Xqto,3014
48
+ novel_downloader/core/parsers/qidian/browser/chapter_router.py,sha256=92pczn5_had3I5i67bmt2Qeg4pmOh5MquYtwJ3_ByzE,2124
49
+ novel_downloader/core/parsers/qidian/browser/main_parser.py,sha256=dwqEUG60Il5tWCpA8NCmazegm4fqmuXgV9oq8wNm0fw,3898
50
+ novel_downloader/core/parsers/qidian/session/__init__.py,sha256=hNxcEkTgD7mTZukHjCQm7L62HBiT7GUCOu6JZjPYwTY,308
51
+ novel_downloader/core/parsers/qidian/session/chapter_encrypted.py,sha256=kGL8mClK-fThEVNw8FQEnFTqJhn7kaoapFjK01NCpEo,15727
52
+ novel_downloader/core/parsers/qidian/session/chapter_normal.py,sha256=T3UOKRf0ET-ngz0oMfsSRU_24ODIaJ_wWdNo0CDcabU,3688
53
+ novel_downloader/core/parsers/qidian/session/chapter_router.py,sha256=dHumd9itE8Scai_h7pctI0o21f6HL4DtMpZpSZg2KDk,2001
54
+ novel_downloader/core/parsers/qidian/session/main_parser.py,sha256=h6sE0XByX1gbdUAi1sDEgmJ2xBANsicX5DB8T1IDPwo,3989
55
+ novel_downloader/core/parsers/qidian/session/node_decryptor.py,sha256=qKJ6BDkshtQNSHmrjnsbIVvAX08g4MmwIrQFJmakvYU,5629
56
+ novel_downloader/core/parsers/qidian/shared/__init__.py,sha256=9Yb0qCiszbIfuUUQ1gHKcW1xjItHGJug8aM2cSKLcY8,940
57
+ novel_downloader/core/parsers/qidian/shared/book_info_parser.py,sha256=kKkRuF1QUA97VlX7XO7zcwAjWhjIqYB5onx_uzvjj6g,4809
58
+ novel_downloader/core/parsers/qidian/shared/helpers.py,sha256=xdzWMHaQI_7K-SEV8iUcIj577ys2B2eU_IbOIC7Ilwk,4249
59
+ novel_downloader/core/requesters/__init__.py,sha256=bBUdZtuWqWnjdhUx8Hu7iajsBY3T18JLRFobh-u_45w,718
60
+ novel_downloader/core/requesters/base/__init__.py,sha256=lIwwdQWDf83NbAikZGFKUnkSa2jKX4jqKnSy0_zRRlg,296
61
+ novel_downloader/core/requesters/base/async_session.py,sha256=xayGshZd1tb98kfuMjC4KF8DQlU_SeoPk6m6vowLr9E,13000
62
+ novel_downloader/core/requesters/base/browser.py,sha256=FY_H6MLvUZllFyeYcfWsvBJnMhfouVrsQs4i5WYy9Tw,10609
63
+ novel_downloader/core/requesters/base/session.py,sha256=x7PAU8f2Lk3B7n6gIg58Ul6MqK5btZLQKkidEpKuETk,10526
64
+ novel_downloader/core/requesters/biquge/__init__.py,sha256=T1PQXdHUEhR0WRltFW4aTOCWIF3isWXUFx-WjUXU99M,184
65
+ novel_downloader/core/requesters/biquge/session.py,sha256=toSdptICqGWknk_J5Ocb0yQ8oD0zXQDCiA1qbpXzTzA,2563
66
+ novel_downloader/core/requesters/common/__init__.py,sha256=QRYbc4QEUszv_bgBdKCXHq82K8MIUhgYDMnm3iceAzI,465
67
+ novel_downloader/core/requesters/common/async_session.py,sha256=5RwGAXmtCdeLCuMkqT_ZyERi1lYgOpUE1gTTf_f2uck,3051
68
+ novel_downloader/core/requesters/common/session.py,sha256=bqr4viPytTaD6XRuXEcSjSe6CfI9zABqhbITFvyrx-Y,3430
69
+ novel_downloader/core/requesters/qidian/__init__.py,sha256=DyiuNMoBVAVxMJspdElgQ8fvpf4gKtCAc_nqW4qQ0rM,506
70
+ novel_downloader/core/requesters/qidian/broswer.py,sha256=stTywDGR7BdcLnZaNNgkPyo2PRERwollrAKeuOgrJR8,10642
71
+ novel_downloader/core/requesters/qidian/session.py,sha256=TsszpDnUQa5Lj0KpnQccowIS1APITnZ2v63OXbQmCtg,9036
72
+ novel_downloader/core/savers/__init__.py,sha256=Wnjy7te6uZtSbD_lo2wCBrMprATu8y-UAh-S-HEI9q4,427
73
+ novel_downloader/core/savers/base.py,sha256=7KCfDdE_Tq5nyoGGenXryd-nortwbJvZKteMsldCpi0,5514
74
+ novel_downloader/core/savers/biquge.py,sha256=8kZ6hcXuXmaizaKKytjy3LIGX5kMJRwgUJdhYzH5ajE,445
75
+ novel_downloader/core/savers/qidian.py,sha256=HtIz2SHLL-BZbVuahu6voKpgWtlli14_YYCA9qv7pMA,704
76
+ novel_downloader/core/savers/common/__init__.py,sha256=V5EbaRwmdXlQKi4Ce9SLstl0c9x9yTTRUB23rXFCvXw,256
77
+ novel_downloader/core/savers/common/epub.py,sha256=ZmTt3kAODa8lQKpgegbJY_7qcKQmoVQ8h5JPZYBSJbI,6370
78
+ novel_downloader/core/savers/common/main_saver.py,sha256=O-XFfDAlOun0kSBBHEIktwUcm47CsXgmFOkyK3fP_1M,3826
79
+ novel_downloader/core/savers/common/txt.py,sha256=nsYQp3-xti5uAj3r736ZKDg0gzrme2YoQ8bUTOG2jHk,4853
80
+ novel_downloader/core/savers/epub_utils/__init__.py,sha256=ZzOOsuHL9O0CsE9xl0vAcvxxlBz4AkDVOlTnsfNGN7Q,714
81
+ novel_downloader/core/savers/epub_utils/css_builder.py,sha256=sa8t72YNKkGeqC6dflXz1iefcpUqZ6t80VVZE91XfNw,2087
82
+ novel_downloader/core/savers/epub_utils/initializer.py,sha256=6qId5UTSjXnqSs9S9XrWGELAgq4fNut71CYIq7b6LQE,3199
83
+ novel_downloader/core/savers/epub_utils/text_to_html.py,sha256=omqob2yKFYru2dvHTQuGVQH9RH_VQXxMSopzwd_-6_I,4087
84
+ novel_downloader/core/savers/epub_utils/volume_intro.py,sha256=aJAdEU2YvYkX2sqnU5VkZ33XRfb5x083GdDmDXcQpUI,1771
85
+ novel_downloader/locales/en.json,sha256=Q7dkpaet_NyjMRIHiBaIu5JBz1pX3-VdffpcwYBZbRQ,5888
86
+ novel_downloader/locales/zh.json,sha256=Aq1zxm18-ijifsExK5kZeBLDV8drYRsHslsjJd9v9QU,5782
87
+ novel_downloader/resources/config/rules.toml,sha256=hrED6h3Z3cjSY5hRPQhp4TFAU5QXnN9xHfVABOJQNrM,4979
88
+ novel_downloader/resources/config/settings.toml,sha256=yLqI38FhY6d9_fiKUSsabIo_YRR3j8uRDTu85Bf70uQ,3769
89
+ novel_downloader/resources/css_styles/main.css,sha256=WM6GePwdOGgM86fbbOxQ0_0oerTBDZeQHt8zRVfcJp8,1617
90
+ novel_downloader/resources/css_styles/volume-intro.css,sha256=6gaUnNKkrb2w8tYJRq1BGD1FwbhT1I5W2GI_Zelo9G4,1156
91
+ novel_downloader/resources/images/volume_border.png,sha256=2dEVimnTHKOfLMhi7bhkh_5joWNnrqg8duomLSNOZx4,28613
92
+ novel_downloader/resources/js_scripts/qidian_decrypt_node.js,sha256=spNrk_gXI7pPW9abr4XGc2LASMe1UuN4BUe4cH24L8s,2195
93
+ novel_downloader/resources/json/replace_word_map.json,sha256=ptL9sGO9aK7rnnAaOIyZ0OiH7gaT0BhFzficzYZSDks,55
94
+ novel_downloader/resources/text/blacklist.txt,sha256=sovK9JgARZP3lud5b1EZgvv8LSVKPthf4ADpCSZZgQ8,154
95
+ novel_downloader/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
96
+ novel_downloader/utils/cache.py,sha256=_jVEWAaMGY86kWVnAuuIvJA_hA8dH2ClI7o3p99mEvE,618
97
+ novel_downloader/utils/chapter_storage.py,sha256=bbQ6mLvIzhWNUypIcgg6-nPo1_fIgGCOyQ3eejuvcK4,10100
98
+ novel_downloader/utils/constants.py,sha256=7qr6_w7bM-Xf-Pz6ZFgamsR9JNl7RgFNmHvSWN2fcgg,5507
99
+ novel_downloader/utils/crypto_utils.py,sha256=9uMNpg0rAWaoD3YsqDucFajPjoQuwEfeZDInUBISuSI,4166
100
+ novel_downloader/utils/hash_store.py,sha256=pFg2SzPF9sZqezNx-24jSDvYw4YMSBOpiIjyUbZx_r8,9739
101
+ novel_downloader/utils/hash_utils.py,sha256=oaXICmANsqImXW1qNSVmVQbMfNbx35FHe7EHL2VT2tM,2974
102
+ novel_downloader/utils/i18n.py,sha256=pdAcSIA5Tp-uPEBwNByHL7C1NayTnpOsl7zFv9p2G1k,1033
103
+ novel_downloader/utils/logger.py,sha256=n5xggOejtNdZv2X9f4Cq8weOaT1KG0n0-im2LIYycu0,3377
104
+ novel_downloader/utils/model_loader.py,sha256=JKgRFrr4HlAW9zuDUBAuuo_Kk_T_g9dWiU8E3zYk0vo,1996
105
+ novel_downloader/utils/network.py,sha256=2VX33jo11tzHSqvinpd0VoXDkXK_uxdxqaq50ndWXXg,8622
106
+ novel_downloader/utils/state.py,sha256=FcNJ85GvBu7uEIjy0QHGr4sXMbHPEMkCjwUKNg5EabI,5132
107
+ novel_downloader/utils/file_utils/__init__.py,sha256=zvOm2qSEmWd_mRGJceGBZb5MYMSDAlWYjS5MkVQNZgI,1159
108
+ novel_downloader/utils/file_utils/io.py,sha256=73uRrr41_Obqy9HMFBnvcOcApMZYBNi4KWT4DpskSk8,7472
109
+ novel_downloader/utils/file_utils/normalize.py,sha256=MrsCq4FqmskKRkHRV_J0z0dmn69OerMum-9sqx2XOGM,2023
110
+ novel_downloader/utils/file_utils/sanitize.py,sha256=rE-u4vpDL10zH8FT8d9wqwWsz-7dR6PJ-LE45K8VaeE,2112
111
+ novel_downloader/utils/fontocr/__init__.py,sha256=fe-04om3xxBvFKt5BBCApXCzv-Z0K_AY7lv9IB1jEHM,543
112
+ novel_downloader/utils/fontocr/ocr_v1.py,sha256=WreUf0E0n9oDP2C2JBDKVT2_Lsm46zKuFjYAySI64LY,11209
113
+ novel_downloader/utils/fontocr/ocr_v2.py,sha256=HpcHZYl3BCMICqeznghkUre0shHRO9KhhjrGSrtFbzE,27311
114
+ novel_downloader/utils/text_utils/__init__.py,sha256=JQtEnJ22K9o_syY5PV4Bf_p4BKZonX5g9-A5eqlxpZs,806
115
+ novel_downloader/utils/text_utils/chapter_formatting.py,sha256=9y5TodhpC-FNh5cTHNQNKDEMF9WLyA4pCxuGTLzY6KA,1279
116
+ novel_downloader/utils/text_utils/diff_display.py,sha256=aUvjMcYO-1_P8ZYiYbmYbJOByKo2bWoBV_ifRuAqwb8,2528
117
+ novel_downloader/utils/text_utils/font_mapping.py,sha256=ePpNLSrIEwIpqpnPZBMODj-gyNAPjLITyt6JQ50EfNw,867
118
+ novel_downloader/utils/text_utils/text_cleaning.py,sha256=KQhTGKiSX-eBVmjSpggxtf1hSjQLTu3cIjt65Ir4SWs,1632
119
+ novel_downloader/utils/time_utils/__init__.py,sha256=BwlvP0T9ABLdIxfcZXPmPv2yWO6F3-JiPwnBJioLouE,556
120
+ novel_downloader/utils/time_utils/datetime_utils.py,sha256=DfKobjxcrJlPdB32rqaP2ZCQ6ORohiwt6n2TRFrBraM,4815
121
+ novel_downloader/utils/time_utils/sleep_utils.py,sha256=FshjN9WW-y_pC5GJmN62J7uwXtRBzW3Wr82CEhg_UnU,1809
122
+ novel_downloader-1.3.0.dist-info/licenses/LICENSE,sha256=XgmnH0mBf-qEiizoVAfJQAKzPB9y3rBa-ni7M0Aqv4A,1066
123
+ novel_downloader-1.3.0.dist-info/METADATA,sha256=iOt-Pm4Bib-mlFWTwXCGxicG_yDbc8BL1NohQINm2LI,6154
124
+ novel_downloader-1.3.0.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
125
+ novel_downloader-1.3.0.dist-info/entry_points.txt,sha256=v23QrJrfrAcYpxUYslCVxubOVRRTaTw7vlG_tfMsFP8,65
126
+ novel_downloader-1.3.0.dist-info/top_level.txt,sha256=hP4jYWM2LTm1jxsW4hqEB8N0dsRvldO2QdhggJT917I,17
127
+ novel_downloader-1.3.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.7.1)
2
+ Generator: setuptools (80.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,214 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- novel_downloader.core.requesters.base_browser
5
- ---------------------------------------------
6
-
7
- This module defines the BaseBrowser class, which provides common functionalities
8
- for browser operations. Derived classes can extend these methods for
9
- specialized purposes.
10
- """
11
-
12
- import abc
13
- import logging
14
- from typing import Any, Dict, Literal, Optional, cast
15
-
16
- from DrissionPage import Chromium, ChromiumOptions
17
- from DrissionPage._pages.mix_tab import MixTab
18
-
19
- from novel_downloader.config.models import RequesterConfig
20
- from novel_downloader.core.interfaces import RequesterProtocol
21
- from novel_downloader.utils.constants import (
22
- DEFAULT_USER_AGENT,
23
- DEFAULT_USER_DATA_DIR,
24
- DEFAULT_USER_PROFILE_NAME,
25
- )
26
-
27
- logger = logging.getLogger(__name__)
28
-
29
-
30
- def _is_valid(value: str) -> bool:
31
- return bool(value and value.strip())
32
-
33
-
34
- class BaseBrowser(RequesterProtocol, abc.ABC):
35
- """
36
- BaseBrowser wraps basic browser operations using DrissionPage,
37
- with full control over browser configuration, session profile,
38
- retry and timeout behavior.
39
-
40
- Attributes:
41
- _options (ChromiumOptions): Configuration object for Chromium.
42
- _browser (Chromium): Chromium instance.
43
- _page (ChromiumPage): The active browser tab.
44
- """
45
-
46
- def is_async(self) -> Literal[False]:
47
- return False
48
-
49
- def _init_browser(self, config: RequesterConfig) -> None:
50
- """
51
- Initialize the browser with specified options from RequesterConfig.
52
-
53
- :param config: Configuration settings for
54
- browser behavior, profile, timeouts, etc.
55
- """
56
- self._config = config
57
- self._options = ChromiumOptions()
58
-
59
- user_data_path = (
60
- config.user_data_folder
61
- if _is_valid(config.user_data_folder)
62
- else DEFAULT_USER_DATA_DIR
63
- )
64
- if _is_valid(config.user_data_folder):
65
- logger.warning(
66
- "[browser] Using user_data_folder='%s'. "
67
- "This may interfere with an active Chrome session. "
68
- "Do NOT use this profile in both the browser and "
69
- "this script at the same time.",
70
- config.user_data_folder,
71
- )
72
- self._options.set_user_data_path(user_data_path)
73
-
74
- profile_name = (
75
- config.profile_name
76
- if _is_valid(config.profile_name)
77
- else DEFAULT_USER_PROFILE_NAME
78
- )
79
- self._options.set_user(profile_name)
80
-
81
- self._options.headless(config.headless)
82
- self._options.set_user_agent(DEFAULT_USER_AGENT)
83
- self._options.set_timeouts(base=config.wait_time)
84
- self._options.set_retry(
85
- times=config.retry_times, interval=config.retry_interval
86
- )
87
-
88
- self._disable_images_orig = config.disable_images
89
- if config.disable_images:
90
- self._options.no_imgs(True)
91
- if config.mute_audio:
92
- self._options.mute(True)
93
-
94
- # self._options.set_argument('--disable-blink-features', 'AutomationControlled')
95
- # self._options.set_argument('--log-level', '3')
96
- # self._options.set_argument('--disable-gpu')
97
- # self._options.set_argument('no-sandbox')
98
-
99
- self._setup()
100
-
101
- def _setup(self) -> None:
102
- """
103
- Set up the browser instance and open the default tab.
104
- """
105
- self._browser = Chromium(self._options)
106
- self._page = cast(MixTab, self._browser.get_tab())
107
-
108
- def login(self, max_retries: int = 3, manual_login: bool = False) -> bool:
109
- """
110
- Attempt to log in
111
- """
112
- raise NotImplementedError(
113
- "Login is not supported by this browser type. "
114
- "Override login() in your subclass to enable it."
115
- )
116
-
117
- @abc.abstractmethod
118
- def get_book_info(self, book_id: str, wait_time: Optional[float] = None) -> str:
119
- """
120
- Fetch the raw HTML (or JSON) of the book info page.
121
-
122
- :param book_id: The book identifier.
123
- :param wait_time: Base number of seconds to wait before returning content.
124
- :return: The page content as a string.
125
- """
126
- ...
127
-
128
- @abc.abstractmethod
129
- def get_book_chapter(
130
- self, book_id: str, chapter_id: str, wait_time: Optional[float] = None
131
- ) -> str:
132
- """
133
- Fetch the raw HTML (or JSON) of a single chapter.
134
-
135
- :param book_id: The book identifier.
136
- :param chapter_id: The chapter identifier.
137
- :param wait_time: Base number of seconds to wait before returning content.
138
- :return: The chapter content as a string.
139
- """
140
- ...
141
-
142
- def get_bookcase(self, wait_time: Optional[float] = None) -> str:
143
- """
144
- Optional: Retrieve the HTML content of the authenticated user's bookcase page.
145
-
146
- Subclasses that support login+bookcase retrieval should override this.
147
-
148
- :param wait_time: Base number of seconds to wait before returning content.
149
- :return: The HTML markup of the bookcase page.
150
- :raises NotImplementedError: If bookcase fetching is not supported.
151
- """
152
- raise NotImplementedError(
153
- "Bookcase fetching is not supported by this browser type. "
154
- "Override get_bookcase() in your subclass to enable it."
155
- )
156
-
157
- @property
158
- def page(self) -> Optional[MixTab]:
159
- """
160
- Return the current Chromium page object.
161
-
162
- :return: ChromiumPage instance of the current tab.
163
- """
164
- return self._page
165
-
166
- @property
167
- def browser(self) -> Optional[Chromium]:
168
- """
169
- Return the Chromium browser instance.
170
-
171
- :return: Chromium instance used by this browser.
172
- """
173
- return self._browser
174
-
175
- def _clear_browser_refs(self) -> None:
176
- """
177
- Clear internal browser/page references without quitting.
178
- """
179
- self._browser = None
180
- self._page = None
181
-
182
- def shutdown(self) -> None:
183
- """
184
- Shutdown the browser session and release resources.
185
-
186
- This quits the Chromium instance and clears references to browser and page.
187
- """
188
- if self._browser:
189
- self._browser.quit()
190
- self._clear_browser_refs()
191
-
192
- def __getstate__(self) -> Dict[str, Any]:
193
- """
194
- Prepare object state for serialization (e.g., pickling).
195
-
196
- Removes browser-related fields that cannot be pickled.
197
-
198
- :return: A dict representing the serializable object state.
199
- """
200
- state = self.__dict__.copy()
201
- state.pop("_browser", None)
202
- state.pop("_page", None)
203
- return state
204
-
205
- def __setstate__(self, state: Dict[str, Any]) -> None:
206
- """
207
- Restore object state after deserialization.
208
-
209
- Automatically reinitializes the browser setup.
210
-
211
- :param state: The saved state dictionary.
212
- """
213
- self.__dict__.update(state)
214
- self._setup()
@@ -1,246 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- novel_downloader.core.requesters.base_session
5
- ---------------------------------------------
6
-
7
- This module defines the BaseSession class, which provides basic HTTP
8
- request capabilities using the requests library. It maintains a
9
- persistent session and supports retries, headers, and timeout configurations.
10
- """
11
-
12
- import abc
13
- from typing import Any, Dict, Literal, Optional, Union
14
-
15
- import requests
16
- from requests import Response, Session
17
- from requests.adapters import HTTPAdapter, Retry
18
-
19
- from novel_downloader.config.models import RequesterConfig
20
- from novel_downloader.core.interfaces import RequesterProtocol
21
- from novel_downloader.utils.constants import DEFAULT_USER_HEADERS
22
-
23
-
24
- class BaseSession(RequesterProtocol, abc.ABC):
25
- """
26
- BaseSession wraps basic HTTP operations using requests.Session,
27
- supporting retry logic, timeout, and persistent connections.
28
-
29
- Attributes:
30
- _session (requests.Session): The persistent HTTP session.
31
- _timeout (float): Timeout for each request in seconds.
32
- """
33
-
34
- def is_async(self) -> Literal[False]:
35
- return False
36
-
37
- def _init_session(
38
- self, config: RequesterConfig, cookies: Optional[Dict[str, str]] = None
39
- ) -> None:
40
- """
41
- Initialize the requests.Session with default headers and retry strategy.
42
-
43
- :param config: Configuration object for session behavior
44
- (timeouts, retries, headers, etc.)
45
- """
46
- self._config = config
47
- self._timeout = config.timeout
48
- self._retry_times = config.retry_times
49
- self._retry_interval = config.retry_interval
50
- self._cookies = cookies or {}
51
- self._headers = DEFAULT_USER_HEADERS
52
- self._session: Optional[Session] = None
53
-
54
- self._setup()
55
-
56
- def _setup(self) -> None:
57
- """
58
- Set up the session with retry strategy and apply default headers.
59
- """
60
- self._session = requests.Session()
61
-
62
- retry_strategy = Retry(
63
- total=self._config.retry_times,
64
- backoff_factor=self._config.retry_interval,
65
- status_forcelist=[429, 500, 502, 503, 504],
66
- allowed_methods=["HEAD", "GET", "OPTIONS"],
67
- )
68
-
69
- adapter = HTTPAdapter(max_retries=retry_strategy)
70
- self._session.mount("http://", adapter)
71
- self._session.mount("https://", adapter)
72
- self._session.headers.update(self._headers)
73
-
74
- if self._cookies:
75
- self._session.cookies.update(self._cookies)
76
-
77
- def login(self, max_retries: int = 3, manual_login: bool = False) -> bool:
78
- """
79
- Attempt to log in
80
- """
81
- raise NotImplementedError(
82
- "Login is not supported by this session type. "
83
- "Override login() in your subclass to enable it."
84
- )
85
-
86
- @abc.abstractmethod
87
- def get_book_info(self, book_id: str, wait_time: Optional[float] = None) -> str:
88
- """
89
- Fetch the raw HTML (or JSON) of the book info page.
90
-
91
- :param book_id: The book identifier.
92
- :param wait_time: Base number of seconds to wait before returning content.
93
- :return: The page content as a string.
94
- """
95
- ...
96
-
97
- @abc.abstractmethod
98
- def get_book_chapter(
99
- self, book_id: str, chapter_id: str, wait_time: Optional[float] = None
100
- ) -> str:
101
- """
102
- Fetch the raw HTML (or JSON) of a single chapter.
103
-
104
- :param book_id: The book identifier.
105
- :param chapter_id: The chapter identifier.
106
- :param wait_time: Base number of seconds to wait before returning content.
107
- :return: The chapter content as a string.
108
- """
109
- ...
110
-
111
- def get_bookcase(self, wait_time: Optional[float] = None) -> str:
112
- """
113
- Optional: Retrieve the HTML content of the authenticated user's bookcase page.
114
-
115
- Subclasses that support user login and bookcase retrieval should override this.
116
-
117
- :param wait_time: Base number of seconds to wait before returning content.
118
- :return: The HTML markup of the bookcase page.
119
- :raises NotImplementedError: If the subclass does not implement.
120
- """
121
- raise NotImplementedError(
122
- "Bookcase fetching is not supported by this session type. "
123
- "Override get_bookcase() in your subclass to enable it."
124
- )
125
-
126
- def get(
127
- self, url: str, params: Optional[Dict[str, Any]] = None, **kwargs: Any
128
- ) -> Response:
129
- """
130
- Send a GET request.
131
-
132
- :param url: The target URL.
133
- :param params: Query parameters to include in the request.
134
- :param kwargs: Additional arguments passed to requests.
135
- :return: Response object from the GET request.
136
- :raises RuntimeError: If the session is not initialized.
137
- """
138
- if not self._session:
139
- raise RuntimeError("Session is not initialized or has been shut down.")
140
- return self._session.get(url, params=params, timeout=self._timeout, **kwargs)
141
-
142
- def post(
143
- self,
144
- url: str,
145
- data: Optional[Union[Dict[str, Any], bytes]] = None,
146
- json: Optional[Dict[str, Any]] = None,
147
- **kwargs: Any,
148
- ) -> Response:
149
- """
150
- Send a POST request.
151
-
152
- :param url: The target URL.
153
- :param data: Form data to include in the request body.
154
- :param json: JSON body to include in the request.
155
- :param kwargs: Additional arguments passed to requests.
156
- :return: Response object from the POST request.
157
- :raises RuntimeError: If the session is not initialized.
158
- """
159
- if not self._session:
160
- raise RuntimeError("Session is not initialized or has been shut down.")
161
- return self._session.post(
162
- url, data=data, json=json, timeout=self._timeout, **kwargs
163
- )
164
-
165
- @property
166
- def session(self) -> Session:
167
- """
168
- Return the active requests.Session.
169
-
170
- :raises RuntimeError: If the session is uninitialized or has been shut down.
171
- """
172
- if self._session is None:
173
- raise RuntimeError("Session is not initialized or has been shut down.")
174
- return self._session
175
-
176
- @property
177
- def timeout(self) -> float:
178
- """Return the default timeout setting."""
179
- return self._timeout
180
-
181
- @property
182
- def retry_times(self) -> int:
183
- """Return the maximum number of retry attempts."""
184
- return self._retry_times
185
-
186
- @property
187
- def retry_interval(self) -> float:
188
- """Return the base interval (in seconds) between retries."""
189
- return self._retry_interval
190
-
191
- @property
192
- def headers(self) -> Dict[str, str]:
193
- """Return the default headers."""
194
- if not self._session:
195
- return {}
196
- return {k: v for k, v in self._session.headers.items() if isinstance(v, str)}
197
-
198
- def update_cookies(self, cookies: Dict[str, str], overwrite: bool = True) -> None:
199
- """
200
- Update cookies for the current session (if initialized) as well as for the
201
- internal cache kept in ``self._cookies`` so that subsequent ``_setup`` calls
202
- also see the latest values.
203
- """
204
- if not cookies:
205
- return
206
-
207
- if overwrite:
208
- for k, v in cookies.items():
209
- self._cookies[str(k)] = str(v)
210
- else:
211
- for k, v in cookies.items():
212
- self._cookies.setdefault(str(k), str(v))
213
-
214
- if self._session is not None:
215
- self._session.cookies.update(self._cookies)
216
-
217
- def shutdown(self) -> None:
218
- """
219
- Shutdown and clean up the session.
220
-
221
- This closes the underlying connection pool and removes the session.
222
- """
223
- if self._session:
224
- self._session.close()
225
- self._session = None
226
-
227
- def __getstate__(self) -> Dict[str, Any]:
228
- """
229
- Prepare object state for serialization.
230
-
231
- Removes unpickleable session object.
232
-
233
- :return: Serializable dict of the object state.
234
- """
235
- state = self.__dict__.copy()
236
- state.pop("_session", None)
237
- return state
238
-
239
- def __setstate__(self, state: Dict[str, Any]) -> None:
240
- """
241
- Restore object state and reinitialize session.
242
-
243
- :param state: Saved state dictionary.
244
- """
245
- self.__dict__.update(state)
246
- self._setup()