Примеры интеграции

Создайте задачу, дождитесь результата и скачайте файл.

Используйте подключение и сохраните JSON из примеров как request.json. Вкладки ниже используют Bun 1.4+ TypeScript, Python 3, Go 1.22+ или Bash. BASE_URL должен быть HTTPS origin, ключ хранится на сервере. Один файл состояния относится к одному заказу; повторный запуск с сохранённым ID выполняет только GET.

Интеграция с вебхуком

Добавьте callback_url в request.json, как в примере создания. Примеры отправки на cURL, TypeScript, Python и Go используют этот JSON без изменения метода или пути.

Создайте задачу один раз и сохраните ID. Ваш HTTPS-обработчик принимает POST со снимком задачи, сохраняет его и быстро отвечает 200 или 204. Дальнейшая обработка выполняется после ответа: прочитайте задачу через GET с API-ключом, проверьте статус и скачайте результат. Не скачивайте видео до HTTP-подтверждения вебхука.

Сохраните периодический GET раз в 60–120 секунд как восстановление при пропущенном уведомлении. Повторный callback не создаёт новый заказ и не должен повторять действия приложения. Если callback_url не задан, используйте обычный polling из примеров ниже.

Рабочий процесс

bash
umask 077
touch result.json content.headers && chmod 600 result.json content.headers || exit 1
[[ "$BASE_URL" =~ ^https://[A-Za-z0-9.-]+(:[0-9]{1,5})?$ &&
  -n "$API_KEY" && -r request.json ]] || {
  printf 'Задайте HTTPS BASE_URL, API_KEY и request.json\n' >&2
  exit 1
}
OUTPUT_FILE=${VIDEO_OUTPUT:-seedance.mp4}
if [[ -f task-id.txt ]]; then
  TASK_ID=$(< task-id.txt)
else
  (
    set -C
    printf 'creating\n' > task-id.txt
  ) || {
    printf 'Файл состояния уже существует; проверьте его до нового POST\n' >&2
    exit 1
  }
  if curl --fail --silent --show-error --max-time 120 \
    "$BASE_URL/v1/videos" \
    -H "Authorization: Bearer $API_KEY" \
    -H 'Content-Type: application/json' \
    --data-binary @request.json \
    -o created.json; then
    TASK_ID=$(jq -er '
      .id | select(type == "string" and length > 0)
    ' created.json) || exit 1
    printf '%s\n' "$TASK_ID" > task-id.txt
  else
    printf 'Исход заказа неизвестен; проверьте кабинет до нового POST\n' >&2
    exit 1
  fi
fi
[[ "$TASK_ID" != creating && "$TASK_ID" =~ ^[A-Za-z0-9_-]+$ ]] || {
  printf 'Некорректный ID задачи\n' >&2
  exit 1
}
deadline=$((SECONDS + 2700))
completed=0
while ((SECONDS < deadline)); do
  if ! http_status=$(curl --silent --show-error --max-time 60 \
    --write-out '%{http_code}' \
    "$BASE_URL/v1/videos/$TASK_ID" \
    -H "Authorization: Bearer $API_KEY" \
    -o result.json); then
    sleep 30
    continue
  fi
  case "$http_status" in
    200) ;;
    429 | 5??)
      sleep 30
      continue
      ;;
    *)
      printf 'Ошибка статуса HTTP %s; ID сохранён\n' "$http_status" >&2
      exit 1
      ;;
  esac
  jq '{id, status, error}' result.json
  status=$(jq -r '.status' result.json)
  case "$status" in
    completed)
      completed=1
      break
      ;;
    failed)
      printf 'Итоговый статус: %s\n' "$status" >&2
      exit 1
      ;;
    queued | in_progress) sleep 15 ;;
    *)
      printf 'Неизвестный статус: %s\n' "$status" >&2
      exit 1
      ;;
  esac
done
if ((!completed)); then
  printf 'Лимит ожидания; продолжите с %s\n' "$TASK_ID" >&2
  exit 1
fi
VIDEO_URL=$(jq -r '.content.video_url // empty' result.json)
if [[ -z "$VIDEO_URL" ]]; then
  metadata=$(curl --silent --show-error --max-time 300 \
    --dump-header content.headers --write-out '%{http_code} %{content_type}' \
    "$BASE_URL/v1/videos/$TASK_ID/content" \
    -H "Authorization: Bearer $API_KEY" -o seedance.download) || exit 1
  http_status=${metadata%% *}
  if [[ "$http_status" == 307 ]]; then
    VIDEO_URL=$(awk 'tolower($1)=="location:" {print $2}' content.headers | tr -d '\r' | tail -1)
  elif [[ "$http_status" == 200 ]]; then
    mime=${metadata#* }
    case "$mime" in
      video/mp4* | video/quicktime* | application/octet-stream*)
        mv seedance.download "$OUTPUT_FILE"
        exit 0
        ;;
    esac
  fi
  [[ "$http_status" == 307 ]] || {
    printf 'Content HTTP %s; ID preserved\n' "$http_status" >&2
    exit 1
  }
fi
[[ "$VIDEO_URL" =~ ^https://[^/@?#]+ ]] \
  && [[ ! "$VIDEO_URL" =~ ^https://[^/?#]*@ ]] || {
  printf 'Invalid signed HTTPS media URL\n' >&2
  exit 1
}
# The signed URL grants temporary access. Never send the API Bearer to it.
metadata=$(curl --silent --show-error --max-time 300 \
  --write-out '%{http_code} %{content_type}' \
  "$VIDEO_URL" -o seedance.download) || exit 1
http_status=${metadata%% *}
mime=${metadata#* }
case "$http_status:$mime" in
  2??:video/mp4* | 2??:video/quicktime* | 2??:application/octet-stream*)
    mv seedance.download "$OUTPUT_FILE"
    ;;
  *)
    printf 'Неожиданный HTTP-ответ или тип файла\n' >&2
    exit 1
    ;;
esac

Повторяйте только GET статуса каждые 10–15 секунд до итогового статуса или локального предела 45 минут. При таймауте POST до получения ID проверьте кабинет или поддержку. Для нового заказа используйте новое имя файла состояния. Локальный предел не отменяет удалённую задачу; для MOV задайте VIDEO_OUTPUT=seedance.mov (это имя используется всеми примерами). Защищайте локальные файлы состояния и результата: JSON ответа может содержать подписанную media-ссылку.