LunaTranslator 内置网络服务完全指南:Web 页面、HTTP/WebSocket API 与源码级解析

原创2026-09-14 22:08:551,413 阅读
文章标签:桌面应用AI 应用

LunaTranslator 内置网络服务完全指南:Web 页面、HTTP/WebSocket API 与源码级解析

LunaTranslator 作为一款视觉小说翻译器,内置了一套完整的本地网络服务:开启后即可通过浏览器访问与主程序实时同步的 Web 界面,并通过 HTTP 与 WebSocket 接口调用翻译、词典、Mecab 分词、OCR、TTS 等核心能力。本文以官方文档 docs/cht/apiservice.md 为骨架,结合 servicecollection.py、tcpservice.py 等源码,完整讲解每一个页面与接口的用途、请求格式、响应结构,以及背后的实现原理,帮助你将其接入自己的工具链。

一、服务概览:如何开启与访问

网络服务默认是关闭的,需要手动开启。在 LunaTranslator 的设置界面(对应源码 textinput.py)中有两个关键配置项:

配置项 默认值 说明
networktcpenable false 网络服务总开关,开启后立即启动 TCP 服务
networktcpport 2333 监听端口,取值范围 0–65535,修改后即时生效

设置界面还附带一个"打开"按钮,点击后调用 os.startfile 直接以默认浏览器打开 http://127.0.0.1:{端口号}。也就是说,开启后默认访问地址即为:

http://127.0.0.1:2333

服务的启动逻辑在 LunaTranslator.py 的 serviceinit() 中:

@threader
def serviceinit(self):
    gobject.base.portconflict.emit("")
    self.service.stop()
    if globalconfig.get("networktcpenable", False):
        try:
            self.service.init(globalconfig.get("networktcpport", 2333))
        except OSError:
            gobject.base.portconflict.emit("端口冲突")

值得注意的几点:

  • 服务在独立线程中启动,不会阻塞主程序 UI;
  • 每次开启前会先 stop() 旧服务,因此修改端口号后无需重启程序,设置界面回调 serviceinit() 即会以新端口重新监听;
  • 如果端口已被占用,socket.bind 抛出 OSError,界面上会显示"端口冲突"提示。

从源码看,TCPService.init() 使用 socket.bind(("0.0.0.0", port))(见 tcpservice.py),意味着监听所有网卡地址,局域网内的其他设备也可以通过 http://<主机IP>:2333 访问;如果你只希望本机访问,请在防火墙或代理层面自行限制。服务注册的全部处理器由 registerall() 一次性挂载(见 servicecollection.py)。

二、Web 页面:浏览器里的 LunaTranslator

Web 页面与桌面端共享同一套渲染逻辑——主界面、历史记录页面的 HTML 模板复用自 gui 模块(TextBrowser.loadex_()、wvtranshist.loadex_(),对应 htmlcode/uiwebview 下的 mainui.html、transhist.html),因此浏览器中看到的内容与主窗口高度一致。

导航页 /

导航页,列出全部页面入口。其 HTML 实现位于 htmlcode/service/index.html,实际内容即一组指向各页面的链接:

<a href="/page/mainui">/page/mainui</a>
<a href="/page/transhist">/page/transhist</a>
<a href="/page/dictionary">/page/dictionary</a>
<a href="/page/manyinone">/page/manyinone</a>
<a href="/page/translate">/page/translate</a>
<a href="/page/ocr">/page/ocr</a>
<a href="/page/tts">/page/tts</a>

对应的处理器是 servicecollection.py 中的 PageIndex。

主界面 /page/mainui

与主窗口显示的文字内容同步。包括原文、翻译结果,甚至支持在页面上点击单词查词——此时会唤出 /page/dictionary 页面。

底层机制:页面通过 WebSocket 与程序保持实时同步。处理器 PageMainui 返回 TextBrowser.loadex_() 的 HTML,而页面内的连接对应内部 WebSocket 端点 /__internalservice/mainuiws(servicecollection.py),它把每个活跃连接登记进 mainuiwsoutputsave 列表,主窗口文本刷新时通过 WSForEach 批量推送;页面还能回传事件(calllunaclickedword 等)触发主程序的点击查词回调。

历史记录 /page/transhist

与历史文本窗口显示的内容同步,同样支持页面内交互(展开/收起原文、切换翻译名称等),对应内部端点 /__internalservice/transhistws(servicecollection.py)。

查词页面 /page/dictionary

查询单词的页面。在 /page/mainui 中点击单词时即会唤出此页。处理器 PageSearchWord(servicecollection.py)有一个实用细节:支持 URL 参数 word,若该词存在"原型"(prototype,例如动词活用还原为原形),会自动 302 重定向到原型词条。

整合页面 /page/manyinone

将 /page/mainui、/page/transhist、/page/dictionary 三个页面整合为一个页面(manyinone.html)。在该页面内的主界面子区域点击单词查词时,不会新开查词窗口,而是在当前窗口的词典子区域就地显示结果,适合单窗口集中操作。

翻译界面 /page/translate

独立的翻译输入界面,对应 translate.html。适合临时手动翻译一段文本。

OCR 界面 /page/ocr

OCR 识别界面,对应 ocr.html,页面中上传/粘贴图片后调用后端 OCR 引擎返回识别文本。

TTS 界面 /page/tts

TTS 语音合成界面,对应 tts.html,输入文本即可合成为语音并播放。

三、HTTP API:程序化调用核心能力

所有 API 均支持跨域(响应头固定携带 Access-Control-Allow-Origin: *,见 tcpservice.py),因此可以直接从前端页面或 Node/Python 脚本跨域调用。

3.1 翻译 /api/translate

  • 必填查询参数:text
  • 可选查询参数:id(翻译器的 ID)
  • 返回:application/json

当指定 id 时强制使用该翻译器;不指定时,自动选择"最快的翻译接口"(即当前配置排序下可用的第一个)。

请求示例:

GET /api/translate?text=こんにちは&id=google

成功响应(字段含义:id 翻译器 ID、name 翻译器名称、result 翻译结果):

{
  "id": "google",
  "name": "Google 翻译",
  "result": "你好"
}

失败时返回错误信息:

{
  "error": "翻译失败的具体原因",
  "id": "google",
  "name": "Google 翻译"
}

实现位于 servicecollection.py:请求被转交给 gobject.base.textgetmethod(),waitforresultcallbackengine 指定翻译器、waitforresultcallbackengine_force=True 表示强制使用指定引擎,通过 threading.Event 同步等待翻译完成后返回结果。

3.2 词典查询 /api/dictionary

  • 必填查询参数:word
  • 可选查询参数:id(词典的 ID)

指定 id 时:仅查询该词典,返回 application/json,包含词典 ID id、名称 name、HTML 内容 result;查询失败则返回空对象 {}:

{
  "id": "jisho",
  "name": "Jisho",
  "result": "<div>…查词结果 HTML…</div>"
}

不指定 id 时:并行查询所有已启用词典,响应为 text/event-stream(SSE),每个 Event 是一个 JSON 对象,结构与上述相同。实现见 APISearchWord:无 id 时遍历 gobject.base.cishus,用 threading.Semaphore 并发收集各词典结果并以生成器(Generator)形式逐条产出,响应体被自动识别为 SSE 流(见下文"底层实现")。

3.3 Mecab 分词 /api/mecab

  • 必填查询参数:text
  • 返回:Mecab 对 text 的解析结果(JSON 数组)

用于日文分词/形态分析,底层调用 gobject.base.parsehira(text)(servicecollection.py)。该功能依赖项目中 Mecab 模块(myutils/mecab.py)及对应的原生解析组件。

3.4 TTS 合成 /api/tts

  • 必填查询参数:text
  • 返回:音频二进制数据(HTTP 响应)

实现(servicecollection.py)调用 gobject.base.reader.ttscallback() 异步合成语音,通过 threading.Event 等待合成完成后,以 ResponseWithHeader 返回原始音频数据,响应头中的 Content-Type 为当前 TTS 引擎的 MIME 类型(如 audio/mpeg),并附带 Content-Length;若合成失败则返回 JSON 错误对象。

3.5 OCR 识别 /api/ocr

  • 方法:POST
  • 请求体:JSON,含 image 字段,值为 Base64 编码的图片(注意源码中并不包含 data:image/... 前缀,需要自行保证是纯 Base64 数据)
  • 返回:OCR 识别结果 JSON

实现(servicecollection.py):base64.b64decode 解码后经 QImage.loadFromData 还原为图像,交给 ocr_run()(见 myutils/ocrutil.py)走完整的 OCR 引擎链路,返回结果对象的 json 序列化。

请求示例:

POST /api/ocr
Content-Type: application/json

{"image": "<base64编码的图片数据>"}

3.6 列出可用词典 /api/list/dictionary

返回当前可用的词典列表(JSON 数组),每项包含 id 与 name。实现遍历 globalconfig<a href="https://link.gitcode.com/i/95be484c4250a0deedba9845accab0e2" target="_blank">"cishuvisrank"] 并过滤未实例化的词典([servicecollection.py),其结果可直接用于 /api/dictionary 的 id 参数。

3.7 列出可用翻译器 /api/list/translator

返回当前可用的翻译器列表(JSON 数组),每项包含 id 与 name。实现遍历 globalconfig<a href="https://link.gitcode.com/i/1693fd6d1a24b0f232e1431186b72750" target="_blank">"fix_translate_rank_rank"](翻译器排序配置)并过滤未实例化的引擎([servicecollection.py),其结果可直接用于 /api/translate 的 id 参数。

3.8 文本输入 /api/textinput

  • 必填查询参数:text
  • 无返回内容

将文本送入 LunaTranslator 的文本处理管线(等价于手动输入文本触发翻译流程),实现调用 gobject.base.textgetmethod(text, is_auto_run=False)(servicecollection.py)。典型用途:把外部复制的文本注入翻译器统一处理。

四、WebSocket 服务:实时文本流

WebSocket 端点用于持续推送实时提取的文本,适合对接需要逐句获取原文/译文的第三方工具(例如弹幕、字幕、聊天机器人)。

/api/ws/text/origin

持续输出所有提取到的原文文本。当客户端连接后,连接对象被加入 wsoutputsave 列表(servicecollection.py),此后每次游戏/OCR 提取到新的原文句子,都会向该连接推送文本帧。

/api/ws/text/trans

持续输出所有翻译结果,机制同上(servicecollection.py)。

这两个端点的推送到派发逻辑在 textio/textoutput/websocket.py:

def dispatch(self, text: str, isorigin: bool):
    def __(handle):
        if isorigin and isinstance(handle, TextOutputOrigin):
            handle.send_text(text)
        elif (not isorigin) and isinstance(handle, TextOutputTrans):
            handle.send_text(text)
    WSForEach(wsoutputsave, __)

要点:

  • 它们本质上是 LunaTranslator 的一种"文本输出器"(Outputer),在设置中启用对应的 WebSocket 输出后,原文与译文才会被推送到所有在线客户端;
  • WSForEach(servicecollection_1.py)在线程中遍历所有连接逐个发送;若某个客户端断开(抛出 OSError),会自动将其从列表移除,不会影响其他连接;
  • 消息格式为 WebSocket 文本帧,每帧一条完整句子。

五、底层实现:自研的轻量 HTTP + WebSocket 服务端

整个网络服务不依赖 Flask/Django 等框架,而是基于 Python 标准库 socket 手写的 TCP 服务(tcpservice.py),理解它有助于排查自定义调用中的异常。

5.1 请求路由与分发

TCPService 维护一个 handlers 列表;每个处理器是一个类,通过类属性 path 声明路径(HTTP 处理器还可通过 method 限定请求方法,如 /api/ocr 限定 POST)。连接建立后:

  1. RequestInfo.readfrom() 解析请求行、请求头和可选请求体,path/query 由 urlsplit + parse_qsl 拆分(tcpservice.py);
  2. 依据请求头中是否有 Upgrade: websocket 区分 WebSocket 与普通 HTTP 请求,再遍历 handlers 按路径精确匹配;
  3. 匹配成功则实例化处理器执行 parse(),失败返回 404。

5.2 丰富的响应类型

ResponseInfo 会根据 parse() 的返回值类型自动选择序列化方式(tcpservice.py):

返回类型 响应表现
str Content-Type: text/html; charset=utf-8
dict / list / tuple Content-Type: application/json; charset=utf-8(ensure_ascii=False)
FileResponse 以 1024 字节分块流式发送文件,MIME 类型由 query_mime 推断
GeneratorType(生成器) Content-Type: text/event-stream(SSE),每项以 data: ...\n\n 逐条发送——这正是 /api/dictionary 全词典查询的实现基础
RedirectResponse 302 重定向(用于查词原型跳转)
ResponseWithHeader 自定义响应头(用于 /api/tts 的音频 MIME 与长度)

5.3 WebSocket 握手与帧协议

WSHandler 实现了 RFC 6455 的核心握手:用 Sec-WebSocket-Key 拼接固定魔数 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 后做 SHA-1 + Base64,得到 Sec-WebSocket-Accept 返回 101 状态码(tcpservice.py)。

随后按帧协议收发:解析 FIN/opcode/mask/扩展长度(126→2 字节、127→8 字节),处理文本帧(opcode 0x1)、关闭帧(0x8)、Ping/Pong 帧(0x9/0xA),服务器发送帧不带掩码(tcpservice.py)。这意味着标准 WebSocket 客户端(浏览器 WebSocket、Python websocket-client 等)均可直接连接。

六、配置项与注意事项

network_service_disabled_paths

默认配置(defaultconfig/config.json)中为 <a href="https://link.gitcode.com/i/e03fae14e560b7a40ae6a6cf9b27c29b" target="_blank">]。TCPService 在路由前会检查请求路径是否命中该列表,命中则直接返回 404([tcpservice.py)。如需对外暴露服务又不想开放某些敏感接口(例如 OCR 或文本输入),可在此列表中加入对应路径(如 "/api/textinput")。

安全与使用提示

  • 服务监听 0.0.0.0,且所有接口无鉴权,请勿在不可信网络中开启,或将端口暴露到公网;
  • /api/ocr 的 image 字段是纯 Base64 字符串,不含 data:image/...;base64, 前缀;
  • 修改端口或开关后即时生效,无需重启程序;端口被占用时会收到"端口冲突"提示;
  • WebSocket 文本推送依赖在输出设置中启用对应的 WebSocket 输出器,否则即使建立了连接也不会收到数据。

七、典型应用场景小结

需求 推荐接口
网页端远程查看/控制主界面文本 /page/mainui、/page/manyinone
自己的程序里翻译文本 /api/translate
集成词典查询(含多词典并行) /api/dictionary
日文分词/形态分析 /api/mecab
语音合成 /api/tts
图片文字识别 /api/ocr(POST + Base64)
实时逐句获取原文/译文 WebSocket /api/ws/text/origin、/api/ws/text/trans
动态发现可用引擎 /api/list/dictionary、/api/list/translator

LunaTranslator 的这套内置网络服务把翻译器最核心的文本处理能力(翻译、查词、分词、OCR、TTS)完整暴露为标准的 HTTP/WebSocket/SSE 接口,且页面与桌面端共享同一渲染与同步链路。无论是想用手机/浏览器做第二屏幕,还是将翻译能力接入自己的脚本与自动化流程,都可以直接基于本指南的接口约定快速落地。

登录后查看全文
LunaTranslator