skills/ai-video-production-master/scripts/cost_calculator.py

312 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
AI Video Production Cost Calculator
Compares costs across different approaches:
- Full local (M4 Max)
- Hybrid (local images + cloud I2V)
- Cloud-only (Vast.ai, RunPod)
- SaaS platforms (InVideo, Runway)
"""
import argparse
from dataclasses import dataclass
from enum import Enum
from typing import Optional
class Provider(Enum):
LOCAL_M4_MAX = "local_m4_max"
VASTAI_H100 = "vastai_h100"
RUNPOD_H100 = "runpod_h100"
RUNPOD_A100 = "runpod_a100"
LAMBDA_H100 = "lambda_h100"
INVIDEO_PLUS = "invideo_plus"
INVIDEO_MAX = "invideo_max"
RUNWAY_GEN4 = "runway_gen4"
@dataclass
class ProviderConfig:
name: str
hourly_cost: float # $/hr for cloud, monthly for SaaS
i2v_time_per_clip: float # minutes
image_time: float # seconds per image
monthly_minutes: Optional[int] = None # For SaaS platforms
is_saas: bool = False
is_local: bool = False
PROVIDERS = {
Provider.LOCAL_M4_MAX: ProviderConfig(
name="Local M4 Max (128GB)",
hourly_cost=0.0, # Just electricity
i2v_time_per_clip=90, # 15 min/step × 6 steps
image_time=45, # seconds
is_local=True,
),
Provider.VASTAI_H100: ProviderConfig(
name="Vast.ai H100 80GB",
hourly_cost=1.87,
i2v_time_per_clip=2, # minutes
image_time=5, # seconds
),
Provider.RUNPOD_H100: ProviderConfig(
name="RunPod H100 80GB",
hourly_cost=1.99,
i2v_time_per_clip=2,
image_time=5,
),
Provider.RUNPOD_A100: ProviderConfig(
name="RunPod A100 80GB",
hourly_cost=1.74,
i2v_time_per_clip=3,
image_time=7,
),
Provider.LAMBDA_H100: ProviderConfig(
name="Lambda Labs H100",
hourly_cost=2.99,
i2v_time_per_clip=2,
image_time=5,
),
Provider.INVIDEO_PLUS: ProviderConfig(
name="InVideo Plus",
hourly_cost=20.0, # monthly
i2v_time_per_clip=0.5, # Their pipeline
image_time=0, # Included
monthly_minutes=50,
is_saas=True,
),
Provider.INVIDEO_MAX: ProviderConfig(
name="InVideo Max",
hourly_cost=48.0, # monthly
i2v_time_per_clip=0.5,
image_time=0,
monthly_minutes=200,
is_saas=True,
),
Provider.RUNWAY_GEN4: ProviderConfig(
name="Runway Gen-4",
hourly_cost=0.05, # per second of video
i2v_time_per_clip=0.5,
image_time=0,
is_saas=True,
),
}
def calculate_project_cost(
num_shots: int,
clip_duration: float, # seconds
provider: Provider,
include_images: bool = True,
) -> dict:
"""Calculate total cost for a video project."""
config = PROVIDERS[provider]
result = {
"provider": config.name,
"num_shots": num_shots,
"clip_duration": clip_duration,
"total_video_seconds": num_shots * clip_duration,
}
if config.is_local:
# Local processing - just time cost
image_time = (num_shots * config.image_time) / 60 # minutes
i2v_time = num_shots * config.i2v_time_per_clip # minutes
total_time = image_time + i2v_time
result["image_generation_min"] = round(image_time, 1)
result["i2v_generation_min"] = round(i2v_time, 1)
result["total_time_hours"] = round(total_time / 60, 2)
result["cost"] = 0.0
result["cost_note"] = "Free (electricity ~$0.50)"
elif config.is_saas:
if provider == Provider.RUNWAY_GEN4:
# Runway charges per second
total_seconds = num_shots * clip_duration
result["cost"] = round(total_seconds * config.hourly_cost, 2)
result["cost_note"] = f"${config.hourly_cost}/sec of video"
else:
# InVideo-style - monthly subscription
total_minutes = (num_shots * clip_duration) / 60
if config.monthly_minutes and total_minutes <= config.monthly_minutes:
result["cost"] = config.hourly_cost
result["cost_note"] = f"Monthly subscription (includes {config.monthly_minutes} min)"
result["minutes_used"] = round(total_minutes, 1)
result["minutes_remaining"] = round(config.monthly_minutes - total_minutes, 1)
else:
result["cost"] = config.hourly_cost * 2 # Need higher tier
result["cost_note"] = "Exceeds plan limits"
else:
# Cloud GPU
image_time = (num_shots * config.image_time) / 60 if include_images else 0
i2v_time = num_shots * config.i2v_time_per_clip
total_minutes = image_time + i2v_time
total_hours = total_minutes / 60
# Add 15 min startup overhead
total_hours += 0.25
result["image_generation_min"] = round(image_time, 1)
result["i2v_generation_min"] = round(i2v_time, 1)
result["total_gpu_hours"] = round(total_hours, 2)
result["cost"] = round(total_hours * config.hourly_cost, 2)
result["cost_note"] = f"${config.hourly_cost}/hr"
return result
def calculate_hybrid_cost(
num_shots: int,
clip_duration: float,
cloud_provider: Provider = Provider.VASTAI_H100,
) -> dict:
"""Calculate cost for hybrid approach (local images + cloud I2V)."""
local_config = PROVIDERS[Provider.LOCAL_M4_MAX]
cloud_config = PROVIDERS[cloud_provider]
# Images locally
image_time = (num_shots * local_config.image_time) / 60 # minutes
# I2V on cloud
i2v_time = num_shots * cloud_config.i2v_time_per_clip # minutes
i2v_hours = (i2v_time + 15) / 60 # Add startup overhead
cloud_cost = i2v_hours * cloud_config.hourly_cost
return {
"approach": "Hybrid (Local images + Cloud I2V)",
"num_shots": num_shots,
"clip_duration": clip_duration,
"local_image_time_min": round(image_time, 1),
"cloud_provider": cloud_config.name,
"cloud_i2v_time_min": round(i2v_time, 1),
"cloud_hours": round(i2v_hours, 2),
"cloud_cost": round(cloud_cost, 2),
"total_cost": round(cloud_cost, 2),
"total_time_hours": round((image_time + i2v_time) / 60, 2),
}
def compare_all(num_shots: int, clip_duration: float) -> None:
"""Compare all providers for a given project."""
print(f"\n{'='*70}")
print(f"Cost Comparison: {num_shots} shots × {clip_duration}s = {num_shots * clip_duration}s total video")
print(f"{'='*70}\n")
results = []
# Cloud/Local providers
for provider in [
Provider.LOCAL_M4_MAX,
Provider.VASTAI_H100,
Provider.RUNPOD_H100,
Provider.RUNPOD_A100,
]:
r = calculate_project_cost(num_shots, clip_duration, provider)
results.append(r)
# Hybrid
hybrid = calculate_hybrid_cost(num_shots, clip_duration)
results.append({
"provider": hybrid["approach"],
"cost": hybrid["total_cost"],
"total_time_hours": hybrid["total_time_hours"],
"cost_note": f"via {hybrid['cloud_provider']}",
})
# SaaS
for provider in [Provider.INVIDEO_MAX, Provider.RUNWAY_GEN4]:
r = calculate_project_cost(num_shots, clip_duration, provider)
results.append(r)
# Sort by cost
results.sort(key=lambda x: x.get("cost", 0))
# Display
print(f"{'Provider':<35} {'Cost':>10} {'Time':>12} {'Notes':<25}")
print("-" * 85)
for r in results:
cost_str = f"${r['cost']:.2f}" if r['cost'] > 0 else "FREE"
time_str = f"{r.get('total_time_hours', 'N/A')} hrs" if 'total_time_hours' in r else "Quick"
notes = r.get('cost_note', '')[:25]
print(f"{r['provider']:<35} {cost_str:>10} {time_str:>12} {notes:<25}")
print("\n" + "="*70)
print("RECOMMENDATION:")
# Find best value
cheapest = min([r for r in results if r['cost'] > 0], key=lambda x: x['cost'])
print(f" Best Value: {cheapest['provider']} at ${cheapest['cost']:.2f}")
local = next(r for r in results if 'Local' in r['provider'])
if local['total_time_hours'] < 4:
print(f" Best for Quick Projects: Local M4 Max ({local['total_time_hours']} hrs, free)")
else:
print(f" Local Too Slow: {local['total_time_hours']} hrs - use cloud")
print("="*70)
def monthly_comparison(videos_per_month: int, shots_per_video: int, clip_duration: float) -> None:
"""Compare monthly costs for regular production."""
print(f"\n{'='*70}")
print(f"Monthly Cost: {videos_per_month} videos × {shots_per_video} shots × {clip_duration}s")
print(f"{'='*70}\n")
total_shots = videos_per_month * shots_per_video
total_seconds = total_shots * clip_duration
total_minutes = total_seconds / 60
print(f"Total monthly: {total_shots} shots, {total_minutes:.1f} minutes of video\n")
comparisons = [
("InVideo Plus ($20/mo)", 20 if total_minutes <= 50 else "Exceeds limit"),
("InVideo Max ($48/mo)", 48 if total_minutes <= 200 else "Exceeds limit"),
("Runway Gen-4", round(total_seconds * 0.05, 2)),
("Hybrid (Vast.ai)", round(calculate_hybrid_cost(total_shots, clip_duration)["total_cost"], 2)),
("Full Cloud (Vast.ai)", round(calculate_project_cost(total_shots, clip_duration, Provider.VASTAI_H100)["cost"], 2)),
("Full Local (M4 Max)", "FREE (but slow)"),
]
print(f"{'Approach':<30} {'Monthly Cost':>15}")
print("-" * 50)
for name, cost in comparisons:
cost_str = f"${cost}" if isinstance(cost, (int, float)) else cost
print(f"{name:<30} {cost_str:>15}")
def main():
parser = argparse.ArgumentParser(description="AI Video Production Cost Calculator")
parser.add_argument("--shots", type=int, default=10, help="Number of shots/clips")
parser.add_argument("--duration", type=float, default=5.0, help="Duration per clip (seconds)")
parser.add_argument("--monthly", type=int, help="Videos per month (for monthly comparison)")
parser.add_argument("--provider", type=str, help="Specific provider to calculate")
args = parser.parse_args()
if args.monthly:
monthly_comparison(args.monthly, args.shots, args.duration)
elif args.provider:
try:
provider = Provider[args.provider.upper()]
result = calculate_project_cost(args.shots, args.duration, provider)
print(f"\n{result['provider']}:")
for k, v in result.items():
if k != 'provider':
print(f" {k}: {v}")
except KeyError:
print(f"Unknown provider: {args.provider}")
print(f"Available: {[p.name for p in Provider]}")
else:
compare_all(args.shots, args.duration)
if __name__ == "__main__":
main()