Accédez à des exemples de code complets et aux ressources API pour Happy Horse 1.0 afin de simplifier votre processus d'intégration. Notre documentation détaillée fournit des instructions étape par étape pour vous aider à exploiter tout le potentiel de Happy Horse 1.0 dans vos projets.
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);
}