New: v2/v3/etc handling for anime

pull/109/head
Mark McDowall 10 years ago
parent 8bef19448f
commit a3d013d908

@ -131,13 +131,16 @@ namespace NzbDrone.Api.Indexers
if (downloadDecision.RemoteEpisode.Series != null)
{
release.QualityWeight = downloadDecision.RemoteEpisode.Series.Profile.Value.Items.FindIndex(v => v.Quality == release.Quality.Quality) * 2;
release.QualityWeight = downloadDecision.RemoteEpisode
.Series
.Profile
.Value
.Items
.FindIndex(v => v.Quality == release.Quality.Quality) * 100;
}
if (!release.Quality.Proper)
{
release.QualityWeight++;
}
release.QualityWeight += release.Quality.Revision.Real * 10;
release.QualityWeight += release.Quality.Revision.Version;
result.Add(release);
}

@ -1,4 +1,5 @@
using System.Linq;
using System;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
@ -12,65 +13,6 @@ namespace NzbDrone.Core.Test.Datastore
[TestFixture]
public class DatabaseRelationshipFixture : DbTest
{
/* [Test]
[Explicit]
public void benchmark()
{
var series = Builder<Series>.CreateNew()
.With(c => c.Id = 0)
.Build();
Marr.Data.MapRepository.Instance.EnableTraceLogging = false;
Db.Insert(series);
var covers = Builder<MediaCover.MediaCover>.CreateListOfSize(5)
.All()
.With(c => c.SeriesId = series.Id)
.With(c => c.Id = 0)
.Build()
.ToList();
Db.InsertMany(covers);
var loadedSeries = Db.Single<Series>();
var sw = Stopwatch.StartNew();
for (int i = 0; i < 10000; i++)
{
loadedSeries = Db.Single<Series>();
var list = loadedSeries.Covers.Value;
}
sw.Stop();
Console.WriteLine(sw.Elapsed);
loadedSeries.Covers.Value.Should().HaveSameCount(covers);
}
[Test]
public void one_to_many()
{
var series = Builder<Series>.CreateNew()
.With(c => c.Id = 0)
.Build();
Db.Insert(series);
var covers = Builder<MediaCover.MediaCover>.CreateListOfSize(5)
.All()
.With(c => c.SeriesId = series.Id)
.With(c => c.Id = 0)
.Build()
.ToList();
Db.InsertMany(covers);
var loadedSeries = Db.Single<Series>();
loadedSeries.Covers.Value.Should().HaveSameCount(covers);
}*/
[Test]
public void one_to_one()
{
@ -114,7 +56,7 @@ namespace NzbDrone.Core.Test.Datastore
[Test]
public void embedded_document_as_json()
{
var quality = new QualityModel { Quality = Quality.Bluray720p, Proper = true };
var quality = new QualityModel { Quality = Quality.Bluray720p, Revision = new Revision(version: 2 )};
var history = Builder<History.History>.CreateNew()
.With(c => c.Id = 0)
@ -130,14 +72,12 @@ namespace NzbDrone.Core.Test.Datastore
[Test]
public void embedded_list_of_document_with_json()
{
var quality = new QualityModel { Quality = Quality.Bluray720p, Proper = true };
var history = Builder<History.History>.CreateListOfSize(2)
.All().With(c => c.Id = 0)
.Build().ToList();
history[0].Quality = new QualityModel(Quality.HDTV1080p, true);
history[1].Quality = new QualityModel(Quality.Bluray720p, true);
history[0].Quality = new QualityModel(Quality.HDTV1080p, new Revision(version: 2));
history[1].Quality = new QualityModel(Quality.Bluray720p, new Revision(version: 2));
Db.InsertMany(history);

@ -31,7 +31,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{
Series = series,
Release = new ReleaseInfo(),
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, true) },
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, new Revision(version: 2)) },
Episodes = new List<Episode> { new Episode(), new Episode(), new Episode(), new Episode(), new Episode(), new Episode() }
};
@ -39,7 +39,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{
Series = series,
Release = new ReleaseInfo(),
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, true) },
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, new Revision(version: 2)) },
Episodes = new List<Episode> { new Episode(), new Episode() }
};
@ -47,7 +47,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{
Series = series,
Release = new ReleaseInfo(),
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, true) },
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, new Revision(version: 2)) },
Episodes = new List<Episode> { new Episode() { Id = 2 } }
};
@ -182,7 +182,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{
var parseResult = new RemoteEpisode
{
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.RAWHD, false) },
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.RAWHD) },
};
Subject.IsSatisfiedBy(parseResult, null).Should().BeTrue();
@ -193,7 +193,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{
var parseResult = new RemoteEpisode
{
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.Unknown, false) },
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.Unknown) },
};
Subject.IsSatisfiedBy(parseResult, null).Should().BeFalse();

@ -2,7 +2,6 @@
using NUnit.Framework;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Test.Framework;
@ -15,35 +14,37 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_return_true_if_current_episode_is_less_than_cutoff()
{
Subject.CutoffNotMet(new Profile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities() },
new QualityModel(Quality.DVD, true)).Should().BeTrue();
new QualityModel(Quality.DVD, new Revision(version: 2))).Should().BeTrue();
}
[Test]
public void should_return_false_if_current_episode_is_equal_to_cutoff()
{
Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() },
new QualityModel(Quality.HDTV720p, true)).Should().BeFalse();
new QualityModel(Quality.HDTV720p, new Revision(version: 2))).Should().BeFalse();
}
[Test]
public void should_return_false_if_current_episode_is_greater_than_cutoff()
{
Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() },
new QualityModel(Quality.Bluray1080p, true)).Should().BeFalse();
new QualityModel(Quality.Bluray1080p, new Revision(version: 2))).Should().BeFalse();
}
[Test]
public void should_return_true_when_new_episode_is_proper_but_existing_is_not()
{
Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() },
new QualityModel(Quality.HDTV720p, false), new QualityModel(Quality.HDTV720p, true)).Should().BeTrue();
new QualityModel(Quality.HDTV720p, new Revision(version: 1)),
new QualityModel(Quality.HDTV720p, new Revision(version: 2))).Should().BeTrue();
}
[Test]
public void should_return_false_if_cutoff_is_met_and_quality_is_higher()
{
Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() },
new QualityModel(Quality.HDTV720p, true), new QualityModel(Quality.Bluray1080p, true)).Should().BeFalse();
new QualityModel(Quality.HDTV720p, new Revision(version: 2)),
new QualityModel(Quality.Bluray1080p, new Revision(version: 2))).Should().BeFalse();
}
}
}
}

@ -49,19 +49,19 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_parseResultMulti = new RemoteEpisode
{
Series = _fakeSeries,
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) },
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) },
Episodes = doubleEpisodeList
};
_parseResultSingle = new RemoteEpisode
{
Series = _fakeSeries,
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) },
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) },
Episodes = singleEpisodeList
};
_upgradableQuality = new QualityModel(Quality.SDTV, false);
_notupgradableQuality = new QualityModel(Quality.HDTV1080p, true);
_upgradableQuality = new QualityModel(Quality.SDTV, new Revision(version: 1));
_notupgradableQuality = new QualityModel(Quality.HDTV1080p, new Revision(version: 2));
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<Profile>(), 1)).Returns(_notupgradableQuality);
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<Profile>(), 2)).Returns(_notupgradableQuality);
@ -134,8 +134,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_not_be_upgradable_if_episode_is_of_same_quality_as_existing()
{
_fakeSeries.Profile = new Profile { Cutoff = Quality.WEBDL1080p, Items = Qualities.QualityFixture.GetDefaultQualities() };
_parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, false);
_upgradableQuality = new QualityModel(Quality.WEBDL1080p, false);
_parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1));
_upgradableQuality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1));
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<Profile>(), 1)).Returns(_upgradableQuality);

@ -49,15 +49,15 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test]
public void should_put_propers_before_non_propers()
{
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p, false));
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p, true));
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p, new Revision(version: 1)));
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p, new Revision(version: 2)));
var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode1));
decisions.Add(new DownloadDecision(remoteEpisode2));
var qualifiedReports = Subject.PrioritizeDecisions(decisions);
qualifiedReports.First().RemoteEpisode.ParsedEpisodeInfo.Quality.Proper.Should().BeTrue();
qualifiedReports.First().RemoteEpisode.ParsedEpisodeInfo.Quality.Revision.Version.Should().Be(2);
}
[Test]

@ -42,7 +42,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
remoteEpisode = new RemoteEpisode
{
Series = fakeSeries,
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) },
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) },
};
}

@ -1,10 +1,9 @@
using System.Linq;
using System;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Test.Framework;
@ -16,14 +15,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{
public static object[] IsUpgradeTestCases =
{
new object[] { Quality.SDTV, false, Quality.SDTV, true, Quality.SDTV, true },
new object[] { Quality.WEBDL720p, false, Quality.WEBDL720p, true, Quality.WEBDL720p, true },
new object[] { Quality.SDTV, false, Quality.SDTV, false, Quality.SDTV, false },
new object[] { Quality.WEBDL720p, false, Quality.HDTV720p, true, Quality.Bluray720p, false },
new object[] { Quality.WEBDL720p, false, Quality.HDTV720p, true, Quality.WEBDL720p, false },
new object[] { Quality.WEBDL720p, false, Quality.WEBDL720p, false, Quality.WEBDL720p, false },
new object[] { Quality.SDTV, false, Quality.SDTV, true, Quality.SDTV, true },
new object[] { Quality.WEBDL1080p, false, Quality.WEBDL1080p, false, Quality.WEBDL1080p, false }
new object[] { Quality.SDTV, 1, Quality.SDTV, 2, Quality.SDTV, true },
new object[] { Quality.WEBDL720p, 1, Quality.WEBDL720p, 2, Quality.WEBDL720p, true },
new object[] { Quality.SDTV, 1, Quality.SDTV, 1, Quality.SDTV, false },
new object[] { Quality.WEBDL720p, 1, Quality.HDTV720p, 2, Quality.Bluray720p, false },
new object[] { Quality.WEBDL720p, 1, Quality.HDTV720p, 2, Quality.WEBDL720p, false },
new object[] { Quality.WEBDL720p, 1, Quality.WEBDL720p, 1, Quality.WEBDL720p, false },
new object[] { Quality.SDTV, 1, Quality.SDTV, 2, Quality.SDTV, true },
new object[] { Quality.WEBDL1080p, 1, Quality.WEBDL1080p, 1, Quality.WEBDL1080p, false }
};
[SetUp]
@ -40,13 +39,13 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
}
[Test, TestCaseSource("IsUpgradeTestCases")]
public void IsUpgradeTest(Quality current, bool currentProper, Quality newQuality, bool newProper, Quality cutoff, bool expected)
public void IsUpgradeTest(Quality current, Int32 currentVersion, Quality newQuality, Int32 newVersion, Quality cutoff, Boolean expected)
{
GivenAutoDownloadPropers(true);
var profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() };
Subject.IsUpgradable(profile, new QualityModel(current, currentProper), new QualityModel(newQuality, newProper))
Subject.IsUpgradable(profile, new QualityModel(current, new Revision(version: currentVersion)), new QualityModel(newQuality, new Revision(version: newVersion)))
.Should().Be(expected);
}
@ -57,7 +56,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
var profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() };
Subject.IsUpgradable(profile, new QualityModel(Quality.DVD, true), new QualityModel(Quality.DVD, false))
Subject.IsUpgradable(profile, new QualityModel(Quality.DVD, new Revision(version: 2)), new QualityModel(Quality.DVD, new Revision(version: 1)))
.Should().BeFalse();
}
}

@ -6,9 +6,9 @@ using FluentAssertions;
using Marr.Data;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.DecisionEngine.Specifications.RssSync;
using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.History;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Parser.Model;
@ -46,23 +46,32 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
_profile.Items.Add(new ProfileQualityItem { Allowed = true, Quality = Quality.Bluray720p });
_profile.Cutoff = Quality.WEBDL720p;
_profile.GrabDelayMode = GrabDelayMode.Always;
_remoteEpisode.ParsedEpisodeInfo = new ParsedEpisodeInfo();
_remoteEpisode.Release = new ReleaseInfo();
_remoteEpisode.Episodes = Builder<Episode>.CreateListOfSize(1).Build().ToList();
_remoteEpisode.Episodes.First().EpisodeFileId = 0;
}
private void GivenExistingFile(QualityModel quality)
{
_remoteEpisode.Episodes[0].EpisodeFile = new LazyLoaded<EpisodeFile>(new EpisodeFile
_remoteEpisode.Episodes.First().EpisodeFileId = 1;
_remoteEpisode.Episodes.First().EpisodeFile = new LazyLoaded<EpisodeFile>(new EpisodeFile
{
Quality = quality
});
}
private void GivenUpgradeForExistingFile()
{
Mocker.GetMock<IQualityUpgradableSpecification>()
.Setup(s => s.IsUpgradable(It.IsAny<Profile>(), It.IsAny<QualityModel>(), It.IsAny<QualityModel>()))
.Returns(true);
}
[Test]
public void should_be_true_when_search()
{
@ -108,12 +117,17 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
}
[Test]
public void should_be_true_when_release_is_proper_for_existing_episode()
public void should_be_true_when_release_is_a_proper_for_existing_episode()
{
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p, true);
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(version: 2));
_remoteEpisode.Release.PublishDate = DateTime.UtcNow;
GivenExistingFile(new QualityModel(Quality.HDTV720p));
GivenUpgradeForExistingFile();
Mocker.GetMock<IQualityUpgradableSpecification>()
.Setup(s => s.IsRevisionUpgrade(It.IsAny<QualityModel>(), It.IsAny<QualityModel>()))
.Returns(true);
_profile.GrabDelay = 12;
@ -121,9 +135,21 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
}
[Test]
public void should_be_false_when_release_is_proper_and_no_existing_episode()
public void should_be_true_when_release_is_a_real_for_existing_episode()
{
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(real: 1));
_remoteEpisode.Release.PublishDate = DateTime.UtcNow;
GivenExistingFile(new QualityModel(Quality.HDTV720p));
GivenUpgradeForExistingFile();
Mocker.GetMock<IQualityUpgradableSpecification>()
.Setup(s => s.IsRevisionUpgrade(It.IsAny<QualityModel>(), It.IsAny<QualityModel>()))
.Returns(true);
_profile.GrabDelay = 12;
Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeTrue();
}
[Test]
@ -165,7 +191,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
[Test]
public void should_be_false_when_release_is_proper_for_existing_episode_of_different_quality()
{
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p, true);
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(version: 2));
_remoteEpisode.Release.PublishDate = DateTime.UtcNow;
GivenExistingFile(new QualityModel(Quality.SDTV));
@ -200,8 +226,6 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
_profile.GrabDelay = 12;
var pendingRemoteEpisode = _remoteEpisode.JsonClone();
Mocker.GetMock<IPendingReleaseService>()
.Setup(s => s.GetPendingRemoteEpisodes(It.IsAny<Int32>()))
.Returns(new List<RemoteEpisode> { _remoteEpisode.JsonClone() });

@ -31,8 +31,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
{
Mocker.Resolve<QualityUpgradableSpecification>();
_firstFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, false), DateAdded = DateTime.Now };
_secondFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, false), DateAdded = DateTime.Now };
_firstFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 1)), DateAdded = DateTime.Now };
_secondFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 1)), DateAdded = DateTime.Now };
var singleEpisodeList = new List<Episode> { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } };
var doubleEpisodeList = new List<Episode> { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = _secondFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } };
@ -44,14 +44,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
_parseResultMulti = new RemoteEpisode
{
Series = fakeSeries,
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) },
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) },
Episodes = doubleEpisodeList
};
_parseResultSingle = new RemoteEpisode
{
Series = fakeSeries,
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) },
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) },
Episodes = singleEpisodeList
};
}

@ -32,8 +32,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Mocker.Resolve<QualityUpgradableSpecification>();
_upgradeDisk = Mocker.Resolve<UpgradeDiskSpecification>();
_firstFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, true), DateAdded = DateTime.Now };
_secondFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, true), DateAdded = DateTime.Now };
_firstFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 2)), DateAdded = DateTime.Now };
_secondFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 2)), DateAdded = DateTime.Now };
var singleEpisodeList = new List<Episode> { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } };
var doubleEpisodeList = new List<Episode> { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = _secondFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } };
@ -45,14 +45,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_parseResultMulti = new RemoteEpisode
{
Series = fakeSeries,
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) },
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) },
Episodes = doubleEpisodeList
};
_parseResultSingle = new RemoteEpisode
{
Series = fakeSeries,
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) },
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) },
Episodes = singleEpisodeList
};
}
@ -121,7 +121,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_not_be_upgradable_if_qualities_are_the_same()
{
_firstFile.Quality = new QualityModel(Quality.WEBDL1080p);
_parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, false);
_parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p);
_upgradeDisk.IsSatisfiedBy(_parseResultSingle, null).Should().BeFalse();
}
}

@ -30,7 +30,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
_localEpisode = new LocalEpisode
{
Path = @"C:\Test\30 Rock\30.rock.s01e01.avi",
Quality = new QualityModel(Quality.HDTV720p, false),
Quality = new QualityModel(Quality.HDTV720p, new Revision(version: 1)),
Series = _series
};
}
@ -70,7 +70,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
new EpisodeFile
{
Quality = new QualityModel(Quality.SDTV, false)
Quality = new QualityModel(Quality.SDTV, new Revision(version: 1))
}))
.Build()
.ToList();
@ -87,7 +87,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
new EpisodeFile
{
Quality = new QualityModel(Quality.SDTV, false)
Quality = new QualityModel(Quality.SDTV, new Revision(version: 1))
}))
.Build()
.ToList();
@ -104,7 +104,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
new EpisodeFile
{
Quality = new QualityModel(Quality.Bluray720p, false)
Quality = new QualityModel(Quality.Bluray720p, new Revision(version: 1))
}))
.Build()
.ToList();
@ -121,7 +121,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
new EpisodeFile
{
Quality = new QualityModel(Quality.Bluray720p, false)
Quality = new QualityModel(Quality.Bluray720p, new Revision(version: 1))
}))
.Build()
.ToList();
@ -138,14 +138,14 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
new EpisodeFile
{
Quality = new QualityModel(Quality.SDTV, false)
Quality = new QualityModel(Quality.SDTV, new Revision(version: 1))
}))
.TheNext(1)
.With(e => e.EpisodeFileId = 2)
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
new EpisodeFile
{
Quality = new QualityModel(Quality.Bluray720p, false)
Quality = new QualityModel(Quality.Bluray720p, new Revision(version: 1))
}))
.Build()
.ToList();

@ -197,6 +197,7 @@
<Compile Include="MediaFiles\EpisodeImport\Specifications\UpgradeSpecificationFixture.cs" />
<Compile Include="MediaFiles\ImportApprovedEpisodesFixture.cs" />
<Compile Include="MediaFiles\MediaFileRepositoryFixture.cs" />
<Compile Include="Qualities\RevisionComparableFixture.cs" />
<Compile Include="RemotePathMappingsTests\RemotePathMappingServiceFixture.cs" />
<Compile Include="OrganizerTests\CleanFixture.cs" />
<Compile Include="MediaFiles\MediaFileServiceTests\FilterFixture.cs" />
@ -229,6 +230,7 @@
<Compile Include="OrganizerTests\GetSeriesFolderFixture.cs" />
<Compile Include="ParserTests\AbsoluteEpisodeNumberParserFixture.cs" />
<Compile Include="ParserTests\AnimeMetadataParserFixture.cs" />
<Compile Include="ParserTests\ExtendedQualityParserRegex.cs" />
<Compile Include="ParserTests\CrapParserFixture.cs" />
<Compile Include="ParserTests\DailyEpisodeParserFixture.cs" />
<Compile Include="ParserTests\HashedReleaseFixture.cs" />

@ -61,7 +61,7 @@ namespace NzbDrone.Core.Test.OrganizerTests
private void GivenProper()
{
_episodeFile.Quality.Proper = true;
_episodeFile.Quality.Revision.Version =2;
}
[Test]
@ -208,7 +208,7 @@ namespace NzbDrone.Core.Test.OrganizerTests
public void should_replace_quality_title_with_proper()
{
_namingConfig.StandardEpisodeFormat = "{Quality Title}";
_episodeFile.Quality.Proper = true;
GivenProper();
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
.Should().Be("HDTV-720p Proper");

@ -0,0 +1,49 @@
using System;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ParserTests
{
[TestFixture]
public class ExtendedQualityParserRegex : CoreTest
{
[TestCase("Chuck.S04E05.HDTV.XviD-LOL", 0)]
[TestCase("Gold.Rush.S04E05.Garnets.or.Gold.REAL.REAL.PROPER.HDTV.x264-W4F", 2)]
[TestCase("Chuck.S03E17.REAL.PROPER.720p.HDTV.x264-ORENJI-RP", 1)]
[TestCase("Covert.Affairs.S05E09.REAL.PROPER.HDTV.x264-KILLERS", 1)]
[TestCase("Mythbusters.S14E01.REAL.PROPER.720p.HDTV.x264-KILLERS", 1)]
[TestCase("Orange.Is.the.New.Black.s02e06.real.proper.720p.webrip.x264-2hd", 1)]
[TestCase("Top.Gear.S21E07.Super.Duper.Real.Proper.HDTV.x264-FTP", 1)]
[TestCase("Top.Gear.S21E07.PROPER.HDTV.x264-RiVER-RP", 0)]
[TestCase("House.S07E11.PROPER.REAL.RERIP.1080p.BluRay.x264-TENEIGHTY", 1)]
[TestCase("[MGS] - Kuragehime - Episode 02v2 - [D8B6C90D]", 0)]
[TestCase("[Hatsuyuki] Tokyo Ghoul - 07 [v2][848x480][23D8F455].avi", 0)]
[TestCase("[DeadFish] Barakamon - 01v3 [720p][AAC]", 0)]
[TestCase("[DeadFish] Momo Kyun Sword - 01v4 [720p][AAC]", 0)]
public void should_parse_reality_from_title(String title, Int32 reality)
{
//TODO: re-enable this when we have a reliable way to determine real
//QualityParser.ParseQuality(title).Revision.Real.Should().Be(reality);
}
[TestCase("Chuck.S04E05.HDTV.XviD-LOL", 1)]
[TestCase("Gold.Rush.S04E05.Garnets.or.Gold.REAL.REAL.PROPER.HDTV.x264-W4F", 2)]
[TestCase("Chuck.S03E17.REAL.PROPER.720p.HDTV.x264-ORENJI-RP", 2)]
[TestCase("Covert.Affairs.S05E09.REAL.PROPER.HDTV.x264-KILLERS", 2)]
[TestCase("Mythbusters.S14E01.REAL.PROPER.720p.HDTV.x264-KILLERS", 2)]
[TestCase("Orange.Is.the.New.Black.s02e06.real.proper.720p.webrip.x264-2hd", 2)]
[TestCase("Top.Gear.S21E07.Super.Duper.Real.Proper.HDTV.x264-FTP", 2)]
[TestCase("Top.Gear.S21E07.PROPER.HDTV.x264-RiVER-RP", 2)]
[TestCase("House.S07E11.PROPER.REAL.RERIP.1080p.BluRay.x264-TENEIGHTY", 2)]
[TestCase("[MGS] - Kuragehime - Episode 02v2 - [D8B6C90D]", 2)]
[TestCase("[Hatsuyuki] Tokyo Ghoul - 07 [v2][848x480][23D8F455].avi", 2)]
[TestCase("[DeadFish] Barakamon - 01v3 [720p][AAC]", 3)]
[TestCase("[DeadFish] Momo Kyun Sword - 01v4 [720p][AAC]", 4)]
public void should_parse_version_from_title(String title, Int32 version)
{
QualityParser.ParseQuality(title).Revision.Version.Should().Be(version);
}
}
}

@ -52,7 +52,7 @@ namespace NzbDrone.Core.Test.ParserTests
{
var result = Parser.Parser.ParsePath(path);
result.SeriesTitle.Should().Be(title);
result.Quality.ToString().Should().Be(quality);
result.Quality.ToString().Should().Be(quality + " v1");
result.ReleaseGroup.Should().Be(releaseGroup);
}
}

@ -1,6 +1,7 @@
using System;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
@ -206,7 +207,7 @@ namespace NzbDrone.Core.Test.ParserTests
public void parsing_our_own_quality_enum_name(Quality quality)
{
var fileName = String.Format("My series S01E01 [{0}]", quality.Name);
var result = Parser.QualityParser.ParseQuality(fileName);
var result = QualityParser.ParseQuality(fileName);
result.Quality.Should().Be(quality);
}
@ -221,12 +222,13 @@ namespace NzbDrone.Core.Test.ParserTests
}
}
private void ParseAndVerifyQuality(string title, Quality quality, bool proper)
{
var result = Parser.QualityParser.ParseQuality(title);
var result = QualityParser.ParseQuality(title);
result.Quality.Should().Be(quality);
result.Proper.Should().Be(proper);
var version = proper ? 2 : 1;
result.Revision.Version.Should().Be(version);
}
}
}

@ -1,10 +1,7 @@
using System.Linq;
using System.Collections.Generic;
using FluentAssertions;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Qualities
@ -25,38 +22,25 @@ namespace NzbDrone.Core.Test.Qualities
}
[Test]
public void Icomparer_greater_test()
public void should_be_greater_when_first_quality_is_greater_than_second()
{
GivenDefaultProfile();
var first = new QualityModel(Quality.DVD, true);
var second = new QualityModel(Quality.Bluray1080p, true);
var first = new QualityModel(Quality.Bluray1080p);
var second = new QualityModel(Quality.DVD);
var compare = Subject.Compare(second, first);
compare.Should().BeGreaterThan(0);
}
[Test]
public void Icomparer_greater_proper()
{
GivenDefaultProfile();
var first = new QualityModel(Quality.Bluray1080p, false);
var second = new QualityModel(Quality.Bluray1080p, true);
var compare = Subject.Compare(second, first);
var compare = Subject.Compare(first, second);
compare.Should().BeGreaterThan(0);
}
[Test]
public void Icomparer_lesser()
public void should_be_lesser_when_second_quality_is_greater_than_first()
{
GivenDefaultProfile();
var first = new QualityModel(Quality.DVD, true);
var second = new QualityModel(Quality.Bluray1080p, true);
var first = new QualityModel(Quality.DVD);
var second = new QualityModel(Quality.Bluray1080p);
var compare = Subject.Compare(first, second);
@ -64,25 +48,25 @@ namespace NzbDrone.Core.Test.Qualities
}
[Test]
public void Icomparer_lesser_proper()
public void should_be_greater_when_first_quality_is_a_proper_for_the_same_quality()
{
GivenDefaultProfile();
var first = new QualityModel(Quality.DVD, false);
var second = new QualityModel(Quality.DVD, true);
var first = new QualityModel(Quality.Bluray1080p, new Revision(version: 2));
var second = new QualityModel(Quality.Bluray1080p, new Revision(version: 1));
var compare = Subject.Compare(first, second);
compare.Should().BeLessThan(0);
compare.Should().BeGreaterThan(0);
}
[Test]
public void Icomparer_greater_custom_order()
public void should_be_greater_when_using_a_custom_profile()
{
GivenCustomProfile();
var first = new QualityModel(Quality.DVD, true);
var second = new QualityModel(Quality.Bluray720p, true);
var first = new QualityModel(Quality.DVD);
var second = new QualityModel(Quality.Bluray720p);
var compare = Subject.Compare(first, second);

@ -0,0 +1,149 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Qualities
{
[TestFixture]
public class RevisionComparableFixture : CoreTest
{
[Test]
public void should_be_greater_when_first_quality_is_a_real()
{
var first = new Revision(real: 1);
var second = new Revision();
first.Should().BeGreaterThan(second);
}
[Test]
public void should_be_greater_when_first_quality_is_a_proper()
{
var first = new Revision(version: 2);
var second = new Revision();
first.Should().BeGreaterThan(second);
}
[Test]
public void should_be_greater_when_first_is_a_proper_for_a_real()
{
var first = new Revision(real: 1, version: 2);
var second = new Revision(real: 1);
first.Should().BeGreaterThan(second);
}
[Test]
public void should_be_lesser_when_second_quality_is_a_real()
{
var first = new Revision();
var second = new Revision(real: 1);
first.Should().BeLessThan(second);
}
[Test]
public void should_be_lesser_when_second_quality_is_a_proper()
{
var first = new Revision();
var second = new Revision(version: 2);
first.Should().BeLessThan(second);
}
[Test]
public void should_be_lesser_when_second_is_a_proper_for_a_real()
{
var first = new Revision(real: 1);
var second = new Revision(real: 1, version: 2);
first.Should().BeLessThan(second);
}
[Test]
public void should_be_equal_when_both_real_and_version_match()
{
var first = new Revision();
var second = new Revision();
first.CompareTo(second).Should().Be(0);
}
[Test]
public void should_be_equal_when_both_real_and_version_match_for_real()
{
var first = new Revision(real: 1);
var second = new Revision(real: 1);
first.CompareTo(second).Should().Be(0);
}
[Test]
public void should_be_equal_when_both_real_and_version_match_for_real_proper()
{
var first = new Revision(version: 2, real: 1);
var second = new Revision(version: 2, real: 1);
first.CompareTo(second).Should().Be(0);
}
[Test]
public void equal_operator_tests()
{
var first = new Revision();
var second = new Revision();
(first > second).Should().BeFalse();
(first < second).Should().BeFalse();
(first != second).Should().BeFalse();
(first >= second).Should().BeTrue();
(first <= second).Should().BeTrue();
(first == second).Should().BeTrue();
}
[Test]
public void greater_than_operator_tests()
{
var first = new Revision(version: 2);
var second = new Revision();
(first > second).Should().BeTrue();
(first < second).Should().BeFalse();
(first != second).Should().BeTrue();
(first >= second).Should().BeTrue();
(first <= second).Should().BeFalse();
(first == second).Should().BeFalse();
}
[Test]
public void less_than_operator_tests()
{
var first = new Revision();
var second = new Revision(version: 2);
(first > second).Should().BeFalse();
(first < second).Should().BeTrue();
(first != second).Should().BeTrue();
(first >= second).Should().BeFalse();
(first <= second).Should().BeTrue();
(first == second).Should().BeFalse();
}
[Test]
public void operating_on_nulls()
{
(null > new Revision()).Should().BeFalse();
(null >= new Revision()).Should().BeFalse();
(null < new Revision()).Should().BeTrue();
(null <= new Revision()).Should().BeTrue();
(new Revision() > null).Should().BeTrue();
(new Revision() >= null).Should().BeTrue();
(new Revision() < null).Should().BeFalse();
(new Revision() <= null).Should().BeFalse();
}
}
}

@ -2,8 +2,6 @@
using Marr.Data.Converters;
using Marr.Data.Mapping;
using NzbDrone.Core.Qualities;
using System.Collections.Generic;
using NzbDrone.Common.Serializer;
using Newtonsoft.Json;
namespace NzbDrone.Core.Datastore.Converters

@ -1,4 +1,5 @@
using FluentMigrator;
using System;
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
using System.Data;
using System.Linq;
@ -82,9 +83,9 @@ namespace NzbDrone.Core.Datastore.Migration
{
var qualityJson = qualityModelReader.GetString(0);
QualityModel quality;
QualityModel036 quality;
if (!Json.TryDeserialize<QualityModel>(qualityJson, out quality))
if (!Json.TryDeserialize<QualityModel036>(qualityJson, out quality))
{
continue;
}
@ -104,5 +105,11 @@ namespace NzbDrone.Core.Datastore.Migration
}
}
}
private class QualityModel036
{
public Int32 Quality { get; set; }
public Boolean Proper { get; set; }
}
}
}

@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Data;
using FluentMigrator;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore.Migration.Framework;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(62)]
public class convert_quality_models : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Execute.WithConnection(ConvertQualityModels);
}
private void ConvertQualityModels(IDbConnection conn, IDbTransaction tran)
{
ConvertQualityModelsOnTable(conn, tran, "EpisodeFiles");
ConvertQualityModelsOnTable(conn, tran, "Blacklist");
ConvertQualityModelsOnTable(conn, tran, "History");
}
private void ConvertQualityModelsOnTable(IDbConnection conn, IDbTransaction tran, String tableName)
{
var qualitiesToUpdate = new Dictionary<String, String>();
using (IDbCommand qualityModelCmd = conn.CreateCommand())
{
qualityModelCmd.Transaction = tran;
qualityModelCmd.CommandText = @"SELECT Distinct Quality FROM " + tableName;
using (IDataReader qualityModelReader = qualityModelCmd.ExecuteReader())
{
while (qualityModelReader.Read())
{
var qualityJson = qualityModelReader.GetString(0);
LegacyQualityModel062 quality;
if (!Json.TryDeserialize<LegacyQualityModel062>(qualityJson, out quality))
{
continue;
}
var newQualityModel = new QualityModel062 { Quality = quality.Quality, Revision = new Revision() };
if (quality.Proper)
newQualityModel.Revision.Version = 2;
var newQualityJson = newQualityModel.ToJson();
qualitiesToUpdate.Add(qualityJson, newQualityJson);
}
}
}
foreach (var quality in qualitiesToUpdate)
{
using (IDbCommand updateCmd = conn.CreateCommand())
{
updateCmd.Transaction = tran;
updateCmd.CommandText = "UPDATE " + tableName + " SET Quality = ? WHERE Quality = ?";
updateCmd.AddParameter(quality.Value);
updateCmd.AddParameter(quality.Key);
updateCmd.ExecuteNonQuery();
}
}
}
private class LegacyQualityModel062
{
public Int32 Quality { get; set; }
public Boolean Proper { get; set; }
}
private class QualityModel062
{
public Int32 Quality { get; set; }
public Revision Revision { get; set; }
}
}
}

@ -8,7 +8,7 @@ namespace NzbDrone.Core.DecisionEngine
{
bool IsUpgradable(Profile profile, QualityModel currentQuality, QualityModel newQuality = null);
bool CutoffNotMet(Profile profile, QualityModel currentQuality, QualityModel newQuality = null);
bool IsProperUpgrade(QualityModel currentQuality, QualityModel newQuality);
bool IsRevisionUpgrade(QualityModel currentQuality, QualityModel newQuality);
}
public class QualityUpgradableSpecification : IQualityUpgradableSpecification
@ -31,7 +31,7 @@ namespace NzbDrone.Core.DecisionEngine
return false;
}
if (IsProperUpgrade(currentQuality, newQuality))
if (IsRevisionUpgrade(currentQuality, newQuality))
{
return true;
}
@ -46,7 +46,7 @@ namespace NzbDrone.Core.DecisionEngine
if (compare >= 0)
{
if (newQuality != null && IsProperUpgrade(currentQuality, newQuality))
if (newQuality != null && IsRevisionUpgrade(currentQuality, newQuality))
{
return true;
}
@ -58,13 +58,13 @@ namespace NzbDrone.Core.DecisionEngine
return true;
}
public bool IsProperUpgrade(QualityModel currentQuality, QualityModel newQuality)
public bool IsRevisionUpgrade(QualityModel currentQuality, QualityModel newQuality)
{
int compare = newQuality.Proper.CompareTo(currentQuality.Proper);
int compare = newQuality.Revision.CompareTo(currentQuality.Revision);
if (currentQuality.Quality == newQuality.Quality && compare > 0)
{
_logger.Debug("New quality is a proper for existing quality");
_logger.Debug("New quality is a better revision for existing quality");
return true;
}

@ -11,11 +11,13 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
public class DelaySpecification : IDecisionEngineSpecification
{
private readonly IPendingReleaseService _pendingReleaseService;
private readonly IQualityUpgradableSpecification _qualityUpgradableSpecification;
private readonly Logger _logger;
public DelaySpecification(IPendingReleaseService pendingReleaseService, Logger logger)
public DelaySpecification(IPendingReleaseService pendingReleaseService, IQualityUpgradableSpecification qualityUpgradableSpecification, Logger logger)
{
_pendingReleaseService = pendingReleaseService;
_qualityUpgradableSpecification = qualityUpgradableSpecification;
_logger = logger;
}
@ -50,19 +52,18 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
var comparer = new QualityModelComparer(profile);
if (subject.ParsedEpisodeInfo.Quality.Proper)
foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value))
{
foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value))
var upgradable = _qualityUpgradableSpecification.IsUpgradable(profile, file.Quality, subject.ParsedEpisodeInfo.Quality);
if (upgradable)
{
if (comparer.Compare(subject.ParsedEpisodeInfo.Quality, file.Quality) > 0)
{
var properCompare = subject.ParsedEpisodeInfo.Quality.Proper.CompareTo(file.Quality.Proper);
var revisionUpgrade = _qualityUpgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedEpisodeInfo.Quality);
if (subject.ParsedEpisodeInfo.Quality.Quality == file.Quality.Quality && properCompare > 0)
{
_logger.Debug("New quality is a proper for existing quality, skipping delay");
return true;
}
if (revisionUpgrade)
{
_logger.Debug("New quality is a better revision for existing quality, skipping delay");
return true;
}
}
}

@ -39,7 +39,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value))
{
if (_qualityUpgradableSpecification.IsProperUpgrade(file.Quality, subject.ParsedEpisodeInfo.Quality))
if (_qualityUpgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedEpisodeInfo.Quality))
{
if (file.DateAdded < DateTime.Today.AddDays(-7))
{

@ -226,6 +226,7 @@
<Compile Include="Datastore\Migration\063_add_remotepathmappings.cs" />
<Compile Include="Datastore\Migration\061_clear_bad_scene_names.cs" />
<Compile Include="Datastore\Migration\060_remove_enable_from_indexers.cs" />
<Compile Include="Datastore\Migration\062_convert_quality_models.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationExtension.cs" />
@ -562,6 +563,7 @@
<Compile Include="MetadataSource\Trakt\TraktException.cs" />
<Compile Include="MetadataSource\TraktProxy.cs" />
<Compile Include="MetadataSource\Tvdb\TvdbProxy.cs" />
<Compile Include="Qualities\Revision.cs" />
<Compile Include="RemotePathMappings\RemotePathMapping.cs" />
<Compile Include="RemotePathMappings\RemotePathMappingRepository.cs" />
<Compile Include="RemotePathMappings\RemotePathMappingService.cs" />

@ -102,7 +102,6 @@ namespace NzbDrone.Core.Organizer
}
var pattern = namingConfig.StandardEpisodeFormat;
var tokenHandlers = new Dictionary<String, Func<TokenMatch, String>>(FileNameBuilderTokenEqualityComparer.Instance);
episodes = episodes.OrderBy(e => e.SeasonNumber).ThenBy(e => e.EpisodeNumber).ToList();
@ -118,15 +117,11 @@ namespace NzbDrone.Core.Organizer
}
pattern = AddSeasonEpisodeNumberingTokens(pattern, tokenHandlers, episodes, namingConfig);
pattern = AddAbsoluteNumberingTokens(pattern, tokenHandlers, series, episodes, namingConfig);
AddSeriesTokens(tokenHandlers, series);
AddEpisodeTokens(tokenHandlers, episodes);
AddEpisodeFileTokens(tokenHandlers, episodeFile);
AddEpisodeFileTokens(tokenHandlers, series, episodeFile);
AddMediaInfoTokens(tokenHandlers, episodeFile);
var filename = ReplaceTokens(pattern, tokenHandlers).Trim();
@ -401,11 +396,11 @@ namespace NzbDrone.Core.Organizer
tokenHandlers["{Episode Title}"] = m => GetEpisodeTitle(episodes);
}
private void AddEpisodeFileTokens(Dictionary<String, Func<TokenMatch, String>> tokenHandlers, EpisodeFile episodeFile)
private void AddEpisodeFileTokens(Dictionary<String, Func<TokenMatch, String>> tokenHandlers, Series series, EpisodeFile episodeFile)
{
tokenHandlers["{Original Title}"] = m => episodeFile.SceneName;
tokenHandlers["{Release Group}"] = m => episodeFile.ReleaseGroup ?? "DRONE";
tokenHandlers["{Quality Title}"] = m => GetQualityTitle(episodeFile.Quality);
tokenHandlers["{Quality Title}"] = m => GetQualityTitle(series, episodeFile.Quality);
}
private void AddMediaInfoTokens(Dictionary<String, Func<TokenMatch, String>> tokenHandlers, EpisodeFile episodeFile)
@ -651,12 +646,24 @@ namespace NzbDrone.Core.Organizer
return String.Join(" + ", titles);
}
private String GetQualityTitle(QualityModel quality)
private String GetQualityTitle(Series series, QualityModel quality)
{
if (quality.Proper)
return _qualityDefinitionService.Get(quality.Quality).Title + " Proper";
else
return _qualityDefinitionService.Get(quality.Quality).Title;
var qualitySuffix = String.Empty;
if (quality.Revision.Version > 1)
{
if (series.SeriesType == SeriesTypes.Anime)
{
qualitySuffix = " v" + quality.Revision.Version;
}
else
{
qualitySuffix = " Proper";
}
}
return _qualityDefinitionService.Get(quality.Quality).Title + qualitySuffix;
}
}

@ -31,6 +31,12 @@ namespace NzbDrone.Core.Parser
private static readonly Regex ProperRegex = new Regex(@"\b(?<proper>proper|repack)\b",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex VersionRegex = new Regex(@"\dv(?<version>\d)\b|\[v(?<version>\d)\]",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex RealRegex = new Regex(@"\b(?<real>)real\b",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex ResolutionRegex = new Regex(@"\b(?:(?<_480p>480p|640x480|848x480)|(?<_576p>576p)|(?<_720p>720p|1280x720)|(?<_1080p>1080p|1920x1080))\b",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
@ -49,9 +55,8 @@ namespace NzbDrone.Core.Parser
name = name.Trim();
var normalizedName = name.Replace('_', ' ').Trim().ToLower();
var result = new QualityModel { Quality = Quality.Unknown };
var result = ParseQualityModifiers(normalizedName);
result.Proper = ProperRegex.IsMatch(normalizedName);
if (RawHDRegex.IsMatch(normalizedName))
{
@ -282,7 +287,7 @@ namespace NzbDrone.Core.Parser
return result;
}
private static Resolution ParseResolution(string name)
private static Resolution ParseResolution(String name)
{
var match = ResolutionRegex.Match(name);
@ -295,7 +300,7 @@ namespace NzbDrone.Core.Parser
return Resolution.Unknown;
}
private static Quality OtherSourceMatch(string name)
private static Quality OtherSourceMatch(String name)
{
var match = OtherSourceRegex.Match(name);
@ -305,6 +310,34 @@ namespace NzbDrone.Core.Parser
return Quality.Unknown;
}
private static QualityModel ParseQualityModifiers(String normalizedName)
{
var result = new QualityModel { Quality = Quality.Unknown };
if (ProperRegex.IsMatch(normalizedName))
{
result.Revision.Version = 2;
}
var versionRegexResult = VersionRegex.Match(normalizedName);
if (versionRegexResult.Success)
{
result.Revision.Version = Convert.ToInt32(versionRegexResult.Groups["version"].Value);
}
//TODO: re-enable this when we have a reliable way to determine real
//TODO: Only treat it as a real if it comes AFTER the season/epsiode number
// var realRegexResult = RealRegex.Matches(normalizedName);
//
// if (realRegexResult.Count > 0)
// {
// result.Revision.Real = realRegexResult.Count;
// }
return result;
}
}
public enum Resolution

@ -1,37 +1,28 @@
using System;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.Qualities
{
public class QualityModel : IEmbeddedDocument, IEquatable<QualityModel>
{
public Quality Quality { get; set; }
public Boolean Proper { get; set; }
public Revision Revision { get; set; }
public QualityModel()
: this(Quality.Unknown)
: this(Quality.Unknown, new Revision())
{
}
public QualityModel(Quality quality, Boolean proper = false)
public QualityModel(Quality quality, Revision revision = null)
{
Quality = quality;
Proper = proper;
Revision = revision ?? new Revision();
}
public override string ToString()
{
string result = Quality.ToString();
if (Proper)
{
result += " Proper";
}
return result;
return String.Format("{0} {1}", Quality, Revision);
}
public override int GetHashCode()
@ -39,7 +30,7 @@ namespace NzbDrone.Core.Qualities
unchecked // Overflow is fine, just wrap
{
int hash = 17;
hash = hash * 23 + Proper.GetHashCode();
hash = hash * 23 + Revision.GetHashCode();
hash = hash * 23 + Quality.GetHashCode();
return hash;
}
@ -49,7 +40,8 @@ namespace NzbDrone.Core.Qualities
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return other.Quality.Equals(Quality) && other.Proper.Equals(Proper);
return other.Quality.Equals(Quality) && other.Revision.Equals(Revision);
}
public override bool Equals(object obj)

@ -29,7 +29,9 @@ namespace NzbDrone.Core.Qualities
int result = Compare(left.Quality, right.Quality);
if (result == 0)
result = left.Proper.CompareTo(right.Proper);
{
result = left.Revision.CompareTo(right.Revision);
}
return result;
}

@ -0,0 +1,102 @@
using System;
using System.Text;
namespace NzbDrone.Core.Qualities
{
public class Revision : IEquatable<Revision>, IComparable<Revision>
{
public Revision(Int32 version = 1, Int32 real = 0)
{
Version = version;
Real = real;
}
public Int32 Version { get; set; }
public Int32 Real { get; set; }
public bool Equals(Revision other)
{
if (ReferenceEquals(null, other)) return false;
return other.Version.Equals(Version) && other.Real.Equals(Real);
}
public int CompareTo(Revision other)
{
if (Real > other.Real) return 1;
if (Real < other.Real) return -1;
if (Version > other.Version) return 1;
if (Version < other.Version) return -1;
return 0;
}
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendFormat("v{0}", Version);
if (Real > 0)
{
sb.AppendFormat(" Real:{0}", Real);
}
return sb.ToString();
}
public override int GetHashCode()
{
return Version ^ Real << 8;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return Equals(obj as Revision);
}
public static bool operator ==(Revision left, Revision right)
{
return Equals(left, right);
}
public static bool operator !=(Revision left, Revision right)
{
return !Equals(left, right);
}
public static bool operator >(Revision left, Revision right)
{
if (left == null) return false;
if (right == null) return true;
return left.CompareTo(right) > 0;
}
public static bool operator <(Revision left, Revision right)
{
if (left == null) return true;
if (right == null) return false;
return left.CompareTo(right) < 0;
}
public static bool operator >=(Revision left, Revision right)
{
if (left == null) return false;
if (right == null) return true;
return left.CompareTo(right) >= 0;
}
public static bool operator <=(Revision left, Revision right)
{
if (left == null) return true;
if (right == null) return false;
return left.CompareTo(right) <= 0;
}
}
}

@ -55,8 +55,11 @@ define(
});
var newQuality = {
proper : false,
quality: profileItem.quality
quality : profileItem.quality,
revision : {
version : 1,
real : 0
}
};
model.set(column.get('name'), newQuality);

@ -41,10 +41,15 @@ define(
this.listenTo(this.episodeFile, 'change', this._refresh);
var quality = this.episodeFile.get('quality');
var revision = quality.revision;
var size = FormatHelpers.bytes(this.episodeFile.get('size'));
var title = 'Episode downloaded';
if (quality.proper) {
if (revision.real && revision.real > 0) {
title += '[REAL]';
}
if (revision.version && revision.version > 1) {
title += ' [PROPER]';
}
@ -59,7 +64,6 @@ define(
this.$el.html('<span class="badge" title="{0}">{1}</span>'.format(title, quality.quality.name));
}
return;
}

@ -1,5 +1,5 @@
{{#if proper}}
{{#if_gt proper compare="1"}}
<span class="badge badge-info" title="PROPER">{{quality.name}}</span>
{{else}}
<span class="badge">{{quality.name}}</span>
{{/if}}
{{/if_gt}}

@ -11,11 +11,18 @@ define(
var title = '';
var quality = this.model.get('quality');
var revision = quality.revision;
if (quality.proper) {
title = 'PROPER';
if (revision.real && revision.real > 0) {
title += ' REAL';
}
if (revision.version && revision.version > 1) {
title += ' PROPER';
}
title = title.trim();
if (this.model.get('qualityCutoffNotMet')) {
this.$el.html('<span class="badge badge-inverse" title="{0}">{1}</span>'.format(title, quality.quality.name));
}

Loading…
Cancel
Save