mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -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(),
|
language: language().notNull(),
|
||||||
name: text(),
|
name: text(),
|
||||||
description: text(),
|
description: text(),
|
||||||
|
// those two are only used if kind === "movie"
|
||||||
tagline: text(),
|
tagline: text(),
|
||||||
|
poster: image(),
|
||||||
},
|
},
|
||||||
(t) => [primaryKey({ columns: [t.pk, t.language] })],
|
(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