ruiyun-human 1.0.18 → 1.0.19

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.
@@ -388,7 +388,6 @@ const Recorder = /* @__PURE__ */ getDefaultExportFromCjs(jsAudioRecorder);
388
388
  const VOICE_CLIENT_ID = "e5cd2b";
389
389
  const VOICE_TENANT_ID = "000000";
390
390
  const TTS_USER = "web-test-user";
391
- const szrUrl = `https://sqsyky.xzspj.suqian.gov.cn:8443/szrimage/`;
392
391
  const TTS_PCM_SAMPLE_RATE = 16e3;
393
392
  const TTS_MAX_RETRIES = 5;
394
393
  const _sfc_main = {
@@ -406,15 +405,15 @@ const _sfc_main = {
406
405
  emits: ["loaded", "changed", "paused", "resumed", "audio-ready"],
407
406
  setup(__props, { expose: __expose, emit: __emit }) {
408
407
  const props = __props;
409
- const TTS_URL = `${props.szraddress}`;
410
- const ASR_URL = `${props.szraddress}`;
411
- const HUMAN_PRESETS = {
408
+ const TTS_URL = vue.computed(() => props.szraddress || "");
409
+ const ASR_URL = vue.computed(() => props.szraddress || "");
410
+ const szrUrl = vue.computed(() => `${props.szraddress || ""}/szrimage`);
411
+ const buildPresets = (rootUrl) => ({
412
412
  1: {
413
413
  humanId: 1,
414
- title: "西装动漫男",
415
- // TODO: 替换为真实资源
416
- folder: `${szrUrl}/xiangyu-green-suit`,
417
- folder_mobile: `${szrUrl}/xiangyu-green-suit-header`,
414
+ title: "羽哥",
415
+ folder: `${rootUrl}/xiangyu-green-suit`,
416
+ folder_mobile: `${rootUrl}/xiangyu-green-suit-header`,
418
417
  standby: { interval: 100, standbyNum: 11 },
419
418
  speak: { interval: 30, speakNum: 274 },
420
419
  humanType: 1,
@@ -422,9 +421,9 @@ const _sfc_main = {
422
421
  },
423
422
  2: {
424
423
  humanId: 2,
425
- title: "西装动漫女",
426
- folder: `${szrUrl}/anime-woman`,
427
- folder_mobile: `${szrUrl}/anime-woman-header`,
424
+ title: "虞姐",
425
+ folder: `${rootUrl}/anime-woman`,
426
+ folder_mobile: `${rootUrl}/anime-woman-header`,
428
427
  standby: { interval: 100, standbyNum: 1 },
429
428
  speak: { interval: 30, speakNum: 75 },
430
429
  humanType: 1,
@@ -432,9 +431,9 @@ const _sfc_main = {
432
431
  },
433
432
  3: {
434
433
  humanId: 3,
435
- title: "真人形象男",
436
- folder: `${szrUrl}/real-man`,
437
- folder_mobile: `${szrUrl}/real-man-header`,
434
+ title: "羽哥",
435
+ folder: `${rootUrl}/real-man`,
436
+ folder_mobile: `${rootUrl}/real-man-header`,
438
437
  standby: { interval: 100, standbyNum: 1 },
439
438
  speak: { interval: 30, speakNum: 139 },
440
439
  humanType: 1,
@@ -442,9 +441,9 @@ const _sfc_main = {
442
441
  },
443
442
  4: {
444
443
  humanId: 4,
445
- title: "真人形象女",
446
- folder: `${szrUrl}/real-woman`,
447
- folder_mobile: `${szrUrl}/real-woman-header`,
444
+ title: "虞姐",
445
+ folder: `${rootUrl}/real-woman`,
446
+ folder_mobile: `${rootUrl}/real-woman-header`,
448
447
  standby: { interval: 100, standbyNum: 2 },
449
448
  speak: { interval: 40, speakNum: 139 },
450
449
  humanType: 1,
@@ -452,15 +451,15 @@ const _sfc_main = {
452
451
  },
453
452
  5: {
454
453
  humanId: 5,
455
- title: "Q版形象",
456
- folder: `${szrUrl}/xiangyu-q`,
457
- folder_mobile: `${szrUrl}/xiangyu-q-header`,
454
+ title: "羽哥",
455
+ folder: `${rootUrl}/xiangyu-q`,
456
+ folder_mobile: `${rootUrl}/xiangyu-q-header`,
458
457
  standby: { interval: 100, standbyNum: 23 },
459
458
  speak: { interval: 30, speakNum: 118 },
460
459
  humanType: 1,
461
460
  timbre: 47
462
461
  }
463
- };
462
+ });
464
463
  const isMobile = () => {
465
464
  if (typeof navigator === "undefined") return false;
466
465
  return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|HarmonyOS/i.test(navigator.userAgent);
@@ -473,9 +472,9 @@ const _sfc_main = {
473
472
  const resolveHuman = (input) => {
474
473
  if (input == null) return null;
475
474
  const humanId = typeof input === "number" ? input : input.humanId;
476
- const preset = HUMAN_PRESETS[humanId];
475
+ const preset = buildPresets(szrUrl.value)[humanId];
477
476
  if (!preset) {
478
- console.error("[DigitalHuman] 未找到 humanId=", humanId, "的预设形象,可用 id:", Object.keys(HUMAN_PRESETS));
477
+ console.error("[DigitalHuman] 未找到 humanId=", humanId, "的预设形象,可用 id:", Object.keys(buildPresets(szrUrl.value)));
479
478
  return null;
480
479
  }
481
480
  const merged = { ...preset, ...typeof input === "object" ? input : {} };
@@ -731,10 +730,18 @@ const _sfc_main = {
731
730
  const dpr = window.devicePixelRatio || 1;
732
731
  const width = containerRef.value.clientWidth;
733
732
  const height = containerRef.value.clientHeight;
734
- canvas.width = width * dpr;
735
- canvas.height = height * dpr;
736
- canvas.style.width = width + "px";
737
- canvas.style.height = height + "px";
733
+ if (!width || !height) {
734
+ console.warn('[DigitalHuman] 容器宽高为 0!组件根元素是 width:100%;height:100%,需要父容器提供宽高(如 style="width:500px;height:500px" class="w-full h-screen")。已自动设置最小尺寸 300×300 作为兜底');
735
+ canvas.style.width = "300px";
736
+ canvas.style.height = "300px";
737
+ canvas.width = 300 * dpr;
738
+ canvas.height = 300 * dpr;
739
+ } else {
740
+ canvas.width = width * dpr;
741
+ canvas.height = height * dpr;
742
+ canvas.style.width = width + "px";
743
+ canvas.style.height = height + "px";
744
+ }
738
745
  ctx.value = canvas.getContext("2d");
739
746
  loadFrames();
740
747
  waitFramesLoaded(() => {
@@ -808,7 +815,7 @@ const _sfc_main = {
808
815
  for (let attempt = 1; attempt <= TTS_MAX_RETRIES; attempt++) {
809
816
  if (signal == null ? void 0 : signal.aborted) throw new DOMException("Aborted", "AbortError");
810
817
  console.log("[TTS] 提交合成请求1111:", ((_a = currentHuman.value) == null ? void 0 : _a.timbre) ?? 1);
811
- const res = await fetch(`${TTS_URL}/web/voice/jd/text-to-speech`, {
818
+ const res = await fetch(`${TTS_URL.value}/web/voice/jd/text-to-speech`, {
812
819
  method: "POST",
813
820
  headers: {
814
821
  "Content-Type": "application/json",
@@ -946,7 +953,7 @@ const _sfc_main = {
946
953
  domain: "general",
947
954
  sampleRate: "16000"
948
955
  });
949
- const url = `${ASR_URL}/web/voice/jd/speech-to-text?${params}`;
956
+ const url = `${ASR_URL.value}/web/voice/jd/speech-to-text?${params}`;
950
957
  try {
951
958
  const res = await fetch(url, {
952
959
  method: "POST",
@@ -981,7 +988,7 @@ const _sfc_main = {
981
988
  }
982
989
  };
983
990
  vue.watch(
984
- () => [props.human],
991
+ () => props.human,
985
992
  () => {
986
993
  init();
987
994
  },
@@ -1038,7 +1045,7 @@ const _sfc_main = {
1038
1045
  };
1039
1046
  }
1040
1047
  };
1041
- const DigitalHuman = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-60e083ad"]]);
1048
+ const DigitalHuman = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-b6f17245"]]);
1042
1049
  DigitalHuman.install = (app) => app.component("DigitalHuman", DigitalHuman);
1043
1050
  const components = [DemoButton, DemoInput, DigitalHuman];
1044
1051
  const install = (app) => {
@@ -1,4 +1,4 @@
1
- import { openBlock, createElementBlock, normalizeClass, renderSlot, createCommentVNode, createElementVNode, ref, watch, onMounted, onUnmounted, nextTick } from "vue";
1
+ import { openBlock, createElementBlock, normalizeClass, renderSlot, createCommentVNode, createElementVNode, computed, ref, watch, onMounted, onUnmounted, nextTick } from "vue";
2
2
  const _export_sfc = (sfc, props) => {
3
3
  const target = sfc.__vccOpts || sfc;
4
4
  for (const [key, val] of props) {
@@ -386,7 +386,6 @@ const Recorder = /* @__PURE__ */ getDefaultExportFromCjs(jsAudioRecorder);
386
386
  const VOICE_CLIENT_ID = "e5cd2b";
387
387
  const VOICE_TENANT_ID = "000000";
388
388
  const TTS_USER = "web-test-user";
389
- const szrUrl = `https://sqsyky.xzspj.suqian.gov.cn:8443/szrimage/`;
390
389
  const TTS_PCM_SAMPLE_RATE = 16e3;
391
390
  const TTS_MAX_RETRIES = 5;
392
391
  const _sfc_main = {
@@ -404,15 +403,15 @@ const _sfc_main = {
404
403
  emits: ["loaded", "changed", "paused", "resumed", "audio-ready"],
405
404
  setup(__props, { expose: __expose, emit: __emit }) {
406
405
  const props = __props;
407
- const TTS_URL = `${props.szraddress}`;
408
- const ASR_URL = `${props.szraddress}`;
409
- const HUMAN_PRESETS = {
406
+ const TTS_URL = computed(() => props.szraddress || "");
407
+ const ASR_URL = computed(() => props.szraddress || "");
408
+ const szrUrl = computed(() => `${props.szraddress || ""}/szrimage`);
409
+ const buildPresets = (rootUrl) => ({
410
410
  1: {
411
411
  humanId: 1,
412
- title: "西装动漫男",
413
- // TODO: 替换为真实资源
414
- folder: `${szrUrl}/xiangyu-green-suit`,
415
- folder_mobile: `${szrUrl}/xiangyu-green-suit-header`,
412
+ title: "羽哥",
413
+ folder: `${rootUrl}/xiangyu-green-suit`,
414
+ folder_mobile: `${rootUrl}/xiangyu-green-suit-header`,
416
415
  standby: { interval: 100, standbyNum: 11 },
417
416
  speak: { interval: 30, speakNum: 274 },
418
417
  humanType: 1,
@@ -420,9 +419,9 @@ const _sfc_main = {
420
419
  },
421
420
  2: {
422
421
  humanId: 2,
423
- title: "西装动漫女",
424
- folder: `${szrUrl}/anime-woman`,
425
- folder_mobile: `${szrUrl}/anime-woman-header`,
422
+ title: "虞姐",
423
+ folder: `${rootUrl}/anime-woman`,
424
+ folder_mobile: `${rootUrl}/anime-woman-header`,
426
425
  standby: { interval: 100, standbyNum: 1 },
427
426
  speak: { interval: 30, speakNum: 75 },
428
427
  humanType: 1,
@@ -430,9 +429,9 @@ const _sfc_main = {
430
429
  },
431
430
  3: {
432
431
  humanId: 3,
433
- title: "真人形象男",
434
- folder: `${szrUrl}/real-man`,
435
- folder_mobile: `${szrUrl}/real-man-header`,
432
+ title: "羽哥",
433
+ folder: `${rootUrl}/real-man`,
434
+ folder_mobile: `${rootUrl}/real-man-header`,
436
435
  standby: { interval: 100, standbyNum: 1 },
437
436
  speak: { interval: 30, speakNum: 139 },
438
437
  humanType: 1,
@@ -440,9 +439,9 @@ const _sfc_main = {
440
439
  },
441
440
  4: {
442
441
  humanId: 4,
443
- title: "真人形象女",
444
- folder: `${szrUrl}/real-woman`,
445
- folder_mobile: `${szrUrl}/real-woman-header`,
442
+ title: "虞姐",
443
+ folder: `${rootUrl}/real-woman`,
444
+ folder_mobile: `${rootUrl}/real-woman-header`,
446
445
  standby: { interval: 100, standbyNum: 2 },
447
446
  speak: { interval: 40, speakNum: 139 },
448
447
  humanType: 1,
@@ -450,15 +449,15 @@ const _sfc_main = {
450
449
  },
451
450
  5: {
452
451
  humanId: 5,
453
- title: "Q版形象",
454
- folder: `${szrUrl}/xiangyu-q`,
455
- folder_mobile: `${szrUrl}/xiangyu-q-header`,
452
+ title: "羽哥",
453
+ folder: `${rootUrl}/xiangyu-q`,
454
+ folder_mobile: `${rootUrl}/xiangyu-q-header`,
456
455
  standby: { interval: 100, standbyNum: 23 },
457
456
  speak: { interval: 30, speakNum: 118 },
458
457
  humanType: 1,
459
458
  timbre: 47
460
459
  }
461
- };
460
+ });
462
461
  const isMobile = () => {
463
462
  if (typeof navigator === "undefined") return false;
464
463
  return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|HarmonyOS/i.test(navigator.userAgent);
@@ -471,9 +470,9 @@ const _sfc_main = {
471
470
  const resolveHuman = (input) => {
472
471
  if (input == null) return null;
473
472
  const humanId = typeof input === "number" ? input : input.humanId;
474
- const preset = HUMAN_PRESETS[humanId];
473
+ const preset = buildPresets(szrUrl.value)[humanId];
475
474
  if (!preset) {
476
- console.error("[DigitalHuman] 未找到 humanId=", humanId, "的预设形象,可用 id:", Object.keys(HUMAN_PRESETS));
475
+ console.error("[DigitalHuman] 未找到 humanId=", humanId, "的预设形象,可用 id:", Object.keys(buildPresets(szrUrl.value)));
477
476
  return null;
478
477
  }
479
478
  const merged = { ...preset, ...typeof input === "object" ? input : {} };
@@ -729,10 +728,18 @@ const _sfc_main = {
729
728
  const dpr = window.devicePixelRatio || 1;
730
729
  const width = containerRef.value.clientWidth;
731
730
  const height = containerRef.value.clientHeight;
732
- canvas.width = width * dpr;
733
- canvas.height = height * dpr;
734
- canvas.style.width = width + "px";
735
- canvas.style.height = height + "px";
731
+ if (!width || !height) {
732
+ console.warn('[DigitalHuman] 容器宽高为 0!组件根元素是 width:100%;height:100%,需要父容器提供宽高(如 style="width:500px;height:500px" class="w-full h-screen")。已自动设置最小尺寸 300×300 作为兜底');
733
+ canvas.style.width = "300px";
734
+ canvas.style.height = "300px";
735
+ canvas.width = 300 * dpr;
736
+ canvas.height = 300 * dpr;
737
+ } else {
738
+ canvas.width = width * dpr;
739
+ canvas.height = height * dpr;
740
+ canvas.style.width = width + "px";
741
+ canvas.style.height = height + "px";
742
+ }
736
743
  ctx.value = canvas.getContext("2d");
737
744
  loadFrames();
738
745
  waitFramesLoaded(() => {
@@ -806,7 +813,7 @@ const _sfc_main = {
806
813
  for (let attempt = 1; attempt <= TTS_MAX_RETRIES; attempt++) {
807
814
  if (signal == null ? void 0 : signal.aborted) throw new DOMException("Aborted", "AbortError");
808
815
  console.log("[TTS] 提交合成请求1111:", ((_a = currentHuman.value) == null ? void 0 : _a.timbre) ?? 1);
809
- const res = await fetch(`${TTS_URL}/web/voice/jd/text-to-speech`, {
816
+ const res = await fetch(`${TTS_URL.value}/web/voice/jd/text-to-speech`, {
810
817
  method: "POST",
811
818
  headers: {
812
819
  "Content-Type": "application/json",
@@ -944,7 +951,7 @@ const _sfc_main = {
944
951
  domain: "general",
945
952
  sampleRate: "16000"
946
953
  });
947
- const url = `${ASR_URL}/web/voice/jd/speech-to-text?${params}`;
954
+ const url = `${ASR_URL.value}/web/voice/jd/speech-to-text?${params}`;
948
955
  try {
949
956
  const res = await fetch(url, {
950
957
  method: "POST",
@@ -979,7 +986,7 @@ const _sfc_main = {
979
986
  }
980
987
  };
981
988
  watch(
982
- () => [props.human],
989
+ () => props.human,
983
990
  () => {
984
991
  init();
985
992
  },
@@ -1036,7 +1043,7 @@ const _sfc_main = {
1036
1043
  };
1037
1044
  }
1038
1045
  };
1039
- const DigitalHuman = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-60e083ad"]]);
1046
+ const DigitalHuman = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-b6f17245"]]);
1040
1047
  DigitalHuman.install = (app) => app.component("DigitalHuman", DigitalHuman);
1041
1048
  const components = [DemoButton, DemoInput, DigitalHuman];
1042
1049
  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-60e083ad]{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.digital-human canvas[data-v-60e083ad]{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-b6f17245]{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.digital-human canvas[data-v-b6f17245]{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.18",
3
+ "version": "1.0.19",
4
4
  "type": "module",
5
5
  "description": "数字人初步实现",
6
6
  "main": "./dist/ruiyun-human.cjs.js",