Update profile page to use GraphQL, see #27
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details

This commit is contained in:
Timothy Warren 2020-08-24 15:20:07 -04:00
parent 26a1c464a1
commit ba276cc86e
11 changed files with 215 additions and 106 deletions

View File

@ -50,6 +50,18 @@ use function Aviat\AnimeClient\getLocalImg;
</td>
</tr>
<?php endif ?>
<?php if (count($data['links']) > 0): ?>
<tr>
<td>External Links</td>
<td>
<?php foreach ($data['links'] as $urlName => $externalUrl): ?>
<a rel='external' href="<?= $externalUrl ?>"><?= $urlName ?></a><br />
<?php endforeach ?>
</td>
</tr>
<?php endif ?>
<tr>
<td>Genres</td>
<td>
@ -58,11 +70,13 @@ use function Aviat\AnimeClient\getLocalImg;
</tr>
</table>
<br />
</aside>
<article class="text">
<h2 class="toph"><a rel="external" href="<?= $data['url'] ?>"><?= $data['title'] ?></a></h2>
<h2 class="toph"><?= $data['title'] ?></h2>
<?php foreach ($data['titles_more'] as $title): ?>
<h3><?= $title ?></h3>
<?php endforeach ?>

View File

@ -34,6 +34,18 @@
</td>
</tr>
<?php endif ?>
<?php if (count($data['links']) > 0): ?>
<tr>
<td>External Links</td>
<td>
<?php foreach ($data['links'] as $urlName => $externalUrl): ?>
<a rel='external' href="<?= $externalUrl ?>"><?= $urlName ?></a><br />
<?php endforeach ?>
</td>
</tr>
<?php endif ?>
<tr>
<td>Genres</td>
<td>
@ -45,7 +57,7 @@
<br />
</aside>
<article class="text">
<h2 class="toph"><a rel="external" href="<?= $data['url'] ?>"><?= $data['title'] ?></a></h2>
<h2 class="toph"><?= $data['title'] ?></h2>
<?php foreach ($data['titles_more'] as $title): ?>
<h3><?= $title ?></h3>
<?php endforeach ?>

View File

@ -36,7 +36,7 @@ use Aviat\AnimeClient\API\Kitsu;
$character = $data['waifu']['character'];
echo $helper->a(
$url->generate('character', ['slug' => $character['slug']]),
$character['canonicalName']
$character['names']['canonical']
);
?>
</td>
@ -59,29 +59,32 @@ use Aviat\AnimeClient\API\Kitsu;
<h3>Favorites</h3>
<?= $component->tabs('user-favorites', $data['favorites'], static function ($items, $type) use ($component, $helper, $url) {
$rendered = [];
if ($type === 'characters')
if ($type === 'character')
{
uasort($items, fn ($a, $b) => $a['canonicalName'] <=> $b['canonicalName']);
uasort($items, fn ($a, $b) => $a['names']['canonical'] <=> $b['names']['canonical']);
}
else
{
uasort($items, fn ($a, $b) => Kitsu::filterTitles($a)[0] <=> Kitsu::filterTitles($b)[0]);
uasort($items, fn ($a, $b) => $a['titles']['canonical'] <=> $b['titles']['canonical']);
}
foreach ($items as $id => $item)
{
if ($type === 'characters')
if ($type === 'character')
{
$rendered[] = $component->character(
$item['canonicalName'],
$url->generate('character', ['slug', $item['slug']]),
$item['names']['canonical'],
$url->generate('character', ['slug' => $item['slug']]),
$helper->picture("images/characters/{$item['id']}.webp")
);
}
else
{
$rendered[] = $component->media(
Kitsu::filterTitles($item),
array_merge(
[$item['titles']['canonical']],
Kitsu::getFilteredTitles($item['titles']),
),
$url->generate("{$type}.details", ['id' => $item['slug']]),
$helper->picture("images/{$type}/{$item['id']}.webp"),
);

View File

@ -91,6 +91,48 @@ final class Kitsu {
return MangaPublishingStatus::NOT_YET_PUBLISHED;
}
public static function mappingsToUrls(array $mappings, string $kitsuLink = ''): array
{
$output = [];
foreach ($mappings as $mapping)
{
switch ($mapping['externalSite'])
{
case 'ANIDB':
$output['AniDB'] = "https://anidb.net/anime/{$mapping['externalId']}";
break;
case 'ANILIST_ANIME':
$output['Anilist'] = "https://anilist.co/anime/{$mapping['externalId']}/";
break;
case 'ANILIST_MANGA':
$output['Anilist'] = "https://anilist.co/manga/{$mapping['externalId']}/";
break;
case 'MYANIMELIST_ANIME':
$output['MyAnimeList'] = "https://myanimelist.net/anime/{$mapping['externalId']}";
break;
case 'MYANIMELIST_MANGA':
$output['MyAnimeList'] = "https://myanimelist.net/manga/{$mapping['externalId']}";
break;
default:
continue 2;
}
}
if ($kitsuLink !== '')
{
$output['Kitsu'] = $kitsuLink;
}
ksort($output);
return $output;
}
/**
* Reorganize streaming links
*

View File

@ -231,18 +231,8 @@ final class Model {
*/
public function getUserData(string $username): array
{
return $this->requestBuilder->getRequest('users', [
'query' => [
'filter' => [
'name' => $username,
],
'fields' => [
'anime' => 'slug,canonicalTitle,posterImage',
'manga' => 'slug,canonicalTitle,posterImage',
'characters' => 'slug,canonicalName,image',
],
'include' => 'waifu,favorites.item,stats'
]
return $this->requestBuilder->runQuery('UserDetails', [
'slug' => $username,
]);
}

View File

@ -19,6 +19,7 @@ query ($slug: String!) {
}
birthday
id
location
name
proMessage
proTier
@ -29,6 +30,89 @@ query ($slug: String!) {
url
}
}
favorites {
nodes {
id
item {
__typename,
...on Anime {
id
slug
posterImage {
original {
url
height
width
}
views {
url
height
width
}
}
titles {
canonical
localized
}
}
...on Manga {
id
slug
posterImage {
original {
url
height
width
}
views {
url
height
width
}
}
titles {
canonical
localized
}
}
...on Person {
id
slug
image {
original {
url
}
views {
url
height
width
}
}
names {
alternatives
canonical
canonicalLocale
localized
},
}
...on Character {
id
slug
image {
original {
url
}
}
names {
alternatives
canonical
canonicalLocale
localized
},
}
}
}
}
stats {
animeAmountConsumed {
completed

View File

@ -38,6 +38,7 @@ final class AnimeTransformer extends AbstractTransformer {
? $item['data']['findAnimeBySlug']
: $item['data']['findAnimeById'];
$characters = [];
$links = [];
$staff = [];
$genres = array_map(fn ($genre) => $genre['title']['en'], $base['categories']['nodes']);
@ -108,6 +109,11 @@ final class AnimeTransformer extends AbstractTransformer {
ksort($staff);
}
if (count($base['mappings']['nodes']) > 0)
{
$links = Kitsu::mappingsToUrls($base['mappings']['nodes'], "https://kitsu.io/anime/{$base['slug']}");
}
return AnimePage::from([
'age_rating' => $base['ageRating'],
'age_rating_guide' => $base['ageRatingGuide'],
@ -116,12 +122,13 @@ final class AnimeTransformer extends AbstractTransformer {
'episode_count' => $base['episodeCount'],
'episode_length' => $base['episodeLength'],
'genres' => $genres,
'links' => $links,
'id' => $base['id'],
'slug' => $base['slug'],
'staff' => $staff,
'show_type' => $base['subtype'],
'status' => Kitsu::getAiringStatus($base['startDate'], $base['endDate']),
'streaming_links' => Kitsu::parseStreamingLinks($base['streamingLinks']['nodes']),
'streaming_links' => Kitsu::parseStreamingLinks($base['streamingLinks']['nodes'] ?? []),
'synopsis' => $base['description']['en'],
'title' => $title,
'titles' => $titles,

View File

@ -39,6 +39,7 @@ final class MangaTransformer extends AbstractTransformer {
: $item['data']['findMangaById'];
$characters = [];
$links = [];
$staff = [];
$genres = array_map(fn ($genre) => $genre['title']['en'], $base['categories']['nodes']);
sort($genres);
@ -108,6 +109,11 @@ final class MangaTransformer extends AbstractTransformer {
ksort($staff);
}
if (count($base['mappings']['nodes']) > 0)
{
$links = Kitsu::mappingsToUrls($base['mappings']['nodes'], "https://kitsu.io/manga/{$base['slug']}");
}
$data = [
'age_rating' => $base['ageRating'],
'age_rating_guide' => $base['ageRatingGuide'],
@ -116,6 +122,7 @@ final class MangaTransformer extends AbstractTransformer {
'volume_count' => $base['volumeCount'],
'cover_image' => $base['posterImage']['views'][1]['url'],
'genres' => $genres,
'links' => $links,
'manga_type' => $base['subtype'],
'id' => $base['id'],
'staff' => $staff,

View File

@ -16,9 +16,9 @@
namespace Aviat\AnimeClient\API\Kitsu\Transformer;
use Aviat\AnimeClient\API\Kitsu;
use function Aviat\AnimeClient\getLocalImg;
use Aviat\AnimeClient\API\JsonAPI;
use Aviat\AnimeClient\Types\User;
use Aviat\Ion\Transformer\AbstractTransformer;
@ -31,36 +31,24 @@ use Aviat\Ion\Transformer\AbstractTransformer;
final class UserTransformer extends AbstractTransformer {
public function transform($profileData): User
{
$orgData = JsonAPI::organizeData($profileData)[0];
$attributes = $orgData['attributes'];
$rels = $orgData['relationships'] ?? [];
$favorites = array_key_exists('favorites', $rels) ? $rels['favorites'] : [];
$stats = [];
foreach ($rels['stats'] as $sid => &$item)
{
$key = $item['attributes']['kind'];
$stats[$key] = $item['attributes']['statsData'];
unset($item);
}
unset($item);
$waifu = (array_key_exists('waifu', $rels)) ? [
'label' => $attributes['waifuOrHusbando'],
'character' => $rels['waifu']['attributes'],
$base = $profileData['data']['findProfileBySlug'] ?? [];
$favorites = $base['favorites']['nodes'] ?? [];
$stats = $base['stats'] ?? [];
$waifu = (array_key_exists('waifu', $base)) ? [
'label' => $base['waifuOrHusbando'],
'character' => $base['waifu'],
] : [];
return User::from([
'about' => $attributes['about'],
'avatar' => getLocalImg($attributes['avatar']['original'], FALSE),
'about' => $base['about'],
'avatar' => getLocalImg($base['avatarImage']['original']['url'], FALSE),
'favorites' => $this->organizeFavorites($favorites),
'location' => $attributes['location'],
'name' => $attributes['name'],
'slug' => $attributes['slug'],
'stats' => $this->organizeStats($stats, $attributes),
'location' => $base['location'],
'name' => $base['name'],
'slug' => $base['slug'],
'stats' => $this->organizeStats($stats),
'waifu' => $waifu,
'website' => $attributes['website'],
'website' => $base['siteLinks']['nodes'][0]['url'],
]);
}
@ -74,58 +62,10 @@ final class UserTransformer extends AbstractTransformer {
{
$output = [];
unset($rawFavorites['data']);
foreach ($rawFavorites as $item)
{
$rank = $item['attributes']['favRank'];
foreach ($item['relationships']['item'] as $key => $fav)
{
$output[$key] = $output[$key] ?? [];
foreach ($fav as $id => $data)
{
$output[$key][$rank] = array_merge(['id' => $id], $data['attributes']);
}
ksort($output[$key]);
}
}
return $output;
}
/**
* Format the time spent on anime in a more readable format
*
* @param int $seconds
* @return string
*/
private function formatAnimeTime(int $seconds): string
{
// All the seconds left
$remSeconds = $seconds % 60;
$minutes = ($seconds - $remSeconds) / 60;
$minutesPerDay = 1440;
$minutesPerYear = $minutesPerDay * 365;
// Minutes short of a year
$years = (int)floor($minutes / $minutesPerYear);
$minutes %= $minutesPerYear;
// Minutes short of a day
$extraMinutes = $minutes % $minutesPerDay;
$days = ($minutes - $extraMinutes) / $minutesPerDay;
// Minutes short of an hour
$remMinutes = $extraMinutes % 60;
$hours = ($extraMinutes - $remMinutes) / 60;
$output = "{$days} days, {$hours} hours, {$remMinutes} minutes, and {$remSeconds} seconds.";
if ($years > 0)
{
$output = "{$years} year(s),{$output}";
$type = strtolower($item['item']['__typename']);
$output[$type][$item['id']] = $item['item'];
}
return $output;
@ -137,20 +77,20 @@ final class UserTransformer extends AbstractTransformer {
$mangaStats = [];
$otherStats = [];
if (array_key_exists('anime-amount-consumed', $stats))
if (array_key_exists('animeAmountConsumed', $stats))
{
$animeStats = [
'Time spent watching anime:' => $this->formatAnimeTime($stats['anime-amount-consumed']['time']),
'Anime series watched:' => number_format($stats['anime-amount-consumed']['media']),
'Anime episodes watched:' => number_format($stats['anime-amount-consumed']['units']),
'Time spent watching anime:' => Kitsu::friendlyTime($stats['animeAmountConsumed']['time']),
'Anime series watched:' => number_format($stats['animeAmountConsumed']['media']),
'Anime episodes watched:' => number_format($stats['animeAmountConsumed']['units']),
];
}
if (array_key_exists('manga-amount-consumed', $stats))
if (array_key_exists('mangaAmountConsumed', $stats))
{
$mangaStats = [
'Manga series read:' => number_format($stats['manga-amount-consumed']['media']),
'Manga chapters read:' => number_format($stats['manga-amount-consumed']['units']),
'Manga series read:' => number_format($stats['mangaAmountConsumed']['media']),
'Manga chapters read:' => number_format($stats['mangaAmountConsumed']['units']),
];
}

View File

@ -25,6 +25,11 @@ final class AnimePage extends Anime {
*/
public array $characters = [];
/**
* @var array
*/
public array $links = [];
/**
* @var array
*/

View File

@ -52,6 +52,11 @@ final class MangaPage extends AbstractType {
*/
public array $genres;
/**
* @var array
*/
public array $links;
/**
* @var string
*/