mirror of
https://github.com/Radarr/Radarr.git
synced 2026-04-16 21:15:33 -04:00
Compare commits
9 Commits
v2.0.0.515
...
v2.0.0.516
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52588509ed | ||
|
|
e607a67f00 | ||
|
|
96d7382a1c | ||
|
|
e15530cee1 | ||
|
|
940f59468a | ||
|
|
ff885ab3bd | ||
|
|
17cfaf170e | ||
|
|
f1b2186313 | ||
|
|
ac379e3b84 |
@@ -29,6 +29,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
Title = "Dexter.S08E01.EDITED.WEBRip.x264-KYR"
|
||||
}
|
||||
};
|
||||
|
||||
Mocker.SetConstant<ITermMatcher>(Mocker.Resolve<TermMatcher>());
|
||||
}
|
||||
|
||||
private void GivenRestictions(string required, string ignored)
|
||||
@@ -123,5 +125,16 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestCase("/WEB/", true)]
|
||||
[TestCase("/WEB\b/", false)]
|
||||
[TestCase("/WEb/", false)]
|
||||
[TestCase(@"/\.WEB/", true)]
|
||||
public void should_match_perl_regex(string pattern, bool expected)
|
||||
{
|
||||
GivenRestictions(pattern, null);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().Be(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
|
||||
Mocker.GetMock<IDownloadClientStatusRepository>()
|
||||
.Verify(v => v.UpdateMany(
|
||||
It.Is<List<DownloadClientStatus>>(i => i.All(
|
||||
s => s.DisabledTill.Value < DateTime.UtcNow.AddMinutes(disabledTillTime)))
|
||||
s => s.DisabledTill.Value <= DateTime.UtcNow.AddMinutes(disabledTillTime)))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -137,6 +137,9 @@ namespace NzbDrone.Core.Test.Messaging.Commands
|
||||
QueueAndWaitForExecution(commandModel);
|
||||
|
||||
VerifyEventPublished<CommandExecutedEvent>();
|
||||
|
||||
Thread.Sleep(10);
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("[Cthuyuu] Taimadou Gakuen 35 Shiken Shoutai - 03 [720p H264 AAC][8AD82C3A]", "Taimadou Gakuen 35 Shiken Shoutai", 3, 0, 0)]
|
||||
[TestCase("Dragon Ball Super Episode 56 [VOSTFR V2][720p][AAC]-Mystic Z-Team", "Dragon Ball Super", 56, 0, 0)]
|
||||
[TestCase("[Mystic Z-Team] Dragon Ball Super Episode 69 [VOSTFR_Finale][1080p][AAC].mp4", "Dragon Ball Super", 69, 0, 0)]
|
||||
[TestCase("[Shark-Raws] Crayon Shin-chan #957 (NBN 1280x720 x264 AAC).mp4", "Crayon Shin-chan", 957, 0, 0)]
|
||||
//[TestCase("", "", 0, 0, 0)]
|
||||
public void should_parse_absolute_numbers(string postTitle, string title, int absoluteEpisodeNumber, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.DataAugmentation.Scene;
|
||||
@@ -117,6 +118,10 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
|
||||
{
|
||||
GivenAbsoluteNumberingSeries();
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Setup(s => s.FindEpisodesBySceneNumbering(It.IsAny<int>(), It.IsAny<int>()))
|
||||
.Returns(new List<Episode>());
|
||||
|
||||
Subject.Map(_parsedEpisodeInfo, _series.TvdbId, _series.TvRageId, _singleEpisodeSearchCriteria);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
@@ -253,7 +258,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
|
||||
[TestCase(0)]
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
public void should_find_episode_by_season_and_absolute_episode_number_when_scene_absolute_episode_number_returns_multiple_results(int seasonNumber)
|
||||
public void should_return_episodes_when_scene_absolute_episode_number_returns_multiple_results(int seasonNumber)
|
||||
{
|
||||
GivenAbsoluteNumberingSeries();
|
||||
|
||||
@@ -265,6 +270,32 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
|
||||
.Setup(s => s.FindEpisodesBySceneNumbering(It.IsAny<int>(), seasonNumber, It.IsAny<int>()))
|
||||
.Returns(Builder<Episode>.CreateListOfSize(5).Build().ToList());
|
||||
|
||||
var result = Subject.GetEpisodes(_parsedEpisodeInfo, _series, true, null);
|
||||
|
||||
result.Should().HaveCount(5);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Verify(v => v.FindEpisodesBySceneNumbering(It.IsAny<int>(), seasonNumber, It.IsAny<int>()), Times.Once());
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Verify(v => v.FindEpisode(It.IsAny<int>(), seasonNumber, It.IsAny<int>()), Times.Never());
|
||||
}
|
||||
|
||||
[TestCase(0)]
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
public void should_find_episode_by_season_and_absolute_episode_number_when_scene_absolute_episode_number_returns_no_results(int seasonNumber)
|
||||
{
|
||||
GivenAbsoluteNumberingSeries();
|
||||
|
||||
Mocker.GetMock<ISceneMappingService>()
|
||||
.Setup(s => s.GetSceneSeasonNumber(_parsedEpisodeInfo.SeriesTitle, It.IsAny<string>()))
|
||||
.Returns(seasonNumber);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Setup(s => s.FindEpisodesBySceneNumbering(It.IsAny<int>(), seasonNumber, It.IsAny<int>()))
|
||||
.Returns(new List<Episode>());
|
||||
|
||||
Subject.GetEpisodes(_parsedEpisodeInfo, _series, true, null);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
|
||||
@@ -8,7 +8,8 @@ namespace NzbDrone.Core.Test.TvTests
|
||||
public class SeriesTitleNormalizerFixture
|
||||
{
|
||||
[TestCase("A to Z", 281588, "a to z")]
|
||||
[TestCase("A. D. - The Trials & Triumph of the Early Church", 266757, "ad trials triumph early church")]
|
||||
[TestCase("A.D. The Bible Continues", 289260, "ad bible continues")]
|
||||
[TestCase("A.P. Bio", 328534, "ap bio")]
|
||||
public void should_use_precomputed_title(string title, int tvdbId, string expected)
|
||||
{
|
||||
SeriesTitleNormalizer.Normalize(title, tvdbId).Should().Be(expected);
|
||||
|
||||
@@ -11,13 +11,15 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
{
|
||||
public class ReleaseRestrictionsSpecification : IDecisionEngineSpecification
|
||||
{
|
||||
private readonly IRestrictionService _restrictionService;
|
||||
private readonly Logger _logger;
|
||||
private readonly IRestrictionService _restrictionService;
|
||||
private readonly ITermMatcher _termMatcher;
|
||||
|
||||
public ReleaseRestrictionsSpecification(IRestrictionService restrictionService, Logger logger)
|
||||
public ReleaseRestrictionsSpecification(ITermMatcher termMatcher, IRestrictionService restrictionService, Logger logger)
|
||||
{
|
||||
_restrictionService = restrictionService;
|
||||
_logger = logger;
|
||||
_restrictionService = restrictionService;
|
||||
_termMatcher = termMatcher;
|
||||
}
|
||||
|
||||
public SpecificationPriority Priority => SpecificationPriority.Default;
|
||||
@@ -63,9 +65,9 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
private static List<string> ContainsAny(List<string> terms, string title)
|
||||
private List<string> ContainsAny(List<string> terms, string title)
|
||||
{
|
||||
return terms.Where(t => title.ToLowerInvariant().Contains(t.ToLowerInvariant())).ToList();
|
||||
return terms.Where(t => _termMatcher.IsMatch(t, title)).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,25 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
|
||||
if (!criteriaEpisodes.Intersect(remoteEpisodes).Any())
|
||||
{
|
||||
_logger.Debug("Release rejected since the episode wasn't requested: {0}", remoteEpisode.ParsedEpisodeInfo);
|
||||
return Decision.Reject("Episode wasn't requested");
|
||||
|
||||
if (remoteEpisodes.Any())
|
||||
{
|
||||
var episodes = remoteEpisode.Episodes.OrderBy(v => v.SeasonNumber).ThenBy(v => v.EpisodeNumber).ToList();
|
||||
|
||||
if (episodes.Count > 1)
|
||||
{
|
||||
return Decision.Reject($"Episode wasn't requested: {episodes.First().SeasonNumber}x{episodes.First().EpisodeNumber}-{episodes.Last().EpisodeNumber}");
|
||||
}
|
||||
else
|
||||
{
|
||||
return Decision.Reject($"Episode wasn't requested: {episodes.First().SeasonNumber}x{episodes.First().EpisodeNumber}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Decision.Reject("Episode wasn't requested");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
|
||||
@@ -25,8 +25,16 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
|
||||
}
|
||||
|
||||
var singleEpisodeSpec = searchCriteria as SingleEpisodeSearchCriteria;
|
||||
if (singleEpisodeSpec == null) return Decision.Accept();
|
||||
if (singleEpisodeSpec != null) return IsSatisfiedBy(remoteEpisode, singleEpisodeSpec);
|
||||
|
||||
var animeEpisodeSpec = searchCriteria as AnimeEpisodeSearchCriteria;
|
||||
if (animeEpisodeSpec != null) return IsSatisfiedBy(remoteEpisode, animeEpisodeSpec);
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
private Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SingleEpisodeSearchCriteria singleEpisodeSpec)
|
||||
{
|
||||
if (singleEpisodeSpec.SeasonNumber != remoteEpisode.ParsedEpisodeInfo.SeasonNumber)
|
||||
{
|
||||
_logger.Debug("Season number does not match searched season number, skipping.");
|
||||
@@ -47,5 +55,16 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
private Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, AnimeEpisodeSearchCriteria singleEpisodeSpec)
|
||||
{
|
||||
if (remoteEpisode.ParsedEpisodeInfo.FullSeason)
|
||||
{
|
||||
_logger.Debug("Full season result during single episode search, skipping.");
|
||||
return Decision.Reject("Full season pack");
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,11 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
{
|
||||
var actualHash = _proxy.AddTorrentFromMagnet(magnetLink, Settings);
|
||||
|
||||
if (actualHash.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new DownloadClientException("Deluge failed to add magnet " + magnetLink);
|
||||
}
|
||||
|
||||
if (!Settings.TvCategory.IsNullOrWhiteSpace())
|
||||
{
|
||||
_proxy.SetLabel(actualHash, Settings.TvCategory, Settings);
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation.Responses
|
||||
|
||||
FileStationMessages = new Dictionary<int, string>
|
||||
{
|
||||
{ 160, "Permission denied. Give your user access to FileStation."},
|
||||
{ 400, "Invalid parameter of file operation" },
|
||||
{ 401, "Unknown error of file operation" },
|
||||
{ 402, "System is too busy" },
|
||||
|
||||
@@ -97,6 +97,11 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
|
||||
var result = ProcessRequest(request, settings);
|
||||
|
||||
if (settings.TvCategory.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
request.AddFormParameter("category", settings.TvCategory);
|
||||
}
|
||||
|
||||
// Note: Current qbit versions return nothing, so we can't do != "Ok." here.
|
||||
if (result == "Fails.")
|
||||
{
|
||||
@@ -110,11 +115,6 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
.Post()
|
||||
.AddFormParameter("hashes", hash);
|
||||
|
||||
if (settings.TvCategory.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
request.AddFormParameter("category", settings.TvCategory);
|
||||
}
|
||||
|
||||
ProcessRequest(request, settings);
|
||||
}
|
||||
|
||||
|
||||
@@ -349,7 +349,7 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
|
||||
|
||||
try
|
||||
{
|
||||
Logger.Debug("Formatiting audio channels using 'AudioChannelPositions', with a value of: '{0}'", audioChannelPositions);
|
||||
Logger.Debug("Formatting audio channels using 'AudioChannelPositions', with a value of: '{0}'", audioChannelPositions);
|
||||
|
||||
if (audioChannelPositions.Contains("+"))
|
||||
{
|
||||
|
||||
@@ -1087,9 +1087,11 @@
|
||||
<Compile Include="Queue\Queue.cs" />
|
||||
<Compile Include="Queue\QueueService.cs" />
|
||||
<Compile Include="Queue\QueueUpdatedEvent.cs" />
|
||||
<Compile Include="Restrictions\PerlRegexFactory.cs" />
|
||||
<Compile Include="Restrictions\Restriction.cs" />
|
||||
<Compile Include="Restrictions\RestrictionRepository.cs" />
|
||||
<Compile Include="Restrictions\RestrictionService.cs" />
|
||||
<Compile Include="Restrictions\TermMatcher.cs" />
|
||||
<Compile Include="Rest\JsonNetSerializer.cs" />
|
||||
<Compile Include="Rest\RestClientFactory.cs" />
|
||||
<Compile Include="Rest\RestException.cs" />
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace NzbDrone.Core.Parser
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||
|
||||
//Anime - [SubGroup] Title Absolute Episode Number
|
||||
new Regex(@"^\[(?<subgroup>.+?)\][-_. ]?(?<title>.+?)[-_. ]+\(?(?:[-_. ]?(?<absoluteepisode>\d{2,3}(?!\d+)))+\)?(?:[-_. ]+(?<special>special|ova|ovd))?.*?(?<hash>\[\w{8}\])?(?:$|\.mkv)",
|
||||
new Regex(@"^\[(?<subgroup>.+?)\][-_. ]?(?<title>.+?)[-_. ]+\(?(?:[-_. ]?#?(?<absoluteepisode>\d{2,3}(?!\d+)))+\)?(?:[-_. ]+(?<special>special|ova|ovd))?.*?(?<hash>\[\w{8}\])?(?:$|\.mkv)",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||
|
||||
//Anime - Title Season EpisodeNumber + Absolute Episode Number [SubGroup]
|
||||
|
||||
@@ -363,13 +363,13 @@ namespace NzbDrone.Core.Parser
|
||||
|
||||
foreach (var absoluteEpisodeNumber in parsedEpisodeInfo.AbsoluteEpisodeNumbers)
|
||||
{
|
||||
Episode episode = null;
|
||||
var episodes = new List<Episode>();
|
||||
|
||||
if (parsedEpisodeInfo.Special)
|
||||
{
|
||||
episode = _episodeService.FindEpisode(series.Id, 0, absoluteEpisodeNumber);
|
||||
var episode = _episodeService.FindEpisode(series.Id, 0, absoluteEpisodeNumber);
|
||||
episodes.AddIfNotNull(episode);
|
||||
}
|
||||
|
||||
else if (sceneSource)
|
||||
{
|
||||
// Is there a reason why we excluded season 1 from this handling before?
|
||||
@@ -377,31 +377,33 @@ namespace NzbDrone.Core.Parser
|
||||
// If this needs to be reverted tests will need to be added
|
||||
if (sceneSeasonNumber.HasValue)
|
||||
{
|
||||
var episodes = _episodeService.FindEpisodesBySceneNumbering(series.Id, sceneSeasonNumber.Value, absoluteEpisodeNumber);
|
||||
episodes = _episodeService.FindEpisodesBySceneNumbering(series.Id, sceneSeasonNumber.Value, absoluteEpisodeNumber);
|
||||
|
||||
if (episodes.Count == 1)
|
||||
if (episodes.Empty())
|
||||
{
|
||||
episode = episodes.First();
|
||||
}
|
||||
|
||||
if (episode == null)
|
||||
{
|
||||
episode = _episodeService.FindEpisode(series.Id, sceneSeasonNumber.Value, absoluteEpisodeNumber);
|
||||
var episode = _episodeService.FindEpisode(series.Id, sceneSeasonNumber.Value, absoluteEpisodeNumber);
|
||||
episodes.AddIfNotNull(episode);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
episode = _episodeService.FindEpisodeBySceneNumbering(series.Id, absoluteEpisodeNumber);
|
||||
episodes = _episodeService.FindEpisodesBySceneNumbering(series.Id, absoluteEpisodeNumber);
|
||||
|
||||
// Don't allow multiple results without a scene name mapping.
|
||||
if (episodes.Count > 1)
|
||||
{
|
||||
episodes.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (episode == null)
|
||||
if (episodes.Empty())
|
||||
{
|
||||
episode = _episodeService.FindEpisode(series.Id, absoluteEpisodeNumber);
|
||||
var episode = _episodeService.FindEpisode(series.Id, absoluteEpisodeNumber);
|
||||
episodes.AddIfNotNull(episode);
|
||||
}
|
||||
|
||||
if (episode != null)
|
||||
foreach (var episode in episodes)
|
||||
{
|
||||
_logger.Debug("Using absolute episode number {0} for: {1} - TVDB: {2}x{3:00}",
|
||||
absoluteEpisodeNumber,
|
||||
|
||||
72
src/NzbDrone.Core/Restrictions/PerlRegexFactory.cs
Normal file
72
src/NzbDrone.Core/Restrictions/PerlRegexFactory.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using NzbDrone.Common.Exceptions;
|
||||
|
||||
namespace NzbDrone.Core.Restrictions
|
||||
{
|
||||
public static class PerlRegexFactory
|
||||
{
|
||||
private static Regex _perlRegexFormat = new Regex(@"/(?<pattern>.*)/(?<modifiers>[a-z]*)", RegexOptions.Compiled);
|
||||
|
||||
public static bool TryCreateRegex(string pattern, out Regex regex)
|
||||
{
|
||||
var match = _perlRegexFormat.Match(pattern);
|
||||
|
||||
if (!match.Success)
|
||||
{
|
||||
regex = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
regex = CreateRegex(match.Groups["pattern"].Value, match.Groups["modifiers"].Value);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Regex CreateRegex(string pattern, string modifiers)
|
||||
{
|
||||
var options = GetOptions(modifiers);
|
||||
|
||||
// For now we simply expect the pattern to be .net compliant. We should probably check and reject perl-specific constructs.
|
||||
return new Regex(pattern, options | RegexOptions.Compiled);
|
||||
}
|
||||
|
||||
private static RegexOptions GetOptions(string modifiers)
|
||||
{
|
||||
var options = RegexOptions.None;
|
||||
|
||||
foreach (var modifier in modifiers)
|
||||
{
|
||||
switch (modifier)
|
||||
{
|
||||
case 'm':
|
||||
options |= RegexOptions.Multiline;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
options |= RegexOptions.Singleline;
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
options |= RegexOptions.IgnoreCase;
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
options |= RegexOptions.IgnorePatternWhitespace;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
options |= RegexOptions.ExplicitCapture;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException("Unknown or unsupported perl regex modifier: " + modifier);
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
||||
}
|
||||
63
src/NzbDrone.Core/Restrictions/TermMatcher.cs
Normal file
63
src/NzbDrone.Core/Restrictions/TermMatcher.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using NzbDrone.Common.Cache;
|
||||
|
||||
namespace NzbDrone.Core.Restrictions
|
||||
{
|
||||
public interface ITermMatcher
|
||||
{
|
||||
bool IsMatch(string term, string value);
|
||||
}
|
||||
|
||||
public class TermMatcher : ITermMatcher
|
||||
{
|
||||
private ICached<Predicate<string>> _matcherCache;
|
||||
|
||||
public TermMatcher(ICacheManager cacheManager)
|
||||
{
|
||||
_matcherCache = cacheManager.GetCache<Predicate<string>>(GetType());
|
||||
}
|
||||
|
||||
public bool IsMatch(string term, string value)
|
||||
{
|
||||
return GetMatcher(term)(value);
|
||||
}
|
||||
|
||||
public Predicate<string> GetMatcher(string term)
|
||||
{
|
||||
return _matcherCache.Get(term, () => CreateMatcherInternal(term), TimeSpan.FromHours(24));
|
||||
}
|
||||
|
||||
private Predicate<string> CreateMatcherInternal(string term)
|
||||
{
|
||||
Regex regex;
|
||||
if (PerlRegexFactory.TryCreateRegex(term, out regex))
|
||||
{
|
||||
return regex.IsMatch;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new CaseInsensitiveTermMatcher(term).IsMatch;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class CaseInsensitiveTermMatcher
|
||||
{
|
||||
private readonly string _term;
|
||||
|
||||
public CaseInsensitiveTermMatcher(string term)
|
||||
{
|
||||
_term = term.ToLowerInvariant();
|
||||
}
|
||||
|
||||
public bool IsMatch(string value)
|
||||
{
|
||||
return value.ToLowerInvariant().Contains(_term);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ namespace NzbDrone.Core.Tv
|
||||
PagingSpec<Episode> EpisodesWithoutFiles(PagingSpec<Episode> pagingSpec, bool includeSpecials);
|
||||
PagingSpec<Episode> EpisodesWhereCutoffUnmet(PagingSpec<Episode> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff, bool includeSpecials);
|
||||
List<Episode> FindEpisodesBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber);
|
||||
Episode FindEpisodeBySceneNumbering(int seriesId, int sceneAbsoluteEpisodeNumber);
|
||||
List<Episode> FindEpisodesBySceneNumbering(int seriesId, int sceneAbsoluteEpisodeNumber);
|
||||
List<Episode> EpisodesBetweenDates(DateTime startDate, DateTime endDate, bool includeUnmonitored);
|
||||
void SetMonitoredFlat(Episode episode, bool monitored);
|
||||
void SetMonitoredBySeason(int seriesId, int seasonNumber, bool monitored);
|
||||
@@ -134,21 +134,15 @@ namespace NzbDrone.Core.Tv
|
||||
{
|
||||
return Query.Where(s => s.SeriesId == seriesId)
|
||||
.AndWhere(s => s.SceneSeasonNumber == seasonNumber)
|
||||
.AndWhere(s => s.SceneEpisodeNumber == episodeNumber);
|
||||
.AndWhere(s => s.SceneEpisodeNumber == episodeNumber)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public Episode FindEpisodeBySceneNumbering(int seriesId, int sceneAbsoluteEpisodeNumber)
|
||||
public List<Episode> FindEpisodesBySceneNumbering(int seriesId, int sceneAbsoluteEpisodeNumber)
|
||||
{
|
||||
var episodes = Query.Where(s => s.SeriesId == seriesId)
|
||||
return Query.Where(s => s.SeriesId == seriesId)
|
||||
.AndWhere(s => s.SceneAbsoluteEpisodeNumber == sceneAbsoluteEpisodeNumber)
|
||||
.ToList();
|
||||
|
||||
if (episodes.Empty() || episodes.Count > 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return episodes.Single();
|
||||
}
|
||||
|
||||
public List<Episode> EpisodesBetweenDates(DateTime startDate, DateTime endDate, bool includeUnmonitored)
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace NzbDrone.Core.Tv
|
||||
Episode FindEpisode(int seriesId, int absoluteEpisodeNumber);
|
||||
Episode FindEpisodeByTitle(int seriesId, int seasonNumber, string releaseTitle);
|
||||
List<Episode> FindEpisodesBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber);
|
||||
Episode FindEpisodeBySceneNumbering(int seriesId, int sceneAbsoluteEpisodeNumber);
|
||||
List<Episode> FindEpisodesBySceneNumbering(int seriesId, int sceneAbsoluteEpisodeNumber);
|
||||
Episode GetEpisode(int seriesId, string date);
|
||||
Episode FindEpisode(int seriesId, string date);
|
||||
List<Episode> GetEpisodeBySeries(int seriesId);
|
||||
@@ -78,9 +78,9 @@ namespace NzbDrone.Core.Tv
|
||||
return _episodeRepository.FindEpisodesBySceneNumbering(seriesId, seasonNumber, episodeNumber);
|
||||
}
|
||||
|
||||
public Episode FindEpisodeBySceneNumbering(int seriesId, int sceneAbsoluteEpisodeNumber)
|
||||
public List<Episode> FindEpisodesBySceneNumbering(int seriesId, int sceneAbsoluteEpisodeNumber)
|
||||
{
|
||||
return _episodeRepository.FindEpisodeBySceneNumbering(seriesId, sceneAbsoluteEpisodeNumber);
|
||||
return _episodeRepository.FindEpisodesBySceneNumbering(seriesId, sceneAbsoluteEpisodeNumber);
|
||||
}
|
||||
|
||||
public Episode GetEpisode(int seriesId, string date)
|
||||
|
||||
@@ -7,8 +7,8 @@ namespace NzbDrone.Core.Tv
|
||||
private readonly static Dictionary<int, string> PreComputedTitles = new Dictionary<int, string>
|
||||
{
|
||||
{ 281588, "a to z" },
|
||||
{ 266757, "ad trials triumph early church" },
|
||||
{ 289260, "ad bible continues"}
|
||||
{ 289260, "ad bible continues"},
|
||||
{ 328534, "ap bio"}
|
||||
};
|
||||
|
||||
public static string Normalize(string title, int tvdbId)
|
||||
|
||||
@@ -13,8 +13,11 @@ namespace NzbDrone.Mono.Test.EnvironmentInfo
|
||||
[Test]
|
||||
public void should_get_framework_version()
|
||||
{
|
||||
Subject.Version.Major.Should().Be(4);
|
||||
Subject.Version.Minor.Should().BeOneOf(0, 5, 6);
|
||||
Subject.Version.Major.Should().BeOneOf(4, 5);
|
||||
if (Subject.Version.Major == 4)
|
||||
{
|
||||
Subject.Version.Minor.Should().BeOneOf(0, 5, 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user