diff --git a/app/views/manga/edit.php b/app/views/manga/edit.php index a6eefb27..024dce2b 100644 --- a/app/views/manga/edit.php +++ b/app/views/manga/edit.php @@ -25,9 +25,9 @@ diff --git a/public/css/base.css b/public/css/base.css index b80a42b1..a2483688 100644 --- a/public/css/base.css +++ b/public/css/base.css @@ -1174,8 +1174,8 @@ a:hover, a:active { position:absolute; top: 86px; top: calc(50% - 58.5px); - left: 5px; - left: calc(50% - 95px); + left: 43.5px; + left: calc(50% - 66.5px); } /* ----------------------------------------------------------------------------- @@ -1222,11 +1222,16 @@ a:hover, a:active { position:absolute; display:block; top:0; + left:0; height:100%; width:100%; vertical-align:middle; } +#series_list .name small { + color: #fff; +} + /* ---------------------------------------------------------------------------- Details page styles -----------------------------------------------------------------------------*/ diff --git a/public/css/base.myth.css b/public/css/base.myth.css index b91f8be5..6e243f99 100644 --- a/public/css/base.myth.css +++ b/public/css/base.myth.css @@ -439,8 +439,8 @@ a:hover, a:active { position:absolute; top: 86px; top: calc(50% - 58.5px); - left: 5px; - left: calc(50% - 95px); + left: 43.5px; + left: calc(50% - 66.5px); } @@ -484,10 +484,14 @@ a:hover, a:active { position:absolute; display:block; top:0; + left:0; height:100%; width:100%; vertical-align:middle; } +#series_list .name small { + color: #fff; +} /* ---------------------------------------------------------------------------- Details page styles diff --git a/public/js/manga_edit.js b/public/js/manga_edit.js old mode 100755 new mode 100644 index d59a9587..a97fb35f --- a/public/js/manga_edit.js +++ b/public/js/manga_edit.js @@ -18,12 +18,16 @@ completed = 0; } + // Setup the update data let data = { - id: manga_id + id: manga_id, + data: { + progress: completed + } }; // Update the total count - data[type + "s_read"] = ++completed; + data['data']['progress'] = ++completed; _.ajax(_.url('/manga/update'), { data: data, diff --git a/public/templates/anime-ajax-search-results.html b/public/templates/anime-ajax-search-results.html index a28371f7..c5a64562 100644 --- a/public/templates/anime-ajax-search-results.html +++ b/public/templates/anime-ajax-search-results.html @@ -4,10 +4,13 @@ diff --git a/public/templates/manga-ajax-search-results.html b/public/templates/manga-ajax-search-results.html index 25594c2b..c5a64562 100644 --- a/public/templates/manga-ajax-search-results.html +++ b/public/templates/manga-ajax-search-results.html @@ -1,10 +1,18 @@ -{{#search}} +{{#data}} -{{/search}} \ No newline at end of file +{{/data}} \ No newline at end of file diff --git a/src/API/Kitsu.php b/src/API/Kitsu.php index 9bee9b22..30bd94c6 100644 --- a/src/API/Kitsu.php +++ b/src/API/Kitsu.php @@ -16,7 +16,11 @@ namespace Aviat\AnimeClient\API; -use Aviat\AnimeClient\API\Kitsu\Enum\{AnimeAiringStatus, AnimeWatchingStatus}; +use Aviat\AnimeClient\API\Kitsu\Enum\{ + AnimeAiringStatus, + AnimeWatchingStatus, + MangaReadingStatus +}; use DateTimeImmutable; /** @@ -41,6 +45,17 @@ class Kitsu { ]; } + public static function getStatusToMangaSelectMap() + { + return [ + MangaReadingStatus::READING => 'Currently Reading', + MangaReadingStatus::PLAN_TO_READ => 'Plan to Read', + MangaReadingStatus::COMPLETED => 'Completed', + MangaReadingStatus::ON_HOLD => 'On Hold', + MangaReadingStatus::DROPPED => 'Dropped' + ]; + } + /** * Determine whether an anime is airing, finished airing, or has not yet aired * diff --git a/src/API/Kitsu/Enum/MangaReadingStatus.php b/src/API/Kitsu/Enum/MangaReadingStatus.php index 20d00712..15351824 100644 --- a/src/API/Kitsu/Enum/MangaReadingStatus.php +++ b/src/API/Kitsu/Enum/MangaReadingStatus.php @@ -22,10 +22,10 @@ use Aviat\Ion\Enum as BaseEnum; * Possible values for current reading status of manga */ class MangaReadingStatus extends BaseEnum { - const READING = 1; - const PLAN_TO_READ = 2; - const DROPPED = 5; - const ON_HOLD = 4; - const COMPLETED = 3; + const READING = 'current'; + const PLAN_TO_READ = 'planned'; + const DROPPED = 'dropped'; + const ON_HOLD = 'on_hold'; + const COMPLETED = 'completed'; } // End of MangaReadingStatus.php \ No newline at end of file diff --git a/src/API/Kitsu/KitsuModel.php b/src/API/Kitsu/KitsuModel.php index 4aa4081e..ef5890f1 100644 --- a/src/API/Kitsu/KitsuModel.php +++ b/src/API/Kitsu/KitsuModel.php @@ -24,6 +24,7 @@ use Aviat\AnimeClient\API\Kitsu\Transformer\{ use Aviat\Ion\Di\ContainerAware; use Aviat\Ion\Json; use GuzzleHttp\Exception\ClientException; +use PHP_CodeSniffer\Tokenizers\JS; /** * Kitsu API Model @@ -32,9 +33,6 @@ class KitsuModel { use ContainerAware; use KitsuTrait; - const CLIENT_ID = 'dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd'; - const CLIENT_SECRET = '54d7307928f63414defd96399fc31ba847961ceaecef3a5fd93144e960c0e151'; - /** * Class to map anime list items * to a common format used by @@ -101,7 +99,8 @@ class KitsuModel { */ public function authenticate(string $username, string $password) { - $data = $this->postRequest(K::AUTH_URL, [ + $response = $this->getResponse('POST', K::AUTH_URL, [ + 'headers' => [], 'form_params' => [ 'grant_type' => 'password', 'username' => $username, @@ -109,6 +108,8 @@ class KitsuModel { ] ]); + $data = Json::decode((string)$response->getBody()); + if (array_key_exists('access_token', $data)) { return $data['access_token']; @@ -161,7 +162,6 @@ class KitsuModel { $data = $this->getRequest('library-entries', $options); $included = K::organizeIncludes($data['included']); -/*?>
&$item) { @@ -173,8 +173,6 @@ class KitsuModel { { $item['genres'][] = $included['genres'][$id]['name']; } - - // $item['genres'] = array_pluck($genres, 'name'); } $transformed = $this->animeListTransformer->transformCollection($data['data']); @@ -223,7 +221,15 @@ class KitsuModel { 'include' => 'media' ]; - return $this->getRequest($type, $options); + $raw = $this->getRequest($type, $options); + + foreach ($raw['data'] as &$item) + { + $item['attributes']['titles'] = K::filterTitles($item['attributes']); + array_shift($item['attributes']['titles']); + } + + return $raw; } public function getListItem(string $listId): array @@ -264,6 +270,11 @@ class KitsuModel { } } + public function deleteListItem(string $id): bool + { + return $this->listItem->delete($id); + } + private function getUsername(): string { return $this->getContainer() diff --git a/src/API/Kitsu/KitsuTrait.php b/src/API/Kitsu/KitsuTrait.php index 32940231..4b483ef7 100644 --- a/src/API/Kitsu/KitsuTrait.php +++ b/src/API/Kitsu/KitsuTrait.php @@ -18,12 +18,12 @@ namespace Aviat\AnimeClient\API\Kitsu; use Aviat\AnimeClient\AnimeClient; use Aviat\AnimeClient\API\GuzzleTrait; +use Aviat\AnimeClient\API\Kitsu as K; use Aviat\Ion\Json; use GuzzleHttp\Client; use GuzzleHttp\Cookie\CookieJar; use GuzzleHttp\Psr7\Response; use InvalidArgumentException; -use PHP_CodeSniffer\Tokenizers\JS; use RuntimeException; trait KitsuTrait { @@ -43,7 +43,7 @@ trait KitsuTrait { protected $defaultHeaders = [ 'User-Agent' => "Tim's Anime Client/4.0", 'Accept-Encoding' => 'application/vnd.api+json', - 'Content-Type' => 'application/vnd.api+json; charset=utf-8', + 'Content-Type' => 'application/vnd.api+json', 'client_id' => 'dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd', 'client_secret' => '54d7307928f63414defd96399fc31ba847961ceaecef3a5fd93144e960c0e151', ]; @@ -93,23 +93,22 @@ trait KitsuTrait { 'headers' => $this->defaultHeaders ]; - if ($this->getContainer()); - { - $logger = $this->container->getLogger('request'); - $sessionSegment = $this->getContainer() - ->get('session') - ->getSegment(AnimeClient::SESSION_SEGMENT); + $logger = $this->container->getLogger('request'); + $sessionSegment = $this->getContainer() + ->get('session') + ->getSegment(AnimeClient::SESSION_SEGMENT); - if ($sessionSegment->get('auth_token') !== null) - { - $token = $sessionSegment->get('auth_token'); - $defaultOptions['headers']['Authorization'] = "bearer {$token}"; - } - $logger->debug(Json::encode(func_get_args())); + if ($sessionSegment->get('auth_token') !== null && $url !== K::AUTH_URL) + { + $token = $sessionSegment->get('auth_token'); + $defaultOptions['headers']['Authorization'] = "bearer {$token}"; } $options = array_merge($defaultOptions, $options); + $logger->debug(Json::encode([$type, $url])); + $logger->debug(Json::encode($options)); + return $this->client->request($type, $url, $options); } @@ -131,7 +130,7 @@ trait KitsuTrait { $response = $this->getResponse($type, $url, $options); - if ((int) $response->getStatusCode() !== 200) + if ((int) $response->getStatusCode() > 299 || (int) $response->getStatusCode() < 200) { if ($logger) { @@ -192,7 +191,7 @@ trait KitsuTrait { $logger->warning($response->getBody()); } - throw new RuntimeException($response->getBody()); + // throw new RuntimeException($response->getBody()); } return JSON::decode($response->getBody(), TRUE); diff --git a/src/API/Kitsu/ListItem.php b/src/API/Kitsu/ListItem.php index b59c74c6..67c8c566 100644 --- a/src/API/Kitsu/ListItem.php +++ b/src/API/Kitsu/ListItem.php @@ -37,14 +37,33 @@ class ListItem extends AbstractListItem { public function create(array $data): bool { - // TODO: Implement create() method. - return false; + $response = $this->getResponse('post', 'library-entries', [ + 'body' => [ + 'type' => 'libraryEntries', + 'attributes' => [ + 'status' => $data['status'], + 'progress' => $data['progress'] ?? 0 + ], + 'relationships' => [ + 'user' => [ + 'id' => $data['user_id'], + 'type' => 'users' + ], + 'media' => [ + 'id' => $data['id'], + 'type' => $data['type'] + ] + ] + ] + ]); + + return ($response->getStatusCode() === 201); } public function delete(string $id): bool { - // TODO: Implement delete() method. - return false; + $response = $this->getResponse('DELETE', "library-entries/{$id}"); + return ($response->getStatusCode() === 204); } public function get(string $id): array diff --git a/src/API/Kitsu/Transformer/MangaListTransformer.php b/src/API/Kitsu/Transformer/MangaListTransformer.php index e826d3b5..7e5e0645 100644 --- a/src/API/Kitsu/Transformer/MangaListTransformer.php +++ b/src/API/Kitsu/Transformer/MangaListTransformer.php @@ -91,18 +91,18 @@ class MangaListTransformer extends AbstractTransformer { $map = [ 'id' => $item['id'], - 'manga_id' => $item['manga_id'], - 'status' => $item['status'], - 'chapters_read' => (int)$item['chapters_read'], - 'volumes_read' => (int)$item['volumes_read'], - 'rereading' => $rereading, - 'reread_count' => (int)$item['reread_count'], - 'notes' => $item['notes'], + 'data' => [ + 'status' => $item['status'], + 'progress' => (int)$item['chapters_read'], + 'reconsuming' => $rereading, + 'reconsumeCount' => (int)$item['reread_count'], + 'notes' => $item['notes'], + ], ]; if ($item['new_rating'] !== $item['old_rating'] && $item['new_rating'] !== "") { - $map['rating'] = ($item['new_rating'] > 0) + $map['data']['rating'] = ($item['new_rating'] > 0) ? $item['new_rating'] / 2 : $item['old_rating'] / 2; } diff --git a/src/Controller/Anime.php b/src/Controller/Anime.php index 1c108a97..4809e21e 100644 --- a/src/Controller/Anime.php +++ b/src/Controller/Anime.php @@ -260,8 +260,6 @@ class Anime extends BaseController { } $response = $this->model->updateLibraryItem($data); -//echo JSON::encode($response); -//die(); // $this->cache->purge(); $this->outputJSON($response['body'], $response['statusCode']); @@ -274,12 +272,13 @@ class Anime extends BaseController { */ public function delete() { - $response = $this->model->delete($this->request->getParsedBody()); + $body = $this->request->getParsedBody(); + $response = $this->model->deleteLibraryItem($body['id']); - if ((bool)$response['body'] === TRUE) + if ((bool)$response === TRUE) { $this->set_flash_message("Successfully deleted anime.", 'success'); - $this->cache->purge(); + // $this->cache->purge(); } else { diff --git a/src/Controller/Manga.php b/src/Controller/Manga.php index d8149885..a0113207 100644 --- a/src/Controller/Manga.php +++ b/src/Controller/Manga.php @@ -17,6 +17,7 @@ namespace Aviat\AnimeClient\Controller; use Aviat\AnimeClient\Controller; +use Aviat\AnimeClient\API\Kitsu; use Aviat\AnimeClient\API\Kitsu\Enum\MangaReadingStatus; use Aviat\AnimeClient\API\Kitsu\Transformer\MangaListTransformer; use Aviat\AnimeClient\Model\Manga as MangaModel; @@ -166,7 +167,7 @@ class Manga extends Controller { $this->outputHTML('manga/edit', [ 'title' => $title, - 'status_list' => MangaReadingStatus::getConstList(), + 'status_list' => Kitsu::getStatusToMangaSelectMap(), 'item' => $item, 'action' => $this->container->get('url-generator') ->url('/manga/update_form'), @@ -185,50 +186,54 @@ class Manga extends Controller { } /** - * Update an anime item via a form submission + * Update an manga item via a form submission * * @return void */ public function form_update() { - $post_data = $this->request->getParsedBody(); + $data = $this->request->getParsedBody(); // Do some minor data manipulation for // large form-based updates $transformer = new MangaListTransformer(); - $post_data = $transformer->untransform($post_data); - $full_result = $this->model->update($post_data); + $post_data = $transformer->untransform($data); + $full_result = $this->model->updateLibraryItem($post_data); - $result = Json::decode((string)$full_result['body']); - - if ((int)$full_result['statusCode'] === 200) + if ($full_result['statusCode'] === 200) { - $m =& $result['manga'][0]; - $title = ( ! empty($m['english_title'])) - ? "{$m['romaji_title']} ({$m['english_title']})" - : "{$m['romaji_title']}"; - - $this->set_flash_message("Successfully updated {$title}.", 'success'); - $this->cache->purge(); + $this->set_flash_message("Successfully updated manga.", 'success'); + // $this->cache->purge(); } else { $this->set_flash_message('Failed to update manga.', 'error'); + } $this->session_redirect(); } /** - * Update an anime item + * Update a manga item * - * @return boolean|null + * @return void */ public function update() { - $result = $this->model->update($this->request->getParsedBody()); - $this->cache->purge(); - $this->outputJSON($result['body'], $result['statusCode']); + if ($this->request->getHeader('content-type')[0] === 'application/json') + { + $data = JSON::decode((string)$this->request->getBody()); + } + else + { + $data = $this->request->getParsedBody(); + } + + $response = $this->model->updateLibraryItem($data); + + // $this->cache->purge(); + $this->outputJSON($response['body'], $response['statusCode']); } /** diff --git a/src/Model/API.php b/src/Model/API.php index a0ff29db..158639ae 100644 --- a/src/Model/API.php +++ b/src/Model/API.php @@ -38,10 +38,10 @@ class API extends Model { */ protected $cache; - /** - * Default settings for Guzzle - * @var array - */ + /** + * Default settings for Guzzle + * @var array + */ protected $connectionDefaults = []; /** @@ -75,4 +75,4 @@ class API extends Model { array_multisort($sort, SORT_ASC, $array); } } -// End of BaseApiModel.php \ No newline at end of file +// End of BaseApiModel.php diff --git a/src/Model/Anime.php b/src/Model/Anime.php index 288157d3..553d18e8 100644 --- a/src/Model/Anime.php +++ b/src/Model/Anime.php @@ -115,5 +115,10 @@ class Anime extends API { { return $this->kitsuModel->updateListItem($data); } + + public function deleteLibraryItem($id): bool + { + return $this->kitsuModel->deleteListItem($id); + } } // End of AnimeModel.php \ No newline at end of file diff --git a/src/Model/Collection.php b/src/Model/Collection.php index 327bde04..12184c7d 100644 --- a/src/Model/Collection.php +++ b/src/Model/Collection.php @@ -18,6 +18,7 @@ namespace Aviat\AnimeClient\Model; use Aviat\Ion\Di\ContainerInterface; use Aviat\Ion\Model\DB; +use PDO; use PDOException; /** diff --git a/src/Model/Manga.php b/src/Model/Manga.php index bb61cb12..7df7a4c3 100644 --- a/src/Model/Manga.php +++ b/src/Model/Manga.php @@ -95,6 +95,28 @@ class Manga extends API return $this->kitsuModel->getListItem($itemId); } + /** + * Update a list entry + * + * @param array $data + * @return array + */ + public function updateLibraryItem(array $data): array + { + return $this->kitsuModel->updateListItem($data); + } + + /** + * Search for anime by name + * + * @param string $name + * @return array + */ + public function search($name) + { + return $this->kitsuModel->search('manga', $name); + } + /** * Map transformed anime data to be organized by reading status * diff --git a/src/Util.php b/src/Util.php index 9031de31..e4743034 100644 --- a/src/Util.php +++ b/src/Util.php @@ -109,97 +109,5 @@ class Util { { return ! $this->is_view_page(); } +} - /** - * Get the path of the cached version of the image. Create the cached image - * if the file does not already exist - * - * @codeCoverageIgnore - * @param string $api_path - The original image url - * @param string $series_slug - The part of the url with the series name, becomes the image name - * @param string $type - Anime or Manga, controls cache path - * @return string - the frontend path for the cached image - * @throws \DomainException - */ - public function get_cached_image($api_path, $series_slug, $type = "anime") - { - $path_parts = explode('?', basename($api_path)); - $path = current($path_parts); - $ext_parts = explode('.', $path); - $ext = end($ext_parts); - - // Workaround for some broken file extensions - if ($ext === "jjpg") - { - $ext = "jpg"; - } - - // Failsafe for weird urls - if (strlen($ext) > 3) - { - return $api_path; - } - - $img_cache_path = $this->config->get('img_cache_path'); - $cached_image = "{$series_slug}.{$ext}"; - $cached_path = "{$img_cache_path}/{$type}/{$cached_image}"; - - // Cache the file if it doesn't already exist - if ( ! file_exists($cached_path)) - { - if (function_exists('curl_init')) - { - $ch = curl_init($api_path); - $fp = fopen($cached_path, 'wb'); - curl_setopt_array($ch, [ - CURLOPT_FILE => $fp, - CURLOPT_HEADER => 0 - ]); - curl_exec($ch); - curl_close($ch); - fclose($fp); - } - else if (ini_get('allow_url_fopen')) - { - copy($api_path, $cached_path); - } - else - { - throw new DomainException("Couldn't cache images because they couldn't be downloaded."); - } - - // Resize the image - if ($type === 'anime') - { - $resize_width = 220; - $resize_height = 319; - $this->_resize($cached_path, $resize_width, $resize_height); - } - } - - return "/public/images/{$type}/{$cached_image}"; - } - - /** - * Resize an image - * - * @codeCoverageIgnore - * @param string $path - * @param string $width - * @param string $height - * @return void - */ - private function _resize($path, $width, $height) - { - try - { - $img = new SimpleImage($path); - $img->resize($width, $height)->save(); - } - catch (Exception $e) - { - // Catch image errors, since they don't otherwise affect - // functionality - } - } -} \ No newline at end of file