rss-bridge/bridges/ShanaprojectBridge.php
logmanoriginal c0edf6e424 [ShanaprojectBridge] Add filter options
- Filter by minimum number of episodes
- Filter by minimum number of total episodes
- Filter by banner image
2019-07-03 19:34:43 +02:00

188 lines
5.2 KiB
PHP

<?php
class ShanaprojectBridge extends BridgeAbstract {
const MAINTAINER = 'logmanoriginal';
const NAME = 'Shanaproject Bridge';
const URI = 'https://www.shanaproject.com';
const DESCRIPTION = 'Returns a list of anime from the current Season Anime List';
const PARAMETERS = array(
array(
'min_episodes' => array(
'name' => 'Minimum Episodes',
'type' => 'number',
'title' => 'Minimum number of episodes before including in feed',
'defaultValue' => 0,
),
'min_total_episodes' => array(
'name' => 'Minimum Total Episodes',
'type' => 'number',
'title' => 'Minimum total number of episodes before including in feed',
'defaultValue' => 0,
),
'require_banner' => array(
'name' => 'Require Banner',
'type' => 'checkbox',
'title' => 'Only include anime with custom banner image',
'defaultValue' => false,
),
),
);
private $uri;
public function getURI() {
return isset($this->uri) ? $this->uri : parent::getURI();
}
public function collectData(){
$html = $this->loadSeasonAnimeList();
$animes = $html->find('div.header_display_box_info')
or returnServerError('Could not find anime headers!');
$min_episodes = $this->getInput('min_episodes') ?: 0;
$min_total_episodes = $this->getInput('min_total_episodes') ?: 0;
foreach($animes as $anime) {
list(
$episodes_released,
/* of */,
$episodes_total
) = explode(' ', $this->extractAnimeEpisodeInformation($anime));
// Skip if not enough episodes yet
if ($episodes_released < $min_episodes) {
continue;
}
// Skip if too many episodes in total
if ($episodes_total !== '?' && $episodes_total < $min_total_episodes) {
continue;
}
// Skip if https://static.shanaproject.com/no-art.jpg
if ($this->getInput('require_banner')
&& strpos($this->extractAnimeBackgroundImage($anime), 'no-art') !== false) {
continue;
}
$this->items[] = array(
'title' => $this->extractAnimeTitle($anime),
'author' => $this->extractAnimeAuthor($anime),
'uri' => $this->extractAnimeUri($anime),
'timestamp' => $this->extractAnimeTimestamp($anime),
'content' => $this->buildAnimeContent($anime),
);
}
}
// Returns an html object for the Season Anime List (latest season)
private function loadSeasonAnimeList(){
$html = getSimpleHTMLDOM(self::URI . '/seasons')
or returnServerError('Could not load \'seasons\' page!');
$html = defaultLinkTo($html, self::URI . '/seasons');
$season = $html->find('div.follows_menu > a', 1)
or returnServerError('Could not find \'Season Anime List\'!');
$html = getSimpleHTMLDOM($season->href)
or returnServerError(
'Could not load \'Season Anime List\' from \''
. $season->innertext
. '\'!'
);
$this->uri = $season->href;
$html = defaultLinkTo($html, $season->href);
return $html;
}
// Extracts the anime title
private function extractAnimeTitle($anime){
$title = $anime->find('a', 0)
or returnServerError('Could not find anime title!');
return trim($title->innertext);
}
// Extracts the anime URI
private function extractAnimeUri($anime){
$uri = $anime->find('a', 0)
or returnServerError('Could not find anime URI!');
return $uri->href;
}
// Extracts the anime release date (timestamp)
private function extractAnimeTimestamp($anime){
$timestamp = $anime->find('span.header_info_block', 1);
if(!$timestamp) {
return null;
}
return strtotime($timestamp->innertext);
}
// Extracts the anime studio name (author)
private function extractAnimeAuthor($anime){
$author = $anime->find('span.header_info_block', 2);
if(!$author) {
return null; // Sometimes the studio is unknown, so leave empty
}
return trim($author->innertext);
}
// Extracts the episode information (x of y released)
private function extractAnimeEpisodeInformation($anime){
$episode = $anime->find('div.header_info_episode', 0)
or returnServerError('Could not find anime episode information!');
$retVal = preg_replace('/\r|\n/', ' ', $episode->plaintext);
$retVal = preg_replace('/\s+/', ' ', $retVal);
return $retVal;
}
// Extracts the background image
private function extractAnimeBackgroundImage($anime){
// Getting the picture is a little bit tricky as it is part of the style.
// Luckily the style is part of the parent div :)
if(preg_match('/url\(\/\/([^\)]+)\)/i', $anime->parent->style, $matches)) {
return $matches[1];
}
returnServerError('Could not extract background image!');
}
// Builds an URI to search for a specific anime (subber is left empty)
private function buildAnimeSearchUri($anime){
return self::URI
. '/search/?title='
. urlencode($this->extractAnimeTitle($anime))
. '&subber=';
}
// Builds the content string for a given anime
private function buildAnimeContent($anime){
// We'll use a template string to place our contents
return '<a href="'
. $this->extractAnimeUri($anime)
. '"><img src="http://'
. $this->extractAnimeBackgroundImage($anime)
. '" alt="'
. htmlspecialchars($this->extractAnimeTitle($anime))
. '" style="border: 1px solid black"></a><br><p>'
. $this->extractAnimeEpisodeInformation($anime)
. '</p><br><p><a href="'
. $this->buildAnimeSearchUri($anime)
. '">Search episodes</a></p>';
}
}