Version 5.1 - All the GraphQL #32
@ -48,10 +48,13 @@ return function(array $config_array = []) {
|
||||
|
||||
$app_logger = new Logger('animeclient');
|
||||
$app_logger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/app.log', Logger::NOTICE));
|
||||
$request_logger = new Logger('request');
|
||||
$request_logger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/request.log', Logger::NOTICE));
|
||||
$kitsu_request_logger = new Logger('kitsu_request');
|
||||
$kitsu_request_logger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/kitsu_request.log', Logger::NOTICE));
|
||||
$mal_request_logger = new Logger('mal_request');
|
||||
$mal_request_logger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/mal_request.log', Logger::NOTICE));
|
||||
$container->setLogger($app_logger, 'default');
|
||||
$container->setLogger($request_logger, 'request');
|
||||
$container->setLogger($kitsu_request_logger, 'kitsu_request');
|
||||
$container->setLogger($mal_request_logger, 'mal_request');
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Injected Objects
|
||||
|
@ -1,5 +1,4 @@
|
||||
<?php if ($auth->is_authenticated()): ?>
|
||||
<?php /* <pre><?= json_encode($item, \JSON_PRETTY_PRINT); ?></pre> */ ?>
|
||||
<main>
|
||||
<h2>Edit Anime List Item</h2>
|
||||
<form action="<?= $action ?>" method="post">
|
||||
@ -86,15 +85,20 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
<br />
|
||||
<br />
|
||||
<fieldset>
|
||||
<legend>Danger Zone</legend>
|
||||
<form class="js-delete" action="<?= $url->generate('anime.delete') ?>" method="post">
|
||||
<table class="form invisible">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<strong>Permanently</strong> remove this list item and <strong>all</strong> its data?
|
||||
</td>
|
||||
<td>
|
||||
<input type="hidden" value="<?= $item['id'] ?>" name="id" />
|
||||
<input type="hidden" value="<?= $item['mal_id'] ?>" name="mal_id" />
|
||||
<button type="submit" class="danger">Delete Entry</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -27,5 +27,4 @@ class AnimeWatchingStatus extends BaseEnum {
|
||||
const COMPLETED = 'completed';
|
||||
const ON_HOLD = 'on_hold';
|
||||
const DROPPED = 'dropped';
|
||||
}
|
||||
// End of AnimeWatchingStatus.php
|
||||
}
|
@ -93,7 +93,7 @@ trait KitsuTrait {
|
||||
'headers' => $this->defaultHeaders
|
||||
];
|
||||
|
||||
$logger = $this->container->getLogger('request');
|
||||
$logger = $this->container->getLogger('kitsu_request');
|
||||
$sessionSegment = $this->getContainer()
|
||||
->get('session')
|
||||
->getSegment(AnimeClient::SESSION_SEGMENT);
|
||||
@ -106,10 +106,19 @@ trait KitsuTrait {
|
||||
|
||||
$options = array_merge($defaultOptions, $options);
|
||||
|
||||
$logger->debug(Json::encode([$type, $url]));
|
||||
$logger->debug(Json::encode($options));
|
||||
$response = $this->client->request($type, $url, $options);
|
||||
|
||||
return $this->client->request($type, $url, $options);
|
||||
$logger->debug('Kitsu API request', [
|
||||
'requestParams' => [
|
||||
'type' => $type,
|
||||
'url' => $url,
|
||||
],
|
||||
'responseValues' => [
|
||||
'status' => $response->getStatusCode()
|
||||
]
|
||||
]);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -125,7 +134,7 @@ trait KitsuTrait {
|
||||
$logger = null;
|
||||
if ($this->getContainer())
|
||||
{
|
||||
$logger = $this->container->getLogger('request');
|
||||
$logger = $this->container->getLogger('kitsu_request');
|
||||
}
|
||||
|
||||
$response = $this->getResponse($type, $url, $options);
|
||||
@ -134,11 +143,8 @@ trait KitsuTrait {
|
||||
{
|
||||
if ($logger)
|
||||
{
|
||||
$logger->warning('Non 200 response for api call');
|
||||
$logger->warning($response->getBody());
|
||||
$logger->warning('Non 200 response for api call', $response->getBody());
|
||||
}
|
||||
|
||||
// throw new RuntimeException($response->getBody());
|
||||
}
|
||||
|
||||
return JSON::decode($response->getBody(), TRUE);
|
||||
@ -177,7 +183,7 @@ trait KitsuTrait {
|
||||
$logger = null;
|
||||
if ($this->getContainer())
|
||||
{
|
||||
$logger = $this->container->getLogger('request');
|
||||
$logger = $this->container->getLogger('kitsu_request');
|
||||
}
|
||||
|
||||
$response = $this->getResponse('POST', ...$args);
|
||||
@ -187,11 +193,8 @@ trait KitsuTrait {
|
||||
{
|
||||
if ($logger)
|
||||
{
|
||||
$logger->warning('Non 201 response for POST api call');
|
||||
$logger->warning($response->getBody());
|
||||
$logger->warning('Non 201 response for POST api call', $response->getBody());
|
||||
}
|
||||
|
||||
// throw new RuntimeException($response->getBody());
|
||||
}
|
||||
|
||||
return JSON::decode($response->getBody(), TRUE);
|
||||
|
@ -20,9 +20,9 @@ use Aviat\AnimeClient\API\CacheTrait;
|
||||
use Aviat\AnimeClient\API\JsonAPI;
|
||||
use Aviat\AnimeClient\API\Kitsu as K;
|
||||
use Aviat\AnimeClient\API\Kitsu\Transformer\{
|
||||
AnimeTransformer,
|
||||
AnimeListTransformer,
|
||||
MangaTransformer,
|
||||
AnimeTransformer,
|
||||
AnimeListTransformer,
|
||||
MangaTransformer,
|
||||
MangaListTransformer
|
||||
};
|
||||
use Aviat\Ion\Di\ContainerAware;
|
||||
@ -65,7 +65,7 @@ class Model {
|
||||
* @var MangaListTransformer
|
||||
*/
|
||||
protected $mangaListTransformer;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -94,9 +94,9 @@ class Model {
|
||||
{
|
||||
$username = $this->getUsername();
|
||||
}
|
||||
|
||||
|
||||
$cacheItem = $this->cache->getItem(K::AUTH_USER_ID_KEY);
|
||||
|
||||
|
||||
if ( ! $cacheItem->isHit())
|
||||
{
|
||||
$data = $this->getRequest('users', [
|
||||
@ -110,7 +110,7 @@ class Model {
|
||||
$cacheItem->set($data['data'][0]['id']);
|
||||
$cacheItem->save();
|
||||
}
|
||||
|
||||
|
||||
return $cacheItem->get();
|
||||
}
|
||||
|
||||
@ -154,7 +154,7 @@ class Model {
|
||||
$baseData = $this->getRawMediaData('anime', $slug);
|
||||
return $this->animeTransformer->transform($baseData);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get information about a particular anime
|
||||
*
|
||||
@ -167,6 +167,34 @@ class Model {
|
||||
return $this->animeTransformer->transform($baseData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
@ -178,7 +206,17 @@ class Model {
|
||||
$baseData = $this->getRawMediaData('manga', $mangaId);
|
||||
return $this->mangaTransformer->transform($baseData);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get and transform the entirety of the user's anime list
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFullAnimeList(): array
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw (unorganized) anime list for the configured user
|
||||
*
|
||||
@ -204,7 +242,7 @@ class Model {
|
||||
'sort' => '-updated_at'
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
return $this->getRequest('library-entries', $options);
|
||||
}
|
||||
|
||||
@ -219,7 +257,7 @@ class Model {
|
||||
public function getAnimeList(string $status, int $limit = 600, int $offset = 0): array
|
||||
{
|
||||
$cacheItem = $this->cache->getItem($this->getHashForMethodCall($this, __METHOD__, [$status]));
|
||||
|
||||
|
||||
if ( ! $cacheItem->isHit())
|
||||
{
|
||||
$data = $this->getRawAnimeList($status, $limit, $offset);
|
||||
@ -231,7 +269,7 @@ class Model {
|
||||
$item['included'] = $included;
|
||||
}
|
||||
$transformed = $this->animeListTransformer->transformCollection($data['data']);
|
||||
|
||||
|
||||
$cacheItem->set($transformed);
|
||||
$cacheItem->save();
|
||||
}
|
||||
@ -264,7 +302,7 @@ class Model {
|
||||
'sort' => '-updated_at'
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
$cacheItem = $this->cache->getItem($this->getHashForMethodCall($this, __METHOD__, $options));
|
||||
|
||||
if ( ! $cacheItem->isHit())
|
||||
@ -277,7 +315,7 @@ class Model {
|
||||
$cacheItem->set($transformed);
|
||||
$cacheItem->save();
|
||||
}
|
||||
|
||||
|
||||
return $cacheItem->get();
|
||||
}
|
||||
|
||||
@ -397,7 +435,7 @@ class Model {
|
||||
->get('config')
|
||||
->get(['kitsu_username']);
|
||||
}
|
||||
|
||||
|
||||
private function getRawMediaDataById(string $type, string $id): array
|
||||
{
|
||||
$options = [
|
||||
|
@ -113,6 +113,7 @@ class AnimeListTransformer extends AbstractTransformer {
|
||||
|
||||
$untransformed = [
|
||||
'id' => $item['id'],
|
||||
'mal_id' => $item['mal_id'] ?? null,
|
||||
'data' => [
|
||||
'status' => $item['watching_status'],
|
||||
'rating' => $item['user_rating'] / 2,
|
||||
|
@ -16,6 +16,10 @@
|
||||
|
||||
namespace Aviat\AnimeClient\API;
|
||||
|
||||
use Aviat\AnimeClient\API\Kitsu\Enum\{
|
||||
AnimeWatchingStatus as KAWS,
|
||||
MangaReadingStatus as KMRS
|
||||
};
|
||||
use Aviat\AnimeClient\API\MAL\Enum\{AnimeWatchingStatus, MangaReadingStatus};
|
||||
|
||||
/**
|
||||
@ -25,6 +29,14 @@ class MAL {
|
||||
const AUTH_URL = 'https://myanimelist.net/api/account/verify_credentials.xml';
|
||||
const BASE_URL = 'https://myanimelist.net/api/';
|
||||
|
||||
const KITSU_MAL_WATCHING_STATUS_MAP = [
|
||||
KAWS::WATCHING => AnimeWatchingStatus::WATCHING,
|
||||
KAWS::COMPLETED => AnimeWatchingStatus::COMPLETED,
|
||||
KAWS::ON_HOLD => AnimeWatchingStatus::ON_HOLD,
|
||||
KAWS::DROPPED => AnimeWatchingStatus::DROPPED,
|
||||
KAWS::PLAN_TO_WATCH => AnimeWatchingStatus::PLAN_TO_WATCH
|
||||
];
|
||||
|
||||
public static function getIdToWatchingStatusMap()
|
||||
{
|
||||
return [
|
||||
@ -32,7 +44,12 @@ class MAL {
|
||||
2 => AnimeWatchingStatus::COMPLETED,
|
||||
3 => AnimeWatchingStatus::ON_HOLD,
|
||||
4 => AnimeWatchingStatus::DROPPED,
|
||||
5 => AnimeWatchingStatus::PLAN_TO_WATCH
|
||||
6 => AnimeWatchingStatus::PLAN_TO_WATCH,
|
||||
'watching' => AnimeWatchingStatus::WATCHING,
|
||||
'completed' => AnimeWatchingStatus::COMPLETED,
|
||||
'onhold' => AnimeWatchingStatus::ON_HOLD,
|
||||
'dropped' => AnimeWatchingStatus::DROPPED,
|
||||
'plantowatch' => AnimeWatchingStatus::PLAN_TO_WATCH
|
||||
];
|
||||
}
|
||||
|
||||
@ -43,7 +60,12 @@ class MAL {
|
||||
2 => MangaReadingStatus::COMPLETED,
|
||||
3 => MangaReadingStatus::ON_HOLD,
|
||||
4 => MangaReadingStatus::DROPPED,
|
||||
5 => MangaReadingStatus::PLAN_TO_READ
|
||||
6 => MangaReadingStatus::PLAN_TO_READ,
|
||||
'reading' => MangaReadingStatus::READING,
|
||||
'completed' => MangaReadingStatus::COMPLETED,
|
||||
'onhold' => MangaReadingStatus::ON_HOLD,
|
||||
'dropped' => MangaReadingStatus::DROPPED,
|
||||
'plantoread' => MangaReadingStatus::PLAN_TO_WATCH
|
||||
];
|
||||
}
|
||||
}
|
@ -22,9 +22,9 @@ use Aviat\Ion\Enum as BaseEnum;
|
||||
* Possible values for watching status for the current anime
|
||||
*/
|
||||
class AnimeWatchingStatus extends BaseEnum {
|
||||
const WATCHING = 'watching';
|
||||
const COMPLETED = 'completed';
|
||||
const ON_HOLD = 'onhold';
|
||||
const DROPPED = 'dropped';
|
||||
const PLAN_TO_WATCH = 'plantowatch';
|
||||
const WATCHING = 1;
|
||||
const COMPLETED = 2;
|
||||
const ON_HOLD = 3;
|
||||
const DROPPED = 4;
|
||||
const PLAN_TO_WATCH = 6;
|
||||
}
|
@ -33,27 +33,27 @@ class ListItem {
|
||||
public function create(array $data): bool
|
||||
{
|
||||
$id = $data['id'];
|
||||
$body = (new FormBody)
|
||||
->addField('id', $data['id'])
|
||||
->addField('data', XML::toXML(['entry' => $data['data']]));
|
||||
$createData = [
|
||||
'id' => $id,
|
||||
'data' => XML::toXML([
|
||||
'entry' => $data['data']
|
||||
])
|
||||
];
|
||||
|
||||
$response = $this->getResponse('POST', "animelist/add/{$id}.xml", [
|
||||
'headers' => [
|
||||
'Content-type' => 'application/x-www-form-urlencoded',
|
||||
'Accept' => 'text/plain'
|
||||
],
|
||||
'body' => $body
|
||||
'body' => $this->fixBody((new FormBody)->addFields($createData))
|
||||
]);
|
||||
|
||||
return $response->getStatus() === 201;
|
||||
|
||||
return $response->getBody() === 'Created';
|
||||
}
|
||||
|
||||
public function delete(string $id): bool
|
||||
{
|
||||
$response = $this->getResponse('DELETE', "animeclient/delete/{$id}.xml", [
|
||||
'body' => (new FormBody)->addField('id', $id)
|
||||
$response = $this->getResponse('DELETE', "animelist/delete/{$id}.xml", [
|
||||
'body' => $this->fixBody((new FormBody)->addField('id', $id))
|
||||
]);
|
||||
|
||||
return $response->getBody() === 'Deleted';
|
||||
|
||||
return $response->getBody() === 'Deleted';
|
||||
}
|
||||
|
||||
public function get(string $id): array
|
||||
@ -61,18 +61,15 @@ class ListItem {
|
||||
return [];
|
||||
}
|
||||
|
||||
public function update(string $id, array $data): Response
|
||||
public function update(string $id, array $data)
|
||||
{
|
||||
$xml = XML::toXML(['entry' => $data]);
|
||||
$body = (new FormBody)
|
||||
->addField('id', $id)
|
||||
->addField('data', XML::toXML(['entry' => $data]))
|
||||
|
||||
return $this->postRequest("animelist/update/{$id}.xml", [
|
||||
'headers' => [
|
||||
'Content-type' => 'application/x-www-form-urlencoded',
|
||||
'Accept' => 'text/plain'
|
||||
],
|
||||
'body' => $body
|
||||
->addField('data', $xml);
|
||||
|
||||
return $this->getResponse('POST', "animelist/update/{$id}.xml", [
|
||||
'body' => $this->fixBody($body)
|
||||
]);
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
|
||||
namespace Aviat\AnimeClient\API\MAL;
|
||||
|
||||
use Amp\Artax\{Client, Request};
|
||||
use Amp\Artax\{Client, FormBody, Request};
|
||||
use Aviat\AnimeClient\API\{
|
||||
MAL as M,
|
||||
XML
|
||||
@ -38,8 +38,26 @@ trait MALTrait {
|
||||
* @var array
|
||||
*/
|
||||
protected $defaultHeaders = [
|
||||
'Accept' => 'text/xml',
|
||||
'Accept-Encoding' => 'gzip',
|
||||
'Content-type' => 'application/x-www-form-urlencoded',
|
||||
'User-Agent' => "Tim's Anime Client/4.0"
|
||||
];
|
||||
|
||||
/**
|
||||
* Unencode the dual-encoded ampersands in the body
|
||||
*
|
||||
* This is a dirty hack until I can fully track down where
|
||||
* the dual-encoding happens
|
||||
*
|
||||
* @param FormBody $formBody The form builder object to fix
|
||||
* @return string
|
||||
*/
|
||||
private function fixBody(FormBody $formBody): string
|
||||
{
|
||||
$rawBody = \Amp\wait($formBody->getBody());
|
||||
return html_entity_decode($rawBody, \ENT_HTML5, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a request via Guzzle
|
||||
@ -51,39 +69,60 @@ trait MALTrait {
|
||||
*/
|
||||
private function getResponse(string $type, string $url, array $options = [])
|
||||
{
|
||||
$this->defaultHeaders['User-Agent'] = $_SERVER['HTTP_USER_AGENT'] ?? $this->defaultHeaders;
|
||||
|
||||
$type = strtoupper($type);
|
||||
$validTypes = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];
|
||||
$validTypes = ['GET', 'POST', 'DELETE'];
|
||||
|
||||
if ( ! in_array($type, $validTypes))
|
||||
{
|
||||
throw new InvalidArgumentException('Invalid http request type');
|
||||
}
|
||||
|
||||
|
||||
$config = $this->container->get('config');
|
||||
$logger = $this->container->getLogger('request');
|
||||
|
||||
$logger = $this->container->getLogger('mal_request');
|
||||
|
||||
$headers = array_merge($this->defaultHeaders, $options['headers'] ?? [], [
|
||||
'Authorization' => 'Basic ' .
|
||||
'Authorization' => 'Basic ' .
|
||||
base64_encode($config->get(['mal','username']) . ':' .$config->get(['mal','password']))
|
||||
]);
|
||||
|
||||
|
||||
$query = $options['query'] ?? [];
|
||||
|
||||
|
||||
$url = (strpos($url, '//') !== FALSE)
|
||||
? $url . '?' . http_build_query($query)
|
||||
: $this->baseUrl . $url . '?' . http_build_query($query);
|
||||
|
||||
? $url
|
||||
: $this->baseUrl . $url;
|
||||
|
||||
if ( ! empty($query))
|
||||
{
|
||||
$url .= '?' . http_build_query($query);
|
||||
}
|
||||
|
||||
$request = (new Request)
|
||||
->setMethod($type)
|
||||
->setUri($url)
|
||||
->setProtocol('1.1')
|
||||
->setAllHeaders($headers)
|
||||
->setBody($options['body']);
|
||||
->setAllHeaders($headers);
|
||||
|
||||
$logger->debug(Json::encode([$type, $url]));
|
||||
$logger->debug(Json::encode($options));
|
||||
if (array_key_exists('body', $options))
|
||||
{
|
||||
$request->setBody($options['body']);
|
||||
}
|
||||
|
||||
return \Amp\wait((new Client)->request($request));
|
||||
$response = \Amp\wait((new Client)->request($request));
|
||||
|
||||
$logger->debug('MAL api request', [
|
||||
'url' => $url,
|
||||
'status' => $response->getStatus(),
|
||||
'reason' => $response->getReason(),
|
||||
'headers' => $response->getAllHeaders(),
|
||||
'requestHeaders' => $request->getAllHeaders(),
|
||||
'requestBody' => $request->hasBody() ? $request->getBody() : 'No request body',
|
||||
'requestBodyBeforeEncode' => $request->hasBody() ? urldecode($request->getBody()) : '',
|
||||
'body' => $response->getBody()
|
||||
]);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -99,7 +138,7 @@ trait MALTrait {
|
||||
$logger = null;
|
||||
if ($this->getContainer())
|
||||
{
|
||||
$logger = $this->container->getLogger('request');
|
||||
$logger = $this->container->getLogger('mal_request');
|
||||
}
|
||||
|
||||
$response = $this->getResponse($type, $url, $options);
|
||||
@ -108,8 +147,7 @@ trait MALTrait {
|
||||
{
|
||||
if ($logger)
|
||||
{
|
||||
$logger->warning('Non 200 response for api call');
|
||||
$logger->warning($response->getBody());
|
||||
$logger->warning('Non 200 response for api call', $response->getBody());
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,7 +176,7 @@ trait MALTrait {
|
||||
$logger = null;
|
||||
if ($this->getContainer())
|
||||
{
|
||||
$logger = $this->container->getLogger('request');
|
||||
$logger = $this->container->getLogger('mal_request');
|
||||
}
|
||||
|
||||
$response = $this->getResponse('POST', ...$args);
|
||||
@ -148,8 +186,7 @@ trait MALTrait {
|
||||
{
|
||||
if ($logger)
|
||||
{
|
||||
$logger->warning('Non 201 response for POST api call');
|
||||
$logger->warning($response->getBody());
|
||||
$logger->warning('Non 201 response for POST api call', $response->getBody());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,13 +17,10 @@
|
||||
namespace Aviat\AnimeClient\API\MAL;
|
||||
|
||||
use Aviat\AnimeClient\API\MAL as M;
|
||||
use Aviat\AnimeClient\API\MAL\{
|
||||
AnimeListTransformer,
|
||||
ListItem
|
||||
};
|
||||
use Aviat\AnimeClient\API\MAL\ListItem;
|
||||
use Aviat\AnimeClient\API\MAL\Transformer\AnimeListTransformer;
|
||||
use Aviat\AnimeClient\API\XML;
|
||||
use Aviat\Ion\Di\ContainerAware;
|
||||
use Aviat\Ion\Json;
|
||||
|
||||
/**
|
||||
* MyAnimeList API Model
|
||||
@ -42,13 +39,37 @@ class Model {
|
||||
*/
|
||||
public function __construct(ListItem $listItem)
|
||||
{
|
||||
//$this->animeListTransformer = new AnimeListTransformer();
|
||||
$this->animeListTransformer = new AnimeListTransformer();
|
||||
$this->listItem = $listItem;
|
||||
}
|
||||
|
||||
public function createListItem(array $data): bool
|
||||
{
|
||||
return $this->listItem->create($data);
|
||||
$createData = [
|
||||
'id' => $data['id'],
|
||||
'data' => [
|
||||
'status' => M::KITSU_MAL_WATCHING_STATUS_MAP[$data['status']]
|
||||
]
|
||||
];
|
||||
|
||||
return $this->listItem->create($createData);
|
||||
}
|
||||
|
||||
public function getFullList(): array
|
||||
{
|
||||
$config = $this->container->get('config');
|
||||
$userName = $config->get(['mal', 'username']);
|
||||
$list = $this->getRequest('https://myanimelist.net/malappinfo.php', [
|
||||
'headers' => [
|
||||
'Accept' => 'text/xml'
|
||||
],
|
||||
'query' => [
|
||||
'u' => $userName,
|
||||
'status' => 'all'
|
||||
]
|
||||
]);
|
||||
|
||||
return $list;//['anime'];
|
||||
}
|
||||
|
||||
public function getListItem(string $listId): array
|
||||
@ -58,8 +79,8 @@ class Model {
|
||||
|
||||
public function updateListItem(array $data)
|
||||
{
|
||||
//$updateData = $this->animeListTransformer->transform($data['data']);
|
||||
return $this->listItem->update($data['mal_id'], $updateData);
|
||||
$updateData = $this->animeListTransformer->untransform($data);
|
||||
return $this->listItem->update($updateData['id'], $updateData['data']);
|
||||
}
|
||||
|
||||
public function deleteListItem(string $id): bool
|
||||
|
@ -14,27 +14,32 @@
|
||||
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\API\MAL;
|
||||
namespace Aviat\AnimeClient\API\MAL\Transformer;
|
||||
|
||||
use Aviat\AnimeClient\API\Kitsu\Enum\AnimeWatchingStatus;
|
||||
use Aviat\Ion\Transformer\AbstractTransformer;
|
||||
|
||||
/**
|
||||
* Transformer for updating MAL List
|
||||
*/
|
||||
class AnimeListTransformer extends AbstractTransformer {
|
||||
|
||||
const statusMap = [
|
||||
AnimeWatchingStatus::WATCHING => '1',
|
||||
AnimeWatchingStatus::COMPLETED => '2',
|
||||
AnimeWatchingStatus::ON_HOLD => '3',
|
||||
AnimeWatchingStatus::DROPPED => '4',
|
||||
AnimeWatchingStatus::PLAN_TO_WATCH => '6'
|
||||
];
|
||||
|
||||
public function transform($item)
|
||||
{
|
||||
$rewatching = 'false';
|
||||
if (array_key_exists('rewatching', $item) && $item['rewatching'])
|
||||
{
|
||||
$rewatching = 'true';
|
||||
}
|
||||
$rewatching = (array_key_exists('rewatching', $item) && $item['rewatching']);
|
||||
|
||||
return [
|
||||
'id' => $item['id'],
|
||||
'id' => $item['mal_id'],
|
||||
'data' => [
|
||||
'status' => $item['watching_status'],
|
||||
'status' => self::statusMap[$item['watching_status']],
|
||||
'rating' => $item['user_rating'],
|
||||
'rewatch_value' => (int) $rewatching,
|
||||
'times_rewatched' => $item['rewatched'],
|
||||
@ -43,4 +48,31 @@ class AnimeListTransformer extends AbstractTransformer {
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform Kitsu episode data to MAL episode data
|
||||
*
|
||||
* @param array $item
|
||||
* @return array
|
||||
*/
|
||||
public function untransform(array $item): array
|
||||
{
|
||||
$rewatching = (array_key_exists('reconsuming', $item['data']) && $item['data']['reconsuming']);
|
||||
|
||||
$map = [
|
||||
'id' => $item['mal_id'],
|
||||
'data' => [
|
||||
'episode' => $item['data']['progress'],
|
||||
'status' => self::statusMap[$item['data']['status']],
|
||||
'score' => (array_key_exists('rating', $item['data']))
|
||||
? $item['data']['rating'] * 2
|
||||
: "",
|
||||
// 'enable_rewatching' => $rewatching,
|
||||
// 'times_rewatched' => $item['data']['reconsumeCount'],
|
||||
// 'comments' => $item['data']['notes'],
|
||||
]
|
||||
];
|
||||
|
||||
return $map;
|
||||
}
|
||||
}
|
@ -107,12 +107,7 @@ class XML {
|
||||
{
|
||||
$data = [];
|
||||
|
||||
// Get rid of unimportant text nodes by removing
|
||||
// whitespace characters from between xml tags,
|
||||
// except for the xml declaration tag, Which looks
|
||||
// something like:
|
||||
/* <?xml version="1.0" encoding="UTF-8"?> */
|
||||
$xml = preg_replace('/([^\?])>\s+</', '$1><', $xml);
|
||||
$xml = static::stripXMLWhitespace($xml);
|
||||
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadXML($xml);
|
||||
@ -166,6 +161,16 @@ class XML {
|
||||
return static::toXML($this->getData());
|
||||
}
|
||||
|
||||
private static function stripXMLWhitespace(string $xml): string
|
||||
{
|
||||
// Get rid of unimportant text nodes by removing
|
||||
// whitespace characters from between xml tags,
|
||||
// except for the xml declaration tag, Which looks
|
||||
// something like:
|
||||
/* <?xml version="1.0" encoding="UTF-8"?> */
|
||||
return preg_replace('/([^\?])>\s+</', '$1><', $xml);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively create array structure based on xml structure
|
||||
*
|
||||
@ -180,7 +185,7 @@ class XML {
|
||||
{
|
||||
$el = $nodeList->item($i);
|
||||
$current =& $root[$el->nodeName];
|
||||
|
||||
|
||||
// It's a top level element!
|
||||
if (is_a($el->childNodes->item(0), 'DomText') || ( ! $el->hasChildNodes()))
|
||||
{
|
||||
@ -239,9 +244,9 @@ class XML {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$node = $dom->createElement($key);
|
||||
|
||||
|
||||
if (is_array($props))
|
||||
{
|
||||
static::arrayPropertiesToXmlNodes($dom, $node, $props);
|
||||
|
@ -259,7 +259,7 @@ class Anime extends BaseController {
|
||||
$data = $this->request->getParsedBody();
|
||||
}
|
||||
|
||||
$response = $this->model->updateLibraryItem($data);
|
||||
$response = $this->model->updateLibraryItem($data, $data);
|
||||
|
||||
$this->cache->clear();
|
||||
$this->outputJSON($response['body'], $response['statusCode']);
|
||||
@ -273,7 +273,7 @@ class Anime extends BaseController {
|
||||
public function delete()
|
||||
{
|
||||
$body = $this->request->getParsedBody();
|
||||
$response = $this->model->deleteLibraryItem($body['id']);
|
||||
$response = $this->model->deleteLibraryItem($body['id'], $body['mal_id']);
|
||||
|
||||
if ((bool)$response === TRUE)
|
||||
{
|
||||
|
@ -43,6 +43,12 @@ class Anime extends API {
|
||||
AnimeWatchingStatus::COMPLETED => self::COMPLETED,
|
||||
];
|
||||
|
||||
protected $kitsuModel;
|
||||
|
||||
protected $malModel;
|
||||
|
||||
protected $useMALAPI;
|
||||
|
||||
/**
|
||||
* Anime constructor.
|
||||
* @param ContainerInterface $container
|
||||
@ -50,7 +56,11 @@ class Anime extends API {
|
||||
public function __construct(ContainerInterface $container) {
|
||||
parent::__construct($container);
|
||||
|
||||
$config = $container->get('config');
|
||||
$this->kitsuModel = $container->get('kitsu-model');
|
||||
$this->malModel = $container->get('mal-model');
|
||||
|
||||
$this->useMALAPI = $config->get(['use_mal_api']) === TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -80,7 +90,7 @@ class Anime extends API {
|
||||
{
|
||||
return $this->kitsuModel->getAnime($slug);
|
||||
}
|
||||
|
||||
|
||||
public function getAnimeById($anime_id)
|
||||
{
|
||||
return $this->kitsuModel->getAnimeById($anime_id);
|
||||
@ -110,8 +120,26 @@ class Anime extends API {
|
||||
return $this->kitsuModel->getListItem($itemId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an anime to your list
|
||||
*
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
public function createLibraryItem(array $data): bool
|
||||
{
|
||||
if ($this->useMALAPI)
|
||||
{
|
||||
$malData = $data;
|
||||
$malId = $this->kitsuModel->getMalIdForAnime($malData['id']);
|
||||
|
||||
if ( ! is_null($malId))
|
||||
{
|
||||
$malData['id'] = $malId;
|
||||
$this->malModel->createListItem($malData);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->kitsuModel->createListItem($data);
|
||||
}
|
||||
|
||||
@ -123,11 +151,28 @@ class Anime extends API {
|
||||
*/
|
||||
public function updateLibraryItem(array $data): array
|
||||
{
|
||||
if ($this->useMALAPI)
|
||||
{
|
||||
$this->malModel->updateListItem($data);
|
||||
}
|
||||
|
||||
return $this->kitsuModel->updateListItem($data);
|
||||
}
|
||||
|
||||
public function deleteLibraryItem($id): bool
|
||||
/**
|
||||
* Delete a list entry
|
||||
*
|
||||
* @param string $id
|
||||
* @param string|null $malId
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteLibraryItem(string $id, string $malId = null): bool
|
||||
{
|
||||
if ($this->useMALAPI && ! is_null($malId))
|
||||
{
|
||||
$this->malModel->deleteListItem($malId);
|
||||
}
|
||||
|
||||
return $this->kitsuModel->deleteListItem($id);
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ class AnimeListTransformerTest extends AnimeClient_TestCase {
|
||||
],
|
||||
'expected' => [
|
||||
'id' => 14047981,
|
||||
'mal_id' => null,
|
||||
'data' => [
|
||||
'status' => 'current',
|
||||
'rating' => 4,
|
||||
@ -57,6 +58,7 @@ class AnimeListTransformerTest extends AnimeClient_TestCase {
|
||||
], [
|
||||
'input' => [
|
||||
'id' => 14047981,
|
||||
'mal_id' => '12345',
|
||||
'watching_status' => 'current',
|
||||
'user_rating' => 8,
|
||||
'episodes_watched' => 38,
|
||||
@ -68,6 +70,7 @@ class AnimeListTransformerTest extends AnimeClient_TestCase {
|
||||
],
|
||||
'expected' => [
|
||||
'id' => 14047981,
|
||||
'mal_id' => '12345',
|
||||
'data' => [
|
||||
'status' => 'current',
|
||||
'rating' => 4,
|
||||
|
Loading…
Reference in New Issue
Block a user