PageRenderTime 56ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/getid3/write.php

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