Version 5.1 - All the GraphQL #32
@ -15,7 +15,7 @@ test:7:
|
|||||||
- php composer.phar install --no-dev
|
- php composer.phar install --no-dev
|
||||||
image: php:7
|
image: php:7
|
||||||
script:
|
script:
|
||||||
- phpunit -c build
|
- phpunit -c build --coverage-text
|
||||||
|
|
||||||
test:7.1:
|
test:7.1:
|
||||||
before_script:
|
before_script:
|
||||||
@ -24,7 +24,7 @@ test:7.1:
|
|||||||
- php composer.phar install --no-dev
|
- php composer.phar install --no-dev
|
||||||
image: php:7.1
|
image: php:7.1
|
||||||
script:
|
script:
|
||||||
- phpunit -c build
|
- phpunit -c build --coverage-text
|
||||||
|
|
||||||
test:hhvm:
|
test:hhvm:
|
||||||
before_script:
|
before_script:
|
||||||
@ -34,4 +34,4 @@ test:hhvm:
|
|||||||
- composer install --no-dev
|
- composer install --no-dev
|
||||||
image: 51systems/docker-gitlab-ci-runner-hhvm
|
image: 51systems/docker-gitlab-ci-runner-hhvm
|
||||||
script:
|
script:
|
||||||
- hhvm -d hhvm.php7.all=true vendor/bin/phpunit -c build
|
- hhvm -d hhvm.php7.all=true /usr/local/bin/phpunit -c build --coverage-text
|
@ -1,7 +1,8 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## Version 4
|
## Version 4
|
||||||
* Updated to use Kitsu API after discontinuation of Hummingbird
|
* Updated to use Kitsu API after discontinuation of Hummingbird
|
||||||
|
* Added streaming links to list entries from the Kitsu API
|
||||||
|
|
||||||
## Version 3
|
## Version 3
|
||||||
* Converted user configuration to toml files
|
* Converted user configuration to toml files
|
||||||
|
@ -22,7 +22,7 @@ use Aura\Session\SessionFactory;
|
|||||||
use Aviat\AnimeClient\API\Kitsu\{
|
use Aviat\AnimeClient\API\Kitsu\{
|
||||||
Auth as KitsuAuth,
|
Auth as KitsuAuth,
|
||||||
ListItem as KitsuListItem,
|
ListItem as KitsuListItem,
|
||||||
KitsuModel
|
Model as KitsuModel
|
||||||
};
|
};
|
||||||
use Aviat\AnimeClient\API\MAL\{
|
use Aviat\AnimeClient\API\MAL\{
|
||||||
ListItem as MALListItem,
|
ListItem as MALListItem,
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
<?php foreach($item['anime']['streaming_links'] as $link): ?>
|
<?php foreach($item['anime']['streaming_links'] as $link): ?>
|
||||||
<div class="cover_streaming_link">
|
<div class="cover_streaming_link">
|
||||||
<?php if($link['meta']['link']): ?>
|
<?php if($link['meta']['link']): ?>
|
||||||
<a href="<?= $link['link']?>">
|
<a href="<?= $link['link']?>" title="Stream '<?= $item['anime']['title'] ?>' on <?= $link['meta']['name'] ?>">
|
||||||
<?= $link['meta']['logo'] ?>
|
<?= $link['meta']['logo'] ?>
|
||||||
</a>
|
</a>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
|
@ -12,27 +12,18 @@ set -xe
|
|||||||
apt-get update -yqq
|
apt-get update -yqq
|
||||||
apt-get install \
|
apt-get install \
|
||||||
git \
|
git \
|
||||||
unzip \
|
|
||||||
libfreetype6 \
|
|
||||||
libjpeg62-turbo \
|
|
||||||
libmcrypt4 \
|
|
||||||
libpng12-0 \
|
|
||||||
libfreetype6-dev \
|
|
||||||
libjpeg-dev \
|
|
||||||
libmcrypt-dev \
|
|
||||||
libpng12-dev \
|
|
||||||
libxslt1-dev \
|
libxslt1-dev \
|
||||||
libxslt1.1 \
|
libxslt1.1 \
|
||||||
zlib1g-dev \
|
zlib1g-dev \
|
||||||
|
unzip \
|
||||||
-yqq
|
-yqq
|
||||||
|
|
||||||
# Install phpunit, the tool that we will use for testing
|
# Install phpunit, the tool that we will use for testing
|
||||||
curl -Lo /usr/local/bin/phpunit https://phar.phpunit.de/phpunit.phar
|
curl -Lo /usr/local/bin/phpunit https://phar.phpunit.de/phpunit.phar
|
||||||
chmod +x /usr/local/bin/phpunit
|
chmod +x /usr/local/bin/phpunit
|
||||||
|
|
||||||
# Install gd
|
# Install extensions
|
||||||
docker-php-ext-configure gd --enable-gd-native-ttf --with-jpeg-dir=/usr/lib/x86_64-linux-gnu --with-png-dir=/usr/lib/x86_64-linux-gnu --with-freetype-dir=/usr/lib/x86_64-linux-gnu
|
pecl install xdebug
|
||||||
docker-php-ext-install gd
|
echo "zend_extension=$(find /usr/local/lib/php/extensions/ -name xdebug.so)" > /usr/local/etc/php/conf.d/xdebug.ini
|
||||||
docker-php-ext-install mcrypt
|
|
||||||
docker-php-ext-install xsl
|
docker-php-ext-install xsl
|
||||||
docker-php-ext-install zip
|
docker-php-ext-install zip
|
2
console
2
console
@ -34,8 +34,8 @@ unset($CONF_DIR);
|
|||||||
// Start console script
|
// Start console script
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
$console = new \ConsoleKit\Console([
|
$console = new \ConsoleKit\Console([
|
||||||
'cache-images' => '\Aviat\AnimeClient\Command\CacheImages',
|
|
||||||
'clear-cache' => '\Aviat\AnimeClient\Command\ClearCache',
|
'clear-cache' => '\Aviat\AnimeClient\Command\ClearCache',
|
||||||
|
'sync-lists' => '\Aviat\AnimeClient\Command\SyncKitsuWithMal'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$console->run();
|
$console->run();
|
@ -1307,4 +1307,13 @@ a:hover, a:active {
|
|||||||
padding:0 0,5em 0.5em;
|
padding:0 0,5em 0.5em;
|
||||||
padding:0 0.5rem 0.5rem;
|
padding:0 0.5rem 0.5rem;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
Images / Logos
|
||||||
|
-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.streaming-logo {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
}
|
}
|
@ -560,4 +560,12 @@ a:hover, a:active {
|
|||||||
padding:0 0,5em 0.5em;
|
padding:0 0,5em 0.5em;
|
||||||
padding:0 0.5rem 0.5rem;
|
padding:0 0.5rem 0.5rem;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
Images / Logos
|
||||||
|
-----------------------------------------------------------------------------*/
|
||||||
|
.streaming-logo {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
}
|
}
|
@ -28,6 +28,8 @@ use DateTimeImmutable;
|
|||||||
*/
|
*/
|
||||||
class Kitsu {
|
class Kitsu {
|
||||||
const AUTH_URL = 'https://kitsu.io/api/oauth/token';
|
const AUTH_URL = 'https://kitsu.io/api/oauth/token';
|
||||||
|
const AUTH_USER_ID_KEY = 'kitsu-auth-userid';
|
||||||
|
const AUTH_TOKEN_CACHE_KEY = 'kitsu-auth-token';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map of Kitsu status to label for select menus
|
* Map of Kitsu status to label for select menus
|
||||||
@ -104,21 +106,21 @@ class Kitsu {
|
|||||||
return [
|
return [
|
||||||
'name' => 'Crunchyroll',
|
'name' => 'Crunchyroll',
|
||||||
'link' => true,
|
'link' => true,
|
||||||
'logo' => '<svg width="50" height="50" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg"><g fill="#F78B24" fill-rule="evenodd"><path d="M22.549 49.145c-.815-.077-2.958-.456-3.753-.663-6.873-1.79-12.693-6.59-15.773-13.009C1.335 31.954.631 28.807.633 24.788c.003-4.025.718-7.235 2.38-10.686 1.243-2.584 2.674-4.609 4.706-6.66 3.8-3.834 8.614-6.208 14.067-6.936 1.783-.239 5.556-.161 7.221.148 3.463.642 6.571 1.904 9.357 3.797 5.788 3.934 9.542 9.951 10.52 16.861.21 1.48.332 4.559.19 4.816-.077.14-.117-.007-.167-.615-.25-3.015-1.528-6.66-3.292-9.388C40.253 7.836 30.249 4.32 20.987 7.467c-7.15 2.43-12.522 8.596-13.997 16.06-.73 3.692-.51 7.31.658 10.882a21.426 21.426 0 0 0 13.247 13.518c1.475.515 3.369.944 4.618 1.047 1.496.122 1.119.239-.727.224-1.006-.008-2.013-.032-2.237-.053z"></path><path d="M27.685 46.1c-7.731-.575-14.137-6.455-15.474-14.204-.243-1.41-.29-4.047-.095-5.345 1.16-7.706 6.97-13.552 14.552-14.639 1.537-.22 4.275-.143 5.746.162 1.28.266 2.7.737 3.814 1.266l.865.411-.814.392c-2.936 1.414-4.748 4.723-4.323 7.892.426 3.173 2.578 5.664 5.667 6.56 1.112.322 2.812.322 3.925 0 1.438-.417 2.566-1.1 3.593-2.173.346-.362.652-.621.68-.576.027.046.106.545.176 1.11.171 1.395.07 4.047-.204 5.371-.876 4.218-3.08 7.758-6.463 10.374-3.2 2.476-7.434 3.711-11.645 3.399z"></path></g></svg>'
|
'logo' => '<svg class="streaming-logo" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg"><g fill="#F78B24" fill-rule="evenodd"><path d="M22.549 49.145c-.815-.077-2.958-.456-3.753-.663-6.873-1.79-12.693-6.59-15.773-13.009C1.335 31.954.631 28.807.633 24.788c.003-4.025.718-7.235 2.38-10.686 1.243-2.584 2.674-4.609 4.706-6.66 3.8-3.834 8.614-6.208 14.067-6.936 1.783-.239 5.556-.161 7.221.148 3.463.642 6.571 1.904 9.357 3.797 5.788 3.934 9.542 9.951 10.52 16.861.21 1.48.332 4.559.19 4.816-.077.14-.117-.007-.167-.615-.25-3.015-1.528-6.66-3.292-9.388C40.253 7.836 30.249 4.32 20.987 7.467c-7.15 2.43-12.522 8.596-13.997 16.06-.73 3.692-.51 7.31.658 10.882a21.426 21.426 0 0 0 13.247 13.518c1.475.515 3.369.944 4.618 1.047 1.496.122 1.119.239-.727.224-1.006-.008-2.013-.032-2.237-.053z"></path><path d="M27.685 46.1c-7.731-.575-14.137-6.455-15.474-14.204-.243-1.41-.29-4.047-.095-5.345 1.16-7.706 6.97-13.552 14.552-14.639 1.537-.22 4.275-.143 5.746.162 1.28.266 2.7.737 3.814 1.266l.865.411-.814.392c-2.936 1.414-4.748 4.723-4.323 7.892.426 3.173 2.578 5.664 5.667 6.56 1.112.322 2.812.322 3.925 0 1.438-.417 2.566-1.1 3.593-2.173.346-.362.652-.621.68-.576.027.046.106.545.176 1.11.171 1.395.07 4.047-.204 5.371-.876 4.218-3.08 7.758-6.463 10.374-3.2 2.476-7.434 3.711-11.645 3.399z"></path></g></svg>'
|
||||||
];
|
];
|
||||||
|
|
||||||
case 'www.funimation.com':
|
case 'www.funimation.com':
|
||||||
return [
|
return [
|
||||||
'name' => 'Funimation',
|
'name' => 'Funimation',
|
||||||
'link' => true,
|
'link' => true,
|
||||||
'logo' => '<svg width="50" height="50" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg"><path d="M24.066.017a24.922 24.922 0 0 1 13.302 3.286 25.098 25.098 0 0 1 7.833 7.058 24.862 24.862 0 0 1 4.207 9.575c.82 4.001.641 8.201-.518 12.117a24.946 24.946 0 0 1-4.868 9.009 24.98 24.98 0 0 1-7.704 6.118 24.727 24.727 0 0 1-10.552 2.718A24.82 24.82 0 0 1 13.833 47.3c-5.815-2.872-10.408-8.107-12.49-14.25-2.162-6.257-1.698-13.375 1.303-19.28C5.483 8.07 10.594 3.55 16.602 1.435A24.94 24.94 0 0 1 24.066.017zm-8.415 33.31c.464 2.284 1.939 4.358 3.99 5.48 2.174 1.217 4.765 1.444 7.202 1.181 2.002-.217 3.986-.992 5.455-2.397 1.173-1.151 2.017-2.648 2.33-4.267-1.189-.027-2.378 0-3.566-.03-.568.082-1.137-.048-1.705.014-1.232.012-2.465.003-3.697-.01-.655.066-1.309-.035-1.963.013-1.166-.053-2.334.043-3.5-.025-1.515.08-3.03-.035-4.546.042z" fill="#411299" fill-rule="evenodd"></path></svg>'
|
'logo' => '<svg class="streaming-logo" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg"><path d="M24.066.017a24.922 24.922 0 0 1 13.302 3.286 25.098 25.098 0 0 1 7.833 7.058 24.862 24.862 0 0 1 4.207 9.575c.82 4.001.641 8.201-.518 12.117a24.946 24.946 0 0 1-4.868 9.009 24.98 24.98 0 0 1-7.704 6.118 24.727 24.727 0 0 1-10.552 2.718A24.82 24.82 0 0 1 13.833 47.3c-5.815-2.872-10.408-8.107-12.49-14.25-2.162-6.257-1.698-13.375 1.303-19.28C5.483 8.07 10.594 3.55 16.602 1.435A24.94 24.94 0 0 1 24.066.017zm-8.415 33.31c.464 2.284 1.939 4.358 3.99 5.48 2.174 1.217 4.765 1.444 7.202 1.181 2.002-.217 3.986-.992 5.455-2.397 1.173-1.151 2.017-2.648 2.33-4.267-1.189-.027-2.378 0-3.566-.03-.568.082-1.137-.048-1.705.014-1.232.012-2.465.003-3.697-.01-.655.066-1.309-.035-1.963.013-1.166-.053-2.334.043-3.5-.025-1.515.08-3.03-.035-4.546.042z" fill="#411299" fill-rule="evenodd"></path></svg>'
|
||||||
];
|
];
|
||||||
|
|
||||||
case 'www.hulu.com':
|
case 'www.hulu.com':
|
||||||
return [
|
return [
|
||||||
'name' => 'Hulu',
|
'name' => 'Hulu',
|
||||||
'link' => true,
|
'link' => true,
|
||||||
'logo' => '<svg width="50" height="50" viewBox="0 0 34 50" xmlns="http://www.w3.org/2000/svg"><path d="M22.222 13.889h-11.11V0H0v50h11.111V27.778c0-1.39 1.111-2.778 2.778-2.778h5.555c1.39 0 2.778 1.111 2.778 2.778V50h11.111V25c0-6.111-5-11.111-11.11-11.111z" fill="#8BC34A" fill-rule="evenodd"></path></svg>'
|
'logo' => '<svg class="streaming-logo" viewBox="0 0 34 50" xmlns="http://www.w3.org/2000/svg"><path d="M22.222 13.889h-11.11V0H0v50h11.111V27.778c0-1.39 1.111-2.778 2.778-2.778h5.555c1.39 0 2.778 1.111 2.778 2.778V50h11.111V25c0-6.111-5-11.111-11.11-11.111z" fill="#8BC34A" fill-rule="evenodd"></path></svg>'
|
||||||
];
|
];
|
||||||
|
|
||||||
// Default to Netflix, because the API links are broken,
|
// Default to Netflix, because the API links are broken,
|
||||||
@ -127,7 +129,7 @@ class Kitsu {
|
|||||||
return [
|
return [
|
||||||
'name' => 'Netflix',
|
'name' => 'Netflix',
|
||||||
'link' => false,
|
'link' => false,
|
||||||
'logo' => '<svg width="50" height="50" viewBox="0 0 26 50" xmlns="http://www.w3.org/2000/svg"><path d="M.057.258C2.518.253 4.982.263 7.446.253c2.858 7.76 5.621 15.556 8.456 23.324.523 1.441 1.003 2.897 1.59 4.312.078-9.209.01-18.42.034-27.631h7.763v46.36c-2.812.372-5.637.627-8.457.957-1.203-3.451-2.396-6.902-3.613-10.348-1.796-5.145-3.557-10.302-5.402-15.428.129 8.954.015 17.912.057 26.871-2.603.39-5.227.637-7.815 1.119C.052 33.279.06 16.768.057.258z" fill="#E21221" fill-rule="evenodd"></path></svg>'
|
'logo' => '<svg class="streaming-logo" viewBox="0 0 26 50" xmlns="http://www.w3.org/2000/svg"><path d="M.057.258C2.518.253 4.982.263 7.446.253c2.858 7.76 5.621 15.556 8.456 23.324.523 1.441 1.003 2.897 1.59 4.312.078-9.209.01-18.42.034-27.631h7.763v46.36c-2.812.372-5.637.627-8.457.957-1.203-3.451-2.396-6.902-3.613-10.348-1.796-5.145-3.557-10.302-5.402-15.428.129 8.954.015 17.912.057 26.871-2.603.39-5.227.637-7.815 1.119C.052 33.279.06 16.768.057.258z" fill="#E21221" fill-rule="evenodd"></path></svg>'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -161,6 +163,39 @@ class Kitsu {
|
|||||||
|
|
||||||
return $links;
|
return $links;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reorganize streaming links for the current list item
|
||||||
|
*
|
||||||
|
* @param array $included
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function parseListItemStreamingLinks(array $included, string $animeId): array
|
||||||
|
{
|
||||||
|
// Anime lists have a different structure to search through
|
||||||
|
if (array_key_exists('anime', $included) && ! array_key_exists('streamingLinks', $included))
|
||||||
|
{
|
||||||
|
$links = [];
|
||||||
|
$anime = $included['anime'][$animeId];
|
||||||
|
|
||||||
|
if (count($anime['relationships']['streamingLinks']) > 0)
|
||||||
|
{
|
||||||
|
foreach ($anime['relationships']['streamingLinks'] as $streamingLink)
|
||||||
|
{
|
||||||
|
$host = parse_url($streamingLink['url'], \PHP_URL_HOST);
|
||||||
|
|
||||||
|
$links[] = [
|
||||||
|
'meta' => static::getServiceMetaData($host),
|
||||||
|
'link' => $streamingLink['url'],
|
||||||
|
'subs' => $streamingLink['subs'],
|
||||||
|
'dubs' => $streamingLink['dubs']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $links;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter out duplicate and very similar names from
|
* Filter out duplicate and very similar names from
|
||||||
|
@ -17,6 +17,10 @@
|
|||||||
namespace Aviat\AnimeClient\API\Kitsu;
|
namespace Aviat\AnimeClient\API\Kitsu;
|
||||||
|
|
||||||
use Aviat\AnimeClient\AnimeClient;
|
use Aviat\AnimeClient\AnimeClient;
|
||||||
|
use Aviat\AnimeClient\API\{
|
||||||
|
CacheTrait,
|
||||||
|
Kitsu as K
|
||||||
|
};
|
||||||
use Aviat\Ion\Di\{ContainerAware, ContainerInterface};
|
use Aviat\Ion\Di\{ContainerAware, ContainerInterface};
|
||||||
use Exception;
|
use Exception;
|
||||||
|
|
||||||
@ -24,7 +28,7 @@ use Exception;
|
|||||||
* Kitsu API Authentication
|
* Kitsu API Authentication
|
||||||
*/
|
*/
|
||||||
class Auth {
|
class Auth {
|
||||||
|
use CacheTrait;
|
||||||
use ContainerAware;
|
use ContainerAware;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,6 +53,7 @@ class Auth {
|
|||||||
public function __construct(ContainerInterface $container)
|
public function __construct(ContainerInterface $container)
|
||||||
{
|
{
|
||||||
$this->setContainer($container);
|
$this->setContainer($container);
|
||||||
|
$this->setCache($container->get('cache'));
|
||||||
$this->segment = $container->get('session')
|
$this->segment = $container->get('session')
|
||||||
->getSegment(AnimeClient::SESSION_SEGMENT);
|
->getSegment(AnimeClient::SESSION_SEGMENT);
|
||||||
$this->model = $container->get('kitsu-model');
|
$this->model = $container->get('kitsu-model');
|
||||||
@ -68,7 +73,7 @@ class Auth {
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$auth_token = $this->model->authenticate($username, $password);
|
$auth = $this->model->authenticate($username, $password);
|
||||||
}
|
}
|
||||||
catch (Exception $e)
|
catch (Exception $e)
|
||||||
{
|
{
|
||||||
@ -76,9 +81,14 @@ class Auth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (FALSE !== $auth_token)
|
if (FALSE !== $auth)
|
||||||
{
|
{
|
||||||
$this->segment->set('auth_token', $auth_token);
|
// Set the token in the cache for command line operations
|
||||||
|
$cacheItem = $this->cache->getItem(K::AUTH_TOKEN_CACHE_KEY);
|
||||||
|
$cacheItem->set($auth['access_token']);
|
||||||
|
$cacheItem->save();
|
||||||
|
|
||||||
|
$this->segment->set('auth_token', $auth['access_token']);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,10 @@ use Aviat\AnimeClient\API\CacheTrait;
|
|||||||
use Aviat\AnimeClient\API\JsonAPI;
|
use Aviat\AnimeClient\API\JsonAPI;
|
||||||
use Aviat\AnimeClient\API\Kitsu as K;
|
use Aviat\AnimeClient\API\Kitsu as K;
|
||||||
use Aviat\AnimeClient\API\Kitsu\Transformer\{
|
use Aviat\AnimeClient\API\Kitsu\Transformer\{
|
||||||
AnimeTransformer, AnimeListTransformer, MangaTransformer, MangaListTransformer
|
AnimeTransformer,
|
||||||
|
AnimeListTransformer,
|
||||||
|
MangaTransformer,
|
||||||
|
MangaListTransformer
|
||||||
};
|
};
|
||||||
use Aviat\Ion\Di\ContainerAware;
|
use Aviat\Ion\Di\ContainerAware;
|
||||||
use Aviat\Ion\Json;
|
use Aviat\Ion\Json;
|
||||||
@ -29,7 +32,7 @@ use GuzzleHttp\Exception\ClientException;
|
|||||||
/**
|
/**
|
||||||
* Kitsu API Model
|
* Kitsu API Model
|
||||||
*/
|
*/
|
||||||
class KitsuModel {
|
class Model {
|
||||||
use CacheTrait;
|
use CacheTrait;
|
||||||
use ContainerAware;
|
use ContainerAware;
|
||||||
use KitsuTrait;
|
use KitsuTrait;
|
||||||
@ -65,7 +68,7 @@ class KitsuModel {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* KitsuModel constructor.
|
* Constructor.
|
||||||
*/
|
*/
|
||||||
public function __construct(ListItem $listItem)
|
public function __construct(ListItem $listItem)
|
||||||
{
|
{
|
||||||
@ -85,17 +88,30 @@ class KitsuModel {
|
|||||||
* @param string $username
|
* @param string $username
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getUserIdByUsername(string $username)
|
public function getUserIdByUsername(string $username = NULL)
|
||||||
{
|
{
|
||||||
$data = $this->getRequest('users', [
|
if (is_null($username))
|
||||||
'query' => [
|
{
|
||||||
'filter' => [
|
$username = $this->getUsername();
|
||||||
'name' => $username
|
}
|
||||||
|
|
||||||
|
$cacheItem = $this->cache->getItem(K::AUTH_USER_ID_KEY);
|
||||||
|
|
||||||
|
if ( ! $cacheItem->isHit())
|
||||||
|
{
|
||||||
|
$data = $this->getRequest('users', [
|
||||||
|
'query' => [
|
||||||
|
'filter' => [
|
||||||
|
'name' => $username
|
||||||
|
]
|
||||||
]
|
]
|
||||||
]
|
]);
|
||||||
]);
|
|
||||||
|
|
||||||
return $data['data'][0]['id'];
|
$cacheItem->set($data['data'][0]['id']);
|
||||||
|
$cacheItem->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cacheItem->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -120,7 +136,7 @@ class KitsuModel {
|
|||||||
|
|
||||||
if (array_key_exists('access_token', $data))
|
if (array_key_exists('access_token', $data))
|
||||||
{
|
{
|
||||||
return $data['access_token'];
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -139,6 +155,12 @@ class KitsuModel {
|
|||||||
return $this->animeTransformer->transform($baseData);
|
return $this->animeTransformer->transform($baseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get information about a particular anime
|
||||||
|
*
|
||||||
|
* @param string $animeId
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
public function getAnimeById(string $animeId): array
|
public function getAnimeById(string $animeId): array
|
||||||
{
|
{
|
||||||
$baseData = $this->getRawMediaDataById('anime', $animeId);
|
$baseData = $this->getRawMediaDataById('anime', $animeId);
|
||||||
@ -156,14 +178,16 @@ class KitsuModel {
|
|||||||
$baseData = $this->getRawMediaData('manga', $mangaId);
|
$baseData = $this->getRawMediaData('manga', $mangaId);
|
||||||
return $this->mangaTransformer->transform($baseData);
|
return $this->mangaTransformer->transform($baseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the anime list for the configured user
|
* Get the raw (unorganized) anime list for the configured user
|
||||||
*
|
*
|
||||||
* @param string $status - The watching status to filter the list with
|
* @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
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getAnimeList(string $status): array
|
public function getRawAnimeList(string $status, int $limit = 600, int $offset = 0): array
|
||||||
{
|
{
|
||||||
$options = [
|
$options = [
|
||||||
'query' => [
|
'query' => [
|
||||||
@ -174,17 +198,31 @@ class KitsuModel {
|
|||||||
],
|
],
|
||||||
'include' => 'media,media.genres,media.mappings,anime.streamingLinks',
|
'include' => 'media,media.genres,media.mappings,anime.streamingLinks',
|
||||||
'page' => [
|
'page' => [
|
||||||
'offset' => 0,
|
'offset' => $offset,
|
||||||
'limit' => 500
|
'limit' => $limit
|
||||||
]
|
],
|
||||||
|
'sort' => '-updated_at'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$cacheItem = $this->cache->getItem($this->getHashForMethodCall($this, __METHOD__, $options));
|
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]));
|
||||||
|
|
||||||
if ( ! $cacheItem->isHit())
|
if ( ! $cacheItem->isHit())
|
||||||
{
|
{
|
||||||
$data = $this->getRequest('library-entries', $options);
|
$data = $this->getRawAnimeList($status, $limit, $offset);
|
||||||
$included = JsonAPI::organizeIncludes($data['included']);
|
$included = JsonAPI::organizeIncludes($data['included']);
|
||||||
$included = JsonAPI::inlineIncludedRelationships($included, 'anime');
|
$included = JsonAPI::inlineIncludedRelationships($included, 'anime');
|
||||||
|
|
||||||
@ -201,7 +239,15 @@ class KitsuModel {
|
|||||||
return $cacheItem->get();
|
return $cacheItem->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMangaList($status): array
|
/**
|
||||||
|
* 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
|
||||||
{
|
{
|
||||||
$options = [
|
$options = [
|
||||||
'query' => [
|
'query' => [
|
||||||
@ -212,8 +258,8 @@ class KitsuModel {
|
|||||||
],
|
],
|
||||||
'include' => 'media',
|
'include' => 'media',
|
||||||
'page' => [
|
'page' => [
|
||||||
'offset' => 0,
|
'offset' => $offset,
|
||||||
'limit' => 200
|
'limit' => $limit
|
||||||
],
|
],
|
||||||
'sort' => '-updated_at'
|
'sort' => '-updated_at'
|
||||||
]
|
]
|
||||||
@ -239,6 +285,13 @@ class KitsuModel {
|
|||||||
return $cacheItem->get();
|
return $cacheItem->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
public function search(string $type, string $query): array
|
||||||
{
|
{
|
||||||
$options = [
|
$options = [
|
||||||
@ -264,12 +317,24 @@ class KitsuModel {
|
|||||||
return $raw;
|
return $raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a list item
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
public function createListItem(array $data): bool
|
public function createListItem(array $data): bool
|
||||||
{
|
{
|
||||||
$data['user_id'] = $this->getUserIdByUsername($this->getUsername());
|
$data['user_id'] = $this->getUserIdByUsername($this->getUsername());
|
||||||
return $this->listItem->create($data);
|
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
|
public function getListItem(string $listId): array
|
||||||
{
|
{
|
||||||
$baseData = $this->listItem->get($listId);
|
$baseData = $this->listItem->get($listId);
|
||||||
@ -294,6 +359,12 @@ class KitsuModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modify a list item
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
public function updateListItem(array $data)
|
public function updateListItem(array $data)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -313,6 +384,12 @@ class KitsuModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a list item
|
||||||
|
*
|
||||||
|
* @param string $id - The id of the list item to remove
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
public function deleteListItem(string $id): bool
|
public function deleteListItem(string $id): bool
|
||||||
{
|
{
|
||||||
return $this->listItem->delete($id);
|
return $this->listItem->delete($id);
|
@ -60,6 +60,10 @@ class AnimeListTransformer extends AbstractTransformer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$streamingLinks = (array_key_exists('streamingLinks', $anime['relationships']))
|
||||||
|
? Kitsu::parseListItemStreamingLinks($included, $animeId)
|
||||||
|
: [];
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'id' => $item['id'],
|
'id' => $item['id'],
|
||||||
@ -78,12 +82,13 @@ class AnimeListTransformer extends AbstractTransformer {
|
|||||||
],
|
],
|
||||||
'anime' => [
|
'anime' => [
|
||||||
'age_rating' => $anime['ageRating'],
|
'age_rating' => $anime['ageRating'],
|
||||||
|
'title' => $anime['canonicalTitle'],
|
||||||
'titles' => Kitsu::filterTitles($anime),
|
'titles' => Kitsu::filterTitles($anime),
|
||||||
'slug' => $anime['slug'],
|
'slug' => $anime['slug'],
|
||||||
'type' => $this->string($anime['showType'])->upperCaseFirst()->__toString(),
|
'type' => $this->string($anime['showType'])->upperCaseFirst()->__toString(),
|
||||||
'image' => $anime['posterImage']['small'],
|
'image' => $anime['posterImage']['small'],
|
||||||
'genres' => $genres,
|
'genres' => $genres,
|
||||||
'streaming_links' => Kitsu::parseStreamingLinks($included),
|
'streaming_links' => $streamingLinks,
|
||||||
],
|
],
|
||||||
'watching_status' => $item['attributes']['status'],
|
'watching_status' => $item['attributes']['status'],
|
||||||
'notes' => $item['attributes']['notes'],
|
'notes' => $item['attributes']['notes'],
|
||||||
|
@ -179,15 +179,43 @@ class XML {
|
|||||||
for ($i = 0; $i < $length; $i++)
|
for ($i = 0; $i < $length; $i++)
|
||||||
{
|
{
|
||||||
$el = $nodeList->item($i);
|
$el = $nodeList->item($i);
|
||||||
|
$current =& $root[$el->nodeName];
|
||||||
|
|
||||||
|
// It's a top level element!
|
||||||
if (is_a($el->childNodes->item(0), 'DomText') || ( ! $el->hasChildNodes()))
|
if (is_a($el->childNodes->item(0), 'DomText') || ( ! $el->hasChildNodes()))
|
||||||
{
|
{
|
||||||
$root[$el->nodeName] = $el->textContent;
|
$current = $el->textContent;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
// An empty value at the current root
|
||||||
|
if (is_null($current))
|
||||||
{
|
{
|
||||||
$root[$el->nodeName] = [];
|
$current = [];
|
||||||
static::childNodesToArray($root[$el->nodeName], $el->childNodes);
|
static::childNodesToArray($current, $el->childNodes);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$keys = array_keys($current);
|
||||||
|
|
||||||
|
// Wrap the array in a containing array
|
||||||
|
// if there are only string keys
|
||||||
|
if ( ! is_numeric($keys[0]))
|
||||||
|
{
|
||||||
|
// But if there is only one key, don't wrap it in
|
||||||
|
// an array, just recurse to parse the child nodes
|
||||||
|
if (count($current) === 1)
|
||||||
|
{
|
||||||
|
static::childNodesToArray($current, $el->childNodes);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$current = [$current];
|
||||||
|
}
|
||||||
|
|
||||||
|
array_push($current, []);
|
||||||
|
$index = count($current) - 1;
|
||||||
|
|
||||||
|
static::childNodesToArray($current[$index], $el->childNodes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +231,17 @@ class XML {
|
|||||||
{
|
{
|
||||||
foreach($data as $key => $props)
|
foreach($data as $key => $props)
|
||||||
{
|
{
|
||||||
|
// 'Flatten' the array as you create the xml
|
||||||
|
if (is_numeric($key))
|
||||||
|
{
|
||||||
|
foreach($props as $key => $props)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$node = $dom->createElement($key);
|
$node = $dom->createElement($key);
|
||||||
|
|
||||||
if (is_array($props))
|
if (is_array($props))
|
||||||
{
|
{
|
||||||
static::arrayPropertiesToXmlNodes($dom, $node, $props);
|
static::arrayPropertiesToXmlNodes($dom, $node, $props);
|
||||||
|
@ -22,20 +22,30 @@ use Aviat\AnimeClient\{
|
|||||||
Model,
|
Model,
|
||||||
Util
|
Util
|
||||||
};
|
};
|
||||||
use Aviat\AnimeClient\Auth\HummingbirdAuth;
|
use Aviat\AnimeClient\API\CacheTrait;
|
||||||
|
use Aviat\AnimeClient\API\Kitsu\{
|
||||||
|
Auth as KitsuAuth,
|
||||||
|
ListItem as KitsuListItem,
|
||||||
|
Model as KitsuModel
|
||||||
|
};
|
||||||
|
use Aviat\AnimeClient\API\MAL\{
|
||||||
|
ListItem as MALListItem,
|
||||||
|
Model as MALModel
|
||||||
|
};
|
||||||
use Aviat\Banker\Pool;
|
use Aviat\Banker\Pool;
|
||||||
use Aviat\Ion\Config;
|
use Aviat\Ion\Config;
|
||||||
use Aviat\Ion\Di\Container;
|
use Aviat\Ion\Di\{Container, ContainerAware};
|
||||||
use ConsoleKit\Command;
|
use ConsoleKit\Command;
|
||||||
use ConsoleKit\Widgets\Box;
|
use ConsoleKit\Widgets\Box;
|
||||||
use Monolog\Handler\RotatingFileHandler;
|
use Monolog\Handler\NullHandler;
|
||||||
use Monolog\Logger;
|
use Monolog\Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for console command setup
|
* Base class for console command setup
|
||||||
*/
|
*/
|
||||||
class BaseCommand extends Command {
|
class BaseCommand extends Command {
|
||||||
use \Aviat\Ion\Di\ContainerAware;
|
use CacheTrait;
|
||||||
|
use ContainerAware;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Echo text in a box
|
* Echo text in a box
|
||||||
@ -68,9 +78,16 @@ class BaseCommand extends Command {
|
|||||||
$di = function ($config_array) use ($APP_DIR) {
|
$di = function ($config_array) use ($APP_DIR) {
|
||||||
$container = new Container();
|
$container = new Container();
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Logging
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
$app_logger = new Logger('animeclient');
|
$app_logger = new Logger('animeclient');
|
||||||
$app_logger->pushHandler(new RotatingFileHandler("{$APP_DIR}/logs/app.log", Logger::NOTICE));
|
$app_logger->pushHandler(new NullHandler);
|
||||||
|
$request_logger = new Logger('request');
|
||||||
|
$request_logger->pushHandler(new NullHandler);
|
||||||
$container->setLogger($app_logger, 'default');
|
$container->setLogger($app_logger, 'default');
|
||||||
|
$container->setLogger($request_logger, 'request');
|
||||||
|
|
||||||
// Create Config Object
|
// Create Config Object
|
||||||
$container->set('config', function() use ($config_array) {
|
$container->set('config', function() use ($config_array) {
|
||||||
@ -90,18 +107,21 @@ class BaseCommand extends Command {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Models
|
// Models
|
||||||
$container->set('api-model', function($container) {
|
$container->set('kitsu-model', function($container) {
|
||||||
return new Model\API($container);
|
$listItem = new KitsuListItem();
|
||||||
|
$listItem->setContainer($container);
|
||||||
|
$model = new KitsuModel($listItem);
|
||||||
|
$model->setContainer($container);
|
||||||
|
$cache = $container->get('cache');
|
||||||
|
$model->setCache($cache);
|
||||||
|
return $model;
|
||||||
});
|
});
|
||||||
$container->set('anime-model', function($container) {
|
$container->set('mal-model', function($container) {
|
||||||
return new Model\Anime($container);
|
$listItem = new MALListItem();
|
||||||
});
|
$listItem->setContainer($container);
|
||||||
$container->set('manga-model', function($container) {
|
$model = new MALModel($listItem);
|
||||||
return new Model\Manga($container);
|
$model->setContainer($container);
|
||||||
});
|
return $model;
|
||||||
|
|
||||||
$container->set('auth', function($container) {
|
|
||||||
return new HummingbirdAuth($container);
|
|
||||||
});
|
});
|
||||||
$container->set('util', function($container) {
|
$container->set('util', function($container) {
|
||||||
return new Util($container);
|
return new Util($container);
|
||||||
|
86
src/Command/SyncKitsuWithMal.php
Normal file
86
src/Command/SyncKitsuWithMal.php
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<?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
|
||||||
|
*
|
||||||
|
* @package AnimeListClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2017 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 4.0
|
||||||
|
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\Command;
|
||||||
|
|
||||||
|
use Amp\Artax;
|
||||||
|
use Aviat\AnimeClient\API\Kitsu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the API Cache
|
||||||
|
*/
|
||||||
|
class SyncKitsuWithMal extends BaseCommand {
|
||||||
|
|
||||||
|
protected $kitsuModel;
|
||||||
|
|
||||||
|
public function getKitsuAnimeListPageCount()
|
||||||
|
{
|
||||||
|
$cacheItem = $this->cache->getItem(Kitsu::AUTH_TOKEN_CACHE_KEY);
|
||||||
|
|
||||||
|
$query = http_build_query([
|
||||||
|
'filter' => [
|
||||||
|
'user_id' => $this->kitsuModel->getUserIdByUsername(),
|
||||||
|
'media_type' => 'Anime'
|
||||||
|
],
|
||||||
|
'include' => 'anime,anime.genres,anime.mappings,anime.streamingLinks',
|
||||||
|
'page' => [
|
||||||
|
'limit' => 1
|
||||||
|
],
|
||||||
|
'sort' => '-updated_at'
|
||||||
|
]);
|
||||||
|
$request = (new Artax\Request)
|
||||||
|
->setUri("https://kitsu.io/api/edge/library-entries?{$query}")
|
||||||
|
->setProtocol('1.1')
|
||||||
|
->setAllHeaders([
|
||||||
|
'Accept' => 'application/vnd.api+json',
|
||||||
|
'Content-Type' => 'application/vnd.api+json',
|
||||||
|
'User-Agent' => "Tim's Anime Client/4.0"
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($cacheItem->isHit())
|
||||||
|
{
|
||||||
|
$token = $cacheItem->get();
|
||||||
|
$request->setHeader('Authorization', "bearer {$token}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->echoBox("WARNING: NOT LOGGED IN\nSome data might be missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = \Amp\wait((new Artax\Client)->request($request));
|
||||||
|
|
||||||
|
$body = json_decode($response->getBody(), TRUE);
|
||||||
|
return $body['meta']['count'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the image conversion script
|
||||||
|
*
|
||||||
|
* @param array $args
|
||||||
|
* @param array $options
|
||||||
|
* @return void
|
||||||
|
* @throws \ConsoleKit\ConsoleException
|
||||||
|
*/
|
||||||
|
public function execute(array $args, array $options = [])
|
||||||
|
{
|
||||||
|
$this->setContainer($this->setupContainer());
|
||||||
|
$this->setCache($this->container->get('cache'));
|
||||||
|
$this->kitsuModel = $this->container->get('kitsu-model');
|
||||||
|
|
||||||
|
$kitsuCount = $this->getKitsuAnimeListPageCount();
|
||||||
|
$this->echoBox("List item count: {$kitsuCount}");
|
||||||
|
}
|
||||||
|
}
|
@ -34,7 +34,7 @@ class API extends Model {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache manager
|
* Cache manager
|
||||||
* @var \Aviat\Ion\Cache\CacheInterface
|
* @var \Psr\Cache\CacheItemPoolInterface
|
||||||
*/
|
*/
|
||||||
protected $cache;
|
protected $cache;
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ class API extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sort the manga entries by their title
|
* Sort the list entries by their title
|
||||||
*
|
*
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
* @param array $array
|
* @param array $array
|
||||||
@ -75,4 +75,3 @@ class API extends Model {
|
|||||||
array_multisort($sort, SORT_ASC, $array);
|
array_multisort($sort, SORT_ASC, $array);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// End of BaseApiModel.php
|
|
||||||
|
@ -9,6 +9,7 @@ class XMLTest extends TestCase {
|
|||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
|
$this->malExport = file_get_contents(__DIR__ . '/../test_data/XML/MALExport.xml');
|
||||||
$this->xml = file_get_contents(__DIR__ . '/../test_data/XML/xmlTestFile.xml');
|
$this->xml = file_get_contents(__DIR__ . '/../test_data/XML/xmlTestFile.xml');
|
||||||
$this->expectedXml = file_get_contents(__DIR__ . '/../test_data/XML/minifiedXmlTestFile.xml');
|
$this->expectedXml = file_get_contents(__DIR__ . '/../test_data/XML/minifiedXmlTestFile.xml');
|
||||||
|
|
||||||
@ -43,6 +44,13 @@ class XMLTest extends TestCase {
|
|||||||
{
|
{
|
||||||
$this->assertEquals($this->array, XML::toArray($this->xml));
|
$this->assertEquals($this->array, XML::toArray($this->xml));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testMALExport()
|
||||||
|
{
|
||||||
|
$array = XML::toArray($this->malExport);
|
||||||
|
$this->assertEquals($array['myanimelist']['myinfo']['user_total_anime'], count($array['myanimelist']['anime']));
|
||||||
|
// $this->assertEquals($array, XML::toArray($this->malExport));
|
||||||
|
}
|
||||||
|
|
||||||
public function testParse()
|
public function testParse()
|
||||||
{
|
{
|
||||||
|
@ -7,16 +7,6 @@ class RequirementsTest extends AnimeClient_TestCase {
|
|||||||
$this->assertTrue(version_compare(PHP_VERSION, "5.4", "ge"));
|
$this->assertTrue(version_compare(PHP_VERSION, "5.4", "ge"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testHasGd()
|
|
||||||
{
|
|
||||||
$this->assertTrue(extension_loaded('gd'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testHasMcrypt()
|
|
||||||
{
|
|
||||||
$this->assertTrue(extension_loaded('mcrypt'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testHasPDO()
|
public function testHasPDO()
|
||||||
{
|
{
|
||||||
$this->assertTrue(class_exists('PDO'));
|
$this->assertTrue(class_exists('PDO'));
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"meta": {
|
"meta": {
|
||||||
"name": "Crunchyroll",
|
"name": "Crunchyroll",
|
||||||
"link": true,
|
"link": true,
|
||||||
"logo": "<svg width=\"50\" height=\"50\" viewBox=\"0 0 50 50\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><g fill=\"#F78B24\" fill-rule=\"evenodd\"><path d=\"M22.549 49.145c-.815-.077-2.958-.456-3.753-.663-6.873-1.79-12.693-6.59-15.773-13.009C1.335 31.954.631 28.807.633 24.788c.003-4.025.718-7.235 2.38-10.686 1.243-2.584 2.674-4.609 4.706-6.66 3.8-3.834 8.614-6.208 14.067-6.936 1.783-.239 5.556-.161 7.221.148 3.463.642 6.571 1.904 9.357 3.797 5.788 3.934 9.542 9.951 10.52 16.861.21 1.48.332 4.559.19 4.816-.077.14-.117-.007-.167-.615-.25-3.015-1.528-6.66-3.292-9.388C40.253 7.836 30.249 4.32 20.987 7.467c-7.15 2.43-12.522 8.596-13.997 16.06-.73 3.692-.51 7.31.658 10.882a21.426 21.426 0 0 0 13.247 13.518c1.475.515 3.369.944 4.618 1.047 1.496.122 1.119.239-.727.224-1.006-.008-2.013-.032-2.237-.053z\"><\/path><path d=\"M27.685 46.1c-7.731-.575-14.137-6.455-15.474-14.204-.243-1.41-.29-4.047-.095-5.345 1.16-7.706 6.97-13.552 14.552-14.639 1.537-.22 4.275-.143 5.746.162 1.28.266 2.7.737 3.814 1.266l.865.411-.814.392c-2.936 1.414-4.748 4.723-4.323 7.892.426 3.173 2.578 5.664 5.667 6.56 1.112.322 2.812.322 3.925 0 1.438-.417 2.566-1.1 3.593-2.173.346-.362.652-.621.68-.576.027.046.106.545.176 1.11.171 1.395.07 4.047-.204 5.371-.876 4.218-3.08 7.758-6.463 10.374-3.2 2.476-7.434 3.711-11.645 3.399z\"><\/path><\/g><\/svg>"
|
"logo": "<svg class=\"streaming-logo\" viewBox=\"0 0 50 50\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><g fill=\"#F78B24\" fill-rule=\"evenodd\"><path d=\"M22.549 49.145c-.815-.077-2.958-.456-3.753-.663-6.873-1.79-12.693-6.59-15.773-13.009C1.335 31.954.631 28.807.633 24.788c.003-4.025.718-7.235 2.38-10.686 1.243-2.584 2.674-4.609 4.706-6.66 3.8-3.834 8.614-6.208 14.067-6.936 1.783-.239 5.556-.161 7.221.148 3.463.642 6.571 1.904 9.357 3.797 5.788 3.934 9.542 9.951 10.52 16.861.21 1.48.332 4.559.19 4.816-.077.14-.117-.007-.167-.615-.25-3.015-1.528-6.66-3.292-9.388C40.253 7.836 30.249 4.32 20.987 7.467c-7.15 2.43-12.522 8.596-13.997 16.06-.73 3.692-.51 7.31.658 10.882a21.426 21.426 0 0 0 13.247 13.518c1.475.515 3.369.944 4.618 1.047 1.496.122 1.119.239-.727.224-1.006-.008-2.013-.032-2.237-.053z\"><\/path><path d=\"M27.685 46.1c-7.731-.575-14.137-6.455-15.474-14.204-.243-1.41-.29-4.047-.095-5.345 1.16-7.706 6.97-13.552 14.552-14.639 1.537-.22 4.275-.143 5.746.162 1.28.266 2.7.737 3.814 1.266l.865.411-.814.392c-2.936 1.414-4.748 4.723-4.323 7.892.426 3.173 2.578 5.664 5.667 6.56 1.112.322 2.812.322 3.925 0 1.438-.417 2.566-1.1 3.593-2.173.346-.362.652-.621.68-.576.027.046.106.545.176 1.11.171 1.395.07 4.047-.204 5.371-.876 4.218-3.08 7.758-6.463 10.374-3.2 2.476-7.434 3.711-11.645 3.399z\"><\/path><\/g><\/svg>"
|
||||||
},
|
},
|
||||||
"link": "http:\/\/www.crunchyroll.com\/attack-on-titan",
|
"link": "http:\/\/www.crunchyroll.com\/attack-on-titan",
|
||||||
"subs": ["en"],
|
"subs": ["en"],
|
||||||
@ -24,7 +24,7 @@
|
|||||||
"meta": {
|
"meta": {
|
||||||
"name": "Hulu",
|
"name": "Hulu",
|
||||||
"link": true,
|
"link": true,
|
||||||
"logo": "<svg width=\"50\" height=\"50\" viewBox=\"0 0 34 50\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><path d=\"M22.222 13.889h-11.11V0H0v50h11.111V27.778c0-1.39 1.111-2.778 2.778-2.778h5.555c1.39 0 2.778 1.111 2.778 2.778V50h11.111V25c0-6.111-5-11.111-11.11-11.111z\" fill=\"#8BC34A\" fill-rule=\"evenodd\"><\/path><\/svg>"
|
"logo": "<svg class=\"streaming-logo\" viewBox=\"0 0 34 50\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><path d=\"M22.222 13.889h-11.11V0H0v50h11.111V27.778c0-1.39 1.111-2.778 2.778-2.778h5.555c1.39 0 2.778 1.111 2.778 2.778V50h11.111V25c0-6.111-5-11.111-11.11-11.111z\" fill=\"#8BC34A\" fill-rule=\"evenodd\"><\/path><\/svg>"
|
||||||
},
|
},
|
||||||
"link": "http:\/\/www.hulu.com\/attack-on-titan",
|
"link": "http:\/\/www.hulu.com\/attack-on-titan",
|
||||||
"subs": ["en"],
|
"subs": ["en"],
|
||||||
@ -33,7 +33,7 @@
|
|||||||
"meta": {
|
"meta": {
|
||||||
"name": "Funimation",
|
"name": "Funimation",
|
||||||
"link": true,
|
"link": true,
|
||||||
"logo": "<svg width=\"50\" height=\"50\" viewBox=\"0 0 50 50\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><path d=\"M24.066.017a24.922 24.922 0 0 1 13.302 3.286 25.098 25.098 0 0 1 7.833 7.058 24.862 24.862 0 0 1 4.207 9.575c.82 4.001.641 8.201-.518 12.117a24.946 24.946 0 0 1-4.868 9.009 24.98 24.98 0 0 1-7.704 6.118 24.727 24.727 0 0 1-10.552 2.718A24.82 24.82 0 0 1 13.833 47.3c-5.815-2.872-10.408-8.107-12.49-14.25-2.162-6.257-1.698-13.375 1.303-19.28C5.483 8.07 10.594 3.55 16.602 1.435A24.94 24.94 0 0 1 24.066.017zm-8.415 33.31c.464 2.284 1.939 4.358 3.99 5.48 2.174 1.217 4.765 1.444 7.202 1.181 2.002-.217 3.986-.992 5.455-2.397 1.173-1.151 2.017-2.648 2.33-4.267-1.189-.027-2.378 0-3.566-.03-.568.082-1.137-.048-1.705.014-1.232.012-2.465.003-3.697-.01-.655.066-1.309-.035-1.963.013-1.166-.053-2.334.043-3.5-.025-1.515.08-3.03-.035-4.546.042z\" fill=\"#411299\" fill-rule=\"evenodd\"><\/path><\/svg>"
|
"logo": "<svg class=\"streaming-logo\" viewBox=\"0 0 50 50\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><path d=\"M24.066.017a24.922 24.922 0 0 1 13.302 3.286 25.098 25.098 0 0 1 7.833 7.058 24.862 24.862 0 0 1 4.207 9.575c.82 4.001.641 8.201-.518 12.117a24.946 24.946 0 0 1-4.868 9.009 24.98 24.98 0 0 1-7.704 6.118 24.727 24.727 0 0 1-10.552 2.718A24.82 24.82 0 0 1 13.833 47.3c-5.815-2.872-10.408-8.107-12.49-14.25-2.162-6.257-1.698-13.375 1.303-19.28C5.483 8.07 10.594 3.55 16.602 1.435A24.94 24.94 0 0 1 24.066.017zm-8.415 33.31c.464 2.284 1.939 4.358 3.99 5.48 2.174 1.217 4.765 1.444 7.202 1.181 2.002-.217 3.986-.992 5.455-2.397 1.173-1.151 2.017-2.648 2.33-4.267-1.189-.027-2.378 0-3.566-.03-.568.082-1.137-.048-1.705.014-1.232.012-2.465.003-3.697-.01-.655.066-1.309-.035-1.963.013-1.166-.053-2.334.043-3.5-.025-1.515.08-3.03-.035-4.546.042z\" fill=\"#411299\" fill-rule=\"evenodd\"><\/path><\/svg>"
|
||||||
},
|
},
|
||||||
"link": "http:\/\/www.funimation.com\/shows\/attack-on-titan\/videos\/episodes",
|
"link": "http:\/\/www.funimation.com\/shows\/attack-on-titan\/videos\/episodes",
|
||||||
"subs": ["en"],
|
"subs": ["en"],
|
||||||
@ -42,7 +42,7 @@
|
|||||||
"meta": {
|
"meta": {
|
||||||
"name": "Netflix",
|
"name": "Netflix",
|
||||||
"link": false,
|
"link": false,
|
||||||
"logo": "<svg width=\"50\" height=\"50\" viewBox=\"0 0 26 50\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><path d=\"M.057.258C2.518.253 4.982.263 7.446.253c2.858 7.76 5.621 15.556 8.456 23.324.523 1.441 1.003 2.897 1.59 4.312.078-9.209.01-18.42.034-27.631h7.763v46.36c-2.812.372-5.637.627-8.457.957-1.203-3.451-2.396-6.902-3.613-10.348-1.796-5.145-3.557-10.302-5.402-15.428.129 8.954.015 17.912.057 26.871-2.603.39-5.227.637-7.815 1.119C.052 33.279.06 16.768.057.258z\" fill=\"#E21221\" fill-rule=\"evenodd\"><\/path><\/svg>"
|
"logo": "<svg class=\"streaming-logo\" viewBox=\"0 0 26 50\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><path d=\"M.057.258C2.518.253 4.982.263 7.446.253c2.858 7.76 5.621 15.556 8.456 23.324.523 1.441 1.003 2.897 1.59 4.312.078-9.209.01-18.42.034-27.631h7.763v46.36c-2.812.372-5.637.627-8.457.957-1.203-3.451-2.396-6.902-3.613-10.348-1.796-5.145-3.557-10.302-5.402-15.428.129 8.954.015 17.912.057 26.871-2.603.39-5.227.637-7.815 1.119C.052 33.279.06 16.768.057.258z\" fill=\"#E21221\" fill-rule=\"evenodd\"><\/path><\/svg>"
|
||||||
},
|
},
|
||||||
"link": "t",
|
"link": "t",
|
||||||
"subs": ["en"],
|
"subs": ["en"],
|
||||||
|
@ -1 +1,30 @@
|
|||||||
{"id":"15839442","mal_id":"33206","episodes":{"watched":0,"total":"-","length":null},"airing":{"status":"Currently Airing","started":"2017-01-12","ended":null},"anime":{"age_rating":null,"titles":["Kobayashi-san Chi no Maid Dragon","Miss Kobayashi's Dragon Maid","\u5c0f\u6797\u3055\u3093\u3061\u306e\u30e1\u30a4\u30c9\u30e9\u30b4\u30f3"],"slug":"kobayashi-san-chi-no-maid-dragon","type":"TV","image":"https:\/\/media.kitsu.io\/anime\/poster_images\/12243\/small.jpg?1481144116","genres":["Comedy","Fantasy","Slice of Life"],"streaming_links":[]},"watching_status":"current","notes":null,"rewatching":false,"rewatched":0,"user_rating":"-","private":false}
|
{
|
||||||
|
"id": "15839442",
|
||||||
|
"mal_id": "33206",
|
||||||
|
"episodes": {
|
||||||
|
"watched": 0,
|
||||||
|
"total": "-",
|
||||||
|
"length": null
|
||||||
|
},
|
||||||
|
"airing": {
|
||||||
|
"status": "Currently Airing",
|
||||||
|
"started": "2017-01-12",
|
||||||
|
"ended": null
|
||||||
|
},
|
||||||
|
"anime": {
|
||||||
|
"age_rating": null,
|
||||||
|
"title": "Kobayashi-san Chi no Maid Dragon",
|
||||||
|
"titles": ["Kobayashi-san Chi no Maid Dragon", "Miss Kobayashi's Dragon Maid", "\u5c0f\u6797\u3055\u3093\u3061\u306e\u30e1\u30a4\u30c9\u30e9\u30b4\u30f3"],
|
||||||
|
"slug": "kobayashi-san-chi-no-maid-dragon",
|
||||||
|
"type": "TV",
|
||||||
|
"image": "https:\/\/media.kitsu.io\/anime\/poster_images\/12243\/small.jpg?1481144116",
|
||||||
|
"genres": ["Comedy", "Fantasy", "Slice of Life"],
|
||||||
|
"streaming_links": []
|
||||||
|
},
|
||||||
|
"watching_status": "current",
|
||||||
|
"notes": null,
|
||||||
|
"rewatching": false,
|
||||||
|
"rewatched": 0,
|
||||||
|
"user_rating": "-",
|
||||||
|
"private": false
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user