1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
|
<?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 = [
[
'min_episodes' => [
'name' => 'Minimum Episodes',
'type' => 'number',
'title' => 'Minimum number of episodes before including in feed',
'defaultValue' => 0,
],
'min_total_episodes' => [
'name' => 'Minimum Total Episodes',
'type' => 'number',
'title' => 'Minimum total number of episodes before including in feed',
'defaultValue' => 0,
],
'require_banner' => [
'name' => 'Require Banner',
'type' => 'checkbox',
'title' => 'Only include anime with custom banner image',
'defaultValue' => false,
],
],
];
private $uri;
public function getURI()
{
return $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) {
[
$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[] = [
'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');
$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);
$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>';
}
}
|