@vidtreo/recorder 1.9.0 → 1.11.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 +14 -1
- package/dist/index.js +135 -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;
|
|
@@ -453,6 +455,7 @@ export type TranscodedAudio = {
|
|
|
453
455
|
};
|
|
454
456
|
|
|
455
457
|
export declare const DISABLED_AUDIO_RECORDING_CONFIG: Readonly<AudioRecordingConfig>;
|
|
458
|
+
export declare const DEMO_AUDIO_RECORDING_CONFIG: Readonly<AudioRecordingConfig>;
|
|
456
459
|
export declare function parseAudioRecordingConfig(value: unknown): AudioRecordingConfig;
|
|
457
460
|
export declare function requireEnabledAudioRecordingConfig(config: AudioRecordingConfig | undefined): AudioRecordingConfig;
|
|
458
461
|
|
|
@@ -1121,6 +1124,7 @@ export declare class ConfigManager {
|
|
|
1121
1124
|
private configFetched;
|
|
1122
1125
|
private configReady;
|
|
1123
1126
|
initialize(apiKey: string | null, backendUrl: string | null, getAccessToken?: AccessTokenProvider | null): Promise<void>;
|
|
1127
|
+
applyDemoConfig(): void;
|
|
1124
1128
|
fetchConfig(): Promise<void>;
|
|
1125
1129
|
getConfig(): Promise<TranscodeConfig>;
|
|
1126
1130
|
getConfigForRecording(): TranscodeConfig;
|
|
@@ -3666,6 +3670,8 @@ export declare class RecorderController {
|
|
|
3666
3670
|
prepareLocalRecording(blob: Blob): Promise<LocalRecording>;
|
|
3667
3671
|
uploadVideo(blob: Blob, apiKey: string | null, backendUrl: string, metadata: Record<string, unknown>, getAccessToken?: AccessTokenProvider): Promise<void>;
|
|
3668
3672
|
private uploadVideoAfterStatusClear;
|
|
3673
|
+
private handleQueuedUploadError;
|
|
3674
|
+
private sendUploadFailedTelemetry;
|
|
3669
3675
|
private uploadDirectlyWithoutQueue;
|
|
3670
3676
|
getStream(): MediaStream | null;
|
|
3671
3677
|
isConfigReady(): boolean;
|
|
@@ -3777,6 +3783,7 @@ export declare class UploadQueueManager {
|
|
|
3777
3783
|
private readonly networkOnlineHandler;
|
|
3778
3784
|
private readonly progressFloors;
|
|
3779
3785
|
private readonly interruptedUploadIds;
|
|
3786
|
+
private readonly creditRejectedUploadIds;
|
|
3780
3787
|
private isProcessing;
|
|
3781
3788
|
private hasRecoveredStaleUploads;
|
|
3782
3789
|
private hasNetworkRecovered;
|
|
@@ -3802,11 +3809,14 @@ export declare class UploadQueueManager {
|
|
|
3802
3809
|
private canProcessMatchingUpload;
|
|
3803
3810
|
private matchesMetadata;
|
|
3804
3811
|
private processUpload;
|
|
3812
|
+
private handleUploadFailure;
|
|
3805
3813
|
private isResumedUpload;
|
|
3806
3814
|
private resolveRetryCount;
|
|
3807
3815
|
private reportUploadProgress;
|
|
3808
3816
|
private forgetUploadProgress;
|
|
3809
3817
|
private isOffline;
|
|
3818
|
+
private getRemainingRetryDelay;
|
|
3819
|
+
private resolveRetryDelay;
|
|
3810
3820
|
private calculateRetryDelay;
|
|
3811
3821
|
private scheduleRetry;
|
|
3812
3822
|
private clearTimer;
|
|
@@ -3817,10 +3827,13 @@ export declare const UPLOAD_ERROR_INSUFFICIENT_CREDITS = "upload.insufficient-cr
|
|
|
3817
3827
|
export type UploadErrorCode = typeof UPLOAD_ERROR_INSUFFICIENT_CREDITS;
|
|
3818
3828
|
export type UploadError = Error & {
|
|
3819
3829
|
code: UploadErrorCode;
|
|
3830
|
+
isRepeatedRejection?: true;
|
|
3820
3831
|
};
|
|
3821
3832
|
export declare function createUploadError(code: UploadErrorCode, message: string): UploadError;
|
|
3822
3833
|
export declare function getUploadErrorCode(error: unknown): UploadErrorCode | null;
|
|
3823
|
-
export declare function
|
|
3834
|
+
export declare function isInsufficientCreditsError(error: unknown): boolean;
|
|
3835
|
+
export declare function markRepeatedUploadRejection(error: Error): void;
|
|
3836
|
+
export declare function isRepeatedUploadRejection(error: unknown): boolean;
|
|
3824
3837
|
|
|
3825
3838
|
export type PartHasher = {
|
|
3826
3839
|
hash: (part: Blob) => Promise<PartChecksums>;
|
package/dist/index.js
CHANGED
|
@@ -544,6 +544,10 @@ var DISABLED_AUDIO_RECORDING_CONFIG = Object.freeze({
|
|
|
544
544
|
autoGain: true,
|
|
545
545
|
normalize: false
|
|
546
546
|
});
|
|
547
|
+
var DEMO_AUDIO_RECORDING_CONFIG = Object.freeze({
|
|
548
|
+
...DISABLED_AUDIO_RECORDING_CONFIG,
|
|
549
|
+
enabled: true
|
|
550
|
+
});
|
|
547
551
|
function isRecord(value) {
|
|
548
552
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
549
553
|
}
|
|
@@ -1098,6 +1102,12 @@ class ConfigManager {
|
|
|
1098
1102
|
}
|
|
1099
1103
|
this.fetchConfigInBackground();
|
|
1100
1104
|
}
|
|
1105
|
+
applyDemoConfig() {
|
|
1106
|
+
this.currentConfig = {
|
|
1107
|
+
...DEFAULT_TRANSCODE_CONFIG,
|
|
1108
|
+
audioRecording: { ...DEMO_AUDIO_RECORDING_CONFIG }
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1101
1111
|
async fetchConfig() {
|
|
1102
1112
|
if (!this.configService) {
|
|
1103
1113
|
return;
|
|
@@ -2937,8 +2947,14 @@ function getUploadErrorCode(error) {
|
|
|
2937
2947
|
}
|
|
2938
2948
|
return null;
|
|
2939
2949
|
}
|
|
2940
|
-
function
|
|
2941
|
-
return getUploadErrorCode(error) ===
|
|
2950
|
+
function isInsufficientCreditsError(error) {
|
|
2951
|
+
return getUploadErrorCode(error) === UPLOAD_ERROR_INSUFFICIENT_CREDITS;
|
|
2952
|
+
}
|
|
2953
|
+
function markRepeatedUploadRejection(error) {
|
|
2954
|
+
Object.assign(error, { isRepeatedRejection: true });
|
|
2955
|
+
}
|
|
2956
|
+
function isRepeatedUploadRejection(error) {
|
|
2957
|
+
return error instanceof Error && "isRepeatedRejection" in error && error.isRepeatedRejection === true;
|
|
2942
2958
|
}
|
|
2943
2959
|
|
|
2944
2960
|
// src/core/upload/upload-queue.ts
|
|
@@ -2947,6 +2963,7 @@ var INITIAL_RETRY_DELAY = 2000;
|
|
|
2947
2963
|
var MAX_RETRY_DELAY = 300000;
|
|
2948
2964
|
var RETRY_MULTIPLIER = 1.5;
|
|
2949
2965
|
var PROCESSING_INTERVAL = 5000;
|
|
2966
|
+
var creditRejectedUploadIdsInPageSession = new Set;
|
|
2950
2967
|
|
|
2951
2968
|
class UploadQueueManager {
|
|
2952
2969
|
getAccessToken;
|
|
@@ -2956,6 +2973,7 @@ class UploadQueueManager {
|
|
|
2956
2973
|
networkOnlineHandler;
|
|
2957
2974
|
progressFloors = new Map;
|
|
2958
2975
|
interruptedUploadIds = new Set;
|
|
2976
|
+
creditRejectedUploadIds = new Set;
|
|
2959
2977
|
isProcessing = false;
|
|
2960
2978
|
hasRecoveredStaleUploads = false;
|
|
2961
2979
|
hasNetworkRecovered = false;
|
|
@@ -3016,20 +3034,19 @@ class UploadQueueManager {
|
|
|
3016
3034
|
const failedUploads = await this.storageService.getPendingUploads("failed");
|
|
3017
3035
|
const retryableUploads = failedUploads.filter((upload) => upload.retryCount < MAX_RETRIES2);
|
|
3018
3036
|
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;
|
|
3037
|
+
const hasNetworkRecovered = this.hasNetworkRecovered;
|
|
3023
3038
|
this.hasNetworkRecovered = false;
|
|
3024
|
-
|
|
3039
|
+
const dueUploads = retryableUploads.filter((upload) => hasNetworkRecovered || this.getRemainingRetryDelay(upload) <= 0);
|
|
3040
|
+
if (dueUploads.length > 0) {
|
|
3041
|
+
const upload = this.getOldestFailedUpload(dueUploads);
|
|
3025
3042
|
await this.storageService.updateUploadStatus(upload.id, {
|
|
3026
3043
|
status: "pending",
|
|
3027
3044
|
retryCount: upload.retryCount
|
|
3028
3045
|
});
|
|
3029
3046
|
await this.processUpload(upload);
|
|
3030
3047
|
} else {
|
|
3031
|
-
const
|
|
3032
|
-
this.scheduleRetry(
|
|
3048
|
+
const remainingDelays = retryableUploads.map((upload) => this.getRemainingRetryDelay(upload));
|
|
3049
|
+
this.scheduleRetry(Math.min(...remainingDelays));
|
|
3033
3050
|
}
|
|
3034
3051
|
}
|
|
3035
3052
|
this.isProcessing = false;
|
|
@@ -3059,7 +3076,7 @@ class UploadQueueManager {
|
|
|
3059
3076
|
return result;
|
|
3060
3077
|
} catch (error) {
|
|
3061
3078
|
this.isProcessing = false;
|
|
3062
|
-
if (
|
|
3079
|
+
if (isInsufficientCreditsError(error)) {
|
|
3063
3080
|
throw error;
|
|
3064
3081
|
}
|
|
3065
3082
|
throw new Error(`Error processing matching upload: ${extractErrorMessage(error)}`);
|
|
@@ -3187,57 +3204,80 @@ class UploadQueueManager {
|
|
|
3187
3204
|
this.callbacks.onUploadComplete?.(upload.id, result);
|
|
3188
3205
|
return result;
|
|
3189
3206
|
} 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, {
|
|
3207
|
+
return this.handleUploadFailure({
|
|
3208
|
+
upload,
|
|
3209
|
+
error,
|
|
3202
3210
|
attempt,
|
|
3211
|
+
progressDetails,
|
|
3212
|
+
shouldThrowOnError
|
|
3213
|
+
});
|
|
3214
|
+
}
|
|
3215
|
+
}
|
|
3216
|
+
async handleUploadFailure({
|
|
3217
|
+
upload,
|
|
3218
|
+
error,
|
|
3219
|
+
attempt,
|
|
3220
|
+
progressDetails,
|
|
3221
|
+
shouldThrowOnError
|
|
3222
|
+
}) {
|
|
3223
|
+
const errorMessage = extractErrorMessage(error);
|
|
3224
|
+
const isCreditRejected = isInsufficientCreditsError(error);
|
|
3225
|
+
const uploadError = error instanceof Error ? error : new Error(errorMessage);
|
|
3226
|
+
const retryCount = this.resolveRetryCount(upload, isCreditRejected);
|
|
3227
|
+
const willRetry = retryCount < MAX_RETRIES2;
|
|
3228
|
+
if (isCreditRejected) {
|
|
3229
|
+
if (creditRejectedUploadIdsInPageSession.has(upload.id)) {
|
|
3230
|
+
markRepeatedUploadRejection(uploadError);
|
|
3231
|
+
}
|
|
3232
|
+
creditRejectedUploadIdsInPageSession.add(upload.id);
|
|
3233
|
+
this.creditRejectedUploadIds.add(upload.id);
|
|
3234
|
+
} else {
|
|
3235
|
+
this.creditRejectedUploadIds.delete(upload.id);
|
|
3236
|
+
}
|
|
3237
|
+
const retryDelayMs = this.resolveRetryDelay(upload.id, retryCount);
|
|
3238
|
+
await this.storageService.updateUploadStatus(upload.id, {
|
|
3239
|
+
status: "failed",
|
|
3240
|
+
retryCount,
|
|
3241
|
+
lastError: errorMessage
|
|
3242
|
+
});
|
|
3243
|
+
this.callbacks.onUploadAttemptFailed?.(upload.id, {
|
|
3244
|
+
attempt,
|
|
3245
|
+
maxAttempts: MAX_RETRIES2,
|
|
3246
|
+
loadedBytes: progressDetails.loadedBytes,
|
|
3247
|
+
totalBytes: progressDetails.totalBytes,
|
|
3248
|
+
reason: errorMessage,
|
|
3249
|
+
retryDelayMs: willRetry ? retryDelayMs : null,
|
|
3250
|
+
willRetry
|
|
3251
|
+
});
|
|
3252
|
+
if (!willRetry) {
|
|
3253
|
+
this.forgetUploadProgress(upload.id);
|
|
3254
|
+
}
|
|
3255
|
+
if (willRetry && !isCreditRejected) {
|
|
3256
|
+
this.callbacks.onUploadRetry?.(upload.id, {
|
|
3257
|
+
attempt: attempt + 1,
|
|
3203
3258
|
maxAttempts: MAX_RETRIES2,
|
|
3204
|
-
loadedBytes: progressDetails.loadedBytes,
|
|
3205
|
-
totalBytes: progressDetails.totalBytes,
|
|
3206
3259
|
reason: errorMessage,
|
|
3207
|
-
retryDelayMs
|
|
3208
|
-
willRetry
|
|
3260
|
+
retryDelayMs
|
|
3209
3261
|
});
|
|
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
3262
|
}
|
|
3263
|
+
if (willRetry) {
|
|
3264
|
+
this.scheduleRetry(retryDelayMs);
|
|
3265
|
+
}
|
|
3266
|
+
if (isCreditRejected) {
|
|
3267
|
+
this.callbacks.onUploadError?.(upload.id, uploadError);
|
|
3268
|
+
} else if (!willRetry) {
|
|
3269
|
+
this.callbacks.onUploadError?.(upload.id, new Error(`Upload failed after ${MAX_RETRIES2} attempts: ${errorMessage}`));
|
|
3270
|
+
}
|
|
3271
|
+
if (shouldThrowOnError) {
|
|
3272
|
+
throw isCreditRejected ? uploadError : new Error(errorMessage);
|
|
3273
|
+
}
|
|
3274
|
+
return null;
|
|
3232
3275
|
}
|
|
3233
3276
|
isResumedUpload(upload) {
|
|
3234
|
-
return upload.retryCount > 0 || this.interruptedUploadIds.has(upload.id) || this.progressFloors.has(upload.id);
|
|
3277
|
+
return upload.retryCount > 0 || upload.status === "failed" || this.interruptedUploadIds.has(upload.id) || this.progressFloors.has(upload.id);
|
|
3235
3278
|
}
|
|
3236
|
-
resolveRetryCount(upload,
|
|
3237
|
-
if (
|
|
3238
|
-
return MAX_RETRIES2;
|
|
3239
|
-
}
|
|
3240
|
-
if (this.isOffline()) {
|
|
3279
|
+
resolveRetryCount(upload, isCreditRejected) {
|
|
3280
|
+
if (isCreditRejected || this.isOffline()) {
|
|
3241
3281
|
return upload.retryCount;
|
|
3242
3282
|
}
|
|
3243
3283
|
return upload.retryCount + 1;
|
|
@@ -3253,10 +3293,22 @@ class UploadQueueManager {
|
|
|
3253
3293
|
forgetUploadProgress(id) {
|
|
3254
3294
|
this.progressFloors.delete(id);
|
|
3255
3295
|
this.interruptedUploadIds.delete(id);
|
|
3296
|
+
this.creditRejectedUploadIds.delete(id);
|
|
3297
|
+
creditRejectedUploadIdsInPageSession.delete(id);
|
|
3256
3298
|
}
|
|
3257
3299
|
isOffline() {
|
|
3258
3300
|
return typeof navigator !== "undefined" && navigator.onLine === false;
|
|
3259
3301
|
}
|
|
3302
|
+
getRemainingRetryDelay(upload) {
|
|
3303
|
+
const delay = this.resolveRetryDelay(upload.id, upload.retryCount);
|
|
3304
|
+
return delay - (Date.now() - upload.updatedAt);
|
|
3305
|
+
}
|
|
3306
|
+
resolveRetryDelay(uploadId, retryCount) {
|
|
3307
|
+
if (this.creditRejectedUploadIds.has(uploadId)) {
|
|
3308
|
+
return MAX_RETRY_DELAY;
|
|
3309
|
+
}
|
|
3310
|
+
return this.calculateRetryDelay(retryCount);
|
|
3311
|
+
}
|
|
3260
3312
|
calculateRetryDelay(retryCount) {
|
|
3261
3313
|
const delay = INITIAL_RETRY_DELAY * RETRY_MULTIPLIER ** (retryCount - 1);
|
|
3262
3314
|
return Math.min(delay, MAX_RETRY_DELAY);
|
|
@@ -9049,7 +9101,7 @@ function resolveDeviceError(input) {
|
|
|
9049
9101
|
// package.json
|
|
9050
9102
|
var package_default = {
|
|
9051
9103
|
name: "@vidtreo/recorder",
|
|
9052
|
-
version: "1.
|
|
9104
|
+
version: "1.11.0",
|
|
9053
9105
|
type: "module",
|
|
9054
9106
|
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
9107
|
main: "./dist/index.js",
|
|
@@ -32143,6 +32195,9 @@ class RecorderController {
|
|
|
32143
32195
|
isDemo = config.demo;
|
|
32144
32196
|
}
|
|
32145
32197
|
this.isDemo = isDemo;
|
|
32198
|
+
if (isDemo) {
|
|
32199
|
+
this.configManager.applyDemoConfig();
|
|
32200
|
+
}
|
|
32146
32201
|
let apiKey = null;
|
|
32147
32202
|
if (typeof config.apiKey === "string" && config.apiKey.length > 0) {
|
|
32148
32203
|
apiKey = config.apiKey;
|
|
@@ -32528,6 +32583,30 @@ class RecorderController {
|
|
|
32528
32583
|
sourceType
|
|
32529
32584
|
});
|
|
32530
32585
|
}
|
|
32586
|
+
handleQueuedUploadError(id, error) {
|
|
32587
|
+
this.isUploadInProgress = false;
|
|
32588
|
+
this.uploadCallbacks.onError(error);
|
|
32589
|
+
const metadata = this.uploadMetadataManager.getMetadata(id);
|
|
32590
|
+
if (isInsufficientCreditsError(error)) {
|
|
32591
|
+
if (metadata && !isRepeatedUploadRejection(error)) {
|
|
32592
|
+
this.sendUploadFailedTelemetry(metadata, error);
|
|
32593
|
+
}
|
|
32594
|
+
return;
|
|
32595
|
+
}
|
|
32596
|
+
if (metadata) {
|
|
32597
|
+
this.sendUploadFailedTelemetry(metadata, error);
|
|
32598
|
+
this.uploadMetadataManager.clearMetadata(id);
|
|
32599
|
+
}
|
|
32600
|
+
this.uploadAttemptFailureById.delete(id);
|
|
32601
|
+
this.uploadAttemptSuccessById.delete(id);
|
|
32602
|
+
}
|
|
32603
|
+
sendUploadFailedTelemetry(metadata, error) {
|
|
32604
|
+
this.telemetryManager.sendEvent("upload.failed", {
|
|
32605
|
+
filename: metadata.filename,
|
|
32606
|
+
duration: metadata.duration,
|
|
32607
|
+
sourceType: metadata.sourceType
|
|
32608
|
+
}, error);
|
|
32609
|
+
}
|
|
32531
32610
|
async uploadDirectlyWithoutQueue(options) {
|
|
32532
32611
|
if (!this.uploadService) {
|
|
32533
32612
|
const error = new Error("Upload service not available");
|
|
@@ -32952,19 +33031,7 @@ class RecorderController {
|
|
|
32952
33031
|
this.uploadAttemptSuccessById.delete(id);
|
|
32953
33032
|
},
|
|
32954
33033
|
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);
|
|
33034
|
+
this.handleQueuedUploadError(id, error);
|
|
32968
33035
|
}
|
|
32969
33036
|
});
|
|
32970
33037
|
this.uploadQueueManager.processQueue().catch((error) => {
|
|
@@ -33727,6 +33794,7 @@ export {
|
|
|
33727
33794
|
isCredentialConflictError,
|
|
33728
33795
|
isMobileDevice2 as isMobileDevice,
|
|
33729
33796
|
isPictureInPictureSupported,
|
|
33797
|
+
isRepeatedUploadRejection,
|
|
33730
33798
|
logger,
|
|
33731
33799
|
mapPresetToConfig,
|
|
33732
33800
|
parseAudioRecordingConfig,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vidtreo/recorder",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.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",
|