diff --git a/src/api/context/VideoPlayerContext.tsx b/src/api/context/VideoPlayerContext.tsx
index 3b72020..983104e 100644
--- a/src/api/context/VideoPlayerContext.tsx
+++ b/src/api/context/VideoPlayerContext.tsx
@@ -33,6 +33,7 @@ export interface VideoPlayerAPI {
setMuted: (m: boolean) => void;
seekTo: (ts: number) => void;
+ seekToChapterOffset: (offset: number) => void;
};
// @ts-ignore
diff --git a/src/api/context/VideoPlayerProvider.tsx b/src/api/context/VideoPlayerProvider.tsx
index ef3d60e..24c7a77 100644
--- a/src/api/context/VideoPlayerProvider.tsx
+++ b/src/api/context/VideoPlayerProvider.tsx
@@ -52,6 +52,10 @@ export const VideoPlayerProvider = ({
setAvailableFormats([]);
setActiveFormat(null);
setVideoInfo(null);
+ setActiveChapters({
+ chapters: [],
+ type: "video",
+ });
if(!videoID) return;
try {
@@ -81,7 +85,11 @@ export const VideoPlayerProvider = ({
if(!videoInfo) return;
setAvailableFormats(videoInfo.formats);
- setActiveFormat(videoInfo.formats[0]);
+ setActiveFormat(videoInfo.formats
+ .filter(f => f.type == "basic")
+ .filter(f => currentInstance.type != "invidious" || f.isProxied)
+ .findLast(x=>x)
+ );
}, [videoInfo]);
useEffect(() => {
@@ -113,6 +121,27 @@ export const VideoPlayerProvider = ({
setPlayState("playing");
});
+ const seekTo = (ts: number) => {
+ videoElement.currentTime = clamp(0, ts, videoElement.duration);
+ };
+
+ const seekToChapterOffset = (offset: number = 0) => {
+ let currentChapterIndex = activeChapters.chapters.findIndex(c => c.time > videoElement.currentTime) - 1;
+ let currentChapter = activeChapters.chapters[currentChapterIndex];
+ if(!currentChapter) {
+ if(offset == 1) videoElement.fastSeek(videoElement.duration);
+ return;
+ }
+ let chapterProgress = videoElement.currentTime - currentChapter.time;
+ let targetIndex = currentChapterIndex + offset;
+ if(offset == -1) {
+ if(chapterProgress > 3) targetIndex++;
+ }
+ let target = activeChapters.chapters[targetIndex];
+
+ videoElement.fastSeek(clamp(0, target?.time || 0, videoElement.duration));
+ }
+
return (
{
- videoElement.currentTime = clamp(0, ts, videoElement.duration);
- },
+ seekTo,
+ seekToChapterOffset,
}}>
{children}
diff --git a/src/components/cards/FormatCard.tsx b/src/components/cards/FormatCard.tsx
new file mode 100644
index 0000000..3aac717
--- /dev/null
+++ b/src/components/cards/FormatCard.tsx
@@ -0,0 +1,45 @@
+import { Code, Grid, Group, Paper, Stack, Text } from "@mantine/core";
+import { VideoFormat } from "../../api/types/format";
+import { IconCheck } from "@tabler/icons-react";
+
+export const FormatCard = ({
+ format,
+ isSelected,
+ onClick,
+}: {
+ format: VideoFormat,
+ isSelected?: boolean,
+ onClick?: () => void,
+}) => {
+ return (
+
+
+
+
+ {isSelected && (
+
+ )}
+
+
+
+
+
+
+ {format.id}
+
+
+ {format.itag}
+
+
+
+ {format.mimeType}
+
+
+
+
+
+ );
+};
+
diff --git a/src/components/options/OptionsContext.tsx b/src/components/options/OptionsContext.tsx
index bd3e64b..6d9a200 100644
--- a/src/components/options/OptionsContext.tsx
+++ b/src/components/options/OptionsContext.tsx
@@ -1,8 +1,9 @@
import { useDisclosure, useHotkeys } from "@mantine/hooks";
-import { createContext, useState } from "react";
+import { createContext, useCallback, useState } from "react";
import { useSoundEffect } from "../../hooks/useSoundEffect";
-import { Drawer, ScrollArea } from "@mantine/core";
+import { Button, Drawer, Group, ScrollArea, Text } from "@mantine/core";
import { OptionsRouter } from "./OptionsRouter";
+import { IconArrowLeft } from "@tabler/icons-react";
export type OptionsView = "main" | "instanceSelect" | "openWith" | "formatSelect";
@@ -17,11 +18,11 @@ export interface OptionsContextAPI {
export const OptionsContext = createContext({
opened: false,
- open: () => {},
- close: () => {},
- toggle: () => {},
+ open: () => { },
+ close: () => { },
+ toggle: () => { },
view: "main",
- setView: () => {},
+ setView: () => { },
});
export const OptionsProvider = ({ children }: React.PropsWithChildren) => {
@@ -30,24 +31,24 @@ export const OptionsProvider = ({ children }: React.PropsWithChildren) => {
const openOptionsSfx = useSoundEffect(["openSettings"]);
const closeOptionsSfx = useSoundEffect(["closeSettings"]);
- const open = () => {
- if(!opened) openOptionsSfx();
+ const open = useCallback(() => {
+ if (!opened) openOptionsSfx();
handlers.open();
- };
+ }, [opened]);
- const close = () => {
- if(opened) closeOptionsSfx();
+ const close = useCallback(() => {
+ if (opened) closeOptionsSfx();
handlers.close();
setView("main");
- };
+ }, [opened]);
- const toggle = () => {
- if(opened) {
+ const toggle = useCallback(() => {
+ if (opened) {
close();
} else {
open();
}
- };
+ }, [opened]);
useHotkeys([
["o", () => toggle()],
@@ -56,7 +57,7 @@ export const OptionsProvider = ({ children }: React.PropsWithChildren) => {
useHotkeys([
["Ctrl + o", () => toggle()],
], [], true);
-
+
return (
{
>
close()}
position="right"
- title="Options"
scrollAreaComponent={ScrollArea.Autosize}
+ styles={{
+ title: {
+ width: "100%",
+ }
+ }}
+ title={(
+
+ {view !== "main" && (
+ }
+ onClick={() => setView("main")}
+ fullWidth
+ size="compact-md"
+ >
+ Back
+
+ )}
+
+ {({
+ main: "Options",
+ formatSelect: "Select Format",
+ instanceSelect: "Select Instance",
+ openWith: "Open with...",
+ } as Record)[view]}
+
+
+ )}
>
diff --git a/src/components/options/OptionsRouter.tsx b/src/components/options/OptionsRouter.tsx
index 0e9e02d..cf2a261 100644
--- a/src/components/options/OptionsRouter.tsx
+++ b/src/components/options/OptionsRouter.tsx
@@ -4,6 +4,7 @@ import { OptionsContext } from "./OptionsContext";
import { OptionsMainView } from "./views/MainView";
import { OptionsInstanceView } from "./views/InstanceView";
import { OptionsOpenWithView } from "./views/OpenWith";
+import { OptionsFormatView } from "./views/FormatView";
export const OptionsRouter = () => {
const { view } = useContext(OptionsContext);
@@ -13,6 +14,7 @@ export const OptionsRouter = () => {
{view == "main" && }
{view == "instanceSelect" && }
{view == "openWith" && }
+ {view == "formatSelect" && }
);
};
diff --git a/src/components/options/comps/FormatSelect.tsx b/src/components/options/comps/FormatSelect.tsx
index 506f7bd..8a772c5 100644
--- a/src/components/options/comps/FormatSelect.tsx
+++ b/src/components/options/comps/FormatSelect.tsx
@@ -1,37 +1,32 @@
-import { Group, Select, Stack } from "@mantine/core";
+import { Grid, Select, Stack, Text, UnstyledButton } from "@mantine/core";
import { useContext } from "react";
import { VideoPlayerContext } from "../../../api/context/VideoPlayerContext";
-import { IconCheck } from "@tabler/icons-react";
+import { FormatCard } from "../../cards/FormatCard";
+import { OptionsContext } from "../OptionsContext";
+import { IconArrowRight } from "@tabler/icons-react";
export const FormatSelect = () => {
- const {
- availableFormats,
- activeFormat,
- setFormat,
- } = useContext(VideoPlayerContext);
+ const { setView } = useContext(OptionsContext);
+ const { activeFormat } = useContext(VideoPlayerContext);
return (
-