(data: Record<string, unknown>)
| 123 | } |
| 124 | |
| 125 | function parseWhisperCpp(data: Record<string, unknown>): Word[] { |
| 126 | const words: Word[] = []; |
| 127 | const transcription = data.transcription as Array<{ |
| 128 | tokens?: Array<{ |
| 129 | text?: string; |
| 130 | offsets?: { from?: number; to?: number }; |
| 131 | }>; |
| 132 | }>; |
| 133 | |
| 134 | for (const seg of transcription ?? []) { |
| 135 | for (const token of seg.tokens ?? []) { |
| 136 | const rawText = token.text ?? ""; |
| 137 | const text = rawText.trim(); |
| 138 | if (!text || text.startsWith("[_") || text.startsWith("[BLANK")) continue; |
| 139 | |
| 140 | const lastWord = words[words.length - 1]; |
| 141 | |
| 142 | // Merge into previous word when the token is a sub-word continuation, |
| 143 | // trailing punctuation, or a contraction suffix. |
| 144 | // Whisper uses leading spaces to mark word boundaries in all languages. |
| 145 | const shouldMerge = |
| 146 | lastWord && |
| 147 | (!rawText.startsWith(" ") || |
| 148 | /^[.,!?;:'")\]}>…–—¡¿-]+$/.test(text) || |
| 149 | /^'(t|m|s|ve|re|ll|d)$/i.test(text)); |
| 150 | if (shouldMerge) { |
| 151 | lastWord.text += text; |
| 152 | lastWord.end = round3((token.offsets?.to ?? 0) / 1000); |
| 153 | continue; |
| 154 | } |
| 155 | |
| 156 | words.push({ |
| 157 | text, |
| 158 | start: round3((token.offsets?.from ?? 0) / 1000), |
| 159 | end: round3((token.offsets?.to ?? 0) / 1000), |
| 160 | }); |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | mergeFragments(words); |
| 165 | interpolateZeroDuration(words); |
| 166 | |
| 167 | return words; |
| 168 | } |
| 169 | |
| 170 | function parseOpenAI(data: Record<string, unknown>): Word[] { |
| 171 | const words = (data.words ?? []) as Array<{ |
no test coverage detected