@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/plugin.js
CHANGED
|
@@ -37,18 +37,28 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
|
|
|
37
37
|
return durationP;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/** Success wrapper for boolean plugin responses. */
|
|
40
41
|
const successResponse = () => ({ value: true });
|
|
42
|
+
/** Failure wrapper for boolean plugin responses. */
|
|
41
43
|
const failureResponse = () => ({ value: false });
|
|
44
|
+
/** Error for missing microphone permission. */
|
|
42
45
|
const missingPermissionError = () => new Error('MISSING_PERMISSION');
|
|
46
|
+
/** Error for attempting to start while already recording. */
|
|
43
47
|
const alreadyRecordingError = () => new Error('ALREADY_RECORDING');
|
|
48
|
+
/** Error for devices that cannot record audio. */
|
|
44
49
|
const deviceCannotVoiceRecordError = () => new Error('DEVICE_CANNOT_VOICE_RECORD');
|
|
50
|
+
/** Error for recorder start failures. */
|
|
45
51
|
const failedToRecordError = () => new Error('FAILED_TO_RECORD');
|
|
52
|
+
/** Error for empty or zero-length recordings. */
|
|
46
53
|
const emptyRecordingError = () => new Error('EMPTY_RECORDING');
|
|
54
|
+
/** Error for stopping without an active recording. */
|
|
47
55
|
const recordingHasNotStartedError = () => new Error('RECORDING_HAS_NOT_STARTED');
|
|
56
|
+
/** Error for failures when fetching recording data. */
|
|
48
57
|
const failedToFetchRecordingError = () => new Error('FAILED_TO_FETCH_RECORDING');
|
|
58
|
+
/** Error for browsers that do not support permission queries. */
|
|
49
59
|
const couldNotQueryPermissionStatusError = () => new Error('COULD_NOT_QUERY_PERMISSION_STATUS');
|
|
50
60
|
|
|
51
|
-
|
|
61
|
+
/** Preferred MIME types to probe in order of fallback. */
|
|
52
62
|
const POSSIBLE_MIME_TYPES = {
|
|
53
63
|
'audio/aac': '.aac',
|
|
54
64
|
'audio/webm;codecs=opus': '.ogg',
|
|
@@ -56,13 +66,19 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
|
|
|
56
66
|
'audio/webm': '.ogg',
|
|
57
67
|
'audio/ogg;codecs=opus': '.ogg',
|
|
58
68
|
};
|
|
69
|
+
/** Creates a promise that never resolves. */
|
|
59
70
|
const neverResolvingPromise = () => new Promise(() => undefined);
|
|
71
|
+
/** Browser implementation backed by MediaRecorder and Capacitor Filesystem. */
|
|
60
72
|
class VoiceRecorderImpl {
|
|
61
73
|
constructor() {
|
|
74
|
+
/** Active MediaRecorder instance, if recording. */
|
|
62
75
|
this.mediaRecorder = null;
|
|
76
|
+
/** Collected data chunks from MediaRecorder. */
|
|
63
77
|
this.chunks = [];
|
|
78
|
+
/** Promise resolved when the recorder stops and payload is ready. */
|
|
64
79
|
this.pendingResult = neverResolvingPromise();
|
|
65
80
|
}
|
|
81
|
+
/** Returns whether the browser can start a recording session. */
|
|
66
82
|
static async canDeviceVoiceRecord() {
|
|
67
83
|
var _a;
|
|
68
84
|
if (((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) == null || VoiceRecorderImpl.getSupportedMimeType() == null) {
|
|
@@ -72,6 +88,7 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
|
|
|
72
88
|
return successResponse();
|
|
73
89
|
}
|
|
74
90
|
}
|
|
91
|
+
/** Starts a recording session using MediaRecorder. */
|
|
75
92
|
async startRecording(options) {
|
|
76
93
|
if (this.mediaRecorder != null) {
|
|
77
94
|
throw alreadyRecordingError();
|
|
@@ -89,6 +106,7 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
|
|
|
89
106
|
.then((stream) => this.onSuccessfullyStartedRecording(stream, options))
|
|
90
107
|
.catch(this.onFailedToStartRecording.bind(this));
|
|
91
108
|
}
|
|
109
|
+
/** Stops the current recording and resolves the pending payload. */
|
|
92
110
|
async stopRecording() {
|
|
93
111
|
if (this.mediaRecorder == null) {
|
|
94
112
|
throw recordingHasNotStartedError();
|
|
@@ -105,6 +123,7 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
|
|
|
105
123
|
this.prepareInstanceForNextOperation();
|
|
106
124
|
}
|
|
107
125
|
}
|
|
126
|
+
/** Returns whether the browser has microphone permission. */
|
|
108
127
|
static async hasAudioRecordingPermission() {
|
|
109
128
|
// Safari does not support navigator.permissions.query
|
|
110
129
|
if (!navigator.permissions.query) {
|
|
@@ -124,6 +143,7 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
|
|
|
124
143
|
throw couldNotQueryPermissionStatusError();
|
|
125
144
|
});
|
|
126
145
|
}
|
|
146
|
+
/** Requests microphone permission from the browser. */
|
|
127
147
|
static async requestAudioRecordingPermission() {
|
|
128
148
|
const havingPermission = await VoiceRecorderImpl.hasAudioRecordingPermission().catch(() => failureResponse());
|
|
129
149
|
if (havingPermission.value) {
|
|
@@ -134,6 +154,7 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
|
|
|
134
154
|
.then(() => successResponse())
|
|
135
155
|
.catch(() => failureResponse());
|
|
136
156
|
}
|
|
157
|
+
/** Pauses the recording session when supported. */
|
|
137
158
|
pauseRecording() {
|
|
138
159
|
if (this.mediaRecorder == null) {
|
|
139
160
|
throw recordingHasNotStartedError();
|
|
@@ -146,6 +167,7 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
|
|
|
146
167
|
return Promise.resolve(failureResponse());
|
|
147
168
|
}
|
|
148
169
|
}
|
|
170
|
+
/** Resumes a paused recording session when supported. */
|
|
149
171
|
resumeRecording() {
|
|
150
172
|
if (this.mediaRecorder == null) {
|
|
151
173
|
throw recordingHasNotStartedError();
|
|
@@ -158,6 +180,7 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
|
|
|
158
180
|
return Promise.resolve(failureResponse());
|
|
159
181
|
}
|
|
160
182
|
}
|
|
183
|
+
/** Returns the current recording status from MediaRecorder. */
|
|
161
184
|
getCurrentStatus() {
|
|
162
185
|
if (this.mediaRecorder == null) {
|
|
163
186
|
return Promise.resolve({ status: 'NONE' });
|
|
@@ -172,12 +195,14 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
|
|
|
172
195
|
return Promise.resolve({ status: 'NONE' });
|
|
173
196
|
}
|
|
174
197
|
}
|
|
198
|
+
/** Returns the first supported MIME type, if any. */
|
|
175
199
|
static getSupportedMimeType() {
|
|
176
200
|
if ((MediaRecorder === null || MediaRecorder === void 0 ? void 0 : MediaRecorder.isTypeSupported) == null)
|
|
177
201
|
return null;
|
|
178
202
|
const foundSupportedType = Object.keys(POSSIBLE_MIME_TYPES).find((type) => MediaRecorder.isTypeSupported(type));
|
|
179
203
|
return foundSupportedType !== null && foundSupportedType !== void 0 ? foundSupportedType : null;
|
|
180
204
|
}
|
|
205
|
+
/** Initializes MediaRecorder and wires up handlers. */
|
|
181
206
|
onSuccessfullyStartedRecording(stream, options) {
|
|
182
207
|
this.pendingResult = new Promise((resolve, reject) => {
|
|
183
208
|
this.mediaRecorder = new MediaRecorder(stream);
|
|
@@ -225,10 +250,12 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
|
|
|
225
250
|
});
|
|
226
251
|
return successResponse();
|
|
227
252
|
}
|
|
253
|
+
/** Handles failures from getUserMedia. */
|
|
228
254
|
onFailedToStartRecording() {
|
|
229
255
|
this.prepareInstanceForNextOperation();
|
|
230
256
|
throw failedToRecordError();
|
|
231
257
|
}
|
|
258
|
+
/** Converts a Blob payload into a base64 string. */
|
|
232
259
|
static blobToBase64(blob) {
|
|
233
260
|
return new Promise((resolve) => {
|
|
234
261
|
const reader = new FileReader();
|
|
@@ -241,6 +268,7 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
|
|
|
241
268
|
reader.readAsDataURL(blob);
|
|
242
269
|
});
|
|
243
270
|
}
|
|
271
|
+
/** Resets state for the next recording attempt. */
|
|
244
272
|
prepareInstanceForNextOperation() {
|
|
245
273
|
if (this.mediaRecorder != null && this.mediaRecorder.state === 'recording') {
|
|
246
274
|
try {
|
|
@@ -256,34 +284,197 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
|
|
|
256
284
|
}
|
|
257
285
|
}
|
|
258
286
|
|
|
259
|
-
|
|
287
|
+
/** Web adapter that delegates to the browser-specific implementation. */
|
|
288
|
+
class VoiceRecorderWebAdapter {
|
|
260
289
|
constructor() {
|
|
261
|
-
|
|
262
|
-
this.
|
|
290
|
+
/** Browser implementation that talks to MediaRecorder APIs. */
|
|
291
|
+
this.voiceRecorderImpl = new VoiceRecorderImpl();
|
|
263
292
|
}
|
|
293
|
+
/** Checks whether the browser can record audio. */
|
|
264
294
|
canDeviceVoiceRecord() {
|
|
265
295
|
return VoiceRecorderImpl.canDeviceVoiceRecord();
|
|
266
296
|
}
|
|
297
|
+
/** Returns whether the browser has microphone permission. */
|
|
267
298
|
hasAudioRecordingPermission() {
|
|
268
299
|
return VoiceRecorderImpl.hasAudioRecordingPermission();
|
|
269
300
|
}
|
|
301
|
+
/** Requests microphone permission through the browser. */
|
|
270
302
|
requestAudioRecordingPermission() {
|
|
271
303
|
return VoiceRecorderImpl.requestAudioRecordingPermission();
|
|
272
304
|
}
|
|
305
|
+
/** Starts a recording session using MediaRecorder. */
|
|
306
|
+
startRecording(options) {
|
|
307
|
+
return this.voiceRecorderImpl.startRecording(options);
|
|
308
|
+
}
|
|
309
|
+
/** Stops the recording session and returns the payload. */
|
|
310
|
+
stopRecording() {
|
|
311
|
+
return this.voiceRecorderImpl.stopRecording();
|
|
312
|
+
}
|
|
313
|
+
/** Pauses the recording session when supported. */
|
|
314
|
+
pauseRecording() {
|
|
315
|
+
return this.voiceRecorderImpl.pauseRecording();
|
|
316
|
+
}
|
|
317
|
+
/** Resumes a paused recording session when supported. */
|
|
318
|
+
resumeRecording() {
|
|
319
|
+
return this.voiceRecorderImpl.resumeRecording();
|
|
320
|
+
}
|
|
321
|
+
/** Returns the current recording state. */
|
|
322
|
+
getCurrentStatus() {
|
|
323
|
+
return this.voiceRecorderImpl.getCurrentStatus();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** Default response shape when no config is provided. */
|
|
328
|
+
const DEFAULT_RESPONSE_FORMAT = 'legacy';
|
|
329
|
+
/** Parses a user-provided response format into a supported value. */
|
|
330
|
+
const resolveResponseFormat = (value) => {
|
|
331
|
+
if (typeof value === 'string' && value.toLowerCase() === 'normalized') {
|
|
332
|
+
return 'normalized';
|
|
333
|
+
}
|
|
334
|
+
return DEFAULT_RESPONSE_FORMAT;
|
|
335
|
+
};
|
|
336
|
+
/** Reads the response format from a Capacitor plugin config object. */
|
|
337
|
+
const getResponseFormatFromConfig = (config) => {
|
|
338
|
+
if (config && typeof config === 'object' && 'responseFormat' in config) {
|
|
339
|
+
return resolveResponseFormat(config.responseFormat);
|
|
340
|
+
}
|
|
341
|
+
return DEFAULT_RESPONSE_FORMAT;
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
/** Maps legacy error messages to canonical error codes. */
|
|
345
|
+
const legacyToCanonical = {
|
|
346
|
+
CANNOT_RECORD_ON_THIS_PHONE: 'DEVICE_CANNOT_VOICE_RECORD',
|
|
347
|
+
};
|
|
348
|
+
/** Normalizes legacy error messages into canonical error codes. */
|
|
349
|
+
const toCanonicalErrorCode = (legacyMessage) => {
|
|
350
|
+
var _a;
|
|
351
|
+
return (_a = legacyToCanonical[legacyMessage]) !== null && _a !== void 0 ? _a : legacyMessage;
|
|
352
|
+
};
|
|
353
|
+
/** Adds a canonical `code` field to Error-like objects when possible. */
|
|
354
|
+
const attachCanonicalErrorCode = (error) => {
|
|
355
|
+
if (!error || typeof error !== 'object') {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
const messageValue = error.message;
|
|
359
|
+
if (typeof messageValue !== 'string') {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
error.code = toCanonicalErrorCode(messageValue);
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
/** Normalizes recording payloads into a stable contract shape. */
|
|
366
|
+
const normalizeRecordingData = (data) => {
|
|
367
|
+
const { recordDataBase64, uri, msDuration, mimeType } = data.value;
|
|
368
|
+
const normalizedValue = { msDuration, mimeType };
|
|
369
|
+
const trimmedUri = typeof uri === 'string' && uri.length > 0 ? uri : undefined;
|
|
370
|
+
const trimmedBase64 = typeof recordDataBase64 === 'string' && recordDataBase64.length > 0 ? recordDataBase64 : undefined;
|
|
371
|
+
if (trimmedUri) {
|
|
372
|
+
normalizedValue.uri = trimmedUri;
|
|
373
|
+
}
|
|
374
|
+
else if (trimmedBase64) {
|
|
375
|
+
normalizedValue.recordDataBase64 = trimmedBase64;
|
|
376
|
+
}
|
|
377
|
+
return { value: normalizedValue };
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
/** Orchestrates platform calls and normalizes responses when requested. */
|
|
381
|
+
class VoiceRecorderService {
|
|
382
|
+
constructor(platform, responseFormat) {
|
|
383
|
+
this.platform = platform;
|
|
384
|
+
this.responseFormat = responseFormat;
|
|
385
|
+
}
|
|
386
|
+
/** Checks whether the device can record audio. */
|
|
387
|
+
canDeviceVoiceRecord() {
|
|
388
|
+
return this.execute(() => this.platform.canDeviceVoiceRecord());
|
|
389
|
+
}
|
|
390
|
+
/** Returns whether microphone permission is currently granted. */
|
|
391
|
+
hasAudioRecordingPermission() {
|
|
392
|
+
return this.execute(() => this.platform.hasAudioRecordingPermission());
|
|
393
|
+
}
|
|
394
|
+
/** Requests microphone permission from the user. */
|
|
395
|
+
requestAudioRecordingPermission() {
|
|
396
|
+
return this.execute(() => this.platform.requestAudioRecordingPermission());
|
|
397
|
+
}
|
|
398
|
+
/** Starts a recording session. */
|
|
399
|
+
startRecording(options) {
|
|
400
|
+
return this.execute(() => this.platform.startRecording(options));
|
|
401
|
+
}
|
|
402
|
+
/** Stops the recording session and formats the payload if needed. */
|
|
403
|
+
async stopRecording() {
|
|
404
|
+
return this.execute(async () => {
|
|
405
|
+
const data = await this.platform.stopRecording();
|
|
406
|
+
if (this.responseFormat === 'normalized') {
|
|
407
|
+
return normalizeRecordingData(data);
|
|
408
|
+
}
|
|
409
|
+
return data;
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
/** Pauses the recording session when supported. */
|
|
413
|
+
pauseRecording() {
|
|
414
|
+
return this.execute(() => this.platform.pauseRecording());
|
|
415
|
+
}
|
|
416
|
+
/** Resumes a paused recording session when supported. */
|
|
417
|
+
resumeRecording() {
|
|
418
|
+
return this.execute(() => this.platform.resumeRecording());
|
|
419
|
+
}
|
|
420
|
+
/** Returns the current recording state. */
|
|
421
|
+
getCurrentStatus() {
|
|
422
|
+
return this.execute(() => this.platform.getCurrentStatus());
|
|
423
|
+
}
|
|
424
|
+
/** Wraps calls to apply canonical error codes when requested. */
|
|
425
|
+
async execute(fn) {
|
|
426
|
+
try {
|
|
427
|
+
return await fn();
|
|
428
|
+
}
|
|
429
|
+
catch (error) {
|
|
430
|
+
if (this.responseFormat === 'normalized') {
|
|
431
|
+
attachCanonicalErrorCode(error);
|
|
432
|
+
}
|
|
433
|
+
throw error;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/** Web implementation of the VoiceRecorder Capacitor plugin. */
|
|
439
|
+
class VoiceRecorderWeb extends core.WebPlugin {
|
|
440
|
+
constructor() {
|
|
441
|
+
var _a, _b;
|
|
442
|
+
super();
|
|
443
|
+
const pluginConfig = (_b = (_a = core.Capacitor === null || core.Capacitor === void 0 ? void 0 : core.Capacitor.config) === null || _a === void 0 ? void 0 : _a.plugins) === null || _b === void 0 ? void 0 : _b.VoiceRecorder;
|
|
444
|
+
const responseFormat = getResponseFormatFromConfig(pluginConfig);
|
|
445
|
+
this.service = new VoiceRecorderService(new VoiceRecorderWebAdapter(), responseFormat);
|
|
446
|
+
}
|
|
447
|
+
/** Checks whether the browser can record audio. */
|
|
448
|
+
canDeviceVoiceRecord() {
|
|
449
|
+
return this.service.canDeviceVoiceRecord();
|
|
450
|
+
}
|
|
451
|
+
/** Returns whether microphone permission is currently granted. */
|
|
452
|
+
hasAudioRecordingPermission() {
|
|
453
|
+
return this.service.hasAudioRecordingPermission();
|
|
454
|
+
}
|
|
455
|
+
/** Requests microphone permission from the user. */
|
|
456
|
+
requestAudioRecordingPermission() {
|
|
457
|
+
return this.service.requestAudioRecordingPermission();
|
|
458
|
+
}
|
|
459
|
+
/** Starts a recording session. */
|
|
273
460
|
startRecording(options) {
|
|
274
|
-
return this.
|
|
461
|
+
return this.service.startRecording(options);
|
|
275
462
|
}
|
|
463
|
+
/** Stops the current recording session and returns the payload. */
|
|
276
464
|
stopRecording() {
|
|
277
|
-
return this.
|
|
465
|
+
return this.service.stopRecording();
|
|
278
466
|
}
|
|
467
|
+
/** Pauses the recording session when supported. */
|
|
279
468
|
pauseRecording() {
|
|
280
|
-
return this.
|
|
469
|
+
return this.service.pauseRecording();
|
|
281
470
|
}
|
|
471
|
+
/** Resumes a paused recording session when supported. */
|
|
282
472
|
resumeRecording() {
|
|
283
|
-
return this.
|
|
473
|
+
return this.service.resumeRecording();
|
|
284
474
|
}
|
|
475
|
+
/** Returns the current recording state. */
|
|
285
476
|
getCurrentStatus() {
|
|
286
|
-
return this.
|
|
477
|
+
return this.service.getCurrentStatus();
|
|
287
478
|
}
|
|
288
479
|
}
|
|
289
480
|
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/helper/get-blob-duration.js","esm/predefined-web-responses.js","esm/VoiceRecorderImpl.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst VoiceRecorder = registerPlugin('VoiceRecorder', {\n web: () => import('./web').then((m) => new m.VoiceRecorderWeb()),\n});\nexport * from './definitions';\nexport { VoiceRecorder };\n//# sourceMappingURL=index.js.map","/**\n * @param {Blob | string} blob\n * @returns {Promise<number>} Blob duration in seconds.\n */\nexport default function getBlobDuration(blob) {\n const tempVideoEl = document.createElement('video');\n if (!tempVideoEl)\n throw new Error('Failed to create video element');\n const durationP = new Promise((resolve, reject) => {\n tempVideoEl.addEventListener('loadedmetadata', () => {\n // Chrome bug: https://bugs.chromium.org/p/chromium/issues/detail?id=642012\n if (tempVideoEl.duration === Infinity) {\n tempVideoEl.currentTime = Number.MAX_SAFE_INTEGER;\n tempVideoEl.ontimeupdate = () => {\n tempVideoEl.ontimeupdate = null;\n resolve(tempVideoEl.duration);\n tempVideoEl.currentTime = 0;\n };\n }\n else {\n resolve(tempVideoEl.duration);\n }\n });\n tempVideoEl.onerror = (event) => {\n const error = event.error || new Error('Unknown error occurred');\n reject(error);\n };\n });\n tempVideoEl.src = typeof blob === 'string' ? blob : URL.createObjectURL(blob);\n return durationP;\n}\n//# sourceMappingURL=get-blob-duration.js.map","export const successResponse = () => ({ value: true });\nexport const failureResponse = () => ({ value: false });\nexport const missingPermissionError = () => new Error('MISSING_PERMISSION');\nexport const alreadyRecordingError = () => new Error('ALREADY_RECORDING');\nexport const microphoneBeingUsedError = () => new Error('MICROPHONE_BEING_USED');\nexport const deviceCannotVoiceRecordError = () => new Error('DEVICE_CANNOT_VOICE_RECORD');\nexport const failedToRecordError = () => new Error('FAILED_TO_RECORD');\nexport const emptyRecordingError = () => new Error('EMPTY_RECORDING');\nexport const recordingHasNotStartedError = () => new Error('RECORDING_HAS_NOT_STARTED');\nexport const failedToFetchRecordingError = () => new Error('FAILED_TO_FETCH_RECORDING');\nexport const couldNotQueryPermissionStatusError = () => new Error('COULD_NOT_QUERY_PERMISSION_STATUS');\n//# sourceMappingURL=predefined-web-responses.js.map","import { Filesystem } from '@capacitor/filesystem';\nimport write_blob from 'capacitor-blob-writer';\nimport getBlobDuration from \"./helper/get-blob-duration\";\nimport { alreadyRecordingError, couldNotQueryPermissionStatusError, deviceCannotVoiceRecordError, emptyRecordingError, failedToFetchRecordingError, failedToRecordError, failureResponse, missingPermissionError, recordingHasNotStartedError, successResponse, } from './predefined-web-responses';\n// these mime types will be checked one by one in order until one of them is found to be supported by the current browser\nconst POSSIBLE_MIME_TYPES = {\n 'audio/aac': '.aac',\n 'audio/webm;codecs=opus': '.ogg',\n 'audio/mp4': '.mp3',\n 'audio/webm': '.ogg',\n 'audio/ogg;codecs=opus': '.ogg',\n};\nconst neverResolvingPromise = () => new Promise(() => undefined);\nexport class VoiceRecorderImpl {\n constructor() {\n this.mediaRecorder = null;\n this.chunks = [];\n this.pendingResult = neverResolvingPromise();\n }\n static async canDeviceVoiceRecord() {\n var _a;\n if (((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) == null || VoiceRecorderImpl.getSupportedMimeType() == null) {\n return failureResponse();\n }\n else {\n return successResponse();\n }\n }\n async startRecording(options) {\n if (this.mediaRecorder != null) {\n throw alreadyRecordingError();\n }\n const deviceCanRecord = await VoiceRecorderImpl.canDeviceVoiceRecord();\n if (!deviceCanRecord.value) {\n throw deviceCannotVoiceRecordError();\n }\n const havingPermission = await VoiceRecorderImpl.hasAudioRecordingPermission().catch(() => successResponse());\n if (!havingPermission.value) {\n throw missingPermissionError();\n }\n return navigator.mediaDevices\n .getUserMedia({ audio: true })\n .then((stream) => this.onSuccessfullyStartedRecording(stream, options))\n .catch(this.onFailedToStartRecording.bind(this));\n }\n async stopRecording() {\n if (this.mediaRecorder == null) {\n throw recordingHasNotStartedError();\n }\n try {\n this.mediaRecorder.stop();\n this.mediaRecorder.stream.getTracks().forEach((track) => track.stop());\n return this.pendingResult;\n }\n catch (ignore) {\n throw failedToFetchRecordingError();\n }\n finally {\n this.prepareInstanceForNextOperation();\n }\n }\n static async hasAudioRecordingPermission() {\n // Safari does not support navigator.permissions.query\n if (!navigator.permissions.query) {\n if (navigator.mediaDevices !== undefined) {\n return navigator.mediaDevices\n .getUserMedia({ audio: true })\n .then(() => successResponse())\n .catch(() => {\n throw couldNotQueryPermissionStatusError();\n });\n }\n }\n return navigator.permissions\n .query({ name: 'microphone' })\n .then((result) => ({ value: result.state === 'granted' }))\n .catch(() => {\n throw couldNotQueryPermissionStatusError();\n });\n }\n static async requestAudioRecordingPermission() {\n const havingPermission = await VoiceRecorderImpl.hasAudioRecordingPermission().catch(() => failureResponse());\n if (havingPermission.value) {\n return successResponse();\n }\n return navigator.mediaDevices\n .getUserMedia({ audio: true })\n .then(() => successResponse())\n .catch(() => failureResponse());\n }\n pauseRecording() {\n if (this.mediaRecorder == null) {\n throw recordingHasNotStartedError();\n }\n else if (this.mediaRecorder.state === 'recording') {\n this.mediaRecorder.pause();\n return Promise.resolve(successResponse());\n }\n else {\n return Promise.resolve(failureResponse());\n }\n }\n resumeRecording() {\n if (this.mediaRecorder == null) {\n throw recordingHasNotStartedError();\n }\n else if (this.mediaRecorder.state === 'paused') {\n this.mediaRecorder.resume();\n return Promise.resolve(successResponse());\n }\n else {\n return Promise.resolve(failureResponse());\n }\n }\n getCurrentStatus() {\n if (this.mediaRecorder == null) {\n return Promise.resolve({ status: 'NONE' });\n }\n else if (this.mediaRecorder.state === 'recording') {\n return Promise.resolve({ status: 'RECORDING' });\n }\n else if (this.mediaRecorder.state === 'paused') {\n return Promise.resolve({ status: 'PAUSED' });\n }\n else {\n return Promise.resolve({ status: 'NONE' });\n }\n }\n static getSupportedMimeType() {\n if ((MediaRecorder === null || MediaRecorder === void 0 ? void 0 : MediaRecorder.isTypeSupported) == null)\n return null;\n const foundSupportedType = Object.keys(POSSIBLE_MIME_TYPES).find((type) => MediaRecorder.isTypeSupported(type));\n return foundSupportedType !== null && foundSupportedType !== void 0 ? foundSupportedType : null;\n }\n onSuccessfullyStartedRecording(stream, options) {\n this.pendingResult = new Promise((resolve, reject) => {\n this.mediaRecorder = new MediaRecorder(stream);\n this.mediaRecorder.onerror = () => {\n this.prepareInstanceForNextOperation();\n reject(failedToRecordError());\n };\n this.mediaRecorder.onstop = async () => {\n var _a, _b, _c;\n const mimeType = VoiceRecorderImpl.getSupportedMimeType();\n if (mimeType == null) {\n this.prepareInstanceForNextOperation();\n reject(failedToFetchRecordingError());\n return;\n }\n const blobVoiceRecording = new Blob(this.chunks, { type: mimeType });\n if (blobVoiceRecording.size <= 0) {\n this.prepareInstanceForNextOperation();\n reject(emptyRecordingError());\n return;\n }\n let uri = undefined;\n let recordDataBase64 = '';\n if (options === null || options === void 0 ? void 0 : options.directory) {\n const subDirectory = (_c = (_b = (_a = options.subDirectory) === null || _a === void 0 ? void 0 : _a.match(/^\\/?(.+[^/])\\/?$/)) === null || _b === void 0 ? void 0 : _b[1]) !== null && _c !== void 0 ? _c : '';\n const path = `${subDirectory}/recording-${new Date().getTime()}${POSSIBLE_MIME_TYPES[mimeType]}`;\n await write_blob({\n blob: blobVoiceRecording,\n directory: options.directory,\n fast_mode: true,\n path,\n recursive: true,\n });\n ({ uri } = await Filesystem.getUri({ directory: options.directory, path }));\n }\n else {\n recordDataBase64 = await VoiceRecorderImpl.blobToBase64(blobVoiceRecording);\n }\n const recordingDuration = await getBlobDuration(blobVoiceRecording);\n this.prepareInstanceForNextOperation();\n resolve({ value: { recordDataBase64, mimeType, msDuration: recordingDuration * 1000, uri } });\n };\n this.mediaRecorder.ondataavailable = (event) => this.chunks.push(event.data);\n this.mediaRecorder.start();\n });\n return successResponse();\n }\n onFailedToStartRecording() {\n this.prepareInstanceForNextOperation();\n throw failedToRecordError();\n }\n static blobToBase64(blob) {\n return new Promise((resolve) => {\n const reader = new FileReader();\n reader.onloadend = () => {\n const recordingResult = String(reader.result);\n const splitResult = recordingResult.split('base64,');\n const toResolve = splitResult.length > 1 ? splitResult[1] : recordingResult;\n resolve(toResolve.trim());\n };\n reader.readAsDataURL(blob);\n });\n }\n prepareInstanceForNextOperation() {\n if (this.mediaRecorder != null && this.mediaRecorder.state === 'recording') {\n try {\n this.mediaRecorder.stop();\n }\n catch (ignore) {\n console.warn('Failed to stop recording during cleanup');\n }\n }\n this.pendingResult = neverResolvingPromise();\n this.mediaRecorder = null;\n this.chunks = [];\n }\n}\n//# sourceMappingURL=VoiceRecorderImpl.js.map","import { WebPlugin } from '@capacitor/core';\nimport { VoiceRecorderImpl } from './VoiceRecorderImpl';\nexport class VoiceRecorderWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.voiceRecorderInstance = new VoiceRecorderImpl();\n }\n canDeviceVoiceRecord() {\n return VoiceRecorderImpl.canDeviceVoiceRecord();\n }\n hasAudioRecordingPermission() {\n return VoiceRecorderImpl.hasAudioRecordingPermission();\n }\n requestAudioRecordingPermission() {\n return VoiceRecorderImpl.requestAudioRecordingPermission();\n }\n startRecording(options) {\n return this.voiceRecorderInstance.startRecording(options);\n }\n stopRecording() {\n return this.voiceRecorderInstance.stopRecording();\n }\n pauseRecording() {\n return this.voiceRecorderInstance.pauseRecording();\n }\n resumeRecording() {\n return this.voiceRecorderInstance.resumeRecording();\n }\n getCurrentStatus() {\n return this.voiceRecorderInstance.getCurrentStatus();\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","Filesystem","WebPlugin"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/platform/web/get-blob-duration.js","esm/platform/web/predefined-web-responses.js","esm/platform/web/VoiceRecorderImpl.js","esm/adapters/VoiceRecorderWebAdapter.js","esm/core/response-format.js","esm/core/error-codes.js","esm/core/recording-contract.js","esm/service/VoiceRecorderService.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst VoiceRecorder = registerPlugin('VoiceRecorder', {\n web: () => import('./web').then((m) => new m.VoiceRecorderWeb()),\n});\nexport * from './definitions';\nexport { VoiceRecorder };\n//# sourceMappingURL=index.js.map","/**\n * @param {Blob | string} blob\n * @returns {Promise<number>} Blob duration in seconds.\n */\nexport default function getBlobDuration(blob) {\n const tempVideoEl = document.createElement('video');\n if (!tempVideoEl)\n throw new Error('Failed to create video element');\n const durationP = new Promise((resolve, reject) => {\n tempVideoEl.addEventListener('loadedmetadata', () => {\n // Chrome bug: https://bugs.chromium.org/p/chromium/issues/detail?id=642012\n if (tempVideoEl.duration === Infinity) {\n tempVideoEl.currentTime = Number.MAX_SAFE_INTEGER;\n tempVideoEl.ontimeupdate = () => {\n tempVideoEl.ontimeupdate = null;\n resolve(tempVideoEl.duration);\n tempVideoEl.currentTime = 0;\n };\n }\n else {\n resolve(tempVideoEl.duration);\n }\n });\n tempVideoEl.onerror = (event) => {\n const error = event.error || new Error('Unknown error occurred');\n reject(error);\n };\n });\n tempVideoEl.src = typeof blob === 'string' ? blob : URL.createObjectURL(blob);\n return durationP;\n}\n//# sourceMappingURL=get-blob-duration.js.map","/** Success wrapper for boolean plugin responses. */\nexport const successResponse = () => ({ value: true });\n/** Failure wrapper for boolean plugin responses. */\nexport const failureResponse = () => ({ value: false });\n/** Error for missing microphone permission. */\nexport const missingPermissionError = () => new Error('MISSING_PERMISSION');\n/** Error for attempting to start while already recording. */\nexport const alreadyRecordingError = () => new Error('ALREADY_RECORDING');\n/** Error for microphone in use by another app or recorder. */\nexport const microphoneBeingUsedError = () => new Error('MICROPHONE_BEING_USED');\n/** Error for devices that cannot record audio. */\nexport const deviceCannotVoiceRecordError = () => new Error('DEVICE_CANNOT_VOICE_RECORD');\n/** Error for recorder start failures. */\nexport const failedToRecordError = () => new Error('FAILED_TO_RECORD');\n/** Error for empty or zero-length recordings. */\nexport const emptyRecordingError = () => new Error('EMPTY_RECORDING');\n/** Error for stopping without an active recording. */\nexport const recordingHasNotStartedError = () => new Error('RECORDING_HAS_NOT_STARTED');\n/** Error for failures when fetching recording data. */\nexport const failedToFetchRecordingError = () => new Error('FAILED_TO_FETCH_RECORDING');\n/** Error for browsers that do not support permission queries. */\nexport const couldNotQueryPermissionStatusError = () => new Error('COULD_NOT_QUERY_PERMISSION_STATUS');\n//# sourceMappingURL=predefined-web-responses.js.map","import { Filesystem } from '@capacitor/filesystem';\nimport write_blob from 'capacitor-blob-writer';\nimport getBlobDuration from './get-blob-duration';\nimport { alreadyRecordingError, couldNotQueryPermissionStatusError, deviceCannotVoiceRecordError, emptyRecordingError, failedToFetchRecordingError, failedToRecordError, failureResponse, missingPermissionError, recordingHasNotStartedError, successResponse, } from './predefined-web-responses';\n/** Preferred MIME types to probe in order of fallback. */\nconst POSSIBLE_MIME_TYPES = {\n 'audio/aac': '.aac',\n 'audio/webm;codecs=opus': '.ogg',\n 'audio/mp4': '.mp3',\n 'audio/webm': '.ogg',\n 'audio/ogg;codecs=opus': '.ogg',\n};\n/** Creates a promise that never resolves. */\nconst neverResolvingPromise = () => new Promise(() => undefined);\n/** Browser implementation backed by MediaRecorder and Capacitor Filesystem. */\nexport class VoiceRecorderImpl {\n constructor() {\n /** Active MediaRecorder instance, if recording. */\n this.mediaRecorder = null;\n /** Collected data chunks from MediaRecorder. */\n this.chunks = [];\n /** Promise resolved when the recorder stops and payload is ready. */\n this.pendingResult = neverResolvingPromise();\n }\n /** Returns whether the browser can start a recording session. */\n static async canDeviceVoiceRecord() {\n var _a;\n if (((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) == null || VoiceRecorderImpl.getSupportedMimeType() == null) {\n return failureResponse();\n }\n else {\n return successResponse();\n }\n }\n /** Starts a recording session using MediaRecorder. */\n async startRecording(options) {\n if (this.mediaRecorder != null) {\n throw alreadyRecordingError();\n }\n const deviceCanRecord = await VoiceRecorderImpl.canDeviceVoiceRecord();\n if (!deviceCanRecord.value) {\n throw deviceCannotVoiceRecordError();\n }\n const havingPermission = await VoiceRecorderImpl.hasAudioRecordingPermission().catch(() => successResponse());\n if (!havingPermission.value) {\n throw missingPermissionError();\n }\n return navigator.mediaDevices\n .getUserMedia({ audio: true })\n .then((stream) => this.onSuccessfullyStartedRecording(stream, options))\n .catch(this.onFailedToStartRecording.bind(this));\n }\n /** Stops the current recording and resolves the pending payload. */\n async stopRecording() {\n if (this.mediaRecorder == null) {\n throw recordingHasNotStartedError();\n }\n try {\n this.mediaRecorder.stop();\n this.mediaRecorder.stream.getTracks().forEach((track) => track.stop());\n return this.pendingResult;\n }\n catch (ignore) {\n throw failedToFetchRecordingError();\n }\n finally {\n this.prepareInstanceForNextOperation();\n }\n }\n /** Returns whether the browser has microphone permission. */\n static async hasAudioRecordingPermission() {\n // Safari does not support navigator.permissions.query\n if (!navigator.permissions.query) {\n if (navigator.mediaDevices !== undefined) {\n return navigator.mediaDevices\n .getUserMedia({ audio: true })\n .then(() => successResponse())\n .catch(() => {\n throw couldNotQueryPermissionStatusError();\n });\n }\n }\n return navigator.permissions\n .query({ name: 'microphone' })\n .then((result) => ({ value: result.state === 'granted' }))\n .catch(() => {\n throw couldNotQueryPermissionStatusError();\n });\n }\n /** Requests microphone permission from the browser. */\n static async requestAudioRecordingPermission() {\n const havingPermission = await VoiceRecorderImpl.hasAudioRecordingPermission().catch(() => failureResponse());\n if (havingPermission.value) {\n return successResponse();\n }\n return navigator.mediaDevices\n .getUserMedia({ audio: true })\n .then(() => successResponse())\n .catch(() => failureResponse());\n }\n /** Pauses the recording session when supported. */\n pauseRecording() {\n if (this.mediaRecorder == null) {\n throw recordingHasNotStartedError();\n }\n else if (this.mediaRecorder.state === 'recording') {\n this.mediaRecorder.pause();\n return Promise.resolve(successResponse());\n }\n else {\n return Promise.resolve(failureResponse());\n }\n }\n /** Resumes a paused recording session when supported. */\n resumeRecording() {\n if (this.mediaRecorder == null) {\n throw recordingHasNotStartedError();\n }\n else if (this.mediaRecorder.state === 'paused') {\n this.mediaRecorder.resume();\n return Promise.resolve(successResponse());\n }\n else {\n return Promise.resolve(failureResponse());\n }\n }\n /** Returns the current recording status from MediaRecorder. */\n getCurrentStatus() {\n if (this.mediaRecorder == null) {\n return Promise.resolve({ status: 'NONE' });\n }\n else if (this.mediaRecorder.state === 'recording') {\n return Promise.resolve({ status: 'RECORDING' });\n }\n else if (this.mediaRecorder.state === 'paused') {\n return Promise.resolve({ status: 'PAUSED' });\n }\n else {\n return Promise.resolve({ status: 'NONE' });\n }\n }\n /** Returns the first supported MIME type, if any. */\n static getSupportedMimeType() {\n if ((MediaRecorder === null || MediaRecorder === void 0 ? void 0 : MediaRecorder.isTypeSupported) == null)\n return null;\n const foundSupportedType = Object.keys(POSSIBLE_MIME_TYPES).find((type) => MediaRecorder.isTypeSupported(type));\n return foundSupportedType !== null && foundSupportedType !== void 0 ? foundSupportedType : null;\n }\n /** Initializes MediaRecorder and wires up handlers. */\n onSuccessfullyStartedRecording(stream, options) {\n this.pendingResult = new Promise((resolve, reject) => {\n this.mediaRecorder = new MediaRecorder(stream);\n this.mediaRecorder.onerror = () => {\n this.prepareInstanceForNextOperation();\n reject(failedToRecordError());\n };\n this.mediaRecorder.onstop = async () => {\n var _a, _b, _c;\n const mimeType = VoiceRecorderImpl.getSupportedMimeType();\n if (mimeType == null) {\n this.prepareInstanceForNextOperation();\n reject(failedToFetchRecordingError());\n return;\n }\n const blobVoiceRecording = new Blob(this.chunks, { type: mimeType });\n if (blobVoiceRecording.size <= 0) {\n this.prepareInstanceForNextOperation();\n reject(emptyRecordingError());\n return;\n }\n let uri = undefined;\n let recordDataBase64 = '';\n if (options === null || options === void 0 ? void 0 : options.directory) {\n const subDirectory = (_c = (_b = (_a = options.subDirectory) === null || _a === void 0 ? void 0 : _a.match(/^\\/?(.+[^/])\\/?$/)) === null || _b === void 0 ? void 0 : _b[1]) !== null && _c !== void 0 ? _c : '';\n const path = `${subDirectory}/recording-${new Date().getTime()}${POSSIBLE_MIME_TYPES[mimeType]}`;\n await write_blob({\n blob: blobVoiceRecording,\n directory: options.directory,\n fast_mode: true,\n path,\n recursive: true,\n });\n ({ uri } = await Filesystem.getUri({ directory: options.directory, path }));\n }\n else {\n recordDataBase64 = await VoiceRecorderImpl.blobToBase64(blobVoiceRecording);\n }\n const recordingDuration = await getBlobDuration(blobVoiceRecording);\n this.prepareInstanceForNextOperation();\n resolve({ value: { recordDataBase64, mimeType, msDuration: recordingDuration * 1000, uri } });\n };\n this.mediaRecorder.ondataavailable = (event) => this.chunks.push(event.data);\n this.mediaRecorder.start();\n });\n return successResponse();\n }\n /** Handles failures from getUserMedia. */\n onFailedToStartRecording() {\n this.prepareInstanceForNextOperation();\n throw failedToRecordError();\n }\n /** Converts a Blob payload into a base64 string. */\n static blobToBase64(blob) {\n return new Promise((resolve) => {\n const reader = new FileReader();\n reader.onloadend = () => {\n const recordingResult = String(reader.result);\n const splitResult = recordingResult.split('base64,');\n const toResolve = splitResult.length > 1 ? splitResult[1] : recordingResult;\n resolve(toResolve.trim());\n };\n reader.readAsDataURL(blob);\n });\n }\n /** Resets state for the next recording attempt. */\n prepareInstanceForNextOperation() {\n if (this.mediaRecorder != null && this.mediaRecorder.state === 'recording') {\n try {\n this.mediaRecorder.stop();\n }\n catch (ignore) {\n console.warn('Failed to stop recording during cleanup');\n }\n }\n this.pendingResult = neverResolvingPromise();\n this.mediaRecorder = null;\n this.chunks = [];\n }\n}\n//# sourceMappingURL=VoiceRecorderImpl.js.map","import { VoiceRecorderImpl } from '../platform/web/VoiceRecorderImpl';\n/** Web adapter that delegates to the browser-specific implementation. */\nexport class VoiceRecorderWebAdapter {\n constructor() {\n /** Browser implementation that talks to MediaRecorder APIs. */\n this.voiceRecorderImpl = new VoiceRecorderImpl();\n }\n /** Checks whether the browser can record audio. */\n canDeviceVoiceRecord() {\n return VoiceRecorderImpl.canDeviceVoiceRecord();\n }\n /** Returns whether the browser has microphone permission. */\n hasAudioRecordingPermission() {\n return VoiceRecorderImpl.hasAudioRecordingPermission();\n }\n /** Requests microphone permission through the browser. */\n requestAudioRecordingPermission() {\n return VoiceRecorderImpl.requestAudioRecordingPermission();\n }\n /** Starts a recording session using MediaRecorder. */\n startRecording(options) {\n return this.voiceRecorderImpl.startRecording(options);\n }\n /** Stops the recording session and returns the payload. */\n stopRecording() {\n return this.voiceRecorderImpl.stopRecording();\n }\n /** Pauses the recording session when supported. */\n pauseRecording() {\n return this.voiceRecorderImpl.pauseRecording();\n }\n /** Resumes a paused recording session when supported. */\n resumeRecording() {\n return this.voiceRecorderImpl.resumeRecording();\n }\n /** Returns the current recording state. */\n getCurrentStatus() {\n return this.voiceRecorderImpl.getCurrentStatus();\n }\n}\n//# sourceMappingURL=VoiceRecorderWebAdapter.js.map","/** Default response shape when no config is provided. */\nexport const DEFAULT_RESPONSE_FORMAT = 'legacy';\n/** Parses a user-provided response format into a supported value. */\nexport const resolveResponseFormat = (value) => {\n if (typeof value === 'string' && value.toLowerCase() === 'normalized') {\n return 'normalized';\n }\n return DEFAULT_RESPONSE_FORMAT;\n};\n/** Reads the response format from a Capacitor plugin config object. */\nexport const getResponseFormatFromConfig = (config) => {\n if (config && typeof config === 'object' && 'responseFormat' in config) {\n return resolveResponseFormat(config.responseFormat);\n }\n return DEFAULT_RESPONSE_FORMAT;\n};\n//# sourceMappingURL=response-format.js.map","/** Maps legacy error messages to canonical error codes. */\nconst legacyToCanonical = {\n CANNOT_RECORD_ON_THIS_PHONE: 'DEVICE_CANNOT_VOICE_RECORD',\n};\n/** Normalizes legacy error messages into canonical error codes. */\nexport const toCanonicalErrorCode = (legacyMessage) => {\n var _a;\n return (_a = legacyToCanonical[legacyMessage]) !== null && _a !== void 0 ? _a : legacyMessage;\n};\n/** Adds a canonical `code` field to Error-like objects when possible. */\nexport const attachCanonicalErrorCode = (error) => {\n if (!error || typeof error !== 'object') {\n return;\n }\n const messageValue = error.message;\n if (typeof messageValue !== 'string') {\n return;\n }\n error.code = toCanonicalErrorCode(messageValue);\n};\n//# sourceMappingURL=error-codes.js.map","/** Normalizes recording payloads into a stable contract shape. */\nexport const normalizeRecordingData = (data) => {\n const { recordDataBase64, uri, msDuration, mimeType } = data.value;\n const normalizedValue = { 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 if (trimmedUri) {\n normalizedValue.uri = trimmedUri;\n }\n else if (trimmedBase64) {\n normalizedValue.recordDataBase64 = trimmedBase64;\n }\n return { value: normalizedValue };\n};\n//# sourceMappingURL=recording-contract.js.map","import { attachCanonicalErrorCode } from '../core/error-codes';\nimport { normalizeRecordingData } from '../core/recording-contract';\n/** Orchestrates platform calls and normalizes responses when requested. */\nexport class VoiceRecorderService {\n constructor(platform, responseFormat) {\n this.platform = platform;\n this.responseFormat = responseFormat;\n }\n /** Checks whether the device can record audio. */\n canDeviceVoiceRecord() {\n return this.execute(() => this.platform.canDeviceVoiceRecord());\n }\n /** Returns whether microphone permission is currently granted. */\n hasAudioRecordingPermission() {\n return this.execute(() => this.platform.hasAudioRecordingPermission());\n }\n /** Requests microphone permission from the user. */\n requestAudioRecordingPermission() {\n return this.execute(() => this.platform.requestAudioRecordingPermission());\n }\n /** Starts a recording session. */\n startRecording(options) {\n return this.execute(() => this.platform.startRecording(options));\n }\n /** Stops the recording session and formats the payload if needed. */\n async stopRecording() {\n return this.execute(async () => {\n const data = await this.platform.stopRecording();\n if (this.responseFormat === 'normalized') {\n return normalizeRecordingData(data);\n }\n return data;\n });\n }\n /** Pauses the recording session when supported. */\n pauseRecording() {\n return this.execute(() => this.platform.pauseRecording());\n }\n /** Resumes a paused recording session when supported. */\n resumeRecording() {\n return this.execute(() => this.platform.resumeRecording());\n }\n /** Returns the current recording state. */\n getCurrentStatus() {\n return this.execute(() => this.platform.getCurrentStatus());\n }\n /** Wraps calls to apply canonical error codes when requested. */\n async execute(fn) {\n try {\n return await fn();\n }\n catch (error) {\n if (this.responseFormat === 'normalized') {\n attachCanonicalErrorCode(error);\n }\n throw error;\n }\n }\n}\n//# sourceMappingURL=VoiceRecorderService.js.map","import { Capacitor, WebPlugin } from '@capacitor/core';\nimport { VoiceRecorderWebAdapter } from './adapters/VoiceRecorderWebAdapter';\nimport { getResponseFormatFromConfig } from './core/response-format';\nimport { VoiceRecorderService } from './service/VoiceRecorderService';\n/** Web implementation of the VoiceRecorder Capacitor plugin. */\nexport class VoiceRecorderWeb extends WebPlugin {\n constructor() {\n var _a, _b;\n super();\n const pluginConfig = (_b = (_a = Capacitor === null || Capacitor === void 0 ? void 0 : Capacitor.config) === null || _a === void 0 ? void 0 : _a.plugins) === null || _b === void 0 ? void 0 : _b.VoiceRecorder;\n const responseFormat = getResponseFormatFromConfig(pluginConfig);\n this.service = new VoiceRecorderService(new VoiceRecorderWebAdapter(), responseFormat);\n }\n /** Checks whether the browser can record audio. */\n canDeviceVoiceRecord() {\n return this.service.canDeviceVoiceRecord();\n }\n /** Returns whether microphone permission is currently granted. */\n hasAudioRecordingPermission() {\n return this.service.hasAudioRecordingPermission();\n }\n /** Requests microphone permission from the user. */\n requestAudioRecordingPermission() {\n return this.service.requestAudioRecordingPermission();\n }\n /** Starts a recording session. */\n startRecording(options) {\n return this.service.startRecording(options);\n }\n /** Stops the current recording session and returns the payload. */\n stopRecording() {\n return this.service.stopRecording();\n }\n /** Pauses the recording session when supported. */\n pauseRecording() {\n return this.service.pauseRecording();\n }\n /** Resumes a paused recording session when supported. */\n resumeRecording() {\n return this.service.resumeRecording();\n }\n /** Returns the current recording state. */\n getCurrentStatus() {\n return this.service.getCurrentStatus();\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","Filesystem","WebPlugin","Capacitor"],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import AVFoundation
|
|
3
|
+
|
|
4
|
+
/// Default platform adapter for file IO and duration lookup.
|
|
5
|
+
final class DefaultRecorderPlatform: RecorderPlatform {
|
|
6
|
+
/// Returns whether the device can record audio.
|
|
7
|
+
func canDeviceVoiceRecord() -> Bool {
|
|
8
|
+
return true
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/// Reads a file as base64, returning nil on failure.
|
|
12
|
+
func readFileAsBase64(_ filePath: URL?) -> String? {
|
|
13
|
+
guard let filePath = filePath else {
|
|
14
|
+
return nil
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
do {
|
|
18
|
+
let fileData = try Data(contentsOf: filePath)
|
|
19
|
+
return fileData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
|
|
20
|
+
} catch {
|
|
21
|
+
return nil
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// Returns the file duration in milliseconds, or -1 on failure.
|
|
26
|
+
func getDurationMs(_ filePath: URL?) -> Int {
|
|
27
|
+
guard let filePath = filePath else {
|
|
28
|
+
return -1
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return Int(CMTimeGetSeconds(AVURLAsset(url: filePath).duration) * 1000)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// Maps record data into legacy or normalized dictionary payloads.
|
|
4
|
+
struct RecordDataMapper {
|
|
5
|
+
/// Converts record data to the legacy payload shape.
|
|
6
|
+
static func toLegacyDictionary(_ recordData: RecordData) -> Dictionary<String, Any> {
|
|
7
|
+
return recordData.toDictionary()
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/// Converts record data to the normalized payload shape.
|
|
11
|
+
static func toNormalizedDictionary(_ recordData: RecordData) -> Dictionary<String, Any> {
|
|
12
|
+
var normalized: Dictionary<String, Any> = [
|
|
13
|
+
"msDuration": recordData.msDuration,
|
|
14
|
+
"mimeType": recordData.mimeType,
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
if let uri = normalizedUri(from: recordData.uri) {
|
|
18
|
+
normalized["uri"] = uri
|
|
19
|
+
} else if let base64 = recordData.recordDataBase64, !base64.isEmpty {
|
|
20
|
+
normalized["recordDataBase64"] = base64
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return normalized
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/// Normalizes legacy file paths into file:// URIs.
|
|
27
|
+
private static func normalizedUri(from legacyUri: String?) -> String? {
|
|
28
|
+
guard let legacyUri = legacyUri, !legacyUri.isEmpty else {
|
|
29
|
+
return nil
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if legacyUri.hasPrefix("file://") {
|
|
33
|
+
return legacyUri
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return URL(fileURLWithPath: legacyUri).absoluteString
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// Recorder abstraction used by the service layer.
|
|
4
|
+
protocol RecorderAdapter: AnyObject {
|
|
5
|
+
/// Options supplied when recording starts.
|
|
6
|
+
var options: RecordOptions? { get }
|
|
7
|
+
/// Callback invoked when interruptions begin.
|
|
8
|
+
var onInterruptionBegan: (() -> Void)? { get set }
|
|
9
|
+
/// Callback invoked when interruptions end.
|
|
10
|
+
var onInterruptionEnded: (() -> Void)? { get set }
|
|
11
|
+
|
|
12
|
+
/// Starts recording audio.
|
|
13
|
+
func startRecording(recordOptions: RecordOptions?) -> Bool
|
|
14
|
+
/// Stops recording and returns success via callback.
|
|
15
|
+
func stopRecording(completion: @escaping (Bool) -> Void)
|
|
16
|
+
/// Pauses recording if supported.
|
|
17
|
+
func pauseRecording() -> Bool
|
|
18
|
+
/// Resumes recording if supported.
|
|
19
|
+
func resumeRecording() -> Bool
|
|
20
|
+
/// Returns the current recording status.
|
|
21
|
+
func getCurrentStatus() -> CurrentRecordingStatus
|
|
22
|
+
/// Returns the output file for the current session.
|
|
23
|
+
func getOutputFile() -> URL
|
|
24
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// Platform abstraction for device and file operations.
|
|
4
|
+
protocol RecorderPlatform {
|
|
5
|
+
/// Returns whether the device can record audio.
|
|
6
|
+
func canDeviceVoiceRecord() -> Bool
|
|
7
|
+
/// Reads the file as base64, returning nil on failure.
|
|
8
|
+
func readFileAsBase64(_ filePath: URL?) -> String?
|
|
9
|
+
/// Returns the file duration in milliseconds, or -1 on failure.
|
|
10
|
+
func getDurationMs(_ filePath: URL?) -> Int
|
|
11
|
+
}
|