Fix voiceprint upload temp file collisions
This commit is contained in:
parent
83d863daf7
commit
41baca051c
@ -3,9 +3,10 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
import uuid
|
||||||
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from flask import Blueprint, request, jsonify, send_file, current_app
|
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)}")
|
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
|
@login_required
|
||||||
def get_voiceprints():
|
def get_voiceprints():
|
||||||
"""获取声纹列表"""
|
"""获取声纹列表"""
|
||||||
@ -141,8 +177,7 @@ def create_voiceprint():
|
|||||||
os.makedirs(voiceprint_dir, exist_ok=True)
|
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 = _make_temp_audio_path(voiceprint_dir, file)
|
||||||
temp_filepath = os.path.join(voiceprint_dir, temp_filename)
|
|
||||||
file.save(temp_filepath)
|
file.save(temp_filepath)
|
||||||
original_file_path = temp_filepath
|
original_file_path = temp_filepath
|
||||||
|
|
||||||
@ -150,8 +185,7 @@ def create_voiceprint():
|
|||||||
converted_file_path = convert_to_wav(temp_filepath)
|
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 = _make_final_voiceprint_path(voiceprint_dir, name)
|
||||||
final_filepath = os.path.join(voiceprint_dir, final_filename)
|
|
||||||
|
|
||||||
# 如果转换后的文件不是原始文件,则移动
|
# 如果转换后的文件不是原始文件,则移动
|
||||||
if converted_file_path != temp_filepath:
|
if converted_file_path != temp_filepath:
|
||||||
@ -242,8 +276,7 @@ def update_voiceprint(voiceprint_id):
|
|||||||
os.makedirs(voiceprint_dir, exist_ok=True)
|
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 = _make_temp_audio_path(voiceprint_dir, file)
|
||||||
temp_filepath = os.path.join(voiceprint_dir, temp_filename)
|
|
||||||
file.save(temp_filepath)
|
file.save(temp_filepath)
|
||||||
original_file_path = temp_filepath
|
original_file_path = temp_filepath
|
||||||
|
|
||||||
@ -251,8 +284,7 @@ def update_voiceprint(voiceprint_id):
|
|||||||
converted_file_path = convert_to_wav(temp_filepath)
|
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 = _make_final_voiceprint_path(voiceprint_dir, name)
|
||||||
final_filepath = os.path.join(voiceprint_dir, final_filename)
|
|
||||||
|
|
||||||
# 如果转换后的文件不是原始文件,则移动
|
# 如果转换后的文件不是原始文件,则移动
|
||||||
if converted_file_path != temp_filepath:
|
if converted_file_path != temp_filepath:
|
||||||
|
|||||||
@ -1109,7 +1109,7 @@
|
|||||||
style="background-color: #fff;border-radius: 4px !important;"></textarea> -->
|
style="background-color: #fff;border-radius: 4px !important;"></textarea> -->
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class="btn" onclick="closeModal()">取消</button>
|
<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>
|
style="color: #fff;">保存</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1791,10 +1791,11 @@
|
|||||||
let voices = []; // 存储声纹数据
|
let voices = []; // 存储声纹数据
|
||||||
let editIndex = null;
|
let editIndex = null;
|
||||||
let deleteIndex = null;
|
let deleteIndex = null;
|
||||||
let mediaRecorder;
|
let mediaRecorder;
|
||||||
let audioChunks = [];
|
let audioChunks = [];
|
||||||
let isRecording = false;
|
let isRecording = false;
|
||||||
let modetypes = "add"
|
let isSavingVoice = false;
|
||||||
|
let modetypes = "add"
|
||||||
let modechooseIndex = 0
|
let modechooseIndex = 0
|
||||||
// 打开添加/编辑弹框
|
// 打开添加/编辑弹框
|
||||||
async function openModal(mode, index) {
|
async function openModal(mode, index) {
|
||||||
@ -1851,8 +1852,11 @@ async function synchronize() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
// 保存声纹信息
|
// 保存声纹信息
|
||||||
async function saveVoice() {
|
async function saveVoice() {
|
||||||
const name = document.getElementById('voiceName').value.trim();
|
if (isSavingVoice) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const name = document.getElementById('voiceName').value.trim();
|
||||||
// const desc
|
// const desc
|
||||||
// = document.getElementById('voiceDesc').value.trim();
|
// = document.getElementById('voiceDesc').value.trim();
|
||||||
const fileInput = document.getElementById('voiceFile').files[0];
|
const fileInput = document.getElementById('voiceFile').files[0];
|
||||||
@ -1879,9 +1883,17 @@ console.log("audioBlob:", audioBlob);
|
|||||||
|
|
||||||
// 使用 FormData 发送音频文件
|
// 使用 FormData 发送音频文件
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("name", name);
|
formData.append("name", name);
|
||||||
formData.append("voice_data", audioBlob);
|
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') {
|
if (modetypes == 'add') {
|
||||||
try {
|
try {
|
||||||
@ -1901,9 +1913,15 @@ console.log("audioBlob:", audioBlob);
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
// console.error("上传出错:", err);
|
// console.error("上传出错:", err);
|
||||||
// alert("上传失败:" + err.message);
|
// 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 {
|
} else {
|
||||||
try {
|
try {
|
||||||
let v = voices[modechooseIndex]
|
let v = voices[modechooseIndex]
|
||||||
@ -1926,8 +1944,14 @@ console.log("audioBlob:", audioBlob);
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
// console.error("上传出错:", err);
|
// console.error("上传出错:", err);
|
||||||
// alert("上传失败:" + err.message);
|
// 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) {
|
function showMessage(msg, type, duration = 3000) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user