This commit is contained in:
afkarxyz
2025-11-24 14:52:47 +07:00
parent 73d8205f6f
commit 6ee3c2f653
22 changed files with 865 additions and 253 deletions
+3
View File
@@ -20,6 +20,7 @@ interface AlbumInfoProps {
sortBy: string;
selectedTracks: string[];
downloadedTracks: Set<string>;
failedTracks: Set<string>;
downloadingTrack: string | null;
isDownloading: boolean;
bulkDownloadType: "all" | "selected" | null;
@@ -46,6 +47,7 @@ export function AlbumInfo({
sortBy,
selectedTracks,
downloadedTracks,
failedTracks,
downloadingTrack,
isDownloading,
bulkDownloadType,
@@ -145,6 +147,7 @@ export function AlbumInfo({
sortBy={sortBy}
selectedTracks={selectedTracks}
downloadedTracks={downloadedTracks}
failedTracks={failedTracks}
downloadingTrack={downloadingTrack}
isDownloading={isDownloading}
currentPage={currentPage}
+3
View File
@@ -27,6 +27,7 @@ interface ArtistInfoProps {
sortBy: string;
selectedTracks: string[];
downloadedTracks: Set<string>;
failedTracks: Set<string>;
downloadingTrack: string | null;
isDownloading: boolean;
bulkDownloadType: "all" | "selected" | null;
@@ -55,6 +56,7 @@ export function ArtistInfo({
sortBy,
selectedTracks,
downloadedTracks,
failedTracks,
downloadingTrack,
isDownloading,
bulkDownloadType,
@@ -197,6 +199,7 @@ export function ArtistInfo({
sortBy={sortBy}
selectedTracks={selectedTracks}
downloadedTracks={downloadedTracks}
failedTracks={failedTracks}
downloadingTrack={downloadingTrack}
isDownloading={isDownloading}
currentPage={currentPage}
+3
View File
@@ -26,6 +26,7 @@ interface PlaylistInfoProps {
sortBy: string;
selectedTracks: string[];
downloadedTracks: Set<string>;
failedTracks: Set<string>;
downloadingTrack: string | null;
isDownloading: boolean;
bulkDownloadType: "all" | "selected" | null;
@@ -52,6 +53,7 @@ export function PlaylistInfo({
sortBy,
selectedTracks,
downloadedTracks,
failedTracks,
downloadingTrack,
isDownloading,
bulkDownloadType,
@@ -151,6 +153,7 @@ export function PlaylistInfo({
sortBy={sortBy}
selectedTracks={selectedTracks}
downloadedTracks={downloadedTracks}
failedTracks={failedTracks}
downloadingTrack={downloadingTrack}
isDownloading={isDownloading}
currentPage={currentPage}
+46 -51
View File
@@ -1,6 +1,5 @@
import { Button } from "@/components/ui/button";
import { InputWithContext } from "@/components/ui/input-with-context";
import { Card, CardContent } from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { Search, Info, XCircle } from "lucide-react";
import { Spinner } from "@/components/ui/spinner";
@@ -19,56 +18,52 @@ interface SearchBarProps {
export function SearchBar({ url, loading, onUrlChange, onFetch }: SearchBarProps) {
return (
<Card>
<CardContent className="px-6 py-6 space-y-4">
<div className="space-y-2">
<div className="flex items-center gap-2">
<Label htmlFor="spotify-url">Spotify URL</Label>
<Tooltip>
<TooltipTrigger asChild>
<Info className="h-4 w-4 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent side="right">
<p>Supports track, album, playlist, and artist URLs</p>
</TooltipContent>
</Tooltip>
</div>
<div className="flex gap-2">
<div className="relative flex-1">
<InputWithContext
id="spotify-url"
placeholder="https://open.spotify.com/..."
value={url}
onChange={(e) => onUrlChange(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && onFetch()}
className="pr-8"
/>
{url && (
<button
type="button"
className="absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors"
onClick={() => onUrlChange("")}
>
<XCircle className="h-4 w-4" />
</button>
)}
</div>
<Button onClick={onFetch} disabled={loading}>
{loading ? (
<>
<Spinner />
Fetching...
</>
) : (
<>
<Search className="h-4 w-4" />
Fetch
</>
)}
</Button>
</div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<Label htmlFor="spotify-url">Spotify URL</Label>
<Tooltip>
<TooltipTrigger asChild>
<Info className="h-4 w-4 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent side="right">
<p>Supports track, album, playlist, and artist URLs</p>
</TooltipContent>
</Tooltip>
</div>
<div className="flex gap-2">
<div className="relative flex-1">
<InputWithContext
id="spotify-url"
placeholder="https://open.spotify.com/..."
value={url}
onChange={(e) => onUrlChange(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && onFetch()}
className="pr-8"
/>
{url && (
<button
type="button"
className="absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors"
onClick={() => onUrlChange("")}
>
<XCircle className="h-4 w-4" />
</button>
)}
</div>
</CardContent>
</Card>
<Button onClick={onFetch} disabled={loading}>
{loading ? (
<>
<Spinner />
Fetching...
</>
) : (
<>
<Search className="h-4 w-4" />
Fetch
</>
)}
</Button>
</div>
</div>
);
}
+1 -1
View File
@@ -27,7 +27,7 @@ export function TitleBar() {
/>
{/* Window control buttons */}
<div className="absolute top-4 left-4 z-50 flex gap-2">
<div className="fixed top-4 left-4 z-50 flex gap-2">
<button
onClick={handleClose}
onMouseEnter={() => setHoveredButton("close")}
+12 -2
View File
@@ -1,6 +1,6 @@
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Download, FolderOpen } from "lucide-react";
import { Download, FolderOpen, CheckCircle, XCircle } from "lucide-react";
import { Spinner } from "@/components/ui/spinner";
import type { TrackMetadata } from "@/types/api";
@@ -9,6 +9,7 @@ interface TrackInfoProps {
isDownloading: boolean;
downloadingTrack: string | null;
isDownloaded: boolean;
isFailed: boolean;
onDownload: (isrc: string, name: string, artists: string, albumName?: string, spotifyId?: string) => void;
onOpenFolder: () => void;
}
@@ -18,6 +19,7 @@ export function TrackInfo({
isDownloading,
downloadingTrack,
isDownloaded,
isFailed,
onDownload,
onOpenFolder,
}: TrackInfoProps) {
@@ -34,7 +36,15 @@ export function TrackInfo({
)}
<div className="flex-1 space-y-4 min-w-0">
<div className="space-y-1">
<h1 className="text-3xl font-bold wrap-break-word">{track.name}</h1>
<div className="flex items-center gap-3">
<h1 className="text-3xl font-bold wrap-break-word">{track.name}</h1>
{isDownloaded && (
<CheckCircle className="h-6 w-6 text-green-500 shrink-0" />
)}
{isFailed && (
<XCircle className="h-6 w-6 text-red-500 shrink-0" />
)}
</div>
<p className="text-lg text-muted-foreground">{track.artists}</p>
</div>
<div className="grid grid-cols-2 gap-3 text-sm">
+6 -1
View File
@@ -1,6 +1,6 @@
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Download, CheckCircle } from "lucide-react";
import { Download, CheckCircle, XCircle } from "lucide-react";
import { Spinner } from "@/components/ui/spinner";
import {
Pagination,
@@ -18,6 +18,7 @@ interface TrackListProps {
sortBy: string;
selectedTracks: string[];
downloadedTracks: Set<string>;
failedTracks: Set<string>;
downloadingTrack: string | null;
isDownloading: boolean;
currentPage: number;
@@ -36,6 +37,7 @@ export function TrackList({
sortBy,
selectedTracks,
downloadedTracks,
failedTracks,
downloadingTrack,
isDownloading,
currentPage,
@@ -165,6 +167,9 @@ export function TrackList({
{downloadedTracks.has(track.isrc) && (
<CheckCircle className="h-4 w-4 text-green-500 shrink-0" />
)}
{failedTracks.has(track.isrc) && (
<XCircle className="h-4 w-4 text-red-500 shrink-0" />
)}
</div>
<span className="text-sm text-muted-foreground">
{track.artists}