127 lines
4.0 KiB
Python
127 lines
4.0 KiB
Python
import os
|
|
import uuid
|
|
import threading
|
|
from flask import Flask, request, render_template, send_file, url_for
|
|
from faster_whisper import WhisperModel
|
|
|
|
app = Flask(__name__)
|
|
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024 # 100 MB limit
|
|
app.secret_key = os.urandom(24)
|
|
|
|
# ── Whisper model ───────────────────────────────────────────────
|
|
# Modello small (1.5 GB) — ottimo per italiano, veloce su CPU.
|
|
# Cambia in "base" o "tiny" se vuoi più velocità, "medium" per più accuratezza.
|
|
WHISPER_MODEL = "small"
|
|
|
|
print(f"[INFO] Caricamento modello Whisper '{WHISPER_MODEL}'...")
|
|
whisper = WhisperModel(WHISPER_MODEL, device="cpu", compute_type="int8")
|
|
print("[INFO] Modello caricato!")
|
|
|
|
# ── Job tracking (in memoria) ───────────────────────────────────
|
|
jobs = {}
|
|
|
|
# ── Routes ──────────────────────────────────────────────────────
|
|
|
|
@app.route('/')
|
|
def index():
|
|
return render_template('index.html')
|
|
|
|
|
|
@app.route('/upload', methods=['POST'])
|
|
def upload():
|
|
if 'audio' not in request.files:
|
|
return {'error': 'Nessun file audio'}, 400
|
|
file = request.files['audio']
|
|
if file.filename == '':
|
|
return {'error': 'Nome file vuoto'}, 400
|
|
|
|
job_id = str(uuid.uuid4())
|
|
os.makedirs('tmp', exist_ok=True)
|
|
tmp_path = os.path.join('tmp', f'{job_id}.m4a')
|
|
file.save(tmp_path)
|
|
|
|
jobs[job_id] = {
|
|
'status': 'running',
|
|
'progress': 0,
|
|
'message': 'Caricato',
|
|
'download': None,
|
|
}
|
|
|
|
threading.Thread(target=job_worker, args=(job_id, tmp_path), daemon=True).start()
|
|
|
|
return {'statusUrl': url_for('status', job_id=job_id)}
|
|
|
|
|
|
@app.route('/status/<job_id>')
|
|
def status(job_id):
|
|
job = jobs.get(job_id)
|
|
if not job:
|
|
return {'error': 'Job non trovato'}, 404
|
|
|
|
# Mentre whisper lavora, mostriamo "Trascrizione in corso..." con progress 50
|
|
if job.get('status') == 'running' and job.get('progress', 0) < 100:
|
|
return {
|
|
'status': 'running',
|
|
'progress': 50,
|
|
'message': 'Trascrizione in corso...',
|
|
'download': None,
|
|
}
|
|
|
|
return {
|
|
'status': job.get('status'),
|
|
'progress': job.get('progress', 100),
|
|
'message': job.get('message'),
|
|
'download': job.get('download'),
|
|
}
|
|
|
|
|
|
@app.route('/download/<job_id>.txt')
|
|
def download(job_id):
|
|
txt_path = os.path.join('tmp', f'trascrizione_{job_id}.txt')
|
|
if not os.path.exists(txt_path):
|
|
return {'error': 'File non trovato'}, 404
|
|
return send_file(
|
|
txt_path,
|
|
as_attachment=True,
|
|
download_name='trascrizione.txt',
|
|
mimetype='text/plain',
|
|
)
|
|
|
|
|
|
# ── Worker (thread background) ──────────────────────────────────
|
|
|
|
def job_worker(job_id, tmp_path):
|
|
try:
|
|
jobs[job_id]['message'] = 'Trascrizione in corso...'
|
|
|
|
# Whisper trascrive direttamente dal file (supporta .m4a via ffmpeg)
|
|
segments, info = whisper.transcribe(tmp_path, language="it", beam_size=5)
|
|
|
|
transcript_parts = []
|
|
for segment in segments:
|
|
transcript_parts.append(segment.text.strip())
|
|
|
|
transcription = "\n".join(transcript_parts)
|
|
|
|
txt_path = os.path.join('tmp', f'trascrizione_{job_id}.txt')
|
|
with open(txt_path, 'w', encoding='utf-8') as f:
|
|
f.write(transcription)
|
|
|
|
jobs[job_id]['download'] = f'/download/{job_id}.txt'
|
|
jobs[job_id]['message'] = 'Completato'
|
|
jobs[job_id]['progress'] = 100
|
|
jobs[job_id]['status'] = 'done'
|
|
|
|
except Exception as e:
|
|
jobs[job_id]['status'] = 'error'
|
|
jobs[job_id]['message'] = str(e)
|
|
finally:
|
|
try:
|
|
os.remove(tmp_path)
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
if __name__ == '__main__':
|
|
app.run(host='0.0.0.0', port=5000, debug=True)
|