Skip to content

Commit

Permalink
init stage 2
Browse files Browse the repository at this point in the history
  • Loading branch information
jee-r committed Nov 16, 2023
1 parent 70858f2 commit e195edf
Show file tree
Hide file tree
Showing 6 changed files with 510 additions and 0 deletions.
137 changes: 137 additions & 0 deletions functions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<?php

// Debug purpose print $data to browser console
function console_log($data) {
$output = $data;
if (is_array($output))
$output = implode(',', $output);

echo "<script>console.log('Debug Objects: " . $output . "' );</script>";
}

// check if LastFM user exist
function checkUserExist($lastfmUser, $apiKey)
{
$query = "https://ws.audioscrobbler.com/2.0/?method=user.getinfo&user=$lastfmUser&api_key=$apiKey&format=json";
try {
$lastfmUserInfo = file_get_contents($query);

// Check for errors
if ($lastfmUserInfo === false) {
throw new Exception("Unable to fetch Last.fm user info.");
}

$lastfmUserInfoJson = json_decode($lastfmUserInfo, true);

if (isset($lastfmUserInfoJson['user']['name'])) {
return true;
}

} catch (Exception $e) {
// Handle the exception (log, display an error message, etc.)
// For demonstration purposes, we'll return a string with the error message
return $e->getMessage();
// return false;
}
}

// Fatch Top Albums
function fetchtopalbums($lastfmuser, $apikey, $period, $limit) {

// create the url
// https://www.last.fm/api/show/user.gettopalbums
$method = "user.gettopalbums";
$apiurl = "https://ws.audioscrobbler.com/2.0/";

$query = "$apiurl?method=$method&user=$lastfmuser&period=$period&limit=$limit&api_key=$apikey&format=json";

$lastfmdata = file_get_contents($query);
$lastfmdatajson = json_decode($lastfmdata, true);
$topalbums = $lastfmdatajson['topalbums']['album'];

return $topalbums;
}

// create albums cover array
function createAlbumsCoverArray($topAlbums)
{
$albumsCoverUrlList = array();
foreach ($topAlbums as $topAlbum) {
// check if extralarge image exist
$extralarge_image_link = isset($topAlbum['image'][3]['#text']) && !empty($topAlbum['image'][3]['#text'])
? parse_url($topAlbum['image'][3]['#text'])
: null;

if ($extralarge_image_link) {
$image_filename = pathinfo($extralarge_image_link['path'], PATHINFO_BASENAME);
$original_image_link = "https://" . $extralarge_image_link['host'] . "/i/u/" . $image_filename;
$albumsCoverUrlList[] = $original_image_link;
}
}

return $albumsCoverUrlList;
}

function createImagesFromUrls($urls)
{

$images = array();

foreach ($urls as $url) {
$fileExtension = pathinfo($url, PATHINFO_EXTENSION);

switch ($fileExtension) {
case 'jpg':
$images[] = imagecreatefromjpeg($url);
break;
case 'png':
$images[] = imagecreatefrompng($url);
break;
case 'gif':
$images[] = imagecreatefromgif($url);
break;
// Add more cases for other supported image formats if needed
}
}

return $images;
}

function createPatchwork($imagesSideSize, $patchworkHeight, $patchworkWidth, $noborder, $cols, $rows, $covers)
{

// $patchworkWidth = $imagesSideSize * $cols + ($cols - 1); // 299 is the max size of the Last.fm profile left column ;)
// $patchworkHeight = $imagesSideSize * $rows + ($rows - 1);

// create the "empty" patchwork
$patchwork = imagecreatetruecolor($patchworkWidth, $patchworkHeight);

if (!$noborder) {
// create a white color (reminds me of SDL ^^)
$white = imagecolorallocate($patchwork, 255, 255, 255);
// we fill our patchwork by the white color
imagefilltoborder($patchwork, 0, 0, $white, $white);
}

// now we "parse" our images in the patchwork, while resizing them :]
for ($i = 0; $i < $rows; $i++) {
for ($j = 0; $j < $cols; $j++) {
imagecopyresampled($patchwork, $covers[$cols * $i + $j], $j * $imagesSideSize + $j, $i * $imagesSideSize + $i, 0, 0, $imagesSideSize + intval($noborder), $imagesSideSize + intval($noborder), imagesx($covers[$cols * $i + $j]), imagesy($covers[$cols * $i + $j]));
}
}

return $patchwork;
}

function createImageJsonData($fileName, $PatchworkWidth, $PatchworkHeight)
{
$response = [
'imagePath' => $fileName,
'width' => $PatchworkWidth,
'height' => $PatchworkHeight,
];

// header('Content-Type: application/json');
return json_encode($response);
}
?>
Empty file added images/.gitkeep
Empty file.
121 changes: 121 additions & 0 deletions index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Last.fm top albums patchwork generator</title>
<meta charset="utf-8" />
<meta name="description" content="A tool that generates a patchwork, an image, based on the covers of your Last.fm top albums. It's simple, free, and it works." />
<meta name="keywords" content="lastfm top albums generator, last.fm top albums generator, lastfm top albums, last.fm top albums, lastfm, last.fm, top albums" />
<link href="styles/main.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="/vendor/picocss/pico/css/pico.min.css">
<script src="/scripts/main.js"></script>
</head>
<body>
<nav class="container-fluid">
<ul>
<li><strong>Top Album Patchwork</strong></li>
</ul>
<ul>
<li>
<a class="contrast" href="https://github.com" role="button">
Fork Me
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
<path fill="currentColor" d="M2.6 10.59L8.38 4.8l1.69 1.7c-.24.85.15 1.78.93 2.23v5.54c-.6.34-1 .99-1 1.73a2 2 0 0 0 2 2a2 2 0 0 0 2-2c0-.74-.4-1.39-1-1.73V9.41l2.07 2.09c-.07.15-.07.32-.07.5a2 2 0 0 0 2 2a2 2 0 0 0 2-2a2 2 0 0 0-2-2c-.18 0-.35 0-.5.07L13.93 7.5a1.98 1.98 0 0 0-1.15-2.34c-.43-.16-.88-.2-1.28-.09L9.8 3.38l.79-.78c.78-.79 2.04-.79 2.82 0l7.99 7.99c.79.78.79 2.04 0 2.82l-7.99 7.99c-.78.79-2.04.79-2.82 0L2.6 13.41c-.79-.78-.79-2.04 0-2.82Z" />
</svg>
</a>
</li>
</ul>
</nav>
<main class="container">
<article>
<form action="patchwork.php" method="GET" onsubmit="submitForm(event)">
<label for="username">
LastFM Username
<input type="text" id="username" name="username" placeholder="username" required>
</label>
<fieldset class="grid">
<legend>Period</legend>
<div>
<label for="overall">
<input type="radio" id="overall" name="period" value="overall" checked>
Overall
</label>
<label for="7day">
<input type="radio" id="7day" name="period" value="7day">
7 days
</label>
<label for="1month">
<input type="radio" id="1month" name="period" value="1month">
1 Month
</label>
</div>
<div>
<label for="3months">
<input type="radio" id="3months" name="period" value="3months">
3 Months
</label>
<label for="6months">
<input type="radio" id="6months" name="period" value="6months">
6 Months
</label>
<label for="1year">
<input type="radio" id="1year" name="period" value="12month">
1 Year
</label>
</div>
</fieldset>
<fieldset class="grid">
<div>
<label for="cols">Nr. of rows
<output id="rowsOutput">3</output>
</label>
<input type="range" min="1" max="20" value="3" id="rows" name="rows" oninput="rowsOutput.value = rows.value">
</label>
</div>
<div>
<label for="cols">Nr. of columns
<output id="colsOutput">3</output>
</label>
<input type="range" min="1" max="20" value="3" id="cols" name="cols" oninput="colsOutput.value = cols.value">
</label>
</div>
</fieldset>
<fieldset class="">
<label for="imageSize">Images size in pixel</label>
<input type="text" value="150" name="imageSize" id="imagesSize" />
<label for="noborder">
<input type="checkbox" name="noborder" id="noborder" />
No border
</label>
</fieldset>
<button id="submitbtn" type="submit">Generate</button>
</form>
</article>
<article class="hidden" id="resultcontainer">
<div class="">
<div class="field-title">Dynamyc Image link :</div>
<div class="img-link">
<a id="patchworkDynLink" href="" target="_blank"></a>
</div>
<div role="button" class="outline contrast" onclick="copyToClipboard(event, 'patchworkDynLink')">Copy</div>
</div>
<div>
<div class="field-title">Static Image link :</div>
<div class="img-link">
<a id="patchworkStaticLink" href="" target="_blank"></a>
</div>
<div role="button" class="outline contrast" onclick="copyToClipboard(event, 'patchworkStaticLink')">Copy</div>
</div>
<div class="patchwork">
<img src="" id="patchworkImg" width="" height="" alt="Patchwork">
</div>
</article>
</main>
</body>
</html>
106 changes: 106 additions & 0 deletions patchwork.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php
// import functions
require_once 'functions.php';

// load Lastfm APIKEY from .env
require_once realpath(__DIR__ . "/vendor/autoload.php");
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

$apiKey = $_ENV['LASTFM_API_KEY'];

// GET Params;
$lastfmUser = $_GET["username"];
$period = $_GET["period"];
$rows = $_GET["rows"];
$cols = $_GET["cols"];
$imagesSize = $_GET["imageSize"];
$noborder = (bool)(isset($_GET["noborder"]) && $_GET["noborder"]);

// return json data or image file
$json = (bool)(isset($_GET["json"]) && $_GET["json"]);

// Get 5 more albums incase there isn't an available
// image for one of the requested albums #lazyhackftw
$limit = ($cols * $rows) + 5;

// Fallback if imagesSize is not set
(isset($imagesSize)) ? $imagesSideSize = $imagesSize : $imagesSideSize = 99;

// Calculate patchwork size
$patchworkWidth = $imagesSideSize * $cols + ($cols - 1); // 299 is the max size of the Last.fm profile left column ;)
$patchworkHeight = $imagesSideSize * $rows + ($rows - 1);

do {
// check if username is valid
if (preg_match('/^[a-zA-Z0-9_.-]+$/', $lastfmUser) !== 1) {
$response = [
'error' => "Invalid Username",
];
break;
}
// check if username exist
if (!checkUserExist($lastfmUser, $apiKey) === true) {
$response = [
'error' => checkUserExist($lastfmUser, $apiKey),
];
break;
}

// Fetch top albums fron LastFM
$topAlbums = fetchtopalbums($lastfmUser, $apiKey, $period, $limit);

// Check if albums is not empty
if (empty($topAlbums)) {
$response = [
'error' => "User does not have scrobbled any albums",
];
break;
}

$border = $noborder ? "noborder" : "border";
// create Hash filename to avoid duplication
$topAlbumsDataHash = hash('md5',json_encode($topAlbums));
// $fileName = "images/$lastfmUser_$period_$rows_$cols_$imagesSize_$border-hash_$topAlbumsDataHash.jpg";
$fileName = "images/{$lastfmUser}_{$period}_{$rows}_{$cols}_{$imagesSize}_{$border}-hash_{$topAlbumsDataHash}.jpg";
// If file exist return existing data or image
if (file_exists($fileName)) {
$response = [
'imagePath' => $fileName,
'width' => $patchworkWidth,
'height' => $patchworkHeight,
];

// $patchwork = file_get_contents($fileName);
$patchwork = imagecreatefromjpeg($fileName);
// console_log($patchwork);
break;
}

// Else Generate a new patchwork
$albumsCovers = createAlbumsCoverArray($topAlbums);
$covers = createImagesFromUrls($albumsCovers);
$patchwork = createPatchwork($imagesSideSize, $patchworkHeight, $patchworkWidth, $noborder, $cols, $rows, $covers);
// console_log(gettype($patchwork));

// save the image into a file
imagejpeg($patchwork, $fileName);

$response = [
'imagePath' => $fileName,
'width' => $patchworkWidth,
'height' => $patchworkHeight,
];

} while (0);

// return json if requested else return image
if ($json) {
// return json data
header('Content-Type: application/json');
echo json_encode($response);
} else {
// display the image
header("Content-type: image/jpg");
imagejpeg($patchwork);
}
Loading

0 comments on commit e195edf

Please sign in to comment.