ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5f
getid3_apetag Class Reference

getID3() by James Heinrich info@.nosp@m.geti.nosp@m.d3.or.nosp@m.g // More...

+ Inheritance diagram for getid3_apetag:
+ Collaboration diagram for getid3_apetag:

Public Member Functions

 getid3_apetag (&$fd, &$ThisFileInfo, $overrideendoffset=0)
 
 parseAPEheaderFooter ($APEheaderFooterData)
 
 parseAPEtagFlags ($rawflagint)
 
 APEcontentTypeFlagLookup ($contenttypeid)
 
 APEtagItemIsUTF8Lookup ($itemkey)
 
 Analyze ()
 
 parseAPEheaderFooter ($APEheaderFooterData)
 
 parseAPEtagFlags ($rawflagint)
 
 APEcontentTypeFlagLookup ($contenttypeid)
 
 APEtagItemIsUTF8Lookup ($itemkey)
 
- Public Member Functions inherited from getid3_handler
 __construct (getID3 $getid3, $call_module=null)
 
 Analyze ()
 
 AnalyzeString ($string)
 
 setStringMode ($string)
 
 saveAttachment ($name, $offset, $length, $image_mime=null)
 

Data Fields

 $inline_attachments = true
 
 $overrideendoffset = 0
 

Additional Inherited Members

- Protected Member Functions inherited from getid3_handler
 ftell ()
 
 fread ($bytes)
 
 fseek ($bytes, $whence=SEEK_SET)
 
 feof ()
 
 isDependencyFor ($module)
 
 error ($text)
 
 warning ($text)
 
 notice ($text)
 
- Protected Attributes inherited from getid3_handler
 $getid3
 
 $data_string_flag = false
 
 $data_string = ''
 
 $data_string_position = 0
 
 $data_string_length = 0
 

Detailed Description

getID3() by James Heinrich info@.nosp@m.geti.nosp@m.d3.or.nosp@m.g //

Definition at line 16 of file module.tag.apetag.php.

Member Function Documentation

◆ Analyze()

getid3_apetag::Analyze ( )

Definition at line 22 of file module.tag.apetag.php.

References $comment, $info, $overrideendoffset, getid3_handler\fread(), getid3_handler\fseek(), getid3_handler\ftell(), getid3_lib\GetDataImageSize(), getid3_lib\intValueSupported(), getid3_lib\LittleEndian2Int(), parseAPEheaderFooter(), and parseAPEtagFlags().

22  {
23  $info = &$this->getid3->info;
24 
25  if (!getid3_lib::intValueSupported($info['filesize'])) {
26  $info['warning'][] = 'Unable to check for APEtags because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
27  return false;
28  }
29 
30  $id3v1tagsize = 128;
31  $apetagheadersize = 32;
32  $lyrics3tagsize = 10;
33 
34  if ($this->overrideendoffset == 0) {
35 
36  $this->fseek(0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END);
37  $APEfooterID3v1 = $this->fread($id3v1tagsize + $apetagheadersize + $lyrics3tagsize);
38 
39  //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) {
40  if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') {
41 
42  // APE tag found before ID3v1
43  $info['ape']['tag_offset_end'] = $info['filesize'] - $id3v1tagsize;
44 
45  //} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) {
46  } elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') {
47 
48  // APE tag found, no ID3v1
49  $info['ape']['tag_offset_end'] = $info['filesize'];
50 
51  }
52 
53  } else {
54 
55  $this->fseek($this->overrideendoffset - $apetagheadersize);
56  if ($this->fread(8) == 'APETAGEX') {
57  $info['ape']['tag_offset_end'] = $this->overrideendoffset;
58  }
59 
60  }
61  if (!isset($info['ape']['tag_offset_end'])) {
62 
63  // APE tag not found
64  unset($info['ape']);
65  return false;
66 
67  }
68 
69  // shortcut
70  $thisfile_ape = &$info['ape'];
71 
72  $this->fseek($thisfile_ape['tag_offset_end'] - $apetagheadersize);
73  $APEfooterData = $this->fread(32);
74  if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) {
75  $info['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end'];
76  return false;
77  }
78 
79  if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
80  $this->fseek($thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize);
81  $thisfile_ape['tag_offset_start'] = $this->ftell();
82  $APEtagData = $this->fread($thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize);
83  } else {
84  $thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'];
85  $this->fseek($thisfile_ape['tag_offset_start']);
86  $APEtagData = $this->fread($thisfile_ape['footer']['raw']['tagsize']);
87  }
88  $info['avdataend'] = $thisfile_ape['tag_offset_start'];
89 
90  if (isset($info['id3v1']['tag_offset_start']) && ($info['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) {
91  $info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data';
92  unset($info['id3v1']);
93  foreach ($info['warning'] as $key => $value) {
94  if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
95  unset($info['warning'][$key]);
96  sort($info['warning']);
97  break;
98  }
99  }
100  }
101 
102  $offset = 0;
103  if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
104  if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) {
105  $offset += $apetagheadersize;
106  } else {
107  $info['error'][] = 'Error parsing APE header at offset '.$thisfile_ape['tag_offset_start'];
108  return false;
109  }
110  }
111 
112  // shortcut
113  $info['replay_gain'] = array();
114  $thisfile_replaygain = &$info['replay_gain'];
115 
116  for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) {
117  $value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
118  $offset += 4;
119  $item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
120  $offset += 4;
121  if (strstr(substr($APEtagData, $offset), "\x00") === false) {
122  $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);
123  return false;
124  }
125  $ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset;
126  $item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength));
127 
128  // shortcut
129  $thisfile_ape['items'][$item_key] = array();
130  $thisfile_ape_items_current = &$thisfile_ape['items'][$item_key];
131 
132  $thisfile_ape_items_current['offset'] = $thisfile_ape['tag_offset_start'] + $offset;
133 
134  $offset += ($ItemKeyLength + 1); // skip 0x00 terminator
135  $thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size);
136  $offset += $value_size;
137 
138  $thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags);
139  switch ($thisfile_ape_items_current['flags']['item_contents_raw']) {
140  case 0: // UTF-8
141  case 2: // Locator (URL, filename, etc), UTF-8 encoded
142  $thisfile_ape_items_current['data'] = explode("\x00", $thisfile_ape_items_current['data']);
143  break;
144 
145  case 1: // binary data
146  default:
147  break;
148  }
149 
150  switch (strtolower($item_key)) {
151  // http://wiki.hydrogenaud.io/index.php?title=ReplayGain#MP3Gain
152  case 'replaygain_track_gain':
153  if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
154  $thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
155  $thisfile_replaygain['track']['originator'] = 'unspecified';
156  } else {
157  $info['warning'][] = 'MP3gainTrackGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
158  }
159  break;
160 
161  case 'replaygain_track_peak':
162  if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
163  $thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
164  $thisfile_replaygain['track']['originator'] = 'unspecified';
165  if ($thisfile_replaygain['track']['peak'] <= 0) {
166  $info['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
167  }
168  } else {
169  $info['warning'][] = 'MP3gainTrackPeak value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
170  }
171  break;
172 
173  case 'replaygain_album_gain':
174  if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
175  $thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
176  $thisfile_replaygain['album']['originator'] = 'unspecified';
177  } else {
178  $info['warning'][] = 'MP3gainAlbumGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
179  }
180  break;
181 
182  case 'replaygain_album_peak':
183  if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
184  $thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
185  $thisfile_replaygain['album']['originator'] = 'unspecified';
186  if ($thisfile_replaygain['album']['peak'] <= 0) {
187  $info['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
188  }
189  } else {
190  $info['warning'][] = 'MP3gainAlbumPeak value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
191  }
192  break;
193 
194  case 'mp3gain_undo':
195  if (preg_match('#^[\\-\\+][0-9]{3},[\\-\\+][0-9]{3},[NW]$#', $thisfile_ape_items_current['data'][0])) {
196  list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]);
197  $thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left);
198  $thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
199  $thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false);
200  } else {
201  $info['warning'][] = 'MP3gainUndo value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
202  }
203  break;
204 
205  case 'mp3gain_minmax':
206  if (preg_match('#^[0-9]{3},[0-9]{3}$#', $thisfile_ape_items_current['data'][0])) {
207  list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]);
208  $thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
209  $thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
210  } else {
211  $info['warning'][] = 'MP3gainMinMax value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
212  }
213  break;
214 
215  case 'mp3gain_album_minmax':
216  if (preg_match('#^[0-9]{3},[0-9]{3}$#', $thisfile_ape_items_current['data'][0])) {
217  list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]);
218  $thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
219  $thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
220  } else {
221  $info['warning'][] = 'MP3gainAlbumMinMax value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
222  }
223  break;
224 
225  case 'tracknumber':
226  if (is_array($thisfile_ape_items_current['data'])) {
227  foreach ($thisfile_ape_items_current['data'] as $comment) {
228  $thisfile_ape['comments']['track'][] = $comment;
229  }
230  }
231  break;
232 
233  case 'cover art (artist)':
234  case 'cover art (back)':
235  case 'cover art (band logo)':
236  case 'cover art (band)':
237  case 'cover art (colored fish)':
238  case 'cover art (composer)':
239  case 'cover art (conductor)':
240  case 'cover art (front)':
241  case 'cover art (icon)':
242  case 'cover art (illustration)':
243  case 'cover art (lead)':
244  case 'cover art (leaflet)':
245  case 'cover art (lyricist)':
246  case 'cover art (media)':
247  case 'cover art (movie scene)':
248  case 'cover art (other icon)':
249  case 'cover art (other)':
250  case 'cover art (performance)':
251  case 'cover art (publisher logo)':
252  case 'cover art (recording)':
253  case 'cover art (studio)':
254  // list of possible cover arts from http://taglib-sharp.sourcearchive.com/documentation/2.0.3.0-2/Ape_2Tag_8cs-source.html
255  if (is_array($thisfile_ape_items_current['data'])) {
256  $info['warning'][] = 'APEtag "'.$item_key.'" should be flagged as Binary data, but was incorrectly flagged as UTF-8';
257  $thisfile_ape_items_current['data'] = implode("\x00", $thisfile_ape_items_current['data']);
258  }
259  list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = explode("\x00", $thisfile_ape_items_current['data'], 2);
260  $thisfile_ape_items_current['data_offset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename']."\x00");
261  $thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']);
262 
263  do {
264  $thisfile_ape_items_current['image_mime'] = '';
265  $imageinfo = array();
266  $imagechunkcheck = getid3_lib::GetDataImageSize($thisfile_ape_items_current['data'], $imageinfo);
267  if (($imagechunkcheck === false) || !isset($imagechunkcheck[2])) {
268  $info['warning'][] = 'APEtag "'.$item_key.'" contains invalid image data';
269  break;
270  }
271  $thisfile_ape_items_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]);
272 
273  if ($this->inline_attachments === false) {
274  // skip entirely
275  unset($thisfile_ape_items_current['data']);
276  break;
277  }
278  if ($this->inline_attachments === true) {
279  // great
280  } elseif (is_int($this->inline_attachments)) {
281  if ($this->inline_attachments < $thisfile_ape_items_current['data_length']) {
282  // too big, skip
283  $info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' is too large to process inline ('.number_format($thisfile_ape_items_current['data_length']).' bytes)';
284  unset($thisfile_ape_items_current['data']);
285  break;
286  }
287  } elseif (is_string($this->inline_attachments)) {
288  $this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR);
289  if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) {
290  // cannot write, skip
291  $info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$this->inline_attachments.'" (not writable)';
292  unset($thisfile_ape_items_current['data']);
293  break;
294  }
295  }
296  // if we get this far, must be OK
297  if (is_string($this->inline_attachments)) {
298  $destination_filename = $this->inline_attachments.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$thisfile_ape_items_current['data_offset'];
299  if (!file_exists($destination_filename) || is_writable($destination_filename)) {
300  file_put_contents($destination_filename, $thisfile_ape_items_current['data']);
301  } else {
302  $info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$destination_filename.'" (not writable)';
303  }
304  $thisfile_ape_items_current['data_filename'] = $destination_filename;
305  unset($thisfile_ape_items_current['data']);
306  } else {
307  if (!isset($info['ape']['comments']['picture'])) {
308  $info['ape']['comments']['picture'] = array();
309  }
310  $comments_picture_data = array();
311  foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) {
312  if (isset($thisfile_ape_items_current[$picture_key])) {
313  $comments_picture_data[$picture_key] = $thisfile_ape_items_current[$picture_key];
314  }
315  }
316  $info['ape']['comments']['picture'][] = $comments_picture_data;
317  unset($comments_picture_data);
318  }
319  } while (false);
320  break;
321 
322  default:
323  if (is_array($thisfile_ape_items_current['data'])) {
324  foreach ($thisfile_ape_items_current['data'] as $comment) {
325  $thisfile_ape['comments'][strtolower($item_key)][] = $comment;
326  }
327  }
328  break;
329  }
330 
331  }
332  if (empty($thisfile_replaygain)) {
333  unset($info['replay_gain']);
334  }
335  return true;
336  }
static intValueSupported($num)
Definition: getid3.lib.php:80
LittleEndian2Int($byteword, $signed=false)
Definition: getid3.lib.php:266
GetDataImageSize($imgData)
$info
Definition: example_052.php:80
fread($bytes)
Definition: getid3.php:1685
$comment
Definition: buildRTE.php:83
parseAPEtagFlags($rawflagint)
parseAPEheaderFooter($APEheaderFooterData)
fseek($bytes, $whence=SEEK_SET)
Definition: getid3.php:1697
+ Here is the call graph for this function:

◆ APEcontentTypeFlagLookup() [1/2]

getid3_apetag::APEcontentTypeFlagLookup (   $contenttypeid)

Definition at line 243 of file module.tag.apetag.php.

Referenced by parseAPEtagFlags().

243  {
244  static $APEcontentTypeFlagLookup = array(
245  0 => 'utf-8',
246  1 => 'binary',
247  2 => 'external',
248  3 => 'reserved'
249  );
250  return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid');
251  }
+ Here is the caller graph for this function:

◆ APEcontentTypeFlagLookup() [2/2]

getid3_apetag::APEcontentTypeFlagLookup (   $contenttypeid)

Definition at line 377 of file module.tag.apetag.php.

377  {
378  static $APEcontentTypeFlagLookup = array(
379  0 => 'utf-8',
380  1 => 'binary',
381  2 => 'external',
382  3 => 'reserved'
383  );
384  return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid');
385  }

◆ APEtagItemIsUTF8Lookup() [1/2]

getid3_apetag::APEtagItemIsUTF8Lookup (   $itemkey)

Definition at line 253 of file module.tag.apetag.php.

253  {
254  static $APEtagItemIsUTF8Lookup = array(
255  'title',
256  'subtitle',
257  'artist',
258  'album',
259  'debut album',
260  'publisher',
261  'conductor',
262  'track',
263  'composer',
264  'comment',
265  'copyright',
266  'publicationright',
267  'file',
268  'year',
269  'record date',
270  'record location',
271  'genre',
272  'media',
273  'related',
274  'isrc',
275  'abstract',
276  'language',
277  'bibliography'
278  );
279  return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup);
280  }

◆ APEtagItemIsUTF8Lookup() [2/2]

getid3_apetag::APEtagItemIsUTF8Lookup (   $itemkey)

Definition at line 387 of file module.tag.apetag.php.

387  {
388  static $APEtagItemIsUTF8Lookup = array(
389  'title',
390  'subtitle',
391  'artist',
392  'album',
393  'debut album',
394  'publisher',
395  'conductor',
396  'track',
397  'composer',
398  'comment',
399  'copyright',
400  'publicationright',
401  'file',
402  'year',
403  'record date',
404  'record location',
405  'genre',
406  'media',
407  'related',
408  'isrc',
409  'abstract',
410  'language',
411  'bibliography'
412  );
413  return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup);
414  }

◆ getid3_apetag()

getid3_apetag::getid3_apetag ( $fd,
$ThisFileInfo,
  $overrideendoffset = 0 
)

Definition at line 19 of file module.tag.apetag.php.

References $comment, $overrideendoffset, getid3_handler\fread(), getid3_handler\fseek(), getid3_handler\ftell(), getid3_lib\LittleEndian2Int(), parseAPEheaderFooter(), and parseAPEtagFlags().

19  {
20  $id3v1tagsize = 128;
21  $apetagheadersize = 32;
22  $lyrics3tagsize = 10;
23 
24  if ($overrideendoffset == 0) {
25 
26  fseek($fd, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END);
27  $APEfooterID3v1 = fread($fd, $id3v1tagsize + $apetagheadersize + $lyrics3tagsize);
28 
29  //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) {
30  if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') {
31 
32  // APE tag found before ID3v1
33  $ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize'] - $id3v1tagsize;
34 
35  //} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) {
36  } elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') {
37 
38  // APE tag found, no ID3v1
39  $ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize'];
40 
41  }
42 
43  } else {
44 
45  fseek($fd, $overrideendoffset - $apetagheadersize, SEEK_SET);
46  if (fread($fd, 8) == 'APETAGEX') {
47  $ThisFileInfo['ape']['tag_offset_end'] = $overrideendoffset;
48  }
49 
50  }
51  if (!isset($ThisFileInfo['ape']['tag_offset_end'])) {
52 
53  // APE tag not found
54  unset($ThisFileInfo['ape']);
55  return false;
56 
57  }
58 
59  // shortcut
60  $thisfile_ape = &$ThisFileInfo['ape'];
61 
62  fseek($fd, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET);
63  $APEfooterData = fread($fd, 32);
64  if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) {
65  $ThisFileInfo['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end'];
66  return false;
67  }
68 
69  if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
70  fseek($fd, $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize, SEEK_SET);
71  $thisfile_ape['tag_offset_start'] = ftell($fd);
72  $APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize);
73  } else {
74  $thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'];
75  fseek($fd, $thisfile_ape['tag_offset_start'], SEEK_SET);
76  $APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize']);
77  }
78  $ThisFileInfo['avdataend'] = $thisfile_ape['tag_offset_start'];
79 
80  if (isset($ThisFileInfo['id3v1']['tag_offset_start']) && ($ThisFileInfo['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) {
81  $ThisFileInfo['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data';
82  unset($ThisFileInfo['id3v1']);
83  foreach ($ThisFileInfo['warning'] as $key => $value) {
84  if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
85  unset($ThisFileInfo['warning'][$key]);
86  sort($ThisFileInfo['warning']);
87  break;
88  }
89  }
90  }
91 
92  $offset = 0;
93  if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
94  if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) {
95  $offset += $apetagheadersize;
96  } else {
97  $ThisFileInfo['error'][] = 'Error parsing APE header at offset '.$thisfile_ape['tag_offset_start'];
98  return false;
99  }
100  }
101 
102  // shortcut
103  $ThisFileInfo['replay_gain'] = array();
104  $thisfile_replaygain = &$ThisFileInfo['replay_gain'];
105 
106  for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) {
107  $value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
108  $offset += 4;
109  $item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
110  $offset += 4;
111  if (strstr(substr($APEtagData, $offset), "\x00") === false) {
112  $ThisFileInfo['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);
113  return false;
114  }
115  $ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset;
116  $item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength));
117 
118  // shortcut
119  $thisfile_ape['items'][$item_key] = array();
120  $thisfile_ape_items_current = &$thisfile_ape['items'][$item_key];
121 
122  $offset += ($ItemKeyLength + 1); // skip 0x00 terminator
123  $thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size);
124  $offset += $value_size;
125 
126  $thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags);
127  switch ($thisfile_ape_items_current['flags']['item_contents_raw']) {
128  case 0: // UTF-8
129  case 3: // Locator (URL, filename, etc), UTF-8 encoded
130  $thisfile_ape_items_current['data'] = explode("\x00", trim($thisfile_ape_items_current['data']));
131  break;
132 
133  default: // binary data
134  break;
135  }
136 
137  switch (strtolower($item_key)) {
138  case 'replaygain_track_gain':
139  $thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
140  $thisfile_replaygain['track']['originator'] = 'unspecified';
141  break;
142 
143  case 'replaygain_track_peak':
144  $thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
145  $thisfile_replaygain['track']['originator'] = 'unspecified';
146  if ($thisfile_replaygain['track']['peak'] <= 0) {
147  $ThisFileInfo['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
148  }
149  break;
150 
151  case 'replaygain_album_gain':
152  $thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
153  $thisfile_replaygain['album']['originator'] = 'unspecified';
154  break;
155 
156  case 'replaygain_album_peak':
157  $thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
158  $thisfile_replaygain['album']['originator'] = 'unspecified';
159  if ($thisfile_replaygain['album']['peak'] <= 0) {
160  $ThisFileInfo['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
161  }
162  break;
163 
164  case 'mp3gain_undo':
165  list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]);
166  $thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left);
167  $thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
168  $thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false);
169  break;
170 
171  case 'mp3gain_minmax':
172  list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]);
173  $thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
174  $thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
175  break;
176 
177  case 'mp3gain_album_minmax':
178  list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]);
179  $thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
180  $thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
181  break;
182 
183  case 'tracknumber':
184  foreach ($thisfile_ape_items_current['data'] as $comment) {
185  $thisfile_ape['comments']['track'][] = $comment;
186  }
187  break;
188 
189  default:
190  foreach ($thisfile_ape_items_current['data'] as $comment) {
191  $thisfile_ape['comments'][strtolower($item_key)][] = $comment;
192  }
193  break;
194  }
195 
196  }
197  if (empty($thisfile_replaygain)) {
198  unset($ThisFileInfo['replay_gain']);
199  }
200 
201  return true;
202  }
LittleEndian2Int($byteword, $signed=false)
Definition: getid3.lib.php:266
fread($bytes)
Definition: getid3.php:1685
$comment
Definition: buildRTE.php:83
parseAPEtagFlags($rawflagint)
parseAPEheaderFooter($APEheaderFooterData)
fseek($bytes, $whence=SEEK_SET)
Definition: getid3.php:1697
+ Here is the call graph for this function:

◆ parseAPEheaderFooter() [1/2]

getid3_apetag::parseAPEheaderFooter (   $APEheaderFooterData)

Definition at line 204 of file module.tag.apetag.php.

References getid3_lib\LittleEndian2Int(), and parseAPEtagFlags().

Referenced by Analyze(), and getid3_apetag().

204  {
205  // http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
206 
207  // shortcut
208  $headerfooterinfo['raw'] = array();
209  $headerfooterinfo_raw = &$headerfooterinfo['raw'];
210 
211  $headerfooterinfo_raw['footer_tag'] = substr($APEheaderFooterData, 0, 8);
212  if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') {
213  return false;
214  }
215  $headerfooterinfo_raw['version'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 8, 4));
216  $headerfooterinfo_raw['tagsize'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 12, 4));
217  $headerfooterinfo_raw['tag_items'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 16, 4));
218  $headerfooterinfo_raw['global_flags'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 20, 4));
219  $headerfooterinfo_raw['reserved'] = substr($APEheaderFooterData, 24, 8);
220 
221  $headerfooterinfo['tag_version'] = $headerfooterinfo_raw['version'] / 1000;
222  if ($headerfooterinfo['tag_version'] >= 2) {
223  $headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']);
224  }
225  return $headerfooterinfo;
226  }
LittleEndian2Int($byteword, $signed=false)
Definition: getid3.lib.php:266
parseAPEtagFlags($rawflagint)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ parseAPEheaderFooter() [2/2]

getid3_apetag::parseAPEheaderFooter (   $APEheaderFooterData)

Definition at line 338 of file module.tag.apetag.php.

References getid3_lib\LittleEndian2Int(), and parseAPEtagFlags().

338  {
339  // http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
340 
341  // shortcut
342  $headerfooterinfo['raw'] = array();
343  $headerfooterinfo_raw = &$headerfooterinfo['raw'];
344 
345  $headerfooterinfo_raw['footer_tag'] = substr($APEheaderFooterData, 0, 8);
346  if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') {
347  return false;
348  }
349  $headerfooterinfo_raw['version'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 8, 4));
350  $headerfooterinfo_raw['tagsize'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 12, 4));
351  $headerfooterinfo_raw['tag_items'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 16, 4));
352  $headerfooterinfo_raw['global_flags'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 20, 4));
353  $headerfooterinfo_raw['reserved'] = substr($APEheaderFooterData, 24, 8);
354 
355  $headerfooterinfo['tag_version'] = $headerfooterinfo_raw['version'] / 1000;
356  if ($headerfooterinfo['tag_version'] >= 2) {
357  $headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']);
358  }
359  return $headerfooterinfo;
360  }
LittleEndian2Int($byteword, $signed=false)
Definition: getid3.lib.php:266
parseAPEtagFlags($rawflagint)
+ Here is the call graph for this function:

◆ parseAPEtagFlags() [1/2]

getid3_apetag::parseAPEtagFlags (   $rawflagint)

Definition at line 228 of file module.tag.apetag.php.

References APEcontentTypeFlagLookup().

Referenced by Analyze(), getid3_apetag(), and parseAPEheaderFooter().

228  {
229  // "Note: APE Tags 1.0 do not use any of the APE Tag flags.
230  // All are set to zero on creation and ignored on reading."
231  // http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html
232  $flags['header'] = (bool) ($rawflagint & 0x80000000);
233  $flags['footer'] = (bool) ($rawflagint & 0x40000000);
234  $flags['this_is_header'] = (bool) ($rawflagint & 0x20000000);
235  $flags['item_contents_raw'] = ($rawflagint & 0x00000006) >> 1;
236  $flags['read_only'] = (bool) ($rawflagint & 0x00000001);
237 
238  $flags['item_contents'] = $this->APEcontentTypeFlagLookup($flags['item_contents_raw']);
239 
240  return $flags;
241  }
APEcontentTypeFlagLookup($contenttypeid)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ parseAPEtagFlags() [2/2]

getid3_apetag::parseAPEtagFlags (   $rawflagint)

Definition at line 362 of file module.tag.apetag.php.

References APEcontentTypeFlagLookup().

362  {
363  // "Note: APE Tags 1.0 do not use any of the APE Tag flags.
364  // All are set to zero on creation and ignored on reading."
365  // http://wiki.hydrogenaud.io/index.php?title=Ape_Tags_Flags
366  $flags['header'] = (bool) ($rawflagint & 0x80000000);
367  $flags['footer'] = (bool) ($rawflagint & 0x40000000);
368  $flags['this_is_header'] = (bool) ($rawflagint & 0x20000000);
369  $flags['item_contents_raw'] = ($rawflagint & 0x00000006) >> 1;
370  $flags['read_only'] = (bool) ($rawflagint & 0x00000001);
371 
372  $flags['item_contents'] = $this->APEcontentTypeFlagLookup($flags['item_contents_raw']);
373 
374  return $flags;
375  }
APEcontentTypeFlagLookup($contenttypeid)
+ Here is the call graph for this function:

Field Documentation

◆ $inline_attachments

getid3_apetag::$inline_attachments = true

Definition at line 19 of file module.tag.apetag.php.

◆ $overrideendoffset

getid3_apetag::$overrideendoffset = 0

Definition at line 20 of file module.tag.apetag.php.

Referenced by Analyze(), and getid3_apetag().


The documentation for this class was generated from the following file: