Version 5.1 - All the GraphQL #32
@ -8,13 +8,12 @@
|
|||||||
*
|
*
|
||||||
* @package HummingbirdAnimeClient
|
* @package HummingbirdAnimeClient
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2017 Timothy J. Warren
|
* @copyright 2015 - 2018 Timothy J. Warren
|
||||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
* @version 4.0
|
* @version 4.0
|
||||||
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
use const Aviat\AnimeClient\{
|
use const Aviat\AnimeClient\{
|
||||||
DEFAULT_CONTROLLER_METHOD,
|
DEFAULT_CONTROLLER_METHOD,
|
||||||
DEFAULT_CONTROLLER
|
DEFAULT_CONTROLLER
|
||||||
@ -184,6 +183,16 @@ return [
|
|||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
// Default / Shared routes
|
// Default / Shared routes
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
|
'anilist-redirect' => [
|
||||||
|
'path' => '/anilist-redirect',
|
||||||
|
'action' => 'anilistRedirect',
|
||||||
|
'controller' => DEFAULT_CONTROLLER,
|
||||||
|
],
|
||||||
|
'anilist-oauth' => [
|
||||||
|
'path' => '/anilist-oauth',
|
||||||
|
'action' => 'anilistCallback',
|
||||||
|
'controller' => DEFAULT_CONTROLLER,
|
||||||
|
],
|
||||||
'image_proxy' => [
|
'image_proxy' => [
|
||||||
'path' => '/public/images/{type}/{file}',
|
'path' => '/public/images/{type}/{file}',
|
||||||
'action' => 'images',
|
'action' => 'images',
|
||||||
|
@ -20,6 +20,7 @@ use Aura\Html\HelperLocatorFactory;
|
|||||||
use Aura\Router\RouterContainer;
|
use Aura\Router\RouterContainer;
|
||||||
use Aura\Session\SessionFactory;
|
use Aura\Session\SessionFactory;
|
||||||
use Aviat\AnimeClient\API\{
|
use Aviat\AnimeClient\API\{
|
||||||
|
Anilist,
|
||||||
Kitsu,
|
Kitsu,
|
||||||
MAL,
|
MAL,
|
||||||
Kitsu\KitsuRequestBuilder,
|
Kitsu\KitsuRequestBuilder,
|
||||||
@ -45,11 +46,14 @@ return function (array $configArray = []) {
|
|||||||
|
|
||||||
$appLogger = new Logger('animeclient');
|
$appLogger = new Logger('animeclient');
|
||||||
$appLogger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/app.log', Logger::NOTICE));
|
$appLogger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/app.log', Logger::NOTICE));
|
||||||
|
$anilistRequestLogger = new Logger('anilist-request');
|
||||||
|
$anilistRequestLogger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/anilist_request.log', Logger::NOTICE));
|
||||||
$kitsuRequestLogger = new Logger('kitsu-request');
|
$kitsuRequestLogger = new Logger('kitsu-request');
|
||||||
$kitsuRequestLogger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/kitsu_request.log', Logger::NOTICE));
|
$kitsuRequestLogger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/kitsu_request.log', Logger::NOTICE));
|
||||||
$malRequestLogger = new Logger('mal-request');
|
$malRequestLogger = new Logger('mal-request');
|
||||||
$malRequestLogger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/mal_request.log', Logger::NOTICE));
|
$malRequestLogger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/mal_request.log', Logger::NOTICE));
|
||||||
$container->setLogger($appLogger);
|
$container->setLogger($appLogger);
|
||||||
|
$container->setLogger($anilistRequestLogger, 'anilist-request');
|
||||||
$container->setLogger($kitsuRequestLogger, 'kitsu-request');
|
$container->setLogger($kitsuRequestLogger, 'kitsu-request');
|
||||||
$container->setLogger($malRequestLogger, 'mal-request');
|
$container->setLogger($malRequestLogger, 'mal-request');
|
||||||
|
|
||||||
|
73
src/API/Anilist.php
Normal file
73
src/API/Anilist.php
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Hummingbird Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 7
|
||||||
|
*
|
||||||
|
* @package HummingbirdAnimeClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2018 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 4.0
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\API;
|
||||||
|
|
||||||
|
use Aviat\AnimeClient\API\Enum\{
|
||||||
|
AnimeWatchingStatus\Kitsu as KAWS,
|
||||||
|
MangaReadingStatus\Kitsu as KMRS
|
||||||
|
};
|
||||||
|
use Aviat\AnimeClient\API\Enum\{
|
||||||
|
AnimeWatchingStatus\Anilist as AnimeWatchingStatus,
|
||||||
|
MangaReadingStatus\Anilist as MangaReadingStatus
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constants and mappings for the Anilist API
|
||||||
|
*/
|
||||||
|
final class Anilist {
|
||||||
|
const AUTH_URL = 'https://anilist.co/api/v2/oauth/authorize';
|
||||||
|
const BASE_URL = 'https://graphql.anilist.co';
|
||||||
|
|
||||||
|
const KITSU_ANILIST_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,
|
||||||
|
];
|
||||||
|
|
||||||
|
const ANILIST_KITSU_WATCHING_STATUS_MAP = [
|
||||||
|
'CURRENT' => KAWS::WATCHING,
|
||||||
|
'COMPLETED' => KAWS::COMPLETED,
|
||||||
|
'PAUSED' => KAWS::ON_HOLD,
|
||||||
|
'DROPPED' => KAWS::DROPPED,
|
||||||
|
'PLANNING' => KAWS::PLAN_TO_WATCH,
|
||||||
|
];
|
||||||
|
|
||||||
|
public static function getIdToWatchingStatusMap()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'CURRENT' => AnimeWatchingStatus::WATCHING,
|
||||||
|
'COMPLETED' => AnimeWatchingStatus::COMPLETED,
|
||||||
|
'PAUSED' => AnimeWatchingStatus::ON_HOLD,
|
||||||
|
'DROPPED' => AnimeWatchingStatus::DROPPED,
|
||||||
|
'PLANNING' => AnimeWatchingStatus::PLAN_TO_WATCH,
|
||||||
|
'REPEATING' => AnimeWatchingStatus::WATCHING,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getIdToReadingStatusMap()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'CURRENT' => MangaReadingStatus::READING,
|
||||||
|
'COMPLETED' => MangaReadingStatus::COMPLETED,
|
||||||
|
'PAUSED' => MangaReadingStatus::ON_HOLD,
|
||||||
|
'DROPPED' => MangaReadingStatus::DROPPED,
|
||||||
|
'PLANNING' => MangaReadingStatus::PLAN_TO_READ
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
49
src/API/Anilist/AnilistRequestBuilder.php
Normal file
49
src/API/Anilist/AnilistRequestBuilder.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Hummingbird Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 7
|
||||||
|
*
|
||||||
|
* @package HummingbirdAnimeClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2018 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 4.0
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\API\Anilist;
|
||||||
|
|
||||||
|
use const Aviat\AnimeClient\USER_AGENT;
|
||||||
|
|
||||||
|
use Aviat\AnimeClient\API\APIRequestBuilder;
|
||||||
|
|
||||||
|
final class AnilistRequestBuilder extends APIRequestBuilder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base url for api requests
|
||||||
|
* @var string $base_url
|
||||||
|
*/
|
||||||
|
protected $baseUrl = 'https://kitsu.io/api/edge/';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valid HTTP request methods
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $validMethods = ['POST'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP headers to send with every request
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $defaultHeaders = [
|
||||||
|
'User-Agent' => USER_AGENT,
|
||||||
|
'Accept' => 'application/vnd.api+json',
|
||||||
|
'Content-Type' => 'application/vnd.api+json',
|
||||||
|
'CLIENT_ID' => 'dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd',
|
||||||
|
'CLIENT_SECRET' => '54d7307928f63414defd96399fc31ba847961ceaecef3a5fd93144e960c0e151',
|
||||||
|
];
|
||||||
|
}
|
179
src/API/Anilist/AnilistTrait.php
Normal file
179
src/API/Anilist/AnilistTrait.php
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Hummingbird Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 7
|
||||||
|
*
|
||||||
|
* @package HummingbirdAnimeClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2018 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 4.0
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\API\MAL;
|
||||||
|
|
||||||
|
use function Amp\Promise\wait;
|
||||||
|
|
||||||
|
use Aviat\AnimeClient\API\{
|
||||||
|
Anilist,
|
||||||
|
HummingbirdClient
|
||||||
|
};
|
||||||
|
|
||||||
|
trait AnilistTrait {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The request builder for the MAL API
|
||||||
|
* @var AnilistRequestBuilder
|
||||||
|
*/
|
||||||
|
protected $requestBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base url for api requests
|
||||||
|
* @var string $base_url
|
||||||
|
*/
|
||||||
|
protected $baseUrl = Anilist::BASE_URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP headers to send with every request
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $defaultHeaders = [
|
||||||
|
'Accept' => 'application/json',
|
||||||
|
'Accept-Encoding' => 'gzip',
|
||||||
|
'Content-type' => 'application/json',
|
||||||
|
'User-Agent' => "Tim's Anime Client/4.0"
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the request builder object
|
||||||
|
*
|
||||||
|
* @param MALRequestBuilder $requestBuilder
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setRequestBuilder($requestBuilder): self
|
||||||
|
{
|
||||||
|
$this->requestBuilder = $requestBuilder;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a request object
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @param string $url
|
||||||
|
* @param array $options
|
||||||
|
* @return \Amp\Artax\Response
|
||||||
|
*/
|
||||||
|
public function setUpRequest(string $type, string $url, array $options = [])
|
||||||
|
{
|
||||||
|
$config = $this->container->get('config');
|
||||||
|
|
||||||
|
$request = $this->requestBuilder
|
||||||
|
->newRequest($type, $url)
|
||||||
|
->setBasicAuth($config->get(['mal','username']), $config->get(['mal','password']));
|
||||||
|
|
||||||
|
if (array_key_exists('query', $options))
|
||||||
|
{
|
||||||
|
$request = $request->setQuery($options['query']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('body', $options))
|
||||||
|
{
|
||||||
|
$request = $request->setBody($options['body']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $request->getFullRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a request
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @param string $url
|
||||||
|
* @param array $options
|
||||||
|
* @return \Amp\Artax\Response
|
||||||
|
*/
|
||||||
|
private function getResponse(string $type, string $url, array $options = [])
|
||||||
|
{
|
||||||
|
$logger = NULL;
|
||||||
|
if ($this->getContainer())
|
||||||
|
{
|
||||||
|
$logger = $this->container->getLogger('mal-request');
|
||||||
|
}
|
||||||
|
|
||||||
|
$request = $this->setUpRequest($type, $url, $options);
|
||||||
|
$response = wait((new HummingbirdClient)->request($request));
|
||||||
|
|
||||||
|
$logger->debug('MAL api response', [
|
||||||
|
'status' => $response->getStatus(),
|
||||||
|
'reason' => $response->getReason(),
|
||||||
|
'body' => $response->getBody(),
|
||||||
|
'headers' => $response->getHeaders(),
|
||||||
|
'requestHeaders' => $request->getHeaders(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a request
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @param string $url
|
||||||
|
* @param array $options
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function request(string $type, string $url, array $options = []): array
|
||||||
|
{
|
||||||
|
$logger = NULL;
|
||||||
|
if ($this->getContainer())
|
||||||
|
{
|
||||||
|
$logger = $this->container->getLogger('anilist-request');
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->getResponse($type, $url, $options);
|
||||||
|
|
||||||
|
if ((int) $response->getStatus() > 299 OR (int) $response->getStatus() < 200)
|
||||||
|
{
|
||||||
|
if ($logger)
|
||||||
|
{
|
||||||
|
$logger->warning('Non 200 response for api call', (array)$response->getBody());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return XML::toArray(wait($response->getBody()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove some boilerplate for post requests
|
||||||
|
*
|
||||||
|
* @param mixed ...$args
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function postRequest(...$args): array
|
||||||
|
{
|
||||||
|
$logger = NULL;
|
||||||
|
if ($this->getContainer())
|
||||||
|
{
|
||||||
|
$logger = $this->container->getLogger('anilist-request');
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->getResponse('POST', ...$args);
|
||||||
|
$validResponseCodes = [200, 201];
|
||||||
|
|
||||||
|
if ( ! \in_array((int) $response->getStatus(), $validResponseCodes, TRUE))
|
||||||
|
{
|
||||||
|
if ($logger)
|
||||||
|
{
|
||||||
|
$logger->warning('Non 201 response for POST api call', (array)$response->getBody());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return XML::toArray($response->getBody());
|
||||||
|
}
|
||||||
|
}
|
109
src/API/Anilist/ListItem.php
Normal file
109
src/API/Anilist/ListItem.php
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Hummingbird Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 7
|
||||||
|
*
|
||||||
|
* @package HummingbirdAnimeClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2018 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 4.0
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\API\Anilist;
|
||||||
|
|
||||||
|
use Amp\Artax\{FormBody, Request};
|
||||||
|
use Aviat\AnimeClient\API\{
|
||||||
|
XML
|
||||||
|
};
|
||||||
|
use Aviat\AnimeClient\Types\AbstractType;
|
||||||
|
use Aviat\Ion\Di\ContainerAware;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CRUD operations for MAL list items
|
||||||
|
*/
|
||||||
|
final class ListItem {
|
||||||
|
use ContainerAware;
|
||||||
|
use AnilistTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a list item
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @param string $type
|
||||||
|
* @return Request
|
||||||
|
*/
|
||||||
|
public function create(array $data, string $type = 'anime'): Request
|
||||||
|
{
|
||||||
|
$id = $data['id'];
|
||||||
|
$createData = [
|
||||||
|
'id' => $id,
|
||||||
|
'data' => XML::toXML([
|
||||||
|
'entry' => $data['data']
|
||||||
|
])
|
||||||
|
];
|
||||||
|
|
||||||
|
$config = $this->container->get('config');
|
||||||
|
|
||||||
|
return $this->requestBuilder->newRequest('POST', "{$type}list/add/{$id}.xml")
|
||||||
|
->setFormFields($createData)
|
||||||
|
->setBasicAuth($config->get(['mal','username']), $config->get(['mal', 'password']))
|
||||||
|
->getFullRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a list item
|
||||||
|
*
|
||||||
|
* @param string $id
|
||||||
|
* @param string $type
|
||||||
|
* @return Request
|
||||||
|
*/
|
||||||
|
public function delete(string $id, string $type = 'anime'): Request
|
||||||
|
{
|
||||||
|
$config = $this->container->get('config');
|
||||||
|
|
||||||
|
return $this->requestBuilder->newRequest('DELETE', "{$type}list/delete/{$id}.xml")
|
||||||
|
->setFormFields([
|
||||||
|
'id' => $id
|
||||||
|
])
|
||||||
|
->setBasicAuth($config->get(['mal','username']), $config->get(['mal', 'password']))
|
||||||
|
->getFullRequest();
|
||||||
|
|
||||||
|
// return $response->getBody() === 'Deleted'
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get(string $id): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a list item
|
||||||
|
*
|
||||||
|
* @param string $id
|
||||||
|
* @param AbstractType $data
|
||||||
|
* @param string $type
|
||||||
|
* @return Request
|
||||||
|
*/
|
||||||
|
public function update(string $id, AbstractType $data, string $type = 'anime'): Request
|
||||||
|
{
|
||||||
|
$config = $this->container->get('config');
|
||||||
|
|
||||||
|
$xml = XML::toXML(['entry' => $data]);
|
||||||
|
$body = new FormBody();
|
||||||
|
$body->addField('id', $id);
|
||||||
|
$body->addField('data', $xml);
|
||||||
|
|
||||||
|
return $this->requestBuilder->newRequest('POST', "{$type}list/update/{$id}.xml")
|
||||||
|
->setFormFields([
|
||||||
|
'id' => $id,
|
||||||
|
'data' => $xml
|
||||||
|
])
|
||||||
|
->setBasicAuth($config->get(['mal','username']), $config->get(['mal', 'password']))
|
||||||
|
->getFullRequest();
|
||||||
|
}
|
||||||
|
}
|
23
src/API/Anilist/Model.php
Normal file
23
src/API/Anilist/Model.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Hummingbird Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 7
|
||||||
|
*
|
||||||
|
* @package HummingbirdAnimeClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2018 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 4.0
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\API\Anilist;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Anilist API Model
|
||||||
|
*/
|
||||||
|
final class Model {
|
||||||
|
}
|
31
src/API/Enum/AnimeWatchingStatus/Anilist.php
Normal file
31
src/API/Enum/AnimeWatchingStatus/Anilist.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Hummingbird Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 7
|
||||||
|
*
|
||||||
|
* @package HummingbirdAnimeClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2018 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 4.0
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\API\Enum\AnimeWatchingStatus;
|
||||||
|
|
||||||
|
use Aviat\Ion\Enum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible values for watching status for the current anime
|
||||||
|
*/
|
||||||
|
final class Anilist extends Enum {
|
||||||
|
const WATCHING = 'CURRENT';
|
||||||
|
const COMPLETED = 'COMPLETED';
|
||||||
|
const ON_HOLD = 'PAUSED';
|
||||||
|
const DROPPED = 'DROPPED';
|
||||||
|
const PLAN_TO_WATCH = 'PLANNING';
|
||||||
|
const REPEATING = 'REPEATING';
|
||||||
|
}
|
31
src/API/Enum/MangaReadingStatus/Anilist.php
Normal file
31
src/API/Enum/MangaReadingStatus/Anilist.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Hummingbird Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 7
|
||||||
|
*
|
||||||
|
* @package HummingbirdAnimeClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2018 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 4.0
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\API\Enum\MangaReadingStatus;
|
||||||
|
|
||||||
|
use Aviat\Ion\Enum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible values for watching status for the current anime
|
||||||
|
*/
|
||||||
|
final class Anilist extends Enum {
|
||||||
|
const WATCHING = 'CURRENT';
|
||||||
|
const COMPLETED = 'COMPLETED';
|
||||||
|
const ON_HOLD = 'PAUSED';
|
||||||
|
const DROPPED = 'DROPPED';
|
||||||
|
const PLAN_TO_WATCH = 'PLANNING';
|
||||||
|
const REPEATING = 'REPEATING';
|
||||||
|
}
|
@ -143,7 +143,10 @@ final class Kitsu {
|
|||||||
*/
|
*/
|
||||||
public static function parseStreamingLinks(array $included): array
|
public static function parseStreamingLinks(array $included): array
|
||||||
{
|
{
|
||||||
if ( ! array_key_exists('streamingLinks', $included))
|
if (
|
||||||
|
( ! array_key_exists('streamingLinks', $included)) ||
|
||||||
|
count($included['streamingLinks']) === 0
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@ -152,7 +155,16 @@ final class Kitsu {
|
|||||||
|
|
||||||
foreach ($included['streamingLinks'] as $streamingLink)
|
foreach ($included['streamingLinks'] as $streamingLink)
|
||||||
{
|
{
|
||||||
$host = parse_url($streamingLink['url'], \PHP_URL_HOST);
|
$url = $streamingLink['url'];
|
||||||
|
|
||||||
|
// 'Fix' links that start with the hostname,
|
||||||
|
// rather than a protocol
|
||||||
|
if (strpos($url, '//') === FALSE)
|
||||||
|
{
|
||||||
|
$url = '//' . $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
$host = parse_url($url, \PHP_URL_HOST);
|
||||||
|
|
||||||
$links[] = [
|
$links[] = [
|
||||||
'meta' => static::getServiceMetaData($host),
|
'meta' => static::getServiceMetaData($host),
|
||||||
@ -182,17 +194,7 @@ final class Kitsu {
|
|||||||
|
|
||||||
if (count($anime['relationships']['streamingLinks']) > 0)
|
if (count($anime['relationships']['streamingLinks']) > 0)
|
||||||
{
|
{
|
||||||
foreach ($anime['relationships']['streamingLinks'] as $streamingLink)
|
return static::parseStreamingLinks($anime['relationships']);
|
||||||
{
|
|
||||||
$host = parse_url($streamingLink['url'], \PHP_URL_HOST);
|
|
||||||
|
|
||||||
$links[] = [
|
|
||||||
'meta' => static::getServiceMetaData($host),
|
|
||||||
'link' => $streamingLink['url'],
|
|
||||||
'subs' => $streamingLink['subs'],
|
|
||||||
'dubs' => $streamingLink['dubs']
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $links;
|
return $links;
|
||||||
@ -243,10 +245,9 @@ final class Kitsu {
|
|||||||
foreach($existingTitles as $existing)
|
foreach($existingTitles as $existing)
|
||||||
{
|
{
|
||||||
$isSubset = mb_substr_count($existing, $title) > 0;
|
$isSubset = mb_substr_count($existing, $title) > 0;
|
||||||
$diff = levenshtein($existing, $title);
|
$diff = levenshtein(mb_strtolower($existing), mb_strtolower($title));
|
||||||
$onlydifferentCase = (mb_strtolower($existing) === mb_strtolower($title));
|
|
||||||
|
|
||||||
if ($diff <= 3 OR $isSubset OR $onlydifferentCase OR mb_strlen($title) > 55 OR mb_strlen($existing) > 60)
|
if ($diff <= 4 || $isSubset || mb_strlen($title) > 40 || mb_strlen($existing) > 40)
|
||||||
{
|
{
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
@ -39,8 +39,8 @@ final class AnimeTransformer extends AbstractTransformer {
|
|||||||
$item['genres'] = array_column($genres, 'title') ?? [];
|
$item['genres'] = array_column($genres, 'title') ?? [];
|
||||||
sort($item['genres']);
|
sort($item['genres']);
|
||||||
|
|
||||||
$titles = Kitsu::filterTitles($item);
|
$title = $item['canonicalTitle'];
|
||||||
$title = array_shift($titles);
|
$titles = array_diff($item['titles'], [$title]);
|
||||||
|
|
||||||
return new Anime([
|
return new Anime([
|
||||||
'age_rating' => $item['ageRating'],
|
'age_rating' => $item['ageRating'],
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient\API\MAL;
|
namespace Aviat\AnimeClient\API\MAL;
|
||||||
|
|
||||||
|
use const Aviat\AnimeClient\USER_AGENT;
|
||||||
|
|
||||||
use Aviat\AnimeClient\API\{
|
use Aviat\AnimeClient\API\{
|
||||||
APIRequestBuilder,
|
APIRequestBuilder,
|
||||||
MAL as M
|
MAL as M
|
||||||
@ -38,7 +40,7 @@ final class MALRequestBuilder extends APIRequestBuilder {
|
|||||||
'Accept' => 'text/xml',
|
'Accept' => 'text/xml',
|
||||||
'Accept-Encoding' => 'gzip',
|
'Accept-Encoding' => 'gzip',
|
||||||
'Content-type' => 'application/x-www-form-urlencoded',
|
'Content-type' => 'application/x-www-form-urlencoded',
|
||||||
'User-Agent' => "Tim's Anime Client/4.0"
|
'User-Agent' => USER_AGENT,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient\API\Mapping;
|
namespace Aviat\AnimeClient\API\Mapping;
|
||||||
|
|
||||||
use Aviat\AnimeClient\API\Enum\AnimeWatchingStatus\{Kitsu, MAL, Route, Title};
|
use Aviat\AnimeClient\API\Enum\AnimeWatchingStatus\{Anilist, Kitsu, MAL, Route, Title};
|
||||||
use Aviat\Ion\Enum;
|
use Aviat\Ion\Enum;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,6 +24,22 @@ use Aviat\Ion\Enum;
|
|||||||
* and url route segments
|
* and url route segments
|
||||||
*/
|
*/
|
||||||
final class AnimeWatchingStatus extends Enum {
|
final class AnimeWatchingStatus extends Enum {
|
||||||
|
const ANILIST_TO_KITSU = [
|
||||||
|
Anilist::WATCHING => Kitsu::WATCHING,
|
||||||
|
Anilist::PLAN_TO_WATCH => Kitsu::PLAN_TO_WATCH,
|
||||||
|
Anilist::COMPLETED => Kitsu::COMPLETED,
|
||||||
|
Anilist::ON_HOLD => Kitsu::ON_HOLD,
|
||||||
|
Anilist::DROPPED => Kitsu::DROPPED
|
||||||
|
];
|
||||||
|
|
||||||
|
const KITSU_TO_ANILIST = [
|
||||||
|
Kitsu::WATCHING => Anilist::WATCHING,
|
||||||
|
Kitsu::PLAN_TO_WATCH => Anilist::PLAN_TO_WATCH,
|
||||||
|
Kitsu::COMPLETED => Anilist::COMPLETED,
|
||||||
|
Kitsu::ON_HOLD => Anilist::ON_HOLD,
|
||||||
|
Kitsu::DROPPED => Anilist::DROPPED
|
||||||
|
];
|
||||||
|
|
||||||
const KITSU_TO_MAL = [
|
const KITSU_TO_MAL = [
|
||||||
Kitsu::WATCHING => MAL::WATCHING,
|
Kitsu::WATCHING => MAL::WATCHING,
|
||||||
Kitsu::PLAN_TO_WATCH => MAL::PLAN_TO_WATCH,
|
Kitsu::PLAN_TO_WATCH => MAL::PLAN_TO_WATCH,
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient\API\Mapping;
|
namespace Aviat\AnimeClient\API\Mapping;
|
||||||
|
|
||||||
use Aviat\AnimeClient\API\Enum\MangaReadingStatus\{Kitsu, MAL, Title, Route};
|
use Aviat\AnimeClient\API\Enum\MangaReadingStatus\{Anilist, Kitsu, MAL, Title, Route};
|
||||||
use Aviat\Ion\Enum;
|
use Aviat\Ion\Enum;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,6 +24,23 @@ use Aviat\Ion\Enum;
|
|||||||
* and url route segments
|
* and url route segments
|
||||||
*/
|
*/
|
||||||
final class MangaReadingStatus extends Enum {
|
final class MangaReadingStatus extends Enum {
|
||||||
|
const ANILIST_TO_KITSU = [
|
||||||
|
Anilist::READING => Kitsu::READING,
|
||||||
|
Anilist::PLAN_TO_READ => Kitsu::PLAN_TO_READ,
|
||||||
|
Anilist::COMPLETED => Kitsu::COMPLETED,
|
||||||
|
Anilist::ON_HOLD => Kitsu::ON_HOLD,
|
||||||
|
Anilist::DROPPED => Kitsu::DROPPED
|
||||||
|
];
|
||||||
|
|
||||||
|
const KITSU_TO_ANILIST = [
|
||||||
|
Kitsu::READING => Anilist::READING,
|
||||||
|
Kitsu::PLAN_TO_READ => Anilist::PLAN_TO_READ,
|
||||||
|
Kitsu::COMPLETED => Anilist::COMPLETED,
|
||||||
|
Kitsu::ON_HOLD => Anilist::ON_HOLD,
|
||||||
|
Kitsu::DROPPED => Anilist::DROPPED
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
const KITSU_TO_MAL = [
|
const KITSU_TO_MAL = [
|
||||||
Kitsu::READING => MAL::READING,
|
Kitsu::READING => MAL::READING,
|
||||||
Kitsu::PLAN_TO_READ => MAL::PLAN_TO_READ,
|
Kitsu::PLAN_TO_READ => MAL::PLAN_TO_READ,
|
||||||
|
@ -71,6 +71,24 @@ final class Index extends BaseController {
|
|||||||
'message' => $message
|
'message' => $message
|
||||||
], $view);
|
], $view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirect to Anilist to start Oauth flow
|
||||||
|
*/
|
||||||
|
public function anilistRedirect()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Oauth callback for Anilist API
|
||||||
|
*/
|
||||||
|
public function anilistCallback()
|
||||||
|
{
|
||||||
|
$this->outputHTML('blank', [
|
||||||
|
'title' => 'Oauth!'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt login authentication
|
* Attempt login authentication
|
||||||
|
@ -20,6 +20,7 @@ use Aviat\AnimeClient\Controller;
|
|||||||
use Aviat\AnimeClient\API\Kitsu\Transformer\MangaListTransformer;
|
use Aviat\AnimeClient\API\Kitsu\Transformer\MangaListTransformer;
|
||||||
use Aviat\AnimeClient\API\Mapping\MangaReadingStatus;
|
use Aviat\AnimeClient\API\Mapping\MangaReadingStatus;
|
||||||
use Aviat\AnimeClient\Model\Manga as MangaModel;
|
use Aviat\AnimeClient\Model\Manga as MangaModel;
|
||||||
|
use Aviat\AnimeClient\Types\MangaFormItem;
|
||||||
use Aviat\Ion\Di\ContainerInterface;
|
use Aviat\Ion\Di\ContainerInterface;
|
||||||
use Aviat\Ion\{Json, StringWrapper};
|
use Aviat\Ion\{Json, StringWrapper};
|
||||||
|
|
||||||
@ -200,7 +201,7 @@ final class Manga extends Controller {
|
|||||||
// large form-based updates
|
// large form-based updates
|
||||||
$transformer = new MangaListTransformer();
|
$transformer = new MangaListTransformer();
|
||||||
$post_data = $transformer->untransform($data);
|
$post_data = $transformer->untransform($data);
|
||||||
$full_result = $this->model->updateLibraryItem($post_data);
|
$full_result = $this->model->updateLibraryItem(new MangaFormItem($post_data));
|
||||||
|
|
||||||
if ($full_result['statusCode'] === 200)
|
if ($full_result['statusCode'] === 200)
|
||||||
{
|
{
|
||||||
@ -234,7 +235,7 @@ final class Manga extends Controller {
|
|||||||
$data = $this->request->getParsedBody();
|
$data = $this->request->getParsedBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = $this->model->updateLibraryItem($data);
|
$response = $this->model->updateLibraryItem(new MangaFormItem($data));
|
||||||
|
|
||||||
$this->cache->clear();
|
$this->cache->clear();
|
||||||
$this->outputJSON($response['body'], $response['statusCode']);
|
$this->outputJSON($response['body'], $response['statusCode']);
|
||||||
|
@ -71,12 +71,18 @@ class Manga extends API {
|
|||||||
{
|
{
|
||||||
if ($status === 'All')
|
if ($status === 'All')
|
||||||
{
|
{
|
||||||
return $this->kitsuModel->getFullOrganizedMangaList();
|
$data = $this->kitsuModel->getFullOrganizedMangaList();
|
||||||
|
foreach($data as &$section)
|
||||||
|
{
|
||||||
|
$this->sortByName($section, 'manga');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
$APIstatus = MangaReadingStatus::TITLE_TO_KITSU[$status];
|
$APIstatus = MangaReadingStatus::TITLE_TO_KITSU[$status];
|
||||||
$data =
|
$data = $this->mapByStatus($this->kitsuModel->getMangaList($APIstatus));
|
||||||
$this->mapByStatus($this->kitsuModel->getMangaList($APIstatus));
|
$this->sortByName($data[$status], 'manga');
|
||||||
return $data[$status];
|
return $data[$status];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,11 +234,6 @@ class Manga extends API {
|
|||||||
|
|
||||||
unset($entry);
|
unset($entry);
|
||||||
|
|
||||||
foreach ($output as &$val)
|
|
||||||
{
|
|
||||||
$this->sortByName($val, 'manga');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,4 +23,5 @@ const DEFAULT_LIST_CONTROLLER = Controller\Anime::class;
|
|||||||
const ERROR_MESSAGE_METHOD = 'errorPage';
|
const ERROR_MESSAGE_METHOD = 'errorPage';
|
||||||
const NOT_FOUND_METHOD = 'notFound';
|
const NOT_FOUND_METHOD = 'notFound';
|
||||||
const SESSION_SEGMENT = 'Aviat\AnimeClient\Auth';
|
const SESSION_SEGMENT = 'Aviat\AnimeClient\Auth';
|
||||||
const SRC_DIR = __DIR__;
|
const SRC_DIR = __DIR__;
|
||||||
|
const USER_AGENT = "Tim's Anime Client/4.0";
|
Loading…
Reference in New Issue
Block a user