diff --git a/intermedio/ejercicios/68-reproductor-playlist/resoluciones/maria-montepeque/index.html b/intermedio/ejercicios/68-reproductor-playlist/resoluciones/maria-montepeque/index.html new file mode 100644 index 0000000..c5654c5 --- /dev/null +++ b/intermedio/ejercicios/68-reproductor-playlist/resoluciones/maria-montepeque/index.html @@ -0,0 +1,23 @@ + + + + + + Playlist interactiva + + + +
+

Cargando...

+

+

Favorito: no

+
+ + + + +
+
+ + + diff --git a/intermedio/ejercicios/68-reproductor-playlist/resoluciones/maria-montepeque/maria-montepeque.js b/intermedio/ejercicios/68-reproductor-playlist/resoluciones/maria-montepeque/maria-montepeque.js new file mode 100644 index 0000000..183efdb --- /dev/null +++ b/intermedio/ejercicios/68-reproductor-playlist/resoluciones/maria-montepeque/maria-montepeque.js @@ -0,0 +1,145 @@ +// 68. Playlist interactiva - Resolucion Maria Montepeque + +const canciones = [ + { id: 1, titulo: 'Ignition', artista: 'Nova' }, + { id: 2, titulo: 'Firestorm', artista: 'Blaze' }, + { id: 3, titulo: 'Slow Burn', artista: 'Ember' }, + { id: 4, titulo: 'Wildfire', artista: 'Nova' }, + { id: 5, titulo: 'Fade Out', artista: 'Echo' } +]; + +function esCancionValida(cancion) { + return ( + cancion && + typeof cancion.id !== 'undefined' && + typeof cancion.titulo === 'string' && + cancion.titulo.trim() !== '' && + typeof cancion.artista === 'string' && + cancion.artista.trim() !== '' + ); +} + +function esListaValida(listaCanciones) { + return Array.isArray(listaCanciones) && listaCanciones.length > 0 && listaCanciones.every(esCancionValida); +} + +function crearReproductor(cancionesBase) { + if (!esListaValida(cancionesBase)) { + return { valido: false, motivo: 'lista de canciones invalida o vacia' }; + } + return { valido: true, canciones: cancionesBase, indiceActual: 0, favoritos: [] }; +} + +function obtenerCancionActual(estado) { + return estado.canciones[estado.indiceActual]; +} + +function siguiente(estado) { + return { ...estado, indiceActual: (estado.indiceActual + 1) % estado.canciones.length }; +} + +function anterior(estado) { + return { ...estado, indiceActual: (estado.indiceActual - 1 + estado.canciones.length) % estado.canciones.length }; +} + +function alternarFavorito(estado, cancionId) { + const yaFavorito = estado.favoritos.includes(cancionId); + const favoritos = yaFavorito ? estado.favoritos.filter((id) => id !== cancionId) : [...estado.favoritos, cancionId]; + return { ...estado, favoritos }; +} + +function reproducirAleatoria(estado, generadorAleatorio = Math.random) { + if (estado.canciones.length <= 1) return estado; + + let nuevoIndice; + do { + nuevoIndice = Math.floor(generadorAleatorio() * estado.canciones.length); + } while (nuevoIndice === estado.indiceActual); + + return { ...estado, indiceActual: nuevoIndice }; +} + +function crearGeneradorSecuencial(valores) { + let indice = 0; + return () => { + const valor = valores[indice % valores.length]; + indice += 1; + return valor; + }; +} + +function renderizarReproductor(estado) { + if (typeof document === 'undefined') return; + const cancionActual = obtenerCancionActual(estado); + const elTitulo = document.querySelector('#reproductor-titulo'); + const elArtista = document.querySelector('#reproductor-artista'); + const elFavorito = document.querySelector('#reproductor-favorito'); + if (!elTitulo) return; + + elTitulo.textContent = cancionActual.titulo; + if (elArtista) elArtista.textContent = cancionActual.artista; + if (elFavorito) elFavorito.textContent = estado.favoritos.includes(cancionActual.id) ? 'Favorito: si' : 'Favorito: no'; +} + +function inicializarReproductor(cancionesBase) { + if (typeof document === 'undefined') return; + + let estado = crearReproductor(cancionesBase); + if (!estado.valido) return; + renderizarReproductor(estado); + + const botonSiguiente = document.querySelector('#boton-siguiente'); + const botonAnterior = document.querySelector('#boton-anterior'); + const botonFavorito = document.querySelector('#boton-favorito'); + const botonShuffle = document.querySelector('#boton-shuffle'); + + if (botonSiguiente) { + botonSiguiente.addEventListener('click', () => { + estado = siguiente(estado); + renderizarReproductor(estado); + }); + } + + if (botonAnterior) { + botonAnterior.addEventListener('click', () => { + estado = anterior(estado); + renderizarReproductor(estado); + }); + } + + if (botonFavorito) { + botonFavorito.addEventListener('click', () => { + estado = alternarFavorito(estado, obtenerCancionActual(estado).id); + renderizarReproductor(estado); + }); + } + + if (botonShuffle) { + botonShuffle.addEventListener('click', () => { + estado = reproducirAleatoria(estado); + renderizarReproductor(estado); + }); + } +} + +console.log('Caso 1: siguiente x2, anterior x1, marcar favorito y wrap-around al llegar al final'); +let estado1 = crearReproductor(canciones); +estado1 = siguiente(estado1); +estado1 = siguiente(estado1); +estado1 = anterior(estado1); +estado1 = alternarFavorito(estado1, obtenerCancionActual(estado1).id); +console.log('Cancion actual tras siguiente/anterior:', JSON.stringify(obtenerCancionActual(estado1))); +console.log('Favoritos:', JSON.stringify(estado1.favoritos)); + +let estado1WrapAround = { ...estado1, indiceActual: canciones.length - 1 }; +estado1WrapAround = siguiente(estado1WrapAround); +console.log('Wrap-around al pasar la ultima cancion:', JSON.stringify(obtenerCancionActual(estado1WrapAround))); + +console.log('\nCaso 2: shuffle deterministico y lista invalida (vacia)'); +const generadorDeterminista = crearGeneradorSecuencial([0.9, 0.1]); +let estado2 = crearReproductor(canciones); +estado2 = reproducirAleatoria(estado2, generadorDeterminista); +console.log('Cancion tras shuffle:', JSON.stringify(obtenerCancionActual(estado2))); +console.log('Reproductor con lista vacia:', JSON.stringify(crearReproductor([]))); + +inicializarReproductor(canciones); diff --git a/intermedio/ejercicios/68-reproductor-playlist/resoluciones/maria-montepeque/style.css b/intermedio/ejercicios/68-reproductor-playlist/resoluciones/maria-montepeque/style.css new file mode 100644 index 0000000..4d205da --- /dev/null +++ b/intermedio/ejercicios/68-reproductor-playlist/resoluciones/maria-montepeque/style.css @@ -0,0 +1,44 @@ +body { + font-family: sans-serif; + background-color: #10131a; + color: #eef1f7; + display: flex; + justify-content: center; + padding: 3rem; +} + +.reproductor { + text-align: center; + width: min(360px, 100%); +} + +#reproductor-titulo { + margin-bottom: 0.25rem; +} + +#reproductor-artista { + color: #9fb4d4; + margin: 0 0 0.5rem; +} + +#reproductor-favorito { + color: #ff6b6b; + font-weight: bold; +} + +.botones { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + justify-content: center; + margin-top: 1rem; +} + +.botones button { + padding: 0.6rem 1rem; + border-radius: 4px; + border: none; + background-color: #4c9aff; + color: #10131a; + cursor: pointer; +}