kent1@arscenic.info 6 years ago
parent
commit
8670be5739
  1. 1
      .gitattributes
  2. 4
      getid3/extension.cache.mysql.php
  3. 183
      getid3/extension.cache.mysqli.php
  4. 74
      getid3/getid3.lib.php
  5. 163
      getid3/getid3.php
  6. 2
      getid3/module.audio-video.asf.php
  7. 7
      getid3/module.audio-video.matroska.php
  8. 12
      getid3/module.audio-video.quicktime.php
  9. 42
      getid3/module.audio.dss.php
  10. 11
      getid3/module.graphic.jpg.php
  11. 2
      getid3/module.tag.apetag.php
  12. 21
      getid3/module.tag.id3v1.php
  13. 120
      getid3/module.tag.id3v2.php
  14. 358
      getid3/write.id3v2.php
  15. 34
      getid3/write.php

1
.gitattributes vendored

@ -11,6 +11,7 @@ formulaires/editer_id3.php -text
genie/getid3_taches_generales.php -text
getid3/extension.cache.dbm.php -text
getid3/extension.cache.mysql.php -text
getid3/extension.cache.mysqli.php -text
getid3/extension.cache.sqlite3.php -text
getid3/getid3.lib.php -text
getid3/getid3.php -text

4
getid3/extension.cache.mysql.php

@ -178,12 +178,12 @@ class getID3_cached_mysql extends getID3
private function create_table($drop=false) {
$SQLquery = 'CREATE TABLE IF NOT EXISTS `'.mysql_real_escape_string($this->table).'` (';
$SQLquery .= '`filename` VARCHAR(500) NOT NULL DEFAULT \'\'';
$SQLquery .= '`filename` VARCHAR(990) NOT NULL DEFAULT \'\'';
$SQLquery .= ', `filesize` INT(11) NOT NULL DEFAULT \'0\'';
$SQLquery .= ', `filetime` INT(11) NOT NULL DEFAULT \'0\'';
$SQLquery .= ', `analyzetime` INT(11) NOT NULL DEFAULT \'0\'';
$SQLquery .= ', `value` LONGTEXT NOT NULL';
$SQLquery .= ', PRIMARY KEY (`filename`, `filesize`, `filetime`)) ENGINE=MyISAM';
$SQLquery .= ', PRIMARY KEY (`filename`, `filesize`, `filetime`)) ENGINE=MyISAM CHARACTER SET=latin1 COLLATE=latin1_general_ci';
$this->cursor = mysql_query($SQLquery, $this->connection);
echo mysql_error($this->connection);
}

183
getid3/extension.cache.mysqli.php

@ -0,0 +1,183 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// //
// extension.cache.mysqli.php - part of getID3() //
// Please see readme.txt for more information //
// ///
/////////////////////////////////////////////////////////////////
// //
// This extension written by Allan Hansen <ahØartemis*dk> //
// Table name mod by Carlo Capocasa <calroØcarlocapocasa*com> //
// ///
/////////////////////////////////////////////////////////////////
/**
* This is a caching extension for getID3(). It works the exact same
* way as the getID3 class, but return cached information very fast
*
* Example: (see also demo.cache.mysql.php in /demo/)
*
* Normal getID3 usage (example):
*
* require_once 'getid3/getid3.php';
* $getID3 = new getID3;
* $getID3->encoding = 'UTF-8';
* $info1 = $getID3->analyze('file1.flac');
* $info2 = $getID3->analyze('file2.wv');
*
* getID3_cached usage:
*
* require_once 'getid3/getid3.php';
* require_once 'getid3/getid3/extension.cache.mysqli.php';
* // 5th parameter (tablename) is optional, default is 'getid3_cache'
* $getID3 = new getID3_cached_mysqli('localhost', 'database', 'username', 'password', 'tablename');
* $getID3->encoding = 'UTF-8';
* $info1 = $getID3->analyze('file1.flac');
* $info2 = $getID3->analyze('file2.wv');
*
*
* Supported Cache Types (this extension)
*
* SQL Databases:
*
* cache_type cache_options
* -------------------------------------------------------------------
* mysqli host, database, username, password
*
*
* DBM-Style Databases: (use extension.cache.dbm)
*
* cache_type cache_options
* -------------------------------------------------------------------
* gdbm dbm_filename, lock_filename
* ndbm dbm_filename, lock_filename
* db2 dbm_filename, lock_filename
* db3 dbm_filename, lock_filename
* db4 dbm_filename, lock_filename (PHP5 required)
*
* PHP must have write access to both dbm_filename and lock_filename.
*
*
* Recommended Cache Types
*
* Infrequent updates, many reads any DBM
* Frequent updates mysqli
*/
class getID3_cached_mysqli extends getID3
{
// private vars
private $mysqli;
private $cursor;
// public: constructor - see top of this file for cache type and cache_options
public function __construct($host, $database, $username, $password, $table='getid3_cache') {
// Check for mysqli support
if (!function_exists('mysqli_connect')) {
throw new Exception('PHP not compiled with mysqli support.');
}
// Connect to database
$this->mysqli = new mysqli($host, $username, $password);
if (!$this->mysqli) {
throw new Exception('mysqli_connect() failed - check permissions and spelling.');
}
// Select database
if (!$this->mysqli->select_db($database)) {
throw new Exception('Cannot use database '.$database);
}
// Set table
$this->table = $table;
// Create cache table if not exists
$this->create_table();
// Check version number and clear cache if changed
$version = '';
$SQLquery = 'SELECT `value`';
$SQLquery .= ' FROM `'.$this->mysqli->real_escape_string($this->table).'`';
$SQLquery .= ' WHERE (`filename` = \''.$this->mysqli->real_escape_string(getID3::VERSION).'\')';
$SQLquery .= ' AND (`filesize` = -1)';
$SQLquery .= ' AND (`filetime` = -1)';
$SQLquery .= ' AND (`analyzetime` = -1)';
if ($this->cursor = $this->mysqli->query($SQLquery)) {
list($version) = $this->cursor->fetch_array();
}
if ($version != getID3::VERSION) {
$this->clear_cache();
}
parent::__construct();
}
// public: clear cache
public function clear_cache() {
$this->mysqli->query('DELETE FROM `'.$this->mysqli->real_escape_string($this->table).'`');
$this->mysqli->query('INSERT INTO `'.$this->mysqli->real_escape_string($this->table).'` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES (\''.getID3::VERSION.'\', -1, -1, -1, \''.getID3::VERSION.'\')');
}
// public: analyze file
public function analyze($filename, $filesize=null, $original_filename='') {
if (file_exists($filename)) {
// Short-hands
$filetime = filemtime($filename);
$filesize = filesize($filename);
// Lookup file
$SQLquery = 'SELECT `value`';
$SQLquery .= ' FROM `'.$this->mysqli->real_escape_string($this->table).'`';
$SQLquery .= ' WHERE (`filename` = \''.$this->mysqli->real_escape_string($filename).'\')';
$SQLquery .= ' AND (`filesize` = \''.$this->mysqli->real_escape_string($filesize).'\')';
$SQLquery .= ' AND (`filetime` = \''.$this->mysqli->real_escape_string($filetime).'\')';
$this->cursor = $this->mysqli->query($SQLquery);
if ($this->cursor->num_rows > 0) {
// Hit
list($result) = $this->cursor->fetch_array();
return unserialize(base64_decode($result));
}
}
// Miss
$analysis = parent::analyze($filename, $filesize, $original_filename);
// Save result
if (file_exists($filename)) {
$SQLquery = 'INSERT INTO `'.$this->mysqli->real_escape_string($this->table).'` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES (';
$SQLquery .= '\''.$this->mysqli->real_escape_string($filename).'\'';
$SQLquery .= ', \''.$this->mysqli->real_escape_string($filesize).'\'';
$SQLquery .= ', \''.$this->mysqli->real_escape_string($filetime).'\'';
$SQLquery .= ', \''.$this->mysqli->real_escape_string(time() ).'\'';
$SQLquery .= ', \''.$this->mysqli->real_escape_string(base64_encode(serialize($analysis))).'\')';
$this->cursor = $this->mysqli->query($SQLquery);
}
return $analysis;
}
// private: (re)create mysqli table
private function create_table($drop=false) {
$SQLquery = 'CREATE TABLE IF NOT EXISTS `'.$this->mysqli->real_escape_string($this->table).'` (';
$SQLquery .= '`filename` VARCHAR(990) NOT NULL DEFAULT \'\'';
$SQLquery .= ', `filesize` INT(11) NOT NULL DEFAULT \'0\'';
$SQLquery .= ', `filetime` INT(11) NOT NULL DEFAULT \'0\'';
$SQLquery .= ', `analyzetime` INT(11) NOT NULL DEFAULT \'0\'';
$SQLquery .= ', `value` LONGTEXT NOT NULL';
$SQLquery .= ', PRIMARY KEY (`filename`, `filesize`, `filetime`)) ENGINE=MyISAM CHARACTER SET=latin1 COLLATE=latin1_general_ci';
$this->cursor = $this->mysqli->query($SQLquery);
echo $this->mysqli->error;
}
}

74
getid3/getid3.lib.php

@ -960,8 +960,20 @@ class getid3_lib
return $string;
}
// mb_convert_encoding() availble
if (function_exists('mb_convert_encoding')) {
if ($converted_string = @mb_convert_encoding($string, $out_charset, $in_charset)) {
switch ($out_charset) {
case 'ISO-8859-1':
$converted_string = rtrim($converted_string, "\x00");
break;
}
return $converted_string;
}
return $string;
}
// iconv() availble
if (function_exists('iconv')) {
else if (function_exists('iconv')) {
if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) {
switch ($out_charset) {
case 'ISO-8859-1':
@ -977,7 +989,7 @@ class getid3_lib
}
// iconv() not available
// neither mb_convert_encoding or iconv() is available
static $ConversionFunctionList = array();
if (empty($ConversionFunctionList)) {
$ConversionFunctionList['ISO-8859-1']['UTF-8'] = 'iconv_fallback_iso88591_utf8';
@ -999,7 +1011,7 @@ class getid3_lib
$ConversionFunction = $ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)];
return self::$ConversionFunction($string);
}
throw new Exception('PHP does not have iconv() support - cannot convert from '.$in_charset.' to '.$out_charset);
throw new Exception('PHP does not has mb_convert_encoding() or iconv() support - cannot convert from '.$in_charset.' to '.$out_charset);
}
public static function recursiveMultiByteCharString2HTML($data, $charset='ISO-8859-1') {
@ -1020,38 +1032,38 @@ class getid3_lib
$string = (string) $string; // in case trying to pass a numeric (float, int) string, would otherwise return an empty string
$HTMLstring = '';
switch ($charset) {
switch (strtolower($charset)) {
case '1251':
case '1252':
case '866':
case '932':
case '936':
case '950':
case 'BIG5':
case 'BIG5-HKSCS':
case 'big5':
case 'big5-hkscs':
case 'cp1251':
case 'cp1252':
case 'cp866':
case 'EUC-JP':
case 'EUCJP':
case 'GB2312':
case 'euc-jp':
case 'eucjp':
case 'gb2312':
case 'ibm866':
case 'ISO-8859-1':
case 'ISO-8859-15':
case 'ISO8859-1':
case 'ISO8859-15':
case 'KOI8-R':
case 'iso-8859-1':
case 'iso-8859-15':
case 'iso8859-1':
case 'iso8859-15':
case 'koi8-r':
case 'koi8-ru':
case 'koi8r':
case 'Shift_JIS':
case 'SJIS':
case 'shift_jis':
case 'sjis':
case 'win-1251':
case 'Windows-1251':
case 'Windows-1252':
case 'windows-1251':
case 'windows-1252':
$HTMLstring = htmlentities($string, ENT_COMPAT, $charset);
break;
case 'UTF-8':
case 'utf-8':
$strlen = strlen($string);
for ($i = 0; $i < $strlen; $i++) {
$char_ord_val = ord($string{$i});
@ -1079,7 +1091,7 @@ class getid3_lib
}
break;
case 'UTF-16LE':
case 'utf-16le':
for ($i = 0; $i < strlen($string); $i += 2) {
$charval = self::LittleEndian2Int(substr($string, $i, 2));
if (($charval >= 32) && ($charval <= 127)) {
@ -1090,7 +1102,7 @@ class getid3_lib
}
break;
case 'UTF-16BE':
case 'utf-16be':
for ($i = 0; $i < strlen($string); $i += 2) {
$charval = self::BigEndian2Int(substr($string, $i, 2));
if (($charval >= 32) && ($charval <= 127)) {
@ -1262,10 +1274,14 @@ class getid3_lib
}
if (is_array($value) || empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) {
$value = (is_string($value) ? trim($value) : $value);
if (!is_numeric($key)) {
if (!is_int($key) && !ctype_digit($key)) {
$ThisFileInfo['comments'][$tagname][$key] = $value;
} else {
$ThisFileInfo['comments'][$tagname][] = $value;
if (isset($ThisFileInfo['comments'][$tagname])) {
$ThisFileInfo['comments'][$tagname] = array($value);
} else {
$ThisFileInfo['comments'][$tagname][] = $value;
}
}
}
}
@ -1273,6 +1289,18 @@ class getid3_lib
}
}
// attempt to standardize spelling of returned keys
$StandardizeFieldNames = array(
'tracknumber' => 'track_number',
'track' => 'track_number',
);
foreach ($StandardizeFieldNames as $badkey => $goodkey) {
if (array_key_exists($badkey, $ThisFileInfo['comments']) && !array_key_exists($goodkey, $ThisFileInfo['comments'])) {
$ThisFileInfo['comments'][$goodkey] = $ThisFileInfo['comments'][$badkey];
unset($ThisFileInfo['comments'][$badkey]);
}
}
// Copy to ['comments_html']
if (!empty($ThisFileInfo['comments'])) {
foreach ($ThisFileInfo['comments'] as $field => $values) {

163
getid3/getid3.php

@ -22,6 +22,9 @@ if (!defined('GETID3_INCLUDEPATH')) {
if (!defined('IMG_JPG') && defined('IMAGETYPE_JPEG')) {
define('IMG_JPG', IMAGETYPE_JPEG);
}
if (!defined('ENT_SUBSTITUTE')) { // PHP5.3 adds ENT_IGNORE, PHP5.4 adds ENT_SUBSTITUTE
define('ENT_SUBSTITUTE', (defined('ENT_IGNORE') ? ENT_IGNORE : 8));
}
// attempt to define temp dir as something flexible but reliable
$temp_dir = ini_get('upload_tmp_dir');
@ -109,7 +112,7 @@ class getID3
protected $startup_error = '';
protected $startup_warning = '';
const VERSION = '1.9.12-201602240818';
const VERSION = '1.9.13-201612051806';
const FREAD_BUFFER_SIZE = 32768;
const ATTACHMENTS_NONE = false;
@ -121,7 +124,7 @@ class getID3
// Check for PHP version
$required_php_version = '5.3.0';
if (version_compare(PHP_VERSION, $required_php_version, '<')) {
$this->startup_error .= 'getID3() requires PHP v'.$required_php_version.' or higher - you are running v'.PHP_VERSION;
$this->startup_error .= 'getID3() requires PHP v'.$required_php_version.' or higher - you are running v'.PHP_VERSION."\n";
return false;
}
@ -137,9 +140,9 @@ class getID3
if ($this->memory_limit <= 0) {
// memory limits probably disabled
} elseif ($this->memory_limit <= 4194304) {
$this->startup_error .= 'PHP has less than 4MB available memory and will very likely run out. Increase memory_limit in php.ini';
$this->startup_error .= 'PHP has less than 4MB available memory and will very likely run out. Increase memory_limit in php.ini'."\n";
} elseif ($this->memory_limit <= 12582912) {
$this->startup_warning .= 'PHP has less than 12MB available memory and might run out if all modules are loaded. Increase memory_limit in php.ini';
$this->startup_warning .= 'PHP has less than 12MB available memory and might run out if all modules are loaded. Increase memory_limit in php.ini'."\n";
}
// Check safe_mode off
@ -147,27 +150,30 @@ class getID3
$this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.');
}
if (intval(ini_get('mbstring.func_overload')) > 0) {
$this->warning('WARNING: php.ini contains "mbstring.func_overload = '.ini_get('mbstring.func_overload').'", this may break things.');
if (($mbstring_func_overload = ini_get('mbstring.func_overload')) && ($mbstring_func_overload & 0x02)) {
// http://php.net/manual/en/mbstring.overload.php
// "mbstring.func_overload in php.ini is a positive value that represents a combination of bitmasks specifying the categories of functions to be overloaded. It should be set to 1 to overload the mail() function. 2 for string functions, 4 for regular expression functions"
// getID3 cannot run when string functions are overloaded. It doesn't matter if mail() or ereg* functions are overloaded since getID3 does not use those.
$this->startup_error .= 'WARNING: php.ini contains "mbstring.func_overload = '.ini_get('mbstring.func_overload').'", getID3 cannot run with this setting (bitmask 2 (string functions) cannot be set). Recommended to disable entirely.'."\n";
}
// Check for magic_quotes_runtime
if (function_exists('get_magic_quotes_runtime')) {
if (get_magic_quotes_runtime()) {
return $this->startup_error('magic_quotes_runtime must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_runtime(0) and set_magic_quotes_runtime(1).');
$this->startup_error .= 'magic_quotes_runtime must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_runtime(0) and set_magic_quotes_runtime(1).'."\n";
}
}
// Check for magic_quotes_gpc
if (function_exists('magic_quotes_gpc')) {
if (get_magic_quotes_gpc()) {
return $this->startup_error('magic_quotes_gpc must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_gpc(0) and set_magic_quotes_gpc(1).');
$this->startup_error .= 'magic_quotes_gpc must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_gpc(0) and set_magic_quotes_gpc(1).'."\n";
}
}
// Load support library
if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
$this->startup_error .= 'getid3.lib.php is missing or corrupt';
$this->startup_error .= 'getid3.lib.php is missing or corrupt'."\n";
}
if ($this->option_max_2gb_check === null) {
@ -186,7 +192,7 @@ class getID3
$helperappsdir = GETID3_INCLUDEPATH.'..'.DIRECTORY_SEPARATOR.'helperapps'; // must not have any space in this path
if (!is_dir($helperappsdir)) {
$this->startup_warning .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist';
$this->startup_warning .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist'."\n";
} elseif (strpos(realpath($helperappsdir), ' ') !== false) {
$DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir));
$path_so_far = array();
@ -206,7 +212,7 @@ class getID3
}
}
} else {
$this->startup_warning .= 'GETID3_HELPERAPPSDIR must not have any spaces in it - use 8dot3 naming convention if neccesary. You can run "dir /x" from the commandline to see the correct 8.3-style names.';
$this->startup_warning .= 'GETID3_HELPERAPPSDIR must not have any spaces in it - use 8dot3 naming convention if neccesary. You can run "dir /x" from the commandline to see the correct 8.3-style names.'."\n";
}
}
$path_so_far[] = $value;
@ -216,6 +222,11 @@ class getID3
define('GETID3_HELPERAPPSDIR', $helperappsdir.DIRECTORY_SEPARATOR);
}
if (!empty($this->startup_error)) {
echo $this->startup_error;
throw new getid3_exception($this->startup_error);
}
return true;
}
@ -249,7 +260,9 @@ class getID3
throw new getid3_exception($this->startup_error);
}
if (!empty($this->startup_warning)) {
$this->warning($this->startup_warning);
foreach (explode("\n", $this->startup_warning) as $startup_warning) {
$this->warning($startup_warning);
}
}
// init result array and set parameters
@ -259,7 +272,7 @@ class getID3
$this->info['php_memory_limit'] = (($this->memory_limit > 0) ? $this->memory_limit : false);
// remote files not supported
if (preg_match('/^(ht|f)tp:\/\//', $filename)) {
if (preg_match('#^(ht|f)tp://#', $filename)) {
throw new getid3_exception('Remote files are not supported - please copy the file locally first');
}
@ -426,14 +439,14 @@ class getID3
return $this->error('Format not supported, module "'.$determined_format['include'].'" was removed.');
}
// module requires iconv support
// module requires mb_convert_encoding/iconv support
// Check encoding/iconv support
if (!empty($determined_format['iconv_req']) && !function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) {
$errormessage = 'iconv() support is required for this module ('.$determined_format['include'].') for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. ';
if (!empty($determined_format['iconv_req']) && !function_exists('mb_convert_encoding') && !function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) {
$errormessage = 'mb_convert_encoding() or iconv() support is required for this module ('.$determined_format['include'].') for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. ';
if (GETID3_OS_ISWINDOWS) {
$errormessage .= 'PHP does not have iconv() support. Please enable php_iconv.dll in php.ini, and copy iconv.dll from c:/php/dlls to c:/windows/system32';
$errormessage .= 'PHP does not have mb_convert_encoding() or iconv() support. Please enable php_mbstring.dll / php_iconv.dll in php.ini, and copy php_mbstring.dll / iconv.dll from c:/php/dlls to c:/windows/system32';
} else {
$errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch';
$errormessage .= 'PHP is not compiled with mb_convert_encoding() or iconv() support. Please recompile with the --enable-mbstring / --with-iconv switch';
}
return $this->error($errormessage);
}
@ -568,7 +581,7 @@ class getID3
// AC-3 - audio - Dolby AC-3 / Dolby Digital
'ac3' => array(
'pattern' => '^\x0B\x77',
'pattern' => '^\\x0B\\x77',
'group' => 'audio',
'module' => 'ac3',
'mime_type' => 'audio/ac3',
@ -586,7 +599,7 @@ class getID3
/*
// AA - audio - Audible Audiobook
'aa' => array(
'pattern' => '^.{4}\x57\x90\x75\x36',
'pattern' => '^.{4}\\x57\\x90\\x75\\x36',
'group' => 'audio',
'module' => 'aa',
'mime_type' => 'audio/audible',
@ -594,7 +607,7 @@ class getID3
*/
// AAC - audio - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3)
'adts' => array(
'pattern' => '^\xFF[\xF0-\xF1\xF8-\xF9]',
'pattern' => '^\\xFF[\\xF0-\\xF1\\xF8-\\xF9]',
'group' => 'audio',
'module' => 'aac',
'mime_type' => 'application/octet-stream',
@ -604,7 +617,7 @@ class getID3
// AU - audio - NeXT/Sun AUdio (AU)
'au' => array(
'pattern' => '^\.snd',
'pattern' => '^\\.snd',
'group' => 'audio',
'module' => 'au',
'mime_type' => 'audio/basic',
@ -612,7 +625,7 @@ class getID3
// AMR - audio - Adaptive Multi Rate
'amr' => array(
'pattern' => '^\x23\x21AMR\x0A', // #!AMR[0A]
'pattern' => '^\\x23\\x21AMR\\x0A', // #!AMR[0A]
'group' => 'audio',
'module' => 'amr',
'mime_type' => 'audio/amr',
@ -628,7 +641,7 @@ class getID3
// BONK - audio - Bonk v0.9+
'bonk' => array(
'pattern' => '^\x00(BONK|INFO|META| ID3)',
'pattern' => '^\\x00(BONK|INFO|META| ID3)',
'group' => 'audio',
'module' => 'bonk',
'mime_type' => 'audio/xmms-bonk',
@ -644,7 +657,7 @@ class getID3
// DSS - audio - Digital Speech Standard
'dss' => array(
'pattern' => '^[\x02-\x03]ds[s2]',
'pattern' => '^[\\x02-\\x06]ds[s2]',
'group' => 'audio',
'module' => 'dss',
'mime_type' => 'application/octet-stream',
@ -652,7 +665,7 @@ class getID3
// DTS - audio - Dolby Theatre System
'dts' => array(
'pattern' => '^\x7F\xFE\x80\x01',
'pattern' => '^\\x7F\\xFE\\x80\\x01',
'group' => 'audio',
'module' => 'dts',
'mime_type' => 'audio/dts',
@ -737,7 +750,7 @@ class getID3
// MPC - audio - Musepack / MPEGplus
'mpc' => array(
'pattern' => '^(MPCK|MP\+|[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0])',
'pattern' => '^(MPCK|MP\\+|[\\x00\\x01\\x10\\x11\\x40\\x41\\x50\\x51\\x80\\x81\\x90\\x91\\xC0\\xC1\\xD0\\xD1][\\x20-\\x37][\\x00\\x20\\x40\\x60\\x80\\xA0\\xC0\\xE0])',
'group' => 'audio',
'module' => 'mpc',
'mime_type' => 'audio/x-musepack',
@ -745,7 +758,7 @@ class getID3
// MP3 - audio - MPEG-audio Layer 3 (very similar to AAC-ADTS)
'mp3' => array(
'pattern' => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\x0B\x10-\x1B\x20-\x2B\x30-\x3B\x40-\x4B\x50-\x5B\x60-\x6B\x70-\x7B\x80-\x8B\x90-\x9B\xA0-\xAB\xB0-\xBB\xC0-\xCB\xD0-\xDB\xE0-\xEB\xF0-\xFB]',
'pattern' => '^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\x0B\\x10-\\x1B\\x20-\\x2B\\x30-\\x3B\\x40-\\x4B\\x50-\\x5B\\x60-\\x6B\\x70-\\x7B\\x80-\\x8B\\x90-\\x9B\\xA0-\\xAB\\xB0-\\xBB\\xC0-\\xCB\\xD0-\\xDB\\xE0-\\xEB\\xF0-\\xFB]',
'group' => 'audio',
'module' => 'mp3',
'mime_type' => 'audio/mpeg',
@ -753,7 +766,7 @@ class getID3
// OFR - audio - OptimFROG
'ofr' => array(
'pattern' => '^(\*RIFF|OFR)',
'pattern' => '^(\\*RIFF|OFR)',
'group' => 'audio',
'module' => 'optimfrog',
'mime_type' => 'application/octet-stream',
@ -779,7 +792,7 @@ class getID3
// TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org)
'tta' => array(
'pattern' => '^TTA', // could also be '^TTA(\x01|\x02|\x03|2|1)'
'pattern' => '^TTA', // could also be '^TTA(\\x01|\\x02|\\x03|2|1)'
'group' => 'audio',
'module' => 'tta',
'mime_type' => 'application/octet-stream',
@ -814,7 +827,7 @@ class getID3
// ASF - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio
'asf' => array(
'pattern' => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C',
'pattern' => '^\\x30\\x26\\xB2\\x75\\x8E\\x66\\xCF\\x11\\xA6\\xD9\\x00\\xAA\\x00\\x62\\xCE\\x6C',
'group' => 'audio-video',
'module' => 'asf',
'mime_type' => 'video/x-ms-asf',
@ -831,7 +844,7 @@ class getID3
// FLV - audio/video - FLash Video
'flv' => array(
'pattern' => '^FLV\x01',
'pattern' => '^FLV[\\x01]',
'group' => 'audio-video',
'module' => 'flv',
'mime_type' => 'video/x-flv',
@ -839,7 +852,7 @@ class getID3
// MKAV - audio/video - Mastroka
'matroska' => array(
'pattern' => '^\x1A\x45\xDF\xA3',
'pattern' => '^\\x1A\\x45\\xDF\\xA3',
'group' => 'audio-video',
'module' => 'matroska',
'mime_type' => 'video/x-matroska', // may also be audio/x-matroska
@ -847,7 +860,7 @@ class getID3
// MPEG - audio/video - MPEG (Moving Pictures Experts Group)
'mpeg' => array(
'pattern' => '^\x00\x00\x01(\xBA|\xB3)',
'pattern' => '^\\x00\\x00\\x01[\\xB3\\xBA]',
'group' => 'audio-video',
'module' => 'mpeg',
'mime_type' => 'video/mpeg',
@ -890,7 +903,7 @@ class getID3
// Real - audio/video - RealAudio, RealVideo
'real' => array(
'pattern' => '^(\\.RMF|\\.ra)',
'pattern' => '^\\.(RMF|ra)',
'group' => 'audio-video',
'module' => 'real',
'mime_type' => 'audio/x-realaudio',
@ -906,7 +919,7 @@ class getID3
// TS - audio/video - MPEG-2 Transport Stream
'ts' => array(
'pattern' => '^(\x47.{187}){10,}', // packets are 188 bytes long and start with 0x47 "G". Check for at least 10 packets matching this pattern
'pattern' => '^(\\x47.{187}){10,}', // packets are 188 bytes long and start with 0x47 "G". Check for at least 10 packets matching this pattern
'group' => 'audio-video',
'module' => 'ts',
'mime_type' => 'video/MP2T',
@ -937,7 +950,7 @@ class getID3
// JPEG - still image - Joint Photographic Experts Group (JPEG)
'jpg' => array(
'pattern' => '^\xFF\xD8\xFF',
'pattern' => '^\\xFF\\xD8\\xFF',
'group' => 'graphic',
'module' => 'jpg',
'mime_type' => 'image/jpeg',
@ -947,7 +960,7 @@ class getID3
// PCD - still image - Kodak Photo CD
'pcd' => array(
'pattern' => '^.{2048}PCD_IPI\x00',
'pattern' => '^.{2048}PCD_IPI\\x00',
'group' => 'graphic',
'module' => 'pcd',
'mime_type' => 'image/x-photo-cd',
@ -958,7 +971,7 @@ class getID3
// PNG - still image - Portable Network Graphics (PNG)
'png' => array(
'pattern' => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A',
'pattern' => '^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A',
'group' => 'graphic',
'module' => 'png',
'mime_type' => 'image/png',
@ -969,7 +982,7 @@ class getID3
// SVG - still image - Scalable Vector Graphics (SVG)
'svg' => array(
'pattern' => '(<!DOCTYPE svg PUBLIC |xmlns="http:\/\/www\.w3\.org\/2000\/svg")',
'pattern' => '(<!DOCTYPE svg PUBLIC |xmlns="http://www\\.w3\\.org/2000/svg")',
'group' => 'graphic',
'module' => 'svg',
'mime_type' => 'image/svg+xml',
@ -980,7 +993,7 @@ class getID3
// TIFF - still image - Tagged Information File Format (TIFF)
'tiff' => array(
'pattern' => '^(II\x2A\x00|MM\x00\x2A)',
'pattern' => '^(II\\x2A\\x00|MM\\x00\\x2A)',
'group' => 'graphic',
'module' => 'tiff',
'mime_type' => 'image/tiff',
@ -991,7 +1004,7 @@ class getID3
// EFAX - still image - eFax (TIFF derivative)
'efax' => array(
'pattern' => '^\xDC\xFE',
'pattern' => '^\\xDC\\xFE',
'group' => 'graphic',
'module' => 'efax',
'mime_type' => 'image/efax',
@ -1015,7 +1028,7 @@ class getID3
// RAR - data - RAR compressed data
'rar' => array(
'pattern' => '^Rar\!',
'pattern' => '^Rar\\!',
'group' => 'archive',
'module' => 'rar',
'mime_type' => 'application/octet-stream',
@ -1025,7 +1038,7 @@ class getID3
// SZIP - audio/data - SZIP compressed data
'szip' => array(
'pattern' => '^SZ\x0A\x04',
'pattern' => '^SZ\\x0A\\x04',
'group' => 'archive',
'module' => 'szip',
'mime_type' => 'application/octet-stream',
@ -1035,7 +1048,7 @@ class getID3
// TAR - data - TAR compressed data
'tar' => array(
'pattern' => '^.{100}[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20\x00]{12}[0-9\x20\x00]{12}',
'pattern' => '^.{100}[0-9\\x20]{7}\\x00[0-9\\x20]{7}\\x00[0-9\\x20]{7}\\x00[0-9\\x20\\x00]{12}[0-9\\x20\\x00]{12}',
'group' => 'archive',
'module' => 'tar',
'mime_type' => 'application/x-tar',
@ -1045,7 +1058,7 @@ class getID3
// GZIP - data - GZIP compressed data
'gz' => array(
'pattern' => '^\x1F\x8B\x08',
'pattern' => '^\\x1F\\x8B\\x08',
'group' => 'archive',
'module' => 'gzip',
'mime_type' => 'application/x-gzip',
@ -1055,7 +1068,7 @@ class getID3
// ZIP - data - ZIP compressed data
'zip' => array(
'pattern' => '^PK\x03\x04',
'pattern' => '^PK\\x03\\x04',
'group' => 'archive',
'module' => 'zip',
'mime_type' => 'application/zip',
@ -1068,7 +1081,7 @@ class getID3
// PAR2 - data - Parity Volume Set Specification 2.0
'par2' => array (
'pattern' => '^PAR2\x00PKT',
'pattern' => '^PAR2\\x00PKT',
'group' => 'misc',
'module' => 'par2',
'mime_type' => 'application/octet-stream',
@ -1078,7 +1091,7 @@ class getID3
// PDF - data - Portable Document Format
'pdf' => array(
'pattern' => '^\x25PDF',
'pattern' => '^\\x25PDF',
'group' => 'misc',
'module' => 'pdf',
'mime_type' => 'application/pdf',
@ -1088,7 +1101,7 @@ class getID3
// MSOFFICE - data - ZIP compressed data
'msoffice' => array(
'pattern' => '^\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1', // D0CF11E == DOCFILE == Microsoft Office Document
'pattern' => '^\\xD0\\xCF\\x11\\xE0\\xA1\\xB1\\x1A\\xE1', // D0CF11E == DOCFILE == Microsoft Office Document
'group' => 'misc',
'module' => 'msoffice',
'mime_type' => 'application/octet-stream',
@ -1129,14 +1142,14 @@ class getID3
}
if (preg_match('#\.mp[123a]$#i', $filename)) {
if (preg_match('#\\.mp[123a]$#i', $filename)) {
// Too many mp3 encoders on the market put gabage in front of mpeg files
// use assume format on these if format detection failed
$GetFileFormatArray = $this->GetFileFormatArray();
$info = $GetFileFormatArray['mp3'];
$info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
return $info;
} elseif (preg_match('/\.cue$/i', $filename) && preg_match('#FILE "[^"]+" (BINARY|MOTOROLA|AIFF|WAVE|MP3)#', $filedata)) {
} elseif (preg_match('#\\.cue$#i', $filename) && preg_match('#FILE "[^"]+" (BINARY|MOTOROLA|AIFF|WAVE|MP3)#', $filedata)) {
// there's not really a useful consistent "magic" at the beginning of .cue files to identify them
// so until I think of something better, just go by filename if all other format checks fail
// and verify there's at least one instance of "TRACK xx AUDIO" in the file
@ -1237,36 +1250,14 @@ class getID3
continue;
}
$this->CharConvert($this->info['tags'][$tag_name], $this->info[$comment_name]['encoding']); // only copy gets converted!
if ($this->option_tags_html) {
foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
$this->info['tags_html'][$tag_name][$tag_key] = getid3_lib::recursiveMultiByteCharString2HTML($valuearray, $encoding);
$this->info['tags_html'][$tag_name][$tag_key] = getid3_lib::recursiveMultiByteCharString2HTML($valuearray, $this->info[$comment_name]['encoding']);
}
}
// ID3v1 encoding detection hack start
// ID3v1 is defined as always using ISO-8859-1 encoding, but it is not uncommon to find files tagged with ID3v1 using Windows-1251 or other character sets
// Since ID3v1 has no concept of character sets there is no certain way to know we have the correct non-ISO-8859-1 character set, but we can guess
if ($comment_name == 'id3v1') {
if ($encoding == 'ISO-8859-1') {
if (function_exists('iconv')) {
foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
foreach ($valuearray as $key => $value) {
if (preg_match('#^[\\x80-\\xFF]+$#', $value)) {
foreach (array('windows-1251', 'KOI8-R') as $id3v1_bad_encoding) {
if (@iconv($id3v1_bad_encoding, $id3v1_bad_encoding, $value) === $value) {
$encoding = $id3v1_bad_encoding;
break 3;
}
}
}
}
}
}
}
}
// ID3v1 encoding detection hack end
$this->CharConvert($this->info['tags'][$tag_name], $encoding); // only copy gets converted!
}
}
@ -1699,7 +1690,23 @@ abstract class getid3_handler {
if (!getid3_lib::intValueSupported($pos)) {
throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().') because beyond PHP filesystem limit', 10);
}
return fread($this->getid3->fp, $bytes);
//return fread($this->getid3->fp, $bytes);
/*
* http://www.getid3.org/phpBB3/viewtopic.php?t=1930
* "I found out that the root cause for the problem was how getID3 uses the PHP system function fread().
* It seems to assume that fread() would always return as many bytes as were requested.
* However, according the PHP manual (http://php.net/manual/en/function.fread.php), this is the case only with regular local files, but not e.g. with Linux pipes.
* The call may return only part of the requested data and a new call is needed to get more."
*/
$contents = '';
do {
$part = fread($this->getid3->fp, $bytes);
$partLength = strlen($part);
$bytes -= $partLength;
$contents .= $part;
} while (($bytes > 0) && ($partLength > 0));
return $contents;
}
protected function fseek($bytes, $whence=SEEK_SET) {

2
getid3/module.audio-video.asf.php

@ -349,7 +349,7 @@ class getid3_asf extends getid3_handler {
if ($thisfile_asf_codeclistobject_codecentries_current['type_raw'] == 2) { // audio codec
if (strpos($thisfile_asf_codeclistobject_codecentries_current['description'], ',') === false) {
$info['warning'][] = '[asf][codec_list_object][codec_entries]['.$CodecEntryCounter.'][description] expected to contain comma-seperated list of parameters: "'.$thisfile_asf_codeclistobject_codecentries_current['description'].'"';
$info['warning'][] = '[asf][codec_list_object][codec_entries]['.$CodecEntryCounter.'][description] expected to contain comma-separated list of parameters: "'.$thisfile_asf_codeclistobject_codecentries_current['description'].'"';
} else {
list($AudioCodecBitrate, $AudioCodecFrequency, $AudioCodecChannels) = explode(',', $this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['description']));

7
getid3/module.audio-video.matroska.php

@ -566,8 +566,11 @@ class getid3_matroska extends getid3_handler
$this->unhandledElement('seekhead.seek', __LINE__, $sub_seek_entry); }
break;
}
if ($seek_entry['target_id'] != EBML_ID_CLUSTER || !self::$hide_clusters) { // collect clusters only if required
if (!isset($seek_entry['target_id'])) {
$this->warning('seek_entry[target_id] unexpectedly not set at '.$seek_entry['offset']);
break;
}
if (($seek_entry['target_id'] != EBML_ID_CLUSTER) || !self::$hide_clusters) { // collect clusters only if required
$info['matroska']['seek'][] = $seek_entry;
}
break;

12
getid3/module.audio-video.quicktime.php

@ -35,7 +35,7 @@ class getid3_quicktime extends getid3_handler
$offset = 0;
$atomcounter = 0;
$atom_data_read_buffer_size = ($info['php_memory_limit'] ? round($info['php_memory_limit'] / 2) : $this->getid3->option_fread_buffer_size * 1024); // allow [default: 32MB] if PHP configured with no memory_limit
$atom_data_read_buffer_size = max($this->getid3->option_fread_buffer_size * 1024, ($info['php_memory_limit'] ? round($info['php_memory_limit'] / 4) : 1024)); // set read buffer to 25% of PHP memory limit (if one is specified), otherwise use option_fread_buffer_size [default: 32MB]
while ($offset < $info['avdataend']) {
if (!getid3_lib::intValueSupported($offset)) {
$info['error'][] = 'Unable to parse atom at offset '.$offset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions';
@ -173,10 +173,14 @@ class getid3_quicktime extends getid3_handler
}
}
}
if (($info['audio']['dataformat'] == 'mp4') && empty($info['video']['resolution_x'])) {
if ($info['audio']['dataformat'] == 'mp4') {
$info['fileformat'] = 'mp4';
$info['mime_type'] = 'audio/mp4';
unset($info['video']['dataformat']);
if (empty($info['video']['resolution_x'])) {
$info['mime_type'] = 'audio/mp4';
unset($info['video']['dataformat']);
} else {
$info['mime_type'] = 'video/mp4';
}
}
if (!$this->ReturnAtomData) {

42
getid3/module.audio.dss.php

@ -24,8 +24,8 @@ class getid3_dss extends getid3_handler
$this->fseek($info['avdataoffset']);
$DSSheader = $this->fread(1540);
if (!preg_match('#^(\x02|\x03)ds[s2]#', $DSSheader)) {
$info['error'][] = 'Expecting "[02-03] 64 73 [73|32]" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($DSSheader, 0, 4)).'"';
if (!preg_match('#^[\\x02-\\x06]ds[s2]#', $DSSheader)) {
$info['error'][] = 'Expecting "[02-06] 64 73 [73|32]" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($DSSheader, 0, 4)).'"';
return false;
}
@ -38,27 +38,33 @@ class getid3_dss extends getid3_handler
$info['audio']['dataformat'] = substr($DSSheader, 1, 3); // "dss" or "ds2"
$info['audio']['bitrate_mode'] = 'cbr';
$info['dss']['version'] = ord(substr($DSSheader, 0, 1));
$info['dss']['hardware'] = trim(substr($DSSheader, 12, 16)); // identification string for hardware used to create the file, e.g. "DPM 9600", "DS2400"
$info['dss']['unknown1'] = getid3_lib::LittleEndian2Int(substr($DSSheader, 28, 4));
$info['dss']['version'] = ord(substr($DSSheader, 0, 1));
$info['dss']['hardware'] = trim(substr($DSSheader, 12, 16)); // identification string for hardware used to create the file, e.g. "DPM 9600", "DS2400"
$info['dss']['unknown1'] = getid3_lib::LittleEndian2Int(substr($DSSheader, 28, 4));
// 32-37 = "FE FF FE FF F7 FF" in all the sample files I've seen
$info['dss']['date_create'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 38, 12));
$info['dss']['date_complete'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 50, 12));
$info['dss']['playtime_sec'] = intval((substr($DSSheader, 62, 2) * 3600) + (substr($DSSheader, 64, 2) * 60) + substr($DSSheader, 66, 2)); // approximate file playtime in HHMMSS
$info['dss']['playtime_ms'] = getid3_lib::LittleEndian2Int(substr($DSSheader, 512, 4)); // exact file playtime in milliseconds. Has also been observed at offset 530 in one sample file, with something else (unknown) at offset 512
$info['dss']['priority'] = ord(substr($DSSheader, 793, 1));
$info['dss']['comments'] = trim(substr($DSSheader, 798, 100));
$info['dss']['sample_rate_index'] = ord(substr($DSSheader, 1538, 1)); // this isn't certain, this may or may not be where the sample rate info is stored, but it seems consistent on my small selection of sample files
$info['dss']['date_create_unix'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 38, 12));
$info['dss']['date_complete_unix'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 50, 12));
$info['dss']['playtime_sec'] = intval((substr($DSSheader, 62, 2) * 3600) + (substr($DSSheader, 64, 2) * 60) + substr($DSSheader, 66, 2)); // approximate file playtime in HHMMSS
if ($info['dss']['version'] <= 3) {
$info['dss']['playtime_ms'] = getid3_lib::LittleEndian2Int(substr($DSSheader, 512, 4)); // exact file playtime in milliseconds. Has also been observed at offset 530 in one sample file, with something else (unknown) at offset 512
$info['dss']['priority'] = ord(substr($DSSheader, 793, 1));
$info['dss']['comments'] = trim(substr($DSSheader, 798, 100));
$info['dss']['sample_rate_index'] = ord(substr($DSSheader, 1538, 1)); // this isn't certain, this may or may not be where the sample rate info is stored, but it seems consistent on my small selection of sample files
$info['audio']['sample_rate'] = $this->DSSsampleRateLookup($info['dss']['sample_rate_index']);
} else {
$this->getid3->warning('DSS above version 3 not fully supported in this version of getID3. Any additional documentation or format specifications would be welcome. This file is version '.$info['dss']['version']);
}
$info['audio']['bits_per_sample'] = 16; // maybe, maybe not -- most compressed audio formats don't have a fixed bits-per-sample value, but this is a reasonable approximation
$info['audio']['sample_rate'] = $this->DSSsampleRateLookup($info['dss']['sample_rate_index']);
$info['audio']['channels'] = 1;
$info['playtime_seconds'] = $info['dss']['playtime_ms'] / 1000;
if (floor($info['dss']['playtime_ms'] / 1000) != $info['dss']['playtime_sec']) {
// *should* just be playtime_ms / 1000 but at least one sample file has playtime_ms at offset 530 instead of offset 512, so safety check
if (!empty($info['dss']['playtime_ms']) && (floor($info['dss']['playtime_ms'] / 1000) == $info['dss']['playtime_sec'])) { // *should* just be playtime_ms / 1000 but at least one sample file has playtime_ms at offset 530 instead of offset 512, so safety check
$info['playtime_seconds'] = $info['dss']['playtime_ms'] / 1000;
} else {
$info['playtime_seconds'] = $info['dss']['playtime_sec'];
$this->getid3->warning('playtime_ms ('.number_format($info['dss']['playtime_ms'] / 1000, 3).') does not match playtime_sec ('.number_format($info['dss']['playtime_sec']).') - using playtime_sec value');
if (!empty($info['dss']['playtime_ms'])) {
$this->getid3->warning('playtime_ms ('.number_format($info['dss']['playtime_ms'] / 1000, 3).') does not match playtime_sec ('.number_format($info['dss']['playtime_sec']).') - using playtime_sec value');
}
}
$info['audio']['bitrate'] = ($info['filesize'] * 8) / $info['playtime_seconds'];
@ -84,7 +90,7 @@ class getid3_dss extends getid3_handler
0x15 => 8000,
);
if (!array_key_exists($sample_rate_index, $dssSampleRateLookup)) {
$this->getid3->warning('unknown sample_rate_index: '.$sample_rate_index);
$this->getid3->warning('unknown sample_rate_index: 0x'.strtoupper(dechex($sample_rate_index)));
return false;
}
return $dssSampleRateLookup[$sample_rate_index];

11
getid3/module.graphic.jpg.php

@ -68,7 +68,18 @@ class getid3_jpg extends getid3_handler
if (substr($imageinfo['APP1'], 0, 4) == 'Exif') {
//$info['warning'][] = 'known issue: https://bugs.php.net/bug.php?id=62523';
//return false;
set_error_handler(function($errno, $errstr, $errfile, $errline, array $errcontext) {
if (!(error_reporting() & $errno)) {
// error is not specified in the error_reporting setting, so we ignore it
return false;
}
$errcontext['info']['warning'][] = 'Error parsing EXIF data ('.$errstr.')';
});
$info['jpg']['exif'] = exif_read_data($info['filenamepath'], null, true, false);
restore_error_handler();
} else {
$info['warning'][] = 'exif_read_data() cannot parse non-EXIF data in APP1 (expected "Exif", found "'.substr($imageinfo['APP1'], 0, 4).'")';
}

2
getid3/module.tag.apetag.php

@ -119,7 +119,7 @@ class getid3_apetag extends getid3_handler
$item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
$offset += 4;
if (strstr(substr($APEtagData, $offset), "\x00") === false) {
$info['error'][] = 'Cannot find null-byte (0x00) seperator between ItemKey #'.$i.' and value. ItemKey starts '.$offset.' bytes into the APE tag, at file offset '.($thisfile_ape['tag_offset_start'] + $offset);
$info['error'][] = 'Cannot find null-byte (0x00) separator between ItemKey #'.$i.' and value. ItemKey starts '.$offset.' bytes into the APE tag, at file offset '.($thisfile_ape['tag_offset_start'] + $offset);
return false;
}
$ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset;

21
getid3/module.tag.id3v1.php

@ -60,6 +60,26 @@ class getid3_id3v1 extends getid3_handler
foreach ($ParsedID3v1 as $key => $value) {
$ParsedID3v1['comments'][$key][0] = $value;
}
// ID3v1 encoding detection hack START
// ID3v1 is defined as always using ISO-8859-1 encoding, but it is not uncommon to find files tagged with ID3v1 using Windows-1251 or other character sets
// Since ID3v1 has no concept of character sets there is no certain way to know we have the correct non-ISO-8859-1 character set, but we can guess
$ID3v1encoding = 'ISO-8859-1';
foreach ($ParsedID3v1['comments'] as $tag_key => $valuearray) {
foreach ($valuearray as $key => $value) {
if (preg_match('#^[\\x00-\\x40\\xA8\\B8\\x80-\\xFF]+$#', $value)) {
foreach (array('Windows-1251', 'KOI8-R') as $id3v1_bad_encoding) {
if (function_exists('mb_convert_encoding') && @mb_convert_encoding($value, $id3v1_bad_encoding, $id3v1_bad_encoding) === $value) {
$ID3v1encoding = $id3v1_bad_encoding;
break 3;
} elseif (function_exists('iconv') && @iconv($id3v1_bad_encoding, $id3v1_bad_encoding, $value) === $value) {
$ID3v1encoding = $id3v1_bad_encoding;
break 3;
}
}
}
}
}
// ID3v1 encoding detection hack END
// ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces
$GoodFormatID3v1tag = $this->GenerateID3v1Tag(
@ -80,6 +100,7 @@ class getid3_id3v1 extends getid3_handler
$ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128;
$info['id3v1'] = $ParsedID3v1;
$info['id3v1']['encoding'] = $ID3v1encoding;
}
if (substr($preid3v1, 0, 3) == 'TAG') {

120
getid3/module.tag.id3v2.php

@ -329,9 +329,9 @@ class getid3_id3v2 extends getid3_handler
break; // skip rest of ID3v2 header
}
if ($frame_name == 'COM ') {
$info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably others too)]';
$frame_name = 'COMM';
if ($iTunesBrokenFrameNameFixed = self::ID3v22iTunesBrokenFrameName($frame_name)) {
$info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1", "v7.0.0.70" are known-guilty, probably others too)]. Translated frame name from "'.str_replace("\x00", ' ', $frame_name).'" to "'.$iTunesBrokenFrameNameFixed.'" for parsing.';
$frame_name = $iTunesBrokenFrameNameFixed;
}
if (($frame_size <= strlen($framedata)) && ($this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion))) {
@ -504,18 +504,27 @@ class getid3_id3v2 extends getid3_handler
// ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)'
// ID3v2.4.x: '21' $00 'Eurodisco' $00
$clean_genres = array();
if (strpos($genrestring, "\x00") === false) {
$genrestring = preg_replace('#\(([0-9]{1,3})\)#', '$1'."\x00", $genrestring);
// hack-fixes for some badly-written ID3v2.3 taggers, while trying not to break correctly-written tags
if (($this->getid3->info['id3v2']['majorversion'] == 3) && !preg_match('#[\x00]#', $genrestring)) {
// note: MusicBrainz Picard incorrectly stores plaintext genres separated by "/" when writing in ID3v2.3 mode, hack-fix here:
// replace / with NULL, then replace back the two ID3v1 genres that legitimately have "/" as part of the single genre name
if (preg_match('#/#', $genrestring)) {
$genrestring = str_replace('/', "\x00", $genrestring);
$genrestring = str_replace('Pop'."\x00".'Funk', 'Pop/Funk', $genrestring);
$genrestring = str_replace('Rock'."\x00".'Rock', 'Folk/Rock', $genrestring);
}
// some other taggers separate multiple genres with semicolon, e.g. "Heavy Metal;Thrash Metal;Metal"
if (preg_match('#;#', $genrestring)) {
$genrestring = str_replace(';', "\x00", $genrestring);
}
}
// note: MusicBrainz Picard incorrectly stores plaintext genres separated by "/" when writing in ID3v2.3 mode, hack-fix here:
// replace / with NULL, then replace back the two ID3v1 genres that legitimately have "/" as part of the single genre name
$genrestring = str_replace('/', "\x00", $genrestring);
$genrestring = str_replace('Pop'."\x00".'Funk', 'Pop/Funk', $genrestring);
$genrestring = str_replace('Rock'."\x00".'Rock', 'Folk/Rock', $genrestring);
// some other taggers separate multiple genres with semicolon, e.g. "Heavy Metal;Thrash Metal;Metal"
$genrestring = str_replace(';', "\x00", $genrestring);
if (strpos($genrestring, "\x00") === false) {
$genrestring = preg_replace('#\(([0-9]{1,3})\)#', '$1'."\x00", $genrestring);
}
$genre_elements = explode("\x00", $genrestring);
foreach ($genre_elements as $element) {
@ -1744,7 +1753,7 @@ class getid3_id3v2 extends getid3_handler
$parsedFrame['pricepaid']['value'] = substr($frame_pricepaid, 3);
$parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8);
if (!$this->IsValidDateStampString($parsedFrame['purchasedate'])) {
if ($this->IsValidDateStampString($parsedFrame['purchasedate'])) {
$parsedFrame['purchasedateunix'] = mktime (0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4));
}
$frame_offset += 8;
@ -3644,5 +3653,90 @@ class getid3_id3v2 extends getid3_handler
return (($majorversion == 2) ? 6 : 10);
}
public static function ID3v22iTunesBrokenFrameName($frame_name) {
// iTunes (multiple versions) has been known to write ID3v2.3 style frames
// but use ID3v2.2 frame names, right-padded using either [space] or [null]
// to make them fit in the 4-byte frame name space of the ID3v2.3 frame.
// This function will detect and translate the corrupt frame name into ID3v2.3 standard.
static $ID3v22_iTunes_BrokenFrames = array(
'BUF' => 'RBUF', // Recommended buffer size
'CNT' => 'PCNT', // Play counter
'COM' => 'COMM', // Comments
'CRA' => 'AENC', // Audio encryption
'EQU' => 'EQUA', // Equalisation
'ETC' => 'ETCO', // Event timing codes
'GEO' => 'GEOB', // General encapsulated object
'IPL' => 'IPLS', // Involved people list
'LNK' => 'LINK', // Linked information
'MCI' => 'MCDI', // Music CD identifier
'MLL' => 'MLLT', // MPEG location lookup table
'PIC' => 'APIC', // Attached picture
'POP' => 'POPM', // Popularimeter
'REV' => 'RVRB', // Reverb
'RVA' => 'RVAD', // Relative volume adjustment
'SLT' => 'SYLT', // Synchronised lyric/text
'STC' => 'SYTC', // Synchronised tempo codes
'TAL' => 'TALB', // Album/Movie/Show title
'TBP' => 'TBPM', // BPM (beats per minute)
'TCM' => 'TCOM', // Composer
'TCO' => 'TCON', // Content type
'TCP' => 'TCMP', // Part of a compilation
'TCR' => 'TCOP', // Copyright message
'TDA' => 'TDAT', // Date
'TDY' => 'TDLY', // Playlist delay
'TEN' => 'TENC', // Encoded by
'TFT' => 'TFLT', // File type
'TIM' => 'TIME', // Time
'TKE' => 'TKEY', // Initial key
'TLA' => 'TLAN', // Language(s)
'TLE' => 'TLEN', // Length
'TMT' => 'TMED', // Media type
'TOA' => 'TOPE', // Original artist(s)/performer(s)
'TOF' => 'TOFN', // Original filename
'TOL' => 'TOLY', // Original lyricist(s)/text writer(s)
'TOR' => 'TORY', // Original release year
'TOT' => 'TOAL', // Original album/movie/show title
'TP1' => 'TPE1', // Lead performer(s)/Soloist(s)
'TP2' => 'TPE2', // Band/orchestra/accompaniment
'TP3' => 'TPE3', // Conductor/performer refinement
'TP4' => 'TPE4', // Interpreted, remixed, or otherwise modified by
'TPA' => 'TPOS', // Part of a set
'TPB' => 'TPUB', // Publisher
'TRC' => 'TSRC', // ISRC (international standard recording code)
'TRD' => 'TRDA', // Recording dates
'TRK' => 'TRCK', // Track number/Position in set
'TS2' => 'TSO2', // Album-Artist sort order
'TSA' => 'TSOA', // Album sort order
'TSC' => 'TSOC', // Composer sort order
'TSI' => 'TSIZ', // Size
'TSP' => 'TSOP', // Performer sort order
'TSS' => 'TSSE', // Software/Hardware and settings used for encoding
'TST' => 'TSOT', // Title sort order
'TT1' => 'TIT1', // Content group description
'TT2' => 'TIT2', // Title/songname/content description
'TT3' => 'TIT3', // Subtitle/Description refinement
'TXT' => 'TEXT', // Lyricist/Text writer
'TXX' => 'TXXX', // User defined text information frame
'TYE' => 'TYER', // Year
'UFI' => 'UFID', // Unique file identifier
'ULT' => 'USLT', // Unsynchronised lyric/text transcription
'WAF' => 'WOAF', // Official audio file webpage
'WAR' => 'WOAR', // Official artist/performer webpage
'WAS' => 'WOAS', // Official audio source webpage
'WCM' => 'WCOM', // Commercial information
'WCP' => 'WCOP', // Copyright/Legal information
'WPB' => 'WPUB', // Publishers official webpage
'WXX' => 'WXXX', // User defined URL link frame
);
if (strlen($frame_name) == 4) {
if ((substr($frame_name, 3, 1) == ' ') || (substr($frame_name, 3, 1) == "\x00")) {
if (isset($ID3v22_iTunes_BrokenFrames[substr($frame_name, 0, 3)])) {
return $ID3v22_iTunes_BrokenFrames[substr($frame_name, 0, 3)];
}
}
}
return false;
}
}

358
getid3/write.id3v2.php

@ -815,7 +815,7 @@ class getid3_write_id3v2
// Counter $xx xx xx xx (xx ...)
if (!$this->IsWithinBitRange($source_data_array['rating'], 8, false)) {
$this->errors[] = 'Invalid Rating byte in '.$frame_name.' ('.$source_data_array['rating'].') (range = 0 to 255)';
} elseif (!IsValidEmail($source_data_array['email'])) {
} elseif (!$this->IsValidEmail($source_data_array['email'])) {
$this->errors[] = 'Invalid Email in '.$frame_name.' ('.$source_data_array['email'].')';
} else {
$framedata .= str_replace("\x00", '', $source_data_array['email'])."\x00";
@ -1183,7 +1183,6 @@ class getid3_write_id3v2
$PreviousFrames = array();
return true;
}
if ($this->majorversion == 4) {
switch ($frame_name) {
case 'UFID':
@ -1744,14 +1743,14 @@ class getid3_write_id3v2
return false;
}
public function ID3v2IsValidRGADname($RGADname) {
public static function ID3v2IsValidRGADname($RGADname) {
if (($RGADname >= 0) && ($RGADname <= 2)) {
return true;
}
return false;
}
public function ID3v2IsValidRGADoriginator($RGADoriginator) {
public static function ID3v2IsValidRGADoriginator($RGADoriginator) {
if (($RGADoriginator >= 0) && ($RGADoriginator <= 3)) {
return true;
}
@ -1771,7 +1770,7 @@ class getid3_write_id3v2
return isset($ID3v2IsValidTextEncoding_cache[$this->majorversion][$textencodingbyte]);
}
public function Unsynchronise($data) {
public static function Unsynchronise($data) {
// Whenever a false synchronisation is found within the tag, one zeroed
// byte is inserted after the first false synchronisation byte. The
// format of a correct sync that should be altered by ID3 encoders is as
@ -1840,14 +1839,11 @@ class getid3_write_id3v2
}
}
public function IsValidMIMEstring($mimestring) {
if ((strlen($mimestring) >= 3) && (strpos($mimestring, '/') > 0) && (strpos($mimestring, '/') < (strlen($mimestring) - 1))) {
return true;
}
return false;
public static function IsValidMIMEstring($mimestring) {
return preg_match('#^.+/.+$#', $mimestring);
}
public function IsWithinBitRange($number, $maxbits, $signed=false) {
public static function IsWithinBitRange($number, $maxbits, $signed=false) {
if ($signed) {
if (($number > (0 - pow(2, $maxbits - 1))) && ($number <= pow(2, $maxbits - 1))) {
return true;
@ -1860,18 +1856,15 @@ class getid3_write_id3v2
return false;
}
public function safe_parse_url($url) {
$parts = @parse_url($url);
$parts['scheme'] = (isset($parts['scheme']) ? $parts['scheme'] : '');
$parts['host'] = (isset($parts['host']) ? $parts['host'] : '');
$parts['user'] = (isset($parts['user']) ? $parts['user'] : '');
$parts['pass'] = (isset($parts['pass']) ? $parts['pass'] : '');
$parts['path'] = (isset($parts['path']) ? $parts['path'] : '');
$parts['query'] = (isset($parts['query']) ? $parts['query'] : '');
return $parts;
public static function IsValidEmail($email) {
if (function_exists('filter_var')) {
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
// VERY crude email validation
return preg_match('#^[^ ]+@[a-z\\-\\.]+\\.[a-z]{2,}$#', $email);
}
public function IsValidURL($url, $allowUserPass=false) {
public static function IsValidURL($url, $allowUserPass=false) {
if ($url == '') {
return false;
}
@ -1882,6 +1875,10 @@ class getid3_write_id3v2
return false;
}
}
// 2016-06-08: relax URL checking to avoid falsely rejecting valid URLs, leave URL validation to the user
// http://www.getid3.org/phpBB3/viewtopic.php?t=1926
return true;
/*
if ($parts = $this->safe_parse_url($url)) {
if (($parts['scheme'] != 'http') && ($parts['scheme'] != 'https') && ($parts['scheme'] != 'ftp') && ($parts['scheme'] != 'gopher')) {
return false;
@ -1900,6 +1897,18 @@ class getid3_write_id3v2
}
}
return false;
*/
}
public static function safe_parse_url($url) {
$parts = @parse_url($url);
$parts['scheme'] = (isset($parts['scheme']) ? $parts['scheme'] : '');
$parts['host'] = (isset($parts['host']) ? $parts['host'] : '');
$parts['user'] = (isset($parts['user']) ? $parts['user'] : '');
$parts['pass'] = (isset($parts['pass']) ? $parts['pass'] : '');
$parts['path'] = (isset($parts['path']) ? $parts['path'] : '');
$parts['query'] = (isset($parts['query']) ? $parts['query'] : '');
return $parts;
}
public static function ID3v2ShortFrameNameLookup($majorversion, $long_description) {
@ -1908,152 +1917,189 @@ class getid3_write_id3v2
if (empty($ID3v2ShortFrameNameLookup)) {
// The following are unique to ID3v2.2
$ID3v2ShortFrameNameLookup[2]['comment'] = 'COM';
$ID3v2ShortFrameNameLookup[2]['album'] = 'TAL';
$ID3v2ShortFrameNameLookup[2]['beats_per_minute'] = 'TBP';
$ID3v2ShortFrameNameLookup[2]['bpm'] = 'TBP';
$ID3v2ShortFrameNameLookup[2]['composer'] = 'TCM';
$ID3v2ShortFrameNameLookup[2]['genre'] = 'TCO';
$ID3v2ShortFrameNameLookup[2]['itunescompilation'] = 'TCP';
$ID3v2ShortFrameNameLookup[2]['copyright'] = 'TCR';
$ID3v2ShortFrameNameLookup[2]['encoded_by'] = 'TEN';
$ID3v2ShortFrameNameLookup[2]['language'] = 'TLA';
$ID3v2ShortFrameNameLookup[2]['length'] = 'TLE';
$ID3v2ShortFrameNameLookup[2]['original_artist'] = 'TOA';
$ID3v2ShortFrameNameLookup[2]['original_filename'] = 'TOF';
$ID3v2ShortFrameNameLookup[2]['original_lyricist'] = 'TOL';
$ID3v2ShortFrameNameLookup[2]['original_album_title'] = 'TOT';
$ID3v2ShortFrameNameLookup[2]['artist'] = 'TP1';
$ID3v2ShortFrameNameLookup[2]['band'] = 'TP2';
$ID3v2ShortFrameNameLookup[2]['conductor'] = 'TP3';
$ID3v2ShortFrameNameLookup[2]['remixer'] = 'TP4';
$ID3v2ShortFrameNameLookup[2]['publisher'] = 'TPB';
$ID3v2ShortFrameNameLookup[2]['isrc'] = 'TRC';
$ID3v2ShortFrameNameLookup[2]['tracknumber'] = 'TRK';
$ID3v2ShortFrameNameLookup[2]['track_number'] = 'TRK';
$ID3v2ShortFrameNameLookup[2]['size'] = 'TSI';
$ID3v2ShortFrameNameLookup[2]['encoder_settings'] = 'TSS';
$ID3v2ShortFrameNameLookup[2]['description'] = 'TT1';
$ID3v2ShortFrameNameLookup[2]['title'] = 'TT2';
$ID3v2ShortFrameNameLookup[2]['subtitle'] = 'TT3';
$ID3v2ShortFrameNameLookup[2]['lyricist'] = 'TXT';
$ID3v2ShortFrameNameLookup[2]['user_text'] = 'TXX';
$ID3v2ShortFrameNameLookup[2]['year'] = 'TYE';
$ID3v2ShortFrameNameLookup[2]['unique_file_identifier'] = 'UFI';
$ID3v2ShortFrameNameLookup[2]['unsynchronised_lyrics'] = 'ULT';
$ID3v2ShortFrameNameLookup[2]['url_file'] = 'WAF';
$ID3v2ShortFrameNameLookup[2]['url_artist'] = 'WAR';
$ID3v2ShortFrameNameLookup[2]['url_source'] = 'WAS';
$ID3v2ShortFrameNameLookup[2]['copyright_information'] = 'WCP';
$ID3v2ShortFrameNameLookup[2]['url_publisher'] = 'WPB';
$ID3v2ShortFrameNameLookup[2]['url_user'] = 'WXX';
$ID3v2ShortFrameNameLookup[2]['recommended_buffer_size'] = 'BUF';
$ID3v2ShortFrameNameLookup[2]['comment'] = 'COM';
$ID3v2ShortFrameNameLookup[2]['audio_encryption'] = 'CRA';
$ID3v2ShortFrameNameLookup[2]['encrypted_meta_frame'] = 'CRM';
$ID3v2ShortFrameNameLookup[2]['equalisation'] = 'EQU';
$ID3v2ShortFrameNameLookup[2]['event_timing_codes'] = 'ETC';
$ID3v2ShortFrameNameLookup[2]['general_encapsulated_object'] = 'GEO';
$ID3v2ShortFrameNameLookup[2]['involved_people_list'] = 'IPL';
$ID3v2ShortFrameNameLookup[2]['linked_information'] = 'LNK';
</