diff --git a/composer.json b/composer.json index 48f5b5ec..3d22556e 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,8 @@ "aura/session": "^2.0", "aviat/banker": "^1.0.0", "aviat/ion": "^2.3.0", + "ext-gd":"*", + "ext-pdo": "*", "maximebf/consolekit": "^1.0", "monolog/monolog": "^1.0", "psr/http-message": "~1.0", diff --git a/src/API/Anilist/AnilistTrait.php b/src/API/Anilist/AnilistTrait.php index 39b01d3b..8d0026f0 100644 --- a/src/API/Anilist/AnilistTrait.php +++ b/src/API/Anilist/AnilistTrait.php @@ -84,26 +84,30 @@ trait AnilistTrait { ->get('session') ->getSegment(SESSION_SEGMENT); - $authenticated = $sessionSegment->get('auth_token') !== NULL; + //$authenticated = $sessionSegment->get('auth_token') !== NULL; - if ($authenticated) + //if ($authenticated) { $request = $request->setAuth('bearer', $anilistConfig['access_token']); } - if (array_key_exists('form_params', $options)) { + if (array_key_exists('form_params', $options)) + { $request = $request->setFormFields($options['form_params']); } - if (array_key_exists('query', $options)) { + if (array_key_exists('query', $options)) + { $request = $request->setQuery($options['query']); } - if (array_key_exists('body', $options)) { + if (array_key_exists('body', $options)) + { $request = $request->setJsonBody($options['body']); } - if (array_key_exists('headers', $options)) { + if (array_key_exists('headers', $options)) + { $request = $request->setHeaders($options['headers']); } @@ -148,7 +152,8 @@ trait AnilistTrait { public function mutateRequest (string $name, array $variables = []): Request { $file = realpath(__DIR__ . "/GraphQL/Mutations/{$name}.graphql"); - if (!file_exists($file)) { + if (!file_exists($file)) + { throw new \LogicException('GraphQL mutation file does not exist.'); } @@ -161,7 +166,8 @@ trait AnilistTrait { if (!empty($variables)) { $body['variables'] = []; - foreach ($variables as $key => $val) { + foreach ($variables as $key => $val) + { $body['variables'][$key] = $val; } } @@ -211,7 +217,8 @@ trait AnilistTrait { private function getResponseFromRequest(Request $request): Response { $logger = NULL; - if ($this->getContainer()) { + if ($this->getContainer()) + { $logger = $this->container->getLogger('anilist-request'); } @@ -236,15 +243,22 @@ trait AnilistTrait { */ protected function postRequest(array $options = []): array { + $response = $this->getResponse(Anilist::BASE_URL, $options); + $validResponseCodes = [200, 201]; + $logger = NULL; if ($this->getContainer()) { $logger = $this->container->getLogger('anilist-request'); + $logger->debug('Anilist response', [ + 'status' => $response->getStatus(), + 'reason' => $response->getReason(), + 'body' => $response->getBody(), + 'headers' => $response->getHeaders(), + //'requestHeaders' => $request->getHeaders(), + ]); } - $response = $this->getResponse(Anilist::BASE_URL, $options); - $validResponseCodes = [200, 201]; - if ( ! \in_array($response->getStatus(), $validResponseCodes, TRUE)) { if ($logger) diff --git a/src/API/Anilist/GraphQL/Mutations/CreateFullMediaListEntry.graphql b/src/API/Anilist/GraphQL/Mutations/CreateFullMediaListEntry.graphql new file mode 100644 index 00000000..623cb085 --- /dev/null +++ b/src/API/Anilist/GraphQL/Mutations/CreateFullMediaListEntry.graphql @@ -0,0 +1,27 @@ +mutation ( + $id: Int, + $notes: String, + $private: Boolean, + $progress: Int, + $repeat: Int, + $status: MediaListStatus, + $score: Int, +) { + SaveMediaListEntry ( + mediaId: $id, + notes: $notes, + private: $private, + progress: $progress, + repeat: $repeat, + scoreRaw: $score, + status: $status + ) { + mediaId + notes + private + progress + repeat + score(format: POINT_10) + status + } +} \ No newline at end of file diff --git a/src/API/Anilist/GraphQL/Queries/ListItemIdByMalId.graphql b/src/API/Anilist/GraphQL/Queries/ListItemIdByMalId.graphql index b73567ba..704f006d 100644 --- a/src/API/Anilist/GraphQL/Queries/ListItemIdByMalId.graphql +++ b/src/API/Anilist/GraphQL/Queries/ListItemIdByMalId.graphql @@ -1,5 +1,5 @@ -query ($id: Int) { - Media (idMal: $id) { +query ($id: Int, $type: MediaType) { + Media (idMal: $id, type: $type) { mediaListEntry { id userId diff --git a/src/API/Anilist/GraphQL/Queries/ListItemIdByMediaId.graphql b/src/API/Anilist/GraphQL/Queries/ListItemIdByMediaId.graphql new file mode 100644 index 00000000..dec155da --- /dev/null +++ b/src/API/Anilist/GraphQL/Queries/ListItemIdByMediaId.graphql @@ -0,0 +1,7 @@ +query ($id: Int, $userName: String) { + MediaList (mediaId: $id, userName: $userName) { + id + userId + mediaId + } +} \ No newline at end of file diff --git a/src/API/Anilist/GraphQL/Queries/SyncUserList.graphql b/src/API/Anilist/GraphQL/Queries/SyncUserList.graphql new file mode 100644 index 00000000..f5c97d18 --- /dev/null +++ b/src/API/Anilist/GraphQL/Queries/SyncUserList.graphql @@ -0,0 +1,27 @@ +query ($name: String, $type: MediaType) { + MediaListCollection(userName: $name, type: $type) { + lists { + entries { + id + mediaId + score + progress + progressVolumes + repeat + private + notes + status + media { + id + idMal + title { + romaji + english + native + userPreferred + } + } + } + } + } +} \ No newline at end of file diff --git a/src/API/Anilist/GraphQL/Queries/UserAnimeList.graphql b/src/API/Anilist/GraphQL/Queries/UserAnimeList.graphql index e7af2e99..ea25ddca 100644 --- a/src/API/Anilist/GraphQL/Queries/UserAnimeList.graphql +++ b/src/API/Anilist/GraphQL/Queries/UserAnimeList.graphql @@ -24,7 +24,6 @@ query ($name: String) { status episodes season - seasonYear genres synonyms countryOfOrigin diff --git a/src/API/Anilist/ListItem.php b/src/API/Anilist/ListItem.php index 966d3bfa..780e3063 100644 --- a/src/API/Anilist/ListItem.php +++ b/src/API/Anilist/ListItem.php @@ -19,6 +19,7 @@ namespace Aviat\AnimeClient\API\Anilist; use Amp\Artax\Request; use Aviat\AnimeClient\API\ListItemInterface; +use Aviat\AnimeClient\API\Enum\AnimeWatchingStatus\Anilist as AnilistStatus; use Aviat\AnimeClient\API\Mapping\AnimeWatchingStatus; use Aviat\AnimeClient\Types\FormItemData; @@ -29,7 +30,7 @@ final class ListItem implements ListItemInterface{ use AnilistTrait; /** - * Create a list item + * Create a minimal list item * * @param array $data * @return Request @@ -39,6 +40,17 @@ final class ListItem implements ListItemInterface{ return $this->mutateRequest('CreateMediaListEntry', $data); } + /** + * Create a fleshed-out list item + * + * @param array $data + * @return Request + */ + public function createFull(array $data): Request + { + return $this->mutateRequest('CreateFullMediaListEntry', $data); + } + /** * Delete a list item * @@ -85,23 +97,25 @@ final class ListItem implements ListItemInterface{ * @return Request */ public function update(string $id, FormItemData $data): Request - { + { $array = $data->toArray(); $notes = $data['notes'] ?? ''; $progress = array_key_exists('progress', $array) ? $data['progress'] : 0; + $private = array_key_exists('private', $array) ? (bool)$data['private'] : false; $rating = array_key_exists('rating', $array) ? $data['rating'] : NULL; - $status = $data['status']; - - // @TODO Handle weirdness with reWatching - return $this->mutateRequest('UpdateMediaListEntry', [ - 'id' => $id, - 'status' => AnimeWatchingStatus::KITSU_TO_ANILIST[$status], - 'score' => $rating * 20, + $status = ($data['reconsuming'] === true) ? AnilistStatus::REPEATING : AnimeWatchingStatus::KITSU_TO_ANILIST[$data['status']]; + + $updateData = [ + 'id' => (int)$id, + 'status' => $status, + 'score' => $rating * 10, 'progress' => $progress, 'repeat' => (int)$data['reconsumeCount'], - 'private' => (bool)$data['private'], + 'private' => $private, 'notes' => $notes, - ]); + ]; + + return $this->mutateRequest('UpdateMediaListEntry', $updateData); } } \ No newline at end of file diff --git a/src/API/Anilist/Model.php b/src/API/Anilist/Model.php index 222dcad0..b6bd22d7 100644 --- a/src/API/Anilist/Model.php +++ b/src/API/Anilist/Model.php @@ -16,6 +16,8 @@ namespace Aviat\AnimeClient\API\Anilist; +use InvalidArgumentException; + use Amp\Artax\Request; use Aviat\AnimeClient\API\Mapping\{AnimeWatchingStatus, MangaReadingStatus}; use Aviat\AnimeClient\Types\FormItem; @@ -45,6 +47,30 @@ final class Model // ! Generic API calls // ------------------------------------------------------------------------- + /** + * Get user list data for syncing with Kitsu + * + * @param string $type + * @return array + * @throws \Aviat\Ion\Di\Exception\ContainerException + * @throws \Aviat\Ion\Di\Exception\NotFoundException + */ + public function getSyncList(string $type = 'anime'): array + { + $config = $this->container->get('config'); + $anilistUser = $config->get(['anilist', 'username']); + + if ( ! is_string($anilistUser)) + { + throw new InvalidArgumentException('Anilist username is not defined in config'); + } + + return $this->runQuery('SyncUserList', [ + 'name' => $anilistUser, + 'type' => $type, + ]); + } + /** * Create a list item * @@ -56,14 +82,22 @@ final class Model { $createData = []; - $mediaId = $this->getMediaIdFromMalId($data['mal_id'], strtoupper($type)); + $mediaId = $this->getMediaIdFromMalId($data['mal_id'], mb_strtoupper($type)); - if ($type === 'anime') { + if (empty($mediaId)) + { + throw new InvalidArgumentException('Media id missing'); + } + + if ($type === 'anime') + { $createData = [ 'id' => $mediaId, 'status' => AnimeWatchingStatus::KITSU_TO_ANILIST[$data['status']], ]; - } elseif ($type === 'manga') { + } + elseif ($type === 'manga') + { $createData = [ 'id' => $mediaId, 'status' => MangaReadingStatus::KITSU_TO_ANILIST[$data['status']], @@ -73,15 +107,32 @@ final class Model return $this->listItem->create($createData, $type); } + /** + * Create a list item with all the relevant data + * + * @param array $data + * @param string $type + * @return Request + */ + public function createFullListItem(array $data, string $type = 'anime'): Request + { + $createData = $data['data']; + $mediaId = $this->getMediaIdFromMalId($data['mal_id']); + + $createData['id'] = $mediaId; + + return $this->listItem->createFull($createData); + } + /** * Get the data for a specific list item, generally for editing * * @param string $malId - The unique identifier of that list item * @return mixed */ - public function getListItem(string $malId): array + public function getListItem(string $malId, string $type): array { - $id = $this->getListIdFromMalId($malId); + $id = $this->getListIdFromMalId($malId, $type); $data = $this->listItem->get($id)['data']; @@ -96,9 +147,9 @@ final class Model * @param FormItem $data * @return Request */ - public function incrementListItem(FormItem $data): Request + public function incrementListItem(FormItem $data, string $type): Request { - $id = $this->getListIdFromMalId($data['mal_id']); + $id = $this->getListIdFromMalId($data['mal_id'], $type); return $this->listItem->increment($id, $data['data']); } @@ -107,11 +158,12 @@ final class Model * Modify a list item * * @param FormItem $data + * @param int [$id] * @return Request */ - public function updateListItem(FormItem $data): Request + public function updateListItem(FormItem $data, string $type): Request { - $id = $this->getListIdFromMalId($data['mal_id']); + $id = $this->getListIdFromMalId($data['mal_id'], mb_strtoupper($type)); return $this->listItem->update($id, $data['data']); } @@ -122,9 +174,9 @@ final class Model * @param string $malId - The id of the list item to remove * @return Request */ - public function deleteListItem(string $malId): Request + public function deleteListItem(string $malId, string $type): Request { - $item_id = $this->getListIdFromMalId($malId); + $item_id = $this->getListIdFromMalId($malId, $type); return $this->listItem->delete($item_id); } @@ -135,10 +187,35 @@ final class Model * @param string $malId * @return string */ - public function getListIdFromMalId(string $malId): ?string + public function getListIdFromMalId(string $malId, string $type): ?string { - $info = $this->runQuery('ListItemIdByMalId', ['id' => $malId]); - return (string)$info['data']['Media']['mediaListEntry']['id'] ?? NULL; + $mediaId = $this->getMediaIdFromMalId($malId, $type); + return $this->getListIdFromMediaId($mediaId); + } + + /** + * Get the Anilist media id from its MAL id + * this way is more accurate than getting the list item id + * directly from the MAL id + */ + private function getListIdFromMediaId(string $mediaId) + { + $config = $this->container->get('config'); + $anilistUser = $config->get(['anilist', 'username']); + + $info = $this->runQuery('ListItemIdByMediaId', [ + 'id' => $mediaId, + 'userName' => $anilistUser, + ]); + + /* dump([ + 'media_id' => $mediaId, + 'userName' => $anilistUser, + 'response' => $info, + ]); + die(); */ + + return (string)$info['data']['MediaList']['id']; } /** @@ -152,8 +229,14 @@ final class Model { $info = $this->runQuery('MediaIdByMalId', [ 'id' => $malId, - 'type' => $type + 'type' => mb_strtoupper($type), ]); + + /* dump([ + 'mal_id' => $malId, + 'response' => $info, + ]); + die(); */ return (string)$info['data']['Media']['id']; } diff --git a/src/API/Anilist/Transformer/AnimeListTransformer.php b/src/API/Anilist/Transformer/AnimeListTransformer.php index 130e1e2f..c23041b8 100644 --- a/src/API/Anilist/Transformer/AnimeListTransformer.php +++ b/src/API/Anilist/Transformer/AnimeListTransformer.php @@ -16,22 +16,51 @@ namespace Aviat\AnimeClient\API\Anilist\Transformer; -use Aviat\AnimeClient\Types\{AnimeListItem, AnimeFormItem}; +use Aviat\AnimeClient\API\Enum\AnimeWatchingStatus\Anilist as AnilistStatus; +use Aviat\AnimeClient\API\Mapping\AnimeWatchingStatus; +use Aviat\AnimeClient\Types\{Anime, AnimeListItem, AnimeFormItem}; + use Aviat\Ion\Transformer\AbstractTransformer; class AnimeListTransformer extends AbstractTransformer { public function transform($item): AnimeListItem { - dump($item); die(); - - return new AnimeListItem([ + return new AnimeListItem([]); + } + /** + * Transform Anilist list item to Kitsu form update format + * + * @param array $item + * @return AnimeFormItem + */ + public function untransform(array $item): AnimeFormItem + { + return new AnimeFormItem([ + 'id' => $item['id'], + 'mal_id' => $item['media']['idMal'], + 'data' => [ + 'notes' => $item['notes'] ?? '', + 'private' => $item['private'], + 'progress' => $item['progress'], + 'rating' => $item['score'], + 'reconsumeCount' => $item['repeat'], + 'reconsuming' => $item['status'] === AnilistStatus::REPEATING, + 'status' => AnimeWatchingStatus::ANILIST_TO_KITSU[$item['status']], + ] ]); } - public function untransform(array $item): AnimeFormItem + /** + * Transform a set of structures + * + * @param array|object $collection + * @return array + */ + public function untransformCollection($collection): array { - return new AnimeFormItem($item); + $list = (array)$collection; + return array_map([$this, 'untransform'], $list); } } \ No newline at end of file diff --git a/src/API/Anilist/Transformer/MangaListTransformer.php b/src/API/Anilist/Transformer/MangaListTransformer.php index 66757090..bfbd6b60 100644 --- a/src/API/Anilist/Transformer/MangaListTransformer.php +++ b/src/API/Anilist/Transformer/MangaListTransformer.php @@ -16,7 +16,10 @@ namespace Aviat\AnimeClient\API\Anilist\Transformer; +use Aviat\AnimeClient\API\Enum\MangaReadingStatus\Anilist as AnilistStatus; +use Aviat\AnimeClient\API\Mapping\MangaReadingStatus; use Aviat\AnimeClient\Types\MangaFormItem; + use Aviat\Ion\Transformer\AbstractTransformer; class MangaListTransformer extends AbstractTransformer { @@ -26,8 +29,38 @@ class MangaListTransformer extends AbstractTransformer { } + /** + * Transform Anilist list item to Kitsu form update format + * + * @param array $item + * @return MangaFormItem + */ public function untransform(array $item): MangaFormItem { - return new MangaFormItem($item); + return new MangaFormItem([ + 'id' => $item['id'], + 'mal_id' => $item['media']['idMal'], + 'data' => [ + 'notes' => $item['notes'] ?? '', + 'private' => $item['private'], + 'progress' => $item['progress'], + 'rating' => $item['score'], + 'reconsumeCount' => $item['repeat'], + 'reconsuming' => $item['status'] === AnilistStatus::REPEATING, + 'status' => MangaReadingStatus::ANILIST_TO_KITSU[$item['status']], + ] + ]); + } + + /** + * Transform a set of structures + * + * @param array|object $collection + * @return array + */ + public function untransformCollection($collection): array + { + $list = (array)$collection; + return array_map([$this, 'untransform'], $list); } } \ No newline at end of file diff --git a/src/API/Kitsu/ListItem.php b/src/API/Kitsu/ListItem.php index 55e42af3..6d8abbcd 100644 --- a/src/API/Kitsu/ListItem.php +++ b/src/API/Kitsu/ListItem.php @@ -37,7 +37,7 @@ final class ListItem implements ListItemInterface { use KitsuTrait; public function create(array $data): Request - { + { $body = [ 'data' => [ 'type' => 'libraryEntries', @@ -61,6 +61,11 @@ final class ListItem implements ListItemInterface { ] ] ]; + + if (array_key_exists('notes', $data)) + { + $body['data']['attributes']['notes'] = $data['notes']; + } $authHeader = $this->getAuthHeader(); diff --git a/src/API/Kitsu/Model.php b/src/API/Kitsu/Model.php index 77a02868..7f782407 100644 --- a/src/API/Kitsu/Model.php +++ b/src/API/Kitsu/Model.php @@ -37,11 +37,8 @@ use Aviat\AnimeClient\API\Kitsu\Transformer\{ MangaListTransformer }; use Aviat\AnimeClient\Types\{ - AbstractType, Anime, FormItem, - FormItemData, - AnimeListItem, MangaPage }; use Aviat\Ion\{Di\ContainerAware, Json}; diff --git a/src/API/Kitsu/Transformer/AnimeListTransformer.php b/src/API/Kitsu/Transformer/AnimeListTransformer.php index 356d8315..cf355183 100644 --- a/src/API/Kitsu/Transformer/AnimeListTransformer.php +++ b/src/API/Kitsu/Transformer/AnimeListTransformer.php @@ -45,8 +45,8 @@ final class AnimeListTransformer extends AbstractTransformer { $genres = array_column($anime['relationships']['genres'], 'name') ?? []; sort($genres); - $rating = (int) $item['attributes']['rating'] !== 0 - ? 2 * $item['attributes']['rating'] + $rating = (int) $item['attributes']['ratingTwenty'] !== 0 + ? $item['attributes']['ratingTwenty'] / 2 : '-'; $total_episodes = array_key_exists('episodeCount', $anime) && (int) $anime['episodeCount'] !== 0 @@ -141,7 +141,7 @@ final class AnimeListTransformer extends AbstractTransformer { if (is_numeric($item['user_rating']) && $item['user_rating'] > 0) { - $untransformed['data']['rating'] = $item['user_rating'] / 2; + $untransformed['data']['ratingTwenty'] = $item['user_rating'] * 2; } return $untransformed; diff --git a/src/API/Kitsu/Transformer/MangaListTransformer.php b/src/API/Kitsu/Transformer/MangaListTransformer.php index b4c927c4..915c039d 100644 --- a/src/API/Kitsu/Transformer/MangaListTransformer.php +++ b/src/API/Kitsu/Transformer/MangaListTransformer.php @@ -46,8 +46,8 @@ final class MangaListTransformer extends AbstractTransformer { $genres = array_column($manga['relationships']['genres'], 'name') ?? []; sort($genres); - $rating = (int) $item['attributes']['rating'] !== 0 - ? 2 * $item['attributes']['rating'] + $rating = (int) $item['attributes']['ratingTwenty'] !== 0 + ? $item['attributes']['ratingTwenty'] / 2 : '-'; $totalChapters = ((int) $manga['chapterCount'] !== 0) @@ -138,7 +138,7 @@ final class MangaListTransformer extends AbstractTransformer { if (is_numeric($item['new_rating']) && $item['new_rating'] > 0) { - $map['data']['rating'] = $item['new_rating'] / 2; + $map['data']['ratingTwenty'] = $item['new_rating'] * 2; } return $map; diff --git a/src/API/ParallelAPIRequest.php b/src/API/ParallelAPIRequest.php index 15ec9e42..a6882fa5 100644 --- a/src/API/ParallelAPIRequest.php +++ b/src/API/ParallelAPIRequest.php @@ -66,6 +66,7 @@ final class ParallelAPIRequest { * Actually make the requests * * @return array + * @throws \Throwable */ public function makeRequests(): array { diff --git a/src/Command/BaseCommand.php b/src/Command/BaseCommand.php index e6a33844..e7eabc12 100644 --- a/src/Command/BaseCommand.php +++ b/src/Command/BaseCommand.php @@ -21,6 +21,7 @@ use function Aviat\AnimeClient\loadToml; use Aura\Session\SessionFactory; use Aviat\AnimeClient\Util; use Aviat\AnimeClient\API\CacheTrait; +use Aviat\AnimeClient\API\Anilist; use Aviat\AnimeClient\API\Kitsu; use Aviat\AnimeClient\API\Kitsu\KitsuRequestBuilder; use Aviat\Banker\Pool; @@ -85,7 +86,10 @@ class BaseCommand extends Command { $app_logger->pushHandler(new RotatingFileHandler($APP_DIR . '/logs/app-cli.log', Logger::NOTICE)); $kitsu_request_logger = new Logger('kitsu-request'); $kitsu_request_logger->pushHandler(new RotatingFileHandler($APP_DIR . '/logs/kitsu_request-cli.log', Logger::NOTICE)); + $anilistRequestLogger = new Logger('anilist-request'); + $anilistRequestLogger->pushHandler(new RotatingFileHandler($APP_DIR . '/logs/anilist_request-cli.log', Logger::NOTICE)); $container->setLogger($app_logger); + $container->setLogger($anilistRequestLogger, 'anilist-request'); $container->setLogger($kitsu_request_logger, 'kitsu-request'); // Create Config Object @@ -122,6 +126,20 @@ class BaseCommand extends Command { $model->setCache($cache); return $model; }); + $container->set('anilist-model', function ($container) { + $requestBuilder = new Anilist\AnilistRequestBuilder(); + $requestBuilder->setLogger($container->getLogger('anilist-request')); + + $listItem = new Anilist\ListItem(); + $listItem->setContainer($container); + $listItem->setRequestBuilder($requestBuilder); + + $model = new Anilist\Model($listItem); + $model->setContainer($container); + $model->setRequestBuilder($requestBuilder); + + return $model; + }); $container->set('util', function($container) { return new Util($container); diff --git a/src/Command/SyncLists.php b/src/Command/SyncLists.php index b75c24d2..971dbc5b 100644 --- a/src/Command/SyncLists.php +++ b/src/Command/SyncLists.php @@ -16,19 +16,28 @@ namespace Aviat\AnimeClient\Command; -use Aviat\AnimeClient\API\{ - FailedResponseException, - JsonAPI, - ParallelAPIRequest +use Aviat\AnimeClient\API\ +{FailedResponseException, JsonAPI, Kitsu\Transformer\MangaListTransformer, ParallelAPIRequest}; +use Aviat\AnimeClient\API\Anilist\Transformer\{ + AnimeListTransformer as AALT, + MangaListTransformer as AMLT, }; +use Aviat\AnimeClient\API\Mapping\{AnimeWatchingStatus, MangaReadingStatus}; +use Aviat\AnimeClient\Types\{AnimeFormItem, MangaFormItem}; use Aviat\Ion\Json; use DateTime; /** - * Clears the API Cache + * Syncs list data between Anilist and Kitsu */ final class SyncLists extends BaseCommand { + /** + * Model for making requests to Anilist API + * @var \Aviat\AnimeClient\API\Anilist\Model + */ + protected $anilistModel; + /** * Model for making requests to Kitsu API * @var \Aviat\AnimeClient\API\Kitsu\Model @@ -36,7 +45,7 @@ final class SyncLists extends BaseCommand { protected $kitsuModel; /** - * Run the Kitsu <=> MAL sync script + * Run the Kitsu <=> Anilist sync script * * @param array $args * @param array $options @@ -48,6 +57,7 @@ final class SyncLists extends BaseCommand { { $this->setContainer($this->setupContainer()); $this->setCache($this->container->get('cache')); + $this->anilistModel = $this->container->get('anilist-model'); $this->kitsuModel = $this->container->get('kitsu-model'); $this->sync('anime'); @@ -64,18 +74,6 @@ final class SyncLists extends BaseCommand { { $uType = ucfirst($type); - // Do a little check to make sure you don't have immediate issues - // if you have 0 or 1 items in a list on MAL. - /* $malList = $this->malModel->getList($type); - $malCount = 0; - if ( ! empty($malList)) - { - $malCount = count(array_key_exists(0, $malList) - ? $malList - : [$malList] - ); - } */ - $kitsuCount = 0; try { @@ -87,24 +85,23 @@ final class SyncLists extends BaseCommand { } - // $this->echoBox("Number of MAL {$type} list items: {$malCount}"); $this->echoBox("Number of Kitsu {$type} list items: {$kitsuCount}"); $data = $this->diffLists($type); - /* if ( ! empty($data['addToMAL'])) + if ( ! empty($data['addToAnilist'])) { - $count = count($data['addToMAL']); - $this->echoBox("Adding {$count} missing {$type} list items to MAL"); - $this->updateMALListItems($data['addToMAL'], 'create', $type); + $count = count($data['addToAnilist']); + $this->echoBox("Adding {$count} missing {$type} list items to Anilist"); + $this->updateAnilistListItems($data['addToAnilist'], 'create', $type); } - if ( ! empty($data['updateMAL'])) + if ( ! empty($data['updateAnilist'])) { - $count = count($data['updateMAL']); - $this->echoBox("Updating {$count} outdated MAL {$type} list items"); - $this->updateMALListItems($data['updateMAL'], 'update', $type); - } */ + $count = count($data['updateAnilist']); + $this->echoBox("Updating {$count} outdated Anilist {$type} list items"); + $this->updateAnilistListItems($data['updateAnilist'], 'update', $type); + } if ( ! empty($data['addToKitsu'])) { @@ -144,107 +141,79 @@ final class SyncLists extends BaseCommand { } /** - * Format a MAL list for comparison + * Format an Anilist list for comparison * * @param string $type * @return array */ - /* protected function formatMALList(string $type): array + protected function formatAnilistList(string $type): array { $type = ucfirst($type); - $method = "formatMAL{$type}List"; + $method = "formatAnilist{$type}List"; return $this->$method(); - } */ + } /** - * Format a MAL anime list for comparison + * Format an Anilist anime list for comparison * * @return array */ - /* protected function formatMALAnimeList(): array + protected function formatAnilistAnimeList(): array { - $orig = $this->malModel->getList('anime'); + $anilistList = $this->anilistModel->getSyncList('ANIME'); + $anilistTransformer = new AALT(); + + $transformedAnilist = []; + + foreach ($anilistList['data']['MediaListCollection']['lists'] as $list) + { + $newTransformed = $anilistTransformer->untransformCollection($list['entries']); + $transformedAnilist = array_merge($transformedAnilist, $newTransformed); + } + + // Key the array by the mal_id for easier reference in the next comparision step $output = []; - - // Bail early on empty list - if (empty($orig)) + foreach ($transformedAnilist as $item) { - return []; + $output[$item['mal_id']] = $item->toArray(); } - // Due to xml parsing differences, - // 1 item has no wrapping array. - // In this case, just re-create the - // wrapper array - if ( ! array_key_exists(0, $orig)) - { - $orig = [$orig]; - } - - foreach($orig as $item) - { - $output[$item['series_animedb_id']] = [ - 'id' => $item['series_animedb_id'], - 'data' => [ - 'status' => AnimeWatchingStatus::MAL_TO_KITSU[$item['my_status']], - 'progress' => $item['my_watched_episodes'], - 'reconsuming' => (bool) $item['my_rewatching'], - 'rating' => $item['my_score'] / 2, - 'updatedAt' => (new \DateTime()) - ->setTimestamp((int)$item['my_last_updated']) - ->format(\DateTime::W3C), - ] - ]; - } + $count = count($output); + $this->echoBox("Number of Anilist anime list items: {$count}"); return $output; - } */ + } /** - * Format a MAL manga list for comparison + * Format an Anilist manga list for comparison * * @return array */ - /* protected function formatMALMangaList(): array + protected function formatAnilistMangaList(): array { - $orig = $this->malModel->getList('manga'); + $anilistList = $this->anilistModel->getSyncList('MANGA'); + $anilistTransformer = new AMLT(); + + $transformedAnilist = []; + + foreach ($anilistList['data']['MediaListCollection']['lists'] as $list) + { + $newTransformed = $anilistTransformer->untransformCollection($list['entries']); + $transformedAnilist = array_merge($transformedAnilist, $newTransformed); + } + + // Key the array by the mal_id for easier reference in the next comparision step $output = []; - - // Bail early on empty list - if (empty($orig)) + foreach ($transformedAnilist as $item) { - return []; + $output[$item['mal_id']] = $item->toArray(); } - // Due to xml parsing differences, - // 1 item has no wrapping array. - // In this case, just re-create the - // wrapper array - if ( ! array_key_exists(0, $orig)) - { - $orig = [$orig]; - } - - foreach($orig as $item) - { - $output[$item['series_mangadb_id']] = [ - 'id' => $item['series_mangadb_id'], - 'data' => [ - 'my_status' => $item['my_status'], - 'status' => MangaReadingStatus::MAL_TO_KITSU[$item['my_status']], - 'progress' => $item['my_read_chapters'], - 'volumes' => $item['my_read_volumes'], - 'reconsuming' => (bool) $item['my_rereadingg'], - 'rating' => $item['my_score'] / 2, - 'updatedAt' => (new \DateTime()) - ->setTimestamp((int)$item['my_last_updated']) - ->format(\DateTime::W3C), - ] - ]; - } + $count = count($output); + $this->echoBox("Number of Anilist manga list items: {$count}"); return $output; - } */ + } /** * Format a kitsu list for the sake of comparision @@ -254,7 +223,7 @@ final class SyncLists extends BaseCommand { */ protected function formatKitsuList(string $type = 'anime'): array { - $data = $this->kitsuModel->{'getFull' . ucfirst($type) . 'List'}(); + $data = $this->kitsuModel->{'getFullRaw' . ucfirst($type) . 'List'}(); if (empty($data)) { @@ -281,7 +250,7 @@ final class SyncLists extends BaseCommand { } } - // Skip to the next item if there isn't a MAL ID + // Skip to the next item if there isn't a Anilist ID if ($malId === NULL) { continue; @@ -309,31 +278,51 @@ final class SyncLists extends BaseCommand { // Organize mappings, and ignore entries without mappings $kitsuList = $this->formatKitsuList($type); - // Get MAL list data - // $malList = $this->formatMALList($type); + // Get Anilist list data + $anilistList = $this->formatAnilistList($type); - $itemsToAddToMAL = []; + $itemsToAddToAnilist = []; $itemsToAddToKitsu = []; - $malUpdateItems = []; + $anilistUpdateItems = []; $kitsuUpdateItems = []; - // $malIds = array_column($malList, 'id'); - // $kitsuMalIds = array_column($kitsuList, 'malId'); - // $missingMalIds = array_diff($malIds, $kitsuMalIds); + $malBlackList = ($type === 'anime') + ? [ + 27821, // Fate/stay night: Unlimited Blade Works - Prologue + 29317, // Saekano: How to Raise a Boring Girlfriend Prologue + 30514, // Nisekoinogatari + ] : [ + 114638, // Cells at Work: Black + ]; + + $malIds = array_keys($anilistList); + $kitsuMalIds = array_map('intval', array_column($kitsuList, 'malId')); + $missingMalIds = array_diff($malIds, $kitsuMalIds); + $missingMalIds = array_diff($missingMalIds, $malBlackList); - /* foreach($missingMalIds as $mid) - { - $itemsToAddToKitsu[] = array_merge($malList[$mid]['data'], [ - 'id' => $this->kitsuModel->getKitsuIdFromMALId($mid, $type), + foreach($missingMalIds as $mid) + { + $itemsToAddToKitsu[] = array_merge($anilistList[$mid]['data'], [ + 'id' => $this->kitsuModel->getKitsuIdFromMALId((string)$mid, $type), 'type' => $type ]); - } */ + } - /* foreach($kitsuList as $kitsuItem) + foreach($kitsuList as $kitsuItem) { - if (\in_array($kitsuItem['malId'], $malIds, TRUE)) + $malId = $kitsuItem['malId']; + + if (in_array($malId, $malBlackList)) { - $item = $this->compareListItems($kitsuItem, $malList[$kitsuItem['malId']]); + continue; + } + + if (array_key_exists($malId, $anilistList)) + { + $anilistItem = $anilistList[$malId]; + // dump($anilistItem); + + $item = $this->compareListItems($kitsuItem, $anilistItem); if ($item === NULL) { @@ -345,25 +334,39 @@ final class SyncLists extends BaseCommand { $kitsuUpdateItems[] = $item['data']; } - if (\in_array('mal', $item['updateType'], TRUE)) + if (\in_array('anilist', $item['updateType'], TRUE)) { - $malUpdateItems[] = $item['data']; + $anilistUpdateItems[] = $item['data']; } continue; } + + $statusMap = ($type === 'anime') ? AnimeWatchingStatus::class : MangaReadingStatus::class; // Looks like this item only exists on Kitsu - $itemsToAddToMAL[] = [ - 'mal_id' => $kitsuItem['malId'], - 'data' => $kitsuItem['data'] + $kItem = $kitsuItem['data']; + $newItemStatus = ($kItem['reconsuming'] === true) ? 'REPEATING' : $statusMap::KITSU_TO_ANILIST[$kItem['status']]; + $itemsToAddToAnilist[] = [ + 'mal_id' => $malId, + 'data' => [ + 'notes' => $kItem['notes'], + 'private' => $kItem['private'], + 'progress' => $kItem['progress'], + 'repeat' => $kItem['reconsumeCount'], + 'score' => $kItem['ratingTwenty'] / 2, + 'status' => $newItemStatus, + ], // $kitsuItem['data'] ]; - } */ + } + + //dump($itemsToAddToAnilist); + //die(); return [ - // 'addToMAL' => $itemsToAddToMAL, - // 'updateMAL' => $malUpdateItems, + 'addToAnilist' => $itemsToAddToAnilist, + 'updateAnilist' => $anilistUpdateItems, 'addToKitsu' => $itemsToAddToKitsu, 'updateKitsu' => $kitsuUpdateItems ]; @@ -373,18 +376,27 @@ final class SyncLists extends BaseCommand { * Compare two list items, and return the out of date one, if one exists * * @param array $kitsuItem - * @param array $malItem + * @param array $anilistItem * @return array|null */ - protected function compareListItems(array $kitsuItem, array $malItem): ?array + protected function compareListItems(array $kitsuItem, array $anilistItem): ?array { - $compareKeys = ['status', 'progress', 'rating', 'reconsuming']; + $compareKeys = [ + 'notes', + 'progress', + 'rating', + 'reconsumeCount', + 'reconsuming', + 'status', + ]; $diff = []; - $dateDiff = new DateTime($kitsuItem['data']['updatedAt']) <=> new DateTime($malItem['data']['updatedAt']); + + // Correct differences in notation + $kitsuItem['data']['rating'] = $kitsuItem['data']['ratingTwenty'] / 2; foreach($compareKeys as $key) { - $diff[$key] = $kitsuItem['data'][$key] <=> $malItem['data'][$key]; + $diff[$key] = $kitsuItem['data'][$key] <=> $anilistItem['data'][$key]; } // No difference? Bail out early @@ -404,9 +416,18 @@ final class SyncLists extends BaseCommand { 'updateType' => [] ]; + $sameNotes = $diff['notes'] === 0; $sameStatus = $diff['status'] === 0; $sameProgress = $diff['progress'] === 0; $sameRating = $diff['rating'] === 0; + $sameRewatchCount = $diff['reconsumeCount'] === 0; + + // If an item is completed, make sure the 'reconsuming' flag is false + if ($kitsuItem['data']['status'] === 'completed' && $kitsuItem['data']['reconsuming'] === TRUE) + { + $update['data']['reconsuming'] = FALSE; + $return['updateType'][] = 'kitsu'; + } // If status is the same, and progress count is different, use greater progress @@ -415,18 +436,25 @@ final class SyncLists extends BaseCommand { if ($diff['progress'] === 1) { $update['data']['progress'] = $kitsuItem['data']['progress']; - $return['updateType'][] = 'mal'; + $return['updateType'][] = 'anilist'; } else if($diff['progress'] === -1) { - $update['data']['progress'] = $malItem['data']['progress']; + $update['data']['progress'] = $anilistItem['data']['progress']; $return['updateType'][] = 'kitsu'; } } + + // If status is different, go with Kitsu + if ( ! $sameStatus) + { + $update['data']['status'] = $kitsuItem['data']['status']; + $return['updateType'][] = 'anilist'; + } // If status and progress are different, it's a bit more complicated... // But, at least for now, assume newer record is correct - if ( ! ($sameStatus || $sameProgress)) + /* if ( ! ($sameStatus || $sameProgress)) { if ($dateDiff === 1) { @@ -437,59 +465,111 @@ final class SyncLists extends BaseCommand { $update['data']['progress'] = $kitsuItem['data']['progress']; } - $return['updateType'][] = 'mal'; + $return['updateType'][] = 'anilist'; } else if($dateDiff === -1) { - $update['data']['status'] = $malItem['data']['status']; + $update['data']['status'] = $anilistItem['data']['status']; - if ((int)$malItem['data']['progress'] !== 0) + if ((int)$anilistItem['data']['progress'] !== 0) { $update['data']['progress'] = $kitsuItem['data']['progress']; } $return['updateType'][] = 'kitsu'; } - } + }*/ - // If rating is different, use the rating from the item most recently updated + // If rating is different, use the kitsu rating, unless the other rating + // is set, and the kitsu rating is not set if ( ! $sameRating) { - if ($dateDiff === 1) + if ($kitsuItem['data']['rating'] !== 0) { $update['data']['rating'] = $kitsuItem['data']['rating']; - $return['updateType'][] = 'mal'; + $return['updateType'][] = 'anilist'; } - else if ($dateDiff === -1) + else { - $update['data']['rating'] = $malItem['data']['rating']; + $update['data']['rating'] = $anilistItem['data']['rating']; + $return['updateType'][] = 'kitsu'; + } + } + + // If notes are set, use kitsu, otherwise, set kitsu from anilist + if ( ! $sameNotes) + { + if ($kitsuItem['data']['notes'] !== '') + { + $update['data']['notes'] = $kitsuItem['data']['notes']; + $return['updateType'][] = 'anilist'; + } + else + { + $update['data']['notes'] = $anilistItem['data']['notes']; + $return['updateType'][] = 'kitsu'; + } + } + + // Assume the larger reconsumeCount is correct + if ( ! $sameRewatchCount) + { + if ($diff['reconsumeCount'] === 1) + { + $update['data']['reconsumeCount'] = $kitsuItem['data']['reconsumeCount']; + $return['updateType'][] = 'anilist'; + } + else if ($diff['reconsumeCount'] === -1) + { + $update['data']['reconsumeCount'] = $anilistItem['data']['reconsumeCount']; $return['updateType'][] = 'kitsu'; } } // If status is different, use the status of the more recently updated item - if ( ! $sameStatus) + /* if ( ! $sameStatus) { if ($dateDiff === 1) { $update['data']['status'] = $kitsuItem['data']['status']; - $return['updateType'][] = 'mal'; + $return['updateType'][] = 'anilist'; } else if ($dateDiff === -1) { - $update['data']['status'] = $malItem['data']['status']; + $update['data']['status'] = $anilistItem['data']['status']; $return['updateType'][] = 'kitsu'; } - } + } */ $return['meta'] = [ 'kitsu' => $kitsuItem['data'], - 'mal' => $malItem['data'], - 'dateDiff' => $dateDiff, + 'anilist' => $anilistItem['data'], + // 'dateDiff' => $dateDiff, 'diff' => $diff, ]; $return['data'] = $update; $return['updateType'] = array_unique($return['updateType']); + + // Fill in missing data values for update on Anlist + // so I don't have to create a really complex graphql query + // to handle each combination of fields + if ($return['updateType'][0] === 'anilist') + { + $prevData = [ + 'notes' => $kitsuItem['data']['notes'], + 'private' => $kitsuItem['data']['private'], + 'progress' => $kitsuItem['data']['progress'], + 'rating' => $kitsuItem['data']['rating'], + 'reconsumeCount' => $kitsuItem['data']['reconsumeCount'], + 'reconsuming' => $kitsuItem['data']['reconsuming'], + 'status' => $kitsuItem['data']['status'], + ]; + + $return['data']['data'] = array_merge($prevData, $return['data']['data']); + } + + dump($return); + return $return; } @@ -505,9 +585,13 @@ final class SyncLists extends BaseCommand { $requester = new ParallelAPIRequest(); foreach($itemsToUpdate as $item) { + $typeClass = '\\Aviat\\AnimeClient\\Types\\' . ucFirst($type) . 'FormItem'; + if ($action === 'update') { - $requester->addRequest($this->kitsuModel->updateListItem($item)); + $requester->addRequest( + $this->kitsuModel->updateListItem(new $typeClass($item)) + ); } else if ($action === 'create') { @@ -537,27 +621,29 @@ final class SyncLists extends BaseCommand { } /** - * Create/Update list items on MAL + * Create/Update list items on Anilist * * @param array $itemsToUpdate * @param string $action * @param string $type */ - /* protected function updateMALListItems(array$itemsToUpdate, string $action = 'update', string $type = 'anime'): void + protected function updateAnilistListItems(array$itemsToUpdate, string $action = 'update', string $type = 'anime'): void { - $transformer = new ALT(); $requester = new ParallelAPIRequest(); + + $typeClass = '\\Aviat\\AnimeClient\\Types\\' . ucFirst($type) . 'FormItem'; foreach($itemsToUpdate as $item) { if ($action === 'update') - { - $requester->addRequest($this->malModel->updateListItem($item, $type)); + { + $requester->addRequest( + $this->anilistModel->updateListItem(new $typeClass($item), $type) + ); } else if ($action === 'create') { - $data = $transformer->untransform($item); - $requester->addRequest($this->malModel->createFullListItem($data, $type)); + $requester->addRequest($this->anilistModel->createFullListItem($item, $type)); } } @@ -566,20 +652,21 @@ final class SyncLists extends BaseCommand { foreach($responses as $key => $response) { $id = $itemsToUpdate[$key]['mal_id']; - $goodResponse = ( - ($action === 'update' && $response === 'Updated') || - ($action === 'create' && $response === 'Created') - ); - if ($goodResponse) + + $responseData = Json::decode($response); + + // $id = $itemsToUpdate[$key]['id']; + if ( ! array_key_exists('errors', $responseData)) { $verb = ($action === 'update') ? 'updated' : 'created'; - $this->echoBox("Successfully {$verb} MAL {$type} list item with id: {$id}"); + $this->echoBox("Successfully {$verb} Anilist {$type} list item with id: {$id}"); } else { + dump($responseData); $verb = ($action === 'update') ? 'update' : 'create'; - $this->echoBox("Failed to {$verb} MAL {$type} list item with id: {$id}"); + $this->echoBox("Failed to {$verb} Anilist {$type} list item with id: {$id}"); } } - } */ + } } diff --git a/src/Controller/Manga.php b/src/Controller/Manga.php index dc677214..dcd86c13 100644 --- a/src/Controller/Manga.php +++ b/src/Controller/Manga.php @@ -130,6 +130,11 @@ final class Manga extends Controller { $this->redirect('manga/add', 303); } + if (empty($data['mal_id'])) + { + unset($data['mal_id']); + } + $result = $this->model->createLibraryItem($data); if ($result) @@ -182,8 +187,9 @@ final class Manga extends Controller { */ public function search(): void { - $query_data = $this->request->getQueryParams(); - $this->outputJSON($this->model->search($query_data['query'])); + $queryParams = $this->request->getQueryParams(); + $query = $queryParams['query']; + $this->outputJSON($this->model->search($query)); } /** @@ -218,13 +224,9 @@ final class Manga extends Controller { } /** - * Update a manga item - * - * @throws \Aviat\Ion\Di\ContainerException - * @throws \Aviat\Ion\Di\NotFoundException - * @return void + * Increment the progress of a manga item */ - public function update(): void + public function increment(): void { if (stripos($this->request->getHeader('content-type')[0], 'application/json') !== FALSE) { @@ -235,7 +237,7 @@ final class Manga extends Controller { $data = $this->request->getParsedBody(); } - $response = $this->model->updateLibraryItem(new MangaFormItem($data)); + $response = $this->model->incrementLibraryItem(new MangaFormItem($data)); $this->cache->clear(); $this->outputJSON($response['body'], $response['statusCode']); @@ -251,13 +253,11 @@ final class Manga extends Controller { public function delete(): void { $body = $this->request->getParsedBody(); - $id = $body['id']; - $malId = $body['mal_id']; - $response = $this->model->deleteLibraryItem($id, $malId); + $response = $this->model->deleteLibraryItem($body['id'], $body['mal_id']); if ($response) { - $this->setFlashMessage("Successfully deleted manga.", 'success'); + $this->setFlashMessage('Successfully deleted manga.', 'success'); $this->cache->clear(); } else diff --git a/src/Model/Anime.php b/src/Model/Anime.php index 5b56c05b..fa975eb1 100644 --- a/src/Model/Anime.php +++ b/src/Model/Anime.php @@ -31,6 +31,13 @@ use Aviat\Ion\Json; */ class Anime extends API { + /** + * Is the Anilist API enabled? + * + * @var boolean + */ + protected $anilistEnabled; + /** * Model for making requests to Anilist API * @@ -54,6 +61,9 @@ class Anime extends API { { $this->anilistModel = $container->get('anilist-model'); $this->kitsuModel = $container->get('kitsu-model'); + + $config = $container->get('config'); + $this->anilistEnabled = (bool) $config->get(['anilist', 'enabled']); } /** @@ -137,18 +147,6 @@ class Anime extends API { $item = $this->kitsuModel->getListItem($itemId); $array = $item->toArray(); - if ( ! empty($item->mal_id)) - { - $anilistInfo = $this->anilistModel->getListItem($item['mal_id']); - - if (empty($anilistInfo)) - { - return $item; - } - - $array['anilist_item_id'] = $anilistInfo['id']; - } - if (is_array($array['notes'])) { $array['notes'] = ''; @@ -168,9 +166,9 @@ class Anime extends API { $requester = new ParallelAPIRequest(); $requester->addRequest($this->kitsuModel->createListItem($data), 'kitsu'); - // @TODO Make sure Anilist integration is optional - if (array_key_exists('mal_id', $data)) { - $requester->addRequest($this->anilistModel->createListItem($data), 'anilist'); + if (array_key_exists('mal_id', $data) && $this->anilistEnabled) + { + $requester->addRequest($this->anilistModel->createListItem($data, 'ANIME'), 'anilist'); } $results = $requester->makeRequests(); @@ -199,9 +197,9 @@ class Anime extends API { $array = $data->toArray(); - // @TODO Make sure Anilist integration is optional - if (array_key_exists('mal_id', $array)) { - $requester->addRequest($this->anilistModel->incrementListItem($data), 'anilist'); + if (array_key_exists('mal_id', $array) && $this->anilistEnabled) + { + $requester->addRequest($this->anilistModel->incrementListItem($data, 'ANIME'), 'anilist'); } $results = $requester->makeRequests(); @@ -228,10 +226,9 @@ class Anime extends API { $array = $data->toArray(); - // @TODO Make sure Anilist integration is optional - if (array_key_exists('mal_id', $array)) + if (array_key_exists('mal_id', $array) && $this->anilistEnabled) { - $requester->addRequest($this->anilistModel->updateListItem($data), 'anilist'); + $requester->addRequest($this->anilistModel->updateListItem($data, 'ANIME'), 'anilist'); } $results = $requester->makeRequests(); @@ -257,9 +254,9 @@ class Anime extends API { $requester = new ParallelAPIRequest(); $requester->addRequest($this->kitsuModel->deleteListItem($id), 'kitsu'); - // @TODO Make sure Anilist integration is optional - if ($malId !== null) { - $requester->addRequest($this->anilistModel->deleteListItem($malId), 'anilist'); + if ($malId !== null && $this->anilistEnabled) + { + $requester->addRequest($this->anilistModel->deleteListItem($malId, 'ANIME'), 'anilist'); } $results = $requester->makeRequests(); diff --git a/src/Model/Manga.php b/src/Model/Manga.php index 0ac48ff8..90abee3b 100644 --- a/src/Model/Manga.php +++ b/src/Model/Manga.php @@ -33,6 +33,19 @@ use Aviat\Ion\Json; * Model for handling requests dealing with the manga list */ class Manga extends API { + /** + * Is the Anilist API enabled? + * + * @var boolean + */ + protected $anilistEnabled; + + /** + * Model for making requests to the Anilist API + * @var \Aviat\AnimeClient\API\Anilist\Model + */ + protected $anilistModel; + /** * Model for making requests to Kitsu API * @var \Aviat\AnimeClient\API\Kitsu\Model @@ -43,12 +56,14 @@ class Manga extends API { * Constructor * * @param ContainerInterface $container - * @throws \Aviat\Ion\Di\ContainerException - * @throws \Aviat\Ion\Di\NotFoundException */ public function __construct(ContainerInterface $container) { + $this->anilistModel = $container->get('anilist-model'); $this->kitsuModel = $container->get('kitsu-model'); + + $config = $container->get('config'); + $this->anilistEnabled = (bool)$config->get(['anilist', 'enabled']); } /** @@ -121,6 +136,11 @@ class Manga extends API { $requester = new ParallelAPIRequest(); $requester->addRequest($this->kitsuModel->createListItem($data), 'kitsu'); + if (array_key_exists('mal_id', $data) && $this->anilistEnabled) + { + $requester->addRequest($this->anilistModel->createListItem($data, 'MANGA'), 'anilist'); + } + $results = $requester->makeRequests(); return count($results) > 0; @@ -137,6 +157,13 @@ class Manga extends API { $requester = new ParallelAPIRequest(); $requester->addRequest($this->kitsuModel->updateListItem($data), 'kitsu'); + $array = $data->toArray(); + + if (array_key_exists('mal_id', $array) && $this->anilistEnabled) + { + $requester->addRequest($this->anilistModel->updateListItem($data, 'MANGA'), 'anilist'); + } + $results = $requester->makeRequests(); $body = Json::decode($results['kitsu']); $statusCode = array_key_exists('error', $body) ? 400: 200; @@ -147,6 +174,34 @@ class Manga extends API { ]; } + /** + * Increase the progress of a list entry + * + * @param MangaFormItem $data + * @return array + */ + public function incrementLibraryItem(MangaFormItem $data): array + { + $requester = new ParallelAPIRequest(); + $requester->addRequest($this->kitsuModel->incrementListItem($data), 'kitsu'); + + $array = $data->toArray(); + + if (array_key_exists('mal_id', $array) && $this->anilistEnabled) + { + $requester->addRequest($this->anilistModel->incrementListItem($data, 'MANGA'), 'anilist'); + } + + $results = $requester->makeRequests(); + $body = Json::decode($results['kitsu']); + $statusCode = array_key_exists('error', $body) ? 400 : 200; + + return [ + 'body' => Json::decode($results['kitsu']), + 'statusCode' => $statusCode + ]; + } + /** * Delete a list entry * @@ -159,6 +214,11 @@ class Manga extends API { $requester = new ParallelAPIRequest(); $requester->addRequest($this->kitsuModel->deleteListItem($id), 'kitsu'); + if ($malId !== null && $this->anilistEnabled) + { + $requester->addRequest($this->anilistModel->deleteListItem($malId, 'MANGA'), 'anilist'); + } + $results = $requester->makeRequests(); return count($results) > 0;