加入音频的人声分离和语音识别

main
mula.liu 2025-08-25 11:30:27 +08:00
parent 0252c4c34a
commit 6ccda55b92
6 changed files with 38 additions and 24 deletions

View File

@ -1,6 +1,6 @@
// API配置文件 // API配置文件
const API_CONFIG = { const API_CONFIG = {
BASE_URL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000', BASE_URL: "",
ENDPOINTS: { ENDPOINTS: {
AUTH: { AUTH: {
LOGIN: '/api/auth/login' LOGIN: '/api/auth/login'

View File

@ -93,6 +93,7 @@ const CreateMeeting = ({ user }) => {
try { try {
// Create meeting // Create meeting
const meetingData = { const meetingData = {
user_id: user.user_id,
title: formData.title, title: formData.title,
meeting_time: formData.meeting_time || null, meeting_time: formData.meeting_time || null,
attendee_ids: formData.attendees.map(a => a.user_id) attendee_ids: formData.attendees.map(a => a.user_id)

View File

@ -220,7 +220,7 @@ const EditMeeting = ({ user }) => {
} }
); );
return `${API_BASE_URL}${response.data.url}`; return `${API_BASE_URL}${response.data.file_path}`;
} catch (err) { } catch (err) {
setError('上传图片失败,请重试'); setError('上传图片失败,请重试');
return null; return null;

View File

@ -441,6 +441,12 @@
background: #f8fafc; background: #f8fafc;
border-radius: 8px; border-radius: 8px;
border-left: 3px solid #667eea; border-left: 3px solid #667eea;
cursor: pointer;
transition: background-color 0.3s ease;
}
.transcript-item:hover {
background-color: #f0f2ff;
} }
.transcript-header-item { .transcript-header-item {
@ -456,21 +462,10 @@
font-size: 0.9rem; font-size: 0.9rem;
} }
.timestamp-button { .timestamp {
background: #667eea;
border: none;
border-radius: 4px;
padding: 0.25rem 0.5rem;
color: white;
cursor: pointer;
font-size: 0.8rem; font-size: 0.8rem;
font-weight: 500; font-weight: 500;
transition: all 0.3s ease; color: #64748b;
}
.timestamp-button:hover {
background: #5a67d8;
transform: scale(1.05);
} }
.transcript-text { .transcript-text {

View File

@ -35,7 +35,7 @@ const MeetingDetails = ({ user }) => {
setLoading(true); setLoading(true);
// Fallback URL construction in case config fails // Fallback URL construction in case config fails
const baseUrl = API_BASE_URL || 'http://localhost:8000'; const baseUrl = ""
const detailEndpoint = API_ENDPOINTS?.MEETINGS?.DETAIL?.(meeting_id) || `/api/meetings/${meeting_id}`; const detailEndpoint = API_ENDPOINTS?.MEETINGS?.DETAIL?.(meeting_id) || `/api/meetings/${meeting_id}`;
const audioEndpoint = API_ENDPOINTS?.MEETINGS?.AUDIO?.(meeting_id) || `/api/meetings/${meeting_id}/audio`; const audioEndpoint = API_ENDPOINTS?.MEETINGS?.AUDIO?.(meeting_id) || `/api/meetings/${meeting_id}/audio`;
const transcriptEndpoint = API_ENDPOINTS?.MEETINGS?.TRANSCRIPT?.(meeting_id) || `/api/meetings/${meeting_id}/transcript`; const transcriptEndpoint = API_ENDPOINTS?.MEETINGS?.TRANSCRIPT?.(meeting_id) || `/api/meetings/${meeting_id}/transcript`;
@ -47,7 +47,7 @@ const MeetingDetails = ({ user }) => {
try { try {
const audioResponse = await axios.get(`${baseUrl}${audioEndpoint}`); const audioResponse = await axios.get(`${baseUrl}${audioEndpoint}`);
// Construct URL using uploads path and relative path from database // Construct URL using uploads path and relative path from database
setAudioUrl(`${baseUrl}/uploads/${audioResponse.data.file_path}`); setAudioUrl(`${baseUrl}${audioResponse.data.file_path}`);
setAudioFileName(audioResponse.data.file_name); setAudioFileName(audioResponse.data.file_name);
} catch (audioError) { } catch (audioError) {
console.warn('No audio file available:', audioError); console.warn('No audio file available:', audioError);
@ -149,6 +149,8 @@ const MeetingDetails = ({ user }) => {
if (audioRef.current) { if (audioRef.current) {
audioRef.current.currentTime = timestamp; audioRef.current.currentTime = timestamp;
setCurrentTime(timestamp); setCurrentTime(timestamp);
audioRef.current.play();
setIsPlaying(true);
} }
}; };
@ -343,16 +345,17 @@ const MeetingDetails = ({ user }) => {
{showTranscript && ( {showTranscript && (
<div className="transcript-content"> <div className="transcript-content">
{transcript.map((item) => ( {transcript.map((item) => (
<div key={item.segment_id} className="transcript-item"> <div
key={item.segment_id}
className="transcript-item"
onClick={() => jumpToTime(item.start_time_ms / 1000)}
title="跳转到此时间点播放"
>
<div className="transcript-header-item"> <div className="transcript-header-item">
<span className="speaker-name">{item.speaker_tag}</span> <span className="speaker-name">{item.speaker_tag}</span>
<button <span className="timestamp">
className="timestamp-button"
onClick={() => jumpToTime(item.start_time_ms / 1000)}
title="跳转到此时间点"
>
{formatTime(item.start_time_ms / 1000)} {formatTime(item.start_time_ms / 1000)}
</button> </span>
</div> </div>
<div className="transcript-text">{item.text_content}</div> <div className="transcript-text">{item.text_content}</div>
</div> </div>

View File

@ -4,4 +4,19 @@ import react from '@vitejs/plugin-react'
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react()],
server: {
host: true, // Optional: Allows the server to be accessible externally
port: 5173, // Optional: Specify a port if needed
allowedHosts: ['c0e02ee.r9.cpolar.top'], // Add the problematic hostname here
proxy: {
'/api': {
target: 'http://localhost:8000', // 后端服务地址
changeOrigin: true, // 是否改变请求的源头
},
'/uploads': {
target: 'http://localhost:8000', // 后端服务地址
changeOrigin: true, // 是否改变请求的源头
},
},
}
}) })