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

/zina/extras/tag_editor.php

https://bitbucket.org/helmespc/zina2
PHP | 815 lines | 677 code | 104 blank | 34 comment | 141 complexity | 8c9366524b0ba85e8e08c7deb6dcc49c MD5 | raw file
  1. <?php
  2. function zina_content_edit_tags(&$zina, $path, $opts) {
  3. global $zc;
  4. if (!is_writeable($zc['cur_dir'])) {
  5. zina_set_message(zt('Directory is not writeable: @dir', array('@dir'=>$zc['cur_dir'])),'error');
  6. zina_goto($path);
  7. }
  8. $dir = zina_get_directory($path);
  9. zina_content_breadcrumb($zina, $path, $dir['title'], true);
  10. require_once($zc['zina_dir_abs'].'/extras/getid3/getid3.php');
  11. $getID3 = new getID3;
  12. $getID3->setOption(array('encoding'=>$zc['tags_format']));
  13. getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.php', __FILE__, true);
  14. if (isset($_POST['write_tags'])) { # WRITE TAG
  15. if (!zina_token_sess_check()) return zina_page_main($path);
  16. if (empty($_POST['files'])) {
  17. zina_set_message(zt('No files to tag'), 'error');
  18. return zina_page_main($path);
  19. }
  20. $changed = 0;
  21. foreach($_POST['files'] as $key => $file) {
  22. $file = zrawurldecode($file);
  23. $full_path = $zc['mp3_dir'].'/'.((empty($path) || $path == '.') ? $file : $path.'/'.$file);
  24. if (file_exists($full_path) && zfile_check_location($full_path, $zc['mp3_dir']) && is_writeable($full_path)) {
  25. $getID3 = new getID3;
  26. $info = $getID3->analyze($full_path);
  27. getid3_lib::CopyTagsToComments($info);
  28. $mtime = filemtime($full_path);
  29. if (isset($_POST['tag_format_override'])) {
  30. $tag_formats_to_write = (isset($_POST['tag_format'])) ? $_POST['tag_format'] : array();
  31. if (empty($tag_formats_to_write)) {
  32. zina_set_message(zt('No global tag formats selected'), 'error');
  33. break;
  34. }
  35. } else {
  36. $tag_formats_to_write = (isset($_POST['tag_formats'][$key])) ? $_POST['tag_formats'][$key] : array();
  37. if (empty($tag_formats_to_write)) {
  38. zina_set_message(zt('No individual tag formats selected'), 'warn');
  39. continue;
  40. }
  41. }
  42. $tags = array();
  43. $common = array('title','artist','album','year','comment','genre');
  44. foreach($common as $keynew) {
  45. if (isset($_POST[$keynew.'_override'])) {
  46. $tags[$keynew][] = getid3_lib::SafeStripSlashes($_POST[$keynew]);
  47. } else {
  48. $tags[$keynew][] = getid3_lib::SafeStripSlashes($_POST[$keynew.'s'][$key]);
  49. }
  50. }
  51. $tags['tracknumber'][] = getid3_lib::SafeStripSlashes($_POST['tracks'][$key]);
  52. if ($zc['tags_keep_existing_data'] && isset($info['comments'])) {
  53. foreach($info['comments'] as $keyold=>$valold) {
  54. if (!isset($tags[$keyold])) {
  55. $tags[$keyold] = $valold;
  56. }
  57. }
  58. # for array compare
  59. if (!isset($info['comments']['comment'])) $info['comments']['comment'] = array('');
  60. if (!isset($info['comments']['tracknumber'])) $info['comments']['tracknumber'] = array('');
  61. }
  62. if (isset($info['tags'])) {
  63. $existing_tags = array_keys($info['tags']);
  64. foreach($existing_tags as $key => $t) {
  65. if ($t == 'id3v2') {
  66. $existing_tags[$key] = $t.'.'.$info[$t]['majorversion'];
  67. }
  68. }
  69. }
  70. if (isset($info['comments']) && $info['comments'] == $tags && $existing_tags == $tag_formats_to_write) {
  71. if (isset($info['comments']['tracknumber']) && isset($tags['tracknumber'])) {
  72. if ($info['comments']['tracknumber'] === $tags['tracknumber']) continue;
  73. } else {
  74. continue;
  75. }
  76. }
  77. $tagwriter = new getid3_writetags;
  78. $tagwriter->filename = $full_path;
  79. $tagwriter->tagformats = $tag_formats_to_write;
  80. $tagwriter->overwrite_tags = !$zc['tags_keep_existing_data'];
  81. $tagwriter->tag_encoding = $zc['tags_format'];
  82. if (!empty($_POST['remove_other_tags'])) $tagwriter->remove_other_tags = true;
  83. if (!empty($_POST['skip_errors'])) $tagwriter->skip_errors = true;
  84. $tagwriter->tag_data = $tags;
  85. if ($tagwriter->WriteTags()) {
  86. zina_set_message(zt('Successfully wrote tag: @file', array('@file'=>$file)));
  87. #TODO: genre update?
  88. if ($zc['tags_filemtime']) @touch($full_path, $mtime);
  89. zdb_log_stat('insertonly', $path, $file, null, true);
  90. $changed++;
  91. } else {
  92. zina_set_message(zt('Failed to write tags for @file.', array('@file'=>$full_path)), 'error');
  93. if (!empty($tagwriter->errors)) {
  94. zina_set_message(implode('<BR><BR>', $tagwriter->errors), 'error');
  95. }
  96. }
  97. if (!empty($tagwriter->warnings)) {
  98. zina_set_message(implode('<BR><BR>', $tagwriter->warnings), 'warn');
  99. }
  100. } else {
  101. zina_set_message(zt('File does not exist or is not writeable: @file', array('@file'=>$full_path)), 'error');
  102. }
  103. } # end loop
  104. if ($changed > 0) zdb_genre_populate(time(), $context);
  105. zina_set_message(zt('@changed of @total files tags updated', array('@changed'=>$changed, '@total'=>sizeof($_POST['files']))));
  106. if ($zc['embed'] != 'standalone') zina_goto($path, 'l=71');
  107. } # END POST
  108. $length = 0;
  109. $theme_opts = array();
  110. $tag_types = array();
  111. if (!empty($dir['files'])) {
  112. $i = 0;
  113. foreach($dir['files'] as $key => $file) {
  114. $full_path = $zc['mp3_dir'].'/'.((empty($file['path']) || $file['path'] == '.') ? $file['file'] : $file['path'].'/'.$file['file']);
  115. $getID3 = new getID3;
  116. $file_info = $getID3->analyze($full_path);
  117. if (!isset($file_info['fileformat'])) {
  118. zina_set_message(zt('File format not supported: @file', array('@file'=>$full_path)), 'warn');
  119. if (isset($getID3->info['error']) && !empty($getID3->info['error'])) {
  120. foreach($getID3->info['error'] as $error) {
  121. zina_set_message(zt('@error', array('@error'=>$error)), 'error');
  122. }
  123. }
  124. if (isset($getID3->info['warning']) && !empty($getID3->info['warning'])) {
  125. foreach($getID3->info['warning'] as $warning) {
  126. zina_set_message(zt('@error', array('@error'=>$warning)), 'warn');
  127. }
  128. }
  129. continue;
  130. }
  131. getid3_lib::CopyTagsToComments($file_info);
  132. $offsets[] = 150+round(($length*75));
  133. $length += $file_info['playtime_seconds'];
  134. $ValidTagTypes = zina_extras_tags_formats($file_info['fileformat'], @$file_info['audio']['dataformat']);
  135. if (isset($file_info['error'])) {
  136. if (is_array($file_info['error'])) {
  137. foreach($file_info['error'] as $error) {
  138. zina_set_message(zt('Error reading tag: @error', array('@error'=>$error)), 'warn');
  139. }
  140. } else {
  141. zina_set_message(zt('Error reading tag: @error', array('@error'=>$file_info['error'])), 'warn');
  142. }
  143. }
  144. $tag_types = array_merge($tag_types, $ValidTagTypes);
  145. $track = FixTextFields(@implode(', ',$file_info['comments']['tracknumber']));
  146. $artist = FixTextFields(@implode(', ',$file_info['comments']['artist']));
  147. $album = FixTextFields(@implode(', ',$file_info['comments']['album']));
  148. $title = FixTextFields(@implode(', ',$file_info['comments']['title']));
  149. $genre = FixTextFields(@implode(', ',$file_info['comments']['genre']));
  150. $year = FixTextFields(@implode(', ',$file_info['comments']['year']));
  151. $comment = FixTextFields(@implode(', ',$file_info['comments']['comment']));
  152. $artists[$artist] = (isset($artists[$artist])) ? ++$artists[$artist] : 1;
  153. $albums[$album] = (isset($albums[$album])) ? ++$albums[$album] : 1;
  154. $years[$year] = (isset($years[$year])) ? ++$years[$year] : 1;
  155. $genres[$genre] = (isset($genres[$genre])) ? ++$genres[$genre] : 1;
  156. $comments[$comment] = (isset($comments[$comment])) ? ++$comments[$comment] : 1;
  157. if (empty($title)) $theme_opts['missing_tags'] = true;
  158. $rows[] = array(
  159. 'cols' => array(
  160. 'track'=> array('size'=>2, 'value'=>$track),
  161. 'title'=> array('size'=>30, 'max'=>50, 'value' => (empty($title) ? ztheme('song_title', $file['file']) : $title)),
  162. 'artist'=> array('size'=>30, 'max'=>30, 'value'=>$artist),
  163. 'album'=> array('size'=>20, 'max'=>40, 'value'=>$album),
  164. 'genre'=> array('size'=>20, 'max'=>30, 'value'=>$genre),
  165. 'year'=> array('size'=>6, 'max'=>6, 'value'=>$year),
  166. 'comment'=> array('size'=>20, 'max'=>40, 'value'=>$comment),
  167. ),
  168. 'valid_tag_types' => $ValidTagTypes,
  169. 'tags' => (isset($file_info['tags'])) ? array_keys($file_info['tags']) : array(),
  170. 'file' => $file['file'],
  171. 'hidden' => zina_content_form_helper('files[]', array('type'=>'hidden', 'def'=>''), zrawurlencode($file['file'])),
  172. );
  173. $i++;
  174. }
  175. $theme_opts['tag_types'] = array_unique($tag_types);
  176. if (!isset($_POST['write_tags'])) {
  177. $length = round($length);
  178. $disc_id = cddb_disc_id($offsets, $length);
  179. $url = "http://".$zc['tags_cddb_server']."/~cddb/cddb.cgi?cmd=cddb+query+$disc_id+".count($offsets)."+".implode('+', $offsets)."+".$length.
  180. "&hello=anonymous+unknown+zina+".ZINA_VERSION."&proto=6";
  181. zina_set_js('file', 'extras/jquery.js');
  182. $url = zurl($path, 'l=72&pl='.zrawurlencode($url).'&rand='.time());
  183. $js = 'function zinaTagSearch(){'.
  184. 'jQuery("#zina-matches").html("'.zt('Loading...').'");'.
  185. 'jQuery("#zina-matches").load("'.$url.'",function(data,status,xml){'.
  186. 'if(status=="error"){'.
  187. 'jQuery("#zina-matches").html(\'Error: \'+msg+status+data);'.
  188. '}else{'.
  189. 'jQuery("#zina-matches").addClass("messages status");'.
  190. '}'.
  191. '});}';
  192. zina_set_js('inline', $js);
  193. }
  194. }
  195. $theme_opts['auto_start'] = $zc['tags_cddb_auto_start'];
  196. if (sizeof($artists) > 1) arsort($artists);
  197. if (sizeof($albums) > 1) arsort($albums);
  198. if (sizeof($years) > 1) arsort($years);
  199. if (sizeof($genres) > 1) arsort($genres);
  200. if (sizeof($comments) > 1) arsort($comments);
  201. $artist = key($artists);
  202. $album = key($albums);
  203. $year = key($years);
  204. $genre = key($genres);
  205. $comment = key($comments);
  206. $theme_opts['various'] = $dir['various'];
  207. $m['artist'] = array('label'=>"Artist", 'value'=>$artist, 'over'=>(!$dir['various']));
  208. $m['album'] = array('label'=>"Album", 'value'=>$album, 'over'=>1);
  209. $m['genre'] = array('label'=>"Genre", 'value'=>$genre, 'over'=>1);
  210. $m['year'] = array('label'=>"Year", 'value'=>$year, 'over'=>1);
  211. $m['comment'] = array('label'=>"Comment", 'value'=>$comment, 'over'=>1);
  212. $form_id = 'zina-tags';
  213. $attr = 'action="'.zurl($path, 'l=71').'" id="'.$form_id.'"';
  214. $zina['content'] = ztheme('tag_editor', $m, $rows, $attr, $form_id, $theme_opts);
  215. }
  216. function ztheme_tag_editor($global, $rows, $form_attr, $form_id, $opts) {
  217. $output = '<h3>'.zt('Edit Tags').'</h3><br/>';
  218. if (!isset($_POST['write_tags'])) {
  219. $txt = '';
  220. if ($opts['auto_start']) {
  221. $txt = '<script type="text/javascript">zinaTagSearch();</script>';
  222. } else {
  223. $txt = '<a href="javascript: void 0;" onclick="zinaTagSearch();">'.zt('Search for tags').'</a>';
  224. }
  225. $output .= '<div id="zina-matches" class="zina_messages">'.$txt.'</div>';
  226. }
  227. $output .= '<table border=0>'.
  228. '<tr><td class="search-header" colspan="3"><strong>'.zt('Global Settings').
  229. '</strong></td></tr>';
  230. foreach($global as $key => $item) {
  231. $output .= '<tr><td valign="top">'.zt($item['label']).'</td>'.
  232. '<td valign="top">'.zina_content_form_helper($key, array('type'=>'textfield', 'id'=>'zina-'.$key, 'def'=>'', 'size'=>30, 'max'=>50, 'v'=>array('req')), $item['value']);
  233. if ($key == 'comment') {
  234. $output .= ' <small><a href="javascript: void 0;" onclick="zClearField(\''.$form_id.'\',\''.$key.'\');">'.zt('Clear').'</a>';
  235. }
  236. $output .= '<br/><div id="zina-'.$key.'-orig" class="small">'.$item['value'].'</div></td>'.
  237. '<td valign="top">'.zina_content_form_helper($key.'_override', array('type'=>'checkbox', 'def'=>'', 'onclick'=>'TagFieldToggle("'.$form_id.'","'.$key.'s[]");'), $item['over']).
  238. ' '.zt('Override/set values in files below').'</td></tr>';
  239. }
  240. $output .= '<tr><td>'.zt('Write tag formats').'</td><td>'.
  241. zina_content_tags_format($opts['tag_types'], array(), 'tag_format[]').
  242. '</td><td>'.
  243. zina_content_form_helper('tag_format_override', array('type'=>'checkbox', 'def'=>'', 'onclick'=>'TagFieldToggle("'.$form_id.'","tag_formats");'), 1).' '.zt('Override/set values in files below').
  244. '</td></tr>'.
  245. '<tr><td colspan="2">'.
  246. zina_content_form_helper('remove_other_tags', array('type'=>'checkbox', 'def'=>''), 1).' '.zt('Remove non-selected tag formats when writing new tag').' '.
  247. zina_content_form_helper('skip_errors', array('type'=>'checkbox', 'def'=>''), 0).' '.zt('Ignore frame errors').
  248. '</td></tr>'.
  249. '</table>';
  250. $output .= '<p style="text-align:center;"><input type="submit" name="write_tags" value="'.zt('Submit').'"/></p>';
  251. $js = '';
  252. $SMALL_WORDS = '(a|an|and|as|at|but|by|en|for|if|in|of|on|or|the|to|v[.]?|via|vs[.]?)';
  253. $foo = <<<FOO
  254. /*
  255. * The list of "small words" which are not capped comes from
  256. * the New York Times Manual of Style, plus "vs" and "v".
  257. *
  258. * Original Perl version by:
  259. * John Gruber
  260. * http://daringfireball.net/
  261. * 10 May 2008
  262. *
  263. * Adapted to JavaScript by:
  264. * Marshall Elfstrand
  265. * http://vengefulcow.com/
  266. * 21 May 2008
  267. *
  268. * License: http://www.opensource.org/licenses/mit-license.php
  269. */
  270. var TitleCase = {};
  271. TitleCase.SPLIT_PATTERN = /([:.;?!][ ]|(?:[ ]|^)["“])/;
  272. TitleCase.BASE_PATTERN = /\b([A-Za-z][a-z.'’]*)\b/g;
  273. TitleCase.INLINE_DOT_PATTERN = /[A-Za-z][.][A-Za-z]/;
  274. TitleCase.SMALL_WORD_PATTERN = /\b$SMALL_WORDS\b/ig;
  275. TitleCase.SMALL_WORD_FIRST_PATTERN = /^(\W*)$SMALL_WORDS\b/ig;
  276. TitleCase.SMALL_WORD_LAST_PATTERN = /\b$SMALL_WORDS(\W*)$/ig;
  277. TitleCase.VS_PATTERN = / V(s?)\. /g;
  278. TitleCase.POSSESSIVE_PATTERN = /(['’])S\b/g;
  279. TitleCase.capitalize = function(w) {
  280. return w.substr(0, 1).toUpperCase() + w.substr(1).toLowerCase();
  281. }
  282. TitleCase.capitalizeUnlessInlineDot = function(w) {
  283. if (TitleCase.INLINE_DOT_PATTERN.test(w)) return w;
  284. else return TitleCase.capitalize(w);
  285. }
  286. TitleCase.capitalizeFirstWord = function(match, leadingPunctuation, smallWord) {
  287. return leadingPunctuation + TitleCase.capitalize(smallWord);
  288. }
  289. TitleCase.capitalizeLastWord = function(match, smallWord, trailingPunctuation) {
  290. return TitleCase.capitalize(smallWord) + trailingPunctuation;
  291. }
  292. TitleCase.toLowerCase=function(w){return w.toLowerCase();}
  293. TitleCase.toUpperCase=function(w){return w.toUpperCase();}
  294. TitleCase.toTitleCase = function(input) {
  295. var result = "";
  296. var tokens = input.split(TitleCase.SPLIT_PATTERN);
  297. for (var i = 0; i < tokens.length; i++) {
  298. var s = tokens[i];
  299. s = s.replace(TitleCase.BASE_PATTERN, TitleCase.capitalizeUnlessInlineDot);
  300. s = s.replace(TitleCase.SMALL_WORD_PATTERN, TitleCase.toLowerCase);
  301. s = s.replace(TitleCase.SMALL_WORD_FIRST_PATTERN, TitleCase.capitalizeFirstWord);
  302. s = s.replace(TitleCase.SMALL_WORD_LAST_PATTERN, TitleCase.capitalizeLastWord);
  303. result += s;
  304. }
  305. // Handle special cases.
  306. result = result.replace(TitleCase.VS_PATTERN, ' v$1. ');
  307. result = result.replace(TitleCase.POSSESSIVE_PATTERN, '$1s');
  308. return result;
  309. }
  310. String.prototype.toTitleCase = function() {
  311. return TitleCase.toTitleCase(this);
  312. };
  313. FOO;
  314. $js .= $foo;
  315. $js .= 'function FillTrackNumbers(formid){'.
  316. 'var e=document.forms[formid].elements["tracks[]"];'.
  317. 'for(var i=0;i<e.length;i++){'.
  318. 'e[i].value = i+1;'.
  319. '}'.
  320. '}';
  321. $js .= 'function zClearField(formid, field){'.
  322. 'document.forms[formid].elements[field].value="";'.
  323. '}';
  324. $js .= 'function zTitleCase(formid, field){'.
  325. 'var e=document.forms[formid].elements[field+"[]"];'.
  326. 'for(var i=0;i<e.length;i++){'.
  327. 'e[i].value = e[i].value.toTitleCase();'.
  328. '}'.
  329. '}';
  330. $js .= 'function zStripLast(formid,field){'.
  331. 'var e=document.forms[formid].elements[field+"[]"];'.
  332. 'for(var i=0;i<e.length;i++){'.
  333. 'var len = e[i].value.length;'.
  334. 'if (len > 4) e[i].value = e[i].value.slice(0,len-4);'.
  335. '}'.
  336. '}';
  337. $js .= 'function TagFieldToggle(formid, element) {'.
  338. 'var $j = jQuery;'.
  339. 'if(element=="tag_formats"){'.
  340. 'var length=document.forms[formid].elements["tracks[]"].length;'.
  341. 'for(var i=0;i<length;i++){'.
  342. 'var e=document.forms[formid].elements["tag_formats["+i+"][]"];'.
  343. 'if (!e) continue;'. # unsupported tag format
  344. 'if (e.length){'.
  345. 'for(var j=0;j<e.length;j++){'.
  346. 'e[j].disabled = !e[j].disabled;'.
  347. '}'.
  348. '}else{'. # singular
  349. 'e.disabled = !e.disabled;'.
  350. '}'.
  351. '}'.
  352. '}else{'.
  353. 'var e=document.forms[formid].elements[element];'.
  354. 'for(var i=0;i<e.length;i++){'.
  355. 'e[i].disabled = !e[i].disabled;'.
  356. 'e[i].style.backgroundColor = (e[i].disabled) ? "#CCC" : "";'.
  357. '}'.
  358. '}'.
  359. '}';
  360. # IE doesnt grey out disabled inputs =(
  361. $js2 = 'var cols = new Array("artists[]", "albums[]", "genres[]", "years[]", "comments[]");'.
  362. 'for(j=0;j<cols.length;j++){'.
  363. 'var e=document.forms["'.$form_id.'"].elements[cols[j]];'.
  364. 'for(var i=0;i<e.length;i++){'.
  365. 'e[i].style.backgroundColor = (e[i].disabled) ? "#CCC" : "";'.
  366. '}'.
  367. '}';
  368. $js2 = '<script>'.$js2.'</script>';
  369. zina_set_js('inline', $js);
  370. $header = array(zt('Track'), zt('Title'), zt('Artist'), zt('Album'), zt('Genre'), zt('Year'), zt('Comment'));
  371. $output .= '<a href="javascript: void 0;" onclick="FillTrackNumbers(\''.$form_id.'\');">'.zt('Fill Track Numbers').'</a>';
  372. $output .= ' | <a href="javascript: void 0;" onclick="zTitleCase(\''.$form_id.'\',\'titles\');">'.zt('Title Case').'</a>';
  373. if ($opts['various'])
  374. $output .= ' | <a href="javascript: void 0;" onclick="zTitleCase(\''.$form_id.'\',\'artists\');">'.zt('Artist Case').'</a>';
  375. if (isset($opts['missing_tags']))
  376. $output .= ' | <a href="javascript: void 0;" onclick="zStripLast(\''.$form_id.'\',\'titles\');">'.zt('Strip Last Four Letters').'</a>';
  377. $output .= '<table>'.
  378. '<tr><td class="search-header"><strong>'.
  379. implode('</strong></td><td class="search-header"><strong>', $header).
  380. '</strong></td></tr>';
  381. $color = 0;
  382. foreach($rows as $i=>$row) {
  383. $output .= '<tr>';
  384. foreach($row['cols'] as $key => $item) {
  385. $field = array('type'=>'textfield', 'id'=>'zina-'.$key.'-'.$i, 'def'=>'', 'size'=>$item['size']);
  386. if (isset($item['max'])) $field['max'] = $item['max'];
  387. $disabled = (isset($global[$key]['over']) && $global[$key]['over']);
  388. $output .= '<td class="row'.$color.'" valign="top">'.zina_content_form_helper($key.'s[]', $field, $item['value'], $disabled).
  389. '<br/><div id="zina-'.$key.'-'.$i.'-orig" class="small">'.$item['value'].'</div></td>';
  390. }
  391. $output .= '</tr>'."\n".
  392. '<tr><td class="small row'.$color.'">'.$row['hidden'].'</td><td colspan="3" class="small row'.$color.'" valign="top">'.zt('File: @file', array('@file'=>$row['file'])).'</td>'.
  393. '<td colspan="'.(sizeof($row['cols'])-4).'" class="small row'.$color.'" valign="top">'.zt('Write tag formats').': '.
  394. zina_content_tags_format($row['valid_tag_types'], $row['tags'], 'tag_formats['.$i.'][]', true).
  395. '</td></tr>';
  396. $color = ++$color % 2;
  397. }
  398. $output .= '</table>'.
  399. '<p style="text-align:center;"><input type="submit" name="write_tags" value="'.zt('Submit').'"/></p>';
  400. return ztheme('form', $form_attr, $output).$js2;
  401. }
  402. function zina_extras_tags_formats($format, $dataformat) {
  403. $types = array();
  404. if (in_array($format, array('mp3','mp2','mp1'))) {
  405. $types = array('id3v1', 'id3v2.3', 'ape');
  406. } elseif ($format == 'mpc') {
  407. $types = array('ape');
  408. } elseif ($format == 'ogg') {
  409. $types = ($dataformat == 'flac') ? array() : array('vorbiscomment');
  410. } elseif ($format == 'flac') {
  411. $types = array('metaflac');
  412. } elseif ($format == 'real') {
  413. $types = array('real');
  414. }
  415. return $types;
  416. }
  417. function zina_content_tags_format($ValidTagTypes, $tags, $name, $disable = false) {
  418. $output = '';
  419. $disabled = ($disable) ? ' disabled="disabled"' : '';
  420. $checked = ' checked="checked"';
  421. foreach ($ValidTagTypes as $type) {
  422. $output .= '<INPUT TYPE="CHECKBOX" NAME="'.$name.'" VALUE="'.$type.'"';
  423. if (count($ValidTagTypes) == 1) {
  424. $output .= $checked;
  425. } else {
  426. switch ($type) {
  427. case 'id3v1':
  428. $output .= $checked;
  429. break;
  430. case 'id3v2.3':
  431. $output .= $checked;
  432. break;
  433. default:
  434. if (isset($tags[$type])) $output .= $checked;
  435. break;
  436. }
  437. }
  438. $output .= $disabled.' />'.$type;
  439. }
  440. return $output;
  441. }
  442. /*
  443. * freedb.org
  444. * http://ftp.freedb.org/pub/freedb/latest/CDDBPROTO
  445. *
  446. */
  447. function zina_extras_tags_freedb_matches($url) {
  448. global $zc;
  449. if (!(bool)ini_get('allow_url_fopen')) {
  450. return zt("PHP setting 'allow_url_fopen' must be enabled for CDDB look-up to work.");
  451. }
  452. $context = stream_context_create(array(
  453. 'http' => array(
  454. 'timeout' => 5,
  455. ),
  456. ));
  457. $codes = _zina_freedb_codes();
  458. if (($results = @file_get_contents($url, false, $context)) !== false) {
  459. $items = explode("\n", $results);
  460. $item = explode(" ", array_shift($items));
  461. $response = (int)substr($results,0,3);
  462. if (!isset($codes[$response])) return zt('Freedb.org query: unknown response');
  463. if ($response == 200 || $response == 210 || $response == 211) {
  464. $output = '';
  465. $js =
  466. 'function zinaFreedb(cat, discid){'.
  467. 'var url = "'.zurl('','l=73&rand='.time()).'&cat="+cat+"&discid="+discid;'.
  468. 'var $j = jQuery;'.
  469. '$j("#zina_messages").html("'.zt('Loading...').'");'.
  470. '$j("#zina_messages").addClass("messages status");'.
  471. '$j.getJSON(url,function(data,status){'.
  472. 'if(status=="error"){'.
  473. '$j("#zina_messages").html(\'Error: \'+msg+status+data);}'.
  474. 'else{'.
  475. 'if (data.error) {'.
  476. '$j("#zina_messages").html(data.error);'.
  477. '$j("#zina_messages").addClass("messages warn");'.
  478. '}else{'.
  479. '$j("#zina_messages").html("'.zt('Tags Found').'");'.
  480. '$j("#zina_messages").addClass("messages status");'.
  481. 'var items = new Array("artist", "album", "genre", "year");'.
  482. 'for(i=0;i<items.length;i++){'.
  483. 'var col = items[i];'.
  484. 'var item = $j("input#zina-"+col);'.
  485. 'var orig = $j("div#zina-"+col+"-orig");'.
  486. 'if (item.val() != orig.html()) {'.
  487. 'item.val(orig.html());'.
  488. 'item.css("background-color","");'.
  489. '}'.
  490. 'if (data[col] != item.val()) {'.
  491. 'item.val(data[col]);'.
  492. 'item.css("background-color","#FFF380");'.
  493. '}'.
  494. '}'.
  495. 'var cols = new Array("track", "title", "artist", "album", "genre", "year", "comment");'.
  496. 'for(i=0;i<data.tracks.length;i++){'.
  497. 'for(j=0;j<cols.length;j++){'.
  498. 'var col = cols[j];'.
  499. 'var item = $j("input#zina-"+col+"-"+i);'.
  500. 'var orig = $j("div#zina-"+col+"-"+i+"-orig");'.
  501. 'if (item.val() != orig.html()) {'.
  502. 'item.val(orig.html());'.
  503. 'item.css("background-color","");'.
  504. '}'.
  505. 'if (data.tracks[i][col] && data.tracks[i][col] != item.val()) {'.
  506. 'item.val(data.tracks[i][col]);'.
  507. 'var color = (item.attr("disabled")) ? "#C9BE62" : "#FFF380";'.
  508. 'item.css("background-color",color);'.
  509. '}'.
  510. '}'.
  511. '}'.
  512. '}'.
  513. '}'.
  514. '});'.
  515. '}';
  516. $output .= '<script type="text/javascript">'.$js.'</script>';
  517. if ($response == 200) { #exact
  518. $cat = $item[1];
  519. $discid = $item[2];
  520. array_shift($item);
  521. array_shift($item);
  522. array_shift($item);
  523. $title = implode('', $item);
  524. $output .= '<h3>'.zt('Exact Match').'</h3><ul id="zina-found" class="zina-list">';
  525. $output .= '<li class="zina-list"><a href="javascript: void 0;" onclick="zinaFreedb(\''.$cat.'\',\''.$discid.'\');">'.$title.' ('.$discid.') ('.$cat.')</a></li>';
  526. $output .= '</ul>';
  527. } else {
  528. $output .= '<h3>'.zt('Matches').'</h3><ul id="zina-found" class="zina-list">';
  529. foreach($items as $i) {
  530. if (trim($i) == '.') break;
  531. $item = explode(" ", $i);
  532. $cat = trim($item[0]);
  533. $discid = trim($item[1]);
  534. $title = trim(substr($i, strlen($cat)+strlen($discid)+2));
  535. $output .= '<li class="zina-list"><a href="javascript: void 0;" onclick="zinaFreedb(\''.$cat.'\',\''.$discid.'\');">'.$title.' ('.$discid.') ('.$cat.')</a></li>';
  536. }
  537. $output .= '</ul>';
  538. }
  539. return $output;
  540. } elseif ($response == 202) {
  541. return zt('No matches found');
  542. } else {
  543. return zt('Freedb error: @code', array('@code'=>$codes[$response]));
  544. }
  545. }
  546. return zt('Cannot connect to: @server', array('@server'=>$zc['tags_cddb_server']));
  547. }
  548. function zina_extras_tags_freedb_match($cat, $discid) {
  549. global $zc;
  550. $context = stream_context_create(array(
  551. 'http' => array(
  552. 'timeout' => 5,
  553. ),
  554. ));
  555. $codes = _zina_freedb_codes();
  556. $url = "http://".$zc['tags_cddb_server']."/~cddb/cddb.cgi?cmd=cddb+read+$cat+$discid&hello=anonymous+unknown+zina+".ZINA_VERSION."&proto=6";
  557. if (($results = file_get_contents($url, false, $context)) !== false) {
  558. $response = (int)substr($results,0,3);
  559. if (!isset($codes[$response])) {
  560. $output = array('error'=>zt('Freedb.org entry: unknown response'));
  561. }
  562. if ($response == 210) {
  563. $output = _zina_freedb_parseRecord($results);
  564. } else {
  565. $output = array('error'=>zt('Freedb record error: @code', array('@code'=>$response)));
  566. }
  567. } else {
  568. $output = array('error'=>zt('Freedb: record error'));
  569. }
  570. return json_encode($output);
  571. }
  572. function _zina_freedb_codes() {
  573. return array(
  574. 200 => zt('Found exact match'),
  575. 202 => zt('No match found'),
  576. 210 => zt('OK, CDDB database entry follows (until terminating marker)'),
  577. 211 => zt('Found inexact matches, list follows (until terminating marker)'),
  578. 401 => zt('Specified CDDB entry not found'),
  579. 402 => zt('Server error'),
  580. 403 => zt('Database entry is corrupt'),
  581. 409 => zt('No handshake'),
  582. 500 => zt('Command syntax error: incorrect arg count forhandshake'),
  583. );
  584. }
  585. /*
  586. * Keith Palmer <Keith@UglySlug.com>
  587. *
  588. * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  589. */
  590. function _zina_freedb_parseRecord($str, $category = '') {
  591. $record = array(
  592. 'discid' => '',
  593. 'album' => '',
  594. 'year' => '',
  595. 'genre' => '',
  596. 'category' => trim($category), // Not ever parsed out of a disc record, just here because we need to store it
  597. 'extd' => '',
  598. 'playorder' => '',
  599. 'tracks' => array(),
  600. 'dlength' => 0,
  601. 'revision' => 0,
  602. 'submitted_via' => '',
  603. 'processed_by' => '',
  604. );
  605. // Some records seem to use \r and some use \n... convert all to one or the other
  606. $str = str_replace("\r", "\n", $str);
  607. $str = str_replace("\r\n", "\n", $str);
  608. $str = str_replace("\n\n", "\n", $str);
  609. $lines = explode("\n", $str);
  610. foreach ($lines as $line_num => $line) {
  611. if (count($explode = explode('=', $line)) == 2) { // key=value type line
  612. $key = trim(strtolower(current($explode)));
  613. $value = trim(end($explode));
  614. if (substr($key, 0, 6) == 'ttitle') {
  615. $track_num = (int) substr($key, 6);
  616. if (!isset($record['tracks'][$track_num])) {
  617. $record['tracks'][$track_num] = array(
  618. 'title' => '',
  619. 'extt' => '',
  620. 'offset' => '',
  621. 'length' => 0,
  622. );
  623. }
  624. $record['tracks'][$track_num]['title'] .= trim($value);
  625. } else if (substr($key, 0, 4) == 'extt') {
  626. $track_num = (int) substr($key, 6);
  627. if (!isset($record['tracks'][$track_num])) {
  628. $record['tracks'][$track_num] = array(
  629. 'title' => '',
  630. 'extt' => '',
  631. 'offset' => '',
  632. 'length' => 0,
  633. );
  634. }
  635. $record['tracks'][$track_num]['extt'] .= trim($value);
  636. } elseif (substr($key, 0, 6) == 'dtitle') {
  637. $record['album'] .= trim($value);
  638. } elseif (substr($key, 0, 7) == 'dartist') {
  639. $record['artist'] .= trim($value);
  640. } elseif (substr($key, 0, 5) == 'dyear') {
  641. $record['year'] .= (int)trim($value);
  642. } elseif (substr($key, 0, 6) == 'dgenre') {
  643. $record['genre'] .= trim($value);
  644. } else {
  645. $record[$key] .= trim($value);
  646. }
  647. } else { // Other data line
  648. if (false !== strpos($line, 'frame offsets')) {
  649. $track_num = 0;
  650. $line_num++;
  651. while ((int) trim(substr($lines[$line_num], 1))) {
  652. if (!isset($record['tracks'][$track_num])) {
  653. $record['tracks'][$track_num] = array(
  654. 'title' => '',
  655. 'extt' => '',
  656. 'offset' => '',
  657. 'length' => 0,
  658. );
  659. }
  660. $record['tracks'][$track_num]['offset'] = (int) trim(substr($lines[$line_num], 1));
  661. $track_num++;
  662. $line_num++;
  663. }
  664. } else if (false !== ($pos = strpos($line, 'Disc length:'))) {
  665. $record['dlength'] = (int) substr($line, $pos + 12);
  666. } else if (false !== ($pos = strpos($line, 'Revision:'))) {
  667. $record['revision'] = substr($line, $pos + 9);
  668. } else if (false !== ($pos = strpos($line, 'Submitted via:'))) {
  669. $record['submitted_via'] = substr($line, $pos + 14);
  670. } else if (false !== ($pos = strpos($line, 'Processed by:'))) {
  671. $record['processed_by'] = substr($line, $pos + 13);
  672. }
  673. }
  674. }
  675. // Now, lets seperate artists from titles
  676. if (count($explode = explode(' / ', $record['album'])) == 2) {
  677. $record['artist'] = current($explode);
  678. $record['album'] = end($explode);
  679. } else {
  680. $record['artist'] = $record['album'];
  681. }
  682. foreach ($record['tracks'] as $key => $track) {
  683. if (count($explode = explode(' / ', $track['title'])) == 2) {
  684. $record['tracks'][$key]['artist'] = current($explode);
  685. $record['tracks'][$key]['title'] = end($explode);
  686. } else {
  687. $record['tracks'][$key]['artist'] = $record['artist'];
  688. }
  689. $record['tracks'][$key]['album'] = $record['album'];
  690. $record['tracks'][$key]['genre'] = $record['genre'];
  691. $record['tracks'][$key]['year'] = $record['year'];
  692. $record['tracks'][$key]['track'] = $key+1;
  693. }
  694. // Calculate the lengths for each of the disc's tracks
  695. $count = count($record['tracks']);
  696. if ($count) {
  697. $start = $record['tracks'][0]['offset']; // Initial disc offset
  698. for ($i = 1; $i < $count; $i++) {
  699. $end = $record['tracks'][$i]['offset'];
  700. $record['tracks'][$i - 1]['length'] = round(($end - $start) / 75); // Set track offsets (seconds get rounded)
  701. $start = $record['tracks'][$i]['offset'];
  702. }
  703. // Set the final track length
  704. $record['tracks'][$count - 1]['length'] = $record['dlength'] - round($start / 75);
  705. }
  706. return $record;
  707. }
  708. function FixTextFields($text) {
  709. return htmlentities(utf8_decode(getid3_lib::SafeStripSlashes($text)), ENT_QUOTES);
  710. }
  711. ?>