diff --git a/front/src/models/video.ts b/front/src/models/video.ts
index 06004a15..50b81263 100644
--- a/front/src/models/video.ts
+++ b/front/src/models/video.ts
@@ -27,9 +27,10 @@ export const Video = z.object({
// Name of the tool that made the guess
from: z.string(),
- get history() {
- return z.array(Video.shape.guess.omit({ history: true })).default([]);
- },
+ // Adding that results in an infinite recursion
+ // get history() {
+ // return z.array(Video.shape.guess.omit({ history: true })).default([]);
+ // },
}),
createdAt: zdate(),
updatedAt: zdate(),
diff --git a/front/src/primitives/image-background.tsx b/front/src/primitives/image-background.tsx
index bc1d4ce0..5eaaf0ac 100644
--- a/front/src/primitives/image-background.tsx
+++ b/front/src/primitives/image-background.tsx
@@ -25,7 +25,7 @@ export const ImageBackground = ({
layout: ImageLayout;
children: ReactNode;
}) => {
- const { css } = useYoshiki();
+ const { css, theme } = useYoshiki();
const { apiUrl, authToken } = useToken();
return (
@@ -42,7 +42,10 @@ export const ImageBackground = ({
}}
placeholder={{ blurhash: src?.blurhash }}
accessibilityLabel={alt}
- {...(css([layout, { overflow: "hidden" }], props) as any)}
+ {...(css(
+ [layout, { overflow: "hidden", backgroundColor: theme.overlay0 }],
+ props,
+ ) as any)}
/>
);
};
diff --git a/front/src/primitives/image.tsx b/front/src/primitives/image.tsx
index 09954bd9..6476d9d5 100644
--- a/front/src/primitives/image.tsx
+++ b/front/src/primitives/image.tsx
@@ -34,7 +34,7 @@ export const Image = ({
style?: ImageStyle;
layout: ImageLayout;
}) => {
- const { css } = useYoshiki();
+ const { css, theme } = useYoshiki();
const { apiUrl, authToken } = useToken();
return (
@@ -51,7 +51,10 @@ export const Image = ({
}}
placeholder={{ blurhash: src?.blurhash }}
accessibilityLabel={alt}
- {...(css([layout, { borderRadius: 6 }], props) as any)}
+ {...(css(
+ [layout, { borderRadius: 6, backgroundColor: theme.overlay0 }],
+ props,
+ ) as any)}
/>
);
};
diff --git a/front/src/primitives/skeleton.tsx b/front/src/primitives/skeleton.tsx
index fd0c8129..4d798467 100644
--- a/front/src/primitives/skeleton.tsx
+++ b/front/src/primitives/skeleton.tsx
@@ -108,7 +108,6 @@ export const Skeleton = ({
colors={["transparent", theme.overlay1, "transparent"]}
style={[
StyleSheet.absoluteFillObject,
- { transform: [{ translateX: -width.value }] },
animated,
]}
/>
diff --git a/front/src/ui/player/controls/back.tsx b/front/src/ui/player/controls/back.tsx
index 43ab4cea..7cef2f03 100644
--- a/front/src/ui/player/controls/back.tsx
+++ b/front/src/ui/player/controls/back.tsx
@@ -11,7 +11,7 @@ import {
tooltip,
} from "~/primitives";
-export const Back = ({ name, ...props }: { name: string } & ViewProps) => {
+export const Back = ({ name, ...props }: { name?: string } & ViewProps) => {
const { css } = useYoshiki();
const { t } = useTranslation();
const router = useRouter();
@@ -35,49 +35,19 @@ export const Back = ({ name, ...props }: { name: string } & ViewProps) => {
onPress={router.back}
{...tooltip(t("player.back"))}
/>
-
- {name}
-
-
- );
-};
-
-Back.Loader = (props: ViewProps) => {
- const { css } = useYoshiki();
- const { t } = useTranslation();
- const router = useRouter();
-
- return (
- theme.darkOverlay,
- display: "flex",
- flexDirection: "row",
- alignItems: "center",
- padding: percent(0.33),
- color: "white",
- },
- props,
+ {name ? (
+
+ {name}
+
+ ) : (
+
)}
- >
-
-
);
};
diff --git a/front/src/ui/player/controls/bottom-controls.tsx b/front/src/ui/player/controls/bottom-controls.tsx
index 9fe79fb3..25d78505 100644
--- a/front/src/ui/player/controls/bottom-controls.tsx
+++ b/front/src/ui/player/controls/bottom-controls.tsx
@@ -4,7 +4,7 @@ import type { ComponentProps } from "react";
import { useTranslation } from "react-i18next";
import { Platform, View, type ViewProps } from "react-native";
import type { VideoPlayer } from "react-native-video";
-import { percent, useYoshiki } from "yoshiki/native";
+import { percent, rem, useYoshiki } from "yoshiki/native";
import type { Chapter, KImage } from "~/models";
import {
H2,
@@ -12,6 +12,7 @@ import {
Link,
type Menu,
Poster,
+ Skeleton,
tooltip,
ts,
useIsTouch,
@@ -31,11 +32,11 @@ export const BottomControls = ({
...props
}: {
player: VideoPlayer;
- poster: KImage;
- name: string;
+ poster?: KImage | null;
+ name?: string;
chapters: Chapter[];
- previous: string | null;
- next: string | null;
+ previous?: string | null;
+ next?: string | null;
setMenu: (isOpen: boolean) => void;
} & ViewProps) => {
const { css } = useYoshiki();
@@ -57,25 +58,34 @@ export const BottomControls = ({
position: "relative",
})}
>
-
+ {poster !== undefined ? (
+
+ ) : (
+
+ )}
-
- name
-
+ {name ? (
+
+ {name}
+
+ ) : (
+
+ )}
void;
}) => {
const { css } = useYoshiki();
@@ -116,7 +126,7 @@ const ControlButtons = ({
{...css(
{
flexDirection: "row",
- flexGrow: 1,
+ flex: 1,
justifyContent: "space-between",
flexWrap: "wrap",
},
@@ -124,7 +134,7 @@ const ControlButtons = ({
)}
>
- {isTouch && (
+ {!isTouch && (
{previous && (
{
const { css } = useYoshiki();
+ const insets = useSafeAreaInsets();
const isTouch = useIsTouch();
const [hover, setHover] = useState(false);
@@ -44,9 +47,13 @@ export const Controls = ({
} satisfies ViewProps;
return (
-
+
theme.darkOverlay,
+ paddingTop: insets.top,
+ paddingLeft: insets.left,
+ paddingRight: insets.right,
},
hoverControls,
)}
@@ -64,7 +74,7 @@ export const Controls = ({
)}
theme.darkOverlay,
+ paddingLeft: insets.left,
+ paddingRight: insets.right,
+ paddingBottom: insets.bottom,
},
hoverControls,
)}
diff --git a/front/src/ui/player/controls/progress.tsx b/front/src/ui/player/controls/progress.tsx
index 24a5030d..08f744e1 100644
--- a/front/src/ui/player/controls/progress.tsx
+++ b/front/src/ui/player/controls/progress.tsx
@@ -39,9 +39,9 @@ export const ProgressBar = ({
}}
setProgress={setSeek}
endSeek={() => {
- setProgress(seek!);
- setSeek(null);
+ player.seekTo(seek!);
setTimeout(player.play, 10);
+ setSeek(null);
}}
// onHover={(progress, layout) => {
// setHoverProgress(progress);
diff --git a/front/src/ui/player/controls/touch.tsx b/front/src/ui/player/controls/touch.tsx
index a330c2a6..9880e4e5 100644
--- a/front/src/ui/player/controls/touch.tsx
+++ b/front/src/ui/player/controls/touch.tsx
@@ -5,7 +5,7 @@ import {
Pressable,
type PressableProps,
} from "react-native";
-import type { VideoPlayer } from "react-native-video";
+import { useEvent, type VideoPlayer } from "react-native-video";
import { useYoshiki } from "yoshiki/native";
import { useIsTouch } from "~/primitives";
@@ -18,9 +18,14 @@ export const TouchControls = ({
const { css } = useYoshiki();
const isTouch = useIsTouch();
- const [_show, setShow] = useState(true);
+ const [playing, setPlay] = useState(player.isPlaying);
+ useEvent(player, "onPlaybackStateChange", (status) => {
+ setPlay(status.isPlaying);
+ });
+
+ const [_show, setShow] = useState(false);
const hideTimeout = useRef(null);
- const shouldShow = forceShow || _show;
+ const shouldShow = forceShow || _show || !playing;
const show = useCallback((val: boolean = true) => {
setShow(val);
if (hideTimeout.current) clearTimeout(hideTimeout.current);
diff --git a/front/src/ui/player/index.tsx b/front/src/ui/player/index.tsx
index c9c9749b..00429b1d 100644
--- a/front/src/ui/player/index.tsx
+++ b/front/src/ui/player/index.tsx
@@ -10,31 +10,15 @@ import { type QueryIdentifier, useFetch } from "~/query";
import { useQueryState } from "~/utils";
import { Controls, LoadingIndicator } from "./controls";
-const mapMetadata = (item: FullVideo | undefined) => {
- if (!item) return null;
-
- // TODO: map current entry using entries' duration & the current playtime
- const currentEntry = 0;
- const entry = item.entries[currentEntry] ?? item.entries[0];
- if (!entry) return null;
-
- return {
- currentEntry,
- title: `${entry.name} (${entryDisplayNumber(entry)})`,
- description: entry.description,
- subtitle: item.show!.kind !== "movie" ? item.show!.name : null,
- poster: item.show!.poster,
- thumbnail: item.show!.thumbnail,
- };
-};
-
export const Player = () => {
const [slug, setSlug] = useQueryState("slug", undefined!);
const [start, setStart] = useQueryState("t", undefined);
const { data, error } = useFetch(Player.query(slug));
const { data: info, error: infoError } = useFetch(Player.infoQuery(slug));
- const metadata = mapMetadata(data);
+ // TODO: map current entry using entries' duration & the current playtime
+ const currentEntry = 0;
+ const entry = data?.entries[currentEntry] ?? data?.entries[0];
const { apiUrl, authToken } = useToken();
const [playMode] = useLocalSetting<"direct" | "hls">("playMode", "direct");
@@ -44,15 +28,15 @@ export const Player = () => {
headers: {
Authorization: `Bearer ${authToken}`,
},
- externalSubtitles: info?.subtitles
- .filter((x) => x.link)
- .map((x) => ({
- uri: x.link!,
- // TODO: translate this `Unknown`
- label: x.title ?? "Unknown",
- language: x.language ?? "und",
- type: x.codec,
- })),
+ // externalSubtitles: info?.subtitles
+ // .filter((x) => x.link)
+ // .map((x) => ({
+ // uri: x.link!,
+ // // TODO: translate this `Unknown`
+ // label: x.title ?? "Unknown",
+ // language: x.language ?? "und",
+ // type: x.codec,
+ // })),
},
(p) => {
p.playWhenInactive = true;
@@ -111,9 +95,9 @@ export const Player = () => {
}}
>
{
pictureInPicture
autoEnterPictureInPicture
resizeMode={"contain"}
- controls
style={StyleSheet.absoluteFillObject}
/>
x)
+ .join(" - ")
+ : undefined
+ }
+ chapters={info?.chapters ?? []}
+ previous={data?.previous?.video}
+ next={data?.next?.video}
/>
@@ -147,7 +140,7 @@ export const Player = () => {
Player.query = (slug: string): QueryIdentifier => ({
path: ["api", "videos", slug],
params: {
- fields: ["next", "previous", "show"],
+ with: ["next", "previous", "show"],
},
parser: FullVideo,
});