This commit is contained in:
firestar5683
2026-08-01 18:43:53 -05:00
parent a44635ea6b
commit 165c960e52
5 changed files with 47 additions and 5 deletions
@@ -19,7 +19,7 @@ import { ModelManager } from "/assets/components/tools/model_manager.js?v=202603
import { LivePlots } from "/assets/components/tools/plots.js"
import { ThemeMaker } from "/assets/components/tools/theme_maker.js"
import { TestingGround } from "/assets/components/tools/testing_ground.js"
import { Tuning } from "/assets/components/tools/tuning.js?v=flm-saved-tunes-1"
import { Tuning } from "/assets/components/tools/tuning.js?v=flm-route-length-1"
import { Troubleshoot } from "/assets/components/tools/troubleshoot.js"
import { TmuxLog } from "/assets/components/tools/tmux.js"
import { ToggleControl } from "/assets/components/tools/toggles.js"
@@ -57,6 +57,16 @@ function safeCount(value) {
return Number.isFinite(n) ? n : 0
}
function formatRouteLength(route) {
const segmentCount = Math.max(0, Math.round(safeCount(route?.segmentCount)))
if (!segmentCount) return "Length unavailable"
const approximateMinutes = Math.max(1, Math.round(safeCount(route?.approxDurationSeconds) / 60) || segmentCount)
const duration = approximateMinutes >= 60
? `~${Math.floor(approximateMinutes / 60)}h ${approximateMinutes % 60}m`
: `~${approximateMinutes} min`
return `${segmentCount} segment${segmentCount === 1 ? "" : "s"} (${duration})`
}
function connectRouteUrl(routeName) {
const dongleId = String(state.connectDongleId || "").trim()
const routeId = String(routeName || "").trim()
@@ -1196,6 +1206,7 @@ export function Tuning() {
<span>
<strong>${route.timestampLabel}</strong>
<small>${route.name}</small>
<small>${formatRouteLength(route)}</small>
</span>
</label>
${() => connectRouteUrl(route.name) ? html`
@@ -259,6 +259,21 @@ class FakeDashboardAnalyzerProcess:
self.terminated = True
def test_route_inventory_counts_segments_without_video_probing(monkeypatch):
segments = [
SimpleNamespace(route_name=SimpleNamespace(time_str="route-new")),
SimpleNamespace(route_name=SimpleNamespace(time_str="route-new")),
SimpleNamespace(route_name=SimpleNamespace(time_str="route-new")),
SimpleNamespace(route_name=SimpleNamespace(time_str="route-old")),
]
monkeypatch.setattr(utilities, "get_all_segment_names", lambda _path: segments)
assert utilities.get_routes_with_segment_counts("/tmp/routes") == [
("route-old", 1),
("route-new", 3),
]
def test_dashboard_background_analysis_does_not_start_onroad(monkeypatch):
def fail_if_started(*args, **kwargs):
raise AssertionError("worker started onroad")
+9 -2
View File
@@ -5350,13 +5350,20 @@ def setup(app):
@app.route("/api/routes", methods=["GET"])
def list_routes():
def generate():
routes = [(path, name) for path in FOOTAGE_PATHS for name in utilities.get_routes_names(path)]
routes = [
(path, name, segment_count)
for path in FOOTAGE_PATHS
for name, segment_count in utilities.get_routes_with_segment_counts(path)
]
total = len(routes)
connect_dongle_id = params.get("StockDongleId", encoding="utf-8") or params.get("DongleId", encoding="utf-8") or ""
yield f"data: {json.dumps({'progress': 0, 'total': total, 'connectDongleId': connect_dongle_id})}\n\n"
with ThreadPoolExecutor(max_workers=10) as executor:
futures = {executor.submit(utilities.process_route, path, name): (path, name) for path, name in routes}
futures = {
executor.submit(utilities.process_route, path, name, segment_count): (path, name)
for path, name, segment_count in routes
}
for processed, future in enumerate(as_completed(futures), start=1):
try:
result = future.result()
+11 -2
View File
@@ -2895,6 +2895,13 @@ def get_routes_names(footage_path):
route_times = {segment.route_name.time_str for segment in segments}
return sorted(route_times, reverse=True)
def get_routes_with_segment_counts(footage_path):
route_counts = {}
for segment in get_all_segment_names(footage_path):
route_name = segment.route_name.time_str
route_counts[route_name] = route_counts.get(route_name, 0) + 1
return sorted(route_counts.items(), reverse=True)
def get_segments_in_route(route_time_str, footage_path):
return [
f"{segment.time_str}--{segment.segment_num}"
@@ -2930,7 +2937,7 @@ def normalize_theme_name(name, for_path=False):
return f"{normalized_parts[0]} ({' '.join(normalized_parts[1:])})".replace(" Week", "")
return ' '.join(normalized_parts).replace(" Week", "")
def process_route(footage_path, route_name):
def process_route(footage_path, route_name, segment_count=0):
segment_path = f"{footage_path}{route_name}--0"
qcamera_path = f"{segment_path}/qcamera.ts"
@@ -2954,7 +2961,9 @@ def process_route(footage_path, route_name):
"name": route_name,
"png": f"/thumbnails/{route_name}--0/preview.png",
"timestamp": route_timestamp_str,
"is_preserved": has_preserve_attr(segment_path)
"is_preserved": has_preserve_attr(segment_path),
"segmentCount": max(0, int(segment_count)),
"approxDurationSeconds": max(0, int(segment_count)) * 60,
}
def process_screen_recording(mp4):