package store import ( "context" "errors" "fmt" "github.com/jackc/pgx/v5" "gitea.d-ma.be/mathias/tapir/internal/domain" ) // GetTranscript returns the shared, stored transcript for a video keyed by the // cross-user dedup key (provider, providerVideoID), and whether one exists // (ADR-021). It reads via the raw pool, NOT withUser: the table holds public // content with no user_id and no RLS policy, so it is shared across users by // construction. A stored SourceNone is a real hit (ok == true, HasText() == // false) — a known caption-less video, so the caller skips without re-fetching. func (s *Store) GetTranscript(ctx context.Context, provider, providerVideoID string) (domain.Transcript, bool, error) { var source, lang, content string err := s.pool.QueryRow(ctx, `SELECT source, COALESCE(language, ''), COALESCE(content, '') FROM transcripts WHERE provider = $1 AND provider_video_id = $2`, provider, providerVideoID).Scan(&source, &lang, &content) if errors.Is(err, pgx.ErrNoRows) { return domain.Transcript{}, false, nil } if err != nil { return domain.Transcript{}, false, fmt.Errorf("store: get transcript: %w", err) } return domain.Transcript{ Source: domain.TranscriptSource(source), Language: lang, Content: content, }, true, nil } // SaveTranscript upserts the shared transcript for (provider, providerVideoID). // Only terminal outcomes belong here: SourceCaptions (with text) or SourceNone // (no captions). A transient SourceRateLimited is rejected so persistence never // masks a 429 as a permanent absence — that stays a per-user retry (ADR-014). // Last write wins on conflict (a later re-fetch may correct an entry). It writes // via the raw pool, NOT withUser — public content, shared, non-RLS (ADR-021). func (s *Store) SaveTranscript(ctx context.Context, provider, providerVideoID string, t domain.Transcript) error { switch t.Source { case domain.SourceCaptions, domain.SourceNone: // terminal — persist case domain.SourceRateLimited: return fmt.Errorf("store: refusing to persist transient rate-limited transcript for %s/%s", provider, providerVideoID) default: return fmt.Errorf("store: invalid transcript source %q", t.Source) } _, err := s.pool.Exec(ctx, `INSERT INTO transcripts (provider, provider_video_id, source, language, content) VALUES ($1, $2, $3, NULLIF($4, ''), NULLIF($5, '')) ON CONFLICT (provider, provider_video_id) DO UPDATE SET source = EXCLUDED.source, language = EXCLUDED.language, content = EXCLUDED.content, fetched_at = NOW()`, provider, providerVideoID, string(t.Source), t.Language, t.Content) if err != nil { return fmt.Errorf("store: save transcript: %w", err) } return nil }