diff --git a/backend/app/AlternativeTitle.php b/backend/app/AlternativeTitle.php index ae52370..592bab2 100644 --- a/backend/app/AlternativeTitle.php +++ b/backend/app/AlternativeTitle.php @@ -34,6 +34,7 @@ /* * Scopes */ + public function scopeFindByTmdbId($query, $tmdbId) { return $query->where('tmdb_id', $tmdbId); diff --git a/backend/app/Episode.php b/backend/app/Episode.php index 3c45071..e3e7ead 100644 --- a/backend/app/Episode.php +++ b/backend/app/Episode.php @@ -53,7 +53,12 @@ return $query->where('tmdb_id', $tmdbId); } - public function scopeFindEpisode($query, $tmdbId, $episode) + public function scopeFindBySrc($query, $src) + { + return $query->where('src', $src); + } + + public function scopeFindSpecificEpisode($query, $tmdbId, $episode) { return $query->where('tmdb_id', $tmdbId) ->where('season_number', $episode->season_number) diff --git a/backend/app/Http/Controllers/SettingController.php b/backend/app/Http/Controllers/SettingController.php index d6cd7fe..8af352e 100644 --- a/backend/app/Http/Controllers/SettingController.php +++ b/backend/app/Http/Controllers/SettingController.php @@ -169,6 +169,6 @@ { $files = $parser->fetch(); - $parser->store($files); + $parser->updateDatabase($files); } } diff --git a/backend/app/Item.php b/backend/app/Item.php index ef7b463..156862b 100644 --- a/backend/app/Item.php +++ b/backend/app/Item.php @@ -79,6 +79,11 @@ return $query->where('tmdb_id', $tmdbId); } + public function scopeFindBySrc($query, $src) + { + return $query->where('src', $src); + } + public function scopeFindByTitle($query, $title) { return $query->where('title', 'like', '%' . $title . '%') diff --git a/backend/app/Services/FileParser.php b/backend/app/Services/FileParser.php index 14ec2a7..27d6a20 100644 --- a/backend/app/Services/FileParser.php +++ b/backend/app/Services/FileParser.php @@ -3,27 +3,30 @@ namespace App\Services; use App\AlternativeTitle; - use App\Episode; + use App\Services\Models\EpisodeService; use App\Services\Models\ItemService; use App\Setting; use Carbon\Carbon; class FileParser { - private $item; - private $episode; + const ADDED = 'added'; + const REMOVED = 'removed'; + + private $itemService; + private $episodeService; private $tmdb; private $alternativeTitle; private $itemCategory; public function __construct( - ItemService $item, - Episode $episode, + ItemService $itemService, + EpisodeService $episodeService, TMDB $tmdb, AlternativeTitle $alternativeTitle ){ - $this->item = $item; - $this->episode = $episode; + $this->itemService = $itemService; + $this->episodeService = $episodeService; $this->tmdb = $tmdb; $this->alternativeTitle = $alternativeTitle; } @@ -43,31 +46,56 @@ } /** - * Loop over local files and see if it can find them in database. Otherwise search in TMDb. + * Loop over all local files. * * @param $files */ - public function store($files) + public function updateDatabase($files) { foreach($files as $type => $items) { $this->itemCategory = $type; foreach($items as $item) { - $title = $item->name; - - // See if file is already in our database. - if($found = $this->item->findBy('title', $title)) { - $this->handleStatus($item, $found->tmdb_id); - - continue; - } - - // Otherwise make a new TMDb request. - $this->tmdbSearch($title, $item); + $this->handleStatus($item); } } } + /** + * Check which status the file has. + * + * @param $item + * @return bool|mixed|void + */ + private function handleStatus($item) + { + switch($item->status) { + case self::ADDED: + return $this->addItem($item); + case self::REMOVED: + return $this->removeSrc($item); + } + } + + /** + * See if it can find the item in our database. Otherwise search in TMDb. + * + * @param $item + * @return bool|mixed + */ + private function addItem($item) + { + $title = $item->name; + + // See if file is already in our database. + if($found = $this->itemService->findBy('title', $title)) { + return $this->storeSrc($item, $found->tmdb_id); + } + + // Otherwise make a new TMDb request. + return $this->tmdbSearch($title, $item); + } + /** * Make a new request to TMDb and check against the database. Otherwise create a new item. * @@ -91,57 +119,83 @@ * * @param $firstResult * @param $item - * @return \Exception|mixed + * @return mixed */ private function findOrCreateItem($firstResult, $item) { $tmdbId = $firstResult['tmdb_id']; // Check against our database. - if($this->item->findBy('tmdb_id', $tmdbId)) { - return $this->handleStatus($item, $tmdbId); + if($this->itemService->findBy('tmdb_id', $tmdbId)) { + return $this->storeSrc($item, $tmdbId); } // Otherwise create a new item from the result. - $created = $this->item->create($firstResult); + $created = $this->itemService->create($firstResult); - return $this->handleStatus($item, $created->tmdb_id); - } - - /** - * Check which status the file has. - * Create new src if the status is 'added'. - * Update src if status is 'updated'. - * Remove src if status is 'removed'. - * - * @param $item - * @param $tmdb_id - * @return mixed - */ - public function handleStatus($item, $tmdbId) - { - if($item->status == 'added') { - return $this->storeSrc($item, $tmdbId); - } + return $this->storeSrc($item, $created->tmdb_id); } /** * Store src from local file into items for movies or episodes for tv shows. * * @param $item - * @param $tmdb_id + * @param $tmdbId + * @return mixed */ private function storeSrc($item, $tmdbId) + { + $model = $this->findItem($item, $tmdbId); + + if($model) { + return $model->update([ + 'src' => $item->src, + ]); + } + } + + /** + * Remove src for local file in items for movies or episodes for tv shows. + * + * @param $item + * @return mixed + */ + private function removeSrc($item) + { + $model = $this->findItemBySrc($item); + + if($model) { + return $model->update([ + 'src' => null, + ]); + } + } + + /** + * @param $item + * @param $tmdbId + * @return \Illuminate\Support\Collection|mixed + */ + private function findItem($item, $tmdbId) { if($this->itemCategory == 'tv') { - $model = $this->episode->findEpisode($tmdbId, $item); - } else { - $model = $this->item->findBy('tmdb_id', $tmdbId); + return $this->episodeService->findBy('episode', $tmdbId, $item); } - return $model->update([ - 'src' => $item->src, - ]); + return $this->itemService->findBy('tmdb_id', $tmdbId); + } + + /** + * @param $item + * @return \Illuminate\Support\Collection|mixed + */ + private function findItemBySrc($item) + { + if($this->itemCategory == 'tv') { + return $this->episodeService->findBy('src', $item->src); + } + + return $this->itemService->findBy('src', $item->src); } /** diff --git a/backend/app/Services/Models/EpisodeService.php b/backend/app/Services/Models/EpisodeService.php index b81fa71..9c8e3ec 100644 --- a/backend/app/Services/Models/EpisodeService.php +++ b/backend/app/Services/Models/EpisodeService.php @@ -66,9 +66,11 @@ { $episode = $this->model->find($id); - return $episode->update([ - 'seen' => ! $episode->seen, - ]); + if($episode) { + return $episode->update([ + 'seen' => ! $episode->seen, + ]); + } } /** @@ -86,4 +88,27 @@ ]); }); } + + /** + * See if we can find a episode by src or tmdb_id. + * Or we search a specific episode in our database. + * + * @param $type + * @param $value + * @param null $episode + * @return \Illuminate\Support\Collection + */ + public function findBy($type, $value, $episode = null) + { + switch($type) { + case 'src': + return $this->model->findBySrc($value)->first(); + case 'tmdb_id': + return $this->model->findByTmdbId($value)->first(); + case 'episode': + return $this->model->findSpecificEpisode($value, $episode)->first(); + } + + return null; + } } diff --git a/backend/app/Services/Models/ItemService.php b/backend/app/Services/Models/ItemService.php index 097209a..bc76831 100644 --- a/backend/app/Services/Models/ItemService.php +++ b/backend/app/Services/Models/ItemService.php @@ -115,7 +115,7 @@ } /** - * See if we can find a item by title or tmdb_id in our database. + * See if we can find a item by title, tmdb_id or src in our database. * * @param $type * @param $value @@ -123,10 +123,15 @@ */ public function findBy($type, $value) { - if($type == 'title') { - return $this->model->findByTitle($value)->first(); + switch($type) { + case 'title': + return $this->model->findByTitle($value)->first(); + case 'tmdb_id': + return $this->model->findByTmdbId($value)->first(); + case 'src': + return $this->model->findBySrc($value)->first(); } - return $this->model->findByTmdbId($value)->first(); + return null; } } diff --git a/backend/tests/Services/FileParserTest.php b/backend/tests/Services/FileParserTest.php index 299fd83..8c09e5b 100644 --- a/backend/tests/Services/FileParserTest.php +++ b/backend/tests/Services/FileParserTest.php @@ -35,7 +35,7 @@ $this->createMovie(); $item1 = $this->item->first(); - $this->parser->store($this->fpFixtures('movie')); + $this->parser->updateDatabase($this->fpFixtures('movie_added')); $item2 = $this->item->first(); $this->assertNull($item1->src); @@ -48,7 +48,7 @@ $this->createTv(); $episodes1 = $this->item->with('episodes')->first()->episodes; - $this->parser->store($this->fpFixtures('tv')); + $this->parser->updateDatabase($this->fpFixtures('tv_added')); $episodes2 = $this->item->with('episodes')->first()->episodes; $episodes1->each(function($episode) { @@ -67,7 +67,7 @@ $this->createTmdbMock($this->tmdbFixtures('movie'), $this->tmdbFixtures('alternative_titles_movie')); $parser = app(FileParser::class); - $parser->store($this->fpFixtures('movie')); + $parser->updateDatabase($this->fpFixtures('movie_added')); $item = $this->item->first(); @@ -86,7 +86,7 @@ $this->createTmdbMock($this->tmdbFixtures('tv'), $this->tmdbFixtures('alternative_titles_tv')); $parser = app(FileParser::class); - $parser->store($this->fpFixtures('tv')); + $parser->updateDatabase($this->fpFixtures('tv_added')); $episodes2 = $this->episode->get(); @@ -120,6 +120,39 @@ $this->assertNotEquals($setting2->last_fetch_to_file_parser, $setting3->last_fetch_to_file_parser); } + /** @test */ + public function it_should_remove_src_from_movie() + { + $this->createMovie(); + $this->parser->updateDatabase($this->fpFixtures('movie_added')); + + $withSrc = $this->item->first(); + $this->parser->updateDatabase($this->fpFixtures('movie_removed')); + $withoutSrc = $this->item->first(); + + $this->assertNotNull($withSrc->src); + $this->assertNull($withoutSrc->src); + } + + /** @test */ + public function it_should_remove_src_from_tv_episode() + { + $this->createTv(); + $this->parser->updateDatabase($this->fpFixtures('tv_added')); + + $withSrc = $this->item->with('episodes')->first()->episodes; + $this->parser->updateDatabase($this->fpFixtures('tv_removed')); + $withoutSrc = $this->item->with('episodes')->first()->episodes; + + $withSrc->each(function($episode) { + $this->assertNotNull($episode->src); + }); + + $withoutSrc->each(function($episode) { + $this->assertNull($episode->src); + }); + } + private function createTmdbMock($fixture, $alternativeTitles) { $mock = new MockHandler([ diff --git a/backend/tests/fixtures/fp/all.json b/backend/tests/fixtures/fp/all.json index 80c5aa1..f3464af 100644 --- a/backend/tests/fixtures/fp/all.json +++ b/backend/tests/fixtures/fp/all.json @@ -122,6 +122,18 @@ ], "status": "added", "subtitles": "/movies/Warcraft.2016.720p.WEB-DL/Warcraft.2016.720p.WEB-DL.srt" + }, + { + "name": "warcraft", + "extension": "mkv", + "filename": "Warcraft.2016.720p.WEB-DL", + "src": "/movies/Warcraft.2016.720p.WEB-DL/Warcraft.2016.720p.WEB-DL.mkv", + "year": 2016, + "tags": [ + "720p" + ], + "status": "removed", + "subtitles": "/movies/Warcraft.2016.720p.WEB-DL/Warcraft.2016.720p.WEB-DL.srt" } ] } \ No newline at end of file diff --git a/backend/tests/fixtures/fp/movie.json b/backend/tests/fixtures/fp/movie_added.json similarity index 100% rename from backend/tests/fixtures/fp/movie.json rename to backend/tests/fixtures/fp/movie_added.json diff --git a/backend/tests/fixtures/fp/movie_removed.json b/backend/tests/fixtures/fp/movie_removed.json new file mode 100644 index 0000000..70360c8 --- /dev/null +++ b/backend/tests/fixtures/fp/movie_removed.json @@ -0,0 +1,16 @@ +{ + "movies": [ + { + "name": "warcraft", + "extension": "mkv", + "filename": "Warcraft.2016.720p.WEB-DL", + "src": "/movies/Warcraft.2016.720p.WEB-DL/Warcraft.2016.720p.WEB-DL.mkv", + "year": 2016, + "tags": [ + "720p" + ], + "status": "removed", + "subtitles": "/movies/Warcraft.2016.720p.WEB-DL/Warcraft.2016.720p.WEB-DL.srt" + } + ] +} \ No newline at end of file diff --git a/backend/tests/fixtures/fp/tv.json b/backend/tests/fixtures/fp/tv_added.json similarity index 100% rename from backend/tests/fixtures/fp/tv.json rename to backend/tests/fixtures/fp/tv_added.json diff --git a/backend/tests/fixtures/fp/tv_removed.json b/backend/tests/fixtures/fp/tv_removed.json new file mode 100644 index 0000000..c37f4c1 --- /dev/null +++ b/backend/tests/fixtures/fp/tv_removed.json @@ -0,0 +1,52 @@ +{ + "tv": [ + { + "name": "Game of Thrones", + "season_number": 2, + "episode_number": 1, + "status": "removed", + "extension": "mkv", + "tags": [], + "year": null, + "filename": "1", + "subtitles": null, + "src": "/tv/Game of Thrones/S2/1.mkv" + }, + { + "name": "Game of Thrones", + "season_number": 2, + "episode_number": 2, + "tags": [], + "status": "removed", + "extension": "mkv", + "year": null, + "filename": "2", + "subtitles": null, + "src": "/tv/Game of Thrones/S2/2.mkv" + }, + { + "name": "Game of Thrones", + "season_number": 1, + "episode_number": 1, + "extension": "mkv", + "status": "removed", + "filename": "1", + "tags": [], + "subtitles": null, + "year": null, + "src": "/tv/Game of Thrones/s1/1.mkv" + }, + { + "name": "Game of Thrones", + "season_number": 1, + "tags": [], + "episode_number": 2, + "status": "removed", + "extension": "mp4", + "filename": "2", + "year": null, + "subtitles": null, + "src": "/tv/Game of Thrones/s1/2.mp4" + } + ] +} \ No newline at end of file