MogensR commited on
Commit
30c74de
·
verified ·
1 Parent(s): 8e80ab9

Create ui.py

Browse files
Files changed (1) hide show
  1. ui.py +205 -0
ui.py ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Modern UI for Video Background Replacer
4
+ - Clean, responsive layout with Streamlit
5
+ - File uploaders, previews, and processing controls
6
+ - Log viewer and download buttons
7
+ """
8
+ import streamlit as st
9
+ import os
10
+ from pathlib import Path
11
+ from PIL import Image
12
+ import numpy as np
13
+ import logging
14
+ from datetime import datetime
15
+ import time
16
+
17
+ logger = logging.getLogger("Advanced Video Background Replacer")
18
+
19
+ # --- UI Helpers ---
20
+ def tail_file(path: str, lines: int = 400) -> str:
21
+ """Read last N lines from a text file efficiently."""
22
+ if not os.path.exists(path):
23
+ return "(log file not found)"
24
+ try:
25
+ with open(path, "r", encoding="utf-8", errors="replace") as f:
26
+ content = f.readlines()
27
+ return "".join(content[-lines:])
28
+ except Exception as e:
29
+ return f"(failed to read log: {e})"
30
+
31
+ def read_file_bytes(path: str) -> bytes:
32
+ """Read a file as bytes; return b'' if missing/error."""
33
+ try:
34
+ if not os.path.exists(path):
35
+ return b""
36
+ with open(path, "rb") as f:
37
+ return f.read()
38
+ except Exception:
39
+ return b""
40
+
41
+ # --- Render UI ---
42
+ def render_ui(process_video_func):
43
+ # --- Sidebar: System Status & Logs ---
44
+ with st.sidebar:
45
+ st.subheader("System Status")
46
+ st.markdown("**Log file:** `/tmp/app.log`")
47
+
48
+ # Log level control
49
+ log_level = st.selectbox(
50
+ "Log Level",
51
+ ["DEBUG", "INFO", "WARNING", "ERROR"],
52
+ index=1, # Default: INFO
53
+ key="log_level_select"
54
+ )
55
+ if log_level != st.session_state.get('log_level_name', 'INFO'):
56
+ from streamlit_app import set_log_level
57
+ set_log_level(log_level)
58
+
59
+ if st.session_state.get('gpu_available', False):
60
+ try:
61
+ import torch
62
+ dev = torch.cuda.get_device_name(0)
63
+ except Exception:
64
+ dev = "Detected (name unavailable)"
65
+ st.success(f"GPU: {dev}")
66
+ else:
67
+ st.error("GPU: Not Available")
68
+
69
+ # Log viewer controls
70
+ st.checkbox("Auto-refresh log tail (every 2s)", key="auto_refresh_logs")
71
+ st.number_input("Tail last N lines", min_value=50, max_value=5000, step=50, key="log_tail_lines")
72
+
73
+ # Download logs button
74
+ log_bytes = read_file_bytes("/tmp/app.log")
75
+ st.download_button(
76
+ "Download Logs",
77
+ data=log_bytes if log_bytes else b"Log file not available yet.",
78
+ file_name="app.log",
79
+ mime="text/plain",
80
+ use_container_width=True,
81
+ disabled=not bool(log_bytes)
82
+ )
83
+
84
+ # Log tail viewer
85
+ with st.expander("View Log Tail", expanded=True):
86
+ if st.session_state.get('auto_refresh_logs', False):
87
+ time.sleep(2)
88
+ st.rerun()
89
+ log_text = tail_file("/tmp/app.log", st.session_state.get('log_tail_lines', 400))
90
+ st.code(log_text, language="text")
91
+
92
+ # --- Main UI: Two-Column Layout ---
93
+ col1, col2 = st.columns([1, 1], gap="large")
94
+
95
+ # --- Column 1: Video Upload ---
96
+ with col1:
97
+ st.header("1. Upload Video")
98
+ uploaded_video = st.file_uploader(
99
+ "Upload Video",
100
+ type=["mp4", "mov", "avi", "mkv", "webm"],
101
+ key="video_uploader"
102
+ )
103
+
104
+ st.markdown("### Video Preview")
105
+ video_preview_placeholder = st.empty()
106
+ if uploaded_video is not None:
107
+ try:
108
+ uploaded_video.seek(0)
109
+ video_bytes = uploaded_video.read()
110
+ with video_preview_placeholder.container():
111
+ st.video(video_bytes)
112
+ except Exception as e:
113
+ logger.error(f"[UI] Video preview error: {e}", exc_info=True)
114
+ video_preview_placeholder.error(f"Cannot display video: {e}")
115
+ else:
116
+ video_preview_placeholder.empty()
117
+
118
+ # --- Column 2: Background & Processing ---
119
+ with col2:
120
+ st.header("2. Background Settings")
121
+ bg_type = st.radio(
122
+ "Select Background Type:",
123
+ ["Image", "Color"],
124
+ horizontal=True,
125
+ key="bg_type_radio"
126
+ )
127
+
128
+ # Background Image Upload
129
+ if bg_type == "Image":
130
+ bg_image = st.file_uploader(
131
+ "Upload Background Image",
132
+ type=["jpg", "png", "jpeg"],
133
+ key="bg_image_uploader"
134
+ )
135
+ bg_preview_placeholder = st.empty()
136
+ if bg_image is not None:
137
+ try:
138
+ bg_image_pil = Image.open(bg_image)
139
+ with bg_preview_placeholder.container():
140
+ st.image(bg_image_pil, caption="Selected Background", use_column_width=True)
141
+ except Exception as e:
142
+ logger.error(f"[UI] Background image error: {e}", exc_info=True)
143
+ bg_preview_placeholder.error(f"Cannot display image: {e}")
144
+ else:
145
+ bg_preview_placeholder.empty()
146
+
147
+ # Background Color Picker
148
+ else:
149
+ selected_color = st.color_picker(
150
+ "Choose Background Color",
151
+ st.session_state.get('bg_color', "#00FF00"),
152
+ key="color_picker"
153
+ )
154
+ if selected_color != st.session_state.get('cached_color'):
155
+ st.session_state.bg_color = selected_color
156
+ st.session_state.cached_color = selected_color
157
+ color_rgb = tuple(int(selected_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))
158
+ color_display = np.zeros((100, 100, 3), dtype=np.uint8)
159
+ color_display[:, :] = color_rgb
160
+ st.session_state.color_display_cache = color_display
161
+ color_preview_placeholder = st.empty()
162
+ if st.session_state.get('color_display_cache') is not None:
163
+ with color_preview_placeholder.container():
164
+ st.image(st.session_state.color_display_cache, caption="Selected Color", width=200)
165
+ else:
166
+ color_preview_placeholder.empty()
167
+
168
+ # --- Process Button ---
169
+ st.header("3. Process Video")
170
+ can_process = (uploaded_video is not None and not st.session_state.get('processing', False))
171
+ if st.button("Process Video", disabled=not can_process, use_container_width=True):
172
+ if bg_type == "Image" and bg_image is None:
173
+ st.error("Please upload a background image")
174
+ else:
175
+ background = bg_image if bg_type == "Image" else st.session_state.get('bg_color')
176
+ success = process_video_func(uploaded_video, bg_image, background, bg_type)
177
+ if success:
178
+ st.success("Video processing complete!")
179
+ else:
180
+ st.error("Video processing failed. Check logs for details.")
181
+
182
+ # --- Results Display ---
183
+ if st.session_state.get('processed_video_bytes') is not None:
184
+ st.markdown("---")
185
+ st.markdown("### Processed Video")
186
+ try:
187
+ st.video(st.session_state.processed_video_bytes)
188
+ st.download_button(
189
+ label="Download Processed Video",
190
+ data=st.session_state.processed_video_bytes,
191
+ file_name="processed_video.mp4",
192
+ mime="video/mp4",
193
+ use_container_width=True
194
+ )
195
+ except Exception as e:
196
+ logger.error(f"[UI] Display error: {e}", exc_info=True)
197
+ st.error(f"Display error: {e}")
198
+
199
+ # --- Error Display ---
200
+ if st.session_state.get('last_error'):
201
+ with st.expander("⚠️ Last Error", expanded=True):
202
+ st.error(st.session_state.last_error)
203
+ if st.button("Clear Error"):
204
+ st.session_state.last_error = None
205
+ st.rerun()