txt2ebook 0.1.43__py3-none-any.whl → 0.1.45__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.
CHANGELOG.md CHANGED
@@ -7,6 +7,42 @@ and this project adheres to [0-based versioning](https://0ver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## v0.1.45 (2023-07-23)
11
+
12
+ ### Added
13
+
14
+ - Add `-of` or `--output-folder` to set a default `output` folder to prevent
15
+ clutter
16
+
17
+ ### Changed
18
+
19
+ - Rename and standardize extra `tox` environment names
20
+ - Refactor and optimize tokenizing metadata header of a source `txt` file
21
+ - Force all source text file to follow a YAML-inspired metadata header
22
+ - Parse and include translators into metadata of a book
23
+
24
+ ### Fixed
25
+
26
+ - Fix missing dashes in metadata header when exporting `txt` format
27
+ - Fix missing translator field in metadata for `md` and `gmi` format
28
+ - Refactor all toc markups for `md`, `gmi`, and `txt` format
29
+
30
+ ## v0.1.44 (2023-07-16)
31
+
32
+ ### Added
33
+
34
+ - Add test fixture with metadata
35
+
36
+ ### Changed
37
+
38
+ - Update `pyenv` installation steps in contributing doc
39
+ - Use active virtualenvs in `poetry` in contributing doc
40
+
41
+ ### Fixed
42
+
43
+ - Fix not using book title as output file name when input source from
44
+ redirection or piping
45
+
10
46
  ## v0.1.43 (2023-07-09)
11
47
 
12
48
  ### Added
CONTRIBUTING.md CHANGED
@@ -11,10 +11,19 @@ cd txt2ebook
11
11
 
12
12
  To set up different Python environments, we need to install all supported
13
13
  Python version using <https://github.com/pyenv/pyenv>. Once you've installed
14
- Pyenv, run the command below:
14
+ Pyenv, install these additional Pyenv plugins:
15
15
 
16
16
  ```console
17
+ git clone https://github.com/pyenv/pyenv-doctor.git "$(pyenv root)/plugins/pyenv-doctor"
18
+ pyenv doctor
19
+
20
+ git clone https://github.com/pyenv/pyenv-update.git $(pyenv root)/plugins/pyenv-update
17
21
  pyenv update
22
+ ```
23
+
24
+ Run the command below to install all Python versions:
25
+
26
+ ```console
18
27
  pyenv install $(cat .python-version)
19
28
  ```
20
29
 
@@ -24,6 +33,7 @@ Setting up development environment and install dependencies:
24
33
  python -m pip install --upgrade pip poetry
25
34
  poetry install
26
35
  poetry check
36
+ poetry config virtualenvs.prefer-active-python true
27
37
  ```
28
38
 
29
39
  Spawn a shell in virtual environment for your development:
@@ -38,15 +48,15 @@ Show all available tox tasks:
38
48
  $ tox -av
39
49
  ...
40
50
  default environments:
41
- py38 -> testing against python3.8
42
- py39 -> testing against python3.9
43
- py310 -> testing against python3.10
44
- py311 -> testing against python3.11
51
+ py38 -> testing against python3.8
52
+ py39 -> testing against python3.9
53
+ py310 -> testing against python3.10
54
+ py311 -> testing against python3.11
45
55
 
46
56
  additional environments:
47
- cover -> generate code coverage report in html
48
- doc -> generate sphinx documentation in html
49
- gettext -> update pot/po/mo files
57
+ cov -> generate code coverage report in html
58
+ doc -> generate sphinx documentation in html
59
+ pot -> update translations (pot/po/mo) files
50
60
  ```
51
61
 
52
62
  To run specific test:
txt2ebook/__init__.py CHANGED
@@ -21,7 +21,7 @@ import sys
21
21
 
22
22
  logger = logging.getLogger(__name__)
23
23
 
24
- __version__ = "0.1.43"
24
+ __version__ = "0.1.45"
25
25
 
26
26
 
27
27
  def setup_logger(config: argparse.Namespace) -> None:
txt2ebook/formats/base.py CHANGED
@@ -25,7 +25,7 @@ from pathlib import Path
25
25
  from typing import Any
26
26
 
27
27
  from txt2ebook.helpers import lower_underscore
28
- from txt2ebook.models import Book
28
+ from txt2ebook.models import Book, Chapter, Volume
29
29
 
30
30
  logger = logging.getLogger(__name__)
31
31
 
@@ -79,8 +79,6 @@ class BaseWriter(ABC):
79
79
  if self.config.filename_format:
80
80
  filename = self.book.filename_format(self.config.filename_format)
81
81
  else:
82
- filename = "default"
83
-
84
82
  if self.output_file:
85
83
  filename = str(self.output_file)
86
84
  elif isinstance(
@@ -88,14 +86,50 @@ class BaseWriter(ABC):
88
86
  ):
89
87
  if self.input_file.name != "<stdin>":
90
88
  filename = self.input_file.name
91
- elif self.book.title:
92
- filename = self.book.title
89
+ # input from redirection or piping
90
+ elif self.book.title:
91
+ filename = self.book.title
92
+ else:
93
+ filename = "default"
93
94
 
94
95
  file = Path(filename)
95
- return Path(file.parent, lower_underscore(file.stem)).with_suffix(
96
- extension
96
+
97
+ # do not create to output folder when we explicit set the output path
98
+ # and file
99
+ if self.config.output_file:
100
+ return Path(file.parent, lower_underscore(file.stem)).with_suffix(
101
+ extension
102
+ )
103
+
104
+ return Path(
105
+ file.parent, self.config.output_folder, lower_underscore(file.stem)
106
+ ).with_suffix(extension)
107
+
108
+ def _to_metadata_txt(self) -> str:
109
+ metadata = [
110
+ self._("title:") + self.book.title,
111
+ self._("author:") + ",".join(self.book.authors),
112
+ self._("translator:") + ",".join(self.book.translators),
113
+ self._("tag:") + ",".join(self.book.tags),
114
+ ]
115
+ return (
116
+ "---\n" + "\n".join(metadata) + "\n---" + self.paragraph_separator
97
117
  )
98
118
 
119
+ def _to_toc(self, list_symbol, header_symbol="") -> str:
120
+ toc = ""
121
+ toc += header_symbol + self._("toc") + "\n"
122
+
123
+ for section in self.book.toc:
124
+ if isinstance(section, Volume):
125
+ toc += f"\n{list_symbol} " + section.title
126
+ for chapter in section.chapters:
127
+ toc += f"\n {list_symbol} " + chapter.title
128
+ if isinstance(section, Chapter):
129
+ toc += f"\n{list_symbol} " + section.title
130
+
131
+ return toc + self.paragraph_separator
132
+
99
133
  @abstractmethod
100
134
  def write(self) -> None:
101
135
  """Generate text files."""
txt2ebook/formats/epub.py CHANGED
@@ -94,7 +94,7 @@ class EpubWriter(BaseWriter):
94
94
  output_filename = self._output_filename(".epub")
95
95
  output_filename.parent.mkdir(parents=True, exist_ok=True)
96
96
  epub.write_epub(output_filename, book, {})
97
- logger.info("Generate EPUB file: %s", output_filename)
97
+ logger.info("Generate EPUB file: %s", output_filename.resolve())
98
98
 
99
99
  def _build_nav(self, book: epub.EpubBook) -> None:
100
100
  book.add_item(epub.EpubNcx())
txt2ebook/formats/gmi.py CHANGED
@@ -62,7 +62,7 @@ class GmiWriter(BaseWriter):
62
62
  )
63
63
  logger.info("Creating %s", export_filename)
64
64
  with open(export_filename, "w", encoding="utf8") as file:
65
- file.write(self._to_toc())
65
+ file.write(self._to_toc("*", "# "))
66
66
 
67
67
  sc_seq = 2
68
68
 
@@ -114,38 +114,16 @@ class GmiWriter(BaseWriter):
114
114
 
115
115
  def _new_file(self) -> None:
116
116
  new_filename = self._output_filename(".gmi")
117
+ new_filename.parent.mkdir(parents=True, exist_ok=True)
118
+
117
119
  with open(new_filename, "w", encoding="utf8") as file:
118
120
  logger.info("Generate GemText file: %s", new_filename.resolve())
119
121
  file.write(self._to_md())
120
122
 
121
123
  def _to_md(self) -> str:
122
- toc = self._to_toc() if self.with_toc else ""
124
+ toc = self._to_toc("*", "# ") if self.with_toc else ""
123
125
  return self._to_metadata_txt() + toc + self._to_body_txt()
124
126
 
125
- def _to_toc(self) -> str:
126
- toc = []
127
- toc.append("#" + self._("toc") + "\n")
128
-
129
- for section in self.book.toc:
130
- if isinstance(section, Volume):
131
- toc.append(section.title)
132
- for chapter in section.chapters:
133
- toc.append(chapter.title)
134
- if isinstance(section, Chapter):
135
- toc.append(section.title)
136
-
137
- return "\n* ".join(toc) + self.paragraph_separator
138
-
139
- def _to_metadata_txt(self) -> str:
140
- metadata = [
141
- self._("title:") + self.book.title,
142
- self._("author:") + ",".join(self.book.authors),
143
- self._("tag:") + ",".join(self.book.tags),
144
- ]
145
- return (
146
- "---\n" + "\n".join(metadata) + "\n---" + self.paragraph_separator
147
- )
148
-
149
127
  def _to_body_txt(self) -> str:
150
128
  content = []
151
129
  for section in self.book.toc:
txt2ebook/formats/md.py CHANGED
@@ -61,7 +61,7 @@ class MdWriter(BaseWriter):
61
61
  )
62
62
  logger.info("Creating %s", export_filename)
63
63
  with open(export_filename, "w", encoding="utf8") as file:
64
- file.write(self._to_toc())
64
+ file.write(self._to_toc("-", "# "))
65
65
 
66
66
  sc_seq = 2
67
67
 
@@ -113,38 +113,16 @@ class MdWriter(BaseWriter):
113
113
 
114
114
  def _new_file(self) -> None:
115
115
  new_filename = self._output_filename(".md")
116
+ new_filename.parent.mkdir(parents=True, exist_ok=True)
117
+
116
118
  with open(new_filename, "w", encoding="utf8") as file:
117
119
  logger.info("Generate Markdown file: %s", new_filename.resolve())
118
120
  file.write(self._to_md())
119
121
 
120
122
  def _to_md(self) -> str:
121
- toc = self._to_toc() if self.with_toc else ""
123
+ toc = self._to_toc("-", "# ") if self.with_toc else ""
122
124
  return self._to_metadata_txt() + toc + self._to_body_txt()
123
125
 
124
- def _to_toc(self) -> str:
125
- toc = []
126
- toc.append(self._("toc") + "\n")
127
-
128
- for section in self.book.toc:
129
- if isinstance(section, Volume):
130
- toc.append(section.title)
131
- for chapter in section.chapters:
132
- toc.append(chapter.title)
133
- if isinstance(section, Chapter):
134
- toc.append(section.title)
135
-
136
- return "\n- ".join(toc) + self.paragraph_separator
137
-
138
- def _to_metadata_txt(self) -> str:
139
- metadata = [
140
- self._("title:") + self.book.title,
141
- self._("author:") + ",".join(self.book.authors),
142
- self._("tag:") + ",".join(self.book.tags),
143
- ]
144
- return (
145
- "---\n" + "\n".join(metadata) + "\n---" + self.paragraph_separator
146
- )
147
-
148
126
  def _to_body_txt(self) -> str:
149
127
  content = []
150
128
  for section in self.book.toc:
txt2ebook/formats/txt.py CHANGED
@@ -71,7 +71,7 @@ class TxtWriter(BaseWriter):
71
71
  )
72
72
  logger.info("Creating %s", export_filename)
73
73
  with open(export_filename, "w", encoding="utf8") as file:
74
- file.write(self._to_toc())
74
+ file.write(self._to_toc("-"))
75
75
 
76
76
  sc_seq = 2
77
77
 
@@ -135,6 +135,8 @@ class TxtWriter(BaseWriter):
135
135
  )
136
136
  )
137
137
 
138
+ new_filename.parent.mkdir(parents=True, exist_ok=True)
139
+
138
140
  with open(new_filename, "w", encoding="utf8") as file:
139
141
  file.write(self._to_txt())
140
142
  logger.info("Generate TXT file: %s", new_filename.resolve())
@@ -147,32 +149,9 @@ class TxtWriter(BaseWriter):
147
149
  logger.info("Overwrite txt file: %s", txt_filename.resolve())
148
150
 
149
151
  def _to_txt(self) -> str:
150
- toc = self._to_toc() if self.with_toc else ""
152
+ toc = self._to_toc("-") if self.with_toc else ""
151
153
  return self._to_metadata_txt() + toc + self._to_body_txt()
152
154
 
153
- def _to_toc(self) -> str:
154
- toc = []
155
- toc.append(self._("toc") + "\n")
156
-
157
- for section in self.book.toc:
158
- if isinstance(section, Volume):
159
- toc.append(section.title)
160
- for chapter in section.chapters:
161
- toc.append(chapter.title)
162
- if isinstance(section, Chapter):
163
- toc.append(section.title)
164
-
165
- return "\n- ".join(toc) + self.paragraph_separator
166
-
167
- def _to_metadata_txt(self) -> str:
168
- metadata = [
169
- self._("title:") + self.book.title,
170
- self._("author:") + ",".join(self.book.authors),
171
- self._("translator:") + ",".join(self.book.translators),
172
- self._("tag:") + ",".join(self.book.tags),
173
- ]
174
- return "\n".join(metadata) + self.paragraph_separator
175
-
176
155
  def _to_body_txt(self) -> str:
177
156
  content = []
178
157
  for section in self.book.toc:
@@ -52,6 +52,7 @@ RE_CHAPTERS = [
52
52
  DEFAULT_RE_TITLE = r"书名:(.*)"
53
53
  DEFAULT_RE_AUTHOR = r"作者:(.*)"
54
54
  DEFAULT_RE_TAG = r"标签:(.*)"
55
+ DEFAULT_RE_TRANSLATOR = r"翻译:(.*)"
55
56
  DEFAULT_RE_VOLUME = "(" + "|".join(RE_VOLUMES) + ")"
56
57
  DEFAULT_RE_CHAPTER = "(" + "|".join(RE_CHAPTERS) + ")"
57
58
 
@@ -5,7 +5,7 @@
5
5
  msgid ""
6
6
  msgstr ""
7
7
  "Project-Id-Version: \n"
8
- "POT-Creation-Date: 2023-07-09 17:44+0800\n"
8
+ "POT-Creation-Date: 2023-07-22 20:57+0800\n"
9
9
  "PO-Revision-Date: 2023-06-17 00:06+0800\n"
10
10
  "Last-Translator: \n"
11
11
  "Language-Team: \n"
@@ -16,37 +16,33 @@ msgstr ""
16
16
  "Generated-By: pygettext.py 1.5\n"
17
17
  "X-Generator: Poedit 3.2.2\n"
18
18
 
19
- #: src/txt2ebook/formats/epub.py:142
20
- msgid "cover"
21
- msgstr "Cover"
22
-
23
- #: src/txt2ebook/formats/gmi.py:46 src/txt2ebook/formats/md.py:45
24
- #: src/txt2ebook/formats/txt.py:55
25
- msgid "metadata"
26
- msgstr "Metadata"
27
-
28
- #: src/txt2ebook/formats/gmi.py:59 src/txt2ebook/formats/gmi.py:127
29
- #: src/txt2ebook/formats/md.py:58 src/txt2ebook/formats/md.py:126
30
- #: src/txt2ebook/formats/pdf.py:126 src/txt2ebook/formats/txt.py:68
31
- #: src/txt2ebook/formats/txt.py:155
32
- msgid "toc"
33
- msgstr "Table of Content"
34
-
35
- #: src/txt2ebook/formats/gmi.py:141 src/txt2ebook/formats/md.py:140
36
- #: src/txt2ebook/formats/txt.py:169
19
+ #: src/txt2ebook/formats/base.py:110 src/txt2ebook/formats/md.py:128
37
20
  msgid "title:"
38
21
  msgstr "Title:"
39
22
 
40
- #: src/txt2ebook/formats/gmi.py:142 src/txt2ebook/formats/md.py:141
41
- #: src/txt2ebook/formats/txt.py:170
23
+ #: src/txt2ebook/formats/base.py:111 src/txt2ebook/formats/md.py:129
42
24
  msgid "author:"
43
25
  msgstr "Author:"
44
26
 
45
- #: src/txt2ebook/formats/gmi.py:143 src/txt2ebook/formats/md.py:142
46
- #: src/txt2ebook/formats/txt.py:172
27
+ #: src/txt2ebook/formats/base.py:112 src/txt2ebook/formats/md.py:130
28
+ msgid "translator:"
29
+ msgstr "Translator:"
30
+
31
+ #: src/txt2ebook/formats/base.py:113 src/txt2ebook/formats/md.py:131
47
32
  msgid "tag:"
48
33
  msgstr "Tag:"
49
34
 
50
- #: src/txt2ebook/formats/txt.py:171
51
- msgid "translator:"
52
- msgstr "Translator:"
35
+ #: src/txt2ebook/formats/base.py:121 src/txt2ebook/formats/gmi.py:59
36
+ #: src/txt2ebook/formats/md.py:58 src/txt2ebook/formats/pdf.py:126
37
+ #: src/txt2ebook/formats/txt.py:68
38
+ msgid "toc"
39
+ msgstr "Table of Content"
40
+
41
+ #: src/txt2ebook/formats/epub.py:142
42
+ msgid "cover"
43
+ msgstr "Cover"
44
+
45
+ #: src/txt2ebook/formats/gmi.py:46 src/txt2ebook/formats/md.py:45
46
+ #: src/txt2ebook/formats/txt.py:55
47
+ msgid "metadata"
48
+ msgstr "Metadata"
@@ -5,7 +5,7 @@
5
5
  msgid ""
6
6
  msgstr ""
7
7
  "Project-Id-Version: PACKAGE VERSION\n"
8
- "POT-Creation-Date: 2023-07-09 17:44+0800\n"
8
+ "POT-Creation-Date: 2023-07-22 20:57+0800\n"
9
9
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
10
10
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11
11
  "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -15,38 +15,34 @@ msgstr ""
15
15
  "Generated-By: pygettext.py 1.5\n"
16
16
 
17
17
 
18
- #: src/txt2ebook/formats/epub.py:142
19
- msgid "cover"
18
+ #: src/txt2ebook/formats/base.py:110 src/txt2ebook/formats/md.py:128
19
+ msgid "title:"
20
20
  msgstr ""
21
21
 
22
- #: src/txt2ebook/formats/gmi.py:46 src/txt2ebook/formats/md.py:45
23
- #: src/txt2ebook/formats/txt.py:55
24
- msgid "metadata"
22
+ #: src/txt2ebook/formats/base.py:111 src/txt2ebook/formats/md.py:129
23
+ msgid "author:"
25
24
  msgstr ""
26
25
 
27
- #: src/txt2ebook/formats/gmi.py:59 src/txt2ebook/formats/gmi.py:127
28
- #: src/txt2ebook/formats/md.py:58 src/txt2ebook/formats/md.py:126
29
- #: src/txt2ebook/formats/pdf.py:126 src/txt2ebook/formats/txt.py:68
30
- #: src/txt2ebook/formats/txt.py:155
31
- msgid "toc"
26
+ #: src/txt2ebook/formats/base.py:112 src/txt2ebook/formats/md.py:130
27
+ msgid "translator:"
32
28
  msgstr ""
33
29
 
34
- #: src/txt2ebook/formats/gmi.py:141 src/txt2ebook/formats/md.py:140
35
- #: src/txt2ebook/formats/txt.py:169
36
- msgid "title:"
30
+ #: src/txt2ebook/formats/base.py:113 src/txt2ebook/formats/md.py:131
31
+ msgid "tag:"
37
32
  msgstr ""
38
33
 
39
- #: src/txt2ebook/formats/gmi.py:142 src/txt2ebook/formats/md.py:141
40
- #: src/txt2ebook/formats/txt.py:170
41
- msgid "author:"
34
+ #: src/txt2ebook/formats/base.py:121 src/txt2ebook/formats/gmi.py:59
35
+ #: src/txt2ebook/formats/md.py:58 src/txt2ebook/formats/pdf.py:126
36
+ #: src/txt2ebook/formats/txt.py:68
37
+ msgid "toc"
42
38
  msgstr ""
43
39
 
44
- #: src/txt2ebook/formats/gmi.py:143 src/txt2ebook/formats/md.py:142
45
- #: src/txt2ebook/formats/txt.py:172
46
- msgid "tag:"
40
+ #: src/txt2ebook/formats/epub.py:142
41
+ msgid "cover"
47
42
  msgstr ""
48
43
 
49
- #: src/txt2ebook/formats/txt.py:171
50
- msgid "translator:"
44
+ #: src/txt2ebook/formats/gmi.py:46 src/txt2ebook/formats/md.py:45
45
+ #: src/txt2ebook/formats/txt.py:55
46
+ msgid "metadata"
51
47
  msgstr ""
52
48
 
@@ -5,7 +5,7 @@
5
5
  msgid ""
6
6
  msgstr ""
7
7
  "Project-Id-Version: \n"
8
- "POT-Creation-Date: 2023-07-09 17:44+0800\n"
8
+ "POT-Creation-Date: 2023-07-22 20:57+0800\n"
9
9
  "PO-Revision-Date: 2023-06-17 00:04+0800\n"
10
10
  "Last-Translator: \n"
11
11
  "Language-Team: \n"
@@ -16,37 +16,33 @@ msgstr ""
16
16
  "Generated-By: pygettext.py 1.5\n"
17
17
  "X-Generator: Poedit 3.2.2\n"
18
18
 
19
- #: src/txt2ebook/formats/epub.py:142
20
- msgid "cover"
21
- msgstr "封面"
22
-
23
- #: src/txt2ebook/formats/gmi.py:46 src/txt2ebook/formats/md.py:45
24
- #: src/txt2ebook/formats/txt.py:55
25
- msgid "metadata"
26
- msgstr "元数据"
27
-
28
- #: src/txt2ebook/formats/gmi.py:59 src/txt2ebook/formats/gmi.py:127
29
- #: src/txt2ebook/formats/md.py:58 src/txt2ebook/formats/md.py:126
30
- #: src/txt2ebook/formats/pdf.py:126 src/txt2ebook/formats/txt.py:68
31
- #: src/txt2ebook/formats/txt.py:155
32
- msgid "toc"
33
- msgstr "目录"
34
-
35
- #: src/txt2ebook/formats/gmi.py:141 src/txt2ebook/formats/md.py:140
36
- #: src/txt2ebook/formats/txt.py:169
19
+ #: src/txt2ebook/formats/base.py:110 src/txt2ebook/formats/md.py:128
37
20
  msgid "title:"
38
21
  msgstr "书名:"
39
22
 
40
- #: src/txt2ebook/formats/gmi.py:142 src/txt2ebook/formats/md.py:141
41
- #: src/txt2ebook/formats/txt.py:170
23
+ #: src/txt2ebook/formats/base.py:111 src/txt2ebook/formats/md.py:129
42
24
  msgid "author:"
43
25
  msgstr "作者:"
44
26
 
45
- #: src/txt2ebook/formats/gmi.py:143 src/txt2ebook/formats/md.py:142
46
- #: src/txt2ebook/formats/txt.py:172
27
+ #: src/txt2ebook/formats/base.py:112 src/txt2ebook/formats/md.py:130
28
+ msgid "translator:"
29
+ msgstr "翻译:"
30
+
31
+ #: src/txt2ebook/formats/base.py:113 src/txt2ebook/formats/md.py:131
47
32
  msgid "tag:"
48
33
  msgstr "票签:"
49
34
 
50
- #: src/txt2ebook/formats/txt.py:171
51
- msgid "translator:"
52
- msgstr "翻译:"
35
+ #: src/txt2ebook/formats/base.py:121 src/txt2ebook/formats/gmi.py:59
36
+ #: src/txt2ebook/formats/md.py:58 src/txt2ebook/formats/pdf.py:126
37
+ #: src/txt2ebook/formats/txt.py:68
38
+ msgid "toc"
39
+ msgstr "目录"
40
+
41
+ #: src/txt2ebook/formats/epub.py:142
42
+ msgid "cover"
43
+ msgstr "封面"
44
+
45
+ #: src/txt2ebook/formats/gmi.py:46 src/txt2ebook/formats/md.py:45
46
+ #: src/txt2ebook/formats/txt.py:55
47
+ msgid "metadata"
48
+ msgstr "元数据"
@@ -1,7 +1,7 @@
1
1
  msgid ""
2
2
  msgstr ""
3
3
  "Project-Id-Version: \n"
4
- "POT-Creation-Date: 2023-07-09 17:44+0800\n"
4
+ "POT-Creation-Date: 2023-07-22 20:57+0800\n"
5
5
  "PO-Revision-Date: \n"
6
6
  "Last-Translator: \n"
7
7
  "Language-Team: \n"
@@ -11,37 +11,33 @@ msgstr ""
11
11
  "Content-Transfer-Encoding: 8bit\n"
12
12
  "X-Generator: Poedit 3.2.2\n"
13
13
 
14
- #: src/txt2ebook/formats/epub.py:142
15
- msgid "cover"
16
- msgstr "封面"
17
-
18
- #: src/txt2ebook/formats/gmi.py:46 src/txt2ebook/formats/md.py:45
19
- #: src/txt2ebook/formats/txt.py:55
20
- msgid "metadata"
21
- msgstr "元数据"
22
-
23
- #: src/txt2ebook/formats/gmi.py:59 src/txt2ebook/formats/gmi.py:127
24
- #: src/txt2ebook/formats/md.py:58 src/txt2ebook/formats/md.py:126
25
- #: src/txt2ebook/formats/pdf.py:126 src/txt2ebook/formats/txt.py:68
26
- #: src/txt2ebook/formats/txt.py:155
27
- msgid "toc"
28
- msgstr "目录"
29
-
30
- #: src/txt2ebook/formats/gmi.py:141 src/txt2ebook/formats/md.py:140
31
- #: src/txt2ebook/formats/txt.py:169
14
+ #: src/txt2ebook/formats/base.py:110 src/txt2ebook/formats/md.py:128
32
15
  msgid "title:"
33
16
  msgstr "书名:"
34
17
 
35
- #: src/txt2ebook/formats/gmi.py:142 src/txt2ebook/formats/md.py:141
36
- #: src/txt2ebook/formats/txt.py:170
18
+ #: src/txt2ebook/formats/base.py:111 src/txt2ebook/formats/md.py:129
37
19
  msgid "author:"
38
20
  msgstr "作者:"
39
21
 
40
- #: src/txt2ebook/formats/gmi.py:143 src/txt2ebook/formats/md.py:142
41
- #: src/txt2ebook/formats/txt.py:172
22
+ #: src/txt2ebook/formats/base.py:112 src/txt2ebook/formats/md.py:130
23
+ msgid "translator:"
24
+ msgstr "翻译:"
25
+
26
+ #: src/txt2ebook/formats/base.py:113 src/txt2ebook/formats/md.py:131
42
27
  msgid "tag:"
43
28
  msgstr "标签:"
44
29
 
45
- #: src/txt2ebook/formats/txt.py:171
46
- msgid "translator:"
47
- msgstr "翻译:"
30
+ #: src/txt2ebook/formats/base.py:121 src/txt2ebook/formats/gmi.py:59
31
+ #: src/txt2ebook/formats/md.py:58 src/txt2ebook/formats/pdf.py:126
32
+ #: src/txt2ebook/formats/txt.py:68
33
+ msgid "toc"
34
+ msgstr "目录"
35
+
36
+ #: src/txt2ebook/formats/epub.py:142
37
+ msgid "cover"
38
+ msgstr "封面"
39
+
40
+ #: src/txt2ebook/formats/gmi.py:46 src/txt2ebook/formats/md.py:45
41
+ #: src/txt2ebook/formats/txt.py:55
42
+ msgid "metadata"
43
+ msgstr "元数据"
txt2ebook/parser.py CHANGED
@@ -141,6 +141,7 @@ class Parser:
141
141
  book_title = ""
142
142
  authors = []
143
143
  tags = []
144
+ translators = []
144
145
  current_volume = Volume("")
145
146
  current_chapter = Chapter("")
146
147
 
@@ -177,6 +178,9 @@ class Parser:
177
178
  if token.type == "TAG":
178
179
  tags.append(token.value)
179
180
 
181
+ if token.type == "TRANSLATOR":
182
+ translators.append(token.value)
183
+
180
184
  if token.type == "VOLUME_CHAPTER":
181
185
  [volume, chapter] = token.value
182
186
 
@@ -230,6 +234,7 @@ class Parser:
230
234
  logger.info("Found or set book title: %s", book_title)
231
235
  logger.info("Found or set authors: %s", repr(authors))
232
236
  logger.info("Found or set categories: %s", repr(tags))
237
+ logger.info("Found or set translators: %s", repr(translators))
233
238
 
234
239
  return (book_title, authors, tags, toc)
235
240
 
@@ -332,7 +337,7 @@ class Parser:
332
337
  Returns:
333
338
  str: The formatted book content.
334
339
  """
335
- for delete_line_regex in self.get_regex("delete_line"):
340
+ for delete_line_regex in self.config.re_delete_line:
336
341
  content = re.sub(
337
342
  re.compile(rf"^.*{delete_line_regex}.*$", re.MULTILINE),
338
343
  "",
txt2ebook/tokenizer.py CHANGED
@@ -53,6 +53,7 @@ class Tokenizer:
53
53
  """Tokenizer class to parse text content."""
54
54
 
55
55
  raw_content: str = field(repr=False)
56
+ metadata_marker: str = field(repr=False)
56
57
  config: argparse.Namespace = field(repr=False)
57
58
  tokens: List[Token] = field(default_factory=List, repr=False)
58
59
  lineno_lookup: Dict = field(default_factory=Dict, repr=False)
@@ -62,6 +63,11 @@ class Tokenizer:
62
63
  self.raw_content = raw_content
63
64
  self.config = config
64
65
 
66
+ if self.config.fullwidth:
67
+ self.metadata_marker = "---"
68
+ else:
69
+ self.metadata_marker = "---"
70
+
65
71
  config_lang = config.language.replace("-", "_")
66
72
  self.langconf = import_module(f"txt2ebook.languages.{config_lang}")
67
73
 
@@ -71,29 +77,8 @@ class Tokenizer:
71
77
  lineno_lookup[line[:10]] = lineno
72
78
  self.lineno_lookup = lineno_lookup
73
79
 
74
- self.tokens = self.parse()
75
-
76
- def __getattr__(self, key: str) -> Any:
77
- """Get a value of the config based on key name.
78
-
79
- This function is called when an attribute is not found on an object
80
- instance or not set in `__init__` function.
81
-
82
- Args:
83
- key(str): The key attribute name of the config, language config,
84
- and current class.
85
-
86
- Returns:
87
- Any: The value of a key, if found. Otherwise raise AttributeError
88
- exception.
89
- """
90
- if hasattr(self.config, key):
91
- return getattr(self.config, key)
92
-
93
- if hasattr(self.langconf, key):
94
- return getattr(self.langconf, key)
95
-
96
- raise AttributeError(f"invalid config key: '{key}'!")
80
+ self.tokens = []
81
+ self.parse()
97
82
 
98
83
  def __repr__(self) -> str:
99
84
  """Return the string representation of Tokenizer for debugging purpose.
@@ -106,27 +91,14 @@ class Tokenizer:
106
91
  self.__class__.__name__, self.raw_content[:5], self.stats()
107
92
  )
108
93
 
109
- def parse(self) -> List:
94
+ def parse(self) -> None:
110
95
  """Parse the content into tokens.
111
96
 
112
97
  Returns:
113
- List: The list of tokens.
98
+ None
114
99
  """
115
- content = self.raw_content.rstrip(self.paragraph_separator)
116
- lines = content.split(self.paragraph_separator)
117
-
118
- if len(lines) <= 1:
119
- msg = (
120
- f"Cannot split content by {repr(self.paragraph_separator)}. "
121
- "Check if content have newline with spaces."
122
- )
123
- log_or_raise_on_warning(msg, self.config.raise_on_warning)
124
-
125
- tokens: List[Token] = []
126
- for line in lines:
127
- self._tokenize_line(line, tokens)
128
-
129
- return tokens
100
+ self._tokenize_metadata()
101
+ self._tokenize_content()
130
102
 
131
103
  def stats(self) -> Counter:
132
104
  """Returns the statistics count for the parsed tokens.
@@ -138,68 +110,100 @@ class Tokenizer:
138
110
  logger.debug("Token stats: %s", repr(stats))
139
111
  return stats
140
112
 
141
- def _tokenize_line(self, line: str, tokens: List) -> None:
113
+ def _tokenize_line(self, line: str) -> None:
142
114
  """Tokenize each line after we split by paragraph separator."""
143
- _ = (
144
- self._tokenize_header(line, tokens)
145
- or self._tokenize_metadata(line, tokens)
146
- or self._tokenize_paragraph(line, tokens)
147
- )
148
-
149
- def _tokenize_metadata(self, line: str, tokens: List) -> bool:
150
- """Tokenize the metadata of the book.
115
+ _ = self._tokenize_header(line) or self._tokenize_paragraph(line)
116
+
117
+ def _tokenize_metadata(self) -> None:
118
+ """Tokenize the metadata of the book."""
119
+ for line in self._extract_metadata():
120
+ re_title = f"^{self.langconf.DEFAULT_RE_TITLE}"
121
+ if self.config.re_title:
122
+ re_title = self.config.re_title[0]
123
+
124
+ re_author = f"{self.langconf.DEFAULT_RE_AUTHOR}"
125
+ if self.config.re_author:
126
+ re_author = self.config.re_author[0]
127
+
128
+ token_type_regex_map = [
129
+ ("TITLE", re_title),
130
+ ("AUTHOR", re_author),
131
+ ("TAG", f"{self.langconf.DEFAULT_RE_TAG}"),
132
+ ("TRANSLATOR", f"{self.langconf.DEFAULT_RE_TRANSLATOR}"),
133
+ ]
134
+
135
+ token = None
136
+ for token_type, regex in token_type_regex_map:
137
+ match = re.search(regex, line)
138
+ if match:
139
+ token_value = match.group(1).strip()
140
+ token = Token(
141
+ token_type, token_value, self._lineno(token_value)
142
+ )
143
+ self.tokens.append(token)
144
+
145
+ def _extract_metadata(self) -> List:
146
+ """Extract YAML-inspired metadata header from file context.
147
+
148
+ Metadata header with line number as follows:
149
+
150
+ 1 ---
151
+ 2 书名:
152
+ 3 作者:
153
+ 4 标签:
154
+ 5 翻译:
155
+ 6 ---
151
156
 
152
- Metadata at the top of the file was grouped and separate as single
153
- newline. By default, the content, or paragraph was separated by two
154
- newlines. Hence, we have to group it into one token.
155
-
156
- Also, we can split the metadata line as these lines can also contains
157
- chapter content, which can also contains newlines.
158
157
  """
159
- re_title = f"^{self.DEFAULT_RE_TITLE}"
160
- if self.config.re_title:
161
- re_title = self.config.re_title[0]
158
+ match = re.search(
159
+ rf"^(?:{self.metadata_marker})\n(.*)\n(?:{self.metadata_marker})$",
160
+ self.raw_content,
161
+ re.MULTILINE | re.DOTALL,
162
+ )
163
+ if not match:
164
+ msg = "Missing or invalid metadata."
165
+ log_or_raise_on_warning(msg, self.config.raise_on_warning)
166
+ return []
162
167
 
163
- re_author = f"\n{self.DEFAULT_RE_AUTHOR}"
164
- if self.config.re_author:
165
- re_author = self.config.re_author[0]
168
+ metadata = match[1].split("\n")
169
+ logger.debug("Metadata: %s", metadata)
170
+ return metadata
166
171
 
167
- token_type_regex_map = [
168
- ("TITLE", re_title),
169
- ("AUTHOR", re_author),
170
- ("TAG", f"\n{self.DEFAULT_RE_TAG}"),
171
- ]
172
+ def _tokenize_content(self) -> None:
173
+ content = self.raw_content.split(f"{self.metadata_marker}\n\n")[1]
174
+ content = content.strip(self.config.paragraph_separator)
175
+ lines = content.split(self.config.paragraph_separator)
172
176
 
173
- token = None
174
- for token_type, regex in token_type_regex_map:
175
- match = re.search(regex, line)
176
- if match:
177
- token_value = match.group(1).strip()
178
- token = Token(
179
- token_type, token_value, self._lineno(token_value)
180
- )
181
- tokens.append(token)
177
+ if len(lines) <= 1:
178
+ msg = (
179
+ "Cannot split content by "
180
+ f"{repr(self.config.paragraph_separator)}. "
181
+ "Check if content have newline with spaces."
182
+ )
183
+ log_or_raise_on_warning(msg, self.config.raise_on_warning)
182
184
 
183
- return bool(token)
185
+ for line in lines:
186
+ self._tokenize_line(line)
184
187
 
185
- def _tokenize_header(self, line: str, tokens: List) -> bool:
188
+ def _tokenize_header(self, line: str) -> bool:
186
189
  """Tokenize section headers.
187
190
 
188
191
  Note that we parse in such sequence: chapter, volume, volume_chapter to
189
192
  prevent unnecessary calls as we've more chapters than volumes.
190
193
  """
191
194
  return (
192
- self._tokenize_chapter(line, tokens)
193
- or self._tokenize_volume_chapter(line, tokens)
194
- or self._tokenize_volume(line, tokens)
195
+ self._tokenize_chapter(line)
196
+ or self._tokenize_volume_chapter(line)
197
+ or self._tokenize_volume(line)
195
198
  )
196
199
 
197
- def _tokenize_volume_chapter(self, line: str, tokens: List) -> bool:
200
+ def _tokenize_volume_chapter(self, line: str) -> bool:
198
201
  line = self._validate_section_header("volume chapter", line)
199
202
  token = None
200
203
 
201
204
  re_volume_chapter = (
202
- rf"^{self.DEFAULT_RE_VOLUME}\s*{self.DEFAULT_RE_CHAPTER}"
205
+ rf"^{self.langconf.DEFAULT_RE_VOLUME}\s*"
206
+ rf"{self.langconf.DEFAULT_RE_CHAPTER}"
203
207
  )
204
208
  if self.config.re_volume_chapter:
205
209
  re_volume_chapter = self.config.re_volume_chapter[0]
@@ -215,15 +219,15 @@ class Tokenizer:
215
219
  Token("CHAPTER", chapter, self._lineno(chapter)),
216
220
  ],
217
221
  )
218
- tokens.append(token)
222
+ self.tokens.append(token)
219
223
 
220
224
  return bool(token)
221
225
 
222
- def _tokenize_volume(self, line: str, tokens: List) -> bool:
226
+ def _tokenize_volume(self, line: str) -> bool:
223
227
  line = self._validate_section_header("volume", line)
224
228
  token = None
225
229
 
226
- re_volume = rf"^{self.DEFAULT_RE_VOLUME}$"
230
+ re_volume = rf"^{self.langconf.DEFAULT_RE_VOLUME}$"
227
231
  if self.config.re_volume:
228
232
  re_volume = "(" + "|".join(self.config.re_volume) + ")"
229
233
 
@@ -231,15 +235,15 @@ class Tokenizer:
231
235
  if match:
232
236
  volume = match.group(1).strip()
233
237
  token = Token("VOLUME", volume, self._lineno(volume))
234
- tokens.append(token)
238
+ self.tokens.append(token)
235
239
 
236
240
  return bool(token)
237
241
 
238
- def _tokenize_chapter(self, line: str, tokens: List) -> bool:
242
+ def _tokenize_chapter(self, line: str) -> bool:
239
243
  line = self._validate_section_header("chapter", line)
240
244
  token = None
241
245
 
242
- re_chapter = rf"^{self.DEFAULT_RE_CHAPTER}$"
246
+ re_chapter = rf"^{self.langconf.DEFAULT_RE_CHAPTER}$"
243
247
  if self.config.re_chapter:
244
248
  re_chapter = "(" + "|".join(self.config.re_chapter) + ")"
245
249
 
@@ -247,12 +251,12 @@ class Tokenizer:
247
251
  if match:
248
252
  chapter = match.group(1).strip()
249
253
  token = Token("CHAPTER", chapter, self._lineno(chapter))
250
- tokens.append(token)
254
+ self.tokens.append(token)
251
255
 
252
256
  return bool(token)
253
257
 
254
- def _tokenize_paragraph(self, line: str, tokens: List) -> bool:
255
- tokens.append(Token("PARAGRAPH", line, self._lineno(line)))
258
+ def _tokenize_paragraph(self, line: str) -> bool:
259
+ self.tokens.append(Token("PARAGRAPH", line, self._lineno(line)))
256
260
  return True
257
261
 
258
262
  def _validate_section_header(self, header_type: str, line: str) -> str:
txt2ebook/txt2ebook.py CHANGED
@@ -114,6 +114,14 @@ def build_parser(
114
114
  metavar="EBOOK_FILENAME",
115
115
  )
116
116
 
117
+ parser.add_argument(
118
+ "-of",
119
+ "--output-folder",
120
+ dest="output_folder",
121
+ default="output",
122
+ help="set default output folder (default: '%(default)s')",
123
+ )
124
+
117
125
  parser.add_argument(
118
126
  "-f",
119
127
  "--format",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: txt2ebook
3
- Version: 0.1.43
3
+ Version: 0.1.45
4
4
  Summary: CLI tool to convert txt file to ebook format
5
5
  Home-page: https://github.com/kianmeng/txt2ebook
6
6
  License: AGPL-3.0-or-later
@@ -20,10 +20,6 @@ Classifier: Programming Language :: Python :: 3.9
20
20
  Classifier: Programming Language :: Python :: 3.10
21
21
  Classifier: Programming Language :: Python :: 3.11
22
22
  Classifier: Programming Language :: Python :: 3 :: Only
23
- Classifier: Programming Language :: Python :: 3.10
24
- Classifier: Programming Language :: Python :: 3.11
25
- Classifier: Programming Language :: Python :: 3.8
26
- Classifier: Programming Language :: Python :: 3.9
27
23
  Classifier: Topic :: Text Processing
28
24
  Classifier: Topic :: Text Processing :: Filters
29
25
  Classifier: Topic :: Text Processing :: General
@@ -1,43 +1,43 @@
1
- CHANGELOG.md,sha256=54GQstmx9pi0ijmjG9iuQlv9p7vemTqat9_Raq6hUNg,18005
2
- CONTRIBUTING.md,sha256=75Ik1RrezRrFkPSesTTYxWSxtKNY7_Av42Mt-8dUFTA,2004
1
+ CHANGELOG.md,sha256=CMTnOue9v10kCcfT0erpykMhQlRccQSLmlsEkOERi6k,18912
2
+ CONTRIBUTING.md,sha256=rmcbJPvVVIoIHJohtO6y9UaY4PUdWm4kuxLTirFyotg,2335
3
3
  LICENSE.md,sha256=tGtFDwxWTjuR9syrJoSv1Hiffd2u8Tu8cYClfrXS_YU,31956
4
- txt2ebook/__init__.py,sha256=9sZlWqwUBKm4DORriJpLB20trE6OrZE2GYAMP0oNRi0,1649
4
+ txt2ebook/__init__.py,sha256=3R_AsDSGLfyMPv40-Yb93fWrwFDcHNxYcC9PFeszips,1649
5
5
  txt2ebook/__main__.py,sha256=69yaheH-Fv2VIPS0T9kiVmiMluSgLGvwo1fa7badX2I,845
6
6
  txt2ebook/exceptions.py,sha256=Y6rqjXhiKIxNeKWVqvAqsUAOaBFKxlUE5Tm5EBcoi9w,837
7
7
  txt2ebook/formats/__init__.py,sha256=lVclUeBZ0Rf_zEAYyzc6m5ak2wXV3696d1tv4gMP9GQ,2322
8
- txt2ebook/formats/base.py,sha256=gmOyrOgmCxyw1MsD1ZLTJ1vIGTHnObPaehzoucZfI8M,3362
9
- txt2ebook/formats/epub.py,sha256=NOo6aYeAFeghhum1lneGCcMzsZErQlqwoSegx2G6Sps,6788
10
- txt2ebook/formats/gmi.py,sha256=5Ao4ageER-VsWWfl1ANhQIs2WXdKP7i4l5YroUkGvKk,6820
11
- txt2ebook/formats/md.py,sha256=tE8ksTkvpRu85-IxUyQIpp3pG2JDi6-9ZVOOOalulgk,6563
8
+ txt2ebook/formats/base.py,sha256=Zm0w_3GY8x2x8E-IzVo-KfYUCw2gsBT0-S3lNK600k4,4694
9
+ txt2ebook/formats/epub.py,sha256=PpUCvyvzKWM8fO-n2raPW-ZKIY0pkiQ07Y6YTWa6a1o,6798
10
+ txt2ebook/formats/gmi.py,sha256=fNFSamuvcWXZYP2pLR1vrXCx3-JcD4hVYVy4nlyATUg,6095
11
+ txt2ebook/formats/md.py,sha256=AIzf_eofrmxnMHqC5ZKRWp1quqHo8-gDrQHwHcjYAmI,5844
12
12
  txt2ebook/formats/pdf.py,sha256=rs4OJoEB4JBHR0BBd8P8x1YRYVcDXpLyI3SBJ8RzzFs,7107
13
13
  txt2ebook/formats/templates/__init__.py,sha256=0Hhm7Dayb7eB9h2cFo2103H4Tr6ie0ySMZ5gR01q6ag,750
14
14
  txt2ebook/formats/templates/epub/__init__.py,sha256=O2sdWRhkuamReBR20WedGWosLp8Dt8NZ33aXzyNQuLQ,756
15
15
  txt2ebook/formats/templates/epub/clean.css,sha256=AnEwMckzUSKcjKsDiWtJW1oaceuklt2tyuS1VbpVK1s,462
16
16
  txt2ebook/formats/templates/epub/condense.css,sha256=Fz80ZqkPsFRmGdURduAxqMV8drD0CCUlrv41P8rUsm8,477
17
17
  txt2ebook/formats/templates/epub/noindent.css,sha256=_O5Tv90TKyyPBRdgjuNKFwtKFbdheh2V9PtDhgRUg3U,483
18
- txt2ebook/formats/txt.py,sha256=K1DXcmxuAzUW8bzYgxh6UGBFevUPL12IXzYR5NrVgIU,7504
18
+ txt2ebook/formats/txt.py,sha256=4c2vAgE4QGPRo7aMECmRFNjQx2g7M_aARuQSeM4xKa8,6746
19
19
  txt2ebook/helpers/__init__.py,sha256=5EFTheYifNYk24rniaehnxd0NRR4K3aakSldZTVot1c,1966
20
20
  txt2ebook/languages/__init__.py,sha256=-c9-YQEkXnb9LKLoi7EtNlQLAw2SqDKVw2UXlWt9Dl0,753
21
21
  txt2ebook/languages/en.py,sha256=xCIhL431eUgoGwci4LCBkso8bR2LBhxKqWCPczKKoJQ,915
22
- txt2ebook/languages/zh_cn.py,sha256=rfAXIlDcWe6SiqFFQp_ShCKIDX3lZ85pjKkxF4JXiQU,1878
22
+ txt2ebook/languages/zh_cn.py,sha256=DZh-M4JcZdix-Bv4su_Aq2bBYBg4COYFHB8LEzbv0Gs,1919
23
23
  txt2ebook/languages/zh_tw.py,sha256=UyZQ8RYiSTaIXjk8W3GHaspdIEHclTtjRAoEk1X8XVw,1374
24
24
  txt2ebook/locales/en/LC_MESSAGES/txt2ebook.mo,sha256=NWBAcL3xlPkQqT8xFK9JCY8OrNtPOIw4s2OPHkvueA4,572
25
- txt2ebook/locales/en/LC_MESSAGES/txt2ebook.po,sha256=RPjcd3f_RyfIau6Mzy_tHTM9rwoqqIzEXiXPZO_Hymw,1424
26
- txt2ebook/locales/txt2ebook.pot,sha256=4fh4QBrtRqf_ePcBKl2U6Dttiv3PNiwg9wl5QL3pO8M,1382
25
+ txt2ebook/locales/en/LC_MESSAGES/txt2ebook.po,sha256=R-vQlS_pQXuJl7rBfP7TonK-il3hSH_GQn5sUVZOwtM,1285
26
+ txt2ebook/locales/txt2ebook.pot,sha256=l6l9Nf2oYclg0r5oWLMt15rzW3Kr_ajbI1JFLRPHqd0,1243
27
27
  txt2ebook/locales/zh-cn/LC_MESSAGES/txt2ebook.mo,sha256=PbZcaLjJpfCx-xw_rzUbYnrPoPlxPGIUaV-hmJQq_9s,572
28
- txt2ebook/locales/zh-cn/LC_MESSAGES/txt2ebook.po,sha256=FbJwMeQv6LVhz-Ntasp0cvJvbyfu4R2-bflqlCHxHms,1424
28
+ txt2ebook/locales/zh-cn/LC_MESSAGES/txt2ebook.po,sha256=FHog4JJ0CTj1lm2nMqRRW28K_KavBfgBckvIQsH-RO0,1285
29
29
  txt2ebook/locales/zh-tw/LC_MESSAGES/txt2ebook.mo,sha256=z-kUOMpQFjy1CjV3eyf38vXww9uH54uww5s4DWt2k6g,523
30
- txt2ebook/locales/zh-tw/LC_MESSAGES/txt2ebook.po,sha256=ShlJj1DdKf6klVCMwillIbZ6lnx1p7nkNU15mWvzi6Y,1271
30
+ txt2ebook/locales/zh-tw/LC_MESSAGES/txt2ebook.po,sha256=AaMZHY78QmlgF40K8w6mmSO-_4AyN1Z1xeNoX3_6LTI,1132
31
31
  txt2ebook/models/__init__.py,sha256=oHf4YvkEV7qKHhHiGxl4m95DSJ3zSIW2uJQec-BwXrc,920
32
32
  txt2ebook/models/book.py,sha256=OMKyw-j4pAVfldvFSTNIFKMp_dqG4Hogy7947st9Z2o,2754
33
33
  txt2ebook/models/chapter.py,sha256=PhPHraG3QNgY4C-USAzTDpDPVNsvTL8yC3r2iTW42KU,1676
34
34
  txt2ebook/models/volume.py,sha256=4n6a9cF69NTcOmWTxT1sVm-Bjvf-fagJ2E8thVX4uzg,1587
35
- txt2ebook/parser.py,sha256=AK3rkY9uItu7AJQnl2b5sMroqJgbxxQdKsOAEfo3nXU,11929
36
- txt2ebook/tokenizer.py,sha256=lsgGZSVpKl5fC6eEf8eIrGKZIVQnLqKM1Kenn4xJ-u4,9263
37
- txt2ebook/txt2ebook.py,sha256=GpZjqd7AEW0Mq-Ey8Ikorc8SLorJVB0yeIIL6BhD-5c,11449
35
+ txt2ebook/parser.py,sha256=6TJeHOOFtHvy1h9zaeVj3BdDTye9pJHiTWFtvMUMXR4,12114
36
+ txt2ebook/tokenizer.py,sha256=kzUW-xtAU4zgDxyUmPZZ7HNbjGDeOQX0MYr3_eOuByM,9313
37
+ txt2ebook/txt2ebook.py,sha256=t6SOHdhWQFufZoZcpJ2b5gfY5gUPuSTxvMrnsSq_ErU,11646
38
38
  txt2ebook/zh_utils.py,sha256=CM6SnsQhJWzSevcJWQ_9Pj028JUGbG3v0AG6xK_KgBY,4877
39
- txt2ebook-0.1.43.dist-info/LICENSE.md,sha256=tGtFDwxWTjuR9syrJoSv1Hiffd2u8Tu8cYClfrXS_YU,31956
40
- txt2ebook-0.1.43.dist-info/METADATA,sha256=4goYA7aZ6gZzuYld_RJe4_Sjo2t26m7GmVHsXmVouds,6777
41
- txt2ebook-0.1.43.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
42
- txt2ebook-0.1.43.dist-info/entry_points.txt,sha256=IQHyIIhd0MHjSSRVC1a6tMeIoLus8D06KHL_cumvEbg,83
43
- txt2ebook-0.1.43.dist-info/RECORD,,
39
+ txt2ebook-0.1.45.dist-info/LICENSE.md,sha256=tGtFDwxWTjuR9syrJoSv1Hiffd2u8Tu8cYClfrXS_YU,31956
40
+ txt2ebook-0.1.45.dist-info/METADATA,sha256=55vO3r0I_whQ1Soha4rbdHm5F5JZBBInRQZttNDKf3s,6575
41
+ txt2ebook-0.1.45.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
42
+ txt2ebook-0.1.45.dist-info/entry_points.txt,sha256=IQHyIIhd0MHjSSRVC1a6tMeIoLus8D06KHL_cumvEbg,83
43
+ txt2ebook-0.1.45.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.4.0
2
+ Generator: poetry-core 1.6.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any