911 lines
26 KiB
Go
911 lines
26 KiB
Go
// 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 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 deleteExpiredJikanCache = `-- name: DeleteExpiredJikanCache :exec
|
|
DELETE FROM jikan_cache WHERE 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 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
|
|
}
|
|
defer rows.Close()
|
|
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 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
|
|
}
|
|
defer rows.Close()
|
|
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
|
|
}
|
|
defer rows.Close()
|
|
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 getJikanCache = `-- name: GetJikanCache :one
|
|
SELECT data FROM jikan_cache
|
|
WHERE key = ? AND 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 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
|
|
}
|
|
defer rows.Close()
|
|
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
|
|
}
|
|
defer rows.Close()
|
|
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
|
|
}
|
|
defer rows.Close()
|
|
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 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 saveWatchProgress = `-- name: SaveWatchProgress :exec
|
|
UPDATE watch_list_entry
|
|
SET current_episode = ?,
|
|
current_time_seconds = ?,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
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 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 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
|
|
}
|