getid3_lyrics3

The WordPress Core getid3 lyrics3 class.

Defined (1)

The class is defined in the following location(s).

/wp-includes/ID3/module.tag.lyrics3.php  
  1. class getid3_lyrics3 extends getid3_handler 
  2.  
  3. public function Analyze() { 
  4. $info = &$this->getid3->info; 
  5.  
  6. // http://www.volweb.cz/str/tags.htm 
  7.  
  8. if (!getid3_lib::intValueSupported($info['filesize'])) { 
  9. $info['warning'][] = 'Unable to check for Lyrics3 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; 
  10. return false; 
  11.  
  12. $this->fseek((0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - "LYRICSEND" - [Lyrics3size] 
  13. $lyrics3_id3v1 = $this->fread(128 + 9 + 6); 
  14. $lyrics3lsz = substr($lyrics3_id3v1, 0, 6); // Lyrics3size 
  15. $lyrics3end = substr($lyrics3_id3v1, 6, 9); // LYRICSEND or LYRICS200 
  16. $id3v1tag = substr($lyrics3_id3v1, 15, 128); // ID3v1 
  17.  
  18. if ($lyrics3end == 'LYRICSEND') { 
  19. // Lyrics3v1, ID3v1, no APE 
  20.  
  21. $lyrics3size = 5100; 
  22. $lyrics3offset = $info['filesize'] - 128 - $lyrics3size; 
  23. $lyrics3version = 1; 
  24.  
  25. } elseif ($lyrics3end == 'LYRICS200') { 
  26. // Lyrics3v2, ID3v1, no APE 
  27.  
  28. // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200' 
  29. $lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200'); 
  30. $lyrics3offset = $info['filesize'] - 128 - $lyrics3size; 
  31. $lyrics3version = 2; 
  32.  
  33. } elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICSEND')) { 
  34. // Lyrics3v1, no ID3v1, no APE 
  35.  
  36. $lyrics3size = 5100; 
  37. $lyrics3offset = $info['filesize'] - $lyrics3size; 
  38. $lyrics3version = 1; 
  39. $lyrics3offset = $info['filesize'] - $lyrics3size; 
  40.  
  41. } elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICS200')) { 
  42.  
  43. // Lyrics3v2, no ID3v1, no APE 
  44.  
  45. $lyrics3size = strrev(substr(strrev($lyrics3_id3v1), 9, 6)) + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200' 
  46. $lyrics3offset = $info['filesize'] - $lyrics3size; 
  47. $lyrics3version = 2; 
  48.  
  49. } else { 
  50.  
  51. if (isset($info['ape']['tag_offset_start']) && ($info['ape']['tag_offset_start'] > 15)) { 
  52.  
  53. $this->fseek($info['ape']['tag_offset_start'] - 15); 
  54. $lyrics3lsz = $this->fread(6); 
  55. $lyrics3end = $this->fread(9); 
  56.  
  57. if ($lyrics3end == 'LYRICSEND') { 
  58. // Lyrics3v1, APE, maybe ID3v1 
  59.  
  60. $lyrics3size = 5100; 
  61. $lyrics3offset = $info['ape']['tag_offset_start'] - $lyrics3size; 
  62. $info['avdataend'] = $lyrics3offset; 
  63. $lyrics3version = 1; 
  64. $info['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability'; 
  65.  
  66. } elseif ($lyrics3end == 'LYRICS200') { 
  67. // Lyrics3v2, APE, maybe ID3v1 
  68.  
  69. $lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200' 
  70. $lyrics3offset = $info['ape']['tag_offset_start'] - $lyrics3size; 
  71. $lyrics3version = 2; 
  72. $info['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability'; 
  73.  
  74.  
  75.  
  76.  
  77. if (isset($lyrics3offset)) { 
  78. $info['avdataend'] = $lyrics3offset; 
  79. $this->getLyrics3Data($lyrics3offset, $lyrics3version, $lyrics3size); 
  80.  
  81. if (!isset($info['ape'])) { 
  82. if (isset($info['lyrics3']['tag_offset_start'])) { 
  83. $GETID3_ERRORARRAY = &$info['warning']; 
  84. getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true); 
  85. $getid3_temp = new getID3(); 
  86. $getid3_temp->openfile($this->getid3->filename); 
  87. $getid3_apetag = new getid3_apetag($getid3_temp); 
  88. $getid3_apetag->overrideendoffset = $info['lyrics3']['tag_offset_start']; 
  89. $getid3_apetag->Analyze(); 
  90. if (!empty($getid3_temp->info['ape'])) { 
  91. $info['ape'] = $getid3_temp->info['ape']; 
  92. if (!empty($getid3_temp->info['replay_gain'])) { 
  93. $info['replay_gain'] = $getid3_temp->info['replay_gain']; 
  94. unset($getid3_temp, $getid3_apetag); 
  95. } else { 
  96. $info['warning'][] = 'Lyrics3 and APE tags appear to have become entangled (most likely due to updating the APE tags with a non-Lyrics3-aware tagger)'; 
  97.  
  98.  
  99. return true; 
  100.  
  101. public function getLyrics3Data($endoffset, $version, $length) { 
  102. // http://www.volweb.cz/str/tags.htm 
  103.  
  104. $info = &$this->getid3->info; 
  105.  
  106. if (!getid3_lib::intValueSupported($endoffset)) { 
  107. $info['warning'][] = 'Unable to check for Lyrics3 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; 
  108. return false; 
  109.  
  110. $this->fseek($endoffset); 
  111. if ($length <= 0) { 
  112. return false; 
  113. $rawdata = $this->fread($length); 
  114.  
  115. $ParsedLyrics3['raw']['lyrics3version'] = $version; 
  116. $ParsedLyrics3['raw']['lyrics3tagsize'] = $length; 
  117. $ParsedLyrics3['tag_offset_start'] = $endoffset; 
  118. $ParsedLyrics3['tag_offset_end'] = $endoffset + $length - 1; 
  119.  
  120. if (substr($rawdata, 0, 11) != 'LYRICSBEGIN') { 
  121. if (strpos($rawdata, 'LYRICSBEGIN') !== false) { 
  122.  
  123. $info['warning'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but actually found at '.($endoffset + strpos($rawdata, 'LYRICSBEGIN')).' - this is invalid for Lyrics3 v'.$version; 
  124. $info['avdataend'] = $endoffset + strpos($rawdata, 'LYRICSBEGIN'); 
  125. $rawdata = substr($rawdata, strpos($rawdata, 'LYRICSBEGIN')); 
  126. $length = strlen($rawdata); 
  127. $ParsedLyrics3['tag_offset_start'] = $info['avdataend']; 
  128. $ParsedLyrics3['raw']['lyrics3tagsize'] = $length; 
  129.  
  130. } else { 
  131.  
  132. $info['error'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but found "'.substr($rawdata, 0, 11).'" instead'; 
  133. return false; 
  134.  
  135.  
  136.  
  137. switch ($version) { 
  138.  
  139. case 1: 
  140. if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICSEND') { 
  141. $ParsedLyrics3['raw']['LYR'] = trim(substr($rawdata, 11, strlen($rawdata) - 11 - 9)); 
  142. $this->Lyrics3LyricsTimestampParse($ParsedLyrics3); 
  143. } else { 
  144. $info['error'][] = '"LYRICSEND" expected at '.($this->ftell() - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead'; 
  145. return false; 
  146. break; 
  147.  
  148. case 2: 
  149. if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICS200') { 
  150. $ParsedLyrics3['raw']['unparsed'] = substr($rawdata, 11, strlen($rawdata) - 11 - 9 - 6); // LYRICSBEGIN + LYRICS200 + LSZ 
  151. $rawdata = $ParsedLyrics3['raw']['unparsed']; 
  152. while (strlen($rawdata) > 0) { 
  153. $fieldname = substr($rawdata, 0, 3); 
  154. $fieldsize = (int) substr($rawdata, 3, 5); 
  155. $ParsedLyrics3['raw'][$fieldname] = substr($rawdata, 8, $fieldsize); 
  156. $rawdata = substr($rawdata, 3 + 5 + $fieldsize); 
  157.  
  158. if (isset($ParsedLyrics3['raw']['IND'])) { 
  159. $i = 0; 
  160. $flagnames = array('lyrics', 'timestamps', 'inhibitrandom'); 
  161. foreach ($flagnames as $flagname) { 
  162. if (strlen($ParsedLyrics3['raw']['IND']) > $i++) { 
  163. $ParsedLyrics3['flags'][$flagname] = $this->IntString2Bool(substr($ParsedLyrics3['raw']['IND'], $i, 1 - 1)); 
  164.  
  165. $fieldnametranslation = array('ETT'=>'title', 'EAR'=>'artist', 'EAL'=>'album', 'INF'=>'comment', 'AUT'=>'author'); 
  166. foreach ($fieldnametranslation as $key => $value) { 
  167. if (isset($ParsedLyrics3['raw'][$key])) { 
  168. $ParsedLyrics3['comments'][$value][] = trim($ParsedLyrics3['raw'][$key]); 
  169.  
  170. if (isset($ParsedLyrics3['raw']['IMG'])) { 
  171. $imagestrings = explode("\r\n", $ParsedLyrics3['raw']['IMG']); 
  172. foreach ($imagestrings as $key => $imagestring) { 
  173. if (strpos($imagestring, '||') !== false) { 
  174. $imagearray = explode('||', $imagestring); 
  175. $ParsedLyrics3['images'][$key]['filename'] = (isset($imagearray[0]) ? $imagearray[0] : ''); 
  176. $ParsedLyrics3['images'][$key]['description'] = (isset($imagearray[1]) ? $imagearray[1] : ''); 
  177. $ParsedLyrics3['images'][$key]['timestamp'] = $this->Lyrics3Timestamp2Seconds(isset($imagearray[2]) ? $imagearray[2] : ''); 
  178. if (isset($ParsedLyrics3['raw']['LYR'])) { 
  179. $this->Lyrics3LyricsTimestampParse($ParsedLyrics3); 
  180. } else { 
  181. $info['error'][] = '"LYRICS200" expected at '.($this->ftell() - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead'; 
  182. return false; 
  183. break; 
  184.  
  185. default: 
  186. $info['error'][] = 'Cannot process Lyrics3 version '.$version.' (only v1 and v2)'; 
  187. return false; 
  188. break; 
  189.  
  190.  
  191. if (isset($info['id3v1']['tag_offset_start']) && ($info['id3v1']['tag_offset_start'] <= $ParsedLyrics3['tag_offset_end'])) { 
  192. $info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in Lyrics3 tag data'; 
  193. unset($info['id3v1']); 
  194. foreach ($info['warning'] as $key => $value) { 
  195. if ($value == 'Some ID3v1 fields do not use NULL characters for padding') { 
  196. unset($info['warning'][$key]); 
  197. sort($info['warning']); 
  198. break; 
  199.  
  200. $info['lyrics3'] = $ParsedLyrics3; 
  201.  
  202. return true; 
  203.  
  204. public function Lyrics3Timestamp2Seconds($rawtimestamp) { 
  205. if (preg_match('#^\\[([0-9]{2}):([0-9]{2})\\]$#', $rawtimestamp, $regs)) { 
  206. return (int) (($regs[1] * 60) + $regs[2]); 
  207. return false; 
  208.  
  209. public function Lyrics3LyricsTimestampParse(&$Lyrics3data) { 
  210. $lyricsarray = explode("\r\n", $Lyrics3data['raw']['LYR']); 
  211. foreach ($lyricsarray as $key => $lyricline) { 
  212. $regs = array(); 
  213. unset($thislinetimestamps); 
  214. while (preg_match('#^(\\[[0-9]{2}:[0-9]{2}\\])#', $lyricline, $regs)) { 
  215. $thislinetimestamps[] = $this->Lyrics3Timestamp2Seconds($regs[0]); 
  216. $lyricline = str_replace($regs[0], '', $lyricline); 
  217. $notimestamplyricsarray[$key] = $lyricline; 
  218. if (isset($thislinetimestamps) && is_array($thislinetimestamps)) { 
  219. sort($thislinetimestamps); 
  220. foreach ($thislinetimestamps as $timestampkey => $timestamp) { 
  221. if (isset($Lyrics3data['synchedlyrics'][$timestamp])) { 
  222. // timestamps only have a 1-second resolution, it's possible that multiple lines 
  223. // could have the same timestamp, if so, append 
  224. $Lyrics3data['synchedlyrics'][$timestamp] .= "\r\n".$lyricline; 
  225. } else { 
  226. $Lyrics3data['synchedlyrics'][$timestamp] = $lyricline; 
  227. $Lyrics3data['unsynchedlyrics'] = implode("\r\n", $notimestamplyricsarray); 
  228. if (isset($Lyrics3data['synchedlyrics']) && is_array($Lyrics3data['synchedlyrics'])) { 
  229. ksort($Lyrics3data['synchedlyrics']); 
  230. return true; 
  231.  
  232. public function IntString2Bool($char) { 
  233. if ($char == '1') { 
  234. return true; 
  235. } elseif ($char == '0') { 
  236. return false; 
  237. return null;