mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-11-03 19:17:16 -05:00 
			
		
		
		
	Split entries & add translations types
This commit is contained in:
		
							parent
							
								
									97d9abca62
								
							
						
					
					
						commit
						cfe6ce9c7e
					
				@ -80,7 +80,9 @@ export const entriesTranslation = schema.table(
 | 
			
		||||
		language: language().notNull(),
 | 
			
		||||
		name: text(),
 | 
			
		||||
		description: text(),
 | 
			
		||||
		// those two are only used if kind === "movie"
 | 
			
		||||
		tagline: text(),
 | 
			
		||||
		poster: image(),
 | 
			
		||||
	},
 | 
			
		||||
	(t) => [primaryKey({ columns: [t.pk, t.language] })],
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
@ -1,138 +0,0 @@
 | 
			
		||||
import { t } from "elysia";
 | 
			
		||||
import { Image } from "./utils/image";
 | 
			
		||||
import { ExternalId, EpisodeId } from "./utils/external-id";
 | 
			
		||||
import { comment } from "../utils";
 | 
			
		||||
import { madeInAbyss, registerExamples } from "./examples";
 | 
			
		||||
 | 
			
		||||
const BaseEntry = t.Object({
 | 
			
		||||
	id: t.String({ format: "uuid" }),
 | 
			
		||||
	slug: t.String(),
 | 
			
		||||
	name: t.Nullable(t.String()),
 | 
			
		||||
	description: t.Nullable(t.String()),
 | 
			
		||||
	airDate: t.Nullable(t.String({ format: "data" })),
 | 
			
		||||
	runtime: t.Nullable(
 | 
			
		||||
		t.Number({ minimum: 0, description: "Runtime of the episode in minutes" }),
 | 
			
		||||
	),
 | 
			
		||||
	thumbnail: t.Nullable(Image),
 | 
			
		||||
 | 
			
		||||
	createdAt: t.String({ format: "date-time" }),
 | 
			
		||||
	nextRefresh: t.String({ format: "date-time" }),
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const Episode = t.Intersect([
 | 
			
		||||
	BaseEntry,
 | 
			
		||||
	t.Object({
 | 
			
		||||
		kind: t.Literal("episode"),
 | 
			
		||||
		order: t.Number({ minimum: 1, description: "Absolute playback order." }),
 | 
			
		||||
		seasonNumber: t.Number(),
 | 
			
		||||
		episodeNumber: t.Number(),
 | 
			
		||||
		externalId: EpisodeId,
 | 
			
		||||
	}),
 | 
			
		||||
]);
 | 
			
		||||
export type Episode = typeof Episode.static;
 | 
			
		||||
 | 
			
		||||
export const MovieEntry = t.Intersect(
 | 
			
		||||
	[
 | 
			
		||||
		t.Omit(BaseEntry, ["thumbnail"]),
 | 
			
		||||
		t.Object({
 | 
			
		||||
			kind: t.Literal("movie"),
 | 
			
		||||
			order: t.Number({
 | 
			
		||||
				minimum: 1,
 | 
			
		||||
				description: "Absolute playback order. Can be mixed with episodes.",
 | 
			
		||||
			}),
 | 
			
		||||
			tagline: t.String(),
 | 
			
		||||
			poster: BaseEntry.properties.thumbnail,
 | 
			
		||||
			externalId: ExternalId,
 | 
			
		||||
		}),
 | 
			
		||||
	],
 | 
			
		||||
	{
 | 
			
		||||
		description: comment`
 | 
			
		||||
			If a movie is part of a serie (watching the movie require context from the serie &
 | 
			
		||||
			the next episode of the serie require you to have seen the movie to understand it.)
 | 
			
		||||
		`,
 | 
			
		||||
	},
 | 
			
		||||
);
 | 
			
		||||
export type MovieEntry = typeof MovieEntry.static;
 | 
			
		||||
 | 
			
		||||
export const Special = t.Intersect(
 | 
			
		||||
	[
 | 
			
		||||
		BaseEntry,
 | 
			
		||||
		t.Object({
 | 
			
		||||
			kind: t.Literal("special"),
 | 
			
		||||
			order: t.Number({
 | 
			
		||||
				minimum: 1,
 | 
			
		||||
				description: "Absolute playback order. Can be mixed with episodes.",
 | 
			
		||||
			}),
 | 
			
		||||
			number: t.Number({ minimum: 1 }),
 | 
			
		||||
			externalId: EpisodeId,
 | 
			
		||||
		}),
 | 
			
		||||
	],
 | 
			
		||||
	{
 | 
			
		||||
		description: comment`
 | 
			
		||||
			A special is either an OAV episode (side story & co) or an important episode that was released standalone
 | 
			
		||||
			(outside of a season.)
 | 
			
		||||
		`,
 | 
			
		||||
	},
 | 
			
		||||
);
 | 
			
		||||
export type Special = typeof Special.static;
 | 
			
		||||
 | 
			
		||||
export const ExtraType = t.UnionEnum([
 | 
			
		||||
	"other",
 | 
			
		||||
	"trailers",
 | 
			
		||||
	"interview",
 | 
			
		||||
	"behind-the-scenes",
 | 
			
		||||
	"deleted-scenes",
 | 
			
		||||
	"bloopers",
 | 
			
		||||
]);
 | 
			
		||||
export type ExtraType = typeof ExtraType.static;
 | 
			
		||||
 | 
			
		||||
export const Extra = t.Intersect(
 | 
			
		||||
	[
 | 
			
		||||
		BaseEntry,
 | 
			
		||||
		t.Object({
 | 
			
		||||
			kind: ExtraType,
 | 
			
		||||
			// not sure about this id type
 | 
			
		||||
			externalId: EpisodeId,
 | 
			
		||||
		}),
 | 
			
		||||
	],
 | 
			
		||||
	{
 | 
			
		||||
		description: comment`
 | 
			
		||||
			An extra can be a beyond-the-scene, short-episodes or anything that is in a different format & not required
 | 
			
		||||
			in the main story plot.
 | 
			
		||||
		`,
 | 
			
		||||
	},
 | 
			
		||||
);
 | 
			
		||||
export type Extra = typeof Extra.static;
 | 
			
		||||
 | 
			
		||||
export const UnknownEntry = t.Intersect(
 | 
			
		||||
	[
 | 
			
		||||
		t.Omit(BaseEntry, ["airDate", "description"]),
 | 
			
		||||
		t.Object({
 | 
			
		||||
			kind: t.Literal("unknown"),
 | 
			
		||||
		}),
 | 
			
		||||
	],
 | 
			
		||||
	{
 | 
			
		||||
		description: comment`
 | 
			
		||||
			A video not releated to any series or movie. This can be due to a matching error but it can be a youtube
 | 
			
		||||
			video or any other video content.
 | 
			
		||||
		`,
 | 
			
		||||
	},
 | 
			
		||||
);
 | 
			
		||||
export type UnknownEntry = typeof UnknownEntry.static;
 | 
			
		||||
 | 
			
		||||
export const Entry = t.Union([Episode, MovieEntry, Special]);
 | 
			
		||||
export type Entry = typeof Entry.static;
 | 
			
		||||
 | 
			
		||||
registerExamples(
 | 
			
		||||
	Episode,
 | 
			
		||||
	...madeInAbyss.entries.filter((x) => x.kind === "episode"),
 | 
			
		||||
);
 | 
			
		||||
registerExamples(
 | 
			
		||||
	MovieEntry,
 | 
			
		||||
	...madeInAbyss.entries.filter((x) => x.kind === "movie"),
 | 
			
		||||
);
 | 
			
		||||
registerExamples(
 | 
			
		||||
	Special,
 | 
			
		||||
	...madeInAbyss.entries.filter((x) => x.kind === "special"),
 | 
			
		||||
);
 | 
			
		||||
registerExamples(Extra, ...madeInAbyss.extras);
 | 
			
		||||
							
								
								
									
										18
									
								
								api/src/models/entry/base-entry.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								api/src/models/entry/base-entry.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
import { t } from "elysia";
 | 
			
		||||
import { Image } from "../utils/image";
 | 
			
		||||
 | 
			
		||||
export const BaseEntry = t.Object({
 | 
			
		||||
	airDate: t.Nullable(t.String({ format: "data" })),
 | 
			
		||||
	runtime: t.Nullable(
 | 
			
		||||
		t.Number({ minimum: 0, description: "Runtime of the episode in minutes" }),
 | 
			
		||||
	),
 | 
			
		||||
	thumbnail: t.Nullable(Image),
 | 
			
		||||
 | 
			
		||||
	createdAt: t.String({ format: "date-time" }),
 | 
			
		||||
	nextRefresh: t.String({ format: "date-time" }),
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const EntryTranslation = t.Object({
 | 
			
		||||
	name: t.Nullable(t.String()),
 | 
			
		||||
	description: t.Nullable(t.String()),
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										18
									
								
								api/src/models/entry/episode.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								api/src/models/entry/episode.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
import { t } from "elysia";
 | 
			
		||||
import { BaseEntry, EntryTranslation } from "./base-entry";
 | 
			
		||||
import { EpisodeId } from "../utils/external-id";
 | 
			
		||||
import { Resource } from "../utils/resource";
 | 
			
		||||
 | 
			
		||||
export const BaseEpisode = t.Intersect([
 | 
			
		||||
	BaseEntry,
 | 
			
		||||
	t.Object({
 | 
			
		||||
		kind: t.Literal("episode"),
 | 
			
		||||
		order: t.Number({ minimum: 1, description: "Absolute playback order." }),
 | 
			
		||||
		seasonNumber: t.Number(),
 | 
			
		||||
		episodeNumber: t.Number(),
 | 
			
		||||
		externalId: EpisodeId,
 | 
			
		||||
	}),
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
export const Episode = t.Intersect([Resource, BaseEpisode, EntryTranslation]);
 | 
			
		||||
export type Episode = typeof Episode.static;
 | 
			
		||||
							
								
								
									
										37
									
								
								api/src/models/entry/extra.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								api/src/models/entry/extra.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
			
		||||
import { t } from "elysia";
 | 
			
		||||
import { BaseEntry, EntryTranslation } from "./base-entry";
 | 
			
		||||
import { EpisodeId } from "../utils/external-id";
 | 
			
		||||
import { comment } from "../../utils";
 | 
			
		||||
import { Resource } from "../utils/resource";
 | 
			
		||||
 | 
			
		||||
export const ExtraType = t.UnionEnum([
 | 
			
		||||
	"other",
 | 
			
		||||
	"trailers",
 | 
			
		||||
	"interview",
 | 
			
		||||
	"behind-the-scenes",
 | 
			
		||||
	"deleted-scenes",
 | 
			
		||||
	"bloopers",
 | 
			
		||||
]);
 | 
			
		||||
export type ExtraType = typeof ExtraType.static;
 | 
			
		||||
 | 
			
		||||
export const BaseExtra = t.Intersect(
 | 
			
		||||
	[
 | 
			
		||||
		BaseEntry,
 | 
			
		||||
		t.Object({
 | 
			
		||||
			kind: ExtraType,
 | 
			
		||||
			// not sure about this id type
 | 
			
		||||
			externalId: EpisodeId,
 | 
			
		||||
		}),
 | 
			
		||||
	],
 | 
			
		||||
	{
 | 
			
		||||
		description: comment`
 | 
			
		||||
			An extra can be a beyond-the-scene, short-episodes or anything that is in a different format & not required
 | 
			
		||||
			in the main story plot.
 | 
			
		||||
		`,
 | 
			
		||||
	},
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export const Extra = t.Intersect([Resource, BaseExtra, EntryTranslation]);
 | 
			
		||||
export type Extra = typeof Extra.static;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								api/src/models/entry/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								api/src/models/entry/index.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
import { t } from "elysia";
 | 
			
		||||
import { Episode, MovieEntry, Special } from "../entry";
 | 
			
		||||
 | 
			
		||||
export const Entry = t.Union([Episode, MovieEntry, Special]);
 | 
			
		||||
export type Entry = typeof Entry.static;
 | 
			
		||||
 | 
			
		||||
export * from "./episode";
 | 
			
		||||
export * from "./movie-entry";
 | 
			
		||||
export * from "./special";
 | 
			
		||||
export * from "./extra";
 | 
			
		||||
export * from "./unknown-entry";
 | 
			
		||||
							
								
								
									
										41
									
								
								api/src/models/entry/movie-entry.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								api/src/models/entry/movie-entry.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,41 @@
 | 
			
		||||
import { t } from "elysia";
 | 
			
		||||
import { comment } from "../../utils";
 | 
			
		||||
import { ExternalId } from "../utils/external-id";
 | 
			
		||||
import { Image } from "../utils/image";
 | 
			
		||||
import { Resource } from "../utils/resource";
 | 
			
		||||
import { BaseEntry, EntryTranslation } from "./base-entry";
 | 
			
		||||
 | 
			
		||||
export const BaseMovieEntry = t.Intersect(
 | 
			
		||||
	[
 | 
			
		||||
		t.Omit(BaseEntry, ["thumbnail"]),
 | 
			
		||||
		t.Object({
 | 
			
		||||
			kind: t.Literal("movie"),
 | 
			
		||||
			order: t.Number({
 | 
			
		||||
				minimum: 1,
 | 
			
		||||
				description: "Absolute playback order. Can be mixed with episodes.",
 | 
			
		||||
			}),
 | 
			
		||||
			externalId: ExternalId,
 | 
			
		||||
		}),
 | 
			
		||||
	],
 | 
			
		||||
	{
 | 
			
		||||
		description: comment`
 | 
			
		||||
			If a movie is part of a serie (watching the movie require context from the serie &
 | 
			
		||||
			the next episode of the serie require you to have seen the movie to understand it.)
 | 
			
		||||
		`,
 | 
			
		||||
	},
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export const MovieEntryTranslation = t.Intersect([
 | 
			
		||||
	EntryTranslation,
 | 
			
		||||
	t.Object({
 | 
			
		||||
		tagline: t.Nullable(t.String()),
 | 
			
		||||
		thumbnail: t.Nullable(Image),
 | 
			
		||||
	}),
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
export const MovieEntry = t.Intersect([
 | 
			
		||||
	Resource,
 | 
			
		||||
	BaseMovieEntry,
 | 
			
		||||
	MovieEntryTranslation,
 | 
			
		||||
]);
 | 
			
		||||
export type MovieEntry = typeof MovieEntry.static;
 | 
			
		||||
							
								
								
									
										29
									
								
								api/src/models/entry/special.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								api/src/models/entry/special.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
import { t } from "elysia";
 | 
			
		||||
import { comment } from "../../utils";
 | 
			
		||||
import { EpisodeId } from "../utils/external-id";
 | 
			
		||||
import { Resource } from "../utils/resource";
 | 
			
		||||
import { BaseEntry, EntryTranslation } from "./base-entry";
 | 
			
		||||
 | 
			
		||||
export const BaseSpecial = t.Intersect(
 | 
			
		||||
	[
 | 
			
		||||
		BaseEntry,
 | 
			
		||||
		t.Object({
 | 
			
		||||
			kind: t.Literal("special"),
 | 
			
		||||
			order: t.Number({
 | 
			
		||||
				minimum: 1,
 | 
			
		||||
				description: "Absolute playback order. Can be mixed with episodes.",
 | 
			
		||||
			}),
 | 
			
		||||
			number: t.Number({ minimum: 1 }),
 | 
			
		||||
			externalId: EpisodeId,
 | 
			
		||||
		}),
 | 
			
		||||
	],
 | 
			
		||||
	{
 | 
			
		||||
		description: comment`
 | 
			
		||||
			A special is either an OAV episode (side story & co) or an important episode that was released standalone
 | 
			
		||||
			(outside of a season.)
 | 
			
		||||
		`,
 | 
			
		||||
	},
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export const Special = t.Intersect([Resource, BaseSpecial, EntryTranslation]);
 | 
			
		||||
export type Special = typeof Special.static;
 | 
			
		||||
							
								
								
									
										30
									
								
								api/src/models/entry/unknown-entry.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								api/src/models/entry/unknown-entry.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
			
		||||
import { t } from "elysia";
 | 
			
		||||
import { comment } from "../../utils";
 | 
			
		||||
import { Resource } from "../utils/resource";
 | 
			
		||||
import { BaseEntry, EntryTranslation } from "./base-entry";
 | 
			
		||||
 | 
			
		||||
export const BaseUnknownEntry = t.Intersect(
 | 
			
		||||
	[
 | 
			
		||||
		t.Omit(BaseEntry, ["airDate"]),
 | 
			
		||||
		t.Object({
 | 
			
		||||
			kind: t.Literal("unknown"),
 | 
			
		||||
		}),
 | 
			
		||||
	],
 | 
			
		||||
	{
 | 
			
		||||
		description: comment`
 | 
			
		||||
			A video not releated to any series or movie. This can be due to a matching error but it can be a youtube
 | 
			
		||||
			video or any other video content.
 | 
			
		||||
		`,
 | 
			
		||||
	},
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export const UnknownEntryTranslation = t.Omit(EntryTranslation, [
 | 
			
		||||
	"description",
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
export const UnknownEntry = t.Intersect([
 | 
			
		||||
	Resource,
 | 
			
		||||
	BaseUnknownEntry,
 | 
			
		||||
	UnknownEntryTranslation,
 | 
			
		||||
]);
 | 
			
		||||
export type UnknownEntry = typeof UnknownEntry.static;
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user