This commit is contained in:
DelLevin-Home
2026-06-17 10:43:33 +08:00
parent db033c661f
commit 05e733e872
18 changed files with 3399 additions and 254 deletions

View File

@@ -156,6 +156,93 @@ def analyze():
})
@bp.route('/manual-slice', methods=['POST'])
def manual_slice():
data = request.get_json()
task_id = data.get('task_id')
task = _tasks.get(task_id)
if not task:
return jsonify({'success': False, 'error': '任务不存在'}), 404
cut_points = sorted(data.get('cut_points', []))
sr = task['sr']
total = task['total']
duration = task['duration']
valid = [p for p in cut_points if 0 < p < duration]
if not valid:
return jsonify({'success': False, 'error': '没有有效的切割点'}), 400
samples = [int(round(p * sr)) for p in valid]
boundaries = [0] + samples + [total]
ranges = [(boundaries[i], boundaries[i + 1])
for i in range(len(boundaries) - 1)
if boundaries[i + 1] > boundaries[i]]
task['ranges'] = ranges
task['status'] = 'analyzed'
preview = [{'index': i, 'duration': round((e - b) / sr, 2), 'samples': e - b}
for i, (b, e) in enumerate(ranges)]
return jsonify({'success': True, 'count': len(ranges), 'preview': preview,
'sample_rate': int(sr), 'channels': int(task['ch'])})
@bp.route('/preview-range/<task_id>')
def preview_range(task_id):
task = _tasks.get(task_id)
if not task:
return jsonify({'error': '任务不存在'}), 404
try:
start = float(request.args.get('start', 0))
end = float(request.args.get('end', task['duration']))
except (ValueError, TypeError):
return jsonify({'error': '参数错误'}), 400
sr = task['sr']
ch = task['ch']
begin = max(0, int(start * sr))
end_sample = min(task['total'], int(end * sr))
if end_sample <= begin:
return jsonify({'error': '无效范围'}), 400
buf = io.BytesIO()
with soundfile.SoundFile(task['src']) as src:
src.seek(begin)
frames = end_sample - begin
data = src.read(frames)
with soundfile.SoundFile(buf, mode='w', samplerate=sr, channels=ch,
format='WAV') as dst:
dst.write(data)
buf.seek(0)
return send_file(buf, mimetype='audio/wav')
@bp.route('/waveform-peaks/<task_id>')
def waveform_peaks(task_id):
"""返回波形峰值数据,供前端绘制波形图"""
task = _tasks.get(task_id)
if not task:
return jsonify({'error': '任务不存在'}), 404
num_samples = int(request.args.get('samples', 400))
with soundfile.SoundFile(task['src']) as f:
sr = f.samplerate
ch = f.channels
total = len(f)
samples_per_bucket = max(1, total // num_samples)
peaks = []
for i in range(num_samples):
start = i * samples_per_bucket
length = min(samples_per_bucket, total - start)
if length <= 0:
break
data = f.read(length)
if ch > 1:
data = data.mean(axis=1)
peaks.append(float(abs(data).max()))
return jsonify({'peaks': peaks, 'duration': task['duration'], 'sr': sr})
@bp.route('/slice', methods=['POST'])
def start_slice():
data = request.get_json()
@@ -166,6 +253,16 @@ def start_slice():
if not task.get('ranges'):
return jsonify({'success': False, 'error': '请先分析'}), 400
# 支持选择性切割
selected = data.get('selected_indices')
all_ranges = task['ranges']
if selected is not None and isinstance(selected, list) and len(selected) > 0:
ranges = [all_ranges[i] for i in selected if 0 <= i < len(all_ranges)]
else:
ranges = all_ranges
if not ranges:
return jsonify({'success': False, 'error': '未选择任何片段'}), 400
task['status'] = 'slicing'
task['progress'] = 0
task['slices'] = []
@@ -176,7 +273,6 @@ def start_slice():
def _do_slice():
base = os.path.splitext(task['orig_name'])[0]
ranges = task['ranges']
total = len(ranges)
for i, (begin, end) in enumerate(ranges):
out_path = os.path.join(out_dir, f'{base}_{i:03d}.wav')