205 lines
6.6 KiB
TypeScript
205 lines
6.6 KiB
TypeScript
import { useState } from "react";
|
|
import { fetchSpotifyMetadata } from "@/lib/api";
|
|
import { toastWithSound as toast } from "@/lib/toast-with-sound";
|
|
import { logger } from "@/lib/logger";
|
|
import type { SpotifyMetadataResponse } from "@/types/api";
|
|
|
|
export function useMetadata() {
|
|
const [loading, setLoading] = useState(false);
|
|
const [metadata, setMetadata] = useState<SpotifyMetadataResponse | null>(null);
|
|
const [showTimeoutDialog, setShowTimeoutDialog] = useState(false);
|
|
const [timeoutValue, setTimeoutValue] = useState(60);
|
|
const [pendingUrl, setPendingUrl] = useState("");
|
|
const [showAlbumDialog, setShowAlbumDialog] = useState(false);
|
|
const [selectedAlbum, setSelectedAlbum] = useState<{
|
|
id: string;
|
|
name: string;
|
|
external_urls: string;
|
|
} | null>(null);
|
|
const [pendingArtistName, setPendingArtistName] = useState<string | null>(null);
|
|
|
|
const getUrlType = (url: string): string => {
|
|
if (url.includes("/track/")) return "track";
|
|
if (url.includes("/album/")) return "album";
|
|
if (url.includes("/playlist/")) return "playlist";
|
|
if (url.includes("/artist/")) return "artist";
|
|
return "unknown";
|
|
};
|
|
|
|
const fetchMetadataDirectly = async (url: string) => {
|
|
const urlType = getUrlType(url);
|
|
logger.info(`fetching ${urlType} metadata...`);
|
|
logger.debug(`url: ${url}`);
|
|
|
|
setLoading(true);
|
|
setMetadata(null);
|
|
|
|
try {
|
|
const startTime = Date.now();
|
|
const data = await fetchSpotifyMetadata(url);
|
|
const elapsed = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
|
|
setMetadata(data);
|
|
|
|
// Log detailed info based on type
|
|
if ("track" in data) {
|
|
logger.success(`fetched track: ${data.track.name} - ${data.track.artists}`);
|
|
logger.debug(`isrc: ${data.track.isrc}, duration: ${data.track.duration_ms}ms`);
|
|
} else if ("album_info" in data) {
|
|
logger.success(`fetched album: ${data.album_info.name}`);
|
|
logger.debug(`${data.track_list.length} tracks, released: ${data.album_info.release_date}`);
|
|
} else if ("playlist_info" in data) {
|
|
logger.success(`fetched playlist: ${data.track_list.length} tracks`);
|
|
logger.debug(`by ${data.playlist_info.owner.display_name || data.playlist_info.owner.name}`);
|
|
} else if ("artist_info" in data) {
|
|
logger.success(`fetched artist: ${data.artist_info.name}`);
|
|
logger.debug(`${data.album_list.length} albums, ${data.track_list.length} tracks`);
|
|
}
|
|
|
|
logger.info(`fetch completed in ${elapsed}s`);
|
|
toast.success("Metadata fetched successfully");
|
|
} catch (err) {
|
|
const errorMsg = err instanceof Error ? err.message : "Failed to fetch metadata";
|
|
logger.error(`fetch failed: ${errorMsg}`);
|
|
toast.error(errorMsg);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleFetchMetadata = async (url: string) => {
|
|
if (!url.trim()) {
|
|
logger.warning("empty url provided");
|
|
toast.error("Please enter a Spotify URL");
|
|
return;
|
|
}
|
|
|
|
let urlToFetch = url.trim();
|
|
const isArtistUrl = urlToFetch.includes("/artist/");
|
|
|
|
if (isArtistUrl && !urlToFetch.includes("/discography")) {
|
|
urlToFetch = urlToFetch.replace(/\/$/, "") + "/discography/all";
|
|
logger.debug("converted to discography url");
|
|
}
|
|
|
|
if (isArtistUrl) {
|
|
logger.info("artist url detected, showing timeout dialog");
|
|
setPendingUrl(urlToFetch);
|
|
setPendingArtistName(null); // Clear artist name for URL input
|
|
setShowTimeoutDialog(true);
|
|
} else {
|
|
await fetchMetadataDirectly(urlToFetch);
|
|
}
|
|
|
|
return urlToFetch;
|
|
};
|
|
|
|
const handleConfirmFetch = async () => {
|
|
setShowTimeoutDialog(false);
|
|
logger.info(`fetching artist discography (timeout: ${timeoutValue}s)...`);
|
|
logger.debug(`url: ${pendingUrl}`);
|
|
|
|
setLoading(true);
|
|
setMetadata(null);
|
|
|
|
try {
|
|
const startTime = Date.now();
|
|
const data = await fetchSpotifyMetadata(pendingUrl, true, 1.0, timeoutValue);
|
|
const elapsed = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
|
|
setMetadata(data);
|
|
|
|
if ("artist_info" in data) {
|
|
logger.success(`fetched artist: ${data.artist_info.name}`);
|
|
logger.debug(`${data.album_list.length} albums, ${data.track_list.length} tracks`);
|
|
}
|
|
|
|
logger.info(`fetch completed in ${elapsed}s`);
|
|
toast.success("Metadata fetched successfully");
|
|
} catch (err) {
|
|
const errorMsg = err instanceof Error ? err.message : "Failed to fetch metadata";
|
|
logger.error(`fetch failed: ${errorMsg}`);
|
|
toast.error(errorMsg);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleAlbumClick = (album: {
|
|
id: string;
|
|
name: string;
|
|
external_urls: string;
|
|
}) => {
|
|
logger.debug(`album clicked: ${album.name}`);
|
|
setSelectedAlbum(album);
|
|
setShowAlbumDialog(true);
|
|
};
|
|
|
|
const handleArtistClick = async (artist: {
|
|
id: string;
|
|
name: string;
|
|
external_urls: string;
|
|
}) => {
|
|
logger.debug(`artist clicked: ${artist.name}`);
|
|
const artistUrl = artist.external_urls.replace(/\/$/, "") + "/discography/all";
|
|
setPendingUrl(artistUrl);
|
|
setPendingArtistName(artist.name);
|
|
setShowTimeoutDialog(true);
|
|
return artistUrl;
|
|
};
|
|
|
|
const handleConfirmAlbumFetch = async () => {
|
|
if (!selectedAlbum) return;
|
|
|
|
const albumUrl = selectedAlbum.external_urls;
|
|
logger.info(`fetching album: ${selectedAlbum.name}...`);
|
|
logger.debug(`url: ${albumUrl}`);
|
|
|
|
setShowAlbumDialog(false);
|
|
setLoading(true);
|
|
setMetadata(null);
|
|
|
|
try {
|
|
const startTime = Date.now();
|
|
const data = await fetchSpotifyMetadata(albumUrl);
|
|
const elapsed = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
|
|
setMetadata(data);
|
|
|
|
if ("album_info" in data) {
|
|
logger.success(`fetched album: ${data.album_info.name}`);
|
|
logger.debug(`${data.track_list.length} tracks, released: ${data.album_info.release_date}`);
|
|
}
|
|
|
|
logger.info(`fetch completed in ${elapsed}s`);
|
|
toast.success("Album metadata fetched successfully");
|
|
return albumUrl;
|
|
} catch (err) {
|
|
const errorMsg = err instanceof Error ? err.message : "Failed to fetch album metadata";
|
|
logger.error(`fetch failed: ${errorMsg}`);
|
|
toast.error(errorMsg);
|
|
} finally {
|
|
setLoading(false);
|
|
setSelectedAlbum(null);
|
|
}
|
|
};
|
|
|
|
return {
|
|
loading,
|
|
metadata,
|
|
showTimeoutDialog,
|
|
setShowTimeoutDialog,
|
|
timeoutValue,
|
|
setTimeoutValue,
|
|
showAlbumDialog,
|
|
setShowAlbumDialog,
|
|
selectedAlbum,
|
|
pendingArtistName,
|
|
handleFetchMetadata,
|
|
handleConfirmFetch,
|
|
handleAlbumClick,
|
|
handleConfirmAlbumFetch,
|
|
handleArtistClick,
|
|
};
|
|
}
|