PageRenderTime 66ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Lovelybooks/Client.php

https://gitlab.com/mjahn/lovelybooks-client
PHP | 1426 lines | 1181 code | 232 blank | 13 comment | 59 complexity | 52e329d8564329e3801aeb88475feb23 MD5 | raw file
  1. <?php
  2. namespace Lovelybooks;
  3. class Client {
  4. protected $curl = null;
  5. const
  6. READING_STATE_UNREAD = 'Lese ich gerade nicht',
  7. READING_STATE_READING = 'Lese ich gerade',
  8. READING_STATE_READ = 'Schon gelesen';
  9. protected $user = array(
  10. 'id' => 0,
  11. 'name' => '',
  12. 'isLoggedIn' => false
  13. );
  14. protected $ajax = [];
  15. public function __construct() {
  16. if(!isset($_SESSION['lb_cookies'])) {
  17. $_SESSION['lb_cookies'] = [];
  18. }
  19. if(!isset($_SESSION['lb_user'])) {
  20. $_SESSION['lb_user'] = '';
  21. }
  22. }
  23. /**
  24. * Checks the given header-lines for cookie-information and stores them in the session
  25. *
  26. * @param array $headers Array of strings with header-information
  27. * @return void
  28. */
  29. public function getCookies($headers) {
  30. $cookies = $_SESSION['lb_cookies'];
  31. foreach($headers as $header) {
  32. if(strpos($header, 'Set-Cookie: ') === 0) {
  33. list($cookie, ) = explode('; ', str_replace('Set-Cookie: ', '', $header));
  34. list($id, $value) = explode('=', $cookie);
  35. $cookies[$id] = $value;
  36. }
  37. }
  38. $_SESSION['lb_cookies'] = $cookies;
  39. }
  40. protected function decode($value) {
  41. return str_replace(
  42. array('-'),
  43. array(' '),
  44. $value
  45. );
  46. }
  47. protected function curlInstance() {
  48. $curl = new \Curl\Curl();
  49. $curl->setUserAgent('');
  50. $curl->setReferrer('https://www.lovelybooks.de');
  51. foreach($_SESSION['lb_cookies'] as $id => $value) {
  52. if ($id === 'LB-Token' && $value === '""') {
  53. continue;
  54. }
  55. $curl->setCookie($id, $value);
  56. }
  57. return $curl;
  58. }
  59. protected function checkResponse($status) {
  60. if ($status >= 500) {
  61. throw new \Exception('Lovelybooks ist nicht erreichbar!');
  62. }
  63. }
  64. protected function parseAjaxLinks($content) {
  65. $iMax = preg_match_all('/Wicket\.Ajax\.ajax\({[^}]*?"u":"([^"]+)".*?,"c":"(id[^"]+)"/', $content, $matches);
  66. $this->ajax = [];;
  67. for ($i = 0; $i < $iMax; $i++) {
  68. $this->ajax[$matches[2][$i]] = $matches[1][$i];
  69. }
  70. }
  71. protected function fetchAjaxContent($id) {
  72. if(!isset($this->ajax[$id])) {
  73. return '';
  74. }
  75. $curl = $this->curlInstance();
  76. $curl->setHeader('Accept', 'application/xml, text/xml, */*; q=0.01');
  77. $curl->setHeader('Wicket-Ajax', 'true');
  78. $curl->setHeader('Wicket-Ajax-BaseURL', '.');
  79. $curl->setHeader('Wicket-FocusedElementId', 'seleniumLogin');
  80. $curl->setHeader('X-Requested-With', 'XMLHttpRequest');
  81. $curl->get('https://www.lovelybooks.de' . $this->ajax[$id]);
  82. $this->getCookies($curl->response_headers);
  83. $this->checkResponse($curl->http_status_code);
  84. $content = $curl->response;
  85. $curl->close();
  86. return $content;
  87. }
  88. public function getUserStatus() {
  89. $curl = $this->curlInstance();
  90. $curl->get('https://www.lovelybooks.de/');
  91. $content = $curl->response;
  92. $this->getCookies($curl->response_headers);
  93. $unreadMessages = false;
  94. if (strpos($content, "'userStatus': ['ausgeloggt']") !== false) {
  95. $this->user['isLoggedIn'] = false;
  96. } else {
  97. $this->user['isLoggedIn'] = true;
  98. foreach($curl->response_headers as $header) {
  99. if(strpos($header, 'Location: /eingeloggt/') === 0) {
  100. $this->user['name'] = str_replace('/', '', str_replace('Location: /eingeloggt/', '', $header));
  101. $curl = $this->curlInstance();
  102. $curl->get('https://www.lovelybooks.de/eingeloggt/' . $this->user['name'] . '/');
  103. $this->getCookies($curl->response_headers);
  104. $this->checkResponse($curl->http_status_code);
  105. $curl->close();
  106. $unreadMessages = (strpos($content, 'new-mails') > 0);
  107. }
  108. }
  109. }
  110. return ['loggedIn' => $this->user['isLoggedIn'], 'name' => $this->user['name'], 'unreadMessages' => $unreadMessages];
  111. }
  112. public function login($email, $password) {
  113. $_SESSION['lb_cookies'] = [];
  114. $status = 500;
  115. $counter = 5;
  116. $curl = $this->curlInstance();
  117. $curl->setHeader('Cache-Control', 'no-cache');
  118. $curl->setHeader('Pragma', 'no-cache');
  119. $curl->setHeader('Connection', 'keep-alive');
  120. $curl->setHeader('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8');
  121. while ($counter > 0 && $status > 305) {
  122. $curl->get('https://www.lovelybooks.de/');
  123. $status = (int) $curl->http_status_code;
  124. if ($status > 305) {
  125. sleep(0.1);
  126. $counter--;
  127. }
  128. }
  129. foreach($curl->response_headers as $header) {
  130. if(strpos($header, 'Location: /eingeloggt/') === 0) {
  131. return true;
  132. }
  133. }
  134. $this->getCookies($curl->response_headers);
  135. $this->checkResponse($curl->http_status_code);
  136. $content = $curl->response;
  137. if (trim($content) === '') {
  138. throw new \Exception('Login failed '.$status . var_export($curl->response_headers, true));
  139. }
  140. preg_match('/Wicket\.Ajax\.ajax\(\{"u":"([^"]+)","e":"click","c":"seleniumLogin"/', $content, $matches);
  141. if(!isset($matches[1])) {
  142. throw new \Exception('Login failed 2');
  143. }
  144. $curl->close();
  145. $query = $matches[1];
  146. sleep(1);
  147. $curl = $this->curlInstance();
  148. $curl->setHeader('Accept', 'application/xml, text/xml, */*; q=0.01');
  149. $curl->setHeader('Wicket-Ajax', 'true');
  150. $curl->setHeader('Wicket-Ajax-BaseURL', '.');
  151. $curl->setHeader('Wicket-FocusedElementId', 'seleniumLogin');
  152. $curl->setHeader('X-Requested-With', 'XMLHttpRequest');
  153. $curl->get('https://www.lovelybooks.de/' . $query .'&_='.round(1000 * microtime(true)));
  154. $this->getCookies($curl->response_headers);
  155. $this->checkResponse($curl->http_status_code);
  156. $content = $curl->response;
  157. $curl->close();
  158. preg_match('/<input\stype="hidden"\sname="([^"]+)/', $content, $matches);
  159. $id = $matches[1];
  160. preg_match('/[\d-]+\.IBehaviorListener\.0-dialogContainer-loginForm-submit/', $content, $matches);
  161. $action = $matches[0];
  162. preg_match('/p::submit"\sid="([^"]+)"/', $content, $matches);
  163. $submitId = $matches[1];
  164. $data = [
  165. 'email' => $email,
  166. 'password' => $password,
  167. 'p::submit' => '1',
  168. $id => ''
  169. ];
  170. sleep(0.1);
  171. $curl = $this->curlInstance();
  172. $curl->verbose(true);
  173. $curl->setHeader('Accept', 'application/xml, text/xml, */*; q=0.01');
  174. $curl->setHeader('Wicket-Ajax', 'true');
  175. $curl->setHeader('Wicket-Ajax-BaseURL', '.');
  176. $curl->setHeader('Wicket-FocusedElementId', $submitId);
  177. $curl->setHeader('X-Requested-With', 'XMLHttpRequest');
  178. $curl->post('https://www.lovelybooks.de/?' . $action, $data);
  179. $this->getCookies($curl->response_headers);
  180. $this->checkResponse($curl->http_status_code);
  181. $content = $curl->response;
  182. $curl->close();
  183. // check for failed login-data
  184. if(strpos($content, '>Leider ist Deine E-Mail-Adresse und/oder Dein Passwort nicht korrekt. Bitte versuche es noch einmal.') > 0) {
  185. throw new \Exception('Login failed 3');
  186. }
  187. sleep(1);
  188. $curl = $this->curlInstance();
  189. $curl->setHeader('Upgrade-Insecure-Requests', '1');
  190. $curl->setHeader('Connection', 'keep-alive');
  191. $curl->setHeader('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8');
  192. $curl->get('https://www.lovelybooks.de/');
  193. $this->getCookies($curl->response_headers);
  194. $this->checkResponse($curl->http_status_code);
  195. foreach($curl->response_headers as $header) {
  196. if (strpos($header, 'Location: /eingeloggt/') === 0) {
  197. $_SESSION['lb_user'] = str_replace('/', '', str_replace('Location: /eingeloggt/', '', $header));
  198. }
  199. }
  200. $curl->close();
  201. $curl = $this->curlInstance();
  202. $curl->get('https://www.lovelybooks.de/eingeloggt/' . $_SESSION['lb_user'] . '/');
  203. $this->getCookies($curl->response_headers);
  204. $this->checkResponse($curl->http_status_code);
  205. $content = $curl->response;
  206. //$curl->close();
  207. if (strpos($content, "'userStatus': ['eingeloggt']") > 0) {
  208. return [
  209. 'name' => $_SESSION['lb_user'],
  210. 'loggedIn' => true,
  211. 'unreadMessages' => (strpos($content, 'new-mails') > 0),
  212. ];
  213. }
  214. return [
  215. 'loggedIn' => false,
  216. 'name' => '',
  217. 'content' => var_export($_SESSION, true)
  218. ];
  219. }
  220. public function getAuthorDetails($name) {
  221. $author = [];
  222. $name = urlencode($name);
  223. $curl = $this->curlInstance();
  224. $curl->get('https://www.lovelybooks.de/autor/' . $name . '/');
  225. $this->getCookies($curl->response_headers);
  226. $this->checkResponse($curl->http_status_code);
  227. $this->parseAjaxLinks($curl->response);
  228. $author['link'] = '/autor/' . $name . '/';
  229. $content = $curl->response;
  230. $curl->close();
  231. $content = explode('<div id="footer">', explode('itemprop="author">', $content)[1])[0];
  232. $author['name'] = explode('</h1>', $content)[0];
  233. preg_match('/Lebenslauf\s*<span>von ([^<]*)<\/span><\/h1>\s*<div class="text">([^<]*)/', $content, $matches);
  234. $author['biography'] = html_entity_decode(trim($matches[2]));
  235. preg_match('/autorpicL">\s*<img src="([^"]*)/', $content, $matches);
  236. $author['image'] = $matches[1];
  237. $author['books'] = [];
  238. // infos
  239. $infos = explode('</li>', explode('</ul>', explode('<ul class="facts">', $content)[2])[0]);
  240. array_pop($infos);
  241. $author['facts'] = [];
  242. foreach($infos as $data) {
  243. list($id, $value) = explode('</b>', $data);
  244. $id = str_replace(':', '', $id);
  245. $id = html_entity_decode(trim(strip_tags($id)));
  246. $value = html_entity_decode(trim(strip_tags($value, '<a>')));
  247. if($id == '' || $value == '') {
  248. continue;
  249. }
  250. $author['facts'][$id] = preg_replace('/<a href="#">\s*<a href="([^"]+)">([^<]+)<\/a>\s*<\/a>/', '<a href="$1">$2</a>', $value);
  251. }
  252. // videos
  253. $videos = explode('<iframe width="245" height="200" src="', explode('<div class="hr">', explode('<div class="videos">', $content)[1])[0]);
  254. array_shift($videos);
  255. $author['videos'] = [];
  256. foreach($videos as $data) {
  257. $author['videos'][] = [
  258. 'embedd' => '<iframe width="245" height="200" src="' . urldecode(explode('<', $data)[0]) . '</iframe>',
  259. 'url' => urldecode(explode('"', $data)[0])
  260. ];
  261. }
  262. $id = explode('"', explode('<a href="javascript:;" class="append" id="', $data)[1])[0];
  263. $videos = $this->fetchAjaxContent($id);
  264. $videos = explode('<iframe width="245" height="200" src="', $videos);
  265. array_shift($videos);
  266. foreach($videos as $data) {
  267. $author['videos'][] = [
  268. 'embedd' => '<iframe width="245" height="200" src="' . urldecode(explode('<', $data)[0]) . '</iframe>',
  269. 'url' => urldecode(explode('"', $data)[0])
  270. ];
  271. }
  272. // tags
  273. $tags = explode('</li>', '<div ' . explode('</ul>', explode('<div class="tagcloud"', $content)[1])[0]);
  274. array_pop($tags);
  275. $author['tags'] = [];
  276. foreach($tags as $data) {
  277. $data = trim(strip_tags($data));
  278. if($data !== '') {
  279. $author['tags'][] = $data;
  280. }
  281. }
  282. // fetch books of the author
  283. $page = 0;
  284. $counter = -1;
  285. $books = [];
  286. while($counter !== count($books)) {
  287. $counter = count($books);
  288. $books = array_merge($books, $this->getAuthorBooks($name, $page));
  289. $page++;
  290. }
  291. foreach($books as $id => $value) {
  292. $value['author'] = $author['name'];
  293. $value['authorlink'] = '/autor/' . $author['name'] . '/';
  294. $books[$id] = $value;
  295. }
  296. $author['books'] = array_keys($books);
  297. return ['books' => $books, 'author' => $author];
  298. }
  299. protected function getAuthorBooks($author, $page = 0) {
  300. $books = [];
  301. $curl = $this->curlInstance();
  302. $curl->get('https://www.lovelybooks.de/autor/' . $author . '/buch/?seite=' . $page);
  303. $this->getCookies($curl->response_headers);
  304. $this->checkResponse($curl->http_status_code);
  305. $content = $curl->response;
  306. $curl->close();
  307. $content = explode('<div class="controls">', $content)[0];
  308. $data = explode('dataviewitem', $content);
  309. array_shift($data);
  310. $i = 1;
  311. foreach($data as $bookdata) {
  312. preg_match('/<img src="([^"]+)/', $bookdata, $matches1);
  313. preg_match('/<h3><a href="([^"]+)[^<]+>([^<]+)<\/a><\/h3>\s*<div class="ratingstars r(\d+)[^>]+>\s*<div>\s*\(<span>(\d+)/', $bookdata, $matches2);
  314. preg_match('/<p class="sideinfo">(?:Erscheinungsdatum: (\d+\.\d+\.\d+))?<\/p>\s*<p>([^<]*)/', $bookdata, $matches3);
  315. $id = html_entity_decode(trim(urldecode($matches2[1])));
  316. $books[$id] = [
  317. 'cover' => $matches1[1],
  318. 'id' => $id,
  319. 'link' => $id,
  320. 'title' => html_entity_decode(trim($matches2[2])),
  321. 'rating' => (int) $matches2[3],
  322. 'votings' => (int) $matches2[4],
  323. 'published' => (trim($matches3[1]) !== '' ? strtotime($matches3[1] . ' 00:00:00') : 0),
  324. 'description' => html_entity_decode(trim($matches3[2])),
  325. ];
  326. }
  327. return $books;
  328. }
  329. public function getUserDetails($name) {
  330. $curl = $this->curlInstance();
  331. $curl->get('https://www.lovelybooks.de/mitglied/' . $name . '/');
  332. $this->getCookies($curl->response_headers);
  333. $this->checkResponse($curl->http_status_code);
  334. $content = $curl->response;
  335. $curl->close();
  336. $content = explode('<div id="content">', $content)[1];
  337. $content = explode('<div id="footer">', $content)[0];
  338. $user = [];
  339. preg_match('/<h2>\s+([^\s]+)\s+&nbsp;&nbsp;\s+<small class="tooltipped bottom" id="seleniumUserId">\(userID:([\d]+)\)/', $content, $matches);
  340. $user['id'] = $matches[2];
  341. $user['name'] = $matches[1];
  342. preg_match('/<strong>Mitglied seit ([\d\.]+)\D+([\d\.]+)\D+([\d\.]+)\D+([\d\.]+)\D+([\d\.]+)\D+([\d\.]+)\D+([\d\.]+)\D+([\d,]+)\D+([\d\.]+)\D+([\d\.]+)/', $content, $matches);
  343. $user['stats']['since'] = $matches[1];
  344. $user['stats']['eselsohren'] = $matches[2];
  345. $user['stats']['books'] = $matches[3];
  346. $user['stats']['wishlist'] = $matches[4];
  347. $user['stats']['reviews'] = $matches[5];
  348. $user['stats']['tags'] = $matches[6];
  349. $user['stats']['ratings'] = $matches[7];
  350. $user['stats']['ratingsAverage'] = $matches[8];
  351. $user['stats']['groups'] = $matches[9];
  352. $user['stats']['friends'] = $matches[10];
  353. preg_match('/<div class="imgbox">\s+<img src="([^"]+)"/', $content, $matches);
  354. $user['image'] = $matches[1];
  355. $iMax = preg_match_all('/<li><a href="#" data-id="(\d+)">\s+<a href="\/autor\/([^\/]+)\/[^\/]+\/" class="bookImgContainer medium"><img src="([^"]+)" alt="([^"]+)"/', $content, $matches);
  356. $user['lastadded'] = [];
  357. for ($i = 0; $i < $iMax; $i++) {
  358. $user['lastadded'][] = [
  359. 'bookid' => $matches[1][$i],
  360. 'author' => $this->decode($matches[2][$i]),
  361. 'cover' => $matches[3][$i],
  362. 'title' => $matches[4][$i],
  363. ];
  364. }
  365. $reviews = explode('<div class="review">', $content);
  366. array_shift($reviews);
  367. $user['reviews'] = [];
  368. foreach ($reviews as $review) {
  369. $data = [];
  370. preg_match('/<img src="([^"]+)"[^>]+><[^>]+>(?:\s*<[^>]+>){4}([^<]+)(?:\s*<[^>]+>){4}([^<]+)(?:\s*<[^>]+>){2}\s+<div title="(\d+)[^>]+>(?:\s*<[^>]+>){9}Rezension vom ([\d\.]+)(?:\s*<[^>]+>){3}\(<span>(\d+)/', $review, $matches);
  371. $data['cover'] = $matches[1];
  372. $data['title'] = $matches[2];
  373. $data['author'] = $matches[3];
  374. $data['rating'] = $matches[4];
  375. $data['helpful'] = $matches[5];
  376. $data['content'] = str_replace('<div class="">', '', '<div class="' . explode('</div>', explode('seven-lines', $review)[1])[0]);
  377. preg_match('/<a href="\/autor\/[^\/]+\/[^\/]+\/rezension\/(\d+)\/">(\d+) Kommentare<\/a>/', $review, $matches);
  378. $data['id'] = $matches[1];
  379. $data['commentsamount'] = $matches[2];
  380. $user['reviews'][] = $data;
  381. }
  382. $current = explode('<div class="appender"', explode(' <div class="ichlese">', $content)[1])[0];
  383. $iMax = preg_match_all('/<div class="singlebook">\s+<[^<]+<a href="(\/autor\/([^\/]+)\/[^"]+)[^>]+><img src?="([^"]+)" alt="([^"]+)"[^<]+<\/a>\s+<[^<]+<a href="\/bibliothek\/[^\/]+\/lesestatus\/(\d+)[^<]+<[^<]+<[^<]+<div class="percent" style="width:(\d+)%"><\/div>\s+<\/div>\s+<p class="pages">([^<]+)<\/p>/', $current, $matches);
  384. $books = [];
  385. for ($i = 0; $i < $iMax; $i++) {
  386. $link = urldecode($matches[1][$i]);
  387. $books[$link] = [
  388. 'id' => $link,
  389. 'link' => $link,
  390. 'author' => html_entity_decode(urldecode($matches[2][$i])),
  391. 'authorlink' => '/autor/' . urldecode($matches[2][$i]) . '/',
  392. 'cover' => $matches[3][$i],
  393. 'title' => html_entity_decode($matches[4][$i]),
  394. 'lesestatus' => $matches[5][$i],
  395. 'percent' => $matches[6][$i],
  396. 'status' => html_entity_decode($matches[7][$i]),
  397. ];
  398. }
  399. $user['current'] = array_keys($books);
  400. $user['details'] = [];
  401. $details = explode('<div class="hr">', explode('Steckbrief</h2>', $content)[1])[0];
  402. $details = explode('</tbody>', explode('<tbody>', $details)[1])[0];
  403. $details = explode('</tr>', $details);
  404. array_pop($details);
  405. foreach($details as $detail) {
  406. list($id, $value) = explode(':', strip_tags($detail), 2);
  407. $user['details'][trim($id)] = preg_replace('/\s+/', ' ', str_replace("\n", ' ', trim($value)));
  408. }
  409. $user['notes'] = [];
  410. $notes = explode('<div class="note">', explode('<div id="navigator">', $content)[0]);
  411. array_shift($notes);
  412. foreach($notes as $note) {
  413. preg_match('/<img src="([^"]+)" alt="([^"]+)[^4]+4><[^<]+<[^<]+<[^>]+>([^<]+)<\/span><\/h4>\s+<p>([^<]+)<\/p>/', $note, $matches);
  414. $user['notes'][] = [
  415. 'username' => $matches[2],
  416. 'userimage' => $matches[1],
  417. 'date' => $matches[3],
  418. 'content' => $matches[4],
  419. ];
  420. }
  421. $curl = $this->curlInstance();
  422. $curl->get('https://www.lovelybooks.de/mitglied/' . $name . '/mitglieder/freunde/');
  423. $this->getCookies($curl->response_headers);
  424. $this->checkResponse($curl->http_status_code);
  425. $content = $curl->response;
  426. $curl->close();
  427. $content = explode(' Freunde</h2>', $content)[1];
  428. $content = explode('<div class="controls">', $content)[0];
  429. $friendsData = explode('dataviewitem', $content);
  430. array_shift($friendsData);
  431. $user['friends'] = [];
  432. foreach($friendsData as $data) {
  433. preg_match('/class="userpicL" title="([^"]+)">\s*<a href="([^"]+)">\s*<div class="mask"><\/div>\s*<img src="([^"]+)/', $data, $matches);
  434. $since = explode('<', explode('seit ', $data)[1])[0];
  435. $user['friends'][] = [
  436. 'name' => $matches[1],
  437. 'link' => $matches[2],
  438. 'image' => $matches[3],
  439. 'since' => strtotime($since . ' 00:00:00'),
  440. ];
  441. }
  442. return ['books' => $books, 'user' => $user];
  443. }
  444. public function getLibraries() {
  445. $curl = $this->curlInstance();
  446. $curl->get('https://www.lovelybooks.de/bibliothek/' . $_SESSION['lb_user'] . '/alle/');
  447. $this->getCookies($curl->response_headers);
  448. $this->checkResponse($curl->http_status_code);
  449. $content = $curl->response;
  450. $curl->close();
  451. $libraries = [];
  452. $iMax = preg_match_all('/href="(\/bibliothek\/' . urldecode($_SESSION['lb_user']) . '\/[^"]+\/)"[^>]+>[^<]+<\/a>\s*<span[^>]+>\s*<strong>([^<]+)/', $content, $matches);
  453. for ($i = 0; $i < $iMax; $i++) {
  454. if(trim($matches[2][$i]) === '') {
  455. continue;
  456. }
  457. if(trim($matches[2][$i]) === '1') {
  458. continue;
  459. }
  460. if(trim($matches[1][$i]) === '') {
  461. continue;
  462. }
  463. if($matches[1][$i] === 'alle') {
  464. continue;
  465. }
  466. $libraries[$matches[1][$i]] = [
  467. 'id' => $matches[1][$i],
  468. 'name' => $matches[2][$i],
  469. 'books' => [],
  470. ];
  471. }
  472. return $libraries;
  473. }
  474. public function getLibraryPage($library, $page = 0) {
  475. if($library{0} !== '/') {
  476. $library = '/' . $library;
  477. }
  478. $curl = $this->curlInstance();
  479. $curl->get('https://www.lovelybooks.de' . urldecode($library) . '/?seite=' . urldecode($page));
  480. $this->getCookies($curl->response_headers);
  481. $this->checkResponse($curl->http_status_code);
  482. $content = $curl->response;
  483. $curl->close();
  484. $books = explode('<div class="libraryitem">', $content);
  485. array_shift($books);
  486. $books[count($books) - 1] = explode('<div id="paging">', $books[count($books) - 1])[0];
  487. $library = [
  488. 'id' => $library . '/',
  489. 'name' => '',
  490. 'books' => [],
  491. 'page' => $page,
  492. 'url' => $url,
  493. ];
  494. $bookList = [];
  495. foreach($books as $book) {
  496. $data = [];
  497. preg_match('/<div\sclass="bookcoverXM"\sdata-id="([\d]+)">/', $book, $matches);
  498. $data['id'] = $matches[1];
  499. preg_match('/href="(\/autor\/[^"]+)" class="bookImgContainer"><img\ssrc="([^"]+)"\salt="([^"]+)"/', $book, $matches);
  500. $data['id'] = urldecode($matches[1]);
  501. $data['link'] = urldecode($matches[1]);
  502. $data['cover'] = $matches[2];
  503. $data['title'] = $matches[3];
  504. preg_match('/.*?href="\/bibliothek\/' . $_SESSION['lb_user'] . '\/lesestatus\/([^"]+)\/"/', $book, $matches);
  505. $data['lesestatus'] = $matches[1];
  506. preg_match('/<a\shref="(\/autor\/[^"]+\/)">\s(.*)\s+<\/a>((?:\s.+)+?)\s+<br\s*\/>\s+<span>([^<]+)<\/span><br\s*\/>\s+<span>([^<]+)<\/span><span>,\s([\d.]+)<\/span><br\s*\/>\s*ISBN[^>]+>([\d]+)<\/span><br\s*\/>\s+<span>Genre: ([^<]+)/', $book, $matches);
  507. $data['authorlink'] = html_entity_decode(urldecode($matches[1]));
  508. $data['author'] = html_entity_decode(trim($matches[2]));
  509. $data['coauthor'] = html_entity_decode(join(' ', explode(',', join('', explode(' ', join('', explode("\n", trim(strip_tags($matches[3])))))))));
  510. $data['edition'] = html_entity_decode($matches[4]);
  511. $data['company'] = html_entity_decode(str_replace('Erschienen bei ', '', $matches[5]));
  512. $data['published'] = strtotime($matches[6] . ' ' . '00:00:00');
  513. $data['isbn'] = $matches[7];
  514. $data['genre'] = html_entity_decode($matches[8]);
  515. $data['rating'] = 0;
  516. preg_match('/<div\stitle="([0-5])\svon\s5\sSternen"/', $book, $matches);
  517. if (isset($matches[1])) {
  518. $data['rating'] = $matches[1];
  519. }
  520. $iMax = preg_match_all('/<strong class="tooltipped left hand">([A-Za-z\s,.]+):<\/strong>\s+<span class="tooltip" style="width:[\d]+px">([^<]*)<\/span>/', $book, $matches);
  521. for ($i = 0; $i < $iMax; $i++) {
  522. $data[$matches[1][$i]] = $matches[2][$i];
  523. }
  524. $bookList[$data['link']] = $data;
  525. $library['books'][] = $data['link'];
  526. }
  527. return [ 'books' => $bookList, 'library' => $library];
  528. }
  529. public function getTagPage($tag, $page = 0) {
  530. $curl = $this->curlInstance();
  531. $curl->get('https://www.lovelybooks.de/stoebern/empfehlung/' . urldecode($tag) . '/?seite=' . urldecode($page));
  532. $this->getCookies($curl->response_headers);
  533. $this->checkResponse($curl->http_status_code);
  534. $content = $curl->response;
  535. $curl->close();
  536. $books = explode('dataviewitem', explode('<div id="sidebar"', $content)[0]);
  537. array_shift($books);
  538. $tag = [
  539. 'id' => $tag,
  540. 'books' => [],
  541. ];
  542. $bookList = [];
  543. foreach($books as $book) {
  544. $data = [];
  545. preg_match('/<img src="([^"]+)[^<]+\s*<\/a>\s*<\/div>\s*<div class="bookinfo">\s*<h3><a href="([^"]+)">([^<]+)<\/a><\/h3>\s*<p class="autor">\s*<a href="([^"]+)">\s*([^<]+)/', $book, $matches);
  546. $data['id'] = $matches[2];
  547. $data['link'] = urldecode($matches[2]);
  548. $data['cover'] = urldecode($matches[1]);
  549. $data['title'] = html_entity_decode(trim($matches[3]));
  550. $data['authorlink'] = urldecode($matches[4]);
  551. $data['author'] = html_entity_decode(trim($matches[5]));
  552. $bookList[$data['link']] = $data;
  553. $tag['books'][] = $data['link'];
  554. }
  555. return [ 'books' => $bookList, 'tag' => $tag];
  556. }
  557. public function getBook($author, $title) {
  558. $book = [];
  559. $book['link'] = '/autor/' . $author . '/' . $title .'/';
  560. $book['id'] = $book['link'];
  561. $curl = $this->curlInstance();
  562. $curl->get('https://www.lovelybooks.de/autor/' . urlencode($author) . '/' . urlencode($title) .'/');
  563. $this->getCookies($curl->response_headers);
  564. $this->checkResponse($curl->http_status_code);
  565. $this->parseAjaxLinks($curl->response);
  566. $content = $curl->response;
  567. $content = explode('<div class="buchdetails', $content)[1];
  568. $content = explode('<div id="footer">', $content)[0];
  569. preg_match('/<h1>\s*<span class="autor"[^>]+>\s*<a href="([^"]+)">\s*([^<]+)<\/a>\s*<\/span>\s*<span[^>]+>([^<]+)/', $content, $matches);
  570. $book['title'] = html_entity_decode($matches[3]);
  571. $book['authorlink'] = html_entity_decode($matches[1]);
  572. $book['author'] = html_entity_decode($matches[2]);
  573. preg_match('/itemprop="description">([^<]*)/', $content, $matches);
  574. $book['description'] = html_entity_decode($matches[1]);
  575. preg_match('/<div class="bookcoverXXL">\s+<div>\s+<img src="([^"]+)/', $content, $matches);
  576. $book['cover'] = $matches[1];
  577. $book['actions'] = [];
  578. $actions = explode('</a>', explode('<ul class="social">', explode('<ul class="buchoptions">', $content)[1])[0]);
  579. array_pop($actions);
  580. foreach($actions as $action) {
  581. $label = array_pop(explode('>', $action));
  582. if($label === 'Buchversand Stein' || $label === 'Amazon' || $label === 'Lieblingsbuchhandlung') {
  583. continue;
  584. }
  585. $book['actions'][] = strip_tags('<a' . array_pop(explode('<a', $action)));
  586. }
  587. $comments = explode('<div class="cellcontent">', explode('<span class="zurueck">', $content)[0]);
  588. array_shift($comments);
  589. $book['comments'] = [];
  590. foreach($comments as $comment) {
  591. $comment = trim(strip_tags($comment, '<p><img>'));
  592. $book['comments'][] = [
  593. 'comment' => html_entity_decode(strip_tags(explode('</p>', $comment)[0])),
  594. 'username' => html_entity_decode(str_replace('— ', '', trim(explode('<img', explode('</p>', $comment)[1])[0]))),
  595. 'userimage' => explode('"', explode('<img src="', explode('</p>', $comment)[1])[1])[0]
  596. ];
  597. }
  598. $additions = explode('<div class="article', explode('<li class="display-more"', explode('<ul class="appending-list">', $content)[1])[0]);
  599. array_shift($additions);
  600. $book['additions'] = [];
  601. foreach($additions as $addition) {
  602. preg_match('/<h3><a href="(\/autor\/[^\/]+\/[^\/]+\/([^\/]+)[^"]*)"><span>([^<]+)/', $addition, $matches);
  603. $data = [
  604. 'link' => $matches[1],
  605. 'type' => trim(explode('"', $addition)[0]),
  606. 'title' => html_entity_decode($matches[3])
  607. ];
  608. preg_match('/<\/div>\s*<img src="([^"]+)" alt="([^"]+)/', $addition, $matches);
  609. $data['username'] = $matches[2];
  610. $data['userimage'] = $matches[1];
  611. $data['rating'] = 0;
  612. if(strpos($addition, '"ratingstars r') > 0) {
  613. $data['rating'] = explode('0" id="id', explode('"ratingstars r', $addition)[1])[0];
  614. }
  615. preg_match('/"timeago" title="([^"]+)/', $addition, $matches);
  616. $data['published'] = strtotime($matches[1]);
  617. preg_match('/<div class="text preview"[^<]+<p id="id[^>]+>([^<]+)</', $addition, $matches);
  618. $data['content'] = $matches[1];
  619. preg_match('/id="(id[^"]+)" href=/', $addition, $matches);
  620. if(isset($matches[1])) {
  621. $data['content'] = $this->fetchAjaxContent($matches[1]);
  622. $data['content'] = str_replace(
  623. ['<strong></strong>', '<em></em>', '"> <p>', '"><p>', '</p></p>'],
  624. ['', '', '">', '">', '</p>'],
  625. explode('<a class="mehr"',
  626. explode('class="text complete"', $data['content'])[1]
  627. )[0]
  628. );
  629. $data['content'] = '<div ' . trim($data['content']) . '</div>';
  630. $data['content'] = strip_tags($data['content'], '<p><br><em><i><u><strong>');
  631. }
  632. $book['additions'][] = $data;
  633. }
  634. $tags = explode('</li><li>', explode('</div>', explode('<div class="tagcloud"', $content)[1])[0]);
  635. $book['tags'] = [];
  636. $book['tags'][] = trim(html_entity_decode(strip_tags('<br ' . array_shift($tags))));
  637. foreach($tags as $tag) {
  638. $book['tags'][] = trim(html_entity_decode(strip_tags($tag)));
  639. }
  640. $info = explode('<div class="shops">', explode('<div class="buchinfo">', $content)[1])[0];
  641. $iMax = preg_match_all('/<li>\s*<b>\s*([^:<]+):*<\/b>\s*([^<]+)/', $info, $matches);
  642. $book['facts'] = [];
  643. for ($i = 0; $i < $iMax; $i++) {
  644. $id = trim($matches[1][$i]);
  645. $value = preg_replace('/\s+/', ' ', $matches[2][$i]);
  646. if($id === 'Verlag') {
  647. $book['company'] = trim(html_entity_decode($value));
  648. continue;
  649. }
  650. if($id === 'Aktuelle Ausgabe') {
  651. $book['published'] = strtotime($value);
  652. continue;
  653. }
  654. if($id === 'ISBN') {
  655. $book['isbn'] = $value;
  656. continue;
  657. }
  658. $book['facts'][trim($matches[1][$i])] = $value;
  659. }
  660. $iMax = preg_match_all('/<li id="[^"]+">\s*<b>\s*([^<:]+):*\s*<\/b>\s*([^<]+)/', $info, $matches);
  661. for ($i = 0; $i < $iMax; $i++) {
  662. $id = trim($matches[1][$i]);
  663. $value = preg_replace('/\s+/', ' ', $matches[2][$i]);
  664. $book['facts'][trim($matches[1][$i])] = $value;
  665. }
  666. $genre = explode('>', explode('</a>', explode('chlid="genreLink"', $info)[1])[0]);
  667. $genre = array_pop($genre);
  668. $book['genre'] = trim($genre);
  669. $editions = explode('<div class="ausgaben"', $info)[1];
  670. $iMax = preg_match_all('/<a href="(\/autor\/[^"]+)">\s*<img src="([^"]+)" alt="([^"]+)/', $editions, $matches);
  671. $book['editions'] = [];
  672. for ($i = 0; $i < $iMax; $i++) {
  673. $book['editions'][] = [
  674. 'link' => urldecode($matches[1][$i]),
  675. 'cover' => $matches[2][$i],
  676. 'title' => html_entity_decode($matches[3][$i])
  677. ];
  678. }
  679. return $book;
  680. }
  681. public function getBookStatus($book) {
  682. $status = [];
  683. $status['id'] = $book;
  684. $curl = $this->curlInstance();
  685. $curl->get('https://www.lovelybooks.de/bibliothek/' . $_SESSION['lb_user'] . '/lesestatus/' . $book .'/');
  686. $this->getCookies($curl->response_headers);
  687. $this->checkResponse($curl->http_status_code);
  688. $content = $curl->response;
  689. $curl->close();
  690. preg_match('/<h1>\s*<span\s+class="autor">\s*<a\s+href="(\/autor\/[^\/]+\/)">\s*(.*)\s*<\/a>\s*<\/span>\s*<span>\s*(.*)\s*<\/span>\s*<\/h1>/', $content, $matches);
  691. $status['authorlink'] = urldecode($matches[1]);
  692. $status['author'] = html_entity_decode($matches[2]);
  693. $status['title'] = html_entity_decode($matches[3]);
  694. preg_match('/<div\s+class="progress">\s*<span\s+class="user">([^<]+)<\/span>\s*<span[^>]+>[^<]+<span\s+class="page">([\d]+)<\/span>/', $content, $matches);
  695. $status['user'] = $matches[1];
  696. $status['page'] = $matches[2];
  697. preg_match('/<div\s+class="bookcoverXXL"\s+data-id="([\d]+)">\s*<a\s+href="(\/autor\/[^"]+)"[^>]+><img\s+src="([^"]+)/', $content, $matches);
  698. $status['bookid'] = $matches[1];
  699. $status['booklink'] = urldecode($matches[2]);
  700. $status['cover'] = $matches[3];
  701. $comments = explode('<div class="singleEntry ', explode('<div id="footer">', explode('<div class="entriesBox"', $content)[1])[0]);
  702. array_shift($comments);
  703. $status['comments'] = [];
  704. foreach($comments as $comment) {
  705. $data = [];
  706. preg_match('/class="timeago"\s+title="([^"]+)">[^>]+>\s*<div class="progressStatus">\s*(?:<div class="progressbar">\s*<div class="percent" style="width:\s*(\d+)%">[^>]+>\s*[^>]+>\s*)?<p class="pages">(?:Seite (\d+) \/ (\d+)|(Lese gerade nicht|Schon gelesen))<\/p>/', $comment, $matches);
  707. if(count($matches) < 2) {
  708. continue;
  709. }
  710. $data['created'] = strtotime($matches[1]);
  711. $data['created_orig'] = $matches[1];
  712. if($data['created'] == false) {
  713. $data['created'] = strtotime($matches[1].' 00:00:00');
  714. }
  715. $data['percent'] = (int) $matches[2];
  716. $data['pageCurrent'] = (int) $matches[3];
  717. $data['pageTotal'] = (int) $matches[4];
  718. $status['pageTotal'] = (int) $matches[4];
  719. $data['comment'] = html_entity_decode(trim($matches[5]));
  720. $data['defaultComment'] = '';
  721. preg_match('/<div class="plaincomment">\s+<p class="defaultText">([^<]+)<\/p>\s+<div class="contentWrap">([^<]*)<\/div>/', $comment, $matches);
  722. if(count($matches) > 0) {
  723. $data['defaultComment'] = html_entity_decode(trim($matches[1]));
  724. $matches[2] = trim($matches[2]);
  725. if ($matches[2] !== '') {
  726. $data['comment'] = html_entity_decode($matches[2]);
  727. }
  728. }
  729. $status['comments'][] = $data;
  730. }
  731. preg_match('/<div class="beginEntry">\s+<span class="timeago" title="([^"]+)">/', $comment, $matches);
  732. $status['comments'][] = [
  733. 'created' => strtotime($matches[1]),
  734. 'pageCurrent' => 0,
  735. 'percent' => 0,
  736. 'pageTotal' => $status['pageTotal'],
  737. 'comment' => 'zur Bibliothek hinzugefügt'
  738. ];
  739. return $status;
  740. }
  741. public function updateBookStatus($book, $page, $comment = '', $status = '') {
  742. $curl = $this->curlInstance();
  743. $curl->get('https://www.lovelybooks.de/bibliothek/' . $_SESSION['lb_user'] . '/lesestatus/' . $book .'/');
  744. $this->getCookies($curl->response_headers);
  745. $this->checkResponse($curl->http_status_code);
  746. $content = $curl->response;
  747. $curl->close();
  748. // fetch the form-popup for updating book-status
  749. preg_match('/Wicket\.Ajax\.ajax\({"u":"(\/bibliothek\/' . $_SESSION['lb_user'] . '\/lesestatus\/' . $book . '\/\?[\d]+-[\d]+\.IBehaviorListener\.0-updateReadingStateLink)","e":"click","c":"([^"]+)"\}\)/', $content, $matches);
  750. $curl = $this->curlInstance();
  751. $curl->setHeader('Accept', 'application/xml, text/xml, */*; q=0.01');
  752. $curl->setHeader('Wicket-Ajax', 'true');
  753. $curl->setHeader('Wicket-Ajax-BaseURL', '.');
  754. $curl->setHeader('Wicket-FocusedElementId', $matches[2]);
  755. $curl->setHeader('X-Requested-With', 'XMLHttpRequest');
  756. $curl->get('https://www.lovelybooks.de' . $matches[1]);
  757. $this->getCookies($curl->response_headers);
  758. $this->checkResponse($curl->http_status_code);
  759. $content = $curl->response;
  760. $curl->close();
  761. preg_match('/Wicket\.Ajax\.ajax\({"f":"([^"]+)","u":"(\/bibliothek\/' . $_SESSION['lb_user'] . '\/lesestatus\/' . $book . '\/\?[\d]+-[\d]+\.IBehaviorListener\.1-dialogContainer-form-submitButton)","e":"click","c":"([^"]+)",/', $content, $matches);
  762. $id = $matches[1].'_hf_0';
  763. $url = $matches[2];
  764. $submitId = $matches[3];
  765. $baseUrl = '/bibliothek/' . $_SESSION['lb_user'] . '/lesestatus/' . $book . '/';
  766. preg_match_all('/<input\s+type="radio"\s+id="[^"]+"\s+name="readingStatePanel:radioGroupContainer:radioGroupReadingState"\s+value="([^"]+)"\s+(checked="checked"\s+)?class="[^"]+">\s*<label for="[^"]+">([^<]+)<\/label>/', $content, $matches);
  767. $options = [];
  768. $iMax = count($matches[0]);
  769. for ($i = 0; $i < $iMax; $i++) {
  770. $options[$matches[3][$i]] = $matches[1][$i];
  771. if(trim($matches[2][$i]) !== '') {
  772. $readingState = $matches[1][$i];
  773. }
  774. }
  775. if (isset($options[$status])) {
  776. $readingState = $options[$status];
  777. }
  778. preg_match('/<input\s+type="text"\s+value="([\d]+)"(?:\s+[^=]+="[^"]+")*\sname="readingStatePanel:containerReading:containerFormat:textField2"/', $content, $matches);
  779. $pagesTotal = $matches[1];
  780. $data = [
  781. $id => '',
  782. 'readingStatePanel:radioGroupContainer:radioGroupReadingState' => $readingState,
  783. 'readingStatePanel:containerReading:containerFormat:textField1' => $page,
  784. 'readingStatePanel:containerReading:containerFormat:textField2' => $pagesTotal,
  785. 'readingStatePanel:containerReading:textArea' => $comment,
  786. 'submitButton' => '1'
  787. ];
  788. $curl = $this->curlInstance();
  789. $curl->setHeader('Accept', 'application/xml, text/xml, */*; q=0.01');
  790. $curl->setHeader('Wicket-Ajax', 'true');
  791. $curl->setHeader('Wicket-Ajax-BaseURL', $baseUrl);
  792. $curl->setHeader('Wicket-FocusedElementId', $submitId);
  793. $curl->setHeader('X-Requested-With', 'XMLHttpRequest');
  794. $curl->post('https://www.lovelybooks.de' . $url, $data);
  795. $this->getCookies($curl->response_headers);
  796. $this->checkResponse($curl->http_status_code);
  797. $content = $curl->response;
  798. $curl->close();
  799. }
  800. public function getCurrentBooks() {
  801. return $this->getLibraryPage('wirdgeradegelesen');
  802. }
  803. public function getMessagePage($folder = 'inbox', $amount = 20, $page = 0) {
  804. $messages = [];
  805. $folders = [
  806. 'inbox' => 'posteingang',
  807. 'outbox' => 'postausgang',
  808. ];
  809. $curl = $this->curlInstance();
  810. $curl->get('https://www.lovelybooks.de/nachrichten/' . $folders[$folder] . '/?anzahl=' . $amount . '&seite=' . $page);
  811. $this->getCookies($curl->response_headers);
  812. $this->checkResponse($curl->http_status_code);
  813. $this->parseAjaxLinks($curl->response);
  814. $content = $curl->response;
  815. $curl->close();
  816. $content = explode('<div id="content">', $content)[1];
  817. $content = explode('<div id="footer">', $content)[0];
  818. $content = explode('<table width="100%">', $content)[1];
  819. $content = explode('</table>', $content)[0];
  820. $messagesData = explode('><input type="checkbox" name="group" ', $content);
  821. array_shift($messagesData);
  822. foreach($messagesData as $message) {
  823. preg_match('/src="([^"]+)" alt="([^"]+)/', $message, $matches1);
  824. preg_match('/>am ([^\s]+) um ([^\s]+) Uhr<[^<]+<[^<]+<[^<]+<a href="[^"]+" id="([^"]+)"( style="[^"]+")?><[^>]+>([^<]+)/', $message, $matches2);
  825. $data = [
  826. 'folder' => $folder,
  827. 'sendername' => html_entity_decode($matches1[2]),
  828. 'senderimage' => $matches1[1],
  829. 'date' => strtotime($matches2[1] . ' ' . $matches2[2] . ':00'),
  830. 'subject' => html_entity_decode(trim($matches2[5])),
  831. 'read' => trim($matches2[4]) === ''
  832. ];
  833. $data['description'] = html_entity_decode($this->fetchAjaxContent($matches2[3]));
  834. $data['description'] = trim(explode('</p></div>', explode('<div class="wiki2html"><p>', $data['description'])[1])[0]);
  835. $data['id'] = $data['date'] * 1000;
  836. $counter = 100;
  837. while(isset($messages[(string) $data['id']]) && $counter > 0) {
  838. $counter--;
  839. $data['id'] = $data['id'] + 1;
  840. }
  841. $messages[(string) $data['id']] = $data;
  842. }
  843. return $messages;
  844. }
  845. public function createMessage($receipients, $subject, $message, $attachments = []) {
  846. $messages = [];
  847. $curl = $this->curlInstance();
  848. $curl->get('https://www.lovelybooks.de/nachrichten/');
  849. $this->getCookies($curl->response_headers);
  850. $this->checkResponse($curl->http_status_code);
  851. $this->parseAjaxLinks($curl->response);
  852. $content = $curl->response;
  853. $curl->close();
  854. preg_match('/id="([^"]+)">Nachricht erstellen/', $content, $matches);
  855. $content = $this->fetchAjaxContent($matches[1]);
  856. $this->parseAjaxLinks($content);
  857. preg_match('/<input\s+type="hidden"\s+name="([^"]+)"\s+id="[^"]+"\s*\/>/', $content, $matches);
  858. $submitId = $matches[1];
  859. preg_match('/button name="okButton" id="([^"]+)"><span>Nachricht senden/', $content, $matches);
  860. $action = $this->ajax[$matches[1]];
  861. $data = [
  862. 'recipientsInput' => join(',', (array) $receipients),
  863. 'subjectInput' => $subject,
  864. 'dialogFormPreview:messageText' => $message,
  865. 'okButton' => 1,
  866. $submitId => ''
  867. ];
  868. $curl = $this->curlInstance();
  869. $curl->verbose(true);
  870. $curl->post('https://www.lovelybooks.de' . $action, $data);
  871. $this->getCookies($curl->response_headers);
  872. $this->checkResponse($curl->http_status_code);
  873. $content = $curl->response;
  874. $curl->close();
  875. return [
  876. 'https://www.lovelybooks.de' . $action, $data
  877. ];
  878. }
  879. public function getRaffles() {
  880. $raffles = [];
  881. $curl = $this->curlInstance();
  882. $curl->get('https://www.lovelybooks.de/leserunden-und-buchverlosungen/');
  883. $this->getCookies($curl->response_headers);
  884. $this->checkResponse($curl->http_status_code);
  885. $content = $curl->response;
  886. $curl->close();
  887. $rafflesData = explode('<div class="slider">', $content)[1];
  888. $rafflesData = explode('<div class="contentblock">', $rafflesData)[0];
  889. $rafflesData = explode('<div class="singlebook"', $rafflesData);
  890. array_shift($rafflesData);
  891. $books = [];
  892. foreach($rafflesData as $raffle) {
  893. if(preg_match('/href="(\/autor\/[^\/]+\/[^\/]+\/leserunde\/(\d+)\/)">\s+<img src="([^"]+)" alt="([^"]+)[^<]+(?:<[^<]+){5}<span>(\d+)(?:<[^<]+){5}<a[^>]+>([^<]+)(?:<[^<]+){3}<a[^>]+>([^<]+)(?:<[^<]+){5}<[^>]+>([^<]+)/', $raffle, $matches)) {
  894. $book = [
  895. 'title' => html_entity_decode(trim($matches[4])),
  896. 'author' => html_entity_decode(trim($matches[6])),
  897. 'leserunde' => $matches[2],
  898. 'cover' => urldecode($matches[3]),
  899. ];
  900. $linkparts = explode('/', urldecode($matches[1]));
  901. array_pop($linkparts);
  902. array_pop($linkparts);
  903. array_pop($linkparts);
  904. $book['link'] = join('/', $linkparts) . '/';
  905. $book['id'] = join('/', $linkparts) . '/';
  906. array_pop($linkparts);
  907. $book['authorlink'] = join('/', $linkparts) . '/';
  908. $books[$book['link']] = $book;
  909. $date = str_replace([
  910. ' Januar ', ' Februar ', ' März ', ' April ', ' Mai ', ' Juni ', ' Juli ', ' August ', ' September ', ' Oktober ', ' November ', ' Dezember ', ' um '
  911. ], [
  912. '01.', '02.', '03.', '04.', '05.', '06.', '07.', '08.', '09.', '10.', '11.', '12.', ' '
  913. ],
  914. html_entity_decode(trim($matches[8]))
  915. );
  916. $raffles[] = [
  917. 'link' => urldecode($matches[1]),
  918. 'id' => (int) $matches[2],
  919. 'book' => $book['link'],
  920. 'amount' => (int) $matches[5],
  921. 'date' => strtotime($date . ':59'),
  922. ];
  923. }
  924. }
  925. return [
  926. 'raffles' => $raffles,
  927. 'books' => $books
  928. ];
  929. }
  930. public function getGroups() {
  931. $groups = [];
  932. $curl = $this->curlInstance();
  933. $curl->get('https://www.lovelybooks.de/mitglied/' . $_SESSION['lb_user'] . '/gruppen/');
  934. $this->getCookies($curl->response_headers);
  935. $this->checkResponse($curl->http_status_code);
  936. $content = $curl->response;
  937. $curl->close();
  938. $content = explode('<div class="controls">', explode('<div class="tab-box">', $content)[1])[0];
  939. $content = explode('dataviewitem', $content);
  940. array_shift($content);
  941. foreach($content as $part) {
  942. $group = [];
  943. preg_match('/<a href="(\/gruppe\/[^"]+)">\s*<img.*?src="([^"]+)" alt="([^"]+)"/', $part, $matches);
  944. $group['link'] = $matches[1];
  945. $group['id'] = $matches[1];
  946. $group['cover'] = $matches[2];
  947. $group['title'] = html_entity_decode(trim($matches[3]));
  948. preg_match('/<p>\s*erstellt am ([\d\.]+)\D+([\d\.]+)\D+([\d\.]+)\D+([\d\.]+)\D+([\d:]+)+/', $part, $matches);
  949. $group['created'] = strtotime($matches[1] . ' 00:00:00');
  950. $group['members'] = (int) $matches[2];
  951. $group['posts'] = (int) $matches[3];
  952. $group['lastupdated'] = strtotime($matches[4] . ' ' . $matches[5]);
  953. $group['description'] = strip_tags(html_entity_decode(explode('</div>', '<div' . explode('expandable five-lines"', $part)[1])[0]), '<p><br><em><strong>');
  954. $groups[$group['id']] = $group;
  955. }
  956. return $groups;
  957. }
  958. public function getGroup($id, $name) {
  959. $group = [];
  960. $curl = $this->curlInstance();
  961. $curl->get('https://www.lovelybooks.de/gruppe/' . $id . '/' . $name . '/');
  962. $this->getCookies($curl->response_headers);
  963. $this->checkResponse($curl->http_status_code);
  964. $content = $curl->response;
  965. $curl->close();
  966. $group['link'] = '/gruppe/' . $id . '/' . $name . '/';
  967. $content = explode('<div id="footer">', explode('<div id="content">', $content)[1])[0];
  968. $parts = explode('<div class="tab-panel">', $content);
  969. $details = $parts[0];
  970. $parts = explode('<div id="marginal">', $parts[1]);
  971. $overview = $parts[0];
  972. $marginal = $parts[1];
  973. preg_match('/<img.*?src="([^"]+)[^“]+“([^”]+)[^(]+\(groupID:(\d+)\)(?:<[^>]+>\s*){2}[^<]+(?:<[^>]+>\s*){2,3}(.*?)<\/div>/', $details, $matches);
  974. $group['cover'] = $matches[1];
  975. $group['title'] = $matches[2];
  976. $group['id'] = (int) $matches[3];
  977. $group['description'] = $matches[4];
  978. preg_match('/<span>Erstellt von<[^<]+\s*<[^>]+>([^<]+)\s*<[^>]+>\s*am\s*([\d\.]+)(?:\s*<[^<]+){3}<[^>]+>\s*(\d+) Mitglieder<[^>]+>\s*(\d+) Themen<[^>]+>\s*(\d+) Beiträge<[^>]+>/', $marginal, $matches);
  979. $group['creator'] = $matches[1];
  980. $group['created'] = strtotime($matches[2] . ' 00:00:00');
  981. $group['members'] = (int) $matches[3];
  982. $group['articles'] = (int) $matches[4];
  983. $group['posts'] = (int) $matches[5];
  984. $notes = explode('<div class="note">', $marginal);
  985. array_shift($notes);
  986. $group['notes'] = [];
  987. foreach($notes as $note) {
  988. preg_match('/<img src="([^"]+)" alt="([^"]+)"[^<]+(?:\s*<[^<]+>){2,5}<a href="[^"]+" name="kommentar_([^"]+)">[^<]+<[^>]+>\s*<[^>]+>([^<]+)(?:\s*<[^>]+>){2,3}([^<]*)/', $note, $matches);
  989. $group['notes'][] = [
  990. 'userimage' => $matches[1],
  991. 'username' => $matches[2],
  992. 'id' => (int) $matches[3],
  993. 'ago' => $matches[4],
  994. 'content' => $matches[5],
  995. ];
  996. }
  997. $parts = explode('</h2>', $overview);
  998. array_shift($parts);
  999. $group['posts'] = [];
  1000. $group['posts']['newest'] = [];
  1001. $entries = explode('<h3>', $parts[0]);
  1002. array_shift($entries);
  1003. foreach($entries as $entry) {
  1004. preg_match('/<a href="([^"]+)">(?:<strong>)?([^<]+)(?:\s*<[^<]+){2,4}<a[^>]+>([^<]+)<\/a>\s*<span>\s*([\d\.:,\s]+)Uhr/', $entry, $matches);
  1005. $group['posts']['newest'][] = [
  1006. 'link' => $matches[1],
  1007. 'title' => $matches[2],
  1008. 'user' => $matches[3],
  1009. 'updated' => strtotime(str_replace(',', '', $matches[4])),
  1010. ];
  1011. }
  1012. $group['posts']['hottest'] = [];
  1013. $entries = explode('<h3>', $parts[1]);
  1014. array_shift($entries);
  1015. foreach($entries as $entry) {
  1016. preg_match('/<a href="([^"]+)">(?:<strong>)?([^<]+)(?:\s*<[^<]+){2,4}<a[^>]+>([^<]+)<\/a>\s*<span>\s*([\d\.:,\s]+)Uhr/', $entry, $matches);
  1017. $group['posts']['hottest'][] = [
  1018. 'link' => $matches[1],
  1019. 'title' => $matches[2],
  1020. 'user' => $matches[3],
  1021. 'updated' => strtotime(str_replace(',', '', $matches[4])),
  1022. ];
  1023. }
  1024. $group['users'] = [];
  1025. $entries = explode('class="userpicL"', $parts[2]);
  1026. array_shift($entries);
  1027. $group['users'] = [];
  1028. foreach($entries as $entry) {
  1029. preg_match('/<img src="([^"]+)" alt="([^"]+)"[^<]+(?:\s*<[^<]+){1,8}<div>(\d+) Beiträge/', $entry, $matches);
  1030. $group['users'][] = [
  1031. 'username' => $matches[2],
  1032. 'userimage' => $matches[1],
  1033. 'posts' => (int) $matches[3],
  1034. ];
  1035. }
  1036. $books = [];
  1037. $entries = explode('<a href="', $parts[3]);
  1038. array_shift($entries);
  1039. foreach($entries as $entry) {
  1040. $book = [];
  1041. preg_match('/(\/autor\/[^\/]+)(\/[^"])+)"[^<]+<[^<]+<[^\/]+([^"]+)[^<]+(?:<[^<]+){2,5}<strong>([^<]+)(?:<[^<]+){2}<span>([^<]+)(?:<[^<]+){2}<p title="\d von 5 Sternen/', $entry, $matches);
  1042. if(!$matches[1]) {
  1043. continue;
  1044. }
  1045. $book['authorlink'] = $matches[1];
  1046. $book['id'] = $matches[1] . '/' . $matches[2];
  1047. $book['link'] = $matches[1] . '/' . $matches[2];
  1048. $book['cover'] = 'http:' . $matches[3];
  1049. $book['title'] = $matches[4];
  1050. $book['author'] = $matches[5];
  1051. preg_match('/<p>(\d+)&nbsp;Bibliotheken, (\d+)&nbsp;Leser, (\d+)&nbsp;Gruppen, (\d+)&nbsp;Rezension<\/p>\s*<div>\s*([^<]+)/', $entry, $matches);
  1052. $book['stats'] = [
  1053. 'libraries' => $matches[1],
  1054. 'users' => $matches[2],
  1055. 'groups' => $matches[3],
  1056. 'reviews' => $matches[4],
  1057. 'tags' => explode(',', str_replace(' ', '', trim($matches[5])))
  1058. ];
  1059. $books[$book['link']] = $book;
  1060. }
  1061. $group['books'] = array_keys($books);
  1062. return ['group' => $group, 'books' => $books];
  1063. }
  1064. public function getGenres() {
  1065. $genres = [];
  1066. $curl = $this->curlInstance();
  1067. $curl->get('https://www.lovelybooks.de/buecher//');
  1068. $this->getCookies($curl->response_headers);
  1069. $this->checkResponse($curl->http_status_code);
  1070. $content = $curl->response;
  1071. $curl->close();
  1072. $data = explode('col990 buehne', explode('<div id="footer">', $content)[0]);
  1073. array_shift($data);
  1074. $books = [];
  1075. foreach($data as $content) {
  1076. preg_match('/<h2>\s*<i[^<]+<\/i>\s*<span>([^<]+)<\/span>\s*<a href="([^"]+)">\s*([^<]+)/', $content, $matches);
  1077. $genre = [
  1078. 'title' => $matches[3],
  1079. 'subtitle' => $matches[1],
  1080. 'link' => $matches[2],
  1081. 'books' => [],
  1082. ];
  1083. $bookData = explode('<div class="bookcover', $content);
  1084. foreach($bookData as $entry) {
  1085. if(!preg_match('/<a href="\/autor\/([^\/]+)\/([^\/]+)\/[^<]+<img src="([^"]+)" alt="([^"]+)/', $entry, $matches)) {
  1086. continue;
  1087. }
  1088. $book = [
  1089. 'title' => html_entity_decode(trim($matches[4])),
  1090. 'cover' => urldecode($matches[3]),
  1091. 'id' => '/autor/' . urldecode($matches[1]) . '/' . urldecode($matches[2]) . '/',
  1092. 'link' => '/autor/' . urldecode($matches[1]) . '/' . urldecode($matches[2]) . '/',
  1093. 'author' => html_entity_decode(urldecode($matches[1])),
  1094. 'authorlink' => '/autor/' . urldecode($matches[1]) . '/',
  1095. ];
  1096. $genre['books'][] = $book['link'];
  1097. $books[$book['link']] = $book;
  1098. }
  1099. $genres[$genre['link']] = $genre;
  1100. }
  1101. return [
  1102. 'books' => $books,
  1103. 'genres' => $genres,
  1104. ];
  1105. }
  1106. public function getGenre($link) {
  1107. $genres = [];
  1108. $curl = $this->curlInstance();
  1109. $curl->get('https://www.lovelybooks.de' . $link);
  1110. $this->getCookies($curl->response_headers);
  1111. $this->checkResponse($curl->http_status_code);
  1112. $content = $curl->response;
  1113. $curl->close();
  1114. $data = explode('col990 buehne', explode('<div id="footer">', $content)[0]);
  1115. array_shift($data);
  1116. $books = [];
  1117. foreach($data as $content) {
  1118. preg_match('/<h2>\s*<i[^<]+<\/i>\s*<span>([^<]+)<\/span>\s*<a href="([^"]+)">\s*([^<]+)/', $content, $matches);
  1119. $genre = [
  1120. 'title' => $matches[3],
  1121. 'subtitle' => $matches[1],
  1122. 'link' => $matches[2],
  1123. 'books' => [],
  1124. ];
  1125. $bookData = explode('<div class="bookcoverXM>', $content);
  1126. array_shift($bookData);
  1127. foreach($bookData as $entry) {
  1128. if(!preg_match('/<a href="\/autor\/([^\/]+)\/([^\/]+)\/[^<]+<img src="([^"]+)" alt="([^"]+)/', $entry, $matches));
  1129. $book = [
  1130. 'title' => html_entity_decode(trim($matches[4])),
  1131. 'cover' => urldecode($matches[3]),
  1132. 'id' => '/autor/' . urldecode($matches[1]) . '/' . urldecode($matches[2]) . '/',
  1133. 'link' => '/autor/' . urldecode($matches[1]) . '/' . urldecode($matches[2]) . '/',
  1134. 'author' => html_entity_decode(urldecode($matches[1])),
  1135. 'authorlink' => '/autor/' . urldecode($matches[1]) . '/',
  1136. ];
  1137. $genre['books'][] = $book['link'];
  1138. $books[$book['link']] = $book;
  1139. }
  1140. $genres[$genre['link']] = $genre;
  1141. }
  1142. return [
  1143. 'books' => $books,
  1144. 'genres' => $genres,
  1145. ];
  1146. }
  1147. public function getForum($url) {
  1148. $forum = [];
  1149. $threads = [];
  1150. $books = [];
  1151. $curl = $this->curlInstance();
  1152. $curl->get('https://www.lovelybooks.de' . $url);
  1153. $this->getCookies($curl->response_headers);
  1154. $this->checkResponse($curl->http_status_code);
  1155. $content = $curl->response;
  1156. $curl->close();
  1157. $forum['title'] = html_entity_decode(explode('</h1>', explode('<h1>', $content)[1])[0]);
  1158. $forum['id'] = urldecode($url);
  1159. $forum['link'] = urldecode($url);
  1160. $forum['follower'] = [];
  1161. $threadList = explode('<div class="aktivitaet">', explode('<div class="unterthemen"', $content)[1])[0];
  1162. $threadList = explode(' href="', $threadList);
  1163. array_shift($threadList);
  1164. array_shift($threadList);
  1165. foreach($threadList as $threadData) {
  1166. $thread = [
  1167. 'id' => str_replace('?tag=', '/?tag=', explode('"', $threadData)[0]),
  1168. 'link' => str_replace('?tag=', '/?tag=', explode('"', $threadData)[0]),
  1169. ];
  1170. $parts = explode('(', strip_tags('<a href="' . $threadData .'></a>'));
  1171. $thread['count'] = explode(')', array_pop($parts))[0];
  1172. $thread['title'] = join('(', $parts);
  1173. $thread['posts'] = [];
  1174. $threads[$thread['id']] = $thread;
  1175. }
  1176. $forum['threads'] = array_keys($threads);
  1177. $content = explode('<div id="sidebar"', explode('>Weitere</', $content)[0])[1];
  1178. $follower = explode('followerDataView', $content);
  1179. array_shift($follower);
  1180. foreach($follower as $data) {
  1181. $forum['follower'][] = [
  1182. 'image' => explode('"', explode('src="', $data)[1])[0],
  1183. 'link' => explode('"', explode('href="', $data)[1])[0],
  1184. 'name' => explode('"', explode('alt="', $data)[1])[0],
  1185. ];
  1186. }
  1187. return [
  1188. 'forum' => $forum,
  1189. 'threads' => $threads,
  1190. 'books' => $books,
  1191. ];
  1192. }
  1193. public function getThread($url) {
  1194. $t