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 +36 -0
- CONTRIBUTING.md +18 -8
- txt2ebook/__init__.py +1 -1
- txt2ebook/formats/base.py +41 -7
- txt2ebook/formats/epub.py +1 -1
- txt2ebook/formats/gmi.py +4 -26
- txt2ebook/formats/md.py +4 -26
- txt2ebook/formats/txt.py +4 -25
- txt2ebook/languages/zh_cn.py +1 -0
- txt2ebook/locales/en/LC_MESSAGES/txt2ebook.po +22 -26
- txt2ebook/locales/txt2ebook.pot +18 -22
- txt2ebook/locales/zh-cn/LC_MESSAGES/txt2ebook.po +22 -26
- txt2ebook/locales/zh-tw/LC_MESSAGES/txt2ebook.po +22 -26
- txt2ebook/parser.py +6 -1
- txt2ebook/tokenizer.py +95 -91
- txt2ebook/txt2ebook.py +8 -0
- {txt2ebook-0.1.43.dist-info → txt2ebook-0.1.45.dist-info}/METADATA +1 -5
- {txt2ebook-0.1.43.dist-info → txt2ebook-0.1.45.dist-info}/RECORD +21 -21
- {txt2ebook-0.1.43.dist-info → txt2ebook-0.1.45.dist-info}/WHEEL +1 -1
- {txt2ebook-0.1.43.dist-info → txt2ebook-0.1.45.dist-info}/LICENSE.md +0 -0
- {txt2ebook-0.1.43.dist-info → txt2ebook-0.1.45.dist-info}/entry_points.txt +0 -0
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,
|
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
|
42
|
-
py39
|
43
|
-
py310
|
44
|
-
py311
|
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
|
-
|
48
|
-
doc
|
49
|
-
|
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
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
|
-
|
92
|
-
|
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
|
-
|
96
|
-
|
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:
|
txt2ebook/languages/zh_cn.py
CHANGED
@@ -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-
|
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/
|
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/
|
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/
|
46
|
-
|
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/
|
51
|
-
|
52
|
-
|
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"
|
txt2ebook/locales/txt2ebook.pot
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
msgid ""
|
6
6
|
msgstr ""
|
7
7
|
"Project-Id-Version: PACKAGE VERSION\n"
|
8
|
-
"POT-Creation-Date: 2023-07-
|
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/
|
19
|
-
msgid "
|
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/
|
23
|
-
|
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/
|
28
|
-
|
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/
|
35
|
-
|
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/
|
40
|
-
#: src/txt2ebook/formats/
|
41
|
-
|
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/
|
45
|
-
|
46
|
-
msgid "tag:"
|
40
|
+
#: src/txt2ebook/formats/epub.py:142
|
41
|
+
msgid "cover"
|
47
42
|
msgstr ""
|
48
43
|
|
49
|
-
#: src/txt2ebook/formats/
|
50
|
-
|
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-
|
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/
|
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/
|
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/
|
46
|
-
|
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/
|
51
|
-
|
52
|
-
|
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-
|
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/
|
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/
|
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/
|
41
|
-
|
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/
|
46
|
-
|
47
|
-
|
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.
|
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 =
|
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) ->
|
94
|
+
def parse(self) -> None:
|
110
95
|
"""Parse the content into tokens.
|
111
96
|
|
112
97
|
Returns:
|
113
|
-
|
98
|
+
None
|
114
99
|
"""
|
115
|
-
|
116
|
-
|
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
|
113
|
+
def _tokenize_line(self, line: str) -> None:
|
142
114
|
"""Tokenize each line after we split by paragraph separator."""
|
143
|
-
_ = (
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
)
|
148
|
-
|
149
|
-
|
150
|
-
|
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
|
-
|
160
|
-
|
161
|
-
|
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
|
-
|
164
|
-
|
165
|
-
|
168
|
+
metadata = match[1].split("\n")
|
169
|
+
logger.debug("Metadata: %s", metadata)
|
170
|
+
return metadata
|
166
171
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
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
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
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
|
-
|
185
|
+
for line in lines:
|
186
|
+
self._tokenize_line(line)
|
184
187
|
|
185
|
-
def _tokenize_header(self, line: str
|
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
|
193
|
-
or self._tokenize_volume_chapter(line
|
194
|
-
or self._tokenize_volume(line
|
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
|
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*
|
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
|
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
|
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
|
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.
|
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=
|
2
|
-
CONTRIBUTING.md,sha256=
|
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=
|
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=
|
9
|
-
txt2ebook/formats/epub.py,sha256=
|
10
|
-
txt2ebook/formats/gmi.py,sha256=
|
11
|
-
txt2ebook/formats/md.py,sha256=
|
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=
|
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=
|
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=
|
26
|
-
txt2ebook/locales/txt2ebook.pot,sha256=
|
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=
|
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=
|
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=
|
36
|
-
txt2ebook/tokenizer.py,sha256=
|
37
|
-
txt2ebook/txt2ebook.py,sha256=
|
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.
|
40
|
-
txt2ebook-0.1.
|
41
|
-
txt2ebook-0.1.
|
42
|
-
txt2ebook-0.1.
|
43
|
-
txt2ebook-0.1.
|
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,,
|
File without changes
|
File without changes
|