mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-11-04 03:27:14 -05:00 
			
		
		
		
	Add get /movies & sort api
This commit is contained in:
		
							parent
							
								
									a4853cb186
								
							
						
					
					
						commit
						587dc4f970
					
				@ -1,13 +1,14 @@
 | 
			
		||||
import { and, eq, sql } from "drizzle-orm";
 | 
			
		||||
import { and, desc, eq, sql } from "drizzle-orm";
 | 
			
		||||
import { Elysia, t } from "elysia";
 | 
			
		||||
import { KError } from "~/models/error";
 | 
			
		||||
import { isUuid, processLanguages } from "~/models/utils";
 | 
			
		||||
import { comment } from "~/utils";
 | 
			
		||||
import { comment, RemovePrefix } from "~/utils";
 | 
			
		||||
import { db } from "../db";
 | 
			
		||||
import { shows, showTranslations } from "../db/schema/shows";
 | 
			
		||||
import { getColumns } from "../db/schema/utils";
 | 
			
		||||
import { bubble } from "../models/examples";
 | 
			
		||||
import { Movie, type MovieStatus, MovieTranslation } from "../models/movie";
 | 
			
		||||
import { Page } from "~/models/utils/page";
 | 
			
		||||
 | 
			
		||||
// drizzle is bugged and doesn't allow js arrays to be used in raw sql.
 | 
			
		||||
export function sqlarr(array: unknown[]) {
 | 
			
		||||
@ -130,4 +131,102 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] })
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	)
 | 
			
		||||
	.get(
 | 
			
		||||
		"",
 | 
			
		||||
		async ({
 | 
			
		||||
			query: { limit, after, sort },
 | 
			
		||||
			headers: { "accept-language": languages },
 | 
			
		||||
		}) => {
 | 
			
		||||
			const langs = processLanguages(languages);
 | 
			
		||||
			const [transQ, transCol] = getTranslationQuery(langs);
 | 
			
		||||
			const order = sort.map((x) => {
 | 
			
		||||
				const desc = x[0] === "-";
 | 
			
		||||
				const key = (desc ? x.substring(1) : x) as RemovePrefix<typeof x, "-">;
 | 
			
		||||
				if (key === "airDate") return { key: "startAir" as const, desc };
 | 
			
		||||
				return { key, desc };
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			const items = await db
 | 
			
		||||
				.select({
 | 
			
		||||
					...moviesCol,
 | 
			
		||||
					...transCol,
 | 
			
		||||
					status: sql<MovieStatus>`${moviesCol.status}`,
 | 
			
		||||
					airDate: startAir,
 | 
			
		||||
				})
 | 
			
		||||
				.from(shows)
 | 
			
		||||
				.innerJoin(transQ, eq(shows.pk, transQ.pk))
 | 
			
		||||
				.orderBy(
 | 
			
		||||
					...order.map((x) => (x.desc ? desc(shows[x.key]) : shows[x.key])),
 | 
			
		||||
					shows.pk,
 | 
			
		||||
				)
 | 
			
		||||
				.limit(limit);
 | 
			
		||||
 | 
			
		||||
			return { items, next: "", prev: "", this: "" };
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			detail: { description: "Get all movies" },
 | 
			
		||||
			query: t.Object({
 | 
			
		||||
				sort: t.Array(
 | 
			
		||||
					t.UnionEnum([
 | 
			
		||||
						"slug",
 | 
			
		||||
						"-slug",
 | 
			
		||||
						"rating",
 | 
			
		||||
						"-rating",
 | 
			
		||||
						"airDate",
 | 
			
		||||
						"-airDate",
 | 
			
		||||
						"createdAt",
 | 
			
		||||
						"-createdAt",
 | 
			
		||||
						"nextRefresh",
 | 
			
		||||
						"-nextRefresh",
 | 
			
		||||
					]),
 | 
			
		||||
					// TODO: support explode: true (allow sort=slug,-createdAt). needs a pr to elysia
 | 
			
		||||
					{ explode: false, default: ["slug"] },
 | 
			
		||||
				),
 | 
			
		||||
				limit: t.Integer({
 | 
			
		||||
					minimum: 1,
 | 
			
		||||
					maximum: 250,
 | 
			
		||||
					default: 50,
 | 
			
		||||
					description: "Max page size.",
 | 
			
		||||
				}),
 | 
			
		||||
				after: t.Optional(
 | 
			
		||||
					t.String({
 | 
			
		||||
						format: "uuid",
 | 
			
		||||
						description: comment`
 | 
			
		||||
							Id of the cursor in the pagination.
 | 
			
		||||
							You can ignore this and only use the prev/next field in the response.
 | 
			
		||||
						`,
 | 
			
		||||
					}),
 | 
			
		||||
				),
 | 
			
		||||
			}),
 | 
			
		||||
			headers: t.Object({
 | 
			
		||||
				"accept-language": t.String({
 | 
			
		||||
					default: "*",
 | 
			
		||||
					example: "en-us, ja;q=0.5",
 | 
			
		||||
					description: comment`
 | 
			
		||||
						List of languages you want the data in.
 | 
			
		||||
						This follows the Accept-Language offical specification
 | 
			
		||||
						(https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language).
 | 
			
		||||
 | 
			
		||||
						In this request, * is always implied (if no language could satisfy the request, kyoo will use any language available).
 | 
			
		||||
					`,
 | 
			
		||||
				}),
 | 
			
		||||
			}),
 | 
			
		||||
			// response: {
 | 
			
		||||
			// 	200: Page(Movie, {
 | 
			
		||||
			// 		description: "Paginated list of movies that match filters.",
 | 
			
		||||
			// 	}),
 | 
			
		||||
			// 	422: {
 | 
			
		||||
			// 		...KError,
 | 
			
		||||
			// 		description: "Invalid query parameters.",
 | 
			
		||||
			// 		examples: [
 | 
			
		||||
			// 			{
 | 
			
		||||
			// 				status: 422,
 | 
			
		||||
			// 				message: "Accept-Language header could not be satisfied.",
 | 
			
		||||
			// 				details: undefined,
 | 
			
		||||
			// 			},
 | 
			
		||||
			// 		],
 | 
			
		||||
			// 	},
 | 
			
		||||
			// },
 | 
			
		||||
		},
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										13
									
								
								api/src/models/utils/page.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								api/src/models/utils/page.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
import type { ObjectOptions } from "@sinclair/typebox";
 | 
			
		||||
import { t, type TSchema } from "elysia";
 | 
			
		||||
 | 
			
		||||
export const Page = <T extends TSchema>(schema: T, options?: ObjectOptions) =>
 | 
			
		||||
	t.Object(
 | 
			
		||||
		{
 | 
			
		||||
			items: t.Array(schema),
 | 
			
		||||
			this: t.String({ format: "uri" }),
 | 
			
		||||
			prev: t.String({ format: "uri" }),
 | 
			
		||||
			next: t.String({ format: "uri" }),
 | 
			
		||||
		},
 | 
			
		||||
		options,
 | 
			
		||||
	);
 | 
			
		||||
@ -1,3 +1,8 @@
 | 
			
		||||
// remove indent in multi-line comments
 | 
			
		||||
export const comment = (str: TemplateStringsArray, ...values: any[]) =>
 | 
			
		||||
	str.reduce((acc, str, i) => `${acc}${str}${values[i]}`).replace(/^\s+/gm, "");
 | 
			
		||||
 | 
			
		||||
export type RemovePrefix<
 | 
			
		||||
	T extends string,
 | 
			
		||||
	Prefix extends string,
 | 
			
		||||
> = T extends `${Prefix}${infer Ret}` ? Ret : T;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user