@vidtreo/recorder-react 1.7.9 → 1.9.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 CHANGED
@@ -9,7 +9,7 @@ npm install @vidtreo/recorder-react
9
9
  ```
10
10
 
11
11
  **Peer Dependencies:**
12
- - `@vidtreo/recorder >=1.0.5`
12
+ - `@vidtreo/recorder >=1.9.0`
13
13
  - `react >=18.0.0`
14
14
  - `react-dom >=18.0.0`
15
15
 
@@ -46,12 +46,14 @@ import VidtreoRecorder from '@vidtreo/recorder-react';
46
46
 
47
47
  | Prop | Type | Required | Default | Description |
48
48
  |------|------|----------|---------|-------------|
49
- | `apiKey` | `string` | No* | - | API key for authentication. Required if `VIDTREO_API_KEY` env var not set. |
49
+ | `apiKey` | `string` | No* | - | API key for authentication. Required if `VIDTREO_API_KEY` env var not set and `getAccessToken` is not used. |
50
+ | `getAccessToken` | `() => Promise<string>` | No* | - | Returns a short-lived desktop access token minted by your server. Use instead of `apiKey` (exactly one of the two; passing both shows a configuration error, see [Credentials](#temporary-access-tokens)). Called again whenever a fresh token is needed, including when a queued upload resumes after a reload. |
51
+ | `audioOnly` | `boolean` | No | `false` | Record audio from the microphone only. See [Audio-Only Recording](#audio-only-recording) |
50
52
  | `countdownDuration` | `number` | No | - | Countdown duration in milliseconds before recording starts |
51
53
  | `minTimeRecord` | `number` | No | - | Minimum recording time in milliseconds before stop is allowed. Must not be greater than `maxRecordingTime` when both are positive. |
52
54
  | `maxRecordingTime` | `number` | No | - | Maximum recording time in milliseconds |
53
55
  | `userMetadata` | `Record<string, unknown>` | No | - | Custom metadata to attach to recordings |
54
- | `recordingPrompt` | `RecordingPrompt` | No | - | Prompt shown during recording. Use `countdown: { show, placement: "above-countdown" }` to float it over the countdown. |
56
+ | `recordingPrompt` | `RecordingPrompt` | No | - | Prompt shown during recording. Use `countdown: { show, placement: "above-countdown" }` to float it over the countdown. A `label` field is still accepted for backward compatibility but is not displayed. |
55
57
  | `enableSourceSwitching` | `boolean` | No | `true` | Enable switching between camera and screen |
56
58
  | `enableMute` | `boolean` | No | `true` | Enable mute/unmute functionality |
57
59
  | `enablePause` | `boolean` | No | `true` | Enable pause/resume functionality |
@@ -62,17 +64,40 @@ import VidtreoRecorder from '@vidtreo/recorder-react';
62
64
  | `maxFileSize` | `number` | No | `2560` | Maximum file size in megabytes (default: 2.5GB) |
63
65
  | `lang` | `string` | No | `"en"` | Language code for UI text (`"en"` or `"es"`) |
64
66
  | `texts` | `PartialTranslations` | No | - | Custom text overrides for localization |
65
- | `onUploadComplete` | `(result: { recordingId: string; uploadUrl: string }) => void` | No | - | Callback when upload completes |
66
- | `onUploadProgress` | `(progress: number) => void` | No | - | Callback for upload progress (0-1) |
67
- | `onUploadError` | `(error: Error) => void` | No | - | Callback when upload fails |
67
+ | `onUploadComplete` | `(result: RecordingUploadResult) => void` | No | - | Callback when upload completes. Same payload for the desktop/overlay recorder and native camera mode. See [Upload Completion Payload](#upload-completion-payload) |
68
+ | `onUploadProgress` | `(progress: number, phase: UploadProgressPhase \| null) => void` | No | - | Callback for upload progress (0-1) in every mode. `phase` is `"preparing"`, `"transferring"`, `"finalizing"`, `"completed"` or `null` when not reported yet (the first `0` call) |
69
+ | `onUploadError` | `(error: Error) => void` | No | - | Callback when upload fails. Use `getUploadErrorCode(error)` to read the [error code](#upload-errors) |
68
70
  | `onRecordingStart` | `() => void` | No | - | Callback when recording starts |
69
- | `onRecordingStop` | `() => void` | No | - | Callback when recording stops |
70
- | `onTranscodingProgress` | `(progress: number) => void` | No | - | Callback for transcoding progress (0-100) |
71
- | `onError` | `(error: Error) => void` | No | - | Callback for any errors |
71
+ | `onRecordingStop` | `() => void` | No | - | Callback when recording stops (once per recording, when finalization starts) |
72
+ | `onTranscodingProgress` | `(progress: number) => void` | No | - | Callback for transcoding progress (0-1). While it runs the recorder shows the `finishing` progress overlay |
73
+ | `onError` | `(error: Error) => void` | No | - | Callback for any errors, including the credential conflict configuration error |
72
74
 
73
75
  `tabVisibilityOverlayText` is optional. When `enableTabVisibilityOverlay` is enabled and text is omitted, the component
74
76
  uses a localized default based on `lang` and `texts`. Overlay rendering is skipped for screen capture recordings.
75
77
 
78
+ ### Audio-Only Recording
79
+
80
+ Set `audioOnly` to record from the microphone only. The camera is never requested and no video preview is rendered. The recorder shows a compact voice-note pill (about 20rem wide, 56px high): a red record/stop button, a live waveform of dots that freezes while paused or uploading, the elapsed time (with `/ 10:00` when the environment sets a maximum duration) and a microphone button that opens a menu with the microphone selector, plus pause/resume and mute while recording. Status text, permission requests, errors, upload progress (`Uploading 42%`) and the completion message appear in a small caption below the pill.
81
+
82
+ ```tsx
83
+ <VidtreoRecorder
84
+ apiKey="your-api-key"
85
+ audioOnly
86
+ onUploadComplete={(result) => {
87
+ if (result.mediaType === 'audio') {
88
+ console.log('Audio uploaded:', result.audioId);
89
+ }
90
+ }}
91
+ />
92
+ ```
93
+
94
+ - Audio recording must be enabled for the environment in the backend. When it is not, the recorder shows "Audio recording is not enabled for this environment" (`audioRecordingDisabled`) and `onError` receives an error whose `code` is `"recording.audio-disabled"` (`ERROR_AUDIO_RECORDING_DISABLED` from `@vidtreo/recorder`).
95
+ - The microphone selector, mute, pause/resume and stop work as for video, from the pill and its menu. The camera selector, source switching and the tab visibility overlay are not shown.
96
+ - The permission flow asks only for the microphone.
97
+ - On mobile, `overlay` and `embed` show the same pill. `mobileMode="native"` and `nativeCamera` are ignored and fall back to `overlay`, because the native camera only captures video.
98
+ - Format, bitrate, channels, processing and the maximum duration come from the environment audio settings. The recorder stops automatically at the maximum duration.
99
+ - Demo mode has no environment audio settings, so audio-only recording needs a real backend.
100
+
76
101
  ### Example with Callbacks
77
102
 
78
103
  ```tsx
@@ -82,8 +107,8 @@ uses a localized default based on `lang` and `texts`. Overlay rendering is skipp
82
107
  console.log('Recording uploaded:', result.recordingId);
83
108
  console.log('View at:', result.uploadUrl);
84
109
  }}
85
- onUploadProgress={(progress) => {
86
- console.log(`Upload: ${Math.round(progress * 100)}%`);
110
+ onUploadProgress={(progress, phase) => {
111
+ console.log(`Upload (${phase ?? "starting"}): ${Math.round(progress * 100)}%`);
87
112
  }}
88
113
  onError={(error) => {
89
114
  console.error('Error:', error.message);
@@ -91,6 +116,92 @@ uses a localized default based on `lang` and `texts`. Overlay rendering is skipp
91
116
  />
92
117
  ```
93
118
 
119
+ ### Temporary Access Tokens
120
+
121
+ ```tsx
122
+ <VidtreoRecorder
123
+ getAccessToken={async () => {
124
+ const response = await fetch('/api/vidtreo-token');
125
+ const { token } = await response.json();
126
+ return token;
127
+ }}
128
+ />
129
+ ```
130
+
131
+ The token is never stored. If an upload is interrupted, the component calls `getAccessToken` again when the upload resumes.
132
+
133
+ Pass either `apiKey` or `getAccessToken`, not both. The prop types reject both at compile time; at runtime (for example from JavaScript) the component renders the configuration error "Provide either apiKey or getAccessToken, not both" and calls `onError` once with an error whose `code` is `CONFIG_ERROR_CREDENTIAL_CONFLICT` (`"config.credential-conflict"`, from `@vidtreo/recorder`). Passing neither (outside demo mode, and without `VIDTREO_API_KEY` in the environment) renders the `errorApiKeyRequired` configuration error and calls `onError` once. Demo mode ignores credentials.
134
+
135
+ ### Progress Overlays
136
+
137
+ After you stop a recording the preview shows a progress overlay until the upload finishes:
138
+
139
+ | Stage | Label key | Progress |
140
+ |-------|-----------|----------|
141
+ | Finalizing the recording (before transcoding reports progress) | `finishing` | 0% |
142
+ | Transcoding | `finishing` | transcoding progress |
143
+ | Uploading | `uploading`, `finalizing` when the backend is finalizing, `retryingUpload` while a retry is pending | upload progress |
144
+
145
+ The same overlays show in the mobile overlay recorder. In native camera mode a resumed upload without a preview shows a standalone progress bar with the same labels.
146
+
147
+ ### Upload Completion Payload
148
+
149
+ `onUploadComplete` receives a `RecordingUploadResult`: every field of the backend upload result plus the backward-compatible `recordingId` and `uploadUrl`.
150
+
151
+ ```typescript
152
+ import type { RecordingUploadResult } from '@vidtreo/recorder-react';
153
+
154
+ type RecordingUploadResult = {
155
+ recordingId: string; // same value as id
156
+ uploadUrl: string; // "" when the backend has no URL yet
157
+ id: string;
158
+ publicId: string;
159
+ filename: string;
160
+ fileSize: number;
161
+ mimeType: string;
162
+ duration: number | null;
163
+ status: string;
164
+ createdAt: string;
165
+ transferStatus: 'pending' | 'transferring' | 'transferred' | 'failed' | null;
166
+ transferError: string | null;
167
+ transferredAt: string | null;
168
+ r2DeleteAfter: string | null;
169
+ r2DeletedAt: string | null;
170
+ mediaType?: 'audio' | 'video'; // 'audio' for audio-only recordings
171
+ audioId?: string; // the audio id, set for audio-only recordings
172
+ };
173
+ ```
174
+
175
+ ### Upload Errors
176
+
177
+ When an upload fails with a known cause, the component shows a dialog (`role="alertdialog"`) after recording and processing finish, and `onUploadError` receives an error with a code:
178
+
179
+ ```tsx
180
+ import VidtreoRecorder, {
181
+ getUploadErrorCode,
182
+ UPLOAD_ERROR_INSUFFICIENT_CREDITS,
183
+ } from '@vidtreo/recorder-react';
184
+
185
+ <VidtreoRecorder
186
+ apiKey="your-api-key"
187
+ onUploadError={(error) => {
188
+ if (getUploadErrorCode(error) === UPLOAD_ERROR_INSUFFICIENT_CREDITS) {
189
+ notifyAdmin();
190
+ }
191
+ }}
192
+ />
193
+ ```
194
+
195
+ | Code | Meaning |
196
+ |------|---------|
197
+ | `upload.insufficient-credits` | The account has no credits left (HTTP 402). The upload is not retried. |
198
+
199
+ `getUploadErrorCode` returns `null` for other errors. Network errors are retried automatically before they are reported.
200
+
201
+ ### Upload Resume
202
+
203
+ Uploads are queued in IndexedDB before they are sent, in every mode (desktop, mobile overlay, mobile embed, and native camera). If the page is reloaded or the connection drops, the next mounted `VidtreoRecorder` resumes the upload with the same idempotency key and sends only the parts that are missing. Progress shows again while it resumes, and `onUploadComplete` fires with the full payload when it finishes. Mount the component with the same credentials (`apiKey` or `getAccessToken`) so the upload can resume. If IndexedDB is unavailable, uploads go directly to the backend without persistence.
204
+
94
205
  ### Minimum Recording Time
95
206
 
96
207
  ```tsx
@@ -147,6 +258,10 @@ function CustomRecorder() {
147
258
  countdown: number | null;
148
259
  timer: string;
149
260
  uploadProgress: number | null;
261
+ uploadPhase: UploadProgressPhase | null; // 'preparing' | 'transferring' | 'finalizing' | 'completed'
262
+ uploadRetryAttempt: number | null;
263
+ uploadErrorCode: UploadErrorCode | null; // 'upload.insufficient-credits'
264
+ transcodingProgress: number | null;
150
265
  devices: {
151
266
  cameras: MediaDeviceInfo[];
152
267
  microphones: MediaDeviceInfo[];
@@ -158,15 +273,18 @@ function CustomRecorder() {
158
273
 
159
274
  ### Hook Actions
160
275
 
161
- - `startRecording(sourceType?: 'camera' | 'screen')` - Start recording
162
- - `stopRecording()` - Stop recording and upload
276
+ - `startRecording(sourceType?: 'camera' | 'screen' | 'audio')` - Start recording
277
+ - `stopRecording()` - Stop recording and upload. Resolves with `{ recordingId, uploadUrl, blob, mediaType }`
163
278
  - `pauseRecording()` - Pause recording
164
279
  - `resumeRecording()` - Resume recording
165
280
  - `toggleMute()` - Toggle mute state
166
281
  - `switchSource(sourceType: 'camera' | 'screen')` - Switch between camera and screen
282
+ - `reselectScreenSource()` - Pick another screen or window without changing the source type (resolves `false` when cancelled)
167
283
  - `changeCamera(deviceId: string)` - Change camera device
168
284
  - `changeMic(deviceId: string)` - Change microphone device
169
- - `startPreview(sourceType?: 'camera' | 'screen')` - Start preview stream
285
+ - `startPreview(sourceType?: 'camera' | 'screen' | 'audio')` - Start preview stream
286
+ - `downloadVideo()` - Download the recorded video (demo mode)
287
+ - `clearUploadError()` - Clear `uploadErrorCode` after the user dismisses the upload error
170
288
  - `cleanup()` - Clean up resources
171
289
 
172
290
  ### Hook Return
@@ -372,6 +490,70 @@ function App() {
372
490
  |-----|----------------|-----------------|
373
491
  | `initializingCamera` | Initializing camera... | Inicializando cámara... |
374
492
  | `grantPermissions` | Grant camera and microphone permissions when prompted | Otorga permisos de cámara y micrófono cuando se solicite |
493
+ | `browserUnsupported` | This browser is not supported | Este navegador no es compatible |
494
+ | `browserUnsupportedDynamic` | `{browser} ({version}) is not supported, please use <a href="https://www.google.com/chrome/" target="_blank" rel="noopener noreferrer">Chrome</a>` | `{browser} ({version}) no es compatible, por favor usa <a href="https://www.google.com/chrome/" target="_blank" rel="noopener noreferrer">Chrome</a>` |
495
+ | `browserUnsupportedSafari` | `Safari (latest stable) is supported. If you experience issues, please use <a href="https://www.google.com/chrome/" target="_blank" rel="noopener noreferrer">Chrome</a>` | `Safari (última versión estable) es compatible. Si experimentas problemas, por favor usa <a href="https://www.google.com/chrome/" target="_blank" rel="noopener noreferrer">Chrome</a>` |
496
+ | `browserUnsupportedFirefox` | `Firefox is not supported, please use <a href="https://www.google.com/chrome/" target="_blank" rel="noopener noreferrer">Chrome</a>` | `Firefox no es compatible, por favor use <a href="https://www.google.com/chrome/" target="_blank" rel="noopener noreferrer">Chrome</a>` |
497
+ | `retryCamera` | Retry Camera | Reintentar Cámara |
498
+ | `cameraInUse` | Your camera is being used by another application. Close other apps using the camera (Zoom, Teams, Discord) and try again. | Tu cámara está siendo usada por otra aplicación. Cierra otras apps que usen la cámara (Zoom, Teams, Discord) e intenta de nuevo. |
499
+ | `cameraNotFound` | No camera detected. Connect a camera or check your device settings. | No se detectó ninguna cámara. Conecta una cámara o revisa la configuración de tu dispositivo. |
500
+ | `cameraPermissionDenied` | Camera access was denied. Allow camera access in your browser settings and try again. | Se denegó el acceso a la cámara. Permite el acceso a la cámara en la configuración de tu navegador e intenta de nuevo. |
501
+ | `cameraTimeout` | The camera took too long to start. Close other apps using the camera and try again. | La cámara tardó demasiado en iniciar. Cierra otras apps que usen la cámara e intenta de nuevo. |
502
+ | `failedToStartCamera` | Failed to start camera | Error al iniciar la cámara |
503
+ | `audioInUse` | Your microphone is being used by another application. Close other apps using the microphone (Zoom, Teams, Discord) and try again. | Tu micrófono está siendo usado por otra aplicación. Cierra otras apps que usen el micrófono (Zoom, Teams, Discord) e intenta de nuevo. |
504
+ | `audioNotFound` | No microphone detected. Connect a microphone or check your device settings. | No se detectó ningún micrófono. Conecta un micrófono o revisa la configuración de tu dispositivo. |
505
+ | `audioPermissionDenied` | Microphone access was denied. Allow microphone access in your browser settings and try again. | Se denegó el acceso al micrófono. Permite el acceso al micrófono en la configuración de tu navegador e intenta de nuevo. |
506
+ | `audioTimeout` | The microphone took too long to start. Close other apps using the microphone and try again. | El micrófono tardó demasiado en iniciar. Cierra otras apps que usen el micrófono e intenta de nuevo. |
507
+ | `failedToStartAudio` | Failed to start microphone | Error al iniciar el micrófono |
508
+ | `audioErrorTitle` | Microphone unavailable | Micrófono no disponible |
509
+ | `audioErrorMessage` | We couldn't access your microphone. It may be in use by another app like Zoom, Teams, or Discord. Please close it and try again. | No pudimos acceder a tu micrófono. Puede estar en uso por otra app como Zoom, Teams o Discord. Ciérrala e inténtalo de nuevo. |
510
+ | `audioErrorRetry` | Try again | Intentar de nuevo |
511
+ | `audioMissingTitle` | Recording has no audio | La grabación no tiene audio |
512
+ | `audioMissingMessage` | We couldn't capture audio in this recording. Your microphone may have been disconnected or blocked by the browser. Check your microphone and record again. | No pudimos capturar audio en esta grabación. Es posible que el micrófono se haya desconectado o que el navegador lo haya bloqueado. Verifica tu micrófono y vuelve a grabar. |
513
+ | `audioSilentTitle` | Recording is silent | La grabación quedó en silencio |
514
+ | `audioSilentMessage` | We didn't detect any sound during the recording. Make sure your microphone is working and not muted by your operating system, then record again. | No detectamos sonido durante la grabación. Verifica que tu micrófono esté funcionando y no esté silenciado por el sistema, luego vuelve a grabar. |
515
+ | `audioProcessingErrorTitle` | Audio processing error | Error al procesar el audio |
516
+ | `audioProcessingErrorMessage` | We couldn't process the audio in this recording. Please record again. | No pudimos procesar el audio de esta grabación. Por favor vuelve a grabar. |
517
+ | `audioMissingRetry` | Record again | Grabar de nuevo |
518
+ | `audioWarningNoSignalTitle` | No microphone signal | Sin señal del micrófono |
519
+ | `audioWarningNoSignalMessage` | We're not detecting any sound. Check that your microphone is active. | No estamos detectando sonido. Verifica que tu micrófono esté activo. |
520
+ | `audioWarningLowSignalTitle` | Audio is very quiet | Audio muy bajo |
521
+ | `audioWarningLowSignalMessage` | Move closer to the microphone or turn up the input volume. | Acércate al micrófono o sube el volumen de entrada. |
522
+ | `audioWarningSpeakingMutedTitle` | You're speaking while muted | Estás hablando muteado |
523
+ | `audioWarningSpeakingMutedMessage` | Unmute your microphone if you want your voice in the recording. | Activa el micrófono si quieres que tu voz se escuche en la grabación. |
524
+ | `audioWarningTrackEndedTitle` | Microphone disconnected | Micrófono desconectado |
525
+ | `audioWarningTrackEndedMessage` | The microphone was disconnected. Reconnect it before continuing. | Se desconectó el micrófono. Reconéctalo antes de continuar. |
526
+ | `audioWarningNoChunksTitle` | No microphone input | Sin entrada del micrófono |
527
+ | `audioWarningNoChunksMessage` | We're not receiving audio from the microphone. Check browser permissions. | No estamos recibiendo audio del micrófono. Revisa los permisos del navegador. |
528
+ | `audioWarningTrackMutedTitle` | Microphone muted by browser | Micrófono silenciado por el navegador |
529
+ | `audioWarningTrackMutedMessage` | The browser muted your microphone. Check site settings. | El navegador silenció tu micrófono. Revisa la configuración del sitio. |
530
+ | `audioWarningDismiss` | Dismiss | Cerrar |
531
+ | `cameraErrorTitle` | Camera unavailable | Cámara no disponible |
532
+ | `cameraErrorMessage` | We couldn't access your camera. It may be in use by another app. Please close it and try again. | No pudimos acceder a tu cámara. Puede estar en uso por otra app. Ciérrala e inténtalo de nuevo. |
533
+ | `browserErrorTitle` | Browser not supported | Navegador no compatible |
534
+ | `browserErrorMessage` | Your browser doesn't support video recording. Please use the latest version of Chrome for the best experience. | Tu navegador no es compatible con la grabación de video. Por favor usa la última versión de Chrome. |
535
+ | `recordingCodecUnsupportedTitle` | Recording format not supported | Formato de grabación no compatible |
536
+ | `recordingCodecUnsupportedMessage` | This device can't use the selected video format. Close other apps, update Chrome if possible, and record again. | Este dispositivo no puede usar el formato de video seleccionado. Cierra otras apps, actualiza Chrome si es posible y vuelve a grabar. |
537
+ | `recordingMediaEmptyTitle` | No video was produced | No se generó ningún video |
538
+ | `recordingMediaEmptyMessage` | The browser didn't produce a usable video. Check your camera and record again. | El navegador no produjo un video utilizable. Verifica la cámara y vuelve a grabar. |
539
+ | `recordingMediaErrorTitle` | Recording could not be finished | No se pudo finalizar la grabación |
540
+ | `recordingMediaErrorMessage` | The browser reported an error before the video was ready to upload. Record again. | El navegador informó un error antes de preparar el video para subirlo. Vuelve a grabar. |
541
+ | `recordingServerTranscodeFallbackTitle` | Video processing failed | No se pudo procesar el video |
542
+ | `recordingServerTranscodeFallbackMessage` | We couldn't prepare this video for upload on your device. Record again. | No pudimos preparar este video para subirlo en tu dispositivo. Vuelve a grabar. |
543
+ | `recordingTrackEndedTitle` | Camera stopped during recording | La cámara se detuvo durante la grabación |
544
+ | `recordingTrackEndedMessage` | The camera feed ended before the recording finished. Check that the camera is still connected and record again. | La señal de la cámara terminó antes de finalizar la grabación. Verifica que la cámara siga conectada y vuelve a grabar. |
545
+ | `recordingFinalizeTimeoutTitle` | Recording took too long to finish | La grabación tardó demasiado en finalizar |
546
+ | `recordingFinalizeTimeoutMessage` | This device may be low on memory or processing power. Close other apps or browser tabs, then record again. | Es posible que este dispositivo tenga poca memoria o potencia de procesamiento. Cierra otras apps o pestañas del navegador y vuelve a grabar. |
547
+ | `recordingStateRaceTitle` | Recording did not start correctly | La grabación no inició correctamente |
548
+ | `recordingStateRaceMessage` | The recording could not be finalized because it did not start cleanly. Please refresh the page and record again. | No se pudo finalizar la grabación porque no inició correctamente. Actualiza la página y vuelve a grabar. |
549
+ | `recordingNoFramesTitle` | No video was captured | No se capturó video |
550
+ | `recordingNoFramesMessage` | We couldn't receive video frames from the camera. Check your camera connection and record again. | No pudimos recibir fotogramas de video de la cámara. Verifica la conexión de la cámara y vuelve a grabar. |
551
+ | `recordingUnsupportedTitle` | Recording not available | Grabación no disponible |
552
+ | `recordingUnsupportedMessage` | This device or browser can't record video reliably right now. Check your connection, update Chrome if possible, or use another device. | Este dispositivo o navegador no puede grabar video de forma estable ahora. Revisa tu conexión, actualiza Chrome si es posible o usa otro dispositivo. |
553
+ | `recordingPipelineRetry` | Record again | Grabar de nuevo |
554
+ | `genericErrorTitle` | Something went wrong | Algo salió mal |
555
+ | `genericErrorMessage` | An unexpected error occurred. Please try again. | Ocurrió un error inesperado. Por favor inténtalo de nuevo. |
556
+ | `errorRetry` | Try again | Intentar de nuevo |
375
557
  | `switchingDevice` | Switching device... | Cambiando dispositivo... |
376
558
  | `recordingStartsIn` | Recording starts in... | La grabación comienza en... |
377
559
  | `switchingSource` | Switching source... | Cambiando fuente... |
@@ -379,7 +561,6 @@ function App() {
379
561
  | `settings` | Settings | Configuración |
380
562
  | `record` | Record | Grabar |
381
563
  | `stop` | Stop | Detener |
382
- | `minimumRecordingTimeNotReached` | You have not met the minimum recording time yet | Aun no cumples el tiempo minimo de grabacion |
383
564
  | `pause` | Pause | Pausar |
384
565
  | `resume` | Resume | Reanudar |
385
566
  | `mute` | Mute | Silenciar |
@@ -387,11 +568,72 @@ function App() {
387
568
  | `switchSource` | Switch Source | Cambiar Fuente |
388
569
  | `camera` | Camera | Cámara |
389
570
  | `microphone` | Microphone | Micrófono |
571
+ | `microphoneConnecting` | Connecting microphone... | Conectando micrófono... |
572
+ | `minimumRecordingTimeNotReached` | You have not met the minimum recording time yet | Aun no cumples el tiempo minimo de grabacion |
390
573
  | `uploading` | Uploading... | Subiendo... |
574
+ | `finalizing` | Finalizing… | Finalizando… |
575
+ | `retryingUpload` | Retrying upload... | Reintentando subida... |
576
+ | `uploadInsufficientCredits` | This video couldn't be uploaded. Please contact the site administrator. | No se pudo subir el video. Contacta al administrador del sitio. |
577
+ | `uploadInsufficientCreditsTitle` | Video couldn't be uploaded | No se pudo subir el video |
578
+ | `uploadErrorClose` | Close | Cerrar |
579
+ | `finishing` | Finishing... | Finalizando... |
580
+ | `userInAnotherTab` | User in another tab | Usuario en otra pestaña |
581
+ | `download` | Download | Descargar |
582
+ | `nativeCameraSelectVideo` | Select Video from Gallery | Seleccionar Video de la Galería |
583
+ | `nativeCameraRecordVideo` | Record with Camera | Grabar con Cámara |
584
+ | `nativeCameraProcessing` | Processing video... | Procesando video... |
585
+ | `nativeCameraConfigLoading` | Loading settings... | Cargando configuración... |
586
+ | `nativeCameraConfigLoadError` | Failed to load settings | No se pudo cargar la configuración |
587
+ | `nativeCameraPreviewAlt` | Video preview | Vista previa del video |
588
+ | `errorTitle` | Configuration Error | Error de Configuración |
589
+ | `errorApiKeyRequired` | An API Key or access token provider is required. | Se requiere una API Key o un proveedor de token de acceso. |
590
+ | `errorGeneric` | An error occurred while initializing the recorder | Ocurrió un error al inicializar la grabadora |
591
+ | `mobileOpenCamera` | Allow Access | Permitir Acceso |
592
+ | `mobileOpenCameraTitle` | Camera & Microphone | Cámara y Micrófono |
593
+ | `mobileOpenCameraDescription` | We need access to your camera and microphone to start recording | Necesitamos acceso a tu cámara y micrófono para comenzar a grabar |
594
+ | `mobileCloseCamera` | Close | Cerrar |
595
+ | `mobileHideRecordingPrompt` | Hide prompt | Ocultar pregunta |
596
+ | `mobileShowRecordingPrompt` | Show prompt | Mostrar pregunta |
597
+ | `mobilePermissionDenied` | Camera Access Denied | Acceso a Cámara Denegado |
598
+ | `mobilePermissionDeniedDescription` | Please allow camera access in your browser settings to record video. | Por favor permite el acceso a la cámara en la configuración de tu navegador para grabar video. |
599
+ | `permissionFlowTitle` | Set Up Your Camera | Configurar tu Cámara |
600
+ | `permissionFlowCameraTitle` | Camera & Microphone | Cámara y Micrófono |
601
+ | `permissionFlowMicrophoneTitle` | Camera & Microphone | Cámara y Micrófono |
602
+ | `permissionFlowCameraLabel` | We need access to your camera and microphone to start recording | Necesitamos acceso a tu cámara y micrófono para comenzar a grabar |
603
+ | `permissionFlowMicrophoneLabel` | We need access to your camera and microphone to start recording | Necesitamos acceso a tu cámara y micrófono para comenzar a grabar |
604
+ | `permissionFlowStatusPending` | Pending | Pendiente |
605
+ | `permissionFlowStatusGranted` | Ready | Listo |
606
+ | `permissionFlowStatusDenied` | Blocked | Bloqueado |
607
+ | `permissionFlowStatusRequesting` | Click "Allow" in the browser popup above | Haz clic en "Permitir" en el aviso del navegador |
608
+ | `permissionFlowAllowCamera` | Allow Access | Permitir Acceso |
609
+ | `permissionFlowAllowMicrophone` | Allow Access | Permitir Acceso |
610
+ | `permissionFlowRetry` | Try Again | Reintentar |
611
+ | `permissionFlowRecoveryTitle` | Allow access to continue | Permite el acceso para continuar |
612
+ | `permissionFlowRecoveryInstructions` | Open your browser settings and allow camera and microphone access, then try again. | Abre la configuración de tu navegador y permite el acceso a la cámara y micrófono, luego intenta de nuevo. |
613
+ | `permissionFlowConnecting` | Setting up... | Preparando... |
614
+ | `initializingMicrophone` | Initializing microphone... | Iniciando micrófono... |
615
+ | `grantMicrophonePermission` | Grant microphone permission when prompted | Concede el permiso de micrófono cuando se te solicite |
616
+ | `retryMicrophone` | Retry Microphone | Reintentar micrófono |
617
+ | `audioOnlyReady` | Ready to record | Listo para grabar |
618
+ | `audioOnlyRecording` | Recording | Grabando |
619
+ | `audioOnlyPaused` | Paused | En pausa |
620
+ | `audioOnlyUploading` | Uploading {progress}% | Subiendo {progress}% |
621
+ | `audioOnlyUploaded` | Upload complete | Subida completada |
622
+ | `audioOnlyElapsedTime` | Elapsed time | Tiempo transcurrido |
623
+ | `audioOnlyNoMicrophones` | No microphones available | No hay micrófonos disponibles |
624
+ | `audioRecordingDisabledTitle` | Audio recording unavailable | Grabación de audio no disponible |
625
+ | `audioRecordingDisabled` | Audio recording is not enabled for this environment | La grabación de audio no está habilitada para este entorno |
626
+ | `permissionFlowMicrophoneOnlyTitle` | Microphone | Micrófono |
627
+ | `permissionFlowMicrophoneOnlyLabel` | We need access to your microphone to start recording | Necesitamos acceso a tu micrófono para comenzar a grabar |
628
+ | `permissionFlowMicrophoneOnlyRecoveryInstructions` | Open your browser settings and allow microphone access, then try again. | Abre la configuración de tu navegador y permite el acceso al micrófono, luego intenta de nuevo. |
629
+ | `mobileOpenMicrophoneTitle` | Microphone | Micrófono |
630
+ | `mobileOpenMicrophoneDescription` | We need access to your microphone to start recording | Necesitamos acceso a tu micrófono para comenzar a grabar |
631
+
632
+ `TranslationKeys` lists every available key.
391
633
 
392
634
  ### TypeScript Support
393
635
 
394
- The package exports types for better type safety:
636
+ The package exports types for better type safety, including `PartialTranslations`, `TranslationKeys`, `RecordingUploadResult`, `UploadErrorCode`, `UploadProgressPhase`, and `AccessTokenProvider`. It also re-exports `getUploadErrorCode`, `UPLOAD_ERROR_INSUFFICIENT_CREDITS`, `isCredentialConflictError` and `CONFIG_ERROR_CREDENTIAL_CONFLICT` from `@vidtreo/recorder`.
395
637
 
396
638
  ```tsx
397
639
  import VidtreoRecorder from '@vidtreo/recorder-react';
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AudioWarning, AudioWarning as AudioWarning$1, RecorderController, RecordingState, RecordingState as RecordingState$1, SourceType, SourceType as SourceType$1 } from "@vidtreo/recorder";
1
+ import { AccessTokenProvider, AccessTokenProvider as AccessTokenProvider$1, AudioWarning, AudioWarning as AudioWarning$1, CONFIG_ERROR_CREDENTIAL_CONFLICT, ERROR_PICTURE_IN_PICTURE_UNSUPPORTED, ERROR_SCREEN_SELECTION_CANCELLED, LocalRecording, LocalRecording as LocalRecording$1, MediaType, MediaType as MediaType$1, PictureInPictureConfig, PictureInPictureConfig as PictureInPictureConfig$1, PictureInPictureConfigInput, PictureInPictureConfigInput as PictureInPictureConfigInput$1, PictureInPicturePlacement, PictureInPicturePoint, PictureInPicturePosition, PictureInPictureShape, RecorderController, RecordingState, RecordingState as RecordingState$1, RecordingUploadResult, RecordingUploadResult as RecordingUploadResult$1, SourceType, SourceType as SourceType$1, UPLOAD_ERROR_INSUFFICIENT_CREDITS, UploadErrorCode, UploadErrorCode as UploadErrorCode$1, UploadProgressPhase, UploadProgressPhase as UploadProgressPhase$1, getUploadErrorCode, isCredentialConflictError, isPictureInPictureSupported } from "@vidtreo/recorder";
2
2
  import React from "react";
3
3
 
4
4
  //#region src/i18n/translations.d.ts
@@ -86,7 +86,11 @@ type TranslationKeys = {
86
86
  microphoneConnecting: string;
87
87
  minimumRecordingTimeNotReached: string;
88
88
  uploading: string;
89
+ finalizing: string;
89
90
  retryingUpload: string;
91
+ uploadInsufficientCredits: string;
92
+ uploadInsufficientCreditsTitle: string;
93
+ uploadErrorClose: string;
90
94
  finishing: string;
91
95
  userInAnotherTab: string;
92
96
  download: string;
@@ -94,6 +98,8 @@ type TranslationKeys = {
94
98
  nativeCameraRecordVideo: string;
95
99
  nativeCameraProcessing: string;
96
100
  nativeCameraConfigLoading: string;
101
+ nativeCameraConfigLoadError: string;
102
+ nativeCameraPreviewAlt: string;
97
103
  errorTitle: string;
98
104
  errorApiKeyRequired: string;
99
105
  errorGeneric: string;
@@ -119,8 +125,24 @@ type TranslationKeys = {
119
125
  permissionFlowRetry: string;
120
126
  permissionFlowRecoveryTitle: string;
121
127
  permissionFlowRecoveryInstructions: string;
122
- permissionFlowRecoveryGuideHint: string;
123
128
  permissionFlowConnecting: string;
129
+ initializingMicrophone: string;
130
+ grantMicrophonePermission: string;
131
+ retryMicrophone: string;
132
+ audioOnlyReady: string;
133
+ audioOnlyRecording: string;
134
+ audioOnlyPaused: string;
135
+ audioOnlyUploading: string;
136
+ audioOnlyUploaded: string;
137
+ audioOnlyElapsedTime: string;
138
+ audioOnlyNoMicrophones: string;
139
+ audioRecordingDisabledTitle: string;
140
+ audioRecordingDisabled: string;
141
+ permissionFlowMicrophoneOnlyTitle: string;
142
+ permissionFlowMicrophoneOnlyLabel: string;
143
+ permissionFlowMicrophoneOnlyRecoveryInstructions: string;
144
+ mobileOpenMicrophoneTitle: string;
145
+ mobileOpenMicrophoneDescription: string;
124
146
  };
125
147
  type PartialTranslations = Partial<TranslationKeys>;
126
148
  //#endregion
@@ -147,6 +169,7 @@ type RecordingPrompt = {
147
169
  };
148
170
  type BaseRecorderProps = {
149
171
  backendUrl?: string;
172
+ audioOnly?: boolean;
150
173
  countdownDuration?: number;
151
174
  minTimeRecord?: number;
152
175
  maxRecordingTime?: number;
@@ -163,11 +186,8 @@ type BaseRecorderProps = {
163
186
  texts?: PartialTranslations; /** @deprecated Use mobileMode instead */
164
187
  nativeCamera?: boolean; /** Controls mobile recording experience. Default: "overlay" */
165
188
  mobileMode?: MobileMode;
166
- onUploadComplete?: (result: {
167
- recordingId: string;
168
- uploadUrl: string;
169
- }) => void;
170
- onUploadProgress?: (progress: number) => void;
189
+ onUploadComplete?: (result: RecordingUploadResult$1) => void;
190
+ onUploadProgress?: (progress: number, phase: UploadProgressPhase$1 | null) => void;
171
191
  onUploadError?: (error: Error) => void;
172
192
  onRecordingStart?: () => void;
173
193
  onRecordingStop?: () => void;
@@ -177,15 +197,24 @@ type BaseRecorderProps = {
177
197
  type DemoModeProps = BaseRecorderProps & {
178
198
  demo: true;
179
199
  apiKey?: string;
200
+ getAccessToken?: never;
180
201
  };
181
- type ProductionModeProps = BaseRecorderProps & {
202
+ type ApiKeyProductionModeProps = BaseRecorderProps & {
182
203
  demo?: false;
183
204
  apiKey: string;
205
+ getAccessToken?: never;
184
206
  };
185
- type VidtreoRecorderProps = DemoModeProps | ProductionModeProps;
207
+ type AccessTokenProductionModeProps = BaseRecorderProps & {
208
+ demo?: false;
209
+ apiKey?: never;
210
+ getAccessToken: AccessTokenProvider$1;
211
+ };
212
+ type VidtreoRecorderProps = DemoModeProps | ApiKeyProductionModeProps | AccessTokenProductionModeProps;
186
213
  type UseVidtreoRecorderConfig = {
187
- apiKey: string;
214
+ apiKey?: string;
215
+ getAccessToken?: AccessTokenProvider$1;
188
216
  backendUrl?: string;
217
+ audioOnly?: boolean;
189
218
  countdownDuration?: number;
190
219
  minTimeRecord?: number;
191
220
  maxRecordingTime?: number;
@@ -199,16 +228,18 @@ type UseVidtreoRecorderConfig = {
199
228
  enableTabVisibilityOverlay?: boolean;
200
229
  tabVisibilityOverlayText?: string;
201
230
  proxyEndpoint?: string;
202
- demo?: boolean; /** @deprecated Use mobileMode instead */
231
+ demo?: boolean;
232
+ pictureInPicture?: PictureInPictureConfig$1;
233
+ startScreenWithoutCamera?: boolean;
234
+ captureSystemAudio?: boolean;
235
+ autoUpload?: boolean; /** @deprecated Use mobileMode instead */
203
236
  nativeCamera?: boolean; /** Controls mobile recording experience. Default: "overlay" */
204
237
  mobileMode?: MobileMode;
205
238
  lang?: string;
206
239
  texts?: PartialTranslations;
207
- onUploadComplete?: (result: {
208
- recordingId: string;
209
- uploadUrl: string;
210
- }) => void;
211
- onUploadProgress?: (progress: number) => void;
240
+ onRecordingReady?: (recording: LocalRecording$1) => void;
241
+ onUploadComplete?: (result: RecordingUploadResult$1) => void;
242
+ onUploadProgress?: (progress: number, phase: UploadProgressPhase$1 | null) => void;
212
243
  onUploadError?: (error: Error) => void;
213
244
  onRecordingStart?: () => void;
214
245
  onRecordingStop?: () => void;
@@ -218,6 +249,8 @@ type UseVidtreoRecorderConfig = {
218
249
  type VidtreoRecorderState = {
219
250
  recordingState: RecordingState$1;
220
251
  stream: MediaStream | null;
252
+ sourceType: SourceType$1;
253
+ cameraStream: MediaStream | null;
221
254
  isMuted: boolean;
222
255
  isPaused: boolean;
223
256
  isFinalizingRecording: boolean;
@@ -230,7 +263,9 @@ type VidtreoRecorderState = {
230
263
  countdown: number | null;
231
264
  timer: string;
232
265
  uploadProgress: number | null;
266
+ uploadPhase: UploadProgressPhase$1 | null;
233
267
  uploadRetryAttempt: number | null;
268
+ uploadErrorCode: UploadErrorCode$1 | null;
234
269
  transcodingProgress: number | null;
235
270
  transitionMessage: string | null;
236
271
  isInitialized: boolean;
@@ -245,23 +280,30 @@ type VidtreoRecorderState = {
245
280
  };
246
281
  };
247
282
  type VidtreoRecorderActions = {
248
- startRecording: (sourceType?: SourceType$1) => Promise<void>;
283
+ startRecording: (sourceType?: SourceType$1, options?: {
284
+ skipCountdown?: boolean;
285
+ }) => Promise<void>;
249
286
  stopRecording: () => Promise<RecordingStopResult>;
250
287
  pauseRecording: () => void;
251
288
  resumeRecording: () => void;
252
289
  toggleMute: () => void;
253
290
  switchSource: (sourceType: SourceType$1) => Promise<void>;
291
+ reselectScreenSource: () => Promise<boolean>;
254
292
  changeCamera: (deviceId: string) => Promise<void>;
255
293
  changeMic: (deviceId: string) => Promise<void>;
256
294
  startPreview: (sourceType?: SourceType$1) => Promise<void>;
257
295
  stopPreview: () => void;
258
296
  downloadVideo: () => void;
297
+ clearUploadError: () => void;
298
+ setRecordingTitle: (title: string | null) => void;
299
+ setPictureInPicture: (update: PictureInPictureConfigInput$1) => void;
259
300
  cleanup: () => void;
260
301
  };
261
302
  type RecordingStopResult = {
262
303
  recordingId: string;
263
304
  uploadUrl: string;
264
305
  blob: Blob;
306
+ mediaType: MediaType$1;
265
307
  };
266
308
  type UseVidtreoRecorderReturn = {
267
309
  state: VidtreoRecorderState;
@@ -276,4 +318,4 @@ declare function VidtreoRecorder(props: VidtreoRecorderProps): React.JSX.Element
276
318
  //#region src/hooks/use-vidtreo-recorder.d.ts
277
319
  declare function useVidtreoRecorder(config: UseVidtreoRecorderConfig): UseVidtreoRecorderReturn;
278
320
  //#endregion
279
- export { type AudioWarning, MobileMode, type PartialTranslations, RecordingPrompt, RecordingPromptCountdown, RecordingPromptCountdownPlacement, type RecordingState, RecordingStopResult, type SourceType, type TranslationKeys, UseVidtreoRecorderConfig, UseVidtreoRecorderReturn, VidtreoRecorder, VidtreoRecorder as VidtreoRecorderSSR, VidtreoRecorderActions, VidtreoRecorderProps, VidtreoRecorderState, useVidtreoRecorder };
321
+ export { type AccessTokenProvider, type AudioWarning, CONFIG_ERROR_CREDENTIAL_CONFLICT, ERROR_PICTURE_IN_PICTURE_UNSUPPORTED, ERROR_SCREEN_SELECTION_CANCELLED, type LocalRecording, type MediaType, MobileMode, type PartialTranslations, type PictureInPictureConfig, type PictureInPictureConfigInput, type PictureInPicturePlacement, type PictureInPicturePoint, type PictureInPicturePosition, type PictureInPictureShape, RecordingPrompt, RecordingPromptCountdown, RecordingPromptCountdownPlacement, type RecordingState, RecordingStopResult, type RecordingUploadResult, type SourceType, type TranslationKeys, UPLOAD_ERROR_INSUFFICIENT_CREDITS, type UploadErrorCode, type UploadProgressPhase, UseVidtreoRecorderConfig, UseVidtreoRecorderReturn, VidtreoRecorder, VidtreoRecorder as VidtreoRecorderSSR, VidtreoRecorderActions, VidtreoRecorderProps, VidtreoRecorderState, getUploadErrorCode, isCredentialConflictError, isPictureInPictureSupported, useVidtreoRecorder };