'application/json', FNP_FMT_XML => 'application/xml', ]); if(!empty($_SERVER['HTTP_ORIGIN']) && is_file(FNP_ORIGINS)) { $origin = parse_url($_SERVER['HTTP_ORIGIN'], PHP_URL_HOST); $allowedOrigins = file(FNP_ORIGINS, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); if(in_array($origin, $allowedOrigins)) { header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']); header('Vary: Origin'); } } if(!isset($format)) $format = (string)(filter_input(INPUT_GET, 'f') ?? 'json'); $pretty = !empty($_GET['p']); if($format !== 'json' && $format !== 'xml') { http_response_code(400); return; } $charset = preg_match('#MSIE#i', $_SERVER['HTTP_USER_AGENT'] ?? '') ? '' : 'utf-8'; header('Content-Type: ' . FNP_FMTS[$format] . (empty($charset) ? '' : ('; charset=' . $charset))); function errorResponse(string $text, int $code = -1): void { global $format, $charset; http_response_code(500); switch($format) { case FNP_FMT_JSON: echo json_encode(['error' => $text, 'code' => $code]); break; // Has strange formatting, but XML mode exists for backwards compatibility with an unreleased variation of get.php. case FNP_FMT_XML: $document = new DOMDocument('1.0', $charset); $tracks = $document->appendChild($document->createElement('Tracks')); $track = $tracks->appendChild($document->createElement('Track')); $track->setAttribute('error', $text); $track->setAttribute('code', $code); echo $document->saveXML(); break; } exit; } if(!is_file(FNP_API_KEY)) errorResponse('API key file missing! Create a file called ".apikey" in the root directory and place your last.fm api key in there.'); $apiKey = trim(file_get_contents(FNP_API_KEY)); $userName = (string)filter_input(INPUT_GET, 'u', FILTER_SANITIZE_STRING); $curl = curl_init(FNP_API_URL . '/2.0/?method=user.getrecenttracks&format=json&limit=1&api_key=' . $apiKey . '&user=' . rawurlencode($userName)); curl_setopt_array($curl, [ CURLOPT_AUTOREFERER => false, CURLOPT_COOKIESESSION => false, CURLOPT_VERBOSE => false, CURLOPT_FAILONERROR => false, CURLOPT_FOLLOWLOCATION => false, CURLOPT_TCP_NODELAY => true, CURLOPT_HEADER => false, CURLOPT_RETURNTRANSFER => true, CURLOPT_TCP_FASTOPEN => true, CURLOPT_VERBOSE => false, CURLOPT_CONNECTTIMEOUT => 2, CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS, CURLOPT_TIMEOUT => 2, CURLOPT_USERAGENT => 'NowListening v2 (' . $_SERVER['HTTP_HOST'] . ')', ]); $response = json_decode(curl_exec($curl)); curl_close($curl); if($response === null) errorResponse('API request failed.'); if(isset($response->message)) errorResponse($response->message, $response->error ?? -1); switch($format) { case FNP_FMT_JSON: $tracks = []; if(!empty($response->recenttracks?->track)) foreach($response->recenttracks->track as $recentTrack) { $tracks[] = $track = new stdClass; $track->name = $recentTrack->name; if(!empty($recentTrack->{"@attr"}?->nowplaying)) $track->nowplaying = true; $track->mbid = $recentTrack->mbid; $track->url = $recentTrack->url; $track->date = $recentTrack->date?->uts ?? ''; $track->streamable = $recentTrack->streamable; $track->artist = new stdClass; $track->artist->name = $recentTrack->artist?->{"#text"} ?? ''; $track->artist->mbid = $recentTrack->artist?->mbid ?? ''; $track->artist->url = explode('/_/', $track->url, 2)[0]; $track->album = new stdClass; $track->album->name = $recentTrack->album?->{"#text"}; $track->album->mbid = $recentTrack->album?->mbid ?? ''; $track->images = new stdClass; foreach($recentTrack->image as $trackImage) $track->images->{$trackImage->size} = $trackImage->{"#text"}; } echo json_encode($tracks, $pretty ? JSON_PRETTY_PRINT : 0); break; case FNP_FMT_XML: $document = new DOMDocument('1.0', $charset); $document->formatOutput = $pretty; $tracks = $document->appendChild($document->createElement('Tracks')); if(!empty($response->recenttracks?->track)) foreach($response->recenttracks->track as $recentTrack) { $elem = $tracks->appendChild($document->createElement('Track')); $elem->appendChild($document->createElement('Name', htmlspecialchars($recentTrack->name))); $elem->appendChild($document->createElement('Mbid', $recentTrack->mbid)); $elem->appendChild($document->createElement('Url', htmlspecialchars($recentTrack->url))); $elem->appendChild($document->createElement('Date', empty($recentTrack->date?->uts) ? '' : date('c', $recentTrack->date->uts))); $elem->appendChild($document->createElement('Streamable', empty($recentTrack->streamable) ? '0' : '1')); $elem->appendChild($document->createElement('IsPlaying', empty($recentTrack->{"@attr"}?->nowplaying) ? '0' : '1')); $artist = $elem->appendChild($document->createElement('Artist')); $artist->appendChild($document->createElement('Name', htmlspecialchars($recentTrack->artist?->{"#text"} ?? ''))); $artist->appendChild($document->createElement('Mbid', $recentTrack->artist?->mbid ?? '')); $album = $elem->appendChild($document->createElement('Album')); $album->appendChild($document->createElement('Name', htmlspecialchars($recentTrack->album?->{"#text"} ?? ''))); $album->appendChild($document->createElement('Mbid', $recentTrack->album?->mbid ?? '')); $images = $elem->appendChild($document->createElement('Images')); foreach($recentTrack->image as $trackImage) { $name = $trackImage->size; if($name === 'extralarge') $name = 'ExtraLarge'; else $name = ucfirst($name); $images->appendChild($document->createElement($name, htmlspecialchars($trackImage->{"#text"}))); } } echo $document->saveXML(); break; }