diff --git a/LunaTranslator/LunaTranslator/cishu/youdao.py b/LunaTranslator/LunaTranslator/cishu/youdao.py
index c4dd6e43..ea1e7c69 100644
--- a/LunaTranslator/LunaTranslator/cishu/youdao.py
+++ b/LunaTranslator/LunaTranslator/cishu/youdao.py
@@ -1,4 +1,4 @@
-from myutils.config import globalconfig, static_data
+from myutils.config import getlangsrc
import requests
from urllib.parse import quote
import re
@@ -11,7 +11,7 @@ class youdao(cishubase):
def srclang(self):
try:
- l = static_data["language_list_translator_inner"][globalconfig["srclang3"]]
+ l = getlangsrc()
return l
except:
diff --git a/LunaTranslator/LunaTranslator/gui/setting_lang.py b/LunaTranslator/LunaTranslator/gui/setting_lang.py
index 307ffed0..eb52861c 100644
--- a/LunaTranslator/LunaTranslator/gui/setting_lang.py
+++ b/LunaTranslator/LunaTranslator/gui/setting_lang.py
@@ -1,5 +1,5 @@
import os
-from myutils.config import globalconfig, _TRL, static_data
+from myutils.config import globalconfig, _TRL, static_data,getlanguse
from gui.usefulwidget import getsimplecombobox, getcolorbutton
@@ -46,11 +46,7 @@ def setTablanglz(self):
"",
callback=lambda: os.startfile(
os.path.abspath(
- "./files/lang/{}.json".format(
- static_data["language_list_translator_inner"][
- globalconfig["languageuse"]
- ]
- )
+ "./files/lang/{}.json".format(getlanguse())
)
),
icon="fa.gear",
diff --git a/LunaTranslator/LunaTranslator/gui/showword.py b/LunaTranslator/LunaTranslator/gui/showword.py
index c0519245..d3ba726d 100644
--- a/LunaTranslator/LunaTranslator/gui/showword.py
+++ b/LunaTranslator/LunaTranslator/gui/showword.py
@@ -44,14 +44,14 @@ class AnkiWindow(QWidget):
def langdu(self):
if gobject.baseobject.reader:
- self.audiopath.setText(
- gobject.baseobject.reader.syncttstofile(self.currentword)
+ gobject.baseobject.reader.ttscallback(
+ self.currentword, self.audiopath.setText
)
def langdu2(self):
if gobject.baseobject.reader:
- self.audiopath_sentence.setText(
- gobject.baseobject.reader.syncttstofile(self.example.toPlainText())
+ gobject.baseobject.reader.ttscallback(
+ self.example.toPlainText(), self.audiopath_sentence.setText
)
@threader
@@ -205,27 +205,41 @@ class AnkiWindow(QWidget):
def loadfakefields(self):
if len(self.editpath.text()):
- with open(self.editpath.text(), "rb") as image_file:
- encoded_string = base64.b64encode(image_file.read()).decode("utf-8")
- encoded_string = ''.format(
- encoded_string
- )
+ try:
+ with open(self.editpath.text(), "rb") as image_file:
+ encoded_string = base64.b64encode(image_file.read()).decode("utf-8")
+ encoded_string = ''.format(
+ encoded_string
+ )
+ except:
+ encoded_string = ""
else:
encoded_string = ""
if len(self.audiopath.text()):
- with open(self.audiopath.text(), "rb") as image_file:
- encoded_string2 = base64.b64encode(image_file.read()).decode("utf-8")
- encoded_string2 = """""".format(
- encoded_string2
- )
+ try:
+ with open(self.audiopath.text(), "rb") as image_file:
+ encoded_string2 = base64.b64encode(image_file.read()).decode(
+ "utf-8"
+ )
+ encoded_string2 = """""".format(
+ encoded_string2
+ )
+ except:
+ encoded_string2 = ""
else:
encoded_string2 = ""
if len(self.audiopath_sentence.text()):
- with open(self.audiopath_sentence.text(), "rb") as image_file:
- encoded_string3 = base64.b64encode(image_file.read()).decode("utf-8")
- encoded_string3 = """""".format(
- encoded_string3
- )
+ try:
+ with open(self.audiopath_sentence.text(), "rb") as image_file:
+ encoded_string3 = base64.b64encode(image_file.read()).decode(
+ "utf-8"
+ )
+ encoded_string3 = """""".format(
+ encoded_string3
+ )
+ except:
+
+ encoded_string3 = ""
else:
encoded_string3 = ""
fields = {
diff --git a/LunaTranslator/LunaTranslator/myutils/commonbase.py b/LunaTranslator/LunaTranslator/myutils/commonbase.py
index 66d0aa30..ff34cf81 100644
--- a/LunaTranslator/LunaTranslator/myutils/commonbase.py
+++ b/LunaTranslator/LunaTranslator/myutils/commonbase.py
@@ -1,5 +1,5 @@
from myutils.proxy import getproxy
-from myutils.config import globalconfig, _TR, static_data
+from myutils.config import getlangtgt, _TR, static_data, getlangsrc
from myutils.wrapper import stripwrapper
import requests
@@ -34,7 +34,7 @@ class commonbase:
@property
def srclang(self):
try:
- l = static_data["language_list_translator_inner"][globalconfig["srclang3"]]
+ l = getlangsrc()
return self.langmap_[l]
except:
return ""
@@ -42,7 +42,7 @@ class commonbase:
@property
def tgtlang(self):
try:
- l = static_data["language_list_translator_inner"][globalconfig["tgtlang3"]]
+ l = getlangtgt()
return self.langmap_[l]
except:
return ""
diff --git a/LunaTranslator/LunaTranslator/myutils/config.py b/LunaTranslator/LunaTranslator/myutils/config.py
index 736c9a78..790f2609 100644
--- a/LunaTranslator/LunaTranslator/myutils/config.py
+++ b/LunaTranslator/LunaTranslator/myutils/config.py
@@ -187,14 +187,22 @@ if len(globalconfig["toolbutton"]["rank"]) != len(
)
+def getlanguse():
+ global language, languageshow
+ return static_data["language_list_translator_inner"][language]
+
+def getlangsrc():
+ return static_data["language_list_translator_inner"][globalconfig["srclang3"]]
+
+def getlangtgt():
+ return static_data["language_list_translator_inner"][globalconfig["tgtlang3"]]
+
def setlanguage():
global language, languageshow
language = globalconfig["languageuse"]
try:
with open(
- "./files/lang/{}.json".format(
- static_data["language_list_translator_inner"][language]
- ),
+ "./files/lang/{}.json".format(getlanguse()),
"r",
encoding="utf8",
) as ff:
@@ -255,8 +263,6 @@ def saveallconfig():
"./userconfig/savehook_new_1.39.4.json", [savehook_new_list, savehook_new_data]
)
safesave(
- "./files/lang/{}.json".format(
- static_data["language_list_translator_inner"][language]
- ),
+ "./files/lang/{}.json".format(getlanguse()),
languageshow,
)
diff --git a/LunaTranslator/LunaTranslator/myutils/post.py b/LunaTranslator/LunaTranslator/myutils/post.py
index 6f31971f..7c32d55b 100644
--- a/LunaTranslator/LunaTranslator/myutils/post.py
+++ b/LunaTranslator/LunaTranslator/myutils/post.py
@@ -7,7 +7,7 @@ from myutils.config import (
postprocessconfig,
globalconfig,
savehook_new_data,
- static_data,
+ getlangsrc,
)
@@ -179,7 +179,7 @@ def _4_f(line):
def _6_fEX(line):
- srclang = static_data["language_list_translator_inner"][globalconfig["srclang3"]]
+ srclang = getlangsrc()
if srclang in ["zh", "ja"]:
white = ""
else:
diff --git a/LunaTranslator/LunaTranslator/myutils/utils.py b/LunaTranslator/LunaTranslator/myutils/utils.py
index db1c070b..89261ffd 100644
--- a/LunaTranslator/LunaTranslator/myutils/utils.py
+++ b/LunaTranslator/LunaTranslator/myutils/utils.py
@@ -15,6 +15,7 @@ from traceback import print_exc
from myutils.config import (
globalconfig,
static_data,
+ getlanguse,
savehook_new_list,
savehook_new_data,
getdefaultsavehook,
@@ -537,10 +538,6 @@ def parsemayberegexreplace(dict, res):
return res
-def getlanguse():
- return static_data["language_list_translator_inner"][globalconfig["languageuse"]]
-
-
def checkpostlangmatch(name):
for item in static_data["transoptimi"]:
if name == item["name"]:
diff --git a/LunaTranslator/LunaTranslator/ocrengines/local.py b/LunaTranslator/LunaTranslator/ocrengines/local.py
index 0d65827e..13d41a70 100644
--- a/LunaTranslator/LunaTranslator/ocrengines/local.py
+++ b/LunaTranslator/LunaTranslator/ocrengines/local.py
@@ -1,5 +1,5 @@
import os
-from myutils.config import globalconfig, _TR, static_data
+from myutils.config import globalconfig, _TR, getlangsrc
from ocrengines.baseocrclass import baseocr
from ctypes import (
CDLL,
@@ -131,9 +131,7 @@ class OCR(baseocr):
return
self._ocr.trydestroy()
- path = "./files/ocr/{}".format(
- static_data["language_list_translator_inner"][globalconfig["srclang3"]]
- )
+ path = "./files/ocr/{}".format(getlangsrc())
if not (
os.path.exists(path + "/det.onnx")
and os.path.exists(path + "/rec.onnx")
diff --git a/LunaTranslator/LunaTranslator/translator/basetranslator.py b/LunaTranslator/LunaTranslator/translator/basetranslator.py
index 56468dbb..9aa4f42f 100644
--- a/LunaTranslator/LunaTranslator/translator/basetranslator.py
+++ b/LunaTranslator/LunaTranslator/translator/basetranslator.py
@@ -1,7 +1,7 @@
from traceback import print_exc
from queue import Queue
-from myutils.config import globalconfig, translatorsetting, static_data
+from myutils.config import globalconfig, translatorsetting, getlangtgt
from threading import Thread
import time, types
import zhconv, gobject
@@ -172,7 +172,7 @@ class basetrans(commonbase):
@property
def needzhconv(self):
# The API does not support direct translation to Traditional Chinese, only Simplified Chinese can be translated first and then converted to Traditional Chinese
- l = static_data["language_list_translator_inner"][globalconfig["tgtlang3"]]
+ l = getlangtgt()
return l == "cht" and "cht" not in self.langmap()
@property
diff --git a/LunaTranslator/LunaTranslator/tts/basettsclass.py b/LunaTranslator/LunaTranslator/tts/basettsclass.py
index 3308bd0e..7ec411d3 100644
--- a/LunaTranslator/LunaTranslator/tts/basettsclass.py
+++ b/LunaTranslator/LunaTranslator/tts/basettsclass.py
@@ -1,6 +1,7 @@
from myutils.config import globalconfig
-import threading, os
-
+import threading, os, functools
+from myutils.wrapper import threader
+from traceback import print_exc
class TTSbase:
def init(self):
@@ -19,15 +20,16 @@ class TTSbase:
# 一些可能需要的属性
@property
def config(self):
- return self.privateconfig['args']
+ return self.privateconfig["args"]
@property
def privateconfig(self):
return globalconfig["reader"][self.typename]
-
+
@property
def publicconfig(self):
return globalconfig["ttscommon"]
+
########################
def __init__(self, typename, showlistsignal, mp3playsignal) -> None:
@@ -58,15 +60,14 @@ class TTSbase:
threading.Thread(target=_).start()
def read(self, content, force=False):
- def _(content, force):
- fname = self.syncttstofile(content)
+ def _(force, fname):
volume = self.publicconfig["volume"]
- if fname:
- self.mp3playsignal.emit(fname, volume, force)
+ self.mp3playsignal.emit(fname, volume, force)
- threading.Thread(target=_, args=(content, force)).start()
+ self.ttscallback(content, functools.partial(_, force))
- def syncttstofile(self, content):
+ @threader
+ def ttscallback(self, content, callback):
if self.loadok == False:
return
if len(content) == 0:
@@ -77,5 +78,10 @@ class TTSbase:
rate = self.publicconfig["rate"]
voice = self.privateconfig["voice"]
voice_index = self.voicelist.index(voice)
- fname = self.speak(content, rate, voice, voice_index)
- return os.path.abspath(fname)
+ try:
+ fname = self.speak(content, rate, voice, voice_index)
+ if fname:
+ callback(os.path.abspath(fname))
+ except:
+ print_exc()
+ return
diff --git a/LunaTranslator/LunaTranslator/tts/gtts.py b/LunaTranslator/LunaTranslator/tts/gtts.py
new file mode 100644
index 00000000..2385d1c1
--- /dev/null
+++ b/LunaTranslator/LunaTranslator/tts/gtts.py
@@ -0,0 +1,1093 @@
+# -*- coding: utf-8 -*-
+import base64
+import json, time
+import logging, os
+import re
+import urllib
+from myutils.proxy import getproxy
+import requests
+
+_langs = {
+ "af": "Afrikaans",
+ "ar": "Arabic",
+ "bg": "Bulgarian",
+ "bn": "Bengali",
+ "bs": "Bosnian",
+ "ca": "Catalan",
+ "cs": "Czech",
+ "da": "Danish",
+ "de": "German",
+ "el": "Greek",
+ "en": "English",
+ "es": "Spanish",
+ "et": "Estonian",
+ "fi": "Finnish",
+ "fr": "French",
+ "gu": "Gujarati",
+ "hi": "Hindi",
+ "hr": "Croatian",
+ "hu": "Hungarian",
+ "id": "Indonesian",
+ "is": "Icelandic",
+ "it": "Italian",
+ "iw": "Hebrew",
+ "ja": "Japanese",
+ "jw": "Javanese",
+ "km": "Khmer",
+ "kn": "Kannada",
+ "ko": "Korean",
+ "la": "Latin",
+ "lv": "Latvian",
+ "ml": "Malayalam",
+ "mr": "Marathi",
+ "ms": "Malay",
+ "my": "Myanmar (Burmese)",
+ "ne": "Nepali",
+ "nl": "Dutch",
+ "no": "Norwegian",
+ "pl": "Polish",
+ "pt": "Portuguese",
+ "ro": "Romanian",
+ "ru": "Russian",
+ "si": "Sinhala",
+ "sk": "Slovak",
+ "sq": "Albanian",
+ "sr": "Serbian",
+ "su": "Sundanese",
+ "sv": "Swedish",
+ "sw": "Swahili",
+ "ta": "Tamil",
+ "te": "Telugu",
+ "th": "Thai",
+ "tl": "Filipino",
+ "tr": "Turkish",
+ "uk": "Ukrainian",
+ "ur": "Urdu",
+ "vi": "Vietnamese",
+ "zh-CN": "Chinese (Simplified)",
+ "zh-TW": "Chinese (Traditional)",
+}
+
+
+def _main_langs():
+ return _langs
+
+
+from warnings import warn
+import logging
+
+__all__ = ["tts_langs"]
+
+# Logger
+log = logging.getLogger(__name__)
+log.addHandler(logging.NullHandler())
+
+
+def tts_langs():
+ """Languages Google Text-to-Speech supports.
+
+ Returns:
+ dict: A dictionary of the type `{ '': ''}`
+
+ Where `` is an IETF language tag such as `en` or `zh-TW`,
+ and `` is the full English name of the language, such as
+ `English` or `Chinese (Mandarin/Taiwan)`.
+
+ The dictionary returned combines languages from two origins:
+
+ - Languages fetched from Google Translate (pre-generated in :mod:`gtts.langs`)
+ - Languages that are undocumented variations that were observed to work and
+ present different dialects or accents.
+
+ """
+ langs = dict()
+ langs.update(_main_langs())
+ langs.update(_extra_langs())
+ log.debug("langs: {}".format(langs))
+ return langs
+
+
+def _extra_langs():
+ """Define extra languages.
+
+ Returns:
+ dict: A dictionary of extra languages manually defined.
+
+ Variations of the ones generated in `_main_langs`,
+ observed to provide different dialects or accents or
+ just simply accepted by the Google Translate Text-to-Speech API.
+
+ """
+ return {
+ # Chinese
+ "zh-TW": "Chinese (Mandarin/Taiwan)",
+ "zh": "Chinese (Mandarin)",
+ }
+
+
+def _fallback_deprecated_lang(lang):
+ """Languages Google Text-to-Speech used to support.
+
+ Language tags that don't work anymore, but that can
+ fallback to a more general language code to maintain
+ compatibility.
+
+ Args:
+ lang (string): The language tag.
+
+ Returns:
+ string: The language tag, as-is if not deprecated,
+ or a fallback if it exits.
+
+ Example:
+ ``en-GB`` returns ``en``.
+ ``en-gb`` returns ``en``.
+
+ """
+
+ deprecated = {
+ # '': []
+ "en": [
+ "en-us",
+ "en-ca",
+ "en-uk",
+ "en-gb",
+ "en-au",
+ "en-gh",
+ "en-in",
+ "en-ie",
+ "en-nz",
+ "en-ng",
+ "en-ph",
+ "en-za",
+ "en-tz",
+ ],
+ "fr": ["fr-ca", "fr-fr"],
+ "pt": ["pt-br", "pt-pt"],
+ "es": ["es-es", "es-us"],
+ "zh-CN": ["zh-cn"],
+ "zh-TW": ["zh-tw"],
+ }
+
+ for fallback_lang, deprecated_langs in deprecated.items():
+ if lang.lower() in deprecated_langs:
+ msg = (
+ "'{}' has been deprecated, falling back to '{}'. "
+ "This fallback will be removed in a future version."
+ ).format(lang, fallback_lang)
+
+ warn(msg, DeprecationWarning)
+ log.warning(msg)
+
+ return fallback_lang
+
+ return lang
+
+
+# -*- coding: utf-8 -*-
+import re
+
+
+class symbols:
+ # -*- coding: utf-8 -*-
+
+ ABBREVIATIONS = ["dr", "jr", "mr", "mrs", "ms", "msgr", "prof", "sr", "st"]
+
+ SUB_PAIRS = [("Esq.", "Esquire")]
+
+ ALL_PUNC = "?!?!.,¡()[]¿…‥،;:—。,、:\n"
+
+ TONE_MARKS = "?!?!"
+
+ PERIOD_COMMA = ".,"
+
+ COLON = ":"
+
+
+class RegexBuilder:
+ r"""Builds regex using arguments passed into a pattern template.
+
+ Builds a regex object for which the pattern is made from an argument
+ passed into a template. If more than one argument is passed (iterable),
+ each pattern is joined by "|" (regex alternation 'or') to create a
+ single pattern.
+
+ Args:
+ pattern_args (iteratable): String element(s) to be each passed to
+ ``pattern_func`` to create a regex pattern. Each element is
+ ``re.escape``'d before being passed.
+ pattern_func (callable): A 'template' function that should take a
+ string and return a string. It should take an element of
+ ``pattern_args`` and return a valid regex pattern group string.
+ flags: ``re`` flag(s) to compile with the regex.
+
+ Example:
+ To create a simple regex that matches on the characters "a", "b",
+ or "c", followed by a period::
+
+ >>> rb = RegexBuilder('abc', lambda x: "{}\.".format(x))
+
+ Looking at ``rb.regex`` we get the following compiled regex::
+
+ >>> print(rb.regex)
+ 'a\.|b\.|c\.'
+
+ The above is fairly simple, but this class can help in writing more
+ complex repetitive regex, making them more readable and easier to
+ create by using existing data structures.
+
+ Example:
+ To match the character following the words "lorem", "ipsum", "meili"
+ or "koda"::
+
+ >>> words = ['lorem', 'ipsum', 'meili', 'koda']
+ >>> rb = RegexBuilder(words, lambda x: "(?<={}).".format(x))
+
+ Looking at ``rb.regex`` we get the following compiled regex::
+
+ >>> print(rb.regex)
+ '(?<=lorem).|(?<=ipsum).|(?<=meili).|(?<=koda).'
+
+ """
+
+ def __init__(self, pattern_args, pattern_func, flags=0):
+ self.pattern_args = pattern_args
+ self.pattern_func = pattern_func
+ self.flags = flags
+
+ # Compile
+ self.regex = self._compile()
+
+ def _compile(self):
+ alts = []
+ for arg in self.pattern_args:
+ arg = re.escape(arg)
+ alt = self.pattern_func(arg)
+ alts.append(alt)
+
+ pattern = "|".join(alts)
+ return re.compile(pattern, self.flags)
+
+ def __repr__(self): # pragma: no cover
+ return str(self.regex)
+
+
+class PreProcessorRegex:
+ r"""Regex-based substitution text pre-processor.
+
+ Runs a series of regex substitutions (``re.sub``) from each ``regex`` of a
+ :class:`gtts.tokenizer.core.RegexBuilder` with an extra ``repl``
+ replacement parameter.
+
+ Args:
+ search_args (iteratable): String element(s) to be each passed to
+ ``search_func`` to create a regex pattern. Each element is
+ ``re.escape``'d before being passed.
+ search_func (callable): A 'template' function that should take a
+ string and return a string. It should take an element of
+ ``search_args`` and return a valid regex search pattern string.
+ repl (string): The common replacement passed to the ``sub`` method for
+ each ``regex``. Can be a raw string (the case of a regex
+ backreference, for example)
+ flags: ``re`` flag(s) to compile with each `regex`.
+
+ Example:
+ Add "!" after the words "lorem" or "ipsum", while ignoring case::
+
+ >>> import re
+ >>> words = ['lorem', 'ipsum']
+ >>> pp = PreProcessorRegex(words,
+ ... lambda x: "({})".format(x), r'\\1!',
+ ... re.IGNORECASE)
+
+ In this case, the regex is a group and the replacement uses its
+ backreference ``\\1`` (as a raw string). Looking at ``pp`` we get the
+ following list of search/replacement pairs::
+
+ >>> print(pp)
+ (re.compile('(lorem)', re.IGNORECASE), repl='\1!'),
+ (re.compile('(ipsum)', re.IGNORECASE), repl='\1!')
+
+ It can then be run on any string of text::
+
+ >>> pp.run("LOREM ipSuM")
+ "LOREM! ipSuM!"
+
+ See :mod:`gtts.tokenizer.pre_processors` for more examples.
+
+ """
+
+ def __init__(self, search_args, search_func, repl, flags=0):
+ self.repl = repl
+
+ # Create regex list
+ self.regexes = []
+ for arg in search_args:
+ rb = RegexBuilder([arg], search_func, flags)
+ self.regexes.append(rb.regex)
+
+ def run(self, text):
+ """Run each regex substitution on ``text``.
+
+ Args:
+ text (string): the input text.
+
+ Returns:
+ string: text after all substitutions have been sequentially
+ applied.
+
+ """
+ for regex in self.regexes:
+ text = regex.sub(self.repl, text)
+ return text
+
+ def __repr__(self): # pragma: no cover
+ subs_strs = []
+ for r in self.regexes:
+ subs_strs.append("({}, repl='{}')".format(r, self.repl))
+ return ", ".join(subs_strs)
+
+
+class PreProcessorSub:
+ r"""Simple substitution text preprocessor.
+
+ Performs string-for-string substitution from list a find/replace pairs.
+ It abstracts :class:`gtts.tokenizer.core.PreProcessorRegex` with a default
+ simple substitution regex.
+
+ Args:
+ sub_pairs (list): A list of tuples of the style
+ ``(, )``
+ ignore_case (bool): Ignore case during search. Defaults to ``True``.
+
+ Example:
+ Replace all occurences of "Mac" to "PC" and "Firefox" to "Chrome"::
+
+ >>> sub_pairs = [('Mac', 'PC'), ('Firefox', 'Chrome')]
+ >>> pp = PreProcessorSub(sub_pairs)
+
+ Looking at the ``pp``, we get the following list of
+ search (regex)/replacement pairs::
+
+ >>> print(pp)
+ (re.compile('Mac', re.IGNORECASE), repl='PC'),
+ (re.compile('Firefox', re.IGNORECASE), repl='Chrome')
+
+ It can then be run on any string of text::
+
+ >>> pp.run("I use firefox on my mac")
+ "I use Chrome on my PC"
+
+ See :mod:`gtts.tokenizer.pre_processors` for more examples.
+
+ """
+
+ def __init__(self, sub_pairs, ignore_case=True):
+ def search_func(x):
+ return "{}".format(x)
+
+ flags = re.I if ignore_case else 0
+
+ # Create pre-processor list
+ self.pre_processors = []
+ for sub_pair in sub_pairs:
+ pattern, repl = sub_pair
+ pp = PreProcessorRegex([pattern], search_func, repl, flags)
+ self.pre_processors.append(pp)
+
+ def run(self, text):
+ """Run each substitution on ``text``.
+
+ Args:
+ text (string): the input text.
+
+ Returns:
+ string: text after all substitutions have been sequentially
+ applied.
+
+ """
+ for pp in self.pre_processors:
+ text = pp.run(text)
+ return text
+
+ def __repr__(self): # pragma: no cover
+ return ", ".join([str(pp) for pp in self.pre_processors])
+
+
+class Tokenizer:
+ r"""An extensible but simple generic rule-based tokenizer.
+
+ A generic and simple string tokenizer that takes a list of functions
+ (called `tokenizer cases`) returning ``regex`` objects and joins them by
+ "|" (regex alternation 'or') to create a single regex to use with the
+ standard ``regex.split()`` function.
+
+ ``regex_funcs`` is a list of any function that can return a ``regex``
+ (from ``re.compile()``) object, such as a
+ :class:`gtts.tokenizer.core.RegexBuilder` instance (and its ``regex``
+ attribute).
+
+ See the :mod:`gtts.tokenizer.tokenizer_cases` module for examples.
+
+ Args:
+ regex_funcs (list): List of compiled ``regex`` objects. Each
+ function's pattern will be joined into a single pattern and
+ compiled.
+ flags: ``re`` flag(s) to compile with the final regex. Defaults to
+ ``re.IGNORECASE``
+
+ Note:
+ When the ``regex`` objects obtained from ``regex_funcs`` are joined,
+ their individual ``re`` flags are ignored in favour of ``flags``.
+
+ Raises:
+ TypeError: When an element of ``regex_funcs`` is not a function, or
+ a function that does not return a compiled ``regex`` object.
+
+ Warning:
+ Joined ``regex`` patterns can easily interfere with one another in
+ unexpected ways. It is recommanded that each tokenizer case operate
+ on distinct or non-overlapping chracters/sets of characters
+ (For example, a tokenizer case for the period (".") should also
+ handle not matching/cutting on decimals, instead of making that
+ a seperate tokenizer case).
+
+ Example:
+ A tokenizer with a two simple case (*Note: these are bad cases to
+ tokenize on, this is simply a usage example*)::
+
+ >>> import re, RegexBuilder
+ >>>
+ >>> def case1():
+ ... return re.compile("\,")
+ >>>
+ >>> def case2():
+ ... return RegexBuilder('abc', lambda x: "{}\.".format(x)).regex
+ >>>
+ >>> t = Tokenizer([case1, case2])
+
+ Looking at ``case1().pattern``, we get::
+
+ >>> print(case1().pattern)
+ '\\,'
+
+ Looking at ``case2().pattern``, we get::
+
+ >>> print(case2().pattern)
+ 'a\\.|b\\.|c\\.'
+
+ Finally, looking at ``t``, we get them combined::
+
+ >>> print(t)
+ 're.compile('\\,|a\\.|b\\.|c\\.', re.IGNORECASE)
+ from: [, ]'
+
+ It can then be run on any string of text::
+
+ >>> t.run("Hello, my name is Linda a. Call me Lin, b. I'm your friend")
+ ['Hello', ' my name is Linda ', ' Call me Lin', ' ', " I'm your friend"]
+
+ """
+
+ def __init__(self, regex_funcs, flags=re.IGNORECASE):
+ self.regex_funcs = regex_funcs
+ self.flags = flags
+
+ try:
+ # Combine
+ self.total_regex = self._combine_regex()
+ except (TypeError, AttributeError) as e: # pragma: no cover
+ raise TypeError(
+ "Tokenizer() expects a list of functions returning "
+ "regular expression objects (i.e. re.compile). " + str(e)
+ )
+
+ def _combine_regex(self):
+ alts = []
+ for func in self.regex_funcs:
+ alts.append(func())
+
+ pattern = "|".join(alt.pattern for alt in alts)
+ return re.compile(pattern, self.flags)
+
+ def run(self, text):
+ """Tokenize `text`.
+
+ Args:
+ text (string): the input text to tokenize.
+
+ Returns:
+ list: A list of strings (token) split according to the tokenizer cases.
+
+ """
+ return self.total_regex.split(text)
+
+ def __repr__(self): # pragma: no cover
+ return str(self.total_regex) + " from: " + str(self.regex_funcs)
+
+
+class tokenizer_cases:
+
+ def tone_marks():
+ """Keep tone-modifying punctuation by matching following character.
+
+ Assumes the `tone_marks` pre-processor was run for cases where there might
+ not be any space after a tone-modifying punctuation mark.
+ """
+ return RegexBuilder(
+ pattern_args=symbols.TONE_MARKS, pattern_func=lambda x: "(?<={}).".format(x)
+ ).regex
+
+ def period_comma():
+ """Period and comma case.
+
+ Match if not preceded by "." and only if followed by space.
+ Won't cut in the middle/after dotted abbreviations; won't cut numbers.
+
+ Note:
+ Won't match if a dotted abbreviation ends a sentence.
+
+ Note:
+ Won't match the end of a sentence if not followed by a space.
+
+ """
+ return RegexBuilder(
+ pattern_args=symbols.PERIOD_COMMA,
+ pattern_func=lambda x: r"(?".
+
+ """
+ return PreProcessorRegex(
+ search_args="-", search_func=lambda x: "{}\n".format(x), repl=""
+ ).run(text)
+
+ def abbreviations(text):
+ """Remove periods after an abbreviation from a list of known
+ abbreviations that can be spoken the same without that period. This
+ prevents having to handle tokenization of that period.
+
+ Note:
+ Could potentially remove the ending period of a sentence.
+
+ Note:
+ Abbreviations that Google Translate can't pronounce without
+ (or even with) a period should be added as a word substitution with a
+ :class:`PreProcessorSub` pre-processor. Ex.: 'Esq.', 'Esquire'.
+
+ """
+ return PreProcessorRegex(
+ search_args=symbols.ABBREVIATIONS,
+ search_func=lambda x: r"(?<={})(?=\.).".format(x),
+ repl="",
+ flags=re.IGNORECASE,
+ ).run(text)
+
+ def word_sub(text):
+ """Word-for-word substitutions."""
+ return PreProcessorSub(sub_pairs=symbols.SUB_PAIRS).run(text)
+
+
+punc = symbols.ALL_PUNC
+from string import whitespace as ws
+import re
+
+_ALL_PUNC_OR_SPACE = re.compile("^[{}]*$".format(re.escape(punc + ws)))
+"""Regex that matches if an entire line is only comprised
+of whitespace and punctuation
+
+"""
+
+
+def _minimize(the_string, delim, max_size):
+ """Recursively split a string in the largest chunks
+ possible from the highest position of a delimiter all the way
+ to a maximum size
+
+ Args:
+ the_string (string): The string to split.
+ delim (string): The delimiter to split on.
+ max_size (int): The maximum size of a chunk.
+
+ Returns:
+ list: the minimized string in tokens
+
+ Every chunk size will be at minimum ``the_string[0:idx]`` where ``idx``
+ is the highest index of ``delim`` found in ``the_string``; and at maximum
+ ``the_string[0:max_size]`` if no ``delim`` was found in ``the_string``.
+ In the latter case, the split will occur at ``the_string[max_size]``
+ which can be any character. The function runs itself again on the rest of
+ ``the_string`` (``the_string[idx:]``) until no chunk is larger than
+ ``max_size``.
+
+ """
+ # Remove `delim` from start of `the_string`
+ # i.e. prevent a recursive infinite loop on `the_string[0:0]`
+ # if `the_string` starts with `delim` and is larger than `max_size`
+ if the_string.startswith(delim):
+ the_string = the_string[len(delim) :]
+
+ if len(the_string) > max_size:
+ try:
+ # Find the highest index of `delim` in `the_string[0:max_size]`
+ # i.e. `the_string` will be cut in half on `delim` index
+ idx = the_string.rindex(delim, 0, max_size)
+ except ValueError:
+ # `delim` not found in `the_string`, index becomes `max_size`
+ # i.e. `the_string` will be cut in half arbitrarily on `max_size`
+ idx = max_size
+ # Call itself again for `the_string[idx:]`
+ return [the_string[:idx]] + _minimize(the_string[idx:], delim, max_size)
+ else:
+ return [the_string]
+
+
+def _clean_tokens(tokens):
+ """Clean a list of strings
+
+ Args:
+ tokens (list): A list of strings (tokens) to clean.
+
+ Returns:
+ list: Stripped strings ``tokens`` without the original elements
+ that only consisted of whitespace and/or punctuation characters.
+
+ """
+ return [t.strip() for t in tokens if not _ALL_PUNC_OR_SPACE.match(t)]
+
+
+def _translate_url(tld="com", path=""):
+ """Generates a Google Translate URL
+
+ Args:
+ tld (string): Top-level domain for the Google Translate host,
+ i.e ``https://translate.google.``. Default is ``com``.
+ path: (string): A path to append to the Google Translate host,
+ i.e ``https://translate.google.com/``. Default is ``""``.
+
+ Returns:
+ string: A Google Translate URL `https://translate.google./path`
+ """
+ _GOOGLE_TTS_URL = "https://translate.google.{}/{}"
+ return _GOOGLE_TTS_URL.format(tld, path)
+
+
+__all__ = ["gTTS", "gTTSError"]
+
+# Logger
+log = logging.getLogger(__name__)
+log.addHandler(logging.NullHandler())
+
+
+class Speed:
+ """Read Speed
+
+ The Google TTS Translate API supports two speeds:
+ Slow: True
+ Normal: None
+ """
+
+ SLOW = True
+ NORMAL = None
+
+
+class gTTS:
+ """gTTS -- Google Text-to-Speech.
+
+ An interface to Google Translate's Text-to-Speech API.
+
+ Args:
+ text (string): The text to be read.
+ tld (string): Top-level domain for the Google Translate host,
+ i.e `https://translate.google.`. Different Google domains
+ can produce different localized 'accents' for a given
+ language. This is also useful when ``google.com`` might be blocked
+ within a network but a local or different Google host
+ (e.g. ``google.com.hk``) is not. Default is ``com``.
+ lang (string, optional): The language (IETF language tag) to
+ read the text in. Default is ``en``.
+ slow (bool, optional): Reads text more slowly. Defaults to ``False``.
+ lang_check (bool, optional): Strictly enforce an existing ``lang``,
+ to catch a language error early. If set to ``True``,
+ a ``ValueError`` is raised if ``lang`` doesn't exist.
+ Setting ``lang_check`` to ``False`` skips Web requests
+ (to validate language) and therefore speeds up instantiation.
+ Default is ``True``.
+ pre_processor_funcs (list): A list of zero or more functions that are
+ called to transform (pre-process) text before tokenizing. Those
+ functions must take a string and return a string. Defaults to::
+
+ [
+ pre_processors.tone_marks,
+ pre_processors.end_of_line,
+ pre_processors.abbreviations,
+ pre_processors.word_sub
+ ]
+
+ tokenizer_func (callable): A function that takes in a string and
+ returns a list of string (tokens). Defaults to::
+
+ Tokenizer([
+ tokenizer_cases.tone_marks,
+ tokenizer_cases.period_comma,
+ tokenizer_cases.colon,
+ tokenizer_cases.other_punctuation
+ ]).run
+
+ timeout (float or tuple, optional): Seconds to wait for the server to
+ send data before giving up, as a float, or a ``(connect timeout,
+ read timeout)`` tuple. ``None`` will wait forever (default).
+
+ See Also:
+ :doc:`Pre-processing and tokenizing `
+
+ Raises:
+ AssertionError: When ``text`` is ``None`` or empty; when there's nothing
+ left to speak after pre-precessing, tokenizing and cleaning.
+ ValueError: When ``lang_check`` is ``True`` and ``lang`` is not supported.
+ RuntimeError: When ``lang_check`` is ``True`` but there's an error loading
+ the languages dictionary.
+
+ """
+
+ GOOGLE_TTS_MAX_CHARS = 100 # Max characters the Google TTS API takes at a time
+ GOOGLE_TTS_HEADERS = {
+ "Referer": "http://translate.google.com/",
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) "
+ "AppleWebKit/537.36 (KHTML, like Gecko) "
+ "Chrome/47.0.2526.106 Safari/537.36",
+ "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
+ }
+ GOOGLE_TTS_RPC = "jQ1olc"
+
+ def __init__(
+ self,
+ text,
+ tld="com",
+ lang="en",
+ slow=False,
+ lang_check=True,
+ pre_processor_funcs=[
+ pre_processors.tone_marks,
+ pre_processors.end_of_line,
+ pre_processors.abbreviations,
+ pre_processors.word_sub,
+ ],
+ tokenizer_func=Tokenizer(
+ [
+ tokenizer_cases.tone_marks,
+ tokenizer_cases.period_comma,
+ tokenizer_cases.colon,
+ tokenizer_cases.other_punctuation,
+ ]
+ ).run,
+ timeout=None,
+ ):
+
+ # Debug
+ for k, v in dict(locals()).items():
+ if k == "self":
+ continue
+ log.debug("%s: %s", k, v)
+
+ # Text
+ assert text, "No text to speak"
+ self.text = text
+
+ # Translate URL top-level domain
+ self.tld = tld
+
+ # Language
+ self.lang_check = lang_check
+ self.lang = lang
+
+ if self.lang_check:
+ # Fallback lang in case it is deprecated
+ self.lang = _fallback_deprecated_lang(lang)
+
+ try:
+ langs = tts_langs()
+ if self.lang not in langs:
+ raise ValueError("Language not supported: %s" % lang)
+ except RuntimeError as e:
+ log.debug(str(e), exc_info=True)
+ log.warning(str(e))
+
+ # Read speed
+ if slow:
+ self.speed = Speed.SLOW
+ else:
+ self.speed = Speed.NORMAL
+
+ # Pre-processors and tokenizer
+ self.pre_processor_funcs = pre_processor_funcs
+ self.tokenizer_func = tokenizer_func
+
+ self.timeout = timeout
+
+ def _tokenize(self, text):
+ # Pre-clean
+ text = text.strip()
+
+ # Apply pre-processors
+ for pp in self.pre_processor_funcs:
+ log.debug("pre-processing: %s", pp)
+ text = pp(text)
+
+ if len(text) <= self.GOOGLE_TTS_MAX_CHARS:
+ return _clean_tokens([text])
+
+ # Tokenize
+ log.debug("tokenizing: %s", self.tokenizer_func)
+ tokens = self.tokenizer_func(text)
+
+ # Clean
+ tokens = _clean_tokens(tokens)
+
+ # Minimize
+ min_tokens = []
+ for t in tokens:
+ min_tokens += _minimize(t, " ", self.GOOGLE_TTS_MAX_CHARS)
+
+ # Filter empty tokens, post-minimize
+ tokens = [t for t in min_tokens if t]
+
+ return tokens
+
+ def _prepare_requests(self):
+ """Created the TTS API the request(s) without sending them.
+
+ Returns:
+ list: ``requests.PreparedRequests_``. `_``.
+ """
+ # TTS API URL
+ translate_url = _translate_url(
+ tld=self.tld, path="_/TranslateWebserverUi/data/batchexecute"
+ )
+
+ text_parts = self._tokenize(self.text)
+ log.debug("text_parts: %s", str(text_parts))
+ log.debug("text_parts: %i", len(text_parts))
+ assert text_parts, "No text to send to TTS API"
+
+ prepared_requests = []
+ for idx, part in enumerate(text_parts):
+ data = self._package_rpc(part)
+
+ log.debug("data-%i: %s", idx, data)
+
+ # Request
+ r = requests.post(
+ url=translate_url,
+ data=data,
+ headers=self.GOOGLE_TTS_HEADERS,
+ proxies=getproxy(),
+ )
+
+ # Prepare request
+ prepared_requests.append(r)
+
+ return prepared_requests
+
+ def _package_rpc(self, text):
+ parameter = [text, self.lang, self.speed, "null"]
+ escaped_parameter = json.dumps(parameter, separators=(",", ":"))
+
+ rpc = [[[self.GOOGLE_TTS_RPC, escaped_parameter, None, "generic"]]]
+ espaced_rpc = json.dumps(rpc, separators=(",", ":"))
+ return "f.req={}&".format(urllib.parse.quote(espaced_rpc))
+
+ def stream(self):
+ """Do the TTS API request(s) and stream bytes
+
+ Raises:
+ :class:`gTTSError`: When there's an error with the API request.
+
+ """
+ # When disabling ssl verify in requests (for proxies and firewalls),
+ # urllib3 prints an insecure warning on stdout. We disable that.
+ try:
+ requests.packages.urllib3.disable_warnings(
+ requests.packages.urllib3.exceptions.InsecureRequestWarning
+ )
+ except:
+ pass
+
+ prepared_requests = self._prepare_requests()
+ for idx, r in enumerate(prepared_requests):
+
+ # Write
+ for line in r.content.split(b"\n"):
+ decoded_line = line.decode("utf-8")
+ if "jQ1olc" in decoded_line:
+ audio_search = re.search(r'jQ1olc","\[\\"(.*)\\"]', decoded_line)
+ if audio_search:
+ as_bytes = audio_search.group(1).encode("ascii")
+ yield base64.b64decode(as_bytes)
+ else:
+ # Request successful, good response,
+ # no audio stream in response
+ raise gTTSError(tts=self, response=r)
+ log.debug("part-%i created", idx)
+
+ def write_to_fp(self, fp):
+ """Do the TTS API request(s) and write bytes to a file-like object.
+
+ Args:
+ fp (file object): Any file-like object to write the ``mp3`` to.
+
+ Raises:
+ :class:`gTTSError`: When there's an error with the API request.
+ TypeError: When ``fp`` is not a file-like object that takes bytes.
+
+ """
+
+ try:
+ for idx, decoded in enumerate(self.stream()):
+ fp.write(decoded)
+ log.debug("part-%i written to %s", idx, fp)
+ except (AttributeError, TypeError) as e:
+ raise TypeError(
+ "'fp' is not a file-like object or it does not take bytes: %s" % str(e)
+ )
+
+ def save(self, savefile):
+ """Do the TTS API request and write result to file.
+
+ Args:
+ savefile (string): The path and file name to save the ``mp3`` to.
+
+ Raises:
+ :class:`gTTSError`: When there's an error with the API request.
+
+ """
+ with open(str(savefile), "wb") as f:
+ self.write_to_fp(f)
+ f.flush()
+ log.debug("Saved to %s", savefile)
+
+
+class gTTSError(Exception):
+ """Exception that uses context to present a meaningful error message"""
+
+ def __init__(self, msg=None, **kwargs):
+ self.tts = kwargs.pop("tts", None)
+ self.rsp = kwargs.pop("response", None)
+ if msg:
+ self.msg = msg
+ elif self.tts is not None:
+ self.msg = self.infer_msg(self.tts, self.rsp)
+ else:
+ self.msg = None
+ super(gTTSError, self).__init__(self.msg)
+
+ def infer_msg(self, tts, rsp=None):
+ """Attempt to guess what went wrong by using known
+ information (e.g. http response) and observed behaviour
+
+ """
+ cause = "Unknown"
+
+ if rsp is None:
+ premise = "Failed to connect"
+
+ if tts.tld != "com":
+ host = _translate_url(tld=tts.tld)
+ cause = "Host '{}' is not reachable".format(host)
+
+ else:
+ # rsp should be
+ # http://docs.python-requests.org/en/master/api/
+ status = rsp.status_code
+ reason = rsp.reason
+
+ premise = "{:d} ({}) from TTS API".format(status, reason)
+
+ if status == 403:
+ cause = "Bad token or upstream API changes"
+ elif status == 404 and tts.tld != "com":
+ cause = "Unsupported tld '{}'".format(tts.tld)
+ elif status == 200 and not tts.lang_check:
+ cause = (
+ "No audio stream in response. Unsupported language '%s'"
+ % self.tts.lang
+ )
+ elif status >= 500:
+ cause = "Upstream API error. Try again later."
+
+ return "{}. Probable cause: {}".format(premise, cause)
+
+
+from tts.basettsclass import TTSbase
+from myutils.config import globalconfig, getlangsrc
+
+
+class TTS(TTSbase):
+ def getvoicelist(self):
+ return [""]
+
+ def speak(self, content, rate, voice, voiceidx):
+ tts = gTTS(content, lang=getlangsrc())
+ fname = str(time.time())
+ os.makedirs("./cache/tts/", exist_ok=True)
+
+ tts.save("./cache/tts/" + fname + ".mp3")
+ return "./cache/tts/" + fname + ".mp3"
diff --git a/LunaTranslator/LunaTranslator/tts/huoshantts.py b/LunaTranslator/LunaTranslator/tts/huoshantts.py
index fe5de2ad..f2893705 100644
--- a/LunaTranslator/LunaTranslator/tts/huoshantts.py
+++ b/LunaTranslator/LunaTranslator/tts/huoshantts.py
@@ -24,36 +24,31 @@ class TTS(TTSbase):
def speak(self, content, rate, voice, voiceidx):
- try:
- headers = {
- "authority": "translate.volcengine.com",
- "accept": "application/json, text/plain, */*",
- "accept-language": "zh-CN,zh;q=0.9",
- "origin": "chrome-extension://klgfhbdadaspgppeadghjjemk",
- "sec-fetch-dest": "empty",
- "sec-fetch-mode": "cors",
- "sec-fetch-site": "none",
- "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36",
- }
+ headers = {
+ "authority": "translate.volcengine.com",
+ "accept": "application/json, text/plain, */*",
+ "accept-language": "zh-CN,zh;q=0.9",
+ "origin": "chrome-extension://klgfhbdadaspgppeadghjjemk",
+ "sec-fetch-dest": "empty",
+ "sec-fetch-mode": "cors",
+ "sec-fetch-site": "none",
+ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36",
+ }
- json_data = {
- "text": content,
- "speaker": voice,
- } #
- response = requests.post(
- "https://translate.volcengine.com/crx/tts/v1/",
- headers=headers,
- json=json_data,
- proxies={"http": None, "https": None},
- )
- fname = str(time.time())
- b64 = base64.b64decode(response.json()["audio"]["data"])
- os.makedirs("./cache/tts/", exist_ok=True)
- with open("./cache/tts/" + fname + ".mp3", "wb") as ff:
- ff.write(b64)
+ json_data = {
+ "text": content,
+ "speaker": voice,
+ } #
+ response = requests.post(
+ "https://translate.volcengine.com/crx/tts/v1/",
+ headers=headers,
+ json=json_data,
+ proxies={"http": None, "https": None},
+ )
+ fname = str(time.time())
+ b64 = base64.b64decode(response.json()["audio"]["data"])
+ os.makedirs("./cache/tts/", exist_ok=True)
+ with open("./cache/tts/" + fname + ".mp3", "wb") as ff:
+ ff.write(b64)
- return "./cache/tts/" + fname + ".mp3"
-
- except:
- print_exc()
- return None
+ return "./cache/tts/" + fname + ".mp3"
diff --git a/LunaTranslator/LunaTranslator/tts/voiceroid2.py b/LunaTranslator/LunaTranslator/tts/voiceroid2.py
index dfc21a1f..6e9fbdf3 100644
--- a/LunaTranslator/LunaTranslator/tts/voiceroid2.py
+++ b/LunaTranslator/LunaTranslator/tts/voiceroid2.py
@@ -126,16 +126,16 @@ class TTS(TTSbase):
def speak(self, content, rate, voice, voice_idx):
self.checkpath()
# def _():
- if True:
+
- try:
- content.encode("shift-jis")
- except:
- return
- code1 = content.encode("shift-jis")
- # print(code1)
- windows.WriteFile(self.hPipe, code1)
+ try:
+ content.encode("shift-jis")
+ except:
+ return
+ code1 = content.encode("shift-jis")
+ # print(code1)
+ windows.WriteFile(self.hPipe, code1)
- fname = windows.ReadFile(self.hPipe, 1024).decode("utf8")
- if os.path.exists(fname):
- return fname
+ fname = windows.ReadFile(self.hPipe, 1024).decode("utf8")
+ if os.path.exists(fname):
+ return fname
diff --git a/LunaTranslator/LunaTranslator/tts/voiceroidplus.py b/LunaTranslator/LunaTranslator/tts/voiceroidplus.py
index 8702c450..fb6f3b37 100644
--- a/LunaTranslator/LunaTranslator/tts/voiceroidplus.py
+++ b/LunaTranslator/LunaTranslator/tts/voiceroidplus.py
@@ -115,17 +115,16 @@ class TTS(TTSbase):
def speak(self, content, rate, voice, voice_idx):
self.checkpath()
- # def _():
- if True:
+
- try:
- content.encode("shift-jis")
- except:
- return
- code1 = content.encode("shift-jis")
- # print(code1)
- windows.WriteFile(self.hPipe, code1)
+ try:
+ content.encode("shift-jis")
+ except:
+ return
+ code1 = content.encode("shift-jis")
+ # print(code1)
+ windows.WriteFile(self.hPipe, code1)
- fname = windows.ReadFile(self.hPipe, 1024).decode("utf8")
- if os.path.exists(fname):
- return fname
+ fname = windows.ReadFile(self.hPipe, 1024).decode("utf8")
+ if os.path.exists(fname):
+ return fname
diff --git a/LunaTranslator/LunaTranslator/tts/voicevox.py b/LunaTranslator/LunaTranslator/tts/voicevox.py
index fceb99da..202a6424 100644
--- a/LunaTranslator/LunaTranslator/tts/voicevox.py
+++ b/LunaTranslator/LunaTranslator/tts/voicevox.py
@@ -74,36 +74,33 @@ class TTS(TTSbase):
def speak(self, content, rate, voice, voiceidx):
- # def _():
- if True:
+ headers = {
+ "Content-Type": "application/x-www-form-urlencoded",
+ }
- headers = {
- "Content-Type": "application/x-www-form-urlencoded",
- }
+ params = {"speaker": voiceidx, "text": content}
- params = {"speaker": voiceidx, "text": content}
-
- response = requests.post(
- f"http://localhost:{self.config['Port']}/audio_query",
- params=params,
- headers=headers,
- proxies={"http": None, "https": None},
- )
- print(response.json())
- fname = str(time.time())
- headers = {
- "Content-Type": "application/json",
- }
- params = {
- "speaker": voiceidx,
- }
- response = requests.post(
- f"http://localhost:{self.config['Port']}/synthesis",
- params=params,
- headers=headers,
- data=json.dumps(response.json()),
- )
- os.makedirs("./cache/tts/", exist_ok=True)
- with open("./cache/tts/" + fname + ".wav", "wb") as ff:
- ff.write(response.content)
- return "./cache/tts/" + fname + ".wav"
+ response = requests.post(
+ f"http://localhost:{self.config['Port']}/audio_query",
+ params=params,
+ headers=headers,
+ proxies={"http": None, "https": None},
+ )
+ print(response.json())
+ fname = str(time.time())
+ headers = {
+ "Content-Type": "application/json",
+ }
+ params = {
+ "speaker": voiceidx,
+ }
+ response = requests.post(
+ f"http://localhost:{self.config['Port']}/synthesis",
+ params=params,
+ headers=headers,
+ data=json.dumps(response.json()),
+ )
+ os.makedirs("./cache/tts/", exist_ok=True)
+ with open("./cache/tts/" + fname + ".wav", "wb") as ff:
+ ff.write(response.content)
+ return "./cache/tts/" + fname + ".wav"
diff --git a/LunaTranslator/files/defaultconfig/config.json b/LunaTranslator/files/defaultconfig/config.json
index 48b0caa9..c9dc48a3 100644
--- a/LunaTranslator/files/defaultconfig/config.json
+++ b/LunaTranslator/files/defaultconfig/config.json
@@ -725,6 +725,11 @@
"name": "GPT-SOVITS preset"
}
}
+ },
+ "gtts": {
+ "use": false,
+ "name": "谷歌",
+ "voice": ""
}
},
"hirasetting": {