Files changed (1) hide show
  1. app.py +13 -282
app.py CHANGED
@@ -1,18 +1,20 @@
1
- # app.py
 
2
  import os
3
- import oss2
4
  import sys
5
- import uuid
6
- import shutil
7
  import time
8
  import gradio as gr
9
  import requests
10
-
11
  import dashscope
12
  from dashscope.utils.oss_utils import check_and_upload_local
13
 
 
14
  DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY")
15
- dashscope.api_key = DASHSCOPE_API_KEY
 
 
 
 
16
 
17
 
18
  class WanAnimateApp:
@@ -20,280 +22,9 @@ class WanAnimateApp:
20
  self.url = url
21
  self.get_url = get_url
22
 
23
- def predict(
24
- self,
25
- ref_img,
26
- video,
27
- model_id,
28
- model,
29
- ):
30
- # Upload files to OSS if needed and get URLs
31
- _, image_url = check_and_upload_local(model_id, ref_img, DASHSCOPE_API_KEY)
32
- _, video_url = check_and_upload_local(model_id, video, DASHSCOPE_API_KEY)
33
-
34
- # Prepare the request payload
35
- payload = {
36
- "model": model_id,
37
- "input": {
38
- "image_url": image_url,
39
- "video_url": video_url
40
- },
41
- "parameters": {
42
- "check_image": True,
43
- "mode": model,
44
- }
45
- }
46
-
47
- # Set up headers
48
- headers = {
49
- "X-DashScope-Async": "enable",
50
- "X-DashScope-OssResourceResolve": "enable",
51
- "Authorization": f"Bearer {DASHSCOPE_API_KEY}",
52
- "Content-Type": "application/json"
53
- }
54
-
55
- # Make the initial API request
56
- url = self.url
57
- response = requests.post(url, json=payload, headers=headers)
58
-
59
- # Check if request was successful
60
- if response.status_code != 200:
61
- raise Exception(f"Initial request failed with status code {response.status_code}: {response.text}")
62
-
63
- # Get the task ID from response
64
- result = response.json()
65
- task_id = result.get("output", {}).get("task_id")
66
- if not task_id:
67
- raise Exception("Failed to get task ID from response")
68
-
69
- # Poll for results
70
- get_url = f"{self.get_url}/{task_id}"
71
- headers = {
72
- "Authorization": f"Bearer {DASHSCOPE_API_KEY}",
73
- "Content-Type": "application/json"
74
- }
75
-
76
- while True:
77
- response = requests.get(get_url, headers=headers)
78
- if response.status_code != 200:
79
- raise Exception(f"Failed to get task status: {response.status_code}: {response.text}")
80
-
81
- result = response.json()
82
- print(result)
83
- task_status = result.get("output", {}).get("task_status")
84
-
85
- if task_status == "SUCCEEDED":
86
- # Task completed successfully, return video URL
87
- video_url = result["output"]["results"]["video_url"]
88
- return video_url, "SUCCEEDED"
89
- elif task_status == "FAILED":
90
- # Task failed, raise an exception with error message
91
- error_msg = result.get("output", {}).get("message", "Unknown error")
92
- code_msg = result.get("output", {}).get("code", "Unknown code")
93
- print(f"\n\nTask failed: {error_msg} Code: {code_msg} TaskId: {task_id}\n\n")
94
- return None, f"Task failed: {error_msg} Code: {code_msg} TaskId: {task_id}"
95
- # raise Exception(f"Task failed: {error_msg} TaskId: {task_id}")
96
- else:
97
- # Task is still running, wait and retry
98
- time.sleep(5) # Wait 5 seconds before polling again
99
-
100
- def start_app():
101
- import argparse
102
- parser = argparse.ArgumentParser(description="Wan2.2-Animate 视频生成工具")
103
- args = parser.parse_args()
104
-
105
- url = "https://dashscope.aliyuncs.com/api/v1/services/aigc/image2video/video-synthesis/"
106
- # url = "https://poc-dashscope.aliyuncs.com/api/v1/services/aigc/image2video/video-synthesis"
107
-
108
- get_url = f"https://dashscope.aliyuncs.com/api/v1/tasks/"
109
- # get_url = f"https://poc-dashscope.aliyuncs.com/api/v1/tasks"
110
- app = WanAnimateApp(url=url, get_url=get_url)
111
-
112
- with gr.Blocks(title="Wan2.2-Animate 视频生成") as demo:
113
- gr.HTML("""
114
-
115
-
116
- <div style="padding: 2rem; text-align: center; max-width: 1200px; margin: 0 auto; font-family: Arial, sans-serif;">
117
-
118
- <h1 style="font-size: 2.5rem; font-weight: bold; margin-bottom: 0.5rem; color: #333;">
119
- Wan2.2-Animate: Unified Character Animation and Replacement with Holistic Replication
120
- </h1>
121
-
122
- <h3 style="font-size: 2.5rem; font-weight: bold; margin-bottom: 0.5rem; color: #333;">
123
- Wan2.2-Animate: 统一的角色动画和视频人物替换模型
124
- </h3>
125
-
126
- <div style="font-size: 1.25rem; margin-bottom: 1.5rem; color: #555;">
127
- Tongyi Lab, Alibaba
128
- </div>
129
-
130
- <div style="display: flex; flex-wrap: wrap; justify-content: center; gap: 1rem; margin-bottom: 1rem;">
131
- <!-- 第一行按钮 -->
132
- <a href="https://arxiv.org/abs/2509.14055" target="_blank"
133
- style="display: inline-flex; align-items: center; padding: 0.5rem 1rem; background-color: #f0f0f0; /* 浅灰色背景 */ color: #333; /* 深色文字 */ text-decoration: none; border-radius: 9999px; font-weight: 500; transition: background-color 0.3s;">
134
- <span style="margin-right: 0.5rem;">📄</span> <!-- 使用文档图标 -->
135
- <span>Paper</span>
136
- </a>
137
-
138
- <a href="https://github.com/Wan-Video/Wan2.2" target="_blank"
139
- style="display: inline-flex; align-items: center; padding: 0.5rem 1rem; background-color: #f0f0f0; color: #333; text-decoration: none; border-radius: 9999px; font-weight: 500; transition: background-color 0.3s;">
140
- <span style="margin-right: 0.5rem;">💻</span> <!-- 使用电脑图标 -->
141
- <span>GitHub</span>
142
- </a>
143
-
144
- <a href="https://huggingface.co/Wan-AI/Wan2.2-Animate-14B" target="_blank"
145
- style="display: inline-flex; align-items: center; padding: 0.5rem 1rem; background-color: #f0f0f0; color: #333; text-decoration: none; border-radius: 9999px; font-weight: 500; transition: background-color 0.3s;">
146
- <span style="margin-right: 0.5rem;">🤗</span>
147
- <span>HF Model</span>
148
- </a>
149
-
150
- <a href="https://www.modelscope.cn/models/Wan-AI/Wan2.2-Animate-14B" target="_blank"
151
- style="display: inline-flex; align-items: center; padding: 0.5rem 1rem; background-color: #f0f0f0; color: #333; text-decoration: none; border-radius: 9999px; font-weight: 500; transition: background-color 0.3s;">
152
- <span style="margin-right: 0.5rem;">🤖</span>
153
- <span>MS Model</span>
154
- </a>
155
- </div>
156
-
157
- <div style="display: flex; flex-wrap: wrap; justify-content: center; gap: 1rem;">
158
- <!-- 第二行按钮 -->
159
- <a href="https://huggingface.co/spaces/Wan-AI/Wan2.2-Animate" target="_blank"
160
- style="display: inline-flex; align-items: center; padding: 0.5rem 1rem; background-color: #f0f0f0; color: #333; text-decoration: none; border-radius: 9999px; font-weight: 500; transition: background-color 0.3s;">
161
- <span style="margin-right: 0.5rem;">🤗</span>
162
- <span>HF Space</span>
163
- </a>
164
-
165
- <a href="https://www.modelscope.cn/studios/Wan-AI/Wan2.2-Animate" target="_blank"
166
- style="display: inline-flex; align-items: center; padding: 0.5rem 1rem; background-color: #f0f0f0; color: #333; text-decoration: none; border-radius: 9999px; font-weight: 500; transition: background-color 0.3s;">
167
- <span style="margin-right: 0.5rem;">🤖</span>
168
- <span>MS Studio</span>
169
- </a>
170
- </div>
171
-
172
- </div>
173
-
174
- """)
175
-
176
- gr.HTML("""
177
- <details>
178
- <summary>‼️Usage (使用说明)</summary>
179
-
180
- Wan-Animate supports two mode:
181
- <ul>
182
- <li>Move Mode: animate the character in input image with movements from the input video</li>
183
- <li>Mix Mode: replace the character in input video with the character in input image</li>
184
- </ul>
185
-
186
- Wan-Animate 支持两种模式:
187
- <ul>
188
- <li>Move模式: 用输入视频中提取的动作,驱动输入图片中的角色</li>
189
- <li>Mix模式: 用输入图片中的角色,替换输入视频中的角色</li>
190
- </ul>
191
-
192
- Currently, the following restrictions apply to inputs:
193
-
194
- <ul> <li>Video file size: Less than 200MB</li>
195
- <li>Video resolution: The shorter side must be greater than 200, and the longer side must be less than 2048</li>
196
- <li>Video duration: 2s to 30s</li>
197
- <li>Video aspect ratio: 1:3 to 3:1</li>
198
- <li>Video formats: mp4, avi, mov</li>
199
- <li>Image file size: Less than 5MB</li>
200
- <li>Image resolution: The shorter side must be greater than 200, and the longer side must be less than 4096</li>
201
- <li>Image formats: jpg, png, jpeg, webp, bmp</li> </ul>
202
-
203
-
204
- 当前,对于��入有以下的限制
205
-
206
- <ul>
207
- <li>视频文件大小: 小于 200MB</li>
208
- <li>视频分辨率: 最小边大于 200, 最大边小于2048</li>
209
- <li>视频时长: 2s ~ 30s </li>
210
- <li>视频比例:1:3 ~ 3:1 </li>
211
- <li>视频格式: mp4, avi, mov </li>
212
- <li>图片文件大小: 小于5MB </li>
213
- <li>图片分辨率:最小边大于200,最大边小于4096 </li>
214
- <li>图片格式: jpg, png, jpeg, webp, bmp </li>
215
- </ul>
216
-
217
- <p> Currently, the inference quality has two variants. You can use our open-source code for more flexible configuration. </p>
218
-
219
- <p>当前,推理质量有两个变种。 您可以使用我们的开源代码,来进行更灵活的设置。</p>
220
-
221
- <ul>
222
- <li> wan-pro: 25fps, 720p </li>
223
- <li> wan-std: 15fps, 720p </li>
224
- </ul>
225
-
226
-
227
- </details>
228
- """)
229
-
230
- with gr.Row():
231
- with gr.Column():
232
- ref_img = gr.Image(
233
- label="Reference Image(参考图像)",
234
- type="filepath",
235
- sources=["upload"],
236
- )
237
-
238
- video = gr.Video(
239
- label="Template Video(模版视频)",
240
- sources=["upload"],
241
- )
242
-
243
- with gr.Row():
244
- model_id = gr.Dropdown(
245
- label="Mode(模式)",
246
- choices=["wan2.2-animate-move", "wan2.2-animate-mix"],
247
- value="wan2.2-animate-move",
248
- info=""
249
- )
250
-
251
- model = gr.Dropdown(
252
- label="推理质量(Inference Quality)",
253
- choices=["wan-pro", "wan-std"],
254
- value="wan-pro",
255
- )
256
-
257
- run_button = gr.Button("Generate Video(生成视频)")
258
-
259
- with gr.Column():
260
- output_video = gr.Video(label="Output Video(输出视频)")
261
- output_status = gr.Textbox(label="Status(状态)")
262
-
263
- run_button.click(
264
- fn=app.predict,
265
- inputs=[
266
- ref_img,
267
- video,
268
- model_id,
269
- model,
270
- ],
271
- outputs=[output_video, output_status],
272
- )
273
-
274
- example_data = [
275
- ['./examples/mov/1/1.jpeg', './examples/mov/1/1.mp4', 'wan2.2-animate-move', 'wan-pro'],
276
- ['./examples/mov/2/2.jpeg', './examples/mov/2/2.mp4', 'wan2.2-animate-move', 'wan-pro'],
277
- ['./examples/mix/1/1.jpeg', './examples/mix/1/1.mp4', 'wan2.2-animate-mix', 'wan-pro'],
278
- ['./examples/mix/2/2.jpeg', './examples/mix/2/2.mp4', 'wan2.2-animate-mix', 'wan-pro']
279
- ]
280
-
281
- if example_data:
282
- gr.Examples(
283
- examples=example_data,
284
- inputs=[ref_img, video, model_id, model],
285
- outputs=[output_video, output_status],
286
- fn=app.predict,
287
- cache_examples="lazy",
288
- )
289
-
290
- demo.queue(default_concurrency_limit=100)
291
-
292
- demo.launch(
293
- server_name="0.0.0.0",
294
- server_port=7860
295
- )
296
-
297
 
298
- if __name__ == "__main__":
299
- start_app()
 
1
+ # app.py (updated to commit cfe7995993fd938b55a636e62824f08993bb9d53)
2
+
3
  import os
 
4
  import sys
 
 
5
  import time
6
  import gradio as gr
7
  import requests
 
8
  import dashscope
9
  from dashscope.utils.oss_utils import check_and_upload_local
10
 
11
+ # === 環境変数とAPIキーの設定 ===
12
  DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY")
13
+
14
+ if not DASHSCOPE_API_KEY:
15
+ print("⚠️ Warning: DASHSCOPE_API_KEY is not set. Please set it in your Hugging Face Space settings.")
16
+ else:
17
+ dashscope.api_key = DASHSCOPE_API_KEY
18
 
19
 
20
  class WanAnimateApp:
 
22
  self.url = url
23
  self.get_url = get_url
24
 
25
+ def predict(self, ref_img, video, model_id, model):
26
+ if not DASHSCOPE_API_KEY:
27
+ return None, "❌ Error: DASHSCOPE_API_KEY not found. Please set your key in environment variables."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
+ # === Upload files to OSS ===
30
+ _, image_url = check_and_upload