feat: add API availability checks and fallback for cover art in various components

This commit is contained in:
2025-06-22 21:10:15 -05:00
parent 6fcf58e7ba
commit b07685fe79
12 changed files with 78 additions and 38 deletions

View File

@@ -80,8 +80,12 @@ export default function AlbumPage() {
console.error('Failed to play album from track:', error);
}
};
const handleAddToQueue = (song: Song) => {
if (!api) {
console.error('Navidrome API not available');
return;
}
const track = {
id: song.id,
name: song.title,
@@ -106,9 +110,8 @@ export default function AlbumPage() {
const seconds = duration % 60;
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
};
// Get cover art URL with proper fallback
const coverArtUrl = album.coverArt
const coverArtUrl = album.coverArt && api
? api.getCoverArtUrl(album.coverArt, 300)
: '/default-user.jpg';

View File

@@ -89,9 +89,8 @@ export default function ArtistPage() {
</div>
);
}
// Get artist image URL with proper fallback
const artistImageUrl = artist.coverArt
const artistImageUrl = artist.coverArt && api
? api.getCoverArtUrl(artist.coverArt, 300)
: '/default-user.jpg';

View File

@@ -24,8 +24,12 @@ export default function BrowsePage() {
const albumsPerPage = 84;
const api = getNavidromeAPI();
const loadAlbums = async (page: number, append: boolean = false) => {
if (!api) {
console.error('Navidrome API not available');
return;
}
try {
setIsLoadingAlbums(true);
const offset = page * albumsPerPage;

View File

@@ -57,9 +57,8 @@ export function AlbumArtwork({
starItem(album.id, 'album');
}
};
// Get cover art URL with proper fallback
const coverArtUrl = album.coverArt
const coverArtUrl = album.coverArt && api
? api.getCoverArtUrl(album.coverArt, 300)
: '/default-user.jpg';

View File

@@ -52,9 +52,8 @@ export function ArtistIcon({
starItem(artist.id, 'artist');
}
};
// Get cover art URL with proper fallback
const artistImageUrl = artist.coverArt
const artistImageUrl = artist.coverArt && api
? api.getCoverArtUrl(artist.coverArt, 200)
: '/default-user.jpg';

View File

@@ -51,9 +51,8 @@ const PlaylistsPage: React.FC = () => {
<Separator className="my-4" />
<div className="relative">
<ScrollArea>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 pb-4">
{playlists.map((playlist) => {
const playlistCoverUrl = playlist.coverArt
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 pb-4"> {playlists.map((playlist) => {
const playlistCoverUrl = playlist.coverArt && api
? api.getCoverArtUrl(playlist.coverArt, 200)
: '/default-user.jpg';

View File

@@ -101,8 +101,12 @@ export default function SongsPage() {
setFilteredSongs(filtered);
}, [songs, searchQuery, sortBy, sortDirection]);
const handlePlaySong = (song: Song) => {
if (!api) {
console.error('Navidrome API not available');
return;
}
const track = {
id: song.id,
name: song.title,
@@ -117,8 +121,12 @@ export default function SongsPage() {
playTrack(track);
};
const handleAddToQueue = (song: Song) => {
if (!api) {
console.error('Navidrome API not available');
return;
}
const track = {
id: song.id,
name: song.title,
@@ -229,9 +237,8 @@ export default function SongsPage() {
</div>
{/* Album Art */}
<div className="w-12 h-12 mr-4 flex-shrink-0">
<Image
src={song.coverArt ? api.getCoverArtUrl(song.coverArt, 100) : '/default-user.jpg'}
<div className="w-12 h-12 mr-4 flex-shrink-0"> <Image
src={song.coverArt && api ? api.getCoverArtUrl(song.coverArt, 100) : '/default-user.jpg'}
alt={song.album}
width={48}
height={48}

View File

@@ -44,8 +44,12 @@ export default function PlaylistPage() {
fetchPlaylist();
}
}, [id, getPlaylist]);
const handlePlayClick = (song: Song) => {
if (!api) {
console.error('Navidrome API not available');
return;
}
const track = {
id: song.id,
name: song.title,
@@ -59,8 +63,12 @@ export default function PlaylistPage() {
};
playTrack(track);
};
const handleAddToQueue = (song: Song) => {
if (!api) {
console.error('Navidrome API not available');
return;
}
const track = {
id: song.id,
name: song.title,
@@ -74,9 +82,11 @@ export default function PlaylistPage() {
};
addToQueue(track);
};
const handlePlayPlaylist = () => {
if (tracklist.length === 0) return;
if (tracklist.length === 0 || !api) {
if (!api) console.error('Navidrome API not available');
return;
}
// Convert all songs to tracks
const tracks = tracklist.map(song => ({
@@ -125,9 +135,8 @@ export default function PlaylistPage() {
</div>
);
}
// Get playlist cover art URL with fallback
const playlistCoverUrl = playlist.coverArt
const playlistCoverUrl = playlist.coverArt && api
? api.getCoverArtUrl(playlist.coverArt, 300)
: '/default-user.jpg';
@@ -196,9 +205,8 @@ export default function PlaylistPage() {
</div>
{/* Album Art */}
<div className="w-12 h-12 mr-4 flex-shrink-0">
<Image
src={song.coverArt ? api.getCoverArtUrl(song.coverArt, 100) : '/default-user.jpg'}
<div className="w-12 h-12 mr-4 flex-shrink-0"> <Image
src={song.coverArt && api ? api.getCoverArtUrl(song.coverArt, 100) : '/default-user.jpg'}
alt={song.album}
width={48}
height={48}

View File

@@ -22,11 +22,13 @@ const RadioStationsPage = () => {
});
const { toast } = useToast();
const { playTrack } = useAudioPlayer();
const loadRadioStations = useCallback(async () => {
setIsLoading(true);
try {
const api = getNavidromeAPI();
if (!api) {
throw new Error('Navidrome API not available');
}
const stationList = await api.getInternetRadioStations();
setStations(stationList);
} catch (error) {
@@ -53,10 +55,11 @@ const RadioStationsPage = () => {
variant: "destructive"
});
return;
}
try {
} try {
const api = getNavidromeAPI();
if (!api) {
throw new Error('Navidrome API not available');
}
await api.createInternetRadioStation(
newStation.name,
newStation.streamUrl,
@@ -81,9 +84,11 @@ const RadioStationsPage = () => {
}
};
const deleteRadioStation = async (stationId: string) => {
try {
const deleteRadioStation = async (stationId: string) => { try {
const api = getNavidromeAPI();
if (!api) {
throw new Error('Navidrome API not available');
}
await api.deleteInternetRadioStation(stationId);
toast({

View File

@@ -51,8 +51,12 @@ export default function SearchPage() {
return () => clearTimeout(timeoutId);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchQuery]);
const handlePlaySong = (song: Song) => {
if (!api) {
console.error('Navidrome API not available');
return;
}
const track = {
id: song.id,
name: song.title,
@@ -67,8 +71,12 @@ export default function SearchPage() {
playTrack(track);
};
const handleAddToQueue = (song: Song) => {
if (!api) {
console.error('Navidrome API not available');
return;
}
const track = {
id: song.id,
name: song.title,
@@ -182,9 +190,8 @@ export default function SearchPage() {
</div>
{/* Song Cover */}
<div className="flex-shrink-0">
<Image
src={song.coverArt ? api.getCoverArtUrl(song.coverArt, 64) : '/default-user.jpg'}
<div className="flex-shrink-0"> <Image
src={song.coverArt && api ? api.getCoverArtUrl(song.coverArt, 64) : '/default-user.jpg'}
alt={song.album}
width={48}
height={48}

View File

@@ -30,6 +30,7 @@
"lucide-react": "^0.469.0",
"next": "^15.0.3",
"posthog-js": "^1.255.0",
"posthog-node": "^5.1.1",
"react": "^19",
"react-dom": "^19",
"react-hook-form": "^7.53.2",

9
pnpm-lock.yaml generated
View File

@@ -71,6 +71,9 @@ importers:
posthog-js:
specifier: ^1.255.0
version: 1.255.0
posthog-node:
specifier: ^5.1.1
version: 5.1.1
react:
specifier: ^19
version: 19.0.0
@@ -1968,6 +1971,10 @@ packages:
rrweb-snapshot:
optional: true
posthog-node@5.1.1:
resolution: {integrity: sha512-6VISkNdxO24ehXiDA4dugyCSIV7lpGVaEu5kn/dlAj+SJ1lgcDru9PQ8p/+GSXsXVxohd1t7kHL2JKc9NoGb0w==}
engines: {node: '>=20'}
preact@10.26.9:
resolution: {integrity: sha512-SSjF9vcnF27mJK1XyFMNJzFd5u3pQiATFqoaDy03XuN00u4ziveVVEGt5RKJrDR8MHE/wJo9Nnad56RLzS2RMA==}
@@ -4391,6 +4398,8 @@ snapshots:
preact: 10.26.9
web-vitals: 4.2.4
posthog-node@5.1.1: {}
preact@10.26.9: {}
prelude-ls@1.2.1: {}