fix: wav format audio, #2317
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
import { UploadOutlined } from '@ant-design/icons';
|
import { UploadOutlined } from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Button, Tooltip, Upload } from 'antd';
|
import { Button, Tooltip, Upload, message } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { convertFileSize } from '../../utils';
|
||||||
|
|
||||||
interface UploadAudioProps {
|
interface UploadAudioProps {
|
||||||
accept?: string;
|
accept?: string;
|
||||||
@@ -10,17 +11,36 @@ interface UploadAudioProps {
|
|||||||
icon?: React.ReactNode;
|
icon?: React.ReactNode;
|
||||||
size?: 'small' | 'middle' | 'large';
|
size?: 'small' | 'middle' | 'large';
|
||||||
shape?: 'circle' | 'round' | 'default';
|
shape?: 'circle' | 'round' | 'default';
|
||||||
|
maxFileSize?: number; // in bytes
|
||||||
onChange?: (data: { file: any; fileList: any[] }) => void;
|
onChange?: (data: { file: any; fileList: any[] }) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UploadAudio: React.FC<UploadAudioProps> = (props) => {
|
const UploadAudio: React.FC<UploadAudioProps> = (props) => {
|
||||||
|
const [messageApi, contextHolder] = message.useMessage();
|
||||||
const { icon, accept, type, size = 'large', shape = 'circle' } = props;
|
const { icon, accept, type, size = 'large', shape = 'circle' } = props;
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const beforeUpload = (file: any) => {
|
const beforeUpload = (file: any) => {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isFileSizeValid = (file: File) => {
|
||||||
|
if (props.maxFileSize && file.size > props.maxFileSize) {
|
||||||
|
messageApi.open({
|
||||||
|
type: 'warning',
|
||||||
|
content: intl.formatMessage(
|
||||||
|
{ id: 'playground.uploadfile.sizeError' },
|
||||||
|
{ size: `${convertFileSize(props.maxFileSize)}` } // Convert bytes to MB
|
||||||
|
)
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
const handleOnChange = (data: { file: any; fileList: any }) => {
|
const handleOnChange = (data: { file: any; fileList: any }) => {
|
||||||
|
if (!isFileSizeValid(data.file)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
props.onChange?.(data);
|
props.onChange?.(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -57,6 +77,7 @@ const UploadAudio: React.FC<UploadAudioProps> = (props) => {
|
|||||||
></Button>
|
></Button>
|
||||||
</div>
|
</div>
|
||||||
</Upload>
|
</Upload>
|
||||||
|
{contextHolder}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -154,5 +154,7 @@ export default {
|
|||||||
'If the expected model isn’t showing up, make sure it’s running and correctly categorized. If the category is incorrect, you can manually adjust it in the model’s settings.',
|
'If the expected model isn’t showing up, make sure it’s running and correctly categorized. If the category is incorrect, you can manually adjust it in the model’s settings.',
|
||||||
'playground.rerank.query.validate': 'The query is required.',
|
'playground.rerank.query.validate': 'The query is required.',
|
||||||
'playground.image.generate.error':
|
'playground.image.generate.error':
|
||||||
'Something went wrong. The image could not be generated.'
|
'Something went wrong. The image could not be generated.',
|
||||||
|
'playground.uploadfile.sizeError':
|
||||||
|
'File size exceeds the limit. Maximum allowed: {size}.'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -157,10 +157,13 @@ export default {
|
|||||||
'期待するモデルが表示されない場合は、モデルが実行中で正しく分類されていることを確認してください。カテゴリが間違っている場合は、モデルの設定で手動で調整できます。',
|
'期待するモデルが表示されない場合は、モデルが実行中で正しく分類されていることを確認してください。カテゴリが間違っている場合は、モデルの設定で手動で調整できます。',
|
||||||
'playground.rerank.query.validate': 'The query is required.',
|
'playground.rerank.query.validate': 'The query is required.',
|
||||||
'playground.image.generate.error':
|
'playground.image.generate.error':
|
||||||
'Something went wrong. The image could not be generated.'
|
'Something went wrong. The image could not be generated.',
|
||||||
|
'playground.uploadfile.sizeError':
|
||||||
|
'File size exceeds the limit. Maximum allowed: {size}.'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
// 1. 'playground.rerank.query.validate': 'The query is required.'
|
// 1. 'playground.rerank.query.validate': 'The query is required.'
|
||||||
// 2. 'playground.image.generate.error': 'Something went wrong. The image could not be generated.'
|
// 2. 'playground.image.generate.error': 'Something went wrong. The image could not be generated.',
|
||||||
|
// 3. 'playground.uploadfile.sizeError': 'File size exceeds the limit. Maximum allowed: {size}.'
|
||||||
// ========== End of To-Do List ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -151,10 +151,13 @@ export default {
|
|||||||
'Если нужная модель не отображается, убедитесь, что она запущена и ей присвоена правильная категория. Если категория указана неверно, её можно изменить вручную в настройках модели.',
|
'Если нужная модель не отображается, убедитесь, что она запущена и ей присвоена правильная категория. Если категория указана неверно, её можно изменить вручную в настройках модели.',
|
||||||
'playground.rerank.query.validate': 'The query is required.',
|
'playground.rerank.query.validate': 'The query is required.',
|
||||||
'playground.image.generate.error':
|
'playground.image.generate.error':
|
||||||
'Something went wrong. The image could not be generated.'
|
'Something went wrong. The image could not be generated.',
|
||||||
|
'playground.uploadfile.sizeError':
|
||||||
|
'File size exceeds the limit. Maximum allowed: {size}.'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
// 1. 'playground.rerank.query.validate': 'The query is required.'
|
// 1. 'playground.rerank.query.validate': 'The query is required.'
|
||||||
// 2. 'playground.image.generate.error': 'Something went wrong. The image could not be generated.'
|
// 2. 'playground.image.generate.error': 'Something went wrong. The image could not be generated.',
|
||||||
|
// 3. 'playground.uploadfile.sizeError': 'File size exceeds the limit. Maximum allowed: {size}.'
|
||||||
// ========== End of To-Do List ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -147,5 +147,6 @@ export default {
|
|||||||
'playground.model.noavailable.tips2':
|
'playground.model.noavailable.tips2':
|
||||||
'若预期的模型未显示,请检查模型是否已正常运行并被正确分类。如分类不正确,请编辑模型并手动调整其类别。',
|
'若预期的模型未显示,请检查模型是否已正常运行并被正确分类。如分类不正确,请编辑模型并手动调整其类别。',
|
||||||
'playground.rerank.query.validate': '查询内容不能为空',
|
'playground.rerank.query.validate': '查询内容不能为空',
|
||||||
'playground.image.generate.error': '出了一点问题,图片未能生成。'
|
'playground.image.generate.error': '出了一点问题,图片未能生成。',
|
||||||
|
'playground.uploadfile.sizeError': '上传的文件大小超过限制,最大允许 {size}'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,11 @@ import SimpleAudio from '@/components/audio-player/simple-audio';
|
|||||||
import IconFont from '@/components/icon-font';
|
import IconFont from '@/components/icon-font';
|
||||||
import UploadAudio from '@/components/upload-audio';
|
import UploadAudio from '@/components/upload-audio';
|
||||||
import HotKeys, { KeyMap } from '@/config/hotkeys';
|
import HotKeys, { KeyMap } from '@/config/hotkeys';
|
||||||
import { convertFileToBase64, readAudioFile } from '@/utils/load-audio-file';
|
import {
|
||||||
|
audioTypeMap,
|
||||||
|
convertFileToBase64,
|
||||||
|
readAudioFile
|
||||||
|
} from '@/utils/load-audio-file';
|
||||||
import {
|
import {
|
||||||
ClearOutlined,
|
ClearOutlined,
|
||||||
CustomerServiceOutlined,
|
CustomerServiceOutlined,
|
||||||
@@ -28,12 +32,6 @@ import '../style/message-input.less';
|
|||||||
import ThumbImg from './thumb-img';
|
import ThumbImg from './thumb-img';
|
||||||
import UploadImg from './upload-img';
|
import UploadImg from './upload-img';
|
||||||
|
|
||||||
const audioTypeMap: Record<string, string> = {
|
|
||||||
'audio/wav': 'wav',
|
|
||||||
'audio/mp3': 'mp3',
|
|
||||||
'audio/mpeg': 'mp3'
|
|
||||||
};
|
|
||||||
|
|
||||||
const AudioWrapper = styled.div`
|
const AudioWrapper = styled.div`
|
||||||
padding-block: 10px;
|
padding-block: 10px;
|
||||||
height: max-content;
|
height: max-content;
|
||||||
@@ -303,7 +301,7 @@ const MessageInput: React.FC<MessageInputProps> = forwardRef(
|
|||||||
try {
|
try {
|
||||||
const base64Audio = await convertFileToBase64(data.file);
|
const base64Audio = await convertFileToBase64(data.file);
|
||||||
const audioData = await readAudioFile(data.file);
|
const audioData = await readAudioFile(data.file);
|
||||||
console.log('audioData====', message.imgs);
|
console.log('audioData====', audioData, message.imgs);
|
||||||
setMessage((pre) => {
|
setMessage((pre) => {
|
||||||
return {
|
return {
|
||||||
...pre,
|
...pre,
|
||||||
@@ -421,6 +419,7 @@ const MessageInput: React.FC<MessageInputProps> = forwardRef(
|
|||||||
)}
|
)}
|
||||||
{actions.includes('upload') && message.role === Roles.User && (
|
{actions.includes('upload') && message.role === Roles.User && (
|
||||||
<UploadAudio
|
<UploadAudio
|
||||||
|
maxFileSize={1024 * 1024}
|
||||||
type="text"
|
type="text"
|
||||||
accept={'.mp3,.wav'}
|
accept={'.mp3,.wav'}
|
||||||
size="middle"
|
size="middle"
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import CopyButton from '@/components/copy-button';
|
import CopyButton from '@/components/copy-button';
|
||||||
import UploadAudio from '@/components/upload-audio';
|
import UploadAudio from '@/components/upload-audio';
|
||||||
import { convertFileToBase64, readAudioFile } from '@/utils/load-audio-file';
|
import {
|
||||||
|
audioTypeMap,
|
||||||
|
convertFileToBase64,
|
||||||
|
readAudioFile
|
||||||
|
} from '@/utils/load-audio-file';
|
||||||
import {
|
import {
|
||||||
CustomerServiceOutlined,
|
CustomerServiceOutlined,
|
||||||
EditOutlined,
|
EditOutlined,
|
||||||
@@ -18,12 +22,6 @@ import {
|
|||||||
} from '../../config/types';
|
} from '../../config/types';
|
||||||
import UploadImg from '../upload-img';
|
import UploadImg from '../upload-img';
|
||||||
|
|
||||||
const audioTypeMap: Record<string, string> = {
|
|
||||||
'audio/wav': 'wav',
|
|
||||||
'audio/mp3': 'mp3',
|
|
||||||
'audio/mpeg': 'mp3'
|
|
||||||
};
|
|
||||||
|
|
||||||
interface MessageActionsProps {
|
interface MessageActionsProps {
|
||||||
data: MessageItem;
|
data: MessageItem;
|
||||||
actions: MessageItemAction[];
|
actions: MessageItemAction[];
|
||||||
@@ -88,6 +86,7 @@ const MessageActions: React.FC<MessageActionsProps> = ({
|
|||||||
{actions.includes('upload') && data.role === Roles.User && (
|
{actions.includes('upload') && data.role === Roles.User && (
|
||||||
<UploadAudio
|
<UploadAudio
|
||||||
type="text"
|
type="text"
|
||||||
|
maxFileSize={1024 * 1024}
|
||||||
accept={'.mp3,.wav'}
|
accept={'.mp3,.wav'}
|
||||||
size="small"
|
size="small"
|
||||||
shape="default"
|
shape="default"
|
||||||
|
|||||||
+4
-2
@@ -28,12 +28,14 @@ export const convertFileSize = (
|
|||||||
): string | number => {
|
): string | number => {
|
||||||
if (!sizeInBytes) return allowEmpty ? '' : 0;
|
if (!sizeInBytes) return allowEmpty ? '' : 0;
|
||||||
|
|
||||||
|
const fmt = 1024;
|
||||||
|
|
||||||
const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB'];
|
const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB'];
|
||||||
let size = sizeInBytes;
|
let size = sizeInBytes;
|
||||||
let unitIndex = 0;
|
let unitIndex = 0;
|
||||||
|
|
||||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
while (size >= fmt && unitIndex < units.length - 1) {
|
||||||
size /= 1024;
|
size /= fmt;
|
||||||
unitIndex++;
|
unitIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
|
import { message } from 'antd';
|
||||||
import { convertFileSize } from './index';
|
import { convertFileSize } from './index';
|
||||||
|
|
||||||
|
export const audioTypeMap: Record<string, string> = {
|
||||||
|
'audio/wav': 'wav',
|
||||||
|
'audio/mp3': 'mp3',
|
||||||
|
'audio/mpeg': 'mp3',
|
||||||
|
'audio/x-wav': 'wav'
|
||||||
|
};
|
||||||
|
|
||||||
export const convertFileToBase64 = (file: File): Promise<string> => {
|
export const convertFileToBase64 = (file: File): Promise<string> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
@@ -42,6 +50,12 @@ export const loadAudioData = async (
|
|||||||
audio.addEventListener('ended', () => {
|
audio.addEventListener('ended', () => {
|
||||||
URL.revokeObjectURL(audio.src);
|
URL.revokeObjectURL(audio.src);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
audio.addEventListener('error', () => {
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
message.error('Failed to load audio metadata invalid file');
|
||||||
|
reject(new Error('Failed to load audio metadata invalid file'));
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('error====', error);
|
console.log('error====', error);
|
||||||
reject(error);
|
reject(error);
|
||||||
|
|||||||
Reference in New Issue
Block a user