PowerPress Podcasting plugin by Blubrry - Version 0.2.0

Version Description

Download this release

Release Info

Developer amandato
Plugin Icon 128x128 PowerPress Podcasting plugin by Blubrry
Version 0.2.0
Comparing to
See all releases

Version 0.2.0

FlowPlayerClassic.swf ADDED
Binary file
getid3/getid3.lib.php ADDED
@@ -0,0 +1,1306 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ // //
8
+ // getid3.lib.php - part of getID3() //
9
+ // See readme.txt for more details //
10
+ // ///
11
+ /////////////////////////////////////////////////////////////////
12
+
13
+
14
+ class getid3_lib
15
+ {
16
+
17
+ function PrintHexBytes($string, $hex=true, $spaces=true, $htmlsafe=true) {
18
+ $returnstring = '';
19
+ for ($i = 0; $i < strlen($string); $i++) {
20
+ if ($hex) {
21
+ $returnstring .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT);
22
+ } else {
23
+ $returnstring .= ' '.(ereg("[\x20-\x7E]", $string{$i}) ? $string{$i} : '�');
24
+ }
25
+ if ($spaces) {
26
+ $returnstring .= ' ';
27
+ }
28
+ }
29
+ if ($htmlsafe) {
30
+ $returnstring = htmlentities($returnstring);
31
+ }
32
+ return $returnstring;
33
+ }
34
+
35
+ function SafeStripSlashes($text) {
36
+ if (get_magic_quotes_gpc()) {
37
+ return stripslashes($text);
38
+ }
39
+ return $text;
40
+ }
41
+
42
+
43
+ function trunc($floatnumber) {
44
+ // truncates a floating-point number at the decimal point
45
+ // returns int (if possible, otherwise float)
46
+ if ($floatnumber >= 1) {
47
+ $truncatednumber = floor($floatnumber);
48
+ } elseif ($floatnumber <= -1) {
49
+ $truncatednumber = ceil($floatnumber);
50
+ } else {
51
+ $truncatednumber = 0;
52
+ }
53
+ if ($truncatednumber <= 1073741824) { // 2^30
54
+ $truncatednumber = (int) $truncatednumber;
55
+ }
56
+ return $truncatednumber;
57
+ }
58
+
59
+
60
+ function CastAsInt($floatnum) {
61
+ // convert to float if not already
62
+ $floatnum = (float) $floatnum;
63
+
64
+ // convert a float to type int, only if possible
65
+ if (getid3_lib::trunc($floatnum) == $floatnum) {
66
+ // it's not floating point
67
+ if ($floatnum <= 1073741824) { // 2^30
68
+ // it's within int range
69
+ $floatnum = (int) $floatnum;
70
+ }
71
+ }
72
+ return $floatnum;
73
+ }
74
+
75
+
76
+ function DecimalBinary2Float($binarynumerator) {
77
+ $numerator = getid3_lib::Bin2Dec($binarynumerator);
78
+ $denominator = getid3_lib::Bin2Dec('1'.str_repeat('0', strlen($binarynumerator)));
79
+ return ($numerator / $denominator);
80
+ }
81
+
82
+
83
+ function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) {
84
+ // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
85
+ if (strpos($binarypointnumber, '.') === false) {
86
+ $binarypointnumber = '0.'.$binarypointnumber;
87
+ } elseif ($binarypointnumber{0} == '.') {
88
+ $binarypointnumber = '0'.$binarypointnumber;
89
+ }
90
+ $exponent = 0;
91
+ while (($binarypointnumber{0} != '1') || (substr($binarypointnumber, 1, 1) != '.')) {
92
+ if (substr($binarypointnumber, 1, 1) == '.') {
93
+ $exponent--;
94
+ $binarypointnumber = substr($binarypointnumber, 2, 1).'.'.substr($binarypointnumber, 3);
95
+ } else {
96
+ $pointpos = strpos($binarypointnumber, '.');
97
+ $exponent += ($pointpos - 1);
98
+ $binarypointnumber = str_replace('.', '', $binarypointnumber);
99
+ $binarypointnumber = $binarypointnumber{0}.'.'.substr($binarypointnumber, 1);
100
+ }
101
+ }
102
+ $binarypointnumber = str_pad(substr($binarypointnumber, 0, $maxbits + 2), $maxbits + 2, '0', STR_PAD_RIGHT);
103
+ return array('normalized'=>$binarypointnumber, 'exponent'=>(int) $exponent);
104
+ }
105
+
106
+
107
+ function Float2BinaryDecimal($floatvalue) {
108
+ // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
109
+ $maxbits = 128; // to how many bits of precision should the calculations be taken?
110
+ $intpart = getid3_lib::trunc($floatvalue);
111
+ $floatpart = abs($floatvalue - $intpart);
112
+ $pointbitstring = '';
113
+ while (($floatpart != 0) && (strlen($pointbitstring) < $maxbits)) {
114
+ $floatpart *= 2;
115
+ $pointbitstring .= (string) getid3_lib::trunc($floatpart);
116
+ $floatpart -= getid3_lib::trunc($floatpart);
117
+ }
118
+ $binarypointnumber = decbin($intpart).'.'.$pointbitstring;
119
+ return $binarypointnumber;
120
+ }
121
+
122
+
123
+ function Float2String($floatvalue, $bits) {
124
+ // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
125
+ switch ($bits) {
126
+ case 32:
127
+ $exponentbits = 8;
128
+ $fractionbits = 23;
129
+ break;
130
+
131
+ case 64:
132
+ $exponentbits = 11;
133
+ $fractionbits = 52;
134
+ break;
135
+
136
+ default:
137
+ return false;
138
+ break;
139
+ }
140
+ if ($floatvalue >= 0) {
141
+ $signbit = '0';
142
+ } else {
143
+ $signbit = '1';
144
+ }
145
+ $normalizedbinary = getid3_lib::NormalizeBinaryPoint(getid3_lib::Float2BinaryDecimal($floatvalue), $fractionbits);
146
+ $biasedexponent = pow(2, $exponentbits - 1) - 1 + $normalizedbinary['exponent']; // (127 or 1023) +/- exponent
147
+ $exponentbitstring = str_pad(decbin($biasedexponent), $exponentbits, '0', STR_PAD_LEFT);
148
+ $fractionbitstring = str_pad(substr($normalizedbinary['normalized'], 2), $fractionbits, '0', STR_PAD_RIGHT);
149
+
150
+ return getid3_lib::BigEndian2String(getid3_lib::Bin2Dec($signbit.$exponentbitstring.$fractionbitstring), $bits % 8, false);
151
+ }
152
+
153
+
154
+ function LittleEndian2Float($byteword) {
155
+ return getid3_lib::BigEndian2Float(strrev($byteword));
156
+ }
157
+
158
+
159
+ function BigEndian2Float($byteword) {
160
+ // ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic
161
+ // http://www.psc.edu/general/software/packages/ieee/ieee.html
162
+ // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html
163
+
164
+ $bitword = getid3_lib::BigEndian2Bin($byteword);
165
+ if (!$bitword) {
166
+ return 0;
167
+ }
168
+ $signbit = $bitword{0};
169
+
170
+ switch (strlen($byteword) * 8) {
171
+ case 32:
172
+ $exponentbits = 8;
173
+ $fractionbits = 23;
174
+ break;
175
+
176
+ case 64:
177
+ $exponentbits = 11;
178
+ $fractionbits = 52;
179
+ break;
180
+
181
+ case 80:
182
+ // 80-bit Apple SANE format
183
+ // http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
184
+ $exponentstring = substr($bitword, 1, 15);
185
+ $isnormalized = intval($bitword{16});
186
+ $fractionstring = substr($bitword, 17, 63);
187
+ $exponent = pow(2, getid3_lib::Bin2Dec($exponentstring) - 16383);
188
+ $fraction = $isnormalized + getid3_lib::DecimalBinary2Float($fractionstring);
189
+ $floatvalue = $exponent * $fraction;
190
+ if ($signbit == '1') {
191
+ $floatvalue *= -1;
192
+ }
193
+ return $floatvalue;
194
+ break;
195
+
196
+ default:
197
+ return false;
198
+ break;
199
+ }
200
+ $exponentstring = substr($bitword, 1, $exponentbits);
201
+ $fractionstring = substr($bitword, $exponentbits + 1, $fractionbits);
202
+ $exponent = getid3_lib::Bin2Dec($exponentstring);
203
+ $fraction = getid3_lib::Bin2Dec($fractionstring);
204
+
205
+ if (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction != 0)) {
206
+ // Not a Number
207
+ $floatvalue = false;
208
+ } elseif (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction == 0)) {
209
+ if ($signbit == '1') {
210
+ $floatvalue = '-infinity';
211
+ } else {
212
+ $floatvalue = '+infinity';
213
+ }
214
+ } elseif (($exponent == 0) && ($fraction == 0)) {
215
+ if ($signbit == '1') {
216
+ $floatvalue = -0;
217
+ } else {
218
+ $floatvalue = 0;
219
+ }
220
+ $floatvalue = ($signbit ? 0 : -0);
221
+ } elseif (($exponent == 0) && ($fraction != 0)) {
222
+ // These are 'unnormalized' values
223
+ $floatvalue = pow(2, (-1 * (pow(2, $exponentbits - 1) - 2))) * getid3_lib::DecimalBinary2Float($fractionstring);
224
+ if ($signbit == '1') {
225
+ $floatvalue *= -1;
226
+ }
227
+ } elseif ($exponent != 0) {
228
+ $floatvalue = pow(2, ($exponent - (pow(2, $exponentbits - 1) - 1))) * (1 + getid3_lib::DecimalBinary2Float($fractionstring));
229
+ if ($signbit == '1') {
230
+ $floatvalue *= -1;
231
+ }
232
+ }
233
+ return (float) $floatvalue;
234
+ }
235
+
236
+
237
+ function BigEndian2Int($byteword, $synchsafe=false, $signed=false) {
238
+ $intvalue = 0;
239
+ $bytewordlen = strlen($byteword);
240
+ for ($i = 0; $i < $bytewordlen; $i++) {
241
+ if ($synchsafe) { // disregard MSB, effectively 7-bit bytes
242
+ $intvalue = $intvalue | (ord($byteword{$i}) & 0x7F) << (($bytewordlen - 1 - $i) * 7);
243
+ } else {
244
+ $intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i));
245
+ }
246
+ }
247
+ if ($signed && !$synchsafe) {
248
+ // synchsafe ints are not allowed to be signed
249
+ switch ($bytewordlen) {
250
+ case 1:
251
+ case 2:
252
+ case 3:
253
+ case 4:
254
+ $signmaskbit = 0x80 << (8 * ($bytewordlen - 1));
255
+ if ($intvalue & $signmaskbit) {
256
+ $intvalue = 0 - ($intvalue & ($signmaskbit - 1));
257
+ }
258
+ break;
259
+
260
+ default:
261
+ die('ERROR: Cannot have signed integers larger than 32-bits in getid3_lib::BigEndian2Int()');
262
+ break;
263
+ }
264
+ }
265
+ return getid3_lib::CastAsInt($intvalue);
266
+ }
267
+
268
+
269
+ function LittleEndian2Int($byteword, $signed=false) {
270
+ return getid3_lib::BigEndian2Int(strrev($byteword), false, $signed);
271
+ }
272
+
273
+
274
+ function BigEndian2Bin($byteword) {
275
+ $binvalue = '';
276
+ $bytewordlen = strlen($byteword);
277
+ for ($i = 0; $i < $bytewordlen; $i++) {
278
+ $binvalue .= str_pad(decbin(ord($byteword{$i})), 8, '0', STR_PAD_LEFT);
279
+ }
280
+ return $binvalue;
281
+ }
282
+
283
+
284
+ function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) {
285
+ if ($number < 0) {
286
+ return false;
287
+ }
288
+ $maskbyte = (($synchsafe || $signed) ? 0x7F : 0xFF);
289
+ $intstring = '';
290
+ if ($signed) {
291
+ if ($minbytes > 4) {
292
+ die('ERROR: Cannot have signed integers larger than 32-bits in getid3_lib::BigEndian2String()');
293
+ }
294
+ $number = $number & (0x80 << (8 * ($minbytes - 1)));
295
+ }
296
+ while ($number != 0) {
297
+ $quotient = ($number / ($maskbyte + 1));
298
+ $intstring = chr(ceil(($quotient - floor($quotient)) * $maskbyte)).$intstring;
299
+ $number = floor($quotient);
300
+ }
301
+ return str_pad($intstring, $minbytes, "\x00", STR_PAD_LEFT);
302
+ }
303
+
304
+
305
+ function Dec2Bin($number) {
306
+ while ($number >= 256) {
307
+ $bytes[] = (($number / 256) - (floor($number / 256))) * 256;
308
+ $number = floor($number / 256);
309
+ }
310
+ $bytes[] = $number;
311
+ $binstring = '';
312
+ for ($i = 0; $i < count($bytes); $i++) {
313
+ $binstring = (($i == count($bytes) - 1) ? decbin($bytes[$i]) : str_pad(decbin($bytes[$i]), 8, '0', STR_PAD_LEFT)).$binstring;
314
+ }
315
+ return $binstring;
316
+ }
317
+
318
+
319
+ function Bin2Dec($binstring, $signed=false) {
320
+ $signmult = 1;
321
+ if ($signed) {
322
+ if ($binstring{0} == '1') {
323
+ $signmult = -1;
324
+ }
325
+ $binstring = substr($binstring, 1);
326
+ }
327
+ $decvalue = 0;
328
+ for ($i = 0; $i < strlen($binstring); $i++) {
329
+ $decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i);
330
+ }
331
+ return getid3_lib::CastAsInt($decvalue * $signmult);
332
+ }
333
+
334
+
335
+ function Bin2String($binstring) {
336
+ // return 'hi' for input of '0110100001101001'
337
+ $string = '';
338
+ $binstringreversed = strrev($binstring);
339
+ for ($i = 0; $i < strlen($binstringreversed); $i += 8) {
340
+ $string = chr(getid3_lib::Bin2Dec(strrev(substr($binstringreversed, $i, 8)))).$string;
341
+ }
342
+ return $string;
343
+ }
344
+
345
+
346
+ function LittleEndian2String($number, $minbytes=1, $synchsafe=false) {
347
+ $intstring = '';
348
+ while ($number > 0) {
349
+ if ($synchsafe) {
350
+ $intstring = $intstring.chr($number & 127);
351
+ $number >>= 7;
352
+ } else {
353
+ $intstring = $intstring.chr($number & 255);
354
+ $number >>= 8;
355
+ }
356
+ }
357
+ return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);
358
+ }
359
+
360
+
361
+ function array_merge_clobber($array1, $array2) {
362
+ // written by kc�hireability*com
363
+ // taken from http://www.php.net/manual/en/function.array-merge-recursive.php
364
+ if (!is_array($array1) || !is_array($array2)) {
365
+ return false;
366
+ }
367
+ $newarray = $array1;
368
+ foreach ($array2 as $key => $val) {
369
+ if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
370
+ $newarray[$key] = getid3_lib::array_merge_clobber($newarray[$key], $val);
371
+ } else {
372
+ $newarray[$key] = $val;
373
+ }
374
+ }
375
+ return $newarray;
376
+ }
377
+
378
+
379
+ function array_merge_noclobber($array1, $array2) {
380
+ if (!is_array($array1) || !is_array($array2)) {
381
+ return false;
382
+ }
383
+ $newarray = $array1;
384
+ foreach ($array2 as $key => $val) {
385
+ if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
386
+ $newarray[$key] = getid3_lib::array_merge_noclobber($newarray[$key], $val);
387
+ } elseif (!isset($newarray[$key])) {
388
+ $newarray[$key] = $val;
389
+ }
390
+ }
391
+ return $newarray;
392
+ }
393
+
394
+
395
+ function fileextension($filename, $numextensions=1) {
396
+ if (strstr($filename, '.')) {
397
+ $reversedfilename = strrev($filename);
398
+ $offset = 0;
399
+ for ($i = 0; $i < $numextensions; $i++) {
400
+ $offset = strpos($reversedfilename, '.', $offset + 1);
401
+ if ($offset === false) {
402
+ return '';
403
+ }
404
+ }
405
+ return strrev(substr($reversedfilename, 0, $offset));
406
+ }
407
+ return '';
408
+ }
409
+
410
+
411
+ function PlaytimeString($playtimeseconds) {
412
+ $sign = (($playtimeseconds < 0) ? '-' : '');
413
+ $playtimeseconds = abs($playtimeseconds);
414
+ $contentseconds = round((($playtimeseconds / 60) - floor($playtimeseconds / 60)) * 60);
415
+ $contentminutes = floor($playtimeseconds / 60);
416
+ if ($contentseconds >= 60) {
417
+ $contentseconds -= 60;
418
+ $contentminutes++;
419
+ }
420
+ return $sign.intval($contentminutes).':'.str_pad($contentseconds, 2, 0, STR_PAD_LEFT);
421
+ }
422
+
423
+
424
+ function image_type_to_mime_type($imagetypeid) {
425
+ // only available in PHP v4.3.0+
426
+ static $image_type_to_mime_type = array();
427
+ if (empty($image_type_to_mime_type)) {
428
+ $image_type_to_mime_type[1] = 'image/gif'; // GIF
429
+ $image_type_to_mime_type[2] = 'image/jpeg'; // JPEG
430
+ $image_type_to_mime_type[3] = 'image/png'; // PNG
431
+ $image_type_to_mime_type[4] = 'application/x-shockwave-flash'; // Flash
432
+ $image_type_to_mime_type[5] = 'image/psd'; // PSD
433
+ $image_type_to_mime_type[6] = 'image/bmp'; // BMP
434
+ $image_type_to_mime_type[7] = 'image/tiff'; // TIFF: little-endian (Intel)
435
+ $image_type_to_mime_type[8] = 'image/tiff'; // TIFF: big-endian (Motorola)
436
+ //$image_type_to_mime_type[9] = 'image/jpc'; // JPC
437
+ //$image_type_to_mime_type[10] = 'image/jp2'; // JPC
438
+ //$image_type_to_mime_type[11] = 'image/jpx'; // JPC
439
+ //$image_type_to_mime_type[12] = 'image/jb2'; // JPC
440
+ $image_type_to_mime_type[13] = 'application/x-shockwave-flash'; // Shockwave
441
+ $image_type_to_mime_type[14] = 'image/iff'; // IFF
442
+ }
443
+ return (isset($image_type_to_mime_type[$imagetypeid]) ? $image_type_to_mime_type[$imagetypeid] : 'application/octet-stream');
444
+ }
445
+
446
+
447
+ function DateMac2Unix($macdate) {
448
+ // Macintosh timestamp: seconds since 00:00h January 1, 1904
449
+ // UNIX timestamp: seconds since 00:00h January 1, 1970
450
+ return getid3_lib::CastAsInt($macdate - 2082844800);
451
+ }
452
+
453
+
454
+ function FixedPoint8_8($rawdata) {
455
+ return getid3_lib::BigEndian2Int(substr($rawdata, 0, 1)) + (float) (getid3_lib::BigEndian2Int(substr($rawdata, 1, 1)) / pow(2, 8));
456
+ }
457
+
458
+
459
+ function FixedPoint16_16($rawdata) {
460
+ return getid3_lib::BigEndian2Int(substr($rawdata, 0, 2)) + (float) (getid3_lib::BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16));
461
+ }
462
+
463
+
464
+ function FixedPoint2_30($rawdata) {
465
+ $binarystring = getid3_lib::BigEndian2Bin($rawdata);
466
+ return getid3_lib::Bin2Dec(substr($binarystring, 0, 2)) + (float) (getid3_lib::Bin2Dec(substr($binarystring, 2, 30)) / 1073741824);
467
+ }
468
+
469
+
470
+ function CreateDeepArray($ArrayPath, $Separator, $Value) {
471
+ // assigns $Value to a nested array path:
472
+ // $foo = getid3_lib::CreateDeepArray('/path/to/my', '/', 'file.txt')
473
+ // is the same as:
474
+ // $foo = array('path'=>array('to'=>'array('my'=>array('file.txt'))));
475
+ // or
476
+ // $foo['path']['to']['my'] = 'file.txt';
477
+ while ($ArrayPath && ($ArrayPath{0} == $Separator)) {
478
+ $ArrayPath = substr($ArrayPath, 1);
479
+ }
480
+ if (($pos = strpos($ArrayPath, $Separator)) !== false) {
481
+ $ReturnedArray[substr($ArrayPath, 0, $pos)] = getid3_lib::CreateDeepArray(substr($ArrayPath, $pos + 1), $Separator, $Value);
482
+ } else {
483
+ $ReturnedArray[$ArrayPath] = $Value;
484
+ }
485
+ return $ReturnedArray;
486
+ }
487
+
488
+ function array_max($arraydata, $returnkey=false) {
489
+ $maxvalue = false;
490
+ $maxkey = false;
491
+ foreach ($arraydata as $key => $value) {
492
+ if (!is_array($value)) {
493
+ if ($value > $maxvalue) {
494
+ $maxvalue = $value;
495
+ $maxkey = $key;
496
+ }
497
+ }
498
+ }
499
+ return ($returnkey ? $maxkey : $maxvalue);
500
+ }
501
+
502
+ function array_min($arraydata, $returnkey=false) {
503
+ $minvalue = false;
504
+ $minkey = false;
505
+ foreach ($arraydata as $key => $value) {
506
+ if (!is_array($value)) {
507
+ if ($value > $minvalue) {
508
+ $minvalue = $value;
509
+ $minkey = $key;
510
+ }
511
+ }
512
+ }
513
+ return ($returnkey ? $minkey : $minvalue);
514
+ }
515
+
516
+
517
+ function md5_file($file) {
518
+
519
+ // md5_file() exists in PHP 4.2.0+.
520
+ if (function_exists('md5_file')) {
521
+ return md5_file($file);
522
+ }
523
+
524
+ if (GETID3_OS_ISWINDOWS) {
525
+
526
+ $RequiredFiles = array('cygwin1.dll', 'md5sum.exe');
527
+ foreach ($RequiredFiles as $required_file) {
528
+ if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
529
+ die(implode(' and ', $RequiredFiles).' are required in '.GETID3_HELPERAPPSDIR.' for getid3_lib::md5_file() to function under Windows in PHP < v4.2.0');
530
+ }
531
+ }
532
+ $commandline = GETID3_HELPERAPPSDIR.'md5sum.exe "'.str_replace('/', DIRECTORY_SEPARATOR, $file).'"';
533
+ if (ereg("^[\\]?([0-9a-f]{32})", strtolower(`$commandline`), $r)) {
534
+ return $r[1];
535
+ }
536
+
537
+ } else {
538
+
539
+ // The following works under UNIX only
540
+ $file = str_replace('`', '\\`', $file);
541
+ if (ereg("^([0-9a-f]{32})[ \t\n\r]", `md5sum "$file"`, $r)) {
542
+ return $r[1];
543
+ }
544
+
545
+ }
546
+ return false;
547
+ }
548
+
549
+
550
+ function sha1_file($file) {
551
+
552
+ // sha1_file() exists in PHP 4.3.0+.
553
+ if (function_exists('sha1_file')) {
554
+ return sha1_file($file);
555
+ }
556
+
557
+ $file = str_replace('`', '\\`', $file);
558
+
559
+ if (GETID3_OS_ISWINDOWS) {
560
+
561
+ $RequiredFiles = array('cygwin1.dll', 'sha1sum.exe');
562
+ foreach ($RequiredFiles as $required_file) {
563
+ if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
564
+ die(implode(' and ', $RequiredFiles).' are required in '.GETID3_HELPERAPPSDIR.' for getid3_lib::sha1_file() to function under Windows in PHP < v4.3.0');
565
+ }
566
+ }
567
+ $commandline = GETID3_HELPERAPPSDIR.'sha1sum.exe "'.str_replace('/', DIRECTORY_SEPARATOR, $file).'"';
568
+ if (ereg("^sha1=([0-9a-f]{40})", strtolower(`$commandline`), $r)) {
569
+ return $r[1];
570
+ }
571
+
572
+ } else {
573
+
574
+ $commandline = 'sha1sum '.escapeshellarg($file).'';
575
+ if (ereg("^([0-9a-f]{40})[ \t\n\r]", strtolower(`$commandline`), $r)) {
576
+ return $r[1];
577
+ }
578
+
579
+ }
580
+
581
+ return false;
582
+ }
583
+
584
+
585
+ // Allan Hansen <ah�artemis*dk>
586
+ // getid3_lib::md5_data() - returns md5sum for a file from startuing position to absolute end position
587
+ function hash_data($file, $offset, $end, $algorithm) {
588
+
589
+ switch ($algorithm) {
590
+ case 'md5':
591
+ $hash_function = 'md5_file';
592
+ $unix_call = 'md5sum';
593
+ $windows_call = 'md5sum.exe';
594
+ $hash_length = 32;
595
+ break;
596
+
597
+ case 'sha1':
598
+ $hash_function = 'sha1_file';
599
+ $unix_call = 'sha1sum';
600
+ $windows_call = 'sha1sum.exe';
601
+ $hash_length = 40;
602
+ break;
603
+
604
+ default:
605
+ die('Invalid algorithm ('.$algorithm.') in getid3_lib::hash_data()');
606
+ break;
607
+ }
608
+ $size = $end - $offset;
609
+ while (true) {
610
+ if (GETID3_OS_ISWINDOWS) {
611
+
612
+ // It seems that sha1sum.exe for Windows only works on physical files, does not accept piped data
613
+ // Fall back to create-temp-file method:
614
+ if ($algorithm == 'sha1') {
615
+ break;
616
+ }
617
+
618
+ $RequiredFiles = array('cygwin1.dll', 'head.exe', 'tail.exe', $windows_call);
619
+ foreach ($RequiredFiles as $required_file) {
620
+ if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
621
+ // helper apps not available - fall back to old method
622
+ break;
623
+ }
624
+ }
625
+ $commandline = GETID3_HELPERAPPSDIR.'head.exe -c '.$end.' "'.escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $file)).'" | ';
626
+ $commandline .= GETID3_HELPERAPPSDIR.'tail.exe -c '.$size.' | ';
627
+ $commandline .= GETID3_HELPERAPPSDIR.$windows_call;
628
+
629
+ } else {
630
+
631
+ $commandline = 'head -c'.$end.' '.escapeshellarg($file).' | ';
632
+ $commandline .= 'tail -c'.$size.' | ';
633
+ $commandline .= $unix_call;
634
+
635
+ }
636
+ if ((bool) ini_get('safe_mode')) {
637
+ $ThisFileInfo['warning'][] = 'PHP running in Safe Mode - backtick operator not available, using slower non-system-call '.$algorithm.' algorithm';
638
+ break;
639
+ }
640
+ return substr(`$commandline`, 0, $hash_length);
641
+ }
642
+
643
+ // try to create a temporary file in the system temp directory - invalid dirname should force to system temp dir
644
+ if (($data_filename = tempnam('*', 'getID3')) === false) {
645
+ // can't find anywhere to create a temp file, just die
646
+ return false;
647
+ }
648
+
649
+ // Init
650
+ $result = false;
651
+
652
+ // copy parts of file
653
+ if ($fp = @fopen($file, 'rb')) {
654
+
655
+ if ($fp_data = @fopen($data_filename, 'wb')) {
656
+
657
+ fseek($fp, $offset, SEEK_SET);
658
+ $byteslefttowrite = $end - $offset;
659
+ while (($byteslefttowrite > 0) && ($buffer = fread($fp, GETID3_FREAD_BUFFER_SIZE))) {
660
+ $byteswritten = fwrite($fp_data, $buffer, $byteslefttowrite);
661
+ $byteslefttowrite -= $byteswritten;
662
+ }
663
+ fclose($fp_data);
664
+ $result = getid3_lib::$hash_function($data_filename);
665
+
666
+ }
667
+ fclose($fp);
668
+ }
669
+ unlink($data_filename);
670
+ return $result;
671
+ }
672
+
673
+
674
+ function iconv_fallback_int_utf8($charval) {
675
+ if ($charval < 128) {
676
+ // 0bbbbbbb
677
+ $newcharstring = chr($charval);
678
+ } elseif ($charval < 2048) {
679
+ // 110bbbbb 10bbbbbb
680
+ $newcharstring = chr(($charval >> 6) | 0xC0);
681
+ $newcharstring .= chr(($charval & 0x3F) | 0x80);
682
+ } elseif ($charval < 65536) {
683
+ // 1110bbbb 10bbbbbb 10bbbbbb
684
+ $newcharstring = chr(($charval >> 12) | 0xE0);
685
+ $newcharstring .= chr(($charval >> 6) | 0xC0);
686
+ $newcharstring .= chr(($charval & 0x3F) | 0x80);
687
+ } else {
688
+ // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
689
+ $newcharstring = chr(($charval >> 18) | 0xF0);
690
+ $newcharstring .= chr(($charval >> 12) | 0xC0);
691
+ $newcharstring .= chr(($charval >> 6) | 0xC0);
692
+ $newcharstring .= chr(($charval & 0x3F) | 0x80);
693
+ }
694
+ return $newcharstring;
695
+ }
696
+
697
+ // ISO-8859-1 => UTF-8
698
+ function iconv_fallback_iso88591_utf8($string, $bom=false) {
699
+ if (function_exists('utf8_encode')) {
700
+ return utf8_encode($string);
701
+ }
702
+ // utf8_encode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support)
703
+ $newcharstring = '';
704
+ if ($bom) {
705
+ $newcharstring .= "\xEF\xBB\xBF";
706
+ }
707
+ for ($i = 0; $i < strlen($string); $i++) {
708
+ $charval = ord($string{$i});
709
+ $newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval);
710
+ }
711
+ return $newcharstring;
712
+ }
713
+
714
+ // ISO-8859-1 => UTF-16BE
715
+ function iconv_fallback_iso88591_utf16be($string, $bom=false) {
716
+ $newcharstring = '';
717
+ if ($bom) {
718
+ $newcharstring .= "\xFE\xFF";
719
+ }
720
+ for ($i = 0; $i < strlen($string); $i++) {
721
+ $newcharstring .= "\x00".$string{$i};
722
+ }
723
+ return $newcharstring;
724
+ }
725
+
726
+ // ISO-8859-1 => UTF-16LE
727
+ function iconv_fallback_iso88591_utf16le($string, $bom=false) {
728
+ $newcharstring = '';
729
+ if ($bom) {
730
+ $newcharstring .= "\xFF\xFE";
731
+ }
732
+ for ($i = 0; $i < strlen($string); $i++) {
733
+ $newcharstring .= $string{$i}."\x00";
734
+ }
735
+ return $newcharstring;
736
+ }
737
+
738
+ // ISO-8859-1 => UTF-16LE (BOM)
739
+ function iconv_fallback_iso88591_utf16($string) {
740
+ return getid3_lib::iconv_fallback_iso88591_utf16le($string, true);
741
+ }
742
+
743
+ // UTF-8 => ISO-8859-1
744
+ function iconv_fallback_utf8_iso88591($string) {
745
+ if (function_exists('utf8_decode')) {
746
+ return utf8_decode($string);
747
+ }
748
+ // utf8_decode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support)
749
+ $newcharstring = '';
750
+ $offset = 0;
751
+ $stringlength = strlen($string);
752
+ while ($offset < $stringlength) {
753
+ if ((ord($string{$offset}) | 0x07) == 0xF7) {
754
+ // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
755
+ $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
756
+ ((ord($string{($offset + 1)}) & 0x3F) << 12) &
757
+ ((ord($string{($offset + 2)}) & 0x3F) << 6) &
758
+ (ord($string{($offset + 3)}) & 0x3F);
759
+ $offset += 4;
760
+ } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
761
+ // 1110bbbb 10bbbbbb 10bbbbbb
762
+ $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
763
+ ((ord($string{($offset + 1)}) & 0x3F) << 6) &
764
+ (ord($string{($offset + 2)}) & 0x3F);
765
+ $offset += 3;
766
+ } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
767
+ // 110bbbbb 10bbbbbb
768
+ $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
769
+ (ord($string{($offset + 1)}) & 0x3F);
770
+ $offset += 2;
771
+ } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
772
+ // 0bbbbbbb
773
+ $charval = ord($string{$offset});
774
+ $offset += 1;
775
+ } else {
776
+ // error? throw some kind of warning here?
777
+ $charval = false;
778
+ $offset += 1;
779
+ }
780
+ if ($charval !== false) {
781
+ $newcharstring .= (($charval < 256) ? chr($charval) : '?');
782
+ }
783
+ }
784
+ return $newcharstring;
785
+ }
786
+
787
+ // UTF-8 => UTF-16BE
788
+ function iconv_fallback_utf8_utf16be($string, $bom=false) {
789
+ $newcharstring = '';
790
+ if ($bom) {
791
+ $newcharstring .= "\xFE\xFF";
792
+ }
793
+ $offset = 0;
794
+ $stringlength = strlen($string);
795
+ while ($offset < $stringlength) {
796
+ if ((ord($string{$offset}) | 0x07) == 0xF7) {
797
+ // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
798
+ $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
799
+ ((ord($string{($offset + 1)}) & 0x3F) << 12) &
800
+ ((ord($string{($offset + 2)}) & 0x3F) << 6) &
801
+ (ord($string{($offset + 3)}) & 0x3F);
802
+ $offset += 4;
803
+ } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
804
+ // 1110bbbb 10bbbbbb 10bbbbbb
805
+ $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
806
+ ((ord($string{($offset + 1)}) & 0x3F) << 6) &
807
+ (ord($string{($offset + 2)}) & 0x3F);
808
+ $offset += 3;
809
+ } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
810
+ // 110bbbbb 10bbbbbb
811
+ $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
812
+ (ord($string{($offset + 1)}) & 0x3F);
813
+ $offset += 2;
814
+ } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
815
+ // 0bbbbbbb
816
+ $charval = ord($string{$offset});
817
+ $offset += 1;
818
+ } else {
819
+ // error? throw some kind of warning here?
820
+ $charval = false;
821
+ $offset += 1;
822
+ }
823
+ if ($charval !== false) {
824
+ $newcharstring .= (($charval < 65536) ? getid3_lib::BigEndian2String($charval, 2) : "\x00".'?');
825
+ }
826
+ }
827
+ return $newcharstring;
828
+ }
829
+
830
+ // UTF-8 => UTF-16LE
831
+ function iconv_fallback_utf8_utf16le($string, $bom=false) {
832
+ $newcharstring = '';
833
+ if ($bom) {
834
+ $newcharstring .= "\xFF\xFE";
835
+ }
836
+ $offset = 0;
837
+ $stringlength = strlen($string);
838
+ while ($offset < $stringlength) {
839
+ if ((ord($string{$offset}) | 0x07) == 0xF7) {
840
+ // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
841
+ $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
842
+ ((ord($string{($offset + 1)}) & 0x3F) << 12) &
843
+ ((ord($string{($offset + 2)}) & 0x3F) << 6) &
844
+ (ord($string{($offset + 3)}) & 0x3F);
845
+ $offset += 4;
846
+ } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
847
+ // 1110bbbb 10bbbbbb 10bbbbbb
848
+ $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
849
+ ((ord($string{($offset + 1)}) & 0x3F) << 6) &
850
+ (ord($string{($offset + 2)}) & 0x3F);
851
+ $offset += 3;
852
+ } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
853
+ // 110bbbbb 10bbbbbb
854
+ $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
855
+ (ord($string{($offset + 1)}) & 0x3F);
856
+ $offset += 2;
857
+ } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
858
+ // 0bbbbbbb
859
+ $charval = ord($string{$offset});
860
+ $offset += 1;
861
+ } else {
862
+ // error? maybe throw some warning here?
863
+ $charval = false;
864
+ $offset += 1;
865
+ }
866
+ if ($charval !== false) {
867
+ $newcharstring .= (($charval < 65536) ? getid3_lib::LittleEndian2String($charval, 2) : '?'."\x00");
868
+ }
869
+ }
870
+ return $newcharstring;
871
+ }
872
+
873
+ // UTF-8 => UTF-16LE (BOM)
874
+ function iconv_fallback_utf8_utf16($string) {
875
+ return getid3_lib::iconv_fallback_utf8_utf16le($string, true);
876
+ }
877
+
878
+ // UTF-16BE => UTF-8
879
+ function iconv_fallback_utf16be_utf8($string) {
880
+ if (substr($string, 0, 2) == "\xFE\xFF") {
881
+ // strip BOM
882
+ $string = substr($string, 2);
883
+ }
884
+ $newcharstring = '';
885
+ for ($i = 0; $i < strlen($string); $i += 2) {
886
+ $charval = getid3_lib::BigEndian2Int(substr($string, $i, 2));
887
+ $newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval);
888
+ }
889
+ return $newcharstring;
890
+ }
891
+
892
+ // UTF-16LE => UTF-8
893
+ function iconv_fallback_utf16le_utf8($string) {
894
+ if (substr($string, 0, 2) == "\xFF\xFE") {
895
+ // strip BOM
896
+ $string = substr($string, 2);
897
+ }
898
+ $newcharstring = '';
899
+ for ($i = 0; $i < strlen($string); $i += 2) {
900
+ $charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2));
901
+ $newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval);
902
+ }
903
+ return $newcharstring;
904
+ }
905
+
906
+ // UTF-16BE => ISO-8859-1
907
+ function iconv_fallback_utf16be_iso88591($string) {
908
+ if (substr($string, 0, 2) == "\xFE\xFF") {
909
+ // strip BOM
910
+ $string = substr($string, 2);
911
+ }
912
+ $newcharstring = '';
913
+ for ($i = 0; $i < strlen($string); $i += 2) {
914
+ $charval = getid3_lib::BigEndian2Int(substr($string, $i, 2));
915
+ $newcharstring .= (($charval < 256) ? chr($charval) : '?');
916
+ }
917
+ return $newcharstring;
918
+ }
919
+
920
+ // UTF-16LE => ISO-8859-1
921
+ function iconv_fallback_utf16le_iso88591($string) {
922
+ if (substr($string, 0, 2) == "\xFF\xFE") {
923
+ // strip BOM
924
+ $string = substr($string, 2);
925
+ }
926
+ $newcharstring = '';
927
+ for ($i = 0; $i < strlen($string); $i += 2) {
928
+ $charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2));
929
+ $newcharstring .= (($charval < 256) ? chr($charval) : '?');
930
+ }
931
+ return $newcharstring;
932
+ }
933
+
934
+ // UTF-16 (BOM) => ISO-8859-1
935
+ function iconv_fallback_utf16_iso88591($string) {
936
+ $bom = substr($string, 0, 2);
937
+ if ($bom == "\xFE\xFF") {
938
+ return getid3_lib::iconv_fallback_utf16be_iso88591(substr($string, 2));
939
+ } elseif ($bom == "\xFF\xFE") {
940
+ return getid3_lib::iconv_fallback_utf16le_iso88591(substr($string, 2));
941
+ }
942
+ return $string;
943
+ }
944
+
945
+ // UTF-16 (BOM) => UTF-8
946
+ function iconv_fallback_utf16_utf8($string) {
947
+ $bom = substr($string, 0, 2);
948
+ if ($bom == "\xFE\xFF") {
949
+ return getid3_lib::iconv_fallback_utf16be_utf8(substr($string, 2));
950
+ } elseif ($bom == "\xFF\xFE") {
951
+ return getid3_lib::iconv_fallback_utf16le_utf8(substr($string, 2));
952
+ }
953
+ return $string;
954
+ }
955
+
956
+ function iconv_fallback($in_charset, $out_charset, $string) {
957
+
958
+ if ($in_charset == $out_charset) {
959
+ return $string;
960
+ }
961
+
962
+ // iconv() availble
963
+ if (function_exists('iconv')) {
964
+
965
+ if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) {
966
+ switch ($out_charset) {
967
+ case 'ISO-8859-1':
968
+ $converted_string = rtrim($converted_string, "\x00");
969
+ break;
970
+ }
971
+ return $converted_string;
972
+ }
973
+
974
+ // iconv() may sometimes fail with "illegal character in input string" error message
975
+ // and return an empty string, but returning the unconverted string is more useful
976
+ return $string;
977
+ }
978
+
979
+
980
+ // iconv() not available
981
+ static $ConversionFunctionList = array();
982
+ if (empty($ConversionFunctionList)) {
983
+ $ConversionFunctionList['ISO-8859-1']['UTF-8'] = 'iconv_fallback_iso88591_utf8';
984
+ $ConversionFunctionList['ISO-8859-1']['UTF-16'] = 'iconv_fallback_iso88591_utf16';
985
+ $ConversionFunctionList['ISO-8859-1']['UTF-16BE'] = 'iconv_fallback_iso88591_utf16be';
986
+ $ConversionFunctionList['ISO-8859-1']['UTF-16LE'] = 'iconv_fallback_iso88591_utf16le';
987
+ $ConversionFunctionList['UTF-8']['ISO-8859-1'] = 'iconv_fallback_utf8_iso88591';
988
+ $ConversionFunctionList['UTF-8']['UTF-16'] = 'iconv_fallback_utf8_utf16';
989
+ $ConversionFunctionList['UTF-8']['UTF-16BE'] = 'iconv_fallback_utf8_utf16be';
990
+ $ConversionFunctionList['UTF-8']['UTF-16LE'] = 'iconv_fallback_utf8_utf16le';
991
+ $ConversionFunctionList['UTF-16']['ISO-8859-1'] = 'iconv_fallback_utf16_iso88591';
992
+ $ConversionFunctionList['UTF-16']['UTF-8'] = 'iconv_fallback_utf16_utf8';
993
+ $ConversionFunctionList['UTF-16LE']['ISO-8859-1'] = 'iconv_fallback_utf16le_iso88591';
994
+ $ConversionFunctionList['UTF-16LE']['UTF-8'] = 'iconv_fallback_utf16le_utf8';
995
+ $ConversionFunctionList['UTF-16BE']['ISO-8859-1'] = 'iconv_fallback_utf16be_iso88591';
996
+ $ConversionFunctionList['UTF-16BE']['UTF-8'] = 'iconv_fallback_utf16be_utf8';
997
+ }
998
+ if (isset($ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)])) {
999
+ $ConversionFunction = $ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)];
1000
+ return getid3_lib::$ConversionFunction($string);
1001
+ }
1002
+ die('PHP does not have iconv() support - cannot convert from '.$in_charset.' to '.$out_charset);
1003
+ }
1004
+
1005
+
1006
+ function MultiByteCharString2HTML($string, $charset='ISO-8859-1') {
1007
+ $HTMLstring = '';
1008
+
1009
+ switch ($charset) {
1010
+ case 'ISO-8859-1':
1011
+ case 'ISO8859-1':
1012
+ case 'ISO-8859-15':
1013
+ case 'ISO8859-15':
1014
+ case 'cp866':
1015
+ case 'ibm866':
1016
+ case '866':
1017
+ case 'cp1251':
1018
+ case 'Windows-1251':
1019
+ case 'win-1251':
1020
+ case '1251':
1021
+ case 'cp1252':
1022
+ case 'Windows-1252':
1023
+ case '1252':
1024
+ case 'KOI8-R':
1025
+ case 'koi8-ru':
1026
+ case 'koi8r':
1027
+ case 'BIG5':
1028
+ case '950':
1029
+ case 'GB2312':
1030
+ case '936':
1031
+ case 'BIG5-HKSCS':
1032
+ case 'Shift_JIS':
1033
+ case 'SJIS':
1034
+ case '932':
1035
+ case 'EUC-JP':
1036
+ case 'EUCJP':
1037
+ $HTMLstring = htmlentities($string, ENT_COMPAT, $charset);
1038
+ break;
1039
+
1040
+ case 'UTF-8':
1041
+ $strlen = strlen($string);
1042
+ for ($i = 0; $i < $strlen; $i++) {
1043
+ $char_ord_val = ord($string{$i});
1044
+ $charval = 0;
1045
+ if ($char_ord_val < 0x80) {
1046
+ $charval = $char_ord_val;
1047
+ } elseif ((($char_ord_val & 0xF0) >> 4) == 0x0F && $i+3 < $strlen) {
1048
+ $charval = (($char_ord_val & 0x07) << 18);
1049
+ $charval += ((ord($string{++$i}) & 0x3F) << 12);
1050
+ $charval += ((ord($string{++$i}) & 0x3F) << 6);
1051
+ $charval += (ord($string{++$i}) & 0x3F);
1052
+ } elseif ((($char_ord_val & 0xE0) >> 5) == 0x07 && $i+2 < $strlen) {
1053
+ $charval = (($char_ord_val & 0x0F) << 12);
1054
+ $charval += ((ord($string{++$i}) & 0x3F) << 6);
1055
+ $charval += (ord($string{++$i}) & 0x3F);
1056
+ } elseif ((($char_ord_val & 0xC0) >> 6) == 0x03 && $i+1 < $strlen) {
1057
+ $charval = (($char_ord_val & 0x1F) << 6);
1058
+ $charval += (ord($string{++$i}) & 0x3F);
1059
+ }
1060
+ if (($charval >= 32) && ($charval <= 127)) {
1061
+ $HTMLstring .= chr($charval);
1062
+ } else {
1063
+ $HTMLstring .= '&#'.$charval.';';
1064
+ }
1065
+ }
1066
+ break;
1067
+
1068
+ case 'UTF-16LE':
1069
+ for ($i = 0; $i < strlen($string); $i += 2) {
1070
+ $charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2));
1071
+ if (($charval >= 32) && ($charval <= 127)) {
1072
+ $HTMLstring .= chr($charval);
1073
+ } else {
1074
+ $HTMLstring .= '&#'.$charval.';';
1075
+ }
1076
+ }
1077
+ break;
1078
+
1079
+ case 'UTF-16BE':
1080
+ for ($i = 0; $i < strlen($string); $i += 2) {
1081
+ $charval = getid3_lib::BigEndian2Int(substr($string, $i, 2));
1082
+ if (($charval >= 32) && ($charval <= 127)) {
1083
+ $HTMLstring .= chr($charval);
1084
+ } else {
1085
+ $HTMLstring .= '&#'.$charval.';';
1086
+ }
1087
+ }
1088
+ break;
1089
+
1090
+ default:
1091
+ $HTMLstring = 'ERROR: Character set "'.$charset.'" not supported in MultiByteCharString2HTML()';
1092
+ break;
1093
+ }
1094
+ return $HTMLstring;
1095
+ }
1096
+
1097
+
1098
+
1099
+ function RGADnameLookup($namecode) {
1100
+ static $RGADname = array();
1101
+ if (empty($RGADname)) {
1102
+ $RGADname[0] = 'not set';
1103
+ $RGADname[1] = 'Track Gain Adjustment';
1104
+ $RGADname[2] = 'Album Gain Adjustment';
1105
+ }
1106
+
1107
+ return (isset($RGADname[$namecode]) ? $RGADname[$namecode] : '');
1108
+ }
1109
+
1110
+
1111
+ function RGADoriginatorLookup($originatorcode) {
1112
+ static $RGADoriginator = array();
1113
+ if (empty($RGADoriginator)) {
1114
+ $RGADoriginator[0] = 'unspecified';
1115
+ $RGADoriginator[1] = 'pre-set by artist/producer/mastering engineer';
1116
+ $RGADoriginator[2] = 'set by user';
1117
+ $RGADoriginator[3] = 'determined automatically';
1118
+ }
1119
+
1120
+ return (isset($RGADoriginator[$originatorcode]) ? $RGADoriginator[$originatorcode] : '');
1121
+ }
1122
+
1123
+
1124
+ function RGADadjustmentLookup($rawadjustment, $signbit) {
1125
+ $adjustment = $rawadjustment / 10;
1126
+ if ($signbit == 1) {
1127
+ $adjustment *= -1;
1128
+ }
1129
+ return (float) $adjustment;
1130
+ }
1131
+
1132
+
1133
+ function RGADgainString($namecode, $originatorcode, $replaygain) {
1134
+ if ($replaygain < 0) {
1135
+ $signbit = '1';
1136
+ } else {
1137
+ $signbit = '0';
1138
+ }
1139
+ $storedreplaygain = intval(round($replaygain * 10));
1140
+ $gainstring = str_pad(decbin($namecode), 3, '0', STR_PAD_LEFT);
1141
+ $gainstring .= str_pad(decbin($originatorcode), 3, '0', STR_PAD_LEFT);
1142
+ $gainstring .= $signbit;
1143
+ $gainstring .= str_pad(decbin($storedreplaygain), 9, '0', STR_PAD_LEFT);
1144
+
1145
+ return $gainstring;
1146
+ }
1147
+
1148
+ function RGADamplitude2dB($amplitude) {
1149
+ return 20 * log10($amplitude);
1150
+ }
1151
+
1152
+
1153
+ function GetDataImageSize($imgData) {
1154
+ $GetDataImageSize = false;
1155
+ if ($tempfilename = tempnam('*', 'getID3')) {
1156
+ if ($tmp = @fopen($tempfilename, 'wb')) {
1157
+ fwrite($tmp, $imgData);
1158
+ fclose($tmp);
1159
+ $GetDataImageSize = @GetImageSize($tempfilename);
1160
+ }
1161
+ unlink($tempfilename);
1162
+ }
1163
+ return $GetDataImageSize;
1164
+ }
1165
+
1166
+ function ImageTypesLookup($imagetypeid) {
1167
+ static $ImageTypesLookup = array();
1168
+ if (empty($ImageTypesLookup)) {
1169
+ $ImageTypesLookup[1] = 'gif';
1170
+ $ImageTypesLookup[2] = 'jpeg';
1171
+ $ImageTypesLookup[3] = 'png';
1172
+ $ImageTypesLookup[4] = 'swf';
1173
+ $ImageTypesLookup[5] = 'psd';
1174
+ $ImageTypesLookup[6] = 'bmp';
1175
+ $ImageTypesLookup[7] = 'tiff (little-endian)';
1176
+ $ImageTypesLookup[8] = 'tiff (big-endian)';
1177
+ $ImageTypesLookup[9] = 'jpc';
1178
+ $ImageTypesLookup[10] = 'jp2';
1179
+ $ImageTypesLookup[11] = 'jpx';
1180
+ $ImageTypesLookup[12] = 'jb2';
1181
+ $ImageTypesLookup[13] = 'swc';
1182
+ $ImageTypesLookup[14] = 'iff';
1183
+ }
1184
+ return (isset($ImageTypesLookup[$imagetypeid]) ? $ImageTypesLookup[$imagetypeid] : '');
1185
+ }
1186
+
1187
+ function CopyTagsToComments(&$ThisFileInfo) {
1188
+
1189
+ // Copy all entries from ['tags'] into common ['comments']
1190
+ if (!empty($ThisFileInfo['tags'])) {
1191
+ foreach ($ThisFileInfo['tags'] as $tagtype => $tagarray) {
1192
+ foreach ($tagarray as $tagname => $tagdata) {
1193
+ foreach ($tagdata as $key => $value) {
1194
+ if (!empty($value)) {
1195
+ if (empty($ThisFileInfo['comments'][$tagname])) {
1196
+
1197
+ // fall through and append value
1198
+
1199
+ } elseif ($tagtype == 'id3v1') {
1200
+
1201
+ $newvaluelength = strlen(trim($value));
1202
+ foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
1203
+ $oldvaluelength = strlen(trim($existingvalue));
1204
+ if (($newvaluelength <= $oldvaluelength) && (substr($existingvalue, 0, $newvaluelength) == trim($value))) {
1205
+ // new value is identical but shorter-than (or equal-length to) one already in comments - skip
1206
+ break 2;
1207
+ }
1208
+ }
1209
+
1210
+ } else {
1211
+
1212
+ $newvaluelength = strlen(trim($value));
1213
+ foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
1214
+ $oldvaluelength = strlen(trim($existingvalue));
1215
+ if (($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) {
1216
+ $ThisFileInfo['comments'][$tagname][$existingkey] = trim($value);
1217
+ break 2;
1218
+ }
1219
+ }
1220
+
1221
+ }
1222
+ if (empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) {
1223
+ $ThisFileInfo['comments'][$tagname][] = trim($value);
1224
+ }
1225
+ }
1226
+ }
1227
+ }
1228
+ }
1229
+
1230
+ // Copy to ['comments_html']
1231
+ foreach ($ThisFileInfo['comments'] as $field => $values) {
1232
+ foreach ($values as $index => $value) {
1233
+ $ThisFileInfo['comments_html'][$field][$index] = str_replace('&#0;', '', getid3_lib::MultiByteCharString2HTML($value, $ThisFileInfo['encoding']));
1234
+ }
1235
+ }
1236
+ }
1237
+ }
1238
+
1239
+
1240
+ function EmbeddedLookup($key, $begin, $end, $file, $name) {
1241
+
1242
+ // Cached
1243
+ static $cache;
1244
+ if (isset($cache[$file][$name])) {
1245
+ return @$cache[$file][$name][$key];
1246
+ }
1247
+
1248
+ // Init
1249
+ $keylength = strlen($key);
1250
+ $line_count = $end - $begin - 7;
1251
+
1252
+ // Open php file
1253
+ $fp = fopen($file, 'r');
1254
+
1255
+ // Discard $begin lines
1256
+ for ($i = 0; $i < ($begin + 3); $i++) {
1257
+ fgets($fp, 1024);
1258
+ }
1259
+
1260
+ // Loop thru line
1261
+ while (0 < $line_count--) {
1262
+
1263
+ // Read line
1264
+ $line = ltrim(fgets($fp, 1024), "\t ");
1265
+
1266
+ // METHOD A: only cache the matching key - less memory but slower on next lookup of not-previously-looked-up key
1267
+ //$keycheck = substr($line, 0, $keylength);
1268
+ //if ($key == $keycheck) {
1269
+ // $cache[$file][$name][$keycheck] = substr($line, $keylength + 1);
1270
+ // break;
1271
+ //}
1272
+
1273
+ // METHOD B: cache all keys in this lookup - more memory but faster on next lookup of not-previously-looked-up key
1274
+ //$cache[$file][$name][substr($line, 0, $keylength)] = trim(substr($line, $keylength + 1));
1275
+ @list($ThisKey, $ThisValue) = explode("\t", $line, 2);
1276
+ $cache[$file][$name][$ThisKey] = trim($ThisValue);
1277
+ }
1278
+
1279
+ // Close and return
1280
+ fclose($fp);
1281
+ return @$cache[$file][$name][$key];
1282
+ }
1283
+
1284
+ function IncludeDependency($filename, $sourcefile, $DieOnFailure=false) {
1285
+ global $GETID3_ERRORARRAY;
1286
+
1287
+ if (file_exists($filename)) {
1288
+ if (@include_once($filename)) {
1289
+ return true;
1290
+ } else {
1291
+ $diemessage = basename($sourcefile).' depends on '.$filename.', which has errors';
1292
+ }
1293
+ } else {
1294
+ $diemessage = basename($sourcefile).' depends on '.$filename.', which is missing';
1295
+ }
1296
+ if ($DieOnFailure) {
1297
+ die($diemessage);
1298
+ } else {
1299
+ $GETID3_ERRORARRAY[] = $diemessage;
1300
+ }
1301
+ return false;
1302
+ }
1303
+
1304
+ }
1305
+
1306
+ ?>
getid3/getid3.php ADDED
@@ -0,0 +1,1345 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ // //
8
+ // Please see readme.txt for more information //
9
+ // ///
10
+ /////////////////////////////////////////////////////////////////
11
+
12
+ // Defines
13
+ define('GETID3_VERSION', '1.7.8b2');
14
+ define('GETID3_FREAD_BUFFER_SIZE', 16384); // read buffer size in bytes
15
+
16
+
17
+
18
+ class getID3
19
+ {
20
+ // public: Settings
21
+ var $encoding = 'ISO-8859-1'; // CASE SENSITIVE! - i.e. (must be supported by iconv())
22
+ // Examples: ISO-8859-1 UTF-8 UTF-16 UTF-16BE
23
+
24
+ var $encoding_id3v1 = 'ISO-8859-1'; // Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN'
25
+
26
+ var $tempdir = '*'; // default '*' should use system temp dir
27
+
28
+ // public: Optional tag checks - disable for speed.
29
+ var $option_tag_id3v1 = false; // Read and process ID3v1 tags
30
+ var $option_tag_id3v2 = false; // Read and process ID3v2 tags
31
+ var $option_tag_lyrics3 = false; // Read and process Lyrics3 tags
32
+ var $option_tag_apetag = false; // Read and process APE tags
33
+ var $option_tags_process = false; // Copy tags to root key 'tags' and encode to $this->encoding
34
+ var $option_tags_html = false; // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities
35
+
36
+ // public: Optional tag/comment calucations
37
+ var $option_extra_info = true; // Calculate additional info such as bitrate, channelmode etc
38
+
39
+ // public: Optional calculations
40
+ var $option_md5_data = false; // Get MD5 sum of data part - slow
41
+ var $option_md5_data_source = false; // Use MD5 of source file if availble - only FLAC and OptimFROG
42
+ var $option_sha1_data = false; // Get SHA1 sum of data part - slow
43
+ var $option_max_2gb_check = true; // Check whether file is larger than 2 Gb and thus not supported by PHP
44
+
45
+ // private
46
+ var $filename;
47
+
48
+
49
+ // public: constructor
50
+ function getID3()
51
+ {
52
+
53
+ $this->startup_error = '';
54
+ $this->startup_warning = '';
55
+
56
+ // Check for PHP version >= 4.2.0
57
+ if (phpversion() < '4.2.0') {
58
+ $this->startup_error .= 'getID3() requires PHP v4.2.0 or higher - you are running v'.phpversion();
59
+ }
60
+
61
+ // Check memory
62
+ $memory_limit = ini_get('memory_limit');
63
+ if (eregi('([0-9]+)M', $memory_limit, $matches)) {
64
+ // could be stored as "16M" rather than 16777216 for example
65
+ $memory_limit = $matches[1] * 1048576;
66
+ }
67
+ if ($memory_limit <= 0) {
68
+ // memory limits probably disabled
69
+ } elseif ($memory_limit <= 3145728) {
70
+ $this->startup_error .= 'PHP has less than 3MB available memory and will very likely run out. Increase memory_limit in php.ini';
71
+ } elseif ($memory_limit <= 12582912) {
72
+ $this->startup_warning .= 'PHP has less than 12MB available memory and might run out if all modules are loaded. Increase memory_limit in php.ini';
73
+ }
74
+
75
+ // Check safe_mode off
76
+ if ((bool) ini_get('safe_mode')) {
77
+ $this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.');
78
+ }
79
+
80
+
81
+ // define a constant rather than looking up every time it is needed
82
+ if (!defined('GETID3_OS_ISWINDOWS')) {
83
+ if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
84
+ define('GETID3_OS_ISWINDOWS', true);
85
+ } else {
86
+ define('GETID3_OS_ISWINDOWS', false);
87
+ }
88
+ }
89
+
90
+ // Get base path of getID3() - ONCE
91
+ if (!defined('GETID3_INCLUDEPATH')) {
92
+ foreach (get_included_files() as $key => $val) {
93
+ if (basename($val) == 'getid3.php') {
94
+ define('GETID3_INCLUDEPATH', dirname($val).DIRECTORY_SEPARATOR);
95
+ break;
96
+ }
97
+ }
98
+ }
99
+
100
+ // Load support library
101
+ if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
102
+ $this->startup_error .= 'getid3.lib.php is missing or corrupt';
103
+ }
104
+
105
+
106
+ // Needed for Windows only:
107
+ // Define locations of helper applications for Shorten, VorbisComment, MetaFLAC
108
+ // as well as other helper functions such as head, tail, md5sum, etc
109
+ // IMPORTANT: This path cannot have spaces in it. If neccesary, use the 8dot3 equivalent
110
+ // ie for "C:/Program Files/Apache/" put "C:/PROGRA~1/APACHE/"
111
+ // IMPORTANT: This path must include the trailing slash
112
+ if (GETID3_OS_ISWINDOWS && !defined('GETID3_HELPERAPPSDIR')) {
113
+
114
+ $helperappsdir = GETID3_INCLUDEPATH.'..'.DIRECTORY_SEPARATOR.'helperapps'; // must not have any space in this path
115
+
116
+ if (!is_dir($helperappsdir)) {
117
+ $this->startup_error .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist';
118
+ } elseif (strpos(realpath($helperappsdir), ' ') !== false) {
119
+ $DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir));
120
+ foreach ($DirPieces as $key => $value) {
121
+ if ((strpos($value, '.') !== false) && (strpos($value, ' ') === false)) {
122
+ if (strpos($value, '.') > 8) {
123
+ $value = substr($value, 0, 6).'~1';
124
+ }
125
+ } elseif ((strpos($value, ' ') !== false) || strlen($value) > 8) {
126
+ $value = substr($value, 0, 6).'~1';
127
+ }
128
+ $DirPieces[$key] = strtoupper($value);
129
+ }
130
+ $this->startup_error .= 'GETID3_HELPERAPPSDIR must not have any spaces in it - use 8dot3 naming convention if neccesary (on this server that would be something like "'.implode(DIRECTORY_SEPARATOR, $DirPieces).'" - NOTE: this may or may not be the actual 8.3 equivalent of "'.$helperappsdir.'", please double-check). You can run "dir /x" from the commandline to see the correct 8.3-style names.';
131
+ }
132
+ define('GETID3_HELPERAPPSDIR', realpath($helperappsdir).DIRECTORY_SEPARATOR);
133
+ }
134
+
135
+ }
136
+
137
+
138
+ // public: setOption
139
+ function setOption($optArray) {
140
+ if (!is_array($optArray) || empty($optArray)) {
141
+ return false;
142
+ }
143
+ foreach ($optArray as $opt => $val) {
144
+ if (isset($this, $opt) === false) {
145
+ continue;
146
+ }
147
+ $this->$opt = $val;
148
+ }
149
+ return true;
150
+ }
151
+
152
+
153
+ // public: analyze file - replaces GetAllFileInfo() and GetTagOnly()
154
+ function analyze($filename, $filesize=false) {
155
+
156
+ if (!empty($this->startup_error)) {
157
+ return $this->error($this->startup_error);
158
+ }
159
+ if (!empty($this->startup_warning)) {
160
+ $this->warning($this->startup_warning);
161
+ }
162
+
163
+ // init result array and set parameters
164
+ $this->info = array();
165
+ $this->info['GETID3_VERSION'] = GETID3_VERSION;
166
+
167
+ // Check encoding/iconv support
168
+ if (!function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) {
169
+ $errormessage = 'iconv() support is needed for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. ';
170
+ if (GETID3_OS_ISWINDOWS) {
171
+ $errormessage .= 'PHP does not have iconv() support. Please enable php_iconv.dll in php.ini, and copy iconv.dll from c:/php/dlls to c:/windows/system32';
172
+ } else {
173
+ $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch';
174
+ }
175
+ return $this->error($errormessage);
176
+ }
177
+
178
+ // Disable magic_quotes_runtime, if neccesary
179
+ $old_magic_quotes_runtime = get_magic_quotes_runtime(); // store current setting of magic_quotes_runtime
180
+ if ($old_magic_quotes_runtime) {
181
+ set_magic_quotes_runtime(0); // turn off magic_quotes_runtime
182
+ if (get_magic_quotes_runtime()) {
183
+ return $this->error('Could not disable magic_quotes_runtime - getID3() cannot work properly with this setting enabled');
184
+ }
185
+ }
186
+
187
+ // remote files not supported
188
+ if (preg_match('/^(ht|f)tp:\/\//', $filename)) {
189
+ return $this->error('Remote files are not supported in this version of getID3() - please copy the file locally first');
190
+ }
191
+
192
+ // open local file
193
+ if (!$fp = @fopen($filename, 'rb')) {
194
+ return $this->error('Could not open file "'.$filename.'"');
195
+ }
196
+
197
+ // set parameters
198
+ if( $filesize )
199
+ $this->info['filesize'] = $filesize;
200
+ else
201
+ $this->info['filesize'] = filesize($filename);
202
+
203
+ // option_max_2gb_check
204
+ if ($this->option_max_2gb_check) {
205
+ // PHP doesn't support integers larger than 31-bit (~2GB)
206
+ // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize
207
+ // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer
208
+ fseek($fp, 0, SEEK_END);
209
+ if ((($this->info['filesize'] != 0) && (ftell($fp) == 0)) ||
210
+ ($this->info['filesize'] < 0) ||
211
+ (ftell($fp) < 0)) {
212
+ unset($this->info['filesize']);
213
+ fclose($fp);
214
+ return $this->error('File is most likely larger than 2GB and is not supported by PHP');
215
+ }
216
+ }
217
+
218
+ // set more parameters
219
+ $this->info['avdataoffset'] = 0;
220
+ $this->info['avdataend'] = $this->info['filesize'];
221
+ $this->info['fileformat'] = ''; // filled in later
222
+ $this->info['audio']['dataformat'] = ''; // filled in later, unset if not used
223
+ $this->info['video']['dataformat'] = ''; // filled in later, unset if not used
224
+ $this->info['tags'] = array(); // filled in later, unset if not used
225
+ $this->info['error'] = array(); // filled in later, unset if not used
226
+ $this->info['warning'] = array(); // filled in later, unset if not used
227
+ $this->info['comments'] = array(); // filled in later, unset if not used
228
+ $this->info['encoding'] = $this->encoding; // required by id3v2 and iso modules - can be unset at the end if desired
229
+
230
+ // set redundant parameters - might be needed in some include file
231
+ $this->info['filename'] = basename($filename);
232
+ $this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename)));
233
+ $this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename'];
234
+
235
+
236
+ // handle ID3v2 tag - done first - already at beginning of file
237
+ // ID3v2 detection (even if not parsing) is always done otherwise fileformat is much harder to detect
238
+ if ($this->option_tag_id3v2) {
239
+
240
+ $GETID3_ERRORARRAY = &$this->info['warning'];
241
+ if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, false)) {
242
+ $tag = new getid3_id3v2($fp, $this->info);
243
+ unset($tag);
244
+ }
245
+
246
+ } else {
247
+
248
+ fseek($fp, 0, SEEK_SET);
249
+ $header = fread($fp, 10);
250
+ if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) {
251
+ $this->info['id3v2']['header'] = true;
252
+ $this->info['id3v2']['majorversion'] = ord($header{3});
253
+ $this->info['id3v2']['minorversion'] = ord($header{4});
254
+ $this->info['id3v2']['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
255
+
256
+ $this->info['id3v2']['tag_offset_start'] = 0;
257
+ $this->info['id3v2']['tag_offset_end'] = $this->info['id3v2']['tag_offset_start'] + $this->info['id3v2']['headerlength'];
258
+ $this->info['avdataoffset'] = $this->info['id3v2']['tag_offset_end'];
259
+ }
260
+
261
+ }
262
+
263
+
264
+ // handle ID3v1 tag
265
+ if ($this->option_tag_id3v1) {
266
+ if (!@include_once(GETID3_INCLUDEPATH.'module.tag.id3v1.php')) {
267
+ return $this->error('module.tag.id3v1.php is missing - you may disable option_tag_id3v1.');
268
+ }
269
+ $tag = new getid3_id3v1($fp, $this->info);
270
+ unset($tag);
271
+ }
272
+
273
+ // handle APE tag
274
+ if ($this->option_tag_apetag) {
275
+ if (!@include_once(GETID3_INCLUDEPATH.'module.tag.apetag.php')) {
276
+ return $this->error('module.tag.apetag.php is missing - you may disable option_tag_apetag.');
277
+ }
278
+ $tag = new getid3_apetag($fp, $this->info);
279
+ unset($tag);
280
+ }
281
+
282
+ // handle lyrics3 tag
283
+ if ($this->option_tag_lyrics3) {
284
+ if (!@include_once(GETID3_INCLUDEPATH.'module.tag.lyrics3.php')) {
285
+ return $this->error('module.tag.lyrics3.php is missing - you may disable option_tag_lyrics3.');
286
+ }
287
+ $tag = new getid3_lyrics3($fp, $this->info);
288
+ unset($tag);
289
+ }
290
+
291
+ // read 32 kb file data
292
+ fseek($fp, $this->info['avdataoffset'], SEEK_SET);
293
+ $formattest = fread($fp, 32774);
294
+
295
+ // determine format
296
+ $determined_format = $this->GetFileFormat($formattest, $filename);
297
+
298
+ // unable to determine file format
299
+ if (!$determined_format) {
300
+ fclose($fp);
301
+ return $this->error('unable to determine file format');
302
+ }
303
+
304
+ // check for illegal ID3 tags
305
+ if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) {
306
+ if ($determined_format['fail_id3'] === 'ERROR') {
307
+ fclose($fp);
308
+ return $this->error('ID3 tags not allowed on this file type.');
309
+ } elseif ($determined_format['fail_id3'] === 'WARNING') {
310
+ $this->info['warning'][] = 'ID3 tags not allowed on this file type.';
311
+ }
312
+ }
313
+
314
+ // check for illegal APE tags
315
+ if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) {
316
+ if ($determined_format['fail_ape'] === 'ERROR') {
317
+ fclose($fp);
318
+ return $this->error('APE tags not allowed on this file type.');
319
+ } elseif ($determined_format['fail_ape'] === 'WARNING') {
320
+ $this->info['warning'][] = 'APE tags not allowed on this file type.';
321
+ }
322
+ }
323
+
324
+ // set mime type
325
+ $this->info['mime_type'] = $determined_format['mime_type'];
326
+
327
+ // supported format signature pattern detected, but module deleted
328
+ if (!file_exists(GETID3_INCLUDEPATH.$determined_format['include'])) {
329
+ fclose($fp);
330
+ return $this->error('Format not supported, module, '.$determined_format['include'].', was removed.');
331
+ }
332
+
333
+ // module requires iconv support
334
+ if (!function_exists('iconv') && @$determined_format['iconv_req']) {
335
+ return $this->error('iconv support is required for this module ('.$determined_format['include'].').');
336
+ }
337
+
338
+ // include module
339
+ include_once(GETID3_INCLUDEPATH.$determined_format['include']);
340
+
341
+ // instantiate module class
342
+ $class_name = 'getid3_'.$determined_format['module'];
343
+ if (!class_exists($class_name)) {
344
+ return $this->error('Format not supported, module, '.$determined_format['include'].', is corrupt.');
345
+ }
346
+ if (isset($determined_format['option'])) {
347
+ $class = new $class_name($fp, $this->info, $determined_format['option']);
348
+ } else {
349
+ $class = new $class_name($fp, $this->info);
350
+ }
351
+ unset($class);
352
+
353
+ // close file
354
+ fclose($fp);
355
+
356
+ // process all tags - copy to 'tags' and convert charsets
357
+ if ($this->option_tags_process) {
358
+ $this->HandleAllTags();
359
+ }
360
+
361
+ // perform more calculations
362
+ if ($this->option_extra_info) {
363
+ $this->ChannelsBitratePlaytimeCalculations();
364
+ $this->CalculateCompressionRatioVideo();
365
+ $this->CalculateCompressionRatioAudio();
366
+ $this->CalculateReplayGain();
367
+ $this->ProcessAudioStreams();
368
+ }
369
+
370
+ // get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
371
+ if ($this->option_md5_data) {
372
+ // do not cald md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too
373
+ if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) {
374
+ $this->getHashdata('md5');
375
+ }
376
+ }
377
+
378
+ // get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
379
+ if ($this->option_sha1_data) {
380
+ $this->getHashdata('sha1');
381
+ }
382
+
383
+ // remove undesired keys
384
+ $this->CleanUp();
385
+
386
+ // restore magic_quotes_runtime setting
387
+ set_magic_quotes_runtime($old_magic_quotes_runtime);
388
+
389
+ // return info array
390
+ return $this->info;
391
+ }
392
+
393
+
394
+ // private: error handling
395
+ function error($message) {
396
+
397
+ $this->CleanUp();
398
+
399
+ $this->info['error'][] = $message;
400
+ return $this->info;
401
+ }
402
+
403
+
404
+ // private: warning handling
405
+ function warning($message) {
406
+ $this->info['warning'][] = $message;
407
+ return true;
408
+ }
409
+
410
+
411
+ // private: CleanUp
412
+ function CleanUp() {
413
+
414
+ // remove possible empty keys
415
+ $AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams', 'bitrate');
416
+ foreach ($AVpossibleEmptyKeys as $dummy => $key) {
417
+ if (empty($this->info['audio'][$key]) && isset($this->info['audio'][$key])) {
418
+ unset($this->info['audio'][$key]);
419
+ }
420
+ if (empty($this->info['video'][$key]) && isset($this->info['video'][$key])) {
421
+ unset($this->info['video'][$key]);
422
+ }
423
+ }
424
+
425
+ // remove empty root keys
426
+ if (!empty($this->info)) {
427
+ foreach ($this->info as $key => $value) {
428
+ if (empty($this->info[$key]) && ($this->info[$key] !== 0) && ($this->info[$key] !== '0')) {
429
+ unset($this->info[$key]);
430
+ }
431
+ }
432
+ }
433
+
434
+ // remove meaningless entries from unknown-format files
435
+ if (empty($this->info['fileformat'])) {
436
+ if (isset($this->info['avdataoffset'])) {
437
+ unset($this->info['avdataoffset']);
438
+ }
439
+ if (isset($this->info['avdataend'])) {
440
+ unset($this->info['avdataend']);
441
+ }
442
+ }
443
+ }
444
+
445
+
446
+ // return array containing information about all supported formats
447
+ function GetFileFormatArray() {
448
+ static $format_info = array();
449
+ if (empty($format_info)) {
450
+ $format_info = array(
451
+
452
+ // Audio formats
453
+
454
+ // AC-3 - audio - Dolby AC-3 / Dolby Digital
455
+ 'ac3' => array(
456
+ 'pattern' => '^\x0B\x77',
457
+ 'group' => 'audio',
458
+ 'module' => 'ac3',
459
+ 'mime_type' => 'audio/ac3',
460
+ ),
461
+
462
+ // AAC - audio - Advanced Audio Coding (AAC) - ADIF format
463
+ 'adif' => array(
464
+ 'pattern' => '^ADIF',
465
+ 'group' => 'audio',
466
+ 'module' => 'aac',
467
+ 'option' => 'adif',
468
+ 'mime_type' => 'application/octet-stream',
469
+ 'fail_ape' => 'WARNING',
470
+ ),
471
+
472
+
473
+ // AAC - audio - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3)
474
+ 'adts' => array(
475
+ 'pattern' => '^\xFF[\xF0-\xF1\xF8-\xF9]',
476
+ 'group' => 'audio',
477
+ 'module' => 'aac',
478
+ 'option' => 'adts',
479
+ 'mime_type' => 'application/octet-stream',
480
+ 'fail_ape' => 'WARNING',
481
+ ),
482
+
483
+
484
+ // AU - audio - NeXT/Sun AUdio (AU)
485
+ 'au' => array(
486
+ 'pattern' => '^\.snd',
487
+ 'group' => 'audio',
488
+ 'module' => 'au',
489
+ 'mime_type' => 'audio/basic',
490
+ ),
491
+
492
+ // AVR - audio - Audio Visual Research
493
+ 'avr' => array(
494
+ 'pattern' => '^2BIT',
495
+ 'group' => 'audio',
496
+ 'module' => 'avr',
497
+ 'mime_type' => 'application/octet-stream',
498
+ ),
499
+
500
+ // BONK - audio - Bonk v0.9+
501
+ 'bonk' => array(
502
+ 'pattern' => '^\x00(BONK|INFO|META| ID3)',
503
+ 'group' => 'audio',
504
+ 'module' => 'bonk',
505
+ 'mime_type' => 'audio/xmms-bonk',
506
+ ),
507
+
508
+ // DTS - audio - Dolby Theatre System
509
+ 'dts' => array(
510
+ 'pattern' => '^\x7F\xFE\x80\x01',
511
+ 'group' => 'audio',
512
+ 'module' => 'dts',
513
+ 'mime_type' => 'audio/dts',
514
+ ),
515
+
516
+ // FLAC - audio - Free Lossless Audio Codec
517
+ 'flac' => array(
518
+ 'pattern' => '^fLaC',
519
+ 'group' => 'audio',
520
+ 'module' => 'flac',
521
+ 'mime_type' => 'audio/x-flac',
522
+ ),
523
+
524
+ // LA - audio - Lossless Audio (LA)
525
+ 'la' => array(
526
+ 'pattern' => '^LA0[2-4]',
527
+ 'group' => 'audio',
528
+ 'module' => 'la',
529
+ 'mime_type' => 'application/octet-stream',
530
+ ),
531
+
532
+ // LPAC - audio - Lossless Predictive Audio Compression (LPAC)
533
+ 'lpac' => array(
534
+ 'pattern' => '^LPAC',
535
+ 'group' => 'audio',
536
+ 'module' => 'lpac',
537
+ 'mime_type' => 'application/octet-stream',
538
+ ),
539
+
540
+ // MIDI - audio - MIDI (Musical Instrument Digital Interface)
541
+ 'midi' => array(
542
+ 'pattern' => '^MThd',
543
+ 'group' => 'audio',
544
+ 'module' => 'midi',
545
+ 'mime_type' => 'audio/midi',
546
+ ),
547
+
548
+ // MAC - audio - Monkey's Audio Compressor
549
+ 'mac' => array(
550
+ 'pattern' => '^MAC ',
551
+ 'group' => 'audio',
552
+ 'module' => 'monkey',
553
+ 'mime_type' => 'application/octet-stream',
554
+ ),
555
+
556
+ // MOD - audio - MODule (assorted sub-formats)
557
+ 'mod' => array(
558
+ 'pattern' => '^.{1080}(M.K.|[5-9]CHN|[1-3][0-9]CH)',
559
+ 'group' => 'audio',
560
+ 'module' => 'mod',
561
+ 'option' => 'mod',
562
+ 'mime_type' => 'audio/mod',
563
+ ),
564
+
565
+ // MOD - audio - MODule (Impulse Tracker)
566
+ 'it' => array(
567
+ 'pattern' => '^IMPM',
568
+ 'group' => 'audio',
569
+ 'module' => 'mod',
570
+ 'option' => 'it',
571
+ 'mime_type' => 'audio/it',
572
+ ),
573
+
574
+ // MOD - audio - MODule (eXtended Module, various sub-formats)
575
+ 'xm' => array(
576
+ 'pattern' => '^Extended Module',
577
+ 'group' => 'audio',
578
+ 'module' => 'mod',
579
+ 'option' => 'xm',
580
+ 'mime_type' => 'audio/xm',
581
+ ),
582
+
583
+ // MOD - audio - MODule (ScreamTracker)
584
+ 's3m' => array(
585
+ 'pattern' => '^.{44}SCRM',
586
+ 'group' => 'audio',
587
+ 'module' => 'mod',
588
+ 'option' => 's3m',
589
+ 'mime_type' => 'audio/s3m',
590
+ ),
591
+
592
+ // MPC - audio - Musepack / MPEGplus
593
+ 'mpc' => array(
594
+ 'pattern' => '^(MP\+|[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0])',
595
+ 'group' => 'audio',
596
+ 'module' => 'mpc',
597
+ 'mime_type' => 'audio/x-musepack',
598
+ ),
599
+
600
+ // MP3 - audio - MPEG-audio Layer 3 (very similar to AAC-ADTS)
601
+ 'mp3' => array(
602
+ 'pattern' => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]',
603
+ 'group' => 'audio',
604
+ 'module' => 'mp3',
605
+ 'mime_type' => 'audio/mpeg',
606
+ ),
607
+
608
+ // OFR - audio - OptimFROG
609
+ 'ofr' => array(
610
+ 'pattern' => '^(\*RIFF|OFR)',
611
+ 'group' => 'audio',
612
+ 'module' => 'optimfrog',
613
+ 'mime_type' => 'application/octet-stream',
614
+ ),
615
+
616
+ // RKAU - audio - RKive AUdio compressor
617
+ 'rkau' => array(
618
+ 'pattern' => '^RKA',
619
+ 'group' => 'audio',
620
+ 'module' => 'rkau',
621
+ 'mime_type' => 'application/octet-stream',
622
+ ),
623
+
624
+ // SHN - audio - Shorten
625
+ 'shn' => array(
626
+ 'pattern' => '^ajkg',
627
+ 'group' => 'audio',
628
+ 'module' => 'shorten',
629
+ 'mime_type' => 'audio/xmms-shn',
630
+ 'fail_id3' => 'ERROR',
631
+ 'fail_ape' => 'ERROR',
632
+ ),
633
+
634
+ // TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org)
635
+ 'tta' => array(
636
+ 'pattern' => '^TTA', // could also be '^TTA(\x01|\x02|\x03|2|1)'
637
+ 'group' => 'audio',
638
+ 'module' => 'tta',
639
+ 'mime_type' => 'application/octet-stream',
640
+ ),
641
+
642
+ // VOC - audio - Creative Voice (VOC)
643
+ 'voc' => array(
644
+ 'pattern' => '^Creative Voice File',
645
+ 'group' => 'audio',
646
+ 'module' => 'voc',
647
+ 'mime_type' => 'audio/voc',
648
+ ),
649
+
650
+ // VQF - audio - transform-domain weighted interleave Vector Quantization Format (VQF)
651
+ 'vqf' => array(
652
+ 'pattern' => '^TWIN',
653
+ 'group' => 'audio',
654
+ 'module' => 'vqf',
655
+ 'mime_type' => 'application/octet-stream',
656
+ ),
657
+
658
+ // WV - audio - WavPack (v4.0+)
659
+ 'wv' => array(
660
+ 'pattern' => '^wvpk',
661
+ 'group' => 'audio',
662
+ 'module' => 'wavpack',
663
+ 'mime_type' => 'application/octet-stream',
664
+ ),
665
+
666
+
667
+ // Audio-Video formats
668
+
669
+ // ASF - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio
670
+ 'asf' => array(
671
+ 'pattern' => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C',
672
+ 'group' => 'audio-video',
673
+ 'module' => 'asf',
674
+ 'mime_type' => 'video/x-ms-asf',
675
+ 'iconv_req' => false,
676
+ ),
677
+
678
+ // BINK - audio/video - Bink / Smacker
679
+ 'bink' => array(
680
+ 'pattern' => '^(BIK|SMK)',
681
+ 'group' => 'audio-video',
682
+ 'module' => 'bink',
683
+ 'mime_type' => 'application/octet-stream',
684
+ ),
685
+
686
+ // FLV - audio/video - FLash Video
687
+ 'flv' => array(
688
+ 'pattern' => '^FLV\x01',
689
+ 'group' => 'audio-video',
690
+ 'module' => 'flv',
691
+ 'mime_type' => 'video/x-flv',
692
+ ),
693
+
694
+ // MKAV - audio/video - Mastroka
695
+ 'matroska' => array(
696
+ 'pattern' => '^\x1A\x45\xDF\xA3',
697
+ 'group' => 'audio-video',
698
+ 'module' => 'matroska',
699
+ 'mime_type' => 'application/octet-stream',
700
+ ),
701
+
702
+ // MPEG - audio/video - MPEG (Moving Pictures Experts Group)
703
+ 'mpeg' => array(
704
+ 'pattern' => '^\x00\x00\x01(\xBA|\xB3)',
705
+ 'group' => 'audio-video',
706
+ 'module' => 'mpeg',
707
+ 'mime_type' => 'video/mpeg',
708
+ ),
709
+
710
+ // NSV - audio/video - Nullsoft Streaming Video (NSV)
711
+ 'nsv' => array(
712
+ 'pattern' => '^NSV[sf]',
713
+ 'group' => 'audio-video',
714
+ 'module' => 'nsv',
715
+ 'mime_type' => 'application/octet-stream',
716
+ ),
717
+
718
+ // Ogg - audio/video - Ogg (Ogg-Vorbis, Ogg-FLAC, Speex, Ogg-Theora(*), Ogg-Tarkin(*))
719
+ 'ogg' => array(
720
+ 'pattern' => '^OggS',
721
+ 'group' => 'audio',
722
+ 'module' => 'ogg',
723
+ 'mime_type' => 'application/ogg',
724
+ 'fail_id3' => 'WARNING',
725
+ 'fail_ape' => 'WARNING',
726
+ ),
727
+
728
+ // QT - audio/video - Quicktime
729
+ 'quicktime' => array(
730
+ 'pattern' => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)',
731
+ 'group' => 'audio-video',
732
+ 'module' => 'quicktime',
733
+ 'mime_type' => 'video/quicktime',
734
+ ),
735
+
736
+ // RIFF - audio/video - Resource Interchange File Format (RIFF) / WAV / AVI / CD-audio / SDSS = renamed variant used by SmartSound QuickTracks (www.smartsound.com) / FORM = Audio Interchange File Format (AIFF)
737
+ 'riff' => array(
738
+ 'pattern' => '^(RIFF|SDSS|FORM)',
739
+ 'group' => 'audio-video',
740
+ 'module' => 'riff',
741
+ 'mime_type' => 'audio/x-wave',
742
+ 'fail_ape' => 'WARNING',
743
+ ),
744
+
745
+ // Real - audio/video - RealAudio, RealVideo
746
+ 'real' => array(
747
+ 'pattern' => '^(\.RMF|.ra)',
748
+ 'group' => 'audio-video',
749
+ 'module' => 'real',
750
+ 'mime_type' => 'audio/x-realaudio',
751
+ ),
752
+
753
+ // SWF - audio/video - ShockWave Flash
754
+ 'swf' => array(
755
+ 'pattern' => '^(F|C)WS',
756
+ 'group' => 'audio-video',
757
+ 'module' => 'swf',
758
+ 'mime_type' => 'application/x-shockwave-flash',
759
+ ),
760
+
761
+
762
+ // Still-Image formats
763
+
764
+ // BMP - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4)
765
+ 'bmp' => array(
766
+ 'pattern' => '^BM',
767
+ 'group' => 'graphic',
768
+ 'module' => 'bmp',
769
+ 'mime_type' => 'image/bmp',
770
+ 'fail_id3' => 'ERROR',
771
+ 'fail_ape' => 'ERROR',
772
+ ),
773
+
774
+ // GIF - still image - Graphics Interchange Format
775
+ 'gif' => array(
776
+ 'pattern' => '^GIF',
777
+ 'group' => 'graphic',
778
+ 'module' => 'gif',
779
+ 'mime_type' => 'image/gif',
780
+ 'fail_id3' => 'ERROR',
781
+ 'fail_ape' => 'ERROR',
782
+ ),
783
+
784
+ // JPEG - still image - Joint Photographic Experts Group (JPEG)
785
+ 'jpg' => array(
786
+ 'pattern' => '^\xFF\xD8\xFF',
787
+ 'group' => 'graphic',
788
+ 'module' => 'jpg',
789
+ 'mime_type' => 'image/jpeg',
790
+ 'fail_id3' => 'ERROR',
791
+ 'fail_ape' => 'ERROR',
792
+ ),
793
+
794
+ // PCD - still image - Kodak Photo CD
795
+ 'pcd' => array(
796
+ 'pattern' => '^.{2048}PCD_IPI\x00',
797
+ 'group' => 'graphic',
798
+ 'module' => 'pcd',
799
+ 'mime_type' => 'image/x-photo-cd',
800
+ 'fail_id3' => 'ERROR',
801
+ 'fail_ape' => 'ERROR',
802
+ ),
803
+
804
+
805
+ // PNG - still image - Portable Network Graphics (PNG)
806
+ 'png' => array(
807
+ 'pattern' => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A',
808
+ 'group' => 'graphic',
809
+ 'module' => 'png',
810
+ 'mime_type' => 'image/png',
811
+ 'fail_id3' => 'ERROR',
812
+ 'fail_ape' => 'ERROR',
813
+ ),
814
+
815
+
816
+ // SVG - still image - Scalable Vector Graphics (SVG)
817
+ 'svg' => array(
818
+ 'pattern' => '<!DOCTYPE svg PUBLIC ',
819
+ 'group' => 'graphic',
820
+ 'module' => 'svg',
821
+ 'mime_type' => 'image/svg+xml',
822
+ 'fail_id3' => 'ERROR',
823
+ 'fail_ape' => 'ERROR',
824
+ ),
825
+
826
+
827
+ // TIFF - still image - Tagged Information File Format (TIFF)
828
+ 'tiff' => array(
829
+ 'pattern' => '^(II\x2A\x00|MM\x00\x2A)',
830
+ 'group' => 'graphic',
831
+ 'module' => 'tiff',
832
+ 'mime_type' => 'image/tiff',
833
+ 'fail_id3' => 'ERROR',
834
+ 'fail_ape' => 'ERROR',
835
+ ),
836
+
837
+
838
+ // Data formats
839
+
840
+ // ISO - data - International Standards Organization (ISO) CD-ROM Image
841
+ 'iso' => array(
842
+ 'pattern' => '^.{32769}CD001',
843
+ 'group' => 'misc',
844
+ 'module' => 'iso',
845
+ 'mime_type' => 'application/octet-stream',
846
+ 'fail_id3' => 'ERROR',
847
+ 'fail_ape' => 'ERROR',
848
+ 'iconv_req' => false,
849
+ ),
850
+
851
+ // RAR - data - RAR compressed data
852
+ 'rar' => array(
853
+ 'pattern' => '^Rar\!',
854
+ 'group' => 'archive',
855
+ 'module' => 'rar',
856
+ 'mime_type' => 'application/octet-stream',
857
+ 'fail_id3' => 'ERROR',
858
+ 'fail_ape' => 'ERROR',
859
+ ),
860
+
861
+ // SZIP - audio/data - SZIP compressed data
862
+ 'szip' => array(
863
+ 'pattern' => '^SZ\x0A\x04',
864
+ 'group' => 'archive',
865
+ 'module' => 'szip',
866
+ 'mime_type' => 'application/octet-stream',
867
+ 'fail_id3' => 'ERROR',
868
+ 'fail_ape' => 'ERROR',
869
+ ),
870
+
871
+ // TAR - data - TAR compressed data
872
+ 'tar' => array(
873
+ 'pattern' => '^.{100}[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20\x00]{12}[0-9\x20\x00]{12}',
874
+ 'group' => 'archive',
875
+ 'module' => 'tar',
876
+ 'mime_type' => 'application/x-tar',
877
+ 'fail_id3' => 'ERROR',
878
+ 'fail_ape' => 'ERROR',
879
+ ),
880
+
881
+ // GZIP - data - GZIP compressed data
882
+ 'gz' => array(
883
+ 'pattern' => '^\x1F\x8B\x08',
884
+ 'group' => 'archive',
885
+ 'module' => 'gzip',
886
+ 'mime_type' => 'application/x-gzip',
887
+ 'fail_id3' => 'ERROR',
888
+ 'fail_ape' => 'ERROR',
889
+ ),
890
+
891
+ // ZIP - data - ZIP compressed data
892
+ 'zip' => array(
893
+ 'pattern' => '^PK\x03\x04',
894
+ 'group' => 'archive',
895
+ 'module' => 'zip',
896
+ 'mime_type' => 'application/zip',
897
+ 'fail_id3' => 'ERROR',
898
+ 'fail_ape' => 'ERROR',
899
+ ),
900
+
901
+
902
+ // Misc other formats
903
+
904
+ // PAR2 - data - Parity Volume Set Specification 2.0
905
+ 'par2' => array (
906
+ 'pattern' => '^PAR2\x00PKT',
907
+ 'group' => 'misc',
908
+ 'module' => 'par2',
909
+ 'mime_type' => 'application/octet-stream',
910
+ 'fail_id3' => 'ERROR',
911
+ 'fail_ape' => 'ERROR',
912
+ ),
913
+
914
+ // PDF - data - Portable Document Format
915
+ 'pdf' => array(
916
+ 'pattern' => '^\x25PDF',
917
+ 'group' => 'misc',
918
+ 'module' => 'pdf',
919
+ 'mime_type' => 'application/pdf',
920
+ 'fail_id3' => 'ERROR',
921
+ 'fail_ape' => 'ERROR',
922
+ ),
923
+
924
+ // MSOFFICE - data - ZIP compressed data
925
+ 'msoffice' => array(
926
+ 'pattern' => '^\xD0\xCF\x11\xE0', // D0CF11E == DOCFILE == Microsoft Office Document
927
+ 'group' => 'misc',
928
+ 'module' => 'msoffice',
929
+ 'mime_type' => 'application/octet-stream',
930
+ 'fail_id3' => 'ERROR',
931
+ 'fail_ape' => 'ERROR',
932
+ ),
933
+ );
934
+ }
935
+
936
+ return $format_info;
937
+ }
938
+
939
+
940
+
941
+ function GetFileFormat(&$filedata, $filename='') {
942
+ // this function will determine the format of a file based on usually
943
+ // the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG,
944
+ // and in the case of ISO CD image, 6 bytes offset 32kb from the start
945
+ // of the file).
946
+
947
+ // Identify file format - loop through $format_info and detect with reg expr
948
+ foreach ($this->GetFileFormatArray() as $format_name => $info) {
949
+ // Using preg_match() instead of ereg() - much faster
950
+ // The /s switch on preg_match() forces preg_match() NOT to treat
951
+ // newline (0x0A) characters as special chars but do a binary match
952
+ if (preg_match('/'.$info['pattern'].'/s', $filedata)) {
953
+ $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
954
+ return $info;
955
+ }
956
+ }
957
+
958
+
959
+ if (preg_match('/\.mp[123a]$/i', $filename)) {
960
+ // Too many mp3 encoders on the market put gabage in front of mpeg files
961
+ // use assume format on these if format detection failed
962
+ $GetFileFormatArray = $this->GetFileFormatArray();
963
+ $info = $GetFileFormatArray['mp3'];
964
+ $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
965
+ return $info;
966
+ }
967
+
968
+ return false;
969
+ }
970
+
971
+
972
+ // converts array to $encoding charset from $this->encoding
973
+ function CharConvert(&$array, $encoding) {
974
+
975
+ // identical encoding - end here
976
+ if ($encoding == $this->encoding) {
977
+ return;
978
+ }
979
+
980
+ // loop thru array
981
+ foreach ($array as $key => $value) {
982
+
983
+ // go recursive
984
+ if (is_array($value)) {
985
+ $this->CharConvert($array[$key], $encoding);
986
+ }
987
+
988
+ // convert string
989
+ elseif (is_string($value)) {
990
+ $array[$key] = trim(getid3_lib::iconv_fallback($encoding, $this->encoding, $value));
991
+ }
992
+ }
993
+ }
994
+
995
+
996
+ function HandleAllTags() {
997
+
998
+ // key name => array (tag name, character encoding)
999
+ static $tags;
1000
+ if (empty($tags)) {
1001
+ $tags = array(
1002
+ 'asf' => array('asf' , 'UTF-16LE'),
1003
+ 'midi' => array('midi' , 'ISO-8859-1'),
1004
+ 'nsv' => array('nsv' , 'ISO-8859-1'),
1005
+ 'ogg' => array('vorbiscomment' , 'UTF-8'),
1006
+ 'png' => array('png' , 'UTF-8'),
1007
+ 'tiff' => array('tiff' , 'ISO-8859-1'),
1008
+ 'quicktime' => array('quicktime' , 'ISO-8859-1'),
1009
+ 'real' => array('real' , 'ISO-8859-1'),
1010
+ 'vqf' => array('vqf' , 'ISO-8859-1'),
1011
+ 'zip' => array('zip' , 'ISO-8859-1'),
1012
+ 'riff' => array('riff' , 'ISO-8859-1'),
1013
+ 'lyrics3' => array('lyrics3' , 'ISO-8859-1'),
1014
+ 'id3v1' => array('id3v1' , $this->encoding_id3v1),
1015
+ 'id3v2' => array('id3v2' , 'UTF-8'), // not according to the specs (every frame can have a different encoding), but getID3() force-converts all encodings to UTF-8
1016
+ 'ape' => array('ape' , 'UTF-8')
1017
+ );
1018
+ }
1019
+
1020
+ // loop thru comments array
1021
+ foreach ($tags as $comment_name => $tagname_encoding_array) {
1022
+ list($tag_name, $encoding) = $tagname_encoding_array;
1023
+
1024
+ // fill in default encoding type if not already present
1025
+ if (isset($this->info[$comment_name]) && !isset($this->info[$comment_name]['encoding'])) {
1026
+ $this->info[$comment_name]['encoding'] = $encoding;
1027
+ }
1028
+
1029
+ // copy comments if key name set
1030
+ if (!empty($this->info[$comment_name]['comments'])) {
1031
+
1032
+ foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) {
1033
+ foreach ($valuearray as $key => $value) {
1034
+ if (strlen(trim($value)) > 0) {
1035
+ $this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value; // do not trim!! Unicode characters will get mangled if trailing nulls are removed!
1036
+ }
1037
+ }
1038
+ }
1039
+
1040
+ if (!isset($this->info['tags'][$tag_name])) {
1041
+ // comments are set but contain nothing but empty strings, so skip
1042
+ continue;
1043
+ }
1044
+
1045
+ if ($this->option_tags_html) {
1046
+ foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
1047
+ foreach ($valuearray as $key => $value) {
1048
+ if (is_string($value)) {
1049
+ //$this->info['tags_html'][$tag_name][$tag_key][$key] = getid3_lib::MultiByteCharString2HTML($value, $encoding);
1050
+ $this->info['tags_html'][$tag_name][$tag_key][$key] = str_replace('&#0;', '', getid3_lib::MultiByteCharString2HTML($value, $encoding));
1051
+ } else {
1052
+ $this->info['tags_html'][$tag_name][$tag_key][$key] = $value;
1053
+ }
1054
+ }
1055
+ }
1056
+ }
1057
+
1058
+ $this->CharConvert($this->info['tags'][$tag_name], $encoding); // only copy gets converted!
1059
+ }
1060
+
1061
+ }
1062
+ return true;
1063
+ }
1064
+
1065
+
1066
+ function getHashdata($algorithm) {
1067
+ switch ($algorithm) {
1068
+ case 'md5':
1069
+ case 'sha1':
1070
+ break;
1071
+
1072
+ default:
1073
+ return $this->error('bad algorithm "'.$algorithm.'" in getHashdata()');
1074
+ break;
1075
+ }
1076
+
1077
+ if ((@$this->info['fileformat'] == 'ogg') && (@$this->info['audio']['dataformat'] == 'vorbis')) {
1078
+
1079
+ // We cannot get an identical md5_data value for Ogg files where the comments
1080
+ // span more than 1 Ogg page (compared to the same audio data with smaller
1081
+ // comments) using the normal getID3() method of MD5'ing the data between the
1082
+ // end of the comments and the end of the file (minus any trailing tags),
1083
+ // because the page sequence numbers of the pages that the audio data is on
1084
+ // do not match. Under normal circumstances, where comments are smaller than
1085
+ // the nominal 4-8kB page size, then this is not a problem, but if there are
1086
+ // very large comments, the only way around it is to strip off the comment
1087
+ // tags with vorbiscomment and MD5 that file.
1088
+ // This procedure must be applied to ALL Ogg files, not just the ones with
1089
+ // comments larger than 1 page, because the below method simply MD5's the
1090
+ // whole file with the comments stripped, not just the portion after the
1091
+ // comments block (which is the standard getID3() method.
1092
+
1093
+ // The above-mentioned problem of comments spanning multiple pages and changing
1094
+ // page sequence numbers likely happens for OggSpeex and OggFLAC as well, but
1095
+ // currently vorbiscomment only works on OggVorbis files.
1096
+
1097
+ if ((bool) ini_get('safe_mode')) {
1098
+
1099
+ $this->info['warning'][] = 'Failed making system call to vorbiscomment.exe - '.$algorithm.'_data is incorrect - error returned: PHP running in Safe Mode (backtick operator not available)';
1100
+ $this->info[$algorithm.'_data'] = false;
1101
+
1102
+ } else {
1103
+
1104
+ // Prevent user from aborting script
1105
+ $old_abort = ignore_user_abort(true);
1106
+
1107
+ // Create empty file
1108
+ $empty = tempnam('*', 'getID3');
1109
+ touch($empty);
1110
+
1111
+
1112
+ // Use vorbiscomment to make temp file without comments
1113
+ $temp = tempnam('*', 'getID3');
1114
+ $file = $this->info['filenamepath'];
1115
+
1116
+ if (GETID3_OS_ISWINDOWS) {
1117
+
1118
+ if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) {
1119
+
1120
+ $commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w -c "'.$empty.'" "'.$file.'" "'.$temp.'"';
1121
+ $VorbisCommentError = `$commandline`;
1122
+
1123
+ } else {
1124
+
1125
+ $VorbisCommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR;
1126
+
1127
+ }
1128
+
1129
+ } else {
1130
+
1131
+ $commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1';
1132
+ $commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1';
1133
+ $VorbisCommentError = `$commandline`;
1134
+
1135
+ }
1136
+
1137
+ if (!empty($VorbisCommentError)) {
1138
+
1139
+ $this->info['warning'][] = 'Failed making system call to vorbiscomment(.exe) - '.$algorithm.'_data will be incorrect. If vorbiscomment is unavailable, please download from http://www.vorbis.com/download.psp and put in the getID3() directory. Error returned: '.$VorbisCommentError;
1140
+ $this->info[$algorithm.'_data'] = false;
1141
+
1142
+ } else {
1143
+
1144
+ // Get hash of newly created file
1145
+ switch ($algorithm) {
1146
+ case 'md5':
1147
+ $this->info[$algorithm.'_data'] = getid3_lib::md5_file($temp);
1148
+ break;
1149
+
1150
+ case 'sha1':
1151
+ $this->info[$algorithm.'_data'] = getid3_lib::sha1_file($temp);
1152
+ break;
1153
+ }
1154
+ }
1155
+
1156
+ // Clean up
1157
+ unlink($empty);
1158
+ unlink($temp);
1159
+
1160
+ // Reset abort setting
1161
+ ignore_user_abort($old_abort);
1162
+
1163
+ }
1164
+
1165
+ } else {
1166
+
1167
+ if (!empty($this->info['avdataoffset']) || (isset($this->info['avdataend']) && ($this->info['avdataend'] < $this->info['filesize']))) {
1168
+
1169
+ // get hash from part of file
1170
+ $this->info[$algorithm.'_data'] = getid3_lib::hash_data($this->info['filenamepath'], $this->info['avdataoffset'], $this->info['avdataend'], $algorithm);
1171
+
1172
+ } else {
1173
+
1174
+ // get hash from whole file
1175
+ switch ($algorithm) {
1176
+ case 'md5':
1177
+ $this->info[$algorithm.'_data'] = getid3_lib::md5_file($this->info['filenamepath']);
1178
+ break;
1179
+
1180
+ case 'sha1':
1181
+ $this->info[$algorithm.'_data'] = getid3_lib::sha1_file($this->info['filenamepath']);
1182
+ break;
1183
+ }
1184
+ }
1185
+
1186
+ }
1187
+ return true;
1188
+ }
1189
+
1190
+
1191
+ function ChannelsBitratePlaytimeCalculations() {
1192
+
1193
+ // set channelmode on audio
1194
+ if (@$this->info['audio']['channels'] == '1') {
1195
+ $this->info['audio']['channelmode'] = 'mono';
1196
+ } elseif (@$this->info['audio']['channels'] == '2') {
1197
+ $this->info['audio']['channelmode'] = 'stereo';
1198
+ }
1199
+
1200
+ // Calculate combined bitrate - audio + video
1201
+ $CombinedBitrate = 0;
1202
+ $CombinedBitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0);
1203
+ $CombinedBitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0);
1204
+ if (($CombinedBitrate > 0) && empty($this->info['bitrate'])) {
1205
+ $this->info['bitrate'] = $CombinedBitrate;
1206
+ }
1207
+ //if ((isset($this->info['video']) && !isset($this->info['video']['bitrate'])) || (isset($this->info['audio']) && !isset($this->info['audio']['bitrate']))) {
1208
+ // // for example, VBR MPEG video files cannot determine video bitrate:
1209
+ // // should not set overall bitrate and playtime from audio bitrate only
1210
+ // unset($this->info['bitrate']);
1211
+ //}
1212
+
1213
+ // video bitrate undetermined, but calculable
1214
+ if (isset($this->info['video']['dataformat']) && $this->info['video']['dataformat'] && (!isset($this->info['video']['bitrate']) || ($this->info['video']['bitrate'] == 0))) {
1215
+ // if video bitrate not set
1216
+ if (isset($this->info['audio']['bitrate']) && ($this->info['audio']['bitrate'] > 0) && ($this->info['audio']['bitrate'] == $this->info['bitrate'])) {
1217
+ // AND if audio bitrate is set to same as overall bitrate
1218
+ if (isset($this->info['playtime_seconds']) && ($this->info['playtime_seconds'] > 0)) {
1219
+ // AND if playtime is set
1220
+ if (isset($this->info['avdataend']) && isset($this->info['avdataoffset'])) {
1221
+ // AND if AV data offset start/end is known
1222
+ // THEN we can calculate the video bitrate
1223
+ $this->info['bitrate'] = round((($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds']);
1224
+ $this->info['video']['bitrate'] = $this->info['bitrate'] - $this->info['audio']['bitrate'];
1225
+ }
1226
+ }
1227
+ }
1228
+ }
1229
+
1230
+ if (!isset($this->info['playtime_seconds']) && !empty($this->info['bitrate'])) {
1231
+ $this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate'];
1232
+ }
1233
+
1234
+ // Set playtime string
1235
+ if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) {
1236
+ $this->info['playtime_string'] = getid3_lib::PlaytimeString($this->info['playtime_seconds']);
1237
+ }
1238
+ }
1239
+
1240
+
1241
+ function CalculateCompressionRatioVideo() {
1242
+ if (empty($this->info['video'])) {
1243
+ return false;
1244
+ }
1245
+ if (empty($this->info['video']['resolution_x']) || empty($this->info['video']['resolution_y'])) {
1246
+ return false;
1247
+ }
1248
+ if (empty($this->info['video']['bits_per_sample'])) {
1249
+ return false;
1250
+ }
1251
+
1252
+ switch ($this->info['video']['dataformat']) {
1253
+ case 'bmp':
1254
+ case 'gif':
1255
+ case 'jpeg':
1256
+ case 'jpg':
1257
+ case 'png':
1258
+ case 'tiff':
1259
+ $FrameRate = 1;
1260
+ $PlaytimeSeconds = 1;
1261
+ $BitrateCompressed = $this->info['filesize'] * 8;
1262
+ break;
1263
+
1264
+ default:
1265
+ if (!empty($this->info['video']['frame_rate'])) {
1266
+ $FrameRate = $this->info['video']['frame_rate'];
1267
+ } else {
1268
+ return false;
1269
+ }
1270
+ if (!empty($this->info['playtime_seconds'])) {
1271
+ $PlaytimeSeconds = $this->info['playtime_seconds'];
1272
+ } else {
1273
+ return false;
1274
+ }
1275
+ if (!empty($this->info['video']['bitrate'])) {
1276
+ $BitrateCompressed = $this->info['video']['bitrate'];
1277
+ } else {
1278
+ return false;
1279
+ }
1280
+ break;
1281
+ }
1282
+ $BitrateUncompressed = $this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $FrameRate;
1283
+
1284
+ $this->info['video']['compression_ratio'] = $BitrateCompressed / $BitrateUncompressed;
1285
+ return true;
1286
+ }
1287
+
1288
+
1289
+ function CalculateCompressionRatioAudio() {
1290
+ if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate'])) {
1291
+ return false;
1292
+ }
1293
+ $this->info['audio']['compression_ratio'] = $this->info['audio']['bitrate'] / ($this->info['audio']['channels'] * $this->info['audio']['sample_rate'] * (!empty($this->info['audio']['bits_per_sample']) ? $this->info['audio']['bits_per_sample'] : 16));
1294
+
1295
+ if (!empty($this->info['audio']['streams'])) {
1296
+ foreach ($this->info['audio']['streams'] as $streamnumber => $streamdata) {
1297
+ if (!empty($streamdata['bitrate']) && !empty($streamdata['channels']) && !empty($streamdata['sample_rate'])) {
1298
+ $this->info['audio']['streams'][$streamnumber]['compression_ratio'] = $streamdata['bitrate'] / ($streamdata['channels'] * $streamdata['sample_rate'] * (!empty($streamdata['bits_per_sample']) ? $streamdata['bits_per_sample'] : 16));
1299
+ }
1300
+ }
1301
+ }
1302
+ return true;
1303
+ }
1304
+
1305
+
1306
+ function CalculateReplayGain() {
1307
+ if (isset($this->info['replay_gain'])) {
1308
+ $this->info['replay_gain']['reference_volume'] = 89;
1309
+ if (isset($this->info['replay_gain']['track']['adjustment'])) {
1310
+ $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment'];
1311
+ }
1312
+ if (isset($this->info['replay_gain']['album']['adjustment'])) {
1313
+ $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment'];
1314
+ }
1315
+
1316
+ if (isset($this->info['replay_gain']['track']['peak'])) {
1317
+ $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['track']['peak']);
1318
+ }
1319
+ if (isset($this->info['replay_gain']['album']['peak'])) {
1320
+ $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['album']['peak']);
1321
+ }
1322
+ }
1323
+ return true;
1324
+ }
1325
+
1326
+ function ProcessAudioStreams() {
1327
+ if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) {
1328
+ if (!isset($this->info['audio']['streams'])) {
1329
+ foreach ($this->info['audio'] as $key => $value) {
1330
+ if ($key != 'streams') {
1331
+ $this->info['audio']['streams'][0][$key] = $value;
1332
+ }
1333
+ }
1334
+ }
1335
+ }
1336
+ return true;
1337
+ }
1338
+
1339
+ function getid3_tempnam() {
1340
+ return tempnam($this->tempdir, 'gI3');
1341
+ }
1342
+
1343
+ }
1344
+
1345
+ ?>
getid3/module.audio.mp3.php ADDED
@@ -0,0 +1,1932 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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.audio.mp3.php //
11
+ // module for analyzing MP3 files //
12
+ // dependencies: NONE //
13
+ // ///
14
+ /////////////////////////////////////////////////////////////////
15
+
16
+
17
+ // number of frames to scan to determine if MPEG-audio sequence is valid
18
+ // Lower this number to 5-20 for faster scanning
19
+ // Increase this number to 50+ for most accurate detection of valid VBR/CBR
20
+ // mpeg-audio streams
21
+ define('GETID3_MP3_VALID_CHECK_FRAMES', 35);
22
+
23
+
24
+ class getid3_mp3
25
+ {
26
+
27
+ var $allow_bruteforce = false; // forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, unrecommended, but may provide data from otherwise-unusuable files
28
+
29
+ function getid3_mp3(&$fd, &$ThisFileInfo) {
30
+
31
+ if (!$this->getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $ThisFileInfo['avdataoffset'])) {
32
+ if ($this->allow_bruteforce) {
33
+ $ThisFileInfo['error'][] = 'Rescanning file in BruteForce mode';
34
+ $this->getOnlyMPEGaudioInfoBruteForce($fd, $ThisFileInfo);
35
+ }
36
+ }
37
+
38
+
39
+ if (isset($ThisFileInfo['mpeg']['audio']['bitrate_mode'])) {
40
+ $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']);
41
+ }
42
+
43
+ if (((isset($ThisFileInfo['id3v2']['headerlength']) && ($ThisFileInfo['avdataoffset'] > $ThisFileInfo['id3v2']['headerlength'])) || (!isset($ThisFileInfo['id3v2']) && ($ThisFileInfo['avdataoffset'] > 0)))) {
44
+
45
+ $synchoffsetwarning = 'Unknown data before synch ';
46
+ if (isset($ThisFileInfo['id3v2']['headerlength'])) {
47
+ $synchoffsetwarning .= '(ID3v2 header ends at '.$ThisFileInfo['id3v2']['headerlength'].', then '.($ThisFileInfo['avdataoffset'] - $ThisFileInfo['id3v2']['headerlength']).' bytes garbage, ';
48
+ } else {
49
+ $synchoffsetwarning .= '(should be at beginning of file, ';
50
+ }
51
+ $synchoffsetwarning .= 'synch detected at '.$ThisFileInfo['avdataoffset'].')';
52
+ if ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr') {
53
+
54
+ if (!empty($ThisFileInfo['id3v2']['headerlength']) && (($ThisFileInfo['avdataoffset'] - $ThisFileInfo['id3v2']['headerlength']) == $ThisFileInfo['mpeg']['audio']['framelength'])) {
55
+
56
+ $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.';
57
+ $ThisFileInfo['audio']['codec'] = 'LAME';
58
+ $CurrentDataLAMEversionString = 'LAME3.';
59
+
60
+ } elseif (empty($ThisFileInfo['id3v2']['headerlength']) && ($ThisFileInfo['avdataoffset'] == $ThisFileInfo['mpeg']['audio']['framelength'])) {
61
+
62
+ $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.';
63
+ $ThisFileInfo['audio']['codec'] = 'LAME';
64
+ $CurrentDataLAMEversionString = 'LAME3.';
65
+
66
+ }
67
+
68
+ }
69
+ $ThisFileInfo['warning'][] = $synchoffsetwarning;
70
+
71
+ }
72
+
73
+ if (isset($ThisFileInfo['mpeg']['audio']['LAME'])) {
74
+ $ThisFileInfo['audio']['codec'] = 'LAME';
75
+ if (!empty($ThisFileInfo['mpeg']['audio']['LAME']['long_version'])) {
76
+ $ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['mpeg']['audio']['LAME']['long_version'], "\x00");
77
+ } elseif (!empty($ThisFileInfo['mpeg']['audio']['LAME']['short_version'])) {
78
+ $ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['mpeg']['audio']['LAME']['short_version'], "\x00");
79
+ }
80
+ }
81
+
82
+ $CurrentDataLAMEversionString = (!empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : @$ThisFileInfo['audio']['encoder']);
83
+ if (!empty($CurrentDataLAMEversionString) && (substr($CurrentDataLAMEversionString, 0, 6) == 'LAME3.') && !preg_match('[0-9\)]', substr($CurrentDataLAMEversionString, -1))) {
84
+ // a version number of LAME that does not end with a number like "LAME3.92"
85
+ // or with a closing parenthesis like "LAME3.88 (alpha)"
86
+ // or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92)
87
+
88
+ // not sure what the actual last frame length will be, but will be less than or equal to 1441
89
+ $PossiblyLongerLAMEversion_FrameLength = 1441;
90
+
91
+ // Not sure what version of LAME this is - look in padding of last frame for longer version string
92
+ $PossibleLAMEversionStringOffset = $ThisFileInfo['avdataend'] - $PossiblyLongerLAMEversion_FrameLength;
93
+ fseek($fd, $PossibleLAMEversionStringOffset);
94
+ $PossiblyLongerLAMEversion_Data = fread($fd, $PossiblyLongerLAMEversion_FrameLength);
95
+ switch (substr($CurrentDataLAMEversionString, -1)) {
96
+ case 'a':
97
+ case 'b':
98
+ // "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example
99
+ // need to trim off "a" to match longer string
100
+ $CurrentDataLAMEversionString = substr($CurrentDataLAMEversionString, 0, -1);
101
+ break;
102
+ }
103
+ if (($PossiblyLongerLAMEversion_String = strstr($PossiblyLongerLAMEversion_Data, $CurrentDataLAMEversionString)) !== false) {
104
+ if (substr($PossiblyLongerLAMEversion_String, 0, strlen($CurrentDataLAMEversionString)) == $CurrentDataLAMEversionString) {
105
+ $PossiblyLongerLAMEversion_NewString = substr($PossiblyLongerLAMEversion_String, 0, strspn($PossiblyLongerLAMEversion_String, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3" "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)"
106
+ if (strlen($PossiblyLongerLAMEversion_NewString) > strlen(@$ThisFileInfo['audio']['encoder'])) {
107
+ $ThisFileInfo['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString;
108
+ }
109
+ }
110
+ }
111
+ }
112
+ if (!empty($ThisFileInfo['audio']['encoder'])) {
113
+ $ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['audio']['encoder'], "\x00 ");
114
+ }
115
+
116
+ switch (@$ThisFileInfo['mpeg']['audio']['layer']) {
117
+ case 1:
118
+ case 2:
119
+ $ThisFileInfo['audio']['dataformat'] = 'mp'.$ThisFileInfo['mpeg']['audio']['layer'];
120
+ break;
121
+ }
122
+ if ($ThisFileInfo['fileformat'] == 'mp3') {
123
+ switch ($ThisFileInfo['audio']['dataformat']) {
124
+ case 'mp1':
125
+ case 'mp2':
126
+ case 'mp3':
127
+ $ThisFileInfo['fileformat'] = $ThisFileInfo['audio']['dataformat'];
128
+ break;
129
+
130
+ default:
131
+ $ThisFileInfo['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$ThisFileInfo['audio']['dataformat'].'"';
132
+ break;
133
+ }
134
+ }
135
+
136
+ if (empty($ThisFileInfo['fileformat'])) {
137
+ unset($ThisFileInfo['fileformat']);
138
+ unset($ThisFileInfo['audio']['bitrate_mode']);
139
+ unset($ThisFileInfo['avdataoffset']);
140
+ unset($ThisFileInfo['avdataend']);
141
+ return false;
142
+ }
143
+
144
+ $ThisFileInfo['mime_type'] = 'audio/mpeg';
145
+ $ThisFileInfo['audio']['lossless'] = false;
146
+
147
+ // Calculate playtime
148
+ if (!isset($ThisFileInfo['playtime_seconds']) && isset($ThisFileInfo['audio']['bitrate']) && ($ThisFileInfo['audio']['bitrate'] > 0)) {
149
+ $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['audio']['bitrate'];
150
+ }
151
+
152
+ $ThisFileInfo['audio']['encoder_options'] = $this->GuessEncoderOptions($ThisFileInfo);
153
+
154
+ return true;
155
+ }
156
+
157
+
158
+ function GuessEncoderOptions(&$ThisFileInfo) {
159
+ // shortcuts
160
+ if (!empty($ThisFileInfo['mpeg']['audio'])) {
161
+ $thisfile_mpeg_audio = &$ThisFileInfo['mpeg']['audio'];
162
+ if (!empty($thisfile_mpeg_audio['LAME'])) {
163
+ $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
164
+ }
165
+ }
166
+
167
+ $encoder_options = '';
168
+ static $NamedPresetBitrates = array(16, 24, 40, 56, 112, 128, 160, 192, 256);
169
+
170
+ if ((@$thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) {
171
+
172
+ $encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality'];
173
+
174
+ } elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) {
175
+
176
+ $encoder_options = $thisfile_mpeg_audio_lame['preset_used'];
177
+
178
+ } elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) {
179
+
180
+ static $KnownEncoderValues = array();
181
+ if (empty($KnownEncoderValues)) {
182
+
183
+ //$KnownEncoderValues[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name';
184
+ $KnownEncoderValues[0xFF][58][1][1][3][2][20500] = '--alt-preset insane'; // 3.90, 3.90.1, 3.92
185
+ $KnownEncoderValues[0xFF][58][1][1][3][2][20600] = '--alt-preset insane'; // 3.90.2, 3.90.3, 3.91
186
+ $KnownEncoderValues[0xFF][57][1][1][3][4][20500] = '--alt-preset insane'; // 3.94, 3.95
187
+ $KnownEncoderValues['**'][78][3][2][3][2][19500] = '--alt-preset extreme'; // 3.90, 3.90.1, 3.92
188
+ $KnownEncoderValues['**'][78][3][2][3][2][19600] = '--alt-preset extreme'; // 3.90.2, 3.91
189
+ $KnownEncoderValues['**'][78][3][1][3][2][19600] = '--alt-preset extreme'; // 3.90.3
190
+ $KnownEncoderValues['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme'; // 3.90, 3.90.1, 3.92
191
+ $KnownEncoderValues['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme'; // 3.90.2, 3.90.3, 3.91
192
+ $KnownEncoderValues['**'][78][3][2][3][4][19000] = '--alt-preset standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
193
+ $KnownEncoderValues['**'][78][3][1][3][4][19000] = '--alt-preset standard'; // 3.90.3
194
+ $KnownEncoderValues['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
195
+ $KnownEncoderValues['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3
196
+ $KnownEncoderValues['**'][88][4][1][3][3][19500] = '--r3mix'; // 3.90, 3.90.1, 3.92
197
+ $KnownEncoderValues['**'][88][4][1][3][3][19600] = '--r3mix'; // 3.90.2, 3.90.3, 3.91
198
+ $KnownEncoderValues['**'][67][4][1][3][4][18000] = '--r3mix'; // 3.94, 3.95
199
+ $KnownEncoderValues['**'][68][3][2][3][4][18000] = '--alt-preset medium'; // 3.90.3
200
+ $KnownEncoderValues['**'][68][4][2][3][4][18000] = '--alt-preset fast medium'; // 3.90.3
201
+
202
+ $KnownEncoderValues[0xFF][99][1][1][1][2][0] = '--preset studio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
203
+ $KnownEncoderValues[0xFF][58][2][1][3][2][20600] = '--preset studio'; // 3.90.3, 3.93.1
204
+ $KnownEncoderValues[0xFF][58][2][1][3][2][20500] = '--preset studio'; // 3.93
205
+ $KnownEncoderValues[0xFF][57][2][1][3][4][20500] = '--preset studio'; // 3.94, 3.95
206
+ $KnownEncoderValues[0xC0][88][1][1][1][2][0] = '--preset cd'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
207
+ $KnownEncoderValues[0xC0][58][2][2][3][2][19600] = '--preset cd'; // 3.90.3, 3.93.1
208
+ $KnownEncoderValues[0xC0][58][2][2][3][2][19500] = '--preset cd'; // 3.93
209
+ $KnownEncoderValues[0xC0][57][2][1][3][4][19500] = '--preset cd'; // 3.94, 3.95
210
+ $KnownEncoderValues[0xA0][78][1][1][3][2][18000] = '--preset hifi'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
211
+ $KnownEncoderValues[0xA0][58][2][2][3][2][18000] = '--preset hifi'; // 3.90.3, 3.93, 3.93.1
212
+ $KnownEncoderValues[0xA0][57][2][1][3][4][18000] = '--preset hifi'; // 3.94, 3.95
213
+ $KnownEncoderValues[0x80][67][1][1][3][2][18000] = '--preset tape'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
214
+ $KnownEncoderValues[0x80][67][1][1][3][2][15000] = '--preset radio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
215
+ $KnownEncoderValues[0x70][67][1][1][3][2][15000] = '--preset fm'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
216
+ $KnownEncoderValues[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm'; // 3.90.3, 3.93, 3.93.1
217
+ $KnownEncoderValues[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm'; // 3.94, 3.95
218
+ $KnownEncoderValues[0x38][58][2][2][0][2][10000] = '--preset voice'; // 3.90.3, 3.93, 3.93.1
219
+ $KnownEncoderValues[0x38][57][2][1][0][4][15000] = '--preset voice'; // 3.94, 3.95
220
+ $KnownEncoderValues[0x38][57][2][1][0][4][16000] = '--preset voice'; // 3.94a14
221
+ $KnownEncoderValues[0x28][65][1][1][0][2][7500] = '--preset mw-us'; // 3.90, 3.90.1, 3.92
222
+ $KnownEncoderValues[0x28][65][1][1][0][2][7600] = '--preset mw-us'; // 3.90.2, 3.91
223
+ $KnownEncoderValues[0x28][58][2][2][0][2][7000] = '--preset mw-us'; // 3.90.3, 3.93, 3.93.1
224
+ $KnownEncoderValues[0x28][57][2][1][0][4][10500] = '--preset mw-us'; // 3.94, 3.95
225
+ $KnownEncoderValues[0x28][57][2][1][0][4][11200] = '--preset mw-us'; // 3.94a14
226
+ $KnownEncoderValues[0x28][57][2][1][0][4][8800] = '--preset mw-us'; // 3.94a15
227
+ $KnownEncoderValues[0x18][58][2][2][0][2][4000] = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1
228
+ $KnownEncoderValues[0x18][58][2][2][0][2][3900] = '--preset phon+/lw/mw-eu/sw'; // 3.93
229
+ $KnownEncoderValues[0x18][57][2][1][0][4][5900] = '--preset phon+/lw/mw-eu/sw'; // 3.94, 3.95
230
+ $KnownEncoderValues[0x18][57][2][1][0][4][6200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a14
231
+ $KnownEncoderValues[0x18][57][2][1][0][4][3200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a15
232
+ $KnownEncoderValues[0x10][58][2][2][0][2][3800] = '--preset phone'; // 3.90.3, 3.93.1
233
+ $KnownEncoderValues[0x10][58][2][2][0][2][3700] = '--preset phone'; // 3.93
234
+ $KnownEncoderValues[0x10][57][2][1][0][4][5600] = '--preset phone'; // 3.94, 3.95
235
+ }
236
+
237
+ if (isset($KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
238
+
239
+ $encoder_options = $KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
240
+
241
+ } elseif (isset($KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
242
+
243
+ $encoder_options = $KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
244
+
245
+ } elseif ($ThisFileInfo['audio']['bitrate_mode'] == 'vbr') {
246
+
247
+ // http://gabriel.mp3-tech.org/mp3infotag.html
248
+ // int Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h
249
+
250
+
251
+ $LAME_V_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10);
252
+ $LAME_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($LAME_V_value * 10);
253
+ $encoder_options = '-V'.$LAME_V_value.' -q'.$LAME_q_value;
254
+
255
+ } elseif ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr') {
256
+
257
+ $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']).ceil($ThisFileInfo['audio']['bitrate'] / 1000);
258
+
259
+ } else {
260
+
261
+ $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']);
262
+
263
+ }
264
+
265
+ } elseif (!empty($thisfile_mpeg_audio_lame['bitrate_abr'])) {
266
+
267
+ $encoder_options = 'ABR'.$thisfile_mpeg_audio_lame['bitrate_abr'];
268
+
269
+ } elseif (!empty($ThisFileInfo['audio']['bitrate'])) {
270
+
271
+ if ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr') {
272
+ $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']).ceil($ThisFileInfo['audio']['bitrate'] / 1000);
273
+ } else {
274
+ $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']);
275
+ }
276
+
277
+ }
278
+ if (!empty($thisfile_mpeg_audio_lame['bitrate_min'])) {
279
+ $encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min'];
280
+ }
281
+
282
+ if (@$thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] || @$thisfile_mpeg_audio_lame['encoding_flags']['nogap_next']) {
283
+ $encoder_options .= ' --nogap';
284
+ }
285
+
286
+ if (!empty($thisfile_mpeg_audio_lame['lowpass_frequency'])) {
287
+ $ExplodedOptions = explode(' ', $encoder_options, 4);
288
+ if ($ExplodedOptions[0] == '--r3mix') {
289
+ $ExplodedOptions[1] = 'r3mix';
290
+ }
291
+ switch ($ExplodedOptions[0]) {
292
+ case '--preset':
293
+ case '--alt-preset':
294
+ case '--r3mix':
295
+ if ($ExplodedOptions[1] == 'fast') {
296
+ $ExplodedOptions[1] .= ' '.$ExplodedOptions[2];
297
+ }
298
+ switch ($ExplodedOptions[1]) {
299
+ case 'portable':
300
+ case 'medium':
301
+ case 'standard':
302
+ case 'extreme':
303
+ case 'insane':
304
+ case 'fast portable':
305
+ case 'fast medium':
306
+ case 'fast standard':
307
+ case 'fast extreme':
308
+ case 'fast insane':
309
+ case 'r3mix':
310
+ static $ExpectedLowpass = array(
311
+ 'insane|20500' => 20500,
312
+ 'insane|20600' => 20600, // 3.90.2, 3.90.3, 3.91
313
+ 'medium|18000' => 18000,
314
+ 'fast medium|18000' => 18000,
315
+ 'extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95
316
+ 'extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1
317
+ 'fast extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95
318
+ 'fast extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1
319
+ 'standard|19000' => 19000,
320
+ 'fast standard|19000' => 19000,
321
+ 'r3mix|19500' => 19500, // 3.90, 3.90.1, 3.92
322
+ 'r3mix|19600' => 19600, // 3.90.2, 3.90.3, 3.91
323
+ 'r3mix|18000' => 18000, // 3.94, 3.95
324
+ );
325
+ if (!isset($ExpectedLowpass[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio_lame['lowpass_frequency']]) && ($thisfile_mpeg_audio_lame['lowpass_frequency'] < 22050) && (round($thisfile_mpeg_audio_lame['lowpass_frequency'] / 1000) < round($thisfile_mpeg_audio['sample_rate'] / 2000))) {
326
+ $encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency'];
327
+ }
328
+ break;
329
+
330
+ default:
331
+ break;
332
+ }
333
+ break;
334
+ }
335
+ }
336
+
337
+ if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])) {
338
+ if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1)) {
339
+ $encoder_options .= ' --resample 44100';
340
+ } elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2)) {
341
+ $encoder_options .= ' --resample 48000';
342
+ } elseif ($thisfile_mpeg_audio['sample_rate'] < 44100) {
343
+ switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq']) {
344
+ case 0: // <= 32000
345
+ // may or may not be same as source frequency - ignore
346
+ break;
347
+ case 1: // 44100
348
+ case 2: // 48000
349
+ case 3: // 48000+
350
+ $ExplodedOptions = explode(' ', $encoder_options, 4);
351
+ switch ($ExplodedOptions[0]) {
352
+ case '--preset':
353
+ case '--alt-preset':
354
+ switch ($ExplodedOptions[1]) {
355
+ case 'fast':
356
+ case 'portable':
357
+ case 'medium':
358
+ case 'standard':
359
+ case 'extreme':
360
+ case 'insane':
361
+ $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
362
+ break;
363
+
364
+ default:
365
+ static $ExpectedResampledRate = array(
366
+ 'phon+/lw/mw-eu/sw|16000' => 16000,
367
+ 'mw-us|24000' => 24000, // 3.95
368
+ 'mw-us|32000' => 32000, // 3.93
369
+ 'mw-us|16000' => 16000, // 3.92
370
+ 'phone|16000' => 16000,
371
+ 'phone|11025' => 11025, // 3.94a15
372
+ 'radio|32000' => 32000, // 3.94a15
373
+ 'fm/radio|32000' => 32000, // 3.92
374
+ 'fm|32000' => 32000, // 3.90
375
+ 'voice|32000' => 32000);
376
+ if (!isset($ExpectedResampledRate[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio['sample_rate']])) {
377
+ $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
378
+ }
379
+ break;
380
+ }
381
+ break;
382
+
383
+ case '--r3mix':
384
+ default:
385
+ $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
386
+ break;
387
+ }
388
+ break;
389
+ }
390
+ }
391
+ }
392
+ if (empty($encoder_options) && !empty($ThisFileInfo['audio']['bitrate']) && !empty($ThisFileInfo['audio']['bitrate_mode'])) {
393
+ //$encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']).ceil($ThisFileInfo['audio']['bitrate'] / 1000);
394
+ $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']);
395
+ }
396
+
397
+ return $encoder_options;
398
+ }
399
+
400
+
401
+ function decodeMPEGaudioHeader($fd, $offset, &$ThisFileInfo, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) {
402
+
403
+ static $MPEGaudioVersionLookup;
404
+ static $MPEGaudioLayerLookup;
405
+ static $MPEGaudioBitrateLookup;
406
+ static $MPEGaudioFrequencyLookup;
407
+ static $MPEGaudioChannelModeLookup;
408
+ static $MPEGaudioModeExtensionLookup;
409
+ static $MPEGaudioEmphasisLookup;
410
+ if (empty($MPEGaudioVersionLookup)) {
411
+ $MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray();
412
+ $MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray();
413
+ $MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray();
414
+ $MPEGaudioFrequencyLookup = getid3_mp3::MPEGaudioFrequencyArray();
415
+ $MPEGaudioChannelModeLookup = getid3_mp3::MPEGaudioChannelModeArray();
416
+ $MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray();
417
+ $MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray();
418
+ }
419
+
420
+ if ($offset >= $ThisFileInfo['avdataend']) {
421
+ $ThisFileInfo['error'][] = 'end of file encounter looking for MPEG synch';
422
+ return false;
423
+ }
424
+ fseek($fd, $offset, SEEK_SET);
425
+ //$headerstring = fread($fd, 1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame
426
+ $headerstring = fread($fd, 226); // LAME header at offset 36 + 190 bytes of Xing/LAME data
427
+
428
+ // MP3 audio frame structure:
429
+ // $aa $aa $aa $aa [$bb $bb] $cc...
430
+ // where $aa..$aa is the four-byte mpeg-audio header (below)
431
+ // $bb $bb is the optional 2-byte CRC
432
+ // and $cc... is the audio data
433
+
434
+ $head4 = substr($headerstring, 0, 4);
435
+
436
+ static $MPEGaudioHeaderDecodeCache = array();
437
+ if (isset($MPEGaudioHeaderDecodeCache[$head4])) {
438
+ $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4];
439
+ } else {
440
+ $MPEGheaderRawArray = getid3_mp3::MPEGaudioHeaderDecode($head4);
441
+ $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray;
442
+ }
443
+
444
+ static $MPEGaudioHeaderValidCache = array();
445
+
446
+ // Not in cache
447
+ if (!isset($MPEGaudioHeaderValidCache[$head4])) {
448
+ //$MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true); // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1)
449
+ $MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false);
450
+ }
451
+
452
+ // shortcut
453
+ if (!isset($ThisFileInfo['mpeg']['audio'])) {
454
+ $ThisFileInfo['mpeg']['audio'] = array();
455
+ }
456
+ $thisfile_mpeg_audio = &$ThisFileInfo['mpeg']['audio'];
457
+
458
+
459
+ if ($MPEGaudioHeaderValidCache[$head4]) {
460
+ $thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray;
461
+ } else {
462
+ $ThisFileInfo['error'][] = 'Invalid MPEG audio header at offset '.$offset;
463
+ return false;
464
+ }
465
+
466
+ if (!$FastMPEGheaderScan) {
467
+
468
+ $thisfile_mpeg_audio['version'] = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']];
469
+ $thisfile_mpeg_audio['layer'] = $MPEGaudioLayerLookup[$thisfile_mpeg_audio['raw']['layer']];
470
+
471
+ $thisfile_mpeg_audio['channelmode'] = $MPEGaudioChannelModeLookup[$thisfile_mpeg_audio['raw']['channelmode']];
472
+ $thisfile_mpeg_audio['channels'] = (($thisfile_mpeg_audio['channelmode'] == 'mono') ? 1 : 2);
473
+ $thisfile_mpeg_audio['sample_rate'] = $MPEGaudioFrequencyLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['raw']['sample_rate']];
474
+ $thisfile_mpeg_audio['protection'] = !$thisfile_mpeg_audio['raw']['protection'];
475
+ $thisfile_mpeg_audio['private'] = (bool) $thisfile_mpeg_audio['raw']['private'];
476
+ $thisfile_mpeg_audio['modeextension'] = $MPEGaudioModeExtensionLookup[$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['modeextension']];
477
+ $thisfile_mpeg_audio['copyright'] = (bool) $thisfile_mpeg_audio['raw']['copyright'];
478
+ $thisfile_mpeg_audio['original'] = (bool) $thisfile_mpeg_audio['raw']['original'];
479
+ $thisfile_mpeg_audio['emphasis'] = $MPEGaudioEmphasisLookup[$thisfile_mpeg_audio['raw']['emphasis']];
480
+
481
+ $ThisFileInfo['audio']['channels'] = $thisfile_mpeg_audio['channels'];
482
+ $ThisFileInfo['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate'];
483
+
484
+ if ($thisfile_mpeg_audio['protection']) {
485
+ $thisfile_mpeg_audio['crc'] = getid3_lib::BigEndian2Int(substr($headerstring, 4, 2));
486
+ }
487
+
488
+ }
489
+
490
+ if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) {
491
+ // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0
492
+ $ThisFileInfo['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1';
493
+ $thisfile_mpeg_audio['raw']['bitrate'] = 0;
494
+ }
495
+ $thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding'];
496
+ $thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']];
497
+
498
+ if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $ThisFileInfo['avdataoffset'])) {
499
+ // only skip multiple frame check if free-format bitstream found at beginning of file
500
+ // otherwise is quite possibly simply corrupted data
501
+ $recursivesearch = false;
502
+ }
503
+
504
+ // For Layer 2 there are some combinations of bitrate and mode which are not allowed.
505
+ if (!$FastMPEGheaderScan && ($thisfile_mpeg_audio['layer'] == '2')) {
506
+
507
+ $ThisFileInfo['audio']['dataformat'] = 'mp2';
508
+ switch ($thisfile_mpeg_audio['channelmode']) {
509
+
510
+ case 'mono':
511
+ if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) {
512
+ // these are ok
513
+ } else {
514
+ $ThisFileInfo['error'][] = $thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
515
+ return false;
516
+ }
517
+ break;
518
+
519
+ case 'stereo':
520
+ case 'joint stereo':
521
+ case 'dual channel':
522
+ if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) {
523
+ // these are ok
524
+ } else {
525
+ $ThisFileInfo['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
526
+ return false;
527
+ }
528
+ break;
529
+
530
+ }
531
+
532
+ }
533
+
534
+
535
+ if ($ThisFileInfo['audio']['sample_rate'] > 0) {
536
+ $thisfile_mpeg_audio['framelength'] = getid3_mp3::MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'], $thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['layer'], (int) $thisfile_mpeg_audio['padding'], $ThisFileInfo['audio']['sample_rate']);
537
+ }
538
+
539
+ $nextframetestoffset = $offset + 1;
540
+ if ($thisfile_mpeg_audio['bitrate'] != 'free') {
541
+
542
+ $ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
543
+
544
+ if (isset($thisfile_mpeg_audio['framelength'])) {
545
+ $nextframetestoffset = $offset + $thisfile_mpeg_audio['framelength'];
546
+ } else {
547
+ $ThisFileInfo['error'][] = 'Frame at offset('.$offset.') is has an invalid frame length.';
548
+ return false;
549
+ }
550
+
551
+ }
552
+
553
+ $ExpectedNumberOfAudioBytes = 0;
554
+
555
+ ////////////////////////////////////////////////////////////////////////////////////
556
+ // Variable-bitrate headers
557
+
558
+ if (substr($headerstring, 4 + 32, 4) == 'VBRI') {
559
+ // Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36)
560
+ // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
561
+
562
+ $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
563
+ $thisfile_mpeg_audio['VBR_method'] = 'Fraunhofer';
564
+ $ThisFileInfo['audio']['codec'] = 'Fraunhofer';
565
+
566
+ $SideInfoData = substr($headerstring, 4 + 2, 32);
567
+
568
+ $FraunhoferVBROffset = 36;
569
+
570
+ $thisfile_mpeg_audio['VBR_encoder_version'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 4, 2)); // VbriVersion
571
+ $thisfile_mpeg_audio['VBR_encoder_delay'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 6, 2)); // VbriDelay
572
+ $thisfile_mpeg_audio['VBR_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 8, 2)); // VbriQuality
573
+ $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 10, 4)); // VbriStreamBytes
574
+ $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 14, 4)); // VbriStreamFrames
575
+ $thisfile_mpeg_audio['VBR_seek_offsets'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 18, 2)); // VbriTableSize
576
+ $thisfile_mpeg_audio['VBR_seek_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 2)); // VbriTableScale
577
+ $thisfile_mpeg_audio['VBR_entry_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 22, 2)); // VbriEntryBytes
578
+ $thisfile_mpeg_audio['VBR_entry_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 24, 2)); // VbriEntryFrames
579
+
580
+ $ExpectedNumberOfAudioBytes = $thisfile_mpeg_audio['VBR_bytes'];
581
+
582
+ $previousbyteoffset = $offset;
583
+ for ($i = 0; $i < $thisfile_mpeg_audio['VBR_seek_offsets']; $i++) {
584
+ $Fraunhofer_OffsetN = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, $thisfile_mpeg_audio['VBR_entry_bytes']));
585
+ $FraunhoferVBROffset += $thisfile_mpeg_audio['VBR_entry_bytes'];
586
+ $thisfile_mpeg_audio['VBR_offsets_relative'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']);
587
+ $thisfile_mpeg_audio['VBR_offsets_absolute'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']) + $previousbyteoffset;
588
+ $previousbyteoffset += $Fraunhofer_OffsetN;
589
+ }
590
+
591
+
592
+ } else {
593
+
594
+ // Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36)
595
+ // depending on MPEG layer and number of channels
596
+
597
+ $VBRidOffset = getid3_mp3::XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']);
598
+ $SideInfoData = substr($headerstring, 4 + 2, $VBRidOffset - 4);
599
+
600
+ if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info')) {
601
+ // 'Xing' is traditional Xing VBR frame
602
+ // 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.)
603
+ // 'Info' *can* legally be used to specify a VBR file as well, however.
604
+
605
+ // http://www.multiweb.cz/twoinches/MP3inside.htm
606
+ //00..03 = "Xing" or "Info"
607
+ //04..07 = Flags:
608
+ // 0x01 Frames Flag set if value for number of frames in file is stored
609
+ // 0x02 Bytes Flag set if value for filesize in bytes is stored
610
+ // 0x04 TOC Flag set if values for TOC are stored
611
+ // 0x08 VBR Scale Flag set if values for VBR scale is stored
612
+ //08..11 Frames: Number of frames in file (including the first Xing/Info one)
613
+ //12..15 Bytes: File length in Bytes
614
+ //16..115 TOC (Table of Contents):
615
+ // Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately solves problem with moving inside file.
616
+ // Each Byte has a value according this formula:
617
+ // (TOC[i] / 256) * fileLenInBytes
618
+ // So if song lasts eg. 240 sec. and you want to jump to 60. sec. (and file is 5 000 000 Bytes length) you can use:
619
+ // TOC[(60/240)*100] = TOC[25]
620
+ // and corresponding Byte in file is then approximately at:
621
+ // (TOC[25]/256) * 5000000
622
+ //116..119 VBR Scale
623
+
624
+
625
+ // should be safe to leave this at 'vbr' and let it be overriden to 'cbr' if a CBR preset/mode is used by LAME
626
+ // if (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Xing') {
627
+ $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
628
+ $thisfile_mpeg_audio['VBR_method'] = 'Xing';
629
+ // } else {
630
+ // $ScanAsCBR = true;
631
+ // $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
632
+ // }
633
+
634
+ $thisfile_mpeg_audio['xing_flags_raw'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 4, 4));
635
+
636
+ $thisfile_mpeg_audio['xing_flags']['frames'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000001);
637
+ $thisfile_mpeg_audio['xing_flags']['bytes'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000002);
638
+ $thisfile_mpeg_audio['xing_flags']['toc'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000004);
639
+ $thisfile_mpeg_audio['xing_flags']['vbr_scale'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000008);
640
+
641
+ if ($thisfile_mpeg_audio['xing_flags']['frames']) {
642
+ $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 8, 4));
643
+ //$thisfile_mpeg_audio['VBR_frames']--; // don't count header Xing/Info frame
644
+ }
645
+ if ($thisfile_mpeg_audio['xing_flags']['bytes']) {
646
+ $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4));
647
+ }
648
+
649
+ //if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
650
+ if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
651
+
652
+ $framelengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames'];
653
+
654
+ if ($thisfile_mpeg_audio['layer'] == '1') {
655
+ // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
656
+ //$ThisFileInfo['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
657
+ $ThisFileInfo['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $ThisFileInfo['audio']['channels']) / 12;
658
+ } else {
659
+ // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
660
+ //$ThisFileInfo['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
661
+ $ThisFileInfo['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $ThisFileInfo['audio']['channels']) / 144;
662
+ }
663
+ $thisfile_mpeg_audio['framelength'] = floor($framelengthfloat);
664
+ }
665
+
666
+ if ($thisfile_mpeg_audio['xing_flags']['toc']) {
667
+ $LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
668
+ for ($i = 0; $i < 100; $i++) {
669
+ $thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i});
670
+ }
671
+ }
672
+ if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) {
673
+ $thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4));
674
+ }
675
+
676
+
677
+ // http://gabriel.mp3-tech.org/mp3infotag.html
678
+ if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') {
679
+
680
+ // shortcut
681
+ $thisfile_mpeg_audio['LAME'] = array();
682
+ $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
683
+
684
+
685
+ $thisfile_mpeg_audio_lame['long_version'] = substr($headerstring, $VBRidOffset + 120, 20);
686
+ $thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9);
687
+
688
+ if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') {
689
+
690
+ // extra 11 chars are not part of version string when LAMEtag present
691
+ unset($thisfile_mpeg_audio_lame['long_version']);
692
+
693
+ // It the LAME tag was only introduced in LAME v3.90
694
+ // http://www.hydrogenaudio.org/?act=ST&f=15&t=9933
695
+
696
+ // Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html
697
+ // are assuming a 'Xing' identifier offset of 0x24, which is the case for
698
+ // MPEG-1 non-mono, but not for other combinations
699
+ $LAMEtagOffsetContant = $VBRidOffset - 0x24;
700
+
701
+ // shortcuts
702
+ $thisfile_mpeg_audio_lame['RGAD'] = array('track'=>array(), 'album'=>array());
703
+ $thisfile_mpeg_audio_lame_RGAD = &$thisfile_mpeg_audio_lame['RGAD'];
704
+ $thisfile_mpeg_audio_lame_RGAD_track = &$thisfile_mpeg_audio_lame_RGAD['track'];
705
+ $thisfile_mpeg_audio_lame_RGAD_album = &$thisfile_mpeg_audio_lame_RGAD['album'];
706
+ $thisfile_mpeg_audio_lame['raw'] = array();
707
+ $thisfile_mpeg_audio_lame_raw = &$thisfile_mpeg_audio_lame['raw'];
708
+
709
+ // byte $9B VBR Quality
710
+ // This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications.
711
+ // Actually overwrites original Xing bytes
712
+ unset($thisfile_mpeg_audio['VBR_scale']);
713
+ $thisfile_mpeg_audio_lame['vbr_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9B, 1));
714
+
715
+ // bytes $9C-$A4 Encoder short VersionString
716
+ $thisfile_mpeg_audio_lame['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9C, 9);
717
+
718
+ // byte $A5 Info Tag revision + VBR method
719
+ $LAMEtagRevisionVBRmethod = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA5, 1));
720
+
721
+ $thisfile_mpeg_audio_lame['tag_revision'] = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4;
722
+ $thisfile_mpeg_audio_lame_raw['vbr_method'] = $LAMEtagRevisionVBRmethod & 0x0F;
723
+ $thisfile_mpeg_audio_lame['vbr_method'] = getid3_mp3::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']);
724
+ $thisfile_mpeg_audio['bitrate_mode'] = substr($thisfile_mpeg_audio_lame['vbr_method'], 0, 3); // usually either 'cbr' or 'vbr', but truncates 'vbr-old / vbr-rh' to 'vbr'
725
+
726
+ // byte $A6 Lowpass filter value
727
+ $thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100;
728
+
729
+ // bytes $A7-$AE Replay Gain
730
+ // http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html
731
+ // bytes $A7-$AA : 32 bit floating point "Peak signal amplitude"
732
+ if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') {
733
+ // LAME 3.94a16 and later - 9.23 fixed point
734
+ // ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375
735
+ $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = (float) ((getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4))) / 8388608);
736
+ } else {
737
+ // LAME 3.94a15 and earlier - 32-bit floating point
738
+ // Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15
739
+ $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = getid3_lib::LittleEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4));
740
+ }
741
+ if ($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] == 0) {
742
+ unset($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
743
+ } else {
744
+ $thisfile_mpeg_audio_lame_RGAD['peak_db'] = getid3_lib::RGADamplitude2dB($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
745
+ }
746
+
747
+ $thisfile_mpeg_audio_lame_raw['RGAD_track'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAB, 2));
748
+ $thisfile_mpeg_audio_lame_raw['RGAD_album'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAD, 2));
749
+
750
+
751
+ if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0) {
752
+
753
+ $thisfile_mpeg_audio_lame_RGAD_track['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13;
754
+ $thisfile_mpeg_audio_lame_RGAD_track['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10;
755
+ $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9;
756
+ $thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF;
757
+ $thisfile_mpeg_audio_lame_RGAD_track['name'] = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['name']);
758
+ $thisfile_mpeg_audio_lame_RGAD_track['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']);
759
+ $thisfile_mpeg_audio_lame_RGAD_track['gain_db'] = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']);
760
+
761
+ if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
762
+ $ThisFileInfo['replay_gain']['track']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
763
+ }
764
+ $ThisFileInfo['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator'];
765
+ $ThisFileInfo['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db'];
766
+ } else {
767
+ unset($thisfile_mpeg_audio_lame_RGAD['track']);
768
+ }
769
+ if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0) {
770
+
771
+ $thisfile_mpeg_audio_lame_RGAD_album['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13;
772
+ $thisfile_mpeg_audio_lame_RGAD_album['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10;
773
+ $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9;
774
+ $thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF;
775
+ $thisfile_mpeg_audio_lame_RGAD_album['name'] = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['name']);
776
+ $thisfile_mpeg_audio_lame_RGAD_album['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']);
777
+ $thisfile_mpeg_audio_lame_RGAD_album['gain_db'] = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']);
778
+
779
+ if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
780
+ $ThisFileInfo['replay_gain']['album']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
781
+ }
782
+ $ThisFileInfo['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator'];
783
+ $ThisFileInfo['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db'];
784
+ } else {
785
+ unset($thisfile_mpeg_audio_lame_RGAD['album']);
786
+ }
787
+ if (empty($thisfile_mpeg_audio_lame_RGAD)) {
788
+ unset($thisfile_mpeg_audio_lame['RGAD']);
789
+ }
790
+
791
+
792
+ // byte $AF Encoding flags + ATH Type
793
+ $EncodingFlagsATHtype = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAF, 1));
794
+ $thisfile_mpeg_audio_lame['encoding_flags']['nspsytune'] = (bool) ($EncodingFlagsATHtype & 0x10);
795
+ $thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20);
796
+ $thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'] = (bool) ($EncodingFlagsATHtype & 0x40);
797
+ $thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] = (bool) ($EncodingFlagsATHtype & 0x80);
798
+ $thisfile_mpeg_audio_lame['ath_type'] = $EncodingFlagsATHtype & 0x0F;
799
+
800
+ // byte $B0 if ABR {specified bitrate} else {minimal bitrate}
801
+ $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB0, 1));
802
+ if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2) { // Average BitRate (ABR)
803
+ $thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
804
+ } elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { // Constant BitRate (CBR)
805
+ // ignore
806
+ } elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0) { // Variable BitRate (VBR) - minimum bitrate
807
+ $thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
808
+ }
809
+
810
+ // bytes $B1-$B3 Encoder delays
811
+ $EncoderDelays = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB1, 3));
812
+ $thisfile_mpeg_audio_lame['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12;
813
+ $thisfile_mpeg_audio_lame['end_padding'] = $EncoderDelays & 0x000FFF;
814
+
815
+ // byte $B4 Misc
816
+ $MiscByte = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB4, 1));
817
+ $thisfile_mpeg_audio_lame_raw['noise_shaping'] = ($MiscByte & 0x03);
818
+ $thisfile_mpeg_audio_lame_raw['stereo_mode'] = ($MiscByte & 0x1C) >> 2;
819
+ $thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5;
820
+ $thisfile_mpeg_audio_lame_raw['source_sample_freq'] = ($MiscByte & 0xC0) >> 6;
821
+ $thisfile_mpeg_audio_lame['noise_shaping'] = $thisfile_mpeg_audio_lame_raw['noise_shaping'];
822
+ $thisfile_mpeg_audio_lame['stereo_mode'] = getid3_mp3::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']);
823
+ $thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality'];
824
+ $thisfile_mpeg_audio_lame['source_sample_freq'] = getid3_mp3::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']);
825
+
826
+ // byte $B5 MP3 Gain
827
+ $thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true);
828
+ $thisfile_mpeg_audio_lame['mp3_gain_db'] = (getid3_lib::RGADamplitude2dB(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain'];
829
+ $thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6));
830
+
831
+ // bytes $B6-$B7 Preset and surround info
832
+ $PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2));
833
+ // Reserved = ($PresetSurroundBytes & 0xC000);
834
+ $thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800);
835
+ $thisfile_mpeg_audio_lame['surround_info'] = getid3_mp3::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']);
836
+ $thisfile_mpeg_audio_lame['preset_used_id'] = ($PresetSurroundBytes & 0x07FF);
837
+ $thisfile_mpeg_audio_lame['preset_used'] = getid3_mp3::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame);
838
+ if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) {
839
+ $ThisFileInfo['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org';
840
+ }
841
+ if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) {
842
+ // this may change if 3.90.4 ever comes out
843
+ $thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3';
844
+ }
845
+
846
+ // bytes $B8-$BB MusicLength
847
+ $thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB8, 4));
848
+ $ExpectedNumberOfAudioBytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']);
849
+
850
+ // bytes $BC-$BD MusicCRC
851
+ $thisfile_mpeg_audio_lame['music_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBC, 2));
852
+
853
+ // bytes $BE-$BF CRC-16 of Info Tag
854
+ $thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBE, 2));
855
+
856
+
857
+ // LAME CBR
858
+ if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) {
859
+
860
+ $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
861
+ $thisfile_mpeg_audio['bitrate'] = getid3_mp3::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']);
862
+ $ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
863
+ //if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) {
864
+ // $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min'];
865
+ //}
866
+
867
+ }
868
+
869
+ }
870
+ }
871
+
872
+ } else {
873
+
874
+ // not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header)
875
+ $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
876
+ if ($recursivesearch) {
877
+ $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
878
+ if (getid3_mp3::RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, true)) {
879
+ $recursivesearch = false;
880
+ $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
881
+ }
882
+ if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') {
883
+ $ThisFileInfo['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.';
884
+ }
885
+ }
886
+
887
+ }
888
+
889
+ }
890
+
891
+ if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']))) {
892
+ if ($ExpectedNumberOfAudioBytes > ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) {
893
+ if (($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) == 1) {
894
+ $ThisFileInfo['warning'][] = 'Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)';
895
+ } else {
896
+ $ThisFileInfo['warning'][] = 'Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])).' bytes)';
897
+ }
898
+ } else {
899
+ if ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) {
900
+ // $prenullbytefileoffset = ftell($fd);
901
+ // fseek($fd, $ThisFileInfo['avdataend'], SEEK_SET);
902
+ // $PossibleNullByte = fread($fd, 1);
903
+ // fseek($fd, $prenullbytefileoffset, SEEK_SET);
904
+ // if ($PossibleNullByte === "\x00") {
905
+ $ThisFileInfo['avdataend']--;
906
+ // $ThisFileInfo['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
907
+ // } else {
908
+ // $ThisFileInfo['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' ('.(($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
909
+ // }
910
+ } else {
911
+ $ThisFileInfo['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' ('.(($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
912
+ }
913
+ }
914
+ }
915
+
916
+ if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($ThisFileInfo['audio']['bitrate'])) {
917
+ if (($offset == $ThisFileInfo['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) {
918
+ $framebytelength = getid3_mp3::FreeFormatFrameLength($fd, $offset, $ThisFileInfo, true);
919
+ if ($framebytelength > 0) {
920
+ $thisfile_mpeg_audio['framelength'] = $framebytelength;
921
+ if ($thisfile_mpeg_audio['layer'] == '1') {
922
+ // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
923
+ $ThisFileInfo['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
924
+ } else {
925
+ // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
926
+ $ThisFileInfo['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
927
+ }
928
+ } else {
929
+ $ThisFileInfo['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header';
930
+ }
931
+ }
932
+ }
933
+
934
+ if (@$thisfile_mpeg_audio['VBR_frames']) {
935
+ switch ($thisfile_mpeg_audio['bitrate_mode']) {
936
+ case 'vbr':
937
+ case 'abr':
938
+ if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1)) {
939
+ $thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 384);
940
+ } elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) {
941
+ $thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 576);
942
+ } else {
943
+ $thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 1152);
944
+ }
945
+ if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) {
946
+ $ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate'];
947
+ $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion
948
+ }
949
+ break;
950
+ }
951
+ }
952
+
953
+ // End variable-bitrate headers
954
+ ////////////////////////////////////////////////////////////////////////////////////
955
+
956
+ if ($recursivesearch) {
957
+
958
+ if (!getid3_mp3::RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, $ScanAsCBR)) {
959
+ return false;
960
+ }
961
+
962
+ }
963
+
964
+
965
+ //if (false) {
966
+ // // experimental side info parsing section - not returning anything useful yet
967
+ //
968
+ // $SideInfoBitstream = getid3_lib::BigEndian2Bin($SideInfoData);
969
+ // $SideInfoOffset = 0;
970
+ //
971
+ // if ($thisfile_mpeg_audio['version'] == '1') {
972
+ // if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
973
+ // // MPEG-1 (mono)
974
+ // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
975
+ // $SideInfoOffset += 9;
976
+ // $SideInfoOffset += 5;
977
+ // } else {
978
+ // // MPEG-1 (stereo, joint-stereo, dual-channel)
979
+ // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
980
+ // $SideInfoOffset += 9;
981
+ // $SideInfoOffset += 3;
982
+ // }
983
+ // } else { // 2 or 2.5
984
+ // if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
985
+ // // MPEG-2, MPEG-2.5 (mono)
986
+ // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
987
+ // $SideInfoOffset += 8;
988
+ // $SideInfoOffset += 1;
989
+ // } else {
990
+ // // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel)
991
+ // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
992
+ // $SideInfoOffset += 8;
993
+ // $SideInfoOffset += 2;
994
+ // }
995
+ // }
996
+ //
997
+ // if ($thisfile_mpeg_audio['version'] == '1') {
998
+ // for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) {
999
+ // for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) {
1000
+ // $thisfile_mpeg_audio['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1001
+ // $SideInfoOffset += 2;
1002
+ // }
1003
+ // }
1004
+ // }
1005
+ // for ($granule = 0; $granule < (($thisfile_mpeg_audio['version'] == '1') ? 2 : 1); $granule++) {
1006
+ // for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) {
1007
+ // $thisfile_mpeg_audio['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12);
1008
+ // $SideInfoOffset += 12;
1009
+ // $thisfile_mpeg_audio['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
1010
+ // $SideInfoOffset += 9;
1011
+ // $thisfile_mpeg_audio['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8);
1012
+ // $SideInfoOffset += 8;
1013
+ // if ($thisfile_mpeg_audio['version'] == '1') {
1014
+ // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
1015
+ // $SideInfoOffset += 4;
1016
+ // } else {
1017
+ // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
1018
+ // $SideInfoOffset += 9;
1019
+ // }
1020
+ // $thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1021
+ // $SideInfoOffset += 1;
1022
+ //
1023
+ // if ($thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] == '1') {
1024
+ //
1025
+ // $thisfile_mpeg_audio['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2);
1026
+ // $SideInfoOffset += 2;
1027
+ // $thisfile_mpeg_audio['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1028
+ // $SideInfoOffset += 1;
1029
+ //
1030
+ // for ($region = 0; $region < 2; $region++) {
1031
+ // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
1032
+ // $SideInfoOffset += 5;
1033
+ // }
1034
+ // $thisfile_mpeg_audio['table_select'][$granule][$channel][2] = 0;
1035
+ //
1036
+ // for ($window = 0; $window < 3; $window++) {
1037
+ // $thisfile_mpeg_audio['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3);
1038
+ // $SideInfoOffset += 3;
1039
+ // }
1040
+ //
1041
+ // } else {
1042
+ //
1043
+ // for ($region = 0; $region < 3; $region++) {
1044
+ // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
1045
+ // $SideInfoOffset += 5;
1046
+ // }
1047
+ //
1048
+ // $thisfile_mpeg_audio['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
1049
+ // $SideInfoOffset += 4;
1050
+ // $thisfile_mpeg_audio['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3);
1051
+ // $SideInfoOffset += 3;
1052
+ // $thisfile_mpeg_audio['block_type'][$granule][$channel] = 0;
1053
+ // }
1054
+ //
1055
+ // if ($thisfile_mpeg_audio['version'] == '1') {
1056
+ // $thisfile_mpeg_audio['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1057
+ // $SideInfoOffset += 1;
1058
+ // }
1059
+ // $thisfile_mpeg_audio['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1060
+ // $SideInfoOffset += 1;
1061
+ // $thisfile_mpeg_audio['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1062
+ // $SideInfoOffset += 1;
1063
+ // }
1064
+ // }
1065
+ //}
1066
+
1067
+ return true;
1068
+ }
1069
+
1070
+ function RecursiveFrameScanning(&$fd, &$ThisFileInfo, &$offset, &$nextframetestoffset, $ScanAsCBR) {
1071
+ for ($i = 0; $i < GETID3_MP3_VALID_CHECK_FRAMES; $i++) {
1072
+ // check next GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch
1073
+ if (($nextframetestoffset + 4) >= $ThisFileInfo['avdataend']) {
1074
+ // end of file
1075
+ return true;
1076
+ }
1077
+
1078
+ $nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$ThisFileInfo['avdataend'], 'avdataoffset'=>$ThisFileInfo['avdataoffset']);
1079
+ if (getid3_mp3::decodeMPEGaudioHeader($fd, $nextframetestoffset, $nextframetestarray, false)) {
1080
+ if ($ScanAsCBR) {
1081
+ // force CBR mode, used for trying to pick out invalid audio streams with
1082
+ // valid(?) VBR headers, or VBR streams with no VBR header
1083
+ if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($ThisFileInfo['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $ThisFileInfo['mpeg']['audio']['bitrate'])) {
1084
+ return false;
1085
+ }
1086
+ }
1087
+
1088
+
1089
+ // next frame is OK, get ready to check the one after that
1090
+ if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) {
1091
+ $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength'];
1092
+ } else {
1093
+ $ThisFileInfo['error'][] = 'Frame at offset ('.$offset.') is has an invalid frame length.';
1094
+ return false;
1095
+ }
1096
+
1097
+ } else {
1098
+
1099
+ // next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence
1100
+ $ThisFileInfo['error'][] = 'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.';
1101
+
1102
+ return false;
1103
+ }
1104
+ }
1105
+ return true;
1106
+ }
1107
+
1108
+ function FreeFormatFrameLength($fd, $offset, &$ThisFileInfo, $deepscan=false) {
1109
+ fseek($fd, $offset, SEEK_SET);
1110
+ $MPEGaudioData = fread($fd, 32768);
1111
+
1112
+ $SyncPattern1 = substr($MPEGaudioData, 0, 4);
1113
+ // may be different pattern due to padding
1114
+ $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3};
1115
+ if ($SyncPattern2 === $SyncPattern1) {
1116
+ $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3};
1117
+ }
1118
+
1119
+ $framelength = false;
1120
+ $framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4);
1121
+ $framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4);
1122
+ if ($framelength1 > 4) {
1123
+ $framelength = $framelength1;
1124
+ }
1125
+ if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
1126
+ $framelength = $framelength2;
1127
+ }
1128
+ if (!$framelength) {
1129
+
1130
+ // LAME 3.88 has a different value for modeextension on the first frame vs the rest
1131
+ $framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3), 4);
1132
+ $framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3), 4);
1133
+
1134
+ if ($framelength1 > 4) {
1135
+ $framelength = $framelength1;
1136
+ }
1137
+ if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
1138
+ $framelength = $framelength2;
1139
+ }
1140
+ if (!$framelength) {
1141
+ $ThisFileInfo['error'][] = 'Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset;
1142
+ return false;
1143
+ } else {
1144
+ $ThisFileInfo['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)';
1145
+ $ThisFileInfo['audio']['codec'] = 'LAME';
1146
+ $ThisFileInfo['audio']['encoder'] = 'LAME3.88';
1147
+ $SyncPattern1 = substr($SyncPattern1, 0, 3);
1148
+ $SyncPattern2 = substr($SyncPattern2, 0, 3);
1149
+ }
1150
+ }
1151
+
1152
+ if ($deepscan) {
1153
+
1154
+ $ActualFrameLengthValues = array();
1155
+ $nextoffset = $offset + $framelength;
1156
+ while ($nextoffset < ($ThisFileInfo['avdataend'] - 6)) {
1157
+ fseek($fd, $nextoffset - 1, SEEK_SET);
1158
+ $NextSyncPattern = fread($fd, 6);
1159
+ if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) {
1160
+ // good - found where expected
1161
+ $ActualFrameLengthValues[] = $framelength;
1162
+ } elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 0, strlen($SyncPattern2)) == $SyncPattern2)) {
1163
+ // ok - found one byte earlier than expected (last frame wasn't padded, first frame was)
1164
+ $ActualFrameLengthValues[] = ($framelength - 1);
1165
+ $nextoffset--;
1166
+ } elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) {
1167
+ // ok - found one byte later than expected (last frame was padded, first frame wasn't)
1168
+ $ActualFrameLengthValues[] = ($framelength + 1);
1169
+ $nextoffset++;
1170
+ } else {
1171
+ $ThisFileInfo['error'][] = 'Did not find expected free-format sync pattern at offset '.$nextoffset;
1172
+ return false;
1173
+ }
1174
+ $nextoffset += $framelength;
1175
+ }
1176
+ if (count($ActualFrameLengthValues) > 0) {
1177
+ $framelength = intval(round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues)));
1178
+ }
1179
+ }
1180
+ return $framelength;
1181
+ }
1182
+
1183
+ function getOnlyMPEGaudioInfoBruteForce($fd, &$ThisFileInfo) {
1184
+
1185
+ $MPEGaudioHeaderDecodeCache = array();
1186
+ $MPEGaudioHeaderValidCache = array();
1187
+ $MPEGaudioHeaderLengthCache = array();
1188
+ $MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray();
1189
+ $MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray();
1190
+ $MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray();
1191
+ $MPEGaudioFrequencyLookup = getid3_mp3::MPEGaudioFrequencyArray();
1192
+ $MPEGaudioChannelModeLookup = getid3_mp3::MPEGaudioChannelModeArray();
1193
+ $MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray();
1194
+ $MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray();
1195
+ $LongMPEGversionLookup = array();
1196
+ $LongMPEGlayerLookup = array();
1197
+ $LongMPEGbitrateLookup = array();
1198
+ $LongMPEGpaddingLookup = array();
1199
+ $LongMPEGfrequencyLookup = array();
1200
+
1201
+ $Distribution['bitrate'] = array();
1202
+ $Distribution['frequency'] = array();
1203
+ $Distribution['layer'] = array();
1204
+ $Distribution['version'] = array();
1205
+ $Distribution['padding'] = array();
1206
+
1207
+ fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
1208
+
1209
+ $previousvalidframe = $ThisFileInfo['avdataoffset'];
1210
+ while (ftell($fd) < $ThisFileInfo['avdataend']) {
1211
+ set_time_limit(30);
1212
+ $head4 = fread($fd, 4);
1213
+ if (strlen($head4) < 4) {
1214
+ break;
1215
+ }
1216
+ if ($head4{0} != "\xFF") {
1217
+ for ($i = 1; $i < 4; $i++) {
1218
+ if ($head4{$i} == "\xFF") {
1219
+ fseek($fd, $i - 4, SEEK_CUR);
1220
+ continue 2;
1221
+ }
1222
+ }
1223
+ continue;
1224
+ }
1225
+ if (!isset($MPEGaudioHeaderDecodeCache[$head4])) {
1226
+ $MPEGaudioHeaderDecodeCache[$head4] = getid3_mp3::MPEGaudioHeaderDecode($head4);
1227
+ }
1228
+ if (!isset($MPEGaudioHeaderValidCache[$head4])) {
1229
+ $MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4], false, false);
1230
+ }
1231
+ if ($MPEGaudioHeaderValidCache[$head4]) {
1232
+
1233
+ if (!isset($MPEGaudioHeaderLengthCache[$head4])) {
1234
+ $LongMPEGversionLookup[$head4] = $MPEGaudioVersionLookup[$MPEGaudioHeaderDecodeCache[$head4]['version']];
1235
+ $LongMPEGlayerLookup[$head4] = $MPEGaudioLayerLookup[$MPEGaudioHeaderDecodeCache[$head4]['layer']];
1236
+ $LongMPEGbitrateLookup[$head4] = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']];
1237
+ $LongMPEGpaddingLookup[$head4] = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding'];
1238
+ $LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']];
1239
+ $MPEGaudioHeaderLengthCache[$head4] = getid3_mp3::MPEGaudioFrameLength(
1240
+ $LongMPEGbitrateLookup[$head4],
1241
+ $LongMPEGversionLookup[$head4],
1242
+ $LongMPEGlayerLookup[$head4],
1243
+ $LongMPEGpaddingLookup[$head4],
1244
+ $LongMPEGfrequencyLookup[$head4]);
1245
+ }
1246
+ if ($MPEGaudioHeaderLengthCache[$head4] > 4) {
1247
+ $WhereWeWere = ftell($fd);
1248
+ fseek($fd, $MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
1249
+ $next4 = fread($fd, 4);
1250
+ if ($next4{0} == "\xFF") {
1251
+ if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
1252
+ $MPEGaudioHeaderDecodeCache[$next4] = getid3_mp3::MPEGaudioHeaderDecode($next4);
1253
+ }
1254
+ if (!isset($MPEGaudioHeaderValidCache[$next4])) {
1255
+ $MPEGaudioHeaderValidCache[$next4] = getid3_mp3::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false);
1256
+ }
1257
+ if ($MPEGaudioHeaderValidCache[$next4]) {
1258
+ fseek($fd, -4, SEEK_CUR);
1259
+
1260
+ @$Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]++;
1261
+ @$Distribution['layer'][$LongMPEGlayerLookup[$head4]]++;
1262
+ @$Distribution['version'][$LongMPEGversionLookup[$head4]]++;
1263
+ @$Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]++;
1264
+ @$Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]++;
1265
+ continue;
1266
+ }
1267
+ }
1268
+ unset($next4);
1269
+ fseek($fd, $WhereWeWere - 3, SEEK_SET);
1270
+ }
1271
+
1272
+ }
1273
+ }
1274
+ foreach ($Distribution as $key => $value) {
1275
+ ksort($Distribution[$key], SORT_NUMERIC);
1276
+ }
1277
+ ksort($Distribution['version'], SORT_STRING);
1278
+ $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = $Distribution['bitrate'];
1279
+ $ThisFileInfo['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency'];
1280
+ $ThisFileInfo['mpeg']['audio']['layer_distribution'] = $Distribution['layer'];
1281
+ $ThisFileInfo['mpeg']['audio']['version_distribution'] = $Distribution['version'];
1282
+ $ThisFileInfo['mpeg']['audio']['padding_distribution'] = $Distribution['padding'];
1283
+ if (count($Distribution['version']) > 1) {
1284
+ $ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG version detected';
1285
+ }
1286
+ if (count($Distribution['layer']) > 1) {
1287
+ $ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG layer detected';
1288
+ }
1289
+ if (count($Distribution['frequency']) > 1) {
1290
+ $ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG sample rate detected';
1291
+ }
1292
+
1293
+
1294
+ $bittotal = 0;
1295
+ foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount) {
1296
+ if ($bitratevalue != 'free') {
1297
+ $bittotal += ($bitratevalue * $bitratecount);
1298
+ }
1299
+ }
1300
+ $ThisFileInfo['mpeg']['audio']['frame_count'] = array_sum($Distribution['bitrate']);
1301
+ if ($ThisFileInfo['mpeg']['audio']['frame_count'] == 0) {
1302
+ $ThisFileInfo['error'][] = 'no MPEG audio frames found';
1303
+ return false;
1304
+ }
1305
+ $ThisFileInfo['mpeg']['audio']['bitrate'] = ($bittotal / $ThisFileInfo['mpeg']['audio']['frame_count']);
1306
+ $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr');
1307
+ $ThisFileInfo['mpeg']['audio']['sample_rate'] = getid3_lib::array_max($Distribution['frequency'], true);
1308
+
1309
+ $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate'];
1310
+ $ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['mpeg']['audio']['bitrate_mode'];
1311
+ $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate'];
1312
+ $ThisFileInfo['audio']['dataformat'] = 'mp'.getid3_lib::array_max($Distribution['layer'], true);
1313
+ $ThisFileInfo['fileformat'] = $ThisFileInfo['audio']['dataformat'];
1314
+
1315
+ return true;
1316
+ }
1317
+
1318
+
1319
+ function getOnlyMPEGaudioInfo($fd, &$ThisFileInfo, $avdataoffset, $BitrateHistogram=false) {
1320
+
1321
+ // looks for synch, decodes MPEG audio header
1322
+
1323
+ static $MPEGaudioVersionLookup;
1324
+ static $MPEGaudioLayerLookup;
1325
+ static $MPEGaudioBitrateLookup;
1326
+ if (empty($MPEGaudioVersionLookup)) {
1327
+ $MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray();
1328
+ $MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray();
1329
+ $MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray();
1330
+
1331
+ }
1332
+
1333
+ fseek($fd, $avdataoffset, SEEK_SET);
1334
+ $sync_seek_buffer_size = min(128 * 1024, $ThisFileInfo['avdataend'] - $avdataoffset);
1335
+ $header = fread($fd, $sync_seek_buffer_size);
1336
+ $sync_seek_buffer_size = strlen($header);
1337
+ $SynchSeekOffset = 0;
1338
+ while ($SynchSeekOffset < $sync_seek_buffer_size) {
1339
+
1340
+ if ((($avdataoffset + $SynchSeekOffset) < $ThisFileInfo['avdataend']) && !feof($fd)) {
1341
+
1342
+ if ($SynchSeekOffset > $sync_seek_buffer_size) {
1343
+ // if a synch's not found within the first 128k bytes, then give up
1344
+ $ThisFileInfo['error'][] = 'Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB';
1345
+ if (isset($ThisFileInfo['audio']['bitrate'])) {
1346
+ unset($ThisFileInfo['audio']['bitrate']);
1347
+ }
1348
+ if (isset($ThisFileInfo['mpeg']['audio'])) {
1349
+ unset($ThisFileInfo['mpeg']['audio']);
1350
+ }
1351
+ if (empty($ThisFileInfo['mpeg'])) {
1352
+ unset($ThisFileInfo['mpeg']);
1353
+ }
1354
+ return false;
1355
+
1356
+ } elseif (feof($fd)) {
1357
+
1358
+ $ThisFileInfo['error'][] = 'Could not find valid MPEG audio synch before end of file';
1359
+ if (isset($ThisFileInfo['audio']['bitrate'])) {
1360
+ unset($ThisFileInfo['audio']['bitrate']);
1361
+ }
1362
+ if (isset($ThisFileInfo['mpeg']['audio'])) {
1363
+ unset($ThisFileInfo['mpeg']['audio']);
1364
+ }
1365
+ if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || (count($ThisFileInfo['mpeg']) == 0))) {
1366
+ unset($ThisFileInfo['mpeg']);
1367
+ }
1368
+ return false;
1369
+ }
1370
+ }
1371
+
1372
+ if (($SynchSeekOffset + 1) >= strlen($header)) {
1373
+ $ThisFileInfo['error'][] = 'Could not find valid MPEG synch before end of file';
1374
+ return false;
1375
+ }
1376
+
1377
+ if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected
1378
+
1379
+ if (!isset($FirstFrameThisfileInfo) && !isset($ThisFileInfo['mpeg']['audio'])) {
1380
+ $FirstFrameThisfileInfo = $ThisFileInfo;
1381
+ $FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset;
1382
+ if (!getid3_mp3::decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $FirstFrameThisfileInfo, false)) {
1383
+ // if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
1384
+ // garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below
1385
+ unset($FirstFrameThisfileInfo);
1386
+ }
1387
+ }
1388
+
1389
+ $dummy = $ThisFileInfo; // only overwrite real data if valid header found
1390
+ if (getid3_mp3::decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $dummy, true)) {
1391
+ $ThisFileInfo = $dummy;
1392
+ $ThisFileInfo['avdataoffset'] = $avdataoffset + $SynchSeekOffset;
1393
+ switch ($ThisFileInfo['fileformat']) {
1394
+ case '':
1395
+ case 'id3':
1396
+ case 'ape':
1397
+ case 'mp3':
1398
+ $ThisFileInfo['fileformat'] = 'mp3';
1399
+ $ThisFileInfo['audio']['dataformat'] = 'mp3';
1400
+ break;
1401
+ }
1402
+ if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
1403
+ if (!(abs($ThisFileInfo['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) {
1404
+ // If there is garbage data between a valid VBR header frame and a sequence
1405
+ // of valid MPEG-audio frames the VBR data is no longer discarded.
1406
+ $ThisFileInfo = $FirstFrameThisfileInfo;
1407
+ $ThisFileInfo['avdataoffset'] = $FirstFrameAVDataOffset;
1408
+ $ThisFileInfo['fileformat'] = 'mp3';
1409
+ $ThisFileInfo['audio']['dataformat'] = 'mp3';
1410
+ $dummy = $ThisFileInfo;
1411
+ unset($dummy['mpeg']['audio']);
1412
+ $GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength'];
1413
+ $GarbageOffsetEnd = $avdataoffset + $SynchSeekOffset;
1414
+ if (getid3_mp3::decodeMPEGaudioHeader($fd, $GarbageOffsetEnd, $dummy, true, true)) {
1415
+
1416
+ $ThisFileInfo = $dummy;
1417
+ $ThisFileInfo['avdataoffset'] = $GarbageOffsetEnd;
1418
+ $ThisFileInfo['warning'][] = 'apparently-valid VBR header not used because could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd;
1419
+
1420
+ } else {
1421
+
1422
+ $ThisFileInfo['warning'][] = 'using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')';
1423
+
1424
+ }
1425
+ }
1426
+ }
1427
+ if (isset($ThisFileInfo['mpeg']['audio']['bitrate_mode']) && ($ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($ThisFileInfo['mpeg']['audio']['VBR_method'])) {
1428
+ // VBR file with no VBR header
1429
+ $BitrateHistogram = true;
1430
+ }
1431
+
1432
+ if ($BitrateHistogram) {
1433
+
1434
+ $ThisFileInfo['mpeg']['audio']['stereo_distribution'] = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0);
1435
+ $ThisFileInfo['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0);
1436
+
1437
+ if ($ThisFileInfo['mpeg']['audio']['version'] == '1') {
1438
+ if ($ThisFileInfo['mpeg']['audio']['layer'] == 3) {
1439
+ $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0);
1440
+ } elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 2) {
1441
+ $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0, 384000=>0);
1442
+ } elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 1) {
1443
+ $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 64000=>0, 96000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 288000=>0, 320000=>0, 352000=>0, 384000=>0, 416000=>0, 448000=>0);
1444
+ }
1445
+ } elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 1) {
1446
+ $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0, 176000=>0, 192000=>0, 224000=>0, 256000=>0);
1447
+ } else {
1448
+ $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 8000=>0, 16000=>0, 24000=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0);
1449
+ }
1450
+
1451
+ $dummy = array('error'=>$ThisFileInfo['error'], 'warning'=>$ThisFileInfo['warning'], 'avdataend'=>$ThisFileInfo['avdataend'], 'avdataoffset'=>$ThisFileInfo['avdataoffset']);
1452
+ $synchstartoffset = $ThisFileInfo['avdataoffset'];
1453
+
1454
+ $FastMode = false;
1455
+ $SynchErrorsFound = 0;
1456
+ while (getid3_mp3::decodeMPEGaudioHeader($fd, $synchstartoffset, $dummy, false, false, $FastMode)) {
1457
+ $FastMode = true;
1458
+ $thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
1459
+
1460
+ if (empty($dummy['mpeg']['audio']['framelength'])) {
1461
+ $SynchErrorsFound++;
1462
+ } else {
1463
+ @$ThisFileInfo['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]++;
1464
+ @$ThisFileInfo['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]++;
1465
+ @$ThisFileInfo['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]++;
1466
+
1467
+ $synchstartoffset += $dummy['mpeg']['audio']['framelength'];
1468
+ }
1469
+ }
1470
+ if ($SynchErrorsFound > 0) {
1471
+ $ThisFileInfo['warning'][] = 'Found '.$SynchErrorsFound.' synch errors in histogram analysis';
1472
+ //return false;
1473
+ }
1474
+
1475
+ $bittotal = 0;
1476
+ $framecounter = 0;
1477
+ foreach ($ThisFileInfo['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) {
1478
+ $framecounter += $bitratecount;
1479
+ if ($bitratevalue != 'free') {
1480
+ $bittotal += ($bitratevalue * $bitratecount);
1481
+ }
1482
+ }
1483
+ if ($framecounter == 0) {
1484
+ $ThisFileInfo['error'][] = 'Corrupt MP3 file: framecounter == zero';
1485
+ return false;
1486
+ }
1487
+ $ThisFileInfo['mpeg']['audio']['frame_count'] = $framecounter;
1488
+ $ThisFileInfo['mpeg']['audio']['bitrate'] = ($bittotal / $framecounter);
1489
+
1490
+ $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate'];
1491
+
1492
+
1493
+ // Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently
1494
+ $distinct_bitrates = 0;
1495
+ foreach ($ThisFileInfo['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) {
1496
+ if ($bitrate_count > 0) {
1497
+ $distinct_bitrates++;
1498
+ }
1499
+ }
1500
+ if ($distinct_bitrates > 1) {
1501
+ $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr';
1502
+ } else {
1503
+ $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr';
1504
+ }
1505
+ $ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['mpeg']['audio']['bitrate_mode'];
1506
+
1507
+ }
1508
+
1509
+ break; // exit while()
1510
+ }
1511
+ }
1512
+
1513
+ $SynchSeekOffset++;
1514
+ if (($avdataoffset + $SynchSeekOffset) >= $ThisFileInfo['avdataend']) {
1515
+ // end of file/data
1516
+
1517
+ if (empty($ThisFileInfo['mpeg']['audio'])) {
1518
+
1519
+ $ThisFileInfo['error'][] = 'could not find valid MPEG synch before end of file';
1520
+ if (isset($ThisFileInfo['audio']['bitrate'])) {
1521
+ unset($ThisFileInfo['audio']['bitrate']);
1522
+ }
1523
+ if (isset($ThisFileInfo['mpeg']['audio'])) {
1524
+ unset($ThisFileInfo['mpeg']['audio']);
1525
+ }
1526
+ if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || empty($ThisFileInfo['mpeg']))) {
1527
+ unset($ThisFileInfo['mpeg']);
1528
+ }
1529
+ return false;
1530
+
1531
+ }
1532
+ break;
1533
+ }
1534
+
1535
+ }
1536
+ $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels'];
1537
+ $ThisFileInfo['audio']['channelmode'] = $ThisFileInfo['mpeg']['audio']['channelmode'];
1538
+ $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate'];
1539
+ return true;
1540
+ }
1541
+
1542
+
1543
+ function MPEGaudioVersionArray() {
1544
+ static $MPEGaudioVersion = array('2.5', false, '2', '1');
1545
+ return $MPEGaudioVersion;
1546
+ }
1547
+
1548
+ function MPEGaudioLayerArray() {
1549
+ static $MPEGaudioLayer = array(false, 3, 2, 1);
1550
+ return $MPEGaudioLayer;
1551
+ }
1552
+
1553
+ function MPEGaudioBitrateArray() {
1554
+ static $MPEGaudioBitrate;
1555
+ if (empty($MPEGaudioBitrate)) {
1556
+ $MPEGaudioBitrate = array (
1557
+ '1' => array (1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000),
1558
+ 2 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000),
1559
+ 3 => array('free', 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000)
1560
+ ),
1561
+
1562
+ '2' => array (1 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000),
1563
+ 2 => array('free', 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000),
1564
+ )
1565
+ );
1566
+ $MPEGaudioBitrate['2'][3] = $MPEGaudioBitrate['2'][2];
1567
+ $MPEGaudioBitrate['2.5'] = $MPEGaudioBitrate['2'];
1568
+ }
1569
+ return $MPEGaudioBitrate;
1570
+ }
1571
+
1572
+ function MPEGaudioFrequencyArray() {
1573
+ static $MPEGaudioFrequency;
1574
+ if (empty($MPEGaudioFrequency)) {
1575
+ $MPEGaudioFrequency = array (
1576
+ '1' => array(44100, 48000, 32000),
1577
+ '2' => array(22050, 24000, 16000),
1578
+ '2.5' => array(11025, 12000, 8000)
1579
+ );
1580
+ }
1581
+ return $MPEGaudioFrequency;
1582
+ }
1583
+
1584
+ function MPEGaudioChannelModeArray() {
1585
+ static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono');
1586
+ return $MPEGaudioChannelMode;
1587
+ }
1588
+
1589
+ function MPEGaudioModeExtensionArray() {
1590
+ static $MPEGaudioModeExtension;
1591
+ if (empty($MPEGaudioModeExtension)) {
1592
+ $MPEGaudioModeExtension = array (
1593
+ 1 => array('4-31', '8-31', '12-31', '16-31'),
1594
+ 2 => array('4-31', '8-31', '12-31', '16-31'),
1595
+ 3 => array('', 'IS', 'MS', 'IS+MS')
1596
+ );
1597
+ }
1598
+ return $MPEGaudioModeExtension;
1599
+ }
1600
+
1601
+ function MPEGaudioEmphasisArray() {
1602
+ static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17');
1603
+ return $MPEGaudioEmphasis;
1604
+ }
1605
+
1606
+ function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) {
1607
+ return getid3_mp3::MPEGaudioHeaderValid(getid3_mp3::MPEGaudioHeaderDecode($head4), false, $allowBitrate15);
1608
+ }
1609
+
1610
+ function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) {
1611
+ if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
1612
+ return false;
1613
+ }
1614
+
1615
+ static $MPEGaudioVersionLookup;
1616
+ static $MPEGaudioLayerLookup;
1617
+ static $MPEGaudioBitrateLookup;
1618
+ static $MPEGaudioFrequencyLookup;
1619
+ static $MPEGaudioChannelModeLookup;
1620
+ static $MPEGaudioModeExtensionLookup;
1621
+ static $MPEGaudioEmphasisLookup;
1622
+ if (empty($MPEGaudioVersionLookup)) {
1623
+ $MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray();
1624
+ $MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray();
1625
+ $MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray();
1626
+ $MPEGaudioFrequencyLookup = getid3_mp3::MPEGaudioFrequencyArray();
1627
+ $MPEGaudioChannelModeLookup = getid3_mp3::MPEGaudioChannelModeArray();
1628
+ $MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray();
1629
+ $MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray();
1630
+ }
1631
+
1632
+ if (isset($MPEGaudioVersionLookup[$rawarray['version']])) {
1633
+ $decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']];
1634
+ } else {
1635
+ echo ($echoerrors ? "\n".'invalid Version ('.$rawarray['version'].')' : '');
1636
+ return false;
1637
+ }
1638
+ if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) {
1639
+ $decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']];
1640
+ } else {
1641
+ echo ($echoerrors ? "\n".'invalid Layer ('.$rawarray['layer'].')' : '');
1642
+ return false;
1643
+ }
1644
+ if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) {
1645
+ echo ($echoerrors ? "\n".'invalid Bitrate ('.$rawarray['bitrate'].')' : '');
1646
+ if ($rawarray['bitrate'] == 15) {
1647
+ // known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
1648
+ // let it go through here otherwise file will not be identified
1649
+ if (!$allowBitrate15) {
1650
+ return false;
1651
+ }
1652
+ } else {
1653
+ return false;
1654
+ }
1655
+ }
1656
+ if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) {
1657
+ echo ($echoerrors ? "\n".'invalid Frequency ('.$rawarray['sample_rate'].')' : '');
1658
+ return false;
1659
+ }
1660
+ if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) {
1661
+ echo ($echoerrors ? "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')' : '');
1662
+ return false;
1663
+ }
1664
+ if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) {
1665
+ echo ($echoerrors ? "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')' : '');
1666
+ return false;
1667
+ }
1668
+ if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) {
1669
+ echo ($echoerrors ? "\n".'invalid Emphasis ('.$rawarray['emphasis'].')' : '');
1670
+ return false;
1671
+ }
1672
+ // These are just either set or not set, you can't mess that up :)
1673
+ // $rawarray['protection'];
1674
+ // $rawarray['padding'];
1675
+ // $rawarray['private'];
1676
+ // $rawarray['copyright'];
1677
+ // $rawarray['original'];
1678
+
1679
+ return true;
1680
+ }
1681
+
1682
+ function MPEGaudioHeaderDecode($Header4Bytes) {
1683
+ // AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM
1684
+ // A - Frame sync (all bits set)
1685
+ // B - MPEG Audio version ID
1686
+ // C - Layer description
1687
+ // D - Protection bit
1688
+ // E - Bitrate index
1689
+ // F - Sampling rate frequency index
1690
+ // G - Padding bit
1691
+ // H - Private bit
1692
+ // I - Channel Mode
1693
+ // J - Mode extension (Only if Joint stereo)
1694
+ // K - Copyright
1695
+ // L - Original
1696
+ // M - Emphasis
1697
+
1698
+ if (strlen($Header4Bytes) != 4) {
1699
+ return false;
1700
+ }
1701
+
1702
+ $MPEGrawHeader['synch'] = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4;
1703
+ $MPEGrawHeader['version'] = (ord($Header4Bytes{1}) & 0x18) >> 3; // BB
1704
+ $MPEGrawHeader['layer'] = (ord($Header4Bytes{1}) & 0x06) >> 1; // CC
1705
+ $MPEGrawHeader['protection'] = (ord($Header4Bytes{1}) & 0x01); // D
1706
+ $MPEGrawHeader['bitrate'] = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE
1707
+ $MPEGrawHeader['sample_rate'] = (ord($Header4Bytes{2}) & 0x0C) >> 2; // FF
1708
+ $MPEGrawHeader['padding'] = (ord($Header4Bytes{2}) & 0x02) >> 1; // G
1709
+ $MPEGrawHeader['private'] = (ord($Header4Bytes{2}) & 0x01); // H
1710
+ $MPEGrawHeader['channelmode'] = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II
1711
+ $MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; // JJ
1712
+ $MPEGrawHeader['copyright'] = (ord($Header4Bytes{3}) & 0x08) >> 3; // K
1713
+ $MPEGrawHeader['original'] = (ord($Header4Bytes{3}) & 0x04) >> 2; // L
1714
+ $MPEGrawHeader['emphasis'] = (ord($Header4Bytes{3}) & 0x03); // MM
1715
+
1716
+ return $MPEGrawHeader;
1717
+ }
1718
+
1719
+ function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) {
1720
+ static $AudioFrameLengthCache = array();
1721
+
1722
+ if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) {
1723
+ $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false;
1724
+ if ($bitrate != 'free') {
1725
+
1726
+ if ($version == '1') {
1727
+
1728
+ if ($layer == '1') {
1729
+
1730
+ // For Layer I slot is 32 bits long
1731
+ $FrameLengthCoefficient = 48;
1732
+ $SlotLength = 4;
1733
+
1734
+ } else { // Layer 2 / 3
1735
+
1736
+ // for Layer 2 and Layer 3 slot is 8 bits long.
1737
+ $FrameLengthCoefficient = 144;
1738
+ $SlotLength = 1;
1739
+
1740
+ }
1741
+
1742
+ } else { // MPEG-2 / MPEG-2.5
1743
+
1744
+ if ($layer == '1') {
1745
+
1746
+ // For Layer I slot is 32 bits long
1747
+ $FrameLengthCoefficient = 24;
1748
+ $SlotLength = 4;
1749
+
1750
+ } elseif ($layer == '2') {
1751
+
1752
+ // for Layer 2 and Layer 3 slot is 8 bits long.
1753
+ $FrameLengthCoefficient = 144;
1754
+ $SlotLength = 1;
1755
+
1756
+ } else { // layer 3
1757
+
1758
+ // for Layer 2 and Layer 3 slot is 8 bits long.
1759
+ $FrameLengthCoefficient = 72;
1760
+ $SlotLength = 1;
1761
+
1762
+ }
1763
+
1764
+ }
1765
+
1766
+ // FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding
1767
+ if ($samplerate > 0) {
1768
+ $NewFramelength = ($FrameLengthCoefficient * $bitrate) / $samplerate;
1769
+ $NewFramelength = floor($NewFramelength / $SlotLength) * $SlotLength; // round to next-lower multiple of SlotLength (1 byte for Layer 2/3, 4 bytes for Layer I)
1770
+ if ($padding) {
1771
+ $NewFramelength += $SlotLength;
1772
+ }
1773
+ $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = (int) $NewFramelength;
1774
+ }
1775
+ }
1776
+ }
1777
+ return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate];
1778
+ }
1779
+
1780
+ function ClosestStandardMP3Bitrate($bitrate) {
1781
+ static $StandardBitrates = array(320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000);
1782
+ static $BitrateTable = array(0=>'-');
1783
+ $roundbitrate = intval(round($bitrate, -3));
1784
+ if (!isset($BitrateTable[$roundbitrate])) {
1785
+ if ($roundbitrate > 320000) {
1786
+ $BitrateTable[$roundbitrate] = round($bitrate, -4);
1787
+ } else {
1788
+ $LastBitrate = 320000;
1789
+ foreach ($StandardBitrates as $StandardBitrate) {
1790
+ $BitrateTable[$roundbitrate] = $StandardBitrate;
1791
+ if ($roundbitrate >= $StandardBitrate - (($LastBitrate - $StandardBitrate) / 2)) {
1792
+ break;
1793
+ }
1794
+ $LastBitrate = $StandardBitrate;
1795
+ }
1796
+ }
1797
+ }
1798
+ return $BitrateTable[$roundbitrate];
1799
+ }
1800
+
1801
+ function XingVBRidOffset($version, $channelmode) {
1802
+ static $XingVBRidOffsetCache = array();
1803
+ if (empty($XingVBRidOffset)) {
1804
+ $XingVBRidOffset = array (
1805
+ '1' => array ('mono' => 0x15, // 4 + 17 = 21
1806
+ 'stereo' => 0x24, // 4 + 32 = 36
1807
+ 'joint stereo' => 0x24,
1808
+ 'dual channel' => 0x24
1809
+ ),
1810
+
1811
+ '2' => array ('mono' => 0x0D, // 4 + 9 = 13
1812
+ 'stereo' => 0x15, // 4 + 17 = 21
1813
+ 'joint stereo' => 0x15,
1814
+ 'dual channel' => 0x15
1815
+ ),
1816
+
1817
+ '2.5' => array ('mono' => 0x15,
1818
+ 'stereo' => 0x15,
1819
+ 'joint stereo' => 0x15,
1820
+ 'dual channel' => 0x15
1821
+ )
1822
+ );
1823
+ }
1824
+ return $XingVBRidOffset[$version][$channelmode];
1825
+ }
1826
+
1827
+ function LAMEvbrMethodLookup($VBRmethodID) {
1828
+ static $LAMEvbrMethodLookup = array(
1829
+ 0x00 => 'unknown',
1830
+ 0x01 => 'cbr',
1831
+ 0x02 => 'abr',
1832
+ 0x03 => 'vbr-old / vbr-rh',
1833
+ 0x04 => 'vbr-new / vbr-mtrh',
1834
+ 0x05 => 'vbr-mt',
1835
+ 0x06 => 'vbr (full vbr method 4)',
1836
+ 0x08 => 'cbr (constant bitrate 2 pass)',
1837
+ 0x09 => 'abr (2 pass)',
1838
+ 0x0F => 'reserved'
1839
+ );
1840
+ return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : '');
1841
+ }
1842
+
1843
+ function LAMEmiscStereoModeLookup($StereoModeID) {
1844
+ static $LAMEmiscStereoModeLookup = array(
1845
+ 0 => 'mono',
1846
+ 1 => 'stereo',
1847
+ 2 => 'dual mono',
1848
+ 3 => 'joint stereo',
1849
+ 4 => 'forced stereo',
1850
+ 5 => 'auto',
1851
+ 6 => 'intensity stereo',
1852
+ 7 => 'other'
1853
+ );
1854
+ return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : '');
1855
+ }
1856
+
1857
+ function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) {
1858
+ static $LAMEmiscSourceSampleFrequencyLookup = array(
1859
+ 0 => '<= 32 kHz',
1860
+ 1 => '44.1 kHz',
1861
+ 2 => '48 kHz',
1862
+ 3 => '> 48kHz'
1863
+ );
1864
+ return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : '');
1865
+ }
1866
+
1867
+ function LAMEsurroundInfoLookup($SurroundInfoID) {
1868
+ static $LAMEsurroundInfoLookup = array(
1869
+ 0 => 'no surround info',
1870
+ 1 => 'DPL encoding',
1871
+ 2 => 'DPL2 encoding',
1872
+ 3 => 'Ambisonic encoding'
1873
+ );
1874
+ return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved');
1875
+ }
1876
+
1877
+ function LAMEpresetUsedLookup($LAMEtag) {
1878
+
1879
+ if ($LAMEtag['preset_used_id'] == 0) {
1880
+ // no preset used (LAME >=3.93)
1881
+ // no preset recorded (LAME <3.93)
1882
+ return '';
1883
+ }
1884
+ $LAMEpresetUsedLookup = array();
1885
+
1886
+ ///// THIS PART CANNOT BE STATIC .
1887
+ for ($i = 8; $i <= 320; $i++) {
1888
+ switch ($LAMEtag['vbr_method']) {
1889
+ case 'cbr':
1890
+ $LAMEpresetUsedLookup[$i] = '--alt-preset '.$LAMEtag['vbr_method'].' '.$i;
1891
+ break;
1892
+ case 'abr':
1893
+ default: // other VBR modes shouldn't be here(?)
1894
+ $LAMEpresetUsedLookup[$i] = '--alt-preset '.$i;
1895
+ break;
1896
+ }
1897
+ }
1898
+
1899
+ // named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions()
1900
+
1901
+ // named alt-presets
1902
+ $LAMEpresetUsedLookup[1000] = '--r3mix';
1903
+ $LAMEpresetUsedLookup[1001] = '--alt-preset standard';
1904
+ $LAMEpresetUsedLookup[1002] = '--alt-preset extreme';
1905
+ $LAMEpresetUsedLookup[1003] = '--alt-preset insane';
1906
+ $LAMEpresetUsedLookup[1004] = '--alt-preset fast standard';
1907
+ $LAMEpresetUsedLookup[1005] = '--alt-preset fast extreme';
1908
+ $LAMEpresetUsedLookup[1006] = '--alt-preset medium';
1909
+ $LAMEpresetUsedLookup[1007] = '--alt-preset fast medium';
1910
+
1911
+ // LAME 3.94 additions/changes
1912
+ $LAMEpresetUsedLookup[1010] = '--preset portable'; // 3.94a15 Oct 21 2003
1913
+ $LAMEpresetUsedLookup[1015] = '--preset radio'; // 3.94a15 Oct 21 2003
1914
+
1915
+ $LAMEpresetUsedLookup[320] = '--preset insane'; // 3.94a15 Nov 12 2003
1916
+ $LAMEpresetUsedLookup[410] = '-V9';
1917
+ $LAMEpresetUsedLookup[420] = '-V8';
1918
+ $LAMEpresetUsedLookup[440] = '-V6';
1919
+ $LAMEpresetUsedLookup[430] = '--preset radio'; // 3.94a15 Nov 12 2003
1920
+ $LAMEpresetUsedLookup[450] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'portable'; // 3.94a15 Nov 12 2003
1921
+ $LAMEpresetUsedLookup[460] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'medium'; // 3.94a15 Nov 12 2003
1922
+ $LAMEpresetUsedLookup[470] = '--r3mix'; // 3.94b1 Dec 18 2003
1923
+ $LAMEpresetUsedLookup[480] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'standard'; // 3.94a15 Nov 12 2003
1924
+ $LAMEpresetUsedLookup[490] = '-V1';
1925
+ $LAMEpresetUsedLookup[500] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'extreme'; // 3.94a15 Nov 12 2003
1926
+
1927
+ return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to info@getid3.org');
1928
+ }
1929
+
1930
+ }
1931
+
1932
+ ?>
itunes_default.jpg ADDED
Binary file
mp3info.class.php ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // mp3info class for use in the Blubrry Powerpress
3
+ // Main purpose of this file is to obtain the duration string for the itunes:duration field.
4
+ // Library is packaged thin with only basic mp3 support.
5
+ // Concept with this library is to get the information without downlaoding the entire file.
6
+ // for efficienccy
7
+
8
+ class Mp3Info {
9
+ //var $m_DownloadBytesLimit = 1638400; // 200K (200*1024*8) bytes file
10
+ var $m_DownloadBytesLimit = 204800; // 25K (25*1024*8) bytes file
11
+ var $m_RedirectLimit = 5; // Number of times to do the 302 redirect
12
+ var $m_UserAgent = 'Blubrry Powerpress/1.0';
13
+ var $m_error = '';
14
+ var $m_ContentLength = false;
15
+ var $m_RedirectCount = 0;
16
+
17
+ // Constructor
18
+ function Mp3Info()
19
+ {
20
+ // Empty for now
21
+ }
22
+
23
+ /*
24
+ Set how much of the media file to download. Default: 25K
25
+ */
26
+ function SetDownloadBytesLimit($limit=204800)
27
+ {
28
+ $this->m_DownloadBytesLimit = $limit;
29
+ }
30
+
31
+ /*
32
+ Set how many times we can follow a HTTP 30x header redirect before we fail.
33
+ */
34
+ function SetRedirectLimit($limit=5)
35
+ {
36
+ $this->m_RedirectLimit = $limit;
37
+ }
38
+
39
+ /*
40
+ Set the user agent to be sent by this plugin
41
+ */
42
+ function SetUserAgent($user_agent)
43
+ {
44
+ $this->m_UserAgent = $user_agent;
45
+ }
46
+
47
+ /*
48
+ Return the last set error message
49
+ */
50
+ function GetError()
51
+ {
52
+ return $this->m_error;
53
+ }
54
+
55
+ /*
56
+ Set the last error message
57
+ */
58
+ function SetError($msg)
59
+ {
60
+ $this->m_error = $msg;
61
+ }
62
+
63
+ /*
64
+ Get the length in bytes of the file to download.
65
+ */
66
+ function GetContentLength()
67
+ {
68
+ return $this->m_ContentLength;
69
+ }
70
+
71
+ /*
72
+ Get the number of times we followed 30x header redirects
73
+ */
74
+ function GetRedirectCount()
75
+ {
76
+ return $this->m_RedirectCount;
77
+ }
78
+
79
+ /*
80
+ Start the download and get the headers, handles the redirect if there are any
81
+ */
82
+ function Download($url, $RedirectCount = 0)
83
+ {
84
+ if( $RedirectCount > $this->m_RedirectLimit )
85
+ {
86
+ $this->SetError( 'Download exceeded redirect limit of '.$this->m_RedirectLimit .'.' );
87
+ return false;
88
+ }
89
+
90
+ $this->m_ContentLength = false;
91
+ $this->m_RedirectCount = $RedirectCount;
92
+
93
+ $urlParts = parse_url($url);
94
+ if( !isset( $urlParts['path']) )
95
+ $urlParts['path'] = '/';
96
+ if( !isset( $urlParts['port']) )
97
+ $urlParts['port'] = 80;
98
+ if( !isset( $urlParts['scheme']) )
99
+ $urlParts['scheme'] = 'http';
100
+
101
+ $fp = fsockopen($urlParts['host'], $urlParts['port'], $errno, $errstr, 30);
102
+ if ($fp)
103
+ {
104
+ // Create and send the request headers
105
+ $RequestHeaders = 'GET '.$urlParts['path'].(isset($urlParts['query']) ? '?'.@$urlParts['query'] : '')." HTTP/1.0\r\n";
106
+ $RequestHeaders .= 'Host: '.$urlParts['host'].($urlParts['port'] != 80 ? ':'.$urlParts['port'] : '')."\r\n";
107
+ $RequestHeaders .= "Connection: Close\r\n";
108
+ $RequestHeaders .= "User-Agent: {this->m_UserAgent}\r\n";
109
+ fwrite($fp, $RequestHeaders."\r\n");
110
+
111
+ $Redirect = false;
112
+ $RedirectURL = false;
113
+ $ContentLength = false;
114
+ $ContentType = false;
115
+ // Loop through the headers
116
+ while( !feof($fp) )
117
+ {
118
+ $line = fgets($fp, 1280); // Get the next header line...
119
+ if( $line === false )
120
+ break; // Something happened
121
+ if ($line == "\r\n")
122
+ break; // Okay we're ending the headers, now onto the content
123
+
124
+ $line = rtrim($line); // Clean out the new line characters
125
+
126
+ list($key, $value) = explode(':', $line, 2);
127
+ $key = trim($key);
128
+ $value = trim($value);
129
+
130
+ if( stristr($line, '301 Moved Permanently') || stristr($line, '302 Found') || stristr($line, '307 Temporary Redirect') )
131
+ {
132
+ $Redirect = true; // We are dealing with a redirect, lets handle it
133
+ }
134
+ else
135
+ {
136
+ switch( strtolower($key) )
137
+ {
138
+ case 'location': {
139
+ $RedirectURL = $value;
140
+ }; break;
141
+ case 'content-length': {
142
+ $ContentLength = $value;
143
+ }; break;
144
+ }
145
+ }
146
+ }
147
+
148
+ // Loop through the content till we reach our limit...
149
+ $Content = '';
150
+ if( $this->m_DownloadBytesLimit )
151
+ {
152
+ while( !feof($fp) )
153
+ {
154
+ $Content .= fread($fp, 8096);
155
+ if( strlen($Content) > $this->m_DownloadBytesLimit )
156
+ break; // We got enough of the file we should be able to determine the duration
157
+ }
158
+ }
159
+ fclose($fp);
160
+
161
+ // If we're dealing with a redirect, lets call our nested function call now
162
+ if( $Redirect )
163
+ {
164
+ unset($Content); // clear what may be using a lot of memory
165
+ return $this->Download($RedirectURL, $RedirectCount + 1 ); // Follow this redirect
166
+ }
167
+ else // Otherwise, lets set the data and return true for part two
168
+ {
169
+ global $TempFile;
170
+ if( function_exists('get_temp_dir') ) // If wordpress function is available, lets use it
171
+ $TempFile = tempnam(get_temp_dir(), 'wp_powerpress');
172
+ else // otherwise use the default path
173
+ $TempFile = tempnam('/tmp', 'wp_powerpress');
174
+
175
+ if( $TempFile === false )
176
+ {
177
+ $this->SetError('Unable to save media information to temporary directory.');
178
+ return false;
179
+ }
180
+
181
+ $fp = fopen( $TempFile, 'w' );
182
+ fwrite($fp, $Content);
183
+ fclose($fp);
184
+
185
+ if( $ContentLength )
186
+ $this->m_ContentLength = $ContentLength;
187
+ return $TempFile;
188
+ }
189
+ }
190
+ $this->SetError('Unable to connect to host '.$urlParts['host'].'.');
191
+ return false;
192
+ }
193
+
194
+ /*
195
+ Get the MP3 information
196
+ */
197
+ function GetMp3Info($File)
198
+ {
199
+ $DeleteFile = false;
200
+ if( strtolower( substr($File, 0, 7) ) == 'http://' )
201
+ {
202
+ $LocalFile = $this->Download($File);
203
+ if( $LocalFile === false )
204
+ return false;
205
+ $DeleteFile = true;
206
+ }
207
+ else
208
+ {
209
+ $LocalFile = $File;
210
+ }
211
+
212
+ // Hack so this works in Windows, helper apps are not necessary for what we're doing anyway
213
+ define('GETID3_HELPERAPPSDIR', true);
214
+ require_once(dirname(__FILE__).'/getid3/getid3.php');
215
+ $getID3 = new getID3;
216
+ $FileInfo = $getID3->analyze( $LocalFile, $this->m_ContentLength );
217
+ if( $DeleteFile )
218
+ @unlink($LocalFile);
219
+
220
+ if( $FileInfo )
221
+ {
222
+ // Remove extra data that is not necessary for us to return...
223
+ unset($FileInfo['mpeg']);
224
+ unset($FileInfo['audio']);
225
+ if( isset($FileInfo['id3v2']) )
226
+ unset($FileInfo['id3v2']);
227
+ if( isset($FileInfo['id3v1']) )
228
+ unset($FileInfo['id3v1']);
229
+
230
+ $FileInfo['playtime_seconds'] = round($FileInfo['playtime_seconds']);
231
+ return $FileInfo;
232
+ }
233
+
234
+ return false;
235
+ }
236
+ };
237
+
238
+ /*
239
+ // Example usage:
240
+
241
+ $Mp3Info = new Mp3Info();
242
+ if( $Data = $Mp3Info->GetMp3Info('http://www.podcampohio.com/podpress_trac/web/177/0/TS-107667.mp3') )
243
+ {
244
+ echo 'Success: ';
245
+ echo print_r( $Data );
246
+ echo PHP_EOL;
247
+ exit;
248
+ }
249
+ else
250
+ {
251
+ echo 'Error: ';
252
+ echo $Mp3Info->GetError();
253
+ echo PHP_EOL;
254
+ exit;
255
+ }
256
+ */
257
+
258
+
259
+ ?>
player.js ADDED
@@ -0,0 +1,373 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * jsMediaPlayer 1.0 for Blubrry Powerpress
3
+ *
4
+ * http://www.blubrry.com/powepress/
5
+ *
6
+ * Copyright (c) 2008 Angelo Mandato (angelo [at] mandato {period} com)
7
+ *
8
+ * Released under Aoache 2 license:
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * version 1.0.2 - 07/26/2008 - Fixed pop up player bug caused by v 1.0.1
12
+ * version 1.0.1 - 07/28/2008 - fixed flow player looping playback, flash player no longer loops.
13
+ * version 1.0.0 - 07/26/2008 - initial release
14
+ */
15
+
16
+ function jsMediaPlayer(FlashSrc) {
17
+ // Member variables
18
+ this.m_flash_src = FlashSrc;
19
+ this.m_auto_play = false;
20
+ this.m_width = 320;
21
+ this.m_height = 240;
22
+ this.m_player_div = false;
23
+ this.m_player_wnd = false;
24
+ this.m_one_player_only = false;
25
+ this.m_media_url = false;
26
+
27
+
28
+ this.FlashSrc=function(Src) {
29
+ this.m_flash_src = Src;
30
+ }
31
+
32
+ this.AutoPlay=function(Setting) {
33
+ this.m_auto_play = Setting;
34
+ }
35
+
36
+ this.SetWidth=function(Width) {
37
+ this.m_width = Width;
38
+ }
39
+
40
+ this.SetHeight=function(Height) {
41
+ this.m_height = Height;
42
+ }
43
+
44
+ this.OnePlayerOnly=function(Setting) {
45
+ this.m_one_player_only = Setting;
46
+ }
47
+
48
+ this.PlayInPage = function() {
49
+
50
+ // Check if we should even use javascript based player
51
+ if( this._passthru() )
52
+ return true;
53
+
54
+ // Make sure we're not already playing this div...
55
+ if( this.m_player_div == this.PlayInPage.arguments[1] )
56
+ return false;
57
+
58
+ // Close the last opened player
59
+ if( this.m_one_player_only )
60
+ this._closePrevPlayer();
61
+
62
+ // Set the proeprties:
63
+ this.m_media_url = this.PlayInPage.arguments[0];
64
+ this.m_player_div = this.PlayInPage.arguments[1];
65
+
66
+ var ext = this._getExt(this.m_media_url);
67
+ switch( ext )
68
+ {
69
+ case 'm4v':
70
+ case 'm4a':
71
+ case 'avi':
72
+ case 'mpg':
73
+ case 'mpeg':
74
+ case 'mp4':
75
+ case 'qt':
76
+ case 'mov': {
77
+
78
+ var contentType = 'video/mpeg'; // Default content type
79
+ if( ext == 'm4v' )
80
+ contentType = 'video/x-m4v';
81
+ else if( ext == 'm4a' )
82
+ contentType = 'audio/x-m4a';
83
+ else if( ext == 'avi' )
84
+ contentType = 'video/avi';
85
+ else if( ext == 'qt' || ext == 'mov' )
86
+ contentType = 'video/quicktime';
87
+
88
+ document.getElementById( this.m_player_div ).innerHTML = this._getQuickTime(contentType);
89
+ }; break;
90
+ case 'wma':
91
+ case 'wmv':
92
+ case 'asf': {
93
+ document.getElementById( this.m_player_div ).innerHTML = this._getWinPlayer();
94
+ }; break;
95
+ case 'rm': {
96
+ document.getElementById( this.m_player_div ).innerHTML = this._getRealPlayer();
97
+ }
98
+ case 'swf': {
99
+ document.getElementById( this.m_player_div ).innerHTML = this._getFlash();
100
+ }
101
+ case 'flv': {
102
+ this._doFlowPlayer();
103
+ }; break;
104
+ case 'mp3': {
105
+ this._doFlowPlayer(24);
106
+
107
+ }; break;
108
+ default: {
109
+ return true; // We didn't handle this, so lets let the click to the media handle itself.
110
+ };
111
+ }
112
+
113
+ // Display the div
114
+ document.getElementById( this.m_player_div ).style.display = 'block';
115
+ return false; // Don't let the href go
116
+ }
117
+
118
+ this.PlayNewWindow=function() {
119
+
120
+ // Check if we should even use javascript based player
121
+ if( this._passthru() )
122
+ return true;
123
+
124
+ if( this.m_one_player_only )
125
+ this._closePrevPlayer();
126
+
127
+ // Get the media file and extension
128
+ this.m_media_url = this.PlayNewWindow.arguments[0];
129
+ var ext = this._getExt(this.m_media_url);
130
+
131
+ // Calculate the window height
132
+ height = this.m_height;
133
+ if( ext == 'mp3' )
134
+ {
135
+ height = 24;
136
+ // Adjust the height for Opera web browser, only needed for mp3s
137
+ if( navigator.userAgent.indexOf("Opera") != -1 )
138
+ height += 40;
139
+ }
140
+ else
141
+ height += 40; // Add area for menu navigation
142
+
143
+ this.m_player_wnd = window.open(null,"jsPlayer", 'toolbar=0,status=0,resizable=1,width='+ (this.m_width +40).toString() +',height='+ height.toString() )
144
+ var Html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
145
+ Html += '<html xmlns="http://www.w3.org/1999/xhtml">';
146
+ Html += '<head>';
147
+ Html += '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
148
+ Html += '<title>Media Player</title>';
149
+ if( ext == 'mp3' || ext == 'flv' || ext == 'mp4' )
150
+ {
151
+ Html += '<script type="text/javascript">\n';
152
+ Html += 'function flashembed(root,userParams,flashvars){function getHTML(){var html="";if(typeof flashvars==\'function\'){flashvars=flashvars();}if(navigator.plugins&&navigator.mimeTypes&&navigator.mimeTypes.length){html=\'<embed type="application/x-shockwave-flash" \';if(params.id){extend(params,{name:params.id});}for(var key in params){if(params[key]!==null){html+=[key]+\'="\'+params[key]+\'"\\n\\t\';}}if(flashvars){html+=\'flashvars=\\\'\'+concatVars(flashvars)+\'\\\'\';}html+=\'/>\';}else{html=\'<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" \';html+=\'width="\'+params.width+\'" height="\'+params.height+\'"\';if(!params.id&&document.all){params.id="_"+(""+Math.random()).substring(5);}if(params.id){html+=\' id="\'+params.id+\'"\';}html+=\'>\';html+=\'\\n\\t<param name="movie" value="\'+params.src+\'" />\';params.id=params.src=params.width=params.height=null;for(var k in params){if(params[k]!==null){html+=\'\\n\\t<param name="\'+k+\'" value="\'+params[k]+\'" />\';}}if(flashvars){html+=\'\\n\\t<param name="flashvars" value=\\\'\'+concatVars(flashvars)+\'\\\' />\';}html+="</object>";if(debug){alert(html);}}return html;}function init(name){var timer=setInterval(function(){var doc=document;var el=doc.getElementById(name);if(el){flashembed(el,userParams,flashvars);clearInterval(timer);}else if(doc&&doc.getElementsByTagName&&doc.getElementById&&doc.body){clearInterval(timer);}},13);return true;}function extend(to,from){if(from){for(key in from){if(from.hasOwnProperty(key)){to[key]=from[key];}}}}var params={src:\'#\',width:\'100%\',height:\'100%\',version:null,onFail:null,expressInstall:null,debug:false,bgcolor:\'#ffffff\',allowfullscreen:true,allowscriptaccess:\'always\',quality:\'high\',type:\'application/x-shockwave-flash\',pluginspage:\'http://www.adobe.com/go/getflashplayer\'};if(typeof userParams==\'string\'){userParams={src:userParams};}extend(params,userParams);var version=flashembed.getVersion();var required=params.version;var express=params.expressInstall;var debug=params.debug;if(typeof root==\'string\'){var el=document.getElementById(root);if(el){root=el;}else{return init(root);}}if(!root){return;}if(!required||flashembed.isSupported(required)){params.onFail=params.version=params.expressInstall=params.debug=null;root.innerHTML=getHTML();return root.firstChild;}else if(params.onFail){var ret=params.onFail.call(params,flashembed.getVersion(),flashvars);if(ret){root.innerHTML=ret;}}else if(required&&express&&flashembed.isSupported([6,65])){extend(params,{src:express});flashvars={MMredirectURL:location.href,MMplayerType:\'PlugIn\',MMdoctitle:document.title};root.innerHTML=getHTML();}else{if(root.innerHTML.replace(/\\s/g,\'\')!==\'\'){}else{root.innerHTML="<h2>Flash version "+required+" or greater is required</h2>"+"<h3>"+(version[0]>0?"Your version is "+version:"You have no flash plugin installed")+"</h3>"+"<p>Download latest version from <a href=\'"+params.pluginspage+"\'>here</a></p>";}}function concatVars(vars){var out="";for(var key in vars){if(vars[key]){out+=[key]+\'=\'+asString(vars[key])+\'&\';}}return out.substring(0,out.length-1);}function asString(obj){switch(typeOf(obj)){case\'string\':return\'"\'+obj.replace(new RegExp(\'(["\\\\\\\\])\',\'g\'),\'\\\\$1\')+\'"\';case\'array\':return\'[\'+map(obj,function(el){return asString(el);}).join(\',\')+\']\';case\'function\':return\'"function()"\';case\'object\':var str=[];for(var prop in obj){if(obj.hasOwnProperty(prop)){str.push(\'"\'+prop+\'":\'+asString(obj[prop]));}}return\'{\'+str.join(\',\')+\'}\';}return String(obj).replace(/\\s/g," ").replace(/\\\'/g,"\\"");}function typeOf(obj){if(obj===null||obj===undefined){return false;}var type=typeof obj;return(type==\'object\'&&obj.push)?\'array\':type;}if(window.attachEvent){window.attachEvent("onbeforeunload",function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){};});}function map(arr,func){var newArr=[];for(var i in arr){if(arr.hasOwnProperty(i)){newArr[i]=func(arr[i]);}}return newArr;}return root;}if(typeof jQuery==\'function\'){(function($){$.fn.extend({flashembed:function(params,flashvars){return this.each(function(){flashembed(this,params,flashvars);});}});})(jQuery);}flashembed=flashembed||{};flashembed.getVersion=function(){var version=[0,0];if(navigator.plugins&&typeof navigator.plugins["Shockwave Flash"]=="object"){var _d=navigator.plugins["Shockwave Flash"].description;if(typeof _d!="undefined"){_d=_d.replace(/^.*\\s+(\\S+\\s+\\S+$)/,"$1");var _m=parseInt(_d.replace(/^(.*)\\..*$/,"$1"),10);var _r=/r/.test(_d)?parseInt(_d.replace(/^.*r(.*)$/,"$1"),10):0;version=[_m,_r];}}else if(window.ActiveXObject){try{var _a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");}catch(e){try{_a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");version=[6,0];_a.AllowScriptAccess="always";}catch(ee){if(version[0]==6){return;}}try{_a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");}catch(eee){}}if(typeof _a=="object"){_d=_a.GetVariable("$version");if(typeof _d!="undefined"){_d=_d.replace(/^\\S+\\s+(.*)$/,"$1").split(",");version=[parseInt(_d[0],10),parseInt(_d[2],10)];}}}return version;};flashembed.isSupported=function(version){var now=flashembed.getVersion();var ret=(now[0]>version[0])||(now[0]==version[0]&&now[1]>=version[1]);return ret;};\n';
153
+ Html += '</script>\n';
154
+ }
155
+ Html += '</head>';
156
+ Html += '<body>';
157
+ Html += '<div id="player" style="margin-top: 20px; margin-left: 10px;">';
158
+ if( ext != 'mp3' && ext != 'flv' && ext != 'mp4' )
159
+ {
160
+ switch( ext )
161
+ {
162
+ case 'm4v':
163
+ case 'm4a':
164
+ case 'avi':
165
+ case 'mpg':
166
+ case 'mpeg':
167
+ case 'qt':
168
+ case 'mov': {
169
+
170
+ var contentType = 'video/mpeg'; // Default content type
171
+ if( ext == 'm4v' )
172
+ contentType = 'video/x-m4v';
173
+ else if( ext == 'm4a' )
174
+ contentType = 'audio/x-m4a';
175
+ else if( ext == 'avi' )
176
+ contentType = 'video/avi';
177
+ else if( ext == 'qt' || ext == 'mov' )
178
+ contentType = 'video/quicktime';
179
+
180
+ Html += this._getQuickTime(contentType);
181
+ }; break;
182
+ case 'wma':
183
+ case 'wmv':
184
+ case 'asf': {
185
+ Html += this._getWinPlayer();
186
+ }; break;
187
+ case 'rm': {
188
+ Html += this._getRealPlayer();
189
+ }; break;
190
+ case 'swf': {
191
+ Html += this._getFlash();
192
+ }; break;
193
+ }
194
+ }
195
+ Html += '</div>';
196
+ if( ext == 'mp3' || ext == 'flv' || ext == 'mp4' )
197
+ {
198
+ Html += '<script type="text/javascript">\n';
199
+ if( ext == 'mp3' )
200
+ Html += this._getFlowPlayer('player', 24, true);
201
+ else
202
+ Html += this._getFlowPlayer('player', this.m_height, true);
203
+ Html += '</script>\n';
204
+ }
205
+ Html += '</body>';
206
+ Html += '</html>';
207
+ this.m_player_wnd.document.write( Html );
208
+ this.m_player_wnd.document.close();
209
+ this.m_player_wnd.focus();
210
+ return false;
211
+ }
212
+
213
+ /*
214
+ Private functions:
215
+ */
216
+ this._doFlowPlayer = function() {
217
+
218
+ var height = this.m_height;
219
+ if( this._doFlowPlayer.arguments.length > 0 )
220
+ height = this._doFlowPlayer.arguments[0];
221
+
222
+ flashembed(
223
+ this.m_player_div,
224
+ {src: this.m_flash_src, width: this.m_width, height: height },
225
+ {config: { autoPlay: this.m_auto_play?true:false, autoBuffering: false, initialScale: 'scale', showFullScreenButton: false, showMenu: false, videoFile: this.m_media_url, loop: false, autoRewind: true } }
226
+ );
227
+
228
+ return false;
229
+ }
230
+
231
+ this._getFlowPlayer = function(destDiv) {
232
+
233
+ var height = this.m_height;
234
+ if( this._getFlowPlayer.arguments.length > 1 )
235
+ height = this._getFlowPlayer.arguments[1];
236
+
237
+ var auto_play = this.m_auto_play;
238
+ if( this._getFlowPlayer.arguments.length > 2 )
239
+ auto_play = this._getFlowPlayer.arguments[2];
240
+
241
+ var Html = '';
242
+ Html += "flashembed(\n";
243
+ Html += " '"+ destDiv +"', \n";
244
+ Html += " {src: '"+ this.m_flash_src +"', width: "+ this.m_width +", height: "+ height +"}, \n";
245
+ Html += " {config: { autoPlay: "+ (auto_play?'true':'false') +", autoBuffering: false, initialScale: 'scale', showFullScreenButton: false, showMenu: false, videoFile: '"+ this.m_media_url +"', loop: false, autoRewind: true } } \n";
246
+ Html += " ); \n";
247
+ return Html;
248
+ }
249
+
250
+ this._getFlash = function() {
251
+
252
+ var Html = '';
253
+ Html += '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0"'+ (this.m_auto_play?'':' play="false"') +' width="'+ this.m_width +'" height="'+ this.m_height +'" menu="true">\n';
254
+ Html += ' <param name="movie" value="'+ this.m_media_url +'" />\n';
255
+ Html += ' <param name="quality" value="high" />\n';
256
+ Html += ' <param name="menu" value="true" />\n';
257
+ Html += ' <param name="scale" value="noorder" />\n';
258
+ Html += ' <param name="quality" value="high" />\n';
259
+ Html += ' <embed src="'+ this.m_media_url +'" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash"'+ (this.m_auto_play?'':' play="false"') +' width="'+ this.m_width +'" height="'+ this.m_height +'" menu="true"></embed>';
260
+ Html += '</object>\n';
261
+ return Html;
262
+ }
263
+
264
+ this._getRealPlayer = function() {
265
+
266
+ var Html = '';
267
+ Html += '<object id="realplayer" classid="clsid:cfcdaa03-8be4-11cf-b84b-0020afbbccfa" width="'+ this.m_width +'" height="'+ this.m_height +'">\n';
268
+ Html += ' <param name="src" value="'+ this.m_media_url +'" />\n';
269
+ Html += ' <param name="autostart" value="'+ (this.m_auto_play?'true':'false') +'" />\n';
270
+ Html += ' <param name="controls" value="imagewindow,controlpanel" />\n';
271
+ Html += ' <embed src="'+ this.m_media_url +'" width="'+ this.m_width +'" height="'+ this.m_height +'" autostart="'+(this.m_auto_play?'true':'false')+'" controls="imagewindow,controlpanel" type="audio/x-pn-realaudio-plugin"></embed>';
272
+ Html += '</object>\n';
273
+ return Html;
274
+ }
275
+
276
+ this._getWinPlayer = function() {
277
+
278
+ var Html = '';
279
+ Html += '<object id="winplayer" classid="clsid:6BF52A52-394A-11d3-B153-00C04F79FAA6" width="'+ this.m_width +'" height="'+ this.m_height +'" standby="Media is loading..." type="application/x-oleobject">\n';
280
+ Html += ' <param name="url" value="'+ this.m_media_url +'" />\n';
281
+ Html += ' <param name="AutoStart" value="'+ (this.m_auto_play?'true':'false') +'" />\n';
282
+ Html += ' <param name="AutoSize" value="true" />\n';
283
+ Html += ' <param name="AllowChangeDisplaySize" value="true" />\n';
284
+ Html += ' <param name="standby" value="Media is loading..." />\n';
285
+ Html += ' <param name="AnimationAtStart" value="true" />\n';
286
+ Html += ' <param name="scale" value="aspect" />\n';
287
+ Html += ' <param name="ShowControls" value="true" />\n';
288
+ Html += ' <param name="ShowCaptioning" value="false" />\n';
289
+ Html += ' <param name="ShowDisplay" value="false" />\n';
290
+ Html += ' <param name="ShowStatusBar" value="false" />\n';
291
+ Html += ' <embed type="application/x-mplayer2" src="'+ this.m_media_url +'" width="'+ this.m_width +'" height="'+ this.m_height +'" scale="aspect" AutoStart="'+ (this.m_auto_play?'true':'false') +'" ShowDisplay="0" ShowStatusBar="0" AutoSize="1" AnimationAtStart="1" AllowChangeDisplaySize="1" ShowControls="1"></embed>\n';
292
+ Html += '</object>\n';
293
+ return Html;
294
+ }
295
+
296
+ this._getQuickTime = function() {
297
+
298
+ var contentType = 'video/mpeg';
299
+ if( this._getQuickTime.arguments.length > 0 )
300
+ contentType = this._getQuickTime.arguments[0];
301
+
302
+ var Html = '';
303
+ Html += '<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" width="'+ this.m_width +'" height="'+ this.m_height +'" codebase="http://www.apple.com/qtactivex/qtplugin.cab">\n';
304
+ Html += ' <param name="src" value="'+ this.m_media_url +'" />\n';
305
+ Html += ' <param name="href" value="'+ this.m_media_url +'" />\n';
306
+ Html += ' <param name="scale" value="aspect" />\n';
307
+ Html += ' <param name="controller" value="true" />\n';
308
+ Html += ' <param name="autoplay" value="'+ (this.m_auto_play?'true':'false') +'" />\n';
309
+ Html += ' <param name="bgcolor" value="000000" />\n';
310
+ Html += ' <param name="pluginspage" value="http://www.apple.com/quicktime/download/" />\n';
311
+ Html += ' <embed src="'+ this.m_media_url +'" type="'+ contentType +'" width="'+ this.m_width +'" height="'+ this.m_height +'" scale="aspect" cache="true" bgcolor="000000" autoplay="'+ (this.m_auto_play?'true':'false') +'" controller="true" pluginspage="http://www.apple.com/quicktime/download/"></embed>';
312
+ Html += '</object>\n';
313
+ return Html;
314
+ }
315
+
316
+ this._getExt = function(File) {
317
+ // First remove any anchor if any
318
+ var anchor = File.indexOf('#');
319
+ if( anchor > -1 )
320
+ File = File.substring(0, anchor);
321
+ // Next, get rid of the query string if exists..
322
+ var question = File.indexOf('?');
323
+ if( question > -1 )
324
+ File = File.substring(0, question);
325
+ // Find the last dot at the end of the file
326
+ var dot = File.lastIndexOf('.');
327
+ if( dot > -1 )
328
+ return File.substring(dot+1).toLowerCase();
329
+ return false; // Unable to find a file extension
330
+ }
331
+
332
+ this._passthru = function() {
333
+
334
+ // If we have ourselves an iPhone, let the media passtru when clicked
335
+ if( navigator.userAgent.indexOf("iPhone") != -1 )
336
+ return true; // Let this client download and play the content itself.
337
+
338
+ // Add additional user agents which cannot handle embed, object or flash code here:
339
+
340
+ return false;
341
+ }
342
+
343
+ this._closePrevPlayer = function() {
344
+ if( this.m_player_div )
345
+ {
346
+ document.getElementById( this.m_player_div ).innerHTML = '';
347
+ document.getElementById( this.m_player_div ).style.display = 'none';
348
+ this.m_player_div = false;
349
+ }
350
+ if( this.m_player_wnd )
351
+ {
352
+ this.m_player_wnd.close();
353
+ this.m_player_wnd = false;
354
+ }
355
+ }
356
+ }
357
+
358
+ /**
359
+ * flashembed 0.31. Adobe Flash embedding script
360
+ *
361
+ * http://flowplayer.org/tools/flash-embed.html
362
+ *
363
+ * Copyright (c) 2008 Tero Piirainen (tipiirai@gmail.com)
364
+ *
365
+ * Released under the MIT License:
366
+ * http://www.opensource.org/licenses/mit-license.php
367
+ *
368
+ * >> Basically you can do anything you want but leave this header as is <<
369
+ *
370
+ * version 0.01 - 03/11/2008
371
+ * version 0.31 - Tue Jul 22 2008 06:30:31 GMT+0200 (GMT+02:00)
372
+ */
373
+ function flashembed(root,userParams,flashvars){function getHTML(){var html="";if(typeof flashvars=='function'){flashvars=flashvars();}if(navigator.plugins&&navigator.mimeTypes&&navigator.mimeTypes.length){html='<embed type="application/x-shockwave-flash" ';if(params.id){extend(params,{name:params.id});}for(var key in params){if(params[key]!==null){html+=[key]+'="'+params[key]+'"\n\t';}}if(flashvars){html+='flashvars=\''+concatVars(flashvars)+'\'';}html+='/>';}else{html='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" ';html+='width="'+params.width+'" height="'+params.height+'"';if(!params.id&&document.all){params.id="_"+(""+Math.random()).substring(5);}if(params.id){html+=' id="'+params.id+'"';}html+='>';html+='\n\t<param name="movie" value="'+params.src+'" />';params.id=params.src=params.width=params.height=null;for(var k in params){if(params[k]!==null){html+='\n\t<param name="'+k+'" value="'+params[k]+'" />';}}if(flashvars){html+='\n\t<param name="flashvars" value=\''+concatVars(flashvars)+'\' />';}html+="</object>";if(debug){alert(html);}}return html;}function init(name){var timer=setInterval(function(){var doc=document;var el=doc.getElementById(name);if(el){flashembed(el,userParams,flashvars);clearInterval(timer);}else if(doc&&doc.getElementsByTagName&&doc.getElementById&&doc.body){clearInterval(timer);}},13);return true;}function extend(to,from){if(from){for(key in from){if(from.hasOwnProperty(key)){to[key]=from[key];}}}}var params={src:'#',width:'100%',height:'100%',version:null,onFail:null,expressInstall:null,debug:false,bgcolor:'#ffffff',allowfullscreen:true,allowscriptaccess:'always',quality:'high',type:'application/x-shockwave-flash',pluginspage:'http://www.adobe.com/go/getflashplayer'};if(typeof userParams=='string'){userParams={src:userParams};}extend(params,userParams);var version=flashembed.getVersion();var required=params.version;var express=params.expressInstall;var debug=params.debug;if(typeof root=='string'){var el=document.getElementById(root);if(el){root=el;}else{return init(root);}}if(!root){return;}if(!required||flashembed.isSupported(required)){params.onFail=params.version=params.expressInstall=params.debug=null;root.innerHTML=getHTML();return root.firstChild;}else if(params.onFail){var ret=params.onFail.call(params,flashembed.getVersion(),flashvars);if(ret){root.innerHTML=ret;}}else if(required&&express&&flashembed.isSupported([6,65])){extend(params,{src:express});flashvars={MMredirectURL:location.href,MMplayerType:'PlugIn',MMdoctitle:document.title};root.innerHTML=getHTML();}else{if(root.innerHTML.replace(/\s/g,'')!==''){}else{root.innerHTML="<h2>Flash version "+required+" or greater is required</h2>"+"<h3>"+(version[0]>0?"Your version is "+version:"You have no flash plugin installed")+"</h3>"+"<p>Download latest version from <a href='"+params.pluginspage+"'>here</a></p>";}}function concatVars(vars){var out="";for(var key in vars){if(vars[key]){out+=[key]+'='+asString(vars[key])+'&';}}return out.substring(0,out.length-1);}function asString(obj){switch(typeOf(obj)){case'string':return'"'+obj.replace(new RegExp('(["\\\\])','g'),'\\$1')+'"';case'array':return'['+map(obj,function(el){return asString(el);}).join(',')+']';case'function':return'"function()"';case'object':var str=[];for(var prop in obj){if(obj.hasOwnProperty(prop)){str.push('"'+prop+'":'+asString(obj[prop]));}}return'{'+str.join(',')+'}';}return String(obj).replace(/\s/g," ").replace(/\'/g,"\"");}function typeOf(obj){if(obj===null||obj===undefined){return false;}var type=typeof obj;return(type=='object'&&obj.push)?'array':type;}if(window.attachEvent){window.attachEvent("onbeforeunload",function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){};});}function map(arr,func){var newArr=[];for(var i in arr){if(arr.hasOwnProperty(i)){newArr[i]=func(arr[i]);}}return newArr;}return root;}if(typeof jQuery=='function'){(function($){$.fn.extend({flashembed:function(params,flashvars){return this.each(function(){flashembed(this,params,flashvars);});}});})(jQuery);}flashembed=flashembed||{};flashembed.getVersion=function(){var version=[0,0];if(navigator.plugins&&typeof navigator.plugins["Shockwave Flash"]=="object"){var _d=navigator.plugins["Shockwave Flash"].description;if(typeof _d!="undefined"){_d=_d.replace(/^.*\s+(\S+\s+\S+$)/,"$1");var _m=parseInt(_d.replace(/^(.*)\..*$/,"$1"),10);var _r=/r/.test(_d)?parseInt(_d.replace(/^.*r(.*)$/,"$1"),10):0;version=[_m,_r];}}else if(window.ActiveXObject){try{var _a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");}catch(e){try{_a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");version=[6,0];_a.AllowScriptAccess="always";}catch(ee){if(version[0]==6){return;}}try{_a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");}catch(eee){}}if(typeof _a=="object"){_d=_a.GetVariable("$version");if(typeof _d!="undefined"){_d=_d.replace(/^\S+\s+(.*)$/,"$1").split(",");version=[parseInt(_d[0],10),parseInt(_d[2],10)];}}}return version;};flashembed.isSupported=function(version){var now=flashembed.getVersion();var ret=(now[0]>version[0])||(now[0]==version[0]&&now[1]>=version[1]);return ret;};
powerpress.php ADDED
@@ -0,0 +1,655 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: Blubrry Powerpress
4
+ Plugin URI: http://www.blubrry.com/powerpress/
5
+ Description: <a href="http://www.blubrry.com/powerpress/" target="_blank">Blubrry Powerpress</a> adds podcasting support to your blog. Features include: media player, 3rd party statistics and iTunes integration.
6
+ Version: 0.2.0
7
+ Author: Blubrry
8
+ Author URI: http://www.blubrry.com/
9
+ Change Log:
10
+ 2008-08-05 - v0.2.0: First beta release of Blubrry Powerpress plugin.
11
+ 2008-08-05 - v0.1.2: Fixed minor bugs, trimming empty hour values in duration.
12
+ 2008-08-04 - v0.1.1: Fixed minor bugs, PHP_EOL define, check if function exists for older versions of wordpress and more.
13
+ 2008-08-04 - v0.1.0: Tentative initial release of Blubrry Powerpress plugin.
14
+
15
+ Contributors:
16
+ Angelo Mandato, CIO RawVoice - Plugin founder and architect, mp3info class and javascript media player
17
+ Pat McSweeny, Developer for RawVoice - powerpress.php and powperpressoptions.php
18
+
19
+ Credits:
20
+ getID3(), License: GPL 2.0+ by James Heinrich <info [at] getid3.org> http://www.getid3.org
21
+ Note: getid3.php analyze() function modified to prevent redundant filesize() function call.
22
+ FlowPlayer, License: GPL 3.0+ http://flowplayer.org/; source: http://flowplayer.org/download.html
23
+ flashembed(), License: MIT by Tero Piirainen (tipiirai [at] gmail.com)
24
+ Note: code found at bottom of player.js
25
+
26
+ Copyright 2008 RawVoice Inc. (http://www.rawvoice.com)
27
+
28
+ License: Apache License version 2.0 (http://www.apache.org/licenses/)
29
+
30
+ This project uses source that is GPL licensed that, for the sake of this plugin,
31
+ is interpreted as GPL version 3.0 for compatibility with Apache 2.0 license.
32
+ */
33
+
34
+ define('POWERPRESS_VERSION', '0.1.1' );
35
+
36
+ // Define variables, advanced users could define these in their own wp-config.php so lets not try to re-define
37
+ if( !defined('POWERPRESS_PLUGIN_PATH') )
38
+ define('POWERPRESS_PLUGIN_PATH', '/wp-content/plugins/powerpress/');
39
+ if( !defined('POWERPRESS_LINK_SEPARATOR') )
40
+ define('POWERPRESS_LINK_SEPARATOR', '|');
41
+ if( !defined('PHP_EOL') )
42
+ define('PHP_EOL', "\n"); // We need this variable defined for new lines.
43
+
44
+ function powerpress_content($content)
45
+ {
46
+ global $post;
47
+
48
+ if( is_feed() )
49
+ return $content; // We don't want to do anything to the feed
50
+
51
+ // Powerpress settings:
52
+ $Powerpress = get_option('powerpress_general');
53
+
54
+ // Get the enclosure data
55
+ $enclosureData = get_post_meta($post->ID, 'enclosure', true);
56
+
57
+ if( !$enclosureData )
58
+ {
59
+ $EnclosureURL = '';
60
+ if( $Powerpress['process_podpress'] )
61
+ {
62
+ //$Settings = get_option('powerpress_general');
63
+ $podPressMedia = get_post_meta($post->ID, 'podPressMedia', true);
64
+ if( $podPressMedia )
65
+ $EnclosureURL = $podPressMedia[0]['URI'];
66
+ if( !$EnclosureURL )
67
+ return $content;
68
+ if( strpos($EnclosureURL, 'http://' ) !== 0 )
69
+ $EnclosureURL = $Powerpress['default_url'] . $EnclosureURL;
70
+ }
71
+ }
72
+ else
73
+ {
74
+ list($EnclosureURL, $EnclosureSize, $EnclosureType) = split("\n", $enclosureData);
75
+ $EnclosureURL = trim($EnclosureURL);
76
+ }
77
+
78
+ // Just in case, if there's no URL lets escape!
79
+ if( !$EnclosureURL )
80
+ return $content;
81
+
82
+
83
+ if( !isset($Powerpress['display_player']) )
84
+ $Powerpress['display_player'] = 1;
85
+ if( !isset($Powerpress['player_function']) )
86
+ $Powerpress['player_function'] = 1;
87
+ if( !isset($Powerpress['podcast_link']) )
88
+ $Powerpress['podcast_link'] = 1;
89
+
90
+ // The blog owner doesn't want anything displayed, so don't bother wasting anymore CPU cycles
91
+ if( $Powerpress['display_player'] == 0 )
92
+ return $content;
93
+
94
+ // Create the redirect URL
95
+ $EnclosureURL = 'http://' .str_replace('http://', '', $Powerpress['redirect1'] . $Powerpress['redirect2'] . $Powerpress['redirect3'] . $EnclosureURL );
96
+
97
+ // Build links for player
98
+ $player_links = '';
99
+ switch( $Powerpress['player_function'] )
100
+ {
101
+ case 1: { // On page and new window
102
+ //$player_links .= "<a href=\"$EnclosureURL\" title=\"Play in page\" onclick=\"return bpPlayer.PlayInPage(this.href, 'powerpress_player_{$post->ID}');\">Play in page</a>".PHP_EOL;
103
+ //$player_links .= " ".POWERPRESS_LINK_SEPARATOR." ";
104
+ $player_links .= "<a href=\"$EnclosureURL\" title=\"Play in new window\" onclick=\"return bpPlayer.PlayNewWindow(this.href);\">Play in new window</a>".PHP_EOL;
105
+ }; break;
106
+ case 2: { // Play in page only
107
+ //$player_links .= "<a href=\"$EnclosureURL\" title=\"Play in page\" onclick=\"return bpPlayer.PlayInPage(this.href, 'powerpress_player_{$post->ID}');\">Play in page</a>".PHP_EOL;
108
+ }; break;
109
+ case 3: { //Play in new window only
110
+ $player_links .= "<a href=\"$EnclosureURL\" title=\"Play in new window\" onclick=\"return bpPlayer.PlayNewWindow(this.href);\">Play in new window</a>".PHP_EOL;
111
+ }; break;
112
+ }//end switch
113
+
114
+ if( $Powerpress['podcast_link'] == 1 )
115
+ {
116
+ if( $player_links )
117
+ $player_links .= ' '. POWERPRESS_LINK_SEPARATOR .' ';
118
+ $player_links .= "<a href=\"$EnclosureURL\" title=\"Download\">Download</a>".PHP_EOL;
119
+ }
120
+
121
+ $new_content = '';
122
+ if( $Powerpress['player_function'] == 1 || $Powerpress['player_function'] == 2 ) // We have some kind of on-line player
123
+ {
124
+ $new_content .= '<div class="powerpress_player" id="powerpress_player_'. $post->ID .'"></div>'.PHP_EOL;
125
+ $new_content .= '<script type="text/javascript">'.PHP_EOL;
126
+ $new_content .= "bpPlayer.PlayInPage('$EnclosureURL', 'powerpress_player_{$post->ID}');\n";
127
+ $new_content .= '</script>'.PHP_EOL;
128
+ }
129
+ if( $player_links )
130
+ $new_content .= '<p class="powerpress_links">Podcast: ' . $player_links . '</p>'.PHP_EOL;
131
+
132
+ if( $new_content == '' )
133
+ return $content;
134
+
135
+ switch( $Powerpress['display_player'] )
136
+ {
137
+ case 1: { // Below posts
138
+ return $content.$new_content;
139
+ }; break;
140
+ case 2: { // Above posts
141
+ return $new_content.$content;
142
+ }; break;
143
+ }
144
+ }//end function
145
+
146
+ add_action('the_content', 'powerpress_content');
147
+
148
+ function powerpress_header()
149
+ {
150
+ $PowerpressPluginURL = powerpress_get_root_url();
151
+ ?>
152
+ <script type="text/javascript" src="<?php echo $PowerpressPluginURL; ?>player.js"></script>
153
+ <script type="text/javascript">
154
+ var bpPlayer = new jsMediaPlayer('<?php echo $PowerpressPluginURL; ?>FlowPlayerClassic.swf');
155
+ </script>
156
+ <style type="text/css">
157
+ .powerpress_player_old {
158
+ margin-bottom: 3px;
159
+ margin-top: 10px;
160
+ }
161
+ </style>
162
+ <?php
163
+ }
164
+
165
+ add_action('wp_head', 'powerpress_header');
166
+
167
+ function powerpress_rss2_ns(){
168
+ if( defined('PODPRESS_VERSION') || isset($GLOBALS['podcasting_player_id']) )
169
+ return; // Another podcasting plugin is enabled...
170
+
171
+ // Okay, lets add the namespace
172
+ echo 'xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"'."\n";
173
+ }
174
+
175
+ add_action('rss2_ns', 'powerpress_rss2_ns');
176
+
177
+
178
+ function powerpress_rss2_head()
179
+ {
180
+ global $powerpress_feed, $powerpress_itunes_explicit, $powerpress_itunes_talent_name, $powerpress_default_url, $powerpress_process_podpress;
181
+ $powerpress_feed = false; // By default, lets not apply the feed settings...
182
+
183
+ if( defined('PODPRESS_VERSION') || isset($GLOBALS['podcasting_player_id']) )
184
+ return; // Another podcasting plugin is enabled...
185
+
186
+
187
+ $feed = get_query_var( 'feed' );
188
+ if( $feed != 'feed' && $feed != 'podcast' && $feed != 'rss2' )
189
+ return; // This is definitely not our kind of feed
190
+
191
+ $GeneralSettings = get_option('powerpress_general');
192
+ $powerpress_default_url = $GeneralSettings['default_url'];
193
+ $powerpress_process_podpress = $GeneralSettings['process_podpress'];
194
+
195
+ $Feed = get_option('powerpress_feed');
196
+
197
+ // First, determine if powerpress should even be rewriting this feed...
198
+ if( $Feed['apply_to'] == '0' )
199
+ return; // Okay, we're not suppoed to touch this feed, it's bad enough we added the iTunes namespace, escape!!!
200
+
201
+ if( $Feed['apply_to'] == '3' && $feed != 'podcast' )
202
+ return; // This is definitely not our kind of feed
203
+
204
+ if( $Feed['apply_to'] == '2' && $feed != 'podcast' )
205
+ {
206
+ // If there's anything else in the query string, then something's going on and we shouldn't touch the feed
207
+ if( isset($GLOBALS['wp_query']->query_vars) && count($GLOBALS['wp_query']->query_vars) > 1 )
208
+ return;
209
+ }
210
+
211
+ // if( $Feed['apply_to'] == '1' ) { } // continue...
212
+ // Case 1 (default, we apply to all of the rss2 feeds...)
213
+
214
+ // We made it this far, lets write stuff to the feed!
215
+ $powerpress_feed = true; // Okay, lets continue...
216
+ if( $Feed )
217
+ {
218
+ while( list($key,$value) = each($Feed) )
219
+ $Feed[$key] = htmlentities($value, ENT_NOQUOTES, 'UTF-8');
220
+ reset($Feed);
221
+ }
222
+
223
+ echo '<!-- podcast_generator="Blubrry Powerpress/'. POWERPRESS_VERSION .'" -->'.PHP_EOL;
224
+
225
+ if( defined('POWERPRESS_NEW_FEED_URL') )
226
+ echo "\t<itunes:new-feed-url>".POWERPRESS_NEW_FEED_URL.'</itunes:new-feed-url>'.PHP_EOL;
227
+
228
+ if( $Feed['itunes_summary'] )
229
+ echo "\t".'<itunes:summary>'. htmlentities( $Feed['itunes_summary'], ENT_NOQUOTES, 'UTF-8') .'</itunes:summary>'.PHP_EOL;
230
+ else
231
+ echo "\t".'<itunes:summary>'. htmlentities( get_bloginfo('description'), ENT_NOQUOTES, 'UTF-8') .'</itunes:summary>'.PHP_EOL;
232
+
233
+ // explicit options:
234
+ $explicit = array("no", "yes", "clean");
235
+
236
+ $powerpress_itunes_explicit = $explicit[$Feed['itunes_explicit']];
237
+ $powerpress_itunes_talent_name = $Feed['itunes_talent_name'];
238
+
239
+ if( $powerpress_itunes_explicit )
240
+ echo "\t".'<itunes:explicit>' . $powerpress_itunes_explicit . '</itunes:explicit>'.PHP_EOL;
241
+ if( $Feed['itunes_image'] )
242
+ {
243
+ echo "\t".'<itunes:image href="' . $Feed['itunes_image'] . '" />'.PHP_EOL;
244
+ }
245
+ else
246
+ {
247
+ echo "\t".'<itunes:image href="' . powerpress_get_root_url() . 'itunes_default.jpg" />'.PHP_EOL;
248
+ }
249
+
250
+ if( $Feed['itunes_talent_name'] && $Feed['email'] )
251
+ {
252
+ echo "\t".'<itunes:owner>'.PHP_EOL;
253
+ echo "\t\t".'<itunes:name>' . $Feed['itunes_talent_name'] . '</itunes:name>'.PHP_EOL;
254
+ echo "\t\t".'<itunes:email>' . $Feed['email'] . '</itunes:email>'.PHP_EOL;
255
+ echo "\t".'</itunes:owner>'.PHP_EOL;
256
+ }
257
+ if( $Feed['copyright'] )
258
+ echo "\t".'<copyright>'. str_replace( array('&copy;', '(c)', '(C)'), '&#xA9;', htmlentities($Feed['copyright'], ENT_NOQUOTES, 'UTF-8')) . '</copyright>'.PHP_EOL;
259
+ if( $Feed['itunes_subtitle'] )
260
+ echo "\t".'<itunes:subtitle>' . htmlentities($Feed['itunes_subtitle'], ENT_NOQUOTES, 'UTF-8') . '</itunes:subtitle>'.PHP_EOL;
261
+ if( $Feed['itunes_keywords'] )
262
+ echo "\t".'<itunes:keywords>' . htmlentities($Feed['itunes_keywords'], ENT_NOQUOTES, 'UTF-8') . '</itunes:keywords>'.PHP_EOL;
263
+ if( $Feed['itunes_talent_name'] && $Feed['email'] )
264
+ echo "\t".'<managingEditor>'. $Feed['email'] .' ('. htmlentities($Feed['itunes_talent_name'], ENT_NOQUOTES, 'UTF-8') .')</managingEditor>'.PHP_EOL;
265
+ if( $Feed['rss2_image'] )
266
+ {
267
+ echo"\t". '<image>' .PHP_EOL;
268
+ echo "\t\t".'<title>' . htmlentities(get_bloginfo('name'), ENT_NOQUOTES, 'UTF-8') . '</title>'.PHP_EOL;
269
+ echo "\t\t".'<url>' . $Feed['rss2_image'] . '</url>'.PHP_EOL;
270
+ echo "\t\t".'<link>'. get_bloginfo('url') . '</link>' . PHP_EOL;
271
+ echo "\t".'</image>' . PHP_EOL;
272
+ }
273
+ else // Use the default image
274
+ {
275
+ echo"\t". '<image>' .PHP_EOL;
276
+ echo "\t\t".'<title>' . htmlentities(get_bloginfo('name'), ENT_NOQUOTES, 'UTF-8') . '</title>'.PHP_EOL;
277
+ echo "\t\t".'<url>' . powerpress_get_root_url() . 'rss_default.jpg</url>'.PHP_EOL;
278
+ echo "\t\t".'<link>'. get_bloginfo('url') . '</link>' . PHP_EOL;
279
+ echo "\t".'</image>' . PHP_EOL;
280
+ }
281
+
282
+ $Categories = powerpress_itunes_categories();
283
+ $Cat1 = false; $Cat2 = false; $Cat3 = false;
284
+ if( $Feed['itunes_cat_1'] != '' )
285
+ list($Cat1, $SubCat1) = split('-', $Feed['itunes_cat_1']);
286
+ if( $Feed['itunes_cat_2'] != '' )
287
+ list($Cat2, $SubCat2) = split('-', $Feed['itunes_cat_2']);
288
+ if( $Feed['itunes_cat_3'] != '' )
289
+ list($Cat3, $SubCat3) = split('-', $Feed['itunes_cat_3']);
290
+
291
+ if( $Cat1 )
292
+ {
293
+ $CatDesc = $Categories[$Cat1.'-00'];
294
+ $SubCatDesc = $Categories[$Cat1.'-'.$SubCat1];
295
+ if( $Cat1 != $Cat2 && $SubCat1 == '00' )
296
+ {
297
+ echo "\t".'<itunes:category text="'. htmlspecialchars($CatDesc) .'" />'.PHP_EOL;
298
+ }
299
+ else
300
+ {
301
+ echo "\t".'<itunes:category text="'. htmlspecialchars($CatDesc) .'">'.PHP_EOL;
302
+ if( $SubCat1 != '00' )
303
+ echo "\t\t".'<itunes:category text="'. htmlspecialchars($SubCatDesc) .'" />'.PHP_EOL;
304
+
305
+ // End this category set
306
+ if( $Cat1 != $Cat2 )
307
+ echo "\t".'</itunes:category>'.PHP_EOL;
308
+ }
309
+ }
310
+
311
+ if( $Cat2 )
312
+ {
313
+ $CatDesc = $Categories[$Cat2.'-00'];
314
+ $SubCatDesc = $Categories[$Cat2.'-'.$SubCat2];
315
+
316
+ // It's a continuation of the last category...
317
+ if( $Cat1 == $Cat2 )
318
+ {
319
+ if( $SubCat2 != '00' )
320
+ echo "\t\t".'<itunes:category text="'. htmlspecialchars($SubCatDesc) .'" />'.PHP_EOL;
321
+
322
+ // End this category set
323
+ if( $Cat2 != $Cat3 )
324
+ echo "\t".'</itunes:category>'.PHP_EOL;
325
+ }
326
+ else // This is not a continuation, lets start a new category set
327
+ {
328
+ if( $Cat2 != $Cat3 && $SubCat2 == '00' )
329
+ {
330
+ echo "\t".'<itunes:category text="'. htmlspecialchars($CatDesc) .'" />'.PHP_EOL;
331
+ }
332
+ else // We have nested values
333
+ {
334
+ if( $Cat1 != $Cat2 ) // Start a new category set
335
+ echo "\t".'<itunes:category text="'. htmlspecialchars($CatDesc) .'">'.PHP_EOL;
336
+ if( $SubCat2 != '00' )
337
+ echo "\t\t".'<itunes:category text="'. htmlspecialchars($SubCatDesc) .'" />'.PHP_EOL;
338
+ if( $Cat2 != $Cat3 ) // End this category set
339
+ echo "\t".'</itunes:category>'.PHP_EOL;
340
+ }
341
+ }
342
+ }
343
+
344
+ if( $Cat3 )
345
+ {
346
+ $CatDesc = $Categories[$Cat3.'-00'];
347
+ $SubCatDesc = $Categories[$Cat3.'-'.$SubCat3];
348
+
349
+ // It's a continuation of the last category...
350
+ if( $Cat2 == $Cat3 )
351
+ {
352
+ if( $SubCat3 != '00' )
353
+ echo "\t\t".'<itunes:category text="'. htmlspecialchars($SubCatDesc) .'" />'.PHP_EOL;
354
+
355
+ // End this category set
356
+ echo "\t".'</itunes:category>'.PHP_EOL;
357
+ }
358
+ else // This is not a continuation, lets start a new category set
359
+ {
360
+ if( $Cat2 != $Cat3 && $SubCat3 == '00' )
361
+ {
362
+ echo "\t".'<itunes:category text="'. htmlspecialchars($CatDesc) .'" />'.PHP_EOL;
363
+ }
364
+ else // We have nested values
365
+ {
366
+ if( $Cat2 != $Cat3 ) // Start a new category set
367
+ echo "\t".'<itunes:category text="'. htmlspecialchars($CatDesc) .'">'.PHP_EOL;
368
+ if( $SubCat3 != '00' )
369
+ echo "\t\t".'<itunes:category text="'. htmlspecialchars($SubCatDesc) .'" />'.PHP_EOL;
370
+ // End this category set
371
+ echo "\t".'</itunes:category>'.PHP_EOL;
372
+ }
373
+ }
374
+ }
375
+ }
376
+
377
+ add_action('rss2_head', 'powerpress_rss2_head');
378
+
379
+ function powerpress_rss2_item()
380
+ {
381
+ global $powerpress_feed, $powerpress_itunes_explicit, $powerpress_itunes_talent_name, $powerpress_default_url, $powerpress_process_podpress, $post;
382
+ $duration = false;
383
+ // are we processing a feed that powerpress should handle
384
+ if( $powerpress_feed == false )
385
+ return;
386
+
387
+ // Check and see if we're working with a podcast episode
388
+ $enclosureData = get_post_meta($post->ID, 'enclosure', true);
389
+ if( !$enclosureData )
390
+ {
391
+ $EnclosureURL = '';
392
+ if( $powerpress_process_podpress )
393
+ {
394
+ //$Settings = get_option('powerpress_general');
395
+ $podPressMedia = get_post_meta($post->ID, 'podPressMedia', true);
396
+ if( $podPressMedia )
397
+ {
398
+ $EnclosureURL = $podPressMedia[0]['URI'];
399
+ if( strpos($EnclosureURL, 'http://' ) !== 0 )
400
+ $EnclosureURL = $powerpress_default_url . $EnclosureURL;
401
+ $EnclosureSize = $podPressMedia[0]['size'];
402
+ $duration = $podPressMedia[0]['duration'];
403
+ $EnclosureType = false;
404
+ $UrlParts = parse_url($EnclosureURL);
405
+ if( $UrlParts['path'] )
406
+ {
407
+ // using functions that already exist in Wordpress when possible:
408
+ $FileType = wp_check_filetype($UrlParts['path']);
409
+ if( $FileType )
410
+ $EnclosureType = $FileType['type'];
411
+ }
412
+
413
+ if( $EnclosureType && $EnclosureSize && $EnclosureURL )
414
+ echo "\t\t".'<enclosure url="' . $EnclosureURL . '" length="'. $EnclosureSize .'" type="'. $EnclosureType .'" />'.PHP_EOL;
415
+ else
416
+ return;
417
+ }
418
+ else
419
+ return;
420
+ }
421
+ else
422
+ return;
423
+ }
424
+
425
+ if( !$duration )
426
+ $duration = get_post_meta($post->ID, 'itunes:duration', true);
427
+
428
+ // Get the post tags:
429
+ $tagobject = wp_get_post_tags( $post->ID );
430
+ if( count($tagobject) )
431
+ {
432
+ $tags = array();
433
+ for($c = 0; $c < count($tagobject) && $c < 12; $c++) // iTunes only accepts up to 12 keywords
434
+ $tags[] = htmlentities($tagobject[$c]->name, ENT_NOQUOTES, 'UTF-8');
435
+
436
+ echo "\t\t<itunes:keywords>" . implode(",", $tags) . '</itunes:keywords>'.PHP_EOL;
437
+ }
438
+
439
+ $content_no_html = strip_tags($post->post_content);
440
+
441
+ echo "\t\t<itunes:subtitle>". powerpress_smart_trim($content_no_html, 250, true) .'</itunes:subtitle>'.PHP_EOL;
442
+ if( defined('POWERPRESS_ITEM_SUMMARY') )
443
+ echo "\t\t<itunes:summary>". powerpress_smart_trim($content_no_html, 4000) .'</itunes:summary>'.PHP_EOL;
444
+ if( $powerpress_itunes_talent_name )
445
+ echo "\t\t<itunes:author>" . $powerpress_itunes_talent_name . '</itunes:author>'.PHP_EOL;
446
+
447
+ if( $powerpress_itunes_explicit )
448
+ echo "\t\t<itunes:explicit>" . $powerpress_itunes_explicit . '</itunes:explicit>'.PHP_EOL;
449
+
450
+ if( $duration && preg_match('/^(\d{1,2}:){0,2}\d{1,2}$/i', $duration) ) // Include duration if it is valid
451
+ echo "\t\t<itunes:duration>" . ltrim($duration, '0:') . '</itunes:duration>'.PHP_EOL;
452
+ }
453
+
454
+ add_action('rss2_item', 'powerpress_rss2_item');
455
+
456
+ function powerpress_filter_rss_enclosure($content)
457
+ {
458
+ // Add the redirect url if there is one...
459
+ $RedirectURL = powerpress_get_redirect_url();
460
+ if( $RedirectURL )
461
+ $content = str_replace('http://', $RedirectURL, $content);
462
+ return $content;
463
+ }
464
+
465
+ add_filter('rss_enclosure', 'powerpress_filter_rss_enclosure');
466
+
467
+ function powerpress_do_podcast_feed()
468
+ {
469
+ global $wp_query;
470
+ $wp_query->get_posts();
471
+ load_template(ABSPATH . 'wp-rss2.php');
472
+ }
473
+
474
+ function powerpress_init()
475
+ {
476
+ add_feed('podcast', 'powerpress_do_podcast_feed');
477
+ }
478
+
479
+ add_action('init', 'powerpress_init');
480
+
481
+
482
+ function powerpress_posts_join($join) {
483
+ if ( is_feed() && get_query_var('feed') == 'podcast' ) {
484
+ global $wpdb;
485
+ // Future feature: only display posts that have enclosures...
486
+ // $query->set('cat','-20,-21,-22');
487
+ //$join .= " LEFT JOIN {$wpdb->postmeta} ";
488
+ //$join .= " ON {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id AND {$wpdb->postmeta}.meta_key = 'enclosure' ";
489
+ // $wpdb->postmeta
490
+ }
491
+ return $join;
492
+ }
493
+
494
+ add_filter('posts_join', 'powerpress_posts_join' );
495
+
496
+ //add_filter('pre_get_posts','my_cat_exclude');
497
+
498
+ /*
499
+ Helper functions:
500
+ */
501
+
502
+ function powerpress_itunes_categories($PrefixSubCategories = false)
503
+ {
504
+ $temp = array();
505
+ $temp['01-00'] = 'Arts';
506
+ $temp['01-01'] = 'Design';
507
+ $temp['01-02'] = 'Fashion & Beauty';
508
+ $temp['01-03'] = 'Food';
509
+ $temp['01-04'] = 'Literature';
510
+ $temp['01-05'] = 'Performing Arts';
511
+ $temp['01-06'] = 'Visual Arts';
512
+
513
+ $temp['02-00'] = 'Business';
514
+ $temp['02-01'] = 'Business News';
515
+ $temp['02-02'] = 'Careers';
516
+ $temp['02-03'] = 'Investing';
517
+ $temp['02-04'] = 'Management & Marketing';
518
+ $temp['02-05'] = 'Shopping';
519
+
520
+ $temp['03-00'] = 'Comedy';
521
+
522
+ $temp['04-00'] = 'Education';
523
+ $temp['04-01'] = 'Education Technology';
524
+ $temp['04-02'] = 'Higher Education';
525
+ $temp['04-03'] = 'K-12';
526
+ $temp['04-04'] = 'Language Courses';
527
+ $temp['04-05'] = 'Training';
528
+
529
+ $temp['05-00'] = 'Games & Hobbies';
530
+ $temp['05-01'] = 'Automotive';
531
+ $temp['05-02'] = 'Aviation';
532
+ $temp['05-03'] = 'Hobbies';
533
+ $temp['05-04'] = 'Other Games';
534
+ $temp['05-05'] = 'Video Games';
535
+
536
+ $temp['06-00'] = 'Government & Organizations';
537
+ $temp['06-01'] = 'Local';
538
+ $temp['06-02'] = 'National';
539
+ $temp['06-03'] = 'Non-Profit';
540
+ $temp['06-04'] = 'Regional';
541
+
542
+ $temp['07-00'] = 'Health';
543
+ $temp['07-01'] = 'Alternative Health';
544
+ $temp['07-02'] = 'Fitness & Nutrition';
545
+ $temp['07-03'] = 'Self-Help';
546
+ $temp['07-04'] = 'Sexuality';
547
+
548
+ $temp['08-00'] = 'Kids & Family';
549
+
550
+ $temp['09-00'] = 'Music';
551
+
552
+ $temp['10-00'] = 'News & Politics';
553
+
554
+ $temp['11-00'] = 'Religion & Spirituality';
555
+ $temp['11-01'] = 'Buddhism';
556
+ $temp['11-02'] = 'Christianity';
557
+ $temp['11-03'] = 'Hinduism';
558
+ $temp['11-04'] = 'Islam';
559
+ $temp['11-05'] = 'Judaism';
560
+ $temp['11-06'] = 'Other';
561
+ $temp['11-07'] = 'Spirituality';
562
+
563
+ $temp['12-00'] = 'Science & Medicine';
564
+ $temp['12-01'] = 'Medicine';
565
+ $temp['12-02'] = 'Natural Sciences';
566
+ $temp['12-03'] = 'Social Sciences';
567
+
568
+ $temp['13-00'] = 'Society & Culture';
569
+ $temp['13-01'] = 'History';
570
+ $temp['13-02'] = 'Personal Journals';
571
+ $temp['13-03'] = 'Philosophy';
572
+ $temp['13-04'] = 'Places & Travel';
573
+
574
+ $temp['14-00'] = 'Sports & Recreation';
575
+ $temp['14-01'] = 'Amateur';
576
+ $temp['14-02'] = 'College & High School';
577
+ $temp['14-03'] = 'Outdoor';
578
+ $temp['14-04'] = 'Professional';
579
+
580
+ $temp['15-00'] = 'Technology';
581
+ $temp['15-01'] = 'Gadgets';
582
+ $temp['15-02'] = 'Tech News';
583
+ $temp['15-03'] = 'Podcasting';
584
+ $temp['15-04'] = 'Software How-To';
585
+
586
+ $temp['16-00'] = 'TV & Film';
587
+
588
+ if( $PrefixSubCategories )
589
+ {
590
+ while( list($key,$val) = each($temp) )
591
+ {
592
+ $parts = split('-', $key);
593
+ $cat = $parts[0];
594
+ $subcat = $parts[1];
595
+
596
+ if( $subcat != '00' )
597
+ $temp[$key] = $temp[$cat.'-00'].' > '.$val;
598
+ }
599
+ reset($temp);
600
+ }
601
+
602
+ return $temp;
603
+ }
604
+
605
+ function powerpress_get_root_url()
606
+ {
607
+ return get_bloginfo('url') . POWERPRESS_PLUGIN_PATH;
608
+ }
609
+
610
+ function powerpress_smart_trim($value, $char_limit = 250, $remove_new_lines = false)
611
+ {
612
+ if( strlen($value) > $char_limit )
613
+ {
614
+ $new_value = substr($new_value, 0, $char_limit);
615
+ // Look back at most 50 characters...
616
+ $eos = strrpos($new_value, '.');
617
+ $eol = strrpos($new_value, "\n");
618
+ // If the end of line is longer than the end of sentence and we're not loosing too much of our string...
619
+ if( $eol > $eos && $eol > (strlen($new_value)-50) )
620
+ $return = substr($new_value, 0, $eol);
621
+ // If the end of sentence is longer than the end of line and we're not loosing too much of our string...
622
+ else if( $eos > $eol && $eos > (strlen($new_value)-50) )
623
+ $return = substr($new_value, 0, $eos);
624
+ else // Otherwise, just add some dots to the end
625
+ $return = substr($new_value, 0, -3).'...';
626
+ }
627
+ else
628
+ {
629
+ $return = $value;
630
+ }
631
+
632
+ if( $remove_new_lines )
633
+ $return = str_replace( array("\n", "\r", "\t"), array(' ', '', ' '), $return );
634
+ return $return;
635
+ }
636
+
637
+ function powerpress_get_redirect_url()
638
+ {
639
+ global $powerpress_redirect_url;
640
+ if( !isset($powerpress_redirect_url) )
641
+ {
642
+ $Powerpress = get_option('powerpress_general');
643
+ $powerpress_redirect_url = 'http://' .str_replace('http://', '', $Powerpress['redirect1'] . $Powerpress['redirect2'] . $Powerpress['redirect3'] );
644
+ }
645
+ return $powerpress_redirect_url;
646
+ }
647
+ /*
648
+ End Helper Functions
649
+ */
650
+
651
+ // Are we in the admin?
652
+ if( is_admin() )
653
+ require_once('powerpressadmin.php');
654
+
655
+ ?>
powerpressadmin.php ADDED
@@ -0,0 +1,970 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ function powerpress_admin_menu() {
4
+ if( function_exists('add_meta_box') ) // Otherwise we're using a version of wordpress that is not supported.
5
+ add_meta_box('id', 'Podcast Episode', 'powerpress_meta_box', 'post', 'normal');
6
+ add_options_page('Blubrry Powerpress Settings', 'Blubrry Powerpress', 8, 'powerpress/powerpress.php', 'powerpress_admin_page');
7
+ }
8
+
9
+ add_action('admin_menu', 'powerpress_admin_menu');
10
+
11
+ function powerpress_meta_box($object, $box)
12
+ {
13
+ $DurationHH = '';
14
+ $DurationMM = '';
15
+ $DurationSS = '';
16
+ $EnclosureURL = '';
17
+ $EnclosureLength = '';
18
+
19
+ if( $object->ID )
20
+ {
21
+ $enclosureArray = get_post_meta($object->ID, 'enclosure', true);
22
+ list($EnclosureURL, $EnclosureLength, $EnclosureType) = explode("\n", $enclosureArray);
23
+ $iTunesDuration = get_post_meta($object->ID, 'itunes:duration', true);
24
+ list($DurationHH, $DurationMM, $DurationSS) = explode(':', $iTunesDuration);
25
+ }
26
+
27
+ if( $EnclosureURL )
28
+ {
29
+ ?>
30
+ <div>
31
+ <input type="checkbox" name="Powerpress[change_podcast]" id="powerpress_change" value="1" onchange="javascript:document.getElementById('powerpress_podcast_box').style.display=(this.checked?'block':'none');" /> Modify existing podcast episode
32
+ </div>
33
+ <?php
34
+ }
35
+ else
36
+ {
37
+ echo '<input type="hidden" name="Powerpress[new_podcast]" value="1" />'.PHP_EOL;
38
+ }
39
+ ?>
40
+ <style type="text/css">
41
+ #powerpress_podcast_box {
42
+
43
+ }
44
+ #powerpress_podcast_box label {
45
+ width: 120px;
46
+ font-weight: bold;
47
+ font-size: 110%;
48
+ display: inline;
49
+ position: absolute;
50
+ top: 0;
51
+ left: 0;
52
+ }
53
+ #powerpress_podcast_box .powerpress_row {
54
+ margin-top: 10px;
55
+ margin-bottom: 10px;
56
+ position: relative;
57
+ }
58
+ #powerpress_podcast_box .powerpress_row_content {
59
+ margin-left: 120px;
60
+ }
61
+ </style>
62
+ <div id="powerpress_podcast_box"<?php if( $EnclosureURL ) echo ' style="display:none;"'; ?>>
63
+ <?php
64
+ if( $EnclosureURL )
65
+ {
66
+ ?>
67
+ <div class="powerpress_row">
68
+ <label>Remove</label>
69
+ <div class="powerpress_row_content">
70
+ <input type="checkbox" name="Powerpress[remove_podcast]" id="powerpress_remove" value="1" onchange="javascript:document.getElementById('powerpress_podcast_edit').style.display=(this.checked?'none':'block');" />
71
+ Podcast episode will be removed from this post upon save
72
+ </div>
73
+ </div>
74
+ <?php
75
+ }
76
+ ?>
77
+ <div id="powerpress_podcast_edit">
78
+ <div class="powerpress_row">
79
+ <label for "Powerpress[url]">Media URL</label>
80
+ <div class="powerpress_row_content">
81
+ <input id="powerpress_url" name="Powerpress[url]" value="<?php echo $EnclosureURL; ?>" style="width: 70%; font-size: 90%;" />
82
+ </div>
83
+ </div>
84
+ <div class="powerpress_row">
85
+ <label for "size">File Size</label>
86
+ <div class="powerpress_row_content">
87
+ <div style="margin-bottom: 4px;">
88
+ <input id="powerpress_set_size" name="Powerpress[set_size]" value="0" type="radio" checked /> Auto detect file size
89
+ </div>
90
+ <div>
91
+ <input id="powerpress_set_size" name="Powerpress[set_size]" value="1" type="radio" /> Specify:
92
+ <input id="powerpress_size" name="Powerpress[size]" value="<?php echo $EnclosureLength; ?>" style="width: 110px; font-size: 90%;" /> in bytes
93
+ </div>
94
+ </div>
95
+ </div>
96
+ <div class="powerpress_row">
97
+ <label for "size">Duration</label>
98
+ <div class="powerpress_row_content">
99
+ <div style="margin-bottom: 4px;">
100
+ <input id="powerpress_set_duration" name="Powerpress[set_duration]" value="0" type="radio" checked /> Auto detect duration (mp3's only)
101
+ </div>
102
+ <div style="margin-bottom: 4px;">
103
+ <input id="powerpress_set_duration" name="Powerpress[set_duration]" value="1" type="radio" /> Specify:
104
+ <input id="powerpress_duration_hh" name="Powerpress[duration_hh]" maxlength="2" value="<?php echo $DurationHH; ?>" style="width: 24px; font-size: 90%; text-align: right;" /><strong>:</strong>
105
+ <input id="powerpress_duration_mm" name="Powerpress[duration_mm]" maxlength="2" value="<?php echo $DurationMM; ?>" style="width: 24px; font-size: 90%; text-align: right;" /><strong>:</strong>
106
+ <input id="powerpress_duration_ss" name="Powerpress[duration_ss]" maxlength="2" value="<?php echo $DurationSS; ?>" style="width: 24px; font-size: 90%; text-align: right;" /> HH:MM:SS
107
+ </div>
108
+ <div>
109
+ <input id="powerpress_set_duration" name="Powerpress[set_duration]" value="-1" type="radio" /> Not specified
110
+ </div>
111
+ </div>
112
+ </div>
113
+ </div>
114
+ </div>
115
+ <?php
116
+ }
117
+
118
+
119
+ function powerpress_edit_post($post_ID) {
120
+
121
+ $Powerpress = $_POST['Powerpress'];
122
+
123
+ if( $Powerpress['remove_podcast'] == 1 )
124
+ {
125
+ delete_post_meta( $post_ID, 'enclosure');
126
+ delete_post_meta( $post_ID, 'itunes:duration');
127
+ }
128
+ else if( @$Powerpress['change_podcast'] == 1 || @$Powerpress['new_podcast'] == 1 )
129
+ {
130
+ // No URL specified, then it's not really a podcast to save
131
+ if( $Powerpress['url'] == '' )
132
+ return;
133
+
134
+ // Initialize the important variables:
135
+ $MediaURL = $Powerpress['url'];
136
+ if( strpos($MediaURL, 'http://') !== 0 ) // If the url entered does not start with a http://
137
+ {
138
+ $Settings = get_option('powerpress_general');
139
+ $MediaURL = @$Settings['default_url'] . $MediaURL;
140
+ }
141
+
142
+ $FileSize = '';
143
+ $ContentType = '';
144
+ $Duration = false;
145
+
146
+ // Get the content type based on the file extension, first we have to remove query string if it exists
147
+ $UrlParts = parse_url($Powerpress['url']);
148
+ if( $UrlParts['path'] )
149
+ {
150
+ // using functions that already exist in Wordpress when possible:
151
+ $FileType = wp_check_filetype($UrlParts['path']);
152
+ if( $FileType )
153
+ $ContentType = $FileType['type'];
154
+
155
+ /*
156
+ $FileParts = pathinfo($UrlParts['path']);
157
+ if( $FileParts )
158
+ {
159
+
160
+ $ContentType = powerpress_mimetypes($FileParts['extension']);
161
+ }
162
+ */
163
+ }
164
+
165
+ //Set the duration specified by the user
166
+ if( $Powerpress['set_duration'] == 1 ) // specify duration
167
+ {
168
+ $Duration = sprintf('%02d:%02d:%02d', $Powerpress['duration_hh'], $Powerpress['duration_mm'], $Powerpress['duration_ss'] );
169
+ }
170
+
171
+ //Set the file size specified by the user
172
+ if( $Powerpress['set_size'] == 1 ) // specify file size
173
+ {
174
+ $FileSize = $Powerpress['size'];
175
+ }
176
+
177
+ if( $Powerpress['set_size'] == 0 || $Powerpress['set_duration'] == 0 )
178
+ {
179
+ // Lets use the mp3info class:
180
+ require_once('mp3info.class.php');
181
+
182
+ $Mp3Info = new Mp3Info();
183
+ if( $ContentType == 'audio/mpeg' && $Powerpress['set_duration'] == 0 )
184
+ {
185
+ $Mp3Data = $Mp3Info->GetMp3Info($MediaURL);
186
+ if( $Mp3Data )
187
+ {
188
+ if( @$Powerpress['set_size'] == 0 )
189
+ $FileSize = $Mp3Info->GetContentLength();
190
+ $Duration = $Mp3Data['playtime_string'];
191
+ if( substr_count($Duration, ':' ) == 0 )
192
+ {
193
+ if( $Duration < 60 )
194
+ $Duration = '00:00:'.$Duration;
195
+ }
196
+ else if( substr_count($Duration, ':' ) == 1 )
197
+ {
198
+ $Duration = '00:'.$Duration;
199
+ }
200
+ }
201
+ }
202
+ else // Just get the file size
203
+ {
204
+ $Headers = wp_get_http_headers($MediaURL);
205
+ if( $headers && $headers['content-length'] )
206
+ {
207
+ $FileSize = (int) $headers['content-length'];
208
+ }
209
+ }
210
+ }
211
+
212
+ $EnclosureData = $MediaURL . "\n" . $FileSize . "\n". $ContentType;
213
+
214
+ if( @$Powerpress['new_podcast'] )
215
+ {
216
+ add_post_meta($post_ID, 'enclosure', $EnclosureData, true);
217
+ if( $Duration !== false )
218
+ add_post_meta($post_ID, 'itunes:duration', $Duration, true);
219
+ }
220
+ else
221
+ {
222
+ update_post_meta($post_ID, 'enclosure', $EnclosureData);
223
+ if( $Duration !== false )
224
+ {
225
+ if( !update_post_meta($post_ID, 'itunes:duration', $Duration) )
226
+ add_post_meta($post_ID, 'itunes:duration', $Duration, true); // If we can't update it, lets try to add it
227
+ }
228
+ if( $Powerpress['set_duration'] == -1 ) // Special case, lets remove the duration since they set Not specified
229
+ {
230
+ delete_post_meta( $post_ID, 'itunes:duration');
231
+ }
232
+ }
233
+ }
234
+
235
+ // If we're moving from draft to published, maybe we should ping iTunes?
236
+ if($_POST['prev_status'] == 'draft' && $_POST['publish'] == 'Publish' )
237
+ {
238
+ // Next double check we're looking at a podcast episode...
239
+ $Enclosure = get_post_meta($post_ID, 'enclosure', true);
240
+ if( $Enclosure )
241
+ {
242
+ $Settings = get_option('powerpress_general');
243
+ if( $Settings['ping_itunes'] && $Settings['itunes_url'] )
244
+ {
245
+ $PingResults = powerpress_ping_itunes($Settings['itunes_url']);
246
+ //mail( 'email@host.com', 'Ping iTunes Results', implode("\n", $PingResults) ); // Let me know how the ping went.
247
+ }
248
+ }
249
+ }
250
+
251
+ // And we're done!
252
+ }
253
+
254
+ add_action('edit_post', 'powerpress_edit_post');
255
+
256
+
257
+ function powerpress_admin_head()
258
+ {
259
+ if( strstr($_GET['page'], 'powerpress.php' ) )
260
+ {
261
+ echo '<script type="text/javascript">'.PHP_EOL;
262
+ echo 'function powerpress_show_field(id, show){'.PHP_EOL;
263
+ echo ' if( document.getElementById(id).nodeName == "SPAN" )'.PHP_EOL;
264
+ echo ' document.getElementById(id).style.display = (show?"inline":"none");'.PHP_EOL;
265
+ echo ' else'.PHP_EOL;
266
+ echo ' document.getElementById(id).style.display = (show?"block":"none");'.PHP_EOL;
267
+ echo '}'.PHP_EOL;
268
+ echo '</script>'.PHP_EOL;
269
+
270
+ echo '<style type="text/css">'.PHP_EOL;
271
+ echo '.powerpress-notice {'.PHP_EOL;
272
+ echo ' margin-top: 10px;'.PHP_EOL;
273
+ echo ' margin-bottom: 10px;'.PHP_EOL;
274
+ echo ' line-height: 29px;'.PHP_EOL;
275
+ echo ' font-size: 12px;'.PHP_EOL;
276
+ echo ' text-align: center;'.PHP_EOL;
277
+ echo ' border-width: 1px;'.PHP_EOL;
278
+ echo ' border-style: solid;'.PHP_EOL;
279
+ echo ' font-weight: bold;'.PHP_EOL;
280
+ echo '}'.PHP_EOL;
281
+ echo '.powerpress-error {'.PHP_EOL;
282
+ echo ' margin-top: 10px;'.PHP_EOL;
283
+ echo ' margin-bottom: 10px;'.PHP_EOL;
284
+ echo ' line-height: 29px;'.PHP_EOL;
285
+ echo ' font-size: 12px;'.PHP_EOL;
286
+ echo ' text-align: center;'.PHP_EOL;
287
+ echo ' border-width: 1px;'.PHP_EOL;
288
+ echo ' border-style: solid;'.PHP_EOL;
289
+ echo ' border-color: #c69;'.PHP_EOL;
290
+ echo ' background-color: #ffeff7;'.PHP_EOL;
291
+ echo ' font-weight: bold;'.PHP_EOL;
292
+ echo '}'.PHP_EOL;
293
+ echo '</style>'.PHP_EOL;
294
+ }
295
+ }
296
+
297
+ add_action('admin_head', 'powerpress_admin_head');
298
+
299
+ function powerpress_admin_page()
300
+ {
301
+ global $wp_version;
302
+
303
+ $VersionDiff = version_compare($wp_version, 2.5);
304
+ if( $VersionDiff < 0 )
305
+ echo '<div class="powerpress-error">Blubrry Powerpress requires Wordpress version 2.5 or greater.</div>';
306
+
307
+
308
+ $UploadArray = wp_upload_dir();
309
+ $upload_path = rtrim( substr($UploadArray['path'], 0, 0 - strlen($UploadArray['subdir']) ), '\\/').'/powerpress/';
310
+
311
+ if( !file_exists($upload_path) )
312
+ $SupportUploads = @mkdir($upload_path, 0777);
313
+ else
314
+ $SupportUploads = true;
315
+
316
+
317
+
318
+ if( isset($_POST[ 'Submit' ]) )
319
+ {
320
+ $urlImages = rtrim( substr($UploadArray['url'], 0, 0 - strlen($UploadArray['subdir']) ), '/').'/powerpress/';
321
+
322
+ // Save the posted value in the database
323
+ $Feed = $_POST['Feed'];
324
+ $General = $_POST['General'];
325
+
326
+ // New iTunes image
327
+ if( @$_POST['itunes_image_checkbox'] == 1 )
328
+ {
329
+ $filename = str_replace(" ", "_", basename($_FILES['itunes_image_file']['name']) );
330
+ $temp = $_FILES['itunes_image_file']['tmp_name'];
331
+
332
+ if( file_exists($upload_path . $filename ) )
333
+ {
334
+ $filenameParts = pathinfo($filename);
335
+ do {
336
+ $filename_no_ext = substr($filenameParts['basename'], 0, (strlen($filenameParts['extension'])+1) * -1 );
337
+ $filename = sprintf('%s-%03d.%s', $filename_no_ext, rand(0, 999), $filenameParts['extension'] );
338
+ } while( file_exists($upload_path . $filename ) );
339
+ }
340
+
341
+ // Check the image...
342
+ $ImageData = getimagesize($temp);
343
+ if( $ImageData && ( $ImageData[2] == IMAGETYPE_JPEG || $ImageData[2] == IMAGETYPE_PNG ) && $ImageData[0] == $ImageData[1] ) // Just check that it is an image, the correct image type and that the image is square
344
+ {
345
+ move_uploaded_file($temp, $upload_path . $filename);
346
+ $Feed['itunes_image'] = $urlImages . $filename;
347
+ }
348
+ else
349
+ {
350
+ echo '<div class="powerpress-error">Invalid iTunes image: ' . htmlspecialchars($_FILES['itunes_image_file']['name']) . '</div>';
351
+ }
352
+ }
353
+
354
+ // New RSS2 image
355
+ if( @$_POST['rss2_image_checkbox'] == 1 )
356
+ {
357
+ $filename = str_replace(" ", "_", basename($_FILES['rss2_image_file']['name']) );
358
+ $temp = $_FILES['rss2_image_file']['tmp_name'];
359
+
360
+ if( file_exists($upload_path . $filename ) )
361
+ {
362
+ $filenameParts = pathinfo($filename);
363
+ do {
364
+ $filename_no_ext = substr($filenameParts['basename'], 0, (strlen($filenameParts['extension'])+1) * -1 );
365
+ $filename = sprintf('%s-%03d.%s', $filename_no_ext, rand(0, 999), $filenameParts['extension'] );
366
+ } while( file_exists($upload_path . $filename ) );
367
+ }
368
+
369
+ if( getimagesize($temp) ) // Just check that it is an image, we may add more to this later
370
+ {
371
+ move_uploaded_file($temp, $upload_path . $filename);
372
+ $Feed['rss2_image'] = $urlImages . $filename;
373
+ }
374
+ else
375
+ {
376
+ echo '<div class="powerpress-error">Invalid RSS image: ' . htmlspecialchars($_FILES['rss2_image_file']['name']) . '</div>';
377
+ }
378
+ }
379
+
380
+ // Wordpress adds slashes to everything, but since we're storing everything serialized, lets remove them...
381
+ while( list($key,$value) = each($General) )
382
+ $General[$key] = stripslashes($value);
383
+ reset($General);
384
+ while( list($key,$value) = each($Feed) )
385
+ $Feed[$key] = stripslashes($value);
386
+ reset($Feed);
387
+
388
+ // Update the settings in the database:
389
+ update_option( 'powerpress_general', $General);
390
+ update_option( 'powerpress_feed', $Feed );
391
+
392
+ ?>
393
+ <div class="updated powerpress-notice"><?php _e('Blubrry Powerpress settings saved.'); ?></div>
394
+ <?php
395
+
396
+ if( @$_POST['TestiTunesPing'] == 1 )
397
+ {
398
+ $PingResults = powerpress_ping_itunes($General['itunes_url']);
399
+ if( @$PingResults['success'] )
400
+ {
401
+ ?>
402
+ <div class="updated powerpress-notice">iTunes Ping Successful. Podcast Feed URL: <?php echo $PingResults['feed_url']; ?>
403
+ </div>
404
+ <?php
405
+ }
406
+ else
407
+ {
408
+ echo '<div class="powerpress-error">' . htmlspecialchars($PingResults['content']) . '</div>';
409
+ }
410
+ }
411
+
412
+ }
413
+
414
+ // Get the general settings
415
+ $General = get_option('powerpress_general');
416
+
417
+ // Get previous podpress settings if no general settings set
418
+ if( !$General )
419
+ $PodpressData = get_option('podPress_config');
420
+
421
+ if( !$General ) // If no general settings, lets pre-populate or copy from podpress
422
+ {
423
+ $General = array();
424
+ $General['process_podpress'] = 1;
425
+ $General['display_player'] = 1;
426
+ $General['player_function'] = 1;
427
+ $General['podcast_link'] = 1;
428
+ $General['ping_itunes'] = 1;
429
+ $PodpressData = get_option('podPress_config');
430
+ if( $PodpressData ) // If no general settings, lets set defaults or copy from podpress.
431
+ {
432
+ ?>
433
+ <div class="updated powerpress-notice"><?php _e('Podpress settings detected. Please click \'Save Changes\' to apply detected settings.'); ?></div>
434
+ <?php
435
+ // Lets try to copy settings from podpress
436
+ $General['default_url'] = $PodpressData['mediaWebPath'];
437
+ if( substr($General['default_url'], 0, -1) != '/' )
438
+ $General['default_url'] .= '/'; // Add the trailing slash, donno it's not there...
439
+
440
+ // Insert the blubrry redirect
441
+ if( isset($PodpressData['statBluBrryProgramKeyword']) )
442
+ {
443
+ $General['redirect1'] = 'http://media.blubrry.com/'.$PodpressData['statBluBrryProgramKeyword'].'/';
444
+ }
445
+
446
+ // Insert the Podtrac redirect
447
+ if( $PodpressData['enable3rdPartyStats'] == 'PodTrac' )
448
+ {
449
+ if( $General['redirect1'] )
450
+ $General['redirect2'] = 'http://www.podtrac.com/pts/redirect.mp3/';
451
+ else
452
+ $General['redirect1'] = 'http://www.podtrac.com/pts/redirect.mp3/';
453
+ }
454
+
455
+ if( $PodpressData['contentDownload'] == 'enabled' )
456
+ $General['podcast_link'] = 1;
457
+ else
458
+ $General['podcast_link'] = 0;
459
+
460
+ if( $PodpressData['contentPlayer'] == 'both' )
461
+ $General['player_function'] = 1;
462
+ else if( $PodpressData['contentPlayer'] == 'inline' )
463
+ $General['player_function'] = 2;
464
+ else if( $PodpressData['contentPlayer'] == 'popup' )
465
+ $General['player_function'] = 3;
466
+ else
467
+ $General['player_function'] = 0;
468
+
469
+ if( $PodpressData['contentPlayer'] == 'start' )
470
+ $General['display_player'] = 2;
471
+ else
472
+ $General['display_player'] = 1;
473
+
474
+ $General['itunes_url'] = 'http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id='. $PodpressData['iTunes']['FeedID'];
475
+ }
476
+ }
477
+ // Format the data for printing in html
478
+ while( list($key,$value) = each($General) )
479
+ $General[$key] = htmlspecialchars($value);
480
+ reset($General);
481
+
482
+ // Load feed settings
483
+ $FeedSettings = get_option('powerpress_feed');
484
+
485
+ if( !$FeedSettings ) // If no feed settings, lets set defaults or copy from podpress.
486
+ {
487
+ $FeedSettings = array();
488
+ $FeedSettings['apply_to'] = 1; // Default, apply to all the rss2 feeds
489
+
490
+ if( $PodpressData )
491
+ {
492
+ $FeedSettings['itunes_image'] = $PodpressData['iTunes']['image'];
493
+ if( strstr($FeedSettings['itunes_image'], 'powered_by_podpress') )
494
+ $FeedSettings['itunes_image'] = ''; // We're not using podpress anymore
495
+
496
+ $FeedSettings['itunes_summary'] = $PodpressData['iTunes']['summary'];
497
+ $FeedSettings['itunes_talent_name'] = $PodpressData['iTunes']['author'];
498
+ $FeedSettings['itunes_subtitle'] = $PodpressData['iTunes']['subtitle'];
499
+ $FeedSettings['itunes_keywords'] = $PodpressData['iTunes']['keywords'];
500
+ $FeedSettings['copyright'] = $PodpressData['rss_copyright'];
501
+ // Categories are tricky...
502
+ $iTunesCategories = powerpress_itunes_categories(true);
503
+ for( $x = 0; $x < 3; $x++ )
504
+ {
505
+ $CatDesc = str_replace(':', ' > ', $PodpressData['iTunes']['category'][$x]);
506
+ $CatKey = array_search($CatDesc, $iTunesCategories);
507
+ if( $CatKey )
508
+ $FeedSettings['itunes_cat_'.($x+1)] = $CatKey;
509
+ }
510
+
511
+ if( $PodpressData['iTunes']['explicit'] == 'No' )
512
+ $FeedSettings['itunes_explicit'] = 0;
513
+ else if( $PodpressData['iTunes']['explicit'] == 'Yes' )
514
+ $FeedSettings['itunes_explicit'] = 1;
515
+ else if( $PodpressData['iTunes']['explicit'] == 'Clean' )
516
+ $FeedSettings['itunes_explicit'] = 2;
517
+ }
518
+
519
+ // Lastly, lets try to get the RSS image from the database
520
+ $RSSImage = get_option('rss_image');
521
+ if( $RSSImage )
522
+ $FeedSettings['rss2_image'] = $RSSImage;
523
+ if( strstr($FeedSettings['rss2_image'], 'powered_by_podpress') )
524
+ $FeedSettings['rss2_image'] = ''; // We're not using podpress anymore
525
+ $AdminEmail = get_option('admin_email');
526
+ if( $AdminEmail )
527
+ $FeedSettings['email'] = $AdminEmail;
528
+
529
+ //var_dump($FeedSettings);
530
+ //var_dump($PodpressData);
531
+ //exit;
532
+ }
533
+ // Format the data for printing in html
534
+ while( list($key,$value) = each($FeedSettings) )
535
+ $FeedSettings[$key] = htmlspecialchars($value);
536
+ reset($FeedSettings);
537
+
538
+ // Now display the options editing screen
539
+
540
+ echo '<div class="wrap">';
541
+
542
+ // Start the form
543
+ ?>
544
+
545
+
546
+ <form enctype="multipart/form-data" method="post" action="<?php echo str_replace( '%7E', '~', $_SERVER['REQUEST_URI']); ?>">
547
+
548
+
549
+ <?php wp_nonce_field('update-options'); ?>
550
+
551
+ <h2><?php _e("Basic Settings"); ?></h2>
552
+
553
+ <table class="form-table">
554
+ <tr valign="top">
555
+ <th scope="row"><?php _e("Default Media URL"); ?></th>
556
+ <td>
557
+ <input type="text" style="width: 80%;" name="General[default_url]" value="<?php echo $General['default_url']; ?>" maxlength="250" />
558
+ <p>URL above will prefix entered file names that do not start with 'http://'. URL above must end with a trailing slash.
559
+ You may leave blank if you always enter the complete URL to your media when creating podcast episodes.
560
+ </p>
561
+ <p>e.g. http://example.com/mediafolder/</p>
562
+ </td>
563
+ </tr>
564
+
565
+ <tr valign="top">
566
+ <th scope="row">
567
+
568
+ <?php _e("Podpress Episodes"); ?></th>
569
+ <td>
570
+ <select name="General[process_podpress]">
571
+ <?php
572
+ $options = array(0=>'Ignore', 1=>'Include in Posts and Feeds');
573
+
574
+ while( list($value,$desc) = each($options) )
575
+ echo "\t<option value=\"$value\"". ($General['process_podpress']==$value?' selected':''). ">$desc</option>\n";
576
+
577
+ ?>
578
+ </select> (includes podcast episodes previously created in Podpress)
579
+ </td>
580
+ </tr>
581
+
582
+ <tr valign="top">
583
+ <th scope="row"><?php _e("iTunes URL"); ?></th>
584
+ <td>
585
+ <input type="text" style="width: 80%;" name="General[itunes_url]" value="<?php echo $General['itunes_url']; ?>" maxlength="250" />
586
+ <p>Click the following link to <a href="https://phobos.apple.com/WebObjects/MZFinance.woa/wa/publishPodcast" target="_blank" title="Publish a Podcast on iTunes">Publish a Podcast on iTunes</a>.
587
+ Once your podcast is listed on iTunes, enter your one click subscription URL above.
588
+ </p>
589
+ <p>e.g. http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=000000000</p>
590
+ </td>
591
+ </tr>
592
+
593
+ <?php
594
+ $OpenSSLSupport = extension_loaded('openssl');
595
+ ?>
596
+ <tr valign="top">
597
+ <th scope="row">
598
+
599
+ <?php _e("Ping iTunes"); ?></th>
600
+ <td>
601
+ <select name="General[ping_itunes]"<?php if( $OpenSSLSupport == false ) echo ' disabled'; ?>>
602
+ <?php
603
+ $options = array(0=>'No ', 1=>'Yes ');
604
+
605
+ if( $OpenSSLSupport == false )
606
+ $value = 0;
607
+
608
+ while( list($value,$desc) = each($options) )
609
+ echo "\t<option value=\"$value\"". ($General['ping_itunes']==$value?' selected':''). ">$desc</option>\n";
610
+
611
+ ?>
612
+ </select> (Notify iTunes when you publish a new episode.)
613
+ <p><input name="TestiTunesPing" type="checkbox" value="1"<?php if( $OpenSSLSupport == false ) echo ' disabled'; ?> /> Test iTunes Ping (recommended)</p>
614
+ </td>
615
+ </tr>
616
+
617
+ </table>
618
+ <?php if( $OpenSSLSupport == false ) { ?>
619
+ <div class="powerpress-error">Ping iTunes requires OpenSSL in PHP. Please refer to your php.ini to enable the php_openssl module.</div>
620
+ <?php } ?>
621
+ <br />
622
+
623
+
624
+ <h2><?php _e("Presentation Settings"); ?></h2>
625
+
626
+ <p style="margin-bottom: 0;">Configure how your media will be found on your blog.</p>
627
+
628
+
629
+ <table class="form-table">
630
+ <tr valign="top">
631
+ <th scope="row"><?php _e("Display Player"); ?></th>
632
+ <td><select name="General[display_player]">
633
+ <?php
634
+ $displayoptions = array(1=>"Below Post", 2=>"Above Post", 0=>"None");
635
+
636
+ while( list($value,$desc) = each($displayoptions) )
637
+ echo "\t<option value=\"$value\"". ($General['display_player']==$value?' selected':''). ">$desc</option>\n";
638
+
639
+ ?>
640
+ </select>
641
+ </td>
642
+ </tr>
643
+
644
+ <tr valign="top">
645
+ <th scope="row">
646
+ <?php _e("Player Function"); ?></th>
647
+ <td><select name="General[player_function]">
648
+ <?php
649
+ $playeroptions = array(1=>'On Page & New Window', 2=>'On Page Only', 3=>'New Window Only', 0=>'Disable');
650
+
651
+ while( list($value,$desc) = each($playeroptions) )
652
+ echo "\t<option value=\"$value\"". ($General['player_function']==$value?' selected':''). ">".htmlspecialchars($desc)."</option>\n";
653
+
654
+ ?>
655
+ </select>
656
+ </td>
657
+ </tr>
658
+
659
+ <tr valign="top">
660
+ <th scope="row">
661
+
662
+ <?php _e("Download Link"); ?></th>
663
+ <td>
664
+ <select name="General[podcast_link]">
665
+ <?php
666
+ $linkoptions = array(1=>"Display", 0=>"Disable");
667
+
668
+ while( list($value,$desc) = each($linkoptions) )
669
+ echo "\t<option value=\"$value\"". ($General['podcast_link']==$value?' selected':''). ">$desc</option>\n";
670
+
671
+ ?>
672
+ </select>
673
+ </td>
674
+ </tr>
675
+ </table>
676
+
677
+ <br />
678
+ <h2><?php _e("Media Statistics"); ?></h2>
679
+ <p style="margin-bottom: 0;">Configure 3rd party statistics services to measure your media. (optional)</p>
680
+
681
+ <table class="form-table">
682
+ <tr valign="top">
683
+ <th scope="row">
684
+ <?php _e("Redirect URL 1"); ?>
685
+ </th>
686
+ <td>
687
+ <input type="text" style="width: 60%;" name="General[redirect1]" value="<?php echo $General['redirect1']; ?>" maxlength="250" />
688
+ </td>
689
+ </tr>
690
+
691
+ <tr valign="top">
692
+ <th scope="row">
693
+ <?php _e("Redirect URL 2"); ?>
694
+ </th>
695
+ <td>
696
+ <input type="text" style="width: 60%;" name="General[redirect2]" value="<?php echo $General['redirect2']; ?>" maxlength="250" />
697
+ </td>
698
+ </tr>
699
+
700
+ <tr valign="top">
701
+ <th scope="row">
702
+ <?php _e("Redirect URL 3"); ?>
703
+ </th>
704
+ <td>
705
+ <input type="text" style="width: 60%;" name="General[redirect3]" value="<?php echo $General['redirect3']; ?>" maxlength="250" />
706
+ </td>
707
+ </tr>
708
+ </table>
709
+ <p>
710
+ The services above must support redirects that do
711
+ not include nested 'http://' within the URL. Statistics services such as
712
+ <a href="http://www.podtrac.com" target="_blank" title="PodTrac">PodTrac.com</a>,
713
+ <a href="http://www.blubrry.com/podcast_statistics/" target="_blank" title="Blubrry Statistics">Blubrry.com</a>,
714
+ <a href="http://www.techpodcasts.com/podcast_statistics/" target="_blank" title="TechPodcasts Statistics">TechPodcasts.com</a>,
715
+ <a href="http://www.rawvoice.com/products/statistics/" target="_blank" title="RawVoice Statistics">RawVoice.com</a>
716
+ are supported.
717
+ </p>
718
+ <br />
719
+ <h2><?php _e("Feed Settings"); ?></h2>
720
+ <p style="margin-bottom: 0;">
721
+ Configure your feeds to support podcasting.
722
+ </p>
723
+ <table class="form-table">
724
+
725
+ <tr valign="top">
726
+ <th scope="row">
727
+
728
+ <?php _e("Apply Settings To"); ?></th>
729
+ <td>
730
+ <select name="Feed[apply_to]">
731
+ <?php
732
+ $applyoptions = array(1=>'All RSS2 Feeds', 2=>'Main RSS2 Feed only', 0=>'Disable (settings below ignored)');
733
+
734
+ while( list($value,$desc) = each($applyoptions) )
735
+ echo "\t<option value=\"$value\"". ($FeedSettings['apply_to']==$value?' selected':''). ">$desc</option>\n";
736
+
737
+ ?>
738
+ </select>
739
+ <p>Select 'All RSS Feeds' to include podcast episodes in all feeds such as category and tag feeds.</p>
740
+ <p>Select 'Main RSS2 Feed only' to include podcast episodes only in your primary RSS2 feed.</p>
741
+ <p>Select 'Disable' to prevent Blubrry Powerpress from adding podcast episodes to any feeds.</p>
742
+ </td>
743
+ </tr>
744
+
745
+ <tr valign="top">
746
+ <th scope="row">
747
+
748
+ <?php _e("iTunes Summary"); ?></th>
749
+ <td>
750
+ <p>Your summary may not contain HTML and cannot exceed 4,000 characters in length.</p>
751
+
752
+ <textarea name="Feed[itunes_summary]" rows="5" style="width:80%;" ><?php echo $FeedSettings['itunes_summary']; ?></textarea>
753
+ </td>
754
+ </tr>
755
+
756
+ <tr valign="top">
757
+ <th scope="row">
758
+ <?php _e("iTunes Program Subtitle"); ?> <br />
759
+ </th>
760
+ <td>
761
+ <input type="text" name="Feed[itunes_subtitle]"style="width: 60%;" value="<?php echo $FeedSettings['itunes_subtitle']; ?>" maxlength="250" />
762
+ </td>
763
+ </tr>
764
+
765
+ <tr valign="top">
766
+ <th scope="row">
767
+ <?php _e("iTunes Program Keywords"); ?> <br />
768
+ </th>
769
+ <td>
770
+ <input type="text" name="Feed[itunes_keywords]"style="width: 60%;" value="<?php echo $FeedSettings['itunes_keywords']; ?>" maxlength="250" />
771
+ <p>Enter up to 12 keywords separated by commas.</p>
772
+ </td>
773
+ </tr>
774
+
775
+ <tr valign="top">
776
+ <th scope="row">
777
+ <?php _e("iTunes Category 1"); ?>
778
+ </th>
779
+ <td>
780
+ <select name="Feed[itunes_cat_1]">
781
+ <?php
782
+ $linkoptions = array("On page", "Disable");
783
+
784
+ $Categories = powerpress_itunes_categories(true);
785
+
786
+ echo '<option value="">Select Category</option>';
787
+
788
+ while( list($value,$desc) = each($Categories) )
789
+ echo "\t<option value=\"$value\"". ($FeedSettings['itunes_cat_1']==$value?' selected':''). ">".htmlspecialchars($desc)."</option>\n";
790
+
791
+ reset($Categories);
792
+ ?>
793
+ </select>
794
+ </td>
795
+ </tr>
796
+
797
+ <tr valign="top">
798
+ <th scope="row">
799
+ <?php _e("iTunes Category 2"); ?>
800
+ </th>
801
+ <td>
802
+ <select name="Feed[itunes_cat_2]">
803
+ <?php
804
+ $linkoptions = array("On page", "Disable");
805
+
806
+ echo '<option value="">Select Category</option>';
807
+
808
+ while( list($value,$desc) = each($Categories) )
809
+ echo "\t<option value=\"$value\"". ($FeedSettings['itunes_cat_2']==$value?' selected':''). ">".htmlspecialchars($desc)."</option>\n";
810
+
811
+ reset($Categories);
812
+
813
+ ?>
814
+ </select>
815
+ </td>
816
+ </tr>
817
+
818
+ <tr valign="top">
819
+ <th scope="row">
820
+ <?php _e("iTunes Category 3"); ?>
821
+ </th>
822
+ <td>
823
+ <select name="Feed[itunes_cat_3]">
824
+ <?php
825
+ $linkoptions = array("On page", "Disable");
826
+
827
+ echo '<option value="">Select Category</option>';
828
+
829
+ while( list($value,$desc) = each($Categories) )
830
+ echo "\t<option value=\"$value\"". ($FeedSettings['itunes_cat_3']==$value?' selected':''). ">".htmlspecialchars($desc)."</option>\n";
831
+
832
+ reset($Categories);
833
+ ?>
834
+ </select>
835
+ </td>
836
+ </tr>
837
+
838
+ <tr valign="top">
839
+ <th scope="row">
840
+ <?php _e("iTunes Explicit"); ?>
841
+ </th>
842
+ <td>
843
+ <select name="Feed[itunes_explicit]">
844
+ <?php
845
+ $explicit = array(0=>"no - display nothing", 1=>"yes - explicit content", 2=>"clean - no explicit content");
846
+
847
+ while( list($value,$desc) = each($explicit) )
848
+ echo "\t<option value=\"$value\"". ($FeedSettings['itunes_explicit']==$value?' selected':''). ">$desc</option>\n";
849
+
850
+ ?>
851
+ </select>
852
+ </td>
853
+ </tr>
854
+
855
+ <tr valign="top">
856
+ <th scope="row">
857
+ <?php _e("iTunes Image"); ?>
858
+ </th>
859
+ <td>
860
+ <input type="text" id="itunes_image" name="Feed[itunes_image]" style="width: 60%;" value="<?php echo $FeedSettings['itunes_image']; ?>" maxlength="250" />
861
+ <a href="#" onclick="javascript: window.open( document.getElementById('itunes_image').value ); return false;">preview</a>
862
+
863
+ <p>Put the URL to the iTunes image above. e.g. http://mysite.com/images/itunes.jpg<br /><br />iTunes prefers square .jpg or .png images that are at 600 x 600 pixels (prevously 300 x 300), which is different than what is specified for the standard RSS image.</p>
864
+
865
+ <?php if( $SupportUploads ) { ?>
866
+ <p><input name="itunes_image_checkbox" type="checkbox" onchange="powerpress_show_field('itunes_image_upload', this.checked)" value="1" /> Upload new image: </p>
867
+ <div style="display:none" id="itunes_image_upload">
868
+ <label for="itunes_image">Choose file:</label><input type="file" name="itunes_image_file" />
869
+ </div>
870
+ <?php } ?>
871
+ </td>
872
+ </tr>
873
+
874
+ <tr valign="top">
875
+ <th scope="row">
876
+ <?php _e("RSS2 Image"); ?> <br />
877
+ </th>
878
+ <td>
879
+ <input type="text" id="rss2_image" name="Feed[rss2_image]" style="width: 60%;" value="<?php echo $FeedSettings['rss2_image']; ?>" maxlength="250" />
880
+ <a href="#" onclick="javascript: window.open( document.getElementById('rss2_image').value ); return false;">preview</a>
881
+
882
+ <p>Put the URL to the RSS image above. e.g. http://mysite.com/images/rss.jpg</p>
883
+ <p>RSS image should be at least 88 and at most 144 pixels wide and at least 31 and at most 400 pixels high in either .gif, .jpg and .png format. A square 144 x 144 pixel image is recommended.</p>
884
+
885
+ <?php if( $SupportUploads ) { ?>
886
+ <p><input name="rss2_image_checkbox" type="checkbox" onchange="powerpress_show_field('rss_image_upload', this.checked)" value="1" /> Upload new image</p>
887
+ <div style="display:none" id="rss_image_upload">
888
+ <label for="rss2_image">Choose file:</label><input type="file" name="rss2_image_file" />
889
+ </div>
890
+ <?php } ?>
891
+ </td>
892
+ </tr>
893
+
894
+ <tr valign="top">
895
+ <th scope="row">
896
+ <?php _e("Talent Name"); ?> <br />
897
+ </th>
898
+ <td>
899
+ <input type="text" name="Feed[itunes_talent_name]"style="width: 60%;" value="<?php echo $FeedSettings['itunes_talent_name']; ?>" maxlength="250" />
900
+ </td>
901
+ </tr>
902
+
903
+ <tr valign="top">
904
+ <th scope="row">
905
+ <?php _e("Email"); ?>
906
+ </th>
907
+ <td>
908
+ <input type="text" name="Feed[email]" style="width: 60%;" value="<?php echo $FeedSettings['email']; ?>" maxlength="250" />
909
+ </td>
910
+ </tr>
911
+
912
+ <tr valign="top">
913
+ <th scope="row">
914
+ <?php _e("Copyright"); ?>
915
+ </th>
916
+ <td>
917
+ <input type="text" name="Feed[copyright]" style="width: 60%;" value="<?php echo $FeedSettings['copyright']; ?>" maxlength="250" />
918
+ </td>
919
+ </tr>
920
+ </table>
921
+ <p style="font-size: 85%; text-align: center;">
922
+ <a href="http://www.blubrry.com/powerpress/" title="Blubrry Powerpress" target="_blank">Blubrry Powerpress</a> <?php echo POWERPRESS_VERSION; ?>
923
+ </p>
924
+ <p class="submit">
925
+ <input type="submit" name="Submit" value="<?php _e('Save Changes' ) ?>" />
926
+ </p>
927
+
928
+ </form>
929
+ </div>
930
+ <hr />
931
+
932
+ <?php
933
+ }
934
+
935
+ /*
936
+ // Helper functions:
937
+ */
938
+ function powerpress_ping_itunes($iTunes_url)
939
+ {
940
+ if( strpos($iTunes_url, 'phobos.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=' ) === false )
941
+ return array('error'=>true, 'content'=>'iTunes URL required to ping iTunes.');
942
+
943
+ // convert: https://phobos.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=
944
+ // to: https://phobos.apple.com/WebObjects/MZFinance.woa/wa/pingPodcast?id=
945
+ $ping_url = str_replace(
946
+ array( 'https://phobos.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=',
947
+ 'http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=',
948
+ 'https://www.itunes.com/podcast?id=',
949
+ 'http://www.itunes.com/podcast?id='),
950
+ 'https://phobos.apple.com/WebObjects/MZFinance.woa/wa/pingPodcast?id=', $iTunes_url);
951
+
952
+ $fp = fopen($ping_url, 'rb');
953
+ if( $fp )
954
+ {
955
+ $tempdata = '';
956
+ while( !feof($fp) )
957
+ $tempdata .= fread($fp, 8192);
958
+ fclose($fp);
959
+ if( stristr($tempdata, 'No Podcast Found') )
960
+ return array('error'=>true, 'content'=>'No Podcast Found from iTunes ping request');
961
+
962
+ // Parse the data into something readable
963
+ $results = trim( str_replace('Podcast Ping Received', '', strip_tags($tempdata) ) );
964
+ list($null, $FeedURL, $null, $null, $null, $PodcastID) = split("\n", $results );
965
+
966
+ return array('success'=>true, 'content'=>$tempdata, 'feed_url'=>trim($FeedURL), 'podcast_id'=>trim($PodcastID) );
967
+ }
968
+ return array('error'=>true, 'content'=>'Unable to connect to iTunes ping server.');
969
+ }
970
+ ?>
readme.txt ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Blubrry Powerpress Podcasting plugin ===
2
+ Contributors: Angelo Mandato, Blubrry.com
3
+ Tags: podcast, podcasting, itunes, enclosure, zune, iphone, audio, video, rss2, feed, player, media, rss
4
+ Requires at least: 2.5.0
5
+ Tested up to: 2.6.0
6
+ Stable tag: 0.2
7
+
8
+ Add podcasting support to your blog.
9
+
10
+ == Description ==
11
+ The Blubrry Powerpress Podcast Plugin has all of the essential features needed to provide podcasting support in a Wordpress blog.
12
+
13
+ The Blubrry Powerpress interface allows you to easily add/modify/remove podcast episodes from blog posts and includes a simple on-line media player, iTunes compatibile RSS feed tags, ability to upload cover art images, ping iTunes, detect media size, detect time duration (mp3's only) and add 3rd party media statistics.
14
+
15
+ Features:
16
+
17
+ * Easily add/modify/remove podcast episodes from blog posts
18
+ * Integrated media player
19
+ * iTunes RSS tags
20
+ * iTunes album/cover art
21
+ * upload new iTunes/RSS cover art
22
+ * Ping iTunes
23
+ * Media size detection
24
+ * Duration detection (mp3 only)
25
+ * 3rd party statistics integration
26
+
27
+ For the latest information visit the website.
28
+
29
+ http://www.blubrry.com/powerpress/
30
+
31
+ == Frequently Asked Questions ==
32
+
33
+ = Why doesn't Blubrry Powerpress support multiple enclosures? =
34
+ Blubrry Powerpress does not support multiple enclosures in one blog post. This is because each podcatcher handles multiple enclosures differently. iTunes will download the first enclosure that it sees in the feed ignoring the rest. Other podcatchers and podcasting directories either pick up the first enclosure or the last in each post item. This inconsistancy and combined with the fact that [Dave Winer does not recommend multiple enclosures](http://www.reallysimplesyndication.com/2004/12/21) is why the Blubrry Powerpress does not support them.
35
+
36
+ = Why doesn't Blubrry Powerpress include media statistics? =
37
+ Blubrry Powerpress does not include media statistics. This is not because Blubrry has its own statistics service, although that's a good reason by itself. Maintaining and calculating statistics is a resource and server intensive task that would add bloat to an otherwise lightweight Wordpress podcasting plugin. We recommend you use your media hosting's statistics and you're more than welcome to use the [Blubrry Statistics service](http://www.blubrry.com/podcast_statistics/) as well.
38
+
39
+ == Installation ==
40
+ 1. Copy the entire directory from the downloaded zip file into the /wp-content/plugins/ folder.
41
+ 2. Activate the "Blubrry Powerpress" plugin in the Plugin Management page.
42
+ 3. Configure your Blubrry Powerpress by going to the **Settings** > **Blubrry Powerpress** page.
43
+
44
+ == Screenshots ==
45
+ 1. Add podcast episode, found within the edit post screen
46
+ 2. Cross section of Blubrry Powerpress settings page.
47
+
48
+ == Changelog ==
49
+
50
+ 0.2 released on 08/11/2008
51
+ Initial release of Blubrry Powerpress
52
+
53
+ == Feedback ==
54
+ http://www.blubrry.com/powerpress/
rss_default.jpg ADDED
Binary file
screenshot-1.jpg ADDED
Binary file
screenshot-2.jpg ADDED
Binary file