diff --git a/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs index fbc344945..51eb510a8 100644 --- a/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs @@ -77,7 +77,7 @@ namespace NzbDrone.Core.Test.ParserTests } [TestCase("1941.1979.EXTENDED.720p.BluRay.X264-AMIABLE", 1979)] - public void should_parse_movie_year(string postTitle, int year) + public void should_parse_movie_year(string postTitle, int year) { Parser.Parser.ParseMovieTitle(postTitle).Year.Should().Be(year); } diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs index b11ee0dd4..4a1bc3aba 100644 --- a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs +++ b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs @@ -66,31 +66,28 @@ namespace NzbDrone.Core.DecisionEngine try { - var parsedEpisodeInfo = Parser.Parser.ParseMovieTitle(report.Title); + var parsedMovieInfo = Parser.Parser.ParseMovieTitle(report.Title); - if (parsedEpisodeInfo != null && !parsedEpisodeInfo.MovieTitle.IsNullOrWhiteSpace()) + if (parsedMovieInfo != null && !parsedMovieInfo.MovieTitle.IsNullOrWhiteSpace()) { - RemoteMovie remoteEpisode = _parsingService.Map(parsedEpisodeInfo, "", searchCriteria); - remoteEpisode.Release = report; + RemoteMovie remoteMovie = _parsingService.Map(parsedMovieInfo, report.ImdbId.ToString(), searchCriteria); + remoteMovie.Release = report; - if (remoteEpisode.Movie == null) + if (remoteMovie.Movie == null) { - //remoteEpisode.DownloadAllowed = true; //Fuck you :) - //decision = GetDecisionForReport(remoteEpisode, searchCriteria); - decision = new DownloadDecision(remoteEpisode, new Rejection("Unknown release. Movie not Found.")); + decision = new DownloadDecision(remoteMovie, new Rejection("Unknown movie. Cannot parse release name.")); } else { - if (parsedEpisodeInfo.Quality.HardcodedSubs.IsNotNullOrWhiteSpace()) + if (parsedMovieInfo.Quality.HardcodedSubs.IsNotNullOrWhiteSpace()) { - remoteEpisode.DownloadAllowed = true; - decision = new DownloadDecision(remoteEpisode, new Rejection("Hardcoded subs found: " + parsedEpisodeInfo.Quality.HardcodedSubs)); + remoteMovie.DownloadAllowed = true; + decision = new DownloadDecision(remoteMovie, new Rejection("Hardcoded subs found: " + parsedMovieInfo.Quality.HardcodedSubs)); } else { - remoteEpisode.DownloadAllowed = true; - decision = GetDecisionForReport(remoteEpisode, searchCriteria); - //decision = new DownloadDecision(remoteEpisode); + remoteMovie.DownloadAllowed = true; + decision = GetDecisionForReport(remoteMovie, searchCriteria); } } @@ -100,8 +97,8 @@ namespace NzbDrone.Core.DecisionEngine { _logger.Error(e, "Couldn't process release."); - var remoteEpisode = new RemoteEpisode { Release = report }; - decision = new DownloadDecision(remoteEpisode, new Rejection("Unexpected error processing release")); + var remoteMovie = new RemoteMovie { Release = report }; + decision = new DownloadDecision(remoteMovie, new Rejection("Unexpected error processing release")); } reportNumber++; @@ -244,11 +241,11 @@ namespace NzbDrone.Core.DecisionEngine return null; } - private Rejection EvaluateSpec(IDecisionEngineSpecification spec, RemoteMovie remoteEpisode, SearchCriteriaBase searchCriteriaBase = null) + private Rejection EvaluateSpec(IDecisionEngineSpecification spec, RemoteMovie remoteMovie, SearchCriteriaBase searchCriteriaBase = null) { try { - var result = spec.IsSatisfiedBy(remoteEpisode, searchCriteriaBase); + var result = spec.IsSatisfiedBy(remoteMovie, searchCriteriaBase); if (!result.Accepted) { @@ -261,9 +258,9 @@ namespace NzbDrone.Core.DecisionEngine } catch (Exception e) { - e.Data.Add("report", remoteEpisode.Release.ToJson()); - e.Data.Add("parsed", remoteEpisode.ParsedEpisodeInfo.ToJson()); - _logger.Error(e, "Couldn't evaluate decision on " + remoteEpisode.Release.Title + ", with spec: " + spec.GetType().Name); + e.Data.Add("report", remoteMovie.Release.ToJson()); + e.Data.Add("parsed", remoteMovie.ParsedMovieInfo.ToJson()); + _logger.Error(e, "Couldn't evaluate decision on " + remoteMovie.Release.Title + ", with spec: " + spec.GetType().Name); return new Rejection(string.Format("{0}: {1}", spec.GetType().Name, e.Message));//TODO UPDATE SPECS! } diff --git a/src/NzbDrone.Core/Indexers/Newznab/NewznabRssParser.cs b/src/NzbDrone.Core/Indexers/Newznab/NewznabRssParser.cs index c06894ec2..d9301a14b 100644 --- a/src/NzbDrone.Core/Indexers/Newznab/NewznabRssParser.cs +++ b/src/NzbDrone.Core/Indexers/Newznab/NewznabRssParser.cs @@ -57,20 +57,22 @@ namespace NzbDrone.Core.Indexers.Newznab releaseInfo = base.ProcessItem(item, releaseInfo); releaseInfo.ImdbId = GetImdbId(item); - var imdbMovieTitle = GetImdbTitle(item); - var imdbYear = GetImdbYear(item); - // Fun, lets try to add year to the releaseTitle for our foriegn friends :) - // if (!releaseInfo.Title.ContainsIgnoreCase(imdbMovieTitle + "." + imdbYear)) - var isMatch = Regex.IsMatch(releaseInfo.Title, $@"^{imdbMovieTitle}.*{imdbYear}", RegexOptions.IgnoreCase); - if (!isMatch) - { - if (imdbYear != 1900 && imdbMovieTitle != string.Empty) - { - // releaseInfo.Title = releaseInfo.Title.Replace(imdbMovieTitle, imdbMovieTitle + "." + imdbYear); - releaseInfo.Title = Regex.Replace(releaseInfo.Title, imdbMovieTitle, imdbMovieTitle + "." + imdbYear, RegexOptions.IgnoreCase); - } - } + //// This shouldn't be needed with changes to the DownloadDecisionMaker + //var imdbMovieTitle = GetImdbTitle(item); + //var imdbYear = GetImdbYear(item); + + //// Fun, lets try to add year to the releaseTitle for our foriegn friends :) + //// if (!releaseInfo.Title.ContainsIgnoreCase(imdbMovieTitle + "." + imdbYear)) + //var isMatch = Regex.IsMatch(releaseInfo.Title, $@"^{imdbMovieTitle}.*{imdbYear}", RegexOptions.IgnoreCase); + //if (!isMatch) + //{ + // if (imdbYear != 1900 && imdbMovieTitle != string.Empty) + // { + // // releaseInfo.Title = releaseInfo.Title.Replace(imdbMovieTitle, imdbMovieTitle + "." + imdbYear); + // releaseInfo.Title = Regex.Replace(releaseInfo.Title, imdbMovieTitle, imdbMovieTitle + "." + imdbYear, RegexOptions.IgnoreCase); + // } + //} return releaseInfo; } diff --git a/src/NzbDrone.Core/Parser/Parser.cs b/src/NzbDrone.Core/Parser/Parser.cs index b87844679..1f46ecb18 100644 --- a/src/NzbDrone.Core/Parser/Parser.cs +++ b/src/NzbDrone.Core/Parser/Parser.cs @@ -18,25 +18,24 @@ namespace NzbDrone.Core.Parser private static readonly Regex[] ReportMovieTitleRegex = new[] { //Special, Despecialized, etc. Edition Movies, e.g: Mission.Impossible.3.Special.Edition.2011 - new Regex(@"^(?(?![(\[]).+?)?(?:(?:[-_\W](?<![)\[!]))*(?<edition>(\.?((Extended.|Ultimate.)?(Director.?s|Collector.?s|Theatrical|Ultimate|Final|Extended|Rogue|Special|Despecialized).(Cut|Edition|Version)|Extended|Uncensored|Remastered|Unrated|Uncut|IMAX)))\.(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)", + new Regex(@"^(?<title>(?![(\[]).+?)?(?:(?:[-_\W](?<![)\[!]))*(?<edition>(\.?((Extended.|Ultimate.)?(Director.?s|Collector.?s|Theatrical|Ultimate|Final|Extended|Rogue|Special|Despecialized).(Cut|Edition|Version)|Extended|Uncensored|Remastered|Unrated|Uncut|IMAX)))\.(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled), - //Special, Despecialized, etc. Edition Movies, e.g: Mission.Impossible.3.2011.Special.Edition //TODO: Seems to slow down parsing heavily! - new Regex(@"^(?<title>(?![(\[]).+?)?(?:(?:[-_\W](?<![)\[!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)(?<edition>((Extended.|Ultimate.)?(Director.?s|Collector.?s|Theatrical|Ultimate|Final|Extended|Rogue|Special|Despecialized).(Cut|Edition|Version)|Extended|Uncensored|Remastered|Unrated|Uncut|IMAX))", + + //Special, Despecialized, etc. Edition Movies, e.g: Mission.Impossible.3.2011.Special.Edition //TODO: Seems to slow down parsing heavily! + new Regex(@"^(?<title>(?![(\[]).+?)?(?:(?:[-_\W](?<![)\[!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)(?<edition>((Extended.|Ultimate.)?(Director.?s|Collector.?s|Theatrical|Ultimate|Final|Extended|Rogue|Special|Despecialized).(Cut|Edition|Version)|Extended|Uncensored|Remastered|Unrated|Uncut|IMAX))", RegexOptions.IgnoreCase | RegexOptions.Compiled), - //Normal movie format, e.g: Mission.Impossible.3.2011 - new Regex(@"^(?<title>(?![(\[]).+?)?(?:(?:[-_\W](?<![)\[!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - //PassThePopcorn Torrent names: Star.Wars[PassThePopcorn] - new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![()\[!]))*(?<year>(\[\w *\])))+(\W+|_|$)(?!\\)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), - //That did not work? Maybe some tool uses [] for years. Who would do that? - new Regex(@"^(?<title>(?![(\[]).+?)?(?:(?:[-_\W](?<![)!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\W\d+)))+(\W+|_|$)(?!\\)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), + //Normal movie format, e.g: Mission.Impossible.3.2011 + new Regex(@"^(?<title>(?![(\[]).+?)?(?:(?:[-_\W](?<![)\[!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled), + + //PassThePopcorn Torrent names: Star.Wars[PassThePopcorn] + new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![()\[!]))*(?<year>(\[\w *\])))+(\W+|_|$)(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled), + + //That did not work? Maybe some tool uses [] for years. Who would do that? + new Regex(@"^(?<title>(?![(\[]).+?)?(?:(?:[-_\W](?<![)!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\W\d+)))+(\W+|_|$)(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled), //As a last resort for movies that have ( or [ in their title. - new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), + new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled), }; @@ -598,7 +597,17 @@ namespace NzbDrone.Core.Parser t = t.Replace("ß", "ss"); return t; } - + + public static string NormalizeImdbId(string imdbId) + { + if (imdbId.Length > 2) + { + return (imdbId.Substring(2) != "tt" ? $"tt{imdbId}" : imdbId); + } + + return null; + } + public static string ParseSeriesName(string title) { Logger.Debug("Parsing string '{0}'", title); diff --git a/src/NzbDrone.Core/Parser/ParsingService.cs b/src/NzbDrone.Core/Parser/ParsingService.cs index 7ad4d8afd..52db77468 100644 --- a/src/NzbDrone.Core/Parser/ParsingService.cs +++ b/src/NzbDrone.Core/Parser/ParsingService.cs @@ -177,27 +177,26 @@ namespace NzbDrone.Core.Parser public Movie GetMovie(string title) { - var parsedEpisodeInfo = Parser.ParseMovieTitle(title); + var parsedMovieInfo = Parser.ParseMovieTitle(title); - if (parsedEpisodeInfo == null) + if (parsedMovieInfo == null) { return _movieService.FindByTitle(title); } - var series = _movieService.FindByTitle(parsedEpisodeInfo.MovieTitle); + var movies = _movieService.FindByTitle(parsedMovieInfo.MovieTitle); - if (series == null) + if (movies == null) { - series = _movieService.FindByTitle(parsedEpisodeInfo.MovieTitleInfo.TitleWithoutYear, - parsedEpisodeInfo.MovieTitleInfo.Year); + movies = _movieService.FindByTitle(parsedMovieInfo.MovieTitleInfo.TitleWithoutYear, parsedMovieInfo.MovieTitleInfo.Year); } - if (series == null) + if (movies == null) { - series = _movieService.FindByTitle(parsedEpisodeInfo.MovieTitle.Replace("DC", "").Trim()); + movies = _movieService.FindByTitle(parsedMovieInfo.MovieTitle.Replace("DC", "").Trim()); } - return series; + return movies; } public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvdbId, int tvRageId, SearchCriteriaBase searchCriteria = null) @@ -220,23 +219,23 @@ namespace NzbDrone.Core.Parser return remoteEpisode; } - public RemoteMovie Map(ParsedMovieInfo parsedEpisodeInfo, string imdbId, SearchCriteriaBase searchCriteria = null) + public RemoteMovie Map(ParsedMovieInfo parsedMovieInfo, string imdbId, SearchCriteriaBase searchCriteria = null) { - var remoteEpisode = new RemoteMovie + var remoteMovie = new RemoteMovie { - ParsedMovieInfo = parsedEpisodeInfo, + ParsedMovieInfo = parsedMovieInfo, }; - var movie = GetMovie(parsedEpisodeInfo, imdbId, searchCriteria); + var movie = GetMovie(parsedMovieInfo, imdbId, searchCriteria); if (movie == null) { - return remoteEpisode; + return remoteMovie; } - remoteEpisode.Movie = movie; + remoteMovie.Movie = movie; - return remoteEpisode; + return remoteMovie; } public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int seriesId, IEnumerable<int> episodeIds) @@ -353,7 +352,7 @@ namespace NzbDrone.Core.Parser return null; } - private Movie GetMovie(ParsedMovieInfo parsedEpisodeInfo, string imdbId, SearchCriteriaBase searchCriteria) + private Movie GetMovie(ParsedMovieInfo parsedMovieInfo, string imdbId, SearchCriteriaBase searchCriteria) { if (searchCriteria != null) { @@ -370,7 +369,7 @@ namespace NzbDrone.Core.Parser foreach (string title in possibleTitles) { - if (title == parsedEpisodeInfo.MovieTitle.CleanSeriesTitle()) + if (title == parsedMovieInfo.MovieTitle.CleanSeriesTitle()) { possibleMovie = searchCriteria.Movie; } @@ -380,19 +379,19 @@ namespace NzbDrone.Core.Parser string num = entry.Key; string roman = entry.Value.ToLower(); - if (title.Replace(num, roman) == parsedEpisodeInfo.MovieTitle.CleanSeriesTitle()) + if (title.Replace(num, roman) == parsedMovieInfo.MovieTitle.CleanSeriesTitle()) { possibleMovie = searchCriteria.Movie; } - if (title.Replace(roman, num) == parsedEpisodeInfo.MovieTitle.CleanSeriesTitle()) + if (title.Replace(roman, num) == parsedMovieInfo.MovieTitle.CleanSeriesTitle()) { possibleMovie = searchCriteria.Movie; } } } - if (possibleMovie != null && (parsedEpisodeInfo.Year < 1800 || possibleMovie.Year == parsedEpisodeInfo.Year)) + if (possibleMovie != null && (parsedMovieInfo.Year < 1800 || possibleMovie.Year == parsedMovieInfo.Year)) { return possibleMovie; } @@ -404,21 +403,20 @@ namespace NzbDrone.Core.Parser if (searchCriteria == null) { - if (parsedEpisodeInfo.Year > 1900) + if (parsedMovieInfo.Year > 1900) { - movie = _movieService.FindByTitle(parsedEpisodeInfo.MovieTitle, parsedEpisodeInfo.Year); - + movie = _movieService.FindByTitle(parsedMovieInfo.MovieTitle, parsedMovieInfo.Year); } else { - movie = _movieService.FindByTitle(parsedEpisodeInfo.MovieTitle); + movie = _movieService.FindByTitle(parsedMovieInfo.MovieTitle); } if (movie == null) { - movie = _movieService.FindByTitle(parsedEpisodeInfo.MovieTitle); + movie = _movieService.FindByTitle(parsedMovieInfo.MovieTitle); } - return movie; + // return movie; } @@ -430,7 +428,7 @@ namespace NzbDrone.Core.Parser if (movie == null) { - _logger.Debug("No matching movie {0}", parsedEpisodeInfo.MovieTitle); + _logger.Debug($"No matching movie {parsedMovieInfo.MovieTitle}"); return null; } diff --git a/src/NzbDrone.Core/Tv/MovieRepository.cs b/src/NzbDrone.Core/Tv/MovieRepository.cs index 816b6f35d..46e485504 100644 --- a/src/NzbDrone.Core/Tv/MovieRepository.cs +++ b/src/NzbDrone.Core/Tv/MovieRepository.cs @@ -150,7 +150,8 @@ namespace NzbDrone.Core.Tv public Movie FindByImdbId(string imdbid) { - return Query.Where(s => s.ImdbId == imdbid).SingleOrDefault(); + var imdbIdWithPrefix = Parser.Parser.NormalizeImdbId(imdbid); + return Query.Where(s => s.ImdbId == imdbIdWithPrefix).SingleOrDefault(); } public List<Movie> GetMoviesByFileId(int fileId) diff --git a/src/UI/Content/icons.less b/src/UI/Content/icons.less index 3e2026807..51f8caab4 100644 --- a/src/UI/Content/icons.less +++ b/src/UI/Content/icons.less @@ -336,7 +336,7 @@ } .icon-sonarr-navbar-series { - .fa-icon-content(@fa-var-play); + .fa-icon-content(@fa-var-film); } .icon-sonarr-navbar-calendar { diff --git a/src/UI/Content/navbar.less b/src/UI/Content/navbar.less index be535a779..00bee18e5 100644 --- a/src/UI/Content/navbar.less +++ b/src/UI/Content/navbar.less @@ -98,24 +98,24 @@ border-radius : 6px; padding : 5px 0px 5px; min-height : 76px; - min-width : 64px; + min-width : 50px; margin : 20px 5px 5px; } @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { border-radius : 6px; - padding : 15px 10px 5px; + padding : 15px 5px 5px; min-height : 76px; - min-width : 64px; - margin : 20px 10px 5px; + min-width : 50px; + margin : 20px 5px 5px; } @media (min-width: @screen-lg-min) { border-radius : 6px; - padding : 15px 10px 5px; + padding : 15px 5px 5px; min-height : 76px; min-width : 84px; - margin : 20px 10px 5px; + margin : 20px 5px 5px; } } diff --git a/src/UI/Navbar/NavbarLayoutTemplate.hbs b/src/UI/Navbar/NavbarLayoutTemplate.hbs index 70194596a..cb2237964 100644 --- a/src/UI/Navbar/NavbarLayoutTemplate.hbs +++ b/src/UI/Navbar/NavbarLayoutTemplate.hbs @@ -20,6 +20,7 @@ <div class="navbar-collapse collapse x-navbar-collapse"> <ul class="nav navbar-nav"> <li><a href="{{UrlBase}}/" class="x-series-nav"><i class="icon-sonarr-navbar-icon icon-sonarr-navbar-series"></i> Movies</a></li> + <li><a href="{{UrlBase}}/addmovies" class="x-series-nav"><i class="icon-sonarr-navbar-icon icon-sonarr-add"></i> Add Movies</a></li> <li><a href="{{UrlBase}}/calendar" class="x-calendar-nav"><i class="icon-sonarr-navbar-icon icon-sonarr-navbar-calendar"></i> Calendar</a></li> <li><a href="{{UrlBase}}/activity" class="x-activity-nav"><i class="icon-sonarr-navbar-icon icon-sonarr-navbar-activity"></i> Activity<span id="x-queue-count" class="navbar-info"></span></a></li> <li><a href="{{UrlBase}}/wanted" class="x-wanted-nav"><i class="icon-sonarr-navbar-icon icon-sonarr-navbar-wanted"></i> Wanted</a></li>