# Operations

Vaani stores one folder per recording under `data/jobs/<id>/`. The speech model is cached in `data/models/`. Nothing in `data/` is source code.

## A normal recording

1. Preparing reads the title, length, and thumbnail.
2. Audio downloads the best audio stream and writes `audio.mp3`.
3. Transcript loads the speech model, writes `transcript.txt` in the spoken language, and labels Guest 1, Guest 2, and so on. If an English copy was requested and the speech is not English, it also writes `transcript.en.txt`.
4. Ready. The 16 kHz working wav is deleted. The MP3, thumbnail, and text stay until retention.

Both transcript URLs are on the result page:

- `/api/jobs/<id>/transcript` is the spoken language, Devanagari when the speech is Hindi
- `/api/jobs/<id>/transcript?version=en` is the English file, when it exists

The text files are UTF-8 with a byte order mark.

Guest 1 is whoever speaks first. The labels come from a local voice clustering step, not from a studio diarizer. A line stays with one guest. If two people talk over each other inside one breath, that line stays with one of them. The person at the desk can rename the guests before copying. The names in the downloaded UTF-8 file from the button follow the names on screen. The URL above keeps Guest 1 and Guest 2.

## Length, disk, and retention

The desk refuses a video longer than `VAANI_MAX_DURATION_SECONDS` (default 2700, which is 45 minutes) and an upload over `VAANI_MAX_UPLOAD_MB`. It also refuses a new job when free space is under `VAANI_MIN_FREE_GB` (default 2).

Every hour, and again at startup, finished or failed recordings older than `VAANI_RETENTION_HOURS` are deleted. A job that is queued or running is left alone. If the process dies mid-job, the next start marks the active step as waiting and runs it again. Preparing and audio steps that already finished are not repeated.

## When direct audio retrieval fails

YouTube sometimes will not return a file. The Audio step stops, the MP3 is not written, and the screen gives the reason:

| What happened | What the desk does |
| --- | --- |
| YouTube asks for a signed-in or age check | Stop. Offer an upload of a copy the operator may use. |
| The video is blocked in this country | Stop. Offer that same upload. |
| The video is private or removed | Stop. Private videos can still continue from an upload the operator already has. Removed videos cannot be opened. |
| No audio format came back | Stop. Offer an upload. |
| FFmpeg is missing | Stop. Ask for FFmpeg, then Try again. An upload needs FFmpeg too. |
| The transcript step fails after the MP3 exists | Keep the MP3 and the thumbnail. Try again reruns only the transcript. |

The desk does not collect YouTube cookies and does not bypass a sign-in wall. The authorized fallback is the upload on the stopped job, or the upload on the first screen when the person already has the file.

## Speech model

`VAANI_WHISPER_MODEL` defaults to `small`. The first job downloads it into `data/models/`. Hindi for a real launch should be `medium` or `large-v3`. Those downloads are larger and slower on CPU. A 45 minute episode on CPU can take about as long as the episode, sometimes longer. The progress line stays on Transcript while that happens. That is the model working, not a stuck queue.

Set `VAANI_WHISPER_DEVICE=cuda` only when a CUDA build of the speech engine is installed. If that device fails, the desk retries on CPU.

## Failure and recovery

- Stop on a running job asks the worker to halt between steps, between transcript segments, and during FFmpeg. Try again continues from the last finished step.
- Remove deletes that recording's folder immediately.
- If the page is refreshed, `/?job=<id>` opens the same recording.
- Logs go to the terminal that started the desk. A job crash is one traceback plus a short message on the queue.
- Update the YouTube reader when downloads start failing on videos that used to work: `.\.venv\Scripts\python.exe -m pip install -U yt-dlp`, then restart.
- Restart after installing FFmpeg so the new process can see it.

## Backup

Copy `data/` if you need to keep recordings or avoid downloading the model again. Copy `config/launch-gates.json` separately. Do not commit either file. `data/` and the live launch file are gitignored.

## What this version will not do

It will not sign in to YouTube, take a live stream, exceed the length limit, or keep files past retention. It will not open the production API while the launch checks are unfinished.
