Browse Source

version 2.10.25 : maj de la lib getID3 en version 1.9.10

Ne faudrait-il pas ajouter cette lib aux crédits du plugins ainsi que mejs ?
3.1 v2.10.25
brunobergot@gmail.com 7 years ago
parent
commit
46b8862aac
  1. 8
      lib/getid3/extension.cache.mysql.php
  2. 35
      lib/getid3/extension.cache.sqlite3.php
  3. 36
      lib/getid3/getid3.lib.php
  4. 39
      lib/getid3/getid3.php
  5. 4
      lib/getid3/module.archive.gzip.php
  6. 2
      lib/getid3/module.audio-video.flv.php
  7. 33
      lib/getid3/module.audio-video.matroska.php
  8. 248
      lib/getid3/module.audio-video.quicktime.php
  9. 26
      lib/getid3/module.audio.flac.php
  10. 18
      lib/getid3/module.audio.mp3.php
  11. 92
      lib/getid3/module.audio.ogg.php
  12. 109
      lib/getid3/module.tag.apetag.php
  13. 311
      lib/getid3/module.tag.id3v2.php
  14. 30
      lib/getid3/module.tag.lyrics3.php
  15. 6
      lib/getid3/write.id3v2.php
  16. 2
      paquet.xml

8
lib/getid3/extension.cache.mysql.php

@ -134,7 +134,7 @@ class getID3_cached_mysql extends getID3
// public: analyze file
public function analyze($filename) {
public function analyze($filename, $filesize=null, $original_filename='') {
if (file_exists($filename)) {
@ -157,7 +157,7 @@ class getID3_cached_mysql extends getID3
}
// Miss
$analysis = parent::analyze($filename);
$analysis = parent::analyze($filename, $filesize, $original_filename);
// Save result
if (file_exists($filename)) {
@ -178,11 +178,11 @@ 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(255) NOT NULL DEFAULT \'\'';
$SQLquery .= '`filename` VARCHAR(500) 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` TEXT NOT NULL';
$SQLquery .= ', `value` LONGTEXT NOT NULL';
$SQLquery .= ', PRIMARY KEY (`filename`, `filesize`, `filetime`)) ENGINE=MyISAM';
$this->cursor = mysql_query($SQLquery, $this->connection);
echo mysql_error($this->connection);

35
lib/getid3/extension.cache.sqlite3.php

@ -49,20 +49,20 @@
*
* sqlite3 table='getid3_cache', hide=false (PHP5)
*
*** database file will be stored in the same directory as this script,
*** webserver must have write access to that directory!
*** set $hide to TRUE to prefix db file with .ht to pervent access from web client
*** this is a default setting in the Apache configuration:
# The following lines prevent .htaccess and .htpasswd files from being viewed by Web clients.
<Files ~ "^\.ht">
Order allow,deny
Deny from all
Satisfy all
</Files>
*
* *** database file will be stored in the same directory as this script,
* *** webserver must have write access to that directory!
* *** set $hide to TRUE to prefix db file with .ht to pervent access from web client
* *** this is a default setting in the Apache configuration:
*
* The following lines prevent .htaccess and .htpasswd files from being viewed by Web clients.
*
* <Files ~ "^\.ht">
* Order allow,deny
* Deny from all
* Satisfy all
* </Files>
*
********************************************************************************
*
* -------------------------------------------------------------------
@ -159,7 +159,7 @@ class getID3_cached_sqlite3 extends getID3 {
* @param type $filename
* @return boolean
*/
public function analyze($filename) {
public function analyze($filename, $filesize=null, $original_filename='') {
if (!file_exists($filename)) {
return false;
}
@ -182,7 +182,7 @@ class getID3_cached_sqlite3 extends getID3 {
return unserialize(base64_decode($result));
}
// if it hasn't been analyzed before, then do it now
$analysis = parent::analyze($filename);
$analysis = parent::analyze($filename, $filesize=null, $original_filename='');
// Save result
$sql = $this->cache_file;
$stmt = $db->prepare($sql);
@ -253,7 +253,8 @@ class getID3_cached_sqlite3 extends getID3 {
return "INSERT INTO $this->table (filename, dirname, filesize, filetime, analyzetime, val) VALUES (:filename, :dirname, :filesize, :filetime, :atime, :val)";
break;
case 'make_table':
return "CREATE TABLE IF NOT EXISTS $this->table (filename VARCHAR(255) NOT NULL DEFAULT '', dirname VARCHAR(255) NOT NULL DEFAULT '', filesize INT(11) NOT NULL DEFAULT '0', filetime INT(11) NOT NULL DEFAULT '0', analyzetime INT(11) NOT NULL DEFAULT '0', val text not null, PRIMARY KEY (filename, filesize, filetime))";
//return "CREATE TABLE IF NOT EXISTS $this->table (filename VARCHAR(255) NOT NULL DEFAULT '', dirname VARCHAR(255) NOT NULL DEFAULT '', filesize INT(11) NOT NULL DEFAULT '0', filetime INT(11) NOT NULL DEFAULT '0', analyzetime INT(11) NOT NULL DEFAULT '0', val text not null, PRIMARY KEY (filename, filesize, filetime))";
return "CREATE TABLE IF NOT EXISTS $this->table (filename VARCHAR(255) DEFAULT '', dirname VARCHAR(255) DEFAULT '', filesize INT(11) DEFAULT '0', filetime INT(11) DEFAULT '0', analyzetime INT(11) DEFAULT '0', val text, PRIMARY KEY (filename, filesize, filetime))";
break;
case 'get_cached_dir':
return "SELECT val FROM $this->table WHERE dirname = :dirname";

36
lib/getid3/getid3.lib.php

@ -414,6 +414,20 @@ class getid3_lib
return $newarray;
}
public static function flipped_array_merge_noclobber($array1, $array2) {
if (!is_array($array1) || !is_array($array2)) {
return false;
}
# naturally, this only works non-recursively
$newarray = array_flip($array1);
foreach (array_flip($array2) as $key => $val) {
if (!isset($newarray[$key])) {
$newarray[$key] = count($newarray);
}
}
return array_flip($newarray);
}
public static function ksort_recursive(&$theArray) {
ksort($theArray);
@ -519,15 +533,14 @@ class getid3_lib
}
public static function XML2array($XMLstring) {
if (function_exists('simplexml_load_string')) {
if (function_exists('get_object_vars')) {
if (function_exists('libxml_disable_entity_loader')) { // (PHP 5 >= 5.2.11)
// http://websec.io/2012/08/27/Preventing-XEE-in-PHP.html
libxml_disable_entity_loader(true);
}
$XMLobject = simplexml_load_string($XMLstring);
return self::SimpleXMLelement2array($XMLobject);
}
if (function_exists('simplexml_load_string') && function_exists('libxml_disable_entity_loader')) {
// http://websec.io/2012/08/27/Preventing-XEE-in-PHP.html
// https://core.trac.wordpress.org/changeset/29378
$loader = libxml_disable_entity_loader(true);
$XMLobject = simplexml_load_string($XMLstring, 'SimpleXMLElement', LIBXML_NOENT);
$return = self::SimpleXMLelement2array($XMLobject);
libxml_disable_entity_loader($loader);
return $return;
}
return false;
}
@ -1166,6 +1179,11 @@ class getid3_lib
fwrite($tmp, $imgData);
fclose($tmp);
$GetDataImageSize = @getimagesize($tempfilename, $imageinfo);
if (($GetDataImageSize === false) || !isset($GetDataImageSize[0]) || !isset($GetDataImageSize[1])) {
return false;
}
$GetDataImageSize['height'] = $GetDataImageSize[0];
$GetDataImageSize['width'] = $GetDataImageSize[1];
}
unlink($tempfilename);
}

39
lib/getid3/getid3.php

@ -28,7 +28,7 @@ $temp_dir = ini_get('upload_tmp_dir');
if ($temp_dir && (!is_dir($temp_dir) || !is_readable($temp_dir))) {
$temp_dir = '';
}
if (!$temp_dir) {
if (!$temp_dir && function_exists('sys_get_temp_dir')) { // sys_get_temp_dir added in PHP v5.2.1
// sys_get_temp_dir() may give inaccessible temp dir, e.g. with open_basedir on virtual hosts
$temp_dir = sys_get_temp_dir();
}
@ -109,7 +109,7 @@ class getID3
protected $startup_error = '';
protected $startup_warning = '';
const VERSION = '1.9.8-20140511';
const VERSION = '1.9.10-20150914';
const FREAD_BUFFER_SIZE = 32768;
const ATTACHMENTS_NONE = false;
@ -243,7 +243,7 @@ class getID3
}
public function openfile($filename) {
public function openfile($filename, $filesize=null) {
try {
if (!empty($this->startup_error)) {
throw new getid3_exception($this->startup_error);
@ -256,7 +256,7 @@ class getID3
$this->filename = $filename;
$this->info = array();
$this->info['GETID3_VERSION'] = $this->version();
$this->info['php_memory_limit'] = $this->memory_limit;
$this->info['php_memory_limit'] = (($this->memory_limit > 0) ? $this->memory_limit : false);
// remote files not supported
if (preg_match('/^(ht|f)tp:\/\//', $filename)) {
@ -287,7 +287,7 @@ class getID3
throw new getid3_exception('Could not open "'.$filename.'" ('.implode('; ', $errormessagelist).')');
}
$this->info['filesize'] = filesize($filename);
$this->info['filesize'] = (!is_null($filesize) ? $filesize : filesize($filename));
// set redundant parameters - might be needed in some include file
// filenames / filepaths in getID3 are always expressed with forward slashes (unix-style) for both Windows and other to try and minimize confusion
$filename = str_replace('\\', '/', $filename);
@ -342,9 +342,9 @@ class getID3
}
// public: analyze file
public function analyze($filename) {
public function analyze($filename, $filesize=null, $original_filename='') {
try {
if (!$this->openfile($filename)) {
if (!$this->openfile($filename, $filesize)) {
return $this->info;
}
@ -389,7 +389,7 @@ class getID3
$formattest = fread($this->fp, 32774);
// determine format
$determined_format = $this->GetFileFormat($formattest, $filename);
$determined_format = $this->GetFileFormat($formattest, ($original_filename ? $original_filename : $filename));
// unable to determine file format
if (!$determined_format) {
@ -1235,6 +1235,29 @@ class getID3
}
}
// 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!
}

4
lib/getid3/module.archive.gzip.php

@ -36,7 +36,7 @@ class getid3_gzip extends getid3_handler {
//|ID1|ID2|CM |FLG| MTIME |XFL|OS |
//+---+---+---+---+---+---+---+---+---+---+
if ($info['filesize'] > $info['php_memory_limit']) {
if ($info['php_memory_limit'] && ($info['filesize'] > $info['php_memory_limit'])) {
$info['error'][] = 'File is too large ('.number_format($info['filesize']).' bytes) to read into memory (limit: '.number_format($info['php_memory_limit'] / 1048576).'MB)';
return false;
}
@ -56,7 +56,7 @@ class getid3_gzip extends getid3_handler {
$attr = unpack($unpack_header, substr($buf, 0, $start_length));
if (!$this->get_os_type(ord($attr['os']))) {
// Merge member with previous if wrong OS type
$arr_members[$i - 1] .= $buf;
$arr_members[($i - 1)] .= $buf;
$arr_members[$i] = '';
$is_wrong_members = true;
continue;

2
lib/getid3/module.audio-video.flv.php

@ -541,7 +541,7 @@ class AMFReader {
// Long string
default:
$value = '(unknown or unsupported data type)';
break;
break;
}
return $value;

33
lib/getid3/module.audio-video.matroska.php

@ -457,6 +457,7 @@ class getid3_matroska extends getid3_handler
default:
$this->warning('Unhandled audio type "'.(isset($trackarray['CodecID']) ? $trackarray['CodecID'] : '').'"');
break;
}
$info['audio']['streams'][] = $track_info;
@ -524,6 +525,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('header', __LINE__, $element_data);
break;
}
unset($element_data['offset'], $element_data['end']);
@ -562,6 +564,7 @@ class getid3_matroska extends getid3_handler
default:
$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
@ -571,6 +574,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('seekhead', __LINE__, $seek_entry);
break;
}
}
break;
@ -653,6 +657,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('track.video', __LINE__, $sub_subelement);
break;
}
}
break;
@ -678,6 +683,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('track.audio', __LINE__, $sub_subelement);
break;
}
}
break;
@ -713,6 +719,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement);
break;
}
}
break;
@ -736,24 +743,28 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement);
break;
}
}
break;
default:
$this->unhandledElement('track.contentencodings.contentencoding', __LINE__, $sub_sub_subelement);
break;
}
}
break;
default:
$this->unhandledElement('track.contentencodings', __LINE__, $sub_subelement);
break;
}
}
break;
default:
$this->unhandledElement('track', __LINE__, $subelement);
break;
}
}
@ -762,6 +773,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('tracks', __LINE__, $track_entry);
break;
}
}
break;
@ -825,6 +837,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('info.chaptertranslate', __LINE__, $sub_subelement);
break;
}
}
$info_entry[$subelement['id_name']] = $chaptertranslate_entry;
@ -832,6 +845,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('info', __LINE__, $subelement);
break;
}
}
$info['matroska']['info'][] = $info_entry;
@ -868,6 +882,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('cues.cuepoint.cuetrackpositions', __LINE__, $sub_sub_subelement);
break;
}
}
$cuepoint_entry[$sub_subelement['id_name']][] = $cuetrackpositions_entry;
@ -879,6 +894,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('cues.cuepoint', __LINE__, $sub_subelement);
break;
}
}
$cues_entry[] = $cuepoint_entry;
@ -886,6 +902,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('cues', __LINE__, $subelement);
break;
}
}
$info['matroska']['cues'] = $cues_entry;
@ -927,6 +944,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('tags.tag.targets', __LINE__, $sub_sub_subelement);
break;
}
}
$tag_entry[$sub_subelement['id_name']] = $targets_entry;
@ -938,6 +956,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('tags.tag', __LINE__, $sub_subelement);
break;
}
}
$tags_entry[] = $tag_entry;
@ -945,6 +964,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('tags', __LINE__, $subelement);
break;
}
}
$info['matroska']['tags'] = $tags_entry;
@ -985,6 +1005,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('attachments.attachedfile', __LINE__, $sub_subelement);
break;
}
}
$info['matroska']['attachments'][] = $attachedfile_entry;
@ -992,6 +1013,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('attachments', __LINE__, $subelement);
break;
}
}
break;
@ -1051,6 +1073,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('chapters.editionentry.chapteratom.chaptertrack', __LINE__, $sub_sub_sub_subelement);
break;
}
}
$chapteratom_entry[$sub_sub_subelement['id_name']][] = $chaptertrack_entry;
@ -1070,6 +1093,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('chapters.editionentry.chapteratom.chapterdisplay', __LINE__, $sub_sub_sub_subelement);
break;
}
}
$chapteratom_entry[$sub_sub_subelement['id_name']][] = $chapterdisplay_entry;
@ -1077,6 +1101,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('chapters.editionentry.chapteratom', __LINE__, $sub_sub_subelement);
break;
}
}
$editionentry_entry[$sub_subelement['id_name']][] = $chapteratom_entry;
@ -1084,6 +1109,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('chapters.editionentry', __LINE__, $sub_subelement);
break;
}
}
$info['matroska']['chapters'][] = $editionentry_entry;
@ -1091,6 +1117,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('chapters', __LINE__, $subelement);
break;
}
}
break;
@ -1119,6 +1146,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('cluster.silenttracks', __LINE__, $sub_subelement);
break;
}
}
$cluster_entry[$subelement['id_name']][] = $cluster_silent_tracks;
@ -1149,6 +1177,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('clusters.blockgroup', __LINE__, $sub_subelement);
break;
}
}
$cluster_entry[$subelement['id_name']][] = $cluster_block_group;
@ -1160,6 +1189,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('cluster', __LINE__, $subelement);
break;
}
$this->current_offset = $subelement['end'];
}
@ -1181,12 +1211,14 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('segment', __LINE__, $element_data);
break;
}
}
break;
default:
$this->unhandledElement('root', __LINE__, $top_element);
break;
}
}
}
@ -1339,6 +1371,7 @@ class getid3_matroska extends getid3_handler
default:
$this->unhandledElement('tag.simpletag', __LINE__, $element);
break;
}
}

248
lib/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
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';
@ -68,7 +68,7 @@ class getid3_quicktime extends getid3_handler
break;
}
$atomHierarchy = array();
$info['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, $this->fread(min($atomsize, round($this->getid3->memory_limit / 2))), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms);
$info['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, $this->fread(min($atomsize, $atom_data_read_buffer_size)), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms);
$offset += $atomsize;
$atomcounter++;
@ -120,6 +120,7 @@ class getid3_quicktime extends getid3_handler
public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
// http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm
// https://code.google.com/p/mp4v2/wiki/iTunesMetadata
$info = &$this->getid3->info;
@ -222,81 +223,88 @@ class getid3_quicktime extends getid3_handler
break;
case "\xA9".'alb': // ALBum
case "\xA9".'ART': //
case "\xA9".'art': // ARTist
case "\xA9".'aut': //
case "\xA9".'cmt': // CoMmenT
case "\xA9".'com': // COMposer
case "\xA9".'cpy': //
case "\xA9".'day': // content created year
case "\xA9".'dir': //
case "\xA9".'ed1': //
case "\xA9".'ed2': //
case "\xA9".'ed3': //
case "\xA9".'ed4': //
case "\xA9".'ed5': //
case "\xA9".'ed6': //
case "\xA9".'ed7': //
case "\xA9".'ed8': //
case "\xA9".'ed9': //
case "\xA9".'enc': //
case "\xA9".'fmt': //
case "\xA9".'gen': // GENre
case "\xA9".'grp': // GRouPing
case "\xA9".'hst': //
case "\xA9".'inf': //
case "\xA9".'lyr': // LYRics
case "\xA9".'mak': //
case "\xA9".'mod': //
case "\xA9".'nam': // full NAMe
case "\xA9".'ope': //
case "\xA9".'PRD': //
case "\xA9".'prf': //
case "\xA9".'req': //
case "\xA9".'src': //
case "\xA9".'swr': //
case "\xA9".'too': // encoder
case "\xA9".'trk': // TRacK
case "\xA9".'url': //
case "\xA9".'wrn': //
case "\xA9".'wrt': // WRiTer
case '----': // itunes specific
case 'aART': // Album ARTist
case 'akID': // iTunes store account type
case 'apID': // Purchase Account
case 'atID': //
case 'catg': // CaTeGory
case 'cmID': //
case 'cnID': //
case 'covr': // COVeR artwork
case 'cpil': // ComPILation
case 'cprt': // CoPyRighT
case 'desc': // DESCription
case 'disk': // DISK number
case 'egid': // Episode Global ID
case 'geID': //
case 'gnre': // GeNRE
case 'hdvd': // HD ViDeo
case 'keyw': // KEYWord
case 'ldes':
case 'ldes': // Long DEScription
case 'pcst': // PodCaST
case 'pgap': // GAPless Playback
case 'plID': //
case 'purd': // PURchase Date
case 'purl': // Podcast URL
case 'rati':
case 'rndu':
case 'rpdu':
case 'rati': //
case 'rndu': //
case 'rpdu': //
case 'rtng': // RaTiNG
case 'stik':
case 'sfID': // iTunes store country
case 'soaa': // SOrt Album Artist
case 'soal': // SOrt ALbum
case 'soar': // SOrt ARtist
case 'soco': // SOrt COmposer
case 'sonm': // SOrt NaMe
case 'sosn': // SOrt Show Name
case 'stik': //
case 'tmpo': // TeMPO (BPM)
case 'trkn': // TRacK Number
case 'tven': // tvEpisodeID
case 'tves': // TV EpiSode
case 'tvnn': // TV Network Name
case 'tvsh': // TV SHow Name
case 'tvsn': // TV SeasoN
case 'akID': // iTunes store account type
case 'apID':
case 'atID':
case 'cmID':
case 'cnID':
case 'geID':
case 'plID':
case 'sfID': // iTunes store country
case "\xA9".'alb': // ALBum
case "\xA9".'art': // ARTist
case "\xA9".'ART':
case "\xA9".'aut':
case "\xA9".'cmt': // CoMmenT
case "\xA9".'com': // COMposer
case "\xA9".'cpy':
case "\xA9".'day': // content created year
case "\xA9".'dir':
case "\xA9".'ed1':
case "\xA9".'ed2':
case "\xA9".'ed3':
case "\xA9".'ed4':
case "\xA9".'ed5':
case "\xA9".'ed6':
case "\xA9".'ed7':
case "\xA9".'ed8':
case "\xA9".'ed9':
case "\xA9".'enc':
case "\xA9".'fmt':
case "\xA9".'gen': // GENre
case "\xA9".'grp': // GRouPing
case "\xA9".'hst':
case "\xA9".'inf':
case "\xA9".'lyr': // LYRics
case "\xA9".'mak':
case "\xA9".'mod':
case "\xA9".'nam': // full NAMe
case "\xA9".'ope':
case "\xA9".'PRD':
case "\xA9".'prd':
case "\xA9".'prf':
case "\xA9".'req':
case "\xA9".'src':
case "\xA9".'swr':
case "\xA9".'too': // encoder
case "\xA9".'trk': // TRacK
case "\xA9".'url':
case "\xA9".'wrn':
case "\xA9".'wrt': // WRiTer
case '----': // itunes specific
if ($atom_parent == 'udta') {
// User data atom handler
$atom_structure['data_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
@ -361,17 +369,21 @@ class getid3_quicktime extends getid3_handler
case 21: // tmpo/cpil flag
switch ($atomname) {
case 'cpil':
case 'hdvd':
case 'pcst':
case 'pgap':
// 8-bit integer (boolean)
$atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
break;
case 'tmpo':
// 16-bit integer
$atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2));
break;
case 'disk':
case 'trkn':
// binary
$num = getid3_lib::BigEndian2Int(substr($boxdata, 10, 2));
$num_total = getid3_lib::BigEndian2Int(substr($boxdata, 12, 2));
$atom_structure['data'] = empty($num) ? '' : $num;
@ -379,21 +391,25 @@ class getid3_quicktime extends getid3_handler
break;
case 'gnre':
// enum
$GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
$atom_structure['data'] = getid3_id3v1::LookupGenreName($GenreID - 1);
break;
case 'rtng':
// 8-bit integer
$atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
$atom_structure['data'] = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]);
break;
case 'stik':
// 8-bit integer (enum)
$atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
$atom_structure['data'] = $this->QuicktimeSTIKLookup($atom_structure[$atomname]);
break;
case 'sfID':
// 32-bit integer
$atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
$atom_structure['data'] = $this->QuicktimeStoreFrontCodeLookup($atom_structure[$atomname]);
break;
@ -403,7 +419,17 @@ class getid3_quicktime extends getid3_handler
$atom_structure['data'] = substr($boxdata, 8);
break;
case 'plID':
// 64-bit integer
$atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 8));
case 'atID':
case 'cnID':
case 'geID':
case 'tves':
case 'tvsn':
default:
// 32-bit integer
$atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
}
break;
@ -799,9 +825,9 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($
//$FrameRateCalculatorArray = array();
$frames_count = 0;
$max_stts_entries_to_scan = min(floor($this->getid3->memory_limit / 10000), $atom_structure['number_entries']);
$max_stts_entries_to_scan = ($info['php_memory_limit'] ? min(floor($this->getid3->memory_limit / 10000), $atom_structure['number_entries']) : $atom_structure['number_entries']);
if ($max_stts_entries_to_scan < $atom_structure['number_entries']) {
$info['warning'][] = 'QuickTime atom "stts" has '.$atom_structure['number_entries'].' but only scanning the first '.$max_stts_entries_to_scan.' entries due to limited PHP memory available ('.floor($this->getid3->memory_limit / 1048576).'MB).';
$info['warning'][] = 'QuickTime atom "stts" has '.$atom_structure['number_entries'].' but only scanning the first '.$max_stts_entries_to_scan.' entries due to limited PHP memory available ('.floor($atom_structure['number_entries'] / 1048576).'MB).';
}
for ($i = 0; $i < $max_stts_entries_to_scan; $i++) {
$atom_structure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
@ -1399,7 +1425,7 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($
case "\x00\x00\x00\x00":
case 'meta': // METAdata atom
// some kind of metacontainer, may contain a big data dump such as:
// mdta keys  mdtacom.apple.quicktime.make (mdtacom.apple.quicktime.creationdate ,mdtacom.apple.quicktime.location.ISO6709 $mdtacom.apple.quicktime.software !mdtacom.apple.quicktime.model ilst   data DEApple 0  (data DE2011-05-11T17:54:04+0200 2  *data DE+52.4936+013.3897+040.247/   data DE4.3.1  data DEiPhone 4
// mdta keys \005 mdtacom.apple.quicktime.make (mdtacom.apple.quicktime.creationdate ,mdtacom.apple.quicktime.location.ISO6709 $mdtacom.apple.quicktime.software !mdtacom.apple.quicktime.model ilst \01D \001 \015data \001DE\010Apple 0 \002 (data \001DE\0102011-05-11T17:54:04+0200 2 \003 *data \001DE\010+52.4936+013.3897+040.247/ \01D \004 \015data \001DE\0104.3.1 \005 \018data \001DE\010iPhone 4
// http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
@ -2111,8 +2137,18 @@ echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br
public function CopyToAppropriateCommentsSection($keyname, $data, $boxname='') {
static $handyatomtranslatorarray = array();
if (empty($handyatomtranslatorarray)) {
// http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt
// http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
// http://atomicparsley.sourceforge.net/mpeg-4files.html
// https://code.google.com/p/mp4v2/wiki/iTunesMetadata
$handyatomtranslatorarray["\xA9".'alb'] = 'album'; // iTunes 4.0
$handyatomtranslatorarray["\xA9".'ART'] = 'artist';
$handyatomtranslatorarray["\xA9".'art'] = 'artist'; // iTunes 4.0
$handyatomtranslatorarray["\xA9".'aut'] = 'author';
$handyatomtranslatorarray["\xA9".'cmt'] = 'comment'; // iTunes 4.0
$handyatomtranslatorarray["\xA9".'com'] = 'comment';
$handyatomtranslatorarray["\xA9".'cpy'] = 'copyright';
$handyatomtranslatorarray["\xA9".'day'] = 'creation_date'; // iTunes 4.0
$handyatomtranslatorarray["\xA9".'day'] = 'creation_date'; // iTunes 4.0
$handyatomtranslatorarray["\xA9".'dir'] = 'director';
$handyatomtranslatorarray["\xA9".'ed1'] = 'edit1';
$handyatomtranslatorarray["\xA9".'ed2'] = 'edit2';
@ -2123,64 +2159,60 @@ echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br
$handyatomtranslatorarray["\xA9".'ed7'] = 'edit7';
$handyatomtranslatorarray["\xA9".'ed8'] = 'edit8';
$handyatomtranslatorarray["\xA9".'ed9'] = 'edit9';
$handyatomtranslatorarray["\xA9".'enc'] = 'encoded_by';
$handyatomtranslatorarray["\xA9".'fmt'] = 'format';
$handyatomtranslatorarray["\xA9".'gen'] = 'genre'; // iTunes 4.0
$handyatomtranslatorarray["\xA9".'grp'] = 'grouping'; // iTunes 4.2
$handyatomtranslatorarray["\xA9".'hst'] = 'host_computer';
$handyatomtranslatorarray["\xA9".'inf'] = 'information';
$handyatomtranslatorarray["\xA9".'lyr'] = 'lyrics'; // iTunes 5.0
$handyatomtranslatorarray["\xA9".'mak'] = 'make';
$handyatomtranslatorarray["\xA9".'mod'] = 'model';
$handyatomtranslatorarray["\xA9".'nam'] = 'title'; // iTunes 4.0
$handyatomtranslatorarray["\xA9".'ope'] = 'composer';
$handyatomtranslatorarray["\xA9".'prd'] = 'producer';
$handyatomtranslatorarray["\xA9".'PRD'] = 'product';
$handyatomtranslatorarray["\xA9".'prf'] = 'performers';
$handyatomtranslatorarray["\xA9".'req'] = 'system_requirements';
$handyatomtranslatorarray["\xA9".'src'] = 'source_credit';
$handyatomtranslatorarray["\xA9".'wrt'] = 'writer';
// http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt
$handyatomtranslatorarray["\xA9".'nam'] = 'title'; // iTunes 4.0
$handyatomtranslatorarray["\xA9".'cmt'] = 'comment'; // iTunes 4.0
$handyatomtranslatorarray["\xA9".'wrn'] = 'warning';
$handyatomtranslatorarray["\xA9".'hst'] = 'host_computer';
$handyatomtranslatorarray["\xA9".'mak'] = 'make';
$handyatomtranslatorarray["\xA9".'mod'] = 'model';
$handyatomtranslatorarray["\xA9".'PRD'] = 'product';
$handyatomtranslatorarray["\xA9".'swr'] = 'software';
$handyatomtranslatorarray["\xA9".'aut'] = 'author';
$handyatomtranslatorarray["\xA9".'ART'] = 'artist';
$handyatomtranslatorarray["\xA9".'too'] = 'encoding_tool'; // iTunes 4.0
$handyatomtranslatorarray["\xA9".'trk'] = 'track';
$handyatomtranslatorarray["\xA9".'alb'] = 'album'; // iTunes 4.0
$handyatomtranslatorarray["\xA9".'com'] = 'comment';
$handyatomtranslatorarray["\xA9".'gen'] = 'genre'; // iTunes 4.0
$handyatomtranslatorarray["\xA9".'ope'] = 'composer';
$handyatomtranslatorarray["\xA9".'url'] = 'url';
$handyatomtranslatorarray["\xA9".'enc'] = 'encoder';
// http://atomicparsley.sourceforge.net/mpeg-4files.html
$handyatomtranslatorarray["\xA9".'art'] = 'artist'; // iTunes 4.0
$handyatomtranslatorarray["\xA9".'wrn'] = 'warning';
$handyatomtranslatorarray["\xA9".'wrt'] = 'composer';
$handyatomtranslatorarray['aART'] = 'album_artist';
$handyatomtranslatorarray['trkn'] = 'track_number'; // iTunes 4.0
$handyatomtranslatorarray['disk'] = 'disc_number'; // iTunes 4.0
$handyatomtranslatorarray['gnre'] = 'genre'; // iTunes 4.0
$handyatomtranslatorarray["\xA9".'too'] = 'encoder'; // iTunes 4.0
$handyatomtranslatorarray['tmpo'] = 'bpm'; // iTunes 4.0
$handyatomtranslatorarray['cprt'] = 'copyright'; // iTunes 4.0?
$handyatomtranslatorarray['cpil'] = 'compilation'; // iTunes 4.0
$handyatomtranslatorarray['covr'] = 'picture'; // iTunes 4.0
$handyatomtranslatorarray['rtng'] = 'rating'; // iTunes 4.0
$handyatomtranslatorarray["\xA9".'grp'] = 'grouping'; // iTunes 4.2
$handyatomtranslatorarray['stik'] = 'stik'; // iTunes 4.9
$handyatomtranslatorarray['pcst'] = 'podcast'; // iTunes 4.9
$handyatomtranslatorarray['catg'] = 'category'; // iTunes 4.9
$handyatomtranslatorarray['keyw'] = 'keyword'; // iTunes 4.9
$handyatomtranslatorarray['purl'] = 'podcast_url'; // iTunes 4.9
$handyatomtranslatorarray['egid'] = 'episode_guid'; // iTunes 4.9
$handyatomtranslatorarray['desc'] = 'description'; // iTunes 5.0
$handyatomtranslatorarray["\xA9".'lyr'] = 'lyrics'; // iTunes 5.0
$handyatomtranslatorarray['tvnn'] = 'tv_network_name'; // iTunes 6.0
$handyatomtranslatorarray['tvsh'] = 'tv_show_name'; // iTunes 6.0
$handyatomtranslatorarray['tvsn'] = 'tv_season'; // iTunes 6.0
$handyatomtranslatorarray['tves'] = 'tv_episode'; // iTunes 6.0
$handyatomtranslatorarray['purd'] = 'purchase_date'; // iTunes 6.0.2
$handyatomtranslatorarray['pgap'] = 'gapless_playback'; // iTunes 7.0
// http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
$handyatomtranslatorarray['apID'] = 'purchase_account';
$handyatomtranslatorarray['catg'] = 'category'; // iTunes 4.9
$handyatomtranslatorarray['covr'] = 'picture'; // iTunes 4.0
$handyatomtranslatorarray['cpil'] = 'compilation'; // iTunes 4.0
$handyatomtranslatorarray['cprt'] = 'copyright'; // iTunes 4.0?
$handyatomtranslatorarray['desc'] = 'description'; // iTunes 5.0
$handyatomtranslatorarray['disk'] = 'disc_number'; // iTunes 4.0
$handyatomtranslatorarray['egid'] = 'episode_guid'; // iTunes 4.9
$handyatomtranslatorarray['gnre'] = 'genre'; // iTunes 4.0
$handyatomtranslatorarray['hdvd'] = 'hd_video'; // iTunes 4.0
$handyatomtranslatorarray['ldes'] = 'description_long'; //
$handyatomtranslatorarray['keyw'] = 'keyword'; // iTunes 4.9
$handyatomtranslatorarray['pcst'] = 'podcast'; // iTunes 4.9
$handyatomtranslatorarray['pgap'] = 'gapless_playback'; // iTunes 7.0
$handyatomtranslatorarray['purd'] = 'purchase_date'; // iTunes 6.0.2
$handyatomtranslatorarray['purl'] = 'podcast_url'; // iTunes 4.9
$handyatomtranslatorarray['rtng'] = 'rating'; // iTunes 4.0
$handyatomtranslatorarray['soaa'] = 'sort_album_artist'; //
$handyatomtranslatorarray['soal'] = 'sort_album'; //
$handyatomtranslatorarray['soar'] = 'sort_artist'; //
$handyatomtranslatorarray['soco'] = 'sort_composer'; //
$handyatomtranslatorarray['sonm'] = 'sort_title'; //
$handyatomtranslatorarray['sosn'] = 'sort_show'; //
$handyatomtranslatorarray['stik'] = 'stik'; // iTunes 4.9
$handyatomtranslatorarray['tmpo'] = 'bpm'; // iTunes 4.0
$handyatomtranslatorarray['trkn'] = 'track_number'; // iTunes 4.0
$handyatomtranslatorarray['tven'] = 'tv_episode_id'; //
$handyatomtranslatorarray['tves'] = 'tv_episode'; // iTunes 6.0
$handyatomtranslatorarray['tvnn'] = 'tv_network_name'; // iTunes 6.0
$handyatomtranslatorarray['tvsh'] = 'tv_show_name'; // iTunes 6.0
$handyatomtranslatorarray['tvsn'] = 'tv_season'; // iTunes 6.0
// boxnames:
/*

26
lib/getid3/module.audio.flac.php

@ -135,7 +135,17 @@ class getid3_flac extends getid3_handler
if (isset($info['flac']['PICTURE']) && ($this->getid3->option_save_attachments !== getID3::ATTACHMENTS_NONE)) {
foreach ($info['flac']['PICTURE'] as $entry) {
if (!empty($entry['data'])) {
$info['flac']['comments']['picture'][] = array('image_mime'=>$entry['image_mime'], 'data'=>$entry['data']);
if (!isset($info['flac']['comments']['picture'])) {
$info['flac']['comments']['picture'] = array();
}
$comments_picture_data = array();
foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) {
if (isset($entry[$picture_key])) {
$comments_picture_data[$picture_key] = $entry[$picture_key];
}
}
$info['flac']['comments']['picture'][] = $comments_picture_data;
unset($comments_picture_data);
}
}
}
@ -343,25 +353,25 @@ class getid3_flac extends getid3_handler
$info = &$this->getid3->info;
$picture['typeid'] = getid3_lib::BigEndian2Int($this->fread(4));
$picture['type'] = self::pictureTypeLookup($picture['typeid']);
$picture['picturetype'] = self::pictureTypeLookup($picture['typeid']);
$picture['image_mime'] = $this->fread(getid3_lib::BigEndian2Int($this->fread(4)));
$descr_length = getid3_lib::BigEndian2Int($this->fread(4));
if ($descr_length) {
$picture['description'] = $this->fread($descr_length);
}
$picture['width'] = getid3_lib::BigEndian2Int($this->fread(4));
$picture['height'] = getid3_lib::BigEndian2Int($this->fread(4));
$picture['image_width'] = getid3_lib::BigEndian2Int($this->fread(4));
$picture['image_height'] = getid3_lib::BigEndian2Int($this->fread(4));
$picture['color_depth'] = getid3_lib::BigEndian2Int($this->fread(4));
$picture['colors_indexed'] = getid3_lib::BigEndian2Int($this->fread(4));
$data_length = getid3_lib::BigEndian2Int($this->fread(4));
$picture['datalength'] = getid3_lib::BigEndian2Int($this->fread(4));
if ($picture['image_mime'] == '-->') {
$picture['data'] = $this->fread($data_length);
$picture['data'] = $this->fread($picture['datalength']);
} else {
$picture['data'] = $this->saveAttachment(
str_replace('/', '_', $picture['type']).'_'.$this->ftell(),
str_replace('/', '_', $picture['picturetype']).'_'.$this->ftell(),
$this->ftell(),
$data_length,
$picture['datalength'],
$picture['image_mime']);
}

18
lib/getid3/module.audio.mp3.php

@ -437,7 +437,6 @@ class getid3_mp3 extends getid3_handler
// and $cc... is the audio data
$head4 = substr($headerstring, 0, 4);
static $MPEGaudioHeaderDecodeCache = array();
if (isset($MPEGaudioHeaderDecodeCache[$head4])) {
$MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4];
@ -648,9 +647,20 @@ class getid3_mp3 extends getid3_handler
}
//if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
//if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
if (!empty($thisfile_mpeg_audio['VBR_frames'])) {
$used_filesize = 0;
if (!empty($thisfile_mpeg_audio['VBR_bytes'])) {
$used_filesize = $thisfile_mpeg_audio['VBR_bytes'];
} elseif (!empty($info['filesize'])) {
$used_filesize = $info['filesize'];
$used_filesize -= intval(@$info['id3v2']['headerlength']);
$used_filesize -= (isset($info['id3v1']) ? 128 : 0);
$used_filesize -= (isset($info['tag_offset_end']) ? $info['tag_offset_end'] - $info['tag_offset_start'] : 0);
$info['warning'][] = 'MP3.Xing header missing VBR_bytes, assuming MPEG audio portion of file is '.number_format($used_filesize).' bytes';
}
$framelengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames'];
$framelengthfloat = $used_filesize / $thisfile_mpeg_audio['VBR_frames'];
if ($thisfile_mpeg_audio['layer'] == '1') {
// BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
@ -948,7 +958,7 @@ class getid3_mp3 extends getid3_handler
}
$thisfile_mpeg_audio['VBR_bitrate'] = (isset($thisfile_mpeg_audio['VBR_bytes']) ? (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / $bytes_per_frame) : 0);
if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) {
$info['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate'];
$info['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate'];
$thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion
}
break;

92
lib/getid3/module.audio.ogg.php

@ -63,6 +63,12 @@ class getid3_ogg extends getid3_handler
$this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
} elseif (substr($filedata, 0, 8) == 'OpusHead') {
if( $this->ParseOpusPageHeader($filedata, $filedataoffset, $oggpageinfo) == false ) {
return false;
}
} elseif (substr($filedata, 0, 8) == 'Speex ') {
// http://www.speex.org/manual/node10.html
@ -255,7 +261,7 @@ $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of get
} else {
$info['error'][] = 'Expecting either "Speex " or "vorbis" identifier strings, found "'.substr($filedata, 0, 8).'"';
$info['error'][] = 'Expecting either "Speex ", "OpusHead" or "vorbis" identifier strings, found "'.substr($filedata, 0, 8).'"';
unset($info['ogg']);
unset($info['mime_type']);
return false;
@ -288,8 +294,19 @@ $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of get
$this->fseek($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR);
$this->ParseVorbisComments();
break;
}
case 'opus':
$filedata = $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 0, 8); // hard-coded to 'OpusTags'
if(substr($filedata, 0, 8) != 'OpusTags') {
$info['error'][] = 'Expected "OpusTags" as header but got "'.substr($filedata, 0, 8).'"';
return false;
}
$this->ParseVorbisComments();
break;
}
// Last Page - Number of Samples
if (!getid3_lib::intValueSupported($info['avdataend'])) {
@ -409,6 +426,57 @@ $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of get
return true;
}
// http://tools.ietf.org/html/draft-ietf-codec-oggopus-03
public function ParseOpusPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) {
$info = &$this->getid3->info;
$info['audio']['dataformat'] = 'opus';
$info['mime_type'] = 'audio/ogg; codecs=opus';
/** @todo find a usable way to detect abr (vbr that is padded to be abr) */
$info['audio']['bitrate_mode'] = 'vbr';
$info['audio']['lossless'] = false;
$info['ogg']['pageheader']['opus']['opus_magic'] = substr($filedata, $filedataoffset, 8); // hard-coded to 'OpusHead'
$filedataoffset += 8;
$info['ogg']['pageheader']['opus']['version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
if ($info['ogg']['pageheader']['opus']['version'] < 1 || $info['ogg']['pageheader']['opus']['version'] > 15) {
$info['error'][] = 'Unknown opus version number (only accepting 1-15)';
return false;
}
$info['ogg']['pageheader']['opus']['out_channel_count'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
if ($info['ogg']['pageheader']['opus']['out_channel_count'] == 0) {
$info['error'][] = 'Invalid channel count in opus header (must not be zero)';
return false;
}
$info['ogg']['pageheader']['opus']['pre_skip'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
$filedataoffset += 2;
$info['ogg']['pageheader']['opus']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
//$info['ogg']['pageheader']['opus']['output_gain'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
//$filedataoffset += 2;
//$info['ogg']['pageheader']['opus']['channel_mapping_family'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
//$filedataoffset += 1;
$info['opus']['opus_version'] = $info['ogg']['pageheader']['opus']['version'];
$info['opus']['sample_rate'] = $info['ogg']['pageheader']['opus']['sample_rate'];
$info['opus']['out_channel_count'] = $info['ogg']['pageheader']['opus']['out_channel_count'];
$info['audio']['channels'] = $info['opus']['out_channel_count'];
$info['audio']['sample_rate'] = $info['opus']['sample_rate'];
return true;
}
public function ParseOggPageHeader() {
// http://xiph.org/ogg/vorbis/doc/framing.html
$oggheader['page_start_offset'] = $this->ftell(); // where we started from in the file
@ -471,6 +539,7 @@ $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of get
switch ($info['audio']['dataformat']) {
case 'vorbis':
case 'speex':
case 'opus':
$CommentStartOffset = $info['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block
$this->fseek($CommentStartOffset);
$commentdataoffset = 27 + $info['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
@ -479,6 +548,10 @@ $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of get
if ($info['audio']['dataformat'] == 'vorbis') {
$commentdataoffset += (strlen('vorbis') + 1);
}
else if ($info['audio']['dataformat'] == 'opus') {
$commentdataoffset += strlen('OpusTags');
}
break;
case 'flac':
@ -489,6 +562,7 @@ $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of get
default:
return false;
break;
}
$VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
@ -505,6 +579,12 @@ $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of get
$ThisFileInfo_ogg_comments_raw = &$info['ogg']['comments_raw'];
for ($i = 0; $i < $CommentsCount; $i++) {
if ($i >= 10000) {
// https://github.com/owncloud/music/issues/212#issuecomment-43082336
$info['warning'][] = 'Unexpectedly large number ('.$CommentsCount.') of Ogg comments - breaking after reading '.$i.' comments';
break;
}
$ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset;
if ($this->ftell() < ($ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + 4)) {
@ -615,8 +695,12 @@ $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of get
$ogg = new self($this->getid3);
$ogg->setStringMode($data);
$info['ogg']['comments']['picture'][] = array(
'image_mime' => $imageinfo['mime'],
'data' => $ogg->saveAttachment('coverart', 0, strlen($data), $imageinfo['mime']),
'image_mime' => $imageinfo['mime'],
'datalength' => strlen($data),
'picturetype' => 'cover art',
'image_height' => $imageinfo['height'],
'image_width' => $imageinfo['width'],
'data' => $ogg->saveAttachment('coverart', 0, strlen($data), $imageinfo['mime']),
);
unset($ogg);

109
lib/getid3/module.tag.apetag.php

@ -138,58 +138,88 @@ class getid3_apetag extends getid3_handler
$thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags);
switch ($thisfile_ape_items_current['flags']['item_contents_raw']) {
case 0: // UTF-8
case 3: // Locator (URL, filename, etc), UTF-8 encoded
$thisfile_ape_items_current['data'] = explode("\x00", trim($thisfile_ape_items_current['data']));
case 2: // Locator (URL, filename, etc), UTF-8 encoded
$thisfile_ape_items_current['data'] = explode("\x00", $thisfile_ape_items_current['data']);
break;
default: // binary data
case 1: // binary data
default:
break;
}
switch (strtolower($item_key)) {
// http://wiki.hydrogenaud.io/index.php?title=ReplayGain#MP3Gain
case 'replaygain_track_gain':
$thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
$thisfile_replaygain['track']['originator'] = 'unspecified';
if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
$thisfile_replaygain['track']['originator'] = 'unspecified';
} else {
$info['warning'][] = 'MP3gainTrackGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
}
break;
case 'replaygain_track_peak':
$thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
$thisfile_replaygain['track']['originator'] = 'unspecified';
if ($thisfile_replaygain['track']['peak'] <= 0) {
$info['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
$thisfile_replaygain['track']['originator'] = 'unspecified';
if ($thisfile_replaygain['track']['peak'] <= 0) {
$info['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
}
} else {
$info['warning'][] = 'MP3gainTrackPeak value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
}
break;
case 'replaygain_album_gain':
$thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
$thisfile_replaygain['album']['originator'] = 'unspecified';
if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
$thisfile_replaygain['album']['originator'] = 'unspecified';
} else {
$info['warning'][] = 'MP3gainAlbumGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
}
break;
case 'replaygain_album_peak':
$thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
$thisfile_replaygain['album']['originator'] = 'unspecified';
if ($thisfile_replaygain['album']['peak'] <= 0) {
$info['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
$thisfile_replaygain['album']['originator'] = 'unspecified';
if ($thisfile_replaygain['album']['peak'] <= 0) {
$info['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
}
} else {
$info['warning'][] = 'MP3gainAlbumPeak value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
}
break;
case 'mp3gain_undo':
list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]);
$thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left);
$thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
$thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false);
if (preg_match('#^[\\-\\+][0-9]{3},[\\-\\+][0-9]{3},[NW]$#', $thisfile_ape_items_current['data'][0])) {
list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]);
$thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left);
$thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
$thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false);
} else {
$info['warning'][] = 'MP3gainUndo value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
}
break;
case 'mp3gain_minmax':
list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]);
$thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
$thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
if (preg_match('#^[0-9]{3},[0-9]{3}$#', $thisfile_ape_items_current['data'][0])) {
list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]);
$thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
$thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
} else {
$info['warning'][] = 'MP3gainMinMax value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
}
break;
case 'mp3gain_album_minmax':
list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]);
$thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
$thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
if (preg_match('#^[0-9]{3},[0-9]{3}$#', $thisfile_ape_items_current['data'][0])) {
list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]);
$thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
$thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
} else {
$info['warning'][] = 'MP3gainAlbumMinMax value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
}
break;
case 'tracknumber':
@ -222,16 +252,24 @@ class getid3_apetag extends getid3_handler
case 'cover art (recording)':
case 'cover art (studio)':
// list of possible cover arts from http://taglib-sharp.sourcearchive.com/documentation/2.0.3.0-2/Ape_2Tag_8cs-source.html
if (is_array($thisfile_ape_items_current['data'])) {
$info['warning'][] = 'APEtag "'.$item_key.'" should be flagged as Binary data, but was incorrectly flagged as UTF-8';
$thisfile_ape_items_current['data'] = implode("\x00", $thisfile_ape_items_current['data']);
}
list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = explode("\x00", $thisfile_ape_items_current['data'], 2);
$thisfile_ape_items_current['data_offset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename']."\x00");
$thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']);
$thisfile_ape_items_current['image_mime'] = '';
$imageinfo = array();
$imagechunkcheck = getid3_lib::GetDataImageSize($thisfile_ape_items_current['data'], $imageinfo);
$thisfile_ape_items_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]);
do {
$thisfile_ape_items_current['image_mime'] = '';
$imageinfo = array();
$imagechunkcheck = getid3_lib::GetDataImageSize($thisfile_ape_items_current['data'], $imageinfo);
if (($imagechunkcheck === false) || !isset($imagechunkcheck[2])) {
$info['warning'][] = 'APEtag "'.$item_key.'" contains invalid image data';
break;
}
$thisfile_ape_items_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]);
if ($this->inline_attachments === false) {
// skip entirely
unset($thisfile_ape_items_current['data']);
@ -269,7 +307,14 @@ class getid3_apetag extends getid3_handler
if (!isset($info['ape']['comments']['picture'])) {
$info['ape']['comments']['picture'] = array();
}
$info['ape']['comments']['picture'][] = array('data'=>$thisfile_ape_items_current['data'], 'image_mime'=>$thisfile_ape_items_current['image_mime']);
$comments_picture_data = array();
foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) {
if (isset($thisfile_ape_items_current[$picture_key])) {
$comments_picture_data[$picture_key] = $thisfile_ape_items_current[$picture_key];
}
}
$info['ape']['comments']['picture'][] = $comments_picture_data;
unset($comments_picture_data);
}
} while (false);
break;
@ -317,7 +362,7 @@ class getid3_apetag extends getid3_handler
public function parseAPEtagFlags($rawflagint) {
// "Note: APE Tags 1.0 do not use any of the APE Tag flags.
// All are set to zero on creation and ignored on reading."
// http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html
// http://wiki.hydrogenaud.io/index.php?title=Ape_Tags_Flags
$flags['header'] = (bool) ($rawflagint & 0x80000000);
$flags['footer'] = (bool) ($rawflagint & 0x40000000);
$flags['this_is_header'] = (bool) ($rawflagint & 0x20000000);

311
lib/getid3/module.tag.id3v2.php

@ -442,10 +442,14 @@ class getid3_id3v2 extends getid3_handler
} // end footer
if (isset($thisfile_id3v2['comments']['genre'])) {
$genres = array();
foreach ($thisfile_id3v2['comments']['genre'] as $key => $value) {
unset($thisfile_id3v2['comments']['genre'][$key]);
$thisfile_id3v2['comments'] = getid3_lib::array_merge_noclobber($thisfile_id3v2['comments'], array('genre'=>$this->ParseID3v2GenreString($value)));
foreach ($this->ParseID3v2GenreString($value) as $genre) {
$genres[] = $genre;
}
}
$thisfile_id3v2['comments']['genre'] = array_unique($genres);
unset($key, $value, $genres, $genre);
}
if (isset($thisfile_id3v2['comments']['track'])) {