@spatialwalk/avatarkit 1.0.0-beta.51 → 1.0.0-beta.53

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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.0.0-beta.53] - 2025-01-05
9
+
10
+ ### 🐛 Bugfix
11
+ - **Race Condition Fix** - Fixed race condition where `startStreamingPlaybackInternal()` could be called multiple times when `yieldKeyframes` received data rapidly, causing audio chunks to be cleared and playback to freeze after transition animations. Added `isStartingPlayback` flag to prevent concurrent execution.
12
+
13
+ ## [1.0.0-beta.52] - 2025-01-05
14
+
15
+ ### 🔧 Performance Improvements
16
+ - **Telemetry Optimization** - Consolidated resource download telemetry: removed individual `resource_download` events and added cache information (`cache_hit`, `cache_type`) to total duration metrics (`template_resources_load_measure` and `download_avatar_assets_total_measure`). This enables analysis of download times for cache misses while reducing telemetry overhead.
17
+
8
18
  ## [1.0.0-beta.51] - 2025-01-05
9
19
 
10
20
  ### 🐛 Bugfix
@@ -1,7 +1,7 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { A as APP_CONFIG, e as errorToMessage, l as logEvent, a as logger } from "./index-D2_q6K22.js";
4
+ import { A as APP_CONFIG, e as errorToMessage, l as logEvent, a as logger } from "./index-CLKS00Ly.js";
5
5
  class StreamingAudioPlayer {
6
6
  constructor(options) {
7
7
  __publicField(this, "audioContext", null);
@@ -13,6 +13,7 @@ export declare class AvatarController {
13
13
  isLast: boolean;
14
14
  }>;
15
15
  protected isPlaying: boolean;
16
+ private isStartingPlayback;
16
17
  protected isConnected: boolean;
17
18
  protected currentState: AvatarState;
18
19
  private currentConversationId;
@@ -7579,7 +7579,7 @@ const _AnimationPlayer = class _AnimationPlayer {
7579
7579
  if (this.streamingPlayer) {
7580
7580
  return;
7581
7581
  }
7582
- const { StreamingAudioPlayer } = await import("./StreamingAudioPlayer-DXKLgGU3.js");
7582
+ const { StreamingAudioPlayer } = await import("./StreamingAudioPlayer-DNLrXB1O.js");
7583
7583
  const { AvatarSDK: AvatarSDK2 } = await Promise.resolve().then(() => AvatarSDK$1);
7584
7584
  const audioFormat = AvatarSDK2.getAudioFormat();
7585
7585
  this.streamingPlayer = new StreamingAudioPlayer({
@@ -8969,7 +8969,7 @@ class AvatarSDK {
8969
8969
  }
8970
8970
  __publicField(AvatarSDK, "_isInitialized", false);
8971
8971
  __publicField(AvatarSDK, "_configuration", null);
8972
- __publicField(AvatarSDK, "_version", "1.0.0-beta.51");
8972
+ __publicField(AvatarSDK, "_version", "1.0.0-beta.53");
8973
8973
  __publicField(AvatarSDK, "_avatarCore", null);
8974
8974
  __publicField(AvatarSDK, "_dynamicSdkConfig", null);
8975
8975
  const AvatarSDK$1 = Object.freeze(Object.defineProperty({
@@ -10619,6 +10619,7 @@ class AvatarController {
10619
10619
  __publicField(this, "currentKeyframes", []);
10620
10620
  __publicField(this, "pendingAudioChunks", []);
10621
10621
  __publicField(this, "isPlaying", false);
10622
+ __publicField(this, "isStartingPlayback", false);
10622
10623
  __publicField(this, "isConnected", false);
10623
10624
  __publicField(this, "currentState", AvatarState.idle);
10624
10625
  __publicField(this, "currentConversationId", null);
@@ -10960,9 +10961,10 @@ class AvatarController {
10960
10961
  this.currentKeyframes.push(...keyframes);
10961
10962
  }
10962
10963
  this.emit("keyframesUpdate", this.currentKeyframes);
10963
- if (!this.isPlaying && this.pendingAudioChunks.length > 0 && this.currentKeyframes.length > 0) {
10964
+ if (!this.isPlaying && !this.isStartingPlayback && this.pendingAudioChunks.length > 0 && this.currentKeyframes.length > 0) {
10964
10965
  this.startStreamingPlayback().catch((error) => {
10965
10966
  var _a;
10967
+ this.isStartingPlayback = false;
10966
10968
  logger.error("[AvatarController] Failed to auto-start playback:", error);
10967
10969
  (_a = this.onError) == null ? void 0 : _a.call(this, new SPAvatarError("Failed to start playback", "PLAYBACK_START_FAILED"));
10968
10970
  });
@@ -11185,6 +11187,14 @@ class AvatarController {
11185
11187
  }
11186
11188
  async startStreamingPlaybackInternal() {
11187
11189
  var _a, _b, _c;
11190
+ if (this.isPlaying) {
11191
+ this.isStartingPlayback = false;
11192
+ return;
11193
+ }
11194
+ if (this.isStartingPlayback) {
11195
+ return;
11196
+ }
11197
+ this.isStartingPlayback = true;
11188
11198
  if (!this.animationPlayer) {
11189
11199
  this.animationPlayer = new AnimationPlayer();
11190
11200
  }
@@ -11192,6 +11202,7 @@ class AvatarController {
11192
11202
  try {
11193
11203
  await this.animationPlayer.createAndInitializeStreamingPlayer();
11194
11204
  } catch (error) {
11205
+ this.isStartingPlayback = false;
11195
11206
  const message = error instanceof Error ? error.message : String(error);
11196
11207
  logger.error("[AvatarController] Failed to create streaming player:", message);
11197
11208
  logEvent("character_player", "error", {
@@ -11203,6 +11214,7 @@ class AvatarController {
11203
11214
  }
11204
11215
  }
11205
11216
  if (!this.currentKeyframes || this.currentKeyframes.length === 0) {
11217
+ this.isStartingPlayback = false;
11206
11218
  logger.warn("[AvatarController] No animation data to play");
11207
11219
  return;
11208
11220
  }
@@ -11240,6 +11252,7 @@ class AvatarController {
11240
11252
  this.currentState = AvatarState.playing;
11241
11253
  (_a = this.onConversationState) == null ? void 0 : _a.call(this, this.mapToConversationState(AvatarState.playing));
11242
11254
  this.startPlaybackLoop();
11255
+ this.isStartingPlayback = false;
11243
11256
  logEvent("character_player", "info", {
11244
11257
  avatar_id: this.avatar.id,
11245
11258
  event: "playback_started",
@@ -11250,6 +11263,7 @@ class AvatarController {
11250
11263
  logger.error("[AvatarController] Failed to start streaming playback:", message);
11251
11264
  (_c = this.onError) == null ? void 0 : _c.call(this, new SPAvatarError("Failed to start streaming playback", "INIT_FAILED"));
11252
11265
  this.isPlaying = false;
11266
+ this.isStartingPlayback = false;
11253
11267
  }
11254
11268
  }
11255
11269
  checkPlaybackStuck(audioTime) {
@@ -11506,6 +11520,7 @@ class AvatarController {
11506
11520
  stopPlayback() {
11507
11521
  var _a;
11508
11522
  this.stopPlaybackLoop();
11523
+ this.isStartingPlayback = false;
11509
11524
  if (this.animationPlayer) {
11510
11525
  this.animationPlayer.stop();
11511
11526
  const streamingPlayer = this.animationPlayer.getStreamingPlayer();
@@ -11915,24 +11930,16 @@ class AvatarDownloader {
11915
11930
  }
11916
11931
  updateProgress(resourceName, false);
11917
11932
  logger.log(`📥 Loading ${key} from API CDN: ${url}`);
11918
- const { data: buffer, cacheInfo } = await downloadResource(url, { resourceType: "template" });
11933
+ const { data: buffer } = await downloadResource(url, { resourceType: "template" });
11919
11934
  logger.log(`✅ ${key} loaded: ${buffer.byteLength} bytes`);
11920
11935
  templateResources[key] = buffer;
11921
11936
  updateProgress(resourceName, true);
11922
- logEvent("resource_download", "info", {
11923
- resource_type: "template",
11924
- resource_name: resourceName,
11925
- cache_hit: cacheInfo.cacheHit,
11926
- cache_type: cacheInfo.cacheType,
11927
- pwa_cache_subtype: cacheInfo.pwaCacheSubtype,
11928
- transfer_size: cacheInfo.transferSize,
11929
- cdn_cache_status: cacheInfo.cdnCacheStatus
11930
- });
11931
11937
  });
11932
11938
  await Promise.all(promises);
11933
11939
  return templateResources;
11934
11940
  }
11935
11941
  async loadGlobalFlameResources(progressCallback = null) {
11942
+ var _a;
11936
11943
  await PwaCacheManager.checkTemplateCacheVersion();
11937
11944
  const startTime = Date.now();
11938
11945
  const { cdnBaseUrl, resources } = APP_CONFIG.flame;
@@ -11970,6 +11977,7 @@ class AvatarDownloader {
11970
11977
  }
11971
11978
  };
11972
11979
  const templateResources = {};
11980
+ const cacheInfos = [];
11973
11981
  try {
11974
11982
  const promises = Object.entries(templateFiles).map(async ([key, { url, resourceName }]) => {
11975
11983
  updateProgress(resourceName, false);
@@ -11977,22 +11985,18 @@ class AvatarDownloader {
11977
11985
  const { data: buffer, cacheInfo } = await downloadResource(url, { resourceType: "template" });
11978
11986
  logger.log(`✅ ${key} loaded: ${buffer.byteLength} bytes`);
11979
11987
  templateResources[key] = buffer;
11988
+ cacheInfos.push(cacheInfo);
11980
11989
  updateProgress(resourceName, true);
11981
- logEvent("resource_download", "info", {
11982
- resource_type: "template",
11983
- resource_name: resourceName,
11984
- cache_hit: cacheInfo.cacheHit,
11985
- cache_type: cacheInfo.cacheType,
11986
- pwa_cache_subtype: cacheInfo.pwaCacheSubtype,
11987
- transfer_size: cacheInfo.transferSize,
11988
- cdn_cache_status: cacheInfo.cdnCacheStatus
11989
- });
11990
11990
  });
11991
11991
  await Promise.all(promises);
11992
+ const cacheHit = cacheInfos.length > 0 && cacheInfos.every((info) => info.cacheHit);
11993
+ const cacheType = ((_a = cacheInfos[0]) == null ? void 0 : _a.cacheType) || "none";
11992
11994
  const duration = Date.now() - startTime;
11993
11995
  logEvent("template_resources_load_measure", "info", {
11994
11996
  duration,
11995
- file_count: totalFiles
11997
+ file_count: totalFiles,
11998
+ cache_hit: cacheHit,
11999
+ cache_type: cacheType
11996
12000
  });
11997
12001
  return templateResources;
11998
12002
  } catch (error) {
@@ -12008,21 +12012,12 @@ class AvatarDownloader {
12008
12012
  }
12009
12013
  try {
12010
12014
  logger.log(`📥 Loading camera info from: ${cameraUrl}`);
12011
- const { data: arrayBuffer, cacheInfo } = await downloadResource(cameraUrl, {
12015
+ const { data: arrayBuffer } = await downloadResource(cameraUrl, {
12012
12016
  characterId: characterMeta.characterId ?? void 0,
12013
12017
  resourceType: "character"
12014
12018
  });
12015
12019
  const text = new TextDecoder().decode(arrayBuffer);
12016
12020
  const cameraSettings = JSON.parse(text);
12017
- logEvent("resource_download", "info", {
12018
- resource_type: "camera_settings",
12019
- resource_name: "camera.json",
12020
- cache_hit: cacheInfo.cacheHit,
12021
- cache_type: cacheInfo.cacheType,
12022
- pwa_cache_subtype: cacheInfo.pwaCacheSubtype,
12023
- transfer_size: cacheInfo.transferSize,
12024
- cdn_cache_status: cacheInfo.cdnCacheStatus
12025
- });
12026
12021
  logger.log("✅ Camera info loaded:", cameraSettings);
12027
12022
  return cameraSettings;
12028
12023
  } catch (error) {
@@ -12031,7 +12026,7 @@ class AvatarDownloader {
12031
12026
  }
12032
12027
  }
12033
12028
  async loadCharacterData(characterMeta, options) {
12034
- var _a, _b, _c, _d, _e2, _f, _g, _h, _i2;
12029
+ var _a, _b, _c, _d, _e2, _f, _g, _h, _i2, _j;
12035
12030
  const { progressCallback = null } = options || {};
12036
12031
  const totalStartTime = Date.now();
12037
12032
  const shapeUrl = (_c = (_b = (_a = characterMeta.models) == null ? void 0 : _a.shape) == null ? void 0 : _b.resource) == null ? void 0 : _c.remote;
@@ -12068,6 +12063,7 @@ class AvatarDownloader {
12068
12063
  }
12069
12064
  };
12070
12065
  const characterData = {};
12066
+ const cacheInfos = [];
12071
12067
  const parallelStartTime = Date.now();
12072
12068
  const downloadPromises = filesToLoad.map(async ({ key, url, filename, optional }) => {
12073
12069
  updateProgress(filename, false);
@@ -12083,16 +12079,8 @@ class AvatarDownloader {
12083
12079
  } else if (key === "idleAnimation") {
12084
12080
  characterData.idleAnimation = arrayBuffer;
12085
12081
  }
12082
+ cacheInfos.push(cacheInfo);
12086
12083
  updateProgress(filename, true);
12087
- logEvent("resource_download", "info", {
12088
- resource_type: "character_asset",
12089
- resource_name: filename,
12090
- cache_hit: cacheInfo.cacheHit,
12091
- cache_type: cacheInfo.cacheType,
12092
- pwa_cache_subtype: cacheInfo.pwaCacheSubtype,
12093
- transfer_size: cacheInfo.transferSize,
12094
- cdn_cache_status: cacheInfo.cdnCacheStatus
12095
- });
12096
12084
  return { key, success: true, size: arrayBuffer.byteLength };
12097
12085
  } catch (error) {
12098
12086
  if (!optional) {
@@ -12114,6 +12102,8 @@ class AvatarDownloader {
12114
12102
  });
12115
12103
  throw new Error(reason);
12116
12104
  }
12105
+ const cacheHit = cacheInfos.length > 0 && cacheInfos.every((info) => info.cacheHit);
12106
+ const cacheType = ((_j = cacheInfos[0]) == null ? void 0 : _j.cacheType) || "none";
12117
12107
  const totalSize = Object.values(characterData).reduce((sum, buffer) => {
12118
12108
  return sum + (buffer ? buffer.byteLength : 0);
12119
12109
  }, 0);
@@ -12122,7 +12112,9 @@ class AvatarDownloader {
12122
12112
  total_duration: totalDuration,
12123
12113
  parallel_duration: parallelDuration,
12124
12114
  total_size: totalSize,
12125
- file_count: filesToLoad.length
12115
+ file_count: filesToLoad.length,
12116
+ cache_hit: cacheHit,
12117
+ cache_type: cacheType
12126
12118
  });
12127
12119
  return characterData;
12128
12120
  }
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { b, c, f, d, j, g, C, i, D, E, k, h, L, R, S, m } from "./index-D2_q6K22.js";
1
+ import { b, c, f, d, j, g, C, i, D, E, k, h, L, R, S, m } from "./index-CLKS00Ly.js";
2
2
  export {
3
3
  b as Avatar,
4
4
  c as AvatarController,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@spatialwalk/avatarkit",
3
3
  "type": "module",
4
- "version": "1.0.0-beta.51",
4
+ "version": "1.0.0-beta.53",
5
5
  "packageManager": "pnpm@10.18.2",
6
6
  "description": "SPAvatar SDK - 3D Gaussian Splatting Avatar Rendering SDK",
7
7
  "author": "SPAvatar Team",