mirror of
https://github.com/Radarr/Radarr.git
synced 2024-09-11 12:02:35 +02:00
Fixed: Quality Parser and Augmenter Picks Wrong Quality
This commit is contained in:
parent
5c3fda48f7
commit
bbadf3c7e6
@ -12,11 +12,11 @@ namespace NzbDrone.Core.Test.CustomFormat
|
||||
[TestFixture]
|
||||
public class QualityTagFixture : CoreTest
|
||||
{
|
||||
[TestCase("R_1080", TagType.Resolution, Resolution.R1080P)]
|
||||
[TestCase("R_720", TagType.Resolution, Resolution.R720P)]
|
||||
[TestCase("R_576", TagType.Resolution, Resolution.R576P)]
|
||||
[TestCase("R_480", TagType.Resolution, Resolution.R480P)]
|
||||
[TestCase("R_2160", TagType.Resolution, Resolution.R2160P)]
|
||||
[TestCase("R_1080", TagType.Resolution, Resolution.R1080p)]
|
||||
[TestCase("R_720", TagType.Resolution, Resolution.R720p)]
|
||||
[TestCase("R_576", TagType.Resolution, Resolution.R576p)]
|
||||
[TestCase("R_480", TagType.Resolution, Resolution.R480p)]
|
||||
[TestCase("R_2160", TagType.Resolution, Resolution.R2160p)]
|
||||
[TestCase("S_BLURAY", TagType.Source, Source.BLURAY)]
|
||||
[TestCase("s_tv", TagType.Source, Source.TV)]
|
||||
[TestCase("s_workPRINT", TagType.Source, Source.WORKPRINT)]
|
||||
@ -38,7 +38,7 @@ public class QualityTagFixture : CoreTest
|
||||
[TestCase("G_10<>20", TagType.Size, new[] { 10737418240L, 21474836480L})]
|
||||
[TestCase("G_15.55<>20", TagType.Size, new[] { 16696685363L, 21474836480L})]
|
||||
[TestCase("G_15.55<>25.1908754", TagType.Size, new[] { 16696685363L, 27048496500L})]
|
||||
[TestCase("R__1080", TagType.Resolution, Resolution.R1080P)]
|
||||
[TestCase("R__1080", TagType.Resolution, Resolution.R1080p)]
|
||||
public void should_parse_tag_from_string(string raw, TagType type, object value, params TagModifier[] modifiers)
|
||||
{
|
||||
var parsed = new FormatTag(raw);
|
||||
|
@ -0,0 +1,123 @@
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore.Migration;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Migration
|
||||
{
|
||||
[TestFixture]
|
||||
public class add_webrip_qualitesFixture : MigrationTest<add_webrip_qualites>
|
||||
{
|
||||
private string GenerateQualityJson(int quality, bool allowed)
|
||||
{
|
||||
return $"{{ \"quality\": {quality}, \"allowed\": {allowed.ToString().ToLowerInvariant()} }}";
|
||||
}
|
||||
|
||||
private string GenerateQualityGroupJson(int quality, bool allowed, string groupname, int group)
|
||||
{
|
||||
return $"{{\"id\": {group}, \"name\": \"{groupname}\", \"items\": [ {{ \"quality\": {quality}, \"allowed\": {allowed.ToString().ToLowerInvariant()} }} ] }}";
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_add_webrip_qualities_and_group_with_webdl()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("Profiles").Row(new
|
||||
{
|
||||
Id = 0,
|
||||
Name = "SDTV",
|
||||
Cutoff = 1,
|
||||
Items = $"[{GenerateQualityJson(1, true)}, {GenerateQualityJson((int)Quality.WEBRip480p, false)}, {GenerateQualityJson((int)Quality.WEBRip720p, false)}, {GenerateQualityJson((int)Quality.WEBRip1080p, false)}, {GenerateQualityJson((int)Quality.WEBRip2160p, false)}]"
|
||||
});
|
||||
});
|
||||
|
||||
var profiles = db.Query<Profile159>("SELECT Items FROM Profiles LIMIT 1");
|
||||
|
||||
var items = profiles.First().Items;
|
||||
items.Should().HaveCount(5);
|
||||
items.Select(v => v.Quality).Should().BeEquivalentTo(1, null, null, null, null);
|
||||
items.Select(v => v.Items.Count).Should().BeEquivalentTo(0, 2, 2, 2, 2);
|
||||
items.Select(v => v.Allowed).Should().BeEquivalentTo(true, false, false, false, false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_add_webrip_and_webdl_if_webdl_is_missing()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("Profiles").Row(new
|
||||
{
|
||||
Id = 0,
|
||||
Name = "SDTV",
|
||||
Cutoff = 1,
|
||||
Items = $"[{GenerateQualityJson(1, true)}, {GenerateQualityJson((int)Quality.WEBRip480p, false)}, {GenerateQualityJson((int)Quality.WEBRip720p, false)}, {GenerateQualityJson((int)Quality.WEBRip1080p, false)}]"
|
||||
});
|
||||
});
|
||||
|
||||
var profiles = db.Query<Profile159>("SELECT Items FROM Profiles LIMIT 1");
|
||||
|
||||
var items = profiles.First().Items;
|
||||
items.Should().HaveCount(5);
|
||||
items.Select(v => v.Quality).Should().BeEquivalentTo(1, null, null, null, null);
|
||||
items.Select(v => v.Items.Count).Should().BeEquivalentTo(0, 2, 2, 2, 2);
|
||||
items.Select(v => v.Allowed).Should().BeEquivalentTo(true, false, false, false, false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_add_webrip_beside_webdl_is_grouped()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("Profiles").Row(new
|
||||
{
|
||||
Id = 0,
|
||||
Name = "SDTV",
|
||||
Cutoff = 1,
|
||||
Items = $"[{GenerateQualityJson(1, true)}, {GenerateQualityGroupJson(5, true, "smegrup", 1001)}]"
|
||||
});
|
||||
});
|
||||
|
||||
var profiles = db.Query<Profile159>("SELECT Items FROM Profiles LIMIT 1");
|
||||
|
||||
var items = profiles.First().Items;
|
||||
items.Count(c => c.Id == 1001).Should().Be(1);
|
||||
items.Should().HaveCount(5);
|
||||
items.Select(v => v.Quality).Should().BeEquivalentTo(1, null, null, null, null);
|
||||
items.Select(v => v.Items.Count).Should().BeEquivalentTo(0, 2, 2, 2, 2);
|
||||
items.Select(v => v.Allowed).Should().BeEquivalentTo(true, false, false, false, false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_group_webrip_and_webdl_with_the_same_resolution()
|
||||
{
|
||||
var db = WithMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("Profiles").Row(new
|
||||
{
|
||||
Id = 0,
|
||||
Name = "SDTV",
|
||||
Cutoff = 1,
|
||||
Items = $"[{GenerateQualityJson(1, true)}, {GenerateQualityJson((int)Quality.WEBRip480p, false)}, {GenerateQualityJson((int)Quality.WEBRip720p, false)}, {GenerateQualityJson((int)Quality.WEBRip1080p, false)}, {GenerateQualityJson((int)Quality.WEBRip2160p, false)}]"
|
||||
});
|
||||
});
|
||||
|
||||
var profiles = db.Query<Profile159>("SELECT Items FROM Profiles LIMIT 1");
|
||||
var items = profiles.First().Items;
|
||||
|
||||
items[1].Items.First().Quality.Should().Be((int)Quality.WEBRip480p);
|
||||
items[1].Items.Last().Quality.Should().Be((int)Quality.WEBDL480p);
|
||||
|
||||
items[2].Items.First().Quality.Should().Be((int)Quality.WEBRip720p);
|
||||
items[2].Items.Last().Quality.Should().Be((int)Quality.WEBDL720p);
|
||||
|
||||
items[3].Items.First().Quality.Should().Be((int)Quality.WEBRip1080p);
|
||||
items[3].Items.Last().Quality.Should().Be((int)Quality.WEBDL1080p);
|
||||
|
||||
items[4].Items.First().Quality.Should().Be((int)Quality.WEBRip2160p);
|
||||
items[4].Items.Last().Quality.Should().Be((int)Quality.WEBDL2160p);
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators;
|
||||
using NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Quality;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
@ -27,13 +28,13 @@ public void Setup()
|
||||
_nameAugmenter = new Mock<IAugmentQuality>();
|
||||
|
||||
_mediaInfoAugmenter.Setup(s => s.AugmentQuality(It.IsAny<LocalMovie>()))
|
||||
.Returns(AugmentQualityResult.ResolutionOnly(Resolution.R1080P, Confidence.MediaInfo));
|
||||
.Returns(AugmentQualityResult.ResolutionOnly((int)Resolution.R1080p, Confidence.MediaInfo));
|
||||
|
||||
_fileExtensionAugmenter.Setup(s => s.AugmentQuality(It.IsAny<LocalMovie>()))
|
||||
.Returns(new AugmentQualityResult(Source.TV, Confidence.Fallback, Resolution.R720P, Confidence.Fallback, Modifier.NONE, Confidence.Fallback, new Revision()));
|
||||
.Returns(new AugmentQualityResult(Source.TV, Confidence.Fallback, (int)Resolution.R720p, Confidence.Fallback, Modifier.NONE, Confidence.Fallback, new Revision(), new List<CustomFormats.CustomFormat>()));
|
||||
|
||||
_nameAugmenter.Setup(s => s.AugmentQuality(It.IsAny<LocalMovie>()))
|
||||
.Returns(new AugmentQualityResult(Source.TV, Confidence.Default, Resolution.R480P, Confidence.Default, Modifier.NONE, Confidence.Default, new Revision()));
|
||||
.Returns(new AugmentQualityResult(Source.TV, Confidence.Default, (int)Resolution.R480p, Confidence.Default, Modifier.NONE, Confidence.Default, new Revision(), new List<CustomFormats.CustomFormat>()));
|
||||
}
|
||||
|
||||
private void GivenAugmenters(params Mock<IAugmentQuality>[] mocks)
|
||||
|
@ -6,6 +6,7 @@
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Quality
|
||||
{
|
||||
@ -36,20 +37,20 @@ public void should_return_null_if_media_info_width_is_zero()
|
||||
Subject.AugmentQuality(localMovie).Should().Be(null);
|
||||
}
|
||||
|
||||
[TestCase(4096, Resolution.R2160P)] // True 4K
|
||||
[TestCase(4000, Resolution.R2160P)]
|
||||
[TestCase(3840, Resolution.R2160P)] // 4K UHD
|
||||
[TestCase(3200, Resolution.R2160P)]
|
||||
[TestCase(2000, Resolution.R1080P)]
|
||||
[TestCase(1920, Resolution.R1080P)] // Full HD
|
||||
[TestCase(1800, Resolution.R1080P)]
|
||||
[TestCase(1490, Resolution.R720P)]
|
||||
[TestCase(1280, Resolution.R720P)] // HD
|
||||
[TestCase(1200, Resolution.R720P)]
|
||||
[TestCase(800, Resolution.R480P)]
|
||||
[TestCase(720, Resolution.R480P)] // SDTV
|
||||
[TestCase(600, Resolution.R480P)]
|
||||
[TestCase(100, Resolution.R480P)]
|
||||
[TestCase(4096, Resolution.R2160p)] // True 4K
|
||||
[TestCase(4000, Resolution.R2160p)]
|
||||
[TestCase(3840, Resolution.R2160p)] // 4K UHD
|
||||
[TestCase(3200, Resolution.R2160p)]
|
||||
[TestCase(2000, Resolution.R1080p)]
|
||||
[TestCase(1920, Resolution.R1080p)] // Full HD
|
||||
[TestCase(1800, Resolution.R1080p)]
|
||||
[TestCase(1490, Resolution.R720p)]
|
||||
[TestCase(1280, Resolution.R720p)] // HD
|
||||
[TestCase(1200, Resolution.R720p)]
|
||||
[TestCase(800, Resolution.R480p)]
|
||||
[TestCase(720, Resolution.R480p)] // SDTV
|
||||
[TestCase(600, Resolution.R480p)]
|
||||
[TestCase(100, Resolution.R480p)]
|
||||
public void should_return_closest_resolution(int mediaInfoWidth, Resolution expectedResolution)
|
||||
{
|
||||
var mediaInfo = Builder<MediaInfoModel>.CreateNew()
|
||||
@ -63,7 +64,7 @@ public void should_return_closest_resolution(int mediaInfoWidth, Resolution expe
|
||||
var result = Subject.AugmentQuality(localMovie);
|
||||
|
||||
result.Should().NotBe(null);
|
||||
result.Resolution.Should().Be(expectedResolution);
|
||||
result.Resolution.Should().Be((int)expectedResolution);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ public void should_parse_version_from_title(string title, int version)
|
||||
public void should_parse_ultrahd_from_title(string title, int version)
|
||||
{
|
||||
var parsed = QualityParser.ParseQuality(title);
|
||||
parsed.Resolution.Should().Be(Resolution.R2160P);
|
||||
parsed.Quality.Resolution.Should().Be((int)Resolution.R2160p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -113,24 +113,13 @@ public void should_parse_movie_year(string postTitle, int year)
|
||||
[TestCase("My.Movie.GERMAN.Extended.Cut.2016", "Extended Cut")]
|
||||
[TestCase("My.Movie.GERMAN.Extended.Cut", "Extended Cut")]
|
||||
[TestCase("Mission Impossible: Rogue Nation 2012 Bluray", "")]
|
||||
[TestCase("Loving.Pablo.2018.TS.FRENCH.MD.x264-DROGUERiE","")]
|
||||
[TestCase("Loving.Pablo.2018.TS.FRENCH.MD.x264-DROGUERiE", "")]
|
||||
public void should_parse_edition(string postTitle, string edition)
|
||||
{
|
||||
var parsed = Parser.Parser.ParseMovieTitle(postTitle, true);
|
||||
if (parsed.Edition.IsNullOrWhiteSpace())
|
||||
{
|
||||
parsed.Edition = Parser.Parser.ParseEdition(parsed.SimpleReleaseTitle);
|
||||
}
|
||||
parsed.Edition.Should().Be(edition);
|
||||
}
|
||||
|
||||
[TestCase("The Lord of the Rings The Fellowship of the Ring (Extended Edition) 1080p BD25", "The Lord Of The Rings The Fellowship Of The Ring", "Extended Edition")]
|
||||
[TestCase("The.Lord.of.the.Rings.The.Fellowship.of.the.Ring.(Extended.Edition).1080p.BD25", "The Lord Of The Rings The Fellowship Of The Ring", "Extended Edition")]
|
||||
public void should_parse_edition_lenient_mapping(string postTitle, string foundTitle, string edition)
|
||||
{
|
||||
Parser.Parser.ParseMinimalMovieTitle(postTitle, foundTitle, 1290).Edition.Should().Be(edition);
|
||||
}
|
||||
|
||||
[TestCase("123", "tt0000123")]
|
||||
[TestCase("1234567", "tt1234567")]
|
||||
[TestCase("tt1234567", "tt1234567")]
|
||||
|
@ -1,89 +1,90 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Core.Parser.Augmenters;
|
||||
using NzbDrone.Core.Qualities;
|
||||
//using FluentAssertions;
|
||||
//using NUnit.Framework;
|
||||
//using NzbDrone.Core.CustomFormats;
|
||||
//using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
//using NzbDrone.Core.Parser;
|
||||
//using NzbDrone.Core.Parser.Augmenters;
|
||||
//using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests.AugmentersTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class AugmentWithMediaInfoFixture : AugmentMovieInfoFixture<AugmentWithMediaInfo>
|
||||
{
|
||||
[TestCase(Resolution.R720P, Source.BLURAY, Resolution.R1080P)]
|
||||
[TestCase(Resolution.R1080P, Source.TV, Resolution.R720P)]
|
||||
public void should_correct_resolution(Resolution resolution, Source source, Resolution realResolution)
|
||||
{
|
||||
var quality = new QualityModel
|
||||
{
|
||||
Source = source,
|
||||
Resolution = resolution,
|
||||
};
|
||||
MovieInfo.Quality = quality;
|
||||
//namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests.AugmentersTests
|
||||
//{
|
||||
// [TestFixture]
|
||||
// public class AugmentWithMediaInfoFixture : AugmentMovieInfoFixture<AugmentWithMediaInfo>
|
||||
// {
|
||||
// [TestCase(Resolution.R720p, Source.BLURAY, Resolution.R1080p)]
|
||||
// [TestCase(Resolution.R1080p, Source.TV, Resolution.R720p)]
|
||||
// public void should_correct_resolution(Resolution resolution, Source source, Resolution realResolution)
|
||||
// {
|
||||
// var quality = new QualityModel
|
||||
// {
|
||||
// Source = source,
|
||||
// Resolution = resolution,
|
||||
// };
|
||||
// MovieInfo.Quality = quality;
|
||||
|
||||
var realWidth = 480;
|
||||
switch (realResolution)
|
||||
{
|
||||
case Resolution.R720P:
|
||||
realWidth = 1280;
|
||||
break;
|
||||
case Resolution.R1080P:
|
||||
realWidth = 1920;
|
||||
break;
|
||||
case Resolution.R2160P:
|
||||
realWidth = 2160;
|
||||
break;
|
||||
// var realWidth = 480;
|
||||
// switch (realResolution)
|
||||
// {
|
||||
// case Resolution.R720p:
|
||||
// realWidth = 1280;
|
||||
// break;
|
||||
// case Resolution.R1080p:
|
||||
// realWidth = 1920;
|
||||
// break;
|
||||
// case Resolution.R2160p:
|
||||
// realWidth = 2160;
|
||||
// break;
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
var mediaInfo = new MediaInfoModel
|
||||
{
|
||||
Width = realWidth
|
||||
};
|
||||
// var mediaInfo = new MediaInfoModel
|
||||
// {
|
||||
// Width = realWidth
|
||||
// };
|
||||
|
||||
var movieInfo = Subject.AugmentMovieInfo(MovieInfo, mediaInfo);
|
||||
movieInfo.Quality.Resolution.Should().BeEquivalentTo(realResolution);
|
||||
movieInfo.Quality.QualityDetectionSource.Should().BeEquivalentTo(QualityDetectionSource.MediaInfo);
|
||||
}
|
||||
// var movieInfo = Subject.AugmentMovieInfo(MovieInfo, mediaInfo);
|
||||
// movieInfo.Quality.Resolution.Should().BeEquivalentTo(realResolution);
|
||||
// movieInfo.Quality.QualityDetectionSource.Should().BeEquivalentTo(QualityDetectionSource.MediaInfo);
|
||||
// }
|
||||
|
||||
[TestCase(Resolution.R720P, Source.BLURAY, Resolution.R1080P, Modifier.BRDISK)]
|
||||
[TestCase(Resolution.R1080P, Source.BLURAY, Resolution.R720P, Modifier.REMUX)]
|
||||
[TestCase(Resolution.R480P, Source.BLURAY, Resolution.R720P)]
|
||||
[TestCase(Resolution.R720P, Source.DVD, Resolution.R480P)]
|
||||
public void should_not_correct_resolution(Resolution resolution, Source source, Resolution realResolution, Modifier modifier = Modifier.NONE)
|
||||
{
|
||||
var quality = new QualityModel
|
||||
{
|
||||
Source = source,
|
||||
Resolution = resolution,
|
||||
Modifier = modifier,
|
||||
};
|
||||
// [TestCase(Resolution.R720P, Source.BLURAY, Resolution.R1080P, Modifier.BRDISK)]
|
||||
// [TestCase(Resolution.R1080P, Source.BLURAY, Resolution.R720P, Modifier.REMUX)]
|
||||
// [TestCase(Resolution.R480P, Source.BLURAY, Resolution.R720P)]
|
||||
// [TestCase(Resolution.R720P, Source.DVD, Resolution.R480P)]
|
||||
// public void should_not_correct_resolution(Resolution resolution, Source source, Resolution realResolution, Modifier modifier = Modifier.NONE)
|
||||
// {
|
||||
// var quality = new QualityModel
|
||||
// {
|
||||
// Source = source,
|
||||
// Resolution = resolution,
|
||||
// Modifier = modifier,
|
||||
// };
|
||||
|
||||
MovieInfo.Quality = quality;
|
||||
// MovieInfo.Quality = quality;
|
||||
|
||||
var realWidth = 480;
|
||||
switch (realResolution)
|
||||
{
|
||||
case Resolution.R720P:
|
||||
realWidth = 1280;
|
||||
break;
|
||||
case Resolution.R1080P:
|
||||
realWidth = 1920;
|
||||
break;
|
||||
case Resolution.R2160P:
|
||||
realWidth = 2160;
|
||||
break;
|
||||
// var realWidth = 480;
|
||||
// switch (realResolution)
|
||||
// {
|
||||
// case Resolution.R720P:
|
||||
// realWidth = 1280;
|
||||
// break;
|
||||
// case Resolution.R1080P:
|
||||
// realWidth = 1920;
|
||||
// break;
|
||||
// case Resolution.R2160P:
|
||||
// realWidth = 2160;
|
||||
// break;
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
var mediaInfo = new MediaInfoModel
|
||||
{
|
||||
Width = realWidth
|
||||
};
|
||||
// var mediaInfo = new MediaInfoModel
|
||||
// {
|
||||
// Width = realWidth
|
||||
// };
|
||||
|
||||
var movieInfo = Subject.AugmentMovieInfo(MovieInfo, mediaInfo);
|
||||
movieInfo.Quality.Resolution.Should().BeEquivalentTo(resolution);
|
||||
movieInfo.Quality.QualityDetectionSource.Should().BeEquivalentTo(QualityDetectionSource.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
// var movieInfo = Subject.AugmentMovieInfo(MovieInfo, mediaInfo);
|
||||
// movieInfo.Quality.Resolution.Should().BeEquivalentTo(resolution);
|
||||
// movieInfo.Quality.QualityDetectionSource.Should().BeEquivalentTo(QualityDetectionSource.Name);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
@ -24,27 +24,26 @@ public void Setup()
|
||||
|
||||
public static object[] OtherSourceQualityParserCases =
|
||||
{
|
||||
new object[] { "SD TV", Source.TV, Resolution.R480P, Modifier.NONE },
|
||||
new object[] { "SD DVD", Source.DVD, Resolution.R480P, Modifier.NONE },
|
||||
new object[] { "480p WEB-DL", Source.WEBDL, Resolution.R480P, Modifier.NONE },
|
||||
new object[] { "HD TV", Source.TV, Resolution.R720P, Modifier.NONE },
|
||||
new object[] { "1080p HD TV", Source.TV, Resolution.R1080P, Modifier.NONE },
|
||||
new object[] { "2160p HD TV", Source.TV, Resolution.R2160P, Modifier.NONE },
|
||||
new object[] { "720p WEB-DL", Source.WEBDL, Resolution.R720P, Modifier.NONE },
|
||||
new object[] { "1080p WEB-DL", Source.WEBDL, Resolution.R1080P, Modifier.NONE },
|
||||
new object[] { "2160p WEB-DL", Source.WEBDL, Resolution.R2160P, Modifier.NONE },
|
||||
new object[] { "720p BluRay", Source.BLURAY, Resolution.R720P, Modifier.NONE },
|
||||
new object[] { "1080p BluRay", Source.BLURAY, Resolution.R1080P, Modifier.NONE },
|
||||
new object[] { "2160p BluRay", Source.BLURAY, Resolution.R2160P, Modifier.NONE },
|
||||
new object[] { "1080p Remux", Source.BLURAY, Resolution.R1080P, Modifier.REMUX },
|
||||
new object[] { "2160p Remux", Source.BLURAY, Resolution.R2160P, Modifier.REMUX },
|
||||
new object[] { "SD TV", Source.TV, Resolution.R480p, Modifier.NONE },
|
||||
new object[] { "SD DVD", Source.DVD, Resolution.R480p, Modifier.NONE },
|
||||
new object[] { "480p WEB-DL", Source.WEBDL, Resolution.R480p, Modifier.NONE },
|
||||
new object[] { "HD TV", Source.TV, Resolution.R720p, Modifier.NONE },
|
||||
new object[] { "1080p HD TV", Source.TV, Resolution.R1080p, Modifier.NONE },
|
||||
new object[] { "2160p HD TV", Source.TV, Resolution.R2160p, Modifier.NONE },
|
||||
new object[] { "720p WEB-DL", Source.WEBDL, Resolution.R720p, Modifier.NONE },
|
||||
new object[] { "1080p WEB-DL", Source.WEBDL, Resolution.R1080p, Modifier.NONE },
|
||||
new object[] { "2160p WEB-DL", Source.WEBDL, Resolution.R2160p, Modifier.NONE },
|
||||
new object[] { "720p BluRay", Source.BLURAY, Resolution.R720p, Modifier.NONE },
|
||||
new object[] { "1080p BluRay", Source.BLURAY, Resolution.R1080p, Modifier.NONE },
|
||||
new object[] { "2160p BluRay", Source.BLURAY, Resolution.R2160p, Modifier.NONE },
|
||||
new object[] { "1080p Remux", Source.BLURAY, Resolution.R1080p, Modifier.REMUX },
|
||||
new object[] { "2160p Remux", Source.BLURAY, Resolution.R2160p, Modifier.REMUX },
|
||||
};
|
||||
|
||||
[TestCase("Despicable.Me.3.2017.720p.TSRip.x264.AAC-Ozlem", false)]
|
||||
[TestCase("IT.2017.HDTSRip.x264.AAC-Ozlem[ETRG]", false)]
|
||||
public void should_parse_ts(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.TELESYNC, proper, Resolution.R720P);
|
||||
ParseAndVerifyQuality(title, Source.TELESYNC, proper, Resolution.R720p);
|
||||
}
|
||||
|
||||
[TestCase("S07E23 .avi ", false)]
|
||||
@ -65,38 +64,32 @@ public void should_parse_ts(string title, bool proper)
|
||||
[TestCase("Muppet.Babies.S03.TVRip.XviD-NOGRP", false)]
|
||||
public void should_parse_sdtv_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.TV, proper, Resolution.R480P);
|
||||
ParseAndVerifyQuality(title, Source.TV, proper, Resolution.R480p);
|
||||
}
|
||||
|
||||
[TestCase("WEEDS.S03E01-06.DUAL.XviD.Bluray.AC3-REPACK.-HELLYWOOD.avi", true)]
|
||||
[TestCase("The.Shield.S01E13.NTSC.x264-CtrlSD", false)]
|
||||
[TestCase("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", false)]
|
||||
[TestCase("WEEDS.S03E01-06.DUAL.BDRip.X-viD.AC3.-HELLYWOOD", false)]
|
||||
[TestCase("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi", false)]
|
||||
[TestCase("WEEDS.S03E01-06.DUAL.XviD.Bluray.AC3.-HELLYWOOD.avi", false)]
|
||||
[TestCase("The.Girls.Next.Door.S03E06.DVDRip.XviD-WiDE", false)]
|
||||
[TestCase("The.Girls.Next.Door.S03E06.DVD.Rip.XviD-WiDE", false)]
|
||||
[TestCase("the.shield.1x13.circles.ws.xvidvd-tns", false)]
|
||||
[TestCase("the_x-files.9x18.sunshine_days.ac3.ws_dvdrip_xvid-fov.avi", false)]
|
||||
[TestCase("[FroZen] Miyuki - 23 [DVD][7F6170E6]", false)]
|
||||
[TestCase("[Doki] Clannad - 02 (848x480 XviD BD MP3) [95360783]", false)]
|
||||
[TestCase("The.Shield.S01E13.x264-CtrlSD", false)]
|
||||
[TestCase("[HorribleSubs] Yowamushi Pedal - 32 [480p]", false)]
|
||||
[TestCase("[CR] Sailor Moon - 004 [480p][48CE2D0F]", false)]
|
||||
[TestCase("[Hatsuyuki] Naruto Shippuuden - 363 [848x480][ADE35E38]", false)]
|
||||
public void should_parse_dvd_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.DVD, proper, Resolution.R480P);
|
||||
ParseAndVerifyQuality(title, Source.DVD, proper, Resolution.R480p);
|
||||
}
|
||||
|
||||
[TestCase("Elementary.S01E10.The.Leviathan.480p.WEB-DL.x264-mSD", false)]
|
||||
[TestCase("Glee.S04E10.Glee.Actually.480p.WEB-DL.x264-mSD", false)]
|
||||
[TestCase("The.Big.Bang.Theory.S06E11.The.Santa.Simulation.480p.WEB-DL.x264-mSD", false)]
|
||||
[TestCase("Da.Vincis.Demons.S02E04.480p.WEB.DL.nSD.x264-NhaNc3", false)]
|
||||
[TestCase("Series.Title.1x04.ITA.WEBMux.x264-NovaRip", false)]
|
||||
public void should_parse_webdl480p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.WEBDL, proper, Resolution.R480P);
|
||||
ParseAndVerifyQuality(title, Source.WEBDL, proper, Resolution.R480p);
|
||||
}
|
||||
|
||||
[TestCase("Series.Title.1x04.ITA.WEBMux.x264-NovaRip", false)]
|
||||
public void should_parse_webrip480p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.WEBRIP, proper, Resolution.R480p);
|
||||
}
|
||||
|
||||
[TestCase("Heidi Girl of the Alps (BD)(640x480(RAW) (BATCH 1) (1-13)", false)]
|
||||
@ -104,7 +97,7 @@ public void should_parse_webdl480p_quality(string title, bool proper)
|
||||
[TestCase("WEEDS.S03E01-06.DUAL.BDRip.AC3.-HELLYWOOD", false)]
|
||||
public void should_parse_bluray480p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.BLURAY, proper, Resolution.R480P);
|
||||
ParseAndVerifyQuality(title, Source.BLURAY, proper, Resolution.R480p);
|
||||
}
|
||||
|
||||
[TestCase("Dexter - S01E01 - Title [HDTV]", false)]
|
||||
@ -119,7 +112,7 @@ public void should_parse_bluray480p_quality(string title, bool proper)
|
||||
[TestCase("Survivorman.The.Lost.Pilots.Summer.HR.WS.PDTV.x264-DHD", false)]
|
||||
public void should_parse_hdtv720p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.TV, proper, Resolution.R720P);
|
||||
ParseAndVerifyQuality(title, Source.TV, proper, Resolution.R720p);
|
||||
}
|
||||
|
||||
|
||||
@ -129,10 +122,9 @@ public void should_parse_hdtv720p_quality(string title, bool proper)
|
||||
[TestCase("Dexter - S01E01 - Title [HDTV-1080p]", false)]
|
||||
public void should_parse_hdtv1080p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.TV, proper, Resolution.R1080P);
|
||||
ParseAndVerifyQuality(title, Source.TV, proper, Resolution.R1080p);
|
||||
}
|
||||
|
||||
[TestCase("Arrested.Development.S04E01.720p.WEBRip.AAC2.0.x264-NFRiP", false)]
|
||||
[TestCase("Vanguard S01E04 Mexicos Death Train 720p WEB DL", false)]
|
||||
[TestCase("Hawaii Five 0 S02E21 720p WEB DL DD5 1 H 264", false)]
|
||||
[TestCase("Castle S04E22 720p WEB DL DD5 1 H 264 NFHD", false)]
|
||||
@ -146,24 +138,19 @@ public void should_parse_hdtv1080p_quality(string title, bool proper)
|
||||
[TestCase("Castle.S06E23.720p.WebHD.h264-euHD", false)]
|
||||
[TestCase("The.Nightly.Show.2016.03.14.720p.WEB.x264-spamTV", false)]
|
||||
[TestCase("The.Nightly.Show.2016.03.14.720p.WEB.h264-spamTV", false)]
|
||||
[TestCase("Sonny.With.a.Chance.S02E15.720p", false)]
|
||||
[TestCase("S07E23.mkv ", false)]
|
||||
[TestCase("Sonny.With.a.Chance.S02E15.mkv", false)]
|
||||
[TestCase("[Underwater-FFF] No Game No Life - 01 (720p) [27AAA0A0]", false)]
|
||||
[TestCase("[Doki] Mahouka Koukou no Rettousei - 07 (1280x720 Hi10P AAC) [80AF7DDE]", false)]
|
||||
[TestCase("[Doremi].Yes.Pretty.Cure.5.Go.Go!.31.[1280x720].[C65D4B1F].mkv", false)]
|
||||
[TestCase("[HorribleSubs]_Fairy_Tail_-_145_[720p]", false)]
|
||||
[TestCase("[Eveyuu] No Game No Life - 10 [Hi10P 1280x720 H264][10B23BD8]", false)]
|
||||
[TestCase("Movie.Title.ITA.720p.WEBMux.x264-NovaRip", false)]
|
||||
[TestCase("BrainDead.S01E01.The.Insanity.Principle.720p.WEB-DL.DD5.1.H.264-BD", false)]
|
||||
public void should_parse_webdl720p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.WEBDL, proper, Resolution.R720P);
|
||||
ParseAndVerifyQuality(title, Source.WEBDL, proper, Resolution.R720p);
|
||||
}
|
||||
|
||||
[TestCase("Movie.Title.ITA.720p.WEBMux.x264-NovaRip", false)]
|
||||
[TestCase("Arrested.Development.S04E01.720p.WEBRip.AAC2.0.x264-NFRiP", false)]
|
||||
public void should_parse_webrip720p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.WEBRIP, proper, Resolution.R720p);
|
||||
}
|
||||
|
||||
[TestCase("[HorribleSubs] Yowamushi Pedal - 32 [1080p]", false)]
|
||||
[TestCase("Under the Dome S01E10 Let the Games Begin 1080p", false)]
|
||||
[TestCase("Arrested.Development.S04E01.iNTERNAL.1080p.WEBRip.x264-QRUS", false)]
|
||||
[TestCase("CSI NY S09E03 1080p WEB DL DD5 1 H264 NFHD", false)]
|
||||
[TestCase("Two and a Half Men S10E03 1080p WEB DL DD5 1 H 264 NFHD", false)]
|
||||
[TestCase("Criminal.Minds.S08E01.1080p.WEB-DL.DD5.1.H264-NFHD", false)]
|
||||
@ -180,23 +167,34 @@ public void should_parse_webdl720p_quality(string title, bool proper)
|
||||
[TestCase("Series Title S06E08 No One PROPER 1080p WEB H 264-EXCLUSIVE", true)]
|
||||
[TestCase("The.Simpsons.S25E21.Pay.Pal.1080p.WEB-DL.DD5.1.H.264-NTb", false)]
|
||||
[TestCase("The.Simpsons.2017.1080p.WEB-DL.DD5.1.H.264.Remux.-NTb", false)]
|
||||
[TestCase("Series.Title.1x04.ITA.1080p.WEBMux.x264-NovaRip", false)]
|
||||
[TestCase("Fast.and.Furious.Presents.Hobbs.and.Shaw.2019.1080p.AMZN.WEB-DL.DDP5.1.H.264-NTG", false)]
|
||||
public void should_parse_webdl1080p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.WEBDL, proper, Resolution.R1080P);
|
||||
ParseAndVerifyQuality(title, Source.WEBDL, proper, Resolution.R1080p);
|
||||
}
|
||||
|
||||
[TestCase("Arrested.Development.S04E01.iNTERNAL.1080p.WEBRip.x264-QRUS", false)]
|
||||
[TestCase("Series.Title.1x04.ITA.1080p.WEBMux.x264-NovaRip", false)]
|
||||
public void should_parse_webrip1080p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.WEBRIP, proper, Resolution.R1080p);
|
||||
}
|
||||
|
||||
[TestCase("The.Nightly.Show.2016.03.14.2160p.WEB.x264-spamTV", false)]
|
||||
[TestCase("The.Nightly.Show.2016.03.14.2160p.WEB.h264-spamTV", false)]
|
||||
[TestCase("The.Nightly.Show.2016.03.14.2160p.WEB.PROPER.h264-spamTV", true)]
|
||||
public void should_parse_webdl2160p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.WEBDL, proper, Resolution.R2160p);
|
||||
}
|
||||
|
||||
[TestCase("CASANOVA S01E01.2160P AMZN WEBRIP DD2.0 HI10P X264-TROLLUHD", false)]
|
||||
[TestCase("JUST ADD MAGIC S01E01.2160P AMZN WEBRIP DD2.0 X264-TROLLUHD", false)]
|
||||
[TestCase("The.Man.In.The.High.Castle.S01E01.2160p.AMZN.WEBRip.DD2.0.Hi10p.X264-TrollUHD", false)]
|
||||
[TestCase("The Man In the High Castle S01E01 2160p AMZN WEBRip DD2.0 Hi10P x264-TrollUHD", false)]
|
||||
[TestCase("The.Nightly.Show.2016.03.14.2160p.WEB.x264-spamTV", false)]
|
||||
[TestCase("The.Nightly.Show.2016.03.14.2160p.WEB.h264-spamTV", false)]
|
||||
[TestCase("The.Nightly.Show.2016.03.14.2160p.WEB.PROPER.h264-spamTV", true)]
|
||||
public void should_parse_webdl2160p_quality(string title, bool proper)
|
||||
public void should_parse_webrip2160p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.WEBDL, proper, Resolution.R2160P);
|
||||
ParseAndVerifyQuality(title, Source.WEBRIP, proper, Resolution.R2160p);
|
||||
}
|
||||
|
||||
[TestCase("WEEDS.S03E01-06.DUAL.Bluray.AC3.-HELLYWOOD.avi", false)]
|
||||
@ -217,7 +215,7 @@ public void should_parse_webdl2160p_quality(string title, bool proper)
|
||||
[TestCase("Opeth.Garden.Of.The.Titans.Live.At.Red.Rocks.Amphitheatre.2017.720p.MBluRay.x264-TREBLE.mkv", false)]
|
||||
public void should_parse_bluray720p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.BLURAY, proper, Resolution.R720P);
|
||||
ParseAndVerifyQuality(title, Source.BLURAY, proper, Resolution.R720p);
|
||||
}
|
||||
|
||||
[TestCase("Chuck - S01E03 - Come Fly With Me - 1080p BluRay.mkv", false)]
|
||||
@ -234,14 +232,14 @@ public void should_parse_bluray720p_quality(string title, bool proper)
|
||||
[TestCase("Opeth.Garden.Of.The.Titans.Live.At.Red.Rocks.Amphitheatre.2017.1080p.MBluRay.x264-TREBLE.mkv", false)]
|
||||
public void should_parse_bluray1080p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.BLURAY, proper, Resolution.R1080P);
|
||||
ParseAndVerifyQuality(title, Source.BLURAY, proper, Resolution.R1080p);
|
||||
}
|
||||
|
||||
[TestCase("Movie.Name.2004.576p.BDRip.x264-HANDJOB")]
|
||||
[TestCase("Hannibal.S01E05.576p.BluRay.DD5.1.x264-HiSD")]
|
||||
public void should_parse_bluray576p_quality(string title)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.BLURAY, false, Resolution.R576P);
|
||||
ParseAndVerifyQuality(title, Source.BLURAY, false, Resolution.R576p);
|
||||
}
|
||||
|
||||
[TestCase("Contract.to.Kill.2016.REMUX.1080p.BluRay.AVC.DTS-HD.MA.5.1-iFT")]
|
||||
@ -251,7 +249,7 @@ public void should_parse_bluray576p_quality(string title)
|
||||
[TestCase("Wildling.2018.1080p.BluRay.REMUX.MPEG-2.DTS-HD.MA.5.1-EPSiLON")]
|
||||
public void should_parse_remux1080p_quality(string title)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.BLURAY, false, Resolution.R1080P, Modifier.REMUX);
|
||||
ParseAndVerifyQuality(title, Source.BLURAY, false, Resolution.R1080p, Modifier.REMUX);
|
||||
}
|
||||
|
||||
[TestCase("Contract.to.Kill.2016.REMUX.2160p.BluRay.AVC.DTS-HD.MA.5.1-iFT")]
|
||||
@ -259,7 +257,7 @@ public void should_parse_remux1080p_quality(string title)
|
||||
[TestCase("The.Shining.1980.2160p.UHD.BluRay.Remux.HDR.HEVC.DTS-HD.MA.5.1-PmP.mkv")]
|
||||
public void should_parse_remux2160p_quality(string title)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.BLURAY, false, Resolution.R2160P, Modifier.REMUX);
|
||||
ParseAndVerifyQuality(title, Source.BLURAY, false, Resolution.R2160p, Modifier.REMUX);
|
||||
}
|
||||
|
||||
[TestCase("G.I.Joe.Retaliation.2013.BDISO")]
|
||||
@ -270,13 +268,7 @@ public void should_parse_remux2160p_quality(string title)
|
||||
[TestCase("Daylight.1996.Bluray.ISO")]
|
||||
public void should_parse_brdisk_1080p_quality(string title)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.BLURAY, false, Resolution.R1080P, Modifier.BRDISK);
|
||||
}
|
||||
|
||||
[TestCase("Stripes (1981) 1080i HDTV DD5.1 MPEG2-TrollHD")]
|
||||
public void should_parse_rawhd_quality(string title)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Source.TV, false, Resolution.Unknown, Modifier.RAWHD);
|
||||
ParseAndVerifyQuality(title, Source.BLURAY, false, Resolution.R1080p, Modifier.BRDISK);
|
||||
}
|
||||
|
||||
//[TestCase("POI S02E11 1080i HDTV DD5.1 MPEG2-TrollHD", false)]
|
||||
@ -371,12 +363,12 @@ private void ParseAndVerifyQuality(string title, Source source, bool proper, Res
|
||||
var result = QualityParser.ParseQuality(title);
|
||||
if (resolution != Resolution.Unknown)
|
||||
{
|
||||
result.Resolution.Should().Be(resolution);
|
||||
result.Quality.Resolution.Should().Be((int)resolution);
|
||||
}
|
||||
result.Source.Should().Be(source);
|
||||
result.Quality.Source.Should().Be(source);
|
||||
if (modifier != Modifier.NONE)
|
||||
{
|
||||
result.Modifier.Should().Be(modifier);
|
||||
result.Quality.Modifier.Should().Be(modifier);
|
||||
}
|
||||
|
||||
var version = proper ? 2 : 1;
|
||||
|
@ -1,6 +1,7 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.Test.Qualities
|
||||
@ -11,52 +12,52 @@ public class QualityFinderFixture
|
||||
[TestCase(Source.CAM, 480, Modifier.NONE)]
|
||||
[TestCase(Source.CAM, 1080, Modifier.NONE)]
|
||||
[TestCase(Source.CAM, 0, Modifier.NONE)]
|
||||
public void should_return_CAM(Source source, Resolution resolution, Modifier modifier)
|
||||
public void should_return_CAM(Source source, int resolution, Modifier modifier)
|
||||
{
|
||||
QualityFinder.FindBySourceAndResolution(source, resolution, modifier).Should().Be(Quality.CAM);
|
||||
}
|
||||
|
||||
[TestCase(Source.CAM, 1080, Modifier.SCREENER)]
|
||||
[TestCase(Source.CAM, 0, Modifier.SCREENER)]
|
||||
public void should_return_Unknown(Source source, Resolution resolution, Modifier modifier)
|
||||
public void should_return_Unknown(Source source, int resolution, Modifier modifier)
|
||||
{
|
||||
QualityFinder.FindBySourceAndResolution(source, resolution, modifier).Should().Be(Quality.Unknown);
|
||||
}
|
||||
|
||||
[TestCase(Source.DVD, 480, Modifier.REMUX)]
|
||||
public void should_return_DVD_Remux(Source source, Resolution resolution, Modifier modifier)
|
||||
public void should_return_DVD_Remux(Source source, int resolution, Modifier modifier)
|
||||
{
|
||||
QualityFinder.FindBySourceAndResolution(source, resolution, modifier).Should().Be(Quality.DVDR);
|
||||
}
|
||||
|
||||
[TestCase(Source.DVD, 480, Modifier.NONE)]
|
||||
public void should_return_DVD(Source source, Resolution resolution, Modifier modifier)
|
||||
public void should_return_DVD(Source source, int resolution, Modifier modifier)
|
||||
{
|
||||
QualityFinder.FindBySourceAndResolution(source, resolution, modifier).Should().Be(Quality.DVD);
|
||||
}
|
||||
|
||||
[TestCase(Source.TV, 480, Modifier.NONE)]
|
||||
public void should_return_SDTV(Source source, Resolution resolution, Modifier modifier)
|
||||
public void should_return_SDTV(Source source, int resolution, Modifier modifier)
|
||||
{
|
||||
QualityFinder.FindBySourceAndResolution(source, resolution, modifier).Should().Be(Quality.SDTV);
|
||||
}
|
||||
|
||||
[TestCase(Source.TV, 720, Modifier.NONE)]
|
||||
[TestCase(Source.UNKNOWN, 720, Modifier.NONE)]
|
||||
public void should_return_HDTV_720p(Source source, Resolution resolution, Modifier modifier)
|
||||
public void should_return_HDTV_720p(Source source, int resolution, Modifier modifier)
|
||||
{
|
||||
QualityFinder.FindBySourceAndResolution(source, resolution, modifier).Should().Be(Quality.HDTV720p);
|
||||
}
|
||||
|
||||
[TestCase(Source.TV, 1080, Modifier.NONE)]
|
||||
[TestCase(Source.UNKNOWN, 1080, Modifier.NONE)]
|
||||
public void should_return_HDTV_1080p(Source source, Resolution resolution, Modifier modifier)
|
||||
public void should_return_HDTV_1080p(Source source, int resolution, Modifier modifier)
|
||||
{
|
||||
QualityFinder.FindBySourceAndResolution(source, resolution, modifier).Should().Be(Quality.HDTV1080p);
|
||||
}
|
||||
|
||||
[TestCase(Source.BLURAY, 720, Modifier.NONE)]
|
||||
public void should_return_Bluray720p(Source source, Resolution resolution, Modifier modifier)
|
||||
public void should_return_Bluray720p(Source source, int resolution, Modifier modifier)
|
||||
{
|
||||
QualityFinder.FindBySourceAndResolution(source, resolution, modifier).Should().Be(Quality.Bluray720p);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.CustomFormats
|
||||
@ -74,11 +75,11 @@ private bool DoesItMatchWithoutMods(ParsedMovieInfo movieInfo)
|
||||
case TagType.Language:
|
||||
return movieInfo.Languages.Contains((Language)Value);
|
||||
case TagType.Resolution:
|
||||
return movieInfo.Quality.Resolution == (Resolution) Value;
|
||||
return movieInfo.Quality.Quality.Resolution == (int)(Resolution) Value;
|
||||
case TagType.Modifier:
|
||||
return movieInfo.Quality.Modifier == (Modifier) Value;
|
||||
return movieInfo.Quality.Quality.Modifier == (Modifier) Value;
|
||||
case TagType.Source:
|
||||
return movieInfo.Quality.Source == (Source) Value;
|
||||
return movieInfo.Quality.Quality.Source == (Source) Value;
|
||||
case TagType.Size:
|
||||
var size = (movieInfo.ExtraInfo.GetValueOrDefault("Size", 0.0) as long?) ?? 0;
|
||||
var tuple = Value as (long, long)? ?? (0, 0);
|
||||
@ -108,19 +109,19 @@ private void ParseRawMatch(Match match)
|
||||
switch (value)
|
||||
{
|
||||
case "2160":
|
||||
Value = Resolution.R2160P;
|
||||
Value = Resolution.R2160p;
|
||||
break;
|
||||
case "1080":
|
||||
Value = Resolution.R1080P;
|
||||
Value = Resolution.R1080p;
|
||||
break;
|
||||
case "720":
|
||||
Value = Resolution.R720P;
|
||||
Value = Resolution.R720p;
|
||||
break;
|
||||
case "576":
|
||||
Value = Resolution.R576P;
|
||||
Value = Resolution.R576p;
|
||||
break;
|
||||
case "480":
|
||||
Value = Resolution.R480P;
|
||||
Value = Resolution.R480p;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -248,16 +249,6 @@ public enum TagModifier
|
||||
AbsolutelyRequired = 4
|
||||
}
|
||||
|
||||
public enum Resolution
|
||||
{
|
||||
Unknown = 0,
|
||||
R480P = 480,
|
||||
R576P = 576,
|
||||
R720P = 720,
|
||||
R1080P = 1080,
|
||||
R2160P = 2160
|
||||
}
|
||||
|
||||
public enum Source
|
||||
{
|
||||
UNKNOWN = 0,
|
||||
@ -268,6 +259,7 @@ public enum Source
|
||||
DVD,
|
||||
TV,
|
||||
WEBDL,
|
||||
WEBRIP,
|
||||
BLURAY
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,245 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using FluentMigrator;
|
||||
using Newtonsoft.Json;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(159)]
|
||||
public class add_webrip_qualites : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Execute.WithConnection(ConvertProfile);
|
||||
}
|
||||
|
||||
private void ConvertProfile(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
var updater = new ProfileUpdater159(conn, tran);
|
||||
|
||||
// updater.SplitQualityAppend(8, 12); // APE after Flac
|
||||
// updater.SplitQualityAppend(5, 14); // APE after Flac
|
||||
// updater.SplitQualityAppend(3, 15); // APE after Flacupdater.SplitQualityAppend(8, 12); // APE after Flac
|
||||
// updater.SplitQualityAppend(18, 17); // APE after Flac
|
||||
|
||||
updater.CreateGroupAt(8, "WEB 480p", new int[]{12}); // Group WEBRip480p with WEBDL480p
|
||||
updater.CreateGroupAt(5, "WEB 720p", new int[]{14}); // Group WEBRip720p with WEBDL720p
|
||||
updater.CreateGroupAt(3, "WEB 1080p", new int[]{15}); // Group WEBRip1080p with WEBDL1080p
|
||||
updater.CreateGroupAt(18, "WEB 2160p", new int[]{17}); // Group WEBRip2160p with WEBDL2160p
|
||||
|
||||
updater.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
public class Profile159
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int Cutoff { get; set; }
|
||||
public List<ProfileItem159> Items { get; set; }
|
||||
}
|
||||
|
||||
public class ProfileItem159
|
||||
{
|
||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
public int? Quality { get; set; }
|
||||
public List<ProfileItem159> Items { get; set; }
|
||||
public bool Allowed { get; set; }
|
||||
|
||||
public ProfileItem159()
|
||||
{
|
||||
Items = new List<ProfileItem159>();
|
||||
}
|
||||
}
|
||||
|
||||
public class ProfileUpdater159
|
||||
{
|
||||
private readonly IDbConnection _connection;
|
||||
private readonly IDbTransaction _transaction;
|
||||
|
||||
private List<Profile159> _profiles;
|
||||
private HashSet<Profile159> _changedProfiles = new HashSet<Profile159>();
|
||||
|
||||
public ProfileUpdater159(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
_connection = conn;
|
||||
_transaction = tran;
|
||||
|
||||
_profiles = GetProfiles();
|
||||
}
|
||||
|
||||
public void Commit()
|
||||
{
|
||||
foreach (var profile in _changedProfiles)
|
||||
{
|
||||
using (var updateProfileCmd = _connection.CreateCommand())
|
||||
{
|
||||
updateProfileCmd.Transaction = _transaction;
|
||||
updateProfileCmd.CommandText =
|
||||
"UPDATE Profiles SET Name = ?, Cutoff = ?, Items = ? WHERE Id = ?";
|
||||
updateProfileCmd.AddParameter(profile.Name);
|
||||
updateProfileCmd.AddParameter(profile.Cutoff);
|
||||
updateProfileCmd.AddParameter(profile.Items.ToJson());
|
||||
updateProfileCmd.AddParameter(profile.Id);
|
||||
|
||||
updateProfileCmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
_changedProfiles.Clear();
|
||||
}
|
||||
|
||||
public void CreateGroupAt(int find, string name, int[] newQualities)
|
||||
{
|
||||
foreach (var profile in _profiles)
|
||||
{
|
||||
var nextGroup = 1000;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (profile.Items.FindIndex(v => v.Id == nextGroup) > -1)
|
||||
{
|
||||
nextGroup++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var findIndex = profile.Items.FindIndex(v =>
|
||||
{
|
||||
return v.Quality == find || (v.Items != null && v.Items.Any(b => b.Quality == find));
|
||||
});
|
||||
|
||||
var isGrouped = !profile.Items.Any(p => p.Quality == find);
|
||||
|
||||
|
||||
if (findIndex > -1 && !isGrouped)
|
||||
{
|
||||
var findQuality = profile.Items[findIndex];
|
||||
|
||||
var groupItems = new List<ProfileItem159>();
|
||||
|
||||
foreach (var newQuality in newQualities)
|
||||
{
|
||||
groupItems.Add(new ProfileItem159
|
||||
{
|
||||
Quality = newQuality,
|
||||
Allowed = findQuality.Allowed
|
||||
});
|
||||
}
|
||||
|
||||
groupItems.Add(new ProfileItem159
|
||||
{
|
||||
Quality = find,
|
||||
Allowed = findQuality.Allowed
|
||||
});
|
||||
|
||||
profile.Items.Insert(findIndex, new ProfileItem159
|
||||
{
|
||||
Id = nextGroup,
|
||||
Name = name,
|
||||
Quality = null,
|
||||
Items = groupItems,
|
||||
Allowed = findQuality.Allowed
|
||||
});
|
||||
}
|
||||
else if (findIndex > -1 && isGrouped)
|
||||
{
|
||||
var findQuality = profile.Items[findIndex];
|
||||
|
||||
foreach (var newQuality in newQualities)
|
||||
{
|
||||
profile.Items[findIndex].Items.Insert(findIndex, new ProfileItem159
|
||||
{
|
||||
Quality = newQuality,
|
||||
Allowed = findQuality.Allowed
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the ID isn't found for some reason (mangled migration 71?)
|
||||
|
||||
var groupItems = new List<ProfileItem159>();
|
||||
|
||||
foreach (var newQuality in newQualities)
|
||||
{
|
||||
groupItems.Add(new ProfileItem159
|
||||
{
|
||||
Quality = newQuality,
|
||||
Allowed = false
|
||||
});
|
||||
}
|
||||
|
||||
groupItems.Add(new ProfileItem159
|
||||
{
|
||||
Quality = find,
|
||||
Allowed = false
|
||||
});
|
||||
|
||||
profile.Items.Add(new ProfileItem159
|
||||
{
|
||||
Id = nextGroup,
|
||||
Name = name,
|
||||
Quality = null,
|
||||
Items = groupItems,
|
||||
Allowed = false
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var quality in newQualities)
|
||||
{
|
||||
var index = profile.Items.FindIndex(v => v.Quality == quality);
|
||||
|
||||
if (index > -1)
|
||||
{
|
||||
profile.Items.RemoveAt(index);
|
||||
}
|
||||
|
||||
if (profile.Cutoff == quality)
|
||||
{
|
||||
profile.Cutoff = nextGroup;
|
||||
}
|
||||
}
|
||||
|
||||
_changedProfiles.Add(profile);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Profile159> GetProfiles()
|
||||
{
|
||||
var profiles = new List<Profile159>();
|
||||
|
||||
using (var getProfilesCmd = _connection.CreateCommand())
|
||||
{
|
||||
getProfilesCmd.Transaction = _transaction;
|
||||
getProfilesCmd.CommandText = @"SELECT Id, Name, Cutoff, Items FROM Profiles";
|
||||
|
||||
using (var profileReader = getProfilesCmd.ExecuteReader())
|
||||
{
|
||||
while (profileReader.Read())
|
||||
{
|
||||
profiles.Add(new Profile159
|
||||
{
|
||||
Id = profileReader.GetInt32(0),
|
||||
Name = profileReader.GetString(1),
|
||||
Cutoff = profileReader.GetInt32(2),
|
||||
Items = Json.Deserialize<List<ProfileItem159>>(profileReader.GetString(3))
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return profiles;
|
||||
}
|
||||
}
|
||||
}
|
@ -42,15 +42,15 @@ public DownloadDecisionMaker(IEnumerable<IDecisionEngineSpecification> specifica
|
||||
|
||||
public List<DownloadDecision> GetRssDecision(List<ReleaseInfo> reports)
|
||||
{
|
||||
return GetMovieDecisions(reports).ToList();
|
||||
return GetDecisions(reports).ToList();
|
||||
}
|
||||
|
||||
public List<DownloadDecision> GetSearchDecision(List<ReleaseInfo> reports, SearchCriteriaBase searchCriteriaBase)
|
||||
{
|
||||
return GetMovieDecisions(reports, searchCriteriaBase).ToList();
|
||||
return GetDecisions(reports, searchCriteriaBase).ToList();
|
||||
}
|
||||
|
||||
private IEnumerable<DownloadDecision> GetMovieDecisions(List<ReleaseInfo> reports, SearchCriteriaBase searchCriteria = null)
|
||||
private IEnumerable<DownloadDecision> GetDecisions(List<ReleaseInfo> reports, SearchCriteriaBase searchCriteria = null)
|
||||
{
|
||||
if (reports.Any())
|
||||
{
|
||||
@ -98,12 +98,6 @@ private IEnumerable<DownloadDecision> GetMovieDecisions(List<ReleaseInfo> report
|
||||
result.Movie = null; //To ensure we have a remote movie, else null exception on next line!
|
||||
result.RemoteMovie.ParsedMovieInfo = parsedMovieInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Enhance Parsed Movie Info!
|
||||
result.RemoteMovie.ParsedMovieInfo = Parser.Parser.ParseMinimalMovieTitle(parsedMovieInfo.MovieTitle,
|
||||
result.RemoteMovie.Movie.Title, parsedMovieInfo.Year);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
@ -148,7 +142,8 @@ private IEnumerable<DownloadDecision> GetMovieDecisions(List<ReleaseInfo> report
|
||||
}
|
||||
else
|
||||
{
|
||||
remoteMovie.DownloadAllowed = true;
|
||||
// _aggregationService.Augment(remoteMovie);
|
||||
remoteMovie.DownloadAllowed = remoteMovie.Movie != null;
|
||||
decision = GetDecisionForReport(remoteMovie, searchCriteria);
|
||||
}
|
||||
|
||||
|
@ -75,24 +75,12 @@ public void StopTracking(List<string> downloadIds)
|
||||
|
||||
public TrackedDownload TrackDownload(DownloadClientDefinition downloadClient, DownloadClientItem downloadItem)
|
||||
{
|
||||
if (downloadItem.DownloadId.IsNullOrWhiteSpace())
|
||||
{
|
||||
_logger.Warn("The following download client item ({0}) has no download hash (id), so it cannot be tracked: {1}",
|
||||
downloadClient.Name, downloadItem.Title);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (downloadItem.Title.IsNullOrWhiteSpace())
|
||||
{
|
||||
_logger.Warn("The following download client item ({0}) has no title so it cannot be tracked: {1}",
|
||||
downloadClient.Name, downloadItem.Title);
|
||||
return null;
|
||||
}
|
||||
|
||||
var existingItem = Find(downloadItem.DownloadId);
|
||||
|
||||
if (existingItem != null && existingItem.State != TrackedDownloadStage.Downloading)
|
||||
{
|
||||
LogItemChange(existingItem, existingItem.DownloadItem, downloadItem);
|
||||
|
||||
existingItem.DownloadItem = downloadItem;
|
||||
existingItem.IsTrackable = true;
|
||||
|
||||
@ -141,9 +129,10 @@ public TrackedDownload TrackDownload(DownloadClientDefinition downloadClient, Do
|
||||
}
|
||||
}
|
||||
|
||||
// Track it so it can be displayed in the queue even though we can't determine which movie it is for
|
||||
if (trackedDownload.RemoteMovie == null)
|
||||
{
|
||||
return null;
|
||||
_logger.Trace("No Movie found for download '{0}'", trackedDownload.DownloadItem.Title);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -152,6 +141,8 @@ public TrackedDownload TrackDownload(DownloadClientDefinition downloadClient, Do
|
||||
return null;
|
||||
}
|
||||
|
||||
LogItemChange(trackedDownload, existingItem?.DownloadItem, trackedDownload.DownloadItem);
|
||||
|
||||
_cache.Set(trackedDownload.DownloadItem.DownloadId, trackedDownload);
|
||||
return trackedDownload;
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
|
||||
@ -9,115 +7,82 @@ namespace NzbDrone.Core.MediaFiles
|
||||
{
|
||||
public static class MediaFileExtensions
|
||||
{
|
||||
private static Dictionary<string, Source> _fileExtensions;
|
||||
private static Dictionary<string, Resolution> _resolutionExt;
|
||||
private static Dictionary<string, Quality> _fileExtensions;
|
||||
|
||||
static MediaFileExtensions()
|
||||
{
|
||||
_fileExtensions = new Dictionary<string, Source>
|
||||
_fileExtensions = new Dictionary<string, Quality>
|
||||
{
|
||||
//Unknown
|
||||
{ ".webm", Source.UNKNOWN },
|
||||
{ ".webm", Quality.Unknown },
|
||||
|
||||
//SDTV
|
||||
{ ".m4v", Source.TV },
|
||||
{ ".3gp", Source.TV },
|
||||
{ ".nsv", Source.TV },
|
||||
{ ".ty", Source.TV },
|
||||
{ ".strm", Source.TV },
|
||||
{ ".rm", Source.TV },
|
||||
{ ".rmvb", Source.TV },
|
||||
{ ".m3u", Source.TV },
|
||||
{ ".ifo", Source.TV },
|
||||
{ ".mov", Source.TV },
|
||||
{ ".qt", Source.TV },
|
||||
{ ".divx", Source.TV },
|
||||
{ ".xvid", Source.TV },
|
||||
{ ".bivx", Source.TV },
|
||||
{ ".nrg", Source.TV },
|
||||
{ ".pva", Source.TV },
|
||||
{ ".wmv", Source.TV },
|
||||
{ ".asf", Source.TV },
|
||||
{ ".asx", Source.TV },
|
||||
{ ".ogm", Source.TV },
|
||||
{ ".ogv", Source.TV },
|
||||
{ ".m2v", Source.TV },
|
||||
{ ".avi", Source.TV },
|
||||
{ ".bin", Source.TV },
|
||||
{ ".dat", Source.TV },
|
||||
{ ".dvr-ms", Source.TV },
|
||||
{ ".mpg", Source.TV },
|
||||
{ ".mpeg", Source.TV },
|
||||
{ ".mp4", Source.TV },
|
||||
{ ".avc", Source.TV },
|
||||
{ ".vp3", Source.TV },
|
||||
{ ".svq3", Source.TV },
|
||||
{ ".nuv", Source.TV },
|
||||
{ ".viv", Source.TV },
|
||||
{ ".dv", Source.TV },
|
||||
{ ".fli", Source.TV },
|
||||
{ ".flv", Source.TV },
|
||||
{ ".wpl", Source.TV },
|
||||
{ ".m4v", Quality.SDTV },
|
||||
{ ".3gp", Quality.SDTV },
|
||||
{ ".nsv", Quality.SDTV },
|
||||
{ ".ty", Quality.SDTV },
|
||||
{ ".strm", Quality.SDTV },
|
||||
{ ".rm", Quality.SDTV },
|
||||
{ ".rmvb", Quality.SDTV },
|
||||
{ ".m3u", Quality.SDTV },
|
||||
{ ".ifo", Quality.SDTV },
|
||||
{ ".mov", Quality.SDTV },
|
||||
{ ".qt", Quality.SDTV },
|
||||
{ ".divx", Quality.SDTV },
|
||||
{ ".xvid", Quality.SDTV },
|
||||
{ ".bivx", Quality.SDTV },
|
||||
{ ".nrg", Quality.SDTV },
|
||||
{ ".pva", Quality.SDTV },
|
||||
{ ".wmv", Quality.SDTV },
|
||||
{ ".asf", Quality.SDTV },
|
||||
{ ".asx", Quality.SDTV },
|
||||
{ ".ogm", Quality.SDTV },
|
||||
{ ".ogv", Quality.SDTV },
|
||||
{ ".m2v", Quality.SDTV },
|
||||
{ ".avi", Quality.SDTV },
|
||||
{ ".bin", Quality.SDTV },
|
||||
{ ".dat", Quality.SDTV },
|
||||
{ ".dvr-ms", Quality.SDTV },
|
||||
{ ".mpg", Quality.SDTV },
|
||||
{ ".mpeg", Quality.SDTV },
|
||||
{ ".mp4", Quality.SDTV },
|
||||
{ ".avc", Quality.SDTV },
|
||||
{ ".vp3", Quality.SDTV },
|
||||
{ ".svq3", Quality.SDTV },
|
||||
{ ".nuv", Quality.SDTV },
|
||||
{ ".viv", Quality.SDTV },
|
||||
{ ".dv", Quality.SDTV },
|
||||
{ ".fli", Quality.SDTV },
|
||||
{ ".flv", Quality.SDTV },
|
||||
{ ".wpl", Quality.SDTV },
|
||||
|
||||
//DVD
|
||||
{ ".img", Source.DVD },
|
||||
{ ".iso", Source.DVD },
|
||||
{ ".vob", Source.DVD },
|
||||
{ ".img", Quality.DVD },
|
||||
{ ".iso", Quality.DVD },
|
||||
{ ".vob", Quality.DVD },
|
||||
|
||||
//HD
|
||||
{ ".mkv", Source.WEBDL },
|
||||
{ ".mk3d", Source.WEBDL},
|
||||
{ ".ts", Source.TV },
|
||||
{ ".wtv", Source.TV },
|
||||
{ ".mkv", Quality.WEBDL720p },
|
||||
{ ".mk3d", Quality.WEBDL720p},
|
||||
{ ".ts", Quality.SDTV },
|
||||
{ ".wtv", Quality.SDTV },
|
||||
|
||||
//Bluray
|
||||
{ ".m2ts", Source.BLURAY }
|
||||
};
|
||||
|
||||
_resolutionExt = new Dictionary<string, Resolution>
|
||||
{
|
||||
//HD
|
||||
{ ".mkv", Resolution.R720P },
|
||||
{ ".mk3d", Resolution.R720P },
|
||||
{ ".ts", Resolution.R720P },
|
||||
{ ".wtv", Resolution.R720P },
|
||||
|
||||
//Bluray
|
||||
{ ".m2ts", Resolution.R720P }
|
||||
{ ".m2ts", Quality.Bluray720p}
|
||||
};
|
||||
}
|
||||
|
||||
public static HashSet<string> Extensions => new HashSet<string>(_fileExtensions.Keys, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public static Source GetSourceForExtension(string extension)
|
||||
public static Quality GetQualityForExtension(string extension)
|
||||
{
|
||||
if (_fileExtensions.ContainsKey(extension))
|
||||
{
|
||||
return _fileExtensions[extension];
|
||||
}
|
||||
|
||||
return Source.UNKNOWN;
|
||||
return Quality.Unknown;
|
||||
}
|
||||
|
||||
public static Resolution GetResolutionForExtension(string extension)
|
||||
{
|
||||
if (_resolutionExt.ContainsKey(extension))
|
||||
{
|
||||
return _resolutionExt[extension];
|
||||
}
|
||||
|
||||
var source = Source.UNKNOWN;
|
||||
if (_fileExtensions.ContainsKey(extension))
|
||||
{
|
||||
source = _fileExtensions[extension];
|
||||
}
|
||||
|
||||
if (source == Source.DVD || source == Source.TV)
|
||||
{
|
||||
return Resolution.R480P;
|
||||
}
|
||||
|
||||
return Resolution.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
using NLog;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Quality;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
@ -28,11 +29,12 @@ public LocalMovie Aggregate(LocalMovie localMovie, bool otherFiles)
|
||||
|
||||
var source = Source.UNKNOWN;
|
||||
var sourceConfidence = Confidence.Default;
|
||||
var resolution = Resolution.Unknown;
|
||||
var resolution = 0;
|
||||
var resolutionConfidence = Confidence.Default;
|
||||
var modifier = Modifier.NONE;
|
||||
var modifierConfidence = Confidence.Default;
|
||||
var revison = new Revision();
|
||||
var customFormats = new List<CustomFormat>();
|
||||
|
||||
foreach (var augmentedQuality in augmentedQualities)
|
||||
{
|
||||
@ -61,11 +63,18 @@ public LocalMovie Aggregate(LocalMovie localMovie, bool otherFiles)
|
||||
{
|
||||
revison = augmentedQuality.Revision;
|
||||
}
|
||||
|
||||
if (augmentedQuality.CustomFormats != null)
|
||||
{
|
||||
var newFormats = augmentedQuality.CustomFormats.Where(c => !customFormats.Any(p => p.Id == c.Id));
|
||||
|
||||
customFormats.AddRange(newFormats);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.Trace("Finding quality. Source: {0}. Resolution: {1}. Modifier {2}", source, resolution, modifier);
|
||||
|
||||
var quality = new QualityModel(QualityFinder.FindBySourceAndResolution(source, resolution, modifier), revison);
|
||||
var quality = new QualityModel(QualityFinder.FindBySourceAndResolution(source, resolution, modifier), revison, customFormats);
|
||||
|
||||
if (resolutionConfidence == Confidence.MediaInfo)
|
||||
{
|
||||
|
@ -19,7 +19,8 @@ public AugmentQualityResult AugmentQuality(LocalMovie localMovie)
|
||||
Confidence.Tag,
|
||||
quality.Quality.Modifier,
|
||||
Confidence.Tag,
|
||||
quality.Revision);
|
||||
quality.Revision,
|
||||
quality.CustomFormats);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,8 @@ public AugmentQualityResult AugmentQuality(LocalMovie localMovie)
|
||||
confidence,
|
||||
quality.Quality.Modifier,
|
||||
confidence,
|
||||
quality.Revision);
|
||||
quality.Revision,
|
||||
quality.CustomFormats);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,8 @@ public AugmentQualityResult AugmentQuality(LocalMovie localMovie)
|
||||
Confidence.Tag,
|
||||
quality.Quality.Modifier,
|
||||
Confidence.Tag,
|
||||
quality.Revision);
|
||||
quality.Revision,
|
||||
quality.CustomFormats);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Quality
|
||||
@ -16,22 +16,22 @@ public AugmentQualityResult AugmentQuality(LocalMovie localMovie)
|
||||
|
||||
if (width >= 3200)
|
||||
{
|
||||
return AugmentQualityResult.ResolutionOnly(Resolution.R2160P, Confidence.MediaInfo);
|
||||
return AugmentQualityResult.ResolutionOnly((int)Resolution.R2160p, Confidence.MediaInfo);
|
||||
}
|
||||
|
||||
if (width >= 1800)
|
||||
{
|
||||
return AugmentQualityResult.ResolutionOnly(Resolution.R1080P, Confidence.MediaInfo);
|
||||
return AugmentQualityResult.ResolutionOnly((int)Resolution.R1080p, Confidence.MediaInfo);
|
||||
}
|
||||
|
||||
if (width >= 1200)
|
||||
{
|
||||
return AugmentQualityResult.ResolutionOnly(Resolution.R720P, Confidence.MediaInfo);
|
||||
return AugmentQualityResult.ResolutionOnly((int)Resolution.R720p, Confidence.MediaInfo);
|
||||
}
|
||||
|
||||
if (width > 0)
|
||||
{
|
||||
return AugmentQualityResult.ResolutionOnly(Resolution.R480P, Confidence.MediaInfo);
|
||||
return AugmentQualityResult.ResolutionOnly((int)Resolution.R480p, Confidence.MediaInfo);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -1,5 +1,7 @@
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Quality
|
||||
{
|
||||
@ -7,19 +9,22 @@ public class AugmentQualityResult
|
||||
{
|
||||
public Source Source { get; set; }
|
||||
public Confidence SourceConfidence { get; set; }
|
||||
public Resolution Resolution { get; set; }
|
||||
public int Resolution { get; set; }
|
||||
public Confidence ResolutionConfidence { get; set; }
|
||||
public Modifier Modifier { get; set; }
|
||||
public Confidence ModifierConfidence { get; set; }
|
||||
public Revision Revision { get; set; }
|
||||
|
||||
public List<CustomFormat> CustomFormats { get; set; }
|
||||
|
||||
public AugmentQualityResult(Source source,
|
||||
Confidence sourceConfidence,
|
||||
Resolution resolution,
|
||||
int resolution,
|
||||
Confidence resolutionConfidence,
|
||||
Modifier modifier,
|
||||
Confidence modifierConfidence,
|
||||
Revision revision)
|
||||
Revision revision,
|
||||
List<CustomFormat> customFormats)
|
||||
{
|
||||
Source = source;
|
||||
SourceConfidence = sourceConfidence;
|
||||
@ -28,21 +33,22 @@ public AugmentQualityResult(Source source,
|
||||
Modifier = modifier;
|
||||
ModifierConfidence = modifierConfidence;
|
||||
Revision = revision;
|
||||
CustomFormats = customFormats;
|
||||
}
|
||||
|
||||
public static AugmentQualityResult SourceOnly(Source source, Confidence sourceConfidence)
|
||||
{
|
||||
return new AugmentQualityResult(source, sourceConfidence, 0, Confidence.Default, Modifier.NONE, Confidence.Default, null);
|
||||
return new AugmentQualityResult(source, sourceConfidence, 0, Confidence.Default, Modifier.NONE, Confidence.Default, null, null);
|
||||
}
|
||||
|
||||
public static AugmentQualityResult ResolutionOnly(Resolution resolution, Confidence resolutionConfidence)
|
||||
public static AugmentQualityResult ResolutionOnly(int resolution, Confidence resolutionConfidence)
|
||||
{
|
||||
return new AugmentQualityResult(Source.UNKNOWN, Confidence.Default, resolution, resolutionConfidence, Modifier.NONE, Confidence.Default, null);
|
||||
return new AugmentQualityResult(Source.UNKNOWN, Confidence.Default, resolution, resolutionConfidence, Modifier.NONE, Confidence.Default, null, null);
|
||||
}
|
||||
|
||||
public static AugmentQualityResult ModifierOnly(Modifier modifier, Confidence modifierConfidence)
|
||||
{
|
||||
return new AugmentQualityResult(Source.UNKNOWN, Confidence.Default, 0, Confidence.Default, modifier, modifierConfidence, null);
|
||||
return new AugmentQualityResult(Source.UNKNOWN, Confidence.Default, 0, Confidence.Default, modifier, modifierConfidence, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ public class ImportDecisionMaker : IMakeImportDecision
|
||||
private readonly IQualityDefinitionService _qualitiesService;
|
||||
private readonly IConfigService _config;
|
||||
private readonly IHistoryService _historyService;
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly ICached<string> _warnedFiles;
|
||||
private readonly Logger _logger;
|
||||
|
||||
@ -47,6 +48,7 @@ public ImportDecisionMaker(IEnumerable<IImportDecisionEngineSpecification> speci
|
||||
IQualityDefinitionService qualitiesService,
|
||||
IConfigService config,
|
||||
IHistoryService historyService,
|
||||
IParsingService parsingService,
|
||||
ICacheManager cacheManager,
|
||||
Logger logger)
|
||||
{
|
||||
@ -58,6 +60,7 @@ public ImportDecisionMaker(IEnumerable<IImportDecisionEngineSpecification> speci
|
||||
_qualitiesService = qualitiesService;
|
||||
_config = config;
|
||||
_historyService = historyService;
|
||||
_parsingService = parsingService;
|
||||
_warnedFiles = cacheManager.GetCache<string>(this.GetType());
|
||||
_logger = logger;
|
||||
}
|
||||
@ -78,6 +81,7 @@ public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie mo
|
||||
if (downloadClientItem != null)
|
||||
{
|
||||
downloadClientItemInfo = Parser.Parser.ParseMovieTitle(downloadClientItem.Title, false);
|
||||
downloadClientItemInfo = _parsingService.EnhanceMovieInfo(downloadClientItemInfo);
|
||||
}
|
||||
|
||||
var nonSampleVideoFileCount = GetNonSampleVideoFileCount(newFiles, movie, downloadClientItemInfo, folderInfo);
|
||||
@ -107,6 +111,11 @@ private ImportDecision GetDecision(LocalMovie localMovie, DownloadClientItem dow
|
||||
|
||||
var fileMovieInfo = Parser.Parser.ParseMoviePath(localMovie.Path, false);
|
||||
|
||||
if (fileMovieInfo != null)
|
||||
{
|
||||
fileMovieInfo = _parsingService.EnhanceMovieInfo(fileMovieInfo);
|
||||
}
|
||||
|
||||
localMovie.FileMovieInfo = fileMovieInfo;
|
||||
localMovie.Size = _diskProvider.GetFileSize(localMovie.Path);
|
||||
|
||||
|
@ -24,30 +24,30 @@ public ParsedMovieInfo AugmentMovieInfo(ParsedMovieInfo movieInfo, object helper
|
||||
if (helper is MediaInfoModel mediaInfo)
|
||||
{
|
||||
var quality = movieInfo.Quality;
|
||||
if (!(quality.Modifier == Modifier.BRDISK || quality.Modifier == Modifier.REMUX) &&
|
||||
(quality.Source == Source.BLURAY || quality.Source == Source.TV ||
|
||||
quality.Source == Source.WEBDL) &&
|
||||
!(quality.Resolution == Resolution.R480P || quality.Resolution == Resolution.R576P))
|
||||
if (!(quality.Quality.Modifier == Modifier.BRDISK || quality.Quality.Modifier == Modifier.REMUX) &&
|
||||
(quality.Quality.Source == Source.BLURAY || quality.Quality.Source == Source.TV ||
|
||||
quality.Quality.Source == Source.WEBDL) &&
|
||||
!(quality.Quality.Resolution == (int)Resolution.R480p || quality.Quality.Resolution == (int)Resolution.R576p))
|
||||
{
|
||||
var width = mediaInfo.Width;
|
||||
var existing = quality.Resolution;
|
||||
var existing = quality.Quality.Resolution;
|
||||
|
||||
if (width > 854)
|
||||
{
|
||||
quality.Resolution = Resolution.R720P;
|
||||
quality.Quality.Resolution = (int)Resolution.R720p;
|
||||
}
|
||||
|
||||
if (width > 1280)
|
||||
{
|
||||
quality.Resolution = Resolution.R1080P;
|
||||
quality.Quality.Resolution = (int)Resolution.R1080p;
|
||||
}
|
||||
|
||||
if (width > 1920)
|
||||
{
|
||||
quality.Resolution = Resolution.R2160P;
|
||||
quality.Quality.Resolution = (int)Resolution.R2160p;
|
||||
}
|
||||
|
||||
if (existing != quality.Resolution)
|
||||
if (existing != quality.Quality.Resolution)
|
||||
{
|
||||
//_logger.Debug("Overwriting resolution info {0} with info from media info {1}", existing, quality.Resolution);
|
||||
quality.QualityDetectionSource = QualityDetectionSource.MediaInfo;
|
||||
|
@ -99,24 +99,25 @@ public static class Parser
|
||||
|
||||
private static readonly Regex ReportImdbId = new Regex(@"(?<imdbid>tt\d{7})", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex SimpleTitleRegex = new Regex(@"\s*(?:480[ip]|576[ip]|720[ip]|1080[ip]|2160[ip]|[xh][\W_]?26[45]|DD\W?5\W1|[<>?*:|]|848x480|1280x720|1920x1080|(8|10)b(it)?)",
|
||||
private static readonly RegexReplace SimpleTitleRegex = new RegexReplace(@"\s*(?:480[ip]|576[ip]|720[ip]|1080[ip]|2160[ip]|[xh][\W_]?26[45]|DD\W?5\W1|[<>?*:|]|848x480|1280x720|1920x1080|(8|10)b(it)?)",
|
||||
string.Empty,
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex SimpleReleaseTitleRegex = new Regex(@"\s*(?:[<>?*:|])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex WebsitePrefixRegex = new Regex(@"^\[\s*[a-z]+(\.[a-z]+)+\s*\][- ]*|^www\.[a-z]+\.(?:com|net)[ -]*",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex AirDateRegex = new Regex(@"^(.*?)(?<!\d)((?<airyear>\d{4})[_.-](?<airmonth>[0-1][0-9])[_.-](?<airday>[0-3][0-9])|(?<airmonth>[0-1][0-9])[_.-](?<airday>[0-3][0-9])[_.-](?<airyear>\d{4}))(?!\d)",
|
||||
private static readonly RegexReplace WebsitePrefixRegex = new RegexReplace(@"^\[\s*[a-z]+(\.[a-z]+)+\s*\][- ]*|^www\.[a-z]+\.(?:com|net)[ -]*",
|
||||
string.Empty,
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex SixDigitAirDateRegex = new Regex(@"(?<=[_.-])(?<airdate>(?<!\d)(?<airyear>[1-9]\d{1})(?<airmonth>[0-1][0-9])(?<airday>[0-3][0-9]))(?=[_.-])",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex CleanReleaseGroupRegex = new Regex(@"^(.*?[-._ ](S\d+E\d+)[-._ ])|(-(RP|1|NZBGeek|Obfuscated|sample|Pre|postbot|xpost|Rakuv[a-z0-9]*|WhiteRev|BUYMORE|AsRequested|AlternativeToRequested|GEROV|Z0iDS3N|Chamele0n))+$",
|
||||
private static readonly RegexReplace CleanReleaseGroupRegex = new RegexReplace(@"^(.*?[-._ ](S\d+E\d+)[-._ ])|(-(RP|1|NZBGeek|Obfuscated|Scrambled|sample|Pre|postbot|xpost|Rakuv[a-z0-9]*|WhiteRev|BUYMORE|AsRequested|AlternativeToRequested|GEROV|Z0iDS3N|Chamele0n|4P|4Planet))+$",
|
||||
string.Empty,
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex CleanTorrentSuffixRegex = new Regex(@"\[(?:ettv|rartv|rarbg|cttv)\]$",
|
||||
private static readonly RegexReplace CleanTorrentSuffixRegex = new RegexReplace(@"\[(?:ettv|rartv|rarbg|cttv)\]$",
|
||||
string.Empty,
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex CleanQualityBracketsRegex = new Regex(@"\[[a-z0-9 ._-]+\]$",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex ReleaseGroupRegex = new Regex(@"-(?<releasegroup>[a-z0-9]+(?!.+?(?:480p|720p|1080p|2160p)))(?<!.*?WEB-DL|480p|720p|1080p|2160p)(?:\b|[-._ ]|$)|[-._ ]\[(?<releasegroup>[a-z0-9]+)\]$",
|
||||
@ -173,8 +174,6 @@ public static ParsedMovieInfo ParseMoviePath(string path, bool isLenient)
|
||||
|
||||
public static ParsedMovieInfo ParseMovieTitle(string title, bool isLenient, bool isDir = false)
|
||||
{
|
||||
|
||||
ParsedMovieInfo realResult = null;
|
||||
try
|
||||
{
|
||||
if (!ValidateBeforeParsing(title)) return null;
|
||||
@ -191,17 +190,26 @@ public static ParsedMovieInfo ParseMovieTitle(string title, bool isLenient, bool
|
||||
Logger.Debug("Reversed name detected. Converted to '{0}'", title);
|
||||
}
|
||||
|
||||
var simpleTitle = SimpleTitleRegex.Replace(title, string.Empty);
|
||||
var releaseTitle = RemoveFileExtension(title);
|
||||
|
||||
simpleTitle = RemoveFileExtension(simpleTitle);
|
||||
releaseTitle = releaseTitle.Replace("【", "[").Replace("】", "]");
|
||||
|
||||
var simpleReleaseTitle = SimpleReleaseTitleRegex.Replace(title, string.Empty);
|
||||
simpleReleaseTitle = RemoveFileExtension(simpleReleaseTitle);
|
||||
var simpleTitle = SimpleTitleRegex.Replace(releaseTitle);
|
||||
|
||||
// TODO: Quick fix stripping [url] - prefixes.
|
||||
simpleTitle = WebsitePrefixRegex.Replace(simpleTitle, string.Empty);
|
||||
simpleTitle = WebsitePrefixRegex.Replace(simpleTitle);
|
||||
|
||||
simpleTitle = CleanTorrentSuffixRegex.Replace(simpleTitle, string.Empty);
|
||||
simpleTitle = CleanTorrentSuffixRegex.Replace(simpleTitle);
|
||||
|
||||
simpleTitle = CleanQualityBracketsRegex.Replace(simpleTitle, m =>
|
||||
{
|
||||
if (QualityParser.ParseQualityName(m.Value).Quality != Qualities.Quality.Unknown)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return m.Value;
|
||||
});
|
||||
|
||||
var allRegexes = ReportMovieTitleRegex.ToList();
|
||||
|
||||
@ -231,14 +239,44 @@ public static ParsedMovieInfo ParseMovieTitle(string title, bool isLenient, bool
|
||||
if (result != null)
|
||||
{
|
||||
//TODO: Add tests for this!
|
||||
|
||||
var simpleReleaseTitle = SimpleReleaseTitleRegex.Replace(title, string.Empty);
|
||||
|
||||
if (result.MovieTitle.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
simpleReleaseTitle = simpleReleaseTitle.Replace(result.MovieTitle, result.MovieTitle.Contains(".") ? "A.Movie" : "A Movie");
|
||||
}
|
||||
|
||||
result.Languages = LanguageParser.EnhanceLanguages(simpleReleaseTitle, LanguageParser.ParseLanguages(releaseTitle));
|
||||
Logger.Debug("Languages parsed: {0}", result.Languages);
|
||||
|
||||
result.Quality = QualityParser.ParseQuality(title);
|
||||
Logger.Debug("Quality parsed: {0}", result.Quality);
|
||||
|
||||
if (result.Edition.IsNullOrWhiteSpace())
|
||||
{
|
||||
result.Edition = ParseEdition(simpleReleaseTitle);
|
||||
}
|
||||
|
||||
result.ReleaseGroup = ParseReleaseGroup(releaseTitle);
|
||||
|
||||
var subGroup = GetSubGroup(match);
|
||||
if (!subGroup.IsNullOrWhiteSpace())
|
||||
{
|
||||
result.ReleaseGroup = subGroup;
|
||||
}
|
||||
|
||||
Logger.Debug("Release Group parsed: {0}", result.ReleaseGroup);
|
||||
|
||||
result.ReleaseHash = GetReleaseHash(match);
|
||||
if (!result.ReleaseHash.IsNullOrWhiteSpace())
|
||||
{
|
||||
Logger.Debug("Release Hash parsed: {0}", result.ReleaseHash);
|
||||
}
|
||||
|
||||
result.SimpleReleaseTitle = simpleReleaseTitle;
|
||||
|
||||
realResult = result;
|
||||
result.ImdbId = ParseImdbId(simpleReleaseTitle);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -254,58 +292,11 @@ public static ParsedMovieInfo ParseMovieTitle(string title, bool isLenient, bool
|
||||
catch (Exception e)
|
||||
{
|
||||
if (!title.ToLower().Contains("password") && !title.ToLower().Contains("yenc"))
|
||||
Logger.Error(e, "An error has occurred while trying to parse " + title);
|
||||
Logger.Error(e, "An error has occurred while trying to parse {0}", title);
|
||||
}
|
||||
|
||||
Logger.Debug("Unable to parse {0}", title);
|
||||
return realResult;
|
||||
}
|
||||
|
||||
public static ParsedMovieInfo ParseMinimalMovieTitle(string title, string foundTitle, int foundYear)
|
||||
{
|
||||
var result = new ParsedMovieInfo {MovieTitle = foundTitle};
|
||||
|
||||
var languageTitle = Regex.Replace(title.Replace(".", " "), foundTitle, "A Movie", RegexOptions.IgnoreCase);
|
||||
|
||||
result.Languages = LanguageParser.ParseLanguages(title);
|
||||
Logger.Debug("Language parsed: {0}", result.Languages.ToExtendedString());
|
||||
|
||||
result.Quality = QualityParser.ParseQuality(title);
|
||||
Logger.Debug("Quality parsed: {0}", result.Quality);
|
||||
|
||||
if (result.Edition.IsNullOrWhiteSpace())
|
||||
{
|
||||
result.Edition = ParseEdition(languageTitle);
|
||||
}
|
||||
|
||||
result.ReleaseGroup = ParseReleaseGroup(title);
|
||||
|
||||
result.ImdbId = ParseImdbId(title);
|
||||
|
||||
Logger.Debug("Release Group parsed: {0}", result.ReleaseGroup);
|
||||
|
||||
if (foundYear > 1800)
|
||||
{
|
||||
result.Year = foundYear;
|
||||
}
|
||||
else
|
||||
{
|
||||
var match = ReportYearRegex.Match(title);
|
||||
if (match.Success && match.Groups["year"].Value != null)
|
||||
{
|
||||
int year = 1290;
|
||||
if (int.TryParse(match.Groups["year"].Value, out year))
|
||||
{
|
||||
result.Year = year;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Year = year;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string ParseImdbId(string title)
|
||||
@ -421,7 +412,7 @@ public static string ParseReleaseGroup(string title)
|
||||
{
|
||||
title = title.Trim();
|
||||
title = RemoveFileExtension(title);
|
||||
title = WebsitePrefixRegex.Replace(title, "");
|
||||
title = WebsitePrefixRegex.Replace(title);
|
||||
|
||||
var animeMatch = AnimeReleaseGroupRegex.Match(title);
|
||||
|
||||
@ -430,7 +421,7 @@ public static string ParseReleaseGroup(string title)
|
||||
return animeMatch.Groups["subgroup"].Value;
|
||||
}
|
||||
|
||||
title = CleanReleaseGroupRegex.Replace(title, "");
|
||||
title = CleanReleaseGroupRegex.Replace(title);
|
||||
|
||||
var matches = ReleaseGroupRegex.Matches(title);
|
||||
|
||||
|
@ -25,9 +25,9 @@ public interface IParsingService
|
||||
Movie GetMovie(string title);
|
||||
MappingResult Map(ParsedMovieInfo parsedMovieInfo, string imdbId, SearchCriteriaBase searchCriteria = null);
|
||||
ParsedMovieInfo ParseMovieInfo(string title, List<object> helpers);
|
||||
ParsedMovieInfo EnhanceMovieInfo(ParsedMovieInfo parsedMovieInfo, List<object> helpers = null);
|
||||
ParsedMovieInfo ParseMinimalMovieInfo(string path, bool isDir = false);
|
||||
ParsedMovieInfo ParseMinimalPathMovieInfo(string path);
|
||||
List<CustomFormat> ParseCustomFormat(ParsedMovieInfo movieInfo);
|
||||
List<FormatTagMatchResult> MatchFormatTags(ParsedMovieInfo movieInfo);
|
||||
}
|
||||
|
||||
@ -65,40 +65,26 @@ public ParsingService(
|
||||
public ParsedMovieInfo ParseMovieInfo(string title, List<object> helpers)
|
||||
{
|
||||
var result = Parser.ParseMovieTitle(title, _config.ParsingLeniency > 0);
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
result = EnhanceMinimalInfo(result, helpers);
|
||||
result = EnhanceMovieInfo(result, helpers);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private ParsedMovieInfo EnhanceMinimalInfo(ParsedMovieInfo minimalInfo, List<object> helpers)
|
||||
public ParsedMovieInfo EnhanceMovieInfo(ParsedMovieInfo minimalInfo, List<object> helpers = null)
|
||||
{
|
||||
minimalInfo.Languages = LanguageParser.ParseLanguages(minimalInfo.SimpleReleaseTitle);
|
||||
_logger.Debug("Language(s) parsed: {0}", string.Join(", ", minimalInfo.Languages.ToExtendedString()));
|
||||
|
||||
minimalInfo.Quality = QualityParser.ParseQuality(minimalInfo.SimpleReleaseTitle);
|
||||
|
||||
if (minimalInfo.Edition.IsNullOrWhiteSpace())
|
||||
if (helpers != null)
|
||||
{
|
||||
minimalInfo.Edition = Parser.ParseEdition(minimalInfo.SimpleReleaseTitle);
|
||||
minimalInfo = AugmentMovieInfo(minimalInfo, helpers);
|
||||
}
|
||||
|
||||
minimalInfo.ReleaseGroup = Parser.ParseReleaseGroup(minimalInfo.SimpleReleaseTitle);
|
||||
|
||||
minimalInfo.ImdbId = Parser.ParseImdbId(minimalInfo.SimpleReleaseTitle);
|
||||
|
||||
minimalInfo = AugmentMovieInfo(minimalInfo, helpers);
|
||||
|
||||
// After the augmenters have done their job on languages we can do our static method as well.
|
||||
minimalInfo.Languages =
|
||||
LanguageParser.EnhanceLanguages(minimalInfo.SimpleReleaseTitle, minimalInfo.Languages);
|
||||
|
||||
minimalInfo.Quality.Quality = QualityFinder.FindBySourceAndResolution(minimalInfo.Quality.Source, minimalInfo.Quality.Resolution,
|
||||
minimalInfo.Quality.Modifier);
|
||||
// minimalInfo.Quality.Quality = QualityFinder.FindBySourceAndResolution(minimalInfo.Quality.Quality.Source, minimalInfo.Quality.Quality.Resolution,
|
||||
// minimalInfo.Quality.Quality.Modifier);
|
||||
|
||||
minimalInfo.Quality.CustomFormats = ParseCustomFormat(minimalInfo);
|
||||
|
||||
@ -120,7 +106,7 @@ private ParsedMovieInfo AugmentMovieInfo(ParsedMovieInfo minimalInfo, List<objec
|
||||
return minimalInfo;
|
||||
}
|
||||
|
||||
public List<CustomFormat> ParseCustomFormat(ParsedMovieInfo movieInfo)
|
||||
private List<CustomFormat> ParseCustomFormat(ParsedMovieInfo movieInfo)
|
||||
{
|
||||
var matches = MatchFormatTags(movieInfo);
|
||||
var goodMatches = matches.Where(m => m.GoodMatch);
|
||||
|
@ -17,9 +17,11 @@ public class QualityParser
|
||||
|
||||
private static readonly Regex SourceRegex = new Regex(@"\b(?:
|
||||
(?<bluray>M?BluRay|Blu-Ray|HDDVD|BD(?!$)|BDISO|BD25|BD50|BR.?DISK)|
|
||||
(?<webdl>WEB[-_. ]DL|HDRIP|WEBDL|WebRip|Web-Rip|iTunesHD|MaxdomeHD|NetflixU?HD|WebHD|WEBMux|[. ]WEB[. ](?:[xh]26[45]|DDP?5[. ]1)|\d+0p[-. ]WEB[-. ]|WEB-DLMux|\b\s\/\sWEB\s\/\s\b)|
|
||||
(?<webdl>WEB[-_. ]DL|WEBDL|AmazonHD|iTunesHD|MaxdomeHD|NetflixU?HD|WebHD|[. ]WEB[. ](?:[xh]26[45]|DDP?5[. ]1)|\d+0p[-. ]WEB[-. ]|WEB-DLMux|\b\s\/\sWEB\s\/\s\b)|
|
||||
(?<webrip>WebRip|Web-Rip|WEBMux)|
|
||||
(?<hdtv>HDTV)|
|
||||
(?<bdrip>BDRip)|(?<brrip>BRRip)|
|
||||
(?<bdrip>BDRip)|
|
||||
(?<brrip>BRRip)|
|
||||
(?<dvdr>DVD-R|DVDR)|
|
||||
(?<dvd>DVD|DVDRip|NTSC|PAL|xvidvd)|
|
||||
(?<dsr>WS[-_. ]DSR|DSR)|
|
||||
@ -35,13 +37,11 @@ public class QualityParser
|
||||
)\b",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
|
||||
|
||||
private static readonly Regex HardcodedSubsRegex = new Regex(@"\b(?<hcsub>(\w+SUBS?)\b)|(?<hc>(HC|SUBBED))\b", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
|
||||
|
||||
private static readonly Regex RemuxRegex = new Regex(@"\b(?<remux>(BD)?[-_. ]?Remux)\b",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex RawHDRegex = new Regex(@"\b(?<rawhd>RawHD|1080i[-_. ]HDTV|Raw[-_. ]HD|MPEG[-_. ]?2)\b",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex BRDISKRegex = new Regex(@"\b(COMPLETE|ISO|BDISO|BD25|BD50|BR.?DISK)\b",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex ProperRegex = new Regex(@"\b(?<proper>proper)\b",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
@ -55,7 +55,7 @@ public class QualityParser
|
||||
private static readonly Regex RealRegex = new Regex(@"\b(?<real>REAL)\b",
|
||||
RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex ResolutionRegex = new Regex(@"\b(?:(?<R480p>480(i|p)|640x480|848x480)|(?<R576p>576(i|p))|(?<R720p>720(i|p)|1280x720)|(?<R1080p>1080p|1920x1080|1440p|FHD|1080i)|(?<R2160p>2160p|4k[-_. ](?:UHD|HEVC|BD)|(?:UHD|HEVC|BD)[-_. ]4k))\b",
|
||||
private static readonly Regex ResolutionRegex = new Regex(@"\b(?:(?<R480p>480p|640x480|848x480)|(?<R576p>576p)|(?<R720p>720p|1280x720)|(?<R1080p>1080p|1920x1080|1440p|FHD|1080i)|(?<R2160p>2160p|4k[-_. ](?:UHD|HEVC|BD)|(?:UHD|HEVC|BD)[-_. ]4k))\b",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex CodecRegex = new Regex(@"\b(?:(?<x264>x264)|(?<h264>h264)|(?<xvidhd>XvidHD)|(?<xvid>X-?vid)|(?<divx>divx))\b",
|
||||
@ -67,16 +67,41 @@ public class QualityParser
|
||||
|
||||
private static readonly Regex HighDefPdtvRegex = new Regex(@"hr[-_. ]ws", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex RemuxRegex = new Regex(@"\b(?<remux>(BD)?[-_. ]?Remux)\b", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex HDShitQualityRegex = new Regex(@"(HD-TS|HDTS|HDTSRip|HD-TC|HDTC|HDCAM|HD-CAM)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex RawHDRegex = new Regex(@"\b(?<rawhd>RawHD|1080i[-_. ]HDTV|Raw[-_. ]HD|MPEG[-_. ]?2)\b",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex HardcodedSubsRegex = new Regex(@"\b(?<hcsub>(\w+SUBS?)\b)|(?<hc>(HC|SUBBED))\b",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
|
||||
|
||||
public static QualityModel ParseQuality(string name)
|
||||
{
|
||||
Logger.Debug("Trying to parse quality for {0}", name);
|
||||
|
||||
name = name.Trim();
|
||||
|
||||
var result = ParseQualityName(name);
|
||||
|
||||
// Based on extension
|
||||
if (result.Quality == Quality.Unknown && !name.ContainsInvalidPathChars())
|
||||
{
|
||||
try
|
||||
{
|
||||
result.Quality = MediaFileExtensions.GetQualityForExtension(Path.GetExtension(name));
|
||||
result.QualityDetectionSource = QualityDetectionSource.Extension;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// Swallow exception for cases where string contains illegal
|
||||
// path characters.
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static QualityModel ParseQualityName(string name)
|
||||
{
|
||||
var normalizedName = name.Replace('_', ' ').Trim().ToLower();
|
||||
var result = ParseQualityModifiers(name, normalizedName);
|
||||
var subMatch = HardcodedSubsRegex.Matches(normalizedName).OfType<Match>().LastOrDefault();
|
||||
@ -93,139 +118,210 @@ public static QualityModel ParseQuality(string name)
|
||||
}
|
||||
}
|
||||
|
||||
var test = SourceRegex.Matches(normalizedName);
|
||||
var sourceMatch = SourceRegex.Matches(normalizedName).OfType<Match>().LastOrDefault();
|
||||
var resolution = ParseResolution(normalizedName);
|
||||
var codecRegex = CodecRegex.Match(normalizedName);
|
||||
|
||||
result.Resolution = resolution;
|
||||
|
||||
if (BRDISKRegex.IsMatch(normalizedName) && sourceMatch?.Groups["bluray"].Success == true)
|
||||
{
|
||||
result.Modifier = Modifier.BRDISK;
|
||||
result.Source = Source.BLURAY;
|
||||
}
|
||||
|
||||
if (RemuxRegex.IsMatch(normalizedName) && sourceMatch?.Groups["webdl"].Success != true && sourceMatch?.Groups["hdtv"].Success != true)
|
||||
{
|
||||
result.Modifier = Modifier.REMUX;
|
||||
result.Source = Source.BLURAY;
|
||||
return result; //We found remux!
|
||||
}
|
||||
|
||||
if (RawHDRegex.IsMatch(normalizedName) && result.Modifier != Modifier.BRDISK)
|
||||
{
|
||||
result.Modifier = Modifier.RAWHD;
|
||||
result.Source = Source.TV;
|
||||
return result;
|
||||
}
|
||||
var remuxMatch = RemuxRegex.IsMatch(normalizedName);
|
||||
var brDiskMatch = BRDISKRegex.IsMatch(normalizedName);
|
||||
|
||||
if (sourceMatch != null && sourceMatch.Success)
|
||||
{
|
||||
if (sourceMatch.Groups["bluray"].Success)
|
||||
{
|
||||
result.Source = Source.BLURAY;
|
||||
|
||||
if (codecRegex.Groups["xvid"].Success || codecRegex.Groups["divx"].Success)
|
||||
if (brDiskMatch)
|
||||
{
|
||||
result.Resolution = Resolution.R480P;
|
||||
result.Source = Source.DVD;
|
||||
result.Quality = Quality.BRDISK;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (resolution == Resolution.Unknown) result.Resolution = Resolution.R720P; //Blurays are always at least 720p
|
||||
if (resolution == Resolution.Unknown && result.Modifier == Modifier.BRDISK) result.Resolution = Resolution.R1080P; // BRDISKS are 1080p
|
||||
if (codecRegex.Groups["xvid"].Success || codecRegex.Groups["divx"].Success)
|
||||
{
|
||||
result.Quality = Quality.Bluray480p;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (resolution == Resolution.R2160p)
|
||||
{
|
||||
result.Quality = remuxMatch ? Quality.Remux2160p : Quality.Bluray2160p;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (resolution == Resolution.R1080p)
|
||||
{
|
||||
result.Quality = remuxMatch ? Quality.Remux1080p : Quality.Bluray1080p;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (resolution == Resolution.R576p)
|
||||
{
|
||||
result.Quality = Quality.Bluray576p;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (resolution == Resolution.R480p)
|
||||
{
|
||||
result.Quality = Quality.Bluray480p;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Treat a remux without a source as 1080p, not 720p.
|
||||
if (remuxMatch)
|
||||
{
|
||||
result.Quality = Quality.Remux1080p;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.Quality = Quality.Bluray720p;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sourceMatch.Groups["webdl"].Success)
|
||||
{
|
||||
result.Source = Source.WEBDL;
|
||||
if (resolution == Resolution.Unknown) result.Resolution = Resolution.R480P;
|
||||
if (resolution == Resolution.Unknown && name.Contains("[WEBDL]")) result.Resolution = Resolution.R720P;
|
||||
if (resolution == Resolution.R2160p)
|
||||
{
|
||||
result.Quality = Quality.WEBDL2160p;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (resolution == Resolution.R1080p)
|
||||
{
|
||||
result.Quality = Quality.WEBDL1080p;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (resolution == Resolution.R720p)
|
||||
{
|
||||
result.Quality = Quality.WEBDL720p;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (name.Contains("[WEBDL]"))
|
||||
{
|
||||
result.Quality = Quality.WEBDL720p;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.Quality = Quality.WEBDL480p;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sourceMatch.Groups["webrip"].Success)
|
||||
{
|
||||
if (resolution == Resolution.R2160p)
|
||||
{
|
||||
result.Quality = Quality.WEBRip2160p;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (resolution == Resolution.R1080p)
|
||||
{
|
||||
result.Quality = Quality.WEBRip1080p;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (resolution == Resolution.R720p)
|
||||
{
|
||||
result.Quality = Quality.WEBRip720p;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.Quality = Quality.WEBRip480p;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sourceMatch.Groups["scr"].Success)
|
||||
{
|
||||
result.Quality = Quality.DVDSCR;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sourceMatch.Groups["cam"].Success)
|
||||
{
|
||||
result.Quality = Quality.CAM;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sourceMatch.Groups["ts"].Success)
|
||||
{
|
||||
result.Quality = Quality.TELESYNC;
|
||||
result.Quality.Resolution = (int)resolution;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sourceMatch.Groups["tc"].Success)
|
||||
{
|
||||
result.Quality = Quality.TELECINE;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sourceMatch.Groups["wp"].Success)
|
||||
{
|
||||
result.Quality = Quality.WORKPRINT;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sourceMatch.Groups["regional"].Success)
|
||||
{
|
||||
result.Quality = Quality.REGIONAL;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sourceMatch.Groups["hdtv"].Success)
|
||||
{
|
||||
result.Source = Source.TV;
|
||||
if (resolution == Resolution.Unknown) result.Resolution = Resolution.R480P; //hdtvs are always at least 480p (they might have been downscaled
|
||||
if (resolution == Resolution.Unknown && name.Contains("[HDTV]")) result.Resolution = Resolution.R720P;
|
||||
if (resolution == Resolution.R2160p)
|
||||
{
|
||||
result.Quality = Quality.HDTV2160p;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (resolution == Resolution.R1080p)
|
||||
{
|
||||
result.Quality = Quality.HDTV1080p;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (resolution == Resolution.R720p)
|
||||
{
|
||||
result.Quality = Quality.HDTV720p;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (name.Contains("[HDTV]"))
|
||||
{
|
||||
result.Quality = Quality.HDTV720p;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.Quality = Quality.SDTV;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sourceMatch.Groups["bdrip"].Success ||
|
||||
sourceMatch.Groups["brrip"].Success)
|
||||
{
|
||||
if (codecRegex.Groups["xvid"].Success || codecRegex.Groups["divx"].Success)
|
||||
switch (resolution)
|
||||
{
|
||||
// Since it's a dvd, res is 480p
|
||||
result.Resolution = Resolution.R480P;
|
||||
result.Source = Source.DVD;
|
||||
return result;
|
||||
case Resolution.R720p:
|
||||
result.Quality = Quality.Bluray720p;
|
||||
return result;
|
||||
case Resolution.R1080p:
|
||||
result.Quality = Quality.Bluray1080p;
|
||||
return result;
|
||||
case Resolution.R576p:
|
||||
result.Quality = Quality.Bluray576p;
|
||||
return result;
|
||||
default:
|
||||
result.Quality = Quality.Bluray480p;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (resolution == Resolution.Unknown) result.Resolution = Resolution.R480P; //BDRip are always 480p or more.
|
||||
|
||||
result.Source = Source.BLURAY;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sourceMatch.Groups["wp"].Success)
|
||||
{
|
||||
result.Source = Source.WORKPRINT;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sourceMatch.Groups["dvd"].Success)
|
||||
{
|
||||
result.Resolution = Resolution.R480P;
|
||||
result.Source = Source.DVD;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sourceMatch.Groups["dvdr"].Success)
|
||||
{
|
||||
result.Resolution = Resolution.R480P;
|
||||
result.Source = Source.DVD;
|
||||
//result.Modifier = Modifier.REGIONAL;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sourceMatch.Groups["scr"].Success)
|
||||
{
|
||||
result.Resolution = Resolution.R480P;
|
||||
result.Source = Source.DVD;
|
||||
result.Modifier = Modifier.SCREENER;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sourceMatch.Groups["regional"].Success)
|
||||
{
|
||||
result.Resolution = Resolution.R480P;
|
||||
result.Source = Source.DVD;
|
||||
result.Modifier = Modifier.REGIONAL;
|
||||
return result;
|
||||
}
|
||||
|
||||
// they're shit, but at least 720p
|
||||
if (HDShitQualityRegex.IsMatch(normalizedName)) result.Resolution = Resolution.R720P;
|
||||
|
||||
if (sourceMatch.Groups["cam"].Success)
|
||||
{
|
||||
result.Source = Source.CAM;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sourceMatch.Groups["ts"].Success)
|
||||
{
|
||||
result.Source = Source.TELESYNC;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sourceMatch.Groups["tc"].Success)
|
||||
{
|
||||
result.Source = Source.TELECINE;
|
||||
result.Quality = Quality.DVD;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -234,128 +330,142 @@ public static QualityModel ParseQuality(string name)
|
||||
sourceMatch.Groups["dsr"].Success ||
|
||||
sourceMatch.Groups["tvrip"].Success)
|
||||
{
|
||||
result.Source = Source.TV;
|
||||
if (HighDefPdtvRegex.IsMatch(normalizedName))
|
||||
if (resolution == Resolution.R1080p || normalizedName.Contains("1080p"))
|
||||
{
|
||||
result.Resolution = Resolution.R720P;
|
||||
result.Quality = Quality.HDTV1080p;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.Resolution = Resolution.R480P;
|
||||
if (resolution == Resolution.R720p || normalizedName.Contains("720p"))
|
||||
{
|
||||
result.Quality = Quality.HDTV720p;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (HighDefPdtvRegex.IsMatch(normalizedName))
|
||||
{
|
||||
result.Quality = Quality.HDTV720p;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.Quality = Quality.SDTV;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
//Anime Bluray matching
|
||||
|
||||
// Anime Bluray matching
|
||||
if (AnimeBlurayRegex.Match(normalizedName).Success)
|
||||
{
|
||||
if (resolution == Resolution.R480P || resolution == Resolution.R576P || normalizedName.Contains("480p"))
|
||||
if (resolution == Resolution.R480p || resolution == Resolution.R576p || normalizedName.Contains("480p"))
|
||||
{
|
||||
result.Resolution = Resolution.R480P;
|
||||
result.Source = Source.DVD;
|
||||
result.Quality = Quality.DVD;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (resolution == Resolution.R1080P || normalizedName.Contains("1080p"))
|
||||
if (resolution == Resolution.R1080p || normalizedName.Contains("1080p"))
|
||||
{
|
||||
result.Resolution = Resolution.R1080P;
|
||||
result.Source = Source.BLURAY;
|
||||
result.Quality = remuxMatch ? Quality.Remux1080p : Quality.Bluray1080p;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.Resolution = Resolution.R720P;
|
||||
result.Source = Source.BLURAY;
|
||||
if (resolution == Resolution.R2160p || normalizedName.Contains("2160p"))
|
||||
{
|
||||
result.Quality = remuxMatch ? Quality.Remux2160p : Quality.Bluray2160p;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Treat a remux without a source as 1080p, not 720p.
|
||||
if (remuxMatch)
|
||||
{
|
||||
result.Quality = Quality.Bluray1080p;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.Quality = Quality.Bluray720p;
|
||||
return result;
|
||||
}
|
||||
|
||||
var otherSourceMatch = OtherSourceMatch(normalizedName);
|
||||
|
||||
if (otherSourceMatch.Source != Source.UNKNOWN)
|
||||
if (resolution == Resolution.R2160p)
|
||||
{
|
||||
result.Source = otherSourceMatch.Source;
|
||||
result.Resolution = resolution == Resolution.Unknown ? otherSourceMatch.Resolution : resolution;
|
||||
result.Quality = remuxMatch ? Quality.Remux2160p : Quality.HDTV2160p;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (resolution == Resolution.R2160P || resolution == Resolution.R1080P || resolution == Resolution.R720P)
|
||||
if (resolution == Resolution.R1080p)
|
||||
{
|
||||
result.Source = Source.WEBDL;
|
||||
result.Quality = remuxMatch ? Quality.Remux1080p : Quality.HDTV1080p;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (resolution == Resolution.R480P)
|
||||
if (resolution == Resolution.R720p)
|
||||
{
|
||||
result.Source = Source.DVD;
|
||||
result.Quality = Quality.HDTV720p;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (resolution == Resolution.R480p)
|
||||
{
|
||||
result.Quality = Quality.SDTV;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (codecRegex.Groups["x264"].Success)
|
||||
{
|
||||
result.Source = Source.DVD;
|
||||
result.Resolution = Resolution.R480P;
|
||||
result.Quality = Quality.SDTV;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (normalizedName.Contains("848x480"))
|
||||
{
|
||||
if (normalizedName.Contains("dvd"))
|
||||
{
|
||||
result.Quality = Quality.DVD;
|
||||
}
|
||||
|
||||
result.Source = Source.DVD;
|
||||
result.Resolution = Resolution.R480P;
|
||||
return result;
|
||||
|
||||
result.Quality = Quality.SDTV;
|
||||
}
|
||||
|
||||
if (normalizedName.Contains("1280x720"))
|
||||
{
|
||||
result.Resolution = Resolution.R720P;
|
||||
result.Source = Source.WEBDL;
|
||||
if (normalizedName.Contains("bluray"))
|
||||
{
|
||||
result.Source = Source.BLURAY;
|
||||
result.Quality = Quality.Bluray720p;
|
||||
}
|
||||
return result;
|
||||
|
||||
result.Quality = Quality.HDTV720p;
|
||||
}
|
||||
|
||||
if (normalizedName.Contains("1920x1080"))
|
||||
{
|
||||
result.Resolution = Resolution.R1080P;
|
||||
result.Source = Source.WEBDL;
|
||||
if (normalizedName.Contains("bluray"))
|
||||
{
|
||||
result.Source = Source.BLURAY;
|
||||
result.Quality = Quality.Bluray1080p;
|
||||
}
|
||||
return result;
|
||||
|
||||
result.Quality = Quality.HDTV1080p;
|
||||
}
|
||||
|
||||
if (normalizedName.Contains("bluray720p"))
|
||||
{
|
||||
result.Resolution = Resolution.R720P;
|
||||
result.Source = Source.BLURAY;
|
||||
return result;
|
||||
result.Quality = Quality.Bluray720p;
|
||||
}
|
||||
|
||||
if (normalizedName.Contains("bluray1080p"))
|
||||
{
|
||||
result.Resolution = Resolution.R1080P;
|
||||
result.Source = Source.BLURAY;
|
||||
return result;
|
||||
result.Quality = Quality.Bluray1080p;
|
||||
}
|
||||
|
||||
//Based on extension
|
||||
if (result.Source == Source.UNKNOWN && !name.ContainsInvalidPathChars())
|
||||
if (normalizedName.Contains("bluray2160p"))
|
||||
{
|
||||
try
|
||||
{
|
||||
result.Source = MediaFileExtensions.GetSourceForExtension(Path.GetExtension(name));
|
||||
result.Resolution = MediaFileExtensions.GetResolutionForExtension(Path.GetExtension(name));
|
||||
result.Quality = Quality.Bluray2160p;
|
||||
}
|
||||
|
||||
result.QualityDetectionSource = QualityDetectionSource.Extension;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
//Swallow exception for cases where string contains illegal
|
||||
//path characters.
|
||||
}
|
||||
var otherSourceMatch = OtherSourceMatch(normalizedName);
|
||||
|
||||
if (otherSourceMatch != Quality.Unknown)
|
||||
{
|
||||
result.Quality = otherSourceMatch;
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -366,29 +476,29 @@ private static Resolution ParseResolution(string name)
|
||||
var match = ResolutionRegex.Match(name);
|
||||
|
||||
if (!match.Success) return Resolution.Unknown;
|
||||
if (match.Groups["R480p"].Success) return Resolution.R480P;
|
||||
if (match.Groups["R576p"].Success) return Resolution.R576P;
|
||||
if (match.Groups["R720p"].Success) return Resolution.R720P;
|
||||
if (match.Groups["R1080p"].Success) return Resolution.R1080P;
|
||||
if (match.Groups["R2160p"].Success) return Resolution.R2160P;
|
||||
if (match.Groups["R480p"].Success) return Resolution.R480p;
|
||||
if (match.Groups["R576p"].Success) return Resolution.R576p;
|
||||
if (match.Groups["R720p"].Success) return Resolution.R720p;
|
||||
if (match.Groups["R1080p"].Success) return Resolution.R1080p;
|
||||
if (match.Groups["R2160p"].Success) return Resolution.R2160p;
|
||||
|
||||
return Resolution.Unknown;
|
||||
}
|
||||
|
||||
private static QualityModel OtherSourceMatch(string name)
|
||||
private static Quality OtherSourceMatch(string name)
|
||||
{
|
||||
var match = OtherSourceRegex.Match(name);
|
||||
|
||||
if (!match.Success) return new QualityModel();
|
||||
if (match.Groups["sdtv"].Success) return new QualityModel {Source = Source.TV, Resolution = Resolution.R480P};
|
||||
if (match.Groups["hdtv"].Success) return new QualityModel {Source = Source.TV, Resolution = Resolution.R720P};
|
||||
if (!match.Success) return Quality.Unknown;
|
||||
if (match.Groups["sdtv"].Success) return Quality.SDTV;
|
||||
if (match.Groups["hdtv"].Success) return Quality.HDTV720p;
|
||||
|
||||
return new QualityModel();
|
||||
return Quality.Unknown;
|
||||
}
|
||||
|
||||
private static QualityModel ParseQualityModifiers(string name, string normalizedName)
|
||||
{
|
||||
var result = new QualityModel();
|
||||
var result = new QualityModel { Quality = Quality.Unknown };
|
||||
|
||||
if (ProperRegex.IsMatch(normalizedName))
|
||||
{
|
||||
@ -408,8 +518,8 @@ private static QualityModel ParseQualityModifiers(string name, string normalized
|
||||
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
|
||||
// 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/episode number
|
||||
var realRegexResult = RealRegex.Matches(name);
|
||||
|
||||
if (realRegexResult.Count > 0)
|
||||
@ -420,4 +530,14 @@ private static QualityModel ParseQualityModifiers(string name, string normalized
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Resolution
|
||||
{
|
||||
Unknown,
|
||||
R480p = 480,
|
||||
R576p = 576,
|
||||
R720p = 720,
|
||||
R1080p = 1080,
|
||||
R2160p = 2160
|
||||
}
|
||||
}
|
||||
|
46
src/NzbDrone.Core/Parser/RegexReplace.cs
Normal file
46
src/NzbDrone.Core/Parser/RegexReplace.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NzbDrone.Core.Parser
|
||||
{
|
||||
public class RegexReplace
|
||||
{
|
||||
private readonly Regex _regex;
|
||||
private readonly string _replacementFormat;
|
||||
private readonly MatchEvaluator _replacementFunc;
|
||||
|
||||
public RegexReplace(string pattern, string replacement, RegexOptions regexOptions)
|
||||
{
|
||||
_regex = new Regex(pattern, regexOptions);
|
||||
_replacementFormat = replacement;
|
||||
}
|
||||
|
||||
public RegexReplace(string pattern, MatchEvaluator replacement, RegexOptions regexOptions)
|
||||
{
|
||||
_regex = new Regex(pattern, regexOptions);
|
||||
_replacementFunc = replacement;
|
||||
}
|
||||
|
||||
public string Replace(string input)
|
||||
{
|
||||
if (_replacementFunc != null)
|
||||
return _regex.Replace(input, _replacementFunc);
|
||||
else
|
||||
return _regex.Replace(input, _replacementFormat);
|
||||
}
|
||||
|
||||
public bool TryReplace(ref string input)
|
||||
{
|
||||
var result = _regex.IsMatch(input);
|
||||
if (_replacementFunc != null)
|
||||
input = _regex.Replace(input, _replacementFunc);
|
||||
else
|
||||
input = _regex.Replace(input, _replacementFormat);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@ -130,9 +130,13 @@ public void Handle(ApplicationStartedEvent message)
|
||||
Quality.HDTV1080p,
|
||||
Quality.HDTV2160p,
|
||||
Quality.WEBDL480p,
|
||||
Quality.WEBRip480p,
|
||||
Quality.WEBDL720p,
|
||||
Quality.WEBRip720p,
|
||||
Quality.WEBDL1080p,
|
||||
Quality.WEBRip1080p,
|
||||
Quality.WEBDL2160p,
|
||||
Quality.WEBRip2160p,
|
||||
Quality.Bluray480p,
|
||||
Quality.Bluray576p,
|
||||
Quality.Bluray720p,
|
||||
@ -152,23 +156,27 @@ public void Handle(ApplicationStartedEvent message)
|
||||
Quality.SDTV,
|
||||
Quality.DVD,
|
||||
Quality.WEBDL480p,
|
||||
Quality.WEBRip480p,
|
||||
Quality.Bluray480p,
|
||||
Quality.Bluray576p);
|
||||
|
||||
AddDefaultProfile("HD-720p", Quality.Bluray720p,
|
||||
Quality.HDTV720p,
|
||||
Quality.WEBDL720p,
|
||||
Quality.WEBRip720p,
|
||||
Quality.Bluray720p);
|
||||
|
||||
AddDefaultProfile("HD-1080p", Quality.Bluray1080p,
|
||||
Quality.HDTV1080p,
|
||||
Quality.WEBDL1080p,
|
||||
Quality.WEBRip1080p,
|
||||
Quality.Bluray1080p,
|
||||
Quality.Remux1080p);
|
||||
|
||||
AddDefaultProfile("Ultra-HD", Quality.Remux2160p,
|
||||
Quality.HDTV2160p,
|
||||
Quality.WEBDL2160p,
|
||||
Quality.WEBRip2160p,
|
||||
Quality.Bluray2160p,
|
||||
Quality.Remux2160p);
|
||||
|
||||
@ -176,7 +184,9 @@ public void Handle(ApplicationStartedEvent message)
|
||||
Quality.HDTV720p,
|
||||
Quality.HDTV1080p,
|
||||
Quality.WEBDL720p,
|
||||
Quality.WEBRip720p,
|
||||
Quality.WEBDL1080p,
|
||||
Quality.WEBRip1080p,
|
||||
Quality.Bluray720p,
|
||||
Quality.Bluray1080p,
|
||||
Quality.Remux1080p,
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Qualities
|
||||
@ -11,14 +12,14 @@ public class Quality : IEmbeddedDocument, IEquatable<Quality>
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public Source Source { get; set; }
|
||||
public Resolution Resolution { get; set; }
|
||||
public int Resolution { get; set; }
|
||||
public Modifier Modifier { get; set; }
|
||||
|
||||
public Quality()
|
||||
{
|
||||
}
|
||||
|
||||
private Quality(int id, string name, Source source, Resolution resolution, Modifier modifier = Modifier.NONE)
|
||||
private Quality(int id, string name, Source source, int resolution, Modifier modifier = Modifier.NONE)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
@ -27,12 +28,6 @@ private Quality(int id, string name, Source source, Resolution resolution, Modif
|
||||
Modifier = modifier;
|
||||
}
|
||||
|
||||
private Quality(int id, string name, Source source, int resolution, Modifier modifier = Modifier.NONE)
|
||||
: this(id, name, source, (Resolution) resolution, modifier)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
@ -105,11 +100,16 @@ public override bool Equals(object obj)
|
||||
public static Quality Remux1080p => new Quality(30, "Remux-1080p", Source.BLURAY, 1080, Modifier.REMUX);
|
||||
public static Quality Remux2160p => new Quality(31, "Remux-2160p", Source.BLURAY, 2160, Modifier.REMUX);
|
||||
|
||||
public static Quality BRDISK => new Quality(22, "BR-DISK", Source.BLURAY, 0, Modifier.BRDISK); // new
|
||||
public static Quality BRDISK => new Quality(22, "BR-DISK", Source.BLURAY, 1080, Modifier.BRDISK); // new
|
||||
|
||||
// Others
|
||||
public static Quality RAWHD => new Quality(10, "Raw-HD", Source.TV, 1080, Modifier.RAWHD);
|
||||
|
||||
public static Quality WEBRip480p => new Quality(12, "WEBRip-480p", Source.WEBRIP, 480);
|
||||
public static Quality WEBRip720p => new Quality(14, "WEBRip-720p", Source.WEBRIP, 720);
|
||||
public static Quality WEBRip1080p => new Quality(15, "WEBRip-1080p", Source.WEBRIP, 1080);
|
||||
public static Quality WEBRip2160p => new Quality(17, "WEBRip-2160p", Source.WEBRIP, 2160);
|
||||
|
||||
static Quality()
|
||||
{
|
||||
All = new List<Quality>
|
||||
@ -131,6 +131,10 @@ static Quality()
|
||||
WEBDL720p,
|
||||
WEBDL1080p,
|
||||
WEBDL2160p,
|
||||
WEBRip480p,
|
||||
WEBRip720p,
|
||||
WEBRip1080p,
|
||||
WEBRip2160p,
|
||||
Bluray480p,
|
||||
Bluray576p,
|
||||
Bluray720p,
|
||||
@ -161,21 +165,25 @@ static Quality()
|
||||
new QualityDefinition(Quality.DVD) { Weight = 9, MinSize = 0, MaxSize = 100 },
|
||||
new QualityDefinition(Quality.DVDR) { Weight = 10, MinSize = 0, MaxSize = 100 },
|
||||
|
||||
new QualityDefinition(Quality.WEBDL480p) { Weight = 11, MinSize = 0, MaxSize = 100 },
|
||||
new QualityDefinition(Quality.WEBDL480p) { Weight = 11, MinSize = 0, MaxSize = 100, GroupName = "WEB 480p" },
|
||||
new QualityDefinition(Quality.WEBRip480p) { Weight = 11, MinSize = 0, MaxSize = 100, GroupName = "WEB 480p" },
|
||||
new QualityDefinition(Quality.Bluray480p) { Weight = 12, MinSize = 0, MaxSize = 100 },
|
||||
new QualityDefinition(Quality.Bluray576p) { Weight = 13, MinSize = 0, MaxSize = 100 },
|
||||
|
||||
new QualityDefinition(Quality.HDTV720p) { Weight = 14, MinSize = 0, MaxSize = 100 },
|
||||
new QualityDefinition(Quality.WEBDL720p) { Weight = 15, MinSize = 0, MaxSize = 100 },
|
||||
new QualityDefinition(Quality.WEBDL720p) { Weight = 15, MinSize = 0, MaxSize = 100, GroupName = "WEB 720p" },
|
||||
new QualityDefinition(Quality.WEBRip720p) { Weight = 15, MinSize = 0, MaxSize = 100, GroupName = "WEB 720p" },
|
||||
new QualityDefinition(Quality.Bluray720p) { Weight = 16, MinSize = 0, MaxSize = 100 },
|
||||
|
||||
new QualityDefinition(Quality.HDTV1080p) { Weight = 17, MinSize = 0, MaxSize = 100 },
|
||||
new QualityDefinition(Quality.WEBDL1080p) { Weight = 18, MinSize = 0, MaxSize = 100 },
|
||||
new QualityDefinition(Quality.WEBDL1080p) { Weight = 18, MinSize = 0, MaxSize = 100, GroupName = "WEB 1080p" },
|
||||
new QualityDefinition(Quality.WEBRip1080p) { Weight = 18, MinSize = 0, MaxSize = 100, GroupName = "WEB 1080p" },
|
||||
new QualityDefinition(Quality.Bluray1080p) { Weight = 19, MinSize = 0, MaxSize = null },
|
||||
new QualityDefinition(Quality.Remux1080p) { Weight = 20, MinSize = 0, MaxSize = null },
|
||||
|
||||
new QualityDefinition(Quality.HDTV2160p) { Weight = 21, MinSize = 0, MaxSize = null },
|
||||
new QualityDefinition(Quality.WEBDL2160p) { Weight = 22, MinSize = 0, MaxSize = null },
|
||||
new QualityDefinition(Quality.WEBDL2160p) { Weight = 22, MinSize = 0, MaxSize = null, GroupName = "WEB 2160p" },
|
||||
new QualityDefinition(Quality.WEBRip2160p) { Weight = 22, MinSize = 0, MaxSize = null, GroupName = "WEB 2160p" },
|
||||
new QualityDefinition(Quality.Bluray2160p) { Weight = 23, MinSize = 0, MaxSize = null },
|
||||
new QualityDefinition(Quality.Remux2160p) { Weight = 24, MinSize = 0, MaxSize = null },
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
using NLog;
|
||||
using NzbDrone.Common.Instrumentation;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.Qualities
|
||||
{
|
||||
@ -9,7 +10,7 @@ public static class QualityFinder
|
||||
{
|
||||
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(QualityFinder));
|
||||
|
||||
public static Quality FindBySourceAndResolution(Source source, Resolution resolution, Modifier modifer)
|
||||
public static Quality FindBySourceAndResolution(Source source, int resolution, Modifier modifer)
|
||||
{
|
||||
// Check for a perfect 3-way match
|
||||
var matchingQuality = Quality.All.SingleOrDefault(q => q.Source == source && q.Resolution == resolution && q.Modifier == modifer);
|
||||
@ -20,7 +21,7 @@ public static Quality FindBySourceAndResolution(Source source, Resolution resolu
|
||||
}
|
||||
|
||||
// Check for Source and Modifier Match for Qualities with Unknown Resolution
|
||||
var matchingQualitiesUnknownResolution = Quality.All.Where(q => q.Source == source && (q.Resolution == Resolution.Unknown) && q.Modifier == modifer && q != Quality.Unknown);
|
||||
var matchingQualitiesUnknownResolution = Quality.All.Where(q => q.Source == source && (q.Resolution == 0) && q.Modifier == modifer && q != Quality.Unknown);
|
||||
|
||||
if (matchingQualitiesUnknownResolution.Any())
|
||||
{
|
||||
|
@ -12,13 +12,6 @@ public class QualityModel : IEmbeddedDocument, IEquatable<QualityModel>
|
||||
|
||||
public List<CustomFormat> CustomFormats { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Resolution Resolution { get; set; }
|
||||
[JsonIgnore]
|
||||
public Source Source { get; set; }
|
||||
[JsonIgnore]
|
||||
public Modifier Modifier { get; set; }
|
||||
|
||||
|
||||
public Revision Revision { get; set; }
|
||||
|
||||
@ -33,11 +26,11 @@ public QualityModel()
|
||||
|
||||
}
|
||||
|
||||
public QualityModel(Quality quality, Revision revision = null)
|
||||
public QualityModel(Quality quality, Revision revision = null, List<CustomFormat> customFormats = null)
|
||||
{
|
||||
Quality = quality;
|
||||
Revision = revision ?? new Revision();
|
||||
CustomFormats = new List<CustomFormat>();
|
||||
CustomFormats = customFormats ?? new List<CustomFormat>();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
Loading…
Reference in New Issue
Block a user