ruiyun-human 1.0.5 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -554,6 +554,7 @@ const _sfc_main = {
554
554
  runAnimation("speak");
555
555
  };
556
556
  const stopSpeaking = () => {
557
+ abortTTS();
557
558
  if (animationState.value === "speak" || animationState.value === "speakLoop") {
558
559
  runAnimation("speakEnd");
559
560
  } else {
@@ -629,13 +630,44 @@ const _sfc_main = {
629
630
  };
630
631
  const currentAudio = vue.ref(null);
631
632
  const currentTtsRequestId = vue.ref(null);
632
- const playTTS = async (text) => {
633
- var _a, _b, _c, _d, _e, _f, _g, _h;
634
- if (!text || !((_a = currentHuman.value) == null ? void 0 : _a.isMuted)) return;
633
+ let ttsAbortController = null;
634
+ function abortTTS() {
635
+ currentTtsRequestId.value = null;
636
+ if (ttsAbortController) {
637
+ try {
638
+ ttsAbortController.abort();
639
+ } catch {
640
+ }
641
+ ttsAbortController = null;
642
+ }
635
643
  if (currentAudio.value) {
636
- currentAudio.value.pause();
644
+ try {
645
+ currentAudio.value.pause();
646
+ } catch {
647
+ }
637
648
  currentAudio.value = null;
638
649
  }
650
+ }
651
+ function waitWithSignal(ms, signal) {
652
+ return new Promise((resolve, reject) => {
653
+ if (signal == null ? void 0 : signal.aborted) {
654
+ reject(new DOMException("Aborted", "AbortError"));
655
+ return;
656
+ }
657
+ const timer = setTimeout(resolve, ms);
658
+ const onAbort = () => {
659
+ clearTimeout(timer);
660
+ reject(new DOMException("Aborted", "AbortError"));
661
+ };
662
+ signal == null ? void 0 : signal.addEventListener("abort", onAbort, { once: true });
663
+ });
664
+ }
665
+ const playTTS = async (text) => {
666
+ var _a, _b, _c, _d, _e, _f, _g, _h;
667
+ if (!text || !((_a = currentHuman.value) == null ? void 0 : _a.isMuted)) return;
668
+ abortTTS();
669
+ ttsAbortController = new AbortController();
670
+ const { signal } = ttsAbortController;
639
671
  currentTtsRequestId.value = Date.now();
640
672
  const requestId = currentTtsRequestId.value;
641
673
  try {
@@ -651,7 +683,8 @@ const _sfc_main = {
651
683
  const joinRes = await fetch(`${baseUrl}/gradio_api/queue/join`, {
652
684
  method: "POST",
653
685
  headers: { "Content-Type": "application/json" },
654
- body: JSON.stringify({ data, session_hash: sessionHash, fn_index: 1, trigger_id: 50 })
686
+ body: JSON.stringify({ data, session_hash: sessionHash, fn_index: 1, trigger_id: 50 }),
687
+ signal
655
688
  });
656
689
  const joinData = await joinRes.json();
657
690
  console.log("[OmniVoice] Step1 响应:", joinData);
@@ -660,10 +693,13 @@ const _sfc_main = {
660
693
  let maxPolls = 60;
661
694
  let pollInterval = 1e3;
662
695
  while (!audioUrl && maxPolls > 0) {
663
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
696
+ if (signal.aborted || currentTtsRequestId.value !== requestId) return;
697
+ await waitWithSignal(pollInterval, signal);
664
698
  maxPolls--;
665
699
  try {
666
- const pollRes = await fetch(`${baseUrl}/gradio_api/queue/data?session_hash=${sessionHash}`);
700
+ const pollRes = await fetch(`${baseUrl}/gradio_api/queue/data?session_hash=${sessionHash}`, {
701
+ signal
702
+ });
667
703
  const pollText = await pollRes.text();
668
704
  const lines = pollText.split("\n");
669
705
  for (const line of lines) {
@@ -690,7 +726,7 @@ const _sfc_main = {
690
726
  }
691
727
  const wavBlob = createWavBlob(audioBuffer, samplingRate, 1, 16);
692
728
  const audioObjectUrl = URL.createObjectURL(wavBlob);
693
- if (currentTtsRequestId.value !== requestId) {
729
+ if (signal.aborted || currentTtsRequestId.value !== requestId) {
694
730
  URL.revokeObjectURL(audioObjectUrl);
695
731
  return;
696
732
  }
@@ -726,16 +762,21 @@ const _sfc_main = {
726
762
  }
727
763
  }
728
764
  } catch (e) {
765
+ if ((e == null ? void 0 : e.name) === "AbortError") throw e;
729
766
  console.warn("[OmniVoice] 轮询失败:", e.message);
730
767
  }
731
768
  }
732
769
  if (audioUrl) {
733
770
  if (audioUrl.startsWith("/")) audioUrl = baseUrl + audioUrl;
734
771
  console.log("[OmniVoice] 下载音频:", audioUrl);
735
- if (currentTtsRequestId.value !== requestId) return;
736
- const audioRes = await fetch(audioUrl);
772
+ if (signal.aborted || currentTtsRequestId.value !== requestId) return;
773
+ const audioRes = await fetch(audioUrl, { signal });
737
774
  const audioBlob = await audioRes.blob();
738
775
  const audioObjectUrl = URL.createObjectURL(audioBlob);
776
+ if (signal.aborted || currentTtsRequestId.value !== requestId) {
777
+ URL.revokeObjectURL(audioObjectUrl);
778
+ return;
779
+ }
739
780
  currentAudio.value = new Audio(audioObjectUrl);
740
781
  currentAudio.value.onplaying = () => {
741
782
  console.log("[OmniVoice] 音频开始播放");
@@ -759,6 +800,10 @@ const _sfc_main = {
759
800
  standby();
760
801
  }
761
802
  } catch (e) {
803
+ if ((e == null ? void 0 : e.name) === "AbortError") {
804
+ console.log("[OmniVoice] TTS已取消");
805
+ return;
806
+ }
762
807
  console.error("[OmniVoice] TTS失败:", e);
763
808
  standby();
764
809
  }
@@ -870,6 +915,7 @@ const _sfc_main = {
870
915
  init();
871
916
  });
872
917
  vue.onUnmounted(() => {
918
+ abortTTS();
873
919
  stop();
874
920
  if (recorder2.value) {
875
921
  recorder2.value.stop();
@@ -889,7 +935,7 @@ const _sfc_main = {
889
935
  };
890
936
  }
891
937
  };
892
- const DigitalHuman = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-b3682025"]]);
938
+ const DigitalHuman = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-90fb7a29"]]);
893
939
  DigitalHuman.install = (app) => app.component("DigitalHuman", DigitalHuman);
894
940
  const components = [DemoButton, DemoInput, DigitalHuman];
895
941
  const install = (app) => {
@@ -552,6 +552,7 @@ const _sfc_main = {
552
552
  runAnimation("speak");
553
553
  };
554
554
  const stopSpeaking = () => {
555
+ abortTTS();
555
556
  if (animationState.value === "speak" || animationState.value === "speakLoop") {
556
557
  runAnimation("speakEnd");
557
558
  } else {
@@ -627,13 +628,44 @@ const _sfc_main = {
627
628
  };
628
629
  const currentAudio = ref(null);
629
630
  const currentTtsRequestId = ref(null);
630
- const playTTS = async (text) => {
631
- var _a, _b, _c, _d, _e, _f, _g, _h;
632
- if (!text || !((_a = currentHuman.value) == null ? void 0 : _a.isMuted)) return;
631
+ let ttsAbortController = null;
632
+ function abortTTS() {
633
+ currentTtsRequestId.value = null;
634
+ if (ttsAbortController) {
635
+ try {
636
+ ttsAbortController.abort();
637
+ } catch {
638
+ }
639
+ ttsAbortController = null;
640
+ }
633
641
  if (currentAudio.value) {
634
- currentAudio.value.pause();
642
+ try {
643
+ currentAudio.value.pause();
644
+ } catch {
645
+ }
635
646
  currentAudio.value = null;
636
647
  }
648
+ }
649
+ function waitWithSignal(ms, signal) {
650
+ return new Promise((resolve, reject) => {
651
+ if (signal == null ? void 0 : signal.aborted) {
652
+ reject(new DOMException("Aborted", "AbortError"));
653
+ return;
654
+ }
655
+ const timer = setTimeout(resolve, ms);
656
+ const onAbort = () => {
657
+ clearTimeout(timer);
658
+ reject(new DOMException("Aborted", "AbortError"));
659
+ };
660
+ signal == null ? void 0 : signal.addEventListener("abort", onAbort, { once: true });
661
+ });
662
+ }
663
+ const playTTS = async (text) => {
664
+ var _a, _b, _c, _d, _e, _f, _g, _h;
665
+ if (!text || !((_a = currentHuman.value) == null ? void 0 : _a.isMuted)) return;
666
+ abortTTS();
667
+ ttsAbortController = new AbortController();
668
+ const { signal } = ttsAbortController;
637
669
  currentTtsRequestId.value = Date.now();
638
670
  const requestId = currentTtsRequestId.value;
639
671
  try {
@@ -649,7 +681,8 @@ const _sfc_main = {
649
681
  const joinRes = await fetch(`${baseUrl}/gradio_api/queue/join`, {
650
682
  method: "POST",
651
683
  headers: { "Content-Type": "application/json" },
652
- body: JSON.stringify({ data, session_hash: sessionHash, fn_index: 1, trigger_id: 50 })
684
+ body: JSON.stringify({ data, session_hash: sessionHash, fn_index: 1, trigger_id: 50 }),
685
+ signal
653
686
  });
654
687
  const joinData = await joinRes.json();
655
688
  console.log("[OmniVoice] Step1 响应:", joinData);
@@ -658,10 +691,13 @@ const _sfc_main = {
658
691
  let maxPolls = 60;
659
692
  let pollInterval = 1e3;
660
693
  while (!audioUrl && maxPolls > 0) {
661
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
694
+ if (signal.aborted || currentTtsRequestId.value !== requestId) return;
695
+ await waitWithSignal(pollInterval, signal);
662
696
  maxPolls--;
663
697
  try {
664
- const pollRes = await fetch(`${baseUrl}/gradio_api/queue/data?session_hash=${sessionHash}`);
698
+ const pollRes = await fetch(`${baseUrl}/gradio_api/queue/data?session_hash=${sessionHash}`, {
699
+ signal
700
+ });
665
701
  const pollText = await pollRes.text();
666
702
  const lines = pollText.split("\n");
667
703
  for (const line of lines) {
@@ -688,7 +724,7 @@ const _sfc_main = {
688
724
  }
689
725
  const wavBlob = createWavBlob(audioBuffer, samplingRate, 1, 16);
690
726
  const audioObjectUrl = URL.createObjectURL(wavBlob);
691
- if (currentTtsRequestId.value !== requestId) {
727
+ if (signal.aborted || currentTtsRequestId.value !== requestId) {
692
728
  URL.revokeObjectURL(audioObjectUrl);
693
729
  return;
694
730
  }
@@ -724,16 +760,21 @@ const _sfc_main = {
724
760
  }
725
761
  }
726
762
  } catch (e) {
763
+ if ((e == null ? void 0 : e.name) === "AbortError") throw e;
727
764
  console.warn("[OmniVoice] 轮询失败:", e.message);
728
765
  }
729
766
  }
730
767
  if (audioUrl) {
731
768
  if (audioUrl.startsWith("/")) audioUrl = baseUrl + audioUrl;
732
769
  console.log("[OmniVoice] 下载音频:", audioUrl);
733
- if (currentTtsRequestId.value !== requestId) return;
734
- const audioRes = await fetch(audioUrl);
770
+ if (signal.aborted || currentTtsRequestId.value !== requestId) return;
771
+ const audioRes = await fetch(audioUrl, { signal });
735
772
  const audioBlob = await audioRes.blob();
736
773
  const audioObjectUrl = URL.createObjectURL(audioBlob);
774
+ if (signal.aborted || currentTtsRequestId.value !== requestId) {
775
+ URL.revokeObjectURL(audioObjectUrl);
776
+ return;
777
+ }
737
778
  currentAudio.value = new Audio(audioObjectUrl);
738
779
  currentAudio.value.onplaying = () => {
739
780
  console.log("[OmniVoice] 音频开始播放");
@@ -757,6 +798,10 @@ const _sfc_main = {
757
798
  standby();
758
799
  }
759
800
  } catch (e) {
801
+ if ((e == null ? void 0 : e.name) === "AbortError") {
802
+ console.log("[OmniVoice] TTS已取消");
803
+ return;
804
+ }
760
805
  console.error("[OmniVoice] TTS失败:", e);
761
806
  standby();
762
807
  }
@@ -868,6 +913,7 @@ const _sfc_main = {
868
913
  init();
869
914
  });
870
915
  onUnmounted(() => {
916
+ abortTTS();
871
917
  stop();
872
918
  if (recorder2.value) {
873
919
  recorder2.value.stop();
@@ -887,7 +933,7 @@ const _sfc_main = {
887
933
  };
888
934
  }
889
935
  };
890
- const DigitalHuman = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-b3682025"]]);
936
+ const DigitalHuman = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-90fb7a29"]]);
891
937
  DigitalHuman.install = (app) => app.component("DigitalHuman", DigitalHuman);
892
938
  const components = [DemoButton, DemoInput, DigitalHuman];
893
939
  const install = (app) => {
package/dist/style.css CHANGED
@@ -1 +1 @@
1
- .demo-btn[data-v-9a48516a]{padding:6px 14px;border-radius:4px;border:none;cursor:pointer;font-size:14px}.primary[data-v-9a48516a]{background:#409eff;color:#fff}.success[data-v-9a48516a]{background:#67c23a;color:#fff}.warning[data-v-9a48516a]{background:#e6a23c;color:#fff}.demo-input-wrap[data-v-28a3493c]{display:inline-flex;align-items:center;border:1px solid #dcdcdc;border-radius:4px;padding:0 10px}.demo-input-wrap[data-v-28a3493c]:focus-within{border-color:#409eff}.demo-input[data-v-28a3493c]{border:none;outline:none;padding:8px 6px;flex:1}.prefix[data-v-28a3493c],.suffix[data-v-28a3493c]{color:#909399}.digital-human[data-v-b3682025]{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.digital-human canvas[data-v-b3682025]{max-width:100%;max-height:100%}
1
+ .demo-btn[data-v-9a48516a]{padding:6px 14px;border-radius:4px;border:none;cursor:pointer;font-size:14px}.primary[data-v-9a48516a]{background:#409eff;color:#fff}.success[data-v-9a48516a]{background:#67c23a;color:#fff}.warning[data-v-9a48516a]{background:#e6a23c;color:#fff}.demo-input-wrap[data-v-28a3493c]{display:inline-flex;align-items:center;border:1px solid #dcdcdc;border-radius:4px;padding:0 10px}.demo-input-wrap[data-v-28a3493c]:focus-within{border-color:#409eff}.demo-input[data-v-28a3493c]{border:none;outline:none;padding:8px 6px;flex:1}.prefix[data-v-28a3493c],.suffix[data-v-28a3493c]{color:#909399}.digital-human[data-v-90fb7a29]{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.digital-human canvas[data-v-90fb7a29]{max-width:100%;max-height:100%}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ruiyun-human",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "type": "module",
5
5
  "description": "数字人初步实现",
6
6
  "main": "./dist/ruiyun-human.cjs.js",