source: branches/MootoolsFileManager-Update/plugins/MootoolsFileManager/mootools-filemanager/Assets/Connector/Assets/getid3/module.tag.apetag.php @ 1300

Last change on this file since 1300 was 1300, checked in by gogo, 9 years ago

Update the MootoolsFileManager? to the latest cpojer with some modifications.
Add a demo for the MFM examples/mootools-file-manager.php
Change the default config for ImageManager? and ExtendedFileManager? for added security.

File size: 13.5 KB
RevLine 
[1300]1<?php
2/////////////////////////////////////////////////////////////////
3/// getID3() by James Heinrich <info@getid3.org>               //
4//  available at http://getid3.sourceforge.net                 //
5//            or http://www.getid3.org                         //
6/////////////////////////////////////////////////////////////////
7// See readme.txt for more details                             //
8/////////////////////////////////////////////////////////////////
9//                                                             //
10// module.tag.apetag.php                                       //
11// module for analyzing APE tags                               //
12// dependencies: NONE                                          //
13//                                                            ///
14/////////////////////////////////////////////////////////////////
15
16class getid3_apetag
17{
18
19        function getid3_apetag(&$fd, &$ThisFileInfo, $overrideendoffset=0) {
20
21                if (!getid3_lib::intValueSupported($ThisFileInfo['filesize'])) {
22                        $ThisFileInfo['warning'][] = 'Unable to check for APEtags because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
23                        return false;
24                }
25
26                $id3v1tagsize     = 128;
27                $apetagheadersize = 32;
28                $lyrics3tagsize   = 10;
29
30                if ($overrideendoffset == 0) {
31
32                        fseek($fd, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END);
33                        $APEfooterID3v1 = fread($fd, $id3v1tagsize + $apetagheadersize + $lyrics3tagsize);
34
35                        //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) {
36                        if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') {
37
38                                // APE tag found before ID3v1
39                                $ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize'] - $id3v1tagsize;
40
41                        //} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) {
42                        } elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') {
43
44                                // APE tag found, no ID3v1
45                                $ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize'];
46
47                        }
48
49                } else {
50
51                        fseek($fd, $overrideendoffset - $apetagheadersize, SEEK_SET);
52                        if (fread($fd, 8) == 'APETAGEX') {
53                                $ThisFileInfo['ape']['tag_offset_end'] = $overrideendoffset;
54                        }
55
56                }
57                if (!isset($ThisFileInfo['ape']['tag_offset_end'])) {
58
59                        // APE tag not found
60                        unset($ThisFileInfo['ape']);
61                        return false;
62
63                }
64
65                // shortcut
66                $thisfile_ape = &$ThisFileInfo['ape'];
67
68                fseek($fd, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET);
69                $APEfooterData = fread($fd, 32);
70                if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) {
71                        $ThisFileInfo['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end'];
72                        return false;
73                }
74
75                if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
76                        fseek($fd, $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize, SEEK_SET);
77                        $thisfile_ape['tag_offset_start'] = ftell($fd);
78                        $APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize);
79                } else {
80                        $thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'];
81                        fseek($fd, $thisfile_ape['tag_offset_start'], SEEK_SET);
82                        $APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize']);
83                }
84                $ThisFileInfo['avdataend'] = $thisfile_ape['tag_offset_start'];
85
86                if (isset($ThisFileInfo['id3v1']['tag_offset_start']) && ($ThisFileInfo['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) {
87                        $ThisFileInfo['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data';
88                        unset($ThisFileInfo['id3v1']);
89                        foreach ($ThisFileInfo['warning'] as $key => $value) {
90                                if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
91                                        unset($ThisFileInfo['warning'][$key]);
92                                        sort($ThisFileInfo['warning']);
93                                        break;
94                                }
95                        }
96                }
97
98                $offset = 0;
99                if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
100                        if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) {
101                                $offset += $apetagheadersize;
102                        } else {
103                                $ThisFileInfo['error'][] = 'Error parsing APE header at offset '.$thisfile_ape['tag_offset_start'];
104                                return false;
105                        }
106                }
107
108                // shortcut
109                $ThisFileInfo['replay_gain'] = array();
110                $thisfile_replaygain = &$ThisFileInfo['replay_gain'];
111
112                for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) {
113                        $value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
114                        $offset += 4;
115                        $item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
116                        $offset += 4;
117                        if (strstr(substr($APEtagData, $offset), "\x00") === false) {
118                                $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);
119                                return false;
120                        }
121                        $ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset;
122                        $item_key      = strtolower(substr($APEtagData, $offset, $ItemKeyLength));
123
124                        // shortcut
125                        $thisfile_ape['items'][$item_key] = array();
126                        $thisfile_ape_items_current = &$thisfile_ape['items'][$item_key];
127
128                        $thisfile_ape_items_current['offset'] = $thisfile_ape['tag_offset_start'] + $offset;
129
130                        $offset += ($ItemKeyLength + 1); // skip 0x00 terminator
131                        $thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size);
132                        $offset += $value_size;
133
134                        $thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags);
135                        switch ($thisfile_ape_items_current['flags']['item_contents_raw']) {
136                                case 0: // UTF-8
137                                case 3: // Locator (URL, filename, etc), UTF-8 encoded
138                                        $thisfile_ape_items_current['data'] = explode("\x00", trim($thisfile_ape_items_current['data']));
139                                        break;
140
141                                default: // binary data
142                                        break;
143                        }
144
145                        switch (strtolower($item_key)) {
146                                case 'replaygain_track_gain':
147                                        $thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
148                                        $thisfile_replaygain['track']['originator'] = 'unspecified';
149                                        break;
150
151                                case 'replaygain_track_peak':
152                                        $thisfile_replaygain['track']['peak']       = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
153                                        $thisfile_replaygain['track']['originator'] = 'unspecified';
154                                        if ($thisfile_replaygain['track']['peak'] <= 0) {
155                                                $ThisFileInfo['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
156                                        }
157                                        break;
158
159                                case 'replaygain_album_gain':
160                                        $thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
161                                        $thisfile_replaygain['album']['originator'] = 'unspecified';
162                                        break;
163
164                                case 'replaygain_album_peak':
165                                        $thisfile_replaygain['album']['peak']       = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
166                                        $thisfile_replaygain['album']['originator'] = 'unspecified';
167                                        if ($thisfile_replaygain['album']['peak'] <= 0) {
168                                                $ThisFileInfo['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
169                                        }
170                                        break;
171
172                                case 'mp3gain_undo':
173                                        list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]);
174                                        $thisfile_replaygain['mp3gain']['undo_left']  = intval($mp3gain_undo_left);
175                                        $thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
176                                        $thisfile_replaygain['mp3gain']['undo_wrap']  = (($mp3gain_undo_wrap == 'Y') ? true : false);
177                                        break;
178
179                                case 'mp3gain_minmax':
180                                        list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]);
181                                        $thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
182                                        $thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
183                                        break;
184
185                                case 'mp3gain_album_minmax':
186                                        list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]);
187                                        $thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
188                                        $thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
189                                        break;
190
191                                case 'tracknumber':
192                                        if (is_array($thisfile_ape_items_current['data'])) {
193                                                foreach ($thisfile_ape_items_current['data'] as $comment) {
194                                                        $thisfile_ape['comments']['track'][] = $comment;
195                                                }
196                                        }
197                                        break;
198
199                                case 'cover art (artist)':
200                                case 'cover art (back)':
201                                case 'cover art (band logo)':
202                                case 'cover art (band)':
203                                case 'cover art (colored fish)':
204                                case 'cover art (composer)':
205                                case 'cover art (conductor)':
206                                case 'cover art (front)':
207                                case 'cover art (icon)':
208                                case 'cover art (illustration)':
209                                case 'cover art (lead)':
210                                case 'cover art (leaflet)':
211                                case 'cover art (lyricist)':
212                                case 'cover art (media)':
213                                case 'cover art (movie scene)':
214                                case 'cover art (other icon)':
215                                case 'cover art (other)':
216                                case 'cover art (performance)':
217                                case 'cover art (publisher logo)':
218                                case 'cover art (recording)':
219                                case 'cover art (studio)':
220                                        // list of possible cover arts from http://taglib-sharp.sourcearchive.com/documentation/2.0.3.0-2/Ape_2Tag_8cs-source.html
221                                        list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = explode("\x00", $thisfile_ape_items_current['data'], 2);
222                                        $thisfile_ape_items_current['dataoffset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename']."\x00");
223
224                                        $thisfile_ape_items_current['image_mime'] = '';
225                                        $imageinfo = array();
226                                        $imagechunkcheck = getid3_lib::GetDataImageSize($thisfile_ape_items_current['data'], $imageinfo);
227                                        $thisfile_ape_items_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]);
228
229                                        if (!isset($ThisFileInfo['ape']['comments']['picture'])) {
230                                                $ThisFileInfo['ape']['comments']['picture'] = array();
231                                        }
232                                        $ThisFileInfo['ape']['comments']['picture'][] = array('data'=>$thisfile_ape_items_current['data'], 'image_mime'=>$thisfile_ape_items_current['image_mime']);
233                                        break;
234
235                                default:
236                                        if (is_array($thisfile_ape_items_current['data'])) {
237                                                foreach ($thisfile_ape_items_current['data'] as $comment) {
238                                                        $thisfile_ape['comments'][strtolower($item_key)][] = $comment;
239                                                }
240                                        }
241                                        break;
242                        }
243
244                }
245                if (empty($thisfile_replaygain)) {
246                        unset($ThisFileInfo['replay_gain']);
247                }
248
249                return true;
250        }
251
252        function parseAPEheaderFooter($APEheaderFooterData) {
253                // http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
254
255                // shortcut
256                $headerfooterinfo['raw'] = array();
257                $headerfooterinfo_raw = &$headerfooterinfo['raw'];
258
259                $headerfooterinfo_raw['footer_tag']   =                  substr($APEheaderFooterData,  0, 8);
260                if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') {
261                        return false;
262                }
263                $headerfooterinfo_raw['version']      = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData,  8, 4));
264                $headerfooterinfo_raw['tagsize']      = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 12, 4));
265                $headerfooterinfo_raw['tag_items']    = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 16, 4));
266                $headerfooterinfo_raw['global_flags'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 20, 4));
267                $headerfooterinfo_raw['reserved']     =                              substr($APEheaderFooterData, 24, 8);
268
269                $headerfooterinfo['tag_version']         = $headerfooterinfo_raw['version'] / 1000;
270                if ($headerfooterinfo['tag_version'] >= 2) {
271                        $headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']);
272                }
273                return $headerfooterinfo;
274        }
275
276        function parseAPEtagFlags($rawflagint) {
277                // "Note: APE Tags 1.0 do not use any of the APE Tag flags.
278                // All are set to zero on creation and ignored on reading."
279                // http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html
280                $flags['header']            = (bool) ($rawflagint & 0x80000000);
281                $flags['footer']            = (bool) ($rawflagint & 0x40000000);
282                $flags['this_is_header']    = (bool) ($rawflagint & 0x20000000);
283                $flags['item_contents_raw'] =        ($rawflagint & 0x00000006) >> 1;
284                $flags['read_only']         = (bool) ($rawflagint & 0x00000001);
285
286                $flags['item_contents']     = $this->APEcontentTypeFlagLookup($flags['item_contents_raw']);
287
288                return $flags;
289        }
290
291        function APEcontentTypeFlagLookup($contenttypeid) {
292                static $APEcontentTypeFlagLookup = array(
293                        0 => 'utf-8',
294                        1 => 'binary',
295                        2 => 'external',
296                        3 => 'reserved'
297                );
298                return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid');
299        }
300
301        function APEtagItemIsUTF8Lookup($itemkey) {
302                static $APEtagItemIsUTF8Lookup = array(
303                        'title',
304                        'subtitle',
305                        'artist',
306                        'album',
307                        'debut album',
308                        'publisher',
309                        'conductor',
310                        'track',
311                        'composer',
312                        'comment',
313                        'copyright',
314                        'publicationright',
315                        'file',
316                        'year',
317                        'record date',
318                        'record location',
319                        'genre',
320                        'media',
321                        'related',
322                        'isrc',
323                        'abstract',
324                        'language',
325                        'bibliography'
326                );
327                return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup);
328        }
329
330}
331
332?>
Note: See TracBrowser for help on using the repository browser.