diff --git a/backend/app/Http/Controllers/ItemController.php b/backend/app/Http/Controllers/ItemController.php
index 4495245..e52db55 100644
--- a/backend/app/Http/Controllers/ItemController.php
+++ b/backend/app/Http/Controllers/ItemController.php
@@ -65,11 +65,11 @@
$title = Input::get('q');
if(config('scout.driver')) {
- return $this->item->search($title)->get();
+ return $this->item->search($title)->with('latestEpisode')->get();
}
// We don't have an smart search driver and return an simple 'like' query.
- return $this->item->findByTitle($title)->get();
+ return $this->item->findByTitle($title)->with('latestEpisode')->get();
}
/**
diff --git a/backend/app/Item.php b/backend/app/Item.php
index 2da433b..706fdad 100644
--- a/backend/app/Item.php
+++ b/backend/app/Item.php
@@ -102,11 +102,14 @@
return $query->where('tmdb_id', $tmdbId);
}
- public function scopeFindByFPName($query, $item)
+ public function scopeFindByFPName($query, $item, $mediaType)
{
$changed = isset($item->changed->name) ? $item->changed->name : $item->name;
- return $query->where('fp_name', $item->name)->orWhere('fp_name', $changed);
+ return $query->where('media_type', $mediaType)
+ ->where(function($query) use ($item, $changed) {
+ return $query->where('fp_name', $item->name)->orWhere('fp_name', $changed);
+ });
}
public function scopeFindBySrc($query, $src)
@@ -114,22 +117,32 @@
return $query->where('src', $src);
}
- public function scopeFindByTitle($query, $title)
+ public function scopeFindByTitle($query, $title, $mediaType = null)
{
- return $query->where('title', 'like', '%' . $title . '%')
- ->orWhere('original_title', 'like', '%' . $title . '%')
- ->orWhereHas('alternativeTitles', function($query) use ($title) {
- $query->where('title', 'like', '%' . $title . '%');
- });
+ // Only necessarily if we search from file-parser.
+ if($mediaType) {
+ $query->where('media_type', $mediaType);
+ }
+
+ return $query->where(function($query) use ($title) {
+ return $query->where('title', 'like', '%' . $title . '%')
+ ->orWhere('original_title', 'like', '%' . $title . '%')
+ ->orWhereHas('alternativeTitles', function($query) use ($title) {
+ return $query->where('title', 'like', '%' . $title . '%');
+ });
+ });
}
- public function scopeFindByTitleStrict($query, $title)
+ public function scopeFindByTitleStrict($query, $title, $mediaType)
{
- return $query->where('title', $title)
- ->orWhere('original_title', $title)
- ->orWhere('fp_name', $title)
- ->orWhereHas('alternativeTitles', function($query) use ($title) {
- $query->where('title', $title);
+ return $query->where('media_type', $mediaType)
+ ->where(function($query) use ($title) {
+ $query->where('title', $title)
+ ->orWhere('original_title', $title)
+ ->orWhere('fp_name', $title)
+ ->orWhereHas('alternativeTitles', function($query) use ($title) {
+ $query->where('title', $title);
+ });
});
}
}
diff --git a/backend/app/Services/FileParser.php b/backend/app/Services/FileParser.php
index e48a102..8588391 100644
--- a/backend/app/Services/FileParser.php
+++ b/backend/app/Services/FileParser.php
@@ -107,7 +107,7 @@
}
/**
- * See if it can find the item in our database.
+ * See if it can find the item in our database, filtered by media_type.
* Otherwise search in TMDb or create an empty item.
*
* @param $item
@@ -116,7 +116,7 @@
private function validateStore($item)
{
// See if file is already in our database.
- if($found = $this->itemService->findBy('title_strict', $item->name)) {
+ if($found = $this->itemService->findBy('title_strict', $item->name, $this->itemCategory)) {
return $this->store($item, $found->tmdb_id);
}
@@ -132,7 +132,7 @@
}
/**
- * See if it can find the item in our database.
+ * See if it can find the item in our database, filtered by media_type.
* Check if it is an empty item and search against TMDb and update them.
* Otherwise create an empty item.
*
@@ -142,7 +142,7 @@
private function validateUpdate($item)
{
// See if file is already in our database.
- if($found = $this->itemService->findBy('fp_name', $item)) {
+ if($found = $this->itemService->findBy('fp_name', $item, $this->itemCategory)) {
if( ! $found->tmdb_id) {
return $this->searchTmdbAndUpdateEmptyItem($found, $item);
}
@@ -194,7 +194,7 @@
*/
private function searchTmdb($item)
{
- $found = $this->tmdb->search($this->getFileName($item));
+ $found = $this->tmdb->search($this->getFileName($item), $this->itemCategory);
if( ! $found) {
return false;
@@ -207,7 +207,6 @@
* If TMDb can't find anything, create a simple item with data from local file.
*
* @param $item
- * @param $mediaType
* @return mixed
*/
private function createEmptyItem($item)
diff --git a/backend/app/Services/Models/ItemService.php b/backend/app/Services/Models/ItemService.php
index 1cab881..00ddd63 100644
--- a/backend/app/Services/Models/ItemService.php
+++ b/backend/app/Services/Models/ItemService.php
@@ -134,21 +134,29 @@
}
/**
- * See if we can find a item by title, tmdb_id or src in our database.
+ * See if we can find a item by title, fp_name, tmdb_id or src in our database.
+ *
+ * If we search from file-parser, we also need to filter the results by media_type.
+ * If we have e.g. 'Avatar' as tv show, we don't want results like the 'Avatar' movie.
*
* @param $type
* @param $value
+ * @param $mediaType
* @return mixed
*/
- public function findBy($type, $value)
+ public function findBy($type, $value, $mediaType = null)
{
+ if($mediaType) {
+ $mediaType = $mediaType == 'movies' ? 'movie' : 'tv';
+ }
+
switch($type) {
case 'title':
- return $this->model->findByTitle($value)->first();
+ return $this->model->findByTitle($value, $mediaType)->first();
case 'title_strict':
- return $this->model->findByTitleStrict($value)->first();
+ return $this->model->findByTitleStrict($value, $mediaType)->first();
case 'fp_name':
- return $this->model->findByFPName($value)->first();
+ return $this->model->findByFPName($value, $mediaType)->first();
case 'tmdb_id':
return $this->model->findByTmdbId($value)->first();
case 'src':
diff --git a/backend/app/Services/TMDB.php b/backend/app/Services/TMDB.php
index a333c2b..cbabd7a 100644
--- a/backend/app/Services/TMDB.php
+++ b/backend/app/Services/TMDB.php
@@ -19,7 +19,7 @@
private $base = 'http://api.themoviedb.org';
/**
- * Get the API Key for TMDB and create an instance of Guzzle.
+ * Get the API Key for TMDb and create an instance of Guzzle.
*
* @param Client $client
*/
@@ -31,22 +31,38 @@
}
/**
- * Search TMDB by 'title'.
+ * Search TMDb by 'title'.
*
- * @param $title
- * @return array
+ * @param $title
+ * @param null $mediaType
+ * @return Collection
*/
- public function search($title)
+ public function search($title, $mediaType = null)
{
- $response = $this->requestTmdb($this->base . '/3/search/multi', [
- 'query' => $title
- ]);
+ $tv = collect();
+ $movies = collect();
- return $this->createItems($response);
+ if( ! $mediaType || $mediaType == 'tv') {
+ $response = $this->fetchSearch($title, 'tv');
+ $tv = collect($this->createItems($response, 'tv'));
+ }
+
+ if( ! $mediaType || $mediaType == 'movies') {
+ $response = $this->fetchSearch($title, 'movie');
+ $movies = collect($this->createItems($response, 'movie'));
+ }
+
+ return $tv->merge($movies)->toArray();
+ }
+
+ private function fetchSearch($title, $mediaType) {
+ return $this->requestTmdb($this->base . '/3/search/' . $mediaType, [
+ 'query' => $title,
+ ]);
}
/**
- * Search TMDB for recommendations and similar movies.
+ * Search TMDb for recommendations and similar movies.
*
* @param $mediaType
* @param $tmdbID
@@ -82,7 +98,7 @@
}
/**
- * Search TMDB for upcoming movies.
+ * Search TMDb for upcoming movies.
*
* @return array
*/
@@ -98,7 +114,7 @@
}
/**
- * Search TMDB for current popular movies and tv shows.
+ * Search TMDb for current popular movies and tv shows.
*
* @return array
*/
@@ -148,21 +164,16 @@
/**
* @param $response
- * @param null $type
- *
+ * @param $mediaType
* @return array
+ *
*/
- private function createItems($response, $type = null)
+ private function createItems($response, $mediaType)
{
$items = [];
$response = json_decode($response->getBody());
foreach($response->results as $result) {
- // Suggestions doesn't deliver 'media type' by default
- $mediaType = $type ?: $result->media_type;
-
- if($mediaType == 'person') continue;
-
$dtime = DateTime::createFromFormat('Y-m-d', (array_key_exists('release_date', $result)
? ($result->release_date ?: '1970-12-1')
: ($result->first_air_date ?: '1970-12-1')
diff --git a/backend/tests/Services/ItemServiceTest.php b/backend/tests/Services/ItemServiceTest.php
index d59ddc6..2d5a609 100644
--- a/backend/tests/Services/ItemServiceTest.php
+++ b/backend/tests/Services/ItemServiceTest.php
@@ -67,7 +67,7 @@
{
$this->createMovie();
- $itemFromTitle = $this->itemService->findBy('title', 'craft');
+ $itemFromTitle = $this->itemService->findBy('title', 'craft', 'movies');
$itemFromId = $this->itemService->findBy('tmdb_id', 68735);
$this->assertEquals(68735, $itemFromTitle->tmdb_id);
@@ -79,8 +79,8 @@
{
$this->createMovie();
- $notFound = $this->itemService->findBy('title_strict', 'craft');
- $found = $this->itemService->findBy('title_strict', 'Warcraft: The Beginning');
+ $notFound = $this->itemService->findBy('title_strict', 'craft', 'movies');
+ $found = $this->itemService->findBy('title_strict', 'Warcraft: The Beginning', 'movies');
$this->assertNull($notFound);
$this->assertEquals(68735, $found->tmdb_id);
diff --git a/backend/tests/Services/TMDBTest.php b/backend/tests/Services/TMDBTest.php
index 0b4af02..fd6ffbc 100644
--- a/backend/tests/Services/TMDBTest.php
+++ b/backend/tests/Services/TMDBTest.php
@@ -11,6 +11,48 @@
use DatabaseMigrations;
+ /** @test */
+ public function it_should_search_and_merge_movies_and_tv()
+ {
+ $this->createGuzzleMock($this->tmdbFixtures('tv/search'), $this->tmdbFixtures('movie/search'));
+
+ $result = $this->callSearch();
+
+ $hasTv = $this->in_array_r('Avatar: The Last Airbender', $result);
+ $hasMovie = $this->in_array_r('Avatar: Aufbruch nach Pandora', $result);
+
+ $this->assertTrue($hasTv);
+ $this->assertTrue($hasMovie);
+ }
+
+ /** @test */
+ public function it_should_only_search_for_tv()
+ {
+ $this->createGuzzleMock($this->tmdbFixtures('tv/search'), $this->tmdbFixtures('movie/search'));
+
+ $result = $this->callSearch('tv');
+
+ $hasTv = $this->in_array_r('Avatar: The Last Airbender', $result);
+ $hasMovie = $this->in_array_r('Avatar: Aufbruch nach Pandora', $result);
+
+ $this->assertTrue($hasTv);
+ $this->assertFalse($hasMovie);
+ }
+
+ /** @test */
+ public function it_should_only_search_for_movies()
+ {
+ $this->createGuzzleMock($this->tmdbFixtures('movie/search'), $this->tmdbFixtures('tv/search'));
+
+ $result = $this->callSearch('movies');
+
+ $hasTv = $this->in_array_r('Avatar: The Last Airbender', $result);
+ $hasMovie = $this->in_array_r('Avatar: Aufbruch nach Pandora', $result);
+
+ $this->assertFalse($hasTv);
+ $this->assertTrue($hasMovie);
+ }
+
/** @test */
public function it_should_fetch_and_merge_movies_and_tv_in_trending()
{
@@ -70,17 +112,24 @@
$this->app->instance(Client::class, new Client(['handler' => $handler]));
$tmdb = app(TMDB::class);
- $result = $tmdb->search('Avatar - Legend of Korra');
+ $result = $tmdb->search('Avatar - Legend of Korra', 'tv');
$this->assertCount(1, $result);
$this->assertArrayHasKey('tmdb_id', $result[0]);
}
+ private function callSearch($type = null)
+ {
+ $tmdb = app(TMDB::class);
+
+ return $tmdb->search('Avatar', $type);
+ }
+
private function in_array_r($item , $array){
return (bool) preg_match('/"' . $item . '"/i' , json_encode($array));
}
- private function createGuzzleMock($fixture, $fixture2 = null)
+ private function createGuzzleMock($fixture, $fixture2)
{
$mock = new MockHandler([
new Response(200, ['X-RateLimit-Remaining' => [40]], $fixture),
diff --git a/backend/tests/fixtures/tmdb/movie/search.json b/backend/tests/fixtures/tmdb/movie/search.json
new file mode 100644
index 0000000..0626ade
--- /dev/null
+++ b/backend/tests/fixtures/tmdb/movie/search.json
@@ -0,0 +1,46 @@
+{
+ "page": 1,
+ "results": [
+ {
+ "poster_path": "/kmcqlZGaSh20zpTbuoF0Cdn07dT.jpg",
+ "adult": false,
+ "overview": "overview",
+ "release_date": "2009-12-10",
+ "genre_ids": [
+ 28,
+ 12,
+ 14,
+ 878
+ ],
+ "id": 19995,
+ "original_title": "Avatar",
+ "original_language": "en",
+ "title": "Avatar: Aufbruch nach Pandora",
+ "backdrop_path": "/5XPPB44RQGfkBrbJxmtdndKz05n.jpg",
+ "popularity": 13.117401,
+ "vote_count": 8721,
+ "video": false,
+ "vote_average": 7.1
+ },
+ {
+ "poster_path": "/famiFiwZPQ3A8WVjKFhCplKrZgr.jpg",
+ "adult": false,
+ "overview": "overview",
+ "release_date": "2011-04-30",
+ "genre_ids": [
+ 27
+ ],
+ "id": 282908,
+ "original_title": "Abataa",
+ "original_language": "ja",
+ "title": "Avatar",
+ "backdrop_path": null,
+ "popularity": 1.001713,
+ "vote_count": 0,
+ "video": false,
+ "vote_average": 0
+ }
+ ],
+ "total_results": 29,
+ "total_pages": 2
+}
\ No newline at end of file
diff --git a/backend/tests/fixtures/tmdb/tv/search.json b/backend/tests/fixtures/tmdb/tv/search.json
new file mode 100644
index 0000000..370db52
--- /dev/null
+++ b/backend/tests/fixtures/tmdb/tv/search.json
@@ -0,0 +1,51 @@
+{
+ "page": 1,
+ "results": [
+ {
+ "poster_path": "/4KgScXaTeVZWgsBDJNbDYbeqRjF.jpg",
+ "popularity": 3.04828,
+ "id": 246,
+ "backdrop_path": "/14UEjm0MDQ8C22BqYqdzn3gsAiX.jpg",
+ "vote_average": 7.8,
+ "overview": "overview",
+ "first_air_date": "2005-02-21",
+ "origin_country": [
+ "US"
+ ],
+ "genre_ids": [
+ 28,
+ 12,
+ 16,
+ 14
+ ],
+ "original_language": "en",
+ "vote_count": 67,
+ "name": "Avatar: The Last Airbender",
+ "original_name": "Avatar: The Last Airbender"
+ },
+ {
+ "poster_path": "/8uOOycL6r4vqOT8tgw4behO5MmB.jpg",
+ "popularity": 3.52741,
+ "id": 33880,
+ "backdrop_path": "/r1oTzR9Ke7pICxe1eiP8ZjoGJju.jpg",
+ "vote_average": 7.52,
+ "overview": "overview",
+ "first_air_date": "2012-04-14",
+ "origin_country": [
+ "US"
+ ],
+ "genre_ids": [
+ 10765,
+ 16,
+ 18,
+ 10751
+ ],
+ "original_language": "en",
+ "vote_count": 44,
+ "name": "The Legend of Korra",
+ "original_name": "The Legend of Korra"
+ }
+ ],
+ "total_results": 2,
+ "total_pages": 1
+}
\ No newline at end of file
diff --git a/client/app/components/Content/Item.vue b/client/app/components/Content/Item.vue
index 6b56ec7..8b6053f 100644
--- a/client/app/components/Content/Item.vue
+++ b/client/app/components/Content/Item.vue
@@ -5,7 +5,7 @@
-
+