feat: add API availability checks and fallback for cover art in various components
This commit is contained in:
@@ -80,8 +80,12 @@ export default function AlbumPage() {
|
|||||||
console.error('Failed to play album from track:', error);
|
console.error('Failed to play album from track:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddToQueue = (song: Song) => {
|
const handleAddToQueue = (song: Song) => {
|
||||||
|
if (!api) {
|
||||||
|
console.error('Navidrome API not available');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const track = {
|
const track = {
|
||||||
id: song.id,
|
id: song.id,
|
||||||
name: song.title,
|
name: song.title,
|
||||||
@@ -106,9 +110,8 @@ export default function AlbumPage() {
|
|||||||
const seconds = duration % 60;
|
const seconds = duration % 60;
|
||||||
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get cover art URL with proper fallback
|
// Get cover art URL with proper fallback
|
||||||
const coverArtUrl = album.coverArt
|
const coverArtUrl = album.coverArt && api
|
||||||
? api.getCoverArtUrl(album.coverArt, 300)
|
? api.getCoverArtUrl(album.coverArt, 300)
|
||||||
: '/default-user.jpg';
|
: '/default-user.jpg';
|
||||||
|
|
||||||
|
|||||||
@@ -89,9 +89,8 @@ export default function ArtistPage() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get artist image URL with proper fallback
|
// Get artist image URL with proper fallback
|
||||||
const artistImageUrl = artist.coverArt
|
const artistImageUrl = artist.coverArt && api
|
||||||
? api.getCoverArtUrl(artist.coverArt, 300)
|
? api.getCoverArtUrl(artist.coverArt, 300)
|
||||||
: '/default-user.jpg';
|
: '/default-user.jpg';
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,12 @@ export default function BrowsePage() {
|
|||||||
const albumsPerPage = 84;
|
const albumsPerPage = 84;
|
||||||
|
|
||||||
const api = getNavidromeAPI();
|
const api = getNavidromeAPI();
|
||||||
|
|
||||||
const loadAlbums = async (page: number, append: boolean = false) => {
|
const loadAlbums = async (page: number, append: boolean = false) => {
|
||||||
|
if (!api) {
|
||||||
|
console.error('Navidrome API not available');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setIsLoadingAlbums(true);
|
setIsLoadingAlbums(true);
|
||||||
const offset = page * albumsPerPage;
|
const offset = page * albumsPerPage;
|
||||||
|
|||||||
@@ -57,9 +57,8 @@ export function AlbumArtwork({
|
|||||||
starItem(album.id, 'album');
|
starItem(album.id, 'album');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get cover art URL with proper fallback
|
// Get cover art URL with proper fallback
|
||||||
const coverArtUrl = album.coverArt
|
const coverArtUrl = album.coverArt && api
|
||||||
? api.getCoverArtUrl(album.coverArt, 300)
|
? api.getCoverArtUrl(album.coverArt, 300)
|
||||||
: '/default-user.jpg';
|
: '/default-user.jpg';
|
||||||
|
|
||||||
|
|||||||
@@ -52,9 +52,8 @@ export function ArtistIcon({
|
|||||||
starItem(artist.id, 'artist');
|
starItem(artist.id, 'artist');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get cover art URL with proper fallback
|
// Get cover art URL with proper fallback
|
||||||
const artistImageUrl = artist.coverArt
|
const artistImageUrl = artist.coverArt && api
|
||||||
? api.getCoverArtUrl(artist.coverArt, 200)
|
? api.getCoverArtUrl(artist.coverArt, 200)
|
||||||
: '/default-user.jpg';
|
: '/default-user.jpg';
|
||||||
|
|
||||||
|
|||||||
@@ -51,9 +51,8 @@ const PlaylistsPage: React.FC = () => {
|
|||||||
<Separator className="my-4" />
|
<Separator className="my-4" />
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<ScrollArea>
|
<ScrollArea>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 pb-4">
|
<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) => {
|
||||||
{playlists.map((playlist) => {
|
const playlistCoverUrl = playlist.coverArt && api
|
||||||
const playlistCoverUrl = playlist.coverArt
|
|
||||||
? api.getCoverArtUrl(playlist.coverArt, 200)
|
? api.getCoverArtUrl(playlist.coverArt, 200)
|
||||||
: '/default-user.jpg';
|
: '/default-user.jpg';
|
||||||
|
|
||||||
|
|||||||
@@ -101,8 +101,12 @@ export default function SongsPage() {
|
|||||||
|
|
||||||
setFilteredSongs(filtered);
|
setFilteredSongs(filtered);
|
||||||
}, [songs, searchQuery, sortBy, sortDirection]);
|
}, [songs, searchQuery, sortBy, sortDirection]);
|
||||||
|
|
||||||
const handlePlaySong = (song: Song) => {
|
const handlePlaySong = (song: Song) => {
|
||||||
|
if (!api) {
|
||||||
|
console.error('Navidrome API not available');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const track = {
|
const track = {
|
||||||
id: song.id,
|
id: song.id,
|
||||||
name: song.title,
|
name: song.title,
|
||||||
@@ -117,8 +121,12 @@ export default function SongsPage() {
|
|||||||
|
|
||||||
playTrack(track);
|
playTrack(track);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddToQueue = (song: Song) => {
|
const handleAddToQueue = (song: Song) => {
|
||||||
|
if (!api) {
|
||||||
|
console.error('Navidrome API not available');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const track = {
|
const track = {
|
||||||
id: song.id,
|
id: song.id,
|
||||||
name: song.title,
|
name: song.title,
|
||||||
@@ -229,9 +237,8 @@ export default function SongsPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Album Art */}
|
{/* Album Art */}
|
||||||
<div className="w-12 h-12 mr-4 flex-shrink-0">
|
<div className="w-12 h-12 mr-4 flex-shrink-0"> <Image
|
||||||
<Image
|
src={song.coverArt && api ? api.getCoverArtUrl(song.coverArt, 100) : '/default-user.jpg'}
|
||||||
src={song.coverArt ? api.getCoverArtUrl(song.coverArt, 100) : '/default-user.jpg'}
|
|
||||||
alt={song.album}
|
alt={song.album}
|
||||||
width={48}
|
width={48}
|
||||||
height={48}
|
height={48}
|
||||||
|
|||||||
@@ -44,8 +44,12 @@ export default function PlaylistPage() {
|
|||||||
fetchPlaylist();
|
fetchPlaylist();
|
||||||
}
|
}
|
||||||
}, [id, getPlaylist]);
|
}, [id, getPlaylist]);
|
||||||
|
|
||||||
const handlePlayClick = (song: Song) => {
|
const handlePlayClick = (song: Song) => {
|
||||||
|
if (!api) {
|
||||||
|
console.error('Navidrome API not available');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const track = {
|
const track = {
|
||||||
id: song.id,
|
id: song.id,
|
||||||
name: song.title,
|
name: song.title,
|
||||||
@@ -59,8 +63,12 @@ export default function PlaylistPage() {
|
|||||||
};
|
};
|
||||||
playTrack(track);
|
playTrack(track);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddToQueue = (song: Song) => {
|
const handleAddToQueue = (song: Song) => {
|
||||||
|
if (!api) {
|
||||||
|
console.error('Navidrome API not available');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const track = {
|
const track = {
|
||||||
id: song.id,
|
id: song.id,
|
||||||
name: song.title,
|
name: song.title,
|
||||||
@@ -74,9 +82,11 @@ export default function PlaylistPage() {
|
|||||||
};
|
};
|
||||||
addToQueue(track);
|
addToQueue(track);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePlayPlaylist = () => {
|
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
|
// Convert all songs to tracks
|
||||||
const tracks = tracklist.map(song => ({
|
const tracks = tracklist.map(song => ({
|
||||||
@@ -125,9 +135,8 @@ export default function PlaylistPage() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get playlist cover art URL with fallback
|
// Get playlist cover art URL with fallback
|
||||||
const playlistCoverUrl = playlist.coverArt
|
const playlistCoverUrl = playlist.coverArt && api
|
||||||
? api.getCoverArtUrl(playlist.coverArt, 300)
|
? api.getCoverArtUrl(playlist.coverArt, 300)
|
||||||
: '/default-user.jpg';
|
: '/default-user.jpg';
|
||||||
|
|
||||||
@@ -196,9 +205,8 @@ export default function PlaylistPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Album Art */}
|
{/* Album Art */}
|
||||||
<div className="w-12 h-12 mr-4 flex-shrink-0">
|
<div className="w-12 h-12 mr-4 flex-shrink-0"> <Image
|
||||||
<Image
|
src={song.coverArt && api ? api.getCoverArtUrl(song.coverArt, 100) : '/default-user.jpg'}
|
||||||
src={song.coverArt ? api.getCoverArtUrl(song.coverArt, 100) : '/default-user.jpg'}
|
|
||||||
alt={song.album}
|
alt={song.album}
|
||||||
width={48}
|
width={48}
|
||||||
height={48}
|
height={48}
|
||||||
|
|||||||
@@ -22,11 +22,13 @@ const RadioStationsPage = () => {
|
|||||||
});
|
});
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const { playTrack } = useAudioPlayer();
|
const { playTrack } = useAudioPlayer();
|
||||||
|
|
||||||
const loadRadioStations = useCallback(async () => {
|
const loadRadioStations = useCallback(async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
const api = getNavidromeAPI();
|
const api = getNavidromeAPI();
|
||||||
|
if (!api) {
|
||||||
|
throw new Error('Navidrome API not available');
|
||||||
|
}
|
||||||
const stationList = await api.getInternetRadioStations();
|
const stationList = await api.getInternetRadioStations();
|
||||||
setStations(stationList);
|
setStations(stationList);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -53,10 +55,11 @@ const RadioStationsPage = () => {
|
|||||||
variant: "destructive"
|
variant: "destructive"
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
} try {
|
||||||
|
|
||||||
try {
|
|
||||||
const api = getNavidromeAPI();
|
const api = getNavidromeAPI();
|
||||||
|
if (!api) {
|
||||||
|
throw new Error('Navidrome API not available');
|
||||||
|
}
|
||||||
await api.createInternetRadioStation(
|
await api.createInternetRadioStation(
|
||||||
newStation.name,
|
newStation.name,
|
||||||
newStation.streamUrl,
|
newStation.streamUrl,
|
||||||
@@ -81,9 +84,11 @@ const RadioStationsPage = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteRadioStation = async (stationId: string) => {
|
const deleteRadioStation = async (stationId: string) => { try {
|
||||||
try {
|
|
||||||
const api = getNavidromeAPI();
|
const api = getNavidromeAPI();
|
||||||
|
if (!api) {
|
||||||
|
throw new Error('Navidrome API not available');
|
||||||
|
}
|
||||||
await api.deleteInternetRadioStation(stationId);
|
await api.deleteInternetRadioStation(stationId);
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@@ -51,8 +51,12 @@ export default function SearchPage() {
|
|||||||
return () => clearTimeout(timeoutId);
|
return () => clearTimeout(timeoutId);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [searchQuery]);
|
}, [searchQuery]);
|
||||||
|
|
||||||
const handlePlaySong = (song: Song) => {
|
const handlePlaySong = (song: Song) => {
|
||||||
|
if (!api) {
|
||||||
|
console.error('Navidrome API not available');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const track = {
|
const track = {
|
||||||
id: song.id,
|
id: song.id,
|
||||||
name: song.title,
|
name: song.title,
|
||||||
@@ -67,8 +71,12 @@ export default function SearchPage() {
|
|||||||
|
|
||||||
playTrack(track);
|
playTrack(track);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddToQueue = (song: Song) => {
|
const handleAddToQueue = (song: Song) => {
|
||||||
|
if (!api) {
|
||||||
|
console.error('Navidrome API not available');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const track = {
|
const track = {
|
||||||
id: song.id,
|
id: song.id,
|
||||||
name: song.title,
|
name: song.title,
|
||||||
@@ -182,9 +190,8 @@ export default function SearchPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Song Cover */}
|
{/* Song Cover */}
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0"> <Image
|
||||||
<Image
|
src={song.coverArt && api ? api.getCoverArtUrl(song.coverArt, 64) : '/default-user.jpg'}
|
||||||
src={song.coverArt ? api.getCoverArtUrl(song.coverArt, 64) : '/default-user.jpg'}
|
|
||||||
alt={song.album}
|
alt={song.album}
|
||||||
width={48}
|
width={48}
|
||||||
height={48}
|
height={48}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
"lucide-react": "^0.469.0",
|
"lucide-react": "^0.469.0",
|
||||||
"next": "^15.0.3",
|
"next": "^15.0.3",
|
||||||
"posthog-js": "^1.255.0",
|
"posthog-js": "^1.255.0",
|
||||||
|
"posthog-node": "^5.1.1",
|
||||||
"react": "^19",
|
"react": "^19",
|
||||||
"react-dom": "^19",
|
"react-dom": "^19",
|
||||||
"react-hook-form": "^7.53.2",
|
"react-hook-form": "^7.53.2",
|
||||||
|
|||||||
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@@ -71,6 +71,9 @@ importers:
|
|||||||
posthog-js:
|
posthog-js:
|
||||||
specifier: ^1.255.0
|
specifier: ^1.255.0
|
||||||
version: 1.255.0
|
version: 1.255.0
|
||||||
|
posthog-node:
|
||||||
|
specifier: ^5.1.1
|
||||||
|
version: 5.1.1
|
||||||
react:
|
react:
|
||||||
specifier: ^19
|
specifier: ^19
|
||||||
version: 19.0.0
|
version: 19.0.0
|
||||||
@@ -1968,6 +1971,10 @@ packages:
|
|||||||
rrweb-snapshot:
|
rrweb-snapshot:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
posthog-node@5.1.1:
|
||||||
|
resolution: {integrity: sha512-6VISkNdxO24ehXiDA4dugyCSIV7lpGVaEu5kn/dlAj+SJ1lgcDru9PQ8p/+GSXsXVxohd1t7kHL2JKc9NoGb0w==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
preact@10.26.9:
|
preact@10.26.9:
|
||||||
resolution: {integrity: sha512-SSjF9vcnF27mJK1XyFMNJzFd5u3pQiATFqoaDy03XuN00u4ziveVVEGt5RKJrDR8MHE/wJo9Nnad56RLzS2RMA==}
|
resolution: {integrity: sha512-SSjF9vcnF27mJK1XyFMNJzFd5u3pQiATFqoaDy03XuN00u4ziveVVEGt5RKJrDR8MHE/wJo9Nnad56RLzS2RMA==}
|
||||||
|
|
||||||
@@ -4391,6 +4398,8 @@ snapshots:
|
|||||||
preact: 10.26.9
|
preact: 10.26.9
|
||||||
web-vitals: 4.2.4
|
web-vitals: 4.2.4
|
||||||
|
|
||||||
|
posthog-node@5.1.1: {}
|
||||||
|
|
||||||
preact@10.26.9: {}
|
preact@10.26.9: {}
|
||||||
|
|
||||||
prelude-ls@1.2.1: {}
|
prelude-ls@1.2.1: {}
|
||||||
|
|||||||
Reference in New Issue
Block a user