@independo/capacitor-voice-recorder 8.0.2-dev.1 → 8.1.0-dev.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.
- package/README.md +130 -32
- package/android/build.gradle +44 -1
- package/android/src/main/java/app/independo/capacitorvoicerecorder/VoiceRecorder.java +146 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/adapters/PermissionChecker.java +8 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/adapters/RecordDataMapper.java +32 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/adapters/RecorderAdapter.java +39 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/adapters/RecorderPlatform.java +25 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/core/CurrentRecordingStatus.java +9 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/core/ErrorCodes.java +19 -0
- package/android/src/main/java/{com/tchvu3/capacitorvoicerecorder → app/independo/capacitorvoicerecorder/core}/Messages.java +2 -1
- package/android/src/main/java/{com/tchvu3/capacitorvoicerecorder → app/independo/capacitorvoicerecorder/core}/RecordData.java +15 -1
- package/android/src/main/java/app/independo/capacitorvoicerecorder/core/RecordOptions.java +4 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/core/ResponseFormat.java +18 -0
- package/android/src/main/java/{com/tchvu3/capacitorvoicerecorder → app/independo/capacitorvoicerecorder/core}/ResponseGenerator.java +7 -1
- package/android/src/main/java/app/independo/capacitorvoicerecorder/platform/CustomMediaRecorder.java +281 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/platform/DefaultRecorderPlatform.java +86 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/platform/NotSupportedOsVersion.java +4 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/service/VoiceRecorderService.java +144 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/service/VoiceRecorderServiceException.java +23 -0
- package/dist/docs.json +145 -5
- package/dist/esm/adapters/VoiceRecorderWebAdapter.d.ts +23 -0
- package/dist/esm/adapters/VoiceRecorderWebAdapter.js +41 -0
- package/dist/esm/adapters/VoiceRecorderWebAdapter.js.map +1 -0
- package/dist/esm/core/error-codes.d.ts +4 -0
- package/dist/esm/core/error-codes.js +21 -0
- package/dist/esm/core/error-codes.js.map +1 -0
- package/dist/esm/core/recording-contract.d.ts +3 -0
- package/dist/esm/core/recording-contract.js +15 -0
- package/dist/esm/core/recording-contract.js.map +1 -0
- package/dist/esm/core/response-format.d.ts +8 -0
- package/dist/esm/core/response-format.js +17 -0
- package/dist/esm/core/response-format.js.map +1 -0
- package/dist/esm/definitions.d.ts +36 -3
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/platform/web/VoiceRecorderImpl.d.ts +45 -0
- package/dist/esm/{VoiceRecorderImpl.js → platform/web/VoiceRecorderImpl.js} +20 -2
- package/dist/esm/platform/web/VoiceRecorderImpl.js.map +1 -0
- package/dist/esm/platform/web/get-blob-duration.js.map +1 -0
- package/dist/esm/{predefined-web-responses.d.ts → platform/web/predefined-web-responses.d.ts} +12 -1
- package/dist/esm/{predefined-web-responses.js → platform/web/predefined-web-responses.js} +11 -0
- package/dist/esm/platform/web/predefined-web-responses.js.map +1 -0
- package/dist/esm/service/VoiceRecorderService.d.ts +47 -0
- package/dist/esm/service/VoiceRecorderService.js +60 -0
- package/dist/esm/service/VoiceRecorderService.js.map +1 -0
- package/dist/esm/web.d.ts +12 -1
- package/dist/esm/web.js +26 -12
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +200 -9
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +200 -9
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/VoiceRecorder/Adapters/DefaultRecorderPlatform.swift +33 -0
- package/ios/Sources/VoiceRecorder/Adapters/RecordDataMapper.swift +38 -0
- package/ios/Sources/VoiceRecorder/Adapters/RecorderAdapter.swift +24 -0
- package/ios/Sources/VoiceRecorder/Adapters/RecorderPlatform.swift +11 -0
- package/ios/Sources/VoiceRecorder/Bridge/VoiceRecorder.swift +172 -0
- package/ios/Sources/VoiceRecorder/{CurrentRecordingStatus.swift → Core/CurrentRecordingStatus.swift} +2 -0
- package/ios/Sources/VoiceRecorder/Core/ErrorCodes.swift +16 -0
- package/ios/Sources/VoiceRecorder/{Messages.swift → Core/Messages.swift} +2 -0
- package/ios/Sources/VoiceRecorder/{RecordData.swift → Core/RecordData.swift} +6 -0
- package/ios/Sources/VoiceRecorder/Core/RecordOptions.swift +11 -0
- package/ios/Sources/VoiceRecorder/Core/ResponseFormat.swift +22 -0
- package/ios/Sources/VoiceRecorder/{ResponseGenerator.swift → Core/ResponseGenerator.swift} +6 -0
- package/ios/Sources/VoiceRecorder/Platform/CustomMediaRecorder.swift +359 -0
- package/ios/Sources/VoiceRecorder/Service/VoiceRecorderService.swift +128 -0
- package/ios/Sources/VoiceRecorder/Service/VoiceRecorderServiceError.swift +14 -0
- package/package.json +10 -4
- package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/CurrentRecordingStatus.java +0 -7
- package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/CustomMediaRecorder.java +0 -149
- package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/NotSupportedOsVersion.java +0 -3
- package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/RecordOptions.java +0 -3
- package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/VoiceRecorder.java +0 -203
- package/dist/esm/VoiceRecorderImpl.d.ts +0 -27
- package/dist/esm/VoiceRecorderImpl.js.map +0 -1
- package/dist/esm/helper/get-blob-duration.js.map +0 -1
- package/dist/esm/predefined-web-responses.js.map +0 -1
- package/ios/Sources/VoiceRecorder/CustomMediaRecorder.swift +0 -113
- package/ios/Sources/VoiceRecorder/RecordOptions.swift +0 -8
- package/ios/Sources/VoiceRecorder/VoiceRecorder.swift +0 -147
- /package/dist/esm/{helper → platform/web}/get-blob-duration.d.ts +0 -0
- /package/dist/esm/{helper → platform/web}/get-blob-duration.js +0 -0
package/dist/docs.json
CHANGED
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
"text": "Error with one of the specified error codes if the recording cannot be stopped."
|
|
116
116
|
}
|
|
117
117
|
],
|
|
118
|
-
"docs": "Stops audio recording.\nWill stop the recording that has been previously started.\nIf the function `startRecording` has not been called beforehand, the promise will reject with `RECORDING_HAS_NOT_STARTED`.\nIf the recording has been stopped immediately after it has been started, the promise will reject with `EMPTY_RECORDING`.\nIn a case of unknown error, the promise will reject with `FAILED_TO_FETCH_RECORDING`.\nIn case of success, the promise resolves to RecordingData containing the recording in base-64, the duration of the recording in milliseconds, and the MIME type.",
|
|
118
|
+
"docs": "Stops audio recording.\nWill stop the recording that has been previously started.\nIf the function `startRecording` has not been called beforehand, the promise will reject with `RECORDING_HAS_NOT_STARTED`.\nIf the recording has been stopped immediately after it has been started, the promise will reject with `EMPTY_RECORDING`.\nIn a case of unknown error, the promise will reject with `FAILED_TO_FETCH_RECORDING`.\nOn iOS, if a recording interrupted by the system cannot be merged, the promise will reject with `FAILED_TO_MERGE_RECORDING`.\nIn case of success, the promise resolves to RecordingData containing the recording in base-64, the duration of the recording in milliseconds, and the MIME type.",
|
|
119
119
|
"complexTypes": [
|
|
120
120
|
"RecordingData"
|
|
121
121
|
],
|
|
@@ -157,7 +157,7 @@
|
|
|
157
157
|
"text": "Error with one of the specified error codes if the recording cannot be resumed."
|
|
158
158
|
}
|
|
159
159
|
],
|
|
160
|
-
"docs": "Resumes a paused audio recording.\nIf the recording has not started yet, the promise will reject with an error code `RECORDING_HAS_NOT_STARTED`.\nOn success, the promise will resolve to { value: true } if the resume was successful or { value: false } if the recording is already running.\nOn certain mobile OS versions, this function is not supported and will reject with `NOT_SUPPORTED_OS_VERSION`.",
|
|
160
|
+
"docs": "Resumes a paused or interrupted audio recording.\nIf the recording has not started yet, the promise will reject with an error code `RECORDING_HAS_NOT_STARTED`.\nOn success, the promise will resolve to { value: true } if the resume was successful or { value: false } if the recording is already running.\nOn certain mobile OS versions, this function is not supported and will reject with `NOT_SUPPORTED_OS_VERSION`.",
|
|
161
161
|
"complexTypes": [
|
|
162
162
|
"GenericResponse"
|
|
163
163
|
],
|
|
@@ -178,11 +178,95 @@
|
|
|
178
178
|
"text": "Error if the status cannot be fetched."
|
|
179
179
|
}
|
|
180
180
|
],
|
|
181
|
-
"docs": "Gets the current status of the voice recorder.\nWill resolve with one of the following values:\n`{ status: \"NONE\" }` if the plugin is idle and waiting to start a new recording.\n`{ status: \"RECORDING\" }` if the plugin is in the middle of recording.\n`{ status: \"PAUSED\" }` if the recording is paused.",
|
|
181
|
+
"docs": "Gets the current status of the voice recorder.\nWill resolve with one of the following values:\n`{ status: \"NONE\" }` if the plugin is idle and waiting to start a new recording.\n`{ status: \"RECORDING\" }` if the plugin is in the middle of recording.\n`{ status: \"PAUSED\" }` if the recording is paused.\n`{ status: \"INTERRUPTED\" }` if the recording was paused due to a system interruption.",
|
|
182
182
|
"complexTypes": [
|
|
183
183
|
"CurrentRecordingStatus"
|
|
184
184
|
],
|
|
185
185
|
"slug": "getcurrentstatus"
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"name": "addListener",
|
|
189
|
+
"signature": "(eventName: 'voiceRecordingInterrupted', listenerFunc: (event: VoiceRecordingInterruptedEvent) => void) => Promise<PluginListenerHandle>",
|
|
190
|
+
"parameters": [
|
|
191
|
+
{
|
|
192
|
+
"name": "eventName",
|
|
193
|
+
"docs": "The name of the event to listen for.",
|
|
194
|
+
"type": "'voiceRecordingInterrupted'"
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"name": "listenerFunc",
|
|
198
|
+
"docs": "The callback function to invoke when the event occurs.",
|
|
199
|
+
"type": "(event: VoiceRecordingInterruptedEvent) => void"
|
|
200
|
+
}
|
|
201
|
+
],
|
|
202
|
+
"returns": "Promise<PluginListenerHandle>",
|
|
203
|
+
"tags": [
|
|
204
|
+
{
|
|
205
|
+
"name": "param",
|
|
206
|
+
"text": "eventName The name of the event to listen for."
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"name": "param",
|
|
210
|
+
"text": "listenerFunc The callback function to invoke when the event occurs."
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"name": "returns",
|
|
214
|
+
"text": "A promise that resolves to a PluginListenerHandle."
|
|
215
|
+
}
|
|
216
|
+
],
|
|
217
|
+
"docs": "Listen for audio recording interruptions (e.g., phone calls, other apps using microphone).\nAvailable on iOS and Android only.",
|
|
218
|
+
"complexTypes": [
|
|
219
|
+
"PluginListenerHandle",
|
|
220
|
+
"VoiceRecordingInterruptedEvent"
|
|
221
|
+
],
|
|
222
|
+
"slug": "addlistenervoicerecordinginterrupted-"
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"name": "addListener",
|
|
226
|
+
"signature": "(eventName: 'voiceRecordingInterruptionEnded', listenerFunc: (event: VoiceRecordingInterruptionEndedEvent) => void) => Promise<PluginListenerHandle>",
|
|
227
|
+
"parameters": [
|
|
228
|
+
{
|
|
229
|
+
"name": "eventName",
|
|
230
|
+
"docs": "The name of the event to listen for.",
|
|
231
|
+
"type": "'voiceRecordingInterruptionEnded'"
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"name": "listenerFunc",
|
|
235
|
+
"docs": "The callback function to invoke when the event occurs.",
|
|
236
|
+
"type": "(event: VoiceRecordingInterruptionEndedEvent) => void"
|
|
237
|
+
}
|
|
238
|
+
],
|
|
239
|
+
"returns": "Promise<PluginListenerHandle>",
|
|
240
|
+
"tags": [
|
|
241
|
+
{
|
|
242
|
+
"name": "param",
|
|
243
|
+
"text": "eventName The name of the event to listen for."
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
"name": "param",
|
|
247
|
+
"text": "listenerFunc The callback function to invoke when the event occurs."
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"name": "returns",
|
|
251
|
+
"text": "A promise that resolves to a PluginListenerHandle."
|
|
252
|
+
}
|
|
253
|
+
],
|
|
254
|
+
"docs": "Listen for audio recording interruption end events.\nAvailable on iOS and Android only.",
|
|
255
|
+
"complexTypes": [
|
|
256
|
+
"PluginListenerHandle",
|
|
257
|
+
"VoiceRecordingInterruptionEndedEvent"
|
|
258
|
+
],
|
|
259
|
+
"slug": "addlistenervoicerecordinginterruptionended-"
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"name": "removeAllListeners",
|
|
263
|
+
"signature": "() => Promise<void>",
|
|
264
|
+
"parameters": [],
|
|
265
|
+
"returns": "Promise<void>",
|
|
266
|
+
"tags": [],
|
|
267
|
+
"docs": "Remove all listeners for this plugin.",
|
|
268
|
+
"complexTypes": [],
|
|
269
|
+
"slug": "removealllisteners"
|
|
186
270
|
}
|
|
187
271
|
],
|
|
188
272
|
"properties": []
|
|
@@ -262,9 +346,25 @@
|
|
|
262
346
|
{
|
|
263
347
|
"name": "status",
|
|
264
348
|
"tags": [],
|
|
265
|
-
"docs": "The current status of the recorder, which can be one of the following values: 'RECORDING', 'PAUSED', 'NONE'.",
|
|
349
|
+
"docs": "The current status of the recorder, which can be one of the following values: 'RECORDING', 'PAUSED', 'INTERRUPTED', 'NONE'.",
|
|
266
350
|
"complexTypes": [],
|
|
267
|
-
"type": "'NONE' | 'RECORDING' | 'PAUSED'"
|
|
351
|
+
"type": "'NONE' | 'RECORDING' | 'PAUSED' | 'INTERRUPTED'"
|
|
352
|
+
}
|
|
353
|
+
]
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
"name": "PluginListenerHandle",
|
|
357
|
+
"slug": "pluginlistenerhandle",
|
|
358
|
+
"docs": "",
|
|
359
|
+
"tags": [],
|
|
360
|
+
"methods": [],
|
|
361
|
+
"properties": [
|
|
362
|
+
{
|
|
363
|
+
"name": "remove",
|
|
364
|
+
"tags": [],
|
|
365
|
+
"docs": "",
|
|
366
|
+
"complexTypes": [],
|
|
367
|
+
"type": "() => Promise<void>"
|
|
268
368
|
}
|
|
269
369
|
]
|
|
270
370
|
}
|
|
@@ -387,6 +487,46 @@
|
|
|
387
487
|
"complexTypes": []
|
|
388
488
|
}
|
|
389
489
|
]
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
"name": "VoiceRecordingInterruptedEvent",
|
|
493
|
+
"slug": "voicerecordinginterruptedevent",
|
|
494
|
+
"docs": "Event payload for voiceRecordingInterrupted event (empty - no data).",
|
|
495
|
+
"types": [
|
|
496
|
+
{
|
|
497
|
+
"text": "Record<string, never>",
|
|
498
|
+
"complexTypes": [
|
|
499
|
+
"Record"
|
|
500
|
+
]
|
|
501
|
+
}
|
|
502
|
+
]
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
"name": "Record",
|
|
506
|
+
"slug": "record",
|
|
507
|
+
"docs": "Construct a type with a set of properties K of type T",
|
|
508
|
+
"types": [
|
|
509
|
+
{
|
|
510
|
+
"text": "{\r\n [P in K]: T;\r\n}",
|
|
511
|
+
"complexTypes": [
|
|
512
|
+
"K",
|
|
513
|
+
"T"
|
|
514
|
+
]
|
|
515
|
+
}
|
|
516
|
+
]
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
"name": "VoiceRecordingInterruptionEndedEvent",
|
|
520
|
+
"slug": "voicerecordinginterruptionendedevent",
|
|
521
|
+
"docs": "Event payload for voiceRecordingInterruptionEnded event (empty - no data).",
|
|
522
|
+
"types": [
|
|
523
|
+
{
|
|
524
|
+
"text": "Record<string, never>",
|
|
525
|
+
"complexTypes": [
|
|
526
|
+
"Record"
|
|
527
|
+
]
|
|
528
|
+
}
|
|
529
|
+
]
|
|
390
530
|
}
|
|
391
531
|
],
|
|
392
532
|
"pluginConfigs": []
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CurrentRecordingStatus, GenericResponse, RecordingData, RecordingOptions } from '../definitions';
|
|
2
|
+
import type { VoiceRecorderPlatform } from '../service/VoiceRecorderService';
|
|
3
|
+
/** Web adapter that delegates to the browser-specific implementation. */
|
|
4
|
+
export declare class VoiceRecorderWebAdapter implements VoiceRecorderPlatform {
|
|
5
|
+
/** Browser implementation that talks to MediaRecorder APIs. */
|
|
6
|
+
private readonly voiceRecorderImpl;
|
|
7
|
+
/** Checks whether the browser can record audio. */
|
|
8
|
+
canDeviceVoiceRecord(): Promise<GenericResponse>;
|
|
9
|
+
/** Returns whether the browser has microphone permission. */
|
|
10
|
+
hasAudioRecordingPermission(): Promise<GenericResponse>;
|
|
11
|
+
/** Requests microphone permission through the browser. */
|
|
12
|
+
requestAudioRecordingPermission(): Promise<GenericResponse>;
|
|
13
|
+
/** Starts a recording session using MediaRecorder. */
|
|
14
|
+
startRecording(options?: RecordingOptions): Promise<GenericResponse>;
|
|
15
|
+
/** Stops the recording session and returns the payload. */
|
|
16
|
+
stopRecording(): Promise<RecordingData>;
|
|
17
|
+
/** Pauses the recording session when supported. */
|
|
18
|
+
pauseRecording(): Promise<GenericResponse>;
|
|
19
|
+
/** Resumes a paused recording session when supported. */
|
|
20
|
+
resumeRecording(): Promise<GenericResponse>;
|
|
21
|
+
/** Returns the current recording state. */
|
|
22
|
+
getCurrentStatus(): Promise<CurrentRecordingStatus>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { VoiceRecorderImpl } from '../platform/web/VoiceRecorderImpl';
|
|
2
|
+
/** Web adapter that delegates to the browser-specific implementation. */
|
|
3
|
+
export class VoiceRecorderWebAdapter {
|
|
4
|
+
constructor() {
|
|
5
|
+
/** Browser implementation that talks to MediaRecorder APIs. */
|
|
6
|
+
this.voiceRecorderImpl = new VoiceRecorderImpl();
|
|
7
|
+
}
|
|
8
|
+
/** Checks whether the browser can record audio. */
|
|
9
|
+
canDeviceVoiceRecord() {
|
|
10
|
+
return VoiceRecorderImpl.canDeviceVoiceRecord();
|
|
11
|
+
}
|
|
12
|
+
/** Returns whether the browser has microphone permission. */
|
|
13
|
+
hasAudioRecordingPermission() {
|
|
14
|
+
return VoiceRecorderImpl.hasAudioRecordingPermission();
|
|
15
|
+
}
|
|
16
|
+
/** Requests microphone permission through the browser. */
|
|
17
|
+
requestAudioRecordingPermission() {
|
|
18
|
+
return VoiceRecorderImpl.requestAudioRecordingPermission();
|
|
19
|
+
}
|
|
20
|
+
/** Starts a recording session using MediaRecorder. */
|
|
21
|
+
startRecording(options) {
|
|
22
|
+
return this.voiceRecorderImpl.startRecording(options);
|
|
23
|
+
}
|
|
24
|
+
/** Stops the recording session and returns the payload. */
|
|
25
|
+
stopRecording() {
|
|
26
|
+
return this.voiceRecorderImpl.stopRecording();
|
|
27
|
+
}
|
|
28
|
+
/** Pauses the recording session when supported. */
|
|
29
|
+
pauseRecording() {
|
|
30
|
+
return this.voiceRecorderImpl.pauseRecording();
|
|
31
|
+
}
|
|
32
|
+
/** Resumes a paused recording session when supported. */
|
|
33
|
+
resumeRecording() {
|
|
34
|
+
return this.voiceRecorderImpl.resumeRecording();
|
|
35
|
+
}
|
|
36
|
+
/** Returns the current recording state. */
|
|
37
|
+
getCurrentStatus() {
|
|
38
|
+
return this.voiceRecorderImpl.getCurrentStatus();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=VoiceRecorderWebAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VoiceRecorderWebAdapter.js","sourceRoot":"","sources":["../../../src/adapters/VoiceRecorderWebAdapter.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAGtE,yEAAyE;AACzE,MAAM,OAAO,uBAAuB;IAApC;QACI,+DAA+D;QAC9C,sBAAiB,GAAG,IAAI,iBAAiB,EAAE,CAAC;IAyCjE,CAAC;IAvCG,mDAAmD;IAC5C,oBAAoB;QACvB,OAAO,iBAAiB,CAAC,oBAAoB,EAAE,CAAC;IACpD,CAAC;IAED,6DAA6D;IACtD,2BAA2B;QAC9B,OAAO,iBAAiB,CAAC,2BAA2B,EAAE,CAAC;IAC3D,CAAC;IAED,0DAA0D;IACnD,+BAA+B;QAClC,OAAO,iBAAiB,CAAC,+BAA+B,EAAE,CAAC;IAC/D,CAAC;IAED,sDAAsD;IAC/C,cAAc,CAAC,OAA0B;QAC5C,OAAO,IAAI,CAAC,iBAAiB,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IAC1D,CAAC;IAED,2DAA2D;IACpD,aAAa;QAChB,OAAO,IAAI,CAAC,iBAAiB,CAAC,aAAa,EAAE,CAAC;IAClD,CAAC;IAED,mDAAmD;IAC5C,cAAc;QACjB,OAAO,IAAI,CAAC,iBAAiB,CAAC,cAAc,EAAE,CAAC;IACnD,CAAC;IAED,yDAAyD;IAClD,eAAe;QAClB,OAAO,IAAI,CAAC,iBAAiB,CAAC,eAAe,EAAE,CAAC;IACpD,CAAC;IAED,2CAA2C;IACpC,gBAAgB;QACnB,OAAO,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,EAAE,CAAC;IACrD,CAAC;CACJ","sourcesContent":["import type {\n CurrentRecordingStatus,\n GenericResponse,\n RecordingData,\n RecordingOptions,\n} from '../definitions';\nimport { VoiceRecorderImpl } from '../platform/web/VoiceRecorderImpl';\nimport type { VoiceRecorderPlatform } from '../service/VoiceRecorderService';\n\n/** Web adapter that delegates to the browser-specific implementation. */\nexport class VoiceRecorderWebAdapter implements VoiceRecorderPlatform {\n /** Browser implementation that talks to MediaRecorder APIs. */\n private readonly voiceRecorderImpl = new VoiceRecorderImpl();\n\n /** Checks whether the browser can record audio. */\n public canDeviceVoiceRecord(): Promise<GenericResponse> {\n return VoiceRecorderImpl.canDeviceVoiceRecord();\n }\n\n /** Returns whether the browser has microphone permission. */\n public hasAudioRecordingPermission(): Promise<GenericResponse> {\n return VoiceRecorderImpl.hasAudioRecordingPermission();\n }\n\n /** Requests microphone permission through the browser. */\n public requestAudioRecordingPermission(): Promise<GenericResponse> {\n return VoiceRecorderImpl.requestAudioRecordingPermission();\n }\n\n /** Starts a recording session using MediaRecorder. */\n public startRecording(options?: RecordingOptions): Promise<GenericResponse> {\n return this.voiceRecorderImpl.startRecording(options);\n }\n\n /** Stops the recording session and returns the payload. */\n public stopRecording(): Promise<RecordingData> {\n return this.voiceRecorderImpl.stopRecording();\n }\n\n /** Pauses the recording session when supported. */\n public pauseRecording(): Promise<GenericResponse> {\n return this.voiceRecorderImpl.pauseRecording();\n }\n\n /** Resumes a paused recording session when supported. */\n public resumeRecording(): Promise<GenericResponse> {\n return this.voiceRecorderImpl.resumeRecording();\n }\n\n /** Returns the current recording state. */\n public getCurrentStatus(): Promise<CurrentRecordingStatus> {\n return this.voiceRecorderImpl.getCurrentStatus();\n }\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Normalizes legacy error messages into canonical error codes. */
|
|
2
|
+
export declare const toCanonicalErrorCode: (legacyMessage: string) => string;
|
|
3
|
+
/** Adds a canonical `code` field to Error-like objects when possible. */
|
|
4
|
+
export declare const attachCanonicalErrorCode: (error: unknown) => void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Maps legacy error messages to canonical error codes. */
|
|
2
|
+
const legacyToCanonical = {
|
|
3
|
+
CANNOT_RECORD_ON_THIS_PHONE: 'DEVICE_CANNOT_VOICE_RECORD',
|
|
4
|
+
};
|
|
5
|
+
/** Normalizes legacy error messages into canonical error codes. */
|
|
6
|
+
export const toCanonicalErrorCode = (legacyMessage) => {
|
|
7
|
+
var _a;
|
|
8
|
+
return (_a = legacyToCanonical[legacyMessage]) !== null && _a !== void 0 ? _a : legacyMessage;
|
|
9
|
+
};
|
|
10
|
+
/** Adds a canonical `code` field to Error-like objects when possible. */
|
|
11
|
+
export const attachCanonicalErrorCode = (error) => {
|
|
12
|
+
if (!error || typeof error !== 'object') {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const messageValue = error.message;
|
|
16
|
+
if (typeof messageValue !== 'string') {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
error.code = toCanonicalErrorCode(messageValue);
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=error-codes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-codes.js","sourceRoot":"","sources":["../../../src/core/error-codes.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,MAAM,iBAAiB,GAA2B;IAC9C,2BAA2B,EAAE,4BAA4B;CAC5D,CAAC;AAEF,mEAAmE;AACnE,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,aAAqB,EAAU,EAAE;;IAClE,OAAO,MAAA,iBAAiB,CAAC,aAAa,CAAC,mCAAI,aAAa,CAAC;AAC7D,CAAC,CAAC;AAEF,yEAAyE;AACzE,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,KAAc,EAAQ,EAAE;IAC7D,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO;IACX,CAAC;IACD,MAAM,YAAY,GAAI,KAA+B,CAAC,OAAO,CAAC;IAC9D,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;QACnC,OAAO;IACX,CAAC;IACA,KAA2B,CAAC,IAAI,GAAG,oBAAoB,CAAC,YAAY,CAAC,CAAC;AAC3E,CAAC,CAAC","sourcesContent":["/** Maps legacy error messages to canonical error codes. */\nconst legacyToCanonical: Record<string, string> = {\n CANNOT_RECORD_ON_THIS_PHONE: 'DEVICE_CANNOT_VOICE_RECORD',\n};\n\n/** Normalizes legacy error messages into canonical error codes. */\nexport const toCanonicalErrorCode = (legacyMessage: string): string => {\n return legacyToCanonical[legacyMessage] ?? legacyMessage;\n};\n\n/** Adds a canonical `code` field to Error-like objects when possible. */\nexport const attachCanonicalErrorCode = (error: unknown): void => {\n if (!error || typeof error !== 'object') {\n return;\n }\n const messageValue = (error as { message?: unknown }).message;\n if (typeof messageValue !== 'string') {\n return;\n }\n (error as { code?: string }).code = toCanonicalErrorCode(messageValue);\n};\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Normalizes recording payloads into a stable contract shape. */
|
|
2
|
+
export const normalizeRecordingData = (data) => {
|
|
3
|
+
const { recordDataBase64, uri, msDuration, mimeType } = data.value;
|
|
4
|
+
const normalizedValue = { msDuration, mimeType };
|
|
5
|
+
const trimmedUri = typeof uri === 'string' && uri.length > 0 ? uri : undefined;
|
|
6
|
+
const trimmedBase64 = typeof recordDataBase64 === 'string' && recordDataBase64.length > 0 ? recordDataBase64 : undefined;
|
|
7
|
+
if (trimmedUri) {
|
|
8
|
+
normalizedValue.uri = trimmedUri;
|
|
9
|
+
}
|
|
10
|
+
else if (trimmedBase64) {
|
|
11
|
+
normalizedValue.recordDataBase64 = trimmedBase64;
|
|
12
|
+
}
|
|
13
|
+
return { value: normalizedValue };
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=recording-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recording-contract.js","sourceRoot":"","sources":["../../../src/core/recording-contract.ts"],"names":[],"mappings":"AAEA,kEAAkE;AAClE,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,IAAmB,EAAiB,EAAE;IACzE,MAAM,EAAE,gBAAgB,EAAE,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;IACnE,MAAM,eAAe,GAA4B,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;IAC1E,MAAM,UAAU,GAAG,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/E,MAAM,aAAa,GAAG,OAAO,gBAAgB,KAAK,QAAQ,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAC;IAEzH,IAAI,UAAU,EAAE,CAAC;QACb,eAAe,CAAC,GAAG,GAAG,UAAU,CAAC;IACrC,CAAC;SAAM,IAAI,aAAa,EAAE,CAAC;QACvB,eAAe,CAAC,gBAAgB,GAAG,aAAa,CAAC;IACrD,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,eAAe,EAAmB,CAAC;AACvD,CAAC,CAAC","sourcesContent":["import type { RecordingData } from '../definitions';\n\n/** Normalizes recording payloads into a stable contract shape. */\nexport const normalizeRecordingData = (data: RecordingData): RecordingData => {\n const { recordDataBase64, uri, msDuration, mimeType } = data.value;\n const normalizedValue: Record<string, unknown> = { msDuration, mimeType };\n const trimmedUri = typeof uri === 'string' && uri.length > 0 ? uri : undefined;\n const trimmedBase64 = typeof recordDataBase64 === 'string' && recordDataBase64.length > 0 ? recordDataBase64 : undefined;\n\n if (trimmedUri) {\n normalizedValue.uri = trimmedUri;\n } else if (trimmedBase64) {\n normalizedValue.recordDataBase64 = trimmedBase64;\n }\n\n return { value: normalizedValue } as RecordingData;\n};\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Supported response shapes for plugin results. */
|
|
2
|
+
export type ResponseFormat = 'legacy' | 'normalized';
|
|
3
|
+
/** Default response shape when no config is provided. */
|
|
4
|
+
export declare const DEFAULT_RESPONSE_FORMAT: ResponseFormat;
|
|
5
|
+
/** Parses a user-provided response format into a supported value. */
|
|
6
|
+
export declare const resolveResponseFormat: (value: unknown) => ResponseFormat;
|
|
7
|
+
/** Reads the response format from a Capacitor plugin config object. */
|
|
8
|
+
export declare const getResponseFormatFromConfig: (config: unknown) => ResponseFormat;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Default response shape when no config is provided. */
|
|
2
|
+
export const DEFAULT_RESPONSE_FORMAT = 'legacy';
|
|
3
|
+
/** Parses a user-provided response format into a supported value. */
|
|
4
|
+
export const resolveResponseFormat = (value) => {
|
|
5
|
+
if (typeof value === 'string' && value.toLowerCase() === 'normalized') {
|
|
6
|
+
return 'normalized';
|
|
7
|
+
}
|
|
8
|
+
return DEFAULT_RESPONSE_FORMAT;
|
|
9
|
+
};
|
|
10
|
+
/** Reads the response format from a Capacitor plugin config object. */
|
|
11
|
+
export const getResponseFormatFromConfig = (config) => {
|
|
12
|
+
if (config && typeof config === 'object' && 'responseFormat' in config) {
|
|
13
|
+
return resolveResponseFormat(config.responseFormat);
|
|
14
|
+
}
|
|
15
|
+
return DEFAULT_RESPONSE_FORMAT;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=response-format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-format.js","sourceRoot":"","sources":["../../../src/core/response-format.ts"],"names":[],"mappings":"AAGA,yDAAyD;AACzD,MAAM,CAAC,MAAM,uBAAuB,GAAmB,QAAQ,CAAC;AAEhE,qEAAqE;AACrE,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,KAAc,EAAkB,EAAE;IACpE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,WAAW,EAAE,KAAK,YAAY,EAAE,CAAC;QACpE,OAAO,YAAY,CAAC;IACxB,CAAC;IACD,OAAO,uBAAuB,CAAC;AACnC,CAAC,CAAC;AAEF,uEAAuE;AACvE,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,MAAe,EAAkB,EAAE;IAC3E,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,gBAAgB,IAAI,MAAM,EAAE,CAAC;QACrE,OAAO,qBAAqB,CAAE,MAAuC,CAAC,cAAc,CAAC,CAAC;IAC1F,CAAC;IACD,OAAO,uBAAuB,CAAC;AACnC,CAAC,CAAC","sourcesContent":["/** Supported response shapes for plugin results. */\nexport type ResponseFormat = 'legacy' | 'normalized';\n\n/** Default response shape when no config is provided. */\nexport const DEFAULT_RESPONSE_FORMAT: ResponseFormat = 'legacy';\n\n/** Parses a user-provided response format into a supported value. */\nexport const resolveResponseFormat = (value: unknown): ResponseFormat => {\n if (typeof value === 'string' && value.toLowerCase() === 'normalized') {\n return 'normalized';\n }\n return DEFAULT_RESPONSE_FORMAT;\n};\n\n/** Reads the response format from a Capacitor plugin config object. */\nexport const getResponseFormatFromConfig = (config: unknown): ResponseFormat => {\n if (config && typeof config === 'object' && 'responseFormat' in config) {\n return resolveResponseFormat((config as { responseFormat?: unknown }).responseFormat);\n }\n return DEFAULT_RESPONSE_FORMAT;\n};\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PluginListenerHandle } from "@capacitor/core";
|
|
1
2
|
import type { Directory } from "@capacitor/filesystem";
|
|
2
3
|
/**
|
|
3
4
|
* Represents a Base64 encoded string.
|
|
@@ -59,10 +60,18 @@ export interface GenericResponse {
|
|
|
59
60
|
*/
|
|
60
61
|
export interface CurrentRecordingStatus {
|
|
61
62
|
/**
|
|
62
|
-
* The current status of the recorder, which can be one of the following values: 'RECORDING', 'PAUSED', 'NONE'.
|
|
63
|
+
* The current status of the recorder, which can be one of the following values: 'RECORDING', 'PAUSED', 'INTERRUPTED', 'NONE'.
|
|
63
64
|
*/
|
|
64
|
-
status: 'RECORDING' | 'PAUSED' | 'NONE';
|
|
65
|
+
status: 'RECORDING' | 'PAUSED' | 'INTERRUPTED' | 'NONE';
|
|
65
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Event payload for voiceRecordingInterrupted event (empty - no data).
|
|
69
|
+
*/
|
|
70
|
+
export type VoiceRecordingInterruptedEvent = Record<string, never>;
|
|
71
|
+
/**
|
|
72
|
+
* Event payload for voiceRecordingInterruptionEnded event (empty - no data).
|
|
73
|
+
*/
|
|
74
|
+
export type VoiceRecordingInterruptionEndedEvent = Record<string, never>;
|
|
66
75
|
/**
|
|
67
76
|
* Interface for the VoiceRecorderPlugin which provides methods to record audio.
|
|
68
77
|
*/
|
|
@@ -110,6 +119,7 @@ export interface VoiceRecorderPlugin {
|
|
|
110
119
|
* If the function `startRecording` has not been called beforehand, the promise will reject with `RECORDING_HAS_NOT_STARTED`.
|
|
111
120
|
* If the recording has been stopped immediately after it has been started, the promise will reject with `EMPTY_RECORDING`.
|
|
112
121
|
* In a case of unknown error, the promise will reject with `FAILED_TO_FETCH_RECORDING`.
|
|
122
|
+
* On iOS, if a recording interrupted by the system cannot be merged, the promise will reject with `FAILED_TO_MERGE_RECORDING`.
|
|
113
123
|
* In case of success, the promise resolves to RecordingData containing the recording in base-64, the duration of the recording in milliseconds, and the MIME type.
|
|
114
124
|
* @returns A promise that resolves to RecordingData.
|
|
115
125
|
* @throws Error with one of the specified error codes if the recording cannot be stopped.
|
|
@@ -125,7 +135,7 @@ export interface VoiceRecorderPlugin {
|
|
|
125
135
|
*/
|
|
126
136
|
pauseRecording(): Promise<GenericResponse>;
|
|
127
137
|
/**
|
|
128
|
-
* Resumes a paused audio recording.
|
|
138
|
+
* Resumes a paused or interrupted audio recording.
|
|
129
139
|
* If the recording has not started yet, the promise will reject with an error code `RECORDING_HAS_NOT_STARTED`.
|
|
130
140
|
* On success, the promise will resolve to { value: true } if the resume was successful or { value: false } if the recording is already running.
|
|
131
141
|
* On certain mobile OS versions, this function is not supported and will reject with `NOT_SUPPORTED_OS_VERSION`.
|
|
@@ -139,8 +149,31 @@ export interface VoiceRecorderPlugin {
|
|
|
139
149
|
* `{ status: "NONE" }` if the plugin is idle and waiting to start a new recording.
|
|
140
150
|
* `{ status: "RECORDING" }` if the plugin is in the middle of recording.
|
|
141
151
|
* `{ status: "PAUSED" }` if the recording is paused.
|
|
152
|
+
* `{ status: "INTERRUPTED" }` if the recording was paused due to a system interruption.
|
|
142
153
|
* @returns A promise that resolves to a CurrentRecordingStatus.
|
|
143
154
|
* @throws Error if the status cannot be fetched.
|
|
144
155
|
*/
|
|
145
156
|
getCurrentStatus(): Promise<CurrentRecordingStatus>;
|
|
157
|
+
/**
|
|
158
|
+
* Listen for audio recording interruptions (e.g., phone calls, other apps using microphone).
|
|
159
|
+
* Available on iOS and Android only.
|
|
160
|
+
*
|
|
161
|
+
* @param eventName The name of the event to listen for.
|
|
162
|
+
* @param listenerFunc The callback function to invoke when the event occurs.
|
|
163
|
+
* @returns A promise that resolves to a PluginListenerHandle.
|
|
164
|
+
*/
|
|
165
|
+
addListener(eventName: 'voiceRecordingInterrupted', listenerFunc: (event: VoiceRecordingInterruptedEvent) => void): Promise<PluginListenerHandle>;
|
|
166
|
+
/**
|
|
167
|
+
* Listen for audio recording interruption end events.
|
|
168
|
+
* Available on iOS and Android only.
|
|
169
|
+
*
|
|
170
|
+
* @param eventName The name of the event to listen for.
|
|
171
|
+
* @param listenerFunc The callback function to invoke when the event occurs.
|
|
172
|
+
* @returns A promise that resolves to a PluginListenerHandle.
|
|
173
|
+
*/
|
|
174
|
+
addListener(eventName: 'voiceRecordingInterruptionEnded', listenerFunc: (event: VoiceRecordingInterruptionEndedEvent) => void): Promise<PluginListenerHandle>;
|
|
175
|
+
/**
|
|
176
|
+
* Remove all listeners for this plugin.
|
|
177
|
+
*/
|
|
178
|
+
removeAllListeners(): Promise<void>;
|
|
146
179
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type {Directory} from \"@capacitor/filesystem\";\n\n/**\n * Represents a Base64 encoded string.\n */\nexport type Base64String = string;\n\n/**\n * Can be used to specify options for the recording.\n */\nexport interface RecordingOptions {\n /**\n * The capacitor filesystem directory where the recording should be saved.\n *\n * If not specified, the recording will be stored in a base64 string and returned in the `RecordingData` object.\n * @see RecordingData\n */\n directory?: Directory;\n\n /**\n * An optional subdirectory in the specified directory where the recording should be saved.\n */\n subDirectory?: string;\n}\n\n/**\n * Interface representing the data of a recording.\n */\nexport interface RecordingData {\n /**\n * The value containing the recording details.\n */\n value: {\n /**\n * The recorded data as a Base64 encoded string.\n */\n recordDataBase64: Base64String;\n /**\n * The duration of the recording in milliseconds.\n */\n msDuration: number;\n /**\n * The MIME type of the recorded file.\n */\n mimeType: string;\n\n /**\n * The URI of the recording file.\n */\n uri?: string;\n };\n}\n\n/**\n * Interface representing a generic response with a boolean value.\n */\nexport interface GenericResponse {\n /**\n * The result of the operation as a boolean value.\n */\n value: boolean;\n}\n\n/**\n * Interface representing the current status of the voice recorder.\n */\nexport interface CurrentRecordingStatus {\n /**\n * The current status of the recorder, which can be one of the following values: 'RECORDING', 'PAUSED', 'NONE'.\n */\n status: 'RECORDING' | 'PAUSED' | 'NONE';\n}\n\n/**\n * Interface for the VoiceRecorderPlugin which provides methods to record audio.\n */\nexport interface VoiceRecorderPlugin {\n /**\n * Checks if the current device can record audio.\n * On mobile, this function will always resolve to `{ value: true }`.\n * In a browser, it will resolve to `{ value: true }` or `{ value: false }` based on the browser's ability to record.\n * This method does not take into account the permission status, only if the browser itself is capable of recording at all.\n * @returns A promise that resolves to a GenericResponse.\n * @throws Error with code \"COULD_NOT_QUERY_PERMISSION_STATUS\" if the device cannot query the permission status.\n */\n canDeviceVoiceRecord(): Promise<GenericResponse>;\n\n /**\n * Requests audio recording permission from the user.\n * If the permission has already been provided, the promise will resolve with `{ value: true }`.\n * Otherwise, the promise will resolve to `{ value: true }` or `{ value: false }` based on the user's response.\n * @returns A promise that resolves to a GenericResponse.\n * @throws Error if the permission request fails.\n */\n requestAudioRecordingPermission(): Promise<GenericResponse>;\n\n /**\n * Checks if audio recording permission has been granted.\n * Will resolve to `{ value: true }` or `{ value: false }` based on the status of the permission.\n * The web implementation of this plugin uses the Permissions API, which is not widespread.\n * If the status of the permission cannot be checked, the promise will reject with `COULD_NOT_QUERY_PERMISSION_STATUS`.\n * In that case, use `requestAudioRecordingPermission` or `startRecording` and capture any exception that is thrown.\n * @returns A promise that resolves to a GenericResponse.\n * @throws Error with code \"COULD_NOT_QUERY_PERMISSION_STATUS\" if the device cannot query the permission status.\n */\n hasAudioRecordingPermission(): Promise<GenericResponse>;\n\n /**\n * Starts audio recording.\n * On success, the promise will resolve to { value: true }.\n * On error, the promise will reject with one of the following error codes:\n * \"MISSING_PERMISSION\", \"ALREADY_RECORDING\", \"MICROPHONE_BEING_USED\", \"DEVICE_CANNOT_VOICE_RECORD\", or \"FAILED_TO_RECORD\".\n * @param options The options for the recording.\n * @returns A promise that resolves to a GenericResponse.\n * @throws Error with one of the specified error codes if the recording cannot be started.\n */\n startRecording(options?: RecordingOptions): Promise<GenericResponse>;\n\n /**\n * Stops audio recording.\n * Will stop the recording that has been previously started.\n * If the function `startRecording` has not been called beforehand, the promise will reject with `RECORDING_HAS_NOT_STARTED`.\n * If the recording has been stopped immediately after it has been started, the promise will reject with `EMPTY_RECORDING`.\n * In a case of unknown error, the promise will reject with `FAILED_TO_FETCH_RECORDING`.\n * In case of success, the promise resolves to RecordingData containing the recording in base-64, the duration of the recording in milliseconds, and the MIME type.\n * @returns A promise that resolves to RecordingData.\n * @throws Error with one of the specified error codes if the recording cannot be stopped.\n */\n stopRecording(): Promise<RecordingData>;\n\n /**\n * Pauses the ongoing audio recording.\n * If the recording has not started yet, the promise will reject with an error code `RECORDING_HAS_NOT_STARTED`.\n * On success, the promise will resolve to { value: true } if the pause was successful or { value: false } if the recording is already paused.\n * On certain mobile OS versions, this function is not supported and will reject with `NOT_SUPPORTED_OS_VERSION`.\n * @returns A promise that resolves to a GenericResponse.\n * @throws Error with one of the specified error codes if the recording cannot be paused.\n */\n pauseRecording(): Promise<GenericResponse>;\n\n /**\n * Resumes a paused audio recording.\n * If the recording has not started yet, the promise will reject with an error code `RECORDING_HAS_NOT_STARTED`.\n * On success, the promise will resolve to { value: true } if the resume was successful or { value: false } if the recording is already running.\n * On certain mobile OS versions, this function is not supported and will reject with `NOT_SUPPORTED_OS_VERSION`.\n * @returns A promise that resolves to a GenericResponse.\n * @throws Error with one of the specified error codes if the recording cannot be resumed.\n */\n resumeRecording(): Promise<GenericResponse>;\n\n /**\n * Gets the current status of the voice recorder.\n * Will resolve with one of the following values:\n * `{ status: \"NONE\" }` if the plugin is idle and waiting to start a new recording.\n * `{ status: \"RECORDING\" }` if the plugin is in the middle of recording.\n * `{ status: \"PAUSED\" }` if the recording is paused.\n * @returns A promise that resolves to a CurrentRecordingStatus.\n * @throws Error if the status cannot be fetched.\n */\n getCurrentStatus(): Promise<CurrentRecordingStatus>;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type {PluginListenerHandle} from \"@capacitor/core\";\nimport type {Directory} from \"@capacitor/filesystem\";\n\n/**\n * Represents a Base64 encoded string.\n */\nexport type Base64String = string;\n\n/**\n * Can be used to specify options for the recording.\n */\nexport interface RecordingOptions {\n /**\n * The capacitor filesystem directory where the recording should be saved.\n *\n * If not specified, the recording will be stored in a base64 string and returned in the `RecordingData` object.\n * @see RecordingData\n */\n directory?: Directory;\n\n /**\n * An optional subdirectory in the specified directory where the recording should be saved.\n */\n subDirectory?: string;\n}\n\n/**\n * Interface representing the data of a recording.\n */\nexport interface RecordingData {\n /**\n * The value containing the recording details.\n */\n value: {\n /**\n * The recorded data as a Base64 encoded string.\n */\n recordDataBase64: Base64String;\n /**\n * The duration of the recording in milliseconds.\n */\n msDuration: number;\n /**\n * The MIME type of the recorded file.\n */\n mimeType: string;\n\n /**\n * The URI of the recording file.\n */\n uri?: string;\n };\n}\n\n/**\n * Interface representing a generic response with a boolean value.\n */\nexport interface GenericResponse {\n /**\n * The result of the operation as a boolean value.\n */\n value: boolean;\n}\n\n/**\n * Interface representing the current status of the voice recorder.\n */\nexport interface CurrentRecordingStatus {\n /**\n * The current status of the recorder, which can be one of the following values: 'RECORDING', 'PAUSED', 'INTERRUPTED', 'NONE'.\n */\n status: 'RECORDING' | 'PAUSED' | 'INTERRUPTED' | 'NONE';\n}\n\n/**\n * Event payload for voiceRecordingInterrupted event (empty - no data).\n */\nexport type VoiceRecordingInterruptedEvent = Record<string, never>;\n\n/**\n * Event payload for voiceRecordingInterruptionEnded event (empty - no data).\n */\nexport type VoiceRecordingInterruptionEndedEvent = Record<string, never>;\n\n/**\n * Interface for the VoiceRecorderPlugin which provides methods to record audio.\n */\nexport interface VoiceRecorderPlugin {\n /**\n * Checks if the current device can record audio.\n * On mobile, this function will always resolve to `{ value: true }`.\n * In a browser, it will resolve to `{ value: true }` or `{ value: false }` based on the browser's ability to record.\n * This method does not take into account the permission status, only if the browser itself is capable of recording at all.\n * @returns A promise that resolves to a GenericResponse.\n * @throws Error with code \"COULD_NOT_QUERY_PERMISSION_STATUS\" if the device cannot query the permission status.\n */\n canDeviceVoiceRecord(): Promise<GenericResponse>;\n\n /**\n * Requests audio recording permission from the user.\n * If the permission has already been provided, the promise will resolve with `{ value: true }`.\n * Otherwise, the promise will resolve to `{ value: true }` or `{ value: false }` based on the user's response.\n * @returns A promise that resolves to a GenericResponse.\n * @throws Error if the permission request fails.\n */\n requestAudioRecordingPermission(): Promise<GenericResponse>;\n\n /**\n * Checks if audio recording permission has been granted.\n * Will resolve to `{ value: true }` or `{ value: false }` based on the status of the permission.\n * The web implementation of this plugin uses the Permissions API, which is not widespread.\n * If the status of the permission cannot be checked, the promise will reject with `COULD_NOT_QUERY_PERMISSION_STATUS`.\n * In that case, use `requestAudioRecordingPermission` or `startRecording` and capture any exception that is thrown.\n * @returns A promise that resolves to a GenericResponse.\n * @throws Error with code \"COULD_NOT_QUERY_PERMISSION_STATUS\" if the device cannot query the permission status.\n */\n hasAudioRecordingPermission(): Promise<GenericResponse>;\n\n /**\n * Starts audio recording.\n * On success, the promise will resolve to { value: true }.\n * On error, the promise will reject with one of the following error codes:\n * \"MISSING_PERMISSION\", \"ALREADY_RECORDING\", \"MICROPHONE_BEING_USED\", \"DEVICE_CANNOT_VOICE_RECORD\", or \"FAILED_TO_RECORD\".\n * @param options The options for the recording.\n * @returns A promise that resolves to a GenericResponse.\n * @throws Error with one of the specified error codes if the recording cannot be started.\n */\n startRecording(options?: RecordingOptions): Promise<GenericResponse>;\n\n /**\n * Stops audio recording.\n * Will stop the recording that has been previously started.\n * If the function `startRecording` has not been called beforehand, the promise will reject with `RECORDING_HAS_NOT_STARTED`.\n * If the recording has been stopped immediately after it has been started, the promise will reject with `EMPTY_RECORDING`.\n * In a case of unknown error, the promise will reject with `FAILED_TO_FETCH_RECORDING`.\n * On iOS, if a recording interrupted by the system cannot be merged, the promise will reject with `FAILED_TO_MERGE_RECORDING`.\n * In case of success, the promise resolves to RecordingData containing the recording in base-64, the duration of the recording in milliseconds, and the MIME type.\n * @returns A promise that resolves to RecordingData.\n * @throws Error with one of the specified error codes if the recording cannot be stopped.\n */\n stopRecording(): Promise<RecordingData>;\n\n /**\n * Pauses the ongoing audio recording.\n * If the recording has not started yet, the promise will reject with an error code `RECORDING_HAS_NOT_STARTED`.\n * On success, the promise will resolve to { value: true } if the pause was successful or { value: false } if the recording is already paused.\n * On certain mobile OS versions, this function is not supported and will reject with `NOT_SUPPORTED_OS_VERSION`.\n * @returns A promise that resolves to a GenericResponse.\n * @throws Error with one of the specified error codes if the recording cannot be paused.\n */\n pauseRecording(): Promise<GenericResponse>;\n\n /**\n * Resumes a paused or interrupted audio recording.\n * If the recording has not started yet, the promise will reject with an error code `RECORDING_HAS_NOT_STARTED`.\n * On success, the promise will resolve to { value: true } if the resume was successful or { value: false } if the recording is already running.\n * On certain mobile OS versions, this function is not supported and will reject with `NOT_SUPPORTED_OS_VERSION`.\n * @returns A promise that resolves to a GenericResponse.\n * @throws Error with one of the specified error codes if the recording cannot be resumed.\n */\n resumeRecording(): Promise<GenericResponse>;\n\n /**\n * Gets the current status of the voice recorder.\n * Will resolve with one of the following values:\n * `{ status: \"NONE\" }` if the plugin is idle and waiting to start a new recording.\n * `{ status: \"RECORDING\" }` if the plugin is in the middle of recording.\n * `{ status: \"PAUSED\" }` if the recording is paused.\n * `{ status: \"INTERRUPTED\" }` if the recording was paused due to a system interruption.\n * @returns A promise that resolves to a CurrentRecordingStatus.\n * @throws Error if the status cannot be fetched.\n */\n getCurrentStatus(): Promise<CurrentRecordingStatus>;\n\n /**\n * Listen for audio recording interruptions (e.g., phone calls, other apps using microphone).\n * Available on iOS and Android only.\n *\n * @param eventName The name of the event to listen for.\n * @param listenerFunc The callback function to invoke when the event occurs.\n * @returns A promise that resolves to a PluginListenerHandle.\n */\n addListener(\n eventName: 'voiceRecordingInterrupted',\n listenerFunc: (event: VoiceRecordingInterruptedEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for audio recording interruption end events.\n * Available on iOS and Android only.\n *\n * @param eventName The name of the event to listen for.\n * @param listenerFunc The callback function to invoke when the event occurs.\n * @returns A promise that resolves to a PluginListenerHandle.\n */\n addListener(\n eventName: 'voiceRecordingInterruptionEnded',\n listenerFunc: (event: VoiceRecordingInterruptionEndedEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Remove all listeners for this plugin.\n */\n removeAllListeners(): Promise<void>;\n}\n"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { CurrentRecordingStatus, GenericResponse, RecordingData, RecordingOptions } from '../../definitions';
|
|
2
|
+
/** Preferred MIME types to probe in order of fallback. */
|
|
3
|
+
declare const POSSIBLE_MIME_TYPES: {
|
|
4
|
+
'audio/aac': string;
|
|
5
|
+
'audio/webm;codecs=opus': string;
|
|
6
|
+
'audio/mp4': string;
|
|
7
|
+
'audio/webm': string;
|
|
8
|
+
'audio/ogg;codecs=opus': string;
|
|
9
|
+
};
|
|
10
|
+
/** Browser implementation backed by MediaRecorder and Capacitor Filesystem. */
|
|
11
|
+
export declare class VoiceRecorderImpl {
|
|
12
|
+
/** Active MediaRecorder instance, if recording. */
|
|
13
|
+
private mediaRecorder;
|
|
14
|
+
/** Collected data chunks from MediaRecorder. */
|
|
15
|
+
private chunks;
|
|
16
|
+
/** Promise resolved when the recorder stops and payload is ready. */
|
|
17
|
+
private pendingResult;
|
|
18
|
+
/** Returns whether the browser can start a recording session. */
|
|
19
|
+
static canDeviceVoiceRecord(): Promise<GenericResponse>;
|
|
20
|
+
/** Starts a recording session using MediaRecorder. */
|
|
21
|
+
startRecording(options?: RecordingOptions): Promise<GenericResponse>;
|
|
22
|
+
/** Stops the current recording and resolves the pending payload. */
|
|
23
|
+
stopRecording(): Promise<RecordingData>;
|
|
24
|
+
/** Returns whether the browser has microphone permission. */
|
|
25
|
+
static hasAudioRecordingPermission(): Promise<GenericResponse>;
|
|
26
|
+
/** Requests microphone permission from the browser. */
|
|
27
|
+
static requestAudioRecordingPermission(): Promise<GenericResponse>;
|
|
28
|
+
/** Pauses the recording session when supported. */
|
|
29
|
+
pauseRecording(): Promise<GenericResponse>;
|
|
30
|
+
/** Resumes a paused recording session when supported. */
|
|
31
|
+
resumeRecording(): Promise<GenericResponse>;
|
|
32
|
+
/** Returns the current recording status from MediaRecorder. */
|
|
33
|
+
getCurrentStatus(): Promise<CurrentRecordingStatus>;
|
|
34
|
+
/** Returns the first supported MIME type, if any. */
|
|
35
|
+
static getSupportedMimeType<T extends keyof typeof POSSIBLE_MIME_TYPES>(): T | null;
|
|
36
|
+
/** Initializes MediaRecorder and wires up handlers. */
|
|
37
|
+
private onSuccessfullyStartedRecording;
|
|
38
|
+
/** Handles failures from getUserMedia. */
|
|
39
|
+
private onFailedToStartRecording;
|
|
40
|
+
/** Converts a Blob payload into a base64 string. */
|
|
41
|
+
private static blobToBase64;
|
|
42
|
+
/** Resets state for the next recording attempt. */
|
|
43
|
+
private prepareInstanceForNextOperation;
|
|
44
|
+
}
|
|
45
|
+
export {};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Filesystem } from '@capacitor/filesystem';
|
|
2
2
|
import write_blob from 'capacitor-blob-writer';
|
|
3
|
-
import getBlobDuration from
|
|
3
|
+
import getBlobDuration from './get-blob-duration';
|
|
4
4
|
import { alreadyRecordingError, couldNotQueryPermissionStatusError, deviceCannotVoiceRecordError, emptyRecordingError, failedToFetchRecordingError, failedToRecordError, failureResponse, missingPermissionError, recordingHasNotStartedError, successResponse, } from './predefined-web-responses';
|
|
5
|
-
|
|
5
|
+
/** Preferred MIME types to probe in order of fallback. */
|
|
6
6
|
const POSSIBLE_MIME_TYPES = {
|
|
7
7
|
'audio/aac': '.aac',
|
|
8
8
|
'audio/webm;codecs=opus': '.ogg',
|
|
@@ -10,13 +10,19 @@ const POSSIBLE_MIME_TYPES = {
|
|
|
10
10
|
'audio/webm': '.ogg',
|
|
11
11
|
'audio/ogg;codecs=opus': '.ogg',
|
|
12
12
|
};
|
|
13
|
+
/** Creates a promise that never resolves. */
|
|
13
14
|
const neverResolvingPromise = () => new Promise(() => undefined);
|
|
15
|
+
/** Browser implementation backed by MediaRecorder and Capacitor Filesystem. */
|
|
14
16
|
export class VoiceRecorderImpl {
|
|
15
17
|
constructor() {
|
|
18
|
+
/** Active MediaRecorder instance, if recording. */
|
|
16
19
|
this.mediaRecorder = null;
|
|
20
|
+
/** Collected data chunks from MediaRecorder. */
|
|
17
21
|
this.chunks = [];
|
|
22
|
+
/** Promise resolved when the recorder stops and payload is ready. */
|
|
18
23
|
this.pendingResult = neverResolvingPromise();
|
|
19
24
|
}
|
|
25
|
+
/** Returns whether the browser can start a recording session. */
|
|
20
26
|
static async canDeviceVoiceRecord() {
|
|
21
27
|
var _a;
|
|
22
28
|
if (((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) == null || VoiceRecorderImpl.getSupportedMimeType() == null) {
|
|
@@ -26,6 +32,7 @@ export class VoiceRecorderImpl {
|
|
|
26
32
|
return successResponse();
|
|
27
33
|
}
|
|
28
34
|
}
|
|
35
|
+
/** Starts a recording session using MediaRecorder. */
|
|
29
36
|
async startRecording(options) {
|
|
30
37
|
if (this.mediaRecorder != null) {
|
|
31
38
|
throw alreadyRecordingError();
|
|
@@ -43,6 +50,7 @@ export class VoiceRecorderImpl {
|
|
|
43
50
|
.then((stream) => this.onSuccessfullyStartedRecording(stream, options))
|
|
44
51
|
.catch(this.onFailedToStartRecording.bind(this));
|
|
45
52
|
}
|
|
53
|
+
/** Stops the current recording and resolves the pending payload. */
|
|
46
54
|
async stopRecording() {
|
|
47
55
|
if (this.mediaRecorder == null) {
|
|
48
56
|
throw recordingHasNotStartedError();
|
|
@@ -59,6 +67,7 @@ export class VoiceRecorderImpl {
|
|
|
59
67
|
this.prepareInstanceForNextOperation();
|
|
60
68
|
}
|
|
61
69
|
}
|
|
70
|
+
/** Returns whether the browser has microphone permission. */
|
|
62
71
|
static async hasAudioRecordingPermission() {
|
|
63
72
|
// Safari does not support navigator.permissions.query
|
|
64
73
|
if (!navigator.permissions.query) {
|
|
@@ -78,6 +87,7 @@ export class VoiceRecorderImpl {
|
|
|
78
87
|
throw couldNotQueryPermissionStatusError();
|
|
79
88
|
});
|
|
80
89
|
}
|
|
90
|
+
/** Requests microphone permission from the browser. */
|
|
81
91
|
static async requestAudioRecordingPermission() {
|
|
82
92
|
const havingPermission = await VoiceRecorderImpl.hasAudioRecordingPermission().catch(() => failureResponse());
|
|
83
93
|
if (havingPermission.value) {
|
|
@@ -88,6 +98,7 @@ export class VoiceRecorderImpl {
|
|
|
88
98
|
.then(() => successResponse())
|
|
89
99
|
.catch(() => failureResponse());
|
|
90
100
|
}
|
|
101
|
+
/** Pauses the recording session when supported. */
|
|
91
102
|
pauseRecording() {
|
|
92
103
|
if (this.mediaRecorder == null) {
|
|
93
104
|
throw recordingHasNotStartedError();
|
|
@@ -100,6 +111,7 @@ export class VoiceRecorderImpl {
|
|
|
100
111
|
return Promise.resolve(failureResponse());
|
|
101
112
|
}
|
|
102
113
|
}
|
|
114
|
+
/** Resumes a paused recording session when supported. */
|
|
103
115
|
resumeRecording() {
|
|
104
116
|
if (this.mediaRecorder == null) {
|
|
105
117
|
throw recordingHasNotStartedError();
|
|
@@ -112,6 +124,7 @@ export class VoiceRecorderImpl {
|
|
|
112
124
|
return Promise.resolve(failureResponse());
|
|
113
125
|
}
|
|
114
126
|
}
|
|
127
|
+
/** Returns the current recording status from MediaRecorder. */
|
|
115
128
|
getCurrentStatus() {
|
|
116
129
|
if (this.mediaRecorder == null) {
|
|
117
130
|
return Promise.resolve({ status: 'NONE' });
|
|
@@ -126,12 +139,14 @@ export class VoiceRecorderImpl {
|
|
|
126
139
|
return Promise.resolve({ status: 'NONE' });
|
|
127
140
|
}
|
|
128
141
|
}
|
|
142
|
+
/** Returns the first supported MIME type, if any. */
|
|
129
143
|
static getSupportedMimeType() {
|
|
130
144
|
if ((MediaRecorder === null || MediaRecorder === void 0 ? void 0 : MediaRecorder.isTypeSupported) == null)
|
|
131
145
|
return null;
|
|
132
146
|
const foundSupportedType = Object.keys(POSSIBLE_MIME_TYPES).find((type) => MediaRecorder.isTypeSupported(type));
|
|
133
147
|
return foundSupportedType !== null && foundSupportedType !== void 0 ? foundSupportedType : null;
|
|
134
148
|
}
|
|
149
|
+
/** Initializes MediaRecorder and wires up handlers. */
|
|
135
150
|
onSuccessfullyStartedRecording(stream, options) {
|
|
136
151
|
this.pendingResult = new Promise((resolve, reject) => {
|
|
137
152
|
this.mediaRecorder = new MediaRecorder(stream);
|
|
@@ -179,10 +194,12 @@ export class VoiceRecorderImpl {
|
|
|
179
194
|
});
|
|
180
195
|
return successResponse();
|
|
181
196
|
}
|
|
197
|
+
/** Handles failures from getUserMedia. */
|
|
182
198
|
onFailedToStartRecording() {
|
|
183
199
|
this.prepareInstanceForNextOperation();
|
|
184
200
|
throw failedToRecordError();
|
|
185
201
|
}
|
|
202
|
+
/** Converts a Blob payload into a base64 string. */
|
|
186
203
|
static blobToBase64(blob) {
|
|
187
204
|
return new Promise((resolve) => {
|
|
188
205
|
const reader = new FileReader();
|
|
@@ -195,6 +212,7 @@ export class VoiceRecorderImpl {
|
|
|
195
212
|
reader.readAsDataURL(blob);
|
|
196
213
|
});
|
|
197
214
|
}
|
|
215
|
+
/** Resets state for the next recording attempt. */
|
|
198
216
|
prepareInstanceForNextOperation() {
|
|
199
217
|
if (this.mediaRecorder != null && this.mediaRecorder.state === 'recording') {
|
|
200
218
|
try {
|