faster-whisper-server
faster-whisper-server copied to clipboard
faster-whisper-server suddenly broken (ValueError: max() iterable argument is empty)
I've been using faster-whisper-server via Docker for weeks with no issues with my transcription script on Ubuntu, but suddenly the server is just broken.
I get this error, whenever I try to transcribe something:
INFO: Started server process [1]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
2024-09-03 10:54:59,418:DEBUG:faster_whisper_server.logger:load_model:Loading Systran/faster-whisper-large-v2...
2024-09-03 10:55:02,625:INFO:faster_whisper_server.logger:load_model:Loaded Systran/faster-whisper-large-v2 loaded in 3.21 seconds. auto(default) will be used for inference.
INFO: 172.17.0.1:45646 - "POST /v1/audio/transcriptions HTTP/1.1" 500 Internal Server Error
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "/usr/local/lib/python3.12/dist-packages/uvicorn/protocols/http/h11_impl.py", line 406, in run_asgi
result = await app( # type: ignore[func-returns-value]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/uvicorn/middleware/proxy_headers.py", line 70, in __call__
return await self.app(scope, receive, send)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/fastapi/applications.py", line 1054, in __call__
await super().__call__(scope, receive, send)
File "/usr/local/lib/python3.12/dist-packages/starlette/applications.py", line 123, in __call__
await self.middleware_stack(scope, receive, send)
File "/usr/local/lib/python3.12/dist-packages/starlette/middleware/errors.py", line 186, in __call__
raise exc
File "/usr/local/lib/python3.12/dist-packages/starlette/middleware/errors.py", line 164, in __call__
await self.app(scope, receive, _send)
File "/usr/local/lib/python3.12/dist-packages/starlette/middleware/exceptions.py", line 65, in __call__
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
File "/usr/local/lib/python3.12/dist-packages/starlette/_exception_handler.py", line 64, in wrapped_app
raise exc
File "/usr/local/lib/python3.12/dist-packages/starlette/_exception_handler.py", line 53, in wrapped_app
await app(scope, receive, sender)
File "/usr/local/lib/python3.12/dist-packages/starlette/routing.py", line 754, in __call__
await self.middleware_stack(scope, receive, send)
File "/usr/local/lib/python3.12/dist-packages/starlette/routing.py", line 774, in app
await route.handle(scope, receive, send)
File "/usr/local/lib/python3.12/dist-packages/starlette/routing.py", line 295, in handle
await self.app(scope, receive, send)
File "/usr/local/lib/python3.12/dist-packages/starlette/routing.py", line 77, in app
await wrap_app_handling_exceptions(app, request)(scope, receive, send)
File "/usr/local/lib/python3.12/dist-packages/starlette/_exception_handler.py", line 64, in wrapped_app
raise exc
File "/usr/local/lib/python3.12/dist-packages/starlette/_exception_handler.py", line 53, in wrapped_app
await app(scope, receive, sender)
File "/usr/local/lib/python3.12/dist-packages/starlette/routing.py", line 74, in app
response = await f(request)
^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/fastapi/routing.py", line 297, in app
raw_response = await run_endpoint_function(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/fastapi/routing.py", line 212, in run_endpoint_function
return await run_in_threadpool(dependant.call, **values)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/starlette/concurrency.py", line 42, in run_in_threadpool
return await anyio.to_thread.run_sync(func, *args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/anyio/to_thread.py", line 56, in run_sync
return await get_async_backend().run_sync_in_worker_thread(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/anyio/_backends/_asyncio.py", line 2177, in run_sync_in_worker_thread
return await future
^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/anyio/_backends/_asyncio.py", line 859, in run
result = context.run(func, *args)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/root/faster-whisper-server/faster_whisper_server/main.py", line 285, in transcribe_file
segments, transcription_info = whisper.transcribe(
^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/faster_whisper/transcribe.py", line 419, in transcribe
language = max(
^^^^
ValueError: max() iterable argument is empty
I updated to the latest Docker image, but I get the same error. Would greatly appreciate any help, since I use faster-whisper-server every day and it's crucial for my workflow.
Here is my keyboard-shortcut triggered transcription script that I've been using with faster-whisper-server:
import subprocess
import os
import logging
import time
import requests
import threading
import dbus
import signal
# Configure logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
audio_file = "/tmp/audio_recording.wav"
recording_state_file = "/tmp/recording_state"
volume_state_file = "/tmp/volume_state"
def get_current_volume():
result = subprocess.run(["pactl", "get-sink-volume", "@DEFAULT_SINK@"], capture_output=True, text=True)
for line in result.stdout.split('\n'):
if 'Volume:' in line:
return line.split('/')[1].strip().rstrip('%')
return None
def set_volume(volume):
subprocess.run(["pactl", "set-sink-volume", "@DEFAULT_SINK@", f"{volume}%"])
def smooth_volume_change(start_volume, end_volume, duration=0.5, steps=10):
volume_change = end_volume - start_volume
step_size = volume_change / steps
step_duration = duration / steps
current_volume = start_volume
for _ in range(steps):
current_volume += step_size
set_volume(int(current_volume))
time.sleep(step_duration)
def start_recording():
logging.debug("Starting recording...")
current_volume = int(get_current_volume())
with open(volume_state_file, 'w') as f:
f.write(str(current_volume))
smooth_volume_change(current_volume, 20, duration=0.25)
subprocess.Popen(["ffmpeg", "-f", "pulse", "-i", "default", "-y", audio_file])
open(recording_state_file, 'w').close()
def stop_recording():
logging.debug("Stopping recording...")
# Send SIGTERM to ffmpeg
ffmpeg_process = subprocess.run(["pgrep", "ffmpeg"], capture_output=True, text=True)
if ffmpeg_process.stdout:
pid = int(ffmpeg_process.stdout.strip())
os.kill(pid, signal.SIGTERM)
# Wait for ffmpeg to finish gracefully (max 3 seconds)
for _ in range(30):
if subprocess.run(["pgrep", "ffmpeg"], capture_output=True).returncode != 0:
break
time.sleep(0.1)
else:
# If ffmpeg hasn't terminated after 3 seconds, force kill it
subprocess.call(["pkill", "-9", "ffmpeg"])
# Restore original volume smoothly
if os.path.exists(volume_state_file):
with open(volume_state_file, 'r') as f:
original_volume = int(f.read().strip())
current_volume = int(get_current_volume())
smooth_volume_change(current_volume, original_volume, duration=0.5)
os.remove(volume_state_file)
if os.path.exists(recording_state_file):
os.remove(recording_state_file)
# Add a small delay before transcription to ensure the file is completely written
time.sleep(0.05)
transcribe_audio()
if os.path.exists(audio_file):
os.remove(audio_file)
def is_recording():
return os.path.exists(recording_state_file)
def send_temporary_notification(title, message, duration=5):
bus = dbus.SessionBus()
notify_obj = bus.get_object('org.freedesktop.Notifications', '/org/freedesktop/Notifications')
notify_iface = dbus.Interface(notify_obj, 'org.freedesktop.Notifications')
notification_id = notify_iface.Notify('', 0, '', title, message, [], {}, -1)
def close_notification():
time.sleep(duration)
notify_iface.CloseNotification(notification_id)
threading.Thread(target=close_notification).start()
def transcribe_audio():
if os.path.exists(audio_file) and os.path.getsize(audio_file) > 0:
try:
logging.debug(f"Audio file found: {audio_file} (size: {os.path.getsize(audio_file)} bytes)")
url = "http://localhost:8000/v1/audio/transcriptions"
files = {'file': open(audio_file, 'rb')}
data = {'model': 'Systran/faster-whisper-large-v2'}
response = requests.post(url, files=files, data=data)
if response.status_code == 200:
transcription = response.json()['text']
logging.debug("Transcription completed. Copying to clipboard...")
subprocess.run(["xclip", "-selection", "clipboard"], input=transcription.encode(), check=True)
logging.debug("Transcription copied to clipboard.")
send_temporary_notification("Transcription Complete", "The transcription has been copied to the clipboard.", 5)
else:
logging.error(f"Error during transcription: {response.text}")
except Exception as e:
logging.error(f"Error during transcription: {e}")
else:
logging.error("Audio file not found or is empty. Skipping transcription.")
def main():
if is_recording():
stop_recording()
else:
start_recording()
if __name__ == "__main__":
main()