Fix voiceprint upload temp file collisions
This commit is contained in:
parent
83d863daf7
commit
41baca051c
@ -3,9 +3,10 @@
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import mimetypes
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
import mimetypes
|
||||
import subprocess
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
from flask import Blueprint, request, jsonify, send_file, current_app
|
||||
@ -95,7 +96,42 @@ def convert_to_wav(input_path):
|
||||
raise Exception(f"音频转换错误: {str(e)}")
|
||||
|
||||
|
||||
@voiceprint_bp.route('/api/voiceprints', methods=['GET'])
|
||||
def _safe_audio_extension(uploaded_file):
|
||||
filename = secure_filename(uploaded_file.filename or "")
|
||||
suffix = Path(filename).suffix.lower()
|
||||
if suffix:
|
||||
return suffix
|
||||
|
||||
content_type = (uploaded_file.content_type or "").lower()
|
||||
if "webm" in content_type:
|
||||
return ".webm"
|
||||
if "wav" in content_type or "wave" in content_type:
|
||||
return ".wav"
|
||||
if "mpeg" in content_type or "mp3" in content_type:
|
||||
return ".mp3"
|
||||
if "ogg" in content_type:
|
||||
return ".ogg"
|
||||
if "mp4" in content_type or "m4a" in content_type:
|
||||
return ".m4a"
|
||||
return ".webm"
|
||||
|
||||
|
||||
def _make_temp_audio_path(upload_dir, uploaded_file):
|
||||
ext = _safe_audio_extension(uploaded_file)
|
||||
filename = secure_filename(
|
||||
f"temp_{datetime.now().strftime('%Y%m%d%H%M%S%f')}_{uuid.uuid4().hex}{ext}"
|
||||
)
|
||||
return os.path.join(upload_dir, filename)
|
||||
|
||||
|
||||
def _make_final_voiceprint_path(upload_dir, name):
|
||||
filename = secure_filename(
|
||||
f"{name}_{datetime.now().strftime('%Y%m%d%H%M%S%f')}_{uuid.uuid4().hex[:8]}.wav"
|
||||
)
|
||||
return os.path.join(upload_dir, filename)
|
||||
|
||||
|
||||
@voiceprint_bp.route('/api/voiceprints', methods=['GET'])
|
||||
@login_required
|
||||
def get_voiceprints():
|
||||
"""获取声纹列表"""
|
||||
@ -141,8 +177,7 @@ def create_voiceprint():
|
||||
os.makedirs(voiceprint_dir, exist_ok=True)
|
||||
|
||||
# 保存原始音频文件到临时位置
|
||||
temp_filename = secure_filename(f"temp_{datetime.now().strftime('%Y%m%d%H%M%S')}_{file.filename}")
|
||||
temp_filepath = os.path.join(voiceprint_dir, temp_filename)
|
||||
temp_filepath = _make_temp_audio_path(voiceprint_dir, file)
|
||||
file.save(temp_filepath)
|
||||
original_file_path = temp_filepath
|
||||
|
||||
@ -150,8 +185,7 @@ def create_voiceprint():
|
||||
converted_file_path = convert_to_wav(temp_filepath)
|
||||
|
||||
# 保存转换后的文件
|
||||
final_filename = secure_filename(f"{name}_{datetime.now().strftime('%Y%m%d%H%M%S')}.wav")
|
||||
final_filepath = os.path.join(voiceprint_dir, final_filename)
|
||||
final_filepath = _make_final_voiceprint_path(voiceprint_dir, name)
|
||||
|
||||
# 如果转换后的文件不是原始文件,则移动
|
||||
if converted_file_path != temp_filepath:
|
||||
@ -242,8 +276,7 @@ def update_voiceprint(voiceprint_id):
|
||||
os.makedirs(voiceprint_dir, exist_ok=True)
|
||||
|
||||
# 保存原始音频文件到临时位置
|
||||
temp_filename = secure_filename(f"temp_{datetime.now().strftime('%Y%m%d%H%M%S')}_{file.filename}")
|
||||
temp_filepath = os.path.join(voiceprint_dir, temp_filename)
|
||||
temp_filepath = _make_temp_audio_path(voiceprint_dir, file)
|
||||
file.save(temp_filepath)
|
||||
original_file_path = temp_filepath
|
||||
|
||||
@ -251,8 +284,7 @@ def update_voiceprint(voiceprint_id):
|
||||
converted_file_path = convert_to_wav(temp_filepath)
|
||||
|
||||
# 保存转换后的文件
|
||||
final_filename = secure_filename(f"{name}_{datetime.now().strftime('%Y%m%d%H%M%S')}.wav")
|
||||
final_filepath = os.path.join(voiceprint_dir, final_filename)
|
||||
final_filepath = _make_final_voiceprint_path(voiceprint_dir, name)
|
||||
|
||||
# 如果转换后的文件不是原始文件,则移动
|
||||
if converted_file_path != temp_filepath:
|
||||
|
||||
@ -1109,7 +1109,7 @@
|
||||
style="background-color: #fff;border-radius: 4px !important;"></textarea> -->
|
||||
<div class="modal-footer">
|
||||
<button class="btn" onclick="closeModal()">取消</button>
|
||||
<button class="btn btn-primary" onclick="saveVoice()"
|
||||
<button id="saveVoiceBtn" class="btn btn-primary" onclick="saveVoice()"
|
||||
style="color: #fff;">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -1791,10 +1791,11 @@
|
||||
let voices = []; // 存储声纹数据
|
||||
let editIndex = null;
|
||||
let deleteIndex = null;
|
||||
let mediaRecorder;
|
||||
let audioChunks = [];
|
||||
let isRecording = false;
|
||||
let modetypes = "add"
|
||||
let mediaRecorder;
|
||||
let audioChunks = [];
|
||||
let isRecording = false;
|
||||
let isSavingVoice = false;
|
||||
let modetypes = "add"
|
||||
let modechooseIndex = 0
|
||||
// 打开添加/编辑弹框
|
||||
async function openModal(mode, index) {
|
||||
@ -1851,8 +1852,11 @@ async function synchronize() {
|
||||
|
||||
}
|
||||
// 保存声纹信息
|
||||
async function saveVoice() {
|
||||
const name = document.getElementById('voiceName').value.trim();
|
||||
async function saveVoice() {
|
||||
if (isSavingVoice) {
|
||||
return;
|
||||
}
|
||||
const name = document.getElementById('voiceName').value.trim();
|
||||
// const desc
|
||||
// = document.getElementById('voiceDesc').value.trim();
|
||||
const fileInput = document.getElementById('voiceFile').files[0];
|
||||
@ -1879,9 +1883,17 @@ console.log("audioBlob:", audioBlob);
|
||||
|
||||
// 使用 FormData 发送音频文件
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("name", name);
|
||||
formData.append("voice_data", audioBlob);
|
||||
const formData = new FormData();
|
||||
formData.append("name", name);
|
||||
const uploadFileName = fileInput && fileInput.name ? fileInput.name : `voiceprint_${Date.now()}.webm`;
|
||||
formData.append("voice_data", audioBlob, uploadFileName);
|
||||
isSavingVoice = true;
|
||||
const saveBtn = document.getElementById('saveVoiceBtn');
|
||||
if (saveBtn) {
|
||||
saveBtn.disabled = true;
|
||||
saveBtn.dataset.originalText = saveBtn.textContent;
|
||||
saveBtn.textContent = '保存中...';
|
||||
}
|
||||
|
||||
if (modetypes == 'add') {
|
||||
try {
|
||||
@ -1901,9 +1913,15 @@ console.log("audioBlob:", audioBlob);
|
||||
} catch (err) {
|
||||
// console.error("上传出错:", err);
|
||||
// alert("上传失败:" + err.message);
|
||||
showMessage(`${err.message}`, 'error');
|
||||
|
||||
}
|
||||
showMessage(`${err.message}`, 'error');
|
||||
|
||||
} finally {
|
||||
isSavingVoice = false;
|
||||
if (saveBtn) {
|
||||
saveBtn.disabled = false;
|
||||
saveBtn.textContent = saveBtn.dataset.originalText || '保存';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
let v = voices[modechooseIndex]
|
||||
@ -1926,8 +1944,14 @@ console.log("audioBlob:", audioBlob);
|
||||
} catch (err) {
|
||||
// console.error("上传出错:", err);
|
||||
// alert("上传失败:" + err.message);
|
||||
showMessage(`${err.message}`, 'error');
|
||||
}
|
||||
showMessage(`${err.message}`, 'error');
|
||||
} finally {
|
||||
isSavingVoice = false;
|
||||
if (saveBtn) {
|
||||
saveBtn.disabled = false;
|
||||
saveBtn.textContent = saveBtn.dataset.originalText || '保存';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function showMessage(msg, type, duration = 3000) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user