diff --git a/.env.example b/.env.example index 71d492b..45060ab 100644 --- a/.env.example +++ b/.env.example @@ -2,7 +2,8 @@ NEXT_PUBLIC_NAVIDROME_URL=http://localhost:4533 NEXT_PUBLIC_NAVIDROME_USERNAME=your_username NEXT_PUBLIC_NAVIDROME_PASSWORD=your_password - +NEXT_PUBLIC_POSTHOG_KEY=KEY +NEXT_PUBLIC_POSTHOG_HOST=HOSTURL # Example for external server: # NEXT_PUBLIC_NAVIDROME_URL=https://your-navidrome-server.com # NEXT_PUBLIC_NAVIDROME_USERNAME=your_username diff --git a/.github/workflows/jest.yml b/.github/workflows/jest.yml deleted file mode 100644 index 09682dd..0000000 --- a/.github/workflows/jest.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Run Jest Tests - -on: - push: - branches: - - main - paths: - - 'apps/data/albums.ts' - - 'apps/data/artists.ts' - pull_request: - branches: - - main - paths: - - 'apps/data/albums.ts' - - 'apps/data/artists.ts' - -jobs: - test: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: '22' - - - name: Install dependencies - run: npm install - - - name: Run Jest tests - run: npm test \ No newline at end of file diff --git a/MIGRATION_COMPLETE.md b/MIGRATION_COMPLETE.md deleted file mode 100644 index 4ce2b2e..0000000 --- a/MIGRATION_COMPLETE.md +++ /dev/null @@ -1,145 +0,0 @@ -# Migration Summary: Firebase โ†’ Navidrome/Subsonic - -## โœ… Completed Migration Tasks - -### ๐Ÿ—‘๏ธ Removed Legacy Systems -- [x] **Firebase Dependencies**: Removed firebase, react-firebase-hooks packages -- [x] **Static Data Files**: Moved `app/data/` (albums.ts, artists.ts, playlists.ts) to backup -- [x] **Firebase Config**: Moved `app/firebase/` directory to backup -- [x] **Authentication System**: Removed Firebase Auth integration -- [x] **Database Connections**: Removed Firestore database calls - -### ๐Ÿš€ Implemented Navidrome Integration -- [x] **Navidrome API Client** (`lib/navidrome.ts`) - - Subsonic API authentication with token-based security - - All major endpoints: ping, getArtists, getAlbums, getAlbum, search3, etc. - - Stream URL generation for audio playback - - Cover art URL generation with size parameters - - Star/unstar functionality for favorites - - Scrobbling support for play tracking - -- [x] **React Context Provider** (`app/components/NavidromeContext.tsx`) - - Global state management for music library data - - Loading states for UI feedback - - Error handling and connection testing - - Data fetching with automatic refresh - - CRUD operations for playlists - -### ๐ŸŽต Updated Audio System -- [x] **AudioPlayerContext** - Completely rewritten for Navidrome - - Real audio streaming instead of static file URLs - - Queue management with Navidrome song objects - - Automatic scrobbling when tracks play - - Track conversion from Navidrome Song to playable Track format - -- [x] **AudioPlayer Component** - - Updated to handle Navidrome streaming URLs - - Dynamic cover art from Navidrome getCoverArt API - - Proper track metadata display (artist, album, duration) - -### ๐ŸŽจ Updated UI Components -- [x] **AlbumArtwork Component** - - Uses Navidrome Album interface - - Dynamic cover art with getCoverArt API - - Context menu integration with Navidrome playlists - - Proper album metadata display (year, genre, song count) - -- [x] **ArtistIcon Component** - - Uses Navidrome Artist interface - - Artist cover art support - - Album count display - - Star/unstar functionality in context menu - -### ๐Ÿ“„ Updated Pages -- [x] **Main Page** (`app/page.tsx`) - - Uses NavidromeContext for album data - - Loading states with skeleton UI - - Error handling for connection issues - - Recent and newest album sections - -- [x] **Album Detail Page** (`app/album/[id]/page.tsx`) - - Fetches album and songs from Navidrome - - Real-time song playback with streaming - - Star/unstar album functionality - - Proper track listing with metadata - -- [x] **Artist Page** (`app/artist/[artist]/page.tsx`) - - Artist details from Navidrome API - - Dynamic album grid for artist - - Star/unstar artist functionality - - Modern gradient header design - -- [x] **Library Pages** - - `app/library/albums/page.tsx` - Shows all albums in grid layout - - `app/library/artists/page.tsx` - Shows all artists in grid layout - - `app/library/playlists/page.tsx` - Playlist management with CRUD operations - -### ๐Ÿ”ง Configuration & Documentation -- [x] **Environment Configuration** - - `.env.example` with Navidrome connection settings - - Removed Firebase environment variables from package.json - -- [x] **Documentation** - - `NAVIDROME_MIGRATION.md` - Detailed migration guide - - Updated `README.md` with new setup instructions - - Feature documentation and troubleshooting - -- [x] **Type Safety** - - TypeScript interfaces matching Subsonic API responses - - Proper error handling throughout the application - - Type-safe component props and context values - -### ๐Ÿงช Testing -- [x] **Test Suite** (`__tests__/navidrome.test.ts`) - - API client functionality tests - - TypeScript interface validation - - URL generation testing - - Configuration validation - -## ๐ŸŽฏ Key Benefits Achieved - -### **Real Music Streaming** -- Replaced static MP3 URLs with dynamic Navidrome streaming -- Support for multiple audio formats and bitrates -- Proper audio metadata from music files - -### **Dynamic Library** -- No more manual JSON file management -- Auto-discovery of new music added to Navidrome -- Real-time library updates - -### **Enhanced Features** -- Scrobbling for play tracking -- Star/favorite functionality -- Playlist management (create, edit, delete) -- Search across entire music library -- High-quality album artwork - -### **Better Architecture** -- Removed Firebase dependency completely -- Self-hosted music solution -- Standards-based Subsonic API integration -- Type-safe development with proper interfaces - -## ๐Ÿ”„ Migration Path - -1. **Backup**: Old Firebase and static data moved to `-old` directories -2. **Dependencies**: Firebase packages removed, crypto built-in used -3. **Environment**: New `.env.local` needed with Navidrome credentials -4. **Data Flow**: `Static JSON โ†’ Firebase โ†’ Navidrome API` -5. **Authentication**: `Firebase Auth โ†’ Navidrome Server Authentication` -6. **Streaming**: `Static Files โ†’ Navidrome Stream API` - -## ๐Ÿšฆ Ready for Production - -The application is now fully migrated and ready for use with any Navidrome server. All core functionality has been preserved and enhanced: - -- โœ… Browse music library (albums, artists, songs) -- โœ… Audio playback with queue management -- โœ… Search functionality -- โœ… Playlist management -- โœ… Favorites/starring -- โœ… Responsive design -- โœ… Error handling and loading states - -**Next Steps**: Set up Navidrome server and configure connection in `.env.local` diff --git a/app/components/AudioPlayer.tsx b/app/components/AudioPlayer.tsx index bd0dd56..408bb5a 100644 --- a/app/components/AudioPlayer.tsx +++ b/app/components/AudioPlayer.tsx @@ -10,9 +10,10 @@ import { Progress } from '@/components/ui/progress'; import { useToast } from '@/hooks/use-toast'; export const AudioPlayer: React.FC = () => { - const { currentTrack, playPreviousTrack, addToQueue, playNextTrack, clearQueue } = useAudioPlayer(); + const { currentTrack, playPreviousTrack, addToQueue, playNextTrack, clearQueue, queue } = useAudioPlayer(); const router = useRouter(); const audioRef = useRef(null); + const preloadAudioRef = useRef(null); const [progress, setProgress] = useState(0); const [isPlaying, setIsPlaying] = useState(false); const [showVolumeSlider, setShowVolumeSlider] = useState(false); @@ -31,6 +32,19 @@ export const AudioPlayer: React.FC = () => { useEffect(() => { setIsClient(true); + // Load saved volume + const savedVolume = localStorage.getItem('navidrome-volume'); + if (savedVolume) { + try { + const volumeValue = parseFloat(savedVolume); + if (volumeValue >= 0 && volumeValue <= 1) { + setVolume(volumeValue); + } + } catch (error) { + console.error('Failed to parse saved volume:', error); + } + } + // Clean up old localStorage entries with track IDs const keysToRemove: string[] = []; for (let i = 0; i < localStorage.length; i++) { @@ -42,6 +56,16 @@ export const AudioPlayer: React.FC = () => { keysToRemove.forEach(key => localStorage.removeItem(key)); }, []); + // Apply volume to audio element when volume changes + useEffect(() => { + const audioCurrent = audioRef.current; + if (audioCurrent) { + audioCurrent.volume = volume; + } + // Save volume to localStorage + localStorage.setItem('navidrome-volume', volume.toString()); + }, [volume]); + // Save position when component unmounts or track changes useEffect(() => { const audioCurrent = audioRef.current; @@ -248,9 +272,6 @@ export const AudioPlayer: React.FC = () => { const handleVolumeChange = (e: React.ChangeEvent) => { const newVolume = parseFloat(e.target.value); setVolume(newVolume); - if (audioCurrent) { - audioCurrent.volume = newVolume; - } }; function formatTime(seconds: number): string { @@ -304,6 +325,7 @@ export const AudioPlayer: React.FC = () => {