Spaces:
Running
π§ Fix Text-to-Video Timeout Issues
Browse files## π οΈ Critical Timeout Fixes
- Increased base timeout from 5 to 15 minutes (more realistic)
- Reduced status check interval from 10s to 5s (better UX)
- Fixed timeout multipliers to be less aggressive
## π API Status Handling Improvements
- Added support for Novita's TASK_STATUS_* format
- Handle TASK_STATUS_PROCESSING, TASK_STATUS_SUCCEED correctly
- Improved status mapping for better reliability
## π‘ User Experience Enhancements
- Better timeout estimates and messaging
- Real-time elapsed time display
- More informative status messages
- Clear guidance for users during generation
## π New Timeout Calculations
- Lite models (720p, 5s): ~18 minutes max
- Pro models (1080p, 10s): ~37 minutes max
- More realistic timeouts prevent premature failures
This should resolve the text-to-video timeout issues.
π€ Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- api/novita_client.py +8 -3
- app.py +16 -2
- config.py +7 -7
|
@@ -123,16 +123,21 @@ class NovitaClient:
|
|
| 123 |
task = result.get("task", {})
|
| 124 |
status = task.get("status", "unknown")
|
| 125 |
|
| 126 |
-
|
|
|
|
| 127 |
videos = result.get("videos", [])
|
| 128 |
if videos and videos[0].get("video_url"):
|
| 129 |
return "completed", videos[0]["video_url"]
|
| 130 |
return "completed", None
|
| 131 |
-
elif status
|
| 132 |
reason = task.get("reason", "Generation failed")
|
| 133 |
return "failed", reason
|
|
|
|
|
|
|
| 134 |
else:
|
| 135 |
-
|
|
|
|
|
|
|
| 136 |
elif response.status_code == 404:
|
| 137 |
return "not_found", "Task not found"
|
| 138 |
else:
|
|
|
|
| 123 |
task = result.get("task", {})
|
| 124 |
status = task.get("status", "unknown")
|
| 125 |
|
| 126 |
+
# Handle Novita API status format (TASK_STATUS_*)
|
| 127 |
+
if status in ["TASK_STATUS_SUCCEED", "completed"]:
|
| 128 |
videos = result.get("videos", [])
|
| 129 |
if videos and videos[0].get("video_url"):
|
| 130 |
return "completed", videos[0]["video_url"]
|
| 131 |
return "completed", None
|
| 132 |
+
elif status in ["TASK_STATUS_FAILED", "failed"]:
|
| 133 |
reason = task.get("reason", "Generation failed")
|
| 134 |
return "failed", reason
|
| 135 |
+
elif status in ["TASK_STATUS_PROCESSING", "TASK_STATUS_PENDING", "TASK_STATUS_QUEUED", "processing", "pending", "queued"]:
|
| 136 |
+
return "processing", None
|
| 137 |
else:
|
| 138 |
+
# Return simplified status for display
|
| 139 |
+
simplified_status = status.replace("TASK_STATUS_", "").lower() if status.startswith("TASK_STATUS_") else status
|
| 140 |
+
return simplified_status, None
|
| 141 |
elif response.status_code == 404:
|
| 142 |
return "not_found", "Task not found"
|
| 143 |
else:
|
|
@@ -243,7 +243,10 @@ def generate_and_display_video(api_key, prompt, model_type, resolution, duration
|
|
| 243 |
check_interval = Config.STATUS_CHECK_INTERVAL
|
| 244 |
|
| 245 |
# Display estimated wait time
|
| 246 |
-
|
|
|
|
|
|
|
|
|
|
| 247 |
|
| 248 |
for i in range(0, max_wait_time, check_interval):
|
| 249 |
status, result = check_task_status(api_key, task_id)
|
|
@@ -266,7 +269,18 @@ def generate_and_display_video(api_key, prompt, model_type, resolution, duration
|
|
| 266 |
else:
|
| 267 |
progress = min((i / max_wait_time) * 90, 90) # Show maximum 90%
|
| 268 |
progress_bar.progress(int(progress))
|
| 269 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 270 |
time.sleep(check_interval)
|
| 271 |
else:
|
| 272 |
status_placeholder.warning(f"β° After waiting {max_wait_time // 60} minutes, still not completed. Video may still be generating. Task ID: {task_id}")
|
|
|
|
| 243 |
check_interval = Config.STATUS_CHECK_INTERVAL
|
| 244 |
|
| 245 |
# Display estimated wait time
|
| 246 |
+
estimated_min = max_wait_time // 60
|
| 247 |
+
estimated_sec = max_wait_time % 60
|
| 248 |
+
st.info(f"β±οΈ Maximum wait time: {estimated_min}min {estimated_sec}sec (typically completes in 2-5 minutes)")
|
| 249 |
+
st.info("π‘ Keep this page open - it will automatically update when your video is ready!")
|
| 250 |
|
| 251 |
for i in range(0, max_wait_time, check_interval):
|
| 252 |
status, result = check_task_status(api_key, task_id)
|
|
|
|
| 269 |
else:
|
| 270 |
progress = min((i / max_wait_time) * 90, 90) # Show maximum 90%
|
| 271 |
progress_bar.progress(int(progress))
|
| 272 |
+
|
| 273 |
+
# More user-friendly status messages
|
| 274 |
+
elapsed_min = i // 60
|
| 275 |
+
elapsed_sec = i % 60
|
| 276 |
+
if status == "processing":
|
| 277 |
+
status_msg = f"π¨ Creating your video... ({elapsed_min:02d}:{elapsed_sec:02d})"
|
| 278 |
+
elif status in ["pending", "queued"]:
|
| 279 |
+
status_msg = f"β³ Your video is in queue... ({elapsed_min:02d}:{elapsed_sec:02d})"
|
| 280 |
+
else:
|
| 281 |
+
status_msg = f"π Generating video... ({status}) - {elapsed_min:02d}:{elapsed_sec:02d}"
|
| 282 |
+
|
| 283 |
+
status_placeholder.info(status_msg)
|
| 284 |
time.sleep(check_interval)
|
| 285 |
else:
|
| 286 |
status_placeholder.warning(f"β° After waiting {max_wait_time // 60} minutes, still not completed. Video may still be generating. Task ID: {task_id}")
|
|
@@ -16,15 +16,15 @@ class Config:
|
|
| 16 |
DEFAULT_DURATION = int(os.getenv('DEFAULT_DURATION', '5'))
|
| 17 |
DEFAULT_ASPECT_RATIO = os.getenv('DEFAULT_ASPECT_RATIO', '16:9')
|
| 18 |
|
| 19 |
-
# Timeout Configuration
|
| 20 |
-
BASE_TIMEOUT_SECONDS = int(os.getenv('BASE_TIMEOUT_SECONDS', '
|
| 21 |
-
PRO_MODEL_EXTRA_TIMEOUT = int(os.getenv('PRO_MODEL_EXTRA_TIMEOUT', '
|
| 22 |
-
RESOLUTION_720P_MULTIPLIER = float(os.getenv('RESOLUTION_720P_MULTIPLIER', '1.
|
| 23 |
-
RESOLUTION_1080P_MULTIPLIER = float(os.getenv('RESOLUTION_1080P_MULTIPLIER', '
|
| 24 |
-
LONG_VIDEO_EXTRA_TIMEOUT = int(os.getenv('LONG_VIDEO_EXTRA_TIMEOUT', '
|
| 25 |
|
| 26 |
# API Configuration
|
| 27 |
-
STATUS_CHECK_INTERVAL = int(os.getenv('STATUS_CHECK_INTERVAL', '
|
| 28 |
MAX_RETRIES = int(os.getenv('MAX_RETRIES', '3'))
|
| 29 |
REQUEST_TIMEOUT = int(os.getenv('REQUEST_TIMEOUT', '30'))
|
| 30 |
|
|
|
|
| 16 |
DEFAULT_DURATION = int(os.getenv('DEFAULT_DURATION', '5'))
|
| 17 |
DEFAULT_ASPECT_RATIO = os.getenv('DEFAULT_ASPECT_RATIO', '16:9')
|
| 18 |
|
| 19 |
+
# Timeout Configuration (increased for reliability)
|
| 20 |
+
BASE_TIMEOUT_SECONDS = int(os.getenv('BASE_TIMEOUT_SECONDS', '900')) # 15 minutes base
|
| 21 |
+
PRO_MODEL_EXTRA_TIMEOUT = int(os.getenv('PRO_MODEL_EXTRA_TIMEOUT', '600')) # 10 min extra for Pro
|
| 22 |
+
RESOLUTION_720P_MULTIPLIER = float(os.getenv('RESOLUTION_720P_MULTIPLIER', '1.2'))
|
| 23 |
+
RESOLUTION_1080P_MULTIPLIER = float(os.getenv('RESOLUTION_1080P_MULTIPLIER', '1.5'))
|
| 24 |
+
LONG_VIDEO_EXTRA_TIMEOUT = int(os.getenv('LONG_VIDEO_EXTRA_TIMEOUT', '300')) # 5 min extra for 10s videos
|
| 25 |
|
| 26 |
# API Configuration
|
| 27 |
+
STATUS_CHECK_INTERVAL = int(os.getenv('STATUS_CHECK_INTERVAL', '5')) # Check every 5 seconds
|
| 28 |
MAX_RETRIES = int(os.getenv('MAX_RETRIES', '3'))
|
| 29 |
REQUEST_TIMEOUT = int(os.getenv('REQUEST_TIMEOUT', '30'))
|
| 30 |
|