MogensR commited on
Commit
07f632a
·
verified ·
1 Parent(s): 8420d76

Update ui.py

Browse files
Files changed (1) hide show
  1. ui.py +34 -29
ui.py CHANGED
@@ -3,6 +3,7 @@
3
  Modern UI for Video Background Replacer (PRO)
4
  - Clean, responsive Streamlit layout
5
  - Shows live stage progress and all errors during processing
 
6
  """
7
  import streamlit as st
8
  import os
@@ -13,6 +14,7 @@
13
  import time
14
 
15
  logger = logging.getLogger("Advanced Video Background Replacer")
 
16
 
17
  def tail_file(path: str, lines: int = 400) -> str:
18
  if not os.path.exists(path):
@@ -34,7 +36,7 @@ def read_file_bytes(path: str) -> bytes:
34
  return b""
35
 
36
  def _render_background_settings():
37
- # Available stock images (replace with your real logic or API)
38
  stock_images = {
39
  "Sunset Beach": "stock_images/sunset_beach.jpg",
40
  "Urban Office": "stock_images/urban_office.jpg",
@@ -93,7 +95,6 @@ def _render_background_settings():
93
  ai_ready = False
94
  if st.button("Generate Background", key="gen_bg_btn") and prompt:
95
  # TODO: Plug in real AI image generator
96
- # For now, stub with a purple placeholder
97
  background = Image.new("RGB", (512, 320), (64, 32, 96))
98
  st.session_state.generated_bg = background
99
  st.success("AI-generated background (stub). Replace with your generator!")
@@ -111,7 +112,10 @@ def render_ui(process_video_func):
111
  # --- Sidebar: System Status & Logs ---
112
  with st.sidebar:
113
  st.subheader("System Status")
 
114
  st.markdown("**Log file:** `/tmp/app.log`")
 
 
115
  if st.session_state.get('gpu_available', False):
116
  try:
117
  import torch
@@ -122,11 +126,8 @@ def render_ui(process_video_func):
122
  else:
123
  st.error("GPU: Not Available")
124
 
125
- # Controls
126
- st.checkbox("Auto-refresh log tail (every 2s)", key="auto_refresh_logs")
127
  st.number_input("Tail last N lines", min_value=50, max_value=5000, step=50, key="log_tail_lines")
128
-
129
- # Download logs
130
  log_bytes = read_file_bytes("/tmp/app.log")
131
  st.download_button(
132
  "Download Logs",
@@ -136,18 +137,10 @@ def render_ui(process_video_func):
136
  use_container_width=True,
137
  disabled=not bool(log_bytes)
138
  )
139
-
140
- # Log tail
141
  with st.expander("View Log Tail", expanded=True):
142
- # IMPORTANT: do NOT rerun while a job is active
143
- if st.session_state.get('auto_refresh_logs', False) and st.session_state.get('processing', False):
144
- st.info("⏸ Auto-refresh paused while processing…")
145
- elif st.session_state.get('auto_refresh_logs', False):
146
- time.sleep(2)
147
- st.rerun()
148
-
149
  log_text = tail_file("/tmp/app.log", st.session_state.get('log_tail_lines', 400))
150
- # Make sure this area scrolls and doesn't expand the page infinitely
151
  st.code(log_text, language="text")
152
 
153
  # --- Main UI: Two-Column Layout ---
@@ -167,6 +160,7 @@ def render_ui(process_video_func):
167
  try:
168
  uploaded_video.seek(0)
169
  video_bytes = uploaded_video.read()
 
170
  with video_preview_placeholder.container():
171
  st.video(video_bytes)
172
  except Exception as e:
@@ -179,9 +173,10 @@ def render_ui(process_video_func):
179
  with col2:
180
  background, bg_type = _render_background_settings()
181
  st.header("3. Process Video")
182
- status_box = st.empty() # <--- Status/progress area
 
183
  can_process = (
184
- uploaded_video is not None
185
  and not st.session_state.get('processing', False)
186
  and (background is not None)
187
  )
@@ -190,20 +185,29 @@ def progress_callback(msg):
190
  status_box.info(msg)
191
  logger.info(f"[STAGE] {msg}")
192
 
193
- # Don’t allow starting if auto-refresh is on (optional safety)
194
- if st.session_state.get('auto_refresh_logs', False):
195
- st.warning("Disable **Auto-refresh log tail** in the sidebar before processing.")
196
- can_process = False
197
-
198
  if st.button("Process Video", disabled=not can_process, use_container_width=True):
199
  try:
200
  logger.info("Process Video button clicked")
201
- if bg_type == "Image" and background is None:
202
- st.error("Please upload a background image")
203
- return
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  with st.spinner("Processing video..."):
205
- # bg_type always lowercase for pipeline
206
- success = process_video_func(uploaded_video, background, bg_type.lower(), progress_callback)
 
207
  if success:
208
  status_box.success("✅ Video processing complete!")
209
  st.success("Video processing complete!")
@@ -211,6 +215,7 @@ def progress_callback(msg):
211
  status_box.error("❌ Video processing failed. Check logs for details.")
212
  st.error("Video processing failed. Check logs for details.")
213
  except Exception as e:
 
214
  logger.error(f"[UI] Process video error: {e}", exc_info=True)
215
  status_box.error(f"Processing error: {str(e)}. Check logs for details.")
216
  st.error(f"Processing error: {str(e)}. Check logs for details.")
@@ -238,7 +243,7 @@ def progress_callback(msg):
238
  st.error(st.session_state.last_error)
239
  if st.button("Clear Error"):
240
  st.session_state.last_error = None
241
- st.rerun()
242
  except Exception as e:
243
  logger.error(f"[UI] Render UI error: {e}", exc_info=True)
244
  st.error(f"UI rendering error: {str(e)}. Check logs for details.")
 
3
  Modern UI for Video Background Replacer (PRO)
4
  - Clean, responsive Streamlit layout
5
  - Shows live stage progress and all errors during processing
6
+ - NO automatic reruns (no st.rerun) to avoid killing long-running jobs
7
  """
8
  import streamlit as st
9
  import os
 
14
  import time
15
 
16
  logger = logging.getLogger("Advanced Video Background Replacer")
17
+ UI_BUILD = "ui-2025-10-04-10-30Z" # visible in sidebar so you know this file is active
18
 
19
  def tail_file(path: str, lines: int = 400) -> str:
20
  if not os.path.exists(path):
 
36
  return b""
37
 
38
  def _render_background_settings():
39
+ # Available stock images (may not exist in Space)
40
  stock_images = {
41
  "Sunset Beach": "stock_images/sunset_beach.jpg",
42
  "Urban Office": "stock_images/urban_office.jpg",
 
95
  ai_ready = False
96
  if st.button("Generate Background", key="gen_bg_btn") and prompt:
97
  # TODO: Plug in real AI image generator
 
98
  background = Image.new("RGB", (512, 320), (64, 32, 96))
99
  st.session_state.generated_bg = background
100
  st.success("AI-generated background (stub). Replace with your generator!")
 
112
  # --- Sidebar: System Status & Logs ---
113
  with st.sidebar:
114
  st.subheader("System Status")
115
+ st.caption(f"UI build: {UI_BUILD}")
116
  st.markdown("**Log file:** `/tmp/app.log`")
117
+
118
+ # GPU indicator (best-effort)
119
  if st.session_state.get('gpu_available', False):
120
  try:
121
  import torch
 
126
  else:
127
  st.error("GPU: Not Available")
128
 
129
+ # Log tail controls (NO auto refresh, only manual button)
 
130
  st.number_input("Tail last N lines", min_value=50, max_value=5000, step=50, key="log_tail_lines")
 
 
131
  log_bytes = read_file_bytes("/tmp/app.log")
132
  st.download_button(
133
  "Download Logs",
 
137
  use_container_width=True,
138
  disabled=not bool(log_bytes)
139
  )
 
 
140
  with st.expander("View Log Tail", expanded=True):
141
+ if st.button("Refresh log", use_container_width=True, key="refresh_log_btn"):
142
+ st.session_state['_last_log_refresh'] = time.time()
 
 
 
 
 
143
  log_text = tail_file("/tmp/app.log", st.session_state.get('log_tail_lines', 400))
 
144
  st.code(log_text, language="text")
145
 
146
  # --- Main UI: Two-Column Layout ---
 
160
  try:
161
  uploaded_video.seek(0)
162
  video_bytes = uploaded_video.read()
163
+ st.session_state.video_bytes_cache = video_bytes # persist across reruns
164
  with video_preview_placeholder.container():
165
  st.video(video_bytes)
166
  except Exception as e:
 
173
  with col2:
174
  background, bg_type = _render_background_settings()
175
  st.header("3. Process Video")
176
+ status_box = st.empty() # progress area
177
+
178
  can_process = (
179
+ st.session_state.get('video_bytes_cache') is not None
180
  and not st.session_state.get('processing', False)
181
  and (background is not None)
182
  )
 
185
  status_box.info(msg)
186
  logger.info(f"[STAGE] {msg}")
187
 
 
 
 
 
 
188
  if st.button("Process Video", disabled=not can_process, use_container_width=True):
189
  try:
190
  logger.info("Process Video button clicked")
191
+
192
+ # Rebuild a file-like object from cached bytes so we don't depend on the upload widget object
193
+ import io
194
+ class _MemFile:
195
+ def __init__(self, name, data):
196
+ self.name = name
197
+ self._b = io.BytesIO(data)
198
+ def read(self):
199
+ self._b.seek(0)
200
+ return self._b.read()
201
+ def seek(self, pos):
202
+ self._b.seek(pos)
203
+
204
+ st.session_state.processing = True
205
+ mem_video = _MemFile("uploaded.mp4", st.session_state.video_bytes_cache)
206
+
207
  with st.spinner("Processing video..."):
208
+ success = process_video_func(mem_video, background, bg_type.lower(), progress_callback)
209
+
210
+ st.session_state.processing = False
211
  if success:
212
  status_box.success("✅ Video processing complete!")
213
  st.success("Video processing complete!")
 
215
  status_box.error("❌ Video processing failed. Check logs for details.")
216
  st.error("Video processing failed. Check logs for details.")
217
  except Exception as e:
218
+ st.session_state.processing = False
219
  logger.error(f"[UI] Process video error: {e}", exc_info=True)
220
  status_box.error(f"Processing error: {str(e)}. Check logs for details.")
221
  st.error(f"Processing error: {str(e)}. Check logs for details.")
 
243
  st.error(st.session_state.last_error)
244
  if st.button("Clear Error"):
245
  st.session_state.last_error = None
246
+ # No st.rerun() — the next interaction will refresh naturally
247
  except Exception as e:
248
  logger.error(f"[UI] Render UI error: {e}", exc_info=True)
249
  st.error(f"UI rendering error: {str(e)}. Check logs for details.")