diff --git a/back/src/Kyoo.Abstractions/Models/LibraryItem.cs b/back/src/Kyoo.Abstractions/Models/LibraryItem.cs
index 4650ae3b..6190bf6f 100644
--- a/back/src/Kyoo.Abstractions/Models/LibraryItem.cs
+++ b/back/src/Kyoo.Abstractions/Models/LibraryItem.cs
@@ -130,6 +130,16 @@ namespace Kyoo.Abstractions.Models
///
public Dictionary ExternalId { get; set; } = new();
+ ///
+ /// Links to watch this movie.
+ ///
+ public VideoLinks? Links => Kind == ItemKind.Movie ? new()
+ {
+ Direct = $"/video/movie/{Slug}/direct",
+ Hls = $"/video/movie/{Slug}/master.m3u8",
+ }
+ : null;
+
public LibraryItem() { }
[JsonConstructor]
diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs b/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs
index f9161d96..d5a49da7 100644
--- a/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs
+++ b/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs
@@ -163,7 +163,7 @@ namespace Kyoo.Abstractions.Models
///
/// Links to watch this episode.
///
- public object Links => new
+ public VideoLinks Links => new()
{
Direct = $"/video/episode/{Slug}/direct",
Hls = $"/video/episode/{Slug}/master.m3u8",
diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Movie.cs b/back/src/Kyoo.Abstractions/Models/Resources/Movie.cs
index de9b6243..850bc3fc 100644
--- a/back/src/Kyoo.Abstractions/Models/Resources/Movie.cs
+++ b/back/src/Kyoo.Abstractions/Models/Resources/Movie.cs
@@ -124,7 +124,7 @@ namespace Kyoo.Abstractions.Models
///
/// Links to watch this movie.
///
- public object Links => new
+ public VideoLinks Links => new()
{
Direct = $"/video/movie/{Slug}/direct",
Hls = $"/video/movie/{Slug}/master.m3u8",
diff --git a/back/src/Kyoo.Abstractions/Models/VideoLinks.cs b/back/src/Kyoo.Abstractions/Models/VideoLinks.cs
new file mode 100644
index 00000000..b0c2cb3b
--- /dev/null
+++ b/back/src/Kyoo.Abstractions/Models/VideoLinks.cs
@@ -0,0 +1,36 @@
+// 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 .
+
+namespace Kyoo.Abstractions.Models
+{
+ ///
+ /// The links to see a movie or an episode.
+ ///
+ public class VideoLinks
+ {
+ ///
+ /// The direct link to the unprocessed video (pristine quality).
+ ///
+ public string Direct { get; set; }
+
+ ///
+ /// The link to an HLS master playlist containing all qualities available for this video.
+ ///
+ public string Hls { get; set; }
+ }
+}
diff --git a/back/src/Kyoo.Postgresql/DatabaseContext.cs b/back/src/Kyoo.Postgresql/DatabaseContext.cs
index aa9e5ca5..46605074 100644
--- a/back/src/Kyoo.Postgresql/DatabaseContext.cs
+++ b/back/src/Kyoo.Postgresql/DatabaseContext.cs
@@ -320,6 +320,11 @@ namespace Kyoo.Postgresql
modelBuilder.Entity()
.HasIndex(x => x.Slug)
.IsUnique();
+
+ modelBuilder.Entity()
+ .Ignore(x => x.Links);
+ modelBuilder.Entity()
+ .Ignore(x => x.Links);
}
///
diff --git a/back/src/Kyoo.Swagger/OperationPermissionProcessor.cs b/back/src/Kyoo.Swagger/OperationPermissionProcessor.cs
index 3945f8fd..25781efb 100644
--- a/back/src/Kyoo.Swagger/OperationPermissionProcessor.cs
+++ b/back/src/Kyoo.Swagger/OperationPermissionProcessor.cs
@@ -67,7 +67,7 @@ namespace Kyoo.Swagger
Kind? kind = controller.Type == null
? controller.Kind
: cur.Kind;
- ICollection permissions = _GetPermissionsList(agg, group!.Value);
+ ICollection permissions = _GetPermissionsList(agg, group ?? Group.Overall);
permissions.Add($"{type}.{kind!.Value.ToString().ToLower()}");
agg[nameof(Kyoo)] = permissions;
return agg;
diff --git a/front/apps/mobile/app/movie/[slug].tsx b/front/apps/mobile/app/movie/[slug]/index.tsx
similarity index 100%
rename from front/apps/mobile/app/movie/[slug].tsx
rename to front/apps/mobile/app/movie/[slug]/index.tsx
diff --git a/front/apps/mobile/app/movie/[slug]/watch.tsx b/front/apps/mobile/app/movie/[slug]/watch.tsx
new file mode 100644
index 00000000..25758b2b
--- /dev/null
+++ b/front/apps/mobile/app/movie/[slug]/watch.tsx
@@ -0,0 +1,34 @@
+/*
+ * 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 { Player } from "@kyoo/ui";
+import { withRoute } from "../../../utils";
+
+export default withRoute(
+ Player,
+ {
+ options: {
+ headerShown: false,
+ },
+ statusBar: { hidden: true },
+ fullscreen: true,
+ },
+ { type: "movie" },
+);
diff --git a/front/apps/mobile/app/watch/[slug].tsx b/front/apps/mobile/app/watch/[slug].tsx
index ca211966..ecfdbdf7 100644
--- a/front/apps/mobile/app/watch/[slug].tsx
+++ b/front/apps/mobile/app/watch/[slug].tsx
@@ -21,10 +21,14 @@
import { Player } from "@kyoo/ui";
import { withRoute } from "../../utils";
-export default withRoute(Player, {
- options: {
- headerShown: false,
+export default withRoute(
+ Player,
+ {
+ options: {
+ headerShown: false,
+ },
+ statusBar: { hidden: true },
+ fullscreen: true,
},
- statusBar: { hidden: true },
- fullscreen: true,
-});
+ { type: "episode" },
+);
diff --git a/front/apps/mobile/utils.tsx b/front/apps/mobile/utils.tsx
index 978891fc..fb201bac 100644
--- a/front/apps/mobile/utils.tsx
+++ b/front/apps/mobile/utils.tsx
@@ -42,6 +42,7 @@ export const withRoute = (
statusBar?: StatusBarProps;
fullscreen?: boolean;
},
+ defaultProps?: Partial,
) => {
const { statusBar, fullscreen, ...routeOptions } = options ?? {};
const WithUseRoute = (props: any) => {
@@ -51,7 +52,7 @@ export const withRoute = (
{routeOptions && }
{statusBar && }
{fullscreen && }
-
+
>
);
};
diff --git a/front/apps/web/src/pages/movie/[slug].tsx b/front/apps/web/src/pages/movie/[slug]/index.tsx
similarity index 100%
rename from front/apps/web/src/pages/movie/[slug].tsx
rename to front/apps/web/src/pages/movie/[slug]/index.tsx
diff --git a/front/apps/web/src/pages/movie/[slug]/watch.tsx b/front/apps/web/src/pages/movie/[slug]/watch.tsx
new file mode 100644
index 00000000..17349725
--- /dev/null
+++ b/front/apps/web/src/pages/movie/[slug]/watch.tsx
@@ -0,0 +1,24 @@
+/*
+ * 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 { Player } from "@kyoo/ui";
+import { withRoute } from "~/router";
+
+export default withRoute(Player, { type: "movie" });
diff --git a/front/apps/web/src/pages/watch/[slug].tsx b/front/apps/web/src/pages/watch/[slug].tsx
index 006fcb49..a26ad2ca 100644
--- a/front/apps/web/src/pages/watch/[slug].tsx
+++ b/front/apps/web/src/pages/watch/[slug].tsx
@@ -21,4 +21,4 @@
import { Player } from "@kyoo/ui";
import { withRoute } from "~/router";
-export default withRoute(Player);
+export default withRoute(Player, { type: "episode" });
diff --git a/front/apps/web/src/router.tsx b/front/apps/web/src/router.tsx
index 3ee341a8..571fa0dd 100644
--- a/front/apps/web/src/router.tsx
+++ b/front/apps/web/src/router.tsx
@@ -21,12 +21,12 @@
import { useRouter } from "next/router";
import { ComponentType } from "react";
-export const withRoute = (Component: ComponentType) => {
+export const withRoute = (Component: ComponentType, defaultProps?: Partial) => {
const WithUseRoute = (props: Props) => {
const router = useRouter();
// @ts-ignore
- return ;
+ return ;
};
const { ...all } = Component;
diff --git a/front/packages/models/src/resources/episode.ts b/front/packages/models/src/resources/episode.ts
index 10789a19..fff3be27 100644
--- a/front/packages/models/src/resources/episode.ts
+++ b/front/packages/models/src/resources/episode.ts
@@ -20,8 +20,9 @@
import { z } from "zod";
import { zdate } from "../utils";
-import { ImagesP } from "../traits";
+import { ImagesP, imageFn } from "../traits";
import { ResourceP } from "../traits/resource";
+import { ShowP } from "./show";
const BaseEpisodeP = ResourceP.merge(ImagesP).extend({
/**
@@ -54,6 +55,23 @@ const BaseEpisodeP = ResourceP.merge(ImagesP).extend({
* 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().transform(imageFn),
+
+ /**
+ * The link to an HLS master playlist containing all qualities available for this video.
+ */
+ hls: z.string().transform(imageFn),
+ }),
+
+ show: ShowP.optional()
});
export const EpisodeP = BaseEpisodeP.extend({
diff --git a/front/packages/models/src/resources/index.ts b/front/packages/models/src/resources/index.ts
index 64aa2385..5f06357f 100644
--- a/front/packages/models/src/resources/index.ts
+++ b/front/packages/models/src/resources/index.ts
@@ -27,5 +27,5 @@ export * from "./person";
export * from "./studio";
export * from "./episode";
export * from "./season";
-export * from "./watch-item";
+export * from "./watch-info";
export * from "./user";
diff --git a/front/packages/models/src/resources/movie.ts b/front/packages/models/src/resources/movie.ts
index 402583c0..9571f042 100644
--- a/front/packages/models/src/resources/movie.ts
+++ b/front/packages/models/src/resources/movie.ts
@@ -20,7 +20,7 @@
import { z } from "zod";
import { zdate } from "../utils";
-import { ImagesP, ResourceP } from "../traits";
+import { ImagesP, ResourceP, imageFn } from "../traits";
import { Genre } from "./genre";
import { StudioP } from "./studio";
import { Status } from "./show";
@@ -67,6 +67,21 @@ export const MovieP = ResourceP.merge(ImagesP).extend({
* The studio that made this movie.
*/
studio: StudioP.optional().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().transform(imageFn),
+
+ /**
+ * The link to an HLS master playlist containing all qualities available for this video.
+ */
+ hls: z.string().transform(imageFn),
+ }),
});
/**
diff --git a/front/packages/models/src/resources/watch-info.ts b/front/packages/models/src/resources/watch-info.ts
new file mode 100644
index 00000000..4c2ed400
--- /dev/null
+++ b/front/packages/models/src/resources/watch-info.ts
@@ -0,0 +1,118 @@
+/*
+ * 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 { z } from "zod";
+import { imageFn } from "../traits";
+
+/**
+ * A audio or subtitle track.
+ */
+export const TrackP = z.object({
+ /**
+ * The index of this track on the episode.
+ */
+ index: z.number(),
+ /**
+ * The title of the stream.
+ */
+ title: z.string().nullable(),
+ /**
+ * The language of this stream (as a ISO-639-2 language code)
+ */
+ language: z.string().nullable(),
+ /**
+ * The codec of this stream.
+ */
+ codec: z.string(),
+ /**
+ * Is this stream the default one of it's type?
+ */
+ isDefault: z.boolean(),
+ /**
+ * Is this stream tagged as forced?
+ */
+ isForced: z.boolean(),
+});
+export type Audio = z.infer;
+
+export const SubtitleP = TrackP.extend({
+ /*
+ * The url of this track (only if this is a subtitle)..
+ */
+ link: z.string().transform(imageFn).nullable(),
+});
+export type Subtitle = z.infer;
+
+export const ChapterP = z.object({
+ /**
+ * The start time of the chapter (in second from the start of the episode).
+ */
+ startTime: z.number(),
+ /**
+ * The end time of the chapter (in second from the start of the episode).
+ */
+ endTime: z.number(),
+ /**
+ * The name of this chapter. This should be a human-readable name that could be presented to the
+ * user. There should be well-known chapters name for commonly used chapters. For example, use
+ * "Opening" for the introduction-song and "Credits" for the end chapter with credits.
+ */
+ name: z.string(),
+});
+export type Chapter = z.infer;
+
+/**
+ * The transcoder's info for this item. This include subtitles, fonts, chapters...
+ */
+export const WatchInfoP = z.object({
+ /**
+ * The sha1 of the video file.
+ */
+ sha: z.string(),
+ /**
+ * The internal path of the video file.
+ */
+ path: z.string(),
+ /**
+ * The container of the video file of this episode. Common containers are mp4, mkv, avi and so on.
+ */
+ container: z.string(),
+ /**
+ * The list of audio tracks.
+ */
+ audios: z.array(TrackP),
+ /**
+ * The list of subtitles tracks.
+ */
+ subtitles: z.array(SubtitleP),
+ /**
+ * The list of fonts that can be used to display subtitles.
+ */
+ fonts: z.array(z.string().transform(imageFn)),
+ /**
+ * The list of chapters. See Chapter for more information.
+ */
+ chapters: z.array(ChapterP),
+});
+
+/**
+ * A watch info for a video
+ */
+export type WatchInfo = z.infer;
diff --git a/front/packages/models/src/resources/watch-item.ts b/front/packages/models/src/resources/watch-item.ts
deleted file mode 100644
index 66901505..00000000
--- a/front/packages/models/src/resources/watch-item.ts
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * 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 { z } from "zod";
-import { zdate } from "../utils";
-import { ImagesP, imageFn } from "../traits";
-import { EpisodeP } from "./episode";
-
-/**
- * A audio or subtitle track.
- */
-export const TrackP = z.object({
- /**
- * The index of this track on the episode.
- */
- index: z.number(),
- /**
- * The title of the stream.
- */
- title: z.string().nullable(),
- /**
- * The language of this stream (as a ISO-639-2 language code)
- */
- language: z.string().nullable(),
- /**
- * The codec of this stream.
- */
- codec: z.string(),
- /**
- * Is this stream the default one of it's type?
- */
- isDefault: z.boolean(),
- /**
- * Is this stream tagged as forced?
- */
- isForced: z.boolean(),
-});
-export type Audio = z.infer;
-
-export const SubtitleP = TrackP.extend({
- /*
- * The url of this track (only if this is a subtitle)..
- */
- link: z.string().transform(imageFn).nullable(),
-});
-export type Subtitle = z.infer;
-
-export const ChapterP = z.object({
- /**
- * The start time of the chapter (in second from the start of the episode).
- */
- startTime: z.number(),
- /**
- * The end time of the chapter (in second from the start of the episode).
- */
- endTime: z.number(),
- /**
- * The name of this chapter. This should be a human-readable name that could be presented to the
- * user. There should be well-known chapters name for commonly used chapters. For example, use
- * "Opening" for the introduction-song and "Credits" for the end chapter with credits.
- */
- name: z.string(),
-});
-export type Chapter = z.infer;
-
-const WatchMovieP = z.preprocess(
- (x: any) => {
- if (!x) return x;
-
- x.name = x.title;
- return x;
- },
- ImagesP.extend({
- /**
- * The slug of this episode.
- */
- slug: z.string(),
- /**
- * The title of this episode.
- */
- name: z.string().nullable(),
- /**
- * The sumarry of this episode.
- */
- overview: z.string().nullable(),
- /**
- * The release date of this episode. It can be null if unknown.
- */
- releaseDate: zdate().nullable(),
-
- /**
- * The transcoder's info for this item. This include subtitles, fonts, chapters...
- */
- info: z.object({
- /**
- * The sha1 of the video file.
- */
- sha: z.string(),
- /**
- * The internal path of the video file.
- */
- path: z.string(),
- /**
- * The container of the video file of this episode. Common containers are mp4, mkv, avi and so
- * on.
- */
- container: z.string(),
- /**
- * The list of audio tracks.
- */
- audios: z.array(TrackP),
- /**
- * The list of subtitles tracks.
- */
- subtitles: z.array(SubtitleP),
- /**
- * The list of fonts that can be used to display subtitles.
- */
- fonts: z.array(z.string().transform(imageFn)),
- /**
- * The list of chapters. See Chapter for more information.
- */
- chapters: z.array(ChapterP),
- }),
- /**
- * The links to the videos of this watch item.
- */
- link: z.object({
- direct: z.string().transform(imageFn),
- hls: z.string().transform(imageFn),
- }),
- }),
-);
-
-const WatchEpisodeP = WatchMovieP.and(
- z.object({
- /**
- * The ID of the episode associated with this item.
- */
- episodeID: z.number(),
- /**
- * The title of the show containing this episode.
- */
- showTitle: z.string(),
- /**
- * The slug of the show containing this episode
- */
- showSlug: z.string(),
- /**
- * The season in witch this episode is in.
- */
- seasonNumber: z.number().nullable(),
- /**
- * The number of this episode is 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(),
- }),
-);
-
-export const WatchItemP = z.union([
- WatchMovieP.and(z.object({ isMovie: z.literal(true) })),
- WatchEpisodeP.and(z.object({ isMovie: z.literal(false) })),
-]);
-
-/**
- * A watch item for a movie or an episode
- */
-export type WatchItem = z.infer;
diff --git a/front/packages/primitives/src/icons.tsx b/front/packages/primitives/src/icons.tsx
index 2698f1b9..b7cf9bd6 100644
--- a/front/packages/primitives/src/icons.tsx
+++ b/front/packages/primitives/src/icons.tsx
@@ -113,7 +113,7 @@ export const IconFab = (
bg: (theme) => theme.accent,
fover: {
self: {
- transform: [{ scale: 1.3 }],
+ transform: "scale(1.3)" as any,
bg: (theme: Theme) => theme.accent,
},
},
diff --git a/front/packages/ui/src/details/header.tsx b/front/packages/ui/src/details/header.tsx
index 92c8bbe7..64af0f61 100644
--- a/front/packages/ui/src/details/header.tsx
+++ b/front/packages/ui/src/details/header.tsx
@@ -76,6 +76,7 @@ const TitleLine = ({
poster,
studio,
trailerUrl,
+ type,
...props
}: {
isLoading: boolean;
@@ -86,6 +87,7 @@ const TitleLine = ({
poster?: KyooImage | null;
studio?: Studio | null;
trailerUrl?: string | null;
+ type: "movie" | "show";
} & Stylable) => {
const { css, theme } = useYoshiki();
const { t } = useTranslation();
@@ -193,7 +195,7 @@ const TitleLine = ({
; slug: string }) => {
+export const Header = ({ query, type, slug }: { query: QueryIdentifier; type: "movie" | "show", slug: string }) => {
const { css } = useYoshiki();
return (
@@ -365,6 +367,7 @@ export const Header = ({ query, slug }: { query: QueryIdentifier;
>
= ({ slug }) => {
},
)}
>
-
+
{/* */}
);
diff --git a/front/packages/ui/src/details/show.tsx b/front/packages/ui/src/details/show.tsx
index 8b2f34fa..bc8508d1 100644
--- a/front/packages/ui/src/details/show.tsx
+++ b/front/packages/ui/src/details/show.tsx
@@ -25,8 +25,8 @@ import { DefaultLayout } from "../layout";
import { EpisodeList } from "./season";
import { Header } from "./header";
import Svg, { Path, SvgProps } from "react-native-svg";
-import { Container, SwitchVariant } from "@kyoo/primitives";
-import { forwardRef, useCallback } from "react";
+import { Container } from "@kyoo/primitives";
+import { forwardRef } from "react";
const SvgWave = (props: SvgProps) => {
const { css } = useYoshiki();
@@ -42,7 +42,7 @@ const SvgWave = (props: SvgProps) => {
);
};
-const ShowHeader = forwardRef(function _ShowHeader(
+const ShowHeader = forwardRef(function ShowHeader(
{ children, slug, ...props },
ref,
) {
@@ -69,7 +69,7 @@ const ShowHeader = forwardRef(function _Show
)}
>
{/* TODO: Remove the slug quickfix for the play button */}
-
+
{/* */}
void;
show: boolean;
} & ViewProps) => {
- // TODO animate show
+ // TODO: animate show
const opacity = !show && (Platform.OS === "web" ? { opacity: 0 } : { display: "none" as const});
return (
@@ -126,7 +124,6 @@ export const Hover = ({
@@ -210,7 +207,7 @@ export const Back = ({
);
};
-const VideoPoster = ({ poster }: { poster?: string | null }) => {
+const VideoPoster = ({ poster }: { poster?: KyooImage | null }) => {
const { css } = useYoshiki();
return (
diff --git a/front/packages/ui/src/player/components/right-buttons.tsx b/front/packages/ui/src/player/components/right-buttons.tsx
index ae53a273..84ac76b1 100644
--- a/front/packages/ui/src/player/components/right-buttons.tsx
+++ b/front/packages/ui/src/player/components/right-buttons.tsx
@@ -18,7 +18,7 @@
* along with Kyoo. If not, see .
*/
-import { Subtitle, WatchItem } from "@kyoo/models";
+import { Subtitle } from "@kyoo/models";
import { IconButton, tooltip, Menu, ts } from "@kyoo/primitives";
import { useAtom } from "jotai";
import { Platform, View } from "react-native";
@@ -46,14 +46,12 @@ export const getDisplayName = (sub: Subtitle) => {
export const RightButtons = ({
subtitles,
fonts,
- qualities,
onMenuOpen,
onMenuClose,
...props
}: {
subtitles?: Subtitle[];
fonts?: string[];
- qualities?: WatchItem["link"];
onMenuOpen: () => void;
onMenuClose: () => void;
} & Stylable) => {
diff --git a/front/packages/ui/src/player/index.tsx b/front/packages/ui/src/player/index.tsx
index 8a96fba9..4cacbf33 100644
--- a/front/packages/ui/src/player/index.tsx
+++ b/front/packages/ui/src/player/index.tsx
@@ -18,10 +18,20 @@
* along with Kyoo. If not, see .
*/
-import { QueryIdentifier, QueryPage, WatchItem, WatchItemP, useFetch } from "@kyoo/models";
+import {
+ Episode,
+ EpisodeP,
+ Movie,
+ MovieP,
+ QueryIdentifier,
+ QueryPage,
+ WatchInfo,
+ WatchInfoP,
+ useFetch,
+} from "@kyoo/models";
import { Head } from "@kyoo/primitives";
import { useState, useEffect, ComponentProps } from "react";
-import { Platform, Pressable, PressableProps, StyleSheet, View, PointerEvent as NativePointerEvent } from "react-native";
+import { Platform, StyleSheet, View, PointerEvent as NativePointerEvent } from "react-native";
import { useTranslation } from "react-i18next";
import { useRouter } from "solito/router";
import { useAtom } from "jotai";
@@ -33,43 +43,47 @@ import { useVideoKeyboard } from "./keyboard";
import { MediaSessionManager } from "./media-session";
import { ErrorView } from "../fetch";
-const query = (slug: string): QueryIdentifier => ({
- path: ["watch", slug],
- parser: WatchItemP,
+type Item = (Movie & { type: "movie" }) | (Episode & { type: "episode" });
+
+const query = (type: string, slug: string): QueryIdentifier =>
+ type === "episode"
+ ? {
+ path: ["episode", slug],
+ params: {
+ fields: ["nextEpisode", "previousEpisode", "show"],
+ },
+ parser: EpisodeP.transform((x) => ({ ...x, type: "episode" })),
+ }
+ : {
+ path: ["movie", slug],
+ parser: MovieP.transform((x) => ({ ...x, type: "movie" })),
+ };
+const infoQuery = (type: string, slug: string): QueryIdentifier => ({
+ path: ["video", type, slug, "info"],
+ parser: WatchInfoP,
});
const mapData = (
- data: WatchItem | undefined,
+ data: Item | undefined,
+ info: WatchInfo | undefined,
previousSlug?: string,
nextSlug?: string,
): Partial> & { isLoading: boolean } => {
- if (!data) return { isLoading: true };
+ if (!data || !info) return { isLoading: true };
return {
isLoading: false,
- name: data.isMovie ? data.name : `${episodeDisplayNumber(data, "")} ${data.name}`,
- showName: data.isMovie ? data.name! : data.showTitle,
- href: data ? (data.isMovie ? `/movie/${data.slug}` : `/show/${data.showSlug}`) : "#",
+ name: data.type === "movie" ? data.name : `${episodeDisplayNumber(data, "")} ${data.name}`,
+ showName: data.type === "movie" ? data.name! : data.show!.name,
+ href: data ? (data.type === "movie" ? `/movie/${data.slug}` : `/show/${data.show!.slug}`) : "#",
poster: data.poster,
- qualities: data.link,
- subtitles: data.info.subtitles,
- chapters: data.info.chapters,
- fonts: data.info.fonts,
+ subtitles: info.subtitles,
+ chapters: info.chapters,
+ fonts: info.fonts,
previousSlug,
nextSlug,
};
};
-const PressView =
- Platform.OS === "web"
- ? View
- : ({
- onPointerDown,
- onMobilePress,
- ...props
- }: PressableProps & { onMobilePress: PressableProps["onPress"] }) => (
- onMobilePress?.(e)} {...props} />
- );
-
// Callback used to hide the controls when the mouse goes iddle. This is stored globally to clear the old timeout
// if the mouse moves again (if this is stored as a state, the whole page is redrawn on mouse move)
let mouseCallback: NodeJS.Timeout;
@@ -78,21 +92,24 @@ let mouseCallback: NodeJS.Timeout;
let touchCount = 0;
let touchTimeout: NodeJS.Timeout;
-export const Player: QueryPage<{ slug: string }> = ({ slug }) => {
+export const Player: QueryPage<{ slug: string; type: "episode" | "movie" }> = ({ slug, type }) => {
const { css } = useYoshiki();
const { t } = useTranslation();
const router = useRouter();
const [playbackError, setPlaybackError] = useState(undefined);
- const { data, error } = useFetch(query(slug));
+ const { data, error } = useFetch(query(type, slug));
+ const { data: info, error: infoError } = useFetch(infoQuery(type, slug));
const previous =
- data && !data.isMovie && data.previousEpisode
+ data && data.type === "episode" && data.previousEpisode
? `/watch/${data.previousEpisode.slug}`
: undefined;
const next =
- data && !data.isMovie && data.nextEpisode ? `/watch/${data.nextEpisode.slug}` : undefined;
+ data && data.type === "episode" && data.nextEpisode
+ ? `/watch/${data.nextEpisode.slug}`
+ : undefined;
- useVideoKeyboard(data?.info.subtitles, data?.info.fonts, previous, next);
+ useVideoKeyboard(info?.subtitles, info?.fonts, previous, next);
const [isFullscreen, setFullscreen] = useAtom(fullscreenAtom);
const [isPlaying, setPlay] = useAtom(playAtom);
@@ -139,17 +156,17 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => {
setFullscreen(!isFullscreen);
clearTimeout(touchTimeout);
} else
- touchTimeout = setTimeout(() => {
- touchCount = 0;
- }, 400);
+ touchTimeout = setTimeout(() => {
+ touchCount = 0;
+ }, 400);
setPlay(!isPlaying);
};
- if (error || playbackError)
+ if (error || infoError || playbackError)
return (
<>
theme.accent })} />
-
+
>
);
@@ -158,22 +175,22 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => {
{data && (
)}
@@ -190,20 +207,20 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => {
})}
>