Edzelf / ESP32-Radio

Internet radio based on ESP32, VS1053 and a TFT screen.
GNU General Public License v3.0
964 stars 227 forks source link

Online radio database #434

Open xP373Rx opened 3 years ago

xP373Rx commented 3 years ago

Here is a little radio database. By providing your local ip as ipaddr will make you able to start stream directly from the page by clicking the radio name. http://gsgrid.net/st1.php

example http://gsgrid.net/st1.php?ipaddr=192.168.0.167

where 192.168.0.167 is the local esp32 radio ip address.

It is based on the open source radio database https://de1.api.radio-browser.info


<?php header('Access-Control-Allow-Origin: *'); ?>
<!DOCTYPE HTML>
<html>
<head>

<style>
table {
    border-width: 1px;
    border-style:solid;
    border-color: #000000;
    background-color: #222222;
    margin: 0px;
    padding: 3px;
    /*background-image: url(metal.jpg);*/
    background-attachment: fixed;
    background-repeat: repeat;
    color: #222222;
    border-radius: 15px;
    opacity: 0.9;
}
td { 
border: thin solid #000000;
padding: 4px;
margin: 2px;
color: #cccccc; 
background-color:#111111;
opacity: 0.9;

font-size:13px;
/*background-image: url(td.jpg);*/
background-attachment: fixed;
background-repeat: repeat;
border-radius: 10px;
}

a{
    color: #eeeeee;
    font-style: normal;
    text-decoration: none;
    font-weight: bold;
    font-size:12px;

}

.invistd { 
color: #eeeeee;
}
    body {
    background-color: #000000;

    background-size: auto;
    color: #cccccc;
    font-size:13px;
    font-family:Snell Roundhand, cursive;

}
input{
    background-color: #000000;
    color: #cccccc; 
    border-color: #222222;
    border-width: 1px;
    border-style:solid;

    padding: 3px;
}
select{
    background-color: #000000;
    color: #cccccc; 
    border-color: #222222;
    border-width: 1px;
    border-style:solid;
    padding: 3px;
}
textarea{
    background-color: #000000;
    color: #cccccc; 
    border-color: #222222;
    border-width: 1px;
    border-style:solid;
    padding: 3px;
}
  td 
  {
    font-family:Arial bold; 
    font-size: 18px;
    font-weight: 900;

    vertical-align: middle;
  }
  .tdlink
  {
    font-family:Arial bold; 
    font-size: 12px;
    font-weight: 900;
    text-align: center; 
    vertical-align: middle;
  }
  table 
  {
    font-family:Arial bold; 
    font-size: 18px;
    font-weight: 900;

    vertical-align: middle;
  }
  a 
  {
    font-family:Arial bold; 
    font-size: 12px;
    font-weight: 900;
    text-transform: uppercase;
    text-align: center; 
  }
  p {color:blue;}
</style>

<script>
var addr = "<?echo $_GET["ipaddr"]?>";
//addr="192.168.0.167";
   function httpGet ( theReq )
   {
    var theUrl = "http://"+addr+"/?" + theReq + "&version=" + Math.random() ;
   //console.log(theUrl);
    var xhr = new XMLHttpRequest() ;
    xhr.onreadystatechange = function() {
      if ( xhr.readyState == XMLHttpRequest.DONE )
      {
        resultstr.value = xhr.responseText ;
      }
    }
    xhr.open ( "GET", theUrl ) ;
    xhr.send() ;
   }

   function handlepreset ( presctrl )
   {
     httpGet ( "preset=" + presctrl.value ) ;
   }

   function handletone ( tonectrl )
   {
     var theUrl = "http://"+addr+"/?" + tonectrl.id + "=" + tonectrl.value +
                  "&version=" + Math.random() ;
     var xhr = new XMLHttpRequest() ;
     xhr.onreadystatechange = function() {
       if ( xhr.readyState == XMLHttpRequest.DONE )
       {
         resultstr.value = xhr.responseText ;
       }
     }
     xhr.open ( "GET", theUrl ) ;
     xhr.send() ;
   }

   function setstat(ip,statctrl)
   {
     var theUrl = "http://"+addr+"/?station=" + ip + "&version=" + Math.random() ;
     //console.log(theUrl);
     var xhr = new XMLHttpRequest() ;

     boxesEL = document.getElementsByName("playbutton");
      for(var x=0; x < boxesEL.length; x++)
      {

        boxesEL[x].style.backgroundColor = "#333333";
        boxesEL[x].style.color = "#AAAAAA";
        boxesEL[x].style.fontWeight = 900;
        boxesEL[x].style.width = "450px";
      }
     statctrl.style.backgroundColor = "#00AA00";
     xhr.onreadystatechange = function() {
       if ( xhr.readyState == XMLHttpRequest.DONE )
       {
         resultstr.value = xhr.responseText ;
       }
     }
     xhr.open ( "GET", theUrl, true ) ;
     xhr.send() ;
   }

   function selectItemByValue(elmnt, value)
   {
     var sel = document.getElementById(elmnt) ;
     for(var i=0; i < sel.options.length; i++)
     {
       if(sel.options[i].value == value)
         sel.selectedIndex = i;
     }
   }
   function sendClick(url,uuid)
   {
        var theUrl = "http://de1.api.radio-browser.info/json/url/"+uuid ;
        var xhr = new XMLHttpRequest() ;
        xhr.onreadystatechange = function() 
        {
            if ( xhr.readyState == XMLHttpRequest.DONE )
            {
                //console.log(xhr.responseText)  ;
            }
        }
    xhr.open ( "GET", theUrl ) ;
    xhr.send() ;
   }

   // Request current status.
   function myRefresh()
   {
    httpGet ('status') ;
    setTimeout(myRefresh,5000) ;
   }

</script>

</head>
<body>

<center>
<table ><tr>
<td><button class="button" onclick="httpGet('downvolume=2')">VOL-</button></td>
<td><button class="button" onclick="httpGet('upvolume=2')">VOL+</button></td>
<td><button class="button" onclick="httpGet('mute')">(un)MUTE</button></td>
<td><button class="button" onclick="httpGet('stop')">(un)STOP</button></td>
<td><button class="button" onclick="httpGet('resume')">Resume</button></td>

<td><select class="select" onchange="httpGet('sleep='+this.value)" id="sleep">
        <option value="1">1 min</option>
        <option value="30">30 min</option>
        <option value="60">60 min</option>
        <option value="90">90 min</option>
        <option value="120">120 min</option>
        <option value="0" selected="">Off</option>
    </select></td>
</tr></table><br>
<table style="width:500px">
    <tr>
     <td><center>
      <label for="HA"><big>Treble Gain:</big></label>
      <br>
      <select class="select" onChange="handletone(this)" id="toneha">
       <option value="8">-12 dB</option>
       <option value="9">-10.5 dB</option>
       <option value="10">-9 dB</option>
       <option value="11">-7.5 dB</option>
       <option value="12">-6 dB</option>
       <option value="13">-4.5 dB</option>
       <option value="14">-3 dB</option>
       <option value="15">-1.5 dB</option>
       <option value="0" selected>Off</option>
       <option value="1">+1.5 dB</option>
       <option value="2">+3 dB</option>
       <option value="3">+4.5 dB</option>
       <option value="4">+6 dB</option>
       <option value="5">+7.5 dB</option>
       <option value="6">+9 dB</option>
       <option value="7">+10.5 dB</option>
      </select>

     </center></td>
     <td><center>
      <label for="HF"><big>Treble Freq:</big></label>
      <br>
      <select class="select" onChange="handletone(this)" id="tonehf">
        <option value="1">1 kHz</option>
        <option value="2">2 kHz</option>
        <option value="3">3 kHz</option>
        <option value="4">4 kHz</option>
        <option value="5">5 kHz</option>
        <option value="6">6 kHz</option>
        <option value="7">7 kHz</option>
        <option value="8">8 kHz</option>
        <option value="9">9 kHz</option>
        <option value="10">10 kHz</option>
        <option value="11">11 kHz</option>
        <option value="12">12 kHz</option>
        <option value="13">13 kHz</option>
        <option value="14">14 kHz</option>
        <option value="15">15 kHz</option>
      </select>

     </center></td>

     <td><center>
      <label for="LA"><big>Bass Gain:</big></label>
      <br>
      <select class="select" onChange="handletone(this)" id="tonela">
       <option value="0" selected>Off</option>
       <option value="1">+1 dB</option>
       <option value="2">+2 dB</option>
       <option value="3">+3 dB</option>
       <option value="4">+4 dB</option>
       <option value="5">+5 dB</option>
       <option value="6">+6 dB</option>
       <option value="7">+7 dB</option>
       <option value="8">+8 dB</option>
       <option value="9">+9 dB</option>
       <option value="10">+10 dB</option>
       <option value="11">+11 dB</option>
       <option value="12">+12 dB</option>
       <option value="13">+13 dB</option>
       <option value="14">+14 dB</option>
       <option value="15">+15 dB</option>
      </select>

     </center></td>
     <td><center>
      <label for="LF"><big>Bass Freq:</big></label>
      <br>
      <select class="select" onChange="handletone(this)" id="tonelf">
       <option value="2">10 Hz</option>
       <option value="3">20 Hz</option>
       <option value="4">30 Hz</option>
       <option value="5">40 Hz</option>
       <option value="6">50 Hz</option>
       <option value="7">60 Hz</option>
       <option value="8">70 Hz</option>
       <option value="9">80 Hz</option>
       <option value="10">90 Hz</option>
       <option value="11">100 Hz</option>
       <option value="12">110 Hz</option>
       <option value="13">120 Hz</option>
       <option value="14">130 Hz</option>
       <option value="15">140 Hz</option>
      </select> 
     </center></td>
    </tr>
   </table>
   <br>

<table width="450" class="tdlink">
<tr>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=60s" >60s</a></td>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=70s" >70s</a></td>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=80s" >80s</a></td>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=90s" >90s</a></td>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=metal" >Metal</a></td>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=rock" >Rock</a></td></tr><tr>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=pop" >Pop</a></td>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=disco" >Disco</a></td>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=trance" >Trance</a></td>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=techno" >Techno</a></td>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=house" >House</a></td>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=dance" >Dance</a></td></tr><tr>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=reggae" >Reggae</a></td>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=funk" >Funk</a></td>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=top" >Top</a></td>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=synth" >Synth</a></td></tr><tr>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=fantasy" >Fantasy</a></td>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=nature" >Rain Nature</a></td>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=ambient" >Ambient</a></td>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=meditation" >Meditation</a></td>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=lounge" >Lounge</a></td>
<td><a href="?<?echo "ipaddr=".$_GET["ipaddr"]?>&genre=chill" >Chill</a></td>

<tr>
</table><br>

<!--<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)"> <img src="t1p.jpg" draggable="true" ondragstart="drag(event)" id="drag1" width="88" height="31"></div> <div id="div2" ondrop="drop(event)" ondragover="allowDrop(event)"></div>-->

<?
function isMobile() {
  return preg_match("/(android|avantgo|blackberry|bolt|boost|cricket|docomo|fone|hiptop|mini|mobi|palm|phone|pie|tablet|up\.browser|up\.link|webos|wos)/i", $_SERVER["HTTP_USER_AGENT"]);
}
echo $_GET["ipaddr"];
$mobile=false;
if(isMobile())
  $mobile=true;
function get_string_between($string, $start, $end){
  $string = ' ' . $string;
  $ini = strpos($string, $start);
  if ($ini == 0) return '';
  $ini += strlen($start);
  $len = strpos($string, $end, $ini) - $ini;
  return substr($string, $ini, $len);
}

$search = $_GET["search"];
$data;
$keyword=$_GET["genre"];
if(strlen($keyword) < 3 && strlen($search) < 3)
  $keyword="top";
echo"<table width=''>";
$page = 1;
$preset =1;
$lasturl;
$options = array(
    'http'=>array(
      'method'=>"GET",
      'header'=>"Accept-language: en\r\n" .
                "Cookie: foo=bar\r\n" .  // check function.stream-context-create on php.net
                "User-Agent: Esp32Radio PHP station browser\r\n" // i.e. An iPad 
    )
  );

$context = stream_context_create($options);
$jsn = file_get_contents('https://de1.api.radio-browser.info/json/stations/bytag/'.$keyword.'?order=clickcount&reverse=true',false, $context);

$stat = json_decode($jsn);
//var_dump($stat) ;

foreach($stat as $st)
{
   //if($lasturl!=$st->name)
  // {
       // echo $st->name."<br>";
       $rep = array("https://","http://");
        echo '<tr><td><button name="playbutton" onclick="setstat(\''.str_replace($rep,"",$st->url_resolved).'\',this);sendClick(\''.$st->url.'\',\''.$st->stationuuid.'\'); ">'.$st->name.'</button></td>';
        echo "<td>".$st->codec."</td>";
        echo "<td>".$st->bitrate."</td>";
        echo "<td>".$st->clickcount."</td>";

       // $lasturl=$st->name;

   //}

}

echo "</table>"
?>

<script>
boxesEL = document.getElementsByName("playbutton");
      for(var x=0; x < boxesEL.length; x++) 
      {

        boxesEL[x].style.backgroundColor = "#333333";
        boxesEL[x].style.color = "#AAAAAA";
        boxesEL[x].style.fontWeight = 900;
        boxesEL[x].style.width = "450px";
      }
</script>

</center>
</body>
</html>
´´´
Tibertrix commented 3 years ago

nice ! I'm wondering now ... how difficult is it to make an android app that asks for the ip address and then opens the page?

Edzelf commented 3 years ago

Yes, great! I will add a page like that to the webinterface.

xP373Rx commented 3 years ago

Just open the page on your phone :D

xP373Rx commented 3 years ago

I intergrated this page via iframe in to the esps webinterface and forwarded the esp´s ip as arg.

Esp source needs to be changed to send the ip with the argument list. in getradiostatus() add String ( "\nipaddr=" ) + //send the IP String(WiFi.localIP().toString()); Here is my index_html.h Iframe has been added play station button removed and onload function changed to forward ip. This will also play https streams which are highlighted as light gray when you click the name


// index.html file in raw data format for PROGMEM
//
#define index_html_version 180102
const char index_html[] PROGMEM = R"=====(
<!DOCTYPE html>
<html>
 <head>
  <title>ESP32-radio</title>
  <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
  <link rel="stylesheet" type="text/css" href="radio.css">
  <link rel="Shortcut Icon" type="image/ico" href="favicon.ico">
 </head>
 <body>
  <ul>
   <li><a class="pull-left" href="#">ESP32 Radio</a></li>
   <li><a class="pull-left active" href="/index.html">Control</a></li>
   <li><a class="pull-left" href="/config.html">Config</a></li>
   <li><a class="pull-left" href="/mp3play.html">MP3 player</a></li>
   <li><a class="pull-left" href="/about.html">About</a></li>
  </ul>
  <br><br>
  <center>
   <h1>** ESP32 Radio **</h1>

   <button class="button" onclick="httpGet('downvolume=2')">VOL-</button>
   <button class="button" onclick="httpGet('upvolume=2')">VOL+</button>
   <button class="button" onclick="httpGet('mute')">(un)MUTE</button>
   <button class="button" onclick="httpGet('stop')">(un)STOP</button>
     <button class="button" onclick="httpGet('blmod=1')">BT ON</button>
   <button class="button" onclick="httpGet('blmod=0')">BT OFF</button>
    <select class="select selectb" onChange="httpGet('sleep='+this.value)" id="sleep">
        <option value="1">1 min</option>
        <option value="30">30 min</option>
        <option value="60">60 min</option>
        <option value="90">90 min</option>
        <option value="120">120 min</option>
        <option value="0" selected>SLEEP OFF</option>
    </select><br>
    <table style="width:500px">
    <tr>
     <td colspan="2"><center>
       <select class="select selectw" onChange="handlepreset(this)" id="preset">
        <option value="-1">Select a preset here</option>
       </select>

     </center></td>
    </tr></table>
        <table style="width:500px">
    <tr>
     <td><center>
      <label for="HA"><big>Treble Gain:</big></label>
      <br>
      <select class="select" onChange="handletone(this)" id="toneha">
       <option value="8">-12 dB</option>
       <option value="9">-10.5 dB</option>
       <option value="10">-9 dB</option>
       <option value="11">-7.5 dB</option>
       <option value="12">-6 dB</option>
       <option value="13">-4.5 dB</option>
       <option value="14">-3 dB</option>
       <option value="15">-1.5 dB</option>
       <option value="0" selected>Off</option>
       <option value="1">+1.5 dB</option>
       <option value="2">+3 dB</option>
       <option value="3">+4.5 dB</option>
       <option value="4">+6 dB</option>
       <option value="5">+7.5 dB</option>
       <option value="6">+9 dB</option>
       <option value="7">+10.5 dB</option>
      </select>

     </center></td>
     <td><center>
      <label for="HF"><big>Treble Freq:</big></label>
      <br>
      <select class="select" onChange="handletone(this)" id="tonehf">
        <option value="1">1 kHz</option>
        <option value="2">2 kHz</option>
        <option value="3">3 kHz</option>
        <option value="4">4 kHz</option>
        <option value="5">5 kHz</option>
        <option value="6">6 kHz</option>
        <option value="7">7 kHz</option>
        <option value="8">8 kHz</option>
        <option value="9">9 kHz</option>
        <option value="10">10 kHz</option>
        <option value="11">11 kHz</option>
        <option value="12">12 kHz</option>
        <option value="13">13 kHz</option>
        <option value="14">14 kHz</option>
        <option value="15">15 kHz</option>
      </select>

     </center></td>

     <td><center>
      <label for="LA"><big>Bass Gain:</big></label>
      <br>
      <select class="select" onChange="handletone(this)" id="tonela">
       <option value="0" selected>Off</option>
       <option value="1">+1 dB</option>
       <option value="2">+2 dB</option>
       <option value="3">+3 dB</option>
       <option value="4">+4 dB</option>
       <option value="5">+5 dB</option>
       <option value="6">+6 dB</option>
       <option value="7">+7 dB</option>
       <option value="8">+8 dB</option>
       <option value="9">+9 dB</option>
       <option value="10">+10 dB</option>
       <option value="11">+11 dB</option>
       <option value="12">+12 dB</option>
       <option value="13">+13 dB</option>
       <option value="14">+14 dB</option>
       <option value="15">+15 dB</option>
      </select>

     </center></td>
     <td><center>
      <label for="LF"><big>Bass Freq:</big></label>
      <br>
      <select class="select" onChange="handletone(this)" id="tonelf">
       <option value="2">10 Hz</option>
       <option value="3">20 Hz</option>
       <option value="4">30 Hz</option>
       <option value="5">40 Hz</option>
       <option value="6">50 Hz</option>
       <option value="7">60 Hz</option>
       <option value="8">70 Hz</option>
       <option value="9">80 Hz</option>
       <option value="10">90 Hz</option>
       <option value="11">100 Hz</option>
       <option value="12">110 Hz</option>
       <option value="13">120 Hz</option>
       <option value="14">130 Hz</option>
       <option value="15">140 Hz</option>
      </select> 
     </center></td>
    </tr>
   </table>
   <br>
   <input type="text" width="600px" size="72" id="resultstr" placeholder="Waiting for a command....">
   <br><br>
     <iframe id="ipaddr" src="http://gsgrid.net/st1.php?" title="Online Radios" width="1000" height="5000"></iframe>
   <br>
   <p>Find new radio stations at <a target="blank" href="http://www.internet-radio.com">http://www.internet-radio.com</a></p>
   <p>Examples: us1.internet-radio.com:8105, skonto.ls.lv:8002/mp3, 85.17.121.103:8800</p><br>
  </center>
  <script>
    var radiourl = "http://gsgrid.net/st1.php?ipaddr=";
    var ipaddr  ="192.168.4.1";
   function httpGet ( theReq )
   {
    var theUrl = "/?" + theReq + "&version=" + Math.random() ;
    var xhr = new XMLHttpRequest() ;
    xhr.onreadystatechange = function() {
      if ( xhr.readyState == XMLHttpRequest.DONE )
      {
        resultstr.value = xhr.responseText ;
      }
    }
    xhr.open ( "GET", theUrl ) ;
    xhr.send() ;
   }

   function handlepreset ( presctrl )
   {
     httpGet ( "preset=" + presctrl.value ) ;
   }

   function handletone ( tonectrl )
   {
     var theUrl = "/?" + tonectrl.id + "=" + tonectrl.value +
                  "&version=" + Math.random() ;
     var xhr = new XMLHttpRequest() ;
     xhr.onreadystatechange = function() {
       if ( xhr.readyState == XMLHttpRequest.DONE )
       {
         resultstr.value = xhr.responseText ;
       }
     }
     xhr.open ( "GET", theUrl, false ) ;
     xhr.send() ;
   }

   function setstat()
   {
     var theUrl = "/?station=" + station.value + "&version=" + Math.random() ;
     var xhr = new XMLHttpRequest() ;
     xhr.onreadystatechange = function() {
       if ( xhr.readyState == XMLHttpRequest.DONE )
       {
         resultstr.value = xhr.responseText ;
       }
     }
     xhr.open ( "GET", theUrl, false ) ;
     xhr.send() ;
   }

   function selectItemByValue(elmnt, value)
   {
     var sel = document.getElementById(elmnt) ;
     for(var i=0; i < sel.options.length; i++)
     {
       if(sel.options[i].value == value)
         sel.selectedIndex = i;
     }
   }

   // Request current status.
   function myRefresh()
   {
    httpGet ('status') ;
    setTimeout(myRefresh,5000) ;
   }

   // Fill preset list initially
   //
   function onloadfunc()
   {
     var i, sel, opt, lines, parts ;
     var theUrl = "/?settings" + "&version=" + Math.random() ;
     var xhr = new XMLHttpRequest() ;
     xhr.onreadystatechange = function() {
       if ( xhr.readyState == XMLHttpRequest.DONE )
       {
         lines = xhr.responseText.split ( "\n" ) ;
         for ( i = 0 ; i < ( lines.length-1 ) ; i++ )
         {
           sel = document.getElementById ( "preset" ) ;
           parts = lines[i].split ( "=" ) ;
           if ( parts[0].indexOf ( "preset_" ) == 0 && parts[0].indexOf ( "preset_0" ) == -1)
           {
             opt = document.createElement ( "OPTION" ) ;
             opt.value = parts[0].substring ( 7 ) ;
             opt.text = parts[1] ;
             sel.add( opt ) ;
           }
           if ( ( parts[0].indexOf ( "tone" ) == 0 ) || ( parts[0] == "preset" )||( parts[0] == "ipaddr" ) )                     

           {
            if( parts[0] == "ipaddr" )
                        {
                            var iframe = document.getElementById('ipaddr');
                            iframe.src = radiourl+parts[1];
                        }
                        else
                            selectItemByValue ( parts[0], parts[1] ) ;
           }
         }
         setTimeout(myRefresh,5000) ;
       }
     }
     xhr.open ( "GET", theUrl, false ) ;
     xhr.send() ;
   }

   window.onload = onloadfunc ;   // Run after page has been loaded

 </script>
 </body>
</html>
<noscript>
  <link rel="stylesheet" href="radio.css">
  Sorry, ESP-radio does not work without JavaScript!
</noscript>
)=====" ;

´´´
Edzelf commented 3 years ago

I have added a "search" page to the web interface to explore stations that are available in the "Community Radio Browser", see https://www.radio-browser.info

xP373Rx commented 3 years ago

Noice works like a charm.

Rainer-G commented 3 years ago

Excellent idea and fast implementation, 4 days from opening the issue until it found the way into Ed's code! Many thanks to xP373Rx and Ed! Compiled and worked from the start, this radio becomes more and more as versatile as a Swiss Army Knife! Rainer

xP373Rx commented 3 years ago

That thing is my sleeping pill right now .. i got some rain tracks and other stuff and implemented a sleep function. Also working on adding stations directly from the list through a store button or popup menu with slot numbers.

utw3v0 commented 3 years ago

Really NICE work guys !!! Do you think it's possible putting this on the Nextion Screen so you don't need a web interface or app ?

xP373Rx commented 2 years ago

I fixed it on the web server .. but if you are using the search code on the esp you need to change it before compile.

xP373Rx commented 2 years ago

BTW new browsers seem to block access from external pages to local network. I had to disable something in chrome to get it to play on click again.. i moved my whole code to the esp.

Here is the whole content of my index_html.h file

There is some button for bluetooth module and sleep time which yo u could remove and i changed the colors to dark theme.

// index.html file in raw data format for PROGMEM
//
#define index_html_version 180102
const char index_html[] PROGMEM = R"=====(
<!DOCTYPE html>
<html>
 <head>
  <title>ESP32-radio</title>
  <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
  <link rel="stylesheet" type="text/css" href="radio.css">
  <link rel="Shortcut Icon" type="image/ico" href="favicon.ico">
 </head>
 <body>
  <ul>
   <li><a class="pull-left" href="#">ESP32 Radio</a></li>
   <li><a class="pull-left active" href="/index.html">Control</a></li>
   <li><a class="pull-left" href="/search.html">Search</a></li>
   <li><a class="pull-left" href="/config.html">Config</a></li>
   <li><a class="pull-left" href="/mp3play.html">MP3 player</a></li>
   <li><a class="pull-left" href="/about.html">About</a></li>
  </ul>
  <br><br>
  <center>
   <h1>** ESP32 Radio **</h1>

   <button class="button" onclick="httpGet('downvolume=2')">VOL-</button>
   <button class="button" onclick="httpGet('upvolume=2')">VOL+</button>
   <button class="button" onclick="httpGet('mute')">(un)MUTE</button>
   <button class="button" onclick="httpGet('stop')">(un)STOP</button>
     <button class="button" onclick="httpGet('blmod=1')">BT ON</button>
   <button class="button" onclick="httpGet('blmod=0')">BT OFF</button>
    <select class="select selectb" onChange="httpGet('sleep='+this.value)" id="sleep">
        <option value="1">1 min</option>
        <option value="30">30 min</option>
        <option value="60">60 min</option>
        <option value="90">90 min</option>
        <option value="120">120 min</option>
        <option value="0" selected>SLEEP OFF</option>
    </select><br>
    <table style="width:700px">
    <tr>
     <td colspan="2"><center>
       <select class="select selectw" onChange="handlepreset(this)" id="preset">
        <option value="-1">Select a preset here</option>
       </select>

     </center></td>
    </tr></table>
        <table style="width:700px">
    <tr>
     <td><center>
      <label for="HA"><big>Treble Gain:</big></label>
      <br>
      <select class="select" onChange="handletone(this)" id="toneha">
       <option value="8">-12 dB</option>
       <option value="9">-10.5 dB</option>
       <option value="10">-9 dB</option>
       <option value="11">-7.5 dB</option>
       <option value="12">-6 dB</option>
       <option value="13">-4.5 dB</option>
       <option value="14">-3 dB</option>
       <option value="15">-1.5 dB</option>
       <option value="0" selected>Off</option>
       <option value="1">+1.5 dB</option>
       <option value="2">+3 dB</option>
       <option value="3">+4.5 dB</option>
       <option value="4">+6 dB</option>
       <option value="5">+7.5 dB</option>
       <option value="6">+9 dB</option>
       <option value="7">+10.5 dB</option>
      </select>

     </center></td>
     <td><center>
      <label for="HF"><big>Treble Freq:</big></label>
      <br>
      <select class="select" onChange="handletone(this)" id="tonehf">
        <option value="1">1 kHz</option>
        <option value="2">2 kHz</option>
        <option value="3">3 kHz</option>
        <option value="4">4 kHz</option>
        <option value="5">5 kHz</option>
        <option value="6">6 kHz</option>
        <option value="7">7 kHz</option>
        <option value="8">8 kHz</option>
        <option value="9">9 kHz</option>
        <option value="10">10 kHz</option>
        <option value="11">11 kHz</option>
        <option value="12">12 kHz</option>
        <option value="13">13 kHz</option>
        <option value="14">14 kHz</option>
        <option value="15">15 kHz</option>
      </select>

     </center></td>

     <td><center>
      <label for="LA"><big>Bass Gain:</big></label>
      <br>
      <select class="select" onChange="handletone(this)" id="tonela">
       <option value="0" selected>Off</option>
       <option value="1">+1 dB</option>
       <option value="2">+2 dB</option>
       <option value="3">+3 dB</option>
       <option value="4">+4 dB</option>
       <option value="5">+5 dB</option>
       <option value="6">+6 dB</option>
       <option value="7">+7 dB</option>
       <option value="8">+8 dB</option>
       <option value="9">+9 dB</option>
       <option value="10">+10 dB</option>
       <option value="11">+11 dB</option>
       <option value="12">+12 dB</option>
       <option value="13">+13 dB</option>
       <option value="14">+14 dB</option>
       <option value="15">+15 dB</option>
      </select>

     </center></td>
     <td><center>
      <label for="LF"><big>Bass Freq:</big></label>
      <br>
      <select class="select" onChange="handletone(this)" id="tonelf">
       <option value="2">10 Hz</option>
       <option value="3">20 Hz</option>
       <option value="4">30 Hz</option>
       <option value="5">40 Hz</option>
       <option value="6">50 Hz</option>
       <option value="7">60 Hz</option>
       <option value="8">70 Hz</option>
       <option value="9">80 Hz</option>
       <option value="10">90 Hz</option>
       <option value="11">100 Hz</option>
       <option value="12">110 Hz</option>
       <option value="13">120 Hz</option>
       <option value="14">130 Hz</option>
       <option value="15">140 Hz</option>
      </select> 
     </center></td>
    </tr>
   </table>
   <br>
   <input type="text" width="700" size="80" id="resultstr" placeholder="Waiting for a command....">
   <br><br><table width="700" class="pull-left">
   <tr>
   <td><button class="button" onclick="listStats('60s')">60s</button></td>
   <td><button class="button" onclick="listStats('70s')">70s</button></td>
   <td><button class="button" onclick="listStats('80s')">80s</button></td>
   <td><button class="button" onclick="listStats('90s')">90s</button></td>
   <td><button class="button" onclick="listStats('metal')">Metal</button></td>
   <td><button class="button" onclick="listStats('rock')">Rock</button></td>
   <td><button class="button" onclick="listStats('punk')">Punk</button></td></tr><tr>

   <td><button class="button" onclick="listStats('trance')">Trance</button></td>
   <td><button class="button" onclick="listStats('techno')">Techno</button></td>
   <td><button class="button" onclick="listStats('house')">House</button></td>
   <td><button class="button" onclick="listStats('dance')">Dance</button></td>
   <td><button class="button" onclick="listStats('minimal')">Minimal</button></td>
   <td><button class="button" onclick="listStats('synth')">Synth</button></td>
   <td><button class="button" onclick="listStats('edm')">EDM</button></td></tr><tr>

   <td><button class="button" onclick="listStats('hiphop')">HipHop</button></td>
   <td><button class="button" onclick="listStats('reggae')">Reggae</button></td>
   <td><button class="button" onclick="listStats('ska')">Ska</button></td>
     <td><button class="button" onclick="listStats('funk')">Funk</button></td>
   <td><button class="button" onclick="listStats('soul')">Soul</button></td>
   <td><button class="button" onclick="listStats('blues')">Blues</button></td>
   <td><button class="button" onclick="listStats('jazz')">Jazz</button></td></tr><tr>

   <td><button class="button" onclick="listStats('top40')">Top40</button></td>
   <td><button class="button" onclick="listStats('pop')">Pop</button></td>
   <td><button class="button" onclick="listStats('disco')">Disco</button></td>
     <td><button class="button" onclick="listStats('classical')">classical</button></td>
   <td><button class="button" onclick="listStats('relax')">Relax</button></td>
   <td><button class="button" onclick="listStats('ocean')">Ocean</button></td>
     <td><button class="button" onclick="listStats('nature')">nature</button></td></tr><tr>

     <td><button class="button" onclick="listStats('ambient')">Ambient</button></td>
     <td><button class="button" onclick="listStats('lounge')">Lounge</button></td>
     <td><button class="button" onclick="listStats('chill')">Chill</button></td>
     <td><button class="button" onclick="listStats('fantasy')">Fantasy</button></td>
     <td><button class="button" onclick="listStats('folk')">Folk</button></td>
     <td><button class="button" onclick="listStats('celtic')">Celtic</button></td>
     <td><button class="button" onclick="listStats('meditation')">Meditation</button></td></tr>
   </table>
   <br><input type="text" size="80" id="resultstr1" placeholder="" width="700"><br><br>
   <table class="table2" id="stationsTable" width="700">
   </table>

  </center>
  <script>
var stationArr ;
var boxesEL;
 // Get info from a radiobrowser.  Working servers are:
 // https://de1.api.radio-browser.info, https://fr1.api.radio-browser.info, https://nl1.api.radio-browser.info
 function listStats ( genre )
 {
  var theUrl = "https://nl1.api.radio-browser.info/json/stations/bytag/"+genre+"?hidebroken=true&order=clickcount&reverse=true" ;
  var xhr = new XMLHttpRequest() ;
  xhr.onreadystatechange = function() 
  {
   if ( this.readyState == XMLHttpRequest.DONE )
   {
    var table = document.getElementById('stationsTable') ;
    table.innerHTML = "" ;
    stationArr = JSON.parse ( this.responseText ) ;
    var i ;
    var snam ;
    var oldsnam = "" ;
    for ( i = 0 ; i < stationArr.length ; i++ )
    {
      snam = stationArr[i].name ;
      if ( (stationArr[i].url_resolved.startsWith ( "http:") ||  stationArr[i].url_resolved.startsWith ( "https:")) &&            // https: not supported yet
           snam != oldsnam )
      {
        var row = table.insertRow() ;
        var cell1 = row.insertCell(0) ;
        var cell2 = row.insertCell(1) ;
                var cell3 = row.insertCell(2) ;
        var cell4 = row.insertCell(3) ;
                cell1.setAttribute("name", "playbutton");
        cell1.innerHTML = snam ;
        cell2.innerHTML = stationArr[i].codec ;
                cell3.innerHTML = stationArr[i].bitrate ;
                cell4.innerHTML = stationArr[i].clickcount ;
        cell1.setAttribute ( 'onclick', 'setStation(' + i + ',this);' ) ;
        oldsnam = snam ;
      }
    }
   }
  }
  xhr.open ( "GET", theUrl ) ;
  xhr.send() ;
    boxesEL = document.getElementsByName("playbutton");
      for(var x=0; x < boxesEL.length; x++) 
      {

        boxesEL[x].style.backgroundColor = "#111111";
        //boxesEL[x].style.color = "#AAAAAA";
        //boxesEL[x].style.fontWeight = 900;
        //boxesEL[x].style.width = "450px";
      }
 }

 function setStation ( inx ,statctrl)
 {
  var table = document.getElementById('stationsTable') ;
  var snam ;
  var theUrl ;

  snam = stationArr[inx].url_resolved ;
  if(snam.startsWith ( "http:"))
        snam = snam.replace ('http:\/\/','') ;
    else{
        snam = snam.replace ('https:\/\/','') ;
}
  theUrl = "/?station=" + snam + "&version=" + Math.random() ;
  //table.rows[inx].cells[0].style.backgroundColor = "#333333" 
  var xhr = new XMLHttpRequest() ;
  xhr.onreadystatechange = function() 
  {
   if ( this.readyState == XMLHttpRequest.DONE )
   {
     resultstr1.value = xhr.responseText ;

      for(var x=0; x < boxesEL.length; x++)
      {

        boxesEL[x].style.backgroundColor = "#111111";
        //boxesEL[x].style.color = "#AAAAAA";
        //boxesEL[x].style.fontWeight = 900;
        //boxesEL[x].style.width = "450px";
      }
if(stationArr[inx].url_resolved.startsWith ( "https:"))
     statctrl.style.backgroundColor = "#555555";
else
         statctrl.style.backgroundColor = "#000000";
   }
  }
  xhr.open ( "GET", theUrl ) ;
  xhr.send() ;
 }
   function httpGet ( theReq )
   {
    var theUrl = "/?" + theReq + "&version=" + Math.random() ;
    var xhr = new XMLHttpRequest() ;
    xhr.onreadystatechange = function() {
      if ( xhr.readyState == XMLHttpRequest.DONE )
      {
        resultstr.value = xhr.responseText ;
      }
    }
    xhr.open ( "GET", theUrl ) ;
    xhr.send() ;
   }

   function handlepreset ( presctrl )
   {
     httpGet ( "preset=" + presctrl.value ) ;
   }

   function handletone ( tonectrl )
   {
     var theUrl = "/?" + tonectrl.id + "=" + tonectrl.value +
                  "&version=" + Math.random() ;
     var xhr = new XMLHttpRequest() ;
     xhr.onreadystatechange = function() {
       if ( xhr.readyState == XMLHttpRequest.DONE )
       {
         resultstr.value = xhr.responseText ;
       }
     }
     xhr.open ( "GET", theUrl ) ;
     xhr.send() ;
   }

   function setstat()
   {
     var theUrl = "/?station=" + station.value + "&version=" + Math.random() ;
     var xhr = new XMLHttpRequest() ;
     xhr.onreadystatechange = function() {
       if ( xhr.readyState == XMLHttpRequest.DONE )
       {
         resultstr.value = xhr.responseText ;
       }
     }
     xhr.open ( "GET", theUrl ) ;
     xhr.send() ;
   }

   function selectItemByValue(elmnt, value)
   {
     var sel = document.getElementById(elmnt) ;
     for(var i=0; i < sel.options.length; i++)
     {
       if(sel.options[i].value == value)
         sel.selectedIndex = i;
     }
   }

   // Request current status.
   function myRefresh()
   {
    httpGet ('status') ;
    setTimeout(myRefresh,5000) ;
   }

   // Fill preset list initially
   //
   function onloadfunc()
   {
     var i, sel, opt, lines, parts ;
     var theUrl = "/?settings" + "&version=" + Math.random() ;
     var xhr = new XMLHttpRequest() ;
     xhr.onreadystatechange = function() {
       if ( xhr.readyState == XMLHttpRequest.DONE )
       {
         lines = xhr.responseText.split ( "\n" ) ;
         for ( i = 0 ; i < ( lines.length-1 ) ; i++ )
         {
           sel = document.getElementById ( "preset" ) ;
           parts = lines[i].split ( "=" ) ;
           if ( parts[0].indexOf ( "preset_" ) == 0 && parts[0].indexOf ( "preset_0" ) == -1)
           {
             opt = document.createElement ( "OPTION" ) ;
             opt.value = parts[0].substring ( 7 ) ;
             opt.text = parts[1] ;
             sel.add( opt ) ;
           }
           if ( ( parts[0].indexOf ( "tone" ) == 0 ) || ( parts[0] == "preset" ))                    

           {
                            selectItemByValue ( parts[0], parts[1] ) ;
           }
         }
         setTimeout(myRefresh,5000) ;
       }
     }
     xhr.open ( "GET", theUrl ) ;
     xhr.send() ;
   }

   window.onload = onloadfunc ;   // Run after page has been loaded

 </script>
 </body>
</html>
<noscript>
  <link rel="stylesheet" href="radio.css">
  Sorry, ESP-radio does not work without JavaScript!
</noscript>
)=====" ;