Lots of Anilist integration, see #5

This commit is contained in:
Timothy Warren 2018-09-26 22:31:04 -04:00
parent ede69b6099
commit 2d3295757d
21 changed files with 659 additions and 259 deletions

View File

@ -24,6 +24,8 @@
"aura/session": "^2.0", "aura/session": "^2.0",
"aviat/banker": "^1.0.0", "aviat/banker": "^1.0.0",
"aviat/ion": "^2.3.0", "aviat/ion": "^2.3.0",
"ext-gd":"*",
"ext-pdo": "*",
"maximebf/consolekit": "^1.0", "maximebf/consolekit": "^1.0",
"monolog/monolog": "^1.0", "monolog/monolog": "^1.0",
"psr/http-message": "~1.0", "psr/http-message": "~1.0",

View File

@ -84,26 +84,30 @@ trait AnilistTrait {
->get('session') ->get('session')
->getSegment(SESSION_SEGMENT); ->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']); $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']); $request = $request->setFormFields($options['form_params']);
} }
if (array_key_exists('query', $options)) { if (array_key_exists('query', $options))
{
$request = $request->setQuery($options['query']); $request = $request->setQuery($options['query']);
} }
if (array_key_exists('body', $options)) { if (array_key_exists('body', $options))
{
$request = $request->setJsonBody($options['body']); $request = $request->setJsonBody($options['body']);
} }
if (array_key_exists('headers', $options)) { if (array_key_exists('headers', $options))
{
$request = $request->setHeaders($options['headers']); $request = $request->setHeaders($options['headers']);
} }
@ -148,7 +152,8 @@ trait AnilistTrait {
public function mutateRequest (string $name, array $variables = []): Request public function mutateRequest (string $name, array $variables = []): Request
{ {
$file = realpath(__DIR__ . "/GraphQL/Mutations/{$name}.graphql"); $file = realpath(__DIR__ . "/GraphQL/Mutations/{$name}.graphql");
if (!file_exists($file)) { if (!file_exists($file))
{
throw new \LogicException('GraphQL mutation file does not exist.'); throw new \LogicException('GraphQL mutation file does not exist.');
} }
@ -161,7 +166,8 @@ trait AnilistTrait {
if (!empty($variables)) { if (!empty($variables)) {
$body['variables'] = []; $body['variables'] = [];
foreach ($variables as $key => $val) { foreach ($variables as $key => $val)
{
$body['variables'][$key] = $val; $body['variables'][$key] = $val;
} }
} }
@ -211,7 +217,8 @@ trait AnilistTrait {
private function getResponseFromRequest(Request $request): Response private function getResponseFromRequest(Request $request): Response
{ {
$logger = NULL; $logger = NULL;
if ($this->getContainer()) { if ($this->getContainer())
{
$logger = $this->container->getLogger('anilist-request'); $logger = $this->container->getLogger('anilist-request');
} }
@ -236,15 +243,22 @@ trait AnilistTrait {
*/ */
protected function postRequest(array $options = []): array protected function postRequest(array $options = []): array
{ {
$response = $this->getResponse(Anilist::BASE_URL, $options);
$validResponseCodes = [200, 201];
$logger = NULL; $logger = NULL;
if ($this->getContainer()) if ($this->getContainer())
{ {
$logger = $this->container->getLogger('anilist-request'); $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 ( ! \in_array($response->getStatus(), $validResponseCodes, TRUE))
{ {
if ($logger) if ($logger)

View File

@ -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
}
}

View File

@ -1,5 +1,5 @@
query ($id: Int) { query ($id: Int, $type: MediaType) {
Media (idMal: $id) { Media (idMal: $id, type: $type) {
mediaListEntry { mediaListEntry {
id id
userId userId

View File

@ -0,0 +1,7 @@
query ($id: Int, $userName: String) {
MediaList (mediaId: $id, userName: $userName) {
id
userId
mediaId
}
}

View File

@ -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
}
}
}
}
}
}

View File

@ -24,7 +24,6 @@ query ($name: String) {
status status
episodes episodes
season season
seasonYear
genres genres
synonyms synonyms
countryOfOrigin countryOfOrigin

View File

@ -19,6 +19,7 @@ namespace Aviat\AnimeClient\API\Anilist;
use Amp\Artax\Request; use Amp\Artax\Request;
use Aviat\AnimeClient\API\ListItemInterface; use Aviat\AnimeClient\API\ListItemInterface;
use Aviat\AnimeClient\API\Enum\AnimeWatchingStatus\Anilist as AnilistStatus;
use Aviat\AnimeClient\API\Mapping\AnimeWatchingStatus; use Aviat\AnimeClient\API\Mapping\AnimeWatchingStatus;
use Aviat\AnimeClient\Types\FormItemData; use Aviat\AnimeClient\Types\FormItemData;
@ -29,7 +30,7 @@ final class ListItem implements ListItemInterface{
use AnilistTrait; use AnilistTrait;
/** /**
* Create a list item * Create a minimal list item
* *
* @param array $data * @param array $data
* @return Request * @return Request
@ -39,6 +40,17 @@ final class ListItem implements ListItemInterface{
return $this->mutateRequest('CreateMediaListEntry', $data); 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 * Delete a list item
* *
@ -85,23 +97,25 @@ final class ListItem implements ListItemInterface{
* @return Request * @return Request
*/ */
public function update(string $id, FormItemData $data): Request public function update(string $id, FormItemData $data): Request
{ {
$array = $data->toArray(); $array = $data->toArray();
$notes = $data['notes'] ?? ''; $notes = $data['notes'] ?? '';
$progress = array_key_exists('progress', $array) ? $data['progress'] : 0; $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; $rating = array_key_exists('rating', $array) ? $data['rating'] : NULL;
$status = $data['status']; $status = ($data['reconsuming'] === true) ? AnilistStatus::REPEATING : AnimeWatchingStatus::KITSU_TO_ANILIST[$data['status']];
// @TODO Handle weirdness with reWatching $updateData = [
return $this->mutateRequest('UpdateMediaListEntry', [ 'id' => (int)$id,
'id' => $id, 'status' => $status,
'status' => AnimeWatchingStatus::KITSU_TO_ANILIST[$status], 'score' => $rating * 10,
'score' => $rating * 20,
'progress' => $progress, 'progress' => $progress,
'repeat' => (int)$data['reconsumeCount'], 'repeat' => (int)$data['reconsumeCount'],
'private' => (bool)$data['private'], 'private' => $private,
'notes' => $notes, 'notes' => $notes,
]); ];
return $this->mutateRequest('UpdateMediaListEntry', $updateData);
} }
} }

View File

@ -16,6 +16,8 @@
namespace Aviat\AnimeClient\API\Anilist; namespace Aviat\AnimeClient\API\Anilist;
use InvalidArgumentException;
use Amp\Artax\Request; use Amp\Artax\Request;
use Aviat\AnimeClient\API\Mapping\{AnimeWatchingStatus, MangaReadingStatus}; use Aviat\AnimeClient\API\Mapping\{AnimeWatchingStatus, MangaReadingStatus};
use Aviat\AnimeClient\Types\FormItem; use Aviat\AnimeClient\Types\FormItem;
@ -45,6 +47,30 @@ final class Model
// ! Generic API calls // ! 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 * Create a list item
* *
@ -56,14 +82,22 @@ final class Model
{ {
$createData = []; $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 = [ $createData = [
'id' => $mediaId, 'id' => $mediaId,
'status' => AnimeWatchingStatus::KITSU_TO_ANILIST[$data['status']], 'status' => AnimeWatchingStatus::KITSU_TO_ANILIST[$data['status']],
]; ];
} elseif ($type === 'manga') { }
elseif ($type === 'manga')
{
$createData = [ $createData = [
'id' => $mediaId, 'id' => $mediaId,
'status' => MangaReadingStatus::KITSU_TO_ANILIST[$data['status']], 'status' => MangaReadingStatus::KITSU_TO_ANILIST[$data['status']],
@ -73,15 +107,32 @@ final class Model
return $this->listItem->create($createData, $type); 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 * Get the data for a specific list item, generally for editing
* *
* @param string $malId - The unique identifier of that list item * @param string $malId - The unique identifier of that list item
* @return mixed * @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']; $data = $this->listItem->get($id)['data'];
@ -96,9 +147,9 @@ final class Model
* @param FormItem $data * @param FormItem $data
* @return Request * @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']); return $this->listItem->increment($id, $data['data']);
} }
@ -107,11 +158,12 @@ final class Model
* Modify a list item * Modify a list item
* *
* @param FormItem $data * @param FormItem $data
* @param int [$id]
* @return Request * @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']); 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 * @param string $malId - The id of the list item to remove
* @return Request * @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); return $this->listItem->delete($item_id);
} }
@ -135,10 +187,35 @@ final class Model
* @param string $malId * @param string $malId
* @return string * @return string
*/ */
public function getListIdFromMalId(string $malId): ?string public function getListIdFromMalId(string $malId, string $type): ?string
{ {
$info = $this->runQuery('ListItemIdByMalId', ['id' => $malId]); $mediaId = $this->getMediaIdFromMalId($malId, $type);
return (string)$info['data']['Media']['mediaListEntry']['id'] ?? NULL; 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', [ $info = $this->runQuery('MediaIdByMalId', [
'id' => $malId, 'id' => $malId,
'type' => $type 'type' => mb_strtoupper($type),
]); ]);
/* dump([
'mal_id' => $malId,
'response' => $info,
]);
die(); */
return (string)$info['data']['Media']['id']; return (string)$info['data']['Media']['id'];
} }

View File

@ -16,22 +16,51 @@
namespace Aviat\AnimeClient\API\Anilist\Transformer; 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; use Aviat\Ion\Transformer\AbstractTransformer;
class AnimeListTransformer extends AbstractTransformer { class AnimeListTransformer extends AbstractTransformer {
public function transform($item): AnimeListItem 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);
} }
} }

View File

@ -16,7 +16,10 @@
namespace Aviat\AnimeClient\API\Anilist\Transformer; 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\AnimeClient\Types\MangaFormItem;
use Aviat\Ion\Transformer\AbstractTransformer; use Aviat\Ion\Transformer\AbstractTransformer;
class MangaListTransformer extends 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 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);
} }
} }

View File

@ -37,7 +37,7 @@ final class ListItem implements ListItemInterface {
use KitsuTrait; use KitsuTrait;
public function create(array $data): Request public function create(array $data): Request
{ {
$body = [ $body = [
'data' => [ 'data' => [
'type' => 'libraryEntries', '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(); $authHeader = $this->getAuthHeader();

View File

@ -37,11 +37,8 @@ use Aviat\AnimeClient\API\Kitsu\Transformer\{
MangaListTransformer MangaListTransformer
}; };
use Aviat\AnimeClient\Types\{ use Aviat\AnimeClient\Types\{
AbstractType,
Anime, Anime,
FormItem, FormItem,
FormItemData,
AnimeListItem,
MangaPage MangaPage
}; };
use Aviat\Ion\{Di\ContainerAware, Json}; use Aviat\Ion\{Di\ContainerAware, Json};

View File

@ -45,8 +45,8 @@ final class AnimeListTransformer extends AbstractTransformer {
$genres = array_column($anime['relationships']['genres'], 'name') ?? []; $genres = array_column($anime['relationships']['genres'], 'name') ?? [];
sort($genres); sort($genres);
$rating = (int) $item['attributes']['rating'] !== 0 $rating = (int) $item['attributes']['ratingTwenty'] !== 0
? 2 * $item['attributes']['rating'] ? $item['attributes']['ratingTwenty'] / 2
: '-'; : '-';
$total_episodes = array_key_exists('episodeCount', $anime) && (int) $anime['episodeCount'] !== 0 $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) 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; return $untransformed;

View File

@ -46,8 +46,8 @@ final class MangaListTransformer extends AbstractTransformer {
$genres = array_column($manga['relationships']['genres'], 'name') ?? []; $genres = array_column($manga['relationships']['genres'], 'name') ?? [];
sort($genres); sort($genres);
$rating = (int) $item['attributes']['rating'] !== 0 $rating = (int) $item['attributes']['ratingTwenty'] !== 0
? 2 * $item['attributes']['rating'] ? $item['attributes']['ratingTwenty'] / 2
: '-'; : '-';
$totalChapters = ((int) $manga['chapterCount'] !== 0) $totalChapters = ((int) $manga['chapterCount'] !== 0)
@ -138,7 +138,7 @@ final class MangaListTransformer extends AbstractTransformer {
if (is_numeric($item['new_rating']) && $item['new_rating'] > 0) 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; return $map;

View File

@ -66,6 +66,7 @@ final class ParallelAPIRequest {
* Actually make the requests * Actually make the requests
* *
* @return array * @return array
* @throws \Throwable
*/ */
public function makeRequests(): array public function makeRequests(): array
{ {

View File

@ -21,6 +21,7 @@ use function Aviat\AnimeClient\loadToml;
use Aura\Session\SessionFactory; use Aura\Session\SessionFactory;
use Aviat\AnimeClient\Util; use Aviat\AnimeClient\Util;
use Aviat\AnimeClient\API\CacheTrait; use Aviat\AnimeClient\API\CacheTrait;
use Aviat\AnimeClient\API\Anilist;
use Aviat\AnimeClient\API\Kitsu; use Aviat\AnimeClient\API\Kitsu;
use Aviat\AnimeClient\API\Kitsu\KitsuRequestBuilder; use Aviat\AnimeClient\API\Kitsu\KitsuRequestBuilder;
use Aviat\Banker\Pool; 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)); $app_logger->pushHandler(new RotatingFileHandler($APP_DIR . '/logs/app-cli.log', Logger::NOTICE));
$kitsu_request_logger = new Logger('kitsu-request'); $kitsu_request_logger = new Logger('kitsu-request');
$kitsu_request_logger->pushHandler(new RotatingFileHandler($APP_DIR . '/logs/kitsu_request-cli.log', Logger::NOTICE)); $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($app_logger);
$container->setLogger($anilistRequestLogger, 'anilist-request');
$container->setLogger($kitsu_request_logger, 'kitsu-request'); $container->setLogger($kitsu_request_logger, 'kitsu-request');
// Create Config Object // Create Config Object
@ -122,6 +126,20 @@ class BaseCommand extends Command {
$model->setCache($cache); $model->setCache($cache);
return $model; 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) { $container->set('util', function($container) {
return new Util($container); return new Util($container);

View File

@ -16,19 +16,28 @@
namespace Aviat\AnimeClient\Command; namespace Aviat\AnimeClient\Command;
use Aviat\AnimeClient\API\{ use Aviat\AnimeClient\API\
FailedResponseException, {FailedResponseException, JsonAPI, Kitsu\Transformer\MangaListTransformer, ParallelAPIRequest};
JsonAPI, use Aviat\AnimeClient\API\Anilist\Transformer\{
ParallelAPIRequest AnimeListTransformer as AALT,
MangaListTransformer as AMLT,
}; };
use Aviat\AnimeClient\API\Mapping\{AnimeWatchingStatus, MangaReadingStatus};
use Aviat\AnimeClient\Types\{AnimeFormItem, MangaFormItem};
use Aviat\Ion\Json; use Aviat\Ion\Json;
use DateTime; use DateTime;
/** /**
* Clears the API Cache * Syncs list data between Anilist and Kitsu
*/ */
final class SyncLists extends BaseCommand { 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 * Model for making requests to Kitsu API
* @var \Aviat\AnimeClient\API\Kitsu\Model * @var \Aviat\AnimeClient\API\Kitsu\Model
@ -36,7 +45,7 @@ final class SyncLists extends BaseCommand {
protected $kitsuModel; protected $kitsuModel;
/** /**
* Run the Kitsu <=> MAL sync script * Run the Kitsu <=> Anilist sync script
* *
* @param array $args * @param array $args
* @param array $options * @param array $options
@ -48,6 +57,7 @@ final class SyncLists extends BaseCommand {
{ {
$this->setContainer($this->setupContainer()); $this->setContainer($this->setupContainer());
$this->setCache($this->container->get('cache')); $this->setCache($this->container->get('cache'));
$this->anilistModel = $this->container->get('anilist-model');
$this->kitsuModel = $this->container->get('kitsu-model'); $this->kitsuModel = $this->container->get('kitsu-model');
$this->sync('anime'); $this->sync('anime');
@ -64,18 +74,6 @@ final class SyncLists extends BaseCommand {
{ {
$uType = ucfirst($type); $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; $kitsuCount = 0;
try 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}"); $this->echoBox("Number of Kitsu {$type} list items: {$kitsuCount}");
$data = $this->diffLists($type); $data = $this->diffLists($type);
/* if ( ! empty($data['addToMAL'])) if ( ! empty($data['addToAnilist']))
{ {
$count = count($data['addToMAL']); $count = count($data['addToAnilist']);
$this->echoBox("Adding {$count} missing {$type} list items to MAL"); $this->echoBox("Adding {$count} missing {$type} list items to Anilist");
$this->updateMALListItems($data['addToMAL'], 'create', $type); $this->updateAnilistListItems($data['addToAnilist'], 'create', $type);
} }
if ( ! empty($data['updateMAL'])) if ( ! empty($data['updateAnilist']))
{ {
$count = count($data['updateMAL']); $count = count($data['updateAnilist']);
$this->echoBox("Updating {$count} outdated MAL {$type} list items"); $this->echoBox("Updating {$count} outdated Anilist {$type} list items");
$this->updateMALListItems($data['updateMAL'], 'update', $type); $this->updateAnilistListItems($data['updateAnilist'], 'update', $type);
} */ }
if ( ! empty($data['addToKitsu'])) 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 * @param string $type
* @return array * @return array
*/ */
/* protected function formatMALList(string $type): array protected function formatAnilistList(string $type): array
{ {
$type = ucfirst($type); $type = ucfirst($type);
$method = "formatMAL{$type}List"; $method = "formatAnilist{$type}List";
return $this->$method(); return $this->$method();
} */ }
/** /**
* Format a MAL anime list for comparison * Format an Anilist anime list for comparison
* *
* @return array * @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 = []; $output = [];
foreach ($transformedAnilist as $item)
// Bail early on empty list
if (empty($orig))
{ {
return []; $output[$item['mal_id']] = $item->toArray();
} }
// Due to xml parsing differences, $count = count($output);
// 1 item has no wrapping array. $this->echoBox("Number of Anilist anime list items: {$count}");
// 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),
]
];
}
return $output; return $output;
} */ }
/** /**
* Format a MAL manga list for comparison * Format an Anilist manga list for comparison
* *
* @return array * @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 = []; $output = [];
foreach ($transformedAnilist as $item)
// Bail early on empty list
if (empty($orig))
{ {
return []; $output[$item['mal_id']] = $item->toArray();
} }
// Due to xml parsing differences, $count = count($output);
// 1 item has no wrapping array. $this->echoBox("Number of Anilist manga list items: {$count}");
// 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),
]
];
}
return $output; return $output;
} */ }
/** /**
* Format a kitsu list for the sake of comparision * 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 protected function formatKitsuList(string $type = 'anime'): array
{ {
$data = $this->kitsuModel->{'getFull' . ucfirst($type) . 'List'}(); $data = $this->kitsuModel->{'getFullRaw' . ucfirst($type) . 'List'}();
if (empty($data)) 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) if ($malId === NULL)
{ {
continue; continue;
@ -309,31 +278,51 @@ final class SyncLists extends BaseCommand {
// Organize mappings, and ignore entries without mappings // Organize mappings, and ignore entries without mappings
$kitsuList = $this->formatKitsuList($type); $kitsuList = $this->formatKitsuList($type);
// Get MAL list data // Get Anilist list data
// $malList = $this->formatMALList($type); $anilistList = $this->formatAnilistList($type);
$itemsToAddToMAL = []; $itemsToAddToAnilist = [];
$itemsToAddToKitsu = []; $itemsToAddToKitsu = [];
$malUpdateItems = []; $anilistUpdateItems = [];
$kitsuUpdateItems = []; $kitsuUpdateItems = [];
// $malIds = array_column($malList, 'id'); $malBlackList = ($type === 'anime')
// $kitsuMalIds = array_column($kitsuList, 'malId'); ? [
// $missingMalIds = array_diff($malIds, $kitsuMalIds); 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) foreach($missingMalIds as $mid)
{ {
$itemsToAddToKitsu[] = array_merge($malList[$mid]['data'], [ $itemsToAddToKitsu[] = array_merge($anilistList[$mid]['data'], [
'id' => $this->kitsuModel->getKitsuIdFromMALId($mid, $type), 'id' => $this->kitsuModel->getKitsuIdFromMALId((string)$mid, $type),
'type' => $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) if ($item === NULL)
{ {
@ -345,25 +334,39 @@ final class SyncLists extends BaseCommand {
$kitsuUpdateItems[] = $item['data']; $kitsuUpdateItems[] = $item['data'];
} }
if (\in_array('mal', $item['updateType'], TRUE)) if (\in_array('anilist', $item['updateType'], TRUE))
{ {
$malUpdateItems[] = $item['data']; $anilistUpdateItems[] = $item['data'];
} }
continue; continue;
} }
$statusMap = ($type === 'anime') ? AnimeWatchingStatus::class : MangaReadingStatus::class;
// Looks like this item only exists on Kitsu // Looks like this item only exists on Kitsu
$itemsToAddToMAL[] = [ $kItem = $kitsuItem['data'];
'mal_id' => $kitsuItem['malId'], $newItemStatus = ($kItem['reconsuming'] === true) ? 'REPEATING' : $statusMap::KITSU_TO_ANILIST[$kItem['status']];
'data' => $kitsuItem['data'] $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 [ return [
// 'addToMAL' => $itemsToAddToMAL, 'addToAnilist' => $itemsToAddToAnilist,
// 'updateMAL' => $malUpdateItems, 'updateAnilist' => $anilistUpdateItems,
'addToKitsu' => $itemsToAddToKitsu, 'addToKitsu' => $itemsToAddToKitsu,
'updateKitsu' => $kitsuUpdateItems '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 * Compare two list items, and return the out of date one, if one exists
* *
* @param array $kitsuItem * @param array $kitsuItem
* @param array $malItem * @param array $anilistItem
* @return array|null * @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 = []; $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) 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 // No difference? Bail out early
@ -404,9 +416,18 @@ final class SyncLists extends BaseCommand {
'updateType' => [] 'updateType' => []
]; ];
$sameNotes = $diff['notes'] === 0;
$sameStatus = $diff['status'] === 0; $sameStatus = $diff['status'] === 0;
$sameProgress = $diff['progress'] === 0; $sameProgress = $diff['progress'] === 0;
$sameRating = $diff['rating'] === 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 // 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) if ($diff['progress'] === 1)
{ {
$update['data']['progress'] = $kitsuItem['data']['progress']; $update['data']['progress'] = $kitsuItem['data']['progress'];
$return['updateType'][] = 'mal'; $return['updateType'][] = 'anilist';
} }
else if($diff['progress'] === -1) else if($diff['progress'] === -1)
{ {
$update['data']['progress'] = $malItem['data']['progress']; $update['data']['progress'] = $anilistItem['data']['progress'];
$return['updateType'][] = 'kitsu'; $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... // If status and progress are different, it's a bit more complicated...
// But, at least for now, assume newer record is correct // But, at least for now, assume newer record is correct
if ( ! ($sameStatus || $sameProgress)) /* if ( ! ($sameStatus || $sameProgress))
{ {
if ($dateDiff === 1) if ($dateDiff === 1)
{ {
@ -437,59 +465,111 @@ final class SyncLists extends BaseCommand {
$update['data']['progress'] = $kitsuItem['data']['progress']; $update['data']['progress'] = $kitsuItem['data']['progress'];
} }
$return['updateType'][] = 'mal'; $return['updateType'][] = 'anilist';
} }
else if($dateDiff === -1) 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']; $update['data']['progress'] = $kitsuItem['data']['progress'];
} }
$return['updateType'][] = 'kitsu'; $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 ( ! $sameRating)
{ {
if ($dateDiff === 1) if ($kitsuItem['data']['rating'] !== 0)
{ {
$update['data']['rating'] = $kitsuItem['data']['rating']; $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'; $return['updateType'][] = 'kitsu';
} }
} }
// If status is different, use the status of the more recently updated item // If status is different, use the status of the more recently updated item
if ( ! $sameStatus) /* if ( ! $sameStatus)
{ {
if ($dateDiff === 1) if ($dateDiff === 1)
{ {
$update['data']['status'] = $kitsuItem['data']['status']; $update['data']['status'] = $kitsuItem['data']['status'];
$return['updateType'][] = 'mal'; $return['updateType'][] = 'anilist';
} }
else if ($dateDiff === -1) else if ($dateDiff === -1)
{ {
$update['data']['status'] = $malItem['data']['status']; $update['data']['status'] = $anilistItem['data']['status'];
$return['updateType'][] = 'kitsu'; $return['updateType'][] = 'kitsu';
} }
} } */
$return['meta'] = [ $return['meta'] = [
'kitsu' => $kitsuItem['data'], 'kitsu' => $kitsuItem['data'],
'mal' => $malItem['data'], 'anilist' => $anilistItem['data'],
'dateDiff' => $dateDiff, // 'dateDiff' => $dateDiff,
'diff' => $diff, 'diff' => $diff,
]; ];
$return['data'] = $update; $return['data'] = $update;
$return['updateType'] = array_unique($return['updateType']); $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; return $return;
} }
@ -505,9 +585,13 @@ final class SyncLists extends BaseCommand {
$requester = new ParallelAPIRequest(); $requester = new ParallelAPIRequest();
foreach($itemsToUpdate as $item) foreach($itemsToUpdate as $item)
{ {
$typeClass = '\\Aviat\\AnimeClient\\Types\\' . ucFirst($type) . 'FormItem';
if ($action === 'update') if ($action === 'update')
{ {
$requester->addRequest($this->kitsuModel->updateListItem($item)); $requester->addRequest(
$this->kitsuModel->updateListItem(new $typeClass($item))
);
} }
else if ($action === 'create') 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 array $itemsToUpdate
* @param string $action * @param string $action
* @param string $type * @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(); $requester = new ParallelAPIRequest();
$typeClass = '\\Aviat\\AnimeClient\\Types\\' . ucFirst($type) . 'FormItem';
foreach($itemsToUpdate as $item) foreach($itemsToUpdate as $item)
{ {
if ($action === 'update') if ($action === 'update')
{ {
$requester->addRequest($this->malModel->updateListItem($item, $type)); $requester->addRequest(
$this->anilistModel->updateListItem(new $typeClass($item), $type)
);
} }
else if ($action === 'create') else if ($action === 'create')
{ {
$data = $transformer->untransform($item); $requester->addRequest($this->anilistModel->createFullListItem($item, $type));
$requester->addRequest($this->malModel->createFullListItem($data, $type));
} }
} }
@ -566,20 +652,21 @@ final class SyncLists extends BaseCommand {
foreach($responses as $key => $response) foreach($responses as $key => $response)
{ {
$id = $itemsToUpdate[$key]['mal_id']; $id = $itemsToUpdate[$key]['mal_id'];
$goodResponse = (
($action === 'update' && $response === 'Updated') || $responseData = Json::decode($response);
($action === 'create' && $response === 'Created')
); // $id = $itemsToUpdate[$key]['id'];
if ($goodResponse) if ( ! array_key_exists('errors', $responseData))
{ {
$verb = ($action === 'update') ? 'updated' : 'created'; $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 else
{ {
dump($responseData);
$verb = ($action === 'update') ? 'update' : 'create'; $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}");
} }
} }
} */ }
} }

View File

@ -130,6 +130,11 @@ final class Manga extends Controller {
$this->redirect('manga/add', 303); $this->redirect('manga/add', 303);
} }
if (empty($data['mal_id']))
{
unset($data['mal_id']);
}
$result = $this->model->createLibraryItem($data); $result = $this->model->createLibraryItem($data);
if ($result) if ($result)
@ -182,8 +187,9 @@ final class Manga extends Controller {
*/ */
public function search(): void public function search(): void
{ {
$query_data = $this->request->getQueryParams(); $queryParams = $this->request->getQueryParams();
$this->outputJSON($this->model->search($query_data['query'])); $query = $queryParams['query'];
$this->outputJSON($this->model->search($query));
} }
/** /**
@ -218,13 +224,9 @@ final class Manga extends Controller {
} }
/** /**
* Update a manga item * Increment the progress of a manga item
*
* @throws \Aviat\Ion\Di\ContainerException
* @throws \Aviat\Ion\Di\NotFoundException
* @return void
*/ */
public function update(): void public function increment(): void
{ {
if (stripos($this->request->getHeader('content-type')[0], 'application/json') !== FALSE) if (stripos($this->request->getHeader('content-type')[0], 'application/json') !== FALSE)
{ {
@ -235,7 +237,7 @@ final class Manga extends Controller {
$data = $this->request->getParsedBody(); $data = $this->request->getParsedBody();
} }
$response = $this->model->updateLibraryItem(new MangaFormItem($data)); $response = $this->model->incrementLibraryItem(new MangaFormItem($data));
$this->cache->clear(); $this->cache->clear();
$this->outputJSON($response['body'], $response['statusCode']); $this->outputJSON($response['body'], $response['statusCode']);
@ -251,13 +253,11 @@ final class Manga extends Controller {
public function delete(): void public function delete(): void
{ {
$body = $this->request->getParsedBody(); $body = $this->request->getParsedBody();
$id = $body['id']; $response = $this->model->deleteLibraryItem($body['id'], $body['mal_id']);
$malId = $body['mal_id'];
$response = $this->model->deleteLibraryItem($id, $malId);
if ($response) if ($response)
{ {
$this->setFlashMessage("Successfully deleted manga.", 'success'); $this->setFlashMessage('Successfully deleted manga.', 'success');
$this->cache->clear(); $this->cache->clear();
} }
else else

View File

@ -31,6 +31,13 @@ use Aviat\Ion\Json;
*/ */
class Anime extends API { class Anime extends API {
/**
* Is the Anilist API enabled?
*
* @var boolean
*/
protected $anilistEnabled;
/** /**
* Model for making requests to Anilist API * Model for making requests to Anilist API
* *
@ -54,6 +61,9 @@ class Anime extends API {
{ {
$this->anilistModel = $container->get('anilist-model'); $this->anilistModel = $container->get('anilist-model');
$this->kitsuModel = $container->get('kitsu-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); $item = $this->kitsuModel->getListItem($itemId);
$array = $item->toArray(); $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'])) if (is_array($array['notes']))
{ {
$array['notes'] = ''; $array['notes'] = '';
@ -168,9 +166,9 @@ class Anime extends API {
$requester = new ParallelAPIRequest(); $requester = new ParallelAPIRequest();
$requester->addRequest($this->kitsuModel->createListItem($data), 'kitsu'); $requester->addRequest($this->kitsuModel->createListItem($data), 'kitsu');
// @TODO Make sure Anilist integration is optional if (array_key_exists('mal_id', $data) && $this->anilistEnabled)
if (array_key_exists('mal_id', $data)) { {
$requester->addRequest($this->anilistModel->createListItem($data), 'anilist'); $requester->addRequest($this->anilistModel->createListItem($data, 'ANIME'), 'anilist');
} }
$results = $requester->makeRequests(); $results = $requester->makeRequests();
@ -199,9 +197,9 @@ class Anime extends API {
$array = $data->toArray(); $array = $data->toArray();
// @TODO Make sure Anilist integration is optional if (array_key_exists('mal_id', $array) && $this->anilistEnabled)
if (array_key_exists('mal_id', $array)) { {
$requester->addRequest($this->anilistModel->incrementListItem($data), 'anilist'); $requester->addRequest($this->anilistModel->incrementListItem($data, 'ANIME'), 'anilist');
} }
$results = $requester->makeRequests(); $results = $requester->makeRequests();
@ -228,10 +226,9 @@ class Anime extends API {
$array = $data->toArray(); $array = $data->toArray();
// @TODO Make sure Anilist integration is optional if (array_key_exists('mal_id', $array) && $this->anilistEnabled)
if (array_key_exists('mal_id', $array))
{ {
$requester->addRequest($this->anilistModel->updateListItem($data), 'anilist'); $requester->addRequest($this->anilistModel->updateListItem($data, 'ANIME'), 'anilist');
} }
$results = $requester->makeRequests(); $results = $requester->makeRequests();
@ -257,9 +254,9 @@ class Anime extends API {
$requester = new ParallelAPIRequest(); $requester = new ParallelAPIRequest();
$requester->addRequest($this->kitsuModel->deleteListItem($id), 'kitsu'); $requester->addRequest($this->kitsuModel->deleteListItem($id), 'kitsu');
// @TODO Make sure Anilist integration is optional if ($malId !== null && $this->anilistEnabled)
if ($malId !== null) { {
$requester->addRequest($this->anilistModel->deleteListItem($malId), 'anilist'); $requester->addRequest($this->anilistModel->deleteListItem($malId, 'ANIME'), 'anilist');
} }
$results = $requester->makeRequests(); $results = $requester->makeRequests();

View File

@ -33,6 +33,19 @@ use Aviat\Ion\Json;
* Model for handling requests dealing with the manga list * Model for handling requests dealing with the manga list
*/ */
class Manga extends API { 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 * Model for making requests to Kitsu API
* @var \Aviat\AnimeClient\API\Kitsu\Model * @var \Aviat\AnimeClient\API\Kitsu\Model
@ -43,12 +56,14 @@ class Manga extends API {
* Constructor * Constructor
* *
* @param ContainerInterface $container * @param ContainerInterface $container
* @throws \Aviat\Ion\Di\ContainerException
* @throws \Aviat\Ion\Di\NotFoundException
*/ */
public function __construct(ContainerInterface $container) public function __construct(ContainerInterface $container)
{ {
$this->anilistModel = $container->get('anilist-model');
$this->kitsuModel = $container->get('kitsu-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 = new ParallelAPIRequest();
$requester->addRequest($this->kitsuModel->createListItem($data), 'kitsu'); $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(); $results = $requester->makeRequests();
return count($results) > 0; return count($results) > 0;
@ -137,6 +157,13 @@ class Manga extends API {
$requester = new ParallelAPIRequest(); $requester = new ParallelAPIRequest();
$requester->addRequest($this->kitsuModel->updateListItem($data), 'kitsu'); $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(); $results = $requester->makeRequests();
$body = Json::decode($results['kitsu']); $body = Json::decode($results['kitsu']);
$statusCode = array_key_exists('error', $body) ? 400: 200; $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 * Delete a list entry
* *
@ -159,6 +214,11 @@ class Manga extends API {
$requester = new ParallelAPIRequest(); $requester = new ParallelAPIRequest();
$requester->addRequest($this->kitsuModel->deleteListItem($id), 'kitsu'); $requester->addRequest($this->kitsuModel->deleteListItem($id), 'kitsu');
if ($malId !== null && $this->anilistEnabled)
{
$requester->addRequest($this->anilistModel->deleteListItem($malId, 'MANGA'), 'anilist');
}
$results = $requester->makeRequests(); $results = $requester->makeRequests();
return count($results) > 0; return count($results) > 0;