/**
  * @covers ::parseExtendedPersonal
  */
 public function testParseExtendedPersonal()
 {
     $manga = new Manga();
     $mangaContents = file_get_contents(__DIR__ . '/../InputSamples/manga-11977-mine-detailed.html');
     MangaParser::parseExtendedPersonal($mangaContents, $manga);
     $this->assertContains('drama', $manga->getPersonalTags());
     $this->assertContains('seinen', $manga->getPersonalTags());
     $this->assertEquals('2013-09-25', $manga->getReadingStart()->format('Y-m-d'));
     $this->assertEquals('2013-10-11', $manga->getReadingEnd()->format('Y-m-d'));
     $this->assertEquals('Low', $manga->getPriority('string'));
     $this->assertEquals('Medium', $manga->getRereadValue('string'));
     $this->assertStringStartsWith('An interesting spin', $manga->getPersonalComments());
     $mangaContents = file_get_contents(__DIR__ . '/../InputSamples/manga-17074-mine-detailed.html');
     MangaParser::parseExtendedPersonal($mangaContents, $manga);
     $this->assertEquals('Medium', $manga->getPriority('string'));
     $this->assertEquals(3, $manga->getRereadValue());
     $this->assertEquals(1, $manga->getRereadCount());
 }
 /**
  * @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;
         }
     }
 }
 /**
  * Get the details of an anime or manga.
  *
  * @param int     $id          The ID of the anime or manga as assigned by MyAnimeList
  * @param string  $apiVersion  The API version of the request
  * @param Request $request     HTTP Request object
  * @param string  $requestType The anime or manga request string
  *
  * @return View
  */
 public function getAction($id, $apiVersion, $requestType, Request $request)
 {
     //General information (and basic personal information) at:
     // http://myanimelist.net/anime/#{id}
     // http://myanimelist.net/manga/#{id}
     //Detailed personal information at:
     // http://myanimelist.net/ownlist/anime/{id}/edit?hideLayout
     // http://myanimelist.net/ownlist/manga/{id}/edit?hideLayout
     $usepersonal = (int) $request->query->get('mine');
     $downloader = $this->get('atarashii_api.communicator');
     if ($usepersonal) {
         //get the credentials we received
         $username = $this->getRequest()->server->get('PHP_AUTH_USER');
         $password = $this->getRequest()->server->get('PHP_AUTH_PW');
         try {
             if (!$downloader->cookieLogin($username, $password)) {
                 $view = $this->view(array('error' => 'unauthorized'), 401);
                 $view->setHeader('WWW-Authenticate', 'Basic realm="myanimelist.net"');
                 return $view;
             }
         } catch (Exception\CurlException $e) {
             return $this->view(array('error' => 'network-error'), 500);
         }
     }
     try {
         $recordDetails = $downloader->fetch('/' . $requestType . '/' . $id);
     } catch (Exception\CurlException $e) {
         return $this->view(array('error' => 'network-error'), 500);
     } catch (Exception\ClientErrorResponseException $e) {
         $recordDetails = $e->getResponse();
     }
     if (strpos($recordDetails, 'No series found') !== false || strpos($recordDetails, 'This page doesn\'t exist') !== false) {
         return $this->view(array('error' => 'not-found'), 404);
     } else {
         if ($requestType === 'anime') {
             $record = AnimeParser::parse($recordDetails, $apiVersion);
         } else {
             $record = MangaParser::parse($recordDetails, $apiVersion);
         }
         //Parse extended personal details if API 2.0 or better and personal details are requested
         if ($apiVersion >= '2.0' && $usepersonal) {
             try {
                 $recordDetails = $downloader->fetch('/ownlist/' . $requestType . '/' . $id . '/edit?hideLayout');
             } catch (Exception\CurlException $e) {
                 return $this->view(array('error' => 'network-error'), 500);
             }
             if (strpos($recordDetails, 'delete-form') !== false) {
                 if ($requestType === 'anime') {
                     $record = AnimeParser::parseExtendedPersonal($recordDetails, $record);
                 } else {
                     $record = MangaParser::parseExtendedPersonal($recordDetails, $record);
                 }
             }
         }
         $response = new Response();
         $serializationContext = SerializationContext::create();
         $serializationContext->setVersion($apiVersion);
         //For compatibility, API 1.0 explicitly passes null parameters.
         if ($apiVersion == '1.0') {
             $serializationContext->setSerializeNull(true);
         }
         //Only include cache info if it doesn't include personal data.
         if (!$usepersonal) {
             $response->setPublic();
             $response->setMaxAge(3600);
             //One hour
             $response->headers->addCacheControlDirective('must-revalidate', true);
             $response->setEtag($requestType . '/' . $id);
             //Also, set "expires" header for caches that don't understand Cache-Control
             $date = new \DateTime();
             $date->modify('+3600 seconds');
             //One hour
             $response->setExpires($date);
         }
         $view = $this->view($record);
         $view->setSerializationContext($serializationContext);
         $view->setResponse($response);
         $view->setStatusCode(200);
         return $view;
     }
 }
 /**
  * Request search data using the unOfficial.
  *
  * With the unOfficial way the API will get the data from the search page instead of the official API.
  *
  * @param $type       The type is a string which can be 'anime' or 'manga'
  * @param $page       Integer which is used to get the desired page
  * @param $query      The title or keywords to seach for the record
  * @param $apiVersion The API version which was used for the request
  *
  * @return \FOS\RestBundle\View\View
  */
 private function unOfficialSearch($type, $page, $query, $apiVersion)
 {
     // http://myanimelist.net/anime.php?c[]=a&c[]=b&c[]=c&c[]=d&c[]=e&c[]=f&c[]=g&q=#{name}&show=#{page}
     // http://myanimelist.net/manga.php?c[]=a&c[]=b&c[]=c&c[]=d&c[]=e&c[]=f&c[]=g&q=#{name}&show=#{page}
     if ($page <= 0) {
         $page = 1;
     }
     $downloader = $this->get('atarashii_api.communicator');
     try {
         $content = $downloader->fetch('/' . $type . '.php?c[]=a&c[]=b&c[]=c&c[]=d&c[]=e&c[]=f&c[]=g&q=' . $query . '&show=' . ($page * 50 - 50));
     } catch (Exception\CurlException $e) {
         return $this->view(array('error' => 'network-error'), 500);
     } catch (Exception\ClientErrorResponseException $e) {
         //MAL now returns 404 on searches without results.
         //We still need the content for logic purposes.
         $content = $e->getResponse();
     }
     $response = new Response();
     $serializationContext = SerializationContext::create();
     $serializationContext->setVersion($apiVersion);
     $response->setPublic();
     $response->setMaxAge(3600);
     //One hour
     $response->headers->addCacheControlDirective('must-revalidate', true);
     $response->setEtag($type . '/search?q=' . urlencode($query) . 'page=' . $page);
     //Also, set "expires" header for caches that don't understand Cache-Control
     $date = new \DateTime();
     $date->modify('+3600 seconds');
     //One hour
     $response->setExpires($date);
     if (strpos($content, 'No titles that matched') !== false || strpos($content, 'This page doesn\'t exist') !== false) {
         $view = $this->view(array('error' => 'not-found'));
         $view->setResponse($response);
         $view->setStatusCode(404);
         return $view;
     } else {
         //For compatibility, API 1.0 explicitly passes null parameters.
         if ($apiVersion == '1.0') {
             $serializationContext->setSerializeNull(true);
         }
         //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, $type);
             }
         }
         $view = $this->view($searchResult);
         $view->setSerializationContext($serializationContext);
         $view->setResponse($response);
         $view->setStatusCode(200);
         return $view;
     }
 }