Split entries & add translations types

This commit is contained in:
Zoe Roux 2024-11-15 23:36:41 +01:00
parent 97d9abca62
commit cfe6ce9c7e
No known key found for this signature in database
9 changed files with 186 additions and 138 deletions

View File

@ -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] })],
);

View File

@ -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);

View 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()),
});

View 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;

View 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;

View 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";

View 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;

View 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;

View 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;