novel-downloader 1.4.1__py3-none-any.whl → 1.4.2__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 (29) hide show
  1. novel_downloader/__init__.py +1 -1
  2. novel_downloader/cli/download.py +69 -10
  3. novel_downloader/config/adapter.py +42 -9
  4. novel_downloader/core/downloaders/base.py +26 -22
  5. novel_downloader/core/downloaders/common.py +41 -5
  6. novel_downloader/core/downloaders/qidian.py +60 -32
  7. novel_downloader/core/exporters/common/epub.py +153 -68
  8. novel_downloader/core/exporters/epub_util.py +1358 -0
  9. novel_downloader/core/exporters/linovelib/epub.py +147 -190
  10. novel_downloader/core/fetchers/qidian/browser.py +62 -10
  11. novel_downloader/core/interfaces/downloader.py +13 -12
  12. novel_downloader/locales/en.json +2 -0
  13. novel_downloader/locales/zh.json +2 -0
  14. novel_downloader/models/__init__.py +2 -0
  15. novel_downloader/models/config.py +8 -0
  16. novel_downloader/tui/screens/home.py +5 -4
  17. novel_downloader/utils/constants.py +0 -29
  18. {novel_downloader-1.4.1.dist-info → novel_downloader-1.4.2.dist-info}/METADATA +4 -2
  19. {novel_downloader-1.4.1.dist-info → novel_downloader-1.4.2.dist-info}/RECORD +23 -28
  20. novel_downloader/core/exporters/epub_utils/__init__.py +0 -40
  21. novel_downloader/core/exporters/epub_utils/css_builder.py +0 -75
  22. novel_downloader/core/exporters/epub_utils/image_loader.py +0 -131
  23. novel_downloader/core/exporters/epub_utils/initializer.py +0 -100
  24. novel_downloader/core/exporters/epub_utils/text_to_html.py +0 -178
  25. novel_downloader/core/exporters/epub_utils/volume_intro.py +0 -60
  26. {novel_downloader-1.4.1.dist-info → novel_downloader-1.4.2.dist-info}/WHEEL +0 -0
  27. {novel_downloader-1.4.1.dist-info → novel_downloader-1.4.2.dist-info}/entry_points.txt +0 -0
  28. {novel_downloader-1.4.1.dist-info → novel_downloader-1.4.2.dist-info}/licenses/LICENSE +0 -0
  29. {novel_downloader-1.4.1.dist-info → novel_downloader-1.4.2.dist-info}/top_level.txt +0 -0
@@ -1,178 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- novel_downloader.core.exporters.epub_utils.text_to_html
4
- -------------------------------------------------------
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
- import re
13
- from pathlib import Path
14
- from typing import Any
15
-
16
- from novel_downloader.utils.constants import (
17
- EPUB_IMAGE_WRAPPER,
18
- REPLACE_WORD_MAP_PATH,
19
- )
20
- from novel_downloader.utils.network import download_image
21
- from novel_downloader.utils.text_utils import diff_inline_display
22
-
23
- logger = logging.getLogger(__name__)
24
-
25
- _IMG_TAG_PATTERN = re.compile(
26
- r'<img\s+[^>]*src=[\'"]([^\'"]+)[\'"][^>]*>', re.IGNORECASE
27
- )
28
-
29
-
30
- # Load and sort replacement map from JSON
31
- try:
32
- replace_map_raw = REPLACE_WORD_MAP_PATH.read_text(encoding="utf-8")
33
- REPLACE_WORDS_MAP = json.loads(replace_map_raw)
34
- REPLACE_WORDS_MAP = dict(
35
- sorted(REPLACE_WORDS_MAP.items(), key=lambda x: len(x[0]), reverse=True)
36
- )
37
- except Exception as e:
38
- REPLACE_WORDS_MAP = {}
39
- logger.info(
40
- f"[epub] Failed to load REPLACE_WORDS_MAP from {REPLACE_WORD_MAP_PATH}: {e}"
41
- )
42
-
43
-
44
- def _check_and_correct_words(txt_str: str) -> str:
45
- """
46
- Perform word replacement using REPLACE_WORDS_MAP.
47
-
48
- :param txt_str: Raw string of text.
49
- :return: String with corrected words.
50
- """
51
- for k, v in REPLACE_WORDS_MAP.items():
52
- txt_str = txt_str.replace(k, v)
53
- return txt_str
54
-
55
-
56
- def chapter_txt_to_html(
57
- chapter_title: str,
58
- chapter_text: str,
59
- author_say: str,
60
- ) -> str:
61
- """
62
- Convert chapter text and author note to styled HTML.
63
-
64
- :param chapter_title: Title of the chapter.
65
- :param chapter_text: Main content of the chapter.
66
- :param author_say: Optional author note content.
67
- :return: Rendered HTML as a string.
68
- """
69
-
70
- def _render_lines(text: str) -> str:
71
- parts = []
72
- for line in text.strip().splitlines():
73
- line = line.strip()
74
- if not line:
75
- continue
76
-
77
- if (
78
- line.startswith("<img")
79
- and line.endswith("/>")
80
- or line.startswith('<div class="duokan-image-single illus">')
81
- and line.endswith("</div>")
82
- ):
83
- parts.append(line)
84
- else:
85
- corrected = _check_and_correct_words(line)
86
- if corrected != line:
87
- diff = diff_inline_display(line, corrected)
88
- logger.info("[epub] Correction diff:\n%s", diff)
89
- parts.append(f"<p>{corrected}</p>")
90
- return "\n".join(parts)
91
-
92
- html_parts = [f"<h2>{chapter_title}</h2>"]
93
- html_parts.append(_render_lines(chapter_text))
94
-
95
- if author_say.strip():
96
- html_parts.extend(["<hr />", "<p>作者说:</p>", _render_lines(author_say)])
97
-
98
- return "\n".join(html_parts)
99
-
100
-
101
- def inline_remote_images(
102
- content: str,
103
- image_dir: str | Path,
104
- ) -> str:
105
- """
106
- Download every remote `<img src="...">` in `content` into `image_dir`,
107
- and replace the original tag with EPUB_IMAGE_WRAPPER
108
- pointing to the local filename.
109
-
110
- :param content: HTML/text of the chapter containing <img> tags.
111
- :param image_dir: Directory to save downloaded images into.
112
- :return: Modified content with local image references.
113
- """
114
-
115
- def _replace(match: re.Match[str]) -> str:
116
- url = match.group(1)
117
- try:
118
- # download_image returns a Path or None
119
- local_path = download_image(
120
- url, image_dir, target_name=None, on_exist="skip"
121
- )
122
- if not local_path:
123
- logger.warning(
124
- "Failed to download image, leaving original tag: %s", url
125
- )
126
- return match.group(0)
127
-
128
- # wrap with the EPUB_IMAGE_WRAPPER, inserting just the filename
129
- return EPUB_IMAGE_WRAPPER.format(filename=local_path.name)
130
- except Exception:
131
- logger.exception("Error processing image URL: %s", url)
132
- return match.group(0)
133
-
134
- return _IMG_TAG_PATTERN.sub(_replace, content)
135
-
136
-
137
- def generate_book_intro_html(book_info: dict[str, Any]) -> str:
138
- """
139
- Generate HTML string for a book's information and summary.
140
-
141
- This function takes a dictionary containing book details and formats
142
- it into a styled HTML block, skipping any missing fields gracefully.
143
-
144
- :param book_info: A dictionary containing keys like 'book_name'...
145
-
146
- :return: An HTML-formatted string presenting the book's information.
147
- """
148
- book_name = book_info.get("book_name")
149
- author = book_info.get("author")
150
- serial_status = book_info.get("serial_status")
151
- word_count = book_info.get("word_count")
152
- summary = book_info.get("summary", "").strip()
153
-
154
- # Start composing the HTML output
155
- html_parts = ["<h1>书籍简介</h1>", '<div class="list">', "<ul>"]
156
-
157
- if book_name:
158
- html_parts.append(f"<li>书名: 《{book_name}》</li>")
159
- if author:
160
- html_parts.append(f"<li>作者: {author}</li>")
161
-
162
- if word_count:
163
- html_parts.append(f"<li>字数: {word_count}</li>")
164
- if serial_status:
165
- html_parts.append(f"<li>状态: {serial_status}</li>")
166
-
167
- html_parts.append("</ul>")
168
- html_parts.append("</div>")
169
- html_parts.append('<p class="new-page-after"><br/></p>')
170
-
171
- if summary:
172
- html_parts.append("<h2>简介</h2>")
173
- for paragraph in summary.split("\n"):
174
- paragraph = paragraph.strip()
175
- if paragraph:
176
- html_parts.append(f"<p>{paragraph}</p>")
177
-
178
- return "\n".join(html_parts)
@@ -1,60 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- novel_downloader.core.exporters.epub_utils.volume_intro
4
- -------------------------------------------------------
5
-
6
- Responsible for generating HTML code for volume introduction pages,
7
- including two style variants and a unified entry point.
8
- """
9
-
10
-
11
- from novel_downloader.utils.constants import EPUB_IMAGE_FOLDER
12
-
13
-
14
- def split_volume_title(volume_title: str) -> tuple[str, str]:
15
- """
16
- Split volume title into two parts for better display.
17
-
18
- :param volume_title: Original volume title string.
19
- :return: Tuple of (line1, line2)
20
- """
21
- if " " in volume_title:
22
- parts = volume_title.split(" ")
23
- elif "-" in volume_title:
24
- parts = volume_title.split("-")
25
- else:
26
- return "", volume_title
27
-
28
- return parts[0], "".join(parts[1:])
29
-
30
-
31
- def create_volume_intro(volume_title: str, volume_intro_text: str = "") -> str:
32
- """
33
- Generate the HTML snippet for a volume's introduction section.
34
-
35
- :param volume_title: Title of the volume.
36
- :param volume_intro_text: Optional introduction text for the volume.
37
- :return: HTML string representing the volume's intro section.
38
- """
39
- line1, line2 = split_volume_title(volume_title)
40
-
41
- def make_border_img(class_name: str) -> str:
42
- return (
43
- f'<div class="{class_name}">'
44
- f'<img alt="" class="{class_name}" '
45
- f'src="../{EPUB_IMAGE_FOLDER}/volume_border.png" />'
46
- f"</div>"
47
- )
48
-
49
- html_parts = [make_border_img("border1")]
50
-
51
- if line1:
52
- html_parts.append(f'<h1 class="volume-title-line1">{line1}</h1>')
53
-
54
- html_parts.append(f'<p class="volume-title-line2">{line2}</p>')
55
- html_parts.append(make_border_img("border2"))
56
-
57
- if volume_intro_text:
58
- html_parts.append(f'<p class="intro">{volume_intro_text}</p>')
59
-
60
- return "\n".join(html_parts)