2016-06-12 18:52:15 +00:00
|
|
|
<?php
|
2021-05-20 15:38:25 +00:00
|
|
|
define('FNP_API_KEY', __DIR__ . '/../.apikey');
|
|
|
|
define('FNP_API_URL', 'https://ws.audioscrobbler.com');
|
2021-05-20 19:37:29 +00:00
|
|
|
define('FNP_ORIGINS', __DIR__ . '/../.cors');
|
2016-06-12 18:52:15 +00:00
|
|
|
|
2021-05-20 15:38:25 +00:00
|
|
|
define('FNP_FMT_JSON', 'json');
|
|
|
|
define('FNP_FMT_XML', 'xml');
|
|
|
|
define('FNP_FMTS', [
|
|
|
|
FNP_FMT_JSON => 'application/json',
|
|
|
|
FNP_FMT_XML => 'application/xml',
|
|
|
|
]);
|
|
|
|
|
2021-05-20 19:37:29 +00:00
|
|
|
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');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-20 15:38:25 +00:00
|
|
|
if(!isset($format))
|
|
|
|
$format = (string)(filter_input(INPUT_GET, 'f') ?? 'json');
|
|
|
|
$pretty = !empty($_GET['p']);
|
2016-06-12 18:52:15 +00:00
|
|
|
|
2021-05-20 15:38:25 +00:00
|
|
|
if($format !== 'json' && $format !== 'xml') {
|
|
|
|
http_response_code(400);
|
|
|
|
return;
|
2016-06-12 18:52:15 +00:00
|
|
|
}
|
|
|
|
|
2021-05-20 15:38:25 +00:00
|
|
|
$charset = preg_match('#MSIE#i', $_SERVER['HTTP_USER_AGENT'] ?? '') ? '' : 'utf-8';
|
2016-06-12 18:52:15 +00:00
|
|
|
|
2021-05-20 15:38:25 +00:00
|
|
|
header('Content-Type: ' . FNP_FMTS[$format] . (empty($charset) ? '' : ('; charset=' . $charset)));
|
2016-06-12 18:52:15 +00:00
|
|
|
|
2021-05-20 15:38:25 +00:00
|
|
|
function errorResponse(string $text, int $code = -1): void {
|
|
|
|
global $format, $charset;
|
2016-06-12 18:52:15 +00:00
|
|
|
|
2021-05-20 15:38:25 +00:00
|
|
|
http_response_code(500);
|
2016-06-12 18:52:15 +00:00
|
|
|
|
2021-05-20 15:38:25 +00:00
|
|
|
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;
|
2016-06-12 18:52:15 +00:00
|
|
|
}
|
|
|
|
|
2021-05-20 15:38:25 +00:00
|
|
|
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 ?? '';
|
2021-05-20 20:50:41 +00:00
|
|
|
$track->artist->url = explode('/_/', $track->url, 2)[0];
|
2021-05-20 15:38:25 +00:00
|
|
|
|
|
|
|
$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;
|
|
|
|
}
|