diff --git a/api/README.md b/api/README.md index 734c44d9..28f781d2 100644 --- a/api/README.md +++ b/api/README.md @@ -63,14 +63,14 @@ erDiagram } entries ||--|{ entry_translations : has - video { + videos { guid id PK string path "NN" uint rendering "dedup for duplicates part1/2" uint part uint version "max version is preferred rendering" } - video }|--|{ entries : for + videos }|--|{ entries : for seasons { guid id PK @@ -102,16 +102,16 @@ erDiagram guid id PK } - watched_shows { + watchlist { guid show_id PK, FK guid user_id PK, FK - status status "completed|watching|dropped|planned" + status status "completed|watching|rewatching|dropped|planned" uint seen_entry_count "NN" guid next_entry FK } - shows ||--|{ watched_shows : has - users ||--|{ watched_shows : has - watched_shows ||--|o entries : next_entry + shows ||--|{ watchlist : has + users ||--|{ watchlist : has + watchlist ||--|o entries : next_entry history { int id PK diff --git a/api/src/db/schema/history.ts b/api/src/db/schema/history.ts index 823e098a..487d8776 100644 --- a/api/src/db/schema/history.ts +++ b/api/src/db/schema/history.ts @@ -20,7 +20,9 @@ export const history = schema.table( .references(() => videos.pk, { onDelete: "set null" }), percent: integer().notNull().default(0), time: integer(), - playedDate: timestamp({ mode: "string" }).notNull().defaultNow(), + playedDate: timestamp({ withTimezone: true, mode: "string" }) + .notNull() + .defaultNow(), }, (t) => [ index("history_play_date").on(t.playedDate.desc()), diff --git a/api/src/db/schema/watchlist.ts b/api/src/db/schema/watchlist.ts new file mode 100644 index 00000000..0699134f --- /dev/null +++ b/api/src/db/schema/watchlist.ts @@ -0,0 +1,50 @@ +import { sql } from "drizzle-orm"; +import { + check, + integer, + primaryKey, + text, + timestamp, +} from "drizzle-orm/pg-core"; +import { entries } from "./entries"; +import { profiles } from "./profiles"; +import { shows } from "./shows"; +import { schema } from "./utils"; + +export const watchlistStatus = schema.enum("watchlist_status", [ + "completed", + "watching", + "rewatching", + "dropped", + "planned", +]); + +export const watchlist = schema.table( + "watchlist", + { + profilePk: integer() + .notNull() + .references(() => profiles.pk, { onDelete: "cascade" }), + showPk: integer() + .notNull() + .references(() => shows.pk, { onDelete: "cascade" }), + + status: watchlistStatus().notNull(), + seenCount: integer().notNull().default(0), + nextEntry: integer().references(() => entries.pk, { onDelete: "set null" }), + + score: integer(), + notes: text(), + + createdAt: timestamp({ withTimezone: true, mode: "string" }) + .notNull() + .defaultNow(), + updatedAt: timestamp({ withTimezone: true, mode: "string" }) + .notNull() + .$onUpdate(() => sql`now()`), + }, + (t) => [ + primaryKey({ columns: [t.profilePk, t.showPk] }), + check("score_percent", sql`${t.score} between 0 and 100`), + ], +);