Skip to content

Commit

Permalink
Added more player
Browse files Browse the repository at this point in the history
  • Loading branch information
matvp91 committed Dec 19, 2024
1 parent bb23744 commit b18b0b1
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 116 deletions.
75 changes: 33 additions & 42 deletions packages/app/src/components/PlayerControls.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Button, Select, SelectItem } from "@nextui-org/react";
import { Button } from "@nextui-org/react";
import cn from "clsx";
import { useRef } from "react";
import { Selection } from "./Selection";
import { usePlayerSelector } from "../context/PlayerContext";
import { useSeekbar } from "../hooks/useSeekbar";
import type { ReactNode, RefObject } from "react";
Expand All @@ -11,12 +12,11 @@ export function PlayerControls() {
return null;
}
return (
<div className="flex flex-col gap-2 overflow-hidden">
<div className="flex flex-col gap-4 overflow-hidden">
<div className="flex">
<PlayButton />
</div>
<Seekbar />
<CuePoints />
<Time />
<Tracks />
</div>
Expand Down Expand Up @@ -59,8 +59,8 @@ function Seekbar() {
>
{hms(seekbar.value)}
</Tooltip>
<div className="flex items-center rounded-lg overflow-hidden">
<div className="h-6 bg-gray-100 w-full" />
<div className="flex items-center rounded-lg overflow-hidden mb-2">
<div className="h-2 bg-gray-100 w-full" />
<div
className={cn(
"h-2 absolute left-0 right-0 bg-gray-200 origin-left opacity-0 transition-opacity",
Expand All @@ -77,6 +77,7 @@ function Seekbar() {
}}
/>
</div>
<CuePoints />
</div>
);
}
Expand Down Expand Up @@ -109,7 +110,7 @@ function Tooltip({
<div
ref={ref}
className={cn(
"pointer-events-none absolute h-6 -top-6 -translate-x-1/2 opacity-0 transition-opacity text-xs text-white bg-black px-1 flex items-center rounded-md",
"pointer-events-none absolute h-6 -top-8 -translate-x-1/2 opacity-0 transition-opacity text-xs text-white bg-black px-1 flex items-center rounded-md",
visible && "opacity-100",
)}
style={{ left: `${x * 100}%` }}
Expand Down Expand Up @@ -144,64 +145,54 @@ function CuePoints() {
}

function Time() {
const time = usePlayerSelector((player) => player.time);
const seekableStart = usePlayerSelector((player) => player.seekableStart);
const duration = usePlayerSelector((player) => player.duration);
const live = usePlayerSelector((player) => player.live);

return (
<div className="flex">
{hms(seekableStart)}
<div className="flex text-sm">
{hms(time)}
<div className="grow" />
{hms(duration)}
{live ? `${hms(seekableStart)} - ${hms(duration)}` : `${hms(duration)}`}
</div>
);
}

function Tracks() {
const audioTracks = usePlayerSelector((player) => player.audioTracks);
const setAudioTrack = usePlayerSelector((player) => player.setAudioTrack);
const audioKey = audioTracks.find((track) => track.active)?.id.toString();

const subtitleTracks = usePlayerSelector((player) => player.subtitleTracks);
const setSubtitleTrack = usePlayerSelector(
(player) => player.setSubtitleTrack,
);
const subtitleKey = subtitleTracks
.find((track) => track.active)
?.id.toString();

return (
<div className="flex gap-4">
<Select
label="Audio"
<Selection
items={audioTracks}
selectionMode="single"
selectedKeys={audioKey ? [audioKey] : []}
onChange={(event) => {
const id = Number.parseInt(event.target.value);
if (Number.isNaN(id)) {
return;
}
setAudioTrack(id);
}}
>
{(track) => <SelectItem key={track.id}>{track.label}</SelectItem>}
</Select>

<Select
label="Audio"
getActive={(item) => item.active}
getKey={(item) => item.id}
getLabel={(item) => item.label}
onChange={(item) => setAudioTrack(item.id)}
/>
<Selection
items={[
...subtitleTracks,
{
id: null,
label: "None",
active: !subtitleTracks.some((item) => item.active),
},
]}
getActive={(item) => item.active}
label="Subtitles"
items={subtitleTracks}
selectionMode="single"
selectedKeys={subtitleKey ? [subtitleKey] : []}
onChange={(event) => {
const id = Number.parseInt(event.target.value);
if (Number.isNaN(id)) {
return;
}
setSubtitleTrack(id);
}}
>
{(track) => <SelectItem key={track.id}>{track.label}</SelectItem>}
</Select>
getKey={(item) => item.id}
getLabel={(item) => item.label}
onChange={(item) => setSubtitleTrack(item.id)}
/>
</div>
);
}
Expand Down
50 changes: 50 additions & 0 deletions packages/app/src/components/Selection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Select, SelectItem } from "@nextui-org/react";

interface SelectionProps<T> {
items: T[];
label: string;
getKey(item: T): number | string | null;
getLabel(item: T): string;
getActive(item: T): boolean;
onChange(item: T): void;
}

export function Selection<T extends object>({
items,
label,
getKey,
getLabel,
getActive,
onChange,
}: SelectionProps<T>) {
const selectedKeys: string[] = [];
for (const item of items) {
if (getActive(item)) {
const key = getKey(item);
selectedKeys.push(String(key));
}
}

return (
<Select
label={label}
items={items}
selectionMode="single"
selectedKeys={selectedKeys}
onChange={(event) => {
const item = items.find((item) => {
const key = getKey(item);
return String(key) === event.target.value;
});
if (item) {
onChange(item);
}
}}
disabled={items.length < 2}
>
{(item) => (
<SelectItem key={String(getKey(item))}>{getLabel(item)}</SelectItem>
)}
</Select>
);
}
8 changes: 8 additions & 0 deletions packages/app/src/context/PlayerContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ export function PlayerProvider({ children }: PlayerProviderProps) {
);
}

export function WithPlayer({ children }: { children: ReactNode }) {
const { player } = usePlayer();
if (!player) {
return null;
}
return children;
}

export function usePlayer() {
const context = useContext(PlayerContext);
if (!context) {
Expand Down
111 changes: 37 additions & 74 deletions packages/app/src/routes/(dashboard)/_layout/player.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,9 @@ import { Player } from "../../../components/Player";
import { PlayerControls } from "../../../components/PlayerControls";
import { PlayerStats } from "../../../components/PlayerStats";
import { ScrollCard } from "../../../components/ScrollCard";
import {
PlayerProvider,
usePlayer,
usePlayerSelector,
} from "../../../context/PlayerContext";
import { PlayerProvider, WithPlayer } from "../../../context/PlayerContext";
import { useSwaggerSchema } from "../../../hooks/useSwaggerSchema";
import type { FormRef } from "../../../components/Form";
import type { RefObject } from "react";

export const Route = createFileRoute("/(dashboard)/_layout/player")({
component: RouteComponent,
Expand Down Expand Up @@ -69,7 +64,42 @@ function RouteComponent() {
<Player url={url} />
</div>
</div>
<HasPlayer url={url} setUrl={setUrl} formRef={formRef} />
<WithPlayer>
<Tabs
classNames={{
panel: "grow p-0",
}}
>
<Tab title="Config">
<ScrollCard>
<Form
ref={formRef}
submit="Play"
fields={{
url: {
label: "URL",
type: "string",
value: url,
},
}}
onSubmit={async (values) => {
setUrl(values.url);
}}
/>
</ScrollCard>
</Tab>
<Tab title="Stats">
<ScrollCard>
<PlayerStats />
</ScrollCard>
</Tab>
<Tab title="Controls">
<ScrollCard>
<PlayerControls />
</ScrollCard>
</Tab>
</Tabs>
</WithPlayer>
</div>
</PlayerProvider>
<Card className="w-[420px] py-4">
Expand All @@ -96,70 +126,3 @@ function RouteComponent() {
</div>
);
}

function HasPlayer({
url,
setUrl,
formRef,
}: {
url: string;
setUrl: (url: string) => void;
formRef: RefObject<FormRef>;
}) {
const { player } = usePlayer();

if (!player) {
return null;
}

return <PlayerTabs url={url} setUrl={setUrl} formRef={formRef} />;
}

function PlayerTabs({
url,
setUrl,
formRef,
}: {
url: string;
setUrl: (url: string) => void;
formRef: RefObject<FormRef>;
}) {
const ready = usePlayerSelector((player) => player.ready);

return (
<Tabs
classNames={{
panel: "grow p-0",
}}
>
<Tab title="Config">
<ScrollCard>
<Form
ref={formRef}
submit="Play"
fields={{
url: {
label: "URL",
type: "string",
value: url,
},
}}
onSubmit={async (values) => {
setUrl(values.url);
}}
/>
</ScrollCard>
</Tab>
<Tab title="Stats">
<ScrollCard>
<PlayerStats />
</ScrollCard>
</Tab>
<Tab title="Controls" isDisabled={!ready}>
<ScrollCard>
<PlayerControls />
</ScrollCard>
</Tab>
</Tabs>
);
}

0 comments on commit b18b0b1

Please sign in to comment.