Spaces:
Paused
Paused
Update app_relay2.py
Browse files- app_relay2.py +94 -63
app_relay2.py
CHANGED
|
@@ -1,12 +1,10 @@
|
|
| 1 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 2 |
-
# π§
SODA-GEN ANONYMOUS RELAY V1.
|
| 3 |
#
|
| 4 |
-
#
|
| 5 |
-
#
|
| 6 |
-
#
|
| 7 |
-
#
|
| 8 |
-
#
|
| 9 |
-
# V1.3 FIX: socks5h:// β socks5:// (httpx compatibility)
|
| 10 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 11 |
|
| 12 |
import json
|
|
@@ -35,12 +33,7 @@ import threading
|
|
| 35 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 36 |
|
| 37 |
TARGET_URL = "https://r3gm-wan2-2-fp8da-aoti-preview-2.hf.space"
|
| 38 |
-
|
| 39 |
-
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 40 |
-
# π§ FIX V1.3: socks5h β socks5 (httpx only supports socks5://)
|
| 41 |
-
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 42 |
TOR_PROXY = "socks5://127.0.0.1:9050"
|
| 43 |
-
|
| 44 |
PREDICT_TIMEOUT = 300
|
| 45 |
MAX_TOR_RETRIES = 5
|
| 46 |
GPU_RETRY_DELAY = 15
|
|
@@ -59,12 +52,14 @@ UPLOAD_DIR.mkdir(exist_ok=True)
|
|
| 59 |
TASKS: Dict[str, Dict[str, Any]] = {}
|
| 60 |
|
| 61 |
_last_tor_ip = {"ip": "unknown", "time": 0}
|
|
|
|
| 62 |
|
| 63 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 64 |
# π§
TOR ENGINE
|
| 65 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 66 |
|
| 67 |
def ensure_tor_running():
|
|
|
|
| 68 |
try:
|
| 69 |
result = subprocess.run(
|
| 70 |
["service", "tor", "status"],
|
|
@@ -75,11 +70,15 @@ def ensure_tor_running():
|
|
| 75 |
time.sleep(3)
|
| 76 |
print("π§
Tor started")
|
| 77 |
except:
|
| 78 |
-
|
| 79 |
-
|
|
|
|
|
|
|
|
|
|
| 80 |
|
| 81 |
|
| 82 |
def get_tor_ip() -> str:
|
|
|
|
| 83 |
now = time.time()
|
| 84 |
if now - _last_tor_ip["time"] < 10 and _last_tor_ip["ip"] != "unknown":
|
| 85 |
return _last_tor_ip["ip"]
|
|
@@ -96,6 +95,7 @@ def get_tor_ip() -> str:
|
|
| 96 |
|
| 97 |
|
| 98 |
def rotate_tor_circuit() -> str:
|
|
|
|
| 99 |
try:
|
| 100 |
with Controller.from_port(port=9051) as controller:
|
| 101 |
controller.authenticate()
|
|
@@ -123,18 +123,40 @@ def rotate_tor_circuit() -> str:
|
|
| 123 |
return "rotation_failed"
|
| 124 |
|
| 125 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 127 |
# π‘ GRADIO CLIENT WITH PROXY
|
| 128 |
# ββββββββββββββββββββββββββββββββοΏ½οΏ½οΏ½ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 129 |
|
| 130 |
class GradioClientWithProxy:
|
| 131 |
-
"""
|
| 132 |
-
Wrapper gradio_client yang route traffic via Tor SOCKS5 proxy.
|
| 133 |
-
|
| 134 |
-
V1.3: Menggunakan socks5:// (bukan socks5h://) agar compatible dengan httpx.
|
| 135 |
-
httpx internal gradio_client hanya recognize socks5:// scheme.
|
| 136 |
-
"""
|
| 137 |
-
|
| 138 |
def __init__(self, base_url: str, proxy: str = None):
|
| 139 |
self.base_url = base_url
|
| 140 |
self.proxy = proxy
|
|
@@ -157,7 +179,7 @@ class GradioClientWithProxy:
|
|
| 157 |
|
| 158 |
self.client = Client(
|
| 159 |
self.base_url,
|
| 160 |
-
headers={"User-Agent": "SodaGen-Anonymous/1.
|
| 161 |
)
|
| 162 |
|
| 163 |
def submit(self, **kwargs):
|
|
@@ -269,7 +291,7 @@ def resize_image_for_video(image_path, width=768, height=512):
|
|
| 269 |
|
| 270 |
|
| 271 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 272 |
-
# π GENERATION TASK
|
| 273 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 274 |
|
| 275 |
def run_generation_task(
|
|
@@ -281,27 +303,26 @@ def run_generation_task(
|
|
| 281 |
steps: float,
|
| 282 |
frame_mult: int,
|
| 283 |
):
|
|
|
|
| 284 |
task = TASKS[task_id]
|
| 285 |
|
| 286 |
try:
|
| 287 |
task["status"] = "processing"
|
| 288 |
task["log"] += f"π¬ [ANONYMOUS MODE] Prompt: {prompt[:80]}...\n"
|
| 289 |
task["log"] += f"β±οΈ Duration: {duration}s | Steps: {steps} | FPS Mult: {frame_mult}\n"
|
| 290 |
-
task["log"] += f"β±οΈ Timeout: {PREDICT_TIMEOUT}s | Max Tor retries: {MAX_TOR_RETRIES}\n"
|
| 291 |
-
task["log"] += f"π§ Proxy scheme: socks5:// (V1.3 fix)\n"
|
| 292 |
|
|
|
|
| 293 |
ensure_tor_running()
|
| 294 |
|
| 295 |
-
#
|
| 296 |
-
|
| 297 |
-
task["log"] += f"π§
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
task["log"] += f"
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
task["log"] += f" After restart: {test_ip}\n"
|
| 305 |
|
| 306 |
img1_resized = resize_image_for_video(img1_path) if img1_path else None
|
| 307 |
img2_resized = resize_image_for_video(img2_path) if img2_path else None
|
|
@@ -368,9 +389,6 @@ def run_generation_task(
|
|
| 368 |
|
| 369 |
if is_quota_error(err_msg):
|
| 370 |
task["log"] += f"β οΈ QUOTA EXCEEDED on IP {current_ip}\n"
|
| 371 |
-
retry_time = parse_retry_time(err_msg)
|
| 372 |
-
if retry_time:
|
| 373 |
-
task["log"] += f" β° Retry in: {retry_time}s\n"
|
| 374 |
task["log"] += f"π Rotating Tor circuit...\n"
|
| 375 |
new_ip = rotate_tor_circuit()
|
| 376 |
task["log"] += f" π§
New IP: {new_ip}\n"
|
|
@@ -509,7 +527,7 @@ def stream_video(video_path: str, request: Request):
|
|
| 509 |
# π FASTAPI APPLICATION
|
| 510 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 511 |
|
| 512 |
-
app = FastAPI(title="SODA-GEN Anonymous Relay V1.
|
| 513 |
|
| 514 |
app.add_middleware(
|
| 515 |
CORSMiddleware,
|
|
@@ -520,16 +538,45 @@ app.add_middleware(
|
|
| 520 |
)
|
| 521 |
|
| 522 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 523 |
@app.get("/api/health")
|
| 524 |
async def health_check():
|
| 525 |
-
|
| 526 |
-
|
|
|
|
|
|
|
|
|
|
| 527 |
return JSONResponse({
|
| 528 |
"status": "ok",
|
| 529 |
"mode": "anonymous",
|
| 530 |
-
"version": "1.
|
| 531 |
-
"tor_ip": ip,
|
| 532 |
-
"
|
| 533 |
"proxy_scheme": "socks5://",
|
| 534 |
"ffmpeg": shutil.which("ffmpeg") is not None,
|
| 535 |
})
|
|
@@ -623,15 +670,15 @@ async def serve_thumbnail(filename: str):
|
|
| 623 |
|
| 624 |
@app.get("/api/stats")
|
| 625 |
async def api_stats():
|
| 626 |
-
ip = get_tor_ip()
|
| 627 |
return JSONResponse({
|
| 628 |
"mode": "anonymous",
|
| 629 |
-
"tor_ip": ip,
|
|
|
|
| 630 |
"quota_per_ip": 120,
|
| 631 |
"total_tokens": "infinity (Tor rotation)",
|
| 632 |
"active_tokens": "β",
|
| 633 |
"cooldown_tokens": 0,
|
| 634 |
-
"version": "1.
|
| 635 |
})
|
| 636 |
|
| 637 |
|
|
@@ -645,19 +692,3 @@ async def api_tor_ip():
|
|
| 645 |
async def api_tor_rotate():
|
| 646 |
new_ip = rotate_tor_circuit()
|
| 647 |
return JSONResponse({"new_ip": new_ip, "success": new_ip != "rotation_failed"})
|
| 648 |
-
|
| 649 |
-
|
| 650 |
-
@app.on_event("startup")
|
| 651 |
-
async def on_startup():
|
| 652 |
-
ensure_tor_running()
|
| 653 |
-
ip = get_tor_ip()
|
| 654 |
-
print("=" * 60)
|
| 655 |
-
print("π§
SODA-GEN Anonymous Relay V1.3 β HF Space CPU")
|
| 656 |
-
print(f" π Tor IP: {ip}")
|
| 657 |
-
print(f" π‘ Target: {TARGET_URL}")
|
| 658 |
-
print(f" π§
Proxy: {TOR_PROXY}")
|
| 659 |
-
print(f" π§ Scheme: socks5:// (V1.3 fix)")
|
| 660 |
-
print(f" β±οΈ Timeout: {PREDICT_TIMEOUT}s")
|
| 661 |
-
print(f" π¬ ffmpeg: {'β
' if shutil.which('ffmpeg') else 'β'}")
|
| 662 |
-
print(f" π¬ ffprobe: {'β
' if shutil.which('ffprobe') else 'β'}")
|
| 663 |
-
print("=" * 60)
|
|
|
|
| 1 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 2 |
+
# π§
SODA-GEN ANONYMOUS RELAY V1.4 β HF SPACE CPU (BACKEND ONLY)
|
| 3 |
#
|
| 4 |
+
# V1.4 FIX: Non-blocking startup (was causing 503 on HF Space)
|
| 5 |
+
# - on_startup: lightweight only, no Tor network calls
|
| 6 |
+
# - Tor IP check runs in background thread
|
| 7 |
+
# - Health endpoint always responds instantly
|
|
|
|
|
|
|
| 8 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 9 |
|
| 10 |
import json
|
|
|
|
| 33 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 34 |
|
| 35 |
TARGET_URL = "https://r3gm-wan2-2-fp8da-aoti-preview-2.hf.space"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
TOR_PROXY = "socks5://127.0.0.1:9050"
|
|
|
|
| 37 |
PREDICT_TIMEOUT = 300
|
| 38 |
MAX_TOR_RETRIES = 5
|
| 39 |
GPU_RETRY_DELAY = 15
|
|
|
|
| 52 |
TASKS: Dict[str, Dict[str, Any]] = {}
|
| 53 |
|
| 54 |
_last_tor_ip = {"ip": "unknown", "time": 0}
|
| 55 |
+
_tor_ready = False # Set True once Tor is confirmed working
|
| 56 |
|
| 57 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 58 |
# π§
TOR ENGINE
|
| 59 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 60 |
|
| 61 |
def ensure_tor_running():
|
| 62 |
+
"""Ensure Tor daemon is active (no network calls)"""
|
| 63 |
try:
|
| 64 |
result = subprocess.run(
|
| 65 |
["service", "tor", "status"],
|
|
|
|
| 70 |
time.sleep(3)
|
| 71 |
print("π§
Tor started")
|
| 72 |
except:
|
| 73 |
+
try:
|
| 74 |
+
subprocess.run(["service", "tor", "start"], capture_output=True, timeout=10)
|
| 75 |
+
time.sleep(3)
|
| 76 |
+
except Exception as e:
|
| 77 |
+
print(f"β οΈ Cannot start Tor: {e}")
|
| 78 |
|
| 79 |
|
| 80 |
def get_tor_ip() -> str:
|
| 81 |
+
"""Get current Tor exit node IP (cached for 10s)"""
|
| 82 |
now = time.time()
|
| 83 |
if now - _last_tor_ip["time"] < 10 and _last_tor_ip["ip"] != "unknown":
|
| 84 |
return _last_tor_ip["ip"]
|
|
|
|
| 95 |
|
| 96 |
|
| 97 |
def rotate_tor_circuit() -> str:
|
| 98 |
+
"""Request new Tor circuit β new exit node β new IP"""
|
| 99 |
try:
|
| 100 |
with Controller.from_port(port=9051) as controller:
|
| 101 |
controller.authenticate()
|
|
|
|
| 123 |
return "rotation_failed"
|
| 124 |
|
| 125 |
|
| 126 |
+
def _background_tor_init():
|
| 127 |
+
"""
|
| 128 |
+
Run in background thread AFTER server is ready.
|
| 129 |
+
This prevents blocking the startup event.
|
| 130 |
+
"""
|
| 131 |
+
global _tor_ready
|
| 132 |
+
print("π§
Background Tor init starting...")
|
| 133 |
+
|
| 134 |
+
# Wait extra time for Tor to build circuits
|
| 135 |
+
time.sleep(10)
|
| 136 |
+
|
| 137 |
+
ensure_tor_running()
|
| 138 |
+
|
| 139 |
+
# Try to get IP (might take time)
|
| 140 |
+
ip = get_tor_ip()
|
| 141 |
+
if ip != "unknown":
|
| 142 |
+
_tor_ready = True
|
| 143 |
+
print(f"π§
Tor ready! IP: {ip}")
|
| 144 |
+
else:
|
| 145 |
+
# Retry once after short delay
|
| 146 |
+
time.sleep(5)
|
| 147 |
+
ip = get_tor_ip()
|
| 148 |
+
if ip != "unknown":
|
| 149 |
+
_tor_ready = True
|
| 150 |
+
print(f"π§
Tor ready (retry)! IP: {ip}")
|
| 151 |
+
else:
|
| 152 |
+
print("β οΈ Tor init incomplete β will retry on first request")
|
| 153 |
+
|
| 154 |
+
|
| 155 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 156 |
# π‘ GRADIO CLIENT WITH PROXY
|
| 157 |
# ββββββββββββββββββββββββββββββββοΏ½οΏ½οΏ½ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 158 |
|
| 159 |
class GradioClientWithProxy:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
def __init__(self, base_url: str, proxy: str = None):
|
| 161 |
self.base_url = base_url
|
| 162 |
self.proxy = proxy
|
|
|
|
| 179 |
|
| 180 |
self.client = Client(
|
| 181 |
self.base_url,
|
| 182 |
+
headers={"User-Agent": "SodaGen-Anonymous/1.4"}
|
| 183 |
)
|
| 184 |
|
| 185 |
def submit(self, **kwargs):
|
|
|
|
| 291 |
|
| 292 |
|
| 293 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 294 |
+
# π GENERATION TASK
|
| 295 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 296 |
|
| 297 |
def run_generation_task(
|
|
|
|
| 303 |
steps: float,
|
| 304 |
frame_mult: int,
|
| 305 |
):
|
| 306 |
+
global _tor_ready
|
| 307 |
task = TASKS[task_id]
|
| 308 |
|
| 309 |
try:
|
| 310 |
task["status"] = "processing"
|
| 311 |
task["log"] += f"π¬ [ANONYMOUS MODE] Prompt: {prompt[:80]}...\n"
|
| 312 |
task["log"] += f"β±οΈ Duration: {duration}s | Steps: {steps} | FPS Mult: {frame_mult}\n"
|
|
|
|
|
|
|
| 313 |
|
| 314 |
+
# Ensure Tor is running (may not be ready yet on first call)
|
| 315 |
ensure_tor_running()
|
| 316 |
|
| 317 |
+
# Get Tor IP (might fail first time, that's ok)
|
| 318 |
+
current_ip = get_tor_ip()
|
| 319 |
+
task["log"] += f"π§
Tor IP: {current_ip}\n"
|
| 320 |
+
|
| 321 |
+
if current_ip == "unknown":
|
| 322 |
+
task["log"] += f"β οΈ Tor not ready yet, waiting 15s for circuits...\n"
|
| 323 |
+
time.sleep(15)
|
| 324 |
+
current_ip = get_tor_ip()
|
| 325 |
+
task["log"] += f"π§
Tor IP (retry): {current_ip}\n"
|
|
|
|
| 326 |
|
| 327 |
img1_resized = resize_image_for_video(img1_path) if img1_path else None
|
| 328 |
img2_resized = resize_image_for_video(img2_path) if img2_path else None
|
|
|
|
| 389 |
|
| 390 |
if is_quota_error(err_msg):
|
| 391 |
task["log"] += f"β οΈ QUOTA EXCEEDED on IP {current_ip}\n"
|
|
|
|
|
|
|
|
|
|
| 392 |
task["log"] += f"π Rotating Tor circuit...\n"
|
| 393 |
new_ip = rotate_tor_circuit()
|
| 394 |
task["log"] += f" π§
New IP: {new_ip}\n"
|
|
|
|
| 527 |
# π FASTAPI APPLICATION
|
| 528 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 529 |
|
| 530 |
+
app = FastAPI(title="SODA-GEN Anonymous Relay V1.4")
|
| 531 |
|
| 532 |
app.add_middleware(
|
| 533 |
CORSMiddleware,
|
|
|
|
| 538 |
)
|
| 539 |
|
| 540 |
|
| 541 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 542 |
+
# π§ V1.4 FIX: STARTUP IS LIGHTWEIGHT β NO BLOCKING CALLS
|
| 543 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 544 |
+
|
| 545 |
+
@app.on_event("startup")
|
| 546 |
+
async def on_startup():
|
| 547 |
+
"""
|
| 548 |
+
V1.4: Startup event is FAST and NON-BLOCKING.
|
| 549 |
+
Only prints basic info. Tor IP check runs in background thread.
|
| 550 |
+
This ensures HF Space health check gets instant response β no 503.
|
| 551 |
+
"""
|
| 552 |
+
print("=" * 60)
|
| 553 |
+
print("π§
SODA-GEN Anonymous Relay V1.4 β HF Space CPU")
|
| 554 |
+
print(f" π‘ Target: {TARGET_URL}")
|
| 555 |
+
print(f" π§
Proxy: {TOR_PROXY}")
|
| 556 |
+
print(f" β±οΈ Timeout: {PREDICT_TIMEOUT}s")
|
| 557 |
+
print(f" π¬ ffmpeg: {'β
' if shutil.which('ffmpeg') else 'β'}")
|
| 558 |
+
print(f" π§
Tor init: running in background thread...")
|
| 559 |
+
print("=" * 60)
|
| 560 |
+
|
| 561 |
+
# Start Tor initialization in BACKGROUND thread
|
| 562 |
+
# This does NOT block the startup event
|
| 563 |
+
t = threading.Thread(target=_background_tor_init, daemon=True)
|
| 564 |
+
t.start()
|
| 565 |
+
|
| 566 |
+
|
| 567 |
@app.get("/api/health")
|
| 568 |
async def health_check():
|
| 569 |
+
"""
|
| 570 |
+
Lightweight health check β responds INSTANTLY.
|
| 571 |
+
Tor IP is optional (shows cached or 'initializing').
|
| 572 |
+
HF Space uses this to determine if space is alive.
|
| 573 |
+
"""
|
| 574 |
return JSONResponse({
|
| 575 |
"status": "ok",
|
| 576 |
"mode": "anonymous",
|
| 577 |
+
"version": "1.4",
|
| 578 |
+
"tor_ip": _last_tor_ip["ip"],
|
| 579 |
+
"tor_ready": _tor_ready,
|
| 580 |
"proxy_scheme": "socks5://",
|
| 581 |
"ffmpeg": shutil.which("ffmpeg") is not None,
|
| 582 |
})
|
|
|
|
| 670 |
|
| 671 |
@app.get("/api/stats")
|
| 672 |
async def api_stats():
|
|
|
|
| 673 |
return JSONResponse({
|
| 674 |
"mode": "anonymous",
|
| 675 |
+
"tor_ip": _last_tor_ip["ip"],
|
| 676 |
+
"tor_ready": _tor_ready,
|
| 677 |
"quota_per_ip": 120,
|
| 678 |
"total_tokens": "infinity (Tor rotation)",
|
| 679 |
"active_tokens": "β",
|
| 680 |
"cooldown_tokens": 0,
|
| 681 |
+
"version": "1.4",
|
| 682 |
})
|
| 683 |
|
| 684 |
|
|
|
|
| 692 |
async def api_tor_rotate():
|
| 693 |
new_ip = rotate_tor_circuit()
|
| 694 |
return JSONResponse({"new_ip": new_ip, "success": new_ip != "rotation_failed"})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|