diff --git a/app/bootstrap.php b/app/bootstrap.php index fdc8e0da..5229f433 100644 --- a/app/bootstrap.php +++ b/app/bootstrap.php @@ -20,8 +20,9 @@ use Aura\Html\HelperLocatorFactory; use Aura\Router\RouterContainer; use Aura\Session\SessionFactory; use Aviat\AnimeClient\API\Kitsu\Auth as KitsuAuth; +use Aviat\AnimeClient\API\Kitsu\KitsuModel; use Aviat\AnimeClient\Model; -use Aviat\Ion\Cache\CacheManager; +use Aviat\Banker\Pool; use Aviat\Ion\Config; use Aviat\Ion\Di\Container; use Monolog\Handler\RotatingFileHandler; @@ -54,7 +55,9 @@ return function(array $config_array = []) { // Create Cache Object $container->set('cache', function($container) { - return new CacheManager($container->get('config')); + $logger = $container->getLogger(); + $config = $container->get('config')->get('cache'); + return new Pool($config, $logger); }); // Create Aura Router Object @@ -99,6 +102,11 @@ return function(array $config_array = []) { }); // Models + $container->set('kitsu-model', function($container) { + $model = new KitsuModel(); + $model->setContainer($container); + return $model; + }); $container->set('api-model', function($container) { return new Model\API($container); }); diff --git a/app/views/anime/details.php b/app/views/anime/details.php index 6b27c67c..b06eae01 100644 --- a/app/views/anime/details.php +++ b/app/views/anime/details.php @@ -5,10 +5,10 @@

- + - + */ ?> @@ -23,24 +23,23 @@ - +
Airing Status
Show Type
Age Rating
Genres - - +

- -

+ +

+ + +


diff --git a/app/views/anime/list.php b/app/views/anime/list.php index 52de7a01..488feab3 100644 --- a/app/views/anime/list.php +++ b/app/views/anime/list.php @@ -1,7 +1,7 @@
-is_authenticated()): ?> +is_authenticated()): ?> Add Item - +

There's nothing here!

@@ -10,9 +10,9 @@ - is_authenticated()): ?> + is_authenticated()): ?> - + @@ -26,13 +26,13 @@ - is_authenticated()) continue; ?> + is_authenticated()) continue; ?> - is_authenticated()): ?> + is_authenticated()): ?> - +
  Title Airing Status Score
">Edit @@ -73,5 +73,5 @@ -is_authenticated()) ? 'table_edit' : 'table' ?> +is_authenticated()) ? 'table_edit' :*/ 'table' ?> diff --git a/index.php b/index.php index 1aacb66e..ca589e6f 100644 --- a/index.php +++ b/index.php @@ -58,7 +58,7 @@ $whoops->pushHandler($defaultHandler); //$whoops->pushHandler($jsonHandler); // Register as the error handler -// $whoops->register(); +$whoops->register(); // ----------------------------------------------------------------------------- // Dependency Injection setup diff --git a/src/API/GuzzleTrait.php b/src/API/GuzzleTrait.php index f12b5436..039722ba 100644 --- a/src/API/GuzzleTrait.php +++ b/src/API/GuzzleTrait.php @@ -16,18 +16,8 @@ namespace Aviat\AnimeClient\API; -use Psr\Http\Message\ResponseInterface; - /** * Base trait for api interaction - * - * @method ResponseInterface get(string $uri, array $options); - * @method ResponseInterface delete(string $uri, array $options); - * @method ResponseInterface head(string $uri, array $options); - * @method ResponseInterface options(string $uri, array $options); - * @method ResponseInterface patch(string $uri, array $options); - * @method ResponseInterface post(string $uri, array $options); - * @method ResponseInterface put(string $uri, array $options); */ trait GuzzleTrait { /** @@ -48,39 +38,4 @@ trait GuzzleTrait { * @return void */ abstract protected function init(); - - /** - * Magic methods to call guzzle api client - * - * @param string $method - * @param array $args - * @return ResponseInterface|null - */ - public function __call($method, $args) - { - $valid_methods = [ - 'get', - 'getAsync', - 'delete', - 'deleteAsync', - 'head', - 'headAsync', - 'options', - 'optionsAsync', - 'patch', - 'patchAsync', - 'post', - 'postAsync', - 'put', - 'putAsync' - ]; - - if ( ! in_array($method, $valid_methods)) - { - return NULL; - } - - array_unshift($args, strtoupper($method)); - return call_user_func_array([$this->client, 'request'], $args); - } } \ No newline at end of file diff --git a/src/API/Kitsu/Enum/AnimeWatchingStatus.php b/src/API/Kitsu/Enum/AnimeWatchingStatus.php index 7cfe3883..850aece1 100644 --- a/src/API/Kitsu/Enum/AnimeWatchingStatus.php +++ b/src/API/Kitsu/Enum/AnimeWatchingStatus.php @@ -22,10 +22,10 @@ use Aviat\Ion\Enum as BaseEnum; * Possible values for watching status for the current anime */ class AnimeWatchingStatus extends BaseEnum { - const WATCHING = 'current'; - const PLAN_TO_WATCH = 'planned'; - const COMPLETED = 'completed'; - const ON_HOLD = 'on_hold'; - const DROPPED = 'dropped'; + const WATCHING = 1; + const PLAN_TO_WATCH = 2; + const COMPLETED = 3; + const ON_HOLD = 4; + const DROPPED = 5; } -// End of AnimeWatchingStatus.php +// End of AnimeWatchingStatus.php \ No newline at end of file diff --git a/src/API/Kitsu/KitsuModel.php b/src/API/Kitsu/KitsuModel.php index 3ade0674..4b08f76a 100644 --- a/src/API/Kitsu/KitsuModel.php +++ b/src/API/Kitsu/KitsuModel.php @@ -16,8 +16,10 @@ namespace Aviat\AnimeClient\API\Kitsu; -use Aviat\AnimeClient\API\Kitsu\Transformer\AnimeListTransformer; +use Aviat\AnimeClient\AnimeClient; +use Aviat\AnimeClient\API\Kitsu\Transformer\{AnimeTransformer, AnimeListTransformer}; use Aviat\Ion\Json; +use GuzzleHttp\Exception\ClientException; /** * Kitsu API Model @@ -38,11 +40,20 @@ class KitsuModel { */ protected $animeListTransformer; + /** + * @var AnimeTransformer + */ + protected $animeTransformer; + + /** + * KitsuModel constructor. + */ public function __construct() { // Set up Guzzle trait $this->init(); + $this->animeTransformer = new AnimeTransformer(); $this->animeListTransformer = new AnimeListTransformer(); } @@ -55,7 +66,7 @@ class KitsuModel { */ public function authenticate(string $username, string $password) { - $response = $this->post('https://kitsu.io/api/oauth/token', [ + $data = $this->postRequest(AnimeClient::KITSU_AUTH_URL, [ 'body' => http_build_query([ 'grant_type' => 'password', 'username' => $username, @@ -65,9 +76,7 @@ class KitsuModel { ]) ]); - $info = JSON::decode($response->getBody()); - - if (array_key_exists('access_token', $info)) { + if (array_key_exists('access_token', $data)) { // @TODO save token return true; } @@ -75,41 +84,79 @@ class KitsuModel { return false; } - public function getAnimeMedia($entryId): array { - $response = $this->get("library-entries/{$entryId}/media", [ - 'headers' => [ - 'client_id' => self::CLIENT_ID, - 'client_secret' => self::CLIENT_SECRET - ] - ]); - return JSON::decode($response->getBody(), TRUE); + + public function getAnimeGenres($animeId): array + { + return $this->getGenres('anime', $animeId); } - public function getAnimeList(): array { - $response = $this->get('library-entries', [ - 'headers' => [ - 'client_id' => self::CLIENT_ID, - 'client_secret' => self::CLIENT_SECRET - ], + public function getMangaGenres($mangaId): array + { + return $this->getGenres('manga', $mangaId); + } + + public function getAnime(string $animeId): array + { + $baseData = $this->getRawAnimeData($animeId); + return $this->animeTransformer->transform($baseData); + } + + public function getRawAnimeData($animeId): array + { + return $this->getRawMediaData('anime', $animeId); + } + + public function getAnimeMedia($entryId): array + { + return $this->getRequest("library-entries/{$entryId}/media"); + } + + public function getAnimeList($status): array + { + $options = [ 'query' => [ 'filter' => [ 'user_id' => 2644, - 'media_type' => 'Anime' + 'media_type' => 'Anime', + 'status' => $status, ], - 'include' => 'media' + 'include' => 'media,user', + 'page' => [ + 'offset' => 0, + 'limit' => 200 + ], + 'sort' => '-updated_at' ] - ]); + ]; - $data = JSON::decode($response->getBody(), TRUE); + $data = $this->getRequest('library-entries', $options); foreach($data['data'] as &$item) { - $item['anime'] = $this->getAnimeMedia($item['id'])['data']['attributes']; + $item['anime'] = $this->getRawAnimeData($item['relationships']['media']['data']['id']); } $transformed = $this->animeListTransformer->transformCollection($data['data']); return $transformed; } + + private function getGenres(string $type, string $id): array + { + $data = $this->getRequest("{$type}/{$id}/genres"); + $rawGenres = array_pluck($data['data'], 'attributes'); + $genres = array_pluck($rawGenres, 'name'); + + return $genres; + } + + private function getRawMediaData(string $type, string $id): array + { + $data = $this->getRequest("{$type}/{$id}"); + $baseData = $data['data']['attributes']; + $baseData['genres'] = $this->getGenres($type, $id); + + return $baseData; + } } \ No newline at end of file diff --git a/src/API/Kitsu/KitsuTrait.php b/src/API/Kitsu/KitsuTrait.php index 569597d0..7ab08b3c 100644 --- a/src/API/Kitsu/KitsuTrait.php +++ b/src/API/Kitsu/KitsuTrait.php @@ -17,10 +17,15 @@ namespace Aviat\AnimeClient\API\Kitsu; use Aviat\AnimeClient\API\GuzzleTrait; +use Aviat\Ion\Di\ContainerAware; +use Aviat\Ion\Json; use GuzzleHttp\Client; use GuzzleHttp\Cookie\CookieJar; +use InvalidArgumentException; +use RuntimeException; trait KitsuTrait { + use ContainerAware; use GuzzleTrait; /** @@ -53,4 +58,75 @@ trait KitsuTrait { ] ]); } + + /** + * Make a request via Guzzle + * + * @param string $type + * @param string $url + * @param array $options + * @return array + */ + private function request(string $type, string $url, array $options = []): array + { + $validTypes = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']; + + if ( ! in_array($type, $validTypes)) + { + throw new InvalidArgumentException('Invalid http request type'); + } + + $logger = NULL; + + if ($this->getContainer()) + { + $logger = $this->container->getLogger('default'); + } + + $defaultOptions = [ + 'headers' => [ + 'client_id' => 'dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd', + 'client_secret' => '54d7307928f63414defd96399fc31ba847961ceaecef3a5fd93144e960c0e151' + ] + ]; + + $options = array_merge($defaultOptions, $options); + + $response = $this->client->request($type, $url, $options); + + if ((int) $response->getStatusCode() !== 200) + { + if ($logger) + { + $logger->warning('Non 200 response for api call'); + $logger->warning($response->getBody()); + } + + throw new RuntimeException($response); + } + + return JSON::decode($response->getBody(), TRUE); + } + + /** + * Remove some boilerplate for get requests + * + * @param array $args + * @return array + */ + protected function getRequest(...$args): array + { + return $this->request('GET', ...$args); + } + + /** + * Remove some boilerplate for get requests + * + * @param array $args + * @return array + */ + protected function postRequest(...$args): array + { + return $this->request('POST', ...$args); + } } \ No newline at end of file diff --git a/src/API/Kitsu/Transformer/AnimeListTransformer.php b/src/API/Kitsu/Transformer/AnimeListTransformer.php index ae202f3d..d4cb9bf5 100644 --- a/src/API/Kitsu/Transformer/AnimeListTransformer.php +++ b/src/API/Kitsu/Transformer/AnimeListTransformer.php @@ -33,11 +33,9 @@ class AnimeListTransformer extends AbstractTransformer { public function transform($item) { /* ?>
linearizeGenres($item['anime']['genres']); */ + $genres = $item['anime']['genres'] ?? []; $rating = (int) 2 * $item['attributes']['rating']; @@ -75,17 +73,17 @@ die(); */ 'age_rating' => $anime['ageRating'], 'title' => $anime['canonicalTitle'], 'alternate_title' => $alternate_title, - 'slug' => $anime['slug'], + 'slug' => $item['relationships']['media']['data']['id'],//$anime['slug'], 'url' => $anime['url'] ?? '', 'type' => $anime['showType'], 'image' => $anime['posterImage']['small'], - 'genres' => [],//$genres, + 'genres' => $genres, ], 'watching_status' => $item['attributes']['status'], 'notes' => $item['attributes']['notes'], 'rewatching' => (bool) $item['attributes']['reconsuming'], 'rewatched' => (int) $item['attributes']['reconsumeCount'], - 'user_rating' => $rating, + 'user_rating' => ($rating === 0) ? '-' : $rating, 'private' => (bool) $item['attributes']['private'] ?? false, ]; } @@ -123,23 +121,5 @@ die(); */ 'privacy' => $privacy ]; } - - /** - * Simplify structure of genre list - * - * @param array $rawGenres - * @return array - */ - protected function linearizeGenres(array $rawGenres): array - { - $genres = []; - - foreach ($rawGenres as $genre) - { - $genres[] = $genre['name']; - } - - return $genres; - } } // End of AnimeListTransformer.php \ No newline at end of file diff --git a/src/API/Kitsu/Transformer/AnimeTransformer.php b/src/API/Kitsu/Transformer/AnimeTransformer.php new file mode 100644 index 00000000..4e08ee8c --- /dev/null +++ b/src/API/Kitsu/Transformer/AnimeTransformer.php @@ -0,0 +1,52 @@ + + * @copyright 2015 - 2016 Timothy J. Warren + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version 4.0 + * @link https://github.com/timw4mail/HummingBirdAnimeClient + */ + +namespace Aviat\AnimeClient\API\Kitsu\Transformer; + +use Aviat\Ion\Transformer\AbstractTransformer; + +/** + * Transformer for anime description page + */ +class AnimeTransformer extends AbstractTransformer { + + /** + * Convert raw api response to a more + * logical and workable structure + * + * @param array $item API library item + * @return array + */ + public function transform($item) + { + sort($item['genres']); + + return [ + 'title' => $item['canonicalTitle'], + 'en_title' => $item['titles']['en_jp'], + 'jp_title' => $item['titles']['ja_jp'], + 'cover_image' => $item['posterImage']['small'], + 'show_type' => $item['showType'], + 'episode_count' => $item['episodeCount'], + 'episode_length' => $item['episodeLength'], + 'synopsis' => $item['synopsis'], + 'age_rating' => $item['ageRating'], + 'age_rating_guide' => $item['ageRatingGuide'], + 'url' => "https://kitsu.io/anime/{$item['slug']}", + 'genres' => $item['genres'], + ]; + } +} \ No newline at end of file diff --git a/src/Controller/Anime.php b/src/Controller/Anime.php index 4188e1f4..1453655f 100644 --- a/src/Controller/Anime.php +++ b/src/Controller/Anime.php @@ -36,7 +36,7 @@ class Anime extends BaseController { protected $model; /** - * Data to ve sent to all routes in this controller + * Data to be sent to all routes in this controller * @var array $base_data */ protected $base_data; @@ -71,19 +71,19 @@ class Anime extends BaseController { /** * Show a portion, or all of the anime list * - * @param string $type - The section of the list + * @param string|int $type - The section of the list * @param string $view - List or cover view * @return void */ - public function index($type = "watching", $view = '') + public function index($type = AnimeWatchingStatus::WATCHING, string $view = NULL) { $type_title_map = [ 'all' => 'All', - 'watching' => 'Currently Watching', - 'plan_to_watch' => 'Plan to Watch', - 'on_hold' => 'On Hold', - 'dropped' => 'Dropped', - 'completed' => 'Completed' + AnimeWatchingStatus::WATCHING => 'Currently Watching', + AnimeWatchingStatus::PLAN_TO_WATCH => 'Plan to Watch', + AnimeWatchingStatus::ON_HOLD => 'On Hold', + AnimeWatchingStatus::DROPPED => 'Dropped', + AnimeWatchingStatus::COMPLETED => 'Completed' ]; $model_map = [ @@ -106,8 +106,8 @@ class Anime extends BaseController { ]; $data = ($type !== 'all') - ? $this->cache->get($this->model, 'get_list', ['status' => $model_map[$type]]) - : $this->cache->get($this->model, 'get_all_lists', []); + ? $this->model->get_list($model_map[$type]) + : $this->model->get_all_lists(); $this->outputHTML('anime/' . $view_map[$view], [ 'title' => $title, @@ -161,7 +161,7 @@ class Anime extends BaseController { if (intval($result['statusCode']) === 201) { $this->set_flash_message('Added new anime to list', 'success'); - $this->cache->purge(); + // $this->cache->purge(); } else { diff --git a/src/Model/Anime.php b/src/Model/Anime.php index e3b9959f..80de9952 100644 --- a/src/Model/Anime.php +++ b/src/Model/Anime.php @@ -15,10 +15,7 @@ */ namespace Aviat\AnimeClient\Model; - -use Aviat\AnimeClient\API\Kitsu\KitsuModel; use Aviat\AnimeClient\API\Kitsu\Enum\AnimeWatchingStatus; -use Aviat\AnimeClient\API\Kitsu\Transformer\AnimeListTransformer; use Aviat\Ion\Di\ContainerInterface; use Aviat\Ion\Json; @@ -53,10 +50,14 @@ class Anime extends API { AnimeWatchingStatus::COMPLETED => self::COMPLETED, ]; + /** + * Anime constructor. + * @param ContainerInterface $container + */ public function __construct(ContainerInterface $container) { parent::__construct($container); - $this->kitsuModel = new KitsuModel(); + $this->kitsuModel = $container->get('kitsu-model'); } /** @@ -152,11 +153,8 @@ class Anime extends API { */ public function get_list($status) { - $data = $this->kitsuModel->getAnimeList(); - //return JSON::decode((string)$stream, TRUE); - - /*$data = $this->_get_list_from_api($status); - $this->sort_by_name($data, 'anime');*/ + $data = $this->kitsuModel->getAnimeList($status); + $this->sort_by_name($data, 'anime'); $output = []; $output[$this->const_map[$status]] = $data; @@ -172,15 +170,7 @@ class Anime extends API { */ public function get_anime($anime_id) { - $config = [ - 'query' => [ - 'id' => $anime_id - ] - ]; - - $response = $this->client->get("anime/{$anime_id}", $config); - - return Json::decode($response->getBody(), TRUE); + return $this->kitsuModel->getAnime($anime_id); } /** @@ -213,43 +203,6 @@ class Anime extends API { return Json::decode($response->getBody(), TRUE); } - /** - * Retrieve data from the api - * - * @codeCoverageIgnore - * @param string $status - * @return array - */ - protected function _get_list_from_api(string $status = "all"): array - { - $config = [ - 'allow_redirects' => FALSE - ]; - - if ($status !== 'all') - { - $config['query']['status'] = $status; - } - - $username = $this->config->get('hummingbird_username'); - $auth = $this->container->get('auth'); - if ($auth->is_authenticated()) - { - $config['query']['auth_token'] = $auth->get_auth_token(); - } - - $response = $this->get("library-entries?filter[media_type]=Anime&filter[user_id]=2644&filter[status]=1,2&include=media", $config); - $output = $this->transform($status, $response); - - $util = $this->container->get('util'); - foreach ($output as &$row) - { - $row['anime']['image'] = $util->get_cached_image($row['anime']['image'], $row['anime']['slug'], 'anime'); - } - - return $output; - } - /** * Get the full list from the api * @@ -266,20 +219,5 @@ class Anime extends API { $response = $this->get("users/{$username}/library", $config); return Json::decode($response->getBody(), TRUE); } - - /** - * Handle transforming of api data - * - * @param string $status - * @param \GuzzleHttp\Message\Response $response - * @return array - */ - protected function transform($status, $response) - { - $api_data = Json::decode($response->getBody(), TRUE); - $transformer = new AnimeListTransformer(); - $transformed = $transformer->transformCollection($api_data); - return $transformed; - } } // End of AnimeModel.php \ No newline at end of file