diff --git a/api/src/controllers/watchlist.ts b/api/src/controllers/watchlist.ts index 00bd695a..324e8a20 100644 --- a/api/src/controllers/watchlist.ts +++ b/api/src/controllers/watchlist.ts @@ -18,7 +18,7 @@ import { processLanguages, } from "~/models/utils"; import { desc } from "~/models/utils/descriptions"; -import { WatchStatus } from "~/models/watchlist"; +import { MovieWatchStatus, SerieWatchStatus } from "~/models/watchlist"; import { getShows, showFilters, showSort, watchStatusQ } from "./shows/logic"; async function setWatchStatus({ @@ -26,8 +26,8 @@ async function setWatchStatus({ status, userId, }: { - showFilter?: SQL; - status: Omit; + showFilter: { id: SQL; kind: "movie" | "serie" }; + status: SerieWatchStatus; userId: string; }) { const profileQ = db @@ -38,7 +38,7 @@ async function setWatchStatus({ const showQ = db .select({ pk: shows.pk }) .from(shows) - .where(showFilter) + .where(and(showFilter.id, eq(shows.kind, showFilter.kind))) .as("showQ"); return await db @@ -55,7 +55,12 @@ async function setWatchStatus({ "profilePk", "showPk", "createdAt", + "seenCount", ]), + // do not reset movie's progress during drop + ...(showFilter.kind === "movie" && status.status !== "dropped" + ? { seenCount: sql`excluded.seen_count` } + : {}), }, }) .returning(); @@ -177,10 +182,10 @@ export const watchlistH = new Elysia({ tags: ["profiles"] }) "/series/:id/watchstatus", async ({ params: { id }, body, jwt: { sub } }) => { return await setWatchStatus({ - showFilter: and( - eq(shows.kind, "serie"), - isUuid(id) ? eq(shows.id, id) : eq(shows.slug, id), - ), + showFilter: { + kind: "serie", + id: isUuid(id) ? eq(shows.id, id) : eq(shows.slug, id), + }, userId: sub, status: body, }); @@ -193,9 +198,9 @@ export const watchlistH = new Elysia({ tags: ["profiles"] }) example: madeInAbyss.slug, }), }), - body: t.Omit(WatchStatus, ["percent"]), + body: SerieWatchStatus, response: { - 201: t.Union([t.Omit(WatchStatus, ["percent"]), DbMetadata]), + 201: t.Union([SerieWatchStatus, DbMetadata]), }, permissions: ["core.read"], }, @@ -204,13 +209,17 @@ export const watchlistH = new Elysia({ tags: ["profiles"] }) "/movies/:id/watchstatus", async ({ params: { id }, body, jwt: { sub } }) => { return await setWatchStatus({ - showFilter: and( - eq(shows.kind, "movie"), - isUuid(id) ? eq(shows.id, id) : eq(shows.slug, id), - ), + showFilter: { + kind: "movie", + id: isUuid(id) ? eq(shows.id, id) : eq(shows.slug, id), + }, userId: sub, - // for movies, watch-percent is stored in `seenCount`. - status: { ...body, seenCount: body.status === "completed" ? 100 : 0 }, + status: { + ...body, + startedAt: body.completedAt, + // for movies, watch-percent is stored in `seenCount`. + seenCount: body.status === "completed" ? 100 : 0, + }, }); }, { @@ -221,12 +230,9 @@ export const watchlistH = new Elysia({ tags: ["profiles"] }) example: bubble.slug, }), }), - body: t.Omit(WatchStatus, ["seenCount", "percent"]), + body: t.Omit(MovieWatchStatus, ["percent"]), response: { - 201: t.Union([ - t.Omit(WatchStatus, ["seenCount", "percent"]), - DbMetadata, - ]), + 201: t.Union([MovieWatchStatus, DbMetadata]), }, permissions: ["core.read"], }, diff --git a/api/src/models/movie.ts b/api/src/models/movie.ts index 9de88479..31f28487 100644 --- a/api/src/models/movie.ts +++ b/api/src/models/movie.ts @@ -16,7 +16,7 @@ import { } from "./utils"; import { Original } from "./utils/original"; import { EmbeddedVideo } from "./video"; -import { WatchStatus } from "./watchlist"; +import { MovieWatchStatus } from "./watchlist"; export const MovieStatus = t.UnionEnum(["unknown", "finished", "planned"]); export type MovieStatus = typeof MovieStatus.static; @@ -56,7 +56,7 @@ export const Movie = t.Intersect([ t.Object({ original: Original, isAvailable: t.Boolean(), - watchStatus: t.Nullable(t.Omit(WatchStatus, ["seenCount"])), + watchStatus: t.Nullable(MovieWatchStatus), }), ]); export type Movie = Prettify; diff --git a/api/src/models/serie.ts b/api/src/models/serie.ts index 78ed77e8..2757f493 100644 --- a/api/src/models/serie.ts +++ b/api/src/models/serie.ts @@ -17,7 +17,7 @@ import { TranslationRecord, } from "./utils"; import { Original } from "./utils/original"; -import { WatchStatus } from "./watchlist"; +import { SerieWatchStatus } from "./watchlist"; export const SerieStatus = t.UnionEnum([ "unknown", @@ -71,7 +71,7 @@ export const Serie = t.Intersect([ availableCount: t.Integer({ description: "The number of episodes that can be played right away", }), - watchStatus: t.Nullable(t.Omit(WatchStatus, ["percent"])), + watchStatus: t.Nullable(SerieWatchStatus), }), ]); export type Serie = Prettify; diff --git a/api/src/models/watchlist.ts b/api/src/models/watchlist.ts index 2b1f270d..b341ea3e 100644 --- a/api/src/models/watchlist.ts +++ b/api/src/models/watchlist.ts @@ -36,20 +36,25 @@ export const WatchlistStatus = t.UnionEnum([ "planned", ]); -export const WatchStatus = t.Object({ +export const SerieWatchStatus = t.Object({ status: WatchlistStatus, score: t.Nullable(t.Integer({ minimum: 0, maximum: 100 })), startedAt: t.Nullable(t.String({ format: "date-time" })), completedAt: t.Nullable(t.String({ format: "date-time" })), - // only for series seenCount: t.Integer({ description: "The number of episodes you watched in this serie.", minimum: 0, }), - // only for movies - percent: t.Integer({ - minimum: 0, - maximum: 100, - }), }); -export type WatchStatus = typeof WatchStatus.static; +export type SerieWatchStatus = typeof SerieWatchStatus.static; + +export const MovieWatchStatus = t.Intersect([ + t.Omit(SerieWatchStatus, ["startedAt", "seenCount"]), + t.Object({ + percent: t.Integer({ + minimum: 0, + maximum: 100, + }), + }), +]); +export type MovieWatchStatus = typeof MovieWatchStatus.static;