All in GraphQL #34
|
@ -1,5 +1,10 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Version 5.1
|
||||||
|
* Added session check, so when coming back to a page, if the session is expired, the page will refresh.
|
||||||
|
* Updated logging config so that much fewer, much smaller files are generated.
|
||||||
|
* Updated Kitsu integration to use GraphQL API, reducing a lot of internal complexity.
|
||||||
|
|
||||||
## Version 5
|
## Version 5
|
||||||
* Updated PHP requirement to 7.4
|
* Updated PHP requirement to 7.4
|
||||||
* Added anime watching history view
|
* Added anime watching history view
|
||||||
|
|
|
@ -227,7 +227,7 @@ class RoboFile extends Tasks {
|
||||||
{
|
{
|
||||||
$this->lint();
|
$this->lint();
|
||||||
|
|
||||||
$this->_run(['phpunit']);
|
$this->_run(['vendor/bin/phpunit']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -28,6 +28,17 @@ use const Aviat\AnimeClient\{
|
||||||
// Maps paths to controllers and methods
|
// Maps paths to controllers and methods
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
$routes = [
|
$routes = [
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// AJAX Routes
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
'cache_purge' => [
|
||||||
|
'path' => '/cache_purge',
|
||||||
|
'action' => 'clearCache',
|
||||||
|
],
|
||||||
|
'heartbeat' => [
|
||||||
|
'path' => '/heartbeat',
|
||||||
|
'action' => 'heartbeat',
|
||||||
|
],
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
// Anime List Routes
|
// Anime List Routes
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
|
@ -175,9 +186,9 @@ $routes = [
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'person' => [
|
'person' => [
|
||||||
'path' => '/people/{id}',
|
'path' => '/people/{slug}',
|
||||||
'tokens' => [
|
'tokens' => [
|
||||||
'id' => SLUG_PATTERN
|
'slug' => SLUG_PATTERN,
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'default_user_info' => [
|
'default_user_info' => [
|
||||||
|
@ -215,10 +226,6 @@ $routes = [
|
||||||
'file' => '[a-z0-9\-]+\.[a-z]{3,4}'
|
'file' => '[a-z0-9\-]+\.[a-z]{3,4}'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'cache_purge' => [
|
|
||||||
'path' => '/cache_purge',
|
|
||||||
'action' => 'clearCache',
|
|
||||||
],
|
|
||||||
'settings' => [
|
'settings' => [
|
||||||
'path' => '/settings',
|
'path' => '/settings',
|
||||||
],
|
],
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -19,20 +19,24 @@ namespace Aviat\AnimeClient;
|
||||||
use Aura\Html\HelperLocatorFactory;
|
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};
|
||||||
Anilist,
|
use Aviat\AnimeClient\Component;
|
||||||
Kitsu,
|
|
||||||
Kitsu\KitsuRequestBuilder
|
|
||||||
};
|
|
||||||
use Aviat\AnimeClient\Model;
|
use Aviat\AnimeClient\Model;
|
||||||
use Aviat\Banker\Teller;
|
use Aviat\Banker\Teller;
|
||||||
use Aviat\Ion\Config;
|
use Aviat\Ion\Config;
|
||||||
use Aviat\Ion\Di\Container;
|
use Aviat\Ion\Di\Container;
|
||||||
use Aviat\Ion\Di\ContainerInterface;
|
use Aviat\Ion\Di\ContainerInterface;
|
||||||
use Psr\SimpleCache\CacheInterface;
|
use Laminas\Diactoros\ServerRequestFactory;
|
||||||
use Laminas\Diactoros\{Response, ServerRequestFactory};
|
use Monolog\Formatter\JsonFormatter;
|
||||||
use Monolog\Handler\RotatingFileHandler;
|
use Monolog\Handler\RotatingFileHandler;
|
||||||
use Monolog\Logger;
|
use Monolog\Logger;
|
||||||
|
use Psr\SimpleCache\CacheInterface;
|
||||||
|
|
||||||
|
if ( ! defined('APP_DIR'))
|
||||||
|
{
|
||||||
|
define('APP_DIR', __DIR__);
|
||||||
|
define('TEMPLATE_DIR', APP_DIR . '/templates');
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Setup DI container
|
// Setup DI container
|
||||||
|
@ -45,17 +49,18 @@ return static function (array $configArray = []): Container {
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
$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', 2, Logger::WARNING));
|
||||||
|
|
||||||
$anilistRequestLogger = new Logger('anilist-request');
|
|
||||||
$anilistRequestLogger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/anilist_request.log', Logger::NOTICE));
|
|
||||||
|
|
||||||
$kitsuRequestLogger = new Logger('kitsu-request');
|
|
||||||
$kitsuRequestLogger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/kitsu_request.log', Logger::NOTICE));
|
|
||||||
|
|
||||||
$container->setLogger($appLogger);
|
$container->setLogger($appLogger);
|
||||||
$container->setLogger($anilistRequestLogger, 'anilist-request');
|
|
||||||
$container->setLogger($kitsuRequestLogger, 'kitsu-request');
|
foreach (['anilist-request', 'kitsu-request', 'kitsu-graphql'] as $channel)
|
||||||
|
{
|
||||||
|
$logger = new Logger($channel);
|
||||||
|
$handler = new RotatingFileHandler(__DIR__ . "/logs/{$channel}.log", 2, Logger::WARNING);
|
||||||
|
$handler->setFormatter(new JsonFormatter());
|
||||||
|
$logger->pushHandler($handler);
|
||||||
|
|
||||||
|
$container->setLogger($logger, $channel);
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Injected Objects
|
// Injected Objects
|
||||||
|
@ -74,29 +79,52 @@ return static function (array $configArray = []): Container {
|
||||||
// Create Aura Router Object
|
// Create Aura Router Object
|
||||||
$container->set('aura-router', fn() => new RouterContainer);
|
$container->set('aura-router', fn() => new RouterContainer);
|
||||||
|
|
||||||
// Create Html helper Object
|
// Create Html helpers
|
||||||
$container->set('html-helper', static function(ContainerInterface $container) {
|
$container->set('html-helper', static function(ContainerInterface $container) {
|
||||||
$htmlHelper = (new HelperLocatorFactory)->newInstance();
|
$htmlHelper = (new HelperLocatorFactory)->newInstance();
|
||||||
$htmlHelper->set('menu', static function() use ($container) {
|
$helpers = [
|
||||||
$menuHelper = new Helper\Menu();
|
'menu' => Helper\Menu::class,
|
||||||
$menuHelper->setContainer($container);
|
'field' => Helper\Form::class,
|
||||||
return $menuHelper;
|
'picture' => Helper\Picture::class,
|
||||||
});
|
];
|
||||||
$htmlHelper->set('field', static function() use ($container) {
|
|
||||||
$formHelper = new Helper\Form();
|
foreach ($helpers as $name => $class)
|
||||||
$formHelper->setContainer($container);
|
{
|
||||||
return $formHelper;
|
$htmlHelper->set($name, static function() use ($class, $container) {
|
||||||
});
|
$helper = new $class;
|
||||||
$htmlHelper->set('picture', static function() use ($container) {
|
$helper->setContainer($container);
|
||||||
$pictureHelper = new Helper\Picture();
|
return $helper;
|
||||||
$pictureHelper->setContainer($container);
|
});
|
||||||
return $pictureHelper;
|
}
|
||||||
});
|
|
||||||
|
|
||||||
return $htmlHelper;
|
return $htmlHelper;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create Request/Response Objects
|
// Create Component helpers
|
||||||
|
$container->set('component-helper', static function (ContainerInterface $container) {
|
||||||
|
$helper = (new HelperLocatorFactory)->newInstance();
|
||||||
|
$components = [
|
||||||
|
'animeCover' => Component\AnimeCover::class,
|
||||||
|
'mangaCover' => Component\MangaCover::class,
|
||||||
|
'character' => Component\Character::class,
|
||||||
|
'media' => Component\Media::class,
|
||||||
|
'tabs' => Component\Tabs::class,
|
||||||
|
'verticalTabs' => Component\VerticalTabs::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($components as $name => $componentClass)
|
||||||
|
{
|
||||||
|
$helper->set($name, static function () use ($container, $componentClass) {
|
||||||
|
$helper = new $componentClass;
|
||||||
|
$helper->setContainer($container);
|
||||||
|
return $helper;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return $helper;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create Request Object
|
||||||
$container->set('request', fn () => ServerRequestFactory::fromGlobals(
|
$container->set('request', fn () => ServerRequestFactory::fromGlobals(
|
||||||
$_SERVER,
|
$_SERVER,
|
||||||
$_GET,
|
$_GET,
|
||||||
|
@ -104,7 +132,6 @@ return static function (array $configArray = []): Container {
|
||||||
$_COOKIE,
|
$_COOKIE,
|
||||||
$_FILES
|
$_FILES
|
||||||
));
|
));
|
||||||
$container->set('response', fn () => new Response);
|
|
||||||
|
|
||||||
// Create session Object
|
// Create session Object
|
||||||
$container->set('session', fn () => (new SessionFactory())->newInstance($_COOKIE));
|
$container->set('session', fn () => (new SessionFactory())->newInstance($_COOKIE));
|
||||||
|
@ -114,7 +141,7 @@ return static function (array $configArray = []): Container {
|
||||||
|
|
||||||
// Models
|
// Models
|
||||||
$container->set('kitsu-model', static function(ContainerInterface $container): Kitsu\Model {
|
$container->set('kitsu-model', static function(ContainerInterface $container): Kitsu\Model {
|
||||||
$requestBuilder = new KitsuRequestBuilder($container);
|
$requestBuilder = new Kitsu\RequestBuilder($container);
|
||||||
$requestBuilder->setLogger($container->getLogger('kitsu-request'));
|
$requestBuilder->setLogger($container->getLogger('kitsu-request'));
|
||||||
|
|
||||||
$listItem = new Kitsu\ListItem();
|
$listItem = new Kitsu\ListItem();
|
||||||
|
@ -130,7 +157,7 @@ return static function (array $configArray = []): Container {
|
||||||
return $model;
|
return $model;
|
||||||
});
|
});
|
||||||
$container->set('anilist-model', static function(ContainerInterface $container): Anilist\Model {
|
$container->set('anilist-model', static function(ContainerInterface $container): Anilist\Model {
|
||||||
$requestBuilder = new Anilist\AnilistRequestBuilder();
|
$requestBuilder = new Anilist\RequestBuilder($container);
|
||||||
$requestBuilder->setLogger($container->getLogger('anilist-request'));
|
$requestBuilder->setLogger($container->getLogger('anilist-request'));
|
||||||
|
|
||||||
$listItem = new Anilist\ListItem();
|
$listItem = new Anilist\ListItem();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<article
|
<article
|
||||||
class="media"
|
class="media"
|
||||||
data-kitsu-id="<?= $item['id'] ?>"
|
data-kitsu-id="<?= $item['id'] ?>"
|
||||||
data-mal-id="<?= $item['mal_id'] ?>"
|
data-mal-id="<?= $item['mal_id'] ?>"
|
||||||
>
|
>
|
||||||
<?php if ($auth->isAuthenticated()): ?>
|
<?php if ($auth->isAuthenticated()): ?>
|
||||||
<button title="Increment episode count" class="plus-one" hidden>+1 Episode</button>
|
<button title="Increment episode count" class="plus-one" hidden>+1 Episode</button>
|
||||||
|
@ -31,13 +31,13 @@
|
||||||
<?php if ($item['rewatched'] > 0): ?>
|
<?php if ($item['rewatched'] > 0): ?>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<?php if ($item['rewatched'] == 1): ?>
|
<?php if ($item['rewatched'] == 1): ?>
|
||||||
<div>Rewatched once</div>
|
<div>Rewatched once</div>
|
||||||
<?php elseif ($item['rewatched'] == 2): ?>
|
<?php elseif ($item['rewatched'] == 2): ?>
|
||||||
<div>Rewatched twice</div>
|
<div>Rewatched twice</div>
|
||||||
<?php elseif ($item['rewatched'] == 3): ?>
|
<?php elseif ($item['rewatched'] == 3): ?>
|
||||||
<div>Rewatched thrice</div>
|
<div>Rewatched thrice</div>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<div>Rewatched <?= $item['rewatched'] ?> times</div>
|
<div>Rewatched <?= $item['rewatched'] ?> times</div>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<article class="<?= $className ?>">
|
||||||
|
<div class="name">
|
||||||
|
<a href="<?= $link ?>"><?= $name ?></a>
|
||||||
|
</div>
|
||||||
|
<a href="<?= $link ?>"><?= $picture ?></a>
|
||||||
|
</article>
|
|
@ -0,0 +1,74 @@
|
||||||
|
<article class="media" data-kitsu-id="<?= $item['id'] ?>" data-mal-id="<?= $item['mal_id'] ?>">
|
||||||
|
<?php if ($auth->isAuthenticated()): ?>
|
||||||
|
<div class="edit-buttons" hidden>
|
||||||
|
<button class="plus-one-chapter">+1 Chapter</button>
|
||||||
|
<?php /* <button class="plus-one-volume">+1 Volume</button> */ ?>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
<?= $helper->picture("images/manga/{$item['manga']['id']}.webp") ?>
|
||||||
|
<div class="name">
|
||||||
|
<a href="<?= $url->generate('manga.details', ['id' => $item['manga']['slug']]) ?>">
|
||||||
|
<?= $escape->html($item['manga']['title']) ?>
|
||||||
|
<?php foreach($item['manga']['titles'] as $title): ?>
|
||||||
|
<br /><small><?= $title ?></small>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="table">
|
||||||
|
<?php if ($auth->isAuthenticated()): ?>
|
||||||
|
<div class="row">
|
||||||
|
<span class="edit">
|
||||||
|
<a class="bracketed"
|
||||||
|
title="Edit information about this manga"
|
||||||
|
href="<?= $url->generate('edit', [
|
||||||
|
'controller' => 'manga',
|
||||||
|
'id' => $item['id'],
|
||||||
|
'status' => $name
|
||||||
|
]) ?>">
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
<div class="row">
|
||||||
|
<div><?= $item['manga']['type'] ?></div>
|
||||||
|
<div class="user-rating">Rating: <?= $item['user_rating'] ?> / 10</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($item['rereading']): ?>
|
||||||
|
<div class="row">
|
||||||
|
<?php foreach(['rereading'] as $attr): ?>
|
||||||
|
<?php if($item[$attr]): ?>
|
||||||
|
<span class="item-<?= $attr ?>"><?= ucfirst($attr) ?></span>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if ($item['reread'] > 0): ?>
|
||||||
|
<div class="row">
|
||||||
|
<?php if ($item['reread'] == 1): ?>
|
||||||
|
<div>Reread once</div>
|
||||||
|
<?php elseif ($item['reread'] == 2): ?>
|
||||||
|
<div>Reread twice</div>
|
||||||
|
<?php elseif ($item['reread'] == 3): ?>
|
||||||
|
<div>Reread thrice</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div>Reread <?= $item['reread'] ?> times</div>
|
||||||
|
<?php endif ?>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="chapter_completion">
|
||||||
|
Chapters: <span class="chapters_read"><?= $item['chapters']['read'] ?></span> /
|
||||||
|
<span class="chapter_count"><?= $item['chapters']['total'] ?></span>
|
||||||
|
</div>
|
||||||
|
<?php /* </div>
|
||||||
|
<div class="row"> */ ?>
|
||||||
|
<div class="volume_completion">
|
||||||
|
Volumes: <span class="volume_count"><?= $item['volumes']['total'] ?></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<article class="<?= $className ?>">
|
||||||
|
<a href="<?= $link ?>"><?= $picture ?></a>
|
||||||
|
<div class="name">
|
||||||
|
<a href="<?= $link ?>">
|
||||||
|
<?= array_shift($titles) ?>
|
||||||
|
<?php foreach ($titles as $title): ?>
|
||||||
|
<br />
|
||||||
|
<small><?= $title ?></small>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</article>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<section class="<?= $className ?>">
|
||||||
|
<?php foreach ($data as $tabName => $tabData): ?>
|
||||||
|
<?= $callback($tabData, $tabName) ?>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</section>
|
|
@ -0,0 +1,32 @@
|
||||||
|
<div class="tabs">
|
||||||
|
<?php $i = 0; foreach ($data as $tabName => $tabData): ?>
|
||||||
|
<?php if ( ! empty($tabData)): ?>
|
||||||
|
<?php $id = "{$name}-{$i}"; ?>
|
||||||
|
<input
|
||||||
|
role='tab'
|
||||||
|
aria-controls="_<?= $id ?>"
|
||||||
|
type="radio"
|
||||||
|
name="<?= $name ?>"
|
||||||
|
id="<?= $id ?>"
|
||||||
|
<?= ($i === 0) ? 'checked="checked"' : '' ?>
|
||||||
|
/>
|
||||||
|
<label for="<?= $id ?>"><?= ucfirst($tabName) ?></label>
|
||||||
|
|
||||||
|
<?php if ($hasSectionWrapper): ?>
|
||||||
|
<div class="content full-height">
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<section
|
||||||
|
id="_<?= $id ?>"
|
||||||
|
role="tabpanel"
|
||||||
|
class="<?= $className ?>"
|
||||||
|
>
|
||||||
|
<?= $callback($tabData, $tabName) ?>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<?php if ($hasSectionWrapper): ?>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php $i++; endforeach ?>
|
||||||
|
</div>
|
|
@ -0,0 +1,25 @@
|
||||||
|
<div class="vertical-tabs">
|
||||||
|
<?php $i = 0; ?>
|
||||||
|
<?php foreach ($data as $tabName => $tabData): ?>
|
||||||
|
<?php $id = "{$name}-{$i}" ?>
|
||||||
|
<div class="tab">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
role='tab'
|
||||||
|
aria-controls="_<?= $id ?>"
|
||||||
|
name="<?= $name ?>"
|
||||||
|
id="<?= $id ?>"
|
||||||
|
<?= $i === 0 ? 'checked="checked"' : '' ?>
|
||||||
|
/>
|
||||||
|
<label for="<?= $id ?>"><?= $tabName ?></label>
|
||||||
|
<section
|
||||||
|
id='_<?= $id ?>'
|
||||||
|
role="tabpanel"
|
||||||
|
class="<?= $className ?>"
|
||||||
|
>
|
||||||
|
<?= $callback($tabData, $tabName) ?>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<?php $i++; ?>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</div>
|
|
@ -20,7 +20,7 @@
|
||||||
<section class="media-wrap">
|
<section class="media-wrap">
|
||||||
<?php foreach($items as $item): ?>
|
<?php foreach($items as $item): ?>
|
||||||
<?php if ($item['private'] && ! $auth->isAuthenticated()) continue; ?>
|
<?php if ($item['private'] && ! $auth->isAuthenticated()) continue; ?>
|
||||||
<?php include __DIR__ . '/cover-item.php' ?>
|
<?= $component->animeCover($item) ?>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
<?php use function Aviat\AnimeClient\getLocalImg; ?>
|
<?php
|
||||||
|
|
||||||
|
use Aviat\AnimeClient\Kitsu;
|
||||||
|
use function Aviat\AnimeClient\getLocalImg;
|
||||||
|
|
||||||
|
?>
|
||||||
<main class="details fixed">
|
<main class="details fixed">
|
||||||
<section class="flex">
|
<section class="flex" unselectable>
|
||||||
<aside class="info">
|
<aside class="info">
|
||||||
<?= $helper->picture("images/anime/{$data['id']}-original.webp") ?>
|
<?= $helper->picture("images/anime/{$data['id']}-original.webp") ?>
|
||||||
|
|
||||||
|
@ -11,20 +16,33 @@
|
||||||
<td class="align-right">Airing Status</td>
|
<td class="align-right">Airing Status</td>
|
||||||
<td><?= $data['status'] ?></td>
|
<td><?= $data['status'] ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Show Type</td>
|
<td>Show Type</td>
|
||||||
<td><?= $data['show_type'] ?></td>
|
<td><?= (strlen($data['show_type']) > 3) ? ucfirst(strtolower($data['show_type'])) : $data['show_type'] ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td>Episode Count</td>
|
<?php if ($data['episode_count'] !== 1): ?>
|
||||||
<td><?= $data['episode_count'] ?? '-' ?></td>
|
|
||||||
</tr>
|
|
||||||
<?php if ( ! empty($data['episode_length'])): ?>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Episode Length</td>
|
<td>Episode Count</td>
|
||||||
<td><?= $data['episode_length'] ?> minutes</td>
|
<td><?= $data['episode_count'] ?? '-' ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if (( ! empty($data['episode_length'])) && $data['episode_count'] !== 1): ?>
|
||||||
|
<tr>
|
||||||
|
<td>Episode Length</td>
|
||||||
|
<td><?= Kitsu::friendlyTime($data['episode_length']) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if (isset($data['total_length'], $data['episode_count']) && $data['total_length'] > 0): ?>
|
||||||
|
<tr>
|
||||||
|
<td>Total Length</td>
|
||||||
|
<td><?= Kitsu::friendlyTime($data['total_length']) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
<?php if ( ! empty($data['age_rating'])): ?>
|
<?php if ( ! empty($data['age_rating'])): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Age Rating</td>
|
<td>Age Rating</td>
|
||||||
|
@ -32,6 +50,18 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if (count($data['links']) > 0): ?>
|
||||||
|
<tr>
|
||||||
|
<td>External Links</td>
|
||||||
|
<td>
|
||||||
|
<?php foreach ($data['links'] as $urlName => $externalUrl): ?>
|
||||||
|
<a rel='external' href="<?= $externalUrl ?>"><?= $urlName ?></a><br />
|
||||||
|
<?php endforeach ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Genres</td>
|
<td>Genres</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -40,11 +70,13 @@
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
<article class="text">
|
<article class="text">
|
||||||
<h2 class="toph"><a rel="external" href="<?= $data['url'] ?>"><?= $data['title'] ?></a></h2>
|
<h2 class="toph"><?= $data['title'] ?></h2>
|
||||||
<?php foreach ($data['titles_more'] as $title): ?>
|
<?php foreach ($data['titles_more'] as $title): ?>
|
||||||
<h3><?= $title ?></h3>
|
<h3><?= $title ?></h3>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
|
@ -99,81 +131,69 @@
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<?php if ( ! empty($data['trailer_id'])): ?>
|
<?php if ( ! empty($data['trailer_id'])): ?>
|
||||||
<div class="responsive-iframe">
|
<div class="responsive-iframe">
|
||||||
<h4>Trailer</h4>
|
<h4>Trailer</h4>
|
||||||
<iframe
|
<iframe
|
||||||
width="560"
|
width="560"
|
||||||
height="315"
|
height="315"
|
||||||
src="https://www.youtube.com/embed/<?= $data['trailer_id'] ?>"
|
role='img'
|
||||||
frameborder="0"
|
src="https://www.youtube.com/embed/<?= $data['trailer_id'] ?>"
|
||||||
allow="autoplay; encrypted-media"
|
allow="autoplay; encrypted-media"
|
||||||
allowfullscreen
|
allowfullscreen
|
||||||
></iframe>
|
tabindex='0'
|
||||||
|
title="<?= $data['title'] ?> trailer video"
|
||||||
|
></iframe>
|
||||||
</div>
|
</div>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<?php if (count($data['characters']) > 0): ?>
|
<?php if (count($data['characters']) > 0): ?>
|
||||||
<section>
|
<section>
|
||||||
<h2>Characters</h2>
|
<h2>Characters</h2>
|
||||||
|
|
||||||
<div class="tabs">
|
<?= $component->tabs('character-types', $data['characters'], static function ($characterList, $role)
|
||||||
<?php $i = 0 ?>
|
use ($component, $url, $helper) {
|
||||||
<?php foreach ($data['characters'] as $role => $list): ?>
|
$rendered = [];
|
||||||
<input
|
foreach ($characterList as $id => $character):
|
||||||
type="radio" name="character-types"
|
if (empty($character['image']['original']))
|
||||||
id="character-types-<?= $i ?>" <?= ($i === 0) ? 'checked' : '' ?> />
|
{
|
||||||
<label for="character-types-<?= $i ?>"><?= ucfirst($role) ?></label>
|
continue;
|
||||||
<section class="content media-wrap flex flex-wrap flex-justify-start">
|
}
|
||||||
<?php foreach ($list as $id => $char): ?>
|
$rendered[] = $component->character(
|
||||||
<?php if ( ! empty($char['image']['original'])): ?>
|
$character['name'],
|
||||||
<article class="<?= $role === 'supporting' ? 'small-' : '' ?>character">
|
$url->generate('character', ['slug' => $character['slug']]),
|
||||||
<?php $link = $url->generate('character', ['slug' => $char['slug']]) ?>
|
$helper->picture("images/characters/{$id}.webp"),
|
||||||
<div class="name">
|
(strtolower($role) !== 'main') ? 'small-character' : 'character'
|
||||||
<?= $helper->a($link, $char['name']); ?>
|
);
|
||||||
</div>
|
endforeach;
|
||||||
<a href="<?= $link ?>">
|
|
||||||
<?= $helper->picture("images/characters/{$id}.webp") ?>
|
return implode('', array_map('mb_trim', $rendered));
|
||||||
</a>
|
}) ?>
|
||||||
</article>
|
</section>
|
||||||
<?php endif ?>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</section>
|
|
||||||
<?php $i++; ?>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<?php if (count($data['staff']) > 0): ?>
|
<?php if (count($data['staff']) > 0): ?>
|
||||||
<section>
|
<section>
|
||||||
<h2>Staff</h2>
|
<h2>Staff</h2>
|
||||||
|
|
||||||
<div class="vertical-tabs">
|
<?= $component->verticalTabs('staff-role', $data['staff'], static function ($staffList)
|
||||||
<?php $i = 0; ?>
|
use ($component, $url, $helper) {
|
||||||
<?php foreach ($data['staff'] as $role => $people): ?>
|
$rendered = [];
|
||||||
<div class="tab">
|
foreach ($staffList as $id => $person):
|
||||||
<input type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
|
if (empty($person['image']['original']))
|
||||||
<label for="staff-role<?= $i ?>"><?= $role ?></label>
|
{
|
||||||
<section class='content media-wrap flex flex-wrap flex-justify-start'>
|
continue;
|
||||||
<?php foreach ($people as $pid => $person): ?>
|
}
|
||||||
<article class='character small-person'>
|
$rendered[] = $component->character(
|
||||||
<?php $link = $url->generate('person', ['id' => $person['id']]) ?>
|
$person['name'],
|
||||||
<div class="name">
|
$url->generate('person', ['slug' => $person['slug']]),
|
||||||
<a href="<?= $link ?>">
|
$helper->picture(getLocalImg($person['image']['original'] ?? NULL)),
|
||||||
<?= $person['name'] ?>
|
'character small-person',
|
||||||
</a>
|
);
|
||||||
</div>
|
endforeach;
|
||||||
<a href="<?= $link ?>">
|
|
||||||
<?= $helper->picture(getLocalImg($person['image']['original'] ?? NULL)) ?>
|
return implode('', array_map('mb_trim', $rendered));
|
||||||
</a>
|
}) ?>
|
||||||
</article>
|
</section>
|
||||||
<?php endforeach ?>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
<?php $i++; ?>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</main>
|
</main>
|
|
@ -32,7 +32,7 @@
|
||||||
<td>
|
<td>
|
||||||
<select name="watching_status" id="watching_status">
|
<select name="watching_status" id="watching_status">
|
||||||
<?php foreach($statuses as $status_key => $status_title): ?>
|
<?php foreach($statuses as $status_key => $status_title): ?>
|
||||||
<option <?php if($item['watching_status'] === $status_key): ?>selected="selected"<?php endif ?>
|
<option <?php if(strtolower($item['watching_status']) === $status_key): ?>selected="selected"<?php endif ?>
|
||||||
value="<?= $status_key ?>"><?= $status_title ?></option>
|
value="<?= $status_key ?>"><?= $status_title ?></option>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
</select>
|
</select>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?php use function Aviat\AnimeClient\col_not_empty; ?>
|
<?php use function Aviat\AnimeClient\colNotEmpty; ?>
|
||||||
<main class="media-list">
|
<main class="media-list">
|
||||||
<?php if ($auth->isAuthenticated()): ?>
|
<?php if ($auth->isAuthenticated()): ?>
|
||||||
<a class="bracketed" href="<?= $url->generate('anime.add.get') ?>">Add Item</a>
|
<a class="bracketed" href="<?= $url->generate('anime.add.get') ?>">Add Item</a>
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
<h3>There's nothing here!</h3>
|
<h3>There's nothing here!</h3>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<?php
|
<?php
|
||||||
$hasNotes = col_not_empty($items, 'notes');
|
$hasNotes = colNotEmpty($items, 'notes');
|
||||||
?>
|
?>
|
||||||
<table class='media-wrap'>
|
<table class='media-wrap'>
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -31,7 +31,6 @@
|
||||||
<th>Rated</th>
|
<th>Rated</th>
|
||||||
<th>Attributes</th>
|
<th>Attributes</th>
|
||||||
<?php if($hasNotes): ?><th>Notes</th><?php endif ?>
|
<?php if($hasNotes): ?><th>Notes</th><?php endif ?>
|
||||||
<th>Genres</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -103,10 +102,6 @@
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
<?php if ($hasNotes): ?><td><p><?= $escape->html($item['notes']) ?></p></td><?php endif ?>
|
<?php if ($hasNotes): ?><td><p><?= $escape->html($item['notes']) ?></p></td><?php endif ?>
|
||||||
<td class="align-left">
|
|
||||||
<?php sort($item['anime']->genres) ?>
|
|
||||||
<?= implode(', ', $item['anime']->genres) ?>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use function Aviat\AnimeClient\getLocalImg;
|
use function Aviat\AnimeClient\getLocalImg;
|
||||||
use Aviat\AnimeClient\API\Kitsu;
|
use Aviat\AnimeClient\Kitsu;
|
||||||
|
|
||||||
?>
|
?>
|
||||||
<main class="character-page details fixed">
|
<main class="character-page details fixed">
|
||||||
|
@ -33,63 +33,20 @@ use Aviat\AnimeClient\API\Kitsu;
|
||||||
|
|
||||||
<?php if ( ! (empty($data['media']['anime']) || empty($data['media']['manga']))): ?>
|
<?php if ( ! (empty($data['media']['anime']) || empty($data['media']['manga']))): ?>
|
||||||
<h3>Media</h3>
|
<h3>Media</h3>
|
||||||
<div class="tabs">
|
|
||||||
<?php if ( ! empty($data['media']['anime'])): ?>
|
|
||||||
<input checked="checked" type="radio" id="media-anime" name="media-tabs" />
|
|
||||||
<label for="media-anime">Anime</label>
|
|
||||||
|
|
||||||
<section class="media-wrap content">
|
<?= $component->tabs('character-media', $data['media'], static function ($media, $mediaType) use ($url, $component, $helper) {
|
||||||
<?php foreach ($data['media']['anime'] as $id => $anime): ?>
|
$rendered = [];
|
||||||
<article class="media">
|
foreach ($media as $id => $item)
|
||||||
<?php
|
{
|
||||||
$link = $url->generate('anime.details', ['id' => $anime['attributes']['slug']]);
|
$rendered[] = $component->media(
|
||||||
$titles = Kitsu::filterTitles($anime['attributes']);
|
array_merge([$item['title']], $item['titles']),
|
||||||
?>
|
$url->generate("{$mediaType}.details", ['id' => $item['slug']]),
|
||||||
<a href="<?= $link ?>">
|
$helper->picture("images/{$mediaType}/{$item['id']}.webp")
|
||||||
<?= $helper->picture("images/anime/{$id}.webp") ?>
|
);
|
||||||
</a>
|
}
|
||||||
<div class="name">
|
|
||||||
<a href="<?= $link ?>">
|
|
||||||
<?= array_shift($titles) ?>
|
|
||||||
<?php foreach ($titles as $title): ?>
|
|
||||||
<br />
|
|
||||||
<small><?= $title ?></small>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</section>
|
|
||||||
<?php endif ?>
|
|
||||||
|
|
||||||
<?php if ( ! empty($data['media']['manga'])): ?>
|
return implode('', array_map('mb_trim', $rendered));
|
||||||
<input type="radio" id="media-manga" name="media-tabs" />
|
}, 'media-wrap content') ?>
|
||||||
<label for="media-manga">Manga</label>
|
|
||||||
|
|
||||||
<section class="media-wrap content">
|
|
||||||
<?php foreach ($data['media']['manga'] as $id => $manga): ?>
|
|
||||||
<article class="media">
|
|
||||||
<?php
|
|
||||||
$link = $url->generate('manga.details', ['id' => $manga['attributes']['slug']]);
|
|
||||||
$titles = Kitsu::filterTitles($manga['attributes']);
|
|
||||||
?>
|
|
||||||
<a href="<?= $link ?>">
|
|
||||||
<?= $helper->picture("images/manga/{$id}.webp") ?>
|
|
||||||
</a>
|
|
||||||
<div class="name">
|
|
||||||
<a href="<?= $link ?>">
|
|
||||||
<?= array_shift($titles) ?>
|
|
||||||
<?php foreach ($titles as $title): ?>
|
|
||||||
<br />
|
|
||||||
<small><?= $title ?></small>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</section>
|
|
||||||
<?php endif ?>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
|
@ -158,66 +115,47 @@ use Aviat\AnimeClient\API\Kitsu;
|
||||||
<?php if ( ! empty($vas)): ?>
|
<?php if ( ! empty($vas)): ?>
|
||||||
<h4>Voice Actors</h4>
|
<h4>Voice Actors</h4>
|
||||||
|
|
||||||
<div class="tabs">
|
<?= $component->tabs('character-vas', $vas, static function ($casting) use ($url, $component, $helper) {
|
||||||
<?php $i = 0; ?>
|
$castings = [];
|
||||||
|
foreach ($casting as $id => $c):
|
||||||
|
$person = $component->character(
|
||||||
|
$c['person']['name'],
|
||||||
|
$url->generate('person', ['slug' => $c['person']['slug']]),
|
||||||
|
$helper->picture(getLocalImg($c['person']['image']))
|
||||||
|
);
|
||||||
|
$medias = array_map(fn ($series) => $component->media(
|
||||||
|
array_merge([$series['title']], $series['titles']),
|
||||||
|
$url->generate('anime.details', ['id' => $series['slug']]),
|
||||||
|
$helper->picture(getLocalImg($series['posterImage'], TRUE))
|
||||||
|
), $c['series']);
|
||||||
|
$media = implode('', array_map('mb_trim', $medias));
|
||||||
|
|
||||||
<?php foreach ($vas as $language => $casting): ?>
|
$castings[] = <<<HTML
|
||||||
<input <?= $i === 0 ? 'checked="checked"' : '' ?> type="radio" id="character-va<?= $i ?>"
|
<tr>
|
||||||
name="character-vas"
|
<td>{$person}</td>
|
||||||
/>
|
<td width="75%">
|
||||||
<label for="character-va<?= $i ?>"><?= $language ?></label>
|
<section class="align-left media-wrap-flex">
|
||||||
<section class="content">
|
{$media}
|
||||||
<table class="borderless max-table">
|
</section>
|
||||||
<tr>
|
</td>
|
||||||
<th>Cast Member</th>
|
</tr>
|
||||||
<th>Series</th>
|
HTML;
|
||||||
</tr>
|
endforeach;
|
||||||
<?php foreach ($casting as $cid => $c): ?>
|
|
||||||
<tr>
|
$languages = implode('', array_map('mb_trim', $castings));
|
||||||
<td>
|
|
||||||
<article class="character">
|
return <<<HTML
|
||||||
<?php
|
<table class="borderless max-table">
|
||||||
$link = $url->generate('person', ['id' => $c['person']['id']]);
|
<thead>
|
||||||
?>
|
<tr>
|
||||||
<a href="<?= $link ?>">
|
<th>Cast Member</th>
|
||||||
<?= $helper->picture(getLocalImg($c['person']['image'])) ?>
|
<th>Series</th>
|
||||||
<div class="name">
|
</tr>
|
||||||
<?= $c['person']['name'] ?>
|
</thead>
|
||||||
</div>
|
<tbody>{$languages}</tbody>
|
||||||
</a>
|
</table>
|
||||||
</article>
|
HTML;
|
||||||
</td>
|
}, 'content') ?>
|
||||||
<td width="75%">
|
|
||||||
<section class="align-left media-wrap-flex">
|
|
||||||
<?php foreach ($c['series'] as $series): ?>
|
|
||||||
<article class="media">
|
|
||||||
<?php
|
|
||||||
$link = $url->generate('anime.details', ['id' => $series['attributes']['slug']]);
|
|
||||||
$titles = Kitsu::filterTitles($series['attributes']);
|
|
||||||
?>
|
|
||||||
<a href="<?= $link ?>">
|
|
||||||
<?= $helper->picture(getLocalImg($series['attributes']['posterImage']['small'], TRUE)) ?>
|
|
||||||
</a>
|
|
||||||
<div class="name">
|
|
||||||
<a href="<?= $link ?>">
|
|
||||||
<?= array_shift($titles) ?>
|
|
||||||
<?php foreach ($titles as $title): ?>
|
|
||||||
<br />
|
|
||||||
<small><?= $title ?></small>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</section>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</table>
|
|
||||||
</section>
|
|
||||||
<?php $i++ ?>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td class="align-right"><label for="media_id">Media</label></td>
|
<td class="align-right"><label for="media_id">Media</label></td>
|
||||||
<td class='align-left'>
|
<td class='align-left'>
|
||||||
<?php include '_media-select-list.php' ?>
|
<?php include 'media-select-list.php' ?>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
<?php use function Aviat\AnimeClient\renderTemplate; ?>
|
||||||
<main class="media-list">
|
<main class="media-list">
|
||||||
<?php if ($auth->isAuthenticated()): ?>
|
<?php if ($auth->isAuthenticated()): ?>
|
||||||
<a class="bracketed" href="<?= $url->generate($collection_type . '.collection.add.get') ?>">Add Item</a>
|
<a class="bracketed" href="<?= $url->generate($collection_type . '.collection.add.get') ?>">Add Item</a>
|
||||||
|
@ -8,30 +9,20 @@
|
||||||
<br />
|
<br />
|
||||||
<label>Filter: <input type='text' class='media-filter' /></label>
|
<label>Filter: <input type='text' class='media-filter' /></label>
|
||||||
<br />
|
<br />
|
||||||
<div class="tabs">
|
<?= $component->tabs('collection-tab', $sections, static function ($items) use ($auth, $collection_type, $helper, $url, $component) {
|
||||||
<?php $i = 0; ?>
|
$rendered = [];
|
||||||
<?php foreach ($sections as $name => $items): ?>
|
foreach ($items as $item)
|
||||||
<input type="radio" id="collection-tab-<?= $i ?>" name="collection-tabs" />
|
{
|
||||||
<label for="collection-tab-<?= $i ?>"><h2><?= $name ?></h2></label>
|
$rendered[] = renderTemplate(__DIR__ . '/cover-item.php', [
|
||||||
<div class="content full-height">
|
'auth' => $auth,
|
||||||
<section class="media-wrap">
|
'collection_type' => $collection_type,
|
||||||
<?php foreach ($items as $item): ?>
|
'helper' => $helper,
|
||||||
<?php include __DIR__ . '/cover-item.php'; ?>
|
'item' => $item,
|
||||||
<?php endforeach ?>
|
'url' => $url,
|
||||||
</section>
|
]);
|
||||||
</div>
|
}
|
||||||
<?php $i++; ?>
|
|
||||||
<?php endforeach ?>
|
return implode('', array_map('mb_trim', $rendered));
|
||||||
<!-- All Tab -->
|
}, 'media-wrap', true) ?>
|
||||||
<input type='radio' checked='checked' id='collection-tab-<?= $i ?>' name='collection-tabs' />
|
|
||||||
<label for='collection-tab-<?= $i ?>'><h2>All</h2></label>
|
|
||||||
<div class='content full-height'>
|
|
||||||
<section class="media-wrap">
|
|
||||||
<?php foreach ($all as $item): ?>
|
|
||||||
<?php include __DIR__ . '/cover-item.php'; ?>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
<?php use function Aviat\AnimeClient\renderTemplate ?>
|
||||||
<?php if ($auth->isAuthenticated()): ?>
|
<?php if ($auth->isAuthenticated()): ?>
|
||||||
<main>
|
<main>
|
||||||
<h2>Edit Anime Collection Item</h2>
|
<h2>Edit Anime Collection Item</h2>
|
||||||
|
@ -24,7 +25,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td class="align-right"><label for="media_id">Media</label></td>
|
<td class="align-right"><label for="media_id">Media</label></td>
|
||||||
<td class="align-left">
|
<td class="align-left">
|
||||||
<?php include '_media-select-list.php' ?>
|
<?php include 'media-select-list.php' ?>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
<input type='radio' checked='checked' id='collection-tab-<?= $i ?>' name='collection-tabs' />
|
|
||||||
<label for='collection-tab-<?= $i ?>'><h2>All</h2></label>
|
|
||||||
<div class="content full-height">
|
|
||||||
<table class="full-width media-wrap">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<?php if ($auth->isAuthenticated()): ?><td> </td><?php endif ?>
|
|
||||||
<th>Title</th>
|
|
||||||
<th>Media</th>
|
|
||||||
<th>Episode Count</th>
|
|
||||||
<th>Episode Length</th>
|
|
||||||
<th>Show Type</th>
|
|
||||||
<th>Age Rating</th>
|
|
||||||
<th>Notes</th>
|
|
||||||
<th>Genres</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($all as $item): ?>
|
|
||||||
<?php $editLink = $url->generate($collection_type . '.collection.edit.get', ['id' => $item['hummingbird_id']]); ?>
|
|
||||||
<tr>
|
|
||||||
<?php if ($auth->isAuthenticated()): ?>
|
|
||||||
<td>
|
|
||||||
<a class="bracketed" href="<?= $editLink ?>">Edit</a>
|
|
||||||
</td>
|
|
||||||
<?php endif ?>
|
|
||||||
<td class="align-left">
|
|
||||||
<a href="<?= $url->generate('anime.details', ['id' => $item['slug']]) ?>">
|
|
||||||
<?= $item['title'] ?>
|
|
||||||
</a>
|
|
||||||
<?= ! empty($item['alternate_title']) ? ' <br /><small> ' . $item['alternate_title'] . '</small>' : '' ?>
|
|
||||||
</td>
|
|
||||||
<td><?= implode(', ', $item['media']) ?></td>
|
|
||||||
<td><?= ($item['episode_count'] > 1) ? $item['episode_count'] : '-' ?></td>
|
|
||||||
<td><?= $item['episode_length'] ?></td>
|
|
||||||
<td><?= $item['show_type'] ?></td>
|
|
||||||
<td><?= $item['age_rating'] ?></td>
|
|
||||||
<td class="align-left"><?= nl2br($item['notes'], TRUE) ?></td>
|
|
||||||
<td class="align-left"><?= implode(', ', $item['genres']) ?></td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
|
@ -11,6 +11,9 @@
|
||||||
</a>
|
</a>
|
||||||
<?= ! empty($item['alternate_title']) ? ' <br /><small> ' . $item['alternate_title'] . '</small>' : '' ?>
|
<?= ! empty($item['alternate_title']) ? ' <br /><small> ' . $item['alternate_title'] . '</small>' : '' ?>
|
||||||
</td>
|
</td>
|
||||||
|
<?php if ($hasMedia): ?>
|
||||||
|
<td><?= implode(', ', $item['media']) ?></td>
|
||||||
|
<?php endif ?>
|
||||||
<td><?= ($item['episode_count'] > 1) ? $item['episode_count'] : '-' ?></td>
|
<td><?= ($item['episode_count'] > 1) ? $item['episode_count'] : '-' ?></td>
|
||||||
<td><?= $item['episode_length'] ?></td>
|
<td><?= $item['episode_length'] ?></td>
|
||||||
<td><?= $item['show_type'] ?></td>
|
<td><?= $item['show_type'] ?></td>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?php use function Aviat\AnimeClient\col_not_empty; ?>
|
<?php use function Aviat\AnimeClient\{colNotEmpty, renderTemplate}; ?>
|
||||||
<main>
|
<main>
|
||||||
<?php if ($auth->isAuthenticated()): ?>
|
<?php if ($auth->isAuthenticated()): ?>
|
||||||
<a class="bracketed" href="<?= $url->generate($collection_type . '.collection.add.get') ?>">Add Item</a>
|
<a class="bracketed" href="<?= $url->generate($collection_type . '.collection.add.get') ?>">Add Item</a>
|
||||||
|
@ -9,38 +9,48 @@
|
||||||
<br />
|
<br />
|
||||||
<label>Filter: <input type='text' class='media-filter' /></label>
|
<label>Filter: <input type='text' class='media-filter' /></label>
|
||||||
<br />
|
<br />
|
||||||
<?php $i = 0; ?>
|
<?= $component->tabs('collection-tab', $sections, static function ($items, $section) use ($auth, $helper, $url, $collection_type) {
|
||||||
<div class="tabs">
|
$hasNotes = colNotEmpty($items, 'notes');
|
||||||
<?php foreach ($sections as $name => $items): ?>
|
$hasMedia = $section === 'All';
|
||||||
<?php $hasNotes = col_not_empty($items, 'notes') ?>
|
$firstTh = ($auth->isAuthenticated()) ? '<td> </td>' : '';
|
||||||
<input type="radio" id="collection-tab-<?= $i ?>" name="collection-tabs" />
|
$mediaTh = ($hasMedia) ? '<th>Media</th>' : '';
|
||||||
<label for="collection-tab-<?= $i ?>"><h2><?= $name ?></h2></label>
|
$noteTh = ($hasNotes) ? '<th>Notes</th>' : '';
|
||||||
<div class="content full-height">
|
|
||||||
<table class="full-width media-wrap">
|
$rendered = [];
|
||||||
<thead>
|
foreach ($items as $item)
|
||||||
<tr>
|
{
|
||||||
<?php if ($auth->isAuthenticated()): ?><td> </td><?php endif ?>
|
$rendered[] = renderTemplate(__DIR__ . '/list-item.php', [
|
||||||
<th>Title</th>
|
'auth' => $auth,
|
||||||
<th>Episode Count</th>
|
'collection_type' => $collection_type,
|
||||||
<th>Episode Length</th>
|
'hasMedia' => $hasMedia,
|
||||||
<th>Show Type</th>
|
'hasNotes' => $hasNotes,
|
||||||
<th>Age Rating</th>
|
'helper' => $helper,
|
||||||
<?php if ($hasNotes): ?><th>Notes</th><?php endif ?>
|
'item' => $item,
|
||||||
<th>Genres</th>
|
'url' => $url,
|
||||||
</tr>
|
]);
|
||||||
</thead>
|
}
|
||||||
<tbody>
|
$rows = implode('', array_map('mb_trim', $rendered));
|
||||||
<?php foreach ($items as $item): ?>
|
|
||||||
<?php include 'list-item.php' ?>
|
return <<<HTML
|
||||||
<?php endforeach ?>
|
<table class="full-width media-wrap">
|
||||||
</tbody>
|
<thead>
|
||||||
</table>
|
<tr>
|
||||||
</div>
|
{$firstTh}
|
||||||
<?php $i++ ?>
|
<th>Title</th>
|
||||||
<?php endforeach ?>
|
{$mediaTh}
|
||||||
<!-- All -->
|
<th>Episode Count</th>
|
||||||
<?php include 'list-all.php' ?>
|
<th>Episode Length</th>
|
||||||
</div>
|
<th>Show Type</th>
|
||||||
|
<th>Age Rating</th>
|
||||||
|
{$noteTh}
|
||||||
|
<th>Genres</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>{$rows}</tbody>
|
||||||
|
</table>
|
||||||
|
HTML;
|
||||||
|
|
||||||
|
}) ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</main>
|
</main>
|
||||||
<script defer="defer" src="<?= $urlGenerator->assetUrl('js/tables.min.js') ?>"></script>
|
<script defer="defer" src="<?= $urlGenerator->assetUrl('js/tables.min.js') ?>"></script>
|
|
@ -39,5 +39,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
</header>
|
</header>
|
|
@ -19,80 +19,7 @@
|
||||||
<h2><?= $escape->html($name) ?></h2>
|
<h2><?= $escape->html($name) ?></h2>
|
||||||
<section class="media-wrap">
|
<section class="media-wrap">
|
||||||
<?php foreach($items as $item): ?>
|
<?php foreach($items as $item): ?>
|
||||||
<article class="media" data-kitsu-id="<?= $item['id'] ?>" data-mal-id="<?= $item['mal_id'] ?>">
|
<?= $component->mangaCover($item, $name) ?>
|
||||||
<?php if ($auth->isAuthenticated()): ?>
|
|
||||||
<div class="edit-buttons" hidden>
|
|
||||||
<button class="plus-one-chapter">+1 Chapter</button>
|
|
||||||
<?php /* <button class="plus-one-volume">+1 Volume</button> */ ?>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
|
||||||
<?= $helper->picture("images/manga/{$item['manga']['id']}.webp") ?>
|
|
||||||
<div class="name">
|
|
||||||
<a href="<?= $url->generate('manga.details', ['id' => $item['manga']['slug']]) ?>">
|
|
||||||
<?= $escape->html($item['manga']['title']) ?>
|
|
||||||
<?php foreach($item['manga']['titles'] as $title): ?>
|
|
||||||
<br /><small><?= $title ?></small>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="table">
|
|
||||||
<?php if ($auth->isAuthenticated()): ?>
|
|
||||||
<div class="row">
|
|
||||||
<span class="edit">
|
|
||||||
<a class="bracketed"
|
|
||||||
title="Edit information about this manga"
|
|
||||||
href="<?= $url->generate('edit', [
|
|
||||||
'controller' => 'manga',
|
|
||||||
'id' => $item['id'],
|
|
||||||
'status' => $name
|
|
||||||
]) ?>">
|
|
||||||
Edit
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
|
||||||
<div class="row">
|
|
||||||
<div><?= $item['manga']['type'] ?></div>
|
|
||||||
<div class="user-rating">Rating: <?= $item['user_rating'] ?> / 10</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php if ($item['rereading']): ?>
|
|
||||||
<div class="row">
|
|
||||||
<?php foreach(['rereading'] as $attr): ?>
|
|
||||||
<?php if($item[$attr]): ?>
|
|
||||||
<span class="item-<?= $attr ?>"><?= ucfirst($attr) ?></span>
|
|
||||||
<?php endif ?>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
|
||||||
|
|
||||||
<?php if ($item['reread'] > 0): ?>
|
|
||||||
<div class="row">
|
|
||||||
<?php if ($item['reread'] == 1): ?>
|
|
||||||
<div>Reread once</div>
|
|
||||||
<?php elseif ($item['reread'] == 2): ?>
|
|
||||||
<div>Reread twice</div>
|
|
||||||
<?php elseif ($item['reread'] == 3): ?>
|
|
||||||
<div>Reread thrice</div>
|
|
||||||
<?php else: ?>
|
|
||||||
<div>Reread <?= $item['reread'] ?> times</div>
|
|
||||||
<?php endif ?>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="chapter_completion">
|
|
||||||
Chapters: <span class="chapters_read"><?= $item['chapters']['read'] ?></span> /
|
|
||||||
<span class="chapter_count"><?= $item['chapters']['total'] ?></span>
|
|
||||||
</div>
|
|
||||||
<?php /* </div>
|
|
||||||
<div class="row"> */ ?>
|
|
||||||
<div class="volume_completion">
|
|
||||||
Volumes: <span class="volume_count"><?= $item['volumes']['total'] ?></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -7,17 +7,45 @@
|
||||||
|
|
||||||
<table class="media-details">
|
<table class="media-details">
|
||||||
<tr>
|
<tr>
|
||||||
<td>Manga Type</td>
|
<td class="align-right">Publishing Status</td>
|
||||||
<td><?= ucfirst($data['manga_type']) ?></td>
|
<td><?= $data['status'] ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Manga Type</td>
|
||||||
|
<td><?= ucfirst(strtolower($data['manga_type'])) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php if ( ! empty($data['volume_count'])): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Volume Count</td>
|
<td>Volume Count</td>
|
||||||
<td><?= $data['volume_count'] ?></td>
|
<td><?= $data['volume_count'] ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php if ( ! empty($data['chapter_count'])): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Chapter Count</td>
|
<td>Chapter Count</td>
|
||||||
<td><?= $data['chapter_count'] ?></td>
|
<td><?= $data['chapter_count'] ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if ( ! empty($data['age_rating'])): ?>
|
||||||
|
<tr>
|
||||||
|
<td>Age Rating</td>
|
||||||
|
<td><abbr title="<?= $data['age_rating_guide'] ?>"><?= $data['age_rating'] ?></abbr>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if (count($data['links']) > 0): ?>
|
||||||
|
<tr>
|
||||||
|
<td>External Links</td>
|
||||||
|
<td>
|
||||||
|
<?php foreach ($data['links'] as $urlName => $externalUrl): ?>
|
||||||
|
<a rel='external' href="<?= $externalUrl ?>"><?= $urlName ?></a><br />
|
||||||
|
<?php endforeach ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Genres</td>
|
<td>Genres</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -29,8 +57,8 @@
|
||||||
<br />
|
<br />
|
||||||
</aside>
|
</aside>
|
||||||
<article class="text">
|
<article class="text">
|
||||||
<h2 class="toph"><a rel="external" href="<?= $data['url'] ?>"><?= $data['title'] ?></a></h2>
|
<h2 class="toph"><?= $data['title'] ?></h2>
|
||||||
<?php foreach ($data['titles'] as $title): ?>
|
<?php foreach ($data['titles_more'] as $title): ?>
|
||||||
<h3><?= $title ?></h3>
|
<h3><?= $title ?></h3>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
|
|
||||||
|
@ -44,61 +72,34 @@
|
||||||
<?php if (count($data['characters']) > 0): ?>
|
<?php if (count($data['characters']) > 0): ?>
|
||||||
<h2>Characters</h2>
|
<h2>Characters</h2>
|
||||||
|
|
||||||
<div class="tabs">
|
<?= $component->tabs('manga-characters', $data['characters'], static function($list, $role) use ($component, $helper, $url) {
|
||||||
<?php $i = 0 ?>
|
$rendered = [];
|
||||||
<?php foreach ($data['characters'] as $role => $list): ?>
|
foreach ($list as $id => $char)
|
||||||
<input
|
{
|
||||||
type="radio" name="character-role-tabs"
|
$rendered[] = $component->character(
|
||||||
id="character-tabs<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
|
$char['name'],
|
||||||
<label for="character-tabs<?= $i ?>"><?= ucfirst($role) ?></label>
|
$url->generate('character', ['slug' => $char['slug']]),
|
||||||
<section class="content media-wrap flex flex-wrap flex-justify-start">
|
$helper->picture("images/characters/{$id}.webp"),
|
||||||
<?php foreach ($list as $id => $char): ?>
|
($role !== 'main') ? 'small-character' : 'character'
|
||||||
<?php if ( ! empty($char['image']['original'])): ?>
|
);
|
||||||
<article class="<?= $role === 'supporting' ? 'small-' : '' ?>character">
|
}
|
||||||
<?php $link = $url->generate('character', ['slug' => $char['slug']]) ?>
|
|
||||||
<div class="name">
|
return implode('', array_map('mb_trim', $rendered));
|
||||||
<?= $helper->a($link, $char['name']); ?>
|
}) ?>
|
||||||
</div>
|
|
||||||
<a href="<?= $link ?>">
|
|
||||||
<?= $helper->picture("images/characters/{$id}.webp") ?>
|
|
||||||
</a>
|
|
||||||
</article>
|
|
||||||
<?php endif ?>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</section>
|
|
||||||
<?php $i++ ?>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<?php if (count($data['staff']) > 0): ?>
|
<?php if (count($data['staff']) > 0): ?>
|
||||||
<h2>Staff</h2>
|
<h2>Staff</h2>
|
||||||
|
|
||||||
<div class="vertical-tabs">
|
<?= $component->verticalTabs('manga-staff', $data['staff'],
|
||||||
<?php $i = 0 ?>
|
fn($people) => implode('', array_map(
|
||||||
<?php foreach ($data['staff'] as $role => $people): ?>
|
fn ($person) => $component->character(
|
||||||
<div class="tab">
|
$person['name'],
|
||||||
<input
|
$url->generate('person', ['slug' => $person['slug']]),
|
||||||
type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
|
$helper->picture("images/people/{$person['id']}.webp")
|
||||||
<label for="staff-role<?= $i ?>"><?= $role ?></label>
|
),
|
||||||
<section class='content media-wrap flex flex-wrap flex-justify-start'>
|
$people
|
||||||
<?php foreach ($people as $pid => $person): ?>
|
))
|
||||||
<article class='character person'>
|
) ?>
|
||||||
<?php $link = $url->generate('person', ['id' => $pid]) ?>
|
|
||||||
<div class="name">
|
|
||||||
<a href="<?= $link ?>">
|
|
||||||
<?= $person['name'] ?>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<a href="<?= $link ?>">
|
|
||||||
<?= $helper->picture("images/people/{$pid}.webp") ?>
|
|
||||||
</a>
|
|
||||||
</article>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
<?php $i++ ?>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</main>
|
</main>
|
|
@ -25,7 +25,6 @@
|
||||||
<th># of Volumes</th>
|
<th># of Volumes</th>
|
||||||
<th>Attributes</th>
|
<th>Attributes</th>
|
||||||
<th>Type</th>
|
<th>Type</th>
|
||||||
<th>Genres</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -70,9 +69,6 @@
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
<td><?= $item['manga']['type'] ?></td>
|
<td><?= $item['manga']['type'] ?></td>
|
||||||
<td class="align-left">
|
|
||||||
<?= implode(', ', $item['manga']['genres']) ?>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
<?php
|
|
||||||
use function Aviat\AnimeClient\getLocalImg;
|
|
||||||
use Aviat\AnimeClient\API\Kitsu;
|
|
||||||
?>
|
|
||||||
<h3>Voice Acting Roles</h3>
|
|
||||||
<div class="tabs">
|
|
||||||
<?php $i = 0; ?>
|
|
||||||
<?php foreach($data['characters'] as $role => $characterList): ?>
|
|
||||||
<input <?= $i === 0 ? 'checked="checked"' : '' ?> type="radio" name="character-type-tabs" id="character-type-<?= $i ?>" />
|
|
||||||
<label for="character-type-<?= $i ?>"><h5><?= ucfirst($role) ?></h5></label>
|
|
||||||
<section class="content">
|
|
||||||
<table class="borderless max-table">
|
|
||||||
<tr>
|
|
||||||
<th>Character</th>
|
|
||||||
<th>Series</th>
|
|
||||||
</tr>
|
|
||||||
<?php foreach ($characterList as $cid => $character): ?>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<article class="character">
|
|
||||||
<?php
|
|
||||||
$link = $url->generate('character', ['slug' => $character['character']['slug']]);
|
|
||||||
?>
|
|
||||||
<a href="<?= $link ?>">
|
|
||||||
<?php $imgPath = ($character['character']['image'] === NULL)
|
|
||||||
? 'images/characters/empty.png'
|
|
||||||
: getLocalImg($character['character']['image']['original']);
|
|
||||||
|
|
||||||
echo $helper->picture($imgPath);
|
|
||||||
?>
|
|
||||||
<div class="name">
|
|
||||||
<?= $character['character']['canonicalName'] ?>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</article>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<section class="align-left media-wrap">
|
|
||||||
<?php foreach ($character['media'] as $sid => $series): ?>
|
|
||||||
<article class="media">
|
|
||||||
<?php
|
|
||||||
$link = $url->generate('anime.details', ['id' => $series['slug']]);
|
|
||||||
$titles = Kitsu::filterTitles($series);
|
|
||||||
?>
|
|
||||||
<a href="<?= $link ?>">
|
|
||||||
<?= $helper->picture("images/anime/{$sid}.webp") ?>
|
|
||||||
</a>
|
|
||||||
<div class="name">
|
|
||||||
<a href="<?= $link ?>">
|
|
||||||
<?= array_shift($titles) ?>
|
|
||||||
<?php foreach ($titles as $title): ?>
|
|
||||||
<br />
|
|
||||||
<small><?= $title ?></small>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</section>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</table>
|
|
||||||
</section>
|
|
||||||
<?php $i++ ?>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</div>
|
|
|
@ -1,7 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
use function Aviat\AnimeClient\getLocalImg;
|
||||||
use Aviat\AnimeClient\API\Kitsu;
|
|
||||||
|
|
||||||
?>
|
?>
|
||||||
<main class="details fixed">
|
<main class="details fixed">
|
||||||
<section class="flex flex-no-wrap">
|
<section class="flex flex-no-wrap">
|
||||||
|
@ -10,12 +8,21 @@ use Aviat\AnimeClient\API\Kitsu;
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2 class="toph"><?= $data['name'] ?></h2>
|
<h2 class="toph"><?= $data['name'] ?></h2>
|
||||||
|
<?php foreach ($data['names'] as $name): ?>
|
||||||
|
<h3><?= $name ?></h3>
|
||||||
|
<?php endforeach ?>
|
||||||
|
<br />
|
||||||
|
<hr />
|
||||||
|
<div class="description">
|
||||||
|
<p><?= str_replace("\n", '</p><p>', $data['description']) ?></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<?php if ( ! empty($data['staff'])): ?>
|
<?php if ( ! empty($data['staff'])): ?>
|
||||||
<section>
|
<section>
|
||||||
<h3>Castings</h3>
|
<h3>Castings</h3>
|
||||||
|
|
||||||
<div class="vertical-tabs">
|
<div class="vertical-tabs">
|
||||||
<?php $i = 0 ?>
|
<?php $i = 0 ?>
|
||||||
<?php foreach ($data['staff'] as $role => $entries): ?>
|
<?php foreach ($data['staff'] as $role => $entries): ?>
|
||||||
|
@ -24,31 +31,17 @@ use Aviat\AnimeClient\API\Kitsu;
|
||||||
type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
|
type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
|
||||||
<label for="staff-role<?= $i ?>"><?= $role ?></label>
|
<label for="staff-role<?= $i ?>"><?= $role ?></label>
|
||||||
<?php foreach ($entries as $type => $casting): ?>
|
<?php foreach ($entries as $type => $casting): ?>
|
||||||
<?php if ($type === 'characters') continue; ?>
|
<?php if (isset($entries['manga'], $entries['anime'])): ?>
|
||||||
<?php if ( ! (empty($entries['manga']) || empty($entries['anime']))): ?>
|
|
||||||
<h4><?= ucfirst($type) ?></h4>
|
<h4><?= ucfirst($type) ?></h4>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<section class="content">
|
<section class="content media-wrap flex flex-wrap flex-justify-start">
|
||||||
<?php foreach ($casting as $sid => $series): ?>
|
<?php foreach ($casting as $sid => $series): ?>
|
||||||
<article class="media">
|
<?php $mediaType = in_array($type, ['anime', 'manga'], TRUE) ? $type : 'anime'; ?>
|
||||||
<?php
|
<?= $component->media(
|
||||||
$mediaType = in_array($type, ['anime', 'manga'], TRUE) ? $type : 'anime';
|
$series['titles'],
|
||||||
$link = $url->generate("{$mediaType}.details", ['id' => $series['slug']]);
|
$url->generate("{$mediaType}.details", ['id' => $series['slug']]),
|
||||||
$titles = Kitsu::filterTitles($series);
|
$helper->picture("images/{$type}/{$sid}.webp")
|
||||||
?>
|
) ?>
|
||||||
<a href="<?= $link ?>">
|
|
||||||
<?= $helper->picture("images/{$type}/{$sid}.webp") ?>
|
|
||||||
</a>
|
|
||||||
<div class="name">
|
|
||||||
<a href="<?= $link ?>">
|
|
||||||
<?= array_shift($titles) ?>
|
|
||||||
<?php foreach ($titles as $title): ?>
|
|
||||||
<br />
|
|
||||||
<small><?= $title ?></small>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</section>
|
</section>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
|
@ -59,9 +52,53 @@ use Aviat\AnimeClient\API\Kitsu;
|
||||||
</section>
|
</section>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<?php if ( ! (empty($data['characters']['main']) || empty($data['characters']['supporting']))): ?>
|
<?php if ( ! empty($data['characters'])): ?>
|
||||||
<section>
|
<section>
|
||||||
<?php include 'character-mapping.php' ?>
|
<h3>Voice Acting Roles</h3>
|
||||||
|
<?= $component->tabs('voice-acting-roles', $data['characters'], static function ($characterList) use ($component, $helper, $url) {
|
||||||
|
$voiceRoles = [];
|
||||||
|
foreach ($characterList as $cid => $item):
|
||||||
|
$character = $component->character(
|
||||||
|
$item['character']['canonicalName'],
|
||||||
|
$url->generate('character', ['slug' => $item['character']['slug']]),
|
||||||
|
$helper->picture(getLocalImg($item['character']['image']['original'] ?? null))
|
||||||
|
);
|
||||||
|
$medias = [];
|
||||||
|
foreach ($item['media'] as $sid => $series)
|
||||||
|
{
|
||||||
|
$medias[] = $component->media(
|
||||||
|
$series['titles'],
|
||||||
|
$url->generate('anime.details', ['id' => $series['slug']]),
|
||||||
|
$helper->picture("images/anime/{$sid}.webp")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$media = implode('', array_map('mb_trim', $medias));
|
||||||
|
|
||||||
|
$voiceRoles[] = <<<HTML
|
||||||
|
<tr>
|
||||||
|
<td>{$character}</td>
|
||||||
|
<td>
|
||||||
|
<section class="align-left media-wrap">{$media}</section>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
HTML;
|
||||||
|
endforeach;
|
||||||
|
|
||||||
|
$roles = implode('', array_map('mb_trim', $voiceRoles));
|
||||||
|
|
||||||
|
return <<<HTML
|
||||||
|
<table class="borderless max-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Character</th>
|
||||||
|
<th>Series</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>{$roles}</tbody>
|
||||||
|
</table>
|
||||||
|
HTML;
|
||||||
|
|
||||||
|
}) ?>
|
||||||
</section>
|
</section>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -28,9 +28,7 @@ $nestedPrefix = 'config';
|
||||||
/>
|
/>
|
||||||
<label for="settings-tab<?= $i ?>"><h3><?= $sectionMapping[$section] ?></h3></label>
|
<label for="settings-tab<?= $i ?>"><h3><?= $sectionMapping[$section] ?></h3></label>
|
||||||
<section class="content">
|
<section class="content">
|
||||||
<?php require __DIR__ . '/_form.php' ?>
|
|
||||||
<?php if ($section === 'anilist'): ?>
|
<?php if ($section === 'anilist'): ?>
|
||||||
<hr />
|
|
||||||
<?php $auth = $anilistModel->checkAuth(); ?>
|
<?php $auth = $anilistModel->checkAuth(); ?>
|
||||||
<?php if (array_key_exists('errors', $auth)): ?>
|
<?php if (array_key_exists('errors', $auth)): ?>
|
||||||
<p class="static-message error">Not Authorized.</p>
|
<p class="static-message error">Not Authorized.</p>
|
||||||
|
@ -43,11 +41,15 @@ $nestedPrefix = 'config';
|
||||||
<p class="static-message info">
|
<p class="static-message info">
|
||||||
Linked to Anilist. Your access token will expire around <?= date('F j, Y, g:i a T', $expires) ?>
|
Linked to Anilist. Your access token will expire around <?= date('F j, Y, g:i a T', $expires) ?>
|
||||||
</p>
|
</p>
|
||||||
|
<?php require __DIR__ . '/_form.php' ?>
|
||||||
<?= $helper->a(
|
<?= $helper->a(
|
||||||
$url->generate('anilist-redirect'),
|
$url->generate('anilist-redirect'),
|
||||||
'Update Access Token'
|
'Update Access Token',
|
||||||
) ?>
|
['class' => 'bracketed user-btn']
|
||||||
|
) ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php require __DIR__ . '/_form.php' ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</section>
|
</section>
|
||||||
<?php $i++; ?>
|
<?php $i++; ?>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
use Aviat\AnimeClient\API\Kitsu;
|
use Aviat\AnimeClient\Kitsu;
|
||||||
?>
|
?>
|
||||||
<main class="user-page details">
|
<main class="user-page details">
|
||||||
<h2 class="toph">
|
<h2 class="toph">
|
||||||
|
@ -36,7 +36,7 @@ use Aviat\AnimeClient\API\Kitsu;
|
||||||
$character = $data['waifu']['character'];
|
$character = $data['waifu']['character'];
|
||||||
echo $helper->a(
|
echo $helper->a(
|
||||||
$url->generate('character', ['slug' => $character['slug']]),
|
$url->generate('character', ['slug' => $character['slug']]),
|
||||||
$character['canonicalName']
|
$character['names']['canonical']
|
||||||
);
|
);
|
||||||
?>
|
?>
|
||||||
</td>
|
</td>
|
||||||
|
@ -57,79 +57,43 @@ use Aviat\AnimeClient\API\Kitsu;
|
||||||
<article>
|
<article>
|
||||||
<?php if ( ! empty($data['favorites'])): ?>
|
<?php if ( ! empty($data['favorites'])): ?>
|
||||||
<h3>Favorites</h3>
|
<h3>Favorites</h3>
|
||||||
<div class="tabs">
|
<?= $component->tabs('user-favorites', $data['favorites'], static function ($items, $type) use ($component, $helper, $url) {
|
||||||
<?php $i = 0 ?>
|
$rendered = [];
|
||||||
<?php if ( ! empty($data['favorites']['characters'])): ?>
|
if ($type === 'character')
|
||||||
<input type="radio" name="user-favorites" id="user-fav-chars" <?= $i === 0 ? 'checked' : '' ?> />
|
{
|
||||||
<label for="user-fav-chars">Characters</label>
|
uasort($items, fn ($a, $b) => $a['names']['canonical'] <=> $b['names']['canonical']);
|
||||||
<section class="content full-width media-wrap">
|
}
|
||||||
<?php foreach($data['favorites']['characters'] as $id => $char): ?>
|
else
|
||||||
<?php if ( ! empty($char['image']['original'])): ?>
|
{
|
||||||
<article class="character">
|
uasort($items, fn ($a, $b) => $a['titles']['canonical'] <=> $b['titles']['canonical']);
|
||||||
<?php $link = $url->generate('character', ['slug' => $char['slug']]) ?>
|
}
|
||||||
<div class="name"><?= $helper->a($link, $char['canonicalName']); ?></div>
|
|
||||||
<a href="<?= $link ?>">
|
foreach ($items as $id => $item)
|
||||||
<?= $helper->picture("images/characters/{$char['id']}.webp") ?>
|
{
|
||||||
</a>
|
if ($type === 'character')
|
||||||
</article>
|
{
|
||||||
<?php endif ?>
|
$rendered[] = $component->character(
|
||||||
<?php endforeach ?>
|
$item['names']['canonical'],
|
||||||
</section>
|
$url->generate('character', ['slug' => $item['slug']]),
|
||||||
<?php $i++; ?>
|
$helper->picture("images/characters/{$item['id']}.webp")
|
||||||
<?php endif ?>
|
);
|
||||||
<?php if ( ! empty($data['favorites']['anime'])): ?>
|
}
|
||||||
<input type="radio" name="user-favorites" id="user-fav-anime" <?= $i === 0 ? 'checked' : '' ?> />
|
else
|
||||||
<label for="user-fav-anime">Anime</label>
|
{
|
||||||
<section class="content full-width media-wrap">
|
$rendered[] = $component->media(
|
||||||
<?php foreach($data['favorites']['anime'] as $anime): ?>
|
array_merge(
|
||||||
<article class="media">
|
[$item['titles']['canonical']],
|
||||||
<?php
|
Kitsu::getFilteredTitles($item['titles']),
|
||||||
$link = $url->generate('anime.details', ['id' => $anime['slug']]);
|
),
|
||||||
$titles = Kitsu::filterTitles($anime);
|
$url->generate("{$type}.details", ['id' => $item['slug']]),
|
||||||
?>
|
$helper->picture("images/{$type}/{$item['id']}.webp"),
|
||||||
<a href="<?= $link ?>">
|
);
|
||||||
<?= $helper->picture("images/anime/{$anime['id']}.webp") ?>
|
}
|
||||||
</a>
|
}
|
||||||
<div class="name">
|
|
||||||
<a href="<?= $link ?>">
|
return implode('', array_map('mb_trim', $rendered));
|
||||||
<?= array_shift($titles) ?>
|
|
||||||
<?php foreach ($titles as $title): ?>
|
}, 'content full-width media-wrap') ?>
|
||||||
<br /><small><?= $title ?></small>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</section>
|
|
||||||
<?php $i++; ?>
|
|
||||||
<?php endif ?>
|
|
||||||
<?php if ( ! empty($data['favorites']['manga'])): ?>
|
|
||||||
<input type="radio" name="user-favorites" id="user-fav-manga" <?= $i === 0 ? 'checked' : '' ?> />
|
|
||||||
<label for="user-fav-manga">Manga</label>
|
|
||||||
<section class="content full-width media-wrap">
|
|
||||||
<?php foreach($data['favorites']['manga'] as $manga): ?>
|
|
||||||
<article class="media">
|
|
||||||
<?php
|
|
||||||
$link = $url->generate('manga.details', ['id' => $manga['slug']]);
|
|
||||||
$titles = Kitsu::filterTitles($manga);
|
|
||||||
?>
|
|
||||||
<a href="<?= $link ?>">
|
|
||||||
<?= $helper->picture("images/manga/{$manga['id']}.webp") ?>
|
|
||||||
</a>
|
|
||||||
<div class="name">
|
|
||||||
<a href="<?= $link ?>">
|
|
||||||
<?= array_shift($titles) ?>
|
|
||||||
<?php foreach ($titles as $title): ?>
|
|
||||||
<br /><small><?= $title ?></small>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</section>
|
|
||||||
<?php $i++; ?>
|
|
||||||
<?php endif ?>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"amphp/http-client": "^4.2.2",
|
"amphp/amp": "^2.5.0",
|
||||||
|
"amphp/http-client": "^4.5.0",
|
||||||
"aura/html": "^2.5.0",
|
"aura/html": "^2.5.0",
|
||||||
"aura/router": "^3.1.0",
|
"aura/router": "^3.1.0",
|
||||||
"aura/session": "^2.1.0",
|
"aura/session": "^2.1.0",
|
||||||
|
@ -43,6 +44,7 @@
|
||||||
"danielstjules/stringy": "^3.1.0",
|
"danielstjules/stringy": "^3.1.0",
|
||||||
"ext-dom": "*",
|
"ext-dom": "*",
|
||||||
"ext-iconv": "*",
|
"ext-iconv": "*",
|
||||||
|
"ext-intl": "*",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"ext-gd": "*",
|
"ext-gd": "*",
|
||||||
"ext-pdo": "*",
|
"ext-pdo": "*",
|
||||||
|
@ -65,7 +67,7 @@
|
||||||
"phpstan/phpstan": "^0.12.19",
|
"phpstan/phpstan": "^0.12.19",
|
||||||
"phpunit/phpunit": "^8.5.2",
|
"phpunit/phpunit": "^8.5.2",
|
||||||
"roave/security-advisories": "dev-master",
|
"roave/security-advisories": "dev-master",
|
||||||
"robmorgan/phinx": "^0.10.6",
|
"robmorgan/phinx": "^0.12.4",
|
||||||
"sebastian/phpcpd": "^4.1.0",
|
"sebastian/phpcpd": "^4.1.0",
|
||||||
"spatie/phpunit-snapshot-assertions": "^4.1.0",
|
"spatie/phpunit-snapshot-assertions": "^4.1.0",
|
||||||
"squizlabs/php_codesniffer": "^3.5.4",
|
"squizlabs/php_codesniffer": "^3.5.4",
|
||||||
|
|
3
console
3
console
|
@ -9,6 +9,9 @@ use ConsoleKit\Console;
|
||||||
|
|
||||||
$_SERVER['HTTP_HOST'] = 'localhost';
|
$_SERVER['HTTP_HOST'] = 'localhost';
|
||||||
|
|
||||||
|
define('APP_DIR', __DIR__ . '/app');
|
||||||
|
define('TEMPLATE_DIR', APP_DIR . '/templates');
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Start console script
|
// Start console script
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
|
@ -163,7 +163,7 @@ CSS Tabs
|
||||||
/* text-align: center; */
|
/* text-align: center; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs .content {
|
.tabs .content, .single-tab {
|
||||||
display: none;
|
display: none;
|
||||||
max-height: 950px;
|
max-height: 950px;
|
||||||
border: 1px solid #e5e5e5;
|
border: 1px solid #e5e5e5;
|
||||||
|
@ -175,7 +175,14 @@ CSS Tabs
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs .content.full-height {
|
.single-tab {
|
||||||
|
display: block;
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
box-shadow: 0 48px 80px -32px rgba(0, 0, 0, 0.3);
|
||||||
|
margin-top: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs .content.full-height, .single-tab.full-height {
|
||||||
max-height: none;
|
max-height: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,8 @@ button:active {
|
||||||
.tabs > [type="radio"]:checked + label,
|
.tabs > [type="radio"]:checked + label,
|
||||||
.tabs > [type="radio"]:checked + label + .content,
|
.tabs > [type="radio"]:checked + label + .content,
|
||||||
.vertical-tabs [type="radio"]:checked + label,
|
.vertical-tabs [type="radio"]:checked + label,
|
||||||
.vertical-tabs [type="radio"]:checked ~ .content {
|
.vertical-tabs [type="radio"]:checked ~ .content,
|
||||||
|
.single-tab {
|
||||||
/* border-color: #333; */
|
/* border-color: #333; */
|
||||||
border: 0;
|
border: 0;
|
||||||
background: #666;
|
background: #666;
|
||||||
|
|
|
@ -94,6 +94,7 @@ a:hover, a:active {
|
||||||
iframe {
|
iframe {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
/* -----------------------------------------------------------------------------
|
||||||
|
|
|
@ -261,7 +261,7 @@ function ajaxSerialize(data) {
|
||||||
*
|
*
|
||||||
* @param {string} url - the url to request
|
* @param {string} url - the url to request
|
||||||
* @param {Object} config - the configuration object
|
* @param {Object} config - the configuration object
|
||||||
* @return {void}
|
* @return {XMLHttpRequest}
|
||||||
*/
|
*/
|
||||||
AnimeClient.ajax = (url, config) => {
|
AnimeClient.ajax = (url, config) => {
|
||||||
// Set some sane defaults
|
// Set some sane defaults
|
||||||
|
@ -322,6 +322,8 @@ AnimeClient.ajax = (url, config) => {
|
||||||
} else {
|
} else {
|
||||||
request.send(config.data);
|
request.send(config.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return request
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -330,6 +332,7 @@ AnimeClient.ajax = (url, config) => {
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @param {object|function} data
|
* @param {object|function} data
|
||||||
* @param {function} [callback]
|
* @param {function} [callback]
|
||||||
|
* @return {XMLHttpRequest}
|
||||||
*/
|
*/
|
||||||
AnimeClient.get = (url, data, callback = null) => {
|
AnimeClient.get = (url, data, callback = null) => {
|
||||||
if (callback === null) {
|
if (callback === null) {
|
||||||
|
|
|
@ -6,25 +6,31 @@ const search = (query) => {
|
||||||
_.show('.cssload-loader');
|
_.show('.cssload-loader');
|
||||||
|
|
||||||
// Do the api search
|
// Do the api search
|
||||||
_.get(_.url('/anime-collection/search'), { query }, (searchResults, status) => {
|
return _.get(_.url('/anime-collection/search'), { query }, (searchResults, status) => {
|
||||||
searchResults = JSON.parse(searchResults);
|
searchResults = JSON.parse(searchResults);
|
||||||
|
|
||||||
// Hide the loader
|
// Hide the loader
|
||||||
_.hide('.cssload-loader');
|
_.hide('.cssload-loader');
|
||||||
|
|
||||||
// Show the results
|
// Show the results
|
||||||
_.$('#series-list')[ 0 ].innerHTML = renderAnimeSearchResults(searchResults.data);
|
_.$('#series-list')[ 0 ].innerHTML = renderAnimeSearchResults(searchResults);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (_.hasElement('.anime #search')) {
|
if (_.hasElement('.anime #search')) {
|
||||||
|
let prevRequest = null;
|
||||||
|
|
||||||
_.on('#search', 'input', _.throttle(250, (e) => {
|
_.on('#search', 'input', _.throttle(250, (e) => {
|
||||||
const query = encodeURIComponent(e.target.value);
|
const query = encodeURIComponent(e.target.value);
|
||||||
if (query === '') {
|
if (query === '') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
search(query);
|
if (prevRequest !== null) {
|
||||||
|
prevRequest.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
prevRequest = search(query);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,12 +53,12 @@ _.on('body.anime.list', 'click', '.plus-one', (e) => {
|
||||||
// If the episode count is 0, and incremented,
|
// If the episode count is 0, and incremented,
|
||||||
// change status to currently watching
|
// change status to currently watching
|
||||||
if (isNaN(watchedCount) || watchedCount === 0) {
|
if (isNaN(watchedCount) || watchedCount === 0) {
|
||||||
data.data.status = 'current';
|
data.data.status = 'CURRENT';
|
||||||
}
|
}
|
||||||
|
|
||||||
// If you increment at the last episode, mark as completed
|
// If you increment at the last episode, mark as completed
|
||||||
if ((!isNaN(watchedCount)) && (watchedCount + 1) === totalCount) {
|
if ((!isNaN(watchedCount)) && (watchedCount + 1) === totalCount) {
|
||||||
data.data.status = 'completed';
|
data.data.status = 'COMPLETED';
|
||||||
}
|
}
|
||||||
|
|
||||||
_.show('#loading-shadow');
|
_.show('#loading-shadow');
|
||||||
|
@ -72,7 +78,7 @@ _.on('body.anime.list', 'click', '.plus-one', (e) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resData.data.attributes.status === 'completed') {
|
if (resData.data.libraryEntry.update.libraryEntry.status === 'COMPLETED') {
|
||||||
_.hide(parentSel);
|
_.hide(parentSel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import './anon.js';
|
import './anon.js';
|
||||||
|
|
||||||
|
import './session-check.js';
|
||||||
import './anime.js';
|
import './anime.js';
|
||||||
import './manga.js';
|
import './manga.js';
|
||||||
|
|
|
@ -3,21 +3,27 @@ import { renderMangaSearchResults } from './template-helpers.js'
|
||||||
|
|
||||||
const search = (query) => {
|
const search = (query) => {
|
||||||
_.show('.cssload-loader');
|
_.show('.cssload-loader');
|
||||||
_.get(_.url('/manga/search'), { query }, (searchResults, status) => {
|
return _.get(_.url('/manga/search'), { query }, (searchResults, status) => {
|
||||||
searchResults = JSON.parse(searchResults);
|
searchResults = JSON.parse(searchResults);
|
||||||
_.hide('.cssload-loader');
|
_.hide('.cssload-loader');
|
||||||
_.$('#series-list')[ 0 ].innerHTML = renderMangaSearchResults(searchResults.data);
|
_.$('#series-list')[ 0 ].innerHTML = renderMangaSearchResults(searchResults);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (_.hasElement('.manga #search')) {
|
if (_.hasElement('.manga #search')) {
|
||||||
|
let prevRequest = null
|
||||||
|
|
||||||
_.on('#search', 'input', _.throttle(250, (e) => {
|
_.on('#search', 'input', _.throttle(250, (e) => {
|
||||||
let query = encodeURIComponent(e.target.value);
|
let query = encodeURIComponent(e.target.value);
|
||||||
if (query === '') {
|
if (query === '') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
search(query);
|
if (prevRequest !== null) {
|
||||||
|
prevRequest.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
prevRequest = search(query);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,12 +54,12 @@ _.on('.manga.list', 'click', '.edit-buttons button', (e) => {
|
||||||
// If the episode count is 0, and incremented,
|
// If the episode count is 0, and incremented,
|
||||||
// change status to currently reading
|
// change status to currently reading
|
||||||
if (isNaN(completed) || completed === 0) {
|
if (isNaN(completed) || completed === 0) {
|
||||||
data.data.status = 'current';
|
data.data.status = 'CURRENT';
|
||||||
}
|
}
|
||||||
|
|
||||||
// If you increment at the last chapter, mark as completed
|
// If you increment at the last chapter, mark as completed
|
||||||
if ((!isNaN(completed)) && (completed + 1) === total) {
|
if ((!isNaN(completed)) && (completed + 1) === total) {
|
||||||
data.data.status = 'completed';
|
data.data.status = 'COMPLETED';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the total count
|
// Update the total count
|
||||||
|
@ -67,7 +73,7 @@ _.on('.manga.list', 'click', '.edit-buttons button', (e) => {
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
mimeType: 'application/json',
|
mimeType: 'application/json',
|
||||||
success: () => {
|
success: () => {
|
||||||
if (data.data.status === 'completed') {
|
if (String(data.data.status).toUpperCase() === 'COMPLETED') {
|
||||||
_.hide(parentSel);
|
_.hide(parentSel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
import _ from './anime-client.js';
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
// Var is intentional
|
||||||
|
var hidden = null;
|
||||||
|
var visibilityChange = null;
|
||||||
|
|
||||||
|
if (typeof document.hidden !== "undefined") {
|
||||||
|
hidden = "hidden";
|
||||||
|
visibilityChange = "visibilitychange";
|
||||||
|
} else if (typeof document.msHidden !== "undefined") {
|
||||||
|
hidden = "msHidden";
|
||||||
|
visibilityChange = "msvisibilitychange";
|
||||||
|
} else if (typeof document.webkitHidden !== "undefined") {
|
||||||
|
hidden = "webkitHidden";
|
||||||
|
visibilityChange = "webkitvisibilitychange";
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleVisibilityChange() {
|
||||||
|
// Check the user's session to see if they are currently logged-in
|
||||||
|
// when the page becomes visible
|
||||||
|
if ( ! document[hidden]) {
|
||||||
|
_.get('/heartbeat', (beat) => {
|
||||||
|
const status = JSON.parse(beat)
|
||||||
|
|
||||||
|
// If the session is expired, immediately reload so that
|
||||||
|
// you can't attempt to do an action that requires authentication
|
||||||
|
if (status.hasAuth !== true) {
|
||||||
|
document.removeEventListener(visibilityChange, handleVisibilityChange, false);
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hidden === null) {
|
||||||
|
console.info('Page visibility API not supported, JS session check will not work');
|
||||||
|
} else {
|
||||||
|
document.addEventListener(visibilityChange, handleVisibilityChange, false);
|
||||||
|
}
|
||||||
|
})();
|
|
@ -10,20 +10,19 @@ _.on('main', 'change', '.big-check', (e) => {
|
||||||
export function renderAnimeSearchResults (data) {
|
export function renderAnimeSearchResults (data) {
|
||||||
const results = [];
|
const results = [];
|
||||||
|
|
||||||
data.forEach(x => {
|
data.forEach(item => {
|
||||||
const item = x.attributes;
|
|
||||||
const titles = item.titles.join('<br />');
|
const titles = item.titles.join('<br />');
|
||||||
|
|
||||||
results.push(`
|
results.push(`
|
||||||
<article class="media search">
|
<article class="media search">
|
||||||
<div class="name">
|
<div class="name">
|
||||||
<input type="radio" class="mal-check" id="mal_${item.slug}" name="mal_id" value="${x.mal_id}" />
|
<input type="radio" class="mal-check" id="mal_${item.slug}" name="mal_id" value="${item.mal_id}" />
|
||||||
<input type="radio" class="big-check" id="${item.slug}" name="id" value="${x.id}" />
|
<input type="radio" class="big-check" id="${item.slug}" name="id" value="${item.id}" />
|
||||||
<label for="${item.slug}">
|
<label for="${item.slug}">
|
||||||
<picture width="220">
|
<picture width="220">
|
||||||
<source srcset="/public/images/anime/${x.id}.webp" type="image/webp" />
|
<source srcset="/public/images/anime/${item.id}.webp" type="image/webp" />
|
||||||
<source srcset="/public/images/anime/${x.id}.jpg" type="image/jpeg" />
|
<source srcset="/public/images/anime/${item.id}.jpg" type="image/jpeg" />
|
||||||
<img src="/public/images/anime/${x.id}.jpg" alt="" width="220" />
|
<img src="/public/images/anime/${item.id}.jpg" alt="" width="220" />
|
||||||
</picture>
|
</picture>
|
||||||
<span class="name">
|
<span class="name">
|
||||||
${item.canonicalTitle}<br />
|
${item.canonicalTitle}<br />
|
||||||
|
@ -48,20 +47,19 @@ export function renderAnimeSearchResults (data) {
|
||||||
export function renderMangaSearchResults (data) {
|
export function renderMangaSearchResults (data) {
|
||||||
const results = [];
|
const results = [];
|
||||||
|
|
||||||
data.forEach(x => {
|
data.forEach(item => {
|
||||||
const item = x.attributes;
|
|
||||||
const titles = item.titles.join('<br />');
|
const titles = item.titles.join('<br />');
|
||||||
|
|
||||||
results.push(`
|
results.push(`
|
||||||
<article class="media search">
|
<article class="media search">
|
||||||
<div class="name">
|
<div class="name">
|
||||||
<input type="radio" id="mal_${item.slug}" name="mal_id" value="${x.mal_id}" />
|
<input type="radio" id="mal_${item.slug}" name="mal_id" value="${item.mal_id}" />
|
||||||
<input type="radio" class="big-check" id="${item.slug}" name="id" value="${x.id}" />
|
<input type="radio" class="big-check" id="${item.slug}" name="id" value="${item.id}" />
|
||||||
<label for="${item.slug}">
|
<label for="${item.slug}">
|
||||||
<picture width="220">
|
<picture width="220">
|
||||||
<source srcset="/public/images/manga/${x.id}.webp" type="image/webp" />
|
<source srcset="/public/images/manga/${item.id}.webp" type="image/webp" />
|
||||||
<source srcset="/public/images/manga/${x.id}.jpg" type="image/jpeg" />
|
<source srcset="/public/images/manga/${item.id}.jpg" type="image/jpeg" />
|
||||||
<img src="/public/images/manga/${x.id}.jpg" alt="" width="220" />
|
<img src="/public/images/manga/${item.id}.jpg" alt="" width="220" />
|
||||||
</picture>
|
</picture>
|
||||||
<span class="name">
|
<span class="name">
|
||||||
${item.canonicalTitle}<br />
|
${item.canonicalTitle}<br />
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -260,7 +260,7 @@ function ajaxSerialize(data) {
|
||||||
*
|
*
|
||||||
* @param {string} url - the url to request
|
* @param {string} url - the url to request
|
||||||
* @param {Object} config - the configuration object
|
* @param {Object} config - the configuration object
|
||||||
* @return {void}
|
* @return {XMLHttpRequest}
|
||||||
*/
|
*/
|
||||||
AnimeClient.ajax = (url, config) => {
|
AnimeClient.ajax = (url, config) => {
|
||||||
// Set some sane defaults
|
// Set some sane defaults
|
||||||
|
@ -321,6 +321,8 @@ AnimeClient.ajax = (url, config) => {
|
||||||
} else {
|
} else {
|
||||||
request.send(config.data);
|
request.send(config.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return request
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -329,6 +331,7 @@ AnimeClient.ajax = (url, config) => {
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @param {object|function} data
|
* @param {object|function} data
|
||||||
* @param {function} [callback]
|
* @param {function} [callback]
|
||||||
|
* @return {XMLHttpRequest}
|
||||||
*/
|
*/
|
||||||
AnimeClient.get = (url, data, callback = null) => {
|
AnimeClient.get = (url, data, callback = null) => {
|
||||||
if (callback === null) {
|
if (callback === null) {
|
||||||
|
|
|
@ -260,7 +260,7 @@ function ajaxSerialize(data) {
|
||||||
*
|
*
|
||||||
* @param {string} url - the url to request
|
* @param {string} url - the url to request
|
||||||
* @param {Object} config - the configuration object
|
* @param {Object} config - the configuration object
|
||||||
* @return {void}
|
* @return {XMLHttpRequest}
|
||||||
*/
|
*/
|
||||||
AnimeClient.ajax = (url, config) => {
|
AnimeClient.ajax = (url, config) => {
|
||||||
// Set some sane defaults
|
// Set some sane defaults
|
||||||
|
@ -321,6 +321,8 @@ AnimeClient.ajax = (url, config) => {
|
||||||
} else {
|
} else {
|
||||||
request.send(config.data);
|
request.send(config.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return request
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -329,6 +331,7 @@ AnimeClient.ajax = (url, config) => {
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @param {object|function} data
|
* @param {object|function} data
|
||||||
* @param {function} [callback]
|
* @param {function} [callback]
|
||||||
|
* @return {XMLHttpRequest}
|
||||||
*/
|
*/
|
||||||
AnimeClient.get = (url, data, callback = null) => {
|
AnimeClient.get = (url, data, callback = null) => {
|
||||||
if (callback === null) {
|
if (callback === null) {
|
||||||
|
@ -459,6 +462,46 @@ if ('serviceWorker' in navigator) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
// Var is intentional
|
||||||
|
var hidden = null;
|
||||||
|
var visibilityChange = null;
|
||||||
|
|
||||||
|
if (typeof document.hidden !== "undefined") {
|
||||||
|
hidden = "hidden";
|
||||||
|
visibilityChange = "visibilitychange";
|
||||||
|
} else if (typeof document.msHidden !== "undefined") {
|
||||||
|
hidden = "msHidden";
|
||||||
|
visibilityChange = "msvisibilitychange";
|
||||||
|
} else if (typeof document.webkitHidden !== "undefined") {
|
||||||
|
hidden = "webkitHidden";
|
||||||
|
visibilityChange = "webkitvisibilitychange";
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleVisibilityChange() {
|
||||||
|
// Check the user's session to see if they are currently logged-in
|
||||||
|
// when the page becomes visible
|
||||||
|
if ( ! document[hidden]) {
|
||||||
|
AnimeClient.get('/heartbeat', (beat) => {
|
||||||
|
const status = JSON.parse(beat);
|
||||||
|
|
||||||
|
// If the session is expired, immediately reload so that
|
||||||
|
// you can't attempt to do an action that requires authentication
|
||||||
|
if (status.hasAuth !== true) {
|
||||||
|
document.removeEventListener(visibilityChange, handleVisibilityChange, false);
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hidden === null) {
|
||||||
|
console.info('Page visibility API not supported, JS session check will not work');
|
||||||
|
} else {
|
||||||
|
document.addEventListener(visibilityChange, handleVisibilityChange, false);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
// Click on hidden MAL checkbox so
|
// Click on hidden MAL checkbox so
|
||||||
// that MAL id is passed
|
// that MAL id is passed
|
||||||
AnimeClient.on('main', 'change', '.big-check', (e) => {
|
AnimeClient.on('main', 'change', '.big-check', (e) => {
|
||||||
|
@ -469,20 +512,19 @@ AnimeClient.on('main', 'change', '.big-check', (e) => {
|
||||||
function renderAnimeSearchResults (data) {
|
function renderAnimeSearchResults (data) {
|
||||||
const results = [];
|
const results = [];
|
||||||
|
|
||||||
data.forEach(x => {
|
data.forEach(item => {
|
||||||
const item = x.attributes;
|
|
||||||
const titles = item.titles.join('<br />');
|
const titles = item.titles.join('<br />');
|
||||||
|
|
||||||
results.push(`
|
results.push(`
|
||||||
<article class="media search">
|
<article class="media search">
|
||||||
<div class="name">
|
<div class="name">
|
||||||
<input type="radio" class="mal-check" id="mal_${item.slug}" name="mal_id" value="${x.mal_id}" />
|
<input type="radio" class="mal-check" id="mal_${item.slug}" name="mal_id" value="${item.mal_id}" />
|
||||||
<input type="radio" class="big-check" id="${item.slug}" name="id" value="${x.id}" />
|
<input type="radio" class="big-check" id="${item.slug}" name="id" value="${item.id}" />
|
||||||
<label for="${item.slug}">
|
<label for="${item.slug}">
|
||||||
<picture width="220">
|
<picture width="220">
|
||||||
<source srcset="/public/images/anime/${x.id}.webp" type="image/webp" />
|
<source srcset="/public/images/anime/${item.id}.webp" type="image/webp" />
|
||||||
<source srcset="/public/images/anime/${x.id}.jpg" type="image/jpeg" />
|
<source srcset="/public/images/anime/${item.id}.jpg" type="image/jpeg" />
|
||||||
<img src="/public/images/anime/${x.id}.jpg" alt="" width="220" />
|
<img src="/public/images/anime/${item.id}.jpg" alt="" width="220" />
|
||||||
</picture>
|
</picture>
|
||||||
<span class="name">
|
<span class="name">
|
||||||
${item.canonicalTitle}<br />
|
${item.canonicalTitle}<br />
|
||||||
|
@ -507,20 +549,19 @@ function renderAnimeSearchResults (data) {
|
||||||
function renderMangaSearchResults (data) {
|
function renderMangaSearchResults (data) {
|
||||||
const results = [];
|
const results = [];
|
||||||
|
|
||||||
data.forEach(x => {
|
data.forEach(item => {
|
||||||
const item = x.attributes;
|
|
||||||
const titles = item.titles.join('<br />');
|
const titles = item.titles.join('<br />');
|
||||||
|
|
||||||
results.push(`
|
results.push(`
|
||||||
<article class="media search">
|
<article class="media search">
|
||||||
<div class="name">
|
<div class="name">
|
||||||
<input type="radio" id="mal_${item.slug}" name="mal_id" value="${x.mal_id}" />
|
<input type="radio" id="mal_${item.slug}" name="mal_id" value="${item.mal_id}" />
|
||||||
<input type="radio" class="big-check" id="${item.slug}" name="id" value="${x.id}" />
|
<input type="radio" class="big-check" id="${item.slug}" name="id" value="${item.id}" />
|
||||||
<label for="${item.slug}">
|
<label for="${item.slug}">
|
||||||
<picture width="220">
|
<picture width="220">
|
||||||
<source srcset="/public/images/manga/${x.id}.webp" type="image/webp" />
|
<source srcset="/public/images/manga/${item.id}.webp" type="image/webp" />
|
||||||
<source srcset="/public/images/manga/${x.id}.jpg" type="image/jpeg" />
|
<source srcset="/public/images/manga/${item.id}.jpg" type="image/jpeg" />
|
||||||
<img src="/public/images/manga/${x.id}.jpg" alt="" width="220" />
|
<img src="/public/images/manga/${item.id}.jpg" alt="" width="220" />
|
||||||
</picture>
|
</picture>
|
||||||
<span class="name">
|
<span class="name">
|
||||||
${item.canonicalTitle}<br />
|
${item.canonicalTitle}<br />
|
||||||
|
@ -547,25 +588,31 @@ const search = (query) => {
|
||||||
AnimeClient.show('.cssload-loader');
|
AnimeClient.show('.cssload-loader');
|
||||||
|
|
||||||
// Do the api search
|
// Do the api search
|
||||||
AnimeClient.get(AnimeClient.url('/anime-collection/search'), { query }, (searchResults, status) => {
|
return AnimeClient.get(AnimeClient.url('/anime-collection/search'), { query }, (searchResults, status) => {
|
||||||
searchResults = JSON.parse(searchResults);
|
searchResults = JSON.parse(searchResults);
|
||||||
|
|
||||||
// Hide the loader
|
// Hide the loader
|
||||||
AnimeClient.hide('.cssload-loader');
|
AnimeClient.hide('.cssload-loader');
|
||||||
|
|
||||||
// Show the results
|
// Show the results
|
||||||
AnimeClient.$('#series-list')[ 0 ].innerHTML = renderAnimeSearchResults(searchResults.data);
|
AnimeClient.$('#series-list')[ 0 ].innerHTML = renderAnimeSearchResults(searchResults);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (AnimeClient.hasElement('.anime #search')) {
|
if (AnimeClient.hasElement('.anime #search')) {
|
||||||
|
let prevRequest = null;
|
||||||
|
|
||||||
AnimeClient.on('#search', 'input', AnimeClient.throttle(250, (e) => {
|
AnimeClient.on('#search', 'input', AnimeClient.throttle(250, (e) => {
|
||||||
const query = encodeURIComponent(e.target.value);
|
const query = encodeURIComponent(e.target.value);
|
||||||
if (query === '') {
|
if (query === '') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
search(query);
|
if (prevRequest !== null) {
|
||||||
|
prevRequest.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
prevRequest = search(query);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -588,12 +635,12 @@ AnimeClient.on('body.anime.list', 'click', '.plus-one', (e) => {
|
||||||
// If the episode count is 0, and incremented,
|
// If the episode count is 0, and incremented,
|
||||||
// change status to currently watching
|
// change status to currently watching
|
||||||
if (isNaN(watchedCount) || watchedCount === 0) {
|
if (isNaN(watchedCount) || watchedCount === 0) {
|
||||||
data.data.status = 'current';
|
data.data.status = 'CURRENT';
|
||||||
}
|
}
|
||||||
|
|
||||||
// If you increment at the last episode, mark as completed
|
// If you increment at the last episode, mark as completed
|
||||||
if ((!isNaN(watchedCount)) && (watchedCount + 1) === totalCount) {
|
if ((!isNaN(watchedCount)) && (watchedCount + 1) === totalCount) {
|
||||||
data.data.status = 'completed';
|
data.data.status = 'COMPLETED';
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimeClient.show('#loading-shadow');
|
AnimeClient.show('#loading-shadow');
|
||||||
|
@ -613,7 +660,7 @@ AnimeClient.on('body.anime.list', 'click', '.plus-one', (e) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resData.data.attributes.status === 'completed') {
|
if (resData.data.libraryEntry.update.libraryEntry.status === 'COMPLETED') {
|
||||||
AnimeClient.hide(parentSel);
|
AnimeClient.hide(parentSel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,21 +680,27 @@ AnimeClient.on('body.anime.list', 'click', '.plus-one', (e) => {
|
||||||
|
|
||||||
const search$1 = (query) => {
|
const search$1 = (query) => {
|
||||||
AnimeClient.show('.cssload-loader');
|
AnimeClient.show('.cssload-loader');
|
||||||
AnimeClient.get(AnimeClient.url('/manga/search'), { query }, (searchResults, status) => {
|
return AnimeClient.get(AnimeClient.url('/manga/search'), { query }, (searchResults, status) => {
|
||||||
searchResults = JSON.parse(searchResults);
|
searchResults = JSON.parse(searchResults);
|
||||||
AnimeClient.hide('.cssload-loader');
|
AnimeClient.hide('.cssload-loader');
|
||||||
AnimeClient.$('#series-list')[ 0 ].innerHTML = renderMangaSearchResults(searchResults.data);
|
AnimeClient.$('#series-list')[ 0 ].innerHTML = renderMangaSearchResults(searchResults);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (AnimeClient.hasElement('.manga #search')) {
|
if (AnimeClient.hasElement('.manga #search')) {
|
||||||
|
let prevRequest = null;
|
||||||
|
|
||||||
AnimeClient.on('#search', 'input', AnimeClient.throttle(250, (e) => {
|
AnimeClient.on('#search', 'input', AnimeClient.throttle(250, (e) => {
|
||||||
let query = encodeURIComponent(e.target.value);
|
let query = encodeURIComponent(e.target.value);
|
||||||
if (query === '') {
|
if (query === '') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
search$1(query);
|
if (prevRequest !== null) {
|
||||||
|
prevRequest.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
prevRequest = search$1(query);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -678,12 +731,12 @@ AnimeClient.on('.manga.list', 'click', '.edit-buttons button', (e) => {
|
||||||
// If the episode count is 0, and incremented,
|
// If the episode count is 0, and incremented,
|
||||||
// change status to currently reading
|
// change status to currently reading
|
||||||
if (isNaN(completed) || completed === 0) {
|
if (isNaN(completed) || completed === 0) {
|
||||||
data.data.status = 'current';
|
data.data.status = 'CURRENT';
|
||||||
}
|
}
|
||||||
|
|
||||||
// If you increment at the last chapter, mark as completed
|
// If you increment at the last chapter, mark as completed
|
||||||
if ((!isNaN(completed)) && (completed + 1) === total) {
|
if ((!isNaN(completed)) && (completed + 1) === total) {
|
||||||
data.data.status = 'completed';
|
data.data.status = 'COMPLETED';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the total count
|
// Update the total count
|
||||||
|
@ -697,7 +750,7 @@ AnimeClient.on('.manga.list', 'click', '.edit-buttons button', (e) => {
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
mimeType: 'application/json',
|
mimeType: 'application/json',
|
||||||
success: () => {
|
success: () => {
|
||||||
if (data.data.status === 'completed') {
|
if (String(data.data.status).toUpperCase() === 'COMPLETED') {
|
||||||
AnimeClient.hide(parentSel);
|
AnimeClient.hide(parentSel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 13"><path fill="#7cac3f" d="M7 0a7 7 0 0 0-7 7 7 7 0 0 0 7 6 7 7 0 0 0 6-6 7 7 0 0 0-6-7zM4 4l3 1 3 2-3 1-3 1V7z"/></svg>
|
<svg height="2500" viewBox="2.167 .438 251.038 259.969" width="2500" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="m221.503 210.324c-105.235 50.083-170.545 8.18-212.352-17.271-2.587-1.604-6.984.375-3.169 4.757 13.928 16.888 59.573 57.593 119.153 57.593 59.621 0 95.09-32.532 99.527-38.207 4.407-5.627 1.294-8.731-3.16-6.872zm29.555-16.322c-2.826-3.68-17.184-4.366-26.22-3.256-9.05 1.078-22.634 6.609-21.453 9.93.606 1.244 1.843.686 8.06.127 6.234-.622 23.698-2.826 27.337 1.931 3.656 4.79-5.57 27.608-7.255 31.288-1.628 3.68.622 4.629 3.68 2.178 3.016-2.45 8.476-8.795 12.14-17.774 3.639-9.028 5.858-21.622 3.71-24.424z" fill="#00A8E1" fill-rule="nonzero"/><path d="m150.744 108.13c0 13.141.332 24.1-6.31 35.77-5.361 9.489-13.853 15.324-23.341 15.324-12.952 0-20.495-9.868-20.495-24.432 0-28.75 25.76-33.968 50.146-33.968zm34.015 82.216c-2.23 1.992-5.456 2.135-7.97.806-11.196-9.298-13.189-13.615-19.356-22.487-18.502 18.882-31.596 24.527-55.601 24.527-28.37 0-50.478-17.506-50.478-52.565 0-27.373 14.85-46.018 35.96-55.126 18.313-8.066 43.884-9.489 63.43-11.718v-4.365c0-8.018.616-17.506-4.08-24.432-4.128-6.215-12.003-8.777-18.93-8.777-12.856 0-24.337 6.594-27.136 20.257-.57 3.037-2.799 6.026-5.835 6.168l-32.735-3.51c-2.751-.618-5.787-2.847-5.028-7.07 7.543-39.66 43.36-51.616 75.43-51.616 16.415 0 37.858 4.365 50.81 16.795 16.415 15.323 14.849 35.77 14.849 58.02v52.565c0 15.798 6.547 22.724 12.714 31.264 2.182 3.036 2.657 6.69-.095 8.966-6.879 5.74-19.119 16.415-25.855 22.393l-.095-.095" fill="#000"/><path d="m221.503 210.324c-105.235 50.083-170.545 8.18-212.352-17.271-2.587-1.604-6.984.375-3.169 4.757 13.928 16.888 59.573 57.593 119.153 57.593 59.621 0 95.09-32.532 99.527-38.207 4.407-5.627 1.294-8.731-3.16-6.872zm29.555-16.322c-2.826-3.68-17.184-4.366-26.22-3.256-9.05 1.078-22.634 6.609-21.453 9.93.606 1.244 1.843.686 8.06.127 6.234-.622 23.698-2.826 27.337 1.931 3.656 4.79-5.57 27.608-7.255 31.288-1.628 3.68.622 4.629 3.68 2.178 3.016-2.45 8.476-8.795 12.14-17.774 3.639-9.028 5.858-21.622 3.71-24.424z" fill="#00A8E1" fill-rule="nonzero"/><path d="m150.744 108.13c0 13.141.332 24.1-6.31 35.77-5.361 9.489-13.853 15.324-23.341 15.324-12.952 0-20.495-9.868-20.495-24.432 0-28.75 25.76-33.968 50.146-33.968zm34.015 82.216c-2.23 1.992-5.456 2.135-7.97.806-11.196-9.298-13.189-13.615-19.356-22.487-18.502 18.882-31.596 24.527-55.601 24.527-28.37 0-50.478-17.506-50.478-52.565 0-27.373 14.85-46.018 35.96-55.126 18.313-8.066 43.884-9.489 63.43-11.718v-4.365c0-8.018.616-17.506-4.08-24.432-4.128-6.215-12.003-8.777-18.93-8.777-12.856 0-24.337 6.594-27.136 20.257-.57 3.037-2.799 6.026-5.835 6.168l-32.735-3.51c-2.751-.618-5.787-2.847-5.028-7.07 7.543-39.66 43.36-51.616 75.43-51.616 16.415 0 37.858 4.365 50.81 16.795 16.415 15.323 14.849 35.77 14.849 58.02v52.565c0 15.798 6.547 22.724 12.714 31.264 2.182 3.036 2.657 6.69-.095 8.966-6.879 5.74-19.119 16.415-25.855 22.393l-.095-.095" fill="#000"/></g></svg>
|
||||||
|
|
Before Width: | Height: | Size: 177 B After Width: | Height: | Size: 2.9 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 9.6 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path fill="#1b1a26" d="M26 21L16 27 6 21 6 12 26 12 26 21z" class="hide-inactive"/><g><path fill="#fd0" d="m2.63,8.19l0,15.62l13.37,7.81l13.37,-7.81l0,-15.62l-13.37,-7.81l-13.37,7.81zm22.11,12.72l-8.74,5.09l-8.73,-5.1l0,-6.46l8.73,5.1l8.73,-5.1l0.01,6.47z"/></g></svg>
|
After Width: | Height: | Size: 329 B |
|
@ -6,7 +6,7 @@ undefined)return current.closest(parentSelector);while(current!==document.docume
|
||||||
element){listener.call(element,e);e.stopPropagation()}})})}AnimeClient.on=function(sel,event,target,listener){if(listener===undefined){listener=target;AnimeClient.$(sel).forEach(function(el){addEvent(el,event,listener)})}else AnimeClient.$(sel).forEach(function(el){delegateEvent(el,target,event,listener)})};function ajaxSerialize(data){var pairs=[];Object.keys(data).forEach(function(name){var value=data[name].toString();name=encodeURIComponent(name);value=encodeURIComponent(value);pairs.push(name+
|
element){listener.call(element,e);e.stopPropagation()}})})}AnimeClient.on=function(sel,event,target,listener){if(listener===undefined){listener=target;AnimeClient.$(sel).forEach(function(el){addEvent(el,event,listener)})}else AnimeClient.$(sel).forEach(function(el){delegateEvent(el,target,event,listener)})};function ajaxSerialize(data){var pairs=[];Object.keys(data).forEach(function(name){var value=data[name].toString();name=encodeURIComponent(name);value=encodeURIComponent(value);pairs.push(name+
|
||||||
"="+value)});return pairs.join("&")}AnimeClient.ajax=function(url,config){var defaultConfig={data:{},type:"GET",dataType:"",success:AnimeClient.noop,mimeType:"application/x-www-form-urlencoded",error:AnimeClient.noop};config=Object.assign({},defaultConfig,config);var request=new XMLHttpRequest;var method=String(config.type).toUpperCase();if(method==="GET")url+=url.match(/\?/)?ajaxSerialize(config.data):"?"+ajaxSerialize(config.data);request.open(method,url);request.onreadystatechange=function(){if(request.readyState===
|
"="+value)});return pairs.join("&")}AnimeClient.ajax=function(url,config){var defaultConfig={data:{},type:"GET",dataType:"",success:AnimeClient.noop,mimeType:"application/x-www-form-urlencoded",error:AnimeClient.noop};config=Object.assign({},defaultConfig,config);var request=new XMLHttpRequest;var method=String(config.type).toUpperCase();if(method==="GET")url+=url.match(/\?/)?ajaxSerialize(config.data):"?"+ajaxSerialize(config.data);request.open(method,url);request.onreadystatechange=function(){if(request.readyState===
|
||||||
4){var responseText="";if(request.responseType==="json")responseText=JSON.parse(request.responseText);else responseText=request.responseText;if(request.status>299)config.error.call(null,request.status,responseText,request.response);else config.success.call(null,responseText,request.status)}};if(config.dataType==="json"){config.data=JSON.stringify(config.data);config.mimeType="application/json"}else config.data=ajaxSerialize(config.data);request.setRequestHeader("Content-Type",config.mimeType);if(method===
|
4){var responseText="";if(request.responseType==="json")responseText=JSON.parse(request.responseText);else responseText=request.responseText;if(request.status>299)config.error.call(null,request.status,responseText,request.response);else config.success.call(null,responseText,request.status)}};if(config.dataType==="json"){config.data=JSON.stringify(config.data);config.mimeType="application/json"}else config.data=ajaxSerialize(config.data);request.setRequestHeader("Content-Type",config.mimeType);if(method===
|
||||||
"GET")request.send(null);else request.send(config.data)};AnimeClient.get=function(url,data,callback){callback=callback===undefined?null:callback;if(callback===null){callback=data;data={}}return AnimeClient.ajax(url,{data:data,success:callback})};AnimeClient.on("header","click",".message",hide);AnimeClient.on("form.js-delete","submit",confirmDelete);AnimeClient.on(".js-clear-cache","click",clearAPICache);AnimeClient.on(".vertical-tabs input","change",scrollToSection);AnimeClient.on(".media-filter",
|
"GET")request.send(null);else request.send(config.data);return request};AnimeClient.get=function(url,data,callback){callback=callback===undefined?null:callback;if(callback===null){callback=data;data={}}return AnimeClient.ajax(url,{data:data,success:callback})};AnimeClient.on("header","click",".message",hide);AnimeClient.on("form.js-delete","submit",confirmDelete);AnimeClient.on(".js-clear-cache","click",clearAPICache);AnimeClient.on(".vertical-tabs input","change",scrollToSection);AnimeClient.on(".media-filter",
|
||||||
"input",filterMedia);function hide(event){AnimeClient.hide(event.target)}function confirmDelete(event){var proceed=confirm("Are you ABSOLUTELY SURE you want to delete this item?");if(proceed===false){event.preventDefault();event.stopPropagation()}}function clearAPICache(){AnimeClient.get("/cache_purge",function(){AnimeClient.showMessage("success","Successfully purged api cache")})}function scrollToSection(event){var el=event.currentTarget.parentElement;var rect=el.getBoundingClientRect();var top=
|
"input",filterMedia);function hide(event){AnimeClient.hide(event.target)}function confirmDelete(event){var proceed=confirm("Are you ABSOLUTELY SURE you want to delete this item?");if(proceed===false){event.preventDefault();event.stopPropagation()}}function clearAPICache(){AnimeClient.get("/cache_purge",function(){AnimeClient.showMessage("success","Successfully purged api cache")})}function scrollToSection(event){var el=event.currentTarget.parentElement;var rect=el.getBoundingClientRect();var top=
|
||||||
rect.top+window.pageYOffset;window.scrollTo({top:top,behavior:"smooth"})}function filterMedia(event){var rawFilter=event.target.value;var filter=new RegExp(rawFilter,"i");if(rawFilter!==""){AnimeClient.$("article.media").forEach(function(article){var titleLink=AnimeClient.$(".name a",article)[0];var title=String(titleLink.textContent).trim();if(!filter.test(title))AnimeClient.hide(article);else AnimeClient.show(article)});AnimeClient.$("table.media-wrap tbody tr").forEach(function(tr){var titleCell=
|
rect.top+window.pageYOffset;window.scrollTo({top:top,behavior:"smooth"})}function filterMedia(event){var rawFilter=event.target.value;var filter=new RegExp(rawFilter,"i");if(rawFilter!==""){AnimeClient.$("article.media").forEach(function(article){var titleLink=AnimeClient.$(".name a",article)[0];var title=String(titleLink.textContent).trim();if(!filter.test(title))AnimeClient.hide(article);else AnimeClient.show(article)});AnimeClient.$("table.media-wrap tbody tr").forEach(function(tr){var titleCell=
|
||||||
AnimeClient.$("td.align-left",tr)[0];var titleLink=AnimeClient.$("a",titleCell)[0];var linkTitle=String(titleLink.textContent).trim();var textTitle=String(titleCell.textContent).trim();if(!(filter.test(linkTitle)||filter.test(textTitle)))AnimeClient.hide(tr);else AnimeClient.show(tr)})}else{AnimeClient.show("article.media");AnimeClient.show("table.media-wrap tbody tr")}}if("serviceWorker"in navigator)navigator.serviceWorker.register("/sw.js").then(function(reg){console.log("Service worker registered",
|
AnimeClient.$("td.align-left",tr)[0];var titleLink=AnimeClient.$("a",titleCell)[0];var linkTitle=String(titleLink.textContent).trim();var textTitle=String(titleCell.textContent).trim();if(!(filter.test(linkTitle)||filter.test(textTitle)))AnimeClient.hide(tr);else AnimeClient.show(tr)})}else{AnimeClient.show("article.media");AnimeClient.show("table.media-wrap tbody tr")}}if("serviceWorker"in navigator)navigator.serviceWorker.register("/sw.js").then(function(reg){console.log("Service worker registered",
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -6,21 +6,22 @@ undefined)return current.closest(parentSelector);while(current!==document.docume
|
||||||
element){listener.call(element,e);e.stopPropagation()}})})}AnimeClient.on=function(sel,event,target,listener){if(listener===undefined){listener=target;AnimeClient.$(sel).forEach(function(el){addEvent(el,event,listener)})}else AnimeClient.$(sel).forEach(function(el){delegateEvent(el,target,event,listener)})};function ajaxSerialize(data){var pairs=[];Object.keys(data).forEach(function(name){var value=data[name].toString();name=encodeURIComponent(name);value=encodeURIComponent(value);pairs.push(name+
|
element){listener.call(element,e);e.stopPropagation()}})})}AnimeClient.on=function(sel,event,target,listener){if(listener===undefined){listener=target;AnimeClient.$(sel).forEach(function(el){addEvent(el,event,listener)})}else AnimeClient.$(sel).forEach(function(el){delegateEvent(el,target,event,listener)})};function ajaxSerialize(data){var pairs=[];Object.keys(data).forEach(function(name){var value=data[name].toString();name=encodeURIComponent(name);value=encodeURIComponent(value);pairs.push(name+
|
||||||
"="+value)});return pairs.join("&")}AnimeClient.ajax=function(url,config){var defaultConfig={data:{},type:"GET",dataType:"",success:AnimeClient.noop,mimeType:"application/x-www-form-urlencoded",error:AnimeClient.noop};config=Object.assign({},defaultConfig,config);var request=new XMLHttpRequest;var method=String(config.type).toUpperCase();if(method==="GET")url+=url.match(/\?/)?ajaxSerialize(config.data):"?"+ajaxSerialize(config.data);request.open(method,url);request.onreadystatechange=function(){if(request.readyState===
|
"="+value)});return pairs.join("&")}AnimeClient.ajax=function(url,config){var defaultConfig={data:{},type:"GET",dataType:"",success:AnimeClient.noop,mimeType:"application/x-www-form-urlencoded",error:AnimeClient.noop};config=Object.assign({},defaultConfig,config);var request=new XMLHttpRequest;var method=String(config.type).toUpperCase();if(method==="GET")url+=url.match(/\?/)?ajaxSerialize(config.data):"?"+ajaxSerialize(config.data);request.open(method,url);request.onreadystatechange=function(){if(request.readyState===
|
||||||
4){var responseText="";if(request.responseType==="json")responseText=JSON.parse(request.responseText);else responseText=request.responseText;if(request.status>299)config.error.call(null,request.status,responseText,request.response);else config.success.call(null,responseText,request.status)}};if(config.dataType==="json"){config.data=JSON.stringify(config.data);config.mimeType="application/json"}else config.data=ajaxSerialize(config.data);request.setRequestHeader("Content-Type",config.mimeType);if(method===
|
4){var responseText="";if(request.responseType==="json")responseText=JSON.parse(request.responseText);else responseText=request.responseText;if(request.status>299)config.error.call(null,request.status,responseText,request.response);else config.success.call(null,responseText,request.status)}};if(config.dataType==="json"){config.data=JSON.stringify(config.data);config.mimeType="application/json"}else config.data=ajaxSerialize(config.data);request.setRequestHeader("Content-Type",config.mimeType);if(method===
|
||||||
"GET")request.send(null);else request.send(config.data)};AnimeClient.get=function(url,data,callback){callback=callback===undefined?null:callback;if(callback===null){callback=data;data={}}return AnimeClient.ajax(url,{data:data,success:callback})};AnimeClient.on("header","click",".message",hide);AnimeClient.on("form.js-delete","submit",confirmDelete);AnimeClient.on(".js-clear-cache","click",clearAPICache);AnimeClient.on(".vertical-tabs input","change",scrollToSection);AnimeClient.on(".media-filter",
|
"GET")request.send(null);else request.send(config.data);return request};AnimeClient.get=function(url,data,callback){callback=callback===undefined?null:callback;if(callback===null){callback=data;data={}}return AnimeClient.ajax(url,{data:data,success:callback})};AnimeClient.on("header","click",".message",hide);AnimeClient.on("form.js-delete","submit",confirmDelete);AnimeClient.on(".js-clear-cache","click",clearAPICache);AnimeClient.on(".vertical-tabs input","change",scrollToSection);AnimeClient.on(".media-filter",
|
||||||
"input",filterMedia);function hide(event){AnimeClient.hide(event.target)}function confirmDelete(event){var proceed=confirm("Are you ABSOLUTELY SURE you want to delete this item?");if(proceed===false){event.preventDefault();event.stopPropagation()}}function clearAPICache(){AnimeClient.get("/cache_purge",function(){AnimeClient.showMessage("success","Successfully purged api cache")})}function scrollToSection(event){var el=event.currentTarget.parentElement;var rect=el.getBoundingClientRect();var top=
|
"input",filterMedia);function hide(event){AnimeClient.hide(event.target)}function confirmDelete(event){var proceed=confirm("Are you ABSOLUTELY SURE you want to delete this item?");if(proceed===false){event.preventDefault();event.stopPropagation()}}function clearAPICache(){AnimeClient.get("/cache_purge",function(){AnimeClient.showMessage("success","Successfully purged api cache")})}function scrollToSection(event){var el=event.currentTarget.parentElement;var rect=el.getBoundingClientRect();var top=
|
||||||
rect.top+window.pageYOffset;window.scrollTo({top:top,behavior:"smooth"})}function filterMedia(event){var rawFilter=event.target.value;var filter=new RegExp(rawFilter,"i");if(rawFilter!==""){AnimeClient.$("article.media").forEach(function(article){var titleLink=AnimeClient.$(".name a",article)[0];var title=String(titleLink.textContent).trim();if(!filter.test(title))AnimeClient.hide(article);else AnimeClient.show(article)});AnimeClient.$("table.media-wrap tbody tr").forEach(function(tr){var titleCell=
|
rect.top+window.pageYOffset;window.scrollTo({top:top,behavior:"smooth"})}function filterMedia(event){var rawFilter=event.target.value;var filter=new RegExp(rawFilter,"i");if(rawFilter!==""){AnimeClient.$("article.media").forEach(function(article){var titleLink=AnimeClient.$(".name a",article)[0];var title=String(titleLink.textContent).trim();if(!filter.test(title))AnimeClient.hide(article);else AnimeClient.show(article)});AnimeClient.$("table.media-wrap tbody tr").forEach(function(tr){var titleCell=
|
||||||
AnimeClient.$("td.align-left",tr)[0];var titleLink=AnimeClient.$("a",titleCell)[0];var linkTitle=String(titleLink.textContent).trim();var textTitle=String(titleCell.textContent).trim();if(!(filter.test(linkTitle)||filter.test(textTitle)))AnimeClient.hide(tr);else AnimeClient.show(tr)})}else{AnimeClient.show("article.media");AnimeClient.show("table.media-wrap tbody tr")}}if("serviceWorker"in navigator)navigator.serviceWorker.register("/sw.js").then(function(reg){console.log("Service worker registered",
|
AnimeClient.$("td.align-left",tr)[0];var titleLink=AnimeClient.$("a",titleCell)[0];var linkTitle=String(titleLink.textContent).trim();var textTitle=String(titleCell.textContent).trim();if(!(filter.test(linkTitle)||filter.test(textTitle)))AnimeClient.hide(tr);else AnimeClient.show(tr)})}else{AnimeClient.show("article.media");AnimeClient.show("table.media-wrap tbody tr")}}if("serviceWorker"in navigator)navigator.serviceWorker.register("/sw.js").then(function(reg){console.log("Service worker registered",
|
||||||
reg.scope)})["catch"](function(error){console.error("Failed to register service worker",error)});AnimeClient.on("main","change",".big-check",function(e){var id=e.target.id;document.getElementById("mal_"+id).checked=true});function renderAnimeSearchResults(data){var results=[];data.forEach(function(x){var item=x.attributes;var titles=item.titles.join("<br />");results.push('\n\t\t\t<article class="media search">\n\t\t\t\t<div class="name">\n\t\t\t\t\t<input type="radio" class="mal-check" id="mal_'+
|
reg.scope)})["catch"](function(error){console.error("Failed to register service worker",error)});(function(){var hidden=null;var visibilityChange=null;if(typeof document.hidden!=="undefined"){hidden="hidden";visibilityChange="visibilitychange"}else if(typeof document.msHidden!=="undefined"){hidden="msHidden";visibilityChange="msvisibilitychange"}else if(typeof document.webkitHidden!=="undefined"){hidden="webkitHidden";visibilityChange="webkitvisibilitychange"}function handleVisibilityChange(){if(!document[hidden])AnimeClient.get("/heartbeat",
|
||||||
item.slug+'" name="mal_id" value="'+x.mal_id+'" />\n\t\t\t\t\t<input type="radio" class="big-check" id="'+item.slug+'" name="id" value="'+x.id+'" />\n\t\t\t\t\t<label for="'+item.slug+'">\n\t\t\t\t\t\t<picture width="220">\n\t\t\t\t\t\t\t<source srcset="/public/images/anime/'+x.id+'.webp" type="image/webp" />\n\t\t\t\t\t\t\t<source srcset="/public/images/anime/'+x.id+'.jpg" type="image/jpeg" />\n\t\t\t\t\t\t\t<img src="/public/images/anime/'+x.id+'.jpg" alt="" width="220" />\n\t\t\t\t\t\t</picture>\n\t\t\t\t\t\t<span class="name">\n\t\t\t\t\t\t\t'+
|
function(beat){var status=JSON.parse(beat);if(status.hasAuth!==true){document.removeEventListener(visibilityChange,handleVisibilityChange,false);location.reload()}})}if(hidden===null)console.info("Page visibility API not supported, JS session check will not work");else document.addEventListener(visibilityChange,handleVisibilityChange,false)})();AnimeClient.on("main","change",".big-check",function(e){var id=e.target.id;document.getElementById("mal_"+id).checked=true});function renderAnimeSearchResults(data){var results=
|
||||||
item.canonicalTitle+"<br />\n\t\t\t\t\t\t\t<small>"+titles+'</small>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div class="table">\n\t\t\t\t\t<div class="row">\n\t\t\t\t\t\t<span class="edit">\n\t\t\t\t\t\t\t<a class="bracketed" href="/anime/details/'+item.slug+'">Info Page</a>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</article>\n\t\t')});return results.join("")}function renderMangaSearchResults(data){var results=[];data.forEach(function(x){var item=x.attributes;
|
[];data.forEach(function(item){var titles=item.titles.join("<br />");results.push('\n\t\t\t<article class="media search">\n\t\t\t\t<div class="name">\n\t\t\t\t\t<input type="radio" class="mal-check" id="mal_'+item.slug+'" name="mal_id" value="'+item.mal_id+'" />\n\t\t\t\t\t<input type="radio" class="big-check" id="'+item.slug+'" name="id" value="'+item.id+'" />\n\t\t\t\t\t<label for="'+item.slug+'">\n\t\t\t\t\t\t<picture width="220">\n\t\t\t\t\t\t\t<source srcset="/public/images/anime/'+item.id+'.webp" type="image/webp" />\n\t\t\t\t\t\t\t<source srcset="/public/images/anime/'+
|
||||||
var titles=item.titles.join("<br />");results.push('\n\t\t\t<article class="media search">\n\t\t\t\t<div class="name">\n\t\t\t\t\t<input type="radio" id="mal_'+item.slug+'" name="mal_id" value="'+x.mal_id+'" />\n\t\t\t\t\t<input type="radio" class="big-check" id="'+item.slug+'" name="id" value="'+x.id+'" />\n\t\t\t\t\t<label for="'+item.slug+'">\n\t\t\t\t\t\t<picture width="220">\n\t\t\t\t\t\t\t<source srcset="/public/images/manga/'+x.id+'.webp" type="image/webp" />\n\t\t\t\t\t\t\t<source srcset="/public/images/manga/'+
|
item.id+'.jpg" type="image/jpeg" />\n\t\t\t\t\t\t\t<img src="/public/images/anime/'+item.id+'.jpg" alt="" width="220" />\n\t\t\t\t\t\t</picture>\n\t\t\t\t\t\t<span class="name">\n\t\t\t\t\t\t\t'+item.canonicalTitle+"<br />\n\t\t\t\t\t\t\t<small>"+titles+'</small>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div class="table">\n\t\t\t\t\t<div class="row">\n\t\t\t\t\t\t<span class="edit">\n\t\t\t\t\t\t\t<a class="bracketed" href="/anime/details/'+item.slug+'">Info Page</a>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</article>\n\t\t')});
|
||||||
x.id+'.jpg" type="image/jpeg" />\n\t\t\t\t\t\t\t<img src="/public/images/manga/'+x.id+'.jpg" alt="" width="220" />\n\t\t\t\t\t\t</picture>\n\t\t\t\t\t\t<span class="name">\n\t\t\t\t\t\t\t'+item.canonicalTitle+"<br />\n\t\t\t\t\t\t\t<small>"+titles+'</small>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div class="table">\n\t\t\t\t\t<div class="row">\n\t\t\t\t\t\t<span class="edit">\n\t\t\t\t\t\t\t<a class="bracketed" href="/manga/details/'+item.slug+'">Info Page</a>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</article>\n\t\t')});
|
return results.join("")}function renderMangaSearchResults(data){var results=[];data.forEach(function(item){var titles=item.titles.join("<br />");results.push('\n\t\t\t<article class="media search">\n\t\t\t\t<div class="name">\n\t\t\t\t\t<input type="radio" id="mal_'+item.slug+'" name="mal_id" value="'+item.mal_id+'" />\n\t\t\t\t\t<input type="radio" class="big-check" id="'+item.slug+'" name="id" value="'+item.id+'" />\n\t\t\t\t\t<label for="'+item.slug+'">\n\t\t\t\t\t\t<picture width="220">\n\t\t\t\t\t\t\t<source srcset="/public/images/manga/'+
|
||||||
return results.join("")}var search=function(query){AnimeClient.show(".cssload-loader");AnimeClient.get(AnimeClient.url("/anime-collection/search"),{query:query},function(searchResults,status){searchResults=JSON.parse(searchResults);AnimeClient.hide(".cssload-loader");AnimeClient.$("#series-list")[0].innerHTML=renderAnimeSearchResults(searchResults.data)})};if(AnimeClient.hasElement(".anime #search"))AnimeClient.on("#search","input",AnimeClient.throttle(250,function(e){var query=encodeURIComponent(e.target.value);
|
item.id+'.webp" type="image/webp" />\n\t\t\t\t\t\t\t<source srcset="/public/images/manga/'+item.id+'.jpg" type="image/jpeg" />\n\t\t\t\t\t\t\t<img src="/public/images/manga/'+item.id+'.jpg" alt="" width="220" />\n\t\t\t\t\t\t</picture>\n\t\t\t\t\t\t<span class="name">\n\t\t\t\t\t\t\t'+item.canonicalTitle+"<br />\n\t\t\t\t\t\t\t<small>"+titles+'</small>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div class="table">\n\t\t\t\t\t<div class="row">\n\t\t\t\t\t\t<span class="edit">\n\t\t\t\t\t\t\t<a class="bracketed" href="/manga/details/'+
|
||||||
if(query==="")return;search(query)}));AnimeClient.on("body.anime.list","click",".plus-one",function(e){var parentSel=AnimeClient.closestParent(e.target,"article");var watchedCount=parseInt(AnimeClient.$(".completed_number",parentSel)[0].textContent,10)||0;var totalCount=parseInt(AnimeClient.$(".total_number",parentSel)[0].textContent,10);var title=AnimeClient.$(".name a",parentSel)[0].textContent;var data={id:parentSel.dataset.kitsuId,mal_id:parentSel.dataset.malId,data:{progress:watchedCount+1}};
|
item.slug+'">Info Page</a>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</article>\n\t\t')});return results.join("")}var search=function(query){AnimeClient.show(".cssload-loader");return AnimeClient.get(AnimeClient.url("/anime-collection/search"),{query:query},function(searchResults,status){searchResults=JSON.parse(searchResults);AnimeClient.hide(".cssload-loader");AnimeClient.$("#series-list")[0].innerHTML=renderAnimeSearchResults(searchResults)})};if(AnimeClient.hasElement(".anime #search")){var prevRequest=
|
||||||
if(isNaN(watchedCount)||watchedCount===0)data.data.status="current";if(!isNaN(watchedCount)&&watchedCount+1===totalCount)data.data.status="completed";AnimeClient.show("#loading-shadow");AnimeClient.ajax(AnimeClient.url("/anime/increment"),{data:data,dataType:"json",type:"POST",success:function(res){var resData=JSON.parse(res);if(resData.errors){AnimeClient.hide("#loading-shadow");AnimeClient.showMessage("error","Failed to update "+title+". ");AnimeClient.scrollToTop();return}if(resData.data.attributes.status===
|
null;AnimeClient.on("#search","input",AnimeClient.throttle(250,function(e){var query=encodeURIComponent(e.target.value);if(query==="")return;if(prevRequest!==null)prevRequest.abort();prevRequest=search(query)}))}AnimeClient.on("body.anime.list","click",".plus-one",function(e){var parentSel=AnimeClient.closestParent(e.target,"article");var watchedCount=parseInt(AnimeClient.$(".completed_number",parentSel)[0].textContent,10)||0;var totalCount=parseInt(AnimeClient.$(".total_number",parentSel)[0].textContent,
|
||||||
"completed")AnimeClient.hide(parentSel);AnimeClient.hide("#loading-shadow");AnimeClient.showMessage("success","Successfully updated "+title);AnimeClient.$(".completed_number",parentSel)[0].textContent=++watchedCount;AnimeClient.scrollToTop()},error:function(){AnimeClient.hide("#loading-shadow");AnimeClient.showMessage("error","Failed to update "+title+". ");AnimeClient.scrollToTop()}})});var search$1=function(query){AnimeClient.show(".cssload-loader");AnimeClient.get(AnimeClient.url("/manga/search"),
|
10);var title=AnimeClient.$(".name a",parentSel)[0].textContent;var data={id:parentSel.dataset.kitsuId,mal_id:parentSel.dataset.malId,data:{progress:watchedCount+1}};if(isNaN(watchedCount)||watchedCount===0)data.data.status="CURRENT";if(!isNaN(watchedCount)&&watchedCount+1===totalCount)data.data.status="COMPLETED";AnimeClient.show("#loading-shadow");AnimeClient.ajax(AnimeClient.url("/anime/increment"),{data:data,dataType:"json",type:"POST",success:function(res){var resData=JSON.parse(res);if(resData.errors){AnimeClient.hide("#loading-shadow");
|
||||||
{query:query},function(searchResults,status){searchResults=JSON.parse(searchResults);AnimeClient.hide(".cssload-loader");AnimeClient.$("#series-list")[0].innerHTML=renderMangaSearchResults(searchResults.data)})};if(AnimeClient.hasElement(".manga #search"))AnimeClient.on("#search","input",AnimeClient.throttle(250,function(e){var query=encodeURIComponent(e.target.value);if(query==="")return;search$1(query)}));AnimeClient.on(".manga.list","click",".edit-buttons button",function(e){var thisSel=e.target;
|
AnimeClient.showMessage("error","Failed to update "+title+". ");AnimeClient.scrollToTop();return}if(resData.data.libraryEntry.update.libraryEntry.status==="COMPLETED")AnimeClient.hide(parentSel);AnimeClient.hide("#loading-shadow");AnimeClient.showMessage("success","Successfully updated "+title);AnimeClient.$(".completed_number",parentSel)[0].textContent=++watchedCount;AnimeClient.scrollToTop()},error:function(){AnimeClient.hide("#loading-shadow");AnimeClient.showMessage("error","Failed to update "+
|
||||||
var parentSel=AnimeClient.closestParent(e.target,"article");var type=thisSel.classList.contains("plus-one-chapter")?"chapter":"volume";var completed=parseInt(AnimeClient.$("."+type+"s_read",parentSel)[0].textContent,10)||0;var total=parseInt(AnimeClient.$("."+type+"_count",parentSel)[0].textContent,10);var mangaName=AnimeClient.$(".name",parentSel)[0].textContent;if(isNaN(completed))completed=0;var data={id:parentSel.dataset.kitsuId,mal_id:parentSel.dataset.malId,data:{progress:completed}};if(isNaN(completed)||
|
title+". ");AnimeClient.scrollToTop()}})});var search$1=function(query){AnimeClient.show(".cssload-loader");return AnimeClient.get(AnimeClient.url("/manga/search"),{query:query},function(searchResults,status){searchResults=JSON.parse(searchResults);AnimeClient.hide(".cssload-loader");AnimeClient.$("#series-list")[0].innerHTML=renderMangaSearchResults(searchResults)})};if(AnimeClient.hasElement(".manga #search")){var prevRequest$1=null;AnimeClient.on("#search","input",AnimeClient.throttle(250,function(e){var query=
|
||||||
completed===0)data.data.status="current";if(!isNaN(completed)&&completed+1===total)data.data.status="completed";data.data.progress=++completed;AnimeClient.show("#loading-shadow");AnimeClient.ajax(AnimeClient.url("/manga/increment"),{data:data,dataType:"json",type:"POST",mimeType:"application/json",success:function(){if(data.data.status==="completed")AnimeClient.hide(parentSel);AnimeClient.hide("#loading-shadow");AnimeClient.$("."+type+"s_read",parentSel)[0].textContent=completed;AnimeClient.showMessage("success",
|
encodeURIComponent(e.target.value);if(query==="")return;if(prevRequest$1!==null)prevRequest$1.abort();prevRequest$1=search$1(query)}))}AnimeClient.on(".manga.list","click",".edit-buttons button",function(e){var thisSel=e.target;var parentSel=AnimeClient.closestParent(e.target,"article");var type=thisSel.classList.contains("plus-one-chapter")?"chapter":"volume";var completed=parseInt(AnimeClient.$("."+type+"s_read",parentSel)[0].textContent,10)||0;var total=parseInt(AnimeClient.$("."+type+"_count",
|
||||||
"Successfully updated "+mangaName);AnimeClient.scrollToTop()},error:function(){AnimeClient.hide("#loading-shadow");AnimeClient.showMessage("error","Failed to update "+mangaName);AnimeClient.scrollToTop()}})})})()
|
parentSel)[0].textContent,10);var mangaName=AnimeClient.$(".name",parentSel)[0].textContent;if(isNaN(completed))completed=0;var data={id:parentSel.dataset.kitsuId,mal_id:parentSel.dataset.malId,data:{progress:completed}};if(isNaN(completed)||completed===0)data.data.status="CURRENT";if(!isNaN(completed)&&completed+1===total)data.data.status="COMPLETED";data.data.progress=++completed;AnimeClient.show("#loading-shadow");AnimeClient.ajax(AnimeClient.url("/manga/increment"),{data:data,dataType:"json",
|
||||||
|
type:"POST",mimeType:"application/json",success:function(){if(String(data.data.status).toUpperCase()==="COMPLETED")AnimeClient.hide(parentSel);AnimeClient.hide("#loading-shadow");AnimeClient.$("."+type+"s_read",parentSel)[0].textContent=completed;AnimeClient.showMessage("success","Successfully updated "+mangaName);AnimeClient.scrollToTop()},error:function(){AnimeClient.hide("#loading-shadow");AnimeClient.showMessage("error","Failed to update "+mangaName);AnimeClient.scrollToTop()}})})})()
|
||||||
//# sourceMappingURL=scripts.min.js.map
|
//# sourceMappingURL=scripts.min.js.map
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -10,18 +10,13 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Aviat\AnimeClient\Enum;
|
namespace Aviat\AnimeClient;
|
||||||
|
|
||||||
use Aviat\Ion\Enum as BaseEnum;
|
class API {
|
||||||
|
|
||||||
/**
|
|
||||||
* Types of lists
|
|
||||||
*/
|
|
||||||
final class APISource extends BaseEnum {
|
|
||||||
public const KITSU = 'kitsu';
|
|
||||||
public const ANILIST = 'anilist';
|
public const ANILIST = 'anilist';
|
||||||
|
public const KITSU = 'kitsu';
|
||||||
}
|
}
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -33,6 +33,12 @@ use Psr\Log\LoggerAwareTrait;
|
||||||
abstract class APIRequestBuilder {
|
abstract class APIRequestBuilder {
|
||||||
use LoggerAwareTrait;
|
use LoggerAwareTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where to look for GraphQL request files
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected string $filePath = __DIR__;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Url prefix for making url requests
|
* Url prefix for making url requests
|
||||||
* @var string
|
* @var string
|
||||||
|
@ -294,6 +300,74 @@ abstract class APIRequestBuilder {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a GraphQL query and return the Request object
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param array $variables
|
||||||
|
* @return Request
|
||||||
|
*/
|
||||||
|
public function queryRequest(string $name, array $variables = []): Request
|
||||||
|
{
|
||||||
|
$file = "{$this->filePath}/Queries/{$name}.graphql";
|
||||||
|
if ( ! file_exists($file))
|
||||||
|
{
|
||||||
|
throw new LogicException('GraphQL query file does not exist.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = file_get_contents($file);
|
||||||
|
$body = [
|
||||||
|
'query' => $query
|
||||||
|
];
|
||||||
|
|
||||||
|
if ( ! empty($variables))
|
||||||
|
{
|
||||||
|
$body['variables'] = [];
|
||||||
|
foreach($variables as $key => $val)
|
||||||
|
{
|
||||||
|
$body['variables'][$key] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->setUpRequest('POST', $this->baseUrl, [
|
||||||
|
'body' => $body,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a GraphQL mutation request, and return the Request object
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param array $variables
|
||||||
|
* @return Request
|
||||||
|
* @throws Throwable
|
||||||
|
*/
|
||||||
|
public function mutateRequest (string $name, array $variables = []): Request
|
||||||
|
{
|
||||||
|
$file = "{$this->filePath}/Mutations/{$name}.graphql";
|
||||||
|
if ( ! file_exists($file))
|
||||||
|
{
|
||||||
|
throw new LogicException('GraphQL mutation file does not exist.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = file_get_contents($file);
|
||||||
|
$body = [
|
||||||
|
'query' => $query
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!empty($variables)) {
|
||||||
|
$body['variables'] = [];
|
||||||
|
foreach ($variables as $key => $val)
|
||||||
|
{
|
||||||
|
$body['variables'][$key] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->setUpRequest('POST', $this->baseUrl, [
|
||||||
|
'body' => $body,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the full request url
|
* Create the full request url
|
||||||
*
|
*
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -32,6 +32,14 @@ abstract class AbstractListItem {
|
||||||
*/
|
*/
|
||||||
abstract public function create(array $data): Request;
|
abstract public function create(array $data): Request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a full list item for syncing
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @return Request
|
||||||
|
*/
|
||||||
|
abstract public function createFull(array $data): Request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a list item
|
* Retrieve a list item
|
||||||
*
|
*
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"name": "Anilist Schema",
|
||||||
|
"schemaPath": "schema.graphql",
|
||||||
|
"extensions": {
|
||||||
|
"endpoints": {
|
||||||
|
"Anilist": {
|
||||||
|
"url": "https://graphql.anilist.co",
|
||||||
|
"headers": {
|
||||||
|
"user-agent": "JS GraphQL"
|
||||||
|
},
|
||||||
|
"introspect": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,47 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
/**
|
|
||||||
* Hummingbird Anime List Client
|
|
||||||
*
|
|
||||||
* An API client for Kitsu to manage anime and manga watch lists
|
|
||||||
*
|
|
||||||
* PHP version 7.4
|
|
||||||
*
|
|
||||||
* @package HummingbirdAnimeClient
|
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
|
||||||
* @version 5
|
|
||||||
* @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 string $baseUrl = 'https://graphql.anilist.co';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Valid HTTP request methods
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected array $validMethods = ['POST'];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HTTP headers to send with every request
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected array $defaultHeaders = [
|
|
||||||
'User-Agent' => USER_AGENT,
|
|
||||||
'Accept' => 'application/json',
|
|
||||||
'Content-Type' => 'application/json',
|
|
||||||
];
|
|
||||||
}
|
|
|
@ -1,159 +0,0 @@
|
||||||
query ($id: Int) {
|
|
||||||
Media(type: ANIME, idMal:$id) {
|
|
||||||
id
|
|
||||||
idMal
|
|
||||||
isAdult
|
|
||||||
season
|
|
||||||
title {
|
|
||||||
romaji
|
|
||||||
english
|
|
||||||
native
|
|
||||||
userPreferred
|
|
||||||
}
|
|
||||||
description(asHtml: true)
|
|
||||||
duration
|
|
||||||
format
|
|
||||||
status
|
|
||||||
chapters
|
|
||||||
volumes
|
|
||||||
genres
|
|
||||||
synonyms
|
|
||||||
countryOfOrigin
|
|
||||||
source
|
|
||||||
startDate {
|
|
||||||
year
|
|
||||||
month
|
|
||||||
day
|
|
||||||
}
|
|
||||||
endDate {
|
|
||||||
year
|
|
||||||
month
|
|
||||||
day
|
|
||||||
}
|
|
||||||
trailer {
|
|
||||||
id
|
|
||||||
site
|
|
||||||
}
|
|
||||||
coverImage {
|
|
||||||
large
|
|
||||||
medium
|
|
||||||
}
|
|
||||||
bannerImage
|
|
||||||
tags {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
description
|
|
||||||
category
|
|
||||||
isGeneralSpoiler
|
|
||||||
isMediaSpoiler
|
|
||||||
isAdult
|
|
||||||
}
|
|
||||||
characters {
|
|
||||||
edges {
|
|
||||||
role
|
|
||||||
voiceActors {
|
|
||||||
id
|
|
||||||
name {
|
|
||||||
first
|
|
||||||
last
|
|
||||||
native
|
|
||||||
}
|
|
||||||
language
|
|
||||||
image {
|
|
||||||
large
|
|
||||||
medium
|
|
||||||
}
|
|
||||||
description(asHtml: true)
|
|
||||||
siteUrl
|
|
||||||
}
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
name {
|
|
||||||
first
|
|
||||||
last
|
|
||||||
native
|
|
||||||
}
|
|
||||||
image {
|
|
||||||
large
|
|
||||||
medium
|
|
||||||
}
|
|
||||||
description
|
|
||||||
siteUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pageInfo {
|
|
||||||
total
|
|
||||||
perPage
|
|
||||||
currentPage
|
|
||||||
lastPage
|
|
||||||
hasNextPage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
staff {
|
|
||||||
edges {
|
|
||||||
role
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
name {
|
|
||||||
first
|
|
||||||
last
|
|
||||||
native
|
|
||||||
}
|
|
||||||
language
|
|
||||||
image {
|
|
||||||
large
|
|
||||||
medium
|
|
||||||
}
|
|
||||||
description(asHtml: true)
|
|
||||||
siteUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pageInfo {
|
|
||||||
total
|
|
||||||
perPage
|
|
||||||
currentPage
|
|
||||||
lastPage
|
|
||||||
hasNextPage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
studios {
|
|
||||||
edges {
|
|
||||||
isMain
|
|
||||||
node {
|
|
||||||
name
|
|
||||||
siteUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pageInfo {
|
|
||||||
total
|
|
||||||
perPage
|
|
||||||
currentPage
|
|
||||||
lastPage
|
|
||||||
hasNextPage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
externalLinks {
|
|
||||||
id
|
|
||||||
url
|
|
||||||
site
|
|
||||||
}
|
|
||||||
mediaListEntry {
|
|
||||||
id
|
|
||||||
userId
|
|
||||||
status
|
|
||||||
score
|
|
||||||
progress
|
|
||||||
progressVolumes
|
|
||||||
repeat
|
|
||||||
private
|
|
||||||
notes
|
|
||||||
}
|
|
||||||
streamingEpisodes {
|
|
||||||
title
|
|
||||||
thumbnail
|
|
||||||
url
|
|
||||||
site
|
|
||||||
}
|
|
||||||
siteUrl
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
query ($id: Int, $type: MediaType) {
|
|
||||||
Media (idMal: $id, type: $type) {
|
|
||||||
mediaListEntry {
|
|
||||||
id
|
|
||||||
userId
|
|
||||||
mediaId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
query ($id: Int){
|
|
||||||
Media(type: MANGA, id: $id) {
|
|
||||||
id
|
|
||||||
idMal
|
|
||||||
isAdult
|
|
||||||
season
|
|
||||||
title {
|
|
||||||
romaji
|
|
||||||
english
|
|
||||||
native
|
|
||||||
userPreferred
|
|
||||||
}
|
|
||||||
description(asHtml:true)
|
|
||||||
duration
|
|
||||||
format
|
|
||||||
status
|
|
||||||
chapters
|
|
||||||
volumes
|
|
||||||
genres
|
|
||||||
synonyms
|
|
||||||
countryOfOrigin
|
|
||||||
source
|
|
||||||
startDate {
|
|
||||||
year
|
|
||||||
month
|
|
||||||
day
|
|
||||||
}
|
|
||||||
endDate {
|
|
||||||
year
|
|
||||||
month
|
|
||||||
day
|
|
||||||
}
|
|
||||||
trailer {
|
|
||||||
id
|
|
||||||
site
|
|
||||||
}
|
|
||||||
coverImage {
|
|
||||||
large
|
|
||||||
medium
|
|
||||||
}
|
|
||||||
bannerImage
|
|
||||||
tags {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
description
|
|
||||||
category
|
|
||||||
isGeneralSpoiler
|
|
||||||
isMediaSpoiler
|
|
||||||
isAdult
|
|
||||||
}
|
|
||||||
characters {
|
|
||||||
edges {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
nodes {
|
|
||||||
id
|
|
||||||
name {
|
|
||||||
first
|
|
||||||
last
|
|
||||||
native
|
|
||||||
}
|
|
||||||
image {
|
|
||||||
large
|
|
||||||
medium
|
|
||||||
}
|
|
||||||
description
|
|
||||||
siteUrl
|
|
||||||
}
|
|
||||||
pageInfo {
|
|
||||||
total
|
|
||||||
perPage
|
|
||||||
currentPage
|
|
||||||
lastPage
|
|
||||||
hasNextPage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
externalLinks {
|
|
||||||
id
|
|
||||||
url
|
|
||||||
site
|
|
||||||
}
|
|
||||||
mediaListEntry {
|
|
||||||
id
|
|
||||||
userId
|
|
||||||
status
|
|
||||||
score
|
|
||||||
progress
|
|
||||||
progressVolumes
|
|
||||||
repeat
|
|
||||||
private
|
|
||||||
notes
|
|
||||||
}
|
|
||||||
streamingEpisodes {
|
|
||||||
title
|
|
||||||
thumbnail
|
|
||||||
url
|
|
||||||
site
|
|
||||||
}
|
|
||||||
siteUrl
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
query ($id: Int) {
|
|
||||||
Media (type: ANIME, malId: $id) {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
query ($name: String) {
|
|
||||||
MediaListCollection(userName: $name, type: ANIME) {
|
|
||||||
lists {
|
|
||||||
entries {
|
|
||||||
id
|
|
||||||
mediaId
|
|
||||||
score
|
|
||||||
progress
|
|
||||||
repeat
|
|
||||||
private
|
|
||||||
notes
|
|
||||||
status
|
|
||||||
media {
|
|
||||||
id
|
|
||||||
idMal
|
|
||||||
title {
|
|
||||||
romaji
|
|
||||||
english
|
|
||||||
native
|
|
||||||
userPreferred
|
|
||||||
}
|
|
||||||
type
|
|
||||||
format
|
|
||||||
status
|
|
||||||
episodes
|
|
||||||
season
|
|
||||||
genres
|
|
||||||
synonyms
|
|
||||||
countryOfOrigin
|
|
||||||
source
|
|
||||||
trailer {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
coverImage {
|
|
||||||
large
|
|
||||||
medium
|
|
||||||
}
|
|
||||||
bannerImage
|
|
||||||
tags {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
externalLinks {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
mediaListEntry {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
user {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
query ($name: String) {
|
|
||||||
MediaListCollection(userName: $name, type: MANGA) {
|
|
||||||
lists {
|
|
||||||
entries {
|
|
||||||
id
|
|
||||||
mediaId
|
|
||||||
score
|
|
||||||
progress
|
|
||||||
progressVolumes
|
|
||||||
repeat
|
|
||||||
private
|
|
||||||
notes
|
|
||||||
status
|
|
||||||
media {
|
|
||||||
id
|
|
||||||
idMal
|
|
||||||
title {
|
|
||||||
romaji
|
|
||||||
english
|
|
||||||
native
|
|
||||||
userPreferred
|
|
||||||
}
|
|
||||||
type
|
|
||||||
format
|
|
||||||
status
|
|
||||||
chapters
|
|
||||||
volumes
|
|
||||||
genres
|
|
||||||
synonyms
|
|
||||||
countryOfOrigin
|
|
||||||
source
|
|
||||||
trailer {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
coverImage {
|
|
||||||
large
|
|
||||||
medium
|
|
||||||
}
|
|
||||||
bannerImage
|
|
||||||
tags {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
externalLinks {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
mediaListEntry {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
user {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ use Aviat\AnimeClient\Types\FormItemData;
|
||||||
* CRUD operations for MAL list items
|
* CRUD operations for MAL list items
|
||||||
*/
|
*/
|
||||||
final class ListItem extends AbstractListItem {
|
final class ListItem extends AbstractListItem {
|
||||||
use AnilistTrait;
|
use RequestBuilderTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a minimal list item
|
* Create a minimal list item
|
||||||
|
@ -38,7 +38,7 @@ final class ListItem extends AbstractListItem {
|
||||||
public function create(array $data): Request
|
public function create(array $data): Request
|
||||||
{
|
{
|
||||||
$checkedData = Types\MediaListEntry::check($data);
|
$checkedData = Types\MediaListEntry::check($data);
|
||||||
return $this->mutateRequest('CreateMediaListEntry', $checkedData);
|
return $this->requestBuilder->mutateRequest('CreateMediaListEntry', $checkedData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,7 +50,7 @@ final class ListItem extends AbstractListItem {
|
||||||
public function createFull(array $data): Request
|
public function createFull(array $data): Request
|
||||||
{
|
{
|
||||||
$checkedData = Types\MediaListEntry::check($data);
|
$checkedData = Types\MediaListEntry::check($data);
|
||||||
return $this->mutateRequest('CreateFullMediaListEntry', $checkedData);
|
return $this->requestBuilder->mutateRequest('CreateFullMediaListEntry', $checkedData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,7 +62,7 @@ final class ListItem extends AbstractListItem {
|
||||||
*/
|
*/
|
||||||
public function delete(string $id, string $type = 'anime'): Request
|
public function delete(string $id, string $type = 'anime'): Request
|
||||||
{
|
{
|
||||||
return $this->mutateRequest('DeleteMediaListEntry', ['id' => $id]);
|
return $this->requestBuilder->mutateRequest('DeleteMediaListEntry', ['id' => $id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,7 +73,7 @@ final class ListItem extends AbstractListItem {
|
||||||
*/
|
*/
|
||||||
public function get(string $id): array
|
public function get(string $id): array
|
||||||
{
|
{
|
||||||
return $this->runQuery('MediaListItem', ['id' => $id]);
|
return $this->requestBuilder->runQuery('MediaListItem', ['id' => $id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,7 +90,7 @@ final class ListItem extends AbstractListItem {
|
||||||
'progress' => $data->progress,
|
'progress' => $data->progress,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $this->mutateRequest('IncrementMediaListEntry', $checkedData);
|
return $this->requestBuilder->mutateRequest('IncrementMediaListEntry', $checkedData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -120,6 +120,6 @@ final class ListItem extends AbstractListItem {
|
||||||
'notes' => $notes,
|
'notes' => $notes,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $this->mutateRequest('UpdateMediaListEntry', $updateData);
|
return $this->requestBuilder->mutateRequest('UpdateMediaListEntry', $updateData);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ use Throwable;
|
||||||
*/
|
*/
|
||||||
final class Model
|
final class Model
|
||||||
{
|
{
|
||||||
use AnilistTrait;
|
use RequestBuilderTrait;
|
||||||
/**
|
/**
|
||||||
* @var ListItem
|
* @var ListItem
|
||||||
*/
|
*/
|
||||||
|
@ -77,7 +77,7 @@ final class Model
|
||||||
])
|
])
|
||||||
->getFullRequest();
|
->getFullRequest();
|
||||||
|
|
||||||
$response = $this->getResponseFromRequest($request);
|
$response = $this->requestBuilder->getResponseFromRequest($request);
|
||||||
|
|
||||||
return Json::decode(wait($response->getBody()->buffer()));
|
return Json::decode(wait($response->getBody()->buffer()));
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ final class Model
|
||||||
*/
|
*/
|
||||||
public function checkAuth(): array
|
public function checkAuth(): array
|
||||||
{
|
{
|
||||||
return $this->runQuery('CheckLogin');
|
return $this->requestBuilder->runQuery('CheckLogin');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -110,7 +110,7 @@ final class Model
|
||||||
throw new InvalidArgumentException('Anilist username is not defined in config');
|
throw new InvalidArgumentException('Anilist username is not defined in config');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->runQuery('SyncUserList', [
|
return $this->requestBuilder->runQuery('SyncUserList', [
|
||||||
'name' => $anilistUser,
|
'name' => $anilistUser,
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
]);
|
]);
|
||||||
|
@ -275,17 +275,25 @@ final class Model
|
||||||
* Get the Anilist list item id from the media id from its MAL id
|
* Get the Anilist list item id from the media id from its MAL id
|
||||||
* this way is more accurate than getting the list item id
|
* this way is more accurate than getting the list item id
|
||||||
* directly from the MAL id
|
* directly from the MAL id
|
||||||
|
*
|
||||||
|
* @param string $mediaId
|
||||||
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
private function getListIdFromMediaId(string $mediaId): string
|
private function getListIdFromMediaId(string $mediaId): ?string
|
||||||
{
|
{
|
||||||
$config = $this->container->get('config');
|
$config = $this->container->get('config');
|
||||||
$anilistUser = $config->get(['anilist', 'username']);
|
$anilistUser = $config->get(['anilist', 'username']);
|
||||||
|
|
||||||
$info = $this->runQuery('ListItemIdByMediaId', [
|
$info = $this->requestBuilder->runQuery('ListItemIdByMediaId', [
|
||||||
'id' => $mediaId,
|
'id' => $mediaId,
|
||||||
'userName' => $anilistUser,
|
'userName' => $anilistUser,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if ( ! empty($info['errors']))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return (string)$info['data']['MediaList']['id'];
|
return (string)$info['data']['MediaList']['id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +311,7 @@ final class Model
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
$info = $this->runQuery('MediaIdByMalId', [
|
$info = $this->requestBuilder->runQuery('MediaIdByMalId', [
|
||||||
'id' => $malId,
|
'id' => $malId,
|
||||||
'type' => mb_strtoupper($type),
|
'type' => mb_strtoupper($type),
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -10,42 +10,43 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Aviat\AnimeClient\API\Anilist;
|
namespace Aviat\AnimeClient\API\Anilist;
|
||||||
|
|
||||||
use const Aviat\AnimeClient\USER_AGENT;
|
|
||||||
|
|
||||||
use function Amp\Promise\wait;
|
|
||||||
use function Aviat\AnimeClient\getResponse;
|
|
||||||
|
|
||||||
use Amp\Http\Client\Request;
|
use Amp\Http\Client\Request;
|
||||||
use Amp\Http\Client\Response;
|
use Amp\Http\Client\Response;
|
||||||
|
|
||||||
use Aviat\AnimeClient\API\Anilist;
|
use Aviat\AnimeClient\API\Anilist;
|
||||||
use Aviat\Ion\Json;
|
|
||||||
use Aviat\Ion\Di\ContainerAware;
|
use Aviat\Ion\Di\ContainerAware;
|
||||||
|
use Aviat\Ion\Di\ContainerInterface;
|
||||||
|
use Aviat\Ion\Json;
|
||||||
|
|
||||||
|
use Aviat\Ion\JsonException;
|
||||||
|
use function Amp\Promise\wait;
|
||||||
|
use function Aviat\AnimeClient\getResponse;
|
||||||
|
use const Aviat\AnimeClient\USER_AGENT;
|
||||||
|
|
||||||
|
use Aviat\AnimeClient\API\APIRequestBuilder;
|
||||||
|
|
||||||
use LogicException;
|
use LogicException;
|
||||||
use Throwable;
|
|
||||||
|
|
||||||
trait AnilistTrait {
|
final class RequestBuilder extends APIRequestBuilder {
|
||||||
use ContainerAware;
|
use ContainerAware;
|
||||||
|
|
||||||
/**
|
|
||||||
* The request builder for the Anilist API
|
|
||||||
* @var AnilistRequestBuilder
|
|
||||||
*/
|
|
||||||
protected AnilistRequestBuilder $requestBuilder;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base url for api requests
|
* The base url for api requests
|
||||||
* @var string $base_url
|
* @var string $base_url
|
||||||
*/
|
*/
|
||||||
protected string $baseUrl = Anilist::BASE_URL;
|
protected string $baseUrl = Anilist::BASE_URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valid HTTP request methods
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected array $validMethods = ['POST'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP headers to send with every request
|
* HTTP headers to send with every request
|
||||||
*
|
*
|
||||||
|
@ -53,21 +54,14 @@ trait AnilistTrait {
|
||||||
*/
|
*/
|
||||||
protected array $defaultHeaders = [
|
protected array $defaultHeaders = [
|
||||||
'Accept' => 'application/json',
|
'Accept' => 'application/json',
|
||||||
'Accept-Encoding' => 'gzip',
|
// 'Accept-Encoding' => 'gzip',
|
||||||
'Content-type' => 'application/json',
|
'Content-type' => 'application/json',
|
||||||
'User-Agent' => USER_AGENT,
|
'User-Agent' => USER_AGENT,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
public function __construct(ContainerInterface $container)
|
||||||
* Set the request builder object
|
|
||||||
*
|
|
||||||
* @param AnilistRequestBuilder $requestBuilder
|
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
public function setRequestBuilder($requestBuilder): self
|
|
||||||
{
|
{
|
||||||
$this->requestBuilder = $requestBuilder;
|
$this->setContainer($container);
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -82,7 +76,7 @@ trait AnilistTrait {
|
||||||
$config = $this->getContainer()->get('config');
|
$config = $this->getContainer()->get('config');
|
||||||
$anilistConfig = $config->get('anilist');
|
$anilistConfig = $config->get('anilist');
|
||||||
|
|
||||||
$request = $this->requestBuilder->newRequest('POST', $url);
|
$request = $this->newRequest('POST', $url);
|
||||||
|
|
||||||
// You can only authenticate the request if you
|
// You can only authenticate the request if you
|
||||||
// actually have an access_token saved
|
// actually have an access_token saved
|
||||||
|
@ -123,13 +117,12 @@ trait AnilistTrait {
|
||||||
*/
|
*/
|
||||||
public function runQuery(string $name, array $variables = []): array
|
public function runQuery(string $name, array $variables = []): array
|
||||||
{
|
{
|
||||||
$file = realpath(__DIR__ . "/GraphQL/Queries/{$name}.graphql");
|
$file = realpath(__DIR__ . "/Queries/{$name}.graphql");
|
||||||
if ( ! file_exists($file))
|
if ( ! file_exists($file))
|
||||||
{
|
{
|
||||||
throw new LogicException('GraphQL query file does not exist.');
|
throw new LogicException('GraphQL query file does not exist.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// $query = str_replace(["\t", "\n"], ' ', file_get_contents($file));
|
|
||||||
$query = file_get_contents($file);
|
$query = file_get_contents($file);
|
||||||
$body = [
|
$body = [
|
||||||
'query' => $query
|
'query' => $query
|
||||||
|
@ -157,13 +150,12 @@ trait AnilistTrait {
|
||||||
*/
|
*/
|
||||||
public function mutateRequest (string $name, array $variables = []): Request
|
public function mutateRequest (string $name, array $variables = []): Request
|
||||||
{
|
{
|
||||||
$file = realpath(__DIR__ . "/GraphQL/Mutations/{$name}.graphql");
|
$file = realpath(__DIR__ . "/Mutations/{$name}.graphql");
|
||||||
if (!file_exists($file))
|
if (!file_exists($file))
|
||||||
{
|
{
|
||||||
throw new LogicException('GraphQL mutation file does not exist.');
|
throw new LogicException('GraphQL mutation file does not exist.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// $query = str_replace(["\t", "\n"], ' ', file_get_contents($file));
|
|
||||||
$query = file_get_contents($file);
|
$query = file_get_contents($file);
|
||||||
|
|
||||||
$body = [
|
$body = [
|
||||||
|
@ -207,11 +199,7 @@ trait AnilistTrait {
|
||||||
*/
|
*/
|
||||||
private function getResponse(string $url, array $options = []): Response
|
private function getResponse(string $url, array $options = []): Response
|
||||||
{
|
{
|
||||||
$logger = NULL;
|
$logger = $this->container->getLogger('anilist-request');
|
||||||
if ($this->getContainer())
|
|
||||||
{
|
|
||||||
$logger = $this->container->getLogger('anilist-request');
|
|
||||||
}
|
|
||||||
|
|
||||||
$request = $this->setUpRequest($url, $options);
|
$request = $this->setUpRequest($url, $options);
|
||||||
$response = getResponse($request);
|
$response = getResponse($request);
|
||||||
|
@ -232,13 +220,9 @@ trait AnilistTrait {
|
||||||
* @return Response
|
* @return Response
|
||||||
* @throws Throwable
|
* @throws Throwable
|
||||||
*/
|
*/
|
||||||
private function getResponseFromRequest(Request $request): Response
|
public function getResponseFromRequest(Request $request): Response
|
||||||
{
|
{
|
||||||
$logger = NULL;
|
$logger = $this->container->getLogger('anilist-request');
|
||||||
if ($this->getContainer())
|
|
||||||
{
|
|
||||||
$logger = $this->container->getLogger('anilist-request');
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = getResponse($request);
|
$response = getResponse($request);
|
||||||
|
|
||||||
|
@ -265,29 +249,30 @@ trait AnilistTrait {
|
||||||
$response = $this->getResponse(Anilist::BASE_URL, $options);
|
$response = $this->getResponse(Anilist::BASE_URL, $options);
|
||||||
$validResponseCodes = [200, 201];
|
$validResponseCodes = [200, 201];
|
||||||
|
|
||||||
$logger = NULL;
|
$logger = $this->container->getLogger('anilist-request');
|
||||||
if ($this->getContainer())
|
$logger->debug('Anilist response', [
|
||||||
{
|
'status' => $response->getStatus(),
|
||||||
$logger = $this->container->getLogger('anilist-request');
|
'reason' => $response->getReason(),
|
||||||
$logger->debug('Anilist response', [
|
'body' => $response->getBody(),
|
||||||
'status' => $response->getStatus(),
|
'headers' => $response->getHeaders(),
|
||||||
'reason' => $response->getReason(),
|
//'requestHeaders' => $request->getHeaders(),
|
||||||
'body' => $response->getBody(),
|
]);
|
||||||
'headers' => $response->getHeaders(),
|
|
||||||
//'requestHeaders' => $request->getHeaders(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! \in_array($response->getStatus(), $validResponseCodes, TRUE))
|
if ( ! \in_array($response->getStatus(), $validResponseCodes, TRUE))
|
||||||
{
|
{
|
||||||
if ($logger !== NULL)
|
$logger->warning('Non 200 response for POST api call', (array)$response->getBody());
|
||||||
{
|
|
||||||
$logger->warning('Non 200 response for POST api call', (array)$response->getBody());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// dump(wait($response->getBody()->buffer()));
|
$rawBody = wait($response->getBody()->buffer());
|
||||||
|
try
|
||||||
return Json::decode(wait($response->getBody()->buffer()));
|
{
|
||||||
|
return Json::decode($rawBody);
|
||||||
|
}
|
||||||
|
catch (JsonException $e)
|
||||||
|
{
|
||||||
|
dump($e);
|
||||||
|
dump($rawBody);
|
||||||
|
die();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Hummingbird Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 7.4
|
||||||
|
*
|
||||||
|
* @package HummingbirdAnimeClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2020 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 5.1
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\API\Anilist;
|
||||||
|
|
||||||
|
use Aviat\Ion\Di\ContainerAware;
|
||||||
|
|
||||||
|
trait RequestBuilderTrait {
|
||||||
|
use ContainerAware;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The request builder for the Anilist API
|
||||||
|
* @var RequestBuilder
|
||||||
|
*/
|
||||||
|
protected RequestBuilder $requestBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the request builder object
|
||||||
|
*
|
||||||
|
* @param RequestBuilder $requestBuilder
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setRequestBuilder($requestBuilder): self
|
||||||
|
{
|
||||||
|
$this->requestBuilder = $requestBuilder;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -20,37 +20,37 @@ use Aviat\AnimeClient\Types\AbstractType;
|
||||||
|
|
||||||
class MediaListEntry extends AbstractType {
|
class MediaListEntry extends AbstractType {
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int|string
|
||||||
*/
|
*/
|
||||||
public $id;
|
public $id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string|null
|
||||||
*/
|
*/
|
||||||
public $notes;
|
public ?string $notes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
public $private;
|
public ?bool $private;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
public $progress;
|
public int $progress;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
public $repeat;
|
public ?int $repeat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
public $status;
|
public string $status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
public $score;
|
public ?int $score;
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ trait CacheTrait {
|
||||||
*
|
*
|
||||||
* @param string $key
|
* @param string $key
|
||||||
* @param callable $primer
|
* @param callable $primer
|
||||||
* @param array $primeArgs
|
* @param array|null $primeArgs
|
||||||
* @return mixed|null
|
* @return mixed|null
|
||||||
* @throws InvalidArgumentException
|
* @throws InvalidArgumentException
|
||||||
*/
|
*/
|
||||||
|
@ -78,22 +78,4 @@ trait CacheTrait {
|
||||||
|
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a hash as a cache key from the current method call
|
|
||||||
*
|
|
||||||
* @param mixed $object
|
|
||||||
* @param string $method
|
|
||||||
* @param array $args
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getHashForMethodCall($object, string $method, array $args = []): string
|
|
||||||
{
|
|
||||||
$keyObj = [
|
|
||||||
'class' => get_class($object),
|
|
||||||
'method' => $method,
|
|
||||||
'args' => $args,
|
|
||||||
];
|
|
||||||
return sha1(json_encode($keyObj));
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
* @copyright 2015 - 2020 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 5
|
* @version 5.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -1,354 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
/**
|
|
||||||
* Hummingbird Anime List Client
|
|
||||||
*
|
|
||||||
* An API client for Kitsu to manage anime and manga watch lists
|
|
||||||
*
|
|
||||||
* PHP version 7.4
|
|
||||||
*
|
|
||||||
* @package HummingbirdAnimeClient
|
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
|
||||||
* @version 5
|
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Aviat\AnimeClient\API;
|
|
||||||
|
|
||||||
use function in_array;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class encapsulating Json API data structure for a request or response
|
|
||||||
*/
|
|
||||||
final class JsonAPI {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Basic structure is generally like so:
|
|
||||||
* [
|
|
||||||
* 'id' => '12016665',
|
|
||||||
* 'type' => 'libraryEntries',
|
|
||||||
* 'links' => [
|
|
||||||
* 'self' => 'https://kitsu.io/api/edge/library-entries/13016665'
|
|
||||||
* ],
|
|
||||||
* 'attributes' => [
|
|
||||||
*
|
|
||||||
* ]
|
|
||||||
* ]
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inline all included data
|
|
||||||
*
|
|
||||||
* @param array $data - The raw JsonAPI response data
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function organizeData(array $data): array
|
|
||||||
{
|
|
||||||
// relationships that have singular data
|
|
||||||
$singular = [
|
|
||||||
'waifu'
|
|
||||||
];
|
|
||||||
|
|
||||||
// Reorganize included data
|
|
||||||
$included = array_key_exists('included', $data)
|
|
||||||
? static::organizeIncluded($data['included'])
|
|
||||||
: [];
|
|
||||||
|
|
||||||
// Inline organized data
|
|
||||||
foreach($data['data'] as $i => &$item)
|
|
||||||
{
|
|
||||||
if ( ! is_array($item))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists('relationships', $item))
|
|
||||||
{
|
|
||||||
foreach($item['relationships'] as $relType => $props)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (array_keys($props) === ['links'])
|
|
||||||
{
|
|
||||||
unset($item['relationships'][$relType]);
|
|
||||||
|
|
||||||
if (empty($item['relationships']))
|
|
||||||
{
|
|
||||||
unset($item['relationships']);
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists('links', $props))
|
|
||||||
{
|
|
||||||
unset($item['relationships'][$relType]['links']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists('data', $props))
|
|
||||||
{
|
|
||||||
if (empty($props['data']))
|
|
||||||
{
|
|
||||||
unset($item['relationships'][$relType]['data']);
|
|
||||||
|
|
||||||
if (empty($item['relationships'][$relType]))
|
|
||||||
{
|
|
||||||
unset($item['relationships'][$relType]);
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Single data item
|
|
||||||
if (array_key_exists('id', $props['data']))
|
|
||||||
{
|
|
||||||
$idKey = $props['data']['id'];
|
|
||||||
$dataType = $props['data']['type'];
|
|
||||||
$relationship =& $item['relationships'][$relType];
|
|
||||||
unset($relationship['data']);
|
|
||||||
|
|
||||||
if (in_array($relType, $singular, TRUE))
|
|
||||||
{
|
|
||||||
$relationship = $included[$dataType][$idKey];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($relType === $dataType)
|
|
||||||
{
|
|
||||||
$relationship[$idKey] = $included[$dataType][$idKey];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$relationship[$dataType][$idKey] = $included[$dataType][$idKey];
|
|
||||||
}
|
|
||||||
// Multiple data items
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach($props['data'] as $j => $datum)
|
|
||||||
{
|
|
||||||
$idKey = $props['data'][$j]['id'];
|
|
||||||
$dataType = $props['data'][$j]['type'];
|
|
||||||
$relationship =& $item['relationships'][$relType];
|
|
||||||
|
|
||||||
if ($relType === $dataType)
|
|
||||||
{
|
|
||||||
$relationship[$idKey] = $included[$dataType][$idKey];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$relationship[$dataType][$idKey][$j] = $included[$dataType][$idKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
unset($item['relationships'][$relType]['data']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unset($item);
|
|
||||||
|
|
||||||
$data['data']['included'] = $included;
|
|
||||||
|
|
||||||
return $data['data'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restructure included data to make it simpler to inline
|
|
||||||
*
|
|
||||||
* @param array $included
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function organizeIncluded(array $included): array
|
|
||||||
{
|
|
||||||
$organized = [];
|
|
||||||
|
|
||||||
// First pass, create [ type => items[] ] structure
|
|
||||||
foreach($included as &$item)
|
|
||||||
{
|
|
||||||
$type = $item['type'];
|
|
||||||
$id = $item['id'];
|
|
||||||
$organized[$type] = $organized[$type] ?? [];
|
|
||||||
$newItem = [];
|
|
||||||
|
|
||||||
foreach(['attributes', 'relationships'] as $key)
|
|
||||||
{
|
|
||||||
if (array_key_exists($key, $item))
|
|
||||||
{
|
|
||||||
// Remove 'links' type relationships
|
|
||||||
if ($key === 'relationships')
|
|
||||||
{
|
|
||||||
foreach($item['relationships'] as $relType => $props)
|
|
||||||
{
|
|
||||||
if (array_keys($props) === ['links'])
|
|
||||||
{
|
|
||||||
unset($item['relationships'][$relType]);
|
|
||||||
if (empty($item['relationships']))
|
|
||||||
{
|
|
||||||
continue 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$newItem[$key] = $item[$key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$organized[$type][$id] = $newItem;
|
|
||||||
}
|
|
||||||
unset($item);
|
|
||||||
|
|
||||||
// Second pass, go through and fill missing relationships in the first pass
|
|
||||||
foreach($organized as $type => $items)
|
|
||||||
{
|
|
||||||
foreach($items as $id => $item)
|
|
||||||
{
|
|
||||||
if (array_key_exists('relationships', $item) && is_array($item['relationships']))
|
|
||||||
{
|
|
||||||
foreach($item['relationships'] as $relType => $props)
|
|
||||||
{
|
|
||||||
if (array_key_exists('data', $props) && is_array($props['data']) && array_key_exists('id', $props['data']))
|
|
||||||
{
|
|
||||||
$idKey = $props['data']['id'];
|
|
||||||
$dataType = $props['data']['type'];
|
|
||||||
|
|
||||||
$relationship =& $organized[$type][$id]['relationships'][$relType];
|
|
||||||
unset($relationship['links'], $relationship['data']);
|
|
||||||
|
|
||||||
if ($relType === $dataType)
|
|
||||||
{
|
|
||||||
$relationship[$idKey] = $included[$dataType][$idKey];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! array_key_exists($dataType, $organized))
|
|
||||||
{
|
|
||||||
$organized[$dataType] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists($idKey, $organized[$dataType]))
|
|
||||||
{
|
|
||||||
$relationship[$dataType][$idKey] = $organized[$dataType][$idKey];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $organized;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Take organized includes and inline them, where applicable
|
|
||||||
*
|
|
||||||
* @param array $included
|
|
||||||
* @param string $key The key of the include to inline the other included values into
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function inlineIncludedRelationships(array $included, string $key): array
|
|
||||||
{
|
|
||||||
$inlined = [
|
|
||||||
$key => []
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($included[$key] as $itemId => $item)
|
|
||||||
{
|
|
||||||
// Duplicate the item for the output
|
|
||||||
$inlined[$key][$itemId] = $item;
|
|
||||||
|
|
||||||
foreach($item['relationships'] as $type => $ids)
|
|
||||||
{
|
|
||||||
$inlined[$key][$itemId]['relationships'][$type] = [];
|
|
||||||
|
|
||||||
if ( ! array_key_exists($type, $included)) continue;
|
|
||||||
|
|
||||||
if (array_key_exists('data', $ids ))
|
|
||||||
{
|
|
||||||
$ids = array_column($ids['data'], 'id');
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($ids as $id)
|
|
||||||
{
|
|
||||||
$inlined[$key][$itemId]['relationships'][$type][$id] = $included[$type][$id];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $inlined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reorganizes 'included' data to be keyed by
|
|
||||||
* type => [
|
|
||||||
* id => data/attributes,
|
|
||||||
* ]
|
|
||||||
*
|
|
||||||
* @param array $includes
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function organizeIncludes(array $includes): array
|
|
||||||
{
|
|
||||||
$organized = [];
|
|
||||||
$types = array_unique(array_column($includes, 'type'));
|
|
||||||
sort($types);
|
|
||||||
|
|
||||||
foreach ($types as $type)
|
|
||||||
{
|
|
||||||
$organized[$type] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($includes as $item)
|
|
||||||
{
|
|
||||||
$type = $item['type'];
|
|
||||||
$id = $item['id'];
|
|
||||||
|
|
||||||
if (array_key_exists('attributes', $item))
|
|
||||||
{
|
|
||||||
$organized[$type][$id] = $item['attributes'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists('relationships', $item))
|
|
||||||
{
|
|
||||||
$organized[$type][$id]['relationships'] = static::organizeRelationships($item['relationships']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $organized;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reorganize relationship mappings to make them simpler to use
|
|
||||||
*
|
|
||||||
* Remove verbose structure, and just map:
|
|
||||||
* type => [ idArray ]
|
|
||||||
*
|
|
||||||
* @param array $relationships
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function organizeRelationships(array $relationships): array
|
|
||||||
{
|
|
||||||
$organized = $relationships;
|
|
||||||
|
|
||||||
foreach($relationships as $key => $data)
|
|
||||||
{
|
|
||||||
$organized[$key] = $organized[$key] ?? [];
|
|
||||||
|
|
||||||
if ( ! array_key_exists('data', $data))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($data['data'] as $item)
|
|
||||||
{
|
|
||||||
if (is_array($item) && array_key_exists('id', $item))
|
|
||||||
{
|
|
||||||
$organized[$key][] = $item['id'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $organized;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,270 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
/**
|
|
||||||
* Hummingbird Anime List Client
|
|
||||||
*
|
|
||||||
* An API client for Kitsu to manage anime and manga watch lists
|
|
||||||
*
|
|
||||||
* PHP version 7.4
|
|
||||||
*
|
|
||||||
* @package HummingbirdAnimeClient
|
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
|
||||||
* @copyright 2015 - 2020 Timothy J. Warren
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
|
||||||
* @version 5
|
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Aviat\AnimeClient\API;
|
|
||||||
|
|
||||||
use Aviat\AnimeClient\API\Kitsu\Enum\AnimeAiringStatus;
|
|
||||||
use DateTimeImmutable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data massaging helpers for the Kitsu API
|
|
||||||
*/
|
|
||||||
final class Kitsu {
|
|
||||||
public const AUTH_URL = 'https://kitsu.io/api/oauth/token';
|
|
||||||
public const AUTH_USER_ID_KEY = 'kitsu-auth-userid';
|
|
||||||
public const AUTH_TOKEN_CACHE_KEY = 'kitsu-auth-token';
|
|
||||||
public const AUTH_TOKEN_EXP_CACHE_KEY = 'kitsu-auth-token-expires';
|
|
||||||
public const AUTH_TOKEN_REFRESH_CACHE_KEY = 'kitsu-auth-token-refresh';
|
|
||||||
public const ANIME_HISTORY_LIST_CACHE_KEY = 'kitsu-anime-history-list';
|
|
||||||
public const MANGA_HISTORY_LIST_CACHE_KEY = 'kitsu-manga-history-list';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether an anime is airing, finished airing, or has not yet aired
|
|
||||||
*
|
|
||||||
* @param string $startDate
|
|
||||||
* @param string $endDate
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function getAiringStatus(string $startDate = NULL, string $endDate = NULL): string
|
|
||||||
{
|
|
||||||
$startAirDate = new DateTimeImmutable($startDate ?? 'tomorrow');
|
|
||||||
$endAirDate = new DateTimeImmutable($endDate ?? 'next year');
|
|
||||||
$now = new DateTimeImmutable();
|
|
||||||
|
|
||||||
$isDoneAiring = $now > $endAirDate;
|
|
||||||
$isCurrentlyAiring = ($now > $startAirDate) && ! $isDoneAiring;
|
|
||||||
|
|
||||||
if ($isCurrentlyAiring)
|
|
||||||
{
|
|
||||||
return AnimeAiringStatus::AIRING;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($isDoneAiring)
|
|
||||||
{
|
|
||||||
return AnimeAiringStatus::FINISHED_AIRING;
|
|
||||||
}
|
|
||||||
|
|
||||||
return AnimeAiringStatus::NOT_YET_AIRED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reorganize streaming links
|
|
||||||
*
|
|
||||||
* @param array $included
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function parseStreamingLinks(array $included): array
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
( ! array_key_exists('streamingLinks', $included)) ||
|
|
||||||
count($included['streamingLinks']) === 0
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$links = [];
|
|
||||||
|
|
||||||
foreach ($included['streamingLinks'] as $streamingLink)
|
|
||||||
{
|
|
||||||
$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[] = [
|
|
||||||
'meta' => static::getServiceMetaData($host),
|
|
||||||
'link' => $streamingLink['url'],
|
|
||||||
'subs' => $streamingLink['subs'],
|
|
||||||
'dubs' => $streamingLink['dubs']
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
usort($links, fn ($a, $b) => $a['meta']['name'] <=> $b['meta']['name']);
|
|
||||||
|
|
||||||
return $links;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reorganize streaming links for the current list item
|
|
||||||
*
|
|
||||||
* @param array $included
|
|
||||||
* @param string $animeId
|
|
||||||
* @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)
|
|
||||||
{
|
|
||||||
return static::parseStreamingLinks($anime['relationships']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $links;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the list of titles
|
|
||||||
*
|
|
||||||
* @param array $data
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function getTitles(array $data): array
|
|
||||||
{
|
|
||||||
$raw = array_unique([
|
|
||||||
$data['canonicalTitle'],
|
|
||||||
...array_values($data['titles']),
|
|
||||||
...array_values($data['abbreviatedTitles'] ?? []),
|
|
||||||
]);
|
|
||||||
|
|
||||||
return array_diff($raw,[$data['canonicalTitle']]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter out duplicate and very similar names from
|
|
||||||
*
|
|
||||||
* @param array $data The 'attributes' section of the api data response
|
|
||||||
* @return array List of alternate titles
|
|
||||||
*/
|
|
||||||
public static function filterTitles(array $data): array
|
|
||||||
{
|
|
||||||
// The 'canonical' title is always returned
|
|
||||||
$valid = [$data['canonicalTitle']];
|
|
||||||
|
|
||||||
if (array_key_exists('titles', $data) && is_array($data['titles']))
|
|
||||||
{
|
|
||||||
foreach($data['titles'] as $alternateTitle)
|
|
||||||
{
|
|
||||||
if (self::titleIsUnique($alternateTitle, $valid))
|
|
||||||
{
|
|
||||||
$valid[] = $alternateTitle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the name and logo for the streaming service of the current link
|
|
||||||
*
|
|
||||||
* @param string $hostname
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected static function getServiceMetaData(string $hostname = NULL): array
|
|
||||||
{
|
|
||||||
$hostname = str_replace('www.', '', $hostname);
|
|
||||||
|
|
||||||
$serviceMap = [
|
|
||||||
'amazon.com' => [
|
|
||||||
'name' => 'Amazon Prime',
|
|
||||||
'link' => TRUE,
|
|
||||||
'image' => 'streaming-logos/amazon.svg',
|
|
||||||
],
|
|
||||||
'crunchyroll.com' => [
|
|
||||||
'name' => 'Crunchyroll',
|
|
||||||
'link' => TRUE,
|
|
||||||
'image' => 'streaming-logos/crunchyroll.svg',
|
|
||||||
],
|
|
||||||
'daisuki.net' => [
|
|
||||||
'name' => 'Daisuki',
|
|
||||||
'link' => TRUE,
|
|
||||||
'image' => 'streaming-logos/daisuki.svg'
|
|
||||||
],
|
|
||||||
'funimation.com' => [
|
|
||||||
'name' => 'Funimation',
|
|
||||||
'link' => TRUE,
|
|
||||||
'image' => 'streaming-logos/funimation.svg',
|
|
||||||
],
|
|
||||||
'hidive.com' => [
|
|
||||||
'name' => 'Hidive',
|
|
||||||
'link' => TRUE,
|
|
||||||
'image' => 'streaming-logos/hidive.svg',
|
|
||||||
],
|
|
||||||
'hulu.com' => [
|
|
||||||
'name' => 'Hulu',
|
|
||||||
'link' => TRUE,
|
|
||||||
'image' => 'streaming-logos/hulu.svg',
|
|
||||||
],
|
|
||||||
'tubitv.com' => [
|
|
||||||
'name' => 'TubiTV',
|
|
||||||
'link' => TRUE,
|
|
||||||
'image' => 'streaming-logos/tubitv.svg',
|
|
||||||
],
|
|
||||||
'viewster.com' => [
|
|
||||||
'name' => 'Viewster',
|
|
||||||
'link' => TRUE,
|
|
||||||
'image' => 'streaming-logos/viewster.svg'
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
if (array_key_exists($hostname, $serviceMap))
|
|
||||||
{
|
|
||||||
return $serviceMap[$hostname];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default to Netflix, because the API links are broken,
|
|
||||||
// and there's no other real identifier for Netflix
|
|
||||||
return [
|
|
||||||
'name' => 'Netflix',
|
|
||||||
'link' => FALSE,
|
|
||||||
'image' => 'streaming-logos/netflix.svg',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if an alternate title is unique enough to list
|
|
||||||
*
|
|
||||||
* @param string $title
|
|
||||||
* @param array $existingTitles
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private static function titleIsUnique(string $title = NULL, array $existingTitles = []): bool
|
|
||||||
{
|
|
||||||
if (empty($title))
|
|
||||||
{
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($existingTitles as $existing)
|
|
||||||
{
|
|
||||||
$isSubset = mb_substr_count($existing, $title) > 0;
|
|
||||||
$diff = levenshtein(mb_strtolower($existing), mb_strtolower($title));
|
|
||||||
|
|
||||||
if ($diff <= 4 || $isSubset || mb_strlen($title) > 45 || mb_strlen($existing) > 50)
|
|
||||||
{
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"name": "Kitsu Schema",
|
||||||
|
"schemaPath": "schema.graphql",
|
||||||
|
"extensions": {
|
||||||
|
"endpoints": {
|
||||||
|
"Kitsu": {
|
||||||
|
"url": "https://kitsu.io/api/graphql",
|
||||||
|
"headers": {
|
||||||
|
"user-agent": "JS GraphQL"
|
||||||
|
},
|
||||||
|
"introspect": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue