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 ( )

Reimplemented from getid3_handler.

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

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 }
$comment
Definition: buildRTE.php:83
parseAPEheaderFooter($APEheaderFooterData)
parseAPEtagFlags($rawflagint)
fseek($bytes, $whence=SEEK_SET)
Definition: getid3.php:1697
fread($bytes)
Definition: getid3.php:1685
LittleEndian2Int($byteword, $signed=false)
Definition: getid3.lib.php:266
GetDataImageSize($imgData)
static intValueSupported($num)
Definition: getid3.lib.php:80
$info
Definition: example_052.php:80

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().

+ 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.

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 }

Referenced by parseAPEtagFlags().

+ 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.

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 }

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

+ 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.

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 }

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

Referenced by Analyze(), and getid3_apetag().

+ 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.

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 }

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

+ 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.

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)

References APEcontentTypeFlagLookup().

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

+ 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.

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 }

References APEcontentTypeFlagLookup().

+ 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 files: