Merge branch 'develop' into 'master'
Merge develop into master See merge request !18
This commit is contained in:
commit
e632843bab
6
.gitignore
vendored
6
.gitignore
vendored
@ -13,7 +13,7 @@ composer.lock
|
|||||||
*.sqlite
|
*.sqlite
|
||||||
*.db
|
*.db
|
||||||
*.sqlite3
|
*.sqlite3
|
||||||
docs/*
|
apidocs/**
|
||||||
tests/test_data/sessions/*
|
tests/test_data/sessions/*
|
||||||
cache.properties
|
cache.properties
|
||||||
build/**
|
build/**
|
||||||
@ -26,3 +26,7 @@ phinx.yml
|
|||||||
.idea/
|
.idea/
|
||||||
Caddyfile
|
Caddyfile
|
||||||
build/humbuglog.txt
|
build/humbuglog.txt
|
||||||
|
public/images/anime/**
|
||||||
|
public/images/avatars/**
|
||||||
|
public/images/manga/**
|
||||||
|
public/images/characters/**
|
@ -45,6 +45,10 @@ Update your anime/manga list on Kitsu.io and MyAnimeList.net
|
|||||||
3. Configure settings in `app/config/config.toml` to your liking
|
3. Configure settings in `app/config/config.toml` to your liking
|
||||||
4. Create the following directories if they don't exist, and make sure they are world writable
|
4. Create the following directories if they don't exist, and make sure they are world writable
|
||||||
* public/js/cache
|
* public/js/cache
|
||||||
|
* public/images/avatars
|
||||||
|
* public/images/anime
|
||||||
|
* public/images/characters
|
||||||
|
* public/images/manga
|
||||||
5. Make sure the `console` script is executable
|
5. Make sure the `console` script is executable
|
||||||
|
|
||||||
### Using MAL API
|
### Using MAL API
|
||||||
|
@ -18,46 +18,6 @@
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| CSS Folder
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| The folder where css files exist, in relation to the document root
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
'css_root' => 'css/',
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Path from
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Path fragment to rewrite in css files
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
'path_from' => '',
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Path to
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| The path fragment replacement for the css files
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
'path_to' => '',
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| CSS Groups file
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| The file where the css groups are configured
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
'css_groups_file' => __DIR__ . '/minify_css_groups.php',
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| JS Folder
|
| JS Folder
|
||||||
@ -70,13 +30,40 @@ return [
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| JS Groups file
|
| JS Groups
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| The file where the javascript groups are configured
|
| Config array for javascript files to concatenate and minify
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
'js_groups_file' => __DIR__ . '/minify_js_groups.php',
|
'groups' => [
|
||||||
|
'base' => [
|
||||||
|
'base/classList.js',
|
||||||
|
'base/AnimeClient.js',
|
||||||
|
],
|
||||||
|
'event' => [
|
||||||
|
'base/events.js',
|
||||||
|
],
|
||||||
|
'table' => [
|
||||||
|
'base/sort_tables.js',
|
||||||
|
],
|
||||||
|
'table_edit' => [
|
||||||
|
'base/sort_tables.js',
|
||||||
|
'anime_edit.js',
|
||||||
|
'manga_edit.js',
|
||||||
|
],
|
||||||
|
'edit' => [
|
||||||
|
'anime_edit.js',
|
||||||
|
'manga_edit.js',
|
||||||
|
],
|
||||||
|
'anime_collection' => [
|
||||||
|
'lib/mustache.js',
|
||||||
|
'anime_collection.js',
|
||||||
|
],
|
||||||
|
'manga_collection' => [
|
||||||
|
'lib/mustache.js',
|
||||||
|
'manga_collection.js',
|
||||||
|
],
|
||||||
|
]
|
||||||
];
|
];
|
||||||
// End of minify_config.php
|
// End of minify_config.php
|
@ -1,40 +0,0 @@
|
|||||||
<?php declare(strict_types=1);
|
|
||||||
/**
|
|
||||||
* Hummingbird Anime List Client
|
|
||||||
*
|
|
||||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
|
||||||
*
|
|
||||||
* PHP version 7
|
|
||||||
*
|
|
||||||
* @package HummingbirdAnimeClient
|
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
|
||||||
* @copyright 2015 - 2017 Timothy J. Warren
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
|
||||||
* @version 4.0
|
|
||||||
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the config array for css files to concatenate and minify
|
|
||||||
*/
|
|
||||||
return [
|
|
||||||
/*-----
|
|
||||||
Css
|
|
||||||
-----*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
For each group create an array like so
|
|
||||||
|
|
||||||
'my_group' => array(
|
|
||||||
'path/to/css/file1.css',
|
|
||||||
'path/to/css/file2.css'
|
|
||||||
),
|
|
||||||
*/
|
|
||||||
'base' => [
|
|
||||||
'base.css'
|
|
||||||
]
|
|
||||||
];
|
|
||||||
// End of css_groups.php
|
|
@ -1,53 +0,0 @@
|
|||||||
<?php declare(strict_types=1);
|
|
||||||
/**
|
|
||||||
* Hummingbird Anime List Client
|
|
||||||
*
|
|
||||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
|
||||||
*
|
|
||||||
* PHP version 7
|
|
||||||
*
|
|
||||||
* @package HummingbirdAnimeClient
|
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
|
||||||
* @copyright 2015 - 2017 Timothy J. Warren
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
|
||||||
* @version 4.0
|
|
||||||
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the config array for javascript files to concatenate and minify
|
|
||||||
*/
|
|
||||||
return [
|
|
||||||
'base' => [
|
|
||||||
'base/classList.js',
|
|
||||||
'base/AnimeClient.js',
|
|
||||||
],
|
|
||||||
'event' => [
|
|
||||||
'base/events.js',
|
|
||||||
],
|
|
||||||
'table' => [
|
|
||||||
'base/sort_tables.js',
|
|
||||||
],
|
|
||||||
'table_edit' => [
|
|
||||||
'base/sort_tables.js',
|
|
||||||
'anime_edit.js',
|
|
||||||
'manga_edit.js',
|
|
||||||
],
|
|
||||||
'edit' => [
|
|
||||||
'anime_edit.js',
|
|
||||||
'manga_edit.js',
|
|
||||||
],
|
|
||||||
'anime_collection' => [
|
|
||||||
'lib/mustache.js',
|
|
||||||
'anime_collection.js',
|
|
||||||
],
|
|
||||||
'manga_collection' => [
|
|
||||||
'lib/mustache.js',
|
|
||||||
'manga_collection.js',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
// End of js_groups.php
|
|
@ -147,6 +147,16 @@ return [
|
|||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
// Default / Shared routes
|
// Default / Shared routes
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
|
'image_proxy' => [
|
||||||
|
'path' => '/public/images/{type}/{file}',
|
||||||
|
'action' => 'images',
|
||||||
|
'controller' => DEFAULT_CONTROLLER,
|
||||||
|
'verb' => 'get',
|
||||||
|
'tokens' => [
|
||||||
|
'type' => '[a-z0-9\-]+',
|
||||||
|
'file' => '[a-z0-9\-]+\.[a-z]{3}'
|
||||||
|
]
|
||||||
|
],
|
||||||
'cache_purge' => [
|
'cache_purge' => [
|
||||||
'path' => '/cache_purge',
|
'path' => '/cache_purge',
|
||||||
'action' => 'clearCache',
|
'action' => 'clearCache',
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<?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>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<img src="<?= $item['anime']['image'] ?>" alt="" />
|
<img src="<?= $urlGenerator->assetUrl("images/anime/{$item['anime']['id']}.jpg") ?>" alt="" />
|
||||||
<div class="name">
|
<div class="name">
|
||||||
<a href="<?= $url->generate('anime.details', ['id' => $item['anime']['slug']]); ?>">
|
<a href="<?= $url->generate('anime.details', ['id' => $item['anime']['slug']]); ?>">
|
||||||
<?= array_shift($item['anime']['titles']) ?>
|
<?= array_shift($item['anime']['titles']) ?>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<main class="details fixed">
|
<main class="details fixed">
|
||||||
<section class="flex flex-no-wrap">
|
<section class="flex flex-no-wrap">
|
||||||
<div>
|
<div>
|
||||||
<img class="cover" width="402" height="284" src="<?= $data['cover_image'] ?>" alt="" />
|
<img class="cover" width="402" height="284" src="<?= $urlGenerator->assetUrl("images/anime/{$data['id']}.jpg") ?>" alt="" />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<table class="media_details">
|
<table class="media_details">
|
||||||
@ -79,8 +79,8 @@
|
|||||||
|
|
||||||
<?php if (count($characters) > 0): ?>
|
<?php if (count($characters) > 0): ?>
|
||||||
<h2>Characters</h2>
|
<h2>Characters</h2>
|
||||||
<section class="media-wrap">
|
<section class="align_left media-wrap">
|
||||||
<?php foreach($characters as $char): ?>
|
<?php foreach($characters as $id => $char): ?>
|
||||||
<?php if ( ! empty($char['image']['original'])): ?>
|
<?php if ( ! empty($char['image']['original'])): ?>
|
||||||
<article class="character">
|
<article class="character">
|
||||||
<?php $link = $url->generate('character', ['slug' => $char['slug']]) ?>
|
<?php $link = $url->generate('character', ['slug' => $char['slug']]) ?>
|
||||||
@ -88,7 +88,7 @@
|
|||||||
<?= $helper->a($link, $char['name']); ?>
|
<?= $helper->a($link, $char['name']); ?>
|
||||||
</div>
|
</div>
|
||||||
<a href="<?= $link ?>">
|
<a href="<?= $link ?>">
|
||||||
<?= $helper->img($char['image']['original'], [
|
<?= $helper->img($urlGenerator->assetUrl("images/characters/{$id}.jpg"), [
|
||||||
'width' => '225'
|
'width' => '225'
|
||||||
]) ?>
|
]) ?>
|
||||||
</a>
|
</a>
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
<article class="media">
|
<article class="media">
|
||||||
<?= $helper->img($item['anime']['image']); ?>
|
<?= $helper->img($urlGenerator->assetUrl('images/anime', "{$item['anime']['id']}.jpg")) ?>
|
||||||
</article>
|
</article>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -1,12 +1,126 @@
|
|||||||
<main class="details fixed">
|
<?php use Aviat\AnimeClient\API\Kitsu; ?>
|
||||||
|
<main class="details">
|
||||||
<section class="flex flex-no-wrap">
|
<section class="flex flex-no-wrap">
|
||||||
<div>
|
<div>
|
||||||
<img class="cover" width="284" src="<?= $data['image']['original'] ?>" alt="" />
|
<img class="cover" width="284" src="<?= $urlGenerator->assetUrl("images/characters/{$data[0]['id']}.jpg") ?>" alt="" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2><?= $data['name'] ?></h2>
|
<h2><?= $data[0]['attributes']['name'] ?></h2>
|
||||||
|
|
||||||
<p><?= $data['description'] ?></p>
|
<p class="description"><?= $data[0]['attributes']['description'] ?></p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<?php if (array_key_exists('anime', $data['included']) || array_key_exists('manga', $data['included'])): ?>
|
||||||
|
<h3>Media</h3>
|
||||||
|
<section class="flex flex-no-wrap">
|
||||||
|
<?php if (array_key_exists('anime', $data['included'])): ?>
|
||||||
|
<div>
|
||||||
|
<h4>Anime</h4>
|
||||||
|
<section class="align_left media-wrap">
|
||||||
|
<?php foreach($data['included']['anime'] as $id => $anime): ?>
|
||||||
|
<article class="media">
|
||||||
|
<?php
|
||||||
|
$link = $url->generate('anime.details', ['id' => $anime['attributes']['slug']]);
|
||||||
|
$titles = Kitsu::filterTitles($anime['attributes']);
|
||||||
|
?>
|
||||||
|
<a href="<?= $link ?>">
|
||||||
|
<img src="<?= $urlGenerator->assetUrl("images/anime/{$id}.jpg") ?>" width="220" alt="" />
|
||||||
|
</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>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php if (array_key_exists('manga', $data['included'])): ?>
|
||||||
|
<div>
|
||||||
|
<h4>Manga</h4>
|
||||||
|
<section class="align_left media-wrap">
|
||||||
|
|
||||||
|
<?php foreach($data['included']['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 ?>">
|
||||||
|
<img src="<?= $urlGenerator->assetUrl("images/manga/{$id}.jpg") ?>" width="220" alt="" />
|
||||||
|
</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>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
</section>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<?php if ($castCount > 0): ?>
|
||||||
|
<h3>Castings</h3>
|
||||||
|
<?php foreach($castings as $role => $entries): ?>
|
||||||
|
<h4><?= $role ?></h4>
|
||||||
|
<?php foreach($entries as $language => $casting): ?>
|
||||||
|
<h5><?= $language ?></h5>
|
||||||
|
<table class="min-table">
|
||||||
|
<tr>
|
||||||
|
<th>Cast Member</th>
|
||||||
|
<th>Series</th>
|
||||||
|
</tr>
|
||||||
|
<?php foreach($casting as $c):?>
|
||||||
|
<tr>
|
||||||
|
<td style="width:229px">
|
||||||
|
<article class="character">
|
||||||
|
<img src="<?= $c['person']['image'] ?>" alt="" />
|
||||||
|
<div class="name">
|
||||||
|
<?= $c['person']['name'] ?>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<section class="align_left media-wrap">
|
||||||
|
<?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 ?>">
|
||||||
|
<img src="<?= $series['attributes']['posterImage']['small'] ?>" width="220" alt="" />
|
||||||
|
</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>
|
||||||
|
<?php endforeach ?>
|
||||||
|
<?php endforeach ?>
|
||||||
|
<?php endif ?>
|
||||||
|
</section>
|
||||||
</main>
|
</main>
|
@ -11,7 +11,7 @@
|
|||||||
<section class="media-wrap">
|
<section class="media-wrap">
|
||||||
<?php foreach($items as $item): ?>
|
<?php foreach($items as $item): ?>
|
||||||
<article class="media" id="a-<?= $item['hummingbird_id'] ?>">
|
<article class="media" id="a-<?= $item['hummingbird_id'] ?>">
|
||||||
<img src="https://media.kitsu.io/anime/poster_images/<?= $item['hummingbird_id'] ?>/small.jpg"
|
<img src="<?= $urlGenerator->assetUrl("images/anime/{$item['hummingbird_id']}.jpg") ?>"
|
||||||
alt="<?= $item['title'] ?> cover image" />
|
alt="<?= $item['title'] ?> cover image" />
|
||||||
<div class="name">
|
<div class="name">
|
||||||
<a href="<?= $url->generate('anime.details', ['id' => $item['slug']]) ?>">
|
<a href="<?= $url->generate('anime.details', ['id' => $item['slug']]) ?>">
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<meta http-equiv="cache-control" content="no-store" />
|
<meta http-equiv="cache-control" content="no-store" />
|
||||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self'" />
|
<meta http-equiv="Content-Security-Policy" content="script-src 'self'" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=0" />
|
||||||
<link rel="stylesheet" href="<?= $urlGenerator->assetUrl('css.php/g/base/debug') ?>" />
|
<link rel="stylesheet" href="<?= $urlGenerator->assetUrl('css/app.min.css') ?>" />
|
||||||
<link rel="icon" href="<?= $urlGenerator->assetUrl('images/icons/favicon.ico') ?>" />
|
<link rel="icon" href="<?= $urlGenerator->assetUrl('images/icons/favicon.ico') ?>" />
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="<?= $urlGenerator->assetUrl('images/icons/apple-icon-57x57.png') ?>">
|
<link rel="apple-touch-icon" sizes="57x57" href="<?= $urlGenerator->assetUrl('images/icons/apple-icon-57x57.png') ?>">
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="<?= $urlGenerator->assetUrl('images/icons/apple-icon-60x60.png') ?>">
|
<link rel="apple-touch-icon" sizes="60x60" href="<?= $urlGenerator->assetUrl('images/icons/apple-icon-60x60.png') ?>">
|
||||||
@ -21,17 +21,19 @@
|
|||||||
<link rel="icon" type="image/png" sizes="32x32" href="<?= $urlGenerator->assetUrl('images/icons/favicon-32x32.png') ?>">
|
<link rel="icon" type="image/png" sizes="32x32" href="<?= $urlGenerator->assetUrl('images/icons/favicon-32x32.png') ?>">
|
||||||
<link rel="icon" type="image/png" sizes="96x96" href="<?= $urlGenerator->assetUrl('images/icons/favicon-96x96.png') ?>">
|
<link rel="icon" type="image/png" sizes="96x96" href="<?= $urlGenerator->assetUrl('images/icons/favicon-96x96.png') ?>">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="<?= $urlGenerator->assetUrl('images/icons/favicon-16x16.png') ?>">
|
<link rel="icon" type="image/png" sizes="16x16" href="<?= $urlGenerator->assetUrl('images/icons/favicon-16x16.png') ?>">
|
||||||
<link rel="manifest" href="/manifest.json">
|
|
||||||
<script defer="defer" src="<?= $urlGenerator->assetUrl('js.php/g/base') ?>"></script>
|
<script defer="defer" src="<?= $urlGenerator->assetUrl('js.php/g/base') ?>"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="<?= $escape->attr($url_type) ?> list">
|
<body class="<?= $escape->attr($url_type) ?> list">
|
||||||
<header>
|
<header>
|
||||||
<?php include 'main-menu.php' ?>
|
<?php
|
||||||
<?php if(isset($message) && is_array($message)):
|
include 'main-menu.php';
|
||||||
|
if(isset($message) && is_array($message))
|
||||||
|
{
|
||||||
foreach($message as $m)
|
foreach($message as $m)
|
||||||
{
|
{
|
||||||
extract($m);
|
extract($m);
|
||||||
include 'message.php';
|
include 'message.php';
|
||||||
}
|
}
|
||||||
endif ?>
|
}
|
||||||
|
?>
|
||||||
</header>
|
</header>
|
@ -1,25 +1,41 @@
|
|||||||
<?php declare(strict_types=1); namespace Aviat\AnimeClient; ?>
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient;
|
||||||
|
|
||||||
|
$whose = $config->get('whose_list') . "'s ";
|
||||||
|
$lastSegment = $urlGenerator->lastSegment();
|
||||||
|
$extraSegment = $lastSegment === 'list' ? '/list' : '';
|
||||||
|
|
||||||
|
?>
|
||||||
<h1 class="flex flex-align-end flex-wrap">
|
<h1 class="flex flex-align-end flex-wrap">
|
||||||
<span class="flex-no-wrap grow-1">
|
<span class="flex-no-wrap grow-1">
|
||||||
<?php if(strpos($route_path, 'collection') === FALSE): ?>
|
<?php if(strpos($route_path, 'collection') === FALSE): ?>
|
||||||
<a href="<?= $escape->attr($urlGenerator->defaultUrl($url_type)) ?>">
|
<?= $helper->a(
|
||||||
<?= $config->get('whose_list') ?>'s <?= ucfirst($url_type) ?> List
|
$urlGenerator->defaultUrl($url_type),
|
||||||
</a>
|
$whose . ucfirst($url_type) . ' List'
|
||||||
|
) ?>
|
||||||
<?php if($config->get("show_{$url_type}_collection")): ?>
|
<?php if($config->get("show_{$url_type}_collection")): ?>
|
||||||
[<a href="<?= $url->generate('collection.view') ?>"><?= ucfirst($url_type) ?> Collection</a>]
|
[<?= $helper->a(
|
||||||
|
$url->generate('collection.view') . $extraSegment,
|
||||||
|
ucfirst($url_type) . ' Collection'
|
||||||
|
) ?>]
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
[<a href="<?= $urlGenerator->defaultUrl($other_type) ?>"><?= ucfirst($other_type) ?> List</a>]
|
[<?= $helper->a(
|
||||||
|
$urlGenerator->defaultUrl($other_type) . $extraSegment,
|
||||||
|
ucfirst($other_type) . ' List'
|
||||||
|
) ?>]
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<a href="<?= $url->generate('collection.view') ?>">
|
<?= $whose . ucfirst($url_type) . ' Collection' ?>
|
||||||
<?= $config->get('whose_list') ?>'s <?= ucfirst($url_type) ?> Collection
|
[<?= $helper->a($urlGenerator->defaultUrl('anime') . $extraSegment, 'Anime List') ?>]
|
||||||
</a>
|
[<?= $helper->a($urlGenerator->defaultUrl('manga') . $extraSegment, 'Manga List') ?>]
|
||||||
[<a href="<?= $urlGenerator->defaultUrl('anime') ?>">Anime List</a>]
|
|
||||||
[<a href="<?= $urlGenerator->defaultUrl('manga') ?>">Manga List</a>]
|
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</span>
|
</span>
|
||||||
<span class="flex-no-wrap small-font">
|
|
||||||
[<?= $helper->a($url->generate('user_info'), 'About '. $config->get('whose_list')) ?>]
|
<span class="flex-no-wrap small-font">[<?= $helper->a(
|
||||||
</span>
|
$url->generate('user_info'),
|
||||||
|
'About '. $config->get('whose_list')
|
||||||
|
) ?>]</span>
|
||||||
|
|
||||||
<?php if ($auth->isAuthenticated()): ?>
|
<?php if ($auth->isAuthenticated()): ?>
|
||||||
<span class="flex-no-wrap"> </span>
|
<span class="flex-no-wrap"> </span>
|
||||||
<span class="flex-no-wrap small-font">
|
<span class="flex-no-wrap small-font">
|
||||||
@ -27,11 +43,16 @@
|
|||||||
</span>
|
</span>
|
||||||
<span class="flex-no-wrap"> </span>
|
<span class="flex-no-wrap"> </span>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<span class="flex-no-wrap small-font">
|
<span class="flex-no-wrap small-font">
|
||||||
<?php if ($auth->isAuthenticated()): ?>
|
<?php if ($auth->isAuthenticated()): ?>
|
||||||
<a class="bracketed" href="<?= $url->generate('logout') ?>">Logout</a>
|
<?= $helper->a(
|
||||||
|
$url->generate('logout'),
|
||||||
|
'Logout',
|
||||||
|
['class' => 'bracketed']
|
||||||
|
) ?>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
[<a href="<?= $url->generate('login'); ?>"><?= $config->get('whose_list') ?>'s Login</a>]
|
[<?= $helper->a($url->generate('login'), "{$whose} Login") ?>]
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</span>
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
@ -40,8 +61,8 @@
|
|||||||
<?= $helper->menu($menu_name) ?>
|
<?= $helper->menu($menu_name) ?>
|
||||||
<br />
|
<br />
|
||||||
<ul>
|
<ul>
|
||||||
<li class="<?= Util::isNotSelected('list', $urlGenerator->lastSegment()) ?>"><a href="<?= $urlGenerator->url($route_path) ?>">Cover View</a></li>
|
<li class="<?= Util::isNotSelected('list', $lastSegment) ?>"><a href="<?= $urlGenerator->url($route_path) ?>">Cover View</a></li>
|
||||||
<li class="<?= Util::isSelected('list', $urlGenerator->lastSegment()) ?>"><a href="<?= $urlGenerator->url("{$route_path}/list") ?>">List View</a></li>
|
<li class="<?= Util::isSelected('list', $lastSegment) ?>"><a href="<?= $urlGenerator->url("{$route_path}/list") ?>">List View</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
<?php /* <button class="plus_one_volume">+1 Volume</button> */ ?>
|
<?php /* <button class="plus_one_volume">+1 Volume</button> */ ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<img src="<?= $escape->attr($item['manga']['image']) ?>" />
|
<img src="<?= $urlGenerator->assetUrl('images/manga', "{$item['manga']['id']}.jpg") ?>" />
|
||||||
<div class="name">
|
<div class="name">
|
||||||
<a href="<?= $url->generate('manga.details', ['id' => $item['manga']['slug']]) ?>">
|
<a href="<?= $url->generate('manga.details', ['id' => $item['manga']['slug']]) ?>">
|
||||||
<?= $escape->html(array_shift($item['manga']['titles'])) ?>
|
<?= $escape->html(array_shift($item['manga']['titles'])) ?>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<main class="details fixed">
|
<main class="details fixed">
|
||||||
<section class="flex flex-no-wrap">
|
<section class="flex flex-no-wrap">
|
||||||
<div>
|
<div>
|
||||||
<img class="cover" src="<?= $data['cover_image'] ?>" alt="<?= $data['title'] ?> cover image" />
|
<img class="cover" src="<?= $urlGenerator->assetUrl('images/manga', "{$data['id']}.jpg") ?>" alt="<?= $data['title'] ?> cover image" />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<table>
|
<table>
|
||||||
@ -39,7 +39,7 @@
|
|||||||
<?php if (count($characters) > 0): ?>
|
<?php if (count($characters) > 0): ?>
|
||||||
<h2>Characters</h2>
|
<h2>Characters</h2>
|
||||||
<section class="media-wrap">
|
<section class="media-wrap">
|
||||||
<?php foreach($characters as $char): ?>
|
<?php foreach($characters as $id => $char): ?>
|
||||||
<?php if ( ! empty($char['image']['original'])): ?>
|
<?php if ( ! empty($char['image']['original'])): ?>
|
||||||
<article class="character">
|
<article class="character">
|
||||||
<?php $link = $url->generate('character', ['slug' => $char['slug']]) ?>
|
<?php $link = $url->generate('character', ['slug' => $char['slug']]) ?>
|
||||||
@ -47,7 +47,7 @@
|
|||||||
<?= $helper->a($link, $char['name']); ?>
|
<?= $helper->a($link, $char['name']); ?>
|
||||||
</div>
|
</div>
|
||||||
<a href="<?= $link ?>">
|
<a href="<?= $link ?>">
|
||||||
<?= $helper->img($char['image']['original'], [
|
<?= $helper->img($urlGenerator->assetUrl('images/characters', "{$id}.jpg"), [
|
||||||
'width' => '225'
|
'width' => '225'
|
||||||
]) ?>
|
]) ?>
|
||||||
</a>
|
</a>
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
<article class="media">
|
<article class="media">
|
||||||
<?= $helper->img($item['manga']['image']); ?>
|
<?= $helper->img($urlGenerator->assetUrl('images/manga', "{$item['manga']['id']}.jpg")); ?>
|
||||||
</article>
|
</article>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -9,7 +9,12 @@
|
|||||||
<?= $attributes['name'] ?>
|
<?= $attributes['name'] ?>
|
||||||
</a>
|
</a>
|
||||||
</h2>
|
</h2>
|
||||||
<img src="<?= $attributes['avatar']['original'] ?>" alt="" />
|
<?php
|
||||||
|
$file = basename(parse_url($attributes['avatar']['original'], \PHP_URL_PATH));
|
||||||
|
$parts = explode('.', $file);
|
||||||
|
$ext = end($parts);
|
||||||
|
?>
|
||||||
|
<img src="<?= $urlGenerator->assetUrl('images/avatars', "{$data['id']}.{$ext}") ?>" alt="" />
|
||||||
</center>
|
</center>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
@ -65,13 +70,13 @@
|
|||||||
<?php if ( ! empty($favorites['characters'])): ?>
|
<?php if ( ! empty($favorites['characters'])): ?>
|
||||||
<h4>Favorite Characters</h4>
|
<h4>Favorite Characters</h4>
|
||||||
<section class="media-wrap">
|
<section class="media-wrap">
|
||||||
<?php foreach($favorites['characters'] as $char): ?>
|
<?php foreach($favorites['characters'] as $id => $char): ?>
|
||||||
<?php if ( ! empty($char['image']['original'])): ?>
|
<?php if ( ! empty($char['image']['original'])): ?>
|
||||||
<article class="small_character">
|
<article class="small_character">
|
||||||
<?php $link = $url->generate('character', ['slug' => $char['slug']]) ?>
|
<?php $link = $url->generate('character', ['slug' => $char['slug']]) ?>
|
||||||
<div class="name"><?= $helper->a($link, $char['name']); ?></div>
|
<div class="name"><?= $helper->a($link, $char['name']); ?></div>
|
||||||
<a href="<?= $link ?>">
|
<a href="<?= $link ?>">
|
||||||
<?= $helper->img($char['image']['original']) ?>
|
<?= $helper->img($urlGenerator->assetUrl('images/characters', "{$char['id']}.jpg")) ?>
|
||||||
</a>
|
</a>
|
||||||
</article>
|
</article>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
@ -88,7 +93,7 @@
|
|||||||
$titles = Kitsu::filterTitles($anime);
|
$titles = Kitsu::filterTitles($anime);
|
||||||
?>
|
?>
|
||||||
<a href="<?= $link ?>">
|
<a href="<?= $link ?>">
|
||||||
<img src="<?= $anime['posterImage']['small'] ?>" width="220" alt="" />
|
<img src="<?= $urlGenerator->assetUrl('images/anime', "{$anime['id']}.jpg") ?>" width="220" alt="" />
|
||||||
</a>
|
</a>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
<a href="<?= $link ?>">
|
<a href="<?= $link ?>">
|
||||||
@ -112,7 +117,7 @@
|
|||||||
$titles = Kitsu::filterTitles($manga);
|
$titles = Kitsu::filterTitles($manga);
|
||||||
?>
|
?>
|
||||||
<a href="<?= $link ?>">
|
<a href="<?= $link ?>">
|
||||||
<img src="<?= $manga['posterImage']['small'] ?>" width="220" alt="" />
|
<img src="<?= $urlGenerator->assetUrl('images/manga', "{$manga['id']}.jpg") ?>" width="220" alt="" />
|
||||||
</a>
|
</a>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
<a href="<?= $link ?>">
|
<a href="<?= $link ?>">
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
"aura/router": "^3.0",
|
"aura/router": "^3.0",
|
||||||
"aura/session": "^2.0",
|
"aura/session": "^2.0",
|
||||||
"aviat/banker": "^1.0.0",
|
"aviat/banker": "^1.0.0",
|
||||||
"aviat/ion": "^2.0.0",
|
"aviat/ion": "^2.1.0",
|
||||||
"monolog/monolog": "^1.0",
|
"monolog/monolog": "^1.0",
|
||||||
"psr/http-message": "~1.0",
|
"psr/http-message": "~1.0",
|
||||||
"psr/log": "~1.0",
|
"psr/log": "~1.0",
|
||||||
@ -37,12 +37,13 @@
|
|||||||
"phploc/phploc": "^3.0",
|
"phploc/phploc": "^3.0",
|
||||||
"phpmd/phpmd": "^2.4",
|
"phpmd/phpmd": "^2.4",
|
||||||
"phpunit/phpunit": "^6.0",
|
"phpunit/phpunit": "^6.0",
|
||||||
"robmorgan/phinx": "~0.6.4",
|
"robmorgan/phinx": "^0.8.0",
|
||||||
"consolidation/robo": "~1.0",
|
"consolidation/robo": "~1.0",
|
||||||
"henrikbjorn/lurker": "^1.1.0",
|
"henrikbjorn/lurker": "^1.1.0",
|
||||||
"symfony/var-dumper": "^3.2",
|
"symfony/var-dumper": "^3.2",
|
||||||
"squizlabs/php_codesniffer": "^3.0.0@beta",
|
"squizlabs/php_codesniffer": "^3.0.0@beta",
|
||||||
"phpstan/phpstan": "^0.6.4"
|
"phpstan/phpstan": "^0.6.4",
|
||||||
|
"spatie/phpunit-snapshot-assertions": "^0.4.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vendor/bin/robo build",
|
"build": "vendor/bin/robo build",
|
||||||
|
4
console
4
console
@ -18,9 +18,9 @@ unset($APP_DIR);
|
|||||||
unset($SRC_DIR);
|
unset($SRC_DIR);
|
||||||
unset($CONF_DIR);
|
unset($CONF_DIR);
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Start console script
|
// Start console script
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
$console = new \ConsoleKit\Console([
|
$console = new \ConsoleKit\Console([
|
||||||
'cache-prime' => Command\CachePrime::class,
|
'cache-prime' => Command\CachePrime::class,
|
||||||
'cache-clear' => Command\CacheClear::class,
|
'cache-clear' => Command\CacheClear::class,
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"source": {
|
|
||||||
"directories": [
|
|
||||||
"src"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"timeout": 10,
|
|
||||||
"logs": {
|
|
||||||
"text": "build\/humbuglog.txt",
|
|
||||||
"json": "build\/humbug.json"
|
|
||||||
}
|
|
||||||
}
|
|
@ -56,7 +56,7 @@
|
|||||||
</collector>
|
</collector>
|
||||||
|
|
||||||
<!-- Configuration of generation process -->
|
<!-- Configuration of generation process -->
|
||||||
<generator output="docs">
|
<generator>
|
||||||
<!-- @output - (Base-)Directory to store output data in -->
|
<!-- @output - (Base-)Directory to store output data in -->
|
||||||
|
|
||||||
<!-- A generation process consists of one or more build tasks and of (optional) enrich sources -->
|
<!-- A generation process consists of one or more build tasks and of (optional) enrich sources -->
|
||||||
@ -117,10 +117,10 @@
|
|||||||
<!-- An engine and thus build node can have additional configuration child nodes, please check the documentation for the engine to find out more -->
|
<!-- An engine and thus build node can have additional configuration child nodes, please check the documentation for the engine to find out more -->
|
||||||
|
|
||||||
<!-- default engine "html" -->
|
<!-- default engine "html" -->
|
||||||
<build engine="html" output="html" />
|
<build engine="html" output="apidocs">
|
||||||
<!-- <template dir="${phpDox.home}/templates/html" /> -
|
<!-- <template dir="${phpDox.home}/templates/html" /> -->
|
||||||
<file extension="html" />
|
<file extension="html" />
|
||||||
</build> -->
|
</build>
|
||||||
|
|
||||||
</generator>
|
</generator>
|
||||||
</project>
|
</project>
|
||||||
|
29
public/css.js
Normal file
29
public/css.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Script for optimizing css
|
||||||
|
*/
|
||||||
|
const fs = require('fs');
|
||||||
|
const postcss = require('postcss');
|
||||||
|
const atImport = require('postcss-import');
|
||||||
|
const cssNext = require('postcss-cssnext');
|
||||||
|
const cssNano = require('cssnano');
|
||||||
|
|
||||||
|
const css = fs.readFileSync('css/base.css', 'utf8');
|
||||||
|
|
||||||
|
postcss()
|
||||||
|
.use(atImport())
|
||||||
|
.use(cssNext())
|
||||||
|
.use(cssNano({
|
||||||
|
autoprefixer: false,
|
||||||
|
colormin: false,
|
||||||
|
minifyFontValues: false,
|
||||||
|
options: {
|
||||||
|
sourcemap: false
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.process(css, {
|
||||||
|
from: 'css/base.css',
|
||||||
|
to: 'css/app.min.css'
|
||||||
|
}).then(result => {
|
||||||
|
fs.writeFileSync('css/app.min.css', result.css);
|
||||||
|
fs.writeFileSync('css/app.min.css.map', result.map);
|
||||||
|
});
|
180
public/css.php
180
public/css.php
@ -1,180 +0,0 @@
|
|||||||
<?php declare(strict_types=1);
|
|
||||||
/**
|
|
||||||
* Hummingbird Anime List Client
|
|
||||||
*
|
|
||||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
|
||||||
*
|
|
||||||
* PHP version 7
|
|
||||||
*
|
|
||||||
* @package HummingbirdAnimeClient
|
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
|
||||||
* @copyright 2015 - 2017 Timothy J. Warren
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
|
||||||
* @version 4.0
|
|
||||||
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Aviat\EasyMin;
|
|
||||||
|
|
||||||
require_once('./min.php');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple CSS Minifier
|
|
||||||
*/
|
|
||||||
class CSSMin extends BaseMin {
|
|
||||||
|
|
||||||
protected $cssRoot;
|
|
||||||
protected $pathFrom;
|
|
||||||
protected $pathTo;
|
|
||||||
protected $group;
|
|
||||||
protected $lastModified;
|
|
||||||
protected $requestedTime;
|
|
||||||
|
|
||||||
public function __construct(array $config, array $groups)
|
|
||||||
{
|
|
||||||
$group = $_GET['g'];
|
|
||||||
$this->cssRoot = $config['css_root'];
|
|
||||||
$this->pathFrom = $config['path_from'];
|
|
||||||
$this->pathTo = $config['path_to'];
|
|
||||||
$this->group = $groups[$group];
|
|
||||||
$this->lastModified = $this->getLastModified();
|
|
||||||
|
|
||||||
$this->send();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send the CSS
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function send()
|
|
||||||
{
|
|
||||||
if($this->lastModified >= $this->getIfModified() && $this->isNotDebug())
|
|
||||||
{
|
|
||||||
throw new FileNotChangedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
$css = ( ! array_key_exists('debug', $_GET))
|
|
||||||
? $this->compress($this->getCss())
|
|
||||||
: $this->getCss();
|
|
||||||
|
|
||||||
$this->output($css);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function for compressing the CSS as tightly as possible
|
|
||||||
*
|
|
||||||
* @param string $buffer
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function compress($buffer)
|
|
||||||
{
|
|
||||||
//Remove CSS comments
|
|
||||||
$buffer = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $buffer);
|
|
||||||
|
|
||||||
//Remove tabs, spaces, newlines, etc.
|
|
||||||
$buffer = preg_replace('`\s+`', ' ', $buffer);
|
|
||||||
$replace = [
|
|
||||||
' )' => ')',
|
|
||||||
') ' => ')',
|
|
||||||
' }' => '}',
|
|
||||||
'} ' => '}',
|
|
||||||
' {' => '{',
|
|
||||||
'{ ' => '{',
|
|
||||||
', ' => ',',
|
|
||||||
': ' => ':',
|
|
||||||
'; ' => ';',
|
|
||||||
];
|
|
||||||
|
|
||||||
//Eradicate every last space!
|
|
||||||
$buffer = trim(strtr($buffer, $replace));
|
|
||||||
$buffer = str_replace('{ ', '{', $buffer);
|
|
||||||
$buffer = str_replace('} ', '}', $buffer);
|
|
||||||
|
|
||||||
return $buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the most recent file modification date
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
protected function getLastModified()
|
|
||||||
{
|
|
||||||
$modified = [];
|
|
||||||
|
|
||||||
// Get all the css files, and concatenate them together
|
|
||||||
if(isset($this->group))
|
|
||||||
{
|
|
||||||
foreach($this->group as $file)
|
|
||||||
{
|
|
||||||
$newFile = realpath("{$this->cssRoot}{$file}");
|
|
||||||
$modified[] = filemtime($newFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Add this page for last modified check
|
|
||||||
$modified[] = filemtime(__FILE__);
|
|
||||||
|
|
||||||
//Get the latest modified date
|
|
||||||
rsort($modified);
|
|
||||||
|
|
||||||
return array_shift($modified);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the css to display
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function getCss()
|
|
||||||
{
|
|
||||||
$css = '';
|
|
||||||
|
|
||||||
foreach($this->group as $file)
|
|
||||||
{
|
|
||||||
$newFile = realpath("{$this->cssRoot}{$file}");
|
|
||||||
$css .= file_get_contents($newFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Correct paths that have changed due to concatenation
|
|
||||||
// based on rules in the config file
|
|
||||||
$css = str_replace($this->pathFrom, $this->pathTo, $css);
|
|
||||||
|
|
||||||
return $css;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Output the CSS
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function output($css)
|
|
||||||
{
|
|
||||||
$this->sendFinalOutput($css, 'text/css', $this->lastModified);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// ! Start Minifying
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
//Get config files
|
|
||||||
$config = require('../app/appConf/minify_config.php');
|
|
||||||
$groups = require($config['css_groups_file']);
|
|
||||||
|
|
||||||
if ( ! array_key_exists($_GET['g'], $groups))
|
|
||||||
{
|
|
||||||
throw new InvalidArgumentException('You must specify a css group that exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
new CSSMin($config, $groups);
|
|
||||||
}
|
|
||||||
catch (FileNotChangedException $e)
|
|
||||||
{
|
|
||||||
BaseMin::send304();
|
|
||||||
}
|
|
||||||
|
|
||||||
//End of css.php
|
|
1
public/css/app.min.css
vendored
Normal file
1
public/css/app.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/css/app.min.css.map
Normal file
1
public/css/app.min.css.map
Normal file
@ -0,0 +1 @@
|
|||||||
|
undefined
|
File diff suppressed because it is too large
Load Diff
@ -1,643 +0,0 @@
|
|||||||
@import "./marx.myth.css";
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--link-shadow: 1px 1px 1px #000;
|
|
||||||
--shadow: 1px 2px 1px rgba(0, 0, 0, 0.85);
|
|
||||||
--title-overlay: rgba(0, 0, 0, 0.45);
|
|
||||||
--title-overlay-fallback: #000;
|
|
||||||
--text-color: #ffffff;
|
|
||||||
--normal-padding: 0.25em 0.125em;
|
|
||||||
--link-hover-color: #7d12db;
|
|
||||||
--edit-link-hover-color: #db7d12;
|
|
||||||
--edit-link-color: #12db18;
|
|
||||||
--radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
template, [hidden="hidden"], .media[hidden] {display:none}
|
|
||||||
|
|
||||||
body {margin: 0.5em;}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background:rgba(255,255,255,0.65);
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
min-width:85%;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
padding:1em;
|
|
||||||
padding:1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
thead td, thead th {
|
|
||||||
padding:0.5em;
|
|
||||||
padding:0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=number] {
|
|
||||||
width: 4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
tbody > tr:nth-child(odd) {
|
|
||||||
background: #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover, a:active {
|
|
||||||
color: var(--link-hover-color)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
|
||||||
Utility classes
|
|
||||||
------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
.bracketed {
|
|
||||||
color: var(--edit-link-color);
|
|
||||||
}
|
|
||||||
.bracketed, h1 a {
|
|
||||||
text-shadow: var(--link-shadow);
|
|
||||||
}
|
|
||||||
.bracketed:before {content: '[\00a0'}
|
|
||||||
.bracketed:after {content: '\00a0]'}
|
|
||||||
.bracketed:hover, .bracketed:active {
|
|
||||||
color: var(--edit-link-hover-color)
|
|
||||||
}
|
|
||||||
|
|
||||||
.grow-1 {flex-grow: 1}
|
|
||||||
.flex-wrap {flex-wrap: wrap}
|
|
||||||
.flex-no-wrap {flex-wrap: nowrap}
|
|
||||||
.flex-align-end {align-items: flex-end}
|
|
||||||
.flex-align-space-around {align-content: space-around}
|
|
||||||
.flex-justify-space-around {justify-content: space-around}
|
|
||||||
.flex-self-center {align-self:center}
|
|
||||||
.flex {display: flex}
|
|
||||||
|
|
||||||
.small-font {
|
|
||||||
font-size:1.6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.justify {text-align:justify}
|
|
||||||
.align_center {text-align:center}
|
|
||||||
.align_left {text-align:left}
|
|
||||||
.align_right {text-align:right}
|
|
||||||
|
|
||||||
.valign_top {vertical-align:top}
|
|
||||||
|
|
||||||
.no_border {border:none}
|
|
||||||
|
|
||||||
.media-wrap {
|
|
||||||
text-align:center;
|
|
||||||
margin:0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.danger {
|
|
||||||
background-color: #ff4136;
|
|
||||||
border-color: #924949;
|
|
||||||
color:#fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.danger:hover, .danger:active {
|
|
||||||
background-color: #924949;
|
|
||||||
border-color: #ff4136;
|
|
||||||
color:#fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-btn {
|
|
||||||
border-color: var(--edit-link-color);
|
|
||||||
color: var(--edit-link-color);
|
|
||||||
text-shadow: var(--link-shadow);
|
|
||||||
padding:0 0.5em;
|
|
||||||
padding:0 0.5rem;
|
|
||||||
}
|
|
||||||
.user-btn:hover, .user-btn:active {
|
|
||||||
border-color: var(--edit-link-hover-color);
|
|
||||||
background-color: var(--edit-link-hover-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.full_width {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
|
||||||
CSS loading icon
|
|
||||||
------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
.cssload-loader {
|
|
||||||
position: relative;
|
|
||||||
left: calc(50% - 31px);
|
|
||||||
width: 62px;
|
|
||||||
height: 62px;
|
|
||||||
border-radius: 50%;
|
|
||||||
perspective: 780px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cssload-inner {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cssload-inner.cssload-one {
|
|
||||||
left: 0%;
|
|
||||||
top: 0%;
|
|
||||||
animation: cssload-rotate-one 1.15s linear infinite;
|
|
||||||
border-bottom: 3px solid rgb(0,0,0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cssload-inner.cssload-two {
|
|
||||||
right: 0%;
|
|
||||||
top: 0%;
|
|
||||||
animation: cssload-rotate-two 1.15s linear infinite;
|
|
||||||
border-right: 3px solid rgb(0,0,0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cssload-inner.cssload-three {
|
|
||||||
right: 0%;
|
|
||||||
bottom: 0%;
|
|
||||||
animation: cssload-rotate-three 1.15s linear infinite;
|
|
||||||
border-top: 3px solid rgb(0,0,0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes cssload-rotate-one {
|
|
||||||
0% {
|
|
||||||
transform: rotateX(35deg) rotateY(-45deg) rotateZ(0deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: rotateX(35deg) rotateY(-45deg) rotateZ(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes cssload-rotate-two {
|
|
||||||
0% {
|
|
||||||
transform: rotateX(50deg) rotateY(10deg) rotateZ(0deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: rotateX(50deg) rotateY(10deg) rotateZ(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes cssload-rotate-three {
|
|
||||||
0% {
|
|
||||||
transform: rotateX(35deg) rotateY(55deg) rotateZ(0deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: rotateX(35deg) rotateY(55deg) rotateZ(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
|
||||||
Table sorting and form styles
|
|
||||||
------------------------------------------------------------------------------*/
|
|
||||||
.sorting,
|
|
||||||
.sorting_asc,
|
|
||||||
.sorting_desc {
|
|
||||||
vertical-align:text-bottom;
|
|
||||||
}
|
|
||||||
.sorting::before {
|
|
||||||
content: " ↕\00a0";
|
|
||||||
}
|
|
||||||
.sorting_asc::before {
|
|
||||||
content: " ↑\00a0";
|
|
||||||
}
|
|
||||||
.sorting_desc::before {
|
|
||||||
content: " ↓\00a0";
|
|
||||||
}
|
|
||||||
|
|
||||||
.form { width:100%; }
|
|
||||||
|
|
||||||
.form thead th, .form thead tr {
|
|
||||||
background: inherit;
|
|
||||||
border:0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form tr > td:nth-child(odd) {
|
|
||||||
text-align:right;
|
|
||||||
min-width:25px;
|
|
||||||
max-width:30%;
|
|
||||||
}
|
|
||||||
.form tr > td:nth-child(even) {
|
|
||||||
text-align:left;
|
|
||||||
width:70%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.invisible tbody > tr:nth-child(odd) {
|
|
||||||
background: inherit;
|
|
||||||
}
|
|
||||||
.invisible tr, .invisible td, .invisible th {
|
|
||||||
border:0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
|
||||||
Message boxes
|
|
||||||
------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
.message{
|
|
||||||
position:relative;
|
|
||||||
margin:0.5em auto;
|
|
||||||
padding:0.5em;
|
|
||||||
width:95%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message .close{
|
|
||||||
width:1em;
|
|
||||||
height:1em;
|
|
||||||
position:absolute;
|
|
||||||
right:0.5em;
|
|
||||||
top:0.5em;
|
|
||||||
text-align:center;
|
|
||||||
vertical-align:middle;
|
|
||||||
line-height:1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message:hover .close:after {
|
|
||||||
content: '☒';
|
|
||||||
}
|
|
||||||
|
|
||||||
.message:hover {
|
|
||||||
cursor:pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message .icon{
|
|
||||||
left:0.5em;
|
|
||||||
top:0.5em;
|
|
||||||
margin-right:1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message.error{
|
|
||||||
border:1px solid #924949;
|
|
||||||
background: #f3e6e6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message.error .icon::after {
|
|
||||||
content: '✘';
|
|
||||||
}
|
|
||||||
|
|
||||||
.message.success{
|
|
||||||
border:1px solid #1f8454;
|
|
||||||
background: #70dda9;
|
|
||||||
}
|
|
||||||
.message.success .icon::after {
|
|
||||||
content: '✔'
|
|
||||||
}
|
|
||||||
|
|
||||||
.message.info{
|
|
||||||
border:1px solid #bfbe3a;
|
|
||||||
background: #FFFFCC;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message.info .icon::after {
|
|
||||||
content: '⚠';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
|
||||||
Base list styles
|
|
||||||
------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
.media, .character, .small_character {
|
|
||||||
position:relative;
|
|
||||||
vertical-align:top;
|
|
||||||
display:inline-block;
|
|
||||||
text-align:center;
|
|
||||||
width:220px;
|
|
||||||
height:311px;
|
|
||||||
margin: var(--normal-padding);
|
|
||||||
}
|
|
||||||
|
|
||||||
.media > img,
|
|
||||||
.character > img,
|
|
||||||
.small_character > img {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media .edit_buttons > button {
|
|
||||||
margin:0.5em auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name,
|
|
||||||
.media_metadata > div,
|
|
||||||
.medium_metadata > div,
|
|
||||||
.row {
|
|
||||||
text-shadow: var(--shadow);
|
|
||||||
background: var(--title-overlay-fallback);
|
|
||||||
background: var(--title-overlay);
|
|
||||||
color: var(--text-color);
|
|
||||||
padding: var(--normal-padding);
|
|
||||||
text-align:right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media_type, .age_rating {
|
|
||||||
text-align:left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media > .media_metadata {
|
|
||||||
position:absolute;
|
|
||||||
bottom:0;
|
|
||||||
right:0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media > .medium_metadata {
|
|
||||||
position:absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left:0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media > .name {
|
|
||||||
position:absolute;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
.small_character:hover > .name,
|
|
||||||
.character:hover > .name,
|
|
||||||
.media:hover > .name,
|
|
||||||
.media:hover > .media_metadata > div,
|
|
||||||
.media:hover > .medium_metadata > div,
|
|
||||||
.media:hover > .table .row
|
|
||||||
{
|
|
||||||
transition: .25s ease;
|
|
||||||
background:rgba(0,0,0,0.75);
|
|
||||||
}
|
|
||||||
|
|
||||||
.media:hover > button[hidden],
|
|
||||||
.media:hover > .edit_buttons[hidden]
|
|
||||||
{
|
|
||||||
transition: .25s ease;
|
|
||||||
display:block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.small_character > .name a,
|
|
||||||
.small_character > .name a small,
|
|
||||||
.character > .name a,
|
|
||||||
.character > .name a small,
|
|
||||||
.media > .name a,
|
|
||||||
.media > .name a small
|
|
||||||
{
|
|
||||||
background:none;
|
|
||||||
color:#fff;
|
|
||||||
text-shadow: var(--shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
|
||||||
Anime-list-specific styles
|
|
||||||
------------------------------------------------------------------------------*/
|
|
||||||
.anime .name, .manga .name {
|
|
||||||
text-align:center;
|
|
||||||
width:100%;
|
|
||||||
padding:0.5em 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.anime .media_type,
|
|
||||||
.anime .airing_status,
|
|
||||||
.anime .user_rating,
|
|
||||||
.anime .completion,
|
|
||||||
.anime .age_rating,
|
|
||||||
.anime .edit,
|
|
||||||
.anime .delete {
|
|
||||||
background: none;
|
|
||||||
text-align:center;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.anime .table, .manga .table {
|
|
||||||
position:absolute;
|
|
||||||
bottom:0;
|
|
||||||
left:0;
|
|
||||||
width:100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.anime .row, .manga .row {
|
|
||||||
width:100%;
|
|
||||||
background: var(--title-overlay-fallback);
|
|
||||||
background: var(--title-overlay);
|
|
||||||
display: flex;
|
|
||||||
align-content: space-around;
|
|
||||||
justify-content: space-around;
|
|
||||||
text-align:center;
|
|
||||||
padding:0 inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.anime .row > span, .manga .row > span {
|
|
||||||
text-align:left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.anime .row > div, .manga .row > div {
|
|
||||||
font-size:0.8em;
|
|
||||||
display:flex-item;
|
|
||||||
align-self:center;
|
|
||||||
text-align:center;
|
|
||||||
vertical-align:middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.anime .media > button.plus_one {
|
|
||||||
position:absolute;
|
|
||||||
top: 138px;
|
|
||||||
top: calc(50% - 21.5px);
|
|
||||||
left: 44px;
|
|
||||||
left: calc(50% - 66.5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
|
||||||
Manga-list-specific styles
|
|
||||||
------------------------------------------------------------------------------*/
|
|
||||||
.manga .row {
|
|
||||||
padding:1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.manga .media {
|
|
||||||
border:1px solid #ddd;
|
|
||||||
height:310px;
|
|
||||||
margin:0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.manga .media > .edit_buttons {
|
|
||||||
position:absolute;
|
|
||||||
top: 86px;
|
|
||||||
top: calc(50% - 58.5px);
|
|
||||||
left: 43.5px;
|
|
||||||
left: calc(50% - 66.5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
|
||||||
Search page styles
|
|
||||||
------------------------------------------------------------------------------*/
|
|
||||||
.media.search > .name {
|
|
||||||
background-color:#555;
|
|
||||||
background-color: rgba(000,000,000,0.35);
|
|
||||||
background-size: cover;
|
|
||||||
background-size: contain;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
.big-check {
|
|
||||||
display:none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.big-check:checked + label {
|
|
||||||
transition: .25s ease;
|
|
||||||
background:rgba(0,0,0,0.75);
|
|
||||||
}
|
|
||||||
|
|
||||||
.big-check:checked + label:after {
|
|
||||||
content: '✓';
|
|
||||||
font-size: 15em;
|
|
||||||
font-size: 15rem;
|
|
||||||
text-align:center;
|
|
||||||
color: greenyellow;
|
|
||||||
position:absolute;
|
|
||||||
top:147px;
|
|
||||||
left:0;
|
|
||||||
height:100%;
|
|
||||||
width:100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#series_list article.media {
|
|
||||||
position:relative;
|
|
||||||
}
|
|
||||||
#series_list .name, #series_list .name label {
|
|
||||||
position:absolute;
|
|
||||||
display:block;
|
|
||||||
top:0;
|
|
||||||
left:0;
|
|
||||||
height:100%;
|
|
||||||
width:100%;
|
|
||||||
vertical-align:middle;
|
|
||||||
line-height: 1.25em;
|
|
||||||
}
|
|
||||||
#series_list .name small {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
|
||||||
Details page styles
|
|
||||||
-----------------------------------------------------------------------------*/
|
|
||||||
.details {
|
|
||||||
margin: 1.5rem auto 0 auto;
|
|
||||||
padding:1rem;
|
|
||||||
font-size:inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.details.fixed {
|
|
||||||
max-width:93rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.details .cover {
|
|
||||||
display: block;
|
|
||||||
width: 284px;
|
|
||||||
/* height: 402px; */
|
|
||||||
}
|
|
||||||
|
|
||||||
.details h2 {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.details .flex > div {
|
|
||||||
margin: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.details .media_details {
|
|
||||||
max-width:300px;
|
|
||||||
}
|
|
||||||
.details .media_details td {
|
|
||||||
padding:0 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.details p {
|
|
||||||
text-align:justify;
|
|
||||||
}
|
|
||||||
|
|
||||||
.details .media_details td:nth-child(odd) {
|
|
||||||
width:1%;
|
|
||||||
white-space:nowrap;
|
|
||||||
text-align:right;
|
|
||||||
}
|
|
||||||
.details .media_details td:nth-child(even) {
|
|
||||||
text-align:left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.character,
|
|
||||||
.small_character {
|
|
||||||
background: rgba(0,0,0,0.5);
|
|
||||||
width: 225px;
|
|
||||||
height: 350px;
|
|
||||||
vertical-align: middle;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.small_character a {
|
|
||||||
display:inline-block;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.small_character .name,
|
|
||||||
.character .name {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
.small_character img,
|
|
||||||
.character img {
|
|
||||||
position: relative;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
z-index: 5;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
|
||||||
User page styles
|
|
||||||
-----------------------------------------------------------------------------*/
|
|
||||||
.small_character {
|
|
||||||
width: 160px;
|
|
||||||
height: 250px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-page .media-wrap {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media a {
|
|
||||||
display: inline-block;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
|
||||||
Viewport-based styles
|
|
||||||
-----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
@media screen and (max-width: 40em) {
|
|
||||||
nav a {
|
|
||||||
line-height:4em;
|
|
||||||
line-height:4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media {
|
|
||||||
margin:2px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
padding:0 0,5em 0.5em;
|
|
||||||
padding:0 0.5rem 0.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
|
||||||
Images / Logos
|
|
||||||
-----------------------------------------------------------------------------*/
|
|
||||||
.streaming-logo {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
vertical-align:middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cover_streaming_link .streaming-logo {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
}
|
|
@ -1,6 +1,7 @@
|
|||||||
:root
|
:root {
|
||||||
{
|
--default-font-list: system-ui,sans-serif;
|
||||||
--default-font-list:'Open Sans', 'Nimbus Sans L', 'Helvetica Neue', Helvetica, 'Lucida Grande', sans-serif;
|
--monospace-font-list:'Anonymous Pro','Fira Code',Menlo,Monaco,Consolas,'Courier New',monospace;
|
||||||
|
--serif-font-list:Georgia,Times,'Times New Roman',serif;
|
||||||
-ms-text-size-adjust:100%;
|
-ms-text-size-adjust:100%;
|
||||||
-webkit-text-size-adjust:100%;
|
-webkit-text-size-adjust:100%;
|
||||||
box-sizing:border-box;
|
box-sizing:border-box;
|
||||||
@ -9,48 +10,41 @@
|
|||||||
line-height:1.4;
|
line-height:1.4;
|
||||||
overflow-y:scroll;
|
overflow-y:scroll;
|
||||||
text-size-adjust:100%;
|
text-size-adjust:100%;
|
||||||
scroll-behavior: smooth;
|
scroll-behavior:smooth;
|
||||||
}
|
}
|
||||||
|
|
||||||
audio:not([controls])
|
audio:not([controls]) {
|
||||||
{
|
|
||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
details
|
details {
|
||||||
{
|
|
||||||
display:block;
|
display:block;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=search]
|
input[type=search] {
|
||||||
{
|
|
||||||
-webkit-appearance:textfield;
|
-webkit-appearance:textfield;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration
|
input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration {
|
||||||
{
|
|
||||||
-webkit-appearance:none;
|
-webkit-appearance:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
main
|
main {
|
||||||
{
|
|
||||||
display:block;
|
display:block;
|
||||||
margin:0 auto;
|
margin:0 auto;
|
||||||
padding:0 1.6em 1.6em;
|
padding:0 1.6em 1.6em;
|
||||||
padding:0 1.6rem 1.6rem;
|
padding:0 1.6rem 1.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
summary
|
summary {
|
||||||
{
|
|
||||||
display:block;
|
display:block;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre
|
pre {
|
||||||
{
|
|
||||||
background:#efefef;
|
background:#efefef;
|
||||||
color:#444;
|
color:#444;
|
||||||
display:block;
|
display:block;
|
||||||
font-family:Menlo, Monaco, Consolas, 'Courier New', monospace;
|
font-family:var(--monospace-font-list);
|
||||||
font-size:1.4em;
|
font-size:1.4em;
|
||||||
font-size:1.4rem;
|
font-size:1.4rem;
|
||||||
margin:1.6em 0;
|
margin:1.6em 0;
|
||||||
@ -62,29 +56,24 @@ pre
|
|||||||
word-wrap:break-word;
|
word-wrap:break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
progress
|
progress {
|
||||||
{
|
|
||||||
display:inline-block;
|
display:inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
small
|
small {
|
||||||
{
|
|
||||||
color:#777;
|
color:#777;
|
||||||
font-size:75%;
|
font-size:75%;
|
||||||
}
|
}
|
||||||
|
|
||||||
big
|
big {
|
||||||
{
|
|
||||||
font-size:125%;
|
font-size:125%;
|
||||||
}
|
}
|
||||||
|
|
||||||
template
|
template {
|
||||||
{
|
|
||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea
|
textarea {
|
||||||
{
|
|
||||||
border:.1rem solid #ccc;
|
border:.1rem solid #ccc;
|
||||||
border-radius:0;
|
border-radius:0;
|
||||||
display:block;
|
display:block;
|
||||||
@ -95,55 +84,47 @@ textarea
|
|||||||
vertical-align:middle;
|
vertical-align:middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
[hidden]
|
[hidden] {
|
||||||
{
|
|
||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
[unselectable]
|
[unselectable] {
|
||||||
{
|
|
||||||
-moz-user-select:none;
|
-moz-user-select:none;
|
||||||
-ms-user-select:none;
|
-ms-user-select:none;
|
||||||
-webkit-user-select:none;
|
-webkit-user-select:none;
|
||||||
user-select:none;
|
user-select:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
*,::before,::after
|
*,::before,::after {
|
||||||
{
|
|
||||||
border-style:solid;
|
border-style:solid;
|
||||||
border-width:0;
|
border-width:0;
|
||||||
box-sizing:inherit;
|
box-sizing:inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
*
|
* {
|
||||||
{
|
|
||||||
font-size:inherit;
|
font-size:inherit;
|
||||||
line-height:inherit;
|
line-height:inherit;
|
||||||
margin:0;
|
margin:0;
|
||||||
padding:0;
|
padding:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
::before,::after
|
::before,::after {
|
||||||
{
|
|
||||||
text-decoration:inherit;
|
text-decoration:inherit;
|
||||||
vertical-align:inherit;
|
vertical-align:inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
a
|
a {
|
||||||
{
|
|
||||||
-webkit-transition:.25s ease;
|
-webkit-transition:.25s ease;
|
||||||
color:#1271db;
|
color:#1271db;
|
||||||
text-decoration:none;
|
text-decoration:none;
|
||||||
transition:.25s ease;
|
transition:.25s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
audio,canvas,iframe,img,svg,video
|
audio,canvas,iframe,img,svg,video {
|
||||||
{
|
|
||||||
vertical-align:middle;
|
vertical-align:middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
button,input,select,textarea
|
button,input,select,textarea {
|
||||||
{
|
|
||||||
border:.1rem solid #ccc;
|
border:.1rem solid #ccc;
|
||||||
color:inherit;
|
color:inherit;
|
||||||
font-family:inherit;
|
font-family:inherit;
|
||||||
@ -152,37 +133,31 @@ button,input,select,textarea
|
|||||||
min-height:1.4em;
|
min-height:1.4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
code,kbd,pre,samp
|
code,kbd,pre,samp {
|
||||||
{
|
font-family:var(--monospace-font-list);
|
||||||
font-family:Menlo, Monaco, Consolas, 'Courier New', monospace, monospace;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
table
|
table {
|
||||||
{
|
|
||||||
border-collapse:collapse;
|
border-collapse:collapse;
|
||||||
border-spacing:0;
|
border-spacing:0;
|
||||||
margin-bottom:1.6rem;
|
margin-bottom:1.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-moz-selection
|
::-moz-selection {
|
||||||
{
|
|
||||||
background-color:#b3d4fc;
|
background-color:#b3d4fc;
|
||||||
text-shadow:none;
|
text-shadow:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
::selection
|
::selection {
|
||||||
{
|
|
||||||
background-color:#b3d4fc;
|
background-color:#b3d4fc;
|
||||||
text-shadow:none;
|
text-shadow:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
button::-moz-focus-inner
|
button::-moz-focus-inner {
|
||||||
{
|
|
||||||
border:0;
|
border:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
body
|
body {
|
||||||
{
|
|
||||||
color:#444;
|
color:#444;
|
||||||
font-family:var(--default-font-list);
|
font-family:var(--default-font-list);
|
||||||
font-size:1.6rem;
|
font-size:1.6rem;
|
||||||
@ -191,20 +166,17 @@ body
|
|||||||
padding:0;
|
padding:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
p
|
p {
|
||||||
{
|
|
||||||
margin:0 0 1.6rem;
|
margin:0 0 1.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,h2,h3,h4,h5,h6
|
h1,h2,h3,h4,h5,h6 {
|
||||||
{
|
font-family:var(--default-font-list);
|
||||||
font-family:Lato, var(--default-font-list);
|
|
||||||
margin:2em 0 1.6em;
|
margin:2em 0 1.6em;
|
||||||
margin:2rem 0 1.6rem;
|
margin:2rem 0 1.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1
|
h1 {
|
||||||
{
|
|
||||||
border-bottom:.1rem solid rgba(0,0,0,0.2);
|
border-bottom:.1rem solid rgba(0,0,0,0.2);
|
||||||
font-size:3.6em;
|
font-size:3.6em;
|
||||||
font-size:3.6rem;
|
font-size:3.6rem;
|
||||||
@ -212,16 +184,14 @@ h1
|
|||||||
font-weight:500;
|
font-weight:500;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2
|
h2 {
|
||||||
{
|
|
||||||
font-size:3em;
|
font-size:3em;
|
||||||
font-size:3rem;
|
font-size:3rem;
|
||||||
font-style:normal;
|
font-style:normal;
|
||||||
font-weight:500;
|
font-weight:500;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3
|
h3 {
|
||||||
{
|
|
||||||
font-size:2.4em;
|
font-size:2.4em;
|
||||||
font-size:2.4rem;
|
font-size:2.4rem;
|
||||||
font-style:normal;
|
font-style:normal;
|
||||||
@ -229,8 +199,7 @@ h3
|
|||||||
margin:1.6rem 0 .4rem;
|
margin:1.6rem 0 .4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h4
|
h4 {
|
||||||
{
|
|
||||||
font-size:1.8em;
|
font-size:1.8em;
|
||||||
font-size:1.8rem;
|
font-size:1.8rem;
|
||||||
font-style:normal;
|
font-style:normal;
|
||||||
@ -238,8 +207,7 @@ h4
|
|||||||
margin:1.6rem 0 .4rem;
|
margin:1.6rem 0 .4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h5
|
h5 {
|
||||||
{
|
|
||||||
font-size:1.6em;
|
font-size:1.6em;
|
||||||
font-size:1.6rem;
|
font-size:1.6rem;
|
||||||
font-style:normal;
|
font-style:normal;
|
||||||
@ -247,8 +215,7 @@ h5
|
|||||||
margin:1.6rem 0 .4rem;
|
margin:1.6rem 0 .4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h6
|
h6 {
|
||||||
{
|
|
||||||
color:#777;
|
color:#777;
|
||||||
font-size:1.4em;
|
font-size:1.4em;
|
||||||
font-size:1.4rem;
|
font-size:1.4rem;
|
||||||
@ -257,66 +224,56 @@ h6
|
|||||||
margin:1.6rem 0 .4rem;
|
margin:1.6rem 0 .4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
code
|
code {
|
||||||
{
|
|
||||||
background:#efefef;
|
background:#efefef;
|
||||||
color:#444;
|
color:#444;
|
||||||
font-family:Menlo, Monaco, Consolas, 'Courier New', monospace;
|
font-family:var(--monospace-font-list);
|
||||||
font-size:1.4rem;
|
font-size:1.4rem;
|
||||||
word-break:break-all;
|
word-break:break-all;
|
||||||
word-wrap:break-word;
|
word-wrap:break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover,a:focus
|
a:hover,a:focus {
|
||||||
{
|
|
||||||
text-decoration:none;
|
text-decoration:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
dl
|
dl {
|
||||||
{
|
|
||||||
margin-bottom:1.6rem;
|
margin-bottom:1.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
dd
|
dd {
|
||||||
{
|
|
||||||
margin-left:4rem;
|
margin-left:4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul,ol
|
ul,ol {
|
||||||
{
|
|
||||||
margin-bottom:.8rem;
|
margin-bottom:.8rem;
|
||||||
padding-left:2rem;
|
padding-left:2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
blockquote
|
blockquote {
|
||||||
{
|
|
||||||
border-left:.2rem solid #1271db;
|
border-left:.2rem solid #1271db;
|
||||||
font-family:Georgia, Times, 'Times New Roman', serif;
|
font-family:var(--serif-font-list);
|
||||||
font-style:italic;
|
font-style:italic;
|
||||||
margin:1.6rem 0;
|
margin:1.6rem 0;
|
||||||
padding-left:1.6rem;
|
padding-left:1.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
figcaption
|
figcaption {
|
||||||
{
|
font-family:var(--serif-font-list);
|
||||||
font-family:Georgia, Times, 'Times New Roman', serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
html
|
html {
|
||||||
{
|
|
||||||
font-size:62.5%;
|
font-size:62.5%;
|
||||||
}
|
}
|
||||||
|
|
||||||
main,header,footer,article,section,aside,details,summary
|
main,header,footer,article,section,aside,details,summary {
|
||||||
{
|
|
||||||
display:block;
|
display:block;
|
||||||
height:auto;
|
height:auto;
|
||||||
margin:0 auto;
|
margin:0 auto;
|
||||||
width:100%;
|
width:100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer
|
footer {
|
||||||
{
|
|
||||||
border-top:.1rem solid rgba(0,0,0,0.2);
|
border-top:.1rem solid rgba(0,0,0,0.2);
|
||||||
clear:both;
|
clear:both;
|
||||||
display:inline-block;
|
display:inline-block;
|
||||||
@ -326,23 +283,20 @@ footer
|
|||||||
text-align:center;
|
text-align:center;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr
|
hr {
|
||||||
{
|
|
||||||
border-top:.1rem solid rgba(0,0,0,0.2);
|
border-top:.1rem solid rgba(0,0,0,0.2);
|
||||||
display:block;
|
display:block;
|
||||||
margin-bottom:1.6rem;
|
margin-bottom:1.6rem;
|
||||||
width:100%;
|
width:100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
img
|
img {
|
||||||
{
|
|
||||||
height:auto;
|
height:auto;
|
||||||
/* max-width:100%; */
|
/* max-width:100%; */
|
||||||
vertical-align:baseline;
|
vertical-align:baseline;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=text],input[type=password],input[type=email],input[type=url],input[type=date],input[type=month],input[type=time],input[type=datetime],input[type=datetime-local],input[type=week],input[type=number],input[type=search],input[type=tel],input[type=color],select
|
input[type=text],input[type=password],input[type=email],input[type=url],input[type=date],input[type=month],input[type=time],input[type=datetime],input[type=datetime-local],input[type=week],input[type=number],input[type=search],input[type=tel],input[type=color],select {
|
||||||
{
|
|
||||||
border:.1rem solid #ccc;
|
border:.1rem solid #ccc;
|
||||||
border-radius:0;
|
border-radius:0;
|
||||||
display:inline-block;
|
display:inline-block;
|
||||||
@ -350,8 +304,7 @@ input[type=text],input[type=password],input[type=email],input[type=url],input[ty
|
|||||||
vertical-align:middle;
|
vertical-align:middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:not([type])
|
input:not([type]) {
|
||||||
{
|
|
||||||
-webkit-appearance:none;
|
-webkit-appearance:none;
|
||||||
background-clip:padding-box;
|
background-clip:padding-box;
|
||||||
background-color:#fff;
|
background-color:#fff;
|
||||||
@ -363,88 +316,73 @@ input:not([type])
|
|||||||
text-align:left;
|
text-align:left;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=color]
|
input[type=color] {
|
||||||
{
|
|
||||||
padding:.8rem 1.6rem;
|
padding:.8rem 1.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=text]:focus,input[type=password]:focus,input[type=email]:focus,input[type=url]:focus,input[type=date]:focus,input[type=month]:focus,input[type=time]:focus,input[type=datetime]:focus,input[type=datetime-local]:focus,input[type=week]:focus,input[type=number]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=color]:focus,select:focus,textarea:focus
|
input[type=text]:focus,input[type=password]:focus,input[type=email]:focus,input[type=url]:focus,input[type=date]:focus,input[type=month]:focus,input[type=time]:focus,input[type=datetime]:focus,input[type=datetime-local]:focus,input[type=week]:focus,input[type=number]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=color]:focus,select:focus,textarea:focus {
|
||||||
{
|
|
||||||
border-color:#b3d4fc;
|
border-color:#b3d4fc;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:not([type]):focus
|
input:not([type]):focus {
|
||||||
{
|
|
||||||
border-color:#b3d4fc;
|
border-color:#b3d4fc;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=radio],input[type=checkbox]
|
input[type=radio],input[type=checkbox] {
|
||||||
{
|
|
||||||
vertical-align:middle;
|
vertical-align:middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=file]:focus,input[type=radio]:focus,input[type=checkbox]:focus
|
input[type=file]:focus,input[type=radio]:focus,input[type=checkbox]:focus {
|
||||||
{
|
|
||||||
outline:.1rem solid thin #444;
|
outline:.1rem solid thin #444;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=text][disabled],input[type=password][disabled],input[type=email][disabled],input[type=url][disabled],input[type=date][disabled],input[type=month][disabled],input[type=time][disabled],input[type=datetime][disabled],input[type=datetime-local][disabled],input[type=week][disabled],input[type=number][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=color][disabled],select[disabled],textarea[disabled]
|
input[type=text][disabled],input[type=password][disabled],input[type=email][disabled],input[type=url][disabled],input[type=date][disabled],input[type=month][disabled],input[type=time][disabled],input[type=datetime][disabled],input[type=datetime-local][disabled],input[type=week][disabled],input[type=number][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=color][disabled],select[disabled],textarea[disabled] {
|
||||||
{
|
|
||||||
background-color:#efefef;
|
background-color:#efefef;
|
||||||
color:#777;
|
color:#777;
|
||||||
cursor:not-allowed;
|
cursor:not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:not([type])[disabled]
|
input:not([type])[disabled] {
|
||||||
{
|
|
||||||
background-color:#efefef;
|
background-color:#efefef;
|
||||||
color:#777;
|
color:#777;
|
||||||
cursor:not-allowed;
|
cursor:not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[readonly],select[readonly],textarea[readonly]
|
input[readonly],select[readonly],textarea[readonly] {
|
||||||
{
|
|
||||||
background-color:#efefef;
|
background-color:#efefef;
|
||||||
border-color:#ccc;
|
border-color:#ccc;
|
||||||
color:#777;
|
color:#777;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:focus:invalid,textarea:focus:invalid,select:focus:invalid
|
input:focus:invalid,textarea:focus:invalid,select:focus:invalid {
|
||||||
{
|
|
||||||
border-color:#e9322d;
|
border-color:#e9322d;
|
||||||
color:#b94a48;
|
color:#b94a48;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus,input[type=checkbox]:focus:invalid:focus
|
input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus,input[type=checkbox]:focus:invalid:focus {
|
||||||
{
|
|
||||||
outline-color:#ff4136;
|
outline-color:#ff4136;
|
||||||
}
|
}
|
||||||
|
|
||||||
select
|
select {
|
||||||
{
|
|
||||||
background-color:#fff;
|
background-color:#fff;
|
||||||
border:.1rem solid #ccc;
|
border:.1rem solid #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
select[multiple]
|
select[multiple] {
|
||||||
{
|
|
||||||
height:auto;
|
height:auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
label
|
label {
|
||||||
{
|
|
||||||
line-height:2;
|
line-height:2;
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldset
|
fieldset {
|
||||||
{
|
|
||||||
border:0;
|
border:0;
|
||||||
margin:0;
|
margin:0;
|
||||||
padding:.8rem 0;
|
padding:.8rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
legend
|
legend {
|
||||||
{
|
|
||||||
border-bottom:.1rem solid #ccc;
|
border-bottom:.1rem solid #ccc;
|
||||||
color:#444;
|
color:#444;
|
||||||
display:block;
|
display:block;
|
||||||
@ -453,8 +391,7 @@ legend
|
|||||||
width:100%;
|
width:100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=submit],button
|
input[type=submit],button {
|
||||||
{
|
|
||||||
-moz-user-select:none;
|
-moz-user-select:none;
|
||||||
-ms-user-select:none;
|
-ms-user-select:none;
|
||||||
-webkit-transition:.25s ease;
|
-webkit-transition:.25s ease;
|
||||||
@ -476,62 +413,52 @@ input[type=submit],button
|
|||||||
vertical-align:baseline;
|
vertical-align:baseline;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=submit] a,button a
|
input[type=submit] a,button a {
|
||||||
{
|
|
||||||
color:#444;
|
color:#444;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=submit]::-moz-focus-inner,button::-moz-focus-inner
|
input[type=submit]::-moz-focus-inner,button::-moz-focus-inner {
|
||||||
{
|
|
||||||
padding:0;
|
padding:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=submit]:hover,button:hover
|
input[type=submit]:hover,button:hover {
|
||||||
{
|
|
||||||
background:#444;
|
background:#444;
|
||||||
border-color:#444;
|
border-color:#444;
|
||||||
color:#fff;
|
color:#fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=submit]:hover a,button:hover a
|
input[type=submit]:hover a,button:hover a {
|
||||||
{
|
|
||||||
color:#fff;
|
color:#fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=submit]:active,button:active
|
input[type=submit]:active,button:active {
|
||||||
{
|
|
||||||
background:#6a6a6a;
|
background:#6a6a6a;
|
||||||
border-color:#6a6a6a;
|
border-color:#6a6a6a;
|
||||||
color:#fff;
|
color:#fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=submit]:active a,button:active a
|
input[type=submit]:active a,button:active a {
|
||||||
{
|
|
||||||
color:#fff;
|
color:#fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=submit]:disabled,button:disabled
|
input[type=submit]:disabled,button:disabled {
|
||||||
{
|
|
||||||
box-shadow:none;
|
box-shadow:none;
|
||||||
cursor:not-allowed;
|
cursor:not-allowed;
|
||||||
opacity:.40;
|
opacity:.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav ul
|
nav ul {
|
||||||
{
|
|
||||||
list-style:none;
|
list-style:none;
|
||||||
margin:0;
|
margin:0;
|
||||||
padding:0;
|
padding:0;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav ul li
|
nav ul li {
|
||||||
{
|
|
||||||
display:inline;
|
display:inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav a
|
nav a {
|
||||||
{
|
|
||||||
-webkit-transition:.25s ease;
|
-webkit-transition:.25s ease;
|
||||||
border-bottom:.2rem solid transparent;
|
border-bottom:.2rem solid transparent;
|
||||||
color:#444;
|
color:#444;
|
||||||
@ -540,48 +467,40 @@ nav a
|
|||||||
transition:.25s ease;
|
transition:.25s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav a:hover,nav li.selected a
|
nav a:hover,nav li.selected a {
|
||||||
{
|
|
||||||
border-color:rgba(0,0,0,0.2);
|
border-color:rgba(0,0,0,0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
nav a:active
|
nav a:active {
|
||||||
{
|
|
||||||
border-color:rgba(0,0,0,0.56);
|
border-color:rgba(0,0,0,0.56);
|
||||||
}
|
}
|
||||||
|
|
||||||
caption
|
caption {
|
||||||
{
|
|
||||||
padding:.8rem 0;
|
padding:.8rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
thead th
|
thead th {
|
||||||
{
|
|
||||||
background:#efefef;
|
background:#efefef;
|
||||||
color:#444;
|
color:#444;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr
|
tr {
|
||||||
{
|
|
||||||
background:#fff;
|
background:#fff;
|
||||||
margin-bottom:.8rem;
|
margin-bottom:.8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
th,td
|
th,td {
|
||||||
{
|
|
||||||
border:.1rem solid #ccc;
|
border:.1rem solid #ccc;
|
||||||
padding:.8rem 1.6rem;
|
padding:.8rem 1.6rem;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
vertical-align:inherit;
|
vertical-align:inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
tfoot tr
|
tfoot tr {
|
||||||
{
|
|
||||||
background:none;
|
background:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
tfoot td
|
tfoot td {
|
||||||
{
|
|
||||||
color:#efefef;
|
color:#efefef;
|
||||||
font-size:.8rem;
|
font-size:.8rem;
|
||||||
font-style:italic;
|
font-style:italic;
|
||||||
@ -589,28 +508,24 @@ tfoot td
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen {
|
@media screen {
|
||||||
[hidden~=screen]
|
[hidden~=screen] {
|
||||||
{
|
|
||||||
display:inherit;
|
display:inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
[hidden~=screen]:not(:active):not(:focus):not(:target)
|
[hidden~=screen]:not(:active):not(:focus):not(:target) {
|
||||||
{
|
|
||||||
clip:rect(0000)!important;
|
clip:rect(0000)!important;
|
||||||
position:absolute!important;
|
position:absolute!important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and max-width 40rem {
|
@media screen and max-width 40rem {
|
||||||
article,section,aside
|
article,section,aside {
|
||||||
{
|
|
||||||
clear:both;
|
clear:both;
|
||||||
display:block;
|
display:block;
|
||||||
max-width:100%;
|
max-width:100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
img
|
img {
|
||||||
{
|
|
||||||
margin-right:1.6rem;
|
margin-right:1.6rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
3
public/cssfilter.js
Normal file
3
public/cssfilter.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module.exports = function filter(filename) {
|
||||||
|
return ! String(filename).includes('min');
|
||||||
|
}
|
0
public/images/characters/.gitkeep
Normal file
0
public/images/characters/.gitkeep
Normal file
122
public/js.php
122
public/js.php
@ -22,29 +22,48 @@ use Aviat\Ion\Json;
|
|||||||
|
|
||||||
// Include guzzle
|
// Include guzzle
|
||||||
require_once('../vendor/autoload.php');
|
require_once('../vendor/autoload.php');
|
||||||
require_once('./min.php');
|
|
||||||
|
//Creative rewriting of /g/groupname to ?g=groupname
|
||||||
|
$pi = $_SERVER['PATH_INFO'];
|
||||||
|
$pia = explode('/', $pi);
|
||||||
|
|
||||||
|
$piaLen = count($pia);
|
||||||
|
$i = 1;
|
||||||
|
|
||||||
|
while($i < $piaLen)
|
||||||
|
{
|
||||||
|
$j = $i+1;
|
||||||
|
$j = (isset($pia[$j])) ? $j : $i;
|
||||||
|
|
||||||
|
$_GET[$pia[$i]] = $pia[$j];
|
||||||
|
|
||||||
|
$i = $j + 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileNotChangedException extends \Exception {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple Javascript minfier, using google closure compiler
|
* Simple Javascript minfier, using google closure compiler
|
||||||
*/
|
*/
|
||||||
class JSMin extends BaseMin {
|
class JSMin {
|
||||||
|
|
||||||
protected $jsRoot;
|
protected $jsRoot;
|
||||||
protected $jsGroup;
|
protected $jsGroup;
|
||||||
protected $jsGroupsFile;
|
protected $configFile;
|
||||||
protected $cacheFile;
|
protected $cacheFile;
|
||||||
|
|
||||||
protected $lastModified;
|
protected $lastModified;
|
||||||
protected $requestedTime;
|
protected $requestedTime;
|
||||||
protected $cacheModified;
|
protected $cacheModified;
|
||||||
|
|
||||||
public function __construct(array $config, array $groups)
|
public function __construct(array $config, string $configFile)
|
||||||
{
|
{
|
||||||
$group = $_GET['g'];
|
$group = $_GET['g'];
|
||||||
|
$groups = $config['groups'];
|
||||||
|
|
||||||
$this->jsRoot = $config['js_root'];
|
$this->jsRoot = $config['js_root'];
|
||||||
$this->jsGroup = $groups[$group];
|
$this->jsGroup = $groups[$group];
|
||||||
$this->jsGroupsFile = $config['js_groups_file'];
|
$this->configFile = $configFile;
|
||||||
$this->cacheFile = "{$this->jsRoot}cache/{$group}";
|
$this->cacheFile = "{$this->jsRoot}cache/{$group}";
|
||||||
$this->lastModified = $this->getLastModified();
|
$this->lastModified = $this->getLastModified();
|
||||||
|
|
||||||
@ -178,7 +197,7 @@ class JSMin extends BaseMin {
|
|||||||
|
|
||||||
//Add this page too, as well as the groups file
|
//Add this page too, as well as the groups file
|
||||||
$modified[] = filemtime(__FILE__);
|
$modified[] = filemtime(__FILE__);
|
||||||
$modified[] = filemtime($this->jsGroupsFile);
|
$modified[] = filemtime($this->configFile);
|
||||||
|
|
||||||
rsort($modified);
|
rsort($modified);
|
||||||
$lastModified = $modified[0];
|
$lastModified = $modified[0];
|
||||||
@ -227,14 +246,97 @@ class JSMin extends BaseMin {
|
|||||||
{
|
{
|
||||||
$this->sendFinalOutput($js, 'application/javascript', $this->lastModified);
|
$this->sendFinalOutput($js, 'application/javascript', $this->lastModified);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get value of the if-modified-since header
|
||||||
|
*
|
||||||
|
* @return int - timestamp to compare for cache control
|
||||||
|
*/
|
||||||
|
protected function getIfModified()
|
||||||
|
{
|
||||||
|
return (array_key_exists('HTTP_IF_MODIFIED_SINCE', $_SERVER))
|
||||||
|
? strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])
|
||||||
|
: time();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get value of etag to compare to hash of output
|
||||||
|
*
|
||||||
|
* @return string - the etag to compare
|
||||||
|
*/
|
||||||
|
protected function getIfNoneMatch()
|
||||||
|
{
|
||||||
|
return (array_key_exists('HTTP_IF_NONE_MATCH', $_SERVER))
|
||||||
|
? $_SERVER['HTTP_IF_NONE_MATCH']
|
||||||
|
: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether or not to send debug version
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
protected function isNotDebug()
|
||||||
|
{
|
||||||
|
return ! $this->isDebugCall();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether or not to send debug version
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
protected function isDebugCall()
|
||||||
|
{
|
||||||
|
return array_key_exists('debug', $_GET);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send actual output to browser
|
||||||
|
*
|
||||||
|
* @param string $content - the body of the response
|
||||||
|
* @param string $mimeType - the content type
|
||||||
|
* @param int $lastModified - the last modified date
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function sendFinalOutput($content, $mimeType, $lastModified)
|
||||||
|
{
|
||||||
|
//This GZIPs the CSS for transmission to the user
|
||||||
|
//making file size smaller and transfer rate quicker
|
||||||
|
ob_start("ob_gzhandler");
|
||||||
|
|
||||||
|
$expires = $lastModified + 691200;
|
||||||
|
$lastModifiedDate = gmdate('D, d M Y H:i:s', $lastModified);
|
||||||
|
$expiresDate = gmdate('D, d M Y H:i:s', $expires);
|
||||||
|
|
||||||
|
header("Content-Type: {$mimeType}; charset=utf8");
|
||||||
|
header("Cache-control: public, max-age=691200, must-revalidate");
|
||||||
|
header("Last-Modified: {$lastModifiedDate} GMT");
|
||||||
|
header("Expires: {$expiresDate} GMT");
|
||||||
|
|
||||||
|
echo $content;
|
||||||
|
|
||||||
|
ob_end_flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a 304 Not Modified header
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function send304()
|
||||||
|
{
|
||||||
|
header("status: 304 Not Modified", true, 304);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
// ! Start Minifying
|
// ! Start Minifying
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
$config = require_once('../app/appConf/minify_config.php');
|
$configFile = realpath(__DIR__ . '/../app/appConf/minify_config.php');
|
||||||
$groups = require_once($config['js_groups_file']);
|
$config = require_once($configFile);
|
||||||
|
$groups = $config['groups'];
|
||||||
$cacheDir = "{$config['js_root']}cache";
|
$cacheDir = "{$config['js_root']}cache";
|
||||||
|
|
||||||
if ( ! is_dir($cacheDir))
|
if ( ! is_dir($cacheDir))
|
||||||
@ -249,11 +351,11 @@ if ( ! array_key_exists($_GET['g'], $groups))
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
new JSMin($config, $groups);
|
new JSMin($config, $configFile);
|
||||||
}
|
}
|
||||||
catch (FileNotChangedException $e)
|
catch (FileNotChangedException $e)
|
||||||
{
|
{
|
||||||
BaseMin::send304();
|
JSMin::send304();
|
||||||
}
|
}
|
||||||
|
|
||||||
//end of js.php
|
//end of js.php
|
@ -8,7 +8,7 @@
|
|||||||
// Action to increment episode count
|
// Action to increment episode count
|
||||||
_.on('body.anime.list', 'click', '.plus_one', (e) => {
|
_.on('body.anime.list', 'click', '.plus_one', (e) => {
|
||||||
let parentSel = _.closestParent(e.target, 'article');
|
let parentSel = _.closestParent(e.target, 'article');
|
||||||
let watchedCount = parseInt(_.$('.completed_number', parentSel)[0].textContent, 10);
|
let watchedCount = parseInt(_.$('.completed_number', parentSel)[0].textContent, 10) || 0;
|
||||||
let totalCount = parseInt(_.$('.total_number', parentSel)[0].textContent, 10);
|
let totalCount = parseInt(_.$('.total_number', parentSel)[0].textContent, 10);
|
||||||
let title = _.$('.name a', parentSel)[0].textContent;
|
let title = _.$('.name a', parentSel)[0].textContent;
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
let thisSel = e.target;
|
let thisSel = e.target;
|
||||||
let parentSel = _.closestParent(e.target, 'article');
|
let parentSel = _.closestParent(e.target, 'article');
|
||||||
let type = thisSel.classList.contains('plus_one_chapter') ? 'chapter' : 'volume';
|
let type = thisSel.classList.contains('plus_one_chapter') ? 'chapter' : 'volume';
|
||||||
let completed = parseInt(_.$(`.${type}s_read`, parentSel)[0].textContent, 10);
|
let completed = parseInt(_.$(`.${type}s_read`, parentSel)[0].textContent, 10) || 0;
|
||||||
let total = parseInt(_.$(`.${type}_count`, parentSel)[0].textContent, 10);
|
let total = parseInt(_.$(`.${type}_count`, parentSel)[0].textContent, 10);
|
||||||
let mangaName = _.$('.name', parentSel)[0].textContent;
|
let mangaName = _.$('.name', parentSel)[0].textContent;
|
||||||
|
|
||||||
|
121
public/min.php
121
public/min.php
@ -1,121 +0,0 @@
|
|||||||
<?php declare(strict_types=1);
|
|
||||||
/**
|
|
||||||
* Hummingbird Anime List Client
|
|
||||||
*
|
|
||||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
|
||||||
*
|
|
||||||
* PHP version 7
|
|
||||||
*
|
|
||||||
* @package HummingbirdAnimeClient
|
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
|
||||||
* @copyright 2015 - 2017 Timothy J. Warren
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
|
||||||
* @version 4.0
|
|
||||||
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
namespace Aviat\EasyMin;
|
|
||||||
|
|
||||||
//Creative rewriting of /g/groupname to ?g=groupname
|
|
||||||
$pi = $_SERVER['PATH_INFO'];
|
|
||||||
$pia = explode('/', $pi);
|
|
||||||
|
|
||||||
$piaLen = count($pia);
|
|
||||||
$i = 1;
|
|
||||||
|
|
||||||
while($i < $piaLen)
|
|
||||||
{
|
|
||||||
$j = $i+1;
|
|
||||||
$j = (isset($pia[$j])) ? $j : $i;
|
|
||||||
|
|
||||||
$_GET[$pia[$i]] = $pia[$j];
|
|
||||||
|
|
||||||
$i = $j + 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
class FileNotChangedException extends \Exception {}
|
|
||||||
class BaseMin {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get value of the if-modified-since header
|
|
||||||
*
|
|
||||||
* @return int - timestamp to compare for cache control
|
|
||||||
*/
|
|
||||||
protected function getIfModified()
|
|
||||||
{
|
|
||||||
return (array_key_exists('HTTP_IF_MODIFIED_SINCE', $_SERVER))
|
|
||||||
? strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])
|
|
||||||
: time();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get value of etag to compare to hash of output
|
|
||||||
*
|
|
||||||
* @return string - the etag to compare
|
|
||||||
*/
|
|
||||||
protected function getIfNoneMatch()
|
|
||||||
{
|
|
||||||
return (array_key_exists('HTTP_IF_NONE_MATCH', $_SERVER))
|
|
||||||
? $_SERVER['HTTP_IF_NONE_MATCH']
|
|
||||||
: '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether or not to send debug version
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
protected function isNotDebug()
|
|
||||||
{
|
|
||||||
return ! $this->isDebugCall();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether or not to send debug version
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
protected function isDebugCall()
|
|
||||||
{
|
|
||||||
return array_key_exists('debug', $_GET);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send actual output to browser
|
|
||||||
*
|
|
||||||
* @param string $content - the body of the response
|
|
||||||
* @param string $mimeType - the content type
|
|
||||||
* @param int $lastModified - the last modified date
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function sendFinalOutput($content, $mimeType, $lastModified)
|
|
||||||
{
|
|
||||||
//This GZIPs the CSS for transmission to the user
|
|
||||||
//making file size smaller and transfer rate quicker
|
|
||||||
ob_start("ob_gzhandler");
|
|
||||||
|
|
||||||
$expires = $lastModified + 691200;
|
|
||||||
$lastModifiedDate = gmdate('D, d M Y H:i:s', $lastModified);
|
|
||||||
$expiresDate = gmdate('D, d M Y H:i:s', $expires);
|
|
||||||
|
|
||||||
header("Content-Type: {$mimeType}; charset=utf8");
|
|
||||||
header("Cache-control: public, max-age=691200, must-revalidate");
|
|
||||||
header("Last-Modified: {$lastModifiedDate} GMT");
|
|
||||||
header("Expires: {$expiresDate} GMT");
|
|
||||||
|
|
||||||
echo $content;
|
|
||||||
|
|
||||||
ob_end_flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a 304 Not Modified header
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function send304()
|
|
||||||
{
|
|
||||||
header("status: 304 Not Modified", true, 304);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +1,13 @@
|
|||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "postcss -u postcss-import --autoprefixer.browsers \"> 5%\" -u postcss-cssnext -o css/base.css css/base.myth.css",
|
"build": "node ./css.js",
|
||||||
"watch": "postcss -u postcss-import --autoprefixer.browsers \"> 5%\" -u postcss-cssnext -w -o css/base.css css/base.myth.css"
|
"watch": "watch 'npm run build' --filter=./cssfilter.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^6.6.1",
|
"cssnano": "^3.10.0",
|
||||||
"npm-run-all": "^4.0.0",
|
|
||||||
"postcss-cachify": "^1.3.1",
|
"postcss-cachify": "^1.3.1",
|
||||||
"postcss-cli": "^2.6.0",
|
|
||||||
"postcss-cssnext": "^2.9.0",
|
"postcss-cssnext": "^2.9.0",
|
||||||
"postcss-import": "^9.0.0"
|
"postcss-import": "^9.0.0",
|
||||||
|
"watch": "^1.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +1,9 @@
|
|||||||
{{#data}}
|
{{#data}}
|
||||||
<article class="media search">
|
<article class="media search">
|
||||||
<div class="name" style="background-image:url({{attributes.posterImage.small}})">
|
<div class="name">
|
||||||
<input type="radio" class="big-check" id="{{attributes.slug}}" name="id" value="{{id}}" />
|
<input type="radio" class="big-check" id="{{attributes.slug}}" name="id" value="{{id}}" />
|
||||||
<label for="{{attributes.slug}}">
|
<label for="{{attributes.slug}}">
|
||||||
|
<img src="/public/images/anime/{{id}}.jpg" alt="" width="220" />
|
||||||
<span class="name">
|
<span class="name">
|
||||||
{{attributes.canonicalTitle}}
|
{{attributes.canonicalTitle}}
|
||||||
<br />
|
<br />
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
{{#data}}
|
{{#data}}
|
||||||
<article class="media search">
|
<article class="media search">
|
||||||
<div class="name" style="background-image:url({{attributes.posterImage.small}})">
|
<div class="name">
|
||||||
<input type="radio" class="big-check" id="{{attributes.slug}}" name="id" value="{{id}}" />
|
<input type="radio" class="big-check" id="{{attributes.slug}}" name="id" value="{{id}}" />
|
||||||
<label for="{{attributes.slug}}">
|
<label for="{{attributes.slug}}">
|
||||||
|
<img src="/public/images/manga/{{id}}.jpg" alt="" width="220" />
|
||||||
<span class="name">
|
<span class="name">
|
||||||
{{attributes.canonicalTitle}}
|
{{attributes.canonicalTitle}}
|
||||||
<br />
|
<br />
|
||||||
|
1836
public/yarn.lock
1836
public/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -54,7 +54,9 @@ class JsonAPI {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Reorganize included data
|
// Reorganize included data
|
||||||
$included = static::organizeIncluded($data['included']);
|
$included = (array_key_exists('included', $data))
|
||||||
|
? static::organizeIncluded($data['included'])
|
||||||
|
: [];
|
||||||
|
|
||||||
// Inline organized data
|
// Inline organized data
|
||||||
foreach($data['data'] as $i => &$item)
|
foreach($data['data'] as $i => &$item)
|
||||||
@ -125,23 +127,13 @@ class JsonAPI {
|
|||||||
$typeKey = $props['data'][$j]['type'];
|
$typeKey = $props['data'][$j]['type'];
|
||||||
$relationship =& $item['relationships'][$relType];
|
$relationship =& $item['relationships'][$relType];
|
||||||
|
|
||||||
unset($relationship['data'][$j]);
|
|
||||||
|
|
||||||
if (empty($relationship['data']))
|
|
||||||
{
|
|
||||||
unset($relationship['data']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($relType === $typeKey)
|
if ($relType === $typeKey)
|
||||||
{
|
{
|
||||||
$relationship[$idKey] = $included[$typeKey][$idKey];
|
$relationship[$idKey] = $included[$typeKey][$idKey];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$relationship[$typeKey][$idKey] = array_merge(
|
$relationship[$typeKey][$idKey][$j] = $included[$typeKey][$idKey];
|
||||||
$included[$typeKey][$idKey],
|
|
||||||
$relationship[$typeKey][$idKey] ?? []
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,6 +141,8 @@ class JsonAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$data['data']['included'] = $included;
|
||||||
|
|
||||||
return $data['data'];
|
return $data['data'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,11 +196,11 @@ class JsonAPI {
|
|||||||
{
|
{
|
||||||
foreach($items as $id => $item)
|
foreach($items as $id => $item)
|
||||||
{
|
{
|
||||||
if (array_key_exists('relationships', $item))
|
if (array_key_exists('relationships', $item) && is_array($item['relationships']))
|
||||||
{
|
{
|
||||||
foreach($item['relationships'] as $relType => $props)
|
foreach($item['relationships'] as $relType => $props)
|
||||||
{
|
{
|
||||||
if (array_key_exists('data', $props))
|
if (array_key_exists('data', $props) && is_array($props['data']) && array_key_exists('id', $props['data']))
|
||||||
{
|
{
|
||||||
if (array_key_exists($props['data']['id'], $organized[$props['data']['type']]))
|
if (array_key_exists($props['data']['id'], $organized[$props['data']['type']]))
|
||||||
{
|
{
|
||||||
|
@ -219,11 +219,11 @@ class Kitsu {
|
|||||||
|
|
||||||
foreach($existingTitles as $existing)
|
foreach($existingTitles as $existing)
|
||||||
{
|
{
|
||||||
$isSubset = stripos($existing, $title) !== FALSE;
|
$isSubset = mb_substr_count($existing, $title) > 0;
|
||||||
$diff = levenshtein($existing, $title);
|
$diff = levenshtein($existing, $title);
|
||||||
$onlydifferentCase = (mb_strtolower($existing) === mb_strtolower($title));
|
$onlydifferentCase = (mb_strtolower($existing) === mb_strtolower($title));
|
||||||
|
|
||||||
if ($diff < 3 OR $isSubset OR $onlydifferentCase)
|
if ($diff <= 3 OR $isSubset OR $onlydifferentCase OR mb_strlen($title) > 55)
|
||||||
{
|
{
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ use function Amp\wait;
|
|||||||
|
|
||||||
use Amp\Artax\{Client, Request};
|
use Amp\Artax\{Client, Request};
|
||||||
use Aviat\AnimeClient\AnimeClient;
|
use Aviat\AnimeClient\AnimeClient;
|
||||||
use Aviat\AnimeClient\API\Kitsu as K;
|
use Aviat\AnimeClient\API\{FailedResponseException, Kitsu as K};
|
||||||
use Aviat\Ion\Json;
|
use Aviat\Ion\Json;
|
||||||
|
|
||||||
trait KitsuTrait {
|
trait KitsuTrait {
|
||||||
@ -142,8 +142,10 @@ trait KitsuTrait {
|
|||||||
{
|
{
|
||||||
if ($logger)
|
if ($logger)
|
||||||
{
|
{
|
||||||
$logger->warning('Non 200 response for api call', (array)$response->getBody());
|
$logger->warning('Non 200 response for api call', (array)$response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new FailedResponseException('Failed to get the proper response from the API');
|
||||||
}
|
}
|
||||||
|
|
||||||
return Json::decode($response->getBody(), TRUE);
|
return Json::decode($response->getBody(), TRUE);
|
||||||
|
@ -47,7 +47,7 @@ class Model {
|
|||||||
use ContainerAware;
|
use ContainerAware;
|
||||||
use KitsuTrait;
|
use KitsuTrait;
|
||||||
|
|
||||||
const FULL_TRANSFORMED_LIST_CACHE_KEY = 'kitsu-full-organized-anime-list';
|
const LIST_PAGE_SIZE = 100;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to map anime list items
|
* Class to map anime list items
|
||||||
@ -160,13 +160,16 @@ class Model {
|
|||||||
*/
|
*/
|
||||||
public function getCharacter(string $slug): array
|
public function getCharacter(string $slug): array
|
||||||
{
|
{
|
||||||
// @todo catch non-existent characters and show 404
|
|
||||||
$data = $this->getRequest('/characters', [
|
$data = $this->getRequest('/characters', [
|
||||||
'query' => [
|
'query' => [
|
||||||
'filter' => [
|
'filter' => [
|
||||||
'name' => $slug
|
'slug' => $slug,
|
||||||
],
|
],
|
||||||
// 'include' => 'primaryMedia,castings'
|
'fields' => [
|
||||||
|
'anime' => 'canonicalTitle,titles,slug,posterImage',
|
||||||
|
'manga' => 'canonicalTitle,titles,slug,posterImage'
|
||||||
|
],
|
||||||
|
'include' => 'castings.person,castings.media'
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -235,9 +238,9 @@ class Model {
|
|||||||
*
|
*
|
||||||
* @param string $malId
|
* @param string $malId
|
||||||
* @param string $type "anime" or "manga"
|
* @param string $type "anime" or "manga"
|
||||||
* @return string
|
* @return string|NULL
|
||||||
*/
|
*/
|
||||||
public function getKitsuIdFromMALId(string $malId, string $type="anime"): string
|
public function getKitsuIdFromMALId(string $malId, string $type="anime")
|
||||||
{
|
{
|
||||||
$options = [
|
$options = [
|
||||||
'query' => [
|
'query' => [
|
||||||
@ -254,6 +257,11 @@ class Model {
|
|||||||
|
|
||||||
$raw = $this->getRequest('mappings', $options);
|
$raw = $this->getRequest('mappings', $options);
|
||||||
|
|
||||||
|
if ( ! array_key_exists('included', $raw))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return $raw['included'][0]['id'];
|
return $raw['included'][0]['id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,7 +285,7 @@ class Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$transformed = $this->animeTransformer->transform($baseData);
|
$transformed = $this->animeTransformer->transform($baseData);
|
||||||
$transformed['included'] = $baseData['included'];
|
$transformed['included'] = JsonAPI::organizeIncluded($baseData['included']);
|
||||||
return $transformed;
|
return $transformed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,7 +382,7 @@ class Model {
|
|||||||
{
|
{
|
||||||
$status = $options['filter']['status'] ?? '';
|
$status = $options['filter']['status'] ?? '';
|
||||||
$count = $this->getAnimeListCount($status);
|
$count = $this->getAnimeListCount($status);
|
||||||
$size = 100;
|
$size = static::LIST_PAGE_SIZE;
|
||||||
$pages = ceil($count / $size);
|
$pages = ceil($count / $size);
|
||||||
|
|
||||||
$requester = new ParallelAPIRequest();
|
$requester = new ParallelAPIRequest();
|
||||||
@ -460,7 +468,7 @@ class Model {
|
|||||||
* @param array $options
|
* @param array $options
|
||||||
* @return Request
|
* @return Request
|
||||||
*/
|
*/
|
||||||
public function getPagedAnimeList(int $limit = 100, int $offset = 0, array $options = [
|
public function getPagedAnimeList(int $limit, int $offset = 0, array $options = [
|
||||||
'include' => 'anime.mappings'
|
'include' => 'anime.mappings'
|
||||||
]): Request
|
]): Request
|
||||||
{
|
{
|
||||||
@ -618,7 +626,7 @@ class Model {
|
|||||||
{
|
{
|
||||||
$status = $options['filter']['status'] ?? '';
|
$status = $options['filter']['status'] ?? '';
|
||||||
$count = $this->getMangaListCount($status);
|
$count = $this->getMangaListCount($status);
|
||||||
$size = 100;
|
$size = static::LIST_PAGE_SIZE;
|
||||||
$pages = ceil($count / $size);
|
$pages = ceil($count / $size);
|
||||||
|
|
||||||
$requester = new ParallelAPIRequest();
|
$requester = new ParallelAPIRequest();
|
||||||
@ -668,7 +676,7 @@ class Model {
|
|||||||
* @param array $options
|
* @param array $options
|
||||||
* @return Request
|
* @return Request
|
||||||
*/
|
*/
|
||||||
public function getPagedMangaList(int $limit = 100, int $offset = 0, array $options = [
|
public function getPagedMangaList(int $limit, int $offset = 0, array $options = [
|
||||||
'include' => 'manga.mappings'
|
'include' => 'manga.mappings'
|
||||||
]): Request
|
]): Request
|
||||||
{
|
{
|
||||||
@ -821,6 +829,7 @@ class Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$baseData = $data['data']['attributes'];
|
$baseData = $data['data']['attributes'];
|
||||||
|
$baseData['id'] = $data['id'];
|
||||||
$baseData['included'] = $data['included'];
|
$baseData['included'] = $data['included'];
|
||||||
return $baseData;
|
return $baseData;
|
||||||
}
|
}
|
||||||
@ -856,6 +865,7 @@ class Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$baseData = $data['data'][0]['attributes'];
|
$baseData = $data['data'][0]['attributes'];
|
||||||
|
$baseData['id'] = $data['data'][0]['id'];
|
||||||
$baseData['included'] = $data['included'];
|
$baseData['included'] = $data['included'];
|
||||||
return $baseData;
|
return $baseData;
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,9 @@ class AnimeListTransformer extends AbstractTransformer {
|
|||||||
$genres = array_column($anime['relationships']['genres'], 'name') ?? [];
|
$genres = array_column($anime['relationships']['genres'], 'name') ?? [];
|
||||||
sort($genres);
|
sort($genres);
|
||||||
|
|
||||||
$rating = (int) 2 * $item['attributes']['rating'];
|
$rating = (int) $item['attributes']['rating'] !== 0
|
||||||
|
? (int) 2 * $item['attributes']['rating']
|
||||||
|
: '-';
|
||||||
|
|
||||||
$total_episodes = array_key_exists('episodeCount', $anime) && (int) $anime['episodeCount'] !== 0
|
$total_episodes = array_key_exists('episodeCount', $anime) && (int) $anime['episodeCount'] !== 0
|
||||||
? (int) $anime['episodeCount']
|
? (int) $anime['episodeCount']
|
||||||
@ -68,7 +70,7 @@ class AnimeListTransformer extends AbstractTransformer {
|
|||||||
'id' => $item['id'],
|
'id' => $item['id'],
|
||||||
'mal_id' => $MALid,
|
'mal_id' => $MALid,
|
||||||
'episodes' => [
|
'episodes' => [
|
||||||
'watched' => (int) $item['attributes']['progress'] !== '0'
|
'watched' => (int) $item['attributes']['progress'] !== 0
|
||||||
? (int) $item['attributes']['progress']
|
? (int) $item['attributes']['progress']
|
||||||
: '-',
|
: '-',
|
||||||
'total' => $total_episodes,
|
'total' => $total_episodes,
|
||||||
@ -80,6 +82,7 @@ class AnimeListTransformer extends AbstractTransformer {
|
|||||||
'ended' => $anime['endDate']
|
'ended' => $anime['endDate']
|
||||||
],
|
],
|
||||||
'anime' => [
|
'anime' => [
|
||||||
|
'id' => $animeId,
|
||||||
'age_rating' => $anime['ageRating'],
|
'age_rating' => $anime['ageRating'],
|
||||||
'title' => $anime['canonicalTitle'],
|
'title' => $anime['canonicalTitle'],
|
||||||
'titles' => Kitsu::filterTitles($anime),
|
'titles' => Kitsu::filterTitles($anime),
|
||||||
@ -93,7 +96,7 @@ class AnimeListTransformer extends AbstractTransformer {
|
|||||||
'notes' => $item['attributes']['notes'],
|
'notes' => $item['attributes']['notes'],
|
||||||
'rewatching' => (bool) $item['attributes']['reconsuming'],
|
'rewatching' => (bool) $item['attributes']['reconsuming'],
|
||||||
'rewatched' => (int) $item['attributes']['reconsumeCount'],
|
'rewatched' => (int) $item['attributes']['reconsumeCount'],
|
||||||
'user_rating' => ($rating === 0) ? '-' : (int) $rating,
|
'user_rating' => $rating,
|
||||||
'private' => (bool) $item['attributes']['private'] ?? FALSE,
|
'private' => (bool) $item['attributes']['private'] ?? FALSE,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -118,12 +121,16 @@ class AnimeListTransformer extends AbstractTransformer {
|
|||||||
'reconsuming' => $rewatching,
|
'reconsuming' => $rewatching,
|
||||||
'reconsumeCount' => $item['rewatched'],
|
'reconsumeCount' => $item['rewatched'],
|
||||||
'notes' => $item['notes'],
|
'notes' => $item['notes'],
|
||||||
'progress' => $item['episodes_watched'],
|
|
||||||
'private' => $privacy
|
'private' => $privacy
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
if (is_numeric($item['user_rating']))
|
if (is_numeric($item['episodes_watched']) && $item['episodes_watched'] > 0)
|
||||||
|
{
|
||||||
|
$untransformed['data']['progress'] = (int) $item['episodes_watched'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_numeric($item['user_rating']) && $item['user_rating'] > 0)
|
||||||
{
|
{
|
||||||
$untransformed['data']['rating'] = $item['user_rating'] / 2;
|
$untransformed['data']['rating'] = $item['user_rating'] / 2;
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ class AnimeTransformer extends AbstractTransformer {
|
|||||||
$titles = Kitsu::filterTitles($item);
|
$titles = Kitsu::filterTitles($item);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
'id' => $item['id'],
|
||||||
'slug' => $item['slug'],
|
'slug' => $item['slug'],
|
||||||
'title' => $titles[0],
|
'title' => $titles[0],
|
||||||
'titles' => $titles,
|
'titles' => $titles,
|
||||||
|
@ -42,18 +42,22 @@ class MangaListTransformer extends AbstractTransformer {
|
|||||||
$genres = array_column($manga['relationships']['genres'], 'name') ?? [];
|
$genres = array_column($manga['relationships']['genres'], 'name') ?? [];
|
||||||
sort($genres);
|
sort($genres);
|
||||||
|
|
||||||
$rating = (is_numeric($item['attributes']['rating']))
|
$rating = (int) $item['attributes']['rating'] !== 0
|
||||||
? intval(2 * $item['attributes']['rating'])
|
? (int) 2 * $item['attributes']['rating']
|
||||||
: '-';
|
: '-';
|
||||||
|
|
||||||
$totalChapters = ($manga['chapterCount'] > 0)
|
$totalChapters = ((int) $manga['chapterCount'] !== 0)
|
||||||
? $manga['chapterCount']
|
? $manga['chapterCount']
|
||||||
: '-';
|
: '-';
|
||||||
|
|
||||||
$totalVolumes = ($manga['volumeCount'] > 0)
|
$totalVolumes = ((int) $manga['volumeCount'] !== 0)
|
||||||
? $manga['volumeCount']
|
? $manga['volumeCount']
|
||||||
: '-';
|
: '-';
|
||||||
|
|
||||||
|
$readChapters = ((int) $item['attributes']['progress'] !== 0)
|
||||||
|
? $item['attributes']['progress']
|
||||||
|
: '-';
|
||||||
|
|
||||||
$MALid = NULL;
|
$MALid = NULL;
|
||||||
|
|
||||||
if (array_key_exists('mappings', $manga['relationships']))
|
if (array_key_exists('mappings', $manga['relationships']))
|
||||||
@ -72,7 +76,7 @@ class MangaListTransformer extends AbstractTransformer {
|
|||||||
'id' => $item['id'],
|
'id' => $item['id'],
|
||||||
'mal_id' => $MALid,
|
'mal_id' => $MALid,
|
||||||
'chapters' => [
|
'chapters' => [
|
||||||
'read' => $item['attributes']['progress'],
|
'read' => $readChapters,
|
||||||
'total' => $totalChapters
|
'total' => $totalChapters
|
||||||
],
|
],
|
||||||
'volumes' => [
|
'volumes' => [
|
||||||
@ -80,6 +84,7 @@ class MangaListTransformer extends AbstractTransformer {
|
|||||||
'total' => $totalVolumes
|
'total' => $totalVolumes
|
||||||
],
|
],
|
||||||
'manga' => [
|
'manga' => [
|
||||||
|
'id' => $mangaId,
|
||||||
'titles' => Kitsu::filterTitles($manga),
|
'titles' => Kitsu::filterTitles($manga),
|
||||||
'alternate_title' => NULL,
|
'alternate_title' => NULL,
|
||||||
'slug' => $manga['slug'],
|
'slug' => $manga['slug'],
|
||||||
@ -113,14 +118,18 @@ class MangaListTransformer extends AbstractTransformer {
|
|||||||
'mal_id' => $item['mal_id'],
|
'mal_id' => $item['mal_id'],
|
||||||
'data' => [
|
'data' => [
|
||||||
'status' => $item['status'],
|
'status' => $item['status'],
|
||||||
'progress' => (int)$item['chapters_read'],
|
|
||||||
'reconsuming' => $rereading,
|
'reconsuming' => $rereading,
|
||||||
'reconsumeCount' => (int)$item['reread_count'],
|
'reconsumeCount' => (int)$item['reread_count'],
|
||||||
'notes' => $item['notes'],
|
'notes' => $item['notes'],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
if (is_numeric($item['new_rating']))
|
if (is_numeric($item['chapters_read']) && $item['chapters_read'] > 0)
|
||||||
|
{
|
||||||
|
$map['data']['progress'] = (int)$item['chapters_read'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_numeric($item['new_rating']) && $item['new_rating'] > 0)
|
||||||
{
|
{
|
||||||
$map['data']['rating'] = $item['new_rating'] / 2;
|
$map['data']['rating'] = $item['new_rating'] / 2;
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ class MangaTransformer extends AbstractTransformer {
|
|||||||
sort($genres);
|
sort($genres);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
'id' => $item['id'],
|
||||||
'title' => $item['canonicalTitle'],
|
'title' => $item['canonicalTitle'],
|
||||||
'en_title' => $item['titles']['en'],
|
'en_title' => $item['titles']['en'],
|
||||||
'jp_title' => $item['titles']['en_jp'],
|
'jp_title' => $item['titles']['en_jp'],
|
||||||
|
@ -44,9 +44,7 @@ class AnimeListTransformer extends AbstractTransformer {
|
|||||||
{
|
{
|
||||||
$map = [
|
$map = [
|
||||||
'id' => $item['mal_id'],
|
'id' => $item['mal_id'],
|
||||||
'data' => [
|
'data' => []
|
||||||
'episode' => $item['data']['progress']
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$data =& $item['data'];
|
$data =& $item['data'];
|
||||||
@ -55,6 +53,10 @@ class AnimeListTransformer extends AbstractTransformer {
|
|||||||
{
|
{
|
||||||
switch($key)
|
switch($key)
|
||||||
{
|
{
|
||||||
|
case 'progress':
|
||||||
|
$map['data']['episode'] = $value;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'notes':
|
case 'notes':
|
||||||
$map['data']['comments'] = $value;
|
$map['data']['comments'] = $value;
|
||||||
break;
|
break;
|
||||||
|
@ -44,9 +44,7 @@ class MangaListTransformer extends AbstractTransformer {
|
|||||||
{
|
{
|
||||||
$map = [
|
$map = [
|
||||||
'id' => $item['mal_id'],
|
'id' => $item['mal_id'],
|
||||||
'data' => [
|
'data' => []
|
||||||
'chapter' => $item['data']['progress']
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$data =& $item['data'];
|
$data =& $item['data'];
|
||||||
@ -55,6 +53,10 @@ class MangaListTransformer extends AbstractTransformer {
|
|||||||
{
|
{
|
||||||
switch($key)
|
switch($key)
|
||||||
{
|
{
|
||||||
|
case 'progress':
|
||||||
|
$map['data']['chapter'] = $value;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'notes':
|
case 'notes':
|
||||||
$map['data']['comments'] = $value;
|
$map['data']['comments'] = $value;
|
||||||
break;
|
break;
|
||||||
|
@ -63,61 +63,48 @@ class SyncKitsuWithMal extends BaseCommand {
|
|||||||
$this->kitsuModel = $this->container->get('kitsu-model');
|
$this->kitsuModel = $this->container->get('kitsu-model');
|
||||||
$this->malModel = $this->container->get('mal-model');
|
$this->malModel = $this->container->get('mal-model');
|
||||||
|
|
||||||
$this->syncAnime();
|
$this->sync('anime');
|
||||||
$this->syncManga();
|
$this->sync('manga');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function syncAnime()
|
public function sync(string $type)
|
||||||
{
|
{
|
||||||
$malCount = count($this->malModel->getAnimeList());
|
$uType = ucfirst($type);
|
||||||
$kitsuCount = $this->kitsuModel->getAnimeListCount();
|
$malCount = count($this->malModel->{"get{$uType}List"}());
|
||||||
|
$kitsuCount = $this->kitsuModel->{"get{$uType}ListCount"}();
|
||||||
|
|
||||||
$this->echoBox("Number of MAL anime list items: {$malCount}");
|
$this->echoBox("Number of MAL {$type} list items: {$malCount}");
|
||||||
$this->echoBox("Number of Kitsu anime list items: {$kitsuCount}");
|
$this->echoBox("Number of Kitsu {$type} list items: {$kitsuCount}");
|
||||||
|
|
||||||
$data = $this->diffAnimeLists();
|
$data = $this->diffLists($type);
|
||||||
|
|
||||||
$this->echoBox("Number of anime items that need to be added to MAL: " . count($data['addToMAL']));
|
|
||||||
|
|
||||||
if ( ! empty($data['addToMAL']))
|
if ( ! empty($data['addToMAL']))
|
||||||
{
|
{
|
||||||
$this->echoBox("Adding missing anime list items to MAL");
|
$count = count($data['addToMAL']);
|
||||||
$this->createMALListItems($data['addToMAL'], 'anime');
|
$this->echoBox("Adding {$count} missing {$type} list items to MAL");
|
||||||
|
$this->createMALListItems($data['addToMAL'], $type);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->echoBox('Number of anime items that need to be added to Kitsu: ' . count($data['addToKitsu']));
|
|
||||||
|
|
||||||
if ( ! empty($data['addToKitsu']))
|
if ( ! empty($data['addToKitsu']))
|
||||||
{
|
{
|
||||||
$this->echoBox("Adding missing anime list items to Kitsu");
|
$count = count($data['addToKitsu']);
|
||||||
$this->createKitsuListItems($data['addToKitsu'], 'anime');
|
$this->echoBox("Adding {$count} missing {$type} list items to Kitsu");
|
||||||
}
|
$this->createKitsuListItems($data['addToKitsu'], $type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function syncManga()
|
if ( ! empty($data['updateMAL']))
|
||||||
{
|
{
|
||||||
$malCount = count($this->malModel->getMangaList());
|
$count = count($data['updateMAL']);
|
||||||
$kitsuCount = $this->kitsuModel->getMangaListCount();
|
$this->echoBox("Updating {$count} outdated MAL {$type} list items");
|
||||||
|
$this->updateMALListItems($data['updateMAL'], $type);
|
||||||
$this->echoBox("Number of MAL manga list items: {$malCount}");
|
|
||||||
$this->echoBox("Number of Kitsu manga list items: {$kitsuCount}");
|
|
||||||
|
|
||||||
$data = $this->diffMangaLists();
|
|
||||||
|
|
||||||
$this->echoBox("Number of manga items that need to be added to MAL: " . count($data['addToMAL']));
|
|
||||||
|
|
||||||
if ( ! empty($data['addToMAL']))
|
|
||||||
{
|
|
||||||
$this->echoBox("Adding missing manga list items to MAL");
|
|
||||||
$this->createMALListItems($data['addToMAL'], 'manga');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->echoBox('Number of manga items that need to be added to Kitsu: ' . count($data['addToKitsu']));
|
if ( ! empty($data['updateKitsu']))
|
||||||
|
|
||||||
if ( ! empty($data['addToKitsu']))
|
|
||||||
{
|
{
|
||||||
$this->echoBox("Adding missing manga list items to Kitsu");
|
print_r($data['updateKitsu']);
|
||||||
$this->createKitsuListItems($data['addToKitsu'], 'manga');
|
$count = count($data['updateKitsu']);
|
||||||
|
$this->echoBox("Updating {$count} outdated Kitsu {$type} list items");
|
||||||
|
$this->updateKitsuListItems($data['updateKitsu'], $type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,6 +123,19 @@ class SyncKitsuWithMal extends BaseCommand {
|
|||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function formatMALList(string $type): array
|
||||||
|
{
|
||||||
|
if ($type === 'anime')
|
||||||
|
{
|
||||||
|
return $this->formatMALAnimeList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type === 'manga')
|
||||||
|
{
|
||||||
|
return $this->formatMALMangaList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function formatMALAnimeList()
|
public function formatMALAnimeList()
|
||||||
{
|
{
|
||||||
$orig = $this->malModel->getAnimeList();
|
$orig = $this->malModel->getAnimeList();
|
||||||
@ -149,10 +149,6 @@ class SyncKitsuWithMal extends BaseCommand {
|
|||||||
'status' => AnimeWatchingStatus::MAL_TO_KITSU[$item['my_status']],
|
'status' => AnimeWatchingStatus::MAL_TO_KITSU[$item['my_status']],
|
||||||
'progress' => $item['my_watched_episodes'],
|
'progress' => $item['my_watched_episodes'],
|
||||||
'reconsuming' => (bool) $item['my_rewatching'],
|
'reconsuming' => (bool) $item['my_rewatching'],
|
||||||
'reconsumeCount' => array_key_exists('times_rewatched', $item)
|
|
||||||
? $item['times_rewatched']
|
|
||||||
: 0,
|
|
||||||
// 'notes' => ,
|
|
||||||
'rating' => $item['my_score'] / 2,
|
'rating' => $item['my_score'] / 2,
|
||||||
'updatedAt' => (new \DateTime())
|
'updatedAt' => (new \DateTime())
|
||||||
->setTimestamp((int)$item['my_last_updated'])
|
->setTimestamp((int)$item['my_last_updated'])
|
||||||
@ -179,10 +175,6 @@ class SyncKitsuWithMal extends BaseCommand {
|
|||||||
'progress' => $item['my_read_chapters'],
|
'progress' => $item['my_read_chapters'],
|
||||||
'volumes' => $item['my_read_volumes'],
|
'volumes' => $item['my_read_volumes'],
|
||||||
'reconsuming' => (bool) $item['my_rereadingg'],
|
'reconsuming' => (bool) $item['my_rereadingg'],
|
||||||
/* 'reconsumeCount' => array_key_exists('times_rewatched', $item)
|
|
||||||
? $item['times_rewatched']
|
|
||||||
: 0, */
|
|
||||||
// 'notes' => ,
|
|
||||||
'rating' => $item['my_score'] / 2,
|
'rating' => $item['my_score'] / 2,
|
||||||
'updatedAt' => (new \DateTime())
|
'updatedAt' => (new \DateTime())
|
||||||
->setTimestamp((int)$item['my_last_updated'])
|
->setTimestamp((int)$item['my_last_updated'])
|
||||||
@ -194,18 +186,18 @@ class SyncKitsuWithMal extends BaseCommand {
|
|||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function filterKitsuAnimeList()
|
public function formatKitsuList(string $type = 'anime'): array
|
||||||
{
|
{
|
||||||
$data = $this->kitsuModel->getFullAnimeList();
|
$data = $this->kitsuModel->{'getFull' . ucfirst($type) . 'List'}();
|
||||||
$includes = JsonAPI::organizeIncludes($data['included']);
|
$includes = JsonAPI::organizeIncludes($data['included']);
|
||||||
$includes['mappings'] = $this->filterMappings($includes['mappings']);
|
$includes['mappings'] = $this->filterMappings($includes['mappings'], $type);
|
||||||
|
|
||||||
$output = [];
|
$output = [];
|
||||||
|
|
||||||
foreach($data['data'] as $listItem)
|
foreach($data['data'] as $listItem)
|
||||||
{
|
{
|
||||||
$animeId = $listItem['relationships']['anime']['data']['id'];
|
$id = $listItem['relationships'][$type]['data']['id'];
|
||||||
$potentialMappings = $includes['anime'][$animeId]['relationships']['mappings'];
|
$potentialMappings = $includes[$type][$id]['relationships']['mappings'];
|
||||||
$malId = NULL;
|
$malId = NULL;
|
||||||
|
|
||||||
foreach ($potentialMappings as $mappingId)
|
foreach ($potentialMappings as $mappingId)
|
||||||
@ -232,94 +224,14 @@ class SyncKitsuWithMal extends BaseCommand {
|
|||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function filterKitsuMangaList()
|
public function diffLists(string $type = 'anime'): array
|
||||||
{
|
|
||||||
$data = $this->kitsuModel->getFullMangaList();
|
|
||||||
$includes = JsonAPI::organizeIncludes($data['included']);
|
|
||||||
$includes['mappings'] = $this->filterMappings($includes['mappings'], 'manga');
|
|
||||||
|
|
||||||
$output = [];
|
|
||||||
|
|
||||||
foreach($data['data'] as $listItem)
|
|
||||||
{
|
|
||||||
$mangaId = $listItem['relationships']['manga']['data']['id'];
|
|
||||||
$potentialMappings = $includes['manga'][$mangaId]['relationships']['mappings'];
|
|
||||||
$malId = NULL;
|
|
||||||
|
|
||||||
foreach ($potentialMappings as $mappingId)
|
|
||||||
{
|
|
||||||
if (array_key_exists($mappingId, $includes['mappings']))
|
|
||||||
{
|
|
||||||
$malId = $includes['mappings'][$mappingId]['externalId'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip to the next item if there isn't a MAL ID
|
|
||||||
if (is_null($malId))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$output[$listItem['id']] = [
|
|
||||||
'id' => $listItem['id'],
|
|
||||||
'malId' => $malId,
|
|
||||||
'data' => $listItem['attributes'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $output;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function diffMangaLists()
|
|
||||||
{
|
|
||||||
$kitsuList = $this->filterKitsuMangaList();
|
|
||||||
$malList = $this->formatMALMangaList();
|
|
||||||
|
|
||||||
$itemsToAddToMAL = [];
|
|
||||||
$itemsToAddToKitsu = [];
|
|
||||||
|
|
||||||
$malIds = array_column($malList, 'id');
|
|
||||||
$kitsuMalIds = array_column($kitsuList, 'malId');
|
|
||||||
$missingMalIds = array_diff($malIds, $kitsuMalIds);
|
|
||||||
|
|
||||||
foreach($missingMalIds as $mid)
|
|
||||||
{
|
|
||||||
$itemsToAddToKitsu[] = array_merge($malList[$mid]['data'], [
|
|
||||||
'id' => $this->kitsuModel->getKitsuIdFromMALId($mid, 'manga'),
|
|
||||||
'type' => 'manga'
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($kitsuList as $kitsuItem)
|
|
||||||
{
|
|
||||||
if (in_array($kitsuItem['malId'], $malIds))
|
|
||||||
{
|
|
||||||
// Eventually, compare the list entries, and determine which
|
|
||||||
// needs to be updated
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Looks like this item only exists on Kitsu
|
|
||||||
$itemsToAddToMAL[] = [
|
|
||||||
'mal_id' => $kitsuItem['malId'],
|
|
||||||
'data' => $kitsuItem['data']
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'addToMAL' => $itemsToAddToMAL,
|
|
||||||
'addToKitsu' => $itemsToAddToKitsu
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function diffAnimeLists()
|
|
||||||
{
|
{
|
||||||
// Get libraryEntries with media.mappings from Kitsu
|
// Get libraryEntries with media.mappings from Kitsu
|
||||||
// Organize mappings, and ignore entries without mappings
|
// Organize mappings, and ignore entries without mappings
|
||||||
$kitsuList = $this->filterKitsuAnimeList();
|
$kitsuList = $this->formatKitsuList($type);
|
||||||
|
|
||||||
// Get MAL list data
|
// Get MAL list data
|
||||||
$malList = $this->formatMALAnimeList();
|
$malList = $this->formatMALList($type);
|
||||||
|
|
||||||
$itemsToAddToMAL = [];
|
$itemsToAddToMAL = [];
|
||||||
$itemsToAddToKitsu = [];
|
$itemsToAddToKitsu = [];
|
||||||
@ -334,8 +246,8 @@ class SyncKitsuWithMal extends BaseCommand {
|
|||||||
{
|
{
|
||||||
// print_r($malList[$mid]);
|
// print_r($malList[$mid]);
|
||||||
$itemsToAddToKitsu[] = array_merge($malList[$mid]['data'], [
|
$itemsToAddToKitsu[] = array_merge($malList[$mid]['data'], [
|
||||||
'id' => $this->kitsuModel->getKitsuIdFromMALId($mid),
|
'id' => $this->kitsuModel->getKitsuIdFromMALId($mid, $type),
|
||||||
'type' => 'anime'
|
'type' => $type
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,8 +255,23 @@ class SyncKitsuWithMal extends BaseCommand {
|
|||||||
{
|
{
|
||||||
if (in_array($kitsuItem['malId'], $malIds))
|
if (in_array($kitsuItem['malId'], $malIds))
|
||||||
{
|
{
|
||||||
// Eventually, compare the list entries, and determine which
|
$item = $this->compareListItems($kitsuItem, $malList[$kitsuItem['malId']]);
|
||||||
// needs to be updated
|
|
||||||
|
if (is_null($item))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array('kitsu', $item['updateType']))
|
||||||
|
{
|
||||||
|
$kitsuUpdateItems[] = $item['data'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array('mal', $item['updateType']))
|
||||||
|
{
|
||||||
|
$malUpdateItems[] = $item['data'];
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,15 +283,6 @@ class SyncKitsuWithMal extends BaseCommand {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare each list entry
|
|
||||||
// If a list item exists only on MAL, create it on Kitsu with the existing data from MAL
|
|
||||||
// If a list item exists only on Kitsu, create it on MAL with the existing data from Kitsu
|
|
||||||
// If an item already exists on both APIS:
|
|
||||||
// Compare last updated dates, and use the later one
|
|
||||||
// Otherwise, use rewatch count, then episode progress as critera for selecting the more up
|
|
||||||
// to date entry
|
|
||||||
// Based on the 'newer' entry, update the other api list item
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'addToMAL' => $itemsToAddToMAL,
|
'addToMAL' => $itemsToAddToMAL,
|
||||||
'updateMAL' => $malUpdateItems,
|
'updateMAL' => $malUpdateItems,
|
||||||
@ -373,6 +291,174 @@ class SyncKitsuWithMal extends BaseCommand {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function compareListItems(array $kitsuItem, array $malItem)
|
||||||
|
{
|
||||||
|
$compareKeys = ['status', 'progress', 'rating', 'reconsuming'];
|
||||||
|
$diff = [];
|
||||||
|
$dateDiff = (new \DateTime($kitsuItem['data']['updatedAt'])) <=> (new \DateTime($malItem['data']['updatedAt']));
|
||||||
|
|
||||||
|
foreach($compareKeys as $key)
|
||||||
|
{
|
||||||
|
$diff[$key] = $kitsuItem['data'][$key] <=> $malItem['data'][$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
// No difference? Bail out early
|
||||||
|
$diffValues = array_values($diff);
|
||||||
|
$diffValues = array_unique($diffValues);
|
||||||
|
if (count($diffValues) === 1 && $diffValues[0] === 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$update = [
|
||||||
|
'id' => $kitsuItem['id'],
|
||||||
|
'mal_id' => $kitsuItem['malId'],
|
||||||
|
'data' => []
|
||||||
|
];
|
||||||
|
$return = [
|
||||||
|
'updateType' => []
|
||||||
|
];
|
||||||
|
|
||||||
|
$sameStatus = $diff['status'] === 0;
|
||||||
|
$sameProgress = $diff['progress'] === 0;
|
||||||
|
$sameRating = $diff['rating'] === 0;
|
||||||
|
|
||||||
|
|
||||||
|
// If status is the same, and progress count is different, use greater progress
|
||||||
|
if ($sameStatus && ( ! $sameProgress))
|
||||||
|
{
|
||||||
|
if ($diff['progress'] === 1)
|
||||||
|
{
|
||||||
|
$update['data']['progress'] = $kitsuItem['data']['progress'];
|
||||||
|
$return['updateType'][] = 'mal';
|
||||||
|
}
|
||||||
|
else if($diff['progress'] === -1)
|
||||||
|
{
|
||||||
|
$update['data']['progress'] = $malItem['data']['progress'];
|
||||||
|
$return['updateType'][] = 'kitsu';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If status and progress are different, it's a bit more complicated...
|
||||||
|
// But, at least for now, assume newer record is correct
|
||||||
|
if ( ! ($sameStatus || $sameProgress))
|
||||||
|
{
|
||||||
|
if ($dateDiff === 1)
|
||||||
|
{
|
||||||
|
$update['data']['status'] = $kitsuItem['data']['status'];
|
||||||
|
|
||||||
|
if ((int)$kitsuItem['data']['progress'] !== 0)
|
||||||
|
{
|
||||||
|
$update['data']['progress'] = $kitsuItem['data']['progress'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$return['updateType'][] = 'mal';
|
||||||
|
}
|
||||||
|
else if($dateDiff === -1)
|
||||||
|
{
|
||||||
|
$update['data']['status'] = $malItem['data']['status'];
|
||||||
|
|
||||||
|
if ((int)$malItem['data']['progress'] !== 0)
|
||||||
|
{
|
||||||
|
$update['data']['progress'] = $kitsuItem['data']['progress'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$return['updateType'][] = 'kitsu';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If rating is different, use the rating from the item most recently updated
|
||||||
|
if ( ! $sameRating)
|
||||||
|
{
|
||||||
|
if ($dateDiff === 1)
|
||||||
|
{
|
||||||
|
$update['data']['rating'] = $kitsuItem['data']['rating'];
|
||||||
|
$return['updateType'][] = 'mal';
|
||||||
|
}
|
||||||
|
else if ($dateDiff === -1)
|
||||||
|
{
|
||||||
|
$update['data']['rating'] = $malItem['data']['rating'];
|
||||||
|
$return['updateType'][] = 'kitsu';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If status is different, use the status of the more recently updated item
|
||||||
|
if ( ! $sameStatus)
|
||||||
|
{
|
||||||
|
if ($dateDiff === 1)
|
||||||
|
{
|
||||||
|
$update['data']['status'] = $kitsuItem['data']['status'];
|
||||||
|
$return['updateType'][] = 'mal';
|
||||||
|
}
|
||||||
|
else if ($dateDiff === -1)
|
||||||
|
{
|
||||||
|
$update['data']['status'] = $malItem['data']['status'];
|
||||||
|
$return['updateType'][] = 'kitsu';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$return['meta'] = [
|
||||||
|
'kitsu' => $kitsuItem['data'],
|
||||||
|
'mal' => $malItem['data'],
|
||||||
|
'dateDiff' => $dateDiff,
|
||||||
|
'diff' => $diff,
|
||||||
|
];
|
||||||
|
$return['data'] = $update;
|
||||||
|
$return['updateType'] = array_unique($return['updateType']);
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateKitsuListItems($itemsToUpdate, $type = 'anime')
|
||||||
|
{
|
||||||
|
$requester = new ParallelAPIRequest();
|
||||||
|
foreach($itemsToUpdate as $item)
|
||||||
|
{
|
||||||
|
$requester->addRequest($this->kitsuModel->updateListItem($item));
|
||||||
|
}
|
||||||
|
|
||||||
|
$responses = $requester->makeRequests();
|
||||||
|
|
||||||
|
foreach($responses as $key => $response)
|
||||||
|
{
|
||||||
|
$id = $itemsToUpdate[$key]['id'];
|
||||||
|
if ($response->getStatus() === 200)
|
||||||
|
{
|
||||||
|
$this->echoBox("Successfully updated Kitsu {$type} list item with id: {$id}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo $response->getBody();
|
||||||
|
$this->echoBox("Failed to update Kitsu {$type} list item with id: {$id}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateMALListItems($itemsToUpdate, $type = 'anime')
|
||||||
|
{
|
||||||
|
$transformer = new ALT();
|
||||||
|
$requester = new ParallelAPIRequest();
|
||||||
|
|
||||||
|
foreach($itemsToUpdate as $item)
|
||||||
|
{
|
||||||
|
$requester->addRequest($this->malModel->updateListItem($item, $type));
|
||||||
|
}
|
||||||
|
|
||||||
|
$responses = $requester->makeRequests();
|
||||||
|
|
||||||
|
foreach($responses as $key => $response)
|
||||||
|
{
|
||||||
|
$id = $itemsToUpdate[$key]['mal_id'];
|
||||||
|
if ($response->getBody() === 'Updated')
|
||||||
|
{
|
||||||
|
$this->echoBox("Successfully updated MAL {$type} list item with id: {$id}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->echoBox("Failed to update MAL {$type} list item with id: {$id}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function createKitsuListItems($itemsToAdd, $type = 'anime')
|
public function createKitsuListItems($itemsToAdd, $type = 'anime')
|
||||||
{
|
{
|
||||||
$requester = new ParallelAPIRequest();
|
$requester = new ParallelAPIRequest();
|
||||||
|
@ -239,6 +239,13 @@ class Controller {
|
|||||||
*/
|
*/
|
||||||
protected function renderFullPage($view, string $template, array $data)
|
protected function renderFullPage($view, string $template, array $data)
|
||||||
{
|
{
|
||||||
|
$csp = [
|
||||||
|
"default-src 'self'",
|
||||||
|
"object-src 'none'",
|
||||||
|
"child-src 'none'",
|
||||||
|
];
|
||||||
|
|
||||||
|
$view->addHeader('Content-Security-Policy', implode('; ', $csp));
|
||||||
$view->appendOutput($this->loadPartial($view, 'header', $data));
|
$view->appendOutput($this->loadPartial($view, 'header', $data));
|
||||||
|
|
||||||
if (array_key_exists('message', $data) && is_array($data['message']))
|
if (array_key_exists('message', $data) && is_array($data['message']))
|
||||||
|
@ -280,11 +280,11 @@ class Anime extends BaseController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($data['included'] as $included)
|
if (array_key_exists('characters', $data['included']))
|
||||||
{
|
{
|
||||||
if ($included['type'] === 'characters')
|
foreach($data['included']['characters'] as $id => $character)
|
||||||
{
|
{
|
||||||
$characters[$included['id']] = $included['attributes'];
|
$characters[$id] = $character['attributes'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,19 +17,23 @@
|
|||||||
namespace Aviat\AnimeClient\Controller;
|
namespace Aviat\AnimeClient\Controller;
|
||||||
|
|
||||||
use Aviat\AnimeClient\Controller as BaseController;
|
use Aviat\AnimeClient\Controller as BaseController;
|
||||||
|
use Aviat\AnimeClient\API\JsonAPI;
|
||||||
|
use Aviat\Ion\ArrayWrapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller for character description pages
|
* Controller for character description pages
|
||||||
*/
|
*/
|
||||||
class Character extends BaseController {
|
class Character extends BaseController {
|
||||||
|
|
||||||
|
use ArrayWrapper;
|
||||||
|
|
||||||
public function index(string $slug)
|
public function index(string $slug)
|
||||||
{
|
{
|
||||||
$model = $this->container->get('kitsu-model');
|
$model = $this->container->get('kitsu-model');
|
||||||
|
|
||||||
$data = $model->getCharacter($slug);
|
$rawData = $model->getCharacter($slug);
|
||||||
|
|
||||||
if (( ! array_key_exists('data', $data)) || empty($data['data']))
|
if (( ! array_key_exists('data', $rawData)) || empty($rawData['data']))
|
||||||
{
|
{
|
||||||
return $this->notFound(
|
return $this->notFound(
|
||||||
$this->formatTitle(
|
$this->formatTitle(
|
||||||
@ -40,12 +44,124 @@ class Character extends BaseController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->outputHTML('character', [
|
$data = JsonAPI::organizeData($rawData);
|
||||||
|
|
||||||
|
$viewData = [
|
||||||
'title' => $this->formatTitle(
|
'title' => $this->formatTitle(
|
||||||
'Characters',
|
'Characters',
|
||||||
$data['data'][0]['attributes']['name']
|
$data[0]['attributes']['name']
|
||||||
),
|
),
|
||||||
'data' => $data['data'][0]['attributes']
|
'data' => $data,
|
||||||
]);
|
'castCount' => 0,
|
||||||
|
'castings' => []
|
||||||
|
];
|
||||||
|
|
||||||
|
if (array_key_exists('included', $data) && array_key_exists('castings', $data['included']))
|
||||||
|
{
|
||||||
|
$viewData['castings'] = $this->organizeCast($data['included']['castings']);
|
||||||
|
$viewData['castCount'] = $this->getCastCount($viewData['castings']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->outputHTML('character', $viewData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Organize VA => anime relationships
|
||||||
|
*
|
||||||
|
* @param array $cast
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function dedupeCast(array $cast): array
|
||||||
|
{
|
||||||
|
$output = [];
|
||||||
|
$people = [];
|
||||||
|
|
||||||
|
$i = 0;
|
||||||
|
foreach ($cast as &$role)
|
||||||
|
{
|
||||||
|
if (empty($role['attributes']['role']))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$person = current($role['relationships']['person']['people'])['attributes'];
|
||||||
|
|
||||||
|
if ( ! array_key_exists($person['name'], $people))
|
||||||
|
{
|
||||||
|
$people[$person['name']] = $i;
|
||||||
|
$role['relationships']['media']['anime'] = [current($role['relationships']['media']['anime'])];
|
||||||
|
$output[$i] = $role;
|
||||||
|
|
||||||
|
$i++;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if(array_key_exists($person['name'], $people))
|
||||||
|
{
|
||||||
|
if (array_key_exists('anime', $role['relationships']['media']))
|
||||||
|
{
|
||||||
|
$key = $people[$person['name']];
|
||||||
|
$output[$key]['relationships']['media']['anime'][] = current($role['relationships']['media']['anime']);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getCastCount(array $cast): int
|
||||||
|
{
|
||||||
|
$count = 0;
|
||||||
|
|
||||||
|
foreach($cast as $role)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
array_key_exists('attributes', $role) &&
|
||||||
|
array_key_exists('role', $role['attributes']) &&
|
||||||
|
( ! is_null($role['attributes']['role']))
|
||||||
|
) {
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function organizeCast(array $cast): array
|
||||||
|
{
|
||||||
|
$cast = $this->dedupeCast($cast);
|
||||||
|
$output = [];
|
||||||
|
|
||||||
|
foreach($cast as $id => $role)
|
||||||
|
{
|
||||||
|
if (empty($role['attributes']['role']))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$language = $role['attributes']['language'];
|
||||||
|
$roleName = $role['attributes']['role'];
|
||||||
|
$isVA = $role['attributes']['voiceActor'];
|
||||||
|
|
||||||
|
if ($isVA)
|
||||||
|
{
|
||||||
|
$person = current($role['relationships']['person']['people'])['attributes'];
|
||||||
|
$name = $person['name'];
|
||||||
|
$item = [
|
||||||
|
'person' => $person,
|
||||||
|
'series' => $role['relationships']['media']['anime']
|
||||||
|
];
|
||||||
|
|
||||||
|
$output[$roleName][$language][] = $item;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$output[$roleName][] = $role['relationships']['person']['people'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,10 +16,16 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient\Controller;
|
namespace Aviat\AnimeClient\Controller;
|
||||||
|
|
||||||
|
use function Amp\wait;
|
||||||
|
|
||||||
|
use Amp\Artax\Client;
|
||||||
use Aviat\AnimeClient\Controller as BaseController;
|
use Aviat\AnimeClient\Controller as BaseController;
|
||||||
use Aviat\AnimeClient\API\JsonAPI;
|
use Aviat\AnimeClient\API\JsonAPI;
|
||||||
use Aviat\Ion\View\HtmlView;
|
use Aviat\Ion\View\HtmlView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for handling routes that don't fit elsewhere
|
||||||
|
*/
|
||||||
class Index extends BaseController {
|
class Index extends BaseController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -105,7 +111,7 @@ class Index extends BaseController {
|
|||||||
$data = $model->getUserData($username);
|
$data = $model->getUserData($username);
|
||||||
$orgData = JsonAPI::organizeData($data);
|
$orgData = JsonAPI::organizeData($data);
|
||||||
$this->outputHTML('me', [
|
$this->outputHTML('me', [
|
||||||
'title' => 'About' . $this->config->get('whose_list'),
|
'title' => 'About ' . $this->config->get('whose_list'),
|
||||||
'data' => $orgData[0],
|
'data' => $orgData[0],
|
||||||
'attributes' => $orgData[0]['attributes'],
|
'attributes' => $orgData[0]['attributes'],
|
||||||
'relationships' => $orgData[0]['relationships'],
|
'relationships' => $orgData[0]['relationships'],
|
||||||
@ -113,11 +119,55 @@ class Index extends BaseController {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get image covers from kitsu
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function images($type, $file)
|
||||||
|
{
|
||||||
|
$kitsuUrl = 'https://media.kitsu.io/';
|
||||||
|
list($id, $ext) = explode('.', basename($file));
|
||||||
|
switch ($type)
|
||||||
|
{
|
||||||
|
case 'anime':
|
||||||
|
$kitsuUrl .= "anime/poster_images/{$id}/small.{$ext}";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'avatars':
|
||||||
|
$kitsuUrl .= "users/avatars/{$id}/original.{$ext}";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'manga':
|
||||||
|
$kitsuUrl .= "manga/poster_images/{$id}/small.{$ext}";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'characters':
|
||||||
|
$kitsuUrl .= "characters/images/{$id}/original.{$ext}";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$this->notFound();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$promise = (new Client)->request($kitsuUrl);
|
||||||
|
$response = wait($promise);
|
||||||
|
$data = (string) $response->getBody();
|
||||||
|
|
||||||
|
$baseSavePath = $this->config->get('img_cache_path');
|
||||||
|
file_put_contents("{$baseSavePath}/{$type}/{$id}.{$ext}", $data);
|
||||||
|
header('Content-type: ' . $response->getHeader('content-type')[0]);
|
||||||
|
echo (string) $response->getBody();
|
||||||
|
}
|
||||||
|
|
||||||
private function organizeFavorites(array $rawfavorites): array
|
private function organizeFavorites(array $rawfavorites): array
|
||||||
{
|
{
|
||||||
// return $rawfavorites;
|
// return $rawfavorites;
|
||||||
$output = [];
|
$output = [];
|
||||||
|
|
||||||
|
unset($rawfavorites['data']);
|
||||||
|
|
||||||
foreach($rawfavorites as $item)
|
foreach($rawfavorites as $item)
|
||||||
{
|
{
|
||||||
$rank = $item['attributes']['favRank'];
|
$rank = $item['attributes']['favRank'];
|
||||||
@ -126,7 +176,7 @@ class Index extends BaseController {
|
|||||||
$output[$key] = $output[$key] ?? [];
|
$output[$key] = $output[$key] ?? [];
|
||||||
foreach ($fav as $id => $data)
|
foreach ($fav as $id => $data)
|
||||||
{
|
{
|
||||||
$output[$key][$rank] = $data['attributes'];
|
$output[$key][$rank] = array_merge(['id' => $id], $data['attributes']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ use const Aviat\AnimeClient\{
|
|||||||
|
|
||||||
use function Aviat\Ion\_dir;
|
use function Aviat\Ion\_dir;
|
||||||
|
|
||||||
|
use Aviat\AnimeClient\API\FailedResponseException;
|
||||||
use Aviat\Ion\Di\ContainerInterface;
|
use Aviat\Ion\Di\ContainerInterface;
|
||||||
use Aviat\Ion\Friend;
|
use Aviat\Ion\Friend;
|
||||||
|
|
||||||
@ -256,14 +257,25 @@ class Dispatcher extends RoutingBase {
|
|||||||
{
|
{
|
||||||
$logger = $this->container->getLogger('default');
|
$logger = $this->container->getLogger('default');
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
$controller = new $controllerName($this->container);
|
$controller = new $controllerName($this->container);
|
||||||
|
|
||||||
// Run the appropriate controller method
|
// Run the appropriate controller method
|
||||||
$logger->debug('Dispatcher - controller arguments');
|
$logger->debug('Dispatcher - controller arguments', $params);
|
||||||
$logger->debug(print_r($params, TRUE));
|
|
||||||
|
|
||||||
call_user_func_array([$controller, $method], $params);
|
call_user_func_array([$controller, $method], $params);
|
||||||
}
|
}
|
||||||
|
catch (FailedResponseException $e)
|
||||||
|
{
|
||||||
|
$controllerName = DEFAULT_CONTROLLER;
|
||||||
|
$controller = new $controllerName($this->container);
|
||||||
|
$controller->errorPage(500,
|
||||||
|
'API request timed out',
|
||||||
|
'Failed to retrieve data from API (╯°□°)╯︵ ┻━┻');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the appropriate params for the error page
|
* Get the appropriate params for the error page
|
||||||
|
@ -98,7 +98,7 @@ class MenuGenerator extends UrlGenerator {
|
|||||||
foreach ($menuConfig as $title => $path)
|
foreach ($menuConfig as $title => $path)
|
||||||
{
|
{
|
||||||
$has = $this->string($this->path())->contains($path);
|
$has = $this->string($this->path())->contains($path);
|
||||||
$selected = ($has && strlen($this->path()) >= strlen($path));
|
$selected = ($has && mb_strlen($this->path()) >= mb_strlen($path));
|
||||||
|
|
||||||
$link = $this->helper->a($this->url($path), $title);
|
$link = $this->helper->a($this->url($path), $title);
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ use Aviat\Ion\Friend;
|
|||||||
use Aviat\Ion\Json;
|
use Aviat\Ion\Json;
|
||||||
|
|
||||||
class AnimeListTransformerTest extends AnimeClientTestCase {
|
class AnimeListTransformerTest extends AnimeClientTestCase {
|
||||||
|
|
||||||
protected $dir;
|
protected $dir;
|
||||||
protected $beforeTransform;
|
protected $beforeTransform;
|
||||||
protected $afterTransform;
|
protected $afterTransform;
|
||||||
@ -34,19 +33,14 @@ class AnimeListTransformerTest extends AnimeClientTestCase {
|
|||||||
$this->dir = AnimeClientTestCase::TEST_DATA_DIR . '/Kitsu';
|
$this->dir = AnimeClientTestCase::TEST_DATA_DIR . '/Kitsu';
|
||||||
|
|
||||||
$this->beforeTransform = Json::decodeFile("{$this->dir}/animeListItemBeforeTransform.json");
|
$this->beforeTransform = Json::decodeFile("{$this->dir}/animeListItemBeforeTransform.json");
|
||||||
$this->afterTransform = Json::decodeFile("{$this->dir}/animeListItemAfterTransform.json");
|
|
||||||
|
|
||||||
$this->transformer = new AnimeListTransformer();
|
$this->transformer = new AnimeListTransformer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTransform()
|
public function testTransform()
|
||||||
{
|
{
|
||||||
$expected = $this->afterTransform;
|
|
||||||
$actual = $this->transformer->transform($this->beforeTransform);
|
$actual = $this->transformer->transform($this->beforeTransform);
|
||||||
|
$this->assertMatchesSnapshot($actual);
|
||||||
// Json::encodeFile("{$this->dir}/animeListItemAfterTransform.json", $actual);
|
|
||||||
|
|
||||||
$this->assertEquals($expected, $actual);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dataUntransform()
|
public function dataUntransform()
|
||||||
@ -60,19 +54,6 @@ class AnimeListTransformerTest extends AnimeClientTestCase {
|
|||||||
'rewatched' => 0,
|
'rewatched' => 0,
|
||||||
'notes' => 'Very formulaic.',
|
'notes' => 'Very formulaic.',
|
||||||
'edit' => true
|
'edit' => true
|
||||||
],
|
|
||||||
'expected' => [
|
|
||||||
'id' => 14047981,
|
|
||||||
'mal_id' => null,
|
|
||||||
'data' => [
|
|
||||||
'status' => 'current',
|
|
||||||
'rating' => 4,
|
|
||||||
'reconsuming' => false,
|
|
||||||
'reconsumeCount' => 0,
|
|
||||||
'notes' => 'Very formulaic.',
|
|
||||||
'progress' => 38,
|
|
||||||
'private' => false
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
], [
|
], [
|
||||||
'input' => [
|
'input' => [
|
||||||
@ -86,19 +67,19 @@ class AnimeListTransformerTest extends AnimeClientTestCase {
|
|||||||
'edit' => 'true',
|
'edit' => 'true',
|
||||||
'private' => 'On',
|
'private' => 'On',
|
||||||
'rewatching' => 'On'
|
'rewatching' => 'On'
|
||||||
],
|
|
||||||
'expected' => [
|
|
||||||
'id' => 14047981,
|
|
||||||
'mal_id' => '12345',
|
|
||||||
'data' => [
|
|
||||||
'status' => 'current',
|
|
||||||
'rating' => 4,
|
|
||||||
'reconsuming' => true,
|
|
||||||
'reconsumeCount' => 0,
|
|
||||||
'notes' => 'Very formulaic.',
|
|
||||||
'progress' => 38,
|
|
||||||
'private' => true,
|
|
||||||
]
|
]
|
||||||
|
], [
|
||||||
|
'input' => [
|
||||||
|
'id' => 14047983,
|
||||||
|
'mal_id' => '12347',
|
||||||
|
'watching_status' => 'current',
|
||||||
|
'user_rating' => 0,
|
||||||
|
'episodes_watched' => 12,
|
||||||
|
'rewatched' => 0,
|
||||||
|
'notes' => '',
|
||||||
|
'edit' => 'true',
|
||||||
|
'private' => 'On',
|
||||||
|
'rewatching' => 'On'
|
||||||
]
|
]
|
||||||
]];
|
]];
|
||||||
}
|
}
|
||||||
@ -106,9 +87,9 @@ class AnimeListTransformerTest extends AnimeClientTestCase {
|
|||||||
/**
|
/**
|
||||||
* @dataProvider dataUntransform
|
* @dataProvider dataUntransform
|
||||||
*/
|
*/
|
||||||
public function testUntransform($input, $expected)
|
public function testUntransform($input)
|
||||||
{
|
{
|
||||||
$actual = $this->transformer->untransform($input);
|
$actual = $this->transformer->untransform($input);
|
||||||
$this->assertEquals($expected, $actual);
|
$this->assertMatchesSnapshot($actual);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -34,7 +34,6 @@ class AnimeTransformerTest extends AnimeClientTestCase {
|
|||||||
$this->dir = AnimeClientTestCase::TEST_DATA_DIR . '/Kitsu';
|
$this->dir = AnimeClientTestCase::TEST_DATA_DIR . '/Kitsu';
|
||||||
|
|
||||||
$this->beforeTransform = Json::decodeFile("{$this->dir}/animeBeforeTransform.json");
|
$this->beforeTransform = Json::decodeFile("{$this->dir}/animeBeforeTransform.json");
|
||||||
$this->afterTransform = Json::decodeFile("{$this->dir}/animeAfterTransform.json");
|
|
||||||
|
|
||||||
$this->transformer = new AnimeTransformer();
|
$this->transformer = new AnimeTransformer();
|
||||||
}
|
}
|
||||||
@ -43,8 +42,7 @@ class AnimeTransformerTest extends AnimeClientTestCase {
|
|||||||
{
|
{
|
||||||
$expected = $this->afterTransform;
|
$expected = $this->afterTransform;
|
||||||
$actual = $this->transformer->transform($this->beforeTransform);
|
$actual = $this->transformer->transform($this->beforeTransform);
|
||||||
// Json::encodeFile("{$this->dir}/animeAfterTransform.json", $actual);
|
|
||||||
|
|
||||||
$this->assertEquals($expected, $actual);
|
$this->assertMatchesSnapshot($actual);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -47,19 +47,15 @@ class MangaListTransformerTest extends AnimeClientTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->beforeTransform = $rawBefore['data'];
|
$this->beforeTransform = $rawBefore['data'];
|
||||||
$this->afterTransform = Json::decodeFile("{$this->dir}/mangaListAfterTransform.json");
|
// $this->afterTransform = Json::decodeFile("{$this->dir}/mangaListAfterTransform.json");
|
||||||
|
|
||||||
$this->transformer = new MangaListTransformer();
|
$this->transformer = new MangaListTransformer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTransform()
|
public function testTransform()
|
||||||
{
|
{
|
||||||
$expected = $this->afterTransform;
|
|
||||||
$actual = $this->transformer->transformCollection($this->beforeTransform);
|
$actual = $this->transformer->transformCollection($this->beforeTransform);
|
||||||
|
$this->assertMatchesSnapshot($actual);
|
||||||
// Json::encodeFile("{$this->dir}/mangaListAfterTransform.json", $actual);
|
|
||||||
|
|
||||||
$this->assertEquals($expected, $actual);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUntransform()
|
public function testUntransform()
|
||||||
@ -69,6 +65,7 @@ class MangaListTransformerTest extends AnimeClientTestCase {
|
|||||||
'mal_id' => '26769',
|
'mal_id' => '26769',
|
||||||
'chapters_read' => 67,
|
'chapters_read' => 67,
|
||||||
'manga' => [
|
'manga' => [
|
||||||
|
'id' => '12345',
|
||||||
'titles' => ["Bokura wa Minna Kawaisou"],
|
'titles' => ["Bokura wa Minna Kawaisou"],
|
||||||
'alternate_title' => NULL,
|
'alternate_title' => NULL,
|
||||||
'slug' => "bokura-wa-minna-kawaisou",
|
'slug' => "bokura-wa-minna-kawaisou",
|
||||||
|
@ -36,8 +36,8 @@ class MangaTransformerTest extends AnimeClientTestCase {
|
|||||||
$data = Json::decodeFile("{$this->dir}/mangaBeforeTransform.json");
|
$data = Json::decodeFile("{$this->dir}/mangaBeforeTransform.json");
|
||||||
$baseData = $data['data'][0]['attributes'];
|
$baseData = $data['data'][0]['attributes'];
|
||||||
$baseData['included'] = $data['included'];
|
$baseData['included'] = $data['included'];
|
||||||
|
$baseData['id'] = $data['data'][0]['id'];
|
||||||
$this->beforeTransform = $baseData;
|
$this->beforeTransform = $baseData;
|
||||||
$this->afterTransform = Json::decodeFile("{$this->dir}/mangaAfterTransform.json");
|
|
||||||
|
|
||||||
$this->transformer = new MangaTransformer();
|
$this->transformer = new MangaTransformer();
|
||||||
}
|
}
|
||||||
@ -45,9 +45,6 @@ class MangaTransformerTest extends AnimeClientTestCase {
|
|||||||
public function testTransform()
|
public function testTransform()
|
||||||
{
|
{
|
||||||
$actual = $this->transformer->transform($this->beforeTransform);
|
$actual = $this->transformer->transform($this->beforeTransform);
|
||||||
$expected = $this->afterTransform;
|
$this->assertMatchesSnapshot($actual);
|
||||||
//Json::encodeFile("{$this->dir}/mangaAfterTransform.json", $actual);
|
|
||||||
|
|
||||||
$this->assertEquals($expected, $actual);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
<?php return array (
|
||||||
|
'id' => '15839442',
|
||||||
|
'mal_id' => '33206',
|
||||||
|
'episodes' =>
|
||||||
|
array (
|
||||||
|
'watched' => '-',
|
||||||
|
'total' => '-',
|
||||||
|
'length' => NULL,
|
||||||
|
),
|
||||||
|
'airing' =>
|
||||||
|
array (
|
||||||
|
'status' => 'Currently Airing',
|
||||||
|
'started' => '2017-01-12',
|
||||||
|
'ended' => NULL,
|
||||||
|
),
|
||||||
|
'anime' =>
|
||||||
|
array (
|
||||||
|
'id' => '12243',
|
||||||
|
'age_rating' => NULL,
|
||||||
|
'title' => 'Kobayashi-san Chi no Maid Dragon',
|
||||||
|
'titles' =>
|
||||||
|
array (
|
||||||
|
0 => 'Kobayashi-san Chi no Maid Dragon',
|
||||||
|
1 => 'Miss Kobayashi\'s Dragon Maid',
|
||||||
|
2 => '小林さんちのメイドラゴン',
|
||||||
|
),
|
||||||
|
'slug' => 'kobayashi-san-chi-no-maid-dragon',
|
||||||
|
'type' => 'TV',
|
||||||
|
'image' => 'https://media.kitsu.io/anime/poster_images/12243/small.jpg?1481144116',
|
||||||
|
'genres' =>
|
||||||
|
array (
|
||||||
|
0 => 'Comedy',
|
||||||
|
1 => 'Fantasy',
|
||||||
|
2 => 'Slice of Life',
|
||||||
|
),
|
||||||
|
'streaming_links' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'watching_status' => 'current',
|
||||||
|
'notes' => NULL,
|
||||||
|
'rewatching' => false,
|
||||||
|
'rewatched' => 0,
|
||||||
|
'user_rating' => '-',
|
||||||
|
'private' => false,
|
||||||
|
);
|
@ -0,0 +1,14 @@
|
|||||||
|
<?php return array (
|
||||||
|
'id' => 14047981,
|
||||||
|
'mal_id' => NULL,
|
||||||
|
'data' =>
|
||||||
|
array (
|
||||||
|
'status' => 'current',
|
||||||
|
'reconsuming' => false,
|
||||||
|
'reconsumeCount' => 0,
|
||||||
|
'notes' => 'Very formulaic.',
|
||||||
|
'progress' => 38,
|
||||||
|
'private' => false,
|
||||||
|
'rating' => 4,
|
||||||
|
),
|
||||||
|
);
|
@ -0,0 +1,14 @@
|
|||||||
|
<?php return array (
|
||||||
|
'id' => 14047981,
|
||||||
|
'mal_id' => '12345',
|
||||||
|
'data' =>
|
||||||
|
array (
|
||||||
|
'status' => 'current',
|
||||||
|
'reconsuming' => true,
|
||||||
|
'reconsumeCount' => 0,
|
||||||
|
'notes' => 'Very formulaic.',
|
||||||
|
'progress' => 38,
|
||||||
|
'private' => true,
|
||||||
|
'rating' => 4,
|
||||||
|
),
|
||||||
|
);
|
@ -0,0 +1,13 @@
|
|||||||
|
<?php return array (
|
||||||
|
'id' => 14047983,
|
||||||
|
'mal_id' => '12347',
|
||||||
|
'data' =>
|
||||||
|
array (
|
||||||
|
'status' => 'current',
|
||||||
|
'reconsuming' => true,
|
||||||
|
'reconsumeCount' => 0,
|
||||||
|
'notes' => '',
|
||||||
|
'progress' => 12,
|
||||||
|
'private' => true,
|
||||||
|
),
|
||||||
|
);
|
@ -0,0 +1,104 @@
|
|||||||
|
<?php return array (
|
||||||
|
'id' => 32344,
|
||||||
|
'slug' => 'attack-on-titan',
|
||||||
|
'title' => 'Attack on Titan',
|
||||||
|
'titles' =>
|
||||||
|
array (
|
||||||
|
0 => 'Attack on Titan',
|
||||||
|
1 => 'Shingeki no Kyojin',
|
||||||
|
2 => '進撃の巨人',
|
||||||
|
),
|
||||||
|
'status' => 'Finished Airing',
|
||||||
|
'cover_image' => 'https://media.kitsu.io/anime/poster_images/7442/small.jpg?1418580054',
|
||||||
|
'show_type' => 'TV',
|
||||||
|
'episode_count' => 25,
|
||||||
|
'episode_length' => 24,
|
||||||
|
'synopsis' => 'Several hundred years ago, humans were nearly exterminated by titans. Titans are typically several stories tall, seem to have no intelligence, devour human beings and, worst of all, seem to do it for the pleasure rather than as a food source. A small percentage of humanity survived by enclosing themselves in a city protected by extremely high walls, even taller than the biggest of titans. Flash forward to the present and the city has not seen a titan in over 100 years. Teenage boy Eren and his foster sister Mikasa witness something horrific as the city walls are destroyed by a colossal titan that appears out of thin air. As the smaller titans flood the city, the two kids watch in horror as their mother is eaten alive. Eren vows that he will murder every single titan and take revenge for all of mankind.
|
||||||
|
|
||||||
|
(Source: ANN)',
|
||||||
|
'age_rating' => 'R',
|
||||||
|
'age_rating_guide' => 'Violence, Profanity',
|
||||||
|
'url' => 'https://kitsu.io/anime/attack-on-titan',
|
||||||
|
'genres' =>
|
||||||
|
array (
|
||||||
|
0 => 'Action',
|
||||||
|
1 => 'Drama',
|
||||||
|
2 => 'Fantasy',
|
||||||
|
3 => 'Super Power',
|
||||||
|
),
|
||||||
|
'streaming_links' =>
|
||||||
|
array (
|
||||||
|
0 =>
|
||||||
|
array (
|
||||||
|
'meta' =>
|
||||||
|
array (
|
||||||
|
'name' => 'Crunchyroll',
|
||||||
|
'link' => true,
|
||||||
|
'image' => 'streaming-logos/crunchyroll.svg',
|
||||||
|
),
|
||||||
|
'link' => 'http://www.crunchyroll.com/attack-on-titan',
|
||||||
|
'subs' =>
|
||||||
|
array (
|
||||||
|
0 => 'en',
|
||||||
|
),
|
||||||
|
'dubs' =>
|
||||||
|
array (
|
||||||
|
0 => 'ja',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
1 =>
|
||||||
|
array (
|
||||||
|
'meta' =>
|
||||||
|
array (
|
||||||
|
'name' => 'Hulu',
|
||||||
|
'link' => true,
|
||||||
|
'image' => 'streaming-logos/hulu.svg',
|
||||||
|
),
|
||||||
|
'link' => 'http://www.hulu.com/attack-on-titan',
|
||||||
|
'subs' =>
|
||||||
|
array (
|
||||||
|
0 => 'en',
|
||||||
|
),
|
||||||
|
'dubs' =>
|
||||||
|
array (
|
||||||
|
0 => 'ja',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
2 =>
|
||||||
|
array (
|
||||||
|
'meta' =>
|
||||||
|
array (
|
||||||
|
'name' => 'Funimation',
|
||||||
|
'link' => true,
|
||||||
|
'image' => 'streaming-logos/funimation.svg',
|
||||||
|
),
|
||||||
|
'link' => 'http://www.funimation.com/shows/attack-on-titan/videos/episodes',
|
||||||
|
'subs' =>
|
||||||
|
array (
|
||||||
|
0 => 'en',
|
||||||
|
),
|
||||||
|
'dubs' =>
|
||||||
|
array (
|
||||||
|
0 => 'ja',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
3 =>
|
||||||
|
array (
|
||||||
|
'meta' =>
|
||||||
|
array (
|
||||||
|
'name' => 'Netflix',
|
||||||
|
'link' => false,
|
||||||
|
'image' => 'streaming-logos/netflix.svg',
|
||||||
|
),
|
||||||
|
'link' => 't',
|
||||||
|
'subs' =>
|
||||||
|
array (
|
||||||
|
0 => 'en',
|
||||||
|
),
|
||||||
|
'dubs' =>
|
||||||
|
array (
|
||||||
|
0 => 'ja',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
@ -0,0 +1,206 @@
|
|||||||
|
<?php return array (
|
||||||
|
0 =>
|
||||||
|
array (
|
||||||
|
'id' => '15084773',
|
||||||
|
'mal_id' => '26769',
|
||||||
|
'chapters' =>
|
||||||
|
array (
|
||||||
|
'read' => 67,
|
||||||
|
'total' => '-',
|
||||||
|
),
|
||||||
|
'volumes' =>
|
||||||
|
array (
|
||||||
|
'read' => '-',
|
||||||
|
'total' => '-',
|
||||||
|
),
|
||||||
|
'manga' =>
|
||||||
|
array (
|
||||||
|
'id' => '20286',
|
||||||
|
'titles' =>
|
||||||
|
array (
|
||||||
|
0 => 'Bokura wa Minna Kawaisou',
|
||||||
|
),
|
||||||
|
'alternate_title' => NULL,
|
||||||
|
'slug' => 'bokura-wa-minna-kawaisou',
|
||||||
|
'url' => 'https://kitsu.io/manga/bokura-wa-minna-kawaisou',
|
||||||
|
'type' => 'manga',
|
||||||
|
'image' => 'https://media.kitsu.io/manga/poster_images/20286/small.jpg?1434293999',
|
||||||
|
'genres' =>
|
||||||
|
array (
|
||||||
|
0 => 'Comedy',
|
||||||
|
1 => 'Romance',
|
||||||
|
2 => 'School',
|
||||||
|
3 => 'Slice of Life',
|
||||||
|
4 => 'Thriller',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'reading_status' => 'current',
|
||||||
|
'notes' => '',
|
||||||
|
'rereading' => false,
|
||||||
|
'reread' => 0,
|
||||||
|
'user_rating' => 9.0,
|
||||||
|
),
|
||||||
|
1 =>
|
||||||
|
array (
|
||||||
|
'id' => '15085607',
|
||||||
|
'mal_id' => '16',
|
||||||
|
'chapters' =>
|
||||||
|
array (
|
||||||
|
'read' => 17,
|
||||||
|
'total' => 120,
|
||||||
|
),
|
||||||
|
'volumes' =>
|
||||||
|
array (
|
||||||
|
'read' => '-',
|
||||||
|
'total' => 14,
|
||||||
|
),
|
||||||
|
'manga' =>
|
||||||
|
array (
|
||||||
|
'id' => '47',
|
||||||
|
'titles' =>
|
||||||
|
array (
|
||||||
|
0 => 'Love Hina',
|
||||||
|
),
|
||||||
|
'alternate_title' => NULL,
|
||||||
|
'slug' => 'love-hina',
|
||||||
|
'url' => 'https://kitsu.io/manga/love-hina',
|
||||||
|
'type' => 'manga',
|
||||||
|
'image' => 'https://media.kitsu.io/manga/poster_images/47/small.jpg?1434249493',
|
||||||
|
'genres' =>
|
||||||
|
array (
|
||||||
|
0 => 'Comedy',
|
||||||
|
1 => 'Ecchi',
|
||||||
|
2 => 'Harem',
|
||||||
|
3 => 'Romance',
|
||||||
|
4 => 'Sports',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'reading_status' => 'current',
|
||||||
|
'notes' => '',
|
||||||
|
'rereading' => false,
|
||||||
|
'reread' => 0,
|
||||||
|
'user_rating' => 7.0,
|
||||||
|
),
|
||||||
|
2 =>
|
||||||
|
array (
|
||||||
|
'id' => '15084529',
|
||||||
|
'mal_id' => '35003',
|
||||||
|
'chapters' =>
|
||||||
|
array (
|
||||||
|
'read' => 16,
|
||||||
|
'total' => '-',
|
||||||
|
),
|
||||||
|
'volumes' =>
|
||||||
|
array (
|
||||||
|
'read' => '-',
|
||||||
|
'total' => '-',
|
||||||
|
),
|
||||||
|
'manga' =>
|
||||||
|
array (
|
||||||
|
'id' => '11777',
|
||||||
|
'titles' =>
|
||||||
|
array (
|
||||||
|
0 => 'Yamada-kun to 7-nin no Majo',
|
||||||
|
1 => 'Yamada-kun and the Seven Witches',
|
||||||
|
),
|
||||||
|
'alternate_title' => NULL,
|
||||||
|
'slug' => 'yamada-kun-to-7-nin-no-majo',
|
||||||
|
'url' => 'https://kitsu.io/manga/yamada-kun-to-7-nin-no-majo',
|
||||||
|
'type' => 'manga',
|
||||||
|
'image' => 'https://media.kitsu.io/manga/poster_images/11777/small.jpg?1438784325',
|
||||||
|
'genres' =>
|
||||||
|
array (
|
||||||
|
0 => 'Comedy',
|
||||||
|
1 => 'Ecchi',
|
||||||
|
2 => 'Gender Bender',
|
||||||
|
3 => 'Romance',
|
||||||
|
4 => 'School',
|
||||||
|
5 => 'Sports',
|
||||||
|
6 => 'Supernatural',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'reading_status' => 'current',
|
||||||
|
'notes' => '',
|
||||||
|
'rereading' => false,
|
||||||
|
'reread' => 0,
|
||||||
|
'user_rating' => 9.0,
|
||||||
|
),
|
||||||
|
3 =>
|
||||||
|
array (
|
||||||
|
'id' => '15312827',
|
||||||
|
'mal_id' => '78523',
|
||||||
|
'chapters' =>
|
||||||
|
array (
|
||||||
|
'read' => 68,
|
||||||
|
'total' => '-',
|
||||||
|
),
|
||||||
|
'volumes' =>
|
||||||
|
array (
|
||||||
|
'read' => '-',
|
||||||
|
'total' => '-',
|
||||||
|
),
|
||||||
|
'manga' =>
|
||||||
|
array (
|
||||||
|
'id' => '27175',
|
||||||
|
'titles' =>
|
||||||
|
array (
|
||||||
|
0 => 'ReLIFE',
|
||||||
|
),
|
||||||
|
'alternate_title' => NULL,
|
||||||
|
'slug' => 'relife',
|
||||||
|
'url' => 'https://kitsu.io/manga/relife',
|
||||||
|
'type' => 'manga',
|
||||||
|
'image' => 'https://media.kitsu.io/manga/poster_images/27175/small.jpg?1464379411',
|
||||||
|
'genres' =>
|
||||||
|
array (
|
||||||
|
0 => 'Romance',
|
||||||
|
1 => 'School',
|
||||||
|
2 => 'Slice of Life',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'reading_status' => 'current',
|
||||||
|
'notes' => '',
|
||||||
|
'rereading' => false,
|
||||||
|
'reread' => 0,
|
||||||
|
'user_rating' => '-',
|
||||||
|
),
|
||||||
|
4 =>
|
||||||
|
array (
|
||||||
|
'id' => '15084769',
|
||||||
|
'mal_id' => '60815',
|
||||||
|
'chapters' =>
|
||||||
|
array (
|
||||||
|
'read' => 43,
|
||||||
|
'total' => '-',
|
||||||
|
),
|
||||||
|
'volumes' =>
|
||||||
|
array (
|
||||||
|
'read' => '-',
|
||||||
|
'total' => '-',
|
||||||
|
),
|
||||||
|
'manga' =>
|
||||||
|
array (
|
||||||
|
'id' => '25491',
|
||||||
|
'titles' =>
|
||||||
|
array (
|
||||||
|
0 => 'Joshikausei',
|
||||||
|
),
|
||||||
|
'alternate_title' => NULL,
|
||||||
|
'slug' => 'joshikausei',
|
||||||
|
'url' => 'https://kitsu.io/manga/joshikausei',
|
||||||
|
'type' => 'manga',
|
||||||
|
'image' => 'https://media.kitsu.io/manga/poster_images/25491/small.jpg?1434305043',
|
||||||
|
'genres' =>
|
||||||
|
array (
|
||||||
|
0 => 'Comedy',
|
||||||
|
1 => 'School',
|
||||||
|
2 => 'Slice of Life',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'reading_status' => 'current',
|
||||||
|
'notes' => '',
|
||||||
|
'rereading' => false,
|
||||||
|
'reread' => 0,
|
||||||
|
'user_rating' => 8.0,
|
||||||
|
),
|
||||||
|
);
|
@ -0,0 +1,21 @@
|
|||||||
|
<?php return array (
|
||||||
|
'id' => '20286',
|
||||||
|
'title' => 'Bokura wa Minna Kawaisou',
|
||||||
|
'en_title' => NULL,
|
||||||
|
'jp_title' => 'Bokura wa Minna Kawaisou',
|
||||||
|
'cover_image' => 'https://media.kitsu.io/manga/poster_images/20286/small.jpg?1434293999',
|
||||||
|
'manga_type' => 'manga',
|
||||||
|
'chapter_count' => '-',
|
||||||
|
'volume_count' => '-',
|
||||||
|
'synopsis' => 'Usa, a high-school student aspiring to begin a bachelor lifestyle, moves into a new apartment only to discover that he not only shares a room with a perverted roommate that has an obsession for underaged girls, but also that another girl, Ritsu, a love-at-first-sight, is living in the same building as well!
|
||||||
|
(Source: Kirei Cake)',
|
||||||
|
'url' => 'https://kitsu.io/manga/bokura-wa-minna-kawaisou',
|
||||||
|
'genres' =>
|
||||||
|
array (
|
||||||
|
0 => 'Comedy',
|
||||||
|
1 => 'Romance',
|
||||||
|
2 => 'School',
|
||||||
|
3 => 'Slice of Life',
|
||||||
|
4 => 'Thriller',
|
||||||
|
),
|
||||||
|
);
|
@ -23,6 +23,7 @@ use function Aviat\Ion\_dir;
|
|||||||
use Aura\Web\WebFactory;
|
use Aura\Web\WebFactory;
|
||||||
use Aviat\Ion\Json;
|
use Aviat\Ion\Json;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Spatie\Snapshots\MatchesSnapshots;
|
||||||
use Zend\Diactoros\{
|
use Zend\Diactoros\{
|
||||||
Response as HttpResponse,
|
Response as HttpResponse,
|
||||||
ServerRequestFactory
|
ServerRequestFactory
|
||||||
@ -36,6 +37,9 @@ define('TEST_VIEW_DIR', __DIR__ . '/test_views');
|
|||||||
* Base class for TestCases
|
* Base class for TestCases
|
||||||
*/
|
*/
|
||||||
class AnimeClientTestCase extends TestCase {
|
class AnimeClientTestCase extends TestCase {
|
||||||
|
use MatchesSnapshots;
|
||||||
|
|
||||||
|
|
||||||
// Test directory constants
|
// Test directory constants
|
||||||
const ROOT_DIR = ROOT_DIR;
|
const ROOT_DIR = ROOT_DIR;
|
||||||
const SRC_DIR = SRC_DIR;
|
const SRC_DIR = SRC_DIR;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"id": 32344,
|
||||||
"slug": "attack-on-titan",
|
"slug": "attack-on-titan",
|
||||||
"synopsis": "Several hundred years ago, humans were nearly exterminated by titans. Titans are typically several stories tall, seem to have no intelligence, devour human beings and, worst of all, seem to do it for the pleasure rather than as a food source. A small percentage of humanity survived by enclosing themselves in a city protected by extremely high walls, even taller than the biggest of titans. Flash forward to the present and the city has not seen a titan in over 100 years. Teenage boy Eren and his foster sister Mikasa witness something horrific as the city walls are destroyed by a colossal titan that appears out of thin air. As the smaller titans flood the city, the two kids watch in horror as their mother is eaten alive. Eren vows that he will murder every single titan and take revenge for all of mankind.\n\n(Source: ANN)",
|
"synopsis": "Several hundred years ago, humans were nearly exterminated by titans. Titans are typically several stories tall, seem to have no intelligence, devour human beings and, worst of all, seem to do it for the pleasure rather than as a food source. A small percentage of humanity survived by enclosing themselves in a city protected by extremely high walls, even taller than the biggest of titans. Flash forward to the present and the city has not seen a titan in over 100 years. Teenage boy Eren and his foster sister Mikasa witness something horrific as the city walls are destroyed by a colossal titan that appears out of thin air. As the smaller titans flood the city, the two kids watch in horror as their mother is eaten alive. Eren vows that he will murder every single titan and take revenge for all of mankind.\n\n(Source: ANN)",
|
||||||
"coverImageTopOffset": 263,
|
"coverImageTopOffset": 263,
|
||||||
|
Loading…
Reference in New Issue
Block a user