// Code generated by sqlc. DO NOT EDIT. // versions: // sqlc v1.31.1 // source: queries.sql package db import ( "context" "database/sql" "time" ) const countPendingAnimeFetchRetries = `-- name: CountPendingAnimeFetchRetries :one SELECT COUNT(*) FROM anime_fetch_retry WHERE next_retry_at <= CURRENT_TIMESTAMP ` func (q *Queries) CountPendingAnimeFetchRetries(ctx context.Context) (int64, error) { row := q.db.QueryRowContext(ctx, countPendingAnimeFetchRetries) var count int64 err := row.Scan(&count) return count, err } const createAPIToken = `-- name: CreateAPIToken :one INSERT INTO api_token (id, user_id, token_hash, name) VALUES (?, ?, ?, ?) RETURNING id, user_id, token_hash, name, created_at, last_used_at, revoked_at ` type CreateAPITokenParams struct { ID string `json:"id"` UserID string `json:"user_id"` TokenHash string `json:"token_hash"` Name string `json:"name"` } func (q *Queries) CreateAPIToken(ctx context.Context, arg CreateAPITokenParams) (ApiToken, error) { row := q.db.QueryRowContext(ctx, createAPIToken, arg.ID, arg.UserID, arg.TokenHash, arg.Name, ) var i ApiToken err := row.Scan( &i.ID, &i.UserID, &i.TokenHash, &i.Name, &i.CreatedAt, &i.LastUsedAt, &i.RevokedAt, ) return i, err } const createAuditLog = `-- name: CreateAuditLog :one INSERT INTO audit_log (id, user_id, action, resource_type, resource_id, ip, user_agent, metadata_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?) RETURNING id, occurred_at, user_id, "action", resource_type, resource_id, ip, user_agent, metadata_json ` type CreateAuditLogParams struct { ID string `json:"id"` UserID sql.NullString `json:"user_id"` Action string `json:"action"` ResourceType sql.NullString `json:"resource_type"` ResourceID sql.NullString `json:"resource_id"` Ip sql.NullString `json:"ip"` UserAgent sql.NullString `json:"user_agent"` MetadataJson sql.NullString `json:"metadata_json"` } func (q *Queries) CreateAuditLog(ctx context.Context, arg CreateAuditLogParams) (AuditLog, error) { row := q.db.QueryRowContext(ctx, createAuditLog, arg.ID, arg.UserID, arg.Action, arg.ResourceType, arg.ResourceID, arg.Ip, arg.UserAgent, arg.MetadataJson, ) var i AuditLog err := row.Scan( &i.ID, &i.OccurredAt, &i.UserID, &i.Action, &i.ResourceType, &i.ResourceID, &i.Ip, &i.UserAgent, &i.MetadataJson, ) return i, err } const createSession = `-- name: CreateSession :one INSERT INTO session (id, user_id, expires_at) VALUES (?, ?, ?) RETURNING id, user_id, expires_at, created_at ` type CreateSessionParams struct { ID string `json:"id"` UserID string `json:"user_id"` ExpiresAt time.Time `json:"expires_at"` } func (q *Queries) CreateSession(ctx context.Context, arg CreateSessionParams) (Session, error) { row := q.db.QueryRowContext(ctx, createSession, arg.ID, arg.UserID, arg.ExpiresAt) var i Session err := row.Scan( &i.ID, &i.UserID, &i.ExpiresAt, &i.CreatedAt, ) return i, err } const deleteAnimeFetchRetry = `-- name: DeleteAnimeFetchRetry :exec DELETE FROM anime_fetch_retry WHERE anime_id = ? ` func (q *Queries) DeleteAnimeFetchRetry(ctx context.Context, animeID int64) error { _, err := q.db.ExecContext(ctx, deleteAnimeFetchRetry, animeID) return err } const deleteContinueWatchingEntry = `-- name: DeleteContinueWatchingEntry :exec DELETE FROM continue_watching_entry WHERE user_id = ? AND anime_id = ? ` type DeleteContinueWatchingEntryParams struct { UserID string `json:"user_id"` AnimeID int64 `json:"anime_id"` } func (q *Queries) DeleteContinueWatchingEntry(ctx context.Context, arg DeleteContinueWatchingEntryParams) error { _, err := q.db.ExecContext(ctx, deleteContinueWatchingEntry, arg.UserID, arg.AnimeID) return err } const deleteExpiredFailedEpisodeProviderMappings = `-- name: DeleteExpiredFailedEpisodeProviderMappings :exec DELETE FROM episode_provider_mapping WHERE provider_show_id = '' AND failed_until <= CURRENT_TIMESTAMP ` func (q *Queries) DeleteExpiredFailedEpisodeProviderMappings(ctx context.Context) error { _, err := q.db.ExecContext(ctx, deleteExpiredFailedEpisodeProviderMappings) return err } const deleteExpiredJikanCache = `-- name: DeleteExpiredJikanCache :exec DELETE FROM jikan_cache WHERE datetime(expires_at) <= CURRENT_TIMESTAMP ` func (q *Queries) DeleteExpiredJikanCache(ctx context.Context) error { _, err := q.db.ExecContext(ctx, deleteExpiredJikanCache) return err } const deleteSession = `-- name: DeleteSession :exec DELETE FROM session WHERE id = ? ` func (q *Queries) DeleteSession(ctx context.Context, id string) error { _, err := q.db.ExecContext(ctx, deleteSession, id) return err } const deleteWatchListEntry = `-- name: DeleteWatchListEntry :exec DELETE FROM watch_list_entry WHERE user_id = ? AND anime_id = ? ` type DeleteWatchListEntryParams struct { UserID string `json:"user_id"` AnimeID int64 `json:"anime_id"` } func (q *Queries) DeleteWatchListEntry(ctx context.Context, arg DeleteWatchListEntryParams) error { _, err := q.db.ExecContext(ctx, deleteWatchListEntry, arg.UserID, arg.AnimeID) return err } const enqueueAnimeFetchRetry = `-- name: EnqueueAnimeFetchRetry :exec INSERT INTO anime_fetch_retry (anime_id, attempts, next_retry_at, last_error, updated_at) VALUES (?, 0, CURRENT_TIMESTAMP, ?, CURRENT_TIMESTAMP) ON CONFLICT (anime_id) DO UPDATE SET next_retry_at = CASE WHEN anime_fetch_retry.next_retry_at > CURRENT_TIMESTAMP THEN anime_fetch_retry.next_retry_at ELSE CURRENT_TIMESTAMP END, last_error = excluded.last_error, updated_at = CURRENT_TIMESTAMP ` type EnqueueAnimeFetchRetryParams struct { AnimeID int64 `json:"anime_id"` LastError string `json:"last_error"` } func (q *Queries) EnqueueAnimeFetchRetry(ctx context.Context, arg EnqueueAnimeFetchRetryParams) error { _, err := q.db.ExecContext(ctx, enqueueAnimeFetchRetry, arg.AnimeID, arg.LastError) return err } const getAPITokenByHash = `-- name: GetAPITokenByHash :one SELECT id, user_id, token_hash, name, created_at, last_used_at, revoked_at FROM api_token WHERE token_hash = ? AND revoked_at IS NULL LIMIT 1 ` func (q *Queries) GetAPITokenByHash(ctx context.Context, tokenHash string) (ApiToken, error) { row := q.db.QueryRowContext(ctx, getAPITokenByHash, tokenHash) var i ApiToken err := row.Scan( &i.ID, &i.UserID, &i.TokenHash, &i.Name, &i.CreatedAt, &i.LastUsedAt, &i.RevokedAt, ) return i, err } const getAllCachedAnime = `-- name: GetAllCachedAnime :many SELECT data FROM jikan_cache WHERE key LIKE 'anime:%' AND datetime(expires_at) > CURRENT_TIMESTAMP LIMIT 1000 ` func (q *Queries) GetAllCachedAnime(ctx context.Context) ([]string, error) { rows, err := q.db.QueryContext(ctx, getAllCachedAnime) if err != nil { return nil, err } var items []string for rows.Next() { var data string if err := rows.Scan(&data); err != nil { return nil, err } items = append(items, data) } if err := rows.Close(); err != nil { return nil, err } if err := rows.Err(); err != nil { return nil, err } return items, nil } const getAnime = `-- name: GetAnime :one SELECT id, title_original, image_url, created_at, title_english, title_japanese, airing, status, relations_synced_at, duration_seconds FROM anime WHERE id = ? LIMIT 1 ` func (q *Queries) GetAnime(ctx context.Context, id int64) (Anime, error) { row := q.db.QueryRowContext(ctx, getAnime, id) var i Anime err := row.Scan( &i.ID, &i.TitleOriginal, &i.ImageUrl, &i.CreatedAt, &i.TitleEnglish, &i.TitleJapanese, &i.Airing, &i.Status, &i.RelationsSyncedAt, &i.DurationSeconds, ) return i, err } const getAnimeNeedingRelationSync = `-- name: GetAnimeNeedingRelationSync :many WITH RECURSIVE sequel_chain AS ( SELECT a.id, a.title_original, a.relations_synced_at, w.updated_at as base_updated_at, 0 as depth FROM watch_list_entry w JOIN anime a ON w.anime_id = a.id WHERE w.status IN ('completed', 'watching') UNION SELECT a.id, a.title_original, a.relations_synced_at, sc.base_updated_at, sc.depth + 1 FROM sequel_chain sc JOIN anime_relation r ON sc.id = r.anime_id AND r.relation_type = 'Sequel' JOIN anime a ON r.related_anime_id = a.id WHERE sc.depth < 10 ) SELECT id, title_original FROM sequel_chain WHERE relations_synced_at IS NULL OR relations_synced_at < datetime('now', '-7 days') GROUP BY id, title_original ORDER BY MAX(base_updated_at) DESC, MIN(depth) ASC LIMIT 50 ` type GetAnimeNeedingRelationSyncRow struct { ID int64 `json:"id"` TitleOriginal string `json:"title_original"` } func (q *Queries) GetAnimeNeedingRelationSync(ctx context.Context) ([]GetAnimeNeedingRelationSyncRow, error) { rows, err := q.db.QueryContext(ctx, getAnimeNeedingRelationSync) if err != nil { return nil, err } var items []GetAnimeNeedingRelationSyncRow for rows.Next() { var i GetAnimeNeedingRelationSyncRow if err := rows.Scan(&i.ID, &i.TitleOriginal); err != nil { return nil, err } items = append(items, i) } if err := rows.Close(); err != nil { return nil, err } if err := rows.Err(); err != nil { return nil, err } return items, nil } const getAuditLogsForUser = `-- name: GetAuditLogsForUser :many SELECT id, occurred_at, user_id, "action", resource_type, resource_id, ip, user_agent, metadata_json FROM audit_log WHERE user_id = ? ORDER BY occurred_at DESC LIMIT ? ` type GetAuditLogsForUserParams struct { UserID sql.NullString `json:"user_id"` Limit int64 `json:"limit"` } func (q *Queries) GetAuditLogsForUser(ctx context.Context, arg GetAuditLogsForUserParams) ([]AuditLog, error) { rows, err := q.db.QueryContext(ctx, getAuditLogsForUser, arg.UserID, arg.Limit) if err != nil { return nil, err } var items []AuditLog for rows.Next() { var i AuditLog if err := rows.Scan( &i.ID, &i.OccurredAt, &i.UserID, &i.Action, &i.ResourceType, &i.ResourceID, &i.Ip, &i.UserAgent, &i.MetadataJson, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Close(); err != nil { return nil, err } if err := rows.Err(); err != nil { return nil, err } return items, nil } const getContinueWatchingEntries = `-- name: GetContinueWatchingEntries :many SELECT c.id, c.user_id, c.anime_id, c.current_episode, c.current_time_seconds, c.duration_seconds, c.created_at, c.updated_at, a.title_original, a.title_english, a.title_japanese, a.image_url, a.duration_seconds as anime_duration_seconds FROM continue_watching_entry c JOIN anime a ON c.anime_id = a.id WHERE c.user_id = ? ORDER BY c.updated_at DESC ` type GetContinueWatchingEntriesRow struct { ID string `json:"id"` UserID string `json:"user_id"` AnimeID int64 `json:"anime_id"` CurrentEpisode sql.NullInt64 `json:"current_episode"` CurrentTimeSeconds float64 `json:"current_time_seconds"` DurationSeconds sql.NullFloat64 `json:"duration_seconds"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` TitleOriginal string `json:"title_original"` TitleEnglish sql.NullString `json:"title_english"` TitleJapanese sql.NullString `json:"title_japanese"` ImageUrl string `json:"image_url"` AnimeDurationSeconds sql.NullFloat64 `json:"anime_duration_seconds"` } func (q *Queries) GetContinueWatchingEntries(ctx context.Context, userID string) ([]GetContinueWatchingEntriesRow, error) { rows, err := q.db.QueryContext(ctx, getContinueWatchingEntries, userID) if err != nil { return nil, err } var items []GetContinueWatchingEntriesRow for rows.Next() { var i GetContinueWatchingEntriesRow if err := rows.Scan( &i.ID, &i.UserID, &i.AnimeID, &i.CurrentEpisode, &i.CurrentTimeSeconds, &i.DurationSeconds, &i.CreatedAt, &i.UpdatedAt, &i.TitleOriginal, &i.TitleEnglish, &i.TitleJapanese, &i.ImageUrl, &i.AnimeDurationSeconds, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Close(); err != nil { return nil, err } if err := rows.Err(); err != nil { return nil, err } return items, nil } const getContinueWatchingEntry = `-- name: GetContinueWatchingEntry :one SELECT id, user_id, anime_id, current_episode, current_time_seconds, created_at, updated_at, duration_seconds FROM continue_watching_entry WHERE user_id = ? AND anime_id = ? LIMIT 1 ` type GetContinueWatchingEntryParams struct { UserID string `json:"user_id"` AnimeID int64 `json:"anime_id"` } func (q *Queries) GetContinueWatchingEntry(ctx context.Context, arg GetContinueWatchingEntryParams) (ContinueWatchingEntry, error) { row := q.db.QueryRowContext(ctx, getContinueWatchingEntry, arg.UserID, arg.AnimeID) var i ContinueWatchingEntry err := row.Scan( &i.ID, &i.UserID, &i.AnimeID, &i.CurrentEpisode, &i.CurrentTimeSeconds, &i.CreatedAt, &i.UpdatedAt, &i.DurationSeconds, ) return i, err } const getDueAnimeFetchRetries = `-- name: GetDueAnimeFetchRetries :many SELECT anime_id, attempts, next_retry_at, last_error, created_at, updated_at FROM anime_fetch_retry WHERE next_retry_at <= CURRENT_TIMESTAMP ORDER BY next_retry_at ASC LIMIT ? ` func (q *Queries) GetDueAnimeFetchRetries(ctx context.Context, limit int64) ([]AnimeFetchRetry, error) { rows, err := q.db.QueryContext(ctx, getDueAnimeFetchRetries, limit) if err != nil { return nil, err } var items []AnimeFetchRetry for rows.Next() { var i AnimeFetchRetry if err := rows.Scan( &i.AnimeID, &i.Attempts, &i.NextRetryAt, &i.LastError, &i.CreatedAt, &i.UpdatedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Close(); err != nil { return nil, err } if err := rows.Err(); err != nil { return nil, err } return items, nil } const getEpisodeAvailabilityCache = `-- name: GetEpisodeAvailabilityCache :one SELECT anime_id, data, next_refresh_at, retry_until_at, last_attempt_at, last_success_at, failure_count, last_error, updated_at FROM episode_availability_cache WHERE anime_id = ? LIMIT 1 ` func (q *Queries) GetEpisodeAvailabilityCache(ctx context.Context, animeID int64) (EpisodeAvailabilityCache, error) { row := q.db.QueryRowContext(ctx, getEpisodeAvailabilityCache, animeID) var i EpisodeAvailabilityCache err := row.Scan( &i.AnimeID, &i.Data, &i.NextRefreshAt, &i.RetryUntilAt, &i.LastAttemptAt, &i.LastSuccessAt, &i.FailureCount, &i.LastError, &i.UpdatedAt, ) return i, err } const getEpisodeProviderMapping = `-- name: GetEpisodeProviderMapping :one SELECT anime_id, provider, provider_show_id, failed_until, last_error, updated_at FROM episode_provider_mapping WHERE anime_id = ? AND provider = ? LIMIT 1 ` type GetEpisodeProviderMappingParams struct { AnimeID int64 `json:"anime_id"` Provider string `json:"provider"` } func (q *Queries) GetEpisodeProviderMapping(ctx context.Context, arg GetEpisodeProviderMappingParams) (EpisodeProviderMapping, error) { row := q.db.QueryRowContext(ctx, getEpisodeProviderMapping, arg.AnimeID, arg.Provider) var i EpisodeProviderMapping err := row.Scan( &i.AnimeID, &i.Provider, &i.ProviderShowID, &i.FailedUntil, &i.LastError, &i.UpdatedAt, ) return i, err } const getJikanCache = `-- name: GetJikanCache :one SELECT data FROM jikan_cache WHERE key = ? AND datetime(expires_at) > CURRENT_TIMESTAMP LIMIT 1 ` func (q *Queries) GetJikanCache(ctx context.Context, key string) (string, error) { row := q.db.QueryRowContext(ctx, getJikanCache, key) var data string err := row.Scan(&data) return data, err } const getJikanCacheStale = `-- name: GetJikanCacheStale :one SELECT data FROM jikan_cache WHERE key = ? LIMIT 1 ` func (q *Queries) GetJikanCacheStale(ctx context.Context, key string) (string, error) { row := q.db.QueryRowContext(ctx, getJikanCacheStale, key) var data string err := row.Scan(&data) return data, err } const getSession = `-- name: GetSession :one SELECT id, user_id, expires_at, created_at FROM session WHERE id = ? LIMIT 1 ` func (q *Queries) GetSession(ctx context.Context, id string) (Session, error) { row := q.db.QueryRowContext(ctx, getSession, id) var i Session err := row.Scan( &i.ID, &i.UserID, &i.ExpiresAt, &i.CreatedAt, ) return i, err } const getTrackedAiringAnimeIDsDueForEpisodeRefresh = `-- name: GetTrackedAiringAnimeIDsDueForEpisodeRefresh :many WITH tracked AS ( SELECT DISTINCT w.anime_id FROM watch_list_entry w JOIN anime a ON a.id = w.anime_id WHERE a.airing = 1 AND w.status IN ('watching', 'plan_to_watch') UNION SELECT DISTINCT c.anime_id FROM continue_watching_entry c JOIN anime a ON a.id = c.anime_id WHERE a.airing = 1 ) SELECT tracked.anime_id FROM tracked LEFT JOIN episode_availability_cache e ON e.anime_id = tracked.anime_id WHERE e.anime_id IS NULL OR e.next_refresh_at IS NULL OR e.next_refresh_at <= CURRENT_TIMESTAMP ORDER BY tracked.anime_id LIMIT ? ` func (q *Queries) GetTrackedAiringAnimeIDsDueForEpisodeRefresh(ctx context.Context, limit int64) ([]int64, error) { rows, err := q.db.QueryContext(ctx, getTrackedAiringAnimeIDsDueForEpisodeRefresh, limit) if err != nil { return nil, err } var items []int64 for rows.Next() { var anime_id int64 if err := rows.Scan(&anime_id); err != nil { return nil, err } items = append(items, anime_id) } if err := rows.Close(); err != nil { return nil, err } if err := rows.Err(); err != nil { return nil, err } return items, nil } const getUpcomingSeasons = `-- name: GetUpcomingSeasons :many WITH RECURSIVE sequel_chain AS ( SELECT w.anime_id as root_id, a.title_original as root_title, r.related_anime_id as current_id, 1 as depth, w.user_id FROM watch_list_entry w JOIN anime a ON w.anime_id = a.id JOIN anime_relation r ON w.anime_id = r.anime_id WHERE w.user_id = ? AND w.status IN ('completed', 'watching') AND r.relation_type = 'Sequel' UNION SELECT sc.root_id, sc.root_title, r.related_anime_id, sc.depth + 1, sc.user_id FROM sequel_chain sc JOIN anime_relation r ON sc.current_id = r.anime_id WHERE r.relation_type = 'Sequel' AND sc.depth < 10 ) SELECT DISTINCT related.id, related.title_original, related.image_url, related.created_at, related.title_english, related.title_japanese, related.airing, related.status, related.relations_synced_at, related.duration_seconds, sc.root_title AS prequel_title FROM sequel_chain sc JOIN anime related ON sc.current_id = related.id WHERE related.status IN ('Not yet aired', 'Currently Airing') AND NOT EXISTS ( SELECT 1 FROM watch_list_entry we WHERE we.user_id = sc.user_id AND we.anime_id = related.id ) ORDER BY related.id DESC ` type GetUpcomingSeasonsRow struct { ID int64 `json:"id"` TitleOriginal string `json:"title_original"` ImageUrl string `json:"image_url"` CreatedAt time.Time `json:"created_at"` TitleEnglish sql.NullString `json:"title_english"` TitleJapanese sql.NullString `json:"title_japanese"` Airing sql.NullBool `json:"airing"` Status sql.NullString `json:"status"` RelationsSyncedAt sql.NullTime `json:"relations_synced_at"` DurationSeconds sql.NullFloat64 `json:"duration_seconds"` PrequelTitle string `json:"prequel_title"` } func (q *Queries) GetUpcomingSeasons(ctx context.Context, userID string) ([]GetUpcomingSeasonsRow, error) { rows, err := q.db.QueryContext(ctx, getUpcomingSeasons, userID) if err != nil { return nil, err } var items []GetUpcomingSeasonsRow for rows.Next() { var i GetUpcomingSeasonsRow if err := rows.Scan( &i.ID, &i.TitleOriginal, &i.ImageUrl, &i.CreatedAt, &i.TitleEnglish, &i.TitleJapanese, &i.Airing, &i.Status, &i.RelationsSyncedAt, &i.DurationSeconds, &i.PrequelTitle, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Close(); err != nil { return nil, err } if err := rows.Err(); err != nil { return nil, err } return items, nil } const getUser = `-- name: GetUser :one SELECT id, username, password_hash, created_at, avatar_url FROM user WHERE id = ? LIMIT 1 ` func (q *Queries) GetUser(ctx context.Context, id string) (User, error) { row := q.db.QueryRowContext(ctx, getUser, id) var i User err := row.Scan( &i.ID, &i.Username, &i.PasswordHash, &i.CreatedAt, &i.AvatarUrl, ) return i, err } const getUserByUsername = `-- name: GetUserByUsername :one SELECT id, username, password_hash, created_at, avatar_url FROM user WHERE username = ? LIMIT 1 ` func (q *Queries) GetUserByUsername(ctx context.Context, username string) (User, error) { row := q.db.QueryRowContext(ctx, getUserByUsername, username) var i User err := row.Scan( &i.ID, &i.Username, &i.PasswordHash, &i.CreatedAt, &i.AvatarUrl, ) return i, err } const getUserWatchList = `-- name: GetUserWatchList :many SELECT e.id, e.user_id, e.anime_id, e.status, e.created_at, e.updated_at, e.current_episode, e.last_episode_at, e.current_time_seconds, a.title_original, a.title_english, a.title_japanese, a.image_url, a.airing FROM watch_list_entry e JOIN anime a ON e.anime_id = a.id WHERE e.user_id = ? ORDER BY e.updated_at DESC ` type GetUserWatchListRow struct { ID string `json:"id"` UserID string `json:"user_id"` AnimeID int64 `json:"anime_id"` Status string `json:"status"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` CurrentEpisode sql.NullInt64 `json:"current_episode"` LastEpisodeAt sql.NullTime `json:"last_episode_at"` CurrentTimeSeconds float64 `json:"current_time_seconds"` TitleOriginal string `json:"title_original"` TitleEnglish sql.NullString `json:"title_english"` TitleJapanese sql.NullString `json:"title_japanese"` ImageUrl string `json:"image_url"` Airing sql.NullBool `json:"airing"` } func (q *Queries) GetUserWatchList(ctx context.Context, userID string) ([]GetUserWatchListRow, error) { rows, err := q.db.QueryContext(ctx, getUserWatchList, userID) if err != nil { return nil, err } var items []GetUserWatchListRow for rows.Next() { var i GetUserWatchListRow if err := rows.Scan( &i.ID, &i.UserID, &i.AnimeID, &i.Status, &i.CreatedAt, &i.UpdatedAt, &i.CurrentEpisode, &i.LastEpisodeAt, &i.CurrentTimeSeconds, &i.TitleOriginal, &i.TitleEnglish, &i.TitleJapanese, &i.ImageUrl, &i.Airing, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Close(); err != nil { return nil, err } if err := rows.Err(); err != nil { return nil, err } return items, nil } const getWatchListEntry = `-- name: GetWatchListEntry :one SELECT id, user_id, anime_id, status, created_at, updated_at, current_episode, last_episode_at, current_time_seconds FROM watch_list_entry WHERE user_id = ? AND anime_id = ? LIMIT 1 ` type GetWatchListEntryParams struct { UserID string `json:"user_id"` AnimeID int64 `json:"anime_id"` } func (q *Queries) GetWatchListEntry(ctx context.Context, arg GetWatchListEntryParams) (WatchListEntry, error) { row := q.db.QueryRowContext(ctx, getWatchListEntry, arg.UserID, arg.AnimeID) var i WatchListEntry err := row.Scan( &i.ID, &i.UserID, &i.AnimeID, &i.Status, &i.CreatedAt, &i.UpdatedAt, &i.CurrentEpisode, &i.LastEpisodeAt, &i.CurrentTimeSeconds, ) return i, err } const getWatchingAnime = `-- name: GetWatchingAnime :many SELECT e.id, e.user_id, e.anime_id, e.status, e.created_at, e.updated_at, e.current_episode, e.last_episode_at, e.current_time_seconds, a.title_original, a.title_english, a.title_japanese, a.image_url, a.airing FROM watch_list_entry e JOIN anime a ON e.anime_id = a.id WHERE e.user_id = ? AND e.status IN ('watching', 'plan_to_watch') AND a.airing = 1 ORDER BY e.updated_at DESC ` type GetWatchingAnimeRow struct { ID string `json:"id"` UserID string `json:"user_id"` AnimeID int64 `json:"anime_id"` Status string `json:"status"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` CurrentEpisode sql.NullInt64 `json:"current_episode"` LastEpisodeAt sql.NullTime `json:"last_episode_at"` CurrentTimeSeconds float64 `json:"current_time_seconds"` TitleOriginal string `json:"title_original"` TitleEnglish sql.NullString `json:"title_english"` TitleJapanese sql.NullString `json:"title_japanese"` ImageUrl string `json:"image_url"` Airing sql.NullBool `json:"airing"` } func (q *Queries) GetWatchingAnime(ctx context.Context, userID string) ([]GetWatchingAnimeRow, error) { rows, err := q.db.QueryContext(ctx, getWatchingAnime, userID) if err != nil { return nil, err } var items []GetWatchingAnimeRow for rows.Next() { var i GetWatchingAnimeRow if err := rows.Scan( &i.ID, &i.UserID, &i.AnimeID, &i.Status, &i.CreatedAt, &i.UpdatedAt, &i.CurrentEpisode, &i.LastEpisodeAt, &i.CurrentTimeSeconds, &i.TitleOriginal, &i.TitleEnglish, &i.TitleJapanese, &i.ImageUrl, &i.Airing, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Close(); err != nil { return nil, err } if err := rows.Err(); err != nil { return nil, err } return items, nil } const markAnimeFetchRetryFailed = `-- name: MarkAnimeFetchRetryFailed :exec UPDATE anime_fetch_retry SET attempts = attempts + 1, next_retry_at = datetime(CURRENT_TIMESTAMP, ?), last_error = ?, updated_at = CURRENT_TIMESTAMP WHERE anime_id = ? ` type MarkAnimeFetchRetryFailedParams struct { Datetime interface{} `json:"datetime"` LastError string `json:"last_error"` AnimeID int64 `json:"anime_id"` } func (q *Queries) MarkAnimeFetchRetryFailed(ctx context.Context, arg MarkAnimeFetchRetryFailedParams) error { _, err := q.db.ExecContext(ctx, markAnimeFetchRetryFailed, arg.Datetime, arg.LastError, arg.AnimeID) return err } const markEpisodeAvailabilityRefreshFailed = `-- name: MarkEpisodeAvailabilityRefreshFailed :exec UPDATE episode_availability_cache SET last_attempt_at = ?, failure_count = failure_count + 1, last_error = ?, next_refresh_at = ?, retry_until_at = ?, updated_at = CURRENT_TIMESTAMP WHERE anime_id = ? ` type MarkEpisodeAvailabilityRefreshFailedParams struct { LastAttemptAt sql.NullTime `json:"last_attempt_at"` LastError string `json:"last_error"` NextRefreshAt sql.NullTime `json:"next_refresh_at"` RetryUntilAt sql.NullTime `json:"retry_until_at"` AnimeID int64 `json:"anime_id"` } func (q *Queries) MarkEpisodeAvailabilityRefreshFailed(ctx context.Context, arg MarkEpisodeAvailabilityRefreshFailedParams) error { _, err := q.db.ExecContext(ctx, markEpisodeAvailabilityRefreshFailed, arg.LastAttemptAt, arg.LastError, arg.NextRefreshAt, arg.RetryUntilAt, arg.AnimeID, ) return err } const markRelationsSynced = `-- name: MarkRelationsSynced :exec UPDATE anime SET relations_synced_at = CURRENT_TIMESTAMP WHERE id = ? ` func (q *Queries) MarkRelationsSynced(ctx context.Context, id int64) error { _, err := q.db.ExecContext(ctx, markRelationsSynced, id) return err } const refreshSession = `-- name: RefreshSession :exec UPDATE session SET expires_at = ? WHERE id = ? ` type RefreshSessionParams struct { ExpiresAt time.Time `json:"expires_at"` ID string `json:"id"` } func (q *Queries) RefreshSession(ctx context.Context, arg RefreshSessionParams) error { _, err := q.db.ExecContext(ctx, refreshSession, arg.ExpiresAt, arg.ID) return err } const revokeAllAPITokensForUser = `-- name: RevokeAllAPITokensForUser :exec UPDATE api_token SET revoked_at = CURRENT_TIMESTAMP WHERE user_id = ? AND revoked_at IS NULL ` func (q *Queries) RevokeAllAPITokensForUser(ctx context.Context, userID string) error { _, err := q.db.ExecContext(ctx, revokeAllAPITokensForUser, userID) return err } const saveWatchProgress = `-- name: SaveWatchProgress :exec UPDATE watch_list_entry SET current_episode = ?, current_time_seconds = ? WHERE user_id = ? AND anime_id = ? ` type SaveWatchProgressParams struct { CurrentEpisode sql.NullInt64 `json:"current_episode"` CurrentTimeSeconds float64 `json:"current_time_seconds"` UserID string `json:"user_id"` AnimeID int64 `json:"anime_id"` } func (q *Queries) SaveWatchProgress(ctx context.Context, arg SaveWatchProgressParams) error { _, err := q.db.ExecContext(ctx, saveWatchProgress, arg.CurrentEpisode, arg.CurrentTimeSeconds, arg.UserID, arg.AnimeID, ) return err } const setJikanCache = `-- name: SetJikanCache :exec INSERT INTO jikan_cache (key, data, expires_at) VALUES (?, ?, ?) ON CONFLICT (key) DO UPDATE SET data = excluded.data, expires_at = excluded.expires_at, created_at = CURRENT_TIMESTAMP ` type SetJikanCacheParams struct { Key string `json:"key"` Data string `json:"data"` ExpiresAt time.Time `json:"expires_at"` } func (q *Queries) SetJikanCache(ctx context.Context, arg SetJikanCacheParams) error { _, err := q.db.ExecContext(ctx, setJikanCache, arg.Key, arg.Data, arg.ExpiresAt) return err } const touchAPITokenLastUsedAt = `-- name: TouchAPITokenLastUsedAt :exec UPDATE api_token SET last_used_at = CURRENT_TIMESTAMP WHERE id = ? ` func (q *Queries) TouchAPITokenLastUsedAt(ctx context.Context, id string) error { _, err := q.db.ExecContext(ctx, touchAPITokenLastUsedAt, id) return err } const updateAnimeStatus = `-- name: UpdateAnimeStatus :exec UPDATE anime SET status = ? WHERE id = ? ` type UpdateAnimeStatusParams struct { Status sql.NullString `json:"status"` ID int64 `json:"id"` } func (q *Queries) UpdateAnimeStatus(ctx context.Context, arg UpdateAnimeStatusParams) error { _, err := q.db.ExecContext(ctx, updateAnimeStatus, arg.Status, arg.ID) return err } const upsertAnime = `-- name: UpsertAnime :one INSERT INTO anime (id, title_original, title_english, title_japanese, image_url, airing, duration_seconds) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT (id) DO UPDATE SET title_original = excluded.title_original, title_english = excluded.title_english, title_japanese = excluded.title_japanese, image_url = excluded.image_url, airing = excluded.airing, duration_seconds = excluded.duration_seconds RETURNING id, title_original, image_url, created_at, title_english, title_japanese, airing, status, relations_synced_at, duration_seconds ` type UpsertAnimeParams struct { ID int64 `json:"id"` TitleOriginal string `json:"title_original"` TitleEnglish sql.NullString `json:"title_english"` TitleJapanese sql.NullString `json:"title_japanese"` ImageUrl string `json:"image_url"` Airing sql.NullBool `json:"airing"` DurationSeconds sql.NullFloat64 `json:"duration_seconds"` } func (q *Queries) UpsertAnime(ctx context.Context, arg UpsertAnimeParams) (Anime, error) { row := q.db.QueryRowContext(ctx, upsertAnime, arg.ID, arg.TitleOriginal, arg.TitleEnglish, arg.TitleJapanese, arg.ImageUrl, arg.Airing, arg.DurationSeconds, ) var i Anime err := row.Scan( &i.ID, &i.TitleOriginal, &i.ImageUrl, &i.CreatedAt, &i.TitleEnglish, &i.TitleJapanese, &i.Airing, &i.Status, &i.RelationsSyncedAt, &i.DurationSeconds, ) return i, err } const upsertAnimeRelation = `-- name: UpsertAnimeRelation :exec INSERT INTO anime_relation (anime_id, related_anime_id, relation_type) VALUES (?, ?, ?) ON CONFLICT (anime_id, related_anime_id) DO UPDATE SET relation_type = excluded.relation_type ` type UpsertAnimeRelationParams struct { AnimeID int64 `json:"anime_id"` RelatedAnimeID int64 `json:"related_anime_id"` RelationType string `json:"relation_type"` } func (q *Queries) UpsertAnimeRelation(ctx context.Context, arg UpsertAnimeRelationParams) error { _, err := q.db.ExecContext(ctx, upsertAnimeRelation, arg.AnimeID, arg.RelatedAnimeID, arg.RelationType) return err } const upsertContinueWatchingEntry = `-- name: UpsertContinueWatchingEntry :one INSERT INTO continue_watching_entry (id, user_id, anime_id, current_episode, current_time_seconds, duration_seconds, updated_at) VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT (user_id, anime_id) DO UPDATE SET current_episode = excluded.current_episode, current_time_seconds = excluded.current_time_seconds, duration_seconds = excluded.duration_seconds, updated_at = CURRENT_TIMESTAMP RETURNING id, user_id, anime_id, current_episode, current_time_seconds, created_at, updated_at, duration_seconds ` type UpsertContinueWatchingEntryParams struct { ID string `json:"id"` UserID string `json:"user_id"` AnimeID int64 `json:"anime_id"` CurrentEpisode sql.NullInt64 `json:"current_episode"` CurrentTimeSeconds float64 `json:"current_time_seconds"` DurationSeconds sql.NullFloat64 `json:"duration_seconds"` } func (q *Queries) UpsertContinueWatchingEntry(ctx context.Context, arg UpsertContinueWatchingEntryParams) (ContinueWatchingEntry, error) { row := q.db.QueryRowContext(ctx, upsertContinueWatchingEntry, arg.ID, arg.UserID, arg.AnimeID, arg.CurrentEpisode, arg.CurrentTimeSeconds, arg.DurationSeconds, ) var i ContinueWatchingEntry err := row.Scan( &i.ID, &i.UserID, &i.AnimeID, &i.CurrentEpisode, &i.CurrentTimeSeconds, &i.CreatedAt, &i.UpdatedAt, &i.DurationSeconds, ) return i, err } const upsertEpisodeAvailabilityCache = `-- name: UpsertEpisodeAvailabilityCache :exec INSERT INTO episode_availability_cache ( anime_id, data, next_refresh_at, retry_until_at, last_attempt_at, last_success_at, failure_count, last_error, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT (anime_id) DO UPDATE SET data = excluded.data, next_refresh_at = excluded.next_refresh_at, retry_until_at = excluded.retry_until_at, last_attempt_at = excluded.last_attempt_at, last_success_at = excluded.last_success_at, failure_count = excluded.failure_count, last_error = excluded.last_error, updated_at = CURRENT_TIMESTAMP ` type UpsertEpisodeAvailabilityCacheParams struct { AnimeID int64 `json:"anime_id"` Data string `json:"data"` NextRefreshAt sql.NullTime `json:"next_refresh_at"` RetryUntilAt sql.NullTime `json:"retry_until_at"` LastAttemptAt sql.NullTime `json:"last_attempt_at"` LastSuccessAt sql.NullTime `json:"last_success_at"` FailureCount int64 `json:"failure_count"` LastError string `json:"last_error"` } func (q *Queries) UpsertEpisodeAvailabilityCache(ctx context.Context, arg UpsertEpisodeAvailabilityCacheParams) error { _, err := q.db.ExecContext(ctx, upsertEpisodeAvailabilityCache, arg.AnimeID, arg.Data, arg.NextRefreshAt, arg.RetryUntilAt, arg.LastAttemptAt, arg.LastSuccessAt, arg.FailureCount, arg.LastError, ) return err } const upsertEpisodeProviderMapping = `-- name: UpsertEpisodeProviderMapping :exec INSERT INTO episode_provider_mapping (anime_id, provider, provider_show_id, failed_until, last_error, updated_at) VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT (anime_id, provider) DO UPDATE SET provider_show_id = excluded.provider_show_id, failed_until = excluded.failed_until, last_error = excluded.last_error, updated_at = CURRENT_TIMESTAMP ` type UpsertEpisodeProviderMappingParams struct { AnimeID int64 `json:"anime_id"` Provider string `json:"provider"` ProviderShowID string `json:"provider_show_id"` FailedUntil sql.NullTime `json:"failed_until"` LastError string `json:"last_error"` } func (q *Queries) UpsertEpisodeProviderMapping(ctx context.Context, arg UpsertEpisodeProviderMappingParams) error { _, err := q.db.ExecContext(ctx, upsertEpisodeProviderMapping, arg.AnimeID, arg.Provider, arg.ProviderShowID, arg.FailedUntil, arg.LastError, ) return err } const upsertWatchListEntry = `-- name: UpsertWatchListEntry :one INSERT INTO watch_list_entry (id, user_id, anime_id, status, current_episode, current_time_seconds, updated_at) VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT (user_id, anime_id) DO UPDATE SET status = excluded.status, current_episode = excluded.current_episode, current_time_seconds = excluded.current_time_seconds, updated_at = CURRENT_TIMESTAMP RETURNING id, user_id, anime_id, status, created_at, updated_at, current_episode, last_episode_at, current_time_seconds ` type UpsertWatchListEntryParams struct { ID string `json:"id"` UserID string `json:"user_id"` AnimeID int64 `json:"anime_id"` Status string `json:"status"` CurrentEpisode sql.NullInt64 `json:"current_episode"` CurrentTimeSeconds float64 `json:"current_time_seconds"` } func (q *Queries) UpsertWatchListEntry(ctx context.Context, arg UpsertWatchListEntryParams) (WatchListEntry, error) { row := q.db.QueryRowContext(ctx, upsertWatchListEntry, arg.ID, arg.UserID, arg.AnimeID, arg.Status, arg.CurrentEpisode, arg.CurrentTimeSeconds, ) var i WatchListEntry err := row.Scan( &i.ID, &i.UserID, &i.AnimeID, &i.Status, &i.CreatedAt, &i.UpdatedAt, &i.CurrentEpisode, &i.LastEpisodeAt, &i.CurrentTimeSeconds, ) return i, err }