From fd0bab67fb20c8a122f4ddf7755690e607aa4b07 Mon Sep 17 00:00:00 2001 From: Linrador <68631622+Linrador@users.noreply.github.com> Date: Fri, 29 May 2026 07:28:15 +0200 Subject: [PATCH] bugfixes --- backend/.env | 2 +- backend/training.go | 12 +- frontend/src/components/ui/RatingOverlay.tsx | 178 ++++++++++++------ .../src/components/ui/RecordJobActions.tsx | 2 +- frontend/src/components/ui/TrainingTab.tsx | 10 +- 5 files changed, 136 insertions(+), 68 deletions(-) diff --git a/backend/.env b/backend/.env index e33764f..c1ed77b 100644 --- a/backend/.env +++ b/backend/.env @@ -1,2 +1,2 @@ -HTTPS_ENABLED=1 +HTTPS_ENABLED=0 AUTH_RP_ORIGINS=https://l14pbbk95100006.tegdssd.de:9999,https://l14pbbk95100006.tegdssd.de:5173,https://localhost:9999,https://127.0.0.1:9999,https://10.0.1.25:9999,http://localhost:5173,http://127.0.0.1:5173,http://10.0.1.25:5173 \ No newline at end of file diff --git a/backend/training.go b/backend/training.go index e160909..88a46bf 100644 --- a/backend/training.go +++ b/backend/training.go @@ -25,7 +25,7 @@ import ( "time" ) -const trainingUncertainCandidateCount = 5 +const trainingUncertainCandidateCount = 10 type TrainingLabels struct { People []string `json:"people"` @@ -1078,7 +1078,7 @@ func trainingLabelsHandler(w http.ResponseWriter, r *http.Request) { trainingWriteJSON(w, http.StatusOK, defaultTrainingLabelsFromJSON()) } -const trainingImportVideoDefaultFrameCount = 8 +const trainingImportVideoDefaultFrameCount = 10 const trainingImportVideoMaxFrameCount = 30 type TrainingImportVideoRequest struct { @@ -3229,6 +3229,8 @@ func trainingCreateUncertainNextSampleWithProgress(startedAtMs int64, requestID missing = 1 } + candidateWindowTotal := trainingUncertainCandidateCount + const stepsPerCandidate = 4 totalSteps := missing*stepsPerCandidate + 1 @@ -3238,7 +3240,7 @@ func trainingCreateUncertainNextSampleWithProgress(startedAtMs int64, requestID attempt := i + 1 stepStart := i*stepsPerCandidate + 1 - prefix := fmt.Sprintf("Kandidat %d/%d: ", attempt, missing) + prefix := fmt.Sprintf("Kandidat %d/%d: ", attempt, candidateWindowTotal) candidate, err := trainingCreateUncertainCandidateWithProgress( root, @@ -3258,7 +3260,7 @@ func trainingCreateUncertainNextSampleWithProgress(startedAtMs int64, requestID stepStart+stepsPerCandidate-1, totalSteps, "", - fmt.Sprintf("Kandidat %d/%d fehlgeschlagen…", attempt, missing), + fmt.Sprintf("Kandidat %d/%d fehlgeschlagen…", attempt, candidateWindowTotal), ) continue @@ -3275,7 +3277,7 @@ func trainingCreateUncertainNextSampleWithProgress(startedAtMs int64, requestID fmt.Sprintf( "Kandidat %d/%d bewertet · Unsicherheit %.0f%%", attempt, - missing, + candidateWindowTotal, candidate.score*100, ), ) diff --git a/frontend/src/components/ui/RatingOverlay.tsx b/frontend/src/components/ui/RatingOverlay.tsx index ef95fce..75bce54 100644 --- a/frontend/src/components/ui/RatingOverlay.tsx +++ b/frontend/src/components/ui/RatingOverlay.tsx @@ -703,6 +703,56 @@ function firstNumber(obj: Record, keys: string[]): number | und return undefined } +function normalizeSegmentTimeValue( + key: string, + raw: unknown, + durationSeconds: number +): number | undefined { + const n = Number(raw) + if (!Number.isFinite(n) || n < 0) return undefined + + const k = key.toLowerCase() + const duration = Number(durationSeconds) + const hasDuration = Number.isFinite(duration) && duration > 0 + + if (k.endsWith('ms') || k.includes('millisecond')) { + return n / 1000 + } + + if (hasDuration) { + // Einige Analyse-Backends schreiben Millisekunden in generische Felder + // wie `start`, `end`, `time` oder `timestamp`. Ohne diese Korrektur + // würde der Player z.B. 95500 als 95.500 Sekunden statt 95,5 Sekunden suchen. + const asMillis = n / 1000 + if (n > duration + 1 && asMillis <= duration + 1) { + return asMillis + } + + // Ebenfalls unterstützen: normalisierte Positionen 0..1, falls ein + // Backend explizit ratio/percent/pct im Feldnamen verwendet. + if (n >= 0 && n <= 1 && /(ratio|percent|pct)$/.test(k)) { + return n * duration + } + } + + return n +} + +function firstSegmentTimeNumber( + obj: Record, + keys: string[], + durationSeconds: number +): number | undefined { + for (const key of keys) { + if (!(key in obj)) continue + + const n = normalizeSegmentTimeValue(key, obj[key], durationSeconds) + if (typeof n === 'number') return n + } + + return undefined +} + type PreviewSpriteMeta = { path: string count: number @@ -832,73 +882,81 @@ function readMetaDurationSeconds(metaRaw: unknown): number { return Number.isFinite(n) && n > 0 ? n : 0 } -function segmentPreviewTime(obj: Record): number | undefined { - const start = firstNumber(obj, [ +const RATING_SEGMENT_OPEN_PREROLL_SECONDS = 3 + +function segmentAnchorTime( + obj: Record, + durationSeconds: number +): number | undefined { + const start = firstSegmentTimeNumber(obj, [ 'startSec', 'startSeconds', 'start_s', 'start', 'from', 'startMs', - ]) + 'startRatio', + 'startPercent', + 'startPct', + ], durationSeconds) - const end = firstNumber(obj, [ + const marker = firstSegmentTimeNumber(obj, [ + 'markerTime', + 'markerSec', + 'markerSeconds', + 'time', + 't', + 'timestamp', + 'at', + 'timeMs', + 'timestampMs', + 'timeRatio', + 'timestampRatio', + 'timePercent', + 'timestampPercent', + 'timePct', + 'timestampPct', + ], durationSeconds) + + if (typeof start === 'number') return start + if (typeof marker === 'number') return marker + + return undefined +} + +function segmentPreviewTime( + obj: Record, + durationSeconds: number +): number | undefined { + const anchor = segmentAnchorTime(obj, durationSeconds) + if (typeof anchor === 'number') return anchor + + const end = firstSegmentTimeNumber(obj, [ 'endSec', 'endSeconds', 'end_s', 'end', 'to', 'endMs', - ]) + 'endRatio', + 'endPercent', + 'endPct', + ], durationSeconds) - const marker = firstNumber(obj, [ - 'markerTime', - 'markerSec', - 'markerSeconds', - 'time', - 't', - 'timestamp', - 'at', - 'timeMs', - 'timestampMs', - ]) - - if (typeof start === 'number' && typeof end === 'number' && end > start) { - return start + (end - start) / 2 - } - - if (typeof marker === 'number') return marker - if (typeof start === 'number') return start - - return undefined + return typeof end === 'number' ? end : undefined } -function segmentOpenTime(obj: Record): number | undefined { - const start = firstNumber(obj, [ - 'startSec', - 'startSeconds', - 'start_s', - 'start', - 'from', - 'startMs', - ]) +function segmentOpenTime( + obj: Record, + durationSeconds: number +): number | undefined { + const anchor = + segmentAnchorTime(obj, durationSeconds) ?? + segmentPreviewTime(obj, durationSeconds) - const marker = firstNumber(obj, [ - 'markerTime', - 'markerSec', - 'markerSeconds', - 'time', - 't', - 'timestamp', - 'at', - 'timeMs', - 'timestampMs', - ]) + if (typeof anchor !== 'number') return undefined - if (typeof start === 'number') return start - if (typeof marker === 'number') return marker - - return segmentPreviewTime(obj) + return Math.max(0, anchor - RATING_SEGMENT_OPEN_PREROLL_SECONDS) } function spriteFrameIndexFromTime( @@ -1009,8 +1067,8 @@ function segmentValueLabel(obj: Record): string | undefined { return score.toFixed(2).replace(/\.?0+$/, '') } -function segmentTimeLabel(obj: Record): string | undefined { - const start = firstNumber(obj, [ +function segmentTimeLabel(obj: Record, durationSeconds: number): string | undefined { + const start = firstSegmentTimeNumber(obj, [ 'startSec', 'startSeconds', 'start_s', @@ -1020,16 +1078,22 @@ function segmentTimeLabel(obj: Record): string | undefined { 'time', 'timestamp', 'startMs', - ]) + 'startRatio', + 'startPercent', + 'startPct', + ], durationSeconds) - const end = firstNumber(obj, [ + const end = firstSegmentTimeNumber(obj, [ 'endSec', 'endSeconds', 'end_s', 'end', 'to', 'endMs', - ]) + 'endRatio', + 'endPercent', + 'endPct', + ], durationSeconds) const duration = firstNumber(obj, [ 'durationSec', @@ -1250,8 +1314,8 @@ export function readRatingSegments(metaRaw: unknown): RatingSegment[] { .join(', ') : undefined - const previewTime = segmentPreviewTime(raw) - const openAtSec = segmentOpenTime(raw) + const previewTime = segmentPreviewTime(raw, durationSeconds) + const openAtSec = segmentOpenTime(raw, durationSeconds) const preview = spriteMeta && typeof previewTime === 'number' @@ -1263,7 +1327,7 @@ export function readRatingSegments(metaRaw: unknown): RatingSegment[] { return { key: String(raw.id ?? raw.key ?? `${index}`), title, - timeLabel: segmentTimeLabel(raw), + timeLabel: segmentTimeLabel(raw, durationSeconds), valueLabel: segmentValueLabel(raw), detail, tags: readSegmentTags(raw), diff --git a/frontend/src/components/ui/RecordJobActions.tsx b/frontend/src/components/ui/RecordJobActions.tsx index adc0123..f7ff840 100644 --- a/frontend/src/components/ui/RecordJobActions.tsx +++ b/frontend/src/components/ui/RecordJobActions.tsx @@ -255,7 +255,7 @@ export default function RecordJobActions({ jobId: String((job as any)?.id ?? ''), output, sourceFile: baseName(output), - count: 8, + count: 10, } // Falls TrainingTab gerade nicht gemountet ist. diff --git a/frontend/src/components/ui/TrainingTab.tsx b/frontend/src/components/ui/TrainingTab.tsx index 3c76b9c..23a66b9 100644 --- a/frontend/src/components/ui/TrainingTab.tsx +++ b/frontend/src/components/ui/TrainingTab.tsx @@ -1,4 +1,4 @@ -// frontend/src/components/ui/TrainingTab.tsx +// frontend\src\components\ui\TrainingTab.tsx 'use client' import { useCallback, useEffect, useMemo, useRef, useState, type CSSProperties } from 'react' @@ -2797,14 +2797,16 @@ export default function TrainingTab(props: { setLoadingPreviewCandidate('') + const TRAINING_IMPORT_FRAME_COUNT = 10 + const detail: PendingTrainingVideoImport = { jobId: String(raw?.jobId || '').trim(), output, sourceFile: String(raw?.sourceFile || '').trim(), - count: Number(raw?.count || 8), + count: Number(raw?.count || TRAINING_IMPORT_FRAME_COUNT), } - const importKey = `${detail.jobId || ''}|${detail.output}|${detail.count || 8}` + const importKey = `${detail.jobId || ''}|${detail.output}|${detail.count || TRAINING_IMPORT_FRAME_COUNT}` // Verhindert Doppelimport durch sessionStorage + CustomEvent. if (videoImportInFlightKeyRef.current === importKey) { @@ -2838,7 +2840,7 @@ export default function TrainingTab(props: { body: JSON.stringify({ jobId: detail.jobId, output: detail.output, - count: detail.count || 8, + count: detail.count || TRAINING_IMPORT_FRAME_COUNT, analysisRequestId: requestId, }), })