Merge branch 'vad'
Browse files* vad:
[fix]: update web.
[fix]: whisper_full_with_state: input is too short - 990 ms < 1000 ms. consider padding the input audio with silence.
[fix]: to 1.5b
filter empty message
update zh-en prompt add limit
update code structures and some code
update prompts and words
- config/hotwords.json +16 -1
- config/hotwords.txt +3 -0
- config/keywords.txt +5 -1
- config/prompt.py +20 -12
- frontend/assets/{index-a619a94d.js → index-4906844f.js} +2 -2
- frontend/assets/{index-c1b225bf.css → index-54c9f9d3.css} +1 -1
- frontend/index.html +2 -2
- frontend/index2.html +0 -169
- main.py +1 -2
- transcribe/pipelines/pipe_vad.py +1 -1
- transcribe/{whisper_llm_serve.py → serve.py} +40 -79
- transcribe/translatepipes.py +5 -2
- transcribe/utils.py +29 -2
config/hotwords.json
CHANGED
|
@@ -5,8 +5,23 @@
|
|
| 5 |
"GO SAME": "GOSIM",
|
| 6 |
"go same": "GOSIM",
|
| 7 |
"GoSync": "GOSIM",
|
|
|
|
| 8 |
"CSN": "CSDN",
|
| 9 |
"CSDF": "CSDN",
|
| 10 |
"CSTN": "CSDN",
|
| 11 |
-
"OpenAZI": "Open AGI"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
}
|
|
|
|
| 5 |
"GO SAME": "GOSIM",
|
| 6 |
"go same": "GOSIM",
|
| 7 |
"GoSync": "GOSIM",
|
| 8 |
+
"Go Son": "GOSIM",
|
| 9 |
"CSN": "CSDN",
|
| 10 |
"CSDF": "CSDN",
|
| 11 |
"CSTN": "CSDN",
|
| 12 |
+
"OpenAZI": "Open AGI",
|
| 13 |
+
"CSdn": "CSDN",
|
| 14 |
+
"DeepSeq": "DeepSeek",
|
| 15 |
+
"deep sig": "DeepSeek",
|
| 16 |
+
"deep sick": "DeepSeek",
|
| 17 |
+
"chat GBT": "ChatGPT",
|
| 18 |
+
"Deep sike": "DeepSeek",
|
| 19 |
+
"Deep sink": "DeepSeek",
|
| 20 |
+
"它跟face": "huggingface",
|
| 21 |
+
"拉么index": "LlamaIndex",
|
| 22 |
+
"lama index": "LlamaIndex",
|
| 23 |
+
"rug": "RAG",
|
| 24 |
+
"defive": "Dify",
|
| 25 |
+
"define": "Dify",
|
| 26 |
+
"open minus":"open manus"
|
| 27 |
}
|
config/hotwords.txt
CHANGED
|
@@ -32,3 +32,6 @@ MoXIN
|
|
| 32 |
Function
|
| 33 |
Func
|
| 34 |
Lava
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
Function
|
| 33 |
Func
|
| 34 |
Lava
|
| 35 |
+
C++
|
| 36 |
+
阿里
|
| 37 |
+
pipeline
|
config/keywords.txt
CHANGED
|
@@ -1,4 +1,8 @@
|
|
| 1 |
OpenAGI
|
| 2 |
GOSIM
|
| 3 |
Rust
|
| 4 |
-
LLaMA Factory
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
OpenAGI
|
| 2 |
GOSIM
|
| 3 |
Rust
|
| 4 |
+
LLaMA Factory
|
| 5 |
+
OPENGL
|
| 6 |
+
GPU
|
| 7 |
+
Web3
|
| 8 |
+
DeepSeek
|
config/prompt.py
CHANGED
|
@@ -34,16 +34,24 @@ LLM_SYS_7B_PROMPT_EN = """
|
|
| 34 |
""".format(keywords_mapping_string=keywords_mapping_string)
|
| 35 |
|
| 36 |
LLM_SYS_7B_PROMPT_ZH = """
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
文本:"""
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
""".format(keywords_mapping_string=keywords_mapping_string)
|
| 35 |
|
| 36 |
LLM_SYS_7B_PROMPT_ZH = """
|
| 37 |
+
你是一位中英文翻译专家。请将以下中文文本翻译成英文,遵循以下要求:
|
| 38 |
+
|
| 39 |
+
翻译要求:
|
| 40 |
+
- 保留原文英文内容:以下内容请保持原始英文形式,不进行翻译或改写:
|
| 41 |
+
- 技术术语与专业词汇
|
| 42 |
+
- 产品名称、品牌名称
|
| 43 |
+
- 代码片段、函数名、变量名
|
| 44 |
+
- 专有名词、缩写、首字母缩略词(如 API、NLP、RAG 等)
|
| 45 |
+
- 翻译符合英文表达习惯,流畅自然,不生硬直译。
|
| 46 |
+
- 保持专业性与准确性,清晰传达原意。
|
| 47 |
+
- 如遇原文表达模糊或逻辑不清的情况,允许适度调整语序或措辞,以增强英文表述的清晰度和逻辑性。
|
| 48 |
+
|
| 49 |
+
注意:
|
| 50 |
+
若难以确定某个词汇是否需要翻译,请优先保留原始英文形式。
|
| 51 |
+
不需添加额外解释或注释,仅翻译正文内容。
|
| 52 |
+
特别注意,翻译的内容只能包含英文,不能包含其他的语言。
|
| 53 |
+
|
| 54 |
文本:"""
|
| 55 |
+
|
| 56 |
+
LLM_SYS_PROMPT_EN = LLM_SYS_7B_PROMPT_EN
|
| 57 |
+
LLM_SYS_PROMPT_ZH =LLM_SYS_7B_PROMPT_ZH
|
frontend/assets/{index-a619a94d.js → index-4906844f.js}
RENAMED
|
@@ -492,9 +492,9 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
|
|
| 492 |
* vue-router v4.4.3
|
| 493 |
* (c) 2024 Eduardo San Martin Morote
|
| 494 |
* @license MIT
|
| 495 |
-
*/const Xl=typeof document<"u";function xye(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}const Pt=Object.assign;function sv(e,t){const n={};for(const o in t){const r=t[o];n[o]=Xo(r)?r.map(e):e(r)}return n}const Us=()=>{},Xo=Array.isArray,w_=/#/g,wye=/&/g,Oye=/\//g,Pye=/=/g,Iye=/\?/g,O_=/\+/g,Tye=/%5B/g,Eye=/%5D/g,P_=/%5E/g,Mye=/%60/g,I_=/%7B/g,_ye=/%7C/g,T_=/%7D/g,Aye=/%20/g;function RS(e){return encodeURI(""+e).replace(_ye,"|").replace(Tye,"[").replace(Eye,"]")}function Rye(e){return RS(e).replace(I_,"{").replace(T_,"}").replace(P_,"^")}function i0(e){return RS(e).replace(O_,"%2B").replace(Aye,"+").replace(w_,"%23").replace(wye,"%26").replace(Mye,"`").replace(I_,"{").replace(T_,"}").replace(P_,"^")}function Dye(e){return i0(e).replace(Pye,"%3D")}function Bye(e){return RS(e).replace(w_,"%23").replace(Iye,"%3F")}function Nye(e){return e==null?"":Bye(e).replace(Oye,"%2F")}function Ec(e){try{return decodeURIComponent(""+e)}catch{}return""+e}const kye=/\/$/,Fye=e=>e.replace(kye,"");function cv(e,t,n="/"){let o,r={},i="",l="";const a=t.indexOf("#");let s=t.indexOf("?");return a<s&&a>=0&&(s=-1),s>-1&&(o=t.slice(0,s),i=t.slice(s+1,a>-1?a:t.length),r=e(i)),a>-1&&(o=o||t.slice(0,a),l=t.slice(a,t.length)),o=jye(o??t,n),{fullPath:o+(i&&"?")+i+l,path:o,query:r,hash:Ec(l)}}function Lye(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function D3(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function zye(e,t,n){const o=t.matched.length-1,r=n.matched.length-1;return o>-1&&o===r&&Da(t.matched[o],n.matched[r])&&E_(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function Da(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function E_(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!Hye(e[n],t[n]))return!1;return!0}function Hye(e,t){return Xo(e)?B3(e,t):Xo(t)?B3(t,e):e===t}function B3(e,t){return Xo(t)?e.length===t.length&&e.every((n,o)=>n===t[o]):e.length===1&&e[0]===t}function jye(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),o=e.split("/"),r=o[o.length-1];(r===".."||r===".")&&o.push("");let i=n.length-1,l,a;for(l=0;l<o.length;l++)if(a=o[l],a!==".")if(a==="..")i>1&&i--;else break;return n.slice(0,i).join("/")+"/"+o.slice(l).join("/")}const ti={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0};var Mc;(function(e){e.pop="pop",e.push="push"})(Mc||(Mc={}));var Gs;(function(e){e.back="back",e.forward="forward",e.unknown=""})(Gs||(Gs={}));function Wye(e){if(!e)if(Xl){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),Fye(e)}const Vye=/^[^#]+#/;function Kye(e,t){return e.replace(Vye,"#")+t}function Uye(e,t){const n=document.documentElement.getBoundingClientRect(),o=e.getBoundingClientRect();return{behavior:t.behavior,left:o.left-n.left-(t.left||0),top:o.top-n.top-(t.top||0)}}const wh=()=>({left:window.scrollX,top:window.scrollY});function Gye(e){let t;if("el"in e){const n=e.el,o=typeof n=="string"&&n.startsWith("#"),r=typeof n=="string"?o?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!r)return;t=Uye(r,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.scrollX,t.top!=null?t.top:window.scrollY)}function N3(e,t){return(history.state?history.state.position-t:-1)+e}const l0=new Map;function Xye(e,t){l0.set(e,t)}function Yye(e){const t=l0.get(e);return l0.delete(e),t}let qye=()=>location.protocol+"//"+location.host;function M_(e,t){const{pathname:n,search:o,hash:r}=t,i=e.indexOf("#");if(i>-1){let a=r.includes(e.slice(i))?e.slice(i).length:1,s=r.slice(a);return s[0]!=="/"&&(s="/"+s),D3(s,"")}return D3(n,e)+o+r}function Zye(e,t,n,o){let r=[],i=[],l=null;const a=({state:f})=>{const h=M_(e,location),v=n.value,g=t.value;let b=0;if(f){if(n.value=h,t.value=f,l&&l===v){l=null;return}b=g?f.position-g.position:0}else o(h);r.forEach(y=>{y(n.value,v,{delta:b,type:Mc.pop,direction:b?b>0?Gs.forward:Gs.back:Gs.unknown})})};function s(){l=n.value}function c(f){r.push(f);const h=()=>{const v=r.indexOf(f);v>-1&&r.splice(v,1)};return i.push(h),h}function u(){const{history:f}=window;f.state&&f.replaceState(Pt({},f.state,{scroll:wh()}),"")}function d(){for(const f of i)f();i=[],window.removeEventListener("popstate",a),window.removeEventListener("beforeunload",u)}return window.addEventListener("popstate",a),window.addEventListener("beforeunload",u,{passive:!0}),{pauseListeners:s,listen:c,destroy:d}}function k3(e,t,n,o=!1,r=!1){return{back:e,current:t,forward:n,replaced:o,position:window.history.length,scroll:r?wh():null}}function Qye(e){const{history:t,location:n}=window,o={value:M_(e,n)},r={value:t.state};r.value||i(o.value,{back:null,current:o.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function i(s,c,u){const d=e.indexOf("#"),f=d>-1?(n.host&&document.querySelector("base")?e:e.slice(d))+s:qye()+e+s;try{t[u?"replaceState":"pushState"](c,"",f),r.value=c}catch(h){console.error(h),n[u?"replace":"assign"](f)}}function l(s,c){const u=Pt({},t.state,k3(r.value.back,s,r.value.forward,!0),c,{position:r.value.position});i(s,u,!0),o.value=s}function a(s,c){const u=Pt({},r.value,t.state,{forward:s,scroll:wh()});i(u.current,u,!0);const d=Pt({},k3(o.value,s,null),{position:u.position+1},c);i(s,d,!1),o.value=s}return{location:o,state:r,push:a,replace:l}}function Jye(e){e=Wye(e);const t=Qye(e),n=Zye(e,t.state,t.location,t.replace);function o(i,l=!0){l||n.pauseListeners(),history.go(i)}const r=Pt({location:"",base:e,go:o,createHref:Kye.bind(null,e)},t,n);return Object.defineProperty(r,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(r,"state",{enumerable:!0,get:()=>t.state.value}),r}function e1e(e){return typeof e=="string"||e&&typeof e=="object"}function __(e){return typeof e=="string"||typeof e=="symbol"}const A_=Symbol("");var F3;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(F3||(F3={}));function Ba(e,t){return Pt(new Error,{type:e,[A_]:!0},t)}function Sr(e,t){return e instanceof Error&&A_ in e&&(t==null||!!(e.type&t))}const L3="[^/]+?",t1e={sensitive:!1,strict:!1,start:!0,end:!0},n1e=/[.+*?^${}()[\]/\\]/g;function o1e(e,t){const n=Pt({},t1e,t),o=[];let r=n.start?"^":"";const i=[];for(const c of e){const u=c.length?[]:[90];n.strict&&!c.length&&(r+="/");for(let d=0;d<c.length;d++){const f=c[d];let h=40+(n.sensitive?.25:0);if(f.type===0)d||(r+="/"),r+=f.value.replace(n1e,"\\$&"),h+=40;else if(f.type===1){const{value:v,repeatable:g,optional:b,regexp:y}=f;i.push({name:v,repeatable:g,optional:b});const S=y||L3;if(S!==L3){h+=10;try{new RegExp(`(${S})`)}catch(x){throw new Error(`Invalid custom RegExp for param "${v}" (${S}): `+x.message)}}let $=g?`((?:${S})(?:/(?:${S}))*)`:`(${S})`;d||($=b&&c.length<2?`(?:/${$})`:"/"+$),b&&($+="?"),r+=$,h+=20,b&&(h+=-8),g&&(h+=-20),S===".*"&&(h+=-50)}u.push(h)}o.push(u)}if(n.strict&&n.end){const c=o.length-1;o[c][o[c].length-1]+=.7000000000000001}n.strict||(r+="/?"),n.end?r+="$":n.strict&&(r+="(?:/|$)");const l=new RegExp(r,n.sensitive?"":"i");function a(c){const u=c.match(l),d={};if(!u)return null;for(let f=1;f<u.length;f++){const h=u[f]||"",v=i[f-1];d[v.name]=h&&v.repeatable?h.split("/"):h}return d}function s(c){let u="",d=!1;for(const f of e){(!d||!u.endsWith("/"))&&(u+="/"),d=!1;for(const h of f)if(h.type===0)u+=h.value;else if(h.type===1){const{value:v,repeatable:g,optional:b}=h,y=v in c?c[v]:"";if(Xo(y)&&!g)throw new Error(`Provided param "${v}" is an array but it is not repeatable (* or + modifiers)`);const S=Xo(y)?y.join("/"):y;if(!S)if(b)f.length<2&&(u.endsWith("/")?u=u.slice(0,-1):d=!0);else throw new Error(`Missing required param "${v}"`);u+=S}}return u||"/"}return{re:l,score:o,keys:i,parse:a,stringify:s}}function r1e(e,t){let n=0;for(;n<e.length&&n<t.length;){const o=t[n]-e[n];if(o)return o;n++}return e.length<t.length?e.length===1&&e[0]===40+40?-1:1:e.length>t.length?t.length===1&&t[0]===40+40?1:-1:0}function R_(e,t){let n=0;const o=e.score,r=t.score;for(;n<o.length&&n<r.length;){const i=r1e(o[n],r[n]);if(i)return i;n++}if(Math.abs(r.length-o.length)===1){if(z3(o))return 1;if(z3(r))return-1}return r.length-o.length}function z3(e){const t=e[e.length-1];return e.length>0&&t[t.length-1]<0}const i1e={type:0,value:""},l1e=/[a-zA-Z0-9_]/;function a1e(e){if(!e)return[[]];if(e==="/")return[[i1e]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(h){throw new Error(`ERR (${n})/"${c}": ${h}`)}let n=0,o=n;const r=[];let i;function l(){i&&r.push(i),i=[]}let a=0,s,c="",u="";function d(){c&&(n===0?i.push({type:0,value:c}):n===1||n===2||n===3?(i.length>1&&(s==="*"||s==="+")&&t(`A repeatable param (${c}) must be alone in its segment. eg: '/:ids+.`),i.push({type:1,value:c,regexp:u,repeatable:s==="*"||s==="+",optional:s==="*"||s==="?"})):t("Invalid state to consume buffer"),c="")}function f(){c+=s}for(;a<e.length;){if(s=e[a++],s==="\\"&&n!==2){o=n,n=4;continue}switch(n){case 0:s==="/"?(c&&d(),l()):s===":"?(d(),n=1):f();break;case 4:f(),n=o;break;case 1:s==="("?n=2:l1e.test(s)?f():(d(),n=0,s!=="*"&&s!=="?"&&s!=="+"&&a--);break;case 2:s===")"?u[u.length-1]=="\\"?u=u.slice(0,-1)+s:n=3:u+=s;break;case 3:d(),n=0,s!=="*"&&s!=="?"&&s!=="+"&&a--,u="";break;default:t("Unknown state");break}}return n===2&&t(`Unfinished custom RegExp for param "${c}"`),d(),l(),r}function s1e(e,t,n){const o=o1e(a1e(e.path),n),r=Pt(o,{record:e,parent:t,children:[],alias:[]});return t&&!r.record.aliasOf==!t.record.aliasOf&&t.children.push(r),r}function c1e(e,t){const n=[],o=new Map;t=W3({strict:!1,end:!0,sensitive:!1},t);function r(d){return o.get(d)}function i(d,f,h){const v=!h,g=u1e(d);g.aliasOf=h&&h.record;const b=W3(t,d),y=[g];if("alias"in d){const x=typeof d.alias=="string"?[d.alias]:d.alias;for(const C of x)y.push(Pt({},g,{components:h?h.record.components:g.components,path:C,aliasOf:h?h.record:g}))}let S,$;for(const x of y){const{path:C}=x;if(f&&C[0]!=="/"){const O=f.record.path,w=O[O.length-1]==="/"?"":"/";x.path=f.record.path+(C&&w+C)}if(S=s1e(x,f,b),h?h.alias.push(S):($=$||S,$!==S&&$.alias.push(S),v&&d.name&&!j3(S)&&l(d.name)),D_(S)&&s(S),g.children){const O=g.children;for(let w=0;w<O.length;w++)i(O[w],S,h&&h.children[w])}h=h||S}return $?()=>{l($)}:Us}function l(d){if(__(d)){const f=o.get(d);f&&(o.delete(d),n.splice(n.indexOf(f),1),f.children.forEach(l),f.alias.forEach(l))}else{const f=n.indexOf(d);f>-1&&(n.splice(f,1),d.record.name&&o.delete(d.record.name),d.children.forEach(l),d.alias.forEach(l))}}function a(){return n}function s(d){const f=p1e(d,n);n.splice(f,0,d),d.record.name&&!j3(d)&&o.set(d.record.name,d)}function c(d,f){let h,v={},g,b;if("name"in d&&d.name){if(h=o.get(d.name),!h)throw Ba(1,{location:d});b=h.record.name,v=Pt(H3(f.params,h.keys.filter($=>!$.optional).concat(h.parent?h.parent.keys.filter($=>$.optional):[]).map($=>$.name)),d.params&&H3(d.params,h.keys.map($=>$.name))),g=h.stringify(v)}else if(d.path!=null)g=d.path,h=n.find($=>$.re.test(g)),h&&(v=h.parse(g),b=h.record.name);else{if(h=f.name?o.get(f.name):n.find($=>$.re.test(f.path)),!h)throw Ba(1,{location:d,currentLocation:f});b=h.record.name,v=Pt({},f.params,d.params),g=h.stringify(v)}const y=[];let S=h;for(;S;)y.unshift(S.record),S=S.parent;return{name:b,path:g,params:v,matched:y,meta:f1e(y)}}e.forEach(d=>i(d));function u(){n.length=0,o.clear()}return{addRoute:i,resolve:c,removeRoute:l,clearRoutes:u,getRoutes:a,getRecordMatcher:r}}function H3(e,t){const n={};for(const o of t)o in e&&(n[o]=e[o]);return n}function u1e(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:void 0,beforeEnter:e.beforeEnter,props:d1e(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}}}function d1e(e){const t={},n=e.props||!1;if("component"in e)t.default=n;else for(const o in e.components)t[o]=typeof n=="object"?n[o]:n;return t}function j3(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function f1e(e){return e.reduce((t,n)=>Pt(t,n.meta),{})}function W3(e,t){const n={};for(const o in e)n[o]=o in t?t[o]:e[o];return n}function p1e(e,t){let n=0,o=t.length;for(;n!==o;){const i=n+o>>1;R_(e,t[i])<0?o=i:n=i+1}const r=h1e(e);return r&&(o=t.lastIndexOf(r,o-1)),o}function h1e(e){let t=e;for(;t=t.parent;)if(D_(t)&&R_(e,t)===0)return t}function D_({record:e}){return!!(e.name||e.components&&Object.keys(e.components).length||e.redirect)}function g1e(e){const t={};if(e===""||e==="?")return t;const o=(e[0]==="?"?e.slice(1):e).split("&");for(let r=0;r<o.length;++r){const i=o[r].replace(O_," "),l=i.indexOf("="),a=Ec(l<0?i:i.slice(0,l)),s=l<0?null:Ec(i.slice(l+1));if(a in t){let c=t[a];Xo(c)||(c=t[a]=[c]),c.push(s)}else t[a]=s}return t}function V3(e){let t="";for(let n in e){const o=e[n];if(n=Dye(n),o==null){o!==void 0&&(t+=(t.length?"&":"")+n);continue}(Xo(o)?o.map(i=>i&&i0(i)):[o&&i0(o)]).forEach(i=>{i!==void 0&&(t+=(t.length?"&":"")+n,i!=null&&(t+="="+i))})}return t}function v1e(e){const t={};for(const n in e){const o=e[n];o!==void 0&&(t[n]=Xo(o)?o.map(r=>r==null?null:""+r):o==null?o:""+o)}return t}const m1e=Symbol(""),K3=Symbol(""),DS=Symbol(""),B_=Symbol(""),a0=Symbol("");function ps(){let e=[];function t(o){return e.push(o),()=>{const r=e.indexOf(o);r>-1&&e.splice(r,1)}}function n(){e=[]}return{add:t,list:()=>e.slice(),reset:n}}function ai(e,t,n,o,r,i=l=>l()){const l=o&&(o.enterCallbacks[r]=o.enterCallbacks[r]||[]);return()=>new Promise((a,s)=>{const c=f=>{f===!1?s(Ba(4,{from:n,to:t})):f instanceof Error?s(f):e1e(f)?s(Ba(2,{from:t,to:f})):(l&&o.enterCallbacks[r]===l&&typeof f=="function"&&l.push(f),a())},u=i(()=>e.call(o&&o.instances[r],t,n,c));let d=Promise.resolve(u);e.length<3&&(d=d.then(c)),d.catch(f=>s(f))})}function uv(e,t,n,o,r=i=>i()){const i=[];for(const l of e)for(const a in l.components){let s=l.components[a];if(!(t!=="beforeRouteEnter"&&!l.instances[a]))if(b1e(s)){const u=(s.__vccOpts||s)[t];u&&i.push(ai(u,n,o,l,a,r))}else{let c=s();i.push(()=>c.then(u=>{if(!u)return Promise.reject(new Error(`Couldn't resolve component "${a}" at "${l.path}"`));const d=xye(u)?u.default:u;l.components[a]=d;const h=(d.__vccOpts||d)[t];return h&&ai(h,n,o,l,a,r)()}))}}return i}function b1e(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function U3(e){const t=We(DS),n=We(B_),o=I(()=>{const s=Et(e.to);return t.resolve(s)}),r=I(()=>{const{matched:s}=o.value,{length:c}=s,u=s[c-1],d=n.matched;if(!u||!d.length)return-1;const f=d.findIndex(Da.bind(null,u));if(f>-1)return f;const h=G3(s[c-2]);return c>1&&G3(u)===h&&d[d.length-1].path!==h?d.findIndex(Da.bind(null,s[c-2])):f}),i=I(()=>r.value>-1&&C1e(n.params,o.value.params)),l=I(()=>r.value>-1&&r.value===n.matched.length-1&&E_(n.params,o.value.params));function a(s={}){return $1e(s)?t[Et(e.replace)?"replace":"push"](Et(e.to)).catch(Us):Promise.resolve()}return{route:o,href:I(()=>o.value.href),isActive:i,isExactActive:l,navigate:a}}const y1e=re({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:U3,setup(e,{slots:t}){const n=ct(U3(e)),{options:o}=We(DS),r=I(()=>({[X3(e.activeClass,o.linkActiveClass,"router-link-active")]:n.isActive,[X3(e.exactActiveClass,o.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const i=t.default&&t.default(n);return e.custom?i:Sa("a",{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:r.value},i)}}}),S1e=y1e;function $1e(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function C1e(e,t){for(const n in t){const o=t[n],r=e[n];if(typeof o=="string"){if(o!==r)return!1}else if(!Xo(r)||r.length!==o.length||o.some((i,l)=>i!==r[l]))return!1}return!0}function G3(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const X3=(e,t,n)=>e??t??n,x1e=re({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){const o=We(a0),r=I(()=>e.route||o.value),i=We(K3,0),l=I(()=>{let c=Et(i);const{matched:u}=r.value;let d;for(;(d=u[c])&&!d.components;)c++;return c}),a=I(()=>r.value.matched[l.value]);Ge(K3,I(()=>l.value+1)),Ge(m1e,a),Ge(a0,r);const s=oe();return be(()=>[s.value,a.value,e.name],([c,u,d],[f,h,v])=>{u&&(u.instances[d]=c,h&&h!==u&&c&&c===f&&(u.leaveGuards.size||(u.leaveGuards=h.leaveGuards),u.updateGuards.size||(u.updateGuards=h.updateGuards))),c&&u&&(!h||!Da(u,h)||!f)&&(u.enterCallbacks[d]||[]).forEach(g=>g(c))},{flush:"post"}),()=>{const c=r.value,u=e.name,d=a.value,f=d&&d.components[u];if(!f)return Y3(n.default,{Component:f,route:c});const h=d.props[u],v=h?h===!0?c.params:typeof h=="function"?h(c):h:null,b=Sa(f,Pt({},v,t,{onVnodeUnmounted:y=>{y.component.isUnmounted&&(d.instances[u]=null)},ref:s}));return Y3(n.default,{Component:b,route:c})||b}}});function Y3(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const w1e=x1e;function O1e(e){const t=c1e(e.routes,e),n=e.parseQuery||g1e,o=e.stringifyQuery||V3,r=e.history,i=ps(),l=ps(),a=ps(),s=ne(ti);let c=ti;Xl&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const u=sv.bind(null,K=>""+K),d=sv.bind(null,Nye),f=sv.bind(null,Ec);function h(K,ee){let Z,U;return __(K)?(Z=t.getRecordMatcher(K),U=ee):U=K,t.addRoute(U,Z)}function v(K){const ee=t.getRecordMatcher(K);ee&&t.removeRoute(ee)}function g(){return t.getRoutes().map(K=>K.record)}function b(K){return!!t.getRecordMatcher(K)}function y(K,ee){if(ee=Pt({},ee||s.value),typeof K=="string"){const te=cv(n,K,ee.path),se=t.resolve({path:te.path},ee),ie=r.createHref(te.fullPath);return Pt(te,se,{params:f(se.params),hash:Ec(te.hash),redirectedFrom:void 0,href:ie})}let Z;if(K.path!=null)Z=Pt({},K,{path:cv(n,K.path,ee.path).path});else{const te=Pt({},K.params);for(const se in te)te[se]==null&&delete te[se];Z=Pt({},K,{params:d(te)}),ee.params=d(ee.params)}const U=t.resolve(Z,ee),Q=K.hash||"";U.params=u(f(U.params));const W=Lye(o,Pt({},K,{hash:Rye(Q),path:U.path})),G=r.createHref(W);return Pt({fullPath:W,hash:Q,query:o===V3?v1e(K.query):K.query||{}},U,{redirectedFrom:void 0,href:G})}function S(K){return typeof K=="string"?cv(n,K,s.value.path):Pt({},K)}function $(K,ee){if(c!==K)return Ba(8,{from:ee,to:K})}function x(K){return w(K)}function C(K){return x(Pt(S(K),{replace:!0}))}function O(K){const ee=K.matched[K.matched.length-1];if(ee&&ee.redirect){const{redirect:Z}=ee;let U=typeof Z=="function"?Z(K):Z;return typeof U=="string"&&(U=U.includes("?")||U.includes("#")?U=S(U):{path:U},U.params={}),Pt({query:K.query,hash:K.hash,params:U.path!=null?{}:K.params},U)}}function w(K,ee){const Z=c=y(K),U=s.value,Q=K.state,W=K.force,G=K.replace===!0,te=O(Z);if(te)return w(Pt(S(te),{state:typeof te=="object"?Pt({},Q,te.state):Q,force:W,replace:G}),ee||Z);const se=Z;se.redirectedFrom=ee;let ie;return!W&&zye(o,U,Z)&&(ie=Ba(16,{to:se,from:U}),z(U,U,!0,!1)),(ie?Promise.resolve(ie):M(se,U)).catch(de=>Sr(de)?Sr(de,2)?de:L(de):k(de,se,U)).then(de=>{if(de){if(Sr(de,2))return w(Pt({replace:G},S(de.to),{state:typeof de.to=="object"?Pt({},Q,de.to.state):Q,force:W}),ee||se)}else de=A(se,U,!0,G,Q);return E(se,U,de),de})}function P(K,ee){const Z=$(K,ee);return Z?Promise.reject(Z):Promise.resolve()}function T(K){const ee=Y.values().next().value;return ee&&typeof ee.runWithContext=="function"?ee.runWithContext(K):K()}function M(K,ee){let Z;const[U,Q,W]=P1e(K,ee);Z=uv(U.reverse(),"beforeRouteLeave",K,ee);for(const te of U)te.leaveGuards.forEach(se=>{Z.push(ai(se,K,ee))});const G=P.bind(null,K,ee);return Z.push(G),J(Z).then(()=>{Z=[];for(const te of i.list())Z.push(ai(te,K,ee));return Z.push(G),J(Z)}).then(()=>{Z=uv(Q,"beforeRouteUpdate",K,ee);for(const te of Q)te.updateGuards.forEach(se=>{Z.push(ai(se,K,ee))});return Z.push(G),J(Z)}).then(()=>{Z=[];for(const te of W)if(te.beforeEnter)if(Xo(te.beforeEnter))for(const se of te.beforeEnter)Z.push(ai(se,K,ee));else Z.push(ai(te.beforeEnter,K,ee));return Z.push(G),J(Z)}).then(()=>(K.matched.forEach(te=>te.enterCallbacks={}),Z=uv(W,"beforeRouteEnter",K,ee,T),Z.push(G),J(Z))).then(()=>{Z=[];for(const te of l.list())Z.push(ai(te,K,ee));return Z.push(G),J(Z)}).catch(te=>Sr(te,8)?te:Promise.reject(te))}function E(K,ee,Z){a.list().forEach(U=>T(()=>U(K,ee,Z)))}function A(K,ee,Z,U,Q){const W=$(K,ee);if(W)return W;const G=ee===ti,te=Xl?history.state:{};Z&&(U||G?r.replace(K.fullPath,Pt({scroll:G&&te&&te.scroll},Q)):r.push(K.fullPath,Q)),s.value=K,z(K,ee,Z,G),L()}let R;function H(){R||(R=r.listen((K,ee,Z)=>{if(!X.listening)return;const U=y(K),Q=O(U);if(Q){w(Pt(Q,{replace:!0}),U).catch(Us);return}c=U;const W=s.value;Xl&&Xye(N3(W.fullPath,Z.delta),wh()),M(U,W).catch(G=>Sr(G,12)?G:Sr(G,2)?(w(G.to,U).then(te=>{Sr(te,20)&&!Z.delta&&Z.type===Mc.pop&&r.go(-1,!1)}).catch(Us),Promise.reject()):(Z.delta&&r.go(-Z.delta,!1),k(G,U,W))).then(G=>{G=G||A(U,W,!1),G&&(Z.delta&&!Sr(G,8)?r.go(-Z.delta,!1):Z.type===Mc.pop&&Sr(G,20)&&r.go(-1,!1)),E(U,W,G)}).catch(Us)}))}let _=ps(),B=ps(),N;function k(K,ee,Z){L(K);const U=B.list();return U.length?U.forEach(Q=>Q(K,ee,Z)):console.error(K),Promise.reject(K)}function F(){return N&&s.value!==ti?Promise.resolve():new Promise((K,ee)=>{_.add([K,ee])})}function L(K){return N||(N=!K,H(),_.list().forEach(([ee,Z])=>K?Z(K):ee()),_.reset()),K}function z(K,ee,Z,U){const{scrollBehavior:Q}=e;if(!Xl||!Q)return Promise.resolve();const W=!Z&&Yye(N3(K.fullPath,0))||(U||!Z)&&history.state&&history.state.scroll||null;return ot().then(()=>Q(K,ee,W)).then(G=>G&&Gye(G)).catch(G=>k(G,K,ee))}const j=K=>r.go(K);let q;const Y=new Set,X={currentRoute:s,listening:!0,addRoute:h,removeRoute:v,clearRoutes:t.clearRoutes,hasRoute:b,getRoutes:g,resolve:y,options:e,push:x,replace:C,go:j,back:()=>j(-1),forward:()=>j(1),beforeEach:i.add,beforeResolve:l.add,afterEach:a.add,onError:B.add,isReady:F,install(K){const ee=this;K.component("RouterLink",S1e),K.component("RouterView",w1e),K.config.globalProperties.$router=ee,Object.defineProperty(K.config.globalProperties,"$route",{enumerable:!0,get:()=>Et(s)}),Xl&&!q&&s.value===ti&&(q=!0,x(r.location).catch(Q=>{}));const Z={};for(const Q in ti)Object.defineProperty(Z,Q,{get:()=>s.value[Q],enumerable:!0});K.provide(DS,ee),K.provide(B_,gO(Z)),K.provide(a0,s);const U=K.unmount;Y.add(K),K.unmount=function(){Y.delete(K),Y.size<1&&(c=ti,R&&R(),R=null,s.value=ti,q=!1,N=!1),U()}}};function J(K){return K.reduce((ee,Z)=>ee.then(()=>T(Z)),Promise.resolve())}return X}function P1e(e,t){const n=[],o=[],r=[],i=Math.max(t.matched.length,e.matched.length);for(let l=0;l<i;l++){const a=t.matched[l];a&&(e.matched.find(c=>Da(c,a))?o.push(a):n.push(a));const s=e.matched[l];s&&(t.matched.find(c=>Da(c,s))||r.push(s))}return[n,o,r]}const I1e={class:"not-found-wrapper"},T1e=re({__name:"index",setup(e){const t=()=>{NS.replace("/")};return(n,o)=>{const r=xo("a-button"),i=xo("a-result");return di(),Yl("div",I1e,[p(i,{status:"404",title:"404","sub-title":"Sorry, the page you visited does not exist."},{extra:vn(()=>[p(r,{onClick:t,type:"primary"},{default:vn(()=>[Lt("Back Home")]),_:1})]),_:1})])}}}),E1e=xh(T1e,[["__scopeId","data-v-aef52a59"]]),BS=e=>(X7("data-v-1bbbedd4"),e=e(),Y7(),e),M1e={class:"view-wrapper"},_1e={style:{"margin-top":"10vh",padding:"32px"}},A1e={class:"chat-box-placeholder"},R1e=["data-seg-id"],D1e={class:"actions-box"},B1e={class:"left-actions"},N1e={class:"config-content"},k1e={class:"config-block"},F1e=BS(()=>tn("h4",{style:{"font-weight":"500"}},"Page Max Width:",-1)),L1e={class:"config-block"},z1e=BS(()=>tn("h4",{style:{"font-weight":"500"}},"Show Realtime Buffer:",-1)),H1e={class:"config-block"},j1e=BS(()=>tn("h4",{style:{"font-weight":"500"}},"Text Font Size:",-1)),W1e={style:{display:"flex","justify-content":"end"}},V1e="Please check if the microphone is available before the experience, specify the audio language and translation language, click the switch button to start recording, and you can get the recognized and translated text in real time.",K1e=re({__name:"index",setup(e){const t=Tc(),n=window.location.host;let o="ws";n.startsWith("127.0.0.1")||n.startsWith("localhost")?o="ws":o="wss";const r=`${o}://`+n+"/ws?";console.warn("ws_url: ",r);const i=oe(null),l=async Y=>{console.warn("start websocket ..."),i.value&&i.value.readyState!==WebSocket.CLOSED&&i.value.close();const X=`${r}${Y}`;i.value=new WebSocket(X),i.value.binaryType="arraybuffer",console.warn("created web socket ..."),i.value.addEventListener("open",()=>{console.log("WebSocket 连接成功"),v(),h.value=!0}),i.value.addEventListener("close",()=>{console.log("WebSocket 连接已关闭")}),i.value.onclose=J=>{console.log("code:",J.code,"reason:",J.reason,"wasClean:",J.wasClean),console.log("WebSocket 连接已关闭:",J)},i.value.addEventListener("error",J=>{console.error("WebSocket 连接错误:",J)}),i.value.addEventListener("message",J=>{const K=JSON.parse(J.data);console.log("WebSocket 收到消息:",K),K&&K.result&&E(K.result)})},a=async()=>{i.value&&(await i.value.close(),i.value=null)},s=oe(null),c=oe(null),u=oe(null),d=oe(null),f=oe(null),h=oe(!1),v=async()=>{try{const Y=await navigator.mediaDevices.getUserMedia({audio:{sampleRate:16e3,channelCount:1}});s.value=Y;const X=new AudioContext({sampleRate:16e3});u.value=X;const J=X.createMediaStreamSource(Y);d.value=J;const K=X.createScriptProcessor(4096,1,1);f.value=K,J.connect(K),K.connect(X.destination),K.onaudioprocess=ee=>{if(!h.value||!i.value||i.value.readyState!==WebSocket.OPEN)return;const Z=ee.inputBuffer.getChannelData(0),U=new Int16Array(Z.length);for(let Q=0;Q<Z.length;Q++)U[Q]=Math.max(-1,Math.min(1,Z[Q]))*32767;g(U)},h.value=!0,console.log("音频捕获已启动")}catch(Y){console.error("音频捕获失败:",Y)}},g=Y=>{if(i.value)i.value.send(Y),console.log("WebSocket send audio chunk success");else{console.error("WebSocket not initialized, sendAudioChunk failed");return}},b=async()=>{console.log("requirePermissionAction");try{if(!i.value||i.value.readyState!==WebSocket.OPEN){console.log("current lang_str : ",x.value);const Y=x.value;A(),await l(Y)}}catch(Y){console.log("Error accessing microphone: ",Y)}};je(()=>{console.log("[translator]: mounted"),z.value=t.$state.fs,L.value=t.$state.width_max,F.value=t.$state.vad}),xn(()=>{console.log("[HomePage]: unmounted"),i.value&&i.value.close(),c.value&&y()});const y=()=>{h.value=!1,a(),console.log("音频捕获已停止"),f.value&&(f.value.disconnect(),f.value=null),d.value&&(d.value.disconnect(),d.value=null),s.value&&(s.value.getTracks().forEach(Y=>Y.stop()),s.value=null),u.value&&(u.value.close(),u.value=null),console.log("录音已停止")},S=async Y=>{console.log(`selected ${Y}`),h.value=!1,await y(),console.log("new lang_str: ",Y),console.log("trans_lang : ",x.value)},$=Y=>{h.value=Y,Y?b():y()},x=oe("from=en&to=zh"),C=[{value:"from=en&to=zh",label:"English -> Chinese"},{value:"from=zh&to=en",label:"Chinese -> English"}],O=[],w=ct([]),P=oe(""),T=oe(""),M=oe(""),E=Y=>{if(console.log("updateViewData: ",Y),Y){const{context:X,from:J,to:K,seg_id:ee,partial:Z,tranContent:U}=Y;if(Z==!0){P.value=X,T.value=U,M.value=ee;return}else{const Q={context:X,from:J,to:K,seg_id:ee,partial:Z,tranContent:U};w.length>100&&w.splice(0,40),w.push(Q);const W=`[src]: ${X}
|
| 496 |
${"-".repeat(80)}
|
| 497 |
[dst]: ${U}
|
| 498 |
|
| 499 |
`;O.push(W),P.value="",T.value="",M.value=""}}_()},A=()=>{w.splice(0,w.length),O.splice(0,O.length)},R=async()=>{const Y=O.join(`
|
| 500 |
-
`),X=new Blob([Y],{type:"text/plain"}),J=URL.createObjectURL(X),K=document.createElement("a");K.href=J,K.download="record.txt",document.body.appendChild(K),K.click(),document.body.removeChild(K),URL.revokeObjectURL(J)},H=oe(null),_=()=>{ot(()=>{H.value&&(H.value.scrollTop=H.value.scrollHeight+144)})};be(()=>[...w],()=>{_()},{deep:!0}),be(()=>T.value,()=>{_()});const B=oe(!1),N=()=>{B.value=!0},k=()=>{B.value=!1,F.value!=t.$state.vad&&(t.$state.vad=F.value,c.value&&y())},F=oe(.3),L=oe(!1),z=oe("trans-font-size-18"),j=oe(!0),q=Y=>{console.log("onFontSizeChange",Y.target.value),z.value=Y.target.value,t.$state.fs=Y.target.value};return(Y,X)=>{const J=xo("a-radio"),K=xo("a-radio-group"),ee=xo("a-switch"),Z=xo("a-button"),U=xo("a-popover"),Q=xo("a-select"),W=xo("a-popconfirm"),G=xo("a-card");return di(),Yl("div",M1e,[tn("div",{class:Pr(["content-wrapper",L.value?"wrapper-width-auto":"wrapper-width-fixed"])},[tn("div",_1e,[p(G,{bordered:!1,style:{width:"100%","min-width":"100%"}},{actions:vn(()=>[tn("div",D1e,[tn("div",B1e,[p(U,{open:B.value,"onUpdate:open":X[4]||(X[4]=te=>B.value=te),placement:"topLeft",trigger:"click"},{content:vn(()=>[tn("div",N1e,[LA("",!0),tn("div",k1e,[F1e,p(ee,{checked:L.value,"onUpdate:checked":X[1]||(X[1]=te=>L.value=te)},null,8,["checked"])]),tn("div",L1e,[z1e,p(ee,{checked:j.value,"onUpdate:checked":X[2]||(X[2]=te=>j.value=te)},null,8,["checked"])]),tn("div",H1e,[j1e,p(K,{value:z.value,"onUpdate:value":X[3]||(X[3]=te=>z.value=te),onChange:q},{default:vn(()=>[p(J,{value:"trans-font-size-16"},{default:vn(()=>[Lt("Small")]),_:1}),p(J,{value:"trans-font-size-18"},{default:vn(()=>[Lt("Default")]),_:1}),p(J,{value:"trans-font-size-20"},{default:vn(()=>[Lt("Normal")]),_:1}),p(J,{value:"trans-font-size-22"},{default:vn(()=>[Lt("Medium")]),_:1}),p(J,{value:"trans-font-size-24"},{default:vn(()=>[Lt("Large")]),_:1})]),_:1},8,["value"])])]),tn("div",W1e,[p(Z,{type:"primary",onClick:k},{default:vn(()=>[Lt("Done")]),_:1})])]),default:vn(()=>[p(Z,{type:"dashed",shape:"circle",size:"middle",onClick:N},{icon:vn(()=>[p(Et(vme))]),_:1})]),_:1},8,["open"]),p(Q,{value:x.value,"onUpdate:value":X[5]||(X[5]=te=>x.value=te),style:{width:"240px"},placeholder:"Select Language",options:C,onChange:S},null,8,["value"]),p(W,{title:"save the current text content?",onConfirm:R,"ok-text":"Yes","cancel-text":"No"},{icon:vn(()=>[p(Et(sme),{style:{color:"red"}})]),default:vn(()=>[p(Z,{type:"dashed",shape:"circle",size:"middle"},{icon:vn(()=>[p(Et(MM))]),_:1})]),_:1})]),p(ee,{key:"switcher",size:"large",type:"danger","checked-children":"ON","un-checked-children":"OFF",checked:h.value,"onUpdate:checked":X[6]||(X[6]=te=>h.value=te),onChange:$},null,8,["checked"])])]),default:vn(()=>[fn(tn("div",A1e,ns(V1e),512),[[mn,!(w.length||P.value)]]),fn(tn("div",{class:"trans-list",ref_key:"transListRef",ref:H},[(di(!0),Yl(Ve,null,pA(w,te=>(di(),Yl("div",{key:te.context,class:Pr(["node"]),"data-seg-id":te.seg_id},[tn("div",{class:Pr(["trans-src-lang",z.value])},ns(te.context),3),tn("div",{class:Pr(["trans-dst-lang",z.value])},ns(te.tranContent),3)],8,R1e))),128)),fn((di(),Yl("div",{class:"node current_node",key:M.value},[tn("div",{class:Pr(["trans-src-lang",z.value])},ns(P.value),3),tn("div",{class:Pr(["trans-dst-lang",z.value])},ns(T.value),3)])),[[mn,j.value]])],512),[[mn,w.length||P.value]])]),_:1})])],2)])}}}),U1e=xh(K1e,[["__scopeId","data-v-
|
|
|
|
| 492 |
* vue-router v4.4.3
|
| 493 |
* (c) 2024 Eduardo San Martin Morote
|
| 494 |
* @license MIT
|
| 495 |
+
*/const Xl=typeof document<"u";function xye(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}const Pt=Object.assign;function sv(e,t){const n={};for(const o in t){const r=t[o];n[o]=Xo(r)?r.map(e):e(r)}return n}const Us=()=>{},Xo=Array.isArray,w_=/#/g,wye=/&/g,Oye=/\//g,Pye=/=/g,Iye=/\?/g,O_=/\+/g,Tye=/%5B/g,Eye=/%5D/g,P_=/%5E/g,Mye=/%60/g,I_=/%7B/g,_ye=/%7C/g,T_=/%7D/g,Aye=/%20/g;function RS(e){return encodeURI(""+e).replace(_ye,"|").replace(Tye,"[").replace(Eye,"]")}function Rye(e){return RS(e).replace(I_,"{").replace(T_,"}").replace(P_,"^")}function i0(e){return RS(e).replace(O_,"%2B").replace(Aye,"+").replace(w_,"%23").replace(wye,"%26").replace(Mye,"`").replace(I_,"{").replace(T_,"}").replace(P_,"^")}function Dye(e){return i0(e).replace(Pye,"%3D")}function Bye(e){return RS(e).replace(w_,"%23").replace(Iye,"%3F")}function Nye(e){return e==null?"":Bye(e).replace(Oye,"%2F")}function Ec(e){try{return decodeURIComponent(""+e)}catch{}return""+e}const kye=/\/$/,Fye=e=>e.replace(kye,"");function cv(e,t,n="/"){let o,r={},i="",l="";const a=t.indexOf("#");let s=t.indexOf("?");return a<s&&a>=0&&(s=-1),s>-1&&(o=t.slice(0,s),i=t.slice(s+1,a>-1?a:t.length),r=e(i)),a>-1&&(o=o||t.slice(0,a),l=t.slice(a,t.length)),o=jye(o??t,n),{fullPath:o+(i&&"?")+i+l,path:o,query:r,hash:Ec(l)}}function Lye(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function D3(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function zye(e,t,n){const o=t.matched.length-1,r=n.matched.length-1;return o>-1&&o===r&&Da(t.matched[o],n.matched[r])&&E_(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function Da(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function E_(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!Hye(e[n],t[n]))return!1;return!0}function Hye(e,t){return Xo(e)?B3(e,t):Xo(t)?B3(t,e):e===t}function B3(e,t){return Xo(t)?e.length===t.length&&e.every((n,o)=>n===t[o]):e.length===1&&e[0]===t}function jye(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),o=e.split("/"),r=o[o.length-1];(r===".."||r===".")&&o.push("");let i=n.length-1,l,a;for(l=0;l<o.length;l++)if(a=o[l],a!==".")if(a==="..")i>1&&i--;else break;return n.slice(0,i).join("/")+"/"+o.slice(l).join("/")}const ti={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0};var Mc;(function(e){e.pop="pop",e.push="push"})(Mc||(Mc={}));var Gs;(function(e){e.back="back",e.forward="forward",e.unknown=""})(Gs||(Gs={}));function Wye(e){if(!e)if(Xl){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),Fye(e)}const Vye=/^[^#]+#/;function Kye(e,t){return e.replace(Vye,"#")+t}function Uye(e,t){const n=document.documentElement.getBoundingClientRect(),o=e.getBoundingClientRect();return{behavior:t.behavior,left:o.left-n.left-(t.left||0),top:o.top-n.top-(t.top||0)}}const wh=()=>({left:window.scrollX,top:window.scrollY});function Gye(e){let t;if("el"in e){const n=e.el,o=typeof n=="string"&&n.startsWith("#"),r=typeof n=="string"?o?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!r)return;t=Uye(r,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.scrollX,t.top!=null?t.top:window.scrollY)}function N3(e,t){return(history.state?history.state.position-t:-1)+e}const l0=new Map;function Xye(e,t){l0.set(e,t)}function Yye(e){const t=l0.get(e);return l0.delete(e),t}let qye=()=>location.protocol+"//"+location.host;function M_(e,t){const{pathname:n,search:o,hash:r}=t,i=e.indexOf("#");if(i>-1){let a=r.includes(e.slice(i))?e.slice(i).length:1,s=r.slice(a);return s[0]!=="/"&&(s="/"+s),D3(s,"")}return D3(n,e)+o+r}function Zye(e,t,n,o){let r=[],i=[],l=null;const a=({state:f})=>{const h=M_(e,location),v=n.value,g=t.value;let b=0;if(f){if(n.value=h,t.value=f,l&&l===v){l=null;return}b=g?f.position-g.position:0}else o(h);r.forEach(y=>{y(n.value,v,{delta:b,type:Mc.pop,direction:b?b>0?Gs.forward:Gs.back:Gs.unknown})})};function s(){l=n.value}function c(f){r.push(f);const h=()=>{const v=r.indexOf(f);v>-1&&r.splice(v,1)};return i.push(h),h}function u(){const{history:f}=window;f.state&&f.replaceState(Pt({},f.state,{scroll:wh()}),"")}function d(){for(const f of i)f();i=[],window.removeEventListener("popstate",a),window.removeEventListener("beforeunload",u)}return window.addEventListener("popstate",a),window.addEventListener("beforeunload",u,{passive:!0}),{pauseListeners:s,listen:c,destroy:d}}function k3(e,t,n,o=!1,r=!1){return{back:e,current:t,forward:n,replaced:o,position:window.history.length,scroll:r?wh():null}}function Qye(e){const{history:t,location:n}=window,o={value:M_(e,n)},r={value:t.state};r.value||i(o.value,{back:null,current:o.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function i(s,c,u){const d=e.indexOf("#"),f=d>-1?(n.host&&document.querySelector("base")?e:e.slice(d))+s:qye()+e+s;try{t[u?"replaceState":"pushState"](c,"",f),r.value=c}catch(h){console.error(h),n[u?"replace":"assign"](f)}}function l(s,c){const u=Pt({},t.state,k3(r.value.back,s,r.value.forward,!0),c,{position:r.value.position});i(s,u,!0),o.value=s}function a(s,c){const u=Pt({},r.value,t.state,{forward:s,scroll:wh()});i(u.current,u,!0);const d=Pt({},k3(o.value,s,null),{position:u.position+1},c);i(s,d,!1),o.value=s}return{location:o,state:r,push:a,replace:l}}function Jye(e){e=Wye(e);const t=Qye(e),n=Zye(e,t.state,t.location,t.replace);function o(i,l=!0){l||n.pauseListeners(),history.go(i)}const r=Pt({location:"",base:e,go:o,createHref:Kye.bind(null,e)},t,n);return Object.defineProperty(r,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(r,"state",{enumerable:!0,get:()=>t.state.value}),r}function e1e(e){return typeof e=="string"||e&&typeof e=="object"}function __(e){return typeof e=="string"||typeof e=="symbol"}const A_=Symbol("");var F3;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(F3||(F3={}));function Ba(e,t){return Pt(new Error,{type:e,[A_]:!0},t)}function Sr(e,t){return e instanceof Error&&A_ in e&&(t==null||!!(e.type&t))}const L3="[^/]+?",t1e={sensitive:!1,strict:!1,start:!0,end:!0},n1e=/[.+*?^${}()[\]/\\]/g;function o1e(e,t){const n=Pt({},t1e,t),o=[];let r=n.start?"^":"";const i=[];for(const c of e){const u=c.length?[]:[90];n.strict&&!c.length&&(r+="/");for(let d=0;d<c.length;d++){const f=c[d];let h=40+(n.sensitive?.25:0);if(f.type===0)d||(r+="/"),r+=f.value.replace(n1e,"\\$&"),h+=40;else if(f.type===1){const{value:v,repeatable:g,optional:b,regexp:y}=f;i.push({name:v,repeatable:g,optional:b});const S=y||L3;if(S!==L3){h+=10;try{new RegExp(`(${S})`)}catch(x){throw new Error(`Invalid custom RegExp for param "${v}" (${S}): `+x.message)}}let $=g?`((?:${S})(?:/(?:${S}))*)`:`(${S})`;d||($=b&&c.length<2?`(?:/${$})`:"/"+$),b&&($+="?"),r+=$,h+=20,b&&(h+=-8),g&&(h+=-20),S===".*"&&(h+=-50)}u.push(h)}o.push(u)}if(n.strict&&n.end){const c=o.length-1;o[c][o[c].length-1]+=.7000000000000001}n.strict||(r+="/?"),n.end?r+="$":n.strict&&(r+="(?:/|$)");const l=new RegExp(r,n.sensitive?"":"i");function a(c){const u=c.match(l),d={};if(!u)return null;for(let f=1;f<u.length;f++){const h=u[f]||"",v=i[f-1];d[v.name]=h&&v.repeatable?h.split("/"):h}return d}function s(c){let u="",d=!1;for(const f of e){(!d||!u.endsWith("/"))&&(u+="/"),d=!1;for(const h of f)if(h.type===0)u+=h.value;else if(h.type===1){const{value:v,repeatable:g,optional:b}=h,y=v in c?c[v]:"";if(Xo(y)&&!g)throw new Error(`Provided param "${v}" is an array but it is not repeatable (* or + modifiers)`);const S=Xo(y)?y.join("/"):y;if(!S)if(b)f.length<2&&(u.endsWith("/")?u=u.slice(0,-1):d=!0);else throw new Error(`Missing required param "${v}"`);u+=S}}return u||"/"}return{re:l,score:o,keys:i,parse:a,stringify:s}}function r1e(e,t){let n=0;for(;n<e.length&&n<t.length;){const o=t[n]-e[n];if(o)return o;n++}return e.length<t.length?e.length===1&&e[0]===40+40?-1:1:e.length>t.length?t.length===1&&t[0]===40+40?1:-1:0}function R_(e,t){let n=0;const o=e.score,r=t.score;for(;n<o.length&&n<r.length;){const i=r1e(o[n],r[n]);if(i)return i;n++}if(Math.abs(r.length-o.length)===1){if(z3(o))return 1;if(z3(r))return-1}return r.length-o.length}function z3(e){const t=e[e.length-1];return e.length>0&&t[t.length-1]<0}const i1e={type:0,value:""},l1e=/[a-zA-Z0-9_]/;function a1e(e){if(!e)return[[]];if(e==="/")return[[i1e]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(h){throw new Error(`ERR (${n})/"${c}": ${h}`)}let n=0,o=n;const r=[];let i;function l(){i&&r.push(i),i=[]}let a=0,s,c="",u="";function d(){c&&(n===0?i.push({type:0,value:c}):n===1||n===2||n===3?(i.length>1&&(s==="*"||s==="+")&&t(`A repeatable param (${c}) must be alone in its segment. eg: '/:ids+.`),i.push({type:1,value:c,regexp:u,repeatable:s==="*"||s==="+",optional:s==="*"||s==="?"})):t("Invalid state to consume buffer"),c="")}function f(){c+=s}for(;a<e.length;){if(s=e[a++],s==="\\"&&n!==2){o=n,n=4;continue}switch(n){case 0:s==="/"?(c&&d(),l()):s===":"?(d(),n=1):f();break;case 4:f(),n=o;break;case 1:s==="("?n=2:l1e.test(s)?f():(d(),n=0,s!=="*"&&s!=="?"&&s!=="+"&&a--);break;case 2:s===")"?u[u.length-1]=="\\"?u=u.slice(0,-1)+s:n=3:u+=s;break;case 3:d(),n=0,s!=="*"&&s!=="?"&&s!=="+"&&a--,u="";break;default:t("Unknown state");break}}return n===2&&t(`Unfinished custom RegExp for param "${c}"`),d(),l(),r}function s1e(e,t,n){const o=o1e(a1e(e.path),n),r=Pt(o,{record:e,parent:t,children:[],alias:[]});return t&&!r.record.aliasOf==!t.record.aliasOf&&t.children.push(r),r}function c1e(e,t){const n=[],o=new Map;t=W3({strict:!1,end:!0,sensitive:!1},t);function r(d){return o.get(d)}function i(d,f,h){const v=!h,g=u1e(d);g.aliasOf=h&&h.record;const b=W3(t,d),y=[g];if("alias"in d){const x=typeof d.alias=="string"?[d.alias]:d.alias;for(const C of x)y.push(Pt({},g,{components:h?h.record.components:g.components,path:C,aliasOf:h?h.record:g}))}let S,$;for(const x of y){const{path:C}=x;if(f&&C[0]!=="/"){const O=f.record.path,w=O[O.length-1]==="/"?"":"/";x.path=f.record.path+(C&&w+C)}if(S=s1e(x,f,b),h?h.alias.push(S):($=$||S,$!==S&&$.alias.push(S),v&&d.name&&!j3(S)&&l(d.name)),D_(S)&&s(S),g.children){const O=g.children;for(let w=0;w<O.length;w++)i(O[w],S,h&&h.children[w])}h=h||S}return $?()=>{l($)}:Us}function l(d){if(__(d)){const f=o.get(d);f&&(o.delete(d),n.splice(n.indexOf(f),1),f.children.forEach(l),f.alias.forEach(l))}else{const f=n.indexOf(d);f>-1&&(n.splice(f,1),d.record.name&&o.delete(d.record.name),d.children.forEach(l),d.alias.forEach(l))}}function a(){return n}function s(d){const f=p1e(d,n);n.splice(f,0,d),d.record.name&&!j3(d)&&o.set(d.record.name,d)}function c(d,f){let h,v={},g,b;if("name"in d&&d.name){if(h=o.get(d.name),!h)throw Ba(1,{location:d});b=h.record.name,v=Pt(H3(f.params,h.keys.filter($=>!$.optional).concat(h.parent?h.parent.keys.filter($=>$.optional):[]).map($=>$.name)),d.params&&H3(d.params,h.keys.map($=>$.name))),g=h.stringify(v)}else if(d.path!=null)g=d.path,h=n.find($=>$.re.test(g)),h&&(v=h.parse(g),b=h.record.name);else{if(h=f.name?o.get(f.name):n.find($=>$.re.test(f.path)),!h)throw Ba(1,{location:d,currentLocation:f});b=h.record.name,v=Pt({},f.params,d.params),g=h.stringify(v)}const y=[];let S=h;for(;S;)y.unshift(S.record),S=S.parent;return{name:b,path:g,params:v,matched:y,meta:f1e(y)}}e.forEach(d=>i(d));function u(){n.length=0,o.clear()}return{addRoute:i,resolve:c,removeRoute:l,clearRoutes:u,getRoutes:a,getRecordMatcher:r}}function H3(e,t){const n={};for(const o of t)o in e&&(n[o]=e[o]);return n}function u1e(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:void 0,beforeEnter:e.beforeEnter,props:d1e(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}}}function d1e(e){const t={},n=e.props||!1;if("component"in e)t.default=n;else for(const o in e.components)t[o]=typeof n=="object"?n[o]:n;return t}function j3(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function f1e(e){return e.reduce((t,n)=>Pt(t,n.meta),{})}function W3(e,t){const n={};for(const o in e)n[o]=o in t?t[o]:e[o];return n}function p1e(e,t){let n=0,o=t.length;for(;n!==o;){const i=n+o>>1;R_(e,t[i])<0?o=i:n=i+1}const r=h1e(e);return r&&(o=t.lastIndexOf(r,o-1)),o}function h1e(e){let t=e;for(;t=t.parent;)if(D_(t)&&R_(e,t)===0)return t}function D_({record:e}){return!!(e.name||e.components&&Object.keys(e.components).length||e.redirect)}function g1e(e){const t={};if(e===""||e==="?")return t;const o=(e[0]==="?"?e.slice(1):e).split("&");for(let r=0;r<o.length;++r){const i=o[r].replace(O_," "),l=i.indexOf("="),a=Ec(l<0?i:i.slice(0,l)),s=l<0?null:Ec(i.slice(l+1));if(a in t){let c=t[a];Xo(c)||(c=t[a]=[c]),c.push(s)}else t[a]=s}return t}function V3(e){let t="";for(let n in e){const o=e[n];if(n=Dye(n),o==null){o!==void 0&&(t+=(t.length?"&":"")+n);continue}(Xo(o)?o.map(i=>i&&i0(i)):[o&&i0(o)]).forEach(i=>{i!==void 0&&(t+=(t.length?"&":"")+n,i!=null&&(t+="="+i))})}return t}function v1e(e){const t={};for(const n in e){const o=e[n];o!==void 0&&(t[n]=Xo(o)?o.map(r=>r==null?null:""+r):o==null?o:""+o)}return t}const m1e=Symbol(""),K3=Symbol(""),DS=Symbol(""),B_=Symbol(""),a0=Symbol("");function ps(){let e=[];function t(o){return e.push(o),()=>{const r=e.indexOf(o);r>-1&&e.splice(r,1)}}function n(){e=[]}return{add:t,list:()=>e.slice(),reset:n}}function ai(e,t,n,o,r,i=l=>l()){const l=o&&(o.enterCallbacks[r]=o.enterCallbacks[r]||[]);return()=>new Promise((a,s)=>{const c=f=>{f===!1?s(Ba(4,{from:n,to:t})):f instanceof Error?s(f):e1e(f)?s(Ba(2,{from:t,to:f})):(l&&o.enterCallbacks[r]===l&&typeof f=="function"&&l.push(f),a())},u=i(()=>e.call(o&&o.instances[r],t,n,c));let d=Promise.resolve(u);e.length<3&&(d=d.then(c)),d.catch(f=>s(f))})}function uv(e,t,n,o,r=i=>i()){const i=[];for(const l of e)for(const a in l.components){let s=l.components[a];if(!(t!=="beforeRouteEnter"&&!l.instances[a]))if(b1e(s)){const u=(s.__vccOpts||s)[t];u&&i.push(ai(u,n,o,l,a,r))}else{let c=s();i.push(()=>c.then(u=>{if(!u)return Promise.reject(new Error(`Couldn't resolve component "${a}" at "${l.path}"`));const d=xye(u)?u.default:u;l.components[a]=d;const h=(d.__vccOpts||d)[t];return h&&ai(h,n,o,l,a,r)()}))}}return i}function b1e(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function U3(e){const t=We(DS),n=We(B_),o=I(()=>{const s=Et(e.to);return t.resolve(s)}),r=I(()=>{const{matched:s}=o.value,{length:c}=s,u=s[c-1],d=n.matched;if(!u||!d.length)return-1;const f=d.findIndex(Da.bind(null,u));if(f>-1)return f;const h=G3(s[c-2]);return c>1&&G3(u)===h&&d[d.length-1].path!==h?d.findIndex(Da.bind(null,s[c-2])):f}),i=I(()=>r.value>-1&&C1e(n.params,o.value.params)),l=I(()=>r.value>-1&&r.value===n.matched.length-1&&E_(n.params,o.value.params));function a(s={}){return $1e(s)?t[Et(e.replace)?"replace":"push"](Et(e.to)).catch(Us):Promise.resolve()}return{route:o,href:I(()=>o.value.href),isActive:i,isExactActive:l,navigate:a}}const y1e=re({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:U3,setup(e,{slots:t}){const n=ct(U3(e)),{options:o}=We(DS),r=I(()=>({[X3(e.activeClass,o.linkActiveClass,"router-link-active")]:n.isActive,[X3(e.exactActiveClass,o.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const i=t.default&&t.default(n);return e.custom?i:Sa("a",{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:r.value},i)}}}),S1e=y1e;function $1e(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function C1e(e,t){for(const n in t){const o=t[n],r=e[n];if(typeof o=="string"){if(o!==r)return!1}else if(!Xo(r)||r.length!==o.length||o.some((i,l)=>i!==r[l]))return!1}return!0}function G3(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const X3=(e,t,n)=>e??t??n,x1e=re({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){const o=We(a0),r=I(()=>e.route||o.value),i=We(K3,0),l=I(()=>{let c=Et(i);const{matched:u}=r.value;let d;for(;(d=u[c])&&!d.components;)c++;return c}),a=I(()=>r.value.matched[l.value]);Ge(K3,I(()=>l.value+1)),Ge(m1e,a),Ge(a0,r);const s=oe();return be(()=>[s.value,a.value,e.name],([c,u,d],[f,h,v])=>{u&&(u.instances[d]=c,h&&h!==u&&c&&c===f&&(u.leaveGuards.size||(u.leaveGuards=h.leaveGuards),u.updateGuards.size||(u.updateGuards=h.updateGuards))),c&&u&&(!h||!Da(u,h)||!f)&&(u.enterCallbacks[d]||[]).forEach(g=>g(c))},{flush:"post"}),()=>{const c=r.value,u=e.name,d=a.value,f=d&&d.components[u];if(!f)return Y3(n.default,{Component:f,route:c});const h=d.props[u],v=h?h===!0?c.params:typeof h=="function"?h(c):h:null,b=Sa(f,Pt({},v,t,{onVnodeUnmounted:y=>{y.component.isUnmounted&&(d.instances[u]=null)},ref:s}));return Y3(n.default,{Component:b,route:c})||b}}});function Y3(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const w1e=x1e;function O1e(e){const t=c1e(e.routes,e),n=e.parseQuery||g1e,o=e.stringifyQuery||V3,r=e.history,i=ps(),l=ps(),a=ps(),s=ne(ti);let c=ti;Xl&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const u=sv.bind(null,K=>""+K),d=sv.bind(null,Nye),f=sv.bind(null,Ec);function h(K,ee){let Z,U;return __(K)?(Z=t.getRecordMatcher(K),U=ee):U=K,t.addRoute(U,Z)}function v(K){const ee=t.getRecordMatcher(K);ee&&t.removeRoute(ee)}function g(){return t.getRoutes().map(K=>K.record)}function b(K){return!!t.getRecordMatcher(K)}function y(K,ee){if(ee=Pt({},ee||s.value),typeof K=="string"){const te=cv(n,K,ee.path),se=t.resolve({path:te.path},ee),ie=r.createHref(te.fullPath);return Pt(te,se,{params:f(se.params),hash:Ec(te.hash),redirectedFrom:void 0,href:ie})}let Z;if(K.path!=null)Z=Pt({},K,{path:cv(n,K.path,ee.path).path});else{const te=Pt({},K.params);for(const se in te)te[se]==null&&delete te[se];Z=Pt({},K,{params:d(te)}),ee.params=d(ee.params)}const U=t.resolve(Z,ee),Q=K.hash||"";U.params=u(f(U.params));const W=Lye(o,Pt({},K,{hash:Rye(Q),path:U.path})),G=r.createHref(W);return Pt({fullPath:W,hash:Q,query:o===V3?v1e(K.query):K.query||{}},U,{redirectedFrom:void 0,href:G})}function S(K){return typeof K=="string"?cv(n,K,s.value.path):Pt({},K)}function $(K,ee){if(c!==K)return Ba(8,{from:ee,to:K})}function x(K){return w(K)}function C(K){return x(Pt(S(K),{replace:!0}))}function O(K){const ee=K.matched[K.matched.length-1];if(ee&&ee.redirect){const{redirect:Z}=ee;let U=typeof Z=="function"?Z(K):Z;return typeof U=="string"&&(U=U.includes("?")||U.includes("#")?U=S(U):{path:U},U.params={}),Pt({query:K.query,hash:K.hash,params:U.path!=null?{}:K.params},U)}}function w(K,ee){const Z=c=y(K),U=s.value,Q=K.state,W=K.force,G=K.replace===!0,te=O(Z);if(te)return w(Pt(S(te),{state:typeof te=="object"?Pt({},Q,te.state):Q,force:W,replace:G}),ee||Z);const se=Z;se.redirectedFrom=ee;let ie;return!W&&zye(o,U,Z)&&(ie=Ba(16,{to:se,from:U}),z(U,U,!0,!1)),(ie?Promise.resolve(ie):M(se,U)).catch(de=>Sr(de)?Sr(de,2)?de:L(de):k(de,se,U)).then(de=>{if(de){if(Sr(de,2))return w(Pt({replace:G},S(de.to),{state:typeof de.to=="object"?Pt({},Q,de.to.state):Q,force:W}),ee||se)}else de=A(se,U,!0,G,Q);return E(se,U,de),de})}function P(K,ee){const Z=$(K,ee);return Z?Promise.reject(Z):Promise.resolve()}function T(K){const ee=Y.values().next().value;return ee&&typeof ee.runWithContext=="function"?ee.runWithContext(K):K()}function M(K,ee){let Z;const[U,Q,W]=P1e(K,ee);Z=uv(U.reverse(),"beforeRouteLeave",K,ee);for(const te of U)te.leaveGuards.forEach(se=>{Z.push(ai(se,K,ee))});const G=P.bind(null,K,ee);return Z.push(G),J(Z).then(()=>{Z=[];for(const te of i.list())Z.push(ai(te,K,ee));return Z.push(G),J(Z)}).then(()=>{Z=uv(Q,"beforeRouteUpdate",K,ee);for(const te of Q)te.updateGuards.forEach(se=>{Z.push(ai(se,K,ee))});return Z.push(G),J(Z)}).then(()=>{Z=[];for(const te of W)if(te.beforeEnter)if(Xo(te.beforeEnter))for(const se of te.beforeEnter)Z.push(ai(se,K,ee));else Z.push(ai(te.beforeEnter,K,ee));return Z.push(G),J(Z)}).then(()=>(K.matched.forEach(te=>te.enterCallbacks={}),Z=uv(W,"beforeRouteEnter",K,ee,T),Z.push(G),J(Z))).then(()=>{Z=[];for(const te of l.list())Z.push(ai(te,K,ee));return Z.push(G),J(Z)}).catch(te=>Sr(te,8)?te:Promise.reject(te))}function E(K,ee,Z){a.list().forEach(U=>T(()=>U(K,ee,Z)))}function A(K,ee,Z,U,Q){const W=$(K,ee);if(W)return W;const G=ee===ti,te=Xl?history.state:{};Z&&(U||G?r.replace(K.fullPath,Pt({scroll:G&&te&&te.scroll},Q)):r.push(K.fullPath,Q)),s.value=K,z(K,ee,Z,G),L()}let R;function H(){R||(R=r.listen((K,ee,Z)=>{if(!X.listening)return;const U=y(K),Q=O(U);if(Q){w(Pt(Q,{replace:!0}),U).catch(Us);return}c=U;const W=s.value;Xl&&Xye(N3(W.fullPath,Z.delta),wh()),M(U,W).catch(G=>Sr(G,12)?G:Sr(G,2)?(w(G.to,U).then(te=>{Sr(te,20)&&!Z.delta&&Z.type===Mc.pop&&r.go(-1,!1)}).catch(Us),Promise.reject()):(Z.delta&&r.go(-Z.delta,!1),k(G,U,W))).then(G=>{G=G||A(U,W,!1),G&&(Z.delta&&!Sr(G,8)?r.go(-Z.delta,!1):Z.type===Mc.pop&&Sr(G,20)&&r.go(-1,!1)),E(U,W,G)}).catch(Us)}))}let _=ps(),B=ps(),N;function k(K,ee,Z){L(K);const U=B.list();return U.length?U.forEach(Q=>Q(K,ee,Z)):console.error(K),Promise.reject(K)}function F(){return N&&s.value!==ti?Promise.resolve():new Promise((K,ee)=>{_.add([K,ee])})}function L(K){return N||(N=!K,H(),_.list().forEach(([ee,Z])=>K?Z(K):ee()),_.reset()),K}function z(K,ee,Z,U){const{scrollBehavior:Q}=e;if(!Xl||!Q)return Promise.resolve();const W=!Z&&Yye(N3(K.fullPath,0))||(U||!Z)&&history.state&&history.state.scroll||null;return ot().then(()=>Q(K,ee,W)).then(G=>G&&Gye(G)).catch(G=>k(G,K,ee))}const j=K=>r.go(K);let q;const Y=new Set,X={currentRoute:s,listening:!0,addRoute:h,removeRoute:v,clearRoutes:t.clearRoutes,hasRoute:b,getRoutes:g,resolve:y,options:e,push:x,replace:C,go:j,back:()=>j(-1),forward:()=>j(1),beforeEach:i.add,beforeResolve:l.add,afterEach:a.add,onError:B.add,isReady:F,install(K){const ee=this;K.component("RouterLink",S1e),K.component("RouterView",w1e),K.config.globalProperties.$router=ee,Object.defineProperty(K.config.globalProperties,"$route",{enumerable:!0,get:()=>Et(s)}),Xl&&!q&&s.value===ti&&(q=!0,x(r.location).catch(Q=>{}));const Z={};for(const Q in ti)Object.defineProperty(Z,Q,{get:()=>s.value[Q],enumerable:!0});K.provide(DS,ee),K.provide(B_,gO(Z)),K.provide(a0,s);const U=K.unmount;Y.add(K),K.unmount=function(){Y.delete(K),Y.size<1&&(c=ti,R&&R(),R=null,s.value=ti,q=!1,N=!1),U()}}};function J(K){return K.reduce((ee,Z)=>ee.then(()=>T(Z)),Promise.resolve())}return X}function P1e(e,t){const n=[],o=[],r=[],i=Math.max(t.matched.length,e.matched.length);for(let l=0;l<i;l++){const a=t.matched[l];a&&(e.matched.find(c=>Da(c,a))?o.push(a):n.push(a));const s=e.matched[l];s&&(t.matched.find(c=>Da(c,s))||r.push(s))}return[n,o,r]}const I1e={class:"not-found-wrapper"},T1e=re({__name:"index",setup(e){const t=()=>{NS.replace("/")};return(n,o)=>{const r=xo("a-button"),i=xo("a-result");return di(),Yl("div",I1e,[p(i,{status:"404",title:"404","sub-title":"Sorry, the page you visited does not exist."},{extra:vn(()=>[p(r,{onClick:t,type:"primary"},{default:vn(()=>[Lt("Back Home")]),_:1})]),_:1})])}}}),E1e=xh(T1e,[["__scopeId","data-v-aef52a59"]]),BS=e=>(X7("data-v-b118bb94"),e=e(),Y7(),e),M1e={class:"view-wrapper"},_1e={style:{"margin-top":"10vh",padding:"32px"}},A1e={class:"chat-box-placeholder"},R1e=["data-seg-id"],D1e={class:"actions-box"},B1e={class:"left-actions"},N1e={class:"config-content"},k1e={class:"config-block"},F1e=BS(()=>tn("h4",{style:{"font-weight":"500"}},"Page Max Width:",-1)),L1e={class:"config-block"},z1e=BS(()=>tn("h4",{style:{"font-weight":"500"}},"Show Realtime Buffer:",-1)),H1e={class:"config-block"},j1e=BS(()=>tn("h4",{style:{"font-weight":"500"}},"Text Font Size:",-1)),W1e={style:{display:"flex","justify-content":"end"}},V1e="Please check if the microphone is available before the experience, specify the audio language and translation language, click the switch button to start recording, and you can get the recognized and translated text in real time.",K1e=re({__name:"index",setup(e){const t=Tc(),n=window.location.host;let o="ws";n.startsWith("127.0.0.1")||n.startsWith("localhost")?o="ws":o="wss";const r=`${o}://`+n+"/ws?";console.warn("ws_url: ",r);const i=oe(null),l=async Y=>{console.warn("start websocket ..."),i.value&&i.value.readyState!==WebSocket.CLOSED&&i.value.close();const X=`${r}${Y}`;i.value=new WebSocket(X),i.value.binaryType="arraybuffer",console.warn("created web socket ..."),i.value.addEventListener("open",()=>{console.log("WebSocket 连接成功"),v(),h.value=!0}),i.value.addEventListener("close",()=>{console.log("WebSocket 连接已关闭")}),i.value.onclose=J=>{console.log("code:",J.code,"reason:",J.reason,"wasClean:",J.wasClean),console.log("WebSocket 连接已关闭:",J)},i.value.addEventListener("error",J=>{console.error("WebSocket 连接错误:",J)}),i.value.addEventListener("message",J=>{const K=JSON.parse(J.data);console.log("WebSocket 收到消息:",K),K&&K.result&&E(K.result)})},a=async()=>{i.value&&(await i.value.close(),i.value=null)},s=oe(null),c=oe(null),u=oe(null),d=oe(null),f=oe(null),h=oe(!1),v=async()=>{try{const Y=await navigator.mediaDevices.getUserMedia({audio:{sampleRate:16e3,channelCount:1}});s.value=Y;const X=new AudioContext({sampleRate:16e3});u.value=X;const J=X.createMediaStreamSource(Y);d.value=J;const K=X.createScriptProcessor(1024*6,1,1);f.value=K,J.connect(K),K.connect(X.destination),K.onaudioprocess=ee=>{if(!h.value||!i.value||i.value.readyState!==WebSocket.OPEN)return;const Z=ee.inputBuffer.getChannelData(0),U=new Int16Array(Z.length);for(let Q=0;Q<Z.length;Q++)U[Q]=Math.max(-1,Math.min(1,Z[Q]))*32767;g(U)},h.value=!0,console.log("音频捕获已启动")}catch(Y){console.error("音频捕获失败:",Y)}},g=Y=>{if(i.value)i.value.send(Y),console.log("WebSocket send audio chunk success");else{console.error("WebSocket not initialized, sendAudioChunk failed");return}},b=async()=>{console.log("requirePermissionAction");try{if(!i.value||i.value.readyState!==WebSocket.OPEN){console.log("current lang_str : ",x.value);const Y=x.value;A(),await l(Y)}}catch(Y){console.log("Error accessing microphone: ",Y)}};je(()=>{console.log("[translator]: mounted"),z.value=t.$state.fs,L.value=t.$state.width_max,F.value=t.$state.vad}),xn(()=>{console.log("[HomePage]: unmounted"),i.value&&i.value.close(),c.value&&y()});const y=()=>{h.value=!1,a(),console.log("音频捕获已停止"),f.value&&(f.value.disconnect(),f.value=null),d.value&&(d.value.disconnect(),d.value=null),s.value&&(s.value.getTracks().forEach(Y=>Y.stop()),s.value=null),u.value&&(u.value.close(),u.value=null),P.value="",T.value="",M.value="",console.log("录音已停止")},S=async Y=>{console.log(`selected ${Y}`),h.value=!1,await y(),console.log("new lang_str: ",Y),console.log("trans_lang : ",x.value)},$=Y=>{h.value=Y,Y?b():y()},x=oe("from=en&to=zh"),C=[{value:"from=en&to=zh",label:"English -> Chinese"},{value:"from=zh&to=en",label:"Chinese -> English"}],O=[],w=ct([]),P=oe(""),T=oe(""),M=oe(""),E=Y=>{if(console.log("updateViewData: ",Y),Y){const{context:X,from:J,to:K,seg_id:ee,partial:Z,tranContent:U}=Y;if(Z==!0){P.value=X,T.value=U,M.value=ee;return}else{const Q={context:X,from:J,to:K,seg_id:ee,partial:Z,tranContent:U};w.length>100&&w.splice(0,40),w.push(Q);const W=`[src]: ${X}
|
| 496 |
${"-".repeat(80)}
|
| 497 |
[dst]: ${U}
|
| 498 |
|
| 499 |
`;O.push(W),P.value="",T.value="",M.value=""}}_()},A=()=>{w.splice(0,w.length),O.splice(0,O.length)},R=async()=>{const Y=O.join(`
|
| 500 |
+
`),X=new Blob([Y],{type:"text/plain"}),J=URL.createObjectURL(X),K=document.createElement("a");K.href=J,K.download="record.txt",document.body.appendChild(K),K.click(),document.body.removeChild(K),URL.revokeObjectURL(J)},H=oe(null),_=()=>{ot(()=>{H.value&&(H.value.scrollTop=H.value.scrollHeight+144)})};be(()=>[...w],()=>{_()},{deep:!0}),be(()=>T.value,()=>{_()});const B=oe(!1),N=()=>{B.value=!0},k=()=>{B.value=!1,F.value!=t.$state.vad&&(t.$state.vad=F.value,c.value&&y())},F=oe(.3),L=oe(!1),z=oe("trans-font-size-18"),j=oe(!0),q=Y=>{console.log("onFontSizeChange",Y.target.value),z.value=Y.target.value,t.$state.fs=Y.target.value};return(Y,X)=>{const J=xo("a-radio"),K=xo("a-radio-group"),ee=xo("a-switch"),Z=xo("a-button"),U=xo("a-popover"),Q=xo("a-select"),W=xo("a-popconfirm"),G=xo("a-card");return di(),Yl("div",M1e,[tn("div",{class:Pr(["content-wrapper",L.value?"wrapper-width-auto":"wrapper-width-fixed"])},[tn("div",_1e,[p(G,{bordered:!1,style:{width:"100%","min-width":"100%"}},{actions:vn(()=>[tn("div",D1e,[tn("div",B1e,[p(U,{open:B.value,"onUpdate:open":X[4]||(X[4]=te=>B.value=te),placement:"topLeft",trigger:"click"},{content:vn(()=>[tn("div",N1e,[LA("",!0),tn("div",k1e,[F1e,p(ee,{checked:L.value,"onUpdate:checked":X[1]||(X[1]=te=>L.value=te)},null,8,["checked"])]),tn("div",L1e,[z1e,p(ee,{checked:j.value,"onUpdate:checked":X[2]||(X[2]=te=>j.value=te)},null,8,["checked"])]),tn("div",H1e,[j1e,p(K,{value:z.value,"onUpdate:value":X[3]||(X[3]=te=>z.value=te),onChange:q},{default:vn(()=>[p(J,{value:"trans-font-size-16"},{default:vn(()=>[Lt("Small")]),_:1}),p(J,{value:"trans-font-size-18"},{default:vn(()=>[Lt("Default")]),_:1}),p(J,{value:"trans-font-size-20"},{default:vn(()=>[Lt("Normal")]),_:1}),p(J,{value:"trans-font-size-22"},{default:vn(()=>[Lt("Medium")]),_:1}),p(J,{value:"trans-font-size-24"},{default:vn(()=>[Lt("Large")]),_:1})]),_:1},8,["value"])])]),tn("div",W1e,[p(Z,{type:"primary",onClick:k},{default:vn(()=>[Lt("Done")]),_:1})])]),default:vn(()=>[p(Z,{type:"dashed",shape:"circle",size:"middle",onClick:N},{icon:vn(()=>[p(Et(vme))]),_:1})]),_:1},8,["open"]),p(Q,{value:x.value,"onUpdate:value":X[5]||(X[5]=te=>x.value=te),style:{width:"240px"},placeholder:"Select Language",options:C,onChange:S},null,8,["value"]),p(W,{title:"save the current text content?",onConfirm:R,"ok-text":"Yes","cancel-text":"No"},{icon:vn(()=>[p(Et(sme),{style:{color:"red"}})]),default:vn(()=>[p(Z,{type:"dashed",shape:"circle",size:"middle"},{icon:vn(()=>[p(Et(MM))]),_:1})]),_:1})]),p(ee,{key:"switcher",size:"large",type:"danger","checked-children":"ON","un-checked-children":"OFF",checked:h.value,"onUpdate:checked":X[6]||(X[6]=te=>h.value=te),onChange:$},null,8,["checked"])])]),default:vn(()=>[fn(tn("div",A1e,ns(V1e),512),[[mn,!(w.length||P.value)]]),fn(tn("div",{class:"trans-list",ref_key:"transListRef",ref:H},[(di(!0),Yl(Ve,null,pA(w,te=>(di(),Yl("div",{key:te.context,class:Pr(["node"]),"data-seg-id":te.seg_id},[tn("div",{class:Pr(["trans-src-lang",z.value])},ns(te.context),3),tn("div",{class:Pr(["trans-dst-lang",z.value])},ns(te.tranContent),3)],8,R1e))),128)),fn((di(),Yl("div",{class:"node current_node",key:M.value},[tn("div",{class:Pr(["trans-src-lang",z.value])},ns(P.value),3),tn("div",{class:Pr(["trans-dst-lang",z.value])},ns(T.value),3)])),[[mn,j.value]])],512),[[mn,w.length||P.value]])]),_:1})])],2)])}}}),U1e=xh(K1e,[["__scopeId","data-v-b118bb94"]]),G1e=AS({id:"play_state",persist:!1,state:()=>{const e=Tc();return console.log("configState",e.$state.role_name),{current_state:"init",video_url:"",urls:{waiting:e.$state.roles_resources[e.$state.role_name].waiting,listening:e.$state.roles_resources[e.$state.role_name].listening,thinking:e.$state.roles_resources[e.$state.role_name].thinking,talking:""}}},actions:{changeRole(e){this.$patch(t=>{const n=Tc();t.urls.waiting=n.$state.roles_resources[n.$state.role_name].waiting,t.urls.listening=n.$state.roles_resources[n.$state.role_name].listening,t.urls.thinking=n.$state.roles_resources[n.$state.role_name].thinking})},changeState(e){if(["waiting","listening","thinking","talking","init"].indexOf(e)==-1){console.error("invalid state",e);return}this.$patch(t=>{t.video_url=t.urls[e],t.current_state=e})},changeTalkingVideoUrl(e){this.$patch(t=>{t.urls.talking=e})}}}),X1e={class:"content-wrapper"},Y1e=re({__name:"index",setup(e){const t=Tc();return G1e(),je(()=>{console.log("config",t.$state)}),oe(t.$state.file_type),oe(t.$state.role_name),(n,o)=>{const r=xo("a-result");return di(),Yl("div",X1e,[p(r,{style:{width:"100%"},title:"Settings"})])}}}),q1e=xh(Y1e,[["__scopeId","data-v-c39ab0d6"]]),Z1e=[{name:"home",path:"/",component:U1e,meta:{requiresAgreement:!1}},{name:"settings",path:"/settings",component:q1e},{name:"404",path:"/404",component:E1e}],NS=O1e({history:Jye("/app/"),routes:Z1e});NS.beforeEach((e,t,n)=>{console.log("=============== router to : ",e),e.matched.length===0?n({name:"404"}):n()});const N_=aye();N_.use(yye);rP(Cye).use(N_).use(NS).use(w0e).mount("#app")});export default Q1e();
|
frontend/assets/{index-c1b225bf.css → index-54c9f9d3.css}
RENAMED
|
@@ -1 +1 @@
|
|
| 1 |
-
html,body{width:100%;height:100%}input::-ms-clear,input::-ms-reveal{display:none}*,*:before,*:after{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{margin:0}[tabindex="-1"]:focus{outline:none}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5em;font-weight:500}p{margin-top:0;margin-bottom:1em}abbr[title],abbr[data-original-title]{-webkit-text-decoration:underline dotted;text-decoration:underline;text-decoration:underline dotted;border-bottom:0;cursor:help}address{margin-bottom:1em;font-style:normal;line-height:inherit}input[type=text],input[type=password],input[type=number],textarea{-webkit-appearance:none}ol,ul,dl{margin-top:0;margin-bottom:1em}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:500}dd{margin-bottom:.5em;margin-left:0}blockquote{margin:0 0 1em}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}pre,code,kbd,samp{font-size:1em;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace}pre{margin-top:0;margin-bottom:1em;overflow:auto}figure{margin:0 0 1em}img{vertical-align:middle;border-style:none}a,area,button,[role=button],input:not([type=range]),label,select,summary,textarea{touch-action:manipulation}table{border-collapse:collapse}caption{padding-top:.75em;padding-bottom:.3em;text-align:left;caption-side:bottom}input,button,select,optgroup,textarea{margin:0;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}button,html [type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{padding:0;border-style:none}input[type=radio],input[type=checkbox]{box-sizing:border-box;padding:0}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;margin:0;padding:0;border:0}legend{display:block;width:100%;max-width:100%;margin-bottom:.5em;padding:0;color:inherit;font-size:1.5em;line-height:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item}template{display:none}[hidden]{display:none!important}mark{padding:.2em;background-color:#feffe6}:root{font-family:Inter,system-ui,Avenir,Helvetica,Arial,sans-serif;line-height:1.5;font-weight:400;color-scheme:light dark;color:#ffffffde;background-color:#242424;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-text-size-adjust:100%}a{font-weight:500;color:#646cff;text-decoration:inherit}a:hover{color:#535bf2}body{margin:0;display:flex;place-items:center;min-width:320px;height:auto;min-height:auto;color:#333;background:#fff}h1{font-size:3.2em;line-height:1.1}button{border-radius:8px;border:1px solid transparent;padding:.6em 1.2em;font-size:1em;font-weight:500;font-family:inherit;background-color:#1a1a1a;cursor:pointer;transition:border-color .25s}.card{border-bottom:solid 2px lightgray;align-items:center;justify-content:center;margin-top:40px;display:flex;max-width:1024px;width:100%}.seg-title{margin:24px 0;font-size:20px;font-weight:500}.seg-co{width:1022px;text-align:left;border-left:solid 6px midnightblue;padding-left:8px;margin-left:2px;margin-top:36px;line-height:24px}#app{margin:0 auto;padding:0;text-align:center;width:100%}.ant-btn{padding:4px 12px}@media (prefers-color-scheme: light){:root{color:#213547;background-color:#fff}a:hover{color:#747bff}button{background-color:#f9f9f9}}.ant-card{background:#f5f6fa}.ant-card .ant-card-actions{background-color:#e8e8f8cc!important}.ant-popover{max-width:800px!important}.ant-form-item{background:transparent;margin-bottom:40px!important}.ant-form-item .ant-form-item-explain-error{color:#ff4d4f;text-align:left!important}.ant-form-item-label label{font-size:18px!important;color:#1a1a1a!important;font-weight:500!important}.ant-tooltip{max-width:1022px!important}.ant-page-header-heading{width:1022px!important}.highlight{background:ghostwhite}.content[data-v-178d5f9f]{background-color:#fff;max-width:1280px;min-height:720px;margin:0 auto;display:flex;flex-direction:column;align-items:center;justify-content:space-between}.not-found-wrapper[data-v-aef52a59]{height:calc(100vh - 104px)}.config-content[data-v-
|
|
|
|
| 1 |
+
html,body{width:100%;height:100%}input::-ms-clear,input::-ms-reveal{display:none}*,*:before,*:after{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{margin:0}[tabindex="-1"]:focus{outline:none}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5em;font-weight:500}p{margin-top:0;margin-bottom:1em}abbr[title],abbr[data-original-title]{-webkit-text-decoration:underline dotted;text-decoration:underline;text-decoration:underline dotted;border-bottom:0;cursor:help}address{margin-bottom:1em;font-style:normal;line-height:inherit}input[type=text],input[type=password],input[type=number],textarea{-webkit-appearance:none}ol,ul,dl{margin-top:0;margin-bottom:1em}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:500}dd{margin-bottom:.5em;margin-left:0}blockquote{margin:0 0 1em}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}pre,code,kbd,samp{font-size:1em;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace}pre{margin-top:0;margin-bottom:1em;overflow:auto}figure{margin:0 0 1em}img{vertical-align:middle;border-style:none}a,area,button,[role=button],input:not([type=range]),label,select,summary,textarea{touch-action:manipulation}table{border-collapse:collapse}caption{padding-top:.75em;padding-bottom:.3em;text-align:left;caption-side:bottom}input,button,select,optgroup,textarea{margin:0;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}button,html [type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{padding:0;border-style:none}input[type=radio],input[type=checkbox]{box-sizing:border-box;padding:0}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;margin:0;padding:0;border:0}legend{display:block;width:100%;max-width:100%;margin-bottom:.5em;padding:0;color:inherit;font-size:1.5em;line-height:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item}template{display:none}[hidden]{display:none!important}mark{padding:.2em;background-color:#feffe6}:root{font-family:Inter,system-ui,Avenir,Helvetica,Arial,sans-serif;line-height:1.5;font-weight:400;color-scheme:light dark;color:#ffffffde;background-color:#242424;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-text-size-adjust:100%}a{font-weight:500;color:#646cff;text-decoration:inherit}a:hover{color:#535bf2}body{margin:0;display:flex;place-items:center;min-width:320px;height:auto;min-height:auto;color:#333;background:#fff}h1{font-size:3.2em;line-height:1.1}button{border-radius:8px;border:1px solid transparent;padding:.6em 1.2em;font-size:1em;font-weight:500;font-family:inherit;background-color:#1a1a1a;cursor:pointer;transition:border-color .25s}.card{border-bottom:solid 2px lightgray;align-items:center;justify-content:center;margin-top:40px;display:flex;max-width:1024px;width:100%}.seg-title{margin:24px 0;font-size:20px;font-weight:500}.seg-co{width:1022px;text-align:left;border-left:solid 6px midnightblue;padding-left:8px;margin-left:2px;margin-top:36px;line-height:24px}#app{margin:0 auto;padding:0;text-align:center;width:100%}.ant-btn{padding:4px 12px}@media (prefers-color-scheme: light){:root{color:#213547;background-color:#fff}a:hover{color:#747bff}button{background-color:#f9f9f9}}.ant-card{background:#f5f6fa}.ant-card .ant-card-actions{background-color:#e8e8f8cc!important}.ant-popover{max-width:800px!important}.ant-form-item{background:transparent;margin-bottom:40px!important}.ant-form-item .ant-form-item-explain-error{color:#ff4d4f;text-align:left!important}.ant-form-item-label label{font-size:18px!important;color:#1a1a1a!important;font-weight:500!important}.ant-tooltip{max-width:1022px!important}.ant-page-header-heading{width:1022px!important}.highlight{background:ghostwhite}.content[data-v-178d5f9f]{background-color:#fff;max-width:1280px;min-height:720px;margin:0 auto;display:flex;flex-direction:column;align-items:center;justify-content:space-between}.not-found-wrapper[data-v-aef52a59]{height:calc(100vh - 104px)}.config-content[data-v-b118bb94]{width:420px;margin:12px}.config-content .config-block[data-v-b118bb94]{margin:12px;padding-bottom:12px}.view-wrapper[data-v-b118bb94]{width:100%;height:100%;background-color:#fff}.view-wrapper .wrapper-width-fixed[data-v-b118bb94]{width:1280px}.view-wrapper .wrapper-width-auto[data-v-b118bb94]{width:100vw}.view-wrapper .content-wrapper[data-v-b118bb94]{text-align:left;max-width:100vw;min-width:320px;margin-bottom:64px;min-height:calc(100vh - 438px)}.view-wrapper .content-wrapper .chat-box[data-v-b118bb94]{width:100%;height:54vh;border-radius:4px;padding:12px;color:#2e2f33;font-size:18px}.view-wrapper .content-wrapper .chat-box-placeholder[data-v-b118bb94]{width:100%;height:58vh;border-radius:4px;padding:12px;font-size:18px;color:#a4a6ac}.view-wrapper .content-wrapper .actions-box[data-v-b118bb94]{display:flex;align-items:center;justify-content:space-between;margin:0 24px;height:48px}.view-wrapper .content-wrapper .actions-box .left-actions[data-v-b118bb94]{display:flex;align-items:center;justify-content:space-between;width:332px}.view-wrapper .content-wrapper .actions-box .right-actions[data-v-b118bb94]{display:flex;align-items:center;justify-content:space-between;width:108px}.view-wrapper .content-wrapper .trans-list[data-v-b118bb94]{overflow-y:auto;width:100%;height:58vh;scrollbar-width:none;-ms-overflow-style:none}.view-wrapper .content-wrapper .trans-list[data-v-b118bb94]::-webkit-scrollbar{display:none}.view-wrapper .content-wrapper .trans-list .node[data-v-b118bb94]{margin-bottom:36px;width:100%!important;transition:all .3s ease}.view-wrapper .content-wrapper .trans-list .node .trans-time[data-v-b118bb94]{font-size:14px;color:#c4c6cc}.view-wrapper .content-wrapper .trans-list .node .trans-font-size-16[data-v-b118bb94]{font-size:16px}.view-wrapper .content-wrapper .trans-list .node .trans-font-size-18[data-v-b118bb94]{font-size:18px}.view-wrapper .content-wrapper .trans-list .node .trans-font-size-20[data-v-b118bb94]{font-size:20px}.view-wrapper .content-wrapper .trans-list .node .trans-font-size-22[data-v-b118bb94]{font-size:22px}.view-wrapper .content-wrapper .trans-list .node .trans-font-size-24[data-v-b118bb94]{font-size:24px}.view-wrapper .content-wrapper .trans-list .node .trans-src-lang[data-v-b118bb94]{color:#909299;font-weight:500}.view-wrapper .content-wrapper .trans-list .node .trans-dst-lang[data-v-b118bb94]{color:#2e2f33;font-weight:600}.view-wrapper .content-wrapper .trans-list .current_node[data-v-b118bb94]{background-color:#f0f1f7;padding:4px 8px}@keyframes highlight-b118bb94{0%{background-color:transparent}50%{background-color:#fff1ce80}to{background-color:transparent}}@keyframes slideIn-b118bb94{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.content-wrapper[data-v-c39ab0d6]{text-align:left;max-width:800px;min-width:320px;margin-bottom:64px;min-height:calc(100vh - 438px)}.content-wrapper .content-box[data-v-c39ab0d6]{padding:24px;height:240px;background-color:#e8e8e8;border-radius:16px;width:50%;margin:48px auto;min-width:300px}.content-wrapper .video-box[data-v-c39ab0d6]{max-width:800px;min-width:320px;width:90vw;height:auto}
|
frontend/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
| 5 |
<link rel="icon" type="image/svg+xml" href="./favicon.ico" />
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 7 |
<title>Translator</title>
|
| 8 |
-
<script type="module" crossorigin src="./assets/index-
|
| 9 |
-
<link rel="stylesheet" href="./assets/index-
|
| 10 |
</head>
|
| 11 |
<body>
|
| 12 |
<div id="app"></div>
|
|
|
|
| 5 |
<link rel="icon" type="image/svg+xml" href="./favicon.ico" />
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 7 |
<title>Translator</title>
|
| 8 |
+
<script type="module" crossorigin src="./assets/index-4906844f.js"></script>
|
| 9 |
+
<link rel="stylesheet" href="./assets/index-54c9f9d3.css">
|
| 10 |
</head>
|
| 11 |
<body>
|
| 12 |
<div id="app"></div>
|
frontend/index2.html
DELETED
|
@@ -1,169 +0,0 @@
|
|
| 1 |
-
<!DOCTYPE html>
|
| 2 |
-
<html lang="zh">
|
| 3 |
-
<head>
|
| 4 |
-
<meta charset="UTF-8" />
|
| 5 |
-
<title>试试翻译</title>
|
| 6 |
-
<style>
|
| 7 |
-
body {
|
| 8 |
-
background-color: #f9f9fc;
|
| 9 |
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
|
| 10 |
-
margin: 0;
|
| 11 |
-
padding: 2rem;
|
| 12 |
-
}
|
| 13 |
-
.translation-box {
|
| 14 |
-
background: #f2f2f8;
|
| 15 |
-
border-radius: 12px;
|
| 16 |
-
padding: 1.5rem;
|
| 17 |
-
max-width: 800px;
|
| 18 |
-
margin: 0 auto;
|
| 19 |
-
min-height: 200px;
|
| 20 |
-
}
|
| 21 |
-
.entry {
|
| 22 |
-
margin-bottom: 1.5rem;
|
| 23 |
-
}
|
| 24 |
-
.timestamp {
|
| 25 |
-
font-size: 0.75rem;
|
| 26 |
-
color: #999;
|
| 27 |
-
}
|
| 28 |
-
.original {
|
| 29 |
-
font-size: 1rem;
|
| 30 |
-
color: #333;
|
| 31 |
-
}
|
| 32 |
-
.translation {
|
| 33 |
-
font-size: 1rem;
|
| 34 |
-
font-weight: bold;
|
| 35 |
-
color: #000;
|
| 36 |
-
}
|
| 37 |
-
.footer {
|
| 38 |
-
display: flex;
|
| 39 |
-
justify-content: space-between;
|
| 40 |
-
align-items: center;
|
| 41 |
-
margin-top: 2rem;
|
| 42 |
-
}
|
| 43 |
-
.lang-select {
|
| 44 |
-
background: white;
|
| 45 |
-
border-radius: 9999px;
|
| 46 |
-
padding: 0.4rem 1rem;
|
| 47 |
-
border: none;
|
| 48 |
-
font-size: 1rem;
|
| 49 |
-
box-shadow: 0 0 0 1px #ddd;
|
| 50 |
-
}
|
| 51 |
-
.record-button {
|
| 52 |
-
background-color: #1e40af;
|
| 53 |
-
color: white;
|
| 54 |
-
border: none;
|
| 55 |
-
padding: 0.6rem 1.2rem;
|
| 56 |
-
border-radius: 9999px;
|
| 57 |
-
font-size: 1rem;
|
| 58 |
-
cursor: pointer;
|
| 59 |
-
}
|
| 60 |
-
</style>
|
| 61 |
-
</head>
|
| 62 |
-
<body>
|
| 63 |
-
<div class="translation-box" id="translationBox">
|
| 64 |
-
<!-- 实时内容将插入这里 -->
|
| 65 |
-
</div>
|
| 66 |
-
|
| 67 |
-
<div class="footer">
|
| 68 |
-
<select class="lang-select">
|
| 69 |
-
<option>中文 » 英语</option>
|
| 70 |
-
</select>
|
| 71 |
-
<button class="record-button" onclick="startRecording()">🎤 录音</button>
|
| 72 |
-
</div>
|
| 73 |
-
|
| 74 |
-
<script>
|
| 75 |
-
let ws;
|
| 76 |
-
let mediaRecorder;
|
| 77 |
-
|
| 78 |
-
function formatTimestamp(ms) {
|
| 79 |
-
const sec = ms / 1000;
|
| 80 |
-
const min = Math.floor(sec / 60);
|
| 81 |
-
const s = (sec % 60).toFixed(1);
|
| 82 |
-
return `${String(min).padStart(2, '0')}:${s.padStart(4, '0')}`;
|
| 83 |
-
}
|
| 84 |
-
|
| 85 |
-
let lastSegId = null; // 用于存储上一个 seg_id
|
| 86 |
-
|
| 87 |
-
function addTranslation(result) {
|
| 88 |
-
const box = document.getElementById('translationBox');
|
| 89 |
-
|
| 90 |
-
// 创建一个新的 div 来显示翻译
|
| 91 |
-
const entry = document.createElement('div');
|
| 92 |
-
entry.className = 'entry';
|
| 93 |
-
|
| 94 |
-
console.log(result);
|
| 95 |
-
|
| 96 |
-
const start = formatTimestamp(result.bg);
|
| 97 |
-
const end = formatTimestamp(result.ed);
|
| 98 |
-
|
| 99 |
-
// 判断是否是新的一行
|
| 100 |
-
if (result.seg_id === lastSegId) {
|
| 101 |
-
// 如果 seg_id 相同,更新该行内容
|
| 102 |
-
const existingEntry = box.querySelector(`.entry[data-seg-id="${result.seg_id}"]`);
|
| 103 |
-
if (existingEntry) {
|
| 104 |
-
const translationDiv = existingEntry.querySelector('.translation');
|
| 105 |
-
translationDiv.innerHTML = result.tranContent;
|
| 106 |
-
}
|
| 107 |
-
} else {
|
| 108 |
-
// 如果 seg_id 不同,表示是新的行,添加新行
|
| 109 |
-
entry.setAttribute('data-seg-id', result.seg_id); // 设置 seg_id
|
| 110 |
-
entry.innerHTML = `
|
| 111 |
-
<div class="original">${result.context}</div>
|
| 112 |
-
<div class="translation">${result.tranContent}</div>
|
| 113 |
-
`;
|
| 114 |
-
box.appendChild(entry);
|
| 115 |
-
}
|
| 116 |
-
|
| 117 |
-
// 更新 lastSegId 以便下一次判断
|
| 118 |
-
lastSegId = result.seg_id;
|
| 119 |
-
}
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
async function startRecording() {
|
| 124 |
-
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
| 125 |
-
const audioContext = new AudioContext({ sampleRate: 16000 });
|
| 126 |
-
const source = audioContext.createMediaStreamSource(stream);
|
| 127 |
-
const processor = audioContext.createScriptProcessor(4096, 1, 1);
|
| 128 |
-
|
| 129 |
-
const wsUrl = "ws://localhost:9191/ws?from=zh&to=en";
|
| 130 |
-
ws = new WebSocket(wsUrl);
|
| 131 |
-
|
| 132 |
-
ws.binaryType = "arraybuffer";
|
| 133 |
-
|
| 134 |
-
ws.onopen = () => {
|
| 135 |
-
console.log("WebSocket opened");
|
| 136 |
-
source.connect(processor);
|
| 137 |
-
processor.connect(audioContext.destination);
|
| 138 |
-
|
| 139 |
-
processor.onaudioprocess = (e) => {
|
| 140 |
-
const input = e.inputBuffer.getChannelData(0);
|
| 141 |
-
const buffer = new Int16Array(input.length);
|
| 142 |
-
for (let i = 0; i < input.length; i++) {
|
| 143 |
-
buffer[i] = Math.max(-1, Math.min(1, input[i])) * 0x7FFF;
|
| 144 |
-
}
|
| 145 |
-
ws.send(buffer);
|
| 146 |
-
};
|
| 147 |
-
};
|
| 148 |
-
|
| 149 |
-
ws.onmessage = (event) => {
|
| 150 |
-
try {
|
| 151 |
-
const msg = JSON.parse(event.data);
|
| 152 |
-
if (msg.result) {
|
| 153 |
-
addTranslation(msg.result);
|
| 154 |
-
}
|
| 155 |
-
} catch (e) {
|
| 156 |
-
console.error("Parse error:", e);
|
| 157 |
-
}
|
| 158 |
-
};
|
| 159 |
-
|
| 160 |
-
ws.onerror = (e) => console.error("WebSocket error:", e);
|
| 161 |
-
ws.onclose = () => {
|
| 162 |
-
console.log("WebSocket closed");
|
| 163 |
-
processor.disconnect();
|
| 164 |
-
source.disconnect();
|
| 165 |
-
};
|
| 166 |
-
}
|
| 167 |
-
</script>
|
| 168 |
-
</body>
|
| 169 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
main.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
|
| 2 |
-
from
|
| 3 |
-
from transcribe.whisper_llm_serve import WhisperTranscriptionService
|
| 4 |
from uuid import uuid1
|
| 5 |
from logging import getLogger
|
| 6 |
import numpy as np
|
|
|
|
| 1 |
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
|
| 2 |
+
from transcribe.serve import WhisperTranscriptionService
|
|
|
|
| 3 |
from uuid import uuid1
|
| 4 |
from logging import getLogger
|
| 5 |
import numpy as np
|
transcribe/pipelines/pipe_vad.py
CHANGED
|
@@ -72,7 +72,7 @@ class VadPipe(BasePipe):
|
|
| 72 |
logging.debug(" 🫷Speech ended, capturing audio up to frame: {}".format(rel_end_frame))
|
| 73 |
else:
|
| 74 |
self._status = 'END'
|
| 75 |
-
target_audio = source_audio[rel_start_frame:rel_end_frame]
|
| 76 |
logging.debug(" 🔄 Speech segment captured from frame {} to frame {}".format(rel_start_frame, rel_end_frame))
|
| 77 |
# logging.debug("❌ No valid speech segment detected, setting status to END")
|
| 78 |
else:
|
|
|
|
| 72 |
logging.debug(" 🫷Speech ended, capturing audio up to frame: {}".format(rel_end_frame))
|
| 73 |
else:
|
| 74 |
self._status = 'END'
|
| 75 |
+
target_audio = source_audio[max(rel_start_frame-100, 0):rel_end_frame]
|
| 76 |
logging.debug(" 🔄 Speech segment captured from frame {} to frame {}".format(rel_start_frame, rel_end_frame))
|
| 77 |
# logging.debug("❌ No valid speech segment detected, setting status to END")
|
| 78 |
else:
|
transcribe/{whisper_llm_serve.py → serve.py}
RENAMED
|
@@ -7,9 +7,9 @@ import asyncio
|
|
| 7 |
import numpy as np
|
| 8 |
import config
|
| 9 |
import collections
|
| 10 |
-
from api_model import TransResult, Message
|
| 11 |
|
| 12 |
-
from .utils import log_block,
|
| 13 |
from .translatepipes import TranslatePipes
|
| 14 |
|
| 15 |
from transcribe.pipelines import MetaItem
|
|
@@ -18,26 +18,12 @@ from transcribe.pipelines import MetaItem
|
|
| 18 |
logger = getLogger("TranscriptionService")
|
| 19 |
|
| 20 |
|
| 21 |
-
def _get_text_separator(language: str) -> str:
|
| 22 |
-
"""根据语言返回适当的文本分隔符"""
|
| 23 |
-
return "" if language == "zh" else " "
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
def _start_thread(target_function) -> threading.Thread:
|
| 27 |
-
"""启动守护线程执行指定函数"""
|
| 28 |
-
thread = threading.Thread(target=target_function)
|
| 29 |
-
thread.daemon = True
|
| 30 |
-
thread.start()
|
| 31 |
-
return thread
|
| 32 |
-
|
| 33 |
|
| 34 |
class WhisperTranscriptionService:
|
| 35 |
"""
|
| 36 |
Whisper语音转录服务类,处理音频流转录和翻译
|
| 37 |
"""
|
| 38 |
|
| 39 |
-
SERVER_READY = "SERVER_READY"
|
| 40 |
-
DISCONNECT = "DISCONNECT"
|
| 41 |
|
| 42 |
def __init__(self, websocket, pipe: TranslatePipes, language=None, dst_lang=None, client_uid=None):
|
| 43 |
print('>>>>>>>>>>>>>>>> init service >>>>>>>>>>>>>>>>>>>>>>')
|
|
@@ -47,52 +33,37 @@ class WhisperTranscriptionService:
|
|
| 47 |
self.client_uid = client_uid
|
| 48 |
# 转录结果稳定性管理
|
| 49 |
self.websocket = websocket
|
| 50 |
-
self.
|
| 51 |
-
|
| 52 |
# 音频处理相关
|
| 53 |
self.sample_rate = config.SAMPLE_RATE
|
| 54 |
|
| 55 |
self.lock = threading.Lock()
|
| 56 |
# 文本分隔符,根据语言设置
|
| 57 |
-
self.text_separator =
|
| 58 |
self.loop = asyncio.get_event_loop()
|
| 59 |
# 原始音频队列
|
| 60 |
-
self.
|
| 61 |
# 音频队列缓冲区
|
| 62 |
self.frames_np = np.array([], dtype=np.float32)
|
|
|
|
| 63 |
self.frames_np_start_timestamp = None
|
| 64 |
# 完整音频队列
|
| 65 |
self.full_segments_queue = collections.deque()
|
| 66 |
# 启动处理线程
|
| 67 |
-
self.
|
| 68 |
-
self.
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
self.frame_processing_thread = _start_thread(self._read_frame_processing_loop)
|
| 72 |
self.row_number = 0
|
| 73 |
-
# for test
|
| 74 |
-
self._transcribe_time_cost = 0.
|
| 75 |
-
self._translate_time_cost = 0.
|
| 76 |
|
| 77 |
-
if config.SAVE_DATA_SAVE:
|
| 78 |
-
self._save_task_stop = threading.Event()
|
| 79 |
-
self._save_queue = queue.Queue()
|
| 80 |
-
self._save_thread = _start_thread(self.save_data_loop)
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
def save_data_loop(self):
|
| 84 |
-
writer = TestDataWriter()
|
| 85 |
-
while not self._save_task_stop.is_set():
|
| 86 |
-
test_data = self._save_queue.get()
|
| 87 |
-
writer.write(test_data) # Save test_data to CSV
|
| 88 |
|
| 89 |
def add_frames(self, frame_np: np.ndarray) -> None:
|
| 90 |
"""添加音频帧到处理队列"""
|
| 91 |
-
self.
|
| 92 |
|
| 93 |
def _apply_voice_activity_detection(self, frame_np:np.array):
|
| 94 |
"""应用语音活动检测来优化音频缓冲区"""
|
| 95 |
-
processed_audio = self.
|
| 96 |
speech_audio = np.frombuffer(processed_audio.audio, dtype=np.float32)
|
| 97 |
speech_status = processed_audio.speech_status
|
| 98 |
return speech_audio, speech_status
|
|
@@ -100,9 +71,9 @@ class WhisperTranscriptionService:
|
|
| 100 |
|
| 101 |
def _read_frame_processing_loop(self) -> None:
|
| 102 |
"""从队列获取音频帧并合并到缓冲区"""
|
| 103 |
-
while not self.
|
| 104 |
try:
|
| 105 |
-
frame_np = self.
|
| 106 |
frame_np, speech_status = self._apply_voice_activity_detection(frame_np)
|
| 107 |
|
| 108 |
if frame_np is None:
|
|
@@ -121,7 +92,7 @@ class WhisperTranscriptionService:
|
|
| 121 |
self.full_segments_queue.appendleft(audio_array) # 根据时间是否满足三秒长度 来整合音频块
|
| 122 |
self.frames_np_start_timestamp = time.time()
|
| 123 |
self.frames_np = np.array([], dtype=np.float32)
|
| 124 |
-
|
| 125 |
# 音频结束信号的时候 整合当前缓冲区
|
| 126 |
# START -- END -- START -- END 通常
|
| 127 |
# START -- END -- END end块带有音频信息的通常是4096内断的一个短音
|
|
@@ -134,7 +105,7 @@ class WhisperTranscriptionService:
|
|
| 134 |
self.frames_np = np.array([], dtype=np.float32)
|
| 135 |
else:
|
| 136 |
logger.debug(f"🥳 当前时间与上一句的时间差: {time_diff:.2f}s,继续保留在缓冲区")
|
| 137 |
-
|
| 138 |
except queue.Empty:
|
| 139 |
pass
|
| 140 |
|
|
@@ -142,8 +113,8 @@ class WhisperTranscriptionService:
|
|
| 142 |
"""主转录处理循环"""
|
| 143 |
frame_epoch = 1
|
| 144 |
|
| 145 |
-
while not self.
|
| 146 |
-
|
| 147 |
if len(self.frames_np) ==0:
|
| 148 |
time.sleep(0.1)
|
| 149 |
continue
|
|
@@ -155,11 +126,21 @@ class WhisperTranscriptionService:
|
|
| 155 |
else:
|
| 156 |
audio_buffer = self.frames_np[:int(frame_epoch * 1.5 * self.sample_rate)].copy()# 获取 1.5s * epoch 个音频长度
|
| 157 |
partial = True
|
| 158 |
-
|
| 159 |
if len(audio_buffer) < int(self.sample_rate):
|
| 160 |
-
|
| 161 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
audio_buffer = silence_audio
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
|
| 164 |
logger.debug(f"audio buffer size: {len(audio_buffer) / self.sample_rate:.2f}s")
|
| 165 |
meta_item = self._transcribe_audio(audio_buffer)
|
|
@@ -169,6 +150,8 @@ class WhisperTranscriptionService:
|
|
| 169 |
|
| 170 |
if len(segments):
|
| 171 |
seg_text = self.text_separator.join(seg.text for seg in segments)
|
|
|
|
|
|
|
| 172 |
# 整行
|
| 173 |
if not partial:
|
| 174 |
translated_content = self._translate_text_large(seg_text)
|
|
@@ -187,54 +170,35 @@ class WhisperTranscriptionService:
|
|
| 187 |
partial=partial
|
| 188 |
)
|
| 189 |
self._send_result_to_client(result)
|
| 190 |
-
|
| 191 |
-
|
| 192 |
|
| 193 |
def _transcribe_audio(self, audio_buffer: np.ndarray)->MetaItem:
|
| 194 |
"""转录音频并返回转录片段"""
|
| 195 |
log_block("Audio buffer length", f"{audio_buffer.shape[0]/self.sample_rate:.2f}", "s")
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
segments = result.segments
|
| 200 |
-
time_diff = (time.perf_counter() - start_time)
|
| 201 |
-
logger.debug(f"📝 transcribe Segments: {segments} ")
|
| 202 |
-
log_block("📝 transcribe output", f"{self.text_separator.join(seg.text for seg in segments)}", "")
|
| 203 |
-
log_block("📝 transcribe time", f"{time_diff:.3f}", "s")
|
| 204 |
-
self._transcribe_time_cost = round(time_diff, 3)
|
| 205 |
return result
|
| 206 |
|
| 207 |
def _translate_text(self, text: str) -> str:
|
| 208 |
"""将文本翻译为目标语言"""
|
| 209 |
if not text.strip():
|
| 210 |
return ""
|
| 211 |
-
|
| 212 |
log_block("🐧 Translation input ", f"{text}")
|
| 213 |
-
start_time = time.perf_counter()
|
| 214 |
|
| 215 |
-
result = self.
|
| 216 |
translated_text = result.translate_content
|
| 217 |
-
|
| 218 |
-
log_block("🐧 Translation time ", f"{time_diff:.3f}", "s")
|
| 219 |
log_block("🐧 Translation out ", f"{translated_text}")
|
| 220 |
-
self._translate_time_cost = round(time_diff, 3)
|
| 221 |
return translated_text
|
| 222 |
|
| 223 |
def _translate_text_large(self, text: str) -> str:
|
| 224 |
"""将文本翻译为目标语言"""
|
| 225 |
if not text.strip():
|
| 226 |
return ""
|
| 227 |
-
|
| 228 |
log_block("Translation input", f"{text}")
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
result = self._translate_pipe.translate_large(text, self.source_language, self.target_language)
|
| 232 |
translated_text = result.translate_content
|
| 233 |
-
|
| 234 |
-
time_diff = (time.perf_counter() - start_time)
|
| 235 |
-
log_block("Translation large model time ", f"{time_diff:.3f}", "s")
|
| 236 |
log_block("Translation large model output", f"{translated_text}")
|
| 237 |
-
self._translate_time_cost = round(time_diff, 3)
|
| 238 |
return translated_text
|
| 239 |
|
| 240 |
|
|
@@ -252,8 +216,5 @@ class WhisperTranscriptionService:
|
|
| 252 |
|
| 253 |
def stop(self) -> None:
|
| 254 |
"""停止所有处理线程并清理资源"""
|
| 255 |
-
self.
|
| 256 |
-
self._frame_processing_thread_stop.set()
|
| 257 |
-
if config.SAVE_DATA_SAVE:
|
| 258 |
-
self._save_task_stop.set()
|
| 259 |
logger.info(f"Stopping transcription service for client: {self.client_uid}")
|
|
|
|
| 7 |
import numpy as np
|
| 8 |
import config
|
| 9 |
import collections
|
| 10 |
+
from api_model import TransResult, Message
|
| 11 |
|
| 12 |
+
from .utils import log_block, start_thread, get_text_separator, filter_words
|
| 13 |
from .translatepipes import TranslatePipes
|
| 14 |
|
| 15 |
from transcribe.pipelines import MetaItem
|
|
|
|
| 18 |
logger = getLogger("TranscriptionService")
|
| 19 |
|
| 20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
class WhisperTranscriptionService:
|
| 23 |
"""
|
| 24 |
Whisper语音转录服务类,处理音频流转录和翻译
|
| 25 |
"""
|
| 26 |
|
|
|
|
|
|
|
| 27 |
|
| 28 |
def __init__(self, websocket, pipe: TranslatePipes, language=None, dst_lang=None, client_uid=None):
|
| 29 |
print('>>>>>>>>>>>>>>>> init service >>>>>>>>>>>>>>>>>>>>>>')
|
|
|
|
| 33 |
self.client_uid = client_uid
|
| 34 |
# 转录结果稳定性管理
|
| 35 |
self.websocket = websocket
|
| 36 |
+
self.translate_pipe = pipe
|
|
|
|
| 37 |
# 音频处理相关
|
| 38 |
self.sample_rate = config.SAMPLE_RATE
|
| 39 |
|
| 40 |
self.lock = threading.Lock()
|
| 41 |
# 文本分隔符,根据语言设置
|
| 42 |
+
self.text_separator = get_text_separator(language)
|
| 43 |
self.loop = asyncio.get_event_loop()
|
| 44 |
# 原始音频队列
|
| 45 |
+
self.frame_queue = queue.Queue()
|
| 46 |
# 音频队列缓冲区
|
| 47 |
self.frames_np = np.array([], dtype=np.float32)
|
| 48 |
+
# 音频开始的时间点 用于约束最小断句时间
|
| 49 |
self.frames_np_start_timestamp = None
|
| 50 |
# 完整音频队列
|
| 51 |
self.full_segments_queue = collections.deque()
|
| 52 |
# 启动处理线程
|
| 53 |
+
self._stop = threading.Event()
|
| 54 |
+
self.translate_thread = start_thread(self._transcription_processing_loop)
|
| 55 |
+
self.frame_processing_thread = start_thread(self._read_frame_processing_loop)
|
| 56 |
+
# 行号
|
|
|
|
| 57 |
self.row_number = 0
|
|
|
|
|
|
|
|
|
|
| 58 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
|
| 60 |
def add_frames(self, frame_np: np.ndarray) -> None:
|
| 61 |
"""添加音频帧到处理队列"""
|
| 62 |
+
self.frame_queue.put(frame_np)
|
| 63 |
|
| 64 |
def _apply_voice_activity_detection(self, frame_np:np.array):
|
| 65 |
"""应用语音活动检测来优化音频缓冲区"""
|
| 66 |
+
processed_audio = self.translate_pipe.voice_detect(frame_np.tobytes())
|
| 67 |
speech_audio = np.frombuffer(processed_audio.audio, dtype=np.float32)
|
| 68 |
speech_status = processed_audio.speech_status
|
| 69 |
return speech_audio, speech_status
|
|
|
|
| 71 |
|
| 72 |
def _read_frame_processing_loop(self) -> None:
|
| 73 |
"""从队列获取音频帧并合并到缓冲区"""
|
| 74 |
+
while not self._stop.is_set():
|
| 75 |
try:
|
| 76 |
+
frame_np = self.frame_queue.get(timeout=0.1)
|
| 77 |
frame_np, speech_status = self._apply_voice_activity_detection(frame_np)
|
| 78 |
|
| 79 |
if frame_np is None:
|
|
|
|
| 92 |
self.full_segments_queue.appendleft(audio_array) # 根据时间是否满足三秒长度 来整合音频块
|
| 93 |
self.frames_np_start_timestamp = time.time()
|
| 94 |
self.frames_np = np.array([], dtype=np.float32)
|
| 95 |
+
|
| 96 |
# 音频结束信号的时候 整合当前缓冲区
|
| 97 |
# START -- END -- START -- END 通常
|
| 98 |
# START -- END -- END end块带有音频信息的通常是4096内断的一个短音
|
|
|
|
| 105 |
self.frames_np = np.array([], dtype=np.float32)
|
| 106 |
else:
|
| 107 |
logger.debug(f"🥳 当前时间与上一句的时间差: {time_diff:.2f}s,继续保留在缓冲区")
|
| 108 |
+
|
| 109 |
except queue.Empty:
|
| 110 |
pass
|
| 111 |
|
|
|
|
| 113 |
"""主转录处理循环"""
|
| 114 |
frame_epoch = 1
|
| 115 |
|
| 116 |
+
while not self._stop.is_set():
|
| 117 |
+
|
| 118 |
if len(self.frames_np) ==0:
|
| 119 |
time.sleep(0.1)
|
| 120 |
continue
|
|
|
|
| 126 |
else:
|
| 127 |
audio_buffer = self.frames_np[:int(frame_epoch * 1.5 * self.sample_rate)].copy()# 获取 1.5s * epoch 个音频长度
|
| 128 |
partial = True
|
| 129 |
+
|
| 130 |
if len(audio_buffer) < int(self.sample_rate):
|
| 131 |
+
# Add a small buffer (e.g., 10ms worth of samples) to be safe
|
| 132 |
+
padding_samples = int(self.sample_rate * 0.01) # e.g., 160 samples for 10ms at 16kHz
|
| 133 |
+
target_length = self.sample_rate + padding_samples
|
| 134 |
+
silence_audio = np.zeros(target_length, dtype=np.float32)
|
| 135 |
+
# Ensure we don't try to copy more data than exists if audio_buffer is very short
|
| 136 |
+
copy_length = min(len(audio_buffer), target_length)
|
| 137 |
+
silence_audio[-copy_length:] = audio_buffer[-copy_length:] # Copy from the end of audio_buffer
|
| 138 |
audio_buffer = silence_audio
|
| 139 |
+
elif len(audio_buffer) > self.sample_rate * config.MAX_SPEECH_DURATION_S:
|
| 140 |
+
# If buffer is too long even without padding, truncate it (optional, depends on desired behavior)
|
| 141 |
+
# This case might already be handled elsewhere, but good to consider
|
| 142 |
+
audio_buffer = audio_buffer[:int(self.sample_rate * config.MAX_SPEECH_DURATION_S)]
|
| 143 |
+
|
| 144 |
|
| 145 |
logger.debug(f"audio buffer size: {len(audio_buffer) / self.sample_rate:.2f}s")
|
| 146 |
meta_item = self._transcribe_audio(audio_buffer)
|
|
|
|
| 150 |
|
| 151 |
if len(segments):
|
| 152 |
seg_text = self.text_separator.join(seg.text for seg in segments)
|
| 153 |
+
if not seg_text.strip(): # 过滤空字符
|
| 154 |
+
continue
|
| 155 |
# 整行
|
| 156 |
if not partial:
|
| 157 |
translated_content = self._translate_text_large(seg_text)
|
|
|
|
| 170 |
partial=partial
|
| 171 |
)
|
| 172 |
self._send_result_to_client(result)
|
|
|
|
|
|
|
| 173 |
|
| 174 |
def _transcribe_audio(self, audio_buffer: np.ndarray)->MetaItem:
|
| 175 |
"""转录音频并返回转录片段"""
|
| 176 |
log_block("Audio buffer length", f"{audio_buffer.shape[0]/self.sample_rate:.2f}", "s")
|
| 177 |
+
result = self.translate_pipe.transcribe(audio_buffer.tobytes(), self.source_language)
|
| 178 |
+
log_block("📝 transcribe output", f"{self.text_separator.join(seg.text for seg in result.segments)}", "")
|
| 179 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 180 |
return result
|
| 181 |
|
| 182 |
def _translate_text(self, text: str) -> str:
|
| 183 |
"""将文本翻译为目标语言"""
|
| 184 |
if not text.strip():
|
| 185 |
return ""
|
|
|
|
| 186 |
log_block("🐧 Translation input ", f"{text}")
|
|
|
|
| 187 |
|
| 188 |
+
result = self.translate_pipe.translate(text, self.source_language, self.target_language)
|
| 189 |
translated_text = result.translate_content
|
| 190 |
+
|
|
|
|
| 191 |
log_block("🐧 Translation out ", f"{translated_text}")
|
|
|
|
| 192 |
return translated_text
|
| 193 |
|
| 194 |
def _translate_text_large(self, text: str) -> str:
|
| 195 |
"""将文本翻译为目标语言"""
|
| 196 |
if not text.strip():
|
| 197 |
return ""
|
|
|
|
| 198 |
log_block("Translation input", f"{text}")
|
| 199 |
+
result = self.translate_pipe.translate_large(text, self.source_language, self.target_language)
|
|
|
|
|
|
|
| 200 |
translated_text = result.translate_content
|
|
|
|
|
|
|
|
|
|
| 201 |
log_block("Translation large model output", f"{translated_text}")
|
|
|
|
| 202 |
return translated_text
|
| 203 |
|
| 204 |
|
|
|
|
| 216 |
|
| 217 |
def stop(self) -> None:
|
| 218 |
"""停止所有处理线程并清理资源"""
|
| 219 |
+
self._stop.set()
|
|
|
|
|
|
|
|
|
|
| 220 |
logger.info(f"Stopping transcription service for client: {self.client_uid}")
|
transcribe/translatepipes.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
-
from
|
| 2 |
-
|
| 3 |
|
| 4 |
class TranslatePipes:
|
| 5 |
def __init__(self) -> None:
|
|
@@ -26,6 +26,7 @@ class TranslatePipes:
|
|
| 26 |
for p in self._process:
|
| 27 |
p.wait()
|
| 28 |
|
|
|
|
| 29 |
def translate(self, text, src_lang, dst_lang) -> MetaItem:
|
| 30 |
item = MetaItem(
|
| 31 |
transcribe_content=text,
|
|
@@ -34,6 +35,7 @@ class TranslatePipes:
|
|
| 34 |
self._translate_pipe.input_queue.put(item)
|
| 35 |
return self._translate_pipe.output_queue.get()
|
| 36 |
|
|
|
|
| 37 |
def translate_large(self, text, src_lang, dst_lang) -> MetaItem:
|
| 38 |
item = MetaItem(
|
| 39 |
transcribe_content=text,
|
|
@@ -47,6 +49,7 @@ class TranslatePipes:
|
|
| 47 |
return self._funasr_pipe
|
| 48 |
return self._whisper_pipe_en
|
| 49 |
|
|
|
|
| 50 |
def transcribe(self, audio_buffer: bytes, src_lang: str) -> MetaItem:
|
| 51 |
transcription_model = self.get_transcription_model(src_lang)
|
| 52 |
item = MetaItem(audio=audio_buffer, source_language=src_lang)
|
|
|
|
| 1 |
+
from .pipelines import WhisperPipe, MetaItem, WhisperChinese, Translate7BPipe, FunASRPipe, VadPipe, TranslatePipe
|
| 2 |
+
from .utils import timer
|
| 3 |
|
| 4 |
class TranslatePipes:
|
| 5 |
def __init__(self) -> None:
|
|
|
|
| 26 |
for p in self._process:
|
| 27 |
p.wait()
|
| 28 |
|
| 29 |
+
@timer(name="🐧 Translate")
|
| 30 |
def translate(self, text, src_lang, dst_lang) -> MetaItem:
|
| 31 |
item = MetaItem(
|
| 32 |
transcribe_content=text,
|
|
|
|
| 35 |
self._translate_pipe.input_queue.put(item)
|
| 36 |
return self._translate_pipe.output_queue.get()
|
| 37 |
|
| 38 |
+
@timer(name="🐧 Translate-large")
|
| 39 |
def translate_large(self, text, src_lang, dst_lang) -> MetaItem:
|
| 40 |
item = MetaItem(
|
| 41 |
transcribe_content=text,
|
|
|
|
| 49 |
return self._funasr_pipe
|
| 50 |
return self._whisper_pipe_en
|
| 51 |
|
| 52 |
+
@timer(name="📝 transcribe")
|
| 53 |
def transcribe(self, audio_buffer: bytes, src_lang: str) -> MetaItem:
|
| 54 |
transcription_model = self.get_transcription_model(src_lang)
|
| 55 |
item = MetaItem(audio=audio_buffer, source_language=src_lang)
|
transcribe/utils.py
CHANGED
|
@@ -8,8 +8,9 @@ import config
|
|
| 8 |
import csv
|
| 9 |
import av
|
| 10 |
import re
|
| 11 |
-
import
|
| 12 |
-
|
|
|
|
| 13 |
# Compile regex patterns once outside the loop for better performance
|
| 14 |
p_pattern = re.compile(r"(\s*\[.*?\])")
|
| 15 |
p_start_pattern = re.compile(r"(\s*\[.*)")
|
|
@@ -178,6 +179,32 @@ def pcm_bytes_to_np_array(pcm_bytes: bytes, dtype=np.float32, channels=1):
|
|
| 178 |
audio_np = audio_np.reshape(-1, channels)
|
| 179 |
return audio_np
|
| 180 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
class TestDataWriter:
|
| 182 |
def __init__(self, file_path='test_data.csv'):
|
| 183 |
self.file_path = file_path
|
|
|
|
| 8 |
import csv
|
| 9 |
import av
|
| 10 |
import re
|
| 11 |
+
from functools import wraps
|
| 12 |
+
import time
|
| 13 |
+
import threading
|
| 14 |
# Compile regex patterns once outside the loop for better performance
|
| 15 |
p_pattern = re.compile(r"(\s*\[.*?\])")
|
| 16 |
p_start_pattern = re.compile(r"(\s*\[.*)")
|
|
|
|
| 179 |
audio_np = audio_np.reshape(-1, channels)
|
| 180 |
return audio_np
|
| 181 |
|
| 182 |
+
def timer(name: str):
|
| 183 |
+
def decorator(func):
|
| 184 |
+
@wraps(func)
|
| 185 |
+
def wrapper(*args, **kwargs):
|
| 186 |
+
start_time = time.perf_counter()
|
| 187 |
+
result = func(*args, **kwargs)
|
| 188 |
+
end_time = time.perf_counter()
|
| 189 |
+
duration = end_time - start_time
|
| 190 |
+
log_block(f"{name} cost:", f"{duration:.2f} s")
|
| 191 |
+
return result
|
| 192 |
+
return wrapper
|
| 193 |
+
return decorator
|
| 194 |
+
|
| 195 |
+
def get_text_separator(language: str) -> str:
|
| 196 |
+
"""根据语言返回适当的文本分隔符"""
|
| 197 |
+
return "" if language == "zh" else " "
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
def start_thread(target_function) -> threading.Thread:
|
| 201 |
+
"""启动守护线程执行指定函数"""
|
| 202 |
+
thread = threading.Thread(target=target_function)
|
| 203 |
+
thread.daemon = True
|
| 204 |
+
thread.start()
|
| 205 |
+
return thread
|
| 206 |
+
|
| 207 |
+
|
| 208 |
class TestDataWriter:
|
| 209 |
def __init__(self, file_path='test_data.csv'):
|
| 210 |
self.file_path = file_path
|