.refine artist fetch all tracks
This commit is contained in:
@@ -21,6 +21,7 @@ interface ArtistInfoProps {
|
||||
header?: string;
|
||||
gallery?: string[];
|
||||
followers: number;
|
||||
total_albums?: number;
|
||||
genres: string[];
|
||||
biography?: string;
|
||||
verified?: boolean;
|
||||
@@ -99,6 +100,7 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort
|
||||
const [downloadingGalleryIndex, setDownloadingGalleryIndex] = useState<number | null>(null);
|
||||
const [downloadingAllGallery, setDownloadingAllGallery] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState<"albums" | "tracks" | "gallery">("albums");
|
||||
const displayedAlbumCount = artistInfo.total_albums || albumList.length;
|
||||
const filteredAlbumGroups = useMemo(() => {
|
||||
const albumTypeMap = new Map(albumList.map(a => [a.name, a.album_type]));
|
||||
const albumGroups = trackList.reduce((acc, track) => {
|
||||
@@ -330,9 +332,9 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort
|
||||
</>)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-sm flex-wrap text-white/90">
|
||||
<span>{albumList.length} {albumList.length === 1 ? "album" : "albums"}</span>
|
||||
<span>{displayedAlbumCount.toLocaleString()} {displayedAlbumCount === 1 ? "album" : "albums"}</span>
|
||||
<span>•</span>
|
||||
<span>{trackList.length} {trackList.length === 1 ? "track" : "tracks"}</span>
|
||||
<span>{trackList.length.toLocaleString()} {trackList.length === 1 ? "track" : "tracks"}</span>
|
||||
{artistInfo.genres.length > 0 && (<>
|
||||
<span>•</span>
|
||||
<span>{artistInfo.genres.join(", ")}</span>
|
||||
@@ -383,9 +385,9 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort
|
||||
</>)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-sm flex-wrap">
|
||||
<span>{albumList.length} {albumList.length === 1 ? "album" : "albums"}</span>
|
||||
<span>{displayedAlbumCount.toLocaleString()} {displayedAlbumCount === 1 ? "album" : "albums"}</span>
|
||||
<span>•</span>
|
||||
<span>{trackList.length} {trackList.length === 1 ? "track" : "tracks"}</span>
|
||||
<span>{trackList.length.toLocaleString()} {trackList.length === 1 ? "track" : "tracks"}</span>
|
||||
{artistInfo.genres.length > 0 && (<>
|
||||
<span>•</span>
|
||||
<span>{artistInfo.genres.join(", ")}</span>
|
||||
@@ -412,7 +414,7 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort
|
||||
|
||||
{activeTab === "gallery" && hasGallery && (<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-2xl font-bold">Gallery ({artistInfo.gallery!.length})</h3>
|
||||
<h3 className="text-2xl font-bold">Gallery ({artistInfo.gallery!.length.toLocaleString()})</h3>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button onClick={handleDownloadAllGallery} size="sm" variant="outline" disabled={downloadingAllGallery}>
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { getSettings } from "@/lib/settings";
|
||||
import { fetchSpotifyMetadata } from "@/lib/api";
|
||||
import { toastWithSound as toast } from "@/lib/toast-with-sound";
|
||||
import { logger } from "@/lib/logger";
|
||||
import { AddFetchHistory } from "../../wailsjs/go/main/App";
|
||||
import { EventsOff, EventsOn } from "../../wailsjs/runtime/runtime";
|
||||
import type { SpotifyMetadataResponse } from "@/types/api";
|
||||
export function useMetadata() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [metadata, setMetadata] = useState<SpotifyMetadataResponse | null>(null);
|
||||
const loadingToastId = useRef<string | number | null>(null);
|
||||
const fetchedCount = useRef(0);
|
||||
const currentName = useRef("");
|
||||
const [showApiModal, setShowApiModal] = useState(false);
|
||||
const [showAlbumDialog, setShowAlbumDialog] = useState(false);
|
||||
const [selectedAlbum, setSelectedAlbum] = useState<{
|
||||
@@ -16,6 +20,73 @@ export function useMetadata() {
|
||||
external_urls: string;
|
||||
} | null>(null);
|
||||
const [pendingArtistName, setPendingArtistName] = useState<string | null>(null);
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
fetchedCount.current = 0;
|
||||
currentName.current = "";
|
||||
loadingToastId.current = toast.silentInfo("fetching metadata...", {
|
||||
duration: Infinity,
|
||||
description: "please wait while we retrieve the information"
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (loadingToastId.current) {
|
||||
toast.dismiss(loadingToastId.current);
|
||||
loadingToastId.current = null;
|
||||
}
|
||||
}, [loading]);
|
||||
useEffect(() => {
|
||||
const handler = (data: any) => {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(data)) {
|
||||
fetchedCount.current += data.length;
|
||||
if (loadingToastId.current && currentName.current) {
|
||||
toast.silentInfo(`fetching tracks for ${currentName.current.toLowerCase()}...`, {
|
||||
id: loadingToastId.current,
|
||||
description: `${fetchedCount.current.toLocaleString()} tracks fetched`
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
const baseInfo = data;
|
||||
const name = "artist_info" in baseInfo ? baseInfo.artist_info.name :
|
||||
"album_info" in baseInfo ? baseInfo.album_info.name :
|
||||
"playlist_info" in baseInfo ? (baseInfo.playlist_info.name || baseInfo.playlist_info.owner.name) : "";
|
||||
if (name) {
|
||||
currentName.current = name;
|
||||
if (loadingToastId.current) {
|
||||
toast.silentInfo(`fetching tracks for ${name.toLowerCase()}...`, {
|
||||
id: loadingToastId.current,
|
||||
description: `${fetchedCount.current.toLocaleString()} tracks fetched`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
setMetadata(prev => {
|
||||
if (Array.isArray(data)) {
|
||||
if (!prev || !("track_list" in prev)) {
|
||||
return prev;
|
||||
}
|
||||
return {
|
||||
...prev,
|
||||
track_list: [...prev.track_list, ...data]
|
||||
};
|
||||
}
|
||||
if (prev && "track_list" in prev && prev.track_list.length > 0) {
|
||||
return prev;
|
||||
}
|
||||
const baseInfo = data;
|
||||
if (!("track_list" in baseInfo)) {
|
||||
baseInfo.track_list = [];
|
||||
}
|
||||
return baseInfo;
|
||||
});
|
||||
};
|
||||
EventsOn("metadata-stream", handler);
|
||||
return () => EventsOff("metadata-stream");
|
||||
}, []);
|
||||
const getUrlType = (url: string): string => {
|
||||
if (url.includes("/track/"))
|
||||
return "track";
|
||||
|
||||
@@ -5,41 +5,49 @@ import { getSettings } from "./settings";
|
||||
const toastStyle = {
|
||||
className: "font-mono lowercase",
|
||||
};
|
||||
type ToastData = Parameters<typeof toast.success>[1];
|
||||
const isSfxEnabled = () => getSettings().sfxEnabled;
|
||||
export const toastWithSound = {
|
||||
success: (message: string, data?: any) => {
|
||||
success: (message: string, data?: ToastData) => {
|
||||
const msg = message.toLowerCase();
|
||||
logger.success(msg);
|
||||
if (isSfxEnabled())
|
||||
playSuccessSound();
|
||||
return toast.success(msg, { ...toastStyle, ...data });
|
||||
},
|
||||
error: (message: string, data?: any) => {
|
||||
error: (message: string, data?: ToastData) => {
|
||||
const msg = message.toLowerCase();
|
||||
logger.error(msg);
|
||||
if (isSfxEnabled())
|
||||
playErrorSound();
|
||||
return toast.error(msg, { ...toastStyle, ...data });
|
||||
},
|
||||
warning: (message: string, data?: any) => {
|
||||
warning: (message: string, data?: ToastData) => {
|
||||
const msg = message.toLowerCase();
|
||||
logger.warning(msg);
|
||||
if (isSfxEnabled())
|
||||
playWarningSound();
|
||||
return toast.warning(msg, { ...toastStyle, ...data });
|
||||
},
|
||||
info: (message: string, data?: any) => {
|
||||
info: (message: string, data?: ToastData) => {
|
||||
const msg = message.toLowerCase();
|
||||
logger.info(msg);
|
||||
if (isSfxEnabled())
|
||||
playInfoSound();
|
||||
return toast.info(msg, { ...toastStyle, ...data });
|
||||
},
|
||||
message: (message: string, data?: any) => {
|
||||
message: (message: string, data?: ToastData) => {
|
||||
const msg = message.toLowerCase();
|
||||
logger.info(msg);
|
||||
if (isSfxEnabled())
|
||||
playInfoSound();
|
||||
return toast(msg, { ...toastStyle, ...data });
|
||||
},
|
||||
silentInfo: (message: string, data?: ToastData) => {
|
||||
const msg = message.toLowerCase();
|
||||
logger.info(msg);
|
||||
return toast.info(msg, { ...toastStyle, ...data });
|
||||
},
|
||||
dismiss: (id?: string | number) => toast.dismiss(id),
|
||||
toast: toast,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user