2018-08-10 20:10:19 -04:00
|
|
|
<?php declare(strict_types=1);
|
|
|
|
/**
|
|
|
|
* Hummingbird Anime List Client
|
|
|
|
*
|
2018-08-22 13:48:27 -04:00
|
|
|
* An API client for Kitsu to manage anime and manga watch lists
|
2018-08-10 20:10:19 -04:00
|
|
|
*
|
2020-12-10 17:06:50 -05:00
|
|
|
* PHP version 7.4+
|
2018-08-10 20:10:19 -04:00
|
|
|
*
|
|
|
|
* @package HummingbirdAnimeClient
|
|
|
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
2021-01-13 01:52:03 -05:00
|
|
|
* @copyright 2015 - 2021 Timothy J. Warren
|
2018-08-10 20:10:19 -04:00
|
|
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
2020-12-10 17:06:50 -05:00
|
|
|
* @version 5.2
|
2018-08-10 20:10:19 -04:00
|
|
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace Aviat\AnimeClient\API\Anilist;
|
|
|
|
|
2018-10-09 18:10:20 -04:00
|
|
|
use function Amp\Promise\wait;
|
|
|
|
|
2018-09-26 22:31:04 -04:00
|
|
|
use InvalidArgumentException;
|
|
|
|
|
2020-03-11 16:26:17 -04:00
|
|
|
use Amp\Http\Client\Request;
|
2020-12-10 15:59:37 -05:00
|
|
|
use Aviat\AnimeClient\Anilist;
|
2018-08-15 08:51:37 -04:00
|
|
|
use Aviat\AnimeClient\API\Mapping\{AnimeWatchingStatus, MangaReadingStatus};
|
|
|
|
use Aviat\AnimeClient\Types\FormItem;
|
2018-10-09 18:10:20 -04:00
|
|
|
use Aviat\Ion\Json;
|
2019-12-06 15:46:56 -05:00
|
|
|
use Aviat\Ion\Di\Exception\ContainerException;
|
|
|
|
use Aviat\Ion\Di\Exception\NotFoundException;
|
2018-08-15 08:51:37 -04:00
|
|
|
|
2019-12-09 14:34:23 -05:00
|
|
|
use Throwable;
|
|
|
|
|
2018-08-10 20:10:19 -04:00
|
|
|
/**
|
|
|
|
* Anilist API Model
|
|
|
|
*/
|
2018-08-15 08:51:37 -04:00
|
|
|
final class Model
|
|
|
|
{
|
2020-08-06 09:39:12 -04:00
|
|
|
use RequestBuilderTrait;
|
2018-08-15 08:51:37 -04:00
|
|
|
/**
|
|
|
|
* @var ListItem
|
|
|
|
*/
|
2020-04-10 20:01:46 -04:00
|
|
|
private ListItem $listItem;
|
2018-08-15 08:51:37 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
|
|
|
* @param ListItem $listItem
|
|
|
|
*/
|
|
|
|
public function __construct(ListItem $listItem)
|
|
|
|
{
|
|
|
|
$this->listItem = $listItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
// ! Generic API calls
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
|
2018-10-09 18:10:20 -04:00
|
|
|
/**
|
|
|
|
* Attempt to get an auth token
|
|
|
|
*
|
|
|
|
* @param string $code - The request token
|
|
|
|
* @param string $redirectUri - The oauth callback url
|
|
|
|
* @return array
|
2019-12-09 14:34:23 -05:00
|
|
|
* @throws Throwable
|
2018-10-09 18:10:20 -04:00
|
|
|
*/
|
|
|
|
public function authenticate(string $code, string $redirectUri): array
|
|
|
|
{
|
|
|
|
$config = $this->getContainer()->get('config');
|
|
|
|
$request = $this->requestBuilder
|
|
|
|
->newRequest('POST', Anilist::TOKEN_URL)
|
|
|
|
->setJsonBody([
|
|
|
|
'grant_type' => 'authorization_code',
|
|
|
|
'client_id' => $config->get(['anilist', 'client_id']),
|
|
|
|
'client_secret' => $config->get(['anilist', 'client_secret']),
|
|
|
|
'redirect_uri' => $redirectUri,
|
|
|
|
'code' => $code,
|
|
|
|
])
|
|
|
|
->getFullRequest();
|
|
|
|
|
2020-08-24 13:09:43 -04:00
|
|
|
$response = $this->requestBuilder->getResponseFromRequest($request);
|
2018-10-09 18:10:20 -04:00
|
|
|
|
2020-03-11 16:26:17 -04:00
|
|
|
return Json::decode(wait($response->getBody()->buffer()));
|
2018-10-09 18:10:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check auth status with simple API call
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function checkAuth(): array
|
|
|
|
{
|
2020-08-05 20:57:01 -04:00
|
|
|
return $this->requestBuilder->runQuery('CheckLogin');
|
2018-10-09 18:10:20 -04:00
|
|
|
}
|
|
|
|
|
2018-09-26 22:31:04 -04:00
|
|
|
/**
|
|
|
|
* Get user list data for syncing with Kitsu
|
|
|
|
*
|
|
|
|
* @param string $type
|
|
|
|
* @return array
|
2019-12-06 15:46:56 -05:00
|
|
|
* @throws ContainerException
|
|
|
|
* @throws NotFoundException
|
2018-09-26 22:31:04 -04:00
|
|
|
*/
|
|
|
|
public function getSyncList(string $type = 'anime'): array
|
|
|
|
{
|
|
|
|
$config = $this->container->get('config');
|
|
|
|
$anilistUser = $config->get(['anilist', 'username']);
|
|
|
|
|
2019-07-11 10:28:09 -04:00
|
|
|
if ( ! (is_string($anilistUser) && $anilistUser !== ''))
|
2018-09-26 22:31:04 -04:00
|
|
|
{
|
|
|
|
throw new InvalidArgumentException('Anilist username is not defined in config');
|
|
|
|
}
|
|
|
|
|
2020-08-05 20:57:01 -04:00
|
|
|
return $this->requestBuilder->runQuery('SyncUserList', [
|
2018-09-26 22:31:04 -04:00
|
|
|
'name' => $anilistUser,
|
|
|
|
'type' => $type,
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2018-08-15 08:51:37 -04:00
|
|
|
/**
|
|
|
|
* Create a list item
|
|
|
|
*
|
|
|
|
* @param array $data
|
|
|
|
* @param string $type
|
|
|
|
* @return Request
|
|
|
|
*/
|
2020-01-15 12:35:37 -05:00
|
|
|
public function createListItem(array $data, string $type = 'anime'): ?Request
|
2018-08-15 08:51:37 -04:00
|
|
|
{
|
2020-01-15 15:22:38 -05:00
|
|
|
if ($data['mal_id'] === NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
2018-08-15 08:51:37 -04:00
|
|
|
|
2018-09-26 22:31:04 -04:00
|
|
|
$mediaId = $this->getMediaIdFromMalId($data['mal_id'], mb_strtoupper($type));
|
2018-09-20 10:41:28 -04:00
|
|
|
|
2020-01-15 12:35:37 -05:00
|
|
|
if ($mediaId === NULL)
|
2018-09-26 22:31:04 -04:00
|
|
|
{
|
2020-01-15 12:35:37 -05:00
|
|
|
return NULL;
|
|
|
|
}
|
2018-09-26 22:31:04 -04:00
|
|
|
|
2020-01-15 15:22:38 -05:00
|
|
|
$createData = [];
|
|
|
|
|
2018-10-01 13:03:48 -04:00
|
|
|
if ($type === 'ANIME')
|
2018-09-26 22:31:04 -04:00
|
|
|
{
|
2018-08-15 08:51:37 -04:00
|
|
|
$createData = [
|
2018-09-20 10:41:28 -04:00
|
|
|
'id' => $mediaId,
|
|
|
|
'status' => AnimeWatchingStatus::KITSU_TO_ANILIST[$data['status']],
|
2018-08-15 08:51:37 -04:00
|
|
|
];
|
2018-09-26 22:31:04 -04:00
|
|
|
}
|
2018-10-01 13:03:48 -04:00
|
|
|
elseif ($type === 'MANGA')
|
2018-09-26 22:31:04 -04:00
|
|
|
{
|
2018-08-15 08:51:37 -04:00
|
|
|
$createData = [
|
2018-09-20 10:41:28 -04:00
|
|
|
'id' => $mediaId,
|
|
|
|
'status' => MangaReadingStatus::KITSU_TO_ANILIST[$data['status']],
|
2018-08-15 08:51:37 -04:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2019-12-06 15:46:56 -05:00
|
|
|
return $this->listItem->create($createData);
|
2018-08-15 08:51:37 -04:00
|
|
|
}
|
|
|
|
|
2018-09-26 22:31:04 -04:00
|
|
|
/**
|
|
|
|
* Create a list item with all the relevant data
|
|
|
|
*
|
|
|
|
* @param array $data
|
2019-07-11 15:24:34 -04:00
|
|
|
* @param string $type
|
2018-09-26 22:31:04 -04:00
|
|
|
* @return Request
|
|
|
|
*/
|
2019-07-11 15:24:34 -04:00
|
|
|
public function createFullListItem(array $data, string $type): Request
|
2018-09-26 22:31:04 -04:00
|
|
|
{
|
|
|
|
$createData = $data['data'];
|
2019-07-11 15:24:34 -04:00
|
|
|
$mediaId = $this->getMediaIdFromMalId($data['mal_id'], strtoupper($type));
|
2018-10-05 14:32:05 -04:00
|
|
|
|
2019-07-11 16:38:21 -04:00
|
|
|
if (empty($mediaId))
|
|
|
|
{
|
|
|
|
throw new MissingIdException('No id mapping found');
|
|
|
|
}
|
|
|
|
|
2018-09-26 22:31:04 -04:00
|
|
|
$createData['id'] = $mediaId;
|
2018-10-05 14:32:05 -04:00
|
|
|
|
2018-09-26 22:31:04 -04:00
|
|
|
return $this->listItem->createFull($createData);
|
|
|
|
}
|
|
|
|
|
2018-08-15 08:51:37 -04:00
|
|
|
/**
|
|
|
|
* Get the data for a specific list item, generally for editing
|
|
|
|
*
|
2018-09-20 10:41:28 -04:00
|
|
|
* @param string $malId - The unique identifier of that list item
|
2018-11-09 10:38:35 -05:00
|
|
|
* @param string $type - Them media type (anime/manga)
|
2019-12-09 14:41:04 -05:00
|
|
|
*
|
|
|
|
* @return array
|
2018-08-15 08:51:37 -04:00
|
|
|
*/
|
2018-09-26 22:31:04 -04:00
|
|
|
public function getListItem(string $malId, string $type): array
|
2018-08-15 08:51:37 -04:00
|
|
|
{
|
2018-09-26 22:31:04 -04:00
|
|
|
$id = $this->getListIdFromMalId($malId, $type);
|
2018-09-20 16:08:46 -04:00
|
|
|
|
|
|
|
$data = $this->listItem->get($id)['data'];
|
|
|
|
|
|
|
|
return ($data !== null)
|
|
|
|
? $data['MediaList']
|
|
|
|
: [];
|
2018-09-20 10:41:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Increase the watch count for the current list item
|
|
|
|
*
|
|
|
|
* @param FormItem $data
|
2018-11-09 10:38:35 -05:00
|
|
|
* @param string $type - Them media type (anime/manga)
|
2018-09-20 10:41:28 -04:00
|
|
|
* @return Request
|
|
|
|
*/
|
2020-01-15 12:35:37 -05:00
|
|
|
public function incrementListItem(FormItem $data, string $type): ?Request
|
2018-09-20 10:41:28 -04:00
|
|
|
{
|
2018-09-26 22:31:04 -04:00
|
|
|
$id = $this->getListIdFromMalId($data['mal_id'], $type);
|
2020-01-15 12:35:37 -05:00
|
|
|
if ($id === NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
2018-09-20 10:41:28 -04:00
|
|
|
|
|
|
|
return $this->listItem->increment($id, $data['data']);
|
2018-08-15 08:51:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Modify a list item
|
|
|
|
*
|
|
|
|
* @param FormItem $data
|
2018-11-09 10:38:35 -05:00
|
|
|
* @param string $type - Them media type (anime/manga)
|
2018-08-15 08:51:37 -04:00
|
|
|
* @return Request
|
|
|
|
*/
|
2020-01-15 12:35:37 -05:00
|
|
|
public function updateListItem(FormItem $data, string $type): ?Request
|
2018-08-15 08:51:37 -04:00
|
|
|
{
|
2018-09-26 22:31:04 -04:00
|
|
|
$id = $this->getListIdFromMalId($data['mal_id'], mb_strtoupper($type));
|
2018-09-20 10:41:28 -04:00
|
|
|
|
2020-01-15 12:35:37 -05:00
|
|
|
if ($id === NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-09-20 10:41:28 -04:00
|
|
|
return $this->listItem->update($id, $data['data']);
|
2018-08-15 08:51:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove a list item
|
|
|
|
*
|
2018-09-20 16:08:46 -04:00
|
|
|
* @param string $malId - The id of the list item to remove
|
2019-07-11 15:24:34 -04:00
|
|
|
* @param string $type - Them media type (anime/manga)
|
2018-08-15 08:51:37 -04:00
|
|
|
* @return Request
|
|
|
|
*/
|
2020-01-15 12:35:37 -05:00
|
|
|
public function deleteListItem(string $malId, string $type): ?Request
|
2018-08-15 08:51:37 -04:00
|
|
|
{
|
2020-01-15 12:35:37 -05:00
|
|
|
$id = $this->getListIdFromMalId($malId, $type);
|
|
|
|
if ($id === NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
2018-09-20 16:08:46 -04:00
|
|
|
|
2020-01-15 12:35:37 -05:00
|
|
|
return $this->listItem->delete($id);
|
2018-08-15 08:51:37 -04:00
|
|
|
}
|
2018-09-20 10:41:28 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the id of the specific list entry from the malId
|
|
|
|
*
|
|
|
|
* @param string $malId
|
2018-11-09 10:38:35 -05:00
|
|
|
* @param string $type - The media type (anime/manga)
|
2018-09-20 10:41:28 -04:00
|
|
|
* @return string
|
|
|
|
*/
|
2018-09-26 22:31:04 -04:00
|
|
|
public function getListIdFromMalId(string $malId, string $type): ?string
|
2018-09-20 10:41:28 -04:00
|
|
|
{
|
2018-09-26 22:31:04 -04:00
|
|
|
$mediaId = $this->getMediaIdFromMalId($malId, $type);
|
2020-01-15 12:35:37 -05:00
|
|
|
if ($mediaId === NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-09-26 22:31:04 -04:00
|
|
|
return $this->getListIdFromMediaId($mediaId);
|
|
|
|
}
|
2018-10-05 14:32:05 -04:00
|
|
|
|
2018-09-26 22:31:04 -04:00
|
|
|
/**
|
2018-11-09 10:38:35 -05:00
|
|
|
* Get the Anilist list item id from the media id from its MAL id
|
2018-09-26 22:31:04 -04:00
|
|
|
* this way is more accurate than getting the list item id
|
|
|
|
* directly from the MAL id
|
2020-08-05 20:57:01 -04:00
|
|
|
*
|
|
|
|
* @param string $mediaId
|
|
|
|
* @return string|null
|
2018-09-26 22:31:04 -04:00
|
|
|
*/
|
2020-08-05 20:57:01 -04:00
|
|
|
private function getListIdFromMediaId(string $mediaId): ?string
|
2018-09-26 22:31:04 -04:00
|
|
|
{
|
|
|
|
$config = $this->container->get('config');
|
|
|
|
$anilistUser = $config->get(['anilist', 'username']);
|
2018-10-05 14:32:05 -04:00
|
|
|
|
2020-08-05 20:57:01 -04:00
|
|
|
$info = $this->requestBuilder->runQuery('ListItemIdByMediaId', [
|
2018-09-26 22:31:04 -04:00
|
|
|
'id' => $mediaId,
|
|
|
|
'userName' => $anilistUser,
|
|
|
|
]);
|
2018-10-05 14:32:05 -04:00
|
|
|
|
2020-08-05 20:57:01 -04:00
|
|
|
if ( ! empty($info['errors']))
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-03-11 16:26:17 -04:00
|
|
|
return (string)$info['data']['MediaList']['id'];
|
2018-09-20 10:41:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the Anilist media id from the malId
|
|
|
|
*
|
|
|
|
* @param string $malId
|
|
|
|
* @param string $type
|
2018-09-20 16:08:46 -04:00
|
|
|
* @return string
|
2018-09-20 10:41:28 -04:00
|
|
|
*/
|
2018-09-20 16:08:46 -04:00
|
|
|
private function getMediaIdFromMalId(string $malId, string $type = 'ANIME'): ?string
|
2018-09-20 10:41:28 -04:00
|
|
|
{
|
2019-10-08 19:59:47 -04:00
|
|
|
if ($malId === '')
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-08-05 20:57:01 -04:00
|
|
|
$info = $this->requestBuilder->runQuery('MediaIdByMalId', [
|
2018-09-20 10:41:28 -04:00
|
|
|
'id' => $malId,
|
2018-09-26 22:31:04 -04:00
|
|
|
'type' => mb_strtoupper($type),
|
|
|
|
]);
|
2018-10-05 14:32:05 -04:00
|
|
|
|
2019-10-08 19:59:47 -04:00
|
|
|
if (array_key_exists('errors', $info))
|
2019-10-07 20:10:27 -04:00
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-09-20 16:08:46 -04:00
|
|
|
return (string)$info['data']['Media']['id'];
|
2018-09-20 10:41:28 -04:00
|
|
|
}
|
2018-08-10 20:10:19 -04:00
|
|
|
}
|