> ## Documentation Index
> Fetch the complete documentation index at: https://docs.api.rask.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Choose the dubbing model and read it back

> Request dubbing model v1 or v2 when creating a Rask API project, then read which model and engine actually ran, and why they may differ.

A project dubs with one of two models. You name one when you create the project, and the project tells you afterwards what it
stored and what it actually uses.

| Model | What it is |
| - | - |
| `v2` | Meaning-first dubbing (Dubbing 2.0). The default when you name none |
| `v1` | The legacy dubbing engine |

## 1. Check the language and voices

`capability=meaning_first` lists the languages with the meaning-first engine, and `capabilities.meaning_first_only` marks those that
have nothing else.

```typescript TypeScript theme={null}
const { data: languageList } = await rask.catalogs.listLanguages({ query: { capability: 'meaning_first' } });
```

```python Python theme={null}
language_list = client.catalogs.list_languages(capability="meaning_first")
```

```go Go theme={null}
languageList, err := client.Catalogs.ListLanguages(ctx, &api.ListLanguagesParams{
	Capability: rask.Ptr(api.LanguageCapabilityMeaningFirst),
})
if err != nil {
	return err
}
```

```java Java theme={null}
LanguageList languageList = rask.catalogs().listLanguages(ListLanguagesParams.builder()
        .capability(LanguageCapability.MEANING_FIRST)
        .build()).data();
```

```php PHP theme={null}
$languageList = $rask->catalogs->listLanguages(['capability' => 'meaning_first']);
```

```bash cURL theme={null}
curl "https://api.rask.ai/v3/languages?capability=meaning_first" \
  -H "Authorization: Bearer $RASK_API_KEY"
```

[listVoices](https://docs.api.rask.ai/operations/listVoices.md) lists the voices a new project can use. It takes the model, and each voice says whether it works
with meaning-first dubbing in `meaning_first_compatible`.

```typescript TypeScript theme={null}
const { data: voiceList } = await rask.voices.listVoices({ query: { target_language: 'es-es', dubbing_model: 'v2' } });
```

```python Python theme={null}
voice_list = client.voices.list_voices(target_language="es-es", dubbing_model="v2")
```

```go Go theme={null}
voiceList, err := client.Voices.ListVoices(ctx, api.ListVoicesParams{
	TargetLanguage: "es-es",
	DubbingModel:   rask.Ptr(api.DubbingModelV2),
})
if err != nil {
	return err
}
```

```java Java theme={null}
VoiceList voiceList = rask.voices().listVoices(ListVoicesParams.builder()
        .targetLanguage("es-es")
        .dubbingModel(DubbingModel.V2)
        .build()).data();
```

```php PHP theme={null}
$voiceList = $rask->voices->listVoices(['target_language' => 'es-es', 'dubbing_model' => 'v2']);
```

```bash cURL theme={null}
curl "https://api.rask.ai/v3/voices?target_language=es-es&dubbing_model=v2" \
  -H "Authorization: Bearer $RASK_API_KEY"
```

## 2. Estimate with the model

The estimate says what you would get. `details.dubbing_model` is the model that would run, `details.text_adaptation` says how the
auto-dub would voice the text (null when nothing will be dubbed), and a notice `dubbing_model_overridden` warns when the model is not
the one you named, or not `v2` when you named none.

```typescript TypeScript theme={null}
const { data: estimate } = await rask.projects.estimateProject({
  body: { media_id: 'MEDIA_ID', target_language: 'es-es', dubbing_model: 'v2' },
});
```

```python Python theme={null}
estimate = client.projects.estimate_project(media_id="MEDIA_ID", target_language="es-es", dubbing_model="v2")
```

```go Go theme={null}
estimate, err := client.Projects.EstimateProject(ctx, api.EstimateProjectRequest{
	MediaId:        "MEDIA_ID",
	TargetLanguage: "es-es",
	DubbingModel:   rask.Ptr(api.DubbingModelV2),
})
if err != nil {
	return err
}
```

```java Java theme={null}
Estimate estimate = rask.projects().estimateProject(EstimateProjectRequest.builder()
        .mediaId("MEDIA_ID")
        .targetLanguage("es-es")
        .dubbingModel(DubbingModel.V2)
        .build()).data();
```

```php PHP theme={null}
$estimate = $rask->projects->estimateProject([
    'media_id' => 'MEDIA_ID',
    'target_language' => 'es-es',
    'dubbing_model' => 'v2',
]);
```

```bash cURL theme={null}
curl -X POST https://api.rask.ai/v3/projects/estimate \
  -H "Authorization: Bearer $RASK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"media_id": "MEDIA_ID", "target_language": "es-es", "dubbing_model": "v2"}'
```

## 3. Create the project

```typescript TypeScript theme={null}
const { data: job } = await rask.projects.createProject({
  body: { media_id: 'MEDIA_ID', target_language: 'es-es', dubbing_model: 'v2', expected_cost_minutes: 1 },
  idempotencyKey: createKey,
});
```

```python Python theme={null}
job = client.projects.create_project(
    media_id="MEDIA_ID",
    target_language="es-es",
    dubbing_model="v2",
    expected_cost_minutes=1,
    idempotency_key=create_key,
)
```

```go Go theme={null}
job, err := client.Projects.CreateProject(ctx, api.CreateProjectRequest{
	MediaId:             "MEDIA_ID",
	TargetLanguage:      "es-es",
	DubbingModel:        rask.Ptr(api.DubbingModelV2),
	ExpectedCostMinutes: rask.Ptr(int32(1)),
}, rask.WithIdempotencyKey(createKey))
if err != nil {
	return err
}
```

```java Java theme={null}
Job job = rask.projects().createProject(
        CreateProjectRequest.builder()
                .mediaId("MEDIA_ID")
                .targetLanguage("es-es")
                .dubbingModel(DubbingModel.V2)
                .expectedCostMinutes(1)
                .build(),
        RequestOptions.builder().idempotencyKey(createKey).build()).data();
```

```php PHP theme={null}
$job = $rask->projects->createProject(
    ['media_id' => 'MEDIA_ID', 'target_language' => 'es-es', 'dubbing_model' => 'v2', 'expected_cost_minutes' => 1],
    ['idempotency_key' => $createKey],
);
```

```bash cURL theme={null}
CREATE_KEY=$(uuidgen)
curl -X POST https://api.rask.ai/v3/projects \
  -H "Authorization: Bearer $RASK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $CREATE_KEY" \
  -d '{"media_id": "MEDIA_ID", "target_language": "es-es", "dubbing_model": "v2", "expected_cost_minutes": 1}'
```

The job echoes the request in `params.dubbing_model` (null when you named none). Copies from
[createProjectCopies](https://docs.api.rask.ai/operations/createProjectCopies.md) inherit the model of their source.

The `project_processing` job ends when translation ends. The auto-dub then runs as a `dub` job of its own, named in
`result.auto_dub.dub_job_id`: wait for it before you read which engine voiced the project.

## 4. Read it back

```typescript TypeScript theme={null}
const { data: project } = await rask.projects.getProject({ path: { project_id: projectId } });
```

```python Python theme={null}
project = client.projects.get_project(project_id)
```

```go Go theme={null}
project, err := client.Projects.GetProject(ctx, projectID)
if err != nil {
	return err
}
```

```java Java theme={null}
Project project = rask.projects().getProject(projectId).data();
```

```php PHP theme={null}
$project = $rask->projects->getProject($projectId);
```

```bash cURL theme={null}
curl "https://api.rask.ai/v3/projects/$PROJECT_ID" \
  -H "Authorization: Bearer $RASK_API_KEY"
```

| Field of the project | What it says |
| - | - |
| `requested_dubbing_model` | The model the create request named; null when it named none or the project was not created through v3 |
| `dubbing_model` | The effective model |
| `dubbing_model_override_reason` | Why the effective model differs from the request, or from the default `v2` when the request named none; null when it does not differ |
| `last_dub_engine` | The engine of the last finished dub started through the API, `meaning_first` or `legacy`; null before the first one. A dub run in the web app does not change it |

A dub job also reports `result.engine`, `result.text_adaptation` and `result.notices`.

## When the model differs

These are the allowed fallbacks, in `dubbing_model_override_reason`:

| Reason | What happened |
| - | - |
| `trial_rules` | Trial projects follow the trial engine rule, whatever model the request named |
| `meaning_first_disabled` | Meaning-first dubbing is switched off for paid projects |
| `language_requires_v2` | The language runs only on `v2`, although `v1` was requested |
| `language_not_supported` | `v2` was requested, but the language or its voices run only on `v1` |
| `free_dubbing` | The free dubbing variant runs on `v1` |

The effective model is `v2` only when the project runs meaning-first, its voices can use it and it is not a free dubbing variant. A
voice can still change the engine after creation: on a `v2` project, giving a speaker a voice with `meaning_first_compatible: false`
sends the next voiceover to the legacy engine, while `dubbing_model` stays `v2`. [updateSpeakerVoice](https://docs.api.rask.ai/operations/updateSpeakerVoice.md)
warns with `meaning_first_disabled`, and the dub's result reads `engine: legacy` with the notice `voice_fallback`.

## Text adaptation

`text_adaptation` tells you how a dub treats the translation. It decides whether the words you read are the words you hear.

| Value | Meaning |
| - | - |
| `context` | Voiced with the translation context, so the speech may be adapted to the interval. Auto-dub after a meaning-first translation |
| `as_written` | Voiced exactly as the segment text is written. Every manual dub, segment generation, and auto-dub without a translation context |

## The whole program

```python dubbing_model.py theme={null}
"""Ask for a model and report what ran: python dubbing_model.py MEDIA_ID

Its keys are saved in dubbing-model-MEDIA_ID.json: run it again within 24 hours, and no paid work starts twice. Delete the file to
start anew.
"""
import sys

from rask_client import Progress, call, wait_for_job

progress = Progress(f"dubbing-model-{sys.argv[1]}.json")
order = {"media_id": sys.argv[1], "target_language": "es-es", "dubbing_model": "v2"}
estimate = call("POST", "/projects/estimate", order)
print("Would run:", estimate["details"]["dubbing_model"], "text:", estimate["details"]["text_adaptation"])
for notice in estimate["notices"]:
    print("Notice:", notice["code"], notice["detail"])

processing = call(
    "POST",
    "/projects",
    {**order, "expected_cost_minutes": estimate["total_minutes"]},
    key=progress.key("create project"),
)
auto_dub = wait_for_job(processing["id"])["result"]["auto_dub"]
if auto_dub["state"] == "dispatched":
    dub = wait_for_job(auto_dub["dub_job_id"])["result"]
    print("Dub engine:", dub["engine"], "text:", dub["text_adaptation"])
    for notice in dub["notices"]:
        print("Notice:", notice["code"], notice["detail"])
else:
    print("Auto-dub:", auto_dub["state"], auto_dub["skipped_reason"])

project = call("GET", f"/projects/{processing['project_id']}")
print("Requested:", project["requested_dubbing_model"])
print("Effective:", project["dubbing_model"], "because:", project["dubbing_model_override_reason"])
print("Last engine:", project["last_dub_engine"])
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.