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 }