Last active
August 29, 2015 14:16
-
-
Save ePirat/58f3f00a47dc3ea21880 to your computer and use it in GitHub Desktop.
Ice PlaylistParser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/php | |
<?php | |
if (php_sapi_name() == "cli") { | |
// If this is executed via CLI, parse options and stuff | |
// Helper | |
function report_error($e) { | |
// Get the current script name | |
$our_name = (($v =strrchr(__FILE__, DIRECTORY_SEPARATOR)) == FALSE) | |
? __FILE__ | |
: $v; | |
$our_name = substr($our_name, 1, (strrpos($our_name, '.')-1)); | |
echo("ERROR: {$e}\n\n"); | |
echo("Syntax:\t{$our_name} -i <Input File> [OPTIONS]\n"); | |
echo("Options:\n"); | |
echo("\t-m\tLimit output to specified mountpoint names (comma separated)\n"); | |
echo("\t-l\tThe locale to use for time parsing\n"); | |
echo("\t \t(should be the same that Icecast is using)\n"); | |
echo("\t-v\tVerbose output\n"); | |
die(); | |
} | |
// Options | |
$shortopts = "i:"; // Input Filename, use '-' for stdin | |
$shortopts .= "m:"; // Limit output to specified mountspoints (comma separated) | |
$shortopts .= "l:"; // The locale to use, defaults to the systems locale | |
$shortopts .= "v"; // Verbose output | |
$options = getopt($shortopts); | |
$verbose = isset($options['v']); | |
$infile = (empty($options['i'])) | |
? report_error("No input file given!") | |
: $options['i']; | |
$mounts = (empty($options['m'])) | |
? NULL | |
: explode(',', $options['m']); | |
$locale = (empty($options['l'])) | |
? NULL | |
: $options['l']; | |
// Gather Info | |
try { | |
$parser = new PlaylistParser($locale, $infile); | |
} catch (PlaylistParserException $e) { | |
if ($verbose) | |
report_error("[{$e->getCode()}] {$e->getMessage()}\n{$e->getDetails()}"); | |
else | |
report_error("[{$e->getCode()}] {$e->getMessage()}"); | |
} | |
$list = $parser->getPlaylistByMount($mounts, true); | |
// Clear up the parser, as it has some big internal array | |
unset($parser); | |
// Iterate over the mounts | |
foreach ($list as $m => $v) { | |
echo("\nMountpoint Stats for: {$m}\n"); | |
// Iterate over the days | |
foreach ($v as $key => $value) { | |
$hours = 0; | |
$songs = 0; | |
echo("Date: {$key}\n"); | |
// Iterate over the hours | |
foreach ($value as $hour => $logs) { | |
$hours++; | |
$count = count($logs); | |
$songs += $count; | |
echo("{$hour}:00 - {$count} songs played!\n"); | |
} | |
$avg = ($songs/$hours); | |
echo("Average: {$avg} songs per hour!\n"); | |
} | |
} | |
} | |
class PlaylistParserException extends Exception | |
{ | |
var $details; | |
// Redefine the exception so message isn't optional | |
public function __construct($message, $code, $details = '') { | |
$this->details = $details; | |
parent::__construct($message, $code, NULL); | |
} | |
public function getDetails() | |
{ | |
return $this->details; | |
} | |
// custom string representation of object | |
public function __toString() { | |
return __CLASS__ . ": [{$this->code}]: {$this->message}\n"; | |
} | |
} | |
/** | |
* Icecast Playlist Log Parser | |
* Expected Input format example: | |
* 26/Feb/2015:10:05:45 +0000|/radio|0| - Definitive | |
*/ | |
class PlaylistParser | |
{ | |
var $playlist; | |
public function __construct($locale = NULL, $file = NULL) | |
{ | |
// Initialize the playlist array | |
$this->playlist = array(); | |
// Try to set the right locale | |
if (setlocale(LC_TIME, $locale) === FALSE) { | |
// There was an error changing the locale | |
throw new PlaylistParserException("Failed changing locale to '{$locale}'", 1); | |
} | |
// If no file given, we are done here | |
if (is_null($file)) | |
return; | |
// If a file is fiven, parse it | |
$this->parseFile($file); | |
} | |
/* Parses an Icecast playlist file | |
* This does the same as providing a filename when | |
* initializing the Object | |
*/ | |
public function parseFile($file) | |
{ | |
// Keep track of line number for error reporting | |
$lineno = 1; | |
$handle = @fopen($file, "r"); | |
if ($handle) { | |
while (($line = fgets($handle)) !== FALSE) { | |
// Parse each line | |
$line = $this->_parseLine($line, $lineno); | |
// Add them to the final array | |
$this->playlist[] = $line; | |
// Finally, count up the line | |
$lineno++; | |
} | |
//print_r($this->playlist); | |
} else { | |
// If we can't open the file, throw a exception | |
$e = (is_null($e = error_get_last())) ? '' : $e['message']; | |
throw new PlaylistParserException("Failed opening playlist file '{$file}'", 2, $e); | |
} | |
} | |
private function _parseLine($line, $lineno) | |
{ | |
// Parse the string into it's components | |
list($time, $mount, $listeners, $meta) = explode('|', $line, 4); | |
// Check if all compinents are there | |
if (!isset($time, $mount, $listeners, $meta)) { | |
throw new PlaylistParserException("Failed parsing playlist file: " . | |
"Invalid Argument Number (line {$lineno})", 11); | |
} | |
// Parse the Date | |
$utc = new DateTimeZone('UTC'); | |
$date = DateTime::createFromFormat("d/M/Y:H:i:s O", $time, $utc); | |
// Check if parsing failed | |
if ($t === FALSE) { | |
throw new PlaylistParserException("Failed parsing playlist file: " . | |
"Invalid Timestamp (line {$lineno})", 10); | |
} | |
// Construct return array | |
return array('date' => $date, 'mount' => $mount, 'listeners' => $listeners, 'meta' => $meta); | |
} | |
public function getPlaylistByMount($mount = NULL, $group_hours = FALSE) | |
{ | |
if (count($this->playlist) === 0) { | |
return FALSE; | |
} | |
$arr = array(); | |
// Make an array with mount as index | |
foreach ($this->playlist as $k => $v) { | |
if ($group_hours) { | |
// Group by Date and Hours of Day | |
$datestring = $v['date']->format('Y-m-d'); | |
$hour = $v['date']->format('H'); | |
$arr[$v['mount']][$datestring][$hour][] = $v; | |
} else { | |
$arr[$v['mount']][] = $v; | |
} | |
} | |
// Make $mount an array in case it is a string | |
$mount = (is_string($mount)) ? array($mount) : $mount; | |
return (is_null($mount)) | |
? $arr | |
: array_intersect_key($arr, array_flip($mount)); | |
} | |
} | |
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ ./parse_playlist.php -i ./playlist.log | |
Mountpoint Stats for: /radio | |
Date: 2015-02-26 | |
10:00 - 12 songs played! | |
11:00 - 16 songs played! | |
12:00 - 13 songs played! | |
Average: 13.666666666667 songs per hour! | |
Mountpoint Stats for: /foooo | |
Date: 2015-02-26 | |
12:00 - 1 songs played! | |
Average: 1 songs per hour! |
Author
ePirat
commented
Feb 27, 2015
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment