react-hook-recorder
Advanced tools
Comparing version 0.0.8 to 0.0.9
@@ -8,9 +8,10 @@ /// <reference types="dom-mediacapture-record" /> | ||
}; | ||
declare type StopRecordingCallback = (blob: Blob, url: String) => void; | ||
export declare const enum RecorderStatus { | ||
declare type StopRecordingCallback = (blob: Blob, url: string) => void; | ||
export declare enum RecorderStatus { | ||
"IDLE" = "idle", | ||
"INIT" = "init", | ||
"RECORDING" = "recording" | ||
"RECORDING" = "recording", | ||
"UNREGISTERED" = "unregistered" | ||
} | ||
export declare const enum RecorderError { | ||
export declare enum RecorderError { | ||
"STREAM_INIT" = "stream-init", | ||
@@ -20,10 +21,11 @@ "RECORDER_INIT" = "recorder-init" | ||
declare function useRecorder(mediaStreamConstraints?: Partial<MediaStreamConstraints>, mediaRecorderOptions?: Partial<MediaRecorderOptions>): { | ||
mediaRecorder: MediaRecorder | undefined; | ||
stream: MediaStream | undefined; | ||
mediaRecorder?: MediaRecorder; | ||
stream?: MediaStream; | ||
startRecording: () => void; | ||
stopRecording: (callback: StopRecordingCallback) => () => void; | ||
register: (element: HTMLVideoElement) => void; | ||
unregister: () => void; | ||
status: RecorderStatus; | ||
error: RecorderError | undefined; | ||
error?: RecorderError; | ||
}; | ||
export default useRecorder; |
@@ -50,2 +50,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.RecorderError = exports.RecorderStatus = void 0; | ||
var react_1 = require("react"); | ||
@@ -56,2 +57,14 @@ var defaultContraints = { | ||
}; | ||
var RecorderStatus; | ||
(function (RecorderStatus) { | ||
RecorderStatus["IDLE"] = "idle"; | ||
RecorderStatus["INIT"] = "init"; | ||
RecorderStatus["RECORDING"] = "recording"; | ||
RecorderStatus["UNREGISTERED"] = "unregistered"; | ||
})(RecorderStatus = exports.RecorderStatus || (exports.RecorderStatus = {})); | ||
var RecorderError; | ||
(function (RecorderError) { | ||
RecorderError["STREAM_INIT"] = "stream-init"; | ||
RecorderError["RECORDER_INIT"] = "recorder-init"; | ||
})(RecorderError = exports.RecorderError || (exports.RecorderError = {})); | ||
function useRecorder(mediaStreamConstraints, mediaRecorderOptions) { | ||
@@ -61,42 +74,26 @@ var _this = this; | ||
var streamRef = react_1.useRef(); | ||
var _a = react_1.useState("init" /* INIT */), status = _a[0], setStatus = _a[1]; | ||
var videoElementRef = react_1.useRef(); | ||
var _a = react_1.useState(RecorderStatus.INIT), status = _a[0], setStatus = _a[1]; | ||
var _b = react_1.useState(), error = _b[0], setError = _b[1]; | ||
var register = react_1.useCallback(function (element) { | ||
initStream(element).then(initMediaRecorder).catch(setError); | ||
}, []); | ||
var startRecording = react_1.useCallback(function () { | ||
var _a; | ||
setStatus("recording" /* RECORDING */); | ||
(_a = mediaRecorderRef.current) === null || _a === void 0 ? void 0 : _a.start(); | ||
}, []); | ||
var stopRecording = react_1.useCallback(function (callback) { return function () { | ||
var _a; | ||
setStatus("idle" /* IDLE */); | ||
if (mediaRecorderRef.current) { | ||
mediaRecorderRef.current.ondataavailable = function (_a) { | ||
var blob = _a.data; | ||
callback(blob, URL.createObjectURL(blob)); | ||
}; | ||
(_a = mediaRecorderRef.current) === null || _a === void 0 ? void 0 : _a.stop(); | ||
} | ||
}; }, []); | ||
var initStream = react_1.useCallback(function (videoRef) { return __awaiter(_this, void 0, void 0, function () { | ||
var _a, err_1; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
var _b; | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: | ||
_b.trys.push([0, 2, , 3]); | ||
_c.trys.push([0, 2, , 3]); | ||
_a = streamRef; | ||
return [4 /*yield*/, navigator.mediaDevices.getUserMedia(__assign(__assign({}, defaultContraints), (mediaStreamConstraints ? __assign({}, mediaStreamConstraints) : {})))]; | ||
return [4 /*yield*/, ((_b = navigator.mediaDevices) === null || _b === void 0 ? void 0 : _b.getUserMedia(__assign(__assign({}, defaultContraints), (mediaStreamConstraints ? __assign({}, mediaStreamConstraints) : {}))))]; | ||
case 1: | ||
_a.current = _b.sent(); | ||
videoRef.srcObject = streamRef.current; | ||
_a.current = _c.sent(); | ||
videoElementRef.current = videoRef; | ||
videoElementRef.current.srcObject = streamRef.current; | ||
return [2 /*return*/, streamRef.current]; | ||
case 2: | ||
err_1 = _b.sent(); | ||
throw new Error("stream-init" /* STREAM_INIT */); | ||
err_1 = _c.sent(); | ||
throw new Error(RecorderError.STREAM_INIT); | ||
case 3: return [2 /*return*/]; | ||
} | ||
}); | ||
}); }, []); | ||
}); }, [mediaStreamConstraints]); | ||
var initMediaRecorder = react_1.useCallback(function (stream) { | ||
@@ -110,11 +107,39 @@ if ((mediaRecorderOptions === null || mediaRecorderOptions === void 0 ? void 0 : mediaRecorderOptions.mimeType) && | ||
mediaRecorderRef.current = recorder; | ||
setStatus("idle" /* IDLE */); | ||
setStatus(RecorderStatus.IDLE); | ||
} | ||
catch (_a) { | ||
throw new Error("recorder-init" /* RECORDER_INIT */); | ||
throw new Error(RecorderError.RECORDER_INIT); | ||
} | ||
}, [mediaRecorderOptions]); | ||
var register = react_1.useCallback(function (element) { | ||
initStream(element).then(initMediaRecorder).catch(setError); | ||
}, [initMediaRecorder, initStream]); | ||
var unregister = react_1.useCallback(function () { | ||
var _a; | ||
if (videoElementRef.current) { | ||
videoElementRef.current.pause(); | ||
videoElementRef.current.src = ""; | ||
} | ||
var tracks = (_a = streamRef.current) === null || _a === void 0 ? void 0 : _a.getTracks(); | ||
if (!tracks || (tracks === null || tracks === void 0 ? void 0 : tracks.length) === 0) | ||
return; | ||
tracks.forEach(function (track) { return track.stop(); }); | ||
setStatus(RecorderStatus.UNREGISTERED); | ||
}, []); | ||
if (!navigator.mediaDevices) { | ||
throw new Error("Navigator is not compatible"); | ||
} | ||
var startRecording = react_1.useCallback(function () { | ||
var _a; | ||
setStatus(RecorderStatus.RECORDING); | ||
(_a = mediaRecorderRef.current) === null || _a === void 0 ? void 0 : _a.start(); | ||
}, []); | ||
var stopRecording = react_1.useCallback(function (callback) { return function () { | ||
var _a; | ||
setStatus(RecorderStatus.IDLE); | ||
if (mediaRecorderRef.current) { | ||
mediaRecorderRef.current.ondataavailable = function (_a) { | ||
var blob = _a.data; | ||
callback(blob, URL.createObjectURL(blob)); | ||
}; | ||
(_a = mediaRecorderRef.current) === null || _a === void 0 ? void 0 : _a.stop(); | ||
} | ||
}; }, []); | ||
return { | ||
@@ -126,2 +151,3 @@ mediaRecorder: mediaRecorderRef === null || mediaRecorderRef === void 0 ? void 0 : mediaRecorderRef.current, | ||
register: register, | ||
unregister: unregister, | ||
status: status, | ||
@@ -128,0 +154,0 @@ error: error, |
{ | ||
"name": "react-hook-recorder", | ||
"version": "0.0.8", | ||
"version": "0.0.9", | ||
"description": "React based hook to use native MediaRecorder API", | ||
@@ -25,9 +25,16 @@ "main": "dist/index.js", | ||
"@babel/core": "^7.13.15", | ||
"@storybook/addon-actions": "^6.2.8", | ||
"@storybook/addon-essentials": "^6.2.8", | ||
"@storybook/addon-links": "^6.2.8", | ||
"@storybook/react": "^6.2.8", | ||
"@storybook/addon-actions": "^6.3.4", | ||
"@storybook/addon-essentials": "^6.3.4", | ||
"@storybook/addon-links": "^6.3.4", | ||
"@storybook/react": "^6.3.4", | ||
"@typescript-eslint/eslint-plugin": "^4.28.3", | ||
"@typescript-eslint/parser": "^4.28.3", | ||
"babel-loader": "^8.2.2", | ||
"eslint": "^7.30.0", | ||
"eslint-config-prettier": "^8.3.0", | ||
"eslint-plugin-react": "^7.24.0", | ||
"eslint-plugin-react-hooks": "^4.2.0", | ||
"rimraf": "^3.0.2", | ||
"storybook": "^6.2.8" | ||
"storybook": "^6.3.4", | ||
"typescript": "^4.3.5" | ||
}, | ||
@@ -34,0 +41,0 @@ "repository": { |
@@ -15,11 +15,12 @@ import { useCallback, useRef, useState } from "react"; | ||
type StopRecordingCallback = (blob: Blob, url: String) => void; | ||
type StopRecordingCallback = (blob: Blob, url: string) => void; | ||
export const enum RecorderStatus { | ||
export enum RecorderStatus { | ||
"IDLE" = "idle", | ||
"INIT" = "init", | ||
"RECORDING" = "recording", | ||
"UNREGISTERED" = "unregistered", | ||
} | ||
export const enum RecorderError { | ||
export enum RecorderError { | ||
"STREAM_INIT" = "stream-init", | ||
@@ -32,10 +33,76 @@ "RECORDER_INIT" = "recorder-init", | ||
mediaRecorderOptions?: Partial<MediaRecorderOptions> | ||
) { | ||
): { | ||
mediaRecorder?: MediaRecorder; | ||
stream?: MediaStream; | ||
startRecording: () => void; | ||
stopRecording: (callback: StopRecordingCallback) => () => void; | ||
register: (element: HTMLVideoElement) => void; | ||
unregister: () => void; | ||
status: RecorderStatus; | ||
error?: RecorderError; | ||
} { | ||
const mediaRecorderRef = useRef<MediaRecorder>(); | ||
const streamRef = useRef<MediaStream>(); | ||
const videoElementRef = useRef<HTMLVideoElement>(); | ||
const [status, setStatus] = useState<RecorderStatus>(RecorderStatus.INIT); | ||
const [error, setError] = useState<RecorderError>(); | ||
const register = useCallback((element: HTMLVideoElement) => { | ||
initStream(element).then(initMediaRecorder).catch(setError); | ||
const initStream = useCallback( | ||
async (videoRef: HTMLVideoElement) => { | ||
try { | ||
streamRef.current = await navigator.mediaDevices?.getUserMedia({ | ||
...defaultContraints, | ||
...(mediaStreamConstraints ? { ...mediaStreamConstraints } : {}), | ||
}); | ||
videoElementRef.current = videoRef; | ||
videoElementRef.current.srcObject = streamRef.current; | ||
return streamRef.current; | ||
} catch (err) { | ||
throw new Error(RecorderError.STREAM_INIT); | ||
} | ||
}, | ||
[mediaStreamConstraints] | ||
); | ||
const initMediaRecorder = useCallback( | ||
(stream: MediaStream) => { | ||
if ( | ||
mediaRecorderOptions?.mimeType && | ||
!MediaRecorder.isTypeSupported(mediaRecorderOptions.mimeType) | ||
) { | ||
console.warn( | ||
`MIME type ${mediaRecorderOptions.mimeType} not supported` | ||
); | ||
} | ||
try { | ||
const recorder = new MediaRecorder( | ||
stream, | ||
{ ...mediaRecorderOptions } || {} | ||
); | ||
mediaRecorderRef.current = recorder; | ||
setStatus(RecorderStatus.IDLE); | ||
} catch { | ||
throw new Error(RecorderError.RECORDER_INIT); | ||
} | ||
}, | ||
[mediaRecorderOptions] | ||
); | ||
const register = useCallback( | ||
(element: HTMLVideoElement) => { | ||
initStream(element).then(initMediaRecorder).catch(setError); | ||
}, | ||
[initMediaRecorder, initStream] | ||
); | ||
const unregister = useCallback(() => { | ||
if (videoElementRef.current) { | ||
videoElementRef.current.pause(); | ||
videoElementRef.current.src = ""; | ||
} | ||
const tracks = streamRef.current?.getTracks(); | ||
if (!tracks || tracks?.length === 0) return; | ||
tracks.forEach((track) => track.stop()); | ||
setStatus(RecorderStatus.UNREGISTERED); | ||
}, []); | ||
@@ -63,39 +130,2 @@ | ||
const initStream = useCallback(async (videoRef: HTMLVideoElement) => { | ||
try { | ||
streamRef.current = await navigator.mediaDevices.getUserMedia({ | ||
...defaultContraints, | ||
...(mediaStreamConstraints ? { ...mediaStreamConstraints } : {}), | ||
}); | ||
videoRef.srcObject = streamRef.current; | ||
return streamRef.current; | ||
} catch (err) { | ||
throw new Error(RecorderError.STREAM_INIT); | ||
} | ||
}, []); | ||
const initMediaRecorder = useCallback((stream: MediaStream) => { | ||
if ( | ||
mediaRecorderOptions?.mimeType && | ||
!MediaRecorder.isTypeSupported(mediaRecorderOptions.mimeType) | ||
) { | ||
console.warn(`MIME type ${mediaRecorderOptions.mimeType} not supported`); | ||
} | ||
try { | ||
const recorder = new MediaRecorder( | ||
stream, | ||
{ ...mediaRecorderOptions } || {} | ||
); | ||
mediaRecorderRef.current = recorder; | ||
setStatus(RecorderStatus.IDLE); | ||
} catch { | ||
throw new Error(RecorderError.RECORDER_INIT); | ||
} | ||
}, []); | ||
if (!navigator.mediaDevices) { | ||
throw new Error("Navigator is not compatible"); | ||
} | ||
return { | ||
@@ -107,2 +137,3 @@ mediaRecorder: mediaRecorderRef?.current, | ||
register, | ||
unregister, | ||
status, | ||
@@ -109,0 +140,0 @@ error, |
{ | ||
"compilerOptions": { | ||
"target": "es5", | ||
"lib": ["es2017", "es7", "es6", "dom"], | ||
"module": "commonjs", | ||
"target": "ES5", | ||
"lib": ["ES2017", "ES7", "ES6", "DOM"], | ||
"module": "CommonJS", | ||
"jsx": "react", | ||
@@ -10,2 +10,3 @@ "strict": true, | ||
"skipLibCheck": false, | ||
"strictNullChecks": true, | ||
"forceConsistentCasingInFileNames": true, | ||
@@ -15,3 +16,3 @@ "declaration": true, | ||
}, | ||
"exclude": ["./src/stories"] | ||
"exclude": ["./src/stories", "./dist"] | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
18614
12
350
15