In an earlier post I showed how to display listeners to your Shoutcast audio streaming server by country, like this:

There are currently 12 out of 36 listeners from:
USA (3), Taiwan, Chile (2), Peru (2), France, Germany, Brazil,
Japan

But the program was a bit simplistic. If the listener’s hostname ended in “.de” they were assumed to be in Germany, “.jp” meant Japan, and so on. “.com” and “.edu” and “.org” were assumed to be in the United States, which was a pretty silly assumption, I do admit.

To really do things right, I’d have to look up each listener’s IP address in a huge database of IP ranges mapped to country, and I had no idea how to do that… until now.

These scripts use PHP, and assume you’re using a MySQL database. If you’re fairly technical, maybe you can pull it off too. A link to all the files mentioned in this article is here.

First, define some configuration parameters in a file named config.php:

<?php
    $dbhost = "localhost";
    $dbport = "3306";
    $dbuser = "root";
    $dbpassword = "your_sekrit_password";
    $dbname = "radio_db_name";
    $dbtable = "IP";
    $shoutcasthost = "your.shoutcast.server.com:1234";
    $shoutcastuser = "admin";
    $shoutcastpassword = "your_shoutcast_password";
?>

Make sure the table name isn’t the same as any existing table you might have already. And don’t put “http://” anywhere in the parameters.

To create a new table in your database to hold the IP-to-country mapping data, here’s create.php:

<?php
require("config.php");
$db = mysql_connect($dbhost, $dbuser, $dbpassword);
mysql_select_db($dbname, $db);
$query = "CREATE TABLE `$dbtable` (
  `id` int(11) NOT NULL auto_increment,
  `startip` bigint(20) unsigned NOT NULL,
  `endip` bigint(20) unsigned NOT NULL,
  `country` varchar(5) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `from` (`startip`,`endip`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;"
;
echo $query . "<br>\n";
$result = mysql_query($query, $db);
mysql_close($db);
?>

Run that once. It’ll display the SQL used to create the table. If you don’t see a boatload of error messages, rejoice.

Then it’s time to populate your new table with LOTS AND LOTS of data! Fortunately there is a wonderful group of volunteers at http://software77.net that have done all the work and provide this data as a public service. I am in awe. Create and run a file named update.php:

<?php
require("config.php");
$db = mysql_connect($dbhost, $dbuser, $dbpassword);
mysql_select_db($dbname, $db);
$query = "DELETE FROM $dbtable;";
echo $query . "<br>\n";
$result = mysql_query($query, $db);
$fp = fopen("compress.zlib://http://software77.net/geo-ip?DL=1", "r");
while (!feof($fp)) {
        $row = fgets($fp, 4096);
  if (left($row, 1) != ‘#’) {
          $data = explode("\"", $row);
    if ($data[9] != && $data[9] != ‘US’ && $data[9] != ‘ZZ’) {
            $query = "INSERT INTO $dbtable (startip, endip, country) VALUES (’$data[1]‘, ‘$data[3]‘, ‘$data[9]‘);";
            echo $query . "<br>\n";
            $result = mysql_query($query, $db);
            $num++;
    }
  }
}
mysql_close($db);
fclose($fp);
echo "$num records\n";
function left($string, $count){
    return substr($string, 0, $count);
}
?>

Once you get that script debugged, you only need to run it maybe once every few months to update the table with the latest data. It should display all the SQL statements used to update the data, and once again cross your fingers hoping for no error messages.

If you update too often, you’ll get banned from using the database, so only update the data once a day, at max. Please see http://software77.net/faq.html for details. This is more than reasonable.

And now comes the script to actually connect to your Shoutcast server, look up all your listeners, and list them out. First, make sure your Shoutcast server is configured to display listener addresses as IP addresses, not domain names. Then, try out listeners.php:

<?php

error_reporting(0);

header("Content-Type: text/html");

$countries = array (
"ac" => "Ascension Island",
"ad" => "Andorra",
"ae" => "United Arab Emirates",
"af" => "Afghanistan",
"ag" => "Antigua and Barbuda",
"ai" => "Anguilla",
"al" => "Albania",
"am" => "Armenia",
"an" => "Netherlands Antilles",
"ao" => "Angola",
"aq" => "Antarctica",
"ar" => "Argentina",
"as" => "American Samoa",
"at" => "Austria",
"au" => "Australia",
"aw" => "Aruba",
"ax" => "Åland",
"az" => "Azerbaijan",
"ba" => "Bosnia and Herzegovina",
"bb" => "Barbados",
"bd" => "Bangladesh",
"be" => "Belgium",
"bf" => "Burkina Faso",
"bg" => "Bulgaria",
"bh" => "Bahrain",
"bi" => "Burundi",
"bj" => "Benin",
"bm" => "Bermuda",
"bn" => "Brunei Darussalam",
"bo" => "Bolivia",
"br" => "Brazil",
"bs" => "Bahamas",
"bt" => "Bhutan",
"bw" => "Botswana",
"by" => "Belarus",
"bz" => "Belize",
"ca" => "Canada",
"cd" => "Democratic Republic of the Congo",
"cf" => "Central African Republic",
"cg" => "Republic of the Congo",
"ch" => "Switzerland",
"ci" => "Côte d’Ivoire",
"ck" => "Cook Islands",
"cl" => "Chile",
"cm" => "Cameroon",
"cn" => "People’s Republic of China",
"co" => "Colombia",
"cr" => "Costa Rica",
"cu" => "Cuba",
"cv" => "Cape Verde",
"cx" => "Christmas Island",
"cy" => "Cyprus",
"cz" => "Czech Republic",
"de" => "Germany",
"dj" => "Djibouti",
"dk" => "Denmark",
"dm" => "Dominica",
"do" => "Dominican Republic",
"dz" => "Algeria",
"ec" => "Ecuador",
"ee" => "Estonia",
"eg" => "Egypt",
"er" => "Eritrea",
"es" => "Spain",
"et" => "Ethiopia",
"fi" => "Finland",
"fj" => "Fiji",
"fk" => "Falkland Islands",
"fo" => "Faroe Islands",
"fr" => "France",
"ga" => "Gabon",
"gb" => "United Kingdom",
"gd" => "Grenada",
"ge" => "Georgia",
"gf" => "French Guiana",
"gg" => "Guernsey",
"gh" => "Ghana",
"gi" => "Gibraltar",
"gl" => "Greenland",
"gm" => "The Gambia",
"gn" => "Guinea",
"gp" => "Guadeloupe",
"gq" => "Equatorial Guinea",
"gr" => "Greece",
"gt" => "Guatemala",
"gu" => "Guam",
"gw" => "Guinea-Bissau",
"gy" => "Guyana",
"hk" => "Hong Kong",
"hn" => "Honduras",
"hr" => "Croatia",
"ht" => "Haiti",
"hu" => "Hungary",
"id" => "Indonesia",
"ie" => "Ireland",
"il" => "Israel",
"im" => "Isle of Man",
"in" => "Indi",
"iq" => "Iraq",
"ir" => "Iran",
"is" => "Iceland",
"it" => "Italy",
"je" => "Jersey",
"jm" => "Jamaica",
"jo" => "Jordan",
"jp" => "Japan",
"ke" => "Kenya",
"kg" => "Kyrgyzstan",
"kh" => "Cambodia",
"ki" => "Kiribati",
"km" => "Comoros",
"kr" => "South Korea",
"kw" => "Kuwait",
"ky" => "Cayman Islands",
"kz" => "Kazakhstan",
"la" => "Laos",
"lb" => "Lebanon",
"lc" => "Saint Lucia",
"li" => "Liechtenstein",
"lk" => "Sri Lanka",
"lr" => "Liberia",
"ls" => "Lesotho",
"lt" => "Lithuania",
"lu" => "Luxembourg",
"lv" => "Latvia",
"ly" => "Libya",
"ma" => "Morocco",
"mc" => "Monaco",
"md" => "Moldova",
"mg" => "Madagascar",
"mh" => "Marshall Islands",
"mk" => "Republic of Macedonia",
"ml" => "Mali",
"mm" => "Myanmar",
"mn" => "Mongolia",
"mp" => "Northern Mariana Islands",
"mq" => "Martinique",
"mr" => "Mauritania",
"ms" => "Montserrat",
"mt" => "Malta",
"mu" => "Mauritius",
"mv" => "Maldives",
"mw" => "Malawi",
"mx" => "Mexico",
"my" => "Malaysia",
"mz" => "Mozambique",
"na" => "Namibia",
"nc" => "New Caledonia",
"ne" => "Niger",
"nf" => "Norfolk Island",
"ng" => "Nigeria",
"ni" => "Nicaragua",
"nl" => "Netherlands",
"no" => "Norway",
"np" => "Nepal",
"nr" => "Nauru",
"nz" => "New Zealand",
"om" => "Oman",
"pa" => "Panama",
"pe" => "Peru",
"pf" => "French Polynesia",
"pg" => "Papua New Guinea",
"ph" => "Philippines",
"pk" => "Pakistan",
"pl" => "Poland",
"pn" => "Pitcairn Islands",
"pr" => "Puerto Rico",
"pt" => "Portugal",
"pw" => "Palau",
"py" => "Paraguay",
"qa" => "Qatar",
"re" => "Réunion",
"ro" => "Romania",
"ru" => "Russia",
"rw" => "Rwanda",
"sa" => "Saudi Arabia",
"sb" => "Solomon Islands",
"sc" => "Seychelles",
"sd" => "Sudan",
"se" => "Sweden",
"sg" => "Singapore",
"sh" => "Saint Helena",
"si" => "Slovenia",
"sk" => "Slovakia",
"sl" => "Sierra Leone",
"sm" => "San Marino",
"sn" => "Senegal",
"so" => "Somalia",
"sr" => "Suriname",
"st" => "São Tomé and Príncipe",
"su" => "former Soviet Union",
"sv" => "El Salvador",
"sy" => "Syria",
"sz" => "Swazilan",
"td" => "Chad",
"tg" => "Togo",
"th" => "Thailand",
"tj" => "Tajikistan",
"tl" => "East Timor",
"tm" => "Turkmenistan",
"tn" => "Tunisia",
"to" => "Tonga",
"tp" => "East Timor",
"tr" => "Turkey",
"tt" => "Trinidad and Tobago",
"tw" => "Taiwan",
"tz" => "Tanzania",
"ua" => "Ukraine",
"ug" => "Uganda",
"uk" => "United Kingdom",
"um" => "United States Minor Outlying Island",
"uy" => "Uruguay",
"uz" => "Uzbekistan",
"va" => "Vatican City State",
"vc" => "Saint Vincent and the Grenadines",
"ve" => "Venezuela",
"vg" => "British Virgin Islands",
"vi" => "U.S. Virgin Islands",
"vn" => "Vietnam",
"vu" => "Vanuatu",
"wf" => "Wallis and Futuna",
"ws" => "Samoa",
"ye" => "Yemen",
"yt" => "Mayotte",
"yu" => "Yugoslavia",
"za" => "South Africa",
"zm" => "Zambia",
"zw" => "Zimbabwe"
);

require("config.php");

$db = mysql_connect($dbhost, $dbuser, $dbpassword);
mysql_select_db($dbname, $db);

$conn = new COM("ADODB.Connection") or die("Cannot start ADO");
$conn->Open("DRIVER={MySQL ODBC 3.51 Driver}; SERVER=$dbhost; PORT=$dbport; DATABASE=$dbname; USER=$dbuser; PASSWORD=$dbpassword; OPTION=3;");

$mysession = curl_init();
curl_setopt($mysession, CURLOPT_URL, "http://$shoutcasthost/admin.cgi?mode=viewxml");
curl_setopt($mysession, CURLOPT_HEADER, false);
curl_setopt($mysession, CURLOPT_RETURNTRANSFER, true);
curl_setopt($mysession, CURLOPT_POST, false);
curl_setopt($mysession, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($mysession, CURLOPT_USERPWD, "$shoutcastuser:$shoutcastpassword");
curl_setopt($mysession, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($mysession, CURLOPT_USERAGENT, $_SERVER[‘HTTP_USER_AGENT’]);
$xml = curl_exec($mysession);
curl_close($mysession);

$result = "";
$listeners = "0";
$maxlisteners = "0";

$xml_parser = xml_parser_create();
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData");
xml_parse($xml_parser, $xml);
xml_parser_free($xml_parser);

$usa = $listeners;

$temp = "";
$count = 0;
$suppress = 0;
foreach ($country as $key=>$val) {
   $country = $countries[$key];
   if (strlen($country) > 0) {
      $count++;
      if ($suppress == 0)
         $temp .= ",&nbsp;";
      $suppress = 0;
      if ($val <> 1)
         $temp .= "$country&nbsp;($val)";
      else
         $temp .= "$country";
      $usa -= $val;
      if ($count > 5) {
         $temp .= ",<br>";
         $count = 0;
         $suppress = 1;
      }
   }
}

if (substr($temp, -1) == ";") {
   $temp = substr($temp, 0, -7);
}

if (substr($temp, -1) == ">") {
   $temp = substr($temp, 0, -5);
}

$result = "<b>There are currently $listeners out of $maxlisteners listeners from:</b><br><font size=1>USA ($usa)$temp</font>";

echo trim($result);

function iplookup($ipdata) {
  global $conn;
  global $dbtable;
  $parts = explode(".", $ipdata);
  $value = $parts[0];
  $value = $value * 256 + $parts[1];
  $value = $value * 256 + $parts[2];
  $value = $value * 256 + $parts[3];
  $sql = "SELECT country FROM $dbtable WHERE startip <= $value AND endip >= $value;";
        //echo $sql . "\n";
        $rs = $conn->Execute($sql);
  if ($rs->EOF) {
          return "";
  }
  $rv = $rs->Fields("country");
  @$country = $rv->value;
  $country = strtolower($country);
  //echo "country " . $country . "\n";
  $rs->Close();
        return $country;
}
function startElement($parser, $name, $attrs) {
   global $curTag;
   $curTag .= "^$name";
}
function endElement($parser, $name) {
   global $curTag;
   $caret_pos = strrpos($curTag, ‘^’);
   $curTag = substr($curTag, 0, $caret_pos);
}
function characterData($parser, $data) {
   global $curTag;
   global $country;
   global $listeners;
   global $maxlisteners;
   global $songs;

   if ($curTag == "^SHOUTCASTSERVER^LISTENERS^LISTENER^HOSTNAME") {
      $i = iplookup($data);
      $country[$i]++;
   }
   if ($curTag == "^SHOUTCASTSERVER^CURRENTLISTENERS") {
      $listeners = $data;
   }
   if ($curTag == "^SHOUTCASTSERVER^MAXLISTENERS") {
      $maxlisteners = $data;
   }
}

?>




You must be logged in to post a comment.

Comments

  • ADS

  • ADS