New: Parse and reject split episode releases and files

This commit is contained in:
Mark McDowall 2024-02-06 20:02:03 -08:00 committed by GitHub
parent f722d49b3a
commit 895eccebc5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 87 additions and 0 deletions

View File

@ -211,5 +211,19 @@ namespace NzbDrone.Core.Test.ParserTests
result.FullSeason.Should().BeFalse();
result.Special.Should().BeTrue();
}
[TestCase("Series.Title.S06E01b.Fade.Out.Fade.in.Part.2.1080p.DSNP.WEB-DL.AAC2.0.H.264-FLUX", "Series Title", 6, 1)]
public void should_parse_split_episode(string postTitle, string title, int seasonNumber, int episodeNumber)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Should().NotBeNull();
result.EpisodeNumbers.Should().HaveCount(1);
result.SeasonNumber.Should().Be(seasonNumber);
result.EpisodeNumbers.First().Should().Be(episodeNumber);
result.SeriesTitle.Should().Be(title);
result.AbsoluteEpisodeNumbers.Should().BeEmpty();
result.FullSeason.Should().BeFalse();
result.IsSplitEpisode.Should().BeTrue();
}
}
}

View File

@ -0,0 +1,30 @@
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications
{
public class SplitEpisodeSpecification : IDecisionEngineSpecification
{
private readonly Logger _logger;
public SplitEpisodeSpecification(Logger logger)
{
_logger = logger;
}
public SpecificationPriority Priority => SpecificationPriority.Default;
public RejectionType Type => RejectionType.Permanent;
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
if (subject.ParsedEpisodeInfo.IsSplitEpisode)
{
_logger.Debug("Split episode release {0} rejected. Not supported", subject.Release.Title);
return Decision.Reject("Split episode releases are not supported");
}
return Decision.Accept();
}
}
}

View File

@ -0,0 +1,33 @@
using NLog;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
{
public class SplitEpisodeSpecification : IImportDecisionEngineSpecification
{
private readonly Logger _logger;
public SplitEpisodeSpecification(Logger logger)
{
_logger = logger;
}
public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem)
{
if (localEpisode.FileEpisodeInfo == null)
{
return Decision.Accept();
}
if (localEpisode.FileEpisodeInfo.IsSplitEpisode)
{
_logger.Debug("Single episode split into multiple files");
return Decision.Reject("Single episode split into multiple files");
}
return Decision.Accept();
}
}
}

View File

@ -23,6 +23,7 @@ namespace NzbDrone.Core.Parser.Model
public bool IsPartialSeason { get; set; }
public bool IsMultiSeason { get; set; }
public bool IsSeasonExtra { get; set; }
public bool IsSplitEpisode { get; set; }
public bool Special { get; set; }
public string ReleaseGroup { get; set; }
public string ReleaseHash { get; set; }

View File

@ -74,6 +74,10 @@ namespace NzbDrone.Core.Parser
new Regex(@"^(?:S?(?<season>(?<!\d+)(?:\d{1,2}|\d{4})(?!\d+))(?:(?:[-_]|[ex]){1,2}(?<episode>\d{2,3}(?!\d+))){2,})",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
// Split episodes (S01E05a, S01E05b, etc)
new Regex(@"^(?<title>.+?)(?:S?(?<season>(?<!\d+)(?:\d{1,2}|\d{4})(?!\d+))(?:(?:[-_ ]?[ex])(?<episode>\d{2,3}(?!\d+))(?<splitepisode>[a-d])(?:[ _.])))",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
// Episodes without a title, Single (S01E05, 1x05)
new Regex(@"^(?:S?(?<season>(?<!\d+)(?:\d{1,2}|\d{4})(?!\d+))(?:(?:[-_ ]?[ex])(?<episode>\d{2,3}(?!\d+))))",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
@ -966,6 +970,11 @@ namespace NzbDrone.Core.Parser
{
result.Special = true;
}
if (matchGroup.Groups["splitepisode"].Success)
{
result.IsSplitEpisode = true;
}
}
if (absoluteEpisodeCaptures.Any())