diff --git a/api/src/models/examples.ts b/api/src/models/examples.ts index 25f2f765..8fa3e73d 100644 --- a/api/src/models/examples.ts +++ b/api/src/models/examples.ts @@ -1,5 +1,23 @@ +import type { TSchema } from "elysia"; import type { CompleteVideo } from "./video"; +export const registerExamples = ( + schema: T, + ...examples: (T["static"] | undefined)[] +) => { + for (const example of examples) { + if (!example) continue; + for (const [key, val] of Object.entries(example)) { + const prop = schema.properties[ + key as keyof typeof schema.properties + ] as TSchema; + if (!prop) continue; + prop.examples ??= []; + prop.examples.push(val); + } + } +}; + export const bubble: CompleteVideo = { id: "0934da28-4a49-404e-920b-a150404a3b6d", path: "/video/Bubble/Bubble (2022).mkv", @@ -21,7 +39,7 @@ export const bubble: CompleteVideo = { status: "finished", runtime: 101, airDate: "2022-02-14", - originalLanguage: null, + originalLanguage: "ja", poster: { id: "befdc7dd-2a67-0704-92af-90d49eee0315", source: diff --git a/api/src/models/movie.ts b/api/src/models/movie.ts index beed9b03..3555a5e4 100644 --- a/api/src/models/movie.ts +++ b/api/src/models/movie.ts @@ -2,7 +2,8 @@ import { t } from "elysia"; import { Genre, ShowStatus } from "./show"; import { Image } from "./image"; import { ExternalId } from "./external-id"; -import { bubble } from "./examples"; +import { bubble, registerExamples } from "./examples"; +import { comment } from "../utils"; export const Movie = t.Object({ id: t.String({ format: "uuid" }), @@ -19,7 +20,15 @@ export const Movie = t.Object({ runtime: t.Nullable(t.Number({ minimum: 0 })), airDate: t.Nullable(t.String({ format: "date" })), - originalLanguage: t.Nullable(t.String()), + originalLanguage: t.Nullable( + t.String({ + description: comment` + The language code this movie was made in. + This is a BCP 47 language code (the IETF Best Current Practices on Tags for Identifying Languages). + BCP 47 is also known as RFC 5646. It subsumes ISO 639 and is backward compatible with it. + `, + }), + ), poster: t.Nullable(Image), thumbnail: t.Nullable(Image), @@ -35,4 +44,4 @@ export const Movie = t.Object({ export type Movie = typeof Movie.static; -Movie.examples = [bubble.movie]; +registerExamples(Movie, bubble.movie); diff --git a/api/src/models/video.ts b/api/src/models/video.ts index 248c7266..e8cdb9fa 100644 --- a/api/src/models/video.ts +++ b/api/src/models/video.ts @@ -1,30 +1,38 @@ import { t } from "elysia"; import { Movie } from "./movie"; -import { bubble } from "./examples"; +import { bubble, registerExamples } from "./examples"; export const Video = t.Object({ id: t.String({ format: "uuid" }), path: t.String(), rendering: t.Number({ minimum: 0 }), - part: t.Number({ minimum: 0 }), - version: t.Number({ minimum: 0 }), + part: t.Nullable(t.Number({ minimum: 0 })), + version: t.Nullable( + t.Number({ + minimum: 0, + description: + "Kyoo will prefer playing back the highest `version` number if there's rendering.", + }), + ), createdAt: t.String({ format: "date-time" }), }); export type Video = typeof Video.static; -Video.examples = [bubble]; +registerExamples(Video, bubble); export const CompleteVideo = t.Intersect([ Video, t.Union([ t.Object({ movie: Movie, + episodes: t.Optional(t.Never()), }), t.Object({ // TODO: implement that episodes: t.Array(t.Object({})), + movie: t.Optional(t.Never()), }), ]), ]); diff --git a/api/src/utils.ts b/api/src/utils.ts new file mode 100644 index 00000000..b4b1ca96 --- /dev/null +++ b/api/src/utils.ts @@ -0,0 +1,3 @@ +// remove indent in multi-line comments +export const comment = (str: TemplateStringsArray) => + str.toString().replace(/^\s+/gm, "");