1<?php 2/** 3 * lastfm functions for the lastfm plugin 4 * 5 * @license: GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author: Michael Klier <chi@chimeric.de> 7 */ 8 9if(!defined('DW_LF')) define('DW_LF',"\n"); 10require_once(DOKU_INC.'inc/html.php'); 11 12/** 13 * outputs the requested lastfm chart 14 * 15 * @author Michael Klier <chi@chimeric.de> 16 */ 17function lastfm_xhtml($user,$chart,$limit,$dformat,$utc_offset,$cols,$imgonly) { 18 global $lang; 19 20 $data = array(); 21 $xml = lastfm_get_xml($user,$chart); 22 $data = ($chart != 'profile') ? @array_pop(lastfm_xml2array($xml)) : lastfm_xml2array($xml); 23 24 // do we have any data? 25 if(!is_array($data)) { 26 print $lang['nothingfound']; 27 return; 28 }; 29 30 // apply limit 31 if($chart != 'profile') $data = array_slice($data,0,$limit); 32 33 print '<table class="plugin_lastfm_chart plugin_lastfm_' . $chart . '">' . DW_LF; 34 35 switch($chart) { 36 37 case 'topartists': 38 foreach($data as $rcd) { 39 print '<tr>' . DW_LF; 40 print ' <td class="plugin_lastfm_rank">' . $rcd['rank'] . '.</td>' . DW_LF; 41 print ' <td class="plugin_lastfm_artist">' . DW_LF; 42 print ' <a href="' . $rcd['url'] . '" title="' . $rcd['name'] . '">' . $rcd['name'] . '</a>' . DW_LF; 43 print ' </td>' . DW_LF; 44 print ' <td>' . DW_LF; 45 print ' <div class="plugin_lastfm_playcount"style="width:'; 46 print round($rcd['playcount'] / 2); 47 print 'px;">' . $rcd['playcount'] . '</div>' . DW_LF; 48 print ' </td>' . DW_LF; 49 print '</tr>' . DW_LF; 50 } 51 break; 52 53 case 'topalbums': 54 if(!$imgonly) { 55 foreach($data as $rcd) { 56 print '<tr>' . DW_LF; 57 print ' <td class="plugin_lastfm_rank">' . $rcd['rank'] . '.</td>' . DW_LF; 58 print ' <td>' . DW_LF; 59 print ' <a href="' . $rcd['url'] . '" title="' . $rcd['artist'] . ' - ' . $rcd['name'] . '">' . DW_LF; 60 print ' <img src="' . $rcd['image']['small'] . '" height="45px" width="45px" alt="' . $rcd['name'] . '" />' . DW_LF; 61 print ' </a>' . DW_LF; 62 print ' </td>' . DW_LF; 63 print ' <td class="plugin_lastfm_artist"><a href="' . $rcd['url'] .'" title="' . $rcd['artist'] . '">' . $rcd['artist'] . ' [' . $rcd['name'] . ']</a></td>' . DW_LF; 64 print ' <td class="plugin_lastfm_count">' . $rcd['playcount'] . '</td>' . DW_LF; 65 print '</tr>' . DW_LF; 66 } 67 } else { 68 $num = count($data); 69 $col = 1; 70 71 for($i=0;$i<$num;$i++) { 72 if($col == 1) print '<tr>' . DW_LF; 73 74 print ' <td>' . DW_LF; 75 print ' <a href="' . $data[$i]['url'] . '" title="' . $data[$i]['artist'] . ' - ' . $data[$i]['name'] . '">' . DW_LF; 76 print ' <img src="' . $data[$i]['image']['small'] . '" height="45px" width="45px" alt="' . $data[$i]['name'] . '" />' . DW_LF; 77 print ' </a>' . DW_LF; 78 print ' </td>' . DW_LF; 79 80 if($col == $cols) { 81 print '</tr>' . DW_LF; 82 $col = 1; 83 } else { 84 $col++; 85 } 86 } 87 } 88 break; 89 90 case 'toptracks': 91 foreach($data as $rcd) { 92 print '<tr>' . DW_LF; 93 print ' <td class="plugin_lastfm_rank">' . $rcd['rank'] . '.</td>' . DW_LF; 94 print ' <td class="plugin_lastfm_track"><a href="' . $rcd['url'] . '" title="' . $rcd['artist'] . '">' . $rcd['name'] . '</a></td>' . DW_LF; 95 print ' <td class="plugin_lastfm_count">' . $rcd['playcount'] . '</td>' . DW_LF; 96 print '</tr>' . DW_LF; 97 } 98 break; 99 100 case 'tags': 101 foreach($data as $rcd) { 102 print '<tr>' . DW_LF; 103 print ' <td class="plugin_lastfm_count">' . $rcd['count'] . '</td>' . DW_LF; 104 print ' <td class="plugin_lastfm_tag"><a href="' . $rcd['url'] . '" title="' . $rcd['name'] . '">' . $rcd['name'] . '</a></td>' . DW_LF; 105 print '</tr>' . DW_LF; 106 } 107 break; 108 109 case 'friends': 110 $num = count($data); 111 $col = 1; 112 113 for($i=0;$i<$num;$i++) { 114 if($col == 1) print '<tr>' . DW_LF; 115 116 print ' <td class="plugin_lastfm_friend">' . DW_LF; 117 print ' <a href="' . $data[$i]['url'] . '" title="' . $data[$i]['attributes']['username'] . '">' . DW_LF; 118 print ' <img src="' . $data[$i]['image'] . '" alt="' . $data[$i]['attributes']['username'] . '" width="45px" height="45px" />' . DW_LF; 119 print ' </a>' . DW_LF; 120 print ' </td>' . DW_LF; 121 122 if($col == $cols) { 123 print '</tr>' . DW_LF; 124 $col = 1; 125 } else { 126 $col++; 127 } 128 } 129 break; 130 131 case 'neighbours': 132 $num = count($data); 133 $col = 1; 134 135 for($i=0;$i<$num;$i++) { 136 if($col == 1) print '<tr>' . DW_LF; 137 138 print ' <td class="plugin_lastfm_friend">' . DW_LF; 139 print ' <a href="' . $data[$i]['url'] . '" title="' . $data[$i]['attributes']['username'] . '">' . DW_LF; 140 print ' <img src="' . $data[$i]['image'] . '" alt="' . $data[$i]['attributes']['username'] . ' ' . $data[$i]['match'] . '%" width="45px" height="45px" />' . DW_LF; 141 print ' </a>' . DW_LF; 142 print ' </td>' . DW_LF; 143 144 if($col == $cols) { 145 print '</tr>' . DW_LF; 146 $col = 1; 147 } else { 148 $col++; 149 } 150 } 151 break; 152 153 case 'recenttracks': 154 foreach($data as $rcd) { 155 print '<tr>' . DW_LF; 156 print ' <td class="plugin_lastfm_artist">' . DW_LF; 157 print ' <a href="' . $rcd['url'] . '" title="' . $rcd['artist'] . ' · ' . $rcd['name'] . '">' . $rcd['artist'] . ' · ' . $rcd['name'] . '</a>' . DW_LF; 158 print ' </td>' . DW_LF; 159 print ' <td class="plugin_lastfm_date">' . lastfm_cvdate($rcd['date'],$dformat,$utc_offset) . '</td>' . DW_LF; 160 print '</tr>' . DW_LF; 161 } 162 break; 163 164 case 'weeklyartistchart': 165 foreach($data as $rcd) { 166 print '<tr>' . DW_LF; 167 print ' <td class="plugin_lastfm_rank">' . $rcd['chartposition'] . '.</td>' . DW_LF; 168 print ' <td class="plugin_lastfm_artist">' . DW_LF; 169 print ' <a href="' . $rcd['url'] . '" title="' . $rcd['name'] . '">' . $rcd['name'] . '</a>' . DW_LF; 170 print ' </td>' . DW_LF; 171 print ' <td class="plugin_lastfm_playcount">' . $rcd['playcount'] . '</td>' . DW_LF; 172 print '</tr>' . DW_LF; 173 } 174 break; 175 176 case 'weeklyalbumchart': 177 foreach($data as $rcd) { 178 // FIXME mbid field 179 print '<tr>' . DW_LF; 180 print ' <td class="plugin_lastfm_rank">' . $rcd['chartposition'] . '.</td>' . DW_LF; 181 print ' <td class="plugin_lastfm_artist">' . DW_LF; 182 print ' <a href="' . $rcd['url'] . '" title="' . $rcd['artist'] . ' · ' . $rcd['name'] . '">' . $rcd['artist'] . ' · ' . $rcd['name'] . '</a>' . DW_LF; 183 print ' </td>' . DW_LF; 184 print ' <td class="plugin_lastfm_playcount">' . $rcd['playcount'] . '</td>' . DW_LF; 185 print '</tr>' . DW_LF; 186 } 187 break; 188 189 case 'weeklytrackchart': 190 foreach($data as $rcd) { 191 print '<tr>' . DW_LF; 192 print ' <td class="plugin_lastfm_rank">' . $rcd['chartposition'] . '.</td>' . DW_LF; 193 print ' <td class="plugin_lastfm_artist">' . DW_LF; 194 print ' <a href="' . $rcd['url'] . '" title="' . $rcd['artist'] . ' · ' . $rcd['name'] . '">' . $rcd['artist'] . ' · ' . $rcd['name'] . '</a>' . DW_LF; 195 print ' </td>' . DW_LF; 196 print ' <td class="plugin_lastfm_playcount">' . $rcd['playcount'] . '</td>' . DW_LF; 197 print '</tr>' . DW_LF; 198 } 199 break; 200 201 case 'profile': 202 print '<a href="' . $data['url'] . '" title="' . $user . '">' . DW_LF; 203 print ' <img src="' . $data['avatar'] . '" alt="' . $user . '" />' . DW_LF; 204 print '</a>' . DW_LF; 205 break; 206 } 207 208 print '</table>' . DW_LF; 209} 210 211/** 212 * gets the xml file from the lastfm page 213 * 214 * @author Michael Klier <chi@chimeric.de> 215 */ 216function lastfm_get_xml($user,$chart) { 217 $xml = ''; 218 $http = new DokuHTTPClient(); 219 $url = 'http://ws.audioscrobbler.com/1.0/user/'; 220 $url .= $user . '/' . $chart . '.xml'; 221 222 $xml = $http->get($url); 223 if($http->status == 200) 224 return $xml; 225} 226 227/** 228 * converst the date provided by the lastfm service 229 * 230 * @author Michael Klier <chi@chimeric.de> 231 */ 232function lastfm_cvdate($date,$dformat,$utc_offset) { 233 list($day,$month,$year,$time) = explode(' ',$date); 234 list($hour,$min) = explode(':',$time); 235 $year = substr($year,0,-1); // remove trailing comma 236 $hour = $hour + $utc_offset; 237 return date($dformat,strtotime($day." ".$month." ".$year. " ".$hour.":".$min)); 238} 239 240/** 241 * wrapper function around _xmlToArray() 242 */ 243function lastfm_xml2array($xml) { 244 return _xmlToArray($xml); 245} 246 247/** 248 * This static method converts an xml file to an associative array 249 * duplicating the xml file structure. 250 * 251 * @param $fileName. String. The name of the xml file to convert. 252 * This method returns an Error object if this file does not 253 * exist or is invalid. 254 * @param $includeTopTag. booleal. Whether or not the topmost xml tag 255 * should be included in the array. The default value for this is false. 256 * @param $lowerCaseTags. boolean. Whether or not tags should be 257 * set to lower case. Default value for this parameter is true. 258 * @access public static 259 * @return Associative Array 260 * @author Jason Read <jason@ace.us.com> 261 * @author Michael Klier <chi@chimeric.de> 262 */ 263function & _xmlToArray($xml_raw, $includeTopTag = false, $lowerCaseTags = true) 264{ 265 $p = xml_parser_create(); 266 xml_parse_into_struct($p,$xml_raw,$vals,$index); 267 xml_parser_free($p); 268 $xml = array(); 269 $levels = array(); 270 $multipleData = array(); 271 $prevTag = ""; 272 $currTag = ""; 273 $topTag = false; 274 foreach ($vals as $val) 275 { 276 // Open tag 277 if ($val["type"] == "open") 278 { 279 if (!_xmlFileToArrayOpen($topTag, $includeTopTag, $val, $lowerCaseTags, 280 $levels, $prevTag, $multipleData, $xml)) 281 { 282 continue; 283 } 284 } 285 // Close tag 286 else if ($val["type"] == "close") 287 { 288 if (!_xmlFileToArrayClose($topTag, $includeTopTag, $val, $lowerCaseTags, 289 $levels, $prevTag, $multipleData, $xml)) 290 { 291 continue; 292 } 293 } 294 // Data tag 295 else if ($val["type"] == "complete" && isset($val["value"])) 296 { 297 $loc =& $xml; 298 foreach ($levels as $level) 299 { 300 $temp =& $loc[str_replace(":arr#", "", $level)]; 301 $loc =& $temp; 302 } 303 $tag = $val["tag"]; 304 if ($lowerCaseTags) 305 { 306 $tag = strtolower($val["tag"]); 307 } 308 $loc[$tag] = str_replace("\\n", "\n", $val["value"]); 309 } 310 // Tag without data 311 else if ($val["type"] == "complete") 312 { 313 _xmlFileToArrayOpen($topTag, $includeTopTag, $val, $lowerCaseTags, 314 $levels, $prevTag, $multipleData, $xml); 315 _xmlFileToArrayClose($topTag, $includeTopTag, $val, $lowerCaseTags, 316 $levels, $prevTag, $multipleData, $xml); 317 } 318 } 319 return $xml; 320} 321 322/** 323 * Private support function for xmlFileToArray. Handles an xml OPEN tag. 324 * 325 * @param $topTag. String. xmlFileToArray topTag variable 326 * @param $includeTopTag. boolean. xmlFileToArray includeTopTag variable 327 * @param $val. String[]. xmlFileToArray val variable 328 * @param $currTag. String. xmlFileToArray currTag variable 329 * @param $lowerCaseTags. boolean. xmlFileToArray lowerCaseTags variable 330 * @param $levels. String[]. xmlFileToArray levels variable 331 * @param $prevTag. String. xmlFileToArray prevTag variable 332 * @param $multipleData. boolean. xmlFileToArray multipleData variable 333 * @param $xml. String[]. xmlFileToArray xml variable 334 * @access private static 335 * @return boolean 336 * @author Jason Read <jason@ace.us.com> 337 */ 338function _xmlFileToArrayOpen(& $topTag, & $includeTopTag, & $val, & $lowerCaseTags, 339 & $levels, & $prevTag, & $multipleData, & $xml) 340{ 341 // don't include top tag 342 if (!$topTag && !$includeTopTag) 343 { 344 $topTag = $val["tag"]; 345 return false; 346 } 347 $currTag = $val["tag"]; 348 if ($lowerCaseTags) 349 { 350 $currTag = strtolower($val["tag"]); 351 } 352 $levels[] = $currTag; 353 354 // Multiple items w/ same name. Convert to array. 355 if ($prevTag === $currTag) 356 { 357 if (!array_key_exists($currTag, $multipleData) || 358 !$multipleData[$currTag]["multiple"]) 359 { 360 $loc =& $xml; 361 foreach ($levels as $level) 362 { 363 $temp =& $loc[$level]; 364 $loc =& $temp; 365 } 366 $loc = array($loc); 367 $multipleData[$currTag]["multiple"] = true; 368 $multipleData[$currTag]["multiple_count"] = 0; 369 } 370 $multipleData[$currTag]["popped"] = false; 371 $levels[] = ":arr#" . ++$multipleData[$currTag]["multiple_count"]; 372 } 373 else 374 { 375 $multipleData[$currTag]["multiple"] = false; 376 } 377 378 // Add attributes array 379 if (array_key_exists("attributes", $val)) 380 { 381 $loc =& $xml; 382 foreach ($levels as $level) 383 { 384 $temp =& $loc[str_replace(":arr#", "", $level)]; 385 $loc =& $temp; 386 } 387 $keys = array_keys($val["attributes"]); 388 foreach ($keys as $key) 389 { 390 $tag = $key; 391 if ($lowerCaseTags) 392 { 393 $tag = strtolower($tag); 394 } 395 $loc["attributes"][$tag] = & $val["attributes"][$key]; 396 } 397 } 398 return true; 399} 400 401/** 402 * Private support function for xmlFileToArray. Handles an xml OPEN tag. 403 * 404 * @param $topTag. String. xmlFileToArray topTag variable 405 * @param $includeTopTag. boolean. xmlFileToArray includeTopTag variable 406 * @param $val. String[]. xmlFileToArray val variable 407 * @param $currTag. String. xmlFileToArray currTag variable 408 * @param $lowerCaseTags. boolean. xmlFileToArray lowerCaseTags variable 409 * @param $levels. String[]. xmlFileToArray levels variable 410 * @param $prevTag. String. xmlFileToArray prevTag variable 411 * @param $multipleData. boolean. xmlFileToArray multipleData variable 412 * @param $xml. String[]. xmlFileToArray xml variable 413 * @access private static 414 * @return boolean 415 * @author Jason Read <jason@ace.us.com> 416 */ 417function _xmlFileToArrayClose(& $topTag, & $includeTopTag, & $val, & $lowerCaseTags, 418 & $levels, & $prevTag, & $multipleData, & $xml) 419{ 420 // don't include top tag 421 if ($topTag && !$includeTopTag && $val["tag"] == $topTag) 422 { 423 return false; 424 } 425 if ($multipleData[$currTag]["multiple"]) 426 { 427 $tkeys = array_reverse(array_keys($multipleData)); 428 foreach ($tkeys as $tkey) 429 { 430 if ($multipleData[$tkey]["multiple"] && !$multipleData[$tkey]["popped"]) 431 { 432 array_pop($levels); 433 $multipleData[$tkey]["popped"] = true; 434 break; 435 } 436 else if (!$multipleData[$tkey]["multiple"]) 437 { 438 break; 439 } 440 } 441 } 442 $prevTag = array_pop($levels); 443 if (strpos($prevTag, "arr#")) 444 { 445 $prevTag = array_pop($levels); 446 } 447 return true; 448} 449 450// vim:ts=4:sw=4:et:enc=utf8: 451