Sobre el proyecto Radio Satélite

Quería contarles sobre el proyecto de Radio Satelite y compartir su código para que lo use quien guste.
Lo primero sería decir que estrictamente hablando no es una "radio". No hay broadcast ni streaming, no hay transmisión. Todo funciona mediante código HTML. Me llevó bastante tiempo armarlo con la ayuda de la IA y montones de pruebas infructuosas.
Yendo por partes. Lo primero fue armar un item de audio en archive.org. En mi caso subí más de 100 mp3 de 128 kbps entre canciones, lecturas, separador etc. El código reproduce los archivos de manera aleatoria.
Algunas características del HTML
• Botones de PLAY y PAUSE
• Al clickear PLAY el primer audio comienza ya empezado para dar una sensación de transmisión. Los siguientes se reproducen desde el inicio.
• Dentro del item, el archivo RadioSatelite.mp3 actúa de "separador de radio". Se reproduce 1 vez por cada 2 archivos.
• Memoria de los 3 últimos archivos, para evitar repeticiones seguidas. Igualmente cuantos más mp3 haya en el item, estadísticamente habrá menos repeticiones.
• Timeout de carga de 15 segundos. Si Archive tarda demasiado o hay un archivo roto, pasado los 15 segundos reproduce el siguiente.
• Nombre del track visible en el reproductor. Nombre del archivo sin el ".mp3".
• Debajo del reproductor tiene un guestbook con el branding oculto, suministrado por guestbook.me
Algunas restricciones
• Por si hace falta aclararlo. Al no ser una "transmisión única" (broadcast), cada dispositivo reproduce un audio distinto aunque lo inicien en el mismo momento.
• Intenté agregarle una linea de tiempo para poder tener una "programación de radio" (ej: Martes 21 hs - El show de Pirulo). No hubo forma de lograrlo sin romper el código.
• No todas las plataformas leen bien este código. Depende de las restricciones que tengan para editar el HTML, especialmente los script e iframe. Por ejemplo yo lo uso en Blogger sin problema. Pero en Wordpress suelen bloquear o filtrar JavaScript. En Bearblog ni hablar. Lo ideal es tener un hosting propio pero sino, blogger re va.
Comparto el HTML tal cual está en Radio Satelite, creo que está cerca del límite de sus posibilidades.
Si tenés ganas de jugar con esto y darle alguna mejora, escribime y lo pruebo!
Usalo, modificalo, compartilo ♡
🔥🔥
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Radio Satélite</title>
<style>
body{
color:#fff;
font-family:Arial, Helvetica, sans-serif;
text-align:center;
margin-top:60px;
}
button{
background:#111;
color:#fff;
border:1px solid #444;
padding:10px 20px;
margin:5px;
cursor:pointer;
}
button:hover{
background:#222;
}
#status{
margin-top:20px;
font-size:14px;
color:#aaa;
}
#track{
margin-top:10px;
font-size:14px;
}
#guestbook{
margin-top:60px;
display:flex;
flex-direction:column;
align-items:center;
}
#guestbookTitle{
font-size:14px;
color:#aaa;
margin-bottom:5px;
letter-spacing:2px;
}
#guestbook iframe{
width:100%;
max-width:420px;
height:420px;
border:0;
}
</style>
</head>
<body>
<button onclick="startRadio()">PLAY</button>
<button onclick="pauseRadio()">PAUSE</button>
<div id="status">radio detenida</div>
<div id="track"></div>
<audio id="player" preload="auto" crossorigin="anonymous"></audio>
<div id="guestbook">
<div id="guestbookTitle">
—— mensajes ——
</div>
<iframe
title="Guestbook"
src="https://guestbook.me/start.html?uid=62rzjxxy8wkj"
referrerpolicy="unsafe-url">
</iframe>
</div>
<div id="sbLinks">
<a href="https://guestbook.me">Guestbook Widget</a>
</div>
<script>
document.getElementById("sbLinks").style.display="none";
</script>
<script>
const item = "radio-satelite";
const separator = "RadioSatelite.mp3";
let songs = [];
let playlist = [];
let index = 0;
let songCount = 0;
let firstPlay = true;
let started = false;
let player = document.getElementById("player");
let timeoutCarga = null;
let retry = false;
// 👉 NUEVO: memoria simple de últimos temas
let lastPlayed = [];
// cargar lista
async function loadSongs(){
let url = "https://archive.org/metadata/" + item;
let response = await fetch(url);
let data = await response.json();
songs = data.files
.filter(f => f.name.endsWith(".mp3") && f.name !== separator)
.map(f => f.name);
shuffleSongs();
}
// mezclar
function shuffleSongs(){
playlist = [...songs];
for(let i = playlist.length - 1; i > 0; i--){
let j = Math.floor(Math.random() * (i + 1));
[playlist[i], playlist[j]] = [playlist[j], playlist[i]];
}
index = 0;
}
// siguiente
function nextSong(){
retry = false;
playFile();
}
function getNextValidSong(){
let attempts = 0;
let maxAttempts = 10;
while(attempts < maxAttempts){
if(index >= playlist.length) shuffleSongs();
let candidate = playlist[index];
if(!lastPlayed.includes(candidate)){
return candidate;
}
index++;
attempts++;
}
// si no encuentra (muy raro), devuelve igual
return playlist[index];
}
function playFile(){
let file;
let isSeparator = false;
if(firstPlay){
if(index >= playlist.length) shuffleSongs();
file = getNextValidSong();
songCount = 1;
}else{
if(songCount >= 2){
file = separator;
songCount = -1;
isSeparator = true;
}else{
file = getNextValidSong();
songCount++;
}
}
let url = "https://archive.org/download/" + item + "/" + file;
player.src = url;
if(firstPlay){
document.getElementById("status").innerText = "sintonizando...";
}
clearTimeout(timeoutCarga);
if(!isSeparator){
timeoutCarga = setTimeout(()=>{
if(!retry){
retry = true;
player.load();
}else{
index++;
playFile();
}
},15000); // ← 15 segundos
}
player.oncanplay = ()=>{
clearTimeout(timeoutCarga);
if(!isSeparator && firstPlay && isFinite(player.duration)){
let start = Math.random()*player.duration;
player.currentTime = start;
}
document.getElementById("status").innerText = "sonando:";
if(!isSeparator){
document.getElementById("track").innerText = file.replace(".mp3","");
}else{
document.getElementById("track").innerText = "Radio Satélite";
}
player.play().catch(()=>{
index++;
playFile();
});
if(firstPlay){
firstPlay = false;
index++;
}else{
if(!isSeparator){
index++;
}
}
// 👉 NUEVO: guardar últimos 3 temas
if(!isSeparator){
lastPlayed.push(file);
if(lastPlayed.length > 3){
lastPlayed.shift();
}
}
};
}
// errores
player.addEventListener("error", ()=>{
index++;
playFile();
});
player.addEventListener("stalled", ()=>{
playFile();
});
player.addEventListener("ended", nextSong);
// controles
function startRadio(){
if(!started){
started = true;
nextSong();
return;
}
if(player.paused){
player.play().catch(()=>nextSong());
document.getElementById("status").innerText = "sonando:";
}
}
function pauseRadio(){
player.pause();
document.getElementById("status").innerText = "pausado";
}
loadSongs();
</script>
</body>
</html>