mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-06-01 04:34:50 -04:00
Add external links on show/movie page
This commit is contained in:
parent
a825912ad3
commit
b995b86cee
37
front/packages/models/src/resources/metadata.ts
Normal file
37
front/packages/models/src/resources/metadata.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const MetadataP = z.record(
|
||||||
|
z.object({
|
||||||
|
/*
|
||||||
|
* The ID of the resource on the external provider.
|
||||||
|
*/
|
||||||
|
dataId: z.string(),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The URL of the resource on the external provider.
|
||||||
|
*/
|
||||||
|
link: z.string().nullable(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
export type Metadata = z.infer<typeof MetadataP>;
|
@ -25,6 +25,7 @@ import { Genre } from "./genre";
|
|||||||
import { StudioP } from "./studio";
|
import { StudioP } from "./studio";
|
||||||
import { Status } from "./show";
|
import { Status } from "./show";
|
||||||
import { CollectionP } from "./collection";
|
import { CollectionP } from "./collection";
|
||||||
|
import { MetadataP } from "./metadata";
|
||||||
|
|
||||||
export const MovieP = withImages(
|
export const MovieP = withImages(
|
||||||
ResourceP.extend({
|
ResourceP.extend({
|
||||||
@ -94,6 +95,10 @@ export const MovieP = withImages(
|
|||||||
*/
|
*/
|
||||||
hls: z.string().transform(imageFn),
|
hls: z.string().transform(imageFn),
|
||||||
}),
|
}),
|
||||||
|
/**
|
||||||
|
* The link to metadata providers that this show has.
|
||||||
|
*/
|
||||||
|
externalId: MetadataP,
|
||||||
}),
|
}),
|
||||||
"movies",
|
"movies",
|
||||||
)
|
)
|
||||||
|
@ -26,6 +26,7 @@ import { SeasonP } from "./season";
|
|||||||
import { StudioP } from "./studio";
|
import { StudioP } from "./studio";
|
||||||
import { BaseEpisodeP } from "./episode.base";
|
import { BaseEpisodeP } from "./episode.base";
|
||||||
import { CollectionP } from "./collection";
|
import { CollectionP } from "./collection";
|
||||||
|
import { MetadataP } from "./metadata";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The enum containing show's status.
|
* The enum containing show's status.
|
||||||
@ -99,6 +100,10 @@ export const ShowP = withImages(
|
|||||||
* The first episode of this show
|
* The first episode of this show
|
||||||
*/
|
*/
|
||||||
firstEpisode: BaseEpisodeP.optional().nullable(),
|
firstEpisode: BaseEpisodeP.optional().nullable(),
|
||||||
|
/**
|
||||||
|
* The link to metadata providers that this show has.
|
||||||
|
*/
|
||||||
|
externalId: MetadataP,
|
||||||
}),
|
}),
|
||||||
"shows",
|
"shows",
|
||||||
)
|
)
|
||||||
|
@ -18,35 +18,54 @@
|
|||||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { rem, Stylable, Theme, useYoshiki } from "yoshiki/native";
|
import { px, rem, Stylable, Theme, useYoshiki } from "yoshiki/native";
|
||||||
import { P } from "./text";
|
import { P } from "./text";
|
||||||
import { ts } from "./utils";
|
import { ts } from "./utils";
|
||||||
|
import { A } from "./links";
|
||||||
|
import { ComponentType } from "react";
|
||||||
|
|
||||||
export const Chip = ({
|
export const Chip = <AsProps = { label: string },>({
|
||||||
label,
|
|
||||||
color,
|
color,
|
||||||
size = "medium",
|
size = "medium",
|
||||||
|
outline = false,
|
||||||
|
as,
|
||||||
...props
|
...props
|
||||||
}: { label: string; color?: string; size?: "small" | "medium" | "large" } & Stylable) => {
|
}: {
|
||||||
|
color?: string;
|
||||||
|
size?: "small" | "medium" | "large";
|
||||||
|
outline?: boolean;
|
||||||
|
as?: ComponentType<AsProps>;
|
||||||
|
} & AsProps) => {
|
||||||
const { css } = useYoshiki();
|
const { css } = useYoshiki();
|
||||||
|
|
||||||
const sizeMult = size == "medium" ? 1 : size == "small" ? 0.75 : 1.25;
|
const sizeMult = size == "medium" ? 1 : size == "small" ? 0.75 : 1.25;
|
||||||
|
|
||||||
|
const As = as ?? (P as any);
|
||||||
|
// @ts-ignore backward compatibilty
|
||||||
|
if (!as && props.label) props.children = props.label;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<P
|
<As
|
||||||
{...css(
|
{...css(
|
||||||
{
|
[
|
||||||
pY: ts(1 * sizeMult),
|
{
|
||||||
pX: ts(1.5 * sizeMult),
|
pY: ts(1 * sizeMult),
|
||||||
borderRadius: ts(3),
|
pX: ts(1.5 * sizeMult),
|
||||||
fontSize: rem(0.8),
|
borderRadius: ts(3),
|
||||||
color: (theme: Theme) => theme.contrast,
|
fontSize: rem(0.8),
|
||||||
bg: color ?? ((theme: Theme) => theme.accent),
|
},
|
||||||
},
|
!outline && {
|
||||||
|
color: (theme: Theme) => theme.contrast,
|
||||||
|
bg: color ?? ((theme: Theme) => theme.accent),
|
||||||
|
},
|
||||||
|
outline && {
|
||||||
|
borderColor: color ?? ((theme: Theme) => theme.accent),
|
||||||
|
borderStyle: "solid",
|
||||||
|
borderWidth: px(1),
|
||||||
|
},
|
||||||
|
],
|
||||||
props,
|
props,
|
||||||
)}
|
)}
|
||||||
>
|
/>
|
||||||
{label}
|
|
||||||
</P>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -86,7 +86,7 @@ export const Link = ({
|
|||||||
target,
|
target,
|
||||||
children,
|
children,
|
||||||
...props
|
...props
|
||||||
}: { href?: string; target?: string; replace?: boolean } & PressableProps) => {
|
}: { href?: string | null; target?: string; replace?: boolean } & PressableProps) => {
|
||||||
const linkProps = useLink({
|
const linkProps = useLink({
|
||||||
href: href ?? "#",
|
href: href ?? "#",
|
||||||
replace,
|
replace,
|
||||||
|
@ -332,7 +332,14 @@ const Description = ({
|
|||||||
<P {...css({ textAlign: "justify" })}>{overview ?? t("show.noOverview")}</P>
|
<P {...css({ textAlign: "justify" })}>{overview ?? t("show.noOverview")}</P>
|
||||||
)}
|
)}
|
||||||
</Skeleton>
|
</Skeleton>
|
||||||
<View {...css({ flexWrap: "wrap", flexDirection: "row", marginTop: ts(0.5) })}>
|
<View
|
||||||
|
{...css({
|
||||||
|
flexWrap: "wrap",
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
marginTop: ts(0.5),
|
||||||
|
})}
|
||||||
|
>
|
||||||
<P {...css({ marginRight: ts(0.5) })}>{t("show.tags")}:</P>
|
<P {...css({ marginRight: ts(0.5) })}>{t("show.tags")}:</P>
|
||||||
{(isLoading ? [...Array<string>(3)] : tags!).map((tag, i) => (
|
{(isLoading ? [...Array<string>(3)] : tags!).map((tag, i) => (
|
||||||
<Chip key={tag ?? i} label={tag} size="small" {...css({ m: ts(0.5) })} />
|
<Chip key={tag ?? i} label={tag} size="small" {...css({ m: ts(0.5) })} />
|
||||||
@ -373,6 +380,7 @@ export const Header = ({
|
|||||||
type: "movie" | "show";
|
type: "movie" | "show";
|
||||||
}) => {
|
}) => {
|
||||||
const { css } = useYoshiki();
|
const { css } = useYoshiki();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fetch query={query}>
|
<Fetch query={query}>
|
||||||
@ -407,6 +415,41 @@ export const Header = ({
|
|||||||
tags={data?.tags}
|
tags={data?.tags}
|
||||||
{...css({ paddingTop: { xs: 0, md: ts(2) } })}
|
{...css({ paddingTop: { xs: 0, md: ts(2) } })}
|
||||||
/>
|
/>
|
||||||
|
<Container
|
||||||
|
{...css({
|
||||||
|
flexWrap: "wrap",
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
marginTop: ts(0.5),
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<P {...css({ marginRight: ts(0.5), textAlign: "center" })}>{t("show.links")}:</P>
|
||||||
|
{(!isLoading
|
||||||
|
? Object.entries(data.externalId!).filter(([_, data]) => data.link)
|
||||||
|
: [...Array(3)].map((_, i) => [i, undefined] as const)
|
||||||
|
).map(([name, data]) => (
|
||||||
|
<Chip
|
||||||
|
key={name}
|
||||||
|
as={Link}
|
||||||
|
href={data?.link}
|
||||||
|
target="_blank"
|
||||||
|
size="small"
|
||||||
|
outline
|
||||||
|
{...css({
|
||||||
|
m: ts(0.5),
|
||||||
|
color: "inherit",
|
||||||
|
fover: {
|
||||||
|
self: {
|
||||||
|
color: (theme: Theme) => theme.contrast,
|
||||||
|
bg: (theme: Theme) => theme.accent,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{data ? name : <Skeleton {...css({ width: rem(3) })} />}
|
||||||
|
</Chip>
|
||||||
|
))}
|
||||||
|
</Container>
|
||||||
{data?.collections && (
|
{data?.collections && (
|
||||||
<Container {...css({ marginY: ts(2) })}>
|
<Container {...css({ marginY: ts(2) })}>
|
||||||
{data.collections.map((x) => (
|
{data.collections.map((x) => (
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
"episode-none": "There is no episodes in this season",
|
"episode-none": "There is no episodes in this season",
|
||||||
"episodeNoMetadata": "No metadata available",
|
"episodeNoMetadata": "No metadata available",
|
||||||
"tags": "Tags",
|
"tags": "Tags",
|
||||||
|
"links": "Links",
|
||||||
"jumpToSeason": "Jump to season",
|
"jumpToSeason": "Jump to season",
|
||||||
"partOf": "Part of the"
|
"partOf": "Part of the"
|
||||||
},
|
},
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
"episode-none": "Il n'y a pas d'épisodes dans cette saison",
|
"episode-none": "Il n'y a pas d'épisodes dans cette saison",
|
||||||
"episodeNoMetadata": "Aucune metadonnée disponible",
|
"episodeNoMetadata": "Aucune metadonnée disponible",
|
||||||
"tags": "Tags",
|
"tags": "Tags",
|
||||||
|
"links": "Liens",
|
||||||
"jumpToSeason": "Aller sur une saison",
|
"jumpToSeason": "Aller sur une saison",
|
||||||
"partOf": "Fait parti de"
|
"partOf": "Fait parti de"
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user