d4416549f03f245af1b945f6779c93f99236b45a
[lhc/web/www.git] / www / plugins-dist / medias / lib / getid3 / write.php
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 // also https://github.com/JamesHeinrich/getID3 //
7 /////////////////////////////////////////////////////////////////
8 // See readme.txt for more details //
9 /////////////////////////////////////////////////////////////////
10 /// //
11 // write.php //
12 // module for writing tags (APEv2, ID3v1, ID3v2) //
13 // dependencies: getid3.lib.php //
14 // write.apetag.php (optional) //
15 // write.id3v1.php (optional) //
16 // write.id3v2.php (optional) //
17 // write.vorbiscomment.php (optional) //
18 // write.metaflac.php (optional) //
19 // write.lyrics3.php (optional) //
20 // ///
21 /////////////////////////////////////////////////////////////////
22
23 if (!defined('GETID3_INCLUDEPATH')) {
24 throw new Exception('getid3.php MUST be included before calling getid3_writetags');
25 }
26 if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
27 throw new Exception('write.php depends on getid3.lib.php, which is missing.');
28 }
29
30
31 // NOTES:
32 //
33 // You should pass data here with standard field names as follows:
34 // * TITLE
35 // * ARTIST
36 // * ALBUM
37 // * TRACKNUMBER
38 // * COMMENT
39 // * GENRE
40 // * YEAR
41 // * ATTACHED_PICTURE (ID3v2 only)
42 //
43 // http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
44 // The APEv2 Tag Items Keys definition says "TRACK" is correct but foobar2000 uses "TRACKNUMBER" instead
45 // Pass data here as "TRACKNUMBER" for compatability with all formats
46
47
48 class getid3_writetags
49 {
50 // public
51 public $filename; // absolute filename of file to write tags to
52 public $tagformats = array(); // array of tag formats to write ('id3v1', 'id3v2.2', 'id2v2.3', 'id3v2.4', 'ape', 'vorbiscomment', 'metaflac', 'real')
53 public $tag_data = array(array()); // 2-dimensional array of tag data (ex: $data['ARTIST'][0] = 'Elvis')
54 public $tag_encoding = 'ISO-8859-1'; // text encoding used for tag data ('ISO-8859-1', 'UTF-8', 'UTF-16', 'UTF-16LE', 'UTF-16BE', )
55 public $overwrite_tags = true; // if true will erase existing tag data and write only passed data; if false will merge passed data with existing tag data
56 public $remove_other_tags = false; // if true will erase remove all existing tags and only write those passed in $tagformats; if false will ignore any tags not mentioned in $tagformats
57
58 public $id3v2_tag_language = 'eng'; // ISO-639-2 3-character language code needed for some ID3v2 frames (http://www.id3.org/iso639-2.html)
59 public $id3v2_paddedlength = 4096; // minimum length of ID3v2 tags (will be padded to this length if tag data is shorter)
60
61 public $warnings = array(); // any non-critical errors will be stored here
62 public $errors = array(); // any critical errors will be stored here
63
64 // private
65 private $ThisFileInfo; // analysis of file before writing
66
67 public function __construct() {
68 return true;
69 }
70
71
72 public function WriteTags() {
73
74 if (empty($this->filename)) {
75 $this->errors[] = 'filename is undefined in getid3_writetags';
76 return false;
77 } elseif (!file_exists($this->filename)) {
78 $this->errors[] = 'filename set to non-existant file "'.$this->filename.'" in getid3_writetags';
79 return false;
80 }
81
82 if (!is_array($this->tagformats)) {
83 $this->errors[] = 'tagformats must be an array in getid3_writetags';
84 return false;
85 }
86
87 $TagFormatsToRemove = array();
88 if (filesize($this->filename) == 0) {
89
90 // empty file special case - allow any tag format, don't check existing format
91 // could be useful if you want to generate tag data for a non-existant file
92 $this->ThisFileInfo = array('fileformat'=>'');
93 $AllowedTagFormats = array('id3v1', 'id3v2.2', 'id3v2.3', 'id3v2.4', 'ape', 'lyrics3');
94
95 } else {
96
97 $getID3 = new getID3;
98 $getID3->encoding = $this->tag_encoding;
99 $this->ThisFileInfo = $getID3->analyze($this->filename);
100
101 // check for what file types are allowed on this fileformat
102 switch (isset($this->ThisFileInfo['fileformat']) ? $this->ThisFileInfo['fileformat'] : '') {
103 case 'mp3':
104 case 'mp2':
105 case 'mp1':
106 case 'riff': // maybe not officially, but people do it anyway
107 $AllowedTagFormats = array('id3v1', 'id3v2.2', 'id3v2.3', 'id3v2.4', 'ape', 'lyrics3');
108 break;
109
110 case 'mpc':
111 $AllowedTagFormats = array('ape');
112 break;
113
114 case 'flac':
115 $AllowedTagFormats = array('metaflac');
116 break;
117
118 case 'real':
119 $AllowedTagFormats = array('real');
120 break;
121
122 case 'ogg':
123 switch (isset($this->ThisFileInfo['audio']['dataformat']) ? $this->ThisFileInfo['audio']['dataformat'] : '') {
124 case 'flac':
125 //$AllowedTagFormats = array('metaflac');
126 $this->errors[] = 'metaflac is not (yet) compatible with OggFLAC files';
127 return false;
128 break;
129 case 'vorbis':
130 $AllowedTagFormats = array('vorbiscomment');
131 break;
132 default:
133 $this->errors[] = 'metaflac is not (yet) compatible with Ogg files other than OggVorbis';
134 return false;
135 break;
136 }
137 break;
138
139 default:
140 $AllowedTagFormats = array();
141 break;
142 }
143 foreach ($this->tagformats as $requested_tag_format) {
144 if (!in_array($requested_tag_format, $AllowedTagFormats)) {
145 $errormessage = 'Tag format "'.$requested_tag_format.'" is not allowed on "'.(isset($this->ThisFileInfo['fileformat']) ? $this->ThisFileInfo['fileformat'] : '');
146 $errormessage .= (isset($this->ThisFileInfo['audio']['dataformat']) ? '.'.$this->ThisFileInfo['audio']['dataformat'] : '');
147 $errormessage .= '" files';
148 $this->errors[] = $errormessage;
149 return false;
150 }
151 }
152
153 // List of other tag formats, removed if requested
154 if ($this->remove_other_tags) {
155 foreach ($AllowedTagFormats as $AllowedTagFormat) {
156 switch ($AllowedTagFormat) {
157 case 'id3v2.2':
158 case 'id3v2.3':
159 case 'id3v2.4':
160 if (!in_array('id3v2', $TagFormatsToRemove) && !in_array('id3v2.2', $this->tagformats) && !in_array('id3v2.3', $this->tagformats) && !in_array('id3v2.4', $this->tagformats)) {
161 $TagFormatsToRemove[] = 'id3v2';
162 }
163 break;
164
165 default:
166 if (!in_array($AllowedTagFormat, $this->tagformats)) {
167 $TagFormatsToRemove[] = $AllowedTagFormat;
168 }
169 break;
170 }
171 }
172 }
173 }
174
175 $WritingFilesToInclude = array_merge($this->tagformats, $TagFormatsToRemove);
176
177 // Check for required include files and include them
178 foreach ($WritingFilesToInclude as $tagformat) {
179 switch ($tagformat) {
180 case 'ape':
181 $GETID3_ERRORARRAY = &$this->errors;
182 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.apetag.php', __FILE__, true);
183 break;
184
185 case 'id3v1':
186 case 'lyrics3':
187 case 'vorbiscomment':
188 case 'metaflac':
189 case 'real':
190 $GETID3_ERRORARRAY = &$this->errors;
191 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.'.$tagformat.'.php', __FILE__, true);
192 break;
193
194 case 'id3v2.2':
195 case 'id3v2.3':
196 case 'id3v2.4':
197 case 'id3v2':
198 $GETID3_ERRORARRAY = &$this->errors;
199 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.id3v2.php', __FILE__, true);
200 break;
201
202 default:
203 $this->errors[] = 'unknown tag format "'.$tagformat.'" in $tagformats in WriteTags()';
204 return false;
205 break;
206 }
207
208 }
209
210 // Validation of supplied data
211 if (!is_array($this->tag_data)) {
212 $this->errors[] = '$this->tag_data is not an array in WriteTags()';
213 return false;
214 }
215 // convert supplied data array keys to upper case, if they're not already
216 foreach ($this->tag_data as $tag_key => $tag_array) {
217 if (strtoupper($tag_key) !== $tag_key) {
218 $this->tag_data[strtoupper($tag_key)] = $this->tag_data[$tag_key];
219 unset($this->tag_data[$tag_key]);
220 }
221 }
222 // convert source data array keys to upper case, if they're not already
223 if (!empty($this->ThisFileInfo['tags'])) {
224 foreach ($this->ThisFileInfo['tags'] as $tag_format => $tag_data_array) {
225 foreach ($tag_data_array as $tag_key => $tag_array) {
226 if (strtoupper($tag_key) !== $tag_key) {
227 $this->ThisFileInfo['tags'][$tag_format][strtoupper($tag_key)] = $this->ThisFileInfo['tags'][$tag_format][$tag_key];
228 unset($this->ThisFileInfo['tags'][$tag_format][$tag_key]);
229 }
230 }
231 }
232 }
233
234 // Convert "TRACK" to "TRACKNUMBER" (if needed) for compatability with all formats
235 if (isset($this->tag_data['TRACK']) && !isset($this->tag_data['TRACKNUMBER'])) {
236 $this->tag_data['TRACKNUMBER'] = $this->tag_data['TRACK'];
237 unset($this->tag_data['TRACK']);
238 }
239
240 // Remove all other tag formats, if requested
241 if ($this->remove_other_tags) {
242 $this->DeleteTags($TagFormatsToRemove);
243 }
244
245 // Write data for each tag format
246 foreach ($this->tagformats as $tagformat) {
247 $success = false; // overridden if tag writing is successful
248 switch ($tagformat) {
249 case 'ape':
250 $ape_writer = new getid3_write_apetag;
251 if (($ape_writer->tag_data = $this->FormatDataForAPE()) !== false) {
252 $ape_writer->filename = $this->filename;
253 if (($success = $ape_writer->WriteAPEtag()) === false) {
254 $this->errors[] = 'WriteAPEtag() failed with message(s):<pre><ul><li>'.str_replace("\n", '</li><li>', htmlentities(trim(implode("\n", $ape_writer->errors)))).'</li></ul></pre>';
255 }
256 } else {
257 $this->errors[] = 'FormatDataForAPE() failed';
258 }
259 break;
260
261 case 'id3v1':
262 $id3v1_writer = new getid3_write_id3v1;
263 if (($id3v1_writer->tag_data = $this->FormatDataForID3v1()) !== false) {
264 $id3v1_writer->filename = $this->filename;
265 if (($success = $id3v1_writer->WriteID3v1()) === false) {
266 $this->errors[] = 'WriteID3v1() failed with message(s):<pre><ul><li>'.str_replace("\n", '</li><li>', htmlentities(trim(implode("\n", $id3v1_writer->errors)))).'</li></ul></pre>';
267 }
268 } else {
269 $this->errors[] = 'FormatDataForID3v1() failed';
270 }
271 break;
272
273 case 'id3v2.2':
274 case 'id3v2.3':
275 case 'id3v2.4':
276 $id3v2_writer = new getid3_write_id3v2;
277 $id3v2_writer->majorversion = intval(substr($tagformat, -1));
278 $id3v2_writer->paddedlength = $this->id3v2_paddedlength;
279 if (($id3v2_writer->tag_data = $this->FormatDataForID3v2($id3v2_writer->majorversion)) !== false) {
280 $id3v2_writer->filename = $this->filename;
281 if (($success = $id3v2_writer->WriteID3v2()) === false) {
282 $this->errors[] = 'WriteID3v2() failed with message(s):<pre><ul><li>'.str_replace("\n", '</li><li>', htmlentities(trim(implode("\n", $id3v2_writer->errors)))).'</li></ul></pre>';
283 }
284 } else {
285 $this->errors[] = 'FormatDataForID3v2() failed';
286 }
287 break;
288
289 case 'vorbiscomment':
290 $vorbiscomment_writer = new getid3_write_vorbiscomment;
291 if (($vorbiscomment_writer->tag_data = $this->FormatDataForVorbisComment()) !== false) {
292 $vorbiscomment_writer->filename = $this->filename;
293 if (($success = $vorbiscomment_writer->WriteVorbisComment()) === false) {
294 $this->errors[] = 'WriteVorbisComment() failed with message(s):<pre><ul><li>'.str_replace("\n", '</li><li>', htmlentities(trim(implode("\n", $vorbiscomment_writer->errors)))).'</li></ul></pre>';
295 }
296 } else {
297 $this->errors[] = 'FormatDataForVorbisComment() failed';
298 }
299 break;
300
301 case 'metaflac':
302 $metaflac_writer = new getid3_write_metaflac;
303 if (($metaflac_writer->tag_data = $this->FormatDataForMetaFLAC()) !== false) {
304 $metaflac_writer->filename = $this->filename;
305 if (($success = $metaflac_writer->WriteMetaFLAC()) === false) {
306 $this->errors[] = 'WriteMetaFLAC() failed with message(s):<pre><ul><li>'.str_replace("\n", '</li><li>', htmlentities(trim(implode("\n", $metaflac_writer->errors)))).'</li></ul></pre>';
307 }
308 } else {
309 $this->errors[] = 'FormatDataForMetaFLAC() failed';
310 }
311 break;
312
313 case 'real':
314 $real_writer = new getid3_write_real;
315 if (($real_writer->tag_data = $this->FormatDataForReal()) !== false) {
316 $real_writer->filename = $this->filename;
317 if (($success = $real_writer->WriteReal()) === false) {
318 $this->errors[] = 'WriteReal() failed with message(s):<pre><ul><li>'.str_replace("\n", '</li><li>', htmlentities(trim(implode("\n", $real_writer->errors)))).'</li></ul></pre>';
319 }
320 } else {
321 $this->errors[] = 'FormatDataForReal() failed';
322 }
323 break;
324
325 default:
326 $this->errors[] = 'Invalid tag format to write: "'.$tagformat.'"';
327 return false;
328 break;
329 }
330 if (!$success) {
331 return false;
332 }
333 }
334 return true;
335
336 }
337
338
339 public function DeleteTags($TagFormatsToDelete) {
340 foreach ($TagFormatsToDelete as $DeleteTagFormat) {
341 $success = false; // overridden if tag deletion is successful
342 switch ($DeleteTagFormat) {
343 case 'id3v1':
344 $id3v1_writer = new getid3_write_id3v1;
345 $id3v1_writer->filename = $this->filename;
346 if (($success = $id3v1_writer->RemoveID3v1()) === false) {
347 $this->errors[] = 'RemoveID3v1() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v1_writer->errors)).'</LI></UL></PRE>';
348 }
349 break;
350
351 case 'id3v2':
352 $id3v2_writer = new getid3_write_id3v2;
353 $id3v2_writer->filename = $this->filename;
354 if (($success = $id3v2_writer->RemoveID3v2()) === false) {
355 $this->errors[] = 'RemoveID3v2() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v2_writer->errors)).'</LI></UL></PRE>';
356 }
357 break;
358
359 case 'ape':
360 $ape_writer = new getid3_write_apetag;
361 $ape_writer->filename = $this->filename;
362 if (($success = $ape_writer->DeleteAPEtag()) === false) {
363 $this->errors[] = 'DeleteAPEtag() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $ape_writer->errors)).'</LI></UL></PRE>';
364 }
365 break;
366
367 case 'vorbiscomment':
368 $vorbiscomment_writer = new getid3_write_vorbiscomment;
369 $vorbiscomment_writer->filename = $this->filename;
370 if (($success = $vorbiscomment_writer->DeleteVorbisComment()) === false) {
371 $this->errors[] = 'DeleteVorbisComment() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $vorbiscomment_writer->errors)).'</LI></UL></PRE>';
372 }
373 break;
374
375 case 'metaflac':
376 $metaflac_writer = new getid3_write_metaflac;
377 $metaflac_writer->filename = $this->filename;
378 if (($success = $metaflac_writer->DeleteMetaFLAC()) === false) {
379 $this->errors[] = 'DeleteMetaFLAC() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $metaflac_writer->errors)).'</LI></UL></PRE>';
380 }
381 break;
382
383 case 'lyrics3':
384 $lyrics3_writer = new getid3_write_lyrics3;
385 $lyrics3_writer->filename = $this->filename;
386 if (($success = $lyrics3_writer->DeleteLyrics3()) === false) {
387 $this->errors[] = 'DeleteLyrics3() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $lyrics3_writer->errors)).'</LI></UL></PRE>';
388 }
389 break;
390
391 case 'real':
392 $real_writer = new getid3_write_real;
393 $real_writer->filename = $this->filename;
394 if (($success = $real_writer->RemoveReal()) === false) {
395 $this->errors[] = 'RemoveReal() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $real_writer->errors)).'</LI></UL></PRE>';
396 }
397 break;
398
399 default:
400 $this->errors[] = 'Invalid tag format to delete: "'.$tagformat.'"';
401 return false;
402 break;
403 }
404 if (!$success) {
405 return false;
406 }
407 }
408 return true;
409 }
410
411
412 public function MergeExistingTagData($TagFormat, &$tag_data) {
413 // Merge supplied data with existing data, if requested
414 if ($this->overwrite_tags) {
415 // do nothing - ignore previous data
416 } else {
417 throw new Exception('$this->overwrite_tags=false is known to be buggy in this version of getID3. Will be fixed in the near future, check www.getid3.org for a newer version.');
418 if (!isset($this->ThisFileInfo['tags'][$TagFormat])) {
419 return false;
420 }
421 $tag_data = array_merge_recursive($tag_data, $this->ThisFileInfo['tags'][$TagFormat]);
422 }
423 return true;
424 }
425
426 public function FormatDataForAPE() {
427 $ape_tag_data = array();
428 foreach ($this->tag_data as $tag_key => $valuearray) {
429 switch ($tag_key) {
430 case 'ATTACHED_PICTURE':
431 // ATTACHED_PICTURE is ID3v2 only - ignore
432 $this->warnings[] = '$data['.$tag_key.'] is assumed to be ID3v2 APIC data - NOT written to APE tag';
433 break;
434
435 default:
436 foreach ($valuearray as $key => $value) {
437 if (is_string($value) || is_numeric($value)) {
438 $ape_tag_data[$tag_key][$key] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value);
439 } else {
440 $this->warnings[] = '$data['.$tag_key.']['.$key.'] is not a string value - all of $data['.$tag_key.'] NOT written to APE tag';
441 unset($ape_tag_data[$tag_key]);
442 break;
443 }
444 }
445 break;
446 }
447 }
448 $this->MergeExistingTagData('ape', $ape_tag_data);
449 return $ape_tag_data;
450 }
451
452
453 public function FormatDataForID3v1() {
454 $tag_data_id3v1['genreid'] = 255;
455 if (!empty($this->tag_data['GENRE'])) {
456 foreach ($this->tag_data['GENRE'] as $key => $value) {
457 if (getid3_id3v1::LookupGenreID($value) !== false) {
458 $tag_data_id3v1['genreid'] = getid3_id3v1::LookupGenreID($value);
459 break;
460 }
461 }
462 }
463 $tag_data_id3v1['title'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['TITLE'] ) ? $this->tag_data['TITLE'] : array())));
464 $tag_data_id3v1['artist'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['ARTIST'] ) ? $this->tag_data['ARTIST'] : array())));
465 $tag_data_id3v1['album'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['ALBUM'] ) ? $this->tag_data['ALBUM'] : array())));
466 $tag_data_id3v1['year'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['YEAR'] ) ? $this->tag_data['YEAR'] : array())));
467 $tag_data_id3v1['comment'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['COMMENT'] ) ? $this->tag_data['COMMENT'] : array())));
468 $tag_data_id3v1['track'] = intval(getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['TRACKNUMBER']) ? $this->tag_data['TRACKNUMBER'] : array()))));
469 if ($tag_data_id3v1['track'] <= 0) {
470 $tag_data_id3v1['track'] = '';
471 }
472
473 $this->MergeExistingTagData('id3v1', $tag_data_id3v1);
474 return $tag_data_id3v1;
475 }
476
477 public function FormatDataForID3v2($id3v2_majorversion) {
478 $tag_data_id3v2 = array();
479
480 $ID3v2_text_encoding_lookup[2] = array('ISO-8859-1'=>0, 'UTF-16'=>1);
481 $ID3v2_text_encoding_lookup[3] = array('ISO-8859-1'=>0, 'UTF-16'=>1);
482 $ID3v2_text_encoding_lookup[4] = array('ISO-8859-1'=>0, 'UTF-16'=>1, 'UTF-16BE'=>2, 'UTF-8'=>3);
483 foreach ($this->tag_data as $tag_key => $valuearray) {
484 $ID3v2_framename = getid3_write_id3v2::ID3v2ShortFrameNameLookup($id3v2_majorversion, $tag_key);
485 switch ($ID3v2_framename) {
486 case 'APIC':
487 foreach ($valuearray as $key => $apic_data_array) {
488 if (isset($apic_data_array['data']) &&
489 isset($apic_data_array['picturetypeid']) &&
490 isset($apic_data_array['description']) &&
491 isset($apic_data_array['mime'])) {
492 $tag_data_id3v2['APIC'][] = $apic_data_array;
493 } else {
494 $this->errors[] = 'ID3v2 APIC data is not properly structured';
495 return false;
496 }
497 }
498 break;
499
500 case 'POPM':
501 if (isset($valuearray['email']) &&
502 isset($valuearray['rating']) &&
503 isset($valuearray['data'])) {
504 $tag_data_id3v2['POPM'][] = $valuearray;
505 } else {
506 $this->errors[] = 'ID3v2 POPM data is not properly structured';
507 return false;
508 }
509 break;
510
511 case 'GRID':
512 if (
513 isset($valuearray['groupsymbol']) &&
514 isset($valuearray['ownerid']) &&
515 isset($valuearray['data'])
516 ) {
517 $tag_data_id3v2['GRID'][] = $valuearray;
518 } else {
519 $this->errors[] = 'ID3v2 GRID data is not properly structured';
520 return false;
521 }
522 break;
523
524 case 'UFID':
525 if (isset($valuearray['ownerid']) &&
526 isset($valuearray['data'])) {
527 $tag_data_id3v2['UFID'][] = $valuearray;
528 } else {
529 $this->errors[] = 'ID3v2 UFID data is not properly structured';
530 return false;
531 }
532 break;
533
534 case 'TXXX':
535 foreach ($valuearray as $key => $txxx_data_array) {
536 if (isset($txxx_data_array['description']) && isset($txxx_data_array['data'])) {
537 $tag_data_id3v2['TXXX'][] = $txxx_data_array;
538 } else {
539 $this->errors[] = 'ID3v2 TXXX data is not properly structured';
540 return false;
541 }
542 }
543 break;
544
545 case '':
546 $this->errors[] = 'ID3v2: Skipping "'.$tag_key.'" because cannot match it to a known ID3v2 frame type';
547 // some other data type, don't know how to handle it, ignore it
548 break;
549
550 default:
551 // most other (text) frames can be copied over as-is
552 foreach ($valuearray as $key => $value) {
553 if (isset($ID3v2_text_encoding_lookup[$id3v2_majorversion][$this->tag_encoding])) {
554 // source encoding is valid in ID3v2 - use it with no conversion
555 $tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = $ID3v2_text_encoding_lookup[$id3v2_majorversion][$this->tag_encoding];
556 $tag_data_id3v2[$ID3v2_framename][$key]['data'] = $value;
557 } else {
558 // source encoding is NOT valid in ID3v2 - convert it to an ID3v2-valid encoding first
559 if ($id3v2_majorversion < 4) {
560 // convert data from other encoding to UTF-16 (with BOM)
561 // note: some software, notably Windows Media Player and iTunes are broken and treat files tagged with UTF-16BE (with BOM) as corrupt
562 // therefore we force data to UTF-16LE and manually prepend the BOM
563 $ID3v2_tag_data_converted = false;
564 if (!$ID3v2_tag_data_converted && ($this->tag_encoding == 'ISO-8859-1')) {
565 // great, leave data as-is for minimum compatability problems
566 $tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 0;
567 $tag_data_id3v2[$ID3v2_framename][$key]['data'] = $value;
568 $ID3v2_tag_data_converted = true;
569 }
570 if (!$ID3v2_tag_data_converted && ($this->tag_encoding == 'UTF-8')) {
571 do {
572 // if UTF-8 string does not include any characters above chr(127) then it is identical to ISO-8859-1
573 for ($i = 0; $i < strlen($value); $i++) {
574 if (ord($value{$i}) > 127) {
575 break 2;
576 }
577 }
578 $tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 0;
579 $tag_data_id3v2[$ID3v2_framename][$key]['data'] = $value;
580 $ID3v2_tag_data_converted = true;
581 } while (false);
582 }
583 if (!$ID3v2_tag_data_converted) {
584 $tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 1;
585 //$tag_data_id3v2[$ID3v2_framename][$key]['data'] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-16', $value); // output is UTF-16LE+BOM or UTF-16BE+BOM depending on system architecture
586 $tag_data_id3v2[$ID3v2_framename][$key]['data'] = "\xFF\xFE".getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-16LE', $value); // force LittleEndian order version of UTF-16
587 $ID3v2_tag_data_converted = true;
588 }
589
590 } else {
591 // convert data from other encoding to UTF-8
592 $tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 3;
593 $tag_data_id3v2[$ID3v2_framename][$key]['data'] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value);
594 }
595 }
596
597 // These values are not needed for all frame types, but if they're not used no matter
598 $tag_data_id3v2[$ID3v2_framename][$key]['description'] = '';
599 $tag_data_id3v2[$ID3v2_framename][$key]['language'] = $this->id3v2_tag_language;
600 }
601 break;
602 }
603 }
604 $this->MergeExistingTagData('id3v2', $tag_data_id3v2);
605 return $tag_data_id3v2;
606 }
607
608 public function FormatDataForVorbisComment() {
609 $tag_data_vorbiscomment = $this->tag_data;
610
611 // check for multi-line comment values - split out to multiple comments if neccesary
612 // and convert data to UTF-8 strings
613 foreach ($tag_data_vorbiscomment as $tag_key => $valuearray) {
614 foreach ($valuearray as $key => $value) {
615 str_replace("\r", "\n", $value);
616 if (strstr($value, "\n")) {
617 unset($tag_data_vorbiscomment[$tag_key][$key]);
618 $multilineexploded = explode("\n", $value);
619 foreach ($multilineexploded as $newcomment) {
620 if (strlen(trim($newcomment)) > 0) {
621 $tag_data_vorbiscomment[$tag_key][] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $newcomment);
622 }
623 }
624 } elseif (is_string($value) || is_numeric($value)) {
625 $tag_data_vorbiscomment[$tag_key][$key] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value);
626 } else {
627 $this->warnings[] = '$data['.$tag_key.']['.$key.'] is not a string value - all of $data['.$tag_key.'] NOT written to VorbisComment tag';
628 unset($tag_data_vorbiscomment[$tag_key]);
629 break;
630 }
631 }
632 }
633 $this->MergeExistingTagData('vorbiscomment', $tag_data_vorbiscomment);
634 return $tag_data_vorbiscomment;
635 }
636
637 public function FormatDataForMetaFLAC() {
638 // FLAC & OggFLAC use VorbisComments same as OggVorbis
639 // but require metaflac to do the writing rather than vorbiscomment
640 return $this->FormatDataForVorbisComment();
641 }
642
643 public function FormatDataForReal() {
644 $tag_data_real['title'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['TITLE'] ) ? $this->tag_data['TITLE'] : array())));
645 $tag_data_real['artist'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['ARTIST'] ) ? $this->tag_data['ARTIST'] : array())));
646 $tag_data_real['copyright'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['COPYRIGHT']) ? $this->tag_data['COPYRIGHT'] : array())));
647 $tag_data_real['comment'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['COMMENT'] ) ? $this->tag_data['COMMENT'] : array())));
648
649 $this->MergeExistingTagData('real', $tag_data_real);
650 return $tag_data_real;
651 }
652
653 }