"""豆包视频中转示例:pip install requests export RELAY_BASE_URL=https://your-api-host export RELAY_API_KEY=your-customer-key 调用 create() 会提交真实任务,请在确认额度后使用。 """ import os import time import uuid import mimetypes from pathlib import Path import requests class RelayClient: def __init__(self, base_url=None, api_key=None): self.base = (base_url or os.environ["RELAY_BASE_URL"]).rstrip("/") self.session = requests.Session() self.session.headers["Authorization"] = "Bearer " + (api_key or os.environ["RELAY_API_KEY"]) def _call(self, method, route, **kwargs): response = self.session.request(method, self.base + route, timeout=(10, 90), **kwargs) if not response.ok: try: code = response.json().get("error", {}).get("code", "unknown_error") except ValueError: code = "non_json_response" raise RuntimeError(f"HTTP {response.status_code}: {code}") return response.json() def upload(self, filename): path = Path(filename) mime = mimetypes.guess_type(path.name)[0] if mime not in {"image/png", "image/jpeg", "image/webp"}: raise ValueError("仅支持 PNG / JPEG / WebP") if path.stat().st_size > 10 * 1024 * 1024: raise ValueError("图片超过 10 MiB") return self._call("POST", "/v1/images", data=path.read_bytes(), headers={"Content-Type": mime})["id"] def create(self, prompt, request_id, image_ids=None, ratio="9:16", seconds=10, use_fast=True): if type(seconds) is not int or not 4 <= seconds <= 15: raise ValueError("seconds 必须为 4–15 的整数秒数") # 由调用方保存 request_id;超时重试时使用同一标识和完全相同的内容。 return self._call("POST", "/v1/videos", json={"prompt": prompt, "image_ids": image_ids or [], "ratio": ratio, "seconds": seconds, "useFast": use_fast}, headers={"Idempotency-Key": request_id}) def task(self, task_id): task_id = str(uuid.UUID(task_id)) return self._call("GET", f"/v1/videos/{task_id}") def wait(self, task_id, timeout=1800): deadline = time.monotonic() + timeout while time.monotonic() < deadline: task = self.task(task_id) if task["status"] in {"complete", "failed", "paused", "unknown", "cancelled", "legacy"}: return task time.sleep(15) raise TimeoutError("等待超时不代表失败;保存任务编号,继续查询或联系管理员,不要盲目重新提交。") def result(self, task_id): task_id = str(uuid.UUID(task_id)) # 返回地址即可。若下载视频,请使用不带本接口 Authorization 的新请求。 return self._call("GET", f"/v1/videos/{task_id}/result")["video_url"] # 使用示例(手动取消注释才会提交): # client = RelayClient() # request_id = str(uuid.uuid4()) # 提交前持久保存这个值,供超时后重试使用。 # image_ids = [client.upload("reference1.png"), client.upload("reference2.png")] # task = client.create("你的视频提示词", request_id, image_ids=image_ids, seconds=15) # result = client.wait(task["id"]) # if result["status"] == "complete": # print(client.result(task["id"]))