18 changed files with 1298 additions and 1442 deletions
@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
|
||||
window.__CONFIG__ = { |
||||
// url must NOT end with a slash
|
||||
VITE_CORS_PROXY_URL: "", |
||||
VITE_CORS_PROXY_URL: "https://rough.isra.workers.dev", |
||||
VITE_TMDB_API_KEY: "b030404650f279792a8d3287232358e3", |
||||
VITE_OMDB_API_KEY: "aa0937c0", |
||||
}; |
||||
|
||||
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
import { ChangeEventHandler, useEffect, useRef } from "react"; |
||||
|
||||
export type SliderProps = { |
||||
label: string; |
||||
min: number; |
||||
max: number; |
||||
step: number; |
||||
value?: number; |
||||
valueDisplay?: string; |
||||
onChange: ChangeEventHandler<HTMLInputElement>; |
||||
}; |
||||
|
||||
export function Slider(props: SliderProps) { |
||||
const ref = useRef<HTMLInputElement>(null); |
||||
useEffect(() => { |
||||
const e = ref.current as HTMLInputElement; |
||||
e.style.setProperty("--value", e.value); |
||||
e.style.setProperty("--min", e.min === "" ? "0" : e.min); |
||||
e.style.setProperty("--max", e.max === "" ? "100" : e.max); |
||||
e.addEventListener("input", () => e.style.setProperty("--value", e.value)); |
||||
}, [ref]); |
||||
|
||||
return ( |
||||
<div className="mb-6 flex flex-row gap-4"> |
||||
<div className="flex w-full flex-col gap-2"> |
||||
<label className="font-bold">{props.label}</label> |
||||
<input |
||||
type="range" |
||||
ref={ref} |
||||
className="styled-slider slider-progress" |
||||
onChange={props.onChange} |
||||
value={props.value} |
||||
max={props.max} |
||||
min={props.min} |
||||
step={props.step} |
||||
/> |
||||
</div> |
||||
<div className="mt-1 aspect-[2/1] h-8 rounded-sm bg-[#1C161B] pt-1"> |
||||
<div className="text-center font-bold text-white"> |
||||
{props.valueDisplay ?? props.value} |
||||
</div> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
||||
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
import { Icons } from "@/components/Icon"; |
||||
import { useTranslation } from "react-i18next"; |
||||
import { PopoutListAction } from "../../popouts/PopoutUtils"; |
||||
|
||||
interface Props { |
||||
onClick: () => any; |
||||
} |
||||
|
||||
export function PlaybackSpeedSelectionAction(props: Props) { |
||||
const { t } = useTranslation(); |
||||
|
||||
return ( |
||||
<PopoutListAction icon={Icons.TACHOMETER} onClick={props.onClick}> |
||||
{t("videoPlayer.buttons.playbackSpeed")} |
||||
</PopoutListAction> |
||||
); |
||||
} |
||||
@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
|
||||
import { Icon, Icons } from "@/components/Icon"; |
||||
import { FloatingCardView } from "@/components/popout/FloatingCard"; |
||||
import { FloatingView } from "@/components/popout/FloatingView"; |
||||
import { useFloatingRouter } from "@/hooks/useFloatingRouter"; |
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks"; |
||||
import { useControls } from "@/video/state/logic/controls"; |
||||
import { useTranslation } from "react-i18next"; |
||||
import { useMediaPlaying } from "@/video/state/logic/mediaplaying"; |
||||
import { Slider } from "@/components/Slider"; |
||||
import { PopoutListEntry, PopoutSection } from "./PopoutUtils"; |
||||
|
||||
const speedSelectionOptions = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2]; |
||||
|
||||
export function PlaybackSpeedPopout(props: { |
||||
router: ReturnType<typeof useFloatingRouter>; |
||||
prefix: string; |
||||
}) { |
||||
const { t } = useTranslation(); |
||||
|
||||
const descriptor = useVideoPlayerDescriptor(); |
||||
const controls = useControls(descriptor); |
||||
const mediaPlaying = useMediaPlaying(descriptor); |
||||
|
||||
return ( |
||||
<FloatingView |
||||
{...props.router.pageProps(props.prefix)} |
||||
width={320} |
||||
height={500} |
||||
> |
||||
<FloatingCardView.Header |
||||
title={t("videoPlayer.popouts.playbackSpeed")} |
||||
description={t("videoPlayer.popouts.descriptions.playbackSpeed")} |
||||
goBack={() => props.router.navigate("/")} |
||||
/> |
||||
<FloatingCardView.Content noSection> |
||||
<PopoutSection> |
||||
{speedSelectionOptions.map((speed) => ( |
||||
<PopoutListEntry |
||||
key={speed} |
||||
active={mediaPlaying.playbackSpeed === speed} |
||||
onClick={() => { |
||||
controls.setPlaybackSpeed(speed); |
||||
controls.closePopout(); |
||||
}} |
||||
> |
||||
{speed}x |
||||
</PopoutListEntry> |
||||
))} |
||||
</PopoutSection> |
||||
|
||||
<p className="sticky top-0 z-10 flex items-center space-x-1 bg-ash-300 px-5 py-3 text-xs font-bold uppercase"> |
||||
<Icon className="text-base" icon={Icons.TACHOMETER} /> |
||||
<span>{t("videoPlayer.popouts.customPlaybackSpeed")}</span> |
||||
</p> |
||||
|
||||
<PopoutSection className="pt-0"> |
||||
<div> |
||||
<Slider |
||||
label={t("videoPlayer.popouts.speed")} |
||||
min={0.1} |
||||
max={10} |
||||
step={0.1} |
||||
value={mediaPlaying.playbackSpeed} |
||||
valueDisplay={`${mediaPlaying.playbackSpeed}x`} |
||||
onChange={(e: { target: { valueAsNumber: number } }) => |
||||
controls.setPlaybackSpeed(e.target.valueAsNumber) |
||||
} |
||||
/> |
||||
</div> |
||||
</PopoutSection> |
||||
</FloatingCardView.Content> |
||||
</FloatingView> |
||||
); |
||||
} |
||||
Loading…
Reference in new issue