This commit is contained in:
2025-09-22 11:07:28 -04:00
parent 47f631fedb
commit aa5ad8327e
9 changed files with 821 additions and 620 deletions

View File

@@ -8,8 +8,8 @@
background: #181a20; background: #181a20;
} }
/* Video.js player */ /* Video.js player styles */
.video-js-mod { .video-player-fullscreen {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
@@ -17,98 +17,105 @@
height: 100vh; height: 100vh;
object-fit: contain; object-fit: contain;
background: #000; background: #000;
} }
.vjs-tech { .video-tech {
object-fit: contain; object-fit: contain;
} }
/* Main app layout */ /* Main app container */
.app-container { .app-container {
display: flex;
flex-direction: column;
height: 100vh; height: 100vh;
width: 100vw; width: 95vw;
margin: 0; max-width: 95vw;
margin: 0 auto;
padding: 0; padding: 0;
gap: 12px; gap: 0;
/* background: #181a20; */
} }
/* Controls section */
.flex-group { .controls-section {
display: flex; width: 100%;
flex-direction: column; /* or 'row' if you want horizontal grouping */ margin-bottom: 12px;
flex: 1 1 0;
min-width: 0;
}
/* Section containers */
.section-box-horiz {
overflow: visible;
flex-direction: row;
display: flex;
align-items: center;
justify-content: center;
}
/* Section containers */
.section-box {
flex: 0 0 5%;
overflow: visible;
/* background: #23272f; */
/* padding: 0;
box-sizing: border-box;
border-radius: 10px;
margin: 0 16px;
box-shadow: 0 2px 8px rgba(0,0,0,0.10); */
display: flex;
align-items: center;
justify-content: center;
}
.timeline-container {
flex: 0 0 24%;
overflow: visible;
background: #20232a;
padding: 0;
box-sizing: border-box;
border-radius: 10px;
margin: 0 16px;
box-shadow: 0 2px 8px rgba(0,0,0,0.10);
display: flex;
align-items: center;
justify-content: center;
}
.section-box:last-of-type {
flex: 1 1 68%;
overflow: hidden;
background: #23272f; background: #23272f;
padding: 0;
box-sizing: border-box;
border-radius: 10px; border-radius: 10px;
margin: 0 16px 16px 16px; padding: 16px 0;
box-shadow: 0 2px 8px rgba(0,0,0,0.10); text-align: center;
display: flex; box-sizing: border-box;
align-items: center;
justify-content: center;
} }
/* Responsive tweaks */ /* Control group wrapper */
.control-group {
display: inline-block;
vertical-align: middle;
min-width: 10%;
margin: 0 8px;
}
/* Status display section */
.status-section {
width: 100%;
margin-bottom: 12px;
}
/* Timeline container */
.timeline-section {
width: 100%;
height: 30vh;
min-height: 200px;
background: #20232a;
border-radius: 10px;
margin: 0 auto;
box-shadow: 0 2px 8px rgba(0,0,0,0.10);
text-align: center;
box-sizing: border-box;
margin-bottom: 12px;
padding: 0;
}
/* Video player section */
.video-section {
width: 100%;
height: 60vw;
margin-top: 12px;
min-height: 40vw;
background: #23272f;
border-radius: 10px;
margin: 0 auto;
box-shadow: 0 2px 8px rgba(0,0,0,0.10);
text-align: center;
box-sizing: border-box;
margin-bottom: 12px;
padding: 0;
}
/* Date range picker styles */
.date-picker-wrapper {
max-width: 98vw;
padding: 12px 8px;
border-radius: 8px;
}
/* Responsive styles */
@media (max-width: 600px) { @media (max-width: 600px) {
.app-container { .app-container {
gap: 6px; gap: 0;
} }
.section-box,
.timeline-container, .video-section,
.section-box:last-of-type { .timeline-section {
margin: 0 4px; margin: 0 4px 8px 4px;
border-radius: 6px; border-radius: 6px;
padding: 0;
} }
.date-range-selector {
.date-picker-wrapper {
max-width: 98vw; max-width: 98vw;
padding: 12px 8px; padding: 12px 8px;
border-radius: 8px; border-radius: 8px;
} }
.controls-section {
padding: 8px 0;
}
} }

View File

@@ -155,6 +155,8 @@ function App() {
newData["videos"] = newData["videos"].filter( newData["videos"] = newData["videos"].filter(
(vid) => vid["embed_scores"]["score"][1] >= floatValue (vid) => vid["embed_scores"]["score"][1] >= floatValue
); );
newData['threshold'] = floatValue;
console.log(newData['threshold'])
setDataResults(newData); setDataResults(newData);
} }
@@ -209,22 +211,59 @@ function App() {
); );
} }
} }
const handleTimelineClick = useCallback(
(path, timeoffset) => {
if (playerRef.current && playerInstanceRef.current) {
const player = playerInstanceRef.current;
// Memoize the timeline click handler console.log("Setting video source:", "api/" + path);
const handleTimelineClick = useCallback( console.log("Target time offset:", timeoffset);
(path, timeoffset) => {
if (playerRef.current && playerInstanceRef.current) { // Clear any existing source first
playerInstanceRef.current.src({ player.reset();
src: "api/" + path,
type: "video/mp4", player.src({
}); src: "api/" + path,
playerInstanceRef.current.on("loadedmetadata", () => { type: "video/mp4",
playerInstanceRef.current.currentTime(timeoffset); });
});
} player.load();
},
[] // Empty dependency array since it only uses playerRef // Add multiple event listeners for debugging
); player.one("loadedmetadata", () => {
console.log("Video metadata loaded");
console.log("Video duration:", player.duration());
player.currentTime(timeoffset);
// Try to play after setting time
const playPromise = player.play();
if (playPromise !== undefined) {
playPromise.then(() => {
console.log("Video started playing");
}).catch(error => {
console.error("Error playing video:", error);
});
}
});
player.one("error", (e) => {
console.error("Video error:", e);
console.error("Player error:", player.error());
});
player.one("loadstart", () => {
console.log("Load started");
});
player.one("canplay", () => {
console.log("Video can start playing");
});
} else {
console.error("Player ref not available");
}
},
[]
);
useEffect(() => { useEffect(() => {
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
@@ -263,12 +302,8 @@ function App() {
return ( return (
<div className="app-container"> <div className="app-container">
<div <div className="controls-section">
className={`section-box-horiz${ <div className="control-group">
drawerOpen ? " drawer-open" : ""
}`}
>
<div className="flex-group">
<CustomDateRangePicker <CustomDateRangePicker
startDate={startRange} startDate={startRange}
endDate={endRange} endDate={endRange}
@@ -276,52 +311,46 @@ function App() {
setEndRange={setEndRange} setEndRange={setEndRange}
/> />
</div> </div>
<div className="flex-group"> <div className="control-group">
<div style={{ position: "relative", width: "100%" }}> <input
<input type="text"
type="text" placeholder="Enter query"
placeholder="Enter query" value={queryText}
value={queryText} onChange={(e) => setQueryText(e.target.value)}
onChange={(e) => setQueryText(e.target.value)} onKeyDown={(e) => {
onKeyDown={(e) => { if (e.key === "Enter") handleResubmit();
if (e.key === "Enter") handleResubmit(); }}
}} style={{
style={{ // padding: "8px",
marginLeft: "16px", // borderRadius: "4px",
marginRight: "16px", // border: "1px solid #343a40",
padding: "8px", // color: "#fff",
borderRadius: "4px", // backgroundColor: "#23272f",
border: "1px solid #343a40", // width: "100%",
color: "#fff", // minWidth: 0,
backgroundColor: "#23272f", // boxSizing: "border-box",
width: "100%", fontSize: "1.1em",
minWidth: 0, // transition: "width 0.2s",
boxSizing: "border-box", }}
fontSize: "1.1em", ref={inputRef}
transition: "width 0.2s", size={Math.max(queryText.length, 25)}
}} />
ref={inputRef}
size={Math.max(queryText.length, 1)}
/>
</div>
</div> </div>
<div <div
className="flex-group" className="control-group"
style={{ visibility: queryChanged ? "hidden" : "visible" }} style={{ visibility: queryChanged ? "hidden" : "visible" }}
> >
<label <label style={{ color: "#fff" }}>Threshold:</label>
style={{
marginLeft: "8px",
marginRight: "8px",
color: "#fff",
}}
>
Threshold:
</label>
</div> </div>
<div <div
className="flex-group" className="control-group"
style={{ visibility: queryChanged ? "hidden" : "visible" }} style={{
display: "inline-block",
verticalAlign: "middle",
margin: "0 8px",
width: "120px",
visibility: queryChanged ? "hidden" : "visible",
}}
> >
<input <input
type="range" type="range"
@@ -339,15 +368,13 @@ function App() {
/> />
</div> </div>
<div <div
className="flex-group" className="control-group"
style={{ visibility: queryChanged ? "hidden" : "visible" }} style={{ visibility: queryChanged ? "hidden" : "visible" }}
> >
<span style={{ marginLeft: "8px", color: "#fff" }}> <span style={{ color: "#fff" }}>{sliderValue.toFixed(2)}</span>
{sliderValue.toFixed(2)}
</span>
</div> </div>
<div <div
className="flex-group" className="control-group"
style={{ visibility: queryChanged ? "visible" : "hidden" }} style={{ visibility: queryChanged ? "visible" : "hidden" }}
> >
<button <button
@@ -363,18 +390,20 @@ function App() {
</button> </button>
</div> </div>
</div> </div>
<div>
<div className="status-section">
<StatusesDisplayHUD statusMessages={statusMessages} /> <StatusesDisplayHUD statusMessages={statusMessages} />
</div> </div>
<div className="timeline-container"> <div className="timeline-section">
<EmbedTimeline <EmbedTimeline
chartRef={chartRef} chartRef={chartRef}
data_in={dataResults} data_in={dataResults}
onTimelineClick={handleTimelineClick} onTimelineClick={handleTimelineClick}
/> />
</div> </div>
<div className="section-box">
<div className="video-section vjs-16-9 vjs-fluid">
<VideoPlayer <VideoPlayer
videoRef={playerRef} videoRef={playerRef}
playerInstanceRef={playerInstanceRef} playerInstanceRef={playerInstanceRef}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,61 @@
import pickle
cache_files = ['/mnt/hdd_24tb_1/videos/ftp/leopards1/2025/09/12/embedding_scores@0.0@926895f71538e3683e9af0956af94cf4.pkl', '/mnt/hdd_24tb_1/videos/ftp/leopards1/2025/09/13/embedding_scores@0.0@926895f71538e3683e9af0956af94cf4.pkl', '/srv/ftp_tcc/leopards1/2025/09/14/embedding_scores@0.0@926895f71538e3683e9af0956af94cf4.pkl', '/srv/ftp_tcc/leopards1/2025/09/15/embedding_scores@0.0@926895f71538e3683e9af0956af94cf4.pkl', '/srv/ftp_tcc/leopards1/2025/09/16/embedding_scores@0.0@926895f71538e3683e9af0956af94cf4.pkl', '/srv/ftp_tcc/leopards1/2025/09/17/embedding_scores@0.0@926895f71538e3683e9af0956af94cf4.pkl', '/srv/ftp_tcc/leopards1/2025/09/18/embedding_scores@0.0@926895f71538e3683e9af0956af94cf4.pkl', '/srv/ftp_tcc/leopards1/2025/09/19/embedding_scores@0.0@926895f71538e3683e9af0956af94cf4.pkl']
import time
from datetime import timedelta, datetime
start_time = time.time()
all_c = list()
start_time = 1757892175.042
end_time = 1757894197.548
def check_if_overlap(start_1, end_1, start_2, end_2):
ff = sorted([[start_1, end_1],[start_2, end_2]],key=lambda x: x[0])
return ff[0][1] > ff[1][0]
def get_cache_data(start_time, end_time, cache_files):
targvals = [start_time, end_time]
for f in cache_files:
fold_start_time = datetime(*[int(x) for x in f.split('/')[-4:-1]]).timestamp()
fold_end_time = fold_start_time + 86400.0
has_overlap = check_if_overlap( start_time, end_time, fold_start_time, fold_end_time)
if not has_overlap:
continue
print(f'Loading {f}')
with open(f,'rb') as ff:
all_c.append(pickle.load(ff))
return all_c
st = time.time()
all_cach = get_cache_data(start_time, end_time, cache_files)
vids = list()
for c_c in all_cach:
vids.extend(c_c['videos'])
data_filt = list()
for v in vids:
if check_if_overlap( v['start_time'], v['end_time'], start_time, end_time):
data_filt.append(v)
time_vec = np.hstack([ np.asarray(f['embed_scores']['time'])+f['start_time'] for f in data_filt])
score_vec = np.hstack([f['embed_scores']['score'] for f in data_filt])
s_time, s_ind = np.unique(time_vec, return_index=True)
s_score = score_vec[s_ind]
out_array = np.asarray([s_time, s_score]).T.tolist()

View File

@@ -134,20 +134,22 @@ def calculate_embedding_score_in_folders(c_dirs, threshold, query = None, query_
# kwargs = [{'c_dir':x, 'threshold':threshold, 'query': query} for x in c_dirs] # kwargs = [{'c_dir':x, 'threshold':threshold, 'query': query} for x in c_dirs]
args = [(x, threshold, query, None, logger, redis_key) for x in c_dirs] args = [(x, threshold, query, None, logger, redis_key) for x in c_dirs]
logger.info(f"CALCULATING FOR {args}") # logger.info(f"CALCULATING FOR {args}")
with Pool(processes=8) as pool: with Pool(processes=8) as pool:
out = pool.starmap(calculate_embedding_score_in_folder, args) out = pool.starmap(calculate_embedding_score_in_folder, args)
logger.info(f"DONE CALCULATING FOR {args}") # logger.info(f"DONE CALCULATING FOR {args}")
for x in out: cache_files = list();
for x, cache_file_loc in out:
try: try:
result_list.extend(x['videos']) result_list.extend(x['videos'])
cache_files.append(cache_file_loc);
except Exception as e: except Exception as e:
print(e, x) print(e, x)
return {'videos':result_list} return {'videos':result_list, 'cache_file_locs': cache_files}
def collapse_scores_to_maxmin_avg(folder_scores): def collapse_scores_to_maxmin_avg(folder_scores):
@@ -215,7 +217,7 @@ def calculate_embedding_score_in_folder(og_dir, threshold, query = None, query_v
logger.info(f"LOADED EMBEDDING SCORE FROM CACHE {cache_file_loc}") logger.info(f"LOADED EMBEDDING SCORE FROM CACHE {cache_file_loc}")
message = {'task':'SCORE_CALC_IN_FOLDER_DONE', 'when': str(c_dir), 'time': dt.datetime.now().timestamp(), 'precomputed': True} message = {'task':'SCORE_CALC_IN_FOLDER_DONE', 'when': str(c_dir), 'time': dt.datetime.now().timestamp(), 'precomputed': True}
r.rpush(redis_key, json.dumps(message)) r.rpush(redis_key, json.dumps(message))
return video_json_info return (video_json_info, cache_file_loc)
else: else:
logger.info(f"CACHE FILE IS OLD, DELETING VEC REP FILE AND RECREATING {cache_file_loc}") logger.info(f"CACHE FILE IS OLD, DELETING VEC REP FILE AND RECREATING {cache_file_loc}")
os.remove( get_vec_rep_file_loc(c_dir)) os.remove( get_vec_rep_file_loc(c_dir))
@@ -288,8 +290,8 @@ def calculate_embedding_score_in_folder(og_dir, threshold, query = None, query_v
with open(cache_file_loc, 'wb') as f: with open(cache_file_loc, 'wb') as f:
logger.info(f"WRITING EMBEDDING SCORE TO CACHE {cache_file_loc}") logger.info(f"WRITING EMBEDDING SCORE TO CACHE {cache_file_loc}")
pickle.dump(to_write, f) pickle.dump(to_write, f)
logger.info(f"SAVED EMBEDDING SCORE TO CACHE {cache_file_loc}")
return to_write return (to_write, cache_file_loc)
def get_matching_file_given_filename(web_name, folder_scores): def get_matching_file_given_filename(web_name, folder_scores):

View File

@@ -82,9 +82,6 @@ async def videos_json(
folder_scores["breaks"] = ES.add_breaks_between_videos(folder_scores) folder_scores["breaks"] = ES.add_breaks_between_videos(folder_scores)
folder_scores['videos'] = ES.collapse_scores_to_maxmin_avg(folder_scores) folder_scores['videos'] = ES.collapse_scores_to_maxmin_avg(folder_scores)
session["folder_scores"] = folder_scores
return folder_scores return folder_scores

6
scripts/start_backend.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
cd /home/thebears/Web/Nuggets/SearchInterface/SearchBackend/
/home/thebears/envs/embedding_search_web_server/bin/fastapi dev --host 0.0.0.0 --port 5003 /home/thebears/Web/Nuggets/SearchInterface/SearchBackend/backend.py

6
scripts/start_frontend.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
cd /home/thebears/Web/Nuggets/SearchInterface/SearchFrontend/search_ui
npm run dev

6
scripts/start_vector.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
cd /home/thebears/Web/Nuggets/SearchInterface/VectorService
/home/thebears/envs/embedding_search_web_server/bin/fastapi dev --host 0.0.0.0 --port 5004 /home/thebears/Web/Nuggets/SearchInterface/VectorService/vector_service.py