From ae8485fdb0dbb33203d16f7fc8377379998b40c0 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Wed, 15 Apr 2026 23:26:21 +0200 Subject: [PATCH] Dont store overlapping chapterprints --- transcoder/src/chapters.go | 37 +++++++++++++++++++++---------------- transcoder/src/info.go | 6 ++++-- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/transcoder/src/chapters.go b/transcoder/src/chapters.go index 7ac79c00..6c083373 100644 --- a/transcoder/src/chapters.go +++ b/transcoder/src/chapters.go @@ -172,12 +172,6 @@ func (s *MetadataService) matchByOverlap( continue } - fpId, err := s.StoreChapterprint(ctx, fp) - if err != nil { - slog.WarnContext(ctx, "failed to store intro chapterprint", "err", err) - continue - } - slog.InfoContext(ctx, "Identified intro", "start", intro.StartFirst, "duration", intro.Duration) candidates = append(candidates, Chapter{ Id: info.Id, @@ -185,24 +179,18 @@ func (s *MetadataService) matchByOverlap( EndTime: float32(intro.StartFirst + intro.Duration), Name: "", Type: Intro, - FingerprintId: &fpId, + Fingerprint: fp, MatchAccuracy: new(int32(intro.Accuracy)), }) } for _, cred := range credits { - segData, err := ExtractSegment(fingerprint.End, cred.StartFirst, cred.StartFirst+cred.Duration) + fp, err := ExtractSegment(fingerprint.End, cred.StartFirst, cred.StartFirst+cred.Duration) if err != nil { slog.WarnContext(ctx, "failed to extract credits segment", "err", err) continue } - fpId, err := s.StoreChapterprint(ctx, segData) - if err != nil { - slog.WarnContext(ctx, "failed to store credits chapterprint", "err", err) - continue - } - endOffset := info.Duration - samplesToSec(len(fingerprint.End)) slog.InfoContext(ctx, "Identified credits", "start", endOffset+cred.StartFirst, "duration", cred.Duration, "end_offset", endOffset) candidates = append(candidates, Chapter{ @@ -211,7 +199,7 @@ func (s *MetadataService) matchByOverlap( EndTime: float32(endOffset + cred.StartFirst + cred.Duration), Name: "", Type: Credits, - FingerprintId: &fpId, + Fingerprint: fp, MatchAccuracy: new(int32(cred.Accuracy)), }) } @@ -234,10 +222,12 @@ func mergeChapters(info *MediaInfo, candidates []Chapter) []Chapter { merged := false for i := range chapters { - if absF32(chapters[i].StartTime-cand.StartTime) < MergeWindowSec { + if absF32(chapters[i].StartTime-cand.StartTime) < MergeWindowSec && + absF32(chapters[i].EndTime-cand.EndTime) < MergeWindowSec { if chapters[i].Type == Content { chapters[i].Type = cand.Type } + chapters[i].Fingerprint = cand.Fingerprint chapters[i].FingerprintId = cand.FingerprintId chapters[i].MatchAccuracy = cand.MatchAccuracy merged = true @@ -246,12 +236,19 @@ func mergeChapters(info *MediaInfo, candidates []Chapter) []Chapter { } if !merged { + if cand.StartTime < MergeWindowSec { + cand.StartTime = 0 + } + if absF32(float32(info.Duration)-cand.EndTime) < MergeWindowSec { + cand.EndTime = float32(info.Duration) + } chapters = insertChapter(chapters, Chapter{ Id: info.Id, StartTime: cand.StartTime, EndTime: cand.EndTime, Name: "", Type: cand.Type, + Fingerprint: cand.Fingerprint, FingerprintId: cand.FingerprintId, MatchAccuracy: cand.MatchAccuracy, }, info.Duration) @@ -340,6 +337,14 @@ func (s *MetadataService) saveChapters(ctx context.Context, infoId int32, chapte // Insert new chapters for _, c := range chapters { + if c.FingerprintId == nil && c.Fingerprint != nil { + fpId, err := s.StoreChapterprint(ctx, c.Fingerprint) + if err != nil { + slog.WarnContext(ctx, "failed to store intro chapterprint", "err", err) + } else { + c.FingerprintId = new(fpId) + } + } _, err = tx.Exec(ctx, `insert into gocoder.chapters(id, start_time, end_time, name, type, fingerprint_id, match_accuracy) values ($1, $2, $3, $4, $5, $6, $7)`, diff --git a/transcoder/src/info.go b/transcoder/src/info.go index 0bbbf4c5..5a00f9c5 100644 --- a/transcoder/src/info.go +++ b/transcoder/src/info.go @@ -152,10 +152,12 @@ type Chapter struct { EndTime float32 `json:"endTime" db:"end_time"` /// The name of this chapter. This should be a human-readable name that could be presented to the user. Name string `json:"name" db:"name"` - /// The type value is used to mark special chapters (openning/credits...) + /// The type value is used to mark special chapters (opening/credits...) Type ChapterType `json:"type" db:"type"` /// Reference to the chapterprint used for fingerprint matching. - FingerprintId *int32 `json:"-" db:"fingerprint_id"` + FingerprintId *int32 `json:"-" db:"fingerprint_id"` + /// Only used internally, never fetched from db. + Fingerprint []uint32 `json:"-" db:"-"` /// Accuracy of the fingerprint match (0-100). MatchAccuracy *int32 `json:"matchAccuracy,omitempty" db:"match_accuracy"` }