capacitor-voice-recorder
Advanced tools
Comparing version 1.1.1 to 1.2.0
@@ -67,2 +67,38 @@ { | ||
"slug": "stoprecording" | ||
}, | ||
{ | ||
"name": "pauseRecording", | ||
"signature": "() => any", | ||
"parameters": [], | ||
"returns": "any", | ||
"tags": [], | ||
"docs": "", | ||
"complexTypes": [ | ||
"GenericResponse" | ||
], | ||
"slug": "pauserecording" | ||
}, | ||
{ | ||
"name": "resumeRecording", | ||
"signature": "() => any", | ||
"parameters": [], | ||
"returns": "any", | ||
"tags": [], | ||
"docs": "", | ||
"complexTypes": [ | ||
"GenericResponse" | ||
], | ||
"slug": "resumerecording" | ||
}, | ||
{ | ||
"name": "getCurrentStatus", | ||
"signature": "() => any", | ||
"parameters": [], | ||
"returns": "any", | ||
"tags": [], | ||
"docs": "", | ||
"complexTypes": [ | ||
"CurrentRecordingStatus" | ||
], | ||
"slug": "getcurrentstatus" | ||
} | ||
@@ -106,2 +142,18 @@ ], | ||
] | ||
}, | ||
{ | ||
"name": "CurrentRecordingStatus", | ||
"slug": "currentrecordingstatus", | ||
"docs": "", | ||
"tags": [], | ||
"methods": [], | ||
"properties": [ | ||
{ | ||
"name": "status", | ||
"tags": [], | ||
"docs": "", | ||
"complexTypes": [], | ||
"type": "\"RECORDING\" | \"PAUSED\" | \"NONE\"" | ||
} | ||
] | ||
} | ||
@@ -108,0 +160,0 @@ ], |
@@ -12,2 +12,5 @@ export declare type Base64String = string; | ||
} | ||
export interface CurrentRecordingStatus { | ||
status: 'RECORDING' | 'PAUSED' | 'NONE'; | ||
} | ||
export interface VoiceRecorderPlugin { | ||
@@ -19,2 +22,5 @@ canDeviceVoiceRecord(): Promise<GenericResponse>; | ||
stopRecording(): Promise<RecordingData>; | ||
pauseRecording(): Promise<GenericResponse>; | ||
resumeRecording(): Promise<GenericResponse>; | ||
getCurrentStatus(): Promise<CurrentRecordingStatus>; | ||
} |
import { WebPlugin } from '@capacitor/core'; | ||
import type { GenericResponse, RecordingData, VoiceRecorderPlugin } from './definitions'; | ||
import type { CurrentRecordingStatus, GenericResponse, RecordingData, VoiceRecorderPlugin } from './definitions'; | ||
export declare class VoiceRecorderWeb extends WebPlugin implements VoiceRecorderPlugin { | ||
private voiceRecorderInstance; | ||
canDeviceVoiceRecord(): Promise<GenericResponse>; | ||
@@ -9,2 +10,5 @@ hasAudioRecordingPermission(): Promise<GenericResponse>; | ||
stopRecording(): Promise<RecordingData>; | ||
pauseRecording(): Promise<GenericResponse>; | ||
resumeRecording(): Promise<GenericResponse>; | ||
getCurrentStatus(): Promise<CurrentRecordingStatus>; | ||
} |
import { WebPlugin } from '@capacitor/core'; | ||
import { VoiceRecorderImpl } from './VoiceRecorderImpl'; | ||
export class VoiceRecorderWeb extends WebPlugin { | ||
constructor() { | ||
super(...arguments); | ||
this.voiceRecorderInstance = new VoiceRecorderImpl(); | ||
} | ||
canDeviceVoiceRecord() { | ||
return Promise.reject(new Error('VoiceRecorder does not have web implementation')); | ||
return VoiceRecorderImpl.canDeviceVoiceRecord(); | ||
} | ||
hasAudioRecordingPermission() { | ||
return Promise.reject(new Error('VoiceRecorder does not have web implementation')); | ||
return VoiceRecorderImpl.hasAudioRecordingPermission(); | ||
} | ||
requestAudioRecordingPermission() { | ||
return Promise.reject(new Error('VoiceRecorder does not have web implementation')); | ||
return VoiceRecorderImpl.requestAudioRecordingPermission(); | ||
} | ||
startRecording() { | ||
return Promise.reject(new Error('VoiceRecorder does not have web implementation')); | ||
return this.voiceRecorderInstance.startRecording(); | ||
} | ||
stopRecording() { | ||
return Promise.reject(new Error('VoiceRecorder does not have web implementation')); | ||
return this.voiceRecorderInstance.stopRecording(); | ||
} | ||
pauseRecording() { | ||
return this.voiceRecorderInstance.pauseRecording(); | ||
} | ||
resumeRecording() { | ||
return this.voiceRecorderInstance.resumeRecording(); | ||
} | ||
getCurrentStatus() { | ||
return this.voiceRecorderInstance.getCurrentStatus(); | ||
} | ||
} | ||
//# sourceMappingURL=web.js.map |
@@ -6,3 +6,8 @@ 'use strict'; | ||
var core = require('@capacitor/core'); | ||
var getBlobDuration = require('get-blob-duration'); | ||
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } | ||
var getBlobDuration__default = /*#__PURE__*/_interopDefaultLegacy(getBlobDuration); | ||
const VoiceRecorder = core.registerPlugin('VoiceRecorder', { | ||
@@ -12,18 +17,197 @@ web: () => Promise.resolve().then(function () { return web; }).then(m => new m.VoiceRecorderWeb()), | ||
const successResponse = () => ({ value: true }); | ||
const failureResponse = () => ({ value: false }); | ||
const missingPermissionError = () => new Error('MISSING_PERMISSION'); | ||
const alreadyRecordingError = () => new Error('ALREADY_RECORDING'); | ||
const deviceCannotVoiceRecordError = () => new Error('DEVICE_CANNOT_VOICE_RECORD'); | ||
const failedToRecordError = () => new Error('FAILED_TO_RECORD'); | ||
const recordingHasNotStartedError = () => new Error('RECORDING_HAS_NOT_STARTED'); | ||
const failedToFetchRecordingError = () => new Error('FAILED_TO_FETCH_RECORDING'); | ||
const couldNotQueryPermissionStatusError = () => new Error('COULD_NOT_QUERY_PERMISSION_STATUS'); | ||
// these mime types will be checked one by one in order until one of them is found to be supported by the current browser | ||
const possibleMimeTypes = ['audio/aac', 'audio/webm;codecs=opus', 'audio/mp4', 'audio/webm', 'audio/ogg;codecs=opus']; | ||
const neverResolvingPromise = () => new Promise(() => undefined); | ||
class VoiceRecorderImpl { | ||
constructor() { | ||
this.mediaRecorder = null; | ||
this.chunks = []; | ||
this.pendingResult = neverResolvingPromise(); | ||
} | ||
static async canDeviceVoiceRecord() { | ||
var _a; | ||
if (((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) == null || VoiceRecorderImpl.getSupportedMimeType() == null) { | ||
return failureResponse(); | ||
} | ||
else { | ||
return successResponse(); | ||
} | ||
} | ||
async startRecording() { | ||
if (this.mediaRecorder != null) { | ||
throw alreadyRecordingError(); | ||
} | ||
const deviceCanRecord = await VoiceRecorderImpl.canDeviceVoiceRecord(); | ||
if (!deviceCanRecord.value) { | ||
throw deviceCannotVoiceRecordError(); | ||
} | ||
const havingPermission = await VoiceRecorderImpl.hasAudioRecordingPermission().catch(() => successResponse()); | ||
if (!havingPermission.value) { | ||
throw missingPermissionError(); | ||
} | ||
navigator.mediaDevices.getUserMedia({ audio: true }) | ||
.then(this.onSuccessfullyStartedRecording.bind(this)) | ||
.catch(this.onFailedToStartRecording.bind(this)); | ||
return successResponse(); | ||
} | ||
async stopRecording() { | ||
if (this.mediaRecorder == null) { | ||
throw recordingHasNotStartedError(); | ||
} | ||
try { | ||
this.mediaRecorder.stop(); | ||
return this.pendingResult; | ||
} | ||
catch (ignore) { | ||
throw failedToFetchRecordingError(); | ||
} | ||
finally { | ||
this.prepareInstanceForNextOperation(); | ||
} | ||
} | ||
static async hasAudioRecordingPermission() { | ||
return navigator.permissions.query({ name: 'microphone' }) | ||
.then(result => ({ value: result.state === 'granted' })) | ||
.catch(() => { throw couldNotQueryPermissionStatusError(); }); | ||
} | ||
static async requestAudioRecordingPermission() { | ||
const havingPermission = await VoiceRecorderImpl.hasAudioRecordingPermission().catch(() => failureResponse()); | ||
if (havingPermission.value) { | ||
return successResponse(); | ||
} | ||
return navigator.mediaDevices.getUserMedia({ audio: true }) | ||
.then(() => successResponse()) | ||
.catch(() => failureResponse()); | ||
} | ||
pauseRecording() { | ||
if (this.mediaRecorder == null) { | ||
throw recordingHasNotStartedError(); | ||
} | ||
else if (this.mediaRecorder.state === 'recording') { | ||
this.mediaRecorder.pause(); | ||
return Promise.resolve(successResponse()); | ||
} | ||
else { | ||
return Promise.resolve(failureResponse()); | ||
} | ||
} | ||
resumeRecording() { | ||
if (this.mediaRecorder == null) { | ||
throw recordingHasNotStartedError(); | ||
} | ||
else if (this.mediaRecorder.state === 'paused') { | ||
this.mediaRecorder.resume(); | ||
return Promise.resolve(successResponse()); | ||
} | ||
else { | ||
return Promise.resolve(failureResponse()); | ||
} | ||
} | ||
getCurrentStatus() { | ||
if (this.mediaRecorder == null) { | ||
return Promise.resolve({ status: 'NONE' }); | ||
} | ||
else if (this.mediaRecorder.state === 'recording') { | ||
return Promise.resolve({ status: 'RECORDING' }); | ||
} | ||
else if (this.mediaRecorder.state === 'paused') { | ||
return Promise.resolve({ status: 'PAUSED' }); | ||
} | ||
else { | ||
return Promise.resolve({ status: 'NONE' }); | ||
} | ||
} | ||
static getSupportedMimeType() { | ||
if ((MediaRecorder === null || MediaRecorder === void 0 ? void 0 : MediaRecorder.isTypeSupported) == null) | ||
return null; | ||
const foundSupportedType = possibleMimeTypes.find(type => MediaRecorder.isTypeSupported(type)); | ||
return foundSupportedType !== null && foundSupportedType !== void 0 ? foundSupportedType : null; | ||
} | ||
onSuccessfullyStartedRecording(stream) { | ||
this.pendingResult = new Promise((resolve, reject) => { | ||
this.mediaRecorder = new MediaRecorder(stream); | ||
this.mediaRecorder.onerror = () => { | ||
reject(failedToRecordError()); | ||
this.prepareInstanceForNextOperation(); | ||
}; | ||
this.mediaRecorder.onstop = async () => { | ||
const mimeType = VoiceRecorderImpl.getSupportedMimeType(); | ||
if (mimeType == null) { | ||
reject(failedToFetchRecordingError()); | ||
} | ||
else { | ||
const blobVoiceRecording = new Blob(this.chunks, { 'type': mimeType }); | ||
const recordDataBase64 = await VoiceRecorderImpl.blobToBase64(blobVoiceRecording); | ||
const recordingDuration = await getBlobDuration__default['default'](blobVoiceRecording); | ||
this.prepareInstanceForNextOperation(); | ||
resolve({ value: { recordDataBase64, mimeType, msDuration: recordingDuration * 1000 } }); | ||
} | ||
}; | ||
this.mediaRecorder.ondataavailable = (event) => this.chunks.push(event.data); | ||
this.mediaRecorder.start(); | ||
}); | ||
} | ||
onFailedToStartRecording() { | ||
this.prepareInstanceForNextOperation(); | ||
throw failedToRecordError(); | ||
} | ||
static blobToBase64(blob) { | ||
return new Promise(resolve => { | ||
const reader = new FileReader(); | ||
reader.onloadend = () => resolve(String(reader.result)); | ||
reader.readAsDataURL(blob); | ||
}); | ||
} | ||
prepareInstanceForNextOperation() { | ||
if (this.mediaRecorder != null && this.mediaRecorder.state === 'recording') { | ||
try { | ||
this.mediaRecorder.stop(); | ||
} | ||
catch (ignore) { } | ||
} | ||
this.pendingResult = neverResolvingPromise(); | ||
this.mediaRecorder = null; | ||
this.chunks = []; | ||
} | ||
} | ||
class VoiceRecorderWeb extends core.WebPlugin { | ||
constructor() { | ||
super(...arguments); | ||
this.voiceRecorderInstance = new VoiceRecorderImpl(); | ||
} | ||
canDeviceVoiceRecord() { | ||
return Promise.reject(new Error('VoiceRecorder does not have web implementation')); | ||
return VoiceRecorderImpl.canDeviceVoiceRecord(); | ||
} | ||
hasAudioRecordingPermission() { | ||
return Promise.reject(new Error('VoiceRecorder does not have web implementation')); | ||
return VoiceRecorderImpl.hasAudioRecordingPermission(); | ||
} | ||
requestAudioRecordingPermission() { | ||
return Promise.reject(new Error('VoiceRecorder does not have web implementation')); | ||
return VoiceRecorderImpl.requestAudioRecordingPermission(); | ||
} | ||
startRecording() { | ||
return Promise.reject(new Error('VoiceRecorder does not have web implementation')); | ||
return this.voiceRecorderInstance.startRecording(); | ||
} | ||
stopRecording() { | ||
return Promise.reject(new Error('VoiceRecorder does not have web implementation')); | ||
return this.voiceRecorderInstance.stopRecording(); | ||
} | ||
pauseRecording() { | ||
return this.voiceRecorderInstance.pauseRecording(); | ||
} | ||
resumeRecording() { | ||
return this.voiceRecorderInstance.resumeRecording(); | ||
} | ||
getCurrentStatus() { | ||
return this.voiceRecorderInstance.getCurrentStatus(); | ||
} | ||
} | ||
@@ -30,0 +214,0 @@ |
@@ -1,4 +0,8 @@ | ||
var capacitorVoiceRecorder = (function (exports, core) { | ||
var capacitorVoiceRecorder = (function (exports, core, getBlobDuration) { | ||
'use strict'; | ||
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } | ||
var getBlobDuration__default = /*#__PURE__*/_interopDefaultLegacy(getBlobDuration); | ||
const VoiceRecorder = core.registerPlugin('VoiceRecorder', { | ||
@@ -8,18 +12,197 @@ web: () => Promise.resolve().then(function () { return web; }).then(m => new m.VoiceRecorderWeb()), | ||
const successResponse = () => ({ value: true }); | ||
const failureResponse = () => ({ value: false }); | ||
const missingPermissionError = () => new Error('MISSING_PERMISSION'); | ||
const alreadyRecordingError = () => new Error('ALREADY_RECORDING'); | ||
const deviceCannotVoiceRecordError = () => new Error('DEVICE_CANNOT_VOICE_RECORD'); | ||
const failedToRecordError = () => new Error('FAILED_TO_RECORD'); | ||
const recordingHasNotStartedError = () => new Error('RECORDING_HAS_NOT_STARTED'); | ||
const failedToFetchRecordingError = () => new Error('FAILED_TO_FETCH_RECORDING'); | ||
const couldNotQueryPermissionStatusError = () => new Error('COULD_NOT_QUERY_PERMISSION_STATUS'); | ||
// these mime types will be checked one by one in order until one of them is found to be supported by the current browser | ||
const possibleMimeTypes = ['audio/aac', 'audio/webm;codecs=opus', 'audio/mp4', 'audio/webm', 'audio/ogg;codecs=opus']; | ||
const neverResolvingPromise = () => new Promise(() => undefined); | ||
class VoiceRecorderImpl { | ||
constructor() { | ||
this.mediaRecorder = null; | ||
this.chunks = []; | ||
this.pendingResult = neverResolvingPromise(); | ||
} | ||
static async canDeviceVoiceRecord() { | ||
var _a; | ||
if (((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) == null || VoiceRecorderImpl.getSupportedMimeType() == null) { | ||
return failureResponse(); | ||
} | ||
else { | ||
return successResponse(); | ||
} | ||
} | ||
async startRecording() { | ||
if (this.mediaRecorder != null) { | ||
throw alreadyRecordingError(); | ||
} | ||
const deviceCanRecord = await VoiceRecorderImpl.canDeviceVoiceRecord(); | ||
if (!deviceCanRecord.value) { | ||
throw deviceCannotVoiceRecordError(); | ||
} | ||
const havingPermission = await VoiceRecorderImpl.hasAudioRecordingPermission().catch(() => successResponse()); | ||
if (!havingPermission.value) { | ||
throw missingPermissionError(); | ||
} | ||
navigator.mediaDevices.getUserMedia({ audio: true }) | ||
.then(this.onSuccessfullyStartedRecording.bind(this)) | ||
.catch(this.onFailedToStartRecording.bind(this)); | ||
return successResponse(); | ||
} | ||
async stopRecording() { | ||
if (this.mediaRecorder == null) { | ||
throw recordingHasNotStartedError(); | ||
} | ||
try { | ||
this.mediaRecorder.stop(); | ||
return this.pendingResult; | ||
} | ||
catch (ignore) { | ||
throw failedToFetchRecordingError(); | ||
} | ||
finally { | ||
this.prepareInstanceForNextOperation(); | ||
} | ||
} | ||
static async hasAudioRecordingPermission() { | ||
return navigator.permissions.query({ name: 'microphone' }) | ||
.then(result => ({ value: result.state === 'granted' })) | ||
.catch(() => { throw couldNotQueryPermissionStatusError(); }); | ||
} | ||
static async requestAudioRecordingPermission() { | ||
const havingPermission = await VoiceRecorderImpl.hasAudioRecordingPermission().catch(() => failureResponse()); | ||
if (havingPermission.value) { | ||
return successResponse(); | ||
} | ||
return navigator.mediaDevices.getUserMedia({ audio: true }) | ||
.then(() => successResponse()) | ||
.catch(() => failureResponse()); | ||
} | ||
pauseRecording() { | ||
if (this.mediaRecorder == null) { | ||
throw recordingHasNotStartedError(); | ||
} | ||
else if (this.mediaRecorder.state === 'recording') { | ||
this.mediaRecorder.pause(); | ||
return Promise.resolve(successResponse()); | ||
} | ||
else { | ||
return Promise.resolve(failureResponse()); | ||
} | ||
} | ||
resumeRecording() { | ||
if (this.mediaRecorder == null) { | ||
throw recordingHasNotStartedError(); | ||
} | ||
else if (this.mediaRecorder.state === 'paused') { | ||
this.mediaRecorder.resume(); | ||
return Promise.resolve(successResponse()); | ||
} | ||
else { | ||
return Promise.resolve(failureResponse()); | ||
} | ||
} | ||
getCurrentStatus() { | ||
if (this.mediaRecorder == null) { | ||
return Promise.resolve({ status: 'NONE' }); | ||
} | ||
else if (this.mediaRecorder.state === 'recording') { | ||
return Promise.resolve({ status: 'RECORDING' }); | ||
} | ||
else if (this.mediaRecorder.state === 'paused') { | ||
return Promise.resolve({ status: 'PAUSED' }); | ||
} | ||
else { | ||
return Promise.resolve({ status: 'NONE' }); | ||
} | ||
} | ||
static getSupportedMimeType() { | ||
if ((MediaRecorder === null || MediaRecorder === void 0 ? void 0 : MediaRecorder.isTypeSupported) == null) | ||
return null; | ||
const foundSupportedType = possibleMimeTypes.find(type => MediaRecorder.isTypeSupported(type)); | ||
return foundSupportedType !== null && foundSupportedType !== void 0 ? foundSupportedType : null; | ||
} | ||
onSuccessfullyStartedRecording(stream) { | ||
this.pendingResult = new Promise((resolve, reject) => { | ||
this.mediaRecorder = new MediaRecorder(stream); | ||
this.mediaRecorder.onerror = () => { | ||
reject(failedToRecordError()); | ||
this.prepareInstanceForNextOperation(); | ||
}; | ||
this.mediaRecorder.onstop = async () => { | ||
const mimeType = VoiceRecorderImpl.getSupportedMimeType(); | ||
if (mimeType == null) { | ||
reject(failedToFetchRecordingError()); | ||
} | ||
else { | ||
const blobVoiceRecording = new Blob(this.chunks, { 'type': mimeType }); | ||
const recordDataBase64 = await VoiceRecorderImpl.blobToBase64(blobVoiceRecording); | ||
const recordingDuration = await getBlobDuration__default['default'](blobVoiceRecording); | ||
this.prepareInstanceForNextOperation(); | ||
resolve({ value: { recordDataBase64, mimeType, msDuration: recordingDuration * 1000 } }); | ||
} | ||
}; | ||
this.mediaRecorder.ondataavailable = (event) => this.chunks.push(event.data); | ||
this.mediaRecorder.start(); | ||
}); | ||
} | ||
onFailedToStartRecording() { | ||
this.prepareInstanceForNextOperation(); | ||
throw failedToRecordError(); | ||
} | ||
static blobToBase64(blob) { | ||
return new Promise(resolve => { | ||
const reader = new FileReader(); | ||
reader.onloadend = () => resolve(String(reader.result)); | ||
reader.readAsDataURL(blob); | ||
}); | ||
} | ||
prepareInstanceForNextOperation() { | ||
if (this.mediaRecorder != null && this.mediaRecorder.state === 'recording') { | ||
try { | ||
this.mediaRecorder.stop(); | ||
} | ||
catch (ignore) { } | ||
} | ||
this.pendingResult = neverResolvingPromise(); | ||
this.mediaRecorder = null; | ||
this.chunks = []; | ||
} | ||
} | ||
class VoiceRecorderWeb extends core.WebPlugin { | ||
constructor() { | ||
super(...arguments); | ||
this.voiceRecorderInstance = new VoiceRecorderImpl(); | ||
} | ||
canDeviceVoiceRecord() { | ||
return Promise.reject(new Error('VoiceRecorder does not have web implementation')); | ||
return VoiceRecorderImpl.canDeviceVoiceRecord(); | ||
} | ||
hasAudioRecordingPermission() { | ||
return Promise.reject(new Error('VoiceRecorder does not have web implementation')); | ||
return VoiceRecorderImpl.hasAudioRecordingPermission(); | ||
} | ||
requestAudioRecordingPermission() { | ||
return Promise.reject(new Error('VoiceRecorder does not have web implementation')); | ||
return VoiceRecorderImpl.requestAudioRecordingPermission(); | ||
} | ||
startRecording() { | ||
return Promise.reject(new Error('VoiceRecorder does not have web implementation')); | ||
return this.voiceRecorderInstance.startRecording(); | ||
} | ||
stopRecording() { | ||
return Promise.reject(new Error('VoiceRecorder does not have web implementation')); | ||
return this.voiceRecorderInstance.stopRecording(); | ||
} | ||
pauseRecording() { | ||
return this.voiceRecorderInstance.pauseRecording(); | ||
} | ||
resumeRecording() { | ||
return this.voiceRecorderInstance.resumeRecording(); | ||
} | ||
getCurrentStatus() { | ||
return this.voiceRecorderInstance.getCurrentStatus(); | ||
} | ||
} | ||
@@ -38,3 +221,3 @@ | ||
}({}, capacitorExports)); | ||
}({}, capacitorExports, getBlobDuration)); | ||
//# sourceMappingURL=plugin.js.map |
{ | ||
"name": "capacitor-voice-recorder", | ||
"version": "1.1.1", | ||
"version": "1.2.0", | ||
"description": "Capacitor plugin for voice recording", | ||
@@ -63,2 +63,3 @@ "main": "dist/plugin.cjs.js", | ||
"@ionic/swiftlint-config": "^1.1.2", | ||
"@types/dom-mediacapture-record": "^1.0.10", | ||
"eslint": "^7.11.0", | ||
@@ -87,3 +88,6 @@ "prettier": "~2.2.0", | ||
} | ||
}, | ||
"dependencies": { | ||
"get-blob-duration": "^1.2.0" | ||
} | ||
} |
121
README.md
@@ -42,35 +42,64 @@ <p align="center"> | ||
| :------------------------------ | :------ | :-- | :-- | | ||
| canDeviceVoiceRecord | ✅ | ✅ | ❌ | | | ||
requestAudioRecordingPermission | ✅ | ✅ | ❌ | | ||
| hasAudioRecordingPermission | ✅ | ✅ | ❌ | | ||
| startRecording | ✅ | ✅ | ❌ | | ||
| stopRecording | ✅ | ✅ | ❌ | | ||
| canDeviceVoiceRecord | ✅ | ✅ | ✅ | | ||
requestAudioRecordingPermission | ✅ | ✅ | ✅ | | ||
| hasAudioRecordingPermission | ✅ | ✅ | ✅ | | ||
| startRecording | ✅ | ✅ | ✅ | | ||
| stopRecording | ✅ | ✅ | ✅ | | ||
| pauseRecording | ❌ | ❌ | ✅ | | ||
| resumeRecording | ❌ | ❌ | ✅ | | ||
| getCurrentStatus | ❌ | ❌ | ✅ | | ||
## Explanation | ||
* canDeviceVoiceRecord - this function has been updated for Capacitor version 3 and will now ALWAYS return a promise that resolves to {"value": true}. | ||
it has not been removed completely to not break old implementations. | ||
* canDeviceVoiceRecord - on mobile this function will always return a promise that resolves to `{ value: true }`, | ||
while in a browser it will be resolved to `{ value: true }` / `{ value: false }` based on the browser's ability to record. | ||
note that this method does not take into account the permission status, | ||
only if the browser itself is capable of recording at all. | ||
--- | ||
* requestAudioRecordingPermission - if the permission has already been provided then the promise will resolve with {"value": true}, | ||
otherwise the promise will resolve to {"value": true} / {"value": false} based on the answer of the user to the request. | ||
* requestAudioRecordingPermission - if the permission has already been provided then the promise will resolve with `{ value: true }`, | ||
otherwise the promise will resolve to `{ value: true }` / `{ value: false }` based on the answer of the user to the request. | ||
--- | ||
* hasAudioRecordingPermission - will resolve to {"value": true} / {"value": false} based on the status of the permission. | ||
* hasAudioRecordingPermission - will resolve to `{ value: true }` / `{ value: false }` based on the status of the permission. | ||
please note that the web implementation of this plugin uses the Permissions API under the hood which is not widespread as of now. | ||
as a result, if the status of the permission cannot be checked the promise will reject with `COULD_NOT_QUERY_PERMISSION_STATUS`. | ||
in that case you have no choice but to use the `requestAudioRecordingPermission` function straight away or `startRecording` and capture any exception that is thrown. | ||
--- | ||
* startRecording - if the app lacks the required permission then the promise will reject with the message "MISSING_PERMISSION". | ||
if there's a recording already running then the promise will reject with "ALREADY_RECORDING", | ||
* startRecording - if the app lacks the required permission then the promise will reject with the message `MISSING_PERMISSION`. | ||
if the current device cannot voice record at all (for example, due to old browser) then the promise will reject with `DEVICE_CANNOT_VOICE_RECORD`. | ||
if there's a recording already running then the promise will reject with `ALREADY_RECORDING`, | ||
and if other apps are using the microphone then the promise will reject | ||
with "MICROPHONE_BEING_USED". in a case of unknown error the promise will reject with "FAILED_TO_RECORD". | ||
with `MICROPHONE_BEING_USED`. in a case of unknown error the promise will reject with `FAILED_TO_RECORD`. | ||
--- | ||
* stopRecording - will stop the recording that has been previously started. if the function startRecording() has not been called beforehand | ||
the promise will reject with: "RECORDING_HAS_NOT_STARTED". in case of success, you will get the recording in base-64, the duration of the | ||
* stopRecording - will stop the recording that has been previously started. if the function `startRecording` has not been called beforehand | ||
the promise will reject with `RECORDING_HAS_NOT_STARTED`. | ||
in a case of unknown error the promise will reject with `FAILED_TO_FETCH_RECORDING`. | ||
in case of success, you will get the recording in base-64, the duration of the | ||
recording in milliseconds, and the mime type. | ||
--- | ||
* pauseRecording - will pause an ongoing recording. note that if the recording has not started yet the promise | ||
will reject with `RECORDING_HAS_NOT_STARTED`. in case of success the promise will resolve to `{ value: true }` if the pause | ||
was successful or `{ value: false }` if the recording is already paused. | ||
--- | ||
* resumeRecording - will resume a paused recording. note that if the recording has not started yet the promise | ||
will reject with `RECORDING_HAS_NOT_STARTED`. in case of success the promise will resolve to `{ value: true }` if the resume | ||
was successful or `{ value: false }` if the recording is already running. | ||
--- | ||
* getCurrentStatus - will let you know the current status of the current recording (if there is any at all). | ||
will resolve with one of the following values: `{ status: "NONE" }` if the plugin is idle and waiting to start a new recording. | ||
`{ status: "RECORDING" }` if the plugin is in the middle of recording and `{ status: "PAUSED" }` if the recording is paused right now. | ||
## Usage | ||
@@ -81,5 +110,5 @@ | ||
// only 'VoiceRecorder' is mandatory, the rest is for typing | ||
import { VoiceRecorder, VoiceRecorderPlugin, RecordingData, GenericResponse } from 'capacitor-voice-recorder'; | ||
import { VoiceRecorder, VoiceRecorderPlugin, RecordingData, GenericResponse, CurrentRecordingStatus } from 'capacitor-voice-recorder'; | ||
// will ALWAYS print true (do not use this method, it has not been removed to keep old implementations from breaking) | ||
// will print true / false based on the ability of the current device (or web browser) to record audio | ||
VoiceRecorder.canDeviceVoiceRecord().then((result: GenericResponse) => console.log(result.value)) | ||
@@ -93,9 +122,13 @@ | ||
// will print true / false based on the status of the recording permission | ||
/** | ||
* will print true / false based on the status of the recording permission. | ||
* the promise will reject with "COULD_NOT_QUERY_PERMISSION_STATUS" | ||
* if the current device cannot query the current status of the recording permission | ||
*/ | ||
VoiceRecorder.hasAudioRecordingPermission.then((result: GenericResponse) => console.log(result.value)) | ||
/** | ||
* In case of success the promise will resolve with {"value": true} | ||
* In case of success the promise will resolve to { value: true } | ||
* in case of an error the promise will reject with one of the following messages: | ||
* "MISSING_PERMISSION", "ALREADY_RECORDING", "MICROPHONE_BEING_USED" or "FAILED_TO_RECORD" | ||
* "MISSING_PERMISSION", "ALREADY_RECORDING", "MICROPHONE_BEING_USED", "DEVICE_CANNOT_VOICE_RECORD", or "FAILED_TO_RECORD" | ||
*/ | ||
@@ -107,5 +140,5 @@ VoiceRecorder.startRecording() | ||
/** | ||
* In case of success the promise will resolve with: | ||
* In case of success the promise will resolve to: | ||
* {"value": { recordDataBase64: string, msDuration: number, mimeType: string }}, | ||
* the file will be in *.acc format. | ||
* the file will be in one of several possible formats (more on that later). | ||
* in case of an error the promise will reject with one of the following messages: | ||
@@ -118,4 +151,45 @@ * "RECORDING_HAS_NOT_STARTED" or "FAILED_TO_FETCH_RECORDING" | ||
/** | ||
* will pause an ongoing recording. note that if the recording has not started yet the promise | ||
* will reject with `RECORDING_HAS_NOT_STARTED`. in case of success the promise will resolve to `{ value: true }` if the pause | ||
* was successful or `{ value: false }` if the recording is already paused. | ||
*/ | ||
VoiceRecorder.pauseRecording() | ||
.then((result: GenericResponse) => console.log(result.value)) | ||
.catch(error => console.log(error)) | ||
/** | ||
* will resume a paused recording. note that if the recording has not started yet the promise | ||
* will reject with `RECORDING_HAS_NOT_STARTED`. in case of success the promise will resolve to `{ value: true }` if the resume | ||
* was successful or `{ value: false }` if the recording is already running | ||
*/ | ||
VoiceRecorder.resumeRecording() | ||
.then((result: GenericResponse) => console.log(result.value)) | ||
.catch(error => console.log(error)) | ||
/** | ||
* Will return the current status of the plugin. | ||
* in this example one of these possible values will be printed: "NONE" / "RECORDING" / "PAUSED" | ||
*/ | ||
VoiceRecorder.getCurrentStatus() | ||
.then((result: CurrentRecordingStatus) => console.log(result.status)) | ||
.catch(error => console.log(error)) | ||
``` | ||
## Format and Mime type | ||
The plugin will return the recording in one of several possible formats. | ||
the format is dependent on the os / web browser that the user uses. | ||
on android and ios the mime type will be `audio/aac`, while on chrome and firefox it | ||
will be `audio/webm;codecs=opus` and on safari it will be `audio/mp4`. | ||
note that these 3 browsers has been tested on. the plugin should still work on | ||
other browsers, as there is a list of mime types that the plugin checks against the | ||
user's browser. | ||
Note that this fact might cause unexpected behavior in case you'll try to play recordings | ||
between several devices or browsers - as they not all support the same set of audio formats. | ||
it is recommended to convert the recordings to a format that all your target devices supports. | ||
as this plugin focuses on the recording aspect, it does not provide any conversion between formats. | ||
## Playback | ||
@@ -127,5 +201,6 @@ | ||
const base64Sound = '...' // from plugin | ||
const audioRef = new Audio(`data:audio/aac;base64,${base64Sound}`) | ||
const mimeType = '...' // from plugin | ||
const audioRef = new Audio(`data:${mimeType};base64,${base64Sound}`) | ||
audioRef.oncanplaythrough = () => audioRef.play() | ||
audioRef.load() | ||
``` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
108807
39
856
201
2
15
1
+ Addedget-blob-duration@^1.2.0
+ Added@babel/runtime@7.11.2(transitive)
+ Addedget-blob-duration@1.2.0(transitive)
+ Addedregenerator-runtime@0.13.11(transitive)