🌙 OVERNIGHT AUTOMATION READY
Progress: 254/14,000 assets (1.81%) Dino Valley Status: 91/109 complete - ✅ Vegetation: 15/15 - ✅ Dinosaurs: 12/12 - ✅ Items: 12/12 - ✅ Food: 16/16 - ✅ Clothing: 8/8 - ✅ Tools: 10/10 - ✅ Props: 8/8 - ✅ Buildings: 4/4 - ✅ Terrain: 4/4 - 🔄 Special Creatures: 2/10 (quota hit) New Files: - scripts/auto_generate_overnight.py - OVERNIGHT_AUTO_NAVODILA.md - ASSET_STATUS_03_01_2026.md Quota exhausted at 00:22 Reset at: 01:40 CET Ready for overnight automation!
This commit is contained in:
252
scripts/auto_generate_overnight.py
Normal file
252
scripts/auto_generate_overnight.py
Normal file
@@ -0,0 +1,252 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
OVERNIGHT AUTOMATION SCRIPT - Dino Valley & Beyond
|
||||
Waits for quota reset, then automatically generates remaining assets
|
||||
"""
|
||||
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import requests
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
import base64
|
||||
|
||||
# ========================================
|
||||
# CONFIGURATION
|
||||
# ========================================
|
||||
|
||||
PROJECT_ROOT = Path("/Users/davidkotnik/repos/novafarma")
|
||||
ASSET_ROOT = PROJECT_ROOT / "assets/slike"
|
||||
LOG_FILE = PROJECT_ROOT / "auto_generation_log.txt"
|
||||
|
||||
# Vertex AI Configuration
|
||||
PROJECT_ID = "gen-lang-client-0428644398"
|
||||
LOCATION = "us-central1"
|
||||
MODEL = "imagen-3.0-generate-001"
|
||||
|
||||
# Quota reset time (from error message)
|
||||
QUOTA_RESET_TIME = datetime.fromisoformat("2026-01-03T01:40:39+01:00")
|
||||
|
||||
# Generation limits
|
||||
MAX_IMAGES_PER_HOUR = 60 # Conservative (actual is 60/min)
|
||||
BATCH_SIZE = 10 # Generate in batches for checkpointing
|
||||
DELAY_BETWEEN_IMAGES = 1.2 # Seconds (safe rate limiting)
|
||||
|
||||
# Style definitions
|
||||
STYLE_33_ENTITIES = """exact same art style as reference. Very thick black outlines (4-5px), flat colors NO gradients, pastel colors with dark gothic accents, NO pupils (white eyes), cute-dark aesthetic, clean vector cartoon"""
|
||||
|
||||
STYLE_30_VEGETATION = """exact Garden Story cozy art style. Soft rounded shapes, gentle pastel colors, NO thick outlines (thin 1-2px), watercolor-like soft shading, cute botanical illustration, clean cozy plant art"""
|
||||
|
||||
# ========================================
|
||||
# REMAINING DINO VALLEY ASSETS (18)
|
||||
# ========================================
|
||||
|
||||
DINO_VALLEY_REMAINING = {
|
||||
"creatures": [
|
||||
{
|
||||
"name": "boss_thunder_raptor",
|
||||
"prompt": "Thunder Raptor boss in {STYLE}. Pastel purple/blue raptor with lightning patterns, large empty white eyes NO pupils, dark gothic electric accents. 3/4 angle. Chroma green background (#00FF00)."
|
||||
},
|
||||
{
|
||||
"name": "dino_nest_eggs_special",
|
||||
"prompt": "Special glowing dinosaur nest in {STYLE}. Pastel brown nest with magical glowing eggs, dark gothic mystical aura. 3/4 angle. Chroma green background (#00FF00)."
|
||||
},
|
||||
{
|
||||
"name": "zombie_prehistoric",
|
||||
"prompt": "Prehistoric zombie caveman in {STYLE}. Pastel green/gray decayed caveman, torn tribal clothes, large empty white eyes NO pupils, dark gothic rot. 3/4 angle. Chroma green background (#00FF00)."
|
||||
},
|
||||
{
|
||||
"name": "zombie_dino_raptor",
|
||||
"prompt": "Zombie raptor dinosaur in {STYLE}. Pastel gray/green undead raptor with exposed bones, large empty white eyes NO pupils, dark gothic decay. 3/4 angle. Chroma green background (#00FF00)."
|
||||
},
|
||||
{
|
||||
"name": "zombie_bone",
|
||||
"prompt": "Skeleton zombie in {STYLE}. White/cream animated skeleton warrior, large empty eye sockets, dark gothic cracks. 3/4 angle. Chroma green background (#00FF00)."
|
||||
},
|
||||
{
|
||||
"name": "zombie_tar",
|
||||
"prompt": "Tar zombie in {STYLE}. Black sticky tar monster humanoid, glowing pastel orange eyes, dark gothic dripping texture. 3/4 angle. Chroma green background (#00FF00)."
|
||||
},
|
||||
{
|
||||
"name": "npc_tribal_hunter",
|
||||
"prompt": "Tribal hunter NPC in {STYLE}. Pastel brown skinned caveman with bone spear, tribal tattoos, large empty white eyes NO pupils, dark gothic war paint, friendly stance. 3/4 angle. Chroma green background (#00FF00)."
|
||||
},
|
||||
{
|
||||
"name": "npc_shaman",
|
||||
"prompt": "Tribal shaman NPC in {STYLE}. Elderly caveman with bone staff and feather headdress, pastel skin, large empty white eyes NO pupils, dark gothic mystical symbols, wise pose. 3/4 angle. Chroma green background (#00FF00)."
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
# ========================================
|
||||
# UTILITY FUNCTIONS
|
||||
# ========================================
|
||||
|
||||
def log(message):
|
||||
"""Log message to file and console"""
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
log_msg = f"[{timestamp}] {message}"
|
||||
print(log_msg)
|
||||
with open(LOG_FILE, 'a') as f:
|
||||
f.write(log_msg + "\n")
|
||||
|
||||
def wait_for_quota_reset():
|
||||
"""Wait until quota resets"""
|
||||
now = datetime.now()
|
||||
wait_seconds = (QUOTA_RESET_TIME - now).total_seconds()
|
||||
|
||||
if wait_seconds > 0:
|
||||
log(f"⏰ Waiting for quota reset at {QUOTA_RESET_TIME.strftime('%H:%M')}")
|
||||
log(f" Sleeping for {int(wait_seconds/60)} minutes...")
|
||||
time.sleep(wait_seconds + 10) # Add 10 sec buffer
|
||||
log("✅ Quota should be reset now!")
|
||||
else:
|
||||
log("✅ Quota already reset!")
|
||||
|
||||
def get_access_token():
|
||||
"""Get Google Cloud access token"""
|
||||
import subprocess
|
||||
result = subprocess.run(
|
||||
['gcloud', 'auth', 'print-access-token'],
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
return result.stdout.strip()
|
||||
|
||||
def generate_image_vertex(prompt, output_path):
|
||||
"""Generate image using Vertex AI Imagen 3.0"""
|
||||
|
||||
access_token = get_access_token()
|
||||
|
||||
url = f"https://{LOCATION}-aiplatform.googleapis.com/v1/projects/{PROJECT_ID}/locations/{LOCATION}/publishers/google/models/{MODEL}:predict"
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {access_token}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
payload = {
|
||||
"instances": [
|
||||
{
|
||||
"prompt": prompt
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"sampleCount": 1,
|
||||
"aspectRatio": "1:1",
|
||||
"safetyFilterLevel": "block_few",
|
||||
"personGeneration": "allow_all"
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(url, headers=headers, json=payload, timeout=60)
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json()
|
||||
|
||||
# Extract and save image
|
||||
if "predictions" in result and len(result["predictions"]) > 0:
|
||||
image_data = result["predictions"][0]["bytesBase64Encoded"]
|
||||
image_bytes = base64.b64decode(image_data)
|
||||
|
||||
# Save to file
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(output_path, 'wb') as f:
|
||||
f.write(image_bytes)
|
||||
|
||||
log(f" ✅ Saved: {output_path.name}")
|
||||
return True
|
||||
else:
|
||||
log(f" ❌ No image in response")
|
||||
return False
|
||||
|
||||
except requests.exceptions.HTTPError as e:
|
||||
if e.response.status_code == 429:
|
||||
log(f" ⚠️ Quota exhausted again, waiting 5 minutes...")
|
||||
time.sleep(300)
|
||||
return False
|
||||
else:
|
||||
log(f" ❌ HTTP Error: {e}")
|
||||
return False
|
||||
except Exception as e:
|
||||
log(f" ❌ Error: {str(e)}")
|
||||
return False
|
||||
|
||||
def git_commit_checkpoint(message):
|
||||
"""Create git commit checkpoint"""
|
||||
import subprocess
|
||||
try:
|
||||
subprocess.run(['git', 'add', '-A'], cwd=PROJECT_ROOT, check=True)
|
||||
subprocess.run(['git', 'commit', '-m', message], cwd=PROJECT_ROOT, check=True)
|
||||
log(f" 📦 Git commit: {message}")
|
||||
except Exception as e:
|
||||
log(f" ⚠️ Git commit failed: {e}")
|
||||
|
||||
# ========================================
|
||||
# MAIN GENERATION LOGIC
|
||||
# ========================================
|
||||
|
||||
def generate_dino_valley_remaining():
|
||||
"""Generate remaining Dino Valley assets"""
|
||||
|
||||
log("🦖 Starting Dino Valley completion...")
|
||||
|
||||
total = len(DINO_VALLEY_REMAINING["creatures"])
|
||||
completed = 0
|
||||
|
||||
for asset in DINO_VALLEY_REMAINING["creatures"]:
|
||||
name = asset["name"]
|
||||
prompt_template = asset["prompt"]
|
||||
|
||||
# Use Style 33 for entities
|
||||
prompt = prompt_template.replace("{STYLE}", STYLE_33_ENTITIES)
|
||||
|
||||
output_path = ASSET_ROOT / "biomes/dino_valley/creatures" / f"{name}.png"
|
||||
|
||||
# Skip if already exists
|
||||
if output_path.exists():
|
||||
log(f"⏭️ Skipping {name} (already exists)")
|
||||
completed += 1
|
||||
continue
|
||||
|
||||
log(f"🖼️ Generating {name} ({completed+1}/{total})")
|
||||
|
||||
success = generate_image_vertex(prompt, output_path)
|
||||
|
||||
if success:
|
||||
completed += 1
|
||||
|
||||
# Checkpoint every 5 images
|
||||
if completed % 5 == 0:
|
||||
git_commit_checkpoint(f"🦖 Dino Valley auto: {completed}/{total} creatures")
|
||||
|
||||
# Rate limiting
|
||||
time.sleep(DELAY_BETWEEN_IMAGES)
|
||||
|
||||
log(f"✅ Dino Valley complete! {completed}/{total} assets generated")
|
||||
git_commit_checkpoint(f"🎉 DINO VALLEY COMPLETE - All {completed} creatures done!")
|
||||
|
||||
return completed
|
||||
|
||||
# ========================================
|
||||
# MAIN EXECUTION
|
||||
# ========================================
|
||||
|
||||
if __name__ == "__main__":
|
||||
log("="*60)
|
||||
log("🚀 OVERNIGHT AUTOMATION STARTED")
|
||||
log("="*60)
|
||||
|
||||
# Wait for quota reset
|
||||
wait_for_quota_reset()
|
||||
|
||||
# Generate remaining Dino Valley
|
||||
dino_count = generate_dino_valley_remaining()
|
||||
|
||||
log("="*60)
|
||||
log(f"✅ AUTOMATION COMPLETE!")
|
||||
log(f"📊 Total generated: {dino_count} assets")
|
||||
log(f"💰 Estimated cost: €{dino_count * 0.40:.2f}")
|
||||
log("="*60)
|
||||
Reference in New Issue
Block a user