From d10cd6d2d2ac014d7a74d801d7ac4482ae08e6b0 Mon Sep 17 00:00:00 2001 From: hanashiro Date: Sat, 15 Oct 2022 14:30:41 -0300 Subject: [PATCH] feat: MP4 file type support --- README.md | 15 +++++---- package.json | 2 ++ src/client/data/hooks/pages/useIndex.page.ts | 23 +++++++++++-- src/client/data/services/RecorderService.ts | 35 ++++++++++++++++++-- src/client/ui/pages/index/index.styled.tsx | 4 +-- src/client/ui/pages/index/index.tsx | 17 ++++++++++ src/main.dev.ts | 2 +- src/package.json | 30 ++++++++--------- yarn.lock | 10 ++++++ 9 files changed, 109 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index de5234c..074e0c6 100644 --- a/README.md +++ b/README.md @@ -9,18 +9,19 @@ A Free Simple Screen Recorder with annotation tools. Made with Electron. MemoPlay Screen - Recording --- + ## Table of contents -- [Download](#download) -- [Features](#features) -- [Supported File Formats](#supported-file-formats) -- [Hotkeys and commands](#hotkeys-and-commands) + +- [Download](#download) +- [Features](#features) +- [Supported File Formats](#supported-file-formats) +- [Hotkeys and commands](#hotkeys-and-commands) --- ## Download -- [Windows](https://github.com/mewters/memoplay-screen-recorder/releases/download/v2.0.0-beta-01/MemoPlay.Setup.2.0.0.exe) - +- [Windows](https://github.com/mewters/memoplay-screen-recorder/releases/download/v2.0.0-beta-01/MemoPlay.Setup.2.0.0.exe) --- @@ -45,7 +46,7 @@ A Free Simple Screen Recorder with annotation tools. Made with Electron. ## Supported File Formats - .webm (no video length bug 😁) -- .mp4 (soon) +- .mp4 - .avi (soon) --- diff --git a/package.json b/package.json index 8d135e2..125d592 100644 --- a/package.json +++ b/package.json @@ -160,6 +160,7 @@ "@types/dom-mediacapture-record": "^1.0.10", "@types/enzyme": "^3.10.5", "@types/enzyme-adapter-react-16": "^1.0.6", + "@types/ffmpeg.js": "^3.1.3", "@types/history": "4.7.6", "@types/jest": "^26.0.15", "@types/node": "14.14.10", @@ -244,6 +245,7 @@ "fabric": "^4.5.1", "fabric-history": "^1.6.0", "fabricjs-react": "^1.0.8", + "ffmpeg.js": "^4.2.9003", "fix-webm-duration": "^1.0.0", "history": "^5.0.0", "react": "^17.0.1", diff --git a/src/client/data/hooks/pages/useIndex.page.ts b/src/client/data/hooks/pages/useIndex.page.ts index 6f6df35..d57eadb 100644 --- a/src/client/data/hooks/pages/useIndex.page.ts +++ b/src/client/data/hooks/pages/useIndex.page.ts @@ -1,16 +1,18 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { ipcRenderer } from 'electron'; import { FileService } from '../../services/FileService'; import { RecorderService } from '../../services/RecorderService'; import { UserMediaService } from '../../services/UserMediaService'; import useLocalStorage from '../useLocalStorage.hook'; import useTimeCounter from '../useTimeCounter.hook'; +import { LocalStorage } from '../../services/StorageService'; export default function useIndex() { const [isRecording, setIsRecording] = useState(false), [isRecordingPaused, setIsRecordingPaused] = useState(false), [folder, setFolder] = useLocalStorage('folderName', ''), [fileName, setFileName] = useState(''), + [fileType, setFileType] = useLocalStorage('fileType', 'webm'), [hasTime, setHasTime] = useLocalStorage('hasTimeOnFileName', true), [audioSourceId, setAudioSourceId] = useLocalStorage( 'audioSourceId', @@ -29,6 +31,19 @@ export default function useIndex() { windowSources: Electron.DesktopCapturerSource[]; screenSources: Electron.DesktopCapturerSource[]; } | null>(null), + fileTypeList = useMemo( + () => [ + { + label: 'WebM', + value: 'webm', + }, + { + label: 'MP4', + value: 'mp4', + }, + ], + [] + ), { totalTime, startTimer, @@ -133,9 +148,10 @@ export default function useIndex() { if (isRecording) { stopTimer(async (totalTime) => { const buffer = await RecorderService.stop(totalTime * 1000), + fileType = LocalStorage.get('fileType', 'webm'), fullFileName = `${fileName}${ hasTime ? Date.now().toString() : '' - }.webm`; + }.${fileType}`; await FileService.saveVideo(buffer, folder, fullFileName); setIsRecording(false); @@ -178,6 +194,9 @@ export default function useIndex() { isRecording, isRecordingPaused, folder, + fileTypeList, + fileType, + setFileType, fileName, setFileName, hasTime, diff --git a/src/client/data/services/RecorderService.ts b/src/client/data/services/RecorderService.ts index cea2dc6..915711b 100644 --- a/src/client/data/services/RecorderService.ts +++ b/src/client/data/services/RecorderService.ts @@ -1,4 +1,7 @@ import ysFixWebmDuration from 'fix-webm-duration'; +// @ts-ignore +import ffmpeg from 'ffmpeg.js/ffmpeg-mp4.js'; +import { LocalStorage } from './StorageService'; interface RecorderServiceInterface { recorder: MediaRecorder | null; @@ -70,10 +73,38 @@ export const RecorderService: RecorderServiceInterface = { }, toArrayBuffer(blob: Blob): Promise { return new Promise((resolve) => { + const fileType = LocalStorage.get('fileType', 'webm'); let fileReader = new FileReader(); fileReader.onload = function () { - let arrayBuffer = this.result as ArrayBuffer; - resolve(arrayBuffer); + if (fileType === 'mp4') { + const mp4 = ffmpeg({ + MEMFS: [ + { + name: 'test.webm', + data: this.result as ArrayBuffer, + }, + ], + arguments: [ + '-i', + 'test.webm', + '-vcodec', + 'copy', + '-qscale', + '0', + 'test.mp4', + ], + stdin: function () {}, + }); + + const mp4blob = new Blob([mp4.MEMFS[0].data], { + type: 'video/mp4', + }); + + resolve(mp4blob.arrayBuffer()); + } else { + let arrayBuffer = this.result as ArrayBuffer; + resolve(arrayBuffer); + } }; fileReader.readAsArrayBuffer(blob); }); diff --git a/src/client/ui/pages/index/index.styled.tsx b/src/client/ui/pages/index/index.styled.tsx index 9f4bb88..a83675e 100644 --- a/src/client/ui/pages/index/index.styled.tsx +++ b/src/client/ui/pages/index/index.styled.tsx @@ -1,7 +1,7 @@ import { styled } from '@material-ui/core/styles'; export const PageContainer = styled('div')` - max-width: 510px; + max-width: 600px; background-color: ${({ theme }) => theme.palette.background.default}; color: ${({ theme }) => theme.palette.text.primary}; padding: ${({ theme }) => theme.spacing(2) + ' ' + theme.spacing()}; @@ -66,7 +66,7 @@ export const ButtonsContainer = styled('div')` export const FileNameContainer = styled('div')` display: grid; - grid-template-columns: 1fr 160px 80px; + grid-template-columns: 1fr 160px 92px 80px; gap: ${({ theme }) => theme.spacing()}; margin-top: ${({ theme }) => theme.spacing()}; align-items: center; diff --git a/src/client/ui/pages/index/index.tsx b/src/client/ui/pages/index/index.tsx index 4ae4d7d..af0fd66 100644 --- a/src/client/ui/pages/index/index.tsx +++ b/src/client/ui/pages/index/index.tsx @@ -41,6 +41,9 @@ export default function Index() { isRecording, isRecordingPaused, folder, + fileTypeList, + fileType, + setFileType, fileName, setFileName, hasTime, @@ -193,6 +196,20 @@ export default function Index() { value={fileName} onChange={(event) => setFileName(event.target.value)} /> + { mainWindow = new BrowserWindow({ title: 'MemoPlay Screen Recorder', show: false, - width: 510, + width: 600, height: 240, resizable: !app.isPackaged, icon: getAssetPath('icon.png'), diff --git a/src/package.json b/src/package.json index 384c221..d5d5220 100644 --- a/src/package.json +++ b/src/package.json @@ -1,17 +1,17 @@ { - "name": "memoplay-screen-recorder", - "productName": "MemoPlay Screen Recorder", - "description": "A simple screen recorder", - "version": "2.0.0", - "main": "./main.prod.js", - "author": { - "name": "Akira Hanashiro", - "url": "https://github.com/hanashiro" - }, - "scripts": { - "electron-rebuild": "node -r ../.erb/scripts/BabelRegister.js ../.erb/scripts/ElectronRebuild.js", - "postinstall": "yarn electron-rebuild" - }, - "license": "MIT", - "dependencies": {} + "name": "memoplay-screen-recorder", + "productName": "MemoPlay Screen Recorder", + "description": "A simple screen recorder", + "version": "2.1.0", + "main": "./main.prod.js", + "author": { + "name": "Akira Hanashiro", + "url": "https://github.com/hanashiro" + }, + "scripts": { + "electron-rebuild": "node -r ../.erb/scripts/BabelRegister.js ../.erb/scripts/ElectronRebuild.js", + "postinstall": "yarn electron-rebuild" + }, + "license": "MIT", + "dependencies": {} } diff --git a/yarn.lock b/yarn.lock index 0d1150e..6badbdd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1822,6 +1822,11 @@ resolved "https://registry.yarnpkg.com/@types/fabric/-/fabric-4.5.2.tgz#f6fbd35cec281fb7c0b3f2387b3b0e01fe68d3f5" integrity sha512-uvoUrgBqP/l5MJQ6tWHSwDll3whxxaevPoL7MLxwY4Cm5TBc7ohPrfTZgG/L0/6/8YLWOScjjtv2e+X9xMNIxw== +"@types/ffmpeg.js@^3.1.3": + version "3.1.3" + resolved "https://registry.yarnpkg.com/@types/ffmpeg.js/-/ffmpeg.js-3.1.3.tgz#3b6b39a4a75165f08e82c7547f54ec1720ca0385" + integrity sha512-kqDR3KxSxZg7It3/YGLVGUbrW3pxvYSQ5YR90w4V/GXyAdISzvlS/6Gzdt5y8vCwF+viu1nVl+fD6UBio1Z1UA== + "@types/fs-extra@^9.0.7": version "9.0.11" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.11.tgz#8cc99e103499eab9f347dbc6ca4e99fb8d2c2b87" @@ -5821,6 +5826,11 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +ffmpeg.js@^4.2.9003: + version "4.2.9003" + resolved "https://registry.yarnpkg.com/ffmpeg.js/-/ffmpeg.js-4.2.9003.tgz#3a97d53c35eaee5c27cd7d0edf7c57ccec4a059d" + integrity sha512-l1JBr8HwnnJEaSwg5p8K3Ifbom8O2IDHsZp7UVyr6MzQ7gc32tt/2apoOuQAr/j76c+uDOjla799VSsBnRvSTg== + figures@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"