File size: 4,284 Bytes
d6fcf92
 
 
 
 
 
 
 
 
 
 
 
1323577
d6fcf92
 
 
 
 
 
 
2e0855f
 
 
 
 
 
 
 
d6fcf92
2e0855f
d6fcf92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d0ae679
d6fcf92
 
 
 
 
 
d0ae679
 
 
 
 
 
 
 
d6fcf92
 
 
 
 
 
 
 
9cbfee0
d6fcf92
2e0855f
d6fcf92
2e0855f
d6fcf92
 
 
 
 
 
046ebfc
 
2e0855f
d6fcf92
 
 
 
 
 
 
2e0855f
d6fcf92
 
 
2e0855f
d6fcf92
2e0855f
d6fcf92
2e0855f
d6fcf92
 
 
 
 
 
2e0855f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import asyncio
import os
import ffmpeg
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from dotenv import load_dotenv
from translator import VoiceTranslator

load_dotenv()

app = FastAPI()
#app.mount("/static", StaticFiles(directory="static"), name="static")

# Load environment variables for API keys
google_creds = os.getenv("GOOGLE_APPLICATION_CREDENTIALS")
deepl_key = os.getenv("DEEPL_API_KEY")
eleven_key = os.getenv("ELEVENLABS_API_KEY")
voice_id = os.getenv("ELEVENLABS_VOICE_ID")

# --- Start Debug Prints ---
print("--- API Key Status ---")
print(f"GOOGLE_APPLICATION_CREDENTIALS loaded: {bool(google_creds)}")
print(f"DEEPL_API_KEY loaded: {bool(deepl_key)}")
print(f"ELEVENLABS_API_KEY loaded: {bool(eleven_key)}")
print(f"ELEVENLABS_VOICE_ID loaded: {bool(voice_id)}")
print("----------------------")

if not all([google_creds, deepl_key, eleven_key, voice_id]):
    raise ValueError("CRITICAL: Missing one or more required API keys. Please check your Hugging Face secrets.")

translator = VoiceTranslator(deepl_key, eleven_key, voice_id)

@app.get("/")
async def get():
    return HTMLResponse(open("index.html", "r").read())

async def audio_output_sender(ws: WebSocket, output_queue: asyncio.Queue):
    print("Audio output sender started.")
    while True:
        try:
            audio_chunk = await output_queue.get()
            if audio_chunk is None:
                break
            await ws.send_bytes(audio_chunk)
        except asyncio.CancelledError:
            break
    print("Audio output sender stopped.")

async def handle_audio_input(websocket: WebSocket, input_queue: asyncio.Queue):
    print("Audio input handler started.")
    while True:
        try:
            data = await websocket.receive_bytes()
            # Use ffmpeg to convert webm/opus audio from browser to raw pcm
            process = (
                ffmpeg
                .input('pipe:0')
                .output('pipe:1', format='s16le', acodec='pcm_s16le', ac=1, ar='16k')
                .run_async(pipe_stdin=True, pipe_stdout=True, pipe_stderr=True)
            )
            
            # Write the audio data to ffmpeg's stdin and close it
            process.stdin.write(data)
            process.stdin.close()
            
            # Read the converted PCM data from ffmpeg's stdout
            pcm_data = process.stdout.read()
            await input_queue.put(pcm_data)
        except WebSocketDisconnect:
            break
        except Exception as e:
            print(f"Audio input error: {e}")
            break
    print("Audio input handler stopped.")


@app.websocket("/ws") # This was correct, the error was in the old HTML. No change needed here, but confirming it's /ws.
async def websocket_endpoint(websocket: WebSocket):
    print("[SERVER] WebSocket endpoint called. Awaiting connection...")
    await websocket.accept()
    print("[SERVER] WebSocket connection accepted.")

    output_sender_task = None
    input_handler_task = None

    try:
        # Start translation and audio processing tasks
        print("[SERVER] Awaiting translator.start_translation()...")
        await translator.start_translation()
        print("[SERVER] translator.start_translation() returned. Creating tasks...")
        output_sender_task = asyncio.create_task(
            audio_output_sender(websocket, translator.output_queue)
        )
        input_handler_task = asyncio.create_task(
            handle_audio_input(websocket, translator.input_queue)
        )

        print("[SERVER] Awaiting asyncio.gather for I/O tasks...")
        await asyncio.gather(input_handler_task, output_sender_task)

    except WebSocketDisconnect:
        print("[SERVER] Client disconnected via WebSocketDisconnect.")
    except Exception as e:
        print(f"[SERVER] An error occurred in websocket_endpoint: {e}")
    finally:
        print("[SERVER] Cleaning up tasks and stopping translation...")
        if output_sender_task:
            output_sender_task.cancel()
        if input_handler_task:
            input_handler_task.cancel()
        translator.stop_translation()
        await websocket.close()
        print("[SERVER] WebSocket connection closed.")