Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
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.