using System.Collections.Generic; using System.IO; using System.Linq; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Movies; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.RomanNumerals; namespace NzbDrone.Core.Parser { public interface IParsingService { Movie GetMovie(string title); RemoteMovie Map(ParsedMovieInfo parsedMovieInfo, string imdbId, int tmdbId, SearchCriteriaBase searchCriteria = null); RemoteMovie Map(ParsedMovieInfo parsedMovieInfo, int movieId); ParsedMovieInfo ParseMinimalPathMovieInfo(string path); } public class ParsingService : IParsingService { private static HashSet _arabicRomanNumeralMappings; private readonly IMovieService _movieService; private readonly Logger _logger; public ParsingService(IMovieService movieService, Logger logger) { _movieService = movieService; _logger = logger; if (_arabicRomanNumeralMappings == null) { _arabicRomanNumeralMappings = RomanNumeralParser.GetArabicRomanNumeralsMapping(); } } public ParsedMovieInfo ParseMinimalPathMovieInfo(string path) { var fileInfo = new FileInfo(path); var result = Parser.ParseMovieTitle(fileInfo.Name, true); if (result == null) { _logger.Debug("Attempting to parse movie info using directory and file names. '{0}'", fileInfo.Directory.Name); result = Parser.ParseMovieTitle(fileInfo.Directory.Name + " " + fileInfo.Name); } if (result == null) { _logger.Debug("Attempting to parse movie info using directory name. '{0}'", fileInfo.Directory.Name); result = Parser.ParseMovieTitle(fileInfo.Directory.Name + fileInfo.Extension); } return result; } public Movie GetMovie(string title) { var parsedMovieInfo = Parser.ParseMovieTitle(title); if (parsedMovieInfo == null) { return _movieService.FindByTitle(title); } var result = TryGetMovieByTitleAndOrYear(parsedMovieInfo); if (result != null) { return result.Movie; } return null; } public RemoteMovie Map(ParsedMovieInfo parsedMovieInfo, string imdbId, int tmdbId, SearchCriteriaBase searchCriteria = null) { return Map(parsedMovieInfo, imdbId, tmdbId, null, searchCriteria); } public RemoteMovie Map(ParsedMovieInfo parsedMovieInfo, int movieId) { return new RemoteMovie { ParsedMovieInfo = parsedMovieInfo, Movie = _movieService.GetMovie(movieId) }; } public RemoteMovie Map(ParsedMovieInfo parsedMovieInfo, string imdbId, int tmdbId, Movie movie, SearchCriteriaBase searchCriteria) { var remoteMovie = new RemoteMovie { ParsedMovieInfo = parsedMovieInfo }; if (movie == null) { var movieMatch = FindMovie(parsedMovieInfo, imdbId, tmdbId, searchCriteria); if (movieMatch != null) { movie = movieMatch.Movie; remoteMovie.MovieMatchType = movieMatch.MatchType; } } if (movie != null) { remoteMovie.Movie = movie; } remoteMovie.Languages = parsedMovieInfo.Languages; return remoteMovie; } private FindMovieResult FindMovie(ParsedMovieInfo parsedMovieInfo, string imdbId, int tmdbId, SearchCriteriaBase searchCriteria) { FindMovieResult result = null; if (!string.IsNullOrWhiteSpace(imdbId) && imdbId != "0") { result = TryGetMovieByImDbId(parsedMovieInfo, imdbId); } if (result == null && tmdbId > 0) { result = TryGetMovieByTmdbId(parsedMovieInfo, tmdbId); } if (result == null) { if (searchCriteria != null) { result = TryGetMovieBySearchCriteria(parsedMovieInfo, imdbId, tmdbId, searchCriteria); } else { result = TryGetMovieByTitleAndOrYear(parsedMovieInfo); } } if (result == null) { _logger.Debug($"No matching movie for titles '{string.Join(", ", parsedMovieInfo.MovieTitles)} ({parsedMovieInfo.Year})'"); } return result; } private FindMovieResult TryGetMovieByImDbId(ParsedMovieInfo parsedMovieInfo, string imdbId) { var movie = _movieService.FindByImdbId(imdbId); // Should fix practically all problems, where indexer is shite at adding correct imdbids to movies. if (movie != null && (parsedMovieInfo.Year < 1800 || movie.MovieMetadata.Value.Year == parsedMovieInfo.Year || movie.MovieMetadata.Value.SecondaryYear == parsedMovieInfo.Year)) { return new FindMovieResult(movie, MovieMatchType.Id); } return null; } private FindMovieResult TryGetMovieByTmdbId(ParsedMovieInfo parsedMovieInfo, int tmdbId) { var movie = _movieService.FindByTmdbId(tmdbId); // Should fix practically all problems, where indexer is shite at adding correct imdbids to movies. if (movie != null && (parsedMovieInfo.Year < 1800 || movie.MovieMetadata.Value.Year == parsedMovieInfo.Year || movie.MovieMetadata.Value.SecondaryYear == parsedMovieInfo.Year)) { return new FindMovieResult(movie, MovieMatchType.Id); } return null; } private FindMovieResult TryGetMovieByTitleAndOrYear(ParsedMovieInfo parsedMovieInfo) { var candidates = _movieService.FindByTitleCandidates(parsedMovieInfo.MovieTitles, out var otherTitles); Movie movieByTitleAndOrYear; if (parsedMovieInfo.Year > 1800) { movieByTitleAndOrYear = _movieService.FindByTitle(parsedMovieInfo.MovieTitles, parsedMovieInfo.Year, otherTitles, candidates); if (movieByTitleAndOrYear != null) { return new FindMovieResult(movieByTitleAndOrYear, MovieMatchType.Title); } return null; } movieByTitleAndOrYear = _movieService.FindByTitle(parsedMovieInfo.MovieTitles, null, otherTitles, candidates); if (movieByTitleAndOrYear != null) { return new FindMovieResult(movieByTitleAndOrYear, MovieMatchType.Title); } return null; } private FindMovieResult TryGetMovieBySearchCriteria(ParsedMovieInfo parsedMovieInfo, string imdbId, int tmdbId, SearchCriteriaBase searchCriteria) { Movie possibleMovie = null; var possibleTitles = new List { searchCriteria.Movie.MovieMetadata.Value.CleanTitle }; possibleTitles.AddIfNotNull(searchCriteria.Movie.MovieMetadata.Value.CleanOriginalTitle); possibleTitles.AddRange(searchCriteria.Movie.MovieMetadata.Value.AlternativeTitles.Select(t => t.CleanTitle)); possibleTitles.AddRange(searchCriteria.Movie.MovieMetadata.Value.Translations.Select(t => t.CleanTitle)); var cleanTitles = parsedMovieInfo.MovieTitles.Select(t => t.CleanMovieTitle()).ToArray(); if (possibleTitles.Any(pt => cleanTitles.Contains(pt) || _arabicRomanNumeralMappings.Any(mn => cleanTitles.Contains(pt.Replace(mn.ArabicNumeralAsString, mn.RomanNumeralLowerCase)) || cleanTitles.Any(t => t.Replace(mn.ArabicNumeralAsString, mn.RomanNumeralLowerCase) == pt)))) { possibleMovie = searchCriteria.Movie; } if (possibleMovie != null) { if (parsedMovieInfo.Year < 1800 || possibleMovie.MovieMetadata.Value.Year == parsedMovieInfo.Year || possibleMovie.MovieMetadata.Value.SecondaryYear == parsedMovieInfo.Year) { return new FindMovieResult(possibleMovie, MovieMatchType.Title); } } if (tmdbId > 0 && tmdbId == searchCriteria.Movie.TmdbId) { return new FindMovieResult(searchCriteria.Movie, MovieMatchType.Id); } if (imdbId.IsNotNullOrWhiteSpace() && imdbId == searchCriteria.Movie.ImdbId) { return new FindMovieResult(searchCriteria.Movie, MovieMatchType.Id); } return null; } } }