2018-03-14 20:41:36 +00:00
using System ;
2017-07-08 14:13:28 +00:00
using System.Collections ;
2016-12-29 15:04:01 +00:00
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using NLog ;
using NzbDrone.Common.EnsureThat ;
using NzbDrone.Common.Extensions ;
using NzbDrone.Core.Messaging.Events ;
using NzbDrone.Core.Organizer ;
using NzbDrone.Core.Parser ;
2017-01-03 19:24:55 +00:00
using NzbDrone.Core.MediaFiles ;
using NzbDrone.Core.MediaFiles.Events ;
2017-01-18 18:53:17 +00:00
using NzbDrone.Core.Datastore ;
2017-02-25 02:30:12 +00:00
using NzbDrone.Core.Configuration ;
2018-03-14 20:41:36 +00:00
using NzbDrone.Core.Movies.Events ;
2017-05-09 18:44:07 +00:00
using NzbDrone.Core.NetImport.ImportExclusions ;
2016-12-29 15:04:01 +00:00
2018-03-14 20:41:36 +00:00
namespace NzbDrone.Core.Movies
2016-12-29 15:04:01 +00:00
{
public interface IMovieService
{
Movie GetMovie ( int movieId ) ;
List < Movie > GetMovies ( IEnumerable < int > movieIds ) ;
2017-02-24 18:52:40 +00:00
PagingSpec < Movie > Paged ( PagingSpec < Movie > pagingSpec ) ;
2016-12-29 15:04:01 +00:00
Movie AddMovie ( Movie newMovie ) ;
2017-01-27 18:36:25 +00:00
List < Movie > AddMovies ( List < Movie > newMovies ) ;
2016-12-29 15:04:01 +00:00
Movie FindByImdbId ( string imdbid ) ;
Movie FindByTitle ( string title ) ;
Movie FindByTitle ( string title , int year ) ;
2017-06-18 21:12:14 +00:00
Movie FindByTitleInexact ( string title , int? year ) ;
2017-01-09 22:16:54 +00:00
Movie FindByTitleSlug ( string slug ) ;
2017-01-23 13:04:01 +00:00
bool MovieExists ( Movie movie ) ;
2017-01-03 19:24:55 +00:00
Movie GetMovieByFileId ( int fileId ) ;
2017-01-16 21:40:59 +00:00
List < Movie > GetMoviesBetweenDates ( DateTime start , DateTime end , bool includeUnmonitored ) ;
2017-01-18 18:53:17 +00:00
PagingSpec < Movie > MoviesWithoutFiles ( PagingSpec < Movie > pagingSpec ) ;
2017-02-25 12:50:12 +00:00
void SetFileId ( Movie movie , MovieFile movieFile ) ;
2017-02-25 21:38:52 +00:00
void DeleteMovie ( int movieId , bool deleteFiles , bool addExclusion = false ) ;
2016-12-29 15:04:01 +00:00
List < Movie > GetAllMovies ( ) ;
2018-11-23 07:03:32 +00:00
List < Movie > AllForTag ( int tagId ) ;
2016-12-29 15:04:01 +00:00
Movie UpdateMovie ( Movie movie ) ;
List < Movie > UpdateMovie ( List < Movie > movie ) ;
2017-07-08 14:13:28 +00:00
List < Movie > FilterExistingMovies ( List < Movie > movies ) ;
2016-12-29 15:04:01 +00:00
bool MoviePathExists ( string folder ) ;
2017-01-03 12:26:09 +00:00
void RemoveAddOptions ( Movie movie ) ;
2017-01-20 00:58:57 +00:00
List < Movie > MoviesWithFiles ( int movieId ) ;
2018-03-14 20:41:36 +00:00
System . Linq . Expressions . Expression < Func < Movie , bool > > ConstructFilterExpression ( string FilterKey , string FilterValue , string filterType = null ) ;
2016-12-29 15:04:01 +00:00
}
2017-01-03 19:24:55 +00:00
public class MovieService : IMovieService , IHandle < MovieFileAddedEvent > ,
IHandle < MovieFileDeletedEvent >
2016-12-29 15:04:01 +00:00
{
private readonly IMovieRepository _movieRepository ;
2017-02-25 02:30:12 +00:00
private readonly IConfigService _configService ;
2016-12-29 15:04:01 +00:00
private readonly IEventAggregator _eventAggregator ;
private readonly IBuildFileNames _fileNameBuilder ;
2017-05-09 18:44:07 +00:00
private readonly IImportExclusionsService _exclusionService ;
2016-12-29 15:04:01 +00:00
private readonly Logger _logger ;
2017-04-17 11:08:47 +00:00
2016-12-29 15:04:01 +00:00
public MovieService ( IMovieRepository movieRepository ,
IEventAggregator eventAggregator ,
IBuildFileNames fileNameBuilder ,
2017-02-25 02:30:12 +00:00
IConfigService configService ,
2017-05-09 18:44:07 +00:00
IImportExclusionsService exclusionService ,
2016-12-29 15:04:01 +00:00
Logger logger )
{
_movieRepository = movieRepository ;
2017-02-25 21:47:18 +00:00
_eventAggregator = eventAggregator ;
2016-12-29 15:04:01 +00:00
_fileNameBuilder = fileNameBuilder ;
2017-02-25 02:30:12 +00:00
_configService = configService ;
2017-05-09 18:44:07 +00:00
_exclusionService = exclusionService ;
2016-12-29 15:04:01 +00:00
_logger = logger ;
}
2017-02-24 08:22:55 +00:00
2018-03-14 20:41:36 +00:00
public System . Linq . Expressions . Expression < Func < Movie , bool > > ConstructFilterExpression ( string FilterKey , string FilterValue , string FilterType = null )
2017-02-24 08:22:55 +00:00
{
2017-03-09 16:08:38 +00:00
//if (FilterKey == "all" && FilterValue == "all")
//{
// return v => v.Monitored == true || v.Monitored == false;
//}
2017-03-10 06:42:39 +00:00
if ( FilterKey = = "monitored" & & FilterValue = = "false" )
2017-02-24 08:22:55 +00:00
{
return v = > v . Monitored = = false ;
}
2017-03-10 06:42:39 +00:00
else if ( FilterKey = = "monitored" & & FilterValue = = "true" )
2017-02-24 08:22:55 +00:00
{
return v = > v . Monitored = = true ;
}
2017-03-10 06:42:39 +00:00
else if ( FilterKey = = "status" )
2017-02-24 08:22:55 +00:00
{
2017-03-09 16:08:38 +00:00
switch ( FilterValue )
{
case "released" :
return v = > v . Status = = MovieStatusType . Released ;
break ;
case "inCinemas" :
return v = > v . Status = = MovieStatusType . InCinemas ;
break ;
case "announced" :
return v = > v . Status = = MovieStatusType . Announced ;
break ;
case "available" :
return v = > v . Monitored = = true & &
2017-02-24 08:22:55 +00:00
( ( v . MinimumAvailability = = MovieStatusType . Released & & v . Status > = MovieStatusType . Released ) | |
( v . MinimumAvailability = = MovieStatusType . InCinemas & & v . Status > = MovieStatusType . InCinemas ) | |
( v . MinimumAvailability = = MovieStatusType . Announced & & v . Status > = MovieStatusType . Announced ) | |
2017-03-10 23:17:09 +00:00
( v . MinimumAvailability = = MovieStatusType . PreDB & & v . Status > = MovieStatusType . Released | | v . HasPreDBEntry = = true ) ) ;
2017-03-09 16:08:38 +00:00
break ;
}
2017-02-24 08:22:55 +00:00
}
2017-03-10 06:42:39 +00:00
else if ( FilterKey = = "downloaded" )
2017-02-24 08:22:55 +00:00
{
2017-03-09 16:08:38 +00:00
return v = > v . MovieFileId = = 0 ;
2017-02-24 08:22:55 +00:00
}
2017-03-09 16:08:38 +00:00
else if ( FilterKey = = "title" )
2017-02-24 08:22:55 +00:00
{
2017-03-09 16:08:38 +00:00
if ( FilterValue = = string . Empty | | FilterValue = = null )
{
return v = > true ;
}
else
{
if ( FilterType = = "contains" )
{
return v = > v . CleanTitle . Contains ( FilterValue ) ;
}
else
{
return v = > v . CleanTitle = = FilterValue ;
}
}
2017-02-24 08:22:55 +00:00
}
2017-03-09 16:08:38 +00:00
return v = > true ;
2017-02-24 08:22:55 +00:00
}
2016-12-29 15:04:01 +00:00
public Movie GetMovie ( int movieId )
{
return _movieRepository . Get ( movieId ) ;
}
public List < Movie > GetMovies ( IEnumerable < int > movieIds )
{
return _movieRepository . Get ( movieIds ) . ToList ( ) ;
}
2017-02-24 18:52:40 +00:00
public PagingSpec < Movie > Paged ( PagingSpec < Movie > pagingSpec )
{
return _movieRepository . GetPaged ( pagingSpec ) ;
}
2016-12-29 15:04:01 +00:00
public Movie AddMovie ( Movie newMovie )
{
Ensure . That ( newMovie , ( ) = > newMovie ) . IsNotNull ( ) ;
2017-04-26 11:31:55 +00:00
MoviePathState defaultState = MoviePathState . Static ;
if ( ! _configService . PathsDefaultStatic )
{
defaultState = MoviePathState . Dynamic ;
}
2016-12-29 15:04:01 +00:00
if ( string . IsNullOrWhiteSpace ( newMovie . Path ) )
{
var folderName = _fileNameBuilder . GetMovieFolder ( newMovie ) ;
newMovie . Path = Path . Combine ( newMovie . RootFolderPath , folderName ) ;
2017-04-26 11:31:55 +00:00
newMovie . PathState = defaultState ;
}
else
{
newMovie . PathState = defaultState = = MoviePathState . Dynamic ? MoviePathState . StaticOnce : MoviePathState . Static ;
2016-12-29 15:04:01 +00:00
}
2017-04-26 11:31:55 +00:00
_logger . Info ( "Adding Movie {0} Path: [{1}]" , newMovie , newMovie . Path ) ;
2016-12-29 15:04:01 +00:00
newMovie . CleanTitle = newMovie . Title . CleanSeriesTitle ( ) ;
2017-01-04 19:27:14 +00:00
newMovie . SortTitle = MovieTitleNormalizer . Normalize ( newMovie . Title , newMovie . TmdbId ) ;
2016-12-29 15:04:01 +00:00
newMovie . Added = DateTime . UtcNow ;
_movieRepository . Insert ( newMovie ) ;
_eventAggregator . PublishEvent ( new MovieAddedEvent ( GetMovie ( newMovie . Id ) ) ) ;
return newMovie ;
}
2017-01-27 18:36:25 +00:00
public List < Movie > AddMovies ( List < Movie > newMovies )
{
newMovies . ForEach ( m = > Ensure . That ( m , ( ) = > m ) . IsNotNull ( ) ) ;
newMovies . ForEach ( m = >
{
2017-04-26 11:31:55 +00:00
MoviePathState defaultState = MoviePathState . Static ;
if ( ! _configService . PathsDefaultStatic )
{
defaultState = MoviePathState . Dynamic ;
}
2017-01-27 18:36:25 +00:00
if ( string . IsNullOrWhiteSpace ( m . Path ) )
{
var folderName = _fileNameBuilder . GetMovieFolder ( m ) ;
m . Path = Path . Combine ( m . RootFolderPath , folderName ) ;
2017-04-26 11:31:55 +00:00
m . PathState = defaultState ;
}
else
{
m . PathState = defaultState = = MoviePathState . Dynamic ? MoviePathState . StaticOnce : MoviePathState . Static ;
2017-01-27 18:36:25 +00:00
}
m . CleanTitle = m . Title . CleanSeriesTitle ( ) ;
m . SortTitle = MovieTitleNormalizer . Normalize ( m . Title , m . TmdbId ) ;
m . Added = DateTime . UtcNow ;
} ) ;
2017-02-22 10:21:05 +00:00
var existingMovies = GetAllMovies ( ) ;
var potentialMovieCount = newMovies . Count ;
2017-02-21 20:31:31 +00:00
2017-02-22 10:21:05 +00:00
newMovies = newMovies . DistinctBy ( movie = > movie . TmdbId ) . ToList ( ) ; // Ensure we don't add the same movie twice
newMovies = newMovies . ExceptBy ( n = > n . TmdbId , existingMovies , e = > e . TmdbId , EqualityComparer < int > . Default ) . ToList ( ) ; // Ensure we don't add a movie that already exists
2017-02-21 20:31:31 +00:00
2017-01-27 18:36:25 +00:00
_movieRepository . InsertMany ( newMovies ) ;
2017-02-22 10:21:05 +00:00
_logger . Debug ( "Adding {0} movies, {1} duplicates detected and skipped" , newMovies . Count , potentialMovieCount - newMovies . Count ) ;
2017-02-21 20:31:31 +00:00
2018-11-23 07:03:32 +00:00
_eventAggregator . PublishEvent ( new MoviesImportedEvent ( newMovies . Select ( s = > s . Id ) . ToList ( ) ) ) ;
2017-01-27 18:36:25 +00:00
return newMovies ;
}
2016-12-29 15:04:01 +00:00
public Movie FindByTitle ( string title )
{
return _movieRepository . FindByTitle ( title . CleanSeriesTitle ( ) ) ;
}
public Movie FindByImdbId ( string imdbid )
{
return _movieRepository . FindByImdbId ( imdbid ) ;
}
2017-06-18 21:12:14 +00:00
private List < Movie > FindByTitleInexactAll ( string title )
2016-12-29 15:04:01 +00:00
{
// find any movie clean title within the provided release title
string cleanTitle = title . CleanSeriesTitle ( ) ;
2017-06-18 21:12:14 +00:00
var list = _movieRepository . All ( ) . Where ( s = > cleanTitle . Contains ( s . CleanTitle ) )
. Union ( _movieRepository . All ( ) . Where ( s = > s . CleanTitle . Contains ( cleanTitle ) ) ) . ToList ( ) ;
2016-12-29 15:04:01 +00:00
if ( ! list . Any ( ) )
{
// no movie matched
2018-08-05 14:28:05 +00:00
return list ;
2016-12-29 15:04:01 +00:00
}
// build ordered list of movie by position in the search string
2018-08-05 14:28:05 +00:00
var query =
2016-12-29 15:04:01 +00:00
list . Select ( movie = > new
{
position = cleanTitle . IndexOf ( movie . CleanTitle ) ,
length = movie . CleanTitle . Length ,
movie = movie
} )
. Where ( s = > ( s . position > = 0 ) )
. ToList ( )
. OrderBy ( s = > s . position )
. ThenByDescending ( s = > s . length )
2017-06-18 21:12:14 +00:00
. Select ( s = > s . movie )
2016-12-29 15:04:01 +00:00
. ToList ( ) ;
2017-06-18 21:12:14 +00:00
return query ;
}
public Movie FindByTitleInexact ( string title )
{
var query = FindByTitleInexactAll ( title ) ;
2016-12-29 15:04:01 +00:00
// get the leftmost movie that is the longest
// movie are usually the first thing in release title, so we select the leftmost and longest match
2017-06-18 21:12:14 +00:00
var match = query . First ( ) ;
2016-12-29 15:04:01 +00:00
_logger . Debug ( "Multiple movie matched {0} from title {1}" , match . Title , title ) ;
2017-06-18 21:12:14 +00:00
foreach ( var entry in query )
2016-12-29 15:04:01 +00:00
{
_logger . Debug ( "Multiple movie match candidate: {0} cleantitle: {1}" , entry . Title , entry . CleanTitle ) ;
}
return match ;
}
2017-06-18 21:12:14 +00:00
public Movie FindByTitleInexact ( string title , int? year )
{
return FindByTitleInexactAll ( title ) . FirstWithYear ( year ) ;
}
2016-12-29 15:04:01 +00:00
public Movie FindByTitle ( string title , int year )
{
return _movieRepository . FindByTitle ( title . CleanSeriesTitle ( ) , year ) ;
}
2017-02-25 21:38:52 +00:00
public void DeleteMovie ( int movieId , bool deleteFiles , bool addExclusion = false )
2016-12-29 15:04:01 +00:00
{
var movie = _movieRepository . Get ( movieId ) ;
2017-02-25 21:38:52 +00:00
if ( addExclusion )
{
2017-05-09 18:44:07 +00:00
_exclusionService . AddExclusion ( new ImportExclusion { TmdbId = movie . TmdbId , MovieTitle = movie . Title , MovieYear = movie . Year } ) ;
2017-02-25 21:38:52 +00:00
}
2016-12-29 15:04:01 +00:00
_movieRepository . Delete ( movieId ) ;
_eventAggregator . PublishEvent ( new MovieDeletedEvent ( movie , deleteFiles ) ) ;
2018-08-05 14:28:05 +00:00
_logger . Info ( "Deleted movie {}" , movie ) ;
2016-12-29 15:04:01 +00:00
}
public List < Movie > GetAllMovies ( )
{
return _movieRepository . All ( ) . ToList ( ) ;
}
2018-11-23 07:03:32 +00:00
public List < Movie > AllForTag ( int tagId )
{
return GetAllMovies ( ) . Where ( s = > s . Tags . Contains ( tagId ) )
. ToList ( ) ;
}
2016-12-29 15:04:01 +00:00
public Movie UpdateMovie ( Movie movie )
{
var storedMovie = GetMovie ( movie . Id ) ;
var updatedMovie = _movieRepository . Update ( movie ) ;
_eventAggregator . PublishEvent ( new MovieEditedEvent ( updatedMovie , storedMovie ) ) ;
return updatedMovie ;
}
public List < Movie > UpdateMovie ( List < Movie > movie )
{
_logger . Debug ( "Updating {0} movie" , movie . Count ) ;
foreach ( var s in movie )
{
_logger . Trace ( "Updating: {0}" , s . Title ) ;
if ( ! s . RootFolderPath . IsNullOrWhiteSpace ( ) )
{
var folderName = new DirectoryInfo ( s . Path ) . Name ;
s . Path = Path . Combine ( s . RootFolderPath , folderName ) ;
_logger . Trace ( "Changing path for {0} to {1}" , s . Title , s . Path ) ;
}
else
{
_logger . Trace ( "Not changing path for: {0}" , s . Title ) ;
}
}
2018-08-05 14:28:05 +00:00
2016-12-29 15:04:01 +00:00
_movieRepository . UpdateMany ( movie ) ;
_logger . Debug ( "{0} movie updated" , movie . Count ) ;
return movie ;
}
public bool MoviePathExists ( string folder )
{
return _movieRepository . MoviePathExists ( folder ) ;
}
2017-01-03 12:26:09 +00:00
public void RemoveAddOptions ( Movie movie )
{
_movieRepository . SetFields ( movie , s = > s . AddOptions ) ;
}
2017-01-03 19:24:55 +00:00
public void Handle ( MovieFileAddedEvent message )
{
2017-07-28 14:57:26 +00:00
var movie = message . MovieFile . Movie . Value ;
movie . MovieFileId = message . MovieFile . Id ;
_movieRepository . Update ( movie ) ;
//_movieRepository.SetFileId(message.MovieFile.Id, message.MovieFile.Movie.Value.Id);
2017-03-10 18:47:48 +00:00
_logger . Info ( "Linking [{0}] > [{1}]" , message . MovieFile . RelativePath , message . MovieFile . Movie . Value ) ;
2017-01-03 19:24:55 +00:00
}
2017-02-25 12:50:12 +00:00
public void SetFileId ( Movie movie , MovieFile movieFile )
{
_movieRepository . SetFileId ( movieFile . Id , movie . Id ) ;
2017-03-10 18:47:48 +00:00
_logger . Info ( "Linking [{0}] > [{1}]" , movieFile . RelativePath , movie ) ;
2017-02-25 12:50:12 +00:00
}
2017-01-03 19:24:55 +00:00
public void Handle ( MovieFileDeletedEvent message )
{
2018-08-05 14:28:05 +00:00
2017-01-03 19:24:55 +00:00
var movie = _movieRepository . GetMoviesByFileId ( message . MovieFile . Id ) . First ( ) ;
movie . MovieFileId = 0 ;
2017-02-25 02:30:12 +00:00
_logger . Debug ( "Detaching movie {0} from file." , movie . Id ) ;
2018-11-23 07:03:32 +00:00
if ( message . Reason ! = DeleteMediaFileReason . Upgrade & & _configService . AutoUnmonitorPreviouslyDownloadedMovies )
2017-02-25 02:30:12 +00:00
{
movie . Monitored = false ;
}
2017-01-03 19:24:55 +00:00
UpdateMovie ( movie ) ;
}
public Movie GetMovieByFileId ( int fileId )
{
return _movieRepository . GetMoviesByFileId ( fileId ) . First ( ) ;
}
2017-01-09 22:16:54 +00:00
public Movie FindByTitleSlug ( string slug )
{
return _movieRepository . FindByTitleSlug ( slug ) ;
}
2017-01-16 21:40:59 +00:00
public List < Movie > GetMoviesBetweenDates ( DateTime start , DateTime end , bool includeUnmonitored )
{
2018-03-14 20:41:36 +00:00
var movies = _movieRepository . MoviesBetweenDates ( start . ToUniversalTime ( ) , end . ToUniversalTime ( ) , includeUnmonitored ) ;
2017-01-16 21:40:59 +00:00
2018-03-14 20:41:36 +00:00
return movies ;
2017-01-16 21:40:59 +00:00
}
2017-01-18 18:53:17 +00:00
2017-01-20 00:58:57 +00:00
public List < Movie > MoviesWithFiles ( int movieId )
{
return _movieRepository . MoviesWithFiles ( movieId ) ;
}
2017-01-18 18:53:17 +00:00
public PagingSpec < Movie > MoviesWithoutFiles ( PagingSpec < Movie > pagingSpec )
{
var movieResult = _movieRepository . MoviesWithoutFiles ( pagingSpec ) ;
return movieResult ;
}
2017-01-23 13:04:01 +00:00
public bool MovieExists ( Movie movie )
{
Movie result = null ;
if ( movie . TmdbId ! = 0 )
{
result = _movieRepository . FindByTmdbId ( movie . TmdbId ) ;
if ( result ! = null )
{
return true ;
}
}
if ( movie . ImdbId . IsNotNullOrWhiteSpace ( ) )
{
result = _movieRepository . FindByImdbId ( movie . ImdbId ) ;
if ( result ! = null )
{
return true ;
}
}
if ( movie . Year > 1850 )
{
result = _movieRepository . FindByTitle ( movie . Title . CleanSeriesTitle ( ) , movie . Year ) ;
if ( result ! = null )
{
return true ;
}
}
2017-12-12 22:26:16 +00:00
else
{
result = _movieRepository . FindByTitle ( movie . Title . CleanSeriesTitle ( ) ) ;
if ( result ! = null )
{
return true ;
}
}
2017-01-23 13:04:01 +00:00
return false ;
}
2017-07-08 14:13:28 +00:00
public List < Movie > FilterExistingMovies ( List < Movie > movies )
{
var allMovies = GetAllMovies ( ) ;
var withTmdbid = movies . Where ( m = > m . TmdbId ! = 0 ) . ToList ( ) ;
var withoutTmdbid = movies . Where ( m = > m . TmdbId = = 0 ) . ToList ( ) ;
var withImdbid = withoutTmdbid . Where ( m = > m . ImdbId . IsNotNullOrWhiteSpace ( ) ) ;
var rest = withoutTmdbid . Where ( m = > m . ImdbId . IsNullOrWhiteSpace ( ) ) ;
var ret = withTmdbid . ExceptBy ( m = > m . TmdbId , allMovies , m = > m . TmdbId , EqualityComparer < int > . Default )
. Union ( withImdbid . ExceptBy ( m = > m . ImdbId , allMovies , m = > m . ImdbId , EqualityComparer < string > . Default ) )
. Union ( rest . ExceptBy ( m = > m . Title . CleanSeriesTitle ( ) , allMovies , m = > m . CleanTitle , EqualityComparer < string > . Default ) ) . ToList ( ) ;
return ret ;
}
2016-12-29 15:04:01 +00:00
}
}