File size: 6,336 Bytes
ebd67c4
 
 
 
 
 
 
 
e82e859
e6be728
e82e859
 
ebd67c4
e82e859
 
 
 
ebd67c4
e82e859
 
 
 
6f35883
e82e859
 
6f35883
 
e82e859
ebd67c4
574d828
9de639c
ebd67c4
 
 
 
 
9de639c
ebd67c4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
574d828
e6be728
ebd67c4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e82e859
574d828
 
efc9b0c
ebd67c4
6f35883
 
 
e6be728
 
ebd67c4
 
 
 
 
6f35883
574d828
ebd67c4
 
b5d3142
ebd67c4
 
1b8ac87
b5d3142
 
ebd67c4
 
 
 
b5d3142
ebd67c4
 
 
b5d3142
ebd67c4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
574d828
ebd67c4
 
 
574d828
ebd67c4
 
 
 
6f35883
 
9a4ca5c
574d828
 
ebd67c4
574d828
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
import os
import shutil
import subprocess
import requests
import zipfile
import uuid
import glob
import datetime
from fastapi import FastAPI, BackgroundTasks
from fastapi.responses import FileResponse, JSONResponse
from pydantic import BaseModel

# --- KONFIGURASI PATH ---
os.chdir("/tmp")
os.environ["HOME"] = "/tmp"
os.environ["UNITY_PATH"] = "/opt/unity/Editor/Unity"
BASE_PROJECT_PATH = "/app/UnityProject"
LICENSE_SOURCE = "/app/Unity_lic.ulf"

app = FastAPI()

class ConversionRequest(BaseModel):
    transaction_id: str
    file_url: str
    filename: str
    z_id: str
    z_pw: str

# --- FUNGSI PEMBANTU ---

def setup_license():
    """Menyiapkan lisensi dan mengecek waktu server untuk debug."""
    # 1. Cek Waktu Server (Penting untuk mengatasi 'Time validation failed')
    now = datetime.datetime.now()
    print(f"⏰ [DEBUG] Waktu Server Sekarang: {now.strftime('%Y-%m-%d %H:%M:%S')}", flush=True)

    target_dir = "/tmp/.local/share/unity3d/Unity"
    os.makedirs(target_dir, exist_ok=True)
    
    if os.path.exists(LICENSE_SOURCE):
        # Bersihkan lisensi lama di folder internal jika ada
        internal_license = os.path.join(target_dir, "Unity_lic.ulf")
        if os.path.exists(internal_license):
            os.remove(internal_license)
            
        shutil.copy2(LICENSE_SOURCE, internal_license)
        print("✅ [SYSTEM] Lisensi disalin ke folder internal Unity.", flush=True)
    else:
        print("⚠️ [WARNING] File Unity_lic.ulf tidak ditemukan di /app/", flush=True)

def run_unity_command(project_path, method, extra_args=None):
    """Menjalankan Unity dengan flag headless dan anti-stuck."""
    cmd = [
        os.environ["UNITY_PATH"],
        "-batchmode",
        "-nographics",
        "-quit",
        "-no-webservices",        # Matikan pencarian layanan cloud
        "-no-graphics",           # Proteksi tambahan mode headless
        "-silent-crashes",        # Jangan munculkan popup error
        "-manualLicenseFile", LICENSE_SOURCE, # Paksa baca file lisensi fisik
        "-projectPath", project_path,
        "-executeMethod", method,
        "-logFile", "-"           # Log keluar secara real-time ke console
    ]
    if extra_args:
        cmd.extend(extra_args)
    
    # Jalankan perintah dan tunggu sampai selesai
    return subprocess.run(cmd, capture_output=False)

# --- ENDPOINTS ---

@app.get("/lisensi")
async def generate_new_alf():
    """Endpoint untuk generate file .alf baru jika lisensi lama Expired."""
    # Bersihkan sisa file .alf sebelumnya
    for f in glob.glob("/tmp/*.alf"):
        try: os.remove(f)
        except: pass
        
    print("🛠️ [LICENSE] Meminta file aktivasi (.alf) baru dari Unity...", flush=True)
    cmd = [
        os.environ["UNITY_PATH"], 
        "-batchmode", "-nographics", 
        "-createManualActivationFile", 
        "-logfile", "/dev/stdout"
    ]
    subprocess.run(cmd, cwd="/tmp", timeout=60)
    
    found_alf = glob.glob("/tmp/*.alf")
    if found_alf:
        print(f"✅ [LICENSE] File .alf berhasil dibuat: {found_alf[0]}", flush=True)
        return FileResponse(path=found_alf[0], filename=os.path.basename(found_alf[0]))
    
    return JSONResponse(status_code=500, content={"error": "Gagal generate .alf. Cek tab Logs."})

@app.post("/convert")
async def api_convert(req: ConversionRequest, background_tasks: BackgroundTasks):
    """Endpoint utama untuk memicu pipeline convert dan upload."""
    background_tasks.add_task(process_pipeline, req)
    return {"status": "processing", "transaction_id": req.transaction_id}

# --- INTI PROSES (PIPELINE) ---

def process_pipeline(req: ConversionRequest):
    setup_license()
    
    # Folder unik untuk setiap transaksi
    SESSION_PATH = f"/tmp/Project_{req.transaction_id}"
    extract_tmp = f"/tmp/extract_{req.transaction_id}"
    zip_tmp = f"/tmp/{req.transaction_id}.zip"

    try:
        # 1. DOWNLOAD & PREPARE
        print(f"📥 [START] Mengambil aset: {req.transaction_id}", flush=True)
        r = requests.get(req.file_url, timeout=30)
        with open(zip_tmp, 'wb') as f:
            f.write(r.content)
        
        os.makedirs(extract_tmp, exist_ok=True)
        with zipfile.ZipFile(zip_tmp, 'r') as z:
            z.extractall(extract_tmp)
        
        # 2. SETUP PROJECT WORKSPACE
        # Salin UnityProject dasar ke folder temporer agar project asli tetap bersih
        shutil.copytree(BASE_PROJECT_PATH, SESSION_PATH, ignore=shutil.ignore_patterns("Library", "Temp"))
        target_in = os.path.join(SESSION_PATH, "Assets/InputRaw")
        os.makedirs(target_in, exist_ok=True)
        
        # Pindahkan file hasil ekstrak ke folder input project
        for item in os.listdir(extract_tmp):
            shutil.move(os.path.join(extract_tmp, item), os.path.join(target_in, item))

        # 3. TAHAP 1: PENYEDERHANAAN MESH
        print(f"🛠️ [STEP 1] Menjalankan TestBuilder.SimplifyOnly...", flush=True)
        step1 = run_unity_command(SESSION_PATH, "TestBuilder.SimplifyOnly")

        if step1.returncode != 0:
            print(f"❌ [FAILED] Step 1 Gagal (Exit Code: {step1.returncode}). Cek Logs di atas.", flush=True)
            return

        # 4. TAHAP 2: UPLOAD KE ZEPETO
        print(f"🚀 [STEP 2] Menjalankan Zepeto CLI Upload...", flush=True)
        upload_args = [
            "-id", req.z_id,
            "-password", req.z_pw,
            "-upload"
        ]
        step2 = run_unity_command(SESSION_PATH, "ZepetoPackageCliBuilder.BuildWithArgs", upload_args)

        if step2.returncode == 0:
            print(f"✅ [SUCCESS] Item {req.transaction_id} telah terunggah ke ZEPETO Studio.", flush=True)
        else:
            print(f"❌ [FAILED] Step 2 Gagal (Exit Code: {step2.returncode}).", flush=True)

    except Exception as e:
        print(f"🔥 [CRITICAL ERROR] {str(e)}", flush=True)
    
    finally:
        # PEMBERSIHAN (Wajib agar disk tidak penuh)
        print(f"🧹 [CLEANUP] Menghapus folder sesi {req.transaction_id}...", flush=True)
        for p in [SESSION_PATH, extract_tmp, zip_tmp]:
            if os.path.exists(p):
                if os.path.isdir(p): shutil.rmtree(p)
                else: os.remove(p)

if __name__ == "__main__":
    import uvicorn
    # Jalankan server FastAPI
    uvicorn.run(app, host="0.0.0.0", port=7860)