[Python] Constant 503 502:Bad Gateway Exception During Audio Syntheses

Discussão em 'Python' iniciado por Stack, Setembro 4, 2025.

  1. Stack

    Stack Membro Participativo

    While running the following code I receiving 503 bad gateway errors for seemingly no reason. The error output is attached after the code. I have tried creating a client from the credentials saved in the environment as well as creating the client using a service account file. I have looked at my Google Cloud Console and see that there errors are being detected, but no info about why this is. Under the "APIs & Services" section of my project, I see the following:

    [​IMG]

    My quota for "Count of requests for Chirp3-HD voices per minute" is at 0.5%. Any help or advice is appreciated. Thanks!

    #!/usr/bin/env python3

    import logging
    import os
    import argparse
    from sys import argv, stdin, exit
    from random import random
    from typing import List
    from google.cloud import texttospeech
    from google.oauth2 import service_account
    from google.api_core.retry import Retry, if_transient_error

    logging.basicConfig(level=logging.INFO)

    def get_client(credentials_file: str = None) -> texttospeech.TextToSpeechClient:
    """Returns text to speech client object."""

    if credentials_file != None:
    creds = service_account.Credentials.from_service_account_file(credentials_file)
    return texttospeech.TextToSpeechClient(credentials=creds)

    else:
    return texttospeech.TextToSpeechClient()

    def chunk_text(text: str, delimiter: str = "\n", max_chars: int = 5000) -> List[str]:
    """
    Splits text into chunks divided by delimiter.
    Chunks are further split if more than `max_chars` characters.
    Splitting is done by delimiter boundaries when possible.
    """

    sentences = text.split(delimiter)
    chunks, current_chunk = [], ""

    for sentence in sentences:
    candidate = current_chunk + sentence + delimiter

    if len(candidate) > max_chars:
    chunks.append(current_chunk)
    current_chunk = sentence + delimiter

    else:
    current_chunk = candidate

    if current_chunk:
    chunks.append(current_chunk)

    return chunks

    def generate_paths(
    project_name: str,
    num_chapters: int,
    extension: str = "wav",
    pad_width: int = 2,
    ) -> List[str]:
    """Generate paths for chapter outputs with consistent structure."""

    return [
    f"output/{project_name}/chapters/chapter{str(i).zfill(pad_width)}.{extension}"
    for i in range(1, num_chapters + 1)
    ]

    def synthesize_audio(
    client: texttospeech.TextToSpeechClient,
    project_name: str,
    input_text: str,
    speaking_rate: float = 0.85,
    language_code: str = "en-US",
    voice_name: str = "en-US-Chirp3-HD-Algenib",
    skip_chapters: List[int] = None
    ) -> List[str]:
    """
    Splits long input into chapters and synthesizes each to a GCS bucket.
    Files are saved to: gs://bucket/project_name/chapters/chapterXX.wav
    Returns list of GCS paths for generated files.
    """

    # Break text into manageable chunks
    chunks = chunk_text(
    text = input_text
    )

    logging.info("Split input into %d chapters.", len(chunks))

    # Generate output paths using helper
    chapter_paths = generate_paths(project_name, len(chunks))

    audio_config = texttospeech.AudioConfig(
    audio_encoding = texttospeech.AudioEncoding.LINEAR16,
    speaking_rate = speaking_rate,
    )

    voice = texttospeech.VoiceSelectionParams(
    language_code = language_code,
    name = voice_name,
    )

    # Define a custom retry policy for transient errors (like 502 Bad Gateway)
    # This policy will retry the call with exponential backoff for up to 120 seconds.
    custom_retry = Retry(
    initial = 1.0, # The minimum amount of time to delay.
    maximum = 60.0, # The maximum amount of time to delay.
    multiplier = 2.0, # The multiplier applied to the delay.
    deadline = 120.0, # How long to keep retrying.
    predicate = if_transient_error # The default predicate for transient errors.
    )

    for idx, (chunk, output_path) in enumerate(zip(chunks, chapter_paths), start=1):
    if skip_chapters != None and idx in skip_chapters:
    logging.info("Skipping chapter %d", idx)
    continue

    synthesis_input = texttospeech.SynthesisInput(text=chunk)

    logging.info("Synthesizing chapter %d", idx)

    response = client.synthesize_speech(
    input = synthesis_input,
    voice = voice,
    audio_config = audio_config,
    retry = custom_retry
    )

    logging.info("Synthesizing chapter %d pt. 2", idx)

    try:
    os.makedirs(os.path.dirname(output_path), exist_ok=True)

    with open(output_path, "wb") as out:
    out.write(response.audio_content)
    logging.info("Saved chapter %d at %s", idx, output_path)

    except Exception as e:
    logging.error("Error on chapter %d: %s", idx, e)

    return chapter_paths

    def main():
    """
    Parses command-line arguments, reads input text, and synthesizes
    long audio files using Google Cloud Text-to-Speech.

    The script supports two modes of operation for input:
    1. Reading from a file specified by the `--file-path` argument.
    2. Reading from standard input (stdin) if no file path is provided.

    The output audio is split into manageable chapters and saved to
    the 'output' directory in WAV format. The output directory
    structure is `output/<project_name>/chapters/chapterXX.wav`.

    The `--skip-chapters` argument can be used to restart or resume
    a long synthesis job without reprocessing all chapters.

    Example usage:
    - Synthesize text from a file:
    `python tts.py --file-path "my_book.txt"`

    - Synthesize text from standard input:
    `cat "my_book_chapter.txt" | python tts.py`

    - Synthesize from a file, skipping chapters 1 and 3:
    `python tts.py --file-path "my_book.txt" --skip-chapters 1 3`
    """

    parser = argparse.ArgumentParser(
    description="Synthesizes text into speech using Google Cloud TTS."
    )

    # Optional file path for input text
    parser.add_argument(
    "--file-path",
    type=str,
    help="Path to an input text file. If not provided, reads from stdin."
    )

    # Optional chapters to skip
    parser.add_argument(
    "--skip-chapters",
    type=int,
    nargs="+", # This allows multiple integers
    help="A space-separated list of chapter numbers to skip (e.g., 1 3 5)."
    )

    args = parser.parse_args()

    input_text = ""
    if args.file_path:
    project_name = os.path.basename(args.file_path).rpartition(".")[0]

    try:
    with open(args.file_path, "r") as f:
    input_text = f.read()

    except FileNotFoundError:
    logging.error(f"Error: The file '{args.file_path}' was not found.")
    exit(1)

    else:
    project_name = str(int(random()*(10**32)))

    try:
    for line in stdin:
    input_text += line

    except KeyboardInterrupt:
    exit(1)

    client = get_client() #get_client(credentials_file=credentials_file)

    synthesize_audio(
    client=client,
    project_name=project_name,
    input_text=input_text,
    skip_chapters=args.skip_chapters
    )

    if __name__ == "__main__":
    main()


    INFO:root:Split input into 11 chapters.
    INFO:root:Synthesizing chapter 1
    Traceback (most recent call last):
    File "/var/home/connor/.local/lib/python3.13/site-packages/google/api_core/grpc_helpers.py", line 76, in error_remapped_callable
    return callable_(*args, **kwargs)
    File "/var/home/connor/.local/lib/python3.13/site-packages/grpc/_interceptor.py", line 277, in __call__
    response, ignored_call = self._with_call(
    ~~~~~~~~~~~~~~~^
    request,
    ^^^^^^^^
    ...<4 lines>...
    compression=compression,
    ^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
    File "/var/home/connor/.local/lib/python3.13/site-packages/grpc/_interceptor.py", line 332, in _with_call
    return call.result(), call
    ~~~~~~~~~~~^^
    File "/var/home/connor/.local/lib/python3.13/site-packages/grpc/_channel.py", line 440, in result
    raise self
    File "/var/home/connor/.local/lib/python3.13/site-packages/grpc/_interceptor.py", line 315, in continuation
    response, call = self._thunk(new_method).with_call(
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
    request,
    ^^^^^^^^
    ...<4 lines>...
    compression=new_compression,
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
    File "/var/home/connor/.local/lib/python3.13/site-packages/grpc/_channel.py", line 1192, in with_call
    return _end_unary_response_blocking(state, call, True, None)
    File "/var/home/connor/.local/lib/python3.13/site-packages/grpc/_channel.py", line 1006, in _end_unary_response_blocking
    raise _InactiveRpcError(state) # pytype: disable=not-instantiable
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
    status = StatusCode.UNAVAILABLE
    details = "502:Bad Gateway"
    debug_error_string = "UNKNOWN:Error received from peer {grpc_status:14, grpc_message:"502:Bad Gateway"}"
    >

    The above exception was the direct cause of the following exception:

    Traceback (most recent call last):
    File "/var/home/connor/.local/lib/python3.13/site-packages/google/api_core/retry/retry_unary.py", line 147, in retry_target
    result = target()
    File "/var/home/connor/.local/lib/python3.13/site-packages/google/api_core/grpc_helpers.py", line 78, in error_remapped_callable
    raise exceptions.from_grpc_error(exc) from exc
    google.api_core.exceptions.ServiceUnavailable: 503 502:Bad Gateway

    The above exception was the direct cause of the following exception:

    Traceback (most recent call last):
    File "/var/home/connor/Documents/Classes/Junior Year/A Term/International Politics: Ghana/TTS/./tts.py", line 218, in <module>
    main()
    ~~~~^^
    File "/var/home/connor/Documents/Classes/Junior Year/A Term/International Politics: Ghana/TTS/./tts.py", line 210, in main
    synthesize_audio(
    ~~~~~~~~~~~~~~~~^
    client=client,
    ^^^^^^^^^^^^^^
    ...<2 lines>...
    skip_chapters=args.skip_chapters
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
    File "/var/home/connor/Documents/Classes/Junior Year/A Term/International Politics: Ghana/TTS/./tts.py", line 117, in synthesize_audio
    response = client.synthesize_speech(
    input = synthesis_input,
    ...<2 lines>...
    retry = custom_retry
    )
    File "/var/home/connor/.local/lib/python3.13/site-packages/google/cloud/texttospeech_v1/services/text_to_speech/client.py", line 946, in synthesize_speech
    response = rpc(
    request,
    ...<2 lines>...
    metadata=metadata,
    )
    File "/var/home/connor/.local/lib/python3.13/site-packages/google/api_core/gapic_v1/method.py", line 131, in __call__
    return wrapped_func(*args, **kwargs)
    File "/var/home/connor/.local/lib/python3.13/site-packages/google/api_core/retry/retry_unary.py", line 294, in retry_wrapped_func
    return retry_target(
    target,
    ...<3 lines>...
    on_error=on_error,
    )
    File "/var/home/connor/.local/lib/python3.13/site-packages/google/api_core/retry/retry_unary.py", line 156, in retry_target
    next_sleep = _retry_error_helper(
    exc,
    ...<6 lines>...
    timeout,
    )
    File "/var/home/connor/.local/lib/python3.13/site-packages/google/api_core/retry/retry_base.py", line 229, in _retry_error_helper
    raise final_exc from source_exc
    google.api_core.exceptions.RetryError: Timeout of 120.0s exceeded, last exception: 503 502:Bad Gateway

    Continue reading...

Compartilhe esta Página