Better handling of alternate titles, Airing Status and genres for anime list views

This commit is contained in:
Timothy Warren 2017-01-05 22:24:45 -05:00
parent 9eda005399
commit 4c75701c0d
22 changed files with 379 additions and 213 deletions

View File

@ -18,8 +18,10 @@
<?= $helper->img($item['anime']['image']); ?> <?= $helper->img($item['anime']['image']); ?>
<div class="name"> <div class="name">
<a href="<?= $url->generate('anime.details', ['id' => $item['anime']['slug']]); ?>"> <a href="<?= $url->generate('anime.details', ['id' => $item['anime']['slug']]); ?>">
<?= $escape->html($item['anime']['title']) ?> <?= array_shift($item['anime']['titles']) ?>
<?= ($item['anime']['alternate_title'] != "") ? "<br />({$item['anime']['alternate_title']})" : ""; ?> <?php foreach ($item['anime']['titles'] as $title): ?>
<br /><small><?= $title ?></small>
<?php endforeach ?>
</a> </a>
</div> </div>
<div class="table"> <div class="table">

View File

@ -5,10 +5,10 @@
<br /> <br />
<br /> <br />
<table> <table>
<?php /*<tr> <tr>
<td class="align_right">Airing Status</td> <td class="align_right">Airing Status</td>
<td><?= $data['status'] ?></td> <td><?= $data['status'] ?></td>
</tr>*/ ?> </tr>
<tr> <tr>
<td>Show Type</td> <td>Show Type</td>
<td><?= $data['show_type'] ?></td> <td><?= $data['show_type'] ?></td>
@ -34,14 +34,10 @@
</table> </table>
</div> </div>
<div> <div>
<h2><a rel="external" href="<?= $data['url'] ?>"><?= $data['title'] ?></a></h2> <h2><a rel="external" href="<?= $data['url'] ?>"><?= array_shift($data['titles']) ?></a></h2>
<?php if ( ! empty($data['jp_title'])): ?> <?php foreach ($data['titles'] as $title): ?>
<h3><?= $data['jp_title'] ?></h3> <h3><?= $title ?></h3>
<?php endif ?> <?php endforeach ?>
<?php if( ! empty($data['en_title'] && $data['en_title'] !== $data['title'])): ?>
<h3><?= $data['en_title'] ?></h3>
<?php endif ?>
<br /> <br />
<p><?= nl2br($data['synopsis']) ?></p> <p><?= nl2br($data['synopsis']) ?></p>
</div> </div>

View File

@ -6,10 +6,10 @@
<thead> <thead>
<tr> <tr>
<th> <th>
<h3><?= $escape->html($item['anime']['title']) ?></h3> <h3><?= $escape->html(array_shift($item['anime']['titles'])) ?></h3>
<?php if($item['anime']['alternate_title'] != ""): ?> <?php foreach($item['anime']['titles'] as $title): ?>
<h4><?= $escape->html($item['anime']['alternate_title']) ?></h4> <h4><?= $escape->html($title) ?></h4>
<?php endif ?> <?php endforeach ?>
</th> </th>
<th> <th>
<article class="media"> <article class="media">

View File

@ -26,7 +26,7 @@
</thead> </thead>
<tbody> <tbody>
<?php foreach($items as $item): ?> <?php foreach($items as $item): ?>
<?php if ($item['private']) && ! $auth->is_authenticated()) continue; ?> <?php if ($item['private'] && ! $auth->is_authenticated()) continue; ?>
<tr id="a-<?= $item['id'] ?>"> <tr id="a-<?= $item['id'] ?>">
<?php if ($auth->is_authenticated()): ?> <?php if ($auth->is_authenticated()): ?>
<td> <td>
@ -35,36 +35,38 @@
<?php endif ?> <?php endif ?>
<td class="justify"> <td class="justify">
<a href="<?= $url->generate('anime.details', ['id' => $item['anime']['slug']]) ?>"> <a href="<?= $url->generate('anime.details', ['id' => $item['anime']['slug']]) ?>">
<?= $item['anime']['title'] ?> <?= array_shift($item['anime']['titles']) ?>
</a> </a>
<?= ( ! empty($item['anime']['alternate_title'])) ? " <br /> " . $item['anime']['alternate_title'] : "" ?> <?php foreach($item['anime']['titles'] as $title): ?>
<br /><?= $title ?>
<?php endforeach ?>
</td> </td>
<td class="align_left"><?= $item['airing']['status'] ?></td> <td><?= $item['airing']['status'] ?></td>
<td><?= $item['user_rating'] ?> / 10 </td> <td><?= $item['user_rating'] ?> / 10 </td>
<td><?= $item['anime']['type'] ?></td> <td><?= $item['anime']['type'] ?></td>
<td class="align_left" id="<?= $item['anime']['slug'] ?>"> <td id="<?= $item['anime']['slug'] ?>">
Episodes: <br /> Episodes: <br />
<span class="completed_number"><?= $item['episodes']['watched'] ?></span>&nbsp;/&nbsp;<span class="total_number"><?= $item['episodes']['total'] ?></span> <span class="completed_number"><?= $item['episodes']['watched'] ?></span>&nbsp;/&nbsp;<span class="total_number"><?= $item['episodes']['total'] ?></span>
</td> </td>
<td><?= $item['anime']['age_rating'] ?></td> <td><?= $item['anime']['age_rating'] ?></td>
<td> <td>
<ul>
<?php if ($item['rewatched'] > 0): ?> <?php if ($item['rewatched'] > 0): ?>
Rewatched <?= $item['rewatched'] ?> time(s)<br /> <li>Rewatched <?= $item['rewatched'] ?> time(s)</li>
<?php endif ?> <?php endif ?>
<?php $attr_list = []; ?>
<?php foreach(['private','rewatching'] as $attr): ?> <?php foreach(['private','rewatching'] as $attr): ?>
<?php if($item[$attr]): ?> <?php if($item[$attr]): ?>
<?php $attr_list[] = ucfirst($attr); ?> <li><?= ucfirst($attr); ?></li>
<?php endif ?> <?php endif ?>
<?php endforeach ?> <?php endforeach ?>
<?= implode(', ', $attr_list); ?> </ul>
</td> </td>
<td> <td>
<p><?= $escape->html($item['notes']) ?></p> <p><?= $escape->html($item['notes']) ?></p>
</td> </td>
<td class="align_left"> <td class="align_left">
<?php sort($item['anime']['genres']) ?> <?php sort($item['anime']['genres']) ?>
<?= join(', ', $item['anime']['genres']) ?> <?= implode(', ', $item['anime']['genres']) ?>
</td> </td>
</tr> </tr>
<?php endforeach ?> <?php endforeach ?>

View File

@ -1,7 +1,7 @@
<main> <main>
<?php /* if ($auth->is_authenticated()): ?> <?php if ($auth->is_authenticated()): ?>
<a class="bracketed" href="<?= $urlGenerator->url('manga/add') ?>">Add Item</a> <a class="bracketed" href="<?= $urlGenerator->url('manga/add') ?>">Add Item</a>
<?php endif */ ?> <?php endif ?>
<?php if (empty($sections)): ?> <?php if (empty($sections)): ?>
<h3>There's nothing here!</h3> <h3>There's nothing here!</h3>
<?php else: ?> <?php else: ?>
@ -11,26 +11,28 @@
<section class="media-wrap"> <section class="media-wrap">
<?php foreach($items as $item): ?> <?php foreach($items as $item): ?>
<article class="media" id="manga-<?= $item['id'] ?>"> <article class="media" id="manga-<?= $item['id'] ?>">
<?php /*if ($auth->is_authenticated()): ?> <?php if ($auth->is_authenticated()): ?>
<div class="edit_buttons" hidden> <div class="edit_buttons" hidden>
<button class="plus_one_chapter">+1 Chapter</button> <button class="plus_one_chapter">+1 Chapter</button>
</div> </div>
<?php endif */ ?> <?php endif ?>
<img src="<?= $escape->attr($item['manga']['image']) ?>" /> <img src="<?= $escape->attr($item['manga']['image']) ?>" />
<div class="name"> <div class="name">
<a href="<?= $url->generate('manga.details', ['id' => $item['manga']['slug']]) ?>"> <a href="<?= $url->generate('manga.details', ['id' => $item['manga']['slug']]) ?>">
<?= $escape->html($item['manga']['title']) ?> <?= $escape->html(array_shift($item['manga']['titles'])) ?>
<?= (isset($item['manga']['alternate_title'])) ? "<br />({$item['manga']['alternate_title']})" : ""; ?> <?php foreach($item['manga']['titles'] as $title): ?>
<br /><small><?= $title ?></small>
<?php endforeach ?>
</a> </a>
</div> </div>
<div class="table"> <div class="table">
<?php /*if ($auth->is_authenticated()): ?> <?php if ($auth->is_authenticated()): ?>
<div class="row"> <div class="row">
<span class="edit"> <span class="edit">
<a class="bracketed" title="Edit information about this manga" href="<?= $urlGenerator->url("manga/edit/{$item['id']}/{$name}") ?>">Edit</a> <a class="bracketed" title="Edit information about this manga" href="<?= $urlGenerator->url("manga/edit/{$item['id']}/{$name}") ?>">Edit</a>
</span> </span>
</div> </div>
<?php endif */ ?> <?php endif ?>
<div class="row"> <div class="row">
<div class="user_rating">Rating: <?= $item['user_rating'] ?> / 10</div> <div class="user_rating">Rating: <?= $item['user_rating'] ?> / 10</div>
</div> </div>
@ -53,6 +55,6 @@
<?php endforeach ?> <?php endforeach ?>
<?php endif ?> <?php endif ?>
</main> </main>
<?php /*if ($auth->is_authenticated()): ?> <?php if ($auth->is_authenticated()): ?>
<script src="<?= $urlGenerator->asset_url('js.php/g/edit') ?>"></script> <script src="<?= $urlGenerator->asset_url('js.php/g/edit') ?>"></script>
<?php endif*/ ?> <?php endif ?>

View File

@ -1,19 +1,18 @@
<?php if ($auth->is_authenticated()): ?> <?php if ($auth->is_authenticated()): ?>
<main> <main>
<h1> <h1>
Edit <?= $item['manga']['title'] ?> Edit <?= $item['manga']['titles'][0] ?>
<?= ($item['manga']['alternate_title'] != "") ? "({$item['manga']['alternate_title']})" : ""; ?>
</h1> </h1>
<form action="<?= $action ?>" method="post"> <form action="<?= $action ?>" method="post">
<table class="form"> <table class="form">
<thead> <thead>
<tr> <tr>
<th> <th>
<h3><?= $escape->html($item['manga']['title']) ?></h3> <h3><?= $escape->html(array_shift($item['manga']['titles'])) ?></h3>
<?php if($item['manga']['alternate_title'] != ""): ?> <?php foreach($item['manga']['titles'] as $title): ?>
<h4><?= $escape->html($item['manga']['alternate_title']) ?></h4> <h4><?= $escape->html($title) ?></h4>
<?php endif ?> <?php endforeach ?>
</th> </th>
<th> <th>
<article class="media"> <article class="media">
<?= $helper->img($item['manga']['image']); ?> <?= $helper->img($item['manga']['image']); ?>
@ -45,12 +44,12 @@
<input type="number" min="0" name="chapters_read" id="chapters_read" value="<?= $item['chapters']['read'] ?>" /> / <?= $item['chapters']['total'] ?> <input type="number" min="0" name="chapters_read" id="chapters_read" value="<?= $item['chapters']['read'] ?>" /> / <?= $item['chapters']['total'] ?>
</td> </td>
</tr> </tr>
<tr> <? /*<tr>
<td><label for="volumes_read">Volumes Read</label></td> <td><label for="volumes_read">Volumes Read</label></td>
<td> <td>
<input type="number" min="0" name="volumes_read" id="volumes_read" value="<?= $item['volumes']['read'] ?>" /> / <?= $item['volumes']['total'] ?> <input type="number" min="0" name="volumes_read" id="volumes_read" value="<?= $item['volumes']['read'] ?>" /> / <?= $item['volumes']['total'] ?>
</td> </td>
</tr> </tr> */ ?>
<tr> <tr>
<td><label for="rereading_flag">Rereading?</label></td> <td><label for="rereading_flag">Rereading?</label></td>
<td> <td>

View File

@ -10,9 +10,9 @@
<table> <table>
<thead> <thead>
<tr> <tr>
<?php /*if ($auth->is_authenticated()): ?> <?php if ($auth->is_authenticated()): ?>
<th>&nbsp;</th> <th>&nbsp;</th>
<?php endif*/ ?> <?php endif ?>
<th>Title</th> <th>Title</th>
<th>Rating</th> <th>Rating</th>
<th>Completed Chapters</th> <th>Completed Chapters</th>
@ -23,16 +23,18 @@
<tbody> <tbody>
<?php foreach($items as $item): ?> <?php foreach($items as $item): ?>
<tr id="manga-<?= $item['id'] ?>"> <tr id="manga-<?= $item['id'] ?>">
<?php /*if($auth->is_authenticated()): ?> <?php if($auth->is_authenticated()): ?>
<td> <td>
<a class="bracketed" href="<?= $urlGenerator->url("manga/edit/{$item['id']}/{$name}") ?>">Edit</a> <a class="bracketed" href="<?= $urlGenerator->url("manga/edit/{$item['id']}/{$name}") ?>">Edit</a>
</td> </td>
<?php endif*/ ?> <?php endif ?>
<td class="align_left"> <td class="align_left">
<a href="<?= $url->generate('manga.details', ['id' => $item['manga']['slug']]) ?>"> <a href="<?= $url->generate('manga.details', ['id' => $item['manga']['slug']]) ?>">
<?= $item['manga']['title'] ?> <?= array_shift($item['manga']['titles']) ?>
</a> </a>
<?= ( ! is_null($item['manga']['alternate_title'])) ? " &middot; " . $item['manga']['alternate_title'] : "" ?> <?php foreach($item['manga']['titles'] as $title): ?>
<br /><?= $title ?>
<?php endforeach ?>
</td> </td>
<td><?= $item['user_rating'] ?> / 10</td> <td><?= $item['user_rating'] ?> / 10</td>
<td><?= $item['chapters']['read'] ?> / <?= $item['chapters']['total'] ?></td> <td><?= $item['chapters']['read'] ?> / <?= $item['chapters']['total'] ?></td>

View File

@ -53,12 +53,11 @@ $whoops = new Run();
$defaultHandler = new PrettyPageHandler(); $defaultHandler = new PrettyPageHandler();
$whoops->pushHandler($defaultHandler); $whoops->pushHandler($defaultHandler);
// Set up json handler for ajax errors
//$jsonHandler = new JsonResponseHandler();
//$whoops->pushHandler($jsonHandler);
// Register as the error handler // Register as the error handler
$whoops->register(); if (array_key_exists('whoops', $_GET))
{
$whoops->register();
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Dependency Injection setup // Dependency Injection setup

View File

@ -1085,7 +1085,9 @@ a:hover, a:active {
display:block; display:block;
} }
.media > .name > a { .media > .name a,
.media > .name a small
{
background:none; background:none;
color:#fff; color:#fff;
text-shadow:1px 2px 1px rgba(0, 0, 0, .85); text-shadow:1px 2px 1px rgba(0, 0, 0, .85);

View File

@ -354,7 +354,9 @@ a:hover, a:active {
display:block; display:block;
} }
.media > .name > a { .media > .name a,
.media > .name a small
{
background:none; background:none;
color:#fff; color:#fff;
text-shadow: var(--shadow); text-shadow: var(--shadow);

View File

@ -16,13 +16,20 @@
namespace Aviat\AnimeClient\API; namespace Aviat\AnimeClient\API;
use Aviat\AnimeClient\API\Kitsu\Enum\AnimeWatchingStatus; use Aviat\AnimeClient\API\Kitsu\Enum\{AnimeAiringStatus, AnimeWatchingStatus};
use DateTimeImmutable;
/** /**
* Constants and mappings for the Kitsu API * Constants and mappings for the Kitsu API
*/ */
class Kitsu { class Kitsu {
const AUTH_URL = 'https://kitsu.io/api/oauth/token';
/**
* Map of Kitsu status to label for select menus
*
* @return array
*/
public static function getStatusToSelectMap() public static function getStatusToSelectMap()
{ {
return [ return [
@ -33,4 +40,147 @@ class Kitsu {
AnimeWatchingStatus::DROPPED => 'Dropped' AnimeWatchingStatus::DROPPED => 'Dropped'
]; ];
} }
/**
* Determine whether an anime is airing, finished airing, or has not yet aired
*
* @param string $startDate
* @param string $endDate
* @return string
*/
public static function getAiringStatus(string $startDate = null, string $endDate = null): string
{
$startAirDate = new DateTimeImmutable($startDate ?? 'tomorrow');
$endAirDate = new DateTimeImmutable($endDate ?? 'tomorrow');
$now = new DateTimeImmutable();
$isDoneAiring = $now > $endAirDate;
$isCurrentlyAiring = ($now > $startAirDate) && ! $isDoneAiring;
switch (true)
{
case $isCurrentlyAiring:
return AnimeAiringStatus::AIRING;
case $isDoneAiring:
return AnimeAiringStatus::FINISHED_AIRING;
default:
return AnimeAiringStatus::NOT_YET_AIRED;
}
}
/**
* Filter out duplicate and very similar names from
*
* @param array $data The 'attributes' section of the api data response
* @return array List of alternate titles
*/
public static function filterTitles(array $data): array
{
// The 'canonical' title is always returned
$valid = [$data['canonicalTitle']];
if (array_key_exists('titles', $data))
{
foreach($data['titles'] as $alternateTitle)
{
if (self::titleIsUnique($alternateTitle, $valid))
{
$valid[] = $alternateTitle;
}
}
}
return $valid;
}
/**
* Reorganizes 'included' data to be keyed by
* type => [
* id => data/attributes,
* ]
*
* @param array $includes
* @return array
*/
public static function organizeIncludes(array $includes): array
{
$organized = [];
foreach ($includes as $item)
{
$type = $item['type'];
$id = $item['id'];
$organized[$type] = $organized[$type] ?? [];
$organized[$type][$id] = $item['attributes'];
if (array_key_exists('relationships', $item))
{
$organized[$type][$id]['relationships'] = self::organizeRelationships($item['relationships']);
}
}
return $organized;
}
/**
* Reorganize relationship mappings to make them simpler to use
*
* Remove verbose structure, and just map:
* type => [ idArray ]
*
* @param array $relationships
* @return array
*/
public static function organizeRelationships(array $relationships): array
{
$organized = [];
foreach($relationships as $key => $data)
{
if ( ! array_key_exists('data', $data))
{
continue;
}
$organized[$key] = $organized[$key] ?? [];
foreach ($data['data'] as $item)
{
$organized[$key][] = $item['id'];
}
}
return $organized;
}
/**
* Determine if an alternate title is unique enough to list
*
* @param string $title
* @param array $existingTitles
* @return bool
*/
private static function titleIsUnique(string $title = null, array $existingTitles): bool
{
if (empty($title))
{
return false;
}
foreach($existingTitles as $existing)
{
$isSubset = stripos($existing, $title) !== FALSE;
$diff = levenshtein($existing, $title);
$onlydifferentCase = (mb_strtolower($existing) === mb_strtolower($title));
if ($diff < 3 || $isSubset || $onlydifferentCase)
{
return false;
}
}
return true;
}
} }

View File

@ -1,108 +1,108 @@
<?php declare(strict_types=1); <?php declare(strict_types=1);
/** /**
* Anime List Client * Anime List Client
* *
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists * An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
* *
* PHP version 7 * PHP version 7
* *
* @package AnimeListClient * @package AnimeListClient
* @author Timothy J. Warren <tim@timshomepage.net> * @author Timothy J. Warren <tim@timshomepage.net>
* @copyright 2015 - 2016 Timothy J. Warren * @copyright 2015 - 2016 Timothy J. Warren
* @license http://www.opensource.org/licenses/mit-license.html MIT License * @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 4.0 * @version 4.0
* @link https://github.com/timw4mail/HummingBirdAnimeClient * @link https://github.com/timw4mail/HummingBirdAnimeClient
*/ */
namespace Aviat\AnimeClient\API\Kitsu; namespace Aviat\AnimeClient\API\Kitsu;
use Aviat\AnimeClient\AnimeClient; use Aviat\AnimeClient\AnimeClient;
use Aviat\Ion\Di\{ContainerAware, ContainerInterface}; use Aviat\Ion\Di\{ContainerAware, ContainerInterface};
/** /**
* Kitsu API Authentication * Kitsu API Authentication
*/ */
class Auth { class Auth {
use ContainerAware; use ContainerAware;
/** /**
* Anime API Model * Anime API Model
* *
* @var \Aviat\AnimeClient\API\Kitsu\Model * @var \Aviat\AnimeClient\API\Kitsu\Model
*/ */
protected $model; protected $model;
/** /**
* Session object * Session object
* *
* @var Aura\Session\Segment * @var Aura\Session\Segment
*/ */
protected $segment; protected $segment;
/** /**
* Constructor * Constructor
* *
* @param ContainerInterface $container * @param ContainerInterface $container
*/ */
public function __construct(ContainerInterface $container) public function __construct(ContainerInterface $container)
{ {
$this->setContainer($container); $this->setContainer($container);
$this->segment = $container->get('session') $this->segment = $container->get('session')
->getSegment(AnimeClient::SESSION_SEGMENT); ->getSegment(AnimeClient::SESSION_SEGMENT);
$this->model = $container->get('kitsu-model'); $this->model = $container->get('kitsu-model');
} }
/** /**
* Make the appropriate authentication call, * Make the appropriate authentication call,
* and save the resulting auth token if successful * and save the resulting auth token if successful
* *
* @param string $password * @param string $password
* @return boolean * @return boolean
*/ */
public function authenticate($password) public function authenticate($password)
{ {
$config = $this->container->get('config'); $config = $this->container->get('config');
$username = $config->get(['kitsu_username']); $username = $config->get(['kitsu_username']);
$auth_token = $this->model->authenticate($username, $password); $auth_token = $this->model->authenticate($username, $password);
if (FALSE !== $auth_token) if (FALSE !== $auth_token)
{ {
$this->segment->set('auth_token', $auth_token); $this->segment->set('auth_token', $auth_token);
return TRUE; return TRUE;
} }
return FALSE; return FALSE;
} }
/** /**
* Check whether the current user is authenticated * Check whether the current user is authenticated
* *
* @return boolean * @return boolean
*/ */
public function is_authenticated() public function is_authenticated()
{ {
return ($this->get_auth_token() !== FALSE); return ($this->get_auth_token() !== FALSE);
} }
/** /**
* Clear authentication values * Clear authentication values
* *
* @return void * @return void
*/ */
public function logout() public function logout()
{ {
$this->segment->clear(); $this->segment->clear();
} }
/** /**
* Retrieve the authentication token from the session * Retrieve the authentication token from the session
* *
* @return string|false * @return string|false
*/ */
public function get_auth_token() public function get_auth_token()
{ {
return $this->segment->get('auth_token', FALSE); return $this->segment->get('auth_token', FALSE);
} }
} }
// End of KitsuAuth.php // End of KitsuAuth.php

View File

@ -17,6 +17,7 @@
namespace Aviat\AnimeClient\API\Kitsu; namespace Aviat\AnimeClient\API\Kitsu;
use Aviat\AnimeClient\AnimeClient; use Aviat\AnimeClient\AnimeClient;
use Aviat\AnimeClient\API\Kitsu as K;
use Aviat\AnimeClient\API\Kitsu\Transformer\{ use Aviat\AnimeClient\API\Kitsu\Transformer\{
AnimeTransformer, AnimeListTransformer, MangaTransformer, MangaListTransformer AnimeTransformer, AnimeListTransformer, MangaTransformer, MangaListTransformer
}; };
@ -92,7 +93,7 @@ class KitsuModel {
*/ */
public function authenticate(string $username, string $password) public function authenticate(string $username, string $password)
{ {
$data = $this->postRequest(AnimeClient::KITSU_AUTH_URL, [ $data = $this->postRequest(K::AUTH_URL, [
'form_params' => [ 'form_params' => [
'grant_type' => 'password', 'grant_type' => 'password',
'username' => $username, 'username' => $username,
@ -164,7 +165,7 @@ class KitsuModel {
'media_type' => 'Anime', 'media_type' => 'Anime',
'status' => $status, 'status' => $status,
], ],
'include' => 'media', 'include' => 'media,media.genres',
'page' => [ 'page' => [
'offset' => 0, 'offset' => 0,
'limit' => 200 'limit' => 200
@ -174,10 +175,21 @@ class KitsuModel {
]; ];
$data = $this->getRequest('library-entries', $options); $data = $this->getRequest('library-entries', $options);
$included = K::organizeIncludes($data['included']);
/*?><pre><?= print_r($included, TRUE) ?></pre><?php*/
foreach($data['data'] as $i => &$item) foreach($data['data'] as $i => &$item)
{ {
$item['anime'] = $data['included'][$i]; $item['anime'] = $included['anime'][$item['relationships']['media']['data']['id']];
$animeGenres = $item['anime']['relationships']['genres'];
foreach($animeGenres as $id)
{
$item['genres'][] = $included['genres'][$id]['name'];
}
// $item['genres'] = array_pluck($genres, 'name');
} }
$transformed = $this->animeListTransformer->transformCollection($data['data']); $transformed = $this->animeListTransformer->transformCollection($data['data']);
@ -246,7 +258,9 @@ class KitsuModel {
'filter' => [ 'filter' => [
'slug' => $slug 'slug' => $slug
], ],
'include' => 'genres,mappings,streamingLinks', 'include' => ($type === 'anime')
? 'genres,mappings,streamingLinks'
: 'genres,mappings',
] ]
]; ];

View File

@ -18,7 +18,6 @@ namespace Aviat\AnimeClient\API\Kitsu;
use Aviat\AnimeClient\AnimeClient; use Aviat\AnimeClient\AnimeClient;
use Aviat\AnimeClient\API\GuzzleTrait; use Aviat\AnimeClient\API\GuzzleTrait;
use Aviat\Ion\Di\ContainerAware;
use Aviat\Ion\Json; use Aviat\Ion\Json;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use GuzzleHttp\Cookie\CookieJar; use GuzzleHttp\Cookie\CookieJar;

View File

@ -16,6 +16,7 @@
namespace Aviat\AnimeClient\API\Kitsu\Transformer; namespace Aviat\AnimeClient\API\Kitsu\Transformer;
use Aviat\AnimeClient\API\Kitsu;
use Aviat\Ion\Transformer\AbstractTransformer; use Aviat\Ion\Transformer\AbstractTransformer;
/** /**
@ -34,49 +35,34 @@ class AnimeListTransformer extends AbstractTransformer {
{ {
/* ?><pre><?= print_r($item, TRUE) ?></pre><?php /* ?><pre><?= print_r($item, TRUE) ?></pre><?php
// die(); */ // die(); */
$anime =& $item['anime']; $anime = $item['anime']['attributes'] ?? $item['anime'];
$genres = $item['anime']['genres'] ?? []; $genres = $item['genres'] ?? [];
$rating = (int) 2 * $item['attributes']['rating']; $rating = (int) 2 * $item['attributes']['rating'];
$total_episodes = array_key_exists('episodeCount', $item['anime']['attributes']) $total_episodes = array_key_exists('episodeCount', $anime)
? (int) $anime['attributes']['episodeCount'] ? (int) $anime['episodeCount']
: '-'; : '-';
$alternate_title = NULL;
if (array_key_exists('titles', $item['anime']) && array_key_exists('en_jp', $anime['titles']))
{
// If the alternate title is very similar, or
// a subset of the main title, don't list the
// alternate title
$not_subset = stripos($anime['canonicalTitle'], $anime['titles']['en_jp']) === FALSE;
$diff = levenshtein($anime['canonicalTitle'], $anime['titles']['en_jp'] ?? '');
if ($not_subset && $diff >= 5)
{
$alternate_title = $anime['titles']['en_jp'];
}
}
return [ return [
'id' => $item['id'], 'id' => $item['id'],
'episodes' => [ 'episodes' => [
'watched' => $item['attributes']['progress'], 'watched' => $item['attributes']['progress'],
'total' => $total_episodes, 'total' => $total_episodes,
'length' => $anime['attributes']['episodeLength'], 'length' => $anime['episodeLength'],
], ],
'airing' => [ 'airing' => [
'status' => $anime['status'] ?? '', 'status' => Kitsu::getAiringStatus($anime['startDate'], $anime['endDate']),
'started' => $anime['attributes']['startDate'], 'started' => $anime['startDate'],
'ended' => $anime['attributes']['endDate'] 'ended' => $anime['endDate']
], ],
'anime' => [ 'anime' => [
'age_rating' => $anime['attributes']['ageRating'], 'age_rating' => $anime['ageRating'],
'title' => $anime['attributes']['canonicalTitle'], 'titles' => Kitsu::filterTitles($anime),
'alternate_title' => $alternate_title, 'slug' => $anime['slug'],
'slug' => $anime['attributes']['slug'], 'url' => $anime['url'] ?? '',
'url' => $anime['attributes']['url'] ?? '', 'type' => $this->string($anime['showType'])->upperCaseFirst()->__toString(),
'type' => $anime['attributes']['showType'], 'image' => $anime['posterImage']['small'],
'image' => $anime['attributes']['posterImage']['small'],
'genres' => $genres, 'genres' => $genres,
], ],
'watching_status' => $item['attributes']['status'], 'watching_status' => $item['attributes']['status'],

View File

@ -16,6 +16,7 @@
namespace Aviat\AnimeClient\API\Kitsu\Transformer; namespace Aviat\AnimeClient\API\Kitsu\Transformer;
use Aviat\AnimeClient\API\Kitsu;
use Aviat\Ion\Transformer\AbstractTransformer; use Aviat\Ion\Transformer\AbstractTransformer;
/** /**
@ -32,15 +33,12 @@ class AnimeTransformer extends AbstractTransformer {
*/ */
public function transform($item) public function transform($item)
{ {
?><pre><?= print_r($item, TRUE) ?></pre><?php
$item['genres'] = $item['genres'] ?? []; $item['genres'] = $item['genres'] ?? [];
sort($item['genres']); sort($item['genres']);
return [ return [
'title' => $item['canonicalTitle'], 'titles' => Kitsu::filterTitles($item),
'en_title' => $item['titles']['en_jp'], 'status' => Kitsu::getAiringStatus($item['startDate'], $item['endDate']),
'jp_title' => $item['titles']['ja_jp'],
'cover_image' => $item['posterImage']['small'], 'cover_image' => $item['posterImage']['small'],
'show_type' => $item['showType'], 'show_type' => $item['showType'],
'episode_count' => $item['episodeCount'], 'episode_count' => $item['episodeCount'],

View File

@ -16,6 +16,7 @@
namespace Aviat\AnimeClient\API\Kitsu\Transformer; namespace Aviat\AnimeClient\API\Kitsu\Transformer;
use Aviat\AnimeClient\API\Kitsu;
use Aviat\Ion\StringWrapper; use Aviat\Ion\StringWrapper;
use Aviat\Ion\Transformer\AbstractTransformer; use Aviat\Ion\Transformer\AbstractTransformer;
@ -34,6 +35,7 @@ class MangaListTransformer extends AbstractTransformer {
*/ */
public function transform($item) public function transform($item)
{ {
/*?><pre><?= print_r($item, TRUE) ?></pre><?php*/
$manga =& $item['manga']; $manga =& $item['manga'];
$rating = (is_numeric($item['attributes']['rating'])) $rating = (is_numeric($item['attributes']['rating']))
@ -59,10 +61,10 @@ class MangaListTransformer extends AbstractTransformer {
'total' => $total_volumes 'total' => $total_volumes
], ],
'manga' => [ 'manga' => [
'title' => $manga['attributes']['canonicalTitle'], 'titles' => Kitsu::filterTitles($manga['attributes']),
'alternate_title' => NULL, 'alternate_title' => NULL,
'slug' => $manga['id'], 'slug' => $manga['attributes']['slug'],
'url' => 'https://kitsu.io/manga/' . $manga['id'], 'url' => 'https://kitsu.io/manga/' . $manga['attributes']['slug'],
'type' => $manga['attributes']['mangaType'], 'type' => $manga['attributes']['mangaType'],
'image' => $manga['attributes']['posterImage']['small'], 'image' => $manga['attributes']['posterImage']['small'],
'genres' => [], //$manga['genres'], 'genres' => [], //$manga['genres'],

View File

@ -25,7 +25,6 @@ define('SRC_DIR', realpath(__DIR__));
*/ */
class AnimeClient { class AnimeClient {
const KITSU_AUTH_URL = 'https://kitsu.io/api/oauth/token';
const SESSION_SEGMENT = 'Aviat\AnimeClient\Auth'; const SESSION_SEGMENT = 'Aviat\AnimeClient\Auth';
const DEFAULT_CONTROLLER_NAMESPACE = 'Aviat\AnimeClient\Controller'; const DEFAULT_CONTROLLER_NAMESPACE = 'Aviat\AnimeClient\Controller';
const DEFAULT_CONTROLLER = 'Aviat\AnimeClient\Controller\Anime'; const DEFAULT_CONTROLLER = 'Aviat\AnimeClient\Controller\Anime';

View File

@ -293,7 +293,7 @@ class Anime extends BaseController {
$data = $this->model->getAnime($anime_id); $data = $this->model->getAnime($anime_id);
$this->outputHTML('anime/details', [ $this->outputHTML('anime/details', [
'title' => 'Anime &middot ' . $data['title'], 'title' => 'Anime &middot ' . $data['titles'][0],
'data' => $data, 'data' => $data,
]); ]);
} }

View File

@ -161,7 +161,7 @@ class Manga extends Controller {
public function edit($id, $status = "All") public function edit($id, $status = "All")
{ {
$this->set_session_redirect(); $this->set_session_redirect();
$item = $this->model->get_library_item($id, $status); $item = $this->model->getLibraryItem($id);
$title = $this->config->get('whose_list') . "'s Manga List &middot; Edit"; $title = $this->config->get('whose_list') . "'s Manga List &middot; Edit";
$this->outputHTML('manga/edit', [ $this->outputHTML('manga/edit', [

View File

@ -69,7 +69,7 @@ class API extends Model {
foreach ($array as $key => $item) foreach ($array as $key => $item)
{ {
$sort[$key] = $item[$sort_key]['title']; $sort[$key] = $item[$sort_key]['titles'][0];
} }
array_multisort($sort, SORT_ASC, $array); array_multisort($sort, SORT_ASC, $array);

View File

@ -83,6 +83,18 @@ class Manga extends API
return $this->kitsuModel->getManga($manga_id); return $this->kitsuModel->getManga($manga_id);
} }
/**
* Get information about a specific list item
* for editing/updating that item
*
* @param string $itemId
* @return array
*/
public function getLibraryItem(string $itemId): array
{
return $this->kitsuModel->getListItem($itemId);
}
/** /**
* Map transformed anime data to be organized by reading status * Map transformed anime data to be organized by reading status
* *