spicyapiDokumentacja
Przewodniki
Treść główna

Podmiana osoby

Jeden zestaw narzędzi, dwie postaci. Modele jednowywołaniowe podmieniają twarz albo głowę w pojedynczym żądaniu; modele dwuwywołaniowe najpierw analizują klip, żebyś to ty wskazał, kto ma się zmienić.

Część modeli nie generuje nowej sceny, tylko podmienia osobę: przenosi twarz z jednego zdjęcia na drugie, wymienia całą głowę albo zastępuje całą postać w klipie wideo. Katalog zbiera je pod jedną etykietą, toolkit: "subject-swap", a występują w dwóch postaciach — jedno wywołanie albo dwa wywołania z wyborem pomiędzy nimi.

Poza tym nie mają w sobie nic wyjątkowego. To zwykłe zadania asynchroniczne, parametry pochodzą z ich własnego inputSchema, a wyniki trafiają do output.assets[].

toolkit mówi, że model należy do tej grupy

toolkit to etykieta klasyfikująca przy pozycji katalogu, obecna zarówno w uwierzytelnionej odpowiedzi GET /api/v1/models, jak i w katalogu publicznym. Dziś ma jedną wartość, subject-swap, a przy modelach spoza jakiegokolwiek zestawu tego pola w ogóle nie ma — sprawdzaj więc wartość, nigdy obecność klucza.

Pole jest wyłącznie informacyjne. Nie rozstrzyga, który klucz może wywołać model, jak wyceniane jest żądanie ani co model przyjmuje. Nieznaną wartość potraktuj tak jak nieznany wpis w badges: pomiń ją.

curl -s "https://api.spicyapi.ai/api/v1/models?modality=video" \
  -H "Authorization: Bearer $SPICY_API_KEY" \
  | jq -r '.data.items[]
      | select(.toolkit == "subject-swap" and .enabled and .available)
      | [.model, (.tasks | join(",")), .family] | @tsv'

Każdy wiersz to jeden wywoływalny endpoint. O reszcie tej strony decyduje kolumna tasks: family z endpointem video-analyze to family dwuwywołaniowa, a towarzyszącego jej endpointu video-edit nie da się wywołać samodzielnie.

Którą postać mam przed sobą?

Jedno wywołanieDwa wywołania
EndpointyJeden endpoint edycyjnyvideo-analyze, potem video-edit, w tej samej family
Kto zostaje podmienionyTo, co znajdzie model. Żądanie nie ma żadnego pola wskazującego osobęCele wybrane przez ciebie, wskazane pozycją
Co wysyłaszDocelowy obraz albo klip i twój obraz referencyjny, w jednym żądaniuW pierwszym wywołaniu klip; w drugim wybrane pozycje, każda z własnymi obrazami referencyjnymi
Za co płaciszJedno zadanieDwa zadania, wyceniane osobno
Sprawdza się, gdyW kadrze jest jeden oczywisty bohaterW kadrze jest kilka osób albo podmiana niewłaściwej osoby dużo kosztuje

Obie postaci mają async: true: tworzysz zadanie, a wynik odbierasz odpytywaniem albo webhookiem.

Jedno wywołanie

Nic tu nie odbiega od dowolnego innego modelu edycyjnego. Przeczytaj inputSchema endpointu, wypełnij je i utwórz zadanie:

const created = await fetch('https://api.spicyapi.ai/api/v1/jobs/createTask', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SPICY_API_KEY}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': 'order-8814-swap-1',
  },
  body: JSON.stringify({
    model: swap.model, // jedna z pozycji z powyższego zapytania do katalogu
    input: {
      image_url: 'spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3',
      reference_image_url: 'spicy://f/fil_01k3m8xb2c3d4e5f6g7h8j9k0m',
    },
  }),
});

Nazwy pól różnią się między endpointami: jedne przyjmują prompt, seed albo format wyjścia, inne nic poza dwoma obrazami. Obowiązującą listą jest schemat; nie przenoś nazw pól z jednego endpointu na drugi. Referencją może być publiczny adres HTTPS, mały obraz w Base64 wpisany wprost w żądanie albo przesłany wcześniej URI spicy://; zobacz Pliki multimedialne.

Dwa wywołania

Pierwsze wywołanie ogląda twój klip i zgłasza, co w nim znalazło, w postaci uporządkowanego zestawu klatek podglądu. To ty decydujesz, które z nich podmienić. Właściwą pracę wykonuje drugie wywołanie.

twój klip ──▶ video-analyze ──▶ output.assets[0], [1], [2] …   (jedna klatka na cel)
                                          │
                        wybierasz pozycje │ 0 i 2
                                          ▼
          video-edit (analysis_task_id + swap_targets) ──▶ gotowy klip

Dlaczego nie jednym wywołaniem? Bo w scenie z czterema osobami pojedyncze wywołanie musiałoby zgadywać, kogo miałeś na myśli, a chybione zgadywanie to gotowy, rozliczony film z podmienioną niewłaściwą osobą. Rozbicie pracy na dwa kroki czyni wybór jawnym i możliwym do sprawdzenia.

Przeanalizuj klip

Wyślij klip do endpointu video-analyze. Jeśli endpoint ma pole resolution, ustaw je wprost — drugie wywołanie musi działać na tej samej wartości, więc wybierz tu rozdzielczość, jaką ma mieć gotowy klip. Na tych endpointach cena analizy od niej nie zależy.

const analysis = await createTask({
  model: analyze.model, // pozycja, której tasks zawierają "video-analyze"
  input: {
    video_url: 'spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3',
    resolution: '720p',
  },
});
const analysisTaskId = analysis.taskId;

Odczytaj, co zostało znalezione

Poczekaj na zadanie tak jak zwykle. Udana analiza zwraca po jednej klatce podglądu na każdy śledzony cel, w output.assets[]:

{
  "state": "succeeded",
  "output": {
    "assets": [
      { "url": "https://…", "expiresAt": "2026-09-20T09:32:11Z", "mime": "image/jpeg" },
      { "url": "https://…", "expiresAt": "2026-09-20T09:32:11Z", "mime": "image/jpeg" }
    ]
  },
  "settled": true
}

Kolejność tej tablicy jest kontraktem. Indeks elementu to object_index, który odsyłasz w drugim wywołaniu: pierwsza klatka to 0, druga 1 i tak dalej. Pokaż klatki osobie, która wybiera, albo dopasuj je samodzielnie — tak czy inaczej odsyłaj pozycje z tej samej tablicy.

Analiza, która nie znajdzie nic do śledzenia, nie kończy się powodzeniem z pustą tablicą: kończy się jako failed z errorCode: "generation_failed", a blokada środków zostaje zwolniona w całości. Analiza ze stanem succeeded ma więc zawsze co najmniej jedną klatkę, a twojemu kodowi pozostaje tylko wybór między nimi.

Podmień wybrane cele

Wyślij identyfikator zadania analizy oraz po jednym wpisie swap_targets na każdy cel do podmiany. Cele, których nie wymienisz, pozostają nietknięte.

const swapped = await createTask({
  model: edit.model, // pozycja "video-edit" z tej samej family
  input: {
    analysis_task_id: analysisTaskId,
    resolution: '720p', // ta sama wartość, na której działała analiza
    swap_targets: [
      { object_index: 0, image_urls: ['spicy://f/fil_01k3m8xb2c3d4e5f6g7h8j9k0m'] },
    ],
  },
});

Wynikiem jest jeden klip w output.assets[], jak przy każdym innym zadaniu wideo.

Co drugie wywołanie sprawdza, zanim je przyjmie

Te kontrole odbywają się przy tworzeniu zadania, więc odrzucone żądanie niczego nie tworzy i niczego nie blokuje.

RegułaDlaczego
Analiza musi należeć do twojego konta i zakończyć się powodzeniemAnaliza w kolejce albo nieudana nie ma do czego się odwołać
W obu wywołaniach użyj tego samego klucza APIAnalizę da się odczytać wyłącznie kluczem, który ją utworzył, więc innym w ogóle nie zobaczysz klatek, spośród których wybierasz
Każdy object_index musi mieścić się w zakresie i wystąpić tylko razPoza zakresem podmieniona zostałaby po cichu niewłaściwa osoba, a przy powtórzeniu ta sama osoba dwa razy. Oba zadania dobiegłyby końca i zostałyby normalnie rozliczone
Pola wpływające na cenę, na przykład resolution, muszą zgadzać się z analiząGdyby były dziedziczone, przestałbyś widzieć, według którego progu cenowego jesteś rozliczany
Klip źródłowy musi być ten samPomiń video_url, a zostanie użyty klip z analizy; jeśli go wyślesz, musi być identyczny
Analiza nadaje się do użycia przez 24 godziny od zakończeniaPóźniej znikają stojące za nią dane śledzenia, a często również klip źródłowy

Ustaw `resolution` w obu wywołaniach albo uważnie sprawdź wartość domyślną

Pominięte pola przyjmują wartość default ze schematu, zanim oba żądania zostaną porównane. Analiza zostawiona na wartości domyślnej i podmiana zostawiona na wartości domyślnej faktycznie się zgadzają — ale analiza z ustawioną wartością i podmiana zostawiona pusta już nie, a rozbieżność zostaje odrzucona, zamiast być po cichu poprawiona. Jawne wysłanie wartości w obu wywołaniach zdejmuje ten problem z głowy.

Gdy drugie wywołanie zostaje odrzucone

Wszystkie te przypadki to HTTP 400 z kodem biznesowym code: 400. Pole msg przyjmuje jedną z wartości:

msgCo się stałoCo zrobić
The referenced analysis task was not found.Na tym koncie nie ma takiego zadania albo analysis_task_id nie jest identyfikatorem zadaniaUżyj taskId zwróconego przez twoją własną analizę. Zadanie z cudzego konta jest celowo zgłaszane tak samo
The referenced task belongs to a different model.Identyfikator wskazuje inne zadanie — często samo zadanie podmiany albo analizę z innej familyWskaż zadanie video-analyze z tej samej family
The referenced analysis did not complete successfully.Analiza wciąż trwa albo zakończyła się jako failed / expiredZaczekaj na nią albo uruchom ją ponownie
That analysis has no reusable result. Run the analysis step again.Analiza się powiodła, ale nie zostawiła niczego, co podmiana mogłaby wykorzystaćUruchom analizę ponownie na tym samym klipie
That analysis result has expired. Run the analysis step again.Minęły ponad 24 godzinyUruchom analizę ponownie
"resolution" must match the analysis step.Oba wywołania różnią się w polu wpływającym na cenęWyślij wartość z analizy albo uruchom nową analizę z wartością, której chcesz
"video_url" must match the analysis step.Wysłałeś inny klipWyślij ten sam URI albo pomiń to pole
That analysis found N target(s); "object_index" is out of range.Pozycja wykracza poza to, co zwróciła analizaPozycje liczone są od zera i kończą się na assets.length - 1
"object_index" is listed more than once.Dwa wpisy wskazują ten sam celPrzypisz jeden wpis na cel, a wszystkie jego obrazy referencyjne umieść w image_urls tego wpisu

Ile kosztują oba wywołania

Analiza i podmiana to dwa osobne zadania z dwiema osobnymi cenami, każde z własną blokadą i własnym rozliczeniem. Odrzucone drugie wywołanie nic nie kosztuje; udana analiza zostaje rozliczona nawet wtedy, gdy potem zrezygnujesz z podmiany.

  • Analiza jest zwykle wyceniana per_request: jedno obciążenie na wywołanie, niezależnie od długości klipu i liczby znalezionych celów.
  • Podmiana jest zwykle wyceniana per_second, w pełnych blokach, a długość bierze się z klipu, a nie z pola, które ustawiasz. Kwota podana przed wysłaniem dotyczy więc najdłuższego klipu, jaki endpoint przyjmuje: podana kwota jest kwotą zablokowaną, końcowe obciążenie liczy się z długości zaokrąglonej w górę i nigdy nie przekracza blokady, a różnica wraca na saldo przy rozliczeniu zadania. Blokada większa od końcowego obciążenia jest tutaj zachowaniem oczekiwanym. Zanim zaplanujesz na tym budżet, przeczytaj Rozliczenia.

Obie ceny odczytaj z katalogu, zamiast je zakładać, a jeśli liczy się dokładna kwota, poproś o wycenę każdego wywołania.

Następne kroki

Na tej stronie