录制优化
This commit is contained in:
parent
998f7f781a
commit
0c5365ccb1
8
main.js
8
main.js
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@ window.electron = {
|
|||
getIsMaximized: () => {
|
||||
return ipcRenderer.invoke('getIsMaximized')
|
||||
},
|
||||
// 获取共享屏幕列表
|
||||
getSources: () => {
|
||||
return ipcRenderer.invoke('getSources')
|
||||
},
|
||||
// 复制文字
|
||||
setWriteText: (text) => {
|
||||
return ipcRenderer.invoke('setWriteText', text)
|
||||
|
|
|
|||
|
|
@ -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}>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ export interface IElectronAPI {
|
|||
onDownload: (data: string) => void
|
||||
selectFilePath: () => void
|
||||
onFilePath: (callBack: Function) => void;
|
||||
getSources: () => any;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
|
|
|||
Loading…
Reference in New Issue