1

Update websocket.py
This commit is contained in:
恍兮惚兮 2024-04-09 01:36:59 +08:00
parent 1b6503bb70
commit fd9049e714
30 changed files with 444 additions and 130 deletions

View File

@ -69,6 +69,7 @@ class MAINUI:
self.currentsignature = None self.currentsignature = None
self.isrunning = True self.isrunning = True
self.solvegottextlock = threading.Lock() self.solvegottextlock = threading.Lock()
self.outputers = {}
@property @property
def textsource(self): def textsource(self):
@ -248,13 +249,8 @@ class MAINUI:
except: except:
pass pass
if onlytrans == False: if onlytrans == False:
self.dispatchoutputer(text)
self.currenttext = text self.currenttext = text
if (
globalconfig["outputtopasteboard"]
and globalconfig["sourcestatus2"]["copy"]["use"] == False
):
winsharedutils.clipboard_set(text)
if globalconfig["read_raw"]: if globalconfig["read_raw"]:
self.currentread = text self.currentread = text
self.autoreadcheckname() self.autoreadcheckname()
@ -556,16 +552,32 @@ class MAINUI:
else: else:
self.hira_ = None self.hira_ = None
@threader
def startoutputer_re(self, klass):
self.outputers[klass].init()
@threader
def startoutputer(self):
for classname in globalconfig["textoutputer"]:
if not os.path.exists("./LunaTranslator/textoutput/" + classname + ".py"):
continue
aclass = importlib.import_module("textoutput." + classname).Outputer
self.outputers[classname] = aclass(classname)
def dispatchoutputer(self, text):
for _, kls in self.outputers.items():
if kls.config["use"]:
kls.puttask(text)
def fanyiinitmethod(self, classname): def fanyiinitmethod(self, classname):
try: try:
if classname == "selfbuild": if classname == "selfbuild":
if os.path.exists("./userconfig/selfbuild.py") == False: if not os.path.exists("./userconfig/selfbuild.py"):
return None return None
aclass = importlib.import_module("selfbuild").TS aclass = importlib.import_module("selfbuild").TS
else: else:
if ( if not os.path.exists(
os.path.exists("./LunaTranslator/translator/" + classname + ".py") "./LunaTranslator/translator/" + classname + ".py"
== False
): ):
return None return None
aclass = importlib.import_module("translator." + classname).TS aclass = importlib.import_module("translator." + classname).TS
@ -783,7 +795,7 @@ class MAINUI:
self.prepare() self.prepare()
self.startxiaoxueguan() self.startxiaoxueguan()
self.starthira() self.starthira()
self.startoutputer()
self.settin_ui = Settin(self.translation_ui) self.settin_ui = Settin(self.translation_ui)
self.transhis = gui.transhist.transhist(self.settin_ui) self.transhis = gui.transhist.transhist(self.settin_ui)
gobject.baseobject.Prompt = Prompt() gobject.baseobject.Prompt = Prompt()

View File

@ -54,7 +54,7 @@ import gobject
from myutils.config import _TR, _TRL, globalconfig, static_data from myutils.config import _TR, _TRL, globalconfig, static_data
import winsharedutils import winsharedutils
from myutils.wrapper import Singleton_close, Singleton, threader from myutils.wrapper import Singleton_close, Singleton, threader
from myutils.utils import checkifnewgame from myutils.utils import checkifnewgame, vidchangedtask
from myutils.proxy import getproxy from myutils.proxy import getproxy
from gui.usefulwidget import yuitsu_switch, saveposwindow, getboxlayout from gui.usefulwidget import yuitsu_switch, saveposwindow, getboxlayout
from myutils.vndb import parsehtmlmethod from myutils.vndb import parsehtmlmethod
@ -533,6 +533,7 @@ class dialog_setting_game(QDialog):
def _titlechange(x): def _titlechange(x):
savehook_new_data[exepath]["title"] = x savehook_new_data[exepath]["title"] = x
savehook_new_data[exepath]["istitlesetted"] = True
savehook_new_data[exepath]["searchnoresulttime"] = 0 savehook_new_data[exepath]["searchnoresulttime"] = 0
self.setWindowTitle(x) self.setWindowTitle(x)
gametitleitme.settitle(x) gametitleitme.settitle(x)
@ -553,6 +554,7 @@ class dialog_setting_game(QDialog):
_pixmap = QPixmap(res) _pixmap = QPixmap(res)
if _pixmap.isNull() == False: if _pixmap.isNull() == False:
savehook_new_data[exepath]["imagepath"] = res savehook_new_data[exepath]["imagepath"] = res
savehook_new_data[exepath]["isimagepathusersetted"] = True
imgpath.setText(res) imgpath.setText(res)
gametitleitme.setimg(_pixmap) gametitleitme.setimg(_pixmap)
@ -572,13 +574,7 @@ class dialog_setting_game(QDialog):
vndbid.setValidator(QIntValidator()) vndbid.setValidator(QIntValidator())
vndbid.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) vndbid.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
def changevid(exepath, text): vndbid.textEdited.connect(functools.partial(vidchangedtask, exepath))
savehook_new_data[exepath]["vid"] = int(text)
savehook_new_data[exepath]["infopath"] = None
savehook_new_data[exepath]["searchnoresulttime"] = 0
# savehook_new_data[exepath]['imagepath']=None
vndbid.textEdited.connect(functools.partial(changevid, exepath))
statiswids = [ statiswids = [
QLabel(_TR("统计信息")), QLabel(_TR("统计信息")),
@ -691,8 +687,8 @@ class dialog_setting_game(QDialog):
methodtab = QTabWidget() methodtab = QTabWidget()
methodtab.addTab(self.gethooktab(exepath), "HOOK") methodtab.addTab(self.gethooktab(exepath), "HOOK")
methodtab.addTab(self.getpretranstab(exepath), "预翻译") methodtab.addTab(self.getpretranstab(exepath), _TR("预翻译"))
methodtab.addTab(self.getttssetting(exepath), "语音") methodtab.addTab(self.getttssetting(exepath), _TR("语音"))
formLayout.addWidget(methodtab) formLayout.addWidget(methodtab)
self.show() self.show()

View File

@ -334,17 +334,55 @@ def gethookembedgrid(self):
return grids return grids
def setTabclip(self): def getTabclip(self):
grids = [ grids = [
[ [
("提取的文本自动复制到剪贴板", 5), ("排除复制自翻译器的文本", 3),
(getsimpleswitch(globalconfig, "outputtopasteboard"), 1), getsimpleswitch(globalconfig, "excule_from_self"),
("", 3), ("", 3),
]
]
return grids
def outputgrid(self):
grids = [
[("自动输出提取的文本", 10)],
[],
[("剪贴板", 10)],
[
"",
("输出到剪贴板", 5),
(getsimpleswitch(globalconfig["textoutputer"]["clipboard"], "use"), 1),
],
[("WebSocket", 10)],
[
"",
("输出到WebSocket", 5),
(
getsimpleswitch(
globalconfig["textoutputer"]["websocket"],
"use",
callback=lambda _: gobject.baseobject.startoutputer_re("websocket"),
),
1,
),
], ],
[ [
("排除复制自翻译器的文本", 5), "",
(getsimpleswitch(globalconfig, "excule_from_self"), 1), ("端口号", 5),
(
getspinbox(
0,
65535,
globalconfig["textoutputer"]["websocket"],
"port",
callback=lambda _: gobject.baseobject.startoutputer_re("websocket"),
),
3,
),
], ],
] ]
return grids return grids
@ -462,12 +500,13 @@ def setTabOne(self):
def setTabOne_lazy(self): def setTabOne_lazy(self):
tab = self.makesubtab_lazy( tab = self.makesubtab_lazy(
["HOOK设置", "OCR设置", "剪贴板", "内嵌翻译"], ["HOOK设置", "OCR设置", "剪贴板", "内嵌翻译", "文本输出"],
[ [
lambda: self.makescroll(self.makegrid(gethookgrid(self))), lambda: self.makescroll(self.makegrid(gethookgrid(self))),
lambda: self.makescroll(self.makegrid(getocrgrid(self))), lambda: self.makescroll(self.makegrid(getocrgrid(self))),
lambda: self.makescroll(self.makegrid(setTabclip(self))), lambda: self.makescroll(self.makegrid(getTabclip(self))),
lambda: self.makescroll(self.makegrid(gethookembedgrid(self))), lambda: self.makescroll(self.makegrid(gethookembedgrid(self))),
lambda: self.makescroll(self.makegrid(outputgrid(self))),
], ],
) )

View File

@ -280,6 +280,13 @@ def setTab7_lazy(self):
"", "",
], ],
] ]
if globalconfig["languageuse"] == 2: # en
grids2 += [
[
(("使用VNDB数据替换人名"), 6),
getsimpleswitch(globalconfig, "vndbmapname"),
]
]
def __(): def __():
_w = self.makescroll(self.makegrid(grids, True, savelist, savelay)) _w = self.makescroll(self.makegrid(grids, True, savelist, savelay))

View File

@ -93,6 +93,8 @@ def resourcegrid(self):
else: else:
if link[-8:] == "releases": if link[-8:] == "releases":
__ = False __ = False
elif link[-1] == "/":
__ = False
else: else:
__ = True __ = True
grid.append([(_TR(name), 1, ""), (makehtml(link, __), 2, "link")]) grid.append([(_TR(name), 1, ""), (makehtml(link, __), 2, "link")])

View File

@ -60,8 +60,11 @@ def getdefaultsavehook(gamepath, title=None):
"needinserthookcode": [], "needinserthookcode": [],
"embedablehook": [], "embedablehook": [],
"imagepath": None, "imagepath": None,
"isimagepathusersetted": False,
"istitlesetted": False,
"infopath": None, "infopath": None,
"vid": 0, "vid": 0,
"namemap": {},
"statistic_playtime": 0, "statistic_playtime": 0,
"statistic_wordcount": 0, "statistic_wordcount": 0,
"statistic_wordcount_nodump": 0, "statistic_wordcount_nodump": 0,

View File

@ -311,6 +311,31 @@ _selfdefpost = None
_selfdefpostmd5 = None _selfdefpostmd5 = None
def trymapnameofvndb(s):
if not ((globalconfig["languageuse"] == 2) and globalconfig["vndbmapname"]):
return s
try:
exepath = gobject.baseobject.textsource.pname
namemap = savehook_new_data[exepath]["namemap"]
bettermap = {}
for k, v in namemap.items():
spja = k.split("")
spen = v.split(" ")
if len(spja) == len(spen) and len(spen) > 1:
for i in range(len(spja)):
if len(spja[i]) >= 2:
bettermap[spja[i]] = spen[i]
for k, v in namemap.items():
s = s.replace(k, v)
for k, v in bettermap.items():
s = s.replace(k, v)
except:
pass
return s
def POSTSOLVE(line): def POSTSOLVE(line):
global _selfdefpostmd5, _selfdefpost global _selfdefpostmd5, _selfdefpost
if line == "": if line == "":
@ -399,4 +424,5 @@ def POSTSOLVE(line):
print_exc() print_exc()
if postitem == "_11": if postitem == "_11":
raise e raise e
line = trymapnameofvndb(line)
return line return line

View File

@ -18,10 +18,29 @@ from myutils.config import (
getdefaultsavehook, getdefaultsavehook,
) )
import threading, queue import threading, queue
import re import re, heapq
from myutils.vndb import searchforidimage from myutils.vndb import searchforidimage
class PriorityQueue:
def __init__(self):
self._heap = []
self._sema = threading.Semaphore(0)
self._idx = 0
def put(self, item, priority=0):
heapq.heappush(self._heap, (-priority, self._idx, item))
self._idx += 1
self._sema.release()
def get(self):
self._sema.acquire()
return heapq.heappop(self._heap)[-1]
def empty(self):
return bool(len(self._heap) == 0)
def checkimage(gamepath): def checkimage(gamepath):
return (savehook_new_data[gamepath]["imagepath"] is None) or ( return (savehook_new_data[gamepath]["imagepath"] is None) or (
os.path.exists(savehook_new_data[gamepath]["imagepath"]) == False os.path.exists(savehook_new_data[gamepath]["imagepath"]) == False
@ -49,7 +68,7 @@ def checkneed(gamepath):
return (gamepath in savehook_new_data) and (checkvid(gamepath)) return (gamepath in savehook_new_data) and (checkvid(gamepath))
searchvndbqueue = queue.Queue() searchvndbqueue = PriorityQueue()
def dispatachtask(gamepath): def dispatachtask(gamepath):
@ -57,7 +76,7 @@ def dispatachtask(gamepath):
return return
__t = [] __t = []
if savehook_new_data[gamepath]["vid"]: if savehook_new_data[gamepath]["vid"]:
searchvndbqueue.put((gamepath, [savehook_new_data[gamepath]["vid"]])) searchvndbqueue.put((gamepath, [savehook_new_data[gamepath]["vid"]]), 0)
else: else:
for _ in [ for _ in [
savehook_new_data[gamepath]["title"], savehook_new_data[gamepath]["title"],
@ -82,7 +101,7 @@ def dispatachtask(gamepath):
if (len(t) < 10) and (all(ord(c) < 128 for c in t)): if (len(t) < 10) and (all(ord(c) < 128 for c in t)):
continue continue
lst.append(t) lst.append(t)
searchvndbqueue.put((gamepath, lst)) searchvndbqueue.put((gamepath, lst), 0)
def everymethodsthread(): def everymethodsthread():
@ -102,13 +121,21 @@ def everymethodsthread():
saveimg = data.get("imagepath", None) saveimg = data.get("imagepath", None)
saveinfo = data.get("infopath", None) saveinfo = data.get("infopath", None)
vid = data.get("vid", None) vid = data.get("vid", None)
print(data) title = data.get("title", None)
namemap = data.get("namemap", None)
if not vid: if not vid:
continue continue
savehook_new_data[gamepath]["vid"] = int(vid[1:]) savehook_new_data[gamepath]["vid"] = int(vid[1:])
if checkimage(gamepath): if saveimg and (not savehook_new_data[gamepath]["isimagepathusersetted"]):
savehook_new_data[gamepath]["imagepath"] = saveimg savehook_new_data[gamepath]["imagepath"] = saveimg
if title and (not savehook_new_data[gamepath]["istitlesetted"]):
savehook_new_data[gamepath]["title"] = title
if saveinfo:
savehook_new_data[gamepath]["infopath"] = saveinfo savehook_new_data[gamepath]["infopath"] = saveinfo
if namemap:
savehook_new_data[gamepath]["namemap"] = namemap
print(namemap)
succ = True succ = True
break break
if succ == False: if succ == False:
@ -118,6 +145,17 @@ def everymethodsthread():
threading.Thread(target=everymethodsthread).start() threading.Thread(target=everymethodsthread).start()
def vidchangedtask(gamepath, vid):
try:
vid = int(vid)
except:
return
savehook_new_data[gamepath]["vid"] = vid
savehook_new_data[gamepath]["infopath"] = None
savehook_new_data[gamepath]["searchnoresulttime"] = 0
searchvndbqueue.put((gamepath, [vid]), 1)
def checkifnewgame(gamepath, title=None): def checkifnewgame(gamepath, title=None):
if gamepath not in savehook_new_list: if gamepath not in savehook_new_list:
savehook_new_list.insert(0, gamepath) savehook_new_list.insert(0, gamepath)

View File

@ -72,69 +72,103 @@ def vndbdowloadinfo(vid):
return savepath return savepath
def searchforidimage(title): def safegetvndbjson(url, json, getter):
if isinstance(title, str): try:
_ = requests.post(
url,
json=json,
proxies=getproxy(),
)
try:
return getter(_.json())
except:
print(_.text)
return None
except:
return None
def gettitlebyid(vid):
def _getter(js):
try:
return js["results"][0]["titles"][0]["title"] # ja title
except:
return js["results"][0]["title"] # en title
return safegetvndbjson(
"https://api.vndb.org/kana/vn",
{"filters": ["id", "=", vid], "fields": "title,titles.title"},
_getter,
)
def getimgbyid(vid):
return safegetvndbjson(
"https://api.vndb.org/kana/vn",
{"filters": ["id", "=", vid], "fields": "image.url"},
lambda js: js["results"][0]["image"]["url"],
)
def getvidbytitle_vn(title):
return safegetvndbjson(
"https://api.vndb.org/kana/vn",
{"filters": ["search", "=", title], "fields": "id", "sort": "searchrank"},
lambda js: js["results"][0]["id"],
)
def getvidbytitle_release(title):
return safegetvndbjson(
"https://api.vndb.org/kana/release",
{"filters": ["search", "=", title], "fields": "id", "sort": "searchrank"},
lambda js: js["results"][0]["id"],
)
def getvidbytitle(title):
vid = getvidbytitle_vn(title)
if vid:
return vid
return getvidbytitle_release(title)
def getcharnamemapbyid(vid):
res = safegetvndbjson(
"https://api.vndb.org/kana/character",
{
"filters": [
"vn",
"=",
["id", "=", vid],
],
"fields": "name,original",
},
lambda js: js["results"],
)
namemap = {}
try:
for r in res:
namemap[r["original"]] = r["name"]
except:
pass
return namemap
def searchforidimage(titleorid):
print(titleorid)
if os.path.exists("./cache/vndb") == False: if os.path.exists("./cache/vndb") == False:
os.mkdir("./cache/vndb") os.mkdir("./cache/vndb")
js = requests.post( if isinstance(titleorid, str):
"https://api.vndb.org/kana/vn", vid = getvidbytitle(titleorid)
json={ elif isinstance(titleorid, int):
"filters": ["search", "=", title], vid = "v{}".format(titleorid)
"fields": "image.url", img = getimgbyid(vid)
"sort": "searchrank", title = gettitlebyid(vid)
}, namemap = getcharnamemapbyid(vid)
proxies=getproxy(),
)
try:
results = js.json()["results"]
except:
print(js.text)
return {}
if len(results) == 0:
js = requests.post(
"https://api.vndb.org/kana/release",
json={
"filters": ["search", "=", title],
"fields": "vns.id",
"sort": "searchrank",
},
proxies=getproxy(),
)
results = js.json()["results"]
if len(results) == 0:
return {}
vns = results[0]["vns"]
if len(vns) == 0:
return {}
vid = vns[0]["id"]
js = requests.post(
"https://api.vndb.org/kana/vn",
json={"filters": ["id", "=", vid], "fields": "image.url"},
proxies=getproxy(),
)
try:
results = js.json()["results"]
except:
print(js.text)
return {}
img = results[0]["image"]["url"]
else:
img = results[0]["image"]["url"]
vid = results[0]["id"]
elif isinstance(title, int):
vid = "v{}".format(title)
js = requests.post(
"https://api.vndb.org/kana/vn",
json={"filters": ["id", "=", vid], "fields": "image.url"},
proxies=getproxy(),
)
try:
results = js.json()["results"]
except:
print(js.text)
return {}
img = results[0]["image"]["url"]
return { return {
"namemap": namemap,
"title": title,
"vid": vid, "vid": vid,
"infopath": vndbdowloadinfo(vid), "infopath": vndbdowloadinfo(vid),
"imagepath": vndbdownloadimg(img), "imagepath": vndbdownloadimg(img),

View File

@ -0,0 +1,11 @@
from textoutput.outputerbase import Base
from myutils.config import globalconfig
import winsharedutils
class Outputer(Base):
def dispatch(self, text):
if globalconfig["sourcestatus2"]["copy"]["use"]:
return
winsharedutils.clipboard_set(text)

View File

@ -0,0 +1,29 @@
from myutils.config import globalconfig
from threading import Thread
from queue import Queue
class Base:
@property
def config(self):
return globalconfig["textoutputer"][self.classname]
def dispatch(self, text):
pass
def init(self):
pass
def __init__(self, classname):
self.classname = classname
self.queue = Queue()
self.init()
Thread(target=self.dothread).start()
def dothread(self):
while True:
text = self.queue.get()
self.dispatch(text)
def puttask(self, text):
self.queue.put(text)

View File

@ -0,0 +1,105 @@
from textoutput.outputerbase import Base
from traceback import print_exc
import socket
from base64 import encodebytes as base64encode
import hashlib
from myutils.wrapper import threader
class websocketserver:
def stop(self):
self.server_socket.close()
for sock in self.connectedsockets:
try:
sock.close()
except:
pass
def __init__(self, port):
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.bind(("localhost", port))
self.connectedsockets = []
self.errorsocks = []
self.listen()
@threader
def listen(self):
self.server_socket.listen(1)
while True:
client_socket, address = self.server_socket.accept()
print(f"Client connected: {address}")
self.handle_client(client_socket)
@threader
def handle_client(self, client_socket: socket.socket):
# 接收客户端的握手请求
request = client_socket.recv(1024).decode()
# 解析握手请求中的 WebSocket 关键信息
key = ""
for line in request.split("\r\n"):
if "Sec-WebSocket-Key:" in line:
key = line.split(":")[1].strip()
break
value = f"{key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11".encode("utf-8")
hashed = base64encode(hashlib.sha1(value).digest()).strip().lower().decode()
# 构造握手响应
response = "HTTP/1.1 101 Switching Protocols\r\n"
response += "Upgrade: websocket\r\n"
response += "Connection: Upgrade\r\n"
response += "sec-websocket-protocol: 111\r\n"
response += f"Sec-WebSocket-Accept: {hashed}\r\n\r\n"
# 发送握手响应给客户端
client_socket.send(response.encode())
self.connectedsockets.append(client_socket)
def maketextframe(self, message):
fin = 1
opcode = 0x1
payload = message.encode("utf-8")
length = len(payload)
frame = bytearray()
frame.append((fin << 7) | opcode)
if length <= 125:
frame.append(length)
elif length <= 65535:
frame.extend([126, (length >> 8) & 0xFF, length & 0xFF])
else:
frame.extend([127] + [(length >> (8 * i)) & 0xFF for i in range(7, -1, -1)])
frame.extend(payload)
return frame
def sendtext(self, text):
frame = self.maketextframe(text)
for i, sock in enumerate(self.connectedsockets):
if i in self.errorsocks:
continue
try:
sock.send(frame)
except:
self.errorsocks.append(i)
class Outputer(Base):
def init(self):
try:
self.server.stop()
except:
pass
if not self.config["use"]:
return
try:
self.server = websocketserver(self.config["port"])
except:
print_exc()
def dispatch(self, text):
self.server.sendtext(text)

View File

@ -3,12 +3,12 @@ from queue import Queue
from myutils.config import globalconfig, translatorsetting, static_data from myutils.config import globalconfig, translatorsetting, static_data
from threading import Thread from threading import Thread
import threading, time, types, heapq import time, types
import zhconv, gobject import zhconv, gobject
import sqlite3 import sqlite3
from myutils.commonbase import commonbase from myutils.commonbase import commonbase
import functools import functools
from myutils.utils import stringfyerror, autosql from myutils.utils import stringfyerror, autosql, PriorityQueue
from myutils.commonbase import ArgsEmptyExc from myutils.commonbase import ArgsEmptyExc
@ -57,25 +57,6 @@ def timeoutfunction(
return t.get_result(timeout, checktutukufunction) return t.get_result(timeout, checktutukufunction)
class PriorityQueue:
def __init__(self):
self._heap = []
self._sema = threading.Semaphore(0)
self._idx = 0
def put(self, item, priority=0):
heapq.heappush(self._heap, (-priority, self._idx, item))
self._idx += 1
self._sema.release()
def get(self):
self._sema.acquire()
return heapq.heappop(self._heap)[-1]
def empty(self):
return bool(len(self._heap) == 0)
class basetrans(commonbase): class basetrans(commonbase):
def langmap(self): def langmap(self):
return {} return {}

View File

@ -11,11 +11,21 @@
"read_trans": false, "read_trans": false,
"read_translator": 0, "read_translator": 0,
"disappear_delay": 5, "disappear_delay": 5,
"vndbmapname":false,
"network":1, "network":1,
"hookmagpie":true, "hookmagpie":true,
"hooklossless":true, "hooklossless":true,
"direct_filterrepeat":false, "direct_filterrepeat":false,
"allow_set_text_name":false, "allow_set_text_name":false,
"textoutputer":{
"clipboard":{
"use":false
},
"websocket":{
"use":false,
"port":2333
}
},
"embedded": { "embedded": {
"safecheck_use":true, "safecheck_use":true,
"safecheckregexs": [ "safecheckregexs": [
@ -682,7 +692,6 @@
"lighttheme":0, "lighttheme":0,
"usesearchword": false, "usesearchword": false,
"usecopyword":false, "usecopyword":false,
"outputtopasteboard": false,
"ttscommon": { "ttscommon": {
"rate": 1.0, "rate": 1.0,
"volume": 100.0, "volume": 100.0,

View File

@ -1,5 +1,5 @@
{ {
"version":"v2.42.1", "version":"v2.43.0",
"themes":{ "themes":{
"dark":[ "dark":[
{"file":"dark1.qss","name":"PyQtDarkTheme"}, {"file":"dark1.qss","name":"PyQtDarkTheme"},
@ -101,6 +101,10 @@
"name": "MeCab", "name": "MeCab",
"link": "https://github.com/HIllya51/RESOURCES/releases/download/dictionary/Mecab.zip" "link": "https://github.com/HIllya51/RESOURCES/releases/download/dictionary/Mecab.zip"
}, },
{
"name": "MeCab_unidic_latest",
"link": "https://clrd.ninjal.ac.jp/unidic/"
},
{ {
"name": "Unidic", "name": "Unidic",
"link": "https://clrd.ninjal.ac.jp/unidic_archive/2302/unidic-cwj-202302.zip" "link": "https://clrd.ninjal.ac.jp/unidic_archive/2302/unidic-cwj-202302.zip"

View File

@ -743,5 +743,6 @@
"不被打断": "لا يقاطع", "不被打断": "لا يقاطع",
"显示/隐藏历史翻译": "إظهار / إخفاء التاريخ", "显示/隐藏历史翻译": "إظهار / إخفاء التاريخ",
"全屏/恢复游戏窗口": "كامل الشاشة / استعادة نافذة اللعبة", "全屏/恢复游戏窗口": "كامل الشاشة / استعادة نافذة اللعبة",
"未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "لا تحميل نموذج التعرف الضوئي على الحروف في هذه اللغة ، يرجى [ إعدادات أخرى ] - > [ تحميل الموارد ] - > [ التعرف الضوئي على الحروف حزمة اللغة ] تحميل نموذج استخراج الملفات / التعرف الضوئي على الحروف الطريق بعد استخدام" "未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "لا تحميل نموذج التعرف الضوئي على الحروف في هذه اللغة ، يرجى [ إعدادات أخرى ] - > [ تحميل الموارد ] - > [ التعرف الضوئي على الحروف حزمة اللغة ] تحميل نموذج استخراج الملفات / التعرف الضوئي على الحروف الطريق بعد استخدام",
"使用VNDB数据替换人名": "استبدال اسم الشخص مع بيانات فندب"
} }

View File

@ -743,5 +743,6 @@
"不被打断": "不被打斷", "不被打断": "不被打斷",
"显示/隐藏历史翻译": "顯示/隱藏歷史翻譯", "显示/隐藏历史翻译": "顯示/隱藏歷史翻譯",
"全屏/恢复游戏窗口": "全屏/恢復遊戲視窗", "全屏/恢复游戏窗口": "全屏/恢復遊戲視窗",
"未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "未下載該語言的OCR模型請在[其他設定]->[資源下載]->[OCR語言包]下載模型解壓到files/ocr路徑後使用" "未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "未下載該語言的OCR模型請在[其他設定]->[資源下載]->[OCR語言包]下載模型解壓到files/ocr路徑後使用",
"使用VNDB数据替换人名": "使用VNDB數據替換人名"
} }

View File

@ -743,5 +743,6 @@
"不被打断": "Not interrupted", "不被打断": "Not interrupted",
"显示/隐藏历史翻译": "Show/hide historical translations", "显示/隐藏历史翻译": "Show/hide historical translations",
"全屏/恢复游戏窗口": "Full screen/restore game window", "全屏/恢复游戏窗口": "Full screen/restore game window",
"未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "The OCR model for this language has not been downloaded. Please unzip the model to the files/ocr path in [Other Settings] ->[Resource Download] ->[OCR Language Pack] and use it" "未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "The OCR model for this language has not been downloaded. Please unzip the model to the files/ocr path in [Other Settings] ->[Resource Download] ->[OCR Language Pack] and use it",
"使用VNDB数据替换人名": "Replace person names with VNDB data"
} }

View File

@ -743,5 +743,6 @@
"不被打断": "Sin ser interrumpido", "不被打断": "Sin ser interrumpido",
"显示/隐藏历史翻译": "Mostrar / ocultar la traducción histórica", "显示/隐藏历史翻译": "Mostrar / ocultar la traducción histórica",
"全屏/恢复游戏窗口": "Pantalla completa / restaurar la ventana del juego", "全屏/恢复游戏窗口": "Pantalla completa / restaurar la ventana del juego",
"未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "El modelo OCR del idioma no se ha descargado, por favor use después de descargar el modelo a la ruta files / OCR [configuración adicional] - > descarga de recursos] - > paquete de lenguaje ocr]" "未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "El modelo OCR del idioma no se ha descargado, por favor use después de descargar el modelo a la ruta files / OCR [configuración adicional] - > descarga de recursos] - > paquete de lenguaje ocr]",
"使用VNDB数据替换人名": "Reemplazar nombres con datos vndb"
} }

View File

@ -743,5 +743,6 @@
"不被打断": "Ne pas être interrompu", "不被打断": "Ne pas être interrompu",
"显示/隐藏历史翻译": "Afficher / masquer les traductions historiques", "显示/隐藏历史翻译": "Afficher / masquer les traductions historiques",
"全屏/恢复游戏窗口": "Plein écran / restaurer la fenêtre de jeu", "全屏/恢复游戏窗口": "Plein écran / restaurer la fenêtre de jeu",
"未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "Le modèle OCR pour cette langue n'a pas été téléchargé, utilisez - le après [autres paramètres] - > [ressources télécharger] - > [OCR Language Pack] télécharger le modèle Décompresser le chemin files / ocr" "未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "Le modèle OCR pour cette langue n'a pas été téléchargé, utilisez - le après [autres paramètres] - > [ressources télécharger] - > [OCR Language Pack] télécharger le modèle Décompresser le chemin files / ocr",
"使用VNDB数据替换人名": "Remplacement des noms de personnes par des données vndb"
} }

View File

@ -743,5 +743,6 @@
"不被打断": "Non interrotto", "不被打断": "Non interrotto",
"显示/隐藏历史翻译": "Mostra/nasconde traduzioni storiche", "显示/隐藏历史翻译": "Mostra/nasconde traduzioni storiche",
"全屏/恢复游戏窗口": "Finestra di gioco a schermo intero/ripristino", "全屏/恢复游戏窗口": "Finestra di gioco a schermo intero/ripristino",
"未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "Il modello OCR per questa lingua non è stato scaricato. Si prega di decomprimere il modello nel percorso file/ocr in [Altre impostazioni] ->[Scaricare risorse] ->[OCR Language Pack] e utilizzarlo" "未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "Il modello OCR per questa lingua non è stato scaricato. Si prega di decomprimere il modello nel percorso file/ocr in [Altre impostazioni] ->[Scaricare risorse] ->[OCR Language Pack] e utilizzarlo",
"使用VNDB数据替换人名": "Sostituisci i nomi delle persone con dati VNDB"
} }

View File

@ -743,5 +743,6 @@
"不被打断": "中断されない", "不被打断": "中断されない",
"显示/隐藏历史翻译": "履歴翻訳の表示/非表示", "显示/隐藏历史翻译": "履歴翻訳の表示/非表示",
"全屏/恢复游戏窗口": "フルスクリーン/リカバリゲームウィンドウ", "全屏/恢复游戏窗口": "フルスクリーン/リカバリゲームウィンドウ",
"未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "この言語のOCRモデルはダウンロードされていません。[その他の設定]->[リソースダウンロード]->[OCR言語パック]ダウンロードモデルをfiles/ocrパスに解凍した後に使用してください" "未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "この言語のOCRモデルはダウンロードされていません。[その他の設定]->[リソースダウンロード]->[OCR言語パック]ダウンロードモデルをfiles/ocrパスに解凍した後に使用してください",
"使用VNDB数据替换人名": "VNDBデータを使用した人名の置換"
} }

View File

@ -743,5 +743,6 @@
"离线": "오프라인", "离线": "오프라인",
"显示/隐藏历史翻译": "히스토리 번역 표시 / 숨기기", "显示/隐藏历史翻译": "히스토리 번역 표시 / 숨기기",
"全屏/恢复游戏窗口": "전체 화면 / 게임 창 복원", "全屏/恢复游戏窗口": "전체 화면 / 게임 창 복원",
"未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "이 언어의 OCR 모델을 다운로드하지 않았습니다. [기타 설정] -> [에셋 다운로드] -> [OCR 언어 팩] 모델을 다운로드하여 files/ocr 경로로 압축을 푼 후 사용하십시오." "未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "이 언어의 OCR 모델을 다운로드하지 않았습니다. [기타 설정] -> [에셋 다운로드] -> [OCR 언어 팩] 모델을 다운로드하여 files/ocr 경로로 압축을 푼 후 사용하십시오.",
"使用VNDB数据替换人名": "VNDB 데이터로 사람 이름 바꾸기"
} }

View File

@ -743,5 +743,6 @@
"不被打断": "Bez przerwy", "不被打断": "Bez przerwy",
"显示/隐藏历史翻译": "Pokaż/ukryj tłumaczenia historyczne", "显示/隐藏历史翻译": "Pokaż/ukryj tłumaczenia historyczne",
"全屏/恢复游戏窗口": "Pełny ekran/przywróć okno gry", "全屏/恢复游戏窗口": "Pełny ekran/przywróć okno gry",
"未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "Model OCR dla tego języka nie został pobrany. Proszę rozpakować model do ścieżki plików/ocr w [Inne ustawienia] ->[Pobieranie zasobów] ->[OCR Language Pack] i użyć go" "未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "Model OCR dla tego języka nie został pobrany. Proszę rozpakować model do ścieżki plików/ocr w [Inne ustawienia] ->[Pobieranie zasobów] ->[OCR Language Pack] i użyć go",
"使用VNDB数据替换人名": "Zastąp nazwiska osób na dane VNDB"
} }

View File

@ -743,5 +743,6 @@
"非官方": "Неофициальные", "非官方": "Неофициальные",
"显示/隐藏历史翻译": "Показать / скрыть исторический перевод", "显示/隐藏历史翻译": "Показать / скрыть исторический перевод",
"全屏/恢复游戏窗口": "Полноэкранное / Восстановление игрового окна", "全屏/恢复游戏窗口": "Полноэкранное / Восстановление игрового окна",
"未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "Модель OCR для этого языка не загружена, используйте ее после того, как [другие настройки] - > [загрузка ресурсов] - > [языковой пакет OCR] загрузит модель на путь files / ocr" "未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "Модель OCR для этого языка не загружена, используйте ее после того, как [другие настройки] - > [загрузка ресурсов] - > [языковой пакет OCR] загрузит модель на путь files / ocr",
"使用VNDB数据替换人名": "Использование данных VNDB для замены имен"
} }

View File

@ -743,5 +743,6 @@
"不被打断": "ไม่ถูกขัดจังหวะ", "不被打断": "ไม่ถูกขัดจังหวะ",
"显示/隐藏历史翻译": "แสดง/ซ่อนการแปลประวัติ", "显示/隐藏历史翻译": "แสดง/ซ่อนการแปลประวัติ",
"全屏/恢复游戏窗口": "เต็มหน้าจอ/กู้คืนหน้าต่างเกม", "全屏/恢复游戏窗口": "เต็มหน้าจอ/กู้คืนหน้าต่างเกม",
"未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "ไม่มีการดาวน์โหลดรุ่น OCR สำหรับภาษาโปรดใช้หลังจาก [การตั้งค่าอื่น ๆ] -> [ดาวน์โหลดทรัพยากร] -> [ชุดภาษา OCR] ดาวน์โหลดแบบจำลองเปิดเส้นทางไฟล์ / OCR" "未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "ไม่มีการดาวน์โหลดรุ่น OCR สำหรับภาษาโปรดใช้หลังจาก [การตั้งค่าอื่น ๆ] -> [ดาวน์โหลดทรัพยากร] -> [ชุดภาษา OCR] ดาวน์โหลดแบบจำลองเปิดเส้นทางไฟล์ / OCR",
"使用VNDB数据替换人名": "แทนที่ชื่อบุคคลด้วยข้อมูล VNDB"
} }

View File

@ -743,5 +743,6 @@
"不被打断": "Bırakılmadı", "不被打断": "Bırakılmadı",
"显示/隐藏历史翻译": "Tarihi çevirimleri göster/gizle", "显示/隐藏历史翻译": "Tarihi çevirimleri göster/gizle",
"全屏/恢复游戏窗口": "Full screen/restore game window", "全屏/恢复游戏窗口": "Full screen/restore game window",
"未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "Bu dilin OCR modeli indirilmedi. Lütfen modelini [Diğer Ayarlar] ->[Kaynak İndirme] ->[OCR Dil Paketi] içindeki dosyalara/ikiyüzlü yola bağlayın ve kullanın" "未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "Bu dilin OCR modeli indirilmedi. Lütfen modelini [Diğer Ayarlar] ->[Kaynak İndirme] ->[OCR Dil Paketi] içindeki dosyalara/ikiyüzlü yola bağlayın ve kullanın",
"使用VNDB数据替换人名": "İnsan isimlerini VNDB veriyle değiştir"
} }

View File

@ -743,5 +743,6 @@
"不被打断": "Не перервано", "不被打断": "Не перервано",
"显示/隐藏历史翻译": "Показувати/сховати історичні переклади", "显示/隐藏历史翻译": "Показувати/сховати історичні переклади",
"全屏/恢复游戏窗口": "Повний екран / відновити вікно гри", "全屏/恢复游戏窗口": "Повний екран / відновити вікно гри",
"未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "Модель OCR для цієї мови не було звантажено. Будь ласка, відкрийте модель до шляху до файлів/ocr у [Інші параметри] ->[Звантаження ресурсів] ->[Пакет мови OCR] і скористайтеся ним" "未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "Модель OCR для цієї мови не було звантажено. Будь ласка, відкрийте модель до шляху до файлів/ocr у [Інші параметри] ->[Звантаження ресурсів] ->[Пакет мови OCR] і скористайтеся ним",
"使用VNDB数据替换人名": "Замінити назви людей даними VNDB"
} }

View File

@ -743,5 +743,6 @@
"非官方": "Không chính thức", "非官方": "Không chính thức",
"显示/隐藏历史翻译": "Hiện/ẩn bản dịch lịch sử", "显示/隐藏历史翻译": "Hiện/ẩn bản dịch lịch sử",
"全屏/恢复游戏窗口": "Toàn màn hình/Khôi phục cửa sổ trò chơi", "全屏/恢复游戏窗口": "Toàn màn hình/Khôi phục cửa sổ trò chơi",
"未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "Mô hình OCR cho ngôn ngữ này chưa được tải xuống, vui lòng sử dụng sau khi [Cài đặt bổ sung] ->[Tải xuống tài nguyên] ->[Gói ngôn ngữ OCR] mô hình tải xuống đã được giải nén vào đường dẫn files/ocr" "未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "Mô hình OCR cho ngôn ngữ này chưa được tải xuống, vui lòng sử dụng sau khi [Cài đặt bổ sung] ->[Tải xuống tài nguyên] ->[Gói ngôn ngữ OCR] mô hình tải xuống đã được giải nén vào đường dẫn files/ocr",
"使用VNDB数据替换人名": "Thay tên người bằng dữ liệu VNDB"
} }

View File

@ -129,7 +129,6 @@
"其他": "", "其他": "",
"设置所有词条为全局词条": "", "设置所有词条为全局词条": "",
"过滤数字和英文字母": "", "过滤数字和英文字母": "",
"提取的文本自动复制到剪贴板": "",
"Unicode范围": "", "Unicode范围": "",
"原文颜色": "", "原文颜色": "",
"未开始": "", "未开始": "",
@ -743,5 +742,10 @@
"不被打断": "", "不被打断": "",
"显示/隐藏历史翻译": "", "显示/隐藏历史翻译": "",
"全屏/恢复游戏窗口": "", "全屏/恢复游戏窗口": "",
"未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "" "未下载该语言的OCR模型,请在[其他设置]->[资源下载]->[OCR语言包]下载模型解压到files/ocr路径后使用": "",
"使用VNDB数据替换人名": "",
"文本输出": "",
"自动输出提取的文本": "",
"输出到剪贴板": "",
"输出到WebSocket": ""
} }