// Package ports defines the interfaces (hexagonal "ports") that the use-case // engine depends on. Concrete adapters live in internal/adapters. The engine // imports this package; it never imports a concrete provider, store, or model. // See docs/architecture/architecture.md. package ports import ( "context" "gitea.d-ma.be/mathias/tapir/internal/domain" ) // VideoSource is a video platform Tapir watches (YouTube, Vimeo). type VideoSource interface { // ListSubscriptions returns the channels the user subscribes to. ListSubscriptions(ctx context.Context, userID string) ([]domain.Subscription, error) // NewVideos returns videos newly seen for the given subscription. NewVideos(ctx context.Context, sub domain.Subscription) ([]domain.Video, error) // FetchTranscript resolves a transcript, captions-first. Returns a // Transcript with Source == SourceNone when none is available (not an error). FetchTranscript(ctx context.Context, v domain.Video) (domain.Transcript, error) } // Summarizer turns a transcript into a summary. Backed by the AI router // (local Primary -> BYO Fallback). See docs/use-cases/ai_routing.feature. type Summarizer interface { Summarize(ctx context.Context, v domain.Video, t domain.Transcript) (domain.Summary, error) } // TranscriptStore persists transcripts as shared, video-keyed public content // (ADR-021). It is keyed by the cross-user dedup key (provider, providerVideoID) // — the video's public identity, NOT Tapir's per-user videos.id — and holds only // public caption content, so it is deliberately NOT user-scoped: two users who // share a video share the one row. The engine reads it before any caption fetch // so re-analysis never re-touches YouTube (ADR-010/014). type TranscriptStore interface { // GetTranscript returns the stored transcript for a video and whether one // exists. A stored Source == SourceNone (captions permanently absent) is a // real hit: ok is true and HasText() is false, so callers skip without // re-fetching. A transient rate-limit is never stored, so it never appears // here as a false absence. GetTranscript(ctx context.Context, provider, providerVideoID string) (t domain.Transcript, ok bool, err error) // SaveTranscript upserts the transcript for (provider, providerVideoID). Only // terminal outcomes are persisted: SourceCaptions (with text) or SourceNone. // SourceRateLimited must NOT be passed — it is a per-user retry (ADR-014), not // a shared terminal state. SaveTranscript(ctx context.Context, provider, providerVideoID string, t domain.Transcript) error } // Sink delivers a summary to a destination (user store, brain, ...). // Implementations fail independently of one another. type Sink interface { // Name identifies the sink for delivery records ("store", "brain"). Name() string Deliver(ctx context.Context, s domain.Summary) error } // SecretStore resolves opaque references to secret material (OAuth tokens, // BYO keys). Backed by ESO/1Password. Tables store only the reference. type SecretStore interface { Get(ctx context.Context, ref string) (string, error) }