Update app_enhanced.py
Browse files- app_enhanced.py +40 -8
app_enhanced.py
CHANGED
|
@@ -11,10 +11,43 @@ import random
|
|
| 11 |
from concurrent.futures import ThreadPoolExecutor
|
| 12 |
from flask import Flask, render_template, request, jsonify, send_from_directory, send_file
|
| 13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
# --- 0. CONFIG & LOGGING ---
|
| 15 |
logging.basicConfig(level=logging.INFO)
|
| 16 |
logger = logging.getLogger(__name__)
|
| 17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
# --- 1. CORE DEPENDENCY CHECKS ---
|
| 19 |
try:
|
| 20 |
import cv2
|
|
@@ -98,12 +131,6 @@ except Exception as e:
|
|
| 98 |
|
| 99 |
# --- FLASK APP SETUP ---
|
| 100 |
app = Flask(__name__)
|
| 101 |
-
BASE_USER_DIR = "userdata"
|
| 102 |
-
SAVED_COMICS_DIR = "saved_comics"
|
| 103 |
-
|
| 104 |
-
# Create directories
|
| 105 |
-
os.makedirs(BASE_USER_DIR, exist_ok=True)
|
| 106 |
-
os.makedirs(SAVED_COMICS_DIR, exist_ok=True)
|
| 107 |
|
| 108 |
def generate_save_code(length=8):
|
| 109 |
"""Generate a unique save code"""
|
|
@@ -1361,6 +1388,8 @@ class EnhancedComicGenerator:
|
|
| 1361 |
|
| 1362 |
return pages
|
| 1363 |
|
|
|
|
|
|
|
| 1364 |
def generate_comic(self):
|
| 1365 |
start_time = time.time()
|
| 1366 |
try:
|
|
@@ -1451,6 +1480,7 @@ class EnhancedComicGenerator:
|
|
| 1451 |
except Exception as e:
|
| 1452 |
print(f"❌ Save results failed: {e}")
|
| 1453 |
|
|
|
|
| 1454 |
def regenerate_frame(self, fname, direction):
|
| 1455 |
try:
|
| 1456 |
if not os.path.exists(self.metadata_path):
|
|
@@ -1506,6 +1536,7 @@ class EnhancedComicGenerator:
|
|
| 1506 |
traceback.print_exc()
|
| 1507 |
return {"success": False, "message": str(e)}
|
| 1508 |
|
|
|
|
| 1509 |
def get_frame_at_timestamp(self, fname, ts):
|
| 1510 |
try:
|
| 1511 |
cap = cv2.VideoCapture(self.video_path)
|
|
@@ -1573,6 +1604,7 @@ def upload():
|
|
| 1573 |
f.save(gen.video_path)
|
| 1574 |
gen.update_status("Starting...", 5)
|
| 1575 |
|
|
|
|
| 1576 |
threading.Thread(target=gen.generate_comic).start()
|
| 1577 |
return jsonify({'success': True, 'message': 'Generation started.'})
|
| 1578 |
|
|
@@ -1609,6 +1641,7 @@ def regen():
|
|
| 1609 |
|
| 1610 |
d = request.get_json()
|
| 1611 |
gen = EnhancedComicGenerator(sid)
|
|
|
|
| 1612 |
return jsonify(gen.regenerate_frame(d['filename'], d['direction']))
|
| 1613 |
|
| 1614 |
@app.route('/goto_timestamp', methods=['POST'])
|
|
@@ -1730,8 +1763,7 @@ def get_saved_frame(code, filename):
|
|
| 1730 |
|
| 1731 |
|
| 1732 |
if __name__ == '__main__':
|
| 1733 |
-
|
| 1734 |
-
os.makedirs(SAVED_COMICS_DIR, exist_ok=True)
|
| 1735 |
port = int(os.getenv("PORT", 7860))
|
| 1736 |
print(f"🚀 Starting Enhanced Comic Generator on host 0.0.0.0, port {port}")
|
| 1737 |
print(f"📁 User data directory: {BASE_USER_DIR}")
|
|
|
|
| 11 |
from concurrent.futures import ThreadPoolExecutor
|
| 12 |
from flask import Flask, render_template, request, jsonify, send_from_directory, send_file
|
| 13 |
|
| 14 |
+
# --- HUGGING FACE ZEROGPU SETUP ---
|
| 15 |
+
try:
|
| 16 |
+
import spaces
|
| 17 |
+
print("✅ Hugging Face Spaces 'spaces' module detected.")
|
| 18 |
+
HAS_ZEROGPU = True
|
| 19 |
+
except ImportError:
|
| 20 |
+
print("⚠️ 'spaces' module not found. Running in standard mode (CPU/GPU without ZeroGPU allocator).")
|
| 21 |
+
HAS_ZEROGPU = False
|
| 22 |
+
|
| 23 |
+
# Conditional Decorator for ZeroGPU
|
| 24 |
+
def gpu_task(duration=120):
|
| 25 |
+
def decorator(func):
|
| 26 |
+
if HAS_ZEROGPU:
|
| 27 |
+
return spaces.GPU(duration=duration)(func)
|
| 28 |
+
return func
|
| 29 |
+
return decorator
|
| 30 |
+
|
| 31 |
# --- 0. CONFIG & LOGGING ---
|
| 32 |
logging.basicConfig(level=logging.INFO)
|
| 33 |
logger = logging.getLogger(__name__)
|
| 34 |
|
| 35 |
+
# --- HUGGING FACE FILESYSTEM SETUP ---
|
| 36 |
+
# Spaces are read-only in root. We must use /tmp or /data (persistent storage)
|
| 37 |
+
if os.access('/data', os.W_OK):
|
| 38 |
+
BASE_PATH = '/data'
|
| 39 |
+
print("✅ Using Persistent Storage at /data")
|
| 40 |
+
else:
|
| 41 |
+
BASE_PATH = '/tmp'
|
| 42 |
+
print("✅ Using Temporary Storage at /tmp")
|
| 43 |
+
|
| 44 |
+
BASE_USER_DIR = os.path.join(BASE_PATH, "userdata")
|
| 45 |
+
SAVED_COMICS_DIR = os.path.join(BASE_PATH, "saved_comics")
|
| 46 |
+
|
| 47 |
+
# Create directories
|
| 48 |
+
os.makedirs(BASE_USER_DIR, exist_ok=True)
|
| 49 |
+
os.makedirs(SAVED_COMICS_DIR, exist_ok=True)
|
| 50 |
+
|
| 51 |
# --- 1. CORE DEPENDENCY CHECKS ---
|
| 52 |
try:
|
| 53 |
import cv2
|
|
|
|
| 131 |
|
| 132 |
# --- FLASK APP SETUP ---
|
| 133 |
app = Flask(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
|
| 135 |
def generate_save_code(length=8):
|
| 136 |
"""Generate a unique save code"""
|
|
|
|
| 1388 |
|
| 1389 |
return pages
|
| 1390 |
|
| 1391 |
+
# --- APPLY ZEROGPU DECORATOR HERE ---
|
| 1392 |
+
@gpu_task(duration=180) # Allocate GPU for 3 minutes for this task
|
| 1393 |
def generate_comic(self):
|
| 1394 |
start_time = time.time()
|
| 1395 |
try:
|
|
|
|
| 1480 |
except Exception as e:
|
| 1481 |
print(f"❌ Save results failed: {e}")
|
| 1482 |
|
| 1483 |
+
@gpu_task(duration=60)
|
| 1484 |
def regenerate_frame(self, fname, direction):
|
| 1485 |
try:
|
| 1486 |
if not os.path.exists(self.metadata_path):
|
|
|
|
| 1536 |
traceback.print_exc()
|
| 1537 |
return {"success": False, "message": str(e)}
|
| 1538 |
|
| 1539 |
+
@gpu_task(duration=60)
|
| 1540 |
def get_frame_at_timestamp(self, fname, ts):
|
| 1541 |
try:
|
| 1542 |
cap = cv2.VideoCapture(self.video_path)
|
|
|
|
| 1604 |
f.save(gen.video_path)
|
| 1605 |
gen.update_status("Starting...", 5)
|
| 1606 |
|
| 1607 |
+
# We use a Thread to call the generated GPU function
|
| 1608 |
threading.Thread(target=gen.generate_comic).start()
|
| 1609 |
return jsonify({'success': True, 'message': 'Generation started.'})
|
| 1610 |
|
|
|
|
| 1641 |
|
| 1642 |
d = request.get_json()
|
| 1643 |
gen = EnhancedComicGenerator(sid)
|
| 1644 |
+
# This might use ZeroGPU if configured
|
| 1645 |
return jsonify(gen.regenerate_frame(d['filename'], d['direction']))
|
| 1646 |
|
| 1647 |
@app.route('/goto_timestamp', methods=['POST'])
|
|
|
|
| 1763 |
|
| 1764 |
|
| 1765 |
if __name__ == '__main__':
|
| 1766 |
+
# HF Spaces use port 7860 by default
|
|
|
|
| 1767 |
port = int(os.getenv("PORT", 7860))
|
| 1768 |
print(f"🚀 Starting Enhanced Comic Generator on host 0.0.0.0, port {port}")
|
| 1769 |
print(f"📁 User data directory: {BASE_USER_DIR}")
|