Compare commits

...

21 Commits

Author SHA1 Message Date
yangqiang ae8ba8cbe2 Merge pull request 'yangjie' (#41) from yangjie into master
Reviewed-on: #41
2024-12-11 14:52:08 +08:00
yj 7aa8bb4a8f 共享时录屏按钮不显示 2024-12-11 14:41:30 +08:00
yj bafa7e3a70 设置黑名单 2024-12-10 18:00:10 +08:00
yj 4f462e818b 停止接取共享流 2024-12-10 17:36:19 +08:00
yj c99ec3668c 优化 2024-12-10 17:15:32 +08:00
yj 9a5ef75322 样式&全员退出分享人优化 2024-12-10 17:04:28 +08:00
yj 0781ebc3af 更换定时器方法 2024-12-10 15:04:54 +08:00
yj cb1593a76f 优化 2024-12-10 14:49:43 +08:00
yj 79b1d8c5c2 添加参数 2024-12-10 13:47:45 +08:00
yj 0b57e83e29 修改延迟参数 2024-12-10 13:45:29 +08:00
yj cc2759b242 Merge branch 'master' of https://gitea.23544.com/marking/WGShare.Client.Electron into yangjie 2024-12-10 09:52:58 +08:00
yj b314a2c25f 恢复会议监控尺寸 2024-12-09 14:50:15 +08:00
yj 5fb95d6a15 优化 2024-12-09 13:58:11 +08:00
yj 4fbcf0e319 添加错误日志 2024-12-09 11:51:41 +08:00
yj a20c833de0 统一日志名字 2024-12-09 11:20:47 +08:00
yj 234665d08d 日志 2024-12-09 11:09:27 +08:00
yj 4528696ec7 日志显示 2024-12-09 11:01:13 +08:00
yj 9619230cf8 优化 2024-12-06 18:00:24 +08:00
yj 844547d853 优化 2024-12-06 17:26:02 +08:00
yj b267f8bf4c Merge branch 'yangjie' of https://gitea.23544.com/marking/WGShare.Client.Electron into yangjie 2024-12-06 17:25:44 +08:00
yj a29784bb26 防止出现多个任务 2024-12-06 17:25:28 +08:00
10 changed files with 115 additions and 41 deletions

11
main.js
View File

@ -8,6 +8,7 @@ const {
ipcMain, ipcMain,
clipboard, clipboard,
dialog, dialog,
crashReporter,
desktopCapturer, desktopCapturer,
} = require('electron'); } = require('electron');
const path = require('node:path') const path = require('node:path')
@ -16,6 +17,7 @@ const fs = require('fs');
const Registry = require('winreg'); const Registry = require('winreg');
const { autoUpdater, CancellationToken } = require('electron-updater'); const { autoUpdater, CancellationToken } = require('electron-updater');
const signalR = require('@microsoft/signalr'); const signalR = require('@microsoft/signalr');
const { setTimeout, setInterval, clearTimeout, clearInterval } = require('timers');
const cancellationToken = new CancellationToken() const cancellationToken = new CancellationToken()
app.allowRendererProcessReuse = false; app.allowRendererProcessReuse = false;
let mainWindow = null; let mainWindow = null;
@ -25,6 +27,7 @@ let env;
let regKey; let regKey;
let connection = null; let connection = null;
let envStr; let envStr;
let startNumber = 0;
class AppWindow extends BrowserWindow { class AppWindow extends BrowserWindow {
constructor(config) { constructor(config) {
@ -99,6 +102,11 @@ const additionalData = { myKey: 'myValue' }
app.on('ready', () => { app.on('ready', () => {
const gotTheLock = app.requestSingleInstanceLock(additionalData) const gotTheLock = app.requestSingleInstanceLock(additionalData)
if (gotTheLock) { if (gotTheLock) {
app.getPath('crashDumps')
crashReporter.start({
uploadToServer: false,
ignoreSystemCrashHandler: false
})
env = process.argv.find((arg) => arg.startsWith('--env='))?.split('=')[1]; env = process.argv.find((arg) => arg.startsWith('--env='))?.split('=')[1];
if (env === 'development') { if (env === 'development') {
Object.defineProperty(app, 'isPackaged', { Object.defineProperty(app, 'isPackaged', {
@ -141,11 +149,14 @@ app.on('ready', () => {
}); });
ipcMain.handle('setEnv', (event, str) => { ipcMain.handle('setEnv', (event, str) => {
envStr = str; envStr = str;
if (startNumber === 0) {
updateHandle() // 检查更新 updateHandle() // 检查更新
setInterval(() => { setInterval(() => {
updateHandle() // 每一小时检查更新 updateHandle() // 每一小时检查更新
}, 1000 * 60 * 60) }, 1000 * 60 * 60)
createTray() createTray()
startNumber++
}
}); });
// socket // socket
ipcMain.handle('startSignalr', (event, user) => { ipcMain.handle('startSignalr', (event, user) => {

View File

@ -9,6 +9,7 @@ import Avatar from '@/components/Avatar';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { agora } from '@/utils/package/agora'; import { agora } from '@/utils/package/agora';
import { role } from '@/config/role'; import { role } from '@/config/role';
const { setInterval, clearInterval } = require('timers');
let time = null as any; let time = null as any;
const JoinSetting = forwardRef((_props: any, ref: any) => { const JoinSetting = forwardRef((_props: any, ref: any) => {
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({

View File

@ -65,8 +65,8 @@
height: 0; height: 0;
.userVideoContentListItem { .userVideoContentListItem {
height: 32%; height: 13%;
width: calc(100% / 4 - 8px); width: calc(100% / 3 - 8px);
padding: 4px; padding: 4px;
.userVideoContentListItemVideo { .userVideoContentListItemVideo {

View File

@ -6,6 +6,7 @@ import { Button, Empty, Select, message } from 'antd';
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useLocation } from 'react-router'; import { useLocation } from 'react-router';
import { VideoStreamType } from 'agora-electron-sdk'; import { VideoStreamType } from 'agora-electron-sdk';
const { setInterval, clearInterval } = require('timers');
const UserVideo: React.FC = () => { const UserVideo: React.FC = () => {
const { state } = useLocation(); const { state } = useLocation();
const [from, setFrom] = useState<any>({ const [from, setFrom] = useState<any>({

View File

@ -13,6 +13,7 @@ import { role } from '@/config/role';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import StupWizard from '@/components/StupWizard'; import StupWizard from '@/components/StupWizard';
import { GetSubDpList } from '@/api/Home/User'; import { GetSubDpList } from '@/api/Home/User';
const { setInterval, clearInterval } = require('timers');
const fs = require('fs').promises; const fs = require('fs').promises;
const { exec } = require('child_process'); const { exec } = require('child_process');
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;

View File

@ -8,6 +8,7 @@ import { storage } from '@/utils';
import ImageUrl from '@/utils/package/imageUrl' import ImageUrl from '@/utils/package/imageUrl'
import Avatar from '@/components/Avatar'; import Avatar from '@/components/Avatar';
import StupWizard from '@/components/StupWizard'; import StupWizard from '@/components/StupWizard';
const { setInterval, clearInterval } = require('timers');
dayjs.locale('zh-cn'); dayjs.locale('zh-cn');
type navListType = { type navListType = {
title: string; title: string;

View File

@ -7,6 +7,7 @@ import { CaretDownOutlined, CaretUpOutlined } from '@ant-design/icons';
import { Button } from 'antd'; import { Button } from 'antd';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
const { setInterval, clearInterval } = require('timers');
const ShareScreenWindow: React.FC = () => { const ShareScreenWindow: React.FC = () => {
const [footerLists, setFooterLists] = useState<any>([ const [footerLists, setFooterLists] = useState<any>([
{ {
@ -54,6 +55,11 @@ const ShareScreenWindow: React.FC = () => {
let timeout: NodeJS.Timeout; let timeout: NodeJS.Timeout;
useEffect(() => { useEffect(() => {
getRoomUser() getRoomUser()
if (!role.ID.includes(userInfo.roleId)) {
setFooterLists((res: any) => {
return res.splice(4, 1)
})
}
channel.onmessage = function (event) { channel.onmessage = function (event) {
let { type, time } = event.data; let { type, time } = event.data;
switch (type) { switch (type) {
@ -82,8 +88,10 @@ const ShareScreenWindow: React.FC = () => {
footerListTemplate[0].active = data.parmes.footerList[0][0].active; footerListTemplate[0].active = data.parmes.footerList[0][0].active;
footerListTemplate[1].title = data.parmes.footerList[0][1].active ? '开启视频' : '关闭视频'; footerListTemplate[1].title = data.parmes.footerList[0][1].active ? '开启视频' : '关闭视频';
footerListTemplate[1].active = data.parmes.footerList[0][1].active; footerListTemplate[1].active = data.parmes.footerList[0][1].active;
if (role.ID.includes(userInfo.roleId)) {
footerListTemplate[4].title = data.parmes.footerList[1][3].active ? '录制中' : '录制'; footerListTemplate[4].title = data.parmes.footerList[1][3].active ? '录制中' : '录制';
footerListTemplate[4].active = data.parmes.footerList[1][3].active; footerListTemplate[4].active = data.parmes.footerList[1][3].active;
}
setFooterLists(footerListTemplate) setFooterLists(footerListTemplate)
break; break;
case 'roomUserList': case 'roomUserList':

View File

@ -24,6 +24,7 @@ import { fixWebmDuration } from "webm-duration-fix-buffer";
import { getKeyOpenChildWindow, setKeyOpenChildWindow } from '@/utils/package/public'; import { getKeyOpenChildWindow, setKeyOpenChildWindow } from '@/utils/package/public';
import MeetingDisconnected from '@/components/MeetingDisconnected'; import MeetingDisconnected from '@/components/MeetingDisconnected';
import SingIn from '@/components/SingIn'; import SingIn from '@/components/SingIn';
const { setTimeout, setInterval, clearTimeout, clearInterval } = require('timers');
const { confirm } = Modal; const { confirm } = Modal;
const { exec } = require('child_process'); const { exec } = require('child_process');
const fs = require('fs').promises; const fs = require('fs').promises;
@ -435,6 +436,7 @@ const Meeting: React.FC = () => {
if (!data) { if (!data) {
setIsScreenCapture(bool => { setIsScreenCapture(bool => {
if (!bool) { if (!bool) {
if (role.ID.includes(userInfo.roleId)) {
confirm({ confirm({
title: '提示', title: '提示',
icon: <ExclamationCircleFilled />, icon: <ExclamationCircleFilled />,
@ -456,6 +458,9 @@ const Meeting: React.FC = () => {
showSingIn() showSingIn()
} }
}) })
} else {
showSingIn()
}
} }
return bool return bool
}) })
@ -914,12 +919,18 @@ const Meeting: React.FC = () => {
observer?.unobserve(element); observer?.unobserve(element);
}); });
const observerObject = new IntersectionObserver(async (entries: IntersectionObserverEntry[], _observer: IntersectionObserver) => { const observerObject = new IntersectionObserver(async (entries: IntersectionObserverEntry[], _observer: IntersectionObserver) => {
setIsScreenCapture((bool: boolean) => {
entries.forEach(async (entry) => { entries.forEach(async (entry) => {
if (entry.target.id !== user.uid) { if (entry.target.id !== user.uid) {
await agora.muteRemoteVideoStreamEx(Number(entry.target.id), !entry.isIntersecting) await agora.muteRemoteVideoStreamEx(Number(entry.target.id), bool ? true : !entry.isIntersecting)
} }
}); });
await agora.muteRemoteVideoStreamEx(Number(currentVideoId), false) return bool
})
setIsScreenCapture((bool: boolean) => {
agora.muteRemoteVideoStreamEx(Number(currentVideoId), bool)
return bool
})
}, { threshold: 0, root: document.getElementById('videoView') }); }, { threshold: 0, root: document.getElementById('videoView') });
setObserver(observerObject) setObserver(observerObject)
elements.forEach(element => { elements.forEach(element => {
@ -1611,11 +1622,11 @@ const Meeting: React.FC = () => {
} }
// 退出房间 // 退出房间
const leaveChannel = async (bool: boolean = true): Promise<void> => { const leaveChannel = async (bool: boolean = true): Promise<void> => {
await stopScreenCapture()
await stopRecorderMedia() await stopRecorderMedia()
if (bool) { if (bool) {
await getLeave() await getLeave()
} }
await stopScreenCapture()
await agora.leaveChannel() await agora.leaveChannel()
setTimeout(() => { setTimeout(() => {
if (userInfo.isAnonymous) { if (userInfo.isAnonymous) {
@ -1623,11 +1634,20 @@ const Meeting: React.FC = () => {
} else { } else {
navigate('/home/index') navigate('/home/index')
} }
}, 0) }, 1000)
} }
// 分享屏幕 // 分享屏幕
const clickSharedScreen = async (): Promise<void> => { const clickSharedScreen = async (): Promise<void> => {
let data = sharedScreenList.find((item: any) => item.sourceId === sharedScreenItem.sourceId) let data = sharedScreenList.find((item: any) => item.sourceId === sharedScreenItem.sourceId)
const elements = document.querySelectorAll('.intersectionObserver-view');
if (elements.length) {
elements.forEach(item => {
if (item.id !== userInfo.uid) {
agora.muteRemoteVideoStreamEx(Number(item.id), true)
}
});
agora.setSubscribeVideoBlocklist([Number(user.screenShareId)], 1)
}
if (data) { if (data) {
const footerListTemplate = [...footerList] const footerListTemplate = [...footerList]
footerListTemplate[footerListIndex.itemIndex][footerListIndex.rowIndex].title = '停止共享' footerListTemplate[footerListIndex.itemIndex][footerListIndex.rowIndex].title = '停止共享'
@ -2667,7 +2687,7 @@ const Meeting: React.FC = () => {
</div> </div>
</div> </div>
: :
<div className={styles.meetingUserVideoList} style={{ width: statusList.userVideo ? '100vw' : '500px' }}> <div className={styles.meetingUserVideoList} style={{ width: statusList.userVideo ? '500px' : '500px' }}>
<div className={styles.meetingUserVideoListTitle}> <div className={styles.meetingUserVideoListTitle}>
<span></span> <span></span>
<img src={ImageUrl.icon18} alt="" onClick={() => { <img src={ImageUrl.icon18} alt="" onClick={() => {
@ -2837,6 +2857,21 @@ const Meeting: React.FC = () => {
</label> </label>
<span>{row.title}</span> <span>{row.title}</span>
</div> </div>
case '录制':
case '录制中':
if (role.ID.includes(user.roleId)) {
return <div
className='drag'
onClick={() => changeStatusList(row, itemIndex, rowIndex)}
onMouseDown={() => changeFooterListSelect(row, itemIndex, rowIndex, true)}
onMouseUp={() => changeFooterListSelect(row, itemIndex, rowIndex, false)}
onMouseLeave={() => changeFooterListSelect(row, itemIndex, rowIndex, false)}
key={rowIndex}>
{row.select ? <img src={row.iconSelect} alt="" /> : <img src={row.active ? row.iconActive : row.icon} alt="" />}
<span>{row.title}</span>
</div>
}
return null
default: default:
return <div return <div
className='drag' className='drag'

View File

@ -21,12 +21,14 @@ import {
BeautyOptions, BeautyOptions,
ColorEnhanceOptions, ColorEnhanceOptions,
LowlightEnhanceOptions, LowlightEnhanceOptions,
VirtualBackgroundSource VirtualBackgroundSource,
AudienceLatencyLevelType
} from "agora-electron-sdk"; } from "agora-electron-sdk";
import { GetRoomRtcToken, GetAgoraConf } from "@/api/Home/Index"; import { GetRoomRtcToken, GetAgoraConf } from "@/api/Home/Index";
import { storage } from '@/utils'; import { storage } from '@/utils';
import { role } from "@/config/role"; import { role } from "@/config/role";
import path from "path"; import path from "path";
const os = require("os");
const option: any = { const option: any = {
appId: '', appId: '',
token: '', token: '',
@ -44,6 +46,7 @@ export const agora = {
rtcEngine = createAgoraRtcEngine(); rtcEngine = createAgoraRtcEngine();
await rtcEngine.initialize({ await rtcEngine.initialize({
appId: data, appId: data,
logConfig: { filePath: path.resolve(os.homedir(), "./agorasdk.log") }
}); });
if (bool) { if (bool) {
await agora.setDeviceManager() await agora.setDeviceManager()
@ -213,6 +216,9 @@ export const agora = {
// 本地加入 // 本地加入
setupLocalVideo: async (item: any) => { setupLocalVideo: async (item: any) => {
if (item.view?.childNodes.length === 1 || item.type) { if (item.view?.childNodes.length === 1 || item.type) {
if (String(item.uid).length === 9) {
return
}
await rtcEngine.setupLocalVideo({ await rtcEngine.setupLocalVideo({
renderMode: agora.getRrenderMode(item.uid), renderMode: agora.getRrenderMode(item.uid),
sourceType: item.sourceType, sourceType: item.sourceType,
@ -302,6 +308,7 @@ export const agora = {
publishMicrophoneTrack: true,//设置是否发布麦克风采集到的音频 publishMicrophoneTrack: true,//设置是否发布麦克风采集到的音频
publishCameraTrack: true,//设置是否发布摄像头采集的视频 publishCameraTrack: true,//设置是否发布摄像头采集的视频
publishScreenTrack: false,//设置是否发布屏幕采集的视频 publishScreenTrack: false,//设置是否发布屏幕采集的视频
audienceLatencyLevel: bool ? AudienceLatencyLevelType.AudienceLatencyLevelUltraLowLatency : AudienceLatencyLevelType.AudienceLatencyLevelLowLatency,
}) })
}, },
// 设置接收大小流 // 设置接收大小流
@ -342,6 +349,7 @@ export const agora = {
publishMicrophoneTrack: false,//设置是否发布麦克风采集到的音频 publishMicrophoneTrack: false,//设置是否发布麦克风采集到的音频
publishCameraTrack: true,//设置是否发布摄像头采集的视频 publishCameraTrack: true,//设置是否发布摄像头采集的视频
publishScreenTrack: false,//设置是否发布屏幕采集的视频 publishScreenTrack: false,//设置是否发布屏幕采集的视频
audienceLatencyLevel: bool ? AudienceLatencyLevelType.AudienceLatencyLevelLowLatency : AudienceLatencyLevelType.AudienceLatencyLevelUltraLowLatency,
} }
); );
await rtcEngine.setDualStreamModeEx( await rtcEngine.setDualStreamModeEx(
@ -365,6 +373,10 @@ export const agora = {
muteRemoteVideoStreamEx: async (uid: number, mute: boolean) => { muteRemoteVideoStreamEx: async (uid: number, mute: boolean) => {
await rtcEngine.muteRemoteVideoStreamEx(uid, mute, { channelId: option.channelId, localUid: Number(option.uid) }) await rtcEngine.muteRemoteVideoStreamEx(uid, mute, { channelId: option.channelId, localUid: Number(option.uid) })
}, },
// 设置视频订阅黑名单。
setSubscribeVideoBlocklist: async (uidList: number[], uidNumber: number) => {
await rtcEngine.setSubscribeVideoBlocklist(uidList, uidNumber)
},
// 取消或恢复订阅指定远端用户的音频流 // 取消或恢复订阅指定远端用户的音频流
muteRemoteVideoStream: async (uid: number, mute: boolean) => { muteRemoteVideoStream: async (uid: number, mute: boolean) => {
rtcEngine.muteRemoteVideoStream(uid, mute) rtcEngine.muteRemoteVideoStream(uid, mute)
@ -396,6 +408,7 @@ export const agora = {
publishMicrophoneTrack: publishMicrophoneTrack,//设置是否发布麦克风采集到的音频 publishMicrophoneTrack: publishMicrophoneTrack,//设置是否发布麦克风采集到的音频
publishCameraTrack: publishCameraTrack,//设置是否发布摄像头采集的视频 publishCameraTrack: publishCameraTrack,//设置是否发布摄像头采集的视频
publishScreenTrack: false,//设置是否发布屏幕采集的视频 publishScreenTrack: false,//设置是否发布屏幕采集的视频
audienceLatencyLevel: data ? AudienceLatencyLevelType.AudienceLatencyLevelUltraLowLatency : AudienceLatencyLevelType.AudienceLatencyLevelLowLatency,
}) })
}, },
// 取消或恢复发布本地视频流 // 取消或恢复发布本地视频流
@ -408,6 +421,7 @@ export const agora = {
publishMicrophoneTrack: publishMicrophoneTrack,//设置是否发布麦克风采集到的音频 publishMicrophoneTrack: publishMicrophoneTrack,//设置是否发布麦克风采集到的音频
publishCameraTrack: publishCameraTrack,//设置是否发布摄像头采集的视频 publishCameraTrack: publishCameraTrack,//设置是否发布摄像头采集的视频
publishScreenTrack: false,//设置是否发布屏幕采集的视频 publishScreenTrack: false,//设置是否发布屏幕采集的视频
audienceLatencyLevel: data ? AudienceLatencyLevelType.AudienceLatencyLevelUltraLowLatency : AudienceLatencyLevelType.AudienceLatencyLevelLowLatency,
}) })
}, },
// 摄像头采集 // 摄像头采集

View File

@ -76,7 +76,8 @@ export default defineConfig({
BeautyOptions, BeautyOptions,
ColorEnhanceOptions, ColorEnhanceOptions,
LowlightEnhanceOptions, LowlightEnhanceOptions,
VirtualBackgroundSource VirtualBackgroundSource,
AudienceLatencyLevelType
} = require("agora-electron-sdk") } = require("agora-electron-sdk")
export { export {
createAgoraRtcEngine, createAgoraRtcEngine,
@ -101,7 +102,8 @@ export default defineConfig({
BeautyOptions, BeautyOptions,
ColorEnhanceOptions, ColorEnhanceOptions,
LowlightEnhanceOptions, LowlightEnhanceOptions,
VirtualBackgroundSource VirtualBackgroundSource,
AudienceLatencyLevelType
} }
`, `,
}) })