feat: enhance audio player and favorites functionality with improved type safety, update image handling in components

This commit is contained in:
2025-07-01 23:48:23 +00:00
committed by GitHub
parent 4499bdf147
commit d6ac2479cb
7 changed files with 35 additions and 29 deletions

View File

@@ -1,8 +1,8 @@
'use client';
import React, { useEffect, useRef, useState } from 'react';
import React, { useEffect, useRef, useState, useCallback } from 'react';
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import { useAudioPlayer } from '@/app/components/AudioPlayerContext';
import { useAudioPlayer, Track } from '@/app/components/AudioPlayerContext';
import { FullScreenPlayer } from '@/app/components/FullScreenPlayer';
import { FaPlay, FaPause, FaVolumeHigh, FaForward, FaBackward, FaCompress, FaVolumeXmark, FaExpand, FaShuffle } from "react-icons/fa6";
import { Progress } from '@/components/ui/progress';
@@ -44,30 +44,30 @@ export const AudioPlayer: React.FC = () => {
} = useStandaloneLastFm();
// Combined Last.fm handlers
const onTrackStart = (track: any) => {
const onTrackStart = useCallback((track: Track) => {
navidromeOnTrackStart(track);
standaloneOnTrackStart(track);
};
}, [navidromeOnTrackStart, standaloneOnTrackStart]);
const onTrackPlay = (track: any) => {
const onTrackPlay = useCallback((track: Track) => {
navidromeOnTrackPlay(track);
standaloneOnTrackPlay(track);
};
}, [navidromeOnTrackPlay, standaloneOnTrackPlay]);
const onTrackPause = (currentTime: number) => {
const onTrackPause = useCallback((currentTime: number) => {
navidromeOnTrackPause(currentTime);
standaloneOnTrackPause(currentTime);
};
}, [navidromeOnTrackPause, standaloneOnTrackPause]);
const onTrackProgress = (track: any, currentTime: number, duration: number) => {
const onTrackProgress = useCallback((track: Track, currentTime: number, duration: number) => {
navidromeOnTrackProgress(track, currentTime, duration);
standaloneOnTrackProgress(track, currentTime, duration);
};
}, [navidromeOnTrackProgress, standaloneOnTrackProgress]);
const onTrackEnd = (track: any, currentTime: number, duration: number) => {
const onTrackEnd = useCallback((track: Track, currentTime: number, duration: number) => {
navidromeOnTrackEnd(track, currentTime, duration);
standaloneOnTrackEnd(track, currentTime, duration);
};
}, [navidromeOnTrackEnd, standaloneOnTrackEnd]);
const handleOpenQueue = () => {
setIsFullScreen(false);

View File

@@ -1,4 +1,5 @@
'use client';
import Image from 'next/image';
import { Song } from '@/lib/navidrome';
import { useAudioPlayer } from '@/app/components/AudioPlayerContext';
import { Button } from '@/components/ui/button';
@@ -92,9 +93,11 @@ export function PopularSongs({ songs, artistName }: PopularSongsProps) {
{/* Album Art */}
<div className="relative w-12 h-12 bg-muted rounded-md overflow-hidden flex-shrink-0">
{song.coverArt && api && (
<img
<Image
src={api.getCoverArtUrl(song.coverArt, 96)}
alt={song.album}
width={48}
height={48}
className="w-full h-full object-cover"
/>
)}

View File

@@ -1,5 +1,6 @@
'use client';
import { useState, useEffect } from 'react';
import Image from 'next/image';
import { lastFmAPI } from '@/lib/lastfm-api';
import { Button } from '@/components/ui/button';
import { ScrollArea, ScrollBar } from '@/components/ui/scroll-area';
@@ -71,9 +72,11 @@ export function SimilarArtists({ artistName }: SimilarArtistsProps) {
>
<div className="w-32 space-y-2 group cursor-pointer">
<div className="relative w-32 h-32 bg-muted rounded-full overflow-hidden">
<img
<Image
src={getArtistImage(artist)}
alt={artist.name}
width={128}
height={128}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-200"
/>
</div>

View File

@@ -18,7 +18,7 @@ import {
import { useNavidrome } from "./NavidromeContext"
import Link from "next/link";
import { useAudioPlayer } from "@/app/components/AudioPlayerContext";
import { useAudioPlayer, Track } from "@/app/components/AudioPlayerContext";
import { getNavidromeAPI } from "@/lib/navidrome";
import React, { useState, useEffect } from 'react';
import { Button } from "@/components/ui/button";
@@ -82,7 +82,7 @@ export function AlbumArtwork({
}));
playTrack(tracks[0]);
tracks.slice(1).forEach((track: any) => addToQueue(track));
tracks.slice(1).forEach((track: Track) => addToQueue(track));
}
} catch (error) {
console.error('Failed to play album:', error);

View File

@@ -1,6 +1,6 @@
'use client';
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import {
@@ -68,11 +68,7 @@ export function LoginForm({
}, []);
// Check if Navidrome is already working on component mount
useEffect(() => {
checkNavidromeConnection();
}, []);
const checkNavidromeConnection = async () => {
const checkNavidromeConnection = useCallback(async () => {
try {
// First check if there's a working API instance
const { getNavidromeAPI } = await import('@/lib/navidrome');
@@ -122,7 +118,11 @@ export function LoginForm({
} catch (error) {
console.log('Navidrome connection check failed, will show config step');
}
};
}, [config, setStep, setFormData, setCanSkipNavidrome, testConnection]);
useEffect(() => {
checkNavidromeConnection();
}, [checkNavidromeConnection]);
const handleInputChange = (field: string, value: string) => {
setFormData(prev => ({ ...prev, [field]: value }));

View File

@@ -9,7 +9,7 @@ import { AlbumArtwork } from "@/app/components/album-artwork";
import { ArtistIcon } from "@/app/components/artist-icon";
import { Album, Artist, Song } from "@/lib/navidrome";
import { Heart, Music, Disc, Mic, Play } from "lucide-react";
import { useAudioPlayer } from "@/app/components/AudioPlayerContext";
import { useAudioPlayer, Track } from "@/app/components/AudioPlayerContext";
import Image from "next/image";
const FavoritesPage = () => {
@@ -82,7 +82,7 @@ const FavoritesPage = () => {
}));
playTrack(tracks[0]);
tracks.slice(1).forEach((track: any) => addToQueue(track));
tracks.slice(1).forEach((track: Track) => addToQueue(track));
}
} catch (error) {
console.error('Failed to play album:', error);

View File

@@ -73,7 +73,7 @@ export class LastFmAPI {
}
}
private async makeRequest(method: string, params: Record<string, string>): Promise<any> {
private async makeRequest(method: string, params: Record<string, string>): Promise<Record<string, unknown>> {
const credentials = this.getCredentials();
if (!credentials?.apiKey) {
throw new Error('No Last.fm API key available');
@@ -108,7 +108,7 @@ export class LastFmAPI {
autocorrect: '1'
});
return data.artist || null;
return (data.artist as LastFmArtistInfo) || null;
} catch (error) {
console.error('Failed to fetch artist info from Last.fm:', error);
return null;
@@ -123,7 +123,7 @@ export class LastFmAPI {
autocorrect: '1'
});
return data.toptracks || null;
return (data.toptracks as LastFmTopTracks) || null;
} catch (error) {
console.error('Failed to fetch artist top tracks from Last.fm:', error);
return null;
@@ -138,7 +138,7 @@ export class LastFmAPI {
autocorrect: '1'
});
return data.similarartists || null;
return (data.similarartists as LastFmArtistInfo['similar']) || null;
} catch (error) {
console.error('Failed to fetch similar artists from Last.fm:', error);
return null;