novel-downloader 1.3.2__tar.gz → 1.4.0__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 (252) hide show
  1. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/PKG-INFO +72 -38
  2. novel_downloader-1.4.0/README.md +122 -0
  3. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/__init__.py +1 -1
  4. novel_downloader-1.4.0/novel_downloader/cli/clean.py +145 -0
  5. novel_downloader-1.4.0/novel_downloader/cli/config.py +177 -0
  6. novel_downloader-1.4.0/novel_downloader/cli/download.py +173 -0
  7. novel_downloader-1.4.0/novel_downloader/cli/export.py +77 -0
  8. novel_downloader-1.4.0/novel_downloader/cli/main.py +36 -0
  9. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/config/__init__.py +1 -25
  10. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/config/adapter.py +32 -31
  11. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/config/loader.py +3 -3
  12. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/config/site_rules.py +1 -2
  13. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/core/__init__.py +3 -6
  14. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/core/downloaders/__init__.py +10 -13
  15. novel_downloader-1.4.0/novel_downloader/core/downloaders/base.py +233 -0
  16. novel_downloader-1.4.0/novel_downloader/core/downloaders/biquge.py +27 -0
  17. novel_downloader-1.4.0/novel_downloader/core/downloaders/common.py +414 -0
  18. novel_downloader-1.4.0/novel_downloader/core/downloaders/esjzone.py +27 -0
  19. novel_downloader-1.4.0/novel_downloader/core/downloaders/linovelib.py +27 -0
  20. novel_downloader-1.4.0/novel_downloader/core/downloaders/qianbi.py +27 -0
  21. novel_downloader-1.4.0/novel_downloader/core/downloaders/qidian.py +352 -0
  22. novel_downloader-1.4.0/novel_downloader/core/downloaders/sfacg.py +27 -0
  23. novel_downloader-1.4.0/novel_downloader/core/downloaders/yamibo.py +27 -0
  24. novel_downloader-1.4.0/novel_downloader/core/exporters/__init__.py +37 -0
  25. {novel_downloader-1.3.2/novel_downloader/core/savers → novel_downloader-1.4.0/novel_downloader/core/exporters}/base.py +73 -44
  26. novel_downloader-1.4.0/novel_downloader/core/exporters/biquge.py +25 -0
  27. novel_downloader-1.4.0/novel_downloader/core/exporters/common/__init__.py +12 -0
  28. {novel_downloader-1.3.2/novel_downloader/core/savers → novel_downloader-1.4.0/novel_downloader/core/exporters}/common/epub.py +40 -52
  29. novel_downloader-1.3.2/novel_downloader/core/savers/common/main_saver.py → novel_downloader-1.4.0/novel_downloader/core/exporters/common/main_exporter.py +36 -39
  30. {novel_downloader-1.3.2/novel_downloader/core/savers → novel_downloader-1.4.0/novel_downloader/core/exporters}/common/txt.py +20 -24
  31. novel_downloader-1.4.0/novel_downloader/core/exporters/epub_utils/__init__.py +40 -0
  32. {novel_downloader-1.3.2/novel_downloader/core/savers → novel_downloader-1.4.0/novel_downloader/core/exporters}/epub_utils/css_builder.py +2 -1
  33. novel_downloader-1.4.0/novel_downloader/core/exporters/epub_utils/image_loader.py +131 -0
  34. {novel_downloader-1.3.2/novel_downloader/core/savers → novel_downloader-1.4.0/novel_downloader/core/exporters}/epub_utils/initializer.py +6 -3
  35. {novel_downloader-1.3.2/novel_downloader/core/savers → novel_downloader-1.4.0/novel_downloader/core/exporters}/epub_utils/text_to_html.py +49 -2
  36. {novel_downloader-1.3.2/novel_downloader/core/savers → novel_downloader-1.4.0/novel_downloader/core/exporters}/epub_utils/volume_intro.py +2 -1
  37. novel_downloader-1.4.0/novel_downloader/core/exporters/esjzone.py +25 -0
  38. novel_downloader-1.4.0/novel_downloader/core/exporters/linovelib/__init__.py +10 -0
  39. novel_downloader-1.4.0/novel_downloader/core/exporters/linovelib/epub.py +449 -0
  40. novel_downloader-1.4.0/novel_downloader/core/exporters/linovelib/main_exporter.py +127 -0
  41. novel_downloader-1.4.0/novel_downloader/core/exporters/linovelib/txt.py +129 -0
  42. novel_downloader-1.4.0/novel_downloader/core/exporters/qianbi.py +25 -0
  43. {novel_downloader-1.3.2/novel_downloader/core/savers → novel_downloader-1.4.0/novel_downloader/core/exporters}/qidian.py +8 -8
  44. novel_downloader-1.4.0/novel_downloader/core/exporters/sfacg.py +25 -0
  45. novel_downloader-1.4.0/novel_downloader/core/exporters/yamibo.py +25 -0
  46. novel_downloader-1.4.0/novel_downloader/core/factory/__init__.py +20 -0
  47. novel_downloader-1.4.0/novel_downloader/core/factory/downloader.py +76 -0
  48. novel_downloader-1.4.0/novel_downloader/core/factory/exporter.py +58 -0
  49. novel_downloader-1.4.0/novel_downloader/core/factory/fetcher.py +96 -0
  50. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/core/factory/parser.py +17 -12
  51. {novel_downloader-1.3.2/novel_downloader/core/requesters → novel_downloader-1.4.0/novel_downloader/core/fetchers}/__init__.py +22 -15
  52. {novel_downloader-1.3.2/novel_downloader/core/requesters → novel_downloader-1.4.0/novel_downloader/core/fetchers}/base/__init__.py +2 -4
  53. novel_downloader-1.4.0/novel_downloader/core/fetchers/base/browser.py +383 -0
  54. novel_downloader-1.4.0/novel_downloader/core/fetchers/base/rate_limiter.py +86 -0
  55. novel_downloader-1.4.0/novel_downloader/core/fetchers/base/session.py +419 -0
  56. novel_downloader-1.4.0/novel_downloader/core/fetchers/biquge/__init__.py +14 -0
  57. novel_downloader-1.3.2/novel_downloader/core/requesters/biquge/async_session.py → novel_downloader-1.4.0/novel_downloader/core/fetchers/biquge/browser.py +18 -6
  58. {novel_downloader-1.3.2/novel_downloader/core/requesters → novel_downloader-1.4.0/novel_downloader/core/fetchers}/biquge/session.py +23 -30
  59. novel_downloader-1.4.0/novel_downloader/core/fetchers/common/__init__.py +14 -0
  60. novel_downloader-1.4.0/novel_downloader/core/fetchers/common/browser.py +79 -0
  61. novel_downloader-1.3.2/novel_downloader/core/requesters/common/async_session.py → novel_downloader-1.4.0/novel_downloader/core/fetchers/common/session.py +8 -25
  62. novel_downloader-1.4.0/novel_downloader/core/fetchers/esjzone/__init__.py +14 -0
  63. novel_downloader-1.4.0/novel_downloader/core/fetchers/esjzone/browser.py +202 -0
  64. novel_downloader-1.3.2/novel_downloader/core/requesters/esjzone/async_session.py → novel_downloader-1.4.0/novel_downloader/core/fetchers/esjzone/session.py +62 -42
  65. novel_downloader-1.4.0/novel_downloader/core/fetchers/linovelib/__init__.py +14 -0
  66. novel_downloader-1.4.0/novel_downloader/core/fetchers/linovelib/browser.py +178 -0
  67. novel_downloader-1.4.0/novel_downloader/core/fetchers/linovelib/session.py +178 -0
  68. novel_downloader-1.4.0/novel_downloader/core/fetchers/qianbi/__init__.py +14 -0
  69. novel_downloader-1.3.2/novel_downloader/core/requesters/qianbi/session.py → novel_downloader-1.4.0/novel_downloader/core/fetchers/qianbi/browser.py +30 -48
  70. novel_downloader-1.3.2/novel_downloader/core/requesters/qianbi/async_session.py → novel_downloader-1.4.0/novel_downloader/core/fetchers/qianbi/session.py +18 -6
  71. novel_downloader-1.4.0/novel_downloader/core/fetchers/qidian/__init__.py +14 -0
  72. novel_downloader-1.4.0/novel_downloader/core/fetchers/qidian/browser.py +266 -0
  73. novel_downloader-1.4.0/novel_downloader/core/fetchers/qidian/session.py +326 -0
  74. novel_downloader-1.4.0/novel_downloader/core/fetchers/sfacg/__init__.py +14 -0
  75. novel_downloader-1.4.0/novel_downloader/core/fetchers/sfacg/browser.py +189 -0
  76. novel_downloader-1.3.2/novel_downloader/core/requesters/sfacg/async_session.py → novel_downloader-1.4.0/novel_downloader/core/fetchers/sfacg/session.py +43 -73
  77. novel_downloader-1.4.0/novel_downloader/core/fetchers/yamibo/__init__.py +14 -0
  78. novel_downloader-1.4.0/novel_downloader/core/fetchers/yamibo/browser.py +229 -0
  79. novel_downloader-1.3.2/novel_downloader/core/requesters/yamibo/async_session.py → novel_downloader-1.4.0/novel_downloader/core/fetchers/yamibo/session.py +62 -44
  80. novel_downloader-1.4.0/novel_downloader/core/interfaces/__init__.py +27 -0
  81. novel_downloader-1.4.0/novel_downloader/core/interfaces/downloader.py +54 -0
  82. novel_downloader-1.3.2/novel_downloader/core/interfaces/saver.py → novel_downloader-1.4.0/novel_downloader/core/interfaces/exporter.py +12 -12
  83. novel_downloader-1.4.0/novel_downloader/core/interfaces/fetcher.py +162 -0
  84. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/core/interfaces/parser.py +6 -7
  85. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/core/parsers/__init__.py +5 -6
  86. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/core/parsers/base.py +9 -13
  87. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/core/parsers/biquge/main_parser.py +12 -13
  88. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/core/parsers/common/helper.py +3 -3
  89. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/core/parsers/common/main_parser.py +39 -34
  90. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/core/parsers/esjzone/main_parser.py +24 -17
  91. novel_downloader-1.4.0/novel_downloader/core/parsers/linovelib/__init__.py +10 -0
  92. novel_downloader-1.4.0/novel_downloader/core/parsers/linovelib/main_parser.py +210 -0
  93. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/core/parsers/qianbi/main_parser.py +21 -15
  94. novel_downloader-1.4.0/novel_downloader/core/parsers/qidian/__init__.py +10 -0
  95. novel_downloader-1.4.0/novel_downloader/core/parsers/qidian/book_info_parser.py +113 -0
  96. {novel_downloader-1.3.2/novel_downloader/core/parsers/qidian/browser → novel_downloader-1.4.0/novel_downloader/core/parsers/qidian}/chapter_encrypted.py +162 -135
  97. novel_downloader-1.4.0/novel_downloader/core/parsers/qidian/chapter_normal.py +150 -0
  98. {novel_downloader-1.3.2/novel_downloader/core/parsers/qidian/session → novel_downloader-1.4.0/novel_downloader/core/parsers/qidian}/chapter_router.py +15 -15
  99. {novel_downloader-1.3.2/novel_downloader/core/parsers/qidian/session → novel_downloader-1.4.0/novel_downloader/core/parsers/qidian}/main_parser.py +48 -42
  100. novel_downloader-1.4.0/novel_downloader/core/parsers/qidian/utils/__init__.py +27 -0
  101. novel_downloader-1.4.0/novel_downloader/core/parsers/qidian/utils/decryptor_fetcher.py +145 -0
  102. {novel_downloader-1.3.2/novel_downloader/core/parsers/qidian/shared → novel_downloader-1.4.0/novel_downloader/core/parsers/qidian/utils}/helpers.py +41 -68
  103. {novel_downloader-1.3.2/novel_downloader/core/parsers/qidian/session → novel_downloader-1.4.0/novel_downloader/core/parsers/qidian/utils}/node_decryptor.py +64 -50
  104. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/core/parsers/sfacg/main_parser.py +12 -12
  105. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/core/parsers/yamibo/main_parser.py +10 -10
  106. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/locales/en.json +18 -2
  107. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/locales/zh.json +18 -2
  108. novel_downloader-1.4.0/novel_downloader/models/__init__.py +64 -0
  109. novel_downloader-1.4.0/novel_downloader/models/browser.py +21 -0
  110. novel_downloader-1.4.0/novel_downloader/models/chapter.py +25 -0
  111. novel_downloader-1.4.0/novel_downloader/models/config.py +100 -0
  112. novel_downloader-1.4.0/novel_downloader/models/login.py +20 -0
  113. novel_downloader-1.4.0/novel_downloader/models/site_rules.py +99 -0
  114. novel_downloader-1.4.0/novel_downloader/models/tasks.py +33 -0
  115. novel_downloader-1.4.0/novel_downloader/models/types.py +15 -0
  116. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/resources/config/settings.toml +31 -25
  117. novel_downloader-1.4.0/novel_downloader/resources/json/linovelib_font_map.json +3573 -0
  118. novel_downloader-1.4.0/novel_downloader/tui/__init__.py +7 -0
  119. novel_downloader-1.4.0/novel_downloader/tui/app.py +32 -0
  120. novel_downloader-1.4.0/novel_downloader/tui/main.py +17 -0
  121. novel_downloader-1.4.0/novel_downloader/tui/screens/__init__.py +14 -0
  122. novel_downloader-1.4.0/novel_downloader/tui/screens/home.py +191 -0
  123. novel_downloader-1.4.0/novel_downloader/tui/screens/login.py +74 -0
  124. novel_downloader-1.4.0/novel_downloader/tui/styles/home_layout.tcss +79 -0
  125. novel_downloader-1.4.0/novel_downloader/tui/widgets/richlog_handler.py +24 -0
  126. novel_downloader-1.4.0/novel_downloader/utils/__init__.py +6 -0
  127. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/chapter_storage.py +25 -38
  128. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/constants.py +15 -5
  129. novel_downloader-1.4.0/novel_downloader/utils/cookies.py +66 -0
  130. novel_downloader-1.4.0/novel_downloader/utils/crypto_utils.py +71 -0
  131. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/file_utils/io.py +1 -1
  132. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/fontocr/ocr_v1.py +2 -1
  133. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/fontocr/ocr_v2.py +2 -2
  134. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/hash_store.py +10 -18
  135. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/hash_utils.py +3 -2
  136. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/logger.py +2 -3
  137. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/network.py +53 -39
  138. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/text_utils/chapter_formatting.py +6 -1
  139. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/text_utils/font_mapping.py +1 -1
  140. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/text_utils/text_cleaning.py +1 -1
  141. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/time_utils/datetime_utils.py +3 -3
  142. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/time_utils/sleep_utils.py +3 -3
  143. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader.egg-info/PKG-INFO +72 -38
  144. novel_downloader-1.4.0/novel_downloader.egg-info/SOURCES.txt +173 -0
  145. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader.egg-info/entry_points.txt +1 -0
  146. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader.egg-info/requires.txt +5 -4
  147. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/pyproject.toml +11 -7
  148. novel_downloader-1.3.2/README.md +0 -90
  149. novel_downloader-1.3.2/novel_downloader/cli/clean.py +0 -126
  150. novel_downloader-1.3.2/novel_downloader/cli/download.py +0 -128
  151. novel_downloader-1.3.2/novel_downloader/cli/interactive.py +0 -66
  152. novel_downloader-1.3.2/novel_downloader/cli/main.py +0 -43
  153. novel_downloader-1.3.2/novel_downloader/cli/settings.py +0 -177
  154. novel_downloader-1.3.2/novel_downloader/config/models.py +0 -187
  155. novel_downloader-1.3.2/novel_downloader/core/downloaders/base/__init__.py +0 -14
  156. novel_downloader-1.3.2/novel_downloader/core/downloaders/base/base_async.py +0 -153
  157. novel_downloader-1.3.2/novel_downloader/core/downloaders/base/base_sync.py +0 -208
  158. novel_downloader-1.3.2/novel_downloader/core/downloaders/biquge/__init__.py +0 -14
  159. novel_downloader-1.3.2/novel_downloader/core/downloaders/biquge/biquge_async.py +0 -27
  160. novel_downloader-1.3.2/novel_downloader/core/downloaders/biquge/biquge_sync.py +0 -27
  161. novel_downloader-1.3.2/novel_downloader/core/downloaders/common/__init__.py +0 -14
  162. novel_downloader-1.3.2/novel_downloader/core/downloaders/common/common_async.py +0 -218
  163. novel_downloader-1.3.2/novel_downloader/core/downloaders/common/common_sync.py +0 -210
  164. novel_downloader-1.3.2/novel_downloader/core/downloaders/esjzone/__init__.py +0 -14
  165. novel_downloader-1.3.2/novel_downloader/core/downloaders/esjzone/esjzone_async.py +0 -27
  166. novel_downloader-1.3.2/novel_downloader/core/downloaders/esjzone/esjzone_sync.py +0 -27
  167. novel_downloader-1.3.2/novel_downloader/core/downloaders/qianbi/__init__.py +0 -14
  168. novel_downloader-1.3.2/novel_downloader/core/downloaders/qianbi/qianbi_async.py +0 -27
  169. novel_downloader-1.3.2/novel_downloader/core/downloaders/qianbi/qianbi_sync.py +0 -27
  170. novel_downloader-1.3.2/novel_downloader/core/downloaders/qidian/__init__.py +0 -10
  171. novel_downloader-1.3.2/novel_downloader/core/downloaders/qidian/qidian_sync.py +0 -227
  172. novel_downloader-1.3.2/novel_downloader/core/downloaders/sfacg/__init__.py +0 -14
  173. novel_downloader-1.3.2/novel_downloader/core/downloaders/sfacg/sfacg_async.py +0 -27
  174. novel_downloader-1.3.2/novel_downloader/core/downloaders/sfacg/sfacg_sync.py +0 -27
  175. novel_downloader-1.3.2/novel_downloader/core/downloaders/yamibo/__init__.py +0 -14
  176. novel_downloader-1.3.2/novel_downloader/core/downloaders/yamibo/yamibo_async.py +0 -27
  177. novel_downloader-1.3.2/novel_downloader/core/downloaders/yamibo/yamibo_sync.py +0 -27
  178. novel_downloader-1.3.2/novel_downloader/core/factory/__init__.py +0 -32
  179. novel_downloader-1.3.2/novel_downloader/core/factory/downloader.py +0 -178
  180. novel_downloader-1.3.2/novel_downloader/core/factory/requester.py +0 -144
  181. novel_downloader-1.3.2/novel_downloader/core/factory/saver.py +0 -56
  182. novel_downloader-1.3.2/novel_downloader/core/interfaces/__init__.py +0 -31
  183. novel_downloader-1.3.2/novel_downloader/core/interfaces/async_downloader.py +0 -36
  184. novel_downloader-1.3.2/novel_downloader/core/interfaces/async_requester.py +0 -84
  185. novel_downloader-1.3.2/novel_downloader/core/interfaces/sync_downloader.py +0 -36
  186. novel_downloader-1.3.2/novel_downloader/core/interfaces/sync_requester.py +0 -82
  187. novel_downloader-1.3.2/novel_downloader/core/parsers/qidian/__init__.py +0 -19
  188. novel_downloader-1.3.2/novel_downloader/core/parsers/qidian/browser/__init__.py +0 -12
  189. novel_downloader-1.3.2/novel_downloader/core/parsers/qidian/browser/chapter_normal.py +0 -93
  190. novel_downloader-1.3.2/novel_downloader/core/parsers/qidian/browser/chapter_router.py +0 -71
  191. novel_downloader-1.3.2/novel_downloader/core/parsers/qidian/browser/main_parser.py +0 -125
  192. novel_downloader-1.3.2/novel_downloader/core/parsers/qidian/session/__init__.py +0 -12
  193. novel_downloader-1.3.2/novel_downloader/core/parsers/qidian/session/chapter_encrypted.py +0 -443
  194. novel_downloader-1.3.2/novel_downloader/core/parsers/qidian/session/chapter_normal.py +0 -115
  195. novel_downloader-1.3.2/novel_downloader/core/parsers/qidian/shared/__init__.py +0 -37
  196. novel_downloader-1.3.2/novel_downloader/core/parsers/qidian/shared/book_info_parser.py +0 -150
  197. novel_downloader-1.3.2/novel_downloader/core/requesters/base/async_session.py +0 -410
  198. novel_downloader-1.3.2/novel_downloader/core/requesters/base/browser.py +0 -337
  199. novel_downloader-1.3.2/novel_downloader/core/requesters/base/session.py +0 -378
  200. novel_downloader-1.3.2/novel_downloader/core/requesters/biquge/__init__.py +0 -14
  201. novel_downloader-1.3.2/novel_downloader/core/requesters/common/__init__.py +0 -17
  202. novel_downloader-1.3.2/novel_downloader/core/requesters/common/session.py +0 -113
  203. novel_downloader-1.3.2/novel_downloader/core/requesters/esjzone/__init__.py +0 -13
  204. novel_downloader-1.3.2/novel_downloader/core/requesters/esjzone/session.py +0 -235
  205. novel_downloader-1.3.2/novel_downloader/core/requesters/qianbi/__init__.py +0 -13
  206. novel_downloader-1.3.2/novel_downloader/core/requesters/qidian/__init__.py +0 -21
  207. novel_downloader-1.3.2/novel_downloader/core/requesters/qidian/broswer.py +0 -307
  208. novel_downloader-1.3.2/novel_downloader/core/requesters/qidian/session.py +0 -290
  209. novel_downloader-1.3.2/novel_downloader/core/requesters/sfacg/__init__.py +0 -13
  210. novel_downloader-1.3.2/novel_downloader/core/requesters/sfacg/session.py +0 -242
  211. novel_downloader-1.3.2/novel_downloader/core/requesters/yamibo/__init__.py +0 -13
  212. novel_downloader-1.3.2/novel_downloader/core/requesters/yamibo/session.py +0 -237
  213. novel_downloader-1.3.2/novel_downloader/core/savers/__init__.py +0 -34
  214. novel_downloader-1.3.2/novel_downloader/core/savers/biquge.py +0 -25
  215. novel_downloader-1.3.2/novel_downloader/core/savers/common/__init__.py +0 -12
  216. novel_downloader-1.3.2/novel_downloader/core/savers/epub_utils/__init__.py +0 -26
  217. novel_downloader-1.3.2/novel_downloader/core/savers/esjzone.py +0 -25
  218. novel_downloader-1.3.2/novel_downloader/core/savers/qianbi.py +0 -25
  219. novel_downloader-1.3.2/novel_downloader/core/savers/sfacg.py +0 -25
  220. novel_downloader-1.3.2/novel_downloader/core/savers/yamibo.py +0 -25
  221. novel_downloader-1.3.2/novel_downloader/resources/config/rules.toml +0 -196
  222. novel_downloader-1.3.2/novel_downloader/utils/__init__.py +0 -0
  223. novel_downloader-1.3.2/novel_downloader/utils/crypto_utils.py +0 -144
  224. novel_downloader-1.3.2/novel_downloader.egg-info/SOURCES.txt +0 -168
  225. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/LICENSE +0 -0
  226. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/cli/__init__.py +0 -0
  227. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/core/parsers/biquge/__init__.py +0 -0
  228. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/core/parsers/common/__init__.py +0 -0
  229. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/core/parsers/esjzone/__init__.py +0 -0
  230. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/core/parsers/qianbi/__init__.py +0 -0
  231. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/core/parsers/sfacg/__init__.py +0 -0
  232. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/core/parsers/yamibo/__init__.py +0 -0
  233. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/resources/css_styles/main.css +0 -0
  234. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/resources/css_styles/volume-intro.css +0 -0
  235. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/resources/images/volume_border.png +0 -0
  236. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/resources/js_scripts/qidian_decrypt_node.js +0 -0
  237. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/resources/json/replace_word_map.json +0 -0
  238. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/resources/text/blacklist.txt +0 -0
  239. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/cache.py +0 -0
  240. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/file_utils/__init__.py +0 -0
  241. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/file_utils/normalize.py +0 -0
  242. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/file_utils/sanitize.py +0 -0
  243. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/fontocr/__init__.py +0 -0
  244. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/i18n.py +0 -0
  245. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/model_loader.py +0 -0
  246. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/state.py +0 -0
  247. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/text_utils/__init__.py +0 -0
  248. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/text_utils/diff_display.py +0 -0
  249. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader/utils/time_utils/__init__.py +0 -0
  250. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader.egg-info/dependency_links.txt +0 -0
  251. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/novel_downloader.egg-info/top_level.txt +0 -0
  252. {novel_downloader-1.3.2 → novel_downloader-1.4.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: novel-downloader
3
- Version: 1.3.2
3
+ Version: 1.4.0
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
@@ -34,19 +34,18 @@ Classifier: License :: OSI Approved :: MIT License
34
34
  Classifier: Natural Language :: Chinese (Simplified)
35
35
  Classifier: Topic :: Utilities
36
36
  Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3.11
37
38
  Classifier: Programming Language :: Python :: 3.12
38
39
  Classifier: Programming Language :: Python :: 3.13
39
- Requires-Python: >=3.12
40
+ Requires-Python: >=3.11
40
41
  Description-Content-Type: text/markdown
41
42
  License-File: LICENSE
43
+ Requires-Dist: textual
42
44
  Requires-Dist: requests
43
45
  Requires-Dist: aiohttp
44
- Requires-Dist: beautifulsoup4
45
- Requires-Dist: DrissionPage
46
- Requires-Dist: opencv-python
46
+ Requires-Dist: playwright
47
47
  Requires-Dist: lxml
48
48
  Requires-Dist: platformdirs
49
- Requires-Dist: click
50
49
  Requires-Dist: ebooklib
51
50
  Provides-Extra: dev
52
51
  Requires-Dist: black; extra == "dev"
@@ -55,6 +54,8 @@ Requires-Dist: ruff; extra == "dev"
55
54
  Requires-Dist: pytest; extra == "dev"
56
55
  Requires-Dist: pytest-cov; extra == "dev"
57
56
  Requires-Dist: pytest-mock; extra == "dev"
57
+ Requires-Dist: types-requests; extra == "dev"
58
+ Requires-Dist: types-PyYAML; extra == "dev"
58
59
  Requires-Dist: pre-commit; extra == "dev"
59
60
  Requires-Dist: commitizen; extra == "dev"
60
61
  Provides-Extra: font-recovery
@@ -63,63 +64,96 @@ Requires-Dist: numpy; extra == "font-recovery"
63
64
  Requires-Dist: tinycss2; extra == "font-recovery"
64
65
  Requires-Dist: fonttools; extra == "font-recovery"
65
66
  Requires-Dist: brotli; extra == "font-recovery"
67
+ Requires-Dist: opencv-python; extra == "font-recovery"
66
68
  Requires-Dist: pillow; extra == "font-recovery"
67
69
  Requires-Dist: huggingface_hub; extra == "font-recovery"
68
70
  Dynamic: license-file
69
71
 
70
72
  # novel-downloader
71
73
 
72
- 一个基于 [DrissionPage](https://www.drissionpage.cn) 和 [requests](https://github.com/psf/requests) 的小说下载器。
74
+ 一个基于 [playwright](https://playwright.dev/) 和 [aiohttp](https://github.com/aio-libs/aiohttp) 的小说下载工具/库。
73
75
 
74
- ---
75
-
76
- ## 项目简介
77
-
78
- **novel-downloader** 是一个通用的小说下载库 / CLI 工具,
79
- - 大多数支持的站点仅依赖 [`requests`](https://github.com/psf/requests) 进行 HTTP 抓取
80
- - 对于起点中文网 (Qidian), 可在配置中选择:
81
- - `mode: session` : 纯 Requests 模式
82
- - `mode: browser` : 基于 DrissionPage 驱动 Chrome 的浏览器模式 (可处理更复杂的 JS/加密)。
83
- - 若配置 `login_required: true`, 程序会在运行时自动检查登录状态, 支持自动重用历史 Cookie, 仅在首次登录或 Cookie 失效时需要人工介入:
84
- - 若使用 `browser` 模式, 请在程序打开的浏览器窗口登录, 登录后回车继续
85
- - 若使用 `session` 模式, 请根据程序提示粘贴浏览器中登录成功后的 Cookie ([复制 Cookies](https://github.com/BowenZ217/novel-downloader/blob/main/docs/copy-cookies.md))
76
+ > 本项目开发环境为 Python 3.12, 需确保运行环境为 Python 3.11 及以上版本
86
77
 
87
78
  ## 功能特性
88
79
 
89
- - 爬取起点中文网的小说章节内容 (支持免费与已订阅章节)
90
- - 爬取部分小说网站
91
- - 断点续爬
92
- - 自动整合所有章节并导出为
80
+ - 支持断点续爬, 自动续传未完成任务
81
+ - 自动整合所有章节并导出为:
93
82
  - TXT
94
- - EPUB
83
+ - EPUB (可选包含章节插图)
95
84
  - 支持活动广告过滤:
96
85
  - [x] 章节标题
97
86
  - [ ] 章节正文
98
- - [ ] 作者说
99
87
 
100
88
  ---
101
89
 
102
90
  ## 快速开始
103
91
 
92
+ ### 安装
93
+
94
+ 使用 `pip` 安装:
95
+
104
96
  ```bash
105
- # 克隆 + 安装
106
97
  pip install novel-downloader
98
+ ```
99
+
100
+ 如需使用浏览器模式 (即 `mode: browser`), 请确保已安装 Playwright 依赖:
107
101
 
108
- # 如需支持字体解密功能 (decode_font), 请使用:
109
- # pip install novel-downloader[font-recovery]
102
+ ```bash
103
+ playwright install
104
+ ```
110
105
 
106
+ 如需启用字体解密功能 (`decode_font`, 用于处理起点中文网对近一个月更新章节所采用的字体混淆技术), 请使用扩展安装方式:
107
+
108
+ ```bash
109
+ pip install novel-downloader[font-recovery]
110
+ ```
111
+
112
+ ---
113
+
114
+ ### CLI 模式
115
+
116
+ ```bash
111
117
  # 初始化默认配置 (生成 settings.toml)
112
- novel-cli settings init
118
+ novel-cli config init
113
119
 
114
120
  # 编辑 ./settings.toml 完成 site/book_ids 等
115
- # 可查看 docs/4-settings-schema.md
121
+ # 可查看 docs/3-settings-schema.md
116
122
 
117
- # 运行下载
123
+ # 执行下载任务
118
124
  novel-cli download 123456
119
125
  ```
120
126
 
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)
127
+ - 详细可见: [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/4-supported-sites.md)
128
+ - 更多使用方法, 查看 [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-cli-usage-examples.md)
129
+
130
+ ---
131
+
132
+ ### TUI 模式 (终端用户界面)
133
+
134
+ **注意**: TUI 模式仍在开发中, 目前尚未实现登录和修改设置等功能。建议优先使用稳定的 CLI 模式。
135
+
136
+ ```bash
137
+ # 初始化默认配置 (生成 settings.toml)
138
+ novel-cli config init
139
+
140
+ # 编辑 ./settings.toml 修改网络配置
141
+ # 可查看 docs/3-settings-schema.md
142
+
143
+ # 启动 TUI 界面
144
+ novel-tui
145
+ ```
146
+
147
+ - 详细可见: [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/4-supported-sites.md)
148
+ - 更多使用方法, 查看 [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-tui-usage-examples.md)
149
+
150
+ ---
151
+
152
+ ### GUI 模式 (图形界面)
153
+
154
+ 尚未实现
155
+
156
+ ---
123
157
 
124
158
  ## 从 GitHub 安装 (开发版)
125
159
 
@@ -139,11 +173,11 @@ pip install .
139
173
 
140
174
  - [项目简介](#项目简介)
141
175
  - [安装](https://github.com/BowenZ217/novel-downloader/blob/main/docs/1-installation.md)
142
- - [环境准备](https://github.com/BowenZ217/novel-downloader/blob/main/docs/2-environment-setup.md)
143
- - [配置](https://github.com/BowenZ217/novel-downloader/blob/main/docs/3-configuration.md)
144
- - [settings.toml 配置说明](https://github.com/BowenZ217/novel-downloader/blob/main/docs/4-settings-schema.md)
145
- - [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-usage-examples.md)
146
- - [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-supported-sites.md)
176
+ - [配置](https://github.com/BowenZ217/novel-downloader/blob/main/docs/2-configuration.md)
177
+ - [settings.toml 配置说明](https://github.com/BowenZ217/novel-downloader/blob/main/docs/3-settings-schema.md)
178
+ - [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/4-supported-sites.md)
179
+ - [TUI 使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-tui-usage-examples.md)
180
+ - [CLI 使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-cli-usage-examples.md)
147
181
  - [复制 Cookies](https://github.com/BowenZ217/novel-downloader/blob/main/docs/copy-cookies.md)
148
182
  - [文件保存](https://github.com/BowenZ217/novel-downloader/blob/main/docs/file-saving.md)
149
183
  - [TODO](https://github.com/BowenZ217/novel-downloader/blob/main/docs/todo.md)
@@ -0,0 +1,122 @@
1
+ # novel-downloader
2
+
3
+ 一个基于 [playwright](https://playwright.dev/) 和 [aiohttp](https://github.com/aio-libs/aiohttp) 的小说下载工具/库。
4
+
5
+ > 本项目开发环境为 Python 3.12, 需确保运行环境为 Python 3.11 及以上版本
6
+
7
+ ## 功能特性
8
+
9
+ - 支持断点续爬, 自动续传未完成任务
10
+ - 自动整合所有章节并导出为:
11
+ - TXT
12
+ - EPUB (可选包含章节插图)
13
+ - 支持活动广告过滤:
14
+ - [x] 章节标题
15
+ - [ ] 章节正文
16
+
17
+ ---
18
+
19
+ ## 快速开始
20
+
21
+ ### 安装
22
+
23
+ 使用 `pip` 安装:
24
+
25
+ ```bash
26
+ pip install novel-downloader
27
+ ```
28
+
29
+ 如需使用浏览器模式 (即 `mode: browser`), 请确保已安装 Playwright 依赖:
30
+
31
+ ```bash
32
+ playwright install
33
+ ```
34
+
35
+ 如需启用字体解密功能 (`decode_font`, 用于处理起点中文网对近一个月更新章节所采用的字体混淆技术), 请使用扩展安装方式:
36
+
37
+ ```bash
38
+ pip install novel-downloader[font-recovery]
39
+ ```
40
+
41
+ ---
42
+
43
+ ### CLI 模式
44
+
45
+ ```bash
46
+ # 初始化默认配置 (生成 settings.toml)
47
+ novel-cli config init
48
+
49
+ # 编辑 ./settings.toml 完成 site/book_ids 等
50
+ # 可查看 docs/3-settings-schema.md
51
+
52
+ # 执行下载任务
53
+ novel-cli download 123456
54
+ ```
55
+
56
+ - 详细可见: [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/4-supported-sites.md)
57
+ - 更多使用方法, 查看 [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-cli-usage-examples.md)
58
+
59
+ ---
60
+
61
+ ### TUI 模式 (终端用户界面)
62
+
63
+ **注意**: TUI 模式仍在开发中, 目前尚未实现登录和修改设置等功能。建议优先使用稳定的 CLI 模式。
64
+
65
+ ```bash
66
+ # 初始化默认配置 (生成 settings.toml)
67
+ novel-cli config init
68
+
69
+ # 编辑 ./settings.toml 修改网络配置
70
+ # 可查看 docs/3-settings-schema.md
71
+
72
+ # 启动 TUI 界面
73
+ novel-tui
74
+ ```
75
+
76
+ - 详细可见: [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/4-supported-sites.md)
77
+ - 更多使用方法, 查看 [使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-tui-usage-examples.md)
78
+
79
+ ---
80
+
81
+ ### GUI 模式 (图形界面)
82
+
83
+ 尚未实现
84
+
85
+ ---
86
+
87
+ ## 从 GitHub 安装 (开发版)
88
+
89
+ 如需体验开发中的最新功能, 可通过 GitHub 安装:
90
+
91
+ ```bash
92
+ git clone https://github.com/BowenZ217/novel-downloader.git
93
+ cd novel-downloader
94
+ pip install .
95
+ # 或安装带可选功能:
96
+ # pip install .[font-recovery]
97
+ ```
98
+
99
+ ---
100
+
101
+ ## 文档结构
102
+
103
+ - [项目简介](#项目简介)
104
+ - [安装](https://github.com/BowenZ217/novel-downloader/blob/main/docs/1-installation.md)
105
+ - [配置](https://github.com/BowenZ217/novel-downloader/blob/main/docs/2-configuration.md)
106
+ - [settings.toml 配置说明](https://github.com/BowenZ217/novel-downloader/blob/main/docs/3-settings-schema.md)
107
+ - [支持站点列表](https://github.com/BowenZ217/novel-downloader/blob/main/docs/4-supported-sites.md)
108
+ - [TUI 使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/5-tui-usage-examples.md)
109
+ - [CLI 使用示例](https://github.com/BowenZ217/novel-downloader/blob/main/docs/6-cli-usage-examples.md)
110
+ - [复制 Cookies](https://github.com/BowenZ217/novel-downloader/blob/main/docs/copy-cookies.md)
111
+ - [文件保存](https://github.com/BowenZ217/novel-downloader/blob/main/docs/file-saving.md)
112
+ - [TODO](https://github.com/BowenZ217/novel-downloader/blob/main/docs/todo.md)
113
+ - [开发](https://github.com/BowenZ217/novel-downloader/blob/main/docs/develop.md)
114
+ - [项目说明](#项目说明)
115
+
116
+ ---
117
+
118
+ ## 项目说明
119
+
120
+ - 本项目仅供学习和研究使用, 不得用于任何商业或违法用途。请遵守目标网站的 robots.txt 以及相关法律法规。
121
+ - 本项目开发者对因使用该工具所引起的任何法律责任不承担任何责任。
122
+ - 如果遇到网站结构变化或其他问题, 可能导致程序无法正常工作, 请自行调整代码或寻找其他解决方案。
@@ -6,7 +6,7 @@ novel_downloader
6
6
  Core package for the Novel Downloader project.
7
7
  """
8
8
 
9
- __version__ = "1.3.2"
9
+ __version__ = "1.4.0"
10
10
 
11
11
  __author__ = "Saudade Z"
12
12
  __email__ = "saudadez217@gmail.com"
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ novel_downloader.cli.clean
4
+ --------------------------
5
+
6
+ CLI subcommands for clean resources.
7
+ """
8
+
9
+ import shutil
10
+ from argparse import Namespace, _SubParsersAction
11
+ from pathlib import Path
12
+
13
+ from novel_downloader.utils.constants import (
14
+ CONFIG_DIR,
15
+ DATA_DIR,
16
+ JS_SCRIPT_DIR,
17
+ LOGGER_DIR,
18
+ MODEL_CACHE_DIR,
19
+ REC_CHAR_MODEL_REPO,
20
+ )
21
+ from novel_downloader.utils.i18n import t
22
+
23
+
24
+ def register_clean_subcommand(subparsers: _SubParsersAction) -> None: # type: ignore
25
+ parser = subparsers.add_parser("clean", help=t("help_clean"))
26
+
27
+ parser.add_argument("--logs", action="store_true", help=t("clean_logs"))
28
+ parser.add_argument("--cache", action="store_true", help=t("clean_cache"))
29
+ parser.add_argument("--data", action="store_true", help=t("clean_data"))
30
+ parser.add_argument("--config", action="store_true", help=t("clean_config"))
31
+ parser.add_argument("--models", action="store_true", help=t("clean_models"))
32
+ parser.add_argument("--all", action="store_true", help=t("clean_all"))
33
+ parser.add_argument("-y", "--yes", action="store_true", help=t("clean_yes"))
34
+
35
+ parser.add_argument("--hf-cache", action="store_true", help=t("clean_hf_cache"))
36
+ parser.add_argument(
37
+ "--hf-cache-all", action="store_true", help=t("clean_hf_cache_all")
38
+ )
39
+
40
+ parser.set_defaults(func=handle_clean)
41
+
42
+
43
+ def handle_clean(args: Namespace) -> None:
44
+ targets: list[Path] = []
45
+
46
+ if args.hf_cache_all:
47
+ try:
48
+ if _clean_model_repo_cache(all=True):
49
+ print(t("clean_hf_cache_all_done"))
50
+ except Exception as e:
51
+ print(t("clean_hf_cache_all_fail", err=str(e)))
52
+ elif args.hf_cache:
53
+ try:
54
+ if _clean_model_repo_cache(repo_id=REC_CHAR_MODEL_REPO):
55
+ print(t("clean_hf_model_done", repo=REC_CHAR_MODEL_REPO))
56
+ else:
57
+ print(t("clean_hf_model_not_found", repo=REC_CHAR_MODEL_REPO))
58
+ except Exception as e:
59
+ print(t("clean_hf_model_fail", err=str(e)))
60
+
61
+ if args.all:
62
+ if not args.yes:
63
+ confirm = prompt(t("clean_confirm"), default="n")
64
+ if confirm.lower() != "y":
65
+ print(t("clean_cancelled"))
66
+ return
67
+ targets = [
68
+ LOGGER_DIR,
69
+ JS_SCRIPT_DIR,
70
+ DATA_DIR,
71
+ CONFIG_DIR,
72
+ MODEL_CACHE_DIR,
73
+ ]
74
+ else:
75
+ if args.logs:
76
+ targets.append(LOGGER_DIR)
77
+ if args.cache:
78
+ targets.append(JS_SCRIPT_DIR)
79
+ if args.data:
80
+ targets.append(DATA_DIR)
81
+ if args.config:
82
+ targets.append(CONFIG_DIR)
83
+ if args.models:
84
+ targets.append(MODEL_CACHE_DIR)
85
+
86
+ if not targets and not args.hf_cache and not args.hf_cache_all:
87
+ print(t("clean_nothing"))
88
+ return
89
+
90
+ for path in targets:
91
+ _delete_path(path)
92
+
93
+
94
+ def prompt(message: str, default: str = "n") -> str:
95
+ """
96
+ Prompt the user for input with a default option.
97
+
98
+ :param message: The prompt message to display to the user.
99
+ :param default: The default value to use if the user provides no input ("y" or "n").
100
+ :return: The user's input (lowercased), or the default value if no input is given.
101
+ """
102
+ try:
103
+ full_prompt = f"{message} [{'Y/n' if default.lower() == 'y' else 'y/N'}]: "
104
+ response = input(full_prompt).strip().lower()
105
+ return response if response else default.lower()
106
+ except (KeyboardInterrupt, EOFError):
107
+ print("\n" + "Cancelled.")
108
+ return default.lower()
109
+
110
+
111
+ def _delete_path(p: Path) -> None:
112
+ if p.exists():
113
+ if p.is_file():
114
+ p.unlink()
115
+ else:
116
+ shutil.rmtree(p, ignore_errors=True)
117
+ print(f"[clean] {t('clean_deleted')}: {p}")
118
+ else:
119
+ print(f"[clean] {t('clean_not_found')}: {p}")
120
+
121
+
122
+ def _clean_model_repo_cache(
123
+ repo_id: str | None = None,
124
+ all: bool = False,
125
+ ) -> bool:
126
+ """
127
+ Delete Hugging Face cache for a specific repo.
128
+ """
129
+ from huggingface_hub import scan_cache_dir
130
+
131
+ cache_info = scan_cache_dir()
132
+
133
+ if all:
134
+ targets = cache_info.repos
135
+ elif repo_id:
136
+ targets = [r for r in cache_info.repos if r.repo_id == repo_id]
137
+ else:
138
+ return False
139
+
140
+ strategy = cache_info.delete_revisions(
141
+ *[rev.commit_hash for r in targets for rev in r.revisions]
142
+ )
143
+ print(f"[clean] Will free {strategy.expected_freed_size_str}")
144
+ strategy.execute()
145
+ return True
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ novel_downloader.cli.config
4
+ ---------------------------
5
+
6
+ CLI subcommands for configuration management.
7
+ """
8
+
9
+ import shutil
10
+ from argparse import Namespace, _SubParsersAction
11
+ from importlib.resources import as_file
12
+ from pathlib import Path
13
+
14
+ from novel_downloader.config import save_config_file, save_rules_as_json
15
+ from novel_downloader.utils.constants import DEFAULT_SETTINGS_PATHS
16
+ from novel_downloader.utils.i18n import t
17
+ from novel_downloader.utils.logger import setup_logging
18
+ from novel_downloader.utils.state import state_mgr
19
+
20
+ # from novel_downloader.utils.hash_store import img_hash_store
21
+
22
+
23
+ def register_config_subcommand(subparsers: _SubParsersAction) -> None: # type: ignore
24
+ parser = subparsers.add_parser("config", help=t("help_config"))
25
+ config_subparsers = parser.add_subparsers(dest="subcommand", required=True)
26
+
27
+ _register_init(config_subparsers)
28
+ _register_set_lang(config_subparsers)
29
+ _register_set_config(config_subparsers)
30
+ _register_update_rules(config_subparsers)
31
+ _register_set_cookies(config_subparsers)
32
+ # _register_add_hash(config_subparsers)
33
+
34
+
35
+ def _register_init(subparsers: _SubParsersAction) -> None: # type: ignore
36
+ parser = subparsers.add_parser("init", help=t("settings_init_help"))
37
+ parser.add_argument(
38
+ "--force", action="store_true", help=t("settings_init_force_help")
39
+ )
40
+ parser.set_defaults(func=_handle_init)
41
+
42
+
43
+ def _register_set_lang(subparsers: _SubParsersAction) -> None: # type: ignore
44
+ parser = subparsers.add_parser("set-lang", help=t("settings_set_lang_help"))
45
+ parser.add_argument("lang", choices=["zh", "en"], help="Language code")
46
+ parser.set_defaults(func=_handle_set_lang)
47
+
48
+
49
+ def _register_set_config(subparsers: _SubParsersAction) -> None: # type: ignore
50
+ parser = subparsers.add_parser("set-config", help=t("settings_set_config_help"))
51
+ parser.add_argument("path", type=str, help="Path to YAML config file")
52
+ parser.set_defaults(func=_handle_set_config)
53
+
54
+
55
+ def _register_update_rules(subparsers: _SubParsersAction) -> None: # type: ignore
56
+ parser = subparsers.add_parser("update-rules", help=t("settings_update_rules_help"))
57
+ parser.add_argument("path", type=str, help="Path to TOML/YAML/JSON rule file")
58
+ parser.set_defaults(func=_handle_update_rules)
59
+
60
+
61
+ def _register_set_cookies(subparsers: _SubParsersAction) -> None: # type: ignore
62
+ parser = subparsers.add_parser("set-cookies", help=t("settings_set_cookies_help"))
63
+ parser.add_argument("site", nargs="?", help="Site identifier")
64
+ parser.add_argument("cookies", nargs="?", help="Cookies string")
65
+ parser.set_defaults(func=_handle_set_cookies)
66
+
67
+
68
+ # def _register_add_hash(subparsers: _SubParsersAction) -> None: # type: ignore
69
+ # parser = subparsers.add_parser("add-hash", help=t("settings_add_hash_help"))
70
+ # parser.add_argument("--path", type=str, help=t("settings_add_hash_path_help"))
71
+ # parser.set_defaults(func=_handle_add_hash)
72
+
73
+
74
+ def _handle_init(args: Namespace) -> None:
75
+ setup_logging()
76
+ cwd = Path.cwd()
77
+
78
+ for resource in DEFAULT_SETTINGS_PATHS:
79
+ target_path = cwd / resource.name
80
+ should_copy = True
81
+
82
+ if target_path.exists():
83
+ if args.force:
84
+ print(t("settings_init_overwrite", filename=resource.name))
85
+ else:
86
+ print(t("settings_init_exists", filename=resource.name))
87
+ resp = (
88
+ input(
89
+ t("settings_init_confirm_overwrite", filename=resource.name)
90
+ + " [y/N]: "
91
+ )
92
+ .strip()
93
+ .lower()
94
+ )
95
+ should_copy = resp == "y"
96
+
97
+ if not should_copy:
98
+ print(t("settings_init_skip", filename=resource.name))
99
+ continue
100
+
101
+ try:
102
+ with as_file(resource) as actual_path:
103
+ shutil.copy(actual_path, target_path)
104
+ print(t("settings_init_copy", filename=resource.name))
105
+ except Exception as e:
106
+ print(t("settings_init_error", filename=resource.name, err=str(e)))
107
+ raise
108
+
109
+
110
+ def _handle_set_lang(args: Namespace) -> None:
111
+ state_mgr.set_language(args.lang)
112
+ print(t("settings_set_lang", lang=args.lang))
113
+
114
+
115
+ def _handle_set_config(args: Namespace) -> None:
116
+ try:
117
+ save_config_file(args.path)
118
+ print(t("settings_set_config", path=args.path))
119
+ except Exception as e:
120
+ print(t("settings_set_config_fail", err=str(e)))
121
+ raise
122
+
123
+
124
+ def _handle_update_rules(args: Namespace) -> None:
125
+ try:
126
+ save_rules_as_json(args.path)
127
+ print(t("settings_update_rules", path=args.path))
128
+ except Exception as e:
129
+ print(t("settings_update_rules_fail", err=str(e)))
130
+ raise
131
+
132
+
133
+ def _handle_set_cookies(args: Namespace) -> None:
134
+ site = args.site or input(t("settings_set_cookies_prompt_site") + ": ").strip()
135
+ cookies = (
136
+ args.cookies or input(t("settings_set_cookies_prompt_payload") + ": ").strip()
137
+ )
138
+
139
+ try:
140
+ state_mgr.set_cookies(site, cookies)
141
+ print(t("settings_set_cookies_success", site=site))
142
+ except Exception as e:
143
+ print(t("settings_set_cookies_fail", err=str(e)))
144
+ raise
145
+
146
+
147
+ # def _handle_add_hash(args: Namespace) -> None:
148
+ # if args.path:
149
+ # try:
150
+ # img_hash_store.add_from_map(args.path)
151
+ # img_hash_store.save()
152
+ # print(t("settings_add_hash_loaded", path=args.path))
153
+ # except Exception as e:
154
+ # print(t("settings_add_hash_load_fail", err=str(e)))
155
+ # raise
156
+ # else:
157
+ # print(t("settings_add_hash_prompt_tip"))
158
+ # while True:
159
+ # img_path = input(t("settings_add_hash_prompt_img") + ": ").strip()
160
+ # if not img_path or img_path.lower() in {"exit", "quit"}:
161
+ # break
162
+ # if not Path(img_path).exists():
163
+ # print(t("settings_add_hash_path_invalid"))
164
+ # continue
165
+
166
+ # label = input(t("settings_add_hash_prompt_label") + ": ").strip()
167
+ # if not label or label.lower() in {"exit", "quit"}:
168
+ # break
169
+
170
+ # try:
171
+ # img_hash_store.add_image(img_path, label)
172
+ # print(t("settings_add_hash_added", img=img_path, label=label))
173
+ # except Exception as e:
174
+ # print(t("settings_add_hash_failed", err=str(e)))
175
+
176
+ # img_hash_store.save()
177
+ # print(t("settings_add_hash_saved"))