From 03964c446a9b0d9ce4edbe6c0daa07ca9244141b Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 27 Jan 2017 12:35:28 -0500 Subject: [PATCH] Ugly progress commit --- CHANGELOG.md | 3 +- console | 2 +- public/css/base.css | 9 ++ public/css/base.myth.css | 8 ++ src/API/Kitsu.php | 10 ++- src/API/Kitsu/Auth.php | 18 +++- src/API/Kitsu/Model.php | 42 ++++++--- src/Command/BaseCommand.php | 52 +++++++---- src/Command/SyncKitsuWithMal.php | 86 +++++++++++++++++++ tests/RequirementsTest.php | 10 --- .../test_data/Kitsu/animeAfterTransform.json | 8 +- 11 files changed, 198 insertions(+), 50 deletions(-) create mode 100644 src/Command/SyncKitsuWithMal.php diff --git a/CHANGELOG.md b/CHANGELOG.md index ce2c5c1b..799612aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ # Changelog ## Version 4 -* Updated to use Kitsu API after discontinuation of Hummingbird +* Updated to use Kitsu API after discontinuation of Hummingbird +* Added streaming links to list entries from the Kitsu API ## Version 3 * Converted user configuration to toml files diff --git a/console b/console index fb116f0a..638f9bcc 100755 --- a/console +++ b/console @@ -34,8 +34,8 @@ unset($CONF_DIR); // Start console script // --------------------------------------------------------------------------------------------------------------------- $console = new \ConsoleKit\Console([ - 'cache-images' => '\Aviat\AnimeClient\Command\CacheImages', 'clear-cache' => '\Aviat\AnimeClient\Command\ClearCache', + 'sync-lists' => '\Aviat\AnimeClient\Command\SyncKitsuWithMal' ]); $console->run(); \ No newline at end of file diff --git a/public/css/base.css b/public/css/base.css index c3bcb7b6..2192edcb 100644 --- a/public/css/base.css +++ b/public/css/base.css @@ -1307,4 +1307,13 @@ a:hover, a:active { padding:0 0,5em 0.5em; padding:0 0.5rem 0.5rem; } +} + +/* ---------------------------------------------------------------------------- + Images / Logos +-----------------------------------------------------------------------------*/ + +.streaming-logo { + width: 50px; + height: 50px; } \ No newline at end of file diff --git a/public/css/base.myth.css b/public/css/base.myth.css index 3f481fb7..eabbf71b 100644 --- a/public/css/base.myth.css +++ b/public/css/base.myth.css @@ -560,4 +560,12 @@ a:hover, a:active { padding:0 0,5em 0.5em; padding:0 0.5rem 0.5rem; } +} + +/* ---------------------------------------------------------------------------- + Images / Logos +-----------------------------------------------------------------------------*/ +.streaming-logo { + width: 50px; + height: 50px; } \ No newline at end of file diff --git a/src/API/Kitsu.php b/src/API/Kitsu.php index e83c0be4..98378e5e 100644 --- a/src/API/Kitsu.php +++ b/src/API/Kitsu.php @@ -28,6 +28,8 @@ use DateTimeImmutable; */ class Kitsu { const AUTH_URL = 'https://kitsu.io/api/oauth/token'; + const AUTH_USER_ID_KEY = 'kitsu-auth-userid'; + const AUTH_TOKEN_CACHE_KEY = 'kitsu-auth-token'; /** * Map of Kitsu status to label for select menus @@ -104,21 +106,21 @@ class Kitsu { return [ 'name' => 'Crunchyroll', 'link' => true, - 'logo' => '' + 'logo' => '' ]; case 'www.funimation.com': return [ 'name' => 'Funimation', 'link' => true, - 'logo' => '' + 'logo' => '' ]; case 'www.hulu.com': return [ 'name' => 'Hulu', 'link' => true, - 'logo' => '' + 'logo' => '' ]; // Default to Netflix, because the API links are broken, @@ -127,7 +129,7 @@ class Kitsu { return [ 'name' => 'Netflix', 'link' => false, - 'logo' => '' + 'logo' => '' ]; } } diff --git a/src/API/Kitsu/Auth.php b/src/API/Kitsu/Auth.php index 04e2e395..70691eb1 100644 --- a/src/API/Kitsu/Auth.php +++ b/src/API/Kitsu/Auth.php @@ -17,6 +17,10 @@ namespace Aviat\AnimeClient\API\Kitsu; use Aviat\AnimeClient\AnimeClient; +use Aviat\AnimeClient\API\{ + CacheTrait, + Kitsu as K +}; use Aviat\Ion\Di\{ContainerAware, ContainerInterface}; use Exception; @@ -24,7 +28,7 @@ use Exception; * Kitsu API Authentication */ class Auth { - + use CacheTrait; use ContainerAware; /** @@ -49,6 +53,7 @@ class Auth { public function __construct(ContainerInterface $container) { $this->setContainer($container); + $this->setCache($container->get('cache')); $this->segment = $container->get('session') ->getSegment(AnimeClient::SESSION_SEGMENT); $this->model = $container->get('kitsu-model'); @@ -68,7 +73,7 @@ class Auth { try { - $auth_token = $this->model->authenticate($username, $password); + $auth = $this->model->authenticate($username, $password); } catch (Exception $e) { @@ -76,9 +81,14 @@ class Auth { } - if (FALSE !== $auth_token) + if (FALSE !== $auth) { - $this->segment->set('auth_token', $auth_token); + // Set the token in the cache for command line operations + $cacheItem = $this->cache->getItem(K::AUTH_TOKEN_CACHE_KEY); + $cacheItem->set($auth['access_token']); + $cacheItem->save(); + + $this->segment->set('auth_token', $auth['access_token']); return TRUE; } diff --git a/src/API/Kitsu/Model.php b/src/API/Kitsu/Model.php index 0d1a0c02..fb2e90c3 100644 --- a/src/API/Kitsu/Model.php +++ b/src/API/Kitsu/Model.php @@ -20,7 +20,10 @@ use Aviat\AnimeClient\API\CacheTrait; use Aviat\AnimeClient\API\JsonAPI; use Aviat\AnimeClient\API\Kitsu as K; use Aviat\AnimeClient\API\Kitsu\Transformer\{ - AnimeTransformer, AnimeListTransformer, MangaTransformer, MangaListTransformer + AnimeTransformer, + AnimeListTransformer, + MangaTransformer, + MangaListTransformer }; use Aviat\Ion\Di\ContainerAware; use Aviat\Ion\Json; @@ -85,9 +88,14 @@ class Model { * @param string $username * @return string */ - public function getUserIdByUsername(string $username) + public function getUserIdByUsername(string $username = NULL) { - $cacheItem = $this->cache->getItem($this->getHashForMethodCall($this, __METHOD__, [$username])); + if (is_null($username)) + { + $username = $this->getUsername(); + } + + $cacheItem = $this->cache->getItem(K::AUTH_USER_ID_KEY); if ( ! $cacheItem->isHit()) { @@ -128,7 +136,7 @@ class Model { if (array_key_exists('access_token', $data)) { - return $data['access_token']; + return $data; } return false; @@ -170,16 +178,16 @@ class Model { $baseData = $this->getRawMediaData('manga', $mangaId); return $this->mangaTransformer->transform($baseData); } - + /** - * Get the anime list for the configured user + * Get the raw (unorganized) anime list for the configured user * * @param string $status - The watching status to filter the list with * @param int $limit - The number of list entries to fetch for a page * @param int $offset - The page offset * @return array */ - public function getAnimeList(string $status, int $limit = 600, int $offset = 0): array + public function getRawAnimeList(string $status, int $limit = 600, int $offset = 0): array { $options = [ 'query' => [ @@ -192,15 +200,29 @@ class Model { 'page' => [ 'offset' => $offset, 'limit' => $limit - ] + ], + 'sort' => '-updated_at' ] ]; - $cacheItem = $this->cache->getItem($this->getHashForMethodCall($this, __METHOD__, $options)); + return $this->getRequest('library-entries', $options); + } + + /** + * Get the anime list for the configured user + * + * @param string $status - The watching status to filter the list with + * @param int $limit - The number of list entries to fetch for a page + * @param int $offset - The page offset + * @return array + */ + public function getAnimeList(string $status, int $limit = 600, int $offset = 0): array + { + $cacheItem = $this->cache->getItem($this->getHashForMethodCall($this, __METHOD__, [$status])); if ( ! $cacheItem->isHit()) { - $data = $this->getRequest('library-entries', $options); + $data = $this->getRawAnimeList($status, $limit, $offset); $included = JsonAPI::organizeIncludes($data['included']); $included = JsonAPI::inlineIncludedRelationships($included, 'anime'); diff --git a/src/Command/BaseCommand.php b/src/Command/BaseCommand.php index fecb2131..e929578f 100644 --- a/src/Command/BaseCommand.php +++ b/src/Command/BaseCommand.php @@ -22,20 +22,30 @@ use Aviat\AnimeClient\{ Model, Util }; -use Aviat\AnimeClient\Auth\HummingbirdAuth; +use Aviat\AnimeClient\API\CacheTrait; +use Aviat\AnimeClient\API\Kitsu\{ + Auth as KitsuAuth, + ListItem as KitsuListItem, + Model as KitsuModel +}; +use Aviat\AnimeClient\API\MAL\{ + ListItem as MALListItem, + Model as MALModel +}; use Aviat\Banker\Pool; use Aviat\Ion\Config; -use Aviat\Ion\Di\Container; +use Aviat\Ion\Di\{Container, ContainerAware}; use ConsoleKit\Command; use ConsoleKit\Widgets\Box; -use Monolog\Handler\RotatingFileHandler; +use Monolog\Handler\NullHandler; use Monolog\Logger; /** * Base class for console command setup */ class BaseCommand extends Command { - use \Aviat\Ion\Di\ContainerAware; + use CacheTrait; + use ContainerAware; /** * Echo text in a box @@ -68,9 +78,16 @@ class BaseCommand extends Command { $di = function ($config_array) use ($APP_DIR) { $container = new Container(); + // ------------------------------------------------------------------------- + // Logging + // ------------------------------------------------------------------------- + $app_logger = new Logger('animeclient'); - $app_logger->pushHandler(new RotatingFileHandler("{$APP_DIR}/logs/app.log", Logger::NOTICE)); + $app_logger->pushHandler(new NullHandler); + $request_logger = new Logger('request'); + $request_logger->pushHandler(new NullHandler); $container->setLogger($app_logger, 'default'); + $container->setLogger($request_logger, 'request'); // Create Config Object $container->set('config', function() use ($config_array) { @@ -90,18 +107,21 @@ class BaseCommand extends Command { }); // Models - $container->set('api-model', function($container) { - return new Model\API($container); + $container->set('kitsu-model', function($container) { + $listItem = new KitsuListItem(); + $listItem->setContainer($container); + $model = new KitsuModel($listItem); + $model->setContainer($container); + $cache = $container->get('cache'); + $model->setCache($cache); + return $model; }); - $container->set('anime-model', function($container) { - return new Model\Anime($container); - }); - $container->set('manga-model', function($container) { - return new Model\Manga($container); - }); - - $container->set('auth', function($container) { - return new HummingbirdAuth($container); + $container->set('mal-model', function($container) { + $listItem = new MALListItem(); + $listItem->setContainer($container); + $model = new MALModel($listItem); + $model->setContainer($container); + return $model; }); $container->set('util', function($container) { return new Util($container); diff --git a/src/Command/SyncKitsuWithMal.php b/src/Command/SyncKitsuWithMal.php new file mode 100644 index 00000000..93a714c2 --- /dev/null +++ b/src/Command/SyncKitsuWithMal.php @@ -0,0 +1,86 @@ + + * @copyright 2015 - 2017 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\Command; + +use Amp\Artax; +use Aviat\AnimeClient\API\Kitsu; + +/** + * Clears the API Cache + */ +class SyncKitsuWithMal extends BaseCommand { + + protected $kitsuModel; + + public function getKitsuAnimeListPageCount() + { + $cacheItem = $this->cache->getItem(Kitsu::AUTH_TOKEN_CACHE_KEY); + + $query = http_build_query([ + 'filter' => [ + 'user_id' => $this->kitsuModel->getUserIdByUsername(), + 'media_type' => 'Anime' + ], + 'include' => 'anime,anime.genres,anime.mappings,anime.streamingLinks', + 'page' => [ + 'limit' => 1 + ], + 'sort' => '-updated_at' + ]); + $request = (new Artax\Request) + ->setUri("https://kitsu.io/api/edge/library-entries?{$query}") + ->setProtocol('1.1') + ->setAllHeaders([ + 'Accept' => 'application/vnd.api+json', + 'Content-Type' => 'application/vnd.api+json', + 'User-Agent' => "Tim's Anime Client/4.0" + ]); + + if ($cacheItem->isHit()) + { + $token = $cacheItem->get(); + $request->setHeader('Authorization', "bearer {$token}"); + } + else + { + $this->echoBox("WARNING: NOT LOGGED IN\nSome data might be missing"); + } + + $response = \Amp\wait((new Artax\Client)->request($request)); + + $body = json_decode($response->getBody(), TRUE); + return $body['meta']['count']; + } + + /** + * Run the image conversion script + * + * @param array $args + * @param array $options + * @return void + * @throws \ConsoleKit\ConsoleException + */ + public function execute(array $args, array $options = []) + { + $this->setContainer($this->setupContainer()); + $this->setCache($this->container->get('cache')); + $this->kitsuModel = $this->container->get('kitsu-model'); + + $kitsuCount = $this->getKitsuAnimeListPageCount(); + $this->echoBox("List item count: {$kitsuCount}"); + } +} diff --git a/tests/RequirementsTest.php b/tests/RequirementsTest.php index 7c8c3bce..9b5ef3e8 100644 --- a/tests/RequirementsTest.php +++ b/tests/RequirementsTest.php @@ -7,16 +7,6 @@ class RequirementsTest extends AnimeClient_TestCase { $this->assertTrue(version_compare(PHP_VERSION, "5.4", "ge")); } - public function testHasGd() - { - $this->assertTrue(extension_loaded('gd')); - } - - public function testHasMcrypt() - { - $this->assertTrue(extension_loaded('mcrypt')); - } - public function testHasPDO() { $this->assertTrue(class_exists('PDO')); diff --git a/tests/test_data/Kitsu/animeAfterTransform.json b/tests/test_data/Kitsu/animeAfterTransform.json index af3e16ca..19230fe7 100644 --- a/tests/test_data/Kitsu/animeAfterTransform.json +++ b/tests/test_data/Kitsu/animeAfterTransform.json @@ -15,7 +15,7 @@ "meta": { "name": "Crunchyroll", "link": true, - "logo": "<\/path><\/path><\/g><\/svg>" + "logo": "<\/path><\/path><\/g><\/svg>" }, "link": "http:\/\/www.crunchyroll.com\/attack-on-titan", "subs": ["en"], @@ -24,7 +24,7 @@ "meta": { "name": "Hulu", "link": true, - "logo": "<\/path><\/svg>" + "logo": "<\/path><\/svg>" }, "link": "http:\/\/www.hulu.com\/attack-on-titan", "subs": ["en"], @@ -33,7 +33,7 @@ "meta": { "name": "Funimation", "link": true, - "logo": "<\/path><\/svg>" + "logo": "<\/path><\/svg>" }, "link": "http:\/\/www.funimation.com\/shows\/attack-on-titan\/videos\/episodes", "subs": ["en"], @@ -42,7 +42,7 @@ "meta": { "name": "Netflix", "link": false, - "logo": "<\/path><\/svg>" + "logo": "<\/path><\/svg>" }, "link": "t", "subs": ["en"],