txt2ebook 0.1.157__py3-none-any.whl → 0.1.159__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.
txt2ebook/formats/base.py CHANGED
@@ -134,6 +134,113 @@ class BaseWriter(ABC):
134
134
  file.parent, self.config.output_folder, lower_underscore(file.stem)
135
135
  ).with_suffix(extension)
136
136
 
137
+ def _get_toc_content_for_split(self) -> str:
138
+ raise NotImplementedError
139
+
140
+ def _get_volume_chapter_content_for_split(
141
+ self, volume: Volume, chapter: Chapter
142
+ ) -> str:
143
+ raise NotImplementedError
144
+
145
+ def _get_chapter_content_for_split(self, chapter: Chapter) -> str:
146
+ raise NotImplementedError
147
+
148
+ def _get_file_extension_for_split(self) -> str:
149
+ raise NotImplementedError
150
+
151
+ def _export_multiple_files(self) -> None:
152
+ logger.info("Split multiple files")
153
+
154
+ extension = self._get_file_extension_for_split()
155
+ txt_filename = Path(self.config.input_file.name)
156
+
157
+ export_filename = self._get_metadata_filename_for_split(
158
+ txt_filename, extension
159
+ )
160
+ export_filename.parent.mkdir(parents=True, exist_ok=True)
161
+ logger.info("Creating %s", export_filename)
162
+ with open(export_filename, "w", encoding="utf8") as file:
163
+ file.write(self._to_metadata_txt())
164
+
165
+ sc_seq = 1
166
+ if self.config.with_toc:
167
+ export_filename = self._get_toc_filename_for_split(
168
+ txt_filename, extension
169
+ )
170
+ export_filename.parent.mkdir(parents=True, exist_ok=True)
171
+ logger.info("Creating %s", export_filename)
172
+ with open(export_filename, "w", encoding="utf8") as file:
173
+ file.write(self._get_toc_content_for_split())
174
+
175
+ sc_seq = 2
176
+
177
+ for section in self.book.toc:
178
+ section_seq = str(sc_seq).rjust(2, "0")
179
+
180
+ ct_seq = 0
181
+ if isinstance(section, Volume):
182
+ for chapter in section.chapters:
183
+ chapter_seq = str(ct_seq).rjust(2, "0")
184
+ export_filename = (
185
+ self._get_volume_chapter_filename_for_split(
186
+ txt_filename,
187
+ section_seq,
188
+ chapter_seq,
189
+ section,
190
+ chapter,
191
+ extension,
192
+ )
193
+ )
194
+ export_filename.parent.mkdir(parents=True, exist_ok=True)
195
+ logger.info("Creating %s", export_filename)
196
+ with open(export_filename, "w", encoding="utf8") as file:
197
+ file.write(
198
+ self._get_volume_chapter_content_for_split(
199
+ section, chapter
200
+ )
201
+ )
202
+ ct_seq = ct_seq + 1
203
+ if isinstance(section, Chapter):
204
+ export_filename = self._get_chapter_filename_for_split(
205
+ txt_filename, section_seq, section, extension
206
+ )
207
+ export_filename.parent.mkdir(parents=True, exist_ok=True)
208
+ logger.info("Creating %s", export_filename)
209
+ with open(export_filename, "w", encoding="utf8") as file:
210
+ file.write(self._get_chapter_content_for_split(section))
211
+
212
+ sc_seq = sc_seq + 1
213
+
214
+ def _get_metadata_filename_for_split(
215
+ self, txt_filename: Path, extension: str
216
+ ) -> Path:
217
+ raise NotImplementedError
218
+
219
+ def _get_toc_filename_for_split(
220
+ self, txt_filename: Path, extension: str
221
+ ) -> Path:
222
+ raise NotImplementedError
223
+
224
+ def _get_volume_chapter_filename_for_split(
225
+ self,
226
+ txt_filename: Path,
227
+ section_seq: str,
228
+ chapter_seq: str,
229
+ volume: Volume,
230
+ chapter: Chapter,
231
+ extension: str,
232
+ ) -> Path:
233
+ raise NotImplementedError
234
+
235
+ def _get_chapter_filename_for_split(
236
+ self,
237
+ txt_filename: Path,
238
+ section_seq: str,
239
+ chapter: Chapter,
240
+ extension: str,
241
+ ) -> Path:
242
+ raise NotImplementedError
243
+
137
244
  def _to_metadata_txt(self) -> str:
138
245
  metadata = [
139
246
  self._("title:") + self.book.title,
txt2ebook/formats/gmi.py CHANGED
@@ -34,99 +34,98 @@ class GmiWriter(BaseWriter):
34
34
  if self.config.split_volume_and_chapter:
35
35
  self._export_multiple_files()
36
36
  else:
37
- self._new_file()
37
+ output_filename = self._output_filename(".gmi")
38
+ output_filename.parent.mkdir(parents=True, exist_ok=True)
38
39
 
39
- def _export_multiple_files(self) -> None:
40
- logger.info("Split multiple files")
40
+ with open(output_filename, "w", encoding="utf8") as file:
41
+ logger.info(
42
+ "Generate Gemini file: %s", output_filename.resolve()
43
+ )
44
+ file.write(self._to_gmi())
45
+
46
+ if self.config.open:
47
+ self._open_file(output_filename)
48
+
49
+ def _get_toc_content_for_split(self) -> str:
50
+ return self._to_toc("*", "# ")
51
+
52
+ def _get_volume_chapter_content_for_split(
53
+ self, volume: Volume, chapter: Chapter
54
+ ) -> str:
55
+ return self._to_volume_chapter_txt(volume, chapter)
56
+
57
+ def _get_chapter_content_for_split(self, chapter: Chapter) -> str:
58
+ return self._to_chapter_txt(chapter)
41
59
 
42
- txt_filename = Path(self.config.input_file.name)
43
- export_filename = Path(
60
+ def _get_file_extension_for_split(self) -> str:
61
+ return ".gmi"
62
+
63
+ def _get_metadata_filename_for_split(
64
+ self, txt_filename: Path, extension: str
65
+ ) -> Path:
66
+ return Path(
44
67
  txt_filename.resolve().parent.joinpath(
45
68
  self.config.output_folder,
46
69
  lower_underscore(
47
- f"00_{txt_filename.stem}_" + self._("metadata") + ".gmi"
70
+ f"00_{txt_filename.stem}_" + self._("metadata") + extension
48
71
  ),
49
72
  )
50
73
  )
51
- export_filename.parent.mkdir(parents=True, exist_ok=True)
52
- logger.info("Creating %s", export_filename)
53
- with open(export_filename, "w", encoding="utf8") as file:
54
- file.write(self._to_metadata_txt())
55
-
56
- sc_seq = 1
57
- if self.config.with_toc:
58
- export_filename = Path(
59
- txt_filename.resolve().parent.joinpath(
60
- self.config.output_folder,
61
- lower_underscore(
62
- f"01_{txt_filename.stem}_" + self._("toc") + ".gmi"
63
- ),
64
- )
65
- )
66
- export_filename.parent.mkdir(parents=True, exist_ok=True)
67
- logger.info("Creating %s", export_filename)
68
- with open(export_filename, "w", encoding="utf8") as file:
69
- file.write(self._to_toc("*", "# "))
70
74
 
71
- sc_seq = 2
72
-
73
- for section in self.book.toc:
74
- section_seq = str(sc_seq).rjust(2, "0")
75
-
76
- ct_seq = 0
77
- if isinstance(section, Volume):
78
- for chapter in section.chapters:
79
- chapter_seq = str(ct_seq).rjust(2, "0")
80
- filename = lower_underscore(
81
- (
82
- f"{section_seq}"
83
- f"_{chapter_seq}"
84
- f"_{txt_filename.stem}"
85
- f"_{section.title}"
86
- f"_{chapter.title}"
87
- ".gmi"
88
- )
89
- )
75
+ def _get_toc_filename_for_split(
76
+ self, txt_filename: Path, extension: str
77
+ ) -> Path:
78
+ return Path(
79
+ txt_filename.resolve().parent.joinpath(
80
+ self.config.output_folder,
81
+ lower_underscore(
82
+ f"01_{txt_filename.stem}_" + self._("toc") + extension
83
+ ),
84
+ )
85
+ )
90
86
 
91
- export_filename = Path(
92
- txt_filename.resolve().parent.joinpath(
93
- self.config.output_folder, filename
94
- )
87
+ def _get_volume_chapter_filename_for_split(
88
+ self,
89
+ txt_filename: Path,
90
+ section_seq: str,
91
+ chapter_seq: str,
92
+ volume: Volume,
93
+ chapter: Chapter,
94
+ extension: str,
95
+ ) -> Path:
96
+ return Path(
97
+ txt_filename.resolve().parent.joinpath(
98
+ self.config.output_folder,
99
+ lower_underscore(
100
+ (
101
+ f"{section_seq}"
102
+ f"_{chapter_seq}"
103
+ f"_{txt_filename.stem}"
104
+ f"_{volume.title}"
105
+ f"_{chapter.title}"
106
+ f"{extension}"
95
107
  )
96
- export_filename.parent.mkdir(parents=True, exist_ok=True)
97
- logger.info("Creating %s", export_filename)
98
- with open(export_filename, "w", encoding="utf8") as file:
99
- file.write(
100
- self._to_volume_chapter_txt(section, chapter)
101
- )
102
- ct_seq = ct_seq + 1
103
- if isinstance(section, Chapter):
104
- filename = lower_underscore(
105
- (f"{section_seq}_{txt_filename.stem}_{section.title}.gmi")
106
- )
108
+ ),
109
+ )
110
+ )
107
111
 
108
- export_filename = Path(
109
- txt_filename.resolve().parent.joinpath(
110
- self.config.output_folder, filename
112
+ def _get_chapter_filename_for_split(
113
+ self,
114
+ txt_filename: Path,
115
+ section_seq: str,
116
+ chapter: Chapter,
117
+ extension: str,
118
+ ) -> Path:
119
+ return Path(
120
+ txt_filename.resolve().parent.joinpath(
121
+ self.config.output_folder,
122
+ lower_underscore(
123
+ (
124
+ f"{section_seq}_{txt_filename.stem}_{chapter.title}{extension}"
111
125
  )
112
- )
113
- export_filename.parent.mkdir(parents=True, exist_ok=True)
114
- logger.info("Creating %s", export_filename)
115
- with open(export_filename, "w", encoding="utf8") as file:
116
- file.write(self._to_chapter_txt(section))
117
-
118
- sc_seq = sc_seq + 1
119
-
120
- def _new_file(self) -> None:
121
- new_filename = self._output_filename(".gmi")
122
- new_filename.parent.mkdir(parents=True, exist_ok=True)
123
-
124
- with open(new_filename, "w", encoding="utf8") as file:
125
- logger.info("Generate GemText file: %s", new_filename.resolve())
126
- file.write(self._to_gmi())
127
-
128
- if self.config.open:
129
- self._open_file(new_filename)
126
+ ),
127
+ )
128
+ )
130
129
 
131
130
  def _to_gmi(self) -> str:
132
131
  toc = self._to_toc("*", "# ") if self.config.with_toc else ""
txt2ebook/formats/md.py CHANGED
@@ -33,99 +33,98 @@ class MdWriter(BaseWriter):
33
33
  if self.config.split_volume_and_chapter:
34
34
  self._export_multiple_files()
35
35
  else:
36
- self._new_file()
36
+ output_filename = self._output_filename(".md")
37
+ output_filename.parent.mkdir(parents=True, exist_ok=True)
37
38
 
38
- def _export_multiple_files(self) -> None:
39
- logger.info("Split multiple files")
39
+ with open(output_filename, "w", encoding="utf8") as file:
40
+ logger.info(
41
+ "Generate Markdown file: %s", output_filename.resolve()
42
+ )
43
+ file.write(self._to_md())
44
+
45
+ if self.config.open:
46
+ self._open_file(output_filename)
47
+
48
+ def _get_toc_content_for_split(self) -> str:
49
+ return self._to_toc("-", "# ")
50
+
51
+ def _get_volume_chapter_content_for_split(
52
+ self, volume: Volume, chapter: Chapter
53
+ ) -> str:
54
+ return self._to_volume_chapter_txt(volume, chapter)
55
+
56
+ def _get_chapter_content_for_split(self, chapter: Chapter) -> str:
57
+ return self._to_chapter_txt(chapter)
40
58
 
41
- txt_filename = Path(self.config.input_file.name)
42
- export_filename = Path(
59
+ def _get_file_extension_for_split(self) -> str:
60
+ return ".md"
61
+
62
+ def _get_metadata_filename_for_split(
63
+ self, txt_filename: Path, extension: str
64
+ ) -> Path:
65
+ return Path(
43
66
  txt_filename.resolve().parent.joinpath(
44
67
  self.config.output_folder,
45
68
  lower_underscore(
46
- f"00_{txt_filename.stem}_" + self._("metadata") + ".md"
69
+ f"00_{txt_filename.stem}_" + self._("metadata") + extension
47
70
  ),
48
71
  )
49
72
  )
50
- export_filename.parent.mkdir(parents=True, exist_ok=True)
51
- logger.info("Creating %s", export_filename)
52
- with open(export_filename, "w", encoding="utf8") as file:
53
- file.write(self._to_metadata_txt())
54
-
55
- sc_seq = 1
56
- if self.config.with_toc:
57
- export_filename = Path(
58
- txt_filename.resolve().parent.joinpath(
59
- self.config.output_folder,
60
- lower_underscore(
61
- f"01_{txt_filename.stem}_" + self._("toc") + ".md"
62
- ),
63
- )
64
- )
65
- export_filename.parent.mkdir(parents=True, exist_ok=True)
66
- logger.info("Creating %s", export_filename)
67
- with open(export_filename, "w", encoding="utf8") as file:
68
- file.write(self._to_toc("-", "# "))
69
73
 
70
- sc_seq = 2
71
-
72
- for section in self.book.toc:
73
- section_seq = str(sc_seq).rjust(2, "0")
74
-
75
- ct_seq = 0
76
- if isinstance(section, Volume):
77
- for chapter in section.chapters:
78
- chapter_seq = str(ct_seq).rjust(2, "0")
79
- filename = lower_underscore(
80
- (
81
- f"{section_seq}"
82
- f"_{chapter_seq}"
83
- f"_{txt_filename.stem}"
84
- f"_{section.title}"
85
- f"_{chapter.title}"
86
- ".md"
87
- )
88
- )
74
+ def _get_toc_filename_for_split(
75
+ self, txt_filename: Path, extension: str
76
+ ) -> Path:
77
+ return Path(
78
+ txt_filename.resolve().parent.joinpath(
79
+ self.config.output_folder,
80
+ lower_underscore(
81
+ f"01_{txt_filename.stem}_" + self._("toc") + extension
82
+ ),
83
+ )
84
+ )
89
85
 
90
- export_filename = Path(
91
- txt_filename.resolve().parent.joinpath(
92
- self.config.output_folder, filename
93
- )
86
+ def _get_volume_chapter_filename_for_split(
87
+ self,
88
+ txt_filename: Path,
89
+ section_seq: str,
90
+ chapter_seq: str,
91
+ volume: Volume,
92
+ chapter: Chapter,
93
+ extension: str,
94
+ ) -> Path:
95
+ return Path(
96
+ txt_filename.resolve().parent.joinpath(
97
+ self.config.output_folder,
98
+ lower_underscore(
99
+ (
100
+ f"{section_seq}"
101
+ f"_{chapter_seq}"
102
+ f"_{txt_filename.stem}"
103
+ f"_{volume.title}"
104
+ f"_{chapter.title}"
105
+ f"{extension}"
94
106
  )
95
- export_filename.parent.mkdir(parents=True, exist_ok=True)
96
- logger.info("Creating %s", export_filename)
97
- with open(export_filename, "w", encoding="utf8") as file:
98
- file.write(
99
- self._to_volume_chapter_txt(section, chapter)
100
- )
101
- ct_seq = ct_seq + 1
102
- if isinstance(section, Chapter):
103
- filename = lower_underscore(
104
- (f"{section_seq}_{txt_filename.stem}_{section.title}.md")
105
- )
107
+ ),
108
+ )
109
+ )
106
110
 
107
- export_filename = Path(
108
- txt_filename.resolve().parent.joinpath(
109
- self.config.output_folder, filename
111
+ def _get_chapter_filename_for_split(
112
+ self,
113
+ txt_filename: Path,
114
+ section_seq: str,
115
+ chapter: Chapter,
116
+ extension: str,
117
+ ) -> Path:
118
+ return Path(
119
+ txt_filename.resolve().parent.joinpath(
120
+ self.config.output_folder,
121
+ lower_underscore(
122
+ (
123
+ f"{section_seq}_{txt_filename.stem}_{chapter.title}{extension}"
110
124
  )
111
- )
112
- export_filename.parent.mkdir(parents=True, exist_ok=True)
113
- logger.info("Creating %s", export_filename)
114
- with open(export_filename, "w", encoding="utf8") as file:
115
- file.write(self._to_chapter_txt(section))
116
-
117
- sc_seq = sc_seq + 1
118
-
119
- def _new_file(self) -> None:
120
- new_filename = self._output_filename(".md")
121
- new_filename.parent.mkdir(parents=True, exist_ok=True)
122
-
123
- with open(new_filename, "w", encoding="utf8") as file:
124
- logger.info("Generate Markdown file: %s", new_filename.resolve())
125
- file.write(self._to_md())
126
-
127
- if self.config.open:
128
- self._open_file(new_filename)
125
+ ),
126
+ )
127
+ )
129
128
 
130
129
  def _to_md(self) -> str:
131
130
  toc = self._to_toc("-", "# ") if self.config.with_toc else ""
txt2ebook/formats/txt.py CHANGED
@@ -16,6 +16,7 @@
16
16
  """Convert and backup source text file into text as well."""
17
17
 
18
18
  import logging
19
+ import shutil
19
20
  from datetime import datetime as dt
20
21
  from pathlib import Path
21
22
 
@@ -40,130 +41,149 @@ class TxtWriter(BaseWriter):
40
41
  elif self.config.split_volume_and_chapter:
41
42
  self._export_multiple_files()
42
43
  else:
43
- if self.config.overwrite:
44
- self._overwrite_file()
45
- else:
46
- self._new_file()
44
+ output_filename = self._output_filename(".txt")
45
+ output_filename.parent.mkdir(parents=True, exist_ok=True)
46
+
47
+ if self.config.overwrite and output_filename == Path(
48
+ self.config.input_file.name
49
+ ):
50
+ ymd_hms = dt.now().strftime("%Y%m%d_%H%M%S")
51
+ backup_filename = Path(
52
+ Path(self.config.input_file.name)
53
+ .resolve()
54
+ .parent.joinpath(
55
+ lower_underscore(
56
+ Path(self.config.input_file.name).stem
57
+ + "_"
58
+ + ymd_hms
59
+ + ".txt"
60
+ )
61
+ )
62
+ )
63
+ logger.info(
64
+ "Backup source text file: %s", backup_filename.resolve()
65
+ )
66
+ shutil.copyfile(output_filename, backup_filename)
47
67
 
48
- def _export_multiple_files(self) -> None:
49
- logger.info("Split multiple files")
68
+ with open(output_filename, "w", encoding="utf8") as file:
69
+ logger.info("Generate TXT file: %s", output_filename.resolve())
70
+ file.write(self._to_txt())
50
71
 
51
- txt_filename = Path(self.config.input_file.name)
52
- export_filename = Path(
72
+ if self.config.open:
73
+ self._open_file(output_filename)
74
+
75
+ def _get_metadata_filename_for_split(
76
+ self, txt_filename: Path, extension: str
77
+ ) -> Path:
78
+ return Path(
53
79
  txt_filename.resolve().parent.joinpath(
54
80
  self.config.output_folder,
55
- lower_underscore(
56
- f"00_{txt_filename.stem}_" + self._("metadata") + ".txt"
81
+ f"00_{txt_filename.stem}_" + self._("metadata") + extension,
82
+ )
83
+ )
84
+
85
+ def _get_toc_filename_for_split(
86
+ self, txt_filename: Path, extension: str
87
+ ) -> Path:
88
+ return Path(
89
+ txt_filename.resolve().parent.joinpath(
90
+ self.config.output_folder,
91
+ f"01_{txt_filename.stem}_" + self._("toc") + extension,
92
+ )
93
+ )
94
+
95
+ def _get_volume_chapter_filename_for_split(
96
+ self,
97
+ txt_filename: Path,
98
+ section_seq: str,
99
+ chapter_seq: str,
100
+ volume: Volume,
101
+ chapter: Chapter,
102
+ extension: str,
103
+ ) -> Path:
104
+ return Path(
105
+ txt_filename.resolve().parent.joinpath(
106
+ self.config.output_folder,
107
+ (
108
+ f"{section_seq}"
109
+ f"_{chapter_seq}"
110
+ f"_{txt_filename.stem}"
111
+ f"_{volume.title}"
112
+ f"_{chapter.title}"
113
+ f"{extension}"
57
114
  ),
58
115
  )
59
116
  )
60
- export_filename.parent.mkdir(parents=True, exist_ok=True)
61
- logger.info("Creating %s", export_filename)
62
- with open(export_filename, "w", encoding="utf8") as file:
63
- file.write(self._to_metadata_txt())
64
117
 
65
- sc_seq = 1
66
- if self.config.with_toc:
67
- export_filename = Path(
68
- txt_filename.resolve().parent.joinpath(
69
- self.config.output_folder,
70
- lower_underscore(
71
- f"01_{txt_filename.stem}_" + self._("toc") + ".txt"
72
- ),
73
- )
118
+ def _get_chapter_filename_for_split(
119
+ self,
120
+ txt_filename: Path,
121
+ section_seq: str,
122
+ chapter: Chapter,
123
+ extension: str,
124
+ ) -> Path:
125
+ return Path(
126
+ txt_filename.resolve().parent.joinpath(
127
+ self.config.output_folder,
128
+ (
129
+ f"{section_seq}_{txt_filename.stem}_{chapter.title}{extension}"
130
+ ),
74
131
  )
75
- export_filename.parent.mkdir(parents=True, exist_ok=True)
76
- logger.info("Creating %s", export_filename)
77
- with open(export_filename, "w", encoding="utf8") as file:
78
- file.write(self._to_toc("-"))
132
+ )
79
133
 
80
- sc_seq = 2
134
+ def _export_multiple_files(self) -> None:
135
+ """Export multiple files based on volume and chapter."""
136
+ txt_filename = Path(self.config.input_file.name)
137
+ txt_filename.parent.joinpath(self.config.output_folder).mkdir(
138
+ parents=True, exist_ok=True
139
+ )
81
140
 
82
- for section in self.book.toc:
83
- section_seq = str(sc_seq).rjust(2, "0")
141
+ # 1. Write metadata file
142
+ metadata_filename = self._get_metadata_filename_for_split(
143
+ txt_filename, ".txt"
144
+ )
145
+ with open(metadata_filename, "w", encoding="utf8") as file:
146
+ logger.info("Creating %s", metadata_filename.resolve())
147
+ file.write(self._to_metadata_txt())
84
148
 
85
- ct_seq = 0
149
+ # 2. Write volume/chapter files
150
+ section_seq = 0
151
+ chapter_seq = 0
152
+ for section in self.book.toc:
86
153
  if isinstance(section, Volume):
154
+ section_seq += 1
155
+ chapter_seq = 0
87
156
  for chapter in section.chapters:
88
- chapter_seq = str(ct_seq).rjust(2, "0")
89
- filename = lower_underscore(
90
- (
91
- f"{section_seq}"
92
- f"_{chapter_seq}"
93
- f"_{txt_filename.stem}"
94
- f"_{section.title}"
95
- f"_{chapter.title}"
96
- ".txt"
97
- )
98
- )
99
-
100
- export_filename = Path(
101
- txt_filename.resolve().parent.joinpath(
102
- self.config.output_folder, filename
103
- )
157
+ chapter_seq += 1
158
+ output_filename = self._get_volume_chapter_filename_for_split(
159
+ txt_filename,
160
+ str(section_seq).rjust(2, "0"),
161
+ str(chapter_seq).rjust(2, "0"),
162
+ section,
163
+ chapter,
164
+ ".txt",
104
165
  )
105
- export_filename.parent.mkdir(parents=True, exist_ok=True)
106
- logger.info("Creating %s", export_filename)
107
- with open(export_filename, "w", encoding="utf8") as file:
108
- file.write(
109
- self._to_volume_chapter_txt(section, chapter)
110
- )
111
- ct_seq = ct_seq + 1
112
- if isinstance(section, Chapter):
113
- filename = lower_underscore(
114
- (f"{section_seq}_{txt_filename.stem}_{section.title}.txt")
166
+ with open(output_filename, "w", encoding="utf8") as file:
167
+ logger.info("Creating %s", output_filename.resolve())
168
+ file.write(self._to_volume_chapter_txt(section, chapter))
169
+ elif isinstance(section, Chapter):
170
+ section_seq += 1
171
+ output_filename = self._get_chapter_filename_for_split(
172
+ txt_filename,
173
+ str(section_seq).rjust(2, "0"),
174
+ section,
175
+ ".txt",
115
176
  )
116
-
117
- export_filename = Path(
118
- txt_filename.resolve().parent.joinpath(
119
- self.config.output_folder, filename
120
- )
121
- )
122
- export_filename.parent.mkdir(parents=True, exist_ok=True)
123
- logger.info("Creating %s", export_filename)
124
- with open(export_filename, "w", encoding="utf8") as file:
177
+ with open(output_filename, "w", encoding="utf8") as file:
178
+ logger.info("Creating %s", output_filename.resolve())
125
179
  file.write(self._to_chapter_txt(section))
126
180
 
127
- sc_seq = sc_seq + 1
128
-
129
- def _new_file(self) -> None:
130
- new_filename = self._output_filename(".txt")
131
- txt_filename = Path(self.config.input_file.name)
132
-
133
- if new_filename == txt_filename:
134
- ymd_hms = dt.now().strftime("%Y%m%d_%H%M%S")
135
- new_filename = Path(
136
- txt_filename.resolve().parent.joinpath(
137
- lower_underscore(
138
- txt_filename.stem + "_" + ymd_hms + ".txt"
139
- )
140
- )
141
- )
142
-
143
- new_filename.parent.mkdir(parents=True, exist_ok=True)
144
-
145
- with open(new_filename, "w", encoding="utf8") as file:
146
- file.write(self._to_txt())
147
- logger.info("Generate TXT file: %s", new_filename.resolve())
148
-
149
181
  if self.config.open:
150
- self._open_file(new_filename)
151
-
152
- def _overwrite_file(self) -> None:
153
- txt_filename = Path(self.config.input_file.name)
154
-
155
- with open(txt_filename, "w", encoding="utf8") as file:
156
- file.write(self._to_txt())
157
- logger.info("Overwrite txt file: %s", txt_filename.resolve())
158
-
159
- if self.config.open:
160
- self._open_file(txt_filename)
182
+ self._open_file(metadata_filename)
161
183
 
162
184
  def _to_txt(self) -> str:
163
185
  toc = self._to_toc("-") if self.config.with_toc else ""
164
186
  return self._to_metadata_txt() + toc + self._to_body_txt()
165
-
166
- def _to_body_txt(self) -> str:
167
187
  content = []
168
188
  for section in self.book.toc:
169
189
  if isinstance(section, Volume):
txt2ebook/formats/typ.py CHANGED
@@ -18,11 +18,13 @@
18
18
  import importlib.resources as importlib_res
19
19
  import logging
20
20
  import textwrap
21
+ from pathlib import Path
21
22
 
22
23
  import importlib_resources
23
24
  import typst
24
25
 
25
26
  from txt2ebook.formats.base import BaseWriter
27
+ from txt2ebook.helpers import lower_underscore
26
28
  from txt2ebook.models import Chapter, Volume
27
29
 
28
30
  # workaround for Python 3.8
@@ -43,9 +45,6 @@ class TypWriter(BaseWriter):
43
45
 
44
46
  def write(self) -> None:
45
47
  """Generate Typst files."""
46
- self._new_file()
47
-
48
- def _new_file(self) -> None:
49
48
  new_filename = self._output_filename(".typ")
50
49
  new_filename.parent.mkdir(parents=True, exist_ok=True)
51
50
 
@@ -210,6 +209,20 @@ class TypWriter(BaseWriter):
210
209
 
211
210
  return self.config.paragraph_separator.join(pars)
212
211
 
212
+ def _get_file_extension_for_split(self) -> str:
213
+ return ".typ"
214
+
215
+ def _get_toc_content_for_split(self) -> str:
216
+ return self._to_outline()
217
+
218
+ def _get_volume_chapter_content_for_split(
219
+ self, volume: Volume, chapter: Chapter
220
+ ) -> str:
221
+ return self._to_volume_chapter_txt(volume, chapter)
222
+
223
+ def _get_chapter_content_for_split(self, chapter: Chapter) -> str:
224
+ return self._to_chapter_txt(chapter)
225
+
213
226
  def _index_pages(self) -> str:
214
227
  return textwrap.dedent(
215
228
  """
@@ -221,3 +234,35 @@ class TypWriter(BaseWriter):
221
234
  ]
222
235
  """
223
236
  )
237
+
238
+ def _get_metadata_filename_for_split(
239
+ self, txt_filename: Path, extension: str
240
+ ) -> Path:
241
+ return Path(self._output_folder(), "metadata").with_suffix(extension)
242
+
243
+ def _get_toc_filename_for_split(
244
+ self, txt_filename: Path, extension: str
245
+ ) -> Path:
246
+ return Path(self._output_folder(), "toc").with_suffix(extension)
247
+
248
+ def _get_volume_chapter_filename_for_split(
249
+ self,
250
+ txt_filename: Path,
251
+ section_seq: str,
252
+ chapter_seq: str,
253
+ volume: Volume,
254
+ chapter: Chapter,
255
+ extension: str,
256
+ ) -> Path:
257
+ filename = f"{section_seq}-{lower_underscore(volume.title)}-{chapter_seq}-{lower_underscore(chapter.title)}"
258
+ return Path(self._output_folder(), filename).with_suffix(extension)
259
+
260
+ def _get_chapter_filename_for_split(
261
+ self,
262
+ txt_filename: Path,
263
+ section_seq: str,
264
+ chapter: Chapter,
265
+ extension: str,
266
+ ) -> Path:
267
+ filename = f"{section_seq}-{lower_underscore(chapter.title)}"
268
+ return Path(self._output_folder(), filename).with_suffix(extension)
@@ -38,10 +38,10 @@ def build_subparser(subparsers) -> None:
38
38
 
39
39
  epub_parser.add_argument(
40
40
  "input_file",
41
- nargs="*",
41
+ nargs=1,
42
42
  type=argparse.FileType("rb"),
43
- help="source text filenames (can use glob patterns)",
44
- metavar="TXT_FILENAMES",
43
+ help="source text filename",
44
+ metavar="TXT_FILENAME",
45
45
  )
46
46
 
47
47
  epub_parser.add_argument(
@@ -125,12 +125,12 @@ def run(args: argparse.Namespace) -> None:
125
125
  """
126
126
  input_sources = []
127
127
 
128
- if not sys.stdin.isatty():
129
- # piped input, use stdin as the single input source
130
- input_sources.append(sys.stdin)
131
- elif args.input_file:
132
- # multiple file(s)
128
+ if args.input_file:
129
+ # File path(s) were explicitly provided on the command line
133
130
  input_sources.extend(args.input_file)
131
+ elif not sys.stdin.isatty():
132
+ # No file path provided, check for piped input
133
+ input_sources.append(sys.stdin)
134
134
  else:
135
135
  logger.error("No input files provided.")
136
136
  sys.exit(1)
@@ -147,6 +147,10 @@ def run(args: argparse.Namespace) -> None:
147
147
  current_file_args = argparse.Namespace(**vars(args))
148
148
  current_file_args.input_file = current_input_stream
149
149
 
150
+ logger.debug(
151
+ "Create separate volume page: %s", current_file_args.volume_page
152
+ )
153
+
150
154
  # if an explicit output_file was provided, it must apply to the first
151
155
  # input
152
156
  if i > 0 and args.output_file:
@@ -28,7 +28,9 @@ from bs4 import UnicodeDammit
28
28
 
29
29
  from txt2ebook import detect_and_expect_language
30
30
  from txt2ebook.exceptions import EmptyFileError
31
+ from txt2ebook.formats.txt import TxtWriter
31
32
  from txt2ebook.models.book import Book
33
+ from txt2ebook.parser import Parser
32
34
  from txt2ebook.zh_utils import zh_halfwidth_to_fullwidth, zh_words_to_numbers
33
35
 
34
36
  logger = logging.getLogger(__name__)
@@ -216,26 +218,27 @@ def run(args: argparse.Namespace) -> None:
216
218
  None
217
219
  """
218
220
  massaged_txt = massage_txt(args)
219
- if args.overwrite:
220
- _overwrite_file(args, massaged_txt)
221
- else:
222
- _new_file(args, massaged_txt)
223
221
 
224
- # args.language = detect_and_expect_language(massaged_txt, args.language)
225
- # config_lang = args.language.replace("-", "_")
226
- # langconf = import_module(f"txt2ebook.languages.{config_lang}")
227
- # args.with_toc = False
228
- # parser = Parser(massaged_txt, args, langconf)
229
- # book = parser.parse()
222
+ if args.split_volume_and_chapter:
223
+ args.language = detect_and_expect_language(massaged_txt, args.language)
224
+ config_lang = args.language.replace("-", "_")
225
+ langconf = import_module(f"txt2ebook.languages.{config_lang}")
226
+ args.with_toc = False
227
+ parser = Parser(massaged_txt, args, langconf)
228
+ book = parser.parse()
230
229
 
231
- # if args.debug:
232
- # book.debug(args.verbose)
230
+ if args.debug:
231
+ book.debug(args.verbose)
233
232
 
234
- # if args.header_number:
235
- # book = header_number(args, book)
233
+ if args.header_number:
234
+ book = header_number(args, book)
236
235
 
237
- # writer = TxtWriter(book, args)
238
- # writer.write()
236
+ writer = TxtWriter(book, args)
237
+ writer.write()
238
+ elif args.overwrite:
239
+ _overwrite_file(args, massaged_txt)
240
+ else:
241
+ _new_file(args, massaged_txt)
239
242
 
240
243
 
241
244
  def _overwrite_file(args, massaged_txt) -> None:
@@ -356,11 +359,13 @@ def massage_txt(args: argparse.Namespace) -> str:
356
359
  if args.re_delete_line:
357
360
  body = do_delete_line_regex(args, body)
358
361
 
359
- if args.single_newline:
360
- body = do_single_newline(args, body)
361
-
362
362
  if args.width:
363
363
  body = do_wrapping(args, body)
364
+ elif args.single_newline:
365
+ body = do_single_newline(args, body)
366
+ else:
367
+ # Apply paragraph separation and line unwrapping by default
368
+ body = _unwrap_content(args, body)
364
369
 
365
370
  return f"{metadata}{body}"
366
371
 
@@ -376,6 +381,7 @@ def to_unix_newline(content: str) -> str:
376
381
  """
377
382
  return content.replace("\r\n", "\n").replace("\r", "\n")
378
383
 
384
+
379
385
  def do_reindent_paragraph(args, content: str) -> str:
380
386
  """Reindent each paragraph.
381
387
 
@@ -385,16 +391,16 @@ def do_reindent_paragraph(args, content: str) -> str:
385
391
  Returns:
386
392
  str: The formatted book content.
387
393
  """
388
- paragraphs = re.split(r'\n\s*\n+', content)
394
+ paragraphs = re.split(r"\n\s*\n+", content)
389
395
  reindented_paragraphs = []
390
396
  for paragraph in paragraphs:
391
- lines = paragraph.split('\n')
397
+ lines = paragraph.split("\n")
392
398
  reindented_lines = []
393
399
  for line in lines:
394
400
  stripped_line = line.strip()
395
401
  reindented_lines.append(stripped_line)
396
402
 
397
- reindented_paragraph = '\n'.join(reindented_lines)
403
+ reindented_paragraph = "\n".join(reindented_lines)
398
404
  reindented_paragraph = "  " + reindented_paragraph
399
405
  reindented_paragraphs.append(reindented_paragraph)
400
406
 
@@ -542,7 +548,7 @@ def _unwrap_content(args: argparse.Namespace, content: str) -> str:
542
548
  Returns:
543
549
  str: The formatted book content.
544
550
  """
545
- paragraphs = content.split(args.paragraph_separator)
551
+ paragraphs = re.split(r"\n\s*\n+", content)
546
552
  processed_paragraphs = []
547
553
  for paragraph in paragraphs:
548
554
  single_line_paragraph = " ".join(paragraph.splitlines())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: txt2ebook
3
- Version: 0.1.157
3
+ Version: 0.1.159
4
4
  Summary: CLI tool to convert txt file to ebook format
5
5
  Author-email: Kian-Meng Ang <kianmeng@cpan.org>
6
6
  License-Expression: AGPL-3.0-or-later
@@ -6,14 +6,14 @@ txt2ebook/parser.py,sha256=ITn6pGHO4vTfdrYouVV2mEe9jUM2zm3-FKEcspp2qzI,8968
6
6
  txt2ebook/tokenizer.py,sha256=UGyOBGxlKOXJtvP2UFp38EgFym8-PAU3A7Jl9RF3w6Y,10299
7
7
  txt2ebook/zh_utils.py,sha256=0Yq9r-JL4HntW68vFR6TBP9yQim1a07mfsh_sp-XmaE,4887
8
8
  txt2ebook/formats/__init__.py,sha256=_fW9UuoOTFxCKlej6t-PsFzJOqDFLzVatCci9tcPQeE,1645
9
- txt2ebook/formats/base.py,sha256=xUrLOPlJyV_8tSIbBzrbBe3fP481CuHYSdOUSdtxeik,5796
9
+ txt2ebook/formats/base.py,sha256=bPpfKZwz3KeEtNRYJPZJJIKRcANGs_tw5a3uOw1pzSs,9625
10
10
  txt2ebook/formats/epub.py,sha256=IVz-FmYQlcChOw38YqfKy46bPVSIrHyxA_94iz06N3Y,6941
11
- txt2ebook/formats/gmi.py,sha256=k9PY6tcXf1aXrW3ZGo-0nvt1PDS1LoG5i31hze5oYqM,6662
12
- txt2ebook/formats/md.py,sha256=BzT3jsSYZR7rx9Lxuu1DvxCWQutSmMzoab8huKvxaxE,6408
11
+ txt2ebook/formats/gmi.py,sha256=tUCEHtRHDupEPJ8dYPpxpE6yEKHCk8PRXR0zgjJFgsI,5837
12
+ txt2ebook/formats/md.py,sha256=ZleBFNOGRhWr5WgcA8uyLXBxJm1bdQaunqzjocQYSkI,5587
13
13
  txt2ebook/formats/pdf.py,sha256=tr_ozVlL976yo7Ggny71zjOwzSd6tSnHTl7mcsLII_g,7263
14
14
  txt2ebook/formats/tex.py,sha256=V5B1nPR-WzGc4jqWu-BqxfQhtQsUTKM_sZZJsCcDBAk,5897
15
- txt2ebook/formats/txt.py,sha256=0DOVBTDRV2gcixham5Wj7NaZlNKAeLG9MLRlZgtUxqM,7412
16
- txt2ebook/formats/typ.py,sha256=MNclD5RdCnYAmPRzAaI6ZE6NnI8GdHKJla54wyfTUdc,6705
15
+ txt2ebook/formats/txt.py,sha256=1f-e53oPyhyElLLKqZZ4_HJxIJkwyVjZLi0pnl02EM8,7929
16
+ txt2ebook/formats/typ.py,sha256=0WjGeZHxbdCV1grYkEpgUocehWfmeJz8Ur9cT2ADlFI,8269
17
17
  txt2ebook/formats/templates/__init__.py,sha256=f3K7pJByNmmvu-wvziks6qb2QnnLmkDjUACXyw2s60E,760
18
18
  txt2ebook/formats/templates/epub/__init__.py,sha256=-XVLvnknTJTmQZY9UTH705vMcHgy56rQVRTusYawEZ4,766
19
19
  txt2ebook/helpers/__init__.py,sha256=c2EItHvPABDORfgfjArfa5XR--43es4D1tKWqaPcBxY,1309
@@ -27,17 +27,17 @@ txt2ebook/models/chapter.py,sha256=6YvUDHzR6amGMZgkQl_xHWrYZUmlfpF7mnDLilG2BpA,1
27
27
  txt2ebook/models/volume.py,sha256=koz1KfWjvGWLFbmGEQlZ23frsP93cDsuBMySYBHiXm8,1597
28
28
  txt2ebook/subcommands/__init__.py,sha256=ldhzvsrMsR8lZmhZef77JFz0jValpV3pytFfwJSkjls,1146
29
29
  txt2ebook/subcommands/env.py,sha256=gEzra4b6guy7pRZUTCWX1_eiR7JmrtR1Z-J-vxljvMY,1549
30
- txt2ebook/subcommands/epub.py,sha256=oVk00rypqN53UAOIYhOofkAPbjrdlvkszLDIdVqexvE,4706
30
+ txt2ebook/subcommands/epub.py,sha256=xaXKATCioRVr-vN_Lw4SqQxJdm6dSWs-y3RyTpV3ZV8,4824
31
31
  txt2ebook/subcommands/gmi.py,sha256=ANnPg-RFsylTo44fUzFOSHN1fC3Ce82gBzrv-sBv5fU,3318
32
- txt2ebook/subcommands/massage.py,sha256=WmHHJQLowdjSDQnEqxVKsU9YsabY7fw4mBkfddsFZpI,15535
32
+ txt2ebook/subcommands/massage.py,sha256=f_moVt19n60QH2T2J_EwZnCv1JNFrqLGu5j2VZfp_Lk,15793
33
33
  txt2ebook/subcommands/md.py,sha256=PmIqrqrnzLywvN4qTkle0V9N3FTIJGRWpC0Xbk76B5o,3329
34
34
  txt2ebook/subcommands/parse.py,sha256=xjhW8I9zS5DL3n3m04RyFofgci-6-_L6aF3d4N7c7M4,2938
35
35
  txt2ebook/subcommands/pdf.py,sha256=1JQtpugzAIaho6G3CK1rGYk74hotAexXZxPH9PHpRps,2980
36
36
  txt2ebook/subcommands/tex.py,sha256=8XqTV5GsOEr7sGSLUJB-B1KefIMxW3_BDq_Jm96Bt1Y,4369
37
37
  txt2ebook/subcommands/typ.py,sha256=xeJ_cPmyq_uouUBiH2kbcscckHLqewPmu9j0WO36sXY,4814
38
- txt2ebook-0.1.157.dist-info/licenses/LICENSE.md,sha256=tGtFDwxWTjuR9syrJoSv1Hiffd2u8Tu8cYClfrXS_YU,31956
39
- txt2ebook-0.1.157.dist-info/METADATA,sha256=WSRHi5sIRSNXPlUpUSYK7GWueOhHAdczDQfC3XSUHX4,5295
40
- txt2ebook-0.1.157.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
41
- txt2ebook-0.1.157.dist-info/entry_points.txt,sha256=3jm5vpUsDRgoM6S3CQVMMiP7tJQqfq1vfV0sh_KaK9s,74
42
- txt2ebook-0.1.157.dist-info/top_level.txt,sha256=pesdk4CJRlfhUXVD9vH3Dd_F8ATlLQoqlUsUnU8SJMw,10
43
- txt2ebook-0.1.157.dist-info/RECORD,,
38
+ txt2ebook-0.1.159.dist-info/licenses/LICENSE.md,sha256=tGtFDwxWTjuR9syrJoSv1Hiffd2u8Tu8cYClfrXS_YU,31956
39
+ txt2ebook-0.1.159.dist-info/METADATA,sha256=SaESbcwNBN1WQMIbsaHoUWuEnjYxNQoZqZgg5CeYZag,5295
40
+ txt2ebook-0.1.159.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
41
+ txt2ebook-0.1.159.dist-info/entry_points.txt,sha256=3jm5vpUsDRgoM6S3CQVMMiP7tJQqfq1vfV0sh_KaK9s,74
42
+ txt2ebook-0.1.159.dist-info/top_level.txt,sha256=pesdk4CJRlfhUXVD9vH3Dd_F8ATlLQoqlUsUnU8SJMw,10
43
+ txt2ebook-0.1.159.dist-info/RECORD,,