Bl4ckSpaces commited on
Commit
dc71255
·
verified ·
1 Parent(s): ef9c7fa

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +93 -45
app.py CHANGED
@@ -2,20 +2,19 @@ import os
2
  import random
3
  import time
4
  import threading
 
 
5
  from flask import Flask, request, jsonify, send_file
6
  from flask_cors import CORS
7
- from gradio_client import Client
8
 
9
  app = Flask(__name__)
10
- # Aktifkan CORS untuk semua akses
11
  CORS(app)
12
 
13
- # --- KONFIGURASI TARGET (SESUAI PERINTAH) ---
14
- # Menggunakan direct link Spaces Flux yang kamu berikan
15
- TARGET_SPACE = "https://black-forest-labs-flux-2-dev.hf.space"
16
 
17
- # --- TOKEN LIST ---
18
- # Token sudah ditanam dengan tanda '+' agar lolos deteksi
19
  RAW_TOKENS = [
20
  "hf_+PiRCDDtPcPFMLWkTkVaZmzoleHOunXnLIA",
21
  "hf_+BHvZXGICstaktSwycmwNmzHGrTNmKxnlRZ",
@@ -34,80 +33,130 @@ RAW_TOKENS = [
34
  # --- TOKEN MANAGER ---
35
  class TokenManager:
36
  def __init__(self, raw_tokens):
37
- # Hapus tanda '+' dan spasi
38
  self.tokens = [t.replace("+", "").strip() for t in raw_tokens if t.strip()]
39
  self.current_index = 0
40
  self.lock = threading.Lock()
41
 
42
  def get_token(self):
43
  with self.lock:
44
- if not self.tokens:
45
- raise Exception("List token kosong!")
46
  token = self.tokens[self.current_index]
47
  self.current_index = (self.current_index + 1) % len(self.tokens)
48
  return token
49
 
50
  token_manager = TokenManager(RAW_TOKENS)
51
 
52
- # --- LOGIKA GENERATE ---
53
- def process_generation_with_retry(prompt, width, height, guidance_scale, seed):
54
- # Batasi retry maksimal 5 kali agar tidak stuck
 
 
 
55
  max_retries = 5
56
  attempt = 0
57
  last_error = ""
58
 
 
 
 
 
 
59
  while attempt < max_retries:
60
  current_token = token_manager.get_token()
61
- print(f"[LOG] Mencoba request dengan token akhiran ...{current_token[-5:]}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
  try:
64
- # Langsung konek ke URL yang kamu kasih
65
- client = Client(TARGET_SPACE, hf_token=current_token)
 
 
 
66
 
67
- # Parameter request
68
- result = client.predict(
69
- prompt=prompt,
70
- seed=int(seed) if seed is not None else 0,
71
- randomize_seed=(seed is None),
72
- width=int(width),
73
- height=int(height),
74
- guidance_scale=float(guidance_scale),
75
- num_inference_steps=28,
76
- api_name="/infer"
77
- )
78
 
79
- # Flux biasanya return tuple path file di index 0
80
- return result[0]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
  except Exception as e:
83
- error_msg = str(e)
84
- print(f"[ERROR] Token ...{current_token[-5:]} gagal: {error_msg}")
85
 
86
- # Cek limit atau kuota habis
87
- if "429" in error_msg or "quota" in error_msg.lower():
88
- print("[INFO] Rate limit kena, ganti token selanjutnya...")
89
  else:
90
- last_error = error_msg
91
 
92
  attempt += 1
93
- time.sleep(0.5)
94
 
95
- raise Exception(f"Gagal generate gambar. Error terakhir: {last_error}")
96
 
97
- # --- ROUTES ---
98
 
 
99
  @app.route('/')
100
  def home():
101
  return jsonify({
102
- "status": "Ready",
103
- "target": TARGET_SPACE,
104
- "tokens_active": len(token_manager.tokens)
105
  })
106
 
107
  @app.route('/generate', methods=['POST'])
108
  def generate():
109
  data = request.json
110
-
111
  if not data or 'prompt' not in data:
112
  return jsonify({"error": "Prompt wajib diisi"}), 400
113
 
@@ -118,12 +167,11 @@ def generate():
118
  seed = data.get('seed', None)
119
 
120
  try:
121
- image_path = process_generation_with_retry(prompt, width, height, guidance, seed)
122
- # Mengembalikan file gambar hasil generate
123
  return send_file(image_path, mimetype='image/webp')
124
-
125
  except Exception as e:
126
  return jsonify({"error": str(e)}), 500
127
 
128
  if __name__ == '__main__':
129
  app.run(host='0.0.0.0', port=7860)
 
 
2
  import random
3
  import time
4
  import threading
5
+ import requests
6
+ import json
7
  from flask import Flask, request, jsonify, send_file
8
  from flask_cors import CORS
9
+ from concurrent.futures import ThreadPoolExecutor
10
 
11
  app = Flask(__name__)
 
12
  CORS(app)
13
 
14
+ # --- KONFIGURASI TARGET ---
15
+ TARGET_SPACE_URL = "https://black-forest-labs-flux-2-dev.hf.space"
 
16
 
17
+ # --- TOKEN LIST (Tetap ditanam) ---
 
18
  RAW_TOKENS = [
19
  "hf_+PiRCDDtPcPFMLWkTkVaZmzoleHOunXnLIA",
20
  "hf_+BHvZXGICstaktSwycmwNmzHGrTNmKxnlRZ",
 
33
  # --- TOKEN MANAGER ---
34
  class TokenManager:
35
  def __init__(self, raw_tokens):
 
36
  self.tokens = [t.replace("+", "").strip() for t in raw_tokens if t.strip()]
37
  self.current_index = 0
38
  self.lock = threading.Lock()
39
 
40
  def get_token(self):
41
  with self.lock:
42
+ if not self.tokens: raise Exception("List token kosong!")
 
43
  token = self.tokens[self.current_index]
44
  self.current_index = (self.current_index + 1) % len(self.tokens)
45
  return token
46
 
47
  token_manager = TokenManager(RAW_TOKENS)
48
 
49
+ # --- FUNGSI REQUEST MANUAL (CARA LAIN) ---
50
+ def request_flux_manual(prompt, width, height, guidance_scale, seed):
51
+ """
52
+ Menggunakan requests murni, tanpa gradio_client.
53
+ Menembak endpoint /api/predict milik Space.
54
+ """
55
  max_retries = 5
56
  attempt = 0
57
  last_error = ""
58
 
59
+ # Endpoint API Gradio standar (Legacy & V4 compatible)
60
+ # Kita coba dua kemungkinan endpoint: /api/predict (Gradio 3/Lite) atau /run/predict (Gradio 4)
61
+ # Biasanya untuk space publik, /api/predict adalah yang paling umum terekspos.
62
+ api_url = f"{TARGET_SPACE_URL}/api/predict"
63
+
64
  while attempt < max_retries:
65
  current_token = token_manager.get_token()
66
+ print(f"[LOG] Attempt {attempt+1} - Token: ...{current_token[-5:]}")
67
+
68
+ headers = {
69
+ "Authorization": f"Bearer {current_token}",
70
+ "Content-Type": "application/json"
71
+ }
72
+
73
+ # Payload JSON standar untuk Flux Dev Space
74
+ # Urutan 'data' sangat penting! Biasanya:
75
+ # [prompt, seed, randomize_seed, width, height, guidance, num_inference_steps]
76
+ payload = {
77
+ "fn_index": 0, # Index fungsi default (biasanya 0 untuk generate)
78
+ "data": [
79
+ prompt,
80
+ int(seed) if seed is not None else 0,
81
+ True if seed is None else False, # randomize_seed
82
+ int(width),
83
+ int(height),
84
+ float(guidance_scale),
85
+ 28 # num_inference_steps (default flux)
86
+ ]
87
+ }
88
 
89
  try:
90
+ # 1. Kirim Request Generate
91
+ response = requests.post(api_url, headers=headers, json=payload, timeout=120)
92
+
93
+ if response.status_code != 200:
94
+ raise Exception(f"Status {response.status_code}: {response.text[:100]}")
95
 
96
+ resp_json = response.json()
 
 
 
 
 
 
 
 
 
 
97
 
98
+ # 2. Parsing Hasil
99
+ # Gradio API biasanya mengembalikan: {"data": ["/tmp/..."], ...}
100
+ if "data" in resp_json and len(resp_json["data"]) > 0:
101
+ file_info = resp_json["data"][0]
102
+
103
+ # Cek apakah response berupa URL atau Path
104
+ # Flux space sering mengembalikan object seperti {"path": "...", "url": "..."} atau string path langsung
105
+ download_url = ""
106
+
107
+ if isinstance(file_info, dict) and "url" in file_info:
108
+ download_url = file_info["url"]
109
+ elif isinstance(file_info, str):
110
+ # Jika string path lokal (/tmp/...), kita harus fetch dari endpoint file=
111
+ # Kecuali kalau dia sudah full URL
112
+ if file_info.startswith("http"):
113
+ download_url = file_info
114
+ else:
115
+ download_url = f"{TARGET_SPACE_URL}/file={file_info}"
116
+ else:
117
+ raise Exception("Format response data tidak dikenali.")
118
+
119
+ # 3. Download Gambar Hasil ke Server Proxy (Memory)
120
+ print(f"[LOG] Downloading result from: {download_url}")
121
+ img_response = requests.get(download_url, timeout=60)
122
+
123
+ # Simpan sementara di /tmp server kita untuk dikirim ke user
124
+ temp_filename = f"/tmp/flux_{int(time.time())}_{random.randint(0,1000)}.webp"
125
+ with open(temp_filename, 'wb') as f:
126
+ f.write(img_response.content)
127
+
128
+ return temp_filename
129
+
130
+ else:
131
+ raise Exception(f"Response data kosong/salah format: {resp_json}")
132
 
133
  except Exception as e:
134
+ err_msg = str(e)
135
+ print(f"[ERROR] {err_msg}")
136
 
137
+ if "429" in err_msg or "quota" in err_msg.lower():
138
+ print("[INFO] Rate limit/Quota, lanjut token berikutnya.")
 
139
  else:
140
+ last_error = err_msg
141
 
142
  attempt += 1
143
+ time.sleep(1)
144
 
145
+ raise Exception(f"Gagal total setelah {max_retries}x coba. Error: {last_error}")
146
 
 
147
 
148
+ # --- ROUTES ---
149
  @app.route('/')
150
  def home():
151
  return jsonify({
152
+ "status": "Online (Requests Mode)",
153
+ "target": TARGET_SPACE_URL,
154
+ "mode": "No-Gradio-Client"
155
  })
156
 
157
  @app.route('/generate', methods=['POST'])
158
  def generate():
159
  data = request.json
 
160
  if not data or 'prompt' not in data:
161
  return jsonify({"error": "Prompt wajib diisi"}), 400
162
 
 
167
  seed = data.get('seed', None)
168
 
169
  try:
170
+ image_path = request_flux_manual(prompt, width, height, guidance, seed)
 
171
  return send_file(image_path, mimetype='image/webp')
 
172
  except Exception as e:
173
  return jsonify({"error": str(e)}), 500
174
 
175
  if __name__ == '__main__':
176
  app.run(host='0.0.0.0', port=7860)
177
+