Version 5.1 - All the GraphQL #32
@ -1,21 +0,0 @@
|
||||
test:7.1:
|
||||
stage: test
|
||||
before_script:
|
||||
- sh build/docker_install.sh > /dev/null
|
||||
- apk add --no-cache php7-phpdbg
|
||||
- curl -sS https://getcomposer.org/installer | php
|
||||
- php composer.phar install --ignore-platform-reqs
|
||||
image: php:7.1-alpine
|
||||
script:
|
||||
- phpdbg -qrr -- ./vendor/bin/phpunit --coverage-text --colors=never
|
||||
|
||||
test:7.2:
|
||||
stage: test
|
||||
before_script:
|
||||
- sh build/docker_install.sh > /dev/null
|
||||
- apk add --no-cache php7-phpdbg
|
||||
- curl -sS https://getcomposer.org/installer | php
|
||||
- php composer.phar install --ignore-platform-reqs
|
||||
image: php:7.2-alpine
|
||||
script:
|
||||
- phpdbg -qrr -- ./vendor/bin/phpunit --coverage-text --colors=never
|
@ -7,6 +7,8 @@
|
||||
* Updated console command to sync Kitsu and Anilist data (Kitsu can sync MAL, and MAL's API broke, so MAL sync was removed)
|
||||
* Added page to update settings without having to edit config files
|
||||
* Defaulted to secure (HTTPS) urls
|
||||
* Updated Character pages to show voice actors
|
||||
* Added People pages, showing which works they contributed to, and in what role
|
||||
|
||||
## Version 4
|
||||
* Updated to use Kitsu API after discontinuation of Hummingbird
|
||||
|
@ -174,6 +174,14 @@ return [
|
||||
'slug' => '[a-z0-9\-]+'
|
||||
]
|
||||
],
|
||||
'person' => [
|
||||
'path' => '/people/{id}',
|
||||
'action' => 'index',
|
||||
'params' => [],
|
||||
'tokens' => [
|
||||
'id' => '[a-z0-9\-]+'
|
||||
]
|
||||
],
|
||||
'user_info' => [
|
||||
'path' => '/me',
|
||||
'action' => 'me',
|
||||
|
@ -1,4 +1,7 @@
|
||||
<?php use Aviat\AnimeClient\API\Kitsu; ?>
|
||||
<?php
|
||||
use function Aviat\AnimeClient\getLocalImg;
|
||||
use Aviat\AnimeClient\API\Kitsu;
|
||||
?>
|
||||
<main class="details fixed">
|
||||
<section class="flex flex-no-wrap">
|
||||
<div>
|
||||
@ -89,14 +92,19 @@
|
||||
<th>Cast Member</th>
|
||||
<th>Series</th>
|
||||
</tr>
|
||||
<?php foreach($casting as $c):?>
|
||||
<?php foreach($casting as $cid => $c):?>
|
||||
<tr>
|
||||
<td style="width:229px">
|
||||
<article class="character">
|
||||
<img src="<?= $c['person']['image'] ?>" alt="" />
|
||||
<?php
|
||||
$link = $url->generate('person', ['id' => $c['person']['id']]);
|
||||
?>
|
||||
<a href="<?= $link ?>">
|
||||
<img src="<?= $urlGenerator->assetUrl(getLocalImg($c['person']['image'])) ?>" alt="" />
|
||||
<div class="name">
|
||||
<?= $c['person']['name'] ?>
|
||||
</div>
|
||||
</a>
|
||||
</article>
|
||||
</td>
|
||||
<td>
|
||||
@ -108,7 +116,7 @@
|
||||
$titles = Kitsu::filterTitles($series['attributes']);
|
||||
?>
|
||||
<a href="<?= $link ?>">
|
||||
<img src="<?= $series['attributes']['posterImage']['small'] ?>" width="220" alt="" />
|
||||
<img src="<?= $urlGenerator->assetUrl(getLocalImg($series['attributes']['posterImage']['small'])) ?>" width="220" alt="" />
|
||||
</a>
|
||||
<div class="name">
|
||||
<a href="<?= $link ?>">
|
||||
|
74
app/views/person.php
Normal file
74
app/views/person.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
use function Aviat\AnimeClient\getLocalImg;
|
||||
use Aviat\AnimeClient\API\Kitsu;
|
||||
?>
|
||||
<main class="details fixed">
|
||||
<section class="flex flex-no-wrap">
|
||||
<div>
|
||||
<picture class="cover">
|
||||
<source
|
||||
srcset="<?= $urlGenerator->assetUrl("images/people/{$data['id']}-original.webp") ?>"
|
||||
type="image/webp"
|
||||
>
|
||||
<source
|
||||
srcset="<?= $urlGenerator->assetUrl("images/people/{$data['id']}-original.jpg") ?>"
|
||||
type="image/jpeg"
|
||||
>
|
||||
<img src="<?= $urlGenerator->assetUrl("images/people/{$data['id']}-original.jpg") ?>" alt="" />
|
||||
</picture>
|
||||
</div>
|
||||
<div>
|
||||
<h2><?= $data['attributes']['name'] ?></h2>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<?php if ($castCount > 0): ?>
|
||||
<h3>Castings</h3>
|
||||
<?php foreach ($castings as $role => $entries): ?>
|
||||
<h4><?= $role ?></h4>
|
||||
<?php foreach ($entries as $type => $casting): ?>
|
||||
<?php if ( ! empty($entries['manga'])): ?>
|
||||
<h5><?= ucfirst($type) ?></h5>
|
||||
<?php endif ?>
|
||||
<section class="align_left media-wrap">
|
||||
<?php foreach ($casting as $sid => $series): ?>
|
||||
<article class="media">
|
||||
<?php
|
||||
$link = $url->generate('anime.details', ['id' => $series['attributes']['slug']]);
|
||||
$titles = Kitsu::filterTitles($series['attributes']);
|
||||
?>
|
||||
<a href="<?= $link ?>">
|
||||
<picture>
|
||||
<source
|
||||
srcset="<?= $urlGenerator->assetUrl("images/{$type}/{$sid}.webp") ?>"
|
||||
type="image/webp"
|
||||
/>
|
||||
<source
|
||||
srcset="<?= $urlGenerator->assetUrl("images/{$type}/{$sid}.jpg") ?>"
|
||||
type="image/jpeg"
|
||||
/>
|
||||
<img
|
||||
src="<?= $urlGenerator->assetUrl("images/{$type}/{$sid}.jpg") ?>"
|
||||
width="220" alt=""
|
||||
/>
|
||||
</picture>
|
||||
</a>
|
||||
<div class="name">
|
||||
<a href="<?= $link ?>">
|
||||
<?= array_shift($titles) ?>
|
||||
<?php foreach ($titles as $title): ?>
|
||||
<br />
|
||||
<small><?= $title ?></small>
|
||||
<?php endforeach ?>
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
<?php endforeach; ?>
|
||||
</section>
|
||||
<br />
|
||||
<?php endforeach ?>
|
||||
<?php endforeach ?>
|
||||
<?php endif ?>
|
||||
</section>
|
||||
</main>
|
@ -61,6 +61,11 @@ final class JsonAPI {
|
||||
// Inline organized data
|
||||
foreach($data['data'] as $i => &$item)
|
||||
{
|
||||
if ( ! is_array($item))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (array_key_exists('relationships', $item))
|
||||
{
|
||||
foreach($item['relationships'] as $relType => $props)
|
||||
|
@ -103,7 +103,7 @@ final class ListItem implements ListItemInterface {
|
||||
|
||||
$request = $this->requestBuilder->newRequest('GET', "library-entries/{$id}")
|
||||
->setQuery([
|
||||
'include' => 'media,media.genres,media.mappings'
|
||||
'include' => 'media,media.categories,media.mappings'
|
||||
]);
|
||||
|
||||
if ($authHeader !== FALSE)
|
||||
|
@ -221,6 +221,21 @@ final class Model {
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about a person
|
||||
*
|
||||
* @param string $id
|
||||
* @return array
|
||||
*/
|
||||
public function getPerson(string $id): array
|
||||
{
|
||||
return $this->getRequest("people/{$id}", [
|
||||
'query' => [
|
||||
'include' => 'castings,castings.media,staff,staff.media,voices'
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get profile information for the configured user
|
||||
*
|
||||
@ -585,7 +600,7 @@ final class Model {
|
||||
}
|
||||
|
||||
$transformed = $this->mangaTransformer->transform($baseData);
|
||||
$transformed['included'] = $baseData['included'];
|
||||
$transformed['included'] = JsonAPI::organizeIncluded($baseData['included']);
|
||||
return $transformed;
|
||||
}
|
||||
|
||||
@ -936,8 +951,8 @@ final class Model {
|
||||
'characters' => 'slug,name,image'
|
||||
],
|
||||
'include' => ($type === 'anime')
|
||||
? 'categories,mappings,streamingLinks,animeCharacters.character'
|
||||
: 'categories,mappings,mangaCharacters.character,castings.character',
|
||||
? 'staff,staff.person,categories,mappings,streamingLinks,animeCharacters.character'
|
||||
: 'staff,staff.person,categories,mappings,mangaCharacters.character,castings.character',
|
||||
]
|
||||
];
|
||||
|
||||
|
@ -19,6 +19,10 @@ namespace Aviat\AnimeClient;
|
||||
use Aviat\Ion\ConfigInterface;
|
||||
use Yosymfony\Toml\{Toml, TomlBuilder};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
//! TOML Functions
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Load configuration options from .toml files
|
||||
*
|
||||
@ -67,30 +71,6 @@ function loadTomlFile(string $filename): array
|
||||
return Toml::parseFile($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the array sequential, not associative?
|
||||
*
|
||||
* @param mixed $array
|
||||
* @return bool
|
||||
*/
|
||||
function isSequentialArray($array): bool
|
||||
{
|
||||
if ( ! is_array($array))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
foreach ($array as $k => $v)
|
||||
{
|
||||
if ($k !== $i++)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
function _iterateToml(TomlBuilder $builder, $data, $parentKey = NULL): void
|
||||
{
|
||||
foreach ($data as $key => $value)
|
||||
@ -147,6 +127,34 @@ function tomlToArray(string $toml): array
|
||||
return Toml::parse($toml);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
//! Misc Functions
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Is the array sequential, not associative?
|
||||
*
|
||||
* @param mixed $array
|
||||
* @return bool
|
||||
*/
|
||||
function isSequentialArray($array): bool
|
||||
{
|
||||
if ( ! is_array($array))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
foreach ($array as $k => $v)
|
||||
{
|
||||
if ($k !== $i++)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that folder permissions are correct for proper operation
|
||||
*
|
||||
@ -187,3 +195,37 @@ function checkFolderPermissions(ConfigInterface $config): array
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the path for the cached image from the original iamge
|
||||
*
|
||||
* @param string $kitsuUrl
|
||||
* @return string
|
||||
*/
|
||||
function getLocalImg ($kitsuUrl): string
|
||||
{
|
||||
if ( ! is_string($kitsuUrl))
|
||||
{
|
||||
return '/404';
|
||||
}
|
||||
|
||||
$parts = parse_url($kitsuUrl);
|
||||
|
||||
if ($parts === FALSE)
|
||||
{
|
||||
return '/404';
|
||||
}
|
||||
|
||||
$file = basename($parts['path']);
|
||||
$fileParts = explode('.', $file);
|
||||
$ext = array_pop($fileParts);
|
||||
$segments = explode('/', trim($parts['path'], '/'));
|
||||
|
||||
// dump($segments);
|
||||
|
||||
$type = $segments[0] === 'users' ? $segments[1] : $segments[0];
|
||||
|
||||
$id = $segments[count($segments) - 2];
|
||||
|
||||
return implode('/', ['images', $type, "{$id}.{$ext}"]);
|
||||
}
|
@ -16,20 +16,8 @@
|
||||
|
||||
namespace Aviat\AnimeClient\Command;
|
||||
|
||||
use Aviat\AnimeClient\API\{
|
||||
FailedResponseException,
|
||||
JsonAPI,
|
||||
ParallelAPIRequest
|
||||
};
|
||||
use Aviat\AnimeClient\API\Anilist\Transformer\{
|
||||
AnimeListTransformer as AALT,
|
||||
MangaListTransformer as AMLT
|
||||
};
|
||||
use Aviat\AnimeClient\API\Mapping\{AnimeWatchingStatus, MangaReadingStatus};
|
||||
use Aviat\AnimeClient\API\JsonAPI;
|
||||
use Aviat\AnimeClient\Controller\Index;
|
||||
use Aviat\AnimeClient\Types\FormItem;
|
||||
use Aviat\Ion\Json;
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* Clears out image cache directories, then re-creates the image cache
|
||||
@ -69,9 +57,11 @@ final class UpdateThumbnails extends BaseCommand {
|
||||
{
|
||||
$this->controller->images($type, "{$id}.jpg", FALSE);
|
||||
}
|
||||
|
||||
$this->echoBox("Finished regenerating {$type} thumbnails");
|
||||
}
|
||||
|
||||
$this->echoBox('Finished regenerating thumbnails');
|
||||
$this->echoBox('Finished regenerating all thumbnails');
|
||||
}
|
||||
|
||||
public function clearThumbs()
|
||||
|
@ -275,10 +275,11 @@ final class Anime extends BaseController {
|
||||
*/
|
||||
public function details(string $animeId): void
|
||||
{
|
||||
$show_data = $this->model->getAnime($animeId);
|
||||
$data = $this->model->getAnime($animeId);
|
||||
$characters = [];
|
||||
$staff = [];
|
||||
|
||||
if ($show_data->title === '')
|
||||
if ($data->title === '')
|
||||
{
|
||||
$this->notFound(
|
||||
$this->config->get('whose_list') .
|
||||
@ -290,22 +291,56 @@ final class Anime extends BaseController {
|
||||
return;
|
||||
}
|
||||
|
||||
if (array_key_exists('characters', $show_data['included']))
|
||||
if (array_key_exists('characters', $data['included']))
|
||||
{
|
||||
foreach($show_data['included']['characters'] as $id => $character)
|
||||
|
||||
|
||||
foreach($data['included']['characters'] as $id => $character)
|
||||
{
|
||||
$characters[$id] = $character['attributes'];
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists('mediaStaff', $data['included']))
|
||||
{
|
||||
foreach ($data['included']['mediaStaff'] as $id => $person)
|
||||
{
|
||||
$personDetails = [];
|
||||
foreach ($person['relationships']['person']['people'] as $p)
|
||||
{
|
||||
$personDetails = $p['attributes'];
|
||||
}
|
||||
|
||||
$role = $person['attributes']['role'];
|
||||
|
||||
if ( ! array_key_exists($role, $staff))
|
||||
{
|
||||
$staff[$role] = [];
|
||||
}
|
||||
|
||||
$staff[$role][$id] = [
|
||||
'name' => $personDetails['name'] ?? '??',
|
||||
'image' => $personDetails['image'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
uasort($characters, function ($a, $b) {
|
||||
return $a['name'] <=> $b['name'];
|
||||
});
|
||||
|
||||
// dump($characters);
|
||||
// dump($staff);
|
||||
|
||||
$this->outputHTML('anime/details', [
|
||||
'title' => $this->formatTitle(
|
||||
$this->config->get('whose_list') . "'s Anime List",
|
||||
'Anime',
|
||||
$show_data->title
|
||||
$data->title
|
||||
),
|
||||
'characters' => $characters,
|
||||
'show_data' => $show_data,
|
||||
'show_data' => $data,
|
||||
'staff' => $staff,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
namespace Aviat\AnimeClient\Controller;
|
||||
|
||||
use function Aviat\AnimeClient\getLocalImg;
|
||||
|
||||
use Aviat\AnimeClient\Controller as BaseController;
|
||||
use Aviat\AnimeClient\API\JsonAPI;
|
||||
use Aviat\Ion\ArrayWrapper;
|
||||
@ -23,7 +25,7 @@ use Aviat\Ion\ArrayWrapper;
|
||||
/**
|
||||
* Controller for character description pages
|
||||
*/
|
||||
final class Character extends BaseController {
|
||||
class Character extends BaseController {
|
||||
|
||||
use ArrayWrapper;
|
||||
|
||||
@ -57,6 +59,23 @@ final class Character extends BaseController {
|
||||
|
||||
$data = JsonAPI::organizeData($rawData);
|
||||
|
||||
if (array_key_exists('included', $data))
|
||||
{
|
||||
if (array_key_exists('anime', $data['included']))
|
||||
{
|
||||
uasort($data['included']['anime'], function ($a, $b) {
|
||||
return $a['attributes']['canonicalTitle'] <=> $b['attributes']['canonicalTitle'];
|
||||
});
|
||||
}
|
||||
|
||||
if (array_key_exists('manga', $data['included']))
|
||||
{
|
||||
uasort($data['included']['manga'], function ($a, $b) {
|
||||
return $a['attributes']['canonicalTitle'] <=> $b['attributes']['canonicalTitle'];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$viewData = [
|
||||
'title' => $this->formatTitle(
|
||||
'Characters',
|
||||
@ -67,11 +86,14 @@ final class Character extends BaseController {
|
||||
'castings' => []
|
||||
];
|
||||
|
||||
if (array_key_exists('included', $data) && array_key_exists('castings', $data['included']))
|
||||
if (array_key_exists('included', $data))
|
||||
{
|
||||
if (array_key_exists('castings', $data['included']))
|
||||
{
|
||||
$viewData['castings'] = $this->organizeCast($data['included']['castings']);
|
||||
$viewData['castCount'] = $this->getCastCount($viewData['castings']);
|
||||
}
|
||||
}
|
||||
|
||||
$this->outputHTML('character', $viewData);
|
||||
}
|
||||
@ -121,25 +143,26 @@ final class Character extends BaseController {
|
||||
return $output;
|
||||
}
|
||||
|
||||
private function getCastCount(array $cast): int
|
||||
protected function getCastCount(array $cast): int
|
||||
{
|
||||
$count = 0;
|
||||
|
||||
foreach($cast as $role)
|
||||
{
|
||||
if (
|
||||
$count++;
|
||||
/* if (
|
||||
array_key_exists('attributes', $role) &&
|
||||
array_key_exists('role', $role['attributes']) &&
|
||||
$role['attributes']['role'] !== NULL
|
||||
) {
|
||||
$count++;
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
private function organizeCast(array $cast): array
|
||||
protected function organizeCast(array $cast): array
|
||||
{
|
||||
$cast = $this->dedupeCast($cast);
|
||||
$output = [];
|
||||
@ -157,8 +180,19 @@ final class Character extends BaseController {
|
||||
|
||||
if ($isVA)
|
||||
{
|
||||
$person = current($role['relationships']['person']['people'])['attributes'];
|
||||
$name = $person['name'];
|
||||
foreach($role['relationships']['person']['people'] as $pid => $peoples)
|
||||
{
|
||||
$p = $peoples;
|
||||
}
|
||||
|
||||
$person = $p['attributes'];
|
||||
$person['id'] = $pid;
|
||||
$person['image'] = $person['image']['original'];
|
||||
|
||||
uasort($role['relationships']['media']['anime'], function ($a, $b) {
|
||||
return $a['attributes']['canonicalTitle'] <=> $b['attributes']['canonicalTitle'];
|
||||
});
|
||||
|
||||
$item = [
|
||||
'person' => $person,
|
||||
'series' => $role['relationships']['media']['anime']
|
||||
@ -168,7 +202,11 @@ final class Character extends BaseController {
|
||||
}
|
||||
else
|
||||
{
|
||||
$output[$roleName][] = $role['relationships']['person']['people'];
|
||||
foreach($role['relationships']['person']['people'] as $pid => $person)
|
||||
{
|
||||
$person['id'] = $pid;
|
||||
$output[$roleName][$pid] = $person;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,36 +268,45 @@ final class Index extends BaseController {
|
||||
$kitsuUrl = 'https://media.kitsu.io/';
|
||||
$fileName = str_replace('-original', '', $file);
|
||||
[$id, $ext] = explode('.', basename($fileName));
|
||||
switch ($type)
|
||||
|
||||
$typeMap = [
|
||||
'anime' => [
|
||||
'kitsuUrl' => "anime/poster_images/{$id}/medium.{$ext}",
|
||||
'width' => 220,
|
||||
],
|
||||
'avatars' => [
|
||||
'kitsuUrl' => "users/avatars/{$id}/original.{$ext}",
|
||||
'width' => null,
|
||||
],
|
||||
'characters' => [
|
||||
'kitsuUrl' => "characters/images/{$id}/original.{$ext}",
|
||||
'width' => 225,
|
||||
],
|
||||
'manga' => [
|
||||
'kitsuUrl' => "manga/poster_images/{$id}/medium.{$ext}",
|
||||
'width' => 220,
|
||||
],
|
||||
'people' => [
|
||||
'kitsuUrl' => "people/images/{$id}/original.{$ext}",
|
||||
'width' => null,
|
||||
],
|
||||
];
|
||||
|
||||
if ( ! array_key_exists($type, $typeMap))
|
||||
{
|
||||
case 'anime':
|
||||
$kitsuUrl .= "anime/poster_images/{$id}/small.jpg";
|
||||
$width = 220;
|
||||
break;
|
||||
|
||||
case 'avatars':
|
||||
$kitsuUrl .= "users/avatars/{$id}/original.jpg";
|
||||
break;
|
||||
|
||||
case 'manga':
|
||||
$kitsuUrl .= "manga/poster_images/{$id}/small.jpg";
|
||||
$width = 220;
|
||||
break;
|
||||
|
||||
case 'characters':
|
||||
$kitsuUrl .= "characters/images/{$id}/original.jpg";
|
||||
$width = 225;
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->notFound();
|
||||
return;
|
||||
}
|
||||
|
||||
$kitsuUrl .= $typeMap[$type]['kitsuUrl'];
|
||||
$width = $typeMap[$type]['width'];
|
||||
|
||||
$promise = (new HummingbirdClient)->request($kitsuUrl);
|
||||
$response = wait($promise);
|
||||
$data = wait($response->getBody());
|
||||
|
||||
// echo "Fetching {$kitsuUrl}\n";
|
||||
|
||||
$baseSavePath = $this->config->get('img_cache_path');
|
||||
$filePrefix = "{$baseSavePath}/{$type}/{$id}";
|
||||
|
||||
|
@ -280,6 +280,7 @@ final class Manga extends Controller {
|
||||
public function details($manga_id): void
|
||||
{
|
||||
$data = $this->model->getManga($manga_id);
|
||||
$staff = [];
|
||||
$characters = [];
|
||||
|
||||
if (empty($data))
|
||||
@ -293,14 +294,44 @@ final class Manga extends Controller {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach($data['included'] as $included)
|
||||
if (array_key_exists('characters', $data['included']))
|
||||
{
|
||||
if ($included['type'] === 'characters')
|
||||
foreach ($data['included']['characters'] as $id => $character)
|
||||
{
|
||||
$characters[$included['id']] = $included['attributes'];
|
||||
$characters[$id] = $character['attributes'];
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists('mediaStaff', $data['included']))
|
||||
{
|
||||
foreach ($data['included']['mediaStaff'] as $id => $person)
|
||||
{
|
||||
$personDetails = [];
|
||||
foreach ($person['relationships']['person']['people'] as $p)
|
||||
{
|
||||
$personDetails = $p['attributes'];
|
||||
}
|
||||
|
||||
$role = $person['attributes']['role'];
|
||||
|
||||
if ( ! array_key_exists($role, $staff))
|
||||
{
|
||||
$staff[$role] = [];
|
||||
}
|
||||
|
||||
$staff[$role][$id] = [
|
||||
'name' => $personDetails['name'] ?? '??',
|
||||
'image' => $personDetails['image'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
uasort($characters, function ($a, $b) {
|
||||
return $a['name'] <=> $b['name'];
|
||||
});
|
||||
|
||||
// dump($staff);
|
||||
|
||||
$this->outputHTML('manga/details', [
|
||||
'title' => $this->formatTitle(
|
||||
$this->config->get('whose_list') . "'s Manga List",
|
||||
@ -309,6 +340,7 @@ final class Manga extends Controller {
|
||||
),
|
||||
'characters' => $characters,
|
||||
'data' => $data,
|
||||
'staff' => $staff,
|
||||
]);
|
||||
}
|
||||
|
||||
|
110
src/Controller/People.php
Normal file
110
src/Controller/People.php
Normal file
@ -0,0 +1,110 @@
|
||||
<?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 Aviat\AnimeClient\Controller as BaseController;
|
||||
use Aviat\AnimeClient\API\JsonAPI;
|
||||
|
||||
/**
|
||||
* Controller for People pages
|
||||
*/
|
||||
final class People extends BaseController {
|
||||
/**
|
||||
* Show information about a person
|
||||
*
|
||||
* @param string $id
|
||||
* @return void
|
||||
*/
|
||||
public function index(string $id): void
|
||||
{
|
||||
$model = $this->container->get('kitsu-model');
|
||||
|
||||
$rawData = $model->getPerson($id);
|
||||
|
||||
if (( ! array_key_exists('data', $rawData)) || empty($rawData['data']))
|
||||
{
|
||||
$this->notFound(
|
||||
$this->formatTitle(
|
||||
'People',
|
||||
'Person not found'
|
||||
),
|
||||
'Person Not Found'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$data = JsonAPI::organizeData($rawData);
|
||||
|
||||
$viewData = [
|
||||
'title' => $this->formatTitle(
|
||||
'People',
|
||||
$data['attributes']['name']
|
||||
),
|
||||
'data' => $data,
|
||||
'castCount' => 0,
|
||||
'castings' => []
|
||||
];
|
||||
|
||||
if (array_key_exists('included', $data) && array_key_exists('castings', $data['included']))
|
||||
{
|
||||
$viewData['castings'] = $this->organizeCast($data['included']['castings']);
|
||||
$viewData['castCount'] = count($viewData['castings']);
|
||||
}
|
||||
|
||||
$this->outputHTML('person', $viewData);
|
||||
}
|
||||
|
||||
protected function organizeCast(array $cast): array
|
||||
{
|
||||
$output = [];
|
||||
|
||||
foreach ($cast as $id => $role)
|
||||
{
|
||||
if (empty($role['attributes']['role']))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$roleName = $role['attributes']['role'];
|
||||
$media = $role['relationships']['media'];
|
||||
|
||||
if (array_key_exists('anime', $media))
|
||||
{
|
||||
foreach($media['anime'] as $sid => $series)
|
||||
{
|
||||
$output[$roleName]['anime'][$sid] = $series;
|
||||
}
|
||||
uasort($output[$roleName]['anime'], function ($a, $b) {
|
||||
return $a['attributes']['canonicalTitle'] <=> $b['attributes']['canonicalTitle'];
|
||||
});
|
||||
}
|
||||
else if (array_key_exists('manga', $media))
|
||||
{
|
||||
foreach ($media['manga'] as $sid => $series)
|
||||
{
|
||||
$output[$roleName]['manga'][$sid] = $series;
|
||||
}
|
||||
uasort($output[$roleName]['anime'], function ($a, $b) {
|
||||
return $a['attributes']['canonicalTitle'] <=> $b['attributes']['canonicalTitle'];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user