198 lines
5.1 KiB
PHP
198 lines
5.1 KiB
PHP
|
<?php declare(strict_types=1);
|
||
|
/**
|
||
|
* Hummingbird Anime List Client
|
||
|
*
|
||
|
* An API client for Kitsu to manage anime and manga watch lists
|
||
|
*
|
||
|
* PHP version 7.1
|
||
|
*
|
||
|
* @package HummingbirdAnimeClient
|
||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||
|
* @copyright 2015 - 2018 Timothy J. Warren
|
||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||
|
* @version 4.1
|
||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||
|
*/
|
||
|
|
||
|
namespace Aviat\AnimeClient\Controller;
|
||
|
|
||
|
use function Aviat\AnimeClient\createPlaceholderImage;
|
||
|
use function Amp\Promise\wait;
|
||
|
|
||
|
use Aviat\AnimeClient\Controller as BaseController;
|
||
|
use Aviat\AnimeClient\API\{HummingbirdClient, JsonAPI};
|
||
|
use Aviat\Ion\Di\ContainerInterface;
|
||
|
use Aviat\Ion\View\HtmlView;
|
||
|
|
||
|
/**
|
||
|
* Controller for handling routes that don't fit elsewhere
|
||
|
*/
|
||
|
final class Images extends BaseController {
|
||
|
/**
|
||
|
* Get image covers from kitsu
|
||
|
*
|
||
|
* @param string $type The category of image
|
||
|
* @param string $file The filename to look for
|
||
|
* @param bool $display Whether to output the image to the server
|
||
|
* @throws \Aviat\Ion\Di\ContainerException
|
||
|
* @throws \Aviat\Ion\Di\NotFoundException
|
||
|
* @throws \InvalidArgumentException
|
||
|
* @throws \TypeError
|
||
|
* @throws \Error
|
||
|
* @throws \Throwable
|
||
|
* @return void
|
||
|
*/
|
||
|
public function cache(string $type, string $file, $display = TRUE): void
|
||
|
{
|
||
|
$currentUrl = $this->request->getUri()->__toString();
|
||
|
|
||
|
$kitsuUrl = 'https://media.kitsu.io/';
|
||
|
$fileName = str_replace('-original', '', $file);
|
||
|
[$id, $ext] = explode('.', basename($fileName));
|
||
|
|
||
|
$baseSavePath = $this->config->get('img_cache_path');
|
||
|
|
||
|
// Kitsu doesn't serve webp, but for most use cases,
|
||
|
// jpg is a safe assumption
|
||
|
$tryJpg = ['anime','characters','manga','people'];
|
||
|
if ($ext === 'webp' && in_array($type, $tryJpg, TRUE))
|
||
|
{
|
||
|
$ext = 'jpg';
|
||
|
$currentUrl = str_replace('webp', 'jpg', $currentUrl);
|
||
|
}
|
||
|
|
||
|
$typeMap = [
|
||
|
'anime' => [
|
||
|
'kitsuUrl' => "anime/poster_images/{$id}/medium.{$ext}",
|
||
|
'width' => 220,
|
||
|
'height' => 312,
|
||
|
],
|
||
|
'avatars' => [
|
||
|
'kitsuUrl' => "users/avatars/{$id}/original.{$ext}",
|
||
|
'width' => null,
|
||
|
'height' => null,
|
||
|
],
|
||
|
'characters' => [
|
||
|
'kitsuUrl' => "characters/images/{$id}/original.{$ext}",
|
||
|
'width' => 225,
|
||
|
'height' => 350,
|
||
|
],
|
||
|
'manga' => [
|
||
|
'kitsuUrl' => "manga/poster_images/{$id}/medium.{$ext}",
|
||
|
'width' => 220,
|
||
|
'height' => 312,
|
||
|
],
|
||
|
'people' => [
|
||
|
'kitsuUrl' => "people/images/{$id}/original.{$ext}",
|
||
|
'width' => null,
|
||
|
'height' => null,
|
||
|
],
|
||
|
];
|
||
|
|
||
|
$imageType = $typeMap[$type] ?? NULL;
|
||
|
|
||
|
if (NULL === $imageType)
|
||
|
{
|
||
|
$this->getPlaceholder($baseSavePath, 200, 200);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$kitsuUrl .= $imageType['kitsuUrl'];
|
||
|
$width = $imageType['width'];
|
||
|
$height = $imageType['height'];
|
||
|
$filePrefix = "{$baseSavePath}/{$type}/{$id}";
|
||
|
|
||
|
$promise = (new HummingbirdClient)->request($kitsuUrl);
|
||
|
$response = wait($promise);
|
||
|
|
||
|
if ($response->getStatus() !== 200)
|
||
|
{
|
||
|
// Try a few different file types before giving up
|
||
|
// webm => jpg => png => gif
|
||
|
$nextType = [
|
||
|
'jpg' => 'png',
|
||
|
'png' => 'gif',
|
||
|
];
|
||
|
|
||
|
if (array_key_exists($ext, $nextType))
|
||
|
{
|
||
|
$newUrl = str_replace($ext, $nextType[$ext], $currentUrl);
|
||
|
$this->redirect($newUrl, 303);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ($display)
|
||
|
{
|
||
|
$this->getPlaceholder("{$baseSavePath}/{$type}", $width, $height);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
createPlaceholderImage("{$baseSavePath}/{$type}", $width, $height);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$data = wait($response->getBody());
|
||
|
|
||
|
|
||
|
|
||
|
[$origWidth] = getimagesizefromstring($data);
|
||
|
$gdImg = imagecreatefromstring($data);
|
||
|
$resizedImg = imagescale($gdImg, $width ?? $origWidth);
|
||
|
|
||
|
if ($ext === 'gif')
|
||
|
{
|
||
|
file_put_contents("{$filePrefix}.gif", $data);
|
||
|
imagepalletetotruecolor($gdImg);
|
||
|
}
|
||
|
|
||
|
// save the webp versions
|
||
|
imagewebp($gdImg, "{$filePrefix}-original.webp");
|
||
|
imagewebp($resizedImg, "{$filePrefix}.webp");
|
||
|
|
||
|
// save the scaled jpeg file
|
||
|
imagejpeg($resizedImg, "{$filePrefix}.jpg");
|
||
|
|
||
|
// And the original
|
||
|
file_put_contents("{$filePrefix}-original.jpg", $data);
|
||
|
|
||
|
imagedestroy($gdImg);
|
||
|
imagedestroy($resizedImg);
|
||
|
|
||
|
if ($display)
|
||
|
{
|
||
|
$contentType = ($ext === 'webp')
|
||
|
? "image/webp"
|
||
|
: $response->getHeader('content-type')[0];
|
||
|
|
||
|
$outputFile = (strpos($file, '-original') !== FALSE)
|
||
|
? "{$filePrefix}-original.{$ext}"
|
||
|
: "{$filePrefix}.{$ext}";
|
||
|
|
||
|
header("Content-Type: {$contentType}");
|
||
|
echo file_get_contents($outputFile);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get a placeholder for a missing image
|
||
|
*
|
||
|
* @param string $path
|
||
|
* @param int|null $width
|
||
|
* @param int|null $height
|
||
|
*/
|
||
|
private function getPlaceholder (string $path, ?int $width = 200, ?int $height = NULL): void
|
||
|
{
|
||
|
$height = $height ?? $width;
|
||
|
|
||
|
$filename = $path . '/placeholder.png';
|
||
|
|
||
|
if ( ! file_exists($path . '/placeholder.png'))
|
||
|
{
|
||
|
createPlaceholderImage($path, $width, $height);
|
||
|
}
|
||
|
|
||
|
header('Content-Type: image/png');
|
||
|
echo file_get_contents($filename);
|
||
|
}
|
||
|
}
|