ruiyun-human 1.0.5 → 1.0.7

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 {
@@ -561,6 +562,7 @@ const _sfc_main = {
561
562
  }
562
563
  };
563
564
  const pause = () => {
565
+ stopSpeaking();
564
566
  if (isPaused.value) return;
565
567
  isPaused.value = true;
566
568
  pauseSnapshot.value = {
@@ -629,13 +631,44 @@ const _sfc_main = {
629
631
  };
630
632
  const currentAudio = vue.ref(null);
631
633
  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;
634
+ let ttsAbortController = null;
635
+ function abortTTS() {
636
+ currentTtsRequestId.value = null;
637
+ if (ttsAbortController) {
638
+ try {
639
+ ttsAbortController.abort();
640
+ } catch {
641
+ }
642
+ ttsAbortController = null;
643
+ }
635
644
  if (currentAudio.value) {
636
- currentAudio.value.pause();
645
+ try {
646
+ currentAudio.value.pause();
647
+ } catch {
648
+ }
637
649
  currentAudio.value = null;
638
650
  }
651
+ }
652
+ function waitWithSignal(ms, signal) {
653
+ return new Promise((resolve, reject) => {
654
+ if (signal == null ? void 0 : signal.aborted) {
655
+ reject(new DOMException("Aborted", "AbortError"));
656
+ return;
657
+ }
658
+ const timer = setTimeout(resolve, ms);
659
+ const onAbort = () => {
660
+ clearTimeout(timer);
661
+ reject(new DOMException("Aborted", "AbortError"));
662
+ };
663
+ signal == null ? void 0 : signal.addEventListener("abort", onAbort, { once: true });
664
+ });
665
+ }
666
+ const playTTS = async (text) => {
667
+ var _a, _b, _c, _d, _e, _f, _g, _h;
668
+ if (!text || !((_a = currentHuman.value) == null ? void 0 : _a.isMuted)) return;
669
+ abortTTS();
670
+ ttsAbortController = new AbortController();
671
+ const { signal } = ttsAbortController;
639
672
  currentTtsRequestId.value = Date.now();
640
673
  const requestId = currentTtsRequestId.value;
641
674
  try {
@@ -651,7 +684,8 @@ const _sfc_main = {
651
684
  const joinRes = await fetch(`${baseUrl}/gradio_api/queue/join`, {
652
685
  method: "POST",
653
686
  headers: { "Content-Type": "application/json" },
654
- body: JSON.stringify({ data, session_hash: sessionHash, fn_index: 1, trigger_id: 50 })
687
+ body: JSON.stringify({ data, session_hash: sessionHash, fn_index: 1, trigger_id: 50 }),
688
+ signal
655
689
  });
656
690
  const joinData = await joinRes.json();
657
691
  console.log("[OmniVoice] Step1 响应:", joinData);
@@ -660,10 +694,13 @@ const _sfc_main = {
660
694
  let maxPolls = 60;
661
695
  let pollInterval = 1e3;
662
696
  while (!audioUrl && maxPolls > 0) {
663
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
697
+ if (signal.aborted || currentTtsRequestId.value !== requestId) return;
698
+ await waitWithSignal(pollInterval, signal);
664
699
  maxPolls--;
665
700
  try {
666
- const pollRes = await fetch(`${baseUrl}/gradio_api/queue/data?session_hash=${sessionHash}`);
701
+ const pollRes = await fetch(`${baseUrl}/gradio_api/queue/data?session_hash=${sessionHash}`, {
702
+ signal
703
+ });
667
704
  const pollText = await pollRes.text();
668
705
  const lines = pollText.split("\n");
669
706
  for (const line of lines) {
@@ -690,7 +727,7 @@ const _sfc_main = {
690
727
  }
691
728
  const wavBlob = createWavBlob(audioBuffer, samplingRate, 1, 16);
692
729
  const audioObjectUrl = URL.createObjectURL(wavBlob);
693
- if (currentTtsRequestId.value !== requestId) {
730
+ if (signal.aborted || currentTtsRequestId.value !== requestId) {
694
731
  URL.revokeObjectURL(audioObjectUrl);
695
732
  return;
696
733
  }
@@ -726,16 +763,21 @@ const _sfc_main = {
726
763
  }
727
764
  }
728
765
  } catch (e) {
766
+ if ((e == null ? void 0 : e.name) === "AbortError") throw e;
729
767
  console.warn("[OmniVoice] 轮询失败:", e.message);
730
768
  }
731
769
  }
732
770
  if (audioUrl) {
733
771
  if (audioUrl.startsWith("/")) audioUrl = baseUrl + audioUrl;
734
772
  console.log("[OmniVoice] 下载音频:", audioUrl);
735
- if (currentTtsRequestId.value !== requestId) return;
736
- const audioRes = await fetch(audioUrl);
773
+ if (signal.aborted || currentTtsRequestId.value !== requestId) return;
774
+ const audioRes = await fetch(audioUrl, { signal });
737
775
  const audioBlob = await audioRes.blob();
738
776
  const audioObjectUrl = URL.createObjectURL(audioBlob);
777
+ if (signal.aborted || currentTtsRequestId.value !== requestId) {
778
+ URL.revokeObjectURL(audioObjectUrl);
779
+ return;
780
+ }
739
781
  currentAudio.value = new Audio(audioObjectUrl);
740
782
  currentAudio.value.onplaying = () => {
741
783
  console.log("[OmniVoice] 音频开始播放");
@@ -759,6 +801,10 @@ const _sfc_main = {
759
801
  standby();
760
802
  }
761
803
  } catch (e) {
804
+ if ((e == null ? void 0 : e.name) === "AbortError") {
805
+ console.log("[OmniVoice] TTS已取消");
806
+ return;
807
+ }
762
808
  console.error("[OmniVoice] TTS失败:", e);
763
809
  standby();
764
810
  }
@@ -870,6 +916,7 @@ const _sfc_main = {
870
916
  init();
871
917
  });
872
918
  vue.onUnmounted(() => {
919
+ abortTTS();
873
920
  stop();
874
921
  if (recorder2.value) {
875
922
  recorder2.value.stop();
@@ -889,7 +936,7 @@ const _sfc_main = {
889
936
  };
890
937
  }
891
938
  };
892
- const DigitalHuman = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-b3682025"]]);
939
+ const DigitalHuman = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-414014e1"]]);
893
940
  DigitalHuman.install = (app) => app.component("DigitalHuman", DigitalHuman);
894
941
  const components = [DemoButton, DemoInput, DigitalHuman];
895
942
  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 {
@@ -559,6 +560,7 @@ const _sfc_main = {
559
560
  }
560
561
  };
561
562
  const pause = () => {
563
+ stopSpeaking();
562
564
  if (isPaused.value) return;
563
565
  isPaused.value = true;
564
566
  pauseSnapshot.value = {
@@ -627,13 +629,44 @@ const _sfc_main = {
627
629
  };
628
630
  const currentAudio = ref(null);
629
631
  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;
632
+ let ttsAbortController = null;
633
+ function abortTTS() {
634
+ currentTtsRequestId.value = null;
635
+ if (ttsAbortController) {
636
+ try {
637
+ ttsAbortController.abort();
638
+ } catch {
639
+ }
640
+ ttsAbortController = null;
641
+ }
633
642
  if (currentAudio.value) {
634
- currentAudio.value.pause();
643
+ try {
644
+ currentAudio.value.pause();
645
+ } catch {
646
+ }
635
647
  currentAudio.value = null;
636
648
  }
649
+ }
650
+ function waitWithSignal(ms, signal) {
651
+ return new Promise((resolve, reject) => {
652
+ if (signal == null ? void 0 : signal.aborted) {
653
+ reject(new DOMException("Aborted", "AbortError"));
654
+ return;
655
+ }
656
+ const timer = setTimeout(resolve, ms);
657
+ const onAbort = () => {
658
+ clearTimeout(timer);
659
+ reject(new DOMException("Aborted", "AbortError"));
660
+ };
661
+ signal == null ? void 0 : signal.addEventListener("abort", onAbort, { once: true });
662
+ });
663
+ }
664
+ const playTTS = async (text) => {
665
+ var _a, _b, _c, _d, _e, _f, _g, _h;
666
+ if (!text || !((_a = currentHuman.value) == null ? void 0 : _a.isMuted)) return;
667
+ abortTTS();
668
+ ttsAbortController = new AbortController();
669
+ const { signal } = ttsAbortController;
637
670
  currentTtsRequestId.value = Date.now();
638
671
  const requestId = currentTtsRequestId.value;
639
672
  try {
@@ -649,7 +682,8 @@ const _sfc_main = {
649
682
  const joinRes = await fetch(`${baseUrl}/gradio_api/queue/join`, {
650
683
  method: "POST",
651
684
  headers: { "Content-Type": "application/json" },
652
- body: JSON.stringify({ data, session_hash: sessionHash, fn_index: 1, trigger_id: 50 })
685
+ body: JSON.stringify({ data, session_hash: sessionHash, fn_index: 1, trigger_id: 50 }),
686
+ signal
653
687
  });
654
688
  const joinData = await joinRes.json();
655
689
  console.log("[OmniVoice] Step1 响应:", joinData);
@@ -658,10 +692,13 @@ const _sfc_main = {
658
692
  let maxPolls = 60;
659
693
  let pollInterval = 1e3;
660
694
  while (!audioUrl && maxPolls > 0) {
661
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
695
+ if (signal.aborted || currentTtsRequestId.value !== requestId) return;
696
+ await waitWithSignal(pollInterval, signal);
662
697
  maxPolls--;
663
698
  try {
664
- const pollRes = await fetch(`${baseUrl}/gradio_api/queue/data?session_hash=${sessionHash}`);
699
+ const pollRes = await fetch(`${baseUrl}/gradio_api/queue/data?session_hash=${sessionHash}`, {
700
+ signal
701
+ });
665
702
  const pollText = await pollRes.text();
666
703
  const lines = pollText.split("\n");
667
704
  for (const line of lines) {
@@ -688,7 +725,7 @@ const _sfc_main = {
688
725
  }
689
726
  const wavBlob = createWavBlob(audioBuffer, samplingRate, 1, 16);
690
727
  const audioObjectUrl = URL.createObjectURL(wavBlob);
691
- if (currentTtsRequestId.value !== requestId) {
728
+ if (signal.aborted || currentTtsRequestId.value !== requestId) {
692
729
  URL.revokeObjectURL(audioObjectUrl);
693
730
  return;
694
731
  }
@@ -724,16 +761,21 @@ const _sfc_main = {
724
761
  }
725
762
  }
726
763
  } catch (e) {
764
+ if ((e == null ? void 0 : e.name) === "AbortError") throw e;
727
765
  console.warn("[OmniVoice] 轮询失败:", e.message);
728
766
  }
729
767
  }
730
768
  if (audioUrl) {
731
769
  if (audioUrl.startsWith("/")) audioUrl = baseUrl + audioUrl;
732
770
  console.log("[OmniVoice] 下载音频:", audioUrl);
733
- if (currentTtsRequestId.value !== requestId) return;
734
- const audioRes = await fetch(audioUrl);
771
+ if (signal.aborted || currentTtsRequestId.value !== requestId) return;
772
+ const audioRes = await fetch(audioUrl, { signal });
735
773
  const audioBlob = await audioRes.blob();
736
774
  const audioObjectUrl = URL.createObjectURL(audioBlob);
775
+ if (signal.aborted || currentTtsRequestId.value !== requestId) {
776
+ URL.revokeObjectURL(audioObjectUrl);
777
+ return;
778
+ }
737
779
  currentAudio.value = new Audio(audioObjectUrl);
738
780
  currentAudio.value.onplaying = () => {
739
781
  console.log("[OmniVoice] 音频开始播放");
@@ -757,6 +799,10 @@ const _sfc_main = {
757
799
  standby();
758
800
  }
759
801
  } catch (e) {
802
+ if ((e == null ? void 0 : e.name) === "AbortError") {
803
+ console.log("[OmniVoice] TTS已取消");
804
+ return;
805
+ }
760
806
  console.error("[OmniVoice] TTS失败:", e);
761
807
  standby();
762
808
  }
@@ -868,6 +914,7 @@ const _sfc_main = {
868
914
  init();
869
915
  });
870
916
  onUnmounted(() => {
917
+ abortTTS();
871
918
  stop();
872
919
  if (recorder2.value) {
873
920
  recorder2.value.stop();
@@ -887,7 +934,7 @@ const _sfc_main = {
887
934
  };
888
935
  }
889
936
  };
890
- const DigitalHuman = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-b3682025"]]);
937
+ const DigitalHuman = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-414014e1"]]);
891
938
  DigitalHuman.install = (app) => app.component("DigitalHuman", DigitalHuman);
892
939
  const components = [DemoButton, DemoInput, DigitalHuman];
893
940
  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-414014e1]{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.digital-human canvas[data-v-414014e1]{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.7",
4
4
  "type": "module",
5
5
  "description": "数字人初步实现",
6
6
  "main": "./dist/ruiyun-human.cjs.js",