录制优化

This commit is contained in:
yj 2024-08-08 13:58:28 +08:00
parent 998f7f781a
commit 0c5365ccb1
4 changed files with 89 additions and 8 deletions

View File

@ -8,8 +8,8 @@ const {
ipcMain,
clipboard,
dialog,
webFrame,
Notification,
desktopCapturer,
} = require('electron');
const path = require('node:path')
const { autoUpdater, CancellationToken } = require('electron-updater');
@ -180,6 +180,12 @@ app.on('ready', () => {
ipcMain.handle('getIsMaximized', () => {
return mainWindow.isMaximized();
});
// 获取共享屏幕列表
ipcMain.handle('getSources', async () => {
return await desktopCapturer.getSources({
types: ['screen']
});
});
// 复制文字
ipcMain.handle('setWriteText', (event, text) => {
clipboard.writeText(text)

View File

@ -13,6 +13,10 @@ window.electron = {
getIsMaximized: () => {
return ipcRenderer.invoke('getIsMaximized')
},
// 获取共享屏幕列表
getSources: () => {
return ipcRenderer.invoke('getSources')
},
// 复制文字
setWriteText: (text) => {
return ipcRenderer.invoke('setWriteText', text)

View File

@ -19,6 +19,7 @@ import { GetUserList } from '@/api/Home/User';
import Avatar from '@/components/Avatar';
import SharedFilesModel from '@/components/SharedFilesModel';
import StupWizard from '@/components/StupWizard';
const { exec } = require('child_process');
const fs = require('fs').promises;
dayjs.extend(durationPlugin);
const Meeting: React.FC = () => {
@ -116,6 +117,8 @@ const Meeting: React.FC = () => {
const [userSearchValue, setUserSearchValue] = useState('')
const [noViewChatList, setNoViewChatList] = useState(0)
const [currentLookUserAccount, setCurrentLookUserAccount] = useState<any>('')
const [recorder, setRecorder] = useState<any>('')
const [mediaStream, setMediaStream] = useState<any>('')
const [currentLookUserStatus, setCurrentLookUserStatus] = useState<1 | 2 | 3 | 4>(1)
let userInfo = JSON.parse(storage.getItem('user') as string)
let allUserListArr = [] as any;
@ -269,8 +272,37 @@ const Meeting: React.FC = () => {
}, [])
useEffect(() => {
}, [currentVideoId])
if (recorder) {
const setting = JSON.parse(storage.getItem('setting') as string)
recorder.start();
recorder.ondataavailable = (event: any) => {
const blob = new Blob([event.data], {
type: 'video/mp4',
});
const reader = new FileReader() as any;
reader.onload = async () => {
const buffer = Buffer.from(reader.result);
await fs.writeFile(`${setting.recordingFilesPath}${+new Date()}.mp4`, buffer, {});
message.success(`录制成功!文件已保存至:${setting.recordingFilesPath}`)
try {
await fs.access(setting.recordingFilesPath, fs.constants.F_OK);
if (process.platform === 'win32') {
exec(`explorer "${setting.recordingFilesPath}"`);
} else if (process.platform === 'darwin') {
exec(`open "${setting.recordingFilesPath}"`);
}
} catch (error: any) {
if (error.code === 'ENOENT') {
message.error('文件夹不存在!')
} else {
message.error(error)
}
}
};
reader.readAsArrayBuffer(blob);
}
};
}, [recorder])
// 网络
const handleNetworkChange = (): void => {
@ -427,13 +459,36 @@ const Meeting: React.FC = () => {
invitingPersonnelRef.current.changeInvitingPersonnelModal()
break;
case '录制':
window.electron.getSources().then((sources: any) => {
const screenId = sources[0].id;
navigator.mediaDevices.getUserMedia({
audio: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: screenId,
}
} as any,
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: screenId,
}
} as any
}).then(async (steam) => {
const audioTracks = await navigator.mediaDevices
.getUserMedia({ audio: true, video: false })
.then(audioStream => audioStream.getAudioTracks()[0]);
steam.addTrack(audioTracks);
setMediaStream(steam)
setRecorder(new MediaRecorder(steam))
})
})
const setting = await JSON.parse(storage.getItem('setting') as string)
try {
await fs.access(setting.recordingFilesPath, fs.constants.F_OK)
footerListTemplate[itemIndex][rowIndex].title = '录制中'
footerListTemplate[itemIndex][rowIndex].active = true
setFooterList(footerListTemplate)
// setting.recordingFilesPath
} catch (error: any) {
if (error.code === 'ENOENT') {
message.error('文件夹不存在!')
@ -447,14 +502,29 @@ const Meeting: React.FC = () => {
footerListTemplate[itemIndex][rowIndex].title = '录制'
footerListTemplate[itemIndex][rowIndex].active = false
setFooterList(footerListTemplate)
stopRecorderMedia()
break;
case '共享文件':
sharedFilesModelRef.current.getData()
break;
}
}
// 停止录制
const stopRecorderMedia = async (): Promise<void> => {
if (recorder) {
await recorder.stop();
}
if (mediaStream) {
await mediaStream.getTracks().forEach((track: any) => {
track.stop()
});
}
setRecorder('')
setMediaStream('')
}
// 退出房间
const leaveChannel = async (bool?: boolean): Promise<void> => {
await stopRecorderMedia()
if (!bool) {
await onInvoke('levelChannel', {
roomNum: state.channelId
@ -709,7 +779,7 @@ const Meeting: React.FC = () => {
)
}
)}
{currentLookUserStatus === 1 ?
{currentLookUserStatus === 1 && currentLookUserAccount ?
<div className={`${styles.meetingContentSwiperCard} ${setMeetingContentSwiperCardClass('1')}`} onClick={() => setCurrentVideoId('1')}>
<div className={`${styles.meetingContentSwiperCardVdeio}`} id='video-source-camera-primary'>
{<div className={styles.meetingContentSwiperCardVdeioLoading}>
@ -719,7 +789,7 @@ const Meeting: React.FC = () => {
{meetingContentUser(currentLookUserAccount)}
{currentLookUserAccount.enableCamera ? null : meetingContentError(currentVideoId, currentLookUserAccount)}
</div> : null}
{currentLookUserStatus === 2 ?
{currentLookUserStatus === 2 && currentLookUserAccount ?
<div className={`${styles.meetingContentSwiperCard} ${setMeetingContentSwiperCardClass('2')}`} onClick={() => setCurrentVideoId('2')}>
<div className={`${styles.meetingContentSwiperCardVdeio}`} id='video-source-screen'>
<div className={styles.meetingContentSwiperCardVdeioLoading}>
@ -729,7 +799,7 @@ const Meeting: React.FC = () => {
{meetingContentUser(currentLookUserAccount)}
{currentLookUserAccount.enableCamera ? null : meetingContentError(currentVideoId, currentLookUserAccount)}
</div> : null}
{currentLookUserStatus === 3 ?
{currentLookUserStatus === 3 && currentLookUserAccount ?
<div className={`${styles.meetingContentSwiperCard} ${setMeetingContentSwiperCardClass('3')}`} onClick={() => setCurrentVideoId('3')}>
<div className={`${styles.meetingContentSwiperCardVdeio}`} id='video-source-remote-camera'>
<div className={styles.meetingContentSwiperCardVdeioLoading}>
@ -739,7 +809,7 @@ const Meeting: React.FC = () => {
{meetingContentUser(currentLookUserAccount)}
{currentLookUserAccount.enableCamera ? null : meetingContentError(currentVideoId, currentLookUserAccount)}
</div> : null}
{currentLookUserStatus === 4 ?
{currentLookUserStatus === 4 && currentLookUserAccount ?
<div className={`${styles.meetingContentSwiperCard} ${setMeetingContentSwiperCardClass('4')}`} onClick={() => setCurrentVideoId('4')}>
<div className={`${styles.meetingContentSwiperCardVdeio}`} id='video-source-remote-screen'>
<div className={styles.meetingContentSwiperCardVdeioLoading}>

1
src/render.d.ts vendored
View File

@ -10,6 +10,7 @@ export interface IElectronAPI {
onDownload: (data: string) => void
selectFilePath: () => void
onFilePath: (callBack: Function) => void;
getSources: () => any;
}
declare global {