MangareaderBridge.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. <?php
  2. class MangareaderBridge extends BridgeAbstract {
  3. const MAINTAINER = 'logmanoriginal';
  4. const NAME = 'Mangareader Bridge';
  5. const URI = 'http://www.mangareader.net';
  6. const CACHE_TIMEOUT = 10800; // 3h
  7. const DESCRIPTION = 'Returns the latest updates, popular mangas or manga updates (new chapters)';
  8. const PARAMETERS = array(
  9. 'Get latest updates' => array(),
  10. 'Get popular mangas' => array(
  11. 'category' => array(
  12. 'name' => 'Category',
  13. 'type' => 'list',
  14. 'required' => true,
  15. 'values' => array(
  16. 'All' => 'all',
  17. 'Action' => 'action',
  18. 'Adventure' => 'adventure',
  19. 'Comedy' => 'comedy',
  20. 'Demons' => 'demons',
  21. 'Drama' => 'drama',
  22. 'Ecchi' => 'ecchi',
  23. 'Fantasy' => 'fantasy',
  24. 'Gender Bender' => 'gender-bender',
  25. 'Harem' => 'harem',
  26. 'Historical' => 'historical',
  27. 'Horror' => 'horror',
  28. 'Josei' => 'josei',
  29. 'Magic' => 'magic',
  30. 'Martial Arts' => 'martial-arts',
  31. 'Mature' => 'mature',
  32. 'Mecha' => 'mecha',
  33. 'Military' => 'military',
  34. 'Mystery' => 'mystery',
  35. 'One Shot' => 'one-shot',
  36. 'Psychological' => 'psychological',
  37. 'Romance' => 'romance',
  38. 'School Life' => 'school-life',
  39. 'Sci-Fi' => 'sci-fi',
  40. 'Seinen' => 'seinen',
  41. 'Shoujo' => 'shoujo',
  42. 'Shoujoai' => 'shoujoai',
  43. 'Shounen' => 'shounen',
  44. 'Shounenai' => 'shounenai',
  45. 'Slice of Life' => 'slice-of-life',
  46. 'Smut' => 'smut',
  47. 'Sports' => 'sports',
  48. 'Super Power' => 'super-power',
  49. 'Supernatural' => 'supernatural',
  50. 'Tragedy' => 'tragedy',
  51. 'Vampire' => 'vampire',
  52. 'Yaoi' => 'yaoi',
  53. 'Yuri' => 'yuri'
  54. ),
  55. 'exampleValue' => 'All',
  56. 'title' => 'Select your category'
  57. )
  58. ),
  59. 'Get manga updates' => array(
  60. 'path' => array(
  61. 'name' => 'Path',
  62. 'required' => true,
  63. 'pattern' => '[a-zA-Z0-9-_]*',
  64. 'exampleValue' => 'bleach, umi-no-kishidan',
  65. 'title' => 'URL part of desired manga'
  66. ),
  67. 'limit' => array(
  68. 'name' => 'Limit',
  69. 'type' => 'number',
  70. 'defaultValue' => 10,
  71. 'title' => 'Number of items to return [-1 returns all]'
  72. )
  73. )
  74. );
  75. private $request = '';
  76. public function collectData(){
  77. // We'll use the DOM parser for this as it makes navigation easier
  78. $html = getContents($this->getURI());
  79. if(!$html) {
  80. returnClientError('Could not receive data for ' . $path . '!');
  81. }
  82. libxml_use_internal_errors(true);
  83. $doc = new DomDocument;
  84. @$doc->loadHTML($html);
  85. libxml_clear_errors();
  86. // Navigate via XPath
  87. $xpath = new DomXPath($doc);
  88. $this->request = '';
  89. switch($this->queriedContext) {
  90. case 'Get latest updates':
  91. $this->request = 'Latest updates';
  92. $this->getLatestUpdates($xpath);
  93. break;
  94. case 'Get popular mangas':
  95. // Find manga name within "Popular mangas for ..."
  96. $pagetitle = $xpath->query(".//*[@id='bodyalt']/h1")->item(0)->nodeValue;
  97. $this->request = substr($pagetitle, 0, strrpos($pagetitle, ' -'));
  98. $this->getPopularMangas($xpath);
  99. break;
  100. case 'Get manga updates':
  101. $limit = $this->getInput('limit');
  102. if(empty($limit)) {
  103. $limit = self::PARAMETERS[$this->queriedContext]['limit']['defaultValue'];
  104. }
  105. $this->request = $xpath->query(".//*[@id='mangaproperties']//*[@class='aname']")
  106. ->item(0)
  107. ->nodeValue;
  108. $this->getMangaUpdates($xpath, $limit);
  109. break;
  110. }
  111. // Return some dummy-data if no content available
  112. if(empty($this->items)) {
  113. $item = array();
  114. $item['content'] = '<p>No updates available</p>';
  115. $this->items[] = $item;
  116. }
  117. }
  118. private function getLatestUpdates($xpath){
  119. // Query each item (consists of Manga + chapters)
  120. $nodes = $xpath->query("//*[@id='latestchapters']/table//td");
  121. foreach ($nodes as $node) {
  122. // Query the manga
  123. $manga = $xpath->query("a[@class='chapter']", $node)->item(0);
  124. // Collect the chapters for each Manga
  125. $chapters = $xpath->query("a[@class='chaptersrec']", $node);
  126. if (isset($manga) && $chapters->length >= 1) {
  127. $item = array();
  128. $item['uri'] = self::URI . htmlspecialchars($manga->getAttribute('href'));
  129. $item['title'] = htmlspecialchars($manga->nodeValue);
  130. // Add each chapter to the feed
  131. $item['content'] = '';
  132. foreach ($chapters as $chapter) {
  133. if($item['content'] <> '') {
  134. $item['content'] .= '<br>';
  135. }
  136. $item['content'] .= "<a href='"
  137. . self::URI
  138. . htmlspecialchars($chapter->getAttribute('href'))
  139. . "'>"
  140. . htmlspecialchars($chapter->nodeValue)
  141. . '</a>';
  142. }
  143. $this->items[] = $item;
  144. }
  145. }
  146. }
  147. private function getPopularMangas($xpath){
  148. // Query all mangas
  149. $mangas = $xpath->query("//*[@id='mangaresults']/*[@class='mangaresultitem']");
  150. foreach ($mangas as $manga) {
  151. // The thumbnail is encrypted in a css-style...
  152. // format: "background-image:url('<the part which is actually interesting>')"
  153. $mangaimgelement = $xpath->query(".//*[@class='imgsearchresults']", $manga)
  154. ->item(0)
  155. ->getAttribute('style');
  156. $thumbnail = substr($mangaimgelement, 22, strlen($mangaimgelement) - 24);
  157. $item = array();
  158. $item['title'] = htmlspecialchars($xpath->query(".//*[@class='manga_name']//a", $manga)
  159. ->item(0)
  160. ->nodeValue);
  161. $item['uri'] = self::URI . $xpath->query(".//*[@class='manga_name']//a", $manga)
  162. ->item(0)
  163. ->getAttribute('href');
  164. $item['author'] = htmlspecialchars($xpath->query("//*[@class='author_name']", $manga)
  165. ->item(0)
  166. ->nodeValue);
  167. $item['chaptercount'] = $xpath->query(".//*[@class='chapter_count']", $manga)
  168. ->item(0)
  169. ->nodeValue;
  170. $item['genre'] = htmlspecialchars($xpath->query(".//*[@class='manga_genre']", $manga)
  171. ->item(0)
  172. ->nodeValue);
  173. $item['content'] = <<<EOD
  174. <a href="{$item['uri']}"><img src="{$thumbnail}" alt="{$item['title']}" /></a>
  175. <p>{$item['genre']}</p>
  176. <p>{$item['chaptercount']}</p>
  177. EOD;
  178. $this->items[] = $item;
  179. }
  180. }
  181. private function getMangaUpdates($xpath, $limit){
  182. $query = "(.//*[@id='listing']//tr)[position() > 1]";
  183. if($limit !== -1) {
  184. $query = "(.//*[@id='listing']//tr)[position() > 1][position() > last() - {$limit}]";
  185. }
  186. $chapters = $xpath->query($query);
  187. foreach ($chapters as $chapter) {
  188. $item = array();
  189. $item['title'] = htmlspecialchars($xpath->query('td[1]', $chapter)
  190. ->item(0)
  191. ->nodeValue);
  192. $item['uri'] = self::URI . $xpath->query('td[1]/a', $chapter)
  193. ->item(0)
  194. ->getAttribute('href');
  195. $item['timestamp'] = strtotime($xpath->query('td[2]', $chapter)
  196. ->item(0)
  197. ->nodeValue);
  198. array_unshift($this->items, $item);
  199. }
  200. }
  201. public function getURI(){
  202. switch($this->queriedContext) {
  203. case 'Get latest updates':
  204. $path = 'latest';
  205. break;
  206. case 'Get popular mangas':
  207. $path = 'popular';
  208. if($this->getInput('category') !== 'all') {
  209. $path .= '/' . $this->getInput('category');
  210. }
  211. break;
  212. case 'Get manga updates':
  213. $path = $this->getInput('path');
  214. break;
  215. default: return parent::getURI();
  216. }
  217. return self::URI . '/' . $path;
  218. }
  219. public function getName(){
  220. return (!empty($this->request) ? $this->request . ' - ' : '') . 'Mangareader Bridge';
  221. }
  222. }