1006 lines
52 KiB
TypeScript
1006 lines
52 KiB
TypeScript
import styles from '@/components/StupWizard/index.module.scss'
|
||
import ImageUrl from '@/utils/package/imageUrl';
|
||
import { Button, Checkbox, Empty, Input, message, Modal, Popover, Radio, Select, Slider, Space } from 'antd';
|
||
import { forwardRef, useEffect, useImperativeHandle, useState, memo } from "react";
|
||
import { agora } from '@/utils/package/agora'
|
||
import { CloseOutlined, LoadingOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
||
import { storage } from '@/utils';
|
||
import path from 'path';
|
||
import { role } from '@/config/role';
|
||
import { storageSeeting } from '@/utils/package/public';
|
||
let meetingUserInfo = '' as any;
|
||
const fs = require('fs').promises;
|
||
const { exec } = require('child_process');
|
||
let c = +new Date();
|
||
const StupWizard = forwardRef((_props: any, ref: any) => {
|
||
useImperativeHandle(ref, () => ({
|
||
changeModal: (index: number = 0, data: any) => {
|
||
meetingUserInfo = data;
|
||
setIsStupWizard(true)
|
||
if (location.hash.indexOf('/meeting') === -1) {
|
||
agora.init()
|
||
}
|
||
setList((res: any) => {
|
||
res.forEach((item: any) => {
|
||
item.active = false
|
||
});
|
||
res[index].active = true;
|
||
return res
|
||
})
|
||
const setting = JSON.parse(storage.getItem('setting') as string)
|
||
for (const key in storageSeeting) {
|
||
if (setting[key] === undefined) {
|
||
setting[key] = storageSeeting[key]
|
||
}
|
||
}
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
},
|
||
getStupWizardModal: () => {
|
||
return isStupWizard
|
||
}
|
||
}))
|
||
const [list, setList] = useState([
|
||
{
|
||
title: '通用',
|
||
icon: ImageUrl.icon45,
|
||
iconActive: ImageUrl.icon45Active,
|
||
active: true,
|
||
},
|
||
{
|
||
title: '视频',
|
||
icon: ImageUrl.icon39,
|
||
iconActive: ImageUrl.icon39Active,
|
||
active: false,
|
||
},
|
||
{
|
||
title: '音频',
|
||
icon: ImageUrl.icon40,
|
||
iconActive: ImageUrl.icon40Active,
|
||
active: false,
|
||
},
|
||
{
|
||
title: '录制',
|
||
icon: ImageUrl.icon41,
|
||
iconActive: ImageUrl.icon41Active,
|
||
active: false,
|
||
},
|
||
{
|
||
title: '文件下载',
|
||
icon: ImageUrl.icon42,
|
||
iconActive: ImageUrl.icon42Active,
|
||
active: false,
|
||
}
|
||
])
|
||
const [isStupWizard, setIsStupWizard] = useState(false);
|
||
return (
|
||
<>
|
||
<Modal
|
||
title=""
|
||
open={isStupWizard}
|
||
footer={null}
|
||
closable={false}
|
||
destroyOnClose={true}
|
||
centered
|
||
width={'70vw'}
|
||
className='modal-padding'>
|
||
<div className={styles.stupWizard}>
|
||
<div className={styles.stupWizardLeft}>
|
||
{list.map((row: any, index: number) => {
|
||
return (
|
||
<div key={index} className={`${row.active ? styles.active : ''}`} onClick={async () => {
|
||
const userInfo = JSON.parse(storage.getItem('user') as string)
|
||
await agora.destroyRendererByConfigPreview(Number(userInfo.screenShareId), c)
|
||
const newList = [...list];
|
||
newList.forEach(item => item.active = false);
|
||
newList[index].active = true;
|
||
setList(newList)
|
||
agora.stopPlaybackDeviceTest()
|
||
agora.stopRecordingDeviceTest()
|
||
}}>
|
||
<img src={row.active ? row.iconActive : row.icon} alt="" />
|
||
<span>{row.title}</span>
|
||
</div>
|
||
)
|
||
})}
|
||
</div>
|
||
<div className={styles.stupWizardRight}>
|
||
<CloseOutlined style={{
|
||
position: 'absolute',
|
||
color: 'white',
|
||
right: '20px',
|
||
top: '16px',
|
||
cursor: 'pointer'
|
||
}}
|
||
onClick={async () => {
|
||
const userInfo = JSON.parse(storage.getItem('user') as string)
|
||
if (location.hash.indexOf('/meeting') === -1) {
|
||
agora.release()
|
||
}
|
||
await agora.destroyRendererByConfigPreview(Number(userInfo.screenShareId), c)
|
||
setIsStupWizard(false)
|
||
}}
|
||
/>
|
||
{list[0].active ? <CurrencyComponents /> : null}
|
||
{list[1].active ? <VideoComponents /> : null}
|
||
{list[2].active ? <AudioComponents /> : null}
|
||
{list[3].active ? <RecordingComponents /> : null}
|
||
{list[4].active ? <FileComponents /> : null}
|
||
</div>
|
||
</div>
|
||
</Modal>
|
||
</>
|
||
)
|
||
})
|
||
const CurrencyComponents = () => {
|
||
const [optionsValue, setOperationValue] = useState<'hide' | 'quit'>('hide');
|
||
const [voiceStimulation, setVoiceStimulation] = useState(true);
|
||
const setting = JSON.parse(storage.getItem('setting') as string)
|
||
useEffect(() => {
|
||
setOperationValue(setting.closeSetting)
|
||
setVoiceStimulation(setting.voiceStimulation)
|
||
}, []);
|
||
return (
|
||
<>
|
||
<div>
|
||
<span>通用</span>
|
||
<div className={styles.currencyComponents}>
|
||
<div>
|
||
<div>
|
||
<span>关闭按钮设置</span>
|
||
<Radio.Group onChange={(e: any) => {
|
||
setting.closeSetting = e.target.value;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setOperationValue(e.target.value)
|
||
}} style={{ flexShrink: 0, margin: '10px 0' }} value={optionsValue}>
|
||
<Radio value={'quit'}>退出主程序</Radio>
|
||
<Radio value={'hide'}>不退出程序,最小化到托盘</Radio>
|
||
</Radio.Group>
|
||
</div>
|
||
<div>
|
||
<span>语音激励 <Popover
|
||
content={
|
||
<span
|
||
style={{
|
||
color: 'white'
|
||
}}>
|
||
开启语音激励后,会优先显示正在说话的与会成员。
|
||
</span>
|
||
}
|
||
title=""
|
||
>
|
||
<QuestionCircleOutlined style={{
|
||
color: 'white',
|
||
cursor: 'pointer',
|
||
marginRight: '10px'
|
||
}} />
|
||
</Popover></span>
|
||
<Radio.Group onChange={(e: any) => {
|
||
setting.voiceStimulation = e.target.value;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setVoiceStimulation(e.target.value)
|
||
}} style={{ flexShrink: 0, margin: '10px 0' }} value={voiceStimulation}>
|
||
<Radio value={true}>开启</Radio>
|
||
<Radio value={false}>关闭</Radio>
|
||
</Radio.Group>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</>
|
||
)
|
||
}
|
||
const VideoComponents = () => {
|
||
const [videoDeviceManager, setVideoDeviceManager] = useState<any>({
|
||
list: [],
|
||
item: null,
|
||
});
|
||
const [videoKey, setVideoKey] = useState('beautyEffect');
|
||
const [virtualBackgroundImg] = useState([
|
||
ImageUrl.virtualBackground1,
|
||
ImageUrl.virtualBackground2,
|
||
ImageUrl.virtualBackground3,
|
||
ImageUrl.virtualBackground4,
|
||
ImageUrl.virtualBackground5,
|
||
ImageUrl.virtualBackground6,
|
||
]);
|
||
const [beautyEffect, setBeautyEffect] = useState<any>({
|
||
isBeautyEffect: false, //是否打开美颜
|
||
lighteningContrastLevel: 1, //对比度
|
||
lighteningLevel: 0, //美白程度
|
||
smoothnessLevel: 0, //磨皮程度
|
||
rednessLevel: 0, //红润度
|
||
sharpnessLevel: 0, //锐化程度
|
||
});
|
||
const [colorEnhancement, setColorEnhancement] = useState<any>({
|
||
isColorEnhancement: false, //是否打开色彩增强
|
||
strengthLevel: 0.5, //色彩增强程度
|
||
skinProtectLevel: 1, //肤色保护程度
|
||
});
|
||
const [darkLightEnhancement, setDarkLightEnhancement] = useState<any>({
|
||
isDarkLightEnhancement: false, //是否打开暗光增强
|
||
level: 0, //暗光增强等级
|
||
mode: 0, //暗光增强模式
|
||
});
|
||
const [virtualBackground, setVirtualBackground] = useState<any>({
|
||
isVirtualBackground: false, //是否打开虚拟背景
|
||
color: '0xFFFFFF', // 纯色
|
||
sourceIndex: '', // 背景图下标
|
||
});
|
||
const setting = JSON.parse(storage.getItem('setting') as string)
|
||
useEffect(() => {
|
||
setBeautyEffect(setting.beautyEffect)
|
||
setColorEnhancement(setting.colorEnhancement)
|
||
setDarkLightEnhancement(setting.darkLightEnhancement)
|
||
setVirtualBackground(setting.virtualBackground)
|
||
getVideoDeviceList()
|
||
}, []);
|
||
useEffect(() => {
|
||
agora.setBeautyEffectOptions(beautyEffect.isBeautyEffect, beautyEffect)
|
||
}, [beautyEffect]);
|
||
useEffect(() => {
|
||
agora.setColorEnhanceOptions(colorEnhancement.isColorEnhancement, colorEnhancement)
|
||
}, [colorEnhancement]);
|
||
useEffect(() => {
|
||
agora.setLowlightEnhanceOptions(darkLightEnhancement.isDarkLightEnhancement, darkLightEnhancement)
|
||
}, [darkLightEnhancement]);
|
||
useEffect(() => {
|
||
if (typeof virtualBackground.sourceIndex === 'number') {
|
||
if (storage.getItem('env') === 'development') {
|
||
window.electron.getAppPath().then((res: string) => {
|
||
const imagePath = path.join(res, 'src', 'assets', 'virtualBackground', `${virtualBackground.sourceIndex + 1}.png`);
|
||
agora.enableVirtualBackground(virtualBackground.isVirtualBackground, {
|
||
source: imagePath,
|
||
background_source_type: 2,
|
||
color: Number(virtualBackground.color),
|
||
})
|
||
})
|
||
} else {
|
||
const imagePath = path.join((process as any).resourcesPath, 'images', `${virtualBackground.sourceIndex + 1}.png`);
|
||
agora.enableVirtualBackground(virtualBackground.isVirtualBackground, {
|
||
source: imagePath,
|
||
background_source_type: 2,
|
||
color: Number(virtualBackground.color),
|
||
})
|
||
}
|
||
} else {
|
||
agora.enableVirtualBackground(virtualBackground.isVirtualBackground, {
|
||
background_source_type: 1,
|
||
color: Number(virtualBackground.color),
|
||
})
|
||
}
|
||
}, [virtualBackground]);
|
||
const getVideoDeviceList = async (): Promise<void> => {
|
||
const userInfo = JSON.parse(storage.getItem('user') as string)
|
||
agora.getVideoDeviceManager().then(async (res) => {
|
||
const { list } = res
|
||
setVideoDeviceManager({
|
||
list: list.map((row: any) => {
|
||
return {
|
||
value: row.deviceId,
|
||
label: row.deviceName
|
||
}
|
||
}),
|
||
item: setting.videoDeviceId,
|
||
})
|
||
if (setting.videoDeviceId && list.length) {
|
||
await agora.setVideoDeviceManager(setting.videoDeviceId)
|
||
await agora.startPreview('videoPreview', Number(userInfo.screenShareId), c)
|
||
}
|
||
})
|
||
}
|
||
return (
|
||
<>
|
||
<div>
|
||
<span>视频</span>
|
||
<div className={styles.videoComponents}>
|
||
{
|
||
videoDeviceManager.item ?
|
||
<div>
|
||
<div id='videoPreview'>
|
||
<LoadingOutlined style={{
|
||
position: 'absolute',
|
||
color: 'white',
|
||
right: '50%',
|
||
fontSize: '30px',
|
||
top: '50%',
|
||
}} />
|
||
</div>
|
||
</div> :
|
||
<div>
|
||
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
||
<Empty description={'未检测到摄像头'} />
|
||
</div>
|
||
</div>
|
||
}
|
||
<div>
|
||
<span>摄像头:</span>
|
||
<Popover
|
||
content={
|
||
<span
|
||
style={{
|
||
color: 'white'
|
||
}}>
|
||
解释说明:如未检测到摄像头请插拔后重试
|
||
</span>
|
||
}
|
||
title=""
|
||
>
|
||
<QuestionCircleOutlined style={{
|
||
color: 'white',
|
||
cursor: 'pointer',
|
||
marginRight: '10px'
|
||
}} />
|
||
</Popover>
|
||
<Select
|
||
placeholder={videoDeviceManager.list.length ? '请选择设备' : '未检测到摄像头'}
|
||
options={videoDeviceManager.list} style={{ flexGrow: 1, marginRight: '10px' }}
|
||
value={videoDeviceManager.item || null} onChange={async (e) => {
|
||
setVideoDeviceManager({
|
||
...videoDeviceManager,
|
||
item: e
|
||
})
|
||
agora.setVideoDeviceManager(e)
|
||
if (!setting.videoDeviceId) {
|
||
const userInfo = JSON.parse(storage.getItem('user') as string)
|
||
await agora.startPreview('videoPreview', Number(userInfo.screenShareId), c)
|
||
}
|
||
setting.videoDeviceId = e;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
}} />
|
||
|
||
</div>
|
||
<div>
|
||
<Radio.Group buttonStyle="solid" value={videoKey} onChange={(e) => {
|
||
setVideoKey(e.target.value)
|
||
}}>
|
||
<Radio.Button value="beautyEffect">虚拟背景</Radio.Button>
|
||
<Radio.Button value="colorEnhancement">美颜效果</Radio.Button>
|
||
<Radio.Button value="darkLightEnhancement">色彩增强</Radio.Button>
|
||
<Radio.Button value="virtualBackground">暗光增强</Radio.Button>
|
||
</Radio.Group>
|
||
</div>
|
||
{videoKey === 'beautyEffect' ? <div className={styles.beautyEffect}>
|
||
<span>
|
||
<Checkbox style={{ marginRight: '10px' }} checked={virtualBackground.isVirtualBackground} onChange={(e) => {
|
||
setting.virtualBackground.isVirtualBackground = e.target.checked;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setVirtualBackground({
|
||
...virtualBackground,
|
||
isVirtualBackground: e.target.checked
|
||
})
|
||
}}>
|
||
</Checkbox>
|
||
虚拟背景
|
||
</span>
|
||
<div>
|
||
<div className={virtualBackground.sourceIndex === '' ? styles.active : ''}>
|
||
<div style={{ backgroundColor: '#' + Number(virtualBackground.color)?.toString(16).padStart(6, '0') }} onClick={() => {
|
||
setting.virtualBackground.sourceIndex = '';
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setVirtualBackground({
|
||
...virtualBackground,
|
||
sourceIndex: ''
|
||
})
|
||
}}>
|
||
<input type="color" name="" id="color-select" onChange={(e) => {
|
||
let hexInt = parseInt('0x' + e.target.value.split('#')[1], 16)
|
||
setting.virtualBackground.color = hexInt
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setVirtualBackground({
|
||
...virtualBackground,
|
||
color: hexInt
|
||
})
|
||
}} />
|
||
<label htmlFor='color-select'>(纯色)更改颜色</label>
|
||
</div>
|
||
</div>
|
||
{
|
||
virtualBackgroundImg.map((item, index) => {
|
||
return (
|
||
<div key={index} className={virtualBackground.sourceIndex === index ? styles.active : ''} onClick={() => {
|
||
setting.virtualBackground.sourceIndex = index;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setVirtualBackground({
|
||
...virtualBackground,
|
||
sourceIndex: index
|
||
})
|
||
}}>
|
||
<img src={item} alt="" />
|
||
</div>
|
||
)
|
||
})
|
||
}
|
||
</div>
|
||
</div> : null}
|
||
<div className={styles.otherVideoSeeting}>
|
||
{videoKey === 'colorEnhancement' ? <div>
|
||
<span>
|
||
<Checkbox style={{ marginRight: '10px' }} checked={beautyEffect.isBeautyEffect} onChange={(e) => {
|
||
setting.beautyEffect.isBeautyEffect = e.target.checked;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setBeautyEffect({
|
||
...beautyEffect,
|
||
isBeautyEffect: e.target.checked
|
||
})
|
||
}}>
|
||
</Checkbox>
|
||
美颜效果
|
||
</span>
|
||
<div>
|
||
<div>
|
||
<span>对比度</span>
|
||
<div>
|
||
<Radio.Group disabled={!beautyEffect.isBeautyEffect} onChange={(e: any) => {
|
||
setting.beautyEffect.lighteningContrastLevel = e.target.value;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setBeautyEffect({
|
||
...beautyEffect,
|
||
lighteningContrastLevel: e.target.value
|
||
})
|
||
}} style={{ flexShrink: 0, margin: '10px 0' }} value={beautyEffect.lighteningContrastLevel}>
|
||
<Radio value={0}>低对比度</Radio>
|
||
<Radio value={1}>正常对比度</Radio>
|
||
<Radio value={2}>高对比度</Radio>
|
||
</Radio.Group>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<span>美白程度</span>
|
||
<div>
|
||
<Slider value={beautyEffect.lighteningLevel} step={0.1} style={{ width: '100%' }} max={1}
|
||
onChange={async (e) => {
|
||
setting.beautyEffect.lighteningLevel = e
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setBeautyEffect({
|
||
...beautyEffect,
|
||
lighteningLevel: e
|
||
})
|
||
}} disabled={!beautyEffect.isBeautyEffect} />
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<span>磨皮程度</span>
|
||
<div>
|
||
<Slider value={beautyEffect.smoothnessLevel} step={0.1} style={{ width: '100%' }} max={1}
|
||
onChange={async (e) => {
|
||
setting.beautyEffect.smoothnessLevel = e
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setBeautyEffect({
|
||
...beautyEffect,
|
||
smoothnessLevel: e
|
||
})
|
||
}} disabled={!beautyEffect.isBeautyEffect} />
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<span>红润度</span>
|
||
<div>
|
||
<Slider value={beautyEffect.rednessLevel} step={0.1} style={{ width: '100%' }} max={1}
|
||
onChange={async (e) => {
|
||
setting.beautyEffect.rednessLevel = e
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setBeautyEffect({
|
||
...beautyEffect,
|
||
rednessLevel: e
|
||
})
|
||
}} disabled={!beautyEffect.isBeautyEffect} />
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<span>锐化程度</span>
|
||
<div>
|
||
<Slider value={beautyEffect.sharpnessLevel} step={0.1} style={{ width: '100%' }} max={1}
|
||
onChange={async (e) => {
|
||
setting.beautyEffect.sharpnessLevel = e
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setBeautyEffect({
|
||
...beautyEffect,
|
||
sharpnessLevel: e
|
||
})
|
||
}} disabled={!beautyEffect.isBeautyEffect} />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div> : null}
|
||
{videoKey === 'darkLightEnhancement' ? <div>
|
||
<span>
|
||
<Checkbox style={{ marginRight: '10px' }} checked={colorEnhancement.isColorEnhancement} onChange={(e) => {
|
||
setting.colorEnhancement.isColorEnhancement = e.target.checked;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setColorEnhancement({
|
||
...colorEnhancement,
|
||
isColorEnhancement: e.target.checked
|
||
})
|
||
}}>
|
||
</Checkbox>
|
||
色彩增强
|
||
</span>
|
||
<div>
|
||
<div>
|
||
<span>色彩增强程度</span>
|
||
<div>
|
||
<Slider value={colorEnhancement.strengthLevel} step={0.1} style={{ width: '100%' }} max={1}
|
||
onChange={async (e) => {
|
||
setting.colorEnhancement.strengthLevel = e
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setColorEnhancement({
|
||
...colorEnhancement,
|
||
strengthLevel: e
|
||
})
|
||
}} disabled={!colorEnhancement.isColorEnhancement} />
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<span>肤色保护程度</span>
|
||
<div>
|
||
<Slider value={colorEnhancement.skinProtectLevel} step={0.1} style={{ width: '100%' }} max={1}
|
||
onChange={async (e) => {
|
||
setting.colorEnhancement.skinProtectLevel = e
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setColorEnhancement({
|
||
...colorEnhancement,
|
||
skinProtectLevel: e
|
||
})
|
||
}} disabled={!colorEnhancement.isColorEnhancement} />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div> : null}
|
||
{videoKey === 'virtualBackground' ? <div>
|
||
<span>
|
||
<Checkbox style={{ marginRight: '10px' }} checked={darkLightEnhancement.isDarkLightEnhancement} onChange={(e) => {
|
||
setting.darkLightEnhancement.isDarkLightEnhancement = e.target.checked;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setDarkLightEnhancement({
|
||
...darkLightEnhancement,
|
||
isDarkLightEnhancement: e.target.checked
|
||
})
|
||
}}>
|
||
</Checkbox>
|
||
暗光增强
|
||
</span>
|
||
<div>
|
||
<div>
|
||
<span>暗光增强等级</span>
|
||
<div>
|
||
<Radio.Group disabled={!darkLightEnhancement.isDarkLightEnhancement} onChange={(e: any) => {
|
||
setting.darkLightEnhancement.level = e.target.value;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setDarkLightEnhancement({
|
||
...darkLightEnhancement,
|
||
level: e.target.value
|
||
})
|
||
}} style={{ flexShrink: 0, margin: '10px 0' }} value={darkLightEnhancement.level}>
|
||
<Radio value={0}>(默认)优先画质的暗光增强,会处理视频图像的亮度、细节、噪声,消耗的性能适中,处理速度适中,综合画质最优。</Radio>
|
||
<Radio value={1}>优先性能的暗光增强,会处理视频图像的亮度、细节,消耗的性能较少,处理速度较快。</Radio>
|
||
</Radio.Group>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<span>暗光增强模式</span>
|
||
<div>
|
||
<Radio.Group disabled={!darkLightEnhancement.isDarkLightEnhancement} onChange={(e: any) => {
|
||
setting.darkLightEnhancement.mode = e.target.value;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setDarkLightEnhancement({
|
||
...darkLightEnhancement,
|
||
mode: e.target.value
|
||
})
|
||
}} style={{ flexShrink: 0, margin: '10px 0' }} value={darkLightEnhancement.mode}>
|
||
<Radio value={0}>(默认)自动模式。SDK 会根据环境光亮度自动开启或关闭暗光增强功能,以适时补光和防止过曝。</Radio>
|
||
<Radio value={1}>手动模式。用户需手动开启或关闭暗光增强功能。</Radio>
|
||
</Radio.Group>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div> : null}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</>
|
||
)
|
||
}
|
||
const AudioComponents = () => {
|
||
const [audioDeviceManager, setAudioDeviceManager] = useState<any>({
|
||
playBackList: [],
|
||
ecordingList: [],
|
||
playBackItem: null,
|
||
ecordingItem: null,
|
||
playBackVolume: 0,
|
||
playBackActive: false,
|
||
ecordingActive: false,
|
||
ecordingVolume: 0,
|
||
autoEcordingVolume: true,
|
||
isAINoiseReduction: true,
|
||
aINoiseReduction: 1
|
||
});
|
||
const setting = JSON.parse(storage.getItem('setting') as string)
|
||
useEffect(() => {
|
||
getAudioMediaList()
|
||
agora.registerEventHandler({
|
||
onAudioVolumeIndication: (speakers: any) => {
|
||
speakers.forEach((item: any) => {
|
||
if (item.uid === 0 || item.uid === 1) {
|
||
const percentage = (item.volume / 255) * 100
|
||
const dom = document.getElementById('deviceTest') as any;
|
||
if (dom) {
|
||
dom.style.width = `${percentage}%`
|
||
}
|
||
}
|
||
});
|
||
}
|
||
})
|
||
}, []);
|
||
const getAudioMediaList = async (): Promise<void> => {
|
||
const {
|
||
playBackList,
|
||
ecordingList,
|
||
ecordingVolume
|
||
} = await agora.getAudioMediaList();
|
||
if (!setting.ecordingVolume) {
|
||
setting.ecordingVolume = ecordingVolume;
|
||
}
|
||
if (!setting.playBackVolume) {
|
||
setting.playBackVolume = 127;
|
||
}
|
||
setAudioDeviceManager({
|
||
...audioDeviceManager,
|
||
playBackList: playBackList.map((row: any) => {
|
||
return {
|
||
value: row.deviceId,
|
||
label: row.deviceName
|
||
}
|
||
}),
|
||
ecordingList: ecordingList.map((row: any) => {
|
||
return {
|
||
value: row.deviceId,
|
||
label: row.deviceName
|
||
}
|
||
}),
|
||
playBackItem: setting.playBackDeviceId,
|
||
ecordingItem: setting.ecordingDeviceId,
|
||
ecordingVolume: setting.ecordingVolume,
|
||
playBackVolume: setting.playBackVolume,
|
||
autoEcordingVolume: setting.autoEcordingVolume,
|
||
isAINoiseReduction: setting.isAINoiseReduction,
|
||
aINoiseReduction: setting.aINoiseReduction,
|
||
})
|
||
}
|
||
return (
|
||
<>
|
||
<div>
|
||
<span>音频</span>
|
||
<div className={styles.audioComponents}>
|
||
<div>
|
||
<div className={styles.audioComponentsSelect}>
|
||
<span>麦克风:</span>
|
||
<Select
|
||
placeholder={audioDeviceManager.ecordingList.length ? '请选择设备' : '未检测到麦克风'}
|
||
options={audioDeviceManager.ecordingList} style={{ flexGrow: 1 }}
|
||
value={audioDeviceManager.ecordingItem || null} onChange={async (e) => {
|
||
setting.ecordingDeviceId = e;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setAudioDeviceManager({
|
||
...audioDeviceManager,
|
||
ecordingItem: e
|
||
})
|
||
agora.setRecordingDevice(e)
|
||
}} />;
|
||
{(meetingUserInfo &&
|
||
(role.ID.includes(meetingUserInfo.roleId) || meetingUserInfo.isRoomManager) &&
|
||
meetingUserInfo.enableMicr) ||
|
||
location.hash.indexOf('/meeting') === -1
|
||
? audioDeviceManager.ecordingActive ? <div onClick={() => {
|
||
agora.stopRecordingDeviceTest()
|
||
setAudioDeviceManager({
|
||
...audioDeviceManager,
|
||
playBackActive: false,
|
||
ecordingActive: false,
|
||
})
|
||
}}>结束</div> : <div onClick={() => {
|
||
agora.stopPlaybackDeviceTest()
|
||
agora.startRecordingDeviceTest(100)
|
||
setAudioDeviceManager({
|
||
...audioDeviceManager,
|
||
playBackActive: false,
|
||
ecordingActive: true,
|
||
})
|
||
}}>测试</div> : null}
|
||
</div>
|
||
{audioDeviceManager.ecordingActive ? <div className={styles.audioComponentsVolume}>
|
||
<img src={ImageUrl.icon36} alt="" />
|
||
<div>
|
||
<img src={ImageUrl.icon34} alt="" />
|
||
<div id='deviceTest'>
|
||
<img src={ImageUrl.icon35} alt="" />
|
||
</div>
|
||
</div>
|
||
</div> : null}
|
||
<div className={styles.audioComponentsSlider}>
|
||
<span>输入音量:</span>
|
||
<Slider value={audioDeviceManager.ecordingVolume} style={{ flexGrow: 1 }} max={255}
|
||
onChange={async (e) => {
|
||
setting.ecordingVolume = e;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
await agora.setRecordingDeviceVolume(e)
|
||
setAudioDeviceManager({
|
||
...audioDeviceManager,
|
||
ecordingVolume: e,
|
||
})
|
||
}} disabled={!audioDeviceManager.ecordingItem} />
|
||
{/* || audioDeviceManager.autoEcordingVolume */}
|
||
</div>
|
||
{/* <div style={{ marginBottom: '10px' }}>
|
||
<Checkbox onChange={async (e) => {
|
||
setting.autoEcordingVolume = e.target.checked;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setAudioDeviceManager({
|
||
...audioDeviceManager,
|
||
autoEcordingVolume: e.target.checked
|
||
})
|
||
}} checked={audioDeviceManager.autoEcordingVolume}>自动调整麦克风音量</Checkbox>
|
||
</div> */}
|
||
<div>
|
||
<div>
|
||
<Checkbox checked={audioDeviceManager.isAINoiseReduction} onChange={(e) => {
|
||
setting.isAINoiseReduction = e.target.checked;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setAudioDeviceManager({
|
||
...audioDeviceManager,
|
||
isAINoiseReduction: e.target.checked
|
||
})
|
||
agora.setAINSMode(e.target.checked, audioDeviceManager.aINoiseReduction)
|
||
}}>
|
||
AI降噪
|
||
</Checkbox>
|
||
</div>
|
||
<div style={{ margin: '10px 0 0 20px' }}>
|
||
<Radio.Group onChange={(e) => {
|
||
setting.aINoiseReduction = e.target.value;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setAudioDeviceManager({
|
||
...audioDeviceManager,
|
||
aINoiseReduction: e.target.value
|
||
})
|
||
agora.setAINSMode(audioDeviceManager.isAINoiseReduction, e.target.value)
|
||
}} disabled={!audioDeviceManager.isAINoiseReduction} value={audioDeviceManager.aINoiseReduction}>
|
||
<Space direction="vertical">
|
||
<Radio value={0}>均衡降噪模式</Radio>
|
||
<Radio value={1}>强降噪模式</Radio>
|
||
<Radio value={2}>低延时强降噪模式</Radio>
|
||
</Space>
|
||
</Radio.Group>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div className={styles.audioComponentsSelect}>
|
||
<span>扬声器:</span>
|
||
<Select
|
||
placeholder={audioDeviceManager.playBackList.length ? '请选择设备' : '未检测到麦克风'}
|
||
options={audioDeviceManager.playBackList} style={{ flexGrow: 1 }}
|
||
value={audioDeviceManager.playBackItem || null} onChange={async (e) => {
|
||
setting.playBackDeviceId = e;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setAudioDeviceManager({
|
||
...audioDeviceManager,
|
||
playBackItem: e
|
||
})
|
||
agora.setPlaybackDevice(e)
|
||
}} />;
|
||
{audioDeviceManager.playBackActive ? <div onClick={() => {
|
||
agora.stopPlaybackDeviceTest()
|
||
setAudioDeviceManager({
|
||
...audioDeviceManager,
|
||
playBackActive: false,
|
||
ecordingActive: false,
|
||
})
|
||
}}>结束</div> : <div onClick={() => {
|
||
agora.stopRecordingDeviceTest()
|
||
agora.startPlaybackDeviceTest()
|
||
setAudioDeviceManager({
|
||
...audioDeviceManager,
|
||
playBackActive: true,
|
||
ecordingActive: false,
|
||
})
|
||
}}>测试</div>}
|
||
</div>
|
||
{audioDeviceManager.playBackActive ? <div className={styles.audioComponentsVolume}>
|
||
<img src={ImageUrl.icon36} alt="" />
|
||
<div>
|
||
<img src={ImageUrl.icon34} alt="" />
|
||
<div id='deviceTest'>
|
||
<img src={ImageUrl.icon35} alt="" />
|
||
</div>
|
||
</div>
|
||
</div> : null}
|
||
<div className={styles.audioComponentsSlider}>
|
||
<span>输出音量:</span>
|
||
<Slider value={audioDeviceManager.playBackVolume} style={{ flexGrow: 1 }} max={255}
|
||
onChange={async (e) => {
|
||
setting.playBackVolume = e;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
agora.setPlaybackDeviceVolume(e)
|
||
setAudioDeviceManager({
|
||
...audioDeviceManager,
|
||
playBackVolume: e,
|
||
})
|
||
}} disabled={!audioDeviceManager.playBackItem} />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</>
|
||
)
|
||
}
|
||
const RecordingComponents = () => {
|
||
const [filePath, setFilePath] = useState('')
|
||
const [isRecordingTips, setIsRecordingTips] = useState(false)
|
||
const setting = JSON.parse(storage.getItem('setting') as string)
|
||
useEffect(() => {
|
||
if (!setting.recordingFilesPath) {
|
||
// 获取安装父目录
|
||
const currentDirectory = __dirname;
|
||
const parentDirectory = path.resolve(currentDirectory, '../../Downloads') + '\\';
|
||
setting.recordingFilesPath = parentDirectory;
|
||
setFilePath(setting.recordingFilesPath)
|
||
} else {
|
||
setFilePath(setting.recordingFilesPath);
|
||
}
|
||
if (setting.isRecordingTips === undefined) {
|
||
setting.isRecordingTips = true;
|
||
}
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setIsRecordingTips(setting.isRecordingTips)
|
||
window.addEventListener('customStorageChange', handleCustomStorageChange);
|
||
return () => {
|
||
window.removeEventListener('customStorageChange', handleCustomStorageChange);
|
||
};
|
||
}, [])
|
||
const handleCustomStorageChange = (e: any): void => {
|
||
if (e.key === 'setting') {
|
||
const setting = JSON.parse(storage.getItem('setting') as string)
|
||
setFilePath(setting.recordingFilesPath)
|
||
}
|
||
};
|
||
return (
|
||
<>
|
||
<div>
|
||
<span>录制</span>
|
||
<div className={styles.recordingComponents}>
|
||
<div>
|
||
<span>本地录制</span>
|
||
<div>
|
||
<span>本地录制文件路径</span>
|
||
<Input
|
||
disabled={true}
|
||
placeholder="请填入文件路径"
|
||
style={{ margin: '0 14px', flexGrow: 1 }}
|
||
value={filePath}
|
||
onChange={async (e) => {
|
||
setting.recordingFilesPath = e.target.value;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setFilePath(e.target.value)
|
||
}}
|
||
/>
|
||
<Button type="primary" onClick={() => {
|
||
window.electron.selectFilePath({
|
||
key: 'recordingFilesPath',
|
||
})
|
||
}} style={{ backgroundColor: '#31353A', marginRight: '10px' }}>选择保存目录</Button>
|
||
<Button type="primary" onClick={async () => {
|
||
try {
|
||
await fs.access(filePath, fs.constants.F_OK);
|
||
if (process.platform === 'win32') {
|
||
exec(`explorer "${filePath}"`);
|
||
} else if (process.platform === 'darwin') {
|
||
exec(`open "${filePath}"`);
|
||
}
|
||
} catch (error: any) {
|
||
if (error.code === 'ENOENT') {
|
||
message.error('文件夹不存在!')
|
||
} else {
|
||
message.error(error)
|
||
}
|
||
}
|
||
}} style={{ backgroundColor: '#31353A' }}>打开</Button>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<span>录制设置</span>
|
||
<div>
|
||
<Checkbox onChange={async (e) => {
|
||
setting.isRecordingTips = e.target.checked;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setIsRecordingTips(e.target.checked)
|
||
}} checked={isRecordingTips}>开启入会录制提示</Checkbox>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</>
|
||
)
|
||
}
|
||
const FileComponents = () => {
|
||
const [filePath, setFilePath] = useState('')
|
||
const [isShareSavePath, setIsShareSavePath] = useState(true)
|
||
const setting = JSON.parse(storage.getItem('setting') as string)
|
||
useEffect(() => {
|
||
if (!setting.shareFilesPath) {
|
||
// 获取安装父目录
|
||
const currentDirectory = __dirname;
|
||
const parentDirectory = path.resolve(currentDirectory, '../../Downloads') + '\\';
|
||
setting.shareFilesPath = parentDirectory
|
||
setFilePath(setting.shareFilesPath)
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
} else {
|
||
setFilePath(setting.shareFilesPath);
|
||
}
|
||
setIsShareSavePath(setting.isShareSavePath)
|
||
window.addEventListener('customStorageChange', handleCustomStorageChange);
|
||
return () => {
|
||
window.removeEventListener('customStorageChange', handleCustomStorageChange);
|
||
};
|
||
}, [])
|
||
const handleCustomStorageChange = (e: any): void => {
|
||
if (e.key === 'setting') {
|
||
const setting = JSON.parse(storage.getItem('setting') as string)
|
||
setFilePath(setting.shareFilesPath)
|
||
}
|
||
};
|
||
return (
|
||
<>
|
||
<div>
|
||
<span>文件下载</span>
|
||
<div className={styles.fileComponents}>
|
||
<span>文件下载</span>
|
||
<div>
|
||
<span>保存位置</span>
|
||
<Input
|
||
disabled={true}
|
||
placeholder="请填入保存目录"
|
||
style={{ margin: '0 14px', flexGrow: 1 }}
|
||
value={filePath}
|
||
onChange={async (e) => {
|
||
setting.shareFilesPath = e.target.value;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setFilePath(e.target.value)
|
||
}}
|
||
/>
|
||
<Button type="primary" onClick={() => {
|
||
window.electron.selectFilePath({
|
||
key: 'shareFilesPath',
|
||
})
|
||
}} style={{ backgroundColor: '#31353A', marginRight: '10px' }}>选择保存目录</Button>
|
||
<Button type="primary" onClick={async () => {
|
||
try {
|
||
await fs.access(filePath, fs.constants.F_OK);
|
||
if (process.platform === 'win32') {
|
||
exec(`explorer "${filePath}"`);
|
||
} else if (process.platform === 'darwin') {
|
||
exec(`open "${filePath}"`);
|
||
}
|
||
} catch (error: any) {
|
||
if (error.code === 'ENOENT') {
|
||
message.error('文件夹不存在!')
|
||
} else {
|
||
message.error(error)
|
||
}
|
||
}
|
||
}} style={{ backgroundColor: '#31353A' }}>打开</Button>
|
||
</div>
|
||
<div>
|
||
<Checkbox onChange={async (e) => {
|
||
setting.isShareSavePath = e.target.checked;
|
||
storage.setItem('setting', JSON.stringify(setting))
|
||
setIsShareSavePath(e.target.checked)
|
||
}} checked={isShareSavePath}>下载前询问每个文件保存位置</Checkbox>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</>
|
||
)
|
||
}
|
||
|
||
export default memo(StupWizard)
|