YACWC
This commit is contained in:
@@ -26,12 +26,15 @@
|
||||
|
||||
/* Main app container */
|
||||
.app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
width: 95vw;
|
||||
max-width: 95vw;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
gap: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Controls section */
|
||||
@@ -62,8 +65,7 @@
|
||||
/* Timeline container */
|
||||
.timeline-section {
|
||||
width: 100%;
|
||||
height: 30vh;
|
||||
min-height: 200px;
|
||||
min-height: 60px;
|
||||
background: #20232a;
|
||||
border-radius: 10px;
|
||||
margin: 0 auto;
|
||||
@@ -72,14 +74,15 @@
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 12px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
transition: flex 0.15s ease;
|
||||
}
|
||||
|
||||
/* Video player section */
|
||||
.video-section {
|
||||
flex: 1 1 0;
|
||||
min-height: 0;
|
||||
width: 100%;
|
||||
height: 60vw;
|
||||
margin-top: 12px;
|
||||
min-height: 40vw;
|
||||
background: #23272f;
|
||||
border-radius: 10px;
|
||||
margin: 0 auto;
|
||||
@@ -88,6 +91,8 @@
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 12px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
transition: flex 0.15s ease;
|
||||
}
|
||||
|
||||
/* Date range picker styles */
|
||||
|
||||
@@ -37,8 +37,19 @@ function App() {
|
||||
);
|
||||
const [endRange, setEndRange] = useState(new Date());
|
||||
// const [endRange, setEndRange] = useState(new Date(new Date().getTime() - 6 * 24 * 60 * 60 * 1000));
|
||||
const [queryText, setQueryText] = useState("Two clouded leopards being aggressive");
|
||||
const [queryText, setQueryText] = useState(
|
||||
"Two clouded leopards being aggressive",
|
||||
);
|
||||
const [sliderValue, setSliderValue] = useState(0);
|
||||
const [videoFlex, setVideoFlex] = useState(5.8); // ratio: video vs timeline
|
||||
|
||||
const handleVideoWheel = useCallback((e) => {
|
||||
e.preventDefault();
|
||||
setVideoFlex((prev) => {
|
||||
const delta = e.deltaY > 0 ? 0.3 : -0.3;
|
||||
return Math.max(3, Math.min(10, Math.max(1, prev + 0.5*delta)))
|
||||
});
|
||||
}, []);
|
||||
|
||||
// State to track last submitted values
|
||||
const [lastSubmitted, setLastSubmitted] = useState({
|
||||
@@ -157,22 +168,24 @@ function App() {
|
||||
queryText,
|
||||
selectedCamera,
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
function selectHighResFunc(selectedHighRes, setSelectedHighRes, toggleCheckbox = true) {
|
||||
function selectHighResFunc(
|
||||
selectedHighRes,
|
||||
setSelectedHighRes,
|
||||
sendState = false,
|
||||
) {
|
||||
console.log(selectedHighRes);
|
||||
const params = new URLSearchParams();
|
||||
if (sendState) {
|
||||
params.append("do_high_res", selectedHighRes);
|
||||
} else {
|
||||
params.append("do_high_res", !selectedHighRes);
|
||||
}
|
||||
authenticatedFetch("api/set_parameter?" + params.toString())
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
if (toggleCheckbox) {
|
||||
if (!sendState) {
|
||||
setSelectedHighRes(!selectedHighRes);
|
||||
}
|
||||
});
|
||||
@@ -267,8 +280,8 @@ function App() {
|
||||
tech.el_.addEventListener("loadstart", () => {
|
||||
// Force range request capability
|
||||
if (
|
||||
tech.el_.seekable &&
|
||||
tech.el_.seekable.length === 0
|
||||
tech.el_.seekable
|
||||
&& tech.el_.seekable.length === 0
|
||||
) {
|
||||
console.log(
|
||||
"Video doesn't support seeking - range headers may be needed",
|
||||
@@ -339,11 +352,7 @@ function App() {
|
||||
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
selectHighResFunc(
|
||||
selectedHighRes,
|
||||
setSelectedHighRes,
|
||||
false
|
||||
)
|
||||
selectHighResFunc(selectedHighRes, setSelectedHighRes, true);
|
||||
handleResubmit(params.get("test_mode") === "true");
|
||||
}, []);
|
||||
|
||||
@@ -367,10 +376,9 @@ function App() {
|
||||
),
|
||||
);
|
||||
setSliderMax(max_value);
|
||||
|
||||
original_data.current = data;
|
||||
setDataResults(data);
|
||||
updateDataAndValue(sliderValue)
|
||||
updateDataAndValue(sliderValue);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -601,20 +609,32 @@ function App() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="controls-section" style={{
|
||||
<div
|
||||
className="controls-section"
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: "12px",
|
||||
flexWrap: "nowrap",
|
||||
padding: "12px 16px",
|
||||
background: "linear-gradient(135deg, #2a2d3a 0%, #1f2129 100%)",
|
||||
padding: videoFlex >= 6 ? "0" : "12px 16px",
|
||||
background:
|
||||
"linear-gradient(135deg, #2a2d3a 0%, #1f2129 100%)",
|
||||
borderRadius: "12px",
|
||||
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
|
||||
border: "1px solid #3a3f4f",
|
||||
overflowX: "auto"
|
||||
}}>
|
||||
<div className="control-group" style={{ margin: 0, flexShrink: 0 }}>
|
||||
border: videoFlex >= 6 ? "none" : "1px solid #3a3f4f",
|
||||
overflowX: "auto",
|
||||
maxHeight: videoFlex >= 6 ? "0px" : "200px",
|
||||
overflow: "hidden",
|
||||
opacity: videoFlex >= 6 ? 0 : 1,
|
||||
marginBottom: videoFlex >= 6 ? 0 : 12,
|
||||
transition: "max-height 0.3s ease, opacity 0.3s ease, padding 0.3s ease, margin-bottom 0.3s ease",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="control-group"
|
||||
style={{ margin: 0, flexShrink: 0 }}
|
||||
>
|
||||
<button
|
||||
className="drawer-toggle"
|
||||
onClick={() => setDrawerOpen(!drawerOpen)}
|
||||
@@ -622,35 +642,41 @@ function App() {
|
||||
padding: "8px 12px",
|
||||
fontSize: "0.85em",
|
||||
minWidth: "70px",
|
||||
background: "linear-gradient(135deg, #4a90e2 0%, #357abd 100%)",
|
||||
background:
|
||||
"linear-gradient(135deg, #4a90e2 0%, #357abd 100%)",
|
||||
color: "white",
|
||||
border: "none",
|
||||
borderRadius: "6px",
|
||||
cursor: "pointer",
|
||||
fontWeight: "500",
|
||||
boxShadow: "0 2px 4px rgba(74, 144, 226, 0.3)",
|
||||
transition: "all 0.2s ease"
|
||||
transition: "all 0.2s ease",
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.target.style.transform = "translateY(-1px)";
|
||||
e.target.style.boxShadow = "0 4px 8px rgba(74, 144, 226, 0.4)";
|
||||
e.target.style.boxShadow =
|
||||
"0 4px 8px rgba(74, 144, 226, 0.4)";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.target.style.transform = "translateY(0)";
|
||||
e.target.style.boxShadow = "0 2px 4px rgba(74, 144, 226, 0.3)";
|
||||
e.target.style.boxShadow =
|
||||
"0 2px 4px rgba(74, 144, 226, 0.3)";
|
||||
}}
|
||||
>
|
||||
{drawerOpen ? "✕" : "Options"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="control-group" style={{
|
||||
<div
|
||||
className="control-group"
|
||||
style={{
|
||||
margin: 0,
|
||||
flexShrink: 0,
|
||||
background: "rgba(255, 255, 255, 0.05)",
|
||||
borderRadius: "6px",
|
||||
border: "1px solid rgba(255, 255, 255, 0.1)"
|
||||
}}>
|
||||
border: "1px solid rgba(255, 255, 255, 0.1)",
|
||||
}}
|
||||
>
|
||||
<CustomDateRangePicker
|
||||
startDate={startRange}
|
||||
endDate={endRange}
|
||||
@@ -659,7 +685,15 @@ function App() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="control-group" style={{ margin: 0, flex: "1 1 180px", minWidth: "180px", maxWidth: "300px" }}>
|
||||
<div
|
||||
className="control-group"
|
||||
style={{
|
||||
margin: 0,
|
||||
flex: "1 1 180px",
|
||||
minWidth: "180px",
|
||||
maxWidth: "300px",
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Enter search query..."
|
||||
@@ -680,21 +714,25 @@ function App() {
|
||||
fontWeight: "400",
|
||||
outline: "none",
|
||||
transition: "all 0.2s ease",
|
||||
boxShadow: "inset 0 2px 4px rgba(0, 0, 0, 0.1)"
|
||||
boxShadow: "inset 0 2px 4px rgba(0, 0, 0, 0.1)",
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
e.target.style.borderColor = "#4a90e2";
|
||||
e.target.style.boxShadow = "inset 0 2px 4px rgba(0, 0, 0, 0.1), 0 0 0 3px rgba(74, 144, 226, 0.15)";
|
||||
e.target.style.boxShadow =
|
||||
"inset 0 2px 4px rgba(0, 0, 0, 0.1), 0 0 0 3px rgba(74, 144, 226, 0.15)";
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
e.target.style.borderColor = "#3a3f4f";
|
||||
e.target.style.boxShadow = "inset 0 2px 4px rgba(0, 0, 0, 0.1)";
|
||||
e.target.style.boxShadow =
|
||||
"inset 0 2px 4px rgba(0, 0, 0, 0.1)";
|
||||
}}
|
||||
ref={inputRef}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="control-group" style={{
|
||||
<div
|
||||
className="control-group"
|
||||
style={{
|
||||
margin: 0,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
@@ -705,8 +743,9 @@ function App() {
|
||||
padding: "6px 10px",
|
||||
borderRadius: "6px",
|
||||
border: "1px solid rgba(255, 255, 255, 0.1)",
|
||||
flexShrink: 0
|
||||
}}>
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="do_high_res"
|
||||
@@ -720,13 +759,16 @@ function App() {
|
||||
style={{
|
||||
width: "14px",
|
||||
height: "14px",
|
||||
accentColor: "#4a90e2"
|
||||
accentColor: "#4a90e2",
|
||||
}}
|
||||
/>
|
||||
<span style={{ fontWeight: "500", whiteSpace: "nowrap" }}>HD</span>
|
||||
<span style={{ fontWeight: "500", whiteSpace: "nowrap" }}>
|
||||
HD
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style={{
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
@@ -735,14 +777,17 @@ function App() {
|
||||
padding: "6px 12px",
|
||||
borderRadius: "6px",
|
||||
border: "1px solid rgba(255, 255, 255, 0.1)",
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<label style={{
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<label
|
||||
style={{
|
||||
color: "#e1e5e9",
|
||||
fontSize: "0.85em",
|
||||
whiteSpace: "nowrap",
|
||||
fontWeight: "500"
|
||||
}}>
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Threshold:
|
||||
</label>
|
||||
<input
|
||||
@@ -755,35 +800,42 @@ function App() {
|
||||
style={{
|
||||
width: "80px",
|
||||
height: "6px",
|
||||
background: "linear-gradient(to right, #3a3f4f 0%, #4a90e2 100%)",
|
||||
background:
|
||||
"linear-gradient(to right, #3a3f4f 0%, #4a90e2 100%)",
|
||||
borderRadius: "3px",
|
||||
outline: "none",
|
||||
appearance: "none"
|
||||
appearance: "none",
|
||||
}}
|
||||
/>
|
||||
<span style={{
|
||||
<span
|
||||
style={{
|
||||
color: "#4a90e2",
|
||||
fontSize: "0.8em",
|
||||
minWidth: "35px",
|
||||
textAlign: "right",
|
||||
fontWeight: "600",
|
||||
fontFamily: "monospace"
|
||||
}}>
|
||||
fontFamily: "monospace",
|
||||
}}
|
||||
>
|
||||
{sliderValue.toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="control-group" style={{
|
||||
<div
|
||||
className="control-group"
|
||||
style={{
|
||||
margin: 0,
|
||||
visibility: queryChanged ? "visible" : "hidden",
|
||||
flexShrink: 0
|
||||
}}>
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<button
|
||||
onClick={handleResubmit}
|
||||
style={{
|
||||
padding: "8px 14px",
|
||||
fontSize: "0.85em",
|
||||
background: "linear-gradient(135deg, #28a745 0%, #20a73a 100%)",
|
||||
background:
|
||||
"linear-gradient(135deg, #28a745 0%, #20a73a 100%)",
|
||||
color: "white",
|
||||
border: "none",
|
||||
borderRadius: "6px",
|
||||
@@ -791,24 +843,33 @@ function App() {
|
||||
fontWeight: "600",
|
||||
boxShadow: "0 2px 4px rgba(40, 167, 69, 0.3)",
|
||||
transition: "all 0.2s ease",
|
||||
whiteSpace: "nowrap"
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.target.style.transform = "translateY(-1px)";
|
||||
e.target.style.boxShadow = "0 4px 8px rgba(40, 167, 69, 0.4)";
|
||||
e.target.style.boxShadow =
|
||||
"0 4px 8px rgba(40, 167, 69, 0.4)";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.target.style.transform = "translateY(0)";
|
||||
e.target.style.boxShadow = "0 2px 4px rgba(40, 167, 69, 0.3)";
|
||||
e.target.style.boxShadow =
|
||||
"0 2px 4px rgba(40, 167, 69, 0.3)";
|
||||
}}
|
||||
>
|
||||
🔄 Resubmit
|
||||
Resubmit
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="control-group" style={{ margin: 0, flexShrink: 0 }}>
|
||||
<div
|
||||
className="control-group"
|
||||
style={{ margin: 0, flexShrink: 0 }}
|
||||
>
|
||||
<button
|
||||
onClick={videoPlaying ? () => window.open("api/media_download/low") : undefined}
|
||||
onClick={
|
||||
videoPlaying
|
||||
? () => window.open("api/media_download/low")
|
||||
: undefined
|
||||
}
|
||||
disabled={!videoPlaying}
|
||||
style={{
|
||||
padding: "6px 10px",
|
||||
@@ -826,27 +887,36 @@ function App() {
|
||||
? "0 2px 4px rgba(23, 198, 113, 0.3)"
|
||||
: "0 1px 2px rgba(108, 117, 125, 0.2)",
|
||||
transition: "all 0.2s ease",
|
||||
whiteSpace: "nowrap"
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (videoPlaying) {
|
||||
e.target.style.transform = "translateY(-1px)";
|
||||
e.target.style.boxShadow = "0 4px 8px rgba(23, 198, 113, 0.4)";
|
||||
e.target.style.boxShadow =
|
||||
"0 4px 8px rgba(23, 198, 113, 0.4)";
|
||||
}
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (videoPlaying) {
|
||||
e.target.style.transform = "translateY(0)";
|
||||
e.target.style.boxShadow = "0 2px 4px rgba(23, 198, 113, 0.3)";
|
||||
e.target.style.boxShadow =
|
||||
"0 2px 4px rgba(23, 198, 113, 0.3)";
|
||||
}
|
||||
}}
|
||||
>
|
||||
DL Low-Res Video
|
||||
</button>
|
||||
</div>
|
||||
<div className="control-group" style={{ margin: 0, flexShrink: 0 }}>
|
||||
<div
|
||||
className="control-group"
|
||||
style={{ margin: 0, flexShrink: 0 }}
|
||||
>
|
||||
<button
|
||||
onClick={videoPlaying ? () => window.open("api/media_download/high") : undefined}
|
||||
onClick={
|
||||
videoPlaying
|
||||
? () => window.open("api/media_download/high")
|
||||
: undefined
|
||||
}
|
||||
disabled={!videoPlaying}
|
||||
style={{
|
||||
padding: "6px 10px",
|
||||
@@ -864,18 +934,20 @@ function App() {
|
||||
? "0 2px 4px rgba(0, 123, 255, 0.3)"
|
||||
: "0 1px 2px rgba(108, 117, 125, 0.2)",
|
||||
transition: "all 0.2s ease",
|
||||
whiteSpace: "nowrap"
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (videoPlaying) {
|
||||
e.target.style.transform = "translateY(-1px)";
|
||||
e.target.style.boxShadow = "0 4px 8px rgba(0, 123, 255, 0.4)";
|
||||
e.target.style.boxShadow =
|
||||
"0 4px 8px rgba(0, 123, 255, 0.4)";
|
||||
}
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (videoPlaying) {
|
||||
e.target.style.transform = "translateY(0)";
|
||||
e.target.style.boxShadow = "0 2px 4px rgba(0, 123, 255, 0.3)";
|
||||
e.target.style.boxShadow =
|
||||
"0 2px 4px rgba(0, 123, 255, 0.3)";
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -888,16 +960,17 @@ function App() {
|
||||
<StatusesDisplayHUD statusMessages={statusMessages} />
|
||||
</div>
|
||||
|
||||
<div className="timeline-section">
|
||||
<div className="timeline-section" style={{ flex: `${Math.max(1, 5 - videoFlex * 0.5)} 1 0`, minHeight: 60 }}>
|
||||
<EmbedTimeline
|
||||
chartRef={chartRef}
|
||||
data_in={dataResults}
|
||||
onTimelineClick={handleTimelineClick}
|
||||
authenticatedFetch={authenticatedFetch}
|
||||
hideDataZoom={videoFlex >= 6}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="video-section vjs-16-9 vjs-fluid">
|
||||
<div className="video-section" style={{ flex: `${videoFlex} 1 0` }} onWheel={handleVideoWheel}>
|
||||
<VideoPlayer
|
||||
videoRef={playerRef}
|
||||
playerInstanceRef={playerInstanceRef}
|
||||
|
||||
@@ -62,7 +62,7 @@ export default function CustomDateRangePicker({ startDate, endDate, setStartRang
|
||||
</button>
|
||||
|
||||
{showCalendar && (
|
||||
<div style={{ position: "absolute", zIndex: 10 }}>
|
||||
<div style={{ position: "fixed", zIndex: 9999 }}>
|
||||
<DateRange
|
||||
minDate={minDate}
|
||||
maxDate={maxDate}
|
||||
|
||||
@@ -7,6 +7,7 @@ const EmbedTimeline = React.memo(function EmbedTimeline({
|
||||
onTimelineClick,
|
||||
authenticatedFetch,
|
||||
markerTime,
|
||||
hideDataZoom,
|
||||
}) {
|
||||
// Use useRef instead of props/state to avoid re-renders
|
||||
const zoomed_range = useRef([null, null]);
|
||||
@@ -475,16 +476,18 @@ const EmbedTimeline = React.memo(function EmbedTimeline({
|
||||
},
|
||||
response: true,
|
||||
grid: {
|
||||
top: 30, // Remove top padding
|
||||
top: 30,
|
||||
left: 10,
|
||||
right: 20,
|
||||
bottom: 60,
|
||||
bottom: hideDataZoom ? 10 : 10,
|
||||
containLabel: true,
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: "slider",
|
||||
show: true,
|
||||
// show: !hideDataZoom,
|
||||
show: false,
|
||||
height: hideDataZoom ? 0 : undefined,
|
||||
xAxisIndex: [0],
|
||||
startValue: virtual_x_min,
|
||||
endValue: virtual_x_max,
|
||||
|
||||
@@ -22,14 +22,13 @@ const VideoPlayer = function VideoPlayer({
|
||||
controls: true,
|
||||
preload: "auto",
|
||||
autoplay: true,
|
||||
fluid: true,
|
||||
fill: true,
|
||||
html5: {
|
||||
vhs: {
|
||||
overrideNative: true, // use Video.js native range handling
|
||||
},
|
||||
nativeVideoTracks: false,
|
||||
},
|
||||
responsive: true,
|
||||
techOrder: ['html5'],
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user