package credits import ( "context" "time" "github.com/ponzischeme89/memby/server/internal/store" ) // The adapter between this package's vocabulary and the store's. // // It exists so every rule above it — candidate generation, decay, clustering, confidence, // the queue — can be tested against literals with no database, which is most of why this // subsystem could be built without a Postgres to hand. // Postgres implements Repository, Database and BehaviourSource over the gateway's store. type Postgres struct { Store *store.Store } func (p Postgres) GetMarker( ctx context.Context, itemID, fingerprint string, ) (Marker, bool, error) { row, found, err := p.Store.CreditsMarker(ctx, itemID, fingerprint) if err != nil || !found { return Marker{}, false, err } return markerFrom(row), true, nil } func (p Postgres) SaveMarker(ctx context.Context, marker Marker) error { return p.Store.SaveCreditsMarker(ctx, store.CreditsMarkerRow{ ItemID: marker.ItemID, MediaFingerprint: marker.MediaFingerprint, CreditsStartMs: marker.CreditsStartMs, Confidence: marker.Confidence, DetectionMethod: marker.DetectionMethod, SeriesID: marker.SeriesID, Season: marker.Season, }) } func (p Postgres) SaveScanAttempt(ctx context.Context, attempt ScanAttempt) error { return p.Store.SaveCreditsScanAttempt(ctx, store.CreditsScanHistoryRow{ ItemID: attempt.ItemID, SeriesID: attempt.SeriesID, Season: attempt.Season, Episode: attempt.Episode, Reason: attempt.Reason, Priority: attempt.Priority, Outcome: attempt.Outcome, MarkerMs: attempt.MarkerMs, Confidence: attempt.Confidence, Method: attempt.Method, Frames: attempt.Frames, Error: attempt.Error, StartedAt: attempt.StartedAt, FinishedAt: attempt.FinishedAt, }) } func (p Postgres) RecentScanTimes( ctx context.Context, itemIDs []string, since time.Time, ) (map[string]time.Time, error) { return p.Store.RecentCreditsScanTimes(ctx, itemIDs, since) } func (p Postgres) SeasonMarkers( ctx context.Context, seriesID string, season, limit int, ) ([]Marker, error) { rows, err := p.Store.CreditsSeasonMarkers(ctx, seriesID, season, limit) if err != nil { return nil, err } out := make([]Marker, 0, len(rows)) for _, row := range rows { out = append(out, markerFrom(row)) } return out, nil } func (p Postgres) RecentWatches( ctx context.Context, since time.Time, limit int, ) ([]Watch, error) { rows, err := p.Store.CreditsRecentWatches(ctx, since, limit) if err != nil { return nil, err } out := make([]Watch, 0, len(rows)) for _, row := range rows { out = append(out, Watch{ UserKey: row.UserKey, SeriesID: row.SeriesID, Season: row.Season, Episode: row.Episode, WatchedAt: row.WatchedAt, Completed: row.Completed, }) } return out, nil } func (p Postgres) SeriesEpisodes( ctx context.Context, seriesIDs []string, ) ([]SeriesEpisode, error) { rows, err := p.Store.CreditsSeriesEpisodes(ctx, seriesIDs) if err != nil { return nil, err } out := make([]SeriesEpisode, 0, len(rows)) for _, row := range rows { out = append(out, SeriesEpisode{ ItemID: row.ItemID, SeriesID: row.SeriesID, Season: row.Season, Episode: row.Episode, }) } return out, nil } func (p Postgres) Stops(ctx context.Context, itemID string) ([]StopEvent, error) { rows, err := p.Store.CreditsStops(ctx, itemID) if err != nil { return nil, err } out := make([]StopEvent, 0, len(rows)) for _, row := range rows { out = append(out, StopEvent{ UserKey: row.UserKey, PositionMs: row.PositionMs, RuntimeMs: row.RuntimeMs, NextEpisode: row.NextEpisode, }) } return out, nil } func markerFrom(row store.CreditsMarkerRow) Marker { return Marker{ ItemID: row.ItemID, MediaFingerprint: row.MediaFingerprint, CreditsStartMs: row.CreditsStartMs, Confidence: row.Confidence, DetectionMethod: row.DetectionMethod, SeriesID: row.SeriesID, Season: row.Season, CreatedAt: row.CreatedAt, UpdatedAt: row.UpdatedAt, } }