<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <title>AlbumView Music Player</title>

    <style>

        /* --- CSS for Styling --- */

        :root {

            --primary: #007aff; /* Apple-like blue */

            --background: #f4f6f8;

            --card-bg: #ffffff;

            --text-color: #1c1c1e;

            --border-color: #e0e0e0;

        }


        body {

            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

            background-color: var(--background);

            color: var(--text-color);

            margin: 0;

            padding: 20px;

            display: flex;

            flex-direction: column;

            align-items: center;

        }


        /* Container for the entire application */

        #app-container {

            display: flex;

            width: 90%;

            max-width: 1200px;

            min-height: 80vh;

            background-color: var(--card-bg);

            border-radius: 12px;

            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);

            overflow: hidden;

        }


        /* --- Sidebar (Albums List) --- */

        #album-sidebar {

            width: 300px;

            padding: 15px;

            border-right: 1px solid var(--border-color);

            overflow-y: auto;

            background-color: #f9f9f9;

        }


        #album-sidebar h2 {

            margin-top: 0;

            font-size: 1.5em;

            color: var(--primary);

            border-bottom: 2px solid var(--primary);

            padding-bottom: 5px;

            margin-bottom: 15px;

        }


        .album-item {

            padding: 10px;

            margin-bottom: 5px;

            border-radius: 6px;

            cursor: pointer;

            transition: background-color 0.2s, transform 0.1s;

            font-size: 0.95em;

            display: flex;

            flex-direction: column;

            border: 1px solid transparent;

        }


        .album-item:hover {

            background-color: #e8e8e8;

        }


        .album-item.active {

            background-color: var(--primary);

            color: white;

            font-weight: bold;

        }


        .album-title {

            font-weight: 600;

        }


        .album-artist {

            font-size: 0.8em;

            opacity: 0.8;

            margin-top: 2px;

        }


        .album-item.active .album-artist {

            color: white;

            opacity: 0.9;

        }


        /* --- Main Content (Track List) --- */

        #main-content {

            flex-grow: 1;

            display: flex;

            flex-direction: column;

        }


        #track-list-container {

            flex-grow: 1;

            padding: 20px;

            overflow-y: auto;

        }


        #track-list-container h3 {

            font-size: 1.3em;

            margin-top: 0;

            margin-bottom: 15px;

            border-bottom: 1px solid var(--border-color);

            padding-bottom: 5px;

            color: var(--text-color);

        }


        .track-item {

            display: flex;

            padding: 10px;

            cursor: pointer;

            transition: background-color 0.1s;

            border-radius: 4px;

        }


        .track-item:nth-child(even) {

            background-color: #fcfcfc;

        }


        .track-item:hover {

            background-color: #e0f0ff;

        }


        .track-item.playing {

            background-color: var(--primary);

            color: white;

            font-weight: 600;

        }


        .track-number {

            width: 30px;

            text-align: right;

            margin-right: 15px;

            font-style: italic;

        }


        .track-details {

            flex-grow: 1;

        }


        /* --- Player Controls Bar --- */

        #player-controls {

            display: flex;

            justify-content: space-between;

            align-items: center;

            padding: 10px 20px;

            border-top: 1px solid var(--border-color);

            background-color: #ffffff;

            height: 70px;

            box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);

        }


        #current-track-info {

            width: 30%;

            display: flex;

            flex-direction: column;

            font-size: 0.9em;

        }


        #current-title {

            font-weight: bold;

            overflow: hidden;

            white-space: nowrap;

            text-overflow: ellipsis;

        }


        #current-artist {

            opacity: 0.7;

        }


        #playback-buttons {

            display: flex;

            gap: 20px;

            align-items: center;

        }


        #playback-buttons button {

            background: none;

            border: none;

            cursor: pointer;

            font-size: 1.8em;

            color: var(--text-color);

            transition: color 0.2s, transform 0.1s;

        }


        #playback-buttons button:hover {

            color: var(--primary);

            transform: scale(1.05);

        }

        

        #playback-buttons #play-pause-btn {

            font-size: 2.5em;

        }


        #volume-control {

            width: 30%;

            display: flex;

            align-items: center;

            justify-content: flex-end;

        }


        #volume-control span {

            margin-right: 5px;

        }


        #volume-slider {

            width: 100px;

            cursor: grab;

        }

        

        /* Hidden file input, styled as a button */

        #file-input-label {

            display: inline-block;

            background-color: var(--primary);

            color: white;

            padding: 8px 15px;

            border-radius: 6px;

            cursor: pointer;

            font-weight: 500;

            transition: background-color 0.2s;

            margin-bottom: 20px;

        }

        

        #file-input-label:hover {

            background-color: #005bb5;

        }


        #file-input {

            display: none;

        }


        .loading-message {

            text-align: center;

            padding: 50px;

            font-style: italic;

            color: #6a6a6a;

        }

        

        #track-list-header {

            display: flex;

            justify-content: space-between;

            align-items: center;

        }

        

        #track-list-header h3 {

             flex-grow: 1;

        }


    </style>

    <script src="https://cdn.jsdelivr.net/npm/jsmediatags@3.9.7/dist/jsmediatags.min.js"></script>

</head>

<body>


    <input type="file" id="file-input" accept=".mp3,audio/*" multiple>

    <label for="file-input" id="file-input-label">📁 Load Music Files (MP3, WAV, etc.)</label>


    <div id="app-container">

        <div id="album-sidebar">

            <h2>Albums</h2>

            <div id="albums-list">

                <p class="loading-message">Load some music files to see your albums here.</p>

            </div>

        </div>


        <div id="main-content">

            <div id="track-list-container">

                <div id="track-list-header">

                    <h3 id="current-album-title">Select an Album</h3>

                    <button onclick="playAllTracks()" style="background: none; border: 1px solid var(--primary); color: var(--primary); padding: 5px 10px; border-radius: 4px; cursor: pointer; transition: background-color 0.2s;">▶ Play Album</button>

                </div>

                <div id="tracks-view">

                    <p class="loading-message">Tracks for the selected album will appear here.</p>

                </div>

            </div>


            <div id="player-controls">

                <div id="current-track-info">

                    <span id="current-title">Not Playing</span>

                    <span id="current-artist">Load files to begin</span>

                </div>

                <div id="playback-buttons">

                    <button id="prev-btn">⏮</button>

                    <button id="play-pause-btn">▶</button>

                    <button id="next-btn">⏭</button>

                </div>

                <div id="volume-control">

                    <span>🔊</span>

                    <input type="range" id="volume-slider" min="0" max="1" step="0.05" value="1">

                </div>

            </div>

        </div>

    </div>


    <script>

        // --- JavaScript for Logic ---


        const fileInput = document.getElementById('file-input');

        const albumsListDiv = document.getElementById('albums-list');

        const tracksViewDiv = document.getElementById('tracks-view');

        const currentAlbumTitle = document.getElementById('current-album-title');

        const currentTitleSpan = document.getElementById('current-title');

        const currentArtistSpan = document.getElementById('current-artist');

        const playPauseBtn = document.getElementById('play-pause-btn');

        const prevBtn = document.getElementById('prev-btn');

        const nextBtn = document.getElementById('next-btn');

        const volumeSlider = document.getElementById('volume-slider');

        

        // Audio object is the core of the player

        const audio = new Audio();


        // Data structure to hold all music files, grouped by a unique album key

        // Structure: { 'Album Name - Artist Name': [ {file: File, tags: {}}, ... ], ... }

        let library = {};

        // Array of all tracks in the currently active album

        let currentAlbumTracks = [];

        let currentTrackIndex = -1;

        let activeAlbumKey = null;


        /**

         * 1. Load Files and Read Metadata

         */


        fileInput.addEventListener('change', async (event) => {

            const files = event.target.files;

            if (files.length === 0) return;


            // Clear previous library and UI

            library = {};

            albumsListDiv.innerHTML = '<p class="loading-message">Processing files... Please wait.</p>';


            const fileArray = Array.from(files);

            let processedCount = 0;


            // Process each file to get its metadata

            for (const file of fileArray) {

                if (!file.type.startsWith('audio/')) {

                    processedCount++;

                    continue;

                }

                

                try {

                    const tags = await getTags(file);

                    

                    // Sanitize album/artist names for consistent grouping

                    const album = tags.album || 'Unknown Album';

                    const artist = tags.artist || 'Unknown Artist';

                    const key = `${album.trim()} - ${artist.trim()}`;

                    

                    if (!library[key]) {

                        library[key] = {

                            album: album,

                            artist: artist,

                            tracks: []

                        };

                    }


                    library[key].tracks.push({

                        file: file,

                        tags: tags,

                        // Create a URL for the audio object to play from

                        url: URL.createObjectURL(file) 

                    });


                } catch (error) {

                    console.error('Error reading tags for file:', file.name, error);

                    // Add file even if tags fail, using filename as fallback

                    const key = `Error Tagged: ${file.name}`;

                     if (!library[key]) {

                        library[key] = {

                            album: 'Error Tagged',

                            artist: file.name,

                            tracks: []

                        };

                    }

                    library[key].tracks.push({

                        file: file,

                        tags: {title: file.name, artist: 'Unknown'},

                        url: URL.createObjectURL(file) 

                    });

                }

                

                processedCount++;

            }


            renderAlbumList();

        });


        // Helper function to promisify jsmediatags

        function getTags(file) {

            return new Promise((resolve, reject) => {

                jsmediatags.read(file, {

                    onSuccess: (tag) => {

                        const tags = tag.tags;

                        resolve({

                            title: tags.title || file.name.replace(/\.[^/.]+$/, ""), // Fallback to filename

                            artist: tags.artist || 'Unknown Artist',

                            album: tags.album || 'Unknown Album',

                            track: tags.track ? tags.track.split('/')[0] : null // Clean up track number

                        });

                    },

                    onError: (error) => {

                        reject(error);

                    }

                });

            });

        }



        /**

         * 2. UI Rendering (Albums and Tracks)

         */


        function renderAlbumList() {

            const albumKeys = Object.keys(library).sort();

            albumsListDiv.innerHTML = ''; // Clear existing list


            if (albumKeys.length === 0) {

                albumsListDiv.innerHTML = '<p class="loading-message">No valid audio files were found.</p>';

                return;

            }


            albumKeys.forEach(key => {

                const albumData = library[key];

                const albumEl = document.createElement('div');

                albumEl.className = 'album-item';

                albumEl.dataset.key = key;

                albumEl.innerHTML = `

                    <span class="album-title">${albumData.album}</span>

                    <span class="album-artist">${albumData.artist}</span>

                `;

                albumEl.addEventListener('click', () => selectAlbum(key));

                albumsListDiv.appendChild(albumEl);

            });

            

            // Auto-select the first album if none is active

            if (!activeAlbumKey && albumKeys.length > 0) {

                selectAlbum(albumKeys[0]);

            } else if (activeAlbumKey && library[activeAlbumKey]) {

                // Re-select active album if it still exists

                selectAlbum(activeAlbumKey);

            }

        }


        function selectAlbum(key) {

            if (activeAlbumKey) {

                document.querySelector(`.album-item[data-key="${activeAlbumKey}"]`)?.classList.remove('active');

            }


            activeAlbumKey = key;

            currentAlbumTracks = library[key].tracks.sort((a, b) => {

                // Sort by track number if available, otherwise by title

                const trackA = parseInt(a.tags.track) || Infinity;

                const trackB = parseInt(b.tags.track) || Infinity;

                if (trackA !== Infinity && trackB !== Infinity) {

                    return trackA - trackB;

                }

                return a.tags.title.localeCompare(b.tags.title);

            });


            const albumEl = document.querySelector(`.album-item[data-key="${key}"]`);

            if (albumEl) {

                albumEl.classList.add('active');

            }


            currentAlbumTitle.textContent = `${library[key].album} by ${library[key].artist}`;

            renderTrackList();

        }


        function renderTrackList() {

            tracksViewDiv.innerHTML = '';

            

            currentAlbumTracks.forEach((track, index) => {

                const trackEl = document.createElement('div');

                trackEl.className = 'track-item';

                trackEl.dataset.index = index;

                trackEl.innerHTML = `

                    <span class="track-number">${track.tags.track || (index + 1)}</span>

                    <span class="track-details">${track.tags.title}</span>

                `;

                trackEl.addEventListener('click', () => playTrack(index));

                tracksViewDiv.appendChild(trackEl);

            });

            

            highlightCurrentTrack();

        }



        /**

         * 3. Playback Logic

         */

         

        function playTrack(index) {

            if (index < 0 || index >= currentAlbumTracks.length) return;

            

            currentTrackIndex = index;

            const track = currentAlbumTracks[currentTrackIndex];


            // Set the audio source and play

            audio.src = track.url;

            audio.play();


            // Update UI

            currentTitleSpan.textContent = track.tags.title;

            currentArtistSpan.textContent = track.tags.artist;

            playPauseBtn.textContent = '⏸';

            highlightCurrentTrack();

        }

        

        // Function called from the "Play Album" button

        function playAllTracks() {

            if (currentAlbumTracks.length > 0) {

                 playTrack(0);

            }

        }


        function togglePlayPause() {

            if (audio.paused) {

                if (currentTrackIndex === -1 && currentAlbumTracks.length > 0) {

                    // Start from the beginning if nothing is selected

                    playTrack(0);

                } else {

                    audio.play();

                    playPauseBtn.textContent = '⏸';

                }

            } else {

                audio.pause();

                playPauseBtn.textContent = '▶';

            }

        }


        function playNext() {

            const nextIndex = currentTrackIndex + 1;

            if (nextIndex < currentAlbumTracks.length) {

                playTrack(nextIndex);

            } else {

                // Stop or loop back to the beginning (for simple play functionality)

                audio.pause();

                audio.src = currentAlbumTracks[0].url; // Reset to the first track's source

                currentTrackIndex = -1;

                playPauseBtn.textContent = '▶';

                highlightCurrentTrack();

            }

        }


        function playPrevious() {

            const prevIndex = currentTrackIndex - 1;

            if (prevIndex >= 0) {

                playTrack(prevIndex);

            } else {

                // Stay on the first track

                audio.currentTime = 0;

            }

        }

        

        function highlightCurrentTrack() {

            document.querySelectorAll('.track-item').forEach(el => {

                el.classList.remove('playing');

            });

            

            const currentTrackEl = document.querySelector(`.track-item[data-index="${currentTrackIndex}"]`);

            if (currentTrackEl) {

                currentTrackEl.classList.add('playing');

                // Scroll into view if off-screen

                currentTrackEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });

            }

        }



        /**

         * 4. Event Listeners & Player Setup

         */


        playPauseBtn.addEventListener('click', togglePlayPause);

        prevBtn.addEventListener('click', playPrevious);

        nextBtn.addEventListener('click', playNext);

        volumeSlider.addEventListener('input', (e) => {

            audio.volume = e.target.value;

        });


        // Event for when a song finishes

        audio.addEventListener('ended', playNext);


        // Update UI when audio is paused/playing from external events (like hitting the space bar)

        audio.addEventListener('play', () => {

            playPauseBtn.textContent = '⏸';

        });


        audio.addEventListener('pause', () => {

            playPauseBtn.textContent = '▶';

        });


        // Set initial volume

        audio.volume = volumeSlider.value;

        

    </script>


</body>

</html>