Files
silo-server/internal/scanner/types.go
T
0163df3683 [codex] Add IntroDB marker integration and dialogue-aware Chromaprint refinement (#57)
* docs(markers): design + implementation plans for multi-source markers & TheIntroDB contribution

* fix(markers): TheIntroDB read-path correctness (TVDB, real confidence, best candidate)

Honor TVDB ids in /media lookups (previously dropped — anime/TheTVDB-first
libraries got no markers), decode and use the real per-segment confidence and
submission_count instead of a hardcoded 0.9, and pick the most-submitted /
highest-confidence candidate when several are returned. Adds httptest coverage
for the introdb client and provider.

Phase 1 of docs/superpowers/plans/2026-06-06-marker-sources-and-contribution-implementation.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(markers): multi-source dispatch, per-provider config, per-segment provenance

Add marker_provider_config (per-provider fetch enable/priority + contribute
gates, contribution off by default) and a cached ProviderConfigStore. Add
Registry.FetchMerged: query all fetch-enabled providers concurrently and keep
the best candidate per segment (submission_count, then confidence, then fetch
priority), stamping each winning marker with its provider/algorithm. Thread
per-segment provenance through MarkerUpdatePayload and scanner.MarkerUpdate
(additive SegmentProvenance overrides) so a merged result writes correct
per-segment provider/confidence/algorithm; the legacy shared columns keep a
summary. The lazy-playback path now uses FetchMerged. With only TheIntroDB
enabled, behavior is unchanged.

Phase 2 of docs/superpowers/plans/2026-06-06-marker-sources-and-contribution-implementation.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(markers): TheIntroDB submission client, contribution audit, service engine

Add a markers.Submitter capability and implement it on the introdb provider
(POST /v3/submit, GET /v3/user/stats; key required, usage-limit aware, applies
the null start/end conventions). Add the marker_contributions audit table and a
value-hash-keyed ContributionStore for idempotency. Add ContributionService:
resolves enabled submitter providers, gates eligibility (never re-submit
online-sourced markers; auto runs require contribute_auto_local + scanner-intro
above the per-provider confidence threshold), checks idempotency, submits, and
records. Wired in main.go; no trigger yet (admin API and task follow).

Phase 3 of docs/superpowers/plans/2026-06-06-marker-sources-and-contribution-implementation.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(api): admin marker editing, contribution, and provider config endpoints

Add the RequireAdmin marker API: GET/PUT /admin/files/{id}/markers (read with
provenance; manual upsert where a segment object sets and null clears),
DELETE .../markers/{segment}, POST .../contribute and GET .../contributions,
plus GET/PUT /admin/markers/providers[/{provider}] and a
.../validate key-check returning user stats. Manual writes go through the
priority-gated UpsertMarkers (source=manual) and notify live sessions; a new
FileRepository.ClearMarkers nulls a segment's columns. Validation mirrors the
contribution rules.

Phase 4 of docs/superpowers/plans/2026-06-06-marker-sources-and-contribution-implementation.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(markers): daily auto-contribution task for local intro markers

Add ContributeMarkersTask (daily 04:00, after local detection): when a provider
has contribute_enabled + contribute_auto_local, page through episode files with
a scanner intro marker at/above the provider's confidence threshold (new
ContributionStore.CandidateLocalIntroFiles keyset query) and run them through
ContributionService with Auto=true. No-op when no provider opts in; idempotent
and resumable across runs.

Phase 5 of docs/superpowers/plans/2026-06-06-marker-sources-and-contribution-implementation.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(intromarkers): refine chromaprint starts with dialogue cues

* feat(markers): finish marker management backend

* feat(web): add marker editing UI

* feat(markers): use plugin marker providers

* fix(markers): address PR review feedback

* feat(player): show marker labels on seek hover

* fix(markers): type nullable marker mutation params

* feat(markers): audit marker edits and add permission

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 22:29:26 -04:00

157 lines
4.2 KiB
Go

package scanner
// ScanResult contains the outcome of scanning a media folder.
type ScanResult struct {
New int
Updated int
Unchanged int
Missing int
FilesDeleted int
MembershipsRemoved int
ItemsDeleted int
Errors int
EmptyRootGuarded bool
RootObservations []RootObservation
}
// FileHints contains the OSHash gathered during scanning.
type FileHints struct {
FileHash string // OSHash from xattr or computed
}
// ProbeData contains media file technical information from ffprobe.
type ProbeData struct {
CodecVideo string
CodecAudio string
Resolution string // 1080p, 2160p, etc.
AudioChannels int
HDR bool
Container string
Duration int // seconds
Bitrate int // kbps
VideoTracks []VideoTrackInfo
AudioTracks []AudioTrackInfo
SubtitleTracks []SubtitleTrackInfo
Chapters []ChapterInfo
}
// VideoTrackInfo describes a probed video track.
type VideoTrackInfo struct {
Title string
Codec string
DolbyVision string
Profile string
Level int
Width int
Height int
AspectRatio string
Interlaced bool
FrameRate string
Bitrate int
VideoRange string
ColorPrimaries string
ColorSpace string
ColorTransfer string
BitDepth int
PixelFormat string
ReferenceFrames int
}
// AudioTrackInfo describes a probed audio track.
type AudioTrackInfo struct {
Title string
EmbeddedTitle string
Language string
Codec string
Layout string
Channels int
Bitrate int
SampleRate int
BitDepth int
Default bool
}
// SubtitleTrackInfo describes an embedded subtitle track from probing.
type SubtitleTrackInfo struct {
Index int
Language string
Codec string
Title string
EmbeddedTitle string
Resolution string
Forced bool
Default bool
HearingImpaired bool
}
// ExternalSubtitleInfo describes a discovered sidecar subtitle file.
type ExternalSubtitleInfo struct {
Path string
Language string
Format string // srt, vtt, ass, ssa, sub
Title string
Forced bool
}
// ChapterInfo describes an embedded chapter extracted from ffprobe.
type ChapterInfo struct {
Index int
Title string
StartSeconds float64
EndSeconds float64
Source string
}
// IntroCreditsMarkers contains intro/credits timing from S3 markers.
type IntroCreditsMarkers struct {
IntroStart *float64
IntroEnd *float64
CreditsStart *float64
CreditsEnd *float64
}
// MarkerUpdate is the narrow marker-only update payload shared by scanner and analyzers.
type MarkerUpdate struct {
IntroStart *float64
IntroEnd *float64
CreditsStart *float64
CreditsEnd *float64
RecapStart *float64
RecapEnd *float64
PreviewStart *float64
PreviewEnd *float64
MarkersSource string
MarkersProvider *string
MarkersConfidence *float64
MarkersAlgorithm string
// Optional per-segment provenance overrides. When set for a segment,
// UpsertMarkers writes these source/provider/confidence/algorithm values
// for that segment instead of the shared Markers* fields above. Used by
// merged multi-provider results where each segment may come from a
// different provider/source class; left nil for single-source writes
// (scanner/s3/local).
IntroProvenance *SegmentProvenance
CreditsProvenance *SegmentProvenance
RecapProvenance *SegmentProvenance
PreviewProvenance *SegmentProvenance
}
// SegmentProvenance is a per-segment attribution override for MarkerUpdate.
// When Source is empty, the shared MarkerUpdate.MarkersSource applies.
type SegmentProvenance struct {
Source string
Provider *string
Confidence *float64
Algorithm string
}
// HasAnySegment reports whether the update would write at least one segment.
// An update with no segment bounds set is a no-op and skipped by UpsertMarkers.
func (u MarkerUpdate) HasAnySegment() bool {
return u.IntroStart != nil || u.IntroEnd != nil ||
u.CreditsStart != nil || u.CreditsEnd != nil ||
u.RecapStart != nil || u.RecapEnd != nil ||
u.PreviewStart != nil || u.PreviewEnd != nil
}