# Operații media (/ro/docs/gateway/media-ops)

Operațiile media din codai Studio — elimini fundalul, mărești rezoluția, repictezi zone, separi pistele, reduci zgomotul, faci mastering, transformi audio în MIDI și generezi muzică sau efecte sonore din asset-urile tale.

**Operațiile media** transformă un [asset](./assets.mdx) Studio în altele noi: decupezi un subiect, mărești o imagine, repictezi o zonă mascată, separi o melodie în piste, cureți o voce, faci masterul unui mix, transformi o melodie în MIDI sau generezi o melodie ori un efect sonor. Fiecare operație primește id-uri de asset-uri și parametri stricți, specifici operației, iar fiecare rezultat se salvează ca asset nou în biblioteca ta. Id-ul public al modelului este `codai-studio-media`.

| Metodă | Cale | Rezultat |
| --- | --- | --- |
| `GET` | `/v1/studio/media-ops` | Catalogul operațiilor: motor, mod, intrări, `available`, un preț de listă (gratuit) |
| `POST` | `/v1/studio/media-ops/{op}/quote` | Prețul exact al unei cereri; nu se rezervă și nu se taxează nimic |
| `POST` | `/v1/studio/media-ops/{op}` | `200` cu rezultatele (operații sincrone) sau `202` cu un job (operații asincrone) |
| `GET` | `/v1/studio/media-ops/jobs/{id}` | Un job asincron; unul terminat se finalizează când îl citești |

`{op}` este id-ul operației cu puncte, de exemplu `image.remove_background` sau `audio.separate_stems`.

## Operațiile [#operațiile]

| Operație | Intrări | Rezultat | Mod | Motor |
| --- | --- | --- | --- | --- |
| `image.remove_background` | 1 imagine | Decupaj PNG / WebP cu transparență | sync | BiRefNet |
| `image.upscale` | 1 imagine | Imaginea ×2 sau ×4 (cel mult 64 MP) | sync | Real-ESRGAN x4plus |
| `image.inpaint` | imagine + mască PNG | Imaginea cu zona albă a măștii repictată după `prompt` | sync | Gemini image edit |
| `image.erase` | imagine + mască PNG | Imaginea fără obiectul mascat | sync | Gemini image edit |
| `image.outpaint` | 1 imagine | Scena extinsă la un nou `aspect_ratio` | sync | Gemini image edit |
| `audio.separate_stems` | 1 audio | 4 piste (voce, tobe, bas, restul) sau 2 (voce, acompaniament) | async | Demucs (htdemucs) |
| `audio.denoise` | 1 audio | Același audio fără zgomotul de fond | async | DeepFilterNet 3 |
| `audio.to_midi` | 1 audio | Un fișier MIDI standard (`kind: "midi"`) | async | Basic Pitch |
| `audio.master` | 1 audio | Un master cu loudness normalizat (WAV sau MP3) | async | ffmpeg loudnorm, 2 treceri + limitator |
| `audio.analyze` | 1 audio | Doar JSON: BPM, tonalitate, loudness, vârf | sync | librosa + pyloudnorm |
| `music.generate_ace` | 0–1 audio | O melodie dintr-un prompt de stil și versuri opționale | async | ACE-Step 1.5 |
| `music.repaint` | 1 audio | Melodia cu o secțiune înlocuită | async | ACE-Step 1.5 |
| `music.extend` | 1 audio | Melodia continuată cu `extend_seconds` | async | ACE-Step 1.5 |
| `music.cover` | 1 audio | Propria melodie într-un alt stil | async | ACE-Step 1.5 |
| `sfx.generate` | niciuna | Un efect sonor dintr-un prompt | async | ACE-Step 1.5 |

Operațiile pe imagini acceptă asset-uri `image`, `upload` și `frame` (PNG, JPEG sau WebP); cele audio acceptă asset-uri `music`, `speech`, `sfx` și `upload`. La `image.inpaint` și `image.erase` **al doilea** asset este o mască PNG alb-negru a primului: albul se editează, negrul rămâne neschimbat. Masca ajunge la model ca imagine de referință, așa că editarea o urmează îndeaproape, dar nu la pixel (intrarea din catalog are o `note` care spune asta).

### Parametri [#parametri]

Parametrii se validează strict pentru fiecare operație — o cheie necunoscută dă `400`.

| Operație | Parametri (impliciți) |
| --- | --- |
| `image.remove_background` | `format`: `png` \| `webp` (`png`) |
| `image.upscale` | `scale`: `2` \| `4` (`4`) |
| `image.inpaint` | `prompt` (obligatoriu), `model`, `image_size`: `1K` \| `2K` \| `4K` |
| `image.erase` | `prompt` (indiciu opțional), `model`, `image_size` |
| `image.outpaint` | `aspect_ratio` (obligatoriu: `1:1`, `2:3`, `3:2`, `3:4`, `4:3`, `4:5`, `5:4`, `9:16`, `16:9`, `21:9`, `9:21`), `prompt`, `model`, `image_size` |
| `audio.separate_stems` | `stems`: `2` \| `4` (`4`) |
| `audio.denoise` | `strength`: 0–1 (`1`) |
| `audio.to_midi` | `onset_threshold`: 0.05–0.95 (`0.5`), `min_note_ms`: 10–1000 (`58`) |
| `audio.master` | `target_lufs`: `-14` \| `-16` \| `-23` (`-14`), `true_peak_db`: −3…−0.1 (`-1`), `format`: `wav` \| `mp3` (`wav`) |
| `audio.analyze` | niciunul |
| `music.generate_ace` | `prompt` (obligatoriu, ≤ 1000 caractere), `lyrics` (≤ 4000), `duration_seconds`: 10–240 (`60`), `bpm`: 40–220, `key`, `language` (`en`, `ro`, `es`, `fr`, `de`, `it`, `pt`, `ja`, `ko`, `zh`), `seed` |
| `music.repaint` | `prompt`, `lyrics`, `start_seconds`, `end_seconds` (după început, în interiorul melodiei), `seed` |
| `music.extend` | `prompt`, `lyrics`, `extend_seconds`: 5–120, `seed` |
| `music.cover` | `prompt`, `lyrics`, `strength`: 0–1 (`0.8`), `seed` |
| `sfx.generate` | `prompt` (obligatoriu), `duration_seconds`: 1–30 (`5`), `seed` |

## Rularea unei operații [#rularea-unei-operații]

```bash
curl https://ai.codai.ro/v1/studio/media-ops/image.remove_background \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: cutout-42" \
  -d '{ "asset_ids": ["3f2b8c1e-0000-4000-8000-000000000001"], "params": { "format": "png" } }'
```

O operație **sincronă** răspunde `200`:

```json
{
  "object": "media_op.result",
  "op": "image.remove_background",
  "status": "ready",
  "outputs": [{ "asset_id": "…", "kind": "image", "mime_type": "image/png", "url": "https://…" }],
  "analysis": null,
  "cost_micro_usd": 2640
}
```

`audio.analyze` nu salvează nimic și întoarce `analysis`: `{ duration_seconds, bpm, key, key_confidence, integrated_lufs, sample_peak_dbfs }`.

## Joburi asincrone [#joburi-asincrone]

Separarea pistelor, reducerea zgomotului, MIDI, masteringul și toate operațiile de muzică sau efecte sonore rulează ca **joburi**. Rularea răspunde `202` cu `{ "object": "media_op.job", "id", "status": "pending", … }`. Interoghează `GET /v1/studio/media-ops/jobs/{id}` până când `status` devine `ready` (cu `outputs`, câte un asset pe pistă la separare) sau `failed` (cu `error`: `timeout`, `input_refused`, `dispatch_failed`, `store_failed` sau un mesaj al motorului). Un job care nu a produs rezultat în 20 de minute eșuează cu `timeout`. Citirea unui job terminat îi salvează rezultatele și îl taxează o singură dată; procesul de fundal face același lucru dacă nu întreabă nimeni. Rulează cel mult **3 joburi asincrone** simultan pe cont — al patrulea primește `429` cu `Retry-After`.

## Prețuri [#prețuri]

Fiecare operație are o **cotație** — plafonul de preț — care se rezervă din portofel înainte de orice rulare; dacă portofelul nu o acoperă, răspunsul este `402 insufficient_funds` și nu pornește nimic. După operație se taxează o singură dată costul **măsurat**, iar restul rezervării se eliberează; o operație eșuată eliberează toată rezervarea.

- **Operațiile GPU / CPU** (toate în afară de editările de imagine Gemini) se taxează după timpul de instanță pe serviciul media codai Studio: 0,95 $ pe oră, cel puțin 5 secunde pe operație, estimat din lungimea intrării (de exemplu `audio.separate_stems` ≈ 15 s + 0,5 s pe secundă de intrare). Plătești secundele raportate de motor, niciodată mai mult decât cotația.
- **`image.inpaint` / `erase` / `outpaint`** se taxează ca o editare de imagine cu `model` și `image_size` alese.

`GET /v1/studio/media-ops` listează o cotație de referință pentru fiecare operație (parametri impliciți, 60 s de audio sau o imagine de 1024×1024) ca `quote_micro_usd`, iar suma din portofel ca `quote_micro_eur`; `POST …/{op}/quote` stabilește prețul cererii tale reale, cu aceeași validare ca la rulare.

## Erori [#erori]

| Stare | Când |
| --- | --- |
| `400` | Corp sau parametri greșiți, număr sau tip greșit de asset-uri, o mască ce nu e PNG, o mărire peste 64 MP, `end_seconds` înainte de `start_seconds` |
| `402 insufficient_funds` | Cotația nu încape în portofel |
| `404` | Id de operație necunoscut sau un asset care nu există, nu e gata ori nu e al tău |
| `429` | Trei joburi asincrone deja în lucru sau limita de cereri a cheii |
| `502` | Motorul a eșuat |
| `503 media_ops_unavailable` | Banda operației nu e configurată pe acest gateway (`available: false` în catalog); nu se rezervă și nu se taxează nimic |
| `503 assets_unavailable` | Stocarea asset-urilor nu e configurată |

## Marcaj AI și proveniență [#marcaj-ai-și-proveniență]

Fiecare rezultat e marcat ca generat de AI înainte de salvare: C2PA / XMP pe imagini, etichete RIFF INFO sau ID3 pe audio, plus metadatele de obiect `ai-generated`. Fiecare rezultat păstrează și de unde provine — `parent_asset_id` (prima intrare), `op` și `op_inputs` (`asset_ids` + parametrii validați) — ca să poți urmări oricând o pistă, un master sau un decupaj până la sursă. Generează cover-uri doar din muzică pe care o deții sau pentru care ai drepturi.

## SDK-uri [#sdk-uri]

```ts
const job = await codai.mediaOps.run('audio.separate_stems', { assetIds: [songId], params: { stems: 4 } });
const done = await codai.mediaOps.wait(job.id); // sau mediaOps.runAndWait(...)
```

```python
done = client.media_ops.run_and_wait("audio.master", [mix_id], {"target_lufs": -14})
```

TypeScript: `mediaOps.list / quote / run / job / wait / runAndWait`. Python: `media_ops.list / quote / run / job / wait / run_and_wait`. Melodiile și podcasturile construite din aceste rezultate se editează ca [creații audio](./audio-creatives.mdx) și se randează într-un mix masterizat cu `creatives.renderAudio`; versurile vin din [scriitorul de versuri](./lyrics-jam.mdx).
