File size: 6,341 Bytes
81c7df5
 
 
 
 
 
 
 
ec3d341
 
 
81c7df5
ec3d341
81c7df5
 
 
ec3d341
81c7df5
 
 
ec3d341
81c7df5
 
54aaafb
81c7df5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ec3d341
81c7df5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57a8da4
81c7df5
 
 
 
 
 
 
 
 
df6d41f
 
81c7df5
 
 
 
df6d41f
81c7df5
df6d41f
 
 
 
 
 
 
 
 
 
 
 
 
81c7df5
 
 
df6d41f
81c7df5
 
 
ec3d341
 
 
81c7df5
ec3d341
 
 
81c7df5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ec3d341
 
 
 
 
81c7df5
 
ec3d341
81c7df5
 
 
 
 
ec3d341
81c7df5
 
 
 
 
 
 
 
 
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
import os
import io
import json
import requests
import tempfile
from datetime import date
from fastapi import FastAPI, UploadFile, File, HTTPException, Header
from fastapi.responses import StreamingResponse
from fastapi.middleware.cors import CORSMiddleware
from gradio_client import Client, handle_file

# --- KONFIGURASI (Silakan sesuaikan) ---

# 1. API Key dari remove.bg (dari screenshot Anda)
#    SANGAT DISARANKAN untuk mengatur ini sebagai environment variable di platform deploy Anda.
REMOVEBG_API_KEY = os.getenv("REMOVEBG_API_KEY")

# 2. Nama Gradio Space untuk fallback
BRIA_SPACE = "briaai/BRIA-RMBG-2.0" # Ganti jika Anda punya nama Space Bria yang lain
MYSTYC_SPACE = "Mystyc/remove-bg-rmbg" # Space yang Anda gunakan sekarang

# 3. Pengaturan Jatah Harian untuk remove.bg
DAILY_QUOTA_LIMIT = 2
QUOTA_FILE = os.path.join(tempfile.gettempdir(), "quota.json")

# --- Inisialisasi Aplikasi FastAPI ---
app = FastAPI(title="Dynamic Fallback RMBG Proxy")
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])

# --- FUNGSI-FUNGSI PEMBANTU ---

def _check_and_update_quota():
    """Membaca file quota, memeriksa apakah jatah masih ada, dan mengupdate jika perlu."""
    today = str(date.today())
    quota_data = {"date": today, "count": 0}

    if os.path.exists(QUOTA_FILE):
        try:
            with open(QUOTA_FILE, "r") as f:
                data = json.load(f)
            # Jika tanggal sudah berbeda, reset jatah
            if data.get("date") == today:
                quota_data = data
            else:
                print(f"Tanggal baru terdeteksi. Mereset jatah harian untuk {today}.")
        except (json.JSONDecodeError, FileNotFoundError):
            print("File quota rusak atau tidak ditemukan, membuat yang baru.")
    
    with open(QUOTA_FILE, "w") as f:
        json.dump(quota_data, f)

    if quota_data["count"] < DAILY_QUOTA_LIMIT:
        return True
    else:
        print(f"Jatah harian remove.bg ({DAILY_QUOTA_LIMIT}) untuk tanggal {today} sudah habis.")
        return False

def _increment_quota_count():
    """Menambah hitungan jatah setelah pemanggilan API remove.bg berhasil."""
    try:
        with open(QUOTA_FILE, "r+") as f:
            data = json.load(f)
            data["count"] += 1
            f.seek(0)
            json.dump(data, f)
            f.truncate()
        print(f"Jatah remove.bg digunakan: {data['count']}/{DAILY_QUOTA_LIMIT}")
    except Exception as e:
        print(f"Gagal menambah hitungan jatah: {e}")

def _predict_from_removebg_api(image_blob: bytes):
    """Memanggil API resmi remove.bg."""
    print("Mencoba Prioritas 1: remove.bg API...")
    response = requests.post(
        'https://api.remove.bg/v1/removebg',
        files={'image_file': image_blob},
        headers={'X-Api-Key': REMOVEBG_API_KEY},
        timeout=180
    )
    response.raise_for_status() # Akan error jika status bukan 2xx
    _increment_quota_count() # Tambah hitungan jika berhasil
    print("✅ Sukses menggunakan remove.bg API.")
    return StreamingResponse(io.BytesIO(response.content), media_type="image/png")

# GANTI SELURUH FUNGSI LAMA DENGAN YANG INI

def _predict_from_gradio_space(local_path: str, space_name: str, api_name: str):
    """Fungsi yang direfaktor untuk memanggil Gradio Space manapun dengan api_name dinamis."""
    print(f"Mencoba Prioritas Fallback: Gradio Space '{space_name}' dengan API '{api_name}'...")
    client = Client(src=space_name)
    result = client.predict(image=handle_file(local_path), api_name=api_name)
    
    # --- LOGIKA BARU UNTUK MEMBACA HASIL ---
    image_path = None
    # Jika hasilnya adalah list atau tuple (seperti di Mystyc Space yang baru)
    if isinstance(result, (list, tuple)) and len(result) > 0:
        # Ambil elemen PERTAMA (indeks 0) sebagai path gambar
        image_path = result[0]
    # Jika hasilnya hanya string (seperti di Gradio Space yang lama)
    elif isinstance(result, str):
        image_path = result

    # Lanjutkan proses jika path gambar valid
    if image_path and isinstance(image_path, str) and os.path.exists(image_path):
        with open(image_path, "rb") as f:
            print(f"✅ Sukses menggunakan Gradio Space '{space_name}'.")
            return StreamingResponse(io.BytesIO(f.read()), media_type="image/png")
    
    # Jika format tidak dikenali sama sekali
    raise ValueError(f"Output dari Gradio Space '{space_name}' tidak dikenali: {repr(result)}")

# --- ENDPOINTS ---

@app.get("/health")
def health():
    return {"ok": True, "services": {"primary": "remove.bg", "fallback_1": BRIA_SPACE, "fallback_2": MYSTYC_SPACE}}

@app.post("/remove_bg", response_class=StreamingResponse)
async def remove_bg(file: UploadFile = File(...)):
    blob = await file.read()
    if not blob:
        raise HTTPException(400, "File kosong")

    # --- LOGIKA FALLBACK DIMULAI DI SINI ---

    # 1. Coba remove.bg API jika jatah masih ada
    if REMOVEBG_API_KEY != "MASUKKAN_API_KEY_REMOVEBG_ANDA_DI_SINI" and _check_and_update_quota():
        try:
            return _predict_from_removebg_api(blob)
        except Exception as e:
            print(f"⚠️ Gagal dengan remove.bg API: {e}. Mencoba fallback berikutnya.")

    # Jika remove.bg gagal atau jatah habis, buat file sementara untuk Gradio
    suffix = os.path.splitext(file.filename or ".png")[1]
    tmp_path = None
    try:
        with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as tmp:
            tmp.write(blob)
            tmp_path = tmp.name

        # 2. Coba Bria RM-BG
        try:
            return _predict_from_gradio_space(tmp_path, BRIA_SPACE, api_name="/image")
        except Exception as e:
            print(f"⚠️ Gagal dengan Gradio Space '{BRIA_SPACE}': {e}. Mencoba fallback terakhir.")
        
        # 3. Coba Mystyc RM-BG
        try:
            return _predict_from_gradio_space(tmp_path, MYSTYC_SPACE, api_name="/predict")
        except Exception as e:
            print(f"⚠️ Gagal dengan Gradio Space '{MYSTYC_SPACE}': {e}. Semua layanan gagal.")
            raise HTTPException(status_code=503, detail=f"Semua layanan penghapus background tidak tersedia. Error terakhir: {e}")

    finally:
        # Selalu hapus file sementara
        if tmp_path and os.path.exists(tmp_path):
            os.remove(tmp_path)