Various refactoring, better webp image handling

This commit is contained in:
Timothy Warren 2018-11-01 22:15:20 -04:00
parent c2d51b2b7e
commit cd2dcf2873
11 changed files with 222 additions and 63 deletions

View File

@ -17,6 +17,10 @@ try
(new Console([
'cache:clear' => Command\CacheClear::class,
'cache:refresh' => Command\CachePrime::class,
'clear:cache' => Command\CacheClear::class,
'clear:thumbnails' => Command\ClearThumbnails::class,
'refresh:cache' => Command\CachePrime::class,
'refresh:thumbnails' => Command\UpdateThumbnails::class,
'regenerate-thumbnails' => Command\UpdateThumbnails::class,
'lists:sync' => Command\SyncLists::class,
'mal_id:check' => Command\MALIDCheck::class,

View File

@ -141,6 +141,8 @@ final class JsonAPI {
$relationship[$dataType][$idKey][$j] = $included[$dataType][$idKey];
}
unset($item['relationships'][$relType]['data']);
}
}
}
@ -221,6 +223,11 @@ final class JsonAPI {
continue;
}
if ( ! array_key_exists($dataType, $organized))
{
$organized[$dataType] = [];
}
if (array_key_exists($idKey, $organized[$dataType]))
{
$relationship[$dataType][$idKey] = $organized[$dataType][$idKey];
@ -298,7 +305,10 @@ final class JsonAPI {
$type = $item['type'];
$id = $item['id'];
$organized[$type][$id] = $item['attributes'];
if (array_key_exists('attributes', $item))
{
$organized[$type][$id] = $item['attributes'];
}
if (array_key_exists('relationships', $item))
{

View File

@ -235,15 +235,20 @@ final class Model {
{
$data = $this->getRequest("people/{$id}", [
'query' => [
'filter' => [
'id' => $id,
],
'fields' => [
'characters' => 'canonicalName,slug,image',
'characterVoices' => 'mediaCharacter',
'anime' => 'canonicalTitle,titles,slug,posterImage',
'manga' => 'canonicalTitle,titles,slug,posterImage',
'mediaCharacters' => 'role,media,character',
'mediaStaff' => 'role,media,person',
],
'include' => 'castings.character,castings.media'
'include' => 'voices.mediaCharacter.media,voices.mediaCharacter.character,staff.media',
],
]);
$cacheItem->set($data);
$cacheItem->save();
}
@ -268,7 +273,7 @@ final class Model {
'fields' => [
'anime' => 'slug,canonicalTitle,posterImage',
'manga' => 'slug,canonicalTitle,posterImage',
'characters' => 'slug,canonicalName,image'
'characters' => 'slug,canonicalName,image',
],
'include' => 'waifu,favorites.item,stats'
]
@ -364,13 +369,13 @@ final class Model {
* @param string $slug
* @return Anime
*/
public function getAnime(string $slug): Anime
public function getAnime(string $slug)
{
$baseData = $this->getRawMediaData('anime', $slug);
if (empty($baseData))
{
return new Anime();
return (new Anime([]))->toArray();
}
return $this->animeTransformer->transform($baseData);
@ -966,7 +971,7 @@ final class Model {
'mediaCharacters' => 'character,role',
],
'include' => ($type === 'anime')
? 'staff,staff.person,categories,mappings,streamingLinks,animeCharacters.character'
? 'staff,staff.person,categories,mappings,streamingLinks,animeCharacters.character,characters.character'
: 'staff,staff.person,categories,mappings,characters.character',
]
];

View File

@ -40,7 +40,9 @@ final class AnimeTransformer extends AbstractTransformer {
sort($item['genres']);
$title = $item['canonicalTitle'];
$titles = array_unique(array_diff($item['titles'], [$title]));
$titles = Kitsu::filterTitles($item);
// $titles = array_unique(array_diff($item['titles'], [$title]));
return new Anime([
'age_rating' => $item['ageRating'],

View File

@ -71,7 +71,14 @@ function loadTomlFile(string $filename): array
return Toml::parseFile($filename);
}
function _iterateToml(TomlBuilder $builder, $data, $parentKey = NULL): void
/**
* Recursively create a toml file from a data array
*
* @param TomlBuilder $builder
* @param iterable $data
* @param null $parentKey
*/
function _iterateToml(TomlBuilder $builder, iterable $data, $parentKey = NULL): void
{
foreach ($data as $key => $value)
{
@ -107,7 +114,7 @@ function _iterateToml(TomlBuilder $builder, $data, $parentKey = NULL): void
* @param mixed $data
* @return string
*/
function arrayToToml($data): string
function arrayToToml(iterable $data): string
{
$builder = new TomlBuilder();
@ -197,28 +204,31 @@ function checkFolderPermissions(ConfigInterface $config): array
}
/**
* Generate the path for the cached image from the original iamge
* Generate the path for the cached image from the original image
*
* @param string $kitsuUrl
* @param bool $webp
* @return string
*/
function getLocalImg ($kitsuUrl): string
function getLocalImg ($kitsuUrl, $webp = TRUE): string
{
if ( ! is_string($kitsuUrl))
{
return 'images/404/404.png';
return 'images/placeholder.webp';
}
$parts = parse_url($kitsuUrl);
if ($parts === FALSE)
{
return 'images/404/404.png';
return 'images/placeholder.webp';
}
$file = basename($parts['path']);
$fileParts = explode('.', $file);
$ext = array_pop($fileParts);
$ext = $webp ? 'webp' : $ext;
$segments = explode('/', trim($parts['path'], '/'));
$type = $segments[0] === 'users' ? $segments[1] : $segments[0];
@ -241,12 +251,15 @@ function createPlaceholderImage ($path, $width, $height, $text = 'Image Unavaila
$width = $width ?? 200;
$height = $height ?? 200;
$img = imagecreate($width, $height);
$img = imagecreatetruecolor($width, $height);
imagealphablending($img, TRUE);
$path = rtrim($path, '/');
// Background is the first color by default
imagecolorallocatealpha($img, 255, 255, 255, 127);
$fillColor = imagecolorallocatealpha($img, 255, 255, 255, 127);
imagefill($img, 0, 0, $fillColor);
$textColor = imagecolorallocate($img, 64, 64, 64);
imagealphablending($img, TRUE);
@ -266,6 +279,13 @@ function createPlaceholderImage ($path, $width, $height, $text = 'Image Unavaila
// Save the images
imagesavealpha($img, TRUE);
imagepng($img, $path . '/placeholder.png', 9);
imagedestroy($img);
$pngImage = imagecreatefrompng($path . '/placeholder.png');
imagealphablending($pngImage, TRUE);
imagesavealpha($pngImage, TRUE);
imagewebp($pngImage, $path . '/placeholder.webp');
imagedestroy($pngImage);
}

View File

@ -0,0 +1,59 @@
<?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\Command;
/**
* Clears out image cache directories
*/
class ClearThumbnails extends BaseCommand {
public function execute(array $args, array $options = []): void
{
$this->clearThumbs();
$this->echoBox('All cached images have been removed');
}
public function clearThumbs()
{
$imgDir = realpath(__DIR__ . '/../../public/images');
$paths = [
'avatars/*.gif',
'avatars/*.jpg',
'avatars/*.png',
'avatars/*.webp',
'anime/*.jpg',
'anime/*.png',
'anime/*.webp',
'manga/*.jpg',
'manga/*.png',
'manga/*.webp',
'characters/*.jpg',
'characters/*.png',
'characters/*.webp',
'people/*.jpg',
'people/*.png',
'people/*.webp',
];
foreach($paths as $path)
{
$cmd = "rm -rf {$imgDir}/{$path}";
exec($cmd);
}
}
}

View File

@ -23,7 +23,7 @@ use Aviat\AnimeClient\Controller\Index;
* Clears out image cache directories, then re-creates the image cache
* for manga and anime
*/
final class UpdateThumbnails extends BaseCommand {
final class UpdateThumbnails extends ClearThumbnails {
/**
* Model for making requests to Kitsu API
* @var \Aviat\AnimeClient\API\Kitsu\Model
@ -43,13 +43,11 @@ final class UpdateThumbnails extends BaseCommand {
$this->controller = new Index($this->container);
$this->kitsuModel = $this->container->get('kitsu-model');
$this->clearThumbs();
// Clear the existing thunbnails
parent::execute($args, $options);
$ids = $this->getImageList();
// print_r($ids);
// echo json_encode($ids, \JSON_PRETTY_PRINT);
// Resave the images
foreach($ids as $type => $typeIds)
{
@ -64,26 +62,6 @@ final class UpdateThumbnails extends BaseCommand {
$this->echoBox('Finished regenerating all thumbnails');
}
public function clearThumbs()
{
$imgDir = realpath(__DIR__ . '/../../public/images');
$paths = [
'anime/*.jpg',
'anime/*.webp',
'manga/*.jpg',
'manga/*.webp',
'characters/*.jpg',
'characters/*.webp',
];
foreach($paths as $path)
{
$cmd = "rm -rf {$imgDir}/{$path}";
exec($cmd);
}
}
public function getImageList()
{
$mangaList = $this->kitsuModel->getFullRawMangaList();

View File

@ -363,9 +363,15 @@ final class Dispatcher extends RoutingBase {
? $controllerMap[$routeType]
: DEFAULT_CONTROLLER;
if (array_key_exists($routeType, $controllerMap))
// If there's an explicit controller, try to find
// the full namespaced class name
if (array_key_exists('controller', $route))
{
$controllerClass = $controllerMap[$routeType];
$controllerKey = $route['controller'];
if (array_key_exists($controllerKey, $controllerMap))
{
$controllerClass = $controllerMap[$controllerKey];
}
}
// Prepend the controller to the route parameters

85
src/Helper/Picture.php Normal file
View File

@ -0,0 +1,85 @@
<?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\Helper;
use Aviat\Ion\Di\ContainerAware;
/**
* Simplify picture elements
*/
final class Picture {
use ContainerAware;
/**
* Create the html f
*
* @param string $webp
* @param string $fallbackExt
* @param array $picAttrs
* @param array $imgAttrs
* @return string
*/
public function __invoke(string $webp, string $fallbackExt = 'jpg', $picAttrs = [], $imgAttrs = []): string
{
$urlGenerator = $this->container->get('url-generator');
$helper = $this->container->get('html-helper');
// If it is a placeholder image, make the
// fallback a png, not a jpg
if (strpos($webp, 'placeholder') !== FALSE)
{
$fallbackExt = 'png';
}
if (strpos($webp, '//') === FALSE)
{
$webp = $urlGenerator->assetUrl($webp);
}
$urlParts = explode('.', $webp);
$ext = array_pop($urlParts);
$path = implode('.', $urlParts);
$mime = $ext === 'jpg'
? 'image/jpeg'
: "image/{$ext}";
$fallbackMime = $fallbackExt === 'jpg'
? 'image/jpeg'
: "image/{$fallbackExt}";
$fallbackImg = "{$path}.{$fallbackExt}";
$pictureChildren = [
$helper->void('source', [
'srcset' => $webp,
'type' => $mime,
]),
$helper->void('source', [
'srcset' => $fallbackImg,
'type' => $fallbackMime
]),
$helper->img($fallbackImg, array_merge(['alt' => ''], $imgAttrs)),
];
$sources = implode('', $pictureChildren);
return $helper->elementRaw('picture', $sources, $picAttrs);
}
}
// End of Picture.php

View File

@ -108,7 +108,7 @@ class Anime extends API {
* @param string $slug
* @return AnimeType
*/
public function getAnime(string $slug): AnimeType
public function getAnime(string $slug)
{
return $this->kitsuModel->getAnime($slug);
}
@ -173,14 +173,6 @@ class Anime extends API {
$results = $requester->makeRequests();
// Debug info
/* $body = Json::decode($results['anilist']);
if ($body['errors'])
{
dump($body);
die();
} */
return count($results) > 0;
}
@ -261,13 +253,6 @@ class Anime extends API {
$results = $requester->makeRequests();
// Debug info
/* $body = Json::decode($results['anilist']);
if (isset($body['errors'])) {
dump($body);
die();
} */
return count($results) > 0;
}
}

View File

@ -16,7 +16,7 @@
namespace Aviat\AnimeClient;
const DEFAULT_CONTROLLER = Controller\Index::class;
const DEFAULT_CONTROLLER = Controller\Misc::class;
const DEFAULT_CONTROLLER_METHOD = 'index';
const DEFAULT_CONTROLLER_NAMESPACE = Controller::class;
const DEFAULT_LIST_CONTROLLER = Controller\Anime::class;
@ -24,7 +24,12 @@ const ERROR_MESSAGE_METHOD = 'errorPage';
const NOT_FOUND_METHOD = 'notFound';
const SESSION_SEGMENT = 'Aviat\AnimeClient\Auth';
const SRC_DIR = __DIR__;
const USER_AGENT = "Tim's Anime Client/4.0";
const USER_AGENT = "Tim's Anime Client/4.1";
// Regex patterns
const ALPHA_SLUG_PATTERN = '[a-z_]+';
const NUM_PATTERN = '[0-9]+';
const SLUG_PATTERN = '[a-z0-9\-]+';
// Why doesn't this already exist?
const MILLI_FROM_NANO = 1000 * 1000;
@ -129,7 +134,7 @@ const SETTINGS_MAP = [
],
],
],
/*'options' => [
/* 'options' => [
'type' => 'subfield',
'title' => 'Options',
'fields' => [],