mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-10-31 10:37:13 -04: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