1
0
mirror of https://github.com/devfake/flox.git synced 2024-11-14 22:22:39 +01:00

search by media type (#45)

* search only for media_type for file-parser

* typos

* remove 'add' button if it's an empty item

* fix relations for searching

* add tests for search

* fix missing media type

* fix media type for strict search

* fix test
This commit is contained in:
Viktor Geringer 2017-02-13 18:47:30 +01:00 committed by Tim Meier
parent 89eb67f85a
commit 04d34440df
10 changed files with 230 additions and 53 deletions

View File

@ -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();
}
/**

View File

@ -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('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)
{
// 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) {
$query->where('title', 'like', '%' . $title . '%');
return $query->where('title', 'like', '%' . $title . '%');
});
});
}
public function scopeFindByTitleStrict($query, $title)
public function scopeFindByTitleStrict($query, $title, $mediaType)
{
return $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);
});
});
}
}

View File

@ -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)

View File

@ -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':

View File

@ -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 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')

View File

@ -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);

View File

@ -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),

View File

@ -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
}

View File

@ -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
}

View File

@ -5,7 +5,7 @@
<span v-if="localItem.rating" :class="'item-rating rating-' + localItem.rating" @click="changeRating()">
<i class="icon-rating"></i>
</span>
<span v-if=" ! localItem.rating" class="item-rating item-new" :class="{disabled: disabled}" @click="addNewItem()">
<span v-if=" ! localItem.rating && localItem.tmdb_id" class="item-rating item-new" :class="{disabled: disabled}" @click="addNewItem()">
<span class="loader smallsize-loader" v-if="rated"><i></i></span>
<i class="icon-add" v-if=" ! rated"></i>
</span>