import { state } from './state.js'; import { debounce, _, showNotification } from './utils.js'; import { fetchArtistGenresFromLastFM } from './api.js'; import { getFromDB, addItemsToStore } from './db.js'; let musicSection, genreGrid, artistGrid, songListView, searchInput, serverFilter, loadMoreBtn, backBtn, genreSearchInput; let isInitialized = false; let isClassifying = false; let currentPage = 0; const ARTISTS_PER_PAGE = 20; let currentMusicView = 'genres'; let selectedGenre = null; let genreScrollPosition = 0; export function initMusicView() { if (typeof chrome === 'undefined' || !chrome.storage) { console.warn('Running outside of Chrome extension, skipping initMusicView.'); return; } musicSection = document.getElementById('music-section'); if (!musicSection) return; if (state.musicPlayer && state.musicPlayer.isReady) { setupMusicView(); } else { const initialGrid = document.getElementById('music-section-genre-grid'); if (initialGrid) initialGrid.innerHTML = `

${_('loading')}

`; const checkReady = setInterval(() => { if (state.musicPlayer && state.musicPlayer.isReady) { clearInterval(checkReady); setupMusicView(); } }, 150); } } async function setupMusicView() { genreGrid = document.getElementById('music-section-genre-grid'); artistGrid = document.getElementById('music-section-artist-grid'); songListView = document.getElementById('music-section-song-list'); searchInput = document.getElementById('music-section-search-input'); serverFilter = document.getElementById('music-server-filter'); loadMoreBtn = document.getElementById('music-load-more-btn'); backBtn = document.getElementById('music-back-btn'); genreSearchInput = document.getElementById('genre-search-input'); if (!genreGrid || !artistGrid || !songListView || !searchInput || !serverFilter || !loadMoreBtn || !backBtn || !genreSearchInput) return; if (!isInitialized) { serverFilter.addEventListener('change', () => { currentPage = 0; renderArtists(); }); searchInput.addEventListener('input', debounce(() => { currentPage = 0; renderArtists(); }, 300)); genreSearchInput.addEventListener('input', debounce(() => { renderGenres(); }, 300)); loadMoreBtn.addEventListener('click', () => { currentPage++; renderArtists(true); }); backBtn.addEventListener('click', navigateBack); genreGrid.addEventListener('click', handleGenreClick); artistGrid.addEventListener('click', handleArtistClick); isInitialized = true; } if (isClassifying) { const overlay = document.getElementById('music-classification-overlay'); overlay.style.display = 'flex'; } else { await checkAndClassifyArtists(); } currentMusicView = 'genres'; selectedGenre = null; await renderMusicView(); } async function checkAndClassifyArtists() { if (isClassifying) { const overlay = document.getElementById('music-classification-overlay'); overlay.style.display = 'flex'; return; } const allServers = await getFromDB('artists'); let artistsToClassify = []; allServers.forEach(server => { if (server.titulos) { server.titulos.forEach(artist => { if (!artist.hasOwnProperty('genres')) { artistsToClassify.push(artist); } }); } }); if (artistsToClassify.length > 0) { isClassifying = true; const overlay = document.getElementById('music-classification-overlay'); overlay.style.display = 'flex'; const totalArtists = artistsToClassify.length; try { for (let i = 0; i < artistsToClassify.length; i += 5) { const batch = artistsToClassify.slice(i, i + 5); const promises = batch.map(artist => fetchArtistGenresFromLastFM(artist.title).then(genres => ({ artist, genres })) ); await Promise.all(promises.map(p => p.then(({ artist, genres }) => { artist.genres = genres.length > 0 ? genres : ['Varios']; }))); const processedCount = Math.min(i + batch.length, totalArtists); const percentage = Math.round((processedCount / totalArtists) * 100); const nextArtist = artistsToClassify[processedCount]; updateClassificationProgress(percentage, processedCount, totalArtists, nextArtist ? nextArtist.title : null); } await addItemsToStore('artists', allServers); await state.musicPlayer.loadMusicData(); showNotification('Clasificación de música completada.', 'success'); } catch (error) { showNotification('Ocurrió un error durante la clasificación de artistas.', 'error'); } finally { isClassifying = false; gsap.to(overlay, { autoAlpha: 0, duration: 0.5, onComplete: () => { overlay.style.display = 'none'; } }); } } } function updateClassificationProgress(percentage, processedCount, totalArtists, nextArtistTitle) { const progressFill = document.getElementById('classification-progress-fill'); const percentageText = document.getElementById('classification-percentage'); const progressDetails = document.getElementById('classification-progress-details'); const statusText = document.getElementById('classification-status-text'); if (progressFill) progressFill.style.width = `${percentage}%`; if (percentageText) percentageText.textContent = `${percentage}%`; if (progressDetails) progressDetails.textContent = `${processedCount} / ${totalArtists} artistas`; if (statusText) { if (nextArtistTitle) { statusText.textContent = `Analizando: ${nextArtistTitle}...`; } else { statusText.textContent = `Finalizando...`; } } } async function renderMusicView() { genreGrid.style.display = 'none'; artistGrid.style.display = 'none'; songListView.style.display = 'none'; serverFilter.style.display = 'none'; searchInput.parentElement.style.display = 'none'; genreSearchInput.parentElement.style.display = 'none'; backBtn.style.display = 'none'; loadMoreBtn.style.display = 'none'; switch (currentMusicView) { case 'genres': genreSearchInput.parentElement.style.display = 'flex'; await renderGenres(); break; case 'artists': backBtn.style.display = 'inline-flex'; serverFilter.style.display = 'block'; searchInput.parentElement.style.display = 'flex'; renderArtists(); break; case 'songs': break; } } async function renderGenres() { currentMusicView = 'genres'; genreGrid.style.display = 'grid'; genreGrid.innerHTML = ''; const allArtists = state.musicPlayer._generateFullArtistListForToken('all'); const genreSet = new Set(); allArtists.forEach(artist => { if (artist.genres && artist.genres.length > 0) { artist.genres.forEach(g => genreSet.add(g)); } }); let sortedGenres = Array.from(genreSet).sort((a, b) => a.localeCompare(b)); const filter = genreSearchInput.value.toLowerCase(); if (filter) { sortedGenres = sortedGenres.filter(genre => genre.toLowerCase().includes(filter)); } if (sortedGenres.length === 0) { genreGrid.innerHTML = `

${_('noArtistsFound')}

`; return; } const fragment = document.createDocumentFragment(); sortedGenres.forEach(genre => { const card = document.createElement('div'); card.className = 'genre-card'; card.textContent = genre; card.dataset.genre = genre; fragment.appendChild(card); }); genreGrid.appendChild(fragment); } function handleGenreClick(e) { const card = e.target.closest('.genre-card'); if (card && card.dataset.genre) { genreScrollPosition = window.scrollY; window.scrollTo(0, 0); selectedGenre = card.dataset.genre; currentMusicView = 'artists'; currentPage = 0; populateServerFilter(); renderMusicView(); } } async function navigateBack() { if (currentMusicView === 'songs') { currentMusicView = 'artists'; await renderMusicView(); } else if (currentMusicView === 'artists') { selectedGenre = null; currentMusicView = 'genres'; await renderMusicView(); requestAnimationFrame(() => { window.scrollTo(0, genreScrollPosition); }); } } function populateServerFilter() { if (!serverFilter || !state.musicPlayer) return; const currentVal = serverFilter.value; serverFilter.innerHTML = ``; state.musicPlayer.tokens.forEach(token => { const option = document.createElement('option'); option.value = token.value; option.textContent = token.name; serverFilter.appendChild(option); }); if (currentVal) serverFilter.value = currentVal; } function renderArtists(append = false) { if (!artistGrid || !state.musicPlayer) return; artistGrid.style.display = 'grid'; const musicPlayer = state.musicPlayer; const selectedServer = serverFilter.value; const filter = searchInput.value.toLowerCase(); let allArtists = musicPlayer._generateFullArtistListForToken(selectedServer); if (selectedGenre) { allArtists = allArtists.filter(artist => artist.genres && artist.genres.map(g => g.toLowerCase()).includes(selectedGenre.toLowerCase())); } const filteredArtists = filter ? allArtists.filter(artist => artist.title.toLowerCase().includes(filter)) : allArtists; if (!append) { artistGrid.innerHTML = ''; currentPage = 0; } if (filteredArtists.length === 0 && !append) { artistGrid.innerHTML = `

${_('noArtistsFound')}

`; loadMoreBtn.style.display = 'none'; return; } const startIndex = currentPage * ARTISTS_PER_PAGE; const endIndex = startIndex + ARTISTS_PER_PAGE; const artistsToRender = filteredArtists.slice(startIndex, endIndex); const fragment = document.createDocumentFragment(); artistsToRender.forEach(artist => { const card = createMusicCard(artist.title, artist.serverName, artist.thumb, artist.id, artist.isJellyfin, artist.token, artist.protocolo, artist.ip, artist.puerto); fragment.appendChild(card); }); artistGrid.appendChild(fragment); if (endIndex < filteredArtists.length) { loadMoreBtn.style.display = 'block'; } else { loadMoreBtn.style.display = 'none'; } if (musicPlayer) { musicPlayer.markCurrentArtist(); } } async function handleArtistClick(e) { const card = e.target.closest('.artist-card-spotify'); if (card && card.dataset.id) { currentMusicView = 'songs'; const artistId = card.dataset.id; const musicPlayer = state.musicPlayer; if (musicPlayer) { const fullList = musicPlayer._generateFullArtistListForToken(serverFilter.value); const artistData = fullList.find(a => a.id == artistId); if (artistData) { const songs = await musicPlayer.getArtistSongs(artistData); renderSongList(artistData, songs); } } } } async function renderSongList(artistData, songs) { artistGrid.style.display = 'none'; genreGrid.style.display = 'none'; searchInput.parentElement.style.display = 'none'; serverFilter.style.display = 'none'; loadMoreBtn.style.display = 'none'; backBtn.style.display = 'none'; songListView.style.display = 'block'; songListView.innerHTML = ''; let thumbUrl = 'img/no-profile.png'; if (artistData.thumb) { if (artistData.isJellyfin) { thumbUrl = artistData.thumb; } else { thumbUrl = `${artistData.protocolo}://${artistData.ip}:${artistData.puerto}${artistData.thumb}?X-Plex-Token=${artistData.token}`; } } const header = document.createElement('div'); header.className = 'song-list-header-spotify'; header.innerHTML = `
${_('artist')}

${artistData.title}

`; songListView.appendChild(header); const img = header.querySelector('.artist-header-thumb-spotify'); img.onload = () => { if(img.src.includes('no-profile.png')) return; const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0); const data = ctx.getImageData(0, 0, 1, 1).data; const color = `rgba(${data[0]}, ${data[1]}, ${data[2]}, 0.5)`; const darkColor = `rgba(${Math.round(data[0] * 0.3)}, ${Math.round(data[1] * 0.3)}, ${Math.round(data[2] * 0.3)}, 0.8)`; header.style.background = `linear-gradient(135deg, ${color} 0%, ${darkColor} 100%)`; }; const songListContainer = document.createElement('div'); songListContainer.className = 'song-list-container-spotify'; songListView.appendChild(songListContainer); songListContainer.addEventListener('click', e => { const row = e.target.closest('.song-grid-row'); if(row) { const songId = row.dataset.id; const originalIndex = songs.findIndex(s => s.id === songId); if(originalIndex !== -1) { state.musicPlayer.cancionesActuales = songs; state.musicPlayer.playSong(originalIndex); } } const albumPlayBtn = e.target.closest('.album-play-btn'); if(albumPlayBtn) { const albumTitle = albumPlayBtn.dataset.album; const albumSongs = songs.filter(s => s.album === albumTitle); if(albumSongs.length > 0) { state.musicPlayer.cancionesActuales = albumSongs; state.musicPlayer.playSong(0); } } }); if (songs.length === 0) { songListContainer.innerHTML = `

${_('noSongsFound')}

`; return; } const albums = songs.reduce((acc, song) => { const albumTitle = song.album || 'Unknown Album'; if (!acc[albumTitle]) { acc[albumTitle] = { cover: song.cover, songs: [] }; } acc[albumTitle].songs.push(song); return acc; }, {}); for (const albumTitle in albums) { const album = albums[albumTitle]; const albumContainer = document.createElement('div'); albumContainer.className = 'album-group-container-spotify'; albumContainer.innerHTML = `

${albumTitle}

${album.songs.length} ${_('tracks')}
#
${_('infoModalFieldTitle')}
`; const songsGrid = albumContainer.querySelector('.song-list-grid'); album.songs.forEach(song => { const songRow = document.createElement('div'); songRow.className = 'song-grid-row'; songRow.dataset.id = song.id; const originalIndex = songs.findIndex(s => s.id === song.id); songRow.innerHTML = `
${song.index || originalIndex + 1}
${song.titulo}
${song.duration ? state.musicPlayer.formatTime(song.duration / 1000) : '--:--'}
`; songsGrid.appendChild(songRow); }); songListContainer.appendChild(albumContainer); } document.getElementById('backToArtistsViewBtn').addEventListener('click', navigateBack); if (state.musicPlayer) { state.musicPlayer.markCurrentSong(); } } function createMusicCard(title, subtitle, imageUrl, artistId, isJellyfin, token, protocolo, ip, puerto) { const card = document.createElement('div'); card.className = 'artist-card-spotify'; card.dataset.id = artistId; let thumbUrl = 'img/no-profile.png'; if (imageUrl) { if (isJellyfin === 'true' || isJellyfin === true) { thumbUrl = imageUrl; } else { thumbUrl = `${protocolo}://${ip}:${puerto}${imageUrl}?X-Plex-Token=${token}`; } } card.innerHTML = `
${title}

${title}

${_('artist')}

`; return card; }