2016-12-21 12:46:20 -05:00
|
|
|
<?php declare(strict_types=1);
|
|
|
|
/**
|
2017-02-15 16:13:32 -05:00
|
|
|
* Hummingbird Anime List Client
|
2016-12-21 12:46:20 -05:00
|
|
|
*
|
2018-08-22 13:48:27 -04:00
|
|
|
* An API client for Kitsu to manage anime and manga watch lists
|
2016-12-21 12:46:20 -05:00
|
|
|
*
|
2020-04-10 15:39:39 -04:00
|
|
|
* PHP version 7.4
|
2016-12-21 12:46:20 -05:00
|
|
|
*
|
2017-02-15 16:13:32 -05:00
|
|
|
* @package HummingbirdAnimeClient
|
2017-01-06 23:34:56 -05:00
|
|
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
2020-01-08 15:39:49 -05:00
|
|
|
* @copyright 2015 - 2020 Timothy J. Warren
|
2017-01-06 23:34:56 -05:00
|
|
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
2020-04-10 15:39:39 -04:00
|
|
|
* @version 5
|
2017-03-07 20:53:58 -05:00
|
|
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
2017-01-11 10:34:24 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
namespace Aviat\AnimeClient\API\Kitsu;
|
2016-12-21 12:46:20 -05:00
|
|
|
|
2017-12-08 22:32:00 -05:00
|
|
|
use function Amp\Promise\wait;
|
2017-03-07 17:51:08 -05:00
|
|
|
|
2020-03-11 16:26:17 -04:00
|
|
|
use Amp\Http\Client\Request;
|
2017-03-28 11:01:38 -04:00
|
|
|
use Aviat\AnimeClient\API\{
|
|
|
|
CacheTrait,
|
|
|
|
JsonAPI,
|
|
|
|
Kitsu as K,
|
|
|
|
ParallelAPIRequest
|
|
|
|
};
|
2017-01-03 21:06:49 -05:00
|
|
|
use Aviat\AnimeClient\API\Kitsu\Transformer\{
|
2017-02-04 15:18:34 -05:00
|
|
|
AnimeTransformer,
|
|
|
|
AnimeListTransformer,
|
|
|
|
MangaTransformer,
|
2017-01-27 12:35:28 -05:00
|
|
|
MangaListTransformer
|
2017-01-03 21:06:49 -05:00
|
|
|
};
|
2019-12-09 14:34:23 -05:00
|
|
|
|
|
|
|
use Aviat\Banker\Exception\InvalidArgumentException;
|
2017-03-07 20:49:31 -05:00
|
|
|
use Aviat\Ion\{Di\ContainerAware, Json};
|
2016-12-21 12:46:20 -05:00
|
|
|
|
2019-12-09 14:34:23 -05:00
|
|
|
use Throwable;
|
|
|
|
|
2016-12-21 12:46:20 -05:00
|
|
|
/**
|
|
|
|
* Kitsu API Model
|
|
|
|
*/
|
2018-08-08 10:12:45 -04:00
|
|
|
final class Model {
|
2017-01-13 16:53:56 -05:00
|
|
|
use CacheTrait;
|
2017-01-05 13:41:32 -05:00
|
|
|
use ContainerAware;
|
2016-12-21 12:46:20 -05:00
|
|
|
use KitsuTrait;
|
2020-07-28 16:11:13 -04:00
|
|
|
use KitsuAnimeTrait;
|
|
|
|
use KitsuMangaTrait;
|
|
|
|
use KitsuMutationTrait;
|
2016-12-21 12:46:20 -05:00
|
|
|
|
2020-07-28 16:11:13 -04:00
|
|
|
protected const LIST_PAGE_SIZE = 100;
|
2016-12-22 21:36:23 -05:00
|
|
|
|
2017-01-06 21:39:01 -05:00
|
|
|
/**
|
|
|
|
* @var ListItem
|
|
|
|
*/
|
2020-07-28 16:11:13 -04:00
|
|
|
protected ListItem $listItem;
|
2017-02-04 15:18:34 -05:00
|
|
|
|
2016-12-22 21:36:23 -05:00
|
|
|
/**
|
2017-02-17 11:37:22 -05:00
|
|
|
* Constructor
|
|
|
|
*
|
|
|
|
* @param ListItem $listItem
|
2016-12-22 21:36:23 -05:00
|
|
|
*/
|
2017-01-06 21:39:01 -05:00
|
|
|
public function __construct(ListItem $listItem)
|
2016-12-21 12:46:20 -05:00
|
|
|
{
|
2016-12-22 21:36:23 -05:00
|
|
|
$this->animeTransformer = new AnimeTransformer();
|
2016-12-21 12:46:20 -05:00
|
|
|
$this->animeListTransformer = new AnimeListTransformer();
|
2017-01-04 13:16:58 -05:00
|
|
|
$this->mangaTransformer = new MangaTransformer();
|
2017-01-03 21:06:49 -05:00
|
|
|
$this->mangaListTransformer = new MangaListTransformer();
|
2018-11-09 10:38:35 -05:00
|
|
|
|
|
|
|
$this->listItem = $listItem;
|
2016-12-21 12:46:20 -05:00
|
|
|
}
|
|
|
|
|
2017-03-28 14:34:33 -04:00
|
|
|
/**
|
|
|
|
* Get the access token from the Kitsu API
|
|
|
|
*
|
|
|
|
* @param string $username
|
|
|
|
* @param string $password
|
2017-06-19 15:31:24 -04:00
|
|
|
* @return bool|array
|
2019-12-09 14:34:23 -05:00
|
|
|
* @throws Throwable
|
2017-03-28 14:34:33 -04:00
|
|
|
*/
|
|
|
|
public function authenticate(string $username, string $password)
|
|
|
|
{
|
2017-12-08 22:32:00 -05:00
|
|
|
// K::AUTH_URL
|
2020-07-31 19:03:27 -04:00
|
|
|
$response = $this->requestBuilder->getResponse('POST', K::AUTH_URL, [
|
2017-12-08 22:32:00 -05:00
|
|
|
'headers' => [
|
|
|
|
'accept' => NULL,
|
|
|
|
'Content-type' => 'application/x-www-form-urlencoded',
|
|
|
|
'client_id' => NULL,
|
|
|
|
'client_secret' => NULL
|
|
|
|
],
|
2017-03-28 14:34:33 -04:00
|
|
|
'form_params' => [
|
|
|
|
'grant_type' => 'password',
|
|
|
|
'username' => $username,
|
|
|
|
'password' => $password
|
|
|
|
]
|
|
|
|
]);
|
2020-03-11 16:26:17 -04:00
|
|
|
$data = Json::decode(wait($response->getBody()->buffer()));
|
2018-01-16 14:58:07 -05:00
|
|
|
|
2017-12-08 22:32:00 -05:00
|
|
|
if (array_key_exists('error', $data))
|
|
|
|
{
|
|
|
|
dump($data['error']);
|
|
|
|
dump($response);
|
|
|
|
die();
|
|
|
|
}
|
|
|
|
|
2018-10-05 14:32:05 -04:00
|
|
|
if (array_key_exists('access_token', $data))
|
|
|
|
{
|
|
|
|
return $data;
|
|
|
|
}
|
|
|
|
|
2017-03-28 14:34:33 -04:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2017-06-19 15:31:24 -04:00
|
|
|
/**
|
|
|
|
* Extend the current session with a refresh token
|
|
|
|
*
|
|
|
|
* @param string $token
|
|
|
|
* @return bool|array
|
2019-12-09 14:34:23 -05:00
|
|
|
* @throws Throwable
|
2017-06-19 15:31:24 -04:00
|
|
|
*/
|
|
|
|
public function reAuthenticate(string $token)
|
|
|
|
{
|
2020-07-31 19:03:27 -04:00
|
|
|
$response = $this->requestBuilder->getResponse('POST', K::AUTH_URL, [
|
2017-12-08 22:32:00 -05:00
|
|
|
'headers' => [
|
2020-05-08 21:34:36 -04:00
|
|
|
'accept' => NULL,
|
|
|
|
'Content-type' => 'application/x-www-form-urlencoded',
|
2017-12-08 22:32:00 -05:00
|
|
|
'Accept-encoding' => '*'
|
|
|
|
],
|
2017-06-19 15:31:24 -04:00
|
|
|
'form_params' => [
|
|
|
|
'grant_type' => 'refresh_token',
|
|
|
|
'refresh_token' => $token
|
|
|
|
]
|
|
|
|
]);
|
2020-03-11 16:26:17 -04:00
|
|
|
$data = Json::decode(wait($response->getBody()->buffer()));
|
2017-06-19 15:31:24 -04:00
|
|
|
|
2020-05-08 21:34:36 -04:00
|
|
|
if (array_key_exists('error', $data))
|
|
|
|
{
|
|
|
|
dump($data['error']);
|
|
|
|
dump($response);
|
|
|
|
die();
|
|
|
|
}
|
|
|
|
|
2017-06-19 15:31:24 -04:00
|
|
|
if (array_key_exists('access_token', $data))
|
|
|
|
{
|
|
|
|
return $data;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2017-01-10 12:35:46 -05:00
|
|
|
/**
|
|
|
|
* Get the userid for a username from Kitsu
|
|
|
|
*
|
|
|
|
* @param string $username
|
|
|
|
* @return string
|
2019-12-09 14:34:23 -05:00
|
|
|
* @throws InvalidArgumentException
|
2020-05-08 19:18:10 -04:00
|
|
|
* @throws Throwable
|
2017-01-10 12:35:46 -05:00
|
|
|
*/
|
2017-03-08 12:55:49 -05:00
|
|
|
public function getUserIdByUsername(string $username = NULL): string
|
2017-01-05 13:41:32 -05:00
|
|
|
{
|
2018-02-02 09:50:58 -05:00
|
|
|
if ($username === NULL)
|
2017-01-27 12:35:28 -05:00
|
|
|
{
|
|
|
|
$username = $this->getUsername();
|
|
|
|
}
|
2017-02-04 15:18:34 -05:00
|
|
|
|
2020-05-08 19:15:21 -04:00
|
|
|
return $this->getCached(K::AUTH_USER_ID_KEY, function(string $username) {
|
2020-07-31 19:03:27 -04:00
|
|
|
$data = $this->requestBuilder->getRequest('users', [
|
2017-01-26 13:03:38 -05:00
|
|
|
'query' => [
|
|
|
|
'filter' => [
|
|
|
|
'name' => $username
|
|
|
|
]
|
2017-01-05 13:41:32 -05:00
|
|
|
]
|
2017-01-26 13:03:38 -05:00
|
|
|
]);
|
2017-01-05 13:41:32 -05:00
|
|
|
|
2020-05-08 19:15:21 -04:00
|
|
|
return $data['data'][0]['id'] ?? NULL;
|
|
|
|
}, [$username]);
|
2017-01-05 13:41:32 -05:00
|
|
|
}
|
|
|
|
|
2017-03-08 12:55:49 -05:00
|
|
|
/**
|
|
|
|
* Get information about a character
|
|
|
|
*
|
|
|
|
* @param string $slug
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getCharacter(string $slug): array
|
|
|
|
{
|
2020-07-31 19:03:27 -04:00
|
|
|
return $this->requestBuilder->getRequest('characters', [
|
2017-03-08 12:55:49 -05:00
|
|
|
'query' => [
|
|
|
|
'filter' => [
|
2017-04-05 13:01:51 -04:00
|
|
|
'slug' => $slug,
|
2017-03-08 12:55:49 -05:00
|
|
|
],
|
2020-07-30 10:02:44 -04:00
|
|
|
'fields' => [ // For some characters, these filters cause issues...so leave them out
|
|
|
|
// 'anime' => 'canonicalTitle,abbreviatedTitles,titles,slug,posterImage',
|
|
|
|
// 'manga' => 'canonicalTitle,abbreviatedTitles,titles,slug,posterImage'
|
2017-04-05 13:01:51 -04:00
|
|
|
],
|
|
|
|
'include' => 'castings.person,castings.media'
|
2017-03-08 12:55:49 -05:00
|
|
|
]
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2018-10-19 09:30:27 -04:00
|
|
|
/**
|
|
|
|
* Get information about a person
|
|
|
|
*
|
|
|
|
* @param string $id
|
|
|
|
* @return array
|
2019-12-09 14:34:23 -05:00
|
|
|
* @throws InvalidArgumentException
|
2018-10-19 09:30:27 -04:00
|
|
|
*/
|
|
|
|
public function getPerson(string $id): array
|
|
|
|
{
|
2020-07-31 19:03:27 -04:00
|
|
|
return $this->getCached("kitsu-person-{$id}", fn () => $this->requestBuilder->getRequest("people/{$id}", [
|
2020-05-08 19:18:10 -04:00
|
|
|
'query' => [
|
|
|
|
'filter' => [
|
|
|
|
'id' => $id,
|
2018-10-26 13:08:45 -04:00
|
|
|
],
|
2020-05-08 19:18:10 -04:00
|
|
|
'fields' => [
|
|
|
|
'characters' => 'canonicalName,slug,image',
|
|
|
|
'characterVoices' => 'mediaCharacter',
|
|
|
|
'anime' => 'canonicalTitle,abbreviatedTitles,titles,slug,posterImage',
|
|
|
|
'manga' => 'canonicalTitle,abbreviatedTitles,titles,slug,posterImage',
|
|
|
|
'mediaCharacters' => 'role,media,character',
|
|
|
|
'mediaStaff' => 'role,media,person',
|
|
|
|
],
|
|
|
|
'include' => 'voices.mediaCharacter.media,voices.mediaCharacter.character,staff.media',
|
|
|
|
],
|
|
|
|
]));
|
2018-10-19 09:30:27 -04:00
|
|
|
}
|
|
|
|
|
2017-03-08 13:46:50 -05:00
|
|
|
/**
|
|
|
|
* Get profile information for the configured user
|
|
|
|
*
|
|
|
|
* @param string $username
|
|
|
|
* @return array
|
|
|
|
*/
|
2017-03-08 12:55:49 -05:00
|
|
|
public function getUserData(string $username): array
|
|
|
|
{
|
2020-07-31 19:03:27 -04:00
|
|
|
return $this->requestBuilder->getRequest('users', [
|
2017-03-08 12:55:49 -05:00
|
|
|
'query' => [
|
2017-03-31 13:37:53 -04:00
|
|
|
'filter' => [
|
|
|
|
'name' => $username,
|
|
|
|
],
|
|
|
|
'fields' => [
|
2018-10-30 11:42:32 -04:00
|
|
|
'anime' => 'slug,canonicalTitle,posterImage',
|
|
|
|
'manga' => 'slug,canonicalTitle,posterImage',
|
2018-11-01 22:15:20 -04:00
|
|
|
'characters' => 'slug,canonicalName,image',
|
2017-03-31 13:37:53 -04:00
|
|
|
],
|
2018-10-30 11:42:32 -04:00
|
|
|
'include' => 'waifu,favorites.item,stats'
|
2017-03-08 12:55:49 -05:00
|
|
|
]
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2016-12-21 12:46:20 -05:00
|
|
|
/**
|
2017-03-28 14:34:33 -04:00
|
|
|
* Search for an anime or manga
|
2016-12-21 12:46:20 -05:00
|
|
|
*
|
2017-03-28 14:34:33 -04:00
|
|
|
* @param string $type - 'anime' or 'manga'
|
|
|
|
* @param string $query - name of the item to search for
|
|
|
|
* @return array
|
2016-12-21 12:46:20 -05:00
|
|
|
*/
|
2017-03-28 14:34:33 -04:00
|
|
|
public function search(string $type, string $query): array
|
2016-12-21 12:46:20 -05:00
|
|
|
{
|
2017-03-28 14:34:33 -04:00
|
|
|
$options = [
|
|
|
|
'query' => [
|
|
|
|
'filter' => [
|
2018-09-20 16:08:46 -04:00
|
|
|
'text' => $query,
|
2017-03-28 14:34:33 -04:00
|
|
|
],
|
|
|
|
'page' => [
|
|
|
|
'offset' => 0,
|
|
|
|
'limit' => 20
|
|
|
|
],
|
2018-09-20 16:08:46 -04:00
|
|
|
'include' => 'mappings'
|
2017-01-05 13:41:32 -05:00
|
|
|
]
|
2017-03-28 14:34:33 -04:00
|
|
|
];
|
2016-12-21 12:46:20 -05:00
|
|
|
|
2020-07-31 19:03:27 -04:00
|
|
|
$raw = $this->requestBuilder->getRequest($type, $options);
|
2018-09-20 16:08:46 -04:00
|
|
|
$raw['included'] = JsonAPI::organizeIncluded($raw['included']);
|
2017-01-09 20:36:48 -05:00
|
|
|
|
2017-03-28 14:34:33 -04:00
|
|
|
foreach ($raw['data'] as &$item)
|
2017-01-05 13:41:32 -05:00
|
|
|
{
|
2017-03-28 14:34:33 -04:00
|
|
|
$item['attributes']['titles'] = K::filterTitles($item['attributes']);
|
|
|
|
array_shift($item['attributes']['titles']);
|
2018-09-20 16:08:46 -04:00
|
|
|
|
|
|
|
// Map the mal_id if it exists for syncing with other APIs
|
|
|
|
foreach($item['relationships']['mappings']['data'] as $rel)
|
|
|
|
{
|
|
|
|
$mapping = $raw['included']['mappings'][$rel['id']];
|
|
|
|
|
|
|
|
if ($mapping['attributes']['externalSite'] === "myanimelist/{$type}")
|
|
|
|
{
|
|
|
|
$item['mal_id'] = $mapping['attributes']['externalId'];
|
|
|
|
}
|
|
|
|
}
|
2016-12-21 12:46:20 -05:00
|
|
|
}
|
|
|
|
|
2017-03-28 14:34:33 -04:00
|
|
|
return $raw;
|
2016-12-21 12:46:20 -05:00
|
|
|
}
|
|
|
|
|
2017-03-28 16:52:27 -04:00
|
|
|
/**
|
|
|
|
* Find a media item on Kitsu by its associated MAL id
|
|
|
|
*
|
|
|
|
* @param string $malId
|
|
|
|
* @param string $type "anime" or "manga"
|
2017-04-10 15:31:35 -04:00
|
|
|
* @return string|NULL
|
2017-03-28 16:52:27 -04:00
|
|
|
*/
|
2018-11-09 10:38:35 -05:00
|
|
|
public function getKitsuIdFromMALId(string $malId, string $type='anime'): ?string
|
2017-03-28 16:52:27 -04:00
|
|
|
{
|
|
|
|
$options = [
|
|
|
|
'query' => [
|
|
|
|
'filter' => [
|
|
|
|
'external_site' => "myanimelist/{$type}",
|
|
|
|
'external_id' => $malId
|
|
|
|
],
|
|
|
|
'fields' => [
|
|
|
|
'media' => 'id,slug'
|
|
|
|
],
|
2017-09-15 15:04:57 -04:00
|
|
|
'include' => 'item'
|
2017-03-28 16:52:27 -04:00
|
|
|
]
|
|
|
|
];
|
|
|
|
|
2020-07-31 19:03:27 -04:00
|
|
|
$raw = $this->requestBuilder->getRequest('mappings', $options);
|
2017-03-28 16:52:27 -04:00
|
|
|
|
2017-04-10 15:31:35 -04:00
|
|
|
if ( ! array_key_exists('included', $raw))
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-03-28 16:52:27 -04:00
|
|
|
return $raw['included'][0]['id'];
|
|
|
|
}
|
|
|
|
|
2017-01-26 13:03:38 -05:00
|
|
|
/**
|
|
|
|
* Get the data for a specific list item, generally for editing
|
|
|
|
*
|
|
|
|
* @param string $listId - The unique identifier of that list item
|
2018-08-08 10:12:45 -04:00
|
|
|
* @return mixed
|
2017-01-26 13:03:38 -05:00
|
|
|
*/
|
2018-08-08 10:12:45 -04:00
|
|
|
public function getListItem(string $listId)
|
2017-01-06 21:39:01 -05:00
|
|
|
{
|
|
|
|
$baseData = $this->listItem->get($listId);
|
2017-01-13 16:53:56 -05:00
|
|
|
$included = JsonAPI::organizeIncludes($baseData['included']);
|
|
|
|
|
2018-10-19 10:40:11 -04:00
|
|
|
if (array_key_exists('anime', $included))
|
|
|
|
{
|
|
|
|
$included = JsonAPI::inlineIncludedRelationships($included, 'anime');
|
|
|
|
$baseData['data']['included'] = $included;
|
|
|
|
return $this->animeListTransformer->transform($baseData['data']);
|
|
|
|
}
|
2017-01-06 21:39:01 -05:00
|
|
|
|
2018-10-19 10:40:11 -04:00
|
|
|
if (array_key_exists('manga', $included))
|
2017-01-06 21:39:01 -05:00
|
|
|
{
|
2018-10-19 10:40:11 -04:00
|
|
|
$included = JsonAPI::inlineIncludedRelationships($included, 'manga');
|
|
|
|
$baseData['data']['included'] = $included;
|
|
|
|
$baseData['data']['manga'] = $baseData['included'][0];
|
|
|
|
return $this->mangaListTransformer->transform($baseData['data']);
|
2017-01-06 21:39:01 -05:00
|
|
|
}
|
2018-10-19 10:40:11 -04:00
|
|
|
|
|
|
|
return $baseData['data'];
|
2017-01-06 21:39:01 -05:00
|
|
|
}
|
|
|
|
|
2018-09-20 10:41:28 -04:00
|
|
|
/**
|
2020-07-28 16:11:13 -04:00
|
|
|
* Get the data to sync Kitsu anime/manga list with another API
|
2017-01-26 13:03:38 -05:00
|
|
|
*
|
2020-07-28 16:11:13 -04:00
|
|
|
* @param string $type
|
|
|
|
* @return array
|
|
|
|
* @throws InvalidArgumentException
|
|
|
|
* @throws Throwable
|
2017-01-26 13:03:38 -05:00
|
|
|
*/
|
2020-05-04 17:13:03 -04:00
|
|
|
public function getSyncList(string $type): array
|
|
|
|
{
|
|
|
|
$options = [
|
|
|
|
'filter' => [
|
2020-05-06 13:16:40 -04:00
|
|
|
'user_id' => $this->getUserId(),
|
2020-05-04 17:13:03 -04:00
|
|
|
'kind' => $type,
|
|
|
|
],
|
|
|
|
'include' => "{$type},{$type}.mappings",
|
|
|
|
'sort' => '-updated_at'
|
|
|
|
];
|
|
|
|
|
|
|
|
return $this->getRawSyncList($type, $options);
|
|
|
|
}
|
|
|
|
|
2020-04-21 19:22:56 -04:00
|
|
|
/**
|
|
|
|
* Get the aggregated pages of anime or manga history
|
|
|
|
*
|
|
|
|
* @param string $type
|
|
|
|
* @param int $entries
|
|
|
|
* @return array
|
|
|
|
* @throws InvalidArgumentException
|
|
|
|
* @throws Throwable
|
|
|
|
*/
|
2020-04-22 11:39:44 -04:00
|
|
|
protected function getRawHistoryList(string $type = 'anime', int $entries = 120): array
|
2020-04-21 19:22:56 -04:00
|
|
|
{
|
|
|
|
$size = 20;
|
|
|
|
$pages = ceil($entries / $size);
|
|
|
|
|
|
|
|
$requester = new ParallelAPIRequest();
|
|
|
|
|
|
|
|
// Set up requests
|
|
|
|
for ($i = 0; $i < $pages; $i++)
|
|
|
|
{
|
|
|
|
$offset = $i * $size;
|
|
|
|
$requester->addRequest($this->getRawHistoryPage($type, $offset, $size));
|
|
|
|
}
|
|
|
|
|
|
|
|
$responses = $requester->makeRequests();
|
|
|
|
$output = [];
|
|
|
|
|
|
|
|
foreach($responses as $response)
|
|
|
|
{
|
|
|
|
$data = Json::decode($response);
|
|
|
|
$output[] = $data;
|
|
|
|
}
|
|
|
|
|
|
|
|
return array_merge_recursive(...$output);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve one page of the anime or manga history
|
|
|
|
*
|
|
|
|
* @param string $type
|
|
|
|
* @param int $offset
|
|
|
|
* @param int $limit
|
|
|
|
* @return Request
|
|
|
|
* @throws InvalidArgumentException
|
|
|
|
*/
|
|
|
|
protected function getRawHistoryPage(string $type, int $offset, int $limit = 20): Request
|
|
|
|
{
|
2020-07-31 19:03:27 -04:00
|
|
|
return $this->requestBuilder->setUpRequest('GET', 'library-events', [
|
2020-04-21 19:22:56 -04:00
|
|
|
'query' => [
|
|
|
|
'filter' => [
|
|
|
|
'kind' => 'progressed,updated',
|
2020-05-06 13:16:40 -04:00
|
|
|
'userId' => $this->getUserId(),
|
2020-04-21 19:22:56 -04:00
|
|
|
],
|
|
|
|
'page' => [
|
|
|
|
'offset' => $offset,
|
|
|
|
'limit' => $limit,
|
|
|
|
],
|
2020-04-24 14:14:52 -04:00
|
|
|
'fields' => [
|
|
|
|
'anime' => 'canonicalTitle,titles,slug,posterImage',
|
|
|
|
'manga' => 'canonicalTitle,titles,slug,posterImage',
|
|
|
|
'libraryEntry' => 'reconsuming,reconsumeCount',
|
|
|
|
],
|
2020-04-21 19:22:56 -04:00
|
|
|
'sort' => '-updated_at',
|
2020-04-24 14:14:52 -04:00
|
|
|
'include' => 'anime,manga,libraryEntry',
|
2020-04-21 19:22:56 -04:00
|
|
|
],
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2020-05-06 13:16:40 -04:00
|
|
|
private function getUserId(): string
|
|
|
|
{
|
|
|
|
static $userId = NULL;
|
|
|
|
|
|
|
|
if ($userId === NULL)
|
|
|
|
{
|
|
|
|
$userId = $this->getUserIdByUsername($this->getUsername());
|
|
|
|
}
|
|
|
|
|
|
|
|
return $userId;
|
|
|
|
}
|
|
|
|
|
2017-03-07 20:49:31 -05:00
|
|
|
/**
|
|
|
|
* Get the kitsu username from config
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
2017-01-05 13:41:32 -05:00
|
|
|
private function getUsername(): string
|
2016-12-22 21:36:23 -05:00
|
|
|
{
|
2017-01-05 13:41:32 -05:00
|
|
|
return $this->getContainer()
|
|
|
|
->get('config')
|
|
|
|
->get(['kitsu_username']);
|
|
|
|
}
|
2017-02-04 15:18:34 -05:00
|
|
|
|
2017-03-07 20:49:31 -05:00
|
|
|
/**
|
2018-11-09 10:38:35 -05:00
|
|
|
* Get the raw data for the anime/manga id
|
2017-03-07 20:49:31 -05:00
|
|
|
*
|
|
|
|
* @param string $type
|
|
|
|
* @param string $id
|
|
|
|
* @return array
|
|
|
|
*/
|
2017-01-16 13:49:51 -05:00
|
|
|
private function getRawMediaDataById(string $type, string $id): array
|
|
|
|
{
|
|
|
|
$options = [
|
|
|
|
'query' => [
|
|
|
|
'include' => ($type === 'anime')
|
2017-09-15 15:04:57 -04:00
|
|
|
? 'categories,mappings,streamingLinks'
|
|
|
|
: 'categories,mappings',
|
2017-01-16 13:49:51 -05:00
|
|
|
]
|
|
|
|
];
|
|
|
|
|
2020-07-31 19:03:27 -04:00
|
|
|
$data = $this->requestBuilder->getRequest("{$type}/{$id}", $options);
|
2017-03-24 10:59:07 -04:00
|
|
|
|
|
|
|
if (empty($data['data']))
|
|
|
|
{
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
2017-01-16 13:49:51 -05:00
|
|
|
$baseData = $data['data']['attributes'];
|
2017-12-06 12:30:12 -05:00
|
|
|
$baseData['id'] = $id;
|
2017-01-16 13:49:51 -05:00
|
|
|
$baseData['included'] = $data['included'];
|
|
|
|
return $baseData;
|
|
|
|
}
|
2016-12-22 21:36:23 -05:00
|
|
|
|
2017-03-07 20:49:31 -05:00
|
|
|
/**
|
|
|
|
* Get media item by slug
|
|
|
|
*
|
|
|
|
* @param string $type
|
|
|
|
* @param string $slug
|
|
|
|
* @return array
|
|
|
|
*/
|
2017-01-05 13:41:32 -05:00
|
|
|
private function getRawMediaData(string $type, string $slug): array
|
|
|
|
{
|
|
|
|
$options = [
|
|
|
|
'query' => [
|
|
|
|
'filter' => [
|
|
|
|
'slug' => $slug
|
|
|
|
],
|
2017-03-31 13:37:53 -04:00
|
|
|
'fields' => [
|
2018-10-29 09:39:56 -04:00
|
|
|
'categories' => 'slug,title',
|
|
|
|
'characters' => 'slug,name,image',
|
|
|
|
'mappings' => 'externalSite,externalId',
|
|
|
|
'animeCharacters' => 'character,role',
|
2018-10-29 14:43:06 -04:00
|
|
|
'mediaCharacters' => 'character,role',
|
2017-03-31 13:37:53 -04:00
|
|
|
],
|
2017-01-05 22:24:45 -05:00
|
|
|
'include' => ($type === 'anime')
|
2018-11-01 22:15:20 -04:00
|
|
|
? 'staff,staff.person,categories,mappings,streamingLinks,animeCharacters.character,characters.character'
|
2018-10-29 14:43:06 -04:00
|
|
|
: 'staff,staff.person,categories,mappings,characters.character',
|
2017-01-05 13:41:32 -05:00
|
|
|
]
|
|
|
|
];
|
|
|
|
|
2020-07-31 19:03:27 -04:00
|
|
|
$data = $this->requestBuilder->getRequest($type, $options);
|
2017-03-24 09:08:39 -04:00
|
|
|
|
|
|
|
if (empty($data['data']))
|
|
|
|
{
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
2017-01-05 13:41:32 -05:00
|
|
|
$baseData = $data['data'][0]['attributes'];
|
2017-04-13 11:15:16 -04:00
|
|
|
$baseData['id'] = $data['data'][0]['id'];
|
2017-01-05 13:41:32 -05:00
|
|
|
$baseData['included'] = $data['included'];
|
2016-12-22 21:36:23 -05:00
|
|
|
return $baseData;
|
|
|
|
}
|
2020-05-04 17:13:03 -04:00
|
|
|
|
|
|
|
private function getListCount(string $type, string $status = ''): int
|
|
|
|
{
|
|
|
|
$options = [
|
|
|
|
'query' => [
|
|
|
|
'filter' => [
|
2020-05-06 13:16:40 -04:00
|
|
|
'user_id' => $this->getUserId(),
|
2020-05-04 17:13:03 -04:00
|
|
|
'kind' => $type,
|
|
|
|
],
|
|
|
|
'page' => [
|
|
|
|
'limit' => 1
|
|
|
|
],
|
|
|
|
'sort' => '-updated_at'
|
|
|
|
]
|
|
|
|
];
|
|
|
|
|
|
|
|
if ( ! empty($status))
|
|
|
|
{
|
|
|
|
$options['query']['filter']['status'] = $status;
|
|
|
|
}
|
|
|
|
|
2020-07-31 19:03:27 -04:00
|
|
|
$response = $this->requestBuilder->getRequest('library-entries', $options);
|
2020-05-04 17:13:03 -04:00
|
|
|
|
|
|
|
return $response['meta']['count'];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the full anime list
|
|
|
|
*
|
|
|
|
* @param string $type
|
|
|
|
* @param array $options
|
|
|
|
* @return array
|
|
|
|
* @throws InvalidArgumentException
|
|
|
|
* @throws Throwable
|
|
|
|
*/
|
|
|
|
private function getRawSyncList(string $type, array $options): array
|
|
|
|
{
|
|
|
|
$count = $this->getListCount($type);
|
|
|
|
$size = static::LIST_PAGE_SIZE;
|
|
|
|
$pages = ceil($count / $size);
|
|
|
|
|
|
|
|
$requester = new ParallelAPIRequest();
|
|
|
|
|
|
|
|
// Set up requests
|
|
|
|
for ($i = 0; $i < $pages; $i++)
|
|
|
|
{
|
|
|
|
$offset = $i * $size;
|
|
|
|
$requester->addRequest($this->getRawSyncListPage($type, $size, $offset, $options));
|
|
|
|
}
|
|
|
|
|
|
|
|
$responses = $requester->makeRequests();
|
|
|
|
$output = [];
|
|
|
|
|
|
|
|
foreach($responses as $response)
|
|
|
|
{
|
|
|
|
$data = Json::decode($response);
|
|
|
|
$output[] = $data;
|
|
|
|
}
|
|
|
|
|
|
|
|
return array_merge_recursive(...$output);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the full anime list in paginated form
|
|
|
|
*
|
|
|
|
* @param string $type
|
|
|
|
* @param int $limit
|
|
|
|
* @param int $offset
|
|
|
|
* @param array $options
|
|
|
|
* @return Request
|
|
|
|
* @throws InvalidArgumentException
|
|
|
|
*/
|
|
|
|
private function getRawSyncListPage(string $type, int $limit, int $offset = 0, array $options = []): Request
|
|
|
|
{
|
|
|
|
$defaultOptions = [
|
|
|
|
'filter' => [
|
2020-05-06 13:16:40 -04:00
|
|
|
'user_id' => $this->getUserId(),
|
2020-05-04 17:13:03 -04:00
|
|
|
'kind' => $type,
|
|
|
|
],
|
|
|
|
'page' => [
|
|
|
|
'offset' => $offset,
|
|
|
|
'limit' => $limit
|
|
|
|
],
|
|
|
|
'sort' => '-updated_at'
|
|
|
|
];
|
|
|
|
$options = array_merge($defaultOptions, $options);
|
|
|
|
|
2020-07-31 19:03:27 -04:00
|
|
|
return $this->requestBuilder->setUpRequest('GET', 'library-entries', ['query' => $options]);
|
2020-05-04 17:13:03 -04:00
|
|
|
}
|
2016-12-21 12:46:20 -05:00
|
|
|
}
|