Skip to content
This repository was archived by the owner on Mar 23, 2024. It is now read-only.

Commit cd9010d

Browse files
committed
✨ +last.fm album patchwork example
1 parent aa142eb commit cd9010d

File tree

3 files changed

+288
-0
lines changed

3 files changed

+288
-0
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
/**
3+
* @filesource lastfm-common.php
4+
* @created 03.08.2019
5+
* @author smiley <smiley@chillerlan.net>
6+
* @copyright 2019 smiley
7+
* @license MIT
8+
*/
9+
10+
namespace chillerlan\OAuthExamples\Providers\LastFM;
11+
12+
use chillerlan\OAuth\Core\AccessToken;
13+
use chillerlan\OAuth\Providers\LastFM;
14+
use function file_get_contents;
15+
16+
$ENVVAR = 'LASTFM';
17+
18+
/**
19+
* @var \Psr\Http\Client\ClientInterface $http
20+
* @var \chillerlan\Settings\SettingsContainerInterface $options
21+
* @var \chillerlan\OAuth\Storage\OAuthStorageInterface $storage
22+
* @var \Psr\Log\LoggerInterface $logger
23+
* @var string $CFGDIR
24+
*/
25+
26+
require_once __DIR__.'/../provider-api-example-common.php';
27+
28+
$lfm = new LastFM($http, $options, $logger);
29+
$lfm->setStorage($storage);
30+
31+
if(!$storage->hasAccessToken()){
32+
$token = (new AccessToken)->fromJSON(file_get_contents(($CFGDIR ?? '').'/LastFM.token.json'));
33+
$lfm->storeAccessToken($token);
34+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6+
<title>Last.fm top albums chart</title>
7+
<style>
8+
body{font-size: 20px; line-height: 1.4em; font-family: "Trebuchet MS", sans-serif; color: #000;}
9+
input, textarea, select{font-family: Consolas, "Liberation Mono", Courier, monospace; font-size: 75%; line-height: 1.25em; border: 1px solid #aaa;}
10+
input:focus, textarea:focus, select:focus{ border: 1px solid #ccc;}
11+
input[type=number]{width: 4em;}
12+
label{cursor: pointer;}
13+
#image-settings, div#image-output{ text-align: center; padding: 1em;}
14+
#footer{text-align: center;font-size: 75%;}
15+
</style>
16+
</head>
17+
<body>
18+
19+
<form id="image-settings">
20+
21+
<label for="username">last.fm username</label>
22+
<input id="username" name="username" type="text" autocomplete="off" spellcheck="false"/>
23+
24+
<label for="period">period</label>
25+
<select id="period" name="period">
26+
<option value="7day">7 day</option>
27+
<option value="1month" selected="selected">1 month</option>
28+
<option value="3month">3 month</option>
29+
<option value="6month">6 month</option>
30+
<option value="12month">12 month</option>
31+
<option value="overall">overall</option>
32+
</select>
33+
34+
<label for="width">width</label>
35+
<input id="width" name="width" type="number" min="1" max="10" value="5" placeholder="width"/>
36+
37+
<label for="height">height</label>
38+
<input id="height" name="height" type="number" min="1" max="10" value="5" placeholder="height"/>
39+
40+
<label for="imagesize">size</label>
41+
<input id="imagesize" name="imagesize" type="number" min="30" max="150" value="125" placeholder="imagesize"/>
42+
43+
<button type="submit">generate</button>
44+
</form>
45+
<div id="image-output"></div>
46+
<div id="footer">[ <a href="https://github.com/chillerlan/php-oauth/tree/master/examples/LastFM">github</a> ]</div>
47+
48+
<script src="https://cdnjs.cloudflare.com/ajax/libs/prototype/1.7.3/prototype.js"></script>
49+
<script>
50+
((form, output, url) => {
51+
$(form).observe('submit', ev => {
52+
Event.stop(ev);
53+
new Ajax.Request(url, {
54+
method : 'post',
55+
parameters : JSON.stringify(ev.target.serialize(true)),
56+
onUninitialized: $(output).update(),
57+
onLoading : $(output).update('<img src="https://media.giphy.com/media/3o6nV1reufhmjUXMJi/giphy.gif"/>'),
58+
onFailure : response => $(output).update(response.responseJSON.error),
59+
onSuccess : response => $(output).update(response.responseJSON.image),
60+
});
61+
});
62+
})('image-settings', 'image-output', './topalbum-patchwork.php');
63+
</script>
64+
65+
</body>
66+
</html>
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
<?php
2+
/**
3+
* topalbum-patchwork.php
4+
*
5+
* @link https://www.reddit.com/r/lastfm/search?q=flair_name%3A%22Chart%22&restrict_sr=1
6+
* @link https://github.com/Dinduks/Lastfm-Top-Albums
7+
*
8+
* @created 03.03.2019
9+
* @author smiley <smiley@chillerlan.net>
10+
* @copyright 2019 smiley
11+
* @license MIT
12+
*
13+
* @noinspection PhpComposerExtensionStubsInspection
14+
*/
15+
16+
namespace chillerlan\OAuthExamples\Providers\LastFM;
17+
18+
use chillerlan\HTTP\Utils\MessageUtil;
19+
use Exception;
20+
use function array_column;
21+
use function array_shift;
22+
use function count;
23+
use function dirname;
24+
use function file_exists;
25+
use function file_get_contents;
26+
use function file_put_contents;
27+
use function header;
28+
use function imagecolorallocate;
29+
use function imagecopyresampled;
30+
use function imagecreatefromgif;
31+
use function imagecreatefromjpeg;
32+
use function imagecreatefrompng;
33+
use function imagecreatetruecolor;
34+
use function imagedestroy;
35+
use function imagefill;
36+
use function imagejpeg;
37+
use function imagesx;
38+
use function imagesy;
39+
use function intval;
40+
use function json_decode;
41+
use function json_encode;
42+
use function max;
43+
use function min;
44+
use function mkdir;
45+
use function parse_url;
46+
use function sha1;
47+
use function strlen;
48+
use function substr;
49+
use function trim;
50+
use const JSON_PRETTY_PRINT;
51+
use const PHP_URL_PATH;
52+
53+
$ENVVAR = 'LASTFM';
54+
55+
/**
56+
* @var \Psr\Log\LoggerInterface $logger
57+
* @var \chillerlan\OAuth\Providers\LastFM $lfm
58+
*/
59+
60+
require_once __DIR__.'/lastfm-common.php';
61+
62+
$urlcache = './urlcache';
63+
$imgcache = './cache'; // public access
64+
65+
66+
try{
67+
$request = json_decode(file_get_contents('php://input'));
68+
69+
if(!$request || !isset($request->username)){
70+
header('HTTP/1.1 400 Bad Request');
71+
sendResponse(['error' => 'invalid request']);
72+
}
73+
74+
$user = trim($request->username);
75+
$rows = max(0, min(intval($request->height), 10));
76+
$cols = max(0, min(intval($request->width), 10));
77+
$imageSize = max(30, min(intval($request->imagesize), 150));
78+
$period = trim($request->period);
79+
$limit = ($rows * $cols + 10);
80+
81+
// doesn't necessarily need session auth, api key alone is sufficient
82+
$response = $lfm->request('user.getTopAlbums', ['user' => $user, 'period' => $period, 'limit' => $limit]);
83+
84+
if($response->getStatusCode() !== 200){
85+
header('HTTP/1.1 '.$response->getStatusCode().' '.$response->getReasonPhrase());
86+
sendResponse(['error' => 'last.fm error']);
87+
}
88+
89+
$json = MessageUtil::decodeJSON($response);
90+
91+
if(!$json || !isset($json->topalbums->album)){
92+
header('HTTP/1.1 500 Internal Server Error');
93+
sendResponse(['error' => '...']);
94+
}
95+
96+
// a not-too-unique hash
97+
$hash = sha1(json_encode([$rows, $cols, $imageSize,
98+
array_column($json->topalbums->album, 'artist'),
99+
array_column($json->topalbums->album, 'name'),
100+
array_column($json->topalbums->album, 'mbid'),
101+
]));
102+
103+
$imagefile = $imgcache.'/'.$hash.'.jpg';
104+
105+
if(file_exists($imagefile)){
106+
header('HTTP/1.1 200 OK');
107+
sendResponse(['image' => '<img src="'.$imagefile.'"/>', 'cached' => true]);
108+
}
109+
110+
$res = [];
111+
112+
foreach(array_column($json->topalbums->album, 'image') as $img){
113+
114+
if(empty($img)){
115+
continue;
116+
}
117+
118+
$path = getImage($img[(count($img) - 1)]->{'#text'}, $urlcache);
119+
$ext = substr($path, (strlen($path) - 3));
120+
$res[] = match($ext){
121+
'jpg' => imagecreatefromjpeg($path),
122+
'png' => imagecreatefrompng($path),
123+
'gif' => imagecreatefromgif($path),
124+
};
125+
}
126+
127+
$patchwork = imagecreatetruecolor(($cols * $imageSize), ($rows * $imageSize));
128+
$bg = imagecolorallocate($patchwork, 0, 0, 0);
129+
imagefill($patchwork, 0, 0, $bg);
130+
131+
for($y = 0; $y < $rows; $y++){
132+
for($x = 0; $x < $cols; $x++){
133+
134+
if(empty($res)){
135+
break;
136+
}
137+
138+
$img = array_shift($res);
139+
imagecopyresampled($patchwork, $img, ($x * $imageSize), ($y * $imageSize), 0, 0, $imageSize, $imageSize, imagesx($img), imagesy($img));
140+
imagedestroy($img);
141+
}
142+
}
143+
144+
// save the image into a file
145+
imagejpeg($patchwork, $imagefile, 85);
146+
imagedestroy($patchwork);
147+
148+
if(file_exists($imagefile)){
149+
header('HTTP/1.1 200 OK');
150+
sendResponse(['image' => '<img src="'.$imagefile.'"/>', 'cached' => false]);
151+
}
152+
153+
}
154+
// Pokémon exception handler
155+
catch(Exception $e){
156+
header('HTTP/1.1 500 Internal Server Error');
157+
sendResponse(['error' => $e->getMessage()]);
158+
}
159+
160+
exit;
161+
162+
function getImage(string $url, string $urlcache):string{
163+
164+
$path = parse_url($url, PHP_URL_PATH);
165+
166+
if(file_exists($urlcache.$path)){
167+
return $urlcache.$path;
168+
}
169+
170+
$dir = $urlcache.dirname($path);
171+
$imagedata = file_get_contents($url);
172+
173+
if(!file_exists($dir)){
174+
mkdir($dir, 0777, true);
175+
}
176+
177+
file_put_contents($urlcache.$path, $imagedata);
178+
179+
return $urlcache.$path;
180+
}
181+
182+
function sendResponse(array $response):void{
183+
header('Content-type: application/json;charset=utf-8;');
184+
185+
echo json_encode($response, JSON_PRETTY_PRINT);
186+
exit;
187+
}
188+

0 commit comments

Comments
 (0)