renderfy commited on
Commit
b919cc4
·
verified ·
1 Parent(s): 2e0e104

Upload streamlit_app.py

Browse files
Files changed (1) hide show
  1. streamlit_app.py +119 -88
streamlit_app.py CHANGED
@@ -8,9 +8,14 @@ HF_TOKEN = (os.getenv("AI_LIGHTBOX_TOKEN", "") or "").strip().strip("'\"")
8
  HEADERS = {"Authorization": f"Bearer {HF_TOKEN}"} if HF_TOKEN else {}
9
  PREVIEW_SIZE = int(os.getenv("PREVIEW_SIZE", "512")) # sadece UI kare önizleme
10
 
 
 
 
 
 
 
11
  # ================= Yardımcılar =================
12
  def _needs_auth(url: str) -> bool:
13
- # Sadece kendi backend çağrılarında token gönder
14
  return url.startswith(API_BASE) or url.startswith("outputs/")
15
 
16
  def backend_ok() -> bool:
@@ -22,7 +27,6 @@ def backend_ok() -> bool:
22
  return False
23
 
24
  def post_chain(data: dict, files_payload):
25
- # Videoyu backend'e bırakmıyoruz (SR final ile biz tetikleriz)
26
  data = {**data, "to_video": "false"}
27
  r = requests.post(f"{API_BASE}/v1/tryon/chain", data=data, files=files_payload or None,
28
  headers=HEADERS, timeout=600)
@@ -34,8 +38,7 @@ def post_video(image_url: str, duration="6", resolution="768P", prompt_optimizer
34
  "image_url": image_url,
35
  "duration": duration,
36
  "resolution": resolution,
37
- "prompt_optimizer": "true" if prompt_optimizer else "false", # backend default False
38
- # prompt vermiyoruz -> backend JEWELRY_VIDEO_PROMPT (zoom yok)
39
  }
40
  r = requests.post(f"{API_BASE}/v1/video/from-image", data=payload, headers=HEADERS, timeout=600)
41
  r.raise_for_status()
@@ -61,7 +64,7 @@ def fetch_bytes(url_or_path: str):
61
  def image_size_from_bytes(b: bytes):
62
  try:
63
  im = Image.open(io.BytesIO(b))
64
- return im.size # (w, h)
65
  except Exception:
66
  return None
67
 
@@ -99,7 +102,7 @@ def first_present(d: dict, keys: list, default=None):
99
  return v
100
  return default
101
 
102
- def show_tile(col, title, url: str | None, bg_rgb=(255,255,255), filename_stub="image"):
103
  col.subheader(title)
104
  if not url:
105
  placeholder = Image.new("RGBA", (PREVIEW_SIZE, PREVIEW_SIZE), (0, 0, 0, 0))
@@ -119,9 +122,14 @@ def show_tile(col, title, url: str | None, bg_rgb=(255,255,255), filename_stub="
119
  col.caption(f"Gerçek çözünürlük: {sz[0]}×{sz[1]} px")
120
 
121
  ext = infer_ext(b)
122
- # MIME'ı görüntünün gerçek tipinden bağımsız sabit jpeg vermeyelim:
123
  mime = "image/jpeg" if ext.lower() in [".jpg", ".jpeg"] else ("image/png" if ext.lower()==".png" else "image/webp")
124
- col.download_button("İndir (tam kalite)", data=b, file_name=f"{filename_stub}{ext}", mime=mime)
 
 
 
 
 
 
125
  return url, b
126
 
127
  def make_zip(named_bytes: list[tuple[str, bytes]]) -> bytes:
@@ -143,37 +151,38 @@ ok = backend_ok()
143
  col1, col2, col3, col4, col5, col6, col7 = st.columns([1.6, 1.6, 1.0, 1.3, 0.9, 1.1, 0.9])
144
 
145
  with col1:
146
- file_list = st.file_uploader("Takı görseli (yükle)", type=["jpg","jpeg","png","webp"], accept_multiple_files=True)
147
  with col2:
148
- image_url = st.text_input("veya URL")
149
  with col3:
150
- jewel_type = st.selectbox("Tür", ["auto","earring","ring","necklace","bracelet","pendant","brooch","set"], index=0)
151
  with col4:
152
  padding_ratio_val = st.number_input(
153
  "İçerik ölçeği (0.30–0.95)",
154
  min_value=0.30, max_value=0.95, value=0.50, step=0.01,
155
- help="1200×1200 kare tuvalde ürünün uzun kenarı = 1200 × bu oran. 0.50 önerilir."
 
156
  )
157
  with col5:
158
- upscale = st.checkbox("Super Resolution", True)
159
  with col6:
160
- upscale_stage = st.selectbox("SR aşaması", ["both","final","packshot"], index=0, help="Varsayılan: both")
161
  with col7:
162
- upscale_factor = st.selectbox("SR katsayı", ["2","4"], index=0)
163
 
164
  colA, colB, colC, colD, colE = st.columns([1.0, 1.2, 1.2, 0.9, 1.0])
165
  with colA:
166
  st.info(f"Bağlantı: {'Online' if ok else 'Offline'}", icon="🔌")
167
  with colB:
168
- identity_lock = st.checkbox("Identity lock (önerilir)", True)
169
  with colC:
170
- to_video = st.checkbox("Video üret (SR kaynaklı)", False)
171
  with colD:
172
- duration = st.selectbox("Süre (sn)", ["6","10"], index=0)
173
  with colE:
174
- run = st.button("Çalıştır", type="primary", use_container_width=True)
175
 
176
- mannequin_url = st.text_input("Manken URL (opsiyonel)")
177
 
178
  # ================= Girdi Önizleme =================
179
  c_in, c_pack, c_pad, c_model = st.columns(4)
@@ -199,7 +208,7 @@ if input_preview_bytes:
199
  else:
200
  show_tile(c_in, "Input", None)
201
 
202
- # ================ Koş & Göster ================
203
  if run:
204
  if not ok:
205
  st.error("Backend erişilemiyor. API_BASE / TOKEN kontrol edin.")
@@ -216,10 +225,9 @@ if run:
216
  "mannequin_image_url": (mannequin_url or ""),
217
  "padding_ratio": str(padding_ratio),
218
  "identity_lock": "true" if identity_lock else "false",
219
- # SR
220
  "upscale": "true" if upscale else "false",
221
  "upscale_factor": upscale_factor,
222
- "upscale_stage": upscale_stage, # varsayılan both
223
  }
224
  files_payload = []
225
  if file_list:
@@ -229,7 +237,7 @@ if run:
229
  with st.spinner("Zincir çalışıyor…"):
230
  out = post_chain(data, files_payload)
231
 
232
- # ---- Çıktıları topla ----
233
  packshot_url = None
234
  try:
235
  imgs = out["packshot_step"]["result"]["images"]
@@ -240,7 +248,6 @@ if run:
240
 
241
  padded_url = None
242
  try:
243
- # backward compat
244
  padded_url = out.get("padding_step", {}).get("saved_file")
245
  if not padded_url:
246
  pads = out.get("packshot_step", {}).get("padded_urls") or out.get("padded_urls") or []
@@ -267,28 +274,8 @@ if run:
267
  ], default=[]) or []
268
  placed_sr_url = final_sr_list[0] if final_sr_list else None
269
 
270
- # ---- Görselleri göster + indir (tam kalite) ----
271
- p_url, p_bytes = show_tile(
272
- c_pack,
273
- "Packshot (SR)" if packshot_sr_url else "Packshot",
274
- packshot_sr_url or packshot_url,
275
- bg_rgb=white_bg,
276
- filename_stub="packshot"
277
- )
278
-
279
- pad_url, pad_bytes = show_tile(c_pad, "Padded", padded_url, bg_rgb=white_bg, filename_stub="padded")
280
-
281
- shown_final_url = placed_sr_url or placed_url
282
- m_url, m_bytes = show_tile(
283
- c_model,
284
- "Model Üzerinde (SR)" if placed_sr_url else "Model Üzerinde",
285
- shown_final_url,
286
- bg_rgb=white_bg,
287
- filename_stub="model"
288
- )
289
-
290
- # ---- Video (daima SR edilmiş final görseliyle) ----
291
- vurl, vbytes = None, None
292
  if to_video:
293
  src_for_video = placed_sr_url or placed_url
294
  if not src_for_video:
@@ -297,56 +284,100 @@ if run:
297
  with st.spinner("Video üretiliyor (SR kaynak)…"):
298
  v = post_video(src_for_video, duration=duration, resolution="768P", prompt_optimizer=False)
299
  vurl = (v.get("result") or {}).get("video", {}).get("url")
300
- if vurl:
301
- st.subheader("Video (SR kaynak)")
302
- st.video(vurl, format="video/mp4")
303
- vb = fetch_bytes(vurl)
304
- if vb:
305
- vbytes = vb
306
- st.download_button("Videoyu indir (MP4)", data=vb, file_name="ai_lightbox_video.mp4", mime="video/mp4")
307
- else:
308
- st.info("Video URL gelmedi.")
309
-
310
- # ---- Hepsini ZIP indir ----
311
- named = []
312
- if p_bytes:
313
- ext = infer_ext(p_bytes)
314
- named.append((f"packshot{ext}", p_bytes))
315
- if pad_bytes:
316
- ext = infer_ext(pad_bytes)
317
- named.append((f"padded{ext}", pad_bytes))
318
- if m_bytes:
319
- ext = infer_ext(m_bytes)
320
- named.append((f"model{ext}", m_bytes))
321
- if vbytes:
322
- named.append(("video.mp4", vbytes))
323
- if named:
324
- zip_bytes = make_zip(named)
325
- st.download_button("Hepsini ZIP indir", data=zip_bytes, file_name="ai_lightbox_outputs.zip", mime="application/zip")
326
-
327
- # ---- Debug ----
328
- with st.expander("Debug / Kaynak Alanlar"):
329
- st.json({
330
- "api_base": API_BASE,
331
- "has_auth_header": bool(HEADERS),
332
- "packshot_url": packshot_url,
333
- "packshot_sr_url": packshot_sr_url,
334
- "padded_url": padded_url,
335
- "placed_url": placed_url,
336
- "placed_sr_url": placed_sr_url,
337
- "video_url": vurl,
338
- })
339
 
340
  except requests.HTTPError as e:
341
  st.error(f"HTTP {e.response.status_code}")
342
  except Exception as e:
343
  st.error(f"Hata: {e}")
344
 
345
- # Küçük yardımcı: cache temizleme
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
346
  with st.sidebar:
347
  if st.button("Önbelleği temizle"):
348
  st.cache_data.clear()
349
  st.success("Önbellek temizlendi.")
 
 
 
350
  st.caption(f"API_BASE: {API_BASE}")
351
  if HF_TOKEN:
352
- st.caption("Auth: Bearer (aktif)")
 
8
  HEADERS = {"Authorization": f"Bearer {HF_TOKEN}"} if HF_TOKEN else {}
9
  PREVIEW_SIZE = int(os.getenv("PREVIEW_SIZE", "512")) # sadece UI kare önizleme
10
 
11
+ # ================= Session State =================
12
+ if "results" not in st.session_state:
13
+ st.session_state["results"] = None
14
+ if "job_counter" not in st.session_state:
15
+ st.session_state["job_counter"] = 0 # her çalıştırmada artar; download_button key'leri için
16
+
17
  # ================= Yardımcılar =================
18
  def _needs_auth(url: str) -> bool:
 
19
  return url.startswith(API_BASE) or url.startswith("outputs/")
20
 
21
  def backend_ok() -> bool:
 
27
  return False
28
 
29
  def post_chain(data: dict, files_payload):
 
30
  data = {**data, "to_video": "false"}
31
  r = requests.post(f"{API_BASE}/v1/tryon/chain", data=data, files=files_payload or None,
32
  headers=HEADERS, timeout=600)
 
38
  "image_url": image_url,
39
  "duration": duration,
40
  "resolution": resolution,
41
+ "prompt_optimizer": "true" if prompt_optimizer else "false",
 
42
  }
43
  r = requests.post(f"{API_BASE}/v1/video/from-image", data=payload, headers=HEADERS, timeout=600)
44
  r.raise_for_status()
 
64
  def image_size_from_bytes(b: bytes):
65
  try:
66
  im = Image.open(io.BytesIO(b))
67
+ return im.size
68
  except Exception:
69
  return None
70
 
 
102
  return v
103
  return default
104
 
105
+ def show_tile(col, title, url: str | None, bg_rgb=(255,255,255), filename_stub="image", key_prefix=""):
106
  col.subheader(title)
107
  if not url:
108
  placeholder = Image.new("RGBA", (PREVIEW_SIZE, PREVIEW_SIZE), (0, 0, 0, 0))
 
122
  col.caption(f"Gerçek çözünürlük: {sz[0]}×{sz[1]} px")
123
 
124
  ext = infer_ext(b)
 
125
  mime = "image/jpeg" if ext.lower() in [".jpg", ".jpeg"] else ("image/png" if ext.lower()==".png" else "image/webp")
126
+ col.download_button(
127
+ "İndir (tam kalite)",
128
+ data=b,
129
+ file_name=f"{filename_stub}{ext}",
130
+ mime=mime,
131
+ key=f"{key_prefix}_{filename_stub}_dl"
132
+ )
133
  return url, b
134
 
135
  def make_zip(named_bytes: list[tuple[str, bytes]]) -> bytes:
 
151
  col1, col2, col3, col4, col5, col6, col7 = st.columns([1.6, 1.6, 1.0, 1.3, 0.9, 1.1, 0.9])
152
 
153
  with col1:
154
+ file_list = st.file_uploader("Takı görseli (yükle)", type=["jpg","jpeg","png","webp"], accept_multiple_files=True, key="file_upl")
155
  with col2:
156
+ image_url = st.text_input("veya URL", key="image_url")
157
  with col3:
158
+ jewel_type = st.selectbox("Tür", ["auto","earring","ring","necklace","bracelet","pendant","brooch","set"], index=0, key="jewel_type")
159
  with col4:
160
  padding_ratio_val = st.number_input(
161
  "İçerik ölçeği (0.30–0.95)",
162
  min_value=0.30, max_value=0.95, value=0.50, step=0.01,
163
+ help="1200×1200 kare tuvalde ürünün uzun kenarı = 1200 × bu oran. 0.50 önerilir.",
164
+ key="padding_ratio"
165
  )
166
  with col5:
167
+ upscale = st.checkbox("Super Resolution", True, key="upscale")
168
  with col6:
169
+ upscale_stage = st.selectbox("SR aşaması", ["both","final","packshot"], index=0, help="Varsayılan: both", key="upscale_stage")
170
  with col7:
171
+ upscale_factor = st.selectbox("SR katsayı", ["2","4"], index=0, key="upscale_factor")
172
 
173
  colA, colB, colC, colD, colE = st.columns([1.0, 1.2, 1.2, 0.9, 1.0])
174
  with colA:
175
  st.info(f"Bağlantı: {'Online' if ok else 'Offline'}", icon="🔌")
176
  with colB:
177
+ identity_lock = st.checkbox("Identity lock (önerilir)", True, key="identity_lock")
178
  with colC:
179
+ to_video = st.checkbox("Video üret (SR kaynaklı)", False, key="to_video")
180
  with colD:
181
+ duration = st.selectbox("Süre (sn)", ["6","10"], index=0, key="duration")
182
  with colE:
183
+ run = st.button("Çalıştır", type="primary", use_container_width=True, key="run_btn")
184
 
185
+ mannequin_url = st.text_input("Manken URL (opsiyonel)", key="mannequin_url")
186
 
187
  # ================= Girdi Önizleme =================
188
  c_in, c_pack, c_pad, c_model = st.columns(4)
 
208
  else:
209
  show_tile(c_in, "Input", None)
210
 
211
+ # ================ Koş & Sonuçları Kaydet ================
212
  if run:
213
  if not ok:
214
  st.error("Backend erişilemiyor. API_BASE / TOKEN kontrol edin.")
 
225
  "mannequin_image_url": (mannequin_url or ""),
226
  "padding_ratio": str(padding_ratio),
227
  "identity_lock": "true" if identity_lock else "false",
 
228
  "upscale": "true" if upscale else "false",
229
  "upscale_factor": upscale_factor,
230
+ "upscale_stage": upscale_stage,
231
  }
232
  files_payload = []
233
  if file_list:
 
237
  with st.spinner("Zincir çalışıyor…"):
238
  out = post_chain(data, files_payload)
239
 
240
+ # ---- Çıktı URL'lerini topla ----
241
  packshot_url = None
242
  try:
243
  imgs = out["packshot_step"]["result"]["images"]
 
248
 
249
  padded_url = None
250
  try:
 
251
  padded_url = out.get("padding_step", {}).get("saved_file")
252
  if not padded_url:
253
  pads = out.get("packshot_step", {}).get("padded_urls") or out.get("padded_urls") or []
 
274
  ], default=[]) or []
275
  placed_sr_url = final_sr_list[0] if final_sr_list else None
276
 
277
+ # ---- Video (SR edilmiş final görseliyle) ----
278
+ vurl = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  if to_video:
280
  src_for_video = placed_sr_url or placed_url
281
  if not src_for_video:
 
284
  with st.spinner("Video üretiliyor (SR kaynak)…"):
285
  v = post_video(src_for_video, duration=duration, resolution="768P", prompt_optimizer=False)
286
  vurl = (v.get("result") or {}).get("video", {}).get("url")
287
+
288
+ # ---- Sonuçları session_state'e yaz ----
289
+ st.session_state["job_counter"] += 1
290
+ st.session_state["results"] = {
291
+ "packshot_url": packshot_url,
292
+ "packshot_sr_url": packshot_sr_url,
293
+ "padded_url": padded_url,
294
+ "placed_url": placed_url,
295
+ "placed_sr_url": placed_sr_url,
296
+ "video_url": vurl,
297
+ "api_base": API_BASE,
298
+ "has_auth_header": bool(HEADERS),
299
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300
 
301
  except requests.HTTPError as e:
302
  st.error(f"HTTP {e.response.status_code}")
303
  except Exception as e:
304
  st.error(f"Hata: {e}")
305
 
306
+ # ================ Sonuçları Göster (kalıcı) ================
307
+ res = st.session_state.get("results")
308
+ if res:
309
+ key_prefix = f"job{st.session_state['job_counter']}"
310
+ # Görsel sütunları: Input zaten solda
311
+ p_title = "Packshot (SR)" if res.get("packshot_sr_url") else "Packshot"
312
+ p_url, p_bytes = show_tile(
313
+ c_pack, p_title, res.get("packshot_sr_url") or res.get("packshot_url"),
314
+ bg_rgb=white_bg, filename_stub="packshot", key_prefix=key_prefix
315
+ )
316
+
317
+ pad_url, pad_bytes = show_tile(
318
+ c_pad, "Padded", res.get("padded_url"),
319
+ bg_rgb=white_bg, filename_stub="padded", key_prefix=key_prefix
320
+ )
321
+
322
+ shown_final_url = res.get("placed_sr_url") or res.get("placed_url")
323
+ m_title = "Model Üzerinde (SR)" if res.get("placed_sr_url") else "Model Üzerinde"
324
+ m_url, m_bytes = show_tile(
325
+ c_model, m_title, shown_final_url,
326
+ bg_rgb=white_bg, filename_stub="model", key_prefix=key_prefix
327
+ )
328
+
329
+ # Video
330
+ vurl = res.get("video_url")
331
+ vbytes = None
332
+ if vurl:
333
+ st.subheader("Video (SR kaynak)")
334
+ st.video(vurl, format="video/mp4")
335
+ vb = fetch_bytes(vurl)
336
+ if vb:
337
+ vbytes = vb
338
+ st.download_button(
339
+ "Videoyu indir (MP4)",
340
+ data=vb,
341
+ file_name="ai_lightbox_video.mp4",
342
+ mime="video/mp4",
343
+ key=f"{key_prefix}_video_dl"
344
+ )
345
+ else:
346
+ st.info("Video URL bulundu ama içerik indirilemedi.")
347
+
348
+ # Hepsini ZIP
349
+ named = []
350
+ if p_bytes: named.append((f"packshot{infer_ext(p_bytes)}", p_bytes))
351
+ if pad_bytes: named.append((f"padded{infer_ext(pad_bytes)}", pad_bytes))
352
+ if m_bytes: named.append((f"model{infer_ext(m_bytes)}", m_bytes))
353
+ if vbytes: named.append(("video.mp4", vbytes))
354
+ if named:
355
+ zip_bytes = make_zip(named)
356
+ st.download_button(
357
+ "Hepsini ZIP indir",
358
+ data=zip_bytes,
359
+ file_name="ai_lightbox_outputs.zip",
360
+ mime="application/zip",
361
+ key=f"{key_prefix}_zip_dl"
362
+ )
363
+
364
+ with st.expander("Debug / Kaynak Alanlar"):
365
+ st.json({
366
+ **{k: res.get(k) for k in [
367
+ "api_base","has_auth_header",
368
+ "packshot_url","packshot_sr_url",
369
+ "padded_url","placed_url","placed_sr_url","video_url"
370
+ ]}
371
+ })
372
+
373
+ # ================= Sidebar =================
374
  with st.sidebar:
375
  if st.button("Önbelleği temizle"):
376
  st.cache_data.clear()
377
  st.success("Önbellek temizlendi.")
378
+ if st.button("Son çıktıları temizle"):
379
+ st.session_state["results"] = None
380
+ st.success("Çıktılar temizlendi.")
381
  st.caption(f"API_BASE: {API_BASE}")
382
  if HF_TOKEN:
383
+ st.caption("Auth: Bearer (aktif)")