Claude Opus 5 is now live on CometAPI โ†’
Q

Happy Horse 1.0

Per Detik:$0.112
Dirilis:May 31, 2026

Happy Horse 1.0 โ€” Sebuah model generasi audio-video berkualitas tinggi yang mendukung pembuatan video dari teks dan dari gambar. Model ini dapat menghasilkan visual, audio, dan gerakan bibir yang tersinkronisasi, sehingga cocok untuk film pendek, materi kreatif iklan, dan presentasi produk.

Baru
Penggunaan komersial

Playground untuk Happy Horse 1.0

Jelajahi Playground Happy Horse 1.0 โ€” lingkungan interaktif untuk menguji model dan menjalankan kueri secara real-time. Coba prompt, sesuaikan parameter, dan iterasi secara instan untuk mempercepat pengembangan dan memvalidasi kasus penggunaan.

Spesifikasi Teknis HappyHorse-1.0

ItemHappyHorse-1.0
ProviderAlibaba (dilaporkan secara publik setelah debut anonim di benchmark)
Model TypeGenerasi Video AI Multimodal
InputsTeks, Gambar
OutputsVideo + Audio tersinkron
ArchitectureTransformer aliran tunggal terpadu
Parameters~15B
ResolutionGenerasi 1080p native
Generation ModeGenerasi audio-video gabungan
DenoisingInferensi terdistil (~8 langkah dilaporkan)
Language SupportSinkronisasi bibir multibahasa (7 bahasa dilaporkan)

Apa itu HappyHorse-1.0

HappyHorse-1.0 adalah model generasi video AI terdepan yang dirancang untuk menghasilkan video dan audio tersinkron dalam satu pipeline generasi alih-alih menggabungkan beberapa model. Laporan publik menunjukkan model ini muncul secara anonim di arena benchmark sebelum kemudian dikaitkan dengan upaya AI Alibaba.

Berbeda dari sistem teks-ke-video konvensional yang merender visual terlebih dahulu lalu menambahkan suara belakangan, HappyHorse menekankan sinkronisasi native antara gerak, ucapan, suara latar, dan timing.

Fitur Utama HappyHorse-1.0

  • Generasi audio + video dalam satu kali proses
  • Output 1080p native tanpa keharusan upscaling
  • Alur kerja teks-ke-video dan gambar-ke-video
  • Pipeline generasi terdistil yang cepat
  • Sinkronisasi bibir multibahasa
  • Fokus pada pergerakan kamera sinematik dan kesinambungan adegan

Kinerja Benchmark HappyHorse-1.0

Laporan benchmark publik menyebutkan:

  • Artificial Analysis Arena:
    • Elo Teks-ke-Video: ~1330+
    • Elo Gambar-ke-Video: ~1390+
  • Berada di peringkat #1 atau mendekati pada cuplikan leaderboard publik selama periode rilis awal.

Interpretasi benchmark: Ini adalah skor leaderboard berbasis preferensi dan tidak boleh ditafsirkan sebagai peringkat kualitas universal untuk semua beban kerja produksi.

HappyHorse-1.0 vs Model Serupa

KemampuanHappyHorse-1.0Seedance 2.0Kling 3.0
Audio + Video GabunganYesYesPartial
1080p NativeYesYesYes
Arah Rilis TerbukaDiumumkanTertutupTertutup
Teksโ†’VideoYesYesYes
Gambarโ†’VideoYesYesYes
Sinkronisasi Bibir Multibahasa7 dilaporkanMultibahasaMultibahasa

Bagaimana cara menggunakan HappyHorse-1.0 dengan CometAPI?

  1. Dapatkan kredensial API.
  2. Pilih happyhorse-1.0.
  3. Kirim permintaan generasi dengan prompt + opsi generasi.
  4. Ambil output media yang dihasilkan.

FAQ

Harga untuk Happy Horse 1.0

Jelajahi harga kompetitif untuk Happy Horse 1.0, dirancang untuk berbagai anggaran dan kebutuhan penggunaan. Paket fleksibel kami memastikan Anda hanya membayar untuk apa yang Anda gunakan, memudahkan untuk meningkatkan skala seiring berkembangnya kebutuhan Anda. Temukan bagaimana Happy Horse 1.0 dapat meningkatkan proyek Anda sambil menjaga biaya tetap terkendali.

Happyhorse Video Generation Pricing

Pricing (Per Second)

Model720p1080p
happyhorse-1.0$0.112$0.192

๐Ÿ’ก Billed per second. Total cost = price per second ร— video duration (seconds).

Kode contoh dan API untuk Happy Horse 1.0

Akses kode sampel komprehensif dan sumber daya API untuk Happy Horse 1.0 guna mempermudah proses integrasi Anda. Dokumentasi terperinci kami menyediakan panduan langkah demi langkah, membantu Anda memanfaatkan potensi penuh Happy Horse 1.0 dalam proyek Anda.

#!/bin/bash

set -euo pipefail

if [[ -z "${COMETAPI_KEY:-}" ]]; then
  echo "Set COMETAPI_KEY before running this example."
  exit 0
fi

MODEL="happyhorse-1.0"
PROMPT='A miniature city built from cardboard and bottle caps comes alive at night. A tiny cardboard train rolls through the streets while warm lights glow from paper windows, physically realistic motion, detailed tabletop cinematography.'
VIDEO_SECONDS="3"
SIZE="1280x720"
BASE_URL="https://api.cometapi.com"
OUTPUT_DIR="./output"
POLL_INTERVAL_SECONDS=10
RETRY_DELAY_SECONDS=5
MAX_CREATE_ATTEMPTS=5

is_progress_complete() {
  local progress="$1"
  local normalized="${progress%%%}"

  if [[ -z "$normalized" ]]; then
    return 1
  fi

  [[ "$normalized" =~ ^[0-9]+([.][0-9]+)?$ ]] || return 1
  awk -v value="$normalized" 'BEGIN { exit !(value >= 100) }'
}

is_transient_status() {
  local status_code="$1"
  [[ "$status_code" == "429" || "$status_code" =~ ^5 ]]
}

create_task() {
  local attempt=1
  while (( attempt <= MAX_CREATE_ATTEMPTS )); do
    local response
    local status_code
    if ! response=$(curl -sS -w $'
%{http_code}' "${BASE_URL}/v1/videos"       -H "Authorization: Bearer $COMETAPI_KEY"       -F "model=${MODEL}"       -F "prompt=${PROMPT}"       -F "seconds=${VIDEO_SECONDS}"       -F "size=${SIZE}"); then
      if (( attempt == MAX_CREATE_ATTEMPTS )); then
        return 1
      fi
      echo "Create request failed, retrying..."
      sleep "$RETRY_DELAY_SECONDS"
      (( attempt += 1 ))
      continue
    fi

    status_code=$(echo "$response" | tail -n 1)
    CREATE_RESPONSE=$(echo "$response" | sed '$d')

    if [[ "$status_code" =~ ^2 ]]; then
      return 0
    fi

    if is_transient_status "$status_code" && (( attempt < MAX_CREATE_ATTEMPTS )); then
      echo "Create request returned ${status_code}, retrying..."
      sleep "$RETRY_DELAY_SECONDS"
      (( attempt += 1 ))
      continue
    fi

    echo "$CREATE_RESPONSE"
    return 1
  done
}

get_task() {
  local task_id="$1"
  while true; do
    local response
    local status_code
    if ! response=$(curl -sS -w $'
%{http_code}' "${BASE_URL}/v1/videos/${task_id}"       -H "Authorization: Bearer $COMETAPI_KEY"); then
      echo "Status request failed, retrying..."
      sleep "$RETRY_DELAY_SECONDS"
      continue
    fi

    status_code=$(echo "$response" | tail -n 1)
    TASK_RESPONSE=$(echo "$response" | sed '$d')

    if [[ "$status_code" =~ ^2 ]]; then
      return 0
    fi

    if is_transient_status "$status_code"; then
      echo "Status request returned ${status_code}, retrying..."
      sleep "$RETRY_DELAY_SECONDS"
      continue
    fi

    echo "$TASK_RESPONSE"
    return 1
  done
}

download_video() {
  local content_url="$1"
  local output_path="$2"

  while true; do
    if curl -fsS "$content_url"       -H "Authorization: Bearer $COMETAPI_KEY"       -o "$output_path"; then
      if [[ -s "$output_path" ]]; then
        return 0
      fi
      echo "Content download was empty, retrying..."
    else
      echo "Content request failed, retrying..."
    fi
    sleep "$RETRY_DELAY_SECONDS"
  done
}

create_task

TASK_ID=$(echo "$CREATE_RESPONSE" | jq -r '.id // .task_id // empty')

if [[ -z "$TASK_ID" ]]; then
  echo "$CREATE_RESPONSE" | jq .
  echo "No task id returned."
  exit 1
fi

echo "Task created: $TASK_ID"
echo "Initial status: $(echo "$CREATE_RESPONSE" | jq -r '.status // empty')"

while true; do
  get_task "$TASK_ID"

  STATUS=$(echo "$TASK_RESPONSE" | jq -r '.status // empty')
  NORMALIZED_STATUS=$(echo "$STATUS" | tr '[:upper:]' '[:lower:]')
  PROGRESS=$(echo "$TASK_RESPONSE" | jq -r '.progress // empty')
  SHOULD_DOWNLOAD=0

  if [[ "$NORMALIZED_STATUS" == "completed" || "$NORMALIZED_STATUS" == "success" ]]; then
    SHOULD_DOWNLOAD=1
  elif [[ "$NORMALIZED_STATUS" == "unknown" ]] && is_progress_complete "$PROGRESS"; then
    SHOULD_DOWNLOAD=1
  fi

  echo "Status: ${STATUS}, progress: ${PROGRESS}"

  if [[ "$SHOULD_DOWNLOAD" == "1" || "$NORMALIZED_STATUS" == "failed" || "$NORMALIZED_STATUS" == "error" ]]; then
    if [[ "$SHOULD_DOWNLOAD" != "1" ]]; then
      echo "$TASK_RESPONSE" | jq .
      exit 1
    fi

    VIDEO_URL=$(echo "$TASK_RESPONSE" | jq -r '.video_url // empty')
    CONTENT_URL="${BASE_URL}/v1/videos/${TASK_ID}/content"
    OUTPUT_PATH="${OUTPUT_DIR}/${TASK_ID}.mp4"

    mkdir -p "$OUTPUT_DIR"
    download_video "$CONTENT_URL" "$OUTPUT_PATH"

    echo "Video URL: ${VIDEO_URL}"
    echo "Content endpoint: ${CONTENT_URL}"
    echo "Saved to ${OUTPUT_PATH}"
    echo "File size: $(wc -c < "$OUTPUT_PATH" | tr -d ' ') bytes"
    break
  fi

  sleep "$POLL_INTERVAL_SECONDS"
done

cURL Code Example

#!/bin/bash

set -euo pipefail

if [[ -z "${COMETAPI_KEY:-}" ]]; then
  echo "Set COMETAPI_KEY before running this example."
  exit 0
fi

MODEL="happyhorse-1.0"
PROMPT='A miniature city built from cardboard and bottle caps comes alive at night. A tiny cardboard train rolls through the streets while warm lights glow from paper windows, physically realistic motion, detailed tabletop cinematography.'
VIDEO_SECONDS="3"
SIZE="1280x720"
BASE_URL="https://api.cometapi.com"
OUTPUT_DIR="./output"
POLL_INTERVAL_SECONDS=10
RETRY_DELAY_SECONDS=5
MAX_CREATE_ATTEMPTS=5

is_progress_complete() {
  local progress="$1"
  local normalized="${progress%%%}"

  if [[ -z "$normalized" ]]; then
    return 1
  fi

  [[ "$normalized" =~ ^[0-9]+([.][0-9]+)?$ ]] || return 1
  awk -v value="$normalized" 'BEGIN { exit !(value >= 100) }'
}

is_transient_status() {
  local status_code="$1"
  [[ "$status_code" == "429" || "$status_code" =~ ^5 ]]
}

create_task() {
  local attempt=1
  while (( attempt <= MAX_CREATE_ATTEMPTS )); do
    local response
    local status_code
    if ! response=$(curl -sS -w $'
%{http_code}' "${BASE_URL}/v1/videos"       -H "Authorization: Bearer $COMETAPI_KEY"       -F "model=${MODEL}"       -F "prompt=${PROMPT}"       -F "seconds=${VIDEO_SECONDS}"       -F "size=${SIZE}"); then
      if (( attempt == MAX_CREATE_ATTEMPTS )); then
        return 1
      fi
      echo "Create request failed, retrying..."
      sleep "$RETRY_DELAY_SECONDS"
      (( attempt += 1 ))
      continue
    fi

    status_code=$(echo "$response" | tail -n 1)
    CREATE_RESPONSE=$(echo "$response" | sed '$d')

    if [[ "$status_code" =~ ^2 ]]; then
      return 0
    fi

    if is_transient_status "$status_code" && (( attempt < MAX_CREATE_ATTEMPTS )); then
      echo "Create request returned ${status_code}, retrying..."
      sleep "$RETRY_DELAY_SECONDS"
      (( attempt += 1 ))
      continue
    fi

    echo "$CREATE_RESPONSE"
    return 1
  done
}

get_task() {
  local task_id="$1"
  while true; do
    local response
    local status_code
    if ! response=$(curl -sS -w $'
%{http_code}' "${BASE_URL}/v1/videos/${task_id}"       -H "Authorization: Bearer $COMETAPI_KEY"); then
      echo "Status request failed, retrying..."
      sleep "$RETRY_DELAY_SECONDS"
      continue
    fi

    status_code=$(echo "$response" | tail -n 1)
    TASK_RESPONSE=$(echo "$response" | sed '$d')

    if [[ "$status_code" =~ ^2 ]]; then
      return 0
    fi

    if is_transient_status "$status_code"; then
      echo "Status request returned ${status_code}, retrying..."
      sleep "$RETRY_DELAY_SECONDS"
      continue
    fi

    echo "$TASK_RESPONSE"
    return 1
  done
}

download_video() {
  local content_url="$1"
  local output_path="$2"

  while true; do
    if curl -fsS "$content_url"       -H "Authorization: Bearer $COMETAPI_KEY"       -o "$output_path"; then
      if [[ -s "$output_path" ]]; then
        return 0
      fi
      echo "Content download was empty, retrying..."
    else
      echo "Content request failed, retrying..."
    fi
    sleep "$RETRY_DELAY_SECONDS"
  done
}

create_task

TASK_ID=$(echo "$CREATE_RESPONSE" | jq -r '.id // .task_id // empty')

if [[ -z "$TASK_ID" ]]; then
  echo "$CREATE_RESPONSE" | jq .
  echo "No task id returned."
  exit 1
fi

echo "Task created: $TASK_ID"
echo "Initial status: $(echo "$CREATE_RESPONSE" | jq -r '.status // empty')"

while true; do
  get_task "$TASK_ID"

  STATUS=$(echo "$TASK_RESPONSE" | jq -r '.status // empty')
  NORMALIZED_STATUS=$(echo "$STATUS" | tr '[:upper:]' '[:lower:]')
  PROGRESS=$(echo "$TASK_RESPONSE" | jq -r '.progress // empty')
  SHOULD_DOWNLOAD=0

  if [[ "$NORMALIZED_STATUS" == "completed" || "$NORMALIZED_STATUS" == "success" ]]; then
    SHOULD_DOWNLOAD=1
  elif [[ "$NORMALIZED_STATUS" == "unknown" ]] && is_progress_complete "$PROGRESS"; then
    SHOULD_DOWNLOAD=1
  fi

  echo "Status: ${STATUS}, progress: ${PROGRESS}"

  if [[ "$SHOULD_DOWNLOAD" == "1" || "$NORMALIZED_STATUS" == "failed" || "$NORMALIZED_STATUS" == "error" ]]; then
    if [[ "$SHOULD_DOWNLOAD" != "1" ]]; then
      echo "$TASK_RESPONSE" | jq .
      exit 1
    fi

    VIDEO_URL=$(echo "$TASK_RESPONSE" | jq -r '.video_url // empty')
    CONTENT_URL="${BASE_URL}/v1/videos/${TASK_ID}/content"
    OUTPUT_PATH="${OUTPUT_DIR}/${TASK_ID}.mp4"

    mkdir -p "$OUTPUT_DIR"
    download_video "$CONTENT_URL" "$OUTPUT_PATH"

    echo "Video URL: ${VIDEO_URL}"
    echo "Content endpoint: ${CONTENT_URL}"
    echo "Saved to ${OUTPUT_PATH}"
    echo "File size: $(wc -c < "$OUTPUT_PATH" | tr -d ' ') bytes"
    break
  fi

  sleep "$POLL_INTERVAL_SECONDS"
done

Python Code Example

import json
import os
import time

import requests


MODEL = "happyhorse-1.0"
PROMPT = (
    "A miniature city built from cardboard and bottle caps comes alive at night. "
    "A tiny cardboard train rolls through the streets while warm lights glow from "
    "paper windows, physically realistic motion, detailed tabletop cinematography."
)
SECONDS = "3"
SIZE = "1280x720"
BASE_URL = "https://api.cometapi.com"
OUTPUT_DIR = "./output"
POLL_INTERVAL_SECONDS = 10
RETRY_DELAY_SECONDS = 5
MAX_CREATE_ATTEMPTS = 5
TERMINAL_STATUSES = {"completed", "failed", "error", "success"}
SUCCESS_STATUSES = {"completed", "success"}


try:
    COMETAPI_KEY = os.environ["COMETAPI_KEY"]
except KeyError:
    print("Set COMETAPI_KEY before running this example.")
    raise SystemExit(0)


headers = {"Authorization": f"Bearer {COMETAPI_KEY}"}


def is_progress_complete(progress):
    if isinstance(progress, (int, float)):
        return progress >= 100
    if isinstance(progress, str):
        try:
            return float(progress.rstrip("%")) >= 100
        except ValueError:
            return False
    return False


def is_transient_status(status_code):
    return status_code == 429 or 500 <= status_code < 600


def create_task():
    fields = {
        "model": (None, MODEL),
        "prompt": (None, PROMPT),
        "seconds": (None, SECONDS),
        "size": (None, SIZE),
    }
    for attempt in range(1, MAX_CREATE_ATTEMPTS + 1):
        try:
            response = requests.post(
                f"{BASE_URL}/v1/videos",
                headers=headers,
                files=fields,
                timeout=30,
            )
        except requests.RequestException as error:
            if attempt == MAX_CREATE_ATTEMPTS:
                raise
            print(f"Create request failed: {error}, retrying...")
            time.sleep(RETRY_DELAY_SECONDS)
            continue

        if response.ok:
            return response
        if not is_transient_status(response.status_code) or attempt == MAX_CREATE_ATTEMPTS:
            response.raise_for_status()
        print(f"Create request returned {response.status_code}, retrying...")
        time.sleep(RETRY_DELAY_SECONDS)
    raise SystemExit("Failed to create task.")


def get_task(task_id):
    while True:
        try:
            response = requests.get(
                f"{BASE_URL}/v1/videos/{task_id}",
                headers=headers,
                timeout=15,
            )
        except requests.RequestException as error:
            print(f"Status request failed: {error}, retrying...")
            time.sleep(RETRY_DELAY_SECONDS)
            continue

        if response.ok:
            return response
        if not is_transient_status(response.status_code):
            response.raise_for_status()
        print(f"Status request returned {response.status_code}, retrying...")
        time.sleep(RETRY_DELAY_SECONDS)


def download_video(content_url, output_path):
    while True:
        try:
            with requests.get(content_url, headers=headers, timeout=120, stream=True) as response:
                if response.ok:
                    with open(output_path, "wb") as output_file:
                        for chunk in response.iter_content(chunk_size=8192):
                            if chunk:
                                output_file.write(chunk)
                    return
                if not is_transient_status(response.status_code):
                    response.raise_for_status()
                print(f"Content request returned {response.status_code}, retrying...")
        except requests.RequestException as error:
            print(f"Content request failed: {error}, retrying...")
        time.sleep(RETRY_DELAY_SECONDS)


create_response = create_task()
create_result = create_response.json()

task_id = create_result.get("id") or create_result.get("task_id")
if not task_id:
    print(json.dumps(create_result, indent=2))
    raise SystemExit("No task id returned.")

print(f"Task created: {task_id}")
print(f"Initial status: {create_result.get('status')}")

while True:
    task_response = get_task(task_id)
    task = task_response.json()
    status = str(task.get("status") or "unknown")
    normalized_status = status.lower()
    progress = task.get("progress")
    should_download = normalized_status in SUCCESS_STATUSES or (
        normalized_status == "unknown" and is_progress_complete(progress)
    )

    print(f"Status: {status}, progress: {progress}")

    if should_download or normalized_status in TERMINAL_STATUSES:
        if not should_download:
            print(json.dumps(task, indent=2))
            raise SystemExit(1)

        content_url = f"{BASE_URL}/v1/videos/{task_id}/content"
        output_path = os.path.join(OUTPUT_DIR, f"{task_id}.mp4")
        os.makedirs(OUTPUT_DIR, exist_ok=True)
        download_video(content_url, output_path)

        print(f"Video URL: {task.get('video_url') or ''}")
        print(f"Content endpoint: {content_url}")
        print(f"Saved to {output_path}")
        print(f"File size: {os.path.getsize(output_path)} bytes")
        break

    time.sleep(POLL_INTERVAL_SECONDS)

JavaScript Code Example

import fs from "fs";
import path from "path";

const model = "happyhorse-1.0";
const prompt = "A miniature city built from cardboard and bottle caps comes alive at night. A tiny cardboard train rolls through the streets while warm lights glow from paper windows, physically realistic motion, detailed tabletop cinematography.";
const seconds = "3";
const size = "1280x720";
const api_key = process.env.COMETAPI_KEY;
const base_url = "https://api.cometapi.com";
const output_dir = "./output";
const poll_interval_ms = 10_000;
const retry_delay_ms = 5_000;
const max_create_attempts = 5;
const terminal_statuses = new Set(["completed", "failed", "error", "success"]);
const success_statuses = new Set(["completed", "success"]);

if (!api_key) {
  console.log("Set COMETAPI_KEY before running this example.");
  process.exit(0);
}

function is_progress_complete(progress) {
  if (typeof progress === "number") {
    return progress >= 100;
  }
  if (typeof progress === "string") {
    const numeric = Number(progress.replace(/%$/, ""));
    return Number.isFinite(numeric) && numeric >= 100;
  }
  return false;
}

function is_transient_status(status) {
  return status === 429 || status >= 500;
}

async function sleep(ms) {
  await new Promise((resolve) => setTimeout(resolve, ms));
}

async function fetch_with_retry(url, options, attempts, label) {
  for (let attempt = 1; attempt <= attempts; attempt += 1) {
    try {
      const response = await fetch(url, options);
      if (response.ok) {
        return response;
      }
      if (!is_transient_status(response.status) || attempt === attempts) {
        return response;
      }
      console.log(`${label} returned ${response.status}, retrying...`);
    } catch (error) {
      if (attempt === attempts) {
        throw error;
      }
      console.log(`${label} failed: ${error.message}, retrying...`);
    }
    await sleep(retry_delay_ms);
  }
  throw new Error(`${label} failed`);
}

async function fetch_status(task_id) {
  while (true) {
    try {
      const response = await fetch(`${base_url}/v1/videos/${task_id}`, { headers });
      if (response.ok) {
        return response;
      }
      if (!is_transient_status(response.status)) {
        return response;
      }
      console.log(`Status request returned ${response.status}, retrying...`);
    } catch (error) {
      console.log(`Status request failed: ${error.message}, retrying...`);
    }
    await sleep(retry_delay_ms);
  }
}

async function download_video(content_url, output_path) {
  while (true) {
    try {
      const response = await fetch(content_url, { headers });
      if (response.ok) {
        const video_buffer = Buffer.from(await response.arrayBuffer());
        fs.writeFileSync(output_path, video_buffer);
        return;
      }
      if (!is_transient_status(response.status)) {
        console.log(await response.text());
        process.exit(1);
      }
      console.log(`Content request returned ${response.status}, retrying...`);
    } catch (error) {
      console.log(`Content request failed: ${error.message}, retrying...`);
    }
    await sleep(retry_delay_ms);
  }
}

const form = new FormData();
form.set("model", model);
form.set("prompt", prompt);
form.set("seconds", seconds);
form.set("size", size);

const headers = { Authorization: `Bearer ${api_key}` };
const create_response = await fetch_with_retry(`${base_url}/v1/videos`, {
  method: "POST",
  headers,
  body: form,
}, max_create_attempts, "Create request");

if (!create_response.ok) {
  console.log(await create_response.text());
  process.exit(1);
}

const create_result = await create_response.json();
const task_id = create_result.id || create_result.task_id;

if (!task_id) {
  console.log(JSON.stringify(create_result, null, 2));
  process.exit(1);
}

console.log(`Task created: ${task_id}`);
console.log(`Initial status: ${create_result.status}`);

while (true) {
  const task_response = await fetch_status(task_id);

  if (!task_response.ok) {
    console.log(await task_response.text());
    process.exit(1);
  }

  const task = await task_response.json();
  const status = String(task.status || "unknown");
  const normalized_status = status.toLowerCase();
  const progress = task.progress;
  const should_download = success_statuses.has(normalized_status) || (
    normalized_status === "unknown" && is_progress_complete(progress)
  );

  console.log(`Status: ${status}, progress: ${progress}`);

  if (should_download || terminal_statuses.has(normalized_status)) {
    if (!should_download) {
      console.log(JSON.stringify(task, null, 2));
      process.exit(1);
    }

    const content_url = `${base_url}/v1/videos/${task_id}/content`;
    const output_path = path.join(output_dir, `${task_id}.mp4`);
    fs.mkdirSync(output_dir, { recursive: true });
    await download_video(content_url, output_path);

    console.log(`Video URL: ${task.video_url || ""}`);
    console.log(`Content endpoint: ${content_url}`);
    console.log(`Saved to ${output_path}`);
    console.log(`File size: ${fs.statSync(output_path).size} bytes`);
    break;
  }

  await sleep(poll_interval_ms);
}

Versi Happy Horse 1.0

Alasan Happy Horse 1.0 memiliki beberapa _snapshot_ mungkin mencakup faktor-faktor potensial seperti variasi keluaran setelah pembaruan yang memerlukan _snapshot_ lama untuk konsistensi, memberikan masa transisi bagi pengembang untuk beradaptasi dan bermigrasi, serta _snapshot_ berbeda yang sesuai dengan _endpoint_ global atau regional untuk mengoptimalkan pengalaman pengguna. Untuk perbedaan detail antar versi, silakan merujuk ke dokumentasi resmi.

Version
happyhorse-1.0