Przejdź do treści głównej

API obrazów i wideo AI: od pierwszego wejścia do sprawdzonego wyniku

Stwórz niebieski kubek i animuj go w MiniMax H3. Zacznij w Playground, potem użyj Python, CLI lub agenta, z promptami, kosztami i kontrolą.

Autor
Lydia
Opublikowano
Czas czytania
11 min czytania
Zaktualizowano
Fikcyjna dorosła ceramiczka w niebieskim fartuchu stawia niebieski kubek obok kamery w pracowni ceramicznej

Stwórz obraz, zatwierdź go, a następnie animuj w krótki klip. Te kroki odtwarzają nasz test niebieskiego kubka z października 2026. W przewodniku używamy obrazu Z-Image za 0,010 $ oraz pięciu sekund MiniMax H3 w 768p za 0,185 $: 0,195 $ za jedną próbę na każdym etapie, z wyłączonym rozszerzaniem promptu dla obrazu. Najpierw wypróbuj tę samą scenę w Playground; gdy dane wejściowe będą działać, użyj przykładu Python lub powierz wywołania agentowi.

Cel jest konkretny: prawidłowo uformowany kubek na stole roboczym, para przesuwająca się w prawo i powolny najazd kamery zakończony zatrzymaniem. Nie musisz wdrażać modelu ani konfigurować serwera GPU.

Wypróbuj cały przykład w Playground

Utwórz konto i zasil je, a potem zweryfikuj e-mail. Playground obsługuje żądanie; klucz API jest potrzebny dopiero do kodu i narzędzi agenta poniżej.

1. Wygeneruj obraz początkowy

Otwórz Z-Image tekst na obraz i skorzystaj z Playground. Wklej ten prompt:

Photograph of a handmade blue ceramic mug of tea on a worn wooden workbench in a pottery studio, a thin curl of steam rising from the mug. Soft window light from the left, shallow depth of field, shelves of pots softly out of focus behind. The mug sits left of centre with empty bench to its right. Natural colours, no text, no logos.

Wybierz 16:9, PNG, seed 42 i wyłącz rozszerzanie promptu. Sprawdź wyświetloną cenę, a następnie naciśnij Run model. Obraz podstawowy kosztuje 0,010 $; rozszerzanie zwiększyłoby koszt do 0,016 $. Utrzymuj seed 42 bez zmian, porównując modyfikacje promptu.

Otwórz wynik przed przejściem dalej. Sprawdź:

  • Obiekt: jeden kubek, jedno ucho i poprawna geometria ceramiki.
  • Możliwość ruchu: widoczna para i miejsce po prawej na jej przesuwanie się.
  • Kompozycja: kubek stoi na lewo od środka; półki pozostają spokojnym tłem.
  • Niepożądane szczegóły: brak przypadkowych napisów, logo i dodatkowych kubków.

Pobierz zaakceptowany PNG jako image.png. Jeśli kubek jest nieprawidłowy, zmień jedno zdanie i stwórz nowy obraz, zanim wydasz pieniądze na wideo. Każdy kolejny obraz podstawowy dodaje 0,010 $.

2. Animuj zatwierdzony obraz

Otwórz MiniMax H3 obraz na wideo, dodaj image.png jako Input image i wklej w Prompt:

Steam rises from the mug and drifts to the right. The camera pushes in slowly toward the mug, then holds. The mug, bench and shelves stay where they are. Quiet room, a faint ceramic clink.

Ustaw Duration na 5 sekund i Resolution na 768p; sprawdź cenę i naciśnij Run model. Ten klip kosztuje 0,185 $; wybór 2k kosztowałby 0,290 $.

Prompt oddziela ruch pary, ruch kamery i dźwięk. Nakazuje kubkowi pozostać na miejscu zamiast ponownie go opisywać. Jeśli kubek wędruje po stole, uprość instrukcję kamery zamiast dodawać kolejne działania obiektu. Powolny najazd przybliża kamerę; nie jest prośbą o poruszenie kubka.

3. Sprawdź klip i wybierz następne ujęcie

Odtwórz cały wynik z dźwiękiem. Sprawdź, czy kubek nie przesuwa się i nie zmienia kształtu, kamera wykonuje najazd i zatrzymuje się, nie ma niechcianego cięcia, a dźwięk pasuje do spokojnej pracowni. Oczekiwany wynik to MP4 trwający około pięciu sekund ze ścieżką audio; sam fakt odtwarzania pliku nie oznacza, że ujęcie nadaje się do użycia.

Jeśli obraz był dobry, ale ruch zły, zachowaj zaakceptowany obraz początkowy i popraw prompt ruchu. Jeśli obraz początkowy był zły, najpierw go wymień. H3 nie ma seed: generowanie w 2k daje nowe ujęcie, w którym ruch może być inny niż w polubionej wersji 768p.

Co powstało w teście niebieskiego kubka

Obraz kosztował 0,010 $ i został dostarczony jako PNG 1280 × 720. Żądanie pięciosekundowego wideo kosztowało 0,125 $ (w 480p, który nie jest już oferowany; ten sam klip w 768p kosztuje 0,185 $) i zwróciło MP4 832 × 480 ze ścieżką audio. Łącznie było to 0,135 $; w 768p byłoby 0,195 $.

Kubek pozostał na stole, a para unosiła się w górę i w prawo; najazd kamery był delikatny. Obraz umieścił kubek tylko nieznacznie na lewo od środka, a pod koniec para stała się mniej wyraźna w pobliżu półki. Jeśli chcesz ciaśniejszego ujęcia produktu, doprecyzuj pozycję kubka w prompcie obrazu przed animowaniem.

Dlaczego te dwa modele pasują do pierwszej próby

Z-Image daje niedrogi obraz do ustalenia obiektu i kompozycji. H3 przyjmuje go i generuje ruch oraz ścieżkę dźwiękową. Dane wejściowe różnią się:

Z-Image tekst na obrazH3 obraz na wideo
ID modelualibaba/z-image-turbo/text-to-imageminimax/h3/image-to-video
Wymagane daneprompt (od 1 do 1 000 znaków)prompt (do 7 000) i image_url
Użyte ustawieniaaspect_ratio (1:1, 4:3, 3:4, 16:9 lub 9:16), output_format (tylko png), seed, enable_prompt_expansionduration_seconds (od 4 do 15), resolution (768p lub 2k)
Nieobsługiwanekażdy inny format wyjściowyseed i aspect_ratio: obraz początkowy wyznacza kształt
Wynikjeden PNGjeden MP4 z wygenerowaną ścieżką dźwiękową
Cena0,010 $ za obraz0,037 $ za sekundę w 768p

Użyj obrazu początkowego PNG, JPEG, WebP lub GIF, z każdym bokiem od 256 do 5 760 pikseli, proporcjami od 2:5 do 5:2 i plikiem mniejszym niż 30 MB. Przesłany PNG jest najprostszą opcją. Publiczny link musi zwracać właściwy obraz; strona udostępniania dysku w chmurze nie jest obrazem wejściowym.

Uruchom ten sam przykład za pomocą Python

Użyj Python 3.11 lub nowszego, zainstaluj oficjalny pakiet poleceniem python -m pip install spicyapi i zapewnij dostęp do narzędzia file. Ustaw SPICY_API_KEY we własnym terminalu, zapisz kod jako blue-mug.py i uruchom python blue-mug.py. Prosi o zgodę na obu płatnych etapach i zatrzymuje się na kontrolę obrazu. Zachowaj utworzony katalog uruchomienia przy wznawianiu; nowy RUN_NAME wybierz tylko przy świadomie nowej próbie. Kontrakt API i opcje SDK znajdziesz w dokumentacji SDK Python.

python
import hashlib
import json
import os
from pathlib import Path
import subprocess
import tempfile
import time
import urllib.request
import uuid
from spicyapi import SpicyClient, output_assets

# Change RUN_NAME for a deliberately new pair of paid generations.
folder = Path(os.getenv("RUN_NAME", "blue-mug-1"))
folder.mkdir(exist_ok=True, mode=0o700)
state_file = folder / "state.json"
state = json.loads(state_file.read_text()) if state_file.exists() else {}
identity = hashlib.sha256(os.environ["SPICY_API_KEY"].encode()).hexdigest()
if state.get("identity", identity) != identity:
    raise RuntimeError("This saved run belongs to a different API key.")
state["identity"] = identity
client = SpicyClient()

def save() -> None:
    temp = state_file.with_suffix(".tmp")
    temp.write_text(json.dumps(state, indent=2))
    temp.chmod(0o600)
    temp.replace(state_file)

def run(stage: str, model: str, inputs: dict) -> dict:
    request = {"model": model, "input": inputs}
    if stage not in state:
        state[stage] = {"request": request, "key": str(uuid.uuid4())}
    saved = state[stage]
    if saved["request"] != request:
        raise RuntimeError("Input changed: choose a new RUN_NAME, not a new retry key.")
    save()  # The same request and key survive a lost create response.
    if not saved.get("taskId"):
        if "first_sent" in saved and time.time() - saved["first_sent"] >= 23 * 3600:
            raise RuntimeError("Unknown acceptance: reconcile task history before retrying.")
        if "quote" not in saved:
            quote = client.quote_task(model=model, input_data=inputs)
            if input(f"{stage}: quoted ${quote['estimatedCost']}. Create? [y/N] ") != "y":
                raise SystemExit("No task created for this stage.")
            saved["quote"] = quote
        saved.setdefault("first_sent", time.time())
        save()
        quote = saved["quote"]
        task = client.create_task(model=model, input_data=inputs,
                                  idempotency_key=saved["key"],
                                  quote_id=quote["quoteId"],
                                  expected_cost=quote["estimatedCost"])
        saved["taskId"] = task["taskId"]
        save()
    task_id = saved["taskId"]
    print(stage, "task:", task_id)
    deadline = time.monotonic() + 1200
    while time.monotonic() < deadline:
        task = client.get_task(task_id)
        if task["state"] in {"failed", "canceled", "expired"}:
            raise RuntimeError(f"{task_id}: {task.get('errorCode')} {task.get('errorMessage')}")
        if task["state"] == "succeeded":
            assets = output_assets(task)
            if not assets or assets[0].get("unavailable"):
                raise RuntimeError(f"{task_id}: no retrievable output; keep the task ID.")
            asset = assets[0]
            if not asset.get("pending") and asset.get("url"):
                print("Charge:", task.get("cost"), "settled:", task.get("settled"))
                return asset
        time.sleep(5)
    raise TimeoutError(f"Resume {task_id} by rerunning this file; do not start another task.")

def download(asset: dict, destination: Path, mime: str) -> None:
    temporary = None
    try:
        request = urllib.request.Request(asset["url"], headers={"User-Agent": "SpicyAPI-Blog-Example/1.0"})
        with urllib.request.urlopen(request, timeout=300) as response:
            if response.status != 200 or response.headers.get_content_type() != mime:
                raise RuntimeError("Unexpected download status or content type.")
            with tempfile.NamedTemporaryFile(dir=folder, delete=False) as handle:
                temporary = Path(handle.name)
                while chunk := response.read(1024 * 1024):
                    handle.write(chunk)
        size = temporary.stat().st_size
        expected = int(asset.get("bytes") or 0)
        detected = subprocess.check_output(["file", "-b", "--mime-type", str(temporary)], text=True).strip()
        if not size or (expected and size != expected) or detected != mime:
            raise RuntimeError("Incomplete or incorrectly formatted download.")
        temporary.replace(destination)  # An interrupted transfer leaves the old file alone.
    finally:
        if temporary is not None:
            temporary.unlink(missing_ok=True)

image_input = {
    "prompt": (
        'Photograph of a handmade blue ceramic mug of tea on a worn wooden workbench '
        'in a pottery studio, a thin curl of steam rising from the mug. Soft window '
        'light from the left, shallow depth of field, shelves of pots softly out of '
        'focus behind. The mug sits left of centre with empty bench to its right. '
        'Natural colours, no text, no logos.'
    ),
    "aspect_ratio": "16:9",
    "output_format": "png",
    "seed": 42,
    "enable_prompt_expansion": False,
}
image_asset = run("image", "alibaba/z-image-turbo/text-to-image", image_input)
download(image_asset, folder / "image.png", "image/png")
if input(f"Open {folder / 'image.png'}. Is the mug/frame approved? [y/N] ") != "y":
    raise SystemExit("Stopped before video generation.")
if not state.get("image_uri"):
    state["image_uri"] = client.upload_file(str(folder / "image.png"))["uri"]
    save()
video_input = {"image_url": state["image_uri"], "prompt": 'Steam rises from the mug and drifts to the right. The camera pushes in slowly toward the mug, then holds. The mug, bench and shelves stay where they are. Quiet room, a faint ceramic clink.',
               "duration_seconds": 5, "resolution": "768p"}
video_asset = run("video", "minimax/h3/image-to-video", video_input)
download(video_asset, folder / "video.mp4", "video/mp4")
print("Play", folder / "video.mp4", "with sound and review it before using it.")

Trzymaj klucz API na swoim serwerze lub kontrolowanym komputerze, nigdy na publicznej stronie ani w aplikacji mobilnej. Przed integracją innego modelu sprawdzaj aktualną dostępność i obsługiwane dane na stronach modeli powyżej.

Powierz wywołania agentowi, gdy to wygodniejsze

Skorzystaj z agenta, jeśli chcesz, by przygotował dane i zwrócił wynik, podczas gdy Ty zatwierdzasz cenę i decyzje kreatywne. Osobiście sprawdź obraz początkowy przed autoryzacją wideo.

  • CLI: zapisz ten sam obiekt wejściowy jako input.json i użyj npx @spicyapi/cli tasks create --model alibaba/z-image-turbo/text-to-image --input-file input.json --wait. CLI potwierdza cenę; zachowaj zwrócone ID zadania do wznowienia. Dla klipu prześlij zatwierdzony obraz poleceniem npx @spicyapi/cli files upload image.png, potem utwórz minimax/h3/image-to-video tak samo, używając zwróconego URI spicy:// jako image_url.
  • MCP: poproś asystenta o spicyapi_task_create dla alibaba/z-image-turbo/text-to-image, potem spicyapi_task_wait; dla klipu dodaj spicyapi_upload_file dla zatwierdzonego image.png, potem spicyapi_task_create z minimax/h3/image-to-video, 5 sekundami, 768p i powyższym promptem ruchu. Serwer MCP prosi o potwierdzenie ceny przed utworzeniem.
  • Skill: zainstaluj przez npx skills add spicyapi-ai/spicy-skill, następnie poproś $spicyapi o ten sam przebieg i zatrzymanie się na Twoją zgodę. Skill korzysta ze skonfigurowanego połączenia CLI lub MCP.

Zaplanuj budżet na próby, nie tylko końcowy klip

To szacunki w dolarach amerykańskich. Potwierdź aktualną cenę przed generowaniem.

ŻądanieSzacowana cena
Jeden obraz Z-Image, PNG 16:90,010 $
Ten sam obraz z rozszerzaniem promptu0,016 $
H3, 4 s w 768p0,148 $
H3, 5 s w 768p (domyślnie, jeśli pominięto resolution)0,185 $
H3, 5 s w 2k0,290 $
H3, 10 s w 768p0,370 $
H3, 15 s w 2k0,870 $
  • Długość i rozdzielczość: H3 kosztuje 0,037 $ lub 0,058 $ za sekundę w 768p lub 2k.
  • Nieobsługiwane kombinacje: klip 4-sekundowy w 2k jest odrzucany błędem 40004.
  • Dwie wersje robocze: jeden obraz i dwa pięciosekundowe wideo 768p kosztują 0,380 $.
  • Wersja robocza i końcowe ujęcie: jeden obraz, pięciosekundowy test 768p i jedno pięciosekundowe ujęcie 2k kosztują 0,010 $ + 0,185 $ + 0,290 $ = 0,485 $, przy ponownym użyciu zatwierdzonego obrazu.

Pieniądze są rezerwowane po przyjęciu zadania i rozliczane po jego zakończeniu. Zadania nieudane lub wygasłe zwykle zwalniają rezerwację; niektóre odmowy dotyczące treści są płatne zgodnie z podaną polityką modelu. Przy sprawdzaniu opłaty sprawdź rzeczywisty cost i stan settled zadania.

Napraw problem na właściwym etapie

ObjawCo zmienić
Kubek ma dwa ucha lub przypadkowy tekstPopraw prompt obrazu i zatwierdź nowy obraz początkowy przed generowaniem wideo
Kubek rusza się, choć miała ruszać się kameraZachowaj obraz; poproś o jeden ruch kamery i wyraźnie unieruchom kubek
Wideo wygląda dobrze w 768p, ale nowe ujęcie 2k jest inneTraktuj je jako nowe ujęcie i sprawdź ruch przed akceptacją
Link obrazu nie działaPrześlij plik lub użyj bezpośredniego, dostępnego URL obrazu zamiast strony udostępniania
Konto zwraca 401 lub 40310Sprawdź klucz lub dokończ weryfikację e-mail
40301 / 40302 / 40303Sprawdź uprawnienia modelu, listę dozwolonych IP klucza lub dostępność regionalną
40201 / 40202Sprawdź saldo lub limit wydatków konta/klucza; zachowaj zapisane żądanie podczas uzgadniania nieudanego wysłania
Żądanie odrzuca seed, aspect_ratio lub output_formatSprawdź dane obsługiwane przez wybrane zadanie; nie kopiuj pól Z-Image do H3
40004Zmień niezgodną kombinację: 4 sekundy w 2k
40901 lub konflikt idempotencjiUzyskaj nową wycenę: jeśli nie zapisano ID zadania, usuń wpis tej fazy w state.json i uruchom ponownie. Jeśli poprzednia próba mogła zostać przyjęta, sprawdź listę zadań przed utworzeniem czegokolwiek; zobacz opis błędów
429 lub błąd połączeniaZachowaj katalog uruchomienia i ID zadania; nie zaczynaj kolejnej płatnej próby tylko po to, by odzyskać wynik
Zadanie zgłasza content_rejected lub timeoutPrzeczytaj błąd i opłatę, potem świadomie wybierz zmieniony prompt, inny obraz źródłowy lub krótsze ujęcie
Generowanie powiodło się, ale wynik nie jest gotowyWznów to samo zapisane zadanie; nie generuj kolejnego klipu, aby uzyskać pobieranie

Ogranicz pierwszy proces do minimum

Gdy ujęcie działa, wybierz długość i rozdzielczość do dostarczenia, następnie wygeneruj i sprawdź tę wersję. Opcjonalny obraz końcowy może ukierunkować zakończenie przez last_image_url H3; nie gwarantuje dokładnej ostatniej klatki ani ruchu między klatkami.

W aplikacji zachowaj oba ID zadań i przechowuj zaakceptowane pliki we własnym magazynie. Porównanie odpytywania i Webhooks pomoże wybrać odbiór wyników, przechowywanie mediów zachować pliki, a generowanie wsadowe przygotować wiele ujęć. Pierwszy cel pozostaje ten sam: zatwierdzony obraz i klip, który rzeczywiście realizuje zamierzone ujęcie.

Także w językach English · 日本語 · 한국어 · Deutsch · Français · Español · Português (Brasil) · Русский · Italiano

Wypróbuj samodzielnie

Płacisz tylko za to, co uruchomisz — bez planów i subskrypcji.

Zrobione w MiniMax H3 · ByteDance Seedance 2.0 Fast