HiAPI
Блог

Руководство

Как вызывать happyhorse-1.1/image-to-video в API hiapi: руководство по curl и Python

Nov 3, 2026
hiapi.ai 博客封面图:错位堆叠的骨白纸页与一片锈红内页,扁平几何风格。

Что делает этот эндпоинт и когда его использовать

happyhorse-1.1/image-to-video превращает одно статическое изображение в короткое видео. Вы отправляете на этот эндпоинт изображение первого кадра, и модель генерирует анимацию, начиная с этого кадра. Практический смысл здесь в непрерывности: субъект, композиция и внешний вид из исходного изображения переносятся в сгенерированный фрагмент. Это и есть то, что мы здесь называем сохранением идентичности — модель не создаёт новую сцену с нуля только по текстовому промпту, а продолжает сцену, которую вы уже создали с помощью изображения.

Поэтому этот эндпоинт отлично подходит, когда визуальная идентичность важнее импровизации:

  • У вас уже есть референсный статичный кадр — портрет, фотография продукта, концепт-арт персонажа — и вы хотите сгенерировать движение, верное этому кадру.
  • Вы создаёте короткие ролики в конвейере, где все остальные материалы сначала проектируются в виде изображений.
  • Вам нужны анимированные раскадровки или превью движения для кадров сториборда.

Он менее подходит, когда вы хотите, чтобы модель свободно переосмыслила сцену с нуля, или когда нет статичного кадра, чтобы закрепить результат. В таких случаях рабочий процесс с приоритетом изображения обычно означает: сначала сгенерировать опорное изображение, а затем передать этот результат этому эндпоинту.

Сам вызов асинхронный: вы отправляете задачу и затем опрашиваете её до готовности видео. Остальная часть руководства проведёт вас через этот жизненный цикл от начала до конца.

Перед отправкой запроса

Перед первым вызовом нужно подготовить две вещи.

API-ключ

Все вызовы выполняются через платформу hiapi, API-ключ прикрепляется как bearer-учётные данные. Храните ключ в переменной окружения, а не в системе контроля версий, чтобы один и тот же код работал и при локальной разработке, и в CI:

export HIAPI_API_KEY="your_key_here"

В Python читайте его во время выполнения через os.environ["HIAPI_API_KEY"]. Никогда не логируйте необработанное значение заголовка.

Ссылка на изображение первого кадра

Эндпоинту нужно стартовое изображение. Заранее решите, как его передавать — как URL, который платформа может загрузить, или как загруженный/закодированный в base64 материал, в зависимости от того, что поддерживают ваш аккаунт и документация эндпоинта. Важно, чтобы эта ссылка была стабильной и доступной в момент отправки. Если задача может встать в очередь, подписанный URL, истекающий через пять минут, — плохой выбор.

Также полезно до вызова знать ожидаемое соотношение сторон и длительность вывода, поскольку первый кадр обычно их ограничивает. Принятое заранее решение избавит от повторной отправки позже.

Когда ключ экспортирован, а ссылка на изображение определена, можно отправлять.

Формирование входных данных первого кадра

Первый кадр — это якорь для всего, что модель генерирует после него. На практике относитесь к нему как к контракту идентичности: всё, что чётко видно на этом изображении — лица, одежда, логотипы, цветовая схема, угол камеры — это то, на что модель должна опираться. Если какая-то деталь на статичном кадре размыта или скрыта, вывод тоже будет размытым.

Независимо от конкретных параметров модели, применимы следующие принципы подготовки:

  • Выбирайте чёткие, хорошо освещённые кадры. Артефакты сжатия и сильный шум уменьшают то, что модель может сохранить. Резкое, равномерно освещённое изображение — лучший якорь, чем драматичное, но размытое.
  • Помещайте субъект туда, где должно начинаться движение. Обрезка и компоновка на статичном кадре выгоднее, чем надежда на то, что модель перекадрирует.
  • Соблюдайте соотношение сторон ожидаемого вывода. Если итоговое видео горизонтальное, подготовьте горизонтальное опорное изображение; последующее заполнение не вернёт потерянную композицию.
  • Избегайте противоречивых подсказок. Статичный кадр, который читается в одном смысле, но подразумевает действие за кадром, даёт модели нестабильную отправную точку.

Не предполагайте наличие параметров модели, не описанных в документации эндпоинта. Самый надёжный подход — позволить самому изображению нести замысел, а поля запроса использовать только для того, что они явно покрывают.

Рабочий запрос через curl

Для отправки задачи нужен всего один POST. Структура такова: аутентифицированный запрос к эндпоинту, тело JSON содержит идентификатор модели и входное изображение.

curl -sS -X POST "https://api.hiapi.ai/v1/video/generations" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "happyhorse-1.1/image-to-video",
    "input": {
      "image": "https://your-bucket.example.com/first-frame.jpg"
    }
  }'

По шагам:

  1. Эндпоинт. Цель POST — маршрут генерации видео в API hiapi. Если вы пишете скрипты для нескольких окружений, вынесите его в переменную.
  2. Заголовок аутентификации. Authorization: Bearer $HIAPI_API_KEY — оболочка подставит переменную окружения, экспортированную ранее.
  3. Тип содержимого. application/json, поскольку тело — JSON.
  4. Тело запроса. model выбирает happyhorse-1.1/image-to-video; input.image — ссылка на ваш первый кадр. Сверьтесь со справочной документацией эндпоинта, чтобы узнать точное имя поля изображения и структуру, принимаемые в вашем аккаунте.

Успешный ответ — это не само видео, а дескриптор задачи. Ожидайте получить идентификатор и поле статуса, обычно что-то вроде queued или processing. Запишите этот ID; он понадобится на следующем шаге для опроса. Сначала выполните команду один раз и выведите ответ, чтобы увидеть его структуру до того, как начнёте писать скрипт.

Тот же запрос на Python

Версия на Python — это прямой перенос, а не новая интеграция. Тот же маршрут, те же заголовки, то же тело JSON.

import os
import requests

API_KEY = os.environ["HIAPI_API_KEY"]
URL = "https://api.hiapi.ai/v1/video/generations"

payload = {
    "model": "happyhorse-1.1/image-to-video",
    "input": {
        "image": "https://your-bucket.example.com/first-frame.jpg"
    },
}

resp = requests.post(
    URL,
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    },
    json=payload,
    timeout=60,
)
resp.raise_for_status()
job = resp.json()
print(job)

Пояснения к соответствию:

  • requests с json=payload сериализует словарь так же, как строка -d в curl — если используете json=, не задавайте Content-Type вручную; библиотека справится сама. Явный заголовок выше безвреден, но избыточен.
  • raise_for_status() превращает сбой на уровне HTTP в исключение как можно раньше — до того, как вы начнёте опрашивать несуществующую задачу.
  • timeout — лишь клиентская защита для вызова отправки. Генерация выполняется на сервере и им не ограничена.

Храните payload в переменной и переиспользуйте её. При последующей смене модели или изображения вы должны редактировать один словарь, а не две параллельные реализации.

Работа с асинхронными задачами и опрос

Генерация видео из изображения занимает больше времени, чем цикл запрос/ответ. Поэтому API сразу возвращает ссылку на задачу и ожидает, что вы позже её проверите. Жизненный цикл таков: отправка, получение ID, опрос статуса, получение результата.

Извлеките ID задачи из ответа на отправку — имя поля зависит от документированной схемы ответа этого эндпоинта, поэтому прочитайте её один раз и захардкодьте поиск по ключу, а не значение:

job_id = job["id"]  # check the endpoint docs for the exact field

Затем опрашивайте с интервалом:

import time

poll_url = f"https://api.hiapi.ai/v1/video/generations/{job_id}"

while True:
    status = requests.get(
        poll_url,
        headers={"Authorization": f"Bearer {API_KEY}"},
        timeout=30,
    ).json()

    if status["status"] == "succeeded":
        break
    if status["status"] == "failed":
        raise RuntimeError(status)
    time.sleep(5)

Правила разумного опроса:

  • Опрашивайте с фиксированным интервалом, а не в плотном цикле. Для задач такого масштаба нескольких секунд между проверками достаточно; бомбардировка эндпоинта увеличивает нагрузку, но не ускоряет появление результата.
  • Ограничьте общее время ожидания. while True без верхней границы — это зависание, которое рано или поздно случится. Отслеживайте прошедшее время и громко падайте после разумного бюджета.
  • Обрабатывайте оба терминальных состояния. И успех, и неудача — это ответы. Считайте неизвестные строки статуса нетерминальными, но логируйте их, чтобы заметить изменения в паттернах.
  • Не опрашивайте одну задачу из нескольких параллельных worker'ов. Один опросчик на задачу или координация через очередь.

Как только статус стал терминальным, переходите к скачиванию результата.

Скачивание и хранение вывода

Готовое видео предоставляется как материал в ответе завершённой задачи — обычно это URL, иногда с временем истечения. Первый шаг после успешного опроса — получить эту ссылку и скопировать байты в хранилище, которое вы контролируете.

video_url = status["output"]["video_url"]  # inspect the real response shape

with requests.get(video_url, stream=True, timeout=120) as r:
    r.raise_for_status()
    with open(f"out/{job_id}.mp4", "wb") as f:
        for chunk in r.iter_content(chunk_size=1 << 20):
            f.write(chunk)

Рекомендации по хранению, применимые к любому конвейеру:

  • Копируйте долговременные артефакты, а не ссылайтесь на них. Подписанный или ограниченный по времени URL — это транспорт, а не хранилище. Сохраните файл, затем в базе данных храните свой собственный ключ объекта вместе с ID задачи.
  • Обрабатывайте большие файлы потоково. iter_content держит память стабильной; загрузка всего видео в переменную привязывает лимит памяти к размеру материала.
  • Записывайте происхождение. Сохраняйте job_id, ссылку на входное изображение и идентификатор модели вместе с материалом. Когда понадобится воспроизвести фрагмент, эта запись и будет рецептом.
  • Проверяйте HTTP-статус перед записью. Частичная запись из-за ошибочного ответа хуже чистого сбоя, потому что выглядит как действительный файл.

Как только файл оказался на диске или в объектном хранилище, конвейер замыкается, и можно переходить к следующему кадру.

Ошибки и подводные камни, на которые стоит обратить внимание

Большинство сбоев этого эндпоинта предсказуемы. Краткий чек-лист:

  • Некорректный или недоступный ввод первого кадра. Самая частая причина отказа в отправке. Проверьте, разрешается ли URL изображения из публичной сети, принимает ли эндпоинт этот формат и правильно ли вложено поле изображения в payload. Воспроизведите вызов через curl, чтобы отличить проблему аутентификации от проблемы payload.
  • Опрос до существования задачи. Чтение ID из неверного поля или опрос маршрута, не соответствующего ответу на отправку, даёт 404, который выглядит как сбой задачи. Выведите ответ на отправку один раз и зафиксируйте поиск по ключу на его основе.
  • Неограниченное ожидание. Отсутствие тайм-аута означает, что зависшая задача навсегда подвесит worker. Задайте бюджет, падайте и оповещайте.
  • Потеря ссылки на материал. Генерация успешна, но URL использован один раз и больше не скопирован. Сохраняйте байты при получении и держите собственный ключ; не полагайтесь на возвращённую ссылку как на долговременное хранилище.
  • Утечка учётных данных. Вставка ключа в notebook и случайная фиксация — повторяющаяся проблема. Загружайте из окружения, при сомнениях — ротируйте.
  • Предположение о недокументированных параметрах эндпоинта. Если нужного параметра нет в списке, значит, его не существует. Выражайте замысел только через изображение первого кадра и документированные поля.

Разберитесь с этим, и асинхронный процесс станет банальным — именно этого вы и хотите в производственном конвейере. Что касается специфичных для эндпоинта имён полей и допустимых значений, держите справочную документацию эндпоинта открытой во время разработки.

Похожие статьи

Все статьи →

Начните работать с постоянными артефактами ИИ

Получите API-ключ и вызывайте модели изображений, видео и аудио через один API на основе tasks.

Читать документацию API