Merge branch 'develop' of timw4mail/HummingBirdAnimeClient into master
This commit is contained in:
commit
87d0960424
@ -181,6 +181,7 @@ $routes = [
|
|||||||
'user_info' => [
|
'user_info' => [
|
||||||
'path' => '/user/{username}',
|
'path' => '/user/{username}',
|
||||||
'controller' => 'user',
|
'controller' => 'user',
|
||||||
|
'action' => 'about',
|
||||||
'tokens' => [
|
'tokens' => [
|
||||||
'username' => '.*?'
|
'username' => '.*?'
|
||||||
]
|
]
|
||||||
|
@ -20,5 +20,3 @@ host = "127.0.0.1"
|
|||||||
|
|
||||||
# Database number
|
# Database number
|
||||||
database = 2
|
database = 2
|
||||||
|
|
||||||
[options]
|
|
@ -2,52 +2,55 @@
|
|||||||
<main class="details fixed">
|
<main class="details fixed">
|
||||||
<section class="flex">
|
<section class="flex">
|
||||||
<aside class="info">
|
<aside class="info">
|
||||||
<?= $helper->picture("images/anime/{$show_data['id']}-original.webp") ?>
|
<?= $helper->picture("images/anime/{$data['id']}-original.webp") ?>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<table class="media-details">
|
<table class="media-details">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="align-right">Airing Status</td>
|
<td class="align-right">Airing Status</td>
|
||||||
<td><?= $show_data['status'] ?></td>
|
<td><?= $data['status'] ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Show Type</td>
|
<td>Show Type</td>
|
||||||
<td><?= $show_data['show_type'] ?></td>
|
<td><?= $data['show_type'] ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Episode Count</td>
|
<td>Episode Count</td>
|
||||||
<td><?= $show_data['episode_count'] ?? '-' ?></td>
|
<td><?= $data['episode_count'] ?? '-' ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php if ( ! empty($show_data['episode_length'])): ?>
|
<?php if ( ! empty($data['episode_length'])): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Episode Length</td>
|
<td>Episode Length</td>
|
||||||
<td><?= $show_data['episode_length'] ?> minutes</td>
|
<td><?= $data['episode_length'] ?> minutes</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<?php if ( ! empty($show_data['age_rating'])): ?>
|
<?php if ( ! empty($data['age_rating'])): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Age Rating</td>
|
<td>Age Rating</td>
|
||||||
<td><abbr title="<?= $show_data['age_rating_guide'] ?>"><?= $show_data['age_rating'] ?></abbr>
|
<td><abbr title="<?= $data['age_rating_guide'] ?>"><?= $data['age_rating'] ?></abbr>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Genres</td>
|
<td>Genres</td>
|
||||||
<td>
|
<td>
|
||||||
<?= implode(', ', $show_data['genres']) ?>
|
<?= implode(', ', $data['genres']) ?>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
<article class="text">
|
<article class="text">
|
||||||
<h2 class="toph"><a rel="external" href="<?= $show_data['url'] ?>"><?= $show_data['title'] ?></a></h2>
|
<h2 class="toph"><a rel="external" href="<?= $data['url'] ?>"><?= $data['title'] ?></a></h2>
|
||||||
<?php foreach ($show_data['titles'] as $title): ?>
|
<?php foreach ($data['titles'] as $title): ?>
|
||||||
<h3><?= $title ?></h3>
|
<h3><?= $title ?></h3>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
<br />
|
<br />
|
||||||
<p class="description"><?= nl2br($show_data['synopsis']) ?></p>
|
<p class="description"><?= nl2br($data['synopsis']) ?></p>
|
||||||
<?php if (count($show_data['streaming_links']) > 0): ?>
|
<?php if (count($data['streaming_links']) > 0): ?>
|
||||||
<hr />
|
<hr />
|
||||||
<h4>Streaming on:</h4>
|
<h4>Streaming on:</h4>
|
||||||
<table class="full-width invisible streaming-links">
|
<table class="full-width invisible streaming-links">
|
||||||
@ -59,13 +62,13 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php foreach ($show_data['streaming_links'] as $link): ?>
|
<?php foreach ($data['streaming_links'] as $link): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="align-left">
|
<td class="align-left">
|
||||||
<?php if ($link['meta']['link'] !== FALSE): ?>
|
<?php if ($link['meta']['link'] !== FALSE): ?>
|
||||||
<a
|
<a
|
||||||
href="<?= $link['link'] ?>"
|
href="<?= $link['link'] ?>"
|
||||||
title="Stream '<?= $show_data['title'] ?>' on <?= $link['meta']['name'] ?>"
|
title="Stream '<?= $data['title'] ?>' on <?= $link['meta']['name'] ?>"
|
||||||
>
|
>
|
||||||
<?= $helper->picture("images/{$link['meta']['image']}", 'svg', [
|
<?= $helper->picture("images/{$link['meta']['image']}", 'svg', [
|
||||||
'class' => 'streaming-logo',
|
'class' => 'streaming-logo',
|
||||||
@ -92,13 +95,13 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<?php if ( ! empty($show_data['trailer_id'])): ?>
|
<?php if ( ! empty($data['trailer_id'])): ?>
|
||||||
<div class="responsive-iframe">
|
<div class="responsive-iframe">
|
||||||
<h4>Trailer</h4>
|
<h4>Trailer</h4>
|
||||||
<iframe
|
<iframe
|
||||||
width="560"
|
width="560"
|
||||||
height="315"
|
height="315"
|
||||||
src="https://www.youtube.com/embed/<?= $show_data['trailer_id'] ?>"
|
src="https://www.youtube.com/embed/<?= $data['trailer_id'] ?>"
|
||||||
frameborder="0"
|
frameborder="0"
|
||||||
allow="autoplay; encrypted-media"
|
allow="autoplay; encrypted-media"
|
||||||
allowfullscreen
|
allowfullscreen
|
||||||
@ -108,13 +111,13 @@
|
|||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<?php if (count($characters) > 0): ?>
|
<?php if (count($data['characters']) > 0): ?>
|
||||||
<section>
|
<section>
|
||||||
<h2>Characters</h2>
|
<h2>Characters</h2>
|
||||||
|
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<?php $i = 0 ?>
|
<?php $i = 0 ?>
|
||||||
<?php foreach ($characters as $role => $list): ?>
|
<?php foreach ($data['characters'] as $role => $list): ?>
|
||||||
<input
|
<input
|
||||||
type="radio" name="character-types"
|
type="radio" name="character-types"
|
||||||
id="character-types-<?= $i ?>" <?= ($i === 0) ? 'checked' : '' ?> />
|
id="character-types-<?= $i ?>" <?= ($i === 0) ? 'checked' : '' ?> />
|
||||||
@ -140,14 +143,13 @@
|
|||||||
</section>
|
</section>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<?php if (count($staff) > 0): ?>
|
<?php if (count($data['staff']) > 0): ?>
|
||||||
<?php //dump($staff); ?>
|
|
||||||
<section>
|
<section>
|
||||||
<h2>Staff</h2>
|
<h2>Staff</h2>
|
||||||
|
|
||||||
<div class="vertical-tabs">
|
<div class="vertical-tabs">
|
||||||
<?php $i = 0; ?>
|
<?php $i = 0; ?>
|
||||||
<?php foreach ($staff as $role => $people): ?>
|
<?php foreach ($data['staff'] as $role => $people): ?>
|
||||||
<div class="tab">
|
<div class="tab">
|
||||||
<input type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
|
<input type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
|
||||||
<label for="staff-role<?= $i ?>"><?= $role ?></label>
|
<label for="staff-role<?= $i ?>"><?= $role ?></label>
|
||||||
|
@ -7,10 +7,10 @@ use Aviat\AnimeClient\API\Kitsu;
|
|||||||
<main class="details fixed">
|
<main class="details fixed">
|
||||||
<section class="flex flex-no-wrap">
|
<section class="flex flex-no-wrap">
|
||||||
<div>
|
<div>
|
||||||
<?= $helper->picture("images/characters/{$data[0]['id']}-original.webp") ?>
|
<?= $helper->picture("images/characters/{$data['id']}-original.webp") ?>
|
||||||
<?php if ( ! empty($data[0]['attributes']['otherNames'])): ?>
|
<?php if ( ! empty($data['otherNames'])): ?>
|
||||||
<h3>Nicknames / Other names</h3>
|
<h3>Nicknames / Other names</h3>
|
||||||
<?php foreach ($data[0]['attributes']['otherNames'] as $name): ?>
|
<?php foreach ($data['otherNames'] as $name): ?>
|
||||||
<h4><?= $name ?></h4>
|
<h4><?= $name ?></h4>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
@ -23,19 +23,19 @@ use Aviat\AnimeClient\API\Kitsu;
|
|||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<p class="description"><?= $data[0]['attributes']['description'] ?></p>
|
<p class="description"><?= $data['description'] ?></p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<?php if (array_key_exists('anime', $data['included']) || array_key_exists('manga', $data['included'])): ?>
|
<?php if ( ! (empty($data['media']['anime']) || empty($data['media']['manga']))): ?>
|
||||||
<h3>Media</h3>
|
<h3>Media</h3>
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<?php if (array_key_exists('anime', $data['included'])): ?>
|
<?php if ( ! empty($data['media']['anime'])): ?>
|
||||||
<input checked="checked" type="radio" id="media-anime" name="media-tabs" />
|
<input checked="checked" type="radio" id="media-anime" name="media-tabs" />
|
||||||
<label for="media-anime">Anime</label>
|
<label for="media-anime">Anime</label>
|
||||||
|
|
||||||
<section class="media-wrap content">
|
<section class="media-wrap content">
|
||||||
<?php foreach ($data['included']['anime'] as $id => $anime): ?>
|
<?php foreach ($data['media']['anime'] as $id => $anime): ?>
|
||||||
<article class="media">
|
<article class="media">
|
||||||
<?php
|
<?php
|
||||||
$link = $url->generate('anime.details', ['id' => $anime['attributes']['slug']]);
|
$link = $url->generate('anime.details', ['id' => $anime['attributes']['slug']]);
|
||||||
@ -58,12 +58,12 @@ use Aviat\AnimeClient\API\Kitsu;
|
|||||||
</section>
|
</section>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<?php if (array_key_exists('manga', $data['included'])): ?>
|
<?php if ( ! empty($data['media']['manga'])): ?>
|
||||||
<input type="radio" id="media-manga" name="media-tabs" />
|
<input type="radio" id="media-manga" name="media-tabs" />
|
||||||
<label for="media-manga">Manga</label>
|
<label for="media-manga">Manga</label>
|
||||||
|
|
||||||
<section class="media-wrap content">
|
<section class="media-wrap content">
|
||||||
<?php foreach ($data['included']['manga'] as $id => $manga): ?>
|
<?php foreach ($data['media']['manga'] as $id => $manga): ?>
|
||||||
<article class="media">
|
<article class="media">
|
||||||
<?php
|
<?php
|
||||||
$link = $url->generate('manga.details', ['id' => $manga['attributes']['slug']]);
|
$link = $url->generate('manga.details', ['id' => $manga['attributes']['slug']]);
|
||||||
@ -89,14 +89,68 @@ use Aviat\AnimeClient\API\Kitsu;
|
|||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<?php if ($castCount > 0): ?>
|
<?php if (count($data['castings']) > 0): ?>
|
||||||
<h3>Castings</h3>
|
<h3>Castings</h3>
|
||||||
<?php
|
<?php
|
||||||
$vas = $castings['Voice Actor'];
|
$vas = $data['castings']['Voice Actor'];
|
||||||
unset($castings['Voice Actor']);
|
unset($data['castings']['Voice Actor']);
|
||||||
ksort($vas)
|
ksort($vas)
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
<?php foreach ($data['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 $cid => $c): ?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<article class="character">
|
||||||
|
<?php
|
||||||
|
$link = $url->generate('person', ['id' => $c['person']['id']]);
|
||||||
|
?>
|
||||||
|
<a href="<?= $link ?>">
|
||||||
|
<?= $helper->picture(getLocalImg($c['person']['image'], TRUE)) ?>
|
||||||
|
<div class="name">
|
||||||
|
<?= $c['person']['name'] ?>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</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 ?>">
|
||||||
|
<?= $helper->picture(getLocalImg($series['attributes']['posterImage']['small'], TRUE)) ?>
|
||||||
|
</a>
|
||||||
|
<div class="name">
|
||||||
|
<a href="<?= $link ?>">
|
||||||
|
<?= array_shift($titles) ?>
|
||||||
|
<?php foreach ($titles as $title): ?>
|
||||||
|
<br />
|
||||||
|
<small><?= $title ?></small>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</section>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</table>
|
||||||
|
<?php endforeach ?>
|
||||||
|
<?php endforeach ?>
|
||||||
|
|
||||||
<?php if ( ! empty($vas)): ?>
|
<?php if ( ! empty($vas)): ?>
|
||||||
<h4>Voice Actors</h4>
|
<h4>Voice Actors</h4>
|
||||||
|
|
||||||
@ -161,61 +215,6 @@ use Aviat\AnimeClient\API\Kitsu;
|
|||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
|
|
||||||
<?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 $cid => $c): ?>
|
|
||||||
<tr>
|
|
||||||
<td style="width:229px">
|
|
||||||
<article class="character">
|
|
||||||
<?php
|
|
||||||
$link = $url->generate('person', ['id' => $c['person']['id']]);
|
|
||||||
?>
|
|
||||||
<a href="<?= $link ?>">
|
|
||||||
<?= $helper->picture(getLocalImg($c['person']['image'], TRUE)) ?>
|
|
||||||
<div class="name">
|
|
||||||
<?= $c['person']['name'] ?>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</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 ?>">
|
|
||||||
<?= $helper->picture(getLocalImg($series['attributes']['posterImage']['small'], TRUE)) ?>
|
|
||||||
</a>
|
|
||||||
<div class="name">
|
|
||||||
<a href="<?= $link ?>">
|
|
||||||
<?= array_shift($titles) ?>
|
|
||||||
<?php foreach ($titles as $title): ?>
|
|
||||||
<br />
|
|
||||||
<small><?= $title ?></small>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</section>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</table>
|
|
||||||
<?php endforeach ?>
|
|
||||||
<?php endforeach ?>
|
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
@ -2,7 +2,7 @@
|
|||||||
<main>
|
<main>
|
||||||
<h2>Edit Anime Collection Item</h2>
|
<h2>Edit Anime Collection Item</h2>
|
||||||
<form action="<?= $action_url ?>" method="post">
|
<form action="<?= $action_url ?>" method="post">
|
||||||
<table class="invisible form" style="border:0">
|
<table class="invisible form">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td rowspan="6" class="align-center">
|
<td rowspan="6" class="align-center">
|
||||||
@ -28,7 +28,7 @@
|
|||||||
<td class="align-left">
|
<td class="align-left">
|
||||||
<select name="media_id" id="media_id">
|
<select name="media_id" id="media_id">
|
||||||
<?php foreach($media_items as $id => $name): ?>
|
<?php foreach($media_items as $id => $name): ?>
|
||||||
<option <?= $item['media_id'] == $id ? 'selected="selected"' : '' ?> value="<?= $id ?>"><?= $name ?></option>
|
<option <?= $item['media_id'] === $id ? 'selected="selected"' : '' ?> value="<?= $id ?>"><?= $name ?></option>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<a href="<?= $url->generate('anime.details', ['id' => $item['slug']]) ?>">
|
<a href="<?= $url->generate('anime.details', ['id' => $item['slug']]) ?>">
|
||||||
<?= $item['title'] ?>
|
<?= $item['title'] ?>
|
||||||
</a>
|
</a>
|
||||||
<?= (!empty($item['alternate_title'])) ? " <br /><small> " . $item['alternate_title'] . "</small>" : "" ?>
|
<?= ! empty($item['alternate_title']) ? ' <br /><small> ' . $item['alternate_title'] . '</small>' : '' ?>
|
||||||
</td>
|
</td>
|
||||||
<td><?= $item['episode_count'] ?></td>
|
<td><?= $item['episode_count'] ?></td>
|
||||||
<td><?= $item['episode_length'] ?></td>
|
<td><?= $item['episode_length'] ?></td>
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
<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=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=1" />
|
||||||
<link rel="stylesheet" href="<?= $urlGenerator->assetUrl('css/app.min.css') ?>" />
|
<link rel="stylesheet" href="<?= $urlGenerator->assetUrl('css/app.min.css') ?>" />
|
||||||
|
<link rel="<?= $config->get('dark_theme') ? '' : 'alternate ' ?>stylesheet" title="Dark Theme" href="<?= $urlGenerator->assetUrl('css/dark.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') ?>">
|
||||||
@ -38,4 +39,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
</header>
|
</header>
|
@ -25,6 +25,8 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<br />
|
||||||
</aside>
|
</aside>
|
||||||
<article class="text">
|
<article class="text">
|
||||||
<h2 class="toph"><a rel="external" href="<?= $data['url'] ?>"><?= $data['title'] ?></a></h2>
|
<h2 class="toph"><a rel="external" href="<?= $data['url'] ?>"><?= $data['title'] ?></a></h2>
|
||||||
@ -37,11 +39,12 @@
|
|||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<?php if (count($characters) > 0): ?>
|
<?php if (count($data['characters']) > 0): ?>
|
||||||
<h2>Characters</h2>
|
<h2>Characters</h2>
|
||||||
|
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<?php $i = 0 ?>
|
<?php $i = 0 ?>
|
||||||
<?php foreach ($characters as $role => $list): ?>
|
<?php foreach ($data['characters'] as $role => $list): ?>
|
||||||
<input
|
<input
|
||||||
type="radio" name="character-role-tabs"
|
type="radio" name="character-role-tabs"
|
||||||
id="character-tabs<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
|
id="character-tabs<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
|
||||||
@ -66,12 +69,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<?php if (count($staff) > 0): ?>
|
<?php if (count($data['staff']) > 0): ?>
|
||||||
<h2>Staff</h2>
|
<h2>Staff</h2>
|
||||||
|
|
||||||
<div class="vertical-tabs">
|
<div class="vertical-tabs">
|
||||||
<?php $i = 0 ?>
|
<?php $i = 0 ?>
|
||||||
<?php foreach ($staff as $role => $people): ?>
|
<?php foreach ($data['staff'] as $role => $people): ?>
|
||||||
<div class="tab">
|
<div class="tab">
|
||||||
<input
|
<input
|
||||||
type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
|
type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
|
||||||
|
@ -5,7 +5,7 @@ use Aviat\AnimeClient\API\Kitsu;
|
|||||||
<h3>Voice Acting Roles</h3>
|
<h3>Voice Acting Roles</h3>
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<?php $i = 0; ?>
|
<?php $i = 0; ?>
|
||||||
<?php foreach($characters as $role => $characterList): ?>
|
<?php foreach($data['characters'] as $role => $characterList): ?>
|
||||||
<input <?= $i === 0 ? 'checked="checked"' : '' ?> type="radio" name="character-type-tabs" id="character-type-<?= $i ?>" />
|
<input <?= $i === 0 ? 'checked="checked"' : '' ?> type="radio" name="character-type-tabs" id="character-type-<?= $i ?>" />
|
||||||
<label for="character-type-<?= $i ?>"><h5><?= ucfirst($role) ?></h5></label>
|
<label for="character-type-<?= $i ?>"><h5><?= ucfirst($role) ?></h5></label>
|
||||||
<section class="content">
|
<section class="content">
|
||||||
@ -16,7 +16,7 @@ use Aviat\AnimeClient\API\Kitsu;
|
|||||||
</tr>
|
</tr>
|
||||||
<?php foreach ($characterList as $cid => $character): ?>
|
<?php foreach ($characterList as $cid => $character): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="width:229px">
|
<td>
|
||||||
<article class="character">
|
<article class="character">
|
||||||
<?php
|
<?php
|
||||||
$link = $url->generate('character', ['slug' => $character['character']['slug']]);
|
$link = $url->generate('character', ['slug' => $character['character']['slug']]);
|
||||||
|
@ -9,16 +9,16 @@ use Aviat\AnimeClient\API\Kitsu;
|
|||||||
<?= $helper->picture("images/people/{$data['id']}-original.webp", 'jpg', ['class' => 'cover' ]) ?>
|
<?= $helper->picture("images/people/{$data['id']}-original.webp", 'jpg', ['class' => 'cover' ]) ?>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2 class="toph"><?= $data['attributes']['name'] ?></h2>
|
<h2 class="toph"><?= $data['name'] ?></h2>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<?php if ( ! empty($staff)): ?>
|
<?php if ( ! empty($data['staff'])): ?>
|
||||||
<section>
|
<section>
|
||||||
<h3>Castings</h3>
|
<h3>Castings</h3>
|
||||||
<div class="vertical-tabs">
|
<div class="vertical-tabs">
|
||||||
<?php $i = 0 ?>
|
<?php $i = 0 ?>
|
||||||
<?php foreach ($staff as $role => $entries): ?>
|
<?php foreach ($data['staff'] as $role => $entries): ?>
|
||||||
<div class="tab">
|
<div class="tab">
|
||||||
<input
|
<input
|
||||||
type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
|
type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
|
||||||
@ -59,7 +59,7 @@ use Aviat\AnimeClient\API\Kitsu;
|
|||||||
</section>
|
</section>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<?php if ( ! (empty($characters['main']) || empty($characters['supporting']))): ?>
|
<?php if ( ! (empty($data['characters']['main']) || empty($data['characters']['supporting']))): ?>
|
||||||
<section>
|
<section>
|
||||||
<?php include 'character-mapping.php' ?>
|
<?php include 'character-mapping.php' ?>
|
||||||
</section>
|
</section>
|
||||||
|
@ -1,45 +1,39 @@
|
|||||||
<?php
|
<?php
|
||||||
use function Aviat\AnimeClient\getLocalImg;
|
|
||||||
use Aviat\AnimeClient\API\Kitsu;
|
use Aviat\AnimeClient\API\Kitsu;
|
||||||
?>
|
?>
|
||||||
<main class="user-page details">
|
<main class="user-page details">
|
||||||
<h2 class="toph">
|
<h2 class="toph">
|
||||||
<?= $helper->a(
|
<?= $helper->a(
|
||||||
"https://kitsu.io/users/{$attributes['slug']}",
|
"https://kitsu.io/users/{$data['slug']}",
|
||||||
$attributes['name'], [
|
$data['name'], [
|
||||||
'title' => 'View profile on Kitsu'
|
'title' => 'View profile on Kitsu'
|
||||||
])
|
])
|
||||||
?>
|
?>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<p><?= $escape->html($attributes['about']) ?></p>
|
<p><?= $escape->html($data['about']) ?></p>
|
||||||
|
|
||||||
<section class="flex flex-no-wrap">
|
<section class="flex flex-no-wrap">
|
||||||
<aside class="info">
|
<aside class="info">
|
||||||
<center>
|
<center>
|
||||||
<?php
|
<?= $helper->img($urlGenerator->assetUrl($data['avatar']), ['alt' => '']); ?>
|
||||||
$avatar = $urlGenerator->assetUrl(
|
|
||||||
getLocalImg($attributes['avatar']['original'], FALSE)
|
|
||||||
);
|
|
||||||
echo $helper->img($avatar, ['alt' => '']);
|
|
||||||
?>
|
|
||||||
</center>
|
</center>
|
||||||
<br />
|
<br />
|
||||||
<table class="media-details">
|
<table class="media-details">
|
||||||
<tr>
|
<tr>
|
||||||
<td>Location</td>
|
<td>Location</td>
|
||||||
<td><?= $attributes['location'] ?></td>
|
<td><?= $data['location'] ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Website</td>
|
<td>Website</td>
|
||||||
<td><?= $helper->a($attributes['website'], $attributes['website']) ?></td>
|
<td><?= $helper->a($data['website'], $data['website']) ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php if (array_key_exists('waifu', $relationships)): ?>
|
<?php if ( ! empty($data['waifu'])): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td><?= $escape->html($attributes['waifuOrHusbando']) ?></td>
|
<td><?= $escape->html($data['waifu']['label']) ?></td>
|
||||||
<td>
|
<td>
|
||||||
<?php
|
<?php
|
||||||
$character = $relationships['waifu']['attributes'];
|
$character = $data['waifu']['character'];
|
||||||
echo $helper->a(
|
echo $helper->a(
|
||||||
$url->generate('character', ['slug' => $character['slug']]),
|
$url->generate('character', ['slug' => $character['slug']]),
|
||||||
$character['canonicalName']
|
$character['canonicalName']
|
||||||
@ -52,42 +46,24 @@ use Aviat\AnimeClient\API\Kitsu;
|
|||||||
|
|
||||||
<h3>User Stats</h3><br />
|
<h3>User Stats</h3><br />
|
||||||
<table class="media-details">
|
<table class="media-details">
|
||||||
|
<?php foreach($data['stats'] as $label => $stat): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Time spent watching anime:</td>
|
<td><?= $label ?></td>
|
||||||
<td><?= $timeOnAnime ?></td>
|
<td><?= $stat ?></td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td># of Anime episodes watched</td>
|
|
||||||
<td><?= number_format($stats['anime-amount-consumed']['units']) ?></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td># of Manga chapters read</td>
|
|
||||||
<td><?= number_format($stats['manga-amount-consumed']['units']) ?></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td># of Posts</td>
|
|
||||||
<td><?= number_format($attributes['postsCount']) ?></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td># of Comments</td>
|
|
||||||
<td><?= number_format($attributes['commentsCount']) ?></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td># of Media Rated</td>
|
|
||||||
<td><?= number_format($attributes['ratingsCount']) ?></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
|
<?php endforeach ?>
|
||||||
</table>
|
</table>
|
||||||
</aside>
|
</aside>
|
||||||
<article>
|
<article>
|
||||||
<?php if ( ! empty($favorites)): ?>
|
<?php if ( ! empty($data['favorites'])): ?>
|
||||||
<h3>Favorites</h3>
|
<h3>Favorites</h3>
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<?php $i = 0 ?>
|
<?php $i = 0 ?>
|
||||||
<?php if ( ! empty($favorites['characters'])): ?>
|
<?php if ( ! empty($data['favorites']['characters'])): ?>
|
||||||
<input type="radio" name="user-favorites" id="user-fav-chars" <?= $i === 0 ? 'checked' : '' ?> />
|
<input type="radio" name="user-favorites" id="user-fav-chars" <?= $i === 0 ? 'checked' : '' ?> />
|
||||||
<label for="user-fav-chars">Characters</label>
|
<label for="user-fav-chars">Characters</label>
|
||||||
<section class="content full-width media-wrap">
|
<section class="content full-width media-wrap">
|
||||||
<?php foreach($favorites['characters'] as $id => $char): ?>
|
<?php foreach($data['favorites']['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']]) ?>
|
||||||
@ -101,11 +77,11 @@ use Aviat\AnimeClient\API\Kitsu;
|
|||||||
</section>
|
</section>
|
||||||
<?php $i++; ?>
|
<?php $i++; ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<?php if ( ! empty($favorites['anime'])): ?>
|
<?php if ( ! empty($data['favorites']['anime'])): ?>
|
||||||
<input type="radio" name="user-favorites" id="user-fav-anime" <?= $i === 0 ? 'checked' : '' ?> />
|
<input type="radio" name="user-favorites" id="user-fav-anime" <?= $i === 0 ? 'checked' : '' ?> />
|
||||||
<label for="user-fav-anime">Anime</label>
|
<label for="user-fav-anime">Anime</label>
|
||||||
<section class="content full-width media-wrap">
|
<section class="content full-width media-wrap">
|
||||||
<?php foreach($favorites['anime'] as $anime): ?>
|
<?php foreach($data['favorites']['anime'] as $anime): ?>
|
||||||
<article class="media">
|
<article class="media">
|
||||||
<?php
|
<?php
|
||||||
$link = $url->generate('anime.details', ['id' => $anime['slug']]);
|
$link = $url->generate('anime.details', ['id' => $anime['slug']]);
|
||||||
@ -127,11 +103,11 @@ use Aviat\AnimeClient\API\Kitsu;
|
|||||||
</section>
|
</section>
|
||||||
<?php $i++; ?>
|
<?php $i++; ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<?php if ( ! empty($favorites['manga'])): ?>
|
<?php if ( ! empty($data['favorites']['manga'])): ?>
|
||||||
<input type="radio" name="user-favorites" id="user-fav-manga" <?= $i === 0 ? 'checked' : '' ?> />
|
<input type="radio" name="user-favorites" id="user-fav-manga" <?= $i === 0 ? 'checked' : '' ?> />
|
||||||
<label for="user-fav-manga">Manga</label>
|
<label for="user-fav-manga">Manga</label>
|
||||||
<section class="content full-width media-wrap">
|
<section class="content full-width media-wrap">
|
||||||
<?php foreach($favorites['manga'] as $manga): ?>
|
<?php foreach($data['favorites']['manga'] as $manga): ?>
|
||||||
<article class="media">
|
<article class="media">
|
||||||
<?php
|
<?php
|
||||||
$link = $url->generate('manga.details', ['id' => $manga['slug']]);
|
$link = $url->generate('manga.details', ['id' => $manga['slug']]);
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
"aura/html": "^2.0",
|
"aura/html": "^2.0",
|
||||||
"aura/router": "^3.0",
|
"aura/router": "^3.0",
|
||||||
"aura/session": "^2.0",
|
"aura/session": "^2.0",
|
||||||
"aviat/banker": "^1.0.0",
|
"aviat/banker": "^2.0.0",
|
||||||
"aviat/ion": "^2.4.1",
|
"aviat/ion": "^2.4.1",
|
||||||
"ext-iconv": "*",
|
"ext-iconv": "*",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
@ -37,28 +37,30 @@
|
|||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"consolidation/robo": "~1.0",
|
"consolidation/robo": "~1.0",
|
||||||
|
"filp/whoops": "^2.1",
|
||||||
"henrikbjorn/lurker": "^1.1.0",
|
"henrikbjorn/lurker": "^1.1.0",
|
||||||
"pdepend/pdepend": "^2.2",
|
"pdepend/pdepend": "^2.2",
|
||||||
"phploc/phploc": "^4.0",
|
"phploc/phploc": "^4.0",
|
||||||
"phpmd/phpmd": "^2.4",
|
"phpmd/phpmd": "^2.4",
|
||||||
"phpstan/phpstan": "^0.9.1",
|
"phpstan/phpstan": "^0.10.5",
|
||||||
"phpunit/phpunit": "^6.0",
|
"phpunit/phpunit": "^7.4.3",
|
||||||
"roave/security-advisories": "dev-master",
|
"roave/security-advisories": "dev-master",
|
||||||
"robmorgan/phinx": "^0.10.6",
|
"robmorgan/phinx": "^0.10.6",
|
||||||
"sebastian/phpcpd": "^3.0",
|
"sebastian/phpcpd": "^4.1.0",
|
||||||
"spatie/phpunit-snapshot-assertions": "^1.2.0",
|
"spatie/phpunit-snapshot-assertions": "^1.2.0",
|
||||||
"squizlabs/php_codesniffer": "^3.2.2",
|
"squizlabs/php_codesniffer": "^3.2.2",
|
||||||
"symfony/var-dumper": "^4.0.1",
|
"symfony/var-dumper": "^4.0.1",
|
||||||
"theseer/phpdox": "^0.11.0",
|
"theseer/phpdox": "*"
|
||||||
"filp/whoops": "^2.1"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vendor/bin/robo build",
|
"build": "vendor/bin/robo build",
|
||||||
"build:css": "cd public && npm run build && cd ..",
|
"build:css": "cd public && npm run build:css && cd ..",
|
||||||
|
"build:js": "cd public && npm run build:js && cd ..",
|
||||||
"clean": "vendor/bin/robo clean",
|
"clean": "vendor/bin/robo clean",
|
||||||
"coverage": "phpdbg -qrr -- vendor/bin/phpunit -c build",
|
"coverage": "phpdbg -qrr -- vendor/bin/phpunit -c build",
|
||||||
"phpstan": "phpstan analyse -l 4 -c phpstan.neon src tests ./console index.php",
|
"phpstan": "phpstan analyse -l 4 -c phpstan.neon src tests ./console index.php",
|
||||||
"watch:css": "cd public && npm run watch",
|
"watch:css": "cd public && npm run watch:css",
|
||||||
|
"watch:js": "cd public && npm run watch:js",
|
||||||
"test": "vendor/bin/phpunit"
|
"test": "vendor/bin/phpunit"
|
||||||
},
|
},
|
||||||
"scripts-descriptions": {
|
"scripts-descriptions": {
|
||||||
|
@ -47,12 +47,12 @@ $CONF_DIR = _dir($APP_DIR, 'config');
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Dependency Injection setup
|
// Dependency Injection setup
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
$baseConfig = require $APPCONF_DIR . '/base_config.php';
|
$baseConfig = require "{$APPCONF_DIR}/base_config.php";
|
||||||
$di = require $APP_DIR . '/bootstrap.php';
|
$di = require "{$APP_DIR}/bootstrap.php";
|
||||||
|
|
||||||
$config = loadToml($CONF_DIR);
|
$config = loadToml($CONF_DIR);
|
||||||
|
|
||||||
$overrideFile = $CONF_DIR . '/admin-override.toml';
|
$overrideFile = "{$CONF_DIR}/admin-override.toml";
|
||||||
$overrideConfig = file_exists($overrideFile)
|
$overrideConfig = file_exists($overrideFile)
|
||||||
? loadTomlFile($overrideFile)
|
? loadTomlFile($overrideFile)
|
||||||
: [];
|
: [];
|
||||||
|
2
public/css/app.min.css
vendored
2
public/css/app.min.css
vendored
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
|||||||
undefined
|
|
135
public/css/dark-override.css
Normal file
135
public/css/dark-override.css
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
a {
|
||||||
|
color: rgb(25, 120, 226);
|
||||||
|
text-shadow: var(--link-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #9e34fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
body,
|
||||||
|
legend,
|
||||||
|
nav ul li a {
|
||||||
|
background: #333;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a:hover, nav li.selected a {
|
||||||
|
border-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
header button {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
td, th {
|
||||||
|
border-color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead td,
|
||||||
|
thead th {
|
||||||
|
background: #333;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody > tr:nth-child(2n) {
|
||||||
|
background: #555;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
tbody > tr:nth-child(2n+1) {
|
||||||
|
background: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer, legend, hr {
|
||||||
|
border-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, select, textarea {
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message, .static-message {
|
||||||
|
text-shadow: var(--white-link-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.success, .static-message.success {
|
||||||
|
background: #1f8454;
|
||||||
|
border-color: #70dda9;
|
||||||
|
}
|
||||||
|
.message.error, .static-message.error {
|
||||||
|
border-color:#f3e6e6;
|
||||||
|
background: #924949;
|
||||||
|
}
|
||||||
|
.message.info, .static-message.info {
|
||||||
|
border-color: #FFFFCC;
|
||||||
|
background: #bfbe3a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invisible tr,
|
||||||
|
.invisible td,
|
||||||
|
.invisible th,
|
||||||
|
.invisible tbody > tr:nth-child(2n),
|
||||||
|
.invisible tbody > tr:nth-child(2n+1) {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-nav {
|
||||||
|
border-bottom: .1rem solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs,
|
||||||
|
.vertical-tabs{
|
||||||
|
background: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs > label,
|
||||||
|
.vertical-tabs .tab label {
|
||||||
|
background: #222;
|
||||||
|
border: 0;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-tabs .tab label {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs > label:hover,
|
||||||
|
.vertical-tabs .tab > label:hover {
|
||||||
|
background: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs > label:active,
|
||||||
|
.vertical-tabs .tab > label:active {
|
||||||
|
background: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs > [type="radio"]:checked + label,
|
||||||
|
.tabs > [type="radio"]:checked + label + .content,
|
||||||
|
.vertical-tabs [type="radio"]:checked + label,
|
||||||
|
.vertical-tabs [type="radio"]:checked ~ .content {
|
||||||
|
/* border-color: #333; */
|
||||||
|
border: 0;
|
||||||
|
background: #666;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-tabs {
|
||||||
|
background: #222;
|
||||||
|
border: 1px solid #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-tabs .tab {
|
||||||
|
background: #666;
|
||||||
|
border-bottom: 1px solid #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
1
public/css/dark.min.css
vendored
Normal file
1
public/css/dark.min.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
a{color:#1978e2;text-shadow:var(--link-shadow)}a:hover{color:#9e34fd}body,legend,nav ul li a{background:#333;color:#eee}nav a:hover,nav li.selected a{border-color:#fff}header button{background:transparent}table{-webkit-box-shadow:none;box-shadow:none}td,th{border-color:#111}thead td,thead th{background:#333;color:#eee}tbody>tr:nth-child(2n){background:#555;color:#eee}tbody>tr:nth-child(odd){background:#333}footer,hr,legend{border-color:#ddd}small{color:#fff}input,select,textarea{color:#111}.message,.static-message{text-shadow:var(--white-link-shadow)}.message.success,.static-message.success{background:#1f8454;border-color:#70dda9}.message.error,.static-message.error{border-color:#f3e6e6;background:#924949}.message.info,.static-message.info{border-color:#ffc;background:#bfbe3a}.invisible tbody>tr:nth-child(2n),.invisible tbody>tr:nth-child(odd),.invisible td,.invisible th,.invisible tr{background:transparent}#main-nav{border-bottom:.1rem solid #ddd}.tabs,.vertical-tabs{background:#333}.tabs>label,.vertical-tabs .tab label{background:#222;border:0;color:#eee}.vertical-tabs .tab label{width:100%}.tabs>label:hover,.vertical-tabs .tab>label:hover{background:#888}.tabs>label:active,.vertical-tabs .tab>label:active{background:#999}.tabs>[type=radio]:checked+label,.tabs>[type=radio]:checked+label+.content,.vertical-tabs [type=radio]:checked+label,.vertical-tabs [type=radio]:checked~.content{border:0;background:#666;color:#eee}.vertical-tabs{background:#222;border:1px solid #444}.vertical-tabs .tab{background:#666;border-bottom:1px solid #444}
|
@ -359,7 +359,7 @@ td .media-wrap-flex {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 220px;
|
width: 220px;
|
||||||
height: 311px;
|
height: 312px;
|
||||||
margin: var(--normal-padding);
|
margin: var(--normal-padding);
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
background: rgba(0, 0, 0, 0.15);
|
background: rgba(0, 0, 0, 0.15);
|
||||||
@ -423,7 +423,7 @@ picture.cover {
|
|||||||
background: var(--title-overlay); */
|
background: var(--title-overlay); */
|
||||||
content: '';
|
content: '';
|
||||||
display: block;
|
display: block;
|
||||||
height: 311px;
|
height: 312px;
|
||||||
left: 0;
|
left: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -39,23 +39,26 @@
|
|||||||
table .align-right,
|
table .align-right,
|
||||||
table.align-center {
|
table.align-center {
|
||||||
border: 0;
|
border: 0;
|
||||||
display: block;
|
/* display: block; */
|
||||||
margin: 0 auto;
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
table tbody {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
table td {
|
table td {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table tbody,
|
||||||
|
table.media-details {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
table.media-details td {
|
table.media-details td {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: left !important;
|
text-align: left !important;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
table thead {
|
table thead {
|
||||||
|
27
public/js/scripts-authed.min.js
vendored
27
public/js/scripts-authed.min.js
vendored
@ -7,17 +7,18 @@ sel.addEventListener(event,listener,false)}function delegateEvent(sel,target,eve
|
|||||||
"GET")url+=url.match(/\?/)?ajaxSerialize(config.data):"?"+ajaxSerialize(config.data);request.open(method,url);request.onreadystatechange=function(){if(request.readyState===4){var responseText="";if(request.responseType==="json")responseText=JSON.parse(request.responseText);else responseText=request.responseText;if(request.status>299)config.error.call(null,request.status,responseText,request.response);else config.success.call(null,responseText,request.status)}};if(config.dataType==="json"){config.data=
|
"GET")url+=url.match(/\?/)?ajaxSerialize(config.data):"?"+ajaxSerialize(config.data);request.open(method,url);request.onreadystatechange=function(){if(request.readyState===4){var responseText="";if(request.responseType==="json")responseText=JSON.parse(request.responseText);else responseText=request.responseText;if(request.status>299)config.error.call(null,request.status,responseText,request.response);else config.success.call(null,responseText,request.status)}};if(config.dataType==="json"){config.data=
|
||||||
JSON.stringify(config.data);config.mimeType="application/json"}else config.data=ajaxSerialize(config.data);request.setRequestHeader("Content-Type",config.mimeType);switch(method){case "GET":request.send(null);break;default:request.send(config.data);break}};AnimeClient.get=function(url,data,callback){callback=callback===undefined?null:callback;if(callback===null){callback=data;data={}}return AnimeClient.ajax(url,{data:data,success:callback})};AnimeClient.on("header","click",".message",function(e){AnimeClient.hide(e.target)});
|
JSON.stringify(config.data);config.mimeType="application/json"}else config.data=ajaxSerialize(config.data);request.setRequestHeader("Content-Type",config.mimeType);switch(method){case "GET":request.send(null);break;default:request.send(config.data);break}};AnimeClient.get=function(url,data,callback){callback=callback===undefined?null:callback;if(callback===null){callback=data;data={}}return AnimeClient.ajax(url,{data:data,success:callback})};AnimeClient.on("header","click",".message",function(e){AnimeClient.hide(e.target)});
|
||||||
AnimeClient.on("form.js-delete","submit",function(event){var proceed=confirm("Are you ABSOLUTELY SURE you want to delete this item?");if(proceed===false){event.preventDefault();event.stopPropagation()}});AnimeClient.on(".js-clear-cache","click",function(){AnimeClient.get("/cache_purge",function(){AnimeClient.showMessage("success","Successfully purged api cache")})});AnimeClient.on(".vertical-tabs input","change",function(event){var el=event.currentTarget.parentElement;var rect=el.getBoundingClientRect();
|
AnimeClient.on("form.js-delete","submit",function(event){var proceed=confirm("Are you ABSOLUTELY SURE you want to delete this item?");if(proceed===false){event.preventDefault();event.stopPropagation()}});AnimeClient.on(".js-clear-cache","click",function(){AnimeClient.get("/cache_purge",function(){AnimeClient.showMessage("success","Successfully purged api cache")})});AnimeClient.on(".vertical-tabs input","change",function(event){var el=event.currentTarget.parentElement;var rect=el.getBoundingClientRect();
|
||||||
var top=rect.top+window.pageYOffset;window.scrollTo({top:top,behavior:"smooth"})});AnimeClient.on("main","change",".big-check",function(e){var id=e.target.id;document.getElementById("mal_"+id).checked=true});function renderAnimeSearchResults(data){var results=[];data.forEach(function(x){var item=x.attributes;var titles=item.titles.reduce(function(prev,current){return prev+(current+"<br />")},[]);results.push('\n\t\t\t<article class="media search">\n\t\t\t\t<div class="name">\n\t\t\t\t\t<input type="radio" class="mal-check" id="mal_'+
|
var top=rect.top+window.pageYOffset;window.scrollTo({top:top,behavior:"smooth"})});if("serviceWorker"in navigator)navigator.serviceWorker.register("/sw.js").then(function(reg){console.log("Service worker registered",reg.scope)})["catch"](function(error){console.error("Failed to register service worker",error)});AnimeClient.on("main","change",".big-check",function(e){var id=e.target.id;document.getElementById("mal_"+id).checked=true});function renderAnimeSearchResults(data){var results=[];data.forEach(function(x){var item=
|
||||||
item.slug+'" name="mal_id" value="'+x.mal_id+'" />\n\t\t\t\t\t<input type="radio" class="big-check" id="'+item.slug+'" name="id" value="'+x.id+'" />\n\t\t\t\t\t<label for="'+item.slug+'">\n\t\t\t\t\t\t<picture width="220">\n\t\t\t\t\t\t\t<source srcset="/public/images/anime/'+x.id+'.webp" type="image/webp" />\n\t\t\t\t\t\t\t<source srcset="/public/images/anime/'+x.id+'.jpg" type="image/jpeg" />\n\t\t\t\t\t\t\t<img src="/public/images/anime/'+x.id+'.jpg" alt="" width="220" />\n\t\t\t\t\t\t</picture>\n\t\t\t\t\t\t\n\t\t\t\t\t\t<span class="name">\n\t\t\t\t\t\t\t'+
|
x.attributes;var titles=item.titles.reduce(function(prev,current){return prev+(current+"<br />")},[]);results.push('\n\t\t\t<article class="media search">\n\t\t\t\t<div class="name">\n\t\t\t\t\t<input type="radio" class="mal-check" id="mal_'+item.slug+'" name="mal_id" value="'+x.mal_id+'" />\n\t\t\t\t\t<input type="radio" class="big-check" id="'+item.slug+'" name="id" value="'+x.id+'" />\n\t\t\t\t\t<label for="'+item.slug+'">\n\t\t\t\t\t\t<picture width="220">\n\t\t\t\t\t\t\t<source srcset="/public/images/anime/'+
|
||||||
item.canonicalTitle+"<br />\n\t\t\t\t\t\t\t<small>"+titles+'</small>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div class="table">\n\t\t\t\t\t<div class="row">\n\t\t\t\t\t\t<span class="edit">\n\t\t\t\t\t\t\t<a class="bracketed" href="/anime/details/'+item.slug+'">Info Page</a>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</article>\n\t\t')});return results.join("")}function renderMangaSearchResults(data){var results=[];data.forEach(function(x){var item=x.attributes;
|
x.id+'.webp" type="image/webp" />\n\t\t\t\t\t\t\t<source srcset="/public/images/anime/'+x.id+'.jpg" type="image/jpeg" />\n\t\t\t\t\t\t\t<img src="/public/images/anime/'+x.id+'.jpg" alt="" width="220" />\n\t\t\t\t\t\t</picture>\n\t\t\t\t\t\t\n\t\t\t\t\t\t<span class="name">\n\t\t\t\t\t\t\t'+item.canonicalTitle+"<br />\n\t\t\t\t\t\t\t<small>"+titles+'</small>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div class="table">\n\t\t\t\t\t<div class="row">\n\t\t\t\t\t\t<span class="edit">\n\t\t\t\t\t\t\t<a class="bracketed" href="/anime/details/'+
|
||||||
var titles=item.titles.reduce(function(prev,current){return prev+(current+"<br />")},[]);results.push('\n\t\t\t<article class="media search">\n\t\t\t\t<div class="name">\n\t\t\t\t\t<input type="radio" id="mal_'+item.slug+'" name="mal_id" value="'+x.mal_id+'" />\n\t\t\t\t\t<input type="radio" class="big-check" id="'+item.slug+'" name="id" value="'+x.id+'" />\n\t\t\t\t\t<label for="'+item.slug+'">\n\t\t\t\t\t\t<picture width="220">\n\t\t\t\t\t\t\t<source srcset="/public/images/manga/'+x.id+'.webp" type="image/webp" />\n\t\t\t\t\t\t\t<source srcset="/public/images/manga/'+
|
item.slug+'">Info Page</a>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</article>\n\t\t')});return results.join("")}function renderMangaSearchResults(data){var results=[];data.forEach(function(x){var item=x.attributes;var titles=item.titles.reduce(function(prev,current){return prev+(current+"<br />")},[]);results.push('\n\t\t\t<article class="media search">\n\t\t\t\t<div class="name">\n\t\t\t\t\t<input type="radio" id="mal_'+item.slug+'" name="mal_id" value="'+x.mal_id+'" />\n\t\t\t\t\t<input type="radio" class="big-check" id="'+
|
||||||
x.id+'.jpg" type="image/jpeg" />\n\t\t\t\t\t\t\t<img src="/public/images/manga/'+x.id+'.jpg" alt="" width="220" />\n\t\t\t\t\t\t</picture>\n\t\t\t\t\t\t<span class="name">\n\t\t\t\t\t\t\t'+item.canonicalTitle+"<br />\n\t\t\t\t\t\t\t<small>"+titles+'</small>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div class="table">\n\t\t\t\t\t<div class="row">\n\t\t\t\t\t\t<span class="edit">\n\t\t\t\t\t\t\t<a class="bracketed" href="/manga/details/'+item.slug+'">Info Page</a>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</article>\n\t\t')});
|
item.slug+'" name="id" value="'+x.id+'" />\n\t\t\t\t\t<label for="'+item.slug+'">\n\t\t\t\t\t\t<picture width="220">\n\t\t\t\t\t\t\t<source srcset="/public/images/manga/'+x.id+'.webp" type="image/webp" />\n\t\t\t\t\t\t\t<source srcset="/public/images/manga/'+x.id+'.jpg" type="image/jpeg" />\n\t\t\t\t\t\t\t<img src="/public/images/manga/'+x.id+'.jpg" alt="" width="220" />\n\t\t\t\t\t\t</picture>\n\t\t\t\t\t\t<span class="name">\n\t\t\t\t\t\t\t'+item.canonicalTitle+"<br />\n\t\t\t\t\t\t\t<small>"+titles+
|
||||||
return results.join("")}var search=function(query){AnimeClient.$(".cssload-loader")[0].removeAttribute("hidden");AnimeClient.get(AnimeClient.url("/anime-collection/search"),{query:query},function(searchResults,status){searchResults=JSON.parse(searchResults);AnimeClient.$(".cssload-loader")[0].setAttribute("hidden","hidden");AnimeClient.$("#series-list")[0].innerHTML=renderAnimeSearchResults(searchResults.data)})};if(AnimeClient.hasElement(".anime #search"))AnimeClient.on("#search","keyup",AnimeClient.throttle(250,
|
'</small>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div class="table">\n\t\t\t\t\t<div class="row">\n\t\t\t\t\t\t<span class="edit">\n\t\t\t\t\t\t\t<a class="bracketed" href="/manga/details/'+item.slug+'">Info Page</a>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</article>\n\t\t')});return results.join("")}var search=function(query){AnimeClient.$(".cssload-loader")[0].removeAttribute("hidden");AnimeClient.get(AnimeClient.url("/anime-collection/search"),{query:query},
|
||||||
function(e){var query=encodeURIComponent(e.target.value);if(query==="")return;search(query)}));AnimeClient.on("body.anime.list","click",".plus-one",function(e){var parentSel=AnimeClient.closestParent(e.target,"article");var watchedCount=parseInt(AnimeClient.$(".completed_number",parentSel)[0].textContent,10)||0;var totalCount=parseInt(AnimeClient.$(".total_number",parentSel)[0].textContent,10);var title=AnimeClient.$(".name a",parentSel)[0].textContent;var data={id:parentSel.dataset.kitsuId,mal_id:parentSel.dataset.malId,
|
function(searchResults,status){searchResults=JSON.parse(searchResults);AnimeClient.$(".cssload-loader")[0].setAttribute("hidden","hidden");AnimeClient.$("#series-list")[0].innerHTML=renderAnimeSearchResults(searchResults.data)})};if(AnimeClient.hasElement(".anime #search"))AnimeClient.on("#search","keyup",AnimeClient.throttle(250,function(e){var query=encodeURIComponent(e.target.value);if(query==="")return;search(query)}));AnimeClient.on("body.anime.list","click",".plus-one",function(e){var parentSel=
|
||||||
data:{progress:watchedCount+1}};if(isNaN(watchedCount)||watchedCount===0)data.data.status="current";if(!isNaN(watchedCount)&&watchedCount+1===totalCount)data.data.status="completed";AnimeClient.show(AnimeClient.$("#loading-shadow")[0]);AnimeClient.ajax(AnimeClient.url("/anime/increment"),{data:data,dataType:"json",type:"POST",success:function(res){var resData=JSON.parse(res);if(resData.errors){AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.showMessage("error","Failed to update "+
|
AnimeClient.closestParent(e.target,"article");var watchedCount=parseInt(AnimeClient.$(".completed_number",parentSel)[0].textContent,10)||0;var totalCount=parseInt(AnimeClient.$(".total_number",parentSel)[0].textContent,10);var title=AnimeClient.$(".name a",parentSel)[0].textContent;var data={id:parentSel.dataset.kitsuId,mal_id:parentSel.dataset.malId,data:{progress:watchedCount+1}};if(isNaN(watchedCount)||watchedCount===0)data.data.status="current";if(!isNaN(watchedCount)&&watchedCount+1===totalCount)data.data.status=
|
||||||
title+". ");AnimeClient.scrollToTop();return}if(resData.data.attributes.status==="completed")AnimeClient.hide(parentSel);AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.showMessage("success","Successfully updated "+title);AnimeClient.$(".completed_number",parentSel)[0].textContent=++watchedCount;AnimeClient.scrollToTop()},error:function(){AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.showMessage("error","Failed to update "+title+". ");AnimeClient.scrollToTop()}})});
|
"completed";AnimeClient.show(AnimeClient.$("#loading-shadow")[0]);AnimeClient.ajax(AnimeClient.url("/anime/increment"),{data:data,dataType:"json",type:"POST",success:function(res){var resData=JSON.parse(res);if(resData.errors){AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.showMessage("error","Failed to update "+title+". ");AnimeClient.scrollToTop();return}if(resData.data.attributes.status==="completed")AnimeClient.hide(parentSel);AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);
|
||||||
var search$1=function(query){AnimeClient.$(".cssload-loader")[0].removeAttribute("hidden");AnimeClient.get(AnimeClient.url("/manga/search"),{query:query},function(searchResults,status){searchResults=JSON.parse(searchResults);AnimeClient.$(".cssload-loader")[0].setAttribute("hidden","hidden");AnimeClient.$("#series-list")[0].innerHTML=renderMangaSearchResults(searchResults.data)})};if(AnimeClient.hasElement(".manga #search"))AnimeClient.on("#search","keyup",AnimeClient.throttle(250,function(e){var query=
|
AnimeClient.showMessage("success","Successfully updated "+title);AnimeClient.$(".completed_number",parentSel)[0].textContent=++watchedCount;AnimeClient.scrollToTop()},error:function(){AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.showMessage("error","Failed to update "+title+". ");AnimeClient.scrollToTop()}})});var search$1=function(query){AnimeClient.$(".cssload-loader")[0].removeAttribute("hidden");AnimeClient.get(AnimeClient.url("/manga/search"),{query:query},function(searchResults,
|
||||||
encodeURIComponent(e.target.value);if(query==="")return;search$1(query)}));AnimeClient.on(".manga.list","click",".edit-buttons button",function(e){var thisSel=e.target;var parentSel=AnimeClient.closestParent(e.target,"article");var type=thisSel.classList.contains("plus-one-chapter")?"chapter":"volume";var completed=parseInt(AnimeClient.$("."+type+"s_read",parentSel)[0].textContent,10)||0;var total=parseInt(AnimeClient.$("."+type+"_count",parentSel)[0].textContent,10);var mangaName=AnimeClient.$(".name",
|
status){searchResults=JSON.parse(searchResults);AnimeClient.$(".cssload-loader")[0].setAttribute("hidden","hidden");AnimeClient.$("#series-list")[0].innerHTML=renderMangaSearchResults(searchResults.data)})};if(AnimeClient.hasElement(".manga #search"))AnimeClient.on("#search","keyup",AnimeClient.throttle(250,function(e){var query=encodeURIComponent(e.target.value);if(query==="")return;search$1(query)}));AnimeClient.on(".manga.list","click",".edit-buttons button",function(e){var thisSel=e.target;var parentSel=
|
||||||
parentSel)[0].textContent;if(isNaN(completed))completed=0;var data={id:parentSel.dataset.kitsuId,mal_id:parentSel.dataset.malId,data:{progress:completed}};if(isNaN(completed)||completed===0)data.data.status="current";if(!isNaN(completed)&&completed+1===total)data.data.status="completed";data.data.progress=++completed;AnimeClient.show(AnimeClient.$("#loading-shadow")[0]);AnimeClient.ajax(AnimeClient.url("/manga/increment"),{data:data,dataType:"json",type:"POST",mimeType:"application/json",success:function(){if(data.data.status===
|
AnimeClient.closestParent(e.target,"article");var type=thisSel.classList.contains("plus-one-chapter")?"chapter":"volume";var completed=parseInt(AnimeClient.$("."+type+"s_read",parentSel)[0].textContent,10)||0;var total=parseInt(AnimeClient.$("."+type+"_count",parentSel)[0].textContent,10);var mangaName=AnimeClient.$(".name",parentSel)[0].textContent;if(isNaN(completed))completed=0;var data={id:parentSel.dataset.kitsuId,mal_id:parentSel.dataset.malId,data:{progress:completed}};if(isNaN(completed)||
|
||||||
"completed")AnimeClient.hide(parentSel);AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.$("."+type+"s_read",parentSel)[0].textContent=completed;AnimeClient.showMessage("success","Successfully updated "+mangaName);AnimeClient.scrollToTop()},error:function(){AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.showMessage("error","Failed to update "+mangaName);AnimeClient.scrollToTop()}})})})();
|
completed===0)data.data.status="current";if(!isNaN(completed)&&completed+1===total)data.data.status="completed";data.data.progress=++completed;AnimeClient.show(AnimeClient.$("#loading-shadow")[0]);AnimeClient.ajax(AnimeClient.url("/manga/increment"),{data:data,dataType:"json",type:"POST",mimeType:"application/json",success:function(){if(data.data.status==="completed")AnimeClient.hide(parentSel);AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.$("."+type+"s_read",parentSel)[0].textContent=
|
||||||
|
completed;AnimeClient.showMessage("success","Successfully updated "+mangaName);AnimeClient.scrollToTop()},error:function(){AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.showMessage("error","Failed to update "+mangaName);AnimeClient.scrollToTop()}})})})();
|
||||||
//# sourceMappingURL=scripts-authed.min.js.map
|
//# sourceMappingURL=scripts-authed.min.js.map
|
||||||
|
File diff suppressed because one or more lines are too long
2
public/js/scripts.min.js
vendored
2
public/js/scripts.min.js
vendored
@ -7,5 +7,5 @@ sel.addEventListener(event,listener,false)}function delegateEvent(sel,target,eve
|
|||||||
"GET")url+=url.match(/\?/)?ajaxSerialize(config.data):"?"+ajaxSerialize(config.data);request.open(method,url);request.onreadystatechange=function(){if(request.readyState===4){var responseText="";if(request.responseType==="json")responseText=JSON.parse(request.responseText);else responseText=request.responseText;if(request.status>299)config.error.call(null,request.status,responseText,request.response);else config.success.call(null,responseText,request.status)}};if(config.dataType==="json"){config.data=
|
"GET")url+=url.match(/\?/)?ajaxSerialize(config.data):"?"+ajaxSerialize(config.data);request.open(method,url);request.onreadystatechange=function(){if(request.readyState===4){var responseText="";if(request.responseType==="json")responseText=JSON.parse(request.responseText);else responseText=request.responseText;if(request.status>299)config.error.call(null,request.status,responseText,request.response);else config.success.call(null,responseText,request.status)}};if(config.dataType==="json"){config.data=
|
||||||
JSON.stringify(config.data);config.mimeType="application/json"}else config.data=ajaxSerialize(config.data);request.setRequestHeader("Content-Type",config.mimeType);switch(method){case "GET":request.send(null);break;default:request.send(config.data);break}};AnimeClient.get=function(url,data,callback){callback=callback===undefined?null:callback;if(callback===null){callback=data;data={}}return AnimeClient.ajax(url,{data:data,success:callback})};AnimeClient.on("header","click",".message",function(e){AnimeClient.hide(e.target)});
|
JSON.stringify(config.data);config.mimeType="application/json"}else config.data=ajaxSerialize(config.data);request.setRequestHeader("Content-Type",config.mimeType);switch(method){case "GET":request.send(null);break;default:request.send(config.data);break}};AnimeClient.get=function(url,data,callback){callback=callback===undefined?null:callback;if(callback===null){callback=data;data={}}return AnimeClient.ajax(url,{data:data,success:callback})};AnimeClient.on("header","click",".message",function(e){AnimeClient.hide(e.target)});
|
||||||
AnimeClient.on("form.js-delete","submit",function(event){var proceed=confirm("Are you ABSOLUTELY SURE you want to delete this item?");if(proceed===false){event.preventDefault();event.stopPropagation()}});AnimeClient.on(".js-clear-cache","click",function(){AnimeClient.get("/cache_purge",function(){AnimeClient.showMessage("success","Successfully purged api cache")})});AnimeClient.on(".vertical-tabs input","change",function(event){var el=event.currentTarget.parentElement;var rect=el.getBoundingClientRect();
|
AnimeClient.on("form.js-delete","submit",function(event){var proceed=confirm("Are you ABSOLUTELY SURE you want to delete this item?");if(proceed===false){event.preventDefault();event.stopPropagation()}});AnimeClient.on(".js-clear-cache","click",function(){AnimeClient.get("/cache_purge",function(){AnimeClient.showMessage("success","Successfully purged api cache")})});AnimeClient.on(".vertical-tabs input","change",function(event){var el=event.currentTarget.parentElement;var rect=el.getBoundingClientRect();
|
||||||
var top=rect.top+window.pageYOffset;window.scrollTo({top:top,behavior:"smooth"})})})();
|
var top=rect.top+window.pageYOffset;window.scrollTo({top:top,behavior:"smooth"})});if("serviceWorker"in navigator)navigator.serviceWorker.register("/sw.js").then(function(reg){console.log("Service worker registered",reg.scope)})["catch"](function(error){console.error("Failed to register service worker",error)})})();
|
||||||
//# sourceMappingURL=scripts.min.js.map
|
//# sourceMappingURL=scripts.min.js.map
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,10 +1,10 @@
|
|||||||
import './base/events.js';
|
import './base/events.js';
|
||||||
|
|
||||||
/* if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
navigator.serviceWorker.register('/sw.js').then(reg => {
|
navigator.serviceWorker.register('/sw.js').then(reg => {
|
||||||
console.log('Service worker registered', reg.scope);
|
console.log('Service worker registered', reg.scope);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error('Failed to register service worker', error);
|
console.error('Failed to register service worker', error);
|
||||||
});
|
});
|
||||||
} */
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,9 @@ const cssNext = require('postcss-cssnext');
|
|||||||
const cssNano = require('cssnano');
|
const cssNano = require('cssnano');
|
||||||
|
|
||||||
const css = fs.readFileSync('css/all.css', 'utf-8');
|
const css = fs.readFileSync('css/all.css', 'utf-8');
|
||||||
|
const darkCss = fs.readFileSync('css/dark-override.css', 'utf-8');
|
||||||
|
|
||||||
|
// Basic theme
|
||||||
postcss()
|
postcss()
|
||||||
.use(atImport())
|
.use(atImport())
|
||||||
.use(cssNext())
|
.use(cssNext())
|
||||||
@ -24,6 +26,24 @@ postcss()
|
|||||||
from: 'css/all.css',
|
from: 'css/all.css',
|
||||||
to: 'css/app.min.css'
|
to: 'css/app.min.css'
|
||||||
}).then(result => {
|
}).then(result => {
|
||||||
fs.writeFileSync('css/app.min.css', result.css);
|
fs.writeFileSync('css/app.min.css', result.css);
|
||||||
fs.writeFileSync('css/app.min.css.map', result.map);
|
});
|
||||||
});
|
|
||||||
|
// Dark theme
|
||||||
|
postcss()
|
||||||
|
.use(atImport())
|
||||||
|
.use(cssNext())
|
||||||
|
.use(cssNano({
|
||||||
|
autoprefixer: false,
|
||||||
|
colormin: false,
|
||||||
|
minifyFontValues: false,
|
||||||
|
options: {
|
||||||
|
sourcemap: false
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.process(darkCss, {
|
||||||
|
from: 'css/dark-override.css',
|
||||||
|
to: 'css/dark.min.css'
|
||||||
|
}).then(result => {
|
||||||
|
fs.writeFileSync('css/dark.min.css', result.css);
|
||||||
|
});
|
@ -17,6 +17,7 @@
|
|||||||
namespace Aviat\AnimeClient\API;
|
namespace Aviat\AnimeClient\API;
|
||||||
|
|
||||||
use function Amp\Promise\wait;
|
use function Amp\Promise\wait;
|
||||||
|
use function Aviat\AnimeClient\getResponse;
|
||||||
|
|
||||||
use Amp;
|
use Amp;
|
||||||
use Amp\Artax\{FormBody, Request};
|
use Amp\Artax\{FormBody, Request};
|
||||||
@ -250,7 +251,7 @@ class APIRequestBuilder {
|
|||||||
*/
|
*/
|
||||||
public function getResponseData(Request $request)
|
public function getResponseData(Request $request)
|
||||||
{
|
{
|
||||||
$response = wait((new HummingbirdClient)->request($request));
|
$response = getResponse($request);
|
||||||
return wait($response->getBody());
|
return wait($response->getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,7 +316,7 @@ class APIRequestBuilder {
|
|||||||
* @param string $type
|
* @param string $type
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private function resetState($url, $type = 'GET')
|
private function resetState($url, $type = 'GET'): void
|
||||||
{
|
{
|
||||||
$requestUrl = $url ?: $this->baseUrl;
|
$requestUrl = $url ?: $this->baseUrl;
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ final class Anilist {
|
|||||||
MangaReadingStatus::PLAN_TO_READ => KMRS::PLAN_TO_READ,
|
MangaReadingStatus::PLAN_TO_READ => KMRS::PLAN_TO_READ,
|
||||||
];
|
];
|
||||||
|
|
||||||
public static function getIdToWatchingStatusMap()
|
public static function getIdToWatchingStatusMap(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'CURRENT' => AnimeWatchingStatus::WATCHING,
|
'CURRENT' => AnimeWatchingStatus::WATCHING,
|
||||||
@ -77,7 +77,7 @@ final class Anilist {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getIdToReadingStatusMap()
|
public static function getIdToReadingStatusMap(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'CURRENT' => MangaReadingStatus::READING,
|
'CURRENT' => MangaReadingStatus::READING,
|
||||||
|
@ -19,14 +19,12 @@ namespace Aviat\AnimeClient\API\Anilist;
|
|||||||
use const Aviat\AnimeClient\USER_AGENT;
|
use const Aviat\AnimeClient\USER_AGENT;
|
||||||
|
|
||||||
use function Amp\Promise\wait;
|
use function Amp\Promise\wait;
|
||||||
|
use function Aviat\AnimeClient\getResponse;
|
||||||
|
|
||||||
use Amp\Artax\Request;
|
use Amp\Artax\Request;
|
||||||
use Amp\Artax\Response;
|
use Amp\Artax\Response;
|
||||||
|
|
||||||
use Aviat\AnimeClient\API\{
|
use Aviat\AnimeClient\API\Anilist;
|
||||||
Anilist,
|
|
||||||
HummingbirdClient
|
|
||||||
};
|
|
||||||
use Aviat\Ion\Json;
|
use Aviat\Ion\Json;
|
||||||
use Aviat\Ion\Di\ContainerAware;
|
use Aviat\Ion\Di\ContainerAware;
|
||||||
|
|
||||||
@ -200,7 +198,7 @@ trait AnilistTrait {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$request = $this->setUpRequest($url, $options);
|
$request = $this->setUpRequest($url, $options);
|
||||||
$response = wait((new HummingbirdClient)->request($request));
|
$response = getResponse($request);
|
||||||
|
|
||||||
$logger->debug('Anilist response', [
|
$logger->debug('Anilist response', [
|
||||||
'status' => $response->getStatus(),
|
'status' => $response->getStatus(),
|
||||||
@ -221,7 +219,7 @@ trait AnilistTrait {
|
|||||||
$logger = $this->container->getLogger('anilist-request');
|
$logger = $this->container->getLogger('anilist-request');
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = wait((new HummingbirdClient)->request($request));
|
$response = getResponse($request);
|
||||||
|
|
||||||
$logger->debug('Anilist response', [
|
$logger->debug('Anilist response', [
|
||||||
'status' => $response->getStatus(),
|
'status' => $response->getStatus(),
|
||||||
|
@ -100,7 +100,7 @@ final class Model
|
|||||||
$config = $this->container->get('config');
|
$config = $this->container->get('config');
|
||||||
$anilistUser = $config->get(['anilist', 'username']);
|
$anilistUser = $config->get(['anilist', 'username']);
|
||||||
|
|
||||||
if ( ! is_string($anilistUser))
|
if ( ! \is_string($anilistUser))
|
||||||
{
|
{
|
||||||
throw new InvalidArgumentException('Anilist username is not defined in config');
|
throw new InvalidArgumentException('Anilist username is not defined in config');
|
||||||
}
|
}
|
||||||
@ -151,10 +151,9 @@ final class Model
|
|||||||
* Create a list item with all the relevant data
|
* Create a list item with all the relevant data
|
||||||
*
|
*
|
||||||
* @param array $data
|
* @param array $data
|
||||||
* @param string $type
|
|
||||||
* @return Request
|
* @return Request
|
||||||
*/
|
*/
|
||||||
public function createFullListItem(array $data, string $type = 'anime'): Request
|
public function createFullListItem(array $data): Request
|
||||||
{
|
{
|
||||||
$createData = $data['data'];
|
$createData = $data['data'];
|
||||||
$mediaId = $this->getMediaIdFromMalId($data['mal_id']);
|
$mediaId = $this->getMediaIdFromMalId($data['mal_id']);
|
||||||
@ -168,6 +167,7 @@ final class Model
|
|||||||
* Get the data for a specific list item, generally for editing
|
* Get the data for a specific list item, generally for editing
|
||||||
*
|
*
|
||||||
* @param string $malId - The unique identifier of that list item
|
* @param string $malId - The unique identifier of that list item
|
||||||
|
* @param string $type - Them media type (anime/manga)
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function getListItem(string $malId, string $type): array
|
public function getListItem(string $malId, string $type): array
|
||||||
@ -185,6 +185,7 @@ final class Model
|
|||||||
* Increase the watch count for the current list item
|
* Increase the watch count for the current list item
|
||||||
*
|
*
|
||||||
* @param FormItem $data
|
* @param FormItem $data
|
||||||
|
* @param string $type - Them media type (anime/manga)
|
||||||
* @return Request
|
* @return Request
|
||||||
*/
|
*/
|
||||||
public function incrementListItem(FormItem $data, string $type): Request
|
public function incrementListItem(FormItem $data, string $type): Request
|
||||||
@ -198,7 +199,7 @@ final class Model
|
|||||||
* Modify a list item
|
* Modify a list item
|
||||||
*
|
*
|
||||||
* @param FormItem $data
|
* @param FormItem $data
|
||||||
* @param int [$id]
|
* @param string $type - Them media type (anime/manga)
|
||||||
* @return Request
|
* @return Request
|
||||||
*/
|
*/
|
||||||
public function updateListItem(FormItem $data, string $type): Request
|
public function updateListItem(FormItem $data, string $type): Request
|
||||||
@ -225,6 +226,7 @@ final class Model
|
|||||||
* Get the id of the specific list entry from the malId
|
* Get the id of the specific list entry from the malId
|
||||||
*
|
*
|
||||||
* @param string $malId
|
* @param string $malId
|
||||||
|
* @param string $type - The media type (anime/manga)
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getListIdFromMalId(string $malId, string $type): ?string
|
public function getListIdFromMalId(string $malId, string $type): ?string
|
||||||
@ -234,7 +236,7 @@ final class Model
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Anilist media id from its MAL id
|
* Get the Anilist list item id from the media id from its MAL id
|
||||||
* this way is more accurate than getting the list item id
|
* this way is more accurate than getting the list item id
|
||||||
* directly from the MAL id
|
* directly from the MAL id
|
||||||
*/
|
*/
|
||||||
@ -248,13 +250,6 @@ final class Model
|
|||||||
'userName' => $anilistUser,
|
'userName' => $anilistUser,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/* dump([
|
|
||||||
'media_id' => $mediaId,
|
|
||||||
'userName' => $anilistUser,
|
|
||||||
'response' => $info,
|
|
||||||
]);
|
|
||||||
die(); */
|
|
||||||
|
|
||||||
return (string)$info['data']['MediaList']['id'];
|
return (string)$info['data']['MediaList']['id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,12 +267,6 @@ final class Model
|
|||||||
'type' => mb_strtoupper($type),
|
'type' => mb_strtoupper($type),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/* dump([
|
|
||||||
'mal_id' => $malId,
|
|
||||||
'response' => $info,
|
|
||||||
]);
|
|
||||||
die(); */
|
|
||||||
|
|
||||||
return (string)$info['data']['Media']['id'];
|
return (string)$info['data']['Media']['id'];
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,6 +17,7 @@
|
|||||||
namespace Aviat\AnimeClient\API\Anilist\Transformer;
|
namespace Aviat\AnimeClient\API\Anilist\Transformer;
|
||||||
|
|
||||||
use Aviat\AnimeClient\API\Enum\AnimeWatchingStatus\Anilist as AnilistStatus;
|
use Aviat\AnimeClient\API\Enum\AnimeWatchingStatus\Anilist as AnilistStatus;
|
||||||
|
use Aviat\AnimeClient\API\Enum\AnimeWatchingStatus\Kitsu as KitsuStatus;
|
||||||
use Aviat\AnimeClient\API\Mapping\AnimeWatchingStatus;
|
use Aviat\AnimeClient\API\Mapping\AnimeWatchingStatus;
|
||||||
use Aviat\AnimeClient\Types\{AnimeListItem, FormItem};
|
use Aviat\AnimeClient\Types\{AnimeListItem, FormItem};
|
||||||
|
|
||||||
@ -39,6 +40,8 @@ class AnimeListTransformer extends AbstractTransformer {
|
|||||||
*/
|
*/
|
||||||
public function untransform(array $item): FormItem
|
public function untransform(array $item): FormItem
|
||||||
{
|
{
|
||||||
|
$reconsuming = $item['status'] === AnilistStatus::REPEATING;
|
||||||
|
|
||||||
return new FormItem([
|
return new FormItem([
|
||||||
'id' => $item['id'],
|
'id' => $item['id'],
|
||||||
'mal_id' => $item['media']['idMal'],
|
'mal_id' => $item['media']['idMal'],
|
||||||
@ -46,26 +49,16 @@ class AnimeListTransformer extends AbstractTransformer {
|
|||||||
'notes' => $item['notes'] ?? '',
|
'notes' => $item['notes'] ?? '',
|
||||||
'private' => $item['private'],
|
'private' => $item['private'],
|
||||||
'progress' => $item['progress'],
|
'progress' => $item['progress'],
|
||||||
'rating' => $item['score'],
|
'rating' => $item['score'] ?? NULL,
|
||||||
'reconsumeCount' => $item['repeat'],
|
'reconsumeCount' => $item['repeat'],
|
||||||
'reconsuming' => $item['status'] === AnilistStatus::REPEATING,
|
'reconsuming' => $reconsuming,
|
||||||
'status' => AnimeWatchingStatus::ANILIST_TO_KITSU[$item['status']],
|
'status' => $reconsuming
|
||||||
|
? KitsuStatus::WATCHING
|
||||||
|
:AnimeWatchingStatus::ANILIST_TO_KITSU[$item['status']],
|
||||||
'updatedAt' => (new DateTime())
|
'updatedAt' => (new DateTime())
|
||||||
->setTimestamp($item['updatedAt'])
|
->setTimestamp($item['updatedAt'])
|
||||||
->format(DateTime::W3C)
|
->format(DateTime::W3C)
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Transform a set of structures
|
|
||||||
*
|
|
||||||
* @param array|object $collection
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function untransformCollection($collection): array
|
|
||||||
{
|
|
||||||
$list = (array)$collection;
|
|
||||||
return array_map([$this, 'untransform'], $list);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -18,6 +18,7 @@ namespace Aviat\AnimeClient\API\Anilist\Transformer;
|
|||||||
|
|
||||||
use Aviat\AnimeClient\API\Enum\MangaReadingStatus\Anilist as AnilistStatus;
|
use Aviat\AnimeClient\API\Enum\MangaReadingStatus\Anilist as AnilistStatus;
|
||||||
use Aviat\AnimeClient\API\Mapping\MangaReadingStatus;
|
use Aviat\AnimeClient\API\Mapping\MangaReadingStatus;
|
||||||
|
use Aviat\AnimeClient\Types\MangaListItem;
|
||||||
use Aviat\AnimeClient\Types\FormItem;
|
use Aviat\AnimeClient\Types\FormItem;
|
||||||
|
|
||||||
use Aviat\Ion\Transformer\AbstractTransformer;
|
use Aviat\Ion\Transformer\AbstractTransformer;
|
||||||
@ -28,7 +29,7 @@ class MangaListTransformer extends AbstractTransformer {
|
|||||||
|
|
||||||
public function transform($item)
|
public function transform($item)
|
||||||
{
|
{
|
||||||
|
return new MangaListItem([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,16 +57,4 @@ class MangaListTransformer extends AbstractTransformer {
|
|||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Transform a set of structures
|
|
||||||
*
|
|
||||||
* @param array|object $collection
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function untransformCollection($collection): array
|
|
||||||
{
|
|
||||||
$list = (array)$collection;
|
|
||||||
return array_map([$this, 'untransform'], $list);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -22,10 +22,10 @@ use Aviat\Ion\Enum;
|
|||||||
* Possible values for watching status for the current anime
|
* Possible values for watching status for the current anime
|
||||||
*/
|
*/
|
||||||
final class Anilist extends Enum {
|
final class Anilist extends Enum {
|
||||||
const WATCHING = 'CURRENT';
|
public const WATCHING = 'CURRENT';
|
||||||
const COMPLETED = 'COMPLETED';
|
public const COMPLETED = 'COMPLETED';
|
||||||
const ON_HOLD = 'PAUSED';
|
public const ON_HOLD = 'PAUSED';
|
||||||
const DROPPED = 'DROPPED';
|
public const DROPPED = 'DROPPED';
|
||||||
const PLAN_TO_WATCH = 'PLANNING';
|
public const PLAN_TO_WATCH = 'PLANNING';
|
||||||
const REPEATING = 'REPEATING';
|
public const REPEATING = 'REPEATING';
|
||||||
}
|
}
|
@ -22,9 +22,9 @@ use Aviat\Ion\Enum;
|
|||||||
* Possible values for watching status for the current anime
|
* Possible values for watching status for the current anime
|
||||||
*/
|
*/
|
||||||
final class Kitsu extends Enum {
|
final class Kitsu extends Enum {
|
||||||
const WATCHING = 'current';
|
public const WATCHING = 'current';
|
||||||
const PLAN_TO_WATCH = 'planned';
|
public const PLAN_TO_WATCH = 'planned';
|
||||||
const ON_HOLD = 'on_hold';
|
public const ON_HOLD = 'on_hold';
|
||||||
const DROPPED = 'dropped';
|
public const DROPPED = 'dropped';
|
||||||
const COMPLETED = 'completed';
|
public const COMPLETED = 'completed';
|
||||||
}
|
}
|
@ -16,16 +16,16 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient\API\Enum\AnimeWatchingStatus;
|
namespace Aviat\AnimeClient\API\Enum\AnimeWatchingStatus;
|
||||||
|
|
||||||
use Aviat\Ion\Enum as Enum;
|
use Aviat\Ion\Enum;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Possible values for current watching status of anime
|
* Possible values for current watching status of anime
|
||||||
*/
|
*/
|
||||||
final class Route extends Enum {
|
final class Route extends Enum {
|
||||||
const ALL = 'all';
|
public const ALL = 'all';
|
||||||
const WATCHING = 'watching';
|
public const WATCHING = 'watching';
|
||||||
const PLAN_TO_WATCH = 'plan_to_watch';
|
public const PLAN_TO_WATCH = 'plan_to_watch';
|
||||||
const DROPPED = 'dropped';
|
public const DROPPED = 'dropped';
|
||||||
const ON_HOLD = 'on_hold';
|
public const ON_HOLD = 'on_hold';
|
||||||
const COMPLETED = 'completed';
|
public const COMPLETED = 'completed';
|
||||||
}
|
}
|
||||||
|
@ -16,16 +16,16 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient\API\Enum\AnimeWatchingStatus;
|
namespace Aviat\AnimeClient\API\Enum\AnimeWatchingStatus;
|
||||||
|
|
||||||
use Aviat\Ion\Enum as Enum;
|
use Aviat\Ion\Enum;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Possible values for current watching status of anime
|
* Possible values for current watching status of anime
|
||||||
*/
|
*/
|
||||||
final class Title extends Enum {
|
final class Title extends Enum {
|
||||||
const ALL = 'All';
|
public const ALL = 'All';
|
||||||
const WATCHING = 'Currently Watching';
|
public const WATCHING = 'Currently Watching';
|
||||||
const PLAN_TO_WATCH = 'Plan to Watch';
|
public const PLAN_TO_WATCH = 'Plan to Watch';
|
||||||
const DROPPED = 'Dropped';
|
public const DROPPED = 'Dropped';
|
||||||
const ON_HOLD = 'On Hold';
|
public const ON_HOLD = 'On Hold';
|
||||||
const COMPLETED = 'Completed';
|
public const COMPLETED = 'Completed';
|
||||||
}
|
}
|
||||||
|
@ -22,10 +22,10 @@ use Aviat\Ion\Enum;
|
|||||||
* Possible values for watching status for the current anime
|
* Possible values for watching status for the current anime
|
||||||
*/
|
*/
|
||||||
final class Anilist extends Enum {
|
final class Anilist extends Enum {
|
||||||
const READING = 'CURRENT';
|
public const READING = 'CURRENT';
|
||||||
const COMPLETED = 'COMPLETED';
|
public const COMPLETED = 'COMPLETED';
|
||||||
const ON_HOLD = 'PAUSED';
|
public const ON_HOLD = 'PAUSED';
|
||||||
const DROPPED = 'DROPPED';
|
public const DROPPED = 'DROPPED';
|
||||||
const PLAN_TO_READ = 'PLANNING';
|
public const PLAN_TO_READ = 'PLANNING';
|
||||||
const REPEATING = 'REPEATING';
|
public const REPEATING = 'REPEATING';
|
||||||
}
|
}
|
@ -22,9 +22,9 @@ use Aviat\Ion\Enum;
|
|||||||
* Possible values for current reading status of manga
|
* Possible values for current reading status of manga
|
||||||
*/
|
*/
|
||||||
final class Kitsu extends Enum {
|
final class Kitsu extends Enum {
|
||||||
const READING = 'current';
|
public const READING = 'current';
|
||||||
const PLAN_TO_READ = 'planned';
|
public const PLAN_TO_READ = 'planned';
|
||||||
const DROPPED = 'dropped';
|
public const DROPPED = 'dropped';
|
||||||
const ON_HOLD = 'on_hold';
|
public const ON_HOLD = 'on_hold';
|
||||||
const COMPLETED = 'completed';
|
public const COMPLETED = 'completed';
|
||||||
}
|
}
|
@ -22,10 +22,10 @@ use Aviat\Ion\Enum;
|
|||||||
* Possible values for current reading status of manga
|
* Possible values for current reading status of manga
|
||||||
*/
|
*/
|
||||||
final class Route extends Enum {
|
final class Route extends Enum {
|
||||||
const ALL = 'all';
|
public const ALL = 'all';
|
||||||
const READING = 'reading';
|
public const READING = 'reading';
|
||||||
const PLAN_TO_READ = 'plan_to_read';
|
public const PLAN_TO_READ = 'plan_to_read';
|
||||||
const DROPPED = 'dropped';
|
public const DROPPED = 'dropped';
|
||||||
const ON_HOLD = 'on_hold';
|
public const ON_HOLD = 'on_hold';
|
||||||
const COMPLETED = 'completed';
|
public const COMPLETED = 'completed';
|
||||||
}
|
}
|
||||||
|
@ -22,10 +22,10 @@ use Aviat\Ion\Enum;
|
|||||||
* Possible values for current reading status of manga
|
* Possible values for current reading status of manga
|
||||||
*/
|
*/
|
||||||
final class Title extends Enum {
|
final class Title extends Enum {
|
||||||
const ALL = 'All';
|
public const ALL = 'All';
|
||||||
const READING = 'Currently Reading';
|
public const READING = 'Currently Reading';
|
||||||
const PLAN_TO_READ = 'Plan to Read';
|
public const PLAN_TO_READ = 'Plan to Read';
|
||||||
const DROPPED = 'Dropped';
|
public const DROPPED = 'Dropped';
|
||||||
const ON_HOLD = 'On Hold';
|
public const ON_HOLD = 'On Hold';
|
||||||
const COMPLETED = 'Completed';
|
public const COMPLETED = 'Completed';
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -21,9 +21,7 @@ namespace Aviat\AnimeClient\API;
|
|||||||
*/
|
*/
|
||||||
final class JsonAPI {
|
final class JsonAPI {
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* The full data array
|
|
||||||
*
|
|
||||||
* Basic structure is generally like so:
|
* Basic structure is generally like so:
|
||||||
* [
|
* [
|
||||||
* 'id' => '12016665',
|
* 'id' => '12016665',
|
||||||
@ -35,10 +33,7 @@ final class JsonAPI {
|
|||||||
*
|
*
|
||||||
* ]
|
* ]
|
||||||
* ]
|
* ]
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
*/
|
||||||
protected $data = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inline all included data
|
* Inline all included data
|
||||||
@ -214,8 +209,7 @@ final class JsonAPI {
|
|||||||
$dataType = $props['data']['type'];
|
$dataType = $props['data']['type'];
|
||||||
|
|
||||||
$relationship =& $organized[$type][$id]['relationships'][$relType];
|
$relationship =& $organized[$type][$id]['relationships'][$relType];
|
||||||
unset($relationship['links']);
|
unset($relationship['links'], $relationship['data']);
|
||||||
unset($relationship['data']);
|
|
||||||
|
|
||||||
if ($relType === $dataType)
|
if ($relType === $dataType)
|
||||||
{
|
{
|
||||||
|
@ -23,11 +23,11 @@ use DateTimeImmutable;
|
|||||||
* Data massaging helpers for the Kitsu API
|
* Data massaging helpers for the Kitsu API
|
||||||
*/
|
*/
|
||||||
final class Kitsu {
|
final class Kitsu {
|
||||||
const AUTH_URL = 'https://kitsu.io/api/oauth/token';
|
public const AUTH_URL = 'https://kitsu.io/api/oauth/token';
|
||||||
const AUTH_USER_ID_KEY = 'kitsu-auth-userid';
|
public const AUTH_USER_ID_KEY = 'kitsu-auth-userid';
|
||||||
const AUTH_TOKEN_CACHE_KEY = 'kitsu-auth-token';
|
public const AUTH_TOKEN_CACHE_KEY = 'kitsu-auth-token';
|
||||||
const AUTH_TOKEN_EXP_CACHE_KEY = 'kitsu-auth-token-expires';
|
public const AUTH_TOKEN_EXP_CACHE_KEY = 'kitsu-auth-token-expires';
|
||||||
const AUTH_TOKEN_REFRESH_CACHE_KEY = 'kitsu-auth-token-refresh';
|
public const AUTH_TOKEN_REFRESH_CACHE_KEY = 'kitsu-auth-token-refresh';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether an anime is airing, finished airing, or has not yet aired
|
* Determine whether an anime is airing, finished airing, or has not yet aired
|
||||||
@ -163,7 +163,7 @@ final class Kitsu {
|
|||||||
'dubs' => $streamingLink['dubs']
|
'dubs' => $streamingLink['dubs']
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
usort($links, function ($a, $b) {
|
usort($links, function ($a, $b) {
|
||||||
return $a['meta']['name'] <=> $b['meta']['name'];
|
return $a['meta']['name'] <=> $b['meta']['name'];
|
||||||
});
|
});
|
||||||
|
@ -22,8 +22,8 @@ use Aviat\Ion\Enum as BaseEnum;
|
|||||||
* Status of when anime is being/was/will be aired
|
* Status of when anime is being/was/will be aired
|
||||||
*/
|
*/
|
||||||
final class AnimeAiringStatus extends BaseEnum {
|
final class AnimeAiringStatus extends BaseEnum {
|
||||||
const NOT_YET_AIRED = 'Not Yet Aired';
|
public const NOT_YET_AIRED = 'Not Yet Aired';
|
||||||
const AIRING = 'Currently Airing';
|
public const AIRING = 'Currently Airing';
|
||||||
const FINISHED_AIRING = 'Finished Airing';
|
public const FINISHED_AIRING = 'Finished Airing';
|
||||||
}
|
}
|
||||||
// End of AnimeAiringStatus.php
|
// End of AnimeAiringStatus.php
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient\API\Kitsu;
|
namespace Aviat\AnimeClient\API\Kitsu;
|
||||||
|
|
||||||
|
use const Aviat\AnimeClient\USER_AGENT;
|
||||||
use Aviat\AnimeClient\API\APIRequestBuilder;
|
use Aviat\AnimeClient\API\APIRequestBuilder;
|
||||||
|
|
||||||
final class KitsuRequestBuilder extends APIRequestBuilder {
|
final class KitsuRequestBuilder extends APIRequestBuilder {
|
||||||
@ -32,7 +33,7 @@ final class KitsuRequestBuilder extends APIRequestBuilder {
|
|||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $defaultHeaders = [
|
protected $defaultHeaders = [
|
||||||
'User-Agent' => "Tim's Anime Client/4.0",
|
'User-Agent' => USER_AGENT,
|
||||||
'Accept' => 'application/vnd.api+json',
|
'Accept' => 'application/vnd.api+json',
|
||||||
'Content-Type' => 'application/vnd.api+json',
|
'Content-Type' => 'application/vnd.api+json',
|
||||||
'CLIENT_ID' => 'dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd',
|
'CLIENT_ID' => 'dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd',
|
||||||
|
@ -19,12 +19,12 @@ namespace Aviat\AnimeClient\API\Kitsu;
|
|||||||
use const Aviat\AnimeClient\SESSION_SEGMENT;
|
use const Aviat\AnimeClient\SESSION_SEGMENT;
|
||||||
|
|
||||||
use function Amp\Promise\wait;
|
use function Amp\Promise\wait;
|
||||||
|
use function Aviat\AnimeClient\getResponse;
|
||||||
|
|
||||||
use Amp\Artax\Request;
|
use Amp\Artax\Request;
|
||||||
use Aviat\AnimeClient\AnimeClient;
|
use Amp\Artax\Response;
|
||||||
use Aviat\AnimeClient\API\{
|
use Aviat\AnimeClient\API\{
|
||||||
FailedResponseException,
|
FailedResponseException,
|
||||||
HummingbirdClient,
|
|
||||||
Kitsu as K
|
Kitsu as K
|
||||||
};
|
};
|
||||||
use Aviat\Ion\Json;
|
use Aviat\Ion\Json;
|
||||||
@ -121,7 +121,7 @@ trait KitsuTrait {
|
|||||||
* @param array $options
|
* @param array $options
|
||||||
* @return Response
|
* @return Response
|
||||||
*/
|
*/
|
||||||
private function getResponse(string $type, string $url, array $options = [])
|
private function getResponse(string $type, string $url, array $options = []): Response
|
||||||
{
|
{
|
||||||
$logger = NULL;
|
$logger = NULL;
|
||||||
if ($this->getContainer())
|
if ($this->getContainer())
|
||||||
@ -131,7 +131,7 @@ trait KitsuTrait {
|
|||||||
|
|
||||||
$request = $this->setUpRequest($type, $url, $options);
|
$request = $this->setUpRequest($type, $url, $options);
|
||||||
|
|
||||||
$response = wait((new HummingbirdClient)->request($request));
|
$response = getResponse($request);
|
||||||
|
|
||||||
if ($logger)
|
if ($logger)
|
||||||
{
|
{
|
||||||
|
@ -19,12 +19,10 @@ namespace Aviat\AnimeClient\API\Kitsu;
|
|||||||
use const Aviat\AnimeClient\SESSION_SEGMENT;
|
use const Aviat\AnimeClient\SESSION_SEGMENT;
|
||||||
|
|
||||||
use function Amp\Promise\wait;
|
use function Amp\Promise\wait;
|
||||||
|
use function Aviat\AnimeClient\getResponse;
|
||||||
|
|
||||||
use Amp\Artax\Request;
|
use Amp\Artax\Request;
|
||||||
use Aviat\AnimeClient\API\{
|
use Aviat\AnimeClient\API\ListItemInterface;
|
||||||
HummingbirdClient,
|
|
||||||
ListItemInterface
|
|
||||||
};
|
|
||||||
use Aviat\AnimeClient\Types\FormItemData;
|
use Aviat\AnimeClient\Types\FormItemData;
|
||||||
use Aviat\Ion\Di\ContainerAware;
|
use Aviat\Ion\Di\ContainerAware;
|
||||||
use Aviat\Ion\Json;
|
use Aviat\Ion\Json;
|
||||||
@ -37,7 +35,7 @@ final class ListItem implements ListItemInterface {
|
|||||||
use KitsuTrait;
|
use KitsuTrait;
|
||||||
|
|
||||||
public function create(array $data): Request
|
public function create(array $data): Request
|
||||||
{
|
{
|
||||||
$body = [
|
$body = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'type' => 'libraryEntries',
|
'type' => 'libraryEntries',
|
||||||
@ -61,7 +59,7 @@ final class ListItem implements ListItemInterface {
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
if (array_key_exists('notes', $data))
|
if (array_key_exists('notes', $data))
|
||||||
{
|
{
|
||||||
$body['data']['attributes']['notes'] = $data['notes'];
|
$body['data']['attributes']['notes'] = $data['notes'];
|
||||||
@ -78,8 +76,6 @@ final class ListItem implements ListItemInterface {
|
|||||||
|
|
||||||
return $request->setJsonBody($body)
|
return $request->setJsonBody($body)
|
||||||
->getFullRequest();
|
->getFullRequest();
|
||||||
|
|
||||||
// return ($response->getStatus() === 201);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete(string $id): Request
|
public function delete(string $id): Request
|
||||||
@ -93,8 +89,6 @@ final class ListItem implements ListItemInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return $request->getFullRequest();
|
return $request->getFullRequest();
|
||||||
|
|
||||||
// return ($response->getStatus() === 204);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get(string $id): array
|
public function get(string $id): array
|
||||||
@ -112,8 +106,7 @@ final class ListItem implements ListItemInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$request = $request->getFullRequest();
|
$request = $request->getFullRequest();
|
||||||
|
$response = getResponse($request);
|
||||||
$response = wait((new HummingbirdClient)->request($request));
|
|
||||||
return Json::decode(wait($response->getBody()));
|
return Json::decode(wait($response->getBody()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,9 +91,10 @@ final class Model {
|
|||||||
{
|
{
|
||||||
$this->animeTransformer = new AnimeTransformer();
|
$this->animeTransformer = new AnimeTransformer();
|
||||||
$this->animeListTransformer = new AnimeListTransformer();
|
$this->animeListTransformer = new AnimeListTransformer();
|
||||||
$this->listItem = $listItem;
|
|
||||||
$this->mangaTransformer = new MangaTransformer();
|
$this->mangaTransformer = new MangaTransformer();
|
||||||
$this->mangaListTransformer = new MangaListTransformer();
|
$this->mangaListTransformer = new MangaListTransformer();
|
||||||
|
|
||||||
|
$this->listItem = $listItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -265,7 +266,7 @@ final class Model {
|
|||||||
public function getUserData(string $username): array
|
public function getUserData(string $username): array
|
||||||
{
|
{
|
||||||
// $userId = $this->getUserIdByUsername($username);
|
// $userId = $this->getUserIdByUsername($username);
|
||||||
$data = $this->getRequest("users", [
|
$data = $this->getRequest('users', [
|
||||||
'query' => [
|
'query' => [
|
||||||
'filter' => [
|
'filter' => [
|
||||||
'name' => $username,
|
'name' => $username,
|
||||||
@ -334,7 +335,7 @@ final class Model {
|
|||||||
* @param string $type "anime" or "manga"
|
* @param string $type "anime" or "manga"
|
||||||
* @return string|NULL
|
* @return string|NULL
|
||||||
*/
|
*/
|
||||||
public function getKitsuIdFromMALId(string $malId, string $type="anime")
|
public function getKitsuIdFromMALId(string $malId, string $type='anime'): ?string
|
||||||
{
|
{
|
||||||
$options = [
|
$options = [
|
||||||
'query' => [
|
'query' => [
|
||||||
@ -369,7 +370,7 @@ final class Model {
|
|||||||
* @param string $slug
|
* @param string $slug
|
||||||
* @return Anime
|
* @return Anime
|
||||||
*/
|
*/
|
||||||
public function getAnime(string $slug)
|
public function getAnime(string $slug): Anime
|
||||||
{
|
{
|
||||||
$baseData = $this->getRawMediaData('anime', $slug);
|
$baseData = $this->getRawMediaData('anime', $slug);
|
||||||
|
|
||||||
@ -523,7 +524,7 @@ final class Model {
|
|||||||
* @param string $kitsuAnimeId The id of the anime on Kitsu
|
* @param string $kitsuAnimeId The id of the anime on Kitsu
|
||||||
* @return string|null Returns the mal id if it exists, otherwise null
|
* @return string|null Returns the mal id if it exists, otherwise null
|
||||||
*/
|
*/
|
||||||
public function getMalIdForAnime(string $kitsuAnimeId)
|
public function getMalIdForAnime(string $kitsuAnimeId): ?string
|
||||||
{
|
{
|
||||||
$options = [
|
$options = [
|
||||||
'query' => [
|
'query' => [
|
||||||
@ -625,7 +626,7 @@ final class Model {
|
|||||||
* Get information about a particular manga
|
* Get information about a particular manga
|
||||||
*
|
*
|
||||||
* @param string $mangaId
|
* @param string $mangaId
|
||||||
* @return array
|
* @return MangaPage
|
||||||
*/
|
*/
|
||||||
public function getMangaById(string $mangaId): MangaPage
|
public function getMangaById(string $mangaId): MangaPage
|
||||||
{
|
{
|
||||||
@ -808,7 +809,7 @@ final class Model {
|
|||||||
* @param string $kitsuMangaId The id of the manga on Kitsu
|
* @param string $kitsuMangaId The id of the manga on Kitsu
|
||||||
* @return string|null Returns the mal id if it exists, otherwise null
|
* @return string|null Returns the mal id if it exists, otherwise null
|
||||||
*/
|
*/
|
||||||
public function getMalIdForManga(string $kitsuMangaId)
|
public function getMalIdForManga(string $kitsuMangaId): ?string
|
||||||
{
|
{
|
||||||
$options = [
|
$options = [
|
||||||
'query' => [
|
'query' => [
|
||||||
@ -920,7 +921,7 @@ final class Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the raw data for the anime id
|
* Get the raw data for the anime/manga id
|
||||||
*
|
*
|
||||||
* @param string $type
|
* @param string $type
|
||||||
* @param string $id
|
* @param string $id
|
||||||
|
@ -18,7 +18,6 @@ namespace Aviat\AnimeClient\API\Kitsu\Transformer;
|
|||||||
|
|
||||||
use Aviat\AnimeClient\API\Kitsu;
|
use Aviat\AnimeClient\API\Kitsu;
|
||||||
use Aviat\AnimeClient\Types\{
|
use Aviat\AnimeClient\Types\{
|
||||||
Anime,
|
|
||||||
FormItem,
|
FormItem,
|
||||||
AnimeListItem
|
AnimeListItem
|
||||||
};
|
};
|
||||||
@ -95,7 +94,7 @@ final class AnimeListTransformer extends AbstractTransformer {
|
|||||||
'started' => $anime['startDate'],
|
'started' => $anime['startDate'],
|
||||||
'ended' => $anime['endDate']
|
'ended' => $anime['endDate']
|
||||||
],
|
],
|
||||||
'anime' => new Anime([
|
'anime' => [
|
||||||
'id' => $animeId,
|
'id' => $animeId,
|
||||||
'age_rating' => $anime['ageRating'],
|
'age_rating' => $anime['ageRating'],
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
@ -105,7 +104,7 @@ final class AnimeListTransformer extends AbstractTransformer {
|
|||||||
'cover_image' => $anime['posterImage']['small'],
|
'cover_image' => $anime['posterImage']['small'],
|
||||||
'genres' => $genres,
|
'genres' => $genres,
|
||||||
'streaming_links' => $streamingLinks,
|
'streaming_links' => $streamingLinks,
|
||||||
]),
|
],
|
||||||
'watching_status' => $item['attributes']['status'],
|
'watching_status' => $item['attributes']['status'],
|
||||||
'notes' => $item['attributes']['notes'],
|
'notes' => $item['attributes']['notes'],
|
||||||
'rewatching' => (bool) $item['attributes']['reconsuming'],
|
'rewatching' => (bool) $item['attributes']['reconsuming'],
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
namespace Aviat\AnimeClient\API\Kitsu\Transformer;
|
namespace Aviat\AnimeClient\API\Kitsu\Transformer;
|
||||||
|
|
||||||
use Aviat\AnimeClient\API\{JsonAPI, Kitsu};
|
use Aviat\AnimeClient\API\{JsonAPI, Kitsu};
|
||||||
use Aviat\AnimeClient\Types\Anime;
|
use Aviat\AnimeClient\Types\AnimePage;
|
||||||
use Aviat\Ion\Transformer\AbstractTransformer;
|
use Aviat\Ion\Transformer\AbstractTransformer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,9 +30,9 @@ final class AnimeTransformer extends AbstractTransformer {
|
|||||||
* logical and workable structure
|
* logical and workable structure
|
||||||
*
|
*
|
||||||
* @param array $item API library item
|
* @param array $item API library item
|
||||||
* @return Anime
|
* @return AnimePage
|
||||||
*/
|
*/
|
||||||
public function transform($item): Anime
|
public function transform($item): AnimePage
|
||||||
{
|
{
|
||||||
$item['included'] = JsonAPI::organizeIncludes($item['included']);
|
$item['included'] = JsonAPI::organizeIncludes($item['included']);
|
||||||
$genres = $item['included']['categories'] ?? [];
|
$genres = $item['included']['categories'] ?? [];
|
||||||
@ -40,13 +40,74 @@ final class AnimeTransformer extends AbstractTransformer {
|
|||||||
sort($item['genres']);
|
sort($item['genres']);
|
||||||
|
|
||||||
$title = $item['canonicalTitle'];
|
$title = $item['canonicalTitle'];
|
||||||
|
|
||||||
$titles = Kitsu::filterTitles($item);
|
$titles = Kitsu::filterTitles($item);
|
||||||
// $titles = array_unique(array_diff($item['titles'], [$title]));
|
|
||||||
|
|
||||||
return new Anime([
|
$characters = [];
|
||||||
|
$staff = [];
|
||||||
|
|
||||||
|
if (array_key_exists('animeCharacters', $item['included']))
|
||||||
|
{
|
||||||
|
$animeCharacters = $item['included']['animeCharacters'];
|
||||||
|
|
||||||
|
foreach ($animeCharacters as $rel)
|
||||||
|
{
|
||||||
|
$charId = $rel['relationships']['character']['data']['id'];
|
||||||
|
$role = $rel['role'];
|
||||||
|
|
||||||
|
if (array_key_exists($charId, $item['included']['characters']))
|
||||||
|
{
|
||||||
|
$characters[$role][$charId] = $item['included']['characters'][$charId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('mediaStaff', $item['included']))
|
||||||
|
{
|
||||||
|
foreach ($item['included']['mediaStaff'] as $id => $staffing)
|
||||||
|
{
|
||||||
|
$personId = $staffing['relationships']['person']['data']['id'];
|
||||||
|
$personDetails = $item['included']['people'][$personId];
|
||||||
|
|
||||||
|
$role = $staffing['role'];
|
||||||
|
|
||||||
|
if ( ! array_key_exists($role, $staff))
|
||||||
|
{
|
||||||
|
$staff[$role] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$staff[$role][$personId] = [
|
||||||
|
'id' => $personId,
|
||||||
|
'name' => $personDetails['name'] ?? '??',
|
||||||
|
'image' => $personDetails['image'],
|
||||||
|
];
|
||||||
|
|
||||||
|
usort($staff[$role], function ($a, $b) {
|
||||||
|
return $a['name'] <=> $b['name'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! empty($characters['main']))
|
||||||
|
{
|
||||||
|
uasort($characters['main'], function ($a, $b) {
|
||||||
|
return $a['name'] <=> $b['name'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! empty($characters['supporting']))
|
||||||
|
{
|
||||||
|
uasort($characters['supporting'], function ($a, $b) {
|
||||||
|
return $a['name'] <=> $b['name'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ksort($characters);
|
||||||
|
ksort($staff);
|
||||||
|
|
||||||
|
return new AnimePage([
|
||||||
'age_rating' => $item['ageRating'],
|
'age_rating' => $item['ageRating'],
|
||||||
'age_rating_guide' => $item['ageRatingGuide'],
|
'age_rating_guide' => $item['ageRatingGuide'],
|
||||||
|
'characters' => $characters,
|
||||||
'cover_image' => $item['posterImage']['small'],
|
'cover_image' => $item['posterImage']['small'],
|
||||||
'episode_count' => $item['episodeCount'],
|
'episode_count' => $item['episodeCount'],
|
||||||
'episode_length' => $item['episodeLength'],
|
'episode_length' => $item['episodeLength'],
|
||||||
@ -55,6 +116,7 @@ final class AnimeTransformer extends AbstractTransformer {
|
|||||||
'included' => $item['included'],
|
'included' => $item['included'],
|
||||||
'show_type' => $this->string($item['showType'])->upperCaseFirst()->__toString(),
|
'show_type' => $this->string($item['showType'])->upperCaseFirst()->__toString(),
|
||||||
'slug' => $item['slug'],
|
'slug' => $item['slug'],
|
||||||
|
'staff' => $staff,
|
||||||
'status' => Kitsu::getAiringStatus($item['startDate'], $item['endDate']),
|
'status' => Kitsu::getAiringStatus($item['startDate'], $item['endDate']),
|
||||||
'streaming_links' => Kitsu::parseStreamingLinks($item['included']),
|
'streaming_links' => Kitsu::parseStreamingLinks($item['included']),
|
||||||
'synopsis' => $item['synopsis'],
|
'synopsis' => $item['synopsis'],
|
||||||
|
173
src/API/Kitsu/Transformer/CharacterTransformer.php
Normal file
173
src/API/Kitsu/Transformer/CharacterTransformer.php
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Hummingbird Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 7.1
|
||||||
|
*
|
||||||
|
* @package HummingbirdAnimeClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2018 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 4.1
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\API\Kitsu\Transformer;
|
||||||
|
|
||||||
|
use Aviat\AnimeClient\API\JsonAPI;
|
||||||
|
use Aviat\AnimeClient\Types\Character;
|
||||||
|
|
||||||
|
use Aviat\Ion\Transformer\AbstractTransformer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data transformation class for character pages
|
||||||
|
*/
|
||||||
|
final class CharacterTransformer extends AbstractTransformer {
|
||||||
|
|
||||||
|
public function transform($characterData): Character
|
||||||
|
{
|
||||||
|
$data = JsonAPI::organizeData($characterData);
|
||||||
|
$attributes = $data[0]['attributes'];
|
||||||
|
$castings = [];
|
||||||
|
|
||||||
|
$names = array_unique(
|
||||||
|
array_merge(
|
||||||
|
[$attributes['canonicalName']],
|
||||||
|
$attributes['names']
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$name = array_shift($names);
|
||||||
|
|
||||||
|
if (array_key_exists('included', $data))
|
||||||
|
{
|
||||||
|
if (array_key_exists('anime', $data['included']))
|
||||||
|
{
|
||||||
|
uasort($data['included']['anime'], function ($a, $b) {
|
||||||
|
return $a['attributes']['canonicalTitle'] <=> $b['attributes']['canonicalTitle'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('manga', $data['included']))
|
||||||
|
{
|
||||||
|
uasort($data['included']['manga'], function ($a, $b) {
|
||||||
|
return $a['attributes']['canonicalTitle'] <=> $b['attributes']['canonicalTitle'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('castings', $data['included']))
|
||||||
|
{
|
||||||
|
$castings = $this->organizeCast($data['included']['castings']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Character([
|
||||||
|
'castings' => $castings,
|
||||||
|
'description' => $attributes['description'],
|
||||||
|
'id' => $data[0]['id'],
|
||||||
|
'media' => [
|
||||||
|
'anime' => $data['included']['anime'] ?? [],
|
||||||
|
'manga' => $data['included']['manga'] ?? [],
|
||||||
|
],
|
||||||
|
'name' => $name,
|
||||||
|
'names' => $names,
|
||||||
|
'otherNames' => $attributes['otherNames'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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'];
|
||||||
|
$hasName = array_key_exists($person['name'], $people);
|
||||||
|
|
||||||
|
if ( ! $hasName)
|
||||||
|
{
|
||||||
|
$people[$person['name']] = $i;
|
||||||
|
$role['relationships']['media']['anime'] = [current($role['relationships']['media']['anime'])];
|
||||||
|
$output[$i] = $role;
|
||||||
|
|
||||||
|
$i++;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected 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)
|
||||||
|
{
|
||||||
|
foreach ($role['relationships']['person']['people'] as $pid => $peoples)
|
||||||
|
{
|
||||||
|
$p = $peoples;
|
||||||
|
}
|
||||||
|
|
||||||
|
$person = $p['attributes'];
|
||||||
|
$person['id'] = $pid;
|
||||||
|
$person['image'] = $person['image']['original'];
|
||||||
|
|
||||||
|
uasort($role['relationships']['media']['anime'], function ($a, $b) {
|
||||||
|
return $a['attributes']['canonicalTitle'] <=> $b['attributes']['canonicalTitle'];
|
||||||
|
});
|
||||||
|
|
||||||
|
$item = [
|
||||||
|
'person' => $person,
|
||||||
|
'series' => $role['relationships']['media']['anime']
|
||||||
|
];
|
||||||
|
|
||||||
|
$output[$roleName][$language][] = $item;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
foreach ($role['relationships']['person']['people'] as $pid => $person)
|
||||||
|
{
|
||||||
|
$person['id'] = $pid;
|
||||||
|
$output[$roleName][$pid] = $person;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
}
|
@ -51,13 +51,77 @@ final class MangaTransformer extends AbstractTransformer {
|
|||||||
$rawTitles = array_values($item['titles']);
|
$rawTitles = array_values($item['titles']);
|
||||||
$titles = array_unique(array_diff($rawTitles, [$title]));
|
$titles = array_unique(array_diff($rawTitles, [$title]));
|
||||||
|
|
||||||
|
$characters = [];
|
||||||
|
$staff = [];
|
||||||
|
|
||||||
|
if (array_key_exists('mediaCharacters', $item['included']))
|
||||||
|
{
|
||||||
|
$mediaCharacters = $item['included']['mediaCharacters'];
|
||||||
|
|
||||||
|
foreach ($mediaCharacters as $rel)
|
||||||
|
{
|
||||||
|
// dd($rel);
|
||||||
|
// $charId = $rel['relationships']['character']['data']['id'];
|
||||||
|
$role = $rel['attributes']['role'];
|
||||||
|
|
||||||
|
foreach ($rel['relationships']['character']['characters'] as $charId => $char)
|
||||||
|
{
|
||||||
|
if (array_key_exists($charId, $item['included']['characters']))
|
||||||
|
{
|
||||||
|
$characters[$role][$charId] = $char['attributes'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('mediaStaff', $item['included']))
|
||||||
|
{
|
||||||
|
foreach ($item['included']['mediaStaff'] as $id => $staffing)
|
||||||
|
{
|
||||||
|
$role = $staffing['attributes']['role'];
|
||||||
|
|
||||||
|
foreach ($staffing['relationships']['person']['people'] as $personId => $personDetails)
|
||||||
|
{
|
||||||
|
if ( ! array_key_exists($role, $staff))
|
||||||
|
{
|
||||||
|
$staff[$role] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$staff[$role][$personId] = [
|
||||||
|
'id' => $personId,
|
||||||
|
'name' => $personDetails['attributes']['name'] ?? '??',
|
||||||
|
'image' => $personDetails['attributes']['image'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! empty($characters['main']))
|
||||||
|
{
|
||||||
|
uasort($characters['main'], function ($a, $b) {
|
||||||
|
return $a['name'] <=> $b['name'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! empty($characters['supporting']))
|
||||||
|
{
|
||||||
|
uasort($characters['supporting'], function ($a, $b) {
|
||||||
|
return $a['name'] <=> $b['name'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ksort($characters);
|
||||||
|
ksort($staff);
|
||||||
|
|
||||||
return new MangaPage([
|
return new MangaPage([
|
||||||
|
'characters' => $characters,
|
||||||
'chapter_count' => $this->count($item['chapterCount']),
|
'chapter_count' => $this->count($item['chapterCount']),
|
||||||
'cover_image' => $item['posterImage']['small'],
|
'cover_image' => $item['posterImage']['small'],
|
||||||
'genres' => $genres,
|
'genres' => $genres,
|
||||||
'id' => $item['id'],
|
'id' => $item['id'],
|
||||||
'included' => $item['included'],
|
'included' => $item['included'],
|
||||||
'manga_type' => $item['mangaType'],
|
'manga_type' => $item['mangaType'],
|
||||||
|
'staff' => $staff,
|
||||||
'synopsis' => $item['synopsis'],
|
'synopsis' => $item['synopsis'],
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
'titles' => $titles,
|
'titles' => $titles,
|
||||||
|
132
src/API/Kitsu/Transformer/PersonTransformer.php
Normal file
132
src/API/Kitsu/Transformer/PersonTransformer.php
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Hummingbird Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 7.1
|
||||||
|
*
|
||||||
|
* @package HummingbirdAnimeClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2018 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 4.1
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\API\Kitsu\Transformer;
|
||||||
|
|
||||||
|
use Aviat\AnimeClient\API\JsonAPI;
|
||||||
|
use Aviat\AnimeClient\Types\Person;
|
||||||
|
use Aviat\Ion\Transformer\AbstractTransformer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data transformation class for people pages
|
||||||
|
*/
|
||||||
|
final class PersonTransformer extends AbstractTransformer {
|
||||||
|
|
||||||
|
public function transform($personData): Person
|
||||||
|
{
|
||||||
|
$data = JsonAPI::organizeData($personData);
|
||||||
|
$included = JsonAPI::organizeIncludes($personData['included']);
|
||||||
|
|
||||||
|
$orgData = $this->organizeData($included);
|
||||||
|
|
||||||
|
return new Person([
|
||||||
|
'id' => $data['id'],
|
||||||
|
'name' => $data['attributes']['name'],
|
||||||
|
'characters' => $orgData['characters'],
|
||||||
|
'staff' => $orgData['staff'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function organizeData(array $data): array
|
||||||
|
{
|
||||||
|
$output = [
|
||||||
|
'characters' => [
|
||||||
|
'main' => [],
|
||||||
|
'supporting' => [],
|
||||||
|
],
|
||||||
|
'staff' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
if (array_key_exists('characterVoices', $data))
|
||||||
|
{
|
||||||
|
foreach ($data['characterVoices'] as $cv)
|
||||||
|
{
|
||||||
|
$mcId = $cv['relationships']['mediaCharacter']['data']['id'];
|
||||||
|
|
||||||
|
if ( ! array_key_exists($mcId, $data['mediaCharacters']))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mc = $data['mediaCharacters'][$mcId];
|
||||||
|
|
||||||
|
$role = $mc['role'];
|
||||||
|
|
||||||
|
$charId = $mc['relationships']['character']['data']['id'];
|
||||||
|
$mediaId = $mc['relationships']['media']['data']['id'];
|
||||||
|
|
||||||
|
$existingMedia = array_key_exists($charId, $output['characters'][$role])
|
||||||
|
? $output['characters'][$role][$charId]['media']
|
||||||
|
: [];
|
||||||
|
|
||||||
|
$relatedMedia = [
|
||||||
|
$mediaId => $data['anime'][$mediaId],
|
||||||
|
];
|
||||||
|
|
||||||
|
$includedMedia = array_replace_recursive($existingMedia, $relatedMedia);
|
||||||
|
|
||||||
|
uasort($includedMedia, function ($a, $b) {
|
||||||
|
return $a['canonicalTitle'] <=> $b['canonicalTitle'];
|
||||||
|
});
|
||||||
|
|
||||||
|
$character = $data['characters'][$charId];
|
||||||
|
|
||||||
|
$output['characters'][$role][$charId] = [
|
||||||
|
'character' => $character,
|
||||||
|
'media' => $includedMedia,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('mediaStaff', $data))
|
||||||
|
{
|
||||||
|
foreach ($data['mediaStaff'] as $rid => $role)
|
||||||
|
{
|
||||||
|
$roleName = $role['role'];
|
||||||
|
$mediaType = $role['relationships']['media']['data']['type'];
|
||||||
|
$mediaId = $role['relationships']['media']['data']['id'];
|
||||||
|
$media = $data[$mediaType][$mediaId];
|
||||||
|
$output['staff'][$roleName][$mediaType][$mediaId] = $media;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uasort($output['characters']['main'], function ($a, $b) {
|
||||||
|
return $a['character']['canonicalName'] <=> $b['character']['canonicalName'];
|
||||||
|
});
|
||||||
|
uasort($output['characters']['supporting'], function ($a, $b) {
|
||||||
|
return $a['character']['canonicalName'] <=> $b['character']['canonicalName'];
|
||||||
|
});
|
||||||
|
ksort($output['staff']);
|
||||||
|
foreach ($output['staff'] as $role => &$media)
|
||||||
|
{
|
||||||
|
if (array_key_exists('anime', $media))
|
||||||
|
{
|
||||||
|
uasort($media['anime'], function ($a, $b) {
|
||||||
|
return $a['canonicalTitle'] <=> $b['canonicalTitle'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('manga', $media))
|
||||||
|
{
|
||||||
|
uasort($media['manga'], function ($a, $b) {
|
||||||
|
return $a['canonicalTitle'] <=> $b['canonicalTitle'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
}
|
168
src/API/Kitsu/Transformer/UserTransformer.php
Normal file
168
src/API/Kitsu/Transformer/UserTransformer.php
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Hummingbird Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 7.1
|
||||||
|
*
|
||||||
|
* @package HummingbirdAnimeClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2018 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 4.1
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\API\Kitsu\Transformer;
|
||||||
|
|
||||||
|
use function Aviat\AnimeClient\getLocalImg;
|
||||||
|
|
||||||
|
use Aviat\AnimeClient\API\JsonAPI;
|
||||||
|
use Aviat\AnimeClient\Types\User;
|
||||||
|
use Aviat\Ion\Transformer\AbstractTransformer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform user profile data for display
|
||||||
|
*/
|
||||||
|
final class UserTransformer extends AbstractTransformer {
|
||||||
|
public function transform($profileData): User
|
||||||
|
{
|
||||||
|
$orgData = JsonAPI::organizeData($profileData)[0];
|
||||||
|
$attributes = $orgData['attributes'];
|
||||||
|
|
||||||
|
$rels = $orgData['relationships'] ?? [];
|
||||||
|
$favorites = array_key_exists('favorites', $rels) ? $rels['favorites'] : [];
|
||||||
|
|
||||||
|
$stats = [];
|
||||||
|
foreach ($rels['stats'] as $sid => &$item)
|
||||||
|
{
|
||||||
|
$key = $item['attributes']['kind'];
|
||||||
|
$stats[$key] = $item['attributes']['statsData'];
|
||||||
|
unset($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
$waifu = [];
|
||||||
|
if (array_key_exists('waifu', $rels))
|
||||||
|
{
|
||||||
|
$waifu = [
|
||||||
|
'label' => $attributes['waifuOrHusbando'],
|
||||||
|
'character' => $rels['waifu']['attributes'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return new User([
|
||||||
|
'about' => $attributes['about'],
|
||||||
|
'avatar' => getLocalImg($attributes['avatar']['original'], FALSE),
|
||||||
|
'favorites' => $this->organizeFavorites($favorites),
|
||||||
|
'location' => $attributes['location'],
|
||||||
|
'name' => $attributes['name'],
|
||||||
|
'slug' => $attributes['slug'],
|
||||||
|
'stats' => $this->organizeStats($stats, $attributes),
|
||||||
|
'waifu' => $waifu,
|
||||||
|
'website' => $attributes['website'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reorganize favorites data to be more useful
|
||||||
|
*
|
||||||
|
* @param array $rawFavorites
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function organizeFavorites(array $rawFavorites): array
|
||||||
|
{
|
||||||
|
$output = [];
|
||||||
|
|
||||||
|
unset($rawFavorites['data']);
|
||||||
|
|
||||||
|
foreach ($rawFavorites as $item)
|
||||||
|
{
|
||||||
|
$rank = $item['attributes']['favRank'];
|
||||||
|
foreach ($item['relationships']['item'] as $key => $fav)
|
||||||
|
{
|
||||||
|
$output[$key] = $output[$key] ?? [];
|
||||||
|
foreach ($fav as $id => $data)
|
||||||
|
{
|
||||||
|
$output[$key][$rank] = array_merge(['id' => $id], $data['attributes']);
|
||||||
|
}
|
||||||
|
|
||||||
|
ksort($output[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format the time spent on anime in a more readable format
|
||||||
|
*
|
||||||
|
* @param int $seconds
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function formatAnimeTime(int $seconds): string
|
||||||
|
{
|
||||||
|
// All the seconds left
|
||||||
|
$remSeconds = $seconds % 60;
|
||||||
|
$minutes = ($seconds - $remSeconds) / 60;
|
||||||
|
|
||||||
|
$minutesPerDay = 1440;
|
||||||
|
$minutesPerYear = $minutesPerDay * 365;
|
||||||
|
|
||||||
|
// Minutes short of a year
|
||||||
|
$years = (int)floor($minutes / $minutesPerYear);
|
||||||
|
$minutes %= $minutesPerYear;
|
||||||
|
|
||||||
|
// Minutes short of a day
|
||||||
|
$extraMinutes = $minutes % $minutesPerDay;
|
||||||
|
$days = ($minutes - $extraMinutes) / $minutesPerDay;
|
||||||
|
|
||||||
|
// Minutes short of an hour
|
||||||
|
$remMinutes = $extraMinutes % 60;
|
||||||
|
$hours = ($extraMinutes - $remMinutes) / 60;
|
||||||
|
|
||||||
|
$output = "{$days} days, {$hours} hours, {$remMinutes} minutes, and {$remSeconds} seconds.";
|
||||||
|
|
||||||
|
if ($years > 0)
|
||||||
|
{
|
||||||
|
$output = "{$years} year(s),{$output}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function organizeStats($stats, $data = []): array
|
||||||
|
{
|
||||||
|
$animeStats = [];
|
||||||
|
$mangaStats = [];
|
||||||
|
$otherStats = [];
|
||||||
|
|
||||||
|
if (array_key_exists('anime-amount-consumed', $stats))
|
||||||
|
{
|
||||||
|
$animeStats = [
|
||||||
|
'Time spent watching anime:' => $this->formatAnimeTime($stats['anime-amount-consumed']['time']),
|
||||||
|
'Anime series watched:' => number_format($stats['anime-amount-consumed']['media']),
|
||||||
|
'Anime episodes watched:' => number_format($stats['anime-amount-consumed']['units']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('manga-amount-consumed', $stats))
|
||||||
|
{
|
||||||
|
$mangaStats = [
|
||||||
|
'Manga series read:' => number_format($stats['manga-amount-consumed']['media']),
|
||||||
|
'Manga chapters read:' => number_format($stats['manga-amount-consumed']['units']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! empty($data))
|
||||||
|
{
|
||||||
|
$otherStats = [
|
||||||
|
'Posts:' => number_format($data['postsCount']),
|
||||||
|
'Comments:' => number_format($data['commentsCount']),
|
||||||
|
'Media Rated:' => number_format($data['ratingsCount']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_merge($animeStats, $mangaStats, $otherStats);
|
||||||
|
}
|
||||||
|
}
|
@ -24,7 +24,7 @@ use Aviat\Ion\Enum;
|
|||||||
* and url route segments
|
* and url route segments
|
||||||
*/
|
*/
|
||||||
final class AnimeWatchingStatus extends Enum {
|
final class AnimeWatchingStatus extends Enum {
|
||||||
const ANILIST_TO_KITSU = [
|
public const ANILIST_TO_KITSU = [
|
||||||
Anilist::WATCHING => Kitsu::WATCHING,
|
Anilist::WATCHING => Kitsu::WATCHING,
|
||||||
Anilist::PLAN_TO_WATCH => Kitsu::PLAN_TO_WATCH,
|
Anilist::PLAN_TO_WATCH => Kitsu::PLAN_TO_WATCH,
|
||||||
Anilist::COMPLETED => Kitsu::COMPLETED,
|
Anilist::COMPLETED => Kitsu::COMPLETED,
|
||||||
@ -32,7 +32,7 @@ final class AnimeWatchingStatus extends Enum {
|
|||||||
Anilist::DROPPED => Kitsu::DROPPED
|
Anilist::DROPPED => Kitsu::DROPPED
|
||||||
];
|
];
|
||||||
|
|
||||||
const KITSU_TO_ANILIST = [
|
public const KITSU_TO_ANILIST = [
|
||||||
Kitsu::WATCHING => Anilist::WATCHING,
|
Kitsu::WATCHING => Anilist::WATCHING,
|
||||||
Kitsu::PLAN_TO_WATCH => Anilist::PLAN_TO_WATCH,
|
Kitsu::PLAN_TO_WATCH => Anilist::PLAN_TO_WATCH,
|
||||||
Kitsu::COMPLETED => Anilist::COMPLETED,
|
Kitsu::COMPLETED => Anilist::COMPLETED,
|
||||||
@ -40,7 +40,7 @@ final class AnimeWatchingStatus extends Enum {
|
|||||||
Kitsu::DROPPED => Anilist::DROPPED
|
Kitsu::DROPPED => Anilist::DROPPED
|
||||||
];
|
];
|
||||||
|
|
||||||
const KITSU_TO_TITLE = [
|
public const KITSU_TO_TITLE = [
|
||||||
Kitsu::WATCHING => Title::WATCHING,
|
Kitsu::WATCHING => Title::WATCHING,
|
||||||
Kitsu::PLAN_TO_WATCH => Title::PLAN_TO_WATCH,
|
Kitsu::PLAN_TO_WATCH => Title::PLAN_TO_WATCH,
|
||||||
Kitsu::ON_HOLD => Title::ON_HOLD,
|
Kitsu::ON_HOLD => Title::ON_HOLD,
|
||||||
@ -48,7 +48,7 @@ final class AnimeWatchingStatus extends Enum {
|
|||||||
Kitsu::COMPLETED => Title::COMPLETED
|
Kitsu::COMPLETED => Title::COMPLETED
|
||||||
];
|
];
|
||||||
|
|
||||||
const ROUTE_TO_KITSU = [
|
public const ROUTE_TO_KITSU = [
|
||||||
Route::WATCHING => Kitsu::WATCHING,
|
Route::WATCHING => Kitsu::WATCHING,
|
||||||
Route::PLAN_TO_WATCH => Kitsu::PLAN_TO_WATCH,
|
Route::PLAN_TO_WATCH => Kitsu::PLAN_TO_WATCH,
|
||||||
Route::ON_HOLD => Kitsu::ON_HOLD,
|
Route::ON_HOLD => Kitsu::ON_HOLD,
|
||||||
@ -56,7 +56,7 @@ final class AnimeWatchingStatus extends Enum {
|
|||||||
Route::COMPLETED => Kitsu::COMPLETED
|
Route::COMPLETED => Kitsu::COMPLETED
|
||||||
];
|
];
|
||||||
|
|
||||||
const ROUTE_TO_TITLE = [
|
public const ROUTE_TO_TITLE = [
|
||||||
Route::ALL => Title::ALL,
|
Route::ALL => Title::ALL,
|
||||||
Route::WATCHING => Title::WATCHING,
|
Route::WATCHING => Title::WATCHING,
|
||||||
Route::PLAN_TO_WATCH => Title::PLAN_TO_WATCH,
|
Route::PLAN_TO_WATCH => Title::PLAN_TO_WATCH,
|
||||||
@ -65,7 +65,7 @@ final class AnimeWatchingStatus extends Enum {
|
|||||||
Route::COMPLETED => Title::COMPLETED
|
Route::COMPLETED => Title::COMPLETED
|
||||||
];
|
];
|
||||||
|
|
||||||
const TITLE_TO_ROUTE = [
|
public const TITLE_TO_ROUTE = [
|
||||||
Title::ALL => Route::ALL,
|
Title::ALL => Route::ALL,
|
||||||
Title::WATCHING => Route::WATCHING,
|
Title::WATCHING => Route::WATCHING,
|
||||||
Title::PLAN_TO_WATCH => Route::PLAN_TO_WATCH,
|
Title::PLAN_TO_WATCH => Route::PLAN_TO_WATCH,
|
||||||
|
@ -24,7 +24,7 @@ use Aviat\Ion\Enum;
|
|||||||
* and url route segments
|
* and url route segments
|
||||||
*/
|
*/
|
||||||
final class MangaReadingStatus extends Enum {
|
final class MangaReadingStatus extends Enum {
|
||||||
const ANILIST_TO_KITSU = [
|
public const ANILIST_TO_KITSU = [
|
||||||
Anilist::READING => Kitsu::READING,
|
Anilist::READING => Kitsu::READING,
|
||||||
Anilist::PLAN_TO_READ => Kitsu::PLAN_TO_READ,
|
Anilist::PLAN_TO_READ => Kitsu::PLAN_TO_READ,
|
||||||
Anilist::COMPLETED => Kitsu::COMPLETED,
|
Anilist::COMPLETED => Kitsu::COMPLETED,
|
||||||
@ -32,7 +32,7 @@ final class MangaReadingStatus extends Enum {
|
|||||||
Anilist::DROPPED => Kitsu::DROPPED
|
Anilist::DROPPED => Kitsu::DROPPED
|
||||||
];
|
];
|
||||||
|
|
||||||
const KITSU_TO_ANILIST = [
|
public const KITSU_TO_ANILIST = [
|
||||||
Kitsu::READING => Anilist::READING,
|
Kitsu::READING => Anilist::READING,
|
||||||
Kitsu::PLAN_TO_READ => Anilist::PLAN_TO_READ,
|
Kitsu::PLAN_TO_READ => Anilist::PLAN_TO_READ,
|
||||||
Kitsu::COMPLETED => Anilist::COMPLETED,
|
Kitsu::COMPLETED => Anilist::COMPLETED,
|
||||||
@ -40,7 +40,7 @@ final class MangaReadingStatus extends Enum {
|
|||||||
Kitsu::DROPPED => Anilist::DROPPED
|
Kitsu::DROPPED => Anilist::DROPPED
|
||||||
];
|
];
|
||||||
|
|
||||||
const KITSU_TO_TITLE = [
|
public const KITSU_TO_TITLE = [
|
||||||
Kitsu::READING => Title::READING,
|
Kitsu::READING => Title::READING,
|
||||||
Kitsu::PLAN_TO_READ => Title::PLAN_TO_READ,
|
Kitsu::PLAN_TO_READ => Title::PLAN_TO_READ,
|
||||||
Kitsu::COMPLETED => Title::COMPLETED,
|
Kitsu::COMPLETED => Title::COMPLETED,
|
||||||
@ -48,7 +48,7 @@ final class MangaReadingStatus extends Enum {
|
|||||||
Kitsu::DROPPED => Title::DROPPED,
|
Kitsu::DROPPED => Title::DROPPED,
|
||||||
];
|
];
|
||||||
|
|
||||||
const ROUTE_TO_KITSU = [
|
public const ROUTE_TO_KITSU = [
|
||||||
Route::PLAN_TO_READ => Kitsu::PLAN_TO_READ,
|
Route::PLAN_TO_READ => Kitsu::PLAN_TO_READ,
|
||||||
Route::READING => Kitsu::READING,
|
Route::READING => Kitsu::READING,
|
||||||
Route::COMPLETED => Kitsu::COMPLETED,
|
Route::COMPLETED => Kitsu::COMPLETED,
|
||||||
@ -56,7 +56,7 @@ final class MangaReadingStatus extends Enum {
|
|||||||
Route::ON_HOLD => Kitsu::ON_HOLD,
|
Route::ON_HOLD => Kitsu::ON_HOLD,
|
||||||
];
|
];
|
||||||
|
|
||||||
const ROUTE_TO_TITLE = [
|
public const ROUTE_TO_TITLE = [
|
||||||
Route::ALL => Title::ALL,
|
Route::ALL => Title::ALL,
|
||||||
Route::PLAN_TO_READ => Title::PLAN_TO_READ,
|
Route::PLAN_TO_READ => Title::PLAN_TO_READ,
|
||||||
Route::READING => Title::READING,
|
Route::READING => Title::READING,
|
||||||
@ -65,7 +65,7 @@ final class MangaReadingStatus extends Enum {
|
|||||||
Route::ON_HOLD => Title::ON_HOLD,
|
Route::ON_HOLD => Title::ON_HOLD,
|
||||||
];
|
];
|
||||||
|
|
||||||
const TITLE_TO_KITSU = [
|
public const TITLE_TO_KITSU = [
|
||||||
Title::PLAN_TO_READ => Kitsu::PLAN_TO_READ,
|
Title::PLAN_TO_READ => Kitsu::PLAN_TO_READ,
|
||||||
Title::READING => Kitsu::READING,
|
Title::READING => Kitsu::READING,
|
||||||
Title::COMPLETED => Kitsu::COMPLETED,
|
Title::COMPLETED => Kitsu::COMPLETED,
|
||||||
|
@ -18,6 +18,7 @@ namespace Aviat\AnimeClient\API;
|
|||||||
|
|
||||||
use function Amp\call;
|
use function Amp\call;
|
||||||
use function Amp\Promise\{all, wait};
|
use function Amp\Promise\{all, wait};
|
||||||
|
use function Aviat\AnimeClient\getApiClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to simplify making and validating simultaneous requests
|
* Class to simplify making and validating simultaneous requests
|
||||||
@ -70,7 +71,8 @@ final class ParallelAPIRequest {
|
|||||||
*/
|
*/
|
||||||
public function makeRequests(): array
|
public function makeRequests(): array
|
||||||
{
|
{
|
||||||
$client = new HummingbirdClient();
|
$client = getApiClient();
|
||||||
|
|
||||||
$promises = [];
|
$promises = [];
|
||||||
|
|
||||||
foreach ($this->requests as $key => $url)
|
foreach ($this->requests as $key => $url)
|
||||||
@ -92,7 +94,8 @@ final class ParallelAPIRequest {
|
|||||||
*/
|
*/
|
||||||
public function getResponses(): array
|
public function getResponses(): array
|
||||||
{
|
{
|
||||||
$client = new HummingbirdClient();
|
$client = getApiClient();
|
||||||
|
|
||||||
$promises = [];
|
$promises = [];
|
||||||
|
|
||||||
foreach ($this->requests as $key => $url)
|
foreach ($this->requests as $key => $url)
|
||||||
|
@ -16,6 +16,10 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient;
|
namespace Aviat\AnimeClient;
|
||||||
|
|
||||||
|
use function Amp\Promise\wait;
|
||||||
|
|
||||||
|
use Amp\Artax\{Client, DefaultClient, Response};
|
||||||
|
|
||||||
use Aviat\Ion\ConfigInterface;
|
use Aviat\Ion\ConfigInterface;
|
||||||
use Yosymfony\Toml\{Toml, TomlBuilder};
|
use Yosymfony\Toml\{Toml, TomlBuilder};
|
||||||
|
|
||||||
@ -203,6 +207,37 @@ function checkFolderPermissions(ConfigInterface $config): array
|
|||||||
return $errors;
|
return $errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an API Client, with better defaults
|
||||||
|
*
|
||||||
|
* @return DefaultClient
|
||||||
|
*/
|
||||||
|
function getApiClient ()
|
||||||
|
{
|
||||||
|
static $client;
|
||||||
|
|
||||||
|
if ($client === NULL)
|
||||||
|
{
|
||||||
|
$client = new DefaultClient;
|
||||||
|
$client->setOption(Client::OP_TRANSFER_TIMEOUT, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simplify making a request with Artax
|
||||||
|
*
|
||||||
|
* @param $request
|
||||||
|
* @return Response
|
||||||
|
* @throws \Throwable
|
||||||
|
*/
|
||||||
|
function getResponse ($request): Response
|
||||||
|
{
|
||||||
|
$client = getApiClient();
|
||||||
|
return wait($client->request($request));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the path for the cached image from the original image
|
* Generate the path for the cached image from the original image
|
||||||
*
|
*
|
||||||
@ -246,7 +281,7 @@ function getLocalImg ($kitsuUrl, $webp = TRUE): string
|
|||||||
* @param int $height
|
* @param int $height
|
||||||
* @param string $text
|
* @param string $text
|
||||||
*/
|
*/
|
||||||
function createPlaceholderImage ($path, $width, $height, $text = 'Image Unavailable')
|
function createPlaceholderImage ($path, $width, $height, $text = 'Image Unavailable'): void
|
||||||
{
|
{
|
||||||
$width = $width ?? 200;
|
$width = $width ?? 200;
|
||||||
$height = $height ?? 200;
|
$height = $height ?? 200;
|
||||||
@ -259,7 +294,7 @@ function createPlaceholderImage ($path, $width, $height, $text = 'Image Unavaila
|
|||||||
// Background is the first color by default
|
// Background is the first color by default
|
||||||
$fillColor = imagecolorallocatealpha($img, 255, 255, 255, 127);
|
$fillColor = imagecolorallocatealpha($img, 255, 255, 255, 127);
|
||||||
imagefill($img, 0, 0, $fillColor);
|
imagefill($img, 0, 0, $fillColor);
|
||||||
|
|
||||||
$textColor = imagecolorallocate($img, 64, 64, 64);
|
$textColor = imagecolorallocate($img, 64, 64, 64);
|
||||||
|
|
||||||
imagealphablending($img, TRUE);
|
imagealphablending($img, TRUE);
|
||||||
@ -268,7 +303,7 @@ function createPlaceholderImage ($path, $width, $height, $text = 'Image Unavaila
|
|||||||
$fontSize = 10;
|
$fontSize = 10;
|
||||||
$fontWidth = imagefontwidth($fontSize);
|
$fontWidth = imagefontwidth($fontSize);
|
||||||
$fontHeight = imagefontheight($fontSize);
|
$fontHeight = imagefontheight($fontSize);
|
||||||
$length = strlen($text);
|
$length = \strlen($text);
|
||||||
$textWidth = $length * $fontWidth;
|
$textWidth = $length * $fontWidth;
|
||||||
$fxPos = (int) ceil((imagesx($img) - $textWidth) / 2);
|
$fxPos = (int) ceil((imagesx($img) - $textWidth) / 2);
|
||||||
$fyPos = (int) ceil((imagesy($img) - $fontHeight) / 2);
|
$fyPos = (int) ceil((imagesy($img) - $fontHeight) / 2);
|
||||||
@ -280,11 +315,11 @@ function createPlaceholderImage ($path, $width, $height, $text = 'Image Unavaila
|
|||||||
imagesavealpha($img, TRUE);
|
imagesavealpha($img, TRUE);
|
||||||
imagepng($img, $path . '/placeholder.png', 9);
|
imagepng($img, $path . '/placeholder.png', 9);
|
||||||
imagedestroy($img);
|
imagedestroy($img);
|
||||||
|
|
||||||
$pngImage = imagecreatefrompng($path . '/placeholder.png');
|
$pngImage = imagecreatefrompng($path . '/placeholder.png');
|
||||||
imagealphablending($pngImage, TRUE);
|
imagealphablending($pngImage, TRUE);
|
||||||
imagesavealpha($pngImage, TRUE);
|
imagesavealpha($pngImage, TRUE);
|
||||||
|
|
||||||
imagewebp($pngImage, $path . '/placeholder.webp');
|
imagewebp($pngImage, $path . '/placeholder.webp');
|
||||||
|
|
||||||
imagedestroy($pngImage);
|
imagedestroy($pngImage);
|
||||||
|
@ -21,13 +21,9 @@ use function Aviat\AnimeClient\loadTomlFile;
|
|||||||
|
|
||||||
use Aura\Router\RouterContainer;
|
use Aura\Router\RouterContainer;
|
||||||
use Aura\Session\SessionFactory;
|
use Aura\Session\SessionFactory;
|
||||||
use Aviat\AnimeClient\UrlGenerator;
|
use Aviat\AnimeClient\{Model, UrlGenerator, Util};
|
||||||
use Aviat\AnimeClient\Util;
|
use Aviat\AnimeClient\API\{Anilist, CacheTrait, Kitsu};
|
||||||
use Aviat\AnimeClient\API\CacheTrait;
|
|
||||||
use Aviat\AnimeClient\API\Anilist;
|
|
||||||
use Aviat\AnimeClient\API\Kitsu;
|
|
||||||
use Aviat\AnimeClient\API\Kitsu\KitsuRequestBuilder;
|
use Aviat\AnimeClient\API\Kitsu\KitsuRequestBuilder;
|
||||||
use Aviat\AnimeClient\Model;
|
|
||||||
use Aviat\Banker\Pool;
|
use Aviat\Banker\Pool;
|
||||||
use Aviat\Ion\Config;
|
use Aviat\Ion\Config;
|
||||||
use Aviat\Ion\Di\{Container, ContainerAware};
|
use Aviat\Ion\Di\{Container, ContainerAware};
|
||||||
|
@ -1,59 +1,59 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
/**
|
/**
|
||||||
* Hummingbird Anime List Client
|
* Hummingbird Anime List Client
|
||||||
*
|
*
|
||||||
* An API client for Kitsu to manage anime and manga watch lists
|
* An API client for Kitsu to manage anime and manga watch lists
|
||||||
*
|
*
|
||||||
* PHP version 7.1
|
* PHP version 7.1
|
||||||
*
|
*
|
||||||
* @package HummingbirdAnimeClient
|
* @package HummingbirdAnimeClient
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2018 Timothy J. Warren
|
* @copyright 2015 - 2018 Timothy J. Warren
|
||||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
* @version 4.1
|
* @version 4.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Aviat\AnimeClient\Command;
|
namespace Aviat\AnimeClient\Command;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears out image cache directories
|
* Clears out image cache directories
|
||||||
*/
|
*/
|
||||||
class ClearThumbnails extends BaseCommand {
|
class ClearThumbnails extends BaseCommand {
|
||||||
|
|
||||||
public function execute(array $args, array $options = []): void
|
public function execute(array $args, array $options = []): void
|
||||||
{
|
{
|
||||||
$this->clearThumbs();
|
$this->clearThumbs();
|
||||||
$this->echoBox('All cached images have been removed');
|
$this->echoBox('All cached images have been removed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function clearThumbs()
|
private function clearThumbs(): void
|
||||||
{
|
{
|
||||||
$imgDir = realpath(__DIR__ . '/../../public/images');
|
$imgDir = realpath(__DIR__ . '/../../public/images');
|
||||||
|
|
||||||
$paths = [
|
$paths = [
|
||||||
'avatars/*.gif',
|
'avatars/*.gif',
|
||||||
'avatars/*.jpg',
|
'avatars/*.jpg',
|
||||||
'avatars/*.png',
|
'avatars/*.png',
|
||||||
'avatars/*.webp',
|
'avatars/*.webp',
|
||||||
'anime/*.jpg',
|
'anime/*.jpg',
|
||||||
'anime/*.png',
|
'anime/*.png',
|
||||||
'anime/*.webp',
|
'anime/*.webp',
|
||||||
'manga/*.jpg',
|
'manga/*.jpg',
|
||||||
'manga/*.png',
|
'manga/*.png',
|
||||||
'manga/*.webp',
|
'manga/*.webp',
|
||||||
'characters/*.jpg',
|
'characters/*.jpg',
|
||||||
'characters/*.png',
|
'characters/*.png',
|
||||||
'characters/*.webp',
|
'characters/*.webp',
|
||||||
'people/*.jpg',
|
'people/*.jpg',
|
||||||
'people/*.png',
|
'people/*.png',
|
||||||
'people/*.webp',
|
'people/*.webp',
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach($paths as $path)
|
foreach($paths as $path)
|
||||||
{
|
{
|
||||||
$cmd = "rm -rf {$imgDir}/{$path}";
|
$cmd = "rm -rf {$imgDir}/{$path}";
|
||||||
exec($cmd);
|
exec($cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -264,6 +264,11 @@ final class SyncLists extends BaseCommand {
|
|||||||
|
|
||||||
foreach ($potentialMappings as $mappingId)
|
foreach ($potentialMappings as $mappingId)
|
||||||
{
|
{
|
||||||
|
if (\is_array($mappingId))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (array_key_exists($mappingId, $includes['mappings']))
|
if (array_key_exists($mappingId, $includes['mappings']))
|
||||||
{
|
{
|
||||||
$malId = $includes['mappings'][$mappingId]['externalId'];
|
$malId = $includes['mappings'][$mappingId]['externalId'];
|
||||||
@ -505,12 +510,16 @@ final class SyncLists extends BaseCommand {
|
|||||||
// Use the first set rating, otherwise use the newer rating
|
// Use the first set rating, otherwise use the newer rating
|
||||||
if ( ! $sameRating)
|
if ( ! $sameRating)
|
||||||
{
|
{
|
||||||
if ($kitsuItem['data']['rating'] !== 0 && $dateDiff === 1)
|
if (
|
||||||
|
$dateDiff === 1 &&
|
||||||
|
$kitsuItem['data']['rating'] !== 0 &&
|
||||||
|
$kitsuItem['data']['ratingTwenty'] !== 0
|
||||||
|
)
|
||||||
{
|
{
|
||||||
$update['data']['ratingTwenty'] = $kitsuItem['data']['ratingTwenty'];
|
$update['data']['ratingTwenty'] = $kitsuItem['data']['ratingTwenty'];
|
||||||
$return['updateType'][] = 'anilist';
|
$return['updateType'][] = 'anilist';
|
||||||
}
|
}
|
||||||
else if($dateDiff === -1)
|
else if($dateDiff === -1 && $anilistItem['data']['rating'] !== 0)
|
||||||
{
|
{
|
||||||
$update['data']['ratingTwenty'] = $anilistItem['data']['rating'] * 2;
|
$update['data']['ratingTwenty'] = $anilistItem['data']['rating'] * 2;
|
||||||
$return['updateType'][] = 'kitsu';
|
$return['updateType'][] = 'kitsu';
|
||||||
@ -547,6 +556,12 @@ final class SyncLists extends BaseCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No changes? Let's bail!
|
||||||
|
if (empty($return['updateType']))
|
||||||
|
{
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
$return['meta'] = [
|
$return['meta'] = [
|
||||||
'kitsu' => $kitsuItem['data'],
|
'kitsu' => $kitsuItem['data'],
|
||||||
'anilist' => $anilistItem['data'],
|
'anilist' => $anilistItem['data'],
|
||||||
|
@ -183,7 +183,7 @@ class Controller {
|
|||||||
* @throws \Aviat\Ion\Di\Exception\NotFoundException
|
* @throws \Aviat\Ion\Di\Exception\NotFoundException
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function sessionRedirect()
|
public function sessionRedirect(): void
|
||||||
{
|
{
|
||||||
$target = $this->session->get('redirect_url');
|
$target = $this->session->get('redirect_url');
|
||||||
if (empty($target))
|
if (empty($target))
|
||||||
@ -208,7 +208,7 @@ class Controller {
|
|||||||
* @throws \Aviat\Ion\Di\Exception\NotFoundException
|
* @throws \Aviat\Ion\Di\Exception\NotFoundException
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function loadPartial($view, string $template, array $data = [])
|
protected function loadPartial($view, string $template, array $data = []): string
|
||||||
{
|
{
|
||||||
$router = $this->container->get('dispatcher');
|
$router = $this->container->get('dispatcher');
|
||||||
|
|
||||||
@ -242,7 +242,7 @@ class Controller {
|
|||||||
* @throws \Aviat\Ion\Di\Exception\NotFoundException
|
* @throws \Aviat\Ion\Di\Exception\NotFoundException
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function renderFullPage($view, string $template, array $data)
|
protected function renderFullPage($view, string $template, array $data): void
|
||||||
{
|
{
|
||||||
$csp = [
|
$csp = [
|
||||||
"default-src 'self'",
|
"default-src 'self'",
|
||||||
@ -275,7 +275,7 @@ class Controller {
|
|||||||
public function notFound(
|
public function notFound(
|
||||||
string $title = 'Sorry, page not found',
|
string $title = 'Sorry, page not found',
|
||||||
string $message = 'Page Not Found'
|
string $message = 'Page Not Found'
|
||||||
)
|
): void
|
||||||
{
|
{
|
||||||
$this->outputHTML('404', [
|
$this->outputHTML('404', [
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
@ -383,7 +383,7 @@ class Controller {
|
|||||||
* @throws \Aviat\Ion\Di\Exception\NotFoundException
|
* @throws \Aviat\Ion\Di\Exception\NotFoundException
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function outputHTML(string $template, array $data = [], $view = NULL, int $code = 200)
|
protected function outputHTML(string $template, array $data = [], $view = NULL, int $code = 200): void
|
||||||
{
|
{
|
||||||
if (null === $view)
|
if (null === $view)
|
||||||
{
|
{
|
||||||
|
@ -23,15 +23,11 @@ use Aviat\AnimeClient\API\Mapping\AnimeWatchingStatus;
|
|||||||
use Aviat\AnimeClient\Types\FormItem;
|
use Aviat\AnimeClient\Types\FormItem;
|
||||||
use Aviat\Ion\Di\ContainerInterface;
|
use Aviat\Ion\Di\ContainerInterface;
|
||||||
use Aviat\Ion\Json;
|
use Aviat\Ion\Json;
|
||||||
use Aviat\Ion\StringWrapper;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller for Anime-related pages
|
* Controller for Anime-related pages
|
||||||
*/
|
*/
|
||||||
final class Anime extends BaseController {
|
final class Anime extends BaseController {
|
||||||
|
|
||||||
use StringWrapper;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The anime list model
|
* The anime list model
|
||||||
* @var \Aviat\AnimeClient\Model\Anime $model
|
* @var \Aviat\AnimeClient\Model\Anime $model
|
||||||
@ -276,8 +272,6 @@ final class Anime extends BaseController {
|
|||||||
public function details(string $animeId): void
|
public function details(string $animeId): void
|
||||||
{
|
{
|
||||||
$data = $this->model->getAnime($animeId);
|
$data = $this->model->getAnime($animeId);
|
||||||
$characters = [];
|
|
||||||
$staff = [];
|
|
||||||
|
|
||||||
if (empty($data))
|
if (empty($data))
|
||||||
{
|
{
|
||||||
@ -291,77 +285,13 @@ final class Anime extends BaseController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists('animeCharacters', $data['included']))
|
|
||||||
{
|
|
||||||
$animeCharacters = $data['included']['animeCharacters'];
|
|
||||||
|
|
||||||
foreach ($animeCharacters as $rel)
|
|
||||||
{
|
|
||||||
$charId = $rel['relationships']['character']['data']['id'];
|
|
||||||
$role = $rel['role'];
|
|
||||||
|
|
||||||
if (array_key_exists($charId, $data['included']['characters']))
|
|
||||||
{
|
|
||||||
$characters[$role][$charId] = $data['included']['characters'][$charId];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists('mediaStaff', $data['included']))
|
|
||||||
{
|
|
||||||
foreach ($data['included']['mediaStaff'] as $id => $staffing)
|
|
||||||
{
|
|
||||||
$personId = $staffing['relationships']['person']['data']['id'];
|
|
||||||
$personDetails = $data['included']['people'][$personId];
|
|
||||||
|
|
||||||
$role = $staffing['role'];
|
|
||||||
|
|
||||||
if ( ! array_key_exists($role, $staff))
|
|
||||||
{
|
|
||||||
$staff[$role] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$staff[$role][$personId] = [
|
|
||||||
'id' => $personId,
|
|
||||||
'name' => $personDetails['name'] ?? '??',
|
|
||||||
'image' => $personDetails['image'],
|
|
||||||
];
|
|
||||||
|
|
||||||
usort($staff[$role], function ($a, $b) {
|
|
||||||
return $a['name'] <=> $b['name'];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! empty($characters['main']))
|
|
||||||
{
|
|
||||||
uasort($characters['main'], function ($a, $b) {
|
|
||||||
return $a['name'] <=> $b['name'];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! empty($characters['supporting']))
|
|
||||||
{
|
|
||||||
uasort($characters['supporting'], function ($a, $b) {
|
|
||||||
return $a['name'] <=> $b['name'];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ksort($characters);
|
|
||||||
ksort($staff);
|
|
||||||
|
|
||||||
// dump($characters);
|
|
||||||
// dump($staff);
|
|
||||||
|
|
||||||
$this->outputHTML('anime/details', [
|
$this->outputHTML('anime/details', [
|
||||||
'title' => $this->formatTitle(
|
'title' => $this->formatTitle(
|
||||||
$this->config->get('whose_list') . "'s Anime List",
|
$this->config->get('whose_list') . "'s Anime List",
|
||||||
'Anime',
|
'Anime',
|
||||||
$data->title
|
$data->title
|
||||||
),
|
),
|
||||||
'characters' => $characters,
|
'data' => $data,
|
||||||
'show_data' => $data,
|
|
||||||
'staff' => $staff,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ final class AnimeCollection extends BaseController {
|
|||||||
* @throws \Aviat\Ion\Exception\DoubleRenderException
|
* @throws \Aviat\Ion\Exception\DoubleRenderException
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function search()
|
public function search(): void
|
||||||
{
|
{
|
||||||
$queryParams = $this->request->getQueryParams();
|
$queryParams = $this->request->getQueryParams();
|
||||||
$query = $queryParams['query'];
|
$query = $queryParams['query'];
|
||||||
@ -84,7 +84,7 @@ final class AnimeCollection extends BaseController {
|
|||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function index($view)
|
public function index($view): void
|
||||||
{
|
{
|
||||||
$viewMap = [
|
$viewMap = [
|
||||||
'' => 'cover',
|
'' => 'cover',
|
||||||
@ -110,7 +110,7 @@ final class AnimeCollection extends BaseController {
|
|||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function form($id = NULL)
|
public function form($id = NULL): void
|
||||||
{
|
{
|
||||||
$this->setSessionRedirect();
|
$this->setSessionRedirect();
|
||||||
|
|
||||||
@ -137,7 +137,7 @@ final class AnimeCollection extends BaseController {
|
|||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function edit()
|
public function edit(): void
|
||||||
{
|
{
|
||||||
$data = $this->request->getParsedBody();
|
$data = $this->request->getParsedBody();
|
||||||
if (array_key_exists('hummingbird_id', $data))
|
if (array_key_exists('hummingbird_id', $data))
|
||||||
@ -161,7 +161,7 @@ final class AnimeCollection extends BaseController {
|
|||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function add()
|
public function add(): void
|
||||||
{
|
{
|
||||||
$data = $this->request->getParsedBody();
|
$data = $this->request->getParsedBody();
|
||||||
if (array_key_exists('id', $data))
|
if (array_key_exists('id', $data))
|
||||||
@ -182,7 +182,7 @@ final class AnimeCollection extends BaseController {
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function delete()
|
public function delete(): void
|
||||||
{
|
{
|
||||||
$data = $this->request->getParsedBody();
|
$data = $this->request->getParsedBody();
|
||||||
if ( ! array_key_exists('hummingbird_id', $data))
|
if ( ! array_key_exists('hummingbird_id', $data))
|
||||||
|
@ -17,30 +17,42 @@
|
|||||||
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\AnimeClient\API\Kitsu\Transformer\CharacterTransformer;
|
||||||
use Aviat\Ion\ArrayWrapper;
|
|
||||||
|
use Aviat\Ion\Di\ContainerInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller for character description pages
|
* Controller for character description pages
|
||||||
*/
|
*/
|
||||||
class Character extends BaseController {
|
class Character extends BaseController {
|
||||||
|
|
||||||
use ArrayWrapper;
|
/**
|
||||||
|
* @var \Aviat\AnimeClient\API\Kitsu\Model
|
||||||
|
*/
|
||||||
|
private $model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Character constructor.
|
||||||
|
*
|
||||||
|
* @param ContainerInterface $container
|
||||||
|
* @throws \Aviat\Ion\Di\Exception\ContainerException
|
||||||
|
* @throws \Aviat\Ion\Di\Exception\NotFoundException
|
||||||
|
*/
|
||||||
|
public function __construct(ContainerInterface $container)
|
||||||
|
{
|
||||||
|
parent::__construct($container);
|
||||||
|
$this->model = $container->get('kitsu-model');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show information about a character
|
* Show information about a character
|
||||||
*
|
*
|
||||||
* @param string $slug
|
* @param string $slug
|
||||||
* @throws \Aviat\Ion\Di\ContainerException
|
|
||||||
* @throws \Aviat\Ion\Di\NotFoundException
|
|
||||||
* @throws \InvalidArgumentException
|
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function index(string $slug): void
|
public function index(string $slug): void
|
||||||
{
|
{
|
||||||
$model = $this->container->get('kitsu-model');
|
$rawData = $this->model->getCharacter($slug);
|
||||||
|
|
||||||
$rawData = $model->getCharacter($slug);
|
|
||||||
|
|
||||||
if (( ! array_key_exists('data', $rawData)) || empty($rawData['data']))
|
if (( ! array_key_exists('data', $rawData)) || empty($rawData['data']))
|
||||||
{
|
{
|
||||||
@ -55,167 +67,14 @@ class Character extends BaseController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = JsonAPI::organizeData($rawData);
|
$data = (new CharacterTransformer())->transform($rawData)->toArray();
|
||||||
|
|
||||||
$data['names'] = array_unique(
|
$this->outputHTML('character/details', [
|
||||||
array_merge(
|
|
||||||
[ $data[0]['attributes']['canonicalName'] ],
|
|
||||||
$data[0]['attributes']['names']
|
|
||||||
)
|
|
||||||
);
|
|
||||||
$data['name'] = array_shift($data['names']);
|
|
||||||
|
|
||||||
if (array_key_exists('included', $data))
|
|
||||||
{
|
|
||||||
if (array_key_exists('anime', $data['included']))
|
|
||||||
{
|
|
||||||
uasort($data['included']['anime'], function ($a, $b) {
|
|
||||||
return $a['attributes']['canonicalTitle'] <=> $b['attributes']['canonicalTitle'];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists('manga', $data['included']))
|
|
||||||
{
|
|
||||||
uasort($data['included']['manga'], function ($a, $b) {
|
|
||||||
return $a['attributes']['canonicalTitle'] <=> $b['attributes']['canonicalTitle'];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$viewData = [
|
|
||||||
'title' => $this->formatTitle(
|
'title' => $this->formatTitle(
|
||||||
'Characters',
|
'Characters',
|
||||||
$data[0]['attributes']['name']
|
$data['name']
|
||||||
),
|
),
|
||||||
'data' => $data,
|
'data' => $data,
|
||||||
'castCount' => 0,
|
]);
|
||||||
'castings' => []
|
|
||||||
];
|
|
||||||
|
|
||||||
if (array_key_exists('included', $data))
|
|
||||||
{
|
|
||||||
if (array_key_exists('castings', $data['included']))
|
|
||||||
{
|
|
||||||
$viewData['castings'] = $this->organizeCast($data['included']['castings']);
|
|
||||||
$viewData['castCount'] = $this->getCastCount($viewData['castings']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->outputHTML('character/details', $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'];
|
|
||||||
$hasName = array_key_exists($person['name'], $people);
|
|
||||||
|
|
||||||
if ( ! $hasName)
|
|
||||||
{
|
|
||||||
$people[$person['name']] = $i;
|
|
||||||
$role['relationships']['media']['anime'] = [current($role['relationships']['media']['anime'])];
|
|
||||||
$output[$i] = $role;
|
|
||||||
|
|
||||||
$i++;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getCastCount(array $cast): int
|
|
||||||
{
|
|
||||||
$count = 0;
|
|
||||||
|
|
||||||
foreach($cast as $role)
|
|
||||||
{
|
|
||||||
$count++;
|
|
||||||
/* if (
|
|
||||||
array_key_exists('attributes', $role) &&
|
|
||||||
array_key_exists('role', $role['attributes']) &&
|
|
||||||
$role['attributes']['role'] !== NULL
|
|
||||||
) {
|
|
||||||
$count++;
|
|
||||||
} */
|
|
||||||
}
|
|
||||||
|
|
||||||
return $count;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected 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)
|
|
||||||
{
|
|
||||||
foreach($role['relationships']['person']['people'] as $pid => $peoples)
|
|
||||||
{
|
|
||||||
$p = $peoples;
|
|
||||||
}
|
|
||||||
|
|
||||||
$person = $p['attributes'];
|
|
||||||
$person['id'] = $pid;
|
|
||||||
$person['image'] = $person['image']['original'];
|
|
||||||
|
|
||||||
uasort($role['relationships']['media']['anime'], function ($a, $b) {
|
|
||||||
return $a['attributes']['canonicalTitle'] <=> $b['attributes']['canonicalTitle'];
|
|
||||||
});
|
|
||||||
|
|
||||||
$item = [
|
|
||||||
'person' => $person,
|
|
||||||
'series' => $role['relationships']['media']['anime']
|
|
||||||
];
|
|
||||||
|
|
||||||
$output[$roleName][$language][] = $item;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach($role['relationships']['person']['people'] as $pid => $person)
|
|
||||||
{
|
|
||||||
$person['id'] = $pid;
|
|
||||||
$output[$roleName][$pid] = $person;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $output;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,198 +1,195 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
/**
|
/**
|
||||||
* Hummingbird Anime List Client
|
* Hummingbird Anime List Client
|
||||||
*
|
*
|
||||||
* An API client for Kitsu to manage anime and manga watch lists
|
* An API client for Kitsu to manage anime and manga watch lists
|
||||||
*
|
*
|
||||||
* PHP version 7.1
|
* PHP version 7.1
|
||||||
*
|
*
|
||||||
* @package HummingbirdAnimeClient
|
* @package HummingbirdAnimeClient
|
||||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
* @copyright 2015 - 2018 Timothy J. Warren
|
* @copyright 2015 - 2018 Timothy J. Warren
|
||||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
* @version 4.1
|
* @version 4.1
|
||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Aviat\AnimeClient\Controller;
|
namespace Aviat\AnimeClient\Controller;
|
||||||
|
|
||||||
use function Aviat\AnimeClient\createPlaceholderImage;
|
use function Amp\Promise\wait;
|
||||||
use function Amp\Promise\wait;
|
use function Aviat\AnimeClient\getResponse;
|
||||||
|
use function Aviat\AnimeClient\createPlaceholderImage;
|
||||||
use Aviat\AnimeClient\Controller as BaseController;
|
|
||||||
use Aviat\AnimeClient\API\{HummingbirdClient, JsonAPI};
|
use Aviat\AnimeClient\Controller as BaseController;
|
||||||
use Aviat\Ion\Di\ContainerInterface;
|
|
||||||
use Aviat\Ion\View\HtmlView;
|
/**
|
||||||
|
* Controller for handling routes that don't fit elsewhere
|
||||||
/**
|
*/
|
||||||
* Controller for handling routes that don't fit elsewhere
|
final class Images extends BaseController {
|
||||||
*/
|
/**
|
||||||
final class Images extends BaseController {
|
* Get image covers from kitsu
|
||||||
/**
|
*
|
||||||
* Get image covers from kitsu
|
* @param string $type The category of image
|
||||||
*
|
* @param string $file The filename to look for
|
||||||
* @param string $type The category of image
|
* @param bool $display Whether to output the image to the server
|
||||||
* @param string $file The filename to look for
|
* @throws \Aviat\Ion\Di\ContainerException
|
||||||
* @param bool $display Whether to output the image to the server
|
* @throws \Aviat\Ion\Di\NotFoundException
|
||||||
* @throws \Aviat\Ion\Di\ContainerException
|
* @throws \InvalidArgumentException
|
||||||
* @throws \Aviat\Ion\Di\NotFoundException
|
* @throws \TypeError
|
||||||
* @throws \InvalidArgumentException
|
* @throws \Error
|
||||||
* @throws \TypeError
|
* @throws \Throwable
|
||||||
* @throws \Error
|
* @return void
|
||||||
* @throws \Throwable
|
*/
|
||||||
* @return void
|
public function cache(string $type, string $file, $display = TRUE): void
|
||||||
*/
|
{
|
||||||
public function cache(string $type, string $file, $display = TRUE): void
|
$currentUrl = $this->request->getUri()->__toString();
|
||||||
{
|
|
||||||
$currentUrl = $this->request->getUri()->__toString();
|
$kitsuUrl = 'https://media.kitsu.io/';
|
||||||
|
$fileName = str_replace('-original', '', $file);
|
||||||
$kitsuUrl = 'https://media.kitsu.io/';
|
[$id, $ext] = explode('.', basename($fileName));
|
||||||
$fileName = str_replace('-original', '', $file);
|
|
||||||
[$id, $ext] = explode('.', basename($fileName));
|
$baseSavePath = $this->config->get('img_cache_path');
|
||||||
|
|
||||||
$baseSavePath = $this->config->get('img_cache_path');
|
// Kitsu doesn't serve webp, but for most use cases,
|
||||||
|
// jpg is a safe assumption
|
||||||
// Kitsu doesn't serve webp, but for most use cases,
|
$tryJpg = ['anime','characters','manga','people'];
|
||||||
// jpg is a safe assumption
|
if ($ext === 'webp' && \in_array($type, $tryJpg, TRUE))
|
||||||
$tryJpg = ['anime','characters','manga','people'];
|
{
|
||||||
if ($ext === 'webp' && in_array($type, $tryJpg, TRUE))
|
$ext = 'jpg';
|
||||||
{
|
$currentUrl = str_replace('webp', 'jpg', $currentUrl);
|
||||||
$ext = 'jpg';
|
}
|
||||||
$currentUrl = str_replace('webp', 'jpg', $currentUrl);
|
|
||||||
}
|
$typeMap = [
|
||||||
|
'anime' => [
|
||||||
$typeMap = [
|
'kitsuUrl' => "anime/poster_images/{$id}/medium.{$ext}",
|
||||||
'anime' => [
|
'width' => 220,
|
||||||
'kitsuUrl' => "anime/poster_images/{$id}/medium.{$ext}",
|
'height' => 312,
|
||||||
'width' => 220,
|
],
|
||||||
'height' => 312,
|
'avatars' => [
|
||||||
],
|
'kitsuUrl' => "users/avatars/{$id}/original.{$ext}",
|
||||||
'avatars' => [
|
'width' => null,
|
||||||
'kitsuUrl' => "users/avatars/{$id}/original.{$ext}",
|
'height' => null,
|
||||||
'width' => null,
|
],
|
||||||
'height' => null,
|
'characters' => [
|
||||||
],
|
'kitsuUrl' => "characters/images/{$id}/original.{$ext}",
|
||||||
'characters' => [
|
'width' => 225,
|
||||||
'kitsuUrl' => "characters/images/{$id}/original.{$ext}",
|
'height' => 350,
|
||||||
'width' => 225,
|
],
|
||||||
'height' => 350,
|
'manga' => [
|
||||||
],
|
'kitsuUrl' => "manga/poster_images/{$id}/medium.{$ext}",
|
||||||
'manga' => [
|
'width' => 220,
|
||||||
'kitsuUrl' => "manga/poster_images/{$id}/medium.{$ext}",
|
'height' => 312,
|
||||||
'width' => 220,
|
],
|
||||||
'height' => 312,
|
'people' => [
|
||||||
],
|
'kitsuUrl' => "people/images/{$id}/original.{$ext}",
|
||||||
'people' => [
|
'width' => null,
|
||||||
'kitsuUrl' => "people/images/{$id}/original.{$ext}",
|
'height' => null,
|
||||||
'width' => null,
|
],
|
||||||
'height' => null,
|
];
|
||||||
],
|
|
||||||
];
|
$imageType = $typeMap[$type] ?? NULL;
|
||||||
|
|
||||||
$imageType = $typeMap[$type] ?? NULL;
|
if (NULL === $imageType)
|
||||||
|
{
|
||||||
if (NULL === $imageType)
|
$this->getPlaceholder($baseSavePath, 200, 200);
|
||||||
{
|
return;
|
||||||
$this->getPlaceholder($baseSavePath, 200, 200);
|
}
|
||||||
return;
|
|
||||||
}
|
$kitsuUrl .= $imageType['kitsuUrl'];
|
||||||
|
$width = $imageType['width'];
|
||||||
$kitsuUrl .= $imageType['kitsuUrl'];
|
$height = $imageType['height'];
|
||||||
$width = $imageType['width'];
|
$filePrefix = "{$baseSavePath}/{$type}/{$id}";
|
||||||
$height = $imageType['height'];
|
|
||||||
$filePrefix = "{$baseSavePath}/{$type}/{$id}";
|
$response = getResponse($kitsuUrl);
|
||||||
|
|
||||||
$promise = (new HummingbirdClient)->request($kitsuUrl);
|
if ($response->getStatus() !== 200)
|
||||||
$response = wait($promise);
|
{
|
||||||
|
// Try a few different file types before giving up
|
||||||
if ($response->getStatus() !== 200)
|
// webm => jpg => png => gif
|
||||||
{
|
$nextType = [
|
||||||
// Try a few different file types before giving up
|
'jpg' => 'png',
|
||||||
// webm => jpg => png => gif
|
'png' => 'gif',
|
||||||
$nextType = [
|
];
|
||||||
'jpg' => 'png',
|
|
||||||
'png' => 'gif',
|
if (array_key_exists($ext, $nextType))
|
||||||
];
|
{
|
||||||
|
$newUrl = str_replace($ext, $nextType[$ext], $currentUrl);
|
||||||
if (array_key_exists($ext, $nextType))
|
$this->redirect($newUrl, 303);
|
||||||
{
|
return;
|
||||||
$newUrl = str_replace($ext, $nextType[$ext], $currentUrl);
|
}
|
||||||
$this->redirect($newUrl, 303);
|
|
||||||
return;
|
if ($display)
|
||||||
}
|
{
|
||||||
|
$this->getPlaceholder("{$baseSavePath}/{$type}", $width, $height);
|
||||||
if ($display)
|
}
|
||||||
{
|
else
|
||||||
$this->getPlaceholder("{$baseSavePath}/{$type}", $width, $height);
|
{
|
||||||
}
|
createPlaceholderImage("{$baseSavePath}/{$type}", $width, $height);
|
||||||
else
|
}
|
||||||
{
|
return;
|
||||||
createPlaceholderImage("{$baseSavePath}/{$type}", $width, $height);
|
}
|
||||||
}
|
|
||||||
return;
|
$data = wait($response->getBody());
|
||||||
}
|
|
||||||
|
|
||||||
$data = wait($response->getBody());
|
|
||||||
|
[$origWidth] = getimagesizefromstring($data);
|
||||||
|
$gdImg = imagecreatefromstring($data);
|
||||||
|
$resizedImg = imagescale($gdImg, $width ?? $origWidth);
|
||||||
[$origWidth] = getimagesizefromstring($data);
|
|
||||||
$gdImg = imagecreatefromstring($data);
|
if ($ext === 'gif')
|
||||||
$resizedImg = imagescale($gdImg, $width ?? $origWidth);
|
{
|
||||||
|
file_put_contents("{$filePrefix}.gif", $data);
|
||||||
if ($ext === 'gif')
|
\imagepalletetotruecolor($gdImg);
|
||||||
{
|
}
|
||||||
file_put_contents("{$filePrefix}.gif", $data);
|
|
||||||
imagepalletetotruecolor($gdImg);
|
// save the webp versions
|
||||||
}
|
imagewebp($gdImg, "{$filePrefix}-original.webp");
|
||||||
|
imagewebp($resizedImg, "{$filePrefix}.webp");
|
||||||
// save the webp versions
|
|
||||||
imagewebp($gdImg, "{$filePrefix}-original.webp");
|
// save the scaled jpeg file
|
||||||
imagewebp($resizedImg, "{$filePrefix}.webp");
|
imagejpeg($resizedImg, "{$filePrefix}.jpg");
|
||||||
|
|
||||||
// save the scaled jpeg file
|
// And the original
|
||||||
imagejpeg($resizedImg, "{$filePrefix}.jpg");
|
file_put_contents("{$filePrefix}-original.jpg", $data);
|
||||||
|
|
||||||
// And the original
|
imagedestroy($gdImg);
|
||||||
file_put_contents("{$filePrefix}-original.jpg", $data);
|
imagedestroy($resizedImg);
|
||||||
|
|
||||||
imagedestroy($gdImg);
|
if ($display)
|
||||||
imagedestroy($resizedImg);
|
{
|
||||||
|
$contentType = ($ext === 'webp')
|
||||||
if ($display)
|
? 'image/webp'
|
||||||
{
|
: $response->getHeader('content-type')[0];
|
||||||
$contentType = ($ext === 'webp')
|
|
||||||
? "image/webp"
|
$outputFile = (strpos($file, '-original') !== FALSE)
|
||||||
: $response->getHeader('content-type')[0];
|
? "{$filePrefix}-original.{$ext}"
|
||||||
|
: "{$filePrefix}.{$ext}";
|
||||||
$outputFile = (strpos($file, '-original') !== FALSE)
|
|
||||||
? "{$filePrefix}-original.{$ext}"
|
header("Content-Type: {$contentType}");
|
||||||
: "{$filePrefix}.{$ext}";
|
echo file_get_contents($outputFile);
|
||||||
|
}
|
||||||
header("Content-Type: {$contentType}");
|
}
|
||||||
echo file_get_contents($outputFile);
|
|
||||||
}
|
/**
|
||||||
}
|
* Get a placeholder for a missing image
|
||||||
|
*
|
||||||
/**
|
* @param string $path
|
||||||
* Get a placeholder for a missing image
|
* @param int|null $width
|
||||||
*
|
* @param int|null $height
|
||||||
* @param string $path
|
*/
|
||||||
* @param int|null $width
|
private function getPlaceholder (string $path, ?int $width = 200, ?int $height = NULL): void
|
||||||
* @param int|null $height
|
{
|
||||||
*/
|
$height = $height ?? $width;
|
||||||
private function getPlaceholder (string $path, ?int $width = 200, ?int $height = NULL): void
|
|
||||||
{
|
$filename = $path . '/placeholder.png';
|
||||||
$height = $height ?? $width;
|
|
||||||
|
if ( ! file_exists($path . '/placeholder.png'))
|
||||||
$filename = $path . '/placeholder.png';
|
{
|
||||||
|
createPlaceholderImage($path, $width, $height);
|
||||||
if ( ! file_exists($path . '/placeholder.png'))
|
}
|
||||||
{
|
|
||||||
createPlaceholderImage($path, $width, $height);
|
header('Content-Type: image/png');
|
||||||
}
|
echo file_get_contents($filename);
|
||||||
|
}
|
||||||
header('Content-Type: image/png');
|
|
||||||
echo file_get_contents($filename);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -294,65 +294,6 @@ final class Manga extends Controller {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists('mediaCharacters', $data['included']))
|
|
||||||
{
|
|
||||||
$mediaCharacters = $data['included']['mediaCharacters'];
|
|
||||||
|
|
||||||
foreach ($mediaCharacters as $rel)
|
|
||||||
{
|
|
||||||
// dd($rel);
|
|
||||||
// $charId = $rel['relationships']['character']['data']['id'];
|
|
||||||
$role = $rel['attributes']['role'];
|
|
||||||
|
|
||||||
foreach($rel['relationships']['character']['characters'] as $charId => $char)
|
|
||||||
{
|
|
||||||
if (array_key_exists($charId, $data['included']['characters']))
|
|
||||||
{
|
|
||||||
$characters[$role][$charId] = $char['attributes'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists('mediaStaff', $data['included']))
|
|
||||||
{
|
|
||||||
foreach ($data['included']['mediaStaff'] as $id => $staffing)
|
|
||||||
{
|
|
||||||
$role = $staffing['attributes']['role'];
|
|
||||||
|
|
||||||
foreach($staffing['relationships']['person']['people'] as $personId => $personDetails)
|
|
||||||
{
|
|
||||||
if ( ! array_key_exists($role, $staff))
|
|
||||||
{
|
|
||||||
$staff[$role] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$staff[$role][$personId] = [
|
|
||||||
'id' => $personId,
|
|
||||||
'name' => $personDetails['attributes']['name'] ?? '??',
|
|
||||||
'image' => $personDetails['attributes']['image'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! empty($characters['main']))
|
|
||||||
{
|
|
||||||
uasort($characters['main'], function ($a, $b) {
|
|
||||||
return $a['name'] <=> $b['name'];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! empty($characters['supporting']))
|
|
||||||
{
|
|
||||||
uasort($characters['supporting'], function ($a, $b) {
|
|
||||||
return $a['name'] <=> $b['name'];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ksort($characters);
|
|
||||||
ksort($staff);
|
|
||||||
|
|
||||||
$this->outputHTML('manga/details', [
|
$this->outputHTML('manga/details', [
|
||||||
'title' => $this->formatTitle(
|
'title' => $this->formatTitle(
|
||||||
$this->config->get('whose_list') . "'s Manga List",
|
$this->config->get('whose_list') . "'s Manga List",
|
||||||
|
@ -138,7 +138,7 @@ final class MangaCollection extends BaseController {
|
|||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function edit()
|
public function edit(): void
|
||||||
{
|
{
|
||||||
$data = $this->request->getParsedBody();
|
$data = $this->request->getParsedBody();
|
||||||
if (array_key_exists('hummingbird_id', $data))
|
if (array_key_exists('hummingbird_id', $data))
|
||||||
@ -162,7 +162,7 @@ final class MangaCollection extends BaseController {
|
|||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function add()
|
public function add(): void
|
||||||
{
|
{
|
||||||
$data = $this->request->getParsedBody();
|
$data = $this->request->getParsedBody();
|
||||||
if (array_key_exists('id', $data))
|
if (array_key_exists('id', $data))
|
||||||
@ -183,7 +183,7 @@ final class MangaCollection extends BaseController {
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function delete()
|
public function delete(): void
|
||||||
{
|
{
|
||||||
$data = $this->request->getParsedBody();
|
$data = $this->request->getParsedBody();
|
||||||
if ( ! array_key_exists('hummingbird_id', $data))
|
if ( ! array_key_exists('hummingbird_id', $data))
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
namespace Aviat\AnimeClient\Controller;
|
namespace Aviat\AnimeClient\Controller;
|
||||||
|
|
||||||
use Aviat\AnimeClient\Controller as BaseController;
|
use Aviat\AnimeClient\Controller as BaseController;
|
||||||
use Aviat\Ion\Di\ContainerInterface;
|
|
||||||
use Aviat\Ion\View\HtmlView;
|
use Aviat\Ion\View\HtmlView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,7 +28,7 @@ final class Misc extends BaseController {
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function clearCache()
|
public function clearCache(): void
|
||||||
{
|
{
|
||||||
$this->cache->clear();
|
$this->cache->clear();
|
||||||
$this->outputHTML('blank', [
|
$this->outputHTML('blank', [
|
||||||
@ -43,7 +42,7 @@ final class Misc extends BaseController {
|
|||||||
* @param string $status
|
* @param string $status
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function login(string $status = '')
|
public function login(string $status = ''): void
|
||||||
{
|
{
|
||||||
$message = '';
|
$message = '';
|
||||||
|
|
||||||
@ -68,7 +67,7 @@ final class Misc extends BaseController {
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function loginAction()
|
public function loginAction(): void
|
||||||
{
|
{
|
||||||
$auth = $this->container->get('auth');
|
$auth = $this->container->get('auth');
|
||||||
$post = $this->request->getParsedBody();
|
$post = $this->request->getParsedBody();
|
||||||
@ -88,7 +87,7 @@ final class Misc extends BaseController {
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function logout()
|
public function logout(): void
|
||||||
{
|
{
|
||||||
$auth = $this->container->get('auth');
|
$auth = $this->container->get('auth');
|
||||||
$auth->logout();
|
$auth->logout();
|
||||||
|
@ -17,12 +17,33 @@
|
|||||||
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\AnimeClient\API\Kitsu\Transformer\PersonTransformer;
|
||||||
|
|
||||||
|
use Aviat\Ion\Di\ContainerInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller for People pages
|
* Controller for People pages
|
||||||
*/
|
*/
|
||||||
final class People extends BaseController {
|
final class People extends BaseController {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Aviat\AnimeClient\API\Kitsu\Model
|
||||||
|
*/
|
||||||
|
private $model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* People constructor.
|
||||||
|
*
|
||||||
|
* @param ContainerInterface $container
|
||||||
|
* @throws \Aviat\Ion\Di\Exception\ContainerException
|
||||||
|
* @throws \Aviat\Ion\Di\Exception\NotFoundException
|
||||||
|
*/
|
||||||
|
public function __construct(ContainerInterface $container)
|
||||||
|
{
|
||||||
|
parent::__construct($container);
|
||||||
|
$this->model = $container->get('kitsu-model');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show information about a person
|
* Show information about a person
|
||||||
*
|
*
|
||||||
@ -31,9 +52,8 @@ final class People extends BaseController {
|
|||||||
*/
|
*/
|
||||||
public function index(string $id): void
|
public function index(string $id): void
|
||||||
{
|
{
|
||||||
$model = $this->container->get('kitsu-model');
|
$rawData = $this->model->getPerson($id);
|
||||||
|
$data = (new PersonTransformer())->transform($rawData)->toArray();
|
||||||
$rawData = $model->getPerson($id);
|
|
||||||
|
|
||||||
if (( ! array_key_exists('data', $rawData)) || empty($rawData['data']))
|
if (( ! array_key_exists('data', $rawData)) || empty($rawData['data']))
|
||||||
{
|
{
|
||||||
@ -48,114 +68,12 @@ final class People extends BaseController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = JsonAPI::organizeData($rawData);
|
$this->outputHTML('person/details', [
|
||||||
$included = JsonAPI::organizeIncludes($rawData['included']);
|
|
||||||
|
|
||||||
$orgData = $this->organizeData($included);
|
|
||||||
|
|
||||||
$viewData = [
|
|
||||||
'included' => $included,
|
|
||||||
'title' => $this->formatTitle(
|
'title' => $this->formatTitle(
|
||||||
'People',
|
'People',
|
||||||
$data['attributes']['name']
|
$data['name']
|
||||||
),
|
),
|
||||||
'data' => $data,
|
'data' => $data,
|
||||||
'castCount' => 0,
|
]);
|
||||||
'castings' => [],
|
|
||||||
'characters' => $orgData['characters'],
|
|
||||||
'staff' => $orgData['staff'],
|
|
||||||
];
|
|
||||||
|
|
||||||
$this->outputHTML('person/details', $viewData);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function organizeData(array $data): array
|
|
||||||
{
|
|
||||||
$output = [
|
|
||||||
'characters' => [
|
|
||||||
'main' => [],
|
|
||||||
'supporting' => [],
|
|
||||||
],
|
|
||||||
'staff' => [],
|
|
||||||
];
|
|
||||||
|
|
||||||
if (array_key_exists('characterVoices', $data))
|
|
||||||
{
|
|
||||||
foreach ($data['characterVoices'] as $cv)
|
|
||||||
{
|
|
||||||
$mcId = $cv['relationships']['mediaCharacter']['data']['id'];
|
|
||||||
|
|
||||||
if ( ! array_key_exists($mcId, $data['mediaCharacters']))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$mc = $data['mediaCharacters'][$mcId];
|
|
||||||
|
|
||||||
$role = $mc['role'];
|
|
||||||
|
|
||||||
$charId = $mc['relationships']['character']['data']['id'];
|
|
||||||
$mediaId = $mc['relationships']['media']['data']['id'];
|
|
||||||
|
|
||||||
$existingMedia = array_key_exists($charId, $output['characters'][$role])
|
|
||||||
? $output['characters'][$role][$charId]['media']
|
|
||||||
: [];
|
|
||||||
|
|
||||||
$relatedMedia = [
|
|
||||||
$mediaId => $data['anime'][$mediaId],
|
|
||||||
];
|
|
||||||
|
|
||||||
$includedMedia = array_replace_recursive($existingMedia, $relatedMedia);
|
|
||||||
|
|
||||||
uasort($includedMedia, function ($a, $b) {
|
|
||||||
return $a['canonicalTitle'] <=> $b['canonicalTitle'];
|
|
||||||
});
|
|
||||||
|
|
||||||
$character = $data['characters'][$charId];
|
|
||||||
|
|
||||||
$output['characters'][$role][$charId] = [
|
|
||||||
'character' => $character,
|
|
||||||
'media' => $includedMedia,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists('mediaStaff', $data))
|
|
||||||
{
|
|
||||||
foreach($data['mediaStaff'] as $rid => $role)
|
|
||||||
{
|
|
||||||
$roleName = $role['role'];
|
|
||||||
$mediaType = $role['relationships']['media']['data']['type'];
|
|
||||||
$mediaId = $role['relationships']['media']['data']['id'];
|
|
||||||
$media = $data[$mediaType][$mediaId];
|
|
||||||
$output['staff'][$roleName][$mediaType][$mediaId] = $media;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uasort($output['characters']['main'], function ($a, $b) {
|
|
||||||
return $a['character']['canonicalName'] <=> $b['character']['canonicalName'];
|
|
||||||
});
|
|
||||||
uasort($output['characters']['supporting'], function ($a, $b) {
|
|
||||||
return $a['character']['canonicalName'] <=> $b['character']['canonicalName'];
|
|
||||||
});
|
|
||||||
ksort($output['staff']);
|
|
||||||
foreach($output['staff'] as $role => &$media)
|
|
||||||
{
|
|
||||||
if (array_key_exists('anime', $media))
|
|
||||||
{
|
|
||||||
uasort($media['anime'], function ($a, $b) {
|
|
||||||
return $a['canonicalTitle'] <=> $b['canonicalTitle'];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists('manga', $media))
|
|
||||||
{
|
|
||||||
uasort($media['manga'], function ($a, $b) {
|
|
||||||
return $a['canonicalTitle'] <=> $b['canonicalTitle'];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $output;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -24,7 +24,7 @@ use Aviat\Ion\Di\ContainerInterface;
|
|||||||
*/
|
*/
|
||||||
final class Settings extends BaseController {
|
final class Settings extends BaseController {
|
||||||
/**
|
/**
|
||||||
* @var \Aviat\API\Anilist\Model
|
* @var \Aviat\AnimeClient\API\Anilist\Model
|
||||||
*/
|
*/
|
||||||
private $anilistModel;
|
private $anilistModel;
|
||||||
|
|
||||||
@ -33,6 +33,13 @@ final class Settings extends BaseController {
|
|||||||
*/
|
*/
|
||||||
private $settingsModel;
|
private $settingsModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Settings constructor.
|
||||||
|
*
|
||||||
|
* @param ContainerInterface $container
|
||||||
|
* @throws \Aviat\Ion\Di\Exception\ContainerException
|
||||||
|
* @throws \Aviat\Ion\Di\Exception\NotFoundException
|
||||||
|
*/
|
||||||
public function __construct(ContainerInterface $container)
|
public function __construct(ContainerInterface $container)
|
||||||
{
|
{
|
||||||
parent::__construct($container);
|
parent::__construct($container);
|
||||||
@ -44,7 +51,7 @@ final class Settings extends BaseController {
|
|||||||
/**
|
/**
|
||||||
* Show the user settings, if logged in
|
* Show the user settings, if logged in
|
||||||
*/
|
*/
|
||||||
public function index()
|
public function index(): void
|
||||||
{
|
{
|
||||||
$auth = $this->container->get('auth');
|
$auth = $this->container->get('auth');
|
||||||
$form = $this->settingsModel->getSettingsForm();
|
$form = $this->settingsModel->getSettingsForm();
|
||||||
@ -66,7 +73,7 @@ final class Settings extends BaseController {
|
|||||||
*
|
*
|
||||||
* @throws \Aura\Router\Exception\RouteNotFound
|
* @throws \Aura\Router\Exception\RouteNotFound
|
||||||
*/
|
*/
|
||||||
public function update()
|
public function update(): void
|
||||||
{
|
{
|
||||||
$post = $this->request->getParsedBody();
|
$post = $this->request->getParsedBody();
|
||||||
unset($post['settings-tabs']);
|
unset($post['settings-tabs']);
|
||||||
@ -88,14 +95,15 @@ final class Settings extends BaseController {
|
|||||||
/**
|
/**
|
||||||
* Redirect to Anilist to start Oauth flow
|
* Redirect to Anilist to start Oauth flow
|
||||||
*/
|
*/
|
||||||
public function anilistRedirect()
|
public function anilistRedirect(): void
|
||||||
{
|
{
|
||||||
$redirectUrl = 'https://anilist.co/api/v2/oauth/authorize?' .
|
$query = http_build_query([
|
||||||
http_build_query([
|
'client_id' => $this->config->get(['anilist', 'client_id']),
|
||||||
'client_id' => $this->config->get(['anilist', 'client_id']),
|
'redirect_uri' => $this->urlGenerator->url('/anilist-oauth'),
|
||||||
'redirect_uri' => $this->urlGenerator->url('/anilist-oauth'),
|
'response_type' => 'code',
|
||||||
'response_type' => 'code',
|
]);
|
||||||
]);
|
|
||||||
|
$redirectUrl = "https://anilist.co/api/v2/oauth/authorize?{$query}";
|
||||||
|
|
||||||
$this->redirect($redirectUrl, 303);
|
$this->redirect($redirectUrl, 303);
|
||||||
}
|
}
|
||||||
@ -103,7 +111,7 @@ final class Settings extends BaseController {
|
|||||||
/**
|
/**
|
||||||
* Oauth callback for Anilist API
|
* Oauth callback for Anilist API
|
||||||
*/
|
*/
|
||||||
public function anilistCallback()
|
public function anilistCallback(): void
|
||||||
{
|
{
|
||||||
$query = $this->request->getQueryParams();
|
$query = $this->request->getQueryParams();
|
||||||
$authCode = $query['code'];
|
$authCode = $query['code'];
|
||||||
|
@ -16,8 +16,9 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient\Controller;
|
namespace Aviat\AnimeClient\Controller;
|
||||||
|
|
||||||
|
use Aviat\AnimeClient\API\Kitsu\Transformer\UserTransformer;
|
||||||
use Aviat\AnimeClient\Controller as BaseController;
|
use Aviat\AnimeClient\Controller as BaseController;
|
||||||
use Aviat\AnimeClient\API\JsonAPI;
|
|
||||||
use Aviat\Ion\Di\ContainerInterface;
|
use Aviat\Ion\Di\ContainerInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,8 +26,18 @@ use Aviat\Ion\Di\ContainerInterface;
|
|||||||
*/
|
*/
|
||||||
final class User extends BaseController {
|
final class User extends BaseController {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Aviat\AnimeClient\API\Kitsu\Model
|
||||||
|
*/
|
||||||
private $kitsuModel;
|
private $kitsuModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User constructor.
|
||||||
|
*
|
||||||
|
* @param ContainerInterface $container
|
||||||
|
* @throws \Aviat\Ion\Di\Exception\ContainerException
|
||||||
|
* @throws \Aviat\Ion\Di\Exception\NotFoundException
|
||||||
|
*/
|
||||||
public function __construct(ContainerInterface $container)
|
public function __construct(ContainerInterface $container)
|
||||||
{
|
{
|
||||||
parent::__construct($container);
|
parent::__construct($container);
|
||||||
@ -56,103 +67,16 @@ final class User extends BaseController {
|
|||||||
? $this->config->get(['kitsu_username'])
|
? $this->config->get(['kitsu_username'])
|
||||||
: $username;
|
: $username;
|
||||||
|
|
||||||
$data = $this->kitsuModel->getUserData($username);
|
|
||||||
$orgData = JsonAPI::organizeData($data)[0];
|
|
||||||
$rels = $orgData['relationships'] ?? [];
|
|
||||||
$favorites = array_key_exists('favorites', $rels) ? $rels['favorites'] : [];
|
|
||||||
|
|
||||||
$stats = [];
|
|
||||||
foreach ($rels['stats'] as $sid => &$item)
|
|
||||||
{
|
|
||||||
$key = $item['attributes']['kind'];
|
|
||||||
$stats[$key] = $item['attributes']['statsData'];
|
|
||||||
unset($item);
|
|
||||||
}
|
|
||||||
|
|
||||||
//dump($orgData);
|
|
||||||
// dump($stats);
|
|
||||||
|
|
||||||
// $timeOnAnime = $this->formatAnimeTime($orgData['attributes']['lifeSpentOnAnime']);
|
|
||||||
$timeOnAnime = $this->formatAnimeTime($stats['anime-amount-consumed']['time']);
|
|
||||||
|
|
||||||
|
|
||||||
$whom = $isMainUser
|
$whom = $isMainUser
|
||||||
? $this->config->get('whose_list')
|
? $this->config->get('whose_list')
|
||||||
: $username;
|
: $username;
|
||||||
|
|
||||||
|
$rawData = $this->kitsuModel->getUserData($username);
|
||||||
|
$data = (new UserTransformer())->transform($rawData)->toArray();
|
||||||
|
|
||||||
$this->outputHTML('user/details', [
|
$this->outputHTML('user/details', [
|
||||||
'title' => 'About ' . $whom,
|
'title' => 'About ' . $whom,
|
||||||
'data' => $orgData,
|
'data' => $data,
|
||||||
'attributes' => $orgData['attributes'],
|
|
||||||
'relationships' => $rels,
|
|
||||||
'favorites' => $this->organizeFavorites($favorites),
|
|
||||||
'stats' => $stats,
|
|
||||||
'timeOnAnime' => $timeOnAnime,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reorganize favorites data to be more useful
|
|
||||||
*
|
|
||||||
* @param array $rawFavorites
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function organizeFavorites(array $rawFavorites): array
|
|
||||||
{
|
|
||||||
$output = [];
|
|
||||||
|
|
||||||
unset($rawFavorites['data']);
|
|
||||||
|
|
||||||
foreach ($rawFavorites as $item)
|
|
||||||
{
|
|
||||||
$rank = $item['attributes']['favRank'];
|
|
||||||
foreach ($item['relationships']['item'] as $key => $fav)
|
|
||||||
{
|
|
||||||
$output[$key] = $output[$key] ?? [];
|
|
||||||
foreach ($fav as $id => $data)
|
|
||||||
{
|
|
||||||
$output[$key][$rank] = array_merge(['id' => $id], $data['attributes']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ksort($output[$key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $output;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format the time spent on anime in a more readable format
|
|
||||||
*
|
|
||||||
* @param int $minutes
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function formatAnimeTime(int $minutes): string
|
|
||||||
{
|
|
||||||
$minutesPerDay = 1440;
|
|
||||||
$minutesPerYear = $minutesPerDay * 365;
|
|
||||||
|
|
||||||
// Minutes short of a year
|
|
||||||
$years = (int)floor($minutes / $minutesPerYear);
|
|
||||||
$minutes %= $minutesPerYear;
|
|
||||||
|
|
||||||
// Minutes short of a day
|
|
||||||
$extraMinutes = $minutes % $minutesPerDay;
|
|
||||||
|
|
||||||
$days = ($minutes - $extraMinutes) / $minutesPerDay;
|
|
||||||
|
|
||||||
// Minutes short of an hour
|
|
||||||
$remMinutes = $extraMinutes % 60;
|
|
||||||
|
|
||||||
$hours = ($extraMinutes - $remMinutes) / 60;
|
|
||||||
|
|
||||||
$output = "{$days} days, {$hours} hours, and {$remMinutes} minutes.";
|
|
||||||
|
|
||||||
if ($years > 0)
|
|
||||||
{
|
|
||||||
$output = "{$years} year(s),{$output}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $output;
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -64,8 +64,9 @@ final class Dispatcher extends RoutingBase {
|
|||||||
public function __construct(ContainerInterface $container)
|
public function __construct(ContainerInterface $container)
|
||||||
{
|
{
|
||||||
parent::__construct($container);
|
parent::__construct($container);
|
||||||
$this->router = $container->get('aura-router')->getMap();
|
$router = $this->container->get('aura-router');
|
||||||
$this->matcher = $container->get('aura-router')->getMatcher();
|
$this->router = $router->getMap();
|
||||||
|
$this->matcher = $router->getMatcher();
|
||||||
$this->request = $container->get('request');
|
$this->request = $container->get('request');
|
||||||
|
|
||||||
$this->outputRoutes = $this->setupRoutes();
|
$this->outputRoutes = $this->setupRoutes();
|
||||||
@ -99,7 +100,7 @@ final class Dispatcher extends RoutingBase {
|
|||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getOutputRoutes()
|
public function getOutputRoutes(): array
|
||||||
{
|
{
|
||||||
return $this->outputRoutes;
|
return $this->outputRoutes;
|
||||||
}
|
}
|
||||||
@ -171,7 +172,7 @@ final class Dispatcher extends RoutingBase {
|
|||||||
$controllerName = $map[$controllerName];
|
$controllerName = $map[$controllerName];
|
||||||
}
|
}
|
||||||
|
|
||||||
$actionMethod = (array_key_exists('action', $route->attributes))
|
$actionMethod = array_key_exists('action', $route->attributes)
|
||||||
? $route->attributes['action']
|
? $route->attributes['action']
|
||||||
: NOT_FOUND_METHOD;
|
: NOT_FOUND_METHOD;
|
||||||
|
|
||||||
@ -205,9 +206,9 @@ final class Dispatcher extends RoutingBase {
|
|||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getController()
|
public function getController(): string
|
||||||
{
|
{
|
||||||
$routeType = $this->__get('default_list');
|
$routeType = $this->config->get('default_list');
|
||||||
$requestUri = $this->request->getUri()->getPath();
|
$requestUri = $this->request->getUri()->getPath();
|
||||||
$path = trim($requestUri, '/');
|
$path = trim($requestUri, '/');
|
||||||
|
|
||||||
@ -225,7 +226,7 @@ final class Dispatcher extends RoutingBase {
|
|||||||
$controller = $routeType;
|
$controller = $routeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $controller;
|
return $controller ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -233,11 +234,13 @@ final class Dispatcher extends RoutingBase {
|
|||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getControllerList()
|
public function getControllerList(): array
|
||||||
{
|
{
|
||||||
$defaultNamespace = DEFAULT_CONTROLLER_NAMESPACE;
|
$defaultNamespace = DEFAULT_CONTROLLER_NAMESPACE;
|
||||||
$path = str_replace('\\', '/', $defaultNamespace);
|
$find = ['\\', 'Aviat/AnimeClient/'];
|
||||||
$path = str_replace('Aviat/AnimeClient/', '', $path);
|
$replace = ['/', ''];
|
||||||
|
|
||||||
|
$path = str_replace($find, $replace, $defaultNamespace);
|
||||||
$path = trim($path, '/');
|
$path = trim($path, '/');
|
||||||
$actualPath = realpath(_dir(SRC_DIR, $path));
|
$actualPath = realpath(_dir(SRC_DIR, $path));
|
||||||
$classFiles = glob("{$actualPath}/*.php");
|
$classFiles = glob("{$actualPath}/*.php");
|
||||||
@ -265,7 +268,7 @@ final class Dispatcher extends RoutingBase {
|
|||||||
* @param array $params
|
* @param array $params
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function call($controllerName, $method, array $params)
|
protected function call($controllerName, $method, array $params): void
|
||||||
{
|
{
|
||||||
$logger = $this->container->getLogger('default');
|
$logger = $this->container->getLogger('default');
|
||||||
|
|
||||||
@ -347,7 +350,7 @@ final class Dispatcher extends RoutingBase {
|
|||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function setupRoutes()
|
protected function setupRoutes(): array
|
||||||
{
|
{
|
||||||
$routeType = $this->getController();
|
$routeType = $this->getController();
|
||||||
|
|
||||||
@ -359,7 +362,7 @@ final class Dispatcher extends RoutingBase {
|
|||||||
unset($route['path']);
|
unset($route['path']);
|
||||||
|
|
||||||
$controllerMap = $this->getControllerList();
|
$controllerMap = $this->getControllerList();
|
||||||
$controllerClass = (array_key_exists($routeType, $controllerMap))
|
$controllerClass = array_key_exists($routeType, $controllerMap)
|
||||||
? $controllerMap[$routeType]
|
? $controllerMap[$routeType]
|
||||||
: DEFAULT_CONTROLLER;
|
: DEFAULT_CONTROLLER;
|
||||||
|
|
||||||
|
@ -16,35 +16,28 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient;
|
namespace Aviat\AnimeClient;
|
||||||
|
|
||||||
use Aviat\Ion\
|
|
||||||
{
|
|
||||||
ArrayWrapper, StringWrapper
|
|
||||||
};
|
|
||||||
use Aviat\Ion\Di\ContainerInterface;
|
use Aviat\Ion\Di\ContainerInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper object to manage form generation, especially for config editing
|
* Helper object to manage form generation, especially for config editing
|
||||||
*/
|
*/
|
||||||
final class FormGenerator {
|
final class FormGenerator {
|
||||||
use ArrayWrapper;
|
|
||||||
use StringWrapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Injection Container
|
|
||||||
* @var ContainerInterface $container
|
|
||||||
*/
|
|
||||||
protected $container;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Html generation helper
|
* Html generation helper
|
||||||
*
|
*
|
||||||
* @var \Aura\Html\HelperLocator
|
* @var \Aura\Html\HelperLocator
|
||||||
*/
|
*/
|
||||||
protected $helper;
|
private $helper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FormGenerator constructor.
|
||||||
|
*
|
||||||
|
* @param ContainerInterface $container
|
||||||
|
* @throws \Aviat\Ion\Di\Exception\ContainerException
|
||||||
|
* @throws \Aviat\Ion\Di\Exception\NotFoundException
|
||||||
|
*/
|
||||||
public function __construct(ContainerInterface $container)
|
public function __construct(ContainerInterface $container)
|
||||||
{
|
{
|
||||||
$this->container = $container;
|
|
||||||
$this->helper = $container->get('html-helper');
|
$this->helper = $container->get('html-helper');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +48,7 @@ final class FormGenerator {
|
|||||||
* @param array $form
|
* @param array $form
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function generate(string $name, array $form)
|
public function generate(string $name, array $form): string
|
||||||
{
|
{
|
||||||
$type = $form['type'];
|
$type = $form['type'];
|
||||||
|
|
||||||
@ -105,6 +98,6 @@ final class FormGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->helper->input($params);
|
return (string)$this->helper->input($params);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -45,9 +45,11 @@ final class MenuGenerator extends UrlGenerator {
|
|||||||
protected $request;
|
protected $request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create menu generator
|
* MenuGenerator constructor.
|
||||||
*
|
*
|
||||||
* @param ContainerInterface $container
|
* @param ContainerInterface $container
|
||||||
|
* @throws \Aviat\Ion\Di\Exception\ContainerException
|
||||||
|
* @throws \Aviat\Ion\Di\Exception\NotFoundException
|
||||||
*/
|
*/
|
||||||
public function __construct(ContainerInterface $container)
|
public function __construct(ContainerInterface $container)
|
||||||
{
|
{
|
||||||
@ -106,7 +108,7 @@ final class MenuGenerator extends UrlGenerator {
|
|||||||
|
|
||||||
$link = $this->helper->a($this->url($path), $title);
|
$link = $this->helper->a($this->url($path), $title);
|
||||||
|
|
||||||
$attrs = ($selected)
|
$attrs = $selected
|
||||||
? ['class' => 'selected']
|
? ['class' => 'selected']
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
|
@ -16,14 +16,10 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient\Model;
|
namespace Aviat\AnimeClient\Model;
|
||||||
|
|
||||||
use Aviat\Ion\StringWrapper;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base model for api interaction
|
* Base model for api interaction
|
||||||
*/
|
*/
|
||||||
class API {
|
class API {
|
||||||
use StringWrapper;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sort the list entries by their title
|
* Sort the list entries by their title
|
||||||
*
|
*
|
||||||
@ -31,7 +27,7 @@ class API {
|
|||||||
* @param string $sortKey
|
* @param string $sortKey
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function sortByName(array &$array, string $sortKey)
|
protected function sortByName(array &$array, string $sortKey): void
|
||||||
{
|
{
|
||||||
$sort = [];
|
$sort = [];
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ class Anime extends API {
|
|||||||
* @param string $slug
|
* @param string $slug
|
||||||
* @return AnimeType
|
* @return AnimeType
|
||||||
*/
|
*/
|
||||||
public function getAnime(string $slug)
|
public function getAnime(string $slug): AnimeType
|
||||||
{
|
{
|
||||||
return $this->kitsuModel->getAnime($slug);
|
return $this->kitsuModel->getAnime($slug);
|
||||||
}
|
}
|
||||||
@ -147,7 +147,7 @@ class Anime extends API {
|
|||||||
$item = $this->kitsuModel->getListItem($itemId);
|
$item = $this->kitsuModel->getListItem($itemId);
|
||||||
$array = $item->toArray();
|
$array = $item->toArray();
|
||||||
|
|
||||||
if (is_array($array['notes']))
|
if (\is_array($array['notes']))
|
||||||
{
|
{
|
||||||
$array['notes'] = '';
|
$array['notes'] = '';
|
||||||
}
|
}
|
||||||
|
@ -226,6 +226,11 @@ final class AnimeCollection extends Collection {
|
|||||||
return $query->fetch(PDO::FETCH_ASSOC);
|
return $query->fetch(PDO::FETCH_ASSOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of genres from the database
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
private function getGenresForList(): array
|
private function getGenresForList(): array
|
||||||
{
|
{
|
||||||
$query = $this->db->select('hummingbird_id, genre')
|
$query = $this->db->select('hummingbird_id, genre')
|
||||||
|
@ -83,11 +83,11 @@ class Collection extends DB {
|
|||||||
|
|
||||||
if ( ! empty($filter))
|
if ( ! empty($filter))
|
||||||
{
|
{
|
||||||
$this->db->where_in('hummingbird_id', $filter);
|
$this->db->whereIn('hummingbird_id', $filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = $this->db->order_by('hummingbird_id')
|
$query = $this->db->orderBy('hummingbird_id')
|
||||||
->order_by('genre')
|
->orderBy('genre')
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$output = [];
|
$output = [];
|
||||||
|
@ -17,15 +17,12 @@
|
|||||||
namespace Aviat\AnimeClient\Model;
|
namespace Aviat\AnimeClient\Model;
|
||||||
|
|
||||||
use Aviat\Ion\Di\{ContainerAware, ContainerInterface};
|
use Aviat\Ion\Di\{ContainerAware, ContainerInterface};
|
||||||
use Aviat\Ion\{ArrayWrapper, StringWrapper};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base model for database interaction
|
* Base model for database interaction
|
||||||
*/
|
*/
|
||||||
class DB {
|
class DB {
|
||||||
use ArrayWrapper;
|
|
||||||
use ContainerAware;
|
use ContainerAware;
|
||||||
use StringWrapper;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The query builder object
|
* The query builder object
|
||||||
|
@ -41,7 +41,7 @@ final class Settings {
|
|||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSettings()
|
public function getSettings(): array
|
||||||
{
|
{
|
||||||
$settings = [
|
$settings = [
|
||||||
'config' => [],
|
'config' => [],
|
||||||
@ -66,7 +66,7 @@ final class Settings {
|
|||||||
return $settings;
|
return $settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSettingsForm()
|
public function getSettingsForm(): array
|
||||||
{
|
{
|
||||||
$output = [];
|
$output = [];
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ final class Settings {
|
|||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function validateSettings(array $settings)
|
public function validateSettings(array $settings): array
|
||||||
{
|
{
|
||||||
$config = (new Config($settings))->toArray();
|
$config = (new Config($settings))->toArray();
|
||||||
|
|
||||||
@ -150,7 +150,7 @@ final class Settings {
|
|||||||
$looseConfig[$key] = $val;
|
$looseConfig[$key] = $val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elseif (is_array($val) && ! empty($val))
|
elseif (\is_array($val) && ! empty($val))
|
||||||
{
|
{
|
||||||
foreach($val as $k => $v)
|
foreach($val as $k => $v)
|
||||||
{
|
{
|
||||||
@ -204,7 +204,6 @@ final class Settings {
|
|||||||
{
|
{
|
||||||
dump($e);
|
dump($e);
|
||||||
dump($settings);
|
dump($settings);
|
||||||
die();
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,20 +59,6 @@ class RoutingBase {
|
|||||||
$this->routes = $this->config->get('routes');
|
$this->routes = $this->config->get('routes');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the appropriate value for the routing key
|
|
||||||
*
|
|
||||||
* @param string|int|array $key
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function __get($key)
|
|
||||||
{
|
|
||||||
if ($this->config->has($key))
|
|
||||||
{
|
|
||||||
return $this->config->get($key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current url path
|
* Get the current url path
|
||||||
* @throws \Aviat\Ion\Di\ContainerException
|
* @throws \Aviat\Ion\Di\ContainerException
|
||||||
|
@ -17,9 +17,9 @@
|
|||||||
namespace Aviat\AnimeClient\Types;
|
namespace Aviat\AnimeClient\Types;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type representing an Anime object for display
|
* Type representing an anime within a watch list
|
||||||
*/
|
*/
|
||||||
final class Anime extends AbstractType {
|
class Anime extends AbstractType {
|
||||||
public $age_rating;
|
public $age_rating;
|
||||||
public $age_rating_guide;
|
public $age_rating_guide;
|
||||||
public $cover_image;
|
public $cover_image;
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
namespace Aviat\AnimeClient\Types;
|
namespace Aviat\AnimeClient\Types;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type representing an Anime object for display
|
* Type representing an anime watch list item
|
||||||
*/
|
*/
|
||||||
final class AnimeListItem extends AbstractType {
|
final class AnimeListItem extends AbstractType {
|
||||||
public $id;
|
public $id;
|
||||||
@ -40,4 +40,9 @@ final class AnimeListItem extends AbstractType {
|
|||||||
public $rewatched;
|
public $rewatched;
|
||||||
public $user_rating;
|
public $user_rating;
|
||||||
public $watching_status;
|
public $watching_status;
|
||||||
|
|
||||||
|
public function setAnime($anime): void
|
||||||
|
{
|
||||||
|
$this->anime = new Anime($anime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
25
src/Types/AnimePage.php
Normal file
25
src/Types/AnimePage.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Hummingbird Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 7.1
|
||||||
|
*
|
||||||
|
* @package HummingbirdAnimeClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2018 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 4.1
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\Types;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type representing an Anime object for a detail page
|
||||||
|
*/
|
||||||
|
final class AnimePage extends Anime {
|
||||||
|
public $characters;
|
||||||
|
public $staff;
|
||||||
|
}
|
39
src/Types/Character.php
Normal file
39
src/Types/Character.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Hummingbird Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 7.1
|
||||||
|
*
|
||||||
|
* @package HummingbirdAnimeClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2018 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 4.1
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\Types;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type representing a character for display
|
||||||
|
*/
|
||||||
|
final class Character extends AbstractType {
|
||||||
|
public $castings;
|
||||||
|
public $description;
|
||||||
|
public $id;
|
||||||
|
public $included;
|
||||||
|
public $media;
|
||||||
|
public $name;
|
||||||
|
public $names;
|
||||||
|
public $otherNames;
|
||||||
|
|
||||||
|
public function setMedia ($media): void
|
||||||
|
{
|
||||||
|
$this->media = new class($media) extends AbstractType {
|
||||||
|
public $anime;
|
||||||
|
public $manga;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -24,6 +24,7 @@ class Config extends AbstractType {
|
|||||||
|
|
||||||
// Settings in config.toml
|
// Settings in config.toml
|
||||||
public $asset_path; // Path to public folder for urls
|
public $asset_path; // Path to public folder for urls
|
||||||
|
public $dark_theme;
|
||||||
public $default_anime_list_path;
|
public $default_anime_list_path;
|
||||||
public $default_list;
|
public $default_list;
|
||||||
public $default_manga_list_path;
|
public $default_manga_list_path;
|
||||||
|
@ -20,12 +20,14 @@ namespace Aviat\AnimeClient\Types;
|
|||||||
* Type representing an Anime object for display
|
* Type representing an Anime object for display
|
||||||
*/
|
*/
|
||||||
final class MangaPage extends AbstractType {
|
final class MangaPage extends AbstractType {
|
||||||
|
public $characters;
|
||||||
public $chapter_count;
|
public $chapter_count;
|
||||||
public $cover_image;
|
public $cover_image;
|
||||||
public $genres;
|
public $genres;
|
||||||
public $id;
|
public $id;
|
||||||
public $included;
|
public $included;
|
||||||
public $manga_type;
|
public $manga_type;
|
||||||
|
public $staff;
|
||||||
public $synopsis;
|
public $synopsis;
|
||||||
public $title;
|
public $title;
|
||||||
public $titles;
|
public $titles;
|
||||||
|
35
src/Types/Person.php
Normal file
35
src/Types/Person.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Hummingbird Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 7.1
|
||||||
|
*
|
||||||
|
* @package HummingbirdAnimeClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2018 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 4.1
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\Types;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type representing a person for display
|
||||||
|
*/
|
||||||
|
final class Person extends AbstractType {
|
||||||
|
public $id;
|
||||||
|
public $name;
|
||||||
|
public $characters;
|
||||||
|
public $staff;
|
||||||
|
|
||||||
|
public function setCharacters($characters): void
|
||||||
|
{
|
||||||
|
$this->characters = new class($characters) extends AbstractType {
|
||||||
|
public $main;
|
||||||
|
public $supporting;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
32
src/Types/User.php
Normal file
32
src/Types/User.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Hummingbird Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 7.1
|
||||||
|
*
|
||||||
|
* @package HummingbirdAnimeClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2018 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 4.1
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\Types;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type representing a Kitsu user for display
|
||||||
|
*/
|
||||||
|
final class User extends AbstractType {
|
||||||
|
public $about;
|
||||||
|
public $avatar;
|
||||||
|
public $favorites;
|
||||||
|
public $location;
|
||||||
|
public $name;
|
||||||
|
public $slug;
|
||||||
|
public $stats;
|
||||||
|
public $waifu;
|
||||||
|
public $website;
|
||||||
|
}
|
@ -47,13 +47,13 @@ class UrlGenerator extends RoutingBase {
|
|||||||
/**
|
/**
|
||||||
* Get the base url for css/js/images
|
* Get the base url for css/js/images
|
||||||
*
|
*
|
||||||
* @param string ...$args
|
* @param string[] $args
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function assetUrl(string ...$args): string
|
public function assetUrl(string ...$args): string
|
||||||
{
|
{
|
||||||
$baseUrl = rtrim($this->url(''), '/')
|
$baseUrl = rtrim($this->url(''), '/')
|
||||||
. $this->__get('asset_path');
|
. $this->config->get('asset_path');
|
||||||
|
|
||||||
array_unshift($args, $baseUrl);
|
array_unshift($args, $baseUrl);
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ class UrlGenerator extends RoutingBase {
|
|||||||
{
|
{
|
||||||
if ( ! array_key_exists($i + 1, $segments))
|
if ( ! array_key_exists($i + 1, $segments))
|
||||||
{
|
{
|
||||||
$segments[$i + 1] = "";
|
$segments[$i + 1] = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$path_segments[$i] = preg_replace('`{.*?}`', $segments[$i + 1], $path_segments[$i]);
|
$path_segments[$i] = preg_replace('`{.*?}`', $segments[$i + 1], $path_segments[$i]);
|
||||||
@ -104,7 +104,7 @@ class UrlGenerator extends RoutingBase {
|
|||||||
public function defaultUrl(string $type): string
|
public function defaultUrl(string $type): string
|
||||||
{
|
{
|
||||||
$type = trim($type);
|
$type = trim($type);
|
||||||
$defaultPath = $this->__get("default_{$type}_list_path");
|
$defaultPath = $this->config->get("default_{$type}_list_path");
|
||||||
|
|
||||||
if ($defaultPath !== NULL)
|
if ($defaultPath !== NULL)
|
||||||
{
|
{
|
||||||
|
14
src/Util.php
14
src/Util.php
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
namespace Aviat\AnimeClient;
|
namespace Aviat\AnimeClient;
|
||||||
|
|
||||||
use Aviat\Ion\ConfigInterface;
|
|
||||||
use Aviat\Ion\Di\{ContainerAware, ContainerInterface};
|
use Aviat\Ion\Di\{ContainerAware, ContainerInterface};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,12 +41,6 @@ class Util {
|
|||||||
'me'
|
'me'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* The config manager
|
|
||||||
* @var ConfigInterface
|
|
||||||
*/
|
|
||||||
private $config;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up the Util class
|
* Set up the Util class
|
||||||
*
|
*
|
||||||
@ -58,7 +51,6 @@ class Util {
|
|||||||
public function __construct(ContainerInterface $container)
|
public function __construct(ContainerInterface $container)
|
||||||
{
|
{
|
||||||
$this->setContainer($container);
|
$this->setContainer($container);
|
||||||
$this->config = $container->get('config');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,7 +60,7 @@ class Util {
|
|||||||
* @param string $b - Second item to compare
|
* @param string $b - Second item to compare
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function isSelected($a, $b)
|
public static function isSelected(string $a, string $b): string
|
||||||
{
|
{
|
||||||
return ($a === $b) ? 'selected' : '';
|
return ($a === $b) ? 'selected' : '';
|
||||||
}
|
}
|
||||||
@ -80,7 +72,7 @@ class Util {
|
|||||||
* @param string $b - Second item to compare
|
* @param string $b - Second item to compare
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function isNotSelected($a, $b)
|
public static function isNotSelected(string $a, string $b): string
|
||||||
{
|
{
|
||||||
return ($a !== $b) ? 'selected' : '';
|
return ($a !== $b) ? 'selected' : '';
|
||||||
}
|
}
|
||||||
@ -108,7 +100,7 @@ class Util {
|
|||||||
*
|
*
|
||||||
* @throws \Aviat\Ion\Di\ContainerException
|
* @throws \Aviat\Ion\Di\ContainerException
|
||||||
* @throws \Aviat\Ion\Di\NotFoundException
|
* @throws \Aviat\Ion\Di\NotFoundException
|
||||||
* @return boolean
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isFormPage(): bool
|
public function isFormPage(): bool
|
||||||
{
|
{
|
||||||
|
@ -153,6 +153,12 @@ const SETTINGS_MAP = [
|
|||||||
'default' => 'Somebody',
|
'default' => 'Somebody',
|
||||||
'description' => 'Name of the owner of the list data.',
|
'description' => 'Name of the owner of the list data.',
|
||||||
],
|
],
|
||||||
|
'dark_theme' => [
|
||||||
|
'type' => 'boolean',
|
||||||
|
'title' => 'Use Dark Theme',
|
||||||
|
'default' => FALSE,
|
||||||
|
'description' => 'Use a darker background theme?',
|
||||||
|
],
|
||||||
'show_anime_collection' => [
|
'show_anime_collection' => [
|
||||||
'type' => 'boolean',
|
'type' => 'boolean',
|
||||||
'title' => 'Show Anime Collection',
|
'title' => 'Show Anime Collection',
|
||||||
|
31
sw.js
31
sw.js
@ -5,36 +5,17 @@ async function fromCache (request) {
|
|||||||
return await cache.match(request);
|
return await cache.match(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fromNetwork (request) {
|
async function updateCache (request) {
|
||||||
return await fetch(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function update (request) {
|
|
||||||
const cache = await caches.open(CACHE_NAME);
|
const cache = await caches.open(CACHE_NAME);
|
||||||
const response = await fetch(request);
|
const response = await fetch(request);
|
||||||
|
|
||||||
if (request.url.includes('/public/images/')) {
|
if (request.url.includes('/public/images/')) {
|
||||||
console.log('Saving to cache: ', request.url);
|
|
||||||
await cache.put(request, response.clone());
|
await cache.put(request, response.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* function refresh (response) {
|
|
||||||
return self.clients.matchAll().then(clients => {
|
|
||||||
clients.forEach(client => {
|
|
||||||
const message = {
|
|
||||||
type: 'refresh',
|
|
||||||
url: response.url,
|
|
||||||
eTag: response.headers.get('ETag')
|
|
||||||
};
|
|
||||||
|
|
||||||
client.postMessage(JSON.stringify(message));
|
|
||||||
})
|
|
||||||
});
|
|
||||||
} */
|
|
||||||
|
|
||||||
self.addEventListener('install', event => {
|
self.addEventListener('install', event => {
|
||||||
console.log('Public Folder Worker installed');
|
console.log('Public Folder Worker installed');
|
||||||
|
|
||||||
@ -55,8 +36,8 @@ self.addEventListener('install', event => {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
self.addEventListener('activate', event => {
|
self.addEventListener('activate', () => {
|
||||||
console.log('Public Folder Worker activated');
|
console.info('Public Folder Worker activated');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pull css, images, and javascript from cache
|
// Pull css, images, and javascript from cache
|
||||||
@ -71,11 +52,7 @@ self.addEventListener('fetch', event => {
|
|||||||
if (cached !== undefined) {
|
if (cached !== undefined) {
|
||||||
event.respondWith(cached);
|
event.respondWith(cached);
|
||||||
} else {
|
} else {
|
||||||
event.respondWith(fromNetwork(event.request));
|
event.respondWith(updateCache(event.request));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
event.waitUntil(
|
|
||||||
update(event.request)
|
|
||||||
);
|
|
||||||
});
|
});
|
@ -17,7 +17,9 @@
|
|||||||
namespace Aviat\AnimeClient\Tests\API;
|
namespace Aviat\AnimeClient\Tests\API;
|
||||||
|
|
||||||
use function Amp\Promise\wait;
|
use function Amp\Promise\wait;
|
||||||
use Aviat\AnimeClient\API\{APIRequestBuilder, HummingbirdClient};
|
use function Aviat\AnimeClient\getResponse;
|
||||||
|
|
||||||
|
use Aviat\AnimeClient\API\APIRequestBuilder;
|
||||||
use Aviat\Ion\Json;
|
use Aviat\Ion\Json;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Psr\Log\NullLogger;
|
use Psr\Log\NullLogger;
|
||||||
@ -37,35 +39,35 @@ class APIRequestBuilderTest extends TestCase {
|
|||||||
$this->builder->setLogger(new NullLogger);
|
$this->builder->setLogger(new NullLogger);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGzipRequest()
|
public function testGzipRequest(): void
|
||||||
{
|
{
|
||||||
$request = $this->builder->newRequest('GET', 'gzip')
|
$request = $this->builder->newRequest('GET', 'gzip')
|
||||||
->getFullRequest();
|
->getFullRequest();
|
||||||
$response = wait((new HummingbirdClient)->request($request));
|
$response = getResponse($request);
|
||||||
$body = Json::decode(wait($response->getBody()));
|
$body = Json::decode(wait($response->getBody()));
|
||||||
$this->assertEquals(1, $body['gzipped']);
|
$this->assertEquals(1, $body['gzipped']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testInvalidRequestMethod()
|
public function testInvalidRequestMethod(): void
|
||||||
{
|
{
|
||||||
$this->expectException(\InvalidArgumentException::class);
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
$this->builder->newRequest('FOO', 'gzip')
|
$this->builder->newRequest('FOO', 'gzip')
|
||||||
->getFullRequest();
|
->getFullRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRequestWithBasicAuth()
|
public function testRequestWithBasicAuth(): void
|
||||||
{
|
{
|
||||||
$request = $this->builder->newRequest('GET', 'headers')
|
$request = $this->builder->newRequest('GET', 'headers')
|
||||||
->setBasicAuth('username', 'password')
|
->setBasicAuth('username', 'password')
|
||||||
->getFullRequest();
|
->getFullRequest();
|
||||||
|
|
||||||
$response = wait((new HummingbirdClient)->request($request));
|
$response = getResponse($request);
|
||||||
$body = Json::decode(wait($response->getBody()));
|
$body = Json::decode(wait($response->getBody()));
|
||||||
|
|
||||||
$this->assertEquals('Basic dXNlcm5hbWU6cGFzc3dvcmQ=', $body['headers']['Authorization']);
|
$this->assertEquals('Basic dXNlcm5hbWU6cGFzc3dvcmQ=', $body['headers']['Authorization']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRequestWithQueryString()
|
public function testRequestWithQueryString(): void
|
||||||
{
|
{
|
||||||
$query = [
|
$query = [
|
||||||
'foo' => 'bar',
|
'foo' => 'bar',
|
||||||
@ -87,13 +89,13 @@ class APIRequestBuilderTest extends TestCase {
|
|||||||
->setQuery($query)
|
->setQuery($query)
|
||||||
->getFullRequest();
|
->getFullRequest();
|
||||||
|
|
||||||
$response = wait((new HummingbirdClient)->request($request));
|
$response = getResponse($request);
|
||||||
$body = Json::decode(wait($response->getBody()));
|
$body = Json::decode(wait($response->getBody()));
|
||||||
|
|
||||||
$this->assertEquals($expected, $body['args']);
|
$this->assertEquals($expected, $body['args']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFormValueRequest()
|
public function testFormValueRequest(): void
|
||||||
{
|
{
|
||||||
$formValues = [
|
$formValues = [
|
||||||
'foo' => 'bar',
|
'foo' => 'bar',
|
||||||
@ -104,13 +106,13 @@ class APIRequestBuilderTest extends TestCase {
|
|||||||
->setFormFields($formValues)
|
->setFormFields($formValues)
|
||||||
->getFullRequest();
|
->getFullRequest();
|
||||||
|
|
||||||
$response = wait((new HummingbirdClient)->request($request));
|
$response = getResponse($request);
|
||||||
$body = Json::decode(wait($response->getBody()));
|
$body = Json::decode(wait($response->getBody()));
|
||||||
|
|
||||||
$this->assertEquals($formValues, $body['form']);
|
$this->assertEquals($formValues, $body['form']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFullUrlRequest()
|
public function testFullUrlRequest(): void
|
||||||
{
|
{
|
||||||
$data = [
|
$data = [
|
||||||
'foo' => [
|
'foo' => [
|
||||||
@ -128,7 +130,7 @@ class APIRequestBuilderTest extends TestCase {
|
|||||||
->setJsonBody($data)
|
->setJsonBody($data)
|
||||||
->getFullRequest();
|
->getFullRequest();
|
||||||
|
|
||||||
$response = wait((new HummingbirdClient)->request($request));
|
$response = getResponse($request);
|
||||||
$body = Json::decode(wait($response->getBody()));
|
$body = Json::decode(wait($response->getBody()));
|
||||||
|
|
||||||
$this->assertEquals($data, $body['json']);
|
$this->assertEquals($data, $body['json']);
|
||||||
|
@ -18,7 +18,6 @@ namespace Aviat\AnimeClient\Tests\API\Kitsu\Transformer;
|
|||||||
|
|
||||||
use Aviat\AnimeClient\API\Kitsu\Transformer\AnimeTransformer;
|
use Aviat\AnimeClient\API\Kitsu\Transformer\AnimeTransformer;
|
||||||
use Aviat\AnimeClient\Tests\AnimeClientTestCase;
|
use Aviat\AnimeClient\Tests\AnimeClientTestCase;
|
||||||
use Aviat\Ion\Friend;
|
|
||||||
use Aviat\Ion\Json;
|
use Aviat\Ion\Json;
|
||||||
|
|
||||||
class AnimeTransformerTest extends AnimeClientTestCase {
|
class AnimeTransformerTest extends AnimeClientTestCase {
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
<?php return Aviat\AnimeClient\Types\Anime::__set_state(array(
|
<?php return Aviat\AnimeClient\Types\AnimePage::__set_state(array(
|
||||||
|
'characters' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
'staff' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
'age_rating' => 'R',
|
'age_rating' => 'R',
|
||||||
'age_rating_guide' => 'Violence, Profanity',
|
'age_rating_guide' => 'Violence, Profanity',
|
||||||
'cover_image' => 'https://media.kitsu.io/anime/poster_images/7442/small.jpg?1418580054',
|
'cover_image' => 'https://media.kitsu.io/anime/poster_images/7442/small.jpg?1418580054',
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php return Aviat\AnimeClient\Types\MangaPage::__set_state(array(
|
<?php return Aviat\AnimeClient\Types\MangaPage::__set_state(array(
|
||||||
|
'characters' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
'chapter_count' => '-',
|
'chapter_count' => '-',
|
||||||
'cover_image' => 'https://media.kitsu.io/manga/poster_images/20286/small.jpg?1434293999',
|
'cover_image' => 'https://media.kitsu.io/manga/poster_images/20286/small.jpg?1434293999',
|
||||||
'genres' =>
|
'genres' =>
|
||||||
@ -68,6 +71,9 @@
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
'manga_type' => 'manga',
|
'manga_type' => 'manga',
|
||||||
|
'staff' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
'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!
|
'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)',
|
(Source: Kirei Cake)',
|
||||||
'title' => 'Bokura wa Minna Kawaisou',
|
'title' => 'Bokura wa Minna Kawaisou',
|
||||||
|
Loading…
Reference in New Issue
Block a user