Rework collection & movie type

This commit is contained in:
Zoe Roux 2025-06-20 17:27:12 +02:00
parent 6c243b8961
commit 6823642e33
No known key found for this signature in database
7 changed files with 118 additions and 142 deletions

View File

@ -0,0 +1,40 @@
import { z } from "zod";
import { Genre } from "./utils/genre";
import { Image } from "./utils/images";
import { Metadata } from "./utils/metadata";
import { zdate } from "./utils/utils";
export const Collection = z
.object({
id: z.string(),
slug: z.string(),
name: z.string(),
original: z.object({
name: z.string(),
latinName: z.string().nullable(),
language: z.string(),
}),
tagline: z.string().nullable(),
aliases: z.array(z.string()),
tags: z.array(z.string()),
description: z.string().nullable(),
rating: z.number().int().gte(0).lte(100).nullable(),
startAir: zdate().nullable(),
endAir: zdate().nullable(),
genres: z.array(Genre),
externalId: Metadata,
poster: Image.nullable(),
thumbnail: Image.nullable(),
banner: Image.nullable(),
logo: Image.nullable(),
createdAt: zdate(),
updatedAt: zdate(),
})
.transform((x) => ({
...x,
href: `/collections/${x.slug}`,
}));
export type Collection = z.infer<typeof Collection>;

View File

@ -1,4 +1,9 @@
export * from "./utils/page";
export * from "./kyoo-error";
export * from "./resources";
export * from "./traits";
export * from "./movie";
export * from "./serie";
export * from "./collection";
export * from "./entry";
export * from "./studio";
export * from "./video";

56
front/src/models/movie.ts Normal file
View File

@ -0,0 +1,56 @@
import { z } from "zod";
import { Studio } from "./studio";
import { Genre } from "./utils/genre";
import { Image } from "./utils/images";
import { Metadata } from "./utils/metadata";
import { zdate } from "./utils/utils";
import { EmbeddedVideo } from "./video";
export const Movie = z
.object({
id: z.string(),
slug: z.string(),
name: z.string(),
original: z.object({
name: z.string(),
latinName: z.string().nullable(),
language: z.string(),
}),
tagline: z.string().nullable(),
aliases: z.array(z.string()),
tags: z.array(z.string()),
description: z.string().nullable(),
status: z.enum(["unknown", "finished", "planned"]),
rating: z.number().int().gte(0).lte(100),
runtime: z.number().int().nullable(),
airDate: zdate().nullable(),
genres: z.array(Genre),
externalId: Metadata,
poster: Image.nullable(),
thumbnail: Image.nullable(),
banner: Image.nullable(),
logo: Image.nullable(),
trailerUrl: z.string().optional().nullable(),
isAvailable: z.boolean(),
createdAt: zdate(),
updatedAt: zdate(),
studios: z.array(Studio).optional(),
videos: z.array(EmbeddedVideo).optional(),
watchStatus: z
.object({
status: z.enum(["completed", "watching", "rewatching", "dropped", "planned"]),
score: z.number().int().gte(0).lte(100).nullable(),
completedAt: zdate().nullable(),
percent: z.number().int().gte(0).lte(100),
})
.nullable(),
})
.transform((x) => ({
...x,
href: `/movies/${x.slug}`,
}));
export type Movie = z.infer<typeof Movie>;

View File

@ -1,24 +0,0 @@
import { z } from "zod";
import { ImagesP, ResourceP } from "../traits";
export const CollectionP = ResourceP("collection")
.merge(ImagesP)
.extend({
/**
* The title of this collection.
*/
name: z.string(),
/**
* The summary of this show.
*/
overview: z.string().nullable(),
})
.transform((x) => ({
...x,
href: `/collection/${x.slug}`,
}));
/**
* A class representing collections of show or movies.
*/
export type Collection = z.infer<typeof CollectionP>;

View File

@ -1,112 +0,0 @@
import { z } from "zod";
import { ImagesP, ResourceP } from "../traits";
import { zdate } from "../utils";
import { CollectionP } from "./collection";
import { Genre } from "./genre";
import { MetadataP } from "./metadata";
import { Status } from "./show";
import { StudioP } from "./studio";
import { WatchStatusP } from "./watch-status";
export const MovieP = ResourceP("movie")
.merge(ImagesP)
.extend({
/**
* The title of this movie.
*/
name: z.string(),
/**
* A catchphrase for this show.
*/
tagline: z.string().nullable(),
/**
* The list of alternative titles of this movie.
*/
aliases: z.array(z.string()),
/**
* The summary of this movie.
*/
overview: z.string().nullable(),
/**
* A list of tags that match this movie.
*/
tags: z.array(z.string()),
/**
* /** Is this movie not aired yet or finished?
*/
status: z.nativeEnum(Status),
/**
* How well this item is rated? (from 0 to 100).
*/
rating: z.number().int().gte(0).lte(100),
/**
* How long is this movie? (in minutes).
*/
runtime: z.number().int().nullable(),
/**
* The date this movie aired. It can also be null if this is unknown.
*/
airDate: zdate().nullable(),
/**
* A youtube url for the trailer.
*/
trailer: z.string().optional().nullable(),
/**
* The list of genres (themes) this movie has.
*/
genres: z.array(z.nativeEnum(Genre)),
/**
* The studio that made this movie.
*/
studio: StudioP.optional().nullable(),
/**
* The collection this movie is part of.
*/
collections: z.array(CollectionP).optional(),
/**
* 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 link to metadata providers that this show has.
*/
externalId: MetadataP,
/**
* Metadata of what an user as started/planned to watch.
*/
watchStatus: WatchStatusP.optional().nullable(),
})
.transform((x) => ({
...x,
runtime: x.runtime === 0 ? null : x.runtime,
}))
.transform((x) => {
if (!x.thumbnail && x.poster) {
x.thumbnail = { ...x.poster };
if (x.thumbnail) {
x.thumbnail.low = x.thumbnail.high;
x.thumbnail.medium = x.thumbnail.high;
}
}
return x;
})
.transform((x) => ({
...x,
href: `/movie/${x.slug}`,
playHref: `/movie/${x.slug}/watch`,
}));
/**
* A Movie type
*/
export type Movie = z.infer<typeof MovieP>;

View File

@ -28,15 +28,15 @@ export const Serie = z
runtime: z.number().nullable(),
externalId: Metadata,
entriesCount: z.number().int(),
availableCount: z.number().int(),
poster: Image.nullable(),
thumbnail: Image.nullable(),
banner: Image.nullable(),
logo: Image.nullable(),
trailerUrl: z.string().optional().nullable(),
entriesCount: z.number().int(),
availableCount: z.number().int(),
createdAt: zdate(),
updatedAt: zdate(),
@ -55,7 +55,7 @@ export const Serie = z
})
.transform((x) => ({
...x,
href: `/serie/${x.slug}`,
href: `/series/${x.slug}`,
playHref: x.firstEntry ? `/watch/${x.firstEntry.slug}` : null,
}));

11
front/src/models/video.ts Normal file
View File

@ -0,0 +1,11 @@
import { z } from "zod";
export const EmbeddedVideo = z.object({
id: z.string(),
slug: z.string(),
path: z.string(),
rendering: z.string(),
part: z.number().int().gt(0).nullable(),
version: z.number().gt(0),
});
export type EmbeddedVideo = z.infer<typeof EmbeddedVideo>;