Skip to content

Commit 6aea250

Browse files
committed
feat: add queue
Signed-off-by: rajput-hemant <[email protected]>
1 parent b176981 commit 6aea250

File tree

16 files changed

+321
-97
lines changed

16 files changed

+321
-97
lines changed

bun.lockb

0 Bytes
Binary file not shown.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"name": "infinitunes",
33
"version": "0.3.0",
44
"private": true,
5+
"type": "module",
56
"scripts": {
67
"dev": "next dev",
78
"build": "next build",

postcss.config.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
module.exports = {
1+
const config = {
22
plugins: {
33
tailwindcss: {},
44
autoprefixer: {},
55
},
66
};
7+
8+
export default config;

src/app/(root)/layout.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,15 @@ export default async function Layout({ children }: React.PropsWithChildren) {
1818
}
1919

2020
return (
21-
<>
21+
<React.Fragment>
2222
<Navbar />
23-
2423
<Sidebar user={user} userPlaylists={userPlaylists} />
25-
2624
<main className="p-2 pb-24 sm:p-4 sm:pb-24 lg:ml-[20%] lg:pb-10 xl:ml-[15%]">
2725
<SecondaryNavbar />
28-
2926
{children}
30-
3127
<SiteFooter />
3228
</main>
33-
3429
<Player user={user} playlists={userPlaylists} />
35-
</>
30+
</React.Fragment>
3631
);
3732
}

src/app/(root)/settings/_components/preference-settings.tsx

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ import {
2525
} from "@/hooks/use-store";
2626
import { cn } from "@/lib/utils";
2727

28-
const SONG_QUALITIES: StreamQuality[] = [
29-
"poor",
30-
"low",
31-
"medium",
32-
"high",
33-
"excellent",
28+
const SONG_QUALITIES: { quality: StreamQuality; bitrate: string }[] = [
29+
{ quality: "poor", bitrate: "12kbps" },
30+
{ quality: "low", bitrate: "48kbps" },
31+
{ quality: "medium", bitrate: "96kbps" },
32+
{ quality: "high", bitrate: "160kbps" },
33+
{ quality: "excellent", bitrate: "320kbps" },
3434
];
3535

3636
const IMAGE_QUALITIES: ImageQuality[] = ["low", "medium", "high"];
@@ -111,15 +111,24 @@ export function PreferenceSettings(props: PreferenceSettingsProps) {
111111
<DropdownMenuTrigger asChild>
112112
<Button
113113
variant="outline"
114-
className="group w-40 justify-between font-semibold capitalize"
114+
className="group w-44 justify-between font-semibold capitalize"
115115
>
116-
{streamQuality}
116+
<span>{streamQuality}</span>
117+
<span className="font-light">
118+
(
119+
{
120+
SONG_QUALITIES.find((q) => q.quality === streamQuality)
121+
?.bitrate
122+
}
123+
)
124+
</span>
125+
117126
<ChevronDown className="ml-2 size-4 transition-transform group-data-[state=open]:rotate-180" />
118127
</Button>
119128
</DropdownMenuTrigger>
120129

121-
<DropdownMenuContent className="w-40 *:cursor-pointer *:capitalize">
122-
{SONG_QUALITIES.map((quality) => (
130+
<DropdownMenuContent className="w-44 *:cursor-pointer *:capitalize">
131+
{SONG_QUALITIES.map(({ quality, bitrate }) => (
123132
<DropdownMenuItem
124133
key={quality}
125134
onClick={() => {
@@ -129,9 +138,13 @@ export function PreferenceSettings(props: PreferenceSettingsProps) {
129138
description: `Stream quality set to "${quality}".`,
130139
});
131140
}}
132-
className={cn(quality === streamQuality && "bg-accent/60")}
141+
className={cn(
142+
"justify-between",
143+
quality === downloadQuality && "bg-accent/60"
144+
)}
133145
>
134-
{quality}
146+
<span>{quality}</span>
147+
<span className="font-medium">{bitrate}</span>
135148
</DropdownMenuItem>
136149
))}
137150
</DropdownMenuContent>
@@ -152,15 +165,23 @@ export function PreferenceSettings(props: PreferenceSettingsProps) {
152165
<DropdownMenuTrigger asChild>
153166
<Button
154167
variant="outline"
155-
className="group w-40 justify-between font-semibold capitalize"
168+
className="group w-44 justify-between font-semibold capitalize"
156169
>
157-
{downloadQuality}
170+
<span>{streamQuality}</span>
171+
<span className="font-light">
172+
(
173+
{
174+
SONG_QUALITIES.find((q) => q.quality === downloadQuality)
175+
?.bitrate
176+
}
177+
)
178+
</span>
158179
<ChevronDown className="ml-2 size-4 transition-transform group-data-[state=open]:rotate-180" />
159180
</Button>
160181
</DropdownMenuTrigger>
161182

162-
<DropdownMenuContent className="w-40 *:cursor-pointer *:capitalize">
163-
{SONG_QUALITIES.map((quality) => (
183+
<DropdownMenuContent className="w-44 *:cursor-pointer *:capitalize">
184+
{SONG_QUALITIES.map(({ bitrate, quality }) => (
164185
<DropdownMenuItem
165186
key={quality}
166187
onClick={() => {
@@ -170,9 +191,13 @@ export function PreferenceSettings(props: PreferenceSettingsProps) {
170191
description: `Download quality has been set to "${quality}".`,
171192
});
172193
}}
173-
className={cn(quality === downloadQuality && "bg-accent/60")}
194+
className={cn(
195+
"justify-between",
196+
quality === downloadQuality && "bg-accent/60"
197+
)}
174198
>
175-
{quality}
199+
<span>{quality}</span>
200+
<span className="font-medium">{bitrate}</span>
176201
</DropdownMenuItem>
177202
))}
178203
</DropdownMenuContent>
@@ -193,14 +218,14 @@ export function PreferenceSettings(props: PreferenceSettingsProps) {
193218
<DropdownMenuTrigger asChild>
194219
<Button
195220
variant="outline"
196-
className="group w-40 justify-between font-semibold capitalize"
221+
className="group w-44 justify-between font-semibold capitalize"
197222
>
198223
{imageQuality}
199224
<ChevronDown className="ml-2 size-4 transition-transform group-data-[state=open]:rotate-180" />
200225
</Button>
201226
</DropdownMenuTrigger>
202227

203-
<DropdownMenuContent className="w-40 *:cursor-pointer *:capitalize">
228+
<DropdownMenuContent className="w-44 *:cursor-pointer *:capitalize">
204229
{IMAGE_QUALITIES.map((quality) => (
205230
<DropdownMenuItem
206231
key={quality}

src/components/details-header/more-button.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { toast } from "sonner";
1717
import type { LucideIcon } from "lucide-react";
1818
import type { User } from "next-auth";
1919
import type { MyPlaylist } from "@/lib/db/schema";
20-
import type { Quality, Song, Type } from "@/types";
20+
import type { Quality, Queue, Song, Type } from "@/types";
2121

2222
import {
2323
Drawer,
@@ -74,7 +74,7 @@ export function MoreButton(props: MoreButtonProps) {
7474
const [, setQueue] = useQueue();
7575

7676
function addToQueue() {
77-
const songsPayload = songs.map((song) => ({
77+
const songsPayload: Queue[] = songs.map((song) => ({
7878
id: song.id,
7979
name: song.name,
8080
subtitle: song.subtitle,
@@ -83,6 +83,7 @@ export function MoreButton(props: MoreButtonProps) {
8383
image: song.image,
8484
download_url: song.download_url,
8585
artists: song.artist_map.artists,
86+
duration: song.duration,
8687
}));
8788

8889
setQueue((prev) => [...prev, ...songsPayload]);

src/components/play-button.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export function PlayButton(props: PlayButtonProps) {
113113
image,
114114
download_url,
115115
artist_map: { artists },
116+
duration,
116117
}) => ({
117118
id,
118119
name,
@@ -122,6 +123,7 @@ export function PlayButton(props: PlayButtonProps) {
122123
image,
123124
download_url,
124125
artists,
126+
duration,
125127
})
126128
);
127129

src/components/player.tsx

Lines changed: 83 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
"use client";
22

33
import React from "react";
4+
import Link from "next/link";
45
import {
56
Loader2,
67
MoreVertical,
8+
MoveUpRight,
79
Pause,
810
Repeat,
911
Repeat1,
@@ -26,10 +28,18 @@ import {
2628
useQueue,
2729
useStreamQuality,
2830
} from "@/hooks/use-store";
29-
import { cn, formatDuration, getDownloadLink, getImageSrc } from "@/lib/utils";
31+
import {
32+
cn,
33+
formatDuration,
34+
getDownloadLink,
35+
getHref,
36+
getImageSrc,
37+
} from "@/lib/utils";
3038
import { Icons } from "./icons";
3139
import { ImageWithFallback } from "./image-with-fallback";
40+
import { Queue } from "./queue";
3241
import { TileMoreButton } from "./song-list/more-button";
42+
import { buttonVariants } from "./ui/button";
3343
import { Skeleton } from "./ui/skeleton";
3444
import { Slider, SliderRange, SliderThumb, SliderTrack } from "./ui/slider";
3545
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
@@ -258,9 +268,16 @@ export function Player({ user, playlists }: PlayerProps) {
258268
</div>
259269

260270
<div className="flex flex-col justify-center">
261-
<p className="line-clamp-1 font-heading text-sm text-primary drop-shadow">
271+
<Link
272+
href={getHref(
273+
queue[currentIndex].url,
274+
queue[currentIndex].type === "song" ? "song" : "episode"
275+
)}
276+
className="group line-clamp-1 font-heading text-sm text-primary drop-shadow"
277+
>
262278
{queue[currentIndex].name}
263-
</p>
279+
<MoveUpRight className="invisible mb-1 ml-1 inline-flex size-3 group-hover:visible" />
280+
</Link>
264281

265282
<p className="line-clamp-1 text-xs text-muted-foreground">
266283
{queue[currentIndex].subtitle}
@@ -364,62 +381,74 @@ export function Player({ user, playlists }: PlayerProps) {
364381
</div>
365382

366383
<div className="hidden w-1/3 items-center justify-end gap-4 lg:flex">
367-
<p className="text-sm text-muted-foreground">
384+
<p className="shrink-0 text-sm text-muted-foreground">
368385
{formatDuration(pos, pos > 3600 ? "hh:mm:ss" : "mm:ss")}
369386
{" / "}
370387
{formatDuration(duration, duration > 3600 ? "hh:mm:ss" : "mm:ss")}
371388
</p>
372389

373-
<button
374-
aria-label={muted ? "Unmute" : "Mute"}
375-
disabled={!isReady || muted}
376-
onClick={() => mute(!muted)}
377-
className="disabled:text-muted-foreground"
378-
>
379-
{muted ?
380-
<VolumeX />
381-
: volume < 0.33 ?
382-
<Volume />
383-
: volume < 0.66 ?
384-
<Volume1 />
385-
: <Volume2 strokeWidth={2} />}
386-
</button>
387-
388-
<Slider
389-
aria-label="Volume"
390-
disabled={!isReady || muted}
391-
value={[volume * 100]}
392-
defaultValue={[75]}
393-
onValueChange={([volume]) => {
394-
setVolume(volume / 100);
395-
}}
396-
className="w-44"
397-
>
398-
<SliderTrack className="h-1 cursor-pointer">
399-
<SliderRange className={cn((!isReady || muted) && "bg-accent")} />
400-
</SliderTrack>
401-
402-
<SliderThumb
403-
aria-label="Volume slider"
404-
className={cn(
405-
"size-4 cursor-pointer",
406-
(!isReady || muted) && "bg-accent"
407-
)}
408-
/>
409-
</Slider>
410-
411-
<span className="w-8 text-sm font-medium">
412-
{(volume * 100).toFixed()}%
413-
</span>
414-
415-
{queue.length > 0 ?
416-
<TileMoreButton
417-
item={queue[currentIndex]}
418-
showAlbum
419-
user={user}
420-
playlists={playlists}
421-
/>
422-
: <MoreVertical />}
390+
<div className="hidden items-center gap-4 xl:flex">
391+
<button
392+
aria-label={muted ? "Unmute" : "Mute"}
393+
disabled={!isReady || muted}
394+
onClick={() => mute(!muted)}
395+
className="disabled:text-muted-foreground"
396+
>
397+
{muted ?
398+
<VolumeX />
399+
: volume < 0.33 ?
400+
<Volume />
401+
: volume < 0.66 ?
402+
<Volume1 />
403+
: <Volume2 strokeWidth={2} />}
404+
</button>
405+
406+
<Slider
407+
aria-label="Volume"
408+
disabled={!isReady || muted}
409+
value={[volume * 100]}
410+
defaultValue={[75]}
411+
onValueChange={([volume]) => {
412+
setVolume(volume / 100);
413+
}}
414+
className="w-44"
415+
>
416+
<SliderTrack className="h-1 cursor-pointer">
417+
<SliderRange
418+
className={cn((!isReady || muted) && "bg-accent")}
419+
/>
420+
</SliderTrack>
421+
422+
<SliderThumb
423+
aria-label="Volume slider"
424+
className={cn(
425+
"size-4 cursor-pointer",
426+
(!isReady || muted) && "bg-accent"
427+
)}
428+
/>
429+
</Slider>
430+
431+
<span className="w-8 text-sm font-medium">
432+
{(volume * 100).toFixed()}%
433+
</span>
434+
</div>
435+
436+
<div className="flex">
437+
<Queue />
438+
439+
{queue.length > 0 ?
440+
<TileMoreButton
441+
item={queue[currentIndex]}
442+
showAlbum
443+
user={user}
444+
playlists={playlists}
445+
className={buttonVariants({
446+
size: "icon",
447+
variant: "ghost",
448+
})}
449+
/>
450+
: <MoreVertical />}
451+
</div>
423452
</div>
424453
</div>
425454
</div>

0 commit comments

Comments
 (0)