@independo/capacitor-voice-recorder 8.1.0-dev.1 → 8.1.0-dev.3
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/LICENSE +21 -0
- package/README.md +40 -30
- 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/{com/tchvu3/capacitorvoicerecorder → app/independo/capacitorvoicerecorder/platform}/CustomMediaRecorder.java +33 -2
- 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/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/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} +1 -0
- package/ios/Sources/VoiceRecorder/Core/ErrorCodes.swift +16 -0
- package/ios/Sources/VoiceRecorder/{Messages.swift → Core/Messages.swift} +1 -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/{CustomMediaRecorder.swift → Platform/CustomMediaRecorder.swift} +67 -12
- 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 -8
- 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 -205
- 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/RecordOptions.swift +0 -8
- package/ios/Sources/VoiceRecorder/VoiceRecorder.swift +0 -170
- /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
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
package com.tchvu3.capacitorvoicerecorder;
|
|
2
|
-
|
|
3
|
-
import android.Manifest;
|
|
4
|
-
import android.content.Context;
|
|
5
|
-
import android.media.AudioManager;
|
|
6
|
-
import android.media.MediaPlayer;
|
|
7
|
-
import android.net.Uri;
|
|
8
|
-
import android.util.Base64;
|
|
9
|
-
import com.getcapacitor.PermissionState;
|
|
10
|
-
import com.getcapacitor.Plugin;
|
|
11
|
-
import com.getcapacitor.PluginCall;
|
|
12
|
-
import com.getcapacitor.PluginMethod;
|
|
13
|
-
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
14
|
-
import com.getcapacitor.annotation.Permission;
|
|
15
|
-
import com.getcapacitor.annotation.PermissionCallback;
|
|
16
|
-
import java.io.BufferedInputStream;
|
|
17
|
-
import java.io.File;
|
|
18
|
-
import java.io.FileInputStream;
|
|
19
|
-
import java.io.IOException;
|
|
20
|
-
|
|
21
|
-
@CapacitorPlugin(
|
|
22
|
-
name = "VoiceRecorder",
|
|
23
|
-
permissions = { @Permission(alias = VoiceRecorder.RECORD_AUDIO_ALIAS, strings = { Manifest.permission.RECORD_AUDIO }) }
|
|
24
|
-
)
|
|
25
|
-
public class VoiceRecorder extends Plugin {
|
|
26
|
-
|
|
27
|
-
static final String RECORD_AUDIO_ALIAS = "voice recording";
|
|
28
|
-
private CustomMediaRecorder mediaRecorder;
|
|
29
|
-
|
|
30
|
-
@PluginMethod
|
|
31
|
-
public void canDeviceVoiceRecord(PluginCall call) {
|
|
32
|
-
if (CustomMediaRecorder.canPhoneCreateMediaRecorder(getContext())) {
|
|
33
|
-
call.resolve(ResponseGenerator.successResponse());
|
|
34
|
-
} else {
|
|
35
|
-
call.resolve(ResponseGenerator.failResponse());
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
@PluginMethod
|
|
40
|
-
public void requestAudioRecordingPermission(PluginCall call) {
|
|
41
|
-
if (doesUserGaveAudioRecordingPermission()) {
|
|
42
|
-
call.resolve(ResponseGenerator.successResponse());
|
|
43
|
-
} else {
|
|
44
|
-
requestPermissionForAlias(RECORD_AUDIO_ALIAS, call, "recordAudioPermissionCallback");
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
@PermissionCallback
|
|
49
|
-
private void recordAudioPermissionCallback(PluginCall call) {
|
|
50
|
-
this.hasAudioRecordingPermission(call);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
@PluginMethod
|
|
54
|
-
public void hasAudioRecordingPermission(PluginCall call) {
|
|
55
|
-
call.resolve(ResponseGenerator.fromBoolean(doesUserGaveAudioRecordingPermission()));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
@PluginMethod
|
|
59
|
-
public void startRecording(PluginCall call) {
|
|
60
|
-
if (!CustomMediaRecorder.canPhoneCreateMediaRecorder(getContext())) {
|
|
61
|
-
call.reject(Messages.CANNOT_RECORD_ON_THIS_PHONE);
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (!doesUserGaveAudioRecordingPermission()) {
|
|
66
|
-
call.reject(Messages.MISSING_PERMISSION);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (this.isMicrophoneOccupied()) {
|
|
71
|
-
call.reject(Messages.MICROPHONE_BEING_USED);
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (mediaRecorder != null) {
|
|
76
|
-
call.reject(Messages.ALREADY_RECORDING);
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
String directory = call.getString("directory");
|
|
82
|
-
String subDirectory = call.getString("subDirectory");
|
|
83
|
-
RecordOptions options = new RecordOptions(directory, subDirectory);
|
|
84
|
-
mediaRecorder = new CustomMediaRecorder(getContext(), options);
|
|
85
|
-
mediaRecorder.setOnInterruptionBegan(() -> notifyListeners("voiceRecordingInterrupted", null));
|
|
86
|
-
mediaRecorder.setOnInterruptionEnded(() -> notifyListeners("voiceRecordingInterruptionEnded", null));
|
|
87
|
-
mediaRecorder.startRecording();
|
|
88
|
-
call.resolve(ResponseGenerator.successResponse());
|
|
89
|
-
} catch (Exception exp) {
|
|
90
|
-
call.reject(Messages.FAILED_TO_RECORD, exp);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
@PluginMethod
|
|
95
|
-
public void stopRecording(PluginCall call) {
|
|
96
|
-
if (mediaRecorder == null) {
|
|
97
|
-
call.reject(Messages.RECORDING_HAS_NOT_STARTED);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
try {
|
|
102
|
-
mediaRecorder.stopRecording();
|
|
103
|
-
File recordedFile = mediaRecorder.getOutputFile();
|
|
104
|
-
RecordOptions options = mediaRecorder.getRecordOptions();
|
|
105
|
-
|
|
106
|
-
String recordDataBase64 = null;
|
|
107
|
-
String uri = null;
|
|
108
|
-
if (options.directory() != null) {
|
|
109
|
-
uri = Uri.fromFile(recordedFile).toString();
|
|
110
|
-
} else {
|
|
111
|
-
recordDataBase64 = readRecordedFileAsBase64(recordedFile);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
RecordData recordData = new RecordData(
|
|
115
|
-
recordDataBase64,
|
|
116
|
-
getMsDurationOfAudioFile(recordedFile.getAbsolutePath()),
|
|
117
|
-
"audio/aac",
|
|
118
|
-
uri
|
|
119
|
-
);
|
|
120
|
-
if ((recordDataBase64 == null && uri == null) || recordData.getMsDuration() < 0) {
|
|
121
|
-
call.reject(Messages.EMPTY_RECORDING);
|
|
122
|
-
} else {
|
|
123
|
-
call.resolve(ResponseGenerator.dataResponse(recordData.toJSObject()));
|
|
124
|
-
}
|
|
125
|
-
} catch (Exception exp) {
|
|
126
|
-
call.reject(Messages.FAILED_TO_FETCH_RECORDING, exp);
|
|
127
|
-
} finally {
|
|
128
|
-
RecordOptions options = mediaRecorder.getRecordOptions();
|
|
129
|
-
if (options.directory() == null) {
|
|
130
|
-
mediaRecorder.deleteOutputFile();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
mediaRecorder = null;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
@PluginMethod
|
|
138
|
-
public void pauseRecording(PluginCall call) {
|
|
139
|
-
if (mediaRecorder == null) {
|
|
140
|
-
call.reject(Messages.RECORDING_HAS_NOT_STARTED);
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
try {
|
|
144
|
-
call.resolve(ResponseGenerator.fromBoolean(mediaRecorder.pauseRecording()));
|
|
145
|
-
} catch (NotSupportedOsVersion exception) {
|
|
146
|
-
call.reject(Messages.NOT_SUPPORTED_OS_VERSION);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
@PluginMethod
|
|
151
|
-
public void resumeRecording(PluginCall call) {
|
|
152
|
-
if (mediaRecorder == null) {
|
|
153
|
-
call.reject(Messages.RECORDING_HAS_NOT_STARTED);
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
try {
|
|
157
|
-
call.resolve(ResponseGenerator.fromBoolean(mediaRecorder.resumeRecording()));
|
|
158
|
-
} catch (NotSupportedOsVersion exception) {
|
|
159
|
-
call.reject(Messages.NOT_SUPPORTED_OS_VERSION);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
@PluginMethod
|
|
164
|
-
public void getCurrentStatus(PluginCall call) {
|
|
165
|
-
if (mediaRecorder == null) {
|
|
166
|
-
call.resolve(ResponseGenerator.statusResponse(CurrentRecordingStatus.NONE));
|
|
167
|
-
} else {
|
|
168
|
-
call.resolve(ResponseGenerator.statusResponse(mediaRecorder.getCurrentStatus()));
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
private boolean doesUserGaveAudioRecordingPermission() {
|
|
173
|
-
return getPermissionState(VoiceRecorder.RECORD_AUDIO_ALIAS).equals(PermissionState.GRANTED);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
private String readRecordedFileAsBase64(File recordedFile) {
|
|
177
|
-
BufferedInputStream bufferedInputStream;
|
|
178
|
-
byte[] bArray = new byte[(int) recordedFile.length()];
|
|
179
|
-
try {
|
|
180
|
-
bufferedInputStream = new BufferedInputStream(new FileInputStream(recordedFile));
|
|
181
|
-
bufferedInputStream.read(bArray);
|
|
182
|
-
bufferedInputStream.close();
|
|
183
|
-
} catch (IOException exp) {
|
|
184
|
-
return null;
|
|
185
|
-
}
|
|
186
|
-
return Base64.encodeToString(bArray, Base64.DEFAULT);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
private int getMsDurationOfAudioFile(String recordedFilePath) {
|
|
190
|
-
try {
|
|
191
|
-
MediaPlayer mediaPlayer = new MediaPlayer();
|
|
192
|
-
mediaPlayer.setDataSource(recordedFilePath);
|
|
193
|
-
mediaPlayer.prepare();
|
|
194
|
-
return mediaPlayer.getDuration();
|
|
195
|
-
} catch (Exception ignore) {
|
|
196
|
-
return -1;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
private boolean isMicrophoneOccupied() {
|
|
201
|
-
AudioManager audioManager = (AudioManager) this.getContext().getSystemService(Context.AUDIO_SERVICE);
|
|
202
|
-
if (audioManager == null) return true;
|
|
203
|
-
return audioManager.getMode() != AudioManager.MODE_NORMAL;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { CurrentRecordingStatus, GenericResponse, RecordingData, RecordingOptions } from './definitions';
|
|
2
|
-
declare const POSSIBLE_MIME_TYPES: {
|
|
3
|
-
'audio/aac': string;
|
|
4
|
-
'audio/webm;codecs=opus': string;
|
|
5
|
-
'audio/mp4': string;
|
|
6
|
-
'audio/webm': string;
|
|
7
|
-
'audio/ogg;codecs=opus': string;
|
|
8
|
-
};
|
|
9
|
-
export declare class VoiceRecorderImpl {
|
|
10
|
-
private mediaRecorder;
|
|
11
|
-
private chunks;
|
|
12
|
-
private pendingResult;
|
|
13
|
-
static canDeviceVoiceRecord(): Promise<GenericResponse>;
|
|
14
|
-
startRecording(options?: RecordingOptions): Promise<GenericResponse>;
|
|
15
|
-
stopRecording(): Promise<RecordingData>;
|
|
16
|
-
static hasAudioRecordingPermission(): Promise<GenericResponse>;
|
|
17
|
-
static requestAudioRecordingPermission(): Promise<GenericResponse>;
|
|
18
|
-
pauseRecording(): Promise<GenericResponse>;
|
|
19
|
-
resumeRecording(): Promise<GenericResponse>;
|
|
20
|
-
getCurrentStatus(): Promise<CurrentRecordingStatus>;
|
|
21
|
-
static getSupportedMimeType<T extends keyof typeof POSSIBLE_MIME_TYPES>(): T | null;
|
|
22
|
-
private onSuccessfullyStartedRecording;
|
|
23
|
-
private onFailedToStartRecording;
|
|
24
|
-
private static blobToBase64;
|
|
25
|
-
private prepareInstanceForNextOperation;
|
|
26
|
-
}
|
|
27
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"VoiceRecorderImpl.js","sourceRoot":"","sources":["../../src/VoiceRecorderImpl.ts"],"names":[],"mappings":"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","sourcesContent":["import {Filesystem} from '@capacitor/filesystem';\nimport write_blob from 'capacitor-blob-writer';\n\nimport type {\n Base64String,\n CurrentRecordingStatus,\n GenericResponse,\n RecordingData,\n RecordingOptions,\n} from './definitions';\nimport getBlobDuration from \"./helper/get-blob-duration\";\nimport {\n alreadyRecordingError,\n couldNotQueryPermissionStatusError,\n deviceCannotVoiceRecordError,\n emptyRecordingError,\n failedToFetchRecordingError,\n failedToRecordError,\n failureResponse,\n missingPermissionError,\n recordingHasNotStartedError,\n successResponse,\n} from './predefined-web-responses';\n\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};\n\nconst neverResolvingPromise = (): Promise<any> => new Promise(() => undefined);\n\nexport class VoiceRecorderImpl {\n private mediaRecorder: MediaRecorder | null = null;\n private chunks: any[] = [];\n private pendingResult: Promise<RecordingData> = neverResolvingPromise();\n\n public static async canDeviceVoiceRecord(): Promise<GenericResponse> {\n if (navigator?.mediaDevices?.getUserMedia == null || VoiceRecorderImpl.getSupportedMimeType() == null) {\n return failureResponse();\n } else {\n return successResponse();\n }\n }\n\n public async startRecording(options?: RecordingOptions): Promise<GenericResponse> {\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\n return navigator.mediaDevices\n .getUserMedia({audio: true})\n .then((stream) => this.onSuccessfullyStartedRecording(stream, options))\n .catch(this.onFailedToStartRecording.bind(this));\n }\n\n public async stopRecording(): Promise<RecordingData> {\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 } catch (ignore) {\n throw failedToFetchRecordingError();\n } finally {\n this.prepareInstanceForNextOperation();\n }\n }\n\n public static async hasAudioRecordingPermission(): Promise<GenericResponse> {\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' as any})\n .then((result) => ({value: result.state === 'granted'}))\n .catch(() => {\n throw couldNotQueryPermissionStatusError();\n });\n }\n\n public static async requestAudioRecordingPermission(): Promise<GenericResponse> {\n const havingPermission = await VoiceRecorderImpl.hasAudioRecordingPermission().catch(() => failureResponse());\n if (havingPermission.value) {\n return successResponse();\n }\n\n return navigator.mediaDevices\n .getUserMedia({audio: true})\n .then(() => successResponse())\n .catch(() => failureResponse());\n }\n\n public pauseRecording(): Promise<GenericResponse> {\n if (this.mediaRecorder == null) {\n throw recordingHasNotStartedError();\n } else if (this.mediaRecorder.state === 'recording') {\n this.mediaRecorder.pause();\n return Promise.resolve(successResponse());\n } else {\n return Promise.resolve(failureResponse());\n }\n }\n\n public resumeRecording(): Promise<GenericResponse> {\n if (this.mediaRecorder == null) {\n throw recordingHasNotStartedError();\n } else if (this.mediaRecorder.state === 'paused') {\n this.mediaRecorder.resume();\n return Promise.resolve(successResponse());\n } else {\n return Promise.resolve(failureResponse());\n }\n }\n\n public getCurrentStatus(): Promise<CurrentRecordingStatus> {\n if (this.mediaRecorder == null) {\n return Promise.resolve({status: 'NONE'});\n } else if (this.mediaRecorder.state === 'recording') {\n return Promise.resolve({status: 'RECORDING'});\n } else if (this.mediaRecorder.state === 'paused') {\n return Promise.resolve({status: 'PAUSED'});\n } else {\n return Promise.resolve({status: 'NONE'});\n }\n }\n\n public static getSupportedMimeType<T extends keyof typeof POSSIBLE_MIME_TYPES>(): T | null {\n if (MediaRecorder?.isTypeSupported == null) return null;\n\n const foundSupportedType = Object.keys(POSSIBLE_MIME_TYPES).find((type) => MediaRecorder.isTypeSupported(type)) as\n | T\n | undefined;\n\n return foundSupportedType ?? null;\n }\n\n private onSuccessfullyStartedRecording(stream: MediaStream, options?: RecordingOptions): GenericResponse {\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 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\n let uri: string | undefined = undefined;\n let recordDataBase64 = '';\n if (options?.directory) {\n const subDirectory = options.subDirectory?.match(/^\\/?(.+[^/])\\/?$/)?.[1] ?? '';\n const path = `${subDirectory}/recording-${new Date().getTime()}${POSSIBLE_MIME_TYPES[mimeType]}`;\n\n await write_blob({\n blob: blobVoiceRecording,\n directory: options.directory,\n fast_mode: true,\n path,\n recursive: true,\n });\n\n ({uri} = await Filesystem.getUri({directory: options.directory, path}));\n } else {\n recordDataBase64 = await VoiceRecorderImpl.blobToBase64(blobVoiceRecording);\n }\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: any) => this.chunks.push(event.data);\n this.mediaRecorder.start();\n });\n return successResponse();\n }\n\n private onFailedToStartRecording(): GenericResponse {\n this.prepareInstanceForNextOperation();\n throw failedToRecordError();\n }\n\n private static blobToBase64(blob: Blob): Promise<Base64String> {\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\n private prepareInstanceForNextOperation(): void {\n if (this.mediaRecorder != null && this.mediaRecorder.state === 'recording') {\n try {\n this.mediaRecorder.stop();\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"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"get-blob-duration.js","sourceRoot":"","sources":["../../../src/helper/get-blob-duration.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,IAAmB;IACvD,MAAM,WAAW,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACpD,IAAI,CAAC,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpE,MAAM,SAAS,GAAG,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtD,WAAW,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,GAAG,EAAE;YAChD,2EAA2E;YAC3E,IAAI,WAAW,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBACpC,WAAW,CAAC,WAAW,GAAG,MAAM,CAAC,gBAAgB,CAAC;gBAClD,WAAW,CAAC,YAAY,GAAG,GAAG,EAAE;oBAC5B,WAAW,CAAC,YAAY,GAAG,IAAI,CAAC;oBAChC,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;oBAC9B,WAAW,CAAC,WAAW,GAAG,CAAC,CAAC;gBAChC,CAAC,CAAC;YACN,CAAC;iBAAM,CAAC;gBACJ,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;YAClC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,WAAW,CAAC,OAAO,GAAG,CAAC,KAAK,EAAE,EAAE;YAC5B,MAAM,KAAK,GAAI,KAAoB,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;YACjF,MAAM,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC,CAAC;IACN,CAAC,CAAC,CAAC;IAEH,WAAW,CAAC,GAAG,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IAE9E,OAAO,SAAS,CAAC;AACrB,CAAC","sourcesContent":["/**\n * @param {Blob | string} blob\n * @returns {Promise<number>} Blob duration in seconds.\n */\nexport default function getBlobDuration(blob: Blob | string): Promise<number> {\n const tempVideoEl = document.createElement('video');\n if (!tempVideoEl) throw new Error('Failed to create video element');\n const durationP = new Promise<number>((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 } else {\n resolve(tempVideoEl.duration);\n }\n });\n\n tempVideoEl.onerror = (event) => {\n const error = (event as ErrorEvent).error || new Error('Unknown error occurred');\n reject(error);\n };\n });\n\n tempVideoEl.src = typeof blob === 'string' ? blob : URL.createObjectURL(blob);\n\n return durationP;\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"predefined-web-responses.js","sourceRoot":"","sources":["../../src/predefined-web-responses.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,eAAe,GAAG,GAAoB,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACxE,MAAM,CAAC,MAAM,eAAe,GAAG,GAAoB,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;AAEzE,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAU,EAAE,CAAC,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;AACnF,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAU,EAAE,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;AACjF,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAU,EAAE,CAAC,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;AACxF,MAAM,CAAC,MAAM,4BAA4B,GAAG,GAAU,EAAE,CAAC,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;AACjG,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAU,EAAE,CAAC,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;AAC9E,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAU,EAAE,CAAC,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;AAE7E,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAU,EAAE,CAAC,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;AAC/F,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAU,EAAE,CAAC,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;AAE/F,MAAM,CAAC,MAAM,kCAAkC,GAAG,GAAU,EAAE,CAAC,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC","sourcesContent":["import type { GenericResponse } from './definitions';\n\nexport const successResponse = (): GenericResponse => ({ value: true });\nexport const failureResponse = (): GenericResponse => ({ value: false });\n\nexport const missingPermissionError = (): Error => new Error('MISSING_PERMISSION');\nexport const alreadyRecordingError = (): Error => new Error('ALREADY_RECORDING');\nexport const microphoneBeingUsedError = (): Error => new Error('MICROPHONE_BEING_USED');\nexport const deviceCannotVoiceRecordError = (): Error => new Error('DEVICE_CANNOT_VOICE_RECORD');\nexport const failedToRecordError = (): Error => new Error('FAILED_TO_RECORD');\nexport const emptyRecordingError = (): Error => new Error('EMPTY_RECORDING');\n\nexport const recordingHasNotStartedError = (): Error => new Error('RECORDING_HAS_NOT_STARTED');\nexport const failedToFetchRecordingError = (): Error => new Error('FAILED_TO_FETCH_RECORDING');\n\nexport const couldNotQueryPermissionStatusError = (): Error => new Error('COULD_NOT_QUERY_PERMISSION_STATUS');\n"]}
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
import AVFoundation
|
|
3
|
-
import Capacitor
|
|
4
|
-
|
|
5
|
-
@objc(VoiceRecorder)
|
|
6
|
-
public class VoiceRecorder: CAPPlugin, CAPBridgedPlugin {
|
|
7
|
-
public let identifier = "VoiceRecorder"
|
|
8
|
-
public let jsName = "VoiceRecorder"
|
|
9
|
-
public let pluginMethods: [CAPPluginMethod] = [
|
|
10
|
-
CAPPluginMethod(name: "canDeviceVoiceRecord", returnType: CAPPluginReturnPromise),
|
|
11
|
-
CAPPluginMethod(name: "requestAudioRecordingPermission", returnType: CAPPluginReturnPromise),
|
|
12
|
-
CAPPluginMethod(name: "hasAudioRecordingPermission", returnType: CAPPluginReturnPromise),
|
|
13
|
-
CAPPluginMethod(name: "startRecording", returnType: CAPPluginReturnPromise),
|
|
14
|
-
CAPPluginMethod(name: "stopRecording", returnType: CAPPluginReturnPromise),
|
|
15
|
-
CAPPluginMethod(name: "pauseRecording", returnType: CAPPluginReturnPromise),
|
|
16
|
-
CAPPluginMethod(name: "resumeRecording", returnType: CAPPluginReturnPromise),
|
|
17
|
-
CAPPluginMethod(name: "getCurrentStatus", returnType: CAPPluginReturnPromise),
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
private var customMediaRecorder: CustomMediaRecorder? = nil
|
|
21
|
-
|
|
22
|
-
@objc func canDeviceVoiceRecord(_ call: CAPPluginCall) {
|
|
23
|
-
call.resolve(ResponseGenerator.successResponse())
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
@objc func requestAudioRecordingPermission(_ call: CAPPluginCall) {
|
|
27
|
-
AVAudioSession.sharedInstance().requestRecordPermission { granted in
|
|
28
|
-
if granted {
|
|
29
|
-
call.resolve(ResponseGenerator.successResponse())
|
|
30
|
-
} else {
|
|
31
|
-
call.resolve(ResponseGenerator.failResponse())
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
@objc func hasAudioRecordingPermission(_ call: CAPPluginCall) {
|
|
37
|
-
call.resolve(ResponseGenerator.fromBoolean(doesUserGaveAudioRecordingPermission()))
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
@objc func startRecording(_ call: CAPPluginCall) {
|
|
41
|
-
if(!doesUserGaveAudioRecordingPermission()) {
|
|
42
|
-
call.reject(Messages.MISSING_PERMISSION)
|
|
43
|
-
return
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if(customMediaRecorder != nil) {
|
|
47
|
-
call.reject(Messages.ALREADY_RECORDING)
|
|
48
|
-
return
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
customMediaRecorder = CustomMediaRecorder()
|
|
52
|
-
if(customMediaRecorder == nil) {
|
|
53
|
-
call.reject(Messages.CANNOT_RECORD_ON_THIS_PHONE)
|
|
54
|
-
return
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
customMediaRecorder?.onInterruptionBegan = { [weak self] in
|
|
58
|
-
self?.notifyListeners("voiceRecordingInterrupted", data: [:])
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
customMediaRecorder?.onInterruptionEnded = { [weak self] in
|
|
62
|
-
self?.notifyListeners("voiceRecordingInterruptionEnded", data: [:])
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
let directory: String? = call.getString("directory")
|
|
66
|
-
let subDirectory: String? = call.getString("subDirectory")
|
|
67
|
-
let recordOptions = RecordOptions(directory: directory, subDirectory: subDirectory)
|
|
68
|
-
let successfullyStartedRecording = customMediaRecorder!.startRecording(recordOptions: recordOptions)
|
|
69
|
-
if successfullyStartedRecording == false {
|
|
70
|
-
call.reject(Messages.CANNOT_RECORD_ON_THIS_PHONE)
|
|
71
|
-
} else {
|
|
72
|
-
call.resolve(ResponseGenerator.successResponse())
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
@objc func stopRecording(_ call: CAPPluginCall) {
|
|
77
|
-
if(customMediaRecorder == nil) {
|
|
78
|
-
call.reject(Messages.RECORDING_HAS_NOT_STARTED)
|
|
79
|
-
return
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
customMediaRecorder?.stopRecording { [weak self] stopSuccess in
|
|
83
|
-
DispatchQueue.main.async {
|
|
84
|
-
guard let self = self else {
|
|
85
|
-
call.reject(Messages.FAILED_TO_FETCH_RECORDING)
|
|
86
|
-
return
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if !stopSuccess {
|
|
90
|
-
self.customMediaRecorder = nil
|
|
91
|
-
call.reject(Messages.FAILED_TO_MERGE_RECORDING)
|
|
92
|
-
return
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
let audioFileUrl = self.customMediaRecorder?.getOutputFile()
|
|
96
|
-
if(audioFileUrl == nil) {
|
|
97
|
-
self.customMediaRecorder = nil
|
|
98
|
-
call.reject(Messages.FAILED_TO_FETCH_RECORDING)
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
let fileExtension = audioFileUrl!.pathExtension.lowercased()
|
|
103
|
-
let mimeType = fileExtension == "m4a" ? "audio/mp4" : "audio/aac"
|
|
104
|
-
let sendDataAsBase64 = self.customMediaRecorder?.options?.directory == nil
|
|
105
|
-
let recordData = RecordData(
|
|
106
|
-
recordDataBase64: sendDataAsBase64 ? self.readFileAsBase64(audioFileUrl) : nil,
|
|
107
|
-
mimeType: mimeType,
|
|
108
|
-
msDuration: self.getMsDurationOfAudioFile(audioFileUrl),
|
|
109
|
-
uri: sendDataAsBase64 ? nil : audioFileUrl!.path
|
|
110
|
-
)
|
|
111
|
-
self.customMediaRecorder = nil
|
|
112
|
-
if (sendDataAsBase64 && recordData.recordDataBase64 == nil) || recordData.msDuration < 0 {
|
|
113
|
-
call.reject(Messages.EMPTY_RECORDING)
|
|
114
|
-
} else {
|
|
115
|
-
call.resolve(ResponseGenerator.dataResponse(recordData.toDictionary()))
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
@objc func pauseRecording(_ call: CAPPluginCall) {
|
|
122
|
-
if(customMediaRecorder == nil) {
|
|
123
|
-
call.reject(Messages.RECORDING_HAS_NOT_STARTED)
|
|
124
|
-
} else {
|
|
125
|
-
call.resolve(ResponseGenerator.fromBoolean(customMediaRecorder?.pauseRecording() ?? false))
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
@objc func resumeRecording(_ call: CAPPluginCall) {
|
|
130
|
-
if(customMediaRecorder == nil) {
|
|
131
|
-
call.reject(Messages.RECORDING_HAS_NOT_STARTED)
|
|
132
|
-
} else {
|
|
133
|
-
call.resolve(ResponseGenerator.fromBoolean(customMediaRecorder?.resumeRecording() ?? false))
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
@objc func getCurrentStatus(_ call: CAPPluginCall) {
|
|
138
|
-
if(customMediaRecorder == nil) {
|
|
139
|
-
call.resolve(ResponseGenerator.statusResponse(CurrentRecordingStatus.NONE))
|
|
140
|
-
} else {
|
|
141
|
-
call.resolve(ResponseGenerator.statusResponse(customMediaRecorder?.getCurrentStatus() ?? CurrentRecordingStatus.NONE))
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
func doesUserGaveAudioRecordingPermission() -> Bool {
|
|
146
|
-
return AVAudioSession.sharedInstance().recordPermission == AVAudioSession.RecordPermission.granted
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
func readFileAsBase64(_ filePath: URL?) -> String? {
|
|
150
|
-
if(filePath == nil) {
|
|
151
|
-
return nil
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
do {
|
|
155
|
-
let fileData = try Data.init(contentsOf: filePath!)
|
|
156
|
-
let fileStream = fileData.base64EncodedString(options: NSData.Base64EncodingOptions.init(rawValue: 0))
|
|
157
|
-
return fileStream
|
|
158
|
-
} catch {}
|
|
159
|
-
|
|
160
|
-
return nil
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
func getMsDurationOfAudioFile(_ filePath: URL?) -> Int {
|
|
164
|
-
if filePath == nil {
|
|
165
|
-
return -1
|
|
166
|
-
}
|
|
167
|
-
return Int(CMTimeGetSeconds(AVURLAsset(url: filePath!).duration) * 1000)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
}
|
|
File without changes
|
|
File without changes
|