Handle watch status on entries

This commit is contained in:
Zoe Roux 2025-04-06 18:25:41 +02:00
parent 11e1c59698
commit c3abd7c61b
No known key found for this signature in database
2 changed files with 45 additions and 10 deletions

View File

@ -1,11 +1,13 @@
import { type SQL, and, desc, eq, isNotNull, ne, sql } from "drizzle-orm";
import { Elysia, t } from "elysia";
import { auth } from "~/auth";
import { db } from "~/db";
import {
entries,
entryTranslations,
entryVideoJoin,
history,
profiles,
shows,
videos,
} from "~/db/schema";
@ -124,7 +126,7 @@ export const entryVideosQ = db
.leftJoin(videos, eq(videos.pk, entryVideoJoin.videoPk))
.as("videos");
export const getEntryProgressQ = (userId: number) =>
export const getEntryProgressQ = (userId: string) =>
db
.selectDistinctOn([history.entryPk], {
percent: history.percent,
@ -133,11 +135,23 @@ export const getEntryProgressQ = (userId: number) =>
videoId: videos.id,
})
.from(history)
.where(eq(history.profilePk, userId))
.leftJoin(videos, eq(history.videoPk, videos.pk))
.leftJoin(profiles, eq(history.profilePk, profiles.pk))
.where(eq(profiles.id, userId))
.orderBy(history.entryPk, desc(history.playedDate))
.as("progress");
export const mapProgress = (
progressQ: ReturnType<typeof getEntryProgressQ>,
) => {
const { time, percent, videoId } = getColumns(progressQ);
return {
time: coalesce(time, sql`0`),
percent: coalesce(percent, sql`0`),
videoId,
};
};
async function getEntries({
after,
limit,
@ -153,7 +167,7 @@ async function getEntries({
sort: Sort;
filter: SQL | undefined;
languages: string[];
userId: number;
userId: string;
}): Promise<(Entry | Extra | UnknownEntry)[]> {
const transQ = db
.selectDistinctOn([entryTranslations.pk])
@ -181,7 +195,7 @@ async function getEntries({
...entryCol,
...transCol,
videos: entryVideosQ.videos,
progress: getColumns(entryProgressQ),
progress: mapProgress(entryProgressQ),
// specials don't have an `episodeNumber` but a `number` field.
number: episodeNumber,
@ -230,6 +244,7 @@ export const entriesH = new Elysia({ tags: ["series"] })
...models,
entry: t.Union([models.episode, models.movie_entry, models.special]),
}))
.use(auth)
.get(
"/series/:id/entries",
async ({
@ -237,6 +252,7 @@ export const entriesH = new Elysia({ tags: ["series"] })
query: { limit, after, query, sort, filter },
headers: { "accept-language": languages },
request: { url },
jwt: { sub },
error,
}) => {
const [serie] = await db
@ -270,6 +286,7 @@ export const entriesH = new Elysia({ tags: ["series"] })
filter,
),
languages: langs,
userId: sub,
})) as Entry[];
return createPage(items, { url, sort, limit });
@ -316,6 +333,7 @@ export const entriesH = new Elysia({ tags: ["series"] })
params: { id },
query: { limit, after, query, sort, filter },
request: { url },
jwt: { sub },
error,
}) => {
const [serie] = await db
@ -347,6 +365,7 @@ export const entriesH = new Elysia({ tags: ["series"] })
filter,
),
languages: ["extra"],
userId: sub,
})) as Extra[];
return createPage(items, { url, sort, limit });
@ -386,6 +405,7 @@ export const entriesH = new Elysia({ tags: ["series"] })
async ({
query: { limit, after, query, sort, filter },
request: { url },
jwt: { sub },
}) => {
const items = (await getEntries({
limit,
@ -394,6 +414,7 @@ export const entriesH = new Elysia({ tags: ["series"] })
sort: sort,
filter: and(eq(entries.kind, "unknown"), filter),
languages: ["extra"],
userId: sub,
})) as UnknownEntry[];
return createPage(items, { url, sort, limit });
@ -421,7 +442,11 @@ export const entriesH = new Elysia({ tags: ["series"] })
)
.get(
"/news",
async ({ query: { limit, after, query, filter }, request: { url } }) => {
async ({
query: { limit, after, query, filter },
request: { url },
jwt: { sub },
}) => {
const sort = newsSort;
const items = (await getEntries({
limit,
@ -435,6 +460,7 @@ export const entriesH = new Elysia({ tags: ["series"] })
filter,
),
languages: ["extra"],
userId: sub,
})) as Entry[];
return createPage(items, { url, sort, limit });

View File

@ -1,6 +1,6 @@
import {
Column,
type ColumnsSelection,
InferColumnsDataTypes,
type SQL,
type SQLWrapper,
type Subquery,
@ -13,7 +13,7 @@ import {
} from "drizzle-orm";
import type { CasingCache } from "drizzle-orm/casing";
import type { AnyMySqlSelect } from "drizzle-orm/mysql-core";
import type { AnyPgSelect } from "drizzle-orm/pg-core";
import type { AnyPgSelect, SelectedFieldsFlat } from "drizzle-orm/pg-core";
import type { AnySQLiteSelect } from "drizzle-orm/sqlite-core";
import type { WithSubquery } from "drizzle-orm/subquery";
import { db } from "./index";
@ -95,7 +95,7 @@ export function values(items: Record<string, unknown>[]) {
};
}
export const coalesce = <T>(val: SQL<T>, def: SQLWrapper) => {
export const coalesce = <T>(val: SQL<T> | Column, def: SQL<T>) => {
return sql<T>`coalesce(${val}, ${def})`;
};
@ -109,10 +109,19 @@ export const jsonbAgg = <T>(val: SQL<T>) => {
return sql<T[]>`jsonb_agg(${val})`;
};
export const jsonbBuildObject = <T>(select: Record<string, SQLWrapper>) => {
type JsonFields = {
[k: string]:
| SelectedFieldsFlat[string]
| Table
| SelectedFieldsFlat
| JsonFields;
};
export const jsonbBuildObject = <T>(select: JsonFields) => {
const query = sql.join(
Object.entries(select).flatMap(([k, v]) => {
return [sql.raw(`'${k}'`), v];
if (v.getSQL) return [sql.raw(`'${k}'`), v];
// nested object (getSql is present in all SqlWrappers)
return [sql.raw(`'${k}'`), jsonbBuildObject<any>(v as JsonFields)];
}),
sql.raw(", "),
);