| <!DOCTYPE html> |
| <html lang="pt-BR"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Web Radio Player</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap'); |
| |
| body { |
| font-family: 'Poppins', sans-serif; |
| background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); |
| color: #fff; |
| min-height: 100vh; |
| } |
| |
| .radio-container { |
| background: rgba(255, 255, 255, 0.05); |
| backdrop-filter: blur(10px); |
| border-radius: 20px; |
| box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2); |
| overflow: hidden; |
| } |
| |
| .station-card { |
| transition: all 0.3s ease; |
| cursor: pointer; |
| background: rgba(255, 255, 255, 0.03); |
| border: 1px solid rgba(255, 255, 255, 0.1); |
| } |
| |
| .station-card:hover { |
| transform: translateY(-5px); |
| background: rgba(255, 255, 255, 0.1); |
| border-color: rgba(255, 255, 255, 0.3); |
| } |
| |
| .station-card.active { |
| background: rgba(59, 130, 246, 0.2); |
| border-color: rgba(59, 130, 246, 0.5); |
| } |
| |
| .progress-bar { |
| height: 4px; |
| background: rgba(255, 255, 255, 0.1); |
| } |
| |
| .progress { |
| height: 100%; |
| background: linear-gradient(90deg, #3b82f6 0%, #8b5cf6 100%); |
| width: 0%; |
| transition: width 0.1s linear; |
| } |
| |
| .volume-slider::-webkit-slider-thumb { |
| -webkit-appearance: none; |
| width: 15px; |
| height: 15px; |
| border-radius: 50%; |
| background: #fff; |
| cursor: pointer; |
| } |
| |
| .now-playing-artwork { |
| animation: rotate 20s linear infinite; |
| animation-play-state: paused; |
| } |
| |
| .now-playing-artwork.playing { |
| animation-play-state: running; |
| } |
| |
| @keyframes rotate { |
| from { transform: rotate(0deg); } |
| to { transform: rotate(360deg); } |
| } |
| |
| .equalizer { |
| display: flex; |
| align-items: flex-end; |
| height: 30px; |
| gap: 3px; |
| } |
| |
| .equalizer-bar { |
| width: 4px; |
| background: #3b82f6; |
| border-radius: 2px; |
| animation: equalize 1.5s infinite ease-in-out; |
| } |
| |
| .equalizer-bar:nth-child(1) { animation-delay: 0.1s; height: 60%; } |
| .equalizer-bar:nth-child(2) { animation-delay: 0.3s; height: 30%; } |
| .equalizer-bar:nth-child(3) { animation-delay: 0.5s; height: 75%; } |
| .equalizer-bar:nth-child(4) { animation-delay: 0.2s; height: 40%; } |
| .equalizer-bar:nth-child(5) { animation-delay: 0.4s; height: 65%; } |
| |
| @keyframes equalize { |
| 0%, 100% { transform: scaleY(1); } |
| 50% { transform: scaleY(1.5); } |
| } |
| </style> |
| </head> |
| <body class="flex items-center justify-center p-4"> |
| <div class="radio-container w-full max-w-4xl"> |
| |
| <div class="p-6 bg-gradient-to-r from-blue-600 to-purple-600 flex justify-between items-center"> |
| <div> |
| <h1 class="text-2xl font-bold">Web Radio Player</h1> |
| <p class="text-sm opacity-80">Escolha sua estação favorita</p> |
| </div> |
| <div class="flex items-center space-x-2"> |
| <div class="equalizer hidden" id="equalizer"> |
| <div class="equalizer-bar"></div> |
| <div class="equalizer-bar"></div> |
| <div class="equalizer-bar"></div> |
| <div class="equalizer-bar"></div> |
| <div class="equalizer-bar"></div> |
| </div> |
| <span class="text-sm" id="current-time">00:00</span> |
| </div> |
| </div> |
| |
| |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-0"> |
| |
| <div class="p-4 md:col-span-1 bg-gray-900 bg-opacity-50 overflow-y-auto max-h-[500px]"> |
| <div class="mb-4 relative"> |
| <input type="text" placeholder="Buscar rádio..." class="w-full bg-gray-800 bg-opacity-50 border border-gray-700 rounded-lg px-4 py-2 text-white focus:outline-none focus:ring-2 focus:ring-blue-500"> |
| <i class="fas fa-search absolute right-3 top-3 text-gray-400"></i> |
| </div> |
| |
| <h3 class="font-medium text-gray-300 mb-3">Categorias</h3> |
| <div class="flex flex-wrap gap-2 mb-4"> |
| <span class="px-3 py-1 bg-blue-600 bg-opacity-30 rounded-full text-xs cursor-pointer hover:bg-opacity-50">Todas</span> |
| <span class="px-3 py-1 bg-gray-700 rounded-full text-xs cursor-pointer hover:bg-gray-600">Pop</span> |
| <span class="px-3 py-1 bg-gray-700 rounded-full text-xs cursor-pointer hover:bg-gray-600">Rock</span> |
| <span class="px-3 py-1 bg-gray-700 rounded-full text-xs cursor-pointer hover:bg-gray-600">Eletrônica</span> |
| <span class="px-3 py-1 bg-gray-700 rounded-full text-xs cursor-pointer hover:bg-gray-600">Jazz</span> |
| </div> |
| |
| <h3 class="font-medium text-gray-300 mb-3">Estações</h3> |
| <div class="space-y-2" id="station-list"> |
| |
| </div> |
| </div> |
| |
| |
| <div class="p-6 md:col-span-2 flex flex-col bg-gray-900 bg-opacity-30"> |
| <div class="flex flex-col md:flex-row items-center mb-6"> |
| <div class="relative mb-4 md:mb-0 md:mr-6"> |
| <div class="now-playing-artwork w-40 h-40 rounded-full overflow-hidden border-4 border-white border-opacity-10"> |
| <img src="https://source.unsplash.com/random/400x400/?music" alt="Album Art" class="w-full h-full object-cover" id="station-artwork"> |
| </div> |
| <div class="absolute -bottom-2 -right-2 bg-blue-600 rounded-full p-2 shadow-lg"> |
| <i class="fas fa-music text-white"></i> |
| </div> |
| </div> |
| |
| <div class="text-center md:text-left"> |
| <h2 class="text-xl font-bold" id="station-name">Selecione uma estação</h2> |
| <p class="text-gray-300 mb-2" id="station-genre">Gênero</p> |
| <p class="text-sm text-gray-400" id="current-song">Nenhuma música tocando</p> |
| </div> |
| </div> |
| |
| |
| <div class="progress-bar mb-4 rounded-full"> |
| <div class="progress rounded-full" id="progress-bar"></div> |
| </div> |
| |
| |
| <div class="flex justify-between items-center mb-6"> |
| <button class="text-gray-400 hover:text-white" id="btn-shuffle"> |
| <i class="fas fa-random"></i> |
| </button> |
| |
| <button class="text-gray-400 hover:text-white" id="btn-prev"> |
| <i class="fas fa-step-backward text-2xl"></i> |
| </button> |
| |
| <button class="bg-blue-600 hover:bg-blue-700 rounded-full w-12 h-12 flex items-center justify-center text-white" id="btn-play"> |
| <i class="fas fa-play"></i> |
| </button> |
| |
| <button class="text-gray-400 hover:text-white" id="btn-next"> |
| <i class="fas fa-step-forward text-2xl"></i> |
| </button> |
| |
| <button class="text-gray-400 hover:text-white" id="btn-repeat"> |
| <i class="fas fa-redo"></i> |
| </button> |
| </div> |
| |
| |
| <div class="flex items-center"> |
| <i class="fas fa-volume-down text-gray-400 mr-2"></i> |
| <input type="range" min="0" max="100" value="70" class="volume-slider w-full h-1 bg-gray-700 rounded-lg appearance-none cursor-pointer" id="volume-control"> |
| <i class="fas fa-volume-up text-gray-400 ml-2"></i> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="p-4 bg-gray-900 bg-opacity-50 text-center text-sm text-gray-400"> |
| Web Radio Player © 2025 - Todos os direitos reservados |
| </div> |
| </div> |
|
|
| <script> |
| |
| const stations = [ |
| { |
| id: 1, |
| name: "Radio Pop Hits", |
| genre: "Pop", |
| url: "https://stream.laut.fm/pophits", |
| image: "https://source.unsplash.com/random/300x300/?pop,music", |
| currentSong: "Dua Lipa - Don't Start Now" |
| }, |
| { |
| id: 2, |
| name: "Rock FM", |
| genre: "Rock", |
| url: "https://stream.laut.fm/rockfm", |
| image: "https://source.unsplash.com/random/300x300/?rock,music", |
| currentSong: "Foo Fighters - Everlong" |
| }, |
| { |
| id: 3, |
| name: "Jazz Lounge", |
| genre: "Jazz", |
| url: "https://stream.laut.fm/jazzlounge", |
| image: "https://source.unsplash.com/random/300x300/?jazz,music", |
| currentSong: "Miles Davis - So What" |
| }, |
| { |
| id: 4, |
| name: "EDM Festival", |
| genre: "Eletrônica", |
| url: "https://stream.laut.fm/edmfestival", |
| image: "https://source.unsplash.com/random/300x300/?electronic,music", |
| currentSong: "Swedish House Mafia - Don't You Worry Child" |
| }, |
| { |
| id: 5, |
| name: "Classical Radio", |
| genre: "Clássica", |
| url: "https://stream.laut.fm/classicalradio", |
| image: "https://source.unsplash.com/random/300x300/?classical,music", |
| currentSong: "Beethoven - Symphony No. 5" |
| }, |
| { |
| id: 6, |
| name: "Hip Hop Nation", |
| genre: "Hip Hop", |
| url: "https://stream.laut.fm/hiphopnation", |
| image: "https://source.unsplash.com/random/300x300/?hiphop,music", |
| currentSong: "Kendrick Lamar - HUMBLE." |
| } |
| ]; |
| |
| |
| const stationList = document.getElementById('station-list'); |
| const stationName = document.getElementById('station-name'); |
| const stationGenre = document.getElementById('station-genre'); |
| const currentSong = document.getElementById('current-song'); |
| const stationArtwork = document.getElementById('station-artwork'); |
| const btnPlay = document.getElementById('btn-play'); |
| const btnPrev = document.getElementById('btn-prev'); |
| const btnNext = document.getElementById('btn-next'); |
| const btnShuffle = document.getElementById('btn-shuffle'); |
| const btnRepeat = document.getElementById('btn-repeat'); |
| const volumeControl = document.getElementById('volume-control'); |
| const progressBar = document.getElementById('progress-bar'); |
| const equalizer = document.getElementById('equalizer'); |
| const currentTime = document.getElementById('current-time'); |
| |
| |
| const audio = new Audio(); |
| let isPlaying = false; |
| let currentStationIndex = 0; |
| let progressInterval; |
| let updateTimeInterval; |
| |
| |
| function init() { |
| renderStations(); |
| setupEventListeners(); |
| } |
| |
| |
| function renderStations() { |
| stationList.innerHTML = ''; |
| stations.forEach((station, index) => { |
| const stationElement = document.createElement('div'); |
| stationElement.className = 'station-card p-3 rounded-lg'; |
| stationElement.innerHTML = ` |
| <div class="flex items-center"> |
| <img src="${station.image}" alt="${station.name}" class="w-12 h-12 rounded-lg object-cover mr-3"> |
| <div> |
| <h4 class="font-medium">${station.name}</h4> |
| <p class="text-xs text-gray-400">${station.genre}</p> |
| </div> |
| </div> |
| `; |
| stationElement.addEventListener('click', () => selectStation(index)); |
| stationList.appendChild(stationElement); |
| }); |
| } |
| |
| |
| function selectStation(index) { |
| |
| const stationCards = document.querySelectorAll('.station-card'); |
| stationCards.forEach(card => card.classList.remove('active')); |
| stationCards[index].classList.add('active'); |
| |
| |
| currentStationIndex = index; |
| const station = stations[index]; |
| |
| |
| stationName.textContent = station.name; |
| stationGenre.textContent = station.genre; |
| currentSong.textContent = station.currentSong; |
| stationArtwork.src = station.image; |
| |
| |
| audio.src = station.url; |
| playStation(); |
| } |
| |
| |
| function playStation() { |
| audio.play() |
| .then(() => { |
| isPlaying = true; |
| btnPlay.innerHTML = '<i class="fas fa-pause"></i>'; |
| document.querySelector('.now-playing-artwork').classList.add('playing'); |
| equalizer.classList.remove('hidden'); |
| |
| |
| startProgressUpdate(); |
| startTimeUpdate(); |
| }) |
| .catch(error => { |
| console.error('Error playing audio:', error); |
| alert('Erro ao reproduzir a estação. Verifique sua conexão ou tente outra estação.'); |
| }); |
| } |
| |
| |
| function pauseStation() { |
| audio.pause(); |
| isPlaying = false; |
| btnPlay.innerHTML = '<i class="fas fa-play"></i>'; |
| document.querySelector('.now-playing-artwork').classList.remove('playing'); |
| equalizer.classList.add('hidden'); |
| |
| |
| stopProgressUpdate(); |
| stopTimeUpdate(); |
| } |
| |
| |
| function togglePlayPause() { |
| if (isPlaying) { |
| pauseStation(); |
| } else { |
| if (audio.src) { |
| playStation(); |
| } else { |
| |
| selectStation(0); |
| } |
| } |
| } |
| |
| |
| function playNext() { |
| const nextIndex = (currentStationIndex + 1) % stations.length; |
| selectStation(nextIndex); |
| } |
| |
| |
| function playPrev() { |
| const prevIndex = (currentStationIndex - 1 + stations.length) % stations.length; |
| selectStation(prevIndex); |
| } |
| |
| |
| function startProgressUpdate() { |
| |
| let progress = 0; |
| progressInterval = setInterval(() => { |
| progress = (progress + 0.5) % 100; |
| progressBar.style.width = `${progress}%`; |
| }, 1000); |
| } |
| |
| function stopProgressUpdate() { |
| clearInterval(progressInterval); |
| } |
| |
| |
| function startTimeUpdate() { |
| let seconds = 0; |
| updateTimeInterval = setInterval(() => { |
| seconds++; |
| const mins = Math.floor(seconds / 60); |
| const secs = seconds % 60; |
| currentTime.textContent = `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; |
| }, 1000); |
| } |
| |
| function stopTimeUpdate() { |
| clearInterval(updateTimeInterval); |
| } |
| |
| |
| function setupEventListeners() { |
| btnPlay.addEventListener('click', togglePlayPause); |
| btnNext.addEventListener('click', playNext); |
| btnPrev.addEventListener('click', playPrev); |
| |
| |
| volumeControl.addEventListener('input', () => { |
| audio.volume = volumeControl.value / 100; |
| }); |
| |
| |
| btnShuffle.addEventListener('click', () => { |
| btnShuffle.classList.toggle('text-blue-400'); |
| }); |
| |
| btnRepeat.addEventListener('click', () => { |
| btnRepeat.classList.toggle('text-blue-400'); |
| }); |
| |
| |
| selectStation(0); |
| } |
| |
| |
| document.addEventListener('DOMContentLoaded', init); |
| </script> |
| </body> |
| </html> |