Mercurial > judyates
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 ?> |