diff --git a/README.md b/README.md index 0f4ce9af..dfde7c1c 100644 --- a/README.md +++ b/README.md @@ -31,10 +31,10 @@ A self-hosted client that allows custom formatting of data from the hummingbird ### Requirements -* PHP 5.5+ +* PHP 7.0+ * PDO SQLite or PDO PostgreSQL (For collection tab) * GD -* Redis and PHP Redis extension (optional, for caching) +* Redis or Memcached for caching ### Installation @@ -48,23 +48,14 @@ or 3. Configure settings in `app/config/config.toml` to your liking 4. Create the following directories if they don't exist, and make sure they are world writable * public/js/cache - * public/images/manga - * public/images/anime 5. Make sure the `console` script is executable -6. To batch-create image thumbnails, run `console cache-images`. ### Server Setup #### Caching -To setup API caching, choose a caching method: -* Database - 1. Follow the instructions for the Anime Collection setup below. - 2. Set `cache_driver` in `app/config/config.toml` to 'SQLDriver' +Update `app/config/cache.toml` based on the instructions [here](https://git.timshomepage.net/timw4mail/banker/blob/master/README.md). -* Redis - 1. Copy `app/redis.toml.example` to `app/redis.toml`, and edit to match your setup. - 2. Set `cache_driver` in `app/config/config.toml` to 'RedisDriver' #### nginx Basic nginx setup diff --git a/app/config/cache.toml.example b/app/config/cache.toml.example index 8822d336..50e19777 100644 --- a/app/config/cache.toml.example +++ b/app/config/cache.toml.example @@ -4,7 +4,7 @@ # See https://git.timshomepage.net/timw4mail/banker for more information -# Available drivers are memcache, memcached, redis or null +# Available drivers are apcu, memcache, memcached, redis or null # Null cache driver means no caching driver = "redis" diff --git a/app/views/collection/cover.php b/app/views/collection/cover.php index 22db0c4a..919b2d54 100644 --- a/app/views/collection/cover.php +++ b/app/views/collection/cover.php @@ -11,11 +11,11 @@
- <?= $item['title'] ?> cover image + <?= $item['title'] ?> cover image
diff --git a/app/views/collection/edit.php b/app/views/collection/edit.php index 37ee2ddf..0d1add75 100644 --- a/app/views/collection/edit.php +++ b/app/views/collection/edit.php @@ -8,12 +8,12 @@

html($item['title']) ?>

-

html($item['alternate_title']) ?>

+

- img($urlGenerator->asset_url('images', 'anime', basename($item['cover_image']))); ?> + img($item['cover_image']); ?>
diff --git a/app/views/collection/list.php b/app/views/collection/list.php index 77f61bca..61840d5f 100644 --- a/app/views/collection/list.php +++ b/app/views/collection/list.php @@ -34,7 +34,7 @@ - + " . $item['alternate_title'] . "" : "" ?> diff --git a/composer.json b/composer.json index 789f6eb1..e35efd48 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "timw4mail/hummingbird-anime-client", - "description": "A self-hosted anime/manga client for hummingbird.", + "description": "A self-hosted anime/manga client for Kitsu.", "license":"MIT", "autoload": { "psr-4": { @@ -17,7 +17,7 @@ "aura/html": "2.*", "aura/router": "3.*", "aura/session": "2.*", - "aviat/banker": "dev-master", + "aviat/banker": "^1.0.0", "aviat/ion": "1.0.*", "filp/whoops": "2.0.*", "guzzlehttp/guzzle": "6.*", diff --git a/src/API/Kitsu/KitsuModel.php b/src/API/Kitsu/KitsuModel.php index d50cacb6..97bd7998 100644 --- a/src/API/Kitsu/KitsuModel.php +++ b/src/API/Kitsu/KitsuModel.php @@ -129,13 +129,19 @@ class KitsuModel { /** * Get information about a particular anime * - * @param string $animeId + * @param string $slug * @return array */ - public function getAnime(string $animeId): array + public function getAnime(string $slug): array { // @TODO catch non-existent anime - $baseData = $this->getRawMediaData('anime', $animeId); + $baseData = $this->getRawMediaData('anime', $slug); + return $this->animeTransformer->transform($baseData); + } + + public function getAnimeById(string $animeId): array + { + $baseData = $this->getRawMediaDataById('anime', $animeId); return $this->animeTransformer->transform($baseData); } @@ -212,17 +218,25 @@ class KitsuModel { 'sort' => '-updated_at' ] ]; + + $cacheItem = $this->cache->getItem($this->getHashForMethodCall($this, __METHOD__, $options)); - $data = $this->getRequest('library-entries', $options); - - foreach($data['data'] as $i => &$item) + if ( ! $cacheItem->isHit()) { - $item['manga'] = $data['included'][$i]; + $data = $this->getRequest('library-entries', $options); + + foreach($data['data'] as $i => &$item) + { + $item['manga'] = $data['included'][$i]; + } + + $transformed = $this->mangaListTransformer->transformCollection($data['data']); + + $cacheItem->set($transformed); + $cacheItem->save(); } - - $transformed = $this->mangaListTransformer->transformCollection($data['data']); - - return $transformed; + + return $cacheItem->get(); } public function search(string $type, string $query): array @@ -310,6 +324,22 @@ class KitsuModel { ->get('config') ->get(['kitsu_username']); } + + private function getRawMediaDataById(string $type, string $id): array + { + $options = [ + 'query' => [ + 'include' => ($type === 'anime') + ? 'genres,mappings,streamingLinks' + : 'genres,mappings', + ] + ]; + + $data = $this->getRequest("{$type}/{$id}", $options); + $baseData = $data['data']['attributes']; + $baseData['included'] = $data['included']; + return $baseData; + } private function getRawMediaData(string $type, string $slug): array { diff --git a/src/API/Kitsu/Transformer/AnimeTransformer.php b/src/API/Kitsu/Transformer/AnimeTransformer.php index 66f344f1..a72a0779 100644 --- a/src/API/Kitsu/Transformer/AnimeTransformer.php +++ b/src/API/Kitsu/Transformer/AnimeTransformer.php @@ -40,6 +40,7 @@ class AnimeTransformer extends AbstractTransformer { $titles = Kitsu::filterTitles($item); return [ + 'slug' => $item['slug'], 'title' => $titles[0], 'titles' => $titles, 'status' => Kitsu::getAiringStatus($item['startDate'], $item['endDate']), diff --git a/src/Command/CacheImages.php b/src/Command/CacheImages.php deleted file mode 100644 index 5f9df74f..00000000 --- a/src/Command/CacheImages.php +++ /dev/null @@ -1,116 +0,0 @@ - - * @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 Aviat\AnimeClient\Util; - -/** - * Generates thumbnail image cache so that cover images load faster - */ -class CacheImages extends BaseCommand { - - /** - * Manga Model - * - * @var Aviat\AnimeClient\Model\Manga - */ - protected $mangaModel; - - /** - * Anime Model - * - * @var Aviat\AnimeClient\Model\Anime - */ - protected $animeModel; - - /** - * Miscellaneous helper methods - * - * @var Aviat\AnimeClient\Util - */ - protected $util; - - /** - * Convert manga images - * - * @throws \ConsoleKit\ConsoleException - * @return void - */ - protected function getMangaImages() - { - $raw_list = $this->mangaModel->_get_list_from_api(); - $manga_list = array_column($raw_list, 'manga'); - - $total = count($raw_list); - $current = 0; - foreach($manga_list as $item) - { - $this->util->get_cached_image($item['poster_image'], $item['id'], 'manga'); - $current++; - - echo "Cached {$current} of {$total} manga images. \n"; - } - } - - /** - * Convert anime images - * - * @throws \ConsoleKit\ConsoleException - * @return void - */ - protected function getAnimeImages() - { - $raw_list = $this->animeModel->get_raw_list(); - - $total = count($raw_list); - $current = 0; - foreach($raw_list as $item) - { - $this->util->get_cached_image($item['anime']['cover_image'], $item['anime']['slug'], 'anime'); - $current++; - - echo "Cached {$current} of {$total} anime images. \n"; - } - } - - /** - * 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->util = new Util($this->container); - $this->animeModel = $this->container->get('anime-model'); - $this->mangaModel = $this->container->get('manga-model'); - - $this->echoBox('Starting image conversion'); - - $this->echoBox('Converting manga images'); - $this->getMangaImages(); - - $this->echoBox('Converting anime images'); - $this->getAnimeImages(); - - $this->echoBox('Finished image conversion'); - } -} -// End of CacheImages.php \ No newline at end of file diff --git a/src/Command/ClearCache.php b/src/Command/ClearCache.php index d47bc0ba..2e58019d 100644 --- a/src/Command/ClearCache.php +++ b/src/Command/ClearCache.php @@ -32,7 +32,7 @@ class ClearCache extends BaseCommand { { $this->setContainer($this->setupContainer()); $cache = $this->container->get('cache'); - $cache->purge(); + $cache->clear(); $this->echoBox('API Cache has been cleared.'); } diff --git a/src/Model/Anime.php b/src/Model/Anime.php index 7d644bc2..ba2eb889 100644 --- a/src/Model/Anime.php +++ b/src/Model/Anime.php @@ -71,14 +71,19 @@ class Anime extends API { } /** - * Get information about an anime from its id + * Get information about an anime from its slug * - * @param string $anime_id + * @param string $slug * @return array */ - public function getAnime($anime_id) + public function getAnime($slug) { - return $this->kitsuModel->getAnime($anime_id); + return $this->kitsuModel->getAnime($slug); + } + + public function getAnimeById($anime_id) + { + return $this->kitsuModel->getAnimeById($anime_id); } /** diff --git a/src/Model/AnimeCollection.php b/src/Model/AnimeCollection.php index 83c64123..da8e989a 100644 --- a/src/Model/AnimeCollection.php +++ b/src/Model/AnimeCollection.php @@ -16,6 +16,7 @@ namespace Aviat\AnimeClient\Model; +use Aviat\AnimeClient\API\Kitsu; use Aviat\Ion\Di\ContainerInterface; use Aviat\Ion\Json; use PDO; @@ -25,19 +26,6 @@ use PDO; */ class AnimeCollection extends Collection { - /** - * Constructor - * - * @param ContainerInterface $container - */ - public function __construct(ContainerInterface $container) - { - parent::__construct($container); - - // Do an import if an import file exists - $this->json_import(); - } - /** * Get collection from the database, and organize by media type * @@ -131,20 +119,17 @@ class AnimeCollection extends Collection { */ public function add($data) { - $anime = (object)$this->anime_model->get_anime($data['id']); - + $anime = (object)$this->anime_model->getAnimeById($data['id']); $util = $this->container->get('util'); $this->db->set([ 'hummingbird_id' => $data['id'], 'slug' => $anime->slug, - 'title' => $anime->title, - 'alternate_title' => $anime->alternate_title, + 'title' => array_shift($anime->titles), + 'alternate_title' => implode('
', $anime->titles), 'show_type' => $anime->show_type, 'age_rating' => $anime->age_rating, - 'cover_image' => basename( - $util->get_cached_image($anime->cover_image, $anime->slug, 'anime') - ), + 'cover_image' => $anime->cover_image, 'episode_count' => $anime->episode_count, 'episode_length' => $anime->episode_length, 'media_id' => $data['media_id'], @@ -212,46 +197,6 @@ class AnimeCollection extends Collection { return $query->fetch(PDO::FETCH_ASSOC); } - /** - * Import anime into collection from a json file - * - * @return void - */ - private function json_import() - { - if ( ! file_exists('import.json') OR ! $this->valid_database) - { - return; - } - - $anime = Json::decodeFile("import.json"); - - foreach ($anime as $item) - { - $util = $this->container->get('util'); - - $this->db->set([ - 'hummingbird_id' => $item->id, - 'slug' => $item->slug, - 'title' => $item->title, - 'alternate_title' => $item->alternate_title, - 'show_type' => $item->show_type, - 'age_rating' => $item->age_rating, - 'cover_image' => basename( - $util->get_cached_image($item->cover_image, $item->slug, 'anime') - ), - 'episode_count' => $item->episode_count, - 'episode_length' => $item->episode_length - ])->insert('anime_set'); - } - - // Delete the import file - unlink('import.json'); - - // Update genre info - $this->update_genres(); - } - /** * Update genre information for selected anime * @@ -264,17 +209,17 @@ class AnimeCollection extends Collection { extract($genre_info); // Get api information - $anime = $this->anime_model->get_anime($anime_id); + $anime = $this->anime_model->getAnimeById($anime_id); foreach ($anime['genres'] as $genre) { // Add genres that don't currently exist - if ( ! in_array($genre['name'], $genres)) + if ( ! in_array($genre, $genres)) { - $this->db->set('genre', $genre['name']) + $this->db->set('genre', $genre) ->insert('genres'); - $genres[] = $genre['name']; + $genres[] = $genre; } // Update link table @@ -282,13 +227,13 @@ class AnimeCollection extends Collection { $flipped_genres = array_flip($genres); $insert_array = [ - 'hummingbird_id' => $anime['id'], - 'genre_id' => $flipped_genres[$genre['name']] + 'hummingbird_id' => $anime_id, + 'genre_id' => $flipped_genres[$genre] ]; - if (array_key_exists($anime['id'], $links)) + if (array_key_exists($anime_id, $links)) { - if ( ! in_array($flipped_genres[$genre['name']], $links[$anime['id']])) + if ( ! in_array($flipped_genres[$genre], $links[$anime_id])) { $this->db->set($insert_array)->insert('genre_anime_set_link'); } diff --git a/src/Model/Collection.php b/src/Model/Collection.php index 1abf5bfc..e3ff607c 100644 --- a/src/Model/Collection.php +++ b/src/Model/Collection.php @@ -16,6 +16,8 @@ namespace Aviat\AnimeClient\Model; + +use Aviat\Ion\Di\ContainerAware; use Aviat\Ion\Di\ContainerInterface; use Aviat\Ion\Model\DB; use PDO; @@ -25,6 +27,8 @@ use PDOException; * Base model for anime and manga collections */ class Collection extends DB { + + use ContainerAware; /** * Anime API Model @@ -45,6 +49,8 @@ class Collection extends DB { */ public function __construct(ContainerInterface $container) { + $this->container = $container; + parent::__construct($container->get('config')); try @@ -53,8 +59,8 @@ class Collection extends DB { } catch (PDOException $e) { - $this->valid_database = FALSE; - return FALSE; + //$this->valid_database = FALSE; + //return FALSE; } $this->anime_model = $container->get('anime-model'); diff --git a/tests/test_data/Kitsu/animeAfterTransform.json b/tests/test_data/Kitsu/animeAfterTransform.json index 0ac1b38f..af3e16ca 100644 --- a/tests/test_data/Kitsu/animeAfterTransform.json +++ b/tests/test_data/Kitsu/animeAfterTransform.json @@ -47,5 +47,6 @@ "link": "t", "subs": ["en"], "dubs": ["ja"] - }] + }], + "slug": "attack-on-titan" } \ No newline at end of file