comparison e2gallerypro/e2upload/Backend/Assets/getid3/module.tag.apetag.php @ 3:3f6b44aa6b35 judyates

[svn r4] added ability to buy stuff, from a Prints page, but it doesn't work well with the css, and it also has not been fitted into the perl make system.
author rlm
date Mon, 22 Feb 2010 08:02:39 -0500
parents
children
comparison
equal deleted inserted replaced
2:670229c4eb4b 3:3f6b44aa6b35
1 <?php
2 // +----------------------------------------------------------------------+
3 // | PHP version 5 |
4 // +----------------------------------------------------------------------+
5 // | Copyright (c) 2002-2006 James Heinrich, Allan Hansen |
6 // +----------------------------------------------------------------------+
7 // | This source file is subject to version 2 of the GPL license, |
8 // | that is bundled with this package in the file license.txt and is |
9 // | available through the world-wide-web at the following url: |
10 // | http://www.gnu.org/copyleft/gpl.html |
11 // +----------------------------------------------------------------------+
12 // | getID3() - http://getid3.sourceforge.net or http://www.getid3.org |
13 // +----------------------------------------------------------------------+
14 // | Authors: James Heinrich <infoØgetid3*org> |
15 // | Allan Hansen <ahØartemis*dk> |
16 // +----------------------------------------------------------------------+
17 // | module.tag.apetag.php |
18 // | module for analyzing APE tags |
19 // | dependencies: NONE |
20 // +----------------------------------------------------------------------+
21 //
22 // $Id: module.tag.apetag.php,v 1.5 2006/11/16 14:05:21 ah Exp $
23
24
25
26 class getid3_apetag extends getid3_handler
27 {
28 /*
29 ID3v1_TAG_SIZE = 128;
30 APETAG_HEADER_SIZE = 32;
31 LYRICS3_TAG_SIZE = 10;
32 */
33
34 public $option_override_end_offset = 0;
35
36
37
38 public function Analyze() {
39
40 $getid3 = $this->getid3;
41
42 if ($this->option_override_end_offset == 0) {
43
44 fseek($getid3->fp, 0 - 170, SEEK_END); // 170 = ID3v1_TAG_SIZE + APETAG_HEADER_SIZE + LYRICS3_TAG_SIZE
45 $apetag_footer_id3v1 = fread($getid3->fp, 170); // 170 = ID3v1_TAG_SIZE + APETAG_HEADER_SIZE + LYRICS3_TAG_SIZE
46
47 // APE tag found before ID3v1
48 if (substr($apetag_footer_id3v1, strlen($apetag_footer_id3v1) - 160, 8) == 'APETAGEX') { // 160 = ID3v1_TAG_SIZE + APETAG_HEADER_SIZE
49 $getid3->info['ape']['tag_offset_end'] = filesize($getid3->filename) - 128; // 128 = ID3v1_TAG_SIZE
50 }
51
52 // APE tag found, no ID3v1
53 elseif (substr($apetag_footer_id3v1, strlen($apetag_footer_id3v1) - 32, 8) == 'APETAGEX') { // 32 = APETAG_HEADER_SIZE
54 $getid3->info['ape']['tag_offset_end'] = filesize($getid3->filename);
55 }
56
57 }
58 else {
59
60 fseek($getid3->fp, $this->option_override_end_offset - 32, SEEK_SET); // 32 = APETAG_HEADER_SIZE
61 if (fread($getid3->fp, 8) == 'APETAGEX') {
62 $getid3->info['ape']['tag_offset_end'] = $this->option_override_end_offset;
63 }
64
65 }
66
67 // APE tag not found
68 if (!@$getid3->info['ape']['tag_offset_end']) {
69 return false;
70 }
71
72 // Shortcut
73 $info_ape = &$getid3->info['ape'];
74
75 // Read and parse footer
76 fseek($getid3->fp, $info_ape['tag_offset_end'] - 32, SEEK_SET); // 32 = APETAG_HEADER_SIZE
77 $apetag_footer_data = fread($getid3->fp, 32);
78 if (!($this->ParseAPEHeaderFooter($apetag_footer_data, $info_ape['footer']))) {
79 throw new getid3_exception('Error parsing APE footer at offset '.$info_ape['tag_offset_end']);
80 }
81
82 if (isset($info_ape['footer']['flags']['header']) && $info_ape['footer']['flags']['header']) {
83 fseek($getid3->fp, $info_ape['tag_offset_end'] - $info_ape['footer']['raw']['tagsize'] - 32, SEEK_SET);
84 $info_ape['tag_offset_start'] = ftell($getid3->fp);
85 $apetag_data = fread($getid3->fp, $info_ape['footer']['raw']['tagsize'] + 32);
86 }
87 else {
88 $info_ape['tag_offset_start'] = $info_ape['tag_offset_end'] - $info_ape['footer']['raw']['tagsize'];
89 fseek($getid3->fp, $info_ape['tag_offset_start'], SEEK_SET);
90 $apetag_data = fread($getid3->fp, $info_ape['footer']['raw']['tagsize']);
91 }
92 $getid3->info['avdataend'] = $info_ape['tag_offset_start'];
93
94 if (isset($getid3->info['id3v1']['tag_offset_start']) && ($getid3->info['id3v1']['tag_offset_start'] < $info_ape['tag_offset_end'])) {
95 $getid3->warning('ID3v1 tag information ignored since it appears to be a false synch in APEtag data');
96 unset($getid3->info['id3v1']);
97 }
98
99 $offset = 0;
100 if (isset($info_ape['footer']['flags']['header']) && $info_ape['footer']['flags']['header']) {
101 if (!$this->ParseAPEHeaderFooter(substr($apetag_data, 0, 32), $info_ape['header'])) {
102 throw new getid3_exception('Error parsing APE header at offset '.$info_ape['tag_offset_start']);
103 }
104 $offset = 32;
105 }
106
107 // Shortcut
108 $getid3->info['replay_gain'] = array ();
109 $info_replaygain = &$getid3->info['replay_gain'];
110
111 for ($i = 0; $i < $info_ape['footer']['raw']['tag_items']; $i++) {
112 $value_size = getid3_lib::LittleEndian2Int(substr($apetag_data, $offset, 4));
113 $item_flags = getid3_lib::LittleEndian2Int(substr($apetag_data, $offset + 4, 4));
114 $offset += 8;
115
116 if (strstr(substr($apetag_data, $offset), "\x00") === false) {
117 throw new getid3_exception('Cannot find null-byte (0x00) seperator between ItemKey #'.$i.' and value. ItemKey starts ' . $offset . ' bytes into the APE tag, at file offset '.($info_ape['tag_offset_start'] + $offset));
118 }
119
120 $item_key_length = strpos($apetag_data, "\x00", $offset) - $offset;
121 $item_key = strtolower(substr($apetag_data, $offset, $item_key_length));
122
123 // Shortcut
124 $info_ape['items'][$item_key] = array ();
125 $info_ape_items_current = &$info_ape['items'][$item_key];
126
127 $offset += $item_key_length + 1; // skip 0x00 terminator
128 $info_ape_items_current['data'] = substr($apetag_data, $offset, $value_size);
129 $offset += $value_size;
130
131
132 $info_ape_items_current['flags'] = $this->ParseAPEtagFlags($item_flags);
133
134 switch ($info_ape_items_current['flags']['item_contents_raw']) {
135 case 0: // UTF-8
136 case 3: // Locator (URL, filename, etc), UTF-8 encoded
137 $info_ape_items_current['data'] = explode("\x00", trim($info_ape_items_current['data']));
138 break;
139
140 default: // binary data
141 break;
142 }
143
144 switch (strtolower($item_key)) {
145 case 'replaygain_track_gain':
146 $info_replaygain['track']['adjustment'] = (float)str_replace(',', '.', $info_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
147 $info_replaygain['track']['originator'] = 'unspecified';
148 break;
149
150 case 'replaygain_track_peak':
151 $info_replaygain['track']['peak'] = (float)str_replace(',', '.', $info_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
152 $info_replaygain['track']['originator'] = 'unspecified';
153 if ($info_replaygain['track']['peak'] <= 0) {
154 $getid3->warning('ReplayGain Track peak from APEtag appears invalid: '.$info_replaygain['track']['peak'].' (original value = "'.$info_ape_items_current['data'][0].'")');
155 }
156 break;
157
158 case 'replaygain_album_gain':
159 $info_replaygain['album']['adjustment'] = (float)str_replace(',', '.', $info_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
160 $info_replaygain['album']['originator'] = 'unspecified';
161 break;
162
163 case 'replaygain_album_peak':
164 $info_replaygain['album']['peak'] = (float)str_replace(',', '.', $info_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
165 $info_replaygain['album']['originator'] = 'unspecified';
166 if ($info_replaygain['album']['peak'] <= 0) {
167 $getid3->warning('ReplayGain Album peak from APEtag appears invalid: '.$info_replaygain['album']['peak'].' (original value = "'.$info_ape_items_current['data'][0].'")');
168 }
169 break;
170
171 case 'mp3gain_undo':
172 list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $info_ape_items_current['data'][0]);
173 $info_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left);
174 $info_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
175 $info_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false);
176 break;
177
178 case 'mp3gain_minmax':
179 list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $info_ape_items_current['data'][0]);
180 $info_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
181 $info_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
182 break;
183
184 case 'mp3gain_album_minmax':
185 list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $info_ape_items_current['data'][0]);
186 $info_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
187 $info_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
188 break;
189
190 case 'tracknumber':
191 foreach ($info_ape_items_current['data'] as $comment) {
192 $info_ape['comments']['track'][] = $comment;
193 }
194 break;
195
196 default:
197 foreach ($info_ape_items_current['data'] as $comment) {
198 $info_ape['comments'][strtolower($item_key)][] = $comment;
199 }
200 break;
201 }
202
203 }
204 if (empty($info_replaygain)) {
205 unset($getid3->info['replay_gain']);
206 }
207
208 return true;
209 }
210
211
212
213 protected function ParseAPEheaderFooter($data, &$target) {
214
215 // http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
216
217 if (substr($data, 0, 8) != 'APETAGEX') {
218 return false;
219 }
220
221 // shortcut
222 $target['raw'] = array ();
223 $target_raw = &$target['raw'];
224
225 $target_raw['footer_tag'] = 'APETAGEX';
226
227 getid3_lib::ReadSequence("LittleEndian2Int", $target_raw, $data, 8,
228 array (
229 'version' => 4,
230 'tagsize' => 4,
231 'tag_items' => 4,
232 'global_flags' => 4
233 )
234 );
235 $target_raw['reserved'] = substr($data, 24, 8);
236
237 $target['tag_version'] = $target_raw['version'] / 1000;
238 if ($target['tag_version'] >= 2) {
239
240 $target['flags'] = $this->ParseAPEtagFlags($target_raw['global_flags']);
241 }
242
243 return true;
244 }
245
246
247
248 protected function ParseAPEtagFlags($raw_flag_int) {
249
250 // "Note: APE Tags 1.0 do not use any of the APE Tag flags.
251 // All are set to zero on creation and ignored on reading."
252 // http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html
253
254 $target['header'] = (bool) ($raw_flag_int & 0x80000000);
255 $target['footer'] = (bool) ($raw_flag_int & 0x40000000);
256 $target['this_is_header'] = (bool) ($raw_flag_int & 0x20000000);
257 $target['item_contents_raw'] = ($raw_flag_int & 0x00000006) >> 1;
258 $target['read_only'] = (bool) ($raw_flag_int & 0x00000001);
259
260 $target['item_contents'] = getid3_apetag::APEcontentTypeFlagLookup($target['item_contents_raw']);
261
262 return $target;
263 }
264
265
266
267 public static function APEcontentTypeFlagLookup($content_type_id) {
268
269 static $lookup = array (
270 0 => 'utf-8',
271 1 => 'binary',
272 2 => 'external',
273 3 => 'reserved'
274 );
275 return (isset($lookup[$content_type_id]) ? $lookup[$content_type_id] : 'invalid');
276 }
277
278
279
280 public static function APEtagItemIsUTF8Lookup($item_key) {
281
282 static $lookup = array (
283 'title',
284 'subtitle',
285 'artist',
286 'album',
287 'debut album',
288 'publisher',
289 'conductor',
290 'track',
291 'composer',
292 'comment',
293 'copyright',
294 'publicationright',
295 'file',
296 'year',
297 'record date',
298 'record location',
299 'genre',
300 'media',
301 'related',
302 'isrc',
303 'abstract',
304 'language',
305 'bibliography'
306 );
307 return in_array(strtolower($item_key), $lookup);
308 }
309
310 }
311
312 ?>