HummingBirdAnimeClient/src/API/Kitsu/Model.php

456 lines
10 KiB
PHP
Raw Normal View History

2016-12-21 12:46:20 -05:00
<?php declare(strict_types=1);
/**
* Anime List Client
*
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
*
* PHP version 7
*
2017-01-06 23:34:56 -05:00
* @package AnimeListClient
* @author Timothy J. Warren <tim@timshomepage.net>
2017-01-11 10:30:53 -05:00
* @copyright 2015 - 2017 Timothy J. Warren
2017-01-06 23:34:56 -05:00
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 4.0
* @link https://github.com/timw4mail/HummingBirdAnimeClient
*/
namespace Aviat\AnimeClient\API\Kitsu;
2016-12-21 12:46:20 -05:00
use Amp\Artax\Request;
use Aviat\AnimeClient\API\CacheTrait;
2017-01-12 15:41:20 -05:00
use Aviat\AnimeClient\API\JsonAPI;
use Aviat\AnimeClient\API\Kitsu as K;
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
};
use Aviat\Ion\Di\ContainerAware;
use Aviat\Ion\Json;
2016-12-21 12:46:20 -05:00
/**
* Kitsu API Model
*/
class Model {
use CacheTrait;
use ContainerAware;
2016-12-21 12:46:20 -05:00
use KitsuTrait;
/**
* Class to map anime list items
* to a common format used by
* templates
*
* @var AnimeListTransformer
*/
protected $animeListTransformer;
/**
* @var AnimeTransformer
*/
protected $animeTransformer;
/**
* @var ListItem
*/
protected $listItem;
/**
* @var MangaTransformer
*/
protected $mangaTransformer;
2017-01-04 13:16:58 -05:00
/**
* @var MangaListTransformer
*/
2017-01-03 21:06:49 -05:00
protected $mangaListTransformer;
2017-02-04 15:18:34 -05:00
2017-01-03 21:06:49 -05:00
/**
* Constructor.
*/
public function __construct(ListItem $listItem)
2016-12-21 12:46:20 -05:00
{
$this->animeTransformer = new AnimeTransformer();
2016-12-21 12:46:20 -05:00
$this->animeListTransformer = new AnimeListTransformer();
$this->listItem = $listItem;
2017-01-04 13:16:58 -05:00
$this->mangaTransformer = new MangaTransformer();
2017-01-03 21:06:49 -05:00
$this->mangaListTransformer = new MangaListTransformer();
2016-12-21 12:46:20 -05:00
}
/**
* Get the userid for a username from Kitsu
*
* @param string $username
* @return string
*/
2017-01-27 12:35:28 -05:00
public function getUserIdByUsername(string $username = NULL)
{
2017-01-27 12:35:28 -05:00
if (is_null($username))
{
$username = $this->getUsername();
}
2017-02-04 15:18:34 -05:00
2017-01-27 12:35:28 -05:00
$cacheItem = $this->cache->getItem(K::AUTH_USER_ID_KEY);
2017-02-04 15:18:34 -05:00
if ( ! $cacheItem->isHit())
{
$data = $this->getRequest('users', [
'query' => [
'filter' => [
'name' => $username
]
]
]);
$cacheItem->set($data['data'][0]['id']);
$cacheItem->save();
}
2017-02-04 15:18:34 -05:00
return $cacheItem->get();
}
2016-12-21 12:46:20 -05:00
/**
* Get the access token from the Kitsu API
*
* @param string $username
* @param string $password
* @return bool|string
*/
public function authenticate(string $username, string $password)
{
$response = $this->getResponse('POST', K::AUTH_URL, [
'headers' => [],
'form_params' => [
2016-12-21 12:46:20 -05:00
'grant_type' => 'password',
'username' => $username,
'password' => $password
]
2016-12-21 12:46:20 -05:00
]);
$data = Json::decode((string)$response->getBody());
if (array_key_exists('access_token', $data))
{
2017-01-27 12:35:28 -05:00
return $data;
2016-12-21 12:46:20 -05:00
}
return false;
}
/**
* Get information about a particular anime
*
2017-01-16 13:49:51 -05:00
* @param string $slug
* @return array
*/
2017-01-16 13:49:51 -05:00
public function getAnime(string $slug): array
{
// @TODO catch non-existent anime
2017-01-16 13:49:51 -05:00
$baseData = $this->getRawMediaData('anime', $slug);
return $this->animeTransformer->transform($baseData);
}
2017-02-04 15:18:34 -05:00
/**
* Get information about a particular anime
*
* @param string $animeId
* @return array
*/
2017-01-16 13:49:51 -05:00
public function getAnimeById(string $animeId): array
{
$baseData = $this->getRawMediaDataById('anime', $animeId);
return $this->animeTransformer->transform($baseData);
}
2017-02-04 15:18:34 -05:00
/**
* Get the mal id for the anime represented by the kitsu id
* to enable updating MyAnimeList
*
* @param string $kitsuAnimeId The id of the anime on Kitsu
* @return string|null Returns the mal id if it exists, otherwise null
*/
public function getMalIdForAnime(string $kitsuAnimeId)
{
$options = [
'query' => [
'include' => 'mappings'
]
];
$data = $this->getRequest("anime/{$kitsuAnimeId}", $options);
$mappings = array_column($data['included'], 'attributes');
foreach($mappings as $map)
{
if ($map['externalSite'] === 'myanimelist/anime')
{
return $map['externalId'];
}
}
return null;
}
/**
* Get information about a particular manga
*
* @param string $mangaId
* @return array
*/
2017-01-04 13:16:58 -05:00
public function getManga(string $mangaId): array
{
2017-01-04 13:16:58 -05:00
$baseData = $this->getRawMediaData('manga', $mangaId);
return $this->mangaTransformer->transform($baseData);
}
2017-02-04 15:18:34 -05:00
/**
* Get and transform the entirety of the user's anime list
*
* @return array
*/
public function getFullAnimeList(): array
{
}
/**
2017-01-27 12:35:28 -05:00
* Get the raw (unorganized) anime list for the configured user
*
* @param string $status - The watching status to filter the list with
* @param int $limit - The number of list entries to fetch for a page
* @param int $offset - The page offset
* @return array
*/
2017-01-27 12:35:28 -05:00
public function getRawAnimeList(string $status, int $limit = 600, int $offset = 0): array
{
$options = [
2016-12-21 12:46:20 -05:00
'query' => [
'filter' => [
'user_id' => $this->getUserIdByUsername($this->getUsername()),
'media_type' => 'Anime',
'status' => $status,
],
'include' => 'media,media.genres,media.mappings,anime.streamingLinks',
'page' => [
'offset' => $offset,
'limit' => $limit
2017-01-27 12:35:28 -05:00
],
'sort' => '-updated_at'
2016-12-21 12:46:20 -05:00
]
];
2017-02-04 15:18:34 -05:00
2017-01-27 12:35:28 -05:00
return $this->getRequest('library-entries', $options);
}
/**
* Get the anime list for the configured user
*
* @param string $status - The watching status to filter the list with
* @param int $limit - The number of list entries to fetch for a page
* @param int $offset - The page offset
* @return array
*/
public function getAnimeList(string $status, int $limit = 600, int $offset = 0): array
{
$cacheItem = $this->cache->getItem($this->getHashForMethodCall($this, __METHOD__, [$status]));
2017-02-04 15:18:34 -05:00
if ( ! $cacheItem->isHit())
2016-12-21 12:46:20 -05:00
{
2017-01-27 12:35:28 -05:00
$data = $this->getRawAnimeList($status, $limit, $offset);
$included = JsonAPI::organizeIncludes($data['included']);
$included = JsonAPI::inlineIncludedRelationships($included, 'anime');
foreach($data['data'] as $i => &$item)
{
$item['included'] = $included;
}
$transformed = $this->animeListTransformer->transformCollection($data['data']);
2017-02-04 15:18:34 -05:00
$cacheItem->set($transformed);
$cacheItem->save();
2016-12-21 12:46:20 -05:00
}
return $cacheItem->get();
2016-12-21 12:46:20 -05:00
}
/**
* Get the manga list for the configured user
*
* @param string $status - The reading status by which to filter the list
* @param int $limit - The number of list items to fetch per page
* @param int $offset - The page offset
* @return array
*/
public function getMangaList(string $status, int $limit = 200, int $offset = 0): array
2017-01-04 13:16:58 -05:00
{
$options = [
'query' => [
'filter' => [
'user_id' => $this->getUserIdByUsername($this->getUsername()),
2017-01-04 13:16:58 -05:00
'media_type' => 'Manga',
'status' => $status,
],
'include' => 'media',
'page' => [
'offset' => $offset,
'limit' => $limit
2017-01-04 13:16:58 -05:00
],
'sort' => '-updated_at'
]
];
2017-02-04 15:18:34 -05:00
2017-01-16 14:14:45 -05:00
$cacheItem = $this->cache->getItem($this->getHashForMethodCall($this, __METHOD__, $options));
2017-01-04 13:16:58 -05:00
2017-01-16 14:14:45 -05:00
if ( ! $cacheItem->isHit())
2017-01-04 13:16:58 -05:00
{
2017-01-16 14:14:45 -05:00
$data = $this->getRequest('library-entries', $options);
2017-01-31 12:52:43 -05:00
$data = JsonAPI::inlineRawIncludes($data, 'manga');
2017-01-04 13:16:58 -05:00
2017-01-31 12:52:43 -05:00
$transformed = $this->mangaListTransformer->transformCollection($data);
2017-01-04 13:16:58 -05:00
2017-01-16 14:14:45 -05:00
$cacheItem->set($transformed);
$cacheItem->save();
}
2017-02-04 15:18:34 -05:00
2017-01-16 14:14:45 -05:00
return $cacheItem->get();
2017-01-04 13:16:58 -05:00
}
2017-01-03 21:06:49 -05:00
/**
* Search for an anime or manga
*
* @param string $type - 'anime' or 'manga'
* @param string $query - name of the item to search for
* @return array
*/
public function search(string $type, string $query): array
{
$options = [
'query' => [
'filter' => [
'text' => $query
2017-01-10 21:13:44 -05:00
],
'page' => [
'offset' => 0,
'limit' => 20
],
]
];
$raw = $this->getRequest($type, $options);
foreach ($raw['data'] as &$item)
{
$item['attributes']['titles'] = K::filterTitles($item['attributes']);
array_shift($item['attributes']['titles']);
}
return $raw;
}
/**
* Create a list item
*
* @param array $data
* @return Request
*/
public function createListItem(array $data): Request
2017-01-10 21:13:44 -05:00
{
$data['user_id'] = $this->getUserIdByUsername($this->getUsername());
return $this->listItem->create($data);
}
/**
* Get the data for a specific list item, generally for editing
*
* @param string $listId - The unique identifier of that list item
* @return array
*/
public function getListItem(string $listId): array
{
$baseData = $this->listItem->get($listId);
$included = JsonAPI::organizeIncludes($baseData['included']);
switch (TRUE)
{
case in_array('anime', array_keys($included)):
$included = JsonAPI::inlineIncludedRelationships($included, 'anime');
$baseData['data']['included'] = $included;
return $this->animeListTransformer->transform($baseData['data']);
case in_array('manga', array_keys($included)):
$included = JsonAPI::inlineIncludedRelationships($included, 'manga');
$baseData['data']['included'] = $included;
$baseData['data']['manga'] = $baseData['included'][0];
return $this->mangaListTransformer->transform($baseData['data']);
default:
return $baseData['data'];
}
}
/**
* Modify a list item
*
* @param array $data
* @return Request
*/
public function updateListItem(array $data): Request
{
return $this->listItem->update($data['id'], $data['data']);
}
/**
* Remove a list item
*
* @param string $id - The id of the list item to remove
* @return Request
*/
public function deleteListItem(string $id): Request
{
return $this->listItem->delete($id);
}
private function getUsername(): string
{
return $this->getContainer()
->get('config')
->get(['kitsu_username']);
}
2017-02-04 15:18:34 -05:00
2017-01-16 13:49:51 -05:00
private function getRawMediaDataById(string $type, string $id): array
{
$options = [
'query' => [
'include' => ($type === 'anime')
? 'genres,mappings,streamingLinks'
: 'genres,mappings',
]
];
$data = $this->getRequest("{$type}/{$id}", $options);
$baseData = $data['data']['attributes'];
$baseData['included'] = $data['included'];
return $baseData;
}
private function getRawMediaData(string $type, string $slug): array
{
$options = [
'query' => [
'filter' => [
'slug' => $slug
],
'include' => ($type === 'anime')
? 'genres,mappings,streamingLinks'
: 'genres,mappings',
]
];
$data = $this->getRequest($type, $options);
$baseData = $data['data'][0]['attributes'];
$baseData['included'] = $data['included'];
return $baseData;
}
2016-12-21 12:46:20 -05:00
}