Merge remote-tracking branch 'origin/develop'
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details

master
Timothy Warren 1 year ago
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:&nbsp;&nbsp;&nbsp;&nbsp;<input type="search" id="search" name="search" /></label>
<label for="search-anime-collection">Search for <?= $collection_type ?> by name:&nbsp;&nbsp;&nbsp;&nbsp;<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>&nbsp;</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]),
);
}
}

@ -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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -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