[{"data":1,"prerenderedAt":11257},["ShallowReactive",2],{"blog-/zh/blog/seedance-2-api-tutorial-python":3},{"id":4,"title":5,"body":6,"description":11246,"extension":11247,"meta":11248,"navigation":57,"path":11253,"seo":11254,"stem":11255,"__hash__":11256},"content/zh/blog/seedance-2-api-tutorial-python.md","Seedance 2.0 API 教程：用 Python 从零生成你的第一个 AI 视频",{"type":7,"value":8,"toc":11188},"minimark",[9,13,16,31,43,46,51,54,65,68,90,94,123,126,199,201,205,214,217,246,249,270,289,298,306,308,312,315,329,336,470,473,508,511,1027,1030,1066,1240,1250,1253,1261,1263,1267,1270,1736,1739,1827,1830,1842,1846,1853,2031,2034,2155,2169,2172,2177,2185,2188,2196,2203,2210,2212,2216,2223,2259,2262,2265,2271,2341,2344,2350,2356,2417,2429,2432,2435,2561,2575,2582,2592,2594,2598,2610,2616,2879,2882,2931,2935,2963,2966,3048,3051,3203,3209,3212,3266,3276,3284,3288,3291,3370,3377,3379,3382,3385,3605,3632,3635,3640,3644,3735,3743,3748,3843,3850,3855,3943,3948,4023,4040,4043,4046,4117,4130,4148,4150,4153,4156,4159,4162,4220,4241,4244,4249,4292,4298,4303,4357,4362,4367,4419,4429,4434,4486,4502,4507,4550,4557,4562,4616,4622,4627,4670,4673,4678,4721,4724,4729,4783,4789,4792,5002,5005,5008,5012,5765,5768,5800,5807,5810,5813,6393,6404,6406,6410,6417,6420,6426,6431,6589,6595,6599,6661,6665,6668,7859,7862,7885,7888,7915,7919,7928,7976,7979,7985,7990,8037,8052,8056,8059,8231,8235,8313,8320,8322,8326,8329,8334,8882,8885,8907,8909,8912,8915,8918,8957,8960,8982,8985,8988,9021,9031,9033,9036,9040,9046,9050,9056,9060,9066,9070,9081,9085,9098,9102,9112,9116,9131,9135,9142,9146,9162,9166,9173,9175,9178,9181,11161,11176,11184],[10,11,12],"p",{},"Seedance 2.0 是字节跳动最强的 AI 视频生成模型——支持多模态引用、原生音频生成、电影级镜头控制，可生成 4–15 秒、最高 1080p 的视频。本教程将带你用 Python 走完整个 API 调用流程：从获取 API Key 到下载你生成的第一个视频。",[10,14,15],{},"读完这篇教程，你将拥有文生视频、图生视频、异步轮询、Webhook 回调和错误重试的完整可运行代码。所有代码示例都经过真实 API 测试验证。",[17,18,19],"blockquote",{},[10,20,21,25,26,30],{},[22,23,24],"strong",{},"关于 Seedance 2.0 与 1.5 的说明："," Seedance 2.0 正在逐步开放中。你现在就可以用 ",[27,28,29],"code",{},"seedance-1.5-pro"," 测试完整流程——等 2.0 全面上线后，只需改一下模型名称即可。所有接口、参数和返回格式完全一致。2.0 的主要升级：多模态引用（图片、视频、音频混合输入）、原生音频生成、更好的物理模拟和视频编辑能力。本教程的所有内容对两个版本都适用。",[10,32,33],{},[22,34,35,42],{},[36,37,41],"a",{"href":38,"rel":39},"https://evolink.ai/early-access",[40],"nofollow","免费获取 API Key","，跟着教程一起动手。",[44,45],"hr",{},[47,48,50],"h2",{"id":49},"你将构建什么以及你需要什么","你将构建什么（以及你需要什么）",[10,52,53],{},"先看看 Seedance 生成的视频效果——只用了一次 API 调用：",[55,56,59,60],"video",{"autoPlay":57,"loop":57,"muted":57,"playsInline":57,"style":58},true,"width:100%;border-radius:12px;margin:1.5em 0","\n  ",[61,62],"source",{"src":63,"type":64},"https://cdn.evolink.ai/seedance2api/%E5%B0%8F%E5%A5%B3%E5%AD%A9%E5%9B%BE%E4%B9%A6%E9%A6%86%E5%A5%87%E9%81%87.mp4","video/mp4",[10,66,67],{},"在本教程中，你将编写 Python 代码实现以下功能：",[69,70,71,75,78,81,84,87],"ol",{},[72,73,74],"li",{},"发送文本提示词 → 获得生成的视频",[72,76,77],{},"发送图片 → 将静态图片转为动态视频",[72,79,80],{},"异步轮询获取结果",[72,82,83],{},"像生产环境一样处理错误和重试",[72,85,86],{},"通过 Webhook 接收结果（无需轮询）",[72,88,89],{},"取消进行中的任务",[91,92,93],"h3",{"id":93},"前置条件",[95,96,97,107,117],"ul",{},[72,98,99,102,103,106],{},[22,100,101],{},"Python 3.8+","（用 ",[27,104,105],{},"python3 --version"," 检查）",[72,108,109,112,113,116],{},[22,110,111],{},"requests"," 库（",[27,114,115],{},"pip install requests","）",[72,118,119,122],{},[22,120,121],{},"一个 EvoLink API Key","（免费注册——下一节会讲怎么获取）",[10,124,125],{},"不需要 GPU，不需要 Docker，不需要复杂配置。只要 Python 和一个 API Key。",[17,127,128,134],{},[10,129,130,133],{},[22,131,132],{},"小贴士："," 如果你在开发正式项目，建议用虚拟环境隔离依赖：",[135,136,141],"pre",{"className":137,"code":138,"language":139,"meta":140,"style":140},"language-bash shiki shiki-themes github-dark","python3 -m venv seedance-env\nsource seedance-env/bin/activate  # macOS/Linux\nseedance-env\\Scripts\\activate     # Windows\npip install requests flask\n","bash","",[27,142,143,163,175,184],{"__ignoreMap":140},[144,145,148,152,156,160],"span",{"class":146,"line":147},"line",1,[144,149,151],{"class":150},"svObZ","python3",[144,153,155],{"class":154},"sDLfK"," -m",[144,157,159],{"class":158},"sU2Wk"," venv",[144,161,162],{"class":158}," seedance-env\n",[144,164,166,168,171],{"class":146,"line":165},2,[144,167,61],{"class":154},[144,169,170],{"class":158}," seedance-env/bin/activate",[144,172,174],{"class":173},"sAwPA","  # macOS/Linux\n",[144,176,178,181],{"class":146,"line":177},3,[144,179,180],{"class":150},"seedance-env\\Scripts\\activate",[144,182,183],{"class":173},"     # Windows\n",[144,185,187,190,193,196],{"class":146,"line":186},4,[144,188,189],{"class":150},"pip",[144,191,192],{"class":158}," install",[144,194,195],{"class":158}," requests",[144,197,198],{"class":158}," flask\n",[44,200],{},[47,202,204],{"id":203},"获取-api-key","获取 API Key",[10,206,207,208,213],{},"Seedance 2.0 通过 ",[36,209,212],{"href":210,"rel":211},"https://evolink.ai",[40],"EvoLink"," 提供接入。EvoLink 是一个 API 网关，通过一个 API Key 统一接入多个 AI 视频模型——包括 Seedance 2.0、Kling 等。",[10,215,216],{},"获取步骤：",[69,218,219,227,233,239],{},[72,220,221,222,226],{},"访问 ",[36,223,225],{"href":38,"rel":224},[40],"evolink.ai/early-access"," 注册账号",[72,228,229,230],{},"进入 ",[22,231,232],{},"Dashboard → API Keys",[72,234,235,236],{},"点击 ",[22,237,238],{},"Create New Key",[72,240,241,242,245],{},"复制你的 Key——以 ",[27,243,244],{},"sk-"," 开头",[10,247,248],{},"妥善保管你的 Key，不要提交到版本控制。我们用环境变量来存储：",[135,250,252],{"className":137,"code":251,"language":139,"meta":140,"style":140},"export EVOLINK_API_KEY=\"sk-your-api-key-here\"\n",[27,253,254],{"__ignoreMap":140},[144,255,256,260,264,267],{"class":146,"line":147},[144,257,259],{"class":258},"snl16","export",[144,261,263],{"class":262},"s95oV"," EVOLINK_API_KEY",[144,265,266],{"class":258},"=",[144,268,269],{"class":158},"\"sk-your-api-key-here\"\n",[10,271,272,273,276,277,280,281,284,285,288],{},"这行命令在当前终端会话中设置 ",[27,274,275],{},"EVOLINK_API_KEY"," 环境变量。macOS/Linux 用户可以把它加到 ",[27,278,279],{},"~/.bashrc"," 或 ",[27,282,283],{},"~/.zshrc"," 中实现持久化。Windows 用户在命令提示符中用 ",[27,286,287],{},"set EVOLINK_API_KEY=sk-your-api-key-here","，或者在系统属性 → 环境变量中设置。",[10,290,291,292,297],{},"注册后账户会包含初始额度供你体验。具体定价详情请查看",[36,293,296],{"href":294,"rel":295},"https://seedance2api.app/docs/getting-started",[40],"快速入门文档","。",[17,299,300],{},[10,301,302,305],{},[22,303,304],{},"常见错误："," 不要把 API Key 硬编码在源文件里。如果你推送到 GitHub，自动化爬虫几分钟内就会发现它。务必使用环境变量或密钥管理服务（如 AWS Secrets Manager 或 HashiCorp Vault）。",[44,307],{},[47,309,311],{"id":310},"搭建-python-环境","搭建 Python 环境",[10,313,314],{},"安装唯一需要的依赖：",[135,316,318],{"className":137,"code":317,"language":139,"meta":140,"style":140},"pip install requests\n",[27,319,320],{"__ignoreMap":140},[144,321,322,324,326],{"class":146,"line":147},[144,323,189],{"class":150},[144,325,192],{"class":158},[144,327,328],{"class":158}," requests\n",[10,330,331,332,335],{},"创建一个名为 ",[27,333,334],{},"seedance_tutorial.py"," 的文件，添加以下基础代码。本教程的所有示例都基于这个基础：",[135,337,341],{"className":338,"code":339,"language":340,"meta":140,"style":140},"language-python shiki shiki-themes github-dark","import requests\nimport time\nimport os\nimport json\n\n# ── 配置 ─────────────────────────────────────────────────────\nAPI_KEY = os.getenv(\"EVOLINK_API_KEY\", \"sk-your-api-key-here\")\nBASE_URL = \"https://api.evolink.ai/v1\"\nHEADERS = {\n    \"Authorization\": f\"Bearer {API_KEY}\",\n    \"Content-Type\": \"application/json\"\n}\n","python",[27,342,343,350,357,364,371,377,383,407,418,429,453,464],{"__ignoreMap":140},[144,344,345,348],{"class":146,"line":147},[144,346,347],{"class":258},"import",[144,349,328],{"class":262},[144,351,352,354],{"class":146,"line":165},[144,353,347],{"class":258},[144,355,356],{"class":262}," time\n",[144,358,359,361],{"class":146,"line":177},[144,360,347],{"class":258},[144,362,363],{"class":262}," os\n",[144,365,366,368],{"class":146,"line":186},[144,367,347],{"class":258},[144,369,370],{"class":262}," json\n",[144,372,374],{"class":146,"line":373},5,[144,375,376],{"emptyLinePlaceholder":57},"\n",[144,378,380],{"class":146,"line":379},6,[144,381,382],{"class":173},"# ── 配置 ─────────────────────────────────────────────────────\n",[144,384,386,389,392,395,398,401,404],{"class":146,"line":385},7,[144,387,388],{"class":154},"API_KEY",[144,390,391],{"class":258}," =",[144,393,394],{"class":262}," os.getenv(",[144,396,397],{"class":158},"\"EVOLINK_API_KEY\"",[144,399,400],{"class":262},", ",[144,402,403],{"class":158},"\"sk-your-api-key-here\"",[144,405,406],{"class":262},")\n",[144,408,410,413,415],{"class":146,"line":409},8,[144,411,412],{"class":154},"BASE_URL",[144,414,391],{"class":258},[144,416,417],{"class":158}," \"https://api.evolink.ai/v1\"\n",[144,419,421,424,426],{"class":146,"line":420},9,[144,422,423],{"class":154},"HEADERS",[144,425,391],{"class":258},[144,427,428],{"class":262}," {\n",[144,430,432,435,438,441,444,447,450],{"class":146,"line":431},10,[144,433,434],{"class":158},"    \"Authorization\"",[144,436,437],{"class":262},": ",[144,439,440],{"class":258},"f",[144,442,443],{"class":158},"\"Bearer ",[144,445,446],{"class":154},"{API_KEY}",[144,448,449],{"class":158},"\"",[144,451,452],{"class":262},",\n",[144,454,456,459,461],{"class":146,"line":455},11,[144,457,458],{"class":158},"    \"Content-Type\"",[144,460,437],{"class":262},[144,462,463],{"class":158},"\"application/json\"\n",[144,465,467],{"class":146,"line":466},12,[144,468,469],{"class":262},"}\n",[10,471,472],{},"逐行解释：",[95,474,475,483,493],{},[72,476,477,482],{},[22,478,479],{},[27,480,481],{},"os.getenv(\"EVOLINK_API_KEY\", \"sk-your-api-key-here\")"," — 从环境变量读取 API Key。第二个参数是默认值（仅在本地测试时替换为你的真实 Key）。",[72,484,485,489,490,297],{},[22,486,487],{},[27,488,412],{}," — 所有 EvoLink API 端点的根 URL。所有请求都发送到 ",[27,491,492],{},"https://api.evolink.ai/v1/...",[72,494,495,499,500,503,504,507],{},[22,496,497],{},[27,498,423],{}," — 每个请求都携带的两个 Header：",[27,501,502],{},"Authorization"," 使用 Bearer Token 方式携带 API Key，",[27,505,506],{},"Content-Type"," 告诉服务器我们发送的是 JSON。",[10,509,510],{},"接下来添加可复用的辅助函数：",[135,512,514],{"className":338,"code":513,"language":340,"meta":140,"style":140},"# ── 可复用的轮询辅助函数 ─────────────────────────────────────\ndef wait_for_video(task_id, poll_interval=10, timeout=600):\n    \"\"\"\n    轮询视频生成任务，直到完成或失败。\n    \n    Args:\n        task_id: 生成接口返回的任务 ID。\n        poll_interval: 轮询间隔秒数（默认 10）。\n        timeout: 最大等待时间秒数（默认 600）。\n    \n    Returns:\n        dict: 包含视频 URL 的已完成任务响应。\n    \n    Raises:\n        TimeoutError: 任务在超时时间内未完成。\n        RuntimeError: 任务失败。\n    \"\"\"\n    elapsed = 0\n    while elapsed \u003C timeout:\n        # 发送 GET 请求检查任务当前状态\n        response = requests.get(\n            f\"{BASE_URL}/tasks/{task_id}\",\n            headers=HEADERS\n        )\n        # 如果 HTTP 状态码表示错误，抛出异常\n        response.raise_for_status()\n        task = response.json()\n\n        # 从响应中提取状态和进度\n        status = task[\"status\"]\n        progress = task.get(\"progress\", 0)\n        print(f\"  [{elapsed}s] Status: {status} | Progress: {progress}%\")\n\n        # 检查终态\n        if status == \"completed\":\n            return task\n        elif status == \"failed\":\n            error_info = task.get(\"error\", {})\n            raise RuntimeError(\n                f\"Task {task_id} failed: {error_info.get('message', 'Unknown error')}\"\n            )\n\n        # 等待后再次轮询\n        time.sleep(poll_interval)\n        elapsed += poll_interval\n\n    raise TimeoutError(f\"Task {task_id} timed out after {timeout}s\")\n",[27,515,516,521,548,553,558,563,568,573,578,583,587,592,597,602,608,614,620,625,636,651,657,668,695,707,713,719,725,736,741,747,764,785,831,836,842,860,869,884,900,912,951,957,962,968,974,986,991],{"__ignoreMap":140},[144,517,518],{"class":146,"line":147},[144,519,520],{"class":173},"# ── 可复用的轮询辅助函数 ─────────────────────────────────────\n",[144,522,523,526,529,532,534,537,540,542,545],{"class":146,"line":165},[144,524,525],{"class":258},"def",[144,527,528],{"class":150}," wait_for_video",[144,530,531],{"class":262},"(task_id, poll_interval",[144,533,266],{"class":258},[144,535,536],{"class":154},"10",[144,538,539],{"class":262},", timeout",[144,541,266],{"class":258},[144,543,544],{"class":154},"600",[144,546,547],{"class":262},"):\n",[144,549,550],{"class":146,"line":177},[144,551,552],{"class":158},"    \"\"\"\n",[144,554,555],{"class":146,"line":186},[144,556,557],{"class":158},"    轮询视频生成任务，直到完成或失败。\n",[144,559,560],{"class":146,"line":373},[144,561,562],{"class":158},"    \n",[144,564,565],{"class":146,"line":379},[144,566,567],{"class":158},"    Args:\n",[144,569,570],{"class":146,"line":385},[144,571,572],{"class":158},"        task_id: 生成接口返回的任务 ID。\n",[144,574,575],{"class":146,"line":409},[144,576,577],{"class":158},"        poll_interval: 轮询间隔秒数（默认 10）。\n",[144,579,580],{"class":146,"line":420},[144,581,582],{"class":158},"        timeout: 最大等待时间秒数（默认 600）。\n",[144,584,585],{"class":146,"line":431},[144,586,562],{"class":158},[144,588,589],{"class":146,"line":455},[144,590,591],{"class":158},"    Returns:\n",[144,593,594],{"class":146,"line":466},[144,595,596],{"class":158},"        dict: 包含视频 URL 的已完成任务响应。\n",[144,598,600],{"class":146,"line":599},13,[144,601,562],{"class":158},[144,603,605],{"class":146,"line":604},14,[144,606,607],{"class":158},"    Raises:\n",[144,609,611],{"class":146,"line":610},15,[144,612,613],{"class":158},"        TimeoutError: 任务在超时时间内未完成。\n",[144,615,617],{"class":146,"line":616},16,[144,618,619],{"class":158},"        RuntimeError: 任务失败。\n",[144,621,623],{"class":146,"line":622},17,[144,624,552],{"class":158},[144,626,628,631,633],{"class":146,"line":627},18,[144,629,630],{"class":262},"    elapsed ",[144,632,266],{"class":258},[144,634,635],{"class":154}," 0\n",[144,637,639,642,645,648],{"class":146,"line":638},19,[144,640,641],{"class":258},"    while",[144,643,644],{"class":262}," elapsed ",[144,646,647],{"class":258},"\u003C",[144,649,650],{"class":262}," timeout:\n",[144,652,654],{"class":146,"line":653},20,[144,655,656],{"class":173},"        # 发送 GET 请求检查任务当前状态\n",[144,658,660,663,665],{"class":146,"line":659},21,[144,661,662],{"class":262},"        response ",[144,664,266],{"class":258},[144,666,667],{"class":262}," requests.get(\n",[144,669,671,674,676,679,682,685,688,691,693],{"class":146,"line":670},22,[144,672,673],{"class":258},"            f",[144,675,449],{"class":158},[144,677,678],{"class":154},"{BASE_URL}",[144,680,681],{"class":158},"/tasks/",[144,683,684],{"class":154},"{",[144,686,687],{"class":262},"task_id",[144,689,690],{"class":154},"}",[144,692,449],{"class":158},[144,694,452],{"class":262},[144,696,698,702,704],{"class":146,"line":697},23,[144,699,701],{"class":700},"s9osk","            headers",[144,703,266],{"class":258},[144,705,706],{"class":154},"HEADERS\n",[144,708,710],{"class":146,"line":709},24,[144,711,712],{"class":262},"        )\n",[144,714,716],{"class":146,"line":715},25,[144,717,718],{"class":173},"        # 如果 HTTP 状态码表示错误，抛出异常\n",[144,720,722],{"class":146,"line":721},26,[144,723,724],{"class":262},"        response.raise_for_status()\n",[144,726,728,731,733],{"class":146,"line":727},27,[144,729,730],{"class":262},"        task ",[144,732,266],{"class":258},[144,734,735],{"class":262}," response.json()\n",[144,737,739],{"class":146,"line":738},28,[144,740,376],{"emptyLinePlaceholder":57},[144,742,744],{"class":146,"line":743},29,[144,745,746],{"class":173},"        # 从响应中提取状态和进度\n",[144,748,750,753,755,758,761],{"class":146,"line":749},30,[144,751,752],{"class":262},"        status ",[144,754,266],{"class":258},[144,756,757],{"class":262}," task[",[144,759,760],{"class":158},"\"status\"",[144,762,763],{"class":262},"]\n",[144,765,767,770,772,775,778,780,783],{"class":146,"line":766},31,[144,768,769],{"class":262},"        progress ",[144,771,266],{"class":258},[144,773,774],{"class":262}," task.get(",[144,776,777],{"class":158},"\"progress\"",[144,779,400],{"class":262},[144,781,782],{"class":154},"0",[144,784,406],{"class":262},[144,786,788,791,794,796,799,801,804,806,809,811,814,816,819,821,824,826,829],{"class":146,"line":787},32,[144,789,790],{"class":154},"        print",[144,792,793],{"class":262},"(",[144,795,440],{"class":258},[144,797,798],{"class":158},"\"  [",[144,800,684],{"class":154},[144,802,803],{"class":262},"elapsed",[144,805,690],{"class":154},[144,807,808],{"class":158},"s] Status: ",[144,810,684],{"class":154},[144,812,813],{"class":262},"status",[144,815,690],{"class":154},[144,817,818],{"class":158}," | Progress: ",[144,820,684],{"class":154},[144,822,823],{"class":262},"progress",[144,825,690],{"class":154},[144,827,828],{"class":158},"%\"",[144,830,406],{"class":262},[144,832,834],{"class":146,"line":833},33,[144,835,376],{"emptyLinePlaceholder":57},[144,837,839],{"class":146,"line":838},34,[144,840,841],{"class":173},"        # 检查终态\n",[144,843,845,848,851,854,857],{"class":146,"line":844},35,[144,846,847],{"class":258},"        if",[144,849,850],{"class":262}," status ",[144,852,853],{"class":258},"==",[144,855,856],{"class":158}," \"completed\"",[144,858,859],{"class":262},":\n",[144,861,863,866],{"class":146,"line":862},36,[144,864,865],{"class":258},"            return",[144,867,868],{"class":262}," task\n",[144,870,872,875,877,879,882],{"class":146,"line":871},37,[144,873,874],{"class":258},"        elif",[144,876,850],{"class":262},[144,878,853],{"class":258},[144,880,881],{"class":158}," \"failed\"",[144,883,859],{"class":262},[144,885,887,890,892,894,897],{"class":146,"line":886},38,[144,888,889],{"class":262},"            error_info ",[144,891,266],{"class":258},[144,893,774],{"class":262},[144,895,896],{"class":158},"\"error\"",[144,898,899],{"class":262},", {})\n",[144,901,903,906,909],{"class":146,"line":902},39,[144,904,905],{"class":258},"            raise",[144,907,908],{"class":154}," RuntimeError",[144,910,911],{"class":262},"(\n",[144,913,915,918,921,923,925,927,930,932,935,938,940,943,946,948],{"class":146,"line":914},40,[144,916,917],{"class":258},"                f",[144,919,920],{"class":158},"\"Task ",[144,922,684],{"class":154},[144,924,687],{"class":262},[144,926,690],{"class":154},[144,928,929],{"class":158}," failed: ",[144,931,684],{"class":154},[144,933,934],{"class":262},"error_info.get(",[144,936,937],{"class":158},"'message'",[144,939,400],{"class":262},[144,941,942],{"class":158},"'Unknown error'",[144,944,945],{"class":262},")",[144,947,690],{"class":154},[144,949,950],{"class":158},"\"\n",[144,952,954],{"class":146,"line":953},41,[144,955,956],{"class":262},"            )\n",[144,958,960],{"class":146,"line":959},42,[144,961,376],{"emptyLinePlaceholder":57},[144,963,965],{"class":146,"line":964},43,[144,966,967],{"class":173},"        # 等待后再次轮询\n",[144,969,971],{"class":146,"line":970},44,[144,972,973],{"class":262},"        time.sleep(poll_interval)\n",[144,975,977,980,983],{"class":146,"line":976},45,[144,978,979],{"class":262},"        elapsed ",[144,981,982],{"class":258},"+=",[144,984,985],{"class":262}," poll_interval\n",[144,987,989],{"class":146,"line":988},46,[144,990,376],{"emptyLinePlaceholder":57},[144,992,994,997,1000,1002,1004,1006,1008,1010,1012,1015,1017,1020,1022,1025],{"class":146,"line":993},47,[144,995,996],{"class":258},"    raise",[144,998,999],{"class":154}," TimeoutError",[144,1001,793],{"class":262},[144,1003,440],{"class":258},[144,1005,920],{"class":158},[144,1007,684],{"class":154},[144,1009,687],{"class":262},[144,1011,690],{"class":154},[144,1013,1014],{"class":158}," timed out after ",[144,1016,684],{"class":154},[144,1018,1019],{"class":262},"timeout",[144,1021,690],{"class":154},[144,1023,1024],{"class":158},"s\"",[144,1026,406],{"class":262},[10,1028,1029],{},"这个函数的几个关键设计决策：",[95,1031,1032,1040,1048,1056],{},[72,1033,1034,1039],{},[22,1035,1036],{},[27,1037,1038],{},"poll_interval=10"," — 10 秒是最佳间隔。太快浪费 API 配额，太慢又影响效率。",[72,1041,1042,1047],{},[22,1043,1044],{},[27,1045,1046],{},"timeout=600"," — 10 分钟足够宽裕。大多数视频在 30–120 秒内完成，但这能覆盖队列拥堵等极端情况。",[72,1049,1050,1055],{},[22,1051,1052],{},[27,1053,1054],{},"response.raise_for_status()"," — 将 HTTP 错误（4xx/5xx）转换为 Python 异常，避免静默失败。",[72,1057,1058,1061,1062,1065],{},[22,1059,1060],{},"进度打印"," — ",[27,1063,1064],{},"[elapsed]s"," 前缀帮你关联时间。调试慢生成时很有用。",[135,1067,1069],{"className":338,"code":1068,"language":340,"meta":140,"style":140},"# ── 辅助函数：下载视频 ───────────────────────────────────────\ndef download_video(url, filename=\"output.mp4\"):\n    \"\"\"从 URL 下载视频文件。\"\"\"\n    print(f\"Downloading video to {filename}...\")\n    resp = requests.get(url, stream=True)\n    resp.raise_for_status()\n    with open(filename, \"wb\") as f:\n        for chunk in resp.iter_content(chunk_size=8192):\n            f.write(chunk)\n    print(f\"Saved: {filename} ({os.path.getsize(filename) / 1024:.0f} KB)\")\n",[27,1070,1071,1076,1093,1098,1122,1142,1147,1170,1194,1199],{"__ignoreMap":140},[144,1072,1073],{"class":146,"line":147},[144,1074,1075],{"class":173},"# ── 辅助函数：下载视频 ───────────────────────────────────────\n",[144,1077,1078,1080,1083,1086,1088,1091],{"class":146,"line":165},[144,1079,525],{"class":258},[144,1081,1082],{"class":150}," download_video",[144,1084,1085],{"class":262},"(url, filename",[144,1087,266],{"class":258},[144,1089,1090],{"class":158},"\"output.mp4\"",[144,1092,547],{"class":262},[144,1094,1095],{"class":146,"line":177},[144,1096,1097],{"class":158},"    \"\"\"从 URL 下载视频文件。\"\"\"\n",[144,1099,1100,1103,1105,1107,1110,1112,1115,1117,1120],{"class":146,"line":186},[144,1101,1102],{"class":154},"    print",[144,1104,793],{"class":262},[144,1106,440],{"class":258},[144,1108,1109],{"class":158},"\"Downloading video to ",[144,1111,684],{"class":154},[144,1113,1114],{"class":262},"filename",[144,1116,690],{"class":154},[144,1118,1119],{"class":158},"...\"",[144,1121,406],{"class":262},[144,1123,1124,1127,1129,1132,1135,1137,1140],{"class":146,"line":373},[144,1125,1126],{"class":262},"    resp ",[144,1128,266],{"class":258},[144,1130,1131],{"class":262}," requests.get(url, ",[144,1133,1134],{"class":700},"stream",[144,1136,266],{"class":258},[144,1138,1139],{"class":154},"True",[144,1141,406],{"class":262},[144,1143,1144],{"class":146,"line":379},[144,1145,1146],{"class":262},"    resp.raise_for_status()\n",[144,1148,1149,1152,1155,1158,1161,1164,1167],{"class":146,"line":385},[144,1150,1151],{"class":258},"    with",[144,1153,1154],{"class":154}," open",[144,1156,1157],{"class":262},"(filename, ",[144,1159,1160],{"class":158},"\"wb\"",[144,1162,1163],{"class":262},") ",[144,1165,1166],{"class":258},"as",[144,1168,1169],{"class":262}," f:\n",[144,1171,1172,1175,1178,1181,1184,1187,1189,1192],{"class":146,"line":409},[144,1173,1174],{"class":258},"        for",[144,1176,1177],{"class":262}," chunk ",[144,1179,1180],{"class":258},"in",[144,1182,1183],{"class":262}," resp.iter_content(",[144,1185,1186],{"class":700},"chunk_size",[144,1188,266],{"class":258},[144,1190,1191],{"class":154},"8192",[144,1193,547],{"class":262},[144,1195,1196],{"class":146,"line":420},[144,1197,1198],{"class":262},"            f.write(chunk)\n",[144,1200,1201,1203,1205,1207,1210,1212,1214,1216,1219,1221,1224,1227,1230,1233,1235,1238],{"class":146,"line":431},[144,1202,1102],{"class":154},[144,1204,793],{"class":262},[144,1206,440],{"class":258},[144,1208,1209],{"class":158},"\"Saved: ",[144,1211,684],{"class":154},[144,1213,1114],{"class":262},[144,1215,690],{"class":154},[144,1217,1218],{"class":158}," (",[144,1220,684],{"class":154},[144,1222,1223],{"class":262},"os.path.getsize(filename) ",[144,1225,1226],{"class":258},"/",[144,1228,1229],{"class":154}," 1024",[144,1231,1232],{"class":258},":.0f",[144,1234,690],{"class":154},[144,1236,1237],{"class":158}," KB)\"",[144,1239,406],{"class":262},[10,1241,1242,1243,1246,1247,1249],{},"这个函数以 8 KB 为单位流式下载，而不是把整个视频加载到内存。这很重要——生成的视频可能有 10–50 MB。",[27,1244,1245],{},"stream=True"," 参数告诉 ",[27,1248,111],{}," 增量下载。",[10,1251,1252],{},"以上三部分——配置、轮询、下载——就是基础框架。后面的每个代码示例都会用到它们，不再重复，只展示新的 payload。",[10,1254,1255,1256,297],{},"完整 API 参考请查看",[36,1257,1260],{"href":1258,"rel":1259},"https://seedance2api.app/docs/video-generation",[40],"视频生成文档",[44,1262],{},[47,1264,1266],{"id":1265},"生成你的第一个视频文生视频","生成你的第一个视频（文生视频）",[10,1268,1269],{},"开始生成视频吧。把以下代码添加到你的脚本中：",[135,1271,1273],{"className":338,"code":1272,"language":340,"meta":140,"style":140},"# ── 文生视频 ─────────────────────────────────────────────────\ndef text_to_video():\n    payload = {\n        \"model\": \"seedance-2.0\",          # 使用的 AI 模型\n        \"prompt\": (\n            \"A golden retriever puppy chases a butterfly through \"\n            \"a sunlit meadow. The camera follows the puppy with a \"\n            \"smooth tracking shot as wildflowers sway in the breeze.\"\n        ),\n        \"duration\": 5,                     # 视频时长：4-15 秒\n        \"quality\": \"720p\",                 # 分辨率：480p、720p、1080p\n        \"aspect_ratio\": \"16:9\",            # 标准宽屏\n        \"generate_audio\": True             # AI 生成匹配的音频\n    }\n\n    print(\"Submitting text-to-video request...\")\n    response = requests.post(\n        f\"{BASE_URL}/videos/generations\",  # 视频生成接口\n        headers=HEADERS,                   # 认证 + content-type 头\n        json=payload                       # 自动序列化为 JSON\n    )\n    response.raise_for_status()            # 非 200 则抛异常\n    task = response.json()                 # 解析 JSON 响应\n\n    # 打印响应中的关键信息\n    print(f\"Task created: {task['id']}\")\n    print(f\"Estimated time: {task['task_info']['estimated_time']}s\")\n    print(f\"Credits reserved: {task['usage']['credits_reserved']}\")\n\n    # 轮询直到视频就绪\n    result = wait_for_video(task[\"id\"])\n\n    # results 数组包含一个或多个视频 URL\n    video_url = result[\"results\"][0]\n    print(f\"\\nVideo URL: {video_url}\")\n    download_video(video_url, \"my_first_video.mp4\")\n\n    return result\n\n\nif __name__ == \"__main__\":\n    text_to_video()\n",[27,1274,1275,1280,1290,1299,1315,1323,1328,1333,1338,1343,1359,1375,1391,1403,1408,1412,1423,1433,1451,1466,1479,1484,1492,1505,1509,1514,1542,1574,1605,1609,1614,1630,1634,1639,1658,1685,1695,1699,1707,1711,1715,1731],{"__ignoreMap":140},[144,1276,1277],{"class":146,"line":147},[144,1278,1279],{"class":173},"# ── 文生视频 ─────────────────────────────────────────────────\n",[144,1281,1282,1284,1287],{"class":146,"line":165},[144,1283,525],{"class":258},[144,1285,1286],{"class":150}," text_to_video",[144,1288,1289],{"class":262},"():\n",[144,1291,1292,1295,1297],{"class":146,"line":177},[144,1293,1294],{"class":262},"    payload ",[144,1296,266],{"class":258},[144,1298,428],{"class":262},[144,1300,1301,1304,1306,1309,1312],{"class":146,"line":186},[144,1302,1303],{"class":158},"        \"model\"",[144,1305,437],{"class":262},[144,1307,1308],{"class":158},"\"seedance-2.0\"",[144,1310,1311],{"class":262},",          ",[144,1313,1314],{"class":173},"# 使用的 AI 模型\n",[144,1316,1317,1320],{"class":146,"line":373},[144,1318,1319],{"class":158},"        \"prompt\"",[144,1321,1322],{"class":262},": (\n",[144,1324,1325],{"class":146,"line":379},[144,1326,1327],{"class":158},"            \"A golden retriever puppy chases a butterfly through \"\n",[144,1329,1330],{"class":146,"line":385},[144,1331,1332],{"class":158},"            \"a sunlit meadow. The camera follows the puppy with a \"\n",[144,1334,1335],{"class":146,"line":409},[144,1336,1337],{"class":158},"            \"smooth tracking shot as wildflowers sway in the breeze.\"\n",[144,1339,1340],{"class":146,"line":420},[144,1341,1342],{"class":262},"        ),\n",[144,1344,1345,1348,1350,1353,1356],{"class":146,"line":431},[144,1346,1347],{"class":158},"        \"duration\"",[144,1349,437],{"class":262},[144,1351,1352],{"class":154},"5",[144,1354,1355],{"class":262},",                     ",[144,1357,1358],{"class":173},"# 视频时长：4-15 秒\n",[144,1360,1361,1364,1366,1369,1372],{"class":146,"line":455},[144,1362,1363],{"class":158},"        \"quality\"",[144,1365,437],{"class":262},[144,1367,1368],{"class":158},"\"720p\"",[144,1370,1371],{"class":262},",                 ",[144,1373,1374],{"class":173},"# 分辨率：480p、720p、1080p\n",[144,1376,1377,1380,1382,1385,1388],{"class":146,"line":466},[144,1378,1379],{"class":158},"        \"aspect_ratio\"",[144,1381,437],{"class":262},[144,1383,1384],{"class":158},"\"16:9\"",[144,1386,1387],{"class":262},",            ",[144,1389,1390],{"class":173},"# 标准宽屏\n",[144,1392,1393,1396,1398,1400],{"class":146,"line":599},[144,1394,1395],{"class":158},"        \"generate_audio\"",[144,1397,437],{"class":262},[144,1399,1139],{"class":154},[144,1401,1402],{"class":173},"             # AI 生成匹配的音频\n",[144,1404,1405],{"class":146,"line":604},[144,1406,1407],{"class":262},"    }\n",[144,1409,1410],{"class":146,"line":610},[144,1411,376],{"emptyLinePlaceholder":57},[144,1413,1414,1416,1418,1421],{"class":146,"line":616},[144,1415,1102],{"class":154},[144,1417,793],{"class":262},[144,1419,1420],{"class":158},"\"Submitting text-to-video request...\"",[144,1422,406],{"class":262},[144,1424,1425,1428,1430],{"class":146,"line":622},[144,1426,1427],{"class":262},"    response ",[144,1429,266],{"class":258},[144,1431,1432],{"class":262}," requests.post(\n",[144,1434,1435,1438,1440,1442,1445,1448],{"class":146,"line":627},[144,1436,1437],{"class":258},"        f",[144,1439,449],{"class":158},[144,1441,678],{"class":154},[144,1443,1444],{"class":158},"/videos/generations\"",[144,1446,1447],{"class":262},",  ",[144,1449,1450],{"class":173},"# 视频生成接口\n",[144,1452,1453,1456,1458,1460,1463],{"class":146,"line":638},[144,1454,1455],{"class":700},"        headers",[144,1457,266],{"class":258},[144,1459,423],{"class":154},[144,1461,1462],{"class":262},",                   ",[144,1464,1465],{"class":173},"# 认证 + content-type 头\n",[144,1467,1468,1471,1473,1476],{"class":146,"line":653},[144,1469,1470],{"class":700},"        json",[144,1472,266],{"class":258},[144,1474,1475],{"class":262},"payload                       ",[144,1477,1478],{"class":173},"# 自动序列化为 JSON\n",[144,1480,1481],{"class":146,"line":659},[144,1482,1483],{"class":262},"    )\n",[144,1485,1486,1489],{"class":146,"line":670},[144,1487,1488],{"class":262},"    response.raise_for_status()            ",[144,1490,1491],{"class":173},"# 非 200 则抛异常\n",[144,1493,1494,1497,1499,1502],{"class":146,"line":697},[144,1495,1496],{"class":262},"    task ",[144,1498,266],{"class":258},[144,1500,1501],{"class":262}," response.json()                 ",[144,1503,1504],{"class":173},"# 解析 JSON 响应\n",[144,1506,1507],{"class":146,"line":709},[144,1508,376],{"emptyLinePlaceholder":57},[144,1510,1511],{"class":146,"line":715},[144,1512,1513],{"class":173},"    # 打印响应中的关键信息\n",[144,1515,1516,1518,1520,1522,1525,1527,1530,1533,1536,1538,1540],{"class":146,"line":721},[144,1517,1102],{"class":154},[144,1519,793],{"class":262},[144,1521,440],{"class":258},[144,1523,1524],{"class":158},"\"Task created: ",[144,1526,684],{"class":154},[144,1528,1529],{"class":262},"task[",[144,1531,1532],{"class":158},"'id'",[144,1534,1535],{"class":262},"]",[144,1537,690],{"class":154},[144,1539,449],{"class":158},[144,1541,406],{"class":262},[144,1543,1544,1546,1548,1550,1553,1555,1557,1560,1563,1566,1568,1570,1572],{"class":146,"line":727},[144,1545,1102],{"class":154},[144,1547,793],{"class":262},[144,1549,440],{"class":258},[144,1551,1552],{"class":158},"\"Estimated time: ",[144,1554,684],{"class":154},[144,1556,1529],{"class":262},[144,1558,1559],{"class":158},"'task_info'",[144,1561,1562],{"class":262},"][",[144,1564,1565],{"class":158},"'estimated_time'",[144,1567,1535],{"class":262},[144,1569,690],{"class":154},[144,1571,1024],{"class":158},[144,1573,406],{"class":262},[144,1575,1576,1578,1580,1582,1585,1587,1589,1592,1594,1597,1599,1601,1603],{"class":146,"line":738},[144,1577,1102],{"class":154},[144,1579,793],{"class":262},[144,1581,440],{"class":258},[144,1583,1584],{"class":158},"\"Credits reserved: ",[144,1586,684],{"class":154},[144,1588,1529],{"class":262},[144,1590,1591],{"class":158},"'usage'",[144,1593,1562],{"class":262},[144,1595,1596],{"class":158},"'credits_reserved'",[144,1598,1535],{"class":262},[144,1600,690],{"class":154},[144,1602,449],{"class":158},[144,1604,406],{"class":262},[144,1606,1607],{"class":146,"line":743},[144,1608,376],{"emptyLinePlaceholder":57},[144,1610,1611],{"class":146,"line":749},[144,1612,1613],{"class":173},"    # 轮询直到视频就绪\n",[144,1615,1616,1619,1621,1624,1627],{"class":146,"line":766},[144,1617,1618],{"class":262},"    result ",[144,1620,266],{"class":258},[144,1622,1623],{"class":262}," wait_for_video(task[",[144,1625,1626],{"class":158},"\"id\"",[144,1628,1629],{"class":262},"])\n",[144,1631,1632],{"class":146,"line":787},[144,1633,376],{"emptyLinePlaceholder":57},[144,1635,1636],{"class":146,"line":833},[144,1637,1638],{"class":173},"    # results 数组包含一个或多个视频 URL\n",[144,1640,1641,1644,1646,1649,1652,1654,1656],{"class":146,"line":838},[144,1642,1643],{"class":262},"    video_url ",[144,1645,266],{"class":258},[144,1647,1648],{"class":262}," result[",[144,1650,1651],{"class":158},"\"results\"",[144,1653,1562],{"class":262},[144,1655,782],{"class":154},[144,1657,763],{"class":262},[144,1659,1660,1662,1664,1666,1668,1671,1674,1676,1679,1681,1683],{"class":146,"line":844},[144,1661,1102],{"class":154},[144,1663,793],{"class":262},[144,1665,440],{"class":258},[144,1667,449],{"class":158},[144,1669,1670],{"class":154},"\\n",[144,1672,1673],{"class":158},"Video URL: ",[144,1675,684],{"class":154},[144,1677,1678],{"class":262},"video_url",[144,1680,690],{"class":154},[144,1682,449],{"class":158},[144,1684,406],{"class":262},[144,1686,1687,1690,1693],{"class":146,"line":862},[144,1688,1689],{"class":262},"    download_video(video_url, ",[144,1691,1692],{"class":158},"\"my_first_video.mp4\"",[144,1694,406],{"class":262},[144,1696,1697],{"class":146,"line":871},[144,1698,376],{"emptyLinePlaceholder":57},[144,1700,1701,1704],{"class":146,"line":886},[144,1702,1703],{"class":258},"    return",[144,1705,1706],{"class":262}," result\n",[144,1708,1709],{"class":146,"line":902},[144,1710,376],{"emptyLinePlaceholder":57},[144,1712,1713],{"class":146,"line":914},[144,1714,376],{"emptyLinePlaceholder":57},[144,1716,1717,1720,1723,1726,1729],{"class":146,"line":953},[144,1718,1719],{"class":258},"if",[144,1721,1722],{"class":154}," __name__",[144,1724,1725],{"class":258}," ==",[144,1727,1728],{"class":158}," \"__main__\"",[144,1730,859],{"class":262},[144,1732,1733],{"class":146,"line":959},[144,1734,1735],{"class":262},"    text_to_video()\n",[10,1737,1738],{},"逐一解释 payload 中的参数：",[95,1740,1741,1755,1767,1775,1795,1815],{},[72,1742,1743,1748,1749,1752,1753,297],{},[22,1744,1745],{},[27,1746,1747],{},"model"," — 使用哪个 Seedance 模型。设为 ",[27,1750,1751],{},"seedance-2.0"," 使用最新版；如果 2.0 在你所在地区还不可用，先用 ",[27,1754,29],{},[72,1756,1757,1762,1763,297],{},[22,1758,1759],{},[27,1760,1761],{},"prompt"," — 视频描述。尽量具体地描述主体、动作、镜头运动和氛围。上面的提示词用了三段式结构：主体（\"golden retriever puppy\"）、动作（\"chases a butterfly\"）、镜头（\"smooth tracking shot\"）。更多提示词技巧请看",[36,1764,1766],{"href":1765},"/blog/seedance-2-prompt-guide","提示词工程指南",[72,1768,1769,1774],{},[22,1770,1771],{},[27,1772,1773],{},"duration"," — 视频时长，单位秒（4–15）。短视频生成更快、消耗更少额度。测试时建议用 5 秒。",[72,1776,1777,1782,1783,1786,1787,1790,1791,1794],{},[22,1778,1779],{},[27,1780,1781],{},"quality"," — 分辨率档位。",[27,1784,1785],{},"720p"," 是开发阶段质量和速度的最佳平衡。",[27,1788,1789],{},"480p"," 适合快速迭代，",[27,1792,1793],{},"1080p"," 用于最终渲染。",[72,1796,1797,1802,1803,1806,1807,1810,1811,1814],{},[22,1798,1799],{},[27,1800,1801],{},"aspect_ratio"," — 输出比例。",[27,1804,1805],{},"16:9"," 适合 YouTube/横屏，",[27,1808,1809],{},"9:16"," 适合抖音/Reels/Shorts，",[27,1812,1813],{},"1:1"," 适合 Instagram 信息流。",[72,1816,1817,1822,1823,1826],{},[22,1818,1819],{},[27,1820,1821],{},"generate_audio"," — 设为 ",[27,1824,1825],{},"true"," 时，Seedance 会生成与视觉内容匹配的环境音和音乐，大约增加 ~2 秒生成时间。",[10,1828,1829],{},"运行：",[135,1831,1833],{"className":137,"code":1832,"language":139,"meta":140,"style":140},"python seedance_tutorial.py\n",[27,1834,1835],{"__ignoreMap":140},[144,1836,1837,1839],{"class":146,"line":147},[144,1838,340],{"class":150},[144,1840,1841],{"class":158}," seedance_tutorial.py\n",[91,1843,1845],{"id":1844},"api-返回内容","API 返回内容",[10,1847,1848,1849,1852],{},"提交生成请求后，你会立即收到一个 ",[22,1850,1851],{},"task 对象","——视频还没准备好。以下是实际的响应：",[135,1854,1858],{"className":1855,"code":1856,"language":1857,"meta":140,"style":140},"language-json shiki shiki-themes github-dark","{\n  \"created\": 1772203771,\n  \"id\": \"task-unified-1772203771-yf1dxogh\",\n  \"model\": \"seedance-2.0\",\n  \"object\": \"video.generation.task\",\n  \"progress\": 0,\n  \"status\": \"pending\",\n  \"task_info\": {\n    \"can_cancel\": true,\n    \"estimated_time\": 132\n  },\n  \"type\": \"video\",\n  \"usage\": {\n    \"billing_rule\": \"per_second\",\n    \"credits_reserved\": 17.784,\n    \"user_group\": \"default\"\n  }\n}\n","json",[27,1859,1860,1865,1877,1889,1900,1912,1923,1935,1943,1954,1964,1969,1981,1988,2000,2012,2022,2027],{"__ignoreMap":140},[144,1861,1862],{"class":146,"line":147},[144,1863,1864],{"class":262},"{\n",[144,1866,1867,1870,1872,1875],{"class":146,"line":165},[144,1868,1869],{"class":154},"  \"created\"",[144,1871,437],{"class":262},[144,1873,1874],{"class":154},"1772203771",[144,1876,452],{"class":262},[144,1878,1879,1882,1884,1887],{"class":146,"line":177},[144,1880,1881],{"class":154},"  \"id\"",[144,1883,437],{"class":262},[144,1885,1886],{"class":158},"\"task-unified-1772203771-yf1dxogh\"",[144,1888,452],{"class":262},[144,1890,1891,1894,1896,1898],{"class":146,"line":186},[144,1892,1893],{"class":154},"  \"model\"",[144,1895,437],{"class":262},[144,1897,1308],{"class":158},[144,1899,452],{"class":262},[144,1901,1902,1905,1907,1910],{"class":146,"line":373},[144,1903,1904],{"class":154},"  \"object\"",[144,1906,437],{"class":262},[144,1908,1909],{"class":158},"\"video.generation.task\"",[144,1911,452],{"class":262},[144,1913,1914,1917,1919,1921],{"class":146,"line":379},[144,1915,1916],{"class":154},"  \"progress\"",[144,1918,437],{"class":262},[144,1920,782],{"class":154},[144,1922,452],{"class":262},[144,1924,1925,1928,1930,1933],{"class":146,"line":385},[144,1926,1927],{"class":154},"  \"status\"",[144,1929,437],{"class":262},[144,1931,1932],{"class":158},"\"pending\"",[144,1934,452],{"class":262},[144,1936,1937,1940],{"class":146,"line":409},[144,1938,1939],{"class":154},"  \"task_info\"",[144,1941,1942],{"class":262},": {\n",[144,1944,1945,1948,1950,1952],{"class":146,"line":420},[144,1946,1947],{"class":154},"    \"can_cancel\"",[144,1949,437],{"class":262},[144,1951,1825],{"class":154},[144,1953,452],{"class":262},[144,1955,1956,1959,1961],{"class":146,"line":431},[144,1957,1958],{"class":154},"    \"estimated_time\"",[144,1960,437],{"class":262},[144,1962,1963],{"class":154},"132\n",[144,1965,1966],{"class":146,"line":455},[144,1967,1968],{"class":262},"  },\n",[144,1970,1971,1974,1976,1979],{"class":146,"line":466},[144,1972,1973],{"class":154},"  \"type\"",[144,1975,437],{"class":262},[144,1977,1978],{"class":158},"\"video\"",[144,1980,452],{"class":262},[144,1982,1983,1986],{"class":146,"line":599},[144,1984,1985],{"class":154},"  \"usage\"",[144,1987,1942],{"class":262},[144,1989,1990,1993,1995,1998],{"class":146,"line":604},[144,1991,1992],{"class":154},"    \"billing_rule\"",[144,1994,437],{"class":262},[144,1996,1997],{"class":158},"\"per_second\"",[144,1999,452],{"class":262},[144,2001,2002,2005,2007,2010],{"class":146,"line":610},[144,2003,2004],{"class":154},"    \"credits_reserved\"",[144,2006,437],{"class":262},[144,2008,2009],{"class":154},"17.784",[144,2011,452],{"class":262},[144,2013,2014,2017,2019],{"class":146,"line":616},[144,2015,2016],{"class":154},"    \"user_group\"",[144,2018,437],{"class":262},[144,2020,2021],{"class":158},"\"default\"\n",[144,2023,2024],{"class":146,"line":622},[144,2025,2026],{"class":262},"  }\n",[144,2028,2029],{"class":146,"line":627},[144,2030,469],{"class":262},[10,2032,2033],{},"关键字段说明：",[2035,2036,2037,2050],"table",{},[2038,2039,2040],"thead",{},[2041,2042,2043,2047],"tr",{},[2044,2045,2046],"th",{},"字段",[2044,2048,2049],{},"含义",[2051,2052,2053,2064,2087,2099,2109,2119,2131,2141],"tbody",{},[2041,2054,2055,2061],{},[2056,2057,2058],"td",{},[27,2059,2060],{},"id",[2056,2062,2063],{},"任务 ID——用它来查询状态和获取结果",[2041,2065,2066,2070],{},[2056,2067,2068],{},[27,2069,813],{},[2056,2071,2072,2073,2076,2077,2080,2081,280,2084],{},"初始为 ",[27,2074,2075],{},"pending","，然后变为 ",[27,2078,2079],{},"processing","，最终变为 ",[27,2082,2083],{},"completed",[27,2085,2086],{},"failed",[2041,2088,2089,2093],{},[2056,2090,2091],{},[27,2092,823],{},[2056,2094,2095,2096,2098],{},"0–100 的百分比。在 ",[27,2097,2079],{}," 阶段实时更新",[2041,2100,2101,2106],{},[2056,2102,2103],{},[27,2104,2105],{},"estimated_time",[2056,2107,2108],{},"预计完成时间（秒），服务端估算",[2041,2110,2111,2116],{},[2056,2112,2113],{},[27,2114,2115],{},"credits_reserved",[2056,2117,2118],{},"本次任务预扣的额度。任务失败时自动退还",[2041,2120,2121,2126],{},[2056,2122,2123],{},[27,2124,2125],{},"task_info.can_cancel",[2056,2127,2128,2129,116],{},"是否可以取消该任务（完成前始终为 ",[27,2130,1825],{},[2041,2132,2133,2138],{},[2056,2134,2135],{},[27,2136,2137],{},"created",[2056,2139,2140],{},"任务提交时间的 Unix 时间戳",[2041,2142,2143,2148],{},[2056,2144,2145],{},[27,2146,2147],{},"usage.billing_rule",[2056,2149,2150,2151,2154],{},"计费方式——",[27,2152,2153],{},"per_second"," 表示按时长计费",[17,2156,2157],{},[10,2158,2159,2161,2162,2164,2165,2168],{},[22,2160,132],{}," 提交后立即把 ",[27,2163,2060],{}," 保存到文件或数据库。如果你的脚本在轮询过程中崩溃了，可以用保存的任务 ID 调用 ",[27,2166,2167],{},"wait_for_video()"," 恢复。任务在服务器上保留 24 小时。",[91,2170,2171],{"id":2171},"轮询过程",[10,2173,2174,2176],{},[27,2175,2167],{}," 函数每 10 秒轮询一次。以下是真实的输出：",[135,2178,2183],{"className":2179,"code":2181,"language":2182},[2180],"language-text","Submitting text-to-video request...\nTask created: task-unified-1772203771-yf1dxogh\nEstimated time: 132s\nCredits reserved: 17.784\n  [0s] Status: pending | Progress: 0%\n  [10s] Status: processing | Progress: 7%\n  [20s] Status: processing | Progress: 13%\n  [30s] Status: processing | Progress: 20%\n  [40s] Status: processing | Progress: 27%\n  [50s] Status: completed | Progress: 100%\n\nVideo URL: https://files.evolink.ai/.../cgt-20260227224931-8vl7s.mp4\nDownloading video to my_first_video.mp4...\nSaved: my_first_video.mp4 (2847 KB)\n","text",[27,2184,2181],{"__ignoreMap":140},[10,2186,2187],{},"就这么简单——从发起 API 调用到视频文件保存到本地，大约 50 秒。",[17,2189,2190],{},[10,2191,2192,2195],{},[22,2193,2194],{},"重要："," 视频 URL 在 24 小时后过期。务必及时下载文件或存储到你自己的对象存储（S3、GCS、Cloudflare R2 等）。",[17,2197,2198],{},[10,2199,2200,2202],{},[22,2201,304],{}," 不要依赖视频 URL 做长期存储。你的流水线应该在生成完成后立即下载。如果你在异步处理视频，用 Webhook（下面会讲）在生成完成的那一刻触发下载。",[10,2204,2205,2206,2209],{},"更多提示词编写技巧请看 ",[36,2207,2208],{"href":1765},"Seedance 2.0 提示词指南","——涵盖分镜脚本格式、风格关键词和时间轴语法。",[44,2211],{},[47,2213,2215],{"id":2214},"轮询结果理解异步工作流","轮询结果：理解异步工作流",[10,2217,2218,2219,2222],{},"视频生成需要 30–120+ 秒，取决于时长和质量设置。API 使用",[22,2220,2221],{},"异步任务模式","——和 OpenAI、Stability AI 以及大多数生成式 AI API 用的是同一套模式：",[69,2224,2225,2235,2245],{},[72,2226,2227,2230,2231,2234],{},[22,2228,2229],{},"提交"," → POST 到 ",[27,2232,2233],{},"/v1/videos/generations"," → 立即获得任务 ID",[72,2236,2237,2240,2241,2244],{},[22,2238,2239],{},"轮询"," → GET ",[27,2242,2243],{},"/v1/tasks/{task_id}"," → 定期检查状态",[72,2246,2247,2250,2251,2254,2255,2258],{},[22,2248,2249],{},"获取"," → 当 ",[27,2252,2253],{},"status: \"completed\""," 时，",[27,2256,2257],{},"results"," 数组中包含视频 URL",[10,2260,2261],{},"这种模式存在的原因是视频生成计算量巨大。如果用同步 HTTP 请求，视频还没生成完就会超时。",[91,2263,2264],{"id":2264},"任务状态生命周期",[135,2266,2269],{"className":2267,"code":2268,"language":2182},[2180],"pending → processing → completed\n                    ↘ failed\n",[27,2270,2268],{"__ignoreMap":140},[2035,2272,2273,2286],{},[2038,2274,2275],{},[2041,2276,2277,2280,2283],{},[2044,2278,2279],{},"状态",[2044,2281,2282],{},"发生了什么",[2044,2284,2285],{},"典型耗时",[2051,2287,2288,2300,2315,2330],{},[2041,2289,2290,2294,2297],{},[2056,2291,2292],{},[27,2293,2075],{},[2056,2295,2296],{},"任务排队中，等待 GPU 资源",[2056,2298,2299],{},"0–30 秒",[2041,2301,2302,2306,2312],{},[2056,2303,2304],{},[27,2305,2079],{},[2056,2307,2308,2309,2311],{},"视频正在生成——",[27,2310,823],{}," 实时更新",[2056,2313,2314],{},"30–120 秒",[2041,2316,2317,2321,2327],{},[2056,2318,2319],{},[27,2320,2083],{},[2056,2322,2323,2324,2326],{},"完成！",[27,2325,2257],{}," 数组中有视频 URL",[2056,2328,2329],{},"终态",[2041,2331,2332,2336,2339],{},[2056,2333,2334],{},[27,2335,2086],{},[2056,2337,2338],{},"出错了——查看错误详情",[2056,2340,2329],{},[91,2342,2343],{"id":2343},"轮询最佳实践",[10,2345,2346,2349],{},[22,2347,2348],{},"轮询间隔："," 10 秒是合理的默认值。太快浪费请求次数，还可能触发限流；太慢则拖延你的流水线。对于时间敏感的应用，可以 5 秒一次，但没必要更快了。",[10,2351,2352,2355],{},[22,2353,2354],{},"超时时间："," 根据你的参数设置合理的上限：",[2035,2357,2358,2371],{},[2038,2359,2360],{},[2041,2361,2362,2365,2368],{},[2044,2363,2364],{},"配置",[2044,2366,2367],{},"预期耗时",[2044,2369,2370],{},"建议超时",[2051,2372,2373,2384,2395,2406],{},[2041,2374,2375,2378,2381],{},[2056,2376,2377],{},"4s, 480p",[2056,2379,2380],{},"20–40 秒",[2056,2382,2383],{},"120 秒",[2041,2385,2386,2389,2392],{},[2056,2387,2388],{},"5s, 720p",[2056,2390,2391],{},"30–60 秒",[2056,2393,2394],{},"180 秒",[2041,2396,2397,2400,2403],{},[2056,2398,2399],{},"10s, 720p",[2056,2401,2402],{},"60–90 秒",[2056,2404,2405],{},"300 秒",[2041,2407,2408,2411,2414],{},[2056,2409,2410],{},"15s, 1080p",[2056,2412,2413],{},"90–180 秒",[2056,2415,2416],{},"600 秒",[10,2418,2419,2422,2423,2425,2426,2428],{},[22,2420,2421],{},"进度跟踪："," ",[27,2424,823],{}," 字段（0–100）提供细粒度反馈——适合在 UI 中构建进度条。在 ",[27,2427,2079],{}," 阶段大约每 5–7 秒更新一次。",[91,2430,2431],{"id":2431},"取消任务",[10,2433,2434],{},"如果你需要停止正在进行的生成（提示词写错了、改主意了），可以取消它：",[135,2436,2438],{"className":338,"code":2437,"language":340,"meta":140,"style":140},"def cancel_task(task_id):\n    \"\"\"取消 pending 或 processing 状态的任务。额度会退还。\"\"\"\n    response = requests.post(\n        f\"{BASE_URL}/tasks/{task_id}/cancel\",\n        headers=HEADERS\n    )\n    if response.status_code == 200:\n        print(f\"Task {task_id} cancelled. Credits refunded.\")\n    else:\n        print(f\"Cancel failed: {response.json()}\")\n",[27,2439,2440,2450,2455,2463,2484,2492,2496,2511,2532,2539],{"__ignoreMap":140},[144,2441,2442,2444,2447],{"class":146,"line":147},[144,2443,525],{"class":258},[144,2445,2446],{"class":150}," cancel_task",[144,2448,2449],{"class":262},"(task_id):\n",[144,2451,2452],{"class":146,"line":165},[144,2453,2454],{"class":158},"    \"\"\"取消 pending 或 processing 状态的任务。额度会退还。\"\"\"\n",[144,2456,2457,2459,2461],{"class":146,"line":177},[144,2458,1427],{"class":262},[144,2460,266],{"class":258},[144,2462,1432],{"class":262},[144,2464,2465,2467,2469,2471,2473,2475,2477,2479,2482],{"class":146,"line":186},[144,2466,1437],{"class":258},[144,2468,449],{"class":158},[144,2470,678],{"class":154},[144,2472,681],{"class":158},[144,2474,684],{"class":154},[144,2476,687],{"class":262},[144,2478,690],{"class":154},[144,2480,2481],{"class":158},"/cancel\"",[144,2483,452],{"class":262},[144,2485,2486,2488,2490],{"class":146,"line":373},[144,2487,1455],{"class":700},[144,2489,266],{"class":258},[144,2491,706],{"class":154},[144,2493,2494],{"class":146,"line":379},[144,2495,1483],{"class":262},[144,2497,2498,2501,2504,2506,2509],{"class":146,"line":385},[144,2499,2500],{"class":258},"    if",[144,2502,2503],{"class":262}," response.status_code ",[144,2505,853],{"class":258},[144,2507,2508],{"class":154}," 200",[144,2510,859],{"class":262},[144,2512,2513,2515,2517,2519,2521,2523,2525,2527,2530],{"class":146,"line":409},[144,2514,790],{"class":154},[144,2516,793],{"class":262},[144,2518,440],{"class":258},[144,2520,920],{"class":158},[144,2522,684],{"class":154},[144,2524,687],{"class":262},[144,2526,690],{"class":154},[144,2528,2529],{"class":158}," cancelled. Credits refunded.\"",[144,2531,406],{"class":262},[144,2533,2534,2537],{"class":146,"line":420},[144,2535,2536],{"class":258},"    else",[144,2538,859],{"class":262},[144,2540,2541,2543,2545,2547,2550,2552,2555,2557,2559],{"class":146,"line":431},[144,2542,790],{"class":154},[144,2544,793],{"class":262},[144,2546,440],{"class":258},[144,2548,2549],{"class":158},"\"Cancel failed: ",[144,2551,684],{"class":154},[144,2553,2554],{"class":262},"response.json()",[144,2556,690],{"class":154},[144,2558,449],{"class":158},[144,2560,406],{"class":262},[10,2562,2563,2564,2566,2567,2569,2570,280,2572,2574],{},"取消在 ",[27,2565,2125],{}," 为 ",[27,2568,1825],{}," 时有效。任务达到 ",[27,2571,2083],{},[27,2573,2086],{}," 后无法取消。取消时预扣的额度会自动退还。",[17,2576,2577],{},[10,2578,2579,2581],{},[22,2580,132],{}," 尽早在你的 UI 中加入取消功能。用户难免会提交错误的提示词，等 2 分钟生成一个不想要的视频既浪费时间又浪费额度。",[10,2583,2584,2585,2587,2588,297],{},"前面搭建的 ",[27,2586,2167],{}," 函数已经处理了标准轮询流程。如果你想完全跳过轮询，请直接跳到下面的 ",[36,2589,2591],{"href":2590},"#%E9%85%8D%E7%BD%AE-webhook%E8%B7%B3%E8%BF%87%E8%BD%AE%E8%AF%A2","Webhook 章节",[44,2593],{},[47,2595,2597],{"id":2596},"让图片动起来图生视频","让图片动起来（图生视频）",[10,2599,2600,2601,2604,2605,2609],{},"有产品图、角色插画或风景照想让它动起来？传入 ",[27,2602,2603],{},"image_url","，Seedance 就会把它动画化。这是",[36,2606,2608],{"href":2607},"/blog/seedance-2-ecommerce-product-videos","电商产品视频","最强大的功能之一——把一张静态产品图变成吸引眼球的视频广告。",[10,2611,2612],{},[2613,2614,2615],"em",{},"使用前面第一个示例中相同的配置和轮询函数。",[135,2617,2619],{"className":338,"code":2618,"language":340,"meta":140,"style":140},"# ── 图生视频 ─────────────────────────────────────────────────\ndef image_to_video():\n    payload = {\n        \"model\": \"seedance-2.0\",\n        \"prompt\": (\n            \"@Image1 as the first frame. The scene slowly comes \"\n            \"to life — leaves rustle gently, soft light shifts \"\n            \"across the frame, and the subject blinks naturally.\"\n        ),\n        \"image_urls\": [\n            \"https://example.com/your-image.jpg\"\n        ],\n        \"duration\": 5,\n        \"quality\": \"720p\",\n        \"aspect_ratio\": \"16:9\"\n    }\n\n    print(\"Submitting image-to-video request...\")\n    response = requests.post(\n        f\"{BASE_URL}/videos/generations\",\n        headers=HEADERS,\n        json=payload\n    )\n    response.raise_for_status()\n    task = response.json()\n\n    print(f\"Task created: {task['id']}\")\n    result = wait_for_video(task[\"id\"])\n\n    video_url = result[\"results\"][0]\n    download_video(video_url, \"animated_image.mp4\")\n\n    return result\n",[27,2620,2621,2626,2635,2643,2653,2659,2664,2669,2674,2678,2686,2691,2696,2706,2716,2725,2729,2733,2744,2752,2764,2774,2783,2787,2792,2800,2804,2828,2840,2844,2860,2869,2873],{"__ignoreMap":140},[144,2622,2623],{"class":146,"line":147},[144,2624,2625],{"class":173},"# ── 图生视频 ─────────────────────────────────────────────────\n",[144,2627,2628,2630,2633],{"class":146,"line":165},[144,2629,525],{"class":258},[144,2631,2632],{"class":150}," image_to_video",[144,2634,1289],{"class":262},[144,2636,2637,2639,2641],{"class":146,"line":177},[144,2638,1294],{"class":262},[144,2640,266],{"class":258},[144,2642,428],{"class":262},[144,2644,2645,2647,2649,2651],{"class":146,"line":186},[144,2646,1303],{"class":158},[144,2648,437],{"class":262},[144,2650,1308],{"class":158},[144,2652,452],{"class":262},[144,2654,2655,2657],{"class":146,"line":373},[144,2656,1319],{"class":158},[144,2658,1322],{"class":262},[144,2660,2661],{"class":146,"line":379},[144,2662,2663],{"class":158},"            \"@Image1 as the first frame. The scene slowly comes \"\n",[144,2665,2666],{"class":146,"line":385},[144,2667,2668],{"class":158},"            \"to life — leaves rustle gently, soft light shifts \"\n",[144,2670,2671],{"class":146,"line":409},[144,2672,2673],{"class":158},"            \"across the frame, and the subject blinks naturally.\"\n",[144,2675,2676],{"class":146,"line":420},[144,2677,1342],{"class":262},[144,2679,2680,2683],{"class":146,"line":431},[144,2681,2682],{"class":158},"        \"image_urls\"",[144,2684,2685],{"class":262},": [\n",[144,2687,2688],{"class":146,"line":455},[144,2689,2690],{"class":158},"            \"https://example.com/your-image.jpg\"\n",[144,2692,2693],{"class":146,"line":466},[144,2694,2695],{"class":262},"        ],\n",[144,2697,2698,2700,2702,2704],{"class":146,"line":599},[144,2699,1347],{"class":158},[144,2701,437],{"class":262},[144,2703,1352],{"class":154},[144,2705,452],{"class":262},[144,2707,2708,2710,2712,2714],{"class":146,"line":604},[144,2709,1363],{"class":158},[144,2711,437],{"class":262},[144,2713,1368],{"class":158},[144,2715,452],{"class":262},[144,2717,2718,2720,2722],{"class":146,"line":610},[144,2719,1379],{"class":158},[144,2721,437],{"class":262},[144,2723,2724],{"class":158},"\"16:9\"\n",[144,2726,2727],{"class":146,"line":616},[144,2728,1407],{"class":262},[144,2730,2731],{"class":146,"line":622},[144,2732,376],{"emptyLinePlaceholder":57},[144,2734,2735,2737,2739,2742],{"class":146,"line":627},[144,2736,1102],{"class":154},[144,2738,793],{"class":262},[144,2740,2741],{"class":158},"\"Submitting image-to-video request...\"",[144,2743,406],{"class":262},[144,2745,2746,2748,2750],{"class":146,"line":638},[144,2747,1427],{"class":262},[144,2749,266],{"class":258},[144,2751,1432],{"class":262},[144,2753,2754,2756,2758,2760,2762],{"class":146,"line":653},[144,2755,1437],{"class":258},[144,2757,449],{"class":158},[144,2759,678],{"class":154},[144,2761,1444],{"class":158},[144,2763,452],{"class":262},[144,2765,2766,2768,2770,2772],{"class":146,"line":659},[144,2767,1455],{"class":700},[144,2769,266],{"class":258},[144,2771,423],{"class":154},[144,2773,452],{"class":262},[144,2775,2776,2778,2780],{"class":146,"line":670},[144,2777,1470],{"class":700},[144,2779,266],{"class":258},[144,2781,2782],{"class":262},"payload\n",[144,2784,2785],{"class":146,"line":697},[144,2786,1483],{"class":262},[144,2788,2789],{"class":146,"line":709},[144,2790,2791],{"class":262},"    response.raise_for_status()\n",[144,2793,2794,2796,2798],{"class":146,"line":715},[144,2795,1496],{"class":262},[144,2797,266],{"class":258},[144,2799,735],{"class":262},[144,2801,2802],{"class":146,"line":721},[144,2803,376],{"emptyLinePlaceholder":57},[144,2805,2806,2808,2810,2812,2814,2816,2818,2820,2822,2824,2826],{"class":146,"line":727},[144,2807,1102],{"class":154},[144,2809,793],{"class":262},[144,2811,440],{"class":258},[144,2813,1524],{"class":158},[144,2815,684],{"class":154},[144,2817,1529],{"class":262},[144,2819,1532],{"class":158},[144,2821,1535],{"class":262},[144,2823,690],{"class":154},[144,2825,449],{"class":158},[144,2827,406],{"class":262},[144,2829,2830,2832,2834,2836,2838],{"class":146,"line":738},[144,2831,1618],{"class":262},[144,2833,266],{"class":258},[144,2835,1623],{"class":262},[144,2837,1626],{"class":158},[144,2839,1629],{"class":262},[144,2841,2842],{"class":146,"line":743},[144,2843,376],{"emptyLinePlaceholder":57},[144,2845,2846,2848,2850,2852,2854,2856,2858],{"class":146,"line":749},[144,2847,1643],{"class":262},[144,2849,266],{"class":258},[144,2851,1648],{"class":262},[144,2853,1651],{"class":158},[144,2855,1562],{"class":262},[144,2857,782],{"class":154},[144,2859,763],{"class":262},[144,2861,2862,2864,2867],{"class":146,"line":766},[144,2863,1689],{"class":262},[144,2865,2866],{"class":158},"\"animated_image.mp4\"",[144,2868,406],{"class":262},[144,2870,2871],{"class":146,"line":787},[144,2872,376],{"emptyLinePlaceholder":57},[144,2874,2875,2877],{"class":146,"line":833},[144,2876,1703],{"class":258},[144,2878,1706],{"class":262},[10,2880,2881],{},"与文生视频相比，这里有几个不同点：",[95,2883,2884,2896,2917],{},[72,2885,2886,2891,2892,2895],{},[22,2887,2888],{},[27,2889,2890],{},"image_urls"," — 一个公开可访问的图片 URL 数组。API 会直接拉取这些图片，所以它们必须能从公网访问（不能是 ",[27,2893,2894],{},"localhost"," 或内网地址）。",[72,2897,2898,2904,2905,2907,2908,2910,2911,2910,2914,297],{},[22,2899,2900,2903],{},[27,2901,2902],{},"@Image1"," 在提示词中"," — 这个标签告诉 Seedance 引用哪张图片以及如何使用它。它对应 ",[27,2906,2890],{}," 中的第一个 URL。如果你传了三张图片，就用 ",[27,2909,2902],{},"、",[27,2912,2913],{},"@Image2",[27,2915,2916],{},"@Image3",[72,2918,2919,2924,2925,2927,2928,297],{},[22,2920,2921,2922],{},"没有 ",[27,2923,1821],{}," — 这里省略了，默认值是 ",[27,2926,1825],{},"。如果要生成无声动画，设为 ",[27,2929,2930],{},"false",[91,2932,2934],{"id":2933},"image-标签的工作原理","@Image 标签的工作原理",[10,2936,2937,2938,2940,2941,2943,2944,2946,2947,2950,2951,2954,2955,2958,2959,297],{},"提示词中的 ",[27,2939,2902],{}," 标签告诉 Seedance 如何使用图片，它引用的是 ",[27,2942,2890],{}," 数组中的第一个 URL。最多可以传 9 张图片（",[27,2945,2902],{}," 到 ",[27,2948,2949],{},"@Image9","）。关于多模态标签（包括 ",[27,2952,2953],{},"@Video"," 和 ",[27,2956,2957],{},"@Audio","）的完整指南，请看 ",[36,2960,2962],{"href":2961},"/blog/seedance-2-multimodal-tags-guide","多模态 @Tags 指南",[10,2964,2965],{},"常用模式：",[2035,2967,2968,2981],{},[2038,2969,2970],{},[2041,2971,2972,2975,2978],{},[2044,2973,2974],{},"提示词模式",[2044,2976,2977],{},"效果",[2044,2979,2980],{},"适用场景",[2051,2982,2983,2996,3009,3022,3035],{},[2041,2984,2985,2990,2993],{},[2056,2986,2987],{},[27,2988,2989],{},"@Image1 as first frame",[2056,2991,2992],{},"把图片用作开头帧",[2056,2994,2995],{},"产品展示、场景设定",[2041,2997,2998,3003,3006],{},[2056,2999,3000],{},[27,3001,3002],{},"@Image1 as last frame",[2056,3004,3005],{},"把图片用作结尾帧",[2056,3007,3008],{},"Logo 揭示、转场",[2041,3010,3011,3016,3019],{},[2056,3012,3013],{},[27,3014,3015],{},"@Image1 as character reference",[2056,3017,3018],{},"保持角色外观一致",[2056,3020,3021],{},"跨片段的角色一致性",[2041,3023,3024,3029,3032],{},[2056,3025,3026],{},[27,3027,3028],{},"@Image1 as style reference",[2056,3030,3031],{},"应用图片的视觉风格",[2056,3033,3034],{},"品牌一致性、美术指导",[2041,3036,3037,3042,3045],{},[2056,3038,3039],{},[27,3040,3041],{},"@Image1 as first frame, @Image2 as last frame",[2056,3043,3044],{},"在两张图片之间创建过渡",[2056,3046,3047],{},"前后对比、变形",[10,3049,3050],{},"我们测试得到的实际响应：",[135,3052,3054],{"className":1855,"code":3053,"language":1857,"meta":140,"style":140},"{\n  \"created\": 1772204037,\n  \"id\": \"task-unified-1772204036-lify8u5p\",\n  \"model\": \"seedance-2.0\",\n  \"object\": \"video.generation.task\",\n  \"progress\": 0,\n  \"status\": \"pending\",\n  \"task_info\": {\n    \"can_cancel\": true,\n    \"estimated_time\": 145\n  },\n  \"type\": \"video\",\n  \"usage\": {\n    \"billing_rule\": \"per_second\",\n    \"credits_reserved\": 17.784,\n    \"user_group\": \"default\"\n  }\n}\n",[27,3055,3056,3060,3071,3082,3092,3102,3112,3122,3128,3138,3147,3151,3161,3167,3177,3187,3195,3199],{"__ignoreMap":140},[144,3057,3058],{"class":146,"line":147},[144,3059,1864],{"class":262},[144,3061,3062,3064,3066,3069],{"class":146,"line":165},[144,3063,1869],{"class":154},[144,3065,437],{"class":262},[144,3067,3068],{"class":154},"1772204037",[144,3070,452],{"class":262},[144,3072,3073,3075,3077,3080],{"class":146,"line":177},[144,3074,1881],{"class":154},[144,3076,437],{"class":262},[144,3078,3079],{"class":158},"\"task-unified-1772204036-lify8u5p\"",[144,3081,452],{"class":262},[144,3083,3084,3086,3088,3090],{"class":146,"line":186},[144,3085,1893],{"class":154},[144,3087,437],{"class":262},[144,3089,1308],{"class":158},[144,3091,452],{"class":262},[144,3093,3094,3096,3098,3100],{"class":146,"line":373},[144,3095,1904],{"class":154},[144,3097,437],{"class":262},[144,3099,1909],{"class":158},[144,3101,452],{"class":262},[144,3103,3104,3106,3108,3110],{"class":146,"line":379},[144,3105,1916],{"class":154},[144,3107,437],{"class":262},[144,3109,782],{"class":154},[144,3111,452],{"class":262},[144,3113,3114,3116,3118,3120],{"class":146,"line":385},[144,3115,1927],{"class":154},[144,3117,437],{"class":262},[144,3119,1932],{"class":158},[144,3121,452],{"class":262},[144,3123,3124,3126],{"class":146,"line":409},[144,3125,1939],{"class":154},[144,3127,1942],{"class":262},[144,3129,3130,3132,3134,3136],{"class":146,"line":420},[144,3131,1947],{"class":154},[144,3133,437],{"class":262},[144,3135,1825],{"class":154},[144,3137,452],{"class":262},[144,3139,3140,3142,3144],{"class":146,"line":431},[144,3141,1958],{"class":154},[144,3143,437],{"class":262},[144,3145,3146],{"class":154},"145\n",[144,3148,3149],{"class":146,"line":455},[144,3150,1968],{"class":262},[144,3152,3153,3155,3157,3159],{"class":146,"line":466},[144,3154,1973],{"class":154},[144,3156,437],{"class":262},[144,3158,1978],{"class":158},[144,3160,452],{"class":262},[144,3162,3163,3165],{"class":146,"line":599},[144,3164,1985],{"class":154},[144,3166,1942],{"class":262},[144,3168,3169,3171,3173,3175],{"class":146,"line":604},[144,3170,1992],{"class":154},[144,3172,437],{"class":262},[144,3174,1997],{"class":158},[144,3176,452],{"class":262},[144,3178,3179,3181,3183,3185],{"class":146,"line":610},[144,3180,2004],{"class":154},[144,3182,437],{"class":262},[144,3184,2009],{"class":154},[144,3186,452],{"class":262},[144,3188,3189,3191,3193],{"class":146,"line":616},[144,3190,2016],{"class":154},[144,3192,437],{"class":262},[144,3194,2021],{"class":158},[144,3196,3197],{"class":146,"line":622},[144,3198,2026],{"class":262},[144,3200,3201],{"class":146,"line":627},[144,3202,469],{"class":262},[10,3204,3205,3206,3208],{},"图生视频和文生视频遵循完全相同的异步模式——提交、轮询、下载。",[27,3207,2105],{}," 会稍长一些，因为模型需要分析输入图片。",[91,3210,3211],{"id":3211},"图片要求",[2035,3213,3214,3224],{},[2038,3215,3216],{},[2041,3217,3218,3221],{},[2044,3219,3220],{},"约束",[2044,3222,3223],{},"值",[2051,3225,3226,3234,3242,3250,3258],{},[2041,3227,3228,3231],{},[2056,3229,3230],{},"最大图片数",[2056,3232,3233],{},"每次请求 9 张",[2041,3235,3236,3239],{},[2056,3237,3238],{},"最大文件大小",[2056,3240,3241],{},"每张 30 MB",[2041,3243,3244,3247],{},[2056,3245,3246],{},"支持格式",[2056,3248,3249],{},"JPEG、PNG、WebP、BMP、TIFF、GIF",[2041,3251,3252,3255],{},[2056,3253,3254],{},"URL 要求",[2056,3256,3257],{},"必须公开可访问",[2041,3259,3260,3263],{},[2056,3261,3262],{},"推荐分辨率",[2056,3264,3265],{},"短边至少 720px",[17,3267,3268],{},[10,3269,3270,3272,3273,3275],{},[22,3271,304],{}," 传本地文件路径而不是 URL。",[27,3274,2890],{}," 字段需要公开可访问的 HTTP/HTTPS URL。如果你的图片在本地，先上传到 S3、Cloudflare R2 或临时文件托管服务。",[17,3277,3278],{},[10,3279,3280,3283],{},[22,3281,3282],{},"限制："," Seedance 不支持上传写实人脸图片。系统会自动拒绝。请使用插画或风格化角色。",[91,3285,3287],{"id":3286},"为-api-托管图片","为 API 托管图片",[10,3289,3290],{},"如果你没有 CDN，以下是快速获取公开 URL 的方法：",[135,3292,3294],{"className":338,"code":3293,"language":340,"meta":140,"style":140},"# 方式一：上传到 S3（如果你有 AWS）\nimport boto3\ns3 = boto3.client('s3')\ns3.upload_file('local_image.jpg', 'my-bucket', 'seedance/input.jpg')\nimage_url = f\"https://my-bucket.s3.amazonaws.com/seedance/input.jpg\"\n\n# 方式二：使用临时文件托管 API\n# 很多服务提供免费的临时托管用于测试\n",[27,3295,3296,3301,3308,3323,3343,3356,3360,3365],{"__ignoreMap":140},[144,3297,3298],{"class":146,"line":147},[144,3299,3300],{"class":173},"# 方式一：上传到 S3（如果你有 AWS）\n",[144,3302,3303,3305],{"class":146,"line":165},[144,3304,347],{"class":258},[144,3306,3307],{"class":262}," boto3\n",[144,3309,3310,3313,3315,3318,3321],{"class":146,"line":177},[144,3311,3312],{"class":262},"s3 ",[144,3314,266],{"class":258},[144,3316,3317],{"class":262}," boto3.client(",[144,3319,3320],{"class":158},"'s3'",[144,3322,406],{"class":262},[144,3324,3325,3328,3331,3333,3336,3338,3341],{"class":146,"line":186},[144,3326,3327],{"class":262},"s3.upload_file(",[144,3329,3330],{"class":158},"'local_image.jpg'",[144,3332,400],{"class":262},[144,3334,3335],{"class":158},"'my-bucket'",[144,3337,400],{"class":262},[144,3339,3340],{"class":158},"'seedance/input.jpg'",[144,3342,406],{"class":262},[144,3344,3345,3348,3350,3353],{"class":146,"line":373},[144,3346,3347],{"class":262},"image_url ",[144,3349,266],{"class":258},[144,3351,3352],{"class":258}," f",[144,3354,3355],{"class":158},"\"https://my-bucket.s3.amazonaws.com/seedance/input.jpg\"\n",[144,3357,3358],{"class":146,"line":379},[144,3359,376],{"emptyLinePlaceholder":57},[144,3361,3362],{"class":146,"line":385},[144,3363,3364],{"class":173},"# 方式二：使用临时文件托管 API\n",[144,3366,3367],{"class":146,"line":409},[144,3368,3369],{"class":173},"# 很多服务提供免费的临时托管用于测试\n",[10,3371,3372,3373,297],{},"更多图生视频高级技巧——首尾帧控制、多图合成、电商产品动画——请看",[36,3374,3376],{"href":3375},"/blog/seedance-2-image-to-video-api","图生视频详解",[44,3378],{},[47,3380,3381],{"id":3381},"自定义你的视频",[10,3383,3384],{},"生成请求中可调整的所有参数：",[2035,3386,3387,3406],{},[2038,3388,3389],{},[2041,3390,3391,3394,3397,3400,3403],{},[2044,3392,3393],{},"参数",[2044,3395,3396],{},"类型",[2044,3398,3399],{},"默认值",[2044,3401,3402],{},"选项",[2044,3404,3405],{},"说明",[2051,3407,3408,3427,3443,3460,3483,3515,3537,3554,3571,3588],{},[2041,3409,3410,3414,3417,3420,3424],{},[2056,3411,3412],{},[27,3413,1747],{},[2056,3415,3416],{},"string",[2056,3418,3419],{},"—",[2056,3421,3422],{},[27,3423,1751],{},[2056,3425,3426],{},"必填。使用的模型。",[2041,3428,3429,3433,3435,3437,3440],{},[2056,3430,3431],{},[27,3432,1761],{},[2056,3434,3416],{},[2056,3436,3419],{},[2056,3438,3439],{},"≤2000 tokens",[2056,3441,3442],{},"必填。视频描述，可使用 @tags。",[2041,3444,3445,3449,3452,3454,3457],{},[2056,3446,3447],{},[27,3448,1773],{},[2056,3450,3451],{},"integer",[2056,3453,1352],{},[2056,3455,3456],{},"4–15",[2056,3458,3459],{},"视频时长（秒）。",[2041,3461,3462,3466,3468,3472,3480],{},[2056,3463,3464],{},[27,3465,1781],{},[2056,3467,3416],{},[2056,3469,3470],{},[27,3471,1785],{},[2056,3473,3474,2910,3476,2910,3478],{},[27,3475,1789],{},[27,3477,1785],{},[27,3479,1793],{},[2056,3481,3482],{},"分辨率档位。越高消耗越多额度。",[2041,3484,3485,3489,3491,3495,3512],{},[2056,3486,3487],{},[27,3488,1801],{},[2056,3490,3416],{},[2056,3492,3493],{},[27,3494,1805],{},[2056,3496,3497,2910,3499,2910,3501,2910,3503,2910,3506,2910,3509],{},[27,3498,1805],{},[27,3500,1809],{},[27,3502,1813],{},[27,3504,3505],{},"4:3",[27,3507,3508],{},"3:4",[27,3510,3511],{},"21:9",[2056,3513,3514],{},"输出宽高比。",[2041,3516,3517,3521,3524,3528,3534],{},[2056,3518,3519],{},[27,3520,1821],{},[2056,3522,3523],{},"boolean",[2056,3525,3526],{},[27,3527,1825],{},[2056,3529,3530,2910,3532],{},[27,3531,1825],{},[27,3533,2930],{},[2056,3535,3536],{},"启用 AI 生成音频/音乐。",[2041,3538,3539,3543,3546,3548,3551],{},[2056,3540,3541],{},[27,3542,2890],{},[2056,3544,3545],{},"array",[2056,3547,3419],{},[2056,3549,3550],{},"≤9 张图片",[2056,3552,3553],{},"参考图片。在提示词中用 @Image1、@Image2... 引用。",[2041,3555,3556,3561,3563,3565,3568],{},[2056,3557,3558],{},[27,3559,3560],{},"video_urls",[2056,3562,3545],{},[2056,3564,3419],{},[2056,3566,3567],{},"≤3 个视频",[2056,3569,3570],{},"参考视频。在提示词中用 @Video1、@Video2... 引用。",[2041,3572,3573,3578,3580,3582,3585],{},[2056,3574,3575],{},[27,3576,3577],{},"audio_urls",[2056,3579,3545],{},[2056,3581,3419],{},[2056,3583,3584],{},"≤3 个音频",[2056,3586,3587],{},"参考音频。在提示词中用 @Audio1、@Audio2... 引用。",[2041,3589,3590,3595,3597,3599,3602],{},[2056,3591,3592],{},[27,3593,3594],{},"callback_url",[2056,3596,3416],{},[2056,3598,3419],{},[2056,3600,3601],{},"HTTPS URL",[2056,3603,3604],{},"完成时的 Webhook 回调地址。",[17,3606,3607],{},[10,3608,3609,3612,3613,2954,3615,3617,3618,2910,3620,3622,3623,2946,3625,3627,3628,3631],{},[22,3610,3611],{},"Seedance 2.0 与 1.5 的区别："," 以上所有参数对 ",[27,3614,1751],{},[27,3616,29],{}," 都适用。主要区别在于：",[27,3619,3560],{},[27,3621,3577],{}," 和多图引用（",[27,3624,2913],{},[27,3626,2949],{},"）是 2.0 独有功能。在 1.5 上使用它们，API 会返回 ",[27,3629,3630],{},"400"," 错误，并明确提示该功能不支持。",[91,3633,3634],{"id":3634},"快速示例",[10,3636,3637],{},[22,3638,3639],{},"竖版社交媒体视频（抖音/Reels）：",[10,3641,3642],{},[2613,3643,2615],{},[135,3645,3647],{"className":338,"code":3646,"language":340,"meta":140,"style":140},"payload = {\n    \"model\": \"seedance-2.0\",\n    \"prompt\": \"A barista pours latte art in slow motion. Close-up overhead shot.\",\n    \"duration\": 8,\n    \"quality\": \"1080p\",\n    \"aspect_ratio\": \"9:16\",       # 竖版，适合手机\n    \"generate_audio\": True\n}\n",[27,3648,3649,3658,3669,3681,3693,3705,3721,3731],{"__ignoreMap":140},[144,3650,3651,3654,3656],{"class":146,"line":147},[144,3652,3653],{"class":262},"payload ",[144,3655,266],{"class":258},[144,3657,428],{"class":262},[144,3659,3660,3663,3665,3667],{"class":146,"line":165},[144,3661,3662],{"class":158},"    \"model\"",[144,3664,437],{"class":262},[144,3666,1308],{"class":158},[144,3668,452],{"class":262},[144,3670,3671,3674,3676,3679],{"class":146,"line":177},[144,3672,3673],{"class":158},"    \"prompt\"",[144,3675,437],{"class":262},[144,3677,3678],{"class":158},"\"A barista pours latte art in slow motion. Close-up overhead shot.\"",[144,3680,452],{"class":262},[144,3682,3683,3686,3688,3691],{"class":146,"line":186},[144,3684,3685],{"class":158},"    \"duration\"",[144,3687,437],{"class":262},[144,3689,3690],{"class":154},"8",[144,3692,452],{"class":262},[144,3694,3695,3698,3700,3703],{"class":146,"line":373},[144,3696,3697],{"class":158},"    \"quality\"",[144,3699,437],{"class":262},[144,3701,3702],{"class":158},"\"1080p\"",[144,3704,452],{"class":262},[144,3706,3707,3710,3712,3715,3718],{"class":146,"line":379},[144,3708,3709],{"class":158},"    \"aspect_ratio\"",[144,3711,437],{"class":262},[144,3713,3714],{"class":158},"\"9:16\"",[144,3716,3717],{"class":262},",       ",[144,3719,3720],{"class":173},"# 竖版，适合手机\n",[144,3722,3723,3726,3728],{"class":146,"line":385},[144,3724,3725],{"class":158},"    \"generate_audio\"",[144,3727,437],{"class":262},[144,3729,3730],{"class":154},"True\n",[144,3732,3733],{"class":146,"line":409},[144,3734,469],{"class":262},[10,3736,3737,3739,3740,3742],{},[27,3738,1809],{}," 比例生成 1080×1920 的视频——抖音、Instagram Reels 和 YouTube Shorts 的原生分辨率。",[27,3741,1793],{}," 质量档保证手机屏幕上的清晰度。",[10,3744,3745],{},[22,3746,3747],{},"电影级宽屏 + 镜头运动：",[135,3749,3751],{"className":338,"code":3750,"language":340,"meta":140,"style":140},"payload = {\n    \"model\": \"seedance-2.0\",\n    \"prompt\": (\n        \"Aerial drone shot over a misty mountain range at sunrise. \"\n        \"Camera slowly pushes forward, revealing a hidden valley. \"\n        \"Cinematic color grading, volumetric lighting.\"\n    ),\n    \"duration\": 10,\n    \"quality\": \"1080p\",\n    \"aspect_ratio\": \"21:9\",       # 超宽屏电影比例\n    \"generate_audio\": True\n}\n",[27,3752,3753,3761,3771,3777,3782,3787,3792,3797,3807,3817,3831,3839],{"__ignoreMap":140},[144,3754,3755,3757,3759],{"class":146,"line":147},[144,3756,3653],{"class":262},[144,3758,266],{"class":258},[144,3760,428],{"class":262},[144,3762,3763,3765,3767,3769],{"class":146,"line":165},[144,3764,3662],{"class":158},[144,3766,437],{"class":262},[144,3768,1308],{"class":158},[144,3770,452],{"class":262},[144,3772,3773,3775],{"class":146,"line":177},[144,3774,3673],{"class":158},[144,3776,1322],{"class":262},[144,3778,3779],{"class":146,"line":186},[144,3780,3781],{"class":158},"        \"Aerial drone shot over a misty mountain range at sunrise. \"\n",[144,3783,3784],{"class":146,"line":373},[144,3785,3786],{"class":158},"        \"Camera slowly pushes forward, revealing a hidden valley. \"\n",[144,3788,3789],{"class":146,"line":379},[144,3790,3791],{"class":158},"        \"Cinematic color grading, volumetric lighting.\"\n",[144,3793,3794],{"class":146,"line":385},[144,3795,3796],{"class":262},"    ),\n",[144,3798,3799,3801,3803,3805],{"class":146,"line":409},[144,3800,3685],{"class":158},[144,3802,437],{"class":262},[144,3804,536],{"class":154},[144,3806,452],{"class":262},[144,3808,3809,3811,3813,3815],{"class":146,"line":420},[144,3810,3697],{"class":158},[144,3812,437],{"class":262},[144,3814,3702],{"class":158},[144,3816,452],{"class":262},[144,3818,3819,3821,3823,3826,3828],{"class":146,"line":431},[144,3820,3709],{"class":158},[144,3822,437],{"class":262},[144,3824,3825],{"class":158},"\"21:9\"",[144,3827,3717],{"class":262},[144,3829,3830],{"class":173},"# 超宽屏电影比例\n",[144,3832,3833,3835,3837],{"class":146,"line":455},[144,3834,3725],{"class":158},[144,3836,437],{"class":262},[144,3838,3730],{"class":154},[144,3840,3841],{"class":146,"line":466},[144,3842,469],{"class":262},[10,3844,3845,3846,297],{},"关于编程控制镜头——推拉变焦、环绕镜头、希区柯克式运镜——请看",[36,3847,3849],{"href":3848},"/blog/seedance-2-camera-movement-api","镜头运动 API 指南",[10,3851,3852],{},[22,3853,3854],{},"无声网页背景视频：",[135,3856,3858],{"className":338,"code":3857,"language":340,"meta":140,"style":140},"payload = {\n    \"model\": \"seedance-2.0\",\n    \"prompt\": \"Abstract flowing particles in deep blue and gold. Slow, meditative movement.\",\n    \"duration\": 15,               # 最长时长，适合无缝循环\n    \"quality\": \"720p\",\n    \"aspect_ratio\": \"21:9\",       # 宽屏背景\n    \"generate_audio\": False       # 自动播放背景不需要音频\n}\n",[27,3859,3860,3868,3878,3889,3904,3914,3927,3939],{"__ignoreMap":140},[144,3861,3862,3864,3866],{"class":146,"line":147},[144,3863,3653],{"class":262},[144,3865,266],{"class":258},[144,3867,428],{"class":262},[144,3869,3870,3872,3874,3876],{"class":146,"line":165},[144,3871,3662],{"class":158},[144,3873,437],{"class":262},[144,3875,1308],{"class":158},[144,3877,452],{"class":262},[144,3879,3880,3882,3884,3887],{"class":146,"line":177},[144,3881,3673],{"class":158},[144,3883,437],{"class":262},[144,3885,3886],{"class":158},"\"Abstract flowing particles in deep blue and gold. Slow, meditative movement.\"",[144,3888,452],{"class":262},[144,3890,3891,3893,3895,3898,3901],{"class":146,"line":186},[144,3892,3685],{"class":158},[144,3894,437],{"class":262},[144,3896,3897],{"class":154},"15",[144,3899,3900],{"class":262},",               ",[144,3902,3903],{"class":173},"# 最长时长，适合无缝循环\n",[144,3905,3906,3908,3910,3912],{"class":146,"line":373},[144,3907,3697],{"class":158},[144,3909,437],{"class":262},[144,3911,1368],{"class":158},[144,3913,452],{"class":262},[144,3915,3916,3918,3920,3922,3924],{"class":146,"line":379},[144,3917,3709],{"class":158},[144,3919,437],{"class":262},[144,3921,3825],{"class":158},[144,3923,3717],{"class":262},[144,3925,3926],{"class":173},"# 宽屏背景\n",[144,3928,3929,3931,3933,3936],{"class":146,"line":385},[144,3930,3725],{"class":158},[144,3932,437],{"class":262},[144,3934,3935],{"class":154},"False",[144,3937,3938],{"class":173},"       # 自动播放背景不需要音频\n",[144,3940,3941],{"class":146,"line":409},[144,3942,469],{"class":262},[10,3944,3945],{},[22,3946,3947],{},"低成本草稿（快速迭代）：",[135,3949,3951],{"className":338,"code":3950,"language":340,"meta":140,"style":140},"payload = {\n    \"model\": \"seedance-2.0\",\n    \"prompt\": \"A cat wearing sunglasses sits at a DJ booth. Neon club lighting.\",\n    \"duration\": 4,                # 最短时长 = 最快生成\n    \"quality\": \"480p\",            # 最低质量 = 最省额度\n    \"aspect_ratio\": \"16:9\"\n}\n",[27,3952,3953,3961,3971,3982,3997,4011,4019],{"__ignoreMap":140},[144,3954,3955,3957,3959],{"class":146,"line":147},[144,3956,3653],{"class":262},[144,3958,266],{"class":258},[144,3960,428],{"class":262},[144,3962,3963,3965,3967,3969],{"class":146,"line":165},[144,3964,3662],{"class":158},[144,3966,437],{"class":262},[144,3968,1308],{"class":158},[144,3970,452],{"class":262},[144,3972,3973,3975,3977,3980],{"class":146,"line":177},[144,3974,3673],{"class":158},[144,3976,437],{"class":262},[144,3978,3979],{"class":158},"\"A cat wearing sunglasses sits at a DJ booth. Neon club lighting.\"",[144,3981,452],{"class":262},[144,3983,3984,3986,3988,3991,3994],{"class":146,"line":186},[144,3985,3685],{"class":158},[144,3987,437],{"class":262},[144,3989,3990],{"class":154},"4",[144,3992,3993],{"class":262},",                ",[144,3995,3996],{"class":173},"# 最短时长 = 最快生成\n",[144,3998,3999,4001,4003,4006,4008],{"class":146,"line":373},[144,4000,3697],{"class":158},[144,4002,437],{"class":262},[144,4004,4005],{"class":158},"\"480p\"",[144,4007,1387],{"class":262},[144,4009,4010],{"class":173},"# 最低质量 = 最省额度\n",[144,4012,4013,4015,4017],{"class":146,"line":379},[144,4014,3709],{"class":158},[144,4016,437],{"class":262},[144,4018,2724],{"class":158},[144,4020,4021],{"class":146,"line":385},[144,4022,469],{"class":262},[17,4024,4025],{},[10,4026,4027,4029,4030,2954,4033,4036,4037,4039],{},[22,4028,132],{}," 开发阶段始终用 ",[27,4031,4032],{},"duration: 4",[27,4034,4035],{},"quality: \"480p\"","。这是最便宜最快的组合——非常适合迭代提示词。满意后再用 ",[27,4038,1793],{}," 和你需要的时长渲染最终版。",[91,4041,4042],{"id":4042},"额度消耗估算",[10,4044,4045],{},"额度消耗随时长和质量递增。大致参考：",[2035,4047,4048,4067],{},[2038,4049,4050],{},[2041,4051,4052,4055,4058,4061,4064],{},[2044,4053,4054],{},"质量",[2044,4056,4057],{},"4s",[2044,4059,4060],{},"5s",[2044,4062,4063],{},"10s",[2044,4065,4066],{},"15s",[2051,4068,4069,4085,4101],{},[2041,4070,4071,4073,4076,4079,4082],{},[2056,4072,1789],{},[2056,4074,4075],{},"~8",[2056,4077,4078],{},"~10",[2056,4080,4081],{},"~20",[2056,4083,4084],{},"~30",[2041,4086,4087,4089,4092,4095,4098],{},[2056,4088,1785],{},[2056,4090,4091],{},"~14",[2056,4093,4094],{},"~18",[2056,4096,4097],{},"~36",[2056,4099,4100],{},"~53",[2041,4102,4103,4105,4108,4111,4114],{},[2056,4104,1793],{},[2056,4106,4107],{},"~22",[2056,4109,4110],{},"~28",[2056,4112,4113],{},"~55",[2056,4115,4116],{},"~83",[10,4118,4119],{},[2613,4120,4121,4122,4124,4125,4129],{},"近似额度。实际费用以 ",[27,4123,2115],{}," 字段为准。查看 ",[36,4126,4128],{"href":210,"rel":4127},[40],"EvoLink 控制台","了解当前费率。",[10,4131,4132,4133,2910,4136,2910,4138,4140,4141,4144,4145,297],{},"多模态引用系统——",[27,4134,4135],{},"@Image",[27,4137,2953],{},[27,4139,2957],{}," 标签——是 Seedance 2.0 真正强大的地方。你可以从参考视频中复刻",[36,4142,4143],{"href":3848},"镜头运动","，保持跨镜头的角色一致性，还能与音频节拍同步。完整指南请看",[36,4146,4147],{"href":2961},"多模态 @Tags 终极指南",[44,4149],{},[47,4151,4152],{"id":4152},"优雅地处理错误",[10,4154,4155],{},"API 调用会失败，网络会中断，限流会被触发。以下是如何构建健壮的代码来应对每种真实的错误场景。",[91,4157,4158],{"id":4158},"常见错误响应",[10,4160,4161],{},"所有错误遵循相同的格式：",[135,4163,4165],{"className":1855,"code":4164,"language":1857,"meta":140,"style":140},"{\n  \"error\": {\n    \"message\": \"description of what went wrong\",\n    \"type\": \"error_category\",\n    \"code\": \"specific_error_code\"\n  }\n}\n",[27,4166,4167,4171,4178,4190,4202,4212,4216],{"__ignoreMap":140},[144,4168,4169],{"class":146,"line":147},[144,4170,1864],{"class":262},[144,4172,4173,4176],{"class":146,"line":165},[144,4174,4175],{"class":154},"  \"error\"",[144,4177,1942],{"class":262},[144,4179,4180,4183,4185,4188],{"class":146,"line":177},[144,4181,4182],{"class":154},"    \"message\"",[144,4184,437],{"class":262},[144,4186,4187],{"class":158},"\"description of what went wrong\"",[144,4189,452],{"class":262},[144,4191,4192,4195,4197,4200],{"class":146,"line":186},[144,4193,4194],{"class":154},"    \"type\"",[144,4196,437],{"class":262},[144,4198,4199],{"class":158},"\"error_category\"",[144,4201,452],{"class":262},[144,4203,4204,4207,4209],{"class":146,"line":373},[144,4205,4206],{"class":154},"    \"code\"",[144,4208,437],{"class":262},[144,4210,4211],{"class":158},"\"specific_error_code\"\n",[144,4213,4214],{"class":146,"line":379},[144,4215,2026],{"class":262},[144,4217,4218],{"class":146,"line":385},[144,4219,469],{"class":262},[10,4221,4222,4225,4226,2954,4229,297,4232,4234,4235,4237,4238,4240],{},[27,4223,4224],{},"error"," 对象始终包含 ",[27,4227,4228],{},"message",[27,4230,4231],{},"type",[27,4233,27],{}," 字段在大多数错误中存在但不是全部。优先检查 ",[27,4236,4231],{},"，再看 ",[27,4239,27],{}," 获取具体信息。",[10,4242,4243],{},"以下是 API 返回的真实错误响应：",[10,4245,4246],{},[22,4247,4248],{},"401 — API Key 无效：",[135,4250,4252],{"className":1855,"code":4251,"language":1857,"meta":140,"style":140},"{\n  \"error\": {\n    \"message\": \"Invalid token (request id: 20260227225245660301729AApJNAhJ)\",\n    \"type\": \"evo_api_error\"\n  }\n}\n",[27,4253,4254,4258,4264,4275,4284,4288],{"__ignoreMap":140},[144,4255,4256],{"class":146,"line":147},[144,4257,1864],{"class":262},[144,4259,4260,4262],{"class":146,"line":165},[144,4261,4175],{"class":154},[144,4263,1942],{"class":262},[144,4265,4266,4268,4270,4273],{"class":146,"line":177},[144,4267,4182],{"class":154},[144,4269,437],{"class":262},[144,4271,4272],{"class":158},"\"Invalid token (request id: 20260227225245660301729AApJNAhJ)\"",[144,4274,452],{"class":262},[144,4276,4277,4279,4281],{"class":146,"line":186},[144,4278,4194],{"class":154},[144,4280,437],{"class":262},[144,4282,4283],{"class":158},"\"evo_api_error\"\n",[144,4285,4286],{"class":146,"line":373},[144,4287,2026],{"class":262},[144,4289,4290],{"class":146,"line":379},[144,4291,469],{"class":262},[10,4293,4294,4295,4297],{},"说明你的 API Key 错误、过期或已被撤销。检查 ",[27,4296,275],{}," 环境变量。常见原因：复制 Key 时带了尾部空格。",[10,4299,4300],{},[22,4301,4302],{},"400 — 缺少必填字段：",[135,4304,4306],{"className":1855,"code":4305,"language":1857,"meta":140,"style":140},"{\n  \"error\": {\n    \"code\": \"invalid_parameter\",\n    \"message\": \"prompt cannot be empty\",\n    \"type\": \"invalid_request_error\"\n  }\n}\n",[27,4307,4308,4312,4318,4329,4340,4349,4353],{"__ignoreMap":140},[144,4309,4310],{"class":146,"line":147},[144,4311,1864],{"class":262},[144,4313,4314,4316],{"class":146,"line":165},[144,4315,4175],{"class":154},[144,4317,1942],{"class":262},[144,4319,4320,4322,4324,4327],{"class":146,"line":177},[144,4321,4206],{"class":154},[144,4323,437],{"class":262},[144,4325,4326],{"class":158},"\"invalid_parameter\"",[144,4328,452],{"class":262},[144,4330,4331,4333,4335,4338],{"class":146,"line":186},[144,4332,4182],{"class":154},[144,4334,437],{"class":262},[144,4336,4337],{"class":158},"\"prompt cannot be empty\"",[144,4339,452],{"class":262},[144,4341,4342,4344,4346],{"class":146,"line":373},[144,4343,4194],{"class":154},[144,4345,437],{"class":262},[144,4347,4348],{"class":158},"\"invalid_request_error\"\n",[144,4350,4351],{"class":146,"line":379},[144,4352,2026],{"class":262},[144,4354,4355],{"class":146,"line":385},[144,4356,469],{"class":262},[10,4358,4359,4361],{},[27,4360,1761],{}," 字段在所有生成请求中都是必填的。传空字符串或纯空格也会触发这个错误。",[10,4363,4364],{},[22,4365,4366],{},"400 — 参数值无效：",[135,4368,4370],{"className":1855,"code":4369,"language":1857,"meta":140,"style":140},"{\n  \"error\": {\n    \"code\": \"invalid_parameter\",\n    \"message\": \"duration must be between 4 and 15\",\n    \"type\": \"invalid_request_error\"\n  }\n}\n",[27,4371,4372,4376,4382,4392,4403,4411,4415],{"__ignoreMap":140},[144,4373,4374],{"class":146,"line":147},[144,4375,1864],{"class":262},[144,4377,4378,4380],{"class":146,"line":165},[144,4379,4175],{"class":154},[144,4381,1942],{"class":262},[144,4383,4384,4386,4388,4390],{"class":146,"line":177},[144,4385,4206],{"class":154},[144,4387,437],{"class":262},[144,4389,4326],{"class":158},[144,4391,452],{"class":262},[144,4393,4394,4396,4398,4401],{"class":146,"line":186},[144,4395,4182],{"class":154},[144,4397,437],{"class":262},[144,4399,4400],{"class":158},"\"duration must be between 4 and 15\"",[144,4402,452],{"class":262},[144,4404,4405,4407,4409],{"class":146,"line":373},[144,4406,4194],{"class":154},[144,4408,437],{"class":262},[144,4410,4348],{"class":158},[144,4412,4413],{"class":146,"line":379},[144,4414,2026],{"class":262},[144,4416,4417],{"class":146,"line":385},[144,4418,469],{"class":262},[10,4420,4421,4422,280,4425,4428],{},"当你传 ",[27,4423,4424],{},"duration: 3",[27,4426,4427],{},"duration: 20"," 时会出现。有效范围是 4–15 秒（含）。",[10,4430,4431],{},[22,4432,4433],{},"400 — 不支持的质量档位：",[135,4435,4437],{"className":1855,"code":4436,"language":1857,"meta":140,"style":140},"{\n  \"error\": {\n    \"code\": \"invalid_parameter\",\n    \"message\": \"quality must be one of: 480p, 720p, 1080p\",\n    \"type\": \"invalid_request_error\"\n  }\n}\n",[27,4438,4439,4443,4449,4459,4470,4478,4482],{"__ignoreMap":140},[144,4440,4441],{"class":146,"line":147},[144,4442,1864],{"class":262},[144,4444,4445,4447],{"class":146,"line":165},[144,4446,4175],{"class":154},[144,4448,1942],{"class":262},[144,4450,4451,4453,4455,4457],{"class":146,"line":177},[144,4452,4206],{"class":154},[144,4454,437],{"class":262},[144,4456,4326],{"class":158},[144,4458,452],{"class":262},[144,4460,4461,4463,4465,4468],{"class":146,"line":186},[144,4462,4182],{"class":154},[144,4464,437],{"class":262},[144,4466,4467],{"class":158},"\"quality must be one of: 480p, 720p, 1080p\"",[144,4469,452],{"class":262},[144,4471,4472,4474,4476],{"class":146,"line":373},[144,4473,4194],{"class":154},[144,4475,437],{"class":262},[144,4477,4348],{"class":158},[144,4479,4480],{"class":146,"line":379},[144,4481,2026],{"class":262},[144,4483,4484],{"class":146,"line":385},[144,4485,469],{"class":262},[10,4487,4488,4489,280,4492,4495,4496,2910,4498,280,4500,297],{},"常见于传 ",[27,4490,4491],{},"\"quality\": \"4k\"",[27,4493,4494],{},"\"quality\": \"hd\""," 时。必须使用精确的字符串：",[27,4497,1789],{},[27,4499,1785],{},[27,4501,1793],{},[10,4503,4504],{},[22,4505,4506],{},"402 — 额度不足：",[135,4508,4510],{"className":1855,"code":4509,"language":1857,"meta":140,"style":140},"{\n  \"error\": {\n    \"message\": \"Insufficient credits. Required: 17.784, Available: 2.100\",\n    \"type\": \"insufficient_quota_error\"\n  }\n}\n",[27,4511,4512,4516,4522,4533,4542,4546],{"__ignoreMap":140},[144,4513,4514],{"class":146,"line":147},[144,4515,1864],{"class":262},[144,4517,4518,4520],{"class":146,"line":165},[144,4519,4175],{"class":154},[144,4521,1942],{"class":262},[144,4523,4524,4526,4528,4531],{"class":146,"line":177},[144,4525,4182],{"class":154},[144,4527,437],{"class":262},[144,4529,4530],{"class":158},"\"Insufficient credits. Required: 17.784, Available: 2.100\"",[144,4532,452],{"class":262},[144,4534,4535,4537,4539],{"class":146,"line":186},[144,4536,4194],{"class":154},[144,4538,437],{"class":262},[144,4540,4541],{"class":158},"\"insufficient_quota_error\"\n",[144,4543,4544],{"class":146,"line":373},[144,4545,2026],{"class":262},[144,4547,4548],{"class":146,"line":379},[144,4549,469],{"class":262},[10,4551,4552,4553,4556],{},"你的账户额度不够。错误信息会告诉你需要多少、还剩多少。去 ",[36,4554,4128],{"href":210,"rel":4555},[40]," 充值。",[10,4558,4559],{},[22,4560,4561],{},"404 — 任务未找到：",[135,4563,4565],{"className":1855,"code":4564,"language":1857,"meta":140,"style":140},"{\n  \"error\": {\n    \"message\": \"Task not found\",\n    \"type\": \"invalid_request_error\",\n    \"code\": \"task_not_found\"\n  }\n}\n",[27,4566,4567,4571,4577,4588,4599,4608,4612],{"__ignoreMap":140},[144,4568,4569],{"class":146,"line":147},[144,4570,1864],{"class":262},[144,4572,4573,4575],{"class":146,"line":165},[144,4574,4175],{"class":154},[144,4576,1942],{"class":262},[144,4578,4579,4581,4583,4586],{"class":146,"line":177},[144,4580,4182],{"class":154},[144,4582,437],{"class":262},[144,4584,4585],{"class":158},"\"Task not found\"",[144,4587,452],{"class":262},[144,4589,4590,4592,4594,4597],{"class":146,"line":186},[144,4591,4194],{"class":154},[144,4593,437],{"class":262},[144,4595,4596],{"class":158},"\"invalid_request_error\"",[144,4598,452],{"class":262},[144,4600,4601,4603,4605],{"class":146,"line":373},[144,4602,4206],{"class":154},[144,4604,437],{"class":262},[144,4606,4607],{"class":158},"\"task_not_found\"\n",[144,4609,4610],{"class":146,"line":379},[144,4611,2026],{"class":262},[144,4613,4614],{"class":146,"line":385},[144,4615,469],{"class":262},[10,4617,4618,4619,4621],{},"通常是任务 ID 错误，或者任务创建超过 24 小时已过期。确认你用的是创建响应中的 ",[27,4620,2060],{}," 字段，而不是其他字段。",[10,4623,4624],{},[22,4625,4626],{},"413 — 图片太大：",[135,4628,4630],{"className":1855,"code":4629,"language":1857,"meta":140,"style":140},"{\n  \"error\": {\n    \"message\": \"Image file size exceeds 30MB limit\",\n    \"type\": \"request_too_large_error\"\n  }\n}\n",[27,4631,4632,4636,4642,4653,4662,4666],{"__ignoreMap":140},[144,4633,4634],{"class":146,"line":147},[144,4635,1864],{"class":262},[144,4637,4638,4640],{"class":146,"line":165},[144,4639,4175],{"class":154},[144,4641,1942],{"class":262},[144,4643,4644,4646,4648,4651],{"class":146,"line":177},[144,4645,4182],{"class":154},[144,4647,437],{"class":262},[144,4649,4650],{"class":158},"\"Image file size exceeds 30MB limit\"",[144,4652,452],{"class":262},[144,4654,4655,4657,4659],{"class":146,"line":186},[144,4656,4194],{"class":154},[144,4658,437],{"class":262},[144,4660,4661],{"class":158},"\"request_too_large_error\"\n",[144,4663,4664],{"class":146,"line":373},[144,4665,2026],{"class":262},[144,4667,4668],{"class":146,"line":379},[144,4669,469],{"class":262},[10,4671,4672],{},"上传前先压缩图片。对 API 来说，超过 2–3 MB 的图片对生成效果的提升微乎其微。",[10,4674,4675],{},[22,4676,4677],{},"429 — 触发限流：",[135,4679,4681],{"className":1855,"code":4680,"language":1857,"meta":140,"style":140},"{\n  \"error\": {\n    \"message\": \"Rate limit exceeded. Please retry after 60 seconds.\",\n    \"type\": \"rate_limit_error\"\n  }\n}\n",[27,4682,4683,4687,4693,4704,4713,4717],{"__ignoreMap":140},[144,4684,4685],{"class":146,"line":147},[144,4686,1864],{"class":262},[144,4688,4689,4691],{"class":146,"line":165},[144,4690,4175],{"class":154},[144,4692,1942],{"class":262},[144,4694,4695,4697,4699,4702],{"class":146,"line":177},[144,4696,4182],{"class":154},[144,4698,437],{"class":262},[144,4700,4701],{"class":158},"\"Rate limit exceeded. Please retry after 60 seconds.\"",[144,4703,452],{"class":262},[144,4705,4706,4708,4710],{"class":146,"line":186},[144,4707,4194],{"class":154},[144,4709,437],{"class":262},[144,4711,4712],{"class":158},"\"rate_limit_error\"\n",[144,4714,4715],{"class":146,"line":373},[144,4716,2026],{"class":262},[144,4718,4719],{"class":146,"line":379},[144,4720,469],{"class":262},[10,4722,4723],{},"请求太频繁了。默认限制对开发来说很宽裕，但批量脚本可能会触发。实现指数退避（见下文）。",[10,4725,4726],{},[22,4727,4728],{},"422 — 内容审核拒绝：",[135,4730,4732],{"className":1855,"code":4731,"language":1857,"meta":140,"style":140},"{\n  \"error\": {\n    \"message\": \"Content rejected by safety filter\",\n    \"type\": \"content_policy_violation\",\n    \"code\": \"content_filtered\"\n  }\n}\n",[27,4733,4734,4738,4744,4755,4766,4775,4779],{"__ignoreMap":140},[144,4735,4736],{"class":146,"line":147},[144,4737,1864],{"class":262},[144,4739,4740,4742],{"class":146,"line":165},[144,4741,4175],{"class":154},[144,4743,1942],{"class":262},[144,4745,4746,4748,4750,4753],{"class":146,"line":177},[144,4747,4182],{"class":154},[144,4749,437],{"class":262},[144,4751,4752],{"class":158},"\"Content rejected by safety filter\"",[144,4754,452],{"class":262},[144,4756,4757,4759,4761,4764],{"class":146,"line":186},[144,4758,4194],{"class":154},[144,4760,437],{"class":262},[144,4762,4763],{"class":158},"\"content_policy_violation\"",[144,4765,452],{"class":262},[144,4767,4768,4770,4772],{"class":146,"line":373},[144,4769,4206],{"class":154},[144,4771,437],{"class":262},[144,4773,4774],{"class":158},"\"content_filtered\"\n",[144,4776,4777],{"class":146,"line":379},[144,4778,2026],{"class":262},[144,4780,4781],{"class":146,"line":385},[144,4782,469],{"class":262},[10,4784,4785,4786,4788],{},"你的提示词或输入图片触发了内容审核系统。修改提示词避免受限内容。",[27,4787,2890],{}," 中的写实人脸图片会被自动拒绝。",[91,4790,4791],{"id":4791},"错误速查表",[2035,4793,4794,4811],{},[2038,4795,4796],{},[2041,4797,4798,4801,4803,4805,4808],{},[2044,4799,4800],{},"HTTP 状态码",[2044,4802,3396],{},[2044,4804,2049],{},[2044,4806,4807],{},"可重试？",[2044,4809,4810],{},"处理方式",[2051,4812,4813,4831,4849,4867,4885,4903,4921,4942,4962,4982],{},[2041,4814,4815,4817,4822,4825,4828],{},[2056,4816,3630],{},[2056,4818,4819],{},[27,4820,4821],{},"invalid_request_error",[2056,4823,4824],{},"参数错误",[2056,4826,4827],{},"否",[2056,4829,4830],{},"修正 payload",[2041,4832,4833,4836,4841,4844,4846],{},[2056,4834,4835],{},"401",[2056,4837,4838],{},[27,4839,4840],{},"authentication_error",[2056,4842,4843],{},"API Key 无效",[2056,4845,4827],{},[2056,4847,4848],{},"检查 Key",[2041,4850,4851,4854,4859,4862,4864],{},[2056,4852,4853],{},"402",[2056,4855,4856],{},[27,4857,4858],{},"insufficient_quota_error",[2056,4860,4861],{},"额度不足",[2056,4863,4827],{},[2056,4865,4866],{},"充值",[2041,4868,4869,4872,4877,4880,4882],{},[2056,4870,4871],{},"404",[2056,4873,4874],{},[27,4875,4876],{},"not_found_error",[2056,4878,4879],{},"任务或模型未找到",[2056,4881,4827],{},[2056,4883,4884],{},"检查 task_id / 模型名",[2041,4886,4887,4890,4895,4898,4900],{},[2056,4888,4889],{},"413",[2056,4891,4892],{},[27,4893,4894],{},"request_too_large_error",[2056,4896,4897],{},"请求体太大",[2056,4899,4827],{},[2056,4901,4902],{},"减小文件大小",[2041,4904,4905,4908,4913,4916,4918],{},[2056,4906,4907],{},"422",[2056,4909,4910],{},[27,4911,4912],{},"content_policy_violation",[2056,4914,4915],{},"内容被过滤",[2056,4917,4827],{},[2056,4919,4920],{},"修改提示词",[2041,4922,4923,4926,4931,4934,4939],{},[2056,4924,4925],{},"429",[2056,4927,4928],{},[27,4929,4930],{},"rate_limit_error",[2056,4932,4933],{},"请求太频繁",[2056,4935,4936],{},[22,4937,4938],{},"是",[2056,4940,4941],{},"等 60 秒后重试",[2041,4943,4944,4947,4952,4955,4959],{},[2056,4945,4946],{},"500",[2056,4948,4949],{},[27,4950,4951],{},"internal_server_error",[2056,4953,4954],{},"服务器问题",[2056,4956,4957],{},[22,4958,4938],{},[2056,4960,4961],{},"几秒后重试",[2041,4963,4964,4967,4972,4975,4979],{},[2056,4965,4966],{},"502",[2056,4968,4969],{},[27,4970,4971],{},"bad_gateway",[2056,4973,4974],{},"上游错误",[2056,4976,4977],{},[22,4978,4938],{},[2056,4980,4981],{},"5 秒后重试",[2041,4983,4984,4987,4992,4995,4999],{},[2056,4985,4986],{},"503",[2056,4988,4989],{},[27,4990,4991],{},"service_unavailable_error",[2056,4993,4994],{},"服务不可用",[2056,4996,4997],{},[22,4998,4938],{},[2056,5000,5001],{},"30 秒后重试",[91,5003,5004],{"id":5004},"生产级错误处理",[10,5006,5007],{},"用重试逻辑包装你的 API 调用，处理瞬态错误：",[10,5009,5010],{},[2613,5011,2615],{},[135,5013,5015],{"className":338,"code":5014,"language":340,"meta":140,"style":140},"import random\n\ndef generate_video_with_retry(payload, max_retries=3):\n    \"\"\"\n    提交视频生成请求，对瞬态错误（429、500、502、503）自动重试。\n    \n    使用指数退避 + 随机抖动避免惊群效应：\n    - 第 1 次：等待 ~1s\n    - 第 2 次：等待 ~2s  \n    - 第 3 次：等待 ~4s\n    \n    不可重试的错误（400、401、402、404、413、422）立即失败，\n    因为重试不会修复根本问题。\n    \"\"\"\n    for attempt in range(max_retries):\n        try:\n            response = requests.post(\n                f\"{BASE_URL}/videos/generations\",\n                headers=HEADERS,\n                json=payload,\n                timeout=30       # 30 秒连接超时\n            )\n\n            # 成功——返回 task 对象\n            if response.status_code == 200:\n                return response.json()\n\n            # 解析错误响应\n            error = response.json().get(\"error\", {})\n            error_type = error.get(\"type\", \"\")\n            error_msg = error.get(\"message\", \"Unknown error\")\n\n            # 不可重试的错误——立即失败\n            if response.status_code in (400, 401, 402, 404, 413, 422):\n                raise ValueError(\n                    f\"API error {response.status_code}: {error_msg}\"\n                )\n\n            # 可重试的错误——指数退避 + 随机抖动\n            if response.status_code in (429, 500, 502, 503):\n                wait = (2 ** attempt) + random.uniform(0, 1)\n                print(f\"  Retry {attempt + 1}/{max_retries} \"\n                      f\"after {wait:.1f}s ({error_type}: {error_msg})\")\n                time.sleep(wait)\n                continue\n\n        except requests.exceptions.Timeout:\n            # 服务器在 30 秒内没有响应\n            wait = (2 ** attempt) + random.uniform(0, 1)\n            print(f\"  Timeout. Retry {attempt + 1}/{max_retries} \"\n                  f\"after {wait:.1f}s\")\n            time.sleep(wait)\n            continue\n\n        except requests.exceptions.ConnectionError as e:\n            # DNS 解析失败、连接被拒等\n            wait = (2 ** attempt) + random.uniform(0, 1)\n            print(f\"  Connection error: {e}. Retry {attempt + 1}/{max_retries} \"\n                  f\"after {wait:.1f}s\")\n            time.sleep(wait)\n            continue\n\n    raise RuntimeError(f\"Failed after {max_retries} retries\")\n",[27,5016,5017,5024,5028,5045,5049,5054,5058,5063,5068,5073,5078,5082,5087,5092,5096,5112,5119,5128,5140,5151,5161,5174,5178,5182,5187,5200,5207,5211,5216,5230,5250,5269,5273,5278,5312,5322,5348,5353,5357,5362,5388,5421,5455,5496,5501,5506,5510,5518,5524,5552,5583,5603,5609,5615,5620,5633,5639,5666,5706,5725,5730,5735,5740],{"__ignoreMap":140},[144,5018,5019,5021],{"class":146,"line":147},[144,5020,347],{"class":258},[144,5022,5023],{"class":262}," random\n",[144,5025,5026],{"class":146,"line":165},[144,5027,376],{"emptyLinePlaceholder":57},[144,5029,5030,5032,5035,5038,5040,5043],{"class":146,"line":177},[144,5031,525],{"class":258},[144,5033,5034],{"class":150}," generate_video_with_retry",[144,5036,5037],{"class":262},"(payload, max_retries",[144,5039,266],{"class":258},[144,5041,5042],{"class":154},"3",[144,5044,547],{"class":262},[144,5046,5047],{"class":146,"line":186},[144,5048,552],{"class":158},[144,5050,5051],{"class":146,"line":373},[144,5052,5053],{"class":158},"    提交视频生成请求，对瞬态错误（429、500、502、503）自动重试。\n",[144,5055,5056],{"class":146,"line":379},[144,5057,562],{"class":158},[144,5059,5060],{"class":146,"line":385},[144,5061,5062],{"class":158},"    使用指数退避 + 随机抖动避免惊群效应：\n",[144,5064,5065],{"class":146,"line":409},[144,5066,5067],{"class":158},"    - 第 1 次：等待 ~1s\n",[144,5069,5070],{"class":146,"line":420},[144,5071,5072],{"class":158},"    - 第 2 次：等待 ~2s  \n",[144,5074,5075],{"class":146,"line":431},[144,5076,5077],{"class":158},"    - 第 3 次：等待 ~4s\n",[144,5079,5080],{"class":146,"line":455},[144,5081,562],{"class":158},[144,5083,5084],{"class":146,"line":466},[144,5085,5086],{"class":158},"    不可重试的错误（400、401、402、404、413、422）立即失败，\n",[144,5088,5089],{"class":146,"line":599},[144,5090,5091],{"class":158},"    因为重试不会修复根本问题。\n",[144,5093,5094],{"class":146,"line":604},[144,5095,552],{"class":158},[144,5097,5098,5101,5104,5106,5109],{"class":146,"line":610},[144,5099,5100],{"class":258},"    for",[144,5102,5103],{"class":262}," attempt ",[144,5105,1180],{"class":258},[144,5107,5108],{"class":154}," range",[144,5110,5111],{"class":262},"(max_retries):\n",[144,5113,5114,5117],{"class":146,"line":616},[144,5115,5116],{"class":258},"        try",[144,5118,859],{"class":262},[144,5120,5121,5124,5126],{"class":146,"line":622},[144,5122,5123],{"class":262},"            response ",[144,5125,266],{"class":258},[144,5127,1432],{"class":262},[144,5129,5130,5132,5134,5136,5138],{"class":146,"line":627},[144,5131,917],{"class":258},[144,5133,449],{"class":158},[144,5135,678],{"class":154},[144,5137,1444],{"class":158},[144,5139,452],{"class":262},[144,5141,5142,5145,5147,5149],{"class":146,"line":638},[144,5143,5144],{"class":700},"                headers",[144,5146,266],{"class":258},[144,5148,423],{"class":154},[144,5150,452],{"class":262},[144,5152,5153,5156,5158],{"class":146,"line":653},[144,5154,5155],{"class":700},"                json",[144,5157,266],{"class":258},[144,5159,5160],{"class":262},"payload,\n",[144,5162,5163,5166,5168,5171],{"class":146,"line":659},[144,5164,5165],{"class":700},"                timeout",[144,5167,266],{"class":258},[144,5169,5170],{"class":154},"30",[144,5172,5173],{"class":173},"       # 30 秒连接超时\n",[144,5175,5176],{"class":146,"line":670},[144,5177,956],{"class":262},[144,5179,5180],{"class":146,"line":697},[144,5181,376],{"emptyLinePlaceholder":57},[144,5183,5184],{"class":146,"line":709},[144,5185,5186],{"class":173},"            # 成功——返回 task 对象\n",[144,5188,5189,5192,5194,5196,5198],{"class":146,"line":715},[144,5190,5191],{"class":258},"            if",[144,5193,2503],{"class":262},[144,5195,853],{"class":258},[144,5197,2508],{"class":154},[144,5199,859],{"class":262},[144,5201,5202,5205],{"class":146,"line":721},[144,5203,5204],{"class":258},"                return",[144,5206,735],{"class":262},[144,5208,5209],{"class":146,"line":727},[144,5210,376],{"emptyLinePlaceholder":57},[144,5212,5213],{"class":146,"line":738},[144,5214,5215],{"class":173},"            # 解析错误响应\n",[144,5217,5218,5221,5223,5226,5228],{"class":146,"line":743},[144,5219,5220],{"class":262},"            error ",[144,5222,266],{"class":258},[144,5224,5225],{"class":262}," response.json().get(",[144,5227,896],{"class":158},[144,5229,899],{"class":262},[144,5231,5232,5235,5237,5240,5243,5245,5248],{"class":146,"line":749},[144,5233,5234],{"class":262},"            error_type ",[144,5236,266],{"class":258},[144,5238,5239],{"class":262}," error.get(",[144,5241,5242],{"class":158},"\"type\"",[144,5244,400],{"class":262},[144,5246,5247],{"class":158},"\"\"",[144,5249,406],{"class":262},[144,5251,5252,5255,5257,5259,5262,5264,5267],{"class":146,"line":766},[144,5253,5254],{"class":262},"            error_msg ",[144,5256,266],{"class":258},[144,5258,5239],{"class":262},[144,5260,5261],{"class":158},"\"message\"",[144,5263,400],{"class":262},[144,5265,5266],{"class":158},"\"Unknown error\"",[144,5268,406],{"class":262},[144,5270,5271],{"class":146,"line":787},[144,5272,376],{"emptyLinePlaceholder":57},[144,5274,5275],{"class":146,"line":833},[144,5276,5277],{"class":173},"            # 不可重试的错误——立即失败\n",[144,5279,5280,5282,5284,5286,5288,5290,5292,5294,5296,5298,5300,5302,5304,5306,5308,5310],{"class":146,"line":838},[144,5281,5191],{"class":258},[144,5283,2503],{"class":262},[144,5285,1180],{"class":258},[144,5287,1218],{"class":262},[144,5289,3630],{"class":154},[144,5291,400],{"class":262},[144,5293,4835],{"class":154},[144,5295,400],{"class":262},[144,5297,4853],{"class":154},[144,5299,400],{"class":262},[144,5301,4871],{"class":154},[144,5303,400],{"class":262},[144,5305,4889],{"class":154},[144,5307,400],{"class":262},[144,5309,4907],{"class":154},[144,5311,547],{"class":262},[144,5313,5314,5317,5320],{"class":146,"line":844},[144,5315,5316],{"class":258},"                raise",[144,5318,5319],{"class":154}," ValueError",[144,5321,911],{"class":262},[144,5323,5324,5327,5330,5332,5335,5337,5339,5341,5344,5346],{"class":146,"line":862},[144,5325,5326],{"class":258},"                    f",[144,5328,5329],{"class":158},"\"API error ",[144,5331,684],{"class":154},[144,5333,5334],{"class":262},"response.status_code",[144,5336,690],{"class":154},[144,5338,437],{"class":158},[144,5340,684],{"class":154},[144,5342,5343],{"class":262},"error_msg",[144,5345,690],{"class":154},[144,5347,950],{"class":158},[144,5349,5350],{"class":146,"line":871},[144,5351,5352],{"class":262},"                )\n",[144,5354,5355],{"class":146,"line":886},[144,5356,376],{"emptyLinePlaceholder":57},[144,5358,5359],{"class":146,"line":902},[144,5360,5361],{"class":173},"            # 可重试的错误——指数退避 + 随机抖动\n",[144,5363,5364,5366,5368,5370,5372,5374,5376,5378,5380,5382,5384,5386],{"class":146,"line":914},[144,5365,5191],{"class":258},[144,5367,2503],{"class":262},[144,5369,1180],{"class":258},[144,5371,1218],{"class":262},[144,5373,4925],{"class":154},[144,5375,400],{"class":262},[144,5377,4946],{"class":154},[144,5379,400],{"class":262},[144,5381,4966],{"class":154},[144,5383,400],{"class":262},[144,5385,4986],{"class":154},[144,5387,547],{"class":262},[144,5389,5390,5393,5395,5397,5400,5403,5406,5409,5412,5414,5416,5419],{"class":146,"line":953},[144,5391,5392],{"class":262},"                wait ",[144,5394,266],{"class":258},[144,5396,1218],{"class":262},[144,5398,5399],{"class":154},"2",[144,5401,5402],{"class":258}," **",[144,5404,5405],{"class":262}," attempt) ",[144,5407,5408],{"class":258},"+",[144,5410,5411],{"class":262}," random.uniform(",[144,5413,782],{"class":154},[144,5415,400],{"class":262},[144,5417,5418],{"class":154},"1",[144,5420,406],{"class":262},[144,5422,5423,5426,5428,5430,5433,5435,5438,5440,5443,5445,5447,5450,5452],{"class":146,"line":959},[144,5424,5425],{"class":154},"                print",[144,5427,793],{"class":262},[144,5429,440],{"class":258},[144,5431,5432],{"class":158},"\"  Retry ",[144,5434,684],{"class":154},[144,5436,5437],{"class":262},"attempt ",[144,5439,5408],{"class":258},[144,5441,5442],{"class":154}," 1}",[144,5444,1226],{"class":158},[144,5446,684],{"class":154},[144,5448,5449],{"class":262},"max_retries",[144,5451,690],{"class":154},[144,5453,5454],{"class":158}," \"\n",[144,5456,5457,5460,5463,5465,5468,5471,5473,5476,5478,5481,5483,5485,5487,5489,5491,5494],{"class":146,"line":964},[144,5458,5459],{"class":258},"                      f",[144,5461,5462],{"class":158},"\"after ",[144,5464,684],{"class":154},[144,5466,5467],{"class":262},"wait",[144,5469,5470],{"class":258},":.1f",[144,5472,690],{"class":154},[144,5474,5475],{"class":158},"s (",[144,5477,684],{"class":154},[144,5479,5480],{"class":262},"error_type",[144,5482,690],{"class":154},[144,5484,437],{"class":158},[144,5486,684],{"class":154},[144,5488,5343],{"class":262},[144,5490,690],{"class":154},[144,5492,5493],{"class":158},")\"",[144,5495,406],{"class":262},[144,5497,5498],{"class":146,"line":970},[144,5499,5500],{"class":262},"                time.sleep(wait)\n",[144,5502,5503],{"class":146,"line":976},[144,5504,5505],{"class":258},"                continue\n",[144,5507,5508],{"class":146,"line":988},[144,5509,376],{"emptyLinePlaceholder":57},[144,5511,5512,5515],{"class":146,"line":993},[144,5513,5514],{"class":258},"        except",[144,5516,5517],{"class":262}," requests.exceptions.Timeout:\n",[144,5519,5521],{"class":146,"line":5520},48,[144,5522,5523],{"class":173},"            # 服务器在 30 秒内没有响应\n",[144,5525,5527,5530,5532,5534,5536,5538,5540,5542,5544,5546,5548,5550],{"class":146,"line":5526},49,[144,5528,5529],{"class":262},"            wait ",[144,5531,266],{"class":258},[144,5533,1218],{"class":262},[144,5535,5399],{"class":154},[144,5537,5402],{"class":258},[144,5539,5405],{"class":262},[144,5541,5408],{"class":258},[144,5543,5411],{"class":262},[144,5545,782],{"class":154},[144,5547,400],{"class":262},[144,5549,5418],{"class":154},[144,5551,406],{"class":262},[144,5553,5555,5558,5560,5562,5565,5567,5569,5571,5573,5575,5577,5579,5581],{"class":146,"line":5554},50,[144,5556,5557],{"class":154},"            print",[144,5559,793],{"class":262},[144,5561,440],{"class":258},[144,5563,5564],{"class":158},"\"  Timeout. Retry ",[144,5566,684],{"class":154},[144,5568,5437],{"class":262},[144,5570,5408],{"class":258},[144,5572,5442],{"class":154},[144,5574,1226],{"class":158},[144,5576,684],{"class":154},[144,5578,5449],{"class":262},[144,5580,690],{"class":154},[144,5582,5454],{"class":158},[144,5584,5586,5589,5591,5593,5595,5597,5599,5601],{"class":146,"line":5585},51,[144,5587,5588],{"class":258},"                  f",[144,5590,5462],{"class":158},[144,5592,684],{"class":154},[144,5594,5467],{"class":262},[144,5596,5470],{"class":258},[144,5598,690],{"class":154},[144,5600,1024],{"class":158},[144,5602,406],{"class":262},[144,5604,5606],{"class":146,"line":5605},52,[144,5607,5608],{"class":262},"            time.sleep(wait)\n",[144,5610,5612],{"class":146,"line":5611},53,[144,5613,5614],{"class":258},"            continue\n",[144,5616,5618],{"class":146,"line":5617},54,[144,5619,376],{"emptyLinePlaceholder":57},[144,5621,5623,5625,5628,5630],{"class":146,"line":5622},55,[144,5624,5514],{"class":258},[144,5626,5627],{"class":262}," requests.exceptions.ConnectionError ",[144,5629,1166],{"class":258},[144,5631,5632],{"class":262}," e:\n",[144,5634,5636],{"class":146,"line":5635},56,[144,5637,5638],{"class":173},"            # DNS 解析失败、连接被拒等\n",[144,5640,5642,5644,5646,5648,5650,5652,5654,5656,5658,5660,5662,5664],{"class":146,"line":5641},57,[144,5643,5529],{"class":262},[144,5645,266],{"class":258},[144,5647,1218],{"class":262},[144,5649,5399],{"class":154},[144,5651,5402],{"class":258},[144,5653,5405],{"class":262},[144,5655,5408],{"class":258},[144,5657,5411],{"class":262},[144,5659,782],{"class":154},[144,5661,400],{"class":262},[144,5663,5418],{"class":154},[144,5665,406],{"class":262},[144,5667,5669,5671,5673,5675,5678,5680,5683,5685,5688,5690,5692,5694,5696,5698,5700,5702,5704],{"class":146,"line":5668},58,[144,5670,5557],{"class":154},[144,5672,793],{"class":262},[144,5674,440],{"class":258},[144,5676,5677],{"class":158},"\"  Connection error: ",[144,5679,684],{"class":154},[144,5681,5682],{"class":262},"e",[144,5684,690],{"class":154},[144,5686,5687],{"class":158},". Retry ",[144,5689,684],{"class":154},[144,5691,5437],{"class":262},[144,5693,5408],{"class":258},[144,5695,5442],{"class":154},[144,5697,1226],{"class":158},[144,5699,684],{"class":154},[144,5701,5449],{"class":262},[144,5703,690],{"class":154},[144,5705,5454],{"class":158},[144,5707,5709,5711,5713,5715,5717,5719,5721,5723],{"class":146,"line":5708},59,[144,5710,5588],{"class":258},[144,5712,5462],{"class":158},[144,5714,684],{"class":154},[144,5716,5467],{"class":262},[144,5718,5470],{"class":258},[144,5720,690],{"class":154},[144,5722,1024],{"class":158},[144,5724,406],{"class":262},[144,5726,5728],{"class":146,"line":5727},60,[144,5729,5608],{"class":262},[144,5731,5733],{"class":146,"line":5732},61,[144,5734,5614],{"class":258},[144,5736,5738],{"class":146,"line":5737},62,[144,5739,376],{"emptyLinePlaceholder":57},[144,5741,5743,5745,5747,5749,5751,5754,5756,5758,5760,5763],{"class":146,"line":5742},63,[144,5744,996],{"class":258},[144,5746,908],{"class":154},[144,5748,793],{"class":262},[144,5750,440],{"class":258},[144,5752,5753],{"class":158},"\"Failed after ",[144,5755,684],{"class":154},[144,5757,5449],{"class":262},[144,5759,690],{"class":154},[144,5761,5762],{"class":158}," retries\"",[144,5764,406],{"class":262},[10,5766,5767],{},"这段代码处理了：",[95,5769,5770,5776,5782,5788,5794],{},[72,5771,5772,5775],{},[22,5773,5774],{},"限流（429）"," — 指数退避 + 随机抖动避免多个客户端同步重试",[72,5777,5778,5781],{},[22,5779,5780],{},"服务器错误（500/502/503）"," — 自动重试，间隔递增",[72,5783,5784,5787],{},[22,5785,5786],{},"超时"," — 30 秒超时防止在无响应的服务器上挂起",[72,5789,5790,5793],{},[22,5791,5792],{},"连接中断"," — DNS 解析失败、连接被拒、网络波动",[72,5795,5796,5799],{},[22,5797,5798],{},"客户端错误（400/401/402/404/413/422）"," — 立即失败，因为重试不会修复错误的输入",[17,5801,5802],{},[10,5803,5804,5806],{},[22,5805,132],{}," 对于生产系统，建议记录失败请求的完整 payload 和错误响应。凌晨 3 点出问题时，这会让调试轻松很多。",[91,5808,5809],{"id":5809},"调用前校验输入",[10,5811,5812],{},"在发送 API 请求前先在本地捕获明显的错误，节省额度和时间：",[135,5814,5816],{"className":338,"code":5815,"language":340,"meta":140,"style":140},"def validate_payload(payload):\n    \"\"\"\n    在发送到 API 之前校验生成 payload。\n    捕获会导致 400 错误的常见错误。\n    \"\"\"\n    errors = []\n    \n    # 必填字段\n    if not payload.get(\"model\"):\n        errors.append(\"'model' is required\")\n    if not payload.get(\"prompt\") or not payload[\"prompt\"].strip():\n        errors.append(\"'prompt' is required and cannot be empty\")\n    \n    # 时长范围\n    duration = payload.get(\"duration\", 5)\n    if duration \u003C 4 or duration > 15:\n        errors.append(f\"'duration' must be 4-15, got {duration}\")\n    \n    # 质量值\n    valid_qualities = {\"480p\", \"720p\", \"1080p\"}\n    quality = payload.get(\"quality\", \"720p\")\n    if quality not in valid_qualities:\n        errors.append(f\"'quality' must be one of {valid_qualities}, got '{quality}'\")\n    \n    # 宽高比\n    valid_ratios = {\"16:9\", \"9:16\", \"1:1\", \"4:3\", \"3:4\", \"21:9\"}\n    ratio = payload.get(\"aspect_ratio\", \"16:9\")\n    if ratio not in valid_ratios:\n        errors.append(f\"'aspect_ratio' must be one of {valid_ratios}, got '{ratio}'\")\n    \n    # 图片 URL 校验\n    image_urls = payload.get(\"image_urls\", [])\n    if len(image_urls) > 9:\n        errors.append(f\"Maximum 9 images allowed, got {len(image_urls)}\")\n    for i, url in enumerate(image_urls):\n        if not url.startswith((\"http://\", \"https://\")):\n            errors.append(f\"image_urls[{i}] must be an HTTP(S) URL\")\n    \n    if errors:\n        raise ValueError(f\"Payload validation failed:\\n\" + \"\\n\".join(f\"  - {e}\" for e in errors))\n    \n    return True\n",[27,5817,5818,5828,5832,5837,5842,5846,5856,5860,5865,5880,5890,5916,5925,5929,5934,5952,5977,5996,6000,6005,6027,6045,6061,6091,6095,6100,6136,6154,6168,6197,6201,6206,6221,6238,6259,6274,6294,6316,6320,6327,6382,6386],{"__ignoreMap":140},[144,5819,5820,5822,5825],{"class":146,"line":147},[144,5821,525],{"class":258},[144,5823,5824],{"class":150}," validate_payload",[144,5826,5827],{"class":262},"(payload):\n",[144,5829,5830],{"class":146,"line":165},[144,5831,552],{"class":158},[144,5833,5834],{"class":146,"line":177},[144,5835,5836],{"class":158},"    在发送到 API 之前校验生成 payload。\n",[144,5838,5839],{"class":146,"line":186},[144,5840,5841],{"class":158},"    捕获会导致 400 错误的常见错误。\n",[144,5843,5844],{"class":146,"line":373},[144,5845,552],{"class":158},[144,5847,5848,5851,5853],{"class":146,"line":379},[144,5849,5850],{"class":262},"    errors ",[144,5852,266],{"class":258},[144,5854,5855],{"class":262}," []\n",[144,5857,5858],{"class":146,"line":385},[144,5859,562],{"class":262},[144,5861,5862],{"class":146,"line":409},[144,5863,5864],{"class":173},"    # 必填字段\n",[144,5866,5867,5869,5872,5875,5878],{"class":146,"line":420},[144,5868,2500],{"class":258},[144,5870,5871],{"class":258}," not",[144,5873,5874],{"class":262}," payload.get(",[144,5876,5877],{"class":158},"\"model\"",[144,5879,547],{"class":262},[144,5881,5882,5885,5888],{"class":146,"line":431},[144,5883,5884],{"class":262},"        errors.append(",[144,5886,5887],{"class":158},"\"'model' is required\"",[144,5889,406],{"class":262},[144,5891,5892,5894,5896,5898,5901,5903,5906,5908,5911,5913],{"class":146,"line":455},[144,5893,2500],{"class":258},[144,5895,5871],{"class":258},[144,5897,5874],{"class":262},[144,5899,5900],{"class":158},"\"prompt\"",[144,5902,1163],{"class":262},[144,5904,5905],{"class":258},"or",[144,5907,5871],{"class":258},[144,5909,5910],{"class":262}," payload[",[144,5912,5900],{"class":158},[144,5914,5915],{"class":262},"].strip():\n",[144,5917,5918,5920,5923],{"class":146,"line":466},[144,5919,5884],{"class":262},[144,5921,5922],{"class":158},"\"'prompt' is required and cannot be empty\"",[144,5924,406],{"class":262},[144,5926,5927],{"class":146,"line":599},[144,5928,562],{"class":262},[144,5930,5931],{"class":146,"line":604},[144,5932,5933],{"class":173},"    # 时长范围\n",[144,5935,5936,5939,5941,5943,5946,5948,5950],{"class":146,"line":610},[144,5937,5938],{"class":262},"    duration ",[144,5940,266],{"class":258},[144,5942,5874],{"class":262},[144,5944,5945],{"class":158},"\"duration\"",[144,5947,400],{"class":262},[144,5949,1352],{"class":154},[144,5951,406],{"class":262},[144,5953,5954,5956,5959,5961,5964,5967,5969,5972,5975],{"class":146,"line":616},[144,5955,2500],{"class":258},[144,5957,5958],{"class":262}," duration ",[144,5960,647],{"class":258},[144,5962,5963],{"class":154}," 4",[144,5965,5966],{"class":258}," or",[144,5968,5958],{"class":262},[144,5970,5971],{"class":258},">",[144,5973,5974],{"class":154}," 15",[144,5976,859],{"class":262},[144,5978,5979,5981,5983,5986,5988,5990,5992,5994],{"class":146,"line":622},[144,5980,5884],{"class":262},[144,5982,440],{"class":258},[144,5984,5985],{"class":158},"\"'duration' must be 4-15, got ",[144,5987,684],{"class":154},[144,5989,1773],{"class":262},[144,5991,690],{"class":154},[144,5993,449],{"class":158},[144,5995,406],{"class":262},[144,5997,5998],{"class":146,"line":627},[144,5999,562],{"class":262},[144,6001,6002],{"class":146,"line":638},[144,6003,6004],{"class":173},"    # 质量值\n",[144,6006,6007,6010,6012,6015,6017,6019,6021,6023,6025],{"class":146,"line":653},[144,6008,6009],{"class":262},"    valid_qualities ",[144,6011,266],{"class":258},[144,6013,6014],{"class":262}," {",[144,6016,4005],{"class":158},[144,6018,400],{"class":262},[144,6020,1368],{"class":158},[144,6022,400],{"class":262},[144,6024,3702],{"class":158},[144,6026,469],{"class":262},[144,6028,6029,6032,6034,6036,6039,6041,6043],{"class":146,"line":659},[144,6030,6031],{"class":262},"    quality ",[144,6033,266],{"class":258},[144,6035,5874],{"class":262},[144,6037,6038],{"class":158},"\"quality\"",[144,6040,400],{"class":262},[144,6042,1368],{"class":158},[144,6044,406],{"class":262},[144,6046,6047,6049,6052,6055,6058],{"class":146,"line":670},[144,6048,2500],{"class":258},[144,6050,6051],{"class":262}," quality ",[144,6053,6054],{"class":258},"not",[144,6056,6057],{"class":258}," in",[144,6059,6060],{"class":262}," valid_qualities:\n",[144,6062,6063,6065,6067,6070,6072,6075,6077,6080,6082,6084,6086,6089],{"class":146,"line":697},[144,6064,5884],{"class":262},[144,6066,440],{"class":258},[144,6068,6069],{"class":158},"\"'quality' must be one of ",[144,6071,684],{"class":154},[144,6073,6074],{"class":262},"valid_qualities",[144,6076,690],{"class":154},[144,6078,6079],{"class":158},", got '",[144,6081,684],{"class":154},[144,6083,1781],{"class":262},[144,6085,690],{"class":154},[144,6087,6088],{"class":158},"'\"",[144,6090,406],{"class":262},[144,6092,6093],{"class":146,"line":709},[144,6094,562],{"class":262},[144,6096,6097],{"class":146,"line":715},[144,6098,6099],{"class":173},"    # 宽高比\n",[144,6101,6102,6105,6107,6109,6111,6113,6115,6117,6120,6122,6125,6127,6130,6132,6134],{"class":146,"line":721},[144,6103,6104],{"class":262},"    valid_ratios ",[144,6106,266],{"class":258},[144,6108,6014],{"class":262},[144,6110,1384],{"class":158},[144,6112,400],{"class":262},[144,6114,3714],{"class":158},[144,6116,400],{"class":262},[144,6118,6119],{"class":158},"\"1:1\"",[144,6121,400],{"class":262},[144,6123,6124],{"class":158},"\"4:3\"",[144,6126,400],{"class":262},[144,6128,6129],{"class":158},"\"3:4\"",[144,6131,400],{"class":262},[144,6133,3825],{"class":158},[144,6135,469],{"class":262},[144,6137,6138,6141,6143,6145,6148,6150,6152],{"class":146,"line":727},[144,6139,6140],{"class":262},"    ratio ",[144,6142,266],{"class":258},[144,6144,5874],{"class":262},[144,6146,6147],{"class":158},"\"aspect_ratio\"",[144,6149,400],{"class":262},[144,6151,1384],{"class":158},[144,6153,406],{"class":262},[144,6155,6156,6158,6161,6163,6165],{"class":146,"line":738},[144,6157,2500],{"class":258},[144,6159,6160],{"class":262}," ratio ",[144,6162,6054],{"class":258},[144,6164,6057],{"class":258},[144,6166,6167],{"class":262}," valid_ratios:\n",[144,6169,6170,6172,6174,6177,6179,6182,6184,6186,6188,6191,6193,6195],{"class":146,"line":743},[144,6171,5884],{"class":262},[144,6173,440],{"class":258},[144,6175,6176],{"class":158},"\"'aspect_ratio' must be one of ",[144,6178,684],{"class":154},[144,6180,6181],{"class":262},"valid_ratios",[144,6183,690],{"class":154},[144,6185,6079],{"class":158},[144,6187,684],{"class":154},[144,6189,6190],{"class":262},"ratio",[144,6192,690],{"class":154},[144,6194,6088],{"class":158},[144,6196,406],{"class":262},[144,6198,6199],{"class":146,"line":749},[144,6200,562],{"class":262},[144,6202,6203],{"class":146,"line":766},[144,6204,6205],{"class":173},"    # 图片 URL 校验\n",[144,6207,6208,6211,6213,6215,6218],{"class":146,"line":787},[144,6209,6210],{"class":262},"    image_urls ",[144,6212,266],{"class":258},[144,6214,5874],{"class":262},[144,6216,6217],{"class":158},"\"image_urls\"",[144,6219,6220],{"class":262},", [])\n",[144,6222,6223,6225,6228,6231,6233,6236],{"class":146,"line":833},[144,6224,2500],{"class":258},[144,6226,6227],{"class":154}," len",[144,6229,6230],{"class":262},"(image_urls) ",[144,6232,5971],{"class":258},[144,6234,6235],{"class":154}," 9",[144,6237,859],{"class":262},[144,6239,6240,6242,6244,6247,6250,6253,6255,6257],{"class":146,"line":838},[144,6241,5884],{"class":262},[144,6243,440],{"class":258},[144,6245,6246],{"class":158},"\"Maximum 9 images allowed, got ",[144,6248,6249],{"class":154},"{len",[144,6251,6252],{"class":262},"(image_urls)",[144,6254,690],{"class":154},[144,6256,449],{"class":158},[144,6258,406],{"class":262},[144,6260,6261,6263,6266,6268,6271],{"class":146,"line":844},[144,6262,5100],{"class":258},[144,6264,6265],{"class":262}," i, url ",[144,6267,1180],{"class":258},[144,6269,6270],{"class":154}," enumerate",[144,6272,6273],{"class":262},"(image_urls):\n",[144,6275,6276,6278,6280,6283,6286,6288,6291],{"class":146,"line":862},[144,6277,847],{"class":258},[144,6279,5871],{"class":258},[144,6281,6282],{"class":262}," url.startswith((",[144,6284,6285],{"class":158},"\"http://\"",[144,6287,400],{"class":262},[144,6289,6290],{"class":158},"\"https://\"",[144,6292,6293],{"class":262},")):\n",[144,6295,6296,6299,6301,6304,6306,6309,6311,6314],{"class":146,"line":871},[144,6297,6298],{"class":262},"            errors.append(",[144,6300,440],{"class":258},[144,6302,6303],{"class":158},"\"image_urls[",[144,6305,684],{"class":154},[144,6307,6308],{"class":262},"i",[144,6310,690],{"class":154},[144,6312,6313],{"class":158},"] must be an HTTP(S) URL\"",[144,6315,406],{"class":262},[144,6317,6318],{"class":146,"line":886},[144,6319,562],{"class":262},[144,6321,6322,6324],{"class":146,"line":902},[144,6323,2500],{"class":258},[144,6325,6326],{"class":262}," errors:\n",[144,6328,6329,6332,6334,6336,6338,6341,6343,6345,6348,6351,6353,6355,6358,6360,6363,6365,6367,6369,6371,6374,6377,6379],{"class":146,"line":914},[144,6330,6331],{"class":258},"        raise",[144,6333,5319],{"class":154},[144,6335,793],{"class":262},[144,6337,440],{"class":258},[144,6339,6340],{"class":158},"\"Payload validation failed:",[144,6342,1670],{"class":154},[144,6344,449],{"class":158},[144,6346,6347],{"class":258}," +",[144,6349,6350],{"class":158}," \"",[144,6352,1670],{"class":154},[144,6354,449],{"class":158},[144,6356,6357],{"class":262},".join(",[144,6359,440],{"class":258},[144,6361,6362],{"class":158},"\"  - ",[144,6364,684],{"class":154},[144,6366,5682],{"class":262},[144,6368,690],{"class":154},[144,6370,449],{"class":158},[144,6372,6373],{"class":258}," for",[144,6375,6376],{"class":262}," e ",[144,6378,1180],{"class":258},[144,6380,6381],{"class":262}," errors))\n",[144,6383,6384],{"class":146,"line":953},[144,6385,562],{"class":262},[144,6387,6388,6390],{"class":146,"line":959},[144,6389,1703],{"class":258},[144,6391,6392],{"class":154}," True\n",[17,6394,6395],{},[10,6396,6397,6399,6400,6403],{},[22,6398,304],{}," 忘记对图片 URL 中的特殊字符进行 URL 编码。如果你的图片路径包含空格或非 ASCII 字符，用 ",[27,6401,6402],{},"urllib.parse.quote()"," 编码。",[44,6405],{},[47,6407,6409],{"id":6408},"配置-webhook跳过轮询","配置 Webhook（跳过轮询）",[10,6411,6412,6413,6416],{},"轮询对脚本和原型开发来说完全够用。对于生产系统，",[22,6414,6415],{},"Webhook"," 更高效——API 在视频就绪时主动推送结果到你的服务器。不浪费请求，完成与通知之间零延迟。",[91,6418,6419],{"id":6419},"工作原理",[10,6421,6422,6423,6425],{},"在生成请求中添加 ",[27,6424,3594],{},"：",[10,6427,6428],{},[2613,6429,6430],{},"使用前面第一个示例中相同的配置。",[135,6432,6434],{"className":338,"code":6433,"language":340,"meta":140,"style":140},"payload = {\n    \"model\": \"seedance-2.0\",\n    \"prompt\": \"A spaceship launches from a desert landscape at sunset.\",\n    \"duration\": 8,\n    \"quality\": \"720p\",\n    \"callback_url\": \"https://your-server.com/api/webhook/seedance\"\n}\n\nresponse = requests.post(\n    f\"{BASE_URL}/videos/generations\",\n    headers=HEADERS,\n    json=payload\n)\ntask = response.json()\nprint(f\"Task submitted: {task['id']}\")\n# 不需要轮询——你的 Webhook 会收到结果\n",[27,6435,6436,6444,6454,6465,6475,6485,6495,6499,6503,6512,6525,6536,6545,6549,6558,6584],{"__ignoreMap":140},[144,6437,6438,6440,6442],{"class":146,"line":147},[144,6439,3653],{"class":262},[144,6441,266],{"class":258},[144,6443,428],{"class":262},[144,6445,6446,6448,6450,6452],{"class":146,"line":165},[144,6447,3662],{"class":158},[144,6449,437],{"class":262},[144,6451,1308],{"class":158},[144,6453,452],{"class":262},[144,6455,6456,6458,6460,6463],{"class":146,"line":177},[144,6457,3673],{"class":158},[144,6459,437],{"class":262},[144,6461,6462],{"class":158},"\"A spaceship launches from a desert landscape at sunset.\"",[144,6464,452],{"class":262},[144,6466,6467,6469,6471,6473],{"class":146,"line":186},[144,6468,3685],{"class":158},[144,6470,437],{"class":262},[144,6472,3690],{"class":154},[144,6474,452],{"class":262},[144,6476,6477,6479,6481,6483],{"class":146,"line":373},[144,6478,3697],{"class":158},[144,6480,437],{"class":262},[144,6482,1368],{"class":158},[144,6484,452],{"class":262},[144,6486,6487,6490,6492],{"class":146,"line":379},[144,6488,6489],{"class":158},"    \"callback_url\"",[144,6491,437],{"class":262},[144,6493,6494],{"class":158},"\"https://your-server.com/api/webhook/seedance\"\n",[144,6496,6497],{"class":146,"line":385},[144,6498,469],{"class":262},[144,6500,6501],{"class":146,"line":409},[144,6502,376],{"emptyLinePlaceholder":57},[144,6504,6505,6508,6510],{"class":146,"line":420},[144,6506,6507],{"class":262},"response ",[144,6509,266],{"class":258},[144,6511,1432],{"class":262},[144,6513,6514,6517,6519,6521,6523],{"class":146,"line":431},[144,6515,6516],{"class":258},"    f",[144,6518,449],{"class":158},[144,6520,678],{"class":154},[144,6522,1444],{"class":158},[144,6524,452],{"class":262},[144,6526,6527,6530,6532,6534],{"class":146,"line":455},[144,6528,6529],{"class":700},"    headers",[144,6531,266],{"class":258},[144,6533,423],{"class":154},[144,6535,452],{"class":262},[144,6537,6538,6541,6543],{"class":146,"line":466},[144,6539,6540],{"class":700},"    json",[144,6542,266],{"class":258},[144,6544,2782],{"class":262},[144,6546,6547],{"class":146,"line":599},[144,6548,406],{"class":262},[144,6550,6551,6554,6556],{"class":146,"line":604},[144,6552,6553],{"class":262},"task ",[144,6555,266],{"class":258},[144,6557,735],{"class":262},[144,6559,6560,6563,6565,6567,6570,6572,6574,6576,6578,6580,6582],{"class":146,"line":610},[144,6561,6562],{"class":154},"print",[144,6564,793],{"class":262},[144,6566,440],{"class":258},[144,6568,6569],{"class":158},"\"Task submitted: ",[144,6571,684],{"class":154},[144,6573,1529],{"class":262},[144,6575,1532],{"class":158},[144,6577,1535],{"class":262},[144,6579,690],{"class":154},[144,6581,449],{"class":158},[144,6583,406],{"class":262},[144,6585,6586],{"class":146,"line":616},[144,6587,6588],{"class":173},"# 不需要轮询——你的 Webhook 会收到结果\n",[10,6590,6591,6592,6594],{},"视频就绪后，API 会向你的 ",[27,6593,3594],{}," 发送一个 POST 请求，携带完整的 task 对象——和你通过轮询拿到的内容完全一样。",[91,6596,6598],{"id":6597},"webhook-要求","Webhook 要求",[2035,6600,6601,6611],{},[2038,6602,6603],{},[2041,6604,6605,6608],{},[2044,6606,6607],{},"要求",[2044,6609,6610],{},"详情",[2051,6612,6613,6621,6629,6637,6645,6653],{},[2041,6614,6615,6618],{},[2056,6616,6617],{},"协议",[2056,6619,6620],{},"仅限 HTTPS（不支持 HTTP）——安全要求",[2041,6622,6623,6626],{},[2056,6624,6625],{},"响应",[2056,6627,6628],{},"10 秒内返回 2xx",[2041,6630,6631,6634],{},[2056,6632,6633],{},"重试",[2056,6635,6636],{},"失败时重试 3 次（间隔 1s、2s、4s）",[2041,6638,6639,6642],{},[2056,6640,6641],{},"URL 长度",[2056,6643,6644],{},"≤ 2048 字符",[2041,6646,6647,6650],{},[2056,6648,6649],{},"网络",[2056,6651,6652],{},"不支持内网/私有 IP（localhost、10.x.x.x、192.168.x.x）",[2041,6654,6655,6658],{},[2056,6656,6657],{},"请求体",[2056,6659,6660],{},"JSON POST，携带完整 task 对象",[91,6662,6664],{"id":6663},"生产级-flask-webhook-接收器","生产级 Flask Webhook 接收器",[10,6666,6667],{},"以下是一个完整的 Webhook 服务器，使用 Flask 实现，包含校验、错误处理和异步视频下载：",[135,6669,6671],{"className":338,"code":6670,"language":340,"meta":140,"style":140},"# webhook_server.py\n\"\"\"\nSeedance Webhook 接收器——处理视频生成完成的回调。\n运行：pip install flask requests\n      python webhook_server.py\n\"\"\"\nfrom flask import Flask, request, jsonify\nimport json\nimport os\nimport threading\nimport requests as req  # 重命名以避免和 flask.request 冲突\n\napp = Flask(__name__)\n\n# 保存已完成视频的目录\nOUTPUT_DIR = os.getenv(\"VIDEO_OUTPUT_DIR\", \"./videos\")\nos.makedirs(OUTPUT_DIR, exist_ok=True)\n\n\ndef download_video_async(video_url, task_id):\n    \"\"\"在后台线程中下载视频，不阻塞 Webhook 响应。\"\"\"\n    try:\n        filename = os.path.join(OUTPUT_DIR, f\"{task_id}.mp4\")\n        print(f\"  Downloading {task_id} to {filename}...\")\n        resp = req.get(video_url, stream=True, timeout=120)\n        resp.raise_for_status()\n        with open(filename, \"wb\") as f:\n            for chunk in resp.iter_content(chunk_size=8192):\n                f.write(chunk)\n        size_mb = os.path.getsize(filename) / (1024 * 1024)\n        print(f\"  Saved: {filename} ({size_mb:.1f} MB)\")\n    except Exception as e:\n        print(f\"  Download failed for {task_id}: {e}\")\n\n\n@app.route(\"/api/webhook/seedance\", methods=[\"POST\"])\ndef handle_webhook():\n    \"\"\"\n    处理 Seedance 视频生成完成的 Webhook 回调。\n    \n    视频生成完成（成功或失败）时，\n    API 会发送一个携带完整 task 对象的 POST 请求。\n    \"\"\"\n    # 解析传入的 task 对象\n    task = request.json\n    if not task:\n        return jsonify({\"error\": \"Empty body\"}), 400\n    \n    task_id = task.get(\"id\", \"unknown\")\n    status = task.get(\"status\", \"unknown\")\n    model = task.get(\"model\", \"unknown\")\n\n    print(f\"\\n{'='*50}\")\n    print(f\"Webhook received: task={task_id}\")\n    print(f\"  Status: {status}\")\n    print(f\"  Model: {model}\")\n\n    if status == \"completed\":\n        # 从 results 中提取视频 URL\n        results = task.get(\"results\", [])\n        if results:\n            video_url = results[0]\n            print(f\"  Video URL: {video_url}\")\n            \n            # 在后台线程中下载，确保快速响应\n            thread = threading.Thread(\n                target=download_video_async,\n                args=(video_url, task_id)\n            )\n            thread.start()\n        else:\n            print(f\"  WARNING: Completed but no results array!\")\n\n    elif status == \"failed\":\n        error_info = task.get(\"error\", {})\n        print(f\"  FAILED: {json.dumps(error_info, indent=2)}\")\n        # TODO: 记录到你的错误追踪系统（Sentry 等）\n        # TODO: 可选择修改参数后重试生成\n\n    else:\n        print(f\"  Unexpected status: {status}\")\n        print(f\"  Full payload: {json.dumps(task, indent=2)}\")\n\n    # 始终快速返回 200——API 期望在 10 秒内收到响应\n    return jsonify({\"received\": True, \"task_id\": task_id}), 200\n\n\n@app.route(\"/health\", methods=[\"GET\"])\ndef health_check():\n    \"\"\"负载均衡器的健康检查端点。\"\"\"\n    return jsonify({\"status\": \"ok\"}), 200\n\n\nif __name__ == \"__main__\":\n    print(f\"Starting webhook server...\")\n    print(f\"Videos will be saved to: {os.path.abspath(OUTPUT_DIR)}\")\n    print(f\"Webhook URL: http://localhost:5000/api/webhook/seedance\")\n    app.run(host=\"0.0.0.0\", port=5000, debug=True)\n",[27,6672,6673,6678,6683,6688,6693,6698,6702,6715,6721,6727,6734,6749,6753,6768,6772,6777,6796,6814,6818,6822,6832,6837,6844,6873,6903,6930,6935,6952,6971,6976,7000,7033,7046,7075,7079,7083,7108,7117,7121,7126,7130,7135,7140,7144,7149,7158,7167,7188,7192,7210,7227,7244,7248,7274,7295,7316,7337,7341,7353,7358,7371,7378,7392,7413,7419,7425,7436,7447,7458,7463,7469,7477,7491,7496,7510,7524,7556,7568,7578,7583,7590,7612,7643,7648,7654,7679,7684,7689,7712,7722,7728,7746,7751,7756,7769,7783,7810,7824],{"__ignoreMap":140},[144,6674,6675],{"class":146,"line":147},[144,6676,6677],{"class":173},"# webhook_server.py\n",[144,6679,6680],{"class":146,"line":165},[144,6681,6682],{"class":158},"\"\"\"\n",[144,6684,6685],{"class":146,"line":177},[144,6686,6687],{"class":158},"Seedance Webhook 接收器——处理视频生成完成的回调。\n",[144,6689,6690],{"class":146,"line":186},[144,6691,6692],{"class":158},"运行：pip install flask requests\n",[144,6694,6695],{"class":146,"line":373},[144,6696,6697],{"class":158},"      python webhook_server.py\n",[144,6699,6700],{"class":146,"line":379},[144,6701,6682],{"class":158},[144,6703,6704,6707,6710,6712],{"class":146,"line":385},[144,6705,6706],{"class":258},"from",[144,6708,6709],{"class":262}," flask ",[144,6711,347],{"class":258},[144,6713,6714],{"class":262}," Flask, request, jsonify\n",[144,6716,6717,6719],{"class":146,"line":409},[144,6718,347],{"class":258},[144,6720,370],{"class":262},[144,6722,6723,6725],{"class":146,"line":420},[144,6724,347],{"class":258},[144,6726,363],{"class":262},[144,6728,6729,6731],{"class":146,"line":431},[144,6730,347],{"class":258},[144,6732,6733],{"class":262}," threading\n",[144,6735,6736,6738,6741,6743,6746],{"class":146,"line":455},[144,6737,347],{"class":258},[144,6739,6740],{"class":262}," requests ",[144,6742,1166],{"class":258},[144,6744,6745],{"class":262}," req  ",[144,6747,6748],{"class":173},"# 重命名以避免和 flask.request 冲突\n",[144,6750,6751],{"class":146,"line":466},[144,6752,376],{"emptyLinePlaceholder":57},[144,6754,6755,6758,6760,6763,6766],{"class":146,"line":599},[144,6756,6757],{"class":262},"app ",[144,6759,266],{"class":258},[144,6761,6762],{"class":262}," Flask(",[144,6764,6765],{"class":154},"__name__",[144,6767,406],{"class":262},[144,6769,6770],{"class":146,"line":604},[144,6771,376],{"emptyLinePlaceholder":57},[144,6773,6774],{"class":146,"line":610},[144,6775,6776],{"class":173},"# 保存已完成视频的目录\n",[144,6778,6779,6782,6784,6786,6789,6791,6794],{"class":146,"line":616},[144,6780,6781],{"class":154},"OUTPUT_DIR",[144,6783,391],{"class":258},[144,6785,394],{"class":262},[144,6787,6788],{"class":158},"\"VIDEO_OUTPUT_DIR\"",[144,6790,400],{"class":262},[144,6792,6793],{"class":158},"\"./videos\"",[144,6795,406],{"class":262},[144,6797,6798,6801,6803,6805,6808,6810,6812],{"class":146,"line":622},[144,6799,6800],{"class":262},"os.makedirs(",[144,6802,6781],{"class":154},[144,6804,400],{"class":262},[144,6806,6807],{"class":700},"exist_ok",[144,6809,266],{"class":258},[144,6811,1139],{"class":154},[144,6813,406],{"class":262},[144,6815,6816],{"class":146,"line":627},[144,6817,376],{"emptyLinePlaceholder":57},[144,6819,6820],{"class":146,"line":638},[144,6821,376],{"emptyLinePlaceholder":57},[144,6823,6824,6826,6829],{"class":146,"line":653},[144,6825,525],{"class":258},[144,6827,6828],{"class":150}," download_video_async",[144,6830,6831],{"class":262},"(video_url, task_id):\n",[144,6833,6834],{"class":146,"line":659},[144,6835,6836],{"class":158},"    \"\"\"在后台线程中下载视频，不阻塞 Webhook 响应。\"\"\"\n",[144,6838,6839,6842],{"class":146,"line":670},[144,6840,6841],{"class":258},"    try",[144,6843,859],{"class":262},[144,6845,6846,6849,6851,6854,6856,6858,6860,6862,6864,6866,6868,6871],{"class":146,"line":697},[144,6847,6848],{"class":262},"        filename ",[144,6850,266],{"class":258},[144,6852,6853],{"class":262}," os.path.join(",[144,6855,6781],{"class":154},[144,6857,400],{"class":262},[144,6859,440],{"class":258},[144,6861,449],{"class":158},[144,6863,684],{"class":154},[144,6865,687],{"class":262},[144,6867,690],{"class":154},[144,6869,6870],{"class":158},".mp4\"",[144,6872,406],{"class":262},[144,6874,6875,6877,6879,6881,6884,6886,6888,6890,6893,6895,6897,6899,6901],{"class":146,"line":709},[144,6876,790],{"class":154},[144,6878,793],{"class":262},[144,6880,440],{"class":258},[144,6882,6883],{"class":158},"\"  Downloading ",[144,6885,684],{"class":154},[144,6887,687],{"class":262},[144,6889,690],{"class":154},[144,6891,6892],{"class":158}," to ",[144,6894,684],{"class":154},[144,6896,1114],{"class":262},[144,6898,690],{"class":154},[144,6900,1119],{"class":158},[144,6902,406],{"class":262},[144,6904,6905,6908,6910,6913,6915,6917,6919,6921,6923,6925,6928],{"class":146,"line":715},[144,6906,6907],{"class":262},"        resp ",[144,6909,266],{"class":258},[144,6911,6912],{"class":262}," req.get(video_url, ",[144,6914,1134],{"class":700},[144,6916,266],{"class":258},[144,6918,1139],{"class":154},[144,6920,400],{"class":262},[144,6922,1019],{"class":700},[144,6924,266],{"class":258},[144,6926,6927],{"class":154},"120",[144,6929,406],{"class":262},[144,6931,6932],{"class":146,"line":721},[144,6933,6934],{"class":262},"        resp.raise_for_status()\n",[144,6936,6937,6940,6942,6944,6946,6948,6950],{"class":146,"line":727},[144,6938,6939],{"class":258},"        with",[144,6941,1154],{"class":154},[144,6943,1157],{"class":262},[144,6945,1160],{"class":158},[144,6947,1163],{"class":262},[144,6949,1166],{"class":258},[144,6951,1169],{"class":262},[144,6953,6954,6957,6959,6961,6963,6965,6967,6969],{"class":146,"line":738},[144,6955,6956],{"class":258},"            for",[144,6958,1177],{"class":262},[144,6960,1180],{"class":258},[144,6962,1183],{"class":262},[144,6964,1186],{"class":700},[144,6966,266],{"class":258},[144,6968,1191],{"class":154},[144,6970,547],{"class":262},[144,6972,6973],{"class":146,"line":743},[144,6974,6975],{"class":262},"                f.write(chunk)\n",[144,6977,6978,6981,6983,6986,6988,6990,6993,6996,6998],{"class":146,"line":749},[144,6979,6980],{"class":262},"        size_mb ",[144,6982,266],{"class":258},[144,6984,6985],{"class":262}," os.path.getsize(filename) ",[144,6987,1226],{"class":258},[144,6989,1218],{"class":262},[144,6991,6992],{"class":154},"1024",[144,6994,6995],{"class":258}," *",[144,6997,1229],{"class":154},[144,6999,406],{"class":262},[144,7001,7002,7004,7006,7008,7011,7013,7015,7017,7019,7021,7024,7026,7028,7031],{"class":146,"line":766},[144,7003,790],{"class":154},[144,7005,793],{"class":262},[144,7007,440],{"class":258},[144,7009,7010],{"class":158},"\"  Saved: ",[144,7012,684],{"class":154},[144,7014,1114],{"class":262},[144,7016,690],{"class":154},[144,7018,1218],{"class":158},[144,7020,684],{"class":154},[144,7022,7023],{"class":262},"size_mb",[144,7025,5470],{"class":258},[144,7027,690],{"class":154},[144,7029,7030],{"class":158}," MB)\"",[144,7032,406],{"class":262},[144,7034,7035,7038,7041,7044],{"class":146,"line":787},[144,7036,7037],{"class":258},"    except",[144,7039,7040],{"class":154}," Exception",[144,7042,7043],{"class":258}," as",[144,7045,5632],{"class":262},[144,7047,7048,7050,7052,7054,7057,7059,7061,7063,7065,7067,7069,7071,7073],{"class":146,"line":833},[144,7049,790],{"class":154},[144,7051,793],{"class":262},[144,7053,440],{"class":258},[144,7055,7056],{"class":158},"\"  Download failed for ",[144,7058,684],{"class":154},[144,7060,687],{"class":262},[144,7062,690],{"class":154},[144,7064,437],{"class":158},[144,7066,684],{"class":154},[144,7068,5682],{"class":262},[144,7070,690],{"class":154},[144,7072,449],{"class":158},[144,7074,406],{"class":262},[144,7076,7077],{"class":146,"line":838},[144,7078,376],{"emptyLinePlaceholder":57},[144,7080,7081],{"class":146,"line":844},[144,7082,376],{"emptyLinePlaceholder":57},[144,7084,7085,7088,7090,7093,7095,7098,7100,7103,7106],{"class":146,"line":862},[144,7086,7087],{"class":150},"@app.route",[144,7089,793],{"class":262},[144,7091,7092],{"class":158},"\"/api/webhook/seedance\"",[144,7094,400],{"class":262},[144,7096,7097],{"class":700},"methods",[144,7099,266],{"class":258},[144,7101,7102],{"class":262},"[",[144,7104,7105],{"class":158},"\"POST\"",[144,7107,1629],{"class":262},[144,7109,7110,7112,7115],{"class":146,"line":871},[144,7111,525],{"class":258},[144,7113,7114],{"class":150}," handle_webhook",[144,7116,1289],{"class":262},[144,7118,7119],{"class":146,"line":886},[144,7120,552],{"class":158},[144,7122,7123],{"class":146,"line":902},[144,7124,7125],{"class":158},"    处理 Seedance 视频生成完成的 Webhook 回调。\n",[144,7127,7128],{"class":146,"line":914},[144,7129,562],{"class":158},[144,7131,7132],{"class":146,"line":953},[144,7133,7134],{"class":158},"    视频生成完成（成功或失败）时，\n",[144,7136,7137],{"class":146,"line":959},[144,7138,7139],{"class":158},"    API 会发送一个携带完整 task 对象的 POST 请求。\n",[144,7141,7142],{"class":146,"line":964},[144,7143,552],{"class":158},[144,7145,7146],{"class":146,"line":970},[144,7147,7148],{"class":173},"    # 解析传入的 task 对象\n",[144,7150,7151,7153,7155],{"class":146,"line":976},[144,7152,1496],{"class":262},[144,7154,266],{"class":258},[144,7156,7157],{"class":262}," request.json\n",[144,7159,7160,7162,7164],{"class":146,"line":988},[144,7161,2500],{"class":258},[144,7163,5871],{"class":258},[144,7165,7166],{"class":262}," task:\n",[144,7168,7169,7172,7175,7177,7179,7182,7185],{"class":146,"line":993},[144,7170,7171],{"class":258},"        return",[144,7173,7174],{"class":262}," jsonify({",[144,7176,896],{"class":158},[144,7178,437],{"class":262},[144,7180,7181],{"class":158},"\"Empty body\"",[144,7183,7184],{"class":262},"}), ",[144,7186,7187],{"class":154},"400\n",[144,7189,7190],{"class":146,"line":5520},[144,7191,562],{"class":262},[144,7193,7194,7197,7199,7201,7203,7205,7208],{"class":146,"line":5526},[144,7195,7196],{"class":262},"    task_id ",[144,7198,266],{"class":258},[144,7200,774],{"class":262},[144,7202,1626],{"class":158},[144,7204,400],{"class":262},[144,7206,7207],{"class":158},"\"unknown\"",[144,7209,406],{"class":262},[144,7211,7212,7215,7217,7219,7221,7223,7225],{"class":146,"line":5554},[144,7213,7214],{"class":262},"    status ",[144,7216,266],{"class":258},[144,7218,774],{"class":262},[144,7220,760],{"class":158},[144,7222,400],{"class":262},[144,7224,7207],{"class":158},[144,7226,406],{"class":262},[144,7228,7229,7232,7234,7236,7238,7240,7242],{"class":146,"line":5585},[144,7230,7231],{"class":262},"    model ",[144,7233,266],{"class":258},[144,7235,774],{"class":262},[144,7237,5877],{"class":158},[144,7239,400],{"class":262},[144,7241,7207],{"class":158},[144,7243,406],{"class":262},[144,7245,7246],{"class":146,"line":5605},[144,7247,376],{"emptyLinePlaceholder":57},[144,7249,7250,7252,7254,7256,7258,7261,7264,7267,7270,7272],{"class":146,"line":5611},[144,7251,1102],{"class":154},[144,7253,793],{"class":262},[144,7255,440],{"class":258},[144,7257,449],{"class":158},[144,7259,7260],{"class":154},"\\n{",[144,7262,7263],{"class":158},"'='",[144,7265,7266],{"class":258},"*",[144,7268,7269],{"class":154},"50}",[144,7271,449],{"class":158},[144,7273,406],{"class":262},[144,7275,7276,7278,7280,7282,7285,7287,7289,7291,7293],{"class":146,"line":5617},[144,7277,1102],{"class":154},[144,7279,793],{"class":262},[144,7281,440],{"class":258},[144,7283,7284],{"class":158},"\"Webhook received: task=",[144,7286,684],{"class":154},[144,7288,687],{"class":262},[144,7290,690],{"class":154},[144,7292,449],{"class":158},[144,7294,406],{"class":262},[144,7296,7297,7299,7301,7303,7306,7308,7310,7312,7314],{"class":146,"line":5622},[144,7298,1102],{"class":154},[144,7300,793],{"class":262},[144,7302,440],{"class":258},[144,7304,7305],{"class":158},"\"  Status: ",[144,7307,684],{"class":154},[144,7309,813],{"class":262},[144,7311,690],{"class":154},[144,7313,449],{"class":158},[144,7315,406],{"class":262},[144,7317,7318,7320,7322,7324,7327,7329,7331,7333,7335],{"class":146,"line":5635},[144,7319,1102],{"class":154},[144,7321,793],{"class":262},[144,7323,440],{"class":258},[144,7325,7326],{"class":158},"\"  Model: ",[144,7328,684],{"class":154},[144,7330,1747],{"class":262},[144,7332,690],{"class":154},[144,7334,449],{"class":158},[144,7336,406],{"class":262},[144,7338,7339],{"class":146,"line":5641},[144,7340,376],{"emptyLinePlaceholder":57},[144,7342,7343,7345,7347,7349,7351],{"class":146,"line":5668},[144,7344,2500],{"class":258},[144,7346,850],{"class":262},[144,7348,853],{"class":258},[144,7350,856],{"class":158},[144,7352,859],{"class":262},[144,7354,7355],{"class":146,"line":5708},[144,7356,7357],{"class":173},"        # 从 results 中提取视频 URL\n",[144,7359,7360,7363,7365,7367,7369],{"class":146,"line":5727},[144,7361,7362],{"class":262},"        results ",[144,7364,266],{"class":258},[144,7366,774],{"class":262},[144,7368,1651],{"class":158},[144,7370,6220],{"class":262},[144,7372,7373,7375],{"class":146,"line":5732},[144,7374,847],{"class":258},[144,7376,7377],{"class":262}," results:\n",[144,7379,7380,7383,7385,7388,7390],{"class":146,"line":5737},[144,7381,7382],{"class":262},"            video_url ",[144,7384,266],{"class":258},[144,7386,7387],{"class":262}," results[",[144,7389,782],{"class":154},[144,7391,763],{"class":262},[144,7393,7394,7396,7398,7400,7403,7405,7407,7409,7411],{"class":146,"line":5742},[144,7395,5557],{"class":154},[144,7397,793],{"class":262},[144,7399,440],{"class":258},[144,7401,7402],{"class":158},"\"  Video URL: ",[144,7404,684],{"class":154},[144,7406,1678],{"class":262},[144,7408,690],{"class":154},[144,7410,449],{"class":158},[144,7412,406],{"class":262},[144,7414,7416],{"class":146,"line":7415},64,[144,7417,7418],{"class":262},"            \n",[144,7420,7422],{"class":146,"line":7421},65,[144,7423,7424],{"class":173},"            # 在后台线程中下载，确保快速响应\n",[144,7426,7428,7431,7433],{"class":146,"line":7427},66,[144,7429,7430],{"class":262},"            thread ",[144,7432,266],{"class":258},[144,7434,7435],{"class":262}," threading.Thread(\n",[144,7437,7439,7442,7444],{"class":146,"line":7438},67,[144,7440,7441],{"class":700},"                target",[144,7443,266],{"class":258},[144,7445,7446],{"class":262},"download_video_async,\n",[144,7448,7450,7453,7455],{"class":146,"line":7449},68,[144,7451,7452],{"class":700},"                args",[144,7454,266],{"class":258},[144,7456,7457],{"class":262},"(video_url, task_id)\n",[144,7459,7461],{"class":146,"line":7460},69,[144,7462,956],{"class":262},[144,7464,7466],{"class":146,"line":7465},70,[144,7467,7468],{"class":262},"            thread.start()\n",[144,7470,7472,7475],{"class":146,"line":7471},71,[144,7473,7474],{"class":258},"        else",[144,7476,859],{"class":262},[144,7478,7480,7482,7484,7486,7489],{"class":146,"line":7479},72,[144,7481,5557],{"class":154},[144,7483,793],{"class":262},[144,7485,440],{"class":258},[144,7487,7488],{"class":158},"\"  WARNING: Completed but no results array!\"",[144,7490,406],{"class":262},[144,7492,7494],{"class":146,"line":7493},73,[144,7495,376],{"emptyLinePlaceholder":57},[144,7497,7499,7502,7504,7506,7508],{"class":146,"line":7498},74,[144,7500,7501],{"class":258},"    elif",[144,7503,850],{"class":262},[144,7505,853],{"class":258},[144,7507,881],{"class":158},[144,7509,859],{"class":262},[144,7511,7513,7516,7518,7520,7522],{"class":146,"line":7512},75,[144,7514,7515],{"class":262},"        error_info ",[144,7517,266],{"class":258},[144,7519,774],{"class":262},[144,7521,896],{"class":158},[144,7523,899],{"class":262},[144,7525,7527,7529,7531,7533,7536,7538,7541,7544,7546,7548,7550,7552,7554],{"class":146,"line":7526},76,[144,7528,790],{"class":154},[144,7530,793],{"class":262},[144,7532,440],{"class":258},[144,7534,7535],{"class":158},"\"  FAILED: ",[144,7537,684],{"class":154},[144,7539,7540],{"class":262},"json.dumps(error_info, ",[144,7542,7543],{"class":700},"indent",[144,7545,266],{"class":258},[144,7547,5399],{"class":154},[144,7549,945],{"class":262},[144,7551,690],{"class":154},[144,7553,449],{"class":158},[144,7555,406],{"class":262},[144,7557,7559,7562,7565],{"class":146,"line":7558},77,[144,7560,7561],{"class":173},"        # ",[144,7563,7564],{"class":258},"TODO",[144,7566,7567],{"class":173},": 记录到你的错误追踪系统（Sentry 等）\n",[144,7569,7571,7573,7575],{"class":146,"line":7570},78,[144,7572,7561],{"class":173},[144,7574,7564],{"class":258},[144,7576,7577],{"class":173},": 可选择修改参数后重试生成\n",[144,7579,7581],{"class":146,"line":7580},79,[144,7582,376],{"emptyLinePlaceholder":57},[144,7584,7586,7588],{"class":146,"line":7585},80,[144,7587,2536],{"class":258},[144,7589,859],{"class":262},[144,7591,7593,7595,7597,7599,7602,7604,7606,7608,7610],{"class":146,"line":7592},81,[144,7594,790],{"class":154},[144,7596,793],{"class":262},[144,7598,440],{"class":258},[144,7600,7601],{"class":158},"\"  Unexpected status: ",[144,7603,684],{"class":154},[144,7605,813],{"class":262},[144,7607,690],{"class":154},[144,7609,449],{"class":158},[144,7611,406],{"class":262},[144,7613,7615,7617,7619,7621,7624,7626,7629,7631,7633,7635,7637,7639,7641],{"class":146,"line":7614},82,[144,7616,790],{"class":154},[144,7618,793],{"class":262},[144,7620,440],{"class":258},[144,7622,7623],{"class":158},"\"  Full payload: ",[144,7625,684],{"class":154},[144,7627,7628],{"class":262},"json.dumps(task, ",[144,7630,7543],{"class":700},[144,7632,266],{"class":258},[144,7634,5399],{"class":154},[144,7636,945],{"class":262},[144,7638,690],{"class":154},[144,7640,449],{"class":158},[144,7642,406],{"class":262},[144,7644,7646],{"class":146,"line":7645},83,[144,7647,376],{"emptyLinePlaceholder":57},[144,7649,7651],{"class":146,"line":7650},84,[144,7652,7653],{"class":173},"    # 始终快速返回 200——API 期望在 10 秒内收到响应\n",[144,7655,7657,7659,7661,7664,7666,7668,7670,7673,7676],{"class":146,"line":7656},85,[144,7658,1703],{"class":258},[144,7660,7174],{"class":262},[144,7662,7663],{"class":158},"\"received\"",[144,7665,437],{"class":262},[144,7667,1139],{"class":154},[144,7669,400],{"class":262},[144,7671,7672],{"class":158},"\"task_id\"",[144,7674,7675],{"class":262},": task_id}), ",[144,7677,7678],{"class":154},"200\n",[144,7680,7682],{"class":146,"line":7681},86,[144,7683,376],{"emptyLinePlaceholder":57},[144,7685,7687],{"class":146,"line":7686},87,[144,7688,376],{"emptyLinePlaceholder":57},[144,7690,7692,7694,7696,7699,7701,7703,7705,7707,7710],{"class":146,"line":7691},88,[144,7693,7087],{"class":150},[144,7695,793],{"class":262},[144,7697,7698],{"class":158},"\"/health\"",[144,7700,400],{"class":262},[144,7702,7097],{"class":700},[144,7704,266],{"class":258},[144,7706,7102],{"class":262},[144,7708,7709],{"class":158},"\"GET\"",[144,7711,1629],{"class":262},[144,7713,7715,7717,7720],{"class":146,"line":7714},89,[144,7716,525],{"class":258},[144,7718,7719],{"class":150}," health_check",[144,7721,1289],{"class":262},[144,7723,7725],{"class":146,"line":7724},90,[144,7726,7727],{"class":158},"    \"\"\"负载均衡器的健康检查端点。\"\"\"\n",[144,7729,7731,7733,7735,7737,7739,7742,7744],{"class":146,"line":7730},91,[144,7732,1703],{"class":258},[144,7734,7174],{"class":262},[144,7736,760],{"class":158},[144,7738,437],{"class":262},[144,7740,7741],{"class":158},"\"ok\"",[144,7743,7184],{"class":262},[144,7745,7678],{"class":154},[144,7747,7749],{"class":146,"line":7748},92,[144,7750,376],{"emptyLinePlaceholder":57},[144,7752,7754],{"class":146,"line":7753},93,[144,7755,376],{"emptyLinePlaceholder":57},[144,7757,7759,7761,7763,7765,7767],{"class":146,"line":7758},94,[144,7760,1719],{"class":258},[144,7762,1722],{"class":154},[144,7764,1725],{"class":258},[144,7766,1728],{"class":158},[144,7768,859],{"class":262},[144,7770,7772,7774,7776,7778,7781],{"class":146,"line":7771},95,[144,7773,1102],{"class":154},[144,7775,793],{"class":262},[144,7777,440],{"class":258},[144,7779,7780],{"class":158},"\"Starting webhook server...\"",[144,7782,406],{"class":262},[144,7784,7786,7788,7790,7792,7795,7797,7800,7802,7804,7806,7808],{"class":146,"line":7785},96,[144,7787,1102],{"class":154},[144,7789,793],{"class":262},[144,7791,440],{"class":258},[144,7793,7794],{"class":158},"\"Videos will be saved to: ",[144,7796,684],{"class":154},[144,7798,7799],{"class":262},"os.path.abspath(",[144,7801,6781],{"class":154},[144,7803,945],{"class":262},[144,7805,690],{"class":154},[144,7807,449],{"class":158},[144,7809,406],{"class":262},[144,7811,7813,7815,7817,7819,7822],{"class":146,"line":7812},97,[144,7814,1102],{"class":154},[144,7816,793],{"class":262},[144,7818,440],{"class":258},[144,7820,7821],{"class":158},"\"Webhook URL: http://localhost:5000/api/webhook/seedance\"",[144,7823,406],{"class":262},[144,7825,7827,7830,7833,7835,7838,7840,7843,7845,7848,7850,7853,7855,7857],{"class":146,"line":7826},98,[144,7828,7829],{"class":262},"    app.run(",[144,7831,7832],{"class":700},"host",[144,7834,266],{"class":258},[144,7836,7837],{"class":158},"\"0.0.0.0\"",[144,7839,400],{"class":262},[144,7841,7842],{"class":700},"port",[144,7844,266],{"class":258},[144,7846,7847],{"class":154},"5000",[144,7849,400],{"class":262},[144,7851,7852],{"class":700},"debug",[144,7854,266],{"class":258},[144,7856,1139],{"class":154},[144,7858,406],{"class":262},[10,7860,7861],{},"安装依赖并运行：",[135,7863,7865],{"className":137,"code":7864,"language":139,"meta":140,"style":140},"pip install flask requests\npython webhook_server.py\n",[27,7866,7867,7878],{"__ignoreMap":140},[144,7868,7869,7871,7873,7876],{"class":146,"line":147},[144,7870,189],{"class":150},[144,7872,192],{"class":158},[144,7874,7875],{"class":158}," flask",[144,7877,328],{"class":158},[144,7879,7880,7882],{"class":146,"line":165},[144,7881,340],{"class":150},[144,7883,7884],{"class":158}," webhook_server.py\n",[10,7886,7887],{},"这个服务器的几个关键设计决策：",[95,7889,7890,7900,7909],{},[72,7891,7892,7895,7896,7899],{},[22,7893,7894],{},"后台下载"," — 我们开一个线程下载视频，这样 Webhook 处理函数可以立即返回 ",[27,7897,7898],{},"200","。API 期望 10 秒内收到响应，但视频下载可能需要更久。",[72,7901,7902,1061,7905,7908],{},[22,7903,7904],{},"健康检查端点",[27,7906,7907],{},"/health"," 在部署到负载均衡器（ALB、nginx 等）后面时很有用。",[72,7910,7911,7914],{},[22,7912,7913],{},"错误日志"," — 失败的任务会打印完整的错误 payload。生产环境中，把这些接入 Sentry、Datadog 或你的日志系统。",[91,7916,7918],{"id":7917},"用-ngrok-暴露本地服务","用 ngrok 暴露本地服务",[10,7920,7921,7922,7927],{},"本地开发时，用 ",[36,7923,7926],{"href":7924,"rel":7925},"https://ngrok.com",[40],"ngrok"," 创建一个公开的 HTTPS URL，通过隧道转发到你的本地服务器：",[135,7929,7931],{"className":137,"code":7930,"language":139,"meta":140,"style":140},"# 安装 ngrok（macOS）\nbrew install ngrok\n\n# 或者从 https://ngrok.com/download 下载\n\n# 启动隧道\nngrok http 5000\n",[27,7932,7933,7938,7948,7952,7957,7961,7966],{"__ignoreMap":140},[144,7934,7935],{"class":146,"line":147},[144,7936,7937],{"class":173},"# 安装 ngrok（macOS）\n",[144,7939,7940,7943,7945],{"class":146,"line":165},[144,7941,7942],{"class":150},"brew",[144,7944,192],{"class":158},[144,7946,7947],{"class":158}," ngrok\n",[144,7949,7950],{"class":146,"line":177},[144,7951,376],{"emptyLinePlaceholder":57},[144,7953,7954],{"class":146,"line":186},[144,7955,7956],{"class":173},"# 或者从 https://ngrok.com/download 下载\n",[144,7958,7959],{"class":146,"line":373},[144,7960,376],{"emptyLinePlaceholder":57},[144,7962,7963],{"class":146,"line":379},[144,7964,7965],{"class":173},"# 启动隧道\n",[144,7967,7968,7970,7973],{"class":146,"line":385},[144,7969,7926],{"class":150},[144,7971,7972],{"class":158}," http",[144,7974,7975],{"class":154}," 5000\n",[10,7977,7978],{},"ngrok 输出类似：",[135,7980,7983],{"className":7981,"code":7982,"language":2182},[2180],"Forwarding  https://a1b2c3d4.ngrok-free.app → http://localhost:5000\n",[27,7984,7982],{"__ignoreMap":140},[10,7986,7987,7988,6425],{},"把这个 HTTPS URL 用作你的 ",[27,7989,3594],{},[135,7991,7993],{"className":338,"code":7992,"language":340,"meta":140,"style":140},"payload = {\n    \"model\": \"seedance-2.0\",\n    \"prompt\": \"Your prompt here\",\n    \"callback_url\": \"https://a1b2c3d4.ngrok-free.app/api/webhook/seedance\"\n}\n",[27,7994,7995,8003,8013,8024,8033],{"__ignoreMap":140},[144,7996,7997,7999,8001],{"class":146,"line":147},[144,7998,3653],{"class":262},[144,8000,266],{"class":258},[144,8002,428],{"class":262},[144,8004,8005,8007,8009,8011],{"class":146,"line":165},[144,8006,3662],{"class":158},[144,8008,437],{"class":262},[144,8010,1308],{"class":158},[144,8012,452],{"class":262},[144,8014,8015,8017,8019,8022],{"class":146,"line":177},[144,8016,3673],{"class":158},[144,8018,437],{"class":262},[144,8020,8021],{"class":158},"\"Your prompt here\"",[144,8023,452],{"class":262},[144,8025,8026,8028,8030],{"class":146,"line":186},[144,8027,6489],{"class":158},[144,8029,437],{"class":262},[144,8031,8032],{"class":158},"\"https://a1b2c3d4.ngrok-free.app/api/webhook/seedance\"\n",[144,8034,8035],{"class":146,"line":373},[144,8036,469],{"class":262},[17,8038,8039],{},[10,8040,8041,8043,8044,8047,8048,8051],{},[22,8042,304],{}," 使用 ngrok 的 ",[27,8045,8046],{},"http://"," URL 而不是 ",[27,8049,8050],{},"https://","。Seedance API 要求 Webhook 必须使用 HTTPS——使用纯 HTTP 的回调 URL 会返回 400 错误。",[91,8053,8055],{"id":8054},"webhook-安全","Webhook 安全",[10,8057,8058],{},"在生产环境中，验证 Webhook 请求确实来自 EvoLink API：",[135,8060,8062],{"className":338,"code":8061,"language":340,"meta":140,"style":140},"import hmac\nimport hashlib\n\ndef verify_webhook(request):\n    \"\"\"通过任务 ID 格式验证 Webhook 真实性。\"\"\"\n    task = request.json\n    task_id = task.get(\"id\", \"\")\n    \n    # EvoLink 的任务 ID 遵循特定格式\n    if not task_id.startswith(\"task-unified-\"):\n        return False\n    \n    # 额外验证：检查必填字段是否存在\n    required_fields = [\"id\", \"status\", \"model\", \"created\"]\n    if not all(field in task for field in required_fields):\n        return False\n    \n    return True\n",[27,8063,8064,8071,8078,8082,8092,8097,8105,8121,8125,8130,8144,8151,8155,8160,8187,8215,8221,8225],{"__ignoreMap":140},[144,8065,8066,8068],{"class":146,"line":147},[144,8067,347],{"class":258},[144,8069,8070],{"class":262}," hmac\n",[144,8072,8073,8075],{"class":146,"line":165},[144,8074,347],{"class":258},[144,8076,8077],{"class":262}," hashlib\n",[144,8079,8080],{"class":146,"line":177},[144,8081,376],{"emptyLinePlaceholder":57},[144,8083,8084,8086,8089],{"class":146,"line":186},[144,8085,525],{"class":258},[144,8087,8088],{"class":150}," verify_webhook",[144,8090,8091],{"class":262},"(request):\n",[144,8093,8094],{"class":146,"line":373},[144,8095,8096],{"class":158},"    \"\"\"通过任务 ID 格式验证 Webhook 真实性。\"\"\"\n",[144,8098,8099,8101,8103],{"class":146,"line":379},[144,8100,1496],{"class":262},[144,8102,266],{"class":258},[144,8104,7157],{"class":262},[144,8106,8107,8109,8111,8113,8115,8117,8119],{"class":146,"line":385},[144,8108,7196],{"class":262},[144,8110,266],{"class":258},[144,8112,774],{"class":262},[144,8114,1626],{"class":158},[144,8116,400],{"class":262},[144,8118,5247],{"class":158},[144,8120,406],{"class":262},[144,8122,8123],{"class":146,"line":409},[144,8124,562],{"class":262},[144,8126,8127],{"class":146,"line":420},[144,8128,8129],{"class":173},"    # EvoLink 的任务 ID 遵循特定格式\n",[144,8131,8132,8134,8136,8139,8142],{"class":146,"line":431},[144,8133,2500],{"class":258},[144,8135,5871],{"class":258},[144,8137,8138],{"class":262}," task_id.startswith(",[144,8140,8141],{"class":158},"\"task-unified-\"",[144,8143,547],{"class":262},[144,8145,8146,8148],{"class":146,"line":455},[144,8147,7171],{"class":258},[144,8149,8150],{"class":154}," False\n",[144,8152,8153],{"class":146,"line":466},[144,8154,562],{"class":262},[144,8156,8157],{"class":146,"line":599},[144,8158,8159],{"class":173},"    # 额外验证：检查必填字段是否存在\n",[144,8161,8162,8165,8167,8170,8172,8174,8176,8178,8180,8182,8185],{"class":146,"line":604},[144,8163,8164],{"class":262},"    required_fields ",[144,8166,266],{"class":258},[144,8168,8169],{"class":262}," [",[144,8171,1626],{"class":158},[144,8173,400],{"class":262},[144,8175,760],{"class":158},[144,8177,400],{"class":262},[144,8179,5877],{"class":158},[144,8181,400],{"class":262},[144,8183,8184],{"class":158},"\"created\"",[144,8186,763],{"class":262},[144,8188,8189,8191,8193,8196,8199,8201,8204,8207,8210,8212],{"class":146,"line":610},[144,8190,2500],{"class":258},[144,8192,5871],{"class":258},[144,8194,8195],{"class":154}," all",[144,8197,8198],{"class":262},"(field ",[144,8200,1180],{"class":258},[144,8202,8203],{"class":262}," task ",[144,8205,8206],{"class":258},"for",[144,8208,8209],{"class":262}," field ",[144,8211,1180],{"class":258},[144,8213,8214],{"class":262}," required_fields):\n",[144,8216,8217,8219],{"class":146,"line":616},[144,8218,7171],{"class":258},[144,8220,8150],{"class":154},[144,8222,8223],{"class":146,"line":622},[144,8224,562],{"class":262},[144,8226,8227,8229],{"class":146,"line":627},[144,8228,1703],{"class":258},[144,8230,6392],{"class":154},[91,8232,8234],{"id":8233},"webhook-vs-轮询怎么选","Webhook vs 轮询：怎么选？",[2035,8236,8237,8250],{},[2038,8238,8239],{},[2041,8240,8241,8244,8247],{},[2044,8242,8243],{},"场景",[2044,8245,8246],{},"推荐方案",[2044,8248,8249],{},"原因",[2051,8251,8252,8262,8272,8283,8293,8303],{},[2041,8253,8254,8257,8259],{},[2056,8255,8256],{},"快速原型 / 脚本",[2056,8258,2239],{},[2056,8260,8261],{},"更简单，不需要服务器",[2041,8263,8264,8267,8269],{},[2056,8265,8266],{},"生产级 Web 应用",[2056,8268,6415],{},[2056,8270,8271],{},"可扩展，不浪费请求",[2041,8273,8274,8277,8280],{},[2056,8275,8276],{},"批量处理（100+ 视频）",[2056,8278,8279],{},"Webhook + 消息队列",[2056,8281,8282],{},"全部提交，按完成顺序处理",[2041,8284,8285,8288,8290],{},[2056,8286,8287],{},"CLI 工具",[2056,8289,2239],{},[2056,8291,8292],{},"不需要服务器基础设施",[2041,8294,8295,8298,8300],{},[2056,8296,8297],{},"移动应用后端",[2056,8299,6415],{},[2056,8301,8302],{},"完成后推送通知给用户",[2041,8304,8305,8308,8310],{},[2056,8306,8307],{},"Serverless（Lambda/Cloud Functions）",[2056,8309,6415],{},[2056,8311,8312],{},"完美匹配——每次完成触发一个函数",[17,8314,8315],{},[10,8316,8317,8319],{},[22,8318,132],{}," 对于批量处理，把 Webhook 和消息队列（Redis、RabbitMQ、SQS）结合使用。提交所有生成请求，然后按它们到达队列的顺序处理。这种方式将提交和处理解耦，并且优雅地处理重试。",[44,8321],{},[47,8323,8325],{"id":8324},"批量处理生成多个视频","批量处理：生成多个视频",[10,8327,8328],{},"实际场景中经常需要生成大量视频。以下是带限流控制的批量处理模式：",[10,8330,8331],{},[2613,8332,8333],{},"使用前面第一个示例中相同的配置和辅助函数。",[135,8335,8337],{"className":338,"code":8336,"language":340,"meta":140,"style":140},"import concurrent.futures\n\ndef batch_generate(prompts, max_concurrent=3):\n    \"\"\"\n    带并发控制的批量视频生成。\n    \n    Args:\n        prompts: 提示词字符串列表。\n        max_concurrent: 最大同时生成数。\n    \n    Returns:\n        (prompt, result_or_error) 元组列表。\n    \"\"\"\n    results = []\n    \n    def generate_one(prompt, index):\n        \"\"\"生成单个视频并返回结果。\"\"\"\n        payload = {\n            \"model\": \"seedance-2.0\",\n            \"prompt\": prompt,\n            \"duration\": 5,\n            \"quality\": \"720p\"\n        }\n        try:\n            task = generate_video_with_retry(payload)\n            print(f\"[{index}] Submitted: {task['id']}\")\n            result = wait_for_video(task[\"id\"])\n            video_url = result[\"results\"][0]\n            download_video(video_url, f\"batch_{index}.mp4\")\n            return (prompt, result)\n        except Exception as e:\n            print(f\"[{index}] Failed: {e}\")\n            return (prompt, str(e))\n    \n    # 分批处理以遵守限流规则\n    with concurrent.futures.ThreadPoolExecutor(max_workers=max_concurrent) as executor:\n        futures = {\n            executor.submit(generate_one, prompt, i): i\n            for i, prompt in enumerate(prompts)\n        }\n        for future in concurrent.futures.as_completed(futures):\n            results.append(future.result())\n    \n    # 汇总\n    succeeded = sum(1 for _, r in results if isinstance(r, dict))\n    print(f\"\\nBatch complete: {succeeded}/{len(prompts)} succeeded\")\n    return results\n\n\n# 示例用法\nprompts = [\n    \"A hummingbird hovering near a red flower. Macro lens, shallow depth of field.\",\n    \"Ocean waves crashing on volcanic rocks at sunset. Slow motion.\",\n    \"A street musician playing violin in the rain. Cinematic lighting.\",\n]\nbatch_generate(prompts, max_concurrent=2)\n",[27,8338,8339,8346,8350,8366,8370,8375,8379,8383,8388,8393,8397,8401,8406,8410,8419,8423,8434,8439,8448,8459,8467,8478,8488,8493,8499,8509,8544,8557,8573,8593,8600,8610,8639,8652,8656,8661,8681,8690,8695,8709,8713,8725,8730,8734,8739,8777,8813,8820,8824,8828,8833,8843,8850,8857,8864,8868],{"__ignoreMap":140},[144,8340,8341,8343],{"class":146,"line":147},[144,8342,347],{"class":258},[144,8344,8345],{"class":262}," concurrent.futures\n",[144,8347,8348],{"class":146,"line":165},[144,8349,376],{"emptyLinePlaceholder":57},[144,8351,8352,8354,8357,8360,8362,8364],{"class":146,"line":177},[144,8353,525],{"class":258},[144,8355,8356],{"class":150}," batch_generate",[144,8358,8359],{"class":262},"(prompts, max_concurrent",[144,8361,266],{"class":258},[144,8363,5042],{"class":154},[144,8365,547],{"class":262},[144,8367,8368],{"class":146,"line":186},[144,8369,552],{"class":158},[144,8371,8372],{"class":146,"line":373},[144,8373,8374],{"class":158},"    带并发控制的批量视频生成。\n",[144,8376,8377],{"class":146,"line":379},[144,8378,562],{"class":158},[144,8380,8381],{"class":146,"line":385},[144,8382,567],{"class":158},[144,8384,8385],{"class":146,"line":409},[144,8386,8387],{"class":158},"        prompts: 提示词字符串列表。\n",[144,8389,8390],{"class":146,"line":420},[144,8391,8392],{"class":158},"        max_concurrent: 最大同时生成数。\n",[144,8394,8395],{"class":146,"line":431},[144,8396,562],{"class":158},[144,8398,8399],{"class":146,"line":455},[144,8400,591],{"class":158},[144,8402,8403],{"class":146,"line":466},[144,8404,8405],{"class":158},"        (prompt, result_or_error) 元组列表。\n",[144,8407,8408],{"class":146,"line":599},[144,8409,552],{"class":158},[144,8411,8412,8415,8417],{"class":146,"line":604},[144,8413,8414],{"class":262},"    results ",[144,8416,266],{"class":258},[144,8418,5855],{"class":262},[144,8420,8421],{"class":146,"line":610},[144,8422,562],{"class":262},[144,8424,8425,8428,8431],{"class":146,"line":616},[144,8426,8427],{"class":258},"    def",[144,8429,8430],{"class":150}," generate_one",[144,8432,8433],{"class":262},"(prompt, index):\n",[144,8435,8436],{"class":146,"line":622},[144,8437,8438],{"class":158},"        \"\"\"生成单个视频并返回结果。\"\"\"\n",[144,8440,8441,8444,8446],{"class":146,"line":627},[144,8442,8443],{"class":262},"        payload ",[144,8445,266],{"class":258},[144,8447,428],{"class":262},[144,8449,8450,8453,8455,8457],{"class":146,"line":638},[144,8451,8452],{"class":158},"            \"model\"",[144,8454,437],{"class":262},[144,8456,1308],{"class":158},[144,8458,452],{"class":262},[144,8460,8461,8464],{"class":146,"line":653},[144,8462,8463],{"class":158},"            \"prompt\"",[144,8465,8466],{"class":262},": prompt,\n",[144,8468,8469,8472,8474,8476],{"class":146,"line":659},[144,8470,8471],{"class":158},"            \"duration\"",[144,8473,437],{"class":262},[144,8475,1352],{"class":154},[144,8477,452],{"class":262},[144,8479,8480,8483,8485],{"class":146,"line":670},[144,8481,8482],{"class":158},"            \"quality\"",[144,8484,437],{"class":262},[144,8486,8487],{"class":158},"\"720p\"\n",[144,8489,8490],{"class":146,"line":697},[144,8491,8492],{"class":262},"        }\n",[144,8494,8495,8497],{"class":146,"line":709},[144,8496,5116],{"class":258},[144,8498,859],{"class":262},[144,8500,8501,8504,8506],{"class":146,"line":715},[144,8502,8503],{"class":262},"            task ",[144,8505,266],{"class":258},[144,8507,8508],{"class":262}," generate_video_with_retry(payload)\n",[144,8510,8511,8513,8515,8517,8520,8522,8525,8527,8530,8532,8534,8536,8538,8540,8542],{"class":146,"line":721},[144,8512,5557],{"class":154},[144,8514,793],{"class":262},[144,8516,440],{"class":258},[144,8518,8519],{"class":158},"\"[",[144,8521,684],{"class":154},[144,8523,8524],{"class":262},"index",[144,8526,690],{"class":154},[144,8528,8529],{"class":158},"] Submitted: ",[144,8531,684],{"class":154},[144,8533,1529],{"class":262},[144,8535,1532],{"class":158},[144,8537,1535],{"class":262},[144,8539,690],{"class":154},[144,8541,449],{"class":158},[144,8543,406],{"class":262},[144,8545,8546,8549,8551,8553,8555],{"class":146,"line":727},[144,8547,8548],{"class":262},"            result ",[144,8550,266],{"class":258},[144,8552,1623],{"class":262},[144,8554,1626],{"class":158},[144,8556,1629],{"class":262},[144,8558,8559,8561,8563,8565,8567,8569,8571],{"class":146,"line":738},[144,8560,7382],{"class":262},[144,8562,266],{"class":258},[144,8564,1648],{"class":262},[144,8566,1651],{"class":158},[144,8568,1562],{"class":262},[144,8570,782],{"class":154},[144,8572,763],{"class":262},[144,8574,8575,8578,8580,8583,8585,8587,8589,8591],{"class":146,"line":743},[144,8576,8577],{"class":262},"            download_video(video_url, ",[144,8579,440],{"class":258},[144,8581,8582],{"class":158},"\"batch_",[144,8584,684],{"class":154},[144,8586,8524],{"class":262},[144,8588,690],{"class":154},[144,8590,6870],{"class":158},[144,8592,406],{"class":262},[144,8594,8595,8597],{"class":146,"line":749},[144,8596,865],{"class":258},[144,8598,8599],{"class":262}," (prompt, result)\n",[144,8601,8602,8604,8606,8608],{"class":146,"line":766},[144,8603,5514],{"class":258},[144,8605,7040],{"class":154},[144,8607,7043],{"class":258},[144,8609,5632],{"class":262},[144,8611,8612,8614,8616,8618,8620,8622,8624,8626,8629,8631,8633,8635,8637],{"class":146,"line":787},[144,8613,5557],{"class":154},[144,8615,793],{"class":262},[144,8617,440],{"class":258},[144,8619,8519],{"class":158},[144,8621,684],{"class":154},[144,8623,8524],{"class":262},[144,8625,690],{"class":154},[144,8627,8628],{"class":158},"] Failed: ",[144,8630,684],{"class":154},[144,8632,5682],{"class":262},[144,8634,690],{"class":154},[144,8636,449],{"class":158},[144,8638,406],{"class":262},[144,8640,8641,8643,8646,8649],{"class":146,"line":833},[144,8642,865],{"class":258},[144,8644,8645],{"class":262}," (prompt, ",[144,8647,8648],{"class":154},"str",[144,8650,8651],{"class":262},"(e))\n",[144,8653,8654],{"class":146,"line":838},[144,8655,562],{"class":262},[144,8657,8658],{"class":146,"line":844},[144,8659,8660],{"class":173},"    # 分批处理以遵守限流规则\n",[144,8662,8663,8665,8668,8671,8673,8676,8678],{"class":146,"line":862},[144,8664,1151],{"class":258},[144,8666,8667],{"class":262}," concurrent.futures.ThreadPoolExecutor(",[144,8669,8670],{"class":700},"max_workers",[144,8672,266],{"class":258},[144,8674,8675],{"class":262},"max_concurrent) ",[144,8677,1166],{"class":258},[144,8679,8680],{"class":262}," executor:\n",[144,8682,8683,8686,8688],{"class":146,"line":871},[144,8684,8685],{"class":262},"        futures ",[144,8687,266],{"class":258},[144,8689,428],{"class":262},[144,8691,8692],{"class":146,"line":886},[144,8693,8694],{"class":262},"            executor.submit(generate_one, prompt, i): i\n",[144,8696,8697,8699,8702,8704,8706],{"class":146,"line":902},[144,8698,6956],{"class":258},[144,8700,8701],{"class":262}," i, prompt ",[144,8703,1180],{"class":258},[144,8705,6270],{"class":154},[144,8707,8708],{"class":262},"(prompts)\n",[144,8710,8711],{"class":146,"line":914},[144,8712,8492],{"class":262},[144,8714,8715,8717,8720,8722],{"class":146,"line":953},[144,8716,1174],{"class":258},[144,8718,8719],{"class":262}," future ",[144,8721,1180],{"class":258},[144,8723,8724],{"class":262}," concurrent.futures.as_completed(futures):\n",[144,8726,8727],{"class":146,"line":959},[144,8728,8729],{"class":262},"            results.append(future.result())\n",[144,8731,8732],{"class":146,"line":964},[144,8733,562],{"class":262},[144,8735,8736],{"class":146,"line":970},[144,8737,8738],{"class":173},"    # 汇总\n",[144,8740,8741,8744,8746,8749,8751,8753,8755,8758,8760,8763,8765,8768,8771,8774],{"class":146,"line":976},[144,8742,8743],{"class":262},"    succeeded ",[144,8745,266],{"class":258},[144,8747,8748],{"class":154}," sum",[144,8750,793],{"class":262},[144,8752,5418],{"class":154},[144,8754,6373],{"class":258},[144,8756,8757],{"class":262}," _, r ",[144,8759,1180],{"class":258},[144,8761,8762],{"class":262}," results ",[144,8764,1719],{"class":258},[144,8766,8767],{"class":154}," isinstance",[144,8769,8770],{"class":262},"(r, ",[144,8772,8773],{"class":154},"dict",[144,8775,8776],{"class":262},"))\n",[144,8778,8779,8781,8783,8785,8787,8789,8792,8794,8797,8799,8801,8803,8806,8808,8811],{"class":146,"line":988},[144,8780,1102],{"class":154},[144,8782,793],{"class":262},[144,8784,440],{"class":258},[144,8786,449],{"class":158},[144,8788,1670],{"class":154},[144,8790,8791],{"class":158},"Batch complete: ",[144,8793,684],{"class":154},[144,8795,8796],{"class":262},"succeeded",[144,8798,690],{"class":154},[144,8800,1226],{"class":158},[144,8802,6249],{"class":154},[144,8804,8805],{"class":262},"(prompts)",[144,8807,690],{"class":154},[144,8809,8810],{"class":158}," succeeded\"",[144,8812,406],{"class":262},[144,8814,8815,8817],{"class":146,"line":993},[144,8816,1703],{"class":258},[144,8818,8819],{"class":262}," results\n",[144,8821,8822],{"class":146,"line":5520},[144,8823,376],{"emptyLinePlaceholder":57},[144,8825,8826],{"class":146,"line":5526},[144,8827,376],{"emptyLinePlaceholder":57},[144,8829,8830],{"class":146,"line":5554},[144,8831,8832],{"class":173},"# 示例用法\n",[144,8834,8835,8838,8840],{"class":146,"line":5585},[144,8836,8837],{"class":262},"prompts ",[144,8839,266],{"class":258},[144,8841,8842],{"class":262}," [\n",[144,8844,8845,8848],{"class":146,"line":5605},[144,8846,8847],{"class":158},"    \"A hummingbird hovering near a red flower. Macro lens, shallow depth of field.\"",[144,8849,452],{"class":262},[144,8851,8852,8855],{"class":146,"line":5611},[144,8853,8854],{"class":158},"    \"Ocean waves crashing on volcanic rocks at sunset. Slow motion.\"",[144,8856,452],{"class":262},[144,8858,8859,8862],{"class":146,"line":5617},[144,8860,8861],{"class":158},"    \"A street musician playing violin in the rain. Cinematic lighting.\"",[144,8863,452],{"class":262},[144,8865,8866],{"class":146,"line":5622},[144,8867,763],{"class":262},[144,8869,8870,8873,8876,8878,8880],{"class":146,"line":5635},[144,8871,8872],{"class":262},"batch_generate(prompts, ",[144,8874,8875],{"class":700},"max_concurrent",[144,8877,266],{"class":258},[144,8879,5399],{"class":154},[144,8881,406],{"class":262},[10,8883,8884],{},"批量处理的关键考虑：",[95,8886,8887,8895,8901],{},[72,8888,8889,8894],{},[22,8890,8891],{},[27,8892,8893],{},"max_concurrent=3"," — 不要同时提交太多请求。从 2–3 个并发开始，根据你的限流配额逐步增加。",[72,8896,8897,8900],{},[22,8898,8899],{},"ThreadPoolExecutor"," — 使用线程（而非进程），因为我们是 I/O 密集型（等待 API 响应），而不是 CPU 密集型。",[72,8902,8903,8906],{},[22,8904,8905],{},"错误隔离"," — 每个视频生成都是独立的。一个失败不会中断整个批次。",[44,8908],{},[47,8910,8911],{"id":8911},"下一步",[10,8913,8914],{},"你已经掌握了基础——文生视频、图生视频、异步轮询、Webhook、错误处理和批量处理。以下是深入学习的方向：",[91,8916,8917],{"id":8917},"探索高级功能",[95,8919,8920,8928,8935,8942,8950],{},[72,8921,8922,8927],{},[22,8923,8924],{},[36,8925,8926],{"href":2961},"@Tags 多模态引用指南"," — 掌握 @Image、@Video、@Audio 引用系统，实现多模态生成",[72,8929,8930,8934],{},[22,8931,8932],{},[36,8933,3849],{"href":3848}," — 用代码复刻希区柯克变焦、一镜到底跟踪镜头和环绕镜头",[72,8936,8937,8941],{},[22,8938,8939],{},[36,8940,3376],{"href":3375}," — 首尾帧控制、多图合成、电商产品视频",[72,8943,8944,8949],{},[22,8945,8946],{},[36,8947,8948],{"href":2607},"电商产品视频指南"," — 批量将产品图转化为营销视频",[72,8951,8952,8956],{},[22,8953,8954],{},[36,8955,1766],{"href":1765}," — 分镜脚本格式、时间轴语法，以及我们 Demo 视频背后的提示词",[91,8958,8959],{"id":8959},"参考文档",[95,8961,8962,8968,8975],{},[72,8963,8964],{},[36,8965,8967],{"href":1258,"rel":8966},[40],"视频生成 API 参考",[72,8969,8970],{},[36,8971,8974],{"href":8972,"rel":8973},"https://seedance2api.app/docs/multimodal-reference",[40],"多模态引用规范",[72,8976,8977],{},[36,8978,8981],{"href":8979,"rel":8980},"https://seedance2api.app/docs/sdks",[40],"Python & Node.js SDK",[91,8983,8984],{"id":8984},"动手做点什么",[10,8986,8987],{},"把你学到的东西组合起来。以下是一些项目灵感：",[95,8989,8990,8999,9005,9015],{},[72,8991,8992,8995,8996,116],{},[22,8993,8994],{},"自动化产品视频流水线"," — 上传产品图，批量生成营销视频（参考",[36,8997,8998],{"href":2607},"电商视频指南",[72,9000,9001,9004],{},[22,9002,9003],{},"社交媒体内容引擎"," — 从文本简报生成竖版短视频，直接发布到抖音/Reels",[72,9006,9007,9010,9011,9014],{},[22,9008,9009],{},"分镜到视频工具"," — 将连续图片转化为带",[36,9012,9013],{"href":3848},"镜头运动控制","的动画场景",[72,9016,9017,9020],{},[22,9018,9019],{},"AI 视频编辑流水线"," — 利用 Seedance 2.0 的视频延展功能，从短片段创建更长的叙事",[10,9022,9023],{},[22,9024,9025,9026,9030],{},"准备好了？",[36,9027,9029],{"href":38,"rel":9028},[40],"免费获取 EvoLink API Key","，今天就开始生成视频。",[44,9032],{},[47,9034,9035],{"id":9035},"常见问题",[91,9037,9039],{"id":9038},"seedance-20-视频生成需要多长时间","Seedance 2.0 视频生成需要多长时间？",[10,9041,9042,9043,9045],{},"通常 30–120 秒，取决于时长和质量设置。5 秒 720p 的视频大约 50 秒完成。15 秒 1080p 的视频可能需要 2–3 分钟。API 在每个任务中返回 ",[27,9044,2105],{}," 字段，方便你设置合理的超时时间。高峰时段，排队等待可能额外增加 10–30 秒。",[91,9047,9049],{"id":9048},"seedance-20-api-支持哪些图片格式","Seedance 2.0 API 支持哪些图片格式？",[10,9051,9052,9053,9055],{},"JPEG、PNG、WebP、BMP、TIFF 和 GIF。每张图片不超过 30 MB。每次请求最多通过 ",[27,9054,2890],{}," 参数传 9 张图片。图片必须是公开可访问的 URL——API 会直接拉取。建议使用短边至少 720px 的图片。分辨率太低（低于 256px）可能导致动画模糊。",[91,9057,9059],{"id":9058},"能生成超过-15-秒的视频吗","能生成超过 15 秒的视频吗？",[10,9061,9062,9063,9065],{},"单次生成最长 15 秒。要制作更长的内容，生成多个片段后用 FFmpeg 或任何视频编辑器拼接。Seedance 2.0 支持视频延展——你可以把生成视频的最后一帧作为下一次生成的首帧，实现无缝衔接。基本思路：生成片段 1，提取最后一帧，作为 ",[27,9064,2989],{}," 传入片段 2。",[91,9067,9069],{"id":9068},"通过-evolink-使用-seedance-20-api-要多少钱","通过 EvoLink 使用 Seedance 2.0 API 要多少钱？",[10,9071,9072,9073,9077,9078,9080],{},"定价基于视频时长和质量档位。5 秒 720p 的视频大约消耗 18 个额度。EvoLink 提供",[36,9074,9076],{"href":210,"rel":9075},[40],"智能路由","，相比直接调用 API 可以降低成本。具体的每秒费率请查看控制台。API 响应中的 ",[27,9079,2115],{}," 字段会在生成前显示精确费用——实际收费不会超过这个金额。",[91,9082,9084],{"id":9083},"seedance-15-pro-和-seedance-20-有什么区别","seedance-1.5-pro 和 seedance-2.0 有什么区别？",[10,9086,9087,9088,9090,9091,9093,9094,297],{},"Seedance 2.0 新增了多模态引用（图片、视频、音频混合输入）、原生音频生成、更好的物理一致性和视频编辑能力。API 接口完全一致——相同的端点、相同的参数、相同的响应格式。你现在可以用 ",[27,9089,29],{}," 测试，之后只需改模型名称切换到 ",[27,9092,1751],{},"。1.5 的主要限制：仅支持单张图片输入（不支持 @Image2–9）、不支持视频/音频引用、不支持原生音频生成。详细对比请看 ",[36,9095,9097],{"href":9096},"/blog/seedance-2-vs-sora-2-api-comparison","Seedance 2.0 vs Sora 2 评测",[91,9099,9101],{"id":9100},"如何处理content-rejected-by-safety-filter错误","如何处理\"content rejected by safety filter\"错误？",[10,9103,9104,9105,9107,9108,9111],{},"内容审核系统会拒绝涉及写实暴力、露骨内容和真实公众人物的提示词。通过 ",[27,9106,2890],{}," 上传的写实人脸图片也会被拒绝。要绕过人脸限制，使用插画、风格化或动漫风格的角色图片。对于提示词被拒的情况，修改措辞避免涉及受限主题。错误响应中包含 ",[27,9109,9110],{},"type: \"content_policy_violation\"","——在你的错误处理代码中检查这个字段，给用户一个清晰的提示。",[91,9113,9115],{"id":9114},"能在-nodejs-javascript-项目中使用-seedance-api-吗","能在 Node.js / JavaScript 项目中使用 Seedance API 吗？",[10,9117,9118,9119,280,9122,9125,9126,9130],{},"可以。REST API 与语言无关——任何 HTTP 客户端都能用。本教程中的概念（异步轮询、Webhook、错误处理）可以直接用 Node.js 的 ",[27,9120,9121],{},"fetch",[27,9123,9124],{},"axios"," 实现。EvoLink 还提供官方的 ",[36,9127,9129],{"href":8979,"rel":9128},[40],"Node.js 和 Python SDK","，帮你处理轮询和重试。",[91,9132,9134],{"id":9133},"视频完成时我的-webhook-服务器挂了怎么办","视频完成时我的 Webhook 服务器挂了怎么办？",[10,9136,9137,9138,9141],{},"API 会以递增间隔重试 3 次 Webhook 投递（1s、2s、4s）。如果 3 次都失败，Webhook 就放弃了——但视频仍然可用。你随时可以用 ",[27,9139,9140],{},"GET /v1/tasks/{task_id}"," 轮询获取结果作为兜底。因此，最佳实践是在提交时保存任务 ID，并设置一个后台任务定期检查是否有完成但未通过 Webhook 接收到的任务。",[91,9143,9145],{"id":9144},"api-请求有限流吗","API 请求有限流吗？",[10,9147,9148,9149,9151,9152,9156,9157,9161],{},"有。默认限流对开发和中等规模的生产使用来说很宽裕。如果遇到 ",[27,9150,4925],{}," 错误，按照",[36,9153,9155],{"href":9154},"#%E4%BC%98%E9%9B%85%E5%9C%B0%E5%A4%84%E7%90%86%E9%94%99%E8%AF%AF","错误处理章节","中的方式实现指数退避。对于大规模使用场景（每天数千个视频），联系 ",[36,9158,9160],{"href":210,"rel":9159},[40],"EvoLink 支持"," 讨论自定义限流和专属容量。",[91,9163,9165],{"id":9164},"seedance-20-可以用于商业项目吗","Seedance 2.0 可以用于商业项目吗？",[10,9167,9168,9169,297],{},"可以。通过 EvoLink API 生成的视频授权商业使用。你拥有输出内容的所有权，可以用于产品、营销材料、客户交付物和发布内容。详细的授权条款和商业使用最佳实践请看 ",[36,9170,9172],{"href":9171},"/blog/seedance-2-copyright-api-guide","Seedance 2.0 版权指南",[44,9174],{},[47,9176,9177],{"id":9177},"完整脚本",[10,9179,9180],{},"以下是本教程的完整代码，整合在一个文件中——复制、粘贴、填入你的 API Key，直接运行：",[135,9182,9184],{"className":338,"code":9183,"language":340,"meta":140,"style":140},"\"\"\"\nSeedance 2.0 API 教程 — 完整脚本\n文档：https://seedance2api.app/docs/video-generation\nAPI Key：https://evolink.ai/early-access\n\"\"\"\nimport requests\nimport time\nimport os\nimport json\nimport random\n\n# ── 配置 ─────────────────────────────────────────────────────\nAPI_KEY = os.getenv(\"EVOLINK_API_KEY\", \"sk-your-api-key-here\")\nBASE_URL = \"https://api.evolink.ai/v1\"\nHEADERS = {\n    \"Authorization\": f\"Bearer {API_KEY}\",\n    \"Content-Type\": \"application/json\"\n}\n\n\n# ── 可复用辅助函数 ───────────────────────────────────────────\ndef wait_for_video(task_id, poll_interval=10, timeout=600):\n    \"\"\"轮询视频生成任务直到完成。\"\"\"\n    elapsed = 0\n    while elapsed \u003C timeout:\n        response = requests.get(\n            f\"{BASE_URL}/tasks/{task_id}\",\n            headers=HEADERS\n        )\n        response.raise_for_status()\n        task = response.json()\n        status = task[\"status\"]\n        progress = task.get(\"progress\", 0)\n        print(f\"  [{elapsed}s] Status: {status} | Progress: {progress}%\")\n        if status == \"completed\":\n            return task\n        elif status == \"failed\":\n            raise RuntimeError(f\"Task {task_id} failed: {task}\")\n        time.sleep(poll_interval)\n        elapsed += poll_interval\n    raise TimeoutError(f\"Task {task_id} timed out after {timeout}s\")\n\n\ndef download_video(url, filename=\"output.mp4\"):\n    \"\"\"从 URL 下载视频文件。\"\"\"\n    print(f\"Downloading to {filename}...\")\n    resp = requests.get(url, stream=True)\n    resp.raise_for_status()\n    with open(filename, \"wb\") as f:\n        for chunk in resp.iter_content(chunk_size=8192):\n            f.write(chunk)\n    print(f\"Saved: {filename} ({os.path.getsize(filename) / 1024:.0f} KB)\")\n\n\ndef generate_video_with_retry(payload, max_retries=3):\n    \"\"\"提交生成请求，对瞬态错误自动重试。\"\"\"\n    for attempt in range(max_retries):\n        try:\n            response = requests.post(\n                f\"{BASE_URL}/videos/generations\",\n                headers=HEADERS,\n                json=payload,\n                timeout=30\n            )\n            if response.status_code == 200:\n                return response.json()\n            error = response.json().get(\"error\", {})\n            if response.status_code in (400, 401, 402, 404, 413, 422):\n                raise ValueError(\n                    f\"API error {response.status_code}: \"\n                    f\"{error.get('message', 'Unknown')}\"\n                )\n            if response.status_code in (429, 500, 502, 503):\n                wait = (2 ** attempt) + random.uniform(0, 1)\n                print(f\"  Retry {attempt+1}/{max_retries} after {wait:.1f}s\")\n                time.sleep(wait)\n                continue\n        except requests.exceptions.RequestException:\n            wait = (2 ** attempt) + random.uniform(0, 1)\n            print(f\"  Retry {attempt+1}/{max_retries} after {wait:.1f}s\")\n            time.sleep(wait)\n            continue\n    raise RuntimeError(f\"Failed after {max_retries} retries\")\n\n\ndef validate_payload(payload):\n    \"\"\"在 API 调用前校验生成 payload。\"\"\"\n    errors = []\n    if not payload.get(\"model\"):\n        errors.append(\"'model' is required\")\n    if not payload.get(\"prompt\") or not payload[\"prompt\"].strip():\n        errors.append(\"'prompt' is required\")\n    duration = payload.get(\"duration\", 5)\n    if duration \u003C 4 or duration > 15:\n        errors.append(f\"'duration' must be 4-15, got {duration}\")\n    quality = payload.get(\"quality\", \"720p\")\n    if quality not in {\"480p\", \"720p\", \"1080p\"}:\n        errors.append(f\"Invalid quality: {quality}\")\n    if errors:\n        raise ValueError(\"Validation failed:\\n\" + \"\\n\".join(f\"  - {e}\" for e in errors))\n\n\ndef cancel_task(task_id):\n    \"\"\"取消 pending 或 processing 状态的任务。\"\"\"\n    response = requests.post(\n        f\"{BASE_URL}/tasks/{task_id}/cancel\",\n        headers=HEADERS\n    )\n    if response.status_code == 200:\n        print(f\"Task {task_id} cancelled.\")\n    else:\n        print(f\"Cancel failed: {response.json()}\")\n\n\n# ── 示例 1：文生视频 ─────────────────────────────────────────\ndef text_to_video():\n    payload = {\n        \"model\": \"seedance-2.0\",\n        \"prompt\": (\n            \"A golden retriever puppy chases a butterfly through \"\n            \"a sunlit meadow. The camera follows the puppy with a \"\n            \"smooth tracking shot as wildflowers sway in the breeze.\"\n        ),\n        \"duration\": 5,\n        \"quality\": \"720p\",\n        \"aspect_ratio\": \"16:9\",\n        \"generate_audio\": True\n    }\n    validate_payload(payload)\n    task = generate_video_with_retry(payload)\n    print(f\"Task: {task['id']} (ETA: {task['task_info']['estimated_time']}s)\")\n    result = wait_for_video(task[\"id\"])\n    download_video(result[\"results\"][0], \"text_to_video.mp4\")\n\n\n# ── 示例 2：图生视频 ─────────────────────────────────────────\ndef image_to_video():\n    payload = {\n        \"model\": \"seedance-2.0\",\n        \"prompt\": (\n            \"@Image1 as the first frame. The scene slowly comes \"\n            \"to life — leaves rustle gently, soft light shifts \"\n            \"across the frame.\"\n        ),\n        \"image_urls\": [\"https://example.com/your-image.jpg\"],\n        \"duration\": 5,\n        \"quality\": \"720p\"\n    }\n    validate_payload(payload)\n    task = generate_video_with_retry(payload)\n    print(f\"Task: {task['id']}\")\n    result = wait_for_video(task[\"id\"])\n    download_video(result[\"results\"][0], \"image_to_video.mp4\")\n\n\n# ── 示例 3：竖版社交媒体视频 ─────────────────────────────────\ndef social_media_video():\n    payload = {\n        \"model\": \"seedance-2.0\",\n        \"prompt\": (\n            \"A barista pours latte art in slow motion. \"\n            \"Close-up overhead shot, warm cafe lighting.\"\n        ),\n        \"duration\": 8,\n        \"quality\": \"1080p\",\n        \"aspect_ratio\": \"9:16\",\n        \"generate_audio\": True\n    }\n    validate_payload(payload)\n    task = generate_video_with_retry(payload)\n    print(f\"Task: {task['id']}\")\n    result = wait_for_video(task[\"id\"])\n    download_video(result[\"results\"][0], \"social_video.mp4\")\n\n\nif __name__ == \"__main__\":\n    print(\"=== 文生视频 ===\")\n    text_to_video()\n    # print(\"\\n=== 图生视频 ===\")\n    # image_to_video()  # 取消注释并设置你的图片 URL\n    # print(\"\\n=== 社交媒体视频 ===\")\n    # social_media_video()\n",[27,9185,9186,9190,9195,9200,9205,9209,9215,9221,9227,9233,9239,9243,9247,9263,9271,9279,9295,9303,9307,9311,9315,9320,9340,9345,9353,9363,9371,9391,9399,9403,9407,9415,9427,9443,9479,9491,9497,9509,9540,9544,9552,9582,9586,9590,9604,9608,9629,9645,9649,9665,9683,9687,9721,9725,9729,9743,9748,9760,9766,9774,9786,9796,9804,9813,9817,9829,9835,9847,9881,9889,9904,9928,9932,9958,9984,10027,10031,10035,10042,10068,10108,10112,10116,10138,10142,10146,10154,10159,10167,10179,10187,10209,10218,10234,10254,10272,10288,10313,10332,10339,10385,10390,10395,10404,10410,10419,10440,10449,10454,10467,10489,10496,10517,10522,10527,10533,10542,10551,10562,10569,10574,10579,10584,10589,10600,10611,10622,10631,10636,10642,10651,10695,10708,10728,10733,10738,10744,10753,10762,10773,10780,10785,10790,10796,10801,10815,10826,10835,10840,10845,10854,10879,10892,10910,10915,10920,10926,10936,10945,10956,10963,10969,10975,10980,10991,11002,11013,11022,11027,11032,11041,11066,11079,11097,11102,11107,11120,11132,11137,11143,11149,11155],{"__ignoreMap":140},[144,9187,9188],{"class":146,"line":147},[144,9189,6682],{"class":158},[144,9191,9192],{"class":146,"line":165},[144,9193,9194],{"class":158},"Seedance 2.0 API 教程 — 完整脚本\n",[144,9196,9197],{"class":146,"line":177},[144,9198,9199],{"class":158},"文档：https://seedance2api.app/docs/video-generation\n",[144,9201,9202],{"class":146,"line":186},[144,9203,9204],{"class":158},"API Key：https://evolink.ai/early-access\n",[144,9206,9207],{"class":146,"line":373},[144,9208,6682],{"class":158},[144,9210,9211,9213],{"class":146,"line":379},[144,9212,347],{"class":258},[144,9214,328],{"class":262},[144,9216,9217,9219],{"class":146,"line":385},[144,9218,347],{"class":258},[144,9220,356],{"class":262},[144,9222,9223,9225],{"class":146,"line":409},[144,9224,347],{"class":258},[144,9226,363],{"class":262},[144,9228,9229,9231],{"class":146,"line":420},[144,9230,347],{"class":258},[144,9232,370],{"class":262},[144,9234,9235,9237],{"class":146,"line":431},[144,9236,347],{"class":258},[144,9238,5023],{"class":262},[144,9240,9241],{"class":146,"line":455},[144,9242,376],{"emptyLinePlaceholder":57},[144,9244,9245],{"class":146,"line":466},[144,9246,382],{"class":173},[144,9248,9249,9251,9253,9255,9257,9259,9261],{"class":146,"line":599},[144,9250,388],{"class":154},[144,9252,391],{"class":258},[144,9254,394],{"class":262},[144,9256,397],{"class":158},[144,9258,400],{"class":262},[144,9260,403],{"class":158},[144,9262,406],{"class":262},[144,9264,9265,9267,9269],{"class":146,"line":604},[144,9266,412],{"class":154},[144,9268,391],{"class":258},[144,9270,417],{"class":158},[144,9272,9273,9275,9277],{"class":146,"line":610},[144,9274,423],{"class":154},[144,9276,391],{"class":258},[144,9278,428],{"class":262},[144,9280,9281,9283,9285,9287,9289,9291,9293],{"class":146,"line":616},[144,9282,434],{"class":158},[144,9284,437],{"class":262},[144,9286,440],{"class":258},[144,9288,443],{"class":158},[144,9290,446],{"class":154},[144,9292,449],{"class":158},[144,9294,452],{"class":262},[144,9296,9297,9299,9301],{"class":146,"line":622},[144,9298,458],{"class":158},[144,9300,437],{"class":262},[144,9302,463],{"class":158},[144,9304,9305],{"class":146,"line":627},[144,9306,469],{"class":262},[144,9308,9309],{"class":146,"line":638},[144,9310,376],{"emptyLinePlaceholder":57},[144,9312,9313],{"class":146,"line":653},[144,9314,376],{"emptyLinePlaceholder":57},[144,9316,9317],{"class":146,"line":659},[144,9318,9319],{"class":173},"# ── 可复用辅助函数 ───────────────────────────────────────────\n",[144,9321,9322,9324,9326,9328,9330,9332,9334,9336,9338],{"class":146,"line":670},[144,9323,525],{"class":258},[144,9325,528],{"class":150},[144,9327,531],{"class":262},[144,9329,266],{"class":258},[144,9331,536],{"class":154},[144,9333,539],{"class":262},[144,9335,266],{"class":258},[144,9337,544],{"class":154},[144,9339,547],{"class":262},[144,9341,9342],{"class":146,"line":697},[144,9343,9344],{"class":158},"    \"\"\"轮询视频生成任务直到完成。\"\"\"\n",[144,9346,9347,9349,9351],{"class":146,"line":709},[144,9348,630],{"class":262},[144,9350,266],{"class":258},[144,9352,635],{"class":154},[144,9354,9355,9357,9359,9361],{"class":146,"line":715},[144,9356,641],{"class":258},[144,9358,644],{"class":262},[144,9360,647],{"class":258},[144,9362,650],{"class":262},[144,9364,9365,9367,9369],{"class":146,"line":721},[144,9366,662],{"class":262},[144,9368,266],{"class":258},[144,9370,667],{"class":262},[144,9372,9373,9375,9377,9379,9381,9383,9385,9387,9389],{"class":146,"line":727},[144,9374,673],{"class":258},[144,9376,449],{"class":158},[144,9378,678],{"class":154},[144,9380,681],{"class":158},[144,9382,684],{"class":154},[144,9384,687],{"class":262},[144,9386,690],{"class":154},[144,9388,449],{"class":158},[144,9390,452],{"class":262},[144,9392,9393,9395,9397],{"class":146,"line":738},[144,9394,701],{"class":700},[144,9396,266],{"class":258},[144,9398,706],{"class":154},[144,9400,9401],{"class":146,"line":743},[144,9402,712],{"class":262},[144,9404,9405],{"class":146,"line":749},[144,9406,724],{"class":262},[144,9408,9409,9411,9413],{"class":146,"line":766},[144,9410,730],{"class":262},[144,9412,266],{"class":258},[144,9414,735],{"class":262},[144,9416,9417,9419,9421,9423,9425],{"class":146,"line":787},[144,9418,752],{"class":262},[144,9420,266],{"class":258},[144,9422,757],{"class":262},[144,9424,760],{"class":158},[144,9426,763],{"class":262},[144,9428,9429,9431,9433,9435,9437,9439,9441],{"class":146,"line":833},[144,9430,769],{"class":262},[144,9432,266],{"class":258},[144,9434,774],{"class":262},[144,9436,777],{"class":158},[144,9438,400],{"class":262},[144,9440,782],{"class":154},[144,9442,406],{"class":262},[144,9444,9445,9447,9449,9451,9453,9455,9457,9459,9461,9463,9465,9467,9469,9471,9473,9475,9477],{"class":146,"line":838},[144,9446,790],{"class":154},[144,9448,793],{"class":262},[144,9450,440],{"class":258},[144,9452,798],{"class":158},[144,9454,684],{"class":154},[144,9456,803],{"class":262},[144,9458,690],{"class":154},[144,9460,808],{"class":158},[144,9462,684],{"class":154},[144,9464,813],{"class":262},[144,9466,690],{"class":154},[144,9468,818],{"class":158},[144,9470,684],{"class":154},[144,9472,823],{"class":262},[144,9474,690],{"class":154},[144,9476,828],{"class":158},[144,9478,406],{"class":262},[144,9480,9481,9483,9485,9487,9489],{"class":146,"line":844},[144,9482,847],{"class":258},[144,9484,850],{"class":262},[144,9486,853],{"class":258},[144,9488,856],{"class":158},[144,9490,859],{"class":262},[144,9492,9493,9495],{"class":146,"line":862},[144,9494,865],{"class":258},[144,9496,868],{"class":262},[144,9498,9499,9501,9503,9505,9507],{"class":146,"line":871},[144,9500,874],{"class":258},[144,9502,850],{"class":262},[144,9504,853],{"class":258},[144,9506,881],{"class":158},[144,9508,859],{"class":262},[144,9510,9511,9513,9515,9517,9519,9521,9523,9525,9527,9529,9531,9534,9536,9538],{"class":146,"line":886},[144,9512,905],{"class":258},[144,9514,908],{"class":154},[144,9516,793],{"class":262},[144,9518,440],{"class":258},[144,9520,920],{"class":158},[144,9522,684],{"class":154},[144,9524,687],{"class":262},[144,9526,690],{"class":154},[144,9528,929],{"class":158},[144,9530,684],{"class":154},[144,9532,9533],{"class":262},"task",[144,9535,690],{"class":154},[144,9537,449],{"class":158},[144,9539,406],{"class":262},[144,9541,9542],{"class":146,"line":902},[144,9543,973],{"class":262},[144,9545,9546,9548,9550],{"class":146,"line":914},[144,9547,979],{"class":262},[144,9549,982],{"class":258},[144,9551,985],{"class":262},[144,9553,9554,9556,9558,9560,9562,9564,9566,9568,9570,9572,9574,9576,9578,9580],{"class":146,"line":953},[144,9555,996],{"class":258},[144,9557,999],{"class":154},[144,9559,793],{"class":262},[144,9561,440],{"class":258},[144,9563,920],{"class":158},[144,9565,684],{"class":154},[144,9567,687],{"class":262},[144,9569,690],{"class":154},[144,9571,1014],{"class":158},[144,9573,684],{"class":154},[144,9575,1019],{"class":262},[144,9577,690],{"class":154},[144,9579,1024],{"class":158},[144,9581,406],{"class":262},[144,9583,9584],{"class":146,"line":959},[144,9585,376],{"emptyLinePlaceholder":57},[144,9587,9588],{"class":146,"line":964},[144,9589,376],{"emptyLinePlaceholder":57},[144,9591,9592,9594,9596,9598,9600,9602],{"class":146,"line":970},[144,9593,525],{"class":258},[144,9595,1082],{"class":150},[144,9597,1085],{"class":262},[144,9599,266],{"class":258},[144,9601,1090],{"class":158},[144,9603,547],{"class":262},[144,9605,9606],{"class":146,"line":976},[144,9607,1097],{"class":158},[144,9609,9610,9612,9614,9616,9619,9621,9623,9625,9627],{"class":146,"line":988},[144,9611,1102],{"class":154},[144,9613,793],{"class":262},[144,9615,440],{"class":258},[144,9617,9618],{"class":158},"\"Downloading to ",[144,9620,684],{"class":154},[144,9622,1114],{"class":262},[144,9624,690],{"class":154},[144,9626,1119],{"class":158},[144,9628,406],{"class":262},[144,9630,9631,9633,9635,9637,9639,9641,9643],{"class":146,"line":993},[144,9632,1126],{"class":262},[144,9634,266],{"class":258},[144,9636,1131],{"class":262},[144,9638,1134],{"class":700},[144,9640,266],{"class":258},[144,9642,1139],{"class":154},[144,9644,406],{"class":262},[144,9646,9647],{"class":146,"line":5520},[144,9648,1146],{"class":262},[144,9650,9651,9653,9655,9657,9659,9661,9663],{"class":146,"line":5526},[144,9652,1151],{"class":258},[144,9654,1154],{"class":154},[144,9656,1157],{"class":262},[144,9658,1160],{"class":158},[144,9660,1163],{"class":262},[144,9662,1166],{"class":258},[144,9664,1169],{"class":262},[144,9666,9667,9669,9671,9673,9675,9677,9679,9681],{"class":146,"line":5554},[144,9668,1174],{"class":258},[144,9670,1177],{"class":262},[144,9672,1180],{"class":258},[144,9674,1183],{"class":262},[144,9676,1186],{"class":700},[144,9678,266],{"class":258},[144,9680,1191],{"class":154},[144,9682,547],{"class":262},[144,9684,9685],{"class":146,"line":5585},[144,9686,1198],{"class":262},[144,9688,9689,9691,9693,9695,9697,9699,9701,9703,9705,9707,9709,9711,9713,9715,9717,9719],{"class":146,"line":5605},[144,9690,1102],{"class":154},[144,9692,793],{"class":262},[144,9694,440],{"class":258},[144,9696,1209],{"class":158},[144,9698,684],{"class":154},[144,9700,1114],{"class":262},[144,9702,690],{"class":154},[144,9704,1218],{"class":158},[144,9706,684],{"class":154},[144,9708,1223],{"class":262},[144,9710,1226],{"class":258},[144,9712,1229],{"class":154},[144,9714,1232],{"class":258},[144,9716,690],{"class":154},[144,9718,1237],{"class":158},[144,9720,406],{"class":262},[144,9722,9723],{"class":146,"line":5611},[144,9724,376],{"emptyLinePlaceholder":57},[144,9726,9727],{"class":146,"line":5617},[144,9728,376],{"emptyLinePlaceholder":57},[144,9730,9731,9733,9735,9737,9739,9741],{"class":146,"line":5622},[144,9732,525],{"class":258},[144,9734,5034],{"class":150},[144,9736,5037],{"class":262},[144,9738,266],{"class":258},[144,9740,5042],{"class":154},[144,9742,547],{"class":262},[144,9744,9745],{"class":146,"line":5635},[144,9746,9747],{"class":158},"    \"\"\"提交生成请求，对瞬态错误自动重试。\"\"\"\n",[144,9749,9750,9752,9754,9756,9758],{"class":146,"line":5641},[144,9751,5100],{"class":258},[144,9753,5103],{"class":262},[144,9755,1180],{"class":258},[144,9757,5108],{"class":154},[144,9759,5111],{"class":262},[144,9761,9762,9764],{"class":146,"line":5668},[144,9763,5116],{"class":258},[144,9765,859],{"class":262},[144,9767,9768,9770,9772],{"class":146,"line":5708},[144,9769,5123],{"class":262},[144,9771,266],{"class":258},[144,9773,1432],{"class":262},[144,9775,9776,9778,9780,9782,9784],{"class":146,"line":5727},[144,9777,917],{"class":258},[144,9779,449],{"class":158},[144,9781,678],{"class":154},[144,9783,1444],{"class":158},[144,9785,452],{"class":262},[144,9787,9788,9790,9792,9794],{"class":146,"line":5732},[144,9789,5144],{"class":700},[144,9791,266],{"class":258},[144,9793,423],{"class":154},[144,9795,452],{"class":262},[144,9797,9798,9800,9802],{"class":146,"line":5737},[144,9799,5155],{"class":700},[144,9801,266],{"class":258},[144,9803,5160],{"class":262},[144,9805,9806,9808,9810],{"class":146,"line":5742},[144,9807,5165],{"class":700},[144,9809,266],{"class":258},[144,9811,9812],{"class":154},"30\n",[144,9814,9815],{"class":146,"line":7415},[144,9816,956],{"class":262},[144,9818,9819,9821,9823,9825,9827],{"class":146,"line":7421},[144,9820,5191],{"class":258},[144,9822,2503],{"class":262},[144,9824,853],{"class":258},[144,9826,2508],{"class":154},[144,9828,859],{"class":262},[144,9830,9831,9833],{"class":146,"line":7427},[144,9832,5204],{"class":258},[144,9834,735],{"class":262},[144,9836,9837,9839,9841,9843,9845],{"class":146,"line":7438},[144,9838,5220],{"class":262},[144,9840,266],{"class":258},[144,9842,5225],{"class":262},[144,9844,896],{"class":158},[144,9846,899],{"class":262},[144,9848,9849,9851,9853,9855,9857,9859,9861,9863,9865,9867,9869,9871,9873,9875,9877,9879],{"class":146,"line":7449},[144,9850,5191],{"class":258},[144,9852,2503],{"class":262},[144,9854,1180],{"class":258},[144,9856,1218],{"class":262},[144,9858,3630],{"class":154},[144,9860,400],{"class":262},[144,9862,4835],{"class":154},[144,9864,400],{"class":262},[144,9866,4853],{"class":154},[144,9868,400],{"class":262},[144,9870,4871],{"class":154},[144,9872,400],{"class":262},[144,9874,4889],{"class":154},[144,9876,400],{"class":262},[144,9878,4907],{"class":154},[144,9880,547],{"class":262},[144,9882,9883,9885,9887],{"class":146,"line":7460},[144,9884,5316],{"class":258},[144,9886,5319],{"class":154},[144,9888,911],{"class":262},[144,9890,9891,9893,9895,9897,9899,9901],{"class":146,"line":7465},[144,9892,5326],{"class":258},[144,9894,5329],{"class":158},[144,9896,684],{"class":154},[144,9898,5334],{"class":262},[144,9900,690],{"class":154},[144,9902,9903],{"class":158},": \"\n",[144,9905,9906,9908,9910,9912,9915,9917,9919,9922,9924,9926],{"class":146,"line":7471},[144,9907,5326],{"class":258},[144,9909,449],{"class":158},[144,9911,684],{"class":154},[144,9913,9914],{"class":262},"error.get(",[144,9916,937],{"class":158},[144,9918,400],{"class":262},[144,9920,9921],{"class":158},"'Unknown'",[144,9923,945],{"class":262},[144,9925,690],{"class":154},[144,9927,950],{"class":158},[144,9929,9930],{"class":146,"line":7479},[144,9931,5352],{"class":262},[144,9933,9934,9936,9938,9940,9942,9944,9946,9948,9950,9952,9954,9956],{"class":146,"line":7493},[144,9935,5191],{"class":258},[144,9937,2503],{"class":262},[144,9939,1180],{"class":258},[144,9941,1218],{"class":262},[144,9943,4925],{"class":154},[144,9945,400],{"class":262},[144,9947,4946],{"class":154},[144,9949,400],{"class":262},[144,9951,4966],{"class":154},[144,9953,400],{"class":262},[144,9955,4986],{"class":154},[144,9957,547],{"class":262},[144,9959,9960,9962,9964,9966,9968,9970,9972,9974,9976,9978,9980,9982],{"class":146,"line":7498},[144,9961,5392],{"class":262},[144,9963,266],{"class":258},[144,9965,1218],{"class":262},[144,9967,5399],{"class":154},[144,9969,5402],{"class":258},[144,9971,5405],{"class":262},[144,9973,5408],{"class":258},[144,9975,5411],{"class":262},[144,9977,782],{"class":154},[144,9979,400],{"class":262},[144,9981,5418],{"class":154},[144,9983,406],{"class":262},[144,9985,9986,9988,9990,9992,9994,9996,9999,10001,10004,10006,10008,10010,10012,10015,10017,10019,10021,10023,10025],{"class":146,"line":7512},[144,9987,5425],{"class":154},[144,9989,793],{"class":262},[144,9991,440],{"class":258},[144,9993,5432],{"class":158},[144,9995,684],{"class":154},[144,9997,9998],{"class":262},"attempt",[144,10000,5408],{"class":258},[144,10002,10003],{"class":154},"1}",[144,10005,1226],{"class":158},[144,10007,684],{"class":154},[144,10009,5449],{"class":262},[144,10011,690],{"class":154},[144,10013,10014],{"class":158}," after ",[144,10016,684],{"class":154},[144,10018,5467],{"class":262},[144,10020,5470],{"class":258},[144,10022,690],{"class":154},[144,10024,1024],{"class":158},[144,10026,406],{"class":262},[144,10028,10029],{"class":146,"line":7526},[144,10030,5500],{"class":262},[144,10032,10033],{"class":146,"line":7558},[144,10034,5505],{"class":258},[144,10036,10037,10039],{"class":146,"line":7570},[144,10038,5514],{"class":258},[144,10040,10041],{"class":262}," requests.exceptions.RequestException:\n",[144,10043,10044,10046,10048,10050,10052,10054,10056,10058,10060,10062,10064,10066],{"class":146,"line":7580},[144,10045,5529],{"class":262},[144,10047,266],{"class":258},[144,10049,1218],{"class":262},[144,10051,5399],{"class":154},[144,10053,5402],{"class":258},[144,10055,5405],{"class":262},[144,10057,5408],{"class":258},[144,10059,5411],{"class":262},[144,10061,782],{"class":154},[144,10063,400],{"class":262},[144,10065,5418],{"class":154},[144,10067,406],{"class":262},[144,10069,10070,10072,10074,10076,10078,10080,10082,10084,10086,10088,10090,10092,10094,10096,10098,10100,10102,10104,10106],{"class":146,"line":7585},[144,10071,5557],{"class":154},[144,10073,793],{"class":262},[144,10075,440],{"class":258},[144,10077,5432],{"class":158},[144,10079,684],{"class":154},[144,10081,9998],{"class":262},[144,10083,5408],{"class":258},[144,10085,10003],{"class":154},[144,10087,1226],{"class":158},[144,10089,684],{"class":154},[144,10091,5449],{"class":262},[144,10093,690],{"class":154},[144,10095,10014],{"class":158},[144,10097,684],{"class":154},[144,10099,5467],{"class":262},[144,10101,5470],{"class":258},[144,10103,690],{"class":154},[144,10105,1024],{"class":158},[144,10107,406],{"class":262},[144,10109,10110],{"class":146,"line":7592},[144,10111,5608],{"class":262},[144,10113,10114],{"class":146,"line":7614},[144,10115,5614],{"class":258},[144,10117,10118,10120,10122,10124,10126,10128,10130,10132,10134,10136],{"class":146,"line":7645},[144,10119,996],{"class":258},[144,10121,908],{"class":154},[144,10123,793],{"class":262},[144,10125,440],{"class":258},[144,10127,5753],{"class":158},[144,10129,684],{"class":154},[144,10131,5449],{"class":262},[144,10133,690],{"class":154},[144,10135,5762],{"class":158},[144,10137,406],{"class":262},[144,10139,10140],{"class":146,"line":7650},[144,10141,376],{"emptyLinePlaceholder":57},[144,10143,10144],{"class":146,"line":7656},[144,10145,376],{"emptyLinePlaceholder":57},[144,10147,10148,10150,10152],{"class":146,"line":7681},[144,10149,525],{"class":258},[144,10151,5824],{"class":150},[144,10153,5827],{"class":262},[144,10155,10156],{"class":146,"line":7686},[144,10157,10158],{"class":158},"    \"\"\"在 API 调用前校验生成 payload。\"\"\"\n",[144,10160,10161,10163,10165],{"class":146,"line":7691},[144,10162,5850],{"class":262},[144,10164,266],{"class":258},[144,10166,5855],{"class":262},[144,10168,10169,10171,10173,10175,10177],{"class":146,"line":7714},[144,10170,2500],{"class":258},[144,10172,5871],{"class":258},[144,10174,5874],{"class":262},[144,10176,5877],{"class":158},[144,10178,547],{"class":262},[144,10180,10181,10183,10185],{"class":146,"line":7724},[144,10182,5884],{"class":262},[144,10184,5887],{"class":158},[144,10186,406],{"class":262},[144,10188,10189,10191,10193,10195,10197,10199,10201,10203,10205,10207],{"class":146,"line":7730},[144,10190,2500],{"class":258},[144,10192,5871],{"class":258},[144,10194,5874],{"class":262},[144,10196,5900],{"class":158},[144,10198,1163],{"class":262},[144,10200,5905],{"class":258},[144,10202,5871],{"class":258},[144,10204,5910],{"class":262},[144,10206,5900],{"class":158},[144,10208,5915],{"class":262},[144,10210,10211,10213,10216],{"class":146,"line":7748},[144,10212,5884],{"class":262},[144,10214,10215],{"class":158},"\"'prompt' is required\"",[144,10217,406],{"class":262},[144,10219,10220,10222,10224,10226,10228,10230,10232],{"class":146,"line":7753},[144,10221,5938],{"class":262},[144,10223,266],{"class":258},[144,10225,5874],{"class":262},[144,10227,5945],{"class":158},[144,10229,400],{"class":262},[144,10231,1352],{"class":154},[144,10233,406],{"class":262},[144,10235,10236,10238,10240,10242,10244,10246,10248,10250,10252],{"class":146,"line":7758},[144,10237,2500],{"class":258},[144,10239,5958],{"class":262},[144,10241,647],{"class":258},[144,10243,5963],{"class":154},[144,10245,5966],{"class":258},[144,10247,5958],{"class":262},[144,10249,5971],{"class":258},[144,10251,5974],{"class":154},[144,10253,859],{"class":262},[144,10255,10256,10258,10260,10262,10264,10266,10268,10270],{"class":146,"line":7771},[144,10257,5884],{"class":262},[144,10259,440],{"class":258},[144,10261,5985],{"class":158},[144,10263,684],{"class":154},[144,10265,1773],{"class":262},[144,10267,690],{"class":154},[144,10269,449],{"class":158},[144,10271,406],{"class":262},[144,10273,10274,10276,10278,10280,10282,10284,10286],{"class":146,"line":7785},[144,10275,6031],{"class":262},[144,10277,266],{"class":258},[144,10279,5874],{"class":262},[144,10281,6038],{"class":158},[144,10283,400],{"class":262},[144,10285,1368],{"class":158},[144,10287,406],{"class":262},[144,10289,10290,10292,10294,10296,10298,10300,10302,10304,10306,10308,10310],{"class":146,"line":7812},[144,10291,2500],{"class":258},[144,10293,6051],{"class":262},[144,10295,6054],{"class":258},[144,10297,6057],{"class":258},[144,10299,6014],{"class":262},[144,10301,4005],{"class":158},[144,10303,400],{"class":262},[144,10305,1368],{"class":158},[144,10307,400],{"class":262},[144,10309,3702],{"class":158},[144,10311,10312],{"class":262},"}:\n",[144,10314,10315,10317,10319,10322,10324,10326,10328,10330],{"class":146,"line":7826},[144,10316,5884],{"class":262},[144,10318,440],{"class":258},[144,10320,10321],{"class":158},"\"Invalid quality: ",[144,10323,684],{"class":154},[144,10325,1781],{"class":262},[144,10327,690],{"class":154},[144,10329,449],{"class":158},[144,10331,406],{"class":262},[144,10333,10335,10337],{"class":146,"line":10334},99,[144,10336,2500],{"class":258},[144,10338,6326],{"class":262},[144,10340,10342,10344,10346,10348,10351,10353,10355,10357,10359,10361,10363,10365,10367,10369,10371,10373,10375,10377,10379,10381,10383],{"class":146,"line":10341},100,[144,10343,6331],{"class":258},[144,10345,5319],{"class":154},[144,10347,793],{"class":262},[144,10349,10350],{"class":158},"\"Validation failed:",[144,10352,1670],{"class":154},[144,10354,449],{"class":158},[144,10356,6347],{"class":258},[144,10358,6350],{"class":158},[144,10360,1670],{"class":154},[144,10362,449],{"class":158},[144,10364,6357],{"class":262},[144,10366,440],{"class":258},[144,10368,6362],{"class":158},[144,10370,684],{"class":154},[144,10372,5682],{"class":262},[144,10374,690],{"class":154},[144,10376,449],{"class":158},[144,10378,6373],{"class":258},[144,10380,6376],{"class":262},[144,10382,1180],{"class":258},[144,10384,6381],{"class":262},[144,10386,10388],{"class":146,"line":10387},101,[144,10389,376],{"emptyLinePlaceholder":57},[144,10391,10393],{"class":146,"line":10392},102,[144,10394,376],{"emptyLinePlaceholder":57},[144,10396,10398,10400,10402],{"class":146,"line":10397},103,[144,10399,525],{"class":258},[144,10401,2446],{"class":150},[144,10403,2449],{"class":262},[144,10405,10407],{"class":146,"line":10406},104,[144,10408,10409],{"class":158},"    \"\"\"取消 pending 或 processing 状态的任务。\"\"\"\n",[144,10411,10413,10415,10417],{"class":146,"line":10412},105,[144,10414,1427],{"class":262},[144,10416,266],{"class":258},[144,10418,1432],{"class":262},[144,10420,10422,10424,10426,10428,10430,10432,10434,10436,10438],{"class":146,"line":10421},106,[144,10423,1437],{"class":258},[144,10425,449],{"class":158},[144,10427,678],{"class":154},[144,10429,681],{"class":158},[144,10431,684],{"class":154},[144,10433,687],{"class":262},[144,10435,690],{"class":154},[144,10437,2481],{"class":158},[144,10439,452],{"class":262},[144,10441,10443,10445,10447],{"class":146,"line":10442},107,[144,10444,1455],{"class":700},[144,10446,266],{"class":258},[144,10448,706],{"class":154},[144,10450,10452],{"class":146,"line":10451},108,[144,10453,1483],{"class":262},[144,10455,10457,10459,10461,10463,10465],{"class":146,"line":10456},109,[144,10458,2500],{"class":258},[144,10460,2503],{"class":262},[144,10462,853],{"class":258},[144,10464,2508],{"class":154},[144,10466,859],{"class":262},[144,10468,10470,10472,10474,10476,10478,10480,10482,10484,10487],{"class":146,"line":10469},110,[144,10471,790],{"class":154},[144,10473,793],{"class":262},[144,10475,440],{"class":258},[144,10477,920],{"class":158},[144,10479,684],{"class":154},[144,10481,687],{"class":262},[144,10483,690],{"class":154},[144,10485,10486],{"class":158}," cancelled.\"",[144,10488,406],{"class":262},[144,10490,10492,10494],{"class":146,"line":10491},111,[144,10493,2536],{"class":258},[144,10495,859],{"class":262},[144,10497,10499,10501,10503,10505,10507,10509,10511,10513,10515],{"class":146,"line":10498},112,[144,10500,790],{"class":154},[144,10502,793],{"class":262},[144,10504,440],{"class":258},[144,10506,2549],{"class":158},[144,10508,684],{"class":154},[144,10510,2554],{"class":262},[144,10512,690],{"class":154},[144,10514,449],{"class":158},[144,10516,406],{"class":262},[144,10518,10520],{"class":146,"line":10519},113,[144,10521,376],{"emptyLinePlaceholder":57},[144,10523,10525],{"class":146,"line":10524},114,[144,10526,376],{"emptyLinePlaceholder":57},[144,10528,10530],{"class":146,"line":10529},115,[144,10531,10532],{"class":173},"# ── 示例 1：文生视频 ─────────────────────────────────────────\n",[144,10534,10536,10538,10540],{"class":146,"line":10535},116,[144,10537,525],{"class":258},[144,10539,1286],{"class":150},[144,10541,1289],{"class":262},[144,10543,10545,10547,10549],{"class":146,"line":10544},117,[144,10546,1294],{"class":262},[144,10548,266],{"class":258},[144,10550,428],{"class":262},[144,10552,10554,10556,10558,10560],{"class":146,"line":10553},118,[144,10555,1303],{"class":158},[144,10557,437],{"class":262},[144,10559,1308],{"class":158},[144,10561,452],{"class":262},[144,10563,10565,10567],{"class":146,"line":10564},119,[144,10566,1319],{"class":158},[144,10568,1322],{"class":262},[144,10570,10572],{"class":146,"line":10571},120,[144,10573,1327],{"class":158},[144,10575,10577],{"class":146,"line":10576},121,[144,10578,1332],{"class":158},[144,10580,10582],{"class":146,"line":10581},122,[144,10583,1337],{"class":158},[144,10585,10587],{"class":146,"line":10586},123,[144,10588,1342],{"class":262},[144,10590,10592,10594,10596,10598],{"class":146,"line":10591},124,[144,10593,1347],{"class":158},[144,10595,437],{"class":262},[144,10597,1352],{"class":154},[144,10599,452],{"class":262},[144,10601,10603,10605,10607,10609],{"class":146,"line":10602},125,[144,10604,1363],{"class":158},[144,10606,437],{"class":262},[144,10608,1368],{"class":158},[144,10610,452],{"class":262},[144,10612,10614,10616,10618,10620],{"class":146,"line":10613},126,[144,10615,1379],{"class":158},[144,10617,437],{"class":262},[144,10619,1384],{"class":158},[144,10621,452],{"class":262},[144,10623,10625,10627,10629],{"class":146,"line":10624},127,[144,10626,1395],{"class":158},[144,10628,437],{"class":262},[144,10630,3730],{"class":154},[144,10632,10634],{"class":146,"line":10633},128,[144,10635,1407],{"class":262},[144,10637,10639],{"class":146,"line":10638},129,[144,10640,10641],{"class":262},"    validate_payload(payload)\n",[144,10643,10645,10647,10649],{"class":146,"line":10644},130,[144,10646,1496],{"class":262},[144,10648,266],{"class":258},[144,10650,8508],{"class":262},[144,10652,10654,10656,10658,10660,10663,10665,10667,10669,10671,10673,10676,10678,10680,10682,10684,10686,10688,10690,10693],{"class":146,"line":10653},131,[144,10655,1102],{"class":154},[144,10657,793],{"class":262},[144,10659,440],{"class":258},[144,10661,10662],{"class":158},"\"Task: ",[144,10664,684],{"class":154},[144,10666,1529],{"class":262},[144,10668,1532],{"class":158},[144,10670,1535],{"class":262},[144,10672,690],{"class":154},[144,10674,10675],{"class":158}," (ETA: ",[144,10677,684],{"class":154},[144,10679,1529],{"class":262},[144,10681,1559],{"class":158},[144,10683,1562],{"class":262},[144,10685,1565],{"class":158},[144,10687,1535],{"class":262},[144,10689,690],{"class":154},[144,10691,10692],{"class":158},"s)\"",[144,10694,406],{"class":262},[144,10696,10698,10700,10702,10704,10706],{"class":146,"line":10697},132,[144,10699,1618],{"class":262},[144,10701,266],{"class":258},[144,10703,1623],{"class":262},[144,10705,1626],{"class":158},[144,10707,1629],{"class":262},[144,10709,10711,10714,10716,10718,10720,10723,10726],{"class":146,"line":10710},133,[144,10712,10713],{"class":262},"    download_video(result[",[144,10715,1651],{"class":158},[144,10717,1562],{"class":262},[144,10719,782],{"class":154},[144,10721,10722],{"class":262},"], ",[144,10724,10725],{"class":158},"\"text_to_video.mp4\"",[144,10727,406],{"class":262},[144,10729,10731],{"class":146,"line":10730},134,[144,10732,376],{"emptyLinePlaceholder":57},[144,10734,10736],{"class":146,"line":10735},135,[144,10737,376],{"emptyLinePlaceholder":57},[144,10739,10741],{"class":146,"line":10740},136,[144,10742,10743],{"class":173},"# ── 示例 2：图生视频 ─────────────────────────────────────────\n",[144,10745,10747,10749,10751],{"class":146,"line":10746},137,[144,10748,525],{"class":258},[144,10750,2632],{"class":150},[144,10752,1289],{"class":262},[144,10754,10756,10758,10760],{"class":146,"line":10755},138,[144,10757,1294],{"class":262},[144,10759,266],{"class":258},[144,10761,428],{"class":262},[144,10763,10765,10767,10769,10771],{"class":146,"line":10764},139,[144,10766,1303],{"class":158},[144,10768,437],{"class":262},[144,10770,1308],{"class":158},[144,10772,452],{"class":262},[144,10774,10776,10778],{"class":146,"line":10775},140,[144,10777,1319],{"class":158},[144,10779,1322],{"class":262},[144,10781,10783],{"class":146,"line":10782},141,[144,10784,2663],{"class":158},[144,10786,10788],{"class":146,"line":10787},142,[144,10789,2668],{"class":158},[144,10791,10793],{"class":146,"line":10792},143,[144,10794,10795],{"class":158},"            \"across the frame.\"\n",[144,10797,10799],{"class":146,"line":10798},144,[144,10800,1342],{"class":262},[144,10802,10804,10806,10809,10812],{"class":146,"line":10803},145,[144,10805,2682],{"class":158},[144,10807,10808],{"class":262},": [",[144,10810,10811],{"class":158},"\"https://example.com/your-image.jpg\"",[144,10813,10814],{"class":262},"],\n",[144,10816,10818,10820,10822,10824],{"class":146,"line":10817},146,[144,10819,1347],{"class":158},[144,10821,437],{"class":262},[144,10823,1352],{"class":154},[144,10825,452],{"class":262},[144,10827,10829,10831,10833],{"class":146,"line":10828},147,[144,10830,1363],{"class":158},[144,10832,437],{"class":262},[144,10834,8487],{"class":158},[144,10836,10838],{"class":146,"line":10837},148,[144,10839,1407],{"class":262},[144,10841,10843],{"class":146,"line":10842},149,[144,10844,10641],{"class":262},[144,10846,10848,10850,10852],{"class":146,"line":10847},150,[144,10849,1496],{"class":262},[144,10851,266],{"class":258},[144,10853,8508],{"class":262},[144,10855,10857,10859,10861,10863,10865,10867,10869,10871,10873,10875,10877],{"class":146,"line":10856},151,[144,10858,1102],{"class":154},[144,10860,793],{"class":262},[144,10862,440],{"class":258},[144,10864,10662],{"class":158},[144,10866,684],{"class":154},[144,10868,1529],{"class":262},[144,10870,1532],{"class":158},[144,10872,1535],{"class":262},[144,10874,690],{"class":154},[144,10876,449],{"class":158},[144,10878,406],{"class":262},[144,10880,10882,10884,10886,10888,10890],{"class":146,"line":10881},152,[144,10883,1618],{"class":262},[144,10885,266],{"class":258},[144,10887,1623],{"class":262},[144,10889,1626],{"class":158},[144,10891,1629],{"class":262},[144,10893,10895,10897,10899,10901,10903,10905,10908],{"class":146,"line":10894},153,[144,10896,10713],{"class":262},[144,10898,1651],{"class":158},[144,10900,1562],{"class":262},[144,10902,782],{"class":154},[144,10904,10722],{"class":262},[144,10906,10907],{"class":158},"\"image_to_video.mp4\"",[144,10909,406],{"class":262},[144,10911,10913],{"class":146,"line":10912},154,[144,10914,376],{"emptyLinePlaceholder":57},[144,10916,10918],{"class":146,"line":10917},155,[144,10919,376],{"emptyLinePlaceholder":57},[144,10921,10923],{"class":146,"line":10922},156,[144,10924,10925],{"class":173},"# ── 示例 3：竖版社交媒体视频 ─────────────────────────────────\n",[144,10927,10929,10931,10934],{"class":146,"line":10928},157,[144,10930,525],{"class":258},[144,10932,10933],{"class":150}," social_media_video",[144,10935,1289],{"class":262},[144,10937,10939,10941,10943],{"class":146,"line":10938},158,[144,10940,1294],{"class":262},[144,10942,266],{"class":258},[144,10944,428],{"class":262},[144,10946,10948,10950,10952,10954],{"class":146,"line":10947},159,[144,10949,1303],{"class":158},[144,10951,437],{"class":262},[144,10953,1308],{"class":158},[144,10955,452],{"class":262},[144,10957,10959,10961],{"class":146,"line":10958},160,[144,10960,1319],{"class":158},[144,10962,1322],{"class":262},[144,10964,10966],{"class":146,"line":10965},161,[144,10967,10968],{"class":158},"            \"A barista pours latte art in slow motion. \"\n",[144,10970,10972],{"class":146,"line":10971},162,[144,10973,10974],{"class":158},"            \"Close-up overhead shot, warm cafe lighting.\"\n",[144,10976,10978],{"class":146,"line":10977},163,[144,10979,1342],{"class":262},[144,10981,10983,10985,10987,10989],{"class":146,"line":10982},164,[144,10984,1347],{"class":158},[144,10986,437],{"class":262},[144,10988,3690],{"class":154},[144,10990,452],{"class":262},[144,10992,10994,10996,10998,11000],{"class":146,"line":10993},165,[144,10995,1363],{"class":158},[144,10997,437],{"class":262},[144,10999,3702],{"class":158},[144,11001,452],{"class":262},[144,11003,11005,11007,11009,11011],{"class":146,"line":11004},166,[144,11006,1379],{"class":158},[144,11008,437],{"class":262},[144,11010,3714],{"class":158},[144,11012,452],{"class":262},[144,11014,11016,11018,11020],{"class":146,"line":11015},167,[144,11017,1395],{"class":158},[144,11019,437],{"class":262},[144,11021,3730],{"class":154},[144,11023,11025],{"class":146,"line":11024},168,[144,11026,1407],{"class":262},[144,11028,11030],{"class":146,"line":11029},169,[144,11031,10641],{"class":262},[144,11033,11035,11037,11039],{"class":146,"line":11034},170,[144,11036,1496],{"class":262},[144,11038,266],{"class":258},[144,11040,8508],{"class":262},[144,11042,11044,11046,11048,11050,11052,11054,11056,11058,11060,11062,11064],{"class":146,"line":11043},171,[144,11045,1102],{"class":154},[144,11047,793],{"class":262},[144,11049,440],{"class":258},[144,11051,10662],{"class":158},[144,11053,684],{"class":154},[144,11055,1529],{"class":262},[144,11057,1532],{"class":158},[144,11059,1535],{"class":262},[144,11061,690],{"class":154},[144,11063,449],{"class":158},[144,11065,406],{"class":262},[144,11067,11069,11071,11073,11075,11077],{"class":146,"line":11068},172,[144,11070,1618],{"class":262},[144,11072,266],{"class":258},[144,11074,1623],{"class":262},[144,11076,1626],{"class":158},[144,11078,1629],{"class":262},[144,11080,11082,11084,11086,11088,11090,11092,11095],{"class":146,"line":11081},173,[144,11083,10713],{"class":262},[144,11085,1651],{"class":158},[144,11087,1562],{"class":262},[144,11089,782],{"class":154},[144,11091,10722],{"class":262},[144,11093,11094],{"class":158},"\"social_video.mp4\"",[144,11096,406],{"class":262},[144,11098,11100],{"class":146,"line":11099},174,[144,11101,376],{"emptyLinePlaceholder":57},[144,11103,11105],{"class":146,"line":11104},175,[144,11106,376],{"emptyLinePlaceholder":57},[144,11108,11110,11112,11114,11116,11118],{"class":146,"line":11109},176,[144,11111,1719],{"class":258},[144,11113,1722],{"class":154},[144,11115,1725],{"class":258},[144,11117,1728],{"class":158},[144,11119,859],{"class":262},[144,11121,11123,11125,11127,11130],{"class":146,"line":11122},177,[144,11124,1102],{"class":154},[144,11126,793],{"class":262},[144,11128,11129],{"class":158},"\"=== 文生视频 ===\"",[144,11131,406],{"class":262},[144,11133,11135],{"class":146,"line":11134},178,[144,11136,1735],{"class":262},[144,11138,11140],{"class":146,"line":11139},179,[144,11141,11142],{"class":173},"    # print(\"\\n=== 图生视频 ===\")\n",[144,11144,11146],{"class":146,"line":11145},180,[144,11147,11148],{"class":173},"    # image_to_video()  # 取消注释并设置你的图片 URL\n",[144,11150,11152],{"class":146,"line":11151},181,[144,11153,11154],{"class":173},"    # print(\"\\n=== 社交媒体视频 ===\")\n",[144,11156,11158],{"class":146,"line":11157},182,[144,11159,11160],{"class":173},"    # social_media_video()\n",[17,11162,11163],{},[10,11164,11165,11168,11169,11171,11172,11175],{},[22,11166,11167],{},"提示："," 要用当前可用的模型测试，把 ",[27,11170,1308],{}," 改成 ",[27,11173,11174],{},"\"seedance-1.5-pro\"","。API 接口完全一致——相同的端点、相同的参数、相同的响应格式。等 Seedance 2.0 全面上线后，再把模型名改回来就行。",[10,11177,11178],{},[22,11179,11180],{},[36,11181,11183],{"href":38,"rel":11182},[40],"开始构建 → 免费获取 EvoLink API Key",[11185,11186,11187],"style",{},"html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":140,"searchDepth":165,"depth":165,"links":11189},[11190,11193,11194,11195,11199,11204,11209,11213,11219,11227,11228,11233,11245],{"id":49,"depth":165,"text":50,"children":11191},[11192],{"id":93,"depth":177,"text":93},{"id":203,"depth":165,"text":204},{"id":310,"depth":165,"text":311},{"id":1265,"depth":165,"text":1266,"children":11196},[11197,11198],{"id":1844,"depth":177,"text":1845},{"id":2171,"depth":177,"text":2171},{"id":2214,"depth":165,"text":2215,"children":11200},[11201,11202,11203],{"id":2264,"depth":177,"text":2264},{"id":2343,"depth":177,"text":2343},{"id":2431,"depth":177,"text":2431},{"id":2596,"depth":165,"text":2597,"children":11205},[11206,11207,11208],{"id":2933,"depth":177,"text":2934},{"id":3211,"depth":177,"text":3211},{"id":3286,"depth":177,"text":3287},{"id":3381,"depth":165,"text":3381,"children":11210},[11211,11212],{"id":3634,"depth":177,"text":3634},{"id":4042,"depth":177,"text":4042},{"id":4152,"depth":165,"text":4152,"children":11214},[11215,11216,11217,11218],{"id":4158,"depth":177,"text":4158},{"id":4791,"depth":177,"text":4791},{"id":5004,"depth":177,"text":5004},{"id":5809,"depth":177,"text":5809},{"id":6408,"depth":165,"text":6409,"children":11220},[11221,11222,11223,11224,11225,11226],{"id":6419,"depth":177,"text":6419},{"id":6597,"depth":177,"text":6598},{"id":6663,"depth":177,"text":6664},{"id":7917,"depth":177,"text":7918},{"id":8054,"depth":177,"text":8055},{"id":8233,"depth":177,"text":8234},{"id":8324,"depth":165,"text":8325},{"id":8911,"depth":165,"text":8911,"children":11229},[11230,11231,11232],{"id":8917,"depth":177,"text":8917},{"id":8959,"depth":177,"text":8959},{"id":8984,"depth":177,"text":8984},{"id":9035,"depth":165,"text":9035,"children":11234},[11235,11236,11237,11238,11239,11240,11241,11242,11243,11244],{"id":9038,"depth":177,"text":9039},{"id":9048,"depth":177,"text":9049},{"id":9058,"depth":177,"text":9059},{"id":9068,"depth":177,"text":9069},{"id":9083,"depth":177,"text":9084},{"id":9100,"depth":177,"text":9101},{"id":9114,"depth":177,"text":9115},{"id":9133,"depth":177,"text":9134},{"id":9144,"depth":177,"text":9145},{"id":9164,"depth":177,"text":9165},{"id":9177,"depth":165,"text":9177},"手把手教你用 Python 调用 Seedance 2.0 API 生成 AI 视频。涵盖文生视频、图生视频、异步轮询、Webhook 回调和错误处理的完整教程。","md",{"date":11249,"image":11250,"seoTitle":11251,"author":11252},"2026-02-27","/s2-hero-api-tutorial.webp","Seedance 2.0 API 教程：Python 生成第一个 AI 视频（2026）","J @ EvoLink","/zh/blog/seedance-2-api-tutorial-python",{"title":5,"description":11246},"zh/blog/seedance-2-api-tutorial-python","QU0n5tAToiMW--aUHN3skyrEGJQmhwmAs6fjifeBMUs",1775067580600]