Spaces:
Sleeping
Sleeping
Vinh.Vu commited on
Commit Β·
d92592b
1
Parent(s): 3b7fd58
Update layout
Browse files- App/static/app.jsx +19 -41
- App/static/style.css +2 -0
App/static/app.jsx
CHANGED
|
@@ -46,29 +46,7 @@ function UploadArea({ file, onFileChange }) {
|
|
| 46 |
);
|
| 47 |
}
|
| 48 |
|
| 49 |
-
/* ββ Video Preview ββ */
|
| 50 |
-
function VideoPreview({ file, serverUrl }) {
|
| 51 |
-
const [localUrl, setLocalUrl] = useState(null);
|
| 52 |
-
|
| 53 |
-
useEffect(() => {
|
| 54 |
-
if (file) {
|
| 55 |
-
const url = URL.createObjectURL(file);
|
| 56 |
-
setLocalUrl(url);
|
| 57 |
-
return () => URL.revokeObjectURL(url);
|
| 58 |
-
}
|
| 59 |
-
setLocalUrl(null);
|
| 60 |
-
}, [file]);
|
| 61 |
|
| 62 |
-
const src = serverUrl || localUrl;
|
| 63 |
-
return (
|
| 64 |
-
<div className="video-preview">
|
| 65 |
-
{src
|
| 66 |
-
? <video controls src={src} key={src} />
|
| 67 |
-
: <div className="preview-placeholder">🎬</div>
|
| 68 |
-
}
|
| 69 |
-
</div>
|
| 70 |
-
);
|
| 71 |
-
}
|
| 72 |
|
| 73 |
/* ββ Status Spinner ββ */
|
| 74 |
function StatusIndicator({ status }) {
|
|
@@ -82,7 +60,8 @@ function StatusIndicator({ status }) {
|
|
| 82 |
}
|
| 83 |
|
| 84 |
/* ββ Video Comparison ββ */
|
| 85 |
-
function VideoComparison({ file, processedUrl, isProcessing }) {
|
|
|
|
| 86 |
const [localUrl, setLocalUrl] = useState(null);
|
| 87 |
|
| 88 |
useEffect(() => {
|
|
@@ -94,31 +73,31 @@ function VideoComparison({ file, processedUrl, isProcessing }) {
|
|
| 94 |
setLocalUrl(null);
|
| 95 |
}, [file]);
|
| 96 |
|
| 97 |
-
if (!
|
|
|
|
| 98 |
return (
|
| 99 |
<section className="video-compare">
|
| 100 |
-
<h2>Face Detection</h2>
|
| 101 |
-
<div className=
|
| 102 |
<div className="compare-item">
|
| 103 |
-
{localUrl
|
| 104 |
-
? <video controls src={localUrl} key={localUrl} />
|
| 105 |
-
: <div className="preview-placeholder">🎬</div>
|
| 106 |
-
}
|
| 107 |
<div className="compare-label original">Original</div>
|
| 108 |
</div>
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
|
|
|
|
|
|
| 116 |
</div>
|
| 117 |
-
|
| 118 |
</div>
|
| 119 |
</section>
|
| 120 |
);
|
| 121 |
-
}
|
| 122 |
|
| 123 |
/* ββ Face Row ββ */
|
| 124 |
function FaceRow({ face, index }) {
|
|
@@ -258,13 +237,12 @@ function App() {
|
|
| 258 |
{error && <div className="error-box">{error}</div>}
|
| 259 |
</div>
|
| 260 |
|
| 261 |
-
<VideoPreview file={file} serverUrl={result?.video_url} />
|
| 262 |
</section>
|
| 263 |
|
| 264 |
<VideoComparison
|
| 265 |
file={file}
|
| 266 |
processedUrl={result?.processed_url}
|
| 267 |
-
isProcessing={
|
| 268 |
/>
|
| 269 |
<ResultsPanel data={result} />
|
| 270 |
</>
|
|
|
|
| 46 |
);
|
| 47 |
}
|
| 48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
/* ββ Status Spinner ββ */
|
| 52 |
function StatusIndicator({ status }) {
|
|
|
|
| 60 |
}
|
| 61 |
|
| 62 |
/* ββ Video Comparison ββ */
|
| 63 |
+
const VideoComparison = React.memo(function VideoComparison({ file, processedUrl, isProcessing }) {
|
| 64 |
+
const videoRef = useRef(null);
|
| 65 |
const [localUrl, setLocalUrl] = useState(null);
|
| 66 |
|
| 67 |
useEffect(() => {
|
|
|
|
| 73 |
setLocalUrl(null);
|
| 74 |
}, [file]);
|
| 75 |
|
| 76 |
+
if (!localUrl) return null;
|
| 77 |
+
const showProcessed = processedUrl || isProcessing;
|
| 78 |
return (
|
| 79 |
<section className="video-compare">
|
| 80 |
+
<h2>{showProcessed ? 'Face Detection' : 'Uploaded Video'}</h2>
|
| 81 |
+
<div className={`compare-grid${showProcessed ? '' : ' single'}`}>
|
| 82 |
<div className="compare-item">
|
| 83 |
+
<video ref={videoRef} controls src={localUrl} />
|
|
|
|
|
|
|
|
|
|
| 84 |
<div className="compare-label original">Original</div>
|
| 85 |
</div>
|
| 86 |
+
{showProcessed && (
|
| 87 |
+
<div className="compare-item">
|
| 88 |
+
{processedUrl
|
| 89 |
+
? <video controls src={processedUrl} />
|
| 90 |
+
: <div className="preview-placeholder"><div className="spinner" /></div>
|
| 91 |
+
}
|
| 92 |
+
<div className="compare-label detected">
|
| 93 |
+
{processedUrl ? 'Detected Faces' : 'Generating\u2026'}
|
| 94 |
+
</div>
|
| 95 |
</div>
|
| 96 |
+
)}
|
| 97 |
</div>
|
| 98 |
</section>
|
| 99 |
);
|
| 100 |
+
});
|
| 101 |
|
| 102 |
/* ββ Face Row ββ */
|
| 103 |
function FaceRow({ face, index }) {
|
|
|
|
| 237 |
{error && <div className="error-box">{error}</div>}
|
| 238 |
</div>
|
| 239 |
|
|
|
|
| 240 |
</section>
|
| 241 |
|
| 242 |
<VideoComparison
|
| 243 |
file={file}
|
| 244 |
processedUrl={result?.processed_url}
|
| 245 |
+
isProcessing={submitting}
|
| 246 |
/>
|
| 247 |
<ResultsPanel data={result} />
|
| 248 |
</>
|
App/static/style.css
CHANGED
|
@@ -58,6 +58,8 @@ input[type="file"] { display: none; }
|
|
| 58 |
.video-compare { max-width: 1100px; margin: 40px auto 0; padding: 0 40px; }
|
| 59 |
.video-compare h2 { font-size: 20px; font-weight: 700; color: #fff; margin-bottom: 16px; }
|
| 60 |
.compare-grid { display: flex; gap: 24px; }
|
|
|
|
|
|
|
| 61 |
.compare-item { flex: 1; text-align: center; }
|
| 62 |
.compare-item video {
|
| 63 |
width: 100%; max-height: 360px; border-radius: 12px; border: 1px solid #1e1e35; background: #111122;
|
|
|
|
| 58 |
.video-compare { max-width: 1100px; margin: 40px auto 0; padding: 0 40px; }
|
| 59 |
.video-compare h2 { font-size: 20px; font-weight: 700; color: #fff; margin-bottom: 16px; }
|
| 60 |
.compare-grid { display: flex; gap: 24px; }
|
| 61 |
+
.compare-grid.single { justify-content: center; }
|
| 62 |
+
.compare-grid.single .compare-item { max-width: 640px; }
|
| 63 |
.compare-item { flex: 1; text-align: center; }
|
| 64 |
.compare-item video {
|
| 65 |
width: 100%; max-height: 360px; border-radius: 12px; border: 1px solid #1e1e35; background: #111122;
|