Merge remote-tracking branch 'origin/develop'
This commit is contained in:
commit
dcc5b928c7
@ -1,10 +1,8 @@
|
||||
# Changelog
|
||||
|
||||
## Version 5.3
|
||||
* Updated to support PHP 8.1
|
||||
|
||||
## Version 5.2
|
||||
* Updated PHP requirement to 8
|
||||
* Updated to support PHP 8.1
|
||||
|
||||
## Version 5.1
|
||||
* Added session check, so when coming back to a page, if the session is expired, the page will refresh.
|
||||
|
@ -6,7 +6,7 @@
|
||||
<?php if ($auth->isAuthenticated()): ?>
|
||||
<button title="Increment episode count" class="plus-one" hidden>+1 Episode</button>
|
||||
<?php endif ?>
|
||||
<?= $helper->picture("images/anime/{$item['anime']['id']}.webp") ?>
|
||||
<?= $helper->img($item['anime']['cover_image'], ['width' => 220, 'loading' => 'lazy']) ?>
|
||||
|
||||
<div class="name">
|
||||
<a href="<?= $url->generate('anime.details', ['id' => $item['anime']['slug']]) ?>">
|
||||
|
@ -4,7 +4,7 @@
|
||||
<button class="plus-one-chapter">+1 Chapter</button>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<?= $helper->picture("images/manga/{$item['manga']['id']}.webp") ?>
|
||||
<?= $helper->img($item['manga']['image'], ['width' => 220, 'loading' => 'lazy']) ?>
|
||||
<div class="name">
|
||||
<a href="<?= $url->generate('manga.details', ['id' => $item['manga']['slug']]) ?>">
|
||||
<?= $escape->html($item['manga']['title']) ?>
|
||||
|
@ -1,13 +1,12 @@
|
||||
<?php
|
||||
|
||||
use Aviat\AnimeClient\Kitsu;
|
||||
use function Aviat\AnimeClient\getLocalImg;
|
||||
|
||||
?>
|
||||
<main class="details fixed">
|
||||
<section class="flex" unselectable>
|
||||
<aside class="info">
|
||||
<?= $helper->picture("images/anime/{$data['id']}-original.webp") ?>
|
||||
<?= $helper->img($data['cover_image'], ['width' => '390']) ?>
|
||||
|
||||
<br />
|
||||
|
||||
@ -162,14 +161,14 @@ use function Aviat\AnimeClient\getLocalImg;
|
||||
use ($component, $url, $helper) {
|
||||
$rendered = [];
|
||||
foreach ($characterList as $id => $character):
|
||||
if (empty($character['image']['original']))
|
||||
if (empty($character['image']))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$rendered[] = $component->character(
|
||||
$character['name'],
|
||||
$url->generate('character', ['slug' => $character['slug']]),
|
||||
$helper->picture("images/characters/{$id}.webp"),
|
||||
$helper->img($character['image']),
|
||||
(strtolower($role) !== 'main') ? 'small-character' : 'character'
|
||||
);
|
||||
endforeach;
|
||||
@ -187,14 +186,14 @@ use function Aviat\AnimeClient\getLocalImg;
|
||||
use ($component, $url, $helper) {
|
||||
$rendered = [];
|
||||
foreach ($staffList as $id => $person):
|
||||
if (empty($person['image']['original']))
|
||||
if (empty($person['image']))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$rendered[] = $component->character(
|
||||
$person['name'],
|
||||
$url->generate('person', ['slug' => $person['slug']]),
|
||||
$helper->picture(getLocalImg($person['image']['original'] ?? NULL)),
|
||||
$helper->img($person['image']),
|
||||
'character small-person',
|
||||
);
|
||||
endforeach;
|
||||
|
@ -16,7 +16,7 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="9">
|
||||
<?= $helper->picture("images/anime/{$item['anime']['id']}-original.webp", "jpg", [], ["width" => "390"]) ?>
|
||||
<?= $helper->img($item['anime']['cover_image']) ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -25,10 +25,10 @@
|
||||
<?php endif ?>
|
||||
<th>Title</th>
|
||||
<th>Airing Status</th>
|
||||
<th>Score</th>
|
||||
<th class='numeric'>Score</th>
|
||||
<th>Type</th>
|
||||
<th>Progress</th>
|
||||
<th>Rated</th>
|
||||
<th class='numeric'>Progress</th>
|
||||
<th class='rating'>Age Rating</th>
|
||||
<th>Attributes</th>
|
||||
<?php if($hasNotes): ?><th>Notes</th><?php endif ?>
|
||||
</tr>
|
||||
|
@ -7,7 +7,7 @@ use Aviat\AnimeClient\Kitsu;
|
||||
<main class="character-page details fixed">
|
||||
<section class="flex flex-no-wrap">
|
||||
<aside>
|
||||
<?= $helper->picture("images/characters/{$data['id']}-original.webp") ?>
|
||||
<?= $helper->img($data['image']) ?>
|
||||
</aside>
|
||||
<div>
|
||||
<h2 class="toph"><?= $data['name'] ?></h2>
|
||||
@ -41,7 +41,7 @@ use Aviat\AnimeClient\Kitsu;
|
||||
$rendered[] = $component->media(
|
||||
array_merge([$item['title']], $item['titles']),
|
||||
$url->generate("{$mediaType}.details", ['id' => $item['slug']]),
|
||||
$helper->picture("images/{$mediaType}/{$item['id']}.webp")
|
||||
$helper->img(Kitsu::getPosterImage($item), ['width' => 220, 'loading' => 'lazy']),
|
||||
);
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ use Aviat\AnimeClient\Kitsu;
|
||||
$link = $url->generate('person', ['id' => $c['person']['id']]);
|
||||
?>
|
||||
<a href="<?= $link ?>">
|
||||
<?= $helper->picture(getLocalImg($c['person']['image'], TRUE)) ?>
|
||||
<?= $helper->img($c['person']['image']) ?>
|
||||
<div class="name">
|
||||
<?= $c['person']['name'] ?>
|
||||
</div>
|
||||
@ -91,7 +91,7 @@ use Aviat\AnimeClient\Kitsu;
|
||||
$titles = Kitsu::filterTitles($series['attributes']);
|
||||
?>
|
||||
<a href="<?= $link ?>">
|
||||
<?= $helper->picture(getLocalImg($series['attributes']['posterImage']['small'], TRUE)) ?>
|
||||
<?= $helper->img(Kitsu::getPosterImage($series['attributes'])) ?>
|
||||
</a>
|
||||
<div class="name">
|
||||
<a href="<?= $link ?>">
|
||||
@ -121,12 +121,12 @@ use Aviat\AnimeClient\Kitsu;
|
||||
$person = $component->character(
|
||||
$c['person']['name'],
|
||||
$url->generate('person', ['slug' => $c['person']['slug']]),
|
||||
$helper->picture(getLocalImg($c['person']['image']))
|
||||
$helper->img($c['person']['image']['original']['url']),
|
||||
);
|
||||
$medias = array_map(fn ($series) => $component->media(
|
||||
array_merge([$series['title']], $series['titles']),
|
||||
$url->generate('anime.details', ['id' => $series['slug']]),
|
||||
$helper->picture(getLocalImg($series['posterImage'], TRUE))
|
||||
$helper->img(Kitsu::getPosterImage($series)),
|
||||
), $c['series']);
|
||||
$media = implode('', array_map('mb_trim', $medias));
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
<div class="cssload-inner cssload-two"></div>
|
||||
<div class="cssload-inner cssload-three"></div>
|
||||
</div>
|
||||
<label for="search">Search for <?= $collection_type ?> by name: <input type="search" id="search" name="search" /></label>
|
||||
<label for="search-anime-collection">Search for <?= $collection_type ?> by name: <input type="search" id="search-anime-collection" name="search" /></label>
|
||||
<section id="series-list" class="media-wrap">
|
||||
</section>
|
||||
</section>
|
||||
|
@ -38,10 +38,10 @@
|
||||
{$firstTh}
|
||||
<th>Title</th>
|
||||
{$mediaTh}
|
||||
<th>Episode Count</th>
|
||||
<th>Episode Length</th>
|
||||
<th class='numeric'>Episode Count</th>
|
||||
<th class='numeric'>Episode Length</th>
|
||||
<th>Show Type</th>
|
||||
<th>Age Rating</th>
|
||||
<th class='rating'>Age Rating</th>
|
||||
{$noteTh}
|
||||
<th>Genres</th>
|
||||
</tr>
|
||||
|
@ -7,11 +7,9 @@
|
||||
<article class="flex flex-no-wrap flex-justify-start">
|
||||
<section class="flex-self-center history-img">
|
||||
<a href="<?= $item['url'] ?>">
|
||||
<?= $helper->picture(
|
||||
<?= $helper->img(
|
||||
$item['coverImg'],
|
||||
'jpg',
|
||||
['width' => '110px', 'height' => '156px'],
|
||||
['width' => '110px', 'height' => '156px']
|
||||
) ?>
|
||||
</a>
|
||||
</section>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<main class="details fixed">
|
||||
<section class="flex flex-no-wrap">
|
||||
<aside class="info">
|
||||
<?= $helper->picture("images/manga/{$data['id']}-original.webp", 'jpg', ['class' => 'cover']) ?>
|
||||
<?= $helper->img($data['cover_image'], ['class' => 'cover', 'width' => '350']) ?>
|
||||
|
||||
<br />
|
||||
|
||||
@ -79,7 +79,7 @@
|
||||
$rendered[] = $component->character(
|
||||
$char['name'],
|
||||
$url->generate('character', ['slug' => $char['slug']]),
|
||||
$helper->picture("images/characters/{$id}.webp"),
|
||||
$helper->img($char['image'], ['loading' => 'lazy']),
|
||||
($role !== 'main') ? 'small-character' : 'character'
|
||||
);
|
||||
}
|
||||
@ -96,7 +96,7 @@
|
||||
fn ($person) => $component->character(
|
||||
$person['name'],
|
||||
$url->generate('person', ['slug' => $person['slug']]),
|
||||
$helper->picture("images/people/{$person['id']}.webp")
|
||||
$helper->img($person['image']),
|
||||
),
|
||||
$people
|
||||
))
|
||||
|
@ -18,7 +18,7 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="9">
|
||||
<?= $helper->picture("images/manga/{$item['manga']['id']}-original.webp", "jpg", [], ["width" => "390"]) ?>
|
||||
<?= $helper->img($item['manga']['image']) ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -20,8 +20,8 @@
|
||||
<td> </td>
|
||||
<?php endif ?>
|
||||
<th>Title</th>
|
||||
<th>Rating</th>
|
||||
<th>Completed Chapters</th>
|
||||
<th class='numeric'>Score</th>
|
||||
<th class='numeric'>Completed Chapters</th>
|
||||
<th>Attributes</th>
|
||||
<th>Type</th>
|
||||
</tr>
|
||||
|
@ -1,10 +1,7 @@
|
||||
<?php
|
||||
use function Aviat\AnimeClient\getLocalImg;
|
||||
?>
|
||||
<main class="details fixed">
|
||||
<section class="flex flex-no-wrap">
|
||||
<div>
|
||||
<?= $helper->picture("images/people/{$data['id']}-original.webp", 'jpg', ['class' => 'cover' ]) ?>
|
||||
<?= $helper->img($data['image'], ['class' => 'cover' ]) ?>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="toph"><?= $data['name'] ?></h2>
|
||||
@ -40,7 +37,7 @@ use function Aviat\AnimeClient\getLocalImg;
|
||||
<?= $component->media(
|
||||
$series['titles'],
|
||||
$url->generate("{$mediaType}.details", ['id' => $series['slug']]),
|
||||
$helper->picture("images/{$type}/{$sid}.webp")
|
||||
$helper->img($series['image'], ['width' => 220, 'loading' => 'lazy'])
|
||||
) ?>
|
||||
<?php endforeach; ?>
|
||||
</section>
|
||||
@ -61,7 +58,7 @@ use function Aviat\AnimeClient\getLocalImg;
|
||||
$character = $component->character(
|
||||
$item['character']['canonicalName'],
|
||||
$url->generate('character', ['slug' => $item['character']['slug']]),
|
||||
$helper->picture(getLocalImg($item['character']['image']['original'] ?? null))
|
||||
$helper->img($item['character']['image'], ['loading' => 'lazy']),
|
||||
);
|
||||
$medias = [];
|
||||
foreach ($item['media'] as $sid => $series)
|
||||
@ -69,7 +66,7 @@ use function Aviat\AnimeClient\getLocalImg;
|
||||
$medias[] = $component->media(
|
||||
$series['titles'],
|
||||
$url->generate('anime.details', ['id' => $series['slug']]),
|
||||
$helper->picture("images/anime/{$sid}.webp")
|
||||
$helper->img($series['image'], ['width' => 220, 'loading' => 'lazy'])
|
||||
);
|
||||
}
|
||||
$media = implode('', array_map('mb_trim', $medias));
|
||||
|
@ -16,7 +16,7 @@ use Aviat\AnimeClient\Kitsu;
|
||||
<section class="flex flex-no-wrap">
|
||||
<aside class="info">
|
||||
<center>
|
||||
<?= $helper->img($urlGenerator->assetUrl($data['avatar']), ['alt' => '']); ?>
|
||||
<?= $helper->img($data['avatar'], ['alt' => '']); ?>
|
||||
</center>
|
||||
<br />
|
||||
<table class="media-details">
|
||||
@ -75,7 +75,7 @@ use Aviat\AnimeClient\Kitsu;
|
||||
$rendered[] = $component->character(
|
||||
$item['names']['canonical'],
|
||||
$url->generate('character', ['slug' => $item['slug']]),
|
||||
$helper->picture("images/characters/{$item['id']}.webp")
|
||||
$helper->img($item['image']['original']['url'])
|
||||
);
|
||||
}
|
||||
else
|
||||
@ -86,7 +86,7 @@ use Aviat\AnimeClient\Kitsu;
|
||||
Kitsu::getFilteredTitles($item['titles']),
|
||||
),
|
||||
$url->generate("{$type}.details", ['id' => $item['slug']]),
|
||||
$helper->picture("images/{$type}/{$item['id']}.webp"),
|
||||
$helper->img(Kitsu::getPosterImage($item), ['width' => 220]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
6
console
6
console
@ -9,8 +9,8 @@ use ConsoleKit\Console;
|
||||
|
||||
$GLOBALS['_SERVER']['HTTP_HOST'] = 'localhost';
|
||||
|
||||
define('APP_DIR', __DIR__ . '/app');
|
||||
define('TEMPLATE_DIR', APP_DIR . '/templates');
|
||||
const APP_DIR = __DIR__ . '/app';
|
||||
const TEMPLATE_DIR = APP_DIR . '/templates';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Start console script
|
||||
@ -26,7 +26,7 @@ try
|
||||
'sync:lists' => Command\SyncLists::class
|
||||
]))->run();
|
||||
}
|
||||
catch (\Throwable)
|
||||
catch (Throwable)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import _ from './anime-client.js'
|
||||
import { renderSearchResults } from './template-helpers.js'
|
||||
|
||||
const search = (query) => {
|
||||
const search = (query, isCollection = false) => {
|
||||
// Show the loader
|
||||
_.show('.cssload-loader');
|
||||
|
||||
@ -13,10 +13,11 @@ const search = (query) => {
|
||||
_.hide('.cssload-loader');
|
||||
|
||||
// Show the results
|
||||
_.$('#series-list')[ 0 ].innerHTML = renderSearchResults('anime', searchResults);
|
||||
_.$('#series-list')[ 0 ].innerHTML = renderSearchResults('anime', searchResults, isCollection);
|
||||
});
|
||||
};
|
||||
|
||||
// Anime list search
|
||||
if (_.hasElement('.anime #search')) {
|
||||
let prevRequest = null;
|
||||
|
||||
@ -34,6 +35,24 @@ if (_.hasElement('.anime #search')) {
|
||||
}));
|
||||
}
|
||||
|
||||
// Anime collection search
|
||||
if (_.hasElement('#search-anime-collection')) {
|
||||
let prevRequest = null;
|
||||
|
||||
_.on('#search-anime-collection', 'input', _.throttle(250, (e) => {
|
||||
const query = encodeURIComponent(e.target.value);
|
||||
if (query === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (prevRequest !== null) {
|
||||
prevRequest.abort();
|
||||
}
|
||||
|
||||
prevRequest = search(query, true);
|
||||
}));
|
||||
}
|
||||
|
||||
// Action to increment episode count
|
||||
_.on('body.anime.list', 'click', '.plus-one', (e) => {
|
||||
let parentSel = _.closestParent(e.target, 'article');
|
||||
|
@ -6,9 +6,22 @@ const LightTableSorter = (() => {
|
||||
const sort = (a, b) => {
|
||||
let textA = text(a);
|
||||
let textB = text(b);
|
||||
const n = parseInt(textA, 10);
|
||||
if (n) {
|
||||
textA = n;
|
||||
console.log("Comparing " + textA + " and " + textB)
|
||||
|
||||
if(th.classList.contains("numeric")){
|
||||
let arrayA = textA.replace('episodes: ','').replace('-',0).split("/");
|
||||
let arrayB = textB.replace('episodes: ','').replace('-',0).split("/");
|
||||
if(arrayA.length > 1) {
|
||||
textA = parseInt(arrayA[0],10) / parseInt(arrayA[1],10);
|
||||
textB = parseInt(arrayB[0],10) / parseInt(arrayB[1],10);
|
||||
}
|
||||
else{
|
||||
textA = parseInt(arrayA[0],10);
|
||||
textB = parseInt(arrayB[0],10);
|
||||
}
|
||||
}
|
||||
else if (parseInt(textA, 10)) {
|
||||
textA = parseInt(textA, 10);
|
||||
textB = parseInt(textB, 10);
|
||||
}
|
||||
if (textA > textB) {
|
||||
@ -59,6 +72,7 @@ const LightTableSorter = (() => {
|
||||
for (let i = 0, len = ths.length; i < len; i++) {
|
||||
let th = ths[i];
|
||||
th.classList.add('sorting');
|
||||
th.classList.add('testing');
|
||||
results.push(th.onclick = onClickEvent);
|
||||
}
|
||||
return results;
|
||||
|
@ -13,10 +13,11 @@ _.on('main', 'change', '.big-check', (e) => {
|
||||
*
|
||||
* @param {'anime'|'manga'} type
|
||||
* @param {Object} item
|
||||
* @param isCollection
|
||||
* @returns {String}
|
||||
*/
|
||||
function renderEditLink (type, item) {
|
||||
if (item.libraryEntry === null) {
|
||||
function renderEditLink (type, item, isCollection = false) {
|
||||
if (isCollection || item.libraryEntry === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
@ -38,13 +39,18 @@ function renderEditLink (type, item) {
|
||||
*
|
||||
* @param {'anime'|'manga'} type
|
||||
* @param {Object} data
|
||||
* @param {boolean} isCollection
|
||||
* @returns {String}
|
||||
*/
|
||||
export function renderSearchResults (type, data) {
|
||||
export function renderSearchResults (type, data, isCollection = false) {
|
||||
return data.map(item => {
|
||||
const titles = item.titles.join('<br />');
|
||||
const disabled = item.libraryEntry !== null ? 'disabled' : '';
|
||||
const editLink = renderEditLink(type, item);
|
||||
let disabled = item.libraryEntry !== null ? 'disabled' : '';
|
||||
const editLink = renderEditLink(type, item, isCollection);
|
||||
|
||||
if (isCollection) {
|
||||
disabled = '';
|
||||
}
|
||||
|
||||
return `
|
||||
<article class="media search ${disabled}">
|
||||
@ -52,11 +58,7 @@ export function renderSearchResults (type, data) {
|
||||
<input type="radio" class="mal-check" id="mal_${item.slug}" name="mal_id" value="${item.mal_id}" ${disabled} />
|
||||
<input type="radio" class="big-check" id="${item.slug}" name="id" value="${item.id}" ${disabled} />
|
||||
<label for="${item.slug}">
|
||||
<picture width="220">
|
||||
<source srcset="/public/images/${type}/${item.id}.webp" type="image/webp" />
|
||||
<source srcset="/public/images/${type}/${item.id}.jpg" type="image/jpeg" />
|
||||
<img src="/public/images/${type}/${item.id}.jpg" alt="" width="220" />
|
||||
</picture>
|
||||
<img src="${item.coverImage}" alt="" width="220" />
|
||||
<span class="name">
|
||||
${item.canonicalTitle}<br />
|
||||
<small>${titles}</small>
|
||||
|
@ -25,7 +25,7 @@ setlocale(LC_CTYPE, 'en_US');
|
||||
// Load composer autoloader
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
Debugger::$strictMode = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED; // all errors except deprecated notices
|
||||
Debugger::$strictMode = E_ALL & ~E_DEPRECATED; // all errors except deprecated notices
|
||||
Debugger::$showBar = false;
|
||||
Debugger::enable(Debugger::DEVELOPMENT, __DIR__ . '/app/logs');
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 807 B |
Binary file not shown.
Before Width: | Height: | Size: 1.3 KiB |
2
public/js/scripts.min.js
vendored
2
public/js/scripts.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
public/js/tables.min.js
vendored
2
public/js/tables.min.js
vendored
@ -1 +1 @@
|
||||
var LightTableSorter=function(){var th=null;var cellIndex=null;var order='';var text=function(row){return row.cells.item(cellIndex).textContent.toLowerCase();};var sort=function(a,b){var textA=text(a);var textB=text(b);var n=parseInt(textA,10);if(n){textA=n;textB=parseInt(textB,10);}if(textA>textB)return 1;if(textA<textB)return -1;return 0;};var toggle=function(){var c=order!=='sorting-asc'?'sorting-asc':'sorting-desc';th.className=(th.className.replace(order,'')+' '+c).trim();return order=c;};var reset=function(){th.classList.remove('sorting-asc','sorting-desc');th.classList.add('sorting');return order='';};var onClickEvent=function(e){if(th&&cellIndex!==e.target.cellIndex)reset();th=e.target;if(th.nodeName.toLowerCase()==='th'){cellIndex=th.cellIndex;var tbody=th.offsetParent.getElementsByTagName('tbody')[0];var rows=Array.from(tbody.rows);if(rows){rows.sort(sort);if(order==='sorting-asc')rows.reverse();toggle();tbody.innerHtml='';rows.forEach(function(row){tbody.appendChild(row);});}}};return {init:function(){var ths=document.getElementsByTagName('th');var results=[];for(var i=0,len=ths.length;i<len;i++){var th1=ths[i];th1.classList.add('sorting');results.push(th1.onclick=onClickEvent);}return results;}};}();LightTableSorter.init();
|
||||
var LightTableSorter=function(){var th=null;var cellIndex=null;var order='';var text=function(row){return row.cells.item(cellIndex).textContent.toLowerCase()};var sort=function(a,b){var textA=text(a);var textB=text(b);console.log("Comparing "+textA+" and "+textB);if(th.classList.contains("numeric")){var arrayA=textA.replace('episodes: ','').replace('-',0).split("/");var arrayB=textB.replace('episodes: ','').replace('-',0).split("/");if(arrayA.length>1){textA=parseInt(arrayA[0],10)/parseInt(arrayA[1],10);textB=parseInt(arrayB[0],10)/parseInt(arrayB[1],10)}else{textA=parseInt(arrayA[0],10);textB=parseInt(arrayB[0],10)}}else if(parseInt(textA,10)){textA=parseInt(textA,10);textB=parseInt(textB,10)}if(textA>textB)return 1;if(textA<textB)return -1;return 0};var toggle=function(){var c=order!=='sorting-asc'?'sorting-asc':'sorting-desc';th.className=(th.className.replace(order,'')+' '+c).trim();return order=c};var reset=function(){th.classList.remove('sorting-asc','sorting-desc');th.classList.add('sorting');return order=''};var onClickEvent=function(e){if(th&&cellIndex!==e.target.cellIndex)reset();th=e.target;if(th.nodeName.toLowerCase()==='th'){cellIndex=th.cellIndex;var tbody=th.offsetParent.getElementsByTagName('tbody')[0];var rows=Array.from(tbody.rows);if(rows){rows.sort(sort);if(order==='sorting-asc')rows.reverse();toggle();tbody.innerHtml='';rows.forEach(function(row){tbody.appendChild(row)})}}};return{init:function(){var ths=document.getElementsByTagName('th');var results=[];for(var i=0,len=ths.length;i<len;i++){var th=ths[i];th.classList.add('sorting');th.classList.add('testing');results.push(th.onclick=onClickEvent)}return results}}}();LightTableSorter.init()
|
File diff suppressed because one or more lines are too long
@ -154,7 +154,7 @@ abstract class APIRequestBuilder {
|
||||
* Set a request header
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
* @param string|null $value
|
||||
* @return self
|
||||
*/
|
||||
public function setHeader(string $name, string $value = NULL): self
|
||||
|
@ -70,7 +70,7 @@ abstract class AbstractListItem {
|
||||
* Delete a list item
|
||||
*
|
||||
* @param string $id - The id of the list item to delete
|
||||
* @return Request
|
||||
* @return Request|null
|
||||
*/
|
||||
abstract public function delete(string $id):?Request;
|
||||
}
|
@ -207,7 +207,7 @@ final class Model
|
||||
*
|
||||
* @param FormItem $data
|
||||
* @param string $type - Them media type (anime/manga)
|
||||
* @return Request
|
||||
* @return Request|null
|
||||
*/
|
||||
public function incrementListItem(FormItem $data, string $type): ?Request
|
||||
{
|
||||
@ -225,7 +225,7 @@ final class Model
|
||||
*
|
||||
* @param FormItem $data
|
||||
* @param string $type - Them media type (anime/manga)
|
||||
* @return Request
|
||||
* @return Request|null
|
||||
*/
|
||||
public function updateListItem(FormItem $data, string $type): ?Request
|
||||
{
|
||||
@ -244,7 +244,7 @@ final class Model
|
||||
*
|
||||
* @param string $malId - The id of the list item to remove
|
||||
* @param string $type - Them media type (anime/manga)
|
||||
* @return Request
|
||||
* @return Request|null
|
||||
*/
|
||||
public function deleteListItem(string $malId, string $type): ?Request
|
||||
{
|
||||
@ -262,7 +262,7 @@ final class Model
|
||||
*
|
||||
* @param string $malId
|
||||
* @param string $type - The media type (anime/manga)
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getListIdFromMalId(string $malId, string $type): ?string
|
||||
{
|
||||
@ -306,7 +306,7 @@ final class Model
|
||||
*
|
||||
* @param string $malId
|
||||
* @param string $type
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
private function getMediaIdFromMalId(string $malId, string $type = 'ANIME'): ?string
|
||||
{
|
||||
|
@ -257,24 +257,18 @@ final class RequestBuilder extends APIRequestBuilder {
|
||||
$validResponseCodes = [200, 201];
|
||||
|
||||
$logger = $this->container->getLogger('anilist-request');
|
||||
if ($logger !== NULL)
|
||||
{
|
||||
$logger->debug('Anilist response', [
|
||||
'status' => $response->getStatus(),
|
||||
'reason' => $response->getReason(),
|
||||
'body' => $response->getBody(),
|
||||
'headers' => $response->getHeaders(),
|
||||
//'requestHeaders' => $request->getHeaders(),
|
||||
]);
|
||||
}
|
||||
$logger?->debug('Anilist response', [
|
||||
'status' => $response->getStatus(),
|
||||
'reason' => $response->getReason(),
|
||||
'body' => $response->getBody(),
|
||||
'headers' => $response->getHeaders(),
|
||||
//'requestHeaders' => $request->getHeaders(),
|
||||
]);
|
||||
|
||||
|
||||
if ( ! \in_array($response->getStatus(), $validResponseCodes, TRUE))
|
||||
{
|
||||
if ($logger !== NULL)
|
||||
{
|
||||
$logger->warning('Non 200 response for POST api call', (array)$response->getBody());
|
||||
}
|
||||
$logger?->warning('Non 200 response for POST api call', (array)$response->getBody());
|
||||
}
|
||||
|
||||
$rawBody = wait($response->getBody()->buffer());
|
||||
|
@ -24,6 +24,7 @@ use Aviat\AnimeClient\Types\{AnimeListItem, FormItem};
|
||||
use Aviat\Ion\Transformer\AbstractTransformer;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeInterface;
|
||||
|
||||
class AnimeListTransformer extends AbstractTransformer {
|
||||
|
||||
@ -57,7 +58,7 @@ class AnimeListTransformer extends AbstractTransformer {
|
||||
: AnimeWatchingStatus::ANILIST_TO_KITSU[$item['status']],
|
||||
'updatedAt' => (new DateTime())
|
||||
->setTimestamp($item['updatedAt'])
|
||||
->format(DateTime::W3C)
|
||||
->format(DateTimeInterface::W3C)
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ use Aviat\AnimeClient\Types\FormItem;
|
||||
use Aviat\Ion\Transformer\AbstractTransformer;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeInterface;
|
||||
|
||||
class MangaListTransformer extends AbstractTransformer {
|
||||
|
||||
@ -58,7 +59,7 @@ class MangaListTransformer extends AbstractTransformer {
|
||||
: MangaReadingStatus::ANILIST_TO_KITSU[$item['status']],
|
||||
'updatedAt' => (new DateTime())
|
||||
->setTimestamp($item['updatedAt'])
|
||||
->format(DateTime::W3C),
|
||||
->format(DateTimeInterface::W3C),
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
@ -19,10 +19,8 @@ namespace Aviat\AnimeClient\API\Anilist\Types;
|
||||
use Aviat\AnimeClient\Types\AbstractType;
|
||||
|
||||
class MediaListEntry extends AbstractType {
|
||||
/**
|
||||
* @var int|string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
public int|string $id;
|
||||
|
||||
public ?string $notes;
|
||||
|
||||
|
@ -12,7 +12,7 @@ union ActivityUnion = ListActivity | MessageActivity | TextActivity
|
||||
union LikeableUnion = ActivityReply | ListActivity | MessageActivity | TextActivity | Thread | ThreadComment
|
||||
|
||||
"Notification union type"
|
||||
union NotificationUnion = ActivityLikeNotification | ActivityMentionNotification | ActivityMessageNotification | ActivityReplyLikeNotification | ActivityReplyNotification | ActivityReplySubscribedNotification | AiringNotification | FollowingNotification | RelatedMediaAdditionNotification | ThreadCommentLikeNotification | ThreadCommentMentionNotification | ThreadCommentReplyNotification | ThreadCommentSubscribedNotification | ThreadLikeNotification
|
||||
union NotificationUnion = ActivityLikeNotification | ActivityMentionNotification | ActivityMessageNotification | ActivityReplyLikeNotification | ActivityReplyNotification | ActivityReplySubscribedNotification | AiringNotification | FollowingNotification | MediaDataChangeNotification | MediaDeletionNotification | MediaMergeNotification | RelatedMediaAdditionNotification | ThreadCommentLikeNotification | ThreadCommentMentionNotification | ThreadCommentReplyNotification | ThreadCommentSubscribedNotification | ThreadLikeNotification
|
||||
|
||||
"Notification for when a activity is liked"
|
||||
type ActivityLikeNotification {
|
||||
@ -227,6 +227,8 @@ type AniChartUser {
|
||||
type Character {
|
||||
"The character's age. Note this is a string, not an int, it may contain further text and additional ages."
|
||||
age: String
|
||||
"The characters blood type"
|
||||
bloodType: String
|
||||
"The character's birth date"
|
||||
dateOfBirth: FuzzyDate
|
||||
"A general description of the character"
|
||||
@ -262,7 +264,7 @@ type Character {
|
||||
name: CharacterName
|
||||
"The url for the character page on the AniList website"
|
||||
siteUrl: String
|
||||
updatedAt: Int @deprecated(reason : "No data available")
|
||||
updatedAt: Int @deprecated(reason: "No data available")
|
||||
}
|
||||
|
||||
type CharacterConnection {
|
||||
@ -314,15 +316,21 @@ type CharacterName {
|
||||
middle: String
|
||||
"The character's full name in their native language"
|
||||
native: String
|
||||
"The currently authenticated users preferred name language. Default romaji for non-authenticated"
|
||||
userPreferred: String
|
||||
}
|
||||
|
||||
"A submission for a character that features in an anime or manga"
|
||||
type CharacterSubmission {
|
||||
"Data Mod assigned to handle the submission"
|
||||
assignee: User
|
||||
"Character that the submission is referencing"
|
||||
character: Character
|
||||
createdAt: Int
|
||||
"The id of the submission"
|
||||
id: Int!
|
||||
"Whether the submission is locked"
|
||||
locked: Boolean
|
||||
"Inner details of submission status"
|
||||
notes: String
|
||||
source: String
|
||||
@ -543,6 +551,7 @@ type InternalPage {
|
||||
sort: [AiringSort]
|
||||
): [AiringSchedule]
|
||||
characterSubmissions(
|
||||
assigneeId: Int,
|
||||
characterId: Int,
|
||||
"The order the results will be returned in"
|
||||
sort: [SubmissionSort],
|
||||
@ -654,6 +663,8 @@ type InternalPage {
|
||||
id_not_in: [Int],
|
||||
"Filter by if the media's intended for 18+ adult audiences"
|
||||
isAdult: Boolean,
|
||||
"If the media is officially licensed or a self-published doujin release"
|
||||
isLicensed: Boolean,
|
||||
"Filter media by sites with a online streaming or reading license"
|
||||
licensedBy: String,
|
||||
"Filter media by sites with a online streaming or reading license"
|
||||
@ -772,6 +783,7 @@ type InternalPage {
|
||||
userName: String
|
||||
): [MediaList]
|
||||
mediaSubmissions(
|
||||
assigneeId: Int,
|
||||
mediaId: Int,
|
||||
"The order the results will be returned in"
|
||||
sort: [SubmissionSort],
|
||||
@ -864,7 +876,7 @@ type InternalPage {
|
||||
"Filter by user who created the recommendation"
|
||||
userId: Int
|
||||
): [Recommendation]
|
||||
reports: [Report]
|
||||
reports(reportedId: Int, reporterId: Int): [Report]
|
||||
reviews(
|
||||
"Filter by Review id"
|
||||
id: Int,
|
||||
@ -906,6 +918,7 @@ type InternalPage {
|
||||
sort: [StaffSort]
|
||||
): [Staff]
|
||||
staffSubmissions(
|
||||
assigneeId: Int,
|
||||
"The order the results will be returned in"
|
||||
sort: [SubmissionSort],
|
||||
staffId: Int,
|
||||
@ -958,9 +971,15 @@ type InternalPage {
|
||||
"Filter by the user id of the thread's creator"
|
||||
userId: Int
|
||||
): [Thread]
|
||||
userBlockSearch(
|
||||
"Filter by search query"
|
||||
search: String
|
||||
): [User]
|
||||
users(
|
||||
"Filter by the user id"
|
||||
id: Int,
|
||||
"Filter to moderators only if true"
|
||||
isModerator: Boolean,
|
||||
"Filter by the name of the user"
|
||||
name: String,
|
||||
"Filter by search query"
|
||||
@ -1073,6 +1092,8 @@ type Media {
|
||||
isAdult: Boolean
|
||||
"If the media is marked as favourite by the current authenticated user"
|
||||
isFavourite: Boolean!
|
||||
"If the media is blocked from being added to favourites"
|
||||
isFavouriteBlocked: Boolean!
|
||||
"If the media is officially licensed or a self-published doujin release"
|
||||
isLicensed: Boolean
|
||||
"Locked media may not be added to lists our favorited. This may be due to the entry pending for deletion or other reasons."
|
||||
@ -1120,7 +1141,7 @@ type Media {
|
||||
siteUrl: String
|
||||
"Source type the media was adapted from."
|
||||
source(
|
||||
"Provide 2 to use new version 2 of sources enum"
|
||||
"Provide 2 or 3 to use new version 2 or 3 of sources enum"
|
||||
version: Int
|
||||
): MediaSource
|
||||
"The staff who produced the media"
|
||||
@ -1205,6 +1226,40 @@ type MediaCoverImage {
|
||||
medium: String
|
||||
}
|
||||
|
||||
"Notification for when a media entry's data was changed in a significant way impacting users' list tracking"
|
||||
type MediaDataChangeNotification {
|
||||
"The reason for the media data change"
|
||||
context: String
|
||||
"The time the notification was created at"
|
||||
createdAt: Int
|
||||
"The id of the Notification"
|
||||
id: Int!
|
||||
"The media that received data changes"
|
||||
media: Media
|
||||
"The id of the media that received data changes"
|
||||
mediaId: Int!
|
||||
"The reason for the media data change"
|
||||
reason: String
|
||||
"The type of notification"
|
||||
type: NotificationType
|
||||
}
|
||||
|
||||
"Notification for when a media tracked in a user's list is deleted from the site"
|
||||
type MediaDeletionNotification {
|
||||
"The reason for the media deletion"
|
||||
context: String
|
||||
"The time the notification was created at"
|
||||
createdAt: Int
|
||||
"The title of the deleted media"
|
||||
deletedMediaTitle: String
|
||||
"The id of the Notification"
|
||||
id: Int!
|
||||
"The reason for the media deletion"
|
||||
reason: String
|
||||
"The type of notification"
|
||||
type: NotificationType
|
||||
}
|
||||
|
||||
"Media connection edge"
|
||||
type MediaEdge {
|
||||
"Media specific character name"
|
||||
@ -1298,13 +1353,13 @@ type MediaList {
|
||||
"List of anime or manga"
|
||||
type MediaListCollection {
|
||||
"A map of media list entry arrays grouped by custom lists"
|
||||
customLists(asArray: Boolean): [[MediaList]] @deprecated(reason : "Not GraphQL spec compliant, use lists field instead.")
|
||||
customLists(asArray: Boolean): [[MediaList]] @deprecated(reason: "Not GraphQL spec compliant, use lists field instead.")
|
||||
"If there is another chunk"
|
||||
hasNextChunk: Boolean
|
||||
"Grouped media list entries"
|
||||
lists: [MediaListGroup]
|
||||
"A map of media list entry arrays grouped by status"
|
||||
statusLists(asArray: Boolean): [[MediaList]] @deprecated(reason : "Not GraphQL spec compliant, use lists field instead.")
|
||||
statusLists(asArray: Boolean): [[MediaList]] @deprecated(reason: "Not GraphQL spec compliant, use lists field instead.")
|
||||
"The owner of the list"
|
||||
user: User
|
||||
}
|
||||
@ -1330,10 +1385,10 @@ type MediaListOptions {
|
||||
"The score format the user is using for media lists"
|
||||
scoreFormat: ScoreFormat
|
||||
"The list theme options for both lists"
|
||||
sharedTheme: Json @deprecated(reason : "No longer used")
|
||||
sharedTheme: Json @deprecated(reason: "No longer used")
|
||||
"If the shared theme should be used instead of the individual list themes"
|
||||
sharedThemeEnabled: Boolean @deprecated(reason : "No longer used")
|
||||
useLegacyLists: Boolean @deprecated(reason : "No longer used")
|
||||
sharedThemeEnabled: Boolean @deprecated(reason: "No longer used")
|
||||
useLegacyLists: Boolean @deprecated(reason: "No longer used")
|
||||
}
|
||||
|
||||
"A user's list options for anime or manga lists"
|
||||
@ -1349,7 +1404,27 @@ type MediaListTypeOptions {
|
||||
"If the completed sections of the list should be separated by format"
|
||||
splitCompletedSectionByFormat: Boolean
|
||||
"The list theme options"
|
||||
theme: Json @deprecated(reason : "This field has not yet been fully implemented and may change without warning")
|
||||
theme: Json @deprecated(reason: "This field has not yet been fully implemented and may change without warning")
|
||||
}
|
||||
|
||||
"Notification for when a media entry is merged into another for a user who had it on their list"
|
||||
type MediaMergeNotification {
|
||||
"The reason for the media data change"
|
||||
context: String
|
||||
"The time the notification was created at"
|
||||
createdAt: Int
|
||||
"The title of the deleted media"
|
||||
deletedMediaTitles: [String]
|
||||
"The id of the Notification"
|
||||
id: Int!
|
||||
"The media that was merged into"
|
||||
media: Media
|
||||
"The id of the media that was merged into"
|
||||
mediaId: Int!
|
||||
"The reason for the media merge"
|
||||
reason: String
|
||||
"The type of notification"
|
||||
type: NotificationType
|
||||
}
|
||||
|
||||
"The ranking of a media in a particular time span and format compared to other media"
|
||||
@ -1374,7 +1449,7 @@ type MediaRank {
|
||||
|
||||
"A media's statistics"
|
||||
type MediaStats {
|
||||
airingProgression: [AiringProgression] @deprecated(reason : "Replaced by MediaTrends")
|
||||
airingProgression: [AiringProgression] @deprecated(reason: "Replaced by MediaTrends")
|
||||
scoreDistribution: [ScoreDistribution]
|
||||
statusDistribution: [StatusDistribution]
|
||||
}
|
||||
@ -1393,12 +1468,16 @@ type MediaStreamingEpisode {
|
||||
|
||||
"Media submission"
|
||||
type MediaSubmission {
|
||||
"Data Mod assigned to handle the submission"
|
||||
assignee: User
|
||||
changes: [String]
|
||||
characters: [MediaSubmissionComparison]
|
||||
createdAt: Int
|
||||
externalLinks: [MediaExternalLink]
|
||||
"The id of the submission"
|
||||
id: Int!
|
||||
"Whether the submission is locked"
|
||||
locked: Boolean
|
||||
media: Media
|
||||
notes: String
|
||||
relations: [MediaEdge]
|
||||
@ -1458,6 +1537,8 @@ type MediaTag {
|
||||
name: String!
|
||||
"The relevance ranking of the tag out of the 100 for this media"
|
||||
rank: Int
|
||||
"The user who submitted the tag"
|
||||
userId: Int
|
||||
}
|
||||
|
||||
"The official titles of the media in various languages"
|
||||
@ -1738,6 +1819,8 @@ type Mutation {
|
||||
comment: String,
|
||||
"The comment id, required for updating"
|
||||
id: Int,
|
||||
"If the comment tree should be locked. (Mod Only)"
|
||||
locked: Boolean,
|
||||
"The id of thread comment to reply to"
|
||||
parentCommentId: Int,
|
||||
"The id of thread the comment belongs to"
|
||||
@ -1872,6 +1955,8 @@ type Mutation {
|
||||
rowOrder: String,
|
||||
"The user's list scoring system"
|
||||
scoreFormat: ScoreFormat,
|
||||
"The language the user wants to see staff and character names in"
|
||||
staffNameLanguage: UserStaffNameLanguage,
|
||||
"Timezone offset format: -?HH:MM"
|
||||
timezone: String,
|
||||
"User's title language"
|
||||
@ -2094,6 +2179,8 @@ type Page {
|
||||
id_not_in: [Int],
|
||||
"Filter by if the media's intended for 18+ adult audiences"
|
||||
isAdult: Boolean,
|
||||
"If the media is officially licensed or a self-published doujin release"
|
||||
isLicensed: Boolean,
|
||||
"Filter media by sites with a online streaming or reading license"
|
||||
licensedBy: String,
|
||||
"Filter media by sites with a online streaming or reading license"
|
||||
@ -2368,6 +2455,8 @@ type Page {
|
||||
users(
|
||||
"Filter by the user id"
|
||||
id: Int,
|
||||
"Filter to moderators only if true"
|
||||
isModerator: Boolean,
|
||||
"Filter by the name of the user"
|
||||
name: String,
|
||||
"Filter by search query"
|
||||
@ -2618,6 +2707,8 @@ type Query {
|
||||
id_not_in: [Int],
|
||||
"Filter by if the media's intended for 18+ adult audiences"
|
||||
isAdult: Boolean,
|
||||
"If the media is officially licensed or a self-published doujin release"
|
||||
isLicensed: Boolean,
|
||||
"Filter media by sites with a online streaming or reading license"
|
||||
licensedBy: String,
|
||||
"Filter media by sites with a online streaming or reading license"
|
||||
@ -2958,6 +3049,8 @@ type Query {
|
||||
User(
|
||||
"Filter by the user id"
|
||||
id: Int,
|
||||
"Filter to moderators only if true"
|
||||
isModerator: Boolean,
|
||||
"Filter by the name of the user"
|
||||
name: String,
|
||||
"Filter by search query"
|
||||
@ -3014,6 +3107,7 @@ type RelatedMediaAdditionNotification {
|
||||
}
|
||||
|
||||
type Report {
|
||||
cleared: Boolean
|
||||
"When the entry data was created"
|
||||
createdAt: Int
|
||||
id: Int!
|
||||
@ -3179,6 +3273,8 @@ type SiteTrendEdge {
|
||||
type Staff {
|
||||
"The person's age in years"
|
||||
age: Int
|
||||
"The persons blood type"
|
||||
bloodType: String
|
||||
"Media the actor voiced characters in. (Same data as characters with media as node instead of characters)"
|
||||
characterMedia(
|
||||
onList: Boolean,
|
||||
@ -3218,7 +3314,7 @@ type Staff {
|
||||
"If the staff member is blocked from being added to favourites"
|
||||
isFavouriteBlocked: Boolean!
|
||||
"The primary language the staff member dub's in"
|
||||
language: StaffLanguage @deprecated(reason : "Replaced with languageV2")
|
||||
language: StaffLanguage @deprecated(reason: "Replaced with languageV2")
|
||||
"The primary language of the staff member. Current values: Japanese, English, Korean, Italian, Spanish, Portuguese, French, German, Hebrew, Hungarian, Chinese, Arabic, Filipino, Catalan"
|
||||
languageV2: String
|
||||
"Notes for site moderators"
|
||||
@ -3247,7 +3343,7 @@ type Staff {
|
||||
submissionStatus: Int
|
||||
"Submitter for the submission"
|
||||
submitter: User
|
||||
updatedAt: Int @deprecated(reason : "No data available")
|
||||
updatedAt: Int @deprecated(reason: "No data available")
|
||||
"[startYear, endYear] (If the 2nd value is not present staff is still active)"
|
||||
yearsActive: [Int]
|
||||
}
|
||||
@ -3291,6 +3387,8 @@ type StaffName {
|
||||
middle: String
|
||||
"The person's full name in their native language"
|
||||
native: String
|
||||
"The currently authenticated users preferred name language. Default romaji for non-authenticated"
|
||||
userPreferred: String
|
||||
}
|
||||
|
||||
"Voice actor role for a character"
|
||||
@ -3314,9 +3412,13 @@ type StaffStats {
|
||||
|
||||
"A submission for a staff that features in an anime or manga"
|
||||
type StaffSubmission {
|
||||
"Data Mod assigned to handle the submission"
|
||||
assignee: User
|
||||
createdAt: Int
|
||||
"The id of the submission"
|
||||
id: Int!
|
||||
"Whether the submission is locked"
|
||||
locked: Boolean
|
||||
"Inner details of submission status"
|
||||
notes: String
|
||||
source: String
|
||||
@ -3511,6 +3613,8 @@ type ThreadComment {
|
||||
id: Int!
|
||||
"If the currently authenticated user liked the comment"
|
||||
isLiked: Boolean
|
||||
"If the comment tree is locked and may not receive replies or edits"
|
||||
isLocked: Boolean
|
||||
"The amount of likes the comment has"
|
||||
likeCount: Int!
|
||||
"The users who liked the comment"
|
||||
@ -3651,6 +3755,8 @@ type User {
|
||||
"The user's banner images"
|
||||
bannerImage: String
|
||||
bans: Json
|
||||
"When the user's account was created. (Does not exist for accounts created before 2020)"
|
||||
createdAt: Int
|
||||
"Custom donation badge text"
|
||||
donatorBadge: String
|
||||
"The donation tier of the user"
|
||||
@ -3670,18 +3776,22 @@ type User {
|
||||
isFollowing: Boolean
|
||||
"The user's media list options"
|
||||
mediaListOptions: MediaListOptions
|
||||
"The user's moderator roles if they are a site moderator"
|
||||
moderatorRoles: [ModRole]
|
||||
"If the user is a moderator or data moderator"
|
||||
moderatorStatus: String
|
||||
moderatorStatus: String @deprecated(reason: "Deprecated. Replaced with moderatorRoles field.")
|
||||
"The name of the user"
|
||||
name: String!
|
||||
"The user's general options"
|
||||
options: UserOptions
|
||||
"The user's previously used names."
|
||||
previousNames: [UserPreviousName]
|
||||
"The url for the user page on the AniList website"
|
||||
siteUrl: String
|
||||
"The users anime & manga list statistics"
|
||||
statistics: UserStatisticTypes
|
||||
"The user's statistics"
|
||||
stats: UserStats @deprecated(reason : "Deprecated. Replaced with statistics field.")
|
||||
stats: UserStats @deprecated(reason: "Deprecated. Replaced with statistics field.")
|
||||
"The number of unread notifications the user has"
|
||||
unreadNotificationCount: Int
|
||||
"When the user's data was last updated"
|
||||
@ -3747,7 +3857,9 @@ type UserModData {
|
||||
alts: [User]
|
||||
bans: Json
|
||||
counts: Json
|
||||
email: String
|
||||
ip: Json
|
||||
privacy: Int
|
||||
}
|
||||
|
||||
"A user's general options"
|
||||
@ -3762,12 +3874,24 @@ type UserOptions {
|
||||
notificationOptions: [NotificationOption]
|
||||
"Profile highlight color (blue, purple, pink, orange, red, green, gray)"
|
||||
profileColor: String
|
||||
"The language the user wants to see staff and character names in"
|
||||
staffNameLanguage: UserStaffNameLanguage
|
||||
"The user's timezone offset (Auth user only)"
|
||||
timezone: String
|
||||
"The language the user wants to see media titles in"
|
||||
titleLanguage: UserTitleLanguage
|
||||
}
|
||||
|
||||
"A user's previous name"
|
||||
type UserPreviousName {
|
||||
"When the user first changed from this name."
|
||||
createdAt: Int
|
||||
"A previous name of the user."
|
||||
name: String
|
||||
"When the user most recently changed from this name."
|
||||
updatedAt: Int
|
||||
}
|
||||
|
||||
type UserReleaseYearStatistic {
|
||||
chaptersRead: Int!
|
||||
count: Int!
|
||||
@ -4127,24 +4251,36 @@ enum MediaSort {
|
||||
|
||||
"Source type the media was adapted from"
|
||||
enum MediaSource {
|
||||
"Version 2 only. Japanese Anime"
|
||||
"Version 2+ only. Japanese Anime"
|
||||
ANIME
|
||||
"Version 2 only. Self-published works"
|
||||
"Version 3 only. Comics excluding manga"
|
||||
COMIC
|
||||
"Version 2+ only. Self-published works"
|
||||
DOUJINSHI
|
||||
"Version 3 only. Games excluding video games"
|
||||
GAME
|
||||
"Written work published in volumes"
|
||||
LIGHT_NOVEL
|
||||
"Version 3 only. Live action media such as movies or TV show"
|
||||
LIVE_ACTION
|
||||
"Asian comic book"
|
||||
MANGA
|
||||
"Version 2 only. Written works not published in volumes"
|
||||
"Version 3 only. Multimedia project"
|
||||
MULTIMEDIA_PROJECT
|
||||
"Version 2+ only. Written works not published in volumes"
|
||||
NOVEL
|
||||
"An original production not based of another work"
|
||||
ORIGINAL
|
||||
"Other"
|
||||
OTHER
|
||||
"Version 3 only. Picture book"
|
||||
PICTURE_BOOK
|
||||
"Video game"
|
||||
VIDEO_GAME
|
||||
"Video game driven primary by text and narrative"
|
||||
VISUAL_NOVEL
|
||||
"Version 3 only. Written works published online"
|
||||
WEB_NOVEL
|
||||
}
|
||||
|
||||
"The current releasing status of the media"
|
||||
@ -4198,6 +4334,36 @@ enum ModActionType {
|
||||
RESET
|
||||
}
|
||||
|
||||
"Mod role enums"
|
||||
enum ModRole {
|
||||
"An AniList administrator"
|
||||
ADMIN
|
||||
"An anime data moderator"
|
||||
ANIME_DATA
|
||||
"A community moderator"
|
||||
COMMUNITY
|
||||
"An AniList developer"
|
||||
DEVELOPER
|
||||
"A discord community moderator"
|
||||
DISCORD_COMMUNITY
|
||||
"A lead anime data moderator"
|
||||
LEAD_ANIME_DATA
|
||||
"A lead community moderator"
|
||||
LEAD_COMMUNITY
|
||||
"A head developer of AniList"
|
||||
LEAD_DEVELOPER
|
||||
"A lead manga data moderator"
|
||||
LEAD_MANGA_DATA
|
||||
"A lead social media moderator"
|
||||
LEAD_SOCIAL_MEDIA
|
||||
"A manga data moderator"
|
||||
MANGA_DATA
|
||||
"A retired moderator"
|
||||
RETIRED
|
||||
"A social media moderator"
|
||||
SOCIAL_MEDIA
|
||||
}
|
||||
|
||||
"Notification type enum"
|
||||
enum NotificationType {
|
||||
"A user has liked your activity"
|
||||
@ -4216,6 +4382,12 @@ enum NotificationType {
|
||||
AIRING
|
||||
"A user has followed you"
|
||||
FOLLOWING
|
||||
"An anime or manga has had a data change that affects how a user may track it in their lists"
|
||||
MEDIA_DATA_CHANGE
|
||||
"An anime or manga on the user's list has been deleted from the site"
|
||||
MEDIA_DELETION
|
||||
"Anime or manga entries on the user's list have been merged into a single entry"
|
||||
MEDIA_MERGE
|
||||
"A new anime or manga has been added to the site where its related media is on the user's list"
|
||||
RELATED_MEDIA_ADDITION
|
||||
"A user has liked your forum comment"
|
||||
@ -4399,6 +4571,16 @@ enum UserSort {
|
||||
WATCHED_TIME_DESC
|
||||
}
|
||||
|
||||
"The language the user wants to see staff and character names in"
|
||||
enum UserStaffNameLanguage {
|
||||
"The staff or character's name in their native language"
|
||||
NATIVE
|
||||
"The romanization of the staff or character's native name"
|
||||
ROMAJI
|
||||
"The romanization of the staff or character's native name, with western name ordering"
|
||||
ROMAJI_WESTERN
|
||||
}
|
||||
|
||||
"User statistics sort enum"
|
||||
enum UserStatisticsSort {
|
||||
COUNT
|
||||
@ -4427,6 +4609,14 @@ enum UserTitleLanguage {
|
||||
ROMAJI_STYLISED
|
||||
}
|
||||
|
||||
"ISO 3166-1 alpha-2 country code"
|
||||
scalar CountryCode
|
||||
|
||||
"8 digit long date integer (YYYYMMDD). Unknown dates represented by 0. E.g. 2016: 20160000, May 1976: 19760500"
|
||||
scalar FuzzyDateInt
|
||||
|
||||
scalar Json
|
||||
|
||||
input AiringScheduleInput {
|
||||
airingAt: Int
|
||||
episode: Int
|
||||
@ -4521,12 +4711,3 @@ input StaffNameInput {
|
||||
"The person's full name in their native language"
|
||||
native: String
|
||||
}
|
||||
|
||||
|
||||
scalar Json
|
||||
|
||||
"ISO 3166-1 alpha-2 country code"
|
||||
scalar CountryCode
|
||||
|
||||
"8 digit long date integer (YYYYMMDD). Unknown dates represented by 0. E.g. 2016: 20160000, May 1976: 19760500"
|
||||
scalar FuzzyDateInt
|
@ -17,7 +17,6 @@
|
||||
namespace Aviat\AnimeClient\API;
|
||||
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Psr\SimpleCache\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Helper methods for dealing with the Cache
|
||||
|
@ -23,7 +23,6 @@ use const Aviat\AnimeClient\SESSION_SEGMENT;
|
||||
use Aviat\AnimeClient\Kitsu as K;
|
||||
use Aviat\AnimeClient\API\CacheTrait;
|
||||
use Aviat\Ion\Di\{ContainerAware, ContainerInterface};
|
||||
use Aviat\Ion\Di\Exception\{ContainerException, NotFoundException};
|
||||
use Aviat\Ion\Event;
|
||||
|
||||
/**
|
||||
@ -129,11 +128,11 @@ final class Auth {
|
||||
{
|
||||
if (PHP_SAPI === 'cli')
|
||||
{
|
||||
return $this->segment->get('auth_token', NULL)
|
||||
?? $this->cache->get(K::AUTH_TOKEN_CACHE_KEY, NULL);
|
||||
return $this->segment->get('auth_token')
|
||||
?? $this->cache->get(K::AUTH_TOKEN_CACHE_KEY);
|
||||
}
|
||||
|
||||
return $this->segment->get('auth_token', NULL);
|
||||
return $this->segment->get('auth_token');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -146,7 +145,7 @@ final class Auth {
|
||||
if (PHP_SAPI === 'cli')
|
||||
{
|
||||
return $this->segment->get('refresh_token')
|
||||
?? $this->cache->get(K::AUTH_TOKEN_REFRESH_CACHE_KEY, NULL);
|
||||
?? $this->cache->get(K::AUTH_TOKEN_REFRESH_CACHE_KEY);
|
||||
}
|
||||
|
||||
return $this->segment->get('refresh_token');
|
||||
|
@ -16,17 +16,10 @@
|
||||
|
||||
namespace Aviat\AnimeClient\API\Kitsu;
|
||||
|
||||
use Aviat\Ion\Di\Exception\ContainerException;
|
||||
use Aviat\Ion\Di\Exception\NotFoundException;
|
||||
|
||||
use function Amp\Promise\wait;
|
||||
use function Aviat\AnimeClient\getResponse;
|
||||
|
||||
use Amp\Http\Client\Request;
|
||||
use Aviat\AnimeClient\API\AbstractListItem;
|
||||
use Aviat\AnimeClient\Types\FormItemData;
|
||||
use Aviat\Ion\Di\ContainerAware;
|
||||
use Aviat\Ion\Json;
|
||||
|
||||
use Throwable;
|
||||
|
||||
|
@ -44,6 +44,7 @@ use Aviat\Ion\{
|
||||
use Generator;
|
||||
use function Amp\Promise\wait;
|
||||
use function Aviat\AnimeClient\getApiClient;
|
||||
use const Aviat\AnimeClient\SESSION_SEGMENT;
|
||||
|
||||
/**
|
||||
* Kitsu API Model
|
||||
@ -533,6 +534,7 @@ final class Model {
|
||||
$searchItem = [
|
||||
'id' => $item['id'],
|
||||
'slug' => $item['slug'],
|
||||
'coverImage' => K::getPosterImage($item),
|
||||
'canonicalTitle' => $item['titles']['canonical'],
|
||||
'titles' => array_values(K::getTitles($item['titles'])),
|
||||
'libraryEntry' => $item['myLibraryEntry'],
|
||||
@ -707,9 +709,14 @@ final class Model {
|
||||
|
||||
$rawData = Json::decode($json);
|
||||
$data = $rawData['data']['findProfileBySlug']['library']['all'] ?? [];
|
||||
$page = $data['pageInfo'];
|
||||
$page = $data['pageInfo'] ?? [];
|
||||
if (empty($data))
|
||||
{
|
||||
// Clear session, in case the error is an invalid token.
|
||||
$segment = $this->container->get('session')
|
||||
->getSegment(SESSION_SEGMENT);
|
||||
$segment->clear();
|
||||
|
||||
// @TODO Proper Error logging
|
||||
dump($rawData);
|
||||
die();
|
||||
@ -719,7 +726,7 @@ final class Model {
|
||||
|
||||
yield $emit($data['nodes']);
|
||||
|
||||
if ($page['hasNextPage'] === FALSE)
|
||||
if ($page['hasNextPage'] !== TRUE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ namespace Aviat\AnimeClient\API\Kitsu;
|
||||
|
||||
use Amp\Http\Client\Request;
|
||||
use Aviat\AnimeClient\Types\FormItem;
|
||||
use Aviat\Banker\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Kitsu API calls that mutate data, C/U/D parts of CRUD
|
||||
|
@ -40,12 +40,10 @@ query ($slug: String!) {
|
||||
}
|
||||
type
|
||||
}
|
||||
voices(first: 100) {
|
||||
role
|
||||
voices(first: 10) {
|
||||
nodes {
|
||||
id
|
||||
licensor {
|
||||
name
|
||||
}
|
||||
locale
|
||||
person {
|
||||
id
|
||||
|
@ -19,6 +19,11 @@ query ($slug: String!) {
|
||||
id
|
||||
slug
|
||||
posterImage {
|
||||
original {
|
||||
width
|
||||
height
|
||||
url
|
||||
}
|
||||
views {
|
||||
width
|
||||
height
|
||||
|
@ -52,7 +52,7 @@ query ($slug: String!) {
|
||||
}
|
||||
}
|
||||
}
|
||||
voices(first: 100) {
|
||||
voices(first: 500) {
|
||||
nodes {
|
||||
locale
|
||||
mediaCharacter {
|
||||
|
@ -2,6 +2,14 @@ query ($query: String!) {
|
||||
searchAnimeByTitle(title: $query, first: 20) {
|
||||
nodes {
|
||||
id
|
||||
posterImage {
|
||||
original {
|
||||
url
|
||||
}
|
||||
views {
|
||||
url
|
||||
}
|
||||
}
|
||||
mappings(first: 10) {
|
||||
nodes {
|
||||
externalId
|
||||
|
@ -2,6 +2,14 @@ query ($query: String!) {
|
||||
searchMangaByTitle(title: $query, first: 20) {
|
||||
nodes {
|
||||
id
|
||||
posterImage {
|
||||
original {
|
||||
url
|
||||
}
|
||||
views {
|
||||
url
|
||||
}
|
||||
}
|
||||
mappings(first: 10) {
|
||||
nodes {
|
||||
externalId
|
||||
|
@ -102,6 +102,11 @@ query ($slug: String!) {
|
||||
original {
|
||||
url
|
||||
}
|
||||
views {
|
||||
url
|
||||
height
|
||||
width
|
||||
}
|
||||
}
|
||||
names {
|
||||
alternatives
|
||||
|
@ -192,16 +192,13 @@ final class RequestBuilder extends APIRequestBuilder {
|
||||
$request = $this->setUpRequest($type, $url, $options);
|
||||
$response = getResponse($request);
|
||||
|
||||
if ($logger !== NULL)
|
||||
{
|
||||
$logger->debug('Kitsu API Response', [
|
||||
'status' => $response->getStatus(),
|
||||
'reason' => $response->getReason(),
|
||||
'body' => $response->getBody(),
|
||||
'headers' => $response->getHeaders(),
|
||||
'requestHeaders' => $request->getHeaders(),
|
||||
]);
|
||||
}
|
||||
$logger?->debug('Kitsu API Response', [
|
||||
'status' => $response->getStatus(),
|
||||
'reason' => $response->getReason(),
|
||||
'body' => $response->getBody(),
|
||||
'headers' => $response->getHeaders(),
|
||||
'requestHeaders' => $request->getHeaders(),
|
||||
]);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ trait RequestBuilderTrait {
|
||||
* Set the request builder object
|
||||
*
|
||||
* @param RequestBuilder $requestBuilder
|
||||
* @return $this
|
||||
* @return RequestBuilderTrait|ListItem|Model
|
||||
*/
|
||||
public function setRequestBuilder(RequestBuilder $requestBuilder): self
|
||||
{
|
||||
|
@ -96,7 +96,7 @@ final class AnimeListTransformer extends AbstractTransformer {
|
||||
'titles' => $titles,
|
||||
'slug' => $anime['slug'],
|
||||
'show_type' => (string)StringType::from($anime['subtype'])->upperCaseFirst(),
|
||||
'cover_image' => $anime['posterImage']['views'][1]['url'],
|
||||
'cover_image' => Kitsu::getPosterImage($anime),
|
||||
'genres' => $genres,
|
||||
'streaming_links' => $streamingLinks,
|
||||
],
|
||||
|
@ -59,7 +59,7 @@ final class AnimeTransformer extends AbstractTransformer {
|
||||
|
||||
$details = $rawCharacter['character'];
|
||||
$characters[$type][$details['id']] = [
|
||||
'image' => $details['image'],
|
||||
'image' => $details['image']['original']['url'] ?? '',
|
||||
'name' => $details['names']['canonical'],
|
||||
'slug' => $details['slug'],
|
||||
];
|
||||
@ -103,9 +103,7 @@ final class AnimeTransformer extends AbstractTransformer {
|
||||
$staff[$role][$person['id']] = [
|
||||
'id' => $person['id'],
|
||||
'name' => $name,
|
||||
'image' => [
|
||||
'original' => $person['image']['original']['url'] ?? '',
|
||||
],
|
||||
'image' => $person['image']['original']['url'],
|
||||
'slug' => $person['slug'],
|
||||
];
|
||||
|
||||
@ -125,7 +123,7 @@ final class AnimeTransformer extends AbstractTransformer {
|
||||
'age_rating' => $base['ageRating'],
|
||||
'age_rating_guide' => $base['ageRatingGuide'],
|
||||
'characters' => $characters,
|
||||
'cover_image' => $base['posterImage']['views'][1]['url'],
|
||||
'cover_image' => Kitsu::getPosterImage($base),
|
||||
'episode_count' => $base['episodeCount'],
|
||||
'episode_length' => $base['episodeLength'],
|
||||
'genres' => $genres,
|
||||
@ -136,7 +134,7 @@ final class AnimeTransformer extends AbstractTransformer {
|
||||
'show_type' => $base['subtype'],
|
||||
'status' => Kitsu::getAiringStatus($base['startDate'], $base['endDate']),
|
||||
'streaming_links' => Kitsu::parseStreamingLinks($base['streamingLinks']['nodes'] ?? []),
|
||||
'synopsis' => $base['description']['en'],
|
||||
'synopsis' => $base['description']['en'] ?? '',
|
||||
'title' => $title,
|
||||
'titles' => $titles,
|
||||
'titles_more' => $titles_more,
|
||||
|
@ -58,6 +58,7 @@ final class CharacterTransformer extends AbstractTransformer {
|
||||
'castings' => $castings,
|
||||
'description' => $data['description']['en'],
|
||||
'id' => $data['id'],
|
||||
'image' => $data['image']['original']['url'] ?? 'images/placeholder.png',
|
||||
'media' => $media,
|
||||
'name' => $name,
|
||||
'names' => $names,
|
||||
@ -147,7 +148,7 @@ final class CharacterTransformer extends AbstractTransformer {
|
||||
'slug' => $voiceMap['media']['slug'],
|
||||
'title' => $voiceMap['media']['titles']['canonical'],
|
||||
'titles' => Kitsu::getFilteredTitles($voiceMap['media']['titles']),
|
||||
'posterImage' => $voiceMap['media']['posterImage']['views'][1]['url'],
|
||||
'posterImage' => Kitsu::getPosterImage($voiceMap['media']),
|
||||
];
|
||||
|
||||
uasort($castings['Voice Actor'][$lang][$id]['series'], $titleSort);
|
||||
|
@ -17,6 +17,7 @@
|
||||
namespace Aviat\AnimeClient\API\Kitsu\Transformer;
|
||||
|
||||
use Aviat\AnimeClient\Types\HistoryItem;
|
||||
use Aviat\AnimeClient\Kitsu;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use DateTimeZone;
|
||||
@ -184,10 +185,8 @@ abstract class HistoryTransformer {
|
||||
|
||||
protected function transformProgress (array $entry): ?HistoryItem
|
||||
{
|
||||
$id = $entry['media']['id'];
|
||||
$data = $entry['media'];
|
||||
$title = $this->linkTitle($data);
|
||||
$imgUrl = "images/{$this->type}/{$id}.webp";
|
||||
$item = end($entry['changedData']['progress']);
|
||||
|
||||
// No showing episode 0 nonsense
|
||||
@ -215,7 +214,7 @@ abstract class HistoryTransformer {
|
||||
|
||||
return HistoryItem::from([
|
||||
'action' => $action,
|
||||
'coverImg' => $imgUrl,
|
||||
'coverImg' => Kitsu::getPosterImage($data, 0),
|
||||
'kind' => 'progressed',
|
||||
'original' => $entry,
|
||||
'title' => $title,
|
||||
@ -226,10 +225,8 @@ abstract class HistoryTransformer {
|
||||
|
||||
protected function transformUpdated(array $entry): HistoryItem
|
||||
{
|
||||
$id = $entry['media']['id'];
|
||||
$data = $entry['media'];
|
||||
$title = $this->linkTitle($data);
|
||||
$imgUrl = "images/{$this->type}/{$id}.webp";
|
||||
|
||||
$kind = array_key_first($entry['changedData']);
|
||||
|
||||
@ -247,7 +244,7 @@ abstract class HistoryTransformer {
|
||||
|
||||
return HistoryItem::from([
|
||||
'action' => $statusName,
|
||||
'coverImg' => $imgUrl,
|
||||
'coverImg' => Kitsu::getPosterImage($data, 0),
|
||||
'kind' => 'updated',
|
||||
'original' => $entry,
|
||||
'title' => $title,
|
||||
|
@ -102,7 +102,7 @@ final class LibraryEntryTransformer extends AbstractTransformer
|
||||
'titles' => $titles,
|
||||
'slug' => $anime['slug'],
|
||||
'show_type' => (string)StringType::from($anime['subtype'])->upperCaseFirst(),
|
||||
'cover_image' => $anime['posterImage']['views'][1]['url'],
|
||||
'cover_image' => Kitsu::getPosterImage($anime),
|
||||
'genres' => $genres,
|
||||
'streaming_links' => $streamingLinks,
|
||||
],
|
||||
@ -167,7 +167,7 @@ final class LibraryEntryTransformer extends AbstractTransformer
|
||||
'manga' => MangaListItemDetail::from([
|
||||
'genres' => $genres,
|
||||
'id' => $mangaId,
|
||||
'image' => $manga['posterImage']['views'][1]['url'],
|
||||
'image' => Kitsu::getPosterImage($manga),
|
||||
'slug' => $manga['slug'],
|
||||
'title' => $title,
|
||||
'titles' => $titles,
|
||||
|
@ -91,7 +91,7 @@ final class MangaListTransformer extends AbstractTransformer {
|
||||
'manga' => MangaListItemDetail::from([
|
||||
'genres' => $genres,
|
||||
'id' => $mangaId,
|
||||
'image' => $manga['posterImage']['views'][1]['url'],
|
||||
'image' => Kitsu::getPosterImage($manga),
|
||||
'slug' => $manga['slug'],
|
||||
'title' => $title,
|
||||
'titles' => $titles,
|
||||
|
@ -58,7 +58,7 @@ final class MangaTransformer extends AbstractTransformer {
|
||||
|
||||
$details = $rawCharacter['character'];
|
||||
$characters[$type][$details['id']] = [
|
||||
'image' => $details['image'],
|
||||
'image' => $details['image']['original']['url'],
|
||||
'name' => $details['names']['canonical'],
|
||||
'slug' => $details['slug'],
|
||||
];
|
||||
@ -103,9 +103,7 @@ final class MangaTransformer extends AbstractTransformer {
|
||||
'id' => $person['id'],
|
||||
'slug' => $person['slug'],
|
||||
'name' => $name,
|
||||
'image' => [
|
||||
'original' => $person['image']['original']['url'] ?? '',
|
||||
],
|
||||
'image' => $person['image']['original']['url'],
|
||||
];
|
||||
|
||||
usort($staff[$role], fn ($a, $b) => $a['name'] <=> $b['name']);
|
||||
@ -125,7 +123,7 @@ final class MangaTransformer extends AbstractTransformer {
|
||||
'characters' => $characters,
|
||||
'chapter_count' => $base['chapterCount'],
|
||||
'volume_count' => $base['volumeCount'],
|
||||
'cover_image' => $base['posterImage']['views'][1]['url'],
|
||||
'cover_image' => Kitsu::getPosterImage($base),
|
||||
'genres' => $genres,
|
||||
'links' => $links,
|
||||
'manga_type' => $base['subtype'],
|
||||
|
@ -41,6 +41,7 @@ final class PersonTransformer extends AbstractTransformer {
|
||||
return Person::from([
|
||||
'id' => $data['id'],
|
||||
'name' => $canonicalName,
|
||||
'image' => $data['image']['original']['url'],
|
||||
'names' => array_diff($data['names']['localized'], [$canonicalName]),
|
||||
'description' => $data['description']['en'] ?? '',
|
||||
'characters' => $orgData['characters'],
|
||||
@ -83,9 +84,7 @@ final class PersonTransformer extends AbstractTransformer {
|
||||
'id' => $media['id'],
|
||||
'title' => $title,
|
||||
'titles' => array_merge([$title], Kitsu::getFilteredTitles($media['titles'])),
|
||||
'image' => [
|
||||
'original' => $media['posterImage']['views'][1]['url'] ?? '',
|
||||
],
|
||||
'image' => Kitsu::getPosterImage($media),
|
||||
'slug' => $media['slug'],
|
||||
];
|
||||
|
||||
@ -107,6 +106,7 @@ final class PersonTransformer extends AbstractTransformer {
|
||||
$media = [
|
||||
'id' => $rawMedia['id'],
|
||||
'slug' => $rawMedia['slug'],
|
||||
'image' => Kitsu::getPosterImage($rawMedia),
|
||||
'titles' => array_merge(
|
||||
[$rawMedia['titles']['canonical']],
|
||||
Kitsu::getFilteredTitles($rawMedia['titles']),
|
||||
@ -124,9 +124,7 @@ final class PersonTransformer extends AbstractTransformer {
|
||||
'character' => [
|
||||
'id' => $character['id'],
|
||||
'slug' => $character['slug'],
|
||||
'image' => [
|
||||
'original' => $character['image']['original']['url'] ?? '',
|
||||
],
|
||||
'image' => $character['image']['original']['url'],
|
||||
'canonicalName' => $character['names']['canonical'],
|
||||
],
|
||||
'media' => [
|
||||
|
@ -42,7 +42,7 @@ final class UserTransformer extends AbstractTransformer {
|
||||
|
||||
return User::from([
|
||||
'about' => $base['about'],
|
||||
'avatar' => getLocalImg($base['avatarImage']['original']['url'], FALSE),
|
||||
'avatar' => $base['avatarImage']['original']['url'],
|
||||
'favorites' => $this->organizeFavorites($favorites),
|
||||
'location' => $base['location'],
|
||||
'name' => $base['name'],
|
||||
|
@ -16,9 +16,8 @@
|
||||
|
||||
namespace Aviat\AnimeClient;
|
||||
|
||||
use Aviat\AnimeClient\Kitsu;
|
||||
use Aviat\Ion\ImageBuilder;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Psr\SimpleCache\InvalidArgumentException;
|
||||
|
||||
use Amp\Http\Client\Request;
|
||||
use Amp\Http\Client\Response;
|
||||
@ -151,6 +150,21 @@ function tomlToArray(string $toml): array
|
||||
//! Misc Functions
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
if ( ! function_exists('array_is_list'))
|
||||
{
|
||||
/**
|
||||
* Polyfill for PHP 8
|
||||
*
|
||||
* @see https://www.php.net/manual/en/function.array-is-list
|
||||
* @param array $a
|
||||
* @return bool
|
||||
*/
|
||||
function array_is_list(array $a): bool
|
||||
{
|
||||
return $a === [] || (array_keys($a) === range(0, count($a) - 1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the array sequential, not associative?
|
||||
*
|
||||
@ -164,15 +178,7 @@ function isSequentialArray(mixed $array): bool
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
foreach ($array as $k => $v)
|
||||
{
|
||||
if ($k !== $i++)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
return array_is_list($array);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -263,7 +269,7 @@ function getResponse (Request|string $request): Response
|
||||
* @param bool $webp
|
||||
* @return string
|
||||
*/
|
||||
function getLocalImg (string $kitsuUrl, $webp = TRUE): string
|
||||
function getLocalImg (string $kitsuUrl, bool $webp = TRUE): string
|
||||
{
|
||||
if (empty($kitsuUrl) || ( ! is_string($kitsuUrl)))
|
||||
{
|
||||
@ -296,71 +302,26 @@ function getLocalImg (string $kitsuUrl, $webp = TRUE): string
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @param string $path
|
||||
* @param int|null $width
|
||||
* @param int|null $height
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @param string $text
|
||||
* @return bool
|
||||
*/
|
||||
function createPlaceholderImage (string $path, ?int $width, ?int $height, $text = 'Image Unavailable'): bool
|
||||
function createPlaceholderImage (string $path, int $width = 200, int $height = 200, string $text = 'Image Unavailable'): bool
|
||||
{
|
||||
$width = $width ?? 200;
|
||||
$height = $height ?? 200;
|
||||
|
||||
$img = imagecreatetruecolor($width, $height);
|
||||
if ($img === FALSE)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
imagealphablending($img, TRUE);
|
||||
$img = ImageBuilder::new($width, $height)
|
||||
->enableAlphaBlending(TRUE)
|
||||
->addBackgroundColor(255, 255, 255)
|
||||
->addCenteredText($text, 64, 64, 64);
|
||||
|
||||
$path = rtrim($path, '/');
|
||||
|
||||
// Background is the first color by default
|
||||
$fillColor = imagecolorallocatealpha($img, 255, 255, 255, 127);
|
||||
if ($fillColor === FALSE)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
imagefill($img, 0, 0, $fillColor);
|
||||
$savedPng = $img->savePng($path . '/placeholder.png');
|
||||
$savedWebp = $img->saveWebp($path . '/placeholder.webp');
|
||||
|
||||
$textColor = imagecolorallocate($img, 64, 64, 64);
|
||||
if ($textColor === FALSE)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
$img->cleanup();
|
||||
|
||||
imagealphablending($img, TRUE);
|
||||
|
||||
// Generate placeholder text
|
||||
$fontSize = 10;
|
||||
$fontWidth = imagefontwidth($fontSize);
|
||||
$fontHeight = imagefontheight($fontSize);
|
||||
$length = strlen($text);
|
||||
$textWidth = $length * $fontWidth;
|
||||
$fxPos = (int) ceil((imagesx($img) - $textWidth) / 2);
|
||||
$fyPos = (int) ceil((imagesy($img) - $fontHeight) / 2);
|
||||
|
||||
// Add the image text
|
||||
imagestring($img, $fontSize, $fxPos, $fyPos, $text, $textColor);
|
||||
|
||||
// Save the images
|
||||
imagesavealpha($img, TRUE);
|
||||
imagepng($img, $path . '/placeholder.png', 9);
|
||||
imagedestroy($img);
|
||||
|
||||
$pngImage = imagecreatefrompng($path . '/placeholder.png');
|
||||
if ($pngImage === FALSE)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
imagealphablending($pngImage, TRUE);
|
||||
imagesavealpha($pngImage, TRUE);
|
||||
|
||||
imagewebp($pngImage, $path . '/placeholder.webp');
|
||||
|
||||
imagedestroy($pngImage);
|
||||
|
||||
return TRUE;
|
||||
return $savedPng && $savedWebp;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
namespace Aviat\AnimeClient\Command;
|
||||
|
||||
use Aviat\AnimeClient\Kitsu;
|
||||
use Aviat\Ion\Di\Exception\ContainerException;
|
||||
use Aviat\Ion\Di\Exception\NotFoundException;
|
||||
use function Aviat\AnimeClient\clearCache;
|
||||
|
@ -203,7 +203,7 @@ final class SyncLists extends BaseCommand {
|
||||
*
|
||||
* @param string $type
|
||||
* @param array $data
|
||||
* @return array|array[]
|
||||
* @return array
|
||||
*/
|
||||
protected function compare(string $type, array $data): array
|
||||
{
|
||||
|
@ -239,7 +239,7 @@ class Controller {
|
||||
protected function renderFullPage(HtmlView $view, string $template, array $data): HtmlView
|
||||
{
|
||||
$csp = [
|
||||
"default-src 'self'",
|
||||
"default-src 'self' media.kitsu.io kitsu-production-media.s3.us-west-002.backblazeb2.com",
|
||||
"object-src 'none'",
|
||||
"child-src 'self' *.youtube.com polyfill.io",
|
||||
];
|
||||
@ -375,10 +375,10 @@ class Controller {
|
||||
* @param array $data
|
||||
* @param HtmlView|NULL $view
|
||||
* @param int $code
|
||||
* @throws InvalidArgumentException
|
||||
* @return void
|
||||
*@throws InvalidArgumentException
|
||||
*/
|
||||
protected function outputHTML(string $template, array $data = [], $view = NULL, int $code = 200): void
|
||||
protected function outputHTML(string $template, array $data = [], HtmlView $view = NULL, int $code = 200): void
|
||||
{
|
||||
if (NULL === $view)
|
||||
{
|
||||
|
@ -66,15 +66,13 @@ final class Anime extends BaseController {
|
||||
/**
|
||||
* Show a portion, or all of the anime list
|
||||
*
|
||||
* @param string|int $status - The section of the list
|
||||
* @param int|string $status - The section of the list
|
||||
* @param string|null $view - List or cover view
|
||||
* @throws ContainerException
|
||||
* @throws NotFoundException
|
||||
* @return void
|
||||
* @throws InvalidArgumentException
|
||||
* @throws Throwable
|
||||
* @return void
|
||||
*/
|
||||
public function index($status = KitsuWatchingStatus::WATCHING, ?string $view = NULL): void
|
||||
public function index(int|string $status = KitsuWatchingStatus::WATCHING, ?string $view = NULL): void
|
||||
{
|
||||
if ( ! in_array($status, [
|
||||
'all',
|
||||
@ -178,7 +176,7 @@ final class Anime extends BaseController {
|
||||
* @param string $id
|
||||
* @param string $status
|
||||
*/
|
||||
public function edit(string $id, $status = 'all'): void
|
||||
public function edit(string $id, string $status = 'all'): void
|
||||
{
|
||||
$this->checkAuth();
|
||||
|
||||
|
@ -82,7 +82,7 @@ final class AnimeCollection extends BaseController {
|
||||
{
|
||||
$queryParams = $this->request->getQueryParams();
|
||||
$query = $queryParams['query'];
|
||||
$this->outputJSON($this->animeModel->search($query), 200);
|
||||
$this->outputJSON($this->animeModel->search($query, inCollection: TRUE), 200);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -239,8 +239,6 @@ final class AnimeCollection extends BaseController {
|
||||
* Update a collection item
|
||||
*
|
||||
* @param array $data
|
||||
* @throws ContainerException
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
protected function update(array $data): void
|
||||
{
|
||||
|
@ -78,8 +78,6 @@ final class History extends BaseController {
|
||||
'url_type' => 'anime',
|
||||
]);
|
||||
|
||||
// $this->outputJSON($this->animeModel->getHistory());
|
||||
// return;
|
||||
$this->outputHTML('history', [
|
||||
'title' => $this->formatTitle(
|
||||
$this->config->get('whose_list') . "'s Anime List",
|
||||
@ -98,8 +96,6 @@ final class History extends BaseController {
|
||||
'url_type' => 'manga',
|
||||
]);
|
||||
|
||||
// $this->outputJSON($this->mangaModel->getHistory());
|
||||
// return;
|
||||
$this->outputHTML('history', [
|
||||
'title' => $this->formatTitle(
|
||||
$this->config->get('whose_list') . "'s Manga List",
|
||||
|
@ -16,8 +16,6 @@
|
||||
|
||||
namespace Aviat\AnimeClient\Controller;
|
||||
|
||||
use Aviat\Ion\Di\Exception\{ContainerException, NotFoundException};
|
||||
|
||||
use function Amp\Promise\wait;
|
||||
use function Aviat\AnimeClient\getResponse;
|
||||
use function Aviat\AnimeClient\createPlaceholderImage;
|
||||
@ -39,7 +37,7 @@ final class Images extends BaseController {
|
||||
* @return void
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function cache(string $type, string $file, $display = TRUE): void
|
||||
public function cache(string $type, string $file, bool $display = TRUE): void
|
||||
{
|
||||
$currentUrl = (string)$this->request->getUri();
|
||||
|
||||
|
@ -64,10 +64,10 @@ final class Manga extends Controller {
|
||||
*
|
||||
* @param string $status
|
||||
* @param string $view
|
||||
* @throws InvalidArgumentException
|
||||
* @return void
|
||||
*@throws InvalidArgumentException
|
||||
*/
|
||||
public function index($status = 'all', $view = ''): void
|
||||
public function index(string $status = 'all', ?string $view = ''): void
|
||||
{
|
||||
if ( ! in_array($status, [
|
||||
'all',
|
||||
|
@ -52,8 +52,6 @@ final class People extends BaseController {
|
||||
*
|
||||
* @param string $slug
|
||||
* @return void
|
||||
* @throws ContainerException
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function index(string $slug): void
|
||||
{
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
namespace Aviat\AnimeClient;
|
||||
|
||||
use Aviat\AnimeClient\Enum\EventType;
|
||||
use Aviat\Ion\Event;
|
||||
use Aviat\Ion\Json;
|
||||
use Aura\Router\{
|
||||
Map,
|
||||
@ -27,7 +29,6 @@ use Aviat\AnimeClient\API\FailedResponseException;
|
||||
use Aviat\Ion\Di\ContainerInterface;
|
||||
use Aviat\Ion\Friend;
|
||||
use Aviat\Ion\Type\StringType;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use LogicException;
|
||||
use ReflectionException;
|
||||
|
||||
@ -117,7 +118,7 @@ final class Dispatcher extends RoutingBase {
|
||||
* @return void
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function __invoke($route = NULL): void
|
||||
public function __invoke(object $route = NULL): void
|
||||
{
|
||||
$logger = $this->container->getLogger();
|
||||
|
||||
@ -136,7 +137,7 @@ final class Dispatcher extends RoutingBase {
|
||||
{
|
||||
// If not route was matched, return an appropriate http
|
||||
// error message
|
||||
$errorRoute = (array)$this->getErrorParams();
|
||||
$errorRoute = $this->getErrorParams();
|
||||
$controllerName = DEFAULT_CONTROLLER;
|
||||
$actionMethod = $errorRoute['action_method'];
|
||||
$params = $errorRoute['params'];
|
||||
@ -278,17 +279,14 @@ final class Dispatcher extends RoutingBase {
|
||||
*/
|
||||
protected function call(string $controllerName, string $method, array $params): void
|
||||
{
|
||||
$logger = $this->container->getLogger('default');
|
||||
$logger = $this->container->getLogger();
|
||||
|
||||
try
|
||||
{
|
||||
$controller = new $controllerName($this->container);
|
||||
|
||||
// Run the appropriate controller method
|
||||
if ($logger !== NULL)
|
||||
{
|
||||
$logger->debug('Dispatcher - controller arguments', $params);
|
||||
}
|
||||
$logger?->debug('Dispatcher - controller arguments', $params);
|
||||
|
||||
$params = array_values($params);
|
||||
$controller->$method(...$params);
|
||||
@ -360,7 +358,7 @@ final class Dispatcher extends RoutingBase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Select controller based on the current url, and apply its relevent routes
|
||||
* Select controller based on the current url, and apply its relevant routes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
@ -336,6 +336,24 @@ final class Kitsu {
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the url of the posterImage from Kitsu, with fallbacks
|
||||
*
|
||||
* @param array $base
|
||||
* @param int $size
|
||||
* @return string
|
||||
*/
|
||||
public static function getPosterImage(array $base, int $size = 1): string
|
||||
{
|
||||
$rawUrl = $base['posterImage']['views'][$size]['url']
|
||||
?? $base['posterImage']['original']['url']
|
||||
?? '/public/images/placeholder.png';
|
||||
|
||||
$parts = explode('?', $rawUrl);
|
||||
|
||||
return ( ! empty($parts)) ? $parts[0] : $rawUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name and logo for the streaming service of the current link
|
||||
*
|
||||
|
@ -16,17 +16,8 @@
|
||||
|
||||
namespace Aviat\AnimeClient\Model;
|
||||
|
||||
use Aviat\AnimeClient\API\ParallelAPIRequest;
|
||||
use Aviat\AnimeClient\API\Mapping\AnimeWatchingStatus;
|
||||
use Aviat\AnimeClient\Types\{
|
||||
Anime as AnimeType,
|
||||
FormItem,
|
||||
AnimeListItem
|
||||
};
|
||||
use Aviat\Ion\Json;
|
||||
|
||||
use Throwable;
|
||||
use function is_array;
|
||||
use Aviat\AnimeClient\Types\Anime as AnimeType;
|
||||
|
||||
/**
|
||||
* Model for handling requests dealing with the anime list
|
||||
@ -64,7 +55,7 @@ class Anime extends API {
|
||||
{
|
||||
$data = $this->kitsuModel->getFullOrganizedAnimeList();
|
||||
|
||||
foreach($data as $section => &$list)
|
||||
foreach($data as &$list)
|
||||
{
|
||||
$this->sortByName($list, 'anime');
|
||||
}
|
||||
|
@ -578,7 +578,7 @@ final class AnimeCollection extends Collection {
|
||||
* @param string $animeId The current anime
|
||||
* @return void
|
||||
*/
|
||||
private function updateGenres($animeId): void
|
||||
private function updateGenres(string $animeId): void
|
||||
{
|
||||
if ($this->db === NULL)
|
||||
{
|
||||
|
@ -71,11 +71,19 @@ trait MediaTrait {
|
||||
* Search for anime by name
|
||||
*
|
||||
* @param string $name
|
||||
* @param bool $inCollection
|
||||
* @return array
|
||||
*/
|
||||
public function search(string $name): array
|
||||
public function search(string $name, bool $inCollection = false): array
|
||||
{
|
||||
return $this->kitsuModel->search($this->type, urldecode($name));
|
||||
$data = $this->kitsuModel->search($this->type, urldecode($name));
|
||||
|
||||
if ($inCollection)
|
||||
{
|
||||
// @TODO: allow filtering collection search by existing items
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,6 +28,8 @@ final class Character extends AbstractType {
|
||||
|
||||
public ?Media $media;
|
||||
|
||||
public string $image;
|
||||
|
||||
public ?string $name;
|
||||
|
||||
public array $names = [];
|
||||
|
@ -25,6 +25,8 @@ final class Person extends AbstractType {
|
||||
|
||||
public ?string $name;
|
||||
|
||||
public string $image;
|
||||
|
||||
public array $names = [];
|
||||
|
||||
public ?string $description;
|
||||
|
@ -46,8 +46,6 @@ class Util {
|
||||
* Set up the Util class
|
||||
*
|
||||
* @param ContainerInterface $container
|
||||
* @throws ContainerException
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
|
@ -45,10 +45,10 @@ class Config implements ConfigInterface {
|
||||
/**
|
||||
* Does the config item exist?
|
||||
*
|
||||
* @param string|int|array $key
|
||||
* @param array|int|string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key): bool
|
||||
public function has(array|int|string $key): bool
|
||||
{
|
||||
return $this->map->hasKey($key);
|
||||
}
|
||||
@ -60,7 +60,7 @@ class Config implements ConfigInterface {
|
||||
* @return mixed
|
||||
* @throws ConfigException
|
||||
*/
|
||||
public function get($key = NULL)
|
||||
public function get(array|string $key = NULL): mixed
|
||||
{
|
||||
if (\is_array($key))
|
||||
{
|
||||
@ -73,10 +73,10 @@ class Config implements ConfigInterface {
|
||||
/**
|
||||
* Remove a config value
|
||||
*
|
||||
* @param string|array $key
|
||||
* @param array|string $key
|
||||
* @return void
|
||||
*/
|
||||
public function delete($key): void
|
||||
public function delete(array|string $key): void
|
||||
{
|
||||
if (\is_array($key))
|
||||
{
|
||||
@ -92,12 +92,12 @@ class Config implements ConfigInterface {
|
||||
/**
|
||||
* Set a config value
|
||||
*
|
||||
* @param integer|string|array $key
|
||||
* @param mixed $value
|
||||
* @throws InvalidArgumentException
|
||||
* @param array|integer|string $key
|
||||
* @param mixed $value
|
||||
* @return ConfigInterface
|
||||
*@throws InvalidArgumentException
|
||||
*/
|
||||
public function set($key, $value): ConfigInterface
|
||||
public function set(array|int|string $key, mixed $value): ConfigInterface
|
||||
{
|
||||
if (\is_array($key))
|
||||
{
|
||||
|
@ -23,10 +23,10 @@ interface ConfigInterface {
|
||||
/**
|
||||
* Does the config item exist?
|
||||
*
|
||||
* @param string|int|array $key
|
||||
* @param array|int|string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key): bool;
|
||||
public function has(array|int|string $key): bool;
|
||||
|
||||
/**
|
||||
* Get a config value
|
||||
@ -34,23 +34,23 @@ interface ConfigInterface {
|
||||
* @param array|string|null $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key = NULL);
|
||||
public function get(array|string $key = NULL): mixed;
|
||||
|
||||
/**
|
||||
* Set a config value
|
||||
*
|
||||
* @param integer|string|array $key
|
||||
* @param array|integer|string $key
|
||||
* @param mixed $value
|
||||
* @throws \InvalidArgumentException
|
||||
* @return ConfigInterface
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function set($key, $value): self;
|
||||
public function set(array|int|string $key, mixed $value): self;
|
||||
|
||||
/**
|
||||
* Remove a config value
|
||||
*
|
||||
* @param string|array $key
|
||||
* @param array|string $key
|
||||
* @return void
|
||||
*/
|
||||
public function delete($key): void;
|
||||
public function delete(array|string $key): void;
|
||||
}
|
@ -51,7 +51,7 @@ abstract class Enum {
|
||||
* @return boolean
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public static function isValid($key): bool
|
||||
public static function isValid(mixed $key): bool
|
||||
{
|
||||
$values = array_values(static::getConstList());
|
||||
return in_array($key, $values, TRUE);
|
||||
|
26
src/Ion/Exception/ImageCreationException.php
Normal file
26
src/Ion/Exception/ImageCreationException.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 8
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2021 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 5.2
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\Ion\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Exception for bad configuration
|
||||
*/
|
||||
class ImageCreationException extends RuntimeException {
|
||||
|
||||
}
|
@ -31,7 +31,7 @@ class Friend {
|
||||
* Object to create a friend of
|
||||
* @var mixed
|
||||
*/
|
||||
private $_friend_;
|
||||
private mixed $_friend_;
|
||||
|
||||
/**
|
||||
* Reflection class of the object
|
||||
@ -46,7 +46,7 @@ class Friend {
|
||||
* @throws InvalidArgumentException
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function __construct($obj)
|
||||
public function __construct(mixed $obj)
|
||||
{
|
||||
if ( ! \is_object($obj))
|
||||
{
|
||||
@ -63,7 +63,7 @@ class Friend {
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get(string $key)
|
||||
public function __get(string $key): mixed
|
||||
{
|
||||
if ($this->__isset($key))
|
||||
{
|
||||
@ -96,7 +96,7 @@ class Friend {
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function __set(string $key, $value)
|
||||
public function __set(string $key, mixed $value)
|
||||
{
|
||||
if ($this->__isset($key))
|
||||
{
|
||||
|
167
src/Ion/ImageBuilder.php
Normal file
167
src/Ion/ImageBuilder.php
Normal file
@ -0,0 +1,167 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 8
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2021 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 5.2
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\Ion;
|
||||
|
||||
use GdImage;
|
||||
|
||||
use Aviat\Ion\Exception\ImageCreationException;
|
||||
|
||||
/**
|
||||
* A wrapper around GD functions to create images
|
||||
*
|
||||
* @property GdImage|false|null $img
|
||||
*/
|
||||
class ImageBuilder {
|
||||
private GDImage|false|null $_img;
|
||||
|
||||
private int $fontSize = 10;
|
||||
|
||||
private function __construct(private int $width=200, private int $height=200 )
|
||||
{
|
||||
$this->_img = imagecreatetruecolor($this->width, $this->height);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->cleanup();
|
||||
}
|
||||
|
||||
private function getImg(): GdImage
|
||||
{
|
||||
if ($this->_img instanceof GdImage)
|
||||
{
|
||||
return $this->_img;
|
||||
}
|
||||
|
||||
throw new ImageCreationException('Invalid GD object');
|
||||
}
|
||||
|
||||
public static function new(int $width=200, int $height=200): self
|
||||
{
|
||||
$i = new self($width, $height);
|
||||
if ($i->_img === FALSE)
|
||||
{
|
||||
throw new ImageCreationException('Could not create image object');
|
||||
}
|
||||
|
||||
return $i;
|
||||
}
|
||||
|
||||
public function setFontSize(int $size): self
|
||||
{
|
||||
$this->fontSize = $size;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function enableAlphaBlending(bool $enable): self
|
||||
{
|
||||
$ab = imagealphablending($this->getImg(), $enable);
|
||||
if ( ! $ab)
|
||||
{
|
||||
throw new ImageCreationException('Failed to toggle image alpha blending');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addCenteredText(string $text, int $red, int $green, int $blue, int $alpha=-1): self
|
||||
{
|
||||
// Create the font color
|
||||
$textColor = ($alpha > -1)
|
||||
? imagecolorallocatealpha($this->getImg(), $red, $green, $blue, $alpha)
|
||||
: imagecolorallocate($this->getImg(), $red, $green, $blue);
|
||||
if ($textColor === FALSE)
|
||||
{
|
||||
throw new ImageCreationException('Could not create image text color');
|
||||
}
|
||||
|
||||
// Generate placeholder text
|
||||
$fontWidth = imagefontwidth($this->fontSize);
|
||||
$fontHeight = imagefontheight($this->fontSize);
|
||||
$length = strlen($text);
|
||||
$textWidth = $length * $fontWidth;
|
||||
$fxPos = (int) ceil((imagesx($this->getImg()) - $textWidth) / 2);
|
||||
$fyPos = (int) ceil((imagesy($this->getImg()) - $fontHeight) / 2);
|
||||
|
||||
// Add the image text
|
||||
imagestring($this->getImg(), $this->fontSize, $fxPos, $fyPos, $text, $textColor);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addBackgroundColor(int $red, int $green, int $blue, int $alpha=-1): self
|
||||
{
|
||||
$fillColor = ($alpha > -1)
|
||||
? imagecolorallocatealpha($this->getImg(), $red, $green, $blue, $alpha)
|
||||
: imagecolorallocate($this->getImg(), $red, $green, $blue);
|
||||
|
||||
if ($fillColor === FALSE)
|
||||
{
|
||||
throw new ImageCreationException('Failed to create image fill color');
|
||||
}
|
||||
|
||||
$hasFilled = imagefill($this->getImg(), 0, 0, $fillColor);
|
||||
if ($hasFilled === FALSE)
|
||||
{
|
||||
throw new ImageCreationException('Failed to add background color to image');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function savePng(string $savePath, bool $saveAlpha = TRUE): bool
|
||||
{
|
||||
$setAlpha = imagesavealpha($this->getImg(), $saveAlpha);
|
||||
if ($setAlpha === FALSE)
|
||||
{
|
||||
throw new ImageCreationException('Failed to set image save alpha flag');
|
||||
}
|
||||
|
||||
return imagepng($this->getImg(), $savePath, 9);
|
||||
}
|
||||
|
||||
public function saveWebp(string $savePath): bool
|
||||
{
|
||||
return imagewebp($this->getImg(), $savePath);
|
||||
}
|
||||
|
||||
public function saveJpg(string $savePath): bool
|
||||
{
|
||||
return imagejpeg($this->getImg(), $savePath);
|
||||
}
|
||||
|
||||
public function saveGif(string $savePath): bool
|
||||
{
|
||||
return imagegif($this->getImg(), $savePath);
|
||||
}
|
||||
|
||||
public function cleanup(): void
|
||||
{
|
||||
$cleaned = FALSE;
|
||||
|
||||
if ($this->getImg() instanceof GdImage)
|
||||
{
|
||||
$cleaned = imagedestroy($this->getImg());
|
||||
}
|
||||
|
||||
if ($cleaned === FALSE)
|
||||
{
|
||||
throw new ImageCreationException('Failed to clean up image resource');
|
||||
}
|
||||
}
|
||||
}
|
@ -93,7 +93,7 @@ class HttpView implements HttpViewInterface{
|
||||
* @param string|string[] $value
|
||||
* @return HttpView
|
||||
*/
|
||||
public function addHeader(string $name, $value): self
|
||||
public function addHeader(string $name, array|string $value): self
|
||||
{
|
||||
$this->response = $this->response->withHeader($name, $value);
|
||||
return $this;
|
||||
@ -105,7 +105,7 @@ class HttpView implements HttpViewInterface{
|
||||
* @param mixed $string
|
||||
* @return HttpViewInterface
|
||||
*/
|
||||
public function setOutput($string): HttpViewInterface
|
||||
public function setOutput(mixed $string): HttpViewInterface
|
||||
{
|
||||
$this->response->getBody()->write($string);
|
||||
|
||||
|
@ -37,7 +37,7 @@ interface ViewInterface {
|
||||
* @param mixed $string
|
||||
* @return ViewInterface
|
||||
*/
|
||||
public function setOutput($string): self;
|
||||
public function setOutput(mixed $string): self;
|
||||
|
||||
/**
|
||||
* Append additional output.
|
||||
@ -54,7 +54,7 @@ interface ViewInterface {
|
||||
* @param string|string[] $value
|
||||
* @return ViewInterface
|
||||
*/
|
||||
public function addHeader(string $name, $value): self;
|
||||
public function addHeader(string $name, array|string $value): self;
|
||||
|
||||
/**
|
||||
* Get the current output as a string. Does not
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -4,7 +4,7 @@
|
||||
mal_id: '28091'
|
||||
chapters: { read: 94, total: '-' }
|
||||
volumes: { read: '-', total: '-' }
|
||||
manga: { empty: false, genres: { }, id: '21733', image: 'https://media.kitsu.io/manga/poster_images/21733/small.jpg?1496845097', slug: tonari-no-seki-kun, title: 'Tonari no Seki-kun', titles: ['My Neighbour Seki', となりの関くん], type: Manga, url: 'https://kitsu.io/manga/tonari-no-seki-kun' }
|
||||
manga: { empty: false, genres: { }, id: '21733', image: 'https://media.kitsu.io/manga/poster_images/21733/small.jpg', slug: tonari-no-seki-kun, title: 'Tonari no Seki-kun', titles: ['My Neighbour Seki', となりの関くん], type: Manga, url: 'https://kitsu.io/manga/tonari-no-seki-kun' }
|
||||
reading_status: current
|
||||
notes: ''
|
||||
rereading: false
|
||||
@ -16,7 +16,7 @@
|
||||
mal_id: '60815'
|
||||
chapters: { read: 87, total: '-' }
|
||||
volumes: { read: '-', total: '-' }
|
||||
manga: { empty: false, genres: { }, id: '25491', image: 'https://media.kitsu.io/manga/poster_images/25491/small.jpg?1499026452', slug: joshikausei, title: Joshikausei, titles: [女子かう生], type: Manga, url: 'https://kitsu.io/manga/joshikausei' }
|
||||
manga: { empty: false, genres: { }, id: '25491', image: 'https://media.kitsu.io/manga/poster_images/25491/small.jpg', slug: joshikausei, title: Joshikausei, titles: [女子かう生], type: Manga, url: 'https://kitsu.io/manga/joshikausei' }
|
||||
reading_status: current
|
||||
notes: 'Wordless, and it works.'
|
||||
rereading: false
|
||||
|
@ -2,10 +2,10 @@ empty: false
|
||||
age_rating: PG
|
||||
age_rating_guide: ''
|
||||
characters:
|
||||
main: { 40541: { image: { original: { height: null, name: original, url: 'https://media.kitsu.io/characters/images/40541/original.jpg?1483096805', width: null } }, name: 'Kazunari Usa', slug: kazunari-usa }, 40540: { image: { original: { height: null, name: original, url: 'https://media.kitsu.io/characters/images/40540/original.jpg?1483096805', width: null } }, name: 'Ritsu Kawai', slug: ritsu-kawai } }
|
||||
background: { 62591: { image: { original: { height: null, name: original, url: 'https://media.kitsu.io/characters/images/62591/original.jpg?1485073100', width: null } }, name: Chinatsu, slug: chinatsu }, 72839: { image: { original: { height: null, name: original, url: 'https://media.kitsu.io/characters/images/72839/original.jpg?1485079724', width: null } }, name: Hayashi, slug: hayashi-ec3a2705-5d5c-493c-b172-bbee2d04b5b9 }, 78362: { image: { original: { height: null, name: original, url: 'https://media.kitsu.io/characters/images/78362/original.jpg?1485081676', width: null } }, name: Houjou, slug: houjou }, 90353: { image: { original: { height: null, name: original, url: 'https://media.kitsu.io/characters/images/90353/original.jpg?1485086356', width: null } }, name: Kurokawa, slug: kurokawa-a493ddf6-0f02-4abf-8b18-ab6ae2198b6e }, 77996: { image: { original: { height: null, name: original, url: 'https://media.kitsu.io/characters/images/77996/original.jpg?1485081552', width: null } }, name: Maemura, slug: maemura }, 55132: { image: { original: { height: null, name: original, url: 'https://media.kitsu.io/characters/images/55132/original.jpg?1483096805', width: null } }, name: 'Mayumi Nishikino', slug: mayumi-nishikino }, 71479: { image: { original: { height: null, name: original, url: 'https://media.kitsu.io/characters/images/71479/original.jpg?1485079211', width: null } }, name: 'Miharu Tsuneda', slug: miharu-tsuneda }, 55134: { image: { original: { height: null, name: original, url: '/images/original/missing.png?1483096805', width: null } }, name: 'Mother Usa', slug: mother-usa }, 55135: { image: { original: { height: null, name: original, url: 'https://media.kitsu.io/characters/images/55135/original.jpg?1483096805', width: null } }, name: 'Sayaka Watanabe', slug: sayaka-watanabe }, 95032: { image: { original: { height: null, name: original, url: 'https://media.kitsu.io/characters/images/95032/original.jpg?1485088329', width: null } }, name: Shirosaki, slug: shirosaki-2ed2e15c-9cee-4756-92f1-027c4820e224 }, 55131: { image: { original: { height: null, name: original, url: 'https://media.kitsu.io/characters/images/55131/original.jpg?1483096805', width: null } }, name: 'Sumiko Kawai', slug: sumiko-kawai }, 74335: { image: { original: { height: null, name: original, url: 'https://media.kitsu.io/characters/images/74335/original.jpg?1485080245', width: null } }, name: 'Tae Shinohara', slug: tae-shinohara }, 55133: { image: { original: { height: null, name: original, url: 'https://media.kitsu.io/characters/images/55133/original.jpg?1483096805', width: null } }, name: Tagami, slug: tagami }, 83463: { image: { original: { height: null, name: original, url: /images/original/missing.png, width: null } }, name: 'Yoko Mabuchi', slug: yoko-mabuchi } }
|
||||
main: { 40541: { image: 'https://media.kitsu.io/characters/images/40541/original.jpg?1483096805', name: 'Kazunari Usa', slug: kazunari-usa }, 40540: { image: 'https://media.kitsu.io/characters/images/40540/original.jpg?1483096805', name: 'Ritsu Kawai', slug: ritsu-kawai } }
|
||||
background: { 62591: { image: 'https://media.kitsu.io/characters/images/62591/original.jpg?1485073100', name: Chinatsu, slug: chinatsu }, 72839: { image: 'https://media.kitsu.io/characters/images/72839/original.jpg?1485079724', name: Hayashi, slug: hayashi-ec3a2705-5d5c-493c-b172-bbee2d04b5b9 }, 78362: { image: 'https://media.kitsu.io/characters/images/78362/original.jpg?1485081676', name: Houjou, slug: houjou }, 90353: { image: 'https://media.kitsu.io/characters/images/90353/original.jpg?1485086356', name: Kurokawa, slug: kurokawa-a493ddf6-0f02-4abf-8b18-ab6ae2198b6e }, 77996: { image: 'https://media.kitsu.io/characters/images/77996/original.jpg?1485081552', name: Maemura, slug: maemura }, 55132: { image: 'https://media.kitsu.io/characters/images/55132/original.jpg?1483096805', name: 'Mayumi Nishikino', slug: mayumi-nishikino }, 71479: { image: 'https://media.kitsu.io/characters/images/71479/original.jpg?1485079211', name: 'Miharu Tsuneda', slug: miharu-tsuneda }, 55134: { image: '/images/original/missing.png?1483096805', name: 'Mother Usa', slug: mother-usa }, 55135: { image: 'https://media.kitsu.io/characters/images/55135/original.jpg?1483096805', name: 'Sayaka Watanabe', slug: sayaka-watanabe }, 95032: { image: 'https://media.kitsu.io/characters/images/95032/original.jpg?1485088329', name: Shirosaki, slug: shirosaki-2ed2e15c-9cee-4756-92f1-027c4820e224 }, 55131: { image: 'https://media.kitsu.io/characters/images/55131/original.jpg?1483096805', name: 'Sumiko Kawai', slug: sumiko-kawai }, 74335: { image: 'https://media.kitsu.io/characters/images/74335/original.jpg?1485080245', name: 'Tae Shinohara', slug: tae-shinohara }, 55133: { image: 'https://media.kitsu.io/characters/images/55133/original.jpg?1483096805', name: Tagami, slug: tagami }, 83463: { image: /images/original/missing.png, name: 'Yoko Mabuchi', slug: yoko-mabuchi } }
|
||||
chapter_count: 90
|
||||
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'
|
||||
genres:
|
||||
- Comedy
|
||||
- Romance
|
||||
@ -19,7 +19,7 @@ id: '20286'
|
||||
manga_type: MANGA
|
||||
status: Completed
|
||||
staff:
|
||||
'Story & Art': [{ id: '8712', slug: ruri-miyahara, name: 'Ruri Miyahara', image: { original: 'https://media.kitsu.io/people/images/8712/original.jpg?1533271952' } }]
|
||||
'Story & Art': [{ id: '8712', slug: ruri-miyahara, name: 'Ruri Miyahara', image: 'https://media.kitsu.io/people/images/8712/original.jpg?1533271952' }]
|
||||
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!\r\n(Source: Kirei Cake)"
|
||||
title: 'Bokura wa Minna Kawai-sou'
|
||||
titles:
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
||||
empty: false
|
||||
about: 'Web Developer, Anime Fan, Reader of VNs, and web comics.'
|
||||
avatar: images/avatars/2644.gif
|
||||
avatar: 'https://media.kitsu.io/users/avatars/2644/original.gif?1491510751'
|
||||
favorites:
|
||||
anime: { 933073: { __typename: Anime, id: '14212', slug: hataraku-saibou-tv, posterImage: { original: { url: 'https://media.kitsu.io/anime/poster_images/14212/original.jpg?1597697195', height: 1050, width: 750 }, views: [{ url: 'https://media.kitsu.io/anime/poster_images/14212/tiny.jpg?1597697195', height: 156, width: 110 }, { url: 'https://media.kitsu.io/anime/poster_images/14212/small.jpg?1597697195', height: 402, width: 284 }, { url: 'https://media.kitsu.io/anime/poster_images/14212/medium.jpg?1597697195', height: 554, width: 390 }, { url: 'https://media.kitsu.io/anime/poster_images/14212/large.jpg?1597697195', height: 780, width: 550 }] }, titles: { canonical: 'Hataraku Saibou', localized: { en: 'Cells at Work!', en_jp: 'Hataraku Saibou', ja_jp: はたらく細胞 } } }, 586217: { __typename: Anime, id: '323', slug: fate-stay-night, posterImage: { original: { url: 'https://media.kitsu.io/anime/poster_images/323/original.jpg?1597698066', height: 1074, width: 760 }, views: [{ url: 'https://media.kitsu.io/anime/poster_images/323/tiny.jpg?1597698066', height: 156, width: 110 }, { url: 'https://media.kitsu.io/anime/poster_images/323/small.jpg?1597698066', height: 402, width: 284 }, { url: 'https://media.kitsu.io/anime/poster_images/323/medium.jpg?1597698066', height: 554, width: 390 }, { url: 'https://media.kitsu.io/anime/poster_images/323/large.jpg?1597698066', height: 780, width: 550 }] }, titles: { canonical: 'Fate/stay night', localized: { en: 'Fate/stay night', en_jp: 'Fate/stay night', en_us: 'Fate/stay night', ja_jp: 'Fate/stay night' } } }, 607473: { __typename: Anime, id: '310', slug: tsukuyomi-moon-phase, posterImage: { original: { url: 'https://media.kitsu.io/anime/poster_images/310/original.jpg?1597690591', height: 320, width: 225 }, views: [{ url: 'https://media.kitsu.io/anime/poster_images/310/tiny.jpg?1597690591', height: 156, width: 110 }, { url: 'https://media.kitsu.io/anime/poster_images/310/small.jpg?1597690591', height: 402, width: 284 }, { url: 'https://media.kitsu.io/anime/poster_images/310/medium.jpg?1597690591', height: 554, width: 390 }, { url: 'https://media.kitsu.io/anime/poster_images/310/large.jpg?1597690591', height: 780, width: 550 }] }, titles: { canonical: 'Tsukuyomi: Moon Phase', localized: { en: 'Tsukuyomi: Moon Phase', en_jp: 'Tsukuyomi: Moon Phase', en_us: 'Tsukuyomi: Moon Phase', ja_jp: '月詠 −MOON PHASE−' } } }, 607472: { __typename: Anime, id: '5992', slug: carnival-phantasm, posterImage: { original: { url: 'https://media.kitsu.io/anime/poster_images/5992/original.jpg?1597697878', height: 693, width: 533 }, views: [{ url: 'https://media.kitsu.io/anime/poster_images/5992/tiny.jpg?1597697878', height: 156, width: 110 }, { url: 'https://media.kitsu.io/anime/poster_images/5992/small.jpg?1597697878', height: 402, width: 284 }, { url: 'https://media.kitsu.io/anime/poster_images/5992/medium.jpg?1597697878', height: 554, width: 390 }, { url: 'https://media.kitsu.io/anime/poster_images/5992/large.jpg?1597697878', height: 780, width: 550 }] }, titles: { canonical: 'Carnival Phantasm', localized: { en_jp: 'Carnival Phantasm', ja_jp: カーニバル・ファンタズム } } }, 636892: { __typename: Anime, id: '6062', slug: nichijou, posterImage: { original: { url: 'https://media.kitsu.io/anime/poster_images/6062/original.jpg?1597696783', height: 2292, width: 1610 }, views: [{ url: 'https://media.kitsu.io/anime/poster_images/6062/tiny.jpg?1597696783', height: 156, width: 110 }, { url: 'https://media.kitsu.io/anime/poster_images/6062/small.jpg?1597696783', height: 402, width: 284 }, { url: 'https://media.kitsu.io/anime/poster_images/6062/medium.jpg?1597696783', height: 554, width: 390 }, { url: 'https://media.kitsu.io/anime/poster_images/6062/large.jpg?1597696783', height: 780, width: 550 }] }, titles: { canonical: Nichijou, localized: { en: 'Nichijou - My Ordinary Life', en_jp: Nichijou, en_us: 'Nichijou - My Ordinary Life', ja_jp: 日常 } } } }
|
||||
character: { 586219: { __typename: Character, id: '6553', slug: saber, image: { original: { url: 'https://media.kitsu.io/characters/images/6553/original.jpg?1483096805' } }, names: { alternatives: ['King of Knights'], canonical: Saber, canonicalLocale: null, localized: { en: Saber, ja_jp: セイバー } } }, 586218: { __typename: Character, id: '6556', slug: rin-tohsaka, image: { original: { url: 'https://media.kitsu.io/characters/images/6556/original.jpg?1483096805' } }, names: { alternatives: { }, canonical: 'Rin Toosaka', canonicalLocale: null, localized: { en: 'Rin Toosaka', ja_jp: '遠坂 凛' } } }, 611365: { __typename: Character, id: '32035', slug: nano-shinonome, image: { original: { url: 'https://media.kitsu.io/characters/images/32035/original.jpg?1483096805' } }, names: { alternatives: { }, canonical: 'Nano Shinonome', canonicalLocale: null, localized: { en: 'Nano Shinonome', ja_jp: '東雲 なの' } } }, 611364: { __typename: Character, id: '32034', slug: mio-naganohara, image: { original: { url: 'https://media.kitsu.io/characters/images/32034/original.jpg?1483096805' } }, names: { alternatives: { }, canonical: 'Mio Naganohara', canonicalLocale: null, localized: { en: 'Mio Naganohara', ja_jp: 長野原みお } } }, 636590: { __typename: Character, id: '31851', slug: aria-holmes-kanzaki, image: { original: { url: 'https://media.kitsu.io/characters/images/31851/original.jpg?1483096805' } }, names: { alternatives: ['Quadra Aria'], canonical: 'Aria Holmes Kanzaki', canonicalLocale: null, localized: { en: 'Aria Holmes Kanzaki', ja_jp: 神崎・H・アリア } } }, 636591: { __typename: Character, id: '25930', slug: taiga-aisaka, image: { original: { url: 'https://media.kitsu.io/characters/images/25930/original.jpg?1483096805' } }, names: { alternatives: ['Palmtop Tiger'], canonical: 'Taiga Aisaka', canonicalLocale: null, localized: { en: 'Taiga Aisaka', ja_jp: '逢坂 大河' } } }, 636593: { __typename: Character, id: '31625', slug: victorique-de-blois, image: { original: { url: 'https://media.kitsu.io/characters/images/31625/original.jpg?1483096805' } }, names: { alternatives: ['The Golden Fairy', 'Gray Wolf', 'Monstre Charmant'], canonical: 'Victorique de Blois', canonicalLocale: null, localized: { en: 'Victorique de Blois', ja_jp: ヴィクトリカ・ド・ブロワ } } } }
|
||||
|
@ -20,7 +20,7 @@ use Aviat\Ion\Config;
|
||||
|
||||
class ConfigTest extends IonTestCase {
|
||||
|
||||
protected $config;
|
||||
protected Config $config;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
@ -66,12 +66,6 @@ class ConfigTest extends IonTestCase {
|
||||
$this->assertEquals('great', $this->config->get(['apple', 'sauce', 'is']), "Array argument get for config failed.");
|
||||
}
|
||||
|
||||
public function testConfigBadSet(): void
|
||||
{
|
||||
$this->expectException('InvalidArgumentException');
|
||||
$this->config->set(NULL, FALSE);
|
||||
}
|
||||
|
||||
public function dataConfigDelete(): array
|
||||
{
|
||||
return [
|
||||
|
Loading…
Reference in New Issue
Block a user