Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

691 lines (620 sloc) 22.203 kb
<?php
// mp3info class for use in the Blubrry PowerPress
// Main purpose of this file is to obtain the duration string for the itunes:duration field.
// Library is packaged thin with only basic mp3 support.
// Concept with this library is to get the information without downlaoding the entire file.
// for efficienccy
class Mp3Info {
//var $m_DownloadBytesLimit = 1638400; // 1600K (1600*1024) bytes file
//var $m_DownloadBytesLimit = 204800; // 200K (200*1024) bytes file
//var $m_DownloadBytesLimit = 327680; // 320K (320*1024) bytes file
//var $m_DownloadBytesLimit = 409600; // 400K (400*1024) bytes file
// var $m_DownloadBytesLimit = 614400; // 600K (600*1024) bytes file
var $m_DownloadBytesLimit = 1048576; // 1MB (1024*1024) bytes file
var $m_RedirectLimit = 12; // Number of times to do the 302 redirect
var $m_UserAgent = 'Blubrry PowerPress';
var $m_error = '';
var $m_warnings = array();
var $m_ContentLength = false;
var $m_ContentType = '';
var $m_RedirectCount = 0;
Var $m_file_size_only = false;
var $m_data = '';
// Constructor
function Mp3Info()
{
// Empty for now
$this->m_UserAgent = 'Blubrry PowerPress/'.POWERPRESS_VERSION;
}
/*
Set how much of the media file to download. Default: 25K
*/
function SetDownloadBytesLimit($limit=204800)
{
$this->m_DownloadBytesLimit = $limit;
}
/*
Set how many times we can follow a HTTP 30x header redirect before we fail.
*/
function SetRedirectLimit($limit=5)
{
$this->m_RedirectLimit = $limit;
}
/*
Set the user agent to be sent by this plugin
*/
function SetUserAgent($user_agent)
{
$this->m_UserAgent = $user_agent;
}
/*
Return the last set error message
*/
function GetError()
{
return $this->m_error;
}
/*
Set the last error message
*/
function SetError($msg)
{
$this->m_error = $msg;
}
function GetWarnings()
{
return $this->m_warnings;
}
function AddWarning($msg)
{
$this->m_warnings[] = $msg;
}
/*
Get the length in bytes of the file to download.
*/
function GetContentLength()
{
return $this->m_ContentLength;
}
/*
Get the content type of the file to download.
*/
function GetContentType()
{
return $this->m_ContentType;
}
/*
Get the number of times we followed 30x header redirects
*/
function GetRedirectCount()
{
return $this->m_RedirectCount;
}
/*
Start the download and get the headers, handles the redirect if there are any
*/
function Download($url, $RedirectCount = 0)
{
if( !ini_get( 'allow_url_fopen' ) && !function_exists( 'curl_init' ) )
{
$this->SetError( __('Your server must either have the php.ini setting \'allow_url_fopen\' enabled or have the PHP cURL library installed in order to continue.', 'powerpress') );
return false;
}
if( function_exists( 'curl_init' ) )
return $this->DownloadCurl($url);
// The following code relies on fopen_url capability.
if( $RedirectCount > $this->m_RedirectLimit )
{
$this->SetError( sprintf( __('Media URL exceeded redirect limit of %d (fopen).', 'powerpress'), $this->m_RedirectLimit) );
return false;
}
$this->m_ContentLength = false;
$this->m_ContentType = '';
$this->m_RedirectCount = $RedirectCount;
$urlParts = parse_url($url);
if( !isset( $urlParts['host']) )
{
if( empty($url) )
$this->SetError( __('Unable to obtain host name from URL.', 'powerpress') );
else
$this->SetError( __('Unable to obtain host name from the URL:', 'powerpress') .' '.$url );
return false;
}
if( !isset( $urlParts['path']) )
$urlParts['path'] = '/';
if( !isset( $urlParts['port']) )
$urlParts['port'] = 80;
if( !isset( $urlParts['scheme']) )
$urlParts['scheme'] = 'http';
$fp = fsockopen($urlParts['host'], $urlParts['port'], $errno, $errstr, 30);
if ($fp)
{
// Create and send the request headers
$RequestHeaders = ($this->m_file_size_only?'HEAD ':'GET ').$urlParts['path'].(isset($urlParts['query']) ? '?'.$urlParts['query'] : '')." HTTP/1.0\r\n";
$RequestHeaders .= 'Host: '.$urlParts['host'].($urlParts['port'] != 80 ? ':'.$urlParts['port'] : '')."\r\n";
$RequestHeaders .= "Connection: Close\r\n";
$RequestHeaders .= "User-Agent: {$this->m_UserAgent}\r\n";
fwrite($fp, $RequestHeaders."\r\n");
$Redirect = false;
$RedirectURL = false;
$ContentLength = false;
$ContentType = false;
$ReturnCode = 0;
$headers = '';
// Loop through the headers
while( !feof($fp) )
{
$line = fgets($fp, 1280); // Get the next header line...
if( $line === false )
break; // Something happened
if ($line == "\r\n")
break; // Okay we're ending the headers, now onto the content
$headers .= $line;
$line = rtrim($line); // Clean out the new line characters
$key = '';
$value = '';
if( strstr($line, ':') )
list($key, $value) = explode(':', $line, 2);
$key = trim($key);
$value = trim($value);
if( stristr($line, '301 Moved Permanently') || stristr($line, '302 Found') || stristr($line, '307 Temporary Redirect') )
{
$Redirect = true; // We are dealing with a redirect, lets handle it
}
else if( preg_match('/^HTTPS?\/\d\.\d (\d{3})(.*)/i', $line, $matches) )
{
$ReturnCode = $matches[1];
if( $ReturnCode < 200 || $ReturnCode > 206 )
{
fclose($fp);
if( $ReturnCode == 404 )
$this->SetError( __('The requested URL returned error code 404, file not found.', 'powerpress') );
else
$this->SetError( sprintf(__('The requested URL returned error code %d','powerpress'), $ReturnCode.$matches[2]) );
return false;
}
}
else
{
switch( strtolower($key) )
{
case 'location': {
$RedirectURL = $value;
}; break;
case 'content-length': {
$ContentLength = $value;
}; break;
case 'content-type' : {
$this->m_ContentType = $value;
}
}
}
}
// Loop through the content till we reach our limit...
$Content = '';
if( $this->m_DownloadBytesLimit )
{
while( !feof($fp) )
{
$Content .= fread($fp, 8096);
if( strlen($Content) > $this->m_DownloadBytesLimit )
break; // We got enough of the file we should be able to determine the duration
}
}
fclose($fp);
// If we're dealing with a redirect, lets call our nested function call now
if( $Redirect )
{
unset($Content); // clear what may be using a lot of memory
return $this->Download($RedirectURL, $RedirectCount + 1 ); // Follow this redirect
}
else // Otherwise, lets set the data and return true for part two
{
if( $this->m_file_size_only )
{
if( $ContentLength )
{
$this->m_ContentLength = $ContentLength;
return true;
}
else
{
$this->SetError( __('Unable to obtain media size from web server.', 'powerpress') );
return false;
}
}
//global $TempFile;
if( function_exists('get_temp_dir') ) // If wordpress function is available, lets use it
$TempFile = tempnam(get_temp_dir(), 'wp_powerpress');
else // otherwise use the default path
$TempFile = tempnam('/tmp', 'wp_powerpress');
if( $TempFile === false )
{
$this->SetError( __('Unable to save media information to temporary directory.', 'powerpress') );
return false;
}
$fp = fopen( $TempFile, 'w' );
fwrite($fp, $Content);
fclose($fp);
if( $ContentLength )
$this->m_ContentLength = $ContentLength;
return $TempFile;
}
}
$this->SetError( __('Unable to connect to host:','powerpress') .' '.$urlParts['host']);
return false;
}
/*
Alternative method (curl) for downloading portion of a media file
*/
function DownloadCurl($url, $RedirectCount = 0)
{
// In case we are dealing with a restriction with a server that does not allow cURL to do redirects itself...
if ( ini_get('safe_mode') || ini_get('open_basedir') )
{
if( $RedirectCount > $this->m_RedirectLimit )
{
$this->SetError( sprintf( __('Media URL exceeded redirect limit of %d (cURL in safe mode).', 'powerpress'), $this->m_RedirectLimit) );
return false;
}
$this->m_RedirectCount = $RedirectCount;
}
$curl = curl_init();
// First, get the content-length...
curl_setopt($curl, CURLOPT_USERAGENT, $this->m_UserAgent );
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, true); // header will be at output
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD'); // HTTP request
curl_setopt($curl, CURLOPT_NOBODY, true );
curl_setopt($curl, CURLOPT_FAILONERROR, true);
if ( !ini_get('safe_mode') && !ini_get('open_basedir') )
{
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($curl, CURLOPT_MAXREDIRS, $this->m_RedirectLimit);
}
else
{
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($curl, CURLOPT_MAXREDIRS, 0 ); // We will attempt to handle redirects ourself
}
$Headers = curl_exec($curl);
$ContentLength = curl_getinfo($curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
$this->m_ContentType = curl_getinfo($curl, CURLINFO_CONTENT_TYPE);
$HttpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$ContentType = curl_getinfo($curl, CURLINFO_CONTENT_TYPE);
$ErrorMsg = curl_error($curl);
if ( !ini_get('safe_mode') && !ini_get('open_basedir') )
$this->m_RedirectCount = curl_getinfo($curl, CURLINFO_REDIRECT_COUNT);
if( $HttpCode < 200 || $HttpCode > 250 )
{
switch( $HttpCode )
{
case 301:
case 302:
case 307: {
if ( !ini_get('safe_mode') && !ini_get('open_basedir') )
{
$this->SetError( sprintf( __('Media URL exceeded redirect limit of %d (cURL).', 'powerpress'), $this->m_RedirectLimit) );
}
else
{
$redirect_url = false;
if( preg_match('/^location:(.*)$/im', $Headers, $matches) )
$redirect_url = trim($matches[1]);
if( $redirect_url )
{
curl_close($curl);
return $this->DownloadCurl($redirect_url, $RedirectCount +1);
}
else
{
$this->SetError( sprintf(__('Unable to obtain HTTP %d redirect URL.', 'powerpress'), $HttpCode) );
}
}
}; break;
case '404': {
$this->SetError( __('The requested URL returned error code 404, file not found.', 'powerpress') );
}; break;
default: {
$this->SetError( curl_error($curl) );
}; break;
}
curl_close($curl);
return false;
}
/*
if( stristr($ContentType, 'text') )
{
$this->SetError( 'Invalid content type returned.' );
return false;
}
*/
$FinalURL = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL);
curl_close($curl); // Close the first CURL connection
if( $this->m_file_size_only )
{
if( $ContentLength )
{
$this->m_ContentLength = $ContentLength;
return true;
}
$this->SetError( __('Unable to obtain media size from server.', 'powerpress') );
return false;
}
global $TempFile;
if( function_exists('get_temp_dir') ) // If wordpress function is available, lets use it
$TempFile = tempnam(get_temp_dir(), 'wp_powerpress');
else // otherwise use the default path
$TempFile = tempnam('/tmp', 'wp_powerpress');
if( $TempFile === false )
{
$this->SetError( __('Unable to create temporary file for checking media information.', 'powerpress') );
return false;
}
$fp = fopen($TempFile, 'w+b');
// Next get the first chunk of the file...
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $FinalURL);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, false); // Don't set this as it is knwon to cause errors with the function callback.
curl_setopt($curl, CURLOPT_BINARYTRANSFER, true);
curl_setopt($curl, CURLOPT_USERAGENT, $this->m_UserAgent);
curl_setopt($curl, CURLOPT_FILE, $fp);
curl_setopt($curl, CURLOPT_HEADER, false); // header will be at output
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'GET'); // HTTP request
curl_setopt($curl, CURLOPT_NOBODY, false );
if ( !ini_get('safe_mode') && !ini_get('open_basedir') )
{
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($curl, CURLOPT_MAXREDIRS, $this->m_RedirectLimit);
}
else
{
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($curl, CURLOPT_MAXREDIRS, 0 ); // We will attempt to handle redirects ourself
}
curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
// First lets try a range request
curl_setopt($curl, CURLOPT_RANGE, '0-'.($this->m_DownloadBytesLimit - 1) );
// curl_setopt($curl, CURLOPT_HTTPHEADER, array('Range: bytes=0-'.($this->m_DownloadBytesLimit - 1) ));
$success = curl_exec($curl);
if( !$success && curl_getinfo($curl, CURLINFO_HTTP_CODE) == 406 )
{
curl_close($curl);
$curl = curl_init();
//curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_URL, $FinalURL);
curl_setopt($curl, CURLOPT_USERAGENT, $this->m_UserAgent);
curl_setopt($curl, CURLOPT_HEADER, false); // header will be at output
curl_setopt($curl, CURLOPT_NOBODY, false );
curl_setopt($curl, CURLOPT_WRITEFUNCTION, array($this, 'remoteread_curl_writefunc') );
if ( !ini_get('safe_mode') && !ini_get('open_basedir') )
{
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($curl, CURLOPT_MAXREDIRS, $this->m_RedirectLimit);
}
else
{
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($curl, CURLOPT_MAXREDIRS, 0 ); // We will attempt to handle redirects ourself
}
$success = curl_exec($curl);
if( ($success || curl_errno($curl) == 23) && $this->m_data != '' )
{
fwrite($fp , $this->m_data);
$this->m_data = ''; // Free up this memory by setting the value to a blank string
$success = true; // Actually this was a success
}
else if( $success && $this->m_data == '' )
{
$this->SetError( __('Unable to download media.', 'powerpress') );
$success = false;
}
}
if( !$success )
{
if( curl_errno($curl) )
$this->SetError( __('Retrieving file info:', 'powerpress') .' '. curl_error($curl) );
else if( $this->GetError() == '' )
$this->SetError( __('Unable to download media.', 'powerpress') );
}
curl_close($curl);
fclose($fp);
if( $success )
{
if( $ContentLength )
$this->m_ContentLength = $ContentLength;
return $TempFile;
}
@unlink($TempFile);
return false;
}
/*
Get the MP3 information
*/
function GetMp3Info($File, $file_size_only = false)
{
$this->m_file_size_only = $file_size_only;
$DeleteFile = false;
if( strtolower( substr($File, 0, 7) ) == 'http://' )
{
$LocalFile = $this->Download($File);
if( $LocalFile === false )
return false;
if( $this->m_file_size_only )
return true;
$DeleteFile = true;
}
else
{
if( $this->m_file_size_only )
{
if( file_exists($File) )
{
$this->m_ContentLength = filesize($File);
return true;
}
return false;
}
$LocalFile = $File;
}
if( class_exists('getID3') && !defined('POWERPRESS_GETID3_LOADED') )
{
$pre_msg = __('PowerPress is unable to detect media information.', 'powerpress') .'<br />';
$getID3 = new getID3;
if( defined('GETID3_INCLUDEPATH') ) {
$plugin_title = '';
$plugins_path = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR;
$path = substr(GETID3_INCLUDEPATH, strlen($plugins_path));
if( preg_match('/^([^\\/\\\\]*)/i', $path, $matches) )
{
$plugin_folder = $matches[1];
$current_plugins = get_option('active_plugins');
while( list($null,$plugin_local_path) = each($current_plugins) )
{
if( substr($plugin_local_path, 0, strpos($plugin_local_path, '/') ) != $plugin_folder )
continue;
$plugin_data = get_plugin_data( rtrim(WP_PLUGIN_DIR, '/\\'). '/'. rtrim($plugin_local_path, '\\/'), false, false ); //Do not apply markup/translate as it'll be cached.
$plugin_title = $plugin_data['Title'];
}
}
if( $plugin_title )
$this->SetError( $pre_msg. sprintf(__('Plugin \'%s\' has included a different version of the GetID3 library located in %s', 'powerpress'), $plugin_title, $path) );
else
$this->SetError( $pre_msg. sprintf(__('Another plugin has included a different version of the GetID3 library located in %s', 'powerpress'), $path) );
} else {
$this->SetError( $pre_msg. __('Another plugin has included a different version of the GetID3 library.', 'powerpress') );
}
return false;
}
if( !is_file($LocalFile) )
{
$this->SetError( __('Error occurred downloading media file.', 'powerpress') );
return false;
}
if( $this->m_ContentLength == -1 )
{
$this->SetError( __('Error occurred downloading media file.', 'powerpress') );
return false;
}
if( $this->m_ContentLength < 1 )
$this->m_ContentLength = filesize($LocalFile);
if( $this->m_ContentLength == 0 )
{
$this->SetError( __('Downloaded media file is empty.', 'powerpress') );
return false;
}
//if( !preg_match('/(audio|video)/i', $this->GetContentType() ) )
//{
// $this->SetError( sprintf(__('URL is reporting incorrect content type: %s', 'powerpress'), $this->GetContentType()) );
// return false;
//}
// Hack so this works in Windows, helper apps are not necessary for what we're doing anyway
define('GETID3_HELPERAPPSDIR', true);
if( function_exists('get_temp_dir') ) // If wordpress function is available, lets use it
{
$temp_dir = get_temp_dir(); // WordPress temp folder
if( is_dir($temp_dir) )
define('GETID3_TEMP_DIR', $temp_dir);
}
require_once(POWERPRESS_ABSPATH.'/getid3/getid3.php');
define('POWERPRESS_GETID3_LOADED', true);
$getID3 = new getID3;
$FileInfo = $getID3->analyze( $LocalFile, $this->m_ContentLength );
if( $DeleteFile )
@unlink($LocalFile);
if( $FileInfo )
{
if( isset($FileInfo['error']) )
{
// Speical case, if the content type does not include audio or video, report that as possible error...
if( !preg_match('/(audio|video)/i', $this->GetContentType() ) )
{
$this->SetError( sprintf(__('Media URL reporting incorrect content type: %s', 'powerpress'), $this->GetContentType()) );
return false;
}
$errors = '';
while( list($null,$error) = each($FileInfo['error']) )
{
if( strstr($error, 'error parsing') )
continue;
$errors .= " $error.";
}
if( !empty($errors) )
{
$this->SetError( trim($errors) );
return false;
}
}
if( false && isset($FileInfo['warning']) )
{
$errors = '';
while( list($null,$warning) = each($FileInfo['warning']) )
$this->AddWarning($warning );
}
// Remove extra data that is not necessary for us to return...
//unset($FileInfo['mpeg']);
unset($FileInfo['audio']);
if( isset($FileInfo['id3v2']) )
unset($FileInfo['id3v2']);
if( isset($FileInfo['id3v1']) )
unset($FileInfo['id3v1']);
if( !isset($FileInfo['playtime_seconds']) )
$FileInfo['playtime_seconds'] = '';
if( !isset($FileInfo['playtime_string']) )
$FileInfo['playtime_string'] = '';
if( !empty($FileInfo['playtime_seconds']) )
$FileInfo['playtime_seconds'] = round($FileInfo['playtime_seconds']);
else
$FileInfo['playtime_seconds'] = 0;
// No longer checking for the right sample rates and channel mode for flash, flash is now OBSOLETE
/*
if( isset($FileInfo['mpeg']['audio']) && $FileInfo['mpeg']['audio'] )
{
$Audio = $FileInfo['mpeg']['audio'];
if( $Audio['sample_rate'] != 22050 && $Audio['sample_rate'] != 44100 )
{
// Add warning here
$this->AddWarning( sprintf(__('Sample Rate %dKhz may cause playback issues, we recommend 22Khz or 44Khz for maximum player compatibility.', 'powerpress'), $Audio['sample_rate']/1000 ) );
}
//if( stristr($Audio['channelmode'], 'stereo' ) === false )
//{
// // Add warning here
// $this->AddWarning( sprintf(__('Channel Mode \'%s\' may cause playback issues, we recommend \'joint stereo\' for maximum player compatibility.', 'powerpress'), trim($Audio['channelmode']) ) );
//}
}
*/
return $FileInfo;
}
return false;
}
function remoteread_curl_writefunc($curl, $data)
{
$this->m_data .= $data;
if( strlen($this->m_data) > $this->m_DownloadBytesLimit )
{
return 0; // stop the download here...
}
return strlen($data);
}
};
/*
// Example usage:
$Mp3Info = new Mp3Info();
$file = 'http://www.podcampohio.com/podpress_trac/web/177/0/TS-107667.mp3';
if( $Data = $Mp3Info->GetMp3Info($file) )
{
echo 'Success: ';
echo print_r( $Data );
echo PHP_EOL;
exit;
}
else
{
echo 'Error: ';
echo $Mp3Info->GetError();
echo PHP_EOL;
exit;
}
*/
?>
Jump to Line
Something went wrong with that request. Please try again.