更新依赖,增强ASR功能,优化代理路由处理,添加登录页面渲染
This commit is contained in:
25
assistant/src/services/asr/asr-manager.ts
Normal file
25
assistant/src/services/asr/asr-manager.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
import { QwenAsrRelatime } from "@kevisual/video-tools/src/asr/index.ts";
|
||||
|
||||
export class AsrManger {
|
||||
map: Map<string, QwenAsrRelatime>;
|
||||
constructor() {
|
||||
this.map = new Map();
|
||||
}
|
||||
|
||||
getAsr(id: string) {
|
||||
const asr = this.map.get(id);
|
||||
return asr;
|
||||
}
|
||||
|
||||
setAsr(id: string, asr: QwenAsrRelatime) {
|
||||
this.map.set(id, asr);
|
||||
return asr;
|
||||
}
|
||||
|
||||
deleteAsr(id: string) {
|
||||
this.map.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
export const arrManager = new AsrManger();
|
||||
@@ -4,7 +4,7 @@ import { Listener, WebSocketListenerFun, WebSocketReq } from "@kevisual/router";
|
||||
import { lightHA } from "@/routes/ha-api/ha.ts";
|
||||
import { assistantConfig } from "@/app.ts";
|
||||
|
||||
const func: WebSocketListenerFun = async (req: WebSocketReq<{ asr: QwenAsrRelatime, msgId: string, startTime?: number }>, res) => {
|
||||
const func: WebSocketListenerFun = async (req: WebSocketReq<{ asr: QwenAsrRelatime, msgId: string, startTime?: number, loading?: boolean }>, res) => {
|
||||
const { ws, emitter, id, data } = req;
|
||||
let asr = ws.data.asr;
|
||||
|
||||
@@ -14,6 +14,9 @@ const func: WebSocketListenerFun = async (req: WebSocketReq<{ asr: QwenAsrRelati
|
||||
return;
|
||||
}
|
||||
if (!asr) {
|
||||
if (ws.data.loading) return;
|
||||
ws.data.loading = true;
|
||||
|
||||
const confg = assistantConfig.getConfig();
|
||||
const asrConfig = confg?.asr;
|
||||
if (!asrConfig?.enabled) {
|
||||
@@ -27,77 +30,84 @@ const func: WebSocketListenerFun = async (req: WebSocketReq<{ asr: QwenAsrRelati
|
||||
ws.close();
|
||||
return;
|
||||
}
|
||||
const onConnect = () => {
|
||||
const asr = ws.data.asr as QwenAsrRelatime;
|
||||
ws.send(JSON.stringify({ type: 'asr', code: 200, message: 'asr服务已连接', time: Date.now() }));
|
||||
if (!asr) return;
|
||||
asr.emitter.on('message', (message) => {
|
||||
// console.log('ASR message', message);
|
||||
});
|
||||
asr.emitter.on('partial', (message) => {
|
||||
// console.log('ASR message', message);
|
||||
let msgId = ws.data.msgId || Math.random().toString(36).substring(2, 10);
|
||||
ws.data.msgId = msgId;
|
||||
ws.send(JSON.stringify({
|
||||
type: 'partial',
|
||||
msgId: msgId,
|
||||
time: Date.now(),
|
||||
text: message?.text,
|
||||
raw: message?.raw,
|
||||
}));
|
||||
});
|
||||
asr.emitter.on('result', async ({ text, raw }) => {
|
||||
let msgId = ws.data.msgId;
|
||||
ws.data.msgId = Math.random().toString(36).substring(2, 10);
|
||||
const endTime = Date.now();
|
||||
console.log('cost time', ws.data.startTime ? (endTime - ws.data.startTime) : 0);
|
||||
ws.send(JSON.stringify({
|
||||
type: 'result',
|
||||
msgId: msgId,
|
||||
time: Date.now(),
|
||||
text,
|
||||
}));
|
||||
if (!text) return;
|
||||
const command = text?.trim().slice(0, 20);
|
||||
type ParseCommand = {
|
||||
type?: '打开' | '关闭',
|
||||
appName?: string,
|
||||
command?: string,
|
||||
}
|
||||
let obj: ParseCommand = {};
|
||||
if (command.startsWith('打开')) {
|
||||
obj.appName = command.replace('打开', '').trim();
|
||||
obj.type = '打开';
|
||||
} else if (command.startsWith('关闭')) {
|
||||
obj.appName = command.replace('关闭', '').trim();
|
||||
obj.type = '关闭';
|
||||
}
|
||||
if (obj.type) {
|
||||
try {
|
||||
const search = await lightHA.searchLight(obj.appName || '');
|
||||
console.log('searchTime', Date.now() - endTime);
|
||||
if (search.id) {
|
||||
await lightHA.runService({ entity_id: search.id, service: obj.type === '打开' ? 'turn_on' : 'turn_off' });
|
||||
} else if (search.hasMore) {
|
||||
const [first] = search.result;
|
||||
await lightHA.runService({ entity_id: first.entity_id, service: obj.type === '打开' ? 'turn_on' : 'turn_off' });
|
||||
} else {
|
||||
console.log('未找到对应设备:', obj.appName);
|
||||
}
|
||||
console.log('解析到控制指令', obj);
|
||||
} catch (e) {
|
||||
console.error('控制失败', e);
|
||||
}
|
||||
}
|
||||
console.log('toogle light time', Date.now() - endTime);
|
||||
});
|
||||
asr.start();
|
||||
}
|
||||
// 第一次请求
|
||||
asr = new QwenAsrRelatime({
|
||||
token,
|
||||
onConnect: () => {
|
||||
const asr = ws.data.asr as QwenAsrRelatime;
|
||||
ws.send(JSON.stringify({ type: 'asr', code: 200, message: 'asr服务已连接', time: Date.now() }));
|
||||
if (!asr) return;
|
||||
asr.emitter.on('message', (message) => {
|
||||
// console.log('ASR message', message);
|
||||
});
|
||||
asr.emitter.on('partial', (message) => {
|
||||
// console.log('ASR message', message);
|
||||
let msgId = ws.data.msgId || Math.random().toString(36).substring(2, 10);
|
||||
ws.data.msgId = msgId;
|
||||
ws.send(JSON.stringify({
|
||||
type: 'partial',
|
||||
msgId: msgId,
|
||||
time: Date.now(),
|
||||
text: message?.text,
|
||||
raw: message?.raw,
|
||||
}));
|
||||
});
|
||||
asr.emitter.on('result', async ({ text, raw }) => {
|
||||
let msgId = ws.data.msgId;
|
||||
ws.data.msgId = Math.random().toString(36).substring(2, 10);
|
||||
const endTime = Date.now();
|
||||
console.log('cost time', ws.data.startTime ? (endTime - ws.data.startTime) : 0);
|
||||
ws.send(JSON.stringify({
|
||||
type: 'result',
|
||||
msgId: msgId,
|
||||
time: Date.now(),
|
||||
text,
|
||||
}));
|
||||
if (!text) return;
|
||||
const command = text?.trim().slice(0, 20);
|
||||
type ParseCommand = {
|
||||
type?: '打开' | '关闭',
|
||||
appName?: string,
|
||||
command?: string,
|
||||
}
|
||||
let obj: ParseCommand = {};
|
||||
if (command.startsWith('打开')) {
|
||||
obj.appName = command.replace('打开', '').trim();
|
||||
obj.type = '打开';
|
||||
} else if (command.startsWith('关闭')) {
|
||||
obj.appName = command.replace('关闭', '').trim();
|
||||
obj.type = '关闭';
|
||||
}
|
||||
if (obj.type) {
|
||||
try {
|
||||
const search = await lightHA.searchLight(obj.appName || '');
|
||||
console.log('searchTime', Date.now() - endTime);
|
||||
if (search.id) {
|
||||
await lightHA.runService({ entity_id: search.id, service: obj.type === '打开' ? 'turn_on' : 'turn_off' });
|
||||
} else if (search.hasMore) {
|
||||
const [first] = search.result;
|
||||
await lightHA.runService({ entity_id: first.entity_id, service: obj.type === '打开' ? 'turn_on' : 'turn_off' });
|
||||
} else {
|
||||
console.log('未找到对应设备:', obj.appName);
|
||||
}
|
||||
console.log('解析到控制指令', obj);
|
||||
} catch (e) {
|
||||
console.error('控制失败', e);
|
||||
}
|
||||
}
|
||||
console.log('toogle light time', Date.now() - endTime);
|
||||
});
|
||||
asr.start();
|
||||
}
|
||||
emitter,
|
||||
onConnect,
|
||||
})
|
||||
ws.data.asr = asr;
|
||||
ws.data.loading = false;
|
||||
emitter.on('close--' + id, () => {
|
||||
console.log('ASR websocket 关闭, 释放资源');
|
||||
asr?.close?.();
|
||||
});
|
||||
}
|
||||
const isConnected = await asr.checkConnected();
|
||||
if (!isConnected) return;
|
||||
|
||||
@@ -7,6 +7,7 @@ import { getToken } from '@/module/http-token.ts';
|
||||
import { getTokenUserCache } from '@/routes/index.ts';
|
||||
import type { WebSocketListenerFun } from "@kevisual/router";
|
||||
import WebSocket from 'ws';
|
||||
import { renderNoAuthAndLogin } from '@/module/assistant/html/login.ts';
|
||||
const localProxy = new LocalProxy({});
|
||||
localProxy.initFromAssistantConfig(assistantConfig);
|
||||
|
||||
@@ -161,7 +162,8 @@ export const proxyRoute = async (req: http.IncomingMessage, res: http.ServerResp
|
||||
}
|
||||
const filter = await authFilter(req, res);
|
||||
if (filter) {
|
||||
return res.end('Not Authorized Proxy');
|
||||
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
||||
return res.end(renderNoAuthAndLogin('Not Authorized Proxy'));
|
||||
}
|
||||
const localProxyProxyList = localProxy.getLocalProxyList();
|
||||
const localProxyProxy = localProxyProxyList.find((item) => pathname.startsWith(item.path));
|
||||
|
||||
Reference in New Issue
Block a user