This commit is contained in:
yumoqing 2025-05-25 15:25:20 +00:00
parent 70ce70dbd6
commit 7099adf136
2 changed files with 98 additions and 23 deletions

View File

@ -28,13 +28,16 @@ bricks.AudioPlayer = class extends bricks.JsWidget {
if (this.opts.autoplay){
this.audio.addEventListener('canplay', this.play.bind(this));
}
this.audio.addEventListener('ended', this.playended.bind(this));
this.audio.style.width = "100%"
this.dom_element.appendChild(this.audio);
if ( this.url ){
this.set_source(this.url);
}
}
playended(e){
this.dispatch('ended');
}
set_stream_urls(response){
async function* dyn_urls(response) {
const reader = response.body.getReader();
@ -56,6 +59,7 @@ bricks.AudioPlayer = class extends bricks.JsWidget {
}
this.url_generator = dyn_urls(response);
this.srcList = [];
this.notBegin = true;
schedule_once(this.load_queue_url.bind(this), 0.1);
}
async load_queue_url(){
@ -93,7 +97,7 @@ bricks.AudioPlayer = class extends bricks.JsWidget {
this.audio.appendChild(this.source);
}
this.url = this.audio.src = url;
bricks.debug(this.audio.src,' new src seted');
console.log(this.audio.src,' new src seted');
}
set_source_from_response(resp){
// 将服务器返回的数据设置为音频源
@ -113,6 +117,7 @@ bricks.AudioPlayer = class extends bricks.JsWidget {
}
set_url(url){
this.set_source(url);
this.audio.play();
}
}
@ -296,35 +301,58 @@ bricks.AudioRecorder = class extends bricks.HBox {
}
}
bricks.SttClient = class extends bricks.VBox {
/*
{
"upload_url"
}
*/
bricks.TextedAudioPlayer = class extends bricks.VBox {
constructor(opts){
super(opts);
this.recorder = new bricks.AudioRecorder({
icon_rate:2,
upload_url:opts.upload_url,
upload_resp:'json'
this.audio = new bricks.AudioPlayer({
height:'60px'
});
this.add_widget(this.recorder);
this.result_text = new bricks.Text({
text:'',
dynsize:true
this.audio.bind('ended', this.playnext.bind(this));
var filler, panel;
filler = new bricks.Filler({});
this.add_widget(this.audio);
this.add_widget(filler);
panel = new bricks.VScrollPanel({
height: '100%'
});
this.add_widget(this.result_text);
this.recorder.bind('uploaded', this.set_result_text.bind(this));
filler.add_widget(panel);
this.textw = new bricks.Text({
text: ' ',
wrap: true
});
panel.add_widget(this.textw);
this.streaming_buffer = [];
this.wait_play = true;
}
set_result_text(event){
var txt = event.params.text;
this.result_text.set_text(txt);
async playnext(){
var json = this.streaming_buffer.shift();
console.log('======', json);
if (json && ! json.done){
var url = base64_to_url(json.audio);
this.audio.set_url(url);
this.wait_play = false;
this.textw.set_text(json.text);
await this.audio.play();
} else {
this.textw.set_text('');
this.audio.set_url(null);
this.wait_play = true;
}
}
async set_stream_urls(response){
this.wait_play = true;
await streamResponseJson(response, this.load_stream_data.bind(this));
}
async load_stream_data(json){
console.log('***', json);
this.streaming_buffer.push(json);
if (this.wait_play) {
await this.playnext();
}
}
}
bricks.Factory.register('AudioPlayer', bricks.AudioPlayer);
bricks.Factory.register('AudioRecorder', bricks.AudioRecorder);
bricks.Factory.register('SttClient', bricks.SttClient);
bricks.Factory.register('TextedAudioPlayer', bricks.TextedAudioPlayer);

View File

@ -1,6 +1,53 @@
var bricks = window.bricks || {};
bricks.bug = false;
async function streamResponseJson(response, onJson) {
const reader = response.body.getReader();
const decoder = new TextDecoder("utf-8");
let buffer = "";
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// 按换行切分 NDJSON
let lines = buffer.split("\n");
buffer = lines.pop(); // 可能是不完整的一行,留到下一轮
for (const line of lines) {
if (line.trim()) {
try {
const json = JSON.parse(line);
onJson(json); // 👈 回调处理每个 JSON 对象
} catch (err) {
console.warn("Failed to parse JSON line:", line);
}
}
}
}
// 处理最后残留的一行
if (buffer.trim()) {
try {
const json = JSON.parse(buffer);
onJson(json);
} catch (err) {
console.warn("Failed to parse trailing JSON line:", buffer);
}
}
}
function base64_to_url(base64, mimeType = "audio/wav") {
const binary = atob(base64); // 解码 Base64 成 binary 字符串
const len = binary.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binary.charCodeAt(i);
}
const blob = new Blob([bytes], { type: mimeType });
const url = URL.createObjectURL(blob);
return url;
}
function blobToBase64(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();