adds support for comppleted files
This commit is contained in:
@@ -9,13 +9,21 @@ from .api import (
|
||||
is_file,
|
||||
list_input_files,
|
||||
pre_process_a_dictionary_file,
|
||||
proccess_a_completed_file,
|
||||
process_a_dictation_file,
|
||||
process_a_dictionary_file,
|
||||
process_a_phrases_file,
|
||||
select_file,
|
||||
select_folder,
|
||||
)
|
||||
from .constants import DICTATION_TYPE, DICT_TYPE, INPUT, LANGUAGES, PHRASES_TYPE
|
||||
from .constants import (
|
||||
COMPLETED_TYPE,
|
||||
DICTATION_TYPE,
|
||||
DICT_TYPE,
|
||||
INPUT,
|
||||
LANGUAGES,
|
||||
PHRASES_TYPE,
|
||||
)
|
||||
from .utility import ProcessFile, ProcessFolder
|
||||
|
||||
|
||||
@@ -24,7 +32,7 @@ def cli_choose_work_type() -> str:
|
||||
option = None
|
||||
while not option in ["s", "f", "e"]:
|
||||
option = input(
|
||||
"Please select the work option:\n" "s: single file, f:folder, e:exit\n"
|
||||
"Please select the work option:\n" "s:single file, f:folder, e:exit\n"
|
||||
)
|
||||
return option
|
||||
|
||||
@@ -66,7 +74,7 @@ def cli_select_folder() -> ProcessFolder:
|
||||
print(f"Selected {selected}")
|
||||
s = None
|
||||
while s not in ("yes", "y", "no", "n"):
|
||||
s = input("if this the folder? (yes/no, go inside)")
|
||||
s = input("if this the folder? (yes / no, go inside) : ")
|
||||
if s in ("yes", "y"):
|
||||
in_folder = selected
|
||||
else:
|
||||
@@ -100,9 +108,10 @@ def cli_select_language(languages: list = None) -> str:
|
||||
for language_id, language in avaliable_languages:
|
||||
if languages and language_id in languages:
|
||||
print(f"{language_id} - {language}")
|
||||
print("all - All languages")
|
||||
s = None
|
||||
while not s or s not in LANGUAGES.AvailableLanguages:
|
||||
lan_codes = [lan_id for lan_id, lan in avaliable_languages]
|
||||
while not s or s not in LANGUAGES.AvailableLanguages + ("all",):
|
||||
lan_codes = [lan_id for lan_id, lan in avaliable_languages] + ["all"]
|
||||
s = input(f"Please select the language {', '.join(lan_codes)}: ")
|
||||
return s
|
||||
|
||||
@@ -137,8 +146,16 @@ def main():
|
||||
language_id = cli_select_language()
|
||||
print(f"processing file {input_file} with language {language_id}")
|
||||
process_a_dictation_file(input_file, language_id)
|
||||
elif COMPLETED_TYPE in input_file.input_file.suffixes:
|
||||
language_id = input_file.input_file.suffixes[1][1:]
|
||||
print(f"processing file {input_file} with language {language_id}")
|
||||
proccess_a_completed_file(input_file, language_id)
|
||||
elif option == "f":
|
||||
for language_id in LANGUAGES.AvailableLanguages:
|
||||
language_id = cli_select_language()
|
||||
langs = (
|
||||
LANGUAGES.AvailableLanguages if language_id == "all" else [language_id]
|
||||
)
|
||||
for language_id in langs:
|
||||
in_folder = cli_select_folder()
|
||||
print(f"Selected: {in_folder} with language {language_id}")
|
||||
for dirpath, dirnames, filenames in tuple(
|
||||
|
||||
@@ -13,7 +13,13 @@ from pinyin_tone_converter.pinyin_tone_converter import PinyinToneConverter
|
||||
|
||||
# Local
|
||||
from .constants import DICTATION_TYPE, DICT_TYPE, PHRASES_TYPE
|
||||
from .utility import DictionaryResult, ProcessFile, ProcessFolder, TranslationResult
|
||||
from .utility import (
|
||||
DictionaryResult,
|
||||
ProcessFile,
|
||||
ProcessFolder,
|
||||
SimpleResult,
|
||||
TranslationResult,
|
||||
)
|
||||
|
||||
# Constants
|
||||
|
||||
@@ -137,9 +143,86 @@ HSK_MODEL = Model(
|
||||
css=CSS,
|
||||
)
|
||||
|
||||
SIMPLE_HSK_MODEL = Model(
|
||||
2819647620,
|
||||
"Simple HSK Model",
|
||||
fields=[
|
||||
{"name": "Meaning"},
|
||||
{"name": "Pinyin"},
|
||||
{"name": "Simplified"},
|
||||
{"name": "Audio"},
|
||||
],
|
||||
templates=[
|
||||
{
|
||||
"name": "Card 1",
|
||||
"qfmt": (
|
||||
"<strong>{{Pinyin}}</strong>"
|
||||
"<br>{{Meaning}}"
|
||||
"<br>{{Audio}}"
|
||||
"<br>Simplified: {{type:Simplified}}"
|
||||
),
|
||||
"afmt": (
|
||||
"{{FrontSide}}<hr id='answer''><div class='simple'>{{Simplified}}</div>"
|
||||
),
|
||||
},
|
||||
{
|
||||
"name": "Card 2",
|
||||
"qfmt": (
|
||||
"<div class='simple'>{{Simplified}}</div>" "<br>Pinyin: {{type:Pinyin}}"
|
||||
),
|
||||
"afmt": (
|
||||
"{{FrontSide}}<hr id='answer'><strong>{{Pinyin}}</strong>"
|
||||
"<br>{{Meaning}}<br>{{Audio}}"
|
||||
),
|
||||
},
|
||||
{
|
||||
"name": "Card 3",
|
||||
"qfmt": ("{{Audio}}" "<br>Simplified: {{type:Simplified}}"),
|
||||
"afmt": (
|
||||
"{{FrontSide}}<hr id='answer'><strong>{{Pinyin}}</strong>"
|
||||
"<br><div class='simple'>{{Simplified}}</div>"
|
||||
),
|
||||
},
|
||||
],
|
||||
css=CSS,
|
||||
)
|
||||
|
||||
# Proccess
|
||||
|
||||
|
||||
def output_anki_completed(
|
||||
process_file: ProcessFile, results: list[SimpleResult]
|
||||
) -> Path:
|
||||
"""Creates an anki file for a completed file"""
|
||||
final_file = (
|
||||
process_file.output_name.parent / f"{process_file.input_file.stem}.apkg"
|
||||
)
|
||||
deck_name = "::".join(process_file.input_file.parts[:-1] + (final_file.stem,))
|
||||
deck = Deck(
|
||||
random.randrange(1 << 30, 1 << 31),
|
||||
deck_name,
|
||||
f"Deck for {process_file.input_file.stem}, "
|
||||
"created in https://www.wolfang.info.ve/hskankicreator/",
|
||||
)
|
||||
audios = []
|
||||
for result in results:
|
||||
note = Note(
|
||||
model=SIMPLE_HSK_MODEL,
|
||||
fields=[
|
||||
result.meaning,
|
||||
PinyinToneConverter().convert_text(result.pinyin),
|
||||
result.character,
|
||||
f"[sound:{result.audio_path.name}]",
|
||||
],
|
||||
)
|
||||
deck.add_note(note)
|
||||
audios.append(result.audio_path)
|
||||
package = Package(deck)
|
||||
package.media_files = audios
|
||||
package.write_to_file(final_file)
|
||||
return final_file
|
||||
|
||||
|
||||
def output_anki_dictation(
|
||||
process_file: ProcessFile, results: list[DictionaryResult]
|
||||
) -> Path:
|
||||
|
||||
@@ -9,12 +9,14 @@ from pathlib import Path
|
||||
# Local
|
||||
from . import DATA_FOLDER
|
||||
from .anki_generation import (
|
||||
output_anki_completed,
|
||||
output_anki_dictation,
|
||||
output_anki_dictionary,
|
||||
output_anki_package,
|
||||
output_anki_phrase,
|
||||
)
|
||||
from .constants import (
|
||||
COMPLETED_TYPE,
|
||||
DICTATION_TYPE,
|
||||
DICT_TYPE,
|
||||
INPUT,
|
||||
@@ -24,6 +26,7 @@ from .constants import (
|
||||
RESOURCES,
|
||||
)
|
||||
from .proccessor import (
|
||||
completed_process,
|
||||
dictation_process,
|
||||
dictionary_bulk_process,
|
||||
dictionary_pre_process,
|
||||
@@ -249,6 +252,18 @@ def process_a_phrases_file(process_file: ProcessFile, language_id: str) -> Path:
|
||||
return output_anki_phrase(process_file, results)
|
||||
|
||||
|
||||
def proccess_a_completed_file(process_file: ProcessFile, language_id: str) -> Path:
|
||||
"""Process a completed file"""
|
||||
process_file.language_id = language_id
|
||||
TTS.create_tts()
|
||||
with process_file.absolute_input_file.open(
|
||||
"r", encoding="utf8", newline="\n"
|
||||
) as file:
|
||||
text_lines = [line.strip() for line in file.readlines() if line.strip()]
|
||||
results = completed_process(text_lines, process_file)
|
||||
return output_anki_completed(process_file, results)
|
||||
|
||||
|
||||
def folder_proccess(process_folder: ProcessFolder, language_id: str):
|
||||
process_folder.language_id = language_id
|
||||
TTS.create_tts()
|
||||
@@ -278,4 +293,11 @@ def folder_proccess(process_folder: ProcessFolder, language_id: str):
|
||||
) as file:
|
||||
text_lines = [line.strip() for line in file.readlines() if line.strip()]
|
||||
results[process_file] = translator_process(text_lines, process_file)
|
||||
elif process_file.file_type is COMPLETED_TYPE:
|
||||
process_file.language_id = process_file.input_file.suffixes[1][1:]
|
||||
with process_file.absolute_input_file.open(
|
||||
"r", encoding="utf8", newline="\n"
|
||||
) as file:
|
||||
text_lines = [line.strip() for line in file.readlines() if line.strip()]
|
||||
results[process_file] = completed_process(text_lines, process_file)
|
||||
return output_anki_package(process_folder, results)
|
||||
|
||||
@@ -22,6 +22,7 @@ GWEN_TTS = GWEN_FOLDER / "Qwen3-TTS-12Hz-0.6B-CustomVoice"
|
||||
PHRASES_TYPE = ".phrases"
|
||||
DICT_TYPE = ".dictionary"
|
||||
DICTATION_TYPE = ".dictation"
|
||||
COMPLETED_TYPE = ".completed"
|
||||
|
||||
|
||||
class LANGUAGES:
|
||||
@@ -35,7 +36,7 @@ class LANGUAGES:
|
||||
TR = "tr"
|
||||
TH = "th"
|
||||
JP = "jp"
|
||||
AvailableLanguages = (EN, ES, FR, RU, TR, TH, JP)
|
||||
AvailableLanguages = (ES, EN, FR, RU, TR, TH, JP)
|
||||
LanguageNames = {
|
||||
EN: "English",
|
||||
ES: "Spanish",
|
||||
|
||||
@@ -10,7 +10,14 @@ import soundfile as sf
|
||||
|
||||
# Local
|
||||
from .constants import LANGUAGES
|
||||
from .utility import CCCEDICT, TTS, DictionaryResult, ProcessFile, TranslationResult
|
||||
from .utility import (
|
||||
CCCEDICT,
|
||||
TTS,
|
||||
DictionaryResult,
|
||||
ProcessFile,
|
||||
SimpleResult,
|
||||
TranslationResult,
|
||||
)
|
||||
|
||||
# Constants
|
||||
|
||||
@@ -68,7 +75,26 @@ def translator_process(
|
||||
return results
|
||||
|
||||
|
||||
def completed_process(
|
||||
text_lines: list[str], process_file: ProcessFile
|
||||
) -> list[TranslationResult]:
|
||||
"""Process a complete file, not translation necesary"""
|
||||
results = []
|
||||
dictionary = CCCEDICT.create_cedict()
|
||||
for n, line in enumerate(text_lines):
|
||||
char, meaning = line.split(maxsplit=1)
|
||||
entries = dictionary.get(char)
|
||||
pinying = entries[0].pinyin
|
||||
audio_path = process_file.resources / f"{char}.wav"
|
||||
if not audio_path.is_file():
|
||||
wavs, sr = TTS.generate(f"{char}。")
|
||||
sf.write(audio_path, wavs[0], sr)
|
||||
results.append(SimpleResult(char, pinying, meaning, audio_path))
|
||||
return results
|
||||
|
||||
|
||||
def dictionary_bulk_process(words_list: list[str], process_file: ProcessFile):
|
||||
dictionary_en = CCCEDICT.create_cedict(LANGUAGES.EN)
|
||||
dictionary = CCCEDICT.create_cedict(process_file.language_id)
|
||||
with process_file.dictionary_resource_file.open(
|
||||
"w", encoding="utf8", newline=""
|
||||
@@ -81,30 +107,25 @@ def dictionary_bulk_process(words_list: list[str], process_file: ProcessFile):
|
||||
for words in words_list:
|
||||
word = words.split()[0]
|
||||
hint = " ".join(words.split()[1:]) if len(words.split()) > 1 else None
|
||||
if entries := dictionary.get(word):
|
||||
if len(entries) > 1:
|
||||
print(f"\nWARNING: {word} has multiple meanings:")
|
||||
entries_en = dictionary_en.get(word)
|
||||
entries = dictionary.get(word)
|
||||
if entries:
|
||||
pos_meanings = (
|
||||
[
|
||||
entry
|
||||
for entry in entries
|
||||
for meaning in entry.meanings
|
||||
if hint in meaning
|
||||
meaning
|
||||
for entry, entry_en in zip(entries, entries_en)
|
||||
for meaning, meaning_en in zip(
|
||||
entry.meanings, entry_en.meanings
|
||||
)
|
||||
if hint in meaning_en or hint in meaning
|
||||
]
|
||||
if hint
|
||||
else []
|
||||
else [meaning for entry in entries for meaning in entry.meanings]
|
||||
)
|
||||
pos_entries = pos_meanings or entries
|
||||
meanings = []
|
||||
for entry in pos_entries:
|
||||
for meaning in entry.meanings:
|
||||
if hint and hint in meaning:
|
||||
meanings.append(meaning)
|
||||
if not meanings:
|
||||
meanings = [
|
||||
meaning for entry in pos_entries for meaning in entry.meanings
|
||||
]
|
||||
meanings_text = meanings[0]
|
||||
meanings_text = "\n".join(
|
||||
f"{n}: {meaning}" for n, meaning in enumerate(pos_meanings)
|
||||
)
|
||||
entry = entries[0]
|
||||
tsv_writer.writerow(
|
||||
{
|
||||
"n": number,
|
||||
|
||||
@@ -18,6 +18,7 @@ from qwen_tts import Qwen3TTSModel
|
||||
# Local
|
||||
from .constants import (
|
||||
CCCEDICT_PATH,
|
||||
COMPLETED_TYPE,
|
||||
DICTATION_TYPE,
|
||||
DICT_TYPE,
|
||||
GWEN_TTS,
|
||||
@@ -55,6 +56,7 @@ class TRANS:
|
||||
)
|
||||
print(f"available packages {packages[:5]}")
|
||||
packages_to_install = []
|
||||
ready = False
|
||||
for in_package in packages:
|
||||
if in_package.from_code == from_code:
|
||||
if in_package.to_code == to_code:
|
||||
@@ -64,7 +66,12 @@ class TRANS:
|
||||
f"->{in_package.to_code}"
|
||||
)
|
||||
packages_to_install.append(in_package)
|
||||
ready = True
|
||||
break
|
||||
for in_package in packages:
|
||||
if ready:
|
||||
break
|
||||
if in_package.from_code == from_code:
|
||||
for out_package in packages:
|
||||
if out_package.to_code == to_code:
|
||||
if in_package.to_code == out_package.from_code:
|
||||
@@ -78,6 +85,7 @@ class TRANS:
|
||||
)
|
||||
packages_to_install.append(in_package)
|
||||
packages_to_install.append(out_package)
|
||||
ready = True
|
||||
break
|
||||
for package in packages_to_install:
|
||||
print(f"instaling package {package}")
|
||||
@@ -110,6 +118,7 @@ class TranslatedEntry:
|
||||
@property
|
||||
def meanings(self):
|
||||
"""Entry translated meaning list"""
|
||||
if not self._translated_meanings:
|
||||
for meaning in self.entry.meanings:
|
||||
if self.language_id != LANGUAGES.EN:
|
||||
print(f"translating from {LANGUAGES.EN} to {self.language_id}")
|
||||
@@ -227,7 +236,7 @@ class ProcessFolder:
|
||||
def input_files(self):
|
||||
input_files = []
|
||||
for file in self.absolute_input_folder.glob(f"*.txt"):
|
||||
for file_type in (DICT_TYPE, PHRASES_TYPE, DICTATION_TYPE):
|
||||
for file_type in (DICT_TYPE, PHRASES_TYPE, DICTATION_TYPE, COMPLETED_TYPE):
|
||||
if file_type in file.suffixes:
|
||||
input_files.append(
|
||||
ProcessFile(
|
||||
@@ -264,9 +273,18 @@ class ProcessFile:
|
||||
# process file type
|
||||
self.out_folder = OUTPUT / input_file.parent
|
||||
self.out_folder.mkdir(parents=True, exist_ok=True)
|
||||
resources = RESOURCES / input_file
|
||||
self.resources = resources.parent / resources.stem
|
||||
self.resources.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@property
|
||||
def resources(self):
|
||||
resources = RESOURCES / self.input_file
|
||||
if self.file_type is COMPLETED_TYPE:
|
||||
resources = (
|
||||
resources.parent / f"{self.input_file.name.split(".")[0]}.dictionary"
|
||||
)
|
||||
else:
|
||||
resources = resources.parent / resources.stem
|
||||
resources.mkdir(parents=True, exist_ok=True)
|
||||
return resources
|
||||
|
||||
@property
|
||||
def file_type(self):
|
||||
@@ -276,6 +294,8 @@ class ProcessFile:
|
||||
return DICT_TYPE
|
||||
elif PHRASES_TYPE in self.input_file.suffixes:
|
||||
return PHRASES_TYPE
|
||||
elif COMPLETED_TYPE in self.input_file.suffixes:
|
||||
return COMPLETED_TYPE
|
||||
else:
|
||||
raise ValueError("File type not recognized")
|
||||
|
||||
@@ -320,6 +340,16 @@ class ProcessFile:
|
||||
return f"Proccess:{self.input_file}"
|
||||
|
||||
|
||||
class SimpleResult:
|
||||
"""Simplied result class"""
|
||||
|
||||
def __init__(self, character: str, pinyin: str, meaning: str, audio_path: Path):
|
||||
self.character = character
|
||||
self.pinyin = pinyin
|
||||
self.meaning = meaning
|
||||
self.audio_path = audio_path
|
||||
|
||||
|
||||
class TranslationResult:
|
||||
"""Result of a translated process"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user