Add entry & extra types in front

This commit is contained in:
Zoe Roux 2025-07-14 18:53:21 +02:00
parent 4b6f56a293
commit dbf63e1b22
10 changed files with 112 additions and 140 deletions

View File

@ -46,7 +46,7 @@ export const MovieEntry = t.Composite([
MovieEntryTranslation, MovieEntryTranslation,
BaseMovieEntry, BaseMovieEntry,
t.Object({ t.Object({
videos: t.Optional(t.Array(EmbeddedVideo)), videos: t.Array(EmbeddedVideo),
progress: Progress, progress: Progress,
}), }),
DbMetadata, DbMetadata,

View File

@ -1,7 +1,82 @@
import { z } from "zod/v4"; import { z } from "zod/v4";
import { KImage } from "./utils/images";
import { Metadata } from "./utils/metadata";
import { zdate } from "./utils/utils";
export const Entry = z.object({ const Base = z.object({
id: z.string(), id: z.string(),
slug: z.string(), slug: z.string(),
order: z.number(),
name: z.string().nullable(),
description: z.string().nullable(),
airDate: zdate().nullable(),
runtime: z.number().nullable(),
thumbnail: KImage.nullable(),
createdAt: zdate(),
updatedAt: zdate(),
videos: z.array(
z.object({
id: z.string(),
slug: z.string(),
path: z.string(),
rendering: z.string(),
part: z.int().nullable(),
version: z.int(),
}),
),
progress: z.object({
percent: z.int().min(0).max(100),
time: z.int().min(0).nullable(),
playedDate: zdate().nullable(),
videoId: z.string().nullable(),
}),
}); });
export const Episode = Base.extend({
kind: z.literal("episode"),
seasonNumber: z.int().gte(0),
episodeNumber: z.int().gte(0),
externalId: z.record(
z.string(),
z.object({
serieId: z.string(),
season: z.int().nullable(),
episode: z.int(),
link: z.string().nullable(),
}),
),
});
export type Episode = z.infer<typeof Episode>;
export const MovieEntry = Base.extend({
kind: z.literal("movie"),
tagline: z.string().nullable(),
poster: KImage.nullable(),
externalId: Metadata,
});
export type MovieEntry = z.infer<typeof MovieEntry>;
export const Special = Base.extend({
kind: z.literal("special"),
number: z.int(),
externalId: z.record(
z.string(),
z.object({
serieId: z.string(),
season: z.int().nullable(),
episode: z.int(),
link: z.string().nullable(),
}),
),
});
export type Special = z.infer<typeof Special>;
export const Entry = z.discriminatedUnion("kind", [
Episode,
MovieEntry,
Special,
]);
export type Entry = z.infer<typeof Entry>; export type Entry = z.infer<typeof Entry>;

29
front/src/models/extra.ts Normal file
View File

@ -0,0 +1,29 @@
import { z } from "zod/v4";
import { KImage } from "./utils/images";
import { zdate } from "./utils/utils";
export const Extra = z.object({
kind: z.enum([
"other",
"trailer",
"interview",
"behind-the-scene",
"deleted-scene",
"blooper",
]),
id: z.string(),
slug: z.string(),
name: z.string(),
runtime: z.number().nullable(),
thumbnail: KImage.nullable(),
createdAt: zdate(),
updatedAt: zdate(),
progress: z.object({
percent: z.int().min(0).max(100),
time: z.int().min(0).nullable(),
playedDate: zdate().nullable(),
}),
});
export type Extra = z.infer<typeof Extra>;

View File

@ -1,5 +1,6 @@
export * from "./collection"; export * from "./collection";
export * from "./entry"; export * from "./entry";
export * from "./extra";
export * from "./kyoo-error"; export * from "./kyoo-error";
export * from "./movie"; export * from "./movie";
export * from "./serie"; export * from "./serie";

View File

@ -1,62 +0,0 @@
import { z } from "zod";
import { ImagesP, ResourceP } from "../traits";
import { zdate } from "../utils";
export const BaseEpisodeP = ResourceP("episode")
.merge(ImagesP)
.extend({
/**
* The season in witch this episode is in.
*/
seasonNumber: z.number().nullable(),
/**
* The number of this episode in it's season.
*/
episodeNumber: z.number().nullable(),
/**
* The absolute number of this episode. It's an episode number that is not reset to 1 after a new
* season.
*/
absoluteNumber: z.number().nullable(),
/**
* The title of this episode.
*/
name: z.string().nullable(),
/**
* The overview of this episode.
*/
overview: z.string().nullable(),
/**
* How long is this movie? (in minutes).
*/
runtime: z.number().int().nullable(),
/**
* The release date of this episode. It can be null if unknown.
*/
releaseDate: zdate().nullable(),
/**
* The links to see a movie or an episode.
*/
links: z.object({
/**
* The direct link to the unprocessed video (pristine quality).
*/
direct: z.string(),
/**
* The link to an HLS master playlist containing all qualities available for this video.
*/
hls: z.string().nullable(),
}),
/**
* The id of the show containing this episode
*/
showId: z.string(),
})
.transform((x) => ({
...x,
runtime: x.runtime === 0 ? null : x.runtime,
}))
.transform((x) => ({
...x,
href: `/watch/${x.slug}`,
}));

View File

@ -1,34 +0,0 @@
import { z } from "zod";
import { BaseEpisodeP } from "./episode.base";
import { ShowP } from "./show";
import { WatchStatusP } from "./watch-status";
export const EpisodeP = BaseEpisodeP.and(
z.object({
/**
* The episode that come before this one if you follow usual watch orders. If this is the first
* episode, it will be null.
*/
previousEpisode: BaseEpisodeP.nullable().optional(),
/**
* The episode that come after this one if you follow usual watch orders. If this is the last
* aired episode, it will be null.
*/
nextEpisode: BaseEpisodeP.nullable().optional(),
show: ShowP.optional(),
/**
* Metadata of what an user as started/planned to watch.
*/
watchStatus: WatchStatusP.optional().nullable(),
}),
).transform((x) => {
if (x.show && !x.thumbnail && x.show.thumbnail)
x.thumbnail = x.show.thumbnail;
return x;
});
/**
* A class to represent a single show's episode.
*/
export type Episode = z.infer<typeof EpisodeP>;

View File

@ -1,19 +0,0 @@
import { z } from "zod";
import { EpisodeP } from "./episode";
import { MovieP } from "./movie";
export const NewsP = z.union([
/*
* Either an episode
*/
EpisodeP,
/*
* Or a Movie
*/
MovieP,
]);
/**
* A new item added to kyoo.
*/
export type News = z.infer<typeof NewsP>;

View File

@ -1,19 +0,0 @@
import { z } from "zod";
import { MovieP } from "./movie";
import { ShowP } from "./show";
export const WatchlistP = z.union([
/*
* Either a show
*/
ShowP,
/*
* Or a Movie
*/
MovieP,
]);
/**
* A item in the user's watchlist.
*/
export type Watchlist = z.infer<typeof WatchlistP>;

View File

@ -5,10 +5,11 @@ import { zdate } from "./utils/utils";
export const Season = z.object({ export const Season = z.object({
id: z.string(), id: z.string(),
slug: z.string(), slug: z.string(),
seasonNumber: z.number().gte(0), seasonNumber: z.int().gte(0),
name: z.string().nullable(), name: z.string().nullable(),
description: z.string().nullable(), description: z.string().nullable(),
entryCount: z.number(), entryCount: z.int().gte(0),
availableCount: z.int().gte(0),
startAir: zdate().nullable(), startAir: zdate().nullable(),
endAir: zdate().nullable(), endAir: zdate().nullable(),
externalId: z.record( externalId: z.record(

View File

@ -34,8 +34,8 @@ export const Serie = z
logo: KImage.nullable(), logo: KImage.nullable(),
trailerUrl: z.string().nullable(), trailerUrl: z.string().nullable(),
entriesCount: z.number().int(), entriesCount: z.int().gte(0),
availableCount: z.number().int(), availableCount: z.int().gte(0),
createdAt: zdate(), createdAt: zdate(),
updatedAt: zdate(), updatedAt: zdate(),