Added: Options to make parsing more lenient. (Adds support for some german and french releasegroups) (#1692)

Fixes #1676. Fixes #1632.
pull/1696/head
Leonardo Galli 7 years ago committed by GitHub
parent 5238b78813
commit d6cf53e12c

@ -20,6 +20,5 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Sonarr node_modules" level="project" />
</component>
</module>

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<includedPredefinedLibrary name="ECMAScript 6" />
<excludedPredefinedLibrary name="Radarr/node_modules" />
</component>
</project>

@ -1,14 +0,0 @@
<component name="libraryTable">
<library name="Sonarr node_modules" type="javaScript">
<properties>
<option name="frameworkName" value="node_modules" />
<sourceFilesUrls>
<item url="file://$PROJECT_DIR$/node_modules" />
</sourceFilesUrls>
</properties>
<CLASSES>
<root url="file://$PROJECT_DIR$/node_modules" />
</CLASSES>
<SOURCES />
</library>
</component>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RiderRiderContentModelStore">
<excludedPaths />
</component>
</project>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/.idea.NzbDrone/riderModule.iml" filepath="$PROJECT_DIR$/.idea/.idea.NzbDrone/riderModule.iml" />
</modules>
</component>
</project>

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="RIDER_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$/../../../Logo/1024.png">
<sourceFolder url="file://$MODULE_DIR$/../../../Logo/1024.png" isTestSource="false" />
</content>
<content url="file://$MODULE_DIR$/../../../Logo/64.png">
<sourceFolder url="file://$MODULE_DIR$/../../../Logo/64.png" isTestSource="false" />
</content>
<content url="file://$MODULE_DIR$/../..">
<sourceFolder url="file://$MODULE_DIR$/../../.nuget/NuGet.exe" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Common/CommonAssemblyInfo.cs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Common/CommonVersionInfo.cs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Common/GlobalSuppressions.cs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../ExternalModules/CurlSharp/CurlSharp" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Libraries/MediaInfo/libmediainfo.0.dylib" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Libraries/MediaInfo/MediaInfo.dll" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Libraries/Sqlite/libsqlite3.0.dylib" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Libraries/Sqlite/sqlite3.dll" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../LogentriesCore" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../LogentriesNLog" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Marr.Data" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Microsoft.AspNet.SignalR.Core" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../Microsoft.AspNet.SignalR.Owin" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../MonoTorrent" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Api" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Api.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.App.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Automation.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Common" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Common.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Console" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Core" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Core.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Host" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Integration.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Libraries.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Mono" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Mono.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.SignalR" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Test.Common" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Test.Dummy" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Update" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Update.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Windows" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../NzbDrone.Windows.Test" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../ServiceHelpers/ServiceInstall" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../ServiceHelpers/ServiceUninstall" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/../../ExternalModules/CurlSharp/CurlSharp/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../ExternalModules/CurlSharp/CurlSharp/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../LogentriesCore/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../LogentriesCore/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../LogentriesNLog/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../LogentriesNLog/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../Marr.Data/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../Microsoft.AspNet.SignalR.Core/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../Microsoft.AspNet.SignalR.Core/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../Microsoft.AspNet.SignalR.Owin/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../Microsoft.AspNet.SignalR.Owin/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../MonoTorrent/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Api.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Api.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Api/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.App.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.App.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Automation.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Automation.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Common.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Common.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Common/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Console/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Core.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Core.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Core/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Host/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Integration.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Integration.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Libraries.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Libraries.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Mono.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Mono.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Mono/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.SignalR/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Test.Common/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Test.Common/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Test.Dummy/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Test.Dummy/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Update.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Update.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Update/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Windows.Test/bin" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Windows.Test/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone.Windows/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../NzbDrone/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../ServiceHelpers/ServiceInstall/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../ServiceHelpers/ServiceUninstall/obj" />
<excludeFolder url="file://$MODULE_DIR$/../../_ReSharper.Caches/ReSharperHost8.NzbDrone.00" />
<excludeFolder url="file://$MODULE_DIR$/../../packages" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

@ -1,5 +1,6 @@
using NzbDrone.Api.REST;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Parser;
namespace NzbDrone.Api.Config
{
@ -12,6 +13,7 @@ namespace NzbDrone.Api.Config
public int AvailabilityDelay { get; set; }
public bool AllowHardcodedSubs { get; set; }
public string WhitelistedHardcodedSubs { get; set; }
public ParsingLeniencyType ParsingLeniency { get; set; }
}
public static class IndexerConfigResourceMapper
@ -27,7 +29,7 @@ namespace NzbDrone.Api.Config
AvailabilityDelay = model.AvailabilityDelay,
AllowHardcodedSubs = model.AllowHardcodedSubs,
WhitelistedHardcodedSubs = model.WhitelistedHardcodedSubs,
ParsingLeniency = model.ParsingLeniency,
};
}
}

@ -90,7 +90,7 @@ namespace NzbDrone.Api.Movie
return mappedMovie;
}
var parsedTitle = Parser.ParseMoviePath(f.Name);
var parsedTitle = Parser.ParseMoviePath(f.Name, false);
if (parsedTitle == null)
{
m = new Core.Tv.Movie

@ -51,9 +51,12 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Castle.2009.S01E14.HDTV.XviD.HUN-LOL", Language.Hungarian)]
[TestCase("The Danish Girl 2015", Language.English)]
[TestCase("Passengers.2016.German.DL.AC3.Dubbed.1080p.WebHD.h264.iNTERNAL-PsO", Language.German)]
[TestCase("Der.Soldat.James.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", Language.German)]
[TestCase("Passengers.German.DL.AC3.Dubbed..BluRay.x264-PsO", Language.German)]
[TestCase("Valana la Legende FRENCH BluRay 720p 2016 kjhlj", Language.French)]
public void should_parse_language(string postTitle, Language language)
{
var result = Parser.Parser.ParseMovieTitle(postTitle);
var result = Parser.Parser.ParseMovieTitle(postTitle, true);
if (result == null)
{
Parser.Parser.ParseTitle(postTitle).Language.Should().Be(language);

@ -63,6 +63,7 @@ namespace NzbDrone.Core.Test.ParserTests
Parser.Parser.ParseTitle(postTitle).SeriesTitle.Should().Be(title);
}
//Note: This assumes extended language parser is activated
[TestCase("The.Man.from.U.N.C.L.E.2015.1080p.BluRay.x264-SPARKS", "The Man from U.N.C.L.E.")]
[TestCase("1941.1979.EXTENDED.720p.BluRay.X264-AMIABLE", "1941")]
[TestCase("MY MOVIE (2016) [R][Action, Horror][720p.WEB-DL.AVC.8Bit.6ch.AC3].mkv", "MY MOVIE")]
@ -76,22 +77,29 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("A.Movie.Name.(1998)", "A Movie Name")]
[TestCase("Thor: The Dark World 2013", "Thor The Dark World")]
[TestCase("Resident.Evil.The.Final.Chapter.2016", "Resident Evil The Final Chapter")]
[TestCase("Der.Soldat.James.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", "Der Soldat James")]
[TestCase("Passengers.German.DL.AC3.Dubbed..BluRay.x264-PsO", "Passengers")]
[TestCase("Valana la Legende FRENCH BluRay 720p 2016 kjhlj", "Valana la Legende")]
[TestCase("Valana la Legende TRUEFRENCH BluRay 720p 2016 kjhlj", "Valana la Legende")]
[TestCase("Mission Impossible: Rogue Nation (2015)<29>[XviD - Ita Ac3 - SoftSub Ita]azione, spionaggio, thriller *Prima Visione* Team mulnic Tom Cruise", "Mission Impossible Rogue Nation")]
public void should_parse_movie_title(string postTitle, string title)
{
Parser.Parser.ParseMovieTitle(postTitle).MovieTitle.Should().Be(title);
Parser.Parser.ParseMovieTitle(postTitle, true).MovieTitle.Should().Be(title);
}
[TestCase("1941.1979.EXTENDED.720p.BluRay.X264-AMIABLE", 1979)]
[TestCase("Valana la Legende FRENCH BluRay 720p 2016 kjhlj", 2016)]
[TestCase("Der.Soldat.James.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", 1998)]
public void should_parse_movie_year(string postTitle, int year)
{
Parser.Parser.ParseMovieTitle(postTitle).Year.Should().Be(year);
Parser.Parser.ParseMovieTitle(postTitle, false).Year.Should().Be(year);
}
[TestCase("The Danish Girl 2015")]
[TestCase("The.Danish.Girl.2015.1080p.BluRay.x264.DTS-HD.MA.5.1-RARBG")]
public void should_not_parse_language_in_movie_title(string postTitle)
{
Parser.Parser.ParseMovieTitle(postTitle).Language.Should().Be(Language.English);
Parser.Parser.ParseMovieTitle(postTitle, false).Language.Should().Be(Language.English);
}
[TestCase("Prometheus 2012 Directors Cut", "Directors Cut")]
@ -117,9 +125,9 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Prometheus Extended Directors Cut Fan Edit 2012", "Extended Directors Cut Fan Edit")]
[TestCase("Prometheus Director's Cut 2012", "Director's Cut")]
[TestCase("Prometheus Directors Cut 2012", "Directors Cut")]
[TestCase("Prometheus.(Extended.Theatrical.Version.IMAX).BluRay.1080p.2012.asdf", "Extended Theatrical Version IMAX")]
[TestCase("Prometheus.(Extended.Theatrical.Version.IMAX).2012.BluRay.1080p.asdf", "Extended Theatrical Version IMAX")]
[TestCase("2001 A Space Odyssey Director's Cut (1968).mkv", "Director's Cut")]
[TestCase("2001: A Space Odyssey (Extended Directors Cut FanEdit) Bluray 1080p 1968", "Extended Directors Cut FanEdit")]
[TestCase("2001: A Space Odyssey (Extended Directors Cut FanEdit) 1968 Bluray 1080p", "Extended Directors Cut FanEdit")]
[TestCase("A Fake Movie 2035 Directors 2012.mkv", "Directors")]
[TestCase("Blade Runner Director's Cut 2049.mkv", "Director's Cut")]
[TestCase("Prometheus 50th Anniversary Edition 2012.mkv", "50th Anniversary Edition")]
@ -129,7 +137,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Fake Movie 2016 Final Cut ", "Final Cut")]
public void should_parse_edition(string postTitle, string edition)
{
Parser.Parser.ParseMovieTitle(postTitle).Edition.Should().Be(edition);
Parser.Parser.ParseMovieTitle(postTitle, false).Edition.Should().Be(edition);
}
}
}

@ -28,7 +28,7 @@ namespace NzbDrone.Core.Test.ParserTests.RomanNumeralTests
[Test(Description = "Converts the supported range [1-3999] of Arabic to Roman numerals.")]
[Order(0)]
public void should_convert_arabic_numeral_to_roman_numeral([Range(1,3999)] int arabicNumeral)
public void should_convert_arabic_numeral_to_roman_numeral([Range(1,20)] int arabicNumeral)
{
RomanNumeral romanNumeral = new RomanNumeral(arabicNumeral);
@ -39,7 +39,7 @@ namespace NzbDrone.Core.Test.ParserTests.RomanNumeralTests
[Test]
[Order(1)]
public void should_convert_roman_numeral_to_arabic_numeral([Range(1, 3999)] int arabicNumeral)
public void should_convert_roman_numeral_to_arabic_numeral([Range(1, 20)] int arabicNumeral)
{
RomanNumeral romanNumeral = new RomanNumeral(_arabicToRomanNumeralsMapping[arabicNumeral]);

@ -8,6 +8,7 @@ using NzbDrone.Core.Configuration.Events;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Common.Http.Proxy;
using NzbDrone.Core.Parser;
namespace NzbDrone.Core.Configuration
{
@ -211,6 +212,12 @@ namespace NzbDrone.Core.Configuration
set { SetValue("WhitelistedHardcodedSubs", value); }
}
public ParsingLeniencyType ParsingLeniency
{
get { return GetValueEnum<ParsingLeniencyType>("ParsingLeniency", ParsingLeniencyType.Strict); }
set { SetValue("ParsingLeniency", value); }
}
public bool RemoveCompletedDownloads
{
get { return GetValueBoolean("RemoveCompletedDownloads", false); }

@ -1,6 +1,7 @@
using System.Collections.Generic;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Common.Http.Proxy;
using NzbDrone.Core.Parser;
namespace NzbDrone.Core.Configuration
{
@ -54,6 +55,7 @@ namespace NzbDrone.Core.Configuration
bool AllowHardcodedSubs { get; set; }
string WhitelistedHardcodedSubs { get; set; }
ParsingLeniencyType ParsingLeniency { get; set; }
int NetImportSyncInterval { get; set; }
string ListSyncLevel { get; set; }

@ -26,13 +26,13 @@ namespace NzbDrone.Core.Datastore.Migration
var id = seriesReader.GetInt32(0);
var relativePath = seriesReader.GetString(1);
var result = Parser.Parser.ParseMovieTitle(relativePath);
var result = Parser.Parser.ParseMovieTitle(relativePath, false);
var edition = "";
if (result != null)
{
edition = Parser.Parser.ParseMovieTitle(relativePath).Edition;
edition = Parser.Parser.ParseMovieTitle(relativePath, false).Edition;
}
using (IDbCommand updateCmd = conn.CreateCommand())

@ -69,7 +69,7 @@ namespace NzbDrone.Core.DecisionEngine
try
{
var parsedMovieInfo = Parser.Parser.ParseMovieTitle(report.Title);
var parsedMovieInfo = Parser.Parser.ParseMovieTitle(report.Title, _configService.ParsingLeniency > 0);
if (parsedMovieInfo != null && !parsedMovieInfo.MovieTitle.IsNullOrWhiteSpace())
{

@ -3,6 +3,7 @@ using System.Linq;
using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.History;
using NzbDrone.Core.Parser;
@ -18,17 +19,20 @@ namespace NzbDrone.Core.Download.TrackedDownloads
{
private readonly IParsingService _parsingService;
private readonly IHistoryService _historyService;
private readonly IConfigService _config;
private readonly Logger _logger;
private readonly ICached<TrackedDownload> _cache;
public TrackedDownloadService(IParsingService parsingService,
ICacheManager cacheManager,
IHistoryService historyService,
IConfigService config,
Logger logger)
{
_parsingService = parsingService;
_historyService = historyService;
_cache = cacheManager.GetCache<TrackedDownload>(GetType());
_config = config;
_logger = logger;
}
@ -56,7 +60,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
try
{
var parsedMovieInfo = Parser.Parser.ParseMovieTitle(trackedDownload.DownloadItem.Title);
var parsedMovieInfo = Parser.Parser.ParseMovieTitle(trackedDownload.DownloadItem.Title, _config.ParsingLeniency > 0);
var historyItems = _historyService.FindByDownloadId(downloadItem.DownloadId);
if (parsedMovieInfo != null)
@ -73,7 +77,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
trackedDownload.RemoteMovie == null ||
trackedDownload.RemoteMovie.Movie == null)
{
parsedMovieInfo = Parser.Parser.ParseMovieTitle(firstHistoryItem.SourceTitle);
parsedMovieInfo = Parser.Parser.ParseMovieTitle(firstHistoryItem.SourceTitle, _config.ParsingLeniency > 0);
if (parsedMovieInfo != null)
{

@ -3,6 +3,7 @@ using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.MediaFiles.EpisodeImport;
using NzbDrone.Core.Parser;
@ -29,6 +30,7 @@ namespace NzbDrone.Core.MediaFiles
private readonly IMakeImportDecision _importDecisionMaker;
private readonly IImportApprovedMovie _importApprovedMovie;
private readonly IDetectSample _detectSample;
private readonly IConfigService _config;
private readonly Logger _logger;
public DownloadedMovieImportService(IDiskProvider diskProvider,
@ -38,6 +40,7 @@ namespace NzbDrone.Core.MediaFiles
IMakeImportDecision importDecisionMaker,
IImportApprovedMovie importApprovedMovie,
IDetectSample detectSample,
IConfigService config,
Logger logger)
{
_diskProvider = diskProvider;
@ -47,6 +50,7 @@ namespace NzbDrone.Core.MediaFiles
_importDecisionMaker = importDecisionMaker;
_importApprovedMovie = importApprovedMovie;
_detectSample = detectSample;
_config = config;
_logger = logger;
}
@ -160,7 +164,7 @@ namespace NzbDrone.Core.MediaFiles
}
var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name);
var folderInfo = Parser.Parser.ParseMovieTitle(directoryInfo.Name);
var folderInfo = Parser.Parser.ParseMovieTitle(directoryInfo.Name, _config.ParsingLeniency > 0);
if (folderInfo != null)
{

@ -154,7 +154,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
{
var title = Parser.Parser.RemoveFileExtension(downloadClientItem.Title);
var parsedTitle = Parser.Parser.ParseMovieTitle(title);
var parsedTitle = Parser.Parser.ParseMovieTitle(title, false);
if (parsedTitle != null)
{

@ -6,6 +6,7 @@ using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.TrackedDownloads;
@ -38,6 +39,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
private readonly ITrackedDownloadService _trackedDownloadService;
private readonly IDownloadedMovieImportService _downloadedMovieImportService;
private readonly IEventAggregator _eventAggregator;
private readonly IConfigService _config;
private readonly Logger _logger;
public ManualImportService(IDiskProvider diskProvider,
@ -53,6 +55,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
ITrackedDownloadService trackedDownloadService,
IDownloadedMovieImportService downloadedMovieImportService,
IEventAggregator eventAggregator,
IConfigService config,
Logger logger)
{
_diskProvider = diskProvider;
@ -68,6 +71,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
_trackedDownloadService = trackedDownloadService;
_downloadedMovieImportService = downloadedMovieImportService;
_eventAggregator = eventAggregator;
_config = config;
_logger = logger;
}
@ -116,7 +120,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
return files.Select(file => ProcessFile(file, downloadId, folder)).Where(i => i != null).ToList();
}
var folderInfo = Parser.Parser.ParseMovieTitle(directoryInfo.Name);
var folderInfo = Parser.Parser.ParseMovieTitle(directoryInfo.Name, _config.ParsingLeniency > 0);
var seriesFiles = _diskScanService.GetVideoFiles(folder).ToList();
var decisions = _importDecisionMaker.GetImportDecisions(seriesFiles, series, folderInfo, SceneSource(series, folder), false);
@ -282,7 +286,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
var file = message.Files[i];
var movie = _movieService.GetMovie(file.MovieId);
var parsedMovieInfo = Parser.Parser.ParseMoviePath(file.Path) ?? new ParsedMovieInfo();
var parsedMovieInfo = Parser.Parser.ParseMoviePath(file.Path, _config.ParsingLeniency > 0) ?? new ParsedMovieInfo();
var mediaInfo = _videoFileInfoReader.GetMediaInfo(file.Path);
var existingFile = movie.Path.IsParentPath(file.Path);

@ -119,7 +119,7 @@ namespace NzbDrone.Core.MetadataSource.PreDB
foreach (PreDBResult result in results)
{
var parsedInfo = Parser.Parser.ParseMovieTitle(result.Title);
var parsedInfo = Parser.Parser.ParseMovieTitle(result.Title, true);
if (parsedInfo != null)
{
@ -178,7 +178,7 @@ namespace NzbDrone.Core.MetadataSource.PreDB
foreach (PreDBResult result in results)
{
var parsed = Parser.Parser.ParseMovieTitle(result.Title);
var parsed = Parser.Parser.ParseMovieTitle(result.Title, true);
if (parsed == null)
{
parsed = new Parser.Model.ParsedMovieInfo { MovieTitle = result.Title, Year = 0 };

@ -414,7 +414,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
lowerTitle = lowerTitle.Replace(".", "");
var parserResult = Parser.Parser.ParseMovieTitle(title, true);
var parserResult = Parser.Parser.ParseMovieTitle(title, true, true);
var yearTerm = "";

@ -141,7 +141,7 @@ namespace NzbDrone.Core.NetImport.RSSImport
}
releaseInfo.Title = title;
var result = Parser.Parser.ParseMovieTitle(title);
var result = Parser.Parser.ParseMovieTitle(title, false);//Depreciated anyways
if (result != null)
{

@ -997,6 +997,7 @@
<Compile Include="Parser\Model\LocalMovie.cs" />
<Compile Include="Parser\Model\ParsedMovieInfo.cs" />
<Compile Include="Parser\Model\RemoteMovie.cs" />
<Compile Include="Parser\ParsingLeniency.cs" />
<Compile Include="Parser\RomanNumerals\ArabicRomanNumeral.cs" />
<Compile Include="Parser\RomanNumerals\IRomanNumeral.cs" />
<Compile Include="Parser\RomanNumerals\RomanNumeral.cs" />
@ -1381,4 +1382,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using FluentValidation.Results;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Tv;
@ -21,7 +22,7 @@ namespace NzbDrone.Core.Organizer
public ValidationFailure ValidateMovieFilename(SampleResult sampleResult)
{
var validationFailure = new ValidationFailure("MovieFormat", ERROR_MESSAGE);
var parsedMovieInfo = Parser.Parser.ParseMovieTitle(sampleResult.FileName);
var parsedMovieInfo = Parser.Parser.ParseMovieTitle(sampleResult.FileName, false); //We are not lenient when testing naming schemes
if(parsedMovieInfo == null)
{

@ -7,8 +7,10 @@ using System.Text.RegularExpressions;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Instrumentation;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Tv;
using TinyIoC;
namespace NzbDrone.Core.Parser
{
@ -19,16 +21,16 @@ 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(@"^(?<title>(?![(\[]).+?)?(?:(?:[-_\W](?<![)\[!]))*\(?(?<edition>(((Extended.|Ultimate.)?(Director.?s|Collector.?s|Theatrical|Ultimate|Final(?=(.(Cut|Edition|Version)))|Extended|Rogue|Special|Despecialized|\d{2,3}(th)?.Anniversary)(.(Cut|Edition|Version))?(.(Extended|Uncensored|Remastered|Unrated|Uncut|IMAX|Fan.?Edit))?|((Uncensored|Remastered|Unrated|Uncut|IMAX|Fan.?Edit|Edition|Restored|((2|3|4)in1))))))\)?.+(?<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(?=(.(Cut|Edition|Version)))|Extended|Rogue|Special|Despecialized|\d{2,3}(th)?.Anniversary)(.(Cut|Edition|Version))?(.(Extended|Uncensored|Remastered|Unrated|Uncut|IMAX|Fan.?Edit))?|((Uncensored|Remastered|Unrated|Uncut|IMAX|Fan.?Edit|Edition|Restored|((2|3|4)in1))))))\)?.{1,3}(?<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|(19|20)\d{2}|\]|\W(19|20)\d{2})))+(\W+|_|$)(?!\\)\(?(?<edition>(((Extended.|Ultimate.)?(Director.?s|Collector.?s|Theatrical|Ultimate|Final(?=(.(Cut|Edition|Version)))|Extended|Rogue|Special|Despecialized|\d{2,3}(th)?.Anniversary)(.(Cut|Edition|Version))?(.(Extended|Uncensored|Remastered|Unrated|Uncut|IMAX|Fan.?Edit))?|((Uncensored|Remastered|Unrated|Uncut|IMAX|Fan.?Edit|Edition|Restored|((2|3|4)in1))))))\)?",
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),
@ -45,6 +47,17 @@ namespace NzbDrone.Core.Parser
//When year comes first.
new Regex(@"^(?:(?:[-_\W](?<![)!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\W\d+)))+(\W+|_|$)(?<title>.+?)?$")
};
private static readonly Regex[] ReportMovieTitleLenientRegexBefore = new[]
{
//Some german or french tracker formats
new Regex(@"^(?<title>(?![(\[]).+?)((\W|_))(?:(German|French|TrueFrench))(.+?)(?=((19|20)\d{2}|$))(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+))?(\W+|_|$)(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled),
};
private static readonly Regex[] ReportMovieTitleLenientRegexAfter = new Regex[]
{
};
private static readonly Regex[] ReportTitleRegex = new[]
{
@ -338,29 +351,29 @@ namespace NzbDrone.Core.Parser
return result;
}
public static ParsedMovieInfo ParseMoviePath(string path)
public static ParsedMovieInfo ParseMoviePath(string path, bool isLenient)
{
var fileInfo = new FileInfo(path);
var result = ParseMovieTitle(fileInfo.Name, true);
var result = ParseMovieTitle(fileInfo.Name, isLenient, true);
if (result == null)
{
Logger.Debug("Attempting to parse episode info using directory and file names. {0}", fileInfo.Directory.Name);
result = ParseMovieTitle(fileInfo.Directory.Name + " " + fileInfo.Name);
result = ParseMovieTitle(fileInfo.Directory.Name + " " + fileInfo.Name, isLenient);
}
if (result == null)
{
Logger.Debug("Attempting to parse episode info using directory name. {0}", fileInfo.Directory.Name);
result = ParseMovieTitle(fileInfo.Directory.Name + fileInfo.Extension);
result = ParseMovieTitle(fileInfo.Directory.Name + fileInfo.Extension, isLenient);
}
return result;
}
public static ParsedMovieInfo ParseMovieTitle(string title, bool isDir = false)
public static ParsedMovieInfo ParseMovieTitle(string title, bool isLenient, bool isDir = false)
{
ParsedMovieInfo realResult = null;
@ -368,8 +381,6 @@ namespace NzbDrone.Core.Parser
{
if (!ValidateBeforeParsing(title)) return null;
//title = title.Replace(" ", "."); //TODO: Determine if this breaks something. However, it shouldn't.
Logger.Debug("Parsing string '{0}'", title);
if (ReversedTitleRegex.IsMatch(title))
@ -398,6 +409,13 @@ namespace NzbDrone.Core.Parser
allRegexes.AddRange(ReportMovieTitleFolderRegex);
}
if (isLenient)
{
allRegexes.InsertRange(0, ReportMovieTitleLenientRegexBefore);
allRegexes.AddRange(ReportMovieTitleLenientRegexAfter);
}
foreach (var regex in allRegexes)
{
var match = regex.Matches(simpleTitle);

@ -0,0 +1,9 @@
namespace NzbDrone.Core.Parser
{
public enum ParsingLeniencyType
{
Strict = 0,
ParsingLenient = 1,
MappingLenient = 2,
}
}

@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.MediaFiles;
@ -34,6 +35,7 @@ namespace NzbDrone.Core.Parser
private readonly ISeriesService _seriesService;
private readonly ISceneMappingService _sceneMappingService;
private readonly IMovieService _movieService;
private readonly IConfigService _config;
private readonly Logger _logger;
private static HashSet<ArabicRomanNumeral> _arabicRomanNumeralMappings;
@ -42,12 +44,14 @@ namespace NzbDrone.Core.Parser
ISeriesService seriesService,
ISceneMappingService sceneMappingService,
IMovieService movieService,
IConfigService configService,
Logger logger)
{
_episodeService = episodeService;
_seriesService = seriesService;
_sceneMappingService = sceneMappingService;
_movieService = movieService;
_config = configService;
_logger = logger;
if (_arabicRomanNumeralMappings == null)
@ -127,7 +131,7 @@ namespace NzbDrone.Core.Parser
else
{
parsedMovieInfo = Parser.ParseMoviePath(filename);
parsedMovieInfo = Parser.ParseMoviePath(filename, _config.ParsingLeniency > 0);
}
if (parsedMovieInfo == null)
@ -172,7 +176,7 @@ namespace NzbDrone.Core.Parser
public Movie GetMovie(string title)
{
var parsedMovieInfo = Parser.ParseMovieTitle(title);
var parsedMovieInfo = Parser.ParseMovieTitle(title, _config.ParsingLeniency > 0);
if (parsedMovieInfo == null)
{

@ -9,7 +9,7 @@
if (!title.Contains(".")) return false;
if (title.Contains(" ")) return false;
var parsedTitle = Parser.ParseMovieTitle(title);
var parsedTitle = Parser.ParseMovieTitle(title, false); //We are not lenient when it comes to scene checking!
if (parsedTitle == null ||
parsedTitle.ReleaseGroup == null ||

@ -1,21 +1,17 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Debug - Chrome" type="JavascriptDebugType" factoryName="JavaScript Debug" singleton="true" uri="http://localhost:8989">
<mapping url="http://localhost:8989/Calendar" local-file="$PROJECT_DIR$/Calendar" />
<mapping url="http://localhost:8989/MainMenuView.js" local-file="$PROJECT_DIR$/MainMenuView.js" />
<mapping url="http://localhost:8989/Settings" local-file="$PROJECT_DIR$/Settings" />
<mapping url="http://localhost:8989/Upcoming" local-file="$PROJECT_DIR$/Upcoming" />
<configuration default="false" name="Debug - Chrome" type="JavascriptDebugType" factoryName="JavaScript Debug" singleton="true" engineId="98ca6316-2f89-46d9-a9e5-fa9e2b0625b3" uri="http://localhost:7878">
<mapping url="http://localhost:8989/Wanted" local-file="$PROJECT_DIR$/Wanted" />
<mapping url="http://localhost:8989" local-file="$PROJECT_DIR$" />
<mapping url="http://localhost:8989/app.js" local-file="$PROJECT_DIR$/app.js" />
<mapping url="http://localhost:8989/Mixins" local-file="$PROJECT_DIR$/Mixins" />
<mapping url="http://localhost:8989/Wanted" local-file="$PROJECT_DIR$/Wanted" />
<mapping url="http://localhost:8989/Quality" local-file="$PROJECT_DIR$/Quality" />
<mapping url="http://localhost:8989/Config.js" local-file="$PROJECT_DIR$/Config.js" />
<mapping url="http://localhost:8989/Shared" local-file="$PROJECT_DIR$/Shared" />
<mapping url="http://localhost:8989/AddSeries" local-file="$PROJECT_DIR$/AddSeries" />
<mapping url="http://localhost:8989/HeaderView.js" local-file="$PROJECT_DIR$/HeaderView.js" />
<mapping url="http://localhost:8989" local-file="$PROJECT_DIR$" />
<mapping url="http://localhost:8989/Routing.js" local-file="$PROJECT_DIR$/Routing.js" />
<mapping url="http://localhost:8989/Controller.js" local-file="$PROJECT_DIR$/Controller.js" />
<mapping url="http://localhost:8989/Calendar" local-file="$PROJECT_DIR$/Calendar" />
<mapping url="http://localhost:8989/Series" local-file="$PROJECT_DIR$/Series" />
<mapping url="http://localhost:8989/AddSeries" local-file="$PROJECT_DIR$/AddSeries" />
<mapping url="http://localhost:8989/Settings" local-file="$PROJECT_DIR$/Settings" />
<mapping url="http://localhost:8989/Config.js" local-file="$PROJECT_DIR$/Config.js" />
<RunnerSettings RunnerId="JavascriptDebugRunner" />
<ConfigurationWrapper RunnerId="JavascriptDebugRunner" />
<method />

@ -1,23 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Debug - Firefox" type="JavascriptDebugType" factoryName="JavaScript Debug" singleton="true" engineId="firefox" uri="http://localhost:8989">
<mapping url="http://localhost:8989/Calendar" local-file="$PROJECT_DIR$/Calendar" />
<mapping url="http://localhost:8989/MainMenuView.js" local-file="$PROJECT_DIR$/MainMenuView.js" />
<mapping url="http://localhost:8989/Settings" local-file="$PROJECT_DIR$/Settings" />
<mapping url="http://localhost:8989/Upcoming" local-file="$PROJECT_DIR$/Upcoming" />
<mapping url="http://localhost:8989/app.js" local-file="$PROJECT_DIR$/app.js" />
<mapping url="http://localhost:8989/Mixins" local-file="$PROJECT_DIR$/Mixins" />
<mapping url="http://localhost:8989/Wanted" local-file="$PROJECT_DIR$/Wanted" />
<mapping url="http://localhost:8989/Config.js" local-file="$PROJECT_DIR$/Config.js" />
<mapping url="http://localhost:8989/Quality" local-file="$PROJECT_DIR$/Quality" />
<mapping url="http://localhost:8989/AddSeries" local-file="$PROJECT_DIR$/AddSeries" />
<mapping url="http://localhost:8989/Shared" local-file="$PROJECT_DIR$/Shared" />
<mapping url="http://localhost:8989/HeaderView.js" local-file="$PROJECT_DIR$/HeaderView.js" />
<mapping url="http://localhost:8989" local-file="$PROJECT_DIR$" />
<mapping url="http://localhost:8989/Routing.js" local-file="$PROJECT_DIR$/Routing.js" />
<mapping url="http://localhost:8989/Controller.js" local-file="$PROJECT_DIR$/Controller.js" />
<mapping url="http://localhost:8989/Series" local-file="$PROJECT_DIR$/Series" />
<RunnerSettings RunnerId="JavascriptDebugRunner" />
<ConfigurationWrapper RunnerId="JavascriptDebugRunner" />
<method />
</configuration>
</component>

@ -9,7 +9,8 @@ var view = Marionette.ItemView.extend({
template : 'Settings/Indexers/Options/IndexerOptionsViewTemplate',
ui : {
hcwhitelist : '.x-hcwhitelist',
hcwhitelist : '.x-hcwhitelist',
leniencyTooltip : '.x-leniency-tooltip',
},
onRender : function() {
@ -18,6 +19,18 @@ var view = Marionette.ItemView.extend({
allowDuplicates: true,
tagClass : 'label label-success'
});
this.templateFunction = Marionette.TemplateCache.get('Settings/Indexers/Options/LeniencyTooltipTemplate');
var content = this.templateFunction();
this.ui.leniencyTooltip.popover({
content : content,
html : true,
trigger : 'hover',
title : 'Parsing Leniency Notes',
placement : 'right',
container : this.$el
});
},
});

@ -91,6 +91,22 @@
</div>
<div class="form-group advanced-setting">
<label class="col-sm-3 control-label">Parser Leniency</label>
<div class="col-sm-1 col-sm-push-2 help-inline">
<i class="icon-sonarr-form-info leniency-tooltip x-leniency-tooltip"/>
</div>
<div class="col-sm-2 col-sm-pull-1">
<select class="form-control" name="parsingLeniency">
<option value="strict">Strict</option>
<option value="parsingLenient">Lenient Parsing</option>
<option value="mappingLenient">Lenient Mapping</option>
</select>
</div>
</div>
<legend>Availability Options</legend>
<div class="form-group">
<label class="col-sm-3 control-label">Availability Delay</label>

@ -0,0 +1,7 @@
How strict the Parser should be. (Note: Strict is strongly recommended!)
<br><br>
<b>Strict:</b> Just as before, year must immediately follow title.
<br><br>
<b>Lenient Parsing:</b> Either year or language tag must immediately follow after title. (Note: May prevent Movies with language tags in title - e.g. The Danish Girl - from being parsed correctly)
<br><br>
<b>Lenient Mapping:</b> Includes Lenient Parsing. When title cannot be found Try mapping just parts of the title. (Useful when no year is present / not after title. <b>NOT IMPLEMENTED YET</b>)
Loading…
Cancel
Save