novel-downloader 1.1.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 (115) hide show
  1. novel_downloader/__init__.py +14 -0
  2. novel_downloader/cli/__init__.py +14 -0
  3. novel_downloader/cli/clean.py +134 -0
  4. novel_downloader/cli/download.py +132 -0
  5. novel_downloader/cli/interactive.py +67 -0
  6. novel_downloader/cli/main.py +45 -0
  7. novel_downloader/cli/settings.py +177 -0
  8. novel_downloader/config/__init__.py +52 -0
  9. novel_downloader/config/adapter.py +153 -0
  10. novel_downloader/config/loader.py +177 -0
  11. novel_downloader/config/models.py +173 -0
  12. novel_downloader/config/site_rules.py +97 -0
  13. novel_downloader/core/__init__.py +25 -0
  14. novel_downloader/core/downloaders/__init__.py +22 -0
  15. novel_downloader/core/downloaders/base_async_downloader.py +157 -0
  16. novel_downloader/core/downloaders/base_downloader.py +187 -0
  17. novel_downloader/core/downloaders/common_asynb_downloader.py +207 -0
  18. novel_downloader/core/downloaders/common_downloader.py +191 -0
  19. novel_downloader/core/downloaders/qidian_downloader.py +208 -0
  20. novel_downloader/core/factory/__init__.py +33 -0
  21. novel_downloader/core/factory/downloader_factory.py +149 -0
  22. novel_downloader/core/factory/parser_factory.py +62 -0
  23. novel_downloader/core/factory/requester_factory.py +106 -0
  24. novel_downloader/core/factory/saver_factory.py +49 -0
  25. novel_downloader/core/interfaces/__init__.py +32 -0
  26. novel_downloader/core/interfaces/async_downloader_protocol.py +37 -0
  27. novel_downloader/core/interfaces/async_requester_protocol.py +68 -0
  28. novel_downloader/core/interfaces/downloader_protocol.py +37 -0
  29. novel_downloader/core/interfaces/parser_protocol.py +40 -0
  30. novel_downloader/core/interfaces/requester_protocol.py +65 -0
  31. novel_downloader/core/interfaces/saver_protocol.py +61 -0
  32. novel_downloader/core/parsers/__init__.py +28 -0
  33. novel_downloader/core/parsers/base_parser.py +96 -0
  34. novel_downloader/core/parsers/common_parser/__init__.py +14 -0
  35. novel_downloader/core/parsers/common_parser/helper.py +321 -0
  36. novel_downloader/core/parsers/common_parser/main_parser.py +86 -0
  37. novel_downloader/core/parsers/qidian_parser/__init__.py +20 -0
  38. novel_downloader/core/parsers/qidian_parser/browser/__init__.py +13 -0
  39. novel_downloader/core/parsers/qidian_parser/browser/chapter_encrypted.py +498 -0
  40. novel_downloader/core/parsers/qidian_parser/browser/chapter_normal.py +97 -0
  41. novel_downloader/core/parsers/qidian_parser/browser/chapter_router.py +70 -0
  42. novel_downloader/core/parsers/qidian_parser/browser/main_parser.py +110 -0
  43. novel_downloader/core/parsers/qidian_parser/session/__init__.py +13 -0
  44. novel_downloader/core/parsers/qidian_parser/session/chapter_encrypted.py +451 -0
  45. novel_downloader/core/parsers/qidian_parser/session/chapter_normal.py +119 -0
  46. novel_downloader/core/parsers/qidian_parser/session/chapter_router.py +67 -0
  47. novel_downloader/core/parsers/qidian_parser/session/main_parser.py +113 -0
  48. novel_downloader/core/parsers/qidian_parser/session/node_decryptor.py +164 -0
  49. novel_downloader/core/parsers/qidian_parser/shared/__init__.py +38 -0
  50. novel_downloader/core/parsers/qidian_parser/shared/book_info_parser.py +95 -0
  51. novel_downloader/core/parsers/qidian_parser/shared/helpers.py +133 -0
  52. novel_downloader/core/requesters/__init__.py +31 -0
  53. novel_downloader/core/requesters/base_async_session.py +297 -0
  54. novel_downloader/core/requesters/base_browser.py +210 -0
  55. novel_downloader/core/requesters/base_session.py +243 -0
  56. novel_downloader/core/requesters/common_requester/__init__.py +18 -0
  57. novel_downloader/core/requesters/common_requester/common_async_session.py +96 -0
  58. novel_downloader/core/requesters/common_requester/common_session.py +126 -0
  59. novel_downloader/core/requesters/qidian_requester/__init__.py +22 -0
  60. novel_downloader/core/requesters/qidian_requester/qidian_broswer.py +377 -0
  61. novel_downloader/core/requesters/qidian_requester/qidian_session.py +202 -0
  62. novel_downloader/core/savers/__init__.py +20 -0
  63. novel_downloader/core/savers/base_saver.py +169 -0
  64. novel_downloader/core/savers/common_saver/__init__.py +13 -0
  65. novel_downloader/core/savers/common_saver/common_epub.py +232 -0
  66. novel_downloader/core/savers/common_saver/common_txt.py +176 -0
  67. novel_downloader/core/savers/common_saver/main_saver.py +86 -0
  68. novel_downloader/core/savers/epub_utils/__init__.py +27 -0
  69. novel_downloader/core/savers/epub_utils/css_builder.py +68 -0
  70. novel_downloader/core/savers/epub_utils/initializer.py +98 -0
  71. novel_downloader/core/savers/epub_utils/text_to_html.py +132 -0
  72. novel_downloader/core/savers/epub_utils/volume_intro.py +61 -0
  73. novel_downloader/core/savers/qidian_saver.py +22 -0
  74. novel_downloader/locales/en.json +91 -0
  75. novel_downloader/locales/zh.json +91 -0
  76. novel_downloader/resources/config/rules.toml +196 -0
  77. novel_downloader/resources/config/settings.yaml +73 -0
  78. novel_downloader/resources/css_styles/main.css +104 -0
  79. novel_downloader/resources/css_styles/volume-intro.css +56 -0
  80. novel_downloader/resources/images/volume_border.png +0 -0
  81. novel_downloader/resources/js_scripts/qidian_decrypt_node.js +82 -0
  82. novel_downloader/resources/json/replace_word_map.json +4 -0
  83. novel_downloader/resources/text/blacklist.txt +22 -0
  84. novel_downloader/utils/__init__.py +0 -0
  85. novel_downloader/utils/cache.py +24 -0
  86. novel_downloader/utils/constants.py +158 -0
  87. novel_downloader/utils/crypto_utils.py +144 -0
  88. novel_downloader/utils/file_utils/__init__.py +43 -0
  89. novel_downloader/utils/file_utils/io.py +252 -0
  90. novel_downloader/utils/file_utils/normalize.py +68 -0
  91. novel_downloader/utils/file_utils/sanitize.py +77 -0
  92. novel_downloader/utils/fontocr/__init__.py +23 -0
  93. novel_downloader/utils/fontocr/ocr_v1.py +304 -0
  94. novel_downloader/utils/fontocr/ocr_v2.py +658 -0
  95. novel_downloader/utils/hash_store.py +288 -0
  96. novel_downloader/utils/hash_utils.py +103 -0
  97. novel_downloader/utils/i18n.py +41 -0
  98. novel_downloader/utils/logger.py +104 -0
  99. novel_downloader/utils/model_loader.py +72 -0
  100. novel_downloader/utils/network.py +287 -0
  101. novel_downloader/utils/state.py +156 -0
  102. novel_downloader/utils/text_utils/__init__.py +27 -0
  103. novel_downloader/utils/text_utils/chapter_formatting.py +46 -0
  104. novel_downloader/utils/text_utils/diff_display.py +75 -0
  105. novel_downloader/utils/text_utils/font_mapping.py +31 -0
  106. novel_downloader/utils/text_utils/text_cleaning.py +57 -0
  107. novel_downloader/utils/time_utils/__init__.py +22 -0
  108. novel_downloader/utils/time_utils/datetime_utils.py +146 -0
  109. novel_downloader/utils/time_utils/sleep_utils.py +49 -0
  110. novel_downloader-1.1.0.dist-info/METADATA +157 -0
  111. novel_downloader-1.1.0.dist-info/RECORD +115 -0
  112. novel_downloader-1.1.0.dist-info/WHEEL +5 -0
  113. novel_downloader-1.1.0.dist-info/entry_points.txt +2 -0
  114. novel_downloader-1.1.0.dist-info/licenses/LICENSE +21 -0
  115. novel_downloader-1.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ novel_downloader.core.savers.epub_utils.css_builder
5
+
6
+ Reads local CSS files and wraps them into epub.EpubItem objects,
7
+ returning a list ready to be added to the EPUB.
8
+ """
9
+
10
+ import logging
11
+ from importlib.abc import Traversable
12
+ from typing import Dict, List, Union
13
+
14
+ from ebooklib import epub
15
+
16
+ from novel_downloader.utils.constants import (
17
+ CSS_MAIN_PATH,
18
+ CSS_VOLUME_INTRO_PATH,
19
+ )
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ def create_css_items(
25
+ include_main: bool = True,
26
+ include_volume: bool = True,
27
+ ) -> List[epub.EpubItem]:
28
+ """
29
+ :param include_main: Whether to load the main stylesheet.
30
+ :param include_volume: Whether to load the “volume intro” stylesheet.
31
+ :returns: A list of epub.EpubItem ready to add to the book.
32
+ """
33
+ css_config: List[Dict[str, Union[str, bool, Traversable]]] = [
34
+ {
35
+ "include": include_main,
36
+ "path": CSS_MAIN_PATH,
37
+ "uid": "style",
38
+ "file_name": "Styles/main.css",
39
+ },
40
+ {
41
+ "include": include_volume,
42
+ "path": CSS_VOLUME_INTRO_PATH,
43
+ "uid": "volume_style",
44
+ "file_name": "Styles/volume-intro.css",
45
+ },
46
+ ]
47
+ css_items: List[epub.EpubItem] = []
48
+
49
+ for css in css_config:
50
+ if css["include"]:
51
+ path = css["path"]
52
+ assert isinstance(path, Traversable)
53
+ try:
54
+ content: str = path.read_text(encoding="utf-8")
55
+ css_items.append(
56
+ epub.EpubItem(
57
+ uid=css["uid"],
58
+ file_name=css["file_name"],
59
+ media_type="text/css",
60
+ content=content,
61
+ )
62
+ )
63
+ except FileNotFoundError:
64
+ logger.info(f"[epub] CSS 文件不存在: {css['path']}")
65
+ except UnicodeDecodeError:
66
+ logger.info(f"[epub] 无法解码文件: {css['path']}")
67
+
68
+ return css_items
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ novel_downloader.core.savers.epub_utils.initializer
5
+
6
+ Initializes an epub.EpubBook object, sets metadata
7
+ (identifier, title, author, language, description),
8
+ adds a cover, and prepares the initial spine and TOC entries.
9
+ """
10
+
11
+ import logging
12
+ from pathlib import Path
13
+ from typing import Any, Dict, List, Optional, Tuple
14
+
15
+ from ebooklib import epub
16
+
17
+ from novel_downloader.utils.constants import (
18
+ EPUB_IMAGE_FOLDER,
19
+ EPUB_TEXT_FOLDER,
20
+ VOLUME_BORDER_IMAGE_PATH,
21
+ )
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ def init_epub(
27
+ book_info: Dict[str, Any],
28
+ book_id: str,
29
+ intro_html: str,
30
+ book_cover_path: Optional[Path] = None,
31
+ include_toc: bool = False,
32
+ ) -> Tuple[epub.EpubBook, List[Any], List[Any]]:
33
+ """
34
+ Initialize an EPUB book with metadata, optional cover, and intro page.
35
+
36
+ :param book_info: Dict with keys 'book_name', 'author', 'summary'.
37
+ :param book_id: Book identifier (numeric or string).
38
+ :param intro_html: Intro content in XHTML format.
39
+ :param book_cover_path: Optional Path to the cover image file.
40
+ :param include_toc: Whether to include the <nav> item in the spine.
41
+ :return: (book, spine, toc_list)
42
+ """
43
+ book = epub.EpubBook()
44
+ book.set_identifier(str(book_id))
45
+ book.set_title(book_info.get("book_name", "未找到书名"))
46
+ book.set_language("zh-CN")
47
+ book.add_author(book_info.get("author", "未找到作者"))
48
+ book.add_metadata("DC", "description", book_info.get("summary", "未找到作品简介"))
49
+
50
+ spine = []
51
+
52
+ # cover
53
+ if book_cover_path:
54
+ try:
55
+ cover_bytes = book_cover_path.read_bytes()
56
+ ext = book_cover_path.suffix.lower()
57
+ ext = ext if ext in [".jpg", ".jpeg", ".png"] else ".jpeg"
58
+ filename = f"{EPUB_IMAGE_FOLDER}/cover{ext}"
59
+ book.set_cover(filename, cover_bytes)
60
+ spine.append("cover")
61
+ except FileNotFoundError:
62
+ logger.info(f"[epub] 封面图片不存在: {book_cover_path}")
63
+ except Exception as e:
64
+ logger.info(f"[epub] 读取封面失败: {book_cover_path},错误:{e}")
65
+
66
+ # 导航菜单
67
+ if include_toc:
68
+ spine.append("nav")
69
+
70
+ # 简介页面
71
+ intro = epub.EpubHtml(
72
+ title="书籍简介",
73
+ file_name=f"{EPUB_TEXT_FOLDER}/intro.xhtml",
74
+ lang="zh-CN",
75
+ uid="intro",
76
+ )
77
+ intro.content = intro_html
78
+ intro.add_link(href="../Styles/main.css", rel="stylesheet", type="text/css")
79
+ book.add_item(intro)
80
+ spine.append(intro)
81
+
82
+ # 添加卷边框图像 (volume_border.png)
83
+ try:
84
+ border_bytes = VOLUME_BORDER_IMAGE_PATH.read_bytes()
85
+ border_item = epub.EpubItem(
86
+ uid="volume-border",
87
+ file_name=f"{EPUB_IMAGE_FOLDER}/volume_border.png",
88
+ media_type="image/png",
89
+ content=border_bytes,
90
+ )
91
+ book.add_item(border_item)
92
+ except FileNotFoundError:
93
+ logger.info(f"[epub] 卷边框图片不存在: {VOLUME_BORDER_IMAGE_PATH}")
94
+ except Exception as e:
95
+ logger.info(f"[epub] 读取卷边框失败: {VOLUME_BORDER_IMAGE_PATH}: {e}")
96
+
97
+ toc_list = [epub.Link(intro.file_name, "书籍简介", intro.id)]
98
+ return book, spine, toc_list
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ novel_downloader.core.savers.epub_utils.text_to_html
5
+
6
+ Module for converting raw chapter text to formatted HTML,
7
+ with automatic word correction and optional image/tag support.
8
+ """
9
+
10
+ import json
11
+ import logging
12
+ from typing import Any, Dict
13
+
14
+ from novel_downloader.utils.constants import REPLACE_WORD_MAP_PATH
15
+ from novel_downloader.utils.text_utils import diff_inline_display
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ # Load and sort replacement map from JSON
21
+ try:
22
+ replace_map_raw = REPLACE_WORD_MAP_PATH.read_text(encoding="utf-8")
23
+ REPLACE_WORDS_MAP = json.loads(replace_map_raw)
24
+ REPLACE_WORDS_MAP = dict(
25
+ sorted(REPLACE_WORDS_MAP.items(), key=lambda x: len(x[0]), reverse=True)
26
+ )
27
+ except Exception as e:
28
+ REPLACE_WORDS_MAP = {}
29
+ logger.info(
30
+ f"[epub] Failed to load REPLACE_WORDS_MAP from {REPLACE_WORD_MAP_PATH}: {e}"
31
+ )
32
+
33
+
34
+ def _check_and_correct_words(txt_str: str) -> str:
35
+ """
36
+ Perform word replacement using REPLACE_WORDS_MAP.
37
+
38
+ :param txt_str: Raw string of text.
39
+ :return: String with corrected words.
40
+ """
41
+ for k, v in REPLACE_WORDS_MAP.items():
42
+ txt_str = txt_str.replace(k, v)
43
+ return txt_str
44
+
45
+
46
+ def chapter_txt_to_html(
47
+ chapter_title: str,
48
+ chapter_text: str,
49
+ author_say: str,
50
+ ) -> str:
51
+ """
52
+ Convert chapter text and author note to styled HTML.
53
+
54
+ :param chapter_title: Title of the chapter.
55
+ :param chapter_text: Main content of the chapter.
56
+ :param author_say: Optional author note content.
57
+ :return: Rendered HTML as a string.
58
+ """
59
+
60
+ def _render_lines(text: str) -> str:
61
+ parts = []
62
+ for line in text.strip().splitlines():
63
+ line = line.strip()
64
+ if not line:
65
+ continue
66
+
67
+ if (
68
+ line.startswith("<img")
69
+ and line.endswith("/>")
70
+ or line.startswith('<div class="duokan-image-single illus">')
71
+ and line.endswith("</div>")
72
+ ):
73
+ parts.append(line)
74
+ else:
75
+ corrected = _check_and_correct_words(line)
76
+ if corrected != line:
77
+ diff = diff_inline_display(line, corrected)
78
+ logger.info("[epub] Correction diff:\n%s", diff)
79
+ parts.append(f"<p>{corrected}</p>")
80
+ return "\n".join(parts)
81
+
82
+ html_parts = [f"<h2>{chapter_title}</h2>"]
83
+ html_parts.append(_render_lines(chapter_text))
84
+
85
+ if author_say.strip():
86
+ html_parts.extend(["<hr />", "<p>作者说:</p>", _render_lines(author_say)])
87
+
88
+ return "\n".join(html_parts)
89
+
90
+
91
+ def generate_book_intro_html(book_info: Dict[str, Any]) -> str:
92
+ """
93
+ Generate HTML string for a book's information and summary.
94
+
95
+ This function takes a dictionary containing book details and formats
96
+ it into a styled HTML block, skipping any missing fields gracefully.
97
+
98
+ :param book_info: A dictionary containing keys like 'book_name'...
99
+
100
+ :return: An HTML-formatted string presenting the book's information.
101
+ """
102
+ book_name = book_info.get("book_name")
103
+ author = book_info.get("author")
104
+ serial_status = book_info.get("serial_status")
105
+ word_count = book_info.get("word_count")
106
+ summary = book_info.get("summary", "").strip()
107
+
108
+ # Start composing the HTML output
109
+ html_parts = ["<h1>书籍简介</h1>", '<div class="list">', "<ul>"]
110
+
111
+ if book_name:
112
+ html_parts.append(f"<li>书名: 《{book_name}》</li>")
113
+ if author:
114
+ html_parts.append(f"<li>作者: {author}</li>")
115
+
116
+ if word_count:
117
+ html_parts.append(f"<li>字数: {word_count}</li>")
118
+ if serial_status:
119
+ html_parts.append(f"<li>状态: {serial_status}</li>")
120
+
121
+ html_parts.append("</ul>")
122
+ html_parts.append("</div>")
123
+ html_parts.append('<p class="new-page-after"><br/></p>')
124
+
125
+ if summary:
126
+ html_parts.append("<h2>简介</h2>")
127
+ for paragraph in summary.split("\n"):
128
+ paragraph = paragraph.strip()
129
+ if paragraph:
130
+ html_parts.append(f"<p>{paragraph}</p>")
131
+
132
+ return "\n".join(html_parts)
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ novel_downloader.core.savers.epub_utils.volume_intro
5
+
6
+ Responsible for generating HTML code for volume introduction pages,
7
+ including two style variants and a unified entry point.
8
+ """
9
+
10
+ from typing import Tuple
11
+
12
+ from novel_downloader.utils.constants import EPUB_IMAGE_FOLDER
13
+
14
+
15
+ def split_volume_title(volume_title: str) -> Tuple[str, str]:
16
+ """
17
+ Split volume title into two parts for better display.
18
+
19
+ :param volume_title: Original volume title string.
20
+ :return: Tuple of (line1, line2)
21
+ """
22
+ if " " in volume_title:
23
+ parts = volume_title.split(" ")
24
+ elif "-" in volume_title:
25
+ parts = volume_title.split("-")
26
+ else:
27
+ return "", volume_title
28
+
29
+ return parts[0], "".join(parts[1:])
30
+
31
+
32
+ def create_volume_intro(volume_title: str, volume_intro_text: str = "") -> str:
33
+ """
34
+ Generate the HTML snippet for a volume's introduction section.
35
+
36
+ :param volume_title: Title of the volume.
37
+ :param volume_intro_text: Optional introduction text for the volume.
38
+ :return: HTML string representing the volume's intro section.
39
+ """
40
+ line1, line2 = split_volume_title(volume_title)
41
+
42
+ def make_border_img(class_name: str) -> str:
43
+ return (
44
+ f'<div class="{class_name}">'
45
+ f'<img alt="" class="{class_name}" '
46
+ f'src="../{EPUB_IMAGE_FOLDER}/volume_border.png" />'
47
+ f"</div>"
48
+ )
49
+
50
+ html_parts = [make_border_img("border1")]
51
+
52
+ if line1:
53
+ html_parts.append(f'<h1 class="volume-title-line1">{line1}</h1>')
54
+
55
+ html_parts.append(f'<p class="volume-title-line2">{line2}</p>')
56
+ html_parts.append(make_border_img("border2"))
57
+
58
+ if volume_intro_text:
59
+ html_parts.append(f'<p class="intro">{volume_intro_text}</p>')
60
+
61
+ return "\n".join(html_parts)
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ novel_downloader.core.savers.qidian_saver
5
+ -----------------------------------------
6
+
7
+ This module provides the `QidianSaver` class for handling the saving process
8
+ of novels sourced from Qidian (起点中文网). It implements the platform-specific
9
+ logic required to structure and export novel content into desired formats.
10
+ """
11
+
12
+ from novel_downloader.config.models import SaverConfig
13
+
14
+ from .common_saver import CommonSaver
15
+
16
+
17
+ class QidianSaver(CommonSaver):
18
+ def __init__(self, config: SaverConfig):
19
+ super().__init__(config, site="qidian")
20
+
21
+
22
+ __all__ = ["QidianSaver"]
@@ -0,0 +1,91 @@
1
+ {
2
+ "cli_help": "Novel Downloader CLI tool.",
3
+ "help_config": "Path to config file",
4
+ "help_download": "Download novels",
5
+ "help_clean": "Clean cache and configuration files",
6
+ "main_no_command": "No command provided, entering interactive mode…",
7
+
8
+ "settings_help": "Configure downloader settings.",
9
+ "settings_set_lang_help": "Switch language between Chinese and English.",
10
+ "settings_set_config_help": "Set and save a custom YAML configuration file.",
11
+ "settings_update_rules_help": "Update site rules from a TOML/YAML/JSON file.",
12
+ "settings_init_help": "Initialize default config and rule files in the current directory.",
13
+ "settings_init_force_help": "Force overwrite if file already exists.",
14
+ "settings_init_exists": "File already exists: {filename}",
15
+ "settings_init_confirm_overwrite": "Do you want to overwrite {filename}?",
16
+ "settings_init_skip": "Skipped: {filename}",
17
+ "settings_init_copy": "Copied: {filename}",
18
+ "settings_init_overwrite": "Overwriting existing file: {filename}",
19
+ "settings_init_error": "Failed to copy {filename}: {err}",
20
+ "settings_set_lang": "Language switched to {lang}",
21
+ "settings_set_config": "Configuration file saved from {path}",
22
+ "settings_set_config_fail": "Failed to save config file: {err}",
23
+ "settings_update_rules": "Site rules updated from {path}",
24
+ "settings_update_rules_fail": "Failed to update site rules: {err}",
25
+ "settings_set_cookies_help": "Set cookies for a specific site.",
26
+ "settings_set_cookies_prompt_site": "Site identifier (e.g. 'qidian')",
27
+ "settings_set_cookies_prompt_payload": "Cookie payload (JSON or 'k=v; k2=v2')",
28
+ "settings_set_cookies_success": "Cookies for site '{site}' have been updated.",
29
+ "settings_set_cookies_fail": "Failed to set cookies: {err}",
30
+ "settings_add_hash_help": "Add image hashes manually or from file",
31
+ "settings_add_hash_path_help": "JSON path mapping image to label",
32
+ "settings_add_hash_prompt_img": "Image path",
33
+ "settings_add_hash_prompt_label": "Label for image",
34
+ "settings_add_hash_prompt_tip": "Enter image path and label. Leave blank or type 'exit' to finish.",
35
+ "settings_add_hash_path_invalid": "Path does not exist. Try again.",
36
+ "settings_add_hash_added": "Added image '{img}' under label '{label}'",
37
+ "settings_add_hash_failed": "Failed to add image: {err}",
38
+ "settings_add_hash_loaded": "Added images from mapping file: {path}",
39
+ "settings_add_hash_load_fail": "Failed to load image map: {err}",
40
+ "settings_add_hash_saved": "Image hash store saved",
41
+
42
+ "interactive_help": "Interactive mode for novel selection and preview.",
43
+ "interactive_option_download": "Download novels",
44
+ "interactive_browse_help": "Browse available novels interactively.",
45
+ "interactive_prompt_book_ids": "Enter book IDs to download, separated by spaces",
46
+ "interactive_preview_help": "Preview chapters before downloading.",
47
+ "interactive_no_sub": "No subcommand provided. Please select an action:",
48
+ "interactive_option_browse": "Browse novels",
49
+ "interactive_option_preview": "Preview chapters",
50
+ "interactive_option_exit": "Exit",
51
+ "interactive_prompt_choice": "Enter your choice",
52
+ "interactive_browse_start": "Starting interactive browser…",
53
+ "interactive_preview_start": "Previewing chapters…",
54
+ "interactive_exit": "Exiting.",
55
+
56
+ "download_help": "Download full novels by book IDs.",
57
+ "download_short_help": "Download novels",
58
+ "download_option_site": "Website source, default is '{default}'.",
59
+ "download_using_config": "Using config: {path}",
60
+ "download_site_info": "Site: {site}",
61
+ "download_no_ids": "No book IDs provided. Exiting.",
62
+ "download_fail_get_ids": "Failed to get book IDs from config: {err}",
63
+ "download_only_example": "Only example book IDs found (e.g. '{example}').",
64
+ "download_edit_config": "Please edit your config and replace them with real book IDs.",
65
+ "download_downloading": "Downloading book {book_id} from {site}...",
66
+ "download_prompt_parse": "Parse...",
67
+
68
+ "clean_logs": "Clean log directory",
69
+ "clean_cache": "Clean scripts and browser cache",
70
+ "clean_state": "Clean state files (state.json)",
71
+ "clean_data": "Clean data files (e.g. image hashes, browser data)",
72
+ "clean_config": "Clean config files (e.g. settings.yaml, site rules)",
73
+ "clean_models": "Clean downloaded model cache",
74
+ "clean_hf_cache": "Clear Hugging Face model cache",
75
+ "clean_hf_cache_done": "Hugging Face cache cleared",
76
+ "clean_hf_cache_fail": "Failed to clear Hugging Face cache: {err}",
77
+ "clean_hf_model": "Clear cached model from Hugging Face",
78
+ "clean_hf_model_done": "Cache for model '{repo}' has been cleared",
79
+ "clean_hf_model_not_found": "No local cache found for model '{repo}'",
80
+ "clean_hf_model_fail": "Failed to clear model cache: {err}",
81
+ "clean_hf_cache_all": "Clear all Hugging Face model and dataset cache",
82
+ "clean_hf_cache_all_done": "All Hugging Face cache cleared",
83
+ "clean_hf_cache_all_fail": "Failed to clear Hugging Face cache: {err}",
84
+ "clean_all": "Clean all settings, cache, and state files",
85
+ "clean_yes": "Skip confirmation prompt",
86
+ "clean_confirm": "Are you sure you want to delete all local data? (y/N)",
87
+ "clean_nothing": "No clean option specified",
88
+ "clean_deleted": "Deleted",
89
+ "clean_not_found": "Not found",
90
+ "clean_cancelled": "Clean operation cancelled"
91
+ }
@@ -0,0 +1,91 @@
1
+ {
2
+ "cli_help": "小说下载器 CLI 工具",
3
+ "help_config": "配置文件路径",
4
+ "help_download": "下载小说",
5
+ "help_clean": "清理缓存和配置文件",
6
+ "main_no_command": "未提供命令, 进入交互模式...",
7
+
8
+ "settings_help": "配置下载器设置",
9
+ "settings_set_lang_help": "在中文和英文之间切换语言",
10
+ "settings_set_config_help": "设置并保存自定义 YAML 配置文件",
11
+ "settings_update_rules_help": "从 TOML/YAML/JSON 文件更新站点规则",
12
+ "settings_init_help": "在当前目录初始化默认配置和规则文件",
13
+ "settings_init_force_help": "如果文件已存在则强制覆盖",
14
+ "settings_init_exists": "文件已存在: {filename}",
15
+ "settings_init_confirm_overwrite": "你确定要覆盖 {filename} 吗?",
16
+ "settings_init_skip": "文件已存在: {filename}, 已跳过",
17
+ "settings_init_copy": "已复制默认文件: {filename}",
18
+ "settings_init_overwrite": "已覆盖现有文件: {filename}",
19
+ "settings_init_error": "复制 {filename} 失败: {err}",
20
+ "settings_set_lang": "语言已切换为 {lang}",
21
+ "settings_set_config": "已从 {path} 保存配置文件",
22
+ "settings_set_config_fail": "保存配置文件失败: {err}",
23
+ "settings_update_rules": "已从 {path} 更新站点规则",
24
+ "settings_update_rules_fail": "更新站点规则失败: {err}",
25
+ "settings_set_cookies_help": "为特定站点设置 Cookie",
26
+ "settings_set_cookies_prompt_site": "站点标识 (例如 'qidian')",
27
+ "settings_set_cookies_prompt_payload": "Cookie 内容 (JSON 或 'k=v; k2=v2')",
28
+ "settings_set_cookies_success": "已更新站点 '{site}' 的 Cookie",
29
+ "settings_set_cookies_fail": "设置 Cookie 失败: {err}",
30
+ "settings_add_hash_help": "手动或通过文件添加图像哈希",
31
+ "settings_add_hash_path_help": "映射图片路径到标签的 JSON 文件路径",
32
+ "settings_add_hash_prompt_img": "图片路径",
33
+ "settings_add_hash_prompt_label": "该图片对应的标签",
34
+ "settings_add_hash_prompt_tip": "请输入图片路径和标签, 输入空行或 'exit' 退出",
35
+ "settings_add_hash_path_invalid": "路径不存在, 请重试",
36
+ "settings_add_hash_added": "已添加图片 '{img}', 标签为 '{label}'",
37
+ "settings_add_hash_failed": "添加图片失败: {err}",
38
+ "settings_add_hash_loaded": "已从映射文件导入图片: {path}",
39
+ "settings_add_hash_load_fail": "加载映射文件失败: {err}",
40
+ "settings_add_hash_saved": "图像哈希库已保存",
41
+
42
+ "interactive_help": "小说选择和预览的交互式模式",
43
+ "interactive_option_download": "下载小说",
44
+ "interactive_browse_help": "以交互方式浏览可用小说",
45
+ "interactive_prompt_book_ids": "请输入要下载的书籍 ID, 用空格分隔",
46
+ "interactive_preview_help": "下载前预览章节",
47
+ "interactive_no_sub": "未提供子命令请选择一项操作:",
48
+ "interactive_option_browse": "浏览小说",
49
+ "interactive_option_preview": "预览章节",
50
+ "interactive_option_exit": "退出",
51
+ "interactive_prompt_choice": "请输入你的选择",
52
+ "interactive_browse_start": "启动交互式浏览器...",
53
+ "interactive_preview_start": "正在预览章节...",
54
+ "interactive_exit": "正在退出",
55
+
56
+ "download_help": "按书籍 ID 下载完整小说",
57
+ "download_short_help": "下载小说",
58
+ "download_option_site": "网站来源, 默认为 '{default}'",
59
+ "download_using_config": "使用配置: {path}",
60
+ "download_site_info": "站点: {site}",
61
+ "download_no_ids": "未提供书籍 ID, 正在退出",
62
+ "download_fail_get_ids": "从配置获取书籍 ID 失败: {err}",
63
+ "download_only_example": "只发现示例书籍 ID (例如 '{example}')",
64
+ "download_edit_config": "请编辑配置并将示例 ID 替换为真实书籍 ID",
65
+ "download_downloading": "正在从 {site} 下载书籍 {book_id}...",
66
+ "download_prompt_parse": "结束...",
67
+
68
+ "clean_logs": "清理日志目录",
69
+ "clean_cache": "清理脚本和浏览器缓存",
70
+ "clean_state": "清理状态文件 (state.json)",
71
+ "clean_data": "清理数据文件 (如 image hashes, 浏览器数据)",
72
+ "clean_config": "清理配置文件 (如 settings.yaml 和规则)",
73
+ "clean_models": "清理模型缓存目录",
74
+ "clean_hf_cache": "清理 Hugging Face 下载缓存",
75
+ "clean_hf_cache_done": "已清除 Hugging Face 本地缓存",
76
+ "clean_hf_cache_fail": "清除 Hugging Face 缓存失败: {err}",
77
+ "clean_hf_model": "清理 Hugging Face 中模型缓存",
78
+ "clean_hf_model_done": "已清除模型 '{repo}' 的缓存",
79
+ "clean_hf_model_not_found": "未发现模型 '{repo}' 的本地缓存",
80
+ "clean_hf_model_fail": "清理模型缓存失败: {err}",
81
+ "clean_hf_cache_all": "清理所有 Hugging Face 模型与数据集缓存",
82
+ "clean_hf_cache_all_done": "已清除所有 Hugging Face 缓存",
83
+ "clean_hf_cache_all_fail": "清除 Hugging Face 缓存失败: {err}",
84
+ "clean_all": "清理所有配置, 缓存与状态",
85
+ "clean_yes": "跳过确认提示",
86
+ "clean_confirm": "确认要删除所有本地数据? (y/N)",
87
+ "clean_nothing": "未指定任何要清理的内容",
88
+ "clean_deleted": "已删除",
89
+ "clean_not_found": "未找到",
90
+ "clean_cancelled": "已取消清理操作"
91
+ }