@vidtreo/recorder 1.7.0 → 1.7.2

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +2117 -2098
  2. package/dist/index.js +108 -18
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1777,6 +1777,12 @@ async function resolveAudioCodecFromPolicy(options) {
1777
1777
  if (options.bitrate !== undefined) {
1778
1778
  audioCodecOptions.bitrate = options.bitrate;
1779
1779
  }
1780
+ if (options.numberOfChannels !== undefined) {
1781
+ audioCodecOptions.numberOfChannels = options.numberOfChannels;
1782
+ }
1783
+ if (options.sampleRate !== undefined) {
1784
+ audioCodecOptions.sampleRate = options.sampleRate;
1785
+ }
1780
1786
  const probeResults = await Promise.allSettled([
1781
1787
  getFirstEncodableAudioCodec(candidates, audioCodecOptions)
1782
1788
  ]);
@@ -6299,7 +6305,7 @@ function resolveDeviceError(input) {
6299
6305
  // package.json
6300
6306
  var package_default = {
6301
6307
  name: "@vidtreo/recorder",
6302
- version: "1.7.0",
6308
+ version: "1.7.2",
6303
6309
  type: "module",
6304
6310
  description: "Vidtreo SDK for browser-based video recording and transcoding. Features include camera/screen recording, real-time MP4 transcoding, audio level analysis, mute/pause controls, source switching, device selection, and automatic backend uploads. Similar to Ziggeo and Addpipe, Vidtreo provides enterprise-grade video processing capabilities for web applications.",
6305
6311
  main: "./dist/index.js",
@@ -7050,13 +7056,7 @@ class UploadQueueManager {
7050
7056
  if (!this.storageService.isInitialized()) {
7051
7057
  throw new Error("Database not initialized");
7052
7058
  }
7053
- if (!this.hasRecoveredStaleUploads) {
7054
- const staleUploads = await this.storageService.getPendingUploads("uploading");
7055
- await Promise.all(staleUploads.map((upload) => this.storageService.updateUploadStatus(upload.id, {
7056
- status: "pending"
7057
- })));
7058
- this.hasRecoveredStaleUploads = true;
7059
- }
7059
+ await this.recoverStaleUploads();
7060
7060
  const pendingUploads = await this.storageService.getPendingUploads("pending");
7061
7061
  if (pendingUploads.length > 0) {
7062
7062
  const upload = this.getOldestUpload(pendingUploads);
@@ -7090,6 +7090,27 @@ class UploadQueueManager {
7090
7090
  getPendingUploads() {
7091
7091
  return this.storageService.getPendingUploads();
7092
7092
  }
7093
+ async processMatchingUpload(match) {
7094
+ if (this.isProcessing) {
7095
+ return null;
7096
+ }
7097
+ this.isProcessing = true;
7098
+ try {
7099
+ await this.recoverStaleUploads();
7100
+ const uploads = await this.storageService.getPendingUploads();
7101
+ const matchingUpload = this.getOldestMatchingUpload(uploads, match);
7102
+ if (!matchingUpload) {
7103
+ this.isProcessing = false;
7104
+ return null;
7105
+ }
7106
+ const result = await this.processUpload(matchingUpload, true);
7107
+ this.isProcessing = false;
7108
+ return result;
7109
+ } catch (error) {
7110
+ this.isProcessing = false;
7111
+ throw new Error(`Error processing matching upload: ${extractErrorMessage(error)}`);
7112
+ }
7113
+ }
7093
7114
  async getStats() {
7094
7115
  const all = await this.storageService.getPendingUploads();
7095
7116
  const stats = {
@@ -7121,7 +7142,45 @@ class UploadQueueManager {
7121
7142
  }
7122
7143
  return uploads.sort((a, b) => a.updatedAt - b.updatedAt)[0];
7123
7144
  }
7124
- async processUpload(upload) {
7145
+ async recoverStaleUploads() {
7146
+ if (this.hasRecoveredStaleUploads) {
7147
+ return;
7148
+ }
7149
+ const staleUploads = await this.storageService.getPendingUploads("uploading");
7150
+ await Promise.all(staleUploads.map((upload) => this.storageService.updateUploadStatus(upload.id, {
7151
+ status: "pending"
7152
+ })));
7153
+ this.hasRecoveredStaleUploads = true;
7154
+ }
7155
+ getOldestMatchingUpload(uploads, match) {
7156
+ const matchingUploads = uploads.filter((upload) => this.canProcessMatchingUpload(upload, match));
7157
+ if (matchingUploads.length === 0) {
7158
+ return null;
7159
+ }
7160
+ return matchingUploads.sort((a, b) => a.createdAt - b.createdAt)[0];
7161
+ }
7162
+ canProcessMatchingUpload(upload, match) {
7163
+ if (upload.status === "completed") {
7164
+ return false;
7165
+ }
7166
+ if (upload.status === "failed" && upload.retryCount >= MAX_RETRIES2) {
7167
+ return false;
7168
+ }
7169
+ if (!this.matchesMetadata(upload.metadata, match.metadata)) {
7170
+ return false;
7171
+ }
7172
+ return this.matchesMetadata(upload.userMetadata, match.userMetadata);
7173
+ }
7174
+ matchesMetadata(actual, expected) {
7175
+ if (!expected || Object.keys(expected).length === 0) {
7176
+ return true;
7177
+ }
7178
+ if (!actual) {
7179
+ return false;
7180
+ }
7181
+ return Object.entries(expected).every(([key, value]) => actual[key] === value);
7182
+ }
7183
+ async processUpload(upload, shouldThrowOnError = false) {
7125
7184
  try {
7126
7185
  await this.storageService.updateUploadStatus(upload.id, {
7127
7186
  status: "uploading"
@@ -7139,6 +7198,7 @@ class UploadQueueManager {
7139
7198
  });
7140
7199
  await this.storageService.deleteUpload(upload.id);
7141
7200
  this.callbacks.onUploadComplete?.(upload.id, result);
7201
+ return result;
7142
7202
  } catch (error) {
7143
7203
  const errorMessage = extractErrorMessage(error);
7144
7204
  const retryCount = upload.retryCount + 1;
@@ -7153,6 +7213,10 @@ class UploadQueueManager {
7153
7213
  const delay2 = this.calculateRetryDelay(retryCount);
7154
7214
  this.scheduleRetry(delay2);
7155
7215
  }
7216
+ if (shouldThrowOnError) {
7217
+ throw new Error(errorMessage);
7218
+ }
7219
+ return null;
7156
7220
  }
7157
7221
  }
7158
7222
  calculateRetryDelay(retryCount) {
@@ -21162,6 +21226,8 @@ var CODEC_CACHE_AUDIO_OVERRIDE = "audioOverride";
21162
21226
  var CODEC_CACHE_VIDEO_OVERRIDE = "videoOverride";
21163
21227
  var CODEC_CACHE_AUDIO_BITRATE = "audioBitrate";
21164
21228
  var CODEC_CACHE_VIDEO_BITRATE = "videoBitrate";
21229
+ var CODEC_CACHE_AUDIO_CHANNELS = "audioChannels";
21230
+ var CODEC_CACHE_AUDIO_SAMPLE_RATE = "audioSampleRate";
21165
21231
  var CODEC_CACHE_WIDTH = "width";
21166
21232
  var CODEC_CACHE_HEIGHT = "height";
21167
21233
  var CODEC_CACHE_POLICY_PREFERRED_AUDIO = "policyPreferredAudio";
@@ -21423,7 +21489,6 @@ class WorkerProcessor {
21423
21489
  isLinuxPlatform: this.isLinuxPlatformFn()
21424
21490
  });
21425
21491
  const audioBitrate = this.resolveAudioBitrate(config, format);
21426
- const audioCodec = await this.resolveAudioCodecWithCache(config, format, policy, audioBitrate);
21427
21492
  const codec = await this.resolveVideoCodecWithCache(config, format, policy);
21428
21493
  const isScreenCapture = isScreenCaptureStream(stream);
21429
21494
  logger.debug("[WorkerProcessor] Starting processing", {
@@ -21432,7 +21497,6 @@ class WorkerProcessor {
21432
21497
  codec,
21433
21498
  bitrate: config.bitrate
21434
21499
  });
21435
- const workerConfig = this.buildWorkerTranscodeConfig(config, audioCodec, audioBitrate, codec, format);
21436
21500
  if (typeof config.fps === "number" && config.fps > 0) {
21437
21501
  this.lastConfigFps = config.fps;
21438
21502
  }
@@ -21468,6 +21532,8 @@ class WorkerProcessor {
21468
21532
  };
21469
21533
  }
21470
21534
  const { audioConfig, audioStream, shouldStartAudioWorklet } = await this.prepareAudioPipeline(audioTrack, workerProbeResult);
21535
+ const audioCodec = await this.resolveAudioCodecWithCache(config, format, policy, audioBitrate, audioConfig);
21536
+ const workerConfig = this.buildWorkerTranscodeConfig(config, audioCodec, audioBitrate, codec, format);
21471
21537
  const overlayConfigToSend = this.buildOverlayConfigToSend();
21472
21538
  const message = createStartMessage({
21473
21539
  videoTrack: videoInput.videoTrack,
@@ -21727,12 +21793,14 @@ class WorkerProcessor {
21727
21793
  }
21728
21794
  return getPresetAudioBitrateForFormat(format);
21729
21795
  }
21730
- async resolveAudioCodec(config, format, policy, audioBitrate) {
21796
+ async resolveAudioCodec(config, format, policy, audioBitrate, audioConfig) {
21731
21797
  return await resolveAudioCodecFromPolicy({
21732
21798
  format,
21733
21799
  overrideCodec: config.audioCodec,
21734
21800
  policy,
21735
- bitrate: audioBitrate
21801
+ bitrate: audioBitrate,
21802
+ numberOfChannels: audioConfig?.numberOfChannels,
21803
+ sampleRate: audioConfig?.sampleRate
21736
21804
  });
21737
21805
  }
21738
21806
  async resolveVideoCodec(config, format, policy) {
@@ -21745,13 +21813,13 @@ class WorkerProcessor {
21745
21813
  bitrate: config.bitrate
21746
21814
  });
21747
21815
  }
21748
- async resolveAudioCodecWithCache(config, format, policy, audioBitrate) {
21749
- const audioCodecCacheKey = this.buildAudioCodecCacheKey(config, format, policy, audioBitrate);
21816
+ async resolveAudioCodecWithCache(config, format, policy, audioBitrate, audioConfig) {
21817
+ const audioCodecCacheKey = this.buildAudioCodecCacheKey(config, format, policy, audioBitrate, audioConfig);
21750
21818
  const cachedAudioCodec = resolvedAudioCodecCache.get(audioCodecCacheKey);
21751
21819
  if (cachedAudioCodec) {
21752
21820
  return cachedAudioCodec;
21753
21821
  }
21754
- const resolvedAudioCodec = await this.resolveAudioCodec(config, format, policy, audioBitrate);
21822
+ const resolvedAudioCodec = await this.resolveAudioCodec(config, format, policy, audioBitrate, audioConfig);
21755
21823
  setCodecCacheValue(resolvedAudioCodecCache, audioCodecCacheKey, resolvedAudioCodec);
21756
21824
  return resolvedAudioCodec;
21757
21825
  }
@@ -21765,15 +21833,19 @@ class WorkerProcessor {
21765
21833
  setCodecCacheValue(resolvedVideoCodecCache, videoCodecCacheKey, resolvedVideoCodec);
21766
21834
  return resolvedVideoCodec;
21767
21835
  }
21768
- buildAudioCodecCacheKey(config, format, policy, audioBitrate) {
21836
+ buildAudioCodecCacheKey(config, format, policy, audioBitrate, audioConfig) {
21769
21837
  const audioOverride = formatCacheValue(config.audioCodec);
21770
21838
  const preferredAudioCodec = formatCacheValue(policy.preferredAudioCodec);
21771
21839
  const audioFallbackOrder = formatCacheArray(policy.audioCodecFallbackOrder);
21772
21840
  const audioBitrateValue = formatCacheValue(audioBitrate);
21841
+ const audioChannelsValue = formatCacheValue(audioConfig?.numberOfChannels);
21842
+ const audioSampleRateValue = formatCacheValue(audioConfig?.sampleRate);
21773
21843
  return [
21774
21844
  buildCacheEntry(CODEC_CACHE_FORMAT, formatCacheValue(format)),
21775
21845
  buildCacheEntry(CODEC_CACHE_AUDIO_OVERRIDE, audioOverride),
21776
21846
  buildCacheEntry(CODEC_CACHE_AUDIO_BITRATE, audioBitrateValue),
21847
+ buildCacheEntry(CODEC_CACHE_AUDIO_CHANNELS, audioChannelsValue),
21848
+ buildCacheEntry(CODEC_CACHE_AUDIO_SAMPLE_RATE, audioSampleRateValue),
21777
21849
  buildCacheEntry(CODEC_CACHE_POLICY_PREFERRED_AUDIO, preferredAudioCodec),
21778
21850
  buildCacheEntry(CODEC_CACHE_POLICY_AUDIO_FALLBACK, audioFallbackOrder)
21779
21851
  ].join(CODEC_CACHE_KEY_SEPARATOR);
@@ -22202,7 +22274,7 @@ class WorkerProcessor {
22202
22274
  isLinuxPlatform: this.isLinuxPlatformFn()
22203
22275
  });
22204
22276
  const cacheAudioBitrate = this.resolveAudioBitrate(config, format);
22205
- this.resolveAudioCodecWithCache(config, format, cachePolicy, cacheAudioBitrate).catch(this.handleWarmupCacheError);
22277
+ this.resolveAudioCodecWithCache(config, format, cachePolicy, cacheAudioBitrate, null).catch(this.handleWarmupCacheError);
22206
22278
  this.resolveVideoCodecWithCache(config, format, cachePolicy).catch(this.handleWarmupCacheError);
22207
22279
  const workerConfig = this.buildWorkerTranscodeConfig(config, policy.preferredAudioCodec, audioBitrate, codec, format);
22208
22280
  const message = {
@@ -23885,6 +23957,23 @@ class QuotaManager {
23885
23957
  return quota.percentage >= threshold;
23886
23958
  }
23887
23959
  }
23960
+ // src/core/upload/pending-upload-recovery.ts
23961
+ async function recoverPendingUpload(options) {
23962
+ const storageService = new VideoStorageService;
23963
+ await storageService.init();
23964
+ const uploadService = new VideoUploadService;
23965
+ const uploadQueueManager = new UploadQueueManager(storageService, uploadService);
23966
+ uploadQueueManager.setCallbacks({
23967
+ onUploadProgress: (_id, progress) => {
23968
+ options.onProgress?.(progress);
23969
+ }
23970
+ });
23971
+ try {
23972
+ return await uploadQueueManager.processMatchingUpload(options.match);
23973
+ } finally {
23974
+ uploadQueueManager.destroy();
23975
+ }
23976
+ }
23888
23977
  // src/core/utils/audio-utils.ts
23889
23978
  function calculateBarColor(position) {
23890
23979
  if (position < 0.25) {
@@ -24280,6 +24369,7 @@ export {
24280
24369
  requireInitialized,
24281
24370
  requireDefined,
24282
24371
  requireActive,
24372
+ recoverPendingUpload,
24283
24373
  parseBrowserErrorLinkContent,
24284
24374
  mapPresetToConfig,
24285
24375
  logger,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vidtreo/recorder",
3
- "version": "1.7.0",
3
+ "version": "1.7.2",
4
4
  "type": "module",
5
5
  "description": "Vidtreo SDK for browser-based video recording and transcoding. Features include camera/screen recording, real-time MP4 transcoding, audio level analysis, mute/pause controls, source switching, device selection, and automatic backend uploads. Similar to Ziggeo and Addpipe, Vidtreo provides enterprise-grade video processing capabilities for web applications.",
6
6
  "main": "./dist/index.js",