From ba276cc86eb0410983154dacb249b01de4e6d6b8 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Mon, 24 Aug 2020 15:20:07 -0400 Subject: [PATCH] Update profile page to use GraphQL, see #27 --- app/views/anime/details.php | 16 ++- app/views/manga/details.php | 14 ++- app/views/user/details.php | 19 ++-- src/AnimeClient/API/Kitsu.php | 42 +++++++ src/AnimeClient/API/Kitsu/Model.php | 14 +-- .../API/Kitsu/Queries/UserDetails.graphql | 84 ++++++++++++++ .../Kitsu/Transformer/AnimeTransformer.php | 9 +- .../Kitsu/Transformer/MangaTransformer.php | 7 ++ .../API/Kitsu/Transformer/UserTransformer.php | 106 ++++-------------- src/AnimeClient/Types/AnimePage.php | 5 + src/AnimeClient/Types/MangaPage.php | 5 + 11 files changed, 215 insertions(+), 106 deletions(-) diff --git a/app/views/anime/details.php b/app/views/anime/details.php index b6fa4150..77fac541 100644 --- a/app/views/anime/details.php +++ b/app/views/anime/details.php @@ -50,6 +50,18 @@ use function Aviat\AnimeClient\getLocalImg; + + 0): ?> + + External Links + + $externalUrl): ?> +
+ + + + + Genres @@ -58,11 +70,13 @@ use function Aviat\AnimeClient\getLocalImg; + +
-

+

diff --git a/app/views/manga/details.php b/app/views/manga/details.php index 6b21901a..8afe36c4 100644 --- a/app/views/manga/details.php +++ b/app/views/manga/details.php @@ -34,6 +34,18 @@ + + 0): ?> + + External Links + + $externalUrl): ?> +
+ + + + + Genres @@ -45,7 +57,7 @@
-

+

diff --git a/app/views/user/details.php b/app/views/user/details.php index f31d7a79..db0f8a8c 100644 --- a/app/views/user/details.php +++ b/app/views/user/details.php @@ -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'] ); ?> @@ -59,29 +59,32 @@ use Aviat\AnimeClient\API\Kitsu;

Favorites

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"), ); diff --git a/src/AnimeClient/API/Kitsu.php b/src/AnimeClient/API/Kitsu.php index 5401baf1..8b21bd90 100644 --- a/src/AnimeClient/API/Kitsu.php +++ b/src/AnimeClient/API/Kitsu.php @@ -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 * diff --git a/src/AnimeClient/API/Kitsu/Model.php b/src/AnimeClient/API/Kitsu/Model.php index cb339312..ff210c41 100644 --- a/src/AnimeClient/API/Kitsu/Model.php +++ b/src/AnimeClient/API/Kitsu/Model.php @@ -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, ]); } diff --git a/src/AnimeClient/API/Kitsu/Queries/UserDetails.graphql b/src/AnimeClient/API/Kitsu/Queries/UserDetails.graphql index bb060bda..dd58d89f 100644 --- a/src/AnimeClient/API/Kitsu/Queries/UserDetails.graphql +++ b/src/AnimeClient/API/Kitsu/Queries/UserDetails.graphql @@ -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 diff --git a/src/AnimeClient/API/Kitsu/Transformer/AnimeTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/AnimeTransformer.php index 40d9dec7..c2f022f7 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/AnimeTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/AnimeTransformer.php @@ -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, diff --git a/src/AnimeClient/API/Kitsu/Transformer/MangaTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/MangaTransformer.php index 77f2d3b1..d2e82af2 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/MangaTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/MangaTransformer.php @@ -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, diff --git a/src/AnimeClient/API/Kitsu/Transformer/UserTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/UserTransformer.php index d9df93f3..30fd38b0 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/UserTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/UserTransformer.php @@ -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']), ]; } diff --git a/src/AnimeClient/Types/AnimePage.php b/src/AnimeClient/Types/AnimePage.php index 9c270a14..1924925d 100644 --- a/src/AnimeClient/Types/AnimePage.php +++ b/src/AnimeClient/Types/AnimePage.php @@ -25,6 +25,11 @@ final class AnimePage extends Anime { */ public array $characters = []; + /** + * @var array + */ + public array $links = []; + /** * @var array */ diff --git a/src/AnimeClient/Types/MangaPage.php b/src/AnimeClient/Types/MangaPage.php index 57c49297..f5ccce5c 100644 --- a/src/AnimeClient/Types/MangaPage.php +++ b/src/AnimeClient/Types/MangaPage.php @@ -52,6 +52,11 @@ final class MangaPage extends AbstractType { */ public array $genres; + /** + * @var array + */ + public array $links; + /** * @var string */