Browse Source

Various refactoring, better webp image handling

Timothy J. Warren 2 weeks ago
parent
commit
cd2dcf2873

+ 4
- 0
console View File

@@ -17,6 +17,10 @@ try
17 17
 	(new Console([
18 18
 		'cache:clear' => Command\CacheClear::class,
19 19
 		'cache:refresh' => Command\CachePrime::class,
20
+		'clear:cache' => Command\CacheClear::class,
21
+		'clear:thumbnails' => Command\ClearThumbnails::class,
22
+		'refresh:cache' => Command\CachePrime::class,
23
+		'refresh:thumbnails' => Command\UpdateThumbnails::class,
20 24
 		'regenerate-thumbnails' => Command\UpdateThumbnails::class,
21 25
 		'lists:sync' => Command\SyncLists::class,
22 26
 		'mal_id:check' => Command\MALIDCheck::class,

+ 11
- 1
src/API/JsonAPI.php View File

@@ -141,6 +141,8 @@ final class JsonAPI {
141 141
 
142 142
 								$relationship[$dataType][$idKey][$j] = $included[$dataType][$idKey];
143 143
 							}
144
+
145
+							unset($item['relationships'][$relType]['data']);
144 146
 						}
145 147
 					}
146 148
 				}
@@ -221,6 +223,11 @@ final class JsonAPI {
221 223
 								continue;
222 224
 							}
223 225
 
226
+							if ( ! array_key_exists($dataType, $organized))
227
+							{
228
+								$organized[$dataType] = [];
229
+							}
230
+
224 231
 							if (array_key_exists($idKey, $organized[$dataType]))
225 232
 							{
226 233
 								$relationship[$dataType][$idKey] = $organized[$dataType][$idKey];
@@ -298,7 +305,10 @@ final class JsonAPI {
298 305
 			$type = $item['type'];
299 306
 			$id = $item['id'];
300 307
 
301
-			$organized[$type][$id] = $item['attributes'];
308
+			if (array_key_exists('attributes', $item))
309
+			{
310
+				$organized[$type][$id] = $item['attributes'];
311
+			}
302 312
 
303 313
 			if (array_key_exists('relationships', $item))
304 314
 			{

+ 11
- 6
src/API/Kitsu/Model.php View File

@@ -235,15 +235,20 @@ final class Model {
235 235
 		{
236 236
 			$data = $this->getRequest("people/{$id}", [
237 237
 				'query' => [
238
+					'filter' => [
239
+						'id' => $id,
240
+					],
238 241
 					'fields' => [
239 242
 						'characters' => 'canonicalName,slug,image',
243
+						'characterVoices' => 'mediaCharacter',
240 244
 						'anime' => 'canonicalTitle,titles,slug,posterImage',
241 245
 						'manga' => 'canonicalTitle,titles,slug,posterImage',
246
+						'mediaCharacters' => 'role,media,character',
247
+						'mediaStaff' => 'role,media,person',
242 248
 					],
243
-					'include' => 'castings.character,castings.media'
249
+					'include' => 'voices.mediaCharacter.media,voices.mediaCharacter.character,staff.media',
244 250
 				],
245 251
 			]);
246
-
247 252
 			$cacheItem->set($data);
248 253
 			$cacheItem->save();
249 254
 		}
@@ -268,7 +273,7 @@ final class Model {
268 273
 				'fields' => [
269 274
 					'anime' => 'slug,canonicalTitle,posterImage',
270 275
 					'manga' => 'slug,canonicalTitle,posterImage',
271
-					'characters' => 'slug,canonicalName,image'
276
+					'characters' => 'slug,canonicalName,image',
272 277
 				],
273 278
 				'include' => 'waifu,favorites.item,stats'
274 279
 			]
@@ -364,13 +369,13 @@ final class Model {
364 369
 	 * @param string $slug
365 370
 	 * @return Anime
366 371
 	 */
367
-	public function getAnime(string $slug): Anime
372
+	public function getAnime(string $slug)
368 373
 	{
369 374
 		$baseData = $this->getRawMediaData('anime', $slug);
370 375
 
371 376
 		if (empty($baseData))
372 377
 		{
373
-			return new Anime();
378
+			return (new Anime([]))->toArray();
374 379
 		}
375 380
 
376 381
 		return $this->animeTransformer->transform($baseData);
@@ -966,7 +971,7 @@ final class Model {
966 971
 					'mediaCharacters' => 'character,role',
967 972
 				],
968 973
 				'include' => ($type === 'anime')
969
-					? 'staff,staff.person,categories,mappings,streamingLinks,animeCharacters.character'
974
+					? 'staff,staff.person,categories,mappings,streamingLinks,animeCharacters.character,characters.character'
970 975
 					: 'staff,staff.person,categories,mappings,characters.character',
971 976
 			]
972 977
 		];

+ 3
- 1
src/API/Kitsu/Transformer/AnimeTransformer.php View File

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

+ 29
- 9
src/AnimeClient.php View File

@@ -71,7 +71,14 @@ function loadTomlFile(string $filename): array
71 71
 	return Toml::parseFile($filename);
72 72
 }
73 73
 
74
-function _iterateToml(TomlBuilder $builder, $data, $parentKey = NULL): void
74
+/**
75
+ * Recursively create a toml file from a data array
76
+ *
77
+ * @param TomlBuilder $builder
78
+ * @param iterable $data
79
+ * @param null $parentKey
80
+ */
81
+function _iterateToml(TomlBuilder $builder, iterable $data, $parentKey = NULL): void
75 82
 {
76 83
 	foreach ($data as $key => $value)
77 84
 	{
@@ -107,7 +114,7 @@ function _iterateToml(TomlBuilder $builder, $data, $parentKey = NULL): void
107 114
  * @param mixed $data
108 115
  * @return string
109 116
  */
110
-function arrayToToml($data): string
117
+function arrayToToml(iterable $data): string
111 118
 {
112 119
 	$builder = new TomlBuilder();
113 120
 
@@ -197,28 +204,31 @@ function checkFolderPermissions(ConfigInterface $config): array
197 204
 }
198 205
 
199 206
 /**
200
- * Generate the path for the cached image from the original iamge
207
+ * Generate the path for the cached image from the original image
201 208
  *
202 209
  * @param string $kitsuUrl
210
+ * @param bool $webp
203 211
  * @return string
204 212
  */
205
-function getLocalImg ($kitsuUrl): string
213
+function getLocalImg ($kitsuUrl, $webp = TRUE): string
206 214
 {
207 215
 	if ( ! is_string($kitsuUrl))
208 216
 	{
209
-		return 'images/404/404.png';
217
+		return 'images/placeholder.webp';
210 218
 	}
211 219
 
212 220
 	$parts = parse_url($kitsuUrl);
213 221
 
214 222
 	if ($parts === FALSE)
215 223
 	{
216
-		return 'images/404/404.png';
224
+		return 'images/placeholder.webp';
217 225
 	}
218 226
 
219 227
 	$file = basename($parts['path']);
220 228
 	$fileParts = explode('.', $file);
221 229
 	$ext = array_pop($fileParts);
230
+	$ext = $webp ? 'webp' : $ext;
231
+
222 232
 	$segments = explode('/', trim($parts['path'], '/'));
223 233
 
224 234
 	$type = $segments[0] === 'users' ? $segments[1] : $segments[0];
@@ -241,12 +251,15 @@ function createPlaceholderImage ($path, $width, $height, $text = 'Image Unavaila
241 251
 	$width = $width ?? 200;
242 252
 	$height = $height ?? 200;
243 253
 
244
-	$img = imagecreate($width, $height);
254
+	$img = imagecreatetruecolor($width, $height);
255
+	imagealphablending($img, TRUE);
245 256
 
246 257
 	$path = rtrim($path, '/');
247 258
 
248 259
 	// Background is the first color by default
249
-	imagecolorallocatealpha($img, 255, 255, 255, 127);
260
+	$fillColor = imagecolorallocatealpha($img, 255, 255, 255, 127);
261
+	imagefill($img, 0, 0, $fillColor);
262
+	
250 263
 	$textColor = imagecolorallocate($img, 64, 64, 64);
251 264
 
252 265
 	imagealphablending($img, TRUE);
@@ -266,6 +279,13 @@ function createPlaceholderImage ($path, $width, $height, $text = 'Image Unavaila
266 279
 	// Save the images
267 280
 	imagesavealpha($img, TRUE);
268 281
 	imagepng($img, $path . '/placeholder.png', 9);
269
-
270 282
 	imagedestroy($img);
283
+	
284
+	$pngImage = imagecreatefrompng($path . '/placeholder.png');
285
+	imagealphablending($pngImage, TRUE);
286
+	imagesavealpha($pngImage, TRUE);
287
+	
288
+	imagewebp($pngImage, $path . '/placeholder.webp');
289
+
290
+	imagedestroy($pngImage);
271 291
 }

+ 59
- 0
src/Command/ClearThumbnails.php View File

@@ -0,0 +1,59 @@
1
+<?php declare(strict_types=1);
2
+/**
3
+ * Hummingbird Anime List Client
4
+ *
5
+ * An API client for Kitsu to manage anime and manga watch lists
6
+ *
7
+ * PHP version 7.1
8
+ *
9
+ * @package     HummingbirdAnimeClient
10
+ * @author      Timothy J. Warren <tim@timshomepage.net>
11
+ * @copyright   2015 - 2018  Timothy J. Warren
12
+ * @license     http://www.opensource.org/licenses/mit-license.html  MIT License
13
+ * @version     4.1
14
+ * @link        https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
15
+ */
16
+
17
+namespace Aviat\AnimeClient\Command;
18
+
19
+/**
20
+ * Clears out image cache directories
21
+ */
22
+class ClearThumbnails extends BaseCommand {
23
+
24
+	public function execute(array $args, array $options = []): void
25
+	{
26
+		$this->clearThumbs();
27
+		$this->echoBox('All cached images have been removed');
28
+	}
29
+
30
+	public function clearThumbs()
31
+	{
32
+		$imgDir = realpath(__DIR__ . '/../../public/images');
33
+
34
+		$paths = [
35
+			'avatars/*.gif',
36
+			'avatars/*.jpg',
37
+			'avatars/*.png',
38
+			'avatars/*.webp',
39
+			'anime/*.jpg',
40
+			'anime/*.png',
41
+			'anime/*.webp',
42
+			'manga/*.jpg',
43
+			'manga/*.png',
44
+			'manga/*.webp',
45
+			'characters/*.jpg',
46
+			'characters/*.png',
47
+			'characters/*.webp',
48
+			'people/*.jpg',
49
+			'people/*.png',
50
+			'people/*.webp',
51
+		];
52
+
53
+		foreach($paths as $path)
54
+		{
55
+			$cmd = "rm -rf {$imgDir}/{$path}";
56
+			exec($cmd);
57
+		}
58
+	}
59
+}

+ 3
- 25
src/Command/UpdateThumbnails.php View File

@@ -23,7 +23,7 @@ use Aviat\AnimeClient\Controller\Index;
23 23
  * Clears out image cache directories, then re-creates the image cache
24 24
  * for manga and anime
25 25
  */
26
-final class UpdateThumbnails extends BaseCommand {
26
+final class UpdateThumbnails extends ClearThumbnails {
27 27
 	/**
28 28
 	 * Model for making requests to Kitsu API
29 29
 	 * @var \Aviat\AnimeClient\API\Kitsu\Model
@@ -43,13 +43,11 @@ final class UpdateThumbnails extends BaseCommand {
43 43
 		$this->controller = new Index($this->container);
44 44
 		$this->kitsuModel = $this->container->get('kitsu-model');
45 45
 
46
-		$this->clearThumbs();
46
+		// Clear the existing thunbnails
47
+		parent::execute($args, $options);
47 48
 
48 49
 		$ids = $this->getImageList();
49 50
 
50
-		// print_r($ids);
51
-		// echo json_encode($ids, \JSON_PRETTY_PRINT);
52
-
53 51
 		// Resave the images
54 52
 		foreach($ids as $type => $typeIds)
55 53
 		{
@@ -64,26 +62,6 @@ final class UpdateThumbnails extends BaseCommand {
64 62
 		$this->echoBox('Finished regenerating all thumbnails');
65 63
 	}
66 64
 
67
-	public function clearThumbs()
68
-	{
69
-		$imgDir = realpath(__DIR__ . '/../../public/images');
70
-
71
-		$paths = [
72
-			'anime/*.jpg',
73
-			'anime/*.webp',
74
-			'manga/*.jpg',
75
-			'manga/*.webp',
76
-			'characters/*.jpg',
77
-			'characters/*.webp',
78
-		];
79
-
80
-		foreach($paths as $path)
81
-		{
82
-			$cmd = "rm -rf {$imgDir}/{$path}";
83
-			exec($cmd);
84
-		}
85
-	}
86
-
87 65
 	public function getImageList()
88 66
 	{
89 67
 		$mangaList = $this->kitsuModel->getFullRawMangaList();

+ 8
- 2
src/Dispatcher.php View File

@@ -363,9 +363,15 @@ final class Dispatcher extends RoutingBase {
363 363
 				? $controllerMap[$routeType]
364 364
 				: DEFAULT_CONTROLLER;
365 365
 
366
-			if (array_key_exists($routeType, $controllerMap))
366
+			// If there's an explicit controller, try to find
367
+			// the full namespaced class name
368
+			if (array_key_exists('controller', $route))
367 369
 			{
368
-				$controllerClass = $controllerMap[$routeType];
370
+				$controllerKey = $route['controller'];
371
+				if (array_key_exists($controllerKey, $controllerMap))
372
+				{
373
+					$controllerClass = $controllerMap[$controllerKey];
374
+				}
369 375
 			}
370 376
 
371 377
 			// Prepend the controller to the route parameters

+ 85
- 0
src/Helper/Picture.php View File

@@ -0,0 +1,85 @@
1
+<?php declare(strict_types=1);
2
+/**
3
+ * Hummingbird Anime List Client
4
+ *
5
+ * An API client for Kitsu to manage anime and manga watch lists
6
+ *
7
+ * PHP version 7.1
8
+ *
9
+ * @package     HummingbirdAnimeClient
10
+ * @author      Timothy J. Warren <tim@timshomepage.net>
11
+ * @copyright   2015 - 2018  Timothy J. Warren
12
+ * @license     http://www.opensource.org/licenses/mit-license.html  MIT License
13
+ * @version     4.1
14
+ * @link        https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
15
+ */
16
+
17
+namespace Aviat\AnimeClient\Helper;
18
+
19
+use Aviat\Ion\Di\ContainerAware;
20
+
21
+/**
22
+ * Simplify picture elements
23
+ */
24
+final class Picture {
25
+
26
+	use ContainerAware;
27
+
28
+	/**
29
+	 * Create the html f
30
+	 *
31
+	 * @param string $webp
32
+	 * @param string $fallbackExt
33
+	 * @param array $picAttrs
34
+	 * @param array $imgAttrs
35
+	 * @return string
36
+	 */
37
+	public function __invoke(string $webp, string $fallbackExt = 'jpg', $picAttrs = [], $imgAttrs = []): string
38
+	{
39
+		$urlGenerator = $this->container->get('url-generator');
40
+		$helper = $this->container->get('html-helper');
41
+		
42
+		// If it is a placeholder image, make the
43
+		// fallback a png, not a jpg
44
+		if (strpos($webp, 'placeholder') !== FALSE)
45
+		{
46
+			$fallbackExt = 'png';
47
+		}
48
+
49
+		if (strpos($webp, '//') === FALSE)
50
+		{
51
+			$webp = $urlGenerator->assetUrl($webp);
52
+		}
53
+		
54
+
55
+		$urlParts = explode('.', $webp);
56
+		$ext = array_pop($urlParts);
57
+		$path = implode('.', $urlParts);
58
+
59
+		$mime = $ext === 'jpg'
60
+			? 'image/jpeg'
61
+			: "image/{$ext}";
62
+		$fallbackMime = $fallbackExt === 'jpg'
63
+			? 'image/jpeg'
64
+			: "image/{$fallbackExt}";
65
+
66
+		$fallbackImg = "{$path}.{$fallbackExt}";
67
+
68
+		$pictureChildren = [
69
+			$helper->void('source', [
70
+				'srcset' => $webp,
71
+				'type' => $mime,
72
+			]),
73
+			$helper->void('source', [
74
+				'srcset' => $fallbackImg,
75
+				'type' => $fallbackMime
76
+			]),
77
+			$helper->img($fallbackImg, array_merge(['alt' => ''], $imgAttrs)),
78
+		];
79
+
80
+		$sources = implode('', $pictureChildren);
81
+
82
+		return $helper->elementRaw('picture', $sources, $picAttrs);
83
+	}
84
+}
85
+// End of Picture.php

+ 1
- 16
src/Model/Anime.php View File

@@ -108,7 +108,7 @@ class Anime extends API {
108 108
 	 * @param string $slug
109 109
 	 * @return AnimeType
110 110
 	 */
111
-	public function getAnime(string $slug): AnimeType
111
+	public function getAnime(string $slug)
112 112
 	{
113 113
 		return $this->kitsuModel->getAnime($slug);
114 114
 	}
@@ -173,14 +173,6 @@ class Anime extends API {
173 173
 
174 174
 		$results = $requester->makeRequests();
175 175
 
176
-		// Debug info
177
-		/* $body = Json::decode($results['anilist']);
178
-		if ($body['errors'])
179
-		{
180
-			dump($body);
181
-			die();
182
-		} */
183
-
184 176
 		return count($results) > 0;
185 177
 	}
186 178
 
@@ -261,13 +253,6 @@ class Anime extends API {
261 253
 
262 254
 		$results = $requester->makeRequests();
263 255
 
264
-		// Debug info
265
-		/* $body = Json::decode($results['anilist']);
266
-		if (isset($body['errors'])) {
267
-			dump($body);
268
-			die();
269
-		} */
270
-
271 256
 		return count($results) > 0;
272 257
 	}
273 258
 }

+ 8
- 3
src/constants.php View File

@@ -16,7 +16,7 @@
16 16
 
17 17
 namespace Aviat\AnimeClient;
18 18
 
19
-const DEFAULT_CONTROLLER = Controller\Index::class;
19
+const DEFAULT_CONTROLLER = Controller\Misc::class;
20 20
 const DEFAULT_CONTROLLER_METHOD = 'index';
21 21
 const DEFAULT_CONTROLLER_NAMESPACE = Controller::class;
22 22
 const DEFAULT_LIST_CONTROLLER = Controller\Anime::class;
@@ -24,7 +24,12 @@ const ERROR_MESSAGE_METHOD = 'errorPage';
24 24
 const NOT_FOUND_METHOD = 'notFound';
25 25
 const SESSION_SEGMENT = 'Aviat\AnimeClient\Auth';
26 26
 const SRC_DIR = __DIR__;
27
-const USER_AGENT = "Tim's Anime Client/4.0";
27
+const USER_AGENT = "Tim's Anime Client/4.1";
28
+
29
+// Regex patterns
30
+const ALPHA_SLUG_PATTERN = '[a-z_]+';
31
+const NUM_PATTERN = '[0-9]+';
32
+const SLUG_PATTERN = '[a-z0-9\-]+';
28 33
 
29 34
 // Why doesn't this already exist?
30 35
 const MILLI_FROM_NANO = 1000 * 1000;
@@ -129,7 +134,7 @@ const SETTINGS_MAP = [
129 134
 				],
130 135
 			],
131 136
 		],
132
-		/*'options' => [
137
+		/* 'options' => [
133 138
 			'type' => 'subfield',
134 139
 			'title' => 'Options',
135 140
 			'fields' => [],