From 7071e07ef4f946b2f0af5f38d4100f1976a0c45f Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Sat, 9 Nov 2024 13:11:07 +0100 Subject: [PATCH] Define schema for all entries type --- api/src/db/schema/entries.ts | 2 +- api/src/models/entry.ts | 111 ++++++++++++++++++++++++++++ api/src/models/utils/external-id.ts | 20 +++++ 3 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 api/src/models/entry.ts diff --git a/api/src/db/schema/entries.ts b/api/src/db/schema/entries.ts index 0f182912..4393e99c 100644 --- a/api/src/db/schema/entries.ts +++ b/api/src/db/schema/entries.ts @@ -29,7 +29,7 @@ export const entries = schema.table( id: uuid().notNull().unique().defaultRandom(), slug: varchar({ length: 255 }).notNull().unique(), showPk: integer().references(() => shows.pk, { onDelete: "cascade" }), - order: integer().notNull(), + order: integer(), seasonNumber: integer(), episodeNumber: integer(), type: entryType().notNull(), diff --git a/api/src/models/entry.ts b/api/src/models/entry.ts new file mode 100644 index 00000000..9a5f6e38 --- /dev/null +++ b/api/src/models/entry.ts @@ -0,0 +1,111 @@ +import { t } from "elysia"; +import { Image } from "./utils/image"; +import { ExternalId, EpisodeId } from "./utils/external-id"; +import { comment } from "../utils"; + +export const Entry = t.Object({ + id: t.String({ format: "uuid" }), + slug: t.String(), + serieId: t.String({ format: "uuid" }), + 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), + + createtAt: t.String({ format: "date-time" }), + nextRefresh: t.String({ format: "date-time" }), +}); + +export const Episode = t.Union([ + Entry, + t.Object({ + kind: t.Literal("episode"), + seasonId: t.String({ format: "uuid" }), + 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.Union( + [ + Entry, + 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 type MovieEntry = typeof MovieEntry.static; + +export const Special = t.Union( + [ + Entry, + 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 Extra = t.Union( + [ + Entry, + t.Object({ + kind: t.Literal("extra"), + number: t.Number({ minimum: 1 }), + // 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 Video = t.Union( + [ + t.Omit(Entry, ["serieId", "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 type Video = typeof Video.static; diff --git a/api/src/models/utils/external-id.ts b/api/src/models/utils/external-id.ts index 368c00b6..cbcac6fa 100644 --- a/api/src/models/utils/external-id.ts +++ b/api/src/models/utils/external-id.ts @@ -1,4 +1,5 @@ import { t } from "elysia"; +import { comment } from "../../utils"; export const ExternalId = t.Record( t.String(), @@ -9,3 +10,22 @@ export const ExternalId = t.Record( ); export type ExternalId = typeof ExternalId.static; + +export const EpisodeId = t.Record( + t.String(), + t.Object({ + serieId: t.String({ + descrpition: comment` + Id on the external website. + We store the serie's id because episode id are rarely stable. + `, + }), + season: t.Nullable( + t.Number({ + description: "Null if the external website uses absolute numbering.", + }), + ), + episode: t.Number(), + link: t.Nullable(t.String({ format: "uri" })), + }), +);