From 220e6e30511c13c739852a2dfb12da69e1e6687e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=81=8D=E5=85=AE=E6=83=9A=E5=85=AE?=
<101191390+HIllya51@users.noreply.github.com>
Date: Fri, 17 May 2024 13:20:02 +0800
Subject: [PATCH] update
---
LunaTranslator/LunaTranslator/gui/showword.py | 159 ++++--
.../LunaTranslator/gui/usefulwidget.py | 3 +-
LunaTranslator/LunaTranslator/tts/gtts.py | 485 +-----------------
.../files/defaultconfig/config.json | 1 +
LunaTranslator/files/lang/ar.json | 4 +-
LunaTranslator/files/lang/cht.json | 4 +-
LunaTranslator/files/lang/en.json | 4 +-
LunaTranslator/files/lang/es.json | 4 +-
LunaTranslator/files/lang/fr.json | 4 +-
LunaTranslator/files/lang/it.json | 4 +-
LunaTranslator/files/lang/ja.json | 4 +-
LunaTranslator/files/lang/ko.json | 4 +-
LunaTranslator/files/lang/pl.json | 4 +-
LunaTranslator/files/lang/ru.json | 4 +-
LunaTranslator/files/lang/th.json | 4 +-
LunaTranslator/files/lang/tr.json | 4 +-
LunaTranslator/files/lang/uk.json | 4 +-
LunaTranslator/files/lang/vi.json | 4 +-
LunaTranslator/files/lang/zh.json | 4 +-
plugins/CMakeLists.txt | 2 +-
20 files changed, 178 insertions(+), 532 deletions(-)
diff --git a/LunaTranslator/LunaTranslator/gui/showword.py b/LunaTranslator/LunaTranslator/gui/showword.py
index d3ba726d..90220393 100644
--- a/LunaTranslator/LunaTranslator/gui/showword.py
+++ b/LunaTranslator/LunaTranslator/gui/showword.py
@@ -12,14 +12,14 @@ from PyQt5.QtWidgets import (
QTabWidget,
QFileDialog,
QTabBar,
- QLabel,
+ QLabel
)
from PyQt5.QtGui import QPixmap, QImage
from traceback import print_exc
-import requests, json
+import requests, json, subprocess, time
from PyQt5.QtCore import pyqtSignal, Qt
import qtawesome, functools, os, base64
-import gobject, uuid
+import gobject, uuid, signal
from myutils.config import globalconfig, _TR, static_data
import myutils.ankiconnect as anki
from gui.usefulwidget import (
@@ -31,13 +31,65 @@ from gui.usefulwidget import (
getlineedit,
getsimpleswitch,
getcolorbutton,
- tabadd_lazy,
+ tabadd_lazy
)
from myutils.wrapper import threader
from myutils.ocrutil import imageCut, ocr_run
from gui.rangeselect import rangeselct_function
+class ffmpeg_virtual_audio_capturer:
+ def __init__(self):
+ os.makedirs("./cache/tts", exist_ok=True)
+ self.file = os.path.abspath(
+ os.path.join("./cache/tts", str(time.time()) + ".mp3")
+ )
+ try:
+ self.engine = subprocess.Popen(
+ os.path.join(
+ globalconfig["ffmpeg"],
+ f'ffmpeg.exe -f dshow -i audio="virtual-audio-capturer" "{self.file}"',
+ ),
+ stdin=subprocess.PIPE,
+ )
+ except:
+ print_exc()
+ def end(self):
+ try:
+ self.engine.stdin.write(b"q")
+ self.engine.stdin.flush()
+ except:
+ pass
+
+
+class statusbutton(QPushButton):
+ statuschanged1 = pyqtSignal(int)
+ statuschanged2 = pyqtSignal(int)
+
+ def __init__(self, icons, colors):
+ super().__init__()
+ self.idx = 0
+ self.icons = icons
+ self.colors = colors
+ self.clicked.connect(self.setChecked)
+ self.seticon()
+
+ def seticon(self):
+ self.setIcon(
+ qtawesome.icon(
+ self.icons[(self.idx) % len(self.icons)],
+ color=self.colors[(self.idx) % len(self.colors)],
+ )
+ )
+
+ def setChecked(self, a0):
+ super().setChecked(a0)
+ self.idx += 1
+ self.statuschanged1.emit((self.idx) % len(self.icons))
+ self.statuschanged2.emit((self.idx) % len(self.colors))
+ self.seticon()
+
+
class AnkiWindow(QWidget):
__ocrsettext = pyqtSignal(str)
refreshhtml = pyqtSignal()
@@ -288,8 +340,47 @@ class AnkiWindow(QWidget):
getsimpleswitch(globalconfig["ankiconnect"], "ocrcroped"),
)
+ layout.addWidget(QLabel())
+ layout.addRow(_TR("录音"), QLabel())
+ lb = QLabel()
+ lb.setOpenExternalLinks(True)
+ lb.setText(
+ 'virtual-audio-capturer'
+ )
+ layout.addRow(_TR("安装录音驱动"), lb)
+ ffmpegpath = getlineedit(globalconfig, "ffmpeg", readonly=True)
+
+ def selectpath():
+ f = QFileDialog.getExistingDirectory()
+ if f != "":
+ ffmpegpath.setText(f)
+
+ layout.addRow(
+ _TR("ffmpeg"),
+ getboxlayout(
+ [
+ ffmpegpath,
+ getcolorbutton(
+ "",
+ "",
+ selectpath,
+ icon="fa.gear",
+ constcolor="#FF69B4",
+ ),
+ ],
+ makewidget=True,
+ ),
+ )
+
return wid
+ def startorendrecord(self, target: QLineEdit, idx):
+ if idx == 1:
+ self.recorder = ffmpeg_virtual_audio_capturer()
+ else:
+ self.recorder.end()
+ target.setText(self.recorder.file)
+
def createaddtab(self):
layout = QVBoxLayout()
wid = QWidget()
@@ -311,6 +402,14 @@ class AnkiWindow(QWidget):
self.editpath.textChanged.connect(self.wrappedpixmap)
self.example = QPlainTextEdit()
self.remarks = QTextEdit()
+ recordbtn1 = statusbutton(icons=["fa.microphone", "fa.stop"], colors=[""])
+ recordbtn1.statuschanged1.connect(
+ functools.partial(self.startorendrecord, self.audiopath)
+ )
+ recordbtn2 = statusbutton(icons=["fa.microphone", "fa.stop"], colors=[""])
+ recordbtn2.statuschanged1.connect(
+ functools.partial(self.startorendrecord, self.audiopath_sentence)
+ )
layout.addLayout(
getboxlayout(
[
@@ -335,11 +434,14 @@ class AnkiWindow(QWidget):
[
QLabel(_TR("语音")),
self.audiopath,
+ recordbtn1,
soundbutton,
getcolorbutton(
"",
"",
- functools.partial(self.selectaudio),
+ functools.partial(
+ self.selecfile, self.audiopath
+ ),
icon="fa.gear",
constcolor="#FF69B4",
),
@@ -349,11 +451,14 @@ class AnkiWindow(QWidget):
[
QLabel(_TR("语音_例句")),
self.audiopath_sentence,
+ recordbtn2,
soundbutton2,
getcolorbutton(
"",
"",
- functools.partial(self.selectaudio2),
+ functools.partial(
+ self.selecfile, self.audiopath_sentence
+ ),
icon="fa.gear",
constcolor="#FF69B4",
),
@@ -367,7 +472,9 @@ class AnkiWindow(QWidget):
getcolorbutton(
"",
"",
- functools.partial(self.selectimage),
+ functools.partial(
+ self.selecfile, self.editpath
+ ),
icon="fa.gear",
constcolor="#FF69B4",
),
@@ -406,23 +513,11 @@ class AnkiWindow(QWidget):
pix = pix.scaled(self.viewimagelabel.size() * rate, Qt.KeepAspectRatio)
self.viewimagelabel.setPixmap(pix)
- def selectimage(self):
+ def selecfile(self, item):
f = QFileDialog.getOpenFileName()
res = f[0]
if res != "":
- self.editpath.setText(res)
-
- def selectaudio(self):
- f = QFileDialog.getOpenFileName()
- res = f[0]
- if res != "":
- self.audiopath.setText(res)
-
- def selectaudio2(self):
- f = QFileDialog.getOpenFileName()
- res = f[0]
- if res != "":
- self.audiopath_sentence.setText(res)
+ item.setText(res)
def reset(self, text):
self.currentword = text
@@ -578,8 +673,8 @@ class searchwordW(closeashidewindow):
soundbutton.clicked.connect(self.langdu)
self.searchlayout.addWidget(soundbutton)
- ankiconnect = QPushButton(qtawesome.icon("fa.adn"), "")
- ankiconnect.clicked.connect(self.onceaddankiwindow)
+ ankiconnect = statusbutton(icons=["fa.adn"], colors=["", "#FF69B4"])
+ ankiconnect.statuschanged2.connect(self.onceaddankiwindow)
self.searchlayout.addWidget(ankiconnect)
self.tab = QTabBar(self)
@@ -596,7 +691,6 @@ class searchwordW(closeashidewindow):
self.textOutput = textOutput
self.cache_results = {}
self.hiding = True
- self.addankiwindowidx = 0
tablayout = QVBoxLayout()
tablayout.addWidget(self.tab)
@@ -604,16 +698,17 @@ class searchwordW(closeashidewindow):
tablayout.setContentsMargins(0, 0, 0, 0)
tablayout.setSpacing(0)
self.vboxlayout.addLayout(tablayout)
+ self.isfirstshowanki = True
- def onceaddankiwindow(self):
- if self.addankiwindowidx == 0:
- self.vboxlayout.addWidget(self.ankiwindow)
- else:
- if self.addankiwindowidx % 2 == 0:
- self.ankiwindow.show()
+ def onceaddankiwindow(self, idx):
+ if idx == 1:
+ if self.isfirstshowanki:
+ self.vboxlayout.addWidget(self.ankiwindow)
else:
- self.ankiwindow.hide()
- self.addankiwindowidx += 1
+ self.ankiwindow.show()
+ else:
+ self.ankiwindow.hide()
+ self.isfirstshowanki = False
def langdu(self):
if gobject.baseobject.reader:
diff --git a/LunaTranslator/LunaTranslator/gui/usefulwidget.py b/LunaTranslator/LunaTranslator/gui/usefulwidget.py
index 5c9508ad..1beedbc3 100644
--- a/LunaTranslator/LunaTranslator/gui/usefulwidget.py
+++ b/LunaTranslator/LunaTranslator/gui/usefulwidget.py
@@ -399,9 +399,10 @@ def getsimplecombobox(lst, d, k, callback=None):
return s
-def getlineedit(d, key, callback=None):
+def getlineedit(d, key, callback=None, readonly=False):
s = QLineEdit()
s.setText(d[key])
+ s.setReadOnly(readonly)
s.textChanged.connect(functools.partial(callbackwrap, d, key, callback))
return s
diff --git a/LunaTranslator/LunaTranslator/tts/gtts.py b/LunaTranslator/LunaTranslator/tts/gtts.py
index 2385d1c1..e9ca465f 100644
--- a/LunaTranslator/LunaTranslator/tts/gtts.py
+++ b/LunaTranslator/LunaTranslator/tts/gtts.py
@@ -84,22 +84,6 @@ 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())
@@ -108,16 +92,6 @@ def tts_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)",
@@ -126,25 +100,6 @@ def _extra_langs():
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": [
@@ -205,51 +160,6 @@ class symbols:
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
@@ -273,49 +183,6 @@ class RegexBuilder:
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
@@ -327,16 +194,6 @@ class PreProcessorRegex:
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
@@ -349,39 +206,6 @@ class PreProcessorRegex:
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)
@@ -396,16 +220,6 @@ class PreProcessorSub:
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
@@ -415,80 +229,6 @@ class PreProcessorSub:
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
@@ -511,15 +251,6 @@ class Tokenizer:
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
@@ -529,51 +260,22 @@ class Tokenizer:
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),
@@ -642,7 +316,6 @@ class pre_processors:
).run(text)
def word_sub(text):
- """Word-for-word substitutions."""
return PreProcessorSub(sub_pairs=symbols.SUB_PAIRS).run(text)
@@ -651,37 +324,8 @@ 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) :]
@@ -701,31 +345,10 @@ def _minimize(the_string, delim, max_size):
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)
@@ -738,76 +361,11 @@ 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/",
@@ -913,12 +471,6 @@ class gTTS:
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"
)
@@ -956,14 +508,6 @@ class gTTS:
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
@@ -989,16 +533,6 @@ class gTTS:
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()):
@@ -1010,15 +544,6 @@ class gTTS:
)
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()
@@ -1026,8 +551,6 @@ class gTTS:
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)
@@ -1040,10 +563,6 @@ class gTTSError(Exception):
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:
@@ -1077,7 +596,7 @@ class gTTSError(Exception):
from tts.basettsclass import TTSbase
-from myutils.config import globalconfig, getlangsrc
+from myutils.config import getlangsrc
class TTS(TTSbase):
diff --git a/LunaTranslator/files/defaultconfig/config.json b/LunaTranslator/files/defaultconfig/config.json
index c9dc48a3..fc2c1079 100644
--- a/LunaTranslator/files/defaultconfig/config.json
+++ b/LunaTranslator/files/defaultconfig/config.json
@@ -54,6 +54,7 @@
"changecharset": false,
"changecharset_charset": 2
},
+ "ffmpeg": "",
"requestinterval": 1,
"keepontop": true,
"buttonsize": 20,
diff --git a/LunaTranslator/files/lang/ar.json b/LunaTranslator/files/lang/ar.json
index fa154d3f..b804409d 100644
--- a/LunaTranslator/files/lang/ar.json
+++ b/LunaTranslator/files/lang/ar.json
@@ -788,5 +788,7 @@
"添加时更新模板": "تحديث القالب عند إضافة",
"截图后进行OCR": "التعرف الضوئي على الحروف",
"优先级": "الأولوية",
- "编码": "ترميز"
+ "编码": "ترميز",
+ "安装录音驱动": "تثبيت برنامج تشغيل التسجيل",
+ "录音": "تسجيل صوتي"
}
\ No newline at end of file
diff --git a/LunaTranslator/files/lang/cht.json b/LunaTranslator/files/lang/cht.json
index b9e2b642..67fa7bc6 100644
--- a/LunaTranslator/files/lang/cht.json
+++ b/LunaTranslator/files/lang/cht.json
@@ -788,5 +788,7 @@
"添加时更新模板": "添加時更新範本",
"截图后进行OCR": "截圖後進行OCR",
"优先级": "優先順序",
- "编码": "編碼"
+ "编码": "編碼",
+ "安装录音驱动": "安裝錄音驅動",
+ "录音": "錄音"
}
\ No newline at end of file
diff --git a/LunaTranslator/files/lang/en.json b/LunaTranslator/files/lang/en.json
index f521bd15..c2bb2a69 100644
--- a/LunaTranslator/files/lang/en.json
+++ b/LunaTranslator/files/lang/en.json
@@ -788,5 +788,7 @@
"添加时更新模板": "Update template when adding",
"截图后进行OCR": "Perform OCR after taking screenshots",
"优先级": "priority",
- "编码": "coding"
+ "编码": "coding",
+ "安装录音驱动": "Install recording driver",
+ "录音": "tape"
}
\ No newline at end of file
diff --git a/LunaTranslator/files/lang/es.json b/LunaTranslator/files/lang/es.json
index 7b77f780..be90085c 100644
--- a/LunaTranslator/files/lang/es.json
+++ b/LunaTranslator/files/lang/es.json
@@ -788,5 +788,7 @@
"添加时更新模板": "Actualizar la plantilla al agregar",
"截图后进行OCR": "OCR después de la captura de pantalla",
"优先级": "Prioridad",
- "编码": "Codificación"
+ "编码": "Codificación",
+ "安装录音驱动": "Instalación de la unidad de grabación",
+ "录音": "Grabación"
}
\ No newline at end of file
diff --git a/LunaTranslator/files/lang/fr.json b/LunaTranslator/files/lang/fr.json
index 94dbfb48..b10f6d59 100644
--- a/LunaTranslator/files/lang/fr.json
+++ b/LunaTranslator/files/lang/fr.json
@@ -788,5 +788,7 @@
"添加时更新模板": "Mettre à jour le modèle lorsque vous l'ajoutez",
"截图后进行OCR": "OCR après capture d'écran",
"优先级": "Priorité",
- "编码": "Codage"
+ "编码": "Codage",
+ "安装录音驱动": "Installer le driver d'enregistrement",
+ "录音": "Enregistrement sonore"
}
\ No newline at end of file
diff --git a/LunaTranslator/files/lang/it.json b/LunaTranslator/files/lang/it.json
index ce2212a6..dd641424 100644
--- a/LunaTranslator/files/lang/it.json
+++ b/LunaTranslator/files/lang/it.json
@@ -788,5 +788,7 @@
"添加时更新模板": "Aggiorna modello quando aggiungi",
"截图后进行OCR": "Esegui OCR dopo aver scattato screenshot",
"优先级": "priorità",
- "编码": "codifica"
+ "编码": "codifica",
+ "安装录音驱动": "Installa il driver di registrazione",
+ "录音": "nastro"
}
\ No newline at end of file
diff --git a/LunaTranslator/files/lang/ja.json b/LunaTranslator/files/lang/ja.json
index d36520a3..036b5589 100644
--- a/LunaTranslator/files/lang/ja.json
+++ b/LunaTranslator/files/lang/ja.json
@@ -788,5 +788,7 @@
"添加时更新模板": "追加時にテンプレートを更新する",
"截图后进行OCR": "スクリーンショット後にOCR",
"优先级": "優先度",
- "编码": "エンコード"
+ "编码": "エンコード",
+ "安装录音驱动": "録音ドライブのインストール",
+ "录音": "レコーディング"
}
\ No newline at end of file
diff --git a/LunaTranslator/files/lang/ko.json b/LunaTranslator/files/lang/ko.json
index 9f32c330..f88aab30 100644
--- a/LunaTranslator/files/lang/ko.json
+++ b/LunaTranslator/files/lang/ko.json
@@ -788,5 +788,7 @@
"添加时更新模板": "추가 시 템플릿 업데이트",
"截图后进行OCR": "캡처해서 OCR 진행하도록 하겠습니다.",
"优先级": "우선 순위",
- "编码": "인코딩"
+ "编码": "인코딩",
+ "安装录音驱动": "녹음 드라이브 설치",
+ "录音": "녹음"
}
\ No newline at end of file
diff --git a/LunaTranslator/files/lang/pl.json b/LunaTranslator/files/lang/pl.json
index 90ad0ef5..4ee285fc 100644
--- a/LunaTranslator/files/lang/pl.json
+++ b/LunaTranslator/files/lang/pl.json
@@ -788,5 +788,7 @@
"添加时更新模板": "Aktualizuj szablon podczas dodawania",
"截图后进行OCR": "Wykonanie OCR po wykonaniu zrzutów ekranu",
"优先级": "priorytet",
- "编码": "kodowanie"
+ "编码": "kodowanie",
+ "安装录音驱动": "Zainstaluj sterownik nagrywania",
+ "录音": "taśma"
}
\ No newline at end of file
diff --git a/LunaTranslator/files/lang/ru.json b/LunaTranslator/files/lang/ru.json
index ec6f1320..f3f72e6d 100644
--- a/LunaTranslator/files/lang/ru.json
+++ b/LunaTranslator/files/lang/ru.json
@@ -788,5 +788,7 @@
"添加时更新模板": "Обновить шаблон при добавлении",
"截图后进行OCR": "Снимок экрана после OCR",
"优先级": "Приоритеты",
- "编码": "Код"
+ "编码": "Код",
+ "安装录音驱动": "Установка привода звукозаписи",
+ "录音": "Запись"
}
\ No newline at end of file
diff --git a/LunaTranslator/files/lang/th.json b/LunaTranslator/files/lang/th.json
index 4abbc9ca..e9359cd0 100644
--- a/LunaTranslator/files/lang/th.json
+++ b/LunaTranslator/files/lang/th.json
@@ -788,5 +788,7 @@
"添加时更新模板": "ปรับปรุงแม่แบบเมื่อคุณเพิ่ม",
"截图后进行OCR": "ทำ OCR หลังจากจับภาพหน้าจอ",
"优先级": "ลำดับความสำคัญ",
- "编码": "การเข้ารหัส"
+ "编码": "การเข้ารหัส",
+ "安装录音驱动": "ติดตั้งไดรฟ์บันทึก",
+ "录音": "การบันทึกเสียง"
}
\ No newline at end of file
diff --git a/LunaTranslator/files/lang/tr.json b/LunaTranslator/files/lang/tr.json
index c06cdfa3..fe755e49 100644
--- a/LunaTranslator/files/lang/tr.json
+++ b/LunaTranslator/files/lang/tr.json
@@ -788,5 +788,7 @@
"添加时更新模板": "Eklence şablonu güncelle",
"截图后进行OCR": "Ekran fotoğraflarını aldıktan sonra OCR yap",
"优先级": "Prioritet",
- "编码": "coding"
+ "编码": "coding",
+ "安装录音驱动": "Kayıt sürücüsünü kur",
+ "录音": "kaset"
}
\ No newline at end of file
diff --git a/LunaTranslator/files/lang/uk.json b/LunaTranslator/files/lang/uk.json
index 7ffe916f..d92e6e39 100644
--- a/LunaTranslator/files/lang/uk.json
+++ b/LunaTranslator/files/lang/uk.json
@@ -788,5 +788,7 @@
"添加时更新模板": "Оновити шаблон під час додавання",
"截图后进行OCR": "Виконати OCR після роботи знімків екрана",
"优先级": "пріоритет",
- "编码": "кодування"
+ "编码": "кодування",
+ "安装录音驱动": "Встановити драйвер запису",
+ "录音": "стрічку"
}
\ No newline at end of file
diff --git a/LunaTranslator/files/lang/vi.json b/LunaTranslator/files/lang/vi.json
index 8fd39b46..7bdc35ba 100644
--- a/LunaTranslator/files/lang/vi.json
+++ b/LunaTranslator/files/lang/vi.json
@@ -788,5 +788,7 @@
"添加时更新模板": "Cập nhật mẫu khi thêm",
"截图后进行OCR": "OCR sau khi chụp ảnh màn hình",
"优先级": "Ưu tiên",
- "编码": "Mã hóa"
+ "编码": "Mã hóa",
+ "安装录音驱动": "Cài đặt Recording Drive",
+ "录音": "Ghi âm"
}
\ No newline at end of file
diff --git a/LunaTranslator/files/lang/zh.json b/LunaTranslator/files/lang/zh.json
index 427e55b5..07bba8e1 100644
--- a/LunaTranslator/files/lang/zh.json
+++ b/LunaTranslator/files/lang/zh.json
@@ -788,5 +788,7 @@
"添加时更新模板": "",
"截图后进行OCR": "",
"优先级": "",
- "编码": ""
+ "编码": "",
+ "安装录音驱动": "",
+ "录音": ""
}
\ No newline at end of file
diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt
index faff41fe..843cbc0d 100644
--- a/plugins/CMakeLists.txt
+++ b/plugins/CMakeLists.txt
@@ -29,7 +29,7 @@ include(generate_product_version)
set(VERSION_MAJOR 2)
set(VERSION_MINOR 51)
-set(VERSION_PATCH 2)
+set(VERSION_PATCH 3)
add_library(pch pch.cpp)
target_precompile_headers(pch PUBLIC pch.h)