MangareaderBridge.php 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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 DESCRIPTION = "Returns the latest updates, popular mangas or manga updates (new chapters)";
  7. const PARAMETERS = array(
  8. 'Get latest updates' => array(),
  9. 'Get popular mangas' => array(
  10. 'category' => array(
  11. 'name' => 'Category',
  12. 'type' => 'list',
  13. 'required' => true,
  14. 'values' => array(
  15. 'All' => 'all',
  16. 'Action' => 'action',
  17. 'Adventure' => 'adventure',
  18. 'Comedy' => 'comedy',
  19. 'Demons' => 'demons',
  20. 'Drama' => 'drama',
  21. 'Ecchi' => 'ecchi',
  22. 'Fantasy' => 'fantasy',
  23. 'Gender Bender' => 'gender-bender',
  24. 'Harem' => 'harem',
  25. 'Historical' => 'historical',
  26. 'Horror' => 'horror',
  27. 'Josei' => 'josei',
  28. 'Magic' => 'magic',
  29. 'Martial Arts' => 'martial-arts',
  30. 'Mature' => 'mature',
  31. 'Mecha' => 'mecha',
  32. 'Military' => 'military',
  33. 'Mystery' => 'mystery',
  34. 'One Shot' => 'one-shot',
  35. 'Psychological' => 'psychological',
  36. 'Romance' => 'romance',
  37. 'School Life' => 'school-life',
  38. 'Sci-Fi' => 'sci-fi',
  39. 'Seinen' => 'seinen',
  40. 'Shoujo' => 'shoujo',
  41. 'Shoujoai' => 'shoujoai',
  42. 'Shounen' => 'shounen',
  43. 'Shounenai' => 'shounenai',
  44. 'Slice of Life' => 'slice-of-life',
  45. 'Smut' => 'smut',
  46. 'Sports' => 'sports',
  47. 'Super Power' => 'super-power',
  48. 'Supernatural' => 'supernatural',
  49. 'Tragedy' => 'tragedy',
  50. 'Vampire' => 'vampire',
  51. 'Yaoi' => 'yaoi',
  52. 'Yuri' => 'yuri'
  53. ),
  54. 'exampleValue' => 'All',
  55. 'title' => 'Select your category'
  56. )
  57. ),
  58. 'Get manga updates' => array(
  59. 'path' => array(
  60. 'name' => 'Path',
  61. 'required' => true,
  62. 'pattern' => '[a-zA-Z0-9-_]*',
  63. 'exampleValue' => 'bleach, umi-no-kishidan',
  64. 'title' => 'URL part of desired manga'
  65. ),
  66. 'limit' => array(
  67. 'name' => 'Limit',
  68. 'type' => 'number',
  69. 'defaultValue' => 10,
  70. 'title' => 'Number of items to return [-1 returns all]'
  71. )
  72. )
  73. );
  74. private $request = '';
  75. public function collectData(){
  76. // We'll use the DOM parser for this as it makes navigation easier
  77. $html = $this->getContents($this->getURI());
  78. if(!$html){
  79. $this->returnClientError('Could not receive data for ' . $path . '!');
  80. }
  81. libxml_use_internal_errors(true);
  82. $doc = new DomDocument;
  83. @$doc->loadHTML($html);
  84. libxml_clear_errors();
  85. // Navigate via XPath
  86. $xpath = new DomXPath($doc);
  87. $this->request = '';
  88. switch($this->queriedContext){
  89. case 'Get latest updates':
  90. $this->request = 'Latest updates';
  91. $this->get_latest_updates($xpath);
  92. break;
  93. case 'Get popular mangas':
  94. // Find manga name within "Popular mangas for ..."
  95. $pagetitle = $xpath->query(".//*[@id='bodyalt']/h1")->item(0)->nodeValue;
  96. $this->request = substr($pagetitle, 0, strrpos($pagetitle, " -"));
  97. $this->get_popular_mangas($xpath);
  98. break;
  99. case 'Get manga updates':
  100. $limit = $this->getInput('limit');
  101. if(empty($limit)){
  102. $limit = self::PARAMETERS[$this->queriedContext]['limit']['defaultValue'];
  103. }
  104. $this->request = $xpath->query(".//*[@id='mangaproperties']//*[@class='aname']")
  105. ->item(0)
  106. ->nodeValue;
  107. $this->get_manga_updates($xpath, $limit);
  108. break;
  109. }
  110. // Return some dummy-data if no content available
  111. if(empty($this->items)){
  112. $item = array();
  113. $item['content'] = "<p>No updates available</p>";
  114. $this->items[] = $item;
  115. }
  116. }
  117. private function get_latest_updates($xpath){
  118. // Query each item (consists of Manga + chapters)
  119. $nodes = $xpath->query("//*[@id='latestchapters']/table//td");
  120. foreach ($nodes as $node){
  121. // Query the manga
  122. $manga = $xpath->query("a[@class='chapter']", $node)->item(0);
  123. // Collect the chapters for each Manga
  124. $chapters = $xpath->query("a[@class='chaptersrec']", $node);
  125. if (isset($manga) && $chapters->length >= 1){
  126. $item = array();
  127. $item['uri'] = self::URI . htmlspecialchars($manga->getAttribute('href'));
  128. $item['title'] = htmlspecialchars($manga->nodeValue);
  129. // Add each chapter to the feed
  130. $item['content'] = "";
  131. foreach ($chapters as $chapter){
  132. if($item['content'] <> ""){
  133. $item['content'] .= "<br>";
  134. }
  135. $item['content'] .=
  136. "<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 get_popular_mangas($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 get_manga_updates($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. }
  216. return self::URI . $path;
  217. }
  218. public function getName(){
  219. return (!empty($this->request) ? $this->request . ' - ' : '') . 'Mangareader Bridge';
  220. }
  221. public function getCacheDuration(){
  222. return 10800; // 3 hours
  223. }
  224. }
  225. ?>