Spaces:
Running
Running
| import os | |
| import shutil | |
| import subprocess | |
| import json | |
| import time | |
| import requests | |
| from fastapi import FastAPI, BackgroundTasks, HTTPException, Request | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel | |
| from playwright.sync_api import sync_playwright | |
| app = FastAPI() | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # --- KONFIGURASI --- | |
| UNITY_PATH = "/opt/unity/Editor/Unity" | |
| PROJECT_PATH = "/app/UnityProject" | |
| TEMP_DIR = "/tmp/zepeto_work" | |
| os.makedirs(TEMP_DIR, exist_ok=True) | |
| # --- MODEL DATA (Sesuai dengan kiriman Cloudflare + Cloudinary) --- | |
| class ConversionRequest(BaseModel): | |
| file_url: str # URL dari Cloudinary | |
| filename: str # Nama file (contoh: baju_keren.fbx) | |
| zepeto_id: str | |
| zepeto_password: str | |
| # --- FUNGSI: LOGIN & UPLOAD KE ZEPETO STUDIO --- | |
| def upload_to_zepeto(file_path, z_id, z_pw): | |
| print(f"π [Zepeto] Mencoba login untuk: {z_id}") | |
| try: | |
| with sync_playwright() as p: | |
| browser = p.chromium.launch(headless=True, args=['--no-sandbox']) | |
| context = browser.new_context(user_agent="Mozilla/5.0...") | |
| page = context.new_page() | |
| # Login Process | |
| page.goto("https://account.zepeto.me/signin?sm=1") | |
| page.fill('input[name="userId"]', z_id) | |
| page.fill('input[type="password"]', z_pw) | |
| page.click('button[type="submit"]') | |
| # Tunggu Token/Cookie | |
| page.wait_for_url(lambda u: "signin" not in u, timeout=15000) | |
| cookies = context.cookies() | |
| jwt_token = next((c['value'] for c in cookies if c['name'] == 'token'), None) | |
| browser.close() | |
| if not jwt_token: | |
| return False, "Gagal mendapatkan JWT Token" | |
| # API Upload Zepeto Studio | |
| print("π [Zepeto] Mengirim file ke Studio API...") | |
| upload_url = "https://cf-api-studio.zepeto.me/api/items" | |
| headers = {"Authorization": f"Bearer {jwt_token}"} | |
| with open(file_path, "rb") as f: | |
| files = {"file": (os.path.basename(file_path), f, "application/octet-stream")} | |
| res = requests.post(upload_url, headers=headers, files=files) | |
| if res.status_code in [200, 201]: | |
| return True, "Sukses Upload" | |
| else: | |
| return False, f"API Error: {res.text}" | |
| except Exception as e: | |
| return False, str(e) | |
| # --- FUNGSI UTAMA: DOWNLOAD -> UNITY -> UPLOAD --- | |
| def process_pipeline(req: ConversionRequest): | |
| local_fbx = os.path.join(PROJECT_PATH, "Assets", req.filename) | |
| output_filename = req.filename.replace(".fbx", ".zepeto") | |
| local_zepeto = os.path.join(PROJECT_PATH, "output", output_filename) | |
| try: | |
| # 1. DOWNLOAD DARI CLOUDINARY | |
| print(f"π₯ [Cloudinary] Downloading: {req.filename}") | |
| r = requests.get(req.file_url, stream=True) | |
| with open(local_fbx, 'wb') as f: | |
| for chunk in r.iter_content(chunk_size=8192): | |
| f.write(chunk) | |
| # 2. JALANKAN UNITY (Konversi & Optimasi Poligon) | |
| print("βοΈ [Unity] Memulai konversi & optimasi...") | |
| # Perhatikan: Method C# harus menangani decimation/polygon reduction | |
| cmd = [ | |
| UNITY_PATH, "-batchmode", "-nographics", | |
| "-projectPath", PROJECT_PATH, | |
| "-executeMethod", "TestBuilder.ManualConvert", | |
| "-inputFile", req.filename, | |
| "-quit", "-logFile", "-" | |
| ] | |
| result = subprocess.run(cmd, capture_output=True, text=True) | |
| if not os.path.exists(local_zepeto): | |
| print(f"β [Unity] Gagal. Log: {result.stdout}") | |
| return | |
| # 3. UPLOAD KE ZEPETO STUDIO | |
| success, msg = upload_to_zepeto(local_zepeto, req.z_id, req.z_pw) | |
| if success: | |
| print(f"β [FINAL] {req.filename} Berhasil dikirim ke Zepeto!") | |
| else: | |
| print(f"β [FINAL] Gagal upload ke Zepeto Studio: {msg}") | |
| except Exception as e: | |
| print(f"β [CRITICAL] Pipeline Error: {e}") | |
| finally: | |
| # Cleanup | |
| if os.path.exists(local_fbx): os.remove(local_fbx) | |
| async def api_convert(req: ConversionRequest, background_tasks: BackgroundTasks): | |
| background_tasks.add_task(process_pipeline, req) | |
| return {"status": "success", "message": "Proses konversi dan upload Zepeto dimulai di background"} |