mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-11-04 03:27:14 -05:00 
			
		
		
		
	Add /shows & /shows/random routes
This commit is contained in:
		
							parent
							
								
									9ff62ef050
								
							
						
					
					
						commit
						68d4958e3a
					
				@ -96,7 +96,7 @@ export const collections = new Elysia({
 | 
			
		||||
		"random",
 | 
			
		||||
		async ({ error, redirect }) => {
 | 
			
		||||
			const [serie] = await db
 | 
			
		||||
				.select({ id: shows.id })
 | 
			
		||||
				.select({ slug: shows.slug })
 | 
			
		||||
				.from(shows)
 | 
			
		||||
				.where(eq(shows.kind, "collection"))
 | 
			
		||||
				.orderBy(sql`random()`)
 | 
			
		||||
@ -106,7 +106,7 @@ export const collections = new Elysia({
 | 
			
		||||
					status: 404,
 | 
			
		||||
					message: "No collection in the database.",
 | 
			
		||||
				});
 | 
			
		||||
			return redirect(`/collections/${serie.id}`);
 | 
			
		||||
			return redirect(`/collections/${serie.slug}`);
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			detail: {
 | 
			
		||||
 | 
			
		||||
@ -86,6 +86,7 @@ export async function getShows({
 | 
			
		||||
			// movie columns (status is only a typescript hint)
 | 
			
		||||
			status: sql<MovieStatus>`${shows.status}`,
 | 
			
		||||
			airDate: shows.startAir,
 | 
			
		||||
			kind: sql<any>`${shows.kind}`,
 | 
			
		||||
 | 
			
		||||
			poster: sql<Image>`coalesce(${showTranslations.poster}, ${poster})`,
 | 
			
		||||
			thumbnail: sql<Image>`coalesce(${showTranslations.thumbnail}, ${thumbnail})`,
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { type SQL, and, eq, exists, sql } from "drizzle-orm";
 | 
			
		||||
import { and, eq, sql } from "drizzle-orm";
 | 
			
		||||
import { Elysia, t } from "elysia";
 | 
			
		||||
import { db } from "~/db";
 | 
			
		||||
import { shows } from "~/db/schema";
 | 
			
		||||
@ -87,7 +87,7 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] })
 | 
			
		||||
		"random",
 | 
			
		||||
		async ({ error, redirect }) => {
 | 
			
		||||
			const [movie] = await db
 | 
			
		||||
				.select({ id: shows.id })
 | 
			
		||||
				.select({ slug: shows.slug })
 | 
			
		||||
				.from(shows)
 | 
			
		||||
				.where(eq(shows.kind, "movie"))
 | 
			
		||||
				.orderBy(sql`random()`)
 | 
			
		||||
@ -97,7 +97,7 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] })
 | 
			
		||||
					status: 404,
 | 
			
		||||
					message: "No movies in the database.",
 | 
			
		||||
				});
 | 
			
		||||
			return redirect(`/movies/${movie.id}`);
 | 
			
		||||
			return redirect(`/movies/${movie.slug}`);
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			detail: {
 | 
			
		||||
 | 
			
		||||
@ -87,7 +87,7 @@ export const series = new Elysia({ prefix: "/series", tags: ["series"] })
 | 
			
		||||
		"random",
 | 
			
		||||
		async ({ error, redirect }) => {
 | 
			
		||||
			const [serie] = await db
 | 
			
		||||
				.select({ id: shows.id })
 | 
			
		||||
				.select({ slug: shows.slug })
 | 
			
		||||
				.from(shows)
 | 
			
		||||
				.where(eq(shows.kind, "serie"))
 | 
			
		||||
				.orderBy(sql`random()`)
 | 
			
		||||
@ -97,7 +97,7 @@ export const series = new Elysia({ prefix: "/series", tags: ["series"] })
 | 
			
		||||
					status: 404,
 | 
			
		||||
					message: "No series in the database.",
 | 
			
		||||
				});
 | 
			
		||||
			return redirect(`/series/${serie.id}`);
 | 
			
		||||
			return redirect(`/series/${serie.slug}`);
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			detail: {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										119
									
								
								api/src/controllers/shows/shows.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								api/src/controllers/shows/shows.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,119 @@
 | 
			
		||||
import { and, isNull, sql } from "drizzle-orm";
 | 
			
		||||
import { Elysia, t } from "elysia";
 | 
			
		||||
import { db } from "~/db";
 | 
			
		||||
import { shows } from "~/db/schema";
 | 
			
		||||
import { Collection } from "~/models/collections";
 | 
			
		||||
import { KError } from "~/models/error";
 | 
			
		||||
import { Movie } from "~/models/movie";
 | 
			
		||||
import { Serie } from "~/models/serie";
 | 
			
		||||
import {
 | 
			
		||||
	AcceptLanguage,
 | 
			
		||||
	Filter,
 | 
			
		||||
	Page,
 | 
			
		||||
	createPage,
 | 
			
		||||
	processLanguages,
 | 
			
		||||
} from "~/models/utils";
 | 
			
		||||
import { desc } from "~/models/utils/descriptions";
 | 
			
		||||
import { getShows, showFilters, showSort } from "./logic";
 | 
			
		||||
 | 
			
		||||
const Show = t.Union([Movie, Serie, Collection]);
 | 
			
		||||
 | 
			
		||||
export const showsH = new Elysia({ prefix: "/shows", tags: ["shows"] })
 | 
			
		||||
	.model({
 | 
			
		||||
		show: Show,
 | 
			
		||||
	})
 | 
			
		||||
	.get(
 | 
			
		||||
		"random",
 | 
			
		||||
		async ({ error, redirect }) => {
 | 
			
		||||
			const [show] = await db
 | 
			
		||||
				.select({ kind: shows.kind, slug: shows.slug })
 | 
			
		||||
				.from(shows)
 | 
			
		||||
				.orderBy(sql`random()`)
 | 
			
		||||
				.limit(1);
 | 
			
		||||
			if (!show)
 | 
			
		||||
				return error(404, {
 | 
			
		||||
					status: 404,
 | 
			
		||||
					message: "No shows in the database.",
 | 
			
		||||
				});
 | 
			
		||||
			return redirect(`/${show.kind}s/${show.slug}`);
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			detail: {
 | 
			
		||||
				description: "Get a random movie/serie/collection",
 | 
			
		||||
			},
 | 
			
		||||
			response: {
 | 
			
		||||
				302: t.Void({
 | 
			
		||||
					description: "Redirected to the appropriate get endpoint.",
 | 
			
		||||
				}),
 | 
			
		||||
				404: {
 | 
			
		||||
					...KError,
 | 
			
		||||
					description: "No show in the database.",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	)
 | 
			
		||||
	.get(
 | 
			
		||||
		"",
 | 
			
		||||
		async ({
 | 
			
		||||
			query: {
 | 
			
		||||
				limit,
 | 
			
		||||
				after,
 | 
			
		||||
				query,
 | 
			
		||||
				sort,
 | 
			
		||||
				filter,
 | 
			
		||||
				preferOriginal,
 | 
			
		||||
				ignoreInCollection,
 | 
			
		||||
			},
 | 
			
		||||
			headers: { "accept-language": languages },
 | 
			
		||||
			request: { url },
 | 
			
		||||
		}) => {
 | 
			
		||||
			const langs = processLanguages(languages);
 | 
			
		||||
			const items = await getShows({
 | 
			
		||||
				limit,
 | 
			
		||||
				after,
 | 
			
		||||
				query,
 | 
			
		||||
				sort,
 | 
			
		||||
				filter: and(
 | 
			
		||||
					ignoreInCollection ? isNull(shows.collectionPk) : undefined,
 | 
			
		||||
					filter,
 | 
			
		||||
				),
 | 
			
		||||
				languages: langs,
 | 
			
		||||
				preferOriginal,
 | 
			
		||||
			});
 | 
			
		||||
			return createPage(items, { url, sort, limit });
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			detail: { description: "Get all movies/series/collections" },
 | 
			
		||||
			query: t.Object({
 | 
			
		||||
				sort: showSort,
 | 
			
		||||
				filter: t.Optional(Filter({ def: showFilters })),
 | 
			
		||||
				query: t.Optional(t.String({ description: desc.query })),
 | 
			
		||||
				limit: t.Integer({
 | 
			
		||||
					minimum: 1,
 | 
			
		||||
					maximum: 250,
 | 
			
		||||
					default: 50,
 | 
			
		||||
					description: "Max page size.",
 | 
			
		||||
				}),
 | 
			
		||||
				after: t.Optional(t.String({ description: desc.after })),
 | 
			
		||||
				preferOriginal: t.Optional(
 | 
			
		||||
					t.Boolean({
 | 
			
		||||
						description: desc.preferOriginal,
 | 
			
		||||
					}),
 | 
			
		||||
				),
 | 
			
		||||
				ignoreInCollection: t.Optional(
 | 
			
		||||
					t.Boolean({
 | 
			
		||||
						description:
 | 
			
		||||
							"If a movie or serie is part of collection, don't return it.",
 | 
			
		||||
						default: true,
 | 
			
		||||
					}),
 | 
			
		||||
				),
 | 
			
		||||
			}),
 | 
			
		||||
			headers: t.Object({
 | 
			
		||||
				"accept-language": AcceptLanguage({ autoFallback: true }),
 | 
			
		||||
			}),
 | 
			
		||||
			response: {
 | 
			
		||||
				200: Page(Show),
 | 
			
		||||
				422: KError,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	);
 | 
			
		||||
@ -5,6 +5,7 @@ import { seed } from "./controllers/seed";
 | 
			
		||||
import { collections } from "./controllers/shows/collections";
 | 
			
		||||
import { movies } from "./controllers/shows/movies";
 | 
			
		||||
import { series } from "./controllers/shows/series";
 | 
			
		||||
import { showsH } from "./controllers/shows/shows";
 | 
			
		||||
import { videosH } from "./controllers/videos";
 | 
			
		||||
import type { KError } from "./models/error";
 | 
			
		||||
 | 
			
		||||
@ -40,6 +41,7 @@ export const base = new Elysia({ name: "base" })
 | 
			
		||||
 | 
			
		||||
export const app = new Elysia()
 | 
			
		||||
	.use(base)
 | 
			
		||||
	.use(showsH)
 | 
			
		||||
	.use(movies)
 | 
			
		||||
	.use(series)
 | 
			
		||||
	.use(collections)
 | 
			
		||||
 | 
			
		||||
@ -48,6 +48,11 @@ app
 | 
			
		||||
					},
 | 
			
		||||
				],
 | 
			
		||||
				tags: [
 | 
			
		||||
					{
 | 
			
		||||
						name: "shows",
 | 
			
		||||
						description:
 | 
			
		||||
							"Routes to list movies, series & collections at the same time",
 | 
			
		||||
					},
 | 
			
		||||
					{ name: "movies", description: "Routes about movies" },
 | 
			
		||||
					{ name: "series", description: "Routes about series" },
 | 
			
		||||
					{ name: "collections", description: "Routes about collections" },
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@ import {
 | 
			
		||||
} from "./utils";
 | 
			
		||||
 | 
			
		||||
const BaseCollection = t.Object({
 | 
			
		||||
	kind: t.Literal("collection"),
 | 
			
		||||
	genres: t.Array(Genre),
 | 
			
		||||
	rating: t.Nullable(t.Integer({ minimum: 0, maximum: 100 })),
 | 
			
		||||
	startAir: t.Nullable(
 | 
			
		||||
@ -67,7 +68,13 @@ export const FullCollection = t.Intersect([
 | 
			
		||||
export type FullCollection = Prettify<typeof FullCollection.static>;
 | 
			
		||||
 | 
			
		||||
export const SeedCollection = t.Intersect([
 | 
			
		||||
	t.Omit(BaseCollection, ["startAir", "endAir", "createdAt", "nextRefresh"]),
 | 
			
		||||
	t.Omit(BaseCollection, [
 | 
			
		||||
		"kind",
 | 
			
		||||
		"startAir",
 | 
			
		||||
		"endAir",
 | 
			
		||||
		"createdAt",
 | 
			
		||||
		"nextRefresh",
 | 
			
		||||
	]),
 | 
			
		||||
	t.Object({
 | 
			
		||||
		slug: t.String({ format: "slug" }),
 | 
			
		||||
		translations: TranslationRecord(
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@ export const MovieStatus = t.UnionEnum(["unknown", "finished", "planned"]);
 | 
			
		||||
export type MovieStatus = typeof MovieStatus.static;
 | 
			
		||||
 | 
			
		||||
const BaseMovie = t.Object({
 | 
			
		||||
	kind: t.Literal("movie"),
 | 
			
		||||
	genres: t.Array(Genre),
 | 
			
		||||
	rating: t.Nullable(t.Integer({ minimum: 0, maximum: 100 })),
 | 
			
		||||
	status: MovieStatus,
 | 
			
		||||
@ -71,7 +72,7 @@ export const FullMovie = t.Intersect([
 | 
			
		||||
export type FullMovie = Prettify<typeof FullMovie.static>;
 | 
			
		||||
 | 
			
		||||
export const SeedMovie = t.Intersect([
 | 
			
		||||
	t.Omit(BaseMovie, ["createdAt", "nextRefresh"]),
 | 
			
		||||
	t.Omit(BaseMovie, ["kind", "createdAt", "nextRefresh"]),
 | 
			
		||||
	t.Object({
 | 
			
		||||
		slug: t.String({ format: "slug", examples: ["bubble"] }),
 | 
			
		||||
		translations: TranslationRecord(
 | 
			
		||||
 | 
			
		||||
@ -19,6 +19,7 @@ export const SerieStatus = t.UnionEnum([
 | 
			
		||||
export type SerieStatus = typeof SerieStatus.static;
 | 
			
		||||
 | 
			
		||||
export const BaseSerie = t.Object({
 | 
			
		||||
	kind: t.Literal("serie"),
 | 
			
		||||
	genres: t.Array(Genre),
 | 
			
		||||
	rating: t.Nullable(t.Integer({ minimum: 0, maximum: 100 })),
 | 
			
		||||
	status: SerieStatus,
 | 
			
		||||
@ -70,7 +71,7 @@ export const FullSerie = t.Intersect([
 | 
			
		||||
export type FullMovie = Prettify<typeof FullSerie.static>;
 | 
			
		||||
 | 
			
		||||
export const SeedSerie = t.Intersect([
 | 
			
		||||
	t.Omit(BaseSerie, ["createdAt", "nextRefresh"]),
 | 
			
		||||
	t.Omit(BaseSerie, ["kind", "createdAt", "nextRefresh"]),
 | 
			
		||||
	t.Object({
 | 
			
		||||
		slug: t.String({ format: "slug" }),
 | 
			
		||||
		translations: TranslationRecord(
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user