@vidtreo/recorder 1.9.0 → 1.10.0
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/README.md +3 -3
- package/dist/index.d.ts +12 -1
- package/dist/index.js +122 -67
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ Recordings and native-camera uploads are written to an IndexedDB upload queue be
|
|
|
29
29
|
- The queued upload keeps its idempotency key, so the backend returns the same video and the SDK uploads only the parts R2 has not verified yet.
|
|
30
30
|
- Failed attempts are retried by the queue with exponential backoff (up to 10 attempts) on top of the per-part retries. Retries wait while the browser is offline.
|
|
31
31
|
- Uploads authenticated with `apiKey` store the key with the queued item. Uploads authenticated with `getAccessToken` never persist a token: the provider is called again when the upload resumes, so the page must create the recorder (or native-camera handler) with the same `getAccessToken` after a reload.
|
|
32
|
-
-
|
|
32
|
+
- An `upload.insufficient-credits` rejection does not use up the 10 attempts and never gives up: the upload stays in the queue and is retried every 5 minutes and whenever the queue starts. Each rejection is reported through `onUploadError` with the backend message, and the upload is removed once it succeeds.
|
|
33
33
|
- When IndexedDB is unavailable (private browsing, blocked storage) the SDK uploads directly without persistence.
|
|
34
34
|
|
|
35
35
|
Version 1.8.0 does not fall back to the legacy Worker-proxied upload endpoint. Applications that need the previous transport must remain on an earlier SDK version while the backend continues to support those clients. Custom Vidtreo backend deployments must expose initiation, part authorization, and confirmation endpoints. Managed R2 CORS must allow browser `PUT` plus the signed `Content-MD5` header and expose `ETag`.
|
|
@@ -747,7 +747,7 @@ const recorder = new VidtreoRecorder({
|
|
|
747
747
|
|
|
748
748
|
| Code | Meaning |
|
|
749
749
|
|------|---------|
|
|
750
|
-
| `upload.insufficient-credits` | The backend rejected the upload with HTTP 402 because the account has no credits left. The upload is
|
|
750
|
+
| `upload.insufficient-credits` | The backend rejected the upload with HTTP 402 because the account has no credits left. The upload stays queued and is retried every 5 minutes (and when the queue starts), and `onUploadError` receives this error on every rejected attempt. Repeated rejections of the same upload carry `isRepeatedRejection: true` (check it with `isRepeatedUploadRejection(error)`); the built-in UIs use it to show their dialog only once. |
|
|
751
751
|
|
|
752
752
|
`getUploadErrorCode` returns `null` for every other error (network failures, timeouts, and so on). Those errors are retried automatically before they are reported.
|
|
753
753
|
|
|
@@ -1113,7 +1113,7 @@ handler.destroy();
|
|
|
1113
1113
|
```
|
|
1114
1114
|
|
|
1115
1115
|
- `startUploadQueue(callbacks?)` opens the IndexedDB queue and immediately resumes uploads left over from a previous page load (same idempotency key, only missing parts are sent). Its callbacks receive events for those resumed uploads and for queue-level errors. It resolves to `false` when persistent storage is unavailable; uploads then go directly to the backend.
|
|
1116
|
-
- `processAndUpload(onTranscodeProgress, onUploadProgress, onUploadProgressDetails?, onUploadRetry?)` queues the transcoded file and resolves with a `NativeCameraUploadResult` (`RecordingUploadResult & { blob: Blob }`) once the upload is confirmed. It rejects only with a terminal error, after the queue has exhausted its retries or
|
|
1116
|
+
- `processAndUpload(onTranscodeProgress, onUploadProgress, onUploadProgressDetails?, onUploadRetry?)` queues the transcoded file and resolves with a `NativeCameraUploadResult` (`RecordingUploadResult & { blob: Blob }`) once the upload is confirmed. It rejects only with a terminal error, after the queue has exhausted its retries, or with `upload.insufficient-credits` on the first credit rejection; that upload stays queued and keeps retrying, and later rejections and the final success reach the `startUploadQueue` callbacks, so call `startUploadQueue` (with those callbacks) if you want to receive them. Events for this upload go to the per-call callbacks, not to the `startUploadQueue` callbacks.
|
|
1117
1117
|
- Calling `processAndUpload` without `startUploadQueue` opens the queue on demand.
|
|
1118
1118
|
- `destroy()` stops the queue timers. An upload that is still pending stays in IndexedDB and resumes the next time a handler or recorder starts the queue.
|
|
1119
1119
|
|
package/dist/index.d.ts
CHANGED
|
@@ -275,6 +275,8 @@ export {};
|
|
|
275
275
|
|
|
276
276
|
export {};
|
|
277
277
|
|
|
278
|
+
export {};
|
|
279
|
+
|
|
278
280
|
export type VidtreoRecorderConfig = {
|
|
279
281
|
apiKey?: string;
|
|
280
282
|
getAccessToken?: AccessTokenProvider;
|
|
@@ -3666,6 +3668,8 @@ export declare class RecorderController {
|
|
|
3666
3668
|
prepareLocalRecording(blob: Blob): Promise<LocalRecording>;
|
|
3667
3669
|
uploadVideo(blob: Blob, apiKey: string | null, backendUrl: string, metadata: Record<string, unknown>, getAccessToken?: AccessTokenProvider): Promise<void>;
|
|
3668
3670
|
private uploadVideoAfterStatusClear;
|
|
3671
|
+
private handleQueuedUploadError;
|
|
3672
|
+
private sendUploadFailedTelemetry;
|
|
3669
3673
|
private uploadDirectlyWithoutQueue;
|
|
3670
3674
|
getStream(): MediaStream | null;
|
|
3671
3675
|
isConfigReady(): boolean;
|
|
@@ -3777,6 +3781,7 @@ export declare class UploadQueueManager {
|
|
|
3777
3781
|
private readonly networkOnlineHandler;
|
|
3778
3782
|
private readonly progressFloors;
|
|
3779
3783
|
private readonly interruptedUploadIds;
|
|
3784
|
+
private readonly creditRejectedUploadIds;
|
|
3780
3785
|
private isProcessing;
|
|
3781
3786
|
private hasRecoveredStaleUploads;
|
|
3782
3787
|
private hasNetworkRecovered;
|
|
@@ -3802,11 +3807,14 @@ export declare class UploadQueueManager {
|
|
|
3802
3807
|
private canProcessMatchingUpload;
|
|
3803
3808
|
private matchesMetadata;
|
|
3804
3809
|
private processUpload;
|
|
3810
|
+
private handleUploadFailure;
|
|
3805
3811
|
private isResumedUpload;
|
|
3806
3812
|
private resolveRetryCount;
|
|
3807
3813
|
private reportUploadProgress;
|
|
3808
3814
|
private forgetUploadProgress;
|
|
3809
3815
|
private isOffline;
|
|
3816
|
+
private getRemainingRetryDelay;
|
|
3817
|
+
private resolveRetryDelay;
|
|
3810
3818
|
private calculateRetryDelay;
|
|
3811
3819
|
private scheduleRetry;
|
|
3812
3820
|
private clearTimer;
|
|
@@ -3817,10 +3825,13 @@ export declare const UPLOAD_ERROR_INSUFFICIENT_CREDITS = "upload.insufficient-cr
|
|
|
3817
3825
|
export type UploadErrorCode = typeof UPLOAD_ERROR_INSUFFICIENT_CREDITS;
|
|
3818
3826
|
export type UploadError = Error & {
|
|
3819
3827
|
code: UploadErrorCode;
|
|
3828
|
+
isRepeatedRejection?: true;
|
|
3820
3829
|
};
|
|
3821
3830
|
export declare function createUploadError(code: UploadErrorCode, message: string): UploadError;
|
|
3822
3831
|
export declare function getUploadErrorCode(error: unknown): UploadErrorCode | null;
|
|
3823
|
-
export declare function
|
|
3832
|
+
export declare function isInsufficientCreditsError(error: unknown): boolean;
|
|
3833
|
+
export declare function markRepeatedUploadRejection(error: Error): void;
|
|
3834
|
+
export declare function isRepeatedUploadRejection(error: unknown): boolean;
|
|
3824
3835
|
|
|
3825
3836
|
export type PartHasher = {
|
|
3826
3837
|
hash: (part: Blob) => Promise<PartChecksums>;
|
package/dist/index.js
CHANGED
|
@@ -2937,8 +2937,14 @@ function getUploadErrorCode(error) {
|
|
|
2937
2937
|
}
|
|
2938
2938
|
return null;
|
|
2939
2939
|
}
|
|
2940
|
-
function
|
|
2941
|
-
return getUploadErrorCode(error) ===
|
|
2940
|
+
function isInsufficientCreditsError(error) {
|
|
2941
|
+
return getUploadErrorCode(error) === UPLOAD_ERROR_INSUFFICIENT_CREDITS;
|
|
2942
|
+
}
|
|
2943
|
+
function markRepeatedUploadRejection(error) {
|
|
2944
|
+
Object.assign(error, { isRepeatedRejection: true });
|
|
2945
|
+
}
|
|
2946
|
+
function isRepeatedUploadRejection(error) {
|
|
2947
|
+
return error instanceof Error && "isRepeatedRejection" in error && error.isRepeatedRejection === true;
|
|
2942
2948
|
}
|
|
2943
2949
|
|
|
2944
2950
|
// src/core/upload/upload-queue.ts
|
|
@@ -2947,6 +2953,7 @@ var INITIAL_RETRY_DELAY = 2000;
|
|
|
2947
2953
|
var MAX_RETRY_DELAY = 300000;
|
|
2948
2954
|
var RETRY_MULTIPLIER = 1.5;
|
|
2949
2955
|
var PROCESSING_INTERVAL = 5000;
|
|
2956
|
+
var creditRejectedUploadIdsInPageSession = new Set;
|
|
2950
2957
|
|
|
2951
2958
|
class UploadQueueManager {
|
|
2952
2959
|
getAccessToken;
|
|
@@ -2956,6 +2963,7 @@ class UploadQueueManager {
|
|
|
2956
2963
|
networkOnlineHandler;
|
|
2957
2964
|
progressFloors = new Map;
|
|
2958
2965
|
interruptedUploadIds = new Set;
|
|
2966
|
+
creditRejectedUploadIds = new Set;
|
|
2959
2967
|
isProcessing = false;
|
|
2960
2968
|
hasRecoveredStaleUploads = false;
|
|
2961
2969
|
hasNetworkRecovered = false;
|
|
@@ -3016,20 +3024,19 @@ class UploadQueueManager {
|
|
|
3016
3024
|
const failedUploads = await this.storageService.getPendingUploads("failed");
|
|
3017
3025
|
const retryableUploads = failedUploads.filter((upload) => upload.retryCount < MAX_RETRIES2);
|
|
3018
3026
|
if (retryableUploads.length > 0) {
|
|
3019
|
-
const
|
|
3020
|
-
const delay = this.calculateRetryDelay(upload.retryCount);
|
|
3021
|
-
const timeSinceLastAttempt = Date.now() - upload.updatedAt;
|
|
3022
|
-
const isRetryDue = this.hasNetworkRecovered || timeSinceLastAttempt >= delay;
|
|
3027
|
+
const hasNetworkRecovered = this.hasNetworkRecovered;
|
|
3023
3028
|
this.hasNetworkRecovered = false;
|
|
3024
|
-
|
|
3029
|
+
const dueUploads = retryableUploads.filter((upload) => hasNetworkRecovered || this.getRemainingRetryDelay(upload) <= 0);
|
|
3030
|
+
if (dueUploads.length > 0) {
|
|
3031
|
+
const upload = this.getOldestFailedUpload(dueUploads);
|
|
3025
3032
|
await this.storageService.updateUploadStatus(upload.id, {
|
|
3026
3033
|
status: "pending",
|
|
3027
3034
|
retryCount: upload.retryCount
|
|
3028
3035
|
});
|
|
3029
3036
|
await this.processUpload(upload);
|
|
3030
3037
|
} else {
|
|
3031
|
-
const
|
|
3032
|
-
this.scheduleRetry(
|
|
3038
|
+
const remainingDelays = retryableUploads.map((upload) => this.getRemainingRetryDelay(upload));
|
|
3039
|
+
this.scheduleRetry(Math.min(...remainingDelays));
|
|
3033
3040
|
}
|
|
3034
3041
|
}
|
|
3035
3042
|
this.isProcessing = false;
|
|
@@ -3059,7 +3066,7 @@ class UploadQueueManager {
|
|
|
3059
3066
|
return result;
|
|
3060
3067
|
} catch (error) {
|
|
3061
3068
|
this.isProcessing = false;
|
|
3062
|
-
if (
|
|
3069
|
+
if (isInsufficientCreditsError(error)) {
|
|
3063
3070
|
throw error;
|
|
3064
3071
|
}
|
|
3065
3072
|
throw new Error(`Error processing matching upload: ${extractErrorMessage(error)}`);
|
|
@@ -3187,57 +3194,80 @@ class UploadQueueManager {
|
|
|
3187
3194
|
this.callbacks.onUploadComplete?.(upload.id, result);
|
|
3188
3195
|
return result;
|
|
3189
3196
|
} catch (error) {
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
const retryCount = this.resolveRetryCount(upload, isRetryable);
|
|
3194
|
-
const willRetry = retryCount < MAX_RETRIES2;
|
|
3195
|
-
const retryDelayMs = willRetry ? this.calculateRetryDelay(retryCount) : null;
|
|
3196
|
-
await this.storageService.updateUploadStatus(upload.id, {
|
|
3197
|
-
status: "failed",
|
|
3198
|
-
retryCount,
|
|
3199
|
-
lastError: errorMessage
|
|
3200
|
-
});
|
|
3201
|
-
this.callbacks.onUploadAttemptFailed?.(upload.id, {
|
|
3197
|
+
return this.handleUploadFailure({
|
|
3198
|
+
upload,
|
|
3199
|
+
error,
|
|
3202
3200
|
attempt,
|
|
3201
|
+
progressDetails,
|
|
3202
|
+
shouldThrowOnError
|
|
3203
|
+
});
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
async handleUploadFailure({
|
|
3207
|
+
upload,
|
|
3208
|
+
error,
|
|
3209
|
+
attempt,
|
|
3210
|
+
progressDetails,
|
|
3211
|
+
shouldThrowOnError
|
|
3212
|
+
}) {
|
|
3213
|
+
const errorMessage = extractErrorMessage(error);
|
|
3214
|
+
const isCreditRejected = isInsufficientCreditsError(error);
|
|
3215
|
+
const uploadError = error instanceof Error ? error : new Error(errorMessage);
|
|
3216
|
+
const retryCount = this.resolveRetryCount(upload, isCreditRejected);
|
|
3217
|
+
const willRetry = retryCount < MAX_RETRIES2;
|
|
3218
|
+
if (isCreditRejected) {
|
|
3219
|
+
if (creditRejectedUploadIdsInPageSession.has(upload.id)) {
|
|
3220
|
+
markRepeatedUploadRejection(uploadError);
|
|
3221
|
+
}
|
|
3222
|
+
creditRejectedUploadIdsInPageSession.add(upload.id);
|
|
3223
|
+
this.creditRejectedUploadIds.add(upload.id);
|
|
3224
|
+
} else {
|
|
3225
|
+
this.creditRejectedUploadIds.delete(upload.id);
|
|
3226
|
+
}
|
|
3227
|
+
const retryDelayMs = this.resolveRetryDelay(upload.id, retryCount);
|
|
3228
|
+
await this.storageService.updateUploadStatus(upload.id, {
|
|
3229
|
+
status: "failed",
|
|
3230
|
+
retryCount,
|
|
3231
|
+
lastError: errorMessage
|
|
3232
|
+
});
|
|
3233
|
+
this.callbacks.onUploadAttemptFailed?.(upload.id, {
|
|
3234
|
+
attempt,
|
|
3235
|
+
maxAttempts: MAX_RETRIES2,
|
|
3236
|
+
loadedBytes: progressDetails.loadedBytes,
|
|
3237
|
+
totalBytes: progressDetails.totalBytes,
|
|
3238
|
+
reason: errorMessage,
|
|
3239
|
+
retryDelayMs: willRetry ? retryDelayMs : null,
|
|
3240
|
+
willRetry
|
|
3241
|
+
});
|
|
3242
|
+
if (!willRetry) {
|
|
3243
|
+
this.forgetUploadProgress(upload.id);
|
|
3244
|
+
}
|
|
3245
|
+
if (willRetry && !isCreditRejected) {
|
|
3246
|
+
this.callbacks.onUploadRetry?.(upload.id, {
|
|
3247
|
+
attempt: attempt + 1,
|
|
3203
3248
|
maxAttempts: MAX_RETRIES2,
|
|
3204
|
-
loadedBytes: progressDetails.loadedBytes,
|
|
3205
|
-
totalBytes: progressDetails.totalBytes,
|
|
3206
3249
|
reason: errorMessage,
|
|
3207
|
-
retryDelayMs
|
|
3208
|
-
willRetry
|
|
3250
|
+
retryDelayMs
|
|
3209
3251
|
});
|
|
3210
|
-
if (!willRetry) {
|
|
3211
|
-
this.forgetUploadProgress(upload.id);
|
|
3212
|
-
}
|
|
3213
|
-
if (willRetry) {
|
|
3214
|
-
const delay = this.calculateRetryDelay(retryCount);
|
|
3215
|
-
this.callbacks.onUploadRetry?.(upload.id, {
|
|
3216
|
-
attempt: attempt + 1,
|
|
3217
|
-
maxAttempts: MAX_RETRIES2,
|
|
3218
|
-
reason: errorMessage,
|
|
3219
|
-
retryDelayMs: delay
|
|
3220
|
-
});
|
|
3221
|
-
this.scheduleRetry(delay);
|
|
3222
|
-
} else if (isRetryable) {
|
|
3223
|
-
this.callbacks.onUploadError?.(upload.id, new Error(`Upload failed after ${MAX_RETRIES2} attempts: ${errorMessage}`));
|
|
3224
|
-
} else {
|
|
3225
|
-
this.callbacks.onUploadError?.(upload.id, uploadError);
|
|
3226
|
-
}
|
|
3227
|
-
if (shouldThrowOnError) {
|
|
3228
|
-
throw isRetryable ? new Error(errorMessage) : uploadError;
|
|
3229
|
-
}
|
|
3230
|
-
return null;
|
|
3231
3252
|
}
|
|
3253
|
+
if (willRetry) {
|
|
3254
|
+
this.scheduleRetry(retryDelayMs);
|
|
3255
|
+
}
|
|
3256
|
+
if (isCreditRejected) {
|
|
3257
|
+
this.callbacks.onUploadError?.(upload.id, uploadError);
|
|
3258
|
+
} else if (!willRetry) {
|
|
3259
|
+
this.callbacks.onUploadError?.(upload.id, new Error(`Upload failed after ${MAX_RETRIES2} attempts: ${errorMessage}`));
|
|
3260
|
+
}
|
|
3261
|
+
if (shouldThrowOnError) {
|
|
3262
|
+
throw isCreditRejected ? uploadError : new Error(errorMessage);
|
|
3263
|
+
}
|
|
3264
|
+
return null;
|
|
3232
3265
|
}
|
|
3233
3266
|
isResumedUpload(upload) {
|
|
3234
|
-
return upload.retryCount > 0 || this.interruptedUploadIds.has(upload.id) || this.progressFloors.has(upload.id);
|
|
3267
|
+
return upload.retryCount > 0 || upload.status === "failed" || this.interruptedUploadIds.has(upload.id) || this.progressFloors.has(upload.id);
|
|
3235
3268
|
}
|
|
3236
|
-
resolveRetryCount(upload,
|
|
3237
|
-
if (
|
|
3238
|
-
return MAX_RETRIES2;
|
|
3239
|
-
}
|
|
3240
|
-
if (this.isOffline()) {
|
|
3269
|
+
resolveRetryCount(upload, isCreditRejected) {
|
|
3270
|
+
if (isCreditRejected || this.isOffline()) {
|
|
3241
3271
|
return upload.retryCount;
|
|
3242
3272
|
}
|
|
3243
3273
|
return upload.retryCount + 1;
|
|
@@ -3253,10 +3283,22 @@ class UploadQueueManager {
|
|
|
3253
3283
|
forgetUploadProgress(id) {
|
|
3254
3284
|
this.progressFloors.delete(id);
|
|
3255
3285
|
this.interruptedUploadIds.delete(id);
|
|
3286
|
+
this.creditRejectedUploadIds.delete(id);
|
|
3287
|
+
creditRejectedUploadIdsInPageSession.delete(id);
|
|
3256
3288
|
}
|
|
3257
3289
|
isOffline() {
|
|
3258
3290
|
return typeof navigator !== "undefined" && navigator.onLine === false;
|
|
3259
3291
|
}
|
|
3292
|
+
getRemainingRetryDelay(upload) {
|
|
3293
|
+
const delay = this.resolveRetryDelay(upload.id, upload.retryCount);
|
|
3294
|
+
return delay - (Date.now() - upload.updatedAt);
|
|
3295
|
+
}
|
|
3296
|
+
resolveRetryDelay(uploadId, retryCount) {
|
|
3297
|
+
if (this.creditRejectedUploadIds.has(uploadId)) {
|
|
3298
|
+
return MAX_RETRY_DELAY;
|
|
3299
|
+
}
|
|
3300
|
+
return this.calculateRetryDelay(retryCount);
|
|
3301
|
+
}
|
|
3260
3302
|
calculateRetryDelay(retryCount) {
|
|
3261
3303
|
const delay = INITIAL_RETRY_DELAY * RETRY_MULTIPLIER ** (retryCount - 1);
|
|
3262
3304
|
return Math.min(delay, MAX_RETRY_DELAY);
|
|
@@ -9049,7 +9091,7 @@ function resolveDeviceError(input) {
|
|
|
9049
9091
|
// package.json
|
|
9050
9092
|
var package_default = {
|
|
9051
9093
|
name: "@vidtreo/recorder",
|
|
9052
|
-
version: "1.
|
|
9094
|
+
version: "1.10.0",
|
|
9053
9095
|
type: "module",
|
|
9054
9096
|
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.",
|
|
9055
9097
|
main: "./dist/index.js",
|
|
@@ -32528,6 +32570,30 @@ class RecorderController {
|
|
|
32528
32570
|
sourceType
|
|
32529
32571
|
});
|
|
32530
32572
|
}
|
|
32573
|
+
handleQueuedUploadError(id, error) {
|
|
32574
|
+
this.isUploadInProgress = false;
|
|
32575
|
+
this.uploadCallbacks.onError(error);
|
|
32576
|
+
const metadata = this.uploadMetadataManager.getMetadata(id);
|
|
32577
|
+
if (isInsufficientCreditsError(error)) {
|
|
32578
|
+
if (metadata && !isRepeatedUploadRejection(error)) {
|
|
32579
|
+
this.sendUploadFailedTelemetry(metadata, error);
|
|
32580
|
+
}
|
|
32581
|
+
return;
|
|
32582
|
+
}
|
|
32583
|
+
if (metadata) {
|
|
32584
|
+
this.sendUploadFailedTelemetry(metadata, error);
|
|
32585
|
+
this.uploadMetadataManager.clearMetadata(id);
|
|
32586
|
+
}
|
|
32587
|
+
this.uploadAttemptFailureById.delete(id);
|
|
32588
|
+
this.uploadAttemptSuccessById.delete(id);
|
|
32589
|
+
}
|
|
32590
|
+
sendUploadFailedTelemetry(metadata, error) {
|
|
32591
|
+
this.telemetryManager.sendEvent("upload.failed", {
|
|
32592
|
+
filename: metadata.filename,
|
|
32593
|
+
duration: metadata.duration,
|
|
32594
|
+
sourceType: metadata.sourceType
|
|
32595
|
+
}, error);
|
|
32596
|
+
}
|
|
32531
32597
|
async uploadDirectlyWithoutQueue(options) {
|
|
32532
32598
|
if (!this.uploadService) {
|
|
32533
32599
|
const error = new Error("Upload service not available");
|
|
@@ -32952,19 +33018,7 @@ class RecorderController {
|
|
|
32952
33018
|
this.uploadAttemptSuccessById.delete(id);
|
|
32953
33019
|
},
|
|
32954
33020
|
onUploadError: (id, error) => {
|
|
32955
|
-
this.
|
|
32956
|
-
this.uploadCallbacks.onError(error);
|
|
32957
|
-
const metadata = this.uploadMetadataManager.getMetadata(id);
|
|
32958
|
-
if (metadata) {
|
|
32959
|
-
this.telemetryManager.sendEvent("upload.failed", {
|
|
32960
|
-
filename: metadata.filename,
|
|
32961
|
-
duration: metadata.duration,
|
|
32962
|
-
sourceType: metadata.sourceType
|
|
32963
|
-
}, error);
|
|
32964
|
-
this.uploadMetadataManager.clearMetadata(id);
|
|
32965
|
-
}
|
|
32966
|
-
this.uploadAttemptFailureById.delete(id);
|
|
32967
|
-
this.uploadAttemptSuccessById.delete(id);
|
|
33021
|
+
this.handleQueuedUploadError(id, error);
|
|
32968
33022
|
}
|
|
32969
33023
|
});
|
|
32970
33024
|
this.uploadQueueManager.processQueue().catch((error) => {
|
|
@@ -33727,6 +33781,7 @@ export {
|
|
|
33727
33781
|
isCredentialConflictError,
|
|
33728
33782
|
isMobileDevice2 as isMobileDevice,
|
|
33729
33783
|
isPictureInPictureSupported,
|
|
33784
|
+
isRepeatedUploadRejection,
|
|
33730
33785
|
logger,
|
|
33731
33786
|
mapPresetToConfig,
|
|
33732
33787
|
parseAudioRecordingConfig,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vidtreo/recorder",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
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",
|