public static function parse($contents) { $crawler = new Crawler(); $crawler->addHTMLContent($contents, 'UTF-8'); $rows = $crawler->filter('div[class="borderClass"]'); $result = array(); foreach ($rows as $historyItem) { $crawler = new Crawler($historyItem); $anime = new Anime(); $anime->setId(str_replace('#raArea1', '', $crawler->filter('a')->attr('id'))); $anime->setImageUrl(preg_replace('/r(.+?)\\/(.+?)\\?(.+?)$/', '$2', $crawler->filter('img')->attr('data-src'))); $anime->setTitle($crawler->filter('strong')->text()); $resultItem['item'] = $anime; $resultItem['recommendations'] = self::parseInformation($crawler); $result[] = $resultItem; } return $result; }
public static function parseExtendedPersonal($contents, Anime $anime) { $crawler = new Crawler(); $crawler->addHTMLContent($contents, 'UTF-8'); #Personal tags #<td class="borderClass"><textarea name="tags" rows="2" id="tagtext" cols="45" class="textarea">action, sci-fi</textarea></td> $personalTags = $crawler->filter('textarea[id="add_anime_tags"]')->text(); if (strlen($personalTags) > 0) { $personalTags = explode(',', $personalTags); foreach ($personalTags as $tag) { $tagArray[] = trim($tag); } $anime->setPersonalTags($tagArray); } #Start and Finish Dates #<tr> # <td class="borderClass">Start Date</td> # <td class="borderClass"> # Month: # <select name="startMonth" class="inputtext"> # <option value="00"> # <option value="1" >Jan<option value="2" selected>Feb<option value="3" >Mar<option value="4" >Apr<option value="5" >May<option value="6" >Jun<option value="7" >Jul<option value="8" >Aug<option value="9" >Sep<option value="10" >Oct<option value="11" >Nov<option value="12" >Dec </select> # Day: # <select name="startDay" class="inputtext"> # <option value="00"> # <option value="1" >1<option value="2" selected>2<option value="3" >3<option value="4" >4<option value="5" >5<option value="6" >6<option value="7" >7<option value="8" >8<option value="9" >9<option value="10" >10<option value="11" >11<option value="12" >12<option value="13" >13<option value="14" >14<option value="15" >15<option value="16" >16<option value="17" >17<option value="18" >18<option value="19" >19<option value="20" >20<option value="21" >21<option value="22" >22<option value="23" >23<option value="24" >24<option value="25" >25<option value="26" >26<option value="27" >27<option value="28" >28<option value="29" >29<option value="30" >30<option value="31" >31 </select> # Year: # <select name="startYear" class="inputtext"> # <option value="0000"> # <option value="2014" selected>2014<option value="2013" >2013<option value="2012" >2012<option value="2011" >2011<option value="2010" >2010<option value="2009" >2009<option value="2008" >2008<option value="2007" >2007<option value="2006" >2006<option value="2005" >2005<option value="2004" >2004<option value="2003" >2003<option value="2002" >2002<option value="2001" >2001<option value="2000" >2000<option value="1999" >1999<option value="1998" >1998<option value="1997" >1997<option value="1996" >1996<option value="1995" >1995<option value="1994" >1994<option value="1993" >1993<option value="1992" >1992<option value="1991" >1991<option value="1990" >1990<option value="1989" >1989<option value="1988" >1988<option value="1987" >1987<option value="1986" >1986<option value="1985" >1985<option value="1984" >1984 </select> # # <label><input type="checkbox" onchange="ChangeStartDate();" name="unknownStart" value="1"> <small>Unknown Date</label><br>Start Date represents the date you started watching the Anime <a href="javascript:setToday(1);">Insert Today</a></small> # </td> #</tr> #<tr> # <td class="borderClass">Finish Date</td> # <td class="borderClass"> # Month: # <select name="endMonth" class="inputtext" disabled> # <option value="00"> # <option value="1" >Jan<option value="2" >Feb<option value="3" >Mar<option value="4" >Apr<option value="5" >May<option value="6" >Jun<option value="7" >Jul<option value="8" >Aug<option value="9" >Sep<option value="10" >Oct<option value="11" >Nov<option value="12" >Dec </select> # Day: # <select name="endDay" class="inputtext" disabled> # <option value="00"> # <option value="1" >1<option value="2" >2<option value="3" >3<option value="4" >4<option value="5" >5<option value="6" >6<option value="7" >7<option value="8" >8<option value="9" >9<option value="10" >10<option value="11" >11<option value="12" >12<option value="13" >13<option value="14" >14<option value="15" >15<option value="16" >16<option value="17" >17<option value="18" >18<option value="19" >19<option value="20" >20<option value="21" >21<option value="22" >22<option value="23" >23<option value="24" >24<option value="25" >25<option value="26" >26<option value="27" >27<option value="28" >28<option value="29" >29<option value="30" >30<option value="31" >31 </select> # Year: # <select name="endYear" class="inputtext" disabled> # <option value="0000"> # <option value="2014" >2014<option value="2013" >2013<option value="2012" >2012<option value="2011" >2011<option value="2010" >2010<option value="2009" >2009<option value="2008" >2008<option value="2007" >2007<option value="2006" >2006<option value="2005" >2005<option value="2004" >2004<option value="2003" >2003<option value="2002" >2002<option value="2001" >2001<option value="2000" >2000<option value="1999" >1999<option value="1998" >1998<option value="1997" >1997<option value="1996" >1996<option value="1995" >1995<option value="1994" >1994<option value="1993" >1993<option value="1992" >1992<option value="1991" >1991<option value="1990" >1990<option value="1989" >1989<option value="1988" >1988<option value="1987" >1987<option value="1986" >1986<option value="1985" >1985<option value="1984" >1984 </select> # # <small><label><input type="checkbox" onchange="ChangeEndDate();" checked name="unknownEnd" value="1"> Unknown Date</label><br>Do <u>not</u> fill out the Finish Date unless status is <em>Completed</em> <a href="javascript:setToday(2);">Insert Today</a></small> # </td> #</tr> $isStarted = $crawler->filter('input[id="unknown_start"]')->attr('checked'); $isEnded = $crawler->filter('input[id="unknown_end"]')->attr('checked'); if ($isStarted != 'checked') { //So, MAL allows users to put in just years, just years and months, or all three values. //This mess here is to try and avoid things breaking. if ($crawler->filter('select[id="add_anime_start_date_year"] option:selected')->count() > 0) { $startYear = $crawler->filter('select[id="add_anime_start_date_year"] option:selected')->attr('value'); $startMonth = 6; $startDay = 15; if ($startYear !== '') { if ($crawler->filter('select[id="add_anime_start_date_month"] option:selected')->count() > 0) { $startMonth = $crawler->filter('select[id="add_anime_start_date_month"] option:selected')->attr('value'); if ($startMonth === '') { $startMonth = 6; } if ($crawler->filter('select[id="add_anime_start_date_day"] option:selected')->count() > 0) { $startDay = $crawler->filter('select[id="add_anime_start_date_day"] option:selected')->attr('value'); if ($startDay === '') { $startDay = 15; } } } $anime->setWatchingStart(DateTime::createFromFormat('Y-n-j', "{$startYear}-{$startMonth}-{$startDay}")); } } } if ($isEnded != 'checked') { //Same here, avoid breaking MAL's allowing of partial dates. if ($crawler->filter('select[id="add_anime_finish_date_year"] option:selected')->count() > 0) { $endYear = $crawler->filter('select[id="add_anime_finish_date_year"] option:selected')->attr('value'); $endMonth = 6; $endDay = 15; if ($endYear !== '') { if ($crawler->filter('select[id="add_anime_finish_date_month"] option:selected')->count() > 0) { $endMonth = $crawler->filter('select[id="add_anime_finish_date_month"] option:selected')->attr('value'); if ($endMonth === '') { $endMonth = 6; } if ($crawler->filter('select[id="add_anime_finish_date_day"] option:selected')->count() > 0) { $endDay = $crawler->filter('select[id="add_anime_finish_date_day"] option:selected')->attr('value'); if ($endDay === '') { $endDay = 15; } } } $anime->setWatchingEnd(DateTime::createFromFormat('Y-n-j', "{$endYear}-{$endMonth}-{$endDay}")); } } } #Priority #<td class="borderClass"><select name="priority" class="inputtext"> #<option value="0" selected>Low<option value="1" >Medium<option value="2" >High </select> $priority = $crawler->filter('select[id="add_anime_priority"] option:selected')->attr('value'); $anime->setPriority($priority); #Storage # #<td class="borderClass" align="left"><select name="storage" id="storage" onchange="StorageBooleanCheck(2);" class="inputtext"> # <option value="0">Select storage type # <option value="1" >Hard Drive<option value="6" >External HD<option value="7" >NAS<option value="2" >DVD / CD<option value="4" >Retail DVD<option value="5" >VHS<option value="3" >None </select> #<div style="margin: 3px 0px; display: none;" id="StorageDiv">Total <span id="storageDescription">DvD's</span> <input type="text" name="storageVal" id="storageValue" value="0.00" size="4" class="inputtext"></div> #</td> //Note that if storage isn't defined, nothing will be marked as selected. We thus have to get the value in two stages to avoid raising an exception. $storage = $crawler->filter('select[id="add_anime_storage_type"] option:selected'); if (count($storage)) { $anime->setStorage($storage->attr('value')); } #Storage Value - Either number of discs or size in GB #<div style="margin: 3px 0px; display: none;" id="StorageDiv">Total <span id="storageDescription">DvD's</span> <input type="text" name="storageVal" id="storageValue" value="1.00" size="4" class="inputtext"></div> $storageval = (double) $crawler->filter('input[id="add_anime_storage_value"]')->attr('value'); if ($storageval > 0) { $anime->setStorageValue($storageval); } #Rewatched #<label><input type="checkbox" id="add_anime_is_rewatching" name="add_anime[is_rewatching]" value="1" checked="checked"> $rewatch = $crawler->filter('input[id="add_anime_is_rewatching"]')->attr('checked'); if ($rewatch == null) { $anime->setRewatching(false); } else { $anime->setRewatching(true); } #Times Rewatched #<td class="borderClass"><input type="text" name="list_times_watched" value="0" size="4" class="inputtext"> $rewatchCount = $crawler->filter('input[id="add_anime_num_watched_times"]')->attr('value'); if ($rewatchCount > 0) { $anime->setRewatchCount($rewatchCount); } #Rewatch Value #<td class="borderClass"><select name="list_rewatch_value" class="inputtext"> # <option value="0">Select rewatch value<option value="1">Very Low<option value="2">Low<option value="3">Medium<option value="4">High<option selected value="5">Very High </select> $rewatchValue = $crawler->filter('select[id="add_anime_rewatch_value"] option:selected'); if (count($rewatchValue)) { $anime->setRewatchValue($rewatchValue->attr('value')); } #Comments #<td class="borderClass"><textarea name="list_comments" rows="5" cols="45" class="textarea"></textarea></td> $comments = trim($crawler->filter('textarea[id="add_anime_comments"]')->text()); if (strlen($comments)) { $anime->setPersonalComments($comments); } return $anime; }
public static function parseDay($rows) { $result = array(); foreach ($rows as $item) { $crawler = new Crawler($item); $anime = new Anime(); $url = $crawler->filter('a[class="link-title"]')->attr('href'); $id = preg_match('/\\/(anime|manga)\\/(\\d+)\\/.*?/', $url, $urlParts); if ($id !== false || $id !== 0) { $anime->setId((int) $urlParts[2]); } $anime->setTitle(trim($crawler->filter('a[class="link-title"]')->text())); $producer = $crawler->filter('span[class="producer"] a'); if ($producer->count() > 0) { $anime->setProducers(explode(', ', $crawler->filter('span[class="producer"] a')->text())); } $anime->setEpisodes((int) str_replace(' eps', '', $crawler->filter('div[class="eps"] span')->text())); $genres = $crawler->filter('div[class="genres-inner js-genre-inner"] a'); $genreArray = array(); foreach ($genres as $genre) { $genreCrawler = new Crawler($genre); $genreArray[] = $genreCrawler->text(); } $anime->setGenres($genreArray); $anime->setImageUrl($crawler->filter('div[class="image lazyload"]')->attr('data-bg')); $anime->setSynopsis(trim($crawler->filter('div[class="synopsis js-synopsis"]')->text())); $detail = explode('-', $crawler->filter('div[class="info"]')->text()); $anime->setType(trim($detail[0])); $anime->setMembersCount((int) str_replace(',', '', trim($crawler->filter('span[class="member fl-r"]')->text()))); $anime->setMembersScore((double) trim($crawler->filter('span[class="score"]')->text())); $result[] = $anime; } return $result; }
public function testMalXml() { $anime = new Anime(); $items = array(); $output = "<?xml version=\"1.0\"?>\n<entry><episode>7</episode><status>1</status><score>9</score><downloaded_episodes>8</downloaded_episodes><storage_type>2</storage_type><storage_value>3.7</storage_value><times_rewatched>1</times_rewatched><rewatch_value>4</rewatch_value><date_start>01012015</date_start><date_finish>01022015</date_finish><priority>0</priority><comments>This is a comment.</comments><fansub_group>GG</fansub_group><tags>one,two,three</tags></entry>\n"; $anime->setId(1); $anime->setWatchedEpisodes(7); $items[] = 'episodes'; $anime->setWatchedStatus(1); $items[] = 'status'; $anime->setScore(9); $items[] = 'score'; $anime->setEpsDownloaded(8); $items[] = 'downloaded'; $anime->setStorage(2); $items[] = 'storage'; $anime->setStorageValue(3.7); $items[] = 'storageAmt'; $anime->setRewatchCount(1); $items[] = 'rewatchCount'; $anime->setRewatchValue(4); $items[] = 'rewatchValue'; $anime->setWatchingStart(new \DateTime('20150101')); $items[] = 'start'; $anime->setWatchingEnd(new \DateTime('20150102')); $items[] = 'end'; $anime->setPriority(6); $items[] = 'priority'; $anime->setPersonalComments('This is a comment.'); $items[] = 'comments'; $anime->setFansubGroup('GG'); $items[] = 'fansubber'; $anime->setPersonalTags('one,two,three'); $items[] = 'tags'; $this->assertEquals($output, $anime->MALApiXml($items)); }
/** * @param $apiVersion The API version of the request * @param $requestType The anime or manga request string * @param Request $request HTTP Request object * * @return \FOS\RestBundle\View\View */ public function getBrowseAction($apiVersion, $requestType, Request $request) { $downloader = $this->get('atarashii_api.communicator'); $page = (int) $request->query->get('page'); if ($page <= 0) { $page = 1; } // Create URL parts supported by MAL $pagePart = '&show=' . ($page * 50 - 50); $keyword = '&q=' . $request->query->get('keyword'); $score = '&score=' . (int) $request->query->get('score'); $reverse = '&w=' . (int) $request->query->get('reverse'); $rating = '&r=' . (int) $request->query->get('rating'); $genreType = '&gx=' . (int) $request->query->get('genre_type'); $status = '&status=' . $this->getStatusId($request->query->get('status')); $endDateArray = explode('-', $request->query->get('end_date')); if (count($endDateArray) == 3) { $endDate = '&ey=' . $endDateArray[0] . '&em=' . $endDateArray[1] . '&ed=' . $endDateArray[2]; } else { $endDate = ''; } $startDateArray = explode('-', $request->query->get('start_date')); if (count($startDateArray) == 3) { $startDate = '&sy=' . $startDateArray[0] . '&sm=' . $startDateArray[1] . '&sd=' . $startDateArray[2]; } else { $startDate = ''; } if ($requestType === 'anime') { $type = '&type=' . Anime::getTypeId($request->query->get('type')); $genres = Anime::getGenresId($request->query->get('genres')); $sort = '&o=' . Anime::getColumnId($request->query->get('sort'), $requestType); } else { $type = '&type=' . Manga::getTypeId($request->query->get('type')); $genres = Manga::getGenresId($request->query->get('genres')); $sort = '&o=' . Manga::getColumnId($request->query->get('sort'), $requestType); } // Combine all URL parts for the request $url = $genres . $sort . $reverse . $endDate . $startDate . $rating . $status . $type . $keyword . $score . $genreType . $pagePart; try { $content = $downloader->fetch('/' . $requestType . '.php?c[]=a&c[]=b&c[]=c&c[]=d&c[]=e&c[]=f&c[]=g&c[]=g' . $url); } catch (Exception\CurlException $e) { return $this->view(array('error' => 'network-error'), 500); } catch (Exception\ClientErrorResponseException $e) { $content = $e->getResponse(); } $response = new Response(); $serializationContext = SerializationContext::create(); $serializationContext->setVersion($apiVersion); $response->setPublic(); $response->setMaxAge(86400); //One day $response->headers->addCacheControlDirective('must-revalidate', true); $response->setEtag($type . '/' . $requestType . '?' . $url); //Also, set "expires" header for caches that don't understand Cache-Control $date = new \DateTime(); $date->modify('+86400 seconds'); //One day $response->setExpires($date); // MAL does contain a bug where excluded genres allow the same amount of pages as normal without warning // To avoid issues we check if the page number does match the content page number. preg_match('/>\\[(\\d+?)\\]/', $content, $matches); if (strpos($content, 'No titles that matched') !== false || strpos($content, 'This page doesn\'t exist') !== false) { return $this->view(array('error' => 'not-found'), 404); } else { if (count($matches) > 1 && (int) $matches[1] !== $page) { return $this->view(array(), 200); } else { //MAL now returns 404 on a single result. Workaround if (method_exists($content, 'getStatusCode') && $content->getStatusCode() === 404) { $location = $content->getHeader('Location'); try { $content = $downloader->fetch($location); if ($type === 'anime') { $searchResult = array(AnimeParser::parse($content)); } else { $searchResult = array(MangaParser::parse($content)); } } catch (Exception\CurlException $e) { return $this->view(array('error' => 'network-error'), 500); } } else { if ($downloader->wasRedirected()) { if ($type === 'anime') { $searchResult = array(AnimeParser::parse($content)); } else { $searchResult = array(MangaParser::parse($content)); } } else { $searchResult = Upcoming::parse($content, $requestType); } } $view = $this->view($searchResult); $view->setSerializationContext($serializationContext); $view->setResponse($response); $view->setStatusCode(200); return $view; } } }
private static function parseAnime($item) { $crawler = new Crawler($item); $anime = new Anime(); $anime->setId($crawler->filter('id')->text()); $anime->setTitle($crawler->filter('title')->text()); $otherTitles = array(); $english = explode('; ', $crawler->filter('english')->text()); if (count($english) > 0 && $english !== '') { $otherTitles['english'] = $english; } $synonyms = explode('; ', $crawler->filter('synonyms')->text()); if (count($synonyms) > 0 && $synonyms[0] !== '') { $otherTitles['synonyms'] = $synonyms; } $anime->setOtherTitles($otherTitles); $anime->setEpisodes($crawler->filter('episodes')->text()); $anime->setMembersScore($crawler->filter('score')->text()); $anime->setStatus($crawler->filter('status')->text()); $startDate = $crawler->filter('start_date')->text(); if ($startDate !== '0000-00-00') { $anime->setStartDate((new \DateTime())->createFromFormat('Y-m-d', $startDate)); } $EndDate = $crawler->filter('end_date')->text(); if ($EndDate !== '0000-00-00') { $anime->setEndDate((new \DateTime())->createFromFormat('Y-m-d', $EndDate)); } $anime->setSynopsis($crawler->filter('synopsis')->text()); $anime->setImageUrl($crawler->filter('image')->text()); return $anime; }
/** * Update an anime on a user's list. * * Uses the contents of the HTTP Request to get the needed data for updating the * requested title. The user must have passed the basic authentication needs and the * PHP_AUTH_USER and PHP_AUTH_PW variables must be set. If so, the get variables of * "status", "episodes", and "score" are checked and used in the creation of an Anime * object. The object is used to make an XML document that is then posted to MyAnimeList. * * @param Request $request Contains all the needed information to update the title. * @param int $id ID of the anime. * @param float $apiVersion The API version for the request * * @return View */ public function updateAction(Request $request, $id, $apiVersion) { // http://myanimelist.net/api/animelist/update/#{id}.xml //get the credentials we received $username = $this->getRequest()->server->get('PHP_AUTH_USER'); $password = $this->getRequest()->server->get('PHP_AUTH_PW'); //Don't bother making a request if the user didn't send any authentication if ($username === null) { $view = $this->view(array('error' => 'unauthorized'), 401); $view->setHeader('WWW-Authenticate', 'Basic realm="myanimelist.net"'); return $view; } $anime = new Anime(); $anime->setId($id); //Only use values we were sent for the Update XML $update_items = array(); try { if ($request->request->get('status') !== null) { $anime->setWatchedStatus($request->request->get('status')); $update_items[] = 'status'; } if ($request->request->get('episodes') !== null) { $anime->setWatchedEpisodes($request->request->get('episodes')); $update_items[] = 'episodes'; } if ($request->request->get('score') !== null) { $anime->setScore($request->request->get('score')); $update_items[] = 'score'; } //API 2 Items if ($apiVersion >= 2.0) { if ($request->request->get('start') !== null) { $anime->setWatchingStart(DateTime::createFromFormat('Y-m-d', $request->request->get('start'))); //Needs to be DT! $update_items[] = 'start'; } if ($request->request->get('end') !== null) { $anime->setWatchingEnd(DateTime::createFromFormat('Y-m-d', $request->request->get('end'))); //Needs to be DT! $update_items[] = 'end'; } if ($request->request->get('downloaded_eps') !== null) { $anime->setEpsDownloaded($request->request->get('downloaded_eps')); //Int $update_items[] = 'downloaded'; } if ($request->request->get('storage_type') !== null) { $anime->setStorage($request->request->get('storage_type')); //Int (see getStorage mappings) $update_items[] = 'storage'; } if ($request->request->get('storage_amt') !== null) { $anime->setStorageValue($request->request->get('storage_amt')); //Float, either in number of discs or in GB $update_items[] = 'storageAmt'; } if ($request->request->get('priority') !== null) { $anime->setPriority($request->request->get('priority')); $update_items[] = 'priority'; } if ($request->request->get('rewatch_value') !== null) { $anime->setRewatchValue($request->request->get('rewatch_value')); //Int $update_items[] = 'rewatchValue'; } if ($request->request->get('tags') !== null) { $anime->setPersonalTags($request->request->get('tags')); //Comma-separated string $update_items[] = 'tags'; } if ($request->request->get('comments') !== null) { $anime->setPersonalComments($request->request->get('comments')); //Plain text string. No HTML. $update_items[] = 'comments'; } if ($request->request->get('fansubber') !== null) { $anime->setFansubGroup($request->request->get('fansubber')); //Plain string $update_items[] = 'fansubber'; } if ($request->request->get('is_rewatching') !== null) { $anime->setRewatching($request->request->get('is_rewatching')); //Bool - 0 = no, 1 = yes $update_items[] = 'isRewatching'; } if ($request->request->get('rewatch_count') !== null) { $anime->setRewatchCount($request->request->get('rewatch_count')); //Int $update_items[] = 'rewatchCount'; } } } catch (\Exception $e) { return $this->view(array('error' => $e->getMessage()), 500); } $xmlcontent = $anime->MALApiXml($update_items); $connection = $this->get('atarashii_api.communicator'); try { $connection->sendXML('/api/animelist/update/' . $anime->getId() . '.xml', $xmlcontent, $username, $password); return $this->view('ok', 200); } catch (Exception\ClientErrorResponseException $e) { $view = $this->view(array('error' => 'unauthorized'), 401); $view->setHeader('WWW-Authenticate', 'Basic realm="myanimelist.net"'); return $view; } catch (Exception\ServerErrorResponseException $e) { //MAL broke API responses, so we have to check the content on the response to make sure //it actually was an error. $response = $e->getResponse()->getBody(true); if (stripos($response, 'Updated') === 0) { return $this->view('ok', 200); } return $this->view(array('error' => 'not-found'), 404); } catch (Exception\CurlException $e) { return $this->view(array('error' => 'network-error'), 500); } }