[{"data":1,"prerenderedAt":11293},["ShallowReactive",2],{"blog-/ko/blog/seedance-2-api-tutorial-python":3},{"id":4,"title":5,"body":6,"description":11282,"extension":11283,"meta":11284,"navigation":57,"path":11289,"seo":11290,"stem":11291,"__hash__":11292},"content/ko/blog/seedance-2-api-tutorial-python.md","Seedance 2.0 API 튜토리얼: Python으로 첫 AI 비디오 생성하기",{"type":7,"value":8,"toc":11224},"minimark",[9,13,16,31,43,46,51,54,65,68,90,95,124,127,200,202,206,215,218,246,249,270,289,298,306,308,312,315,329,335,469,472,508,511,1026,1029,1065,1238,1248,1251,1260,1262,1266,1269,1735,1738,1826,1829,1841,1845,1852,2030,2033,2154,2168,2172,2177,2185,2188,2196,2203,2210,2212,2216,2223,2259,2262,2266,2272,2341,2345,2351,2357,2418,2435,2439,2442,2568,2581,2588,2599,2601,2605,2617,2623,2886,2889,2939,2943,2970,2973,3055,3058,3210,3216,3220,3274,3284,3292,3296,3299,3378,3385,3387,3391,3394,3613,3642,3646,3651,3655,3746,3754,3759,3854,3861,3866,3954,3959,4034,4051,4055,4058,4129,4142,4160,4162,4166,4169,4173,4176,4234,4255,4258,4263,4306,4312,4317,4371,4376,4381,4433,4442,4447,4499,4516,4521,4564,4571,4576,4630,4636,4641,4684,4687,4692,4735,4738,4743,4797,4803,4807,5017,5021,5024,5028,5787,5790,5822,5829,5833,5836,6416,6427,6429,6433,6440,6444,6450,6455,6613,6619,6623,6685,6689,6692,7882,7885,7908,7911,7938,7942,7951,7999,8002,8008,8014,8061,8076,8080,8083,8255,8259,8338,8345,8347,8351,8354,8359,8907,8910,8932,8934,8938,8941,8945,8984,8988,9010,9014,9017,9051,9061,9063,9067,9071,9081,9085,9091,9095,9101,9105,9116,9120,9133,9137,9147,9151,9167,9171,9178,9182,9198,9202,9209,9211,9215,9218,11198,11212,11220],[10,11,12],"p",{},"Seedance 2.0은 ByteDance의 가장 강력한 AI 비디오 생성 모델입니다 — 멀티모달 참조, 네이티브 오디오 생성, 시네마틱 카메라 제어, 최대 1080p에서 4~15초 비디오 생성을 지원합니다. 이 튜토리얼에서는 API Key 발급부터 첫 번째 비디오 다운로드까지 Python으로 전체 API 워크플로우를 안내합니다.",[10,14,15],{},"이 튜토리얼을 마치면 텍스트-투-비디오, 이미지-투-비디오, 비동기 polling, webhook 처리, 오류 복구를 위한 바로 실행할 수 있는 코드를 갖게 됩니다. 모든 코드 예제는 실제 API에서 테스트되었습니다.",[17,18,19],"blockquote",{},[10,20,21,25,26,30],{},[22,23,24],"strong",{},"참고 — Seedance 2.0 vs 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],{},"비동기로 결과 polling",[72,82,83],{},"프로덕션 코드처럼 오류와 재시도 처리",[72,85,86],{},"webhook으로 결과 수신 (polling 불필요)",[72,88,89],{},"진행 중인 작업 취소",[91,92,94],"h3",{"id":93},"사전-요구사항","사전 요구사항",[96,97,98,108,118],"ul",{},[72,99,100,103,104,107],{},[22,101,102],{},"Python 3.8+"," (",[27,105,106],{},"python3 --version","으로 확인)",[72,109,110,113,114,117],{},[22,111,112],{},"requests"," 라이브러리 (",[27,115,116],{},"pip install requests",")",[72,119,120,123],{},[22,121,122],{},"EvoLink API Key"," (무료 가입 — 다음 섹션에서 발급 방법을 안내합니다)",[10,125,126],{},"GPU도, Docker도, 복잡한 설정도 필요 없습니다. Python과 API Key만 있으면 됩니다.",[17,128,129,135],{},[10,130,131,134],{},[22,132,133],{},"팁:"," 프로덕션 앱을 개발 중이라면 가상 환경으로 의존성을 격리하는 것을 권장합니다:",[136,137,142],"pre",{"className":138,"code":139,"language":140,"meta":141,"style":141},"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,143,144,164,176,185],{"__ignoreMap":141},[145,146,149,153,157,161],"span",{"class":147,"line":148},"line",1,[145,150,152],{"class":151},"svObZ","python3",[145,154,156],{"class":155},"sDLfK"," -m",[145,158,160],{"class":159},"sU2Wk"," venv",[145,162,163],{"class":159}," seedance-env\n",[145,165,167,169,172],{"class":147,"line":166},2,[145,168,61],{"class":155},[145,170,171],{"class":159}," seedance-env/bin/activate",[145,173,175],{"class":174},"sAwPA","  # macOS/Linux\n",[145,177,179,182],{"class":147,"line":178},3,[145,180,181],{"class":151},"seedance-env\\Scripts\\activate",[145,183,184],{"class":174},"     # Windows\n",[145,186,188,191,194,197],{"class":147,"line":187},4,[145,189,190],{"class":151},"pip",[145,192,193],{"class":159}," install",[145,195,196],{"class":159}," requests",[145,198,199],{"class":159}," flask\n",[44,201],{},[47,203,205],{"id":204},"api-key-발급","API Key 발급",[10,207,208,209,214],{},"Seedance 2.0은 ",[36,210,213],{"href":211,"rel":212},"https://evolink.ai",[40],"EvoLink","를 통해 제공됩니다. EvoLink는 하나의 API Key로 Seedance 2.0, Kling 등 여러 AI 비디오 모델에 통합 접근할 수 있는 API 게이트웨이입니다.",[10,216,217],{},"시작하는 방법:",[69,219,220,227,233,239],{},[72,221,222,226],{},[36,223,225],{"href":38,"rel":224},[40],"evolink.ai/early-access","에서 계정 생성",[72,228,229,232],{},[22,230,231],{},"Dashboard → API Keys","로 이동",[72,234,235,238],{},[22,236,237],{},"Create New Key"," 클릭",[72,240,241,242,245],{},"Key 복사 — ",[27,243,244],{},"sk-","로 시작합니다",[10,247,248],{},"Key를 안전하게 보관하세요. 버전 관리에 커밋하지 마세요. 환경 변수를 사용합니다:",[136,250,252],{"className":138,"code":251,"language":140,"meta":141,"style":141},"export EVOLINK_API_KEY=\"sk-your-api-key-here\"\n",[27,253,254],{"__ignoreMap":141},[145,255,256,260,264,267],{"class":147,"line":148},[145,257,259],{"class":258},"snl16","export",[145,261,263],{"class":262},"s95oV"," EVOLINK_API_KEY",[145,265,266],{"class":258},"=",[145,268,269],{"class":159},"\"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],{},"필요한 유일한 의존성을 설치합니다:",[136,316,318],{"className":138,"code":317,"language":140,"meta":141,"style":141},"pip install requests\n",[27,319,320],{"__ignoreMap":141},[145,321,322,324,326],{"class":147,"line":148},[145,323,190],{"class":151},[145,325,193],{"class":159},[145,327,328],{"class":159}," requests\n",[10,330,331,334],{},[27,332,333],{},"seedance_tutorial.py","라는 파일을 만들고 다음 기본 코드를 추가하세요. 이 튜토리얼의 모든 예제는 이 기반 위에 구축됩니다:",[136,336,340],{"className":337,"code":338,"language":339,"meta":141,"style":141},"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,341,342,349,356,363,370,376,382,406,417,428,452,463],{"__ignoreMap":141},[145,343,344,347],{"class":147,"line":148},[145,345,346],{"class":258},"import",[145,348,328],{"class":262},[145,350,351,353],{"class":147,"line":166},[145,352,346],{"class":258},[145,354,355],{"class":262}," time\n",[145,357,358,360],{"class":147,"line":178},[145,359,346],{"class":258},[145,361,362],{"class":262}," os\n",[145,364,365,367],{"class":147,"line":187},[145,366,346],{"class":258},[145,368,369],{"class":262}," json\n",[145,371,373],{"class":147,"line":372},5,[145,374,375],{"emptyLinePlaceholder":57},"\n",[145,377,379],{"class":147,"line":378},6,[145,380,381],{"class":174},"# ── 설정 ─────────────────────────────────────────────────────\n",[145,383,385,388,391,394,397,400,403],{"class":147,"line":384},7,[145,386,387],{"class":155},"API_KEY",[145,389,390],{"class":258}," =",[145,392,393],{"class":262}," os.getenv(",[145,395,396],{"class":159},"\"EVOLINK_API_KEY\"",[145,398,399],{"class":262},", ",[145,401,402],{"class":159},"\"sk-your-api-key-here\"",[145,404,405],{"class":262},")\n",[145,407,409,412,414],{"class":147,"line":408},8,[145,410,411],{"class":155},"BASE_URL",[145,413,390],{"class":258},[145,415,416],{"class":159}," \"https://api.evolink.ai/v1\"\n",[145,418,420,423,425],{"class":147,"line":419},9,[145,421,422],{"class":155},"HEADERS",[145,424,390],{"class":258},[145,426,427],{"class":262}," {\n",[145,429,431,434,437,440,443,446,449],{"class":147,"line":430},10,[145,432,433],{"class":159},"    \"Authorization\"",[145,435,436],{"class":262},": ",[145,438,439],{"class":258},"f",[145,441,442],{"class":159},"\"Bearer ",[145,444,445],{"class":155},"{API_KEY}",[145,447,448],{"class":159},"\"",[145,450,451],{"class":262},",\n",[145,453,455,458,460],{"class":147,"line":454},11,[145,456,457],{"class":159},"    \"Content-Type\"",[145,459,436],{"class":262},[145,461,462],{"class":159},"\"application/json\"\n",[145,464,466],{"class":147,"line":465},12,[145,467,468],{"class":262},"}\n",[10,470,471],{},"한 줄씩 설명합니다:",[96,473,474,482,493],{},[72,475,476,481],{},[22,477,478],{},[27,479,480],{},"os.getenv(\"EVOLINK_API_KEY\", \"sk-your-api-key-here\")"," — 환경 변수에서 API Key를 읽습니다. 두 번째 인자는 기본값입니다(로컬 테스트 시에만 실제 Key로 교체하세요).",[72,483,484,488,489,492],{},[22,485,486],{},[27,487,411],{}," — 모든 EvoLink API 엔드포인트의 루트 URL입니다. 모든 요청은 ",[27,490,491],{},"https://api.evolink.ai/v1/...","으로 전송됩니다.",[72,494,495,499,500,503,504,507],{},[22,496,497],{},[27,498,422],{}," — 모든 요청에 포함되는 두 개의 헤더: ",[27,501,502],{},"Authorization","은 Bearer Token 방식으로 API Key를 전달하고, ",[27,505,506],{},"Content-Type","은 서버에 JSON을 보내고 있음을 알립니다.",[10,509,510],{},"다음으로 재사용 가능한 헬퍼 함수를 추가합니다:",[136,512,514],{"className":337,"code":513,"language":339,"meta":141,"style":141},"# ── 재사용 가능한 Polling 헬퍼 ────────────────────────────────\ndef wait_for_video(task_id, poll_interval=10, timeout=600):\n    \"\"\"\n    비디오 생성 작업을 완료 또는 실패할 때까지 polling합니다.\n    \n    Args:\n        task_id: 생성 엔드포인트가 반환한 작업 ID.\n        poll_interval: polling 간격 초(기본 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        # 다음 polling 전 대기\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,950,956,961,967,973,985,990],{"__ignoreMap":141},[145,517,518],{"class":147,"line":148},[145,519,520],{"class":174},"# ── 재사용 가능한 Polling 헬퍼 ────────────────────────────────\n",[145,522,523,526,529,532,534,537,540,542,545],{"class":147,"line":166},[145,524,525],{"class":258},"def",[145,527,528],{"class":151}," wait_for_video",[145,530,531],{"class":262},"(task_id, poll_interval",[145,533,266],{"class":258},[145,535,536],{"class":155},"10",[145,538,539],{"class":262},", timeout",[145,541,266],{"class":258},[145,543,544],{"class":155},"600",[145,546,547],{"class":262},"):\n",[145,549,550],{"class":147,"line":178},[145,551,552],{"class":159},"    \"\"\"\n",[145,554,555],{"class":147,"line":187},[145,556,557],{"class":159},"    비디오 생성 작업을 완료 또는 실패할 때까지 polling합니다.\n",[145,559,560],{"class":147,"line":372},[145,561,562],{"class":159},"    \n",[145,564,565],{"class":147,"line":378},[145,566,567],{"class":159},"    Args:\n",[145,569,570],{"class":147,"line":384},[145,571,572],{"class":159},"        task_id: 생성 엔드포인트가 반환한 작업 ID.\n",[145,574,575],{"class":147,"line":408},[145,576,577],{"class":159},"        poll_interval: polling 간격 초(기본 10).\n",[145,579,580],{"class":147,"line":419},[145,581,582],{"class":159},"        timeout: 최대 대기 시간 초(기본 600).\n",[145,584,585],{"class":147,"line":430},[145,586,562],{"class":159},[145,588,589],{"class":147,"line":454},[145,590,591],{"class":159},"    Returns:\n",[145,593,594],{"class":147,"line":465},[145,595,596],{"class":159},"        dict: 비디오 URL이 포함된 완료된 작업 응답.\n",[145,598,600],{"class":147,"line":599},13,[145,601,562],{"class":159},[145,603,605],{"class":147,"line":604},14,[145,606,607],{"class":159},"    Raises:\n",[145,609,611],{"class":147,"line":610},15,[145,612,613],{"class":159},"        TimeoutError: 작업이 타임아웃 내에 완료되지 않은 경우.\n",[145,615,617],{"class":147,"line":616},16,[145,618,619],{"class":159},"        RuntimeError: 작업이 실패한 경우.\n",[145,621,623],{"class":147,"line":622},17,[145,624,552],{"class":159},[145,626,628,631,633],{"class":147,"line":627},18,[145,629,630],{"class":262},"    elapsed ",[145,632,266],{"class":258},[145,634,635],{"class":155}," 0\n",[145,637,639,642,645,648],{"class":147,"line":638},19,[145,640,641],{"class":258},"    while",[145,643,644],{"class":262}," elapsed ",[145,646,647],{"class":258},"\u003C",[145,649,650],{"class":262}," timeout:\n",[145,652,654],{"class":147,"line":653},20,[145,655,656],{"class":174},"        # GET 요청으로 작업의 현재 상태 확인\n",[145,658,660,663,665],{"class":147,"line":659},21,[145,661,662],{"class":262},"        response ",[145,664,266],{"class":258},[145,666,667],{"class":262}," requests.get(\n",[145,669,671,674,676,679,682,685,688,691,693],{"class":147,"line":670},22,[145,672,673],{"class":258},"            f",[145,675,448],{"class":159},[145,677,678],{"class":155},"{BASE_URL}",[145,680,681],{"class":159},"/tasks/",[145,683,684],{"class":155},"{",[145,686,687],{"class":262},"task_id",[145,689,690],{"class":155},"}",[145,692,448],{"class":159},[145,694,451],{"class":262},[145,696,698,702,704],{"class":147,"line":697},23,[145,699,701],{"class":700},"s9osk","            headers",[145,703,266],{"class":258},[145,705,706],{"class":155},"HEADERS\n",[145,708,710],{"class":147,"line":709},24,[145,711,712],{"class":262},"        )\n",[145,714,716],{"class":147,"line":715},25,[145,717,718],{"class":174},"        # HTTP 상태 코드가 오류를 나타내면 예외 발생\n",[145,720,722],{"class":147,"line":721},26,[145,723,724],{"class":262},"        response.raise_for_status()\n",[145,726,728,731,733],{"class":147,"line":727},27,[145,729,730],{"class":262},"        task ",[145,732,266],{"class":258},[145,734,735],{"class":262}," response.json()\n",[145,737,739],{"class":147,"line":738},28,[145,740,375],{"emptyLinePlaceholder":57},[145,742,744],{"class":147,"line":743},29,[145,745,746],{"class":174},"        # 응답에서 상태와 진행률 추출\n",[145,748,750,753,755,758,761],{"class":147,"line":749},30,[145,751,752],{"class":262},"        status ",[145,754,266],{"class":258},[145,756,757],{"class":262}," task[",[145,759,760],{"class":159},"\"status\"",[145,762,763],{"class":262},"]\n",[145,765,767,770,772,775,778,780,783],{"class":147,"line":766},31,[145,768,769],{"class":262},"        progress ",[145,771,266],{"class":258},[145,773,774],{"class":262}," task.get(",[145,776,777],{"class":159},"\"progress\"",[145,779,399],{"class":262},[145,781,782],{"class":155},"0",[145,784,405],{"class":262},[145,786,788,791,794,796,799,801,804,806,809,811,814,816,819,821,824,826,829],{"class":147,"line":787},32,[145,789,790],{"class":155},"        print",[145,792,793],{"class":262},"(",[145,795,439],{"class":258},[145,797,798],{"class":159},"\"  [",[145,800,684],{"class":155},[145,802,803],{"class":262},"elapsed",[145,805,690],{"class":155},[145,807,808],{"class":159},"s] Status: ",[145,810,684],{"class":155},[145,812,813],{"class":262},"status",[145,815,690],{"class":155},[145,817,818],{"class":159}," | Progress: ",[145,820,684],{"class":155},[145,822,823],{"class":262},"progress",[145,825,690],{"class":155},[145,827,828],{"class":159},"%\"",[145,830,405],{"class":262},[145,832,834],{"class":147,"line":833},33,[145,835,375],{"emptyLinePlaceholder":57},[145,837,839],{"class":147,"line":838},34,[145,840,841],{"class":174},"        # 종료 상태 확인\n",[145,843,845,848,851,854,857],{"class":147,"line":844},35,[145,846,847],{"class":258},"        if",[145,849,850],{"class":262}," status ",[145,852,853],{"class":258},"==",[145,855,856],{"class":159}," \"completed\"",[145,858,859],{"class":262},":\n",[145,861,863,866],{"class":147,"line":862},36,[145,864,865],{"class":258},"            return",[145,867,868],{"class":262}," task\n",[145,870,872,875,877,879,882],{"class":147,"line":871},37,[145,873,874],{"class":258},"        elif",[145,876,850],{"class":262},[145,878,853],{"class":258},[145,880,881],{"class":159}," \"failed\"",[145,883,859],{"class":262},[145,885,887,890,892,894,897],{"class":147,"line":886},38,[145,888,889],{"class":262},"            error_info ",[145,891,266],{"class":258},[145,893,774],{"class":262},[145,895,896],{"class":159},"\"error\"",[145,898,899],{"class":262},", {})\n",[145,901,903,906,909],{"class":147,"line":902},39,[145,904,905],{"class":258},"            raise",[145,907,908],{"class":155}," RuntimeError",[145,910,911],{"class":262},"(\n",[145,913,915,918,921,923,925,927,930,932,935,938,940,943,945,947],{"class":147,"line":914},40,[145,916,917],{"class":258},"                f",[145,919,920],{"class":159},"\"Task ",[145,922,684],{"class":155},[145,924,687],{"class":262},[145,926,690],{"class":155},[145,928,929],{"class":159}," failed: ",[145,931,684],{"class":155},[145,933,934],{"class":262},"error_info.get(",[145,936,937],{"class":159},"'message'",[145,939,399],{"class":262},[145,941,942],{"class":159},"'Unknown error'",[145,944,117],{"class":262},[145,946,690],{"class":155},[145,948,949],{"class":159},"\"\n",[145,951,953],{"class":147,"line":952},41,[145,954,955],{"class":262},"            )\n",[145,957,959],{"class":147,"line":958},42,[145,960,375],{"emptyLinePlaceholder":57},[145,962,964],{"class":147,"line":963},43,[145,965,966],{"class":174},"        # 다음 polling 전 대기\n",[145,968,970],{"class":147,"line":969},44,[145,971,972],{"class":262},"        time.sleep(poll_interval)\n",[145,974,976,979,982],{"class":147,"line":975},45,[145,977,978],{"class":262},"        elapsed ",[145,980,981],{"class":258},"+=",[145,983,984],{"class":262}," poll_interval\n",[145,986,988],{"class":147,"line":987},46,[145,989,375],{"emptyLinePlaceholder":57},[145,991,993,996,999,1001,1003,1005,1007,1009,1011,1014,1016,1019,1021,1024],{"class":147,"line":992},47,[145,994,995],{"class":258},"    raise",[145,997,998],{"class":155}," TimeoutError",[145,1000,793],{"class":262},[145,1002,439],{"class":258},[145,1004,920],{"class":159},[145,1006,684],{"class":155},[145,1008,687],{"class":262},[145,1010,690],{"class":155},[145,1012,1013],{"class":159}," timed out after ",[145,1015,684],{"class":155},[145,1017,1018],{"class":262},"timeout",[145,1020,690],{"class":155},[145,1022,1023],{"class":159},"s\"",[145,1025,405],{"class":262},[10,1027,1028],{},"이 함수의 주요 설계 결정:",[96,1030,1031,1039,1047,1055],{},[72,1032,1033,1038],{},[22,1034,1035],{},[27,1036,1037],{},"poll_interval=10"," — 10초가 최적입니다. 더 빠르면 API 할당량을 낭비하고, 더 느리면 워크플로우가 지연됩니다.",[72,1040,1041,1046],{},[22,1042,1043],{},[27,1044,1045],{},"timeout=600"," — 10분은 충분히 여유 있습니다. 대부분의 비디오는 30~120초 내에 완료되지만, 큐 혼잡 같은 극단적인 경우를 커버합니다.",[72,1048,1049,1054],{},[22,1050,1051],{},[27,1052,1053],{},"response.raise_for_status()"," — HTTP 오류(4xx/5xx)를 Python 예외로 변환하여 조용히 넘어가지 않게 합니다.",[72,1056,1057,1060,1061,1064],{},[22,1058,1059],{},"진행률 출력"," — ",[27,1062,1063],{},"[elapsed]s"," 접두사로 시간을 연관시킬 수 있습니다. 느린 생성을 디버깅할 때 유용합니다.",[136,1066,1068],{"className":337,"code":1067,"language":339,"meta":141,"style":141},"# ── 헬퍼: 비디오 다운로드 ─────────────────────────────────────\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,1069,1070,1075,1092,1097,1121,1141,1146,1169,1193,1198],{"__ignoreMap":141},[145,1071,1072],{"class":147,"line":148},[145,1073,1074],{"class":174},"# ── 헬퍼: 비디오 다운로드 ─────────────────────────────────────\n",[145,1076,1077,1079,1082,1085,1087,1090],{"class":147,"line":166},[145,1078,525],{"class":258},[145,1080,1081],{"class":151}," download_video",[145,1083,1084],{"class":262},"(url, filename",[145,1086,266],{"class":258},[145,1088,1089],{"class":159},"\"output.mp4\"",[145,1091,547],{"class":262},[145,1093,1094],{"class":147,"line":178},[145,1095,1096],{"class":159},"    \"\"\"URL에서 비디오 파일을 다운로드합니다.\"\"\"\n",[145,1098,1099,1102,1104,1106,1109,1111,1114,1116,1119],{"class":147,"line":187},[145,1100,1101],{"class":155},"    print",[145,1103,793],{"class":262},[145,1105,439],{"class":258},[145,1107,1108],{"class":159},"\"Downloading video to ",[145,1110,684],{"class":155},[145,1112,1113],{"class":262},"filename",[145,1115,690],{"class":155},[145,1117,1118],{"class":159},"...\"",[145,1120,405],{"class":262},[145,1122,1123,1126,1128,1131,1134,1136,1139],{"class":147,"line":372},[145,1124,1125],{"class":262},"    resp ",[145,1127,266],{"class":258},[145,1129,1130],{"class":262}," requests.get(url, ",[145,1132,1133],{"class":700},"stream",[145,1135,266],{"class":258},[145,1137,1138],{"class":155},"True",[145,1140,405],{"class":262},[145,1142,1143],{"class":147,"line":378},[145,1144,1145],{"class":262},"    resp.raise_for_status()\n",[145,1147,1148,1151,1154,1157,1160,1163,1166],{"class":147,"line":384},[145,1149,1150],{"class":258},"    with",[145,1152,1153],{"class":155}," open",[145,1155,1156],{"class":262},"(filename, ",[145,1158,1159],{"class":159},"\"wb\"",[145,1161,1162],{"class":262},") ",[145,1164,1165],{"class":258},"as",[145,1167,1168],{"class":262}," f:\n",[145,1170,1171,1174,1177,1180,1183,1186,1188,1191],{"class":147,"line":408},[145,1172,1173],{"class":258},"        for",[145,1175,1176],{"class":262}," chunk ",[145,1178,1179],{"class":258},"in",[145,1181,1182],{"class":262}," resp.iter_content(",[145,1184,1185],{"class":700},"chunk_size",[145,1187,266],{"class":258},[145,1189,1190],{"class":155},"8192",[145,1192,547],{"class":262},[145,1194,1195],{"class":147,"line":419},[145,1196,1197],{"class":262},"            f.write(chunk)\n",[145,1199,1200,1202,1204,1206,1209,1211,1213,1215,1217,1219,1222,1225,1228,1231,1233,1236],{"class":147,"line":430},[145,1201,1101],{"class":155},[145,1203,793],{"class":262},[145,1205,439],{"class":258},[145,1207,1208],{"class":159},"\"Saved: ",[145,1210,684],{"class":155},[145,1212,1113],{"class":262},[145,1214,690],{"class":155},[145,1216,103],{"class":159},[145,1218,684],{"class":155},[145,1220,1221],{"class":262},"os.path.getsize(filename) ",[145,1223,1224],{"class":258},"/",[145,1226,1227],{"class":155}," 1024",[145,1229,1230],{"class":258},":.0f",[145,1232,690],{"class":155},[145,1234,1235],{"class":159}," KB)\"",[145,1237,405],{"class":262},[10,1239,1240,1241,1244,1245,1247],{},"이 함수는 전체 비디오를 메모리에 로드하는 대신 8 KB 청크 단위로 스트리밍 다운로드합니다. 이 점이 중요한데, 생성된 비디오는 10~50 MB가 될 수 있습니다. ",[27,1242,1243],{},"stream=True"," 파라미터는 ",[27,1246,112],{},"에 점진적으로 다운로드하도록 지시합니다.",[10,1249,1250],{},"이 세 가지 — 설정, polling, 다운로드 — 가 기본 뼈대입니다. 아래의 모든 코드 예제에서 사용됩니다. 반복하지 않고 새로운 payload만 보여드리겠습니다.",[10,1252,1253,1254,1259],{},"전체 API 레퍼런스는 ",[36,1255,1258],{"href":1256,"rel":1257},"https://seedance2api.app/docs/video-generation",[40],"비디오 생성 문서","를 참조하세요.",[44,1261],{},[47,1263,1265],{"id":1264},"첫-번째-비디오-생성-텍스트-투-비디오","첫 번째 비디오 생성 (텍스트-투-비디오)",[10,1267,1268],{},"비디오를 생성해 봅시다. 스크립트에 다음 코드를 추가하세요:",[136,1270,1272],{"className":337,"code":1271,"language":339,"meta":141,"style":141},"# ── 텍스트-투-비디오 ──────────────────────────────────────────\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    # 비디오가 준비될 때까지 polling\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,1273,1274,1279,1289,1298,1314,1322,1327,1332,1337,1342,1358,1374,1390,1402,1407,1411,1422,1432,1450,1465,1478,1483,1491,1504,1508,1513,1541,1573,1604,1608,1613,1629,1633,1638,1657,1684,1694,1698,1706,1710,1714,1730],{"__ignoreMap":141},[145,1275,1276],{"class":147,"line":148},[145,1277,1278],{"class":174},"# ── 텍스트-투-비디오 ──────────────────────────────────────────\n",[145,1280,1281,1283,1286],{"class":147,"line":166},[145,1282,525],{"class":258},[145,1284,1285],{"class":151}," text_to_video",[145,1287,1288],{"class":262},"():\n",[145,1290,1291,1294,1296],{"class":147,"line":178},[145,1292,1293],{"class":262},"    payload ",[145,1295,266],{"class":258},[145,1297,427],{"class":262},[145,1299,1300,1303,1305,1308,1311],{"class":147,"line":187},[145,1301,1302],{"class":159},"        \"model\"",[145,1304,436],{"class":262},[145,1306,1307],{"class":159},"\"seedance-2.0\"",[145,1309,1310],{"class":262},",          ",[145,1312,1313],{"class":174},"# 사용할 AI 모델\n",[145,1315,1316,1319],{"class":147,"line":372},[145,1317,1318],{"class":159},"        \"prompt\"",[145,1320,1321],{"class":262},": (\n",[145,1323,1324],{"class":147,"line":378},[145,1325,1326],{"class":159},"            \"A golden retriever puppy chases a butterfly through \"\n",[145,1328,1329],{"class":147,"line":384},[145,1330,1331],{"class":159},"            \"a sunlit meadow. The camera follows the puppy with a \"\n",[145,1333,1334],{"class":147,"line":408},[145,1335,1336],{"class":159},"            \"smooth tracking shot as wildflowers sway in the breeze.\"\n",[145,1338,1339],{"class":147,"line":419},[145,1340,1341],{"class":262},"        ),\n",[145,1343,1344,1347,1349,1352,1355],{"class":147,"line":430},[145,1345,1346],{"class":159},"        \"duration\"",[145,1348,436],{"class":262},[145,1350,1351],{"class":155},"5",[145,1353,1354],{"class":262},",                     ",[145,1356,1357],{"class":174},"# 비디오 길이: 4-15초\n",[145,1359,1360,1363,1365,1368,1371],{"class":147,"line":454},[145,1361,1362],{"class":159},"        \"quality\"",[145,1364,436],{"class":262},[145,1366,1367],{"class":159},"\"720p\"",[145,1369,1370],{"class":262},",                 ",[145,1372,1373],{"class":174},"# 해상도: 480p, 720p, 1080p\n",[145,1375,1376,1379,1381,1384,1387],{"class":147,"line":465},[145,1377,1378],{"class":159},"        \"aspect_ratio\"",[145,1380,436],{"class":262},[145,1382,1383],{"class":159},"\"16:9\"",[145,1385,1386],{"class":262},",            ",[145,1388,1389],{"class":174},"# 표준 와이드스크린\n",[145,1391,1392,1395,1397,1399],{"class":147,"line":599},[145,1393,1394],{"class":159},"        \"generate_audio\"",[145,1396,436],{"class":262},[145,1398,1138],{"class":155},[145,1400,1401],{"class":174},"             # AI가 매칭되는 오디오 생성\n",[145,1403,1404],{"class":147,"line":604},[145,1405,1406],{"class":262},"    }\n",[145,1408,1409],{"class":147,"line":610},[145,1410,375],{"emptyLinePlaceholder":57},[145,1412,1413,1415,1417,1420],{"class":147,"line":616},[145,1414,1101],{"class":155},[145,1416,793],{"class":262},[145,1418,1419],{"class":159},"\"Submitting text-to-video request...\"",[145,1421,405],{"class":262},[145,1423,1424,1427,1429],{"class":147,"line":622},[145,1425,1426],{"class":262},"    response ",[145,1428,266],{"class":258},[145,1430,1431],{"class":262}," requests.post(\n",[145,1433,1434,1437,1439,1441,1444,1447],{"class":147,"line":627},[145,1435,1436],{"class":258},"        f",[145,1438,448],{"class":159},[145,1440,678],{"class":155},[145,1442,1443],{"class":159},"/videos/generations\"",[145,1445,1446],{"class":262},",  ",[145,1448,1449],{"class":174},"# 비디오 생성 엔드포인트\n",[145,1451,1452,1455,1457,1459,1462],{"class":147,"line":638},[145,1453,1454],{"class":700},"        headers",[145,1456,266],{"class":258},[145,1458,422],{"class":155},[145,1460,1461],{"class":262},",                   ",[145,1463,1464],{"class":174},"# 인증 + content-type 헤더\n",[145,1466,1467,1470,1472,1475],{"class":147,"line":653},[145,1468,1469],{"class":700},"        json",[145,1471,266],{"class":258},[145,1473,1474],{"class":262},"payload                       ",[145,1476,1477],{"class":174},"# 자동으로 JSON 직렬화\n",[145,1479,1480],{"class":147,"line":659},[145,1481,1482],{"class":262},"    )\n",[145,1484,1485,1488],{"class":147,"line":670},[145,1486,1487],{"class":262},"    response.raise_for_status()            ",[145,1489,1490],{"class":174},"# 200이 아니면 예외 발생\n",[145,1492,1493,1496,1498,1501],{"class":147,"line":697},[145,1494,1495],{"class":262},"    task ",[145,1497,266],{"class":258},[145,1499,1500],{"class":262}," response.json()                 ",[145,1502,1503],{"class":174},"# JSON 응답 파싱\n",[145,1505,1506],{"class":147,"line":709},[145,1507,375],{"emptyLinePlaceholder":57},[145,1509,1510],{"class":147,"line":715},[145,1511,1512],{"class":174},"    # 응답의 주요 정보 출력\n",[145,1514,1515,1517,1519,1521,1524,1526,1529,1532,1535,1537,1539],{"class":147,"line":721},[145,1516,1101],{"class":155},[145,1518,793],{"class":262},[145,1520,439],{"class":258},[145,1522,1523],{"class":159},"\"Task created: ",[145,1525,684],{"class":155},[145,1527,1528],{"class":262},"task[",[145,1530,1531],{"class":159},"'id'",[145,1533,1534],{"class":262},"]",[145,1536,690],{"class":155},[145,1538,448],{"class":159},[145,1540,405],{"class":262},[145,1542,1543,1545,1547,1549,1552,1554,1556,1559,1562,1565,1567,1569,1571],{"class":147,"line":727},[145,1544,1101],{"class":155},[145,1546,793],{"class":262},[145,1548,439],{"class":258},[145,1550,1551],{"class":159},"\"Estimated time: ",[145,1553,684],{"class":155},[145,1555,1528],{"class":262},[145,1557,1558],{"class":159},"'task_info'",[145,1560,1561],{"class":262},"][",[145,1563,1564],{"class":159},"'estimated_time'",[145,1566,1534],{"class":262},[145,1568,690],{"class":155},[145,1570,1023],{"class":159},[145,1572,405],{"class":262},[145,1574,1575,1577,1579,1581,1584,1586,1588,1591,1593,1596,1598,1600,1602],{"class":147,"line":738},[145,1576,1101],{"class":155},[145,1578,793],{"class":262},[145,1580,439],{"class":258},[145,1582,1583],{"class":159},"\"Credits reserved: ",[145,1585,684],{"class":155},[145,1587,1528],{"class":262},[145,1589,1590],{"class":159},"'usage'",[145,1592,1561],{"class":262},[145,1594,1595],{"class":159},"'credits_reserved'",[145,1597,1534],{"class":262},[145,1599,690],{"class":155},[145,1601,448],{"class":159},[145,1603,405],{"class":262},[145,1605,1606],{"class":147,"line":743},[145,1607,375],{"emptyLinePlaceholder":57},[145,1609,1610],{"class":147,"line":749},[145,1611,1612],{"class":174},"    # 비디오가 준비될 때까지 polling\n",[145,1614,1615,1618,1620,1623,1626],{"class":147,"line":766},[145,1616,1617],{"class":262},"    result ",[145,1619,266],{"class":258},[145,1621,1622],{"class":262}," wait_for_video(task[",[145,1624,1625],{"class":159},"\"id\"",[145,1627,1628],{"class":262},"])\n",[145,1630,1631],{"class":147,"line":787},[145,1632,375],{"emptyLinePlaceholder":57},[145,1634,1635],{"class":147,"line":833},[145,1636,1637],{"class":174},"    # results 배열에 하나 이상의 비디오 URL 포함\n",[145,1639,1640,1643,1645,1648,1651,1653,1655],{"class":147,"line":838},[145,1641,1642],{"class":262},"    video_url ",[145,1644,266],{"class":258},[145,1646,1647],{"class":262}," result[",[145,1649,1650],{"class":159},"\"results\"",[145,1652,1561],{"class":262},[145,1654,782],{"class":155},[145,1656,763],{"class":262},[145,1658,1659,1661,1663,1665,1667,1670,1673,1675,1678,1680,1682],{"class":147,"line":844},[145,1660,1101],{"class":155},[145,1662,793],{"class":262},[145,1664,439],{"class":258},[145,1666,448],{"class":159},[145,1668,1669],{"class":155},"\\n",[145,1671,1672],{"class":159},"Video URL: ",[145,1674,684],{"class":155},[145,1676,1677],{"class":262},"video_url",[145,1679,690],{"class":155},[145,1681,448],{"class":159},[145,1683,405],{"class":262},[145,1685,1686,1689,1692],{"class":147,"line":862},[145,1687,1688],{"class":262},"    download_video(video_url, ",[145,1690,1691],{"class":159},"\"my_first_video.mp4\"",[145,1693,405],{"class":262},[145,1695,1696],{"class":147,"line":871},[145,1697,375],{"emptyLinePlaceholder":57},[145,1699,1700,1703],{"class":147,"line":886},[145,1701,1702],{"class":258},"    return",[145,1704,1705],{"class":262}," result\n",[145,1707,1708],{"class":147,"line":902},[145,1709,375],{"emptyLinePlaceholder":57},[145,1711,1712],{"class":147,"line":914},[145,1713,375],{"emptyLinePlaceholder":57},[145,1715,1716,1719,1722,1725,1728],{"class":147,"line":952},[145,1717,1718],{"class":258},"if",[145,1720,1721],{"class":155}," __name__",[145,1723,1724],{"class":258}," ==",[145,1726,1727],{"class":159}," \"__main__\"",[145,1729,859],{"class":262},[145,1731,1732],{"class":147,"line":958},[145,1733,1734],{"class":262},"    text_to_video()\n",[10,1736,1737],{},"payload의 각 파라미터를 설명합니다:",[96,1739,1740,1755,1767,1775,1795,1815],{},[72,1741,1742,1747,1748,1751,1752,1754],{},[22,1743,1744],{},[27,1745,1746],{},"model"," — 사용할 Seedance 모델. 최신 버전은 ",[27,1749,1750],{},"seedance-2.0","으로 설정하고, 해당 지역에서 2.0을 아직 사용할 수 없다면 ",[27,1753,29],{},"를 사용하세요.",[72,1756,1757,1762,1763,1259],{},[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","은 TikTok/Reels/Shorts, ",[27,1812,1813],{},"1:1","은 Instagram 피드에 적합합니다.",[72,1816,1817,1060,1822,1825],{},[22,1818,1819],{},[27,1820,1821],{},"generate_audio",[27,1823,1824],{},"true","로 설정하면 Seedance가 시각적 콘텐츠에 맞는 환경음과 음악을 생성합니다. 생성 시간이 약 2초 추가됩니다.",[10,1827,1828],{},"실행:",[136,1830,1832],{"className":138,"code":1831,"language":140,"meta":141,"style":141},"python seedance_tutorial.py\n",[27,1833,1834],{"__ignoreMap":141},[145,1835,1836,1838],{"class":147,"line":148},[145,1837,339],{"class":151},[145,1839,1840],{"class":159}," seedance_tutorial.py\n",[91,1842,1844],{"id":1843},"api-응답-내용","API 응답 내용",[10,1846,1847,1848,1851],{},"생성 요청을 제출하면 즉시 ",[22,1849,1850],{},"task 객체","를 받습니다 — 비디오는 아직 준비되지 않았습니다. 실제 응답은 다음과 같습니다:",[136,1853,1857],{"className":1854,"code":1855,"language":1856,"meta":141,"style":141},"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,1858,1859,1864,1876,1888,1899,1911,1922,1934,1942,1953,1963,1968,1980,1987,1999,2011,2021,2026],{"__ignoreMap":141},[145,1860,1861],{"class":147,"line":148},[145,1862,1863],{"class":262},"{\n",[145,1865,1866,1869,1871,1874],{"class":147,"line":166},[145,1867,1868],{"class":155},"  \"created\"",[145,1870,436],{"class":262},[145,1872,1873],{"class":155},"1772203771",[145,1875,451],{"class":262},[145,1877,1878,1881,1883,1886],{"class":147,"line":178},[145,1879,1880],{"class":155},"  \"id\"",[145,1882,436],{"class":262},[145,1884,1885],{"class":159},"\"task-unified-1772203771-yf1dxogh\"",[145,1887,451],{"class":262},[145,1889,1890,1893,1895,1897],{"class":147,"line":187},[145,1891,1892],{"class":155},"  \"model\"",[145,1894,436],{"class":262},[145,1896,1307],{"class":159},[145,1898,451],{"class":262},[145,1900,1901,1904,1906,1909],{"class":147,"line":372},[145,1902,1903],{"class":155},"  \"object\"",[145,1905,436],{"class":262},[145,1907,1908],{"class":159},"\"video.generation.task\"",[145,1910,451],{"class":262},[145,1912,1913,1916,1918,1920],{"class":147,"line":378},[145,1914,1915],{"class":155},"  \"progress\"",[145,1917,436],{"class":262},[145,1919,782],{"class":155},[145,1921,451],{"class":262},[145,1923,1924,1927,1929,1932],{"class":147,"line":384},[145,1925,1926],{"class":155},"  \"status\"",[145,1928,436],{"class":262},[145,1930,1931],{"class":159},"\"pending\"",[145,1933,451],{"class":262},[145,1935,1936,1939],{"class":147,"line":408},[145,1937,1938],{"class":155},"  \"task_info\"",[145,1940,1941],{"class":262},": {\n",[145,1943,1944,1947,1949,1951],{"class":147,"line":419},[145,1945,1946],{"class":155},"    \"can_cancel\"",[145,1948,436],{"class":262},[145,1950,1824],{"class":155},[145,1952,451],{"class":262},[145,1954,1955,1958,1960],{"class":147,"line":430},[145,1956,1957],{"class":155},"    \"estimated_time\"",[145,1959,436],{"class":262},[145,1961,1962],{"class":155},"132\n",[145,1964,1965],{"class":147,"line":454},[145,1966,1967],{"class":262},"  },\n",[145,1969,1970,1973,1975,1978],{"class":147,"line":465},[145,1971,1972],{"class":155},"  \"type\"",[145,1974,436],{"class":262},[145,1976,1977],{"class":159},"\"video\"",[145,1979,451],{"class":262},[145,1981,1982,1985],{"class":147,"line":599},[145,1983,1984],{"class":155},"  \"usage\"",[145,1986,1941],{"class":262},[145,1988,1989,1992,1994,1997],{"class":147,"line":604},[145,1990,1991],{"class":155},"    \"billing_rule\"",[145,1993,436],{"class":262},[145,1995,1996],{"class":159},"\"per_second\"",[145,1998,451],{"class":262},[145,2000,2001,2004,2006,2009],{"class":147,"line":610},[145,2002,2003],{"class":155},"    \"credits_reserved\"",[145,2005,436],{"class":262},[145,2007,2008],{"class":155},"17.784",[145,2010,451],{"class":262},[145,2012,2013,2016,2018],{"class":147,"line":616},[145,2014,2015],{"class":155},"    \"user_group\"",[145,2017,436],{"class":262},[145,2019,2020],{"class":159},"\"default\"\n",[145,2022,2023],{"class":147,"line":622},[145,2024,2025],{"class":262},"  }\n",[145,2027,2028],{"class":147,"line":627},[145,2029,468],{"class":262},[10,2031,2032],{},"주요 필드 설명:",[2034,2035,2036,2049],"table",{},[2037,2038,2039],"thead",{},[2040,2041,2042,2046],"tr",{},[2043,2044,2045],"th",{},"필드",[2043,2047,2048],{},"의미",[2050,2051,2052,2063,2086,2098,2108,2118,2130,2140],"tbody",{},[2040,2053,2054,2060],{},[2055,2056,2057],"td",{},[27,2058,2059],{},"id",[2055,2061,2062],{},"작업 ID — 상태 확인과 결과 조회에 사용",[2040,2064,2065,2069],{},[2055,2066,2067],{},[27,2068,813],{},[2055,2070,2071,2074,2075,2078,2079,280,2082,2085],{},[27,2072,2073],{},"pending","으로 시작, ",[27,2076,2077],{},"processing","을 거쳐 ",[27,2080,2081],{},"completed",[27,2083,2084],{},"failed","로 변경",[2040,2087,2088,2092],{},[2055,2089,2090],{},[27,2091,823],{},[2055,2093,2094,2095,2097],{},"0~100 백분율. ",[27,2096,2077],{}," 단계에서 실시간 업데이트",[2040,2099,2100,2105],{},[2055,2101,2102],{},[27,2103,2104],{},"estimated_time",[2055,2106,2107],{},"예상 완료 시간(초), 서버 측 추정치",[2040,2109,2110,2115],{},[2055,2111,2112],{},[27,2113,2114],{},"credits_reserved",[2055,2116,2117],{},"이 작업에 예약된 크레딧. 작업 실패 시 자동 환불",[2040,2119,2120,2125],{},[2055,2121,2122],{},[27,2123,2124],{},"task_info.can_cancel",[2055,2126,2127,2128,117],{},"작업 취소 가능 여부 (완료 전에는 항상 ",[27,2129,1824],{},[2040,2131,2132,2137],{},[2055,2133,2134],{},[27,2135,2136],{},"created",[2055,2138,2139],{},"작업 제출 시간의 Unix 타임스탬프",[2040,2141,2142,2147],{},[2055,2143,2144],{},[27,2145,2146],{},"usage.billing_rule",[2055,2148,2149,2150,2153],{},"과금 방식 — ",[27,2151,2152],{},"per_second","는 비용이 길이에 비례",[17,2155,2156],{},[10,2157,2158,2160,2161,2163,2164,2167],{},[22,2159,133],{}," 제출 후 즉시 ",[27,2162,2059],{},"를 파일이나 데이터베이스에 저장하세요. polling 중 스크립트가 크래시되면 저장된 작업 ID로 ",[27,2165,2166],{},"wait_for_video()","를 호출하여 복구할 수 있습니다. 작업은 서버에 24시간 보관됩니다.",[91,2169,2171],{"id":2170},"polling-과정","Polling 과정",[10,2173,2174,2176],{},[27,2175,2166],{}," 함수는 10초마다 polling합니다. 실제 출력은 다음과 같습니다:",[136,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":141},[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},"결과-polling-비동기-워크플로우-이해하기","결과 Polling: 비동기 워크플로우 이해하기",[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],{},"Polling"," → 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,2265],{"id":2264},"작업-상태-생명주기","작업 상태 생명주기",[136,2267,2270],{"className":2268,"code":2269,"language":2182},[2180],"pending → processing → completed\n                    ↘ failed\n",[27,2271,2269],{"__ignoreMap":141},[2034,2273,2274,2287],{},[2037,2275,2276],{},[2040,2277,2278,2281,2284],{},[2043,2279,2280],{},"상태",[2043,2282,2283],{},"진행 상황",[2043,2285,2286],{},"일반적인 소요 시간",[2050,2288,2289,2301,2316,2330],{},[2040,2290,2291,2295,2298],{},[2055,2292,2293],{},[27,2294,2073],{},[2055,2296,2297],{},"작업이 큐에 대기 중, GPU 리소스 대기",[2055,2299,2300],{},"0~30초",[2040,2302,2303,2307,2313],{},[2055,2304,2305],{},[27,2306,2077],{},[2055,2308,2309,2310,2312],{},"비디오 생성 중 — ",[27,2311,823],{}," 실시간 업데이트",[2055,2314,2315],{},"30~120초",[2040,2317,2318,2322,2327],{},[2055,2319,2320],{},[27,2321,2081],{},[2055,2323,2324,2325,2258],{},"완료! ",[27,2326,2257],{},[2055,2328,2329],{},"종료 상태",[2040,2331,2332,2336,2339],{},[2055,2333,2334],{},[27,2335,2084],{},[2055,2337,2338],{},"오류 발생 — 오류 상세 정보 확인",[2055,2340,2329],{},[91,2342,2344],{"id":2343},"polling-모범-사례","Polling 모범 사례",[10,2346,2347,2350],{},[22,2348,2349],{},"Polling 간격:"," 10초가 적절한 기본값입니다. 너무 빠르면 요청을 낭비하고 속도 제한을 트리거할 수 있고, 너무 느리면 파이프라인이 지연됩니다. 시간에 민감한 애플리케이션에서는 5초마다 polling할 수 있지만, 그보다 빠르게 할 필요는 없습니다.",[10,2352,2353,2356],{},[22,2354,2355],{},"타임아웃:"," 파라미터에 따라 합리적인 상한을 설정하세요:",[2034,2358,2359,2372],{},[2037,2360,2361],{},[2040,2362,2363,2366,2369],{},[2043,2364,2365],{},"설정",[2043,2367,2368],{},"예상 소요 시간",[2043,2370,2371],{},"권장 타임아웃",[2050,2373,2374,2385,2396,2407],{},[2040,2375,2376,2379,2382],{},[2055,2377,2378],{},"4s, 480p",[2055,2380,2381],{},"20~40초",[2055,2383,2384],{},"120초",[2040,2386,2387,2390,2393],{},[2055,2388,2389],{},"5s, 720p",[2055,2391,2392],{},"30~60초",[2055,2394,2395],{},"180초",[2040,2397,2398,2401,2404],{},[2055,2399,2400],{},"10s, 720p",[2055,2402,2403],{},"60~90초",[2055,2405,2406],{},"300초",[2040,2408,2409,2412,2415],{},[2055,2410,2411],{},"15s, 1080p",[2055,2413,2414],{},"90~180초",[2055,2416,2417],{},"600초",[10,2419,2420,2423,2424,2426,2427,2434],{},[22,2421,2422],{},"진행률 추적:"," ",[27,2425,823],{}," 필드(0",[2428,2429,2430,2431,2433],"del",{},"100)는 세밀한 피드백을 제공합니다 — UI에서 진행률 표시줄을 구축하는 데 유용합니다. ",[27,2432,2077],{}," 단계에서 약 5","7초마다 업데이트됩니다.",[91,2436,2438],{"id":2437},"작업-취소","작업 취소",[10,2440,2441],{},"진행 중인 생성을 중지해야 할 때(잘못된 프롬프트, 마음이 바뀐 경우) 취소할 수 있습니다:",[136,2443,2445],{"className":337,"code":2444,"language":339,"meta":141,"style":141},"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,2446,2447,2457,2462,2470,2491,2499,2503,2518,2539,2546],{"__ignoreMap":141},[145,2448,2449,2451,2454],{"class":147,"line":148},[145,2450,525],{"class":258},[145,2452,2453],{"class":151}," cancel_task",[145,2455,2456],{"class":262},"(task_id):\n",[145,2458,2459],{"class":147,"line":166},[145,2460,2461],{"class":159},"    \"\"\"pending 또는 processing 상태의 작업을 취소합니다. 크레딧이 환불됩니다.\"\"\"\n",[145,2463,2464,2466,2468],{"class":147,"line":178},[145,2465,1426],{"class":262},[145,2467,266],{"class":258},[145,2469,1431],{"class":262},[145,2471,2472,2474,2476,2478,2480,2482,2484,2486,2489],{"class":147,"line":187},[145,2473,1436],{"class":258},[145,2475,448],{"class":159},[145,2477,678],{"class":155},[145,2479,681],{"class":159},[145,2481,684],{"class":155},[145,2483,687],{"class":262},[145,2485,690],{"class":155},[145,2487,2488],{"class":159},"/cancel\"",[145,2490,451],{"class":262},[145,2492,2493,2495,2497],{"class":147,"line":372},[145,2494,1454],{"class":700},[145,2496,266],{"class":258},[145,2498,706],{"class":155},[145,2500,2501],{"class":147,"line":378},[145,2502,1482],{"class":262},[145,2504,2505,2508,2511,2513,2516],{"class":147,"line":384},[145,2506,2507],{"class":258},"    if",[145,2509,2510],{"class":262}," response.status_code ",[145,2512,853],{"class":258},[145,2514,2515],{"class":155}," 200",[145,2517,859],{"class":262},[145,2519,2520,2522,2524,2526,2528,2530,2532,2534,2537],{"class":147,"line":408},[145,2521,790],{"class":155},[145,2523,793],{"class":262},[145,2525,439],{"class":258},[145,2527,920],{"class":159},[145,2529,684],{"class":155},[145,2531,687],{"class":262},[145,2533,690],{"class":155},[145,2535,2536],{"class":159}," cancelled. Credits refunded.\"",[145,2538,405],{"class":262},[145,2540,2541,2544],{"class":147,"line":419},[145,2542,2543],{"class":258},"    else",[145,2545,859],{"class":262},[145,2547,2548,2550,2552,2554,2557,2559,2562,2564,2566],{"class":147,"line":430},[145,2549,790],{"class":155},[145,2551,793],{"class":262},[145,2553,439],{"class":258},[145,2555,2556],{"class":159},"\"Cancel failed: ",[145,2558,684],{"class":155},[145,2560,2561],{"class":262},"response.json()",[145,2563,690],{"class":155},[145,2565,448],{"class":159},[145,2567,405],{"class":262},[10,2569,2570,2572,2573,2575,2576,280,2578,2580],{},[27,2571,2124],{},"이 ",[27,2574,1824],{},"일 때 취소가 가능합니다. 작업이 ",[27,2577,2081],{},[27,2579,2084],{},"에 도달하면 취소할 수 없습니다. 취소 시 예약된 크레딧은 자동으로 환불됩니다.",[17,2582,2583],{},[10,2584,2585,2587],{},[22,2586,133],{}," UI에 취소 기능을 일찍 구축하세요. 사용자는 필연적으로 잘못된 프롬프트를 제출하게 되고, 원하지 않는 비디오를 2분 동안 기다리는 것은 시간과 크레딧 모두 낭비입니다.",[10,2589,2590,2591,2593,2594,2598],{},"앞서 설정한 ",[27,2592,2166],{}," 함수가 표준 polling 흐름을 처리합니다. polling을 완전히 건너뛰고 싶다면 아래 ",[36,2595,2597],{"href":2596},"#webhook-%EC%84%A4%EC%A0%95polling-%EA%B1%B4%EB%84%88%EB%9B%B0%EA%B8%B0","Webhook 섹션","으로 이동하세요.",[44,2600],{},[47,2602,2604],{"id":2603},"이미지-애니메이션-이미지-투-비디오","이미지 애니메이션 (이미지-투-비디오)",[10,2606,2607,2608,2611,2612,2616],{},"제품 사진, 캐릭터 일러스트, 풍경 사진을 살아 움직이게 만들고 싶으신가요? ",[27,2609,2610],{},"image_url","로 전달하면 Seedance가 애니메이션화합니다. ",[36,2613,2615],{"href":2614},"/blog/seedance-2-ecommerce-product-videos","이커머스 제품 비디오","에서 가장 강력한 기능 중 하나입니다 — 정적인 제품 사진을 매력적인 비디오 광고로 변환합니다.",[10,2618,2619],{},[2620,2621,2622],"em",{},"위 첫 번째 예제와 동일한 설정 및 polling 함수를 사용합니다.",[136,2624,2626],{"className":337,"code":2625,"language":339,"meta":141,"style":141},"# ── 이미지-투-비디오 ──────────────────────────────────────────\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,2627,2628,2633,2642,2650,2660,2666,2671,2676,2681,2685,2693,2698,2703,2713,2723,2732,2736,2740,2751,2759,2771,2781,2790,2794,2799,2807,2811,2835,2847,2851,2867,2876,2880],{"__ignoreMap":141},[145,2629,2630],{"class":147,"line":148},[145,2631,2632],{"class":174},"# ── 이미지-투-비디오 ──────────────────────────────────────────\n",[145,2634,2635,2637,2640],{"class":147,"line":166},[145,2636,525],{"class":258},[145,2638,2639],{"class":151}," image_to_video",[145,2641,1288],{"class":262},[145,2643,2644,2646,2648],{"class":147,"line":178},[145,2645,1293],{"class":262},[145,2647,266],{"class":258},[145,2649,427],{"class":262},[145,2651,2652,2654,2656,2658],{"class":147,"line":187},[145,2653,1302],{"class":159},[145,2655,436],{"class":262},[145,2657,1307],{"class":159},[145,2659,451],{"class":262},[145,2661,2662,2664],{"class":147,"line":372},[145,2663,1318],{"class":159},[145,2665,1321],{"class":262},[145,2667,2668],{"class":147,"line":378},[145,2669,2670],{"class":159},"            \"@Image1 as the first frame. The scene slowly comes \"\n",[145,2672,2673],{"class":147,"line":384},[145,2674,2675],{"class":159},"            \"to life — leaves rustle gently, soft light shifts \"\n",[145,2677,2678],{"class":147,"line":408},[145,2679,2680],{"class":159},"            \"across the frame, and the subject blinks naturally.\"\n",[145,2682,2683],{"class":147,"line":419},[145,2684,1341],{"class":262},[145,2686,2687,2690],{"class":147,"line":430},[145,2688,2689],{"class":159},"        \"image_urls\"",[145,2691,2692],{"class":262},": [\n",[145,2694,2695],{"class":147,"line":454},[145,2696,2697],{"class":159},"            \"https://example.com/your-image.jpg\"\n",[145,2699,2700],{"class":147,"line":465},[145,2701,2702],{"class":262},"        ],\n",[145,2704,2705,2707,2709,2711],{"class":147,"line":599},[145,2706,1346],{"class":159},[145,2708,436],{"class":262},[145,2710,1351],{"class":155},[145,2712,451],{"class":262},[145,2714,2715,2717,2719,2721],{"class":147,"line":604},[145,2716,1362],{"class":159},[145,2718,436],{"class":262},[145,2720,1367],{"class":159},[145,2722,451],{"class":262},[145,2724,2725,2727,2729],{"class":147,"line":610},[145,2726,1378],{"class":159},[145,2728,436],{"class":262},[145,2730,2731],{"class":159},"\"16:9\"\n",[145,2733,2734],{"class":147,"line":616},[145,2735,1406],{"class":262},[145,2737,2738],{"class":147,"line":622},[145,2739,375],{"emptyLinePlaceholder":57},[145,2741,2742,2744,2746,2749],{"class":147,"line":627},[145,2743,1101],{"class":155},[145,2745,793],{"class":262},[145,2747,2748],{"class":159},"\"Submitting image-to-video request...\"",[145,2750,405],{"class":262},[145,2752,2753,2755,2757],{"class":147,"line":638},[145,2754,1426],{"class":262},[145,2756,266],{"class":258},[145,2758,1431],{"class":262},[145,2760,2761,2763,2765,2767,2769],{"class":147,"line":653},[145,2762,1436],{"class":258},[145,2764,448],{"class":159},[145,2766,678],{"class":155},[145,2768,1443],{"class":159},[145,2770,451],{"class":262},[145,2772,2773,2775,2777,2779],{"class":147,"line":659},[145,2774,1454],{"class":700},[145,2776,266],{"class":258},[145,2778,422],{"class":155},[145,2780,451],{"class":262},[145,2782,2783,2785,2787],{"class":147,"line":670},[145,2784,1469],{"class":700},[145,2786,266],{"class":258},[145,2788,2789],{"class":262},"payload\n",[145,2791,2792],{"class":147,"line":697},[145,2793,1482],{"class":262},[145,2795,2796],{"class":147,"line":709},[145,2797,2798],{"class":262},"    response.raise_for_status()\n",[145,2800,2801,2803,2805],{"class":147,"line":715},[145,2802,1495],{"class":262},[145,2804,266],{"class":258},[145,2806,735],{"class":262},[145,2808,2809],{"class":147,"line":721},[145,2810,375],{"emptyLinePlaceholder":57},[145,2812,2813,2815,2817,2819,2821,2823,2825,2827,2829,2831,2833],{"class":147,"line":727},[145,2814,1101],{"class":155},[145,2816,793],{"class":262},[145,2818,439],{"class":258},[145,2820,1523],{"class":159},[145,2822,684],{"class":155},[145,2824,1528],{"class":262},[145,2826,1531],{"class":159},[145,2828,1534],{"class":262},[145,2830,690],{"class":155},[145,2832,448],{"class":159},[145,2834,405],{"class":262},[145,2836,2837,2839,2841,2843,2845],{"class":147,"line":738},[145,2838,1617],{"class":262},[145,2840,266],{"class":258},[145,2842,1622],{"class":262},[145,2844,1625],{"class":159},[145,2846,1628],{"class":262},[145,2848,2849],{"class":147,"line":743},[145,2850,375],{"emptyLinePlaceholder":57},[145,2852,2853,2855,2857,2859,2861,2863,2865],{"class":147,"line":749},[145,2854,1642],{"class":262},[145,2856,266],{"class":258},[145,2858,1647],{"class":262},[145,2860,1650],{"class":159},[145,2862,1561],{"class":262},[145,2864,782],{"class":155},[145,2866,763],{"class":262},[145,2868,2869,2871,2874],{"class":147,"line":766},[145,2870,1688],{"class":262},[145,2872,2873],{"class":159},"\"animated_image.mp4\"",[145,2875,405],{"class":262},[145,2877,2878],{"class":147,"line":787},[145,2879,375],{"emptyLinePlaceholder":57},[145,2881,2882,2884],{"class":147,"line":833},[145,2883,1702],{"class":258},[145,2885,1705],{"class":262},[10,2887,2888],{},"텍스트-투-비디오와 다른 점을 살펴봅시다:",[96,2890,2891,2903,2924],{},[72,2892,2893,2898,2899,2902],{},[22,2894,2895],{},[27,2896,2897],{},"image_urls"," — 공개적으로 접근 가능한 이미지 URL 배열입니다. API가 직접 가져오므로 인터넷에서 접근 가능해야 합니다(",[27,2900,2901],{},"localhost","나 사설 네트워크 URL은 안 됩니다).",[72,2904,2905,2911,2912,2914,2915,399,2917,399,2920,2923],{},[22,2906,2907,2908],{},"프롬프트의 ",[27,2909,2910],{},"@Image1"," — 이 태그는 Seedance에 어떤 이미지를 어떻게 참조할지 알려줍니다. ",[27,2913,2897],{},"의 첫 번째 URL에 해당합니다. 세 장의 이미지를 전달하면 ",[27,2916,2910],{},[27,2918,2919],{},"@Image2",[27,2921,2922],{},"@Image3","을 사용합니다.",[72,2925,2926,2931,2932,2934,2935,2938],{},[22,2927,2928,2930],{},[27,2929,1821],{}," 없음"," — 여기서는 생략했으며, 기본값은 ",[27,2933,1824],{},"입니다. 무음 애니메이션을 원하면 ",[27,2936,2937],{},"false","로 설정하세요.",[91,2940,2942],{"id":2941},"image-태그-작동-방식","@Image 태그 작동 방식",[10,2944,2907,2945,2947,2948,2950,2951,2953,2954,2957,2958,2961,2962,2965,2966,1259],{},[27,2946,2910],{}," 태그는 Seedance에 이미지 사용 방법을 알려줍니다. ",[27,2949,2897],{}," 배열의 첫 번째 URL을 참조합니다. 최대 9장의 이미지를 전달할 수 있습니다(",[27,2952,2910],{},"부터 ",[27,2955,2956],{},"@Image9","까지). ",[27,2959,2960],{},"@Video","와 ",[27,2963,2964],{},"@Audio","를 포함한 멀티모달 태그의 전체 가이드는 ",[36,2967,2969],{"href":2968},"/blog/seedance-2-multimodal-tags-guide","멀티모달 @Tags 가이드",[10,2971,2972],{},"일반적인 패턴:",[2034,2974,2975,2988],{},[2037,2976,2977],{},[2040,2978,2979,2982,2985],{},[2043,2980,2981],{},"프롬프트 패턴",[2043,2983,2984],{},"효과",[2043,2986,2987],{},"적합한 용도",[2050,2989,2990,3003,3016,3029,3042],{},[2040,2991,2992,2997,3000],{},[2055,2993,2994],{},[27,2995,2996],{},"@Image1 as first frame",[2055,2998,2999],{},"이미지를 시작 프레임으로 사용",[2055,3001,3002],{},"제품 쇼케이스, 장면 설정",[2040,3004,3005,3010,3013],{},[2055,3006,3007],{},[27,3008,3009],{},"@Image1 as last frame",[2055,3011,3012],{},"이미지를 마지막 프레임으로 사용",[2055,3014,3015],{},"로고 공개, 전환",[2040,3017,3018,3023,3026],{},[2055,3019,3020],{},[27,3021,3022],{},"@Image1 as character reference",[2055,3024,3025],{},"캐릭터 외형 유지",[2055,3027,3028],{},"클립 간 캐릭터 일관성",[2040,3030,3031,3036,3039],{},[2055,3032,3033],{},[27,3034,3035],{},"@Image1 as style reference",[2055,3037,3038],{},"이미지의 시각적 스타일 적용",[2055,3040,3041],{},"브랜드 일관성, 아트 디렉션",[2040,3043,3044,3049,3052],{},[2055,3045,3046],{},[27,3047,3048],{},"@Image1 as first frame, @Image2 as last frame",[2055,3050,3051],{},"두 이미지 간 전환 생성",[2055,3053,3054],{},"전후 비교, 변형",[10,3056,3057],{},"테스트에서 받은 실제 응답:",[136,3059,3061],{"className":1854,"code":3060,"language":1856,"meta":141,"style":141},"{\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,3062,3063,3067,3078,3089,3099,3109,3119,3129,3135,3145,3154,3158,3168,3174,3184,3194,3202,3206],{"__ignoreMap":141},[145,3064,3065],{"class":147,"line":148},[145,3066,1863],{"class":262},[145,3068,3069,3071,3073,3076],{"class":147,"line":166},[145,3070,1868],{"class":155},[145,3072,436],{"class":262},[145,3074,3075],{"class":155},"1772204037",[145,3077,451],{"class":262},[145,3079,3080,3082,3084,3087],{"class":147,"line":178},[145,3081,1880],{"class":155},[145,3083,436],{"class":262},[145,3085,3086],{"class":159},"\"task-unified-1772204036-lify8u5p\"",[145,3088,451],{"class":262},[145,3090,3091,3093,3095,3097],{"class":147,"line":187},[145,3092,1892],{"class":155},[145,3094,436],{"class":262},[145,3096,1307],{"class":159},[145,3098,451],{"class":262},[145,3100,3101,3103,3105,3107],{"class":147,"line":372},[145,3102,1903],{"class":155},[145,3104,436],{"class":262},[145,3106,1908],{"class":159},[145,3108,451],{"class":262},[145,3110,3111,3113,3115,3117],{"class":147,"line":378},[145,3112,1915],{"class":155},[145,3114,436],{"class":262},[145,3116,782],{"class":155},[145,3118,451],{"class":262},[145,3120,3121,3123,3125,3127],{"class":147,"line":384},[145,3122,1926],{"class":155},[145,3124,436],{"class":262},[145,3126,1931],{"class":159},[145,3128,451],{"class":262},[145,3130,3131,3133],{"class":147,"line":408},[145,3132,1938],{"class":155},[145,3134,1941],{"class":262},[145,3136,3137,3139,3141,3143],{"class":147,"line":419},[145,3138,1946],{"class":155},[145,3140,436],{"class":262},[145,3142,1824],{"class":155},[145,3144,451],{"class":262},[145,3146,3147,3149,3151],{"class":147,"line":430},[145,3148,1957],{"class":155},[145,3150,436],{"class":262},[145,3152,3153],{"class":155},"145\n",[145,3155,3156],{"class":147,"line":454},[145,3157,1967],{"class":262},[145,3159,3160,3162,3164,3166],{"class":147,"line":465},[145,3161,1972],{"class":155},[145,3163,436],{"class":262},[145,3165,1977],{"class":159},[145,3167,451],{"class":262},[145,3169,3170,3172],{"class":147,"line":599},[145,3171,1984],{"class":155},[145,3173,1941],{"class":262},[145,3175,3176,3178,3180,3182],{"class":147,"line":604},[145,3177,1991],{"class":155},[145,3179,436],{"class":262},[145,3181,1996],{"class":159},[145,3183,451],{"class":262},[145,3185,3186,3188,3190,3192],{"class":147,"line":610},[145,3187,2003],{"class":155},[145,3189,436],{"class":262},[145,3191,2008],{"class":155},[145,3193,451],{"class":262},[145,3195,3196,3198,3200],{"class":147,"line":616},[145,3197,2015],{"class":155},[145,3199,436],{"class":262},[145,3201,2020],{"class":159},[145,3203,3204],{"class":147,"line":622},[145,3205,2025],{"class":262},[145,3207,3208],{"class":147,"line":627},[145,3209,468],{"class":262},[10,3211,3212,3213,3215],{},"이미지-투-비디오는 텍스트-투-비디오와 완전히 동일한 비동기 패턴을 따릅니다 — 제출, polling, 다운로드. 모델이 입력 이미지를 분석해야 하므로 ",[27,3214,2104],{},"이 약간 더 깁니다.",[91,3217,3219],{"id":3218},"이미지-요구사항","이미지 요구사항",[2034,3221,3222,3232],{},[2037,3223,3224],{},[2040,3225,3226,3229],{},[2043,3227,3228],{},"제약",[2043,3230,3231],{},"값",[2050,3233,3234,3242,3250,3258,3266],{},[2040,3235,3236,3239],{},[2055,3237,3238],{},"최대 이미지 수",[2055,3240,3241],{},"요청당 9장",[2040,3243,3244,3247],{},[2055,3245,3246],{},"최대 파일 크기",[2055,3248,3249],{},"이미지당 30 MB",[2040,3251,3252,3255],{},[2055,3253,3254],{},"지원 형식",[2055,3256,3257],{},"JPEG, PNG, WebP, BMP, TIFF, GIF",[2040,3259,3260,3263],{},[2055,3261,3262],{},"URL 요구사항",[2055,3264,3265],{},"공개적으로 접근 가능해야 함",[2040,3267,3268,3271],{},[2055,3269,3270],{},"권장 해상도",[2055,3272,3273],{},"짧은 변 최소 720px",[17,3275,3276],{},[10,3277,3278,3280,3281,3283],{},[22,3279,304],{}," URL 대신 로컬 파일 경로를 전달하는 것. ",[27,3282,2897],{}," 필드는 공개적으로 접근 가능한 HTTP/HTTPS URL이 필요합니다. 이미지가 로컬에 있다면 먼저 S3, Cloudflare R2 또는 임시 파일 호스팅 서비스에 업로드하세요.",[17,3285,3286],{},[10,3287,3288,3291],{},[22,3289,3290],{},"제한사항:"," Seedance는 사실적인 인물 얼굴 이미지 업로드를 지원하지 않습니다. 시스템이 자동으로 거부합니다. 일러스트나 스타일화된 캐릭터를 사용하세요.",[91,3293,3295],{"id":3294},"api용-이미지-호스팅","API용 이미지 호스팅",[10,3297,3298],{},"CDN이 없다면 공개 URL을 빠르게 얻는 방법:",[136,3300,3302],{"className":337,"code":3301,"language":339,"meta":141,"style":141},"# 방법 1: 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# 방법 2: 임시 파일 호스팅 API 사용\n# 많은 서비스가 테스트용 무료 임시 호스팅을 제공합니다\n",[27,3303,3304,3309,3316,3331,3351,3364,3368,3373],{"__ignoreMap":141},[145,3305,3306],{"class":147,"line":148},[145,3307,3308],{"class":174},"# 방법 1: S3에 업로드 (AWS가 있는 경우)\n",[145,3310,3311,3313],{"class":147,"line":166},[145,3312,346],{"class":258},[145,3314,3315],{"class":262}," boto3\n",[145,3317,3318,3321,3323,3326,3329],{"class":147,"line":178},[145,3319,3320],{"class":262},"s3 ",[145,3322,266],{"class":258},[145,3324,3325],{"class":262}," boto3.client(",[145,3327,3328],{"class":159},"'s3'",[145,3330,405],{"class":262},[145,3332,3333,3336,3339,3341,3344,3346,3349],{"class":147,"line":187},[145,3334,3335],{"class":262},"s3.upload_file(",[145,3337,3338],{"class":159},"'local_image.jpg'",[145,3340,399],{"class":262},[145,3342,3343],{"class":159},"'my-bucket'",[145,3345,399],{"class":262},[145,3347,3348],{"class":159},"'seedance/input.jpg'",[145,3350,405],{"class":262},[145,3352,3353,3356,3358,3361],{"class":147,"line":372},[145,3354,3355],{"class":262},"image_url ",[145,3357,266],{"class":258},[145,3359,3360],{"class":258}," f",[145,3362,3363],{"class":159},"\"https://my-bucket.s3.amazonaws.com/seedance/input.jpg\"\n",[145,3365,3366],{"class":147,"line":378},[145,3367,375],{"emptyLinePlaceholder":57},[145,3369,3370],{"class":147,"line":384},[145,3371,3372],{"class":174},"# 방법 2: 임시 파일 호스팅 API 사용\n",[145,3374,3375],{"class":147,"line":408},[145,3376,3377],{"class":174},"# 많은 서비스가 테스트용 무료 임시 호스팅을 제공합니다\n",[10,3379,3380,3381,1259],{},"고급 이미지-투-비디오 기법 — 첫/마지막 프레임 제어, 다중 이미지 합성, 이커머스 제품 애니메이션 — 은 ",[36,3382,3384],{"href":3383},"/blog/seedance-2-image-to-video-api","이미지-투-비디오 상세 가이드",[44,3386],{},[47,3388,3390],{"id":3389},"비디오-커스터마이징","비디오 커스터마이징",[10,3392,3393],{},"생성 요청에서 조정할 수 있는 모든 파라미터:",[2034,3395,3396,3415],{},[2037,3397,3398],{},[2040,3399,3400,3403,3406,3409,3412],{},[2043,3401,3402],{},"파라미터",[2043,3404,3405],{},"타입",[2043,3407,3408],{},"기본값",[2043,3410,3411],{},"옵션",[2043,3413,3414],{},"설명",[2050,3416,3417,3436,3452,3469,3492,3524,3546,3563,3580,3596],{},[2040,3418,3419,3423,3426,3429,3433],{},[2055,3420,3421],{},[27,3422,1746],{},[2055,3424,3425],{},"string",[2055,3427,3428],{},"—",[2055,3430,3431],{},[27,3432,1750],{},[2055,3434,3435],{},"필수. 사용할 모델.",[2040,3437,3438,3442,3444,3446,3449],{},[2055,3439,3440],{},[27,3441,1761],{},[2055,3443,3425],{},[2055,3445,3428],{},[2055,3447,3448],{},"≤2000 tokens",[2055,3450,3451],{},"필수. @tags를 포함한 비디오 설명.",[2040,3453,3454,3458,3461,3463,3466],{},[2055,3455,3456],{},[27,3457,1773],{},[2055,3459,3460],{},"integer",[2055,3462,1351],{},[2055,3464,3465],{},"4–15",[2055,3467,3468],{},"비디오 길이(초).",[2040,3470,3471,3475,3477,3481,3489],{},[2055,3472,3473],{},[27,3474,1781],{},[2055,3476,3425],{},[2055,3478,3479],{},[27,3480,1785],{},[2055,3482,3483,399,3485,399,3487],{},[27,3484,1789],{},[27,3486,1785],{},[27,3488,1793],{},[2055,3490,3491],{},"해상도 등급. 높을수록 크레딧 소모 증가.",[2040,3493,3494,3498,3500,3504,3521],{},[2055,3495,3496],{},[27,3497,1801],{},[2055,3499,3425],{},[2055,3501,3502],{},[27,3503,1805],{},[2055,3505,3506,399,3508,399,3510,399,3512,399,3515,399,3518],{},[27,3507,1805],{},[27,3509,1809],{},[27,3511,1813],{},[27,3513,3514],{},"4:3",[27,3516,3517],{},"3:4",[27,3519,3520],{},"21:9",[2055,3522,3523],{},"출력 화면 비율.",[2040,3525,3526,3530,3533,3537,3543],{},[2055,3527,3528],{},[27,3529,1821],{},[2055,3531,3532],{},"boolean",[2055,3534,3535],{},[27,3536,1824],{},[2055,3538,3539,399,3541],{},[27,3540,1824],{},[27,3542,2937],{},[2055,3544,3545],{},"AI 생성 오디오/음악 활성화.",[2040,3547,3548,3552,3555,3557,3560],{},[2055,3549,3550],{},[27,3551,2897],{},[2055,3553,3554],{},"array",[2055,3556,3428],{},[2055,3558,3559],{},"≤9장",[2055,3561,3562],{},"참조 이미지. 프롬프트에서 @Image1, @Image2...로 참조.",[2040,3564,3565,3570,3572,3574,3577],{},[2055,3566,3567],{},[27,3568,3569],{},"video_urls",[2055,3571,3554],{},[2055,3573,3428],{},[2055,3575,3576],{},"≤3개",[2055,3578,3579],{},"참조 비디오. 프롬프트에서 @Video1, @Video2...로 참조.",[2040,3581,3582,3587,3589,3591,3593],{},[2055,3583,3584],{},[27,3585,3586],{},"audio_urls",[2055,3588,3554],{},[2055,3590,3428],{},[2055,3592,3576],{},[2055,3594,3595],{},"참조 오디오. 프롬프트에서 @Audio1, @Audio2...로 참조.",[2040,3597,3598,3603,3605,3607,3610],{},[2055,3599,3600],{},[27,3601,3602],{},"callback_url",[2055,3604,3425],{},[2055,3606,3428],{},[2055,3608,3609],{},"HTTPS URL",[2055,3611,3612],{},"완료 시 webhook 콜백 주소.",[17,3614,3615],{},[10,3616,3617,3620,3621,3623,3624,3626,3627,399,3629,3631,3632,3634,3635,3637,3638,3641],{},[22,3618,3619],{},"Seedance 2.0 vs 1.5 참고:"," 위의 모든 파라미터는 ",[27,3622,1750],{},"과 ",[27,3625,29],{}," 모두에서 작동합니다. 주요 차이점: ",[27,3628,3569],{},[27,3630,3586],{},", 다중 이미지 참조(",[27,3633,2919],{},"~",[27,3636,2956],{},")는 2.0 전용 기능입니다. 1.5에서 사용하면 API가 해당 기능이 지원되지 않는다는 명확한 메시지와 함께 ",[27,3639,3640],{},"400"," 오류를 반환합니다.",[91,3643,3645],{"id":3644},"빠른-예제","빠른 예제",[10,3647,3648],{},[22,3649,3650],{},"세로형 소셜 미디어 비디오 (TikTok/Reels):",[10,3652,3653],{},[2620,3654,2622],{},[136,3656,3658],{"className":337,"code":3657,"language":339,"meta":141,"style":141},"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,3659,3660,3669,3680,3692,3704,3716,3732,3742],{"__ignoreMap":141},[145,3661,3662,3665,3667],{"class":147,"line":148},[145,3663,3664],{"class":262},"payload ",[145,3666,266],{"class":258},[145,3668,427],{"class":262},[145,3670,3671,3674,3676,3678],{"class":147,"line":166},[145,3672,3673],{"class":159},"    \"model\"",[145,3675,436],{"class":262},[145,3677,1307],{"class":159},[145,3679,451],{"class":262},[145,3681,3682,3685,3687,3690],{"class":147,"line":178},[145,3683,3684],{"class":159},"    \"prompt\"",[145,3686,436],{"class":262},[145,3688,3689],{"class":159},"\"A barista pours latte art in slow motion. Close-up overhead shot.\"",[145,3691,451],{"class":262},[145,3693,3694,3697,3699,3702],{"class":147,"line":187},[145,3695,3696],{"class":159},"    \"duration\"",[145,3698,436],{"class":262},[145,3700,3701],{"class":155},"8",[145,3703,451],{"class":262},[145,3705,3706,3709,3711,3714],{"class":147,"line":372},[145,3707,3708],{"class":159},"    \"quality\"",[145,3710,436],{"class":262},[145,3712,3713],{"class":159},"\"1080p\"",[145,3715,451],{"class":262},[145,3717,3718,3721,3723,3726,3729],{"class":147,"line":378},[145,3719,3720],{"class":159},"    \"aspect_ratio\"",[145,3722,436],{"class":262},[145,3724,3725],{"class":159},"\"9:16\"",[145,3727,3728],{"class":262},",       ",[145,3730,3731],{"class":174},"# 모바일용 세로형\n",[145,3733,3734,3737,3739],{"class":147,"line":384},[145,3735,3736],{"class":159},"    \"generate_audio\"",[145,3738,436],{"class":262},[145,3740,3741],{"class":155},"True\n",[145,3743,3744],{"class":147,"line":408},[145,3745,468],{"class":262},[10,3747,3748,3750,3751,3753],{},[27,3749,1809],{}," 비율은 1080×1920 비디오를 생성합니다 — TikTok, Instagram Reels, YouTube Shorts의 네이티브 해상도입니다. ",[27,3752,1793],{}," 품질 등급은 모바일 화면에서 선명한 비주얼을 보장합니다.",[10,3755,3756],{},[22,3757,3758],{},"시네마틱 와이드스크린 + 카메라 움직임:",[136,3760,3762],{"className":337,"code":3761,"language":339,"meta":141,"style":141},"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,3763,3764,3772,3782,3788,3793,3798,3803,3808,3818,3828,3842,3850],{"__ignoreMap":141},[145,3765,3766,3768,3770],{"class":147,"line":148},[145,3767,3664],{"class":262},[145,3769,266],{"class":258},[145,3771,427],{"class":262},[145,3773,3774,3776,3778,3780],{"class":147,"line":166},[145,3775,3673],{"class":159},[145,3777,436],{"class":262},[145,3779,1307],{"class":159},[145,3781,451],{"class":262},[145,3783,3784,3786],{"class":147,"line":178},[145,3785,3684],{"class":159},[145,3787,1321],{"class":262},[145,3789,3790],{"class":147,"line":187},[145,3791,3792],{"class":159},"        \"Aerial drone shot over a misty mountain range at sunrise. \"\n",[145,3794,3795],{"class":147,"line":372},[145,3796,3797],{"class":159},"        \"Camera slowly pushes forward, revealing a hidden valley. \"\n",[145,3799,3800],{"class":147,"line":378},[145,3801,3802],{"class":159},"        \"Cinematic color grading, volumetric lighting.\"\n",[145,3804,3805],{"class":147,"line":384},[145,3806,3807],{"class":262},"    ),\n",[145,3809,3810,3812,3814,3816],{"class":147,"line":408},[145,3811,3696],{"class":159},[145,3813,436],{"class":262},[145,3815,536],{"class":155},[145,3817,451],{"class":262},[145,3819,3820,3822,3824,3826],{"class":147,"line":419},[145,3821,3708],{"class":159},[145,3823,436],{"class":262},[145,3825,3713],{"class":159},[145,3827,451],{"class":262},[145,3829,3830,3832,3834,3837,3839],{"class":147,"line":430},[145,3831,3720],{"class":159},[145,3833,436],{"class":262},[145,3835,3836],{"class":159},"\"21:9\"",[145,3838,3728],{"class":262},[145,3840,3841],{"class":174},"# 울트라 와이드스크린 시네마틱\n",[145,3843,3844,3846,3848],{"class":147,"line":454},[145,3845,3736],{"class":159},[145,3847,436],{"class":262},[145,3849,3741],{"class":155},[145,3851,3852],{"class":147,"line":465},[145,3853,468],{"class":262},[10,3855,3856,3857,1259],{},"프로그래밍 방식의 카메라 제어 — 돌리 줌, 오비탈 샷, 히치콕 스타일 움직임 — 은 ",[36,3858,3860],{"href":3859},"/blog/seedance-2-camera-movement-api","카메라 움직임 API 가이드",[10,3862,3863],{},[22,3864,3865],{},"무음 웹사이트 배경 비디오:",[136,3867,3869],{"className":337,"code":3868,"language":339,"meta":141,"style":141},"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,3870,3871,3879,3889,3900,3915,3925,3938,3950],{"__ignoreMap":141},[145,3872,3873,3875,3877],{"class":147,"line":148},[145,3874,3664],{"class":262},[145,3876,266],{"class":258},[145,3878,427],{"class":262},[145,3880,3881,3883,3885,3887],{"class":147,"line":166},[145,3882,3673],{"class":159},[145,3884,436],{"class":262},[145,3886,1307],{"class":159},[145,3888,451],{"class":262},[145,3890,3891,3893,3895,3898],{"class":147,"line":178},[145,3892,3684],{"class":159},[145,3894,436],{"class":262},[145,3896,3897],{"class":159},"\"Abstract flowing particles in deep blue and gold. Slow, meditative movement.\"",[145,3899,451],{"class":262},[145,3901,3902,3904,3906,3909,3912],{"class":147,"line":187},[145,3903,3696],{"class":159},[145,3905,436],{"class":262},[145,3907,3908],{"class":155},"15",[145,3910,3911],{"class":262},",               ",[145,3913,3914],{"class":174},"# 최대 길이, 심리스 루프에 적합\n",[145,3916,3917,3919,3921,3923],{"class":147,"line":372},[145,3918,3708],{"class":159},[145,3920,436],{"class":262},[145,3922,1367],{"class":159},[145,3924,451],{"class":262},[145,3926,3927,3929,3931,3933,3935],{"class":147,"line":378},[145,3928,3720],{"class":159},[145,3930,436],{"class":262},[145,3932,3836],{"class":159},[145,3934,3728],{"class":262},[145,3936,3937],{"class":174},"# 와이드 배경\n",[145,3939,3940,3942,3944,3947],{"class":147,"line":384},[145,3941,3736],{"class":159},[145,3943,436],{"class":262},[145,3945,3946],{"class":155},"False",[145,3948,3949],{"class":174},"       # 자동 재생 배경에는 오디오 불필요\n",[145,3951,3952],{"class":147,"line":408},[145,3953,468],{"class":262},[10,3955,3956],{},[22,3957,3958],{},"저비용 초안 (빠른 반복):",[136,3960,3962],{"className":337,"code":3961,"language":339,"meta":141,"style":141},"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,3963,3964,3972,3982,3993,4008,4022,4030],{"__ignoreMap":141},[145,3965,3966,3968,3970],{"class":147,"line":148},[145,3967,3664],{"class":262},[145,3969,266],{"class":258},[145,3971,427],{"class":262},[145,3973,3974,3976,3978,3980],{"class":147,"line":166},[145,3975,3673],{"class":159},[145,3977,436],{"class":262},[145,3979,1307],{"class":159},[145,3981,451],{"class":262},[145,3983,3984,3986,3988,3991],{"class":147,"line":178},[145,3985,3684],{"class":159},[145,3987,436],{"class":262},[145,3989,3990],{"class":159},"\"A cat wearing sunglasses sits at a DJ booth. Neon club lighting.\"",[145,3992,451],{"class":262},[145,3994,3995,3997,3999,4002,4005],{"class":147,"line":187},[145,3996,3696],{"class":159},[145,3998,436],{"class":262},[145,4000,4001],{"class":155},"4",[145,4003,4004],{"class":262},",                ",[145,4006,4007],{"class":174},"# 최소 길이 = 가장 빠른 생성\n",[145,4009,4010,4012,4014,4017,4019],{"class":147,"line":372},[145,4011,3708],{"class":159},[145,4013,436],{"class":262},[145,4015,4016],{"class":159},"\"480p\"",[145,4018,1386],{"class":262},[145,4020,4021],{"class":174},"# 최저 품질 = 가장 저렴한 크레딧\n",[145,4023,4024,4026,4028],{"class":147,"line":378},[145,4025,3720],{"class":159},[145,4027,436],{"class":262},[145,4029,2731],{"class":159},[145,4031,4032],{"class":147,"line":384},[145,4033,468],{"class":262},[17,4035,4036],{},[10,4037,4038,4040,4041,2961,4044,4047,4048,4050],{},[22,4039,133],{}," 개발 단계에서는 항상 ",[27,4042,4043],{},"duration: 4",[27,4045,4046],{},"quality: \"480p\"","를 사용하세요. 가장 저렴하고 빠른 조합으로 프롬프트 반복에 이상적입니다. 만족스러운 결과를 얻으면 원하는 길이와 ",[27,4049,1793],{},"로 최종 버전을 렌더링하세요.",[91,4052,4054],{"id":4053},"크레딧-비용-추정","크레딧 비용 추정",[10,4056,4057],{},"크레딧은 길이와 품질에 비례하여 증가합니다. 대략적인 가이드:",[2034,4059,4060,4079],{},[2037,4061,4062],{},[2040,4063,4064,4067,4070,4073,4076],{},[2043,4065,4066],{},"품질",[2043,4068,4069],{},"4s",[2043,4071,4072],{},"5s",[2043,4074,4075],{},"10s",[2043,4077,4078],{},"15s",[2050,4080,4081,4097,4113],{},[2040,4082,4083,4085,4088,4091,4094],{},[2055,4084,1789],{},[2055,4086,4087],{},"~8",[2055,4089,4090],{},"~10",[2055,4092,4093],{},"~20",[2055,4095,4096],{},"~30",[2040,4098,4099,4101,4104,4107,4110],{},[2055,4100,1785],{},[2055,4102,4103],{},"~14",[2055,4105,4106],{},"~18",[2055,4108,4109],{},"~36",[2055,4111,4112],{},"~53",[2040,4114,4115,4117,4120,4123,4126],{},[2055,4116,1793],{},[2055,4118,4119],{},"~22",[2055,4121,4122],{},"~28",[2055,4124,4125],{},"~55",[2055,4127,4128],{},"~83",[10,4130,4131],{},[2620,4132,4133,4134,4136,4137,4141],{},"대략적인 크레딧. 실제 비용은 ",[27,4135,2114],{}," 필드에 표시됩니다. 현재 요금은 ",[36,4138,4140],{"href":211,"rel":4139},[40],"EvoLink 대시보드","에서 확인하세요.",[10,4143,4144,4145,399,4148,399,4150,4152,4153,4156,4157,1259],{},"멀티모달 참조 시스템 — ",[27,4146,4147],{},"@Image",[27,4149,2960],{},[27,4151,2964],{}," 태그 — 은 Seedance 2.0이 가장 돋보이는 부분입니다. 참조 비디오에서 ",[36,4154,4155],{"href":3859},"카메라 움직임","을 복제하고, 샷 간 캐릭터 일관성을 유지하며, 오디오 비트에 동기화할 수 있습니다. 전체 가이드는 ",[36,4158,4159],{"href":2968},"@Tags 완벽 가이드",[44,4161],{},[47,4163,4165],{"id":4164},"오류-처리-전략","오류 처리 전략",[10,4167,4168],{},"API 호출은 실패합니다. 네트워크가 끊깁니다. 속도 제한에 걸립니다. 실제 발생하는 오류 시나리오에 대응하는 견고한 코드 작성법을 알아봅시다.",[91,4170,4172],{"id":4171},"일반적인-오류-응답","일반적인 오류 응답",[10,4174,4175],{},"모든 오류는 동일한 형식을 따릅니다:",[136,4177,4179],{"className":1854,"code":4178,"language":1856,"meta":141,"style":141},"{\n  \"error\": {\n    \"message\": \"description of what went wrong\",\n    \"type\": \"error_category\",\n    \"code\": \"specific_error_code\"\n  }\n}\n",[27,4180,4181,4185,4192,4204,4216,4226,4230],{"__ignoreMap":141},[145,4182,4183],{"class":147,"line":148},[145,4184,1863],{"class":262},[145,4186,4187,4190],{"class":147,"line":166},[145,4188,4189],{"class":155},"  \"error\"",[145,4191,1941],{"class":262},[145,4193,4194,4197,4199,4202],{"class":147,"line":178},[145,4195,4196],{"class":155},"    \"message\"",[145,4198,436],{"class":262},[145,4200,4201],{"class":159},"\"description of what went wrong\"",[145,4203,451],{"class":262},[145,4205,4206,4209,4211,4214],{"class":147,"line":187},[145,4207,4208],{"class":155},"    \"type\"",[145,4210,436],{"class":262},[145,4212,4213],{"class":159},"\"error_category\"",[145,4215,451],{"class":262},[145,4217,4218,4221,4223],{"class":147,"line":372},[145,4219,4220],{"class":155},"    \"code\"",[145,4222,436],{"class":262},[145,4224,4225],{"class":159},"\"specific_error_code\"\n",[145,4227,4228],{"class":147,"line":378},[145,4229,2025],{"class":262},[145,4231,4232],{"class":147,"line":384},[145,4233,468],{"class":262},[10,4235,4236,4239,4240,2961,4243,4246,4247,4249,4250,4252,4253,297],{},[27,4237,4238],{},"error"," 객체는 항상 ",[27,4241,4242],{},"message",[27,4244,4245],{},"type","을 포함합니다. ",[27,4248,27],{}," 필드는 대부분의 오류에 존재하지만 전부는 아닙니다. 먼저 ",[27,4251,4245],{},"을 확인하고, 세부 사항은 ",[27,4254,27],{},[10,4256,4257],{},"API에서 반환하는 실제 오류 응답들:",[10,4259,4260],{},[22,4261,4262],{},"401 — 잘못된 API Key:",[136,4264,4266],{"className":1854,"code":4265,"language":1856,"meta":141,"style":141},"{\n  \"error\": {\n    \"message\": \"Invalid token (request id: 20260227225245660301729AApJNAhJ)\",\n    \"type\": \"evo_api_error\"\n  }\n}\n",[27,4267,4268,4272,4278,4289,4298,4302],{"__ignoreMap":141},[145,4269,4270],{"class":147,"line":148},[145,4271,1863],{"class":262},[145,4273,4274,4276],{"class":147,"line":166},[145,4275,4189],{"class":155},[145,4277,1941],{"class":262},[145,4279,4280,4282,4284,4287],{"class":147,"line":178},[145,4281,4196],{"class":155},[145,4283,436],{"class":262},[145,4285,4286],{"class":159},"\"Invalid token (request id: 20260227225245660301729AApJNAhJ)\"",[145,4288,451],{"class":262},[145,4290,4291,4293,4295],{"class":147,"line":187},[145,4292,4208],{"class":155},[145,4294,436],{"class":262},[145,4296,4297],{"class":159},"\"evo_api_error\"\n",[145,4299,4300],{"class":147,"line":372},[145,4301,2025],{"class":262},[145,4303,4304],{"class":147,"line":378},[145,4305,468],{"class":262},[10,4307,4308,4309,4311],{},"API Key가 잘못되었거나, 만료되었거나, 취소되었습니다. ",[27,4310,275],{}," 환경 변수를 다시 확인하세요. 흔한 원인: Key 복사 시 뒤에 공백이 포함된 경우.",[10,4313,4314],{},[22,4315,4316],{},"400 — 필수 필드 누락:",[136,4318,4320],{"className":1854,"code":4319,"language":1856,"meta":141,"style":141},"{\n  \"error\": {\n    \"code\": \"invalid_parameter\",\n    \"message\": \"prompt cannot be empty\",\n    \"type\": \"invalid_request_error\"\n  }\n}\n",[27,4321,4322,4326,4332,4343,4354,4363,4367],{"__ignoreMap":141},[145,4323,4324],{"class":147,"line":148},[145,4325,1863],{"class":262},[145,4327,4328,4330],{"class":147,"line":166},[145,4329,4189],{"class":155},[145,4331,1941],{"class":262},[145,4333,4334,4336,4338,4341],{"class":147,"line":178},[145,4335,4220],{"class":155},[145,4337,436],{"class":262},[145,4339,4340],{"class":159},"\"invalid_parameter\"",[145,4342,451],{"class":262},[145,4344,4345,4347,4349,4352],{"class":147,"line":187},[145,4346,4196],{"class":155},[145,4348,436],{"class":262},[145,4350,4351],{"class":159},"\"prompt cannot be empty\"",[145,4353,451],{"class":262},[145,4355,4356,4358,4360],{"class":147,"line":372},[145,4357,4208],{"class":155},[145,4359,436],{"class":262},[145,4361,4362],{"class":159},"\"invalid_request_error\"\n",[145,4364,4365],{"class":147,"line":378},[145,4366,2025],{"class":262},[145,4368,4369],{"class":147,"line":384},[145,4370,468],{"class":262},[10,4372,4373,4375],{},[27,4374,1761],{}," 필드는 모든 생성 요청에서 필수입니다. 빈 문자열이나 공백만 있는 프롬프트도 이 오류를 트리거합니다.",[10,4377,4378],{},[22,4379,4380],{},"400 — 잘못된 파라미터 값:",[136,4382,4384],{"className":1854,"code":4383,"language":1856,"meta":141,"style":141},"{\n  \"error\": {\n    \"code\": \"invalid_parameter\",\n    \"message\": \"duration must be between 4 and 15\",\n    \"type\": \"invalid_request_error\"\n  }\n}\n",[27,4385,4386,4390,4396,4406,4417,4425,4429],{"__ignoreMap":141},[145,4387,4388],{"class":147,"line":148},[145,4389,1863],{"class":262},[145,4391,4392,4394],{"class":147,"line":166},[145,4393,4189],{"class":155},[145,4395,1941],{"class":262},[145,4397,4398,4400,4402,4404],{"class":147,"line":178},[145,4399,4220],{"class":155},[145,4401,436],{"class":262},[145,4403,4340],{"class":159},[145,4405,451],{"class":262},[145,4407,4408,4410,4412,4415],{"class":147,"line":187},[145,4409,4196],{"class":155},[145,4411,436],{"class":262},[145,4413,4414],{"class":159},"\"duration must be between 4 and 15\"",[145,4416,451],{"class":262},[145,4418,4419,4421,4423],{"class":147,"line":372},[145,4420,4208],{"class":155},[145,4422,436],{"class":262},[145,4424,4362],{"class":159},[145,4426,4427],{"class":147,"line":378},[145,4428,2025],{"class":262},[145,4430,4431],{"class":147,"line":384},[145,4432,468],{"class":262},[10,4434,4435,280,4438,4441],{},[27,4436,4437],{},"duration: 3",[27,4439,4440],{},"duration: 20","을 전달할 때 발생합니다. 유효 범위는 4~15초(포함)입니다.",[10,4443,4444],{},[22,4445,4446],{},"400 — 지원하지 않는 품질 등급:",[136,4448,4450],{"className":1854,"code":4449,"language":1856,"meta":141,"style":141},"{\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,4451,4452,4456,4462,4472,4483,4491,4495],{"__ignoreMap":141},[145,4453,4454],{"class":147,"line":148},[145,4455,1863],{"class":262},[145,4457,4458,4460],{"class":147,"line":166},[145,4459,4189],{"class":155},[145,4461,1941],{"class":262},[145,4463,4464,4466,4468,4470],{"class":147,"line":178},[145,4465,4220],{"class":155},[145,4467,436],{"class":262},[145,4469,4340],{"class":159},[145,4471,451],{"class":262},[145,4473,4474,4476,4478,4481],{"class":147,"line":187},[145,4475,4196],{"class":155},[145,4477,436],{"class":262},[145,4479,4480],{"class":159},"\"quality must be one of: 480p, 720p, 1080p\"",[145,4482,451],{"class":262},[145,4484,4485,4487,4489],{"class":147,"line":372},[145,4486,4208],{"class":155},[145,4488,436],{"class":262},[145,4490,4362],{"class":159},[145,4492,4493],{"class":147,"line":378},[145,4494,2025],{"class":262},[145,4496,4497],{"class":147,"line":384},[145,4498,468],{"class":262},[10,4500,4501,280,4504,4507,4508,399,4510,4512,4513,4515],{},[27,4502,4503],{},"\"quality\": \"4k\"",[27,4505,4506],{},"\"quality\": \"hd\"","를 전달할 때 흔히 발생합니다. 정확한 문자열을 사용하세요: ",[27,4509,1789],{},[27,4511,1785],{},", 또는 ",[27,4514,1793],{},".",[10,4517,4518],{},[22,4519,4520],{},"402 — 크레딧 부족:",[136,4522,4524],{"className":1854,"code":4523,"language":1856,"meta":141,"style":141},"{\n  \"error\": {\n    \"message\": \"Insufficient credits. Required: 17.784, Available: 2.100\",\n    \"type\": \"insufficient_quota_error\"\n  }\n}\n",[27,4525,4526,4530,4536,4547,4556,4560],{"__ignoreMap":141},[145,4527,4528],{"class":147,"line":148},[145,4529,1863],{"class":262},[145,4531,4532,4534],{"class":147,"line":166},[145,4533,4189],{"class":155},[145,4535,1941],{"class":262},[145,4537,4538,4540,4542,4545],{"class":147,"line":178},[145,4539,4196],{"class":155},[145,4541,436],{"class":262},[145,4543,4544],{"class":159},"\"Insufficient credits. Required: 17.784, Available: 2.100\"",[145,4546,451],{"class":262},[145,4548,4549,4551,4553],{"class":147,"line":187},[145,4550,4208],{"class":155},[145,4552,436],{"class":262},[145,4554,4555],{"class":159},"\"insufficient_quota_error\"\n",[145,4557,4558],{"class":147,"line":372},[145,4559,2025],{"class":262},[145,4561,4562],{"class":147,"line":378},[145,4563,468],{"class":262},[10,4565,4566,4567,4570],{},"계정에 크레딧이 부족합니다. 메시지에 필요한 양과 보유한 양이 정확히 표시됩니다. ",[36,4568,4140],{"href":211,"rel":4569},[40],"에서 충전하세요.",[10,4572,4573],{},[22,4574,4575],{},"404 — 작업을 찾을 수 없음:",[136,4577,4579],{"className":1854,"code":4578,"language":1856,"meta":141,"style":141},"{\n  \"error\": {\n    \"message\": \"Task not found\",\n    \"type\": \"invalid_request_error\",\n    \"code\": \"task_not_found\"\n  }\n}\n",[27,4580,4581,4585,4591,4602,4613,4622,4626],{"__ignoreMap":141},[145,4582,4583],{"class":147,"line":148},[145,4584,1863],{"class":262},[145,4586,4587,4589],{"class":147,"line":166},[145,4588,4189],{"class":155},[145,4590,1941],{"class":262},[145,4592,4593,4595,4597,4600],{"class":147,"line":178},[145,4594,4196],{"class":155},[145,4596,436],{"class":262},[145,4598,4599],{"class":159},"\"Task not found\"",[145,4601,451],{"class":262},[145,4603,4604,4606,4608,4611],{"class":147,"line":187},[145,4605,4208],{"class":155},[145,4607,436],{"class":262},[145,4609,4610],{"class":159},"\"invalid_request_error\"",[145,4612,451],{"class":262},[145,4614,4615,4617,4619],{"class":147,"line":372},[145,4616,4220],{"class":155},[145,4618,436],{"class":262},[145,4620,4621],{"class":159},"\"task_not_found\"\n",[145,4623,4624],{"class":147,"line":378},[145,4625,2025],{"class":262},[145,4627,4628],{"class":147,"line":384},[145,4629,468],{"class":262},[10,4631,4632,4633,4635],{},"보통 작업 ID가 잘못되었거나, 작업이 생성된 지 24시간이 지나 만료된 경우입니다. 생성 응답의 ",[27,4634,2059],{}," 필드를 사용하고 있는지 확인하세요.",[10,4637,4638],{},[22,4639,4640],{},"413 — 이미지가 너무 큼:",[136,4642,4644],{"className":1854,"code":4643,"language":1856,"meta":141,"style":141},"{\n  \"error\": {\n    \"message\": \"Image file size exceeds 30MB limit\",\n    \"type\": \"request_too_large_error\"\n  }\n}\n",[27,4645,4646,4650,4656,4667,4676,4680],{"__ignoreMap":141},[145,4647,4648],{"class":147,"line":148},[145,4649,1863],{"class":262},[145,4651,4652,4654],{"class":147,"line":166},[145,4653,4189],{"class":155},[145,4655,1941],{"class":262},[145,4657,4658,4660,4662,4665],{"class":147,"line":178},[145,4659,4196],{"class":155},[145,4661,436],{"class":262},[145,4663,4664],{"class":159},"\"Image file size exceeds 30MB limit\"",[145,4666,451],{"class":262},[145,4668,4669,4671,4673],{"class":147,"line":187},[145,4670,4208],{"class":155},[145,4672,436],{"class":262},[145,4674,4675],{"class":159},"\"request_too_large_error\"\n",[145,4677,4678],{"class":147,"line":372},[145,4679,2025],{"class":262},[145,4681,4682],{"class":147,"line":378},[145,4683,468],{"class":262},[10,4685,4686],{},"업로드 전에 이미지를 압축하세요. API에서는 2~3 MB 이상의 이미지 품질 향상이 거의 없습니다.",[10,4688,4689],{},[22,4690,4691],{},"429 — 속도 제한:",[136,4693,4695],{"className":1854,"code":4694,"language":1856,"meta":141,"style":141},"{\n  \"error\": {\n    \"message\": \"Rate limit exceeded. Please retry after 60 seconds.\",\n    \"type\": \"rate_limit_error\"\n  }\n}\n",[27,4696,4697,4701,4707,4718,4727,4731],{"__ignoreMap":141},[145,4698,4699],{"class":147,"line":148},[145,4700,1863],{"class":262},[145,4702,4703,4705],{"class":147,"line":166},[145,4704,4189],{"class":155},[145,4706,1941],{"class":262},[145,4708,4709,4711,4713,4716],{"class":147,"line":178},[145,4710,4196],{"class":155},[145,4712,436],{"class":262},[145,4714,4715],{"class":159},"\"Rate limit exceeded. Please retry after 60 seconds.\"",[145,4717,451],{"class":262},[145,4719,4720,4722,4724],{"class":147,"line":187},[145,4721,4208],{"class":155},[145,4723,436],{"class":262},[145,4725,4726],{"class":159},"\"rate_limit_error\"\n",[145,4728,4729],{"class":147,"line":372},[145,4730,2025],{"class":262},[145,4732,4733],{"class":147,"line":378},[145,4734,468],{"class":262},[10,4736,4737],{},"요청을 너무 자주 보내고 있습니다. 기본 제한은 개발에 충분히 여유롭지만, 배치 스크립트에서는 걸릴 수 있습니다. 지수 백오프를 구현하세요(아래 참조).",[10,4739,4740],{},[22,4741,4742],{},"422 — 콘텐츠 필터 거부:",[136,4744,4746],{"className":1854,"code":4745,"language":1856,"meta":141,"style":141},"{\n  \"error\": {\n    \"message\": \"Content rejected by safety filter\",\n    \"type\": \"content_policy_violation\",\n    \"code\": \"content_filtered\"\n  }\n}\n",[27,4747,4748,4752,4758,4769,4780,4789,4793],{"__ignoreMap":141},[145,4749,4750],{"class":147,"line":148},[145,4751,1863],{"class":262},[145,4753,4754,4756],{"class":147,"line":166},[145,4755,4189],{"class":155},[145,4757,1941],{"class":262},[145,4759,4760,4762,4764,4767],{"class":147,"line":178},[145,4761,4196],{"class":155},[145,4763,436],{"class":262},[145,4765,4766],{"class":159},"\"Content rejected by safety filter\"",[145,4768,451],{"class":262},[145,4770,4771,4773,4775,4778],{"class":147,"line":187},[145,4772,4208],{"class":155},[145,4774,436],{"class":262},[145,4776,4777],{"class":159},"\"content_policy_violation\"",[145,4779,451],{"class":262},[145,4781,4782,4784,4786],{"class":147,"line":372},[145,4783,4220],{"class":155},[145,4785,436],{"class":262},[145,4787,4788],{"class":159},"\"content_filtered\"\n",[145,4790,4791],{"class":147,"line":378},[145,4792,2025],{"class":262},[145,4794,4795],{"class":147,"line":384},[145,4796,468],{"class":262},[10,4798,4799,4800,4802],{},"프롬프트나 입력 이미지가 콘텐츠 안전 필터에 감지되었습니다. 제한된 콘텐츠를 피하도록 프롬프트를 수정하세요. ",[27,4801,2897],{},"의 사실적인 인물 얼굴 이미지는 자동으로 거부됩니다.",[91,4804,4806],{"id":4805},"오류-참조-표","오류 참조 표",[2034,4808,4809,4826],{},[2037,4810,4811],{},[2040,4812,4813,4816,4818,4820,4823],{},[2043,4814,4815],{},"HTTP 코드",[2043,4817,3405],{},[2043,4819,2048],{},[2043,4821,4822],{},"재시도 가능?",[2043,4824,4825],{},"조치",[2050,4827,4828,4846,4864,4882,4900,4918,4936,4957,4977,4997],{},[2040,4829,4830,4832,4837,4840,4843],{},[2055,4831,3640],{},[2055,4833,4834],{},[27,4835,4836],{},"invalid_request_error",[2055,4838,4839],{},"잘못된 파라미터",[2055,4841,4842],{},"아니오",[2055,4844,4845],{},"payload 수정",[2040,4847,4848,4851,4856,4859,4861],{},[2055,4849,4850],{},"401",[2055,4852,4853],{},[27,4854,4855],{},"authentication_error",[2055,4857,4858],{},"잘못된 API Key",[2055,4860,4842],{},[2055,4862,4863],{},"Key 확인",[2040,4865,4866,4869,4874,4877,4879],{},[2055,4867,4868],{},"402",[2055,4870,4871],{},[27,4872,4873],{},"insufficient_quota_error",[2055,4875,4876],{},"크레딧 부족",[2055,4878,4842],{},[2055,4880,4881],{},"충전",[2040,4883,4884,4887,4892,4895,4897],{},[2055,4885,4886],{},"404",[2055,4888,4889],{},[27,4890,4891],{},"not_found_error",[2055,4893,4894],{},"작업 또는 모델 없음",[2055,4896,4842],{},[2055,4898,4899],{},"task_id / 모델명 확인",[2040,4901,4902,4905,4910,4913,4915],{},[2055,4903,4904],{},"413",[2055,4906,4907],{},[27,4908,4909],{},"request_too_large_error",[2055,4911,4912],{},"요청이 너무 큼",[2055,4914,4842],{},[2055,4916,4917],{},"파일 크기 줄이기",[2040,4919,4920,4923,4928,4931,4933],{},[2055,4921,4922],{},"422",[2055,4924,4925],{},[27,4926,4927],{},"content_policy_violation",[2055,4929,4930],{},"콘텐츠 필터링됨",[2055,4932,4842],{},[2055,4934,4935],{},"프롬프트 수정",[2040,4937,4938,4941,4946,4949,4954],{},[2055,4939,4940],{},"429",[2055,4942,4943],{},[27,4944,4945],{},"rate_limit_error",[2055,4947,4948],{},"요청이 너무 빈번함",[2055,4950,4951],{},[22,4952,4953],{},"예",[2055,4955,4956],{},"60초 후 재시도",[2040,4958,4959,4962,4967,4970,4974],{},[2055,4960,4961],{},"500",[2055,4963,4964],{},[27,4965,4966],{},"internal_server_error",[2055,4968,4969],{},"서버 문제",[2055,4971,4972],{},[22,4973,4953],{},[2055,4975,4976],{},"몇 초 후 재시도",[2040,4978,4979,4982,4987,4990,4994],{},[2055,4980,4981],{},"502",[2055,4983,4984],{},[27,4985,4986],{},"bad_gateway",[2055,4988,4989],{},"업스트림 오류",[2055,4991,4992],{},[22,4993,4953],{},[2055,4995,4996],{},"5초 후 재시도",[2040,4998,4999,5002,5007,5010,5014],{},[2055,5000,5001],{},"503",[2055,5003,5004],{},[27,5005,5006],{},"service_unavailable_error",[2055,5008,5009],{},"서비스 불가",[2055,5011,5012],{},[22,5013,4953],{},[2055,5015,5016],{},"30초 후 재시도",[91,5018,5020],{"id":5019},"프로덕션-수준-오류-처리","프로덕션 수준 오류 처리",[10,5022,5023],{},"일시적 오류에 대한 재시도 로직으로 API 호출을 감싸세요:",[10,5025,5026],{},[2620,5027,2622],{},[136,5029,5031],{"className":337,"code":5030,"language":339,"meta":141,"style":141},"import random\n\ndef generate_video_with_retry(payload, max_retries=3):\n    \"\"\"\n    일시적 오류(429, 500, 502, 503)에 대해 자동 재시도하는\n    비디오 생성 요청 제출.\n    \n    썬더링 허드를 피하기 위해 지수 백오프 + 지터 사용:\n    - 시도 1: ~1초 대기\n    - 시도 2: ~2초 대기\n    - 시도 3: ~4초 대기\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,5032,5033,5040,5044,5061,5065,5070,5075,5079,5084,5089,5094,5099,5103,5108,5113,5117,5133,5140,5149,5161,5172,5182,5195,5199,5203,5208,5221,5228,5232,5237,5251,5271,5290,5294,5299,5333,5343,5369,5374,5378,5383,5409,5442,5476,5517,5522,5527,5531,5540,5546,5574,5605,5625,5631,5637,5642,5655,5661,5688,5728,5747,5752,5757,5762],{"__ignoreMap":141},[145,5034,5035,5037],{"class":147,"line":148},[145,5036,346],{"class":258},[145,5038,5039],{"class":262}," random\n",[145,5041,5042],{"class":147,"line":166},[145,5043,375],{"emptyLinePlaceholder":57},[145,5045,5046,5048,5051,5054,5056,5059],{"class":147,"line":178},[145,5047,525],{"class":258},[145,5049,5050],{"class":151}," generate_video_with_retry",[145,5052,5053],{"class":262},"(payload, max_retries",[145,5055,266],{"class":258},[145,5057,5058],{"class":155},"3",[145,5060,547],{"class":262},[145,5062,5063],{"class":147,"line":187},[145,5064,552],{"class":159},[145,5066,5067],{"class":147,"line":372},[145,5068,5069],{"class":159},"    일시적 오류(429, 500, 502, 503)에 대해 자동 재시도하는\n",[145,5071,5072],{"class":147,"line":378},[145,5073,5074],{"class":159},"    비디오 생성 요청 제출.\n",[145,5076,5077],{"class":147,"line":384},[145,5078,562],{"class":159},[145,5080,5081],{"class":147,"line":408},[145,5082,5083],{"class":159},"    썬더링 허드를 피하기 위해 지수 백오프 + 지터 사용:\n",[145,5085,5086],{"class":147,"line":419},[145,5087,5088],{"class":159},"    - 시도 1: ~1초 대기\n",[145,5090,5091],{"class":147,"line":430},[145,5092,5093],{"class":159},"    - 시도 2: ~2초 대기\n",[145,5095,5096],{"class":147,"line":454},[145,5097,5098],{"class":159},"    - 시도 3: ~4초 대기\n",[145,5100,5101],{"class":147,"line":465},[145,5102,562],{"class":159},[145,5104,5105],{"class":147,"line":599},[145,5106,5107],{"class":159},"    재시도 불가능한 오류(400, 401, 402, 404, 413, 422)는\n",[145,5109,5110],{"class":147,"line":604},[145,5111,5112],{"class":159},"    근본적인 문제를 재시도로 해결할 수 없으므로 즉시 실패합니다.\n",[145,5114,5115],{"class":147,"line":610},[145,5116,552],{"class":159},[145,5118,5119,5122,5125,5127,5130],{"class":147,"line":616},[145,5120,5121],{"class":258},"    for",[145,5123,5124],{"class":262}," attempt ",[145,5126,1179],{"class":258},[145,5128,5129],{"class":155}," range",[145,5131,5132],{"class":262},"(max_retries):\n",[145,5134,5135,5138],{"class":147,"line":622},[145,5136,5137],{"class":258},"        try",[145,5139,859],{"class":262},[145,5141,5142,5145,5147],{"class":147,"line":627},[145,5143,5144],{"class":262},"            response ",[145,5146,266],{"class":258},[145,5148,1431],{"class":262},[145,5150,5151,5153,5155,5157,5159],{"class":147,"line":638},[145,5152,917],{"class":258},[145,5154,448],{"class":159},[145,5156,678],{"class":155},[145,5158,1443],{"class":159},[145,5160,451],{"class":262},[145,5162,5163,5166,5168,5170],{"class":147,"line":653},[145,5164,5165],{"class":700},"                headers",[145,5167,266],{"class":258},[145,5169,422],{"class":155},[145,5171,451],{"class":262},[145,5173,5174,5177,5179],{"class":147,"line":659},[145,5175,5176],{"class":700},"                json",[145,5178,266],{"class":258},[145,5180,5181],{"class":262},"payload,\n",[145,5183,5184,5187,5189,5192],{"class":147,"line":670},[145,5185,5186],{"class":700},"                timeout",[145,5188,266],{"class":258},[145,5190,5191],{"class":155},"30",[145,5193,5194],{"class":174},"       # 30초 연결 타임아웃\n",[145,5196,5197],{"class":147,"line":697},[145,5198,955],{"class":262},[145,5200,5201],{"class":147,"line":709},[145,5202,375],{"emptyLinePlaceholder":57},[145,5204,5205],{"class":147,"line":715},[145,5206,5207],{"class":174},"            # 성공 — task 객체 반환\n",[145,5209,5210,5213,5215,5217,5219],{"class":147,"line":721},[145,5211,5212],{"class":258},"            if",[145,5214,2510],{"class":262},[145,5216,853],{"class":258},[145,5218,2515],{"class":155},[145,5220,859],{"class":262},[145,5222,5223,5226],{"class":147,"line":727},[145,5224,5225],{"class":258},"                return",[145,5227,735],{"class":262},[145,5229,5230],{"class":147,"line":738},[145,5231,375],{"emptyLinePlaceholder":57},[145,5233,5234],{"class":147,"line":743},[145,5235,5236],{"class":174},"            # 오류 응답 파싱\n",[145,5238,5239,5242,5244,5247,5249],{"class":147,"line":749},[145,5240,5241],{"class":262},"            error ",[145,5243,266],{"class":258},[145,5245,5246],{"class":262}," response.json().get(",[145,5248,896],{"class":159},[145,5250,899],{"class":262},[145,5252,5253,5256,5258,5261,5264,5266,5269],{"class":147,"line":766},[145,5254,5255],{"class":262},"            error_type ",[145,5257,266],{"class":258},[145,5259,5260],{"class":262}," error.get(",[145,5262,5263],{"class":159},"\"type\"",[145,5265,399],{"class":262},[145,5267,5268],{"class":159},"\"\"",[145,5270,405],{"class":262},[145,5272,5273,5276,5278,5280,5283,5285,5288],{"class":147,"line":787},[145,5274,5275],{"class":262},"            error_msg ",[145,5277,266],{"class":258},[145,5279,5260],{"class":262},[145,5281,5282],{"class":159},"\"message\"",[145,5284,399],{"class":262},[145,5286,5287],{"class":159},"\"Unknown error\"",[145,5289,405],{"class":262},[145,5291,5292],{"class":147,"line":833},[145,5293,375],{"emptyLinePlaceholder":57},[145,5295,5296],{"class":147,"line":838},[145,5297,5298],{"class":174},"            # 재시도 불가능한 오류 — 즉시 실패\n",[145,5300,5301,5303,5305,5307,5309,5311,5313,5315,5317,5319,5321,5323,5325,5327,5329,5331],{"class":147,"line":844},[145,5302,5212],{"class":258},[145,5304,2510],{"class":262},[145,5306,1179],{"class":258},[145,5308,103],{"class":262},[145,5310,3640],{"class":155},[145,5312,399],{"class":262},[145,5314,4850],{"class":155},[145,5316,399],{"class":262},[145,5318,4868],{"class":155},[145,5320,399],{"class":262},[145,5322,4886],{"class":155},[145,5324,399],{"class":262},[145,5326,4904],{"class":155},[145,5328,399],{"class":262},[145,5330,4922],{"class":155},[145,5332,547],{"class":262},[145,5334,5335,5338,5341],{"class":147,"line":862},[145,5336,5337],{"class":258},"                raise",[145,5339,5340],{"class":155}," ValueError",[145,5342,911],{"class":262},[145,5344,5345,5348,5351,5353,5356,5358,5360,5362,5365,5367],{"class":147,"line":871},[145,5346,5347],{"class":258},"                    f",[145,5349,5350],{"class":159},"\"API error ",[145,5352,684],{"class":155},[145,5354,5355],{"class":262},"response.status_code",[145,5357,690],{"class":155},[145,5359,436],{"class":159},[145,5361,684],{"class":155},[145,5363,5364],{"class":262},"error_msg",[145,5366,690],{"class":155},[145,5368,949],{"class":159},[145,5370,5371],{"class":147,"line":886},[145,5372,5373],{"class":262},"                )\n",[145,5375,5376],{"class":147,"line":902},[145,5377,375],{"emptyLinePlaceholder":57},[145,5379,5380],{"class":147,"line":914},[145,5381,5382],{"class":174},"            # 재시도 가능한 오류 — 지수 백오프 + 지터\n",[145,5384,5385,5387,5389,5391,5393,5395,5397,5399,5401,5403,5405,5407],{"class":147,"line":952},[145,5386,5212],{"class":258},[145,5388,2510],{"class":262},[145,5390,1179],{"class":258},[145,5392,103],{"class":262},[145,5394,4940],{"class":155},[145,5396,399],{"class":262},[145,5398,4961],{"class":155},[145,5400,399],{"class":262},[145,5402,4981],{"class":155},[145,5404,399],{"class":262},[145,5406,5001],{"class":155},[145,5408,547],{"class":262},[145,5410,5411,5414,5416,5418,5421,5424,5427,5430,5433,5435,5437,5440],{"class":147,"line":958},[145,5412,5413],{"class":262},"                wait ",[145,5415,266],{"class":258},[145,5417,103],{"class":262},[145,5419,5420],{"class":155},"2",[145,5422,5423],{"class":258}," **",[145,5425,5426],{"class":262}," attempt) ",[145,5428,5429],{"class":258},"+",[145,5431,5432],{"class":262}," random.uniform(",[145,5434,782],{"class":155},[145,5436,399],{"class":262},[145,5438,5439],{"class":155},"1",[145,5441,405],{"class":262},[145,5443,5444,5447,5449,5451,5454,5456,5459,5461,5464,5466,5468,5471,5473],{"class":147,"line":963},[145,5445,5446],{"class":155},"                print",[145,5448,793],{"class":262},[145,5450,439],{"class":258},[145,5452,5453],{"class":159},"\"  Retry ",[145,5455,684],{"class":155},[145,5457,5458],{"class":262},"attempt ",[145,5460,5429],{"class":258},[145,5462,5463],{"class":155}," 1}",[145,5465,1224],{"class":159},[145,5467,684],{"class":155},[145,5469,5470],{"class":262},"max_retries",[145,5472,690],{"class":155},[145,5474,5475],{"class":159}," \"\n",[145,5477,5478,5481,5484,5486,5489,5492,5494,5497,5499,5502,5504,5506,5508,5510,5512,5515],{"class":147,"line":969},[145,5479,5480],{"class":258},"                      f",[145,5482,5483],{"class":159},"\"after ",[145,5485,684],{"class":155},[145,5487,5488],{"class":262},"wait",[145,5490,5491],{"class":258},":.1f",[145,5493,690],{"class":155},[145,5495,5496],{"class":159},"s (",[145,5498,684],{"class":155},[145,5500,5501],{"class":262},"error_type",[145,5503,690],{"class":155},[145,5505,436],{"class":159},[145,5507,684],{"class":155},[145,5509,5364],{"class":262},[145,5511,690],{"class":155},[145,5513,5514],{"class":159},")\"",[145,5516,405],{"class":262},[145,5518,5519],{"class":147,"line":975},[145,5520,5521],{"class":262},"                time.sleep(wait)\n",[145,5523,5524],{"class":147,"line":987},[145,5525,5526],{"class":258},"                continue\n",[145,5528,5529],{"class":147,"line":992},[145,5530,375],{"emptyLinePlaceholder":57},[145,5532,5534,5537],{"class":147,"line":5533},48,[145,5535,5536],{"class":258},"        except",[145,5538,5539],{"class":262}," requests.exceptions.Timeout:\n",[145,5541,5543],{"class":147,"line":5542},49,[145,5544,5545],{"class":174},"            # 서버가 30초 내에 응답하지 않음\n",[145,5547,5549,5552,5554,5556,5558,5560,5562,5564,5566,5568,5570,5572],{"class":147,"line":5548},50,[145,5550,5551],{"class":262},"            wait ",[145,5553,266],{"class":258},[145,5555,103],{"class":262},[145,5557,5420],{"class":155},[145,5559,5423],{"class":258},[145,5561,5426],{"class":262},[145,5563,5429],{"class":258},[145,5565,5432],{"class":262},[145,5567,782],{"class":155},[145,5569,399],{"class":262},[145,5571,5439],{"class":155},[145,5573,405],{"class":262},[145,5575,5577,5580,5582,5584,5587,5589,5591,5593,5595,5597,5599,5601,5603],{"class":147,"line":5576},51,[145,5578,5579],{"class":155},"            print",[145,5581,793],{"class":262},[145,5583,439],{"class":258},[145,5585,5586],{"class":159},"\"  Timeout. Retry ",[145,5588,684],{"class":155},[145,5590,5458],{"class":262},[145,5592,5429],{"class":258},[145,5594,5463],{"class":155},[145,5596,1224],{"class":159},[145,5598,684],{"class":155},[145,5600,5470],{"class":262},[145,5602,690],{"class":155},[145,5604,5475],{"class":159},[145,5606,5608,5611,5613,5615,5617,5619,5621,5623],{"class":147,"line":5607},52,[145,5609,5610],{"class":258},"                  f",[145,5612,5483],{"class":159},[145,5614,684],{"class":155},[145,5616,5488],{"class":262},[145,5618,5491],{"class":258},[145,5620,690],{"class":155},[145,5622,1023],{"class":159},[145,5624,405],{"class":262},[145,5626,5628],{"class":147,"line":5627},53,[145,5629,5630],{"class":262},"            time.sleep(wait)\n",[145,5632,5634],{"class":147,"line":5633},54,[145,5635,5636],{"class":258},"            continue\n",[145,5638,5640],{"class":147,"line":5639},55,[145,5641,375],{"emptyLinePlaceholder":57},[145,5643,5645,5647,5650,5652],{"class":147,"line":5644},56,[145,5646,5536],{"class":258},[145,5648,5649],{"class":262}," requests.exceptions.ConnectionError ",[145,5651,1165],{"class":258},[145,5653,5654],{"class":262}," e:\n",[145,5656,5658],{"class":147,"line":5657},57,[145,5659,5660],{"class":174},"            # DNS 실패, 연결 거부 등\n",[145,5662,5664,5666,5668,5670,5672,5674,5676,5678,5680,5682,5684,5686],{"class":147,"line":5663},58,[145,5665,5551],{"class":262},[145,5667,266],{"class":258},[145,5669,103],{"class":262},[145,5671,5420],{"class":155},[145,5673,5423],{"class":258},[145,5675,5426],{"class":262},[145,5677,5429],{"class":258},[145,5679,5432],{"class":262},[145,5681,782],{"class":155},[145,5683,399],{"class":262},[145,5685,5439],{"class":155},[145,5687,405],{"class":262},[145,5689,5691,5693,5695,5697,5700,5702,5705,5707,5710,5712,5714,5716,5718,5720,5722,5724,5726],{"class":147,"line":5690},59,[145,5692,5579],{"class":155},[145,5694,793],{"class":262},[145,5696,439],{"class":258},[145,5698,5699],{"class":159},"\"  Connection error: ",[145,5701,684],{"class":155},[145,5703,5704],{"class":262},"e",[145,5706,690],{"class":155},[145,5708,5709],{"class":159},". Retry ",[145,5711,684],{"class":155},[145,5713,5458],{"class":262},[145,5715,5429],{"class":258},[145,5717,5463],{"class":155},[145,5719,1224],{"class":159},[145,5721,684],{"class":155},[145,5723,5470],{"class":262},[145,5725,690],{"class":155},[145,5727,5475],{"class":159},[145,5729,5731,5733,5735,5737,5739,5741,5743,5745],{"class":147,"line":5730},60,[145,5732,5610],{"class":258},[145,5734,5483],{"class":159},[145,5736,684],{"class":155},[145,5738,5488],{"class":262},[145,5740,5491],{"class":258},[145,5742,690],{"class":155},[145,5744,1023],{"class":159},[145,5746,405],{"class":262},[145,5748,5750],{"class":147,"line":5749},61,[145,5751,5630],{"class":262},[145,5753,5755],{"class":147,"line":5754},62,[145,5756,5636],{"class":258},[145,5758,5760],{"class":147,"line":5759},63,[145,5761,375],{"emptyLinePlaceholder":57},[145,5763,5765,5767,5769,5771,5773,5776,5778,5780,5782,5785],{"class":147,"line":5764},64,[145,5766,995],{"class":258},[145,5768,908],{"class":155},[145,5770,793],{"class":262},[145,5772,439],{"class":258},[145,5774,5775],{"class":159},"\"Failed after ",[145,5777,684],{"class":155},[145,5779,5470],{"class":262},[145,5781,690],{"class":155},[145,5783,5784],{"class":159}," retries\"",[145,5786,405],{"class":262},[10,5788,5789],{},"이 코드가 처리하는 것:",[96,5791,5792,5798,5804,5810,5816],{},[72,5793,5794,5797],{},[22,5795,5796],{},"속도 제한 (429)"," — 지수 백오프 + 지터로 여러 클라이언트의 동기화된 재시도 방지",[72,5799,5800,5803],{},[22,5801,5802],{},"서버 오류 (500/502/503)"," — 증가하는 지연으로 자동 재시도",[72,5805,5806,5809],{},[22,5807,5808],{},"타임아웃"," — 30초 타임아웃으로 응답 없는 서버에서 멈추는 것 방지",[72,5811,5812,5815],{},[22,5813,5814],{},"연결 끊김"," — DNS 실패, 연결 거부, 네트워크 불안정",[72,5817,5818,5821],{},[22,5819,5820],{},"클라이언트 오류 (400/401/402/404/413/422)"," — 잘못된 입력은 재시도로 해결할 수 없으므로 즉시 실패",[17,5823,5824],{},[10,5825,5826,5828],{},[22,5827,133],{}," 프로덕션 시스템에서는 실패한 요청의 전체 payload와 오류 응답을 로깅하는 것을 권장합니다. 새벽 3시에 문제가 발생했을 때 디버깅이 훨씬 쉬워집니다.",[91,5830,5832],{"id":5831},"api-호출-전-입력-검증","API 호출 전 입력 검증",[10,5834,5835],{},"API를 호출하기 전에 명백한 오류를 로컬에서 잡아 크레딧과 시간을 절약하세요:",[136,5837,5839],{"className":337,"code":5838,"language":339,"meta":141,"style":141},"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,5840,5841,5851,5855,5860,5865,5869,5879,5883,5888,5903,5913,5939,5948,5952,5957,5975,6000,6019,6023,6028,6050,6068,6084,6114,6118,6123,6159,6177,6191,6220,6224,6229,6244,6261,6282,6297,6317,6339,6343,6350,6405,6409],{"__ignoreMap":141},[145,5842,5843,5845,5848],{"class":147,"line":148},[145,5844,525],{"class":258},[145,5846,5847],{"class":151}," validate_payload",[145,5849,5850],{"class":262},"(payload):\n",[145,5852,5853],{"class":147,"line":166},[145,5854,552],{"class":159},[145,5856,5857],{"class":147,"line":178},[145,5858,5859],{"class":159},"    API로 보내기 전에 생성 payload를 검증합니다.\n",[145,5861,5862],{"class":147,"line":187},[145,5863,5864],{"class":159},"    400 오류를 유발하는 일반적인 실수를 잡습니다.\n",[145,5866,5867],{"class":147,"line":372},[145,5868,552],{"class":159},[145,5870,5871,5874,5876],{"class":147,"line":378},[145,5872,5873],{"class":262},"    errors ",[145,5875,266],{"class":258},[145,5877,5878],{"class":262}," []\n",[145,5880,5881],{"class":147,"line":384},[145,5882,562],{"class":262},[145,5884,5885],{"class":147,"line":408},[145,5886,5887],{"class":174},"    # 필수 필드\n",[145,5889,5890,5892,5895,5898,5901],{"class":147,"line":419},[145,5891,2507],{"class":258},[145,5893,5894],{"class":258}," not",[145,5896,5897],{"class":262}," payload.get(",[145,5899,5900],{"class":159},"\"model\"",[145,5902,547],{"class":262},[145,5904,5905,5908,5911],{"class":147,"line":430},[145,5906,5907],{"class":262},"        errors.append(",[145,5909,5910],{"class":159},"\"'model' is required\"",[145,5912,405],{"class":262},[145,5914,5915,5917,5919,5921,5924,5926,5929,5931,5934,5936],{"class":147,"line":454},[145,5916,2507],{"class":258},[145,5918,5894],{"class":258},[145,5920,5897],{"class":262},[145,5922,5923],{"class":159},"\"prompt\"",[145,5925,1162],{"class":262},[145,5927,5928],{"class":258},"or",[145,5930,5894],{"class":258},[145,5932,5933],{"class":262}," payload[",[145,5935,5923],{"class":159},[145,5937,5938],{"class":262},"].strip():\n",[145,5940,5941,5943,5946],{"class":147,"line":465},[145,5942,5907],{"class":262},[145,5944,5945],{"class":159},"\"'prompt' is required and cannot be empty\"",[145,5947,405],{"class":262},[145,5949,5950],{"class":147,"line":599},[145,5951,562],{"class":262},[145,5953,5954],{"class":147,"line":604},[145,5955,5956],{"class":174},"    # 길이 범위\n",[145,5958,5959,5962,5964,5966,5969,5971,5973],{"class":147,"line":610},[145,5960,5961],{"class":262},"    duration ",[145,5963,266],{"class":258},[145,5965,5897],{"class":262},[145,5967,5968],{"class":159},"\"duration\"",[145,5970,399],{"class":262},[145,5972,1351],{"class":155},[145,5974,405],{"class":262},[145,5976,5977,5979,5982,5984,5987,5990,5992,5995,5998],{"class":147,"line":616},[145,5978,2507],{"class":258},[145,5980,5981],{"class":262}," duration ",[145,5983,647],{"class":258},[145,5985,5986],{"class":155}," 4",[145,5988,5989],{"class":258}," or",[145,5991,5981],{"class":262},[145,5993,5994],{"class":258},">",[145,5996,5997],{"class":155}," 15",[145,5999,859],{"class":262},[145,6001,6002,6004,6006,6009,6011,6013,6015,6017],{"class":147,"line":622},[145,6003,5907],{"class":262},[145,6005,439],{"class":258},[145,6007,6008],{"class":159},"\"'duration' must be 4-15, got ",[145,6010,684],{"class":155},[145,6012,1773],{"class":262},[145,6014,690],{"class":155},[145,6016,448],{"class":159},[145,6018,405],{"class":262},[145,6020,6021],{"class":147,"line":627},[145,6022,562],{"class":262},[145,6024,6025],{"class":147,"line":638},[145,6026,6027],{"class":174},"    # 품질 값\n",[145,6029,6030,6033,6035,6038,6040,6042,6044,6046,6048],{"class":147,"line":653},[145,6031,6032],{"class":262},"    valid_qualities ",[145,6034,266],{"class":258},[145,6036,6037],{"class":262}," {",[145,6039,4016],{"class":159},[145,6041,399],{"class":262},[145,6043,1367],{"class":159},[145,6045,399],{"class":262},[145,6047,3713],{"class":159},[145,6049,468],{"class":262},[145,6051,6052,6055,6057,6059,6062,6064,6066],{"class":147,"line":659},[145,6053,6054],{"class":262},"    quality ",[145,6056,266],{"class":258},[145,6058,5897],{"class":262},[145,6060,6061],{"class":159},"\"quality\"",[145,6063,399],{"class":262},[145,6065,1367],{"class":159},[145,6067,405],{"class":262},[145,6069,6070,6072,6075,6078,6081],{"class":147,"line":670},[145,6071,2507],{"class":258},[145,6073,6074],{"class":262}," quality ",[145,6076,6077],{"class":258},"not",[145,6079,6080],{"class":258}," in",[145,6082,6083],{"class":262}," valid_qualities:\n",[145,6085,6086,6088,6090,6093,6095,6098,6100,6103,6105,6107,6109,6112],{"class":147,"line":697},[145,6087,5907],{"class":262},[145,6089,439],{"class":258},[145,6091,6092],{"class":159},"\"'quality' must be one of ",[145,6094,684],{"class":155},[145,6096,6097],{"class":262},"valid_qualities",[145,6099,690],{"class":155},[145,6101,6102],{"class":159},", got '",[145,6104,684],{"class":155},[145,6106,1781],{"class":262},[145,6108,690],{"class":155},[145,6110,6111],{"class":159},"'\"",[145,6113,405],{"class":262},[145,6115,6116],{"class":147,"line":709},[145,6117,562],{"class":262},[145,6119,6120],{"class":147,"line":715},[145,6121,6122],{"class":174},"    # 화면 비율 값\n",[145,6124,6125,6128,6130,6132,6134,6136,6138,6140,6143,6145,6148,6150,6153,6155,6157],{"class":147,"line":721},[145,6126,6127],{"class":262},"    valid_ratios ",[145,6129,266],{"class":258},[145,6131,6037],{"class":262},[145,6133,1383],{"class":159},[145,6135,399],{"class":262},[145,6137,3725],{"class":159},[145,6139,399],{"class":262},[145,6141,6142],{"class":159},"\"1:1\"",[145,6144,399],{"class":262},[145,6146,6147],{"class":159},"\"4:3\"",[145,6149,399],{"class":262},[145,6151,6152],{"class":159},"\"3:4\"",[145,6154,399],{"class":262},[145,6156,3836],{"class":159},[145,6158,468],{"class":262},[145,6160,6161,6164,6166,6168,6171,6173,6175],{"class":147,"line":727},[145,6162,6163],{"class":262},"    ratio ",[145,6165,266],{"class":258},[145,6167,5897],{"class":262},[145,6169,6170],{"class":159},"\"aspect_ratio\"",[145,6172,399],{"class":262},[145,6174,1383],{"class":159},[145,6176,405],{"class":262},[145,6178,6179,6181,6184,6186,6188],{"class":147,"line":738},[145,6180,2507],{"class":258},[145,6182,6183],{"class":262}," ratio ",[145,6185,6077],{"class":258},[145,6187,6080],{"class":258},[145,6189,6190],{"class":262}," valid_ratios:\n",[145,6192,6193,6195,6197,6200,6202,6205,6207,6209,6211,6214,6216,6218],{"class":147,"line":743},[145,6194,5907],{"class":262},[145,6196,439],{"class":258},[145,6198,6199],{"class":159},"\"'aspect_ratio' must be one of ",[145,6201,684],{"class":155},[145,6203,6204],{"class":262},"valid_ratios",[145,6206,690],{"class":155},[145,6208,6102],{"class":159},[145,6210,684],{"class":155},[145,6212,6213],{"class":262},"ratio",[145,6215,690],{"class":155},[145,6217,6111],{"class":159},[145,6219,405],{"class":262},[145,6221,6222],{"class":147,"line":749},[145,6223,562],{"class":262},[145,6225,6226],{"class":147,"line":766},[145,6227,6228],{"class":174},"    # 이미지 URL 검증\n",[145,6230,6231,6234,6236,6238,6241],{"class":147,"line":787},[145,6232,6233],{"class":262},"    image_urls ",[145,6235,266],{"class":258},[145,6237,5897],{"class":262},[145,6239,6240],{"class":159},"\"image_urls\"",[145,6242,6243],{"class":262},", [])\n",[145,6245,6246,6248,6251,6254,6256,6259],{"class":147,"line":833},[145,6247,2507],{"class":258},[145,6249,6250],{"class":155}," len",[145,6252,6253],{"class":262},"(image_urls) ",[145,6255,5994],{"class":258},[145,6257,6258],{"class":155}," 9",[145,6260,859],{"class":262},[145,6262,6263,6265,6267,6270,6273,6276,6278,6280],{"class":147,"line":838},[145,6264,5907],{"class":262},[145,6266,439],{"class":258},[145,6268,6269],{"class":159},"\"Maximum 9 images allowed, got ",[145,6271,6272],{"class":155},"{len",[145,6274,6275],{"class":262},"(image_urls)",[145,6277,690],{"class":155},[145,6279,448],{"class":159},[145,6281,405],{"class":262},[145,6283,6284,6286,6289,6291,6294],{"class":147,"line":844},[145,6285,5121],{"class":258},[145,6287,6288],{"class":262}," i, url ",[145,6290,1179],{"class":258},[145,6292,6293],{"class":155}," enumerate",[145,6295,6296],{"class":262},"(image_urls):\n",[145,6298,6299,6301,6303,6306,6309,6311,6314],{"class":147,"line":862},[145,6300,847],{"class":258},[145,6302,5894],{"class":258},[145,6304,6305],{"class":262}," url.startswith((",[145,6307,6308],{"class":159},"\"http://\"",[145,6310,399],{"class":262},[145,6312,6313],{"class":159},"\"https://\"",[145,6315,6316],{"class":262},")):\n",[145,6318,6319,6322,6324,6327,6329,6332,6334,6337],{"class":147,"line":871},[145,6320,6321],{"class":262},"            errors.append(",[145,6323,439],{"class":258},[145,6325,6326],{"class":159},"\"image_urls[",[145,6328,684],{"class":155},[145,6330,6331],{"class":262},"i",[145,6333,690],{"class":155},[145,6335,6336],{"class":159},"] must be an HTTP(S) URL\"",[145,6338,405],{"class":262},[145,6340,6341],{"class":147,"line":886},[145,6342,562],{"class":262},[145,6344,6345,6347],{"class":147,"line":902},[145,6346,2507],{"class":258},[145,6348,6349],{"class":262}," errors:\n",[145,6351,6352,6355,6357,6359,6361,6364,6366,6368,6371,6374,6376,6378,6381,6383,6386,6388,6390,6392,6394,6397,6400,6402],{"class":147,"line":914},[145,6353,6354],{"class":258},"        raise",[145,6356,5340],{"class":155},[145,6358,793],{"class":262},[145,6360,439],{"class":258},[145,6362,6363],{"class":159},"\"Payload validation failed:",[145,6365,1669],{"class":155},[145,6367,448],{"class":159},[145,6369,6370],{"class":258}," +",[145,6372,6373],{"class":159}," \"",[145,6375,1669],{"class":155},[145,6377,448],{"class":159},[145,6379,6380],{"class":262},".join(",[145,6382,439],{"class":258},[145,6384,6385],{"class":159},"\"  - ",[145,6387,684],{"class":155},[145,6389,5704],{"class":262},[145,6391,690],{"class":155},[145,6393,448],{"class":159},[145,6395,6396],{"class":258}," for",[145,6398,6399],{"class":262}," e ",[145,6401,1179],{"class":258},[145,6403,6404],{"class":262}," errors))\n",[145,6406,6407],{"class":147,"line":952},[145,6408,562],{"class":262},[145,6410,6411,6413],{"class":147,"line":958},[145,6412,1702],{"class":258},[145,6414,6415],{"class":155}," True\n",[17,6417,6418],{},[10,6419,6420,6422,6423,6426],{},[22,6421,304],{}," 이미지 URL의 특수 문자를 URL 인코딩하는 것을 잊는 경우. 이미지 경로에 공백이나 비ASCII 문자가 포함되어 있다면 ",[27,6424,6425],{},"urllib.parse.quote()","로 인코딩하세요.",[44,6428],{},[47,6430,6432],{"id":6431},"webhook-설정-polling-건너뛰기","Webhook 설정 (Polling 건너뛰기)",[10,6434,6435,6436,6439],{},"Polling은 스크립트와 프로토타이핑에 충분합니다. 프로덕션 시스템에서는 ",[22,6437,6438],{},"webhook","이 더 효율적입니다 — 비디오가 준비되면 API가 결과를 서버로 직접 푸시합니다. 낭비되는 요청 없이, 완료와 알림 사이에 지연도 없습니다.",[91,6441,6443],{"id":6442},"작동-방식","작동 방식",[10,6445,6446,6447,6449],{},"생성 요청에 ",[27,6448,3602],{},"을 추가합니다:",[10,6451,6452],{},[2620,6453,6454],{},"위 첫 번째 예제와 동일한 설정을 사용합니다.",[136,6456,6458],{"className":337,"code":6457,"language":339,"meta":141,"style":141},"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# Polling 불필요 — webhook이 결과를 수신합니다\n",[27,6459,6460,6468,6478,6489,6499,6509,6519,6523,6527,6536,6549,6560,6569,6573,6582,6608],{"__ignoreMap":141},[145,6461,6462,6464,6466],{"class":147,"line":148},[145,6463,3664],{"class":262},[145,6465,266],{"class":258},[145,6467,427],{"class":262},[145,6469,6470,6472,6474,6476],{"class":147,"line":166},[145,6471,3673],{"class":159},[145,6473,436],{"class":262},[145,6475,1307],{"class":159},[145,6477,451],{"class":262},[145,6479,6480,6482,6484,6487],{"class":147,"line":178},[145,6481,3684],{"class":159},[145,6483,436],{"class":262},[145,6485,6486],{"class":159},"\"A spaceship launches from a desert landscape at sunset.\"",[145,6488,451],{"class":262},[145,6490,6491,6493,6495,6497],{"class":147,"line":187},[145,6492,3696],{"class":159},[145,6494,436],{"class":262},[145,6496,3701],{"class":155},[145,6498,451],{"class":262},[145,6500,6501,6503,6505,6507],{"class":147,"line":372},[145,6502,3708],{"class":159},[145,6504,436],{"class":262},[145,6506,1367],{"class":159},[145,6508,451],{"class":262},[145,6510,6511,6514,6516],{"class":147,"line":378},[145,6512,6513],{"class":159},"    \"callback_url\"",[145,6515,436],{"class":262},[145,6517,6518],{"class":159},"\"https://your-server.com/api/webhook/seedance\"\n",[145,6520,6521],{"class":147,"line":384},[145,6522,468],{"class":262},[145,6524,6525],{"class":147,"line":408},[145,6526,375],{"emptyLinePlaceholder":57},[145,6528,6529,6532,6534],{"class":147,"line":419},[145,6530,6531],{"class":262},"response ",[145,6533,266],{"class":258},[145,6535,1431],{"class":262},[145,6537,6538,6541,6543,6545,6547],{"class":147,"line":430},[145,6539,6540],{"class":258},"    f",[145,6542,448],{"class":159},[145,6544,678],{"class":155},[145,6546,1443],{"class":159},[145,6548,451],{"class":262},[145,6550,6551,6554,6556,6558],{"class":147,"line":454},[145,6552,6553],{"class":700},"    headers",[145,6555,266],{"class":258},[145,6557,422],{"class":155},[145,6559,451],{"class":262},[145,6561,6562,6565,6567],{"class":147,"line":465},[145,6563,6564],{"class":700},"    json",[145,6566,266],{"class":258},[145,6568,2789],{"class":262},[145,6570,6571],{"class":147,"line":599},[145,6572,405],{"class":262},[145,6574,6575,6578,6580],{"class":147,"line":604},[145,6576,6577],{"class":262},"task ",[145,6579,266],{"class":258},[145,6581,735],{"class":262},[145,6583,6584,6587,6589,6591,6594,6596,6598,6600,6602,6604,6606],{"class":147,"line":610},[145,6585,6586],{"class":155},"print",[145,6588,793],{"class":262},[145,6590,439],{"class":258},[145,6592,6593],{"class":159},"\"Task submitted: ",[145,6595,684],{"class":155},[145,6597,1528],{"class":262},[145,6599,1531],{"class":159},[145,6601,1534],{"class":262},[145,6603,690],{"class":155},[145,6605,448],{"class":159},[145,6607,405],{"class":262},[145,6609,6610],{"class":147,"line":616},[145,6611,6612],{"class":174},"# Polling 불필요 — webhook이 결과를 수신합니다\n",[10,6614,6615,6616,6618],{},"비디오가 준비되면 API가 ",[27,6617,3602],{},"로 완료된 task 객체를 담은 POST 요청을 보냅니다 — polling으로 받는 것과 완전히 동일한 내용입니다.",[91,6620,6622],{"id":6621},"webhook-요구사항","Webhook 요구사항",[2034,6624,6625,6635],{},[2037,6626,6627],{},[2040,6628,6629,6632],{},[2043,6630,6631],{},"요구사항",[2043,6633,6634],{},"상세",[2050,6636,6637,6645,6653,6661,6669,6677],{},[2040,6638,6639,6642],{},[2055,6640,6641],{},"프로토콜",[2055,6643,6644],{},"HTTPS만 지원 (HTTP 불가) — 보안 요구사항",[2040,6646,6647,6650],{},[2055,6648,6649],{},"응답",[2055,6651,6652],{},"10초 내에 2xx 반환",[2040,6654,6655,6658],{},[2055,6656,6657],{},"재시도",[2055,6659,6660],{},"실패 시 3회 재시도 (1초, 2초, 4초 간격)",[2040,6662,6663,6666],{},[2055,6664,6665],{},"URL 길이",[2055,6667,6668],{},"≤ 2048자",[2040,6670,6671,6674],{},[2055,6672,6673],{},"네트워크",[2055,6675,6676],{},"내부/사설 IP 불가 (localhost, 10.x.x.x, 192.168.x.x)",[2040,6678,6679,6682],{},[2055,6680,6681],{},"본문",[2055,6683,6684],{},"전체 task 객체를 담은 JSON POST",[91,6686,6688],{"id":6687},"프로덕션-flask-webhook-수신기","프로덕션 Flask Webhook 수신기",[10,6690,6691],{},"적절한 검증, 오류 처리, 비동기 비디오 다운로드를 갖춘 완전한 webhook 서버:",[136,6693,6695],{"className":337,"code":6694,"language":339,"meta":141,"style":141},"# 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,6696,6697,6702,6707,6712,6717,6722,6726,6739,6745,6751,6758,6773,6777,6792,6796,6801,6820,6838,6842,6846,6856,6861,6868,6897,6927,6954,6959,6976,6995,7000,7024,7057,7070,7099,7103,7107,7132,7141,7145,7150,7154,7159,7164,7168,7173,7182,7191,7212,7216,7234,7251,7268,7272,7298,7319,7340,7361,7365,7377,7382,7395,7402,7416,7437,7442,7448,7459,7470,7481,7486,7492,7500,7514,7519,7533,7547,7579,7591,7601,7606,7613,7635,7666,7671,7677,7702,7707,7712,7735,7745,7751,7769,7774,7779,7792,7806,7833,7847],{"__ignoreMap":141},[145,6698,6699],{"class":147,"line":148},[145,6700,6701],{"class":174},"# webhook_server.py\n",[145,6703,6704],{"class":147,"line":166},[145,6705,6706],{"class":159},"\"\"\"\n",[145,6708,6709],{"class":147,"line":178},[145,6710,6711],{"class":159},"Seedance webhook 수신기 — 비디오 생성 완료 콜백을 처리합니다.\n",[145,6713,6714],{"class":147,"line":187},[145,6715,6716],{"class":159},"실행: pip install flask requests\n",[145,6718,6719],{"class":147,"line":372},[145,6720,6721],{"class":159},"      python webhook_server.py\n",[145,6723,6724],{"class":147,"line":378},[145,6725,6706],{"class":159},[145,6727,6728,6731,6734,6736],{"class":147,"line":384},[145,6729,6730],{"class":258},"from",[145,6732,6733],{"class":262}," flask ",[145,6735,346],{"class":258},[145,6737,6738],{"class":262}," Flask, request, jsonify\n",[145,6740,6741,6743],{"class":147,"line":408},[145,6742,346],{"class":258},[145,6744,369],{"class":262},[145,6746,6747,6749],{"class":147,"line":419},[145,6748,346],{"class":258},[145,6750,362],{"class":262},[145,6752,6753,6755],{"class":147,"line":430},[145,6754,346],{"class":258},[145,6756,6757],{"class":262}," threading\n",[145,6759,6760,6762,6765,6767,6770],{"class":147,"line":454},[145,6761,346],{"class":258},[145,6763,6764],{"class":262}," requests ",[145,6766,1165],{"class":258},[145,6768,6769],{"class":262}," req  ",[145,6771,6772],{"class":174},"# flask.request와의 충돌을 피하기 위해 이름 변경\n",[145,6774,6775],{"class":147,"line":465},[145,6776,375],{"emptyLinePlaceholder":57},[145,6778,6779,6782,6784,6787,6790],{"class":147,"line":599},[145,6780,6781],{"class":262},"app ",[145,6783,266],{"class":258},[145,6785,6786],{"class":262}," Flask(",[145,6788,6789],{"class":155},"__name__",[145,6791,405],{"class":262},[145,6793,6794],{"class":147,"line":604},[145,6795,375],{"emptyLinePlaceholder":57},[145,6797,6798],{"class":147,"line":610},[145,6799,6800],{"class":174},"# 완료된 비디오를 저장할 디렉토리\n",[145,6802,6803,6806,6808,6810,6813,6815,6818],{"class":147,"line":616},[145,6804,6805],{"class":155},"OUTPUT_DIR",[145,6807,390],{"class":258},[145,6809,393],{"class":262},[145,6811,6812],{"class":159},"\"VIDEO_OUTPUT_DIR\"",[145,6814,399],{"class":262},[145,6816,6817],{"class":159},"\"./videos\"",[145,6819,405],{"class":262},[145,6821,6822,6825,6827,6829,6832,6834,6836],{"class":147,"line":622},[145,6823,6824],{"class":262},"os.makedirs(",[145,6826,6805],{"class":155},[145,6828,399],{"class":262},[145,6830,6831],{"class":700},"exist_ok",[145,6833,266],{"class":258},[145,6835,1138],{"class":155},[145,6837,405],{"class":262},[145,6839,6840],{"class":147,"line":627},[145,6841,375],{"emptyLinePlaceholder":57},[145,6843,6844],{"class":147,"line":638},[145,6845,375],{"emptyLinePlaceholder":57},[145,6847,6848,6850,6853],{"class":147,"line":653},[145,6849,525],{"class":258},[145,6851,6852],{"class":151}," download_video_async",[145,6854,6855],{"class":262},"(video_url, task_id):\n",[145,6857,6858],{"class":147,"line":659},[145,6859,6860],{"class":159},"    \"\"\"백그라운드 스레드에서 비디오를 다운로드하여 webhook 응답을 차단하지 않습니다.\"\"\"\n",[145,6862,6863,6866],{"class":147,"line":670},[145,6864,6865],{"class":258},"    try",[145,6867,859],{"class":262},[145,6869,6870,6873,6875,6878,6880,6882,6884,6886,6888,6890,6892,6895],{"class":147,"line":697},[145,6871,6872],{"class":262},"        filename ",[145,6874,266],{"class":258},[145,6876,6877],{"class":262}," os.path.join(",[145,6879,6805],{"class":155},[145,6881,399],{"class":262},[145,6883,439],{"class":258},[145,6885,448],{"class":159},[145,6887,684],{"class":155},[145,6889,687],{"class":262},[145,6891,690],{"class":155},[145,6893,6894],{"class":159},".mp4\"",[145,6896,405],{"class":262},[145,6898,6899,6901,6903,6905,6908,6910,6912,6914,6917,6919,6921,6923,6925],{"class":147,"line":709},[145,6900,790],{"class":155},[145,6902,793],{"class":262},[145,6904,439],{"class":258},[145,6906,6907],{"class":159},"\"  Downloading ",[145,6909,684],{"class":155},[145,6911,687],{"class":262},[145,6913,690],{"class":155},[145,6915,6916],{"class":159}," to ",[145,6918,684],{"class":155},[145,6920,1113],{"class":262},[145,6922,690],{"class":155},[145,6924,1118],{"class":159},[145,6926,405],{"class":262},[145,6928,6929,6932,6934,6937,6939,6941,6943,6945,6947,6949,6952],{"class":147,"line":715},[145,6930,6931],{"class":262},"        resp ",[145,6933,266],{"class":258},[145,6935,6936],{"class":262}," req.get(video_url, ",[145,6938,1133],{"class":700},[145,6940,266],{"class":258},[145,6942,1138],{"class":155},[145,6944,399],{"class":262},[145,6946,1018],{"class":700},[145,6948,266],{"class":258},[145,6950,6951],{"class":155},"120",[145,6953,405],{"class":262},[145,6955,6956],{"class":147,"line":721},[145,6957,6958],{"class":262},"        resp.raise_for_status()\n",[145,6960,6961,6964,6966,6968,6970,6972,6974],{"class":147,"line":727},[145,6962,6963],{"class":258},"        with",[145,6965,1153],{"class":155},[145,6967,1156],{"class":262},[145,6969,1159],{"class":159},[145,6971,1162],{"class":262},[145,6973,1165],{"class":258},[145,6975,1168],{"class":262},[145,6977,6978,6981,6983,6985,6987,6989,6991,6993],{"class":147,"line":738},[145,6979,6980],{"class":258},"            for",[145,6982,1176],{"class":262},[145,6984,1179],{"class":258},[145,6986,1182],{"class":262},[145,6988,1185],{"class":700},[145,6990,266],{"class":258},[145,6992,1190],{"class":155},[145,6994,547],{"class":262},[145,6996,6997],{"class":147,"line":743},[145,6998,6999],{"class":262},"                f.write(chunk)\n",[145,7001,7002,7005,7007,7010,7012,7014,7017,7020,7022],{"class":147,"line":749},[145,7003,7004],{"class":262},"        size_mb ",[145,7006,266],{"class":258},[145,7008,7009],{"class":262}," os.path.getsize(filename) ",[145,7011,1224],{"class":258},[145,7013,103],{"class":262},[145,7015,7016],{"class":155},"1024",[145,7018,7019],{"class":258}," *",[145,7021,1227],{"class":155},[145,7023,405],{"class":262},[145,7025,7026,7028,7030,7032,7035,7037,7039,7041,7043,7045,7048,7050,7052,7055],{"class":147,"line":766},[145,7027,790],{"class":155},[145,7029,793],{"class":262},[145,7031,439],{"class":258},[145,7033,7034],{"class":159},"\"  Saved: ",[145,7036,684],{"class":155},[145,7038,1113],{"class":262},[145,7040,690],{"class":155},[145,7042,103],{"class":159},[145,7044,684],{"class":155},[145,7046,7047],{"class":262},"size_mb",[145,7049,5491],{"class":258},[145,7051,690],{"class":155},[145,7053,7054],{"class":159}," MB)\"",[145,7056,405],{"class":262},[145,7058,7059,7062,7065,7068],{"class":147,"line":787},[145,7060,7061],{"class":258},"    except",[145,7063,7064],{"class":155}," Exception",[145,7066,7067],{"class":258}," as",[145,7069,5654],{"class":262},[145,7071,7072,7074,7076,7078,7081,7083,7085,7087,7089,7091,7093,7095,7097],{"class":147,"line":833},[145,7073,790],{"class":155},[145,7075,793],{"class":262},[145,7077,439],{"class":258},[145,7079,7080],{"class":159},"\"  Download failed for ",[145,7082,684],{"class":155},[145,7084,687],{"class":262},[145,7086,690],{"class":155},[145,7088,436],{"class":159},[145,7090,684],{"class":155},[145,7092,5704],{"class":262},[145,7094,690],{"class":155},[145,7096,448],{"class":159},[145,7098,405],{"class":262},[145,7100,7101],{"class":147,"line":838},[145,7102,375],{"emptyLinePlaceholder":57},[145,7104,7105],{"class":147,"line":844},[145,7106,375],{"emptyLinePlaceholder":57},[145,7108,7109,7112,7114,7117,7119,7122,7124,7127,7130],{"class":147,"line":862},[145,7110,7111],{"class":151},"@app.route",[145,7113,793],{"class":262},[145,7115,7116],{"class":159},"\"/api/webhook/seedance\"",[145,7118,399],{"class":262},[145,7120,7121],{"class":700},"methods",[145,7123,266],{"class":258},[145,7125,7126],{"class":262},"[",[145,7128,7129],{"class":159},"\"POST\"",[145,7131,1628],{"class":262},[145,7133,7134,7136,7139],{"class":147,"line":871},[145,7135,525],{"class":258},[145,7137,7138],{"class":151}," handle_webhook",[145,7140,1288],{"class":262},[145,7142,7143],{"class":147,"line":886},[145,7144,552],{"class":159},[145,7146,7147],{"class":147,"line":902},[145,7148,7149],{"class":159},"    Seedance 비디오 생성 완료 webhook을 처리합니다.\n",[145,7151,7152],{"class":147,"line":914},[145,7153,562],{"class":159},[145,7155,7156],{"class":147,"line":952},[145,7157,7158],{"class":159},"    비디오 생성이 완료(성공 또는 실패)되면\n",[145,7160,7161],{"class":147,"line":958},[145,7162,7163],{"class":159},"    API가 전체 task 객체를 담은 POST를 보냅니다.\n",[145,7165,7166],{"class":147,"line":963},[145,7167,552],{"class":159},[145,7169,7170],{"class":147,"line":969},[145,7171,7172],{"class":174},"    # 수신된 task 객체 파싱\n",[145,7174,7175,7177,7179],{"class":147,"line":975},[145,7176,1495],{"class":262},[145,7178,266],{"class":258},[145,7180,7181],{"class":262}," request.json\n",[145,7183,7184,7186,7188],{"class":147,"line":987},[145,7185,2507],{"class":258},[145,7187,5894],{"class":258},[145,7189,7190],{"class":262}," task:\n",[145,7192,7193,7196,7199,7201,7203,7206,7209],{"class":147,"line":992},[145,7194,7195],{"class":258},"        return",[145,7197,7198],{"class":262}," jsonify({",[145,7200,896],{"class":159},[145,7202,436],{"class":262},[145,7204,7205],{"class":159},"\"Empty body\"",[145,7207,7208],{"class":262},"}), ",[145,7210,7211],{"class":155},"400\n",[145,7213,7214],{"class":147,"line":5533},[145,7215,562],{"class":262},[145,7217,7218,7221,7223,7225,7227,7229,7232],{"class":147,"line":5542},[145,7219,7220],{"class":262},"    task_id ",[145,7222,266],{"class":258},[145,7224,774],{"class":262},[145,7226,1625],{"class":159},[145,7228,399],{"class":262},[145,7230,7231],{"class":159},"\"unknown\"",[145,7233,405],{"class":262},[145,7235,7236,7239,7241,7243,7245,7247,7249],{"class":147,"line":5548},[145,7237,7238],{"class":262},"    status ",[145,7240,266],{"class":258},[145,7242,774],{"class":262},[145,7244,760],{"class":159},[145,7246,399],{"class":262},[145,7248,7231],{"class":159},[145,7250,405],{"class":262},[145,7252,7253,7256,7258,7260,7262,7264,7266],{"class":147,"line":5576},[145,7254,7255],{"class":262},"    model ",[145,7257,266],{"class":258},[145,7259,774],{"class":262},[145,7261,5900],{"class":159},[145,7263,399],{"class":262},[145,7265,7231],{"class":159},[145,7267,405],{"class":262},[145,7269,7270],{"class":147,"line":5607},[145,7271,375],{"emptyLinePlaceholder":57},[145,7273,7274,7276,7278,7280,7282,7285,7288,7291,7294,7296],{"class":147,"line":5627},[145,7275,1101],{"class":155},[145,7277,793],{"class":262},[145,7279,439],{"class":258},[145,7281,448],{"class":159},[145,7283,7284],{"class":155},"\\n{",[145,7286,7287],{"class":159},"'='",[145,7289,7290],{"class":258},"*",[145,7292,7293],{"class":155},"50}",[145,7295,448],{"class":159},[145,7297,405],{"class":262},[145,7299,7300,7302,7304,7306,7309,7311,7313,7315,7317],{"class":147,"line":5633},[145,7301,1101],{"class":155},[145,7303,793],{"class":262},[145,7305,439],{"class":258},[145,7307,7308],{"class":159},"\"Webhook received: task=",[145,7310,684],{"class":155},[145,7312,687],{"class":262},[145,7314,690],{"class":155},[145,7316,448],{"class":159},[145,7318,405],{"class":262},[145,7320,7321,7323,7325,7327,7330,7332,7334,7336,7338],{"class":147,"line":5639},[145,7322,1101],{"class":155},[145,7324,793],{"class":262},[145,7326,439],{"class":258},[145,7328,7329],{"class":159},"\"  Status: ",[145,7331,684],{"class":155},[145,7333,813],{"class":262},[145,7335,690],{"class":155},[145,7337,448],{"class":159},[145,7339,405],{"class":262},[145,7341,7342,7344,7346,7348,7351,7353,7355,7357,7359],{"class":147,"line":5644},[145,7343,1101],{"class":155},[145,7345,793],{"class":262},[145,7347,439],{"class":258},[145,7349,7350],{"class":159},"\"  Model: ",[145,7352,684],{"class":155},[145,7354,1746],{"class":262},[145,7356,690],{"class":155},[145,7358,448],{"class":159},[145,7360,405],{"class":262},[145,7362,7363],{"class":147,"line":5657},[145,7364,375],{"emptyLinePlaceholder":57},[145,7366,7367,7369,7371,7373,7375],{"class":147,"line":5663},[145,7368,2507],{"class":258},[145,7370,850],{"class":262},[145,7372,853],{"class":258},[145,7374,856],{"class":159},[145,7376,859],{"class":262},[145,7378,7379],{"class":147,"line":5690},[145,7380,7381],{"class":174},"        # results에서 비디오 URL 추출\n",[145,7383,7384,7387,7389,7391,7393],{"class":147,"line":5730},[145,7385,7386],{"class":262},"        results ",[145,7388,266],{"class":258},[145,7390,774],{"class":262},[145,7392,1650],{"class":159},[145,7394,6243],{"class":262},[145,7396,7397,7399],{"class":147,"line":5749},[145,7398,847],{"class":258},[145,7400,7401],{"class":262}," results:\n",[145,7403,7404,7407,7409,7412,7414],{"class":147,"line":5754},[145,7405,7406],{"class":262},"            video_url ",[145,7408,266],{"class":258},[145,7410,7411],{"class":262}," results[",[145,7413,782],{"class":155},[145,7415,763],{"class":262},[145,7417,7418,7420,7422,7424,7427,7429,7431,7433,7435],{"class":147,"line":5759},[145,7419,5579],{"class":155},[145,7421,793],{"class":262},[145,7423,439],{"class":258},[145,7425,7426],{"class":159},"\"  Video URL: ",[145,7428,684],{"class":155},[145,7430,1677],{"class":262},[145,7432,690],{"class":155},[145,7434,448],{"class":159},[145,7436,405],{"class":262},[145,7438,7439],{"class":147,"line":5764},[145,7440,7441],{"class":262},"            \n",[145,7443,7445],{"class":147,"line":7444},65,[145,7446,7447],{"class":174},"            # 백그라운드 스레드에서 다운로드하여 빠르게 응답\n",[145,7449,7451,7454,7456],{"class":147,"line":7450},66,[145,7452,7453],{"class":262},"            thread ",[145,7455,266],{"class":258},[145,7457,7458],{"class":262}," threading.Thread(\n",[145,7460,7462,7465,7467],{"class":147,"line":7461},67,[145,7463,7464],{"class":700},"                target",[145,7466,266],{"class":258},[145,7468,7469],{"class":262},"download_video_async,\n",[145,7471,7473,7476,7478],{"class":147,"line":7472},68,[145,7474,7475],{"class":700},"                args",[145,7477,266],{"class":258},[145,7479,7480],{"class":262},"(video_url, task_id)\n",[145,7482,7484],{"class":147,"line":7483},69,[145,7485,955],{"class":262},[145,7487,7489],{"class":147,"line":7488},70,[145,7490,7491],{"class":262},"            thread.start()\n",[145,7493,7495,7498],{"class":147,"line":7494},71,[145,7496,7497],{"class":258},"        else",[145,7499,859],{"class":262},[145,7501,7503,7505,7507,7509,7512],{"class":147,"line":7502},72,[145,7504,5579],{"class":155},[145,7506,793],{"class":262},[145,7508,439],{"class":258},[145,7510,7511],{"class":159},"\"  WARNING: Completed but no results array!\"",[145,7513,405],{"class":262},[145,7515,7517],{"class":147,"line":7516},73,[145,7518,375],{"emptyLinePlaceholder":57},[145,7520,7522,7525,7527,7529,7531],{"class":147,"line":7521},74,[145,7523,7524],{"class":258},"    elif",[145,7526,850],{"class":262},[145,7528,853],{"class":258},[145,7530,881],{"class":159},[145,7532,859],{"class":262},[145,7534,7536,7539,7541,7543,7545],{"class":147,"line":7535},75,[145,7537,7538],{"class":262},"        error_info ",[145,7540,266],{"class":258},[145,7542,774],{"class":262},[145,7544,896],{"class":159},[145,7546,899],{"class":262},[145,7548,7550,7552,7554,7556,7559,7561,7564,7567,7569,7571,7573,7575,7577],{"class":147,"line":7549},76,[145,7551,790],{"class":155},[145,7553,793],{"class":262},[145,7555,439],{"class":258},[145,7557,7558],{"class":159},"\"  FAILED: ",[145,7560,684],{"class":155},[145,7562,7563],{"class":262},"json.dumps(error_info, ",[145,7565,7566],{"class":700},"indent",[145,7568,266],{"class":258},[145,7570,5420],{"class":155},[145,7572,117],{"class":262},[145,7574,690],{"class":155},[145,7576,448],{"class":159},[145,7578,405],{"class":262},[145,7580,7582,7585,7588],{"class":147,"line":7581},77,[145,7583,7584],{"class":174},"        # ",[145,7586,7587],{"class":258},"TODO",[145,7589,7590],{"class":174},": 오류 추적 시스템에 기록 (Sentry 등)\n",[145,7592,7594,7596,7598],{"class":147,"line":7593},78,[145,7595,7584],{"class":174},[145,7597,7587],{"class":258},[145,7599,7600],{"class":174},": 선택적으로 수정된 파라미터로 재생성 시도\n",[145,7602,7604],{"class":147,"line":7603},79,[145,7605,375],{"emptyLinePlaceholder":57},[145,7607,7609,7611],{"class":147,"line":7608},80,[145,7610,2543],{"class":258},[145,7612,859],{"class":262},[145,7614,7616,7618,7620,7622,7625,7627,7629,7631,7633],{"class":147,"line":7615},81,[145,7617,790],{"class":155},[145,7619,793],{"class":262},[145,7621,439],{"class":258},[145,7623,7624],{"class":159},"\"  Unexpected status: ",[145,7626,684],{"class":155},[145,7628,813],{"class":262},[145,7630,690],{"class":155},[145,7632,448],{"class":159},[145,7634,405],{"class":262},[145,7636,7638,7640,7642,7644,7647,7649,7652,7654,7656,7658,7660,7662,7664],{"class":147,"line":7637},82,[145,7639,790],{"class":155},[145,7641,793],{"class":262},[145,7643,439],{"class":258},[145,7645,7646],{"class":159},"\"  Full payload: ",[145,7648,684],{"class":155},[145,7650,7651],{"class":262},"json.dumps(task, ",[145,7653,7566],{"class":700},[145,7655,266],{"class":258},[145,7657,5420],{"class":155},[145,7659,117],{"class":262},[145,7661,690],{"class":155},[145,7663,448],{"class":159},[145,7665,405],{"class":262},[145,7667,7669],{"class":147,"line":7668},83,[145,7670,375],{"emptyLinePlaceholder":57},[145,7672,7674],{"class":147,"line":7673},84,[145,7675,7676],{"class":174},"    # 항상 빠르게 200 반환 — API는 10초 내 응답을 기대합니다\n",[145,7678,7680,7682,7684,7687,7689,7691,7693,7696,7699],{"class":147,"line":7679},85,[145,7681,1702],{"class":258},[145,7683,7198],{"class":262},[145,7685,7686],{"class":159},"\"received\"",[145,7688,436],{"class":262},[145,7690,1138],{"class":155},[145,7692,399],{"class":262},[145,7694,7695],{"class":159},"\"task_id\"",[145,7697,7698],{"class":262},": task_id}), ",[145,7700,7701],{"class":155},"200\n",[145,7703,7705],{"class":147,"line":7704},86,[145,7706,375],{"emptyLinePlaceholder":57},[145,7708,7710],{"class":147,"line":7709},87,[145,7711,375],{"emptyLinePlaceholder":57},[145,7713,7715,7717,7719,7722,7724,7726,7728,7730,7733],{"class":147,"line":7714},88,[145,7716,7111],{"class":151},[145,7718,793],{"class":262},[145,7720,7721],{"class":159},"\"/health\"",[145,7723,399],{"class":262},[145,7725,7121],{"class":700},[145,7727,266],{"class":258},[145,7729,7126],{"class":262},[145,7731,7732],{"class":159},"\"GET\"",[145,7734,1628],{"class":262},[145,7736,7738,7740,7743],{"class":147,"line":7737},89,[145,7739,525],{"class":258},[145,7741,7742],{"class":151}," health_check",[145,7744,1288],{"class":262},[145,7746,7748],{"class":147,"line":7747},90,[145,7749,7750],{"class":159},"    \"\"\"로드 밸런서용 헬스 체크 엔드포인트.\"\"\"\n",[145,7752,7754,7756,7758,7760,7762,7765,7767],{"class":147,"line":7753},91,[145,7755,1702],{"class":258},[145,7757,7198],{"class":262},[145,7759,760],{"class":159},[145,7761,436],{"class":262},[145,7763,7764],{"class":159},"\"ok\"",[145,7766,7208],{"class":262},[145,7768,7701],{"class":155},[145,7770,7772],{"class":147,"line":7771},92,[145,7773,375],{"emptyLinePlaceholder":57},[145,7775,7777],{"class":147,"line":7776},93,[145,7778,375],{"emptyLinePlaceholder":57},[145,7780,7782,7784,7786,7788,7790],{"class":147,"line":7781},94,[145,7783,1718],{"class":258},[145,7785,1721],{"class":155},[145,7787,1724],{"class":258},[145,7789,1727],{"class":159},[145,7791,859],{"class":262},[145,7793,7795,7797,7799,7801,7804],{"class":147,"line":7794},95,[145,7796,1101],{"class":155},[145,7798,793],{"class":262},[145,7800,439],{"class":258},[145,7802,7803],{"class":159},"\"Starting webhook server...\"",[145,7805,405],{"class":262},[145,7807,7809,7811,7813,7815,7818,7820,7823,7825,7827,7829,7831],{"class":147,"line":7808},96,[145,7810,1101],{"class":155},[145,7812,793],{"class":262},[145,7814,439],{"class":258},[145,7816,7817],{"class":159},"\"Videos will be saved to: ",[145,7819,684],{"class":155},[145,7821,7822],{"class":262},"os.path.abspath(",[145,7824,6805],{"class":155},[145,7826,117],{"class":262},[145,7828,690],{"class":155},[145,7830,448],{"class":159},[145,7832,405],{"class":262},[145,7834,7836,7838,7840,7842,7845],{"class":147,"line":7835},97,[145,7837,1101],{"class":155},[145,7839,793],{"class":262},[145,7841,439],{"class":258},[145,7843,7844],{"class":159},"\"Webhook URL: http://localhost:5000/api/webhook/seedance\"",[145,7846,405],{"class":262},[145,7848,7850,7853,7856,7858,7861,7863,7866,7868,7871,7873,7876,7878,7880],{"class":147,"line":7849},98,[145,7851,7852],{"class":262},"    app.run(",[145,7854,7855],{"class":700},"host",[145,7857,266],{"class":258},[145,7859,7860],{"class":159},"\"0.0.0.0\"",[145,7862,399],{"class":262},[145,7864,7865],{"class":700},"port",[145,7867,266],{"class":258},[145,7869,7870],{"class":155},"5000",[145,7872,399],{"class":262},[145,7874,7875],{"class":700},"debug",[145,7877,266],{"class":258},[145,7879,1138],{"class":155},[145,7881,405],{"class":262},[10,7883,7884],{},"의존성 설치 및 실행:",[136,7886,7888],{"className":138,"code":7887,"language":140,"meta":141,"style":141},"pip install flask requests\npython webhook_server.py\n",[27,7889,7890,7901],{"__ignoreMap":141},[145,7891,7892,7894,7896,7899],{"class":147,"line":148},[145,7893,190],{"class":151},[145,7895,193],{"class":159},[145,7897,7898],{"class":159}," flask",[145,7900,328],{"class":159},[145,7902,7903,7905],{"class":147,"line":166},[145,7904,339],{"class":151},[145,7906,7907],{"class":159}," webhook_server.py\n",[10,7909,7910],{},"이 서버의 주요 설계 결정:",[96,7912,7913,7923,7932],{},[72,7914,7915,7918,7919,7922],{},[22,7916,7917],{},"백그라운드 다운로드"," — 스레드를 생성하여 비디오를 다운로드하므로 webhook 핸들러가 즉시 ",[27,7920,7921],{},"200","을 반환합니다. API는 10초 내 응답을 기대하지만, 비디오 다운로드는 더 오래 걸릴 수 있습니다.",[72,7924,7925,1060,7928,7931],{},[22,7926,7927],{},"헬스 체크 엔드포인트",[27,7929,7930],{},"/health","는 로드 밸런서(ALB, nginx 등) 뒤에 배포할 때 유용합니다.",[72,7933,7934,7937],{},[22,7935,7936],{},"오류 로깅"," — 실패한 작업은 전체 오류 payload와 함께 출력됩니다. 프로덕션에서는 Sentry, Datadog 또는 로깅 스택으로 연결하세요.",[91,7939,7941],{"id":7940},"ngrok으로-로컬호스트-노출","ngrok으로 로컬호스트 노출",[10,7943,7944,7945,7950],{},"로컬 개발 시 ",[36,7946,7949],{"href":7947,"rel":7948},"https://ngrok.com",[40],"ngrok","을 사용하여 로컬 서버로 터널링하는 공개 HTTPS URL을 생성합니다:",[136,7952,7954],{"className":138,"code":7953,"language":140,"meta":141,"style":141},"# ngrok 설치 (macOS)\nbrew install ngrok\n\n# 또는 https://ngrok.com/download 에서 다운로드\n\n# 터널 시작\nngrok http 5000\n",[27,7955,7956,7961,7971,7975,7980,7984,7989],{"__ignoreMap":141},[145,7957,7958],{"class":147,"line":148},[145,7959,7960],{"class":174},"# ngrok 설치 (macOS)\n",[145,7962,7963,7966,7968],{"class":147,"line":166},[145,7964,7965],{"class":151},"brew",[145,7967,193],{"class":159},[145,7969,7970],{"class":159}," ngrok\n",[145,7972,7973],{"class":147,"line":178},[145,7974,375],{"emptyLinePlaceholder":57},[145,7976,7977],{"class":147,"line":187},[145,7978,7979],{"class":174},"# 또는 https://ngrok.com/download 에서 다운로드\n",[145,7981,7982],{"class":147,"line":372},[145,7983,375],{"emptyLinePlaceholder":57},[145,7985,7986],{"class":147,"line":378},[145,7987,7988],{"class":174},"# 터널 시작\n",[145,7990,7991,7993,7996],{"class":147,"line":384},[145,7992,7949],{"class":151},[145,7994,7995],{"class":159}," http",[145,7997,7998],{"class":155}," 5000\n",[10,8000,8001],{},"ngrok 출력 예시:",[136,8003,8006],{"className":8004,"code":8005,"language":2182},[2180],"Forwarding  https://a1b2c3d4.ngrok-free.app → http://localhost:5000\n",[27,8007,8005],{"__ignoreMap":141},[10,8009,8010,8011,8013],{},"이 HTTPS URL을 ",[27,8012,3602],{},"로 사용하세요:",[136,8015,8017],{"className":337,"code":8016,"language":339,"meta":141,"style":141},"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,8018,8019,8027,8037,8048,8057],{"__ignoreMap":141},[145,8020,8021,8023,8025],{"class":147,"line":148},[145,8022,3664],{"class":262},[145,8024,266],{"class":258},[145,8026,427],{"class":262},[145,8028,8029,8031,8033,8035],{"class":147,"line":166},[145,8030,3673],{"class":159},[145,8032,436],{"class":262},[145,8034,1307],{"class":159},[145,8036,451],{"class":262},[145,8038,8039,8041,8043,8046],{"class":147,"line":178},[145,8040,3684],{"class":159},[145,8042,436],{"class":262},[145,8044,8045],{"class":159},"\"Your prompt here\"",[145,8047,451],{"class":262},[145,8049,8050,8052,8054],{"class":147,"line":187},[145,8051,6513],{"class":159},[145,8053,436],{"class":262},[145,8055,8056],{"class":159},"\"https://a1b2c3d4.ngrok-free.app/api/webhook/seedance\"\n",[145,8058,8059],{"class":147,"line":372},[145,8060,468],{"class":262},[17,8062,8063],{},[10,8064,8065,8067,8068,8071,8072,8075],{},[22,8066,304],{}," ngrok의 ",[27,8069,8070],{},"https://"," URL 대신 ",[27,8073,8074],{},"http://"," URL을 사용하는 것. Seedance API는 webhook에 HTTPS를 요구합니다 — 일반 HTTP 콜백 URL은 400 오류를 반환합니다.",[91,8077,8079],{"id":8078},"webhook-보안","Webhook 보안",[10,8081,8082],{},"프로덕션에서는 webhook 요청이 실제로 EvoLink API에서 온 것인지 검증하세요:",[136,8084,8086],{"className":337,"code":8085,"language":339,"meta":141,"style":141},"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,8087,8088,8095,8102,8106,8116,8121,8129,8145,8149,8154,8168,8175,8179,8184,8211,8239,8245,8249],{"__ignoreMap":141},[145,8089,8090,8092],{"class":147,"line":148},[145,8091,346],{"class":258},[145,8093,8094],{"class":262}," hmac\n",[145,8096,8097,8099],{"class":147,"line":166},[145,8098,346],{"class":258},[145,8100,8101],{"class":262}," hashlib\n",[145,8103,8104],{"class":147,"line":178},[145,8105,375],{"emptyLinePlaceholder":57},[145,8107,8108,8110,8113],{"class":147,"line":187},[145,8109,525],{"class":258},[145,8111,8112],{"class":151}," verify_webhook",[145,8114,8115],{"class":262},"(request):\n",[145,8117,8118],{"class":147,"line":372},[145,8119,8120],{"class":159},"    \"\"\"작업 ID 패턴으로 webhook 진위를 검증합니다.\"\"\"\n",[145,8122,8123,8125,8127],{"class":147,"line":378},[145,8124,1495],{"class":262},[145,8126,266],{"class":258},[145,8128,7181],{"class":262},[145,8130,8131,8133,8135,8137,8139,8141,8143],{"class":147,"line":384},[145,8132,7220],{"class":262},[145,8134,266],{"class":258},[145,8136,774],{"class":262},[145,8138,1625],{"class":159},[145,8140,399],{"class":262},[145,8142,5268],{"class":159},[145,8144,405],{"class":262},[145,8146,8147],{"class":147,"line":408},[145,8148,562],{"class":262},[145,8150,8151],{"class":147,"line":419},[145,8152,8153],{"class":174},"    # EvoLink 작업 ID는 특정 형식을 따릅니다\n",[145,8155,8156,8158,8160,8163,8166],{"class":147,"line":430},[145,8157,2507],{"class":258},[145,8159,5894],{"class":258},[145,8161,8162],{"class":262}," task_id.startswith(",[145,8164,8165],{"class":159},"\"task-unified-\"",[145,8167,547],{"class":262},[145,8169,8170,8172],{"class":147,"line":454},[145,8171,7195],{"class":258},[145,8173,8174],{"class":155}," False\n",[145,8176,8177],{"class":147,"line":465},[145,8178,562],{"class":262},[145,8180,8181],{"class":147,"line":599},[145,8182,8183],{"class":174},"    # 추가 검증: 필수 필드 존재 확인\n",[145,8185,8186,8189,8191,8194,8196,8198,8200,8202,8204,8206,8209],{"class":147,"line":604},[145,8187,8188],{"class":262},"    required_fields ",[145,8190,266],{"class":258},[145,8192,8193],{"class":262}," [",[145,8195,1625],{"class":159},[145,8197,399],{"class":262},[145,8199,760],{"class":159},[145,8201,399],{"class":262},[145,8203,5900],{"class":159},[145,8205,399],{"class":262},[145,8207,8208],{"class":159},"\"created\"",[145,8210,763],{"class":262},[145,8212,8213,8215,8217,8220,8223,8225,8228,8231,8234,8236],{"class":147,"line":610},[145,8214,2507],{"class":258},[145,8216,5894],{"class":258},[145,8218,8219],{"class":155}," all",[145,8221,8222],{"class":262},"(field ",[145,8224,1179],{"class":258},[145,8226,8227],{"class":262}," task ",[145,8229,8230],{"class":258},"for",[145,8232,8233],{"class":262}," field ",[145,8235,1179],{"class":258},[145,8237,8238],{"class":262}," required_fields):\n",[145,8240,8241,8243],{"class":147,"line":616},[145,8242,7195],{"class":258},[145,8244,8174],{"class":155},[145,8246,8247],{"class":147,"line":622},[145,8248,562],{"class":262},[145,8250,8251,8253],{"class":147,"line":627},[145,8252,1702],{"class":258},[145,8254,6415],{"class":155},[91,8256,8258],{"id":8257},"webhook-vs-polling-언제-무엇을-선택할까","Webhook vs Polling: 언제 무엇을 선택할까?",[2034,8260,8261,8274],{},[2037,8262,8263],{},[2040,8264,8265,8268,8271],{},[2043,8266,8267],{},"시나리오",[2043,8269,8270],{},"권장 방식",[2043,8272,8273],{},"이유",[2050,8275,8276,8286,8297,8308,8318,8328],{},[2040,8277,8278,8281,8283],{},[2055,8279,8280],{},"빠른 프로토타이핑 / 스크립트",[2055,8282,2239],{},[2055,8284,8285],{},"더 간단, 서버 불필요",[2040,8287,8288,8291,8294],{},[2055,8289,8290],{},"프로덕션 웹 앱",[2055,8292,8293],{},"Webhook",[2055,8295,8296],{},"확장 가능, 요청 낭비 없음",[2040,8298,8299,8302,8305],{},[2055,8300,8301],{},"배치 처리 (100+ 비디오)",[2055,8303,8304],{},"Webhook + 메시지 큐",[2055,8306,8307],{},"모두 제출, 완료 순서대로 처리",[2040,8309,8310,8313,8315],{},[2055,8311,8312],{},"CLI 도구",[2055,8314,2239],{},[2055,8316,8317],{},"서버 인프라 불필요",[2040,8319,8320,8323,8325],{},[2055,8321,8322],{},"모바일 앱 백엔드",[2055,8324,8293],{},[2055,8326,8327],{},"완료 시 사용자에게 푸시 알림",[2040,8329,8330,8333,8335],{},[2055,8331,8332],{},"Serverless (Lambda/Cloud Functions)",[2055,8334,8293],{},[2055,8336,8337],{},"완벽한 매칭 — 완료마다 함수 트리거",[17,8339,8340],{},[10,8341,8342,8344],{},[22,8343,133],{}," 배치 처리에는 webhook과 메시지 큐(Redis, RabbitMQ, SQS)를 결합하세요. 모든 생성 요청을 제출한 후 큐에 도착하는 순서대로 처리합니다. 이 방식은 제출과 처리를 분리하고 재시도를 깔끔하게 처리합니다.",[44,8346],{},[47,8348,8350],{"id":8349},"배치-처리-여러-비디오-생성","배치 처리: 여러 비디오 생성",[10,8352,8353],{},"실제 사용 사례에서는 많은 비디오를 생성해야 하는 경우가 많습니다. 속도 제한을 고려한 배치 처리 패턴:",[10,8355,8356],{},[2620,8357,8358],{},"위 첫 번째 예제와 동일한 설정 및 헬퍼 함수를 사용합니다.",[136,8360,8362],{"className":337,"code":8361,"language":339,"meta":141,"style":141},"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,8363,8364,8371,8375,8391,8395,8400,8404,8408,8413,8418,8422,8426,8431,8435,8444,8448,8459,8464,8473,8484,8492,8503,8513,8518,8524,8534,8569,8582,8598,8618,8625,8635,8664,8677,8681,8686,8706,8715,8720,8734,8738,8750,8755,8759,8764,8802,8838,8845,8849,8853,8858,8868,8875,8882,8889,8893],{"__ignoreMap":141},[145,8365,8366,8368],{"class":147,"line":148},[145,8367,346],{"class":258},[145,8369,8370],{"class":262}," concurrent.futures\n",[145,8372,8373],{"class":147,"line":166},[145,8374,375],{"emptyLinePlaceholder":57},[145,8376,8377,8379,8382,8385,8387,8389],{"class":147,"line":178},[145,8378,525],{"class":258},[145,8380,8381],{"class":151}," batch_generate",[145,8383,8384],{"class":262},"(prompts, max_concurrent",[145,8386,266],{"class":258},[145,8388,5058],{"class":155},[145,8390,547],{"class":262},[145,8392,8393],{"class":147,"line":187},[145,8394,552],{"class":159},[145,8396,8397],{"class":147,"line":372},[145,8398,8399],{"class":159},"    제어된 동시성으로 여러 비디오를 생성합니다.\n",[145,8401,8402],{"class":147,"line":378},[145,8403,562],{"class":159},[145,8405,8406],{"class":147,"line":384},[145,8407,567],{"class":159},[145,8409,8410],{"class":147,"line":408},[145,8411,8412],{"class":159},"        prompts: 프롬프트 문자열 리스트.\n",[145,8414,8415],{"class":147,"line":419},[145,8416,8417],{"class":159},"        max_concurrent: 최대 동시 생성 수.\n",[145,8419,8420],{"class":147,"line":430},[145,8421,562],{"class":159},[145,8423,8424],{"class":147,"line":454},[145,8425,591],{"class":159},[145,8427,8428],{"class":147,"line":465},[145,8429,8430],{"class":159},"        (prompt, result_or_error) 튜플 리스트.\n",[145,8432,8433],{"class":147,"line":599},[145,8434,552],{"class":159},[145,8436,8437,8440,8442],{"class":147,"line":604},[145,8438,8439],{"class":262},"    results ",[145,8441,266],{"class":258},[145,8443,5878],{"class":262},[145,8445,8446],{"class":147,"line":610},[145,8447,562],{"class":262},[145,8449,8450,8453,8456],{"class":147,"line":616},[145,8451,8452],{"class":258},"    def",[145,8454,8455],{"class":151}," generate_one",[145,8457,8458],{"class":262},"(prompt, index):\n",[145,8460,8461],{"class":147,"line":622},[145,8462,8463],{"class":159},"        \"\"\"단일 비디오를 생성하고 결과를 반환합니다.\"\"\"\n",[145,8465,8466,8469,8471],{"class":147,"line":627},[145,8467,8468],{"class":262},"        payload ",[145,8470,266],{"class":258},[145,8472,427],{"class":262},[145,8474,8475,8478,8480,8482],{"class":147,"line":638},[145,8476,8477],{"class":159},"            \"model\"",[145,8479,436],{"class":262},[145,8481,1307],{"class":159},[145,8483,451],{"class":262},[145,8485,8486,8489],{"class":147,"line":653},[145,8487,8488],{"class":159},"            \"prompt\"",[145,8490,8491],{"class":262},": prompt,\n",[145,8493,8494,8497,8499,8501],{"class":147,"line":659},[145,8495,8496],{"class":159},"            \"duration\"",[145,8498,436],{"class":262},[145,8500,1351],{"class":155},[145,8502,451],{"class":262},[145,8504,8505,8508,8510],{"class":147,"line":670},[145,8506,8507],{"class":159},"            \"quality\"",[145,8509,436],{"class":262},[145,8511,8512],{"class":159},"\"720p\"\n",[145,8514,8515],{"class":147,"line":697},[145,8516,8517],{"class":262},"        }\n",[145,8519,8520,8522],{"class":147,"line":709},[145,8521,5137],{"class":258},[145,8523,859],{"class":262},[145,8525,8526,8529,8531],{"class":147,"line":715},[145,8527,8528],{"class":262},"            task ",[145,8530,266],{"class":258},[145,8532,8533],{"class":262}," generate_video_with_retry(payload)\n",[145,8535,8536,8538,8540,8542,8545,8547,8550,8552,8555,8557,8559,8561,8563,8565,8567],{"class":147,"line":721},[145,8537,5579],{"class":155},[145,8539,793],{"class":262},[145,8541,439],{"class":258},[145,8543,8544],{"class":159},"\"[",[145,8546,684],{"class":155},[145,8548,8549],{"class":262},"index",[145,8551,690],{"class":155},[145,8553,8554],{"class":159},"] Submitted: ",[145,8556,684],{"class":155},[145,8558,1528],{"class":262},[145,8560,1531],{"class":159},[145,8562,1534],{"class":262},[145,8564,690],{"class":155},[145,8566,448],{"class":159},[145,8568,405],{"class":262},[145,8570,8571,8574,8576,8578,8580],{"class":147,"line":727},[145,8572,8573],{"class":262},"            result ",[145,8575,266],{"class":258},[145,8577,1622],{"class":262},[145,8579,1625],{"class":159},[145,8581,1628],{"class":262},[145,8583,8584,8586,8588,8590,8592,8594,8596],{"class":147,"line":738},[145,8585,7406],{"class":262},[145,8587,266],{"class":258},[145,8589,1647],{"class":262},[145,8591,1650],{"class":159},[145,8593,1561],{"class":262},[145,8595,782],{"class":155},[145,8597,763],{"class":262},[145,8599,8600,8603,8605,8608,8610,8612,8614,8616],{"class":147,"line":743},[145,8601,8602],{"class":262},"            download_video(video_url, ",[145,8604,439],{"class":258},[145,8606,8607],{"class":159},"\"batch_",[145,8609,684],{"class":155},[145,8611,8549],{"class":262},[145,8613,690],{"class":155},[145,8615,6894],{"class":159},[145,8617,405],{"class":262},[145,8619,8620,8622],{"class":147,"line":749},[145,8621,865],{"class":258},[145,8623,8624],{"class":262}," (prompt, result)\n",[145,8626,8627,8629,8631,8633],{"class":147,"line":766},[145,8628,5536],{"class":258},[145,8630,7064],{"class":155},[145,8632,7067],{"class":258},[145,8634,5654],{"class":262},[145,8636,8637,8639,8641,8643,8645,8647,8649,8651,8654,8656,8658,8660,8662],{"class":147,"line":787},[145,8638,5579],{"class":155},[145,8640,793],{"class":262},[145,8642,439],{"class":258},[145,8644,8544],{"class":159},[145,8646,684],{"class":155},[145,8648,8549],{"class":262},[145,8650,690],{"class":155},[145,8652,8653],{"class":159},"] Failed: ",[145,8655,684],{"class":155},[145,8657,5704],{"class":262},[145,8659,690],{"class":155},[145,8661,448],{"class":159},[145,8663,405],{"class":262},[145,8665,8666,8668,8671,8674],{"class":147,"line":833},[145,8667,865],{"class":258},[145,8669,8670],{"class":262}," (prompt, ",[145,8672,8673],{"class":155},"str",[145,8675,8676],{"class":262},"(e))\n",[145,8678,8679],{"class":147,"line":838},[145,8680,562],{"class":262},[145,8682,8683],{"class":147,"line":844},[145,8684,8685],{"class":174},"    # 속도 제한을 준수하며 배치 처리\n",[145,8687,8688,8690,8693,8696,8698,8701,8703],{"class":147,"line":862},[145,8689,1150],{"class":258},[145,8691,8692],{"class":262}," concurrent.futures.ThreadPoolExecutor(",[145,8694,8695],{"class":700},"max_workers",[145,8697,266],{"class":258},[145,8699,8700],{"class":262},"max_concurrent) ",[145,8702,1165],{"class":258},[145,8704,8705],{"class":262}," executor:\n",[145,8707,8708,8711,8713],{"class":147,"line":871},[145,8709,8710],{"class":262},"        futures ",[145,8712,266],{"class":258},[145,8714,427],{"class":262},[145,8716,8717],{"class":147,"line":886},[145,8718,8719],{"class":262},"            executor.submit(generate_one, prompt, i): i\n",[145,8721,8722,8724,8727,8729,8731],{"class":147,"line":902},[145,8723,6980],{"class":258},[145,8725,8726],{"class":262}," i, prompt ",[145,8728,1179],{"class":258},[145,8730,6293],{"class":155},[145,8732,8733],{"class":262},"(prompts)\n",[145,8735,8736],{"class":147,"line":914},[145,8737,8517],{"class":262},[145,8739,8740,8742,8745,8747],{"class":147,"line":952},[145,8741,1173],{"class":258},[145,8743,8744],{"class":262}," future ",[145,8746,1179],{"class":258},[145,8748,8749],{"class":262}," concurrent.futures.as_completed(futures):\n",[145,8751,8752],{"class":147,"line":958},[145,8753,8754],{"class":262},"            results.append(future.result())\n",[145,8756,8757],{"class":147,"line":963},[145,8758,562],{"class":262},[145,8760,8761],{"class":147,"line":969},[145,8762,8763],{"class":174},"    # 요약\n",[145,8765,8766,8769,8771,8774,8776,8778,8780,8783,8785,8788,8790,8793,8796,8799],{"class":147,"line":975},[145,8767,8768],{"class":262},"    succeeded ",[145,8770,266],{"class":258},[145,8772,8773],{"class":155}," sum",[145,8775,793],{"class":262},[145,8777,5439],{"class":155},[145,8779,6396],{"class":258},[145,8781,8782],{"class":262}," _, r ",[145,8784,1179],{"class":258},[145,8786,8787],{"class":262}," results ",[145,8789,1718],{"class":258},[145,8791,8792],{"class":155}," isinstance",[145,8794,8795],{"class":262},"(r, ",[145,8797,8798],{"class":155},"dict",[145,8800,8801],{"class":262},"))\n",[145,8803,8804,8806,8808,8810,8812,8814,8817,8819,8822,8824,8826,8828,8831,8833,8836],{"class":147,"line":987},[145,8805,1101],{"class":155},[145,8807,793],{"class":262},[145,8809,439],{"class":258},[145,8811,448],{"class":159},[145,8813,1669],{"class":155},[145,8815,8816],{"class":159},"Batch complete: ",[145,8818,684],{"class":155},[145,8820,8821],{"class":262},"succeeded",[145,8823,690],{"class":155},[145,8825,1224],{"class":159},[145,8827,6272],{"class":155},[145,8829,8830],{"class":262},"(prompts)",[145,8832,690],{"class":155},[145,8834,8835],{"class":159}," succeeded\"",[145,8837,405],{"class":262},[145,8839,8840,8842],{"class":147,"line":992},[145,8841,1702],{"class":258},[145,8843,8844],{"class":262}," results\n",[145,8846,8847],{"class":147,"line":5533},[145,8848,375],{"emptyLinePlaceholder":57},[145,8850,8851],{"class":147,"line":5542},[145,8852,375],{"emptyLinePlaceholder":57},[145,8854,8855],{"class":147,"line":5548},[145,8856,8857],{"class":174},"# 사용 예시\n",[145,8859,8860,8863,8865],{"class":147,"line":5576},[145,8861,8862],{"class":262},"prompts ",[145,8864,266],{"class":258},[145,8866,8867],{"class":262}," [\n",[145,8869,8870,8873],{"class":147,"line":5607},[145,8871,8872],{"class":159},"    \"A hummingbird hovering near a red flower. Macro lens, shallow depth of field.\"",[145,8874,451],{"class":262},[145,8876,8877,8880],{"class":147,"line":5627},[145,8878,8879],{"class":159},"    \"Ocean waves crashing on volcanic rocks at sunset. Slow motion.\"",[145,8881,451],{"class":262},[145,8883,8884,8887],{"class":147,"line":5633},[145,8885,8886],{"class":159},"    \"A street musician playing violin in the rain. Cinematic lighting.\"",[145,8888,451],{"class":262},[145,8890,8891],{"class":147,"line":5639},[145,8892,763],{"class":262},[145,8894,8895,8898,8901,8903,8905],{"class":147,"line":5644},[145,8896,8897],{"class":262},"batch_generate(prompts, ",[145,8899,8900],{"class":700},"max_concurrent",[145,8902,266],{"class":258},[145,8904,5420],{"class":155},[145,8906,405],{"class":262},[10,8908,8909],{},"배치 처리의 주요 고려사항:",[96,8911,8912,8920,8926],{},[72,8913,8914,8919],{},[22,8915,8916],{},[27,8917,8918],{},"max_concurrent=3"," — 너무 많은 요청을 동시에 제출하지 마세요. 2~3으로 시작하고 속도 제한에 따라 늘리세요.",[72,8921,8922,8925],{},[22,8923,8924],{},"ThreadPoolExecutor"," — I/O 바운드(API 응답 대기)이므로 프로세스가 아닌 스레드를 사용합니다.",[72,8927,8928,8931],{},[22,8929,8930],{},"오류 격리"," — 각 비디오 생성은 독립적입니다. 하나의 실패가 배치 전체를 중단시키지 않습니다.",[44,8933],{},[47,8935,8937],{"id":8936},"다음-단계","다음 단계",[10,8939,8940],{},"기본기를 모두 다뤘습니다 — 텍스트-투-비디오, 이미지-투-비디오, 비동기 polling, webhook, 오류 처리, 배치 처리. 더 깊이 들어갈 방향을 안내합니다:",[91,8942,8944],{"id":8943},"고급-기능-탐색","고급 기능 탐색",[96,8946,8947,8955,8962,8969,8977],{},[72,8948,8949,8954],{},[22,8950,8951],{},[36,8952,8953],{"href":2968},"@Tags 멀티모달 참조 가이드"," — @Image, @Video, @Audio 참조 시스템을 마스터하여 멀티모달 생성",[72,8956,8957,8961],{},[22,8958,8959],{},[36,8960,3860],{"href":3859}," — 히치콕 줌, 원테이크 트래킹 샷, 오비탈 카메라를 프로그래밍 방식으로 구현",[72,8963,8964,8968],{},[22,8965,8966],{},[36,8967,3384],{"href":3383}," — 첫/마지막 프레임 제어, 다중 이미지 합성, 이커머스 제품 비디오",[72,8970,8971,8976],{},[22,8972,8973],{},[36,8974,8975],{"href":2614},"이커머스 제품 비디오 가이드"," — 제품 사진을 대규모로 마케팅 비디오로 변환",[72,8978,8979,8983],{},[22,8980,8981],{},[36,8982,1766],{"href":1765}," — 샷 스크립트 형식, 타이밍 구문, 데모 비디오의 프롬프트",[91,8985,8987],{"id":8986},"참조-문서","참조 문서",[96,8989,8990,8996,9003],{},[72,8991,8992],{},[36,8993,8995],{"href":1256,"rel":8994},[40],"비디오 생성 API 레퍼런스",[72,8997,8998],{},[36,8999,9002],{"href":9000,"rel":9001},"https://seedance2api.app/docs/multimodal-reference",[40],"멀티모달 참조 스펙",[72,9004,9005],{},[36,9006,9009],{"href":9007,"rel":9008},"https://seedance2api.app/docs/sdks",[40],"Python & Node.js SDK",[91,9011,9013],{"id":9012},"직접-만들어-보세요","직접 만들어 보세요",[10,9015,9016],{},"배운 것을 조합해 보세요. 프로젝트 아이디어:",[96,9018,9019,9029,9035,9045],{},[72,9020,9021,9024,9025,9028],{},[22,9022,9023],{},"자동화된 제품 비디오 파이프라인"," — 제품 사진을 업로드하고 마케팅 비디오를 대량 생성 (",[36,9026,9027],{"href":2614},"이커머스 비디오 가이드"," 참조)",[72,9030,9031,9034],{},[22,9032,9033],{},"소셜 미디어 콘텐츠 엔진"," — 텍스트 브리프에서 세로형 숏폼 비디오를 생성하여 TikTok/Reels에 직접 게시",[72,9036,9037,9040,9041,9044],{},[22,9038,9039],{},"스토리보드-투-비디오 도구"," — 연속 이미지를 ",[36,9042,9043],{"href":3859},"카메라 움직임 제어","가 적용된 애니메이션 장면으로 변환",[72,9046,9047,9050],{},[22,9048,9049],{},"AI 비디오 편집 파이프라인"," — Seedance 2.0의 비디오 확장 기능을 활용하여 짧은 클립에서 더 긴 내러티브 생성",[10,9052,9053],{},[22,9054,9055,9056,9060],{},"시작할 준비가 되셨나요? ",[36,9057,9059],{"href":38,"rel":9058},[40],"무료 EvoLink API Key 발급받기"," — 오늘부터 비디오를 생성하세요.",[44,9062],{},[47,9064,9066],{"id":9065},"자주-묻는-질문","자주 묻는 질문",[91,9068,9070],{"id":9069},"seedance-20-비디오-생성은-얼마나-걸리나요","Seedance 2.0 비디오 생성은 얼마나 걸리나요?",[10,9072,9073,9074,9077,9078,9080],{},"일반적으로 길이와 품질 설정에 따라 30",[2428,9075,9076],{},"120초입니다. 5초 720p 비디오는 약 50초에 완료됩니다. 15초 1080p 비디오는 2","3분이 걸릴 수 있습니다. API는 각 작업에 ",[27,9079,2104],{}," 필드를 반환하므로 적절한 타임아웃을 설정할 수 있습니다. 피크 시간대에는 큐 대기 시간이 10~30초 추가될 수 있습니다.",[91,9082,9084],{"id":9083},"seedance-20-api는-어떤-이미지-형식을-지원하나요","Seedance 2.0 API는 어떤 이미지 형식을 지원하나요?",[10,9086,9087,9088,9090],{},"JPEG, PNG, WebP, BMP, TIFF, GIF. 각 이미지는 30 MB 이하여야 합니다. ",[27,9089,2897],{}," 파라미터로 요청당 최대 9장의 이미지를 전달할 수 있습니다. 이미지는 공개적으로 접근 가능한 URL이어야 합니다 — API가 직접 가져옵니다. 최상의 결과를 위해 짧은 변이 최소 720px인 이미지를 사용하세요. 매우 낮은 해상도의 이미지(256px 미만)는 흐릿한 애니메이션을 생성할 수 있습니다.",[91,9092,9094],{"id":9093},"_15초보다-긴-비디오를-생성할-수-있나요","15초보다 긴 비디오를 생성할 수 있나요?",[10,9096,9097,9098,9100],{},"단일 생성의 최대 길이는 15초입니다. 더 긴 콘텐츠를 만들려면 여러 클립을 생성한 후 FFmpeg이나 비디오 편집기로 연결하세요. Seedance 2.0은 비디오 확장을 지원합니다 — 생성된 비디오의 마지막 프레임을 다음 생성의 첫 프레임으로 사용하여 매끄러운 연속성을 만들 수 있습니다. 기본 접근법: 클립 1을 생성하고, 마지막 프레임을 추출하여 클립 2에 ",[27,9099,2996],{},"으로 전달합니다.",[91,9102,9104],{"id":9103},"evolink를-통한-seedance-20-api-비용은-얼마인가요","EvoLink를 통한 Seedance 2.0 API 비용은 얼마인가요?",[10,9106,9107,9108,9112,9113,9115],{},"가격은 비디오 길이와 품질 등급에 따라 결정됩니다. 5초 720p 비디오는 약 18 크레딧입니다. EvoLink는 직접 API 접근 대비 비용을 절감할 수 있는 ",[36,9109,9111],{"href":211,"rel":9110},[40],"스마트 라우팅","을 제공합니다. 현재 초당 요금은 대시보드에서 확인하세요. API 응답의 ",[27,9114,2114],{}," 필드가 생성 전 정확한 비용을 보여줍니다 — 이 금액 이상 청구되지 않습니다.",[91,9117,9119],{"id":9118},"seedance-15-pro와-seedance-20의-차이점은-무엇인가요","seedance-1.5-pro와 seedance-2.0의 차이점은 무엇인가요?",[10,9121,9122,9123,9125,9126,9128,9129,1259],{},"Seedance 2.0은 멀티모달 참조(이미지, 비디오, 오디오 혼합 입력), 네이티브 오디오 생성, 향상된 물리 일관성, 비디오 편집 기능을 추가합니다. API 인터페이스는 동일합니다 — 같은 엔드포인트, 같은 파라미터, 같은 응답 형식. 지금 ",[27,9124,29],{},"로 테스트하고 모델 이름만 변경하여 ",[27,9127,1750],{},"으로 전환할 수 있습니다. 1.5의 주요 제한: 단일 이미지 입력만 지원(@Image2~9 불가), 비디오/오디오 참조 불가, 네이티브 오디오 생성 불가. 자세한 비교는 ",[36,9130,9132],{"href":9131},"/blog/seedance-2-vs-sora-2-api-comparison","Seedance 2.0 vs Sora 2 비교",[91,9134,9136],{"id":9135},"content-rejected-by-safety-filter-오류는-어떻게-처리하나요","\"content rejected by safety filter\" 오류는 어떻게 처리하나요?",[10,9138,9139,9140,9142,9143,9146],{},"콘텐츠 안전 필터는 사실적인 폭력, 노골적인 콘텐츠, 실제 공인을 포함하는 프롬프트를 거부합니다. ",[27,9141,2897],{},"를 통해 업로드된 사실적인 인물 얼굴 이미지도 거부됩니다. 얼굴 제한을 우회하려면 일러스트, 스타일화된 또는 애니메이션 스타일의 캐릭터 이미지를 사용하세요. 프롬프트 거부의 경우 제한된 주제를 피하도록 문구를 수정하세요. 오류 응답에 ",[27,9144,9145],{},"type: \"content_policy_violation\"","이 포함됩니다 — 오류 처리 코드에서 이를 확인하여 사용자에게 명확한 메시지를 제공하세요.",[91,9148,9150],{"id":9149},"nodejs-javascript-프로젝트에서-seedance-api를-사용할-수-있나요","Node.js / JavaScript 프로젝트에서 Seedance API를 사용할 수 있나요?",[10,9152,9153,9154,9157,9158,9161,9162,9166],{},"네. REST API는 언어에 구애받지 않습니다 — 어떤 HTTP 클라이언트든 사용할 수 있습니다. 이 튜토리얼의 개념(비동기 polling, webhook, 오류 처리)은 Node.js의 ",[27,9155,9156],{},"fetch","나 ",[27,9159,9160],{},"axios","로 직접 적용할 수 있습니다. EvoLink는 polling과 재시도를 자동으로 처리하는 공식 ",[36,9163,9165],{"href":9007,"rel":9164},[40],"Node.js 및 Python SDK","도 제공합니다.",[91,9168,9170],{"id":9169},"비디오-완료-시-webhook-서버가-다운되어-있으면-어떻게-되나요","비디오 완료 시 webhook 서버가 다운되어 있으면 어떻게 되나요?",[10,9172,9173,9174,9177],{},"API는 증가하는 간격(1초, 2초, 4초)으로 webhook 전달을 3회 재시도합니다. 3회 모두 실패하면 webhook은 포기됩니다 — 하지만 비디오는 여전히 사용 가능합니다. ",[27,9175,9176],{},"GET /v1/tasks/{task_id}","로 polling하여 결과를 조회할 수 있습니다. 따라서 제출 시 작업 ID를 저장하고, 완료되었지만 webhook으로 수신되지 않은 작업을 주기적으로 확인하는 백그라운드 작업을 설정하는 것이 좋습니다.",[91,9179,9181],{"id":9180},"api-요청에-속도-제한이-있나요","API 요청에 속도 제한이 있나요?",[10,9183,9184,9185,9187,9188,9192,9193,9197],{},"네. 기본 속도 제한은 개발과 중간 규모 프로덕션 사용에 충분히 여유롭습니다. ",[27,9186,4940],{}," 오류가 발생하면 ",[36,9189,9191],{"href":9190},"#%EC%98%A4%EB%A5%98-%EC%B2%98%EB%A6%AC-%EC%A0%84%EB%9E%B5","오류 처리 섹션","에서 보여준 대로 지수 백오프를 구현하세요. 대규모 사용(하루 수천 건의 비디오 생성)의 경우 ",[36,9194,9196],{"href":211,"rel":9195},[40],"EvoLink 지원팀","에 연락하여 맞춤 속도 제한과 전용 용량을 논의하세요.",[91,9199,9201],{"id":9200},"seedance-20을-상업-프로젝트에-사용할-수-있나요","Seedance 2.0을 상업 프로젝트에 사용할 수 있나요?",[10,9203,9204,9205,1259],{},"네. EvoLink API를 통해 생성된 비디오는 상업적 사용이 허가됩니다. 출력물의 소유권은 사용자에게 있으며, 제품, 마케팅 자료, 클라이언트 납품물, 게시 콘텐츠에 사용할 수 있습니다. 자세한 라이선스 조건과 상업적 사용 모범 사례는 ",[36,9206,9208],{"href":9207},"/blog/seedance-2-copyright-api-guide","Seedance 2.0 저작권 가이드",[44,9210],{},[47,9212,9214],{"id":9213},"전체-스크립트","전체 스크립트",[10,9216,9217],{},"이 튜토리얼의 전체 코드를 하나의 파일로 정리했습니다 — 복사, 붙여넣기, API Key 입력 후 바로 실행하세요:",[136,9219,9221],{"className":337,"code":9220,"language":339,"meta":141,"style":141},"\"\"\"\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    \"\"\"비디오 생성 작업을 완료까지 polling합니다.\"\"\"\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,9222,9223,9227,9232,9237,9242,9246,9252,9258,9264,9270,9276,9280,9284,9300,9308,9316,9332,9340,9344,9348,9352,9357,9377,9382,9390,9400,9408,9428,9436,9440,9444,9452,9464,9480,9516,9528,9534,9546,9577,9581,9589,9619,9623,9627,9641,9645,9666,9682,9686,9702,9720,9724,9758,9762,9766,9780,9785,9797,9803,9811,9823,9833,9841,9850,9854,9866,9872,9884,9918,9926,9941,9965,9969,9995,10021,10064,10068,10072,10079,10105,10145,10149,10153,10175,10179,10183,10191,10196,10204,10216,10224,10246,10255,10271,10291,10309,10325,10350,10369,10376,10422,10427,10432,10441,10447,10456,10477,10486,10491,10504,10526,10533,10554,10559,10564,10570,10579,10588,10599,10606,10611,10616,10621,10626,10637,10648,10659,10668,10673,10679,10688,10732,10745,10765,10770,10775,10781,10790,10799,10810,10817,10822,10827,10833,10838,10852,10863,10872,10877,10882,10891,10916,10929,10947,10952,10957,10963,10973,10982,10993,11000,11006,11012,11017,11028,11039,11050,11059,11064,11069,11078,11103,11116,11134,11139,11144,11157,11169,11174,11180,11186,11192],{"__ignoreMap":141},[145,9224,9225],{"class":147,"line":148},[145,9226,6706],{"class":159},[145,9228,9229],{"class":147,"line":166},[145,9230,9231],{"class":159},"Seedance 2.0 API 튜토리얼 — 전체 스크립트\n",[145,9233,9234],{"class":147,"line":178},[145,9235,9236],{"class":159},"문서: https://seedance2api.app/docs/video-generation\n",[145,9238,9239],{"class":147,"line":187},[145,9240,9241],{"class":159},"API Key: https://evolink.ai/early-access\n",[145,9243,9244],{"class":147,"line":372},[145,9245,6706],{"class":159},[145,9247,9248,9250],{"class":147,"line":378},[145,9249,346],{"class":258},[145,9251,328],{"class":262},[145,9253,9254,9256],{"class":147,"line":384},[145,9255,346],{"class":258},[145,9257,355],{"class":262},[145,9259,9260,9262],{"class":147,"line":408},[145,9261,346],{"class":258},[145,9263,362],{"class":262},[145,9265,9266,9268],{"class":147,"line":419},[145,9267,346],{"class":258},[145,9269,369],{"class":262},[145,9271,9272,9274],{"class":147,"line":430},[145,9273,346],{"class":258},[145,9275,5039],{"class":262},[145,9277,9278],{"class":147,"line":454},[145,9279,375],{"emptyLinePlaceholder":57},[145,9281,9282],{"class":147,"line":465},[145,9283,381],{"class":174},[145,9285,9286,9288,9290,9292,9294,9296,9298],{"class":147,"line":599},[145,9287,387],{"class":155},[145,9289,390],{"class":258},[145,9291,393],{"class":262},[145,9293,396],{"class":159},[145,9295,399],{"class":262},[145,9297,402],{"class":159},[145,9299,405],{"class":262},[145,9301,9302,9304,9306],{"class":147,"line":604},[145,9303,411],{"class":155},[145,9305,390],{"class":258},[145,9307,416],{"class":159},[145,9309,9310,9312,9314],{"class":147,"line":610},[145,9311,422],{"class":155},[145,9313,390],{"class":258},[145,9315,427],{"class":262},[145,9317,9318,9320,9322,9324,9326,9328,9330],{"class":147,"line":616},[145,9319,433],{"class":159},[145,9321,436],{"class":262},[145,9323,439],{"class":258},[145,9325,442],{"class":159},[145,9327,445],{"class":155},[145,9329,448],{"class":159},[145,9331,451],{"class":262},[145,9333,9334,9336,9338],{"class":147,"line":622},[145,9335,457],{"class":159},[145,9337,436],{"class":262},[145,9339,462],{"class":159},[145,9341,9342],{"class":147,"line":627},[145,9343,468],{"class":262},[145,9345,9346],{"class":147,"line":638},[145,9347,375],{"emptyLinePlaceholder":57},[145,9349,9350],{"class":147,"line":653},[145,9351,375],{"emptyLinePlaceholder":57},[145,9353,9354],{"class":147,"line":659},[145,9355,9356],{"class":174},"# ── 재사용 가능한 헬퍼 ────────────────────────────────────────\n",[145,9358,9359,9361,9363,9365,9367,9369,9371,9373,9375],{"class":147,"line":670},[145,9360,525],{"class":258},[145,9362,528],{"class":151},[145,9364,531],{"class":262},[145,9366,266],{"class":258},[145,9368,536],{"class":155},[145,9370,539],{"class":262},[145,9372,266],{"class":258},[145,9374,544],{"class":155},[145,9376,547],{"class":262},[145,9378,9379],{"class":147,"line":697},[145,9380,9381],{"class":159},"    \"\"\"비디오 생성 작업을 완료까지 polling합니다.\"\"\"\n",[145,9383,9384,9386,9388],{"class":147,"line":709},[145,9385,630],{"class":262},[145,9387,266],{"class":258},[145,9389,635],{"class":155},[145,9391,9392,9394,9396,9398],{"class":147,"line":715},[145,9393,641],{"class":258},[145,9395,644],{"class":262},[145,9397,647],{"class":258},[145,9399,650],{"class":262},[145,9401,9402,9404,9406],{"class":147,"line":721},[145,9403,662],{"class":262},[145,9405,266],{"class":258},[145,9407,667],{"class":262},[145,9409,9410,9412,9414,9416,9418,9420,9422,9424,9426],{"class":147,"line":727},[145,9411,673],{"class":258},[145,9413,448],{"class":159},[145,9415,678],{"class":155},[145,9417,681],{"class":159},[145,9419,684],{"class":155},[145,9421,687],{"class":262},[145,9423,690],{"class":155},[145,9425,448],{"class":159},[145,9427,451],{"class":262},[145,9429,9430,9432,9434],{"class":147,"line":738},[145,9431,701],{"class":700},[145,9433,266],{"class":258},[145,9435,706],{"class":155},[145,9437,9438],{"class":147,"line":743},[145,9439,712],{"class":262},[145,9441,9442],{"class":147,"line":749},[145,9443,724],{"class":262},[145,9445,9446,9448,9450],{"class":147,"line":766},[145,9447,730],{"class":262},[145,9449,266],{"class":258},[145,9451,735],{"class":262},[145,9453,9454,9456,9458,9460,9462],{"class":147,"line":787},[145,9455,752],{"class":262},[145,9457,266],{"class":258},[145,9459,757],{"class":262},[145,9461,760],{"class":159},[145,9463,763],{"class":262},[145,9465,9466,9468,9470,9472,9474,9476,9478],{"class":147,"line":833},[145,9467,769],{"class":262},[145,9469,266],{"class":258},[145,9471,774],{"class":262},[145,9473,777],{"class":159},[145,9475,399],{"class":262},[145,9477,782],{"class":155},[145,9479,405],{"class":262},[145,9481,9482,9484,9486,9488,9490,9492,9494,9496,9498,9500,9502,9504,9506,9508,9510,9512,9514],{"class":147,"line":838},[145,9483,790],{"class":155},[145,9485,793],{"class":262},[145,9487,439],{"class":258},[145,9489,798],{"class":159},[145,9491,684],{"class":155},[145,9493,803],{"class":262},[145,9495,690],{"class":155},[145,9497,808],{"class":159},[145,9499,684],{"class":155},[145,9501,813],{"class":262},[145,9503,690],{"class":155},[145,9505,818],{"class":159},[145,9507,684],{"class":155},[145,9509,823],{"class":262},[145,9511,690],{"class":155},[145,9513,828],{"class":159},[145,9515,405],{"class":262},[145,9517,9518,9520,9522,9524,9526],{"class":147,"line":844},[145,9519,847],{"class":258},[145,9521,850],{"class":262},[145,9523,853],{"class":258},[145,9525,856],{"class":159},[145,9527,859],{"class":262},[145,9529,9530,9532],{"class":147,"line":862},[145,9531,865],{"class":258},[145,9533,868],{"class":262},[145,9535,9536,9538,9540,9542,9544],{"class":147,"line":871},[145,9537,874],{"class":258},[145,9539,850],{"class":262},[145,9541,853],{"class":258},[145,9543,881],{"class":159},[145,9545,859],{"class":262},[145,9547,9548,9550,9552,9554,9556,9558,9560,9562,9564,9566,9568,9571,9573,9575],{"class":147,"line":886},[145,9549,905],{"class":258},[145,9551,908],{"class":155},[145,9553,793],{"class":262},[145,9555,439],{"class":258},[145,9557,920],{"class":159},[145,9559,684],{"class":155},[145,9561,687],{"class":262},[145,9563,690],{"class":155},[145,9565,929],{"class":159},[145,9567,684],{"class":155},[145,9569,9570],{"class":262},"task",[145,9572,690],{"class":155},[145,9574,448],{"class":159},[145,9576,405],{"class":262},[145,9578,9579],{"class":147,"line":902},[145,9580,972],{"class":262},[145,9582,9583,9585,9587],{"class":147,"line":914},[145,9584,978],{"class":262},[145,9586,981],{"class":258},[145,9588,984],{"class":262},[145,9590,9591,9593,9595,9597,9599,9601,9603,9605,9607,9609,9611,9613,9615,9617],{"class":147,"line":952},[145,9592,995],{"class":258},[145,9594,998],{"class":155},[145,9596,793],{"class":262},[145,9598,439],{"class":258},[145,9600,920],{"class":159},[145,9602,684],{"class":155},[145,9604,687],{"class":262},[145,9606,690],{"class":155},[145,9608,1013],{"class":159},[145,9610,684],{"class":155},[145,9612,1018],{"class":262},[145,9614,690],{"class":155},[145,9616,1023],{"class":159},[145,9618,405],{"class":262},[145,9620,9621],{"class":147,"line":958},[145,9622,375],{"emptyLinePlaceholder":57},[145,9624,9625],{"class":147,"line":963},[145,9626,375],{"emptyLinePlaceholder":57},[145,9628,9629,9631,9633,9635,9637,9639],{"class":147,"line":969},[145,9630,525],{"class":258},[145,9632,1081],{"class":151},[145,9634,1084],{"class":262},[145,9636,266],{"class":258},[145,9638,1089],{"class":159},[145,9640,547],{"class":262},[145,9642,9643],{"class":147,"line":975},[145,9644,1096],{"class":159},[145,9646,9647,9649,9651,9653,9656,9658,9660,9662,9664],{"class":147,"line":987},[145,9648,1101],{"class":155},[145,9650,793],{"class":262},[145,9652,439],{"class":258},[145,9654,9655],{"class":159},"\"Downloading to ",[145,9657,684],{"class":155},[145,9659,1113],{"class":262},[145,9661,690],{"class":155},[145,9663,1118],{"class":159},[145,9665,405],{"class":262},[145,9667,9668,9670,9672,9674,9676,9678,9680],{"class":147,"line":992},[145,9669,1125],{"class":262},[145,9671,266],{"class":258},[145,9673,1130],{"class":262},[145,9675,1133],{"class":700},[145,9677,266],{"class":258},[145,9679,1138],{"class":155},[145,9681,405],{"class":262},[145,9683,9684],{"class":147,"line":5533},[145,9685,1145],{"class":262},[145,9687,9688,9690,9692,9694,9696,9698,9700],{"class":147,"line":5542},[145,9689,1150],{"class":258},[145,9691,1153],{"class":155},[145,9693,1156],{"class":262},[145,9695,1159],{"class":159},[145,9697,1162],{"class":262},[145,9699,1165],{"class":258},[145,9701,1168],{"class":262},[145,9703,9704,9706,9708,9710,9712,9714,9716,9718],{"class":147,"line":5548},[145,9705,1173],{"class":258},[145,9707,1176],{"class":262},[145,9709,1179],{"class":258},[145,9711,1182],{"class":262},[145,9713,1185],{"class":700},[145,9715,266],{"class":258},[145,9717,1190],{"class":155},[145,9719,547],{"class":262},[145,9721,9722],{"class":147,"line":5576},[145,9723,1197],{"class":262},[145,9725,9726,9728,9730,9732,9734,9736,9738,9740,9742,9744,9746,9748,9750,9752,9754,9756],{"class":147,"line":5607},[145,9727,1101],{"class":155},[145,9729,793],{"class":262},[145,9731,439],{"class":258},[145,9733,1208],{"class":159},[145,9735,684],{"class":155},[145,9737,1113],{"class":262},[145,9739,690],{"class":155},[145,9741,103],{"class":159},[145,9743,684],{"class":155},[145,9745,1221],{"class":262},[145,9747,1224],{"class":258},[145,9749,1227],{"class":155},[145,9751,1230],{"class":258},[145,9753,690],{"class":155},[145,9755,1235],{"class":159},[145,9757,405],{"class":262},[145,9759,9760],{"class":147,"line":5627},[145,9761,375],{"emptyLinePlaceholder":57},[145,9763,9764],{"class":147,"line":5633},[145,9765,375],{"emptyLinePlaceholder":57},[145,9767,9768,9770,9772,9774,9776,9778],{"class":147,"line":5639},[145,9769,525],{"class":258},[145,9771,5050],{"class":151},[145,9773,5053],{"class":262},[145,9775,266],{"class":258},[145,9777,5058],{"class":155},[145,9779,547],{"class":262},[145,9781,9782],{"class":147,"line":5644},[145,9783,9784],{"class":159},"    \"\"\"일시적 오류에 대해 자동 재시도하는 생성 요청 제출.\"\"\"\n",[145,9786,9787,9789,9791,9793,9795],{"class":147,"line":5657},[145,9788,5121],{"class":258},[145,9790,5124],{"class":262},[145,9792,1179],{"class":258},[145,9794,5129],{"class":155},[145,9796,5132],{"class":262},[145,9798,9799,9801],{"class":147,"line":5663},[145,9800,5137],{"class":258},[145,9802,859],{"class":262},[145,9804,9805,9807,9809],{"class":147,"line":5690},[145,9806,5144],{"class":262},[145,9808,266],{"class":258},[145,9810,1431],{"class":262},[145,9812,9813,9815,9817,9819,9821],{"class":147,"line":5730},[145,9814,917],{"class":258},[145,9816,448],{"class":159},[145,9818,678],{"class":155},[145,9820,1443],{"class":159},[145,9822,451],{"class":262},[145,9824,9825,9827,9829,9831],{"class":147,"line":5749},[145,9826,5165],{"class":700},[145,9828,266],{"class":258},[145,9830,422],{"class":155},[145,9832,451],{"class":262},[145,9834,9835,9837,9839],{"class":147,"line":5754},[145,9836,5176],{"class":700},[145,9838,266],{"class":258},[145,9840,5181],{"class":262},[145,9842,9843,9845,9847],{"class":147,"line":5759},[145,9844,5186],{"class":700},[145,9846,266],{"class":258},[145,9848,9849],{"class":155},"30\n",[145,9851,9852],{"class":147,"line":5764},[145,9853,955],{"class":262},[145,9855,9856,9858,9860,9862,9864],{"class":147,"line":7444},[145,9857,5212],{"class":258},[145,9859,2510],{"class":262},[145,9861,853],{"class":258},[145,9863,2515],{"class":155},[145,9865,859],{"class":262},[145,9867,9868,9870],{"class":147,"line":7450},[145,9869,5225],{"class":258},[145,9871,735],{"class":262},[145,9873,9874,9876,9878,9880,9882],{"class":147,"line":7461},[145,9875,5241],{"class":262},[145,9877,266],{"class":258},[145,9879,5246],{"class":262},[145,9881,896],{"class":159},[145,9883,899],{"class":262},[145,9885,9886,9888,9890,9892,9894,9896,9898,9900,9902,9904,9906,9908,9910,9912,9914,9916],{"class":147,"line":7472},[145,9887,5212],{"class":258},[145,9889,2510],{"class":262},[145,9891,1179],{"class":258},[145,9893,103],{"class":262},[145,9895,3640],{"class":155},[145,9897,399],{"class":262},[145,9899,4850],{"class":155},[145,9901,399],{"class":262},[145,9903,4868],{"class":155},[145,9905,399],{"class":262},[145,9907,4886],{"class":155},[145,9909,399],{"class":262},[145,9911,4904],{"class":155},[145,9913,399],{"class":262},[145,9915,4922],{"class":155},[145,9917,547],{"class":262},[145,9919,9920,9922,9924],{"class":147,"line":7483},[145,9921,5337],{"class":258},[145,9923,5340],{"class":155},[145,9925,911],{"class":262},[145,9927,9928,9930,9932,9934,9936,9938],{"class":147,"line":7488},[145,9929,5347],{"class":258},[145,9931,5350],{"class":159},[145,9933,684],{"class":155},[145,9935,5355],{"class":262},[145,9937,690],{"class":155},[145,9939,9940],{"class":159},": \"\n",[145,9942,9943,9945,9947,9949,9952,9954,9956,9959,9961,9963],{"class":147,"line":7494},[145,9944,5347],{"class":258},[145,9946,448],{"class":159},[145,9948,684],{"class":155},[145,9950,9951],{"class":262},"error.get(",[145,9953,937],{"class":159},[145,9955,399],{"class":262},[145,9957,9958],{"class":159},"'Unknown'",[145,9960,117],{"class":262},[145,9962,690],{"class":155},[145,9964,949],{"class":159},[145,9966,9967],{"class":147,"line":7502},[145,9968,5373],{"class":262},[145,9970,9971,9973,9975,9977,9979,9981,9983,9985,9987,9989,9991,9993],{"class":147,"line":7516},[145,9972,5212],{"class":258},[145,9974,2510],{"class":262},[145,9976,1179],{"class":258},[145,9978,103],{"class":262},[145,9980,4940],{"class":155},[145,9982,399],{"class":262},[145,9984,4961],{"class":155},[145,9986,399],{"class":262},[145,9988,4981],{"class":155},[145,9990,399],{"class":262},[145,9992,5001],{"class":155},[145,9994,547],{"class":262},[145,9996,9997,9999,10001,10003,10005,10007,10009,10011,10013,10015,10017,10019],{"class":147,"line":7521},[145,9998,5413],{"class":262},[145,10000,266],{"class":258},[145,10002,103],{"class":262},[145,10004,5420],{"class":155},[145,10006,5423],{"class":258},[145,10008,5426],{"class":262},[145,10010,5429],{"class":258},[145,10012,5432],{"class":262},[145,10014,782],{"class":155},[145,10016,399],{"class":262},[145,10018,5439],{"class":155},[145,10020,405],{"class":262},[145,10022,10023,10025,10027,10029,10031,10033,10036,10038,10041,10043,10045,10047,10049,10052,10054,10056,10058,10060,10062],{"class":147,"line":7535},[145,10024,5446],{"class":155},[145,10026,793],{"class":262},[145,10028,439],{"class":258},[145,10030,5453],{"class":159},[145,10032,684],{"class":155},[145,10034,10035],{"class":262},"attempt",[145,10037,5429],{"class":258},[145,10039,10040],{"class":155},"1}",[145,10042,1224],{"class":159},[145,10044,684],{"class":155},[145,10046,5470],{"class":262},[145,10048,690],{"class":155},[145,10050,10051],{"class":159}," after ",[145,10053,684],{"class":155},[145,10055,5488],{"class":262},[145,10057,5491],{"class":258},[145,10059,690],{"class":155},[145,10061,1023],{"class":159},[145,10063,405],{"class":262},[145,10065,10066],{"class":147,"line":7549},[145,10067,5521],{"class":262},[145,10069,10070],{"class":147,"line":7581},[145,10071,5526],{"class":258},[145,10073,10074,10076],{"class":147,"line":7593},[145,10075,5536],{"class":258},[145,10077,10078],{"class":262}," requests.exceptions.RequestException:\n",[145,10080,10081,10083,10085,10087,10089,10091,10093,10095,10097,10099,10101,10103],{"class":147,"line":7603},[145,10082,5551],{"class":262},[145,10084,266],{"class":258},[145,10086,103],{"class":262},[145,10088,5420],{"class":155},[145,10090,5423],{"class":258},[145,10092,5426],{"class":262},[145,10094,5429],{"class":258},[145,10096,5432],{"class":262},[145,10098,782],{"class":155},[145,10100,399],{"class":262},[145,10102,5439],{"class":155},[145,10104,405],{"class":262},[145,10106,10107,10109,10111,10113,10115,10117,10119,10121,10123,10125,10127,10129,10131,10133,10135,10137,10139,10141,10143],{"class":147,"line":7608},[145,10108,5579],{"class":155},[145,10110,793],{"class":262},[145,10112,439],{"class":258},[145,10114,5453],{"class":159},[145,10116,684],{"class":155},[145,10118,10035],{"class":262},[145,10120,5429],{"class":258},[145,10122,10040],{"class":155},[145,10124,1224],{"class":159},[145,10126,684],{"class":155},[145,10128,5470],{"class":262},[145,10130,690],{"class":155},[145,10132,10051],{"class":159},[145,10134,684],{"class":155},[145,10136,5488],{"class":262},[145,10138,5491],{"class":258},[145,10140,690],{"class":155},[145,10142,1023],{"class":159},[145,10144,405],{"class":262},[145,10146,10147],{"class":147,"line":7615},[145,10148,5630],{"class":262},[145,10150,10151],{"class":147,"line":7637},[145,10152,5636],{"class":258},[145,10154,10155,10157,10159,10161,10163,10165,10167,10169,10171,10173],{"class":147,"line":7668},[145,10156,995],{"class":258},[145,10158,908],{"class":155},[145,10160,793],{"class":262},[145,10162,439],{"class":258},[145,10164,5775],{"class":159},[145,10166,684],{"class":155},[145,10168,5470],{"class":262},[145,10170,690],{"class":155},[145,10172,5784],{"class":159},[145,10174,405],{"class":262},[145,10176,10177],{"class":147,"line":7673},[145,10178,375],{"emptyLinePlaceholder":57},[145,10180,10181],{"class":147,"line":7679},[145,10182,375],{"emptyLinePlaceholder":57},[145,10184,10185,10187,10189],{"class":147,"line":7704},[145,10186,525],{"class":258},[145,10188,5847],{"class":151},[145,10190,5850],{"class":262},[145,10192,10193],{"class":147,"line":7709},[145,10194,10195],{"class":159},"    \"\"\"API 호출 전 생성 payload를 검증합니다.\"\"\"\n",[145,10197,10198,10200,10202],{"class":147,"line":7714},[145,10199,5873],{"class":262},[145,10201,266],{"class":258},[145,10203,5878],{"class":262},[145,10205,10206,10208,10210,10212,10214],{"class":147,"line":7737},[145,10207,2507],{"class":258},[145,10209,5894],{"class":258},[145,10211,5897],{"class":262},[145,10213,5900],{"class":159},[145,10215,547],{"class":262},[145,10217,10218,10220,10222],{"class":147,"line":7747},[145,10219,5907],{"class":262},[145,10221,5910],{"class":159},[145,10223,405],{"class":262},[145,10225,10226,10228,10230,10232,10234,10236,10238,10240,10242,10244],{"class":147,"line":7753},[145,10227,2507],{"class":258},[145,10229,5894],{"class":258},[145,10231,5897],{"class":262},[145,10233,5923],{"class":159},[145,10235,1162],{"class":262},[145,10237,5928],{"class":258},[145,10239,5894],{"class":258},[145,10241,5933],{"class":262},[145,10243,5923],{"class":159},[145,10245,5938],{"class":262},[145,10247,10248,10250,10253],{"class":147,"line":7771},[145,10249,5907],{"class":262},[145,10251,10252],{"class":159},"\"'prompt' is required\"",[145,10254,405],{"class":262},[145,10256,10257,10259,10261,10263,10265,10267,10269],{"class":147,"line":7776},[145,10258,5961],{"class":262},[145,10260,266],{"class":258},[145,10262,5897],{"class":262},[145,10264,5968],{"class":159},[145,10266,399],{"class":262},[145,10268,1351],{"class":155},[145,10270,405],{"class":262},[145,10272,10273,10275,10277,10279,10281,10283,10285,10287,10289],{"class":147,"line":7781},[145,10274,2507],{"class":258},[145,10276,5981],{"class":262},[145,10278,647],{"class":258},[145,10280,5986],{"class":155},[145,10282,5989],{"class":258},[145,10284,5981],{"class":262},[145,10286,5994],{"class":258},[145,10288,5997],{"class":155},[145,10290,859],{"class":262},[145,10292,10293,10295,10297,10299,10301,10303,10305,10307],{"class":147,"line":7794},[145,10294,5907],{"class":262},[145,10296,439],{"class":258},[145,10298,6008],{"class":159},[145,10300,684],{"class":155},[145,10302,1773],{"class":262},[145,10304,690],{"class":155},[145,10306,448],{"class":159},[145,10308,405],{"class":262},[145,10310,10311,10313,10315,10317,10319,10321,10323],{"class":147,"line":7808},[145,10312,6054],{"class":262},[145,10314,266],{"class":258},[145,10316,5897],{"class":262},[145,10318,6061],{"class":159},[145,10320,399],{"class":262},[145,10322,1367],{"class":159},[145,10324,405],{"class":262},[145,10326,10327,10329,10331,10333,10335,10337,10339,10341,10343,10345,10347],{"class":147,"line":7835},[145,10328,2507],{"class":258},[145,10330,6074],{"class":262},[145,10332,6077],{"class":258},[145,10334,6080],{"class":258},[145,10336,6037],{"class":262},[145,10338,4016],{"class":159},[145,10340,399],{"class":262},[145,10342,1367],{"class":159},[145,10344,399],{"class":262},[145,10346,3713],{"class":159},[145,10348,10349],{"class":262},"}:\n",[145,10351,10352,10354,10356,10359,10361,10363,10365,10367],{"class":147,"line":7849},[145,10353,5907],{"class":262},[145,10355,439],{"class":258},[145,10357,10358],{"class":159},"\"Invalid quality: ",[145,10360,684],{"class":155},[145,10362,1781],{"class":262},[145,10364,690],{"class":155},[145,10366,448],{"class":159},[145,10368,405],{"class":262},[145,10370,10372,10374],{"class":147,"line":10371},99,[145,10373,2507],{"class":258},[145,10375,6349],{"class":262},[145,10377,10379,10381,10383,10385,10388,10390,10392,10394,10396,10398,10400,10402,10404,10406,10408,10410,10412,10414,10416,10418,10420],{"class":147,"line":10378},100,[145,10380,6354],{"class":258},[145,10382,5340],{"class":155},[145,10384,793],{"class":262},[145,10386,10387],{"class":159},"\"Validation failed:",[145,10389,1669],{"class":155},[145,10391,448],{"class":159},[145,10393,6370],{"class":258},[145,10395,6373],{"class":159},[145,10397,1669],{"class":155},[145,10399,448],{"class":159},[145,10401,6380],{"class":262},[145,10403,439],{"class":258},[145,10405,6385],{"class":159},[145,10407,684],{"class":155},[145,10409,5704],{"class":262},[145,10411,690],{"class":155},[145,10413,448],{"class":159},[145,10415,6396],{"class":258},[145,10417,6399],{"class":262},[145,10419,1179],{"class":258},[145,10421,6404],{"class":262},[145,10423,10425],{"class":147,"line":10424},101,[145,10426,375],{"emptyLinePlaceholder":57},[145,10428,10430],{"class":147,"line":10429},102,[145,10431,375],{"emptyLinePlaceholder":57},[145,10433,10435,10437,10439],{"class":147,"line":10434},103,[145,10436,525],{"class":258},[145,10438,2453],{"class":151},[145,10440,2456],{"class":262},[145,10442,10444],{"class":147,"line":10443},104,[145,10445,10446],{"class":159},"    \"\"\"pending 또는 processing 상태의 작업을 취소합니다.\"\"\"\n",[145,10448,10450,10452,10454],{"class":147,"line":10449},105,[145,10451,1426],{"class":262},[145,10453,266],{"class":258},[145,10455,1431],{"class":262},[145,10457,10459,10461,10463,10465,10467,10469,10471,10473,10475],{"class":147,"line":10458},106,[145,10460,1436],{"class":258},[145,10462,448],{"class":159},[145,10464,678],{"class":155},[145,10466,681],{"class":159},[145,10468,684],{"class":155},[145,10470,687],{"class":262},[145,10472,690],{"class":155},[145,10474,2488],{"class":159},[145,10476,451],{"class":262},[145,10478,10480,10482,10484],{"class":147,"line":10479},107,[145,10481,1454],{"class":700},[145,10483,266],{"class":258},[145,10485,706],{"class":155},[145,10487,10489],{"class":147,"line":10488},108,[145,10490,1482],{"class":262},[145,10492,10494,10496,10498,10500,10502],{"class":147,"line":10493},109,[145,10495,2507],{"class":258},[145,10497,2510],{"class":262},[145,10499,853],{"class":258},[145,10501,2515],{"class":155},[145,10503,859],{"class":262},[145,10505,10507,10509,10511,10513,10515,10517,10519,10521,10524],{"class":147,"line":10506},110,[145,10508,790],{"class":155},[145,10510,793],{"class":262},[145,10512,439],{"class":258},[145,10514,920],{"class":159},[145,10516,684],{"class":155},[145,10518,687],{"class":262},[145,10520,690],{"class":155},[145,10522,10523],{"class":159}," cancelled.\"",[145,10525,405],{"class":262},[145,10527,10529,10531],{"class":147,"line":10528},111,[145,10530,2543],{"class":258},[145,10532,859],{"class":262},[145,10534,10536,10538,10540,10542,10544,10546,10548,10550,10552],{"class":147,"line":10535},112,[145,10537,790],{"class":155},[145,10539,793],{"class":262},[145,10541,439],{"class":258},[145,10543,2556],{"class":159},[145,10545,684],{"class":155},[145,10547,2561],{"class":262},[145,10549,690],{"class":155},[145,10551,448],{"class":159},[145,10553,405],{"class":262},[145,10555,10557],{"class":147,"line":10556},113,[145,10558,375],{"emptyLinePlaceholder":57},[145,10560,10562],{"class":147,"line":10561},114,[145,10563,375],{"emptyLinePlaceholder":57},[145,10565,10567],{"class":147,"line":10566},115,[145,10568,10569],{"class":174},"# ── 예제 1: 텍스트-투-비디오 ──────────────────────────────────\n",[145,10571,10573,10575,10577],{"class":147,"line":10572},116,[145,10574,525],{"class":258},[145,10576,1285],{"class":151},[145,10578,1288],{"class":262},[145,10580,10582,10584,10586],{"class":147,"line":10581},117,[145,10583,1293],{"class":262},[145,10585,266],{"class":258},[145,10587,427],{"class":262},[145,10589,10591,10593,10595,10597],{"class":147,"line":10590},118,[145,10592,1302],{"class":159},[145,10594,436],{"class":262},[145,10596,1307],{"class":159},[145,10598,451],{"class":262},[145,10600,10602,10604],{"class":147,"line":10601},119,[145,10603,1318],{"class":159},[145,10605,1321],{"class":262},[145,10607,10609],{"class":147,"line":10608},120,[145,10610,1326],{"class":159},[145,10612,10614],{"class":147,"line":10613},121,[145,10615,1331],{"class":159},[145,10617,10619],{"class":147,"line":10618},122,[145,10620,1336],{"class":159},[145,10622,10624],{"class":147,"line":10623},123,[145,10625,1341],{"class":262},[145,10627,10629,10631,10633,10635],{"class":147,"line":10628},124,[145,10630,1346],{"class":159},[145,10632,436],{"class":262},[145,10634,1351],{"class":155},[145,10636,451],{"class":262},[145,10638,10640,10642,10644,10646],{"class":147,"line":10639},125,[145,10641,1362],{"class":159},[145,10643,436],{"class":262},[145,10645,1367],{"class":159},[145,10647,451],{"class":262},[145,10649,10651,10653,10655,10657],{"class":147,"line":10650},126,[145,10652,1378],{"class":159},[145,10654,436],{"class":262},[145,10656,1383],{"class":159},[145,10658,451],{"class":262},[145,10660,10662,10664,10666],{"class":147,"line":10661},127,[145,10663,1394],{"class":159},[145,10665,436],{"class":262},[145,10667,3741],{"class":155},[145,10669,10671],{"class":147,"line":10670},128,[145,10672,1406],{"class":262},[145,10674,10676],{"class":147,"line":10675},129,[145,10677,10678],{"class":262},"    validate_payload(payload)\n",[145,10680,10682,10684,10686],{"class":147,"line":10681},130,[145,10683,1495],{"class":262},[145,10685,266],{"class":258},[145,10687,8533],{"class":262},[145,10689,10691,10693,10695,10697,10700,10702,10704,10706,10708,10710,10713,10715,10717,10719,10721,10723,10725,10727,10730],{"class":147,"line":10690},131,[145,10692,1101],{"class":155},[145,10694,793],{"class":262},[145,10696,439],{"class":258},[145,10698,10699],{"class":159},"\"Task: ",[145,10701,684],{"class":155},[145,10703,1528],{"class":262},[145,10705,1531],{"class":159},[145,10707,1534],{"class":262},[145,10709,690],{"class":155},[145,10711,10712],{"class":159}," (ETA: ",[145,10714,684],{"class":155},[145,10716,1528],{"class":262},[145,10718,1558],{"class":159},[145,10720,1561],{"class":262},[145,10722,1564],{"class":159},[145,10724,1534],{"class":262},[145,10726,690],{"class":155},[145,10728,10729],{"class":159},"s)\"",[145,10731,405],{"class":262},[145,10733,10735,10737,10739,10741,10743],{"class":147,"line":10734},132,[145,10736,1617],{"class":262},[145,10738,266],{"class":258},[145,10740,1622],{"class":262},[145,10742,1625],{"class":159},[145,10744,1628],{"class":262},[145,10746,10748,10751,10753,10755,10757,10760,10763],{"class":147,"line":10747},133,[145,10749,10750],{"class":262},"    download_video(result[",[145,10752,1650],{"class":159},[145,10754,1561],{"class":262},[145,10756,782],{"class":155},[145,10758,10759],{"class":262},"], ",[145,10761,10762],{"class":159},"\"text_to_video.mp4\"",[145,10764,405],{"class":262},[145,10766,10768],{"class":147,"line":10767},134,[145,10769,375],{"emptyLinePlaceholder":57},[145,10771,10773],{"class":147,"line":10772},135,[145,10774,375],{"emptyLinePlaceholder":57},[145,10776,10778],{"class":147,"line":10777},136,[145,10779,10780],{"class":174},"# ── 예제 2: 이미지-투-비디오 ──────────────────────────────────\n",[145,10782,10784,10786,10788],{"class":147,"line":10783},137,[145,10785,525],{"class":258},[145,10787,2639],{"class":151},[145,10789,1288],{"class":262},[145,10791,10793,10795,10797],{"class":147,"line":10792},138,[145,10794,1293],{"class":262},[145,10796,266],{"class":258},[145,10798,427],{"class":262},[145,10800,10802,10804,10806,10808],{"class":147,"line":10801},139,[145,10803,1302],{"class":159},[145,10805,436],{"class":262},[145,10807,1307],{"class":159},[145,10809,451],{"class":262},[145,10811,10813,10815],{"class":147,"line":10812},140,[145,10814,1318],{"class":159},[145,10816,1321],{"class":262},[145,10818,10820],{"class":147,"line":10819},141,[145,10821,2670],{"class":159},[145,10823,10825],{"class":147,"line":10824},142,[145,10826,2675],{"class":159},[145,10828,10830],{"class":147,"line":10829},143,[145,10831,10832],{"class":159},"            \"across the frame.\"\n",[145,10834,10836],{"class":147,"line":10835},144,[145,10837,1341],{"class":262},[145,10839,10841,10843,10846,10849],{"class":147,"line":10840},145,[145,10842,2689],{"class":159},[145,10844,10845],{"class":262},": [",[145,10847,10848],{"class":159},"\"https://example.com/your-image.jpg\"",[145,10850,10851],{"class":262},"],\n",[145,10853,10855,10857,10859,10861],{"class":147,"line":10854},146,[145,10856,1346],{"class":159},[145,10858,436],{"class":262},[145,10860,1351],{"class":155},[145,10862,451],{"class":262},[145,10864,10866,10868,10870],{"class":147,"line":10865},147,[145,10867,1362],{"class":159},[145,10869,436],{"class":262},[145,10871,8512],{"class":159},[145,10873,10875],{"class":147,"line":10874},148,[145,10876,1406],{"class":262},[145,10878,10880],{"class":147,"line":10879},149,[145,10881,10678],{"class":262},[145,10883,10885,10887,10889],{"class":147,"line":10884},150,[145,10886,1495],{"class":262},[145,10888,266],{"class":258},[145,10890,8533],{"class":262},[145,10892,10894,10896,10898,10900,10902,10904,10906,10908,10910,10912,10914],{"class":147,"line":10893},151,[145,10895,1101],{"class":155},[145,10897,793],{"class":262},[145,10899,439],{"class":258},[145,10901,10699],{"class":159},[145,10903,684],{"class":155},[145,10905,1528],{"class":262},[145,10907,1531],{"class":159},[145,10909,1534],{"class":262},[145,10911,690],{"class":155},[145,10913,448],{"class":159},[145,10915,405],{"class":262},[145,10917,10919,10921,10923,10925,10927],{"class":147,"line":10918},152,[145,10920,1617],{"class":262},[145,10922,266],{"class":258},[145,10924,1622],{"class":262},[145,10926,1625],{"class":159},[145,10928,1628],{"class":262},[145,10930,10932,10934,10936,10938,10940,10942,10945],{"class":147,"line":10931},153,[145,10933,10750],{"class":262},[145,10935,1650],{"class":159},[145,10937,1561],{"class":262},[145,10939,782],{"class":155},[145,10941,10759],{"class":262},[145,10943,10944],{"class":159},"\"image_to_video.mp4\"",[145,10946,405],{"class":262},[145,10948,10950],{"class":147,"line":10949},154,[145,10951,375],{"emptyLinePlaceholder":57},[145,10953,10955],{"class":147,"line":10954},155,[145,10956,375],{"emptyLinePlaceholder":57},[145,10958,10960],{"class":147,"line":10959},156,[145,10961,10962],{"class":174},"# ── 예제 3: 세로형 소셜 미디어 비디오 ─────────────────────────\n",[145,10964,10966,10968,10971],{"class":147,"line":10965},157,[145,10967,525],{"class":258},[145,10969,10970],{"class":151}," social_media_video",[145,10972,1288],{"class":262},[145,10974,10976,10978,10980],{"class":147,"line":10975},158,[145,10977,1293],{"class":262},[145,10979,266],{"class":258},[145,10981,427],{"class":262},[145,10983,10985,10987,10989,10991],{"class":147,"line":10984},159,[145,10986,1302],{"class":159},[145,10988,436],{"class":262},[145,10990,1307],{"class":159},[145,10992,451],{"class":262},[145,10994,10996,10998],{"class":147,"line":10995},160,[145,10997,1318],{"class":159},[145,10999,1321],{"class":262},[145,11001,11003],{"class":147,"line":11002},161,[145,11004,11005],{"class":159},"            \"A barista pours latte art in slow motion. \"\n",[145,11007,11009],{"class":147,"line":11008},162,[145,11010,11011],{"class":159},"            \"Close-up overhead shot, warm cafe lighting.\"\n",[145,11013,11015],{"class":147,"line":11014},163,[145,11016,1341],{"class":262},[145,11018,11020,11022,11024,11026],{"class":147,"line":11019},164,[145,11021,1346],{"class":159},[145,11023,436],{"class":262},[145,11025,3701],{"class":155},[145,11027,451],{"class":262},[145,11029,11031,11033,11035,11037],{"class":147,"line":11030},165,[145,11032,1362],{"class":159},[145,11034,436],{"class":262},[145,11036,3713],{"class":159},[145,11038,451],{"class":262},[145,11040,11042,11044,11046,11048],{"class":147,"line":11041},166,[145,11043,1378],{"class":159},[145,11045,436],{"class":262},[145,11047,3725],{"class":159},[145,11049,451],{"class":262},[145,11051,11053,11055,11057],{"class":147,"line":11052},167,[145,11054,1394],{"class":159},[145,11056,436],{"class":262},[145,11058,3741],{"class":155},[145,11060,11062],{"class":147,"line":11061},168,[145,11063,1406],{"class":262},[145,11065,11067],{"class":147,"line":11066},169,[145,11068,10678],{"class":262},[145,11070,11072,11074,11076],{"class":147,"line":11071},170,[145,11073,1495],{"class":262},[145,11075,266],{"class":258},[145,11077,8533],{"class":262},[145,11079,11081,11083,11085,11087,11089,11091,11093,11095,11097,11099,11101],{"class":147,"line":11080},171,[145,11082,1101],{"class":155},[145,11084,793],{"class":262},[145,11086,439],{"class":258},[145,11088,10699],{"class":159},[145,11090,684],{"class":155},[145,11092,1528],{"class":262},[145,11094,1531],{"class":159},[145,11096,1534],{"class":262},[145,11098,690],{"class":155},[145,11100,448],{"class":159},[145,11102,405],{"class":262},[145,11104,11106,11108,11110,11112,11114],{"class":147,"line":11105},172,[145,11107,1617],{"class":262},[145,11109,266],{"class":258},[145,11111,1622],{"class":262},[145,11113,1625],{"class":159},[145,11115,1628],{"class":262},[145,11117,11119,11121,11123,11125,11127,11129,11132],{"class":147,"line":11118},173,[145,11120,10750],{"class":262},[145,11122,1650],{"class":159},[145,11124,1561],{"class":262},[145,11126,782],{"class":155},[145,11128,10759],{"class":262},[145,11130,11131],{"class":159},"\"social_video.mp4\"",[145,11133,405],{"class":262},[145,11135,11137],{"class":147,"line":11136},174,[145,11138,375],{"emptyLinePlaceholder":57},[145,11140,11142],{"class":147,"line":11141},175,[145,11143,375],{"emptyLinePlaceholder":57},[145,11145,11147,11149,11151,11153,11155],{"class":147,"line":11146},176,[145,11148,1718],{"class":258},[145,11150,1721],{"class":155},[145,11152,1724],{"class":258},[145,11154,1727],{"class":159},[145,11156,859],{"class":262},[145,11158,11160,11162,11164,11167],{"class":147,"line":11159},177,[145,11161,1101],{"class":155},[145,11163,793],{"class":262},[145,11165,11166],{"class":159},"\"=== 텍스트-투-비디오 ===\"",[145,11168,405],{"class":262},[145,11170,11172],{"class":147,"line":11171},178,[145,11173,1734],{"class":262},[145,11175,11177],{"class":147,"line":11176},179,[145,11178,11179],{"class":174},"    # print(\"\\n=== 이미지-투-비디오 ===\")\n",[145,11181,11183],{"class":147,"line":11182},180,[145,11184,11185],{"class":174},"    # image_to_video()  # 주석 해제 후 이미지 URL 설정\n",[145,11187,11189],{"class":147,"line":11188},181,[145,11190,11191],{"class":174},"    # print(\"\\n=== 소셜 미디어 비디오 ===\")\n",[145,11193,11195],{"class":147,"line":11194},182,[145,11196,11197],{"class":174},"    # social_media_video()\n",[17,11199,11200],{},[10,11201,11202,11204,11205,11207,11208,11211],{},[22,11203,133],{}," 현재 사용 가능한 모델로 테스트하려면 ",[27,11206,1307],{},"을 ",[27,11209,11210],{},"\"seedance-1.5-pro\"","로 변경하세요. API 인터페이스는 동일합니다 — 같은 엔드포인트, 같은 파라미터, 같은 응답 형식. Seedance 2.0이 완전히 출시되면 모델 이름만 다시 변경하면 됩니다.",[10,11213,11214],{},[22,11215,11216],{},[36,11217,11219],{"href":38,"rel":11218},[40],"시작하기 → EvoLink에서 무료 API Key 발급받기",[11221,11222,11223],"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":141,"searchDepth":166,"depth":166,"links":11225},[11226,11229,11230,11231,11235,11240,11245,11249,11255,11263,11264,11269,11281],{"id":49,"depth":166,"text":50,"children":11227},[11228],{"id":93,"depth":178,"text":94},{"id":204,"depth":166,"text":205},{"id":310,"depth":166,"text":311},{"id":1264,"depth":166,"text":1265,"children":11232},[11233,11234],{"id":1843,"depth":178,"text":1844},{"id":2170,"depth":178,"text":2171},{"id":2214,"depth":166,"text":2215,"children":11236},[11237,11238,11239],{"id":2264,"depth":178,"text":2265},{"id":2343,"depth":178,"text":2344},{"id":2437,"depth":178,"text":2438},{"id":2603,"depth":166,"text":2604,"children":11241},[11242,11243,11244],{"id":2941,"depth":178,"text":2942},{"id":3218,"depth":178,"text":3219},{"id":3294,"depth":178,"text":3295},{"id":3389,"depth":166,"text":3390,"children":11246},[11247,11248],{"id":3644,"depth":178,"text":3645},{"id":4053,"depth":178,"text":4054},{"id":4164,"depth":166,"text":4165,"children":11250},[11251,11252,11253,11254],{"id":4171,"depth":178,"text":4172},{"id":4805,"depth":178,"text":4806},{"id":5019,"depth":178,"text":5020},{"id":5831,"depth":178,"text":5832},{"id":6431,"depth":166,"text":6432,"children":11256},[11257,11258,11259,11260,11261,11262],{"id":6442,"depth":178,"text":6443},{"id":6621,"depth":178,"text":6622},{"id":6687,"depth":178,"text":6688},{"id":7940,"depth":178,"text":7941},{"id":8078,"depth":178,"text":8079},{"id":8257,"depth":178,"text":8258},{"id":8349,"depth":166,"text":8350},{"id":8936,"depth":166,"text":8937,"children":11265},[11266,11267,11268],{"id":8943,"depth":178,"text":8944},{"id":8986,"depth":178,"text":8987},{"id":9012,"depth":178,"text":9013},{"id":9065,"depth":166,"text":9066,"children":11270},[11271,11272,11273,11274,11275,11276,11277,11278,11279,11280],{"id":9069,"depth":178,"text":9070},{"id":9083,"depth":178,"text":9084},{"id":9093,"depth":178,"text":9094},{"id":9103,"depth":178,"text":9104},{"id":9118,"depth":178,"text":9119},{"id":9135,"depth":178,"text":9136},{"id":9149,"depth":178,"text":9150},{"id":9169,"depth":178,"text":9170},{"id":9180,"depth":178,"text":9181},{"id":9200,"depth":178,"text":9201},{"id":9213,"depth":166,"text":9214},"Python으로 Seedance 2.0 API를 호출하여 AI 비디오를 생성하는 단계별 튜토리얼. 텍스트-투-비디오, 이미지-투-비디오, 비동기 polling, webhook, 오류 처리를 다룹니다.","md",{"date":11285,"image":11286,"seoTitle":11287,"author":11288},"2026-02-27","/s2-hero-api-tutorial.webp","Seedance 2.0 API 튜토리얼: Python으로 첫 AI 비디오 만들기 (2026)","J @ EvoLink","/ko/blog/seedance-2-api-tutorial-python",{"title":5,"description":11282},"ko/blog/seedance-2-api-tutorial-python","0zGZh5NrKkbDv9l6tHExd08fXtLCCWFQZRe2bhQPSRQ",1775067576101]