novel-downloader 1.3.1__tar.gz → 1.3.2__tar.gz

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 (172) hide show
  1. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/PKG-INFO +25 -20
  2. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/README.md +24 -19
  3. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/__init__.py +1 -1
  4. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/cli/download.py +1 -1
  5. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/config/adapter.py +3 -0
  6. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/config/models.py +3 -0
  7. novel_downloader-1.3.2/novel_downloader/core/downloaders/__init__.py +44 -0
  8. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/biquge/__init__.py +2 -0
  9. novel_downloader-1.3.2/novel_downloader/core/downloaders/biquge/biquge_async.py +27 -0
  10. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/biquge/biquge_sync.py +5 -3
  11. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/common/common_async.py +5 -3
  12. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/common/common_sync.py +18 -10
  13. novel_downloader-1.3.2/novel_downloader/core/downloaders/esjzone/__init__.py +14 -0
  14. novel_downloader-1.3.2/novel_downloader/core/downloaders/esjzone/esjzone_async.py +27 -0
  15. novel_downloader-1.3.2/novel_downloader/core/downloaders/esjzone/esjzone_sync.py +27 -0
  16. novel_downloader-1.3.2/novel_downloader/core/downloaders/qianbi/__init__.py +14 -0
  17. novel_downloader-1.3.2/novel_downloader/core/downloaders/qianbi/qianbi_async.py +27 -0
  18. novel_downloader-1.3.2/novel_downloader/core/downloaders/qianbi/qianbi_sync.py +27 -0
  19. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/qidian/qidian_sync.py +9 -6
  20. novel_downloader-1.3.2/novel_downloader/core/downloaders/sfacg/__init__.py +14 -0
  21. novel_downloader-1.3.2/novel_downloader/core/downloaders/sfacg/sfacg_async.py +27 -0
  22. novel_downloader-1.3.2/novel_downloader/core/downloaders/sfacg/sfacg_sync.py +27 -0
  23. novel_downloader-1.3.2/novel_downloader/core/downloaders/yamibo/__init__.py +14 -0
  24. novel_downloader-1.3.2/novel_downloader/core/downloaders/yamibo/yamibo_async.py +27 -0
  25. novel_downloader-1.3.2/novel_downloader/core/downloaders/yamibo/yamibo_sync.py +27 -0
  26. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/factory/downloader.py +35 -7
  27. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/factory/parser.py +23 -2
  28. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/factory/requester.py +32 -7
  29. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/factory/saver.py +14 -2
  30. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/interfaces/async_requester.py +3 -3
  31. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/interfaces/parser.py +7 -2
  32. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/interfaces/sync_requester.py +3 -3
  33. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/__init__.py +15 -5
  34. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/base.py +7 -2
  35. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/biquge/main_parser.py +13 -4
  36. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/common/main_parser.py +13 -4
  37. novel_downloader-1.3.2/novel_downloader/core/parsers/esjzone/__init__.py +10 -0
  38. novel_downloader-1.3.2/novel_downloader/core/parsers/esjzone/main_parser.py +219 -0
  39. novel_downloader-1.3.2/novel_downloader/core/parsers/qianbi/__init__.py +10 -0
  40. novel_downloader-1.3.2/novel_downloader/core/parsers/qianbi/main_parser.py +142 -0
  41. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/browser/main_parser.py +13 -4
  42. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/session/main_parser.py +13 -4
  43. novel_downloader-1.3.2/novel_downloader/core/parsers/sfacg/__init__.py +10 -0
  44. novel_downloader-1.3.2/novel_downloader/core/parsers/sfacg/main_parser.py +166 -0
  45. novel_downloader-1.3.2/novel_downloader/core/parsers/yamibo/__init__.py +10 -0
  46. novel_downloader-1.3.2/novel_downloader/core/parsers/yamibo/main_parser.py +194 -0
  47. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/__init__.py +33 -3
  48. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/base/async_session.py +14 -10
  49. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/base/browser.py +4 -7
  50. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/base/session.py +25 -11
  51. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/biquge/__init__.py +2 -0
  52. novel_downloader-1.3.2/novel_downloader/core/requesters/biquge/async_session.py +71 -0
  53. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/biquge/session.py +6 -6
  54. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/common/async_session.py +4 -4
  55. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/common/session.py +6 -6
  56. novel_downloader-1.3.2/novel_downloader/core/requesters/esjzone/__init__.py +13 -0
  57. novel_downloader-1.3.2/novel_downloader/core/requesters/esjzone/async_session.py +211 -0
  58. novel_downloader-1.3.2/novel_downloader/core/requesters/esjzone/session.py +235 -0
  59. novel_downloader-1.3.2/novel_downloader/core/requesters/qianbi/__init__.py +13 -0
  60. novel_downloader-1.3.2/novel_downloader/core/requesters/qianbi/async_session.py +96 -0
  61. novel_downloader-1.3.2/novel_downloader/core/requesters/qianbi/session.py +125 -0
  62. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/qidian/broswer.py +9 -9
  63. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/qidian/session.py +14 -11
  64. novel_downloader-1.3.2/novel_downloader/core/requesters/sfacg/__init__.py +13 -0
  65. novel_downloader-1.3.2/novel_downloader/core/requesters/sfacg/async_session.py +204 -0
  66. novel_downloader-1.3.2/novel_downloader/core/requesters/sfacg/session.py +242 -0
  67. novel_downloader-1.3.2/novel_downloader/core/requesters/yamibo/__init__.py +13 -0
  68. novel_downloader-1.3.2/novel_downloader/core/requesters/yamibo/async_session.py +211 -0
  69. novel_downloader-1.3.2/novel_downloader/core/requesters/yamibo/session.py +237 -0
  70. novel_downloader-1.3.2/novel_downloader/core/savers/__init__.py +34 -0
  71. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/base.py +1 -0
  72. novel_downloader-1.3.2/novel_downloader/core/savers/esjzone.py +25 -0
  73. novel_downloader-1.3.2/novel_downloader/core/savers/qianbi.py +25 -0
  74. novel_downloader-1.3.2/novel_downloader/core/savers/sfacg.py +25 -0
  75. novel_downloader-1.3.2/novel_downloader/core/savers/yamibo.py +25 -0
  76. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/locales/en.json +1 -0
  77. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/locales/zh.json +1 -0
  78. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/resources/config/settings.toml +40 -4
  79. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/time_utils/__init__.py +2 -1
  80. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/time_utils/datetime_utils.py +3 -1
  81. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/time_utils/sleep_utils.py +43 -1
  82. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader.egg-info/PKG-INFO +25 -20
  83. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader.egg-info/SOURCES.txt +38 -0
  84. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/pyproject.toml +1 -1
  85. novel_downloader-1.3.1/novel_downloader/core/downloaders/__init__.py +0 -22
  86. novel_downloader-1.3.1/novel_downloader/core/savers/__init__.py +0 -22
  87. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/LICENSE +0 -0
  88. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/cli/__init__.py +0 -0
  89. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/cli/clean.py +0 -0
  90. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/cli/interactive.py +0 -0
  91. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/cli/main.py +0 -0
  92. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/cli/settings.py +0 -0
  93. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/config/__init__.py +0 -0
  94. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/config/loader.py +0 -0
  95. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/config/site_rules.py +0 -0
  96. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/__init__.py +0 -0
  97. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/base/__init__.py +0 -0
  98. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/base/base_async.py +0 -0
  99. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/base/base_sync.py +0 -0
  100. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/common/__init__.py +0 -0
  101. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/downloaders/qidian/__init__.py +0 -0
  102. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/factory/__init__.py +0 -0
  103. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/interfaces/__init__.py +0 -0
  104. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/interfaces/async_downloader.py +0 -0
  105. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/interfaces/saver.py +0 -0
  106. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/interfaces/sync_downloader.py +0 -0
  107. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/biquge/__init__.py +0 -0
  108. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/common/__init__.py +0 -0
  109. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/common/helper.py +0 -0
  110. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/__init__.py +0 -0
  111. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/browser/__init__.py +0 -0
  112. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/browser/chapter_encrypted.py +0 -0
  113. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/browser/chapter_normal.py +0 -0
  114. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/browser/chapter_router.py +0 -0
  115. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/session/__init__.py +0 -0
  116. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/session/chapter_encrypted.py +0 -0
  117. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/session/chapter_normal.py +0 -0
  118. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/session/chapter_router.py +0 -0
  119. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/session/node_decryptor.py +0 -0
  120. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/shared/__init__.py +0 -0
  121. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/shared/book_info_parser.py +0 -0
  122. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/parsers/qidian/shared/helpers.py +0 -0
  123. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/base/__init__.py +0 -0
  124. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/common/__init__.py +0 -0
  125. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/requesters/qidian/__init__.py +0 -0
  126. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/biquge.py +0 -0
  127. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/common/__init__.py +0 -0
  128. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/common/epub.py +0 -0
  129. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/common/main_saver.py +0 -0
  130. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/common/txt.py +0 -0
  131. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/epub_utils/__init__.py +0 -0
  132. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/epub_utils/css_builder.py +0 -0
  133. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/epub_utils/initializer.py +0 -0
  134. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/epub_utils/text_to_html.py +0 -0
  135. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/epub_utils/volume_intro.py +0 -0
  136. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/core/savers/qidian.py +0 -0
  137. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/resources/config/rules.toml +0 -0
  138. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/resources/css_styles/main.css +0 -0
  139. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/resources/css_styles/volume-intro.css +0 -0
  140. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/resources/images/volume_border.png +0 -0
  141. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/resources/js_scripts/qidian_decrypt_node.js +0 -0
  142. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/resources/json/replace_word_map.json +0 -0
  143. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/resources/text/blacklist.txt +0 -0
  144. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/__init__.py +0 -0
  145. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/cache.py +0 -0
  146. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/chapter_storage.py +0 -0
  147. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/constants.py +0 -0
  148. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/crypto_utils.py +0 -0
  149. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/file_utils/__init__.py +0 -0
  150. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/file_utils/io.py +0 -0
  151. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/file_utils/normalize.py +0 -0
  152. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/file_utils/sanitize.py +0 -0
  153. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/fontocr/__init__.py +0 -0
  154. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/fontocr/ocr_v1.py +0 -0
  155. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/fontocr/ocr_v2.py +0 -0
  156. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/hash_store.py +0 -0
  157. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/hash_utils.py +0 -0
  158. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/i18n.py +0 -0
  159. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/logger.py +0 -0
  160. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/model_loader.py +0 -0
  161. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/network.py +0 -0
  162. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/state.py +0 -0
  163. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/text_utils/__init__.py +0 -0
  164. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/text_utils/chapter_formatting.py +0 -0
  165. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/text_utils/diff_display.py +0 -0
  166. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/text_utils/font_mapping.py +0 -0
  167. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader/utils/text_utils/text_cleaning.py +0 -0
  168. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader.egg-info/dependency_links.txt +0 -0
  169. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader.egg-info/entry_points.txt +0 -0
  170. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader.egg-info/requires.txt +0 -0
  171. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/novel_downloader.egg-info/top_level.txt +0 -0
  172. {novel_downloader-1.3.1 → novel_downloader-1.3.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: novel-downloader
3
- Version: 1.3.1
3
+ Version: 1.3.2
4
4
  Summary: A command-line tool for downloading Chinese web novels from Qidian and similar platforms.
5
5
  Author-email: Saudade Z <saudadez217@gmail.com>
6
6
  License: MIT License
@@ -80,9 +80,26 @@ Dynamic: license-file
80
80
  - 对于起点中文网 (Qidian), 可在配置中选择:
81
81
  - `mode: session` : 纯 Requests 模式
82
82
  - `mode: browser` : 基于 DrissionPage 驱动 Chrome 的浏览器模式 (可处理更复杂的 JS/加密)。
83
- - 如果在 `browser` 模式下且 `login_required: true`, 首次运行会自动打开浏览器, 请完成登录后继续。
83
+ - 若配置 `login_required: true`, 程序会在运行时自动检查登录状态, 支持自动重用历史 Cookie, 仅在首次登录或 Cookie 失效时需要人工介入:
84
+ - 若使用 `browser` 模式, 请在程序打开的浏览器窗口登录, 登录后回车继续
85
+ - 若使用 `session` 模式, 请根据程序提示粘贴浏览器中登录成功后的 Cookie ([复制 Cookies](https://github.com/BowenZ217/novel-downloader/blob/main/docs/copy-cookies.md))
84
86
 
85
- **快速开始**
87
+ ## 功能特性
88
+
89
+ - 爬取起点中文网的小说章节内容 (支持免费与已订阅章节)
90
+ - 爬取部分小说网站
91
+ - 断点续爬
92
+ - 自动整合所有章节并导出为
93
+ - TXT
94
+ - EPUB
95
+ - 支持活动广告过滤:
96
+ - [x] 章节标题
97
+ - [ ] 章节正文
98
+ - [ ] 作者说
99
+
100
+ ---
101
+
102
+ ## 快速开始
86
103
 
87
104
  ```bash
88
105
  # 克隆 + 安装
@@ -101,7 +118,10 @@ novel-cli settings init
101
118
  novel-cli download 123456
102
119
  ```
103
120
 
104
- **从 GitHub 安装 (开发版)**
121
+ - 详细可见: [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-supported-sites.md)
122
+ - 更多使用方法, 查看 [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-usage-examples.md)
123
+
124
+ ## 从 GitHub 安装 (开发版)
105
125
 
106
126
  如需体验开发中的最新功能, 可通过 GitHub 安装:
107
127
 
@@ -113,22 +133,6 @@ pip install .
113
133
  # pip install .[font-recovery]
114
134
  ```
115
135
 
116
- 更多使用方法, 查看 [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-usage-examples.md)
117
-
118
- ---
119
-
120
- ## 功能特性
121
-
122
- - 爬取起点中文网的小说章节内容 (支持免费与已订阅章节)
123
- - 断点续爬
124
- - 自动整合所有章节并导出为
125
- - TXT
126
- - EPUB
127
- - 支持活动广告过滤:
128
- - [x] 章节标题
129
- - [ ] 章节正文
130
- - [ ] 作者说
131
-
132
136
  ---
133
137
 
134
138
  ## 文档结构
@@ -140,6 +144,7 @@ pip install .
140
144
  - [settings.toml 配置说明](https://github.com/BowenZ217/novel-downloader/blob/main/docs/4-settings-schema.md)
141
145
  - [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-usage-examples.md)
142
146
  - [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-supported-sites.md)
147
+ - [复制 Cookies](https://github.com/BowenZ217/novel-downloader/blob/main/docs/copy-cookies.md)
143
148
  - [文件保存](https://github.com/BowenZ217/novel-downloader/blob/main/docs/file-saving.md)
144
149
  - [TODO](https://github.com/BowenZ217/novel-downloader/blob/main/docs/todo.md)
145
150
  - [开发](https://github.com/BowenZ217/novel-downloader/blob/main/docs/develop.md)
@@ -11,9 +11,26 @@
11
11
  - 对于起点中文网 (Qidian), 可在配置中选择:
12
12
  - `mode: session` : 纯 Requests 模式
13
13
  - `mode: browser` : 基于 DrissionPage 驱动 Chrome 的浏览器模式 (可处理更复杂的 JS/加密)。
14
- - 如果在 `browser` 模式下且 `login_required: true`, 首次运行会自动打开浏览器, 请完成登录后继续。
14
+ - 若配置 `login_required: true`, 程序会在运行时自动检查登录状态, 支持自动重用历史 Cookie, 仅在首次登录或 Cookie 失效时需要人工介入:
15
+ - 若使用 `browser` 模式, 请在程序打开的浏览器窗口登录, 登录后回车继续
16
+ - 若使用 `session` 模式, 请根据程序提示粘贴浏览器中登录成功后的 Cookie ([复制 Cookies](https://github.com/BowenZ217/novel-downloader/blob/main/docs/copy-cookies.md))
15
17
 
16
- **快速开始**
18
+ ## 功能特性
19
+
20
+ - 爬取起点中文网的小说章节内容 (支持免费与已订阅章节)
21
+ - 爬取部分小说网站
22
+ - 断点续爬
23
+ - 自动整合所有章节并导出为
24
+ - TXT
25
+ - EPUB
26
+ - 支持活动广告过滤:
27
+ - [x] 章节标题
28
+ - [ ] 章节正文
29
+ - [ ] 作者说
30
+
31
+ ---
32
+
33
+ ## 快速开始
17
34
 
18
35
  ```bash
19
36
  # 克隆 + 安装
@@ -32,7 +49,10 @@ novel-cli settings init
32
49
  novel-cli download 123456
33
50
  ```
34
51
 
35
- **从 GitHub 安装 (开发版)**
52
+ - 详细可见: [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-supported-sites.md)
53
+ - 更多使用方法, 查看 [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-usage-examples.md)
54
+
55
+ ## 从 GitHub 安装 (开发版)
36
56
 
37
57
  如需体验开发中的最新功能, 可通过 GitHub 安装:
38
58
 
@@ -44,22 +64,6 @@ pip install .
44
64
  # pip install .[font-recovery]
45
65
  ```
46
66
 
47
- 更多使用方法, 查看 [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-usage-examples.md)
48
-
49
- ---
50
-
51
- ## 功能特性
52
-
53
- - 爬取起点中文网的小说章节内容 (支持免费与已订阅章节)
54
- - 断点续爬
55
- - 自动整合所有章节并导出为
56
- - TXT
57
- - EPUB
58
- - 支持活动广告过滤:
59
- - [x] 章节标题
60
- - [ ] 章节正文
61
- - [ ] 作者说
62
-
63
67
  ---
64
68
 
65
69
  ## 文档结构
@@ -71,6 +75,7 @@ pip install .
71
75
  - [settings.toml 配置说明](https://github.com/BowenZ217/novel-downloader/blob/main/docs/4-settings-schema.md)
72
76
  - [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-usage-examples.md)
73
77
  - [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-supported-sites.md)
78
+ - [复制 Cookies](https://github.com/BowenZ217/novel-downloader/blob/main/docs/copy-cookies.md)
74
79
  - [文件保存](https://github.com/BowenZ217/novel-downloader/blob/main/docs/file-saving.md)
75
80
  - [TODO](https://github.com/BowenZ217/novel-downloader/blob/main/docs/todo.md)
76
81
  - [开发](https://github.com/BowenZ217/novel-downloader/blob/main/docs/develop.md)
@@ -6,7 +6,7 @@ novel_downloader
6
6
  Core package for the Novel Downloader project.
7
7
  """
8
8
 
9
- __version__ = "1.3.1"
9
+ __version__ = "1.3.2"
10
10
 
11
11
  __author__ = "Saudade Z"
12
12
  __email__ = "saudadez217@gmail.com"
@@ -119,7 +119,7 @@ def download_cli(ctx: Context, book_ids: list[str], site: str) -> None:
119
119
  config=downloader_cfg,
120
120
  )
121
121
 
122
- for book_id in book_ids:
122
+ for book_id in valid_book_ids:
123
123
  click.echo(t("download_downloading", book_id=book_id, site=site))
124
124
  sync_downloader.download_one(book_id)
125
125
 
@@ -90,6 +90,8 @@ class ConfigAdapter:
90
90
  disable_images=req.get("disable_images", True),
91
91
  mute_audio=req.get("mute_audio", True),
92
92
  mode=site_cfg.get("mode", "session"),
93
+ username=site_cfg.get("username", ""),
94
+ password=site_cfg.get("password", ""),
93
95
  )
94
96
 
95
97
  def get_downloader_config(self) -> DownloaderConfig:
@@ -150,6 +152,7 @@ class ConfigAdapter:
150
152
  naming = out.get("naming", {})
151
153
  epub_opts = out.get("epub", {})
152
154
  return SaverConfig(
155
+ cache_dir=gen.get("cache_dir", "./novel_cache"),
153
156
  raw_data_dir=gen.get("raw_data_dir", "./raw_data"),
154
157
  output_dir=gen.get("output_dir", "./downloads"),
155
158
  storage_backend=gen.get("storage_backend", "json"),
@@ -38,6 +38,8 @@ class RequesterConfig:
38
38
  mode: ModeType = "session"
39
39
  max_connections: int = 10
40
40
  max_rps: float | None = None # Maximum requests per second
41
+ username: str = ""
42
+ password: str = ""
41
43
 
42
44
 
43
45
  # === Downloaders ===
@@ -78,6 +80,7 @@ class ParserConfig:
78
80
  # === Savers ===
79
81
  @dataclass
80
82
  class SaverConfig:
83
+ cache_dir: str = "./novel_cache"
81
84
  raw_data_dir: str = "./raw_data"
82
85
  output_dir: str = "./downloads"
83
86
  storage_backend: StorageBackend = "json"
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ novel_downloader.core.downloaders
4
+ ---------------------------------
5
+
6
+ This subpackage contains concrete downloader implementations for
7
+ specific novel platforms.
8
+
9
+ Each downloader is responsible for orchestrating the full lifecycle
10
+ of retrieving, parsing, and saving novel content for a given source.
11
+
12
+ Currently supported platforms:
13
+ - biquge (笔趣阁)
14
+ - esjzone (ESJ Zone)
15
+ - qianbi (铅笔小说)
16
+ - qidian (起点中文网)
17
+ - sfacg (SF轻小说)
18
+ - yamibo (百合会)
19
+ - common (通用架构)
20
+ """
21
+
22
+ from .biquge import BiqugeAsyncDownloader, BiqugeDownloader
23
+ from .common import CommonAsyncDownloader, CommonDownloader
24
+ from .esjzone import EsjzoneAsyncDownloader, EsjzoneDownloader
25
+ from .qianbi import QianbiAsyncDownloader, QianbiDownloader
26
+ from .qidian import QidianDownloader
27
+ from .sfacg import SfacgAsyncDownloader, SfacgDownloader
28
+ from .yamibo import YamiboAsyncDownloader, YamiboDownloader
29
+
30
+ __all__ = [
31
+ "BiqugeAsyncDownloader",
32
+ "BiqugeDownloader",
33
+ "CommonAsyncDownloader",
34
+ "CommonDownloader",
35
+ "EsjzoneAsyncDownloader",
36
+ "EsjzoneDownloader",
37
+ "QianbiAsyncDownloader",
38
+ "QianbiDownloader",
39
+ "QidianDownloader",
40
+ "SfacgAsyncDownloader",
41
+ "SfacgDownloader",
42
+ "YamiboAsyncDownloader",
43
+ "YamiboDownloader",
44
+ ]
@@ -5,8 +5,10 @@ novel_downloader.core.downloaders.biquge
5
5
 
6
6
  """
7
7
 
8
+ from .biquge_async import BiqugeAsyncDownloader
8
9
  from .biquge_sync import BiqugeDownloader
9
10
 
10
11
  __all__ = [
12
+ "BiqugeAsyncDownloader",
11
13
  "BiqugeDownloader",
12
14
  ]
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ novel_downloader.core.downloaders.biquge.biquge_async
4
+ -----------------------------------------------------
5
+
6
+ """
7
+
8
+ from novel_downloader.config.models import DownloaderConfig
9
+ from novel_downloader.core.downloaders.common import CommonAsyncDownloader
10
+ from novel_downloader.core.interfaces import (
11
+ AsyncRequesterProtocol,
12
+ ParserProtocol,
13
+ SaverProtocol,
14
+ )
15
+
16
+
17
+ class BiqugeAsyncDownloader(CommonAsyncDownloader):
18
+ """"""
19
+
20
+ def __init__(
21
+ self,
22
+ requester: AsyncRequesterProtocol,
23
+ parser: ParserProtocol,
24
+ saver: SaverProtocol,
25
+ config: DownloaderConfig,
26
+ ):
27
+ super().__init__(requester, parser, saver, config, "biquge")
@@ -7,9 +7,11 @@ novel_downloader.core.downloaders.biquge.biquge_sync
7
7
 
8
8
  from novel_downloader.config.models import DownloaderConfig
9
9
  from novel_downloader.core.downloaders.common import CommonDownloader
10
- from novel_downloader.core.interfaces.parser import ParserProtocol
11
- from novel_downloader.core.interfaces.saver import SaverProtocol
12
- from novel_downloader.core.interfaces.sync_requester import SyncRequesterProtocol
10
+ from novel_downloader.core.interfaces import (
11
+ ParserProtocol,
12
+ SaverProtocol,
13
+ SyncRequesterProtocol,
14
+ )
13
15
 
14
16
 
15
17
  class BiqugeDownloader(CommonDownloader):
@@ -48,7 +48,7 @@ class CommonAsyncDownloader(BaseAsyncDownloader):
48
48
  Perform login
49
49
  """
50
50
  if self.login_required and not self._is_logged_in:
51
- success = await self.requester.login(max_retries=3)
51
+ success = await self.requester.login()
52
52
  if not success:
53
53
  raise RuntimeError("Login failed")
54
54
  self._is_logged_in = True
@@ -60,6 +60,7 @@ class CommonAsyncDownloader(BaseAsyncDownloader):
60
60
  :param book_id: The identifier of the book to download.
61
61
  """
62
62
  assert isinstance(self.requester, AsyncRequesterProtocol)
63
+ await self.prepare()
63
64
 
64
65
  TAG = "[AsyncDownloader]"
65
66
  wait_time = self.config.request_interval
@@ -95,7 +96,8 @@ class CommonAsyncDownloader(BaseAsyncDownloader):
95
96
  if re_fetch:
96
97
  info_html = await self.requester.get_book_info(book_id)
97
98
  if self.save_html:
98
- save_as_txt(info_html, chapters_html_dir / "info.html")
99
+ for i, html in enumerate(info_html):
100
+ save_as_txt(html, chapters_html_dir / f"info_{i}.html")
99
101
  book_info = self.parser.parse_book_info(info_html)
100
102
  if book_info.get("book_name") != "未找到书名":
101
103
  save_as_json(book_info, info_path)
@@ -114,7 +116,7 @@ class CommonAsyncDownloader(BaseAsyncDownloader):
114
116
 
115
117
  # setup queue, semaphore, executor
116
118
  semaphore = asyncio.Semaphore(self.download_workers)
117
- queue: asyncio.Queue[tuple[str, str]] = asyncio.Queue()
119
+ queue: asyncio.Queue[tuple[str, list[str]]] = asyncio.Queue()
118
120
  save_queue: asyncio.Queue[ChapterDict] = asyncio.Queue()
119
121
  loop = asyncio.get_running_loop()
120
122
  executor = (
@@ -52,6 +52,17 @@ class CommonDownloader(BaseDownloader):
52
52
  """
53
53
  super().__init__(requester, parser, saver, config, site)
54
54
  self._site = site
55
+ self._is_logged_in = False
56
+
57
+ def prepare(self) -> None:
58
+ """
59
+ Perform login
60
+ """
61
+ if self.login_required and not self._is_logged_in:
62
+ success = self.requester.login()
63
+ if not success:
64
+ raise RuntimeError("Login failed")
65
+ self._is_logged_in = True
55
66
 
56
67
  def download_one(self, book_id: str) -> None:
57
68
  """
@@ -59,6 +70,8 @@ class CommonDownloader(BaseDownloader):
59
70
 
60
71
  :param book_id: The identifier of the book to download.
61
72
  """
73
+ self.prepare()
74
+
62
75
  TAG = "[Downloader]"
63
76
  save_html = self.config.save_html
64
77
  skip_existing = self.config.skip_existing
@@ -96,8 +109,8 @@ class CommonDownloader(BaseDownloader):
96
109
  except Exception:
97
110
  info_html = self.requester.get_book_info(book_id)
98
111
  if save_html:
99
- info_html_path = chapters_html_dir / "info.html"
100
- save_as_txt(info_html, info_html_path)
112
+ for i, html in enumerate(info_html):
113
+ save_as_txt(html, chapters_html_dir / f"info_{i}.html")
101
114
  book_info = self.parser.parse_book_info(info_html)
102
115
  if (
103
116
  book_info.get("book_name", "") != "未找到书名"
@@ -138,14 +151,9 @@ class CommonDownloader(BaseDownloader):
138
151
  chap_html = self.requester.get_book_chapter(book_id, cid)
139
152
 
140
153
  if save_html:
141
- html_path = chapters_html_dir / f"{cid}.html"
142
- save_as_txt(chap_html, html_path, on_exist="skip")
143
- logger.debug(
144
- "%s Saved raw HTML for chapter %s to %s",
145
- TAG,
146
- cid,
147
- html_path,
148
- )
154
+ for i, html in enumerate(chap_html):
155
+ html_path = chapters_html_dir / f"{cid}_{i}.html"
156
+ save_as_txt(html, html_path, on_exist="skip")
149
157
 
150
158
  chap_json = self.parser.parse_chapter(chap_html, cid)
151
159
 
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ novel_downloader.core.downloaders.esjzone
4
+ -----------------------------------------
5
+
6
+ """
7
+
8
+ from .esjzone_async import EsjzoneAsyncDownloader
9
+ from .esjzone_sync import EsjzoneDownloader
10
+
11
+ __all__ = [
12
+ "EsjzoneAsyncDownloader",
13
+ "EsjzoneDownloader",
14
+ ]
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ novel_downloader.core.downloaders.esjzone.esjzone_async
4
+ -------------------------------------------------------
5
+
6
+ """
7
+
8
+ from novel_downloader.config.models import DownloaderConfig
9
+ from novel_downloader.core.downloaders.common import CommonAsyncDownloader
10
+ from novel_downloader.core.interfaces import (
11
+ AsyncRequesterProtocol,
12
+ ParserProtocol,
13
+ SaverProtocol,
14
+ )
15
+
16
+
17
+ class EsjzoneAsyncDownloader(CommonAsyncDownloader):
18
+ """"""
19
+
20
+ def __init__(
21
+ self,
22
+ requester: AsyncRequesterProtocol,
23
+ parser: ParserProtocol,
24
+ saver: SaverProtocol,
25
+ config: DownloaderConfig,
26
+ ):
27
+ super().__init__(requester, parser, saver, config, "esjzone")
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ novel_downloader.core.downloaders.esjzone.esjzone_sync
4
+ ------------------------------------------------------
5
+
6
+ """
7
+
8
+ from novel_downloader.config.models import DownloaderConfig
9
+ from novel_downloader.core.downloaders.common import CommonDownloader
10
+ from novel_downloader.core.interfaces import (
11
+ ParserProtocol,
12
+ SaverProtocol,
13
+ SyncRequesterProtocol,
14
+ )
15
+
16
+
17
+ class EsjzoneDownloader(CommonDownloader):
18
+ """"""
19
+
20
+ def __init__(
21
+ self,
22
+ requester: SyncRequesterProtocol,
23
+ parser: ParserProtocol,
24
+ saver: SaverProtocol,
25
+ config: DownloaderConfig,
26
+ ):
27
+ super().__init__(requester, parser, saver, config, "esjzone")
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ novel_downloader.core.downloaders.qianbi
4
+ ----------------------------------------
5
+
6
+ """
7
+
8
+ from .qianbi_async import QianbiAsyncDownloader
9
+ from .qianbi_sync import QianbiDownloader
10
+
11
+ __all__ = [
12
+ "QianbiAsyncDownloader",
13
+ "QianbiDownloader",
14
+ ]
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ novel_downloader.core.downloaders.qianbi.qianbi_async
4
+ -----------------------------------------------------
5
+
6
+ """
7
+
8
+ from novel_downloader.config.models import DownloaderConfig
9
+ from novel_downloader.core.downloaders.common import CommonAsyncDownloader
10
+ from novel_downloader.core.interfaces import (
11
+ AsyncRequesterProtocol,
12
+ ParserProtocol,
13
+ SaverProtocol,
14
+ )
15
+
16
+
17
+ class QianbiAsyncDownloader(CommonAsyncDownloader):
18
+ """"""
19
+
20
+ def __init__(
21
+ self,
22
+ requester: AsyncRequesterProtocol,
23
+ parser: ParserProtocol,
24
+ saver: SaverProtocol,
25
+ config: DownloaderConfig,
26
+ ):
27
+ super().__init__(requester, parser, saver, config, "qianbi")
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ novel_downloader.core.downloaders.qianbi.qianbi_sync
4
+ ----------------------------------------------------
5
+
6
+ """
7
+
8
+ from novel_downloader.config.models import DownloaderConfig
9
+ from novel_downloader.core.downloaders.common import CommonDownloader
10
+ from novel_downloader.core.interfaces import (
11
+ ParserProtocol,
12
+ SaverProtocol,
13
+ SyncRequesterProtocol,
14
+ )
15
+
16
+
17
+ class QianbiDownloader(CommonDownloader):
18
+ """"""
19
+
20
+ def __init__(
21
+ self,
22
+ requester: SyncRequesterProtocol,
23
+ parser: ParserProtocol,
24
+ saver: SaverProtocol,
25
+ config: DownloaderConfig,
26
+ ):
27
+ super().__init__(requester, parser, saver, config, "qianbi")
@@ -100,9 +100,9 @@ class QidianDownloader(BaseDownloader):
100
100
  raise FileNotFoundError # trigger re-fetch
101
101
  except Exception:
102
102
  info_html = self.requester.get_book_info(book_id)
103
- if save_html:
103
+ if save_html and info_html:
104
104
  info_html_path = chapters_html_dir / "info.html"
105
- save_as_txt(info_html, info_html_path)
105
+ save_as_txt(info_html[0], info_html_path)
106
106
  book_info = self.parser.parse_book_info(info_html)
107
107
  if (
108
108
  book_info.get("book_name", "") != "未找到书名"
@@ -140,6 +140,9 @@ class QidianDownloader(BaseDownloader):
140
140
  chap_title = chap.get("title", "")
141
141
  self.logger.info("%s Fetching chapter: %s (%s)", TAG, chap_title, cid)
142
142
  chap_html = self.requester.get_book_chapter(book_id, cid)
143
+ if not chap_html:
144
+ continue
145
+
143
146
  if scroll:
144
147
  self.requester.scroll_page(wait_time * 2) # type: ignore[attr-defined]
145
148
  else:
@@ -147,7 +150,7 @@ class QidianDownloader(BaseDownloader):
147
150
  wait_time, mul_spread=1.1, max_sleep=wait_time + 2
148
151
  )
149
152
 
150
- is_encrypted = self.parser.is_encrypted(chap_html) # type: ignore[attr-defined]
153
+ is_encrypted = self.parser.is_encrypted(chap_html[0]) # type: ignore[attr-defined]
151
154
 
152
155
  if is_encrypted and encrypted_cs.exists(cid) and skip_existing:
153
156
  self.logger.debug(
@@ -157,18 +160,18 @@ class QidianDownloader(BaseDownloader):
157
160
  )
158
161
  continue
159
162
 
160
- if save_html and not is_vip(chap_html):
163
+ if save_html and chap_html and not is_vip(chap_html[0]):
161
164
  folder = chapters_html_dir / (
162
165
  "html_encrypted" if is_encrypted else "html_plain"
163
166
  )
164
167
  html_path = folder / f"{cid}.html"
165
- save_as_txt(chap_html, html_path, on_exist="skip")
168
+ save_as_txt(chap_html[0], html_path, on_exist="skip")
166
169
  self.logger.debug(
167
170
  "%s Saved raw HTML for chapter %s to %s", TAG, cid, html_path
168
171
  )
169
172
 
170
173
  chap_json = self.parser.parse_chapter(chap_html, cid)
171
- if not chap_json:
174
+ if not chap_json or not chap_json.get("content"):
172
175
  self.logger.warning(
173
176
  "%s Parsed chapter json is empty, skipping: %s (%s)",
174
177
  TAG,
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ novel_downloader.core.downloaders.sfacg
4
+ ---------------------------------------
5
+
6
+ """
7
+
8
+ from .sfacg_async import SfacgAsyncDownloader
9
+ from .sfacg_sync import SfacgDownloader
10
+
11
+ __all__ = [
12
+ "SfacgAsyncDownloader",
13
+ "SfacgDownloader",
14
+ ]
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ novel_downloader.core.downloaders.sfacg.sfacg_async
4
+ ---------------------------------------------------
5
+
6
+ """
7
+
8
+ from novel_downloader.config.models import DownloaderConfig
9
+ from novel_downloader.core.downloaders.common import CommonAsyncDownloader
10
+ from novel_downloader.core.interfaces import (
11
+ AsyncRequesterProtocol,
12
+ ParserProtocol,
13
+ SaverProtocol,
14
+ )
15
+
16
+
17
+ class SfacgAsyncDownloader(CommonAsyncDownloader):
18
+ """"""
19
+
20
+ def __init__(
21
+ self,
22
+ requester: AsyncRequesterProtocol,
23
+ parser: ParserProtocol,
24
+ saver: SaverProtocol,
25
+ config: DownloaderConfig,
26
+ ):
27
+ super().__init__(requester, parser, saver, config, "sfacg")
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ novel_downloader.core.downloaders.sfacg.sfacg_sync
4
+ --------------------------------------------------
5
+
6
+ """
7
+
8
+ from novel_downloader.config.models import DownloaderConfig
9
+ from novel_downloader.core.downloaders.common import CommonDownloader
10
+ from novel_downloader.core.interfaces import (
11
+ ParserProtocol,
12
+ SaverProtocol,
13
+ SyncRequesterProtocol,
14
+ )
15
+
16
+
17
+ class SfacgDownloader(CommonDownloader):
18
+ """"""
19
+
20
+ def __init__(
21
+ self,
22
+ requester: SyncRequesterProtocol,
23
+ parser: ParserProtocol,
24
+ saver: SaverProtocol,
25
+ config: DownloaderConfig,
26
+ ):
27
+ super().__init__(requester, parser, saver, config, "sfacg")