Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

faster-whisper-server suddenly broken (ValueError: max() iterable argument is empty) #70

Open
Arche151 opened this issue Sep 3, 2024 · 4 comments

Comments

@Arche151
Copy link

Arche151 commented Sep 3, 2024

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()

@Arche151
Copy link
Author

Arche151 commented Sep 3, 2024

@luochen1990 suggested setting the audio language as a workaround in this bug report: #59 but thatÄs not an option for me, unfortunately, since I use faster-whisper-server with multiple languages.,

@Arche151 Arche151 closed this as completed Sep 3, 2024
@luochen1990
Copy link

@Arche151 So maybe we should keep this issue open if it is not solved ?

@Arche151
Copy link
Author

Arche151 commented Sep 3, 2024

@luochen1990 The mistake was on my side. I had my microphone off ^^ After enabling it again, everything worked fine.

@thiswillbeyourgithub
Copy link
Contributor

I have the same error but I think it's actually working still? Like IIRC I see that often in the logs but still seem to get an answer.

@fedirz fedirz reopened this Sep 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants