File indexing completed on 2024-12-22 05:33:15
0001 <?php 0002 ///////////////////////////////////////////////////////////////// 0003 /// getID3() by James Heinrich <info@getid3.org> // 0004 // available at http://getid3.sourceforge.net // 0005 // or http://www.getid3.org // 0006 // also https://github.com/JamesHeinrich/getID3 // 0007 ///////////////////////////////////////////////////////////////// 0008 // See readme.txt for more details // 0009 ///////////////////////////////////////////////////////////////// 0010 /// // 0011 // write.php // 0012 // module for writing tags (APEv2, ID3v1, ID3v2) // 0013 // dependencies: getid3.lib.php // 0014 // write.apetag.php (optional) // 0015 // write.id3v1.php (optional) // 0016 // write.id3v2.php (optional) // 0017 // write.vorbiscomment.php (optional) // 0018 // write.metaflac.php (optional) // 0019 // write.lyrics3.php (optional) // 0020 // /// 0021 ///////////////////////////////////////////////////////////////// 0022 0023 if (!defined('GETID3_INCLUDEPATH')) { 0024 throw new Exception('getid3.php MUST be included before calling getid3_writetags'); 0025 } 0026 if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) { 0027 throw new Exception('write.php depends on getid3.lib.php, which is missing.'); 0028 } 0029 0030 0031 // NOTES: 0032 // 0033 // You should pass data here with standard field names as follows: 0034 // * TITLE 0035 // * ARTIST 0036 // * ALBUM 0037 // * TRACKNUMBER 0038 // * COMMENT 0039 // * GENRE 0040 // * YEAR 0041 // * ATTACHED_PICTURE (ID3v2 only) 0042 // 0043 // http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html 0044 // The APEv2 Tag Items Keys definition says "TRACK" is correct but foobar2000 uses "TRACKNUMBER" instead 0045 // Pass data here as "TRACKNUMBER" for compatability with all formats 0046 0047 0048 class getid3_writetags 0049 { 0050 // public 0051 public $filename; // absolute filename of file to write tags to 0052 public $tagformats = array(); // array of tag formats to write ('id3v1', 'id3v2.2', 'id2v2.3', 'id3v2.4', 'ape', 'vorbiscomment', 'metaflac', 'real') 0053 public $tag_data = array(array()); // 2-dimensional array of tag data (ex: $data['ARTIST'][0] = 'Elvis') 0054 public $tag_encoding = 'ISO-8859-1'; // text encoding used for tag data ('ISO-8859-1', 'UTF-8', 'UTF-16', 'UTF-16LE', 'UTF-16BE', ) 0055 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 0056 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 0057 0058 public $id3v2_tag_language = 'eng'; // ISO-639-2 3-character language code needed for some ID3v2 frames (http://www.id3.org/iso639-2.html) 0059 public $id3v2_paddedlength = 4096; // minimum length of ID3v2 tags (will be padded to this length if tag data is shorter) 0060 0061 public $warnings = array(); // any non-critical errors will be stored here 0062 public $errors = array(); // any critical errors will be stored here 0063 0064 // private 0065 private $ThisFileInfo; // analysis of file before writing 0066 0067 public function getid3_writetags() { 0068 return true; 0069 } 0070 0071 0072 public function WriteTags() { 0073 0074 if (empty($this->filename)) { 0075 $this->errors[] = 'filename is undefined in getid3_writetags'; 0076 return false; 0077 } elseif (!file_exists($this->filename)) { 0078 $this->errors[] = 'filename set to non-existant file "'.$this->filename.'" in getid3_writetags'; 0079 return false; 0080 } 0081 0082 if (!is_array($this->tagformats)) { 0083 $this->errors[] = 'tagformats must be an array in getid3_writetags'; 0084 return false; 0085 } 0086 0087 $TagFormatsToRemove = array(); 0088 if (filesize($this->filename) == 0) { 0089 0090 // empty file special case - allow any tag format, don't check existing format 0091 // could be useful if you want to generate tag data for a non-existant file 0092 $this->ThisFileInfo = array('fileformat'=>''); 0093 $AllowedTagFormats = array('id3v1', 'id3v2.2', 'id3v2.3', 'id3v2.4', 'ape', 'lyrics3'); 0094 0095 } else { 0096 0097 $getID3 = new getID3; 0098 $getID3->encoding = $this->tag_encoding; 0099 $this->ThisFileInfo = $getID3->analyze($this->filename); 0100 0101 // check for what file types are allowed on this fileformat 0102 switch (isset($this->ThisFileInfo['fileformat']) ? $this->ThisFileInfo['fileformat'] : '') { 0103 case 'mp3': 0104 case 'mp2': 0105 case 'mp1': 0106 case 'riff': // maybe not officially, but people do it anyway 0107 $AllowedTagFormats = array('id3v1', 'id3v2.2', 'id3v2.3', 'id3v2.4', 'ape', 'lyrics3'); 0108 break; 0109 0110 case 'mpc': 0111 $AllowedTagFormats = array('ape'); 0112 break; 0113 0114 case 'flac': 0115 $AllowedTagFormats = array('metaflac'); 0116 break; 0117 0118 case 'real': 0119 $AllowedTagFormats = array('real'); 0120 break; 0121 0122 case 'ogg': 0123 switch (isset($this->ThisFileInfo['audio']['dataformat']) ? $this->ThisFileInfo['audio']['dataformat'] : '') { 0124 case 'flac': 0125 //$AllowedTagFormats = array('metaflac'); 0126 $this->errors[] = 'metaflac is not (yet) compatible with OggFLAC files'; 0127 return false; 0128 break; 0129 case 'vorbis': 0130 $AllowedTagFormats = array('vorbiscomment'); 0131 break; 0132 default: 0133 $this->errors[] = 'metaflac is not (yet) compatible with Ogg files other than OggVorbis'; 0134 return false; 0135 break; 0136 } 0137 break; 0138 0139 default: 0140 $AllowedTagFormats = array(); 0141 break; 0142 } 0143 foreach ($this->tagformats as $requested_tag_format) { 0144 if (!in_array($requested_tag_format, $AllowedTagFormats)) { 0145 $errormessage = 'Tag format "'.$requested_tag_format.'" is not allowed on "'.(isset($this->ThisFileInfo['fileformat']) ? $this->ThisFileInfo['fileformat'] : ''); 0146 $errormessage .= (isset($this->ThisFileInfo['audio']['dataformat']) ? '.'.$this->ThisFileInfo['audio']['dataformat'] : ''); 0147 $errormessage .= '" files'; 0148 $this->errors[] = $errormessage; 0149 return false; 0150 } 0151 } 0152 0153 // List of other tag formats, removed if requested 0154 if ($this->remove_other_tags) { 0155 foreach ($AllowedTagFormats as $AllowedTagFormat) { 0156 switch ($AllowedTagFormat) { 0157 case 'id3v2.2': 0158 case 'id3v2.3': 0159 case 'id3v2.4': 0160 if (!in_array('id3v2', $TagFormatsToRemove) && !in_array('id3v2.2', $this->tagformats) && !in_array('id3v2.3', $this->tagformats) && !in_array('id3v2.4', $this->tagformats)) { 0161 $TagFormatsToRemove[] = 'id3v2'; 0162 } 0163 break; 0164 0165 default: 0166 if (!in_array($AllowedTagFormat, $this->tagformats)) { 0167 $TagFormatsToRemove[] = $AllowedTagFormat; 0168 } 0169 break; 0170 } 0171 } 0172 } 0173 } 0174 0175 $WritingFilesToInclude = array_merge($this->tagformats, $TagFormatsToRemove); 0176 0177 // Check for required include files and include them 0178 foreach ($WritingFilesToInclude as $tagformat) { 0179 switch ($tagformat) { 0180 case 'ape': 0181 $GETID3_ERRORARRAY = &$this->errors; 0182 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.apetag.php', __FILE__, true); 0183 break; 0184 0185 case 'id3v1': 0186 case 'lyrics3': 0187 case 'vorbiscomment': 0188 case 'metaflac': 0189 case 'real': 0190 $GETID3_ERRORARRAY = &$this->errors; 0191 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.'.$tagformat.'.php', __FILE__, true); 0192 break; 0193 0194 case 'id3v2.2': 0195 case 'id3v2.3': 0196 case 'id3v2.4': 0197 case 'id3v2': 0198 $GETID3_ERRORARRAY = &$this->errors; 0199 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.id3v2.php', __FILE__, true); 0200 break; 0201 0202 default: 0203 $this->errors[] = 'unknown tag format "'.$tagformat.'" in $tagformats in WriteTags()'; 0204 return false; 0205 break; 0206 } 0207 0208 } 0209 0210 // Validation of supplied data 0211 if (!is_array($this->tag_data)) { 0212 $this->errors[] = '$this->tag_data is not an array in WriteTags()'; 0213 return false; 0214 } 0215 // convert supplied data array keys to upper case, if they're not already 0216 foreach ($this->tag_data as $tag_key => $tag_array) { 0217 if (strtoupper($tag_key) !== $tag_key) { 0218 $this->tag_data[strtoupper($tag_key)] = $this->tag_data[$tag_key]; 0219 unset($this->tag_data[$tag_key]); 0220 } 0221 } 0222 // convert source data array keys to upper case, if they're not already 0223 if (!empty($this->ThisFileInfo['tags'])) { 0224 foreach ($this->ThisFileInfo['tags'] as $tag_format => $tag_data_array) { 0225 foreach ($tag_data_array as $tag_key => $tag_array) { 0226 if (strtoupper($tag_key) !== $tag_key) { 0227 $this->ThisFileInfo['tags'][$tag_format][strtoupper($tag_key)] = $this->ThisFileInfo['tags'][$tag_format][$tag_key]; 0228 unset($this->ThisFileInfo['tags'][$tag_format][$tag_key]); 0229 } 0230 } 0231 } 0232 } 0233 0234 // Convert "TRACK" to "TRACKNUMBER" (if needed) for compatability with all formats 0235 if (isset($this->tag_data['TRACK']) && !isset($this->tag_data['TRACKNUMBER'])) { 0236 $this->tag_data['TRACKNUMBER'] = $this->tag_data['TRACK']; 0237 unset($this->tag_data['TRACK']); 0238 } 0239 0240 // Remove all other tag formats, if requested 0241 if ($this->remove_other_tags) { 0242 $this->DeleteTags($TagFormatsToRemove); 0243 } 0244 0245 // Write data for each tag format 0246 foreach ($this->tagformats as $tagformat) { 0247 $success = false; // overridden if tag writing is successful 0248 switch ($tagformat) { 0249 case 'ape': 0250 $ape_writer = new getid3_write_apetag; 0251 if (($ape_writer->tag_data = $this->FormatDataForAPE()) !== false) { 0252 $ape_writer->filename = $this->filename; 0253 if (($success = $ape_writer->WriteAPEtag()) === false) { 0254 $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>'; 0255 } 0256 } else { 0257 $this->errors[] = 'FormatDataForAPE() failed'; 0258 } 0259 break; 0260 0261 case 'id3v1': 0262 $id3v1_writer = new getid3_write_id3v1; 0263 if (($id3v1_writer->tag_data = $this->FormatDataForID3v1()) !== false) { 0264 $id3v1_writer->filename = $this->filename; 0265 if (($success = $id3v1_writer->WriteID3v1()) === false) { 0266 $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>'; 0267 } 0268 } else { 0269 $this->errors[] = 'FormatDataForID3v1() failed'; 0270 } 0271 break; 0272 0273 case 'id3v2.2': 0274 case 'id3v2.3': 0275 case 'id3v2.4': 0276 $id3v2_writer = new getid3_write_id3v2; 0277 $id3v2_writer->majorversion = intval(substr($tagformat, -1)); 0278 $id3v2_writer->paddedlength = $this->id3v2_paddedlength; 0279 if (($id3v2_writer->tag_data = $this->FormatDataForID3v2($id3v2_writer->majorversion)) !== false) { 0280 $id3v2_writer->filename = $this->filename; 0281 if (($success = $id3v2_writer->WriteID3v2()) === false) { 0282 $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>'; 0283 } 0284 } else { 0285 $this->errors[] = 'FormatDataForID3v2() failed'; 0286 } 0287 break; 0288 0289 case 'vorbiscomment': 0290 $vorbiscomment_writer = new getid3_write_vorbiscomment; 0291 if (($vorbiscomment_writer->tag_data = $this->FormatDataForVorbisComment()) !== false) { 0292 $vorbiscomment_writer->filename = $this->filename; 0293 if (($success = $vorbiscomment_writer->WriteVorbisComment()) === false) { 0294 $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>'; 0295 } 0296 } else { 0297 $this->errors[] = 'FormatDataForVorbisComment() failed'; 0298 } 0299 break; 0300 0301 case 'metaflac': 0302 $metaflac_writer = new getid3_write_metaflac; 0303 if (($metaflac_writer->tag_data = $this->FormatDataForMetaFLAC()) !== false) { 0304 $metaflac_writer->filename = $this->filename; 0305 if (($success = $metaflac_writer->WriteMetaFLAC()) === false) { 0306 $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>'; 0307 } 0308 } else { 0309 $this->errors[] = 'FormatDataForMetaFLAC() failed'; 0310 } 0311 break; 0312 0313 case 'real': 0314 $real_writer = new getid3_write_real; 0315 if (($real_writer->tag_data = $this->FormatDataForReal()) !== false) { 0316 $real_writer->filename = $this->filename; 0317 if (($success = $real_writer->WriteReal()) === false) { 0318 $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>'; 0319 } 0320 } else { 0321 $this->errors[] = 'FormatDataForReal() failed'; 0322 } 0323 break; 0324 0325 default: 0326 $this->errors[] = 'Invalid tag format to write: "'.$tagformat.'"'; 0327 return false; 0328 break; 0329 } 0330 if (!$success) { 0331 return false; 0332 } 0333 } 0334 return true; 0335 0336 } 0337 0338 0339 public function DeleteTags($TagFormatsToDelete) { 0340 foreach ($TagFormatsToDelete as $DeleteTagFormat) { 0341 $success = false; // overridden if tag deletion is successful 0342 switch ($DeleteTagFormat) { 0343 case 'id3v1': 0344 $id3v1_writer = new getid3_write_id3v1; 0345 $id3v1_writer->filename = $this->filename; 0346 if (($success = $id3v1_writer->RemoveID3v1()) === false) { 0347 $this->errors[] = 'RemoveID3v1() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v1_writer->errors)).'</LI></UL></PRE>'; 0348 } 0349 break; 0350 0351 case 'id3v2': 0352 $id3v2_writer = new getid3_write_id3v2; 0353 $id3v2_writer->filename = $this->filename; 0354 if (($success = $id3v2_writer->RemoveID3v2()) === false) { 0355 $this->errors[] = 'RemoveID3v2() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v2_writer->errors)).'</LI></UL></PRE>'; 0356 } 0357 break; 0358 0359 case 'ape': 0360 $ape_writer = new getid3_write_apetag; 0361 $ape_writer->filename = $this->filename; 0362 if (($success = $ape_writer->DeleteAPEtag()) === false) { 0363 $this->errors[] = 'DeleteAPEtag() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $ape_writer->errors)).'</LI></UL></PRE>'; 0364 } 0365 break; 0366 0367 case 'vorbiscomment': 0368 $vorbiscomment_writer = new getid3_write_vorbiscomment; 0369 $vorbiscomment_writer->filename = $this->filename; 0370 if (($success = $vorbiscomment_writer->DeleteVorbisComment()) === false) { 0371 $this->errors[] = 'DeleteVorbisComment() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $vorbiscomment_writer->errors)).'</LI></UL></PRE>'; 0372 } 0373 break; 0374 0375 case 'metaflac': 0376 $metaflac_writer = new getid3_write_metaflac; 0377 $metaflac_writer->filename = $this->filename; 0378 if (($success = $metaflac_writer->DeleteMetaFLAC()) === false) { 0379 $this->errors[] = 'DeleteMetaFLAC() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $metaflac_writer->errors)).'</LI></UL></PRE>'; 0380 } 0381 break; 0382 0383 case 'lyrics3': 0384 $lyrics3_writer = new getid3_write_lyrics3; 0385 $lyrics3_writer->filename = $this->filename; 0386 if (($success = $lyrics3_writer->DeleteLyrics3()) === false) { 0387 $this->errors[] = 'DeleteLyrics3() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $lyrics3_writer->errors)).'</LI></UL></PRE>'; 0388 } 0389 break; 0390 0391 case 'real': 0392 $real_writer = new getid3_write_real; 0393 $real_writer->filename = $this->filename; 0394 if (($success = $real_writer->RemoveReal()) === false) { 0395 $this->errors[] = 'RemoveReal() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $real_writer->errors)).'</LI></UL></PRE>'; 0396 } 0397 break; 0398 0399 default: 0400 $this->errors[] = 'Invalid tag format to delete: "'.$tagformat.'"'; 0401 return false; 0402 break; 0403 } 0404 if (!$success) { 0405 return false; 0406 } 0407 } 0408 return true; 0409 } 0410 0411 0412 public function MergeExistingTagData($TagFormat, &$tag_data) { 0413 // Merge supplied data with existing data, if requested 0414 if ($this->overwrite_tags) { 0415 // do nothing - ignore previous data 0416 } else { 0417 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.'); 0418 if (!isset($this->ThisFileInfo['tags'][$TagFormat])) { 0419 return false; 0420 } 0421 $tag_data = array_merge_recursive($tag_data, $this->ThisFileInfo['tags'][$TagFormat]); 0422 } 0423 return true; 0424 } 0425 0426 public function FormatDataForAPE() { 0427 $ape_tag_data = array(); 0428 foreach ($this->tag_data as $tag_key => $valuearray) { 0429 switch ($tag_key) { 0430 case 'ATTACHED_PICTURE': 0431 // ATTACHED_PICTURE is ID3v2 only - ignore 0432 $this->warnings[] = '$data['.$tag_key.'] is assumed to be ID3v2 APIC data - NOT written to APE tag'; 0433 break; 0434 0435 default: 0436 foreach ($valuearray as $key => $value) { 0437 if (is_string($value) || is_numeric($value)) { 0438 $ape_tag_data[$tag_key][$key] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value); 0439 } else { 0440 $this->warnings[] = '$data['.$tag_key.']['.$key.'] is not a string value - all of $data['.$tag_key.'] NOT written to APE tag'; 0441 unset($ape_tag_data[$tag_key]); 0442 break; 0443 } 0444 } 0445 break; 0446 } 0447 } 0448 $this->MergeExistingTagData('ape', $ape_tag_data); 0449 return $ape_tag_data; 0450 } 0451 0452 0453 public function FormatDataForID3v1() { 0454 $tag_data_id3v1['genreid'] = 255; 0455 if (!empty($this->tag_data['GENRE'])) { 0456 foreach ($this->tag_data['GENRE'] as $key => $value) { 0457 if (getid3_id3v1::LookupGenreID($value) !== false) { 0458 $tag_data_id3v1['genreid'] = getid3_id3v1::LookupGenreID($value); 0459 break; 0460 } 0461 } 0462 } 0463 $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()))); 0464 $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()))); 0465 $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()))); 0466 $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()))); 0467 $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()))); 0468 $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())))); 0469 if ($tag_data_id3v1['track'] <= 0) { 0470 $tag_data_id3v1['track'] = ''; 0471 } 0472 0473 $this->MergeExistingTagData('id3v1', $tag_data_id3v1); 0474 return $tag_data_id3v1; 0475 } 0476 0477 public function FormatDataForID3v2($id3v2_majorversion) { 0478 $tag_data_id3v2 = array(); 0479 0480 $ID3v2_text_encoding_lookup[2] = array('ISO-8859-1'=>0, 'UTF-16'=>1); 0481 $ID3v2_text_encoding_lookup[3] = array('ISO-8859-1'=>0, 'UTF-16'=>1); 0482 $ID3v2_text_encoding_lookup[4] = array('ISO-8859-1'=>0, 'UTF-16'=>1, 'UTF-16BE'=>2, 'UTF-8'=>3); 0483 foreach ($this->tag_data as $tag_key => $valuearray) { 0484 $ID3v2_framename = getid3_write_id3v2::ID3v2ShortFrameNameLookup($id3v2_majorversion, $tag_key); 0485 switch ($ID3v2_framename) { 0486 case 'APIC': 0487 foreach ($valuearray as $key => $apic_data_array) { 0488 if (isset($apic_data_array['data']) && 0489 isset($apic_data_array['picturetypeid']) && 0490 isset($apic_data_array['description']) && 0491 isset($apic_data_array['mime'])) { 0492 $tag_data_id3v2['APIC'][] = $apic_data_array; 0493 } else { 0494 $this->errors[] = 'ID3v2 APIC data is not properly structured'; 0495 return false; 0496 } 0497 } 0498 break; 0499 0500 case '': 0501 $this->errors[] = 'ID3v2: Skipping "'.$tag_key.'" because cannot match it to a known ID3v2 frame type'; 0502 // some other data type, don't know how to handle it, ignore it 0503 break; 0504 0505 default: 0506 // most other (text) frames can be copied over as-is 0507 foreach ($valuearray as $key => $value) { 0508 if (isset($ID3v2_text_encoding_lookup[$id3v2_majorversion][$this->tag_encoding])) { 0509 // source encoding is valid in ID3v2 - use it with no conversion 0510 $tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = $ID3v2_text_encoding_lookup[$id3v2_majorversion][$this->tag_encoding]; 0511 $tag_data_id3v2[$ID3v2_framename][$key]['data'] = $value; 0512 } else { 0513 // source encoding is NOT valid in ID3v2 - convert it to an ID3v2-valid encoding first 0514 if ($id3v2_majorversion < 4) { 0515 // convert data from other encoding to UTF-16 (with BOM) 0516 // note: some software, notably Windows Media Player and iTunes are broken and treat files tagged with UTF-16BE (with BOM) as corrupt 0517 // therefore we force data to UTF-16LE and manually prepend the BOM 0518 $ID3v2_tag_data_converted = false; 0519 if (!$ID3v2_tag_data_converted && ($this->tag_encoding == 'ISO-8859-1')) { 0520 // great, leave data as-is for minimum compatability problems 0521 $tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 0; 0522 $tag_data_id3v2[$ID3v2_framename][$key]['data'] = $value; 0523 $ID3v2_tag_data_converted = true; 0524 } 0525 if (!$ID3v2_tag_data_converted && ($this->tag_encoding == 'UTF-8')) { 0526 do { 0527 // if UTF-8 string does not include any characters above chr(127) then it is identical to ISO-8859-1 0528 for ($i = 0; $i < strlen($value); $i++) { 0529 if (ord($value{$i}) > 127) { 0530 break 2; 0531 } 0532 } 0533 $tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 0; 0534 $tag_data_id3v2[$ID3v2_framename][$key]['data'] = $value; 0535 $ID3v2_tag_data_converted = true; 0536 } while (false); 0537 } 0538 if (!$ID3v2_tag_data_converted) { 0539 $tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 1; 0540 //$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 0541 $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 0542 $ID3v2_tag_data_converted = true; 0543 } 0544 0545 } else { 0546 // convert data from other encoding to UTF-8 0547 $tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 3; 0548 $tag_data_id3v2[$ID3v2_framename][$key]['data'] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value); 0549 } 0550 } 0551 0552 // These values are not needed for all frame types, but if they're not used no matter 0553 $tag_data_id3v2[$ID3v2_framename][$key]['description'] = ''; 0554 $tag_data_id3v2[$ID3v2_framename][$key]['language'] = $this->id3v2_tag_language; 0555 } 0556 break; 0557 } 0558 } 0559 $this->MergeExistingTagData('id3v2', $tag_data_id3v2); 0560 return $tag_data_id3v2; 0561 } 0562 0563 public function FormatDataForVorbisComment() { 0564 $tag_data_vorbiscomment = $this->tag_data; 0565 0566 // check for multi-line comment values - split out to multiple comments if neccesary 0567 // and convert data to UTF-8 strings 0568 foreach ($tag_data_vorbiscomment as $tag_key => $valuearray) { 0569 foreach ($valuearray as $key => $value) { 0570 str_replace("\r", "\n", $value); 0571 if (strstr($value, "\n")) { 0572 unset($tag_data_vorbiscomment[$tag_key][$key]); 0573 $multilineexploded = explode("\n", $value); 0574 foreach ($multilineexploded as $newcomment) { 0575 if (strlen(trim($newcomment)) > 0) { 0576 $tag_data_vorbiscomment[$tag_key][] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $newcomment); 0577 } 0578 } 0579 } elseif (is_string($value) || is_numeric($value)) { 0580 $tag_data_vorbiscomment[$tag_key][$key] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value); 0581 } else { 0582 $this->warnings[] = '$data['.$tag_key.']['.$key.'] is not a string value - all of $data['.$tag_key.'] NOT written to VorbisComment tag'; 0583 unset($tag_data_vorbiscomment[$tag_key]); 0584 break; 0585 } 0586 } 0587 } 0588 $this->MergeExistingTagData('vorbiscomment', $tag_data_vorbiscomment); 0589 return $tag_data_vorbiscomment; 0590 } 0591 0592 public function FormatDataForMetaFLAC() { 0593 // FLAC & OggFLAC use VorbisComments same as OggVorbis 0594 // but require metaflac to do the writing rather than vorbiscomment 0595 return $this->FormatDataForVorbisComment(); 0596 } 0597 0598 public function FormatDataForReal() { 0599 $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()))); 0600 $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()))); 0601 $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()))); 0602 $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()))); 0603 0604 $this->MergeExistingTagData('real', $tag_data_real); 0605 return $tag_data_real; 0606 } 0607 0608 }