parent
81716762d8
commit
a80360f6fd
@ -1,182 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.MediaFiles.TrackImport;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.TrackImport
|
||||
{
|
||||
[TestFixture]
|
||||
public class SampleServiceFixture : CoreTest<DetectSample>
|
||||
{
|
||||
private Series _series;
|
||||
private LocalEpisode _localEpisode;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Series>.CreateNew()
|
||||
.With(s => s.SeriesType = SeriesTypes.Standard)
|
||||
.With(s => s.Runtime = 30)
|
||||
.Build();
|
||||
|
||||
var episodes = Builder<Episode>.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(e => e.SeasonNumber = 1)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
_localEpisode = new LocalEpisode
|
||||
{
|
||||
Path = @"C:\Test\30 Rock\30.rock.s01e01.avi",
|
||||
Episodes = episodes,
|
||||
Series = _series,
|
||||
Quality = new QualityModel(Quality.MP3_256)
|
||||
};
|
||||
}
|
||||
|
||||
private void GivenFileSize(long size)
|
||||
{
|
||||
_localEpisode.Size = size;
|
||||
}
|
||||
|
||||
private void GivenRuntime(int seconds)
|
||||
{
|
||||
Mocker.GetMock<IVideoFileInfoReader>()
|
||||
.Setup(s => s.GetRunTime(It.IsAny<string>()))
|
||||
.Returns(new TimeSpan(0, 0, seconds));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_season_zero()
|
||||
{
|
||||
_localEpisode.Episodes[0].SeasonNumber = 0;
|
||||
ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_for_flv()
|
||||
{
|
||||
_localEpisode.Path = @"C:\Test\some.show.s01e01.flv";
|
||||
|
||||
ShouldBeFalse();
|
||||
|
||||
Mocker.GetMock<IVideoFileInfoReader>().Verify(c => c.GetRunTime(It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_for_strm()
|
||||
{
|
||||
_localEpisode.Path = @"C:\Test\some.show.s01e01.strm";
|
||||
|
||||
ShouldBeFalse();
|
||||
|
||||
Mocker.GetMock<IVideoFileInfoReader>().Verify(c => c.GetRunTime(It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_runtime()
|
||||
{
|
||||
GivenRuntime(120);
|
||||
GivenFileSize(1000.Megabytes());
|
||||
|
||||
Subject.IsSample(_localEpisode.Series,
|
||||
_localEpisode.Quality,
|
||||
_localEpisode.Path,
|
||||
_localEpisode.Size,
|
||||
_localEpisode.IsSpecial);
|
||||
|
||||
Mocker.GetMock<IVideoFileInfoReader>().Verify(v => v.GetRunTime(It.IsAny<string>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_runtime_is_less_than_minimum()
|
||||
{
|
||||
GivenRuntime(60);
|
||||
|
||||
ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_runtime_greater_than_minimum()
|
||||
{
|
||||
GivenRuntime(600);
|
||||
|
||||
ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_runtime_greater_than_webisode_minimum()
|
||||
{
|
||||
_series.Runtime = 6;
|
||||
GivenRuntime(299);
|
||||
|
||||
ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_fall_back_to_file_size_if_mediainfo_dll_not_found_acceptable_size()
|
||||
{
|
||||
Mocker.GetMock<IVideoFileInfoReader>()
|
||||
.Setup(s => s.GetRunTime(It.IsAny<string>()))
|
||||
.Throws<DllNotFoundException>();
|
||||
|
||||
GivenFileSize(1000.Megabytes());
|
||||
ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_fall_back_to_file_size_if_mediainfo_dll_not_found_undersize()
|
||||
{
|
||||
Mocker.GetMock<IVideoFileInfoReader>()
|
||||
.Setup(s => s.GetRunTime(It.IsAny<string>()))
|
||||
.Throws<DllNotFoundException>();
|
||||
|
||||
GivenFileSize(1.Megabytes());
|
||||
ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_treat_daily_episode_a_special()
|
||||
{
|
||||
GivenRuntime(600);
|
||||
_series.SeriesType = SeriesTypes.Daily;
|
||||
_localEpisode.Episodes[0].SeasonNumber = 0;
|
||||
ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_for_anime_special()
|
||||
{
|
||||
_series.SeriesType = SeriesTypes.Anime;
|
||||
_localEpisode.Episodes[0].SeasonNumber = 0;
|
||||
|
||||
ShouldBeFalse();
|
||||
}
|
||||
|
||||
private void ShouldBeTrue()
|
||||
{
|
||||
Subject.IsSample(_localEpisode.Series,
|
||||
_localEpisode.Quality,
|
||||
_localEpisode.Path,
|
||||
_localEpisode.Size,
|
||||
_localEpisode.IsSpecial).Should().BeTrue();
|
||||
}
|
||||
|
||||
private void ShouldBeFalse()
|
||||
{
|
||||
Subject.IsSample(_localEpisode.Series,
|
||||
_localEpisode.Quality,
|
||||
_localEpisode.Path,
|
||||
_localEpisode.Size,
|
||||
_localEpisode.IsSpecial).Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,131 +0,0 @@
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.ParserTests
|
||||
{
|
||||
|
||||
[TestFixture]
|
||||
public class AbsoluteEpisodeNumberParserFixture : CoreTest
|
||||
{
|
||||
[TestCase("[SubDESU]_High_School_DxD_07_(1280x720_x264-AAC)_[6B7FD717]", "High School DxD", 7, 0, 0)]
|
||||
[TestCase("[Chihiro]_Working!!_-_06_[848x480_H.264_AAC][859EEAFA]", "Working!!", 6, 0, 0)]
|
||||
[TestCase("[Commie]_Senki_Zesshou_Symphogear_-_11_[65F220B4]", "Senki Zesshou Symphogear", 11, 0, 0)]
|
||||
[TestCase("[Underwater]_Rinne_no_Lagrange_-_12_(720p)_[5C7BC4F9]", "Rinne no Lagrange", 12, 0, 0)]
|
||||
[TestCase("[Commie]_Rinne_no_Lagrange_-_15_[E76552EA]", "Rinne no Lagrange", 15, 0, 0)]
|
||||
[TestCase("[HorribleSubs]_Hunter_X_Hunter_-_33_[720p]", "Hunter X Hunter", 33, 0, 0)]
|
||||
[TestCase("[HorribleSubs]_Fairy_Tail_-_145_[720p]", "Fairy Tail", 145, 0, 0)]
|
||||
[TestCase("[HorribleSubs] Tonari no Kaibutsu-kun - 13 [1080p].mkv", "Tonari no Kaibutsu-kun", 13, 0, 0)]
|
||||
[TestCase("[Doremi].Yes.Pretty.Cure.5.Go.Go!.31.[1280x720].[C65D4B1F].mkv", "Yes Pretty Cure 5 Go Go!", 31, 0, 0)]
|
||||
[TestCase("[K-F] One Piece 214", "One Piece", 214, 0, 0)]
|
||||
[TestCase("[K-F] One Piece S10E14 214", "One Piece", 214, 10, 14)]
|
||||
[TestCase("[K-F] One Piece 10x14 214", "One Piece", 214, 10, 14)]
|
||||
[TestCase("[K-F] One Piece 214 10x14", "One Piece", 214, 10, 14)]
|
||||
// [TestCase("One Piece S10E14 214", "One Piece", 214, 10, 14)]
|
||||
// [TestCase("One Piece 10x14 214", "One Piece", 214, 10, 14)]
|
||||
// [TestCase("One Piece 214 10x14", "One Piece", 214, 10, 14)]
|
||||
// [TestCase("214 One Piece 10x14", "One Piece", 214, 10, 14)]
|
||||
[TestCase("Bleach - 031 - The Resolution to Kill [Lunar].avi", "Bleach", 31, 0, 0)]
|
||||
[TestCase("Bleach - 031 - The Resolution to Kill [Lunar]", "Bleach", 31, 0, 0)]
|
||||
[TestCase("[ACX]Hack Sign 01 Role Play [Kosaka] [9C57891E].mkv", "Hack Sign", 1, 0, 0)]
|
||||
[TestCase("[SFW-sage] Bakuman S3 - 12 [720p][D07C91FC]", "Bakuman S3", 12, 0, 0)]
|
||||
[TestCase("ducktales_e66_time_is_money_part_one_marking_time", "ducktales", 66, 0, 0)]
|
||||
[TestCase("[Underwater-FFF] No Game No Life - 01 (720p) [27AAA0A0].mkv", "No Game No Life", 1, 0, 0)]
|
||||
[TestCase("[FroZen] Miyuki - 23 [DVD][7F6170E6]", "Miyuki", 23, 0, 0)]
|
||||
[TestCase("[Commie] Yowamushi Pedal - 32 [0BA19D5B]", "Yowamushi Pedal", 32, 0, 0)]
|
||||
[TestCase("[Doki] Mahouka Koukou no Rettousei - 07 (1280x720 Hi10P AAC) [80AF7DDE]", "Mahouka Koukou no Rettousei", 7, 0, 0)]
|
||||
[TestCase("[HorribleSubs] Yowamushi Pedal - 32 [480p]", "Yowamushi Pedal", 32, 0, 0)]
|
||||
[TestCase("[CR] Sailor Moon - 004 [480p][48CE2D0F]", "Sailor Moon", 4, 0, 0)]
|
||||
[TestCase("[Chibiki] Puchimas!! - 42 [360p][7A4FC77B]", "Puchimas!!", 42, 0, 0)]
|
||||
[TestCase("[HorribleSubs] Yowamushi Pedal - 32 [1080p]", "Yowamushi Pedal", 32, 0, 0)]
|
||||
[TestCase("[HorribleSubs] Love Live! S2 - 07 [720p]", "Love Live! S2", 7, 0, 0)]
|
||||
[TestCase("[DeadFish] Onee-chan ga Kita - 09v2 [720p][AAC]", "Onee-chan ga Kita", 9, 0, 0)]
|
||||
[TestCase("[Underwater-FFF] No Game No Life - 01 (720p) [27AAA0A0]", "No Game No Life", 1, 0, 0)]
|
||||
[TestCase("[S-T-D] Soul Eater Not! - 06 (1280x720 10bit AAC) [59B3F2EA].mkv", "Soul Eater Not!", 6, 0, 0)]
|
||||
[TestCase("No Game No Life - 010 (720p) [27AAA0A0].mkv", "No Game No Life", 10, 0, 0)]
|
||||
[TestCase("Initial D Fifth Stage - 01 DVD - Central Anime", "Initial D Fifth Stage", 1, 0, 0)]
|
||||
[TestCase("Initial_D_Fifth_Stage_-_01(DVD)_-_(Central_Anime)[5AF6F1E4].mkv", "Initial D Fifth Stage", 1, 0, 0)]
|
||||
[TestCase("Initial_D_Fifth_Stage_-_02(DVD)_-_(Central_Anime)[0CA65F00].mkv", "Initial D Fifth Stage", 2, 0, 0)]
|
||||
[TestCase("Initial D Fifth Stage - 03 DVD - Central Anime", "Initial D Fifth Stage", 3, 0, 0)]
|
||||
[TestCase("Initial_D_Fifth_Stage_-_03(DVD)_-_(Central_Anime)[629BD592].mkv", "Initial D Fifth Stage", 3, 0, 0)]
|
||||
[TestCase("Initial D Fifth Stage - 14 DVD - Central Anime", "Initial D Fifth Stage", 14, 0, 0)]
|
||||
[TestCase("Initial_D_Fifth_Stage_-_14(DVD)_-_(Central_Anime)[0183D922].mkv", "Initial D Fifth Stage", 14, 0, 0)]
|
||||
// [TestCase("Initial D - 4th Stage Ep 01.mkv", "Initial D - 4th Stage", 1, 0, 0)]
|
||||
[TestCase("[ChihiroDesuYo].No.Game.No.Life.-.09.1280x720.10bit.AAC.[24CCE81D]", "No Game No Life", 9, 0, 0)]
|
||||
[TestCase("Fairy Tail - 001 - Fairy Tail", "Fairy Tail", 001, 0, 0)]
|
||||
[TestCase("Fairy Tail - 049 - The Day of Fated Meeting", "Fairy Tail", 049, 0, 0)]
|
||||
[TestCase("Fairy Tail - 050 - Special Request Watch Out for the Guy You Like!", "Fairy Tail", 050, 0, 0)]
|
||||
[TestCase("Fairy Tail - 099 - Natsu vs. Gildarts", "Fairy Tail", 099, 0, 0)]
|
||||
[TestCase("Fairy Tail - 100 - Mest", "Fairy Tail", 100, 0, 0)]
|
||||
// [TestCase("Fairy Tail - 101 - Mest", "Fairy Tail", 101, 0, 0)] //This gets caught up in the 'see' numbering
|
||||
[TestCase("[Exiled-Destiny] Angel Beats Ep01 (D2201EC5).mkv", "Angel Beats", 1, 0, 0)]
|
||||
[TestCase("[Commie] Nobunaga the Fool - 23 [5396CA24].mkv", "Nobunaga the Fool", 23, 0, 0)]
|
||||
[TestCase("[FFF] Seikoku no Dragonar - 01 [1FB538B5].mkv", "Seikoku no Dragonar", 1, 0, 0)]
|
||||
[TestCase("[Hatsuyuki]Fate_Zero-01[1280x720][122E6EF8]", "Fate Zero", 1, 0, 0)]
|
||||
[TestCase("[CBM]_Monster_-_11_-_511_Kinderheim_[6C70C4E4].mkv", "Monster", 11, 0, 0)]
|
||||
[TestCase("[HorribleSubs] Log Horizon 2 - 05 [720p].mkv", "Log Horizon 2", 5, 0, 0)]
|
||||
[TestCase("[Commie] Log Horizon 2 - 05 [FCE4D070].mkv", "Log Horizon 2", 5, 0, 0)]
|
||||
[TestCase("[DRONE]Series.Title.100", "Series Title", 100, 0, 0)]
|
||||
[TestCase("[RlsGrp]Series.Title.2010.S01E01.001.HDTV-720p.x264-DTS", "Series Title 2010", 1, 1, 1)]
|
||||
[TestCase("Dragon Ball Kai - 130 - Found You, Gohan! Harsh Training in the Kaioshin Realm! [Baaro][720p][5A1AD35B].mkv", "Dragon Ball Kai", 130, 0, 0)]
|
||||
[TestCase("Dragon Ball Kai - 131 - A Merged Super-Warrior Is Born, His Name Is Gotenks!! [Baaro][720p][32E03F96].mkv", "Dragon Ball Kai", 131, 0, 0)]
|
||||
[TestCase("[HorribleSubs] Magic Kaito 1412 - 01 [1080p]", "Magic Kaito 1412", 1, 0, 0)]
|
||||
[TestCase("[Jumonji-Giri]_[F-B]_Kagihime_Monogatari_Eikyuu_Alice_Rondo_Ep04_(0b0e2c10).mkv", "Kagihime Monogatari Eikyuu Alice Rondo", 4, 0, 0)]
|
||||
[TestCase("[Jumonji-Giri]_[F-B]_Kagihime_Monogatari_Eikyuu_Alice_Rondo_Ep08_(8246e542).mkv", "Kagihime Monogatari Eikyuu Alice Rondo", 8, 0, 0)]
|
||||
[TestCase("Knights of Sidonia - 01 [1080p 10b DTSHD-MA eng sub].mkv", "Knights of Sidonia", 1, 0, 0)]
|
||||
[TestCase("Series Title (2010) {01} Episode Title (1).hdtv-720p", "Series Title (2010)", 1, 0, 0)]
|
||||
[TestCase("[HorribleSubs] Shirobako - 20 [720p].mkv", "Shirobako", 20, 0, 0)]
|
||||
[TestCase("[Hatsuyuki] Dragon Ball Kai (2014) - 017 (115) [1280x720][B2CFBC0F]", "Dragon Ball Kai (2014)", 17, 0, 0)]
|
||||
[TestCase("[Hatsuyuki] Dragon Ball Kai (2014) - 018 (116) [1280x720][C4A3B16E]", "Dragon Ball Kai (2014)", 18, 0, 0)]
|
||||
[TestCase("Dragon Ball Kai (2014) - 39 (137) [v2][720p.HDTV][Unison Fansub]", "Dragon Ball Kai (2014)", 39, 0, 0)]
|
||||
[TestCase("[HorribleSubs] Eyeshield 21 - 101 [480p].mkv", "Eyeshield 21", 101, 0, 0)]
|
||||
[TestCase("[Cthuyuu].Taimadou.Gakuen.35.Shiken.Shoutai.-.03.[720p.H264.AAC][8AD82C3A]", "Taimadou Gakuen 35 Shiken Shoutai", 3, 0, 0)]
|
||||
//[TestCase("Taimadou.Gakuen.35.Shiken.Shoutai.-.03.(1280x720.HEVC.AAC)", "Taimadou Gakuen 35 Shiken Shoutai", 3, 0, 0)]
|
||||
[TestCase("[Cthuyuu] Taimadou Gakuen 35 Shiken Shoutai - 03 [720p H264 AAC][8AD82C3A]", "Taimadou Gakuen 35 Shiken Shoutai", 3, 0, 0)]
|
||||
[TestCase("Dragon Ball Super Episode 56 [VOSTFR V2][720p][AAC]-Mystic Z-Team", "Dragon Ball Super", 56, 0, 0)]
|
||||
[TestCase("[Mystic Z-Team] Dragon Ball Super Episode 69 [VOSTFR_Finale][1080p][AAC].mp4", "Dragon Ball Super", 69, 0, 0)]
|
||||
//[TestCase("", "", 0, 0, 0)]
|
||||
public void should_parse_absolute_numbers(string postTitle, string title, int absoluteEpisodeNumber, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
var result = Parser.Parser.ParseTitle(postTitle);
|
||||
result.Should().NotBeNull();
|
||||
result.AbsoluteEpisodeNumbers.Single().Should().Be(absoluteEpisodeNumber);
|
||||
result.SeasonNumber.Should().Be(seasonNumber);
|
||||
result.EpisodeNumbers.SingleOrDefault().Should().Be(episodeNumber);
|
||||
result.SeriesTitle.Should().Be(title);
|
||||
result.FullSeason.Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestCase("[DeadFish] Kenzen Robo Daimidaler - 01 - Special [BD][720p][AAC]", "Kenzen Robo Daimidaler", 1)]
|
||||
[TestCase("[DeadFish] Kenzen Robo Daimidaler - 01 - OVA [BD][720p][AAC]", "Kenzen Robo Daimidaler", 1)]
|
||||
[TestCase("[DeadFish] Kenzen Robo Daimidaler - 01 - OVD [BD][720p][AAC]", "Kenzen Robo Daimidaler", 1)]
|
||||
public void should_parse_absolute_specials(string postTitle, string title, int absoluteEpisodeNumber)
|
||||
{
|
||||
var result = Parser.Parser.ParseTitle(postTitle);
|
||||
result.Should().NotBeNull();
|
||||
result.AbsoluteEpisodeNumbers.Single().Should().Be(absoluteEpisodeNumber);
|
||||
result.SeasonNumber.Should().Be(0);
|
||||
result.EpisodeNumbers.SingleOrDefault().Should().Be(0);
|
||||
result.SeriesTitle.Should().Be(title);
|
||||
result.FullSeason.Should().BeFalse();
|
||||
result.Special.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCase("[ANBU-AonE]_Naruto_26-27_[F224EF26].avi", "Naruto", new[] { 26, 27 })]
|
||||
[TestCase("[Doutei] Recently, My Sister is Unusual - 01-12 [BD][720p-AAC]", "Recently, My Sister is Unusual", new [] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 })]
|
||||
[TestCase("Series Title (2010) - 01-02-03 - Episode Title (1) HDTV-720p", "Series Title (2010)", new [] { 1, 2, 3 })]
|
||||
[TestCase("[RlsGrp] Series Title (2010) - S01E01-02-03 - 001-002-003 - Episode Title HDTV-720p v2", "Series Title (2010)", new[] { 1, 2, 3 })]
|
||||
[TestCase("[RlsGrp] Series Title (2010) - S01E01-02 - 001-002 - Episode Title HDTV-720p v2", "Series Title (2010)", new[] { 1, 2 })]
|
||||
[TestCase("Series Title (2010) - S01E01-02 (001-002) - Episode Title (1) HDTV-720p v2 [RlsGrp]", "Series Title (2010)", new[] { 1, 2 })]
|
||||
[TestCase("[HorribleSubs] Haikyuu!! (01-25) [1080p] (Batch)", "Haikyuu!!", new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 })]
|
||||
public void should_parse_multi_episode_absolute_numbers(string postTitle, string title, int[] absoluteEpisodeNumbers)
|
||||
{
|
||||
var result = Parser.Parser.ParseTitle(postTitle);
|
||||
result.Should().NotBeNull();
|
||||
result.AbsoluteEpisodeNumbers.Should().BeEquivalentTo(absoluteEpisodeNumbers);
|
||||
result.SeriesTitle.Should().Be(title);
|
||||
result.FullSeason.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.ParserTests
|
||||
{
|
||||
|
||||
[TestFixture]
|
||||
public class AnimeMetadataParserFixture : CoreTest
|
||||
{
|
||||
[TestCase("[SubDESU]_High_School_DxD_07_(1280x720_x264-AAC)_[6B7FD717]", "SubDESU", "6B7FD717")]
|
||||
[TestCase("[Chihiro]_Working!!_-_06_[848x480_H.264_AAC][859EEAFA]", "Chihiro", "859EEAFA")]
|
||||
[TestCase("[Underwater]_Rinne_no_Lagrange_-_12_(720p)_[5C7BC4F9]", "Underwater", "5C7BC4F9")]
|
||||
[TestCase("[HorribleSubs]_Hunter_X_Hunter_-_33_[720p]", "HorribleSubs", "")]
|
||||
[TestCase("[HorribleSubs] Tonari no Kaibutsu-kun - 13 [1080p].mkv", "HorribleSubs", "")]
|
||||
[TestCase("[Doremi].Yes.Pretty.Cure.5.Go.Go!.31.[1280x720].[C65D4B1F].mkv", "Doremi", "C65D4B1F")]
|
||||
[TestCase("[Doremi].Yes.Pretty.Cure.5.Go.Go!.31.[1280x720].[C65D4B1F]", "Doremi", "C65D4B1F")]
|
||||
[TestCase("[Doremi].Yes.Pretty.Cure.5.Go.Go!.31.[1280x720].mkv", "Doremi", "")]
|
||||
[TestCase("[K-F] One Piece 214", "K-F", "")]
|
||||
[TestCase("[K-F] One Piece S10E14 214", "K-F", "")]
|
||||
[TestCase("[K-F] One Piece 10x14 214", "K-F", "")]
|
||||
[TestCase("[K-F] One Piece 214 10x14", "K-F", "")]
|
||||
[TestCase("Bleach - 031 - The Resolution to Kill [Lunar].avi", "Lunar", "")]
|
||||
[TestCase("[ACX]Hack Sign 01 Role Play [Kosaka] [9C57891E].mkv", "ACX", "9C57891E")]
|
||||
[TestCase("[S-T-D] Soul Eater Not! - 06 (1280x720 10bit AAC) [59B3F2EA].mkv", "S-T-D", "59B3F2EA")]
|
||||
public void should_parse_absolute_numbers(string postTitle, string subGroup, string hash)
|
||||
{
|
||||
var result = Parser.Parser.ParseTitle(postTitle);
|
||||
result.Should().NotBeNull();
|
||||
result.ReleaseGroup.Should().Be(subGroup);
|
||||
result.ReleaseHash.Should().Be(hash);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Expansive;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Test.ParserTests
|
||||
{
|
||||
|
||||
[TestFixture]
|
||||
public class DailyEpisodeParserFixture : CoreTest
|
||||
{
|
||||
[TestCase("Conan 2011 04 18 Emma Roberts HDTV XviD BFF", "Conan", 2011, 04, 18)]
|
||||
[TestCase("The Tonight Show With Jay Leno 2011 04 15 1080i HDTV DD5 1 MPEG2 TrollHD", "The Tonight Show With Jay Leno", 2011, 04, 15)]
|
||||
[TestCase("The.Daily.Show.2010.10.11.Johnny.Knoxville.iTouch-MW", "The Daily Show", 2010, 10, 11)]
|
||||
[TestCase("The Daily Show - 2011-04-12 - Gov. Deval Patrick", "The Daily Show", 2011, 04, 12)]
|
||||
[TestCase("2011.01.10 - Denis Leary - HD TV.mkv", "", 2011, 1, 10)]
|
||||
[TestCase("2011.03.13 - Denis Leary - HD TV.mkv", "", 2011, 3, 13)]
|
||||
[TestCase("The Tonight Show with Jay Leno - 2011-06-16 - Larry David, \"Bachelorette\" Ashley Hebert, Pitbull with Ne-Yo", "The Tonight Show with Jay Leno", 2011, 6, 16)]
|
||||
[TestCase("2020.NZ.2012.16.02.PDTV.XviD-C4TV", "2020 NZ", 2012, 2, 16)]
|
||||
[TestCase("2020.NZ.2012.13.02.PDTV.XviD-C4TV", "2020 NZ", 2012, 2, 13)]
|
||||
[TestCase("2020.NZ.2011.12.02.PDTV.XviD-C4TV", "2020 NZ", 2011, 12, 2)]
|
||||
[TestCase("Series Title - 2013-10-30 - Episode Title (1) [HDTV-720p]", "Series Title", 2013, 10, 30)]
|
||||
[TestCase("The_Voice_US_04.28.2014_hdtv.x264.Poke.mp4", "The Voice US", 2014, 4, 28)]
|
||||
[TestCase("At.Midnight.140722.720p.HDTV.x264-YesTV", "At Midnight", 2014, 07, 22)]
|
||||
[TestCase("At_Midnight_140722_720p_HDTV_x264-YesTV", "At Midnight", 2014, 07, 22)]
|
||||
//[TestCase("Corrie.07.01.15", "Corrie", 2015, 1, 7)]
|
||||
[TestCase("The Nightly Show with Larry Wilmore 2015 02 09 WEBRIP s01e13", "The Nightly Show with Larry Wilmore", 2015, 2, 9)]
|
||||
//[TestCase("", "", 0, 0, 0)]
|
||||
public void should_parse_daily_episode(string postTitle, string title, int year, int month, int day)
|
||||
{
|
||||
var result = Parser.Parser.ParseTitle(postTitle);
|
||||
var airDate = new DateTime(year, month, day);
|
||||
result.Should().NotBeNull();
|
||||
result.SeriesTitle.Should().Be(title);
|
||||
result.AirDate.Should().Be(airDate.ToString(Episode.AIR_DATE_FORMAT));
|
||||
result.EpisodeNumbers.Should().BeEmpty();
|
||||
result.AbsoluteEpisodeNumbers.Should().BeEmpty();
|
||||
result.FullSeason.Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestCase("Conan {year} {month} {day} Emma Roberts HDTV XviD BFF")]
|
||||
[TestCase("The Tonight Show With Jay Leno {year} {month} {day} 1080i HDTV DD5 1 MPEG2 TrollHD")]
|
||||
[TestCase("The.Daily.Show.{year}.{month}.{day}.Johnny.Knoxville.iTouch-MW")]
|
||||
[TestCase("The Daily Show - {year}-{month}-{day} - Gov. Deval Patrick")]
|
||||
[TestCase("{year}.{month}.{day} - Denis Leary - HD TV.mkv")]
|
||||
[TestCase("The Tonight Show with Jay Leno - {year}-{month}-{day} - Larry David, \"Bachelorette\" Ashley Hebert, Pitbull with Ne-Yo")]
|
||||
[TestCase("2020.NZ.{year}.{month}.{day}.PDTV.XviD-C4TV")]
|
||||
public void should_not_accept_ancient_daily_series(string title)
|
||||
{
|
||||
var yearTooLow = title.Expand(new { year = 1950, month = 10, day = 14 });
|
||||
Parser.Parser.ParseTitle(yearTooLow).Should().BeNull();
|
||||
}
|
||||
|
||||
[TestCase("Conan {year} {month} {day} Emma Roberts HDTV XviD BFF")]
|
||||
[TestCase("The Tonight Show With Jay Leno {year} {month} {day} 1080i HDTV DD5 1 MPEG2 TrollHD")]
|
||||
[TestCase("The.Daily.Show.{year}.{month}.{day}.Johnny.Knoxville.iTouch-MW")]
|
||||
[TestCase("The Daily Show - {year}-{month}-{day} - Gov. Deval Patrick")]
|
||||
[TestCase("{year}.{month}.{day} - Denis Leary - HD TV.mkv")]
|
||||
[TestCase("The Tonight Show with Jay Leno - {year}-{month}-{day} - Larry David, \"Bachelorette\" Ashley Hebert, Pitbull with Ne-Yo")]
|
||||
[TestCase("2020.NZ.{year}.{month}.{day}.PDTV.XviD-C4TV")]
|
||||
public void should_not_accept_future_dates(string title)
|
||||
{
|
||||
var twoDaysFromNow = DateTime.Now.AddDays(2);
|
||||
|
||||
var validDate = title.Expand(new { year = twoDaysFromNow.Year, month = twoDaysFromNow.Month.ToString("00"), day = twoDaysFromNow.Day.ToString("00") });
|
||||
|
||||
Parser.Parser.ParseTitle(validDate).Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_fail_if_episode_is_far_in_future()
|
||||
{
|
||||
var title = string.Format("{0:yyyy.MM.dd} - Denis Leary - HD TV.mkv", DateTime.Now.AddDays(2));
|
||||
|
||||
Parser.Parser.ParseTitle(title).Should().BeNull();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.ParserTests
|
||||
{
|
||||
|
||||
[TestFixture]
|
||||
public class MultiEpisodeParserFixture : CoreTest
|
||||
{
|
||||
[TestCase("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", "WEEDS", 3, new[] { 1, 2, 3, 4, 5, 6 })]
|
||||
[TestCase("Two.and.a.Half.Men.103.104.720p.HDTV.X264-DIMENSION", "Two and a Half Men", 1, new[] { 3, 4 })]
|
||||
[TestCase("Weeds.S03E01.S03E02.720p.HDTV.X264-DIMENSION", "Weeds", 3, new[] { 1, 2 })]
|
||||
[TestCase("The Borgias S01e01 e02 ShoHD On Demand 1080i DD5 1 ALANiS", "The Borgias", 1, new[] { 1, 2 })]
|
||||
[TestCase("White.Collar.2x04.2x05.720p.BluRay-FUTV", "White Collar", 2, new[] { 4, 5 })]
|
||||
[TestCase("Desperate.Housewives.S07E22E23.720p.HDTV.X264-DIMENSION", "Desperate Housewives", 7, new[] { 22, 23 })]
|
||||
[TestCase("Desparate Housewives - S07E22 - S07E23 - And Lots of Security.. [HDTV-720p].mkv", "Desparate Housewives", 7, new[] { 22, 23 })]
|
||||
[TestCase("S03E01.S03E02.720p.HDTV.X264-DIMENSION", "", 3, new[] { 1, 2 })]
|
||||
[TestCase("Desparate Housewives - S07E22 - 7x23 - And Lots of Security.. [HDTV-720p].mkv", "Desparate Housewives", 7, new[] { 22, 23 })]
|
||||
[TestCase("S07E22 - 7x23 - And Lots of Security.. [HDTV-720p].mkv", "", 7, new[] { 22, 23 })]
|
||||
[TestCase("2x04x05.720p.BluRay-FUTV", "", 2, new[] { 4, 5 })]
|
||||
[TestCase("S02E04E05.720p.BluRay-FUTV", "", 2, new[] { 4, 5 })]
|
||||
[TestCase("S02E03-04-05.720p.BluRay-FUTV", "", 2, new[] { 3, 4, 5 })]
|
||||
[TestCase("Breakout.Kings.S02E09-E10.HDTV.x264-ASAP", "Breakout Kings", 2, new[] { 9, 10 })]
|
||||
[TestCase("Breakout Kings - 2x9-2x10 - Served Cold [SDTV] ", "Breakout Kings", 2, new[] { 9, 10 })]
|
||||
[TestCase("Breakout Kings - 2x09-2x10 - Served Cold [SDTV] ", "Breakout Kings", 2, new[] { 9, 10 })]
|
||||
[TestCase("Hell on Wheels S02E09 E10 HDTV x264 EVOLVE", "Hell on Wheels", 2, new[] { 9, 10 })]
|
||||
[TestCase("Hell.on.Wheels.S02E09-E10.720p.HDTV.x264-EVOLVE", "Hell on Wheels", 2, new[] { 9, 10 })]
|
||||
[TestCase("Grey's Anatomy - 8x01_02 - Free Falling", "Grey's Anatomy", 8, new [] { 1,2 })]
|
||||
[TestCase("8x01_02 - Free Falling", "", 8, new[] { 1, 2 })]
|
||||
[TestCase("Kaamelott.S01E91-E100", "Kaamelott", 1, new[] { 91, 92, 93, 94, 95, 96, 97, 98, 99, 100 })]
|
||||
[TestCase("Neighbours.S29E161-E165.PDTV.x264-FQM", "Neighbours", 29, new[] { 161, 162, 163, 164, 165 })]
|
||||
[TestCase("Shortland.Street.S22E5363-E5366.HDTV.x264-FiHTV", "Shortland Street", 22, new[] { 5363, 5364, 5365, 5366 })]
|
||||
[TestCase("the.office.101.102.hdtv-lol", "the office", 1, new[] { 1, 2 })]
|
||||
[TestCase("extant.10708.hdtv-lol.mp4", "extant", 1, new[] { 7, 8 })]
|
||||
[TestCase("extant.10910.hdtv-lol.mp4", "extant", 1, new[] { 9, 10 })]
|
||||
[TestCase("E.010910.HDTVx264REPACKLOL.mp4", "E", 1, new[] { 9, 10 })]
|
||||
[TestCase("World Series of Poker - 2013x15 - 2013x16 - HD TV.mkv", "World Series of Poker", 2013, new[] { 15, 16 })]
|
||||
[TestCase("The Librarians US S01E01-E02 720p HDTV x264", "The Librarians US", 1, new [] { 1, 2 })]
|
||||
[TestCase("Series Title Season 01 Episode 05-06 720p", "Series Title", 1,new [] { 5, 6 })]
|
||||
//[TestCase("My Name Is Earl - S03E01-E02 - My Name Is Inmate 28301-016 [SDTV]", "My Name Is Earl", 3, new[] { 1, 2 })]
|
||||
//[TestCase("Adventure Time - 5x01 - x02 - Finn the Human (2) & Jake the Dog (3)", "Adventure Time", 5, new [] { 1, 2 })]
|
||||
[TestCase("The Young And The Restless - S42 Ep10718 - Ep10722", "The Young And The Restless", 42, new[] { 10718, 10719, 10720, 10721, 10722 })]
|
||||
[TestCase("The Young And The Restless - S42 Ep10688 - Ep10692", "The Young And The Restless", 42, new[] { 10688, 10689, 10690, 10691, 10692 })]
|
||||
[TestCase("RWBY.S01E02E03.1080p.BluRay.x264-DeBTViD", "RWBY", 1, new [] { 2, 3 })]
|
||||
[TestCase("grp-zoos01e11e12-1080p", "grp-zoo", 1, new [] { 11, 12 })]
|
||||
[TestCase("grp-zoo-s01e11e12-1080p", "grp-zoo", 1, new [] { 11, 12 })]
|
||||
[TestCase("Series Title.S6.E1.E2.Episode Name.1080p.WEB-DL", "Series Title", 6, new [] { 1, 2 })]
|
||||
[TestCase("Series Title.S6E1-E2.Episode Name.1080p.WEB-DL", "Series Title", 6, new [] { 1, 2 })]
|
||||
[TestCase("Series Title.S6E1-S6E2.Episode Name.1080p.WEB-DL", "Series Title", 6, new [] { 1, 2 })]
|
||||
[TestCase("Series Title.S6E1E2.Episode Name.1080p.WEB-DL", "Series Title", 6, new [] { 1, 2 })]
|
||||
[TestCase("Series Title.S6E1-E2-E3.Episode Name.1080p.WEB-DL", "Series Title", 6, new [] { 1, 2, 3})]
|
||||
[TestCase("Series Title.S6.E1E3.Episode Name.1080p.WEB-DL", "Series Title", 6, new [] { 1, 2, 3 })]
|
||||
[TestCase("Series Title.S6.E1-E2.Episode Name.1080p.WEB-DL", "Series Title", 6, new[] { 1, 2 })]
|
||||
[TestCase("Series Title.S6.E1-S6E2.Episode Name.1080p.WEB-DL", "Series Title", 6, new[] { 1, 2 })]
|
||||
[TestCase("Series Title.S6.E1E2.Episode Name.1080p.WEB-DL", "Series Title", 6, new[] { 1, 2 })]
|
||||
[TestCase("Series Title.S6.E1-E2-E3.Episode Name.1080p.WEB-DL", "Series Title", 6, new[] { 1, 2, 3 })]
|
||||
[TestCase("Mad.Men.S05E01-E02.720p.5.1Ch.BluRay", "Mad Men", 5, new[] { 1, 2 })]
|
||||
[TestCase("Mad.Men.S05E01-02.720p.5.1Ch.BluRay", "Mad Men", 5, new[] { 1, 2 })]
|
||||
//[TestCase("", "", , new [] { })]
|
||||
public void should_parse_multiple_episodes(string postTitle, string title, int season, int[] episodes)
|
||||
{
|
||||
var result = Parser.Parser.ParseTitle(postTitle);
|
||||
result.SeasonNumber.Should().Be(season);
|
||||
result.EpisodeNumbers.Should().BeEquivalentTo(episodes);
|
||||
result.SeriesTitle.Should().Be(title);
|
||||
result.AbsoluteEpisodeNumbers.Should().BeEmpty();
|
||||
result.FullSeason.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,212 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Profiles.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Profiles.Languages;
|
||||
|
||||
namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class EpisodesWhereCutoffUnmetFixture : DbTest<EpisodeRepository, Episode>
|
||||
{
|
||||
private Series _monitoredSeries;
|
||||
private Series _unmonitoredSeries;
|
||||
private PagingSpec<Episode> _pagingSpec;
|
||||
private List<QualitiesBelowCutoff> _qualitiesBelowCutoff;
|
||||
private List<LanguagesBelowCutoff> _languagesBelowCutoff;
|
||||
private List<Episode> _unairedEpisodes;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
var profile = new Profile
|
||||
{
|
||||
Id = 1,
|
||||
Cutoff = Quality.MP3_256,
|
||||
Items = new List<ProfileQualityItem>
|
||||
{
|
||||
new ProfileQualityItem { Allowed = true, Quality = Quality.MP3_192 },
|
||||
new ProfileQualityItem { Allowed = true, Quality = Quality.MP3_256 },
|
||||
new ProfileQualityItem { Allowed = true, Quality = Quality.FLAC }
|
||||
}
|
||||
};
|
||||
|
||||
var langProfile = new LanguageProfile
|
||||
{
|
||||
Id = 1,
|
||||
Languages = Languages.LanguageFixture.GetDefaultLanguages(),
|
||||
Cutoff = Language.Spanish
|
||||
};
|
||||
|
||||
_monitoredSeries = Builder<Series>.CreateNew()
|
||||
.With(s => s.TvRageId = RandomNumber)
|
||||
.With(s => s.Runtime = 30)
|
||||
.With(s => s.Monitored = true)
|
||||
.With(s => s.TitleSlug = "Title3")
|
||||
.With(s => s.ProfileId = profile.Id)
|
||||
.With(s => s.LanguageProfileId = langProfile.Id)
|
||||
.BuildNew();
|
||||
|
||||
_unmonitoredSeries = Builder<Series>.CreateNew()
|
||||
.With(s => s.TvdbId = RandomNumber)
|
||||
.With(s => s.Runtime = 30)
|
||||
.With(s => s.Monitored = false)
|
||||
.With(s => s.TitleSlug = "Title2")
|
||||
.With(s => s.ProfileId = profile.Id)
|
||||
.With(s => s.LanguageProfileId = langProfile.Id)
|
||||
.BuildNew();
|
||||
|
||||
_monitoredSeries.Id = Db.Insert(_monitoredSeries).Id;
|
||||
_unmonitoredSeries.Id = Db.Insert(_unmonitoredSeries).Id;
|
||||
|
||||
_pagingSpec = new PagingSpec<Episode>
|
||||
{
|
||||
Page = 1,
|
||||
PageSize = 10,
|
||||
SortKey = "AirDate",
|
||||
SortDirection = SortDirection.Ascending
|
||||
};
|
||||
|
||||
_qualitiesBelowCutoff = new List<QualitiesBelowCutoff>
|
||||
{
|
||||
new QualitiesBelowCutoff(profile.Id, new[] {Quality.MP3_192.Id})
|
||||
};
|
||||
|
||||
_languagesBelowCutoff = new List<LanguagesBelowCutoff>
|
||||
|
||||
{
|
||||
new LanguagesBelowCutoff(profile.Id, new[] { Language.English.Id })
|
||||
};
|
||||
|
||||
var qualityMetLanguageUnmet = new TrackFile { RelativePath = "a", Quality = new QualityModel { Quality = Quality.MP3_256 }, Language = Language.English };
|
||||
var qualityMetLanguageMet = new TrackFile { RelativePath = "b", Quality = new QualityModel { Quality = Quality.MP3_256 }, Language = Language.Spanish };
|
||||
var qualityMetLanguageExceed = new TrackFile { RelativePath = "c", Quality = new QualityModel { Quality = Quality.MP3_256 }, Language = Language.French };
|
||||
var qualityUnmetLanguageUnmet = new TrackFile { RelativePath = "d", Quality = new QualityModel { Quality = Quality.MP3_192 }, Language = Language.English };
|
||||
var qualityUnmetLanguageMet = new TrackFile { RelativePath = "e", Quality = new QualityModel { Quality = Quality.MP3_192 }, Language = Language.Spanish };
|
||||
var qualityUnmetLanguageExceed = new TrackFile { RelativePath = "f", Quality = new QualityModel { Quality = Quality.MP3_192 }, Language = Language.French };
|
||||
var qualityRawHDLanguageUnmet = new TrackFile { RelativePath = "g", Quality = new QualityModel { Quality = Quality.FLAC }, Language = Language.English };
|
||||
var qualityRawHDLanguageMet = new TrackFile { RelativePath = "h", Quality = new QualityModel { Quality = Quality.FLAC }, Language = Language.Spanish };
|
||||
var qualityRawHDLanguageExceed = new TrackFile { RelativePath = "i", Quality = new QualityModel { Quality = Quality.FLAC }, Language = Language.French };
|
||||
MediaFileRepository fileRepository = Mocker.Resolve<MediaFileRepository>();
|
||||
|
||||
qualityMetLanguageUnmet = fileRepository.Insert(qualityMetLanguageUnmet);
|
||||
qualityMetLanguageMet = fileRepository.Insert(qualityMetLanguageMet);
|
||||
qualityMetLanguageExceed = fileRepository.Insert(qualityMetLanguageExceed);
|
||||
qualityUnmetLanguageUnmet = fileRepository.Insert(qualityUnmetLanguageUnmet);
|
||||
qualityUnmetLanguageMet = fileRepository.Insert(qualityUnmetLanguageMet);
|
||||
qualityUnmetLanguageExceed = fileRepository.Insert(qualityUnmetLanguageExceed);
|
||||
qualityRawHDLanguageUnmet = fileRepository.Insert(qualityRawHDLanguageUnmet);
|
||||
qualityRawHDLanguageMet = fileRepository.Insert(qualityRawHDLanguageMet);
|
||||
qualityRawHDLanguageExceed = fileRepository.Insert(qualityRawHDLanguageExceed);
|
||||
|
||||
var monitoredSeriesEpisodes = Builder<Episode>.CreateListOfSize(4)
|
||||
.All()
|
||||
.With(e => e.Id = 0)
|
||||
.With(e => e.SeriesId = _monitoredSeries.Id)
|
||||
.With(e => e.AirDateUtc = DateTime.Now.AddDays(-5))
|
||||
.With(e => e.Monitored = true)
|
||||
.With(e => e.EpisodeFileId = qualityUnmetLanguageUnmet.Id)
|
||||
.TheFirst(1)
|
||||
.With(e => e.Monitored = false)
|
||||
.With(e => e.EpisodeFileId = qualityMetLanguageMet.Id)
|
||||
.TheNext(1)
|
||||
.With(e => e.EpisodeFileId = qualityRawHDLanguageExceed.Id)
|
||||
.TheLast(1)
|
||||
.With(e => e.SeasonNumber = 0)
|
||||
.Build();
|
||||
|
||||
var unmonitoredSeriesEpisodes = Builder<Episode>.CreateListOfSize(3)
|
||||
.All()
|
||||
.With(e => e.Id = 0)
|
||||
.With(e => e.SeriesId = _unmonitoredSeries.Id)
|
||||
.With(e => e.AirDateUtc = DateTime.Now.AddDays(-5))
|
||||
.With(e => e.Monitored = true)
|
||||
.With(e => e.EpisodeFileId = qualityRawHDLanguageUnmet.Id)
|
||||
.TheFirst(1)
|
||||
.With(e => e.Monitored = false)
|
||||
.With(e => e.EpisodeFileId = qualityMetLanguageMet.Id)
|
||||
.TheLast(1)
|
||||
.With(e => e.SeasonNumber = 0)
|
||||
.Build();
|
||||
|
||||
|
||||
_unairedEpisodes = Builder<Episode>.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(e => e.Id = 0)
|
||||
.With(e => e.SeriesId = _monitoredSeries.Id)
|
||||
.With(e => e.AirDateUtc = DateTime.Now.AddDays(5))
|
||||
.With(e => e.Monitored = true)
|
||||
.With(e => e.EpisodeFileId = qualityUnmetLanguageUnmet.Id)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Db.InsertMany(monitoredSeriesEpisodes);
|
||||
Db.InsertMany(unmonitoredSeriesEpisodes);
|
||||
}
|
||||
|
||||
private void GivenMonitoredFilterExpression()
|
||||
{
|
||||
_pagingSpec.FilterExpression = e => e.Monitored == true && e.Series.Monitored == true;
|
||||
}
|
||||
|
||||
private void GivenUnmonitoredFilterExpression()
|
||||
{
|
||||
_pagingSpec.FilterExpression = e => e.Monitored == false || e.Series.Monitored == false;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_include_episodes_where_cutoff_has_not_be_met()
|
||||
{
|
||||
GivenMonitoredFilterExpression();
|
||||
|
||||
var spec = Subject.EpisodesWhereCutoffUnmet(_pagingSpec, _qualitiesBelowCutoff, _languagesBelowCutoff, false);
|
||||
|
||||
spec.Records.Should().HaveCount(1);
|
||||
spec.Records.Should().OnlyContain(e => e.EpisodeFile.Value.Quality.Quality == Quality.MP3_192);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_only_contain_monitored_episodes()
|
||||
{
|
||||
GivenMonitoredFilterExpression();
|
||||
|
||||
var spec = Subject.EpisodesWhereCutoffUnmet(_pagingSpec, _qualitiesBelowCutoff, _languagesBelowCutoff, false);
|
||||
|
||||
spec.Records.Should().HaveCount(1);
|
||||
spec.Records.Should().OnlyContain(e => e.Monitored);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_only_contain_episode_with_monitored_series()
|
||||
{
|
||||
GivenMonitoredFilterExpression();
|
||||
|
||||
var spec = Subject.EpisodesWhereCutoffUnmet(_pagingSpec, _qualitiesBelowCutoff, _languagesBelowCutoff, false);
|
||||
|
||||
spec.Records.Should().HaveCount(1);
|
||||
spec.Records.Should().OnlyContain(e => e.Series.Monitored);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_contain_unaired_episodes_if_file_does_not_meet_cutoff()
|
||||
{
|
||||
Db.InsertMany(_unairedEpisodes);
|
||||
|
||||
GivenMonitoredFilterExpression();
|
||||
|
||||
var spec = Subject.EpisodesWhereCutoffUnmet(_pagingSpec, _qualitiesBelowCutoff, _languagesBelowCutoff, false);
|
||||
|
||||
spec.Records.Should().HaveCount(2);
|
||||
spec.Records.Should().OnlyContain(e => e.Series.Monitored);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Test.TvTests.EpisodeServiceTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class HandleEpisodeFileDeletedFixture : CoreTest<EpisodeService>
|
||||
{
|
||||
private EpisodeFile _episodeFile;
|
||||
private List<Episode> _episodes;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_episodeFile = Builder<EpisodeFile>
|
||||
.CreateNew()
|
||||
.Build();
|
||||
}
|
||||
|
||||
private void GivenSingleEpisodeFile()
|
||||
{
|
||||
_episodes = Builder<Episode>
|
||||
.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(e => e.Monitored = true)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Mocker.GetMock<IEpisodeRepository>()
|
||||
.Setup(s => s.GetEpisodeByFileId(_episodeFile.Id))
|
||||
.Returns(_episodes);
|
||||
}
|
||||
|
||||
private void GivenMultiEpisodeFile()
|
||||
{
|
||||
_episodes = Builder<Episode>
|
||||
.CreateListOfSize(2)
|
||||
.All()
|
||||
.With(e => e.Monitored = true)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Mocker.GetMock<IEpisodeRepository>()
|
||||
.Setup(s => s.GetEpisodeByFileId(_episodeFile.Id))
|
||||
.Returns(_episodes);
|
||||
}
|
||||
|
||||
//[Test]
|
||||
//public void should_set_EpisodeFileId_to_zero()
|
||||
//{
|
||||
// GivenSingleEpisodeFile();
|
||||
|
||||
// Subject.Handle(new EpisodeFileDeletedEvent(_episodeFile, DeleteMediaFileReason.MissingFromDisk));
|
||||
|
||||
// Mocker.GetMock<IEpisodeRepository>()
|
||||
// .Verify(v => v.Update(It.Is<Episode>(e => e.EpisodeFileId == 0)), Times.Once());
|
||||
//}
|
||||
|
||||
//[Test]
|
||||
//public void should_update_each_episode_for_file()
|
||||
//{
|
||||
// GivenMultiEpisodeFile();
|
||||
|
||||
// Subject.Handle(new EpisodeFileDeletedEvent(_episodeFile, DeleteMediaFileReason.MissingFromDisk));
|
||||
|
||||
// Mocker.GetMock<IEpisodeRepository>()
|
||||
// .Verify(v => v.Update(It.Is<Episode>(e => e.EpisodeFileId == 0)), Times.Exactly(2));
|
||||
//}
|
||||
|
||||
//[Test]
|
||||
//public void should_set_monitored_to_false_if_autoUnmonitor_is_true_and_is_not_for_an_upgrade()
|
||||
//{
|
||||
// GivenSingleEpisodeFile();
|
||||
|
||||
// Mocker.GetMock<IConfigService>()
|
||||
// .SetupGet(s => s.AutoUnmonitorPreviouslyDownloadedTracks)
|
||||
// .Returns(true);
|
||||
|
||||
// Subject.Handle(new EpisodeFileDeletedEvent(_episodeFile, DeleteMediaFileReason.MissingFromDisk));
|
||||
|
||||
// Mocker.GetMock<IEpisodeRepository>()
|
||||
// .Verify(v => v.Update(It.Is<Episode>(e => e.Monitored == false)), Times.Once());
|
||||
//}
|
||||
|
||||
//[Test]
|
||||
//public void should_leave_monitored_to_true_if_autoUnmonitor_is_false()
|
||||
//{
|
||||
// GivenSingleEpisodeFile();
|
||||
|
||||
// Mocker.GetMock<IConfigService>()
|
||||
// .SetupGet(s => s.AutoUnmonitorPreviouslyDownloadedTracks)
|
||||
// .Returns(false);
|
||||
|
||||
// Subject.Handle(new EpisodeFileDeletedEvent(_episodeFile, DeleteMediaFileReason.Upgrade));
|
||||
|
||||
// Mocker.GetMock<IEpisodeRepository>()
|
||||
// .Verify(v => v.Update(It.Is<Episode>(e => e.Monitored == true)), Times.Once());
|
||||
//}
|
||||
|
||||
//[Test]
|
||||
//public void should_leave_monitored_to_true_if_autoUnmonitor_is_true_and_is_for_an_upgrade()
|
||||
//{
|
||||
// GivenSingleEpisodeFile();
|
||||
|
||||
// Mocker.GetMock<IConfigService>()
|
||||
// .SetupGet(s => s.AutoUnmonitorPreviouslyDownloadedTracks)
|
||||
// .Returns(true);
|
||||
|
||||
// Subject.Handle(new EpisodeFileDeletedEvent(_episodeFile, DeleteMediaFileReason.Upgrade));
|
||||
|
||||
// Mocker.GetMock<IEpisodeRepository>()
|
||||
// .Verify(v => v.Update(It.Is<Episode>(e => e.Monitored == true)), Times.Once());
|
||||
//}
|
||||
}
|
||||
}
|
@ -1,397 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.MetadataSource.SkyHook;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.TvTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class RefreshEpisodeServiceFixture : CoreTest<RefreshEpisodeService>
|
||||
{
|
||||
private List<Episode> _insertedEpisodes;
|
||||
private List<Episode> _updatedEpisodes;
|
||||
private List<Episode> _deletedEpisodes;
|
||||
private Tuple<Series, List<Episode>> _gameOfThrones;
|
||||
|
||||
[TestFixtureSetUp]
|
||||
public void TestFixture()
|
||||
{
|
||||
UseRealHttp();
|
||||
|
||||
// _gameOfThrones = Mocker.Resolve<SkyHookProxy>().GetSeriesInfo(121361);//Game of thrones
|
||||
|
||||
// Remove specials.
|
||||
_gameOfThrones.Item2.RemoveAll(v => v.SeasonNumber == 0);
|
||||
}
|
||||
|
||||
private List<Episode> GetEpisodes()
|
||||
{
|
||||
return _gameOfThrones.Item2.JsonClone();
|
||||
}
|
||||
|
||||
private Series GetSeries()
|
||||
{
|
||||
var series = _gameOfThrones.Item1.JsonClone();
|
||||
series.Seasons = new List<Season>();
|
||||
|
||||
return series;
|
||||
}
|
||||
|
||||
private Series GetAnimeSeries()
|
||||
{
|
||||
var series = Builder<Series>.CreateNew().Build();
|
||||
series.SeriesType = SeriesTypes.Anime;
|
||||
series.Seasons = new List<Season>();
|
||||
|
||||
return series;
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_insertedEpisodes = new List<Episode>();
|
||||
_updatedEpisodes = new List<Episode>();
|
||||
_deletedEpisodes = new List<Episode>();
|
||||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.InsertMany(It.IsAny<List<Episode>>()))
|
||||
.Callback<List<Episode>>(e => _insertedEpisodes = e);
|
||||
|
||||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.UpdateMany(It.IsAny<List<Episode>>()))
|
||||
.Callback<List<Episode>>(e => _updatedEpisodes = e);
|
||||
|
||||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.DeleteMany(It.IsAny<List<Episode>>()))
|
||||
.Callback<List<Episode>>(e => _deletedEpisodes = e);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_create_all_when_no_existing_episodes()
|
||||
{
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(new List<Episode>());
|
||||
|
||||
Subject.RefreshEpisodeInfo(GetSeries(), GetEpisodes());
|
||||
|
||||
_insertedEpisodes.Should().HaveSameCount(GetEpisodes());
|
||||
_updatedEpisodes.Should().BeEmpty();
|
||||
_deletedEpisodes.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_update_all_when_all_existing_episodes()
|
||||
{
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(GetEpisodes());
|
||||
|
||||
Subject.RefreshEpisodeInfo(GetSeries(), GetEpisodes());
|
||||
|
||||
_insertedEpisodes.Should().BeEmpty();
|
||||
_updatedEpisodes.Should().HaveSameCount(GetEpisodes());
|
||||
_deletedEpisodes.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_delete_all_when_all_existing_episodes_are_gone_from_datasource()
|
||||
{
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(GetEpisodes());
|
||||
|
||||
Subject.RefreshEpisodeInfo(GetSeries(), new List<Episode>());
|
||||
|
||||
_insertedEpisodes.Should().BeEmpty();
|
||||
_updatedEpisodes.Should().BeEmpty();
|
||||
_deletedEpisodes.Should().HaveSameCount(GetEpisodes());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_delete_duplicated_episodes_based_on_season_episode_number()
|
||||
{
|
||||
var duplicateEpisodes = GetEpisodes().Skip(5).Take(2).ToList();
|
||||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(GetEpisodes().Union(duplicateEpisodes).ToList());
|
||||
|
||||
Subject.RefreshEpisodeInfo(GetSeries(), GetEpisodes());
|
||||
|
||||
_insertedEpisodes.Should().BeEmpty();
|
||||
_updatedEpisodes.Should().HaveSameCount(GetEpisodes());
|
||||
_deletedEpisodes.Should().HaveSameCount(duplicateEpisodes);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_change_monitored_status_for_existing_episodes()
|
||||
{
|
||||
var series = GetSeries();
|
||||
series.Seasons = new List<Season>();
|
||||
series.Seasons.Add(new Season { SeasonNumber = 1, Monitored = false });
|
||||
|
||||
var episodes = GetEpisodes();
|
||||
|
||||
episodes.ForEach(e => e.Monitored = true);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(episodes);
|
||||
|
||||
Subject.RefreshEpisodeInfo(series, GetEpisodes());
|
||||
|
||||
_updatedEpisodes.Should().HaveSameCount(GetEpisodes());
|
||||
_updatedEpisodes.Should().OnlyContain(e => e.Monitored == true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_remove_duplicate_remote_episodes_before_processing()
|
||||
{
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(new List<Episode>());
|
||||
|
||||
var episodes = Builder<Episode>.CreateListOfSize(5)
|
||||
.TheFirst(2)
|
||||
.With(e => e.SeasonNumber = 1)
|
||||
.With(e => e.EpisodeNumber = 1)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.RefreshEpisodeInfo(GetSeries(), episodes);
|
||||
|
||||
_insertedEpisodes.Should().HaveCount(episodes.Count - 1);
|
||||
_updatedEpisodes.Should().BeEmpty();
|
||||
_deletedEpisodes.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_set_absolute_episode_number_for_anime()
|
||||
{
|
||||
var episodes = Builder<Episode>.CreateListOfSize(3).Build().ToList();
|
||||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(new List<Episode>());
|
||||
|
||||
Subject.RefreshEpisodeInfo(GetAnimeSeries(), episodes);
|
||||
|
||||
_insertedEpisodes.All(e => e.AbsoluteEpisodeNumber.HasValue).Should().BeTrue();
|
||||
_updatedEpisodes.Should().BeEmpty();
|
||||
_deletedEpisodes.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_set_absolute_episode_number_even_if_not_previously_set_for_anime()
|
||||
{
|
||||
var episodes = Builder<Episode>.CreateListOfSize(3).Build().ToList();
|
||||
|
||||
var existingEpisodes = episodes.JsonClone();
|
||||
existingEpisodes.ForEach(e => e.AbsoluteEpisodeNumber = null);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(existingEpisodes);
|
||||
|
||||
Subject.RefreshEpisodeInfo(GetAnimeSeries(), episodes);
|
||||
|
||||
_insertedEpisodes.Should().BeEmpty();
|
||||
_updatedEpisodes.All(e => e.AbsoluteEpisodeNumber.HasValue).Should().BeTrue();
|
||||
_deletedEpisodes.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_get_new_season_and_episode_numbers_when_absolute_episode_number_match_found()
|
||||
{
|
||||
const int expectedSeasonNumber = 10;
|
||||
const int expectedEpisodeNumber = 5;
|
||||
const int expectedAbsoluteNumber = 3;
|
||||
|
||||
var episode = Builder<Episode>.CreateNew()
|
||||
.With(e => e.SeasonNumber = expectedSeasonNumber)
|
||||
.With(e => e.EpisodeNumber = expectedEpisodeNumber)
|
||||
.With(e => e.AbsoluteEpisodeNumber = expectedAbsoluteNumber)
|
||||
.Build();
|
||||
|
||||
var existingEpisode = episode.JsonClone();
|
||||
existingEpisode.SeasonNumber = 1;
|
||||
existingEpisode.EpisodeNumber = 1;
|
||||
existingEpisode.AbsoluteEpisodeNumber = expectedAbsoluteNumber;
|
||||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(new List<Episode>{ existingEpisode });
|
||||
|
||||
Subject.RefreshEpisodeInfo(GetAnimeSeries(), new List<Episode> { episode });
|
||||
|
||||
_insertedEpisodes.Should().BeEmpty();
|
||||
_deletedEpisodes.Should().BeEmpty();
|
||||
|
||||
_updatedEpisodes.First().SeasonNumber.Should().Be(expectedSeasonNumber);
|
||||
_updatedEpisodes.First().EpisodeNumber.Should().Be(expectedEpisodeNumber);
|
||||
_updatedEpisodes.First().AbsoluteEpisodeNumber.Should().Be(expectedAbsoluteNumber);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_prefer_absolute_match_over_season_and_epsiode_match()
|
||||
{
|
||||
var episodes = Builder<Episode>.CreateListOfSize(2)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
episodes[0].AbsoluteEpisodeNumber = null;
|
||||
episodes[0].SeasonNumber.Should().NotBe(episodes[1].SeasonNumber);
|
||||
episodes[0].EpisodeNumber.Should().NotBe(episodes[1].EpisodeNumber);
|
||||
|
||||
var existingEpisode = new Episode
|
||||
{
|
||||
SeasonNumber = episodes[0].SeasonNumber,
|
||||
EpisodeNumber = episodes[0].EpisodeNumber,
|
||||
AbsoluteEpisodeNumber = episodes[1].AbsoluteEpisodeNumber
|
||||
};
|
||||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(new List<Episode> { existingEpisode });
|
||||
|
||||
Subject.RefreshEpisodeInfo(GetAnimeSeries(), episodes);
|
||||
|
||||
_updatedEpisodes.First().SeasonNumber.Should().Be(episodes[1].SeasonNumber);
|
||||
_updatedEpisodes.First().EpisodeNumber.Should().Be(episodes[1].EpisodeNumber);
|
||||
_updatedEpisodes.First().AbsoluteEpisodeNumber.Should().Be(episodes[1].AbsoluteEpisodeNumber);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_ignore_episodes_with_no_absolute_episode_in_distinct_by_absolute()
|
||||
{
|
||||
var episodes = Builder<Episode>.CreateListOfSize(10)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
episodes[0].AbsoluteEpisodeNumber = null;
|
||||
episodes[1].AbsoluteEpisodeNumber = null;
|
||||
episodes[2].AbsoluteEpisodeNumber = null;
|
||||
episodes[3].AbsoluteEpisodeNumber = null;
|
||||
episodes[4].AbsoluteEpisodeNumber = null;
|
||||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(new List<Episode>());
|
||||
|
||||
Subject.RefreshEpisodeInfo(GetAnimeSeries(), episodes);
|
||||
|
||||
_insertedEpisodes.Should().HaveCount(episodes.Count);
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_override_empty_airdate_for_direct_to_dvd()
|
||||
{
|
||||
var series = GetSeries();
|
||||
series.Status = SeriesStatusType.Ended;
|
||||
|
||||
var episodes = Builder<Episode>.CreateListOfSize(10)
|
||||
.All()
|
||||
.With(v => v.AirDateUtc = null)
|
||||
.BuildListOfNew();
|
||||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(new List<Episode>());
|
||||
|
||||
List<Episode> updateEpisodes = null;
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.InsertMany(It.IsAny<List<Episode>>()))
|
||||
.Callback<List<Episode>>(c => updateEpisodes = c);
|
||||
|
||||
Subject.RefreshEpisodeInfo(series, episodes);
|
||||
|
||||
updateEpisodes.Should().NotBeNull();
|
||||
updateEpisodes.Should().NotBeEmpty();
|
||||
updateEpisodes.All(v => v.AirDateUtc.HasValue).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_tba_for_episode_title_when_null()
|
||||
{
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(new List<Episode>());
|
||||
|
||||
var episodes = Builder<Episode>.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(e => e.Title = null)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.RefreshEpisodeInfo(GetSeries(), episodes);
|
||||
|
||||
_insertedEpisodes.First().Title.Should().Be("TBA");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_update_air_date_when_multiple_episodes_air_on_the_same_day()
|
||||
{
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(new List<Episode>());
|
||||
|
||||
var series = GetSeries();
|
||||
|
||||
var episodes = Builder<Episode>.CreateListOfSize(2)
|
||||
.All()
|
||||
.With(e => e.SeasonNumber = 1)
|
||||
.With(e => e.AirDate = DateTime.Now.ToShortDateString())
|
||||
.With(e => e.AirDateUtc = DateTime.UtcNow)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.RefreshEpisodeInfo(series, episodes);
|
||||
|
||||
_insertedEpisodes.First().AirDateUtc.Value.ToString("s").Should().Be(episodes.First().AirDateUtc.Value.ToString("s"));
|
||||
_insertedEpisodes.Last().AirDateUtc.Value.ToString("s").Should().Be(episodes.First().AirDateUtc.Value.AddMinutes(series.Runtime).ToString("s"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_update_air_date_when_multiple_episodes_air_on_the_same_day_for_netflix()
|
||||
{
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(new List<Episode>());
|
||||
|
||||
var series = GetSeries();
|
||||
series.Network = "Netflix";
|
||||
|
||||
var episodes = Builder<Episode>.CreateListOfSize(2)
|
||||
.All()
|
||||
.With(e => e.SeasonNumber = 1)
|
||||
.With(e => e.AirDate = DateTime.Now.ToShortDateString())
|
||||
.With(e => e.AirDateUtc = DateTime.UtcNow)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.RefreshEpisodeInfo(series, episodes);
|
||||
|
||||
_insertedEpisodes.Should().OnlyContain(e => e.AirDateUtc.Value.ToString("s") == episodes.First().AirDateUtc.Value.ToString("s"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_prefer_regular_season_when_absolute_numbers_conflict()
|
||||
{
|
||||
var episodes = Builder<Episode>.CreateListOfSize(2)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
episodes[0].AbsoluteEpisodeNumber = episodes[1].AbsoluteEpisodeNumber;
|
||||
episodes[0].SeasonNumber = 0;
|
||||
episodes[0].EpisodeNumber.Should().NotBe(episodes[1].EpisodeNumber);
|
||||
|
||||
var existingEpisode = new Episode
|
||||
{
|
||||
SeasonNumber = episodes[0].SeasonNumber,
|
||||
EpisodeNumber = episodes[0].EpisodeNumber,
|
||||
AbsoluteEpisodeNumber = episodes[1].AbsoluteEpisodeNumber
|
||||
};
|
||||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
|
||||
.Returns(new List<Episode> { existingEpisode });
|
||||
|
||||
Subject.RefreshEpisodeInfo(GetAnimeSeries(), episodes);
|
||||
|
||||
_updatedEpisodes.First().SeasonNumber.Should().Be(episodes[1].SeasonNumber);
|
||||
_updatedEpisodes.First().EpisodeNumber.Should().Be(episodes[1].EpisodeNumber);
|
||||
_updatedEpisodes.First().AbsoluteEpisodeNumber.Should().Be(episodes[1].AbsoluteEpisodeNumber);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Profiles.Qualities;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Profiles.Languages;
|
||||
using NzbDrone.Core.Languages;
|
||||
using System.Linq;
|
||||
|
||||
namespace NzbDrone.Core.Test.TvTests.SeriesRepositoryTests
|
||||
{
|
||||
[TestFixture]
|
||||
|
||||
public class SeriesRepositoryFixture : DbTest<SeriesRepository, Series>
|
||||
{
|
||||
[Test]
|
||||
public void should_lazyload_quality_profile()
|
||||
{
|
||||
var profile = new Profile
|
||||
{
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(Quality.MP3_320, Quality.MP3_256, Quality.MP3_192),
|
||||
|
||||
Cutoff = Quality.MP3_320,
|
||||
Name = "TestProfile"
|
||||
};
|
||||
|
||||
var langProfile = new LanguageProfile
|
||||
{
|
||||
Name = "TestProfile",
|
||||
Languages = Languages.LanguageFixture.GetDefaultLanguages(Language.English),
|
||||
Cutoff = Language.English
|
||||
};
|
||||
|
||||
|
||||
|
||||
Mocker.Resolve<ProfileRepository>().Insert(profile);
|
||||
Mocker.Resolve<LanguageProfileRepository>().Insert(langProfile);
|
||||
|
||||
var series = Builder<Series>.CreateNew().BuildNew();
|
||||
series.ProfileId = profile.Id;
|
||||
series.LanguageProfileId = langProfile.Id;
|
||||
|
||||
Subject.Insert(series);
|
||||
|
||||
|
||||
StoredModel.Profile.Should().NotBeNull();
|
||||
StoredModel.LanguageProfile.Should().NotBeNull();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.DecisionEngine
|
||||
{
|
||||
public class SameEpisodesSpecification
|
||||
{
|
||||
private readonly IEpisodeService _episodeService;
|
||||
|
||||
public SameEpisodesSpecification(IEpisodeService episodeService)
|
||||
{
|
||||
_episodeService = episodeService;
|
||||
}
|
||||
|
||||
public bool IsSatisfiedBy(List<Episode> episodes)
|
||||
{
|
||||
var episodeIds = episodes.SelectList(e => e.Id);
|
||||
var episodeFileIds = episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFileId).Distinct();
|
||||
|
||||
foreach (var episodeFileId in episodeFileIds)
|
||||
{
|
||||
var episodesInFile = _episodeService.GetEpisodesByFileId(episodeFileId);
|
||||
|
||||
if (episodesInFile.Select(e => e.Id).Except(episodeIds).Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Marr.Data;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Core.Languages;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles
|
||||
{
|
||||
public class EpisodeFile : ModelBase
|
||||
{
|
||||
public int SeriesId { get; set; }
|
||||
public int SeasonNumber { get; set; }
|
||||
public string RelativePath { get; set; }
|
||||
public string Path { get; set; }
|
||||
public long Size { get; set; }
|
||||
public DateTime DateAdded { get; set; }
|
||||
public string SceneName { get; set; }
|
||||
public string ReleaseGroup { get; set; }
|
||||
public QualityModel Quality { get; set; }
|
||||
public MediaInfoModel MediaInfo { get; set; }
|
||||
public LazyLoaded<List<Episode>> Episodes { get; set; }
|
||||
public LazyLoaded<Series> Series { get; set; }
|
||||
public Language Language { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[{0}] {1}", Id, RelativePath);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using NLog;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.TrackImport
|
||||
{
|
||||
public interface IDetectSample
|
||||
{
|
||||
bool IsSample(Series series, QualityModel quality, string path, long size, bool isSpecial);
|
||||
}
|
||||
|
||||
public class DetectSample : IDetectSample
|
||||
{
|
||||
private readonly IVideoFileInfoReader _videoFileInfoReader;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private static List<Quality> _largeSampleSizeQualities = new List<Quality> { Quality.FLAC };
|
||||
|
||||
public DetectSample(IVideoFileInfoReader videoFileInfoReader, Logger logger)
|
||||
{
|
||||
_videoFileInfoReader = videoFileInfoReader;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public static long SampleSizeLimit => 70.Megabytes();
|
||||
|
||||
public bool IsSample(Series series, QualityModel quality, string path, long size, bool isSpecial)
|
||||
{
|
||||
if (isSpecial)
|
||||
{
|
||||
_logger.Debug("Special, skipping sample check");
|
||||
return false;
|
||||
}
|
||||
|
||||
var extension = Path.GetExtension(path);
|
||||
|
||||
if (extension != null && extension.Equals(".flv", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
_logger.Debug("Skipping sample check for .flv file");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (extension != null && extension.Equals(".strm", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
_logger.Debug("Skipping sample check for .strm file");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var runTime = _videoFileInfoReader.GetRunTime(path);
|
||||
var minimumRuntime = GetMinimumAllowedRuntime(series);
|
||||
|
||||
if (runTime.TotalMinutes.Equals(0))
|
||||
{
|
||||
_logger.Error("[{0}] has a runtime of 0, is it a valid video file?", path);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (runTime.TotalSeconds < minimumRuntime)
|
||||
{
|
||||
_logger.Debug("[{0}] appears to be a sample. Runtime: {1} seconds. Expected at least: {2} seconds", path, runTime, minimumRuntime);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
catch (DllNotFoundException)
|
||||
{
|
||||
_logger.Debug("Falling back to file size detection");
|
||||
|
||||
return CheckSize(size, quality);
|
||||
}
|
||||
|
||||
_logger.Debug("Runtime is over 90 seconds");
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CheckSize(long size, QualityModel quality)
|
||||
{
|
||||
{
|
||||
if (size < SampleSizeLimit * 2)
|
||||
{
|
||||
_logger.Debug("1080p file is less than sample limit");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (size < SampleSizeLimit)
|
||||
{
|
||||
_logger.Debug("File is less than sample limit");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private int GetMinimumAllowedRuntime(Series series)
|
||||
{
|
||||
//Webisodes - 90 seconds
|
||||
if (series.Runtime <= 10)
|
||||
{
|
||||
return 90;
|
||||
}
|
||||
|
||||
//30 minute episodes - 5 minutes
|
||||
if (series.Runtime <= 30)
|
||||
{
|
||||
return 300;
|
||||
}
|
||||
|
||||
//60 minute episodes - 10 minutes
|
||||
return 600;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Extras;
|
||||
using NzbDrone.Core.Languages;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.TrackImport
|
||||
{
|
||||
[Obsolete("Used by Sonarr, not by Lidarr")]
|
||||
public interface IImportApprovedEpisodes
|
||||
{
|
||||
List<ImportResult> Import(List<ImportDecision> decisions, bool newDownload, DownloadClientItem downloadClientItem = null, ImportMode importMode = ImportMode.Auto);
|
||||
}
|
||||
|
||||
public class ImportApprovedEpisodes : IImportApprovedEpisodes
|
||||
{
|
||||
private readonly IUpgradeMediaFiles _episodeFileUpgrader;
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly IExtraService _extraService;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ImportApprovedEpisodes(IUpgradeMediaFiles episodeFileUpgrader,
|
||||
IMediaFileService mediaFileService,
|
||||
IExtraService extraService,
|
||||
IDiskProvider diskProvider,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
{
|
||||
_episodeFileUpgrader = episodeFileUpgrader;
|
||||
_mediaFileService = mediaFileService;
|
||||
_extraService = extraService;
|
||||
_diskProvider = diskProvider;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<ImportResult> Import(List<ImportDecision> decisions, bool newDownload, DownloadClientItem downloadClientItem = null, ImportMode importMode = ImportMode.Auto)
|
||||
{
|
||||
throw new NotImplementedException("This will be removed");
|
||||
}
|
||||
|
||||
private string GetSceneName(DownloadClientItem downloadClientItem, LocalEpisode localEpisode)
|
||||
{
|
||||
if (downloadClientItem != null)
|
||||
{
|
||||
var title = Parser.Parser.RemoveFileExtension(downloadClientItem.Title);
|
||||
|
||||
var parsedTitle = Parser.Parser.ParseTitle(title);
|
||||
|
||||
if (parsedTitle != null && !parsedTitle.FullSeason)
|
||||
{
|
||||
return title;
|
||||
}
|
||||
}
|
||||
|
||||
var fileName = Path.GetFileNameWithoutExtension(localEpisode.Path.CleanFilePath());
|
||||
|
||||
if (SceneChecker.IsSceneTitle(fileName))
|
||||
{
|
||||
return fileName;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Core.Languages;
|
||||
|
||||
namespace NzbDrone.Core.Parser.Model
|
||||
{
|
||||
public class LocalEpisode
|
||||
{
|
||||
public LocalEpisode()
|
||||
{
|
||||
Episodes = new List<Episode>();
|
||||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
public long Size { get; set; }
|
||||
public ParsedEpisodeInfo ParsedEpisodeInfo { get; set; }
|
||||
public Series Series { get; set; }
|
||||
public List<Episode> Episodes { get; set; }
|
||||
public QualityModel Quality { get; set; }
|
||||
public Language Language { get; set; }
|
||||
public MediaInfoModel MediaInfo { get; set; }
|
||||
public bool ExistingFile { get; set; }
|
||||
|
||||
public int SeasonNumber
|
||||
{
|
||||
get
|
||||
{
|
||||
return Episodes.Select(c => c.SeasonNumber).Distinct().Single();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSpecial => SeasonNumber == 0;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Path;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Languages;
|
||||
|
||||
namespace NzbDrone.Core.Parser.Model
|
||||
{
|
||||
// TODO: This model needs to module music, not TV series
|
||||
public class ParsedEpisodeInfo
|
||||
{
|
||||
public string SeriesTitle { get; set; }
|
||||
public SeriesTitleInfo SeriesTitleInfo { get; set; }
|
||||
public QualityModel Quality { get; set; }
|
||||
public int SeasonNumber { get; set; }
|
||||
public int[] EpisodeNumbers { get; set; }
|
||||
public int[] AbsoluteEpisodeNumbers { get; set; }
|
||||
public string AirDate { get; set; }
|
||||
public Language Language { get; set; }
|
||||
public bool FullSeason { get; set; }
|
||||
public bool Special { get; set; }
|
||||
public string ReleaseGroup { get; set; }
|
||||
public string ReleaseHash { get; set; }
|
||||
|
||||
public ParsedEpisodeInfo()
|
||||
{
|
||||
EpisodeNumbers = new int[0];
|
||||
AbsoluteEpisodeNumbers = new int[0];
|
||||
}
|
||||
|
||||
public bool IsDaily
|
||||
{
|
||||
get
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(AirDate);
|
||||
}
|
||||
|
||||
//This prevents manually downloading a release from blowing up in mono
|
||||
//TODO: Is there a better way?
|
||||
private set { }
|
||||
}
|
||||
|
||||
public bool IsAbsoluteNumbering
|
||||
{
|
||||
get
|
||||
{
|
||||
return AbsoluteEpisodeNumbers.Any();
|
||||
}
|
||||
|
||||
//This prevents manually downloading a release from blowing up in mono
|
||||
//TODO: Is there a better way?
|
||||
private set { }
|
||||
}
|
||||
|
||||
public bool IsPossibleSpecialEpisode
|
||||
{
|
||||
get
|
||||
{
|
||||
// if we don't have eny episode numbers we are likely a special episode and need to do a search by episode title
|
||||
return (AirDate.IsNullOrWhiteSpace() &&
|
||||
SeriesTitle.IsNullOrWhiteSpace() &&
|
||||
(EpisodeNumbers.Length == 0 || SeasonNumber == 0) ||
|
||||
!SeriesTitle.IsNullOrWhiteSpace() && Special);
|
||||
}
|
||||
|
||||
//This prevents manually downloading a release from blowing up in mono
|
||||
//TODO: Is there a better way?
|
||||
private set {}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string episodeString = "[Unknown Episode]";
|
||||
|
||||
if (IsDaily && EpisodeNumbers.Empty())
|
||||
{
|
||||
episodeString = string.Format("{0}", AirDate);
|
||||
}
|
||||
else if (FullSeason)
|
||||
{
|
||||
episodeString = string.Format("Season {0:00}", SeasonNumber);
|
||||
}
|
||||
else if (EpisodeNumbers != null && EpisodeNumbers.Any())
|
||||
{
|
||||
episodeString = string.Format("S{0:00}E{1}", SeasonNumber, string.Join("-", EpisodeNumbers.Select(c => c.ToString("00"))));
|
||||
}
|
||||
else if (AbsoluteEpisodeNumbers != null && AbsoluteEpisodeNumbers.Any())
|
||||
{
|
||||
episodeString = string.Format("{0}", string.Join("-", AbsoluteEpisodeNumbers.Select(c => c.ToString("000"))));
|
||||
}
|
||||
|
||||
return string.Format("{0} - {1} {2}", SeriesTitle, episodeString, Quality);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Parser.Model
|
||||
{
|
||||
public class RemoteEpisode
|
||||
{
|
||||
public ReleaseInfo Release { get; set; }
|
||||
public ParsedEpisodeInfo ParsedEpisodeInfo { get; set; }
|
||||
public Series Series { get; set; }
|
||||
public List<Episode> Episodes { get; set; }
|
||||
public bool DownloadAllowed { get; set; }
|
||||
|
||||
public bool IsRecentEpisode()
|
||||
{
|
||||
return Episodes.Any(e => e.AirDateUtc >= DateTime.UtcNow.Date.AddDays(-14));
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Release.Title;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.Tv
|
||||
{
|
||||
public interface IAddSeriesService
|
||||
{
|
||||
Series AddSeries(Series newSeries);
|
||||
}
|
||||
|
||||
public class AddSeriesService : IAddSeriesService
|
||||
{
|
||||
private readonly ISeriesService _seriesService;
|
||||
private readonly IBuildFileNames _fileNameBuilder;
|
||||
private readonly IAddSeriesValidator _addSeriesValidator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public AddSeriesService(ISeriesService seriesService,
|
||||
IBuildFileNames fileNameBuilder,
|
||||
IAddSeriesValidator addSeriesValidator,
|
||||
Logger logger)
|
||||
{
|
||||
_seriesService = seriesService;
|
||||
_fileNameBuilder = fileNameBuilder;
|
||||
_addSeriesValidator = addSeriesValidator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Series AddSeries(Series newSeries)
|
||||
{
|
||||
Ensure.That(newSeries, () => newSeries).IsNotNull();
|
||||
|
||||
newSeries = SetPropertiesAndValidate(newSeries);
|
||||
|
||||
_logger.Info("Adding Series {0} Path: [{1}]", newSeries, newSeries.Path);
|
||||
_seriesService.AddSeries(newSeries);
|
||||
|
||||
return newSeries;
|
||||
}
|
||||
|
||||
private Series SetPropertiesAndValidate(Series newSeries)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(newSeries.Path))
|
||||
{
|
||||
//var folderName = _fileNameBuilder.GetSeriesFolder(newSeries);
|
||||
//newSeries.Path = Path.Combine(newSeries.RootFolderPath, folderName);
|
||||
}
|
||||
|
||||
newSeries.CleanTitle = newSeries.Title.CleanSeriesTitle();
|
||||
newSeries.SortTitle = SeriesTitleNormalizer.Normalize(newSeries.Title, newSeries.TvdbId);
|
||||
newSeries.Added = DateTime.UtcNow;
|
||||
|
||||
var validationResult = _addSeriesValidator.Validate(newSeries);
|
||||
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
return newSeries;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Core.Validation.Paths;
|
||||
|
||||
namespace NzbDrone.Core.Tv
|
||||
{
|
||||
public interface IAddSeriesValidator
|
||||
{
|
||||
ValidationResult Validate(Series instance);
|
||||
}
|
||||
|
||||
public class AddSeriesValidator : AbstractValidator<Series>, IAddSeriesValidator
|
||||
{
|
||||
public AddSeriesValidator(RootFolderValidator rootFolderValidator,
|
||||
SeriesTitleSlugValidator seriesTitleSlugValidator)
|
||||
{
|
||||
RuleFor(c => c.Path).Cascade(CascadeMode.StopOnFirstFailure)
|
||||
.IsValidPath()
|
||||
.SetValidator(rootFolderValidator);
|
||||
|
||||
RuleFor(c => c.TitleSlug).SetValidator(seriesTitleSlugValidator);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Marr.Data;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
|
||||
namespace NzbDrone.Core.Tv
|
||||
{
|
||||
public class Episode : ModelBase, IComparable
|
||||
{
|
||||
public Episode()
|
||||
{
|
||||
Images = new List<MediaCover.MediaCover>();
|
||||
}
|
||||
|
||||
public const string AIR_DATE_FORMAT = "yyyy-MM-dd";
|
||||
|
||||
public int SeriesId { get; set; }
|
||||
public int EpisodeFileId { get; set; }
|
||||
public int SeasonNumber { get; set; }
|
||||
public int EpisodeNumber { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string AirDate { get; set; }
|
||||
public DateTime? AirDateUtc { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public bool Monitored { get; set; }
|
||||
public int? AbsoluteEpisodeNumber { get; set; }
|
||||
public int? SceneAbsoluteEpisodeNumber { get; set; }
|
||||
public int? SceneSeasonNumber { get; set; }
|
||||
public int? SceneEpisodeNumber { get; set; }
|
||||
public bool UnverifiedSceneNumbering { get; set; }
|
||||
public Ratings Ratings { get; set; }
|
||||
public List<MediaCover.MediaCover> Images { get; set; }
|
||||
|
||||
public string SeriesTitle { get; private set; }
|
||||
|
||||
public LazyLoaded<EpisodeFile> EpisodeFile { get; set; }
|
||||
|
||||
public Series Series { get; set; }
|
||||
|
||||
public bool HasFile => EpisodeFileId > 0;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[{0}]{1}", Id, Title.NullSafe());
|
||||
}
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
var other = (Episode)obj;
|
||||
|
||||
if (SeasonNumber > other.SeasonNumber)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (SeasonNumber < other.SeasonNumber)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EpisodeNumber > other.EpisodeNumber)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (EpisodeNumber < other.EpisodeNumber)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.IndexerSearch;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
|
||||
namespace NzbDrone.Core.Tv
|
||||
{
|
||||
public interface IEpisodeAddedService
|
||||
{
|
||||
void SearchForRecentlyAdded(int seriesId);
|
||||
}
|
||||
|
||||
public class EpisodeAddedService : IHandle<EpisodeInfoRefreshedEvent>, IEpisodeAddedService
|
||||
{
|
||||
private readonly IManageCommandQueue _commandQueueManager;
|
||||
private readonly IEpisodeService _episodeService;
|
||||
private readonly Logger _logger;
|
||||
private readonly ICached<List<int>> _addedEpisodesCache;
|
||||
|
||||
public EpisodeAddedService(ICacheManager cacheManager,
|
||||
IManageCommandQueue commandQueueManager,
|
||||
IEpisodeService episodeService,
|
||||
Logger logger)
|
||||
{
|
||||
_commandQueueManager = commandQueueManager;
|
||||
_episodeService = episodeService;
|
||||
_logger = logger;
|
||||
_addedEpisodesCache = cacheManager.GetCache<List<int>>(GetType());
|
||||
}
|
||||
|
||||
public void SearchForRecentlyAdded(int seriesId)
|
||||
{
|
||||
var previouslyAired = _addedEpisodesCache.Find(seriesId.ToString());
|
||||
|
||||
if (previouslyAired != null && previouslyAired.Any())
|
||||
{
|
||||
var missing = previouslyAired.Select(e => _episodeService.GetEpisode(e)).Where(e => !e.HasFile).ToList();
|
||||
|
||||
if (missing.Any())
|
||||
{
|
||||
//_commandQueueManager.Push(new EpisodeSearchCommand(missing.Select(e => e.Id).ToList()));
|
||||
}
|
||||
}
|
||||
|
||||
_addedEpisodesCache.Remove(seriesId.ToString());
|
||||
}
|
||||
|
||||
public void Handle(EpisodeInfoRefreshedEvent message)
|
||||
{
|
||||
if (message.Series.AddOptions == null)
|
||||
{
|
||||
if (!message.Series.Monitored)
|
||||
{
|
||||
_logger.Debug("Series is not monitored");
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.Added.Empty())
|
||||
{
|
||||
_logger.Debug("No new episodes, skipping search");
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.Added.None(a => a.AirDateUtc.HasValue))
|
||||
{
|
||||
_logger.Debug("No new episodes have an air date");
|
||||
return;
|
||||
}
|
||||
|
||||
var previouslyAired = message.Added.Where(a => a.AirDateUtc.HasValue && a.AirDateUtc.Value.Before(DateTime.UtcNow.AddDays(1)) && a.Monitored).ToList();
|
||||
|
||||
if (previouslyAired.Empty())
|
||||
{
|
||||
_logger.Debug("Newly added episodes all air in the future");
|
||||
return;
|
||||
}
|
||||
|
||||
_addedEpisodesCache.Set(message.Series.Id.ToString(), previouslyAired.Select(e => e.Id).ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Profiles.Qualities;
|
||||
using NzbDrone.Core.Profiles.Languages;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.Tv
|
||||
{
|
||||
public interface IEpisodeCutoffService
|
||||
{
|
||||
PagingSpec<Episode> EpisodesWhereCutoffUnmet(PagingSpec<Episode> pagingSpec);
|
||||
}
|
||||
|
||||
public class EpisodeCutoffService : IEpisodeCutoffService
|
||||
{
|
||||
private readonly IEpisodeRepository _episodeRepository;
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly ILanguageProfileService _languageProfileService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public EpisodeCutoffService(IEpisodeRepository episodeRepository, IProfileService profileService, ILanguageProfileService languageProfileService, Logger logger)
|
||||
{
|
||||
_episodeRepository = episodeRepository;
|
||||
_profileService = profileService;
|
||||
_languageProfileService = languageProfileService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public PagingSpec<Episode> EpisodesWhereCutoffUnmet(PagingSpec<Episode> pagingSpec)
|
||||
{
|
||||
var qualitiesBelowCutoff = new List<QualitiesBelowCutoff>();
|
||||
var languagesBelowCutoff = new List<LanguagesBelowCutoff>();
|
||||
var profiles = _profileService.All();
|
||||
var languageProfiles = _languageProfileService.All();
|
||||
|
||||
//Get all items less than the cutoff
|
||||
foreach (var profile in profiles)
|
||||
{
|
||||
var cutoffIndex = profile.Items.FindIndex(v => v.Quality == profile.Cutoff);
|
||||
var belowCutoff = profile.Items.Take(cutoffIndex).ToList();
|
||||
|
||||
if (belowCutoff.Any())
|
||||
{
|
||||
qualitiesBelowCutoff.Add(new QualitiesBelowCutoff(profile.Id, belowCutoff.Select(i => i.Quality.Id)));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var profile in languageProfiles)
|
||||
{
|
||||
var languageCutoffIndex = profile.Languages.FindIndex(v => v.Language == profile.Cutoff);
|
||||
var belowLanguageCutoff = profile.Languages.Take(languageCutoffIndex).ToList();
|
||||
|
||||
if (belowLanguageCutoff.Any())
|
||||
{
|
||||
languagesBelowCutoff.Add(new LanguagesBelowCutoff(profile.Id, belowLanguageCutoff.Select(l => l.Language.Id)));
|
||||
}
|
||||
}
|
||||
|
||||
return _episodeRepository.EpisodesWhereCutoffUnmet(pagingSpec, qualitiesBelowCutoff, languagesBelowCutoff, false);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,298 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Marr.Data.QGen;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Datastore.Extensions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.Tv
|
||||
{
|
||||
public interface IEpisodeRepository : IBasicRepository<Episode>
|
||||
{
|
||||
Episode Find(int seriesId, int season, int episodeNumber);
|
||||
Episode Find(int seriesId, int absoluteEpisodeNumber);
|
||||
Episode Get(int seriesId, string date);
|
||||
Episode Find(int seriesId, string date);
|
||||
List<Episode> GetEpisodes(int seriesId);
|
||||
List<Episode> GetEpisodes(int seriesId, int seasonNumber);
|
||||
List<Episode> GetEpisodeByFileId(int fileId);
|
||||
List<Episode> EpisodesWithFiles(int seriesId);
|
||||
PagingSpec<Episode> EpisodesWithoutFiles(PagingSpec<Episode> pagingSpec, bool includeSpecials);
|
||||
PagingSpec<Episode> EpisodesWhereCutoffUnmet(PagingSpec<Episode> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff, List<LanguagesBelowCutoff> languagesBelowCutoff, bool includeSpecials);
|
||||
List<Episode> FindEpisodesBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber);
|
||||
Episode FindEpisodeBySceneNumbering(int seriesId, int sceneAbsoluteEpisodeNumber);
|
||||
List<Episode> EpisodesBetweenDates(DateTime startDate, DateTime endDate, bool includeUnmonitored);
|
||||
void SetMonitoredFlat(Episode episode, bool monitored);
|
||||
void SetMonitoredBySeason(int seriesId, int seasonNumber, bool monitored);
|
||||
void SetMonitored(IEnumerable<int> ids, bool monitored);
|
||||
void SetFileId(int episodeId, int fileId);
|
||||
}
|
||||
|
||||
public class EpisodeRepository : BasicRepository<Episode>, IEpisodeRepository
|
||||
{
|
||||
private readonly IMainDatabase _database;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public EpisodeRepository(IMainDatabase database, IEventAggregator eventAggregator, Logger logger)
|
||||
: base(database, eventAggregator)
|
||||
{
|
||||
_database = database;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Episode Find(int seriesId, int season, int episodeNumber)
|
||||
{
|
||||
return Query.Where(s => s.SeriesId == seriesId)
|
||||
.AndWhere(s => s.SeasonNumber == season)
|
||||
.AndWhere(s => s.EpisodeNumber == episodeNumber)
|
||||
.SingleOrDefault();
|
||||
}
|
||||
|
||||
public Episode Find(int seriesId, int absoluteEpisodeNumber)
|
||||
{
|
||||
return Query.Where(s => s.SeriesId == seriesId)
|
||||
.AndWhere(s => s.AbsoluteEpisodeNumber == absoluteEpisodeNumber)
|
||||
.SingleOrDefault();
|
||||
}
|
||||
|
||||
public Episode Get(int seriesId, string date)
|
||||
{
|
||||
var episode = FindOneByAirDate(seriesId, date);
|
||||
|
||||
if (episode == null)
|
||||
{
|
||||
throw new InvalidOperationException("Expected at one episode");
|
||||
}
|
||||
|
||||
return episode;
|
||||
}
|
||||
|
||||
public Episode Find(int seriesId, string date)
|
||||
{
|
||||
return FindOneByAirDate(seriesId, date);
|
||||
}
|
||||
|
||||
public List<Episode> GetEpisodes(int seriesId)
|
||||
{
|
||||
return Query.Where(s => s.SeriesId == seriesId).ToList();
|
||||
}
|
||||
|
||||
public List<Episode> GetEpisodes(int seriesId, int seasonNumber)
|
||||
{
|
||||
return Query.Where(s => s.SeriesId == seriesId)
|
||||
.AndWhere(s => s.SeasonNumber == seasonNumber)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public List<Episode> GetEpisodeByFileId(int fileId)
|
||||
{
|
||||
return Query.Where(e => e.EpisodeFileId == fileId).ToList();
|
||||
}
|
||||
|
||||
public List<Episode> EpisodesWithFiles(int seriesId)
|
||||
{
|
||||
return Query.Join<Episode, EpisodeFile>(JoinType.Inner, e => e.EpisodeFile, (e, ef) => e.EpisodeFileId == ef.Id)
|
||||
.Where(e => e.SeriesId == seriesId);
|
||||
}
|
||||
|
||||
public PagingSpec<Episode> EpisodesWithoutFiles(PagingSpec<Episode> pagingSpec, bool includeSpecials)
|
||||
{
|
||||
var currentTime = DateTime.UtcNow;
|
||||
var startingSeasonNumber = 1;
|
||||
|
||||
if (includeSpecials)
|
||||
{
|
||||
startingSeasonNumber = 0;
|
||||
}
|
||||
|
||||
pagingSpec.TotalRecords = GetMissingEpisodesQuery(pagingSpec, currentTime, startingSeasonNumber).GetRowCount();
|
||||
pagingSpec.Records = GetMissingEpisodesQuery(pagingSpec, currentTime, startingSeasonNumber).ToList();
|
||||
|
||||
return pagingSpec;
|
||||
}
|
||||
|
||||
public PagingSpec<Episode> EpisodesWhereCutoffUnmet(PagingSpec<Episode> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff, List<LanguagesBelowCutoff> languagesBelowCutoff, bool includeSpecials)
|
||||
{
|
||||
var startingSeasonNumber = 1;
|
||||
|
||||
if (includeSpecials)
|
||||
{
|
||||
startingSeasonNumber = 0;
|
||||
}
|
||||
|
||||
pagingSpec.TotalRecords = EpisodesWhereCutoffUnmetQuery(pagingSpec, qualitiesBelowCutoff, languagesBelowCutoff, startingSeasonNumber).GetRowCount();
|
||||
pagingSpec.Records = EpisodesWhereCutoffUnmetQuery(pagingSpec, qualitiesBelowCutoff, languagesBelowCutoff, startingSeasonNumber).ToList();
|
||||
return pagingSpec;
|
||||
}
|
||||
|
||||
public List<Episode> FindEpisodesBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return Query.Where(s => s.SeriesId == seriesId)
|
||||
.AndWhere(s => s.SceneSeasonNumber == seasonNumber)
|
||||
.AndWhere(s => s.SceneEpisodeNumber == episodeNumber);
|
||||
}
|
||||
|
||||
public Episode FindEpisodeBySceneNumbering(int seriesId, int sceneAbsoluteEpisodeNumber)
|
||||
{
|
||||
var episodes = Query.Where(s => s.SeriesId == seriesId)
|
||||
.AndWhere(s => s.SceneAbsoluteEpisodeNumber == sceneAbsoluteEpisodeNumber)
|
||||
.ToList();
|
||||
|
||||
if (episodes.Empty() || episodes.Count > 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return episodes.Single();
|
||||
}
|
||||
|
||||
public List<Episode> EpisodesBetweenDates(DateTime startDate, DateTime endDate, bool includeUnmonitored)
|
||||
{
|
||||
var query = Query.Join<Episode, Series>(JoinType.Inner, e => e.Series, (e, s) => e.SeriesId == s.Id)
|
||||
.Where<Episode>(e => e.AirDateUtc >= startDate)
|
||||
.AndWhere(e => e.AirDateUtc <= endDate);
|
||||
|
||||
|
||||
if (!includeUnmonitored)
|
||||
{
|
||||
query.AndWhere(e => e.Monitored)
|
||||
.AndWhere(e => e.Series.Monitored);
|
||||
}
|
||||
|
||||
return query.ToList();
|
||||
}
|
||||
|
||||
public void SetMonitoredFlat(Episode episode, bool monitored)
|
||||
{
|
||||
episode.Monitored = monitored;
|
||||
SetFields(episode, p => p.Monitored);
|
||||
}
|
||||
|
||||
public void SetMonitoredBySeason(int seriesId, int seasonNumber, bool monitored)
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.AddParameter("seriesId", seriesId);
|
||||
mapper.AddParameter("seasonNumber", seasonNumber);
|
||||
mapper.AddParameter("monitored", monitored);
|
||||
|
||||
const string sql = "UPDATE Episodes " +
|
||||
"SET Monitored = @monitored " +
|
||||
"WHERE SeriesId = @seriesId " +
|
||||
"AND SeasonNumber = @seasonNumber";
|
||||
|
||||
mapper.ExecuteNonQuery(sql);
|
||||
}
|
||||
|
||||
public void SetMonitored(IEnumerable<int> ids, bool monitored)
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.AddParameter("monitored", monitored);
|
||||
|
||||
var sql = "UPDATE Episodes " +
|
||||
"SET Monitored = @monitored " +
|
||||
$"WHERE Id IN ({string.Join(", ", ids)})";
|
||||
|
||||
mapper.ExecuteNonQuery(sql);
|
||||
}
|
||||
|
||||
public void SetFileId(int episodeId, int fileId)
|
||||
{
|
||||
SetFields(new Episode { Id = episodeId, EpisodeFileId = fileId }, episode => episode.EpisodeFileId);
|
||||
}
|
||||
|
||||
private SortBuilder<Episode> GetMissingEpisodesQuery(PagingSpec<Episode> pagingSpec, DateTime currentTime, int startingSeasonNumber)
|
||||
{
|
||||
return Query.Join<Episode, Series>(JoinType.Inner, e => e.Series, (e, s) => e.SeriesId == s.Id)
|
||||
.Where(pagingSpec.FilterExpression)
|
||||
.AndWhere(e => e.EpisodeFileId == 0)
|
||||
.AndWhere(e => e.SeasonNumber >= startingSeasonNumber)
|
||||
.AndWhere(BuildAirDateUtcCutoffWhereClause(currentTime))
|
||||
.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection())
|
||||
.Skip(pagingSpec.PagingOffset())
|
||||
.Take(pagingSpec.PageSize);
|
||||
}
|
||||
|
||||
private SortBuilder<Episode> EpisodesWhereCutoffUnmetQuery(PagingSpec<Episode> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff, List<LanguagesBelowCutoff> languagesBelowCutoff, int startingSeasonNumber)
|
||||
{
|
||||
return Query.Join<Episode, Series>(JoinType.Inner, e => e.Series, (e, s) => e.SeriesId == s.Id)
|
||||
.Join<Episode, EpisodeFile>(JoinType.Left, e => e.EpisodeFile, (e, s) => e.EpisodeFileId == s.Id)
|
||||
.Where(pagingSpec.FilterExpression)
|
||||
.AndWhere(e => e.EpisodeFileId != 0)
|
||||
.AndWhere(e => e.SeasonNumber >= startingSeasonNumber)
|
||||
.AndWhere(
|
||||
String.Format("({0} OR {1})",
|
||||
BuildLanguageCutoffWhereClause(languagesBelowCutoff),
|
||||
BuildQualityCutoffWhereClause(qualitiesBelowCutoff)))
|
||||
.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection())
|
||||
.Skip(pagingSpec.PagingOffset())
|
||||
.Take(pagingSpec.PageSize);
|
||||
}
|
||||
|
||||
private string BuildAirDateUtcCutoffWhereClause(DateTime currentTime)
|
||||
{
|
||||
return string.Format("WHERE datetime(strftime('%s', [t0].[AirDateUtc]) + [t1].[RunTime] * 60, 'unixepoch') <= '{0}'",
|
||||
currentTime.ToString("yyyy-MM-dd HH:mm:ss"));
|
||||
}
|
||||
|
||||
private string BuildLanguageCutoffWhereClause(List<LanguagesBelowCutoff> languagesBelowCutoff)
|
||||
{
|
||||
var clauses = new List<String>();
|
||||
|
||||
foreach (var language in languagesBelowCutoff)
|
||||
{
|
||||
foreach (var belowCutoff in language.LanguageIds)
|
||||
{
|
||||
clauses.Add(String.Format("([t1].[LanguageProfileId] = {0} AND [t2].[Language] = {1})", language.ProfileId, belowCutoff));
|
||||
}
|
||||
}
|
||||
|
||||
return String.Format("({0})", String.Join(" OR ", clauses));
|
||||
}
|
||||
|
||||
private string BuildQualityCutoffWhereClause(List<QualitiesBelowCutoff> qualitiesBelowCutoff)
|
||||
{
|
||||
var clauses = new List<string>();
|
||||
|
||||
foreach (var profile in qualitiesBelowCutoff)
|
||||
{
|
||||
foreach (var belowCutoff in profile.QualityIds)
|
||||
{
|
||||
clauses.Add(string.Format("([t1].[ProfileId] = {0} AND [t2].[Quality] LIKE '%_quality_: {1},%')", profile.ProfileId, belowCutoff));
|
||||
}
|
||||
}
|
||||
|
||||
return string.Format("({0})", string.Join(" OR ", clauses));
|
||||
}
|
||||
|
||||
private Episode FindOneByAirDate(int seriesId, string date)
|
||||
{
|
||||
var episodes = Query.Where(s => s.SeriesId == seriesId)
|
||||
.AndWhere(s => s.AirDate == date)
|
||||
.ToList();
|
||||
|
||||
if (!episodes.Any()) return null;
|
||||
|
||||
if (episodes.Count == 1) return episodes.First();
|
||||
|
||||
_logger.Debug("Multiple episodes with the same air date were found, will exclude specials");
|
||||
|
||||
var regularEpisodes = episodes.Where(e => e.SeasonNumber > 0).ToList();
|
||||
|
||||
if (regularEpisodes.Count == 1)
|
||||
{
|
||||
_logger.Debug("Left with one episode after excluding specials");
|
||||
return regularEpisodes.First();
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Multiple episodes with the same air date found");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,231 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
|
||||
namespace NzbDrone.Core.Tv
|
||||
{
|
||||
public interface IEpisodeService
|
||||
{
|
||||
Episode GetEpisode(int id);
|
||||
List<Episode> GetEpisodes(IEnumerable<int> ids);
|
||||
Episode FindEpisode(int seriesId, int seasonNumber, int episodeNumber);
|
||||
Episode FindEpisode(int seriesId, int absoluteEpisodeNumber);
|
||||
Episode FindEpisodeByTitle(int seriesId, int seasonNumber, string releaseTitle);
|
||||
List<Episode> FindEpisodesBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber);
|
||||
Episode FindEpisodeBySceneNumbering(int seriesId, int sceneAbsoluteEpisodeNumber);
|
||||
Episode GetEpisode(int seriesId, string date);
|
||||
Episode FindEpisode(int seriesId, string date);
|
||||
List<Episode> GetEpisodeBySeries(int seriesId);
|
||||
List<Episode> GetEpisodesBySeason(int seriesId, int seasonNumber);
|
||||
List<Episode> EpisodesWithFiles(int seriesId);
|
||||
PagingSpec<Episode> EpisodesWithoutFiles(PagingSpec<Episode> pagingSpec);
|
||||
List<Episode> GetEpisodesByFileId(int episodeFileId);
|
||||
void UpdateEpisode(Episode episode);
|
||||
void SetEpisodeMonitored(int episodeId, bool monitored);
|
||||
void SetMonitored(IEnumerable<int> ids, bool monitored);
|
||||
void UpdateEpisodes(List<Episode> episodes);
|
||||
List<Episode> EpisodesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored);
|
||||
void InsertMany(List<Episode> episodes);
|
||||
void UpdateMany(List<Episode> episodes);
|
||||
void DeleteMany(List<Episode> episodes);
|
||||
void SetEpisodeMonitoredBySeason(int seriesId, int seasonNumber, bool monitored);
|
||||
}
|
||||
|
||||
public class EpisodeService : IEpisodeService,
|
||||
//IHandle<EpisodeFileDeletedEvent>,
|
||||
//IHandle<EpisodeFileAddedEvent>,
|
||||
IHandleAsync<SeriesDeletedEvent>
|
||||
{
|
||||
private readonly IEpisodeRepository _episodeRepository;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public EpisodeService(IEpisodeRepository episodeRepository, IConfigService configService, Logger logger)
|
||||
{
|
||||
_episodeRepository = episodeRepository;
|
||||
_configService = configService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Episode GetEpisode(int id)
|
||||
{
|
||||
return _episodeRepository.Get(id);
|
||||
}
|
||||
|
||||
public List<Episode> GetEpisodes(IEnumerable<int> ids)
|
||||
{
|
||||
return _episodeRepository.Get(ids).ToList();
|
||||
}
|
||||
|
||||
public Episode FindEpisode(int seriesId, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return _episodeRepository.Find(seriesId, seasonNumber, episodeNumber);
|
||||
}
|
||||
|
||||
public Episode FindEpisode(int seriesId, int absoluteEpisodeNumber)
|
||||
{
|
||||
return _episodeRepository.Find(seriesId, absoluteEpisodeNumber);
|
||||
}
|
||||
|
||||
public List<Episode> FindEpisodesBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return _episodeRepository.FindEpisodesBySceneNumbering(seriesId, seasonNumber, episodeNumber);
|
||||
}
|
||||
|
||||
public Episode FindEpisodeBySceneNumbering(int seriesId, int sceneAbsoluteEpisodeNumber)
|
||||
{
|
||||
return _episodeRepository.FindEpisodeBySceneNumbering(seriesId, sceneAbsoluteEpisodeNumber);
|
||||
}
|
||||
|
||||
public Episode GetEpisode(int seriesId, string date)
|
||||
{
|
||||
return _episodeRepository.Get(seriesId, date);
|
||||
}
|
||||
|
||||
public Episode FindEpisode(int seriesId, string date)
|
||||
{
|
||||
return _episodeRepository.Find(seriesId, date);
|
||||
}
|
||||
|
||||
public List<Episode> GetEpisodeBySeries(int seriesId)
|
||||
{
|
||||
return _episodeRepository.GetEpisodes(seriesId).ToList();
|
||||
}
|
||||
|
||||
public List<Episode> GetEpisodesBySeason(int seriesId, int seasonNumber)
|
||||
{
|
||||
return _episodeRepository.GetEpisodes(seriesId, seasonNumber);
|
||||
}
|
||||
|
||||
public Episode FindEpisodeByTitle(int seriesId, int seasonNumber, string releaseTitle)
|
||||
{
|
||||
// TODO: can replace this search mechanism with something smarter/faster/better
|
||||
var normalizedReleaseTitle = Parser.Parser.NormalizeEpisodeTitle(releaseTitle).Replace(".", " ");
|
||||
var episodes = _episodeRepository.GetEpisodes(seriesId, seasonNumber);
|
||||
|
||||
var matches = episodes.Select(
|
||||
episode => new
|
||||
{
|
||||
Position = normalizedReleaseTitle.IndexOf(Parser.Parser.NormalizeEpisodeTitle(episode.Title), StringComparison.CurrentCultureIgnoreCase),
|
||||
Length = Parser.Parser.NormalizeEpisodeTitle(episode.Title).Length,
|
||||
Episode = episode
|
||||
})
|
||||
.Where(e => e.Episode.Title.Length > 0 && e.Position >= 0)
|
||||
.OrderBy(e => e.Position)
|
||||
.ThenByDescending(e => e.Length)
|
||||
.ToList();
|
||||
|
||||
if (matches.Any())
|
||||
{
|
||||
return matches.First().Episode;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<Episode> EpisodesWithFiles(int seriesId)
|
||||
{
|
||||
return _episodeRepository.EpisodesWithFiles(seriesId);
|
||||
}
|
||||
|
||||
public PagingSpec<Episode> EpisodesWithoutFiles(PagingSpec<Episode> pagingSpec)
|
||||
{
|
||||
var episodeResult = _episodeRepository.EpisodesWithoutFiles(pagingSpec, true);
|
||||
|
||||
return episodeResult;
|
||||
}
|
||||
|
||||
public List<Episode> GetEpisodesByFileId(int episodeFileId)
|
||||
{
|
||||
return _episodeRepository.GetEpisodeByFileId(episodeFileId);
|
||||
}
|
||||
|
||||
public void UpdateEpisode(Episode episode)
|
||||
{
|
||||
_episodeRepository.Update(episode);
|
||||
}
|
||||
|
||||
public void SetEpisodeMonitored(int episodeId, bool monitored)
|
||||
{
|
||||
var episode = _episodeRepository.Get(episodeId);
|
||||
_episodeRepository.SetMonitoredFlat(episode, monitored);
|
||||
|
||||
_logger.Debug("Monitored flag for Episode:{0} was set to {1}", episodeId, monitored);
|
||||
}
|
||||
|
||||
public void SetMonitored(IEnumerable<int> ids, bool monitored)
|
||||
{
|
||||
_episodeRepository.SetMonitored(ids, monitored);
|
||||
}
|
||||
|
||||
public void SetEpisodeMonitoredBySeason(int seriesId, int seasonNumber, bool monitored)
|
||||
{
|
||||
_episodeRepository.SetMonitoredBySeason(seriesId, seasonNumber, monitored);
|
||||
}
|
||||
|
||||
public void UpdateEpisodes(List<Episode> episodes)
|
||||
{
|
||||
_episodeRepository.UpdateMany(episodes);
|
||||
}
|
||||
|
||||
public List<Episode> EpisodesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored)
|
||||
{
|
||||
var episodes = _episodeRepository.EpisodesBetweenDates(start.ToUniversalTime(), end.ToUniversalTime(), includeUnmonitored);
|
||||
|
||||
return episodes;
|
||||
}
|
||||
|
||||
public void InsertMany(List<Episode> episodes)
|
||||
{
|
||||
_episodeRepository.InsertMany(episodes);
|
||||
}
|
||||
|
||||
public void UpdateMany(List<Episode> episodes)
|
||||
{
|
||||
_episodeRepository.UpdateMany(episodes);
|
||||
}
|
||||
|
||||
public void DeleteMany(List<Episode> episodes)
|
||||
{
|
||||
_episodeRepository.DeleteMany(episodes);
|
||||
}
|
||||
|
||||
public void HandleAsync(SeriesDeletedEvent message)
|
||||
{
|
||||
var episodes = GetEpisodeBySeries(message.Series.Id);
|
||||
_episodeRepository.DeleteMany(episodes);
|
||||
}
|
||||
|
||||
//public void Handle(EpisodeFileDeletedEvent message)
|
||||
//{
|
||||
// foreach (var episode in GetEpisodesByFileId(message.EpisodeFile.Id))
|
||||
// {
|
||||
// _logger.Debug("Detaching episode {0} from file.", episode.Id);
|
||||
// episode.EpisodeFileId = 0;
|
||||
|
||||
// if (message.Reason != DeleteMediaFileReason.Upgrade && _configService.AutoUnmonitorPreviouslyDownloadedTracks)
|
||||
// {
|
||||
// episode.Monitored = false;
|
||||
// }
|
||||
|
||||
// UpdateEpisode(episode);
|
||||
// }
|
||||
//}
|
||||
|
||||
//public void Handle(EpisodeFileAddedEvent message)
|
||||
//{
|
||||
// foreach (var episode in message.EpisodeFile.Episodes.Value)
|
||||
// {
|
||||
// _episodeRepository.SetFileId(episode.Id, message.EpisodeFile.Id);
|
||||
// _logger.Debug("Linking [{0}] > [{1}]", message.EpisodeFile.RelativePath, episode);
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Core.DataAugmentation.DailySeries;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using NzbDrone.Core.Tv.Commands;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
|
||||
namespace NzbDrone.Core.Tv
|
||||
{
|
||||
public class RefreshSeriesService : IExecute<RefreshSeriesCommand>
|
||||
{
|
||||
private readonly ISeriesService _seriesService;
|
||||
private readonly IRefreshEpisodeService _refreshEpisodeService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IDailySeriesService _dailySeriesService;
|
||||
private readonly IDiskScanService _diskScanService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public RefreshSeriesService(ISeriesService seriesService,
|
||||
IRefreshEpisodeService refreshEpisodeService,
|
||||
IEventAggregator eventAggregator,
|
||||
IDailySeriesService dailySeriesService,
|
||||
IDiskScanService diskScanService,
|
||||
Logger logger)
|
||||
{
|
||||
_seriesService = seriesService;
|
||||
_refreshEpisodeService = refreshEpisodeService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_dailySeriesService = dailySeriesService;
|
||||
_diskScanService = diskScanService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
private List<Season> UpdateSeasons(Series series, Series seriesInfo)
|
||||
{
|
||||
var seasons = seriesInfo.Seasons.DistinctBy(s => s.SeasonNumber).ToList();
|
||||
|
||||
foreach (var season in seasons)
|
||||
{
|
||||
var existingSeason = series.Seasons.FirstOrDefault(s => s.SeasonNumber == season.SeasonNumber);
|
||||
|
||||
//Todo: Should this should use the previous season's monitored state?
|
||||
if (existingSeason == null)
|
||||
{
|
||||
if (season.SeasonNumber == 0)
|
||||
{
|
||||
season.Monitored = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
_logger.Debug("New season ({0}) for series: [{1}] {2}, setting monitored to true", season.SeasonNumber, series.TvdbId, series.Title);
|
||||
season.Monitored = true;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
season.Monitored = existingSeason.Monitored;
|
||||
}
|
||||
}
|
||||
|
||||
return seasons;
|
||||
}
|
||||
|
||||
public void Execute(RefreshSeriesCommand message)
|
||||
{
|
||||
_eventAggregator.PublishEvent(new SeriesRefreshStartingEvent(message.Trigger == CommandTrigger.Manual));
|
||||
|
||||
if (message.SeriesId.HasValue)
|
||||
{
|
||||
var series = _seriesService.GetSeries(message.SeriesId.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
var allSeries = _seriesService.GetAllSeries().OrderBy(c => c.SortTitle).ToList();
|
||||
|
||||
foreach (var series in allSeries)
|
||||
{
|
||||
if (message.Trigger == CommandTrigger.Manual)
|
||||
{
|
||||
try
|
||||
{
|
||||
//RefreshSeriesInfo(series);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e, "Couldn't refresh info for {0}", series);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.Info("Skipping refresh of series: {0}", series.Title);
|
||||
//_diskScanService.Scan(series);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e, "Couldn't rescan series {0}", series);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Marr.Data;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Profiles.Qualities;
|
||||
using NzbDrone.Core.Profiles.Languages;
|
||||
|
||||
namespace NzbDrone.Core.Tv
|
||||
{
|
||||
public class Series : ModelBase
|
||||
{
|
||||
public Series()
|
||||
{
|
||||
Images = new List<MediaCover.MediaCover>();
|
||||
Genres = new List<string>();
|
||||
Actors = new List<Actor>();
|
||||
Seasons = new List<Season>();
|
||||
Tags = new HashSet<int>();
|
||||
}
|
||||
|
||||
public int TvdbId { get; set; }
|
||||
public int TvRageId { get; set; }
|
||||
public int TvMazeId { get; set; }
|
||||
public string ImdbId { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string CleanTitle { get; set; }
|
||||
public string SortTitle { get; set; }
|
||||
public SeriesStatusType Status { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public string AirTime { get; set; }
|
||||
public bool Monitored { get; set; }
|
||||
public int ProfileId { get; set; }
|
||||
public int LanguageProfileId { get; set; }
|
||||
public bool SeasonFolder { get; set; }
|
||||
public DateTime? LastInfoSync { get; set; }
|
||||
public int Runtime { get; set; }
|
||||
public List<MediaCover.MediaCover> Images { get; set; }
|
||||
public SeriesTypes SeriesType { get; set; }
|
||||
public string Network { get; set; }
|
||||
public bool UseSceneNumbering { get; set; }
|
||||
public string TitleSlug { get; set; }
|
||||
public string Path { get; set; }
|
||||
public int Year { get; set; }
|
||||
public Ratings Ratings { get; set; }
|
||||
public List<string> Genres { get; set; }
|
||||
public List<Actor> Actors { get; set; }
|
||||
public string Certification { get; set; }
|
||||
public string RootFolderPath { get; set; }
|
||||
public DateTime Added { get; set; }
|
||||
public DateTime? FirstAired { get; set; }
|
||||
public LazyLoaded<Profile> Profile { get; set; }
|
||||
public LazyLoaded<LanguageProfile> LanguageProfile { get; set; }
|
||||
|
||||
public List<Season> Seasons { get; set; }
|
||||
public HashSet<int> Tags { get; set; }
|
||||
public AddSeriesOptions AddOptions { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[{0}][{1}]", TvdbId, Title.NullSafe());
|
||||
}
|
||||
|
||||
public void ApplyChanges(Series otherSeries)
|
||||
{
|
||||
TvdbId = otherSeries.TvdbId;
|
||||
|
||||
Seasons = otherSeries.Seasons;
|
||||
Path = otherSeries.Path;
|
||||
ProfileId = otherSeries.ProfileId;
|
||||
LanguageProfileId = otherSeries.LanguageProfileId;
|
||||
|
||||
SeasonFolder = otherSeries.SeasonFolder;
|
||||
Monitored = otherSeries.Monitored;
|
||||
|
||||
SeriesType = otherSeries.SeriesType;
|
||||
RootFolderPath = otherSeries.RootFolderPath;
|
||||
Tags = otherSeries.Tags;
|
||||
AddOptions = otherSeries.AddOptions;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Tv.Commands;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
|
||||
namespace NzbDrone.Core.Tv
|
||||
{
|
||||
public class SeriesAddedHandler : IHandle<SeriesAddedEvent>,
|
||||
IHandle<SeriesImportedEvent>
|
||||
{
|
||||
private readonly IManageCommandQueue _commandQueueManager;
|
||||
|
||||
public SeriesAddedHandler(IManageCommandQueue commandQueueManager)
|
||||
{
|
||||
_commandQueueManager = commandQueueManager;
|
||||
}
|
||||
|
||||
public void Handle(SeriesAddedEvent message)
|
||||
{
|
||||
_commandQueueManager.Push(new RefreshSeriesCommand(message.Series.Id));
|
||||
}
|
||||
|
||||
public void Handle(SeriesImportedEvent message)
|
||||
{
|
||||
_commandQueueManager.PushMany(message.SeriesIds.Select(s => new RefreshSeriesCommand(s)).ToList());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,229 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.DataAugmentation.Scene;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
|
||||
namespace NzbDrone.Core.Tv
|
||||
{
|
||||
public interface ISeriesService
|
||||
{
|
||||
Series GetSeries(int seriesId);
|
||||
List<Series> GetSeries(IEnumerable<int> seriesIds);
|
||||
Series AddSeries(Series newSeries);
|
||||
List<Series> AddSeries(List<Series> newSeries);
|
||||
Series FindByTvdbId(int tvdbId);
|
||||
Series FindByTvRageId(int tvRageId);
|
||||
Series FindByTitle(string title);
|
||||
Series FindByTitle(string title, int year);
|
||||
Series FindByTitleInexact(string title);
|
||||
void DeleteSeries(int seriesId, bool deleteFiles);
|
||||
List<Series> GetAllSeries();
|
||||
List<Series> AllForTag(int tagId);
|
||||
Series UpdateSeries(Series series);
|
||||
List<Series> UpdateSeries(List<Series> series);
|
||||
bool SeriesPathExists(string folder);
|
||||
void RemoveAddOptions(Series series);
|
||||
}
|
||||
|
||||
public class SeriesService : ISeriesService
|
||||
{
|
||||
private readonly ISeriesRepository _seriesRepository;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
// private readonly ISceneMappingService _sceneMappingService;
|
||||
private readonly IEpisodeService _episodeService;
|
||||
private readonly IBuildFileNames _fileNameBuilder;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public SeriesService(ISeriesRepository seriesRepository,
|
||||
IEventAggregator eventAggregator,
|
||||
// ISceneMappingService sceneMappingService,
|
||||
IEpisodeService episodeService,
|
||||
IBuildFileNames fileNameBuilder,
|
||||
Logger logger)
|
||||
{
|
||||
_seriesRepository = seriesRepository;
|
||||
_eventAggregator = eventAggregator;
|
||||
//_sceneMappingService = sceneMappingService;
|
||||
_episodeService = episodeService;
|
||||
_fileNameBuilder = fileNameBuilder;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Series GetSeries(int seriesId)
|
||||
{
|
||||
return _seriesRepository.Get(seriesId);
|
||||
}
|
||||
|
||||
public List<Series> GetSeries(IEnumerable<int> seriesIds)
|
||||
{
|
||||
return _seriesRepository.Get(seriesIds).ToList();
|
||||
}
|
||||
|
||||
public Series AddSeries(Series newSeries)
|
||||
{
|
||||
_seriesRepository.Insert(newSeries);
|
||||
_eventAggregator.PublishEvent(new SeriesAddedEvent(GetSeries(newSeries.Id)));
|
||||
|
||||
return newSeries;
|
||||
}
|
||||
|
||||
public List<Series> AddSeries(List<Series> newSeries)
|
||||
{
|
||||
_seriesRepository.InsertMany(newSeries);
|
||||
_eventAggregator.PublishEvent(new SeriesImportedEvent(newSeries.Select(s => s.Id).ToList()));
|
||||
|
||||
return newSeries;
|
||||
}
|
||||
|
||||
public Series FindByTvdbId(int tvRageId)
|
||||
{
|
||||
return _seriesRepository.FindByTvdbId(tvRageId);
|
||||
}
|
||||
|
||||
public Series FindByTvRageId(int tvRageId)
|
||||
{
|
||||
return _seriesRepository.FindByTvRageId(tvRageId);
|
||||
}
|
||||
|
||||
public Series FindByTitle(string title)
|
||||
{
|
||||
//var tvdbId = _sceneMappingService.FindTvdbId(title);
|
||||
|
||||
//if (tvdbId.HasValue)
|
||||
//{
|
||||
// return _seriesRepository.FindByTvdbId(tvdbId.Value);
|
||||
//}
|
||||
|
||||
return _seriesRepository.FindByTitle(title.CleanSeriesTitle());
|
||||
}
|
||||
|
||||
public Series FindByTitleInexact(string title)
|
||||
{
|
||||
// find any series clean title within the provided release title
|
||||
string cleanTitle = title.CleanSeriesTitle();
|
||||
var list = _seriesRepository.All().Where(s => cleanTitle.Contains(s.CleanTitle)).ToList();
|
||||
if (!list.Any())
|
||||
{
|
||||
// no series matched
|
||||
return null;
|
||||
}
|
||||
if (list.Count == 1)
|
||||
{
|
||||
// return the first series if there is only one
|
||||
return list.Single();
|
||||
}
|
||||
// build ordered list of series by position in the search string
|
||||
var query =
|
||||
list.Select(series => new
|
||||
{
|
||||
position = cleanTitle.IndexOf(series.CleanTitle),
|
||||
length = series.CleanTitle.Length,
|
||||
series = series
|
||||
})
|
||||
.Where(s => (s.position>=0))
|
||||
.ToList()
|
||||
.OrderBy(s => s.position)
|
||||
.ThenByDescending(s => s.length)
|
||||
.ToList();
|
||||
|
||||
// get the leftmost series that is the longest
|
||||
// series are usually the first thing in release title, so we select the leftmost and longest match
|
||||
var match = query.First().series;
|
||||
|
||||
_logger.Debug("Multiple series matched {0} from title {1}", match.Title, title);
|
||||
foreach (var entry in list)
|
||||
{
|
||||
_logger.Debug("Multiple series match candidate: {0} cleantitle: {1}", entry.Title, entry.CleanTitle);
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
public Series FindByTitle(string title, int year)
|
||||
{
|
||||
return _seriesRepository.FindByTitle(title.CleanSeriesTitle(), year);
|
||||
}
|
||||
|
||||
public void DeleteSeries(int seriesId, bool deleteFiles)
|
||||
{
|
||||
var series = _seriesRepository.Get(seriesId);
|
||||
_seriesRepository.Delete(seriesId);
|
||||
_eventAggregator.PublishEvent(new SeriesDeletedEvent(series, deleteFiles));
|
||||
}
|
||||
|
||||
public List<Series> GetAllSeries()
|
||||
{
|
||||
return _seriesRepository.All().ToList();
|
||||
}
|
||||
|
||||
public List<Series> AllForTag(int tagId)
|
||||
{
|
||||
return GetAllSeries().Where(s => s.Tags.Contains(tagId))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
||||
public Series UpdateSeries(Series series)
|
||||
{
|
||||
var storedSeries = GetSeries(series.Id);
|
||||
|
||||
foreach (var season in series.Seasons)
|
||||
{
|
||||
var storedSeason = storedSeries.Seasons.SingleOrDefault(s => s.SeasonNumber == season.SeasonNumber);
|
||||
|
||||
if (storedSeason != null && season.Monitored != storedSeason.Monitored)
|
||||
{
|
||||
_episodeService.SetEpisodeMonitoredBySeason(series.Id, season.SeasonNumber, season.Monitored);
|
||||
}
|
||||
}
|
||||
|
||||
var updatedSeries = _seriesRepository.Update(series);
|
||||
_eventAggregator.PublishEvent(new SeriesEditedEvent(updatedSeries, storedSeries));
|
||||
|
||||
return updatedSeries;
|
||||
}
|
||||
|
||||
public List<Series> UpdateSeries(List<Series> series)
|
||||
{
|
||||
_logger.Debug("Updating {0} series", series.Count);
|
||||
foreach (var s in series)
|
||||
{
|
||||
_logger.Trace("Updating: {0}", s.Title);
|
||||
if (!s.RootFolderPath.IsNullOrWhiteSpace())
|
||||
{
|
||||
var folderName = new DirectoryInfo(s.Path).Name;
|
||||
s.Path = Path.Combine(s.RootFolderPath, folderName);
|
||||
_logger.Trace("Changing path for {0} to {1}", s.Title, s.Path);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
_logger.Trace("Not changing path for: {0}", s.Title);
|
||||
}
|
||||
}
|
||||
|
||||
_seriesRepository.UpdateMany(series);
|
||||
_logger.Debug("{0} series updated", series.Count);
|
||||
|
||||
return series;
|
||||
}
|
||||
|
||||
public bool SeriesPathExists(string folder)
|
||||
{
|
||||
return _seriesRepository.SeriesPathExists(folder);
|
||||
}
|
||||
|
||||
public void RemoveAddOptions(Series series)
|
||||
{
|
||||
_seriesRepository.SetFields(series, s => s.AddOptions);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue