Files
novafarma/scripts/auto_generate_overnight.py
David Kotnik b0c598d7f0 🌙 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!
2026-01-03 00:25:44 +01:00

253 lines
9.0 KiB
Python

#!/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)