Bug report. Technical failure of the Gemini API Free Tier. This bug could cause severe financial damage to Google

Bug Report Recipient and Management Failure at Google

Dear Google employees and managers, dear contractors and vendor personnel for Google. This bug report is primarily intended for Google’s executive management; therefore, please forward this request and matter directly to company leadership. A rather significant management failure has occurred on Google’s side, and I presume that such serious mistakes were not intentional, but rather an oversight, as otherwise this would be an exceedingly critical issue.


The Inoperability of the Free Tier in the Google AI Studio API and Resulting Economic Losses

In practice, the API interface—and more broadly the Gemini API within Google AI Studio—lacks any functional Free Tier; there is neither a working free tier nor an accessible free tier level, which I personally consider a fundamental issue. This situation inflicts serious economic harm on Google, as developers are unable to even test the API in its free tier. In short, the service simply does not work in free mode.

A severe breakdown in communication between lower-level staff and executive management has likely occurred. It appears executive leadership was never informed that the Free Tier is virtually inoperable via API keys in Google AI Studio. Purely in theory and on a hypothetical level, it should work—official documentation states that a Free Tier exists—yet in actual practice, it is completely unavailable. This presents a massive issue that could cause damages to Google ranging from tens to hundreds of millions of euros.

Were I attending Google’s annual general meeting, harsh words would be exchanged, and concrete personnel actions would have to be taken against the responsible managers. The AI space is currently fiercely competitive, with numerous companies active in the market, and lacking a functional Free Tier for the API makes no sense, even from a marketing standpoint. It does not help Google in any way; on the contrary, it only harms it.


Comparison of the Web Graphical Interface and API Limits

I see absolutely no logic in this approach. It is likely the result of poor internal communication, where managers might not even realize what is actually taking place. They must be alerted to this so they can rectify the situation, because the complete unavailability of the free API certainly cannot be Google’s official goal.

Meanwhile, the graphical web interface of Google AI Studio functions very well, and I have never encountered any rate limits there. As an ordinary individual, I do not send thousands of requests per minute, nor even tens or hundreds per hour; thus, I have never exceeded any quotas in the graphical UI. Therefore, I cannot understand the logic of why the web interface works seamlessly without restrictions for an average user, while the API interface immediately throws errors.


Server Overload Error Messages Despite a Low Volume of Requests

In the API interface, the system almost continuously throws errors stating that servers are overloaded. This is a bizarre argument—how can servers be overloaded for API calls when the graphical interface operates without issue? Around 98% to 99% of the time, the system claims servers are experiencing high demand and will not process the request via the API.

I reiterate that I am not a large corporation with hundreds of employees, but an individual user. Under normal circumstances, I send roughly ten requests a day. Only exceptionally, when working on a minor project once every two months, do I submit around two hundred requests per day through the web UI, followed by long periods of inactivity. I am not an enterprise customer, so I do not comprehend what kind of server overload is being referred to here.

It creates the impression that the responsible manager is drawing two salaries—one official paycheck from Google and a second from competitors to sabotage their own company, because this behavior only drives users away. I trust there is no malicious intent or foul play involved, given the fierce market competition and the gravity of this flaw.


Loss of Valuable AI Model Training Data from Regular Users

Through the Free Tier, Google can acquire massive amounts of training data, as free-tier users consent to having their inputs utilized to further train and improve models. In the fierce battle among AI platforms, the volume and diversity of training data are paramount—the more quality data a model ingests, the better it becomes. The inoperability of the Free Tier for the API is therefore a colossal act of self-sabotage against Google’s own interests.

Were I a shareholder at an Alphabet shareholder meeting, the managers responsible would be dismissed on the spot for causing millions in damages. The competition does not sleep, and Google is missing out on valuable data. Even though data is filtered algorithmically, when there is a severe deficit, there is simply nothing to choose from. I personally submit data for speech-to-text processing via the API, data that you are now losing out on.

Large corporations rely on paid tiers where they contractually protect privacy and refuse to share their data for training. It is precisely the everyday individual users who willingly provide their data that represent immense value for model development. Therefore, it makes no sense that a heavy web interface runs without restrictions while the API remains blocked.


Technical Inefficiency of the Web Interface and the Demand to Increase API Limits

Your foremost priority must be to raise API limits at least to the level of the web interface, and ideally set API quotas ten to a hundred times higher than the web UI. A browser-based graphical interface puts a disproportionately higher load on servers—it carries immense amounts of unnecessary overhead and complexity. Constant token re-evaluations and intensive browser-server roundtrips (such as during audio uploads for speech-to-text transcription) generate massive overhead.

Conversely, direct API communication is far more streamlined and vastly less resource-intensive for server infrastructure. It is paradoxical and contrary to common sense that a more power- and compute-hungry web app runs for free, while a lightweight API is rendered unusable. I therefore demand an explanation from management as to why such decisions are made at the expense of shareholder value. Address your internal communication silos; otherwise, Google will rest on its laurels and competitors in artificial intelligence will rapidly overtake it.


Poor Quality of Stripped-Down Models During Forced Fallback

Occasionally, a Free Tier request does go through the API, but only if one manually resorts to the oldest model versions or stripped-down Lite releases—such as being forced to fall back on older models like Gemini 3.1 or Gemini 3.1 Lite. However, the quality of such processing is abysmal, falling to unacceptably low standards with results that are practically unusable in real-world workflows. Using outdated, throttled-down models merely to get a request through offers virtually no real utility.


Low Daily Request Volume and Immediate Server Overload Errors

Furthermore, another utterly incomprehensible oddity—and a vital detail that needs highlighting—is that my daily AI API request volume is remarkably low. In reality, it averages around ten requests per day; yet, the system incessantly insists that the servers are overloaded.


Absurdity of Outages in the Absence of Load vs. Long Audio Speech-to-Text Processing

It was never the case that I queued hundreds of hours of audio for Speech-to-Text processing and transcription, only for the system to complain about overloaded servers. Under such circumstances, I would understand—if I sent four hours of audio to Google servers for transcription, it would be entirely reasonable to see capacity limits triggered after two or four hours of audio processing.

However, that is not the case at all. This occurs right at the start of the day, even after not uploading anything for the previous 48 hours. I then send a single request, and the system immediately reports that the servers are overloaded.


General Inoperability of the Free Tier Across Tasks and the Inadequacy of Gemini 3.1 Lite

And this issue is not confined to speech-to-text transcription alone; the AI platform exhibits this behavior across the board. It does not matter whether the workload is speech transcription or any other task—a viable, usable free tier simply does not exist.

Or, if a request happens to succeed by chance, it only processes using Gemini 3.1 Lite. Needless to say, the quality of such an AI model speaks for itself if you need to accomplish anything even slightly complex.

From API script from terminal output (translated to english)

$  user@user  ~/Desktop/STT  python3 py.py 1.opus 2.opus 3.opus
Multi-file mode detected (3 files). Context will be preserved across all parts.

============================================================
--- [Part 1/3] Processing: 1.opus ---
============================================================

📡 [1/3] Initializing upload for: 1.opus (13.76 MB)
   Connection to Google server established.
🚀 [2/3] Transferring data to server...
   [█████████████████████████] 100.0% |  13.8/ 13.8 MB |  470.4 KB/s  
   ✅ File has been completely uploaded.
   Google is analyzing audio data... Active.
🎙 [3/3] Generating transcript for part 1 via model gemini-3.8-flash (Thinking: MEDIUM)...

   ⚠ Google server reports overload/error (attempt 1/52).
      Audio is SAFELY stored on the server (no reupload). Waiting 8.6s and trying again...

   ⚠ Google server reports overload/error (attempt 2/52).
      Audio is SAFELY stored on the server (no reupload). Waiting 8.4s and trying again...

   ⚠ Google server reports overload/error (attempt 3/52).
      Audio is SAFELY stored on the server (no reupload). Waiting 11.7s and trying again...

   ⚠ Google server reports overload/error (attempt 4/52).
      Audio is SAFELY stored on the server (no reupload). Waiting 13.8s and trying again...

   ⚠ Google server reports overload/error (attempt 5/52).
      Audio is SAFELY stored on the server (no reupload). Waiting 19.7s and trying again...

   ⚠ Google server reports overload/error (attempt 6/52).
      Audio is SAFELY stored on the server (no reupload). Waiting 24.5s and trying again...

   ⚠ Google server reports overload/error (attempt 7/52).
      Audio is SAFELY stored on the server (no reupload). Waiting 32.9s and trying again...

   ⚠ Google server reports overload/error (attempt 8/52).
      Audio is SAFELY stored on the server (no reupload). Waiting 44.3s and trying again...

   ⚠ Google server reports overload/error (attempt 9/52).
      Audio is SAFELY stored on the server (no reupload). Waiting 56.5s and trying again...

   ⚠ Google server reports overload/error (attempt 10/52).
      Audio is SAFELY stored on the server (no reupload). Waiting 60.0s and trying again...

   ⚠ Google server reports overload/error (attempt 11/52).
      Audio is SAFELY stored on the server (no reupload). Waiting 60.0s and trying again...


   ⚠ Google server reports overload/error (attempt 12/52).
      Audio is SAFELY stored on the server (no reupload). Waiting 60.0s and trying again...

Bug is not in script, but issue / bug is in Gemini AI system

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys
import time
import json
import random
import requests
from pathlib import Path
from requests.adapters import HTTPAdapter
from urllib3.util import Retry
from google import genai
from google.genai import types

# =====================================================================
# 🔑 INSERT YOUR GOOGLE AI STUDIO API KEY HERE
# =====================================================================
API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# =====================================================================

API_BASE_URL = "https://generativelanguage.googleapis.com"
CONNECT_TIMEOUT = 45
READ_TIMEOUT = 180
MAX_NETWORK_RETRIES = 52

# Model selection
MODEL_NAME = "gemini-3.8-flash"
MAX_GENERATION_RETRIES = 52

AUDIO_MIME_TYPES = {
    ".opus": "audio/ogg",
    ".ogg":  "audio/ogg",
    ".mp3":  "audio/mp3",
    ".m4a":  "audio/m4a",
    ".flac": "audio/flac",
    ".wav":  "audio/wav",
    ".aac":  "audio/aac",
    ".wma":  "audio/x-ms-wma",
    ".webm": "audio/webm",
}

def create_resilient_session() -> requests.Session:
    session = requests.Session()
    retries = Retry(
        total=3,
        connect=3,
        read=3,
        backoff_factor=1.5,
        status_forcelist=[502, 503, 504],
        raise_on_status=False
    )
    adapter = HTTPAdapter(max_retries=retries, pool_connections=1, pool_maxsize=1)
    session.mount("https://", adapter)
    session.mount("http://", adapter)
    return session

def get_mime_type(file_path: str) -> str:
    ext = Path(file_path).suffix.lower()
    return AUDIO_MIME_TYPES.get(ext, "application/octet-stream")

class ChunkStream:
    def __init__(self, data: bytes, offset: int, total_size: int, progress_callback):
        self.data = data
        self.total_len = len(data)
        self.pos = 0
        self.offset = offset
        self.total_size = total_size
        self.progress_callback = progress_callback

    def read(self, size: int = 8192) -> bytes:
        if self.pos >= self.total_len:
            return b""
        chunk = self.data[self.pos:self.pos + size]
        self.pos += len(chunk)
        if self.progress_callback:
            self.progress_callback(self.offset + self.pos, self.total_size)
        return chunk

    def __len__(self):
        return self.total_len

class UploadProgressTracker:
    def __init__(self):
        self.start_time = time.time()
        self.last_update = 0

    def update(self, current: int, total: int):
        now = time.time()
        if now - self.last_update < 0.1 and current < total:
            return
        self.last_update = now

        bar_len = 25
        pct = (current / total) * 100 if total > 0 else 0
        filled = int(bar_len * current // total) if total > 0 else 0
        bar = '█' * filled + '░' * (bar_len - filled)

        elapsed = now - self.start_time
        speed = (current / elapsed) / 1024 if elapsed > 0 else 0

        cur_mb = current / (1024 * 1024)
        tot_mb = total / (1024 * 1024)

        sys.stdout.write(f"\r    [{bar}] {pct:5.1f}% | {cur_mb:5.1f}/{tot_mb:5.1f} MB | {speed:6.1f} KB/s ")
        sys.stdout.flush()

def upload_file_resumable(file_path: str, api_key: str, client: genai.Client):
    file_size = os.path.getsize(file_path)
    file_name = os.path.basename(file_path)
    mime_type = get_mime_type(file_path)
    session = create_resilient_session()

    print(f"\n📡 [1/3] Initializing upload for: {file_name} ({file_size / (1024*1024):.2f} MB)")

    init_url = f"{API_BASE_URL}/upload/v1beta/files"
    init_headers = {
        "x-goog-api-key": api_key,
        "X-Goog-Upload-Protocol": "resumable",
        "X-Goog-Upload-Command": "start",
        "X-Goog-Upload-Header-Content-Length": str(file_size),
        "X-Goog-Upload-Header-Content-Type": mime_type,
        "Content-Type": "application/json"
    }
    init_body = json.dumps({"file": {"display_name": file_name}})

    upload_url = None
    chunk_granularity = 8 * 1024 * 1024

    for attempt in range(1, MAX_NETWORK_RETRIES + 1):
        try:
            resp = session.post(
                init_url,
                params={"key": api_key},
                headers=init_headers,
                data=init_body,
                timeout=(CONNECT_TIMEOUT, READ_TIMEOUT)
            )
            if resp.status_code == 200:
                upload_url = resp.headers.get("x-goog-upload-url") or resp.headers.get("X-Goog-Upload-URL")
                gran_header = resp.headers.get("x-goog-upload-chunk-granularity")
                if gran_header:
                    chunk_granularity = int(gran_header)
                print("    Connection to Google server established.")
                break
        except (requests.exceptions.RequestException, requests.exceptions.Timeout):
            time.sleep(min(attempt * 2, 20))

    if not upload_url:
        raise RuntimeError("Failed to establish upload connection with Google server.")

    print("🚀 [2/3] Uploading data to server...")
    tracker = UploadProgressTracker()
    offset = 0
    file_resource = None

    with open(file_path, "rb") as f:
        while offset < file_size:
            f.seek(offset)
            remaining = file_size - offset
            if remaining > chunk_granularity:
                chunk_data = f.read(chunk_granularity)
                cmd = "upload"
                is_last = False
            else:
                chunk_data = f.read(remaining)
                cmd = "upload, finalize"
                is_last = True

            chunk_len = len(chunk_data)
            chunk_headers = {
                "Content-Length": str(chunk_len),
                "X-Goog-Upload-Offset": str(offset),
                "X-Goog-Upload-Command": cmd
            }

            for attempt in range(1, MAX_NETWORK_RETRIES + 1):
                try:
                    stream = ChunkStream(chunk_data, offset, file_size, tracker.update)
                    chunk_resp = session.post(
                        upload_url,
                        headers=chunk_headers,
                        data=stream,
                        timeout=(CONNECT_TIMEOUT, READ_TIMEOUT)
                    )
                    if chunk_resp.status_code in (200, 201):
                        offset += chunk_len
                        if is_last:
                            file_resource = chunk_resp.json().get("file")
                        break
                except (requests.exceptions.RequestException, requests.exceptions.Timeout):
                    time.sleep(min(attempt * 2, 20))
                    try:
                        q_resp = session.post(
                            upload_url,
                            headers={"Content-Length": "0", "X-Goog-Upload-Command": "query"},
                            timeout=(CONNECT_TIMEOUT, READ_TIMEOUT)
                        )
                        server_offset = int(
                            q_resp.headers.get("x-goog-upload-size-received") or
                            q_resp.headers.get("x-goog-upload-size") or
                            offset
                        )
                        if server_offset > offset:
                            offset = server_offset
                            break
                    except Exception:
                        pass

    print("\n    ✅ File successfully uploaded.")

    file_name_api = file_resource["name"]
    print("    Google is analyzing audio data...", end="", flush=True)

    while True:
        file_info = client.files.get(name=file_name_api)
        state_str = str(file_info.state)
        if "ACTIVE" in state_str:
            print(" Active.")
            return file_info
        elif "FAILED" in state_str:
            print(" Failed.")
            raise RuntimeError(f"Audio processing for file '{file_name}' failed on Google's side.")
        print(".", end="", flush=True)
        time.sleep(3)

def generate_content_with_retry(client: genai.Client, model: str, contents: list, config: types.GenerateContentConfig, max_retries: int = 52):
    """
    Performs up to 52 attempts to generate text from an already uploaded file.
    The file is not re-uploaded – it remains stored in Google Cloud during all attempts.
    """
    base_delay = 5.0

    for attempt in range(1, max_retries + 1):
        try:
            response = client.models.generate_content(
                model=model,
                contents=contents,
                config=config
            )
            return response

        except Exception as e:
            err_str = str(e).lower()
            transient_indicators = [
                "503", "overloaded", "unavailable",
                "429", "resource_exhausted", "quota", "rate limit",
                "500", "internal", "502", "504", "gateway",
                "timeout", "connection reset", "broken pipe"
            ]
            is_transient = any(ind in err_str for ind in transient_indicators)

            if is_transient and attempt < max_retries:
                # Exponential backoff with jitter (max 60 seconds per attempt)
                sleep_time = min(base_delay * (1.35 ** (attempt - 1)) + random.uniform(1.0, 4.0), 60.0)
                print(f"\n    ⚠️ Google server reports overload/error (attempt {attempt}/{max_retries}).")
                print(f"        Audio is SAFELY stored on server (no re-upload). Waiting {sleep_time:.1f}s and retrying...")
                time.sleep(sleep_time)
            else:
                print(f"\n    ❌ Attempt {attempt}/{max_retries} failed: {e}")
                if attempt >= max_retries:
                    raise RuntimeError(f"All {max_retries} generation attempts failed due to server overload.") from e
                raise e

def print_server_diagnostics(response, file_name: str):
    """Prints structured server diagnostic info from Google Gemini."""
    print(f"\n{'-'*60}")
    print(f"Server diagnostics for file: {file_name}")
    print("Server returned no text or output was blocked.")
    print(f"{'-'*60}")

    if hasattr(response, "prompt_feedback") and response.prompt_feedback:
        pf = response.prompt_feedback
        block_reason = getattr(pf, "block_reason", None)
        if block_reason:
            print(f"Input Block Reason: {block_reason}")
        if hasattr(pf, "safety_ratings") and pf.safety_ratings:
            print("Prompt Safety Ratings:")
            for r in pf.safety_ratings:
                cat = getattr(r, "category", "Unknown")
                prob = getattr(r, "probability", "Unknown")
                blocked = getattr(r, "blocked", False)
                print(f"  - {cat}: probability={prob}, blocked={blocked}")

    candidates = getattr(response, "candidates", None)
    if not candidates:
        print("Server returned no candidates (response is completely empty).")
    else:
        for idx, cand in enumerate(candidates):
            finish_reason = getattr(cand, "finish_reason", "UNKNOWN")
            finish_msg = getattr(cand, "finish_message", None)
            print(f"\nCandidate #{idx + 1}:")
            print(f"  • Finish Reason: {finish_reason}")
            if finish_msg:
                print(f"  • Finish Message: {finish_msg}")

            if hasattr(cand, "safety_ratings") and cand.safety_ratings:
                print("  • Response safety filters:")
                for r in cand.safety_ratings:
                    cat = getattr(r, "category", "Unknown")
                    prob = getattr(r, "probability", "Unknown")
                    blocked = getattr(r, "blocked", False)
                    print(f"      - {cat}: probability={prob}, blocked={blocked}")

            content = getattr(cand, "content", None)
            if content and hasattr(content, "parts") and content.parts:
                has_thought = any(getattr(p, "thought", False) for p in content.parts)
                has_text = any(getattr(p, "text", None) and not getattr(p, "thought", False) for p in content.parts)
                print(f"  • Part presence: Standard text={has_text}, Thinking={has_thought}")

    print(f"{'-'*60}\n")

def main():
    if len(sys.argv) < 2:
        print("Usage: python3 transcript_free_context.py file1.opus [file2.opus file3.opus ...]")
        return

    active_key = os.environ.get("GEMINI_API_KEY") or API_KEY

    if not active_key or active_key.startswith("xxxx") or "INSERT_YOUR" in active_key:
        print("Error: Set a valid API key in the API_KEY variable.")
        sys.exit(1)

    client = genai.Client(api_key=active_key)
    audio_paths = sys.argv[1:]

    for path in audio_paths:
        if not os.path.exists(path):
            print(f"Critical error: File '{path}' does not exist. Operation aborted.")
            sys.exit(1)

    is_multi = len(audio_paths) > 1
    if is_multi:
        print(f"Multi-file mode detected ({len(audio_paths)} files). Context will be preserved across all parts.")
    else:
        print("Single-file mode detected.")

    prompt_base = (
        "Provide a precise speech-to-text transcription of the attached audio. "
        "Because the recording may contain names of politicians, journalists, public figures, geographical names, "
        "and other local references, verify their correct and grammatically accurate spelling. "
        "Fix stutters, remove 'um' and 'uh' sounds. "
        "STRICT PROHIBITION OF SHORTENING SENTENCES. Keep the text in its full, authentic form. "
        "Make minor stylistic adjustments, but without shortening sentences. "
        "Structure the output into logical subheadings according to thematic sections. Each subheading must include the key words of that section."
    )

    system_instruction = (
        "You act as an impartial, objective, and literal audio transcriber for scientific, "
        "historical, and journalistic archive purposes. Your sole task is the accurate conversion "
        "of spoken word to text."
    )

    # Configuration: Thinking level set to MEDIUM as required
    config = types.GenerateContentConfig(
        thinking_config=types.ThinkingConfig(
            thinking_level=types.ThinkingLevel.MEDIUM
        ),
        system_instruction=system_instruction
    )

    combined_results = []
    first_file_base = os.path.splitext(audio_paths[0])[0]
    output_name = f"{first_file_base}_complete_transcript.txt" if is_multi else f"{first_file_base}_transcript.txt"

    try:
        for idx, audio_path in enumerate(audio_paths):
            part_num = idx + 1
            file_name = os.path.basename(audio_path)
            print(f"\n{'='*60}")
            print(f"--- [Part {part_num}/{len(audio_paths)}] Processing: {file_name} ---")
            print(f"{'='*60}")

            # 1. Uploaded ONLY ONCE
            uploaded_file = upload_file_resumable(audio_path, active_key, client)

            print(f"🎙️ [3/3] Generating transcript for part {part_num} via model {MODEL_NAME} (Thinking: MEDIUM)...")

            if is_multi:
                if part_num == 1:
                    current_prompt = f"{prompt_base}\n\nThis is the first part (Part 1/{len(audio_paths)}) of the entire recording."
                else:
                    last_transcript = combined_results[-1][1] if combined_results else ""
                    context_snippet = last_transcript[-3500:] if len(last_transcript) > 3500 else last_transcript

                    current_prompt = (
                        f"{prompt_base}\n\n"
                        f"--- IMPORTANT CONTEXT FROM PREVIOUS PART (Part {part_num - 1}) ---\n"
                        f"Here is the conclusion of the previous transcript. Continue seamlessly from it, maintaining the exact same spelling of proper names, "
                        f"geographical names, terminology, and tone of conversation:\n"
                        f"\"\"\"\n{context_snippet}\n\"\"\"\n\n"
                        f"--- CURRENT TASK ---\n"
                        f"Transcribe this attached audio, which is the immediate continuation (Part {part_num}/{len(audio_paths)})."
                    )
            else:
                current_prompt = prompt_base

            transcript_text = ""
            try:
                # 2. Up to 52 attempts over the already uploaded file
                response = generate_content_with_retry(
                    client=client,
                    model=MODEL_NAME,
                    contents=[current_prompt, uploaded_file],
                    config=config,
                    max_retries=MAX_GENERATION_RETRIES
                )

                try:
                    transcript_text = response.text or ""
                except Exception as text_err:
                    print(f"Exception reading response.text: {text_err}")

                if not transcript_text.strip():
                    print_server_diagnostics(response, file_name)
                else:
                    print(f"    ✓ Part {part_num} was successfully generated ({len(transcript_text)} characters).")

            finally:
                # 3. Delete from server only after generation/attempts complete
                try:
                    client.files.delete(name=uploaded_file.name)
                    print("    🗑️ Temporary file successfully deleted from Google Cloud.")
                except Exception:
                    pass

            combined_results.append((audio_path, transcript_text))

            # Continuous write to disk
            with open(output_name, "w", encoding="utf-8") as f:
                if is_multi:
                    for p_path, text in combined_results:
                        f.write("\n=====================================================================\n")
                        f.write(f" TRANSCRIPT OF PART: {os.path.basename(p_path)}\n")
                        f.write("=====================================================================\n\n")
                        f.write(text if text.strip() else f"[WARNING: Transcript for file {os.path.basename(p_path)} contains no text]")
                        f.write("\n\n")
                else:
                    f.write(combined_results[0][1] if combined_results[0][1].strip() else "[WARNING: Transcript contains no text]")

            # Safety pause before next file (restore free tier quotas)
            if is_multi and part_num < len(audio_paths):
                print("    ⏳ Waiting 10 seconds before next part to stabilize quotas...")
                time.sleep(10)

        print("\n✅ Completed.")
        print(f"Final transcript was saved to:\n -> {output_name}")

    except Exception as e:
        print(f"\nCritical error: {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()