Xin Zhang commited on
Commit
ea81a46
·
1 Parent(s): c055a7b

[feature]: update web portal.

Browse files
config.py CHANGED
@@ -23,7 +23,7 @@ WHISPER_PROMPT_ZH = "以下是简体中文普通话的句子。"
23
  MAX_LENTH_ZH = 4
24
 
25
  WHISPER_PROMPT_EN = "The following is an English sentence."
26
- MAX_LENGTH_EN= 1
27
 
28
  WHISPER_MODEL = 'medium-q5_0'
29
 
 
23
  MAX_LENTH_ZH = 4
24
 
25
  WHISPER_PROMPT_EN = "The following is an English sentence."
26
+ MAX_LENGTH_EN= 3
27
 
28
  WHISPER_MODEL = 'medium-q5_0'
29
 
frontend/assets/index-a48dc1a0.css ADDED
@@ -0,0 +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}.right[data-v-f1347e90]{position:fixed;bottom:0;width:100%;height:40px;line-height:40px;text-align:center;font-size:.8em;background:white}.content[data-v-6ff6f541]{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)}.view-wrapper[data-v-3036387c]{width:100%;height:100%;background-color:#fff}.view-wrapper .content-wrapper[data-v-3036387c]{text-align:left;width:1280px;max-width:100vw;min-width:320px;margin-bottom:64px;min-height:calc(100vh - 438px)}.view-wrapper .content-wrapper .chat-box[data-v-3036387c]{width:100%;height:54vh;border-radius:4px;padding:12px;color:#2e2f33;font-size:18px}.view-wrapper .content-wrapper .chat-box-placeholder[data-v-3036387c]{width:100%;height:58vh;border-radius:4px;padding:12px;font-size:18px;color:#a4a6ac}.view-wrapper .content-wrapper .actions-box[data-v-3036387c]{display:flex;align-items:center;justify-content:space-between;margin:0 24px;height:48px}.view-wrapper .content-wrapper .trans-list[data-v-3036387c]{overflow-y:auto;width:100%;height:58vh;scrollbar-width:none;-ms-overflow-style:none}.view-wrapper .content-wrapper .trans-list[data-v-3036387c]::-webkit-scrollbar{display:none}.view-wrapper .content-wrapper .trans-list .node[data-v-3036387c]{margin-bottom:36px;width:100%!important;transition:all .3s ease}.view-wrapper .content-wrapper .trans-list .node .trans-time[data-v-3036387c]{font-size:14px;color:#c4c6cc}.view-wrapper .content-wrapper .trans-list .node .trans-src-lang[data-v-3036387c]{font-size:17px;color:#909299}.view-wrapper .content-wrapper .trans-list .node .trans-dst-lang[data-v-3036387c]{font-size:18px;color:#2e2f33;font-weight:600}.view-wrapper .content-wrapper .trans-list .current_node[data-v-3036387c]{background-color:#f0f1f7;padding:4px 8px}@keyframes highlight-3036387c{0%{background-color:transparent}50%{background-color:#fff1ce80}to{background-color:transparent}}@keyframes slideIn-3036387c{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/assets/index-d7a7d66c.js ADDED
The diff for this file is too large to render. See raw diff
 
frontend/favicon.ico ADDED
frontend/index.html CHANGED
@@ -1,169 +1,15 @@
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:9090?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>
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-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-d7a7d66c.js"></script>
9
+ <link rel="stylesheet" href="./assets/index-a48dc1a0.css">
10
+ </head>
11
+ <body>
12
+ <div id="app"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
+ </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  </html>
frontend/index2.html ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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:9090?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>
transcribe/strategy.py CHANGED
@@ -118,6 +118,7 @@ def segments_split(segments, audio_buffer: np.ndarray, sample_rate=16000):
118
  is_end = False
119
 
120
  for idx, seg in enumerate(segments):
 
121
  left_watch_sequences.append(seg)
122
  if seg.text and seg.text[-1] in markers:
123
  seg_index = int(seg.t1 / 100 * sample_rate)
 
118
  is_end = False
119
 
120
  for idx, seg in enumerate(segments):
121
+ # print('>>>>>>>>>>>>>>>> seg : ', seg)
122
  left_watch_sequences.append(seg)
123
  if seg.text and seg.text[-1] in markers:
124
  seg_index = int(seg.t1 / 100 * sample_rate)