Initial commit: SmallWhisper - web app per trascrizione audio con faster-whisper

This commit is contained in:
truetype committed 2026-06-25 20:30:34 +02:00
commit 98b0e8f198
6 files changed
+274

No files matched your search

+126
View File
@@ -0,0 +1,126 @@
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)