Browse Source

Ugly Progress Commit

* Update Person pages to have series organized by character for Voice
Acting
* Miscellaneous style updates
* Add placeholder images for items missing images
Timothy J. Warren 3 weeks ago
parent
commit
50b65d66e1

+ 7
- 7
app/views/anime/details.php View File

@@ -1,6 +1,6 @@
1 1
 <main class="details fixed">
2
-	<section class="flex flex-no-wrap">
3
-		<div>
2
+	<section class="flex">
3
+		<aside class="info">
4 4
 			<picture class="cover">
5 5
 				<source srcset="<?= $urlGenerator->assetUrl("images/anime/{$show_data['id']}-original.webp") ?>" type="image/webp">
6 6
 				<source srcset="<?= $urlGenerator->assetUrl("images/anime/{$show_data['id']}-original.jpg") ?>" type="image/jpeg">
@@ -40,8 +40,8 @@
40 40
 					</td>
41 41
 				</tr>
42 42
 			</table>
43
-		</div>
44
-		<div>
43
+		</aside>
44
+		<article class="text">
45 45
 			<h2><a rel="external" href="<?= $show_data['url'] ?>"><?= $show_data['title'] ?></a></h2>
46 46
             <?php foreach ($show_data['titles'] as $title): ?>
47 47
                 <h3><?= $title ?></h3>
@@ -85,13 +85,13 @@
85 85
 				<h4>Trailer</h4>
86 86
 				<iframe width="560" height="315" src="https://www.youtube.com/embed/<?= $show_data['trailer_id'] ?>" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
87 87
 			<?php endif ?>
88
-		</div>
88
+		</article>
89 89
 	</section>
90 90
 
91 91
 	<?php if (count($characters) > 0): ?>
92
-	<hr />
92
+	<br />
93 93
 	<h2>Characters</h2>
94
-	<section class="align_center media-wrap">
94
+	<section class="media-wrap flex flex-wrap flex-justify-start">
95 95
 	<?php foreach($characters as $id => $char): ?>
96 96
 		<?php if ( ! empty($char['image']['original'])): ?>
97 97
 		<article class="character">

+ 17
- 6
app/views/character.php View File

@@ -4,18 +4,29 @@ use Aviat\AnimeClient\API\Kitsu;
4 4
 ?>
5 5
 <main class="details fixed">
6 6
 	<section class="flex flex-no-wrap">
7
-		<div>
8
-			<picture class="cover">
7
+		<aside class="info cover">
8
+			<picture>
9 9
 				<source srcset="<?= $urlGenerator->assetUrl("images/characters/{$data[0]['id']}-original.webp") ?>" type="image/webp">
10 10
 				<source srcset="<?= $urlGenerator->assetUrl("images/characters/{$data[0]['id']}-original.jpg") ?>" type="image/jpeg">
11 11
 				<img src="<?= $urlGenerator->assetUrl("images/characters/{$data[0]['id']}-original.jpg") ?>" alt="" />
12 12
 			</picture>
13
-		</div>
14
-		<div>
15
-			<h2><?= $data[0]['attributes']['name'] ?></h2>
13
+			<?php if ( ! empty($data[0]['attributes']['otherNames'])): ?>
14
+				<h3>Nicknames / Other names</h3>
15
+				<?php foreach ($data[0]['attributes']['otherNames'] as $name): ?>
16
+					<h4><?= $name ?></h4>
17
+				<?php endforeach ?>
18
+			<?php endif ?>
19
+		</aside>
20
+		<article class="text">
21
+			<h2><?= $data['name'] ?></h2>
22
+			<?php foreach ($data['names'] as $name): ?>
23
+				<h3><?= $name ?></h3>
24
+			<?php endforeach ?>
25
+
26
+			<hr />
16 27
 
17 28
 			<p class="description"><?= $data[0]['attributes']['description'] ?></p>
18
-		</div>
29
+		</article>
19 30
 	</section>
20 31
 
21 32
 	<?php if (array_key_exists('anime', $data['included']) || array_key_exists('manga', $data['included'])): ?>

+ 5
- 5
app/views/manga/details.php View File

@@ -1,6 +1,6 @@
1 1
 <main class="details fixed">
2 2
 	<section class="flex flex-no-wrap">
3
-		<div>
3
+		<aside class="info">
4 4
 			<picture class="cover">
5 5
 				<source srcset="<?= $urlGenerator->assetUrl("images/manga/{$data['id']}-original.webp") ?>" type="image/webp">
6 6
 				<source srcset="<?= $urlGenerator->assetUrl("images/manga/{$data['id']}-original.jpg") ?>" type="image/jpeg">
@@ -28,8 +28,8 @@
28 28
 					</td>
29 29
 				</tr>
30 30
 			</table>
31
-		</div>
32
-		<div>
31
+		</aside>
32
+		<article class="text">
33 33
 			<h2><a rel="external" href="<?= $data['url'] ?>"><?= $data['title'] ?></a></h2>
34 34
 			<?php foreach($data['titles'] as $title): ?>
35 35
 				<h3><?= $title ?></h3>
@@ -37,12 +37,12 @@
37 37
 
38 38
 			<br />
39 39
 			<p><?= nl2br($data['synopsis']) ?></p>
40
-		</div>
40
+		</article>
41 41
 	</section>
42 42
 
43 43
 	<?php if (count($characters) > 0): ?>
44 44
 	<h2>Characters</h2>
45
-	<section class="media-wrap">
45
+	<section class="media-wrap flex flex-wrap flex-justify-start">
46 46
 	<?php foreach($characters as $id => $char): ?>
47 47
 		<?php if ( ! empty($char['image']['original'])): ?>
48 48
 		<article class="character">

+ 62
- 0
app/views/person/character-mapping.php View File

@@ -0,0 +1,62 @@
1
+<?php
2
+use function Aviat\AnimeClient\getLocalImg;
3
+use Aviat\AnimeClient\API\Kitsu;
4
+?>
5
+<?php foreach ($entries as $type => $casting): ?>
6
+	<?php if($type === 'characters'): ?>
7
+	<table class="min-table">
8
+		<tr>
9
+			<th>Character</th>
10
+			<th>Series</th>
11
+		</tr>
12
+		<?php foreach ($casting as $cid => $character): ?>
13
+			<tr>
14
+				<td style="width:229px">
15
+					<article class="character">
16
+						<?php
17
+						$link = $url->generate('character', ['slug' => $character['character']['slug']]);
18
+						?>
19
+						<a href="<?= $link ?>">
20
+							<?php $imgPath = ($character['character']['image'] === NULL)
21
+								? $urlGenerator->assetUrl('images/characters/empty.png')
22
+								: $urlGenerator->assetUrl(getLocalImg($character['character']['image']['original']));
23
+							?>
24
+							<img src="<?= $imgPath ?>" alt="" />
25
+							<div class="name">
26
+								<?= $character['character']['canonicalName'] ?>
27
+							</div>
28
+						</a>
29
+					</article>
30
+				</td>
31
+				<td>
32
+					<section class="align_left media-wrap">
33
+						<?php foreach ($character['media'] as $sid => $series): ?>
34
+							<article class="media">
35
+								<?php
36
+								$link = $url->generate('anime.details', ['id' => $series['slug']]);
37
+								$titles = Kitsu::filterTitles($series);
38
+								?>
39
+								<a href="<?= $link ?>">
40
+									<img
41
+										src="<?= $urlGenerator->assetUrl("images/anime/{$sid}.jpg") ?>"
42
+										width="220" alt=""
43
+									/>
44
+								</a>
45
+								<div class="name">
46
+									<a href="<?= $link ?>">
47
+										<?= array_shift($titles) ?>
48
+										<?php foreach ($titles as $title): ?>
49
+											<br />
50
+											<small><?= $title ?></small>
51
+										<?php endforeach ?>
52
+									</a>
53
+								</div>
54
+							</article>
55
+						<?php endforeach ?>
56
+					</section>
57
+				</td>
58
+			</tr>
59
+		<?php endforeach; ?>
60
+	</table>
61
+	<?php endif ?>
62
+<?php endforeach ?>

app/views/person.php → app/views/person/index.php View File

@@ -1,5 +1,4 @@
1 1
 <?php
2
-use function Aviat\AnimeClient\getLocalImg;
3 2
 use Aviat\AnimeClient\API\Kitsu;
4 3
 ?>
5 4
 <main class="details fixed">
@@ -27,7 +26,11 @@ use Aviat\AnimeClient\API\Kitsu;
27 26
 			<h3>Castings</h3>
28 27
 			<?php foreach ($castings as $role => $entries): ?>
29 28
 				<h4><?= $role ?></h4>
29
+				<?php if($role === 'Voice Actor'): ?>
30
+					<?php include 'character-mapping.php' ?>
31
+				<?php else: ?>
30 32
 				<?php foreach ($entries as $type => $casting): ?>
33
+					<?php if ($type === 'characters') continue; ?>
31 34
 					<?php if ( ! empty($entries['manga'])): ?>
32 35
 					<h5><?= ucfirst($type) ?></h5>
33 36
 					<?php endif ?>
@@ -68,6 +71,7 @@ use Aviat\AnimeClient\API\Kitsu;
68 71
 					</section>
69 72
 					<br />
70 73
 				<?php endforeach ?>
74
+				<?php endif ?>
71 75
 			<?php endforeach ?>
72 76
 		<?php endif ?>
73 77
 	</section>

+ 1
- 0
composer.json View File

@@ -24,6 +24,7 @@
24 24
 	"aura/session": "^2.0",
25 25
 	"aviat/banker": "^1.0.0",
26 26
 	"aviat/ion": "^2.4.1",
27
+	"ext-iconv": "*",
27 28
 	"ext-json": "*",
28 29
 	"ext-gd":"*",
29 30
 	"ext-pdo": "*",

+ 2
- 0
index.php View File

@@ -20,6 +20,8 @@ use Aviat\AnimeClient\Types\Config as ConfigType;
20 20
 
21 21
 use function Aviat\Ion\_dir;
22 22
 
23
+setlocale(LC_CTYPE, 'en_US');
24
+
23 25
 // Work around the silly timezone error
24 26
 $timezone = ini_get('date.timezone');
25 27
 if ($timezone === '' || $timezone === FALSE)

+ 1
- 1
public/css/app.min.css
File diff suppressed because it is too large
View File


+ 29
- 6
public/css/base.css View File

@@ -91,6 +91,10 @@ a:hover, a:active {
91 91
 	flex-wrap: nowrap
92 92
 }
93 93
 
94
+.flex-align-start {
95
+	align-content: flex-start;
96
+}
97
+
94 98
 .flex-align-end {
95 99
 	align-items: flex-end
96 100
 }
@@ -99,6 +103,10 @@ a:hover, a:active {
99 103
 	align-content: space-around
100 104
 }
101 105
 
106
+.flex-justify-start {
107
+	justify-content: flex-start;
108
+}
109
+
102 110
 .flex-justify-space-around {
103 111
 	justify-content: space-around
104 112
 }
@@ -107,6 +115,10 @@ a:hover, a:active {
107 115
 	align-self: center
108 116
 }
109 117
 
118
+.flex-space-evenly {
119
+	justify-content: space-evenly;
120
+}
121
+
110 122
 .flex {
111 123
 	display: flex
112 124
 }
@@ -671,7 +683,13 @@ picture.cover {
671 683
 }
672 684
 
673 685
 .fixed {
674
-	max-width: 93rem;
686
+	max-width: 100rem;
687
+	/* max-width: 80%; */
688
+	margin: 0 auto;
689
+}
690
+
691
+.fixed .text {
692
+	max-width: 600px;
675 693
 }
676 694
 
677 695
 .details .cover {
@@ -684,14 +702,10 @@ picture.cover {
684 702
 	margin-top: 0;
685 703
 }
686 704
 
687
-.details .flex > div {
705
+.details .flex > * {
688 706
 	margin: 1rem;
689 707
 }
690 708
 
691
-.details .media_details {
692
-	max-width: 300px;
693
-}
694
-
695 709
 .details .media_details td {
696 710
 	padding: 0 1.5rem;
697 711
 }
@@ -752,6 +766,15 @@ picture.cover {
752 766
 	margin-left: 0;
753 767
 }
754 768
 
769
+aside.info {
770
+	max-width: 390px;
771
+}
772
+
773
+aside.info picture, aside.info img {
774
+	display: block;
775
+	margin: 0 auto;
776
+}
777
+
755 778
 /* ----------------------------------------------------------------------------
756 779
 	User page styles
757 780
 -----------------------------------------------------------------------------*/

BIN
public/images/placeholder.png View File


+ 31
- 26
src/API/JsonAPI.php View File

@@ -101,27 +101,28 @@ final class JsonAPI {
101 101
 
102 102
 							continue;
103 103
 						}
104
+
104 105
 						// Single data item
105
-						else if (array_key_exists('id', $props['data']))
106
+						if (array_key_exists('id', $props['data']))
106 107
 						{
107 108
 							$idKey = $props['data']['id'];
108
-							$typeKey = $props['data']['type'];
109
+							$dataType = $props['data']['type'];
109 110
 							$relationship =& $item['relationships'][$relType];
110 111
 							unset($relationship['data']);
111 112
 
112
-							if (in_array($relType, $singular))
113
+							if (\in_array($relType, $singular, TRUE))
113 114
 							{
114
-								$relationship = $included[$typeKey][$idKey];
115
+								$relationship = $included[$dataType][$idKey];
115 116
 								continue;
116 117
 							}
117 118
 
118
-							if ($relType === $typeKey)
119
+							if ($relType === $dataType)
119 120
 							{
120
-								$relationship[$idKey] = $included[$typeKey][$idKey];
121
+								$relationship[$idKey] = $included[$dataType][$idKey];
121 122
 								continue;
122 123
 							}
123 124
 
124
-							$relationship[$typeKey][$idKey] = $included[$typeKey][$idKey];
125
+							$relationship[$dataType][$idKey] = $included[$dataType][$idKey];
125 126
 						}
126 127
 						// Multiple data items
127 128
 						else
@@ -129,16 +130,16 @@ final class JsonAPI {
129 130
 							foreach($props['data'] as $j => $datum)
130 131
 							{
131 132
 								$idKey = $props['data'][$j]['id'];
132
-								$typeKey = $props['data'][$j]['type'];
133
+								$dataType = $props['data'][$j]['type'];
133 134
 								$relationship =& $item['relationships'][$relType];
134 135
 
135
-								if ($relType === $typeKey)
136
+								if ($relType === $dataType)
136 137
 								{
137
-									$relationship[$idKey] = $included[$typeKey][$idKey];
138
+									$relationship[$idKey] = $included[$dataType][$idKey];
138 139
 									continue;
139 140
 								}
140 141
 
141
-								$relationship[$typeKey][$idKey][$j] = $included[$typeKey][$idKey];
142
+								$relationship[$dataType][$idKey][$j] = $included[$dataType][$idKey];
142 143
 							}
143 144
 						}
144 145
 					}
@@ -201,29 +202,33 @@ final class JsonAPI {
201 202
 		{
202 203
 			foreach($items as $id => $item)
203 204
 			{
204
-				if (array_key_exists('relationships', $item) && is_array($item['relationships']))
205
+				if (array_key_exists('relationships', $item) && \is_array($item['relationships']))
205 206
 				{
206 207
 					foreach($item['relationships'] as $relType => $props)
207 208
 					{
208
-						if (array_key_exists('data', $props) && is_array($props['data']) && array_key_exists('id', $props['data']))
209
+						if (array_key_exists('data', $props) && \is_array($props['data']) && array_key_exists('id', $props['data']))
209 210
 						{
210
-							if (array_key_exists($props['data']['id'], $organized[$props['data']['type']]))
211
-							{
212
-								$idKey = $props['data']['id'];
213
-								$typeKey = $props['data']['type'];
211
+							$idKey = $props['data']['id'];
212
+							$dataType = $props['data']['type'];
214 213
 
214
+							if ( ! array_key_exists($dataType, $organized))
215
+							{
216
+								$organized[$dataType] = [];
217
+							}
215 218
 
216
-								$relationship =& $organized[$type][$id]['relationships'][$relType];
217
-								unset($relationship['links']);
218
-								unset($relationship['data']);
219
+							$relationship =& $organized[$type][$id]['relationships'][$relType];
220
+							unset($relationship['links']);
221
+							unset($relationship['data']);
219 222
 
220
-								if ($relType === $typeKey)
221
-								{
222
-									$relationship[$idKey] = $included[$typeKey][$idKey];
223
-									continue;
224
-								}
223
+							if ($relType === $dataType)
224
+							{
225
+								$relationship[$idKey] = $included[$dataType][$idKey];
226
+								continue;
227
+							}
225 228
 
226
-								$relationship[$typeKey][$idKey] = $organized[$typeKey][$idKey];
229
+							if (array_key_exists($idKey, $organized[$dataType]))
230
+							{
231
+								$relationship[$dataType][$idKey] = $organized[$dataType][$idKey];
227 232
 							}
228 233
 						}
229 234
 					}

+ 20
- 5
src/API/Kitsu/Model.php View File

@@ -229,11 +229,26 @@ final class Model {
229 229
 	 */
230 230
 	public function getPerson(string $id): array
231 231
 	{
232
-		return $this->getRequest("people/{$id}", [
233
-			'query' => [
234
-				'include' => 'castings,castings.media,staff,staff.media,voices'
235
-			],
236
-		]);
232
+		$cacheItem = $this->cache->getItem("kitsu-person-{$id}");
233
+
234
+		if ( ! $cacheItem->isHit())
235
+		{
236
+			$data = $this->getRequest("people/{$id}", [
237
+				'query' => [
238
+					'fields' => [
239
+						'characters' => 'canonicalName,slug,image',
240
+						'anime' => 'canonicalTitle,titles,slug,posterImage',
241
+						'manga' => 'canonicalTitle,titles,slug,posterImage',
242
+					],
243
+					'include' => 'castings.character,castings.media'
244
+				],
245
+			]);
246
+
247
+			$cacheItem->set($data);
248
+			$cacheItem->save();
249
+		}
250
+
251
+		return $cacheItem->get();
237 252
 	}
238 253
 
239 254
 	/**

+ 44
- 4
src/AnimeClient.php View File

@@ -206,14 +206,14 @@ function getLocalImg ($kitsuUrl): string
206 206
 {
207 207
 	if ( ! is_string($kitsuUrl))
208 208
 	{
209
-		return '/404';
209
+		return 'images/404/404.png';
210 210
 	}
211 211
 
212 212
 	$parts = parse_url($kitsuUrl);
213 213
 
214 214
 	if ($parts === FALSE)
215 215
 	{
216
-		return '/404';
216
+		return 'images/404/404.png';
217 217
 	}
218 218
 
219 219
 	$file = basename($parts['path']);
@@ -221,11 +221,51 @@ function getLocalImg ($kitsuUrl): string
221 221
 	$ext = array_pop($fileParts);
222 222
 	$segments = explode('/', trim($parts['path'], '/'));
223 223
 
224
-	// dump($segments);
225
-
226 224
 	$type = $segments[0] === 'users' ? $segments[1] : $segments[0];
227 225
 
228 226
 	$id = $segments[count($segments) - 2];
229 227
 
230 228
 	return implode('/', ['images', $type, "{$id}.{$ext}"]);
229
+}
230
+
231
+/**
232
+ * Create a transparent placeholder image
233
+ *
234
+ * @param string $path
235
+ * @param int $width
236
+ * @param int $height
237
+ * @param string $text
238
+ */
239
+function createPlaceholderImage ($path, $width, $height, $text = 'Image Unavailable')
240
+{
241
+	$width = $width ?? 200;
242
+	$height = $height ?? 200;
243
+
244
+	$img = imagecreate($width, $height);
245
+
246
+	$path = rtrim($path, '/');
247
+
248
+	// Background is the first color by default
249
+	imagecolorallocatealpha($img, 255, 255, 255, 127);
250
+	$textColor = imagecolorallocate($img, 64, 64, 64);
251
+
252
+	imagealphablending($img, TRUE);
253
+
254
+	// Generate placeholder text
255
+	$fontSize = 10;
256
+	$fontWidth = imagefontwidth($fontSize);
257
+	$fontHeight = imagefontheight($fontSize);
258
+	$length = strlen($text);
259
+	$textWidth = $length * $fontWidth;
260
+	$fxPos = (int) ceil((imagesx($img) - $textWidth) / 2);
261
+	$fyPos = (int) ceil((imagesy($img) - $fontHeight) / 2);
262
+
263
+	// Add the image text
264
+	imagestring($img, $fontSize, $fxPos, $fyPos, $text, $textColor);
265
+
266
+	// Save the images
267
+	imagesavealpha($img, TRUE);
268
+	imagepng($img, $path . '/placeholder.png', 9);
269
+
270
+	imagedestroy($img);
231 271
 }

+ 8
- 0
src/Controller/Character.php View File

@@ -59,6 +59,14 @@ class Character extends BaseController {
59 59
 
60 60
 		$data = JsonAPI::organizeData($rawData);
61 61
 
62
+		$data['names'] = array_unique(
63
+			array_merge(
64
+				[ $data[0]['attributes']['canonicalName'] ],
65
+				$data[0]['attributes']['names']
66
+			)
67
+		);
68
+		$data['name'] = array_shift($data['names']);
69
+
62 70
 		if (array_key_exists('included', $data))
63 71
 		{
64 72
 			if (array_key_exists('anime', $data['included']))

+ 35
- 4
src/Controller/Index.php View File

@@ -16,6 +16,7 @@
16 16
 
17 17
 namespace Aviat\AnimeClient\Controller;
18 18
 
19
+use function Aviat\AnimeClient\createPlaceholderImage;
19 20
 use function Amp\Promise\wait;
20 21
 
21 22
 use Aviat\AnimeClient\Controller as BaseController;
@@ -269,45 +270,60 @@ final class Index extends BaseController {
269 270
 		$fileName = str_replace('-original', '', $file);
270 271
 		[$id, $ext] = explode('.', basename($fileName));
271 272
 
273
+		$baseSavePath = $this->config->get('img_cache_path');
274
+
272 275
 		$typeMap = [
273 276
 			'anime' => [
274 277
 				'kitsuUrl' => "anime/poster_images/{$id}/medium.{$ext}",
275 278
 				'width' => 220,
279
+				'height' => 312,
276 280
 			],
277 281
 			'avatars' => [
278 282
 				'kitsuUrl' => "users/avatars/{$id}/original.{$ext}",
279 283
 				'width' => null,
284
+				'height' => null,
280 285
 			],
281 286
 			'characters' => [
282 287
 				'kitsuUrl' => "characters/images/{$id}/original.{$ext}",
283 288
 				'width' => 225,
289
+				'height' => 350,
284 290
 			],
285 291
 			'manga' => [
286 292
 				'kitsuUrl' => "manga/poster_images/{$id}/medium.{$ext}",
287 293
 				'width' => 220,
294
+				'height' => 312,
288 295
 			],
289 296
 			'people' => [
290 297
 				'kitsuUrl' => "people/images/{$id}/original.{$ext}",
291 298
 				'width' => null,
299
+				'height' => null,
292 300
 			],
293 301
 		];
294 302
 
295 303
 		if ( ! array_key_exists($type, $typeMap))
296 304
 		{
297
-			$this->notFound();
305
+			$this->getPlaceholder($baseSavePath, 100, 100);
298 306
 			return;
299 307
 		}
300 308
 
301 309
 		$kitsuUrl .= $typeMap[$type]['kitsuUrl'];
302 310
 		$width = $typeMap[$type]['width'];
311
+		$height = $typeMap[$type]['height'];
303 312
 
304 313
 		$promise = (new HummingbirdClient)->request($kitsuUrl);
305 314
 		$response = wait($promise);
306
-		$data = wait($response->getBody());
307 315
 
308
-		// echo "Fetching {$kitsuUrl}\n";
316
+		if ($response->getStatus() !== 200)
317
+		{
318
+			if ($display)
319
+			{
320
+				$this->getPlaceholder("{$baseSavePath}/{$type}", $width, $height);
321
+			}
322
+			return;
323
+		}
324
+
325
+		$data = wait($response->getBody());
309 326
 
310
-		$baseSavePath = $this->config->get('img_cache_path');
311 327
 		$filePrefix = "{$baseSavePath}/{$type}/{$id}";
312 328
 
313 329
 		[$origWidth] = getimagesizefromstring($data);
@@ -371,4 +387,19 @@ final class Index extends BaseController {
371 387
 
372 388
 		return $output;
373 389
 	}
390
+
391
+	private function getPlaceholder (string $path, ?int $width = 200, ?int $height = NULL): void
392
+	{
393
+		$height = $height ?? $width;
394
+
395
+		$filename = $path . '/placeholder.png';
396
+
397
+		if ( ! file_exists($path . '/placeholder.png'))
398
+		{
399
+			createPlaceholderImage($path, $width, $height);
400
+		}
401
+
402
+		header('Content-Type: image/png');
403
+		echo file_get_contents($filename);
404
+	}
374 405
 }

+ 49
- 2
src/Controller/People.php View File

@@ -62,11 +62,12 @@ final class People extends BaseController {
62 62
 
63 63
 		if (array_key_exists('included', $data) && array_key_exists('castings', $data['included']))
64 64
 		{
65
+			$viewData['included'] = $data['included'];
65 66
 			$viewData['castings'] = $this->organizeCast($data['included']['castings']);
66 67
 			$viewData['castCount'] = count($viewData['castings']);
67 68
 		}
68 69
 
69
-		$this->outputHTML('person', $viewData);
70
+		$this->outputHTML('person/index', $viewData);
70 71
 	}
71 72
 
72 73
 	protected function organizeCast(array $cast): array
@@ -82,6 +83,52 @@ final class People extends BaseController {
82 83
 
83 84
 			$roleName = $role['attributes']['role'];
84 85
 			$media = $role['relationships']['media'];
86
+			$chars = $role['relationships']['character']['characters'] ?? [];
87
+
88
+			if ( ! array_key_exists($roleName, $output))
89
+			{
90
+				$output[$roleName] = [
91
+					'characters' => [],
92
+				];
93
+			}
94
+
95
+			if ( ! empty($chars))
96
+			{
97
+				$relatedMedia = [];
98
+
99
+				if (array_key_exists('anime', $media))
100
+				{
101
+					foreach($media['anime'] as $sid => $series)
102
+					{
103
+						$relatedMedia[$sid] = $series['attributes'];
104
+					}
105
+				}
106
+
107
+				foreach($chars as $cid => $character)
108
+				{
109
+					// To make sure all the media are properly associated,
110
+					// merge the found media for this iteration with
111
+					// existing media, making sure to preserve array keys
112
+					$existingMedia = array_key_exists($cid, $output[$roleName]['characters'])
113
+						? $output[$roleName]['characters'][$cid]['media']
114
+						: [];
115
+
116
+					$includedMedia = array_replace_recursive($existingMedia, $relatedMedia);
117
+
118
+					uasort($includedMedia, function ($a, $b) {
119
+						return $a['canonicalTitle'] <=> $b['canonicalTitle'];
120
+					});
121
+
122
+					$output[$roleName]['characters'][$cid] = [
123
+						'character' => $character['attributes'],
124
+						'media' => $includedMedia,
125
+					];
126
+				}
127
+
128
+				uasort($output[$roleName]['characters'], function ($a, $b) {
129
+					return $a['character']['canonicalName'] <=> $b['character']['canonicalName'];
130
+				});
131
+			}
85 132
 
86 133
 			if (array_key_exists('anime', $media))
87 134
 			{
@@ -99,7 +146,7 @@ final class People extends BaseController {
99 146
 				{
100 147
 					$output[$roleName]['manga'][$sid] = $series;
101 148
 				}
102
-				uasort($output[$roleName]['anime'], function ($a, $b) {
149
+				uasort($output[$roleName]['manga'], function ($a, $b) {
103 150
 					return $a['attributes']['canonicalTitle'] <=> $b['attributes']['canonicalTitle'];
104 151
 				});
105 152
 			}