bugfixes
This commit is contained in:
parent
1a559fe76d
commit
7df52ba9ef
@ -2462,6 +2462,7 @@ type analyzeLabelSegmentPoint struct {
|
||||
Label string
|
||||
Start float64
|
||||
End float64
|
||||
Time float64
|
||||
Score float64
|
||||
}
|
||||
|
||||
@ -2550,10 +2551,19 @@ func buildLabelContinuitySegmentsFromAnalyzeHits(
|
||||
score = 1
|
||||
}
|
||||
|
||||
markerTime := hit.Time
|
||||
if markerTime < 0 {
|
||||
markerTime = (start + end) / 2
|
||||
}
|
||||
if duration > 0 {
|
||||
markerTime = math.Max(0, math.Min(markerTime, duration))
|
||||
}
|
||||
|
||||
byLabel[key] = append(byLabel[key], analyzeLabelSegmentPoint{
|
||||
Label: label,
|
||||
Start: start,
|
||||
End: end,
|
||||
Time: markerTime,
|
||||
Score: score,
|
||||
})
|
||||
}
|
||||
@ -2580,6 +2590,8 @@ func buildLabelContinuitySegmentsFromAnalyzeHits(
|
||||
curEndMarker := points[0].End
|
||||
curScoreSum := points[0].Score
|
||||
curScoreCount := 1
|
||||
curMarkerTime := points[0].Time
|
||||
curBestScore := points[0].Score
|
||||
|
||||
for i := 1; i < len(points); i++ {
|
||||
n := points[i]
|
||||
@ -2598,6 +2610,10 @@ func buildLabelContinuitySegmentsFromAnalyzeHits(
|
||||
|
||||
curScoreSum += n.Score
|
||||
curScoreCount++
|
||||
if n.Score > curBestScore {
|
||||
curBestScore = n.Score
|
||||
curMarkerTime = n.Time
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
@ -2605,6 +2621,7 @@ func buildLabelContinuitySegmentsFromAnalyzeHits(
|
||||
curLabel,
|
||||
curStartMarker,
|
||||
curEndMarker,
|
||||
curMarkerTime,
|
||||
curScoreSum,
|
||||
curScoreCount,
|
||||
halfSample,
|
||||
@ -2620,12 +2637,15 @@ func buildLabelContinuitySegmentsFromAnalyzeHits(
|
||||
curEndMarker = n.End
|
||||
curScoreSum = n.Score
|
||||
curScoreCount = 1
|
||||
curMarkerTime = n.Time
|
||||
curBestScore = n.Score
|
||||
}
|
||||
|
||||
segment := makeLabelContinuitySegment(
|
||||
curLabel,
|
||||
curStartMarker,
|
||||
curEndMarker,
|
||||
curMarkerTime,
|
||||
curScoreSum,
|
||||
curScoreCount,
|
||||
halfSample,
|
||||
@ -2654,6 +2674,7 @@ func makeLabelContinuitySegment(
|
||||
label string,
|
||||
startMarker float64,
|
||||
endMarker float64,
|
||||
markerTime float64,
|
||||
scoreSum float64,
|
||||
scoreCount int,
|
||||
halfSample float64,
|
||||
@ -2680,6 +2701,13 @@ func makeLabelContinuitySegment(
|
||||
score = scoreSum / float64(scoreCount)
|
||||
}
|
||||
|
||||
if markerTime < 0 {
|
||||
markerTime = (start + end) / 2
|
||||
}
|
||||
if duration > 0 {
|
||||
markerTime = math.Max(0, math.Min(markerTime, duration))
|
||||
}
|
||||
|
||||
return aiSegmentMeta{
|
||||
Label: label,
|
||||
StartSeconds: start,
|
||||
@ -2689,6 +2717,8 @@ func makeLabelContinuitySegment(
|
||||
AutoSelected: true,
|
||||
Position: segmentPositionFromAnalyzeLabel(label),
|
||||
Tags: segmentTagsFromAnalyzeLabel(label),
|
||||
MarkerSeconds: markerTime,
|
||||
PreviewSeconds: markerTime,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -104,6 +104,8 @@ type aiSegmentMeta struct {
|
||||
AutoSelected bool `json:"autoSelected,omitempty"`
|
||||
Position string `json:"position,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
MarkerSeconds float64 `json:"markerSeconds,omitempty"`
|
||||
PreviewSeconds float64 `json:"previewSeconds,omitempty"`
|
||||
}
|
||||
|
||||
type aiAnalysisMeta struct {
|
||||
|
||||
@ -23,11 +23,11 @@ type aiRatingMeta struct {
|
||||
|
||||
const (
|
||||
// Normales Zusammenziehen sehr kurzer Pausen.
|
||||
ratingMaxSilentGapSec = 6.5
|
||||
ratingMaxSilentGapSec = 4.5
|
||||
|
||||
// Größere Lücken dürfen überbrückt werden,
|
||||
// wenn beide Seiten stark genug / thematisch ähnlich sind.
|
||||
ratingBridgeStrongGapSec = 35.0
|
||||
ratingBridgeStrongGapSec = 12.0
|
||||
|
||||
ratingMinSegmentDurationSec = 2.5
|
||||
)
|
||||
@ -743,7 +743,41 @@ func ratingBridgeStrengthFromSet(set ratingSignalSet) float64 {
|
||||
return strength
|
||||
}
|
||||
|
||||
func ratingPositionSetFromLabels(labels map[string]bool) map[string]bool {
|
||||
out := map[string]bool{}
|
||||
|
||||
for label := range labels {
|
||||
normalized := normalizeRatingSignalLabel(label)
|
||||
if strings.HasPrefix(normalized, "position:") {
|
||||
out[normalized] = true
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func ratingPositionSetsConflict(a map[string]bool, b map[string]bool) bool {
|
||||
ap := ratingPositionSetFromLabels(a)
|
||||
bp := ratingPositionSetFromLabels(b)
|
||||
|
||||
if len(ap) == 0 || len(bp) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
for label := range ap {
|
||||
if bp[label] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func ratingLabelsBridgeCompatible(a map[string]bool, b map[string]bool) bool {
|
||||
if ratingPositionSetsConflict(a, b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for la := range a {
|
||||
na := normalizeRatingSignalLabel(la)
|
||||
if na == "" {
|
||||
@ -765,9 +799,11 @@ func ratingLabelsBridgeCompatible(a map[string]bool, b map[string]bool) bool {
|
||||
as := ratingSignalSetFromLabels(a)
|
||||
bs := ratingSignalSetFromLabels(b)
|
||||
|
||||
if as.HasPosition && bs.HasPosition {
|
||||
return true
|
||||
}
|
||||
// Wichtig:
|
||||
// Unterschiedliche Positionen NICHT pauschal verbinden.
|
||||
// Vorher hat "Doggy" + "Reverse Cowgirl" als kompatibel gegolten,
|
||||
// nur weil beide irgendeine Position waren.
|
||||
|
||||
if as.HasBody && bs.HasBody {
|
||||
return true
|
||||
}
|
||||
@ -845,6 +881,94 @@ func mergeRatingActivitySegments(
|
||||
scoreSum float64
|
||||
scoreWeight float64
|
||||
labels map[string]bool
|
||||
positionWeights map[string]float64
|
||||
marker float64
|
||||
markerWeight float64
|
||||
}
|
||||
|
||||
var addRatingPositionWeight func(weights map[string]float64, label string, weight float64)
|
||||
|
||||
addRatingPositionWeight = func(weights map[string]float64, label string, weight float64) {
|
||||
label = strings.ToLower(strings.TrimSpace(label))
|
||||
if label == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if strings.HasPrefix(label, "combo:") {
|
||||
raw := strings.TrimPrefix(label, "combo:")
|
||||
for _, part := range strings.Split(raw, "+") {
|
||||
addRatingPositionWeight(weights, part, weight)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
normalized := normalizeRatingSignalLabel(label)
|
||||
if strings.HasPrefix(normalized, "position:") {
|
||||
weights[normalized] += weight
|
||||
}
|
||||
}
|
||||
|
||||
effectiveRatingLabelsForBlock := func(labels map[string]bool, positionWeights map[string]float64) map[string]bool {
|
||||
out := map[string]bool{}
|
||||
|
||||
bestPosition := ""
|
||||
bestWeight := -1.0
|
||||
|
||||
for label := range labels {
|
||||
normalized := normalizeRatingSignalLabel(label)
|
||||
if normalized == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(normalized, "position:") {
|
||||
w := positionWeights[normalized]
|
||||
if w <= 0 {
|
||||
w = ratingSegmentSeverity(normalized)
|
||||
}
|
||||
|
||||
if w > bestWeight {
|
||||
bestWeight = w
|
||||
bestPosition = normalized
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
out[normalized] = true
|
||||
}
|
||||
|
||||
if bestPosition != "" {
|
||||
out[bestPosition] = true
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
segmentMarker := func(s aiSegmentMeta) float64 {
|
||||
if s.PreviewSeconds > 0 {
|
||||
return s.PreviewSeconds
|
||||
}
|
||||
if s.MarkerSeconds > 0 {
|
||||
return s.MarkerSeconds
|
||||
}
|
||||
if s.EndSeconds > s.StartSeconds {
|
||||
return (s.StartSeconds + s.EndSeconds) / 2
|
||||
}
|
||||
return s.StartSeconds
|
||||
}
|
||||
|
||||
segmentMarkerWeight := func(s aiSegmentMeta) float64 {
|
||||
conf := ratingClamp01(s.Score)
|
||||
if conf <= 0 {
|
||||
conf = 0.50
|
||||
}
|
||||
|
||||
sev := ratingSegmentSeverity(s.Label)
|
||||
if sev <= 0 {
|
||||
sev = 0.50
|
||||
}
|
||||
|
||||
return conf * sev
|
||||
}
|
||||
|
||||
newBlock := func(s aiSegmentMeta) activityBlock {
|
||||
@ -857,12 +981,21 @@ func mergeRatingActivitySegments(
|
||||
conf = 0.50
|
||||
}
|
||||
|
||||
positionWeights := map[string]float64{}
|
||||
addRatingPositionWeight(positionWeights, s.Label, conf*math.Max(1, dur))
|
||||
|
||||
marker := segmentMarker(s)
|
||||
markerWeight := segmentMarkerWeight(s)
|
||||
|
||||
return activityBlock{
|
||||
start: s.StartSeconds,
|
||||
end: s.EndSeconds,
|
||||
scoreSum: conf * dur,
|
||||
scoreWeight: dur,
|
||||
labels: labels,
|
||||
positionWeights: positionWeights,
|
||||
marker: marker,
|
||||
markerWeight: markerWeight,
|
||||
}
|
||||
}
|
||||
|
||||
@ -872,7 +1005,8 @@ func mergeRatingActivitySegments(
|
||||
return aiSegmentMeta{}, false
|
||||
}
|
||||
|
||||
label := buildRatingComboLabel(b.labels)
|
||||
effectiveLabels := effectiveRatingLabelsForBlock(b.labels, b.positionWeights)
|
||||
label := buildRatingComboLabel(effectiveLabels)
|
||||
if label == "" {
|
||||
return aiSegmentMeta{}, false
|
||||
}
|
||||
@ -901,6 +1035,8 @@ func mergeRatingActivitySegments(
|
||||
AutoSelected: true,
|
||||
Position: segmentPositionFromAnalyzeLabel(label),
|
||||
Tags: segmentTagsFromAnalyzeLabel(label),
|
||||
MarkerSeconds: b.marker,
|
||||
PreviewSeconds: b.marker,
|
||||
}, true
|
||||
}
|
||||
|
||||
@ -956,8 +1092,17 @@ func mergeRatingActivitySegments(
|
||||
if conf <= 0 {
|
||||
conf = 0.50
|
||||
}
|
||||
|
||||
cur.scoreSum += conf * dur
|
||||
cur.scoreWeight += dur
|
||||
|
||||
addRatingPositionWeight(cur.positionWeights, n.Label, conf*math.Max(1, dur))
|
||||
}
|
||||
|
||||
nextMarkerWeight := segmentMarkerWeight(n)
|
||||
if nextMarkerWeight > cur.markerWeight {
|
||||
cur.markerWeight = nextMarkerWeight
|
||||
cur.marker = segmentMarker(n)
|
||||
}
|
||||
|
||||
addRatingLabelsFromSegment(cur.labels, n.Label)
|
||||
|
||||
@ -888,22 +888,13 @@ function segmentAnchorTime(
|
||||
obj: Record<string, unknown>,
|
||||
durationSeconds: number
|
||||
): number | undefined {
|
||||
const start = firstSegmentTimeNumber(obj, [
|
||||
'startSec',
|
||||
'startSeconds',
|
||||
'start_s',
|
||||
'start',
|
||||
'from',
|
||||
'startMs',
|
||||
'startRatio',
|
||||
'startPercent',
|
||||
'startPct',
|
||||
], durationSeconds)
|
||||
|
||||
const marker = firstSegmentTimeNumber(obj, [
|
||||
'markerTime',
|
||||
'markerSec',
|
||||
'previewSeconds',
|
||||
'previewSec',
|
||||
'previewTime',
|
||||
'markerSeconds',
|
||||
'markerSec',
|
||||
'markerTime',
|
||||
'time',
|
||||
't',
|
||||
'timestamp',
|
||||
@ -918,18 +909,41 @@ function segmentAnchorTime(
|
||||
'timestampPct',
|
||||
], durationSeconds)
|
||||
|
||||
if (typeof start === 'number') return start
|
||||
if (typeof marker === 'number') return marker
|
||||
|
||||
return undefined
|
||||
const start = firstSegmentTimeNumber(obj, [
|
||||
'startSec',
|
||||
'startSeconds',
|
||||
'start_s',
|
||||
'start',
|
||||
'from',
|
||||
'startMs',
|
||||
'startRatio',
|
||||
'startPercent',
|
||||
'startPct',
|
||||
], durationSeconds)
|
||||
|
||||
return typeof start === 'number' ? start : undefined
|
||||
}
|
||||
|
||||
function segmentPreviewTime(
|
||||
obj: Record<string, unknown>,
|
||||
durationSeconds: number
|
||||
): number | undefined {
|
||||
const anchor = segmentAnchorTime(obj, durationSeconds)
|
||||
if (typeof anchor === 'number') return anchor
|
||||
const marker = segmentAnchorTime(obj, durationSeconds)
|
||||
if (typeof marker === 'number') return marker
|
||||
|
||||
const start = firstSegmentTimeNumber(obj, [
|
||||
'startSec',
|
||||
'startSeconds',
|
||||
'start_s',
|
||||
'start',
|
||||
'from',
|
||||
'startMs',
|
||||
'startRatio',
|
||||
'startPercent',
|
||||
'startPct',
|
||||
], durationSeconds)
|
||||
|
||||
const end = firstSegmentTimeNumber(obj, [
|
||||
'endSec',
|
||||
@ -943,7 +957,14 @@ function segmentPreviewTime(
|
||||
'endPct',
|
||||
], durationSeconds)
|
||||
|
||||
return typeof end === 'number' ? end : undefined
|
||||
if (typeof start === 'number' && typeof end === 'number') {
|
||||
return Math.max(0, (start + end) / 2)
|
||||
}
|
||||
|
||||
if (typeof start === 'number') return start
|
||||
if (typeof end === 'number') return end
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
function segmentOpenTime(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user