Version 5.1 - All the GraphQL #32
@ -3,8 +3,6 @@
|
||||
Update your anime/manga list on Kitsu.io and MyAnimeList.net
|
||||
|
||||
[![Build Status](https://travis-ci.org/timw4mail/HummingBirdAnimeClient.svg?branch=master)](https://travis-ci.org/timw4mail/HummingBirdAnimeClient)
|
||||
[![build status](https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient/badges/develop/build.svg)](https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient/commits/develop)
|
||||
[![coverage report](https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient/badges/develop/coverage.svg)](https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient/commits/develop)
|
||||
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/timw4mail/HummingBirdAnimeClient/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/timw4mail/HummingBirdAnimeClient/?branch=master)
|
||||
|
||||
[[Hosted Example](https://list.timshomepage.net)]
|
||||
@ -52,12 +50,8 @@ Update your anime/manga list on Kitsu.io and MyAnimeList.net
|
||||
* public/images/manga
|
||||
5. Make sure the `console` script is executable
|
||||
|
||||
### Using MAL API
|
||||
1. Update `app/config/mal.toml` with your username and password
|
||||
2. Enable MAL api in `app/config/config.toml`
|
||||
|
||||
### Server Setup
|
||||
|
||||
See the [wiki](https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient/wikis/home)
|
||||
See the [wiki](https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient/wiki)
|
||||
for more in-depth information
|
||||
|
||||
|
@ -8,13 +8,12 @@
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2017 Timothy J. Warren
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
|
||||
use const Aviat\AnimeClient\{
|
||||
DEFAULT_CONTROLLER_METHOD,
|
||||
DEFAULT_CONTROLLER
|
||||
@ -184,6 +183,16 @@ return [
|
||||
// ---------------------------------------------------------------------
|
||||
// Default / Shared routes
|
||||
// ---------------------------------------------------------------------
|
||||
'anilist-redirect' => [
|
||||
'path' => '/anilist-redirect',
|
||||
'action' => 'anilistRedirect',
|
||||
'controller' => DEFAULT_CONTROLLER,
|
||||
],
|
||||
'anilist-oauth' => [
|
||||
'path' => '/anilist-oauth',
|
||||
'action' => 'anilistCallback',
|
||||
'controller' => DEFAULT_CONTROLLER,
|
||||
],
|
||||
'image_proxy' => [
|
||||
'path' => '/public/images/{type}/{file}',
|
||||
'action' => 'images',
|
||||
|
@ -20,6 +20,7 @@ use Aura\Html\HelperLocatorFactory;
|
||||
use Aura\Router\RouterContainer;
|
||||
use Aura\Session\SessionFactory;
|
||||
use Aviat\AnimeClient\API\{
|
||||
Anilist,
|
||||
Kitsu,
|
||||
MAL,
|
||||
Kitsu\KitsuRequestBuilder,
|
||||
@ -45,11 +46,14 @@ return function (array $configArray = []) {
|
||||
|
||||
$appLogger = new Logger('animeclient');
|
||||
$appLogger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/app.log', Logger::NOTICE));
|
||||
$anilistRequestLogger = new Logger('anilist-request');
|
||||
$anilistRequestLogger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/anilist_request.log', Logger::NOTICE));
|
||||
$kitsuRequestLogger = new Logger('kitsu-request');
|
||||
$kitsuRequestLogger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/kitsu_request.log', Logger::NOTICE));
|
||||
$malRequestLogger = new Logger('mal-request');
|
||||
$malRequestLogger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/mal_request.log', Logger::NOTICE));
|
||||
$container->setLogger($appLogger);
|
||||
$container->setLogger($anilistRequestLogger, 'anilist-request');
|
||||
$container->setLogger($kitsuRequestLogger, 'kitsu-request');
|
||||
$container->setLogger($malRequestLogger, 'mal-request');
|
||||
|
||||
|
@ -11,8 +11,5 @@ whose_list = "Tim"
|
||||
# do you wish to show the anime collection?
|
||||
show_anime_collection = true
|
||||
|
||||
# do you have a My Anime List account set up in mal.toml?
|
||||
use_mal_api = false
|
||||
|
||||
# path to public directory on the server
|
||||
asset_dir = "/../../public"
|
@ -1,6 +0,0 @@
|
||||
################################################################################
|
||||
# My Anime LIst Integration Config #
|
||||
################################################################################
|
||||
|
||||
username = "timw4mail"
|
||||
password = "mysecretpassword"
|
@ -13,7 +13,7 @@
|
||||
</section>
|
||||
</section>
|
||||
<br />
|
||||
<table class="form">
|
||||
<table class="invisible form">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><label for="status">Watching Status</label></td>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<main>
|
||||
<main class="media-list">
|
||||
<?php if ($auth->isAuthenticated()): ?>
|
||||
<a class="bracketed" href="<?= $url->generate('anime.add.get') ?>">Add Item</a>
|
||||
<?php endif ?>
|
||||
@ -24,27 +24,13 @@
|
||||
<img src="<?= $urlGenerator->assetUrl("images/anime/{$item['anime']['id']}.jpg") ?>" alt="" />
|
||||
<div class="name">
|
||||
<a href="<?= $url->generate('anime.details', ['id' => $item['anime']['slug']]); ?>">
|
||||
<?= array_shift($item['anime']['titles']) ?>
|
||||
<span class="canonical"><?= $item['anime']['title'] ?></span>
|
||||
<?php foreach ($item['anime']['titles'] as $title): ?>
|
||||
<br /><small><?= $title ?></small>
|
||||
<?php endforeach ?>
|
||||
</a>
|
||||
</div>
|
||||
<div class="table">
|
||||
<?php if ($auth->isAuthenticated()): ?>
|
||||
<div class="row">
|
||||
<span class="edit">
|
||||
<a class="bracketed" title="Edit information about this anime" href="<?=
|
||||
$url->generate('edit', [
|
||||
'controller' => 'anime',
|
||||
'id' => $item['id'],
|
||||
'status' => $item['watching_status']
|
||||
]);
|
||||
?>">Edit</a>
|
||||
</span>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ($item['private'] || $item['rewatching']): ?>
|
||||
<div class="row">
|
||||
<?php foreach(['private', 'rewatching'] as $attr): ?>
|
||||
@ -77,6 +63,20 @@
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ($auth->isAuthenticated()): ?>
|
||||
<div class="row">
|
||||
<span class="edit">
|
||||
<a class="bracketed" title="Edit information about this anime" href="<?=
|
||||
$url->generate('edit', [
|
||||
'controller' => 'anime',
|
||||
'id' => $item['id'],
|
||||
'status' => $item['watching_status']
|
||||
]);
|
||||
?>">Edit</a>
|
||||
</span>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="row">
|
||||
<div class="user_rating">Rating: <?= $item['user_rating'] ?> / 10</div>
|
||||
<div class="completion">Episodes:
|
||||
@ -85,7 +85,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="media_type"><?= $escape->html($item['anime']['type']) ?></div>
|
||||
<div class="media_type"><?= $escape->html($item['anime']['show_type']) ?></div>
|
||||
<div class="airing_status"><?= $escape->html($item['airing']['status']) ?></div>
|
||||
<div class="age_rating"><?= $escape->html($item['anime']['age_rating']) ?></div>
|
||||
</div>
|
||||
|
@ -38,7 +38,7 @@
|
||||
</table>
|
||||
</div>
|
||||
<div>
|
||||
<h2><a rel="external" href="<?= $show_data['url'] ?>"><?= array_shift($show_data['titles']) ?></a></h2>
|
||||
<h2><a rel="external" href="<?= $show_data['url'] ?>"><?= $show_data['title'] ?></a></h2>
|
||||
<?php foreach ($show_data['titles'] as $title): ?>
|
||||
<h3><?= $title ?></h3>
|
||||
<?php endforeach ?>
|
||||
|
@ -2,23 +2,25 @@
|
||||
<main>
|
||||
<h2>Edit Anime List Item</h2>
|
||||
<form action="<?= $action ?>" method="post">
|
||||
<table class="form">
|
||||
<table class="invisible form">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<h3><?= $escape->html(array_shift($item['anime']['titles'])) ?></h3>
|
||||
<h3><?= $escape->html($item['anime']['title']) ?></h3>
|
||||
<?php foreach($item['anime']['titles'] as $title): ?>
|
||||
<h4><?= $escape->html($title) ?></h4>
|
||||
<?php endforeach ?>
|
||||
</th>
|
||||
<th>
|
||||
<article class="media">
|
||||
<?= $helper->img($urlGenerator->assetUrl('images/anime', "{$item['anime']['id']}.jpg")) ?>
|
||||
</article>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="9">
|
||||
<article class="media">
|
||||
<?= $helper->img($urlGenerator->assetUrl('images/anime', "{$item['anime']['id']}.jpg")) ?>
|
||||
</article>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="private">Is Private?</label></td>
|
||||
<td>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<main>
|
||||
<main class="media-list">
|
||||
<?php if ($auth->isAuthenticated()): ?>
|
||||
<a class="bracketed" href="<?= $url->generate('anime.add.get') ?>">Add Item</a>
|
||||
<?php endif ?>
|
||||
@ -42,15 +42,15 @@
|
||||
<?php endif ?>
|
||||
<td class="justify">
|
||||
<a href="<?= $url->generate('anime.details', ['id' => $item['anime']['slug']]) ?>">
|
||||
<?= array_shift($item['anime']['titles']) ?>
|
||||
<?= $item['anime']['title'] ?>
|
||||
</a>
|
||||
<?php foreach($item['anime']['titles'] as $title): ?>
|
||||
<br /><?= $title ?>
|
||||
<?php endforeach ?>
|
||||
<?php foreach ($item['anime']['titles'] as $title): ?>
|
||||
<br/><?= $title ?>
|
||||
<?php endforeach ?>
|
||||
</td>
|
||||
<td><?= $item['airing']['status'] ?></td>
|
||||
<td><?= $item['user_rating'] ?> / 10 </td>
|
||||
<td><?= $item['anime']['type'] ?></td>
|
||||
<td><?= $item['anime']['show_type'] ?></td>
|
||||
<td id="<?= $item['anime']['slug'] ?>">
|
||||
Episodes: <br />
|
||||
<span class="completed_number"><?= $item['episodes']['watched'] ?></span> / <span class="total_number"><?= $item['episodes']['total'] ?></span>
|
||||
@ -83,8 +83,8 @@
|
||||
<p><?= $escape->html($item['notes']) ?></p>
|
||||
</td>
|
||||
<td class="align_left">
|
||||
<?php sort($item['anime']['genres']) ?>
|
||||
<?= implode(', ', $item['anime']['genres']) ?>
|
||||
<?php sort($item['anime']->genres) ?>
|
||||
<?= implode(', ', $item['anime']->genres) ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
|
@ -13,7 +13,7 @@
|
||||
</section>
|
||||
</section>
|
||||
<br />
|
||||
<table class="form">
|
||||
<table class="invisible form">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><label for="media_id">Media</label></td>
|
||||
|
27
app/views/collection/cover-item.php
Normal file
27
app/views/collection/cover-item.php
Normal file
@ -0,0 +1,27 @@
|
||||
<article class="media" id="a-<?= $item['hummingbird_id'] ?>">
|
||||
<img src="<?= $urlGenerator->assetUrl("images/anime/{$item['hummingbird_id']}.jpg") ?>"
|
||||
alt="<?= $item['title'] ?> cover image"/>
|
||||
<div class="name">
|
||||
<a href="<?= $url->generate('anime.details', ['id' => $item['slug']]) ?>">
|
||||
<?= $item['title'] ?>
|
||||
<?= ($item['alternate_title'] != "") ? "<small><br />{$item['alternate_title']}</small>" : ""; ?>
|
||||
</a>
|
||||
</div>
|
||||
<div class="table">
|
||||
<?php if ($auth->isAuthenticated()): ?>
|
||||
<div class="row">
|
||||
<span class="edit">
|
||||
<a class="bracketed"
|
||||
href="<?= $url->generate($collection_type . '.collection.edit.get', [
|
||||
'id' => $item['hummingbird_id']
|
||||
]) ?>">Edit</a>
|
||||
</span>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<div class="row">
|
||||
<div class="completion">Episodes: <?= $item['episode_count'] ?></div>
|
||||
<div class="media_type"><?= $item['show_type'] ?></div>
|
||||
<div class="age_rating"><?= $item['age_rating'] ?></div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
@ -1,44 +1,25 @@
|
||||
<main>
|
||||
<main class="media-list">
|
||||
<?php if ($auth->isAuthenticated()): ?>
|
||||
<a class="bracketed" href="<?= $url->generate($collection_type . '.collection.add.get') ?>">Add Item</a>
|
||||
<?php endif ?>
|
||||
<?php if (empty($sections)): ?>
|
||||
<h3>There's nothing here!</h3>
|
||||
<?php else: ?>
|
||||
<div class="tabs">
|
||||
<?php $i = 0; ?>
|
||||
<?php foreach ($sections as $name => $items): ?>
|
||||
<section class="status">
|
||||
<input <?= $i === 0 ? 'checked="checked"' : '' ?> type="radio" id="collection-tab-<?= $i ?>" name="collection-tabs" />
|
||||
<label for="collection-tab-<?= $i ?>"><?= $name ?></label>
|
||||
<div class="content">
|
||||
<h2><?= $name ?></h2>
|
||||
<section class="media-wrap">
|
||||
<?php foreach($items as $item): ?>
|
||||
<article class="media" id="a-<?= $item['hummingbird_id'] ?>">
|
||||
<img src="<?= $urlGenerator->assetUrl("images/anime/{$item['hummingbird_id']}.jpg") ?>"
|
||||
alt="<?= $item['title'] ?> cover image" />
|
||||
<div class="name">
|
||||
<a href="<?= $url->generate('anime.details', ['id' => $item['slug']]) ?>">
|
||||
<?= $item['title'] ?>
|
||||
<?= ($item['alternate_title'] != "") ? "<small><br />{$item['alternate_title']}</small>" : ""; ?>
|
||||
</a>
|
||||
</div>
|
||||
<div class="table">
|
||||
<?php if ($auth->isAuthenticated()): ?>
|
||||
<div class="row">
|
||||
<span class="edit">
|
||||
<a class="bracketed" href="<?= $url->generate($collection_type . '.collection.edit.get', [
|
||||
'id' => $item['hummingbird_id']
|
||||
]) ?>">Edit</a>
|
||||
</span>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<div class="row">
|
||||
<div class="completion">Episodes: <?= $item['episode_count'] ?></div>
|
||||
<div class="media_type"><?= $item['show_type'] ?></div>
|
||||
<div class="age_rating"><?= $item['age_rating'] ?></div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<?php foreach ($items as $item): ?>
|
||||
<?php include __DIR__ . '/cover-item.php'; ?>
|
||||
<?php endforeach ?>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
<?php $i++; ?>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</main>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<main>
|
||||
<h2>Edit Anime Collection Item</h2>
|
||||
<form action="<?= $action_url ?>" method="post">
|
||||
<table class="form">
|
||||
<table class="invisible form" style="border:0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
@ -11,17 +11,19 @@
|
||||
<h4><?= $item['alternate_title'] ?></h4>
|
||||
<?php endif ?>
|
||||
</th>
|
||||
<th>
|
||||
<article class="media">
|
||||
<?= $helper->img($urlGenerator->assetUrl("images/anime/{$item['hummingbird_id']}.jpg")); ?>
|
||||
</article>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><label for="media_id">Media</label></td>
|
||||
<td>
|
||||
<td rowspan="4" class="align_center">
|
||||
<article class="media">
|
||||
<?= $helper->img($urlGenerator->assetUrl("images/anime/{$item['hummingbird_id']}.jpg")); ?>
|
||||
</article>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="align_right"><label for="media_id">Media</label></td>
|
||||
<td class="align_left">
|
||||
<select name="media_id" id="media_id">
|
||||
<?php foreach($media_items as $id => $name): ?>
|
||||
<option <?= $item['media_id'] == $id ? 'selected="selected"' : '' ?> value="<?= $id ?>"><?= $name ?></option>
|
||||
|
19
app/views/collection/list-item.php
Normal file
19
app/views/collection/list-item.php
Normal file
@ -0,0 +1,19 @@
|
||||
<tr>
|
||||
<?php if ($auth->isAuthenticated()): ?>
|
||||
<td>
|
||||
<a class="bracketed"
|
||||
href="<?= $url->generate($collection_type . '.collection.edit.get', ['id' => $item['hummingbird_id']]) ?>">Edit</a>
|
||||
</td>
|
||||
<?php endif ?>
|
||||
<td class="align_left">
|
||||
<a href="<?= $url->generate('anime.details', ['id' => $item['slug']]) ?>">
|
||||
<?= $item['title'] ?>
|
||||
</a>
|
||||
<?= (!empty($item['alternate_title'])) ? " <br /><small> " . $item['alternate_title'] . "</small>" : "" ?>
|
||||
</td>
|
||||
<td><?= $item['episode_count'] ?></td>
|
||||
<td><?= $item['episode_length'] ?></td>
|
||||
<td><?= $item['show_type'] ?></td>
|
||||
<td><?= $item['age_rating'] ?></td>
|
||||
<td class="align_left"><?= $item['notes'] ?></td>
|
||||
</tr>
|
@ -1,51 +1,42 @@
|
||||
<main>
|
||||
<?php if ($auth->isAuthenticated()): ?>
|
||||
<a class="bracketed" href="<?= $url->generate($collection_type . '.collection.add.get') ?>">Add Item</a>
|
||||
<?php endif ?>
|
||||
<?php if (empty($sections)): ?>
|
||||
<h3>There's nothing here!</h3>
|
||||
<?php else: ?>
|
||||
<?php foreach ($sections as $name => $items): ?>
|
||||
<h2><?= $name ?></h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<?php if($auth->isAuthenticated()): ?>
|
||||
<th>Actions</th>
|
||||
<?php endif ?>
|
||||
<th>Title</th>
|
||||
<th>Episode Count</th>
|
||||
<th>Episode Length</th>
|
||||
<th>Show Type</th>
|
||||
<th>Age Rating</th>
|
||||
<th>Notes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach($items as $item): ?>
|
||||
<tr>
|
||||
<?php if($auth->isAuthenticated()): ?>
|
||||
<td>
|
||||
<a class="bracketed" href="<?= $url->generate($collection_type . '.collection.edit.get', ['id' => $item['hummingbird_id']]) ?>">Edit</a>
|
||||
</td>
|
||||
<?php endif ?>
|
||||
<td class="align_left">
|
||||
<a href="<?= $url->generate('anime.details', ['id' => $item['slug']]) ?>">
|
||||
<?= $item['title'] ?>
|
||||
</a>
|
||||
<?= ( ! empty($item['alternate_title'])) ? " <br /><small> " . $item['alternate_title'] . "</small>" : "" ?>
|
||||
</td>
|
||||
<td><?= $item['episode_count'] ?></td>
|
||||
<td><?= $item['episode_length'] ?></td>
|
||||
<td><?= $item['show_type'] ?></td>
|
||||
<td><?= $item['age_rating'] ?></td>
|
||||
<td class="align_left"><?= $item['notes'] ?></td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
<?php endforeach ?>
|
||||
<?php endif ?>
|
||||
<?php if ($auth->isAuthenticated()): ?>
|
||||
<a class="bracketed" href="<?= $url->generate($collection_type . '.collection.add.get') ?>">Add Item</a>
|
||||
<?php endif ?>
|
||||
<?php if (empty($sections)): ?>
|
||||
<h3>There's nothing here!</h3>
|
||||
<?php else: ?>
|
||||
<?php $i = 0; ?>
|
||||
<div class="tabs">
|
||||
<?php foreach ($sections as $name => $items): ?>
|
||||
<input <?= $i === 0 ? 'checked="checked"' : '' ?> type="radio" id="collection-tab-<?= $i ?>"
|
||||
name="collection-tabs"/>
|
||||
<label for="collection-tab-<?= $i ?>"><?= $name ?></label>
|
||||
<div class="content">
|
||||
<h2><?= $name ?></h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<?php if ($auth->isAuthenticated()): ?>
|
||||
<td>Actions</td>
|
||||
<?php endif ?>
|
||||
<th>Title</th>
|
||||
<th>Episode Count</th>
|
||||
<th>Episode Length</th>
|
||||
<th>Show Type</th>
|
||||
<th>Age Rating</th>
|
||||
<th>Notes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($items as $item): ?>
|
||||
<?php include __DIR__ . '/list-item.php' ?>
|
||||
<?php endforeach ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php $i++ ?>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</main>
|
||||
<script defer="defer" src="<?= $urlGenerator->assetUrl('js.php/g/table') ?>"></script>
|
@ -1,11 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title><?= $title ?></title>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="cache-control" content="no-store" />
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self'" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=0" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=1" />
|
||||
<link rel="stylesheet" href="<?= $urlGenerator->assetUrl('css/app.min.css') ?>" />
|
||||
<link rel="icon" href="<?= $urlGenerator->assetUrl('images/icons/favicon.ico') ?>" />
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="<?= $urlGenerator->assetUrl('images/icons/apple-icon-57x57.png') ?>">
|
||||
|
@ -7,7 +7,7 @@ $lastSegment = $urlGenerator->lastSegment();
|
||||
$extraSegment = $lastSegment === 'list' ? '/list' : '';
|
||||
|
||||
?>
|
||||
<h1 class="flex flex-align-end flex-wrap">
|
||||
<div id="main-nav" class="flex flex-align-end flex-wrap">
|
||||
<span class="flex-no-wrap grow-1">
|
||||
<?php if(strpos($route_path, 'collection') === FALSE): ?>
|
||||
<?= $helper->a(
|
||||
@ -67,7 +67,7 @@ $extraSegment = $lastSegment === 'list' ? '/list' : '';
|
||||
[<?= $helper->a($url->generate('login'), "{$whose} Login") ?>]
|
||||
<?php endif ?>
|
||||
</span>
|
||||
</h1>
|
||||
</div>
|
||||
<nav>
|
||||
<?php if ($container->get('util')->isViewPage()): ?>
|
||||
<?= $helper->menu($menu_name) ?>
|
||||
|
@ -13,7 +13,7 @@
|
||||
</section>
|
||||
</section>
|
||||
<br />
|
||||
<table class="form">
|
||||
<table class="invisible form">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><label for="status">Reading Status</label></td>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<main>
|
||||
<main class="media-list">
|
||||
<?php if ($auth->isAuthenticated()): ?>
|
||||
<a class="bracketed" href="<?= $url->generate('manga.add.get') ?>">Add Item</a>
|
||||
<?php endif ?>
|
||||
@ -26,7 +26,7 @@
|
||||
<img src="<?= $urlGenerator->assetUrl('images/manga', "{$item['manga']['id']}.jpg") ?>" />
|
||||
<div class="name">
|
||||
<a href="<?= $url->generate('manga.details', ['id' => $item['manga']['slug']]) ?>">
|
||||
<?= $escape->html(array_shift($item['manga']['titles'])) ?>
|
||||
<?= $escape->html($item['manga']['title']) ?>
|
||||
<?php foreach($item['manga']['titles'] as $title): ?>
|
||||
<br /><small><?= $title ?></small>
|
||||
<?php endforeach ?>
|
||||
@ -73,8 +73,8 @@
|
||||
Chapters: <span class="chapters_read"><?= $item['chapters']['read'] ?></span> /
|
||||
<span class="chapter_count"><?= $item['chapters']['total'] ?></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<?php /* </div>
|
||||
<div class="row"> */ ?>
|
||||
<div class="volume_completion">
|
||||
Volumes: <span class="volume_count"><?= $item['volumes']['total'] ?></span>
|
||||
</div>
|
||||
|
@ -1,67 +1,73 @@
|
||||
<?php if ($auth->isAuthenticated()): ?>
|
||||
<main>
|
||||
<h1>
|
||||
Edit <?= $item['manga']['titles'][0] ?>
|
||||
</h1>
|
||||
<h2>
|
||||
Edit Manga List Item
|
||||
</h2>
|
||||
<form action="<?= $action ?>" method="post">
|
||||
<table class="form">
|
||||
<thead>
|
||||
<table class="invisible form">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<h3><?= $escape->html(array_shift($item['manga']['titles'])) ?></h3>
|
||||
<?php foreach($item['manga']['titles'] as $title): ?>
|
||||
<h4><?= $escape->html($title) ?></h4>
|
||||
<?php endforeach ?>
|
||||
</th>
|
||||
<th>
|
||||
<h3><?= $escape->html($item['manga']['title']) ?></h3>
|
||||
<?php foreach ($item['manga']['titles'] as $title): ?>
|
||||
<h4><?= $escape->html($title) ?></h4>
|
||||
<?php endforeach ?>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="9">
|
||||
<article class="media">
|
||||
<?= $helper->img($urlGenerator->assetUrl('images/manga', "{$item['manga']['id']}.jpg")); ?>
|
||||
</article>
|
||||
</th>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><label for="status">Reading Status</label></td>
|
||||
<td>
|
||||
<select name="status" id="status">
|
||||
<?php foreach($status_list as $val => $status): ?>
|
||||
<option <?php if($item['reading_status'] === $val): ?>selected="selected"<?php endif ?>
|
||||
value="<?= $val ?>"><?= $status ?></option>
|
||||
<?php endforeach ?>
|
||||
<?php foreach ($status_list as $val => $status): ?>
|
||||
<option <?php if ($item['reading_status'] === $val): ?>selected="selected"<?php endif ?>
|
||||
value="<?= $val ?>"><?= $status ?></option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="series_rating">Rating</label></td>
|
||||
<td>
|
||||
<input type="number" min="0" max="10" maxlength="2" name="new_rating" value="<?= $item['user_rating'] ?>" id="series_rating" size="2" /> / 10
|
||||
<input type="number" min="0" max="10" maxlength="2" name="new_rating"
|
||||
value="<?= $item['user_rating'] ?>" id="series_rating" size="2"/> / 10
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="chapters_read">Chapters Read</label></td>
|
||||
<td>
|
||||
<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>
|
||||
</tr>
|
||||
<? /*<tr>
|
||||
<tr>
|
||||
<td><label for="volumes_read">Volumes Read</label></td>
|
||||
<td>
|
||||
<input type="number" min="0" name="volumes_read" id="volumes_read" value="<?= $item['volumes']['read'] ?>" /> / <?= $item['volumes']['total'] ?>
|
||||
<?php /*<input type="number" disabled="disabled" min="0" name="volumes_read" id="volumes_read" value="" /> */ ?>
|
||||
- / <?= $item['volumes']['total'] ?>
|
||||
</td>
|
||||
</tr> */ ?>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="rereading_flag">Rereading?</label></td>
|
||||
<td>
|
||||
<input type="checkbox" name="rereading" id="rereading_flag"
|
||||
<?php if($item['rereading'] === TRUE): ?>checked="checked"<?php endif ?>
|
||||
<?php if ($item['rereading'] === TRUE): ?>checked="checked"<?php endif ?>
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="reread_count">Reread Count</label></td>
|
||||
<td>
|
||||
<input type="number" min="0" id="reread_count" name="reread_count" value="<?= $item['reread'] ?>" />
|
||||
<input type="number" min="0" id="reread_count" name="reread_count"
|
||||
value="<?= $item['reread'] ?>"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -73,16 +79,16 @@
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<input type="hidden" value="<?= $item['id'] ?>" name="id" />
|
||||
<input type="hidden" value="<?= $item['mal_id'] ?>" name="mal_id" />
|
||||
<input type="hidden" value="<?= $item['manga']['slug'] ?>" name="manga_id" />
|
||||
<input type="hidden" value="<?= $item['user_rating'] ?>" name="old_rating" />
|
||||
<input type="hidden" value="true" name="edit" />
|
||||
<input type="hidden" value="<?= $item['id'] ?>" name="id"/>
|
||||
<input type="hidden" value="<?= $item['mal_id'] ?>" name="mal_id"/>
|
||||
<input type="hidden" value="<?= $item['manga']['slug'] ?>" name="manga_id"/>
|
||||
<input type="hidden" value="<?= $item['user_rating'] ?>" name="old_rating"/>
|
||||
<input type="hidden" value="true" name="edit"/>
|
||||
<button type="submit">Submit</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
<fieldset>
|
||||
<legend>Danger Zone</legend>
|
||||
@ -92,8 +98,8 @@
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<input type="hidden" value="<?= $item['id'] ?>" name="id" />
|
||||
<input type="hidden" value="<?= $item['mal_id'] ?>" name="mal_id" />
|
||||
<input type="hidden" value="<?= $item['id'] ?>" name="id"/>
|
||||
<input type="hidden" value="<?= $item['mal_id'] ?>" name="mal_id"/>
|
||||
<button type="submit" class="danger">Delete Entry</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<main>
|
||||
<main class="media-list">
|
||||
<?php if ($auth->isAuthenticated()): ?>
|
||||
<a class="bracketed" href="<?= $url->generate('manga.add.get') ?>">Add Item</a>
|
||||
<?php endif ?>
|
||||
@ -14,7 +14,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<?php if ($auth->isAuthenticated()): ?>
|
||||
<th> </th>
|
||||
<td> </td>
|
||||
<?php endif ?>
|
||||
<th>Title</th>
|
||||
<th>Rating</th>
|
||||
@ -39,7 +39,7 @@
|
||||
<?php endif ?>
|
||||
<td class="align_left">
|
||||
<a href="<?= $url->generate('manga.details', ['id' => $item['manga']['slug']]) ?>">
|
||||
<?= array_shift($item['manga']['titles']) ?>
|
||||
<?= $item['manga']['title'] ?>
|
||||
</a>
|
||||
<?php foreach($item['manga']['titles'] as $title): ?>
|
||||
<br /><?= $title ?>
|
||||
|
@ -39,6 +39,7 @@
|
||||
"phpmd/phpmd": "^2.4",
|
||||
"phpstan/phpstan": "^0.9.1",
|
||||
"phpunit/phpunit": "^6.0",
|
||||
"roave/security-advisories": "dev-master",
|
||||
"robmorgan/phinx": "^0.9.1",
|
||||
"sebastian/phpcpd": "^3.0",
|
||||
"spatie/phpunit-snapshot-assertions": "^1.2.0",
|
||||
|
@ -28,9 +28,12 @@ if ($timezone === '' || $timezone === FALSE)
|
||||
// Load composer autoloader
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
$whoops = new \Whoops\Run;
|
||||
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
|
||||
$whoops->register();
|
||||
// if (array_key_exists('ENV', $_ENV) && $_ENV['ENV'] === 'development')
|
||||
{
|
||||
$whoops = new \Whoops\Run;
|
||||
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
|
||||
$whoops->register();
|
||||
}
|
||||
|
||||
// Define base directories
|
||||
$APP_DIR = _dir(__DIR__, 'app');
|
||||
|
@ -7,7 +7,7 @@ const atImport = require('postcss-import');
|
||||
const cssNext = require('postcss-cssnext');
|
||||
const cssNano = require('cssnano');
|
||||
|
||||
const css = fs.readFileSync('css/base.css', 'utf8');
|
||||
const css = fs.readFileSync('css/base.css', 'utf-8');
|
||||
|
||||
postcss()
|
||||
.use(atImport())
|
||||
|
2
public/css/app.min.css
vendored
2
public/css/app.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -22,7 +22,7 @@ use Aviat\AnimeClient\API\HummingbirdClient;
|
||||
use Aviat\Ion\{Json, JsonException};
|
||||
|
||||
// Include Amp and Artax
|
||||
require_once('../vendor/autoload.php');
|
||||
require_once '../vendor/autoload.php';
|
||||
|
||||
//Creative rewriting of /g/groupname to ?g=groupname
|
||||
$pi = $_SERVER['PATH_INFO'];
|
||||
@ -318,10 +318,11 @@ class JSMin {
|
||||
$lastModifiedDate = gmdate('D, d M Y H:i:s', $lastModified);
|
||||
$expiresDate = gmdate('D, d M Y H:i:s', $expires);
|
||||
|
||||
header("Content-Type: {$mimeType}; charset=utf8");
|
||||
header("Cache-control: public, max-age=691200, must-revalidate");
|
||||
header("Last-Modified: {$lastModifiedDate} GMT");
|
||||
header("Content-Type: {$mimeType}; charset=utf-8");
|
||||
header('Cache-control: public, max-age=691200, must-revalidate');
|
||||
header("Expires: {$expiresDate} GMT");
|
||||
header("Last-Modified: {$lastModifiedDate} GMT");
|
||||
header('X-Content-Type-Options: no-sniff');
|
||||
|
||||
echo $content;
|
||||
|
||||
@ -335,7 +336,7 @@ class JSMin {
|
||||
*/
|
||||
public static function send304()
|
||||
{
|
||||
header("status: 304 Not Modified", true, 304);
|
||||
header('status: 304 Not Modified', true, 304);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,8 +39,17 @@
|
||||
data,
|
||||
dataType: 'json',
|
||||
type: 'POST',
|
||||
success: () => {
|
||||
if (data.data.status === 'completed') {
|
||||
success: (res) => {
|
||||
const resData = JSON.parse(res);
|
||||
|
||||
if (resData.errors) {
|
||||
_.hide(_.$('#loading-shadow')[ 0 ]);
|
||||
_.showMessage('error', `Failed to update ${title}. `);
|
||||
_.scrollToTop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (resData.data.attributes.status === 'completed') {
|
||||
_.hide(parentSel);
|
||||
}
|
||||
|
||||
|
@ -273,7 +273,7 @@ var AnimeClient = (function(w) {
|
||||
responseText = request.responseText;
|
||||
}
|
||||
|
||||
if (request.status > 400) {
|
||||
if (request.status > 299) {
|
||||
config.error.call(null, request.status, responseText, request.response);
|
||||
} else {
|
||||
config.success.call(null, responseText, request.status);
|
||||
|
@ -1,16 +1,15 @@
|
||||
const LightTableSorter = (function() {
|
||||
let _cellIndex, _onClickEvent, _order, _reset, _sort, _text, _th, _toggle;
|
||||
_th = null;
|
||||
_cellIndex = null;
|
||||
_order = '';
|
||||
_text = function(row) {
|
||||
return row.cells.item(_cellIndex).textContent.toLowerCase();
|
||||
'use strict';
|
||||
const LightTableSorter = (() => {
|
||||
let th = null;
|
||||
let cellIndex = null;
|
||||
let order = '';
|
||||
const text = (row) => {
|
||||
return row.cells.item(cellIndex).textContent.toLowerCase();
|
||||
};
|
||||
_sort = function(a, b) {
|
||||
let n, textA, textB;
|
||||
textA = _text(a);
|
||||
textB = _text(b);
|
||||
n = parseInt(textA, 10);
|
||||
const sort = (a, b) => {
|
||||
let textA = text(a);
|
||||
let textB = text(b);
|
||||
const n = parseInt(textA, 10);
|
||||
if (n) {
|
||||
textA = n;
|
||||
textB = parseInt(textB, 10);
|
||||
@ -23,51 +22,49 @@ const LightTableSorter = (function() {
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
_toggle = function() {
|
||||
let c;
|
||||
c = _order !== 'sorting_asc' ? 'sorting_asc' : 'sorting_desc';
|
||||
_th.className = (_th.className.replace(_order, '') + ' ' + c).trim();
|
||||
return _order = c;
|
||||
const toggle = () => {
|
||||
const c = order !== 'sorting_asc' ? 'sorting_asc' : 'sorting_desc';
|
||||
th.className = (th.className.replace(order, '') + ' ' + c).trim();
|
||||
return order = c;
|
||||
};
|
||||
_reset = function() {
|
||||
_th.className = _th.className.replace('sorting_asc', 'sorting').replace('sorting_desc', 'sorting');
|
||||
return _order = '';
|
||||
const reset = () => {
|
||||
th.classList.remove('sorting_asc', 'sorting_desc');
|
||||
th.classList.add('sorting');
|
||||
return order = '';
|
||||
};
|
||||
_onClickEvent = function(e) {
|
||||
let row, rows, tbody, _i, _len;
|
||||
if (_th && (_cellIndex !== e.target.cellIndex)) {
|
||||
_reset();
|
||||
const onClickEvent = (e) => {
|
||||
if (th && (cellIndex !== e.target.cellIndex)) {
|
||||
reset();
|
||||
}
|
||||
_th = e.target;
|
||||
if (_th.nodeName.toLowerCase() === 'th') {
|
||||
_cellIndex = _th.cellIndex;
|
||||
tbody = _th.offsetParent.getElementsByTagName('tbody')[0];
|
||||
rows = tbody.rows;
|
||||
th = e.target;
|
||||
if (th.nodeName.toLowerCase() === 'th') {
|
||||
cellIndex = th.cellIndex;
|
||||
const tbody = th.offsetParent.getElementsByTagName('tbody')[0];
|
||||
let rows = Array.from(tbody.rows);
|
||||
if (rows) {
|
||||
rows = Array.prototype.slice.call(rows, 0);
|
||||
rows = Array.prototype.sort.call(rows, _sort);
|
||||
if (_order === 'sorting_asc') {
|
||||
Array.prototype.reverse.call(rows);
|
||||
rows.sort(sort);
|
||||
if (order === 'sorting_asc') {
|
||||
rows.reverse();
|
||||
}
|
||||
_toggle();
|
||||
toggle();
|
||||
tbody.innerHtml = '';
|
||||
for (_i = 0, _len = rows.length; _i < _len; _i++) {
|
||||
row = rows[_i];
|
||||
|
||||
rows.forEach(row => {
|
||||
tbody.appendChild(row);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
return {
|
||||
init: function() {
|
||||
init: () => {
|
||||
let ths = document.getElementsByTagName('th');
|
||||
let _results = [];
|
||||
for (let _i = 0, _len = ths.length; _i < _len; _i++) {
|
||||
let th = ths[_i];
|
||||
th.className = 'sorting';
|
||||
_results.push(th.onclick = _onClickEvent);
|
||||
let results = [];
|
||||
for (let i = 0, len = ths.length; i < len; i++) {
|
||||
let th = ths[i];
|
||||
th.classList.add('sorting');
|
||||
results.push(th.onclick = onClickEvent);
|
||||
}
|
||||
return _results;
|
||||
return results;
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
0
public/js/cache/.gitkeep
vendored
Normal file → Executable file
0
public/js/cache/.gitkeep
vendored
Normal file → Executable file
@ -4,10 +4,10 @@
|
||||
"watch": "watch 'npm run build' --filter=./cssfilter.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cssnano": "^3.10.0",
|
||||
"cssnano": "^4.0.5",
|
||||
"postcss-cachify": "^1.3.1",
|
||||
"postcss-cssnext": "^3.0.0",
|
||||
"postcss-import": "^10.0.0",
|
||||
"postcss-import": "^12.0.0",
|
||||
"watch": "^1.0.2"
|
||||
}
|
||||
}
|
||||
|
1177
public/yarn.lock
1177
public/yarn.lock
File diff suppressed because it is too large
Load Diff
73
src/API/Anilist.php
Normal file
73
src/API/Anilist.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 7
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\API;
|
||||
|
||||
use Aviat\AnimeClient\API\Enum\{
|
||||
AnimeWatchingStatus\Kitsu as KAWS,
|
||||
MangaReadingStatus\Kitsu as KMRS
|
||||
};
|
||||
use Aviat\AnimeClient\API\Enum\{
|
||||
AnimeWatchingStatus\Anilist as AnimeWatchingStatus,
|
||||
MangaReadingStatus\Anilist as MangaReadingStatus
|
||||
};
|
||||
|
||||
/**
|
||||
* Constants and mappings for the Anilist API
|
||||
*/
|
||||
final class Anilist {
|
||||
const AUTH_URL = 'https://anilist.co/api/v2/oauth/authorize';
|
||||
const BASE_URL = 'https://graphql.anilist.co';
|
||||
|
||||
const KITSU_ANILIST_WATCHING_STATUS_MAP = [
|
||||
KAWS::WATCHING => AnimeWatchingStatus::WATCHING,
|
||||
KAWS::COMPLETED => AnimeWatchingStatus::COMPLETED,
|
||||
KAWS::ON_HOLD => AnimeWatchingStatus::ON_HOLD,
|
||||
KAWS::DROPPED => AnimeWatchingStatus::DROPPED,
|
||||
KAWS::PLAN_TO_WATCH => AnimeWatchingStatus::PLAN_TO_WATCH,
|
||||
];
|
||||
|
||||
const ANILIST_KITSU_WATCHING_STATUS_MAP = [
|
||||
'CURRENT' => KAWS::WATCHING,
|
||||
'COMPLETED' => KAWS::COMPLETED,
|
||||
'PAUSED' => KAWS::ON_HOLD,
|
||||
'DROPPED' => KAWS::DROPPED,
|
||||
'PLANNING' => KAWS::PLAN_TO_WATCH,
|
||||
];
|
||||
|
||||
public static function getIdToWatchingStatusMap()
|
||||
{
|
||||
return [
|
||||
'CURRENT' => AnimeWatchingStatus::WATCHING,
|
||||
'COMPLETED' => AnimeWatchingStatus::COMPLETED,
|
||||
'PAUSED' => AnimeWatchingStatus::ON_HOLD,
|
||||
'DROPPED' => AnimeWatchingStatus::DROPPED,
|
||||
'PLANNING' => AnimeWatchingStatus::PLAN_TO_WATCH,
|
||||
'REPEATING' => AnimeWatchingStatus::WATCHING,
|
||||
];
|
||||
}
|
||||
|
||||
public static function getIdToReadingStatusMap()
|
||||
{
|
||||
return [
|
||||
'CURRENT' => MangaReadingStatus::READING,
|
||||
'COMPLETED' => MangaReadingStatus::COMPLETED,
|
||||
'PAUSED' => MangaReadingStatus::ON_HOLD,
|
||||
'DROPPED' => MangaReadingStatus::DROPPED,
|
||||
'PLANNING' => MangaReadingStatus::PLAN_TO_READ
|
||||
];
|
||||
}
|
||||
}
|
49
src/API/Anilist/AnilistRequestBuilder.php
Normal file
49
src/API/Anilist/AnilistRequestBuilder.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 7
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\API\Anilist;
|
||||
|
||||
use const Aviat\AnimeClient\USER_AGENT;
|
||||
|
||||
use Aviat\AnimeClient\API\APIRequestBuilder;
|
||||
|
||||
final class AnilistRequestBuilder extends APIRequestBuilder {
|
||||
|
||||
/**
|
||||
* The base url for api requests
|
||||
* @var string $base_url
|
||||
*/
|
||||
protected $baseUrl = 'https://kitsu.io/api/edge/';
|
||||
|
||||
/**
|
||||
* Valid HTTP request methods
|
||||
* @var array
|
||||
*/
|
||||
protected $validMethods = ['POST'];
|
||||
|
||||
/**
|
||||
* HTTP headers to send with every request
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $defaultHeaders = [
|
||||
'User-Agent' => USER_AGENT,
|
||||
'Accept' => 'application/vnd.api+json',
|
||||
'Content-Type' => 'application/vnd.api+json',
|
||||
'CLIENT_ID' => 'dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd',
|
||||
'CLIENT_SECRET' => '54d7307928f63414defd96399fc31ba847961ceaecef3a5fd93144e960c0e151',
|
||||
];
|
||||
}
|
179
src/API/Anilist/AnilistTrait.php
Normal file
179
src/API/Anilist/AnilistTrait.php
Normal file
@ -0,0 +1,179 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 7
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\API\MAL;
|
||||
|
||||
use function Amp\Promise\wait;
|
||||
|
||||
use Aviat\AnimeClient\API\{
|
||||
Anilist,
|
||||
HummingbirdClient
|
||||
};
|
||||
|
||||
trait AnilistTrait {
|
||||
|
||||
/**
|
||||
* The request builder for the MAL API
|
||||
* @var AnilistRequestBuilder
|
||||
*/
|
||||
protected $requestBuilder;
|
||||
|
||||
/**
|
||||
* The base url for api requests
|
||||
* @var string $base_url
|
||||
*/
|
||||
protected $baseUrl = Anilist::BASE_URL;
|
||||
|
||||
/**
|
||||
* HTTP headers to send with every request
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $defaultHeaders = [
|
||||
'Accept' => 'application/json',
|
||||
'Accept-Encoding' => 'gzip',
|
||||
'Content-type' => 'application/json',
|
||||
'User-Agent' => "Tim's Anime Client/4.0"
|
||||
];
|
||||
|
||||
/**
|
||||
* Set the request builder object
|
||||
*
|
||||
* @param MALRequestBuilder $requestBuilder
|
||||
* @return self
|
||||
*/
|
||||
public function setRequestBuilder($requestBuilder): self
|
||||
{
|
||||
$this->requestBuilder = $requestBuilder;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a request object
|
||||
*
|
||||
* @param string $type
|
||||
* @param string $url
|
||||
* @param array $options
|
||||
* @return \Amp\Artax\Response
|
||||
*/
|
||||
public function setUpRequest(string $type, string $url, array $options = [])
|
||||
{
|
||||
$config = $this->container->get('config');
|
||||
|
||||
$request = $this->requestBuilder
|
||||
->newRequest($type, $url)
|
||||
->setBasicAuth($config->get(['mal','username']), $config->get(['mal','password']));
|
||||
|
||||
if (array_key_exists('query', $options))
|
||||
{
|
||||
$request = $request->setQuery($options['query']);
|
||||
}
|
||||
|
||||
if (array_key_exists('body', $options))
|
||||
{
|
||||
$request = $request->setBody($options['body']);
|
||||
}
|
||||
|
||||
return $request->getFullRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a request
|
||||
*
|
||||
* @param string $type
|
||||
* @param string $url
|
||||
* @param array $options
|
||||
* @return \Amp\Artax\Response
|
||||
*/
|
||||
private function getResponse(string $type, string $url, array $options = [])
|
||||
{
|
||||
$logger = NULL;
|
||||
if ($this->getContainer())
|
||||
{
|
||||
$logger = $this->container->getLogger('mal-request');
|
||||
}
|
||||
|
||||
$request = $this->setUpRequest($type, $url, $options);
|
||||
$response = wait((new HummingbirdClient)->request($request));
|
||||
|
||||
$logger->debug('MAL api response', [
|
||||
'status' => $response->getStatus(),
|
||||
'reason' => $response->getReason(),
|
||||
'body' => $response->getBody(),
|
||||
'headers' => $response->getHeaders(),
|
||||
'requestHeaders' => $request->getHeaders(),
|
||||
]);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a request
|
||||
*
|
||||
* @param string $type
|
||||
* @param string $url
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
private function request(string $type, string $url, array $options = []): array
|
||||
{
|
||||
$logger = NULL;
|
||||
if ($this->getContainer())
|
||||
{
|
||||
$logger = $this->container->getLogger('anilist-request');
|
||||
}
|
||||
|
||||
$response = $this->getResponse($type, $url, $options);
|
||||
|
||||
if ((int) $response->getStatus() > 299 OR (int) $response->getStatus() < 200)
|
||||
{
|
||||
if ($logger)
|
||||
{
|
||||
$logger->warning('Non 200 response for api call', (array)$response->getBody());
|
||||
}
|
||||
}
|
||||
|
||||
return XML::toArray(wait($response->getBody()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove some boilerplate for post requests
|
||||
*
|
||||
* @param mixed ...$args
|
||||
* @return array
|
||||
*/
|
||||
protected function postRequest(...$args): array
|
||||
{
|
||||
$logger = NULL;
|
||||
if ($this->getContainer())
|
||||
{
|
||||
$logger = $this->container->getLogger('anilist-request');
|
||||
}
|
||||
|
||||
$response = $this->getResponse('POST', ...$args);
|
||||
$validResponseCodes = [200, 201];
|
||||
|
||||
if ( ! \in_array((int) $response->getStatus(), $validResponseCodes, TRUE))
|
||||
{
|
||||
if ($logger)
|
||||
{
|
||||
$logger->warning('Non 201 response for POST api call', (array)$response->getBody());
|
||||
}
|
||||
}
|
||||
|
||||
return XML::toArray($response->getBody());
|
||||
}
|
||||
}
|
109
src/API/Anilist/ListItem.php
Normal file
109
src/API/Anilist/ListItem.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 7
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\API\Anilist;
|
||||
|
||||
use Amp\Artax\{FormBody, Request};
|
||||
use Aviat\AnimeClient\API\{
|
||||
XML
|
||||
};
|
||||
use Aviat\AnimeClient\Types\AbstractType;
|
||||
use Aviat\Ion\Di\ContainerAware;
|
||||
|
||||
/**
|
||||
* CRUD operations for MAL list items
|
||||
*/
|
||||
final class ListItem {
|
||||
use ContainerAware;
|
||||
use AnilistTrait;
|
||||
|
||||
/**
|
||||
* Create a list item
|
||||
*
|
||||
* @param array $data
|
||||
* @param string $type
|
||||
* @return Request
|
||||
*/
|
||||
public function create(array $data, string $type = 'anime'): Request
|
||||
{
|
||||
$id = $data['id'];
|
||||
$createData = [
|
||||
'id' => $id,
|
||||
'data' => XML::toXML([
|
||||
'entry' => $data['data']
|
||||
])
|
||||
];
|
||||
|
||||
$config = $this->container->get('config');
|
||||
|
||||
return $this->requestBuilder->newRequest('POST', "{$type}list/add/{$id}.xml")
|
||||
->setFormFields($createData)
|
||||
->setBasicAuth($config->get(['mal','username']), $config->get(['mal', 'password']))
|
||||
->getFullRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a list item
|
||||
*
|
||||
* @param string $id
|
||||
* @param string $type
|
||||
* @return Request
|
||||
*/
|
||||
public function delete(string $id, string $type = 'anime'): Request
|
||||
{
|
||||
$config = $this->container->get('config');
|
||||
|
||||
return $this->requestBuilder->newRequest('DELETE', "{$type}list/delete/{$id}.xml")
|
||||
->setFormFields([
|
||||
'id' => $id
|
||||
])
|
||||
->setBasicAuth($config->get(['mal','username']), $config->get(['mal', 'password']))
|
||||
->getFullRequest();
|
||||
|
||||
// return $response->getBody() === 'Deleted'
|
||||
}
|
||||
|
||||
public function get(string $id): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a list item
|
||||
*
|
||||
* @param string $id
|
||||
* @param AbstractType $data
|
||||
* @param string $type
|
||||
* @return Request
|
||||
*/
|
||||
public function update(string $id, AbstractType $data, string $type = 'anime'): Request
|
||||
{
|
||||
$config = $this->container->get('config');
|
||||
|
||||
$xml = XML::toXML(['entry' => $data]);
|
||||
$body = new FormBody();
|
||||
$body->addField('id', $id);
|
||||
$body->addField('data', $xml);
|
||||
|
||||
return $this->requestBuilder->newRequest('POST', "{$type}list/update/{$id}.xml")
|
||||
->setFormFields([
|
||||
'id' => $id,
|
||||
'data' => $xml
|
||||
])
|
||||
->setBasicAuth($config->get(['mal','username']), $config->get(['mal', 'password']))
|
||||
->getFullRequest();
|
||||
}
|
||||
}
|
23
src/API/Anilist/Model.php
Normal file
23
src/API/Anilist/Model.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 7
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\API\Anilist;
|
||||
|
||||
/**
|
||||
* Anilist API Model
|
||||
*/
|
||||
final class Model {
|
||||
}
|
31
src/API/Enum/AnimeWatchingStatus/Anilist.php
Normal file
31
src/API/Enum/AnimeWatchingStatus/Anilist.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 7
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\API\Enum\AnimeWatchingStatus;
|
||||
|
||||
use Aviat\Ion\Enum;
|
||||
|
||||
/**
|
||||
* Possible values for watching status for the current anime
|
||||
*/
|
||||
final class Anilist extends Enum {
|
||||
const WATCHING = 'CURRENT';
|
||||
const COMPLETED = 'COMPLETED';
|
||||
const ON_HOLD = 'PAUSED';
|
||||
const DROPPED = 'DROPPED';
|
||||
const PLAN_TO_WATCH = 'PLANNING';
|
||||
const REPEATING = 'REPEATING';
|
||||
}
|
@ -21,7 +21,7 @@ use Aviat\Ion\Enum;
|
||||
/**
|
||||
* Possible values for watching status for the current anime
|
||||
*/
|
||||
class Kitsu extends Enum {
|
||||
final class Kitsu extends Enum {
|
||||
const WATCHING = 'current';
|
||||
const PLAN_TO_WATCH = 'planned';
|
||||
const ON_HOLD = 'on_hold';
|
||||
|
@ -21,7 +21,7 @@ use Aviat\Ion\Enum;
|
||||
/**
|
||||
* Possible values for watching status for the current anime
|
||||
*/
|
||||
class MAL extends Enum {
|
||||
final class MAL extends Enum {
|
||||
const WATCHING = 1;
|
||||
const COMPLETED = 2;
|
||||
const ON_HOLD = 3;
|
||||
|
@ -21,7 +21,7 @@ use Aviat\Ion\Enum as Enum;
|
||||
/**
|
||||
* Possible values for current watching status of anime
|
||||
*/
|
||||
class Route extends Enum {
|
||||
final class Route extends Enum {
|
||||
const ALL = 'all';
|
||||
const WATCHING = 'watching';
|
||||
const PLAN_TO_WATCH = 'plan_to_watch';
|
||||
|
@ -21,7 +21,7 @@ use Aviat\Ion\Enum as Enum;
|
||||
/**
|
||||
* Possible values for current watching status of anime
|
||||
*/
|
||||
class Title extends Enum {
|
||||
final class Title extends Enum {
|
||||
const ALL = 'All';
|
||||
const WATCHING = 'Currently Watching';
|
||||
const PLAN_TO_WATCH = 'Plan to Watch';
|
||||
|
31
src/API/Enum/MangaReadingStatus/Anilist.php
Normal file
31
src/API/Enum/MangaReadingStatus/Anilist.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 7
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\API\Enum\MangaReadingStatus;
|
||||
|
||||
use Aviat\Ion\Enum;
|
||||
|
||||
/**
|
||||
* Possible values for watching status for the current anime
|
||||
*/
|
||||
final class Anilist extends Enum {
|
||||
const WATCHING = 'CURRENT';
|
||||
const COMPLETED = 'COMPLETED';
|
||||
const ON_HOLD = 'PAUSED';
|
||||
const DROPPED = 'DROPPED';
|
||||
const PLAN_TO_WATCH = 'PLANNING';
|
||||
const REPEATING = 'REPEATING';
|
||||
}
|
@ -21,7 +21,7 @@ use Aviat\Ion\Enum;
|
||||
/**
|
||||
* Possible values for current reading status of manga
|
||||
*/
|
||||
class Kitsu extends Enum {
|
||||
final class Kitsu extends Enum {
|
||||
const READING = 'current';
|
||||
const PLAN_TO_READ = 'planned';
|
||||
const DROPPED = 'dropped';
|
||||
|
@ -21,7 +21,7 @@ use Aviat\Ion\Enum;
|
||||
/**
|
||||
* Possible values for watching status for the current anime
|
||||
*/
|
||||
class MAL extends Enum {
|
||||
final class MAL extends Enum {
|
||||
const READING = 'reading';
|
||||
const COMPLETED = 'completed';
|
||||
const ON_HOLD = 'onhold';
|
||||
|
@ -16,12 +16,12 @@
|
||||
|
||||
namespace Aviat\AnimeClient\API\Enum\MangaReadingStatus;
|
||||
|
||||
use Aviat\Ion\Enum as Enum;
|
||||
use Aviat\Ion\Enum;
|
||||
|
||||
/**
|
||||
* Possible values for current reading status of manga
|
||||
*/
|
||||
class Route extends Enum {
|
||||
final class Route extends Enum {
|
||||
const ALL = 'all';
|
||||
const READING = 'reading';
|
||||
const PLAN_TO_READ = 'plan_to_read';
|
||||
|
@ -16,12 +16,12 @@
|
||||
|
||||
namespace Aviat\AnimeClient\API\Enum\MangaReadingStatus;
|
||||
|
||||
use Aviat\Ion\Enum as Enum;
|
||||
use Aviat\Ion\Enum;
|
||||
|
||||
/**
|
||||
* Possible values for current reading status of manga
|
||||
*/
|
||||
class Title extends Enum {
|
||||
final class Title extends Enum {
|
||||
const ALL = 'All';
|
||||
const READING = 'Currently Reading';
|
||||
const PLAN_TO_READ = 'Plan to Read';
|
||||
|
@ -19,7 +19,7 @@ namespace Aviat\AnimeClient\API;
|
||||
/**
|
||||
* Class encapsulating Json API data structure for a request or response
|
||||
*/
|
||||
class JsonAPI {
|
||||
final class JsonAPI {
|
||||
|
||||
/**
|
||||
* The full data array
|
||||
|
@ -22,7 +22,7 @@ use DateTimeImmutable;
|
||||
/**
|
||||
* Data massaging helpers for the Kitsu API
|
||||
*/
|
||||
class Kitsu {
|
||||
final class Kitsu {
|
||||
const AUTH_URL = 'https://kitsu.io/api/oauth/token';
|
||||
const AUTH_USER_ID_KEY = 'kitsu-auth-userid';
|
||||
const AUTH_TOKEN_CACHE_KEY = 'kitsu-auth-token';
|
||||
@ -143,7 +143,10 @@ class Kitsu {
|
||||
*/
|
||||
public static function parseStreamingLinks(array $included): array
|
||||
{
|
||||
if ( ! array_key_exists('streamingLinks', $included))
|
||||
if (
|
||||
( ! array_key_exists('streamingLinks', $included)) ||
|
||||
count($included['streamingLinks']) === 0
|
||||
)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
@ -152,7 +155,16 @@ class Kitsu {
|
||||
|
||||
foreach ($included['streamingLinks'] as $streamingLink)
|
||||
{
|
||||
$host = parse_url($streamingLink['url'], \PHP_URL_HOST);
|
||||
$url = $streamingLink['url'];
|
||||
|
||||
// 'Fix' links that start with the hostname,
|
||||
// rather than a protocol
|
||||
if (strpos($url, '//') === FALSE)
|
||||
{
|
||||
$url = '//' . $url;
|
||||
}
|
||||
|
||||
$host = parse_url($url, \PHP_URL_HOST);
|
||||
|
||||
$links[] = [
|
||||
'meta' => static::getServiceMetaData($host),
|
||||
@ -182,17 +194,7 @@ class Kitsu {
|
||||
|
||||
if (count($anime['relationships']['streamingLinks']) > 0)
|
||||
{
|
||||
foreach ($anime['relationships']['streamingLinks'] as $streamingLink)
|
||||
{
|
||||
$host = parse_url($streamingLink['url'], \PHP_URL_HOST);
|
||||
|
||||
$links[] = [
|
||||
'meta' => static::getServiceMetaData($host),
|
||||
'link' => $streamingLink['url'],
|
||||
'subs' => $streamingLink['subs'],
|
||||
'dubs' => $streamingLink['dubs']
|
||||
];
|
||||
}
|
||||
return static::parseStreamingLinks($anime['relationships']);
|
||||
}
|
||||
|
||||
return $links;
|
||||
@ -243,10 +245,9 @@ class Kitsu {
|
||||
foreach($existingTitles as $existing)
|
||||
{
|
||||
$isSubset = mb_substr_count($existing, $title) > 0;
|
||||
$diff = levenshtein($existing, $title);
|
||||
$onlydifferentCase = (mb_strtolower($existing) === mb_strtolower($title));
|
||||
$diff = levenshtein(mb_strtolower($existing), mb_strtolower($title));
|
||||
|
||||
if ($diff <= 3 OR $isSubset OR $onlydifferentCase OR mb_strlen($title) > 55 OR mb_strlen($existing) > 60)
|
||||
if ($diff <= 4 || $isSubset || mb_strlen($title) > 45 || mb_strlen($existing) > 50)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ use Exception;
|
||||
/**
|
||||
* Kitsu API Authentication
|
||||
*/
|
||||
class Auth {
|
||||
final class Auth {
|
||||
use CacheTrait;
|
||||
use ContainerAware;
|
||||
|
||||
@ -37,14 +37,14 @@ class Auth {
|
||||
*
|
||||
* @var Model
|
||||
*/
|
||||
protected $model;
|
||||
private $model;
|
||||
|
||||
/**
|
||||
* Session object
|
||||
*
|
||||
* @var \Aura\Session\Segment
|
||||
*/
|
||||
protected $segment;
|
||||
private $segment;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -21,7 +21,7 @@ use Aviat\Ion\Enum as BaseEnum;
|
||||
/**
|
||||
* Status of when anime is being/was/will be aired
|
||||
*/
|
||||
class AnimeAiringStatus extends BaseEnum {
|
||||
final class AnimeAiringStatus extends BaseEnum {
|
||||
const NOT_YET_AIRED = 'Not Yet Aired';
|
||||
const AIRING = 'Currently Airing';
|
||||
const FINISHED_AIRING = 'Finished Airing';
|
||||
|
@ -18,7 +18,7 @@ namespace Aviat\AnimeClient\API\Kitsu;
|
||||
|
||||
use Aviat\AnimeClient\API\APIRequestBuilder;
|
||||
|
||||
class KitsuRequestBuilder extends APIRequestBuilder {
|
||||
final class KitsuRequestBuilder extends APIRequestBuilder {
|
||||
|
||||
/**
|
||||
* The base url for api requests
|
||||
|
@ -25,39 +25,17 @@ use Aviat\AnimeClient\API\{
|
||||
HummingbirdClient,
|
||||
ListItemInterface
|
||||
};
|
||||
use Aviat\AnimeClient\Types\FormItemData;
|
||||
use Aviat\Ion\Di\ContainerAware;
|
||||
use Aviat\Ion\Json;
|
||||
|
||||
/**
|
||||
* CRUD operations for Kitsu list items
|
||||
*/
|
||||
class ListItem implements ListItemInterface {
|
||||
final class ListItem implements ListItemInterface {
|
||||
use ContainerAware;
|
||||
use KitsuTrait;
|
||||
|
||||
private function getAuthHeader()
|
||||
{
|
||||
$cache = $this->getContainer()->get('cache');
|
||||
$cacheItem = $cache->getItem('kitsu-auth-token');
|
||||
$sessionSegment = $this->getContainer()
|
||||
->get('session')
|
||||
->getSegment(SESSION_SEGMENT);
|
||||
|
||||
if ($sessionSegment->get('auth_token') !== NULL)
|
||||
{
|
||||
$token = $sessionSegment->get('auth_token');
|
||||
return "bearer {$token}";
|
||||
}
|
||||
|
||||
if ($cacheItem->isHit())
|
||||
{
|
||||
$token = $cacheItem->get();
|
||||
return "bearer {$token}";
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
public function create(array $data): Request
|
||||
{
|
||||
$body = [
|
||||
@ -134,7 +112,7 @@ class ListItem implements ListItemInterface {
|
||||
return Json::decode(wait($response->getBody()));
|
||||
}
|
||||
|
||||
public function update(string $id, array $data): Request
|
||||
public function update(string $id, FormItemData $data): Request
|
||||
{
|
||||
$authHeader = $this->getAuthHeader();
|
||||
$requestData = [
|
||||
@ -155,4 +133,25 @@ class ListItem implements ListItemInterface {
|
||||
|
||||
return $request->getFullRequest();
|
||||
}
|
||||
|
||||
private function getAuthHeader()
|
||||
{
|
||||
$cache = $this->getContainer()->get('cache');
|
||||
$cacheItem = $cache->getItem('kitsu-auth-token');
|
||||
$sessionSegment = $this->getContainer()
|
||||
->get('session')
|
||||
->getSegment(SESSION_SEGMENT);
|
||||
|
||||
if ($sessionSegment->get('auth_token') !== NULL) {
|
||||
$token = $sessionSegment->get('auth_token');
|
||||
return "bearer {$token}";
|
||||
}
|
||||
|
||||
if ($cacheItem->isHit()) {
|
||||
$token = $cacheItem->get();
|
||||
return "bearer {$token}";
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
@ -26,7 +26,6 @@ use Aviat\AnimeClient\API\{
|
||||
ParallelAPIRequest
|
||||
};
|
||||
use Aviat\AnimeClient\API\Enum\{
|
||||
AnimeWatchingStatus\Title,
|
||||
AnimeWatchingStatus\Kitsu as KitsuWatchingStatus,
|
||||
MangaReadingStatus\Kitsu as KitsuReadingStatus
|
||||
};
|
||||
@ -37,17 +36,25 @@ use Aviat\AnimeClient\API\Kitsu\Transformer\{
|
||||
MangaTransformer,
|
||||
MangaListTransformer
|
||||
};
|
||||
use Aviat\AnimeClient\Types\{
|
||||
AbstractType,
|
||||
Anime,
|
||||
FormItem,
|
||||
FormItemData,
|
||||
AnimeListItem,
|
||||
MangaPage
|
||||
};
|
||||
use Aviat\Ion\{Di\ContainerAware, Json};
|
||||
|
||||
/**
|
||||
* Kitsu API Model
|
||||
*/
|
||||
class Model {
|
||||
final class Model {
|
||||
use CacheTrait;
|
||||
use ContainerAware;
|
||||
use KitsuTrait;
|
||||
|
||||
const LIST_PAGE_SIZE = 100;
|
||||
private const LIST_PAGE_SIZE = 100;
|
||||
|
||||
/**
|
||||
* Class to map anime list items
|
||||
@ -56,27 +63,27 @@ class Model {
|
||||
*
|
||||
* @var AnimeListTransformer
|
||||
*/
|
||||
protected $animeListTransformer;
|
||||
private $animeListTransformer;
|
||||
|
||||
/**
|
||||
* @var AnimeTransformer
|
||||
*/
|
||||
protected $animeTransformer;
|
||||
private $animeTransformer;
|
||||
|
||||
/**
|
||||
* @var ListItem
|
||||
*/
|
||||
protected $listItem;
|
||||
private $listItem;
|
||||
|
||||
/**
|
||||
* @var MangaTransformer
|
||||
*/
|
||||
protected $mangaTransformer;
|
||||
private $mangaTransformer;
|
||||
|
||||
/**
|
||||
* @var MangaListTransformer
|
||||
*/
|
||||
protected $mangaListTransformer;
|
||||
private $mangaListTransformer;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -314,15 +321,15 @@ class Model {
|
||||
* Get information about a particular anime
|
||||
*
|
||||
* @param string $slug
|
||||
* @return array
|
||||
* @return Anime
|
||||
*/
|
||||
public function getAnime(string $slug): array
|
||||
public function getAnime(string $slug): Anime
|
||||
{
|
||||
$baseData = $this->getRawMediaData('anime', $slug);
|
||||
|
||||
if (empty($baseData))
|
||||
{
|
||||
return [];
|
||||
return new Anime();
|
||||
}
|
||||
|
||||
$transformed = $this->animeTransformer->transform($baseData);
|
||||
@ -417,7 +424,7 @@ class Model {
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function getFullAnimeList(array $options = [
|
||||
public function getFullRawAnimeList(array $options = [
|
||||
'include' => 'anime.mappings'
|
||||
]): array
|
||||
{
|
||||
@ -441,10 +448,10 @@ class Model {
|
||||
foreach($responses as $response)
|
||||
{
|
||||
$data = Json::decode($response);
|
||||
$output = array_merge_recursive($output, $data);
|
||||
$output[] = $data;
|
||||
}
|
||||
|
||||
return $output;
|
||||
return array_merge_recursive(...$output);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -547,7 +554,7 @@ class Model {
|
||||
'sort' => '-updated_at'
|
||||
];
|
||||
|
||||
return $this->getFullAnimeList($options);
|
||||
return $this->getFullRawAnimeList($options);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -558,15 +565,15 @@ class Model {
|
||||
* Get information about a particular manga
|
||||
*
|
||||
* @param string $slug
|
||||
* @return array
|
||||
* @return MangaPage
|
||||
*/
|
||||
public function getManga(string $slug): array
|
||||
public function getManga(string $slug): MangaPage
|
||||
{
|
||||
$baseData = $this->getRawMediaData('manga', $slug);
|
||||
|
||||
if (empty($baseData))
|
||||
{
|
||||
return [];
|
||||
return new MangaPage([]);
|
||||
}
|
||||
|
||||
$transformed = $this->mangaTransformer->transform($baseData);
|
||||
@ -580,7 +587,7 @@ class Model {
|
||||
* @param string $mangaId
|
||||
* @return array
|
||||
*/
|
||||
public function getMangaById(string $mangaId): array
|
||||
public function getMangaById(string $mangaId): MangaPage
|
||||
{
|
||||
$baseData = $this->getRawMediaDataById('manga', $mangaId);
|
||||
return $this->mangaTransformer->transform($baseData);
|
||||
@ -680,7 +687,7 @@ class Model {
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function getFullMangaList(array $options = [
|
||||
public function getFullRawMangaList(array $options = [
|
||||
'include' => 'manga.mappings'
|
||||
]): array
|
||||
{
|
||||
@ -704,10 +711,10 @@ class Model {
|
||||
foreach($responses as $response)
|
||||
{
|
||||
$data = Json::decode($response);
|
||||
$output = array_merge_recursive($output, $data);
|
||||
$output[] = $data;
|
||||
}
|
||||
|
||||
return $output;
|
||||
return array_merge_recursive(...$output);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -804,9 +811,9 @@ class Model {
|
||||
* Get the data for a specific list item, generally for editing
|
||||
*
|
||||
* @param string $listId - The unique identifier of that list item
|
||||
* @return array
|
||||
* @return mixed
|
||||
*/
|
||||
public function getListItem(string $listId): array
|
||||
public function getListItem(string $listId)
|
||||
{
|
||||
$baseData = $this->listItem->get($listId);
|
||||
$included = JsonAPI::organizeIncludes($baseData['included']);
|
||||
@ -814,12 +821,12 @@ class Model {
|
||||
|
||||
switch (TRUE)
|
||||
{
|
||||
case in_array('anime', array_keys($included)):
|
||||
case array_key_exists('anime', $included): // in_array('anime', array_keys($included)):
|
||||
$included = JsonAPI::inlineIncludedRelationships($included, 'anime');
|
||||
$baseData['data']['included'] = $included;
|
||||
return $this->animeListTransformer->transform($baseData['data']);
|
||||
|
||||
case in_array('manga', array_keys($included)):
|
||||
case array_key_exists('manga', $included): // in_array('manga', array_keys($included)):
|
||||
$included = JsonAPI::inlineIncludedRelationships($included, 'manga');
|
||||
$baseData['data']['included'] = $included;
|
||||
$baseData['data']['manga'] = $baseData['included'][0];
|
||||
@ -833,10 +840,10 @@ class Model {
|
||||
/**
|
||||
* Modify a list item
|
||||
*
|
||||
* @param array $data
|
||||
* @param FormItem $data
|
||||
* @return Request
|
||||
*/
|
||||
public function updateListItem(array $data): Request
|
||||
public function updateListItem(FormItem $data): Request
|
||||
{
|
||||
return $this->listItem->update($data['id'], $data['data']);
|
||||
}
|
||||
|
@ -17,21 +17,27 @@
|
||||
namespace Aviat\AnimeClient\API\Kitsu\Transformer;
|
||||
|
||||
use Aviat\AnimeClient\API\Kitsu;
|
||||
use Aviat\AnimeClient\Types\{
|
||||
Anime,
|
||||
AnimeFormItem,
|
||||
AnimeFormItemData,
|
||||
AnimeListItem
|
||||
};
|
||||
use Aviat\Ion\Transformer\AbstractTransformer;
|
||||
|
||||
/**
|
||||
* Transformer for anime list
|
||||
*/
|
||||
class AnimeListTransformer extends AbstractTransformer {
|
||||
final class AnimeListTransformer extends AbstractTransformer {
|
||||
|
||||
/**
|
||||
* Convert raw api response to a more
|
||||
* logical and workable structure
|
||||
*
|
||||
* @param array $item API library item
|
||||
* @return array
|
||||
* @return AnimeListItem
|
||||
*/
|
||||
public function transform($item): array
|
||||
public function transform($item): AnimeListItem
|
||||
{
|
||||
$included = $item['included'];
|
||||
$animeId = $item['relationships']['media']['data']['id'];
|
||||
@ -66,7 +72,10 @@ class AnimeListTransformer extends AbstractTransformer {
|
||||
? Kitsu::parseListItemStreamingLinks($included, $animeId)
|
||||
: [];
|
||||
|
||||
return [
|
||||
$titles = Kitsu::filterTitles($anime);
|
||||
$title = array_shift($titles);
|
||||
|
||||
return new AnimeListItem([
|
||||
'id' => $item['id'],
|
||||
'mal_id' => $MALid,
|
||||
'episodes' => [
|
||||
@ -81,24 +90,24 @@ class AnimeListTransformer extends AbstractTransformer {
|
||||
'started' => $anime['startDate'],
|
||||
'ended' => $anime['endDate']
|
||||
],
|
||||
'anime' => [
|
||||
'anime' => new Anime([
|
||||
'id' => $animeId,
|
||||
'age_rating' => $anime['ageRating'],
|
||||
'title' => $anime['canonicalTitle'],
|
||||
'titles' => Kitsu::filterTitles($anime),
|
||||
'title' => $title,
|
||||
'titles' => $titles,
|
||||
'slug' => $anime['slug'],
|
||||
'type' => $this->string($anime['showType'])->upperCaseFirst()->__toString(),
|
||||
'image' => $anime['posterImage']['small'],
|
||||
'show_type' => $this->string($anime['showType'])->upperCaseFirst()->__toString(),
|
||||
'cover_image' => $anime['posterImage']['small'],
|
||||
'genres' => $genres,
|
||||
'streaming_links' => $streamingLinks,
|
||||
],
|
||||
]),
|
||||
'watching_status' => $item['attributes']['status'],
|
||||
'notes' => $item['attributes']['notes'],
|
||||
'rewatching' => (bool) $item['attributes']['reconsuming'],
|
||||
'rewatched' => (int) $item['attributes']['reconsumeCount'],
|
||||
'user_rating' => $rating,
|
||||
'private' => $item['attributes']['private'] ?? FALSE,
|
||||
];
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,14 +115,14 @@ class AnimeListTransformer extends AbstractTransformer {
|
||||
* api response format
|
||||
*
|
||||
* @param array $item Transformed library item
|
||||
* @return array API library item
|
||||
* @return AnimeFormItem API library item
|
||||
*/
|
||||
public function untransform($item): array
|
||||
public function untransform($item): AnimeFormItem
|
||||
{
|
||||
$privacy = (array_key_exists('private', $item) && $item['private']);
|
||||
$rewatching = (array_key_exists('rewatching', $item) && $item['rewatching']);
|
||||
|
||||
$untransformed = [
|
||||
$untransformed = new AnimeFormItem([
|
||||
'id' => $item['id'],
|
||||
'mal_id' => $item['mal_id'] ?? NULL,
|
||||
'data' => [
|
||||
@ -123,7 +132,7 @@ class AnimeListTransformer extends AbstractTransformer {
|
||||
'notes' => $item['notes'],
|
||||
'private' => $privacy
|
||||
]
|
||||
];
|
||||
]);
|
||||
|
||||
if (is_numeric($item['episodes_watched']) && $item['episodes_watched'] > 0)
|
||||
{
|
||||
|
@ -17,31 +17,32 @@
|
||||
namespace Aviat\AnimeClient\API\Kitsu\Transformer;
|
||||
|
||||
use Aviat\AnimeClient\API\{JsonAPI, Kitsu};
|
||||
use Aviat\AnimeClient\Types\Anime;
|
||||
use Aviat\Ion\Transformer\AbstractTransformer;
|
||||
|
||||
/**
|
||||
* Transformer for anime description page
|
||||
*/
|
||||
class AnimeTransformer extends AbstractTransformer {
|
||||
final class AnimeTransformer extends AbstractTransformer {
|
||||
|
||||
/**
|
||||
* Convert raw api response to a more
|
||||
* logical and workable structure
|
||||
*
|
||||
* @param array $item API library item
|
||||
* @return array
|
||||
* @return Anime
|
||||
*/
|
||||
public function transform($item): array
|
||||
public function transform($item): Anime
|
||||
{
|
||||
|
||||
$item['included'] = JsonAPI::organizeIncludes($item['included']);
|
||||
$genres = $item['included']['categories'] ?? [];
|
||||
$item['genres'] = array_column($genres, 'title') ?? [];
|
||||
sort($item['genres']);
|
||||
|
||||
$titles = Kitsu::filterTitles($item);
|
||||
$title = $item['canonicalTitle'];
|
||||
$titles = array_diff($item['titles'], [$title]);
|
||||
|
||||
return [
|
||||
return new Anime([
|
||||
'age_rating' => $item['ageRating'],
|
||||
'age_rating_guide' => $item['ageRatingGuide'],
|
||||
'cover_image' => $item['posterImage']['small'],
|
||||
@ -54,10 +55,10 @@ class AnimeTransformer extends AbstractTransformer {
|
||||
'status' => Kitsu::getAiringStatus($item['startDate'], $item['endDate']),
|
||||
'streaming_links' => Kitsu::parseStreamingLinks($item['included']),
|
||||
'synopsis' => $item['synopsis'],
|
||||
'title' => $titles[0],
|
||||
'title' => $title,
|
||||
'titles' => $titles,
|
||||
'trailer_id' => $item['youtubeVideoId'],
|
||||
'url' => "https://kitsu.io/anime/{$item['slug']}",
|
||||
];
|
||||
]);
|
||||
}
|
||||
}
|
@ -17,13 +17,17 @@
|
||||
namespace Aviat\AnimeClient\API\Kitsu\Transformer;
|
||||
|
||||
use Aviat\AnimeClient\API\Kitsu;
|
||||
use Aviat\AnimeClient\Types\{
|
||||
MangaFormItem, MangaFormItemData,
|
||||
MangaListItem, MangaListItemDetail
|
||||
};
|
||||
use Aviat\Ion\StringWrapper;
|
||||
use Aviat\Ion\Transformer\AbstractTransformer;
|
||||
|
||||
/**
|
||||
* Data transformation class for zippered Hummingbird manga
|
||||
*/
|
||||
class MangaListTransformer extends AbstractTransformer {
|
||||
final class MangaListTransformer extends AbstractTransformer {
|
||||
|
||||
use StringWrapper;
|
||||
|
||||
@ -31,9 +35,9 @@ class MangaListTransformer extends AbstractTransformer {
|
||||
* Remap zipped anime data to a more logical form
|
||||
*
|
||||
* @param array $item manga entry item
|
||||
* @return array
|
||||
* @return MangaListItem
|
||||
*/
|
||||
public function transform($item): array
|
||||
public function transform($item): MangaListItem
|
||||
{
|
||||
$included = $item['included'];
|
||||
$mangaId = $item['relationships']['media']['data']['id'];
|
||||
@ -72,7 +76,10 @@ class MangaListTransformer extends AbstractTransformer {
|
||||
}
|
||||
}
|
||||
|
||||
$map = [
|
||||
$titles = Kitsu::filterTitles($manga);
|
||||
$title = array_shift($titles);
|
||||
|
||||
$map = new MangaListItem([
|
||||
'id' => $item['id'],
|
||||
'mal_id' => $MALid,
|
||||
'chapters' => [
|
||||
@ -83,22 +90,22 @@ class MangaListTransformer extends AbstractTransformer {
|
||||
'read' => '-', //$item['attributes']['volumes_read'],
|
||||
'total' => $totalVolumes
|
||||
],
|
||||
'manga' => [
|
||||
'id' => $mangaId,
|
||||
'titles' => Kitsu::filterTitles($manga),
|
||||
'alternate_title' => NULL,
|
||||
'slug' => $manga['slug'],
|
||||
'url' => 'https://kitsu.io/manga/' . $manga['slug'],
|
||||
'type' => $manga['mangaType'],
|
||||
'image' => $manga['posterImage']['small'],
|
||||
'manga' => new MangaListItemDetail([
|
||||
'genres' => $genres,
|
||||
],
|
||||
'id' => $mangaId,
|
||||
'image' => $manga['posterImage']['small'],
|
||||
'slug' => $manga['slug'],
|
||||
'title' => $title,
|
||||
'titles' => $titles,
|
||||
'type' => $manga['mangaType'],
|
||||
'url' => 'https://kitsu.io/manga/' . $manga['slug'],
|
||||
]),
|
||||
'reading_status' => $item['attributes']['status'],
|
||||
'notes' => $item['attributes']['notes'],
|
||||
'rereading' => (bool)$item['attributes']['reconsuming'],
|
||||
'reread' => $item['attributes']['reconsumeCount'],
|
||||
'user_rating' => $rating,
|
||||
];
|
||||
]);
|
||||
|
||||
return $map;
|
||||
}
|
||||
@ -107,22 +114,22 @@ class MangaListTransformer extends AbstractTransformer {
|
||||
* Untransform data to update the api
|
||||
*
|
||||
* @param array $item
|
||||
* @return array
|
||||
* @return MangaFormItem
|
||||
*/
|
||||
public function untransform($item): array
|
||||
public function untransform($item): MangaFormItem
|
||||
{
|
||||
$rereading = array_key_exists('rereading', $item) && (bool)$item['rereading'];
|
||||
|
||||
$map = [
|
||||
$map = new MangaFormItem([
|
||||
'id' => $item['id'],
|
||||
'mal_id' => $item['mal_id'],
|
||||
'data' => [
|
||||
'data' => new MangaFormItemData([
|
||||
'status' => $item['status'],
|
||||
'reconsuming' => $rereading,
|
||||
'reconsumeCount' => (int)$item['reread_count'],
|
||||
'notes' => $item['notes'],
|
||||
],
|
||||
];
|
||||
]),
|
||||
]);
|
||||
|
||||
if (is_numeric($item['chapters_read']) && $item['chapters_read'] > 0)
|
||||
{
|
||||
|
@ -16,35 +16,37 @@
|
||||
|
||||
namespace Aviat\AnimeClient\API\Kitsu\Transformer;
|
||||
|
||||
use Aviat\AnimeClient\Types\MangaPage;
|
||||
use Aviat\Ion\Transformer\AbstractTransformer;
|
||||
|
||||
/**
|
||||
* Transformer for anime description page
|
||||
*/
|
||||
class MangaTransformer extends AbstractTransformer {
|
||||
final class MangaTransformer extends AbstractTransformer {
|
||||
|
||||
/**
|
||||
* Convert raw api response to a more
|
||||
* logical and workable structure
|
||||
*
|
||||
* @param array $item API library item
|
||||
* @return array
|
||||
* @return MangaPage
|
||||
*/
|
||||
public function transform($item)
|
||||
public function transform($item): MangaPage
|
||||
{
|
||||
// \dump($item);
|
||||
$genres = [];
|
||||
|
||||
foreach($item['included'] as $included)
|
||||
{
|
||||
if ($included['type'] === 'genres')
|
||||
if ($included['type'] === 'categories')
|
||||
{
|
||||
$genres[] = $included['attributes']['name'];
|
||||
$genres[] = $included['attributes']['title'];
|
||||
}
|
||||
}
|
||||
|
||||
sort($genres);
|
||||
|
||||
return [
|
||||
return new MangaPage([
|
||||
'id' => $item['id'],
|
||||
'title' => $item['canonicalTitle'],
|
||||
'en_title' => $item['titles']['en'],
|
||||
@ -56,7 +58,7 @@ class MangaTransformer extends AbstractTransformer {
|
||||
'synopsis' => $item['synopsis'],
|
||||
'url' => "https://kitsu.io/manga/{$item['slug']}",
|
||||
'genres' => $genres,
|
||||
];
|
||||
]);
|
||||
}
|
||||
|
||||
private function count(int $value = NULL)
|
||||
|
@ -17,6 +17,7 @@
|
||||
namespace Aviat\AnimeClient\API;
|
||||
|
||||
use Amp\Artax\Request;
|
||||
use Aviat\AnimeClient\Types\FormItemData;
|
||||
|
||||
/**
|
||||
* Common interface for anime and manga list item CRUD
|
||||
@ -43,10 +44,10 @@ interface ListItemInterface {
|
||||
* Update a list item
|
||||
*
|
||||
* @param string $id - The id of the list item to update
|
||||
* @param array $data - The data with which to update the list item
|
||||
* @param FormItemData $data - The data with which to update the list item
|
||||
* @return Request
|
||||
*/
|
||||
public function update(string $id, array $data): Request;
|
||||
public function update(string $id, FormItemData $data): Request;
|
||||
|
||||
/**
|
||||
* Delete a list item
|
||||
|
@ -28,7 +28,7 @@ use Aviat\AnimeClient\API\Enum\{
|
||||
/**
|
||||
* Constants and mappings for the My Anime List API
|
||||
*/
|
||||
class MAL {
|
||||
final class MAL {
|
||||
const AUTH_URL = 'https://myanimelist.net/api/account/verify_credentials.xml';
|
||||
const BASE_URL = 'https://myanimelist.net/api/';
|
||||
|
||||
|
@ -20,12 +20,13 @@ use Amp\Artax\{FormBody, Request};
|
||||
use Aviat\AnimeClient\API\{
|
||||
XML
|
||||
};
|
||||
use Aviat\AnimeClient\Types\AbstractType;
|
||||
use Aviat\Ion\Di\ContainerAware;
|
||||
|
||||
/**
|
||||
* CRUD operations for MAL list items
|
||||
*/
|
||||
class ListItem {
|
||||
final class ListItem {
|
||||
use ContainerAware;
|
||||
use MALTrait;
|
||||
|
||||
@ -84,11 +85,11 @@ class ListItem {
|
||||
* Update a list item
|
||||
*
|
||||
* @param string $id
|
||||
* @param array $data
|
||||
* @param AbstractType $data
|
||||
* @param string $type
|
||||
* @return Request
|
||||
*/
|
||||
public function update(string $id, array $data, string $type = 'anime'): Request
|
||||
public function update(string $id, AbstractType $data, string $type = 'anime'): Request
|
||||
{
|
||||
$config = $this->container->get('config');
|
||||
|
||||
|
@ -16,12 +16,14 @@
|
||||
|
||||
namespace Aviat\AnimeClient\API\MAL;
|
||||
|
||||
use const Aviat\AnimeClient\USER_AGENT;
|
||||
|
||||
use Aviat\AnimeClient\API\{
|
||||
APIRequestBuilder,
|
||||
MAL as M
|
||||
};
|
||||
|
||||
class MALRequestBuilder extends APIRequestBuilder {
|
||||
final class MALRequestBuilder extends APIRequestBuilder {
|
||||
|
||||
/**
|
||||
* The base url for api requests
|
||||
@ -38,7 +40,7 @@ class MALRequestBuilder extends APIRequestBuilder {
|
||||
'Accept' => 'text/xml',
|
||||
'Accept-Encoding' => 'gzip',
|
||||
'Content-type' => 'application/x-www-form-urlencoded',
|
||||
'User-Agent' => "Tim's Anime Client/4.0"
|
||||
'User-Agent' => USER_AGENT,
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -24,12 +24,13 @@ use Aviat\AnimeClient\API\MAL\{
|
||||
};
|
||||
use Aviat\AnimeClient\API\XML;
|
||||
use Aviat\AnimeClient\API\Mapping\{AnimeWatchingStatus, MangaReadingStatus};
|
||||
use Aviat\AnimeClient\Types\{Anime, FormItem};
|
||||
use Aviat\Ion\Di\ContainerAware;
|
||||
|
||||
/**
|
||||
* MyAnimeList API Model
|
||||
*/
|
||||
class Model {
|
||||
final class Model {
|
||||
use ContainerAware;
|
||||
use MALTrait;
|
||||
|
||||
@ -147,11 +148,11 @@ class Model {
|
||||
/**
|
||||
* Update a list item
|
||||
*
|
||||
* @param array $data
|
||||
* @param FormItem $data
|
||||
* @param string $type "anime" or "manga"
|
||||
* @return Request
|
||||
*/
|
||||
public function updateListItem(array $data, string $type = 'anime'): Request
|
||||
public function updateListItem(FormItem $data, string $type = 'anime'): Request
|
||||
{
|
||||
$updateData = [];
|
||||
|
||||
|
@ -17,12 +17,13 @@
|
||||
namespace Aviat\AnimeClient\API\MAL\Transformer;
|
||||
|
||||
use Aviat\AnimeClient\API\Mapping\AnimeWatchingStatus;
|
||||
use Aviat\AnimeClient\Types\{AnimeFormItem, AnimeFormItemData};
|
||||
use Aviat\Ion\Transformer\AbstractTransformer;
|
||||
|
||||
/**
|
||||
* Transformer for updating MAL List
|
||||
*/
|
||||
class AnimeListTransformer extends AbstractTransformer {
|
||||
final class AnimeListTransformer extends AbstractTransformer {
|
||||
/**
|
||||
* Identity transformation
|
||||
*
|
||||
@ -38,16 +39,14 @@ class AnimeListTransformer extends AbstractTransformer {
|
||||
* Transform Kitsu episode data to MAL episode data
|
||||
*
|
||||
* @param array $item
|
||||
* @return array
|
||||
* @return AnimeFormItem
|
||||
*/
|
||||
public function untransform(array $item): array
|
||||
public function untransform(array $item): AnimeFormItem
|
||||
{
|
||||
$map = [
|
||||
$map = new AnimeFormItem([
|
||||
'id' => $item['mal_id'],
|
||||
'data' => []
|
||||
];
|
||||
|
||||
$data =& $item['data'];
|
||||
'data' => new AnimeFormItemData([]),
|
||||
]);
|
||||
|
||||
foreach($item['data'] as $key => $value)
|
||||
{
|
||||
|
@ -22,7 +22,7 @@ use Aviat\Ion\Transformer\AbstractTransformer;
|
||||
/**
|
||||
* Transformer for updating MAL List
|
||||
*/
|
||||
class MangaListTransformer extends AbstractTransformer {
|
||||
final class MangaListTransformer extends AbstractTransformer {
|
||||
/**
|
||||
* Identity transformation
|
||||
*
|
||||
|
@ -16,14 +16,30 @@
|
||||
|
||||
namespace Aviat\AnimeClient\API\Mapping;
|
||||
|
||||
use Aviat\AnimeClient\API\Enum\AnimeWatchingStatus\{Kitsu, MAL, Route, Title};
|
||||
use Aviat\AnimeClient\API\Enum\AnimeWatchingStatus\{Anilist, Kitsu, MAL, Route, Title};
|
||||
use Aviat\Ion\Enum;
|
||||
|
||||
/**
|
||||
* Anime watching status mappings, among Kitsu, MAL, Page titles
|
||||
* and url route segments
|
||||
*/
|
||||
class AnimeWatchingStatus extends Enum {
|
||||
final class AnimeWatchingStatus extends Enum {
|
||||
const ANILIST_TO_KITSU = [
|
||||
Anilist::WATCHING => Kitsu::WATCHING,
|
||||
Anilist::PLAN_TO_WATCH => Kitsu::PLAN_TO_WATCH,
|
||||
Anilist::COMPLETED => Kitsu::COMPLETED,
|
||||
Anilist::ON_HOLD => Kitsu::ON_HOLD,
|
||||
Anilist::DROPPED => Kitsu::DROPPED
|
||||
];
|
||||
|
||||
const KITSU_TO_ANILIST = [
|
||||
Kitsu::WATCHING => Anilist::WATCHING,
|
||||
Kitsu::PLAN_TO_WATCH => Anilist::PLAN_TO_WATCH,
|
||||
Kitsu::COMPLETED => Anilist::COMPLETED,
|
||||
Kitsu::ON_HOLD => Anilist::ON_HOLD,
|
||||
Kitsu::DROPPED => Anilist::DROPPED
|
||||
];
|
||||
|
||||
const KITSU_TO_MAL = [
|
||||
Kitsu::WATCHING => MAL::WATCHING,
|
||||
Kitsu::PLAN_TO_WATCH => MAL::PLAN_TO_WATCH,
|
||||
|
@ -16,14 +16,31 @@
|
||||
|
||||
namespace Aviat\AnimeClient\API\Mapping;
|
||||
|
||||
use Aviat\AnimeClient\API\Enum\MangaReadingStatus\{Kitsu, MAL, Title, Route};
|
||||
use Aviat\AnimeClient\API\Enum\MangaReadingStatus\{Anilist, Kitsu, MAL, Title, Route};
|
||||
use Aviat\Ion\Enum;
|
||||
|
||||
/**
|
||||
* Manga reading status mappings, among Kitsu, MAL, Page titles
|
||||
* and url route segments
|
||||
*/
|
||||
class MangaReadingStatus extends Enum {
|
||||
final class MangaReadingStatus extends Enum {
|
||||
const ANILIST_TO_KITSU = [
|
||||
Anilist::READING => Kitsu::READING,
|
||||
Anilist::PLAN_TO_READ => Kitsu::PLAN_TO_READ,
|
||||
Anilist::COMPLETED => Kitsu::COMPLETED,
|
||||
Anilist::ON_HOLD => Kitsu::ON_HOLD,
|
||||
Anilist::DROPPED => Kitsu::DROPPED
|
||||
];
|
||||
|
||||
const KITSU_TO_ANILIST = [
|
||||
Kitsu::READING => Anilist::READING,
|
||||
Kitsu::PLAN_TO_READ => Anilist::PLAN_TO_READ,
|
||||
Kitsu::COMPLETED => Anilist::COMPLETED,
|
||||
Kitsu::ON_HOLD => Anilist::ON_HOLD,
|
||||
Kitsu::DROPPED => Anilist::DROPPED
|
||||
];
|
||||
|
||||
|
||||
const KITSU_TO_MAL = [
|
||||
Kitsu::READING => MAL::READING,
|
||||
Kitsu::PLAN_TO_READ => MAL::PLAN_TO_READ,
|
||||
|
@ -22,14 +22,14 @@ use function Amp\Promise\{all, wait};
|
||||
/**
|
||||
* Class to simplify making and validating simultaneous requests
|
||||
*/
|
||||
class ParallelAPIRequest {
|
||||
final class ParallelAPIRequest {
|
||||
|
||||
/**
|
||||
* Set of requests to make in parallel
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $requests = [];
|
||||
private $requests = [];
|
||||
|
||||
/**
|
||||
* Add a request
|
||||
@ -76,9 +76,7 @@ class ParallelAPIRequest {
|
||||
{
|
||||
$promises[$key] = call(function () use ($client, $url) {
|
||||
$response = yield $client->request($url);
|
||||
$body = yield $response->getBody();
|
||||
|
||||
return $body;
|
||||
return yield $response->getBody();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ use DOMDocument, DOMNode, DOMNodeList, InvalidArgumentException;
|
||||
/**
|
||||
* XML <=> PHP Array codec
|
||||
*/
|
||||
class XML {
|
||||
final class XML {
|
||||
|
||||
/**
|
||||
* XML representation of the data
|
||||
|
@ -19,7 +19,7 @@ namespace Aviat\AnimeClient\Command;
|
||||
/**
|
||||
* Clears the API Cache
|
||||
*/
|
||||
class CacheClear extends BaseCommand {
|
||||
final class CacheClear extends BaseCommand {
|
||||
/**
|
||||
* Clear the API cache
|
||||
*
|
||||
|
@ -19,7 +19,7 @@ namespace Aviat\AnimeClient\Command;
|
||||
/**
|
||||
* Clears the API Cache
|
||||
*/
|
||||
class CachePrime extends BaseCommand {
|
||||
final class CachePrime extends BaseCommand {
|
||||
/**
|
||||
* Clear, then prime the API cache
|
||||
*
|
||||
|
@ -32,7 +32,7 @@ use DateTime;
|
||||
/**
|
||||
* Clears the API Cache
|
||||
*/
|
||||
class SyncLists extends BaseCommand {
|
||||
final class SyncLists extends BaseCommand {
|
||||
|
||||
/**
|
||||
* Model for making requests to Kitsu API
|
||||
|
@ -20,6 +20,7 @@ use Aviat\AnimeClient\Controller as BaseController;
|
||||
use Aviat\AnimeClient\API\Kitsu\Transformer\AnimeListTransformer;
|
||||
use Aviat\AnimeClient\API\Enum\AnimeWatchingStatus\Kitsu as KitsuWatchingStatus;
|
||||
use Aviat\AnimeClient\API\Mapping\AnimeWatchingStatus;
|
||||
use Aviat\AnimeClient\Types\AnimeFormItem;
|
||||
use Aviat\Ion\Di\ContainerInterface;
|
||||
use Aviat\Ion\Json;
|
||||
use Aviat\Ion\StringWrapper;
|
||||
@ -27,7 +28,7 @@ use Aviat\Ion\StringWrapper;
|
||||
/**
|
||||
* Controller for Anime-related pages
|
||||
*/
|
||||
class Anime extends BaseController {
|
||||
final class Anime extends BaseController {
|
||||
|
||||
use StringWrapper;
|
||||
|
||||
@ -201,7 +202,7 @@ class Anime extends BaseController {
|
||||
// large form-based updates
|
||||
$transformer = new AnimeListTransformer();
|
||||
$postData = $transformer->untransform($data);
|
||||
$fullResult = $this->model->updateLibraryItem($postData);
|
||||
$fullResult = $this->model->updateLibraryItem(new AnimeFormItem($postData));
|
||||
|
||||
if ($fullResult['statusCode'] === 200)
|
||||
{
|
||||
@ -232,7 +233,7 @@ class Anime extends BaseController {
|
||||
$data = $this->request->getParsedBody();
|
||||
}
|
||||
|
||||
$response = $this->model->updateLibraryItem($data);
|
||||
$response = $this->model->updateLibraryItem(new AnimeFormItem($data));
|
||||
|
||||
$this->cache->clear();
|
||||
$this->outputJSON($response['body'], $response['statusCode']);
|
||||
@ -277,7 +278,7 @@ class Anime extends BaseController {
|
||||
$show_data = $this->model->getAnime($animeId);
|
||||
$characters = [];
|
||||
|
||||
if (empty($show_data))
|
||||
if ($show_data->title === '')
|
||||
{
|
||||
$this->notFound(
|
||||
$this->config->get('whose_list') .
|
||||
@ -301,7 +302,7 @@ class Anime extends BaseController {
|
||||
'title' => $this->formatTitle(
|
||||
$this->config->get('whose_list') . "'s Anime List",
|
||||
'Anime',
|
||||
$show_data['titles'][0]
|
||||
$show_data->title
|
||||
),
|
||||
'characters' => $characters,
|
||||
'show_data' => $show_data,
|
||||
|
@ -26,7 +26,7 @@ use Aviat\Ion\Di\ContainerInterface;
|
||||
/**
|
||||
* Controller for Anime collection pages
|
||||
*/
|
||||
class AnimeCollection extends BaseController {
|
||||
final class AnimeCollection extends BaseController {
|
||||
|
||||
/**
|
||||
* The anime collection model
|
||||
|
@ -23,7 +23,7 @@ use Aviat\Ion\ArrayWrapper;
|
||||
/**
|
||||
* Controller for character description pages
|
||||
*/
|
||||
class Character extends BaseController {
|
||||
final class Character extends BaseController {
|
||||
|
||||
use ArrayWrapper;
|
||||
|
||||
|
@ -25,7 +25,7 @@ use Aviat\Ion\View\HtmlView;
|
||||
/**
|
||||
* Controller for handling routes that don't fit elsewhere
|
||||
*/
|
||||
class Index extends BaseController {
|
||||
final class Index extends BaseController {
|
||||
|
||||
/**
|
||||
* Purges the API cache
|
||||
@ -72,6 +72,24 @@ class Index extends BaseController {
|
||||
], $view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to Anilist to start Oauth flow
|
||||
*/
|
||||
public function anilistRedirect()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Oauth callback for Anilist API
|
||||
*/
|
||||
public function anilistCallback()
|
||||
{
|
||||
$this->outputHTML('blank', [
|
||||
'title' => 'Oauth!'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt login authentication
|
||||
*
|
||||
|
@ -20,13 +20,14 @@ use Aviat\AnimeClient\Controller;
|
||||
use Aviat\AnimeClient\API\Kitsu\Transformer\MangaListTransformer;
|
||||
use Aviat\AnimeClient\API\Mapping\MangaReadingStatus;
|
||||
use Aviat\AnimeClient\Model\Manga as MangaModel;
|
||||
use Aviat\AnimeClient\Types\MangaFormItem;
|
||||
use Aviat\Ion\Di\ContainerInterface;
|
||||
use Aviat\Ion\{Json, StringWrapper};
|
||||
|
||||
/**
|
||||
* Controller for manga list
|
||||
*/
|
||||
class Manga extends Controller {
|
||||
final class Manga extends Controller {
|
||||
|
||||
use StringWrapper;
|
||||
|
||||
@ -200,7 +201,7 @@ class Manga extends Controller {
|
||||
// large form-based updates
|
||||
$transformer = new MangaListTransformer();
|
||||
$post_data = $transformer->untransform($data);
|
||||
$full_result = $this->model->updateLibraryItem($post_data);
|
||||
$full_result = $this->model->updateLibraryItem(new MangaFormItem($post_data));
|
||||
|
||||
if ($full_result['statusCode'] === 200)
|
||||
{
|
||||
@ -234,7 +235,7 @@ class Manga extends Controller {
|
||||
$data = $this->request->getParsedBody();
|
||||
}
|
||||
|
||||
$response = $this->model->updateLibraryItem($data);
|
||||
$response = $this->model->updateLibraryItem(new MangaFormItem($data));
|
||||
|
||||
$this->cache->clear();
|
||||
$this->outputJSON($response['body'], $response['statusCode']);
|
||||
|
@ -26,7 +26,7 @@ use Aviat\Ion\Di\ContainerInterface;
|
||||
/**
|
||||
* Controller for manga collection pages
|
||||
*/
|
||||
class MangaCollection extends BaseController {
|
||||
final class MangaCollection extends BaseController {
|
||||
|
||||
/**
|
||||
* The manga collection model
|
||||
|
@ -28,7 +28,7 @@ use Aviat\Ion\StringWrapper;
|
||||
/**
|
||||
* Basic routing/ dispatch
|
||||
*/
|
||||
class Dispatcher extends RoutingBase {
|
||||
final class Dispatcher extends RoutingBase {
|
||||
|
||||
use StringWrapper;
|
||||
|
||||
|
@ -17,13 +17,14 @@
|
||||
namespace Aviat\AnimeClient\Helper;
|
||||
|
||||
use Aviat\AnimeClient\MenuGenerator;
|
||||
use Aviat\Ion\Di\ContainerAware;
|
||||
|
||||
/**
|
||||
* MenuGenerator helper wrapper
|
||||
*/
|
||||
class Menu {
|
||||
final class Menu {
|
||||
|
||||
use \Aviat\Ion\Di\ContainerAware;
|
||||
use ContainerAware;
|
||||
|
||||
/**
|
||||
* Create the html for the selected menu
|
||||
|
@ -25,7 +25,7 @@ use Aviat\Ion\Exception\ConfigException;
|
||||
/**
|
||||
* Helper object to manage menu creation and selection
|
||||
*/
|
||||
class MenuGenerator extends UrlGenerator {
|
||||
final class MenuGenerator extends UrlGenerator {
|
||||
|
||||
use ArrayWrapper;
|
||||
use StringWrapper;
|
||||
|
@ -44,7 +44,7 @@ class API {
|
||||
|
||||
foreach ($array as $key => $item)
|
||||
{
|
||||
$sort[$key] = $item[$sortKey]['titles'][0];
|
||||
$sort[$key] = $item[$sortKey]['title'];
|
||||
}
|
||||
|
||||
array_multisort($sort, SORT_ASC, $array);
|
||||
|
@ -18,6 +18,11 @@ namespace Aviat\AnimeClient\Model;
|
||||
|
||||
use Aviat\AnimeClient\API\ParallelAPIRequest;
|
||||
use Aviat\AnimeClient\API\Mapping\AnimeWatchingStatus;
|
||||
use Aviat\AnimeClient\Types\{
|
||||
Anime as AnimeType,
|
||||
AnimeFormItem,
|
||||
AnimeListItem
|
||||
};
|
||||
use Aviat\Ion\Di\ContainerInterface;
|
||||
use Aviat\Ion\Json;
|
||||
|
||||
@ -93,9 +98,9 @@ class Anime extends API {
|
||||
* Get information about an anime from its slug
|
||||
*
|
||||
* @param string $slug
|
||||
* @return array
|
||||
* @return AnimeType
|
||||
*/
|
||||
public function getAnime(string $slug): array
|
||||
public function getAnime(string $slug): AnimeType
|
||||
{
|
||||
return $this->kitsuModel->getAnime($slug);
|
||||
}
|
||||
@ -127,9 +132,9 @@ class Anime extends API {
|
||||
* for editing/updating that item
|
||||
*
|
||||
* @param string $itemId
|
||||
* @return array
|
||||
* @return AnimeListItem
|
||||
*/
|
||||
public function getLibraryItem(string $itemId): array
|
||||
public function getLibraryItem(string $itemId): AnimeListItem
|
||||
{
|
||||
return $this->kitsuModel->getListItem($itemId);
|
||||
}
|
||||
@ -166,10 +171,10 @@ class Anime extends API {
|
||||
/**
|
||||
* Update a list entry
|
||||
*
|
||||
* @param array $data
|
||||
* @param AnimeFormItem $data
|
||||
* @return array
|
||||
*/
|
||||
public function updateLibraryItem(array $data): array
|
||||
public function updateLibraryItem(AnimeFormItem $data): array
|
||||
{
|
||||
$requester = new ParallelAPIRequest();
|
||||
|
||||
|
@ -22,7 +22,7 @@ use PDO;
|
||||
/**
|
||||
* Model for getting anime collection data
|
||||
*/
|
||||
class AnimeCollection extends Collection {
|
||||
final class AnimeCollection extends Collection {
|
||||
|
||||
/**
|
||||
* Anime API Model
|
||||
|
@ -21,14 +21,18 @@ use Aviat\AnimeClient\API\{
|
||||
Mapping\MangaReadingStatus,
|
||||
ParallelAPIRequest
|
||||
};
|
||||
use Aviat\AnimeClient\Types\{
|
||||
MangaFormItem,
|
||||
MangaListItem,
|
||||
MangaPage
|
||||
};
|
||||
use Aviat\Ion\Di\ContainerInterface;
|
||||
use Aviat\Ion\Json;
|
||||
|
||||
/**
|
||||
* Model for handling requests dealing with the manga list
|
||||
*/
|
||||
class Manga extends API
|
||||
{
|
||||
class Manga extends API {
|
||||
/**
|
||||
* Model for making requests to Kitsu API
|
||||
* @var \Aviat\AnimeClient\API\Kitsu\Model
|
||||
@ -67,21 +71,28 @@ class Manga extends API
|
||||
{
|
||||
if ($status === 'All')
|
||||
{
|
||||
return $this->kitsuModel->getFullOrganizedMangaList();
|
||||
$data = $this->kitsuModel->getFullOrganizedMangaList();
|
||||
foreach($data as &$section)
|
||||
{
|
||||
$this->sortByName($section, 'manga');
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
$APIstatus = MangaReadingStatus::TITLE_TO_KITSU[$status];
|
||||
$data = $this->kitsuModel->getMangaList($APIstatus);
|
||||
return $this->mapByStatus($data)[$status];
|
||||
$data = $this->mapByStatus($this->kitsuModel->getMangaList($APIstatus));
|
||||
$this->sortByName($data[$status], 'manga');
|
||||
return $data[$status];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the details of a manga
|
||||
*
|
||||
* @param string $manga_id
|
||||
* @return array
|
||||
* @return MangaPage
|
||||
*/
|
||||
public function getManga($manga_id): array
|
||||
public function getManga($manga_id): MangaPage
|
||||
{
|
||||
return $this->kitsuModel->getManga($manga_id);
|
||||
}
|
||||
@ -90,9 +101,9 @@ class Manga extends API
|
||||
* Get anime by its kitsu id
|
||||
*
|
||||
* @param string $animeId
|
||||
* @return array
|
||||
* @return MangaPage
|
||||
*/
|
||||
public function getMangaById(string $animeId): array
|
||||
public function getMangaById(string $animeId): MangaPage
|
||||
{
|
||||
return $this->kitsuModel->getMangaById($animeId);
|
||||
}
|
||||
@ -102,9 +113,9 @@ class Manga extends API
|
||||
* for editing/updating that item
|
||||
*
|
||||
* @param string $itemId
|
||||
* @return array
|
||||
* @return MangaListItem
|
||||
*/
|
||||
public function getLibraryItem(string $itemId): array
|
||||
public function getLibraryItem(string $itemId): MangaListItem
|
||||
{
|
||||
return $this->kitsuModel->getListItem($itemId);
|
||||
}
|
||||
@ -141,10 +152,10 @@ class Manga extends API
|
||||
/**
|
||||
* Update a list entry
|
||||
*
|
||||
* @param array $data
|
||||
* @param MangaFormItem $data
|
||||
* @return array
|
||||
*/
|
||||
public function updateLibraryItem(array $data): array
|
||||
public function updateLibraryItem(MangaFormItem $data): array
|
||||
{
|
||||
$requester = new ParallelAPIRequest();
|
||||
|
||||
@ -221,9 +232,7 @@ class Manga extends API
|
||||
$output[$key][] = $entry;
|
||||
}
|
||||
|
||||
foreach ($output as &$val) {
|
||||
$this->sortByName($val, 'manga');
|
||||
}
|
||||
unset($entry);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ use PDO;
|
||||
/**
|
||||
* Model for getting anime collection data
|
||||
*/
|
||||
class MangaCollection extends Collection {
|
||||
final class MangaCollection extends Collection {
|
||||
|
||||
/**
|
||||
* Manga API Model
|
||||
|
157
src/Types/AbstractType.php
Normal file
157
src/Types/AbstractType.php
Normal file
@ -0,0 +1,157 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 7
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\Types;
|
||||
|
||||
use ArrayAccess;
|
||||
use LogicException;
|
||||
|
||||
abstract class AbstractType implements ArrayAccess {
|
||||
/**
|
||||
* Populate values for unserializing data
|
||||
*
|
||||
* @param $properties
|
||||
* @return mixed
|
||||
*/
|
||||
public static function __set_state($properties)
|
||||
{
|
||||
return new static($properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the properties by using the constructor
|
||||
*
|
||||
* @param mixed $data
|
||||
*/
|
||||
public function __construct($data = [])
|
||||
{
|
||||
$typeKeys = array_keys((array)$this);
|
||||
$dataKeys = array_keys((array)$data);
|
||||
|
||||
$unsetKeys = array_diff($typeKeys, $dataKeys);
|
||||
|
||||
foreach ($data as $key => $value)
|
||||
{
|
||||
$this->__set($key, $value);
|
||||
}
|
||||
|
||||
// Remove unset keys so that they aren't serialized
|
||||
foreach ($unsetKeys as $k)
|
||||
{
|
||||
unset($this->$k);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See if a property is set
|
||||
*
|
||||
* @param $name
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name): bool
|
||||
{
|
||||
return property_exists($this, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a property on the type object
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function __set($name, $value): void
|
||||
{
|
||||
$setterMethod = 'set' . ucfirst($name);
|
||||
|
||||
if (method_exists($this, $setterMethod))
|
||||
{
|
||||
$this->$setterMethod($value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!property_exists($this, $name))
|
||||
{
|
||||
$existing = json_encode($this);
|
||||
|
||||
throw new LogicException("Trying to set non-existent property: '$name'. Existing properties: $existing");
|
||||
}
|
||||
|
||||
$this->$name = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a property from the type object
|
||||
*
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
if (property_exists($this, $name))
|
||||
{
|
||||
return $this->$name;
|
||||
}
|
||||
|
||||
throw new LogicException("Trying to get non-existent property: '$name'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementing ArrayAccess
|
||||
*
|
||||
* @param $offset
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset): bool
|
||||
{
|
||||
return $this->__isset($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementing ArrayAccess
|
||||
*
|
||||
* @param $offset
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->__get($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementing ArrayAccess
|
||||
*
|
||||
* @param $offset
|
||||
* @param $value
|
||||
*/
|
||||
public function offsetSet($offset, $value): void
|
||||
{
|
||||
$this->__set($offset, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementing ArrayAccess
|
||||
*
|
||||
* @param $offset
|
||||
*/
|
||||
public function offsetUnset($offset): void
|
||||
{
|
||||
if ($this->offsetExists($offset))
|
||||
{
|
||||
unset($this->$offset);
|
||||
}
|
||||
}
|
||||
}
|
40
src/Types/Anime.php
Normal file
40
src/Types/Anime.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 7
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\Types;
|
||||
|
||||
/**
|
||||
* Type representing an Anime object for display
|
||||
*/
|
||||
final class Anime extends AbstractType {
|
||||
public $age_rating;
|
||||
public $age_rating_guide;
|
||||
public $cover_image;
|
||||
public $episode_count;
|
||||
public $episode_length;
|
||||
public $genres;
|
||||
public $id;
|
||||
public $included;
|
||||
public $show_type;
|
||||
public $slug;
|
||||
public $status;
|
||||
public $streaming_links;
|
||||
public $synopsis;
|
||||
public $title;
|
||||
public $titles;
|
||||
public $trailer_id;
|
||||
public $url;
|
||||
}
|
27
src/Types/AnimeFormItem.php
Normal file
27
src/Types/AnimeFormItem.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 7
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\Types;
|
||||
|
||||
/**
|
||||
* Type representing an Anime object for display
|
||||
*/
|
||||
final class AnimeFormItem extends FormItem {
|
||||
public function setData($value): void
|
||||
{
|
||||
$this->data = new AnimeFormItemData($value);
|
||||
}
|
||||
}
|
22
src/Types/AnimeFormItemData.php
Normal file
22
src/Types/AnimeFormItemData.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 7
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\Types;
|
||||
|
||||
/**
|
||||
* Type representing an Anime object for display
|
||||
*/
|
||||
final class AnimeFormItemData extends FormItemData {}
|
42
src/Types/AnimeListItem.php
Normal file
42
src/Types/AnimeListItem.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 7
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\Types;
|
||||
|
||||
/**
|
||||
* Type representing an Anime object for display
|
||||
*/
|
||||
final class AnimeListItem extends AbstractType {
|
||||
public $id;
|
||||
public $mal_id;
|
||||
public $episodes = [
|
||||
'length' => 0,
|
||||
'total' => 0,
|
||||
'watched' => '',
|
||||
];
|
||||
public $airing = [
|
||||
'status' => '',
|
||||
'started' => '',
|
||||
'ended' => '',
|
||||
];
|
||||
public $anime;
|
||||
public $watching_status;
|
||||
public $notes;
|
||||
public $rewatching;
|
||||
public $rewatched;
|
||||
public $user_rating;
|
||||
public $private;
|
||||
}
|
29
src/Types/FormItem.php
Normal file
29
src/Types/FormItem.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 7
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\Types;
|
||||
|
||||
/**
|
||||
* Type representing an Anime object for display
|
||||
*/
|
||||
abstract class FormItem extends AbstractType {
|
||||
public $id;
|
||||
public $mal_id;
|
||||
public $data;
|
||||
|
||||
abstract public function setData($value): void;
|
||||
}
|
||||
|
30
src/Types/FormItemData.php
Normal file
30
src/Types/FormItemData.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 7
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\Types;
|
||||
|
||||
/**
|
||||
* Type representing an Anime object for display
|
||||
*/
|
||||
abstract class FormItemData extends AbstractType {
|
||||
public $notes;
|
||||
public $private;
|
||||
public $progress;
|
||||
public $rating;
|
||||
public $reconsumeCount;
|
||||
public $reconsuming;
|
||||
public $status;
|
||||
}
|
28
src/Types/MangaFormItem.php
Normal file
28
src/Types/MangaFormItem.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 7
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\Types;
|
||||
|
||||
/**
|
||||
* Form data for updating a Manga List item
|
||||
*/
|
||||
final class MangaFormItem extends FormItem {
|
||||
public function setData($value): void
|
||||
{
|
||||
$this->data = new MangaFormItemData($value);
|
||||
}
|
||||
}
|
||||
|
19
src/Types/MangaFormItemData.php
Normal file
19
src/Types/MangaFormItemData.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 7
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\Types;
|
||||
|
||||
final class MangaFormItemData extends FormItemData {}
|
40
src/Types/MangaListItem.php
Normal file
40
src/Types/MangaListItem.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 7
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\Types;
|
||||
|
||||
/**
|
||||
* Type representing an Anime object for display
|
||||
*/
|
||||
final class MangaListItem extends AbstractType {
|
||||
public $id;
|
||||
public $mal_id;
|
||||
public $chapters = [
|
||||
'read' => 0,
|
||||
'total' => 0,
|
||||
];
|
||||
public $volumes = [
|
||||
'read' => '-',
|
||||
'total' => 0,
|
||||
];
|
||||
public $manga;
|
||||
public $reading_status;
|
||||
public $notes;
|
||||
public $rereading;
|
||||
public $reread;
|
||||
public $user_rating;
|
||||
}
|
||||
|
31
src/Types/MangaListItemDetail.php
Normal file
31
src/Types/MangaListItemDetail.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 7
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\Types;
|
||||
|
||||
/**
|
||||
* Type representing the manga represented by the list item
|
||||
*/
|
||||
final class MangaListItemDetail extends AbstractType {
|
||||
public $genres;
|
||||
public $id;
|
||||
public $image;
|
||||
public $slug;
|
||||
public $title;
|
||||
public $titles;
|
||||
public $type;
|
||||
public $url;
|
||||
}
|
35
src/Types/MangaPage.php
Normal file
35
src/Types/MangaPage.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 7
|
||||
*
|
||||
* @package HummingbirdAnimeClient
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2015 - 2018 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 4.0
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient\Types;
|
||||
|
||||
/**
|
||||
* Type representing an Anime object for display
|
||||
*/
|
||||
final class MangaPage extends AbstractType {
|
||||
public $chapter_count;
|
||||
public $cover_image;
|
||||
public $en_title;
|
||||
public $genres;
|
||||
public $id;
|
||||
public $included;
|
||||
public $jp_title;
|
||||
public $manga_type;
|
||||
public $synopsis;
|
||||
public $title;
|
||||
public $url;
|
||||
public $volume_count;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user