diff --git a/front/src/models/resources/movie.ts b/front/src/models/resources/movie.ts
index 0f7f4505..44fa0305 100644
--- a/front/src/models/resources/movie.ts
+++ b/front/src/models/resources/movie.ts
@@ -18,34 +18,62 @@
* along with Kyoo. If not, see .
*/
-import { Resource, Images } from "../traits";
+import { z } from "zod";
+import { zdate } from "~/utils/zod";
+import { ImagesP, ResourceP } from "../traits";
+import { GenreP } from "./genre";
+import { StudioP } from "./studio";
/**
- * A series or a movie.
+ * The enum containing movie's status.
*/
-export interface Movie extends Resource, Images {
- /**
- * The title of this show.
- */
- name: string;
-
- /**
- * The list of alternative titles of this movie.
- */
- aliases: string[];
-
- /**
- * The summary of this show.
- */
- overview: string;
-
- /**
- * Is this movie aired or planned
- */
- isPlanned: boolean;
-
- /**
- * The date this mavie aired. It can also be null if this is unknown.
- */
- airDate: Date | null;
+export enum MovieStatus {
+ Unknown = 0,
+ Finished = 1,
+ Planned = 3,
}
+
+export const MovieP = z.preprocess(
+ (x: any) => {
+ // Waiting for the API to be updaded
+ x.name = x.title;
+ if (x.aliases === null) x.aliases = [];
+ x.airDate = x.startAir;
+ return x;
+ },
+ ResourceP.merge(ImagesP).extend({
+ /**
+ * The title of this movie.
+ */
+ name: z.string(),
+ /**
+ * The list of alternative titles of this movie.
+ */
+ aliases: z.array(z.string()),
+ /**
+ * The summary of this movie.
+ */
+ overview: z.string().nullable(),
+ /**
+ * Is this movie not aired yet or finished?
+ */
+ status: z.nativeEnum(MovieStatus),
+ /**
+ * The date this movie aired. It can also be null if this is unknown.
+ */
+ airDate: zdate().nullable(),
+ /**
+ * The list of genres (themes) this movie has.
+ */
+ genres: z.array(GenreP).optional(),
+ /**
+ * The studio that made this movie.
+ */
+ studio: StudioP.optional().nullable(),
+ }),
+);
+
+/**
+ * A Movie type
+ */
+export type Movie = z.infer;
diff --git a/front/src/models/utils.ts b/front/src/models/utils.ts
index a8a1cab1..447bd22f 100644
--- a/front/src/models/utils.ts
+++ b/front/src/models/utils.ts
@@ -18,9 +18,22 @@
* along with Kyoo. If not, see .
*/
-export const getDisplayDate = (startAir: Date, endAir?: Date | null) => {
- if (!endAir || startAir.getFullYear() === endAir.getFullYear()) {
- return startAir.getFullYear();
+import { Movie, Show } from "./resources";
+
+export const getDisplayDate = (data: Show | Movie) => {
+ const {
+ startAir,
+ endAir,
+ airDate,
+ }: { startAir?: Date | null; endAir?: Date | null; airDate?: Date | null } = data;
+
+ if (startAir) {
+ if (!endAir || startAir.getFullYear() === endAir.getFullYear()) {
+ return startAir.getFullYear();
+ }
+ return startAir.getFullYear() + (endAir ? ` - ${endAir.getFullYear()}` : "");
+ }
+ else if (airDate) {
+ return airDate.getFullYear();
}
- return startAir.getFullYear() + (endAir ? ` - ${endAir.getFullYear()}` : "");
};
diff --git a/front/src/pages/movie/[slug].tsx b/front/src/pages/movie/[slug].tsx
new file mode 100644
index 00000000..bab2ec71
--- /dev/null
+++ b/front/src/pages/movie/[slug].tsx
@@ -0,0 +1,309 @@
+/*
+ * Kyoo - A portable and vast media library solution.
+ * Copyright (c) Kyoo.
+ *
+ * See AUTHORS.md and LICENSE file in the project root for full license information.
+ *
+ * Kyoo is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * Kyoo is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Kyoo. If not, see .
+ */
+
+import { LocalMovies, PlayArrow } from "@mui/icons-material";
+import {
+ Box,
+ Divider,
+ Fab,
+ IconButton,
+ Skeleton,
+ SxProps,
+ Tooltip,
+ Typography,
+} from "@mui/material";
+import useTranslation from "next-translate/useTranslation";
+import Head from "next/head";
+import { Navbar } from "~/components/navbar";
+import { Image, Poster } from "~/components/poster";
+import { Movie, MovieP, Show } from "~/models";
+import { QueryIdentifier, QueryPage, useFetch, useInfiniteFetch } from "~/utils/query";
+import { getDisplayDate } from "~/models/utils";
+import { withRoute } from "~/utils/router";
+import { Container } from "~/components/container";
+import { makeTitle } from "~/utils/utils";
+import { Link } from "~/utils/link";
+import { Studio } from "~/models/resources/studio";
+import { Person, PersonP } from "~/models";
+import { PersonAvatar } from "~/components/person";
+import { ErrorComponent, ErrorPage } from "~/components/errors";
+import { HorizontalList } from "~/components/horizontal-list";
+
+const StudioText = ({
+ studio,
+ loading = false,
+ sx,
+}: {
+ studio?: Studio | null;
+ loading?: boolean;
+ sx?: SxProps;
+}) => {
+ const { t } = useTranslation("browse");
+
+ if (!loading && !studio) return null;
+ return (
+
+ {t("show.studio")}:{" "}
+ {loading ? (
+
+ ) : (
+ {studio!.name}
+ )}
+
+ );
+};
+
+export const ShowHeader = ({ data }: { data?: Show | Movie }) => {
+ /* const scroll = useScroll(); */
+ const { t } = useTranslation("browse");
+ // TODO: tweek the navbar color with the theme.
+
+ return (
+ <>
+ {/* TODO: Add a shadow on navbar items */}
+ {/* TODO: Put the navbar outside of the scrollbox */}
+
+
+
+
+
+
+
+ {data?.name ?? }
+
+ {(!data || getDisplayDate(data)) && (
+
+ {data != undefined ? (
+ getDisplayDate(data)
+ ) : (
+
+ )}
+
+ )}
+ *": { m: ".3rem !important" } }}>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {data?.logo && (
+
+ )}
+
+
+
+
+
+
+
+ {t("show.genre")}
+ {": "}
+ {!data ? (
+
+ ) : data?.genres && data.genres.length ? (
+ data.genres.map((genre, i) => [
+ i > 0 && ", ",
+
+ {genre.name}
+ ,
+ ])
+ ) : (
+ t("show.genre-none")
+ )}
+
+
+
+
+
+ {data
+ ? data.overview ?? t("show.noOverview")
+ : [...Array(4)].map((_, i) => )}
+
+
+
+
+
+
+ {t("show.genre")}
+
+ {!data || data.genres?.length ? (
+