mirror of
https://github.com/HiMeditator/auto-caption.git
synced 2026-02-04 12:24:42 +08:00
feat(main): 修改 python 子进程与主程序的通信方式,由 WebSocket 改为 IPC 通信
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,4 +4,5 @@ out
|
||||
.DS_Store
|
||||
.eslintcache
|
||||
*.log*
|
||||
__pycache__/
|
||||
__pycache__
|
||||
subenv
|
||||
@@ -5,6 +5,8 @@ from dashscope.audio.asr import (
|
||||
TranslationRecognizerRealtime
|
||||
)
|
||||
from datetime import datetime
|
||||
import json
|
||||
import sys
|
||||
|
||||
class Callback(TranslationRecognizerCallback):
|
||||
"""
|
||||
@@ -17,11 +19,10 @@ class Callback(TranslationRecognizerCallback):
|
||||
self.time_str = ''
|
||||
|
||||
def on_open(self) -> None:
|
||||
print("INFO gummy translation start...")
|
||||
pass
|
||||
|
||||
def on_close(self) -> None:
|
||||
print(f"INFO tokens useage: {self.usage}")
|
||||
print(f"INFO translation end...")
|
||||
pass
|
||||
|
||||
def on_event(
|
||||
self,
|
||||
@@ -32,10 +33,10 @@ class Callback(TranslationRecognizerCallback):
|
||||
) -> None:
|
||||
caption = {}
|
||||
if transcription_result is not None:
|
||||
caption['id'] = transcription_result.sentence_id
|
||||
caption['index'] = transcription_result.sentence_id
|
||||
caption['text'] = transcription_result.text
|
||||
if caption['id'] != self.cur_id:
|
||||
self.cur_id = caption['id']
|
||||
if caption['index'] != self.cur_id:
|
||||
self.cur_id = caption['index']
|
||||
cur_time = datetime.now().strftime('%H:%M:%S')
|
||||
caption['time_s'] = cur_time
|
||||
self.time_str = cur_time
|
||||
@@ -50,7 +51,20 @@ class Callback(TranslationRecognizerCallback):
|
||||
|
||||
if usage:
|
||||
self.usage += usage['duration']
|
||||
print(caption)
|
||||
|
||||
# print(caption)
|
||||
self.send_to_node(caption)
|
||||
|
||||
def send_to_node(self, data):
|
||||
"""
|
||||
将数据发送到 Node.js 进程
|
||||
"""
|
||||
try:
|
||||
json_data = json.dumps(data) + '\n'
|
||||
sys.stdout.write(json_data)
|
||||
sys.stdout.flush()
|
||||
except Exception as e:
|
||||
print(f"Error sending data to Node.js: {e}", file=sys.stderr)
|
||||
|
||||
class GummyTranslator:
|
||||
def __init__(self, rate, source, target):
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import asyncio
|
||||
from sysaudio.win import LoopbackStream, mergeStreamChannels
|
||||
from audio2text.gummy import GummyTranslator
|
||||
import sys
|
||||
|
||||
def main():
|
||||
sys.stdout.reconfigure(line_buffering=True)
|
||||
loopback = LoopbackStream()
|
||||
loopback.openStream()
|
||||
|
||||
gummy = GummyTranslator(loopback.RATE, "zh", "en")
|
||||
gummy.translator.start()
|
||||
|
||||
try:
|
||||
for _ in range(0, 400):
|
||||
if not loopback.stream: continue
|
||||
data = loopback.stream.read(loopback.CHUNK)
|
||||
data = mergeStreamChannels(data, loopback.CHANNELS)
|
||||
gummy.translator.send_audio_frame(data)
|
||||
finally:
|
||||
gummy.translator.stop()
|
||||
loopback.closeStream()
|
||||
for _ in range(0, 400):
|
||||
if not loopback.stream: continue
|
||||
data = loopback.stream.read(loopback.CHUNK)
|
||||
data = mergeStreamChannels(data, loopback.CHANNELS)
|
||||
gummy.translator.send_audio_frame(data)
|
||||
|
||||
gummy.translator.stop()
|
||||
loopback.closeStream()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
BIN
python-subprocess/requirements.txt
Normal file
BIN
python-subprocess/requirements.txt
Normal file
Binary file not shown.
@@ -49,22 +49,23 @@ export function sendStyles(window: BrowserWindow) {
|
||||
console.log('SNED caption.style.set')
|
||||
}
|
||||
|
||||
export let captionLog: CaptionItem[] = [
|
||||
{index: 1, time_s: "00:00:00", time_t: "00:00:00", text: "Long time no see.", translation: "好久不见"},
|
||||
{index: 2, time_s: "00:00:00", time_t: "00:00:00", text: "How have you been?", translation: "你最近怎么样?"},
|
||||
{index: 3, time_s: "00:00:00", time_t: "00:00:00", text: "I've missed you a lot.", translation: "我非常想念你。"},
|
||||
{index: 4, time_s: "00:00:00", time_t: "00:00:00", text: "It's good to see you again.", translation: "很高兴再次见到你。"},
|
||||
{index: 5, time_s: "00:00:00", time_t: "00:00:00", text: "What have you been up to?", translation: "你最近在忙什么?"},
|
||||
{index: 6, time_s: "00:00:00", time_t: "00:00:00", text: "Let's catch up over coffee.", translation: "我们去喝杯咖啡聊聊天吧。"},
|
||||
{index: 7, time_s: "00:00:00", time_t: "00:00:00", text: "You look great!", translation: "你看起来很棒!"},
|
||||
{index: 8, time_s: "00:00:00", time_t: "00:00:00", text: "I can't believe it's been so long.", translation: "真不敢相信已经这么久了。"},
|
||||
{index: 9, time_s: "00:00:00", time_t: "00:00:00", text: "We should do this more often.", translation: "我们应该多聚聚。"},
|
||||
{index: 10, time_s: "00:00:00", time_t: "00:00:00", text: "Thanks for coming to see me.", translation: "谢谢你来看我。"},
|
||||
{index: 11, time_s: "00:00:00", time_t: "00:00:00", text: "We show case the utility of Macformer when combined with molecular docking simulations and wet lab based experimental validation, by applying it to the prospective design of macrocyclic JAK2 inhibitors.", translation: "我们通过将其应用于大环JAK2抑制剂的前瞻性设计,展示了Macformer与分子对接模拟和湿实验验证相结合的实用性。"},
|
||||
{index: 12, time_s: "00:00:00", time_t: "00:00:00", text: "Macrocycles, typically defined as cyclic small molecules or peptides with ring structures consisting of 12 or more atoms, has emerged as promising chemical scaffolds in the field of new drug discovery1,2. The distinct physicochemical properties, including high molecular weight and abundant hydrogen bond donors3, render this structural class occupy a chemical space beyond Lipinski's rule of five4.", translation: "大环分子通常定义为具有由 12 个或更多原子组成的环状结构的环状小分子或肽,已成为新药发现领域中具有前景的化学骨架 [1,2]。其独特的理化性质(包括高分子量和丰富的氢键供体)[3],使这类结构占据了超越 Lipinski 五规则 [4] 的化学空间。"}
|
||||
]
|
||||
export let captionLog: CaptionItem[] = []
|
||||
|
||||
export function sendCaptionLog(window: BrowserWindow) {
|
||||
window.webContents.send('both.log.set', captionLog)
|
||||
console.log('SEND both.log.set')
|
||||
}
|
||||
|
||||
export function addCaptionLog(log: CaptionItem) {
|
||||
if(captionLog.length && captionLog[captionLog.length - 1].index === log.index) {
|
||||
captionLog.splice(captionLog.length - 1, 1)
|
||||
captionLog.push(log)
|
||||
}
|
||||
else {
|
||||
captionLog.push(log)
|
||||
}
|
||||
console.log('ADD caption')
|
||||
for(const window of BrowserWindow.getAllWindows()){
|
||||
sendCaptionLog(window)
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,9 @@ import { app, BrowserWindow } from 'electron'
|
||||
import { electronApp, optimizer } from '@electron-toolkit/utils'
|
||||
import { controlWindow } from './control'
|
||||
import { captionWindow } from './caption'
|
||||
import { WebSocketConnector } from './wsConnector'
|
||||
|
||||
const wsConnector = new WebSocketConnector()
|
||||
import { PythonProcess } from './pythonProcess'
|
||||
const pySubProcess = new PythonProcess()
|
||||
|
||||
app.whenReady().then(() => {
|
||||
electronApp.setAppUserModelId('com.himeditator.autocaption')
|
||||
@@ -18,7 +18,7 @@ app.whenReady().then(() => {
|
||||
|
||||
controlWindow.createWindow()
|
||||
|
||||
wsConnector.connect()
|
||||
pySubProcess.start()
|
||||
|
||||
app.on('activate', function () {
|
||||
if (BrowserWindow.getAllWindows().length === 0){
|
||||
|
||||
42
src/main/pythonProcess.ts
Normal file
42
src/main/pythonProcess.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { spawn } from 'child_process'
|
||||
import { app } from 'electron'
|
||||
import path from 'path'
|
||||
import { addCaptionLog } from './data'
|
||||
|
||||
export class PythonProcess {
|
||||
public start() {
|
||||
const basePath = app.getAppPath()
|
||||
const pythonPath = path.join(
|
||||
basePath,
|
||||
'python-subprocess', 'subenv', 'Scripts', 'python.exe'
|
||||
)
|
||||
const targetPath = path.join(basePath, 'python-subprocess', 'main.py')
|
||||
|
||||
console.log(pythonPath)
|
||||
console.log(targetPath)
|
||||
|
||||
const pythonProcess = spawn(pythonPath, [targetPath])
|
||||
|
||||
pythonProcess.stdout.on('data', (data) => {
|
||||
const lines = data.toString().split('\n');
|
||||
lines.forEach( (line: string) => {
|
||||
if (line.trim()) {
|
||||
try {
|
||||
const caption = JSON.parse(line);
|
||||
addCaptionLog(caption);
|
||||
} catch (e) {
|
||||
console.error('Error parsing JSON:', e);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
pythonProcess.stderr.on('data', (data) => {
|
||||
console.error(`Python Error: ${data}`);
|
||||
});
|
||||
|
||||
pythonProcess.on('close', (code) => {
|
||||
console.log(`Python process exited with code ${code}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import WebSocket from 'ws';
|
||||
|
||||
export class WebSocketConnector {
|
||||
ws: WebSocket | null;
|
||||
|
||||
constructor() {
|
||||
this.ws = null;
|
||||
}
|
||||
|
||||
connect() {
|
||||
this.ws = new WebSocket('ws://localhost:8765');
|
||||
|
||||
this.ws.on('open', () => {
|
||||
console.log('INFO websocket server connected');
|
||||
this.send({ message: 'Electron Initialized' });
|
||||
});
|
||||
|
||||
this.ws.on('message', this.handleMessage);
|
||||
|
||||
this.ws.on('close', () => {
|
||||
console.log('INFO websocket connection closed');
|
||||
});
|
||||
|
||||
this.ws.on('error', (error) => {
|
||||
console.error('ERROR websocket error:', error);
|
||||
});
|
||||
}
|
||||
|
||||
handleMessage(data: any) {
|
||||
const message = JSON.parse(data.toString());
|
||||
console.log('INFO get message from webscoket:', message);
|
||||
}
|
||||
|
||||
send(data: object) {
|
||||
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
||||
this.ws.send(JSON.stringify(data));
|
||||
} else {
|
||||
console.error('ERROR send error: websocket not connected');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user