PageRenderTime 67ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/common/libraries/plugin/osflvplayer/flash/write.id3v2.php

https://bitbucket.org/chamilo/chamilo/
PHP | 1263 lines | 908 code | 100 blank | 255 comment | 256 complexity | cd10adc2abe07895b5d13e77ec8891e5 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1, LGPL-3.0, GPL-3.0, MIT

Large files files are truncated, but you can click here to view the full file

  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.id3v2.php //
  11. // module for writing ID3v2 tags //
  12. // dependencies: module.tag.id3v2.php //
  13. // ///
  14. /////////////////////////////////////////////////////////////////
  15. getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
  16. class getid3_write_id3v2
  17. {
  18. var $filename;
  19. var $tag_data;
  20. var $paddedlength = 4096; // minimum length of ID3v2 tag in bytes
  21. var $majorversion = 3; // ID3v2 major version (2, 3 (recommended), 4)
  22. var $minorversion = 0; // ID3v2 minor version - always 0
  23. var $merge_existing_data = false; // if true, merge new data with existing tags; if false, delete old tag data and only write new tags
  24. var $id3v2_default_encodingid = 0; // default text encoding (ISO-8859-1) if not explicitly passed
  25. var $id3v2_use_unsynchronisation = false; // the specs say it should be TRUE, but most other ID3v2-aware programs are broken if unsynchronization is used, so by default don't use it.
  26. var $warnings = array(); // any non-critical errors will be stored here
  27. var $errors = array(); // any critical errors will be stored here
  28. function getid3_write_id3v2() {
  29. return true;
  30. }
  31. function WriteID3v2() {
  32. // File MUST be writeable - CHMOD(646) at least. It's best if the
  33. // directory is also writeable, because that method is both faster and less susceptible to errors.
  34. if (is_writeable($this->filename) || (!file_exists($this->filename) && is_writeable(dirname($this->filename)))) {
  35. // Initialize getID3 engine
  36. $getID3 = new getID3;
  37. $OldThisFileInfo = $getID3->analyze($this->filename);
  38. if ($this->merge_existing_data) {
  39. // merge with existing data
  40. if (!empty($OldThisFileInfo['id3v2'])) {
  41. $this->tag_data = $this->array_join_merge($OldThisFileInfo['id3v2'], $this->tag_data);
  42. }
  43. }
  44. $this->paddedlength = max(@$OldThisFileInfo['id3v2']['headerlength'], $this->paddedlength);
  45. if ($NewID3v2Tag = $this->GenerateID3v2Tag()) {
  46. if (file_exists($this->filename) && is_writeable($this->filename) && isset($OldThisFileInfo['id3v2']['headerlength']) && ($OldThisFileInfo['id3v2']['headerlength'] == strlen($NewID3v2Tag))) {
  47. // best and fastest method - insert-overwrite existing tag (padded to length of old tag if neccesary)
  48. if (file_exists($this->filename)) {
  49. ob_start();
  50. if ($fp = fopen($this->filename, 'r+b')) {
  51. rewind($fp);
  52. fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag));
  53. fclose($fp);
  54. } else {
  55. $this->errors[] = 'Could not open '.$this->filename.' mode "r+b" - '.strip_tags(ob_get_contents());
  56. }
  57. ob_end_clean();
  58. } else {
  59. ob_start();
  60. if ($fp = fopen($this->filename, 'wb')) {
  61. rewind($fp);
  62. fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag));
  63. fclose($fp);
  64. } else {
  65. $this->errors[] = 'Could not open '.$this->filename.' mode "wb" - '.strip_tags(ob_get_contents());
  66. }
  67. ob_end_clean();
  68. }
  69. } else {
  70. if ($tempfilename = tempnam('*', 'getID3')) {
  71. ob_start();
  72. if ($fp_source = fopen($this->filename, 'rb')) {
  73. if ($fp_temp = fopen($tempfilename, 'wb')) {
  74. fwrite($fp_temp, $NewID3v2Tag, strlen($NewID3v2Tag));
  75. rewind($fp_source);
  76. if (!empty($OldThisFileInfo['avdataoffset'])) {
  77. fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET);
  78. }
  79. while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
  80. fwrite($fp_temp, $buffer, strlen($buffer));
  81. }
  82. fclose($fp_temp);
  83. fclose($fp_source);
  84. copy($tempfilename, $this->filename);
  85. unlink($tempfilename);
  86. ob_end_clean();
  87. return true;
  88. } else {
  89. $this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
  90. }
  91. fclose($fp_source);
  92. } else {
  93. $this->errors[] = 'Could not open '.$this->filename.' mode "rb" - '.strip_tags(ob_get_contents());
  94. }
  95. ob_end_clean();
  96. }
  97. return false;
  98. }
  99. } else {
  100. $this->errors[] = '$this->GenerateID3v2Tag() failed';
  101. }
  102. if (!empty($this->errors)) {
  103. return false;
  104. }
  105. return true;
  106. } else {
  107. $this->errors[] = '!is_writeable('.$this->filename.')';
  108. }
  109. return false;
  110. }
  111. function RemoveID3v2() {
  112. // File MUST be writeable - CHMOD(646) at least. It's best if the
  113. // directory is also writeable, because that method is both faster and less susceptible to errors.
  114. if (is_writeable(dirname($this->filename))) {
  115. // preferred method - only one copying operation, minimal chance of corrupting
  116. // original file if script is interrupted, but required directory to be writeable
  117. if ($fp_source = @fopen($this->filename, 'rb')) {
  118. // Initialize getID3 engine
  119. $getID3 = new getID3;
  120. $OldThisFileInfo = $getID3->analyze($this->filename);
  121. rewind($fp_source);
  122. if ($OldThisFileInfo['avdataoffset'] !== false) {
  123. fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET);
  124. }
  125. if ($fp_temp = @fopen($this->filename.'getid3tmp', 'w+b')) {
  126. while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
  127. fwrite($fp_temp, $buffer, strlen($buffer));
  128. }
  129. fclose($fp_temp);
  130. } else {
  131. $this->errors[] = 'Could not open '.$this->filename.'getid3tmp mode "w+b"';
  132. }
  133. fclose($fp_source);
  134. } else {
  135. $this->errors[] = 'Could not open '.$this->filename.' mode "rb"';
  136. }
  137. if (file_exists($this->filename)) {
  138. unlink($this->filename);
  139. }
  140. rename($this->filename.'getid3tmp', $this->filename);
  141. } elseif (is_writable($this->filename)) {
  142. // less desirable alternate method - double-copies the file, overwrites original file
  143. // and could corrupt source file if the script is interrupted or an error occurs.
  144. if ($fp_source = @fopen($this->filename, 'rb')) {
  145. // Initialize getID3 engine
  146. $getID3 = new getID3;
  147. $OldThisFileInfo = $getID3->analyze($this->filename);
  148. rewind($fp_source);
  149. if ($OldThisFileInfo['avdataoffset'] !== false) {
  150. fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET);
  151. }
  152. if ($fp_temp = tmpfile()) {
  153. while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
  154. fwrite($fp_temp, $buffer, strlen($buffer));
  155. }
  156. fclose($fp_source);
  157. if ($fp_source = @fopen($this->filename, 'wb')) {
  158. rewind($fp_temp);
  159. while ($buffer = fread($fp_temp, GETID3_FREAD_BUFFER_SIZE)) {
  160. fwrite($fp_source, $buffer, strlen($buffer));
  161. }
  162. fseek($fp_temp, -128, SEEK_END);
  163. fclose($fp_source);
  164. } else {
  165. $this->errors[] = 'Could not open '.$this->filename.' mode "wb"';
  166. }
  167. fclose($fp_temp);
  168. } else {
  169. $this->errors[] = 'Could not create tmpfile()';
  170. }
  171. } else {
  172. $this->errors[] = 'Could not open '.$this->filename.' mode "rb"';
  173. }
  174. } else {
  175. $this->errors[] = 'Directory and file both not writeable';
  176. }
  177. if (!empty($this->errors)) {
  178. return false;
  179. }
  180. return true;
  181. }
  182. function GenerateID3v2TagFlags($flags) {
  183. switch ($this->majorversion) {
  184. case 4:
  185. // %abcd0000
  186. $flag = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation
  187. $flag .= (@$flags['extendedheader'] ? '1' : '0'); // b - Extended header
  188. $flag .= (@$flags['experimental'] ? '1' : '0'); // c - Experimental indicator
  189. $flag .= (@$flags['footer'] ? '1' : '0'); // d - Footer present
  190. $flag .= '0000';
  191. break;
  192. case 3:
  193. // %abc00000
  194. $flag = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation
  195. $flag .= (@$flags['extendedheader'] ? '1' : '0'); // b - Extended header
  196. $flag .= (@$flags['experimental'] ? '1' : '0'); // c - Experimental indicator
  197. $flag .= '00000';
  198. break;
  199. case 2:
  200. // %ab000000
  201. $flag = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation
  202. $flag .= (@$flags['compression'] ? '1' : '0'); // b - Compression
  203. $flag .= '000000';
  204. break;
  205. default:
  206. return false;
  207. break;
  208. }
  209. return chr(bindec($flag));
  210. }
  211. function GenerateID3v2FrameFlags($TagAlter=false, $FileAlter=false, $ReadOnly=false, $Compression=false, $Encryption=false, $GroupingIdentity=false, $Unsynchronisation=false, $DataLengthIndicator=false) {
  212. switch ($this->majorversion) {
  213. case 4:
  214. // %0abc0000 %0h00kmnp
  215. $flag1 = '0';
  216. $flag1 .= $TagAlter ? '1' : '0'; // a - Tag alter preservation (true == discard)
  217. $flag1 .= $FileAlter ? '1' : '0'; // b - File alter preservation (true == discard)
  218. $flag1 .= $ReadOnly ? '1' : '0'; // c - Read only (true == read only)
  219. $flag1 .= '0000';
  220. $flag2 = '0';
  221. $flag2 .= $GroupingIdentity ? '1' : '0'; // h - Grouping identity (true == contains group information)
  222. $flag2 .= '00';
  223. $flag2 .= $Compression ? '1' : '0'; // k - Compression (true == compressed)
  224. $flag2 .= $Encryption ? '1' : '0'; // m - Encryption (true == encrypted)
  225. $flag2 .= $Unsynchronisation ? '1' : '0'; // n - Unsynchronisation (true == unsynchronised)
  226. $flag2 .= $DataLengthIndicator ? '1' : '0'; // p - Data length indicator (true == data length indicator added)
  227. break;
  228. case 3:
  229. // %abc00000 %ijk00000
  230. $flag1 = $TagAlter ? '1' : '0'; // a - Tag alter preservation (true == discard)
  231. $flag1 .= $FileAlter ? '1' : '0'; // b - File alter preservation (true == discard)
  232. $flag1 .= $ReadOnly ? '1' : '0'; // c - Read only (true == read only)
  233. $flag1 .= '00000';
  234. $flag2 = $Compression ? '1' : '0'; // i - Compression (true == compressed)
  235. $flag2 .= $Encryption ? '1' : '0'; // j - Encryption (true == encrypted)
  236. $flag2 .= $GroupingIdentity ? '1' : '0'; // k - Grouping identity (true == contains group information)
  237. $flag2 .= '00000';
  238. break;
  239. default:
  240. return false;
  241. break;
  242. }
  243. return chr(bindec($flag1)).chr(bindec($flag2));
  244. }
  245. function GenerateID3v2FrameData($frame_name, $source_data_array) {
  246. if (!getid3_id3v2::IsValidID3v2FrameName($frame_name, $this->majorversion)) {
  247. return false;
  248. }
  249. $framedata = '';
  250. if (($this->majorversion < 3) || ($this->majorversion > 4)) {
  251. $this->errors[] = 'Only ID3v2.3 and ID3v2.4 are supported in GenerateID3v2FrameData()';
  252. } else { // $this->majorversion 3 or 4
  253. switch ($frame_name) {
  254. case 'UFID':
  255. // 4.1 UFID Unique file identifier
  256. // Owner identifier <text string> $00
  257. // Identifier <up to 64 bytes binary data>
  258. if (strlen($source_data_array['data']) > 64) {
  259. $this->errors[] = 'Identifier not allowed to be longer than 64 bytes in '.$frame_name.' (supplied data was '.strlen($source_data_array['data']).' bytes long)';
  260. } else {
  261. $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
  262. $framedata .= substr($source_data_array['data'], 0, 64); // max 64 bytes - truncate anything longer
  263. }
  264. break;
  265. case 'TXXX':
  266. // 4.2.2 TXXX User defined text information frame
  267. // Text encoding $xx
  268. // Description <text string according to encoding> $00 (00)
  269. // Value <text string according to encoding>
  270. $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
  271. if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
  272. $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
  273. } else {
  274. $framedata .= chr($source_data_array['encodingid']);
  275. $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
  276. $framedata .= $source_data_array['data'];
  277. }
  278. break;
  279. case 'WXXX':
  280. // 4.3.2 WXXX User defined URL link frame
  281. // Text encoding $xx
  282. // Description <text string according to encoding> $00 (00)
  283. // URL <text string>
  284. $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
  285. if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
  286. $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
  287. } elseif (!isset($source_data_array['data']) || !$this->IsValidURL($source_data_array['data'], false, false)) {
  288. //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
  289. // probably should be an error, need to rewrite IsValidURL() to handle other encodings
  290. $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
  291. } else {
  292. $framedata .= chr($source_data_array['encodingid']);
  293. $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
  294. $framedata .= $source_data_array['data'];
  295. }
  296. break;
  297. case 'IPLS':
  298. // 4.4 IPLS Involved people list (ID3v2.3 only)
  299. // Text encoding $xx
  300. // People list strings <textstrings>
  301. $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
  302. if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
  303. $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
  304. } else {
  305. $framedata .= chr($source_data_array['encodingid']);
  306. $framedata .= $source_data_array['data'];
  307. }
  308. break;
  309. case 'MCDI':
  310. // 4.4 MCDI Music CD identifier
  311. // CD TOC <binary data>
  312. $framedata .= $source_data_array['data'];
  313. break;
  314. case 'ETCO':
  315. // 4.5 ETCO Event timing codes
  316. // Time stamp format $xx
  317. // Where time stamp format is:
  318. // $01 (32-bit value) MPEG frames from beginning of file
  319. // $02 (32-bit value) milliseconds from beginning of file
  320. // Followed by a list of key events in the following format:
  321. // Type of event $xx
  322. // Time stamp $xx (xx ...)
  323. // The 'Time stamp' is set to zero if directly at the beginning of the sound
  324. // or after the previous event. All events MUST be sorted in chronological order.
  325. if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
  326. $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
  327. } else {
  328. $framedata .= chr($source_data_array['timestampformat']);
  329. foreach ($source_data_array as $key => $val) {
  330. if (!$this->ID3v2IsValidETCOevent($val['typeid'])) {
  331. $this->errors[] = 'Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')';
  332. } elseif (($key != 'timestampformat') && ($key != 'flags')) {
  333. if (($val['timestamp'] > 0) && ($previousETCOtimestamp >= $val['timestamp'])) {
  334. // The 'Time stamp' is set to zero if directly at the beginning of the sound
  335. // or after the previous event. All events MUST be sorted in chronological order.
  336. $this->errors[] = 'Out-of-order timestamp in '.$frame_name.' ('.$val['timestamp'].') for Event Type ('.$val['typeid'].')';
  337. } else {
  338. $framedata .= chr($val['typeid']);
  339. $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
  340. }
  341. }
  342. }
  343. }
  344. break;
  345. case 'MLLT':
  346. // 4.6 MLLT MPEG location lookup table
  347. // MPEG frames between reference $xx xx
  348. // Bytes between reference $xx xx xx
  349. // Milliseconds between reference $xx xx xx
  350. // Bits for bytes deviation $xx
  351. // Bits for milliseconds dev. $xx
  352. // Then for every reference the following data is included;
  353. // Deviation in bytes %xxx....
  354. // Deviation in milliseconds %xxx....
  355. if (($source_data_array['framesbetweenreferences'] > 0) && ($source_data_array['framesbetweenreferences'] <= 65535)) {
  356. $framedata .= getid3_lib::BigEndian2String($source_data_array['framesbetweenreferences'], 2, false);
  357. } else {
  358. $this->errors[] = 'Invalid MPEG Frames Between References in '.$frame_name.' ('.$source_data_array['framesbetweenreferences'].')';
  359. }
  360. if (($source_data_array['bytesbetweenreferences'] > 0) && ($source_data_array['bytesbetweenreferences'] <= 16777215)) {
  361. $framedata .= getid3_lib::BigEndian2String($source_data_array['bytesbetweenreferences'], 3, false);
  362. } else {
  363. $this->errors[] = 'Invalid bytes Between References in '.$frame_name.' ('.$source_data_array['bytesbetweenreferences'].')';
  364. }
  365. if (($source_data_array['msbetweenreferences'] > 0) && ($source_data_array['msbetweenreferences'] <= 16777215)) {
  366. $framedata .= getid3_lib::BigEndian2String($source_data_array['msbetweenreferences'], 3, false);
  367. } else {
  368. $this->errors[] = 'Invalid Milliseconds Between References in '.$frame_name.' ('.$source_data_array['msbetweenreferences'].')';
  369. }
  370. if (!$this->IsWithinBitRange($source_data_array['bitsforbytesdeviation'], 8, false)) {
  371. if (($source_data_array['bitsforbytesdeviation'] % 4) == 0) {
  372. $framedata .= chr($source_data_array['bitsforbytesdeviation']);
  373. } else {
  374. $this->errors[] = 'Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.';
  375. }
  376. } else {
  377. $this->errors[] = 'Invalid Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].')';
  378. }
  379. if (!$this->IsWithinBitRange($source_data_array['bitsformsdeviation'], 8, false)) {
  380. if (($source_data_array['bitsformsdeviation'] % 4) == 0) {
  381. $framedata .= chr($source_data_array['bitsformsdeviation']);
  382. } else {
  383. $this->errors[] = 'Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.';
  384. }
  385. } else {
  386. $this->errors[] = 'Invalid Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsformsdeviation'].')';
  387. }
  388. foreach ($source_data_array as $key => $val) {
  389. if (($key != 'framesbetweenreferences') && ($key != 'bytesbetweenreferences') && ($key != 'msbetweenreferences') && ($key != 'bitsforbytesdeviation') && ($key != 'bitsformsdeviation') && ($key != 'flags')) {
  390. $unwrittenbitstream .= str_pad(getid3_lib::Dec2Bin($val['bytedeviation']), $source_data_array['bitsforbytesdeviation'], '0', STR_PAD_LEFT);
  391. $unwrittenbitstream .= str_pad(getid3_lib::Dec2Bin($val['msdeviation']), $source_data_array['bitsformsdeviation'], '0', STR_PAD_LEFT);
  392. }
  393. }
  394. for ($i = 0; $i < strlen($unwrittenbitstream); $i += 8) {
  395. $highnibble = bindec(substr($unwrittenbitstream, $i, 4)) << 4;
  396. $lownibble = bindec(substr($unwrittenbitstream, $i + 4, 4));
  397. $framedata .= chr($highnibble & $lownibble);
  398. }
  399. break;
  400. case 'SYTC':
  401. // 4.7 SYTC Synchronised tempo codes
  402. // Time stamp format $xx
  403. // Tempo data <binary data>
  404. // Where time stamp format is:
  405. // $01 (32-bit value) MPEG frames from beginning of file
  406. // $02 (32-bit value) milliseconds from beginning of file
  407. if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
  408. $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
  409. } else {
  410. $framedata .= chr($source_data_array['timestampformat']);
  411. foreach ($source_data_array as $key => $val) {
  412. if (!$this->ID3v2IsValidETCOevent($val['typeid'])) {
  413. $this->errors[] = 'Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')';
  414. } elseif (($key != 'timestampformat') && ($key != 'flags')) {
  415. if (($val['tempo'] < 0) || ($val['tempo'] > 510)) {
  416. $this->errors[] = 'Invalid Tempo (max = 510) in '.$frame_name.' ('.$val['tempo'].') at timestamp ('.$val['timestamp'].')';
  417. } else {
  418. if ($val['tempo'] > 255) {
  419. $framedata .= chr(255);
  420. $val['tempo'] -= 255;
  421. }
  422. $framedata .= chr($val['tempo']);
  423. $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
  424. }
  425. }
  426. }
  427. }
  428. break;
  429. case 'USLT':
  430. // 4.8 USLT Unsynchronised lyric/text transcription
  431. // Text encoding $xx
  432. // Language $xx xx xx
  433. // Content descriptor <text string according to encoding> $00 (00)
  434. // Lyrics/text <full text string according to encoding>
  435. $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
  436. if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
  437. $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
  438. } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
  439. $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
  440. } else {
  441. $framedata .= chr($source_data_array['encodingid']);
  442. $framedata .= strtolower($source_data_array['language']);
  443. $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
  444. $framedata .= $source_data_array['data'];
  445. }
  446. break;
  447. case 'SYLT':
  448. // 4.9 SYLT Synchronised lyric/text
  449. // Text encoding $xx
  450. // Language $xx xx xx
  451. // Time stamp format $xx
  452. // $01 (32-bit value) MPEG frames from beginning of file
  453. // $02 (32-bit value) milliseconds from beginning of file
  454. // Content type $xx
  455. // Content descriptor <text string according to encoding> $00 (00)
  456. // Terminated text to be synced (typically a syllable)
  457. // Sync identifier (terminator to above string) $00 (00)
  458. // Time stamp $xx (xx ...)
  459. $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
  460. if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
  461. $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
  462. } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
  463. $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
  464. } elseif (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
  465. $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
  466. } elseif (!$this->ID3v2IsValidSYLTtype($source_data_array['contenttypeid'])) {
  467. $this->errors[] = 'Invalid Content Type byte in '.$frame_name.' ('.$source_data_array['contenttypeid'].')';
  468. } elseif (!is_array($source_data_array['data'])) {
  469. $this->errors[] = 'Invalid Lyric/Timestamp data in '.$frame_name.' (must be an array)';
  470. } else {
  471. $framedata .= chr($source_data_array['encodingid']);
  472. $framedata .= strtolower($source_data_array['language']);
  473. $framedata .= chr($source_data_array['timestampformat']);
  474. $framedata .= chr($source_data_array['contenttypeid']);
  475. $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
  476. ksort($source_data_array['data']);
  477. foreach ($source_data_array['data'] as $key => $val) {
  478. $framedata .= $val['data'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
  479. $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
  480. }
  481. }
  482. break;
  483. case 'COMM':
  484. // 4.10 COMM Comments
  485. // Text encoding $xx
  486. // Language $xx xx xx
  487. // Short content descrip. <text string according to encoding> $00 (00)
  488. // The actual text <full text string according to encoding>
  489. $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
  490. if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
  491. $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
  492. } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
  493. $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
  494. } else {
  495. $framedata .= chr($source_data_array['encodingid']);
  496. $framedata .= strtolower($source_data_array['language']);
  497. $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
  498. $framedata .= $source_data_array['data'];
  499. }
  500. break;
  501. case 'RVA2':
  502. // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only)
  503. // Identification <text string> $00
  504. // The 'identification' string is used to identify the situation and/or
  505. // device where this adjustment should apply. The following is then
  506. // repeated for every channel:
  507. // Type of channel $xx
  508. // Volume adjustment $xx xx
  509. // Bits representing peak $xx
  510. // Peak volume $xx (xx ...)
  511. $framedata .= str_replace("\x00", '', $source_data_array['description'])."\x00";
  512. foreach ($source_data_array as $key => $val) {
  513. if ($key != 'description') {
  514. $framedata .= chr($val['channeltypeid']);
  515. $framedata .= getid3_lib::BigEndian2String($val['volumeadjust'], 2, false, true); // signed 16-bit
  516. if (!$this->IsWithinBitRange($source_data_array['bitspeakvolume'], 8, false)) {
  517. $framedata .= chr($val['bitspeakvolume']);
  518. if ($val['bitspeakvolume'] > 0) {
  519. $framedata .= getid3_lib::BigEndian2String($val['peakvolume'], ceil($val['bitspeakvolume'] / 8), false, false);
  520. }
  521. } else {
  522. $this->errors[] = 'Invalid Bits Representing Peak Volume in '.$frame_name.' ('.$val['bitspeakvolume'].') (range = 0 to 255)';
  523. }
  524. }
  525. }
  526. break;
  527. case 'RVAD':
  528. // 4.12 RVAD Relative volume adjustment (ID3v2.3 only)
  529. // Increment/decrement %00fedcba
  530. // Bits used for volume descr. $xx
  531. // Relative volume change, right $xx xx (xx ...) // a
  532. // Relative volume change, left $xx xx (xx ...) // b
  533. // Peak volume right $xx xx (xx ...)
  534. // Peak volume left $xx xx (xx ...)
  535. // Relative volume change, right back $xx xx (xx ...) // c
  536. // Relative volume change, left back $xx xx (xx ...) // d
  537. // Peak volume right back $xx xx (xx ...)
  538. // Peak volume left back $xx xx (xx ...)
  539. // Relative volume change, center $xx xx (xx ...) // e
  540. // Peak volume center $xx xx (xx ...)
  541. // Relative volume change, bass $xx xx (xx ...) // f
  542. // Peak volume bass $xx xx (xx ...)
  543. if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) {
  544. $this->errors[] = 'Invalid Bits For Volume Description byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)';
  545. } else {
  546. $incdecflag .= '00';
  547. $incdecflag .= $source_data_array['incdec']['right'] ? '1' : '0'; // a - Relative volume change, right
  548. $incdecflag .= $source_data_array['incdec']['left'] ? '1' : '0'; // b - Relative volume change, left
  549. $incdecflag .= $source_data_array['incdec']['rightrear'] ? '1' : '0'; // c - Relative volume change, right back
  550. $incdecflag .= $source_data_array['incdec']['leftrear'] ? '1' : '0'; // d - Relative volume change, left back
  551. $incdecflag .= $source_data_array['incdec']['center'] ? '1' : '0'; // e - Relative volume change, center
  552. $incdecflag .= $source_data_array['incdec']['bass'] ? '1' : '0'; // f - Relative volume change, bass
  553. $framedata .= chr(bindec($incdecflag));
  554. $framedata .= chr($source_data_array['bitsvolume']);
  555. $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['right'], ceil($source_data_array['bitsvolume'] / 8), false);
  556. $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['left'], ceil($source_data_array['bitsvolume'] / 8), false);
  557. $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['right'], ceil($source_data_array['bitsvolume'] / 8), false);
  558. $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['left'], ceil($source_data_array['bitsvolume'] / 8), false);
  559. if ($source_data_array['volumechange']['rightrear'] || $source_data_array['volumechange']['leftrear'] ||
  560. $source_data_array['peakvolume']['rightrear'] || $source_data_array['peakvolume']['leftrear'] ||
  561. $source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] ||
  562. $source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
  563. $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['rightrear'], ceil($source_data_array['bitsvolume']/8), false);
  564. $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['leftrear'], ceil($source_data_array['bitsvolume']/8), false);
  565. $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['rightrear'], ceil($source_data_array['bitsvolume']/8), false);
  566. $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['leftrear'], ceil($source_data_array['bitsvolume']/8), false);
  567. }
  568. if ($source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] ||
  569. $source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
  570. $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['center'], ceil($source_data_array['bitsvolume']/8), false);
  571. $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['center'], ceil($source_data_array['bitsvolume']/8), false);
  572. }
  573. if ($source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
  574. $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['bass'], ceil($source_data_array['bitsvolume']/8), false);
  575. $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['bass'], ceil($source_data_array['bitsvolume']/8), false);
  576. }
  577. }
  578. break;
  579. case 'EQU2':
  580. // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only)
  581. // Interpolation method $xx
  582. // $00 Band
  583. // $01 Linear
  584. // Identification <text string> $00
  585. // The following is then repeated for every adjustment point
  586. // Frequency $xx xx
  587. // Volume adjustment $xx xx
  588. if (($source_data_array['interpolationmethod'] < 0) || ($source_data_array['interpolationmethod'] > 1)) {
  589. $this->errors[] = 'Invalid Interpolation Method byte in '.$frame_name.' ('.$source_data_array['interpolationmethod'].') (valid = 0 or 1)';
  590. } else {
  591. $framedata .= chr($source_data_array['interpolationmethod']);
  592. $framedata .= str_replace("\x00", '', $source_data_array['description'])."\x00";
  593. foreach ($source_data_array['data'] as $key => $val) {
  594. $framedata .= getid3_lib::BigEndian2String(intval(round($key * 2)), 2, false);
  595. $framedata .= getid3_lib::BigEndian2String($val, 2, false, true); // signed 16-bit
  596. }
  597. }
  598. break;
  599. case 'EQUA':
  600. // 4.12 EQUA Equalisation (ID3v2.3 only)
  601. // Adjustment bits $xx
  602. // This is followed by 2 bytes + ('adjustment bits' rounded up to the
  603. // nearest byte) for every equalisation band in the following format,
  604. // giving a frequency range of 0 - 32767Hz:
  605. // Increment/decrement %x (MSB of the Frequency)
  606. // Frequency (lower 15 bits)
  607. // Adjustment $xx (xx ...)
  608. if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) {
  609. $this->errors[] = 'Invalid Adjustment Bits byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)';
  610. } else {
  611. $framedata .= chr($source_data_array['adjustmentbits']);
  612. foreach ($source_data_array as $key => $val) {
  613. if ($key != 'bitsvolume') {
  614. if (($key > 32767) || ($key < 0)) {
  615. $this->errors[] = 'Invalid Frequency in '.$frame_name.' ('.$key.') (range = 0 to 32767)';
  616. } else {
  617. if ($val >= 0) {
  618. // put MSB of frequency to 1 if increment, 0 if decrement
  619. $key |= 0x8000;
  620. }
  621. $framedata .= getid3_lib::BigEndian2String($key, 2, false);
  622. $framedata .= getid3_lib::BigEndian2String($val, ceil($source_data_array['adjustmentbits'] / 8), false);
  623. }
  624. }
  625. }
  626. }
  627. break;
  628. case 'RVRB':
  629. // 4.13 RVRB Reverb
  630. // Reverb left (ms) $xx xx
  631. // Reverb right (ms) $xx xx
  632. // Reverb bounces, left $xx
  633. // Reverb bounces, right $xx
  634. // Reverb feedback, left to left $xx
  635. // Reverb feedback, left to right $xx
  636. // Reverb feedback, right to right $xx
  637. // Reverb feedback, right to left $xx
  638. // Premix left to right $xx
  639. // Premix right to left $xx
  640. if (!$this->IsWithinBitRange($source_data_array['left'], 16, false)) {
  641. $this->errors[] = 'Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['left'].') (range = 0 to 65535)';
  642. } elseif (!$this->IsWithinBitRange($source_data_array['right'], 16, false)) {
  643. $this->errors[] = 'Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['right'].') (range = 0 to 65535)';
  644. } elseif (!$this->IsWithinBitRange($source_data_array['bouncesL'], 8, false)) {
  645. $this->errors[] = 'Invalid Reverb Bounces, Left in '.$frame_name.' ('.$source_data_array['bouncesL'].') (range = 0 to 255)';
  646. } elseif (!$this->IsWithinBitRange($source_data_array['bouncesR'], 8, false)) {
  647. $this->errors[] = 'Invalid Reverb Bounces, Right in '.$frame_name.' ('.$source_data_array['bouncesR'].') (range = 0 to 255)';
  648. } elseif (!$this->IsWithinBitRange($source_data_array['feedbackLL'], 8, false)) {
  649. $this->errors[] = 'Invalid Reverb Feedback, Left-To-Left in '.$frame_name.' ('.$source_data_array['feedbackLL'].') (range = 0 to 255)';
  650. } elseif (!$this->IsWithinBitRange($source_data_array['feedbackLR'], 8, false)) {
  651. $this->errors[] = 'Invalid Reverb Feedback, Left-To-Right in '.$frame_name.' ('.$source_data_array['feedbackLR'].') (range = 0 to 255)';
  652. } elseif (!$this->IsWithinBitRange($source_data_array['feedbackRR'], 8, false)) {
  653. $this->errors[] = 'Invalid Reverb Feedback, Right-To-Right in '.$frame_name.' ('.$source_data_array['feedbackRR'].') (range = 0 to 255)';
  654. } elseif (!$this->IsWithinBitRange($source_data_array['feedbackRL'], 8, false)) {
  655. $this->errors[] = 'Invalid Reverb Feedback, Right-To-Left in '.$frame_name.' ('.$source_data_array['feedbackRL'].') (range = 0 to 255)';
  656. } elseif (!$this->IsWithinBitRange($source_data_array['premixLR'], 8, false)) {
  657. $this->errors[] = 'Invalid Premix, Left-To-Right in '.$frame_name.' ('.$source_data_array['premixLR'].') (range = 0 to 255)';
  658. } elseif (!$this->IsWithinBitRange($source_data_array['premixRL'], 8, false)) {
  659. $this->errors[] = 'Invalid Premix, Right-To-Left in '.$frame_name.' ('.$source_data_array['premixRL'].') (range = 0 to 255)';
  660. } else {
  661. $framedata .= getid3_lib::BigEndian2String($source_data_array['left'], 2, false);
  662. $framedata .= getid3_lib::BigEndian2String($source_data_array['right'], 2, false);
  663. $framedata .= chr($source_data_array['bouncesL']);
  664. $framedata .= chr($source_data_array['bouncesR']);
  665. $framedata .= chr($source_data_array['feedbackLL']);
  666. $framedata .= chr($source_data_array['feedbackLR']);
  667. $framedata .= chr($source_data_array['feedbackRR']);
  668. $framedata .= chr($source_data_array['feedbackRL']);
  669. $framedata .= chr($source_data_array['premixLR']);
  670. $framedata .= chr($source_data_array['premixRL']);
  671. }
  672. break;
  673. case 'APIC':
  674. // 4.14 APIC Attached picture
  675. // Text encoding $xx
  676. // MIME type <text string> $00
  677. // Picture type $xx
  678. // Description <text string according to encoding> $00 (00)
  679. // Picture data <binary data>
  680. $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
  681. if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
  682. $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
  683. } elseif (!$this->ID3v2IsValidAPICpicturetype($source_data_array['picturetypeid'])) {
  684. $this->errors[] = 'Invalid Picture Type byte in '.$frame_name.' ('.$source_data_array['picturetypeid'].') for ID3v2.'.$this->majorversion;
  685. } elseif (($this->majorversion >= 3) && (!$this->ID3v2IsValidAPICimageformat($source_data_array['mime']))) {
  686. $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].') for ID3v2.'.$this->majorversion;
  687. } elseif (($source_data_array['mime'] == '-->') && (!$this->IsValidURL($source_data_array['data'], false, false))) {
  688. //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
  689. // probably should be an error, need to rewrite IsValidURL() to handle other encodings
  690. $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
  691. } else {
  692. $framedata .= chr($source_data_array['encodingid']);
  693. $framedata .= str_replace("\x00", '', $source_data_array['mime'])."\x00";
  694. $framedata .= chr($source_data_array['picturetypeid']);
  695. $framedata .= @$source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
  696. $framedata .= $source_data_array['data'];
  697. }
  698. break;
  699. case 'GEOB':
  700. // 4.15 GEOB General encapsulated object
  701. // Text encoding $xx
  702. // MIME type <text string> $00
  703. // Filename <text string according to encoding> $00 (00)
  704. // Content description <text string according to encoding> $00 (00)
  705. // Encapsulated object <binary data>
  706. $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
  707. if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
  708. $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
  709. } elseif (!$this->IsValidMIMEstring($source_data_array['mime'])) {
  710. $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')';
  711. } elseif (!$source_data_array['description']) {
  712. $this->errors[] = 'Missing Description in '.$frame_name;
  713. } else {
  714. $framedata .= chr($source_data_array['encodingid']);
  715. $framedata .= str_replace("\x00", '', $source_data_array['mime'])."\x00";
  716. $framedata .= $source_data_array['filename'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
  717. $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
  718. $framedata .= $source_data_array['data'];
  719. }
  720. break;
  721. case 'PCNT':
  722. // 4.16 PCNT Play counter
  723. // When the counter reaches all one's, one byte is inserted in
  724. // front of the counter thus making the counter eight bits bigger
  725. // Counter $xx xx xx xx (xx ...)
  726. $framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
  727. break;
  728. case 'POPM':
  729. // 4.17 POPM Popularimeter
  730. // When the counter reaches all one's, one byte is inserted in
  731. // front of the counter thus making the counter eight bits bigger
  732. // Email to user <text string> $00
  733. // Rating $xx
  734. // Counter $xx xx xx xx (xx ...)
  735. if (!$this->IsWithinBitRange($source_data_array['rating'], 8, false)) {
  736. $this->errors[] = 'Invalid Rating byte in '.$frame_name.' ('.$source_data_array['rating'].') (range = 0 to 255)';
  737. } elseif (!IsValidEmail($source_data_array['email'])) {
  738. $this->errors[] = 'Invalid Email in '.$frame_name.' ('.$source_data_array['email'].')';
  739. } else {
  740. $framedata .= str_replace("\x00", '', $source_data_array['email'])."\x00";
  741. $framedata .= chr($source_data_array['rating']);
  742. $framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
  743. }
  744. break;
  745. case 'RBUF':
  746. // 4.18 RBUF Recommended buffer size
  747. // Buffer size $xx xx xx
  748. // Embedded info flag %0000000x
  749. // Offset to next tag $xx xx xx xx
  750. if (!$this->IsWithinBitRange($source_data_array['buffersize'], 24, false)) {
  751. $this->errors[] = 'Invalid Buffer Size in '.$frame_name;
  752. } elseif (!$this->IsWithinBitRange($source_data_array['nexttagoffset'], 32, false)) {
  753. $this->errors[] = 'Invalid Offset To Next Tag in '.$frame_name;
  754. } else {
  755. $framedata .= getid3_lib::BigEndian2String($source_data_array['buffersize'], 3, false);
  756. $flag .= '0000000';
  757. $flag .= $source_data_array['flags']['embededinfo'] ? '1' : '0';
  758. $framedata .= chr(bindec($flag));
  759. $framedata .= getid3_lib::BigEndian2String($source_data_array['nexttagoffset'], 4, false);
  760. }
  761. break;
  762. case 'AENC':
  763. // 4.19 AENC Audio encryption
  764. // Owner identifier <text string> $00
  765. // Preview start $xx xx
  766. // Preview length $xx xx
  767. // Encryption info <binary data>
  768. if (!$this->IsWithinBitRange($source_data_array['previewstart'], 16, false)) {
  769. $this->errors[] = 'Invalid Preview Start in '.$frame_name.' ('.$source_data_array['previewstart'].')';
  770. } elseif (!$this->IsWithinBitRange($source_data_array['previewlength'], 16, false)) {
  771. $this->errors[] = 'Invalid Preview Length in '.$frame_name.' ('.$source_data_array['previewlength'].')';
  772. } else {
  773. $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
  774. $framedata .= getid3_lib::BigEndian2String($source_data_array['previewstart'], 2, false);
  775. $framedata .= getid3_lib::BigEndian2String($source_data_array['previewlength'], 2, false);
  776. $framedata .= $source_data_array['encryptioninfo'];
  777. }
  778. break;
  779. case 'LINK':
  780. // 4.20 LINK Linked information
  781. // Frame identifier $xx xx xx xx
  782. // URL <text string> $00
  783. // ID and additional data <text string(s)>
  784. if (!getid3_id3v2::IsValidID3v2FrameName($source_data_array['frameid'], $this->majorversion)) {
  785. $this->errors[] = 'Invalid Frame Identifier in '.$frame_name.' ('.$source_data_array['frameid'].')';
  786. } elseif (!$this->IsValidURL($source_data_array['data'], true, false)) {
  787. //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
  788. // probably should be an error, need to rewrite IsValidURL() to handle other encodings
  789. $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
  790. } elseif ((($source_data_array['frameid'] == 'AENC') || ($source_data_array['frameid'] == 'APIC') || ($source_data_array['frameid'] == 'GEOB') || ($source_data_array['frameid'] == 'TXXX')) && ($source_data_array['additionaldata'] == '')) {
  791. $this->errors[] = 'Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
  792. } elseif (($source_data_array['frameid'] == 'USER') && (getid3_id3v2::LanguageLookup($source_data_array['additionaldata'], true) == '')) {
  793. $this->errors[] = 'Language must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
  794. } elseif (($source_data_array['frameid'] == 'PRIV') && ($source_data_array['additionaldata'] == '')) {
  795. $this->errors[] = 'Owner Identifier must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
  796. } elseif ((($source_data_array['frameid'] == 'COMM') || ($source_data_array['frameid'] == 'SYLT') || ($source_data_array['frameid'] == 'USLT')) && ((getid3_id3v2::LanguageLookup(substr($source_data_array['additionaldata'], 0, 3), true) == '') || (substr($source_data_array['additionaldata'], 3) == ''))) {
  797. $this->errors[] = 'Language followed by Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
  798. } else {
  799. $framedata .= $source_data_array['frameid'];
  800. $framedata .= str_replace("\x00", '', $source_data_array['data'])."\x00";
  801. switch ($source_data_array['frameid']) {
  802. case 'COMM':
  803. case 'SYLT':
  804. case 'USLT':
  805. case 'PRIV':
  806. case 'USER':
  807. case 'AENC':
  808. case 'APIC':
  809. case 'GEOB':
  810. case 'TXXX':
  811. $framedata .= $source_data_array['additionaldata'];
  812. break;
  813. case 'ASPI':
  814. case 'ETCO':
  815. case 'EQU2':
  816. case 'MCID':
  817. case 'MLLT':
  818. case 'OWNE':
  819. case 'RVA2':
  820. case 'RVRB':
  821. case 'SYTC':
  822. case 'IPLS':
  823. case 'RVAD':
  824. case 'EQUA':
  825. // no additional data required
  826. break;
  827. case 'RBUF':
  828. if ($this->majorversion == 3) {
  829. // no additional data required
  830. } else {
  831. $this->errors[] = $source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.$this->majorversion.')';
  832. }
  833. default:
  834. if ((substr($source_data_array['frameid'], 0, 1) == 'T') || (substr($source_data_array['frameid'], 0, 1) == 'W')) {
  835. // no additional data required
  836. } else {
  837. $this->errors[] = $source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.$this->majorversion.')';
  838. }
  839. break;
  840. }
  841. }
  842. break;
  843. case 'POSS':
  844. // 4.21 POSS Position synchronisation frame (ID3v2.3+ only)
  845. // Time stamp format $xx
  846. // Position $xx (xx ...)
  847. if (($source_data_array['timestampformat'] < 1) || ($source_data_array['timestampformat'] > 2)) {
  848. $this->errors[] = 'Invalid Time Stamp Format in '.$frame_name.' ('.$source_data_array['timestampformat'].') (valid = 1 or 2)';
  849. } elseif (!$this->IsWithinBitRange($source_data_array['position'], 32, false)) {
  850. $this->errors[] = 'Invalid Position in '.$frame_name.' ('.$source_data_array['position'].') (range = 0 to 4294967295)';
  851. } else {
  852. $framedata .= chr($source_data_array['timestampformat']);
  853. $framedata .= getid3_lib::BigEndian2String($source_data_array['position'], 4, false);
  854. }
  855. break;
  856. case 'USER':
  857. // 4.22 USER Terms of use (ID3v2.3+ only)
  858. // Text encoding $xx
  859. // Language $xx xx xx
  860. // The actual text <text string according to encoding>
  861. $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
  862. if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
  863. $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')';
  864. } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
  865. $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
  866. } else {
  867. $framedata .= chr($source_data_array['encodingid']);
  868. $framedata .= strtolower($source_data_array['language']);
  869. $framedata .= $source_data_array['data'];
  870. }
  871. break;
  872. case 'OWNE':
  873. // 4.23 OWNE Ownership frame (ID3v2.3+ only)
  874. // Text encoding $xx
  875. // Price paid <text string> $00
  876. // Date of purch. <text string>
  877. // Seller <text string according to encoding>
  878. $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);

Large files files are truncated, but you can click here to view the full file