Remove Remaining TV Code

pull/110/head
Qstick 7 years ago
parent 81716762d8
commit a80360f6fd

@ -3,7 +3,7 @@ export const BACKUP = 'Backup';
export const CHECK_FOR_FINISHED_DOWNLOAD = 'CheckForFinishedDownload';
export const CLEAR_BLACKLIST = 'ClearBlacklist';
export const CLEAR_LOGS = 'ClearLog';
export const CUTOFF_UNMET_EPISODE_SEARCH = 'CutoffUnmetEpisodeSearch';
export const CUTOFF_UNMET_EPISODE_SEARCH = 'CutoffUnmetAlbumSearch';
export const DELETE_LOG_FILES = 'DeleteLogFiles';
export const DELETE_UPDATE_LOG_FILES = 'DeleteUpdateLogFiles';
export const DOWNLOADED_ALBUMS_SCAN = 'DownloadedAlbumsScan';

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.EnvironmentInfo;
@ -76,8 +76,8 @@ namespace NzbDrone.App.Test
[Test]
public void should_return_same_instance_of_singletons_by_different_same_interface()
{
var first = _container.ResolveAll<IHandle<EpisodeGrabbedEvent>>().OfType<DownloadMonitoringService>().Single();
var second = _container.ResolveAll<IHandle<EpisodeGrabbedEvent>>().OfType<DownloadMonitoringService>().Single();
var first = _container.ResolveAll<IHandle<AlbumGrabbedEvent>>().OfType<DownloadMonitoringService>().Single();
var second = _container.ResolveAll<IHandle<AlbumGrabbedEvent>>().OfType<DownloadMonitoringService>().Single();
first.Should().BeSameAs(second);
}
@ -85,10 +85,10 @@ namespace NzbDrone.App.Test
[Test]
public void should_return_same_instance_of_singletons_by_different_interfaces()
{
var first = _container.ResolveAll<IHandle<EpisodeGrabbedEvent>>().OfType<DownloadMonitoringService>().Single();
var first = _container.ResolveAll<IHandle<AlbumGrabbedEvent>>().OfType<DownloadMonitoringService>().Single();
var second = (DownloadMonitoringService)_container.Resolve<IExecute<CheckForFinishedDownloadCommand>>();
first.Should().BeSameAs(second);
}
}
}
}

@ -1,312 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.DataAugmentation.Xem;
using NzbDrone.Core.DataAugmentation.Xem.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Tv.Events;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.DataAugmentation.SceneNumbering
{
[TestFixture]
public class XemServiceFixture : CoreTest<XemService>
{
private Series _series;
private List<int> _theXemSeriesIds;
private List<XemSceneTvdbMapping> _theXemTvdbMappings;
private List<Episode> _episodes;
[SetUp]
public void SetUp()
{
_series = Builder<Series>.CreateNew()
.With(v => v.TvdbId = 10)
.With(v => v.UseSceneNumbering = false)
.BuildNew();
_theXemSeriesIds = new List<int> { 120 };
Mocker.GetMock<IXemProxy>()
.Setup(v => v.GetXemSeriesIds())
.Returns(_theXemSeriesIds);
_theXemTvdbMappings = new List<XemSceneTvdbMapping>();
Mocker.GetMock<IXemProxy>()
.Setup(v => v.GetSceneTvdbMappings(10))
.Returns(_theXemTvdbMappings);
_episodes = new List<Episode>();
_episodes.Add(new Episode { SeasonNumber = 1, EpisodeNumber = 1 });
_episodes.Add(new Episode { SeasonNumber = 1, EpisodeNumber = 2 });
_episodes.Add(new Episode { SeasonNumber = 2, EpisodeNumber = 1 });
_episodes.Add(new Episode { SeasonNumber = 2, EpisodeNumber = 2 });
_episodes.Add(new Episode { SeasonNumber = 2, EpisodeNumber = 3 });
_episodes.Add(new Episode { SeasonNumber = 2, EpisodeNumber = 4 });
_episodes.Add(new Episode { SeasonNumber = 2, EpisodeNumber = 5 });
_episodes.Add(new Episode { SeasonNumber = 3, EpisodeNumber = 1 });
_episodes.Add(new Episode { SeasonNumber = 3, EpisodeNumber = 2 });
Mocker.GetMock<IEpisodeService>()
.Setup(v => v.GetEpisodeBySeries(It.IsAny<int>()))
.Returns(_episodes);
}
private void GivenTvdbMappings()
{
_theXemSeriesIds.Add(10);
AddTvdbMapping(1, 1, 1, 1, 1, 1); // 1x01 -> 1x01
AddTvdbMapping(2, 1, 2, 2, 1, 2); // 1x02 -> 1x02
AddTvdbMapping(3, 2, 1, 3, 2, 1); // 2x01 -> 2x01
AddTvdbMapping(4, 2, 2, 4, 2, 2); // 2x02 -> 2x02
AddTvdbMapping(5, 2, 3, 5, 2, 3); // 2x03 -> 2x03
AddTvdbMapping(6, 3, 1, 6, 2, 4); // 3x01 -> 2x04
AddTvdbMapping(7, 3, 2, 7, 2, 5); // 3x02 -> 2x05
}
private void GivenExistingMapping()
{
_series.UseSceneNumbering = true;
_episodes[0].SceneSeasonNumber = 1;
_episodes[0].SceneEpisodeNumber = 1;
_episodes[1].SceneSeasonNumber = 1;
_episodes[1].SceneEpisodeNumber = 2;
_episodes[2].SceneSeasonNumber = 2;
_episodes[2].SceneEpisodeNumber = 1;
_episodes[3].SceneSeasonNumber = 2;
_episodes[3].SceneEpisodeNumber = 2;
_episodes[4].SceneSeasonNumber = 2;
_episodes[4].SceneEpisodeNumber = 3;
_episodes[5].SceneSeasonNumber = 3;
_episodes[5].SceneEpisodeNumber = 1;
_episodes[6].SceneSeasonNumber = 3;
_episodes[6].SceneEpisodeNumber = 1;
}
private void AddTvdbMapping(int sceneAbsolute, int sceneSeason, int sceneEpisode, int tvdbAbsolute, int tvdbSeason, int tvdbEpisode)
{
_theXemTvdbMappings.Add(new XemSceneTvdbMapping
{
Scene = new XemValues { Absolute = sceneAbsolute, Season = sceneSeason, Episode = sceneEpisode },
Tvdb = new XemValues { Absolute = tvdbAbsolute, Season = tvdbSeason, Episode = tvdbEpisode },
});
}
[Test]
public void should_not_fetch_scenenumbering_if_not_listed()
{
Subject.Handle(new SeriesUpdatedEvent(_series));
Mocker.GetMock<IXemProxy>()
.Verify(v => v.GetSceneTvdbMappings(10), Times.Never());
Mocker.GetMock<ISeriesService>()
.Verify(v => v.UpdateSeries(It.IsAny<Series>()), Times.Never());
}
[Test]
public void should_fetch_scenenumbering()
{
GivenTvdbMappings();
Subject.Handle(new SeriesUpdatedEvent(_series));
Mocker.GetMock<ISeriesService>()
.Verify(v => v.UpdateSeries(It.Is<Series>(s => s.UseSceneNumbering == true)), Times.Once());
}
[Test]
public void should_clear_scenenumbering_if_removed_from_thexem()
{
GivenExistingMapping();
Subject.Handle(new SeriesUpdatedEvent(_series));
Mocker.GetMock<ISeriesService>()
.Verify(v => v.UpdateSeries(It.IsAny<Series>()), Times.Once());
}
[Test]
public void should_not_clear_scenenumbering_if_no_results_at_all_from_thexem()
{
GivenExistingMapping();
_theXemSeriesIds.Clear();
Subject.Handle(new SeriesUpdatedEvent(_series));
Mocker.GetMock<ISeriesService>()
.Verify(v => v.UpdateSeries(It.IsAny<Series>()), Times.Never());
ExceptionVerification.ExpectedWarns(1);
}
[Test]
public void should_not_clear_scenenumbering_if_thexem_throws()
{
GivenExistingMapping();
Mocker.GetMock<IXemProxy>()
.Setup(v => v.GetXemSeriesIds())
.Throws(new InvalidOperationException());
Subject.Handle(new SeriesUpdatedEvent(_series));
Mocker.GetMock<ISeriesService>()
.Verify(v => v.UpdateSeries(It.IsAny<Series>()), Times.Never());
ExceptionVerification.ExpectedWarns(1);
}
[Test]
public void should_flag_unknown_future_episodes_if_existing_season_is_mapped()
{
GivenTvdbMappings();
_theXemTvdbMappings.RemoveAll(v => v.Tvdb.Season == 2 && v.Tvdb.Episode == 5);
Subject.Handle(new SeriesUpdatedEvent(_series));
var episode = _episodes.First(v => v.SeasonNumber == 2 && v.EpisodeNumber == 5);
episode.UnverifiedSceneNumbering.Should().BeTrue();
}
[Test]
public void should_flag_unknown_future_season_if_future_season_is_shifted()
{
GivenTvdbMappings();
Subject.Handle(new SeriesUpdatedEvent(_series));
var episode = _episodes.First(v => v.SeasonNumber == 3 && v.EpisodeNumber == 1);
episode.UnverifiedSceneNumbering.Should().BeTrue();
}
[Test]
public void should_not_flag_unknown_future_season_if_future_season_is_not_shifted()
{
GivenTvdbMappings();
_theXemTvdbMappings.RemoveAll(v => v.Scene.Season == 3);
Subject.Handle(new SeriesUpdatedEvent(_series));
var episode = _episodes.First(v => v.SeasonNumber == 3 && v.EpisodeNumber == 1);
episode.UnverifiedSceneNumbering.Should().BeFalse();
}
[Test]
public void should_not_flag_past_episodes_if_not_causing_overlaps()
{
GivenTvdbMappings();
_theXemTvdbMappings.RemoveAll(v => v.Scene.Season == 2);
Subject.Handle(new SeriesUpdatedEvent(_series));
var episode = _episodes.First(v => v.SeasonNumber == 2 && v.EpisodeNumber == 1);
episode.UnverifiedSceneNumbering.Should().BeFalse();
}
[Test]
public void should_flag_past_episodes_if_causing_overlap()
{
GivenTvdbMappings();
_theXemTvdbMappings.RemoveAll(v => v.Scene.Season == 2 && v.Tvdb.Episode <= 1);
_theXemTvdbMappings.First(v => v.Scene.Season == 2 && v.Scene.Episode == 2).Scene.Episode = 1;
Subject.Handle(new SeriesUpdatedEvent(_series));
var episode = _episodes.First(v => v.SeasonNumber == 2 && v.EpisodeNumber == 1);
episode.UnverifiedSceneNumbering.Should().BeTrue();
}
[Test]
public void should_not_extrapolate_season_with_specials()
{
GivenTvdbMappings();
var specialMapping = _theXemTvdbMappings.First(v => v.Tvdb.Season == 2 && v.Tvdb.Episode == 5);
specialMapping.Tvdb.Season = 0;
specialMapping.Tvdb.Episode = 1;
Subject.Handle(new SeriesUpdatedEvent(_series));
var episode = _episodes.First(v => v.SeasonNumber == 2 && v.EpisodeNumber == 5);
episode.UnverifiedSceneNumbering.Should().BeTrue();
episode.SceneSeasonNumber.Should().NotHaveValue();
episode.SceneEpisodeNumber.Should().NotHaveValue();
}
[Test]
public void should_extrapolate_season_with_future_episodes()
{
GivenTvdbMappings();
_theXemTvdbMappings.RemoveAll(v => v.Tvdb.Season == 2 && v.Tvdb.Episode == 5);
Subject.Handle(new SeriesUpdatedEvent(_series));
var episode = _episodes.First(v => v.SeasonNumber == 2 && v.EpisodeNumber == 5);
episode.UnverifiedSceneNumbering.Should().BeTrue();
episode.SceneSeasonNumber.Should().Be(3);
episode.SceneEpisodeNumber.Should().Be(2);
}
[Test]
public void should_extrapolate_season_with_shifted_episodes()
{
GivenTvdbMappings();
_theXemTvdbMappings.RemoveAll(v => v.Tvdb.Season == 2 && v.Tvdb.Episode == 5);
var dualMapping = _theXemTvdbMappings.First(v => v.Tvdb.Season == 2 && v.Tvdb.Episode == 4);
dualMapping.Scene.Season = 2;
dualMapping.Scene.Episode = 3;
Subject.Handle(new SeriesUpdatedEvent(_series));
var episode = _episodes.First(v => v.SeasonNumber == 2 && v.EpisodeNumber == 5);
episode.UnverifiedSceneNumbering.Should().BeTrue();
episode.SceneSeasonNumber.Should().Be(2);
episode.SceneEpisodeNumber.Should().Be(4);
}
[Test]
public void should_extrapolate_shifted_future_seasons()
{
GivenTvdbMappings();
Subject.Handle(new SeriesUpdatedEvent(_series));
var episode = _episodes.First(v => v.SeasonNumber == 3 && v.EpisodeNumber == 2);
episode.UnverifiedSceneNumbering.Should().BeTrue();
episode.SceneSeasonNumber.Should().Be(4);
episode.SceneEpisodeNumber.Should().Be(2);
}
[Test]
public void should_not_extrapolate_matching_future_seasons()
{
GivenTvdbMappings();
_theXemTvdbMappings.RemoveAll(v => v.Scene.Season != 1);
Subject.Handle(new SeriesUpdatedEvent(_series));
var episode = _episodes.First(v => v.SeasonNumber == 3 && v.EpisodeNumber == 2);
episode.UnverifiedSceneNumbering.Should().BeFalse();
episode.SceneSeasonNumber.Should().NotHaveValue();
episode.SceneEpisodeNumber.Should().NotHaveValue();
}
}
}

@ -1,76 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.DecisionEngineTests
{
[TestFixture]
public class SameEpisodesSpecificationFixture : CoreTest<SameEpisodesSpecification>
{
private List<Episode> _episodes;
[SetUp]
public void Setup()
{
_episodes = Builder<Episode>.CreateListOfSize(2)
.All()
.With(e => e.EpisodeFileId = 1)
.BuildList();
}
private void GivenEpisodesInFile(List<Episode> episodes)
{
Mocker.GetMock<IEpisodeService>()
.Setup(s => s.GetEpisodesByFileId(It.IsAny<int>()))
.Returns(episodes);
}
[Test]
public void should_not_upgrade_when_new_release_contains_less_episodes()
{
GivenEpisodesInFile(_episodes);
Subject.IsSatisfiedBy(new List<Episode> { _episodes.First() }).Should().BeFalse();
}
[Test]
public void should_upgrade_when_new_release_contains_more_episodes()
{
GivenEpisodesInFile(new List<Episode> { _episodes.First() });
Subject.IsSatisfiedBy(_episodes).Should().BeTrue();
}
[Test]
public void should_upgrade_when_new_release_contains_the_same_episodes()
{
GivenEpisodesInFile(_episodes);
Subject.IsSatisfiedBy(_episodes).Should().BeTrue();
}
[Test]
public void should_upgrade_when_release_contains_the_same_episodes_as_multiple_files()
{
var episodes = Builder<Episode>.CreateListOfSize(2)
.BuildList();
Mocker.GetMock<IEpisodeService>()
.Setup(s => s.GetEpisodesByFileId(episodes.First().EpisodeFileId))
.Returns(new List<Episode> { episodes.First() });
Mocker.GetMock<IEpisodeService>()
.Setup(s => s.GetEpisodesByFileId(episodes.Last().EpisodeFileId))
.Returns(new List<Episode> { episodes.Last() });
Subject.IsSatisfiedBy(episodes).Should().BeTrue();
}
}
}

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
@ -185,7 +185,7 @@ namespace NzbDrone.Core.Test.Download
Subject.DownloadReport(_parseResult);
Mocker.GetMock<IDownloadClient>().Verify(c => c.Download(It.IsAny<RemoteAlbum>()), Times.Never());
VerifyEventNotPublished<EpisodeGrabbedEvent>();
VerifyEventNotPublished<AlbumGrabbedEvent>();
ExceptionVerification.ExpectedWarns(1);
}

@ -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,27 +1,27 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.MetadataSource;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Music;
namespace NzbDrone.Core.Test.MetadataSource
{
[TestFixture]
public class SearchSeriesComparerFixture : CoreTest
public class SearchArtistComparerFixture : CoreTest
{
private List<Series> _series;
private List<Artist> _artist;
[SetUp]
public void Setup()
{
_series = new List<Series>();
_artist = new List<Artist>();
}
private void WithSeries(string title)
private void WithSeries(string name)
{
_series.Add(new Series { Title = title });
_artist.Add(new Artist { Name = name });
}
[Test]
@ -30,9 +30,9 @@ namespace NzbDrone.Core.Test.MetadataSource
WithSeries("Talking Dead");
WithSeries("The Walking Dead");
_series.Sort(new SearchSeriesComparer("the walking dead"));
_artist.Sort(new SearchArtistComparer("the walking dead"));
_series.First().Title.Should().Be("The Walking Dead");
_artist.First().Name.Should().Be("The Walking Dead");
}
[Test]
@ -41,9 +41,9 @@ namespace NzbDrone.Core.Test.MetadataSource
WithSeries("Talking Dead");
WithSeries("The Walking Dead");
_series.Sort(new SearchSeriesComparer("walking dead"));
_artist.Sort(new SearchArtistComparer("walking dead"));
_series.First().Title.Should().Be("The Walking Dead");
_artist.First().Name.Should().Be("The Walking Dead");
}
[Test]
@ -52,9 +52,9 @@ namespace NzbDrone.Core.Test.MetadataSource
WithSeries("The Blacklist");
WithSeries("Blacklist");
_series.Sort(new SearchSeriesComparer("blacklist"));
_artist.Sort(new SearchArtistComparer("blacklist"));
_series.First().Title.Should().Be("Blacklist");
_artist.First().Name.Should().Be("Blacklist");
}
[Test]
@ -63,9 +63,9 @@ namespace NzbDrone.Core.Test.MetadataSource
WithSeries("Blacklist");
WithSeries("The Blacklist");
_series.Sort(new SearchSeriesComparer("the blacklist"));
_artist.Sort(new SearchArtistComparer("the blacklist"));
_series.First().Title.Should().Be("The Blacklist");
_artist.First().Name.Should().Be("The Blacklist");
}
}
}

@ -104,7 +104,6 @@
<Compile Include="BulkImport\BulkImportFixture.cs" />
<Compile Include="Configuration\ConfigCachingFixture.cs" />
<Compile Include="Configuration\ConfigServiceFixture.cs" />
<Compile Include="DataAugmentation\SceneNumbering\XemServiceFixture.cs" />
<Compile Include="DataAugmentation\Scene\SceneMappingProxyFixture.cs" />
<Compile Include="DataAugmentation\Scene\SceneMappingServiceFixture.cs" />
<Compile Include="DataAugmentation\DailySeries\DailySeriesDataProxyFixture.cs" />
@ -155,7 +154,6 @@
<Compile Include="DecisionEngineTests\RssSync\DeletedTrackFileSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\RssSync\ProperSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\Search\ArtistSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\SameEpisodesSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\RawDiskSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\UpgradeDiskSpecificationFixture.cs" />
<Compile Include="DiskSpace\DiskSpaceServiceFixture.cs" />
@ -274,7 +272,6 @@
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatterTests\FormatAudioCodecFixture.cs" />
<Compile Include="MediaFiles\TrackFileMovingServiceTests\MoveTrackFileFixture.cs" />
<Compile Include="MediaFiles\TrackImport\ImportDecisionMakerFixture.cs" />
<Compile Include="MediaFiles\TrackImport\SampleServiceFixture.cs" />
<Compile Include="MediaFiles\TrackImport\Specifications\FreeSpaceSpecificationFixture.cs" />
<Compile Include="MediaFiles\TrackImport\Specifications\NotUnpackingSpecificationFixture.cs" />
<Compile Include="MediaFiles\TrackImport\Specifications\UpgradeSpecificationFixture.cs" />
@ -282,7 +279,7 @@
<Compile Include="MediaFiles\MediaFileRepositoryFixture.cs" />
<Compile Include="Messaging\Commands\CommandQueueManagerFixture.cs" />
<Compile Include="MetadataSource\SkyHook\SkyHookProxySearchFixture.cs" />
<Compile Include="MetadataSource\SearchSeriesComparerFixture.cs" />
<Compile Include="MetadataSource\SearchArtistComparerFixture.cs" />
<Compile Include="MetadataSource\SkyHook\SkyHookProxyFixture.cs" />
<Compile Include="MusicTests\AddArtistFixture.cs" />
<Compile Include="MusicTests\ArtistNameSlugValidatorFixture.cs" />
@ -290,7 +287,6 @@
<Compile Include="NotificationTests\SynologyIndexerFixture.cs" />
<Compile Include="OrganizerTests\FileNameBuilderTests\CleanTitleFixture.cs" />
<Compile Include="OrganizerTests\FileNameBuilderTests\TitleTheFixture.cs" />
<Compile Include="ParserTests\MiniSeriesEpisodeParserFixture.cs" />
<Compile Include="ParserTests\MusicParserFixture.cs" />
<Compile Include="Qualities\RevisionComparableFixture.cs" />
<Compile Include="QueueTests\QueueServiceFixture.cs" />
@ -320,15 +316,10 @@
<Compile Include="OrganizerTests\GetAlbumFolderFixture.cs" />
<Compile Include="OrganizerTests\FileNameBuilderTests\FileNameBuilderFixture.cs" />
<Compile Include="OrganizerTests\GetArtistFolderFixture.cs" />
<Compile Include="ParserTests\AbsoluteEpisodeNumberParserFixture.cs" />
<Compile Include="ParserTests\AnimeMetadataParserFixture.cs" />
<Compile Include="ParserTests\ExtendedQualityParserRegex.cs" />
<Compile Include="ParserTests\CrapParserFixture.cs" />
<Compile Include="ParserTests\DailyEpisodeParserFixture.cs" />
<Compile Include="ParserTests\HashedReleaseFixture.cs" />
<Compile Include="ParserTests\IsPossibleSpecialEpisodeFixture.cs" />
<Compile Include="ParserTests\LanguageParserFixture.cs" />
<Compile Include="ParserTests\MultiEpisodeParserFixture.cs" />
<Compile Include="ParserTests\NormalizeTitleFixture.cs" />
<Compile Include="ParserTests\ParserFixture.cs" />
<Compile Include="ParserTests\ParsingServiceTests\GetArtistFixture.cs" />
@ -336,13 +327,12 @@
<Compile Include="ParserTests\QualityParserFixture.cs" />
<Compile Include="ParserTests\ReleaseGroupParserFixture.cs" />
<Compile Include="ParserTests\SeasonParserFixture.cs" />
<Compile Include="ParserTests\SeriesTitleInfoFixture.cs" />
<Compile Include="ParserTests\ArtistTitleInfoFixture.cs" />
<Compile Include="ParserTests\SingleEpisodeParserFixture.cs" />
<Compile Include="ParserTests\SceneCheckerFixture.cs" />
<Compile Include="Profiles\ProfileRepositoryFixture.cs" />
<Compile Include="Profiles\ProfileServiceFixture.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Providers\XemProxyFixture.cs" />
<Compile Include="ProviderTests\DiskProviderTests\ArchiveProviderFixture.cs" />
<Compile Include="ProviderTests\DiskScanProviderTests\GetAudioFilesFixture.cs" />
<Compile Include="ProviderTests\RecycleBinProviderTests\CleanupFixture.cs" />
@ -355,23 +345,8 @@
<Compile Include="RootFolderTests\RootFolderServiceFixture.cs" />
<Compile Include="ThingiProvider\ProviderBaseFixture.cs" />
<Compile Include="ThingiProviderTests\NullConfigFixture.cs" />
<Compile Include="TvTests\EpisodeServiceTests\FindEpisodeByTitleFixture.cs" />
<Compile Include="TvTests\EpisodeServiceTests\HandleEpisodeFileDeletedFixture.cs" />
<Compile Include="TvTests\EpisodeRepositoryTests\ByAirDateFixture.cs" />
<Compile Include="TvTests\EpisodeRepositoryTests\EpisodesBetweenDatesFixture.cs" />
<Compile Include="TvTests\EpisodeRepositoryTests\EpisodesRepositoryReadFixture.cs" />
<Compile Include="TvTests\EpisodeRepositoryTests\EpisodesWhereCutoffUnmetFixture.cs" />
<Compile Include="TvTests\EpisodeRepositoryTests\EpisodesWithFilesFixture.cs" />
<Compile Include="TvTests\EpisodeRepositoryTests\EpisodesWithoutFilesFixture.cs" />
<Compile Include="TvTests\EpisodeRepositoryTests\FindEpisodeFixture.cs" />
<Compile Include="MusicTests\MoveArtistServiceFixture.cs" />
<Compile Include="TvTests\RefreshEpisodeServiceFixture.cs" />
<Compile Include="MusicTests\RefreshArtistServiceFixture.cs" />
<Compile Include="TvTests\EpisodeMonitoredServiceTests\SetEpisodeMontitoredFixture.cs" />
<Compile Include="TvTests\SeriesRepositoryTests\SeriesRepositoryFixture.cs" />
<Compile Include="TvTests\SeriesServiceTests\UpdateMultipleSeriesFixture.cs" />
<Compile Include="TvTests\SeriesServiceTests\UpdateSeriesFixture.cs" />
<Compile Include="TvTests\SeriesTitleNormalizerFixture.cs" />
<Compile Include="MusicTests\ShouldRefreshArtistFixture.cs" />
<Compile Include="UpdateTests\UpdatePackageProviderFixture.cs" />
<Compile Include="UpdateTests\UpdateServiceFixture.cs" />
@ -563,6 +538,7 @@
</ItemGroup>
<ItemGroup>
<Folder Include="InstrumentationTests\" />
<Folder Include="Providers\" />
<Folder Include="ProviderTests\UpdateProviderTests\" />
</ItemGroup>
<ItemGroup>

@ -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,18 +1,18 @@
using FluentAssertions;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ParserTests
{
[TestFixture]
public class SeriesTitleInfoFixture : CoreTest
public class ArtistTitleInfoFixture : CoreTest
{
[Test]
public void should_have_year_zero_when_title_doesnt_have_a_year()
{
const string title = "House.S01E01.pilot.720p.hdtv";
var result = Parser.Parser.ParseTitle(title).SeriesTitleInfo;
var result = Parser.Parser.ParseAlbumTitle(title).ArtistTitleInfo;
result.Year.Should().Be(0);
}
@ -22,7 +22,7 @@ namespace NzbDrone.Core.Test.ParserTests
{
const string title = "House.S01E01.pilot.720p.hdtv";
var result = Parser.Parser.ParseTitle(title).SeriesTitleInfo;
var result = Parser.Parser.ParseAlbumTitle(title).ArtistTitleInfo;
result.Title.Should().Be(result.TitleWithoutYear);
}
@ -32,7 +32,7 @@ namespace NzbDrone.Core.Test.ParserTests
{
const string title = "House.2004.S01E01.pilot.720p.hdtv";
var result = Parser.Parser.ParseTitle(title).SeriesTitleInfo;
var result = Parser.Parser.ParseAlbumTitle(title).ArtistTitleInfo;
result.Year.Should().Be(2004);
}
@ -42,7 +42,7 @@ namespace NzbDrone.Core.Test.ParserTests
{
const string title = "House.2004.S01E01.pilot.720p.hdtv";
var result = Parser.Parser.ParseTitle(title).SeriesTitleInfo;
var result = Parser.Parser.ParseAlbumTitle(title).ArtistTitleInfo;
result.Title.Should().Be("House 2004");
}
@ -52,7 +52,7 @@ namespace NzbDrone.Core.Test.ParserTests
{
const string title = "House.2004.S01E01.pilot.720p.hdtv";
var result = Parser.Parser.ParseTitle(title).SeriesTitleInfo;
var result = Parser.Parser.ParseAlbumTitle(title).ArtistTitleInfo;
result.TitleWithoutYear.Should().Be("House");
}

@ -33,7 +33,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("QZC4HDl7ncmzyUj9amucWe1ddKU1oFMZDd8r0dEDUsTd")]
public void should_not_parse_crap(string title)
{
Parser.Parser.ParseTitle(title).Should().BeNull();
Parser.Parser.ParseAlbumTitle(title).Should().BeNull();
ExceptionVerification.IgnoreWarns();
}
@ -52,7 +52,7 @@ namespace NzbDrone.Core.Test.ParserTests
hash = BitConverter.ToString(hashData).Replace("-", "");
if (Parser.Parser.ParseTitle(hash) == null)
if (Parser.Parser.ParseAlbumTitle(hash) == null)
success++;
}
@ -78,7 +78,7 @@ namespace NzbDrone.Core.Test.ParserTests
hash.Append(charset[hashAlgo.Next() % charset.Length]);
}
if (Parser.Parser.ParseTitle(hash.ToString()) == null)
if (Parser.Parser.ParseAlbumTitle(hash.ToString()) == null)
success++;
}
@ -88,7 +88,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("thebiggestloser1618finale")]
public void should_not_parse_file_name_without_proper_spacing(string fileName)
{
Parser.Parser.ParseTitle(fileName).Should().BeNull();
Parser.Parser.ParseAlbumTitle(fileName).Should().BeNull();
}
}
}

@ -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,4 +1,4 @@
using FluentAssertions;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
@ -86,7 +86,7 @@ namespace NzbDrone.Core.Test.ParserTests
[Test, TestCaseSource(nameof(HashedReleaseParserCases))]
public void should_properly_parse_hashed_releases(string path, string title, Quality quality, string releaseGroup)
{
var result = Parser.Parser.ParsePath(path);
var result = Parser.Parser.ParseMusicPath(path);
//result.SeriesTitle.Should().Be(title);
result.Quality.Quality.Should().Be(quality);
}

@ -1,43 +0,0 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Test.ParserTests
{
[TestFixture]
public class IsPossibleSpecialEpisodeFixture
{
[Test]
public void should_not_treat_files_without_a_series_title_as_a_special()
{
var parsedEpisodeInfo = new ParsedEpisodeInfo
{
EpisodeNumbers = new[]{ 7 },
SeasonNumber = 1,
SeriesTitle = ""
};
parsedEpisodeInfo.IsPossibleSpecialEpisode.Should().BeFalse();
}
[Test]
public void should_return_true_when_episode_numbers_is_empty()
{
var parsedEpisodeInfo = new ParsedEpisodeInfo
{
SeasonNumber = 1,
SeriesTitle = ""
};
parsedEpisodeInfo.IsPossibleSpecialEpisode.Should().BeTrue();
}
[TestCase("Under.the.Dome.S02.Special-Inside.Chesters.Mill.HDTV.x264-BAJSKORV")]
[TestCase("Under.the.Dome.S02.Special-Inside.Chesters.Mill.720p.HDTV.x264-BAJSKORV")]
[TestCase("Rookie.Blue.Behind.the.Badge.S05.Special.HDTV.x264-2HD")]
public void IsPossibleSpecialEpisode_should_be_true(string title)
{
Parser.Parser.ParseTitle(title).IsPossibleSpecialEpisode.Should().BeTrue();
}
}
}

@ -37,14 +37,14 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Shield,.The.1x13.Tueurs.De.Flics.FR.DVDRip.XviD")]
public void should_parse_language_french(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.French.Id);
}
[TestCase("Castle.2009.S01E14.Spanish.HDTV.XviD-LOL")]
public void should_parse_language_spanish(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Spanish.Id);
}
@ -53,7 +53,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Elementary - S02E16 - Kampfhaehne - mkv - by Videomann")]
public void should_parse_language_german(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.German.Id);
}
@ -61,14 +61,14 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("person.of.interest.1x19.ita.720p.bdmux.x264-novarip")]
public void should_parse_language_italian(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Italian.Id);
}
[TestCase("Castle.2009.S01E14.Danish.HDTV.XviD-LOL")]
public void should_parse_language_danish(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Danish.Id);
}
@ -77,35 +77,35 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Ray Donovan - S01E01.720p.HDtv.x264-Evolve (NLsub)")]
public void should_parse_language_dutch(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Dutch.Id);
}
[TestCase("Castle.2009.S01E14.Japanese.HDTV.XviD-LOL")]
public void should_parse_language_japanese(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Japanese.Id);
}
[TestCase("Castle.2009.S01E14.Cantonese.HDTV.XviD-LOL")]
public void should_parse_language_cantonese(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Cantonese.Id);
}
[TestCase("Castle.2009.S01E14.Mandarin.HDTV.XviD-LOL")]
public void should_parse_language_mandarin(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Mandarin.Id);
}
[TestCase("Castle.2009.S01E14.Korean.HDTV.XviD-LOL")]
public void should_parse_language_korean(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Korean.Id);
}
@ -113,28 +113,28 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("True.Detective.S01E01.1080p.WEB-DL.Rus.Eng.TVKlondike")]
public void should_parse_language_russian(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Russian.Id);
}
[TestCase("Castle.2009.S01E14.Polish.HDTV.XviD-LOL")]
public void should_parse_language_polish(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Polish.Id);
}
[TestCase("Castle.2009.S01E14.Vietnamese.HDTV.XviD-LOL")]
public void should_parse_language_vietnamese(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Vietnamese.Id);
}
[TestCase("Castle.2009.S01E14.Swedish.HDTV.XviD-LOL")]
public void should_parse_language_swedish(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Swedish.Id);
}
@ -142,42 +142,42 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Revolution S01E03 No Quarter 2012 WEB-DL 720p Nordic-philipo mkv")]
public void should_parse_language_norwegian(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Norwegian.Id);
}
[TestCase("Castle.2009.S01E14.Finnish.HDTV.XviD-LOL")]
public void should_parse_language_finnish(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Finnish.Id);
}
[TestCase("Castle.2009.S01E14.Turkish.HDTV.XviD-LOL")]
public void should_parse_language_turkish(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Turkish.Id);
}
[TestCase("Castle.2009.S01E14.Portuguese.HDTV.XviD-LOL")]
public void should_parse_language_portuguese(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Portuguese.Id);
}
[TestCase("Salamander.S01E01.FLEMISH.HDTV.x264-BRiGAND")]
public void should_parse_language_flemish(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Flemish.Id);
}
[TestCase("H.Polukatoikia.S03E13.Greek.PDTV.XviD-Ouzo")]
public void should_parse_language_greek(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Greek.Id);
}
@ -186,14 +186,14 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Castle.2009.S01E14.HDTV.XviD.HUN-LOL")]
public void should_parse_language_hungarian(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Hungarian.Id);
}
[TestCase("Avatar.The.Last.Airbender.S01-03.DVDRip.HebDub")]
public void should_parse_language_hebrew(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Hebrew.Id);
}
@ -201,7 +201,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Prison.Break.S05E01.WEBRip.x264.AC3.LT.EN-CNN")]
public void should_parse_language_lithuanian(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Lithuanian.Id);
}
@ -209,7 +209,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("The.Walking.Dead.S07E11.WEB Rip.XviD.Louige-CZ.EN.5.1")]
public void should_parse_language_czech(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Language.Id.Should().Be(Language.Czech.Id);
}
}

@ -1,30 +0,0 @@
using System.Linq;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ParserTests
{
[TestFixture]
public class MiniSeriesEpisodeParserFixture : CoreTest
{
[TestCase("The.Kennedys.Part.2.DSR.XviD-SYS", "The Kennedys", 2)]
[TestCase("the-pacific-e07-720p", "the-pacific", 7)]
[TestCase("Hatfields and McCoys 2012 Part 1 REPACK 720p HDTV x264 2HD", "Hatfields and McCoys 2012", 1)]
//[TestCase("Band.Of.Brothers.EP02.Day.Of.Days.DVDRiP.XviD-DEiTY", "Band.Of.Brothers", 2)]
//[TestCase("", "", 0, 0)]
[TestCase("Mars.2016.E04.Power.720p.WEB-DL.DD5.1.H.264-MARS", "Mars 2016", 4)]
public void should_parse_mini_series_episode(string postTitle, string title, int episodeNumber)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Should().NotBeNull();
result.EpisodeNumbers.Should().HaveCount(1);
result.SeasonNumber.Should().Be(1);
result.EpisodeNumbers.First().Should().Be(episodeNumber);
result.SeriesTitle.Should().Be(title);
result.AbsoluteEpisodeNumbers.Should().BeEmpty();
result.FullSeason.Should().BeFalse();
}
}
}

@ -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();
}
}
}

@ -40,7 +40,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Reno.911.S01.DVDRip.DD2.0.x264-DEEP", "Reno 911")]
public void should_parse_series_name(string postTitle, string title)
{
var result = Parser.Parser.ParseSeriesName(postTitle).CleanSeriesTitle();
var result = Parser.Parser.ParseArtistName(postTitle).CleanSeriesTitle();
result.Should().Be(title.CleanSeriesTitle());
}
@ -55,21 +55,21 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Discovery TV - Gold Rush : 02 Road From Hell [S04].mp4")]
public void should_clean_up_invalid_path_characters(string postTitle)
{
Parser.Parser.ParseTitle(postTitle);
Parser.Parser.ParseAlbumTitle(postTitle);
}
[TestCase("[scnzbefnet][509103] 2.Broke.Girls.S03E18.720p.HDTV.X264-DIMENSION", "2 Broke Girls")]
public void should_remove_request_info_from_title(string postTitle, string title)
{
Parser.Parser.ParseTitle(postTitle).SeriesTitle.Should().Be(title);
Parser.Parser.ParseAlbumTitle(postTitle).ArtistName.Should().Be(title);
}
[TestCase("Revolution.S01E02.Chained.Heat.mkv")]
[TestCase("Dexter - S01E01 - Title.avi")]
public void should_parse_quality_from_extension(string title)
{
Parser.Parser.ParseTitle(title).Quality.Quality.Should().NotBe(Quality.Unknown);
Parser.Parser.ParseTitle(title).Quality.QualitySource.Should().Be(QualitySource.Extension);
Parser.Parser.ParseAlbumTitle(title).Quality.Quality.Should().NotBe(Quality.Unknown);
Parser.Parser.ParseAlbumTitle(title).Quality.QualitySource.Should().Be(QualitySource.Extension);
}
[TestCase("VA - The Best 101 Love Ballads (2017) MP3 [192 kbps]", "The Best 101 Love Ballads")]

@ -33,12 +33,12 @@ namespace NzbDrone.Core.Test.ParserTests
// [TestCase(@"C:\CSI.NY.S02E04.720p.WEB-DL.DD5.1.H.264\73696S02-04.mkv", 2, 4)] //Gets treated as S01E04 (because it gets parsed as anime)
public void should_parse_from_path(string path, int season, int episode)
{
var result = Parser.Parser.ParsePath(path.AsOsAgnostic());
result.EpisodeNumbers.Should().HaveCount(1);
result.SeasonNumber.Should().Be(season);
result.EpisodeNumbers[0].Should().Be(episode);
result.AbsoluteEpisodeNumbers.Should().BeEmpty();
result.FullSeason.Should().BeFalse();
var result = Parser.Parser.ParseMusicPath(path.AsOsAgnostic());
//result.EpisodeNumbers.Should().HaveCount(1);
//result.SeasonNumber.Should().Be(season);
//result.EpisodeNumbers[0].Should().Be(episode);
//result.AbsoluteEpisodeNumbers.Should().BeEmpty();
//result.FullSeason.Should().BeFalse();
ExceptionVerification.IgnoreWarns();
}

@ -37,7 +37,7 @@ namespace NzbDrone.Core.Test.ParserTests
{
const string path = @"C:\Test\Doctor.Who.2005.s01e01.internal.bdrip.x264-archivist.mkv";
Parser.Parser.ParsePath(path).ReleaseGroup.Should().Be("archivist");
Parser.Parser.ParseMusicPath(path).ReleaseGroup.Should().Be("archivist");
}
[TestCase("Marvels.Daredevil.S02E04.720p.WEBRip.x264-SKGTV English", "SKGTV")]

@ -26,12 +26,12 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("My.Series.S2014.720p.HDTV.x264-ME", "My Series", 2014)]
public void should_parse_full_season_release(string postTitle, string title, int season)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.SeasonNumber.Should().Be(season);
result.SeriesTitle.Should().Be(title);
result.EpisodeNumbers.Should().BeEmpty();
result.AbsoluteEpisodeNumbers.Should().BeEmpty();
result.FullSeason.Should().BeTrue();
var result = Parser.Parser.ParseAlbumTitle(postTitle);
//result.SeasonNumber.Should().Be(season);
//result.SeriesTitle.Should().Be(title);
//result.EpisodeNumbers.Should().BeEmpty();
//result.AbsoluteEpisodeNumbers.Should().BeEmpty();
//result.FullSeason.Should().BeTrue();
}
[TestCase("Acropolis Now S05 EXTRAS DVDRip XviD RUNNER")]
@ -39,7 +39,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Instant Star S03 EXTRAS DVDRip XviD OSiTV")]
public void should_parse_season_extras(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Should().BeNull();
}
@ -49,7 +49,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("CSI.S11.SUBPACK.DVDRip.XviD-REWARD")]
public void should_parse_season_subpack(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Should().BeNull();
}

@ -131,14 +131,14 @@ namespace NzbDrone.Core.Test.ParserTests
//[TestCase("", "", 0, 0)]
public void should_parse_single_episode(string postTitle, string title, int seasonNumber, int episodeNumber)
{
var result = Parser.Parser.ParseTitle(postTitle);
var result = Parser.Parser.ParseAlbumTitle(postTitle);
result.Should().NotBeNull();
result.EpisodeNumbers.Should().HaveCount(1);
result.SeasonNumber.Should().Be(seasonNumber);
result.EpisodeNumbers.First().Should().Be(episodeNumber);
result.SeriesTitle.Should().Be(title);
result.AbsoluteEpisodeNumbers.Should().BeEmpty();
result.FullSeason.Should().BeFalse();
//result.EpisodeNumbers.Should().HaveCount(1);
//result.SeasonNumber.Should().Be(seasonNumber);
//result.EpisodeNumbers.First().Should().Be(episodeNumber);
//result.SeriesTitle.Should().Be(title);
//result.AbsoluteEpisodeNumbers.Should().BeEmpty();
//result.FullSeason.Should().BeFalse();
}
}
}

@ -1,54 +0,0 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.DataAugmentation.Xem;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common.Categories;
namespace NzbDrone.Core.Test.Providers
{
[TestFixture]
[IntegrationTest]
public class XemProxyFixture : CoreTest<XemProxy>
{
[SetUp]
public void Setup()
{
UseRealHttp();
}
[Test]
public void get_series_ids()
{
var ids = Subject.GetXemSeriesIds();
ids.Should().NotBeEmpty();
ids.Should().Contain(i => i == 73141);
}
[TestCase(12345, Description = "invalid id")]
[TestCase(279042, Description = "no single connection")]
public void should_return_empty_when_known_error(int id)
{
Subject.GetSceneTvdbMappings(id).Should().BeEmpty();
}
[TestCase(82807)]
[TestCase(73141, Description = "American Dad!")]
public void should_get_mapping(int seriesId)
{
var result = Subject.GetSceneTvdbMappings(seriesId);
result.Should().NotBeEmpty();
result.Should().OnlyContain(c => c.Scene != null);
result.Should().OnlyContain(c => c.Tvdb != null);
}
[TestCase(78916)]
public void should_filter_out_episodes_without_scene_mapping(int seriesId)
{
var result = Subject.GetSceneTvdbMappings(seriesId);
result.Should().NotContain(c => c.Tvdb == null);
}
}
}

@ -1,221 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.TvTests.EpisodeMonitoredServiceTests
{
[TestFixture]
public class SetEpisodeMontitoredFixture : CoreTest<EpisodeMonitoredService>
{
private Series _series;
private List<Episode> _episodes;
[SetUp]
public void Setup()
{
var seasons = 4;
_series = Builder<Series>.CreateNew()
.With(s => s.Seasons = Builder<Season>.CreateListOfSize(seasons)
.All()
.With(n => n.Monitored = true)
.Build()
.ToList())
.Build();
_episodes = Builder<Episode>.CreateListOfSize(seasons)
.All()
.With(e => e.Monitored = true)
.With(e => e.AirDateUtc = DateTime.UtcNow.AddDays(-7))
//Missing
.TheFirst(1)
.With(e => e.EpisodeFileId = 0)
//Has File
.TheNext(1)
.With(e => e.EpisodeFileId = 1)
//Future
.TheNext(1)
.With(e => e.EpisodeFileId = 0)
.With(e => e.AirDateUtc = DateTime.UtcNow.AddDays(7))
//Future/TBA
.TheNext(1)
.With(e => e.EpisodeFileId = 0)
.With(e => e.AirDateUtc = null)
.Build()
.ToList();
Mocker.GetMock<IEpisodeService>()
.Setup(s => s.GetEpisodeBySeries(It.IsAny<int>()))
.Returns(_episodes);
}
private void GivenSpecials()
{
foreach (var episode in _episodes)
{
episode.SeasonNumber = 0;
}
_series.Seasons = new List<Season>{new Season { Monitored = false, SeasonNumber = 0 }};
}
[Test]
public void should_be_able_to_monitor_series_without_changing_episodes()
{
Subject.SetEpisodeMonitoredStatus(_series, null);
Mocker.GetMock<ISeriesService>()
.Verify(v => v.UpdateSeries(It.IsAny<Series>()), Times.Once());
Mocker.GetMock<IEpisodeService>()
.Verify(v => v.UpdateEpisodes(It.IsAny<List<Episode>>()), Times.Never());
}
[Test]
public void should_be_able_to_monitor_all_episodes()
{
Subject.SetEpisodeMonitoredStatus(_series, new MonitoringOptions());
Mocker.GetMock<IEpisodeService>()
.Verify(v => v.UpdateEpisodes(It.Is<List<Episode>>(l => l.All(e => e.Monitored))));
}
[Test]
public void should_be_able_to_monitor_missing_episodes_only()
{
var monitoringOptions = new MonitoringOptions
{
IgnoreEpisodesWithFiles = true,
IgnoreEpisodesWithoutFiles = false
};
Subject.SetEpisodeMonitoredStatus(_series, monitoringOptions);
VerifyMonitored(e => !e.HasFile);
VerifyNotMonitored(e => e.HasFile);
}
[Test]
public void should_be_able_to_monitor_new_episodes_only()
{
var monitoringOptions = new MonitoringOptions
{
IgnoreEpisodesWithFiles = true,
IgnoreEpisodesWithoutFiles = true
};
Subject.SetEpisodeMonitoredStatus(_series, monitoringOptions);
VerifyMonitored(e => e.AirDateUtc.HasValue && e.AirDateUtc.Value.After(DateTime.UtcNow));
VerifyMonitored(e => !e.AirDateUtc.HasValue);
VerifyNotMonitored(e => e.AirDateUtc.HasValue && e.AirDateUtc.Value.Before(DateTime.UtcNow));
}
[Test]
public void should_not_monitor_missing_specials()
{
GivenSpecials();
var monitoringOptions = new MonitoringOptions
{
IgnoreEpisodesWithFiles = true,
IgnoreEpisodesWithoutFiles = false
};
Subject.SetEpisodeMonitoredStatus(_series, monitoringOptions);
VerifyNotMonitored(e => e.SeasonNumber == 0);
}
[Test]
public void should_not_monitor_new_specials()
{
GivenSpecials();
var monitoringOptions = new MonitoringOptions
{
IgnoreEpisodesWithFiles = true,
IgnoreEpisodesWithoutFiles = true
};
Subject.SetEpisodeMonitoredStatus(_series, monitoringOptions);
VerifyNotMonitored(e => e.SeasonNumber == 0);
}
[Test]
public void should_not_monitor_season_when_all_episodes_are_monitored_except_latest_season()
{
_series.Seasons = Builder<Season>.CreateListOfSize(2)
.All()
.With(n => n.Monitored = true)
.Build()
.ToList();
_episodes = Builder<Episode>.CreateListOfSize(5)
.All()
.With(e => e.SeasonNumber = 1)
.With(e => e.EpisodeFileId = 0)
.With(e => e.AirDateUtc = DateTime.UtcNow.AddDays(-5))
.TheLast(1)
.With(e => e.SeasonNumber = 2)
.Build()
.ToList();
Mocker.GetMock<IEpisodeService>()
.Setup(s => s.GetEpisodeBySeries(It.IsAny<int>()))
.Returns(_episodes);
var monitoringOptions = new MonitoringOptions
{
IgnoreEpisodesWithoutFiles = true
};
Subject.SetEpisodeMonitoredStatus(_series, monitoringOptions);
VerifySeasonMonitored(n => n.SeasonNumber == 2);
VerifySeasonNotMonitored(n => n.SeasonNumber == 1);
}
[Test]
public void should_ignore_episodes_when_season_is_not_monitored()
{
_series.Seasons.ForEach(s => s.Monitored = false);
Subject.SetEpisodeMonitoredStatus(_series, new MonitoringOptions());
Mocker.GetMock<IEpisodeService>()
.Verify(v => v.UpdateEpisodes(It.Is<List<Episode>>(l => l.All(e => !e.Monitored))));
}
private void VerifyMonitored(Func<Episode, bool> predicate)
{
Mocker.GetMock<IEpisodeService>()
.Verify(v => v.UpdateEpisodes(It.Is<List<Episode>>(l => l.Where(predicate).All(e => e.Monitored))));
}
private void VerifyNotMonitored(Func<Episode, bool> predicate)
{
Mocker.GetMock<IEpisodeService>()
.Verify(v => v.UpdateEpisodes(It.Is<List<Episode>>(l => l.Where(predicate).All(e => !e.Monitored))));
}
private void VerifySeasonMonitored(Func<Season, bool> predicate)
{
Mocker.GetMock<ISeriesService>()
.Verify(v => v.UpdateSeries(It.Is<Series>(s => s.Seasons.Where(predicate).All(n => n.Monitored))));
}
private void VerifySeasonNotMonitored(Func<Season, bool> predicate)
{
Mocker.GetMock<ISeriesService>()
.Verify(v => v.UpdateSeries(It.Is<Series>(s => s.Seasons.Where(predicate).All(n => !n.Monitored))));
}
}
}

@ -1,71 +0,0 @@
using System;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
{
[TestFixture]
public class ByAirDateFixture : DbTest<EpisodeRepository, Episode>
{
private const int SERIES_ID = 1;
private const string AIR_DATE = "2014-04-02";
private void GivenEpisode(int seasonNumber)
{
var episode = Builder<Episode>.CreateNew()
.With(e => e.SeriesId = 1)
.With(e => e.SeasonNumber = seasonNumber)
.With(e => e.AirDate = AIR_DATE)
.BuildNew();
Db.Insert(episode);
}
[Test]
public void should_throw_when_multiple_regular_episodes_are_found()
{
GivenEpisode(1);
GivenEpisode(2);
Assert.Throws<InvalidOperationException>(() => Subject.Get(SERIES_ID, AIR_DATE));
Assert.Throws<InvalidOperationException>(() => Subject.Find(SERIES_ID, AIR_DATE));
}
[Test]
public void should_throw_when_get_finds_no_episode()
{
Assert.Throws<InvalidOperationException>(() => Subject.Get(SERIES_ID, AIR_DATE));
}
[Test]
public void should_get_episode_when_single_episode_exists_for_air_date()
{
GivenEpisode(1);
Subject.Get(SERIES_ID, AIR_DATE).Should().NotBeNull();
Subject.Find(SERIES_ID, AIR_DATE).Should().NotBeNull();
}
[Test]
public void should_get_episode_when_regular_episode_and_special_share_the_same_air_date()
{
GivenEpisode(1);
GivenEpisode(0);
Subject.Get(SERIES_ID, AIR_DATE).Should().NotBeNull();
Subject.Find(SERIES_ID, AIR_DATE).Should().NotBeNull();
}
[Test]
public void should_get_special_when_its_the_only_episode_for_the_date_provided()
{
GivenEpisode(0);
Subject.Get(SERIES_ID, AIR_DATE).Should().NotBeNull();
Subject.Find(SERIES_ID, AIR_DATE).Should().NotBeNull();
}
}
}

@ -1,40 +0,0 @@
using System;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
{
[TestFixture]
public class EpisodesBetweenDatesFixture : DbTest<EpisodeRepository, Episode>
{
[SetUp]
public void Setup()
{
var series = Builder<Series>.CreateNew()
.With(s => s.Id = 0)
.With(s => s.Runtime = 30)
.With(s => s.Monitored = true)
.Build();
series.Id = Db.Insert(series).Id;
var episode = Builder<Episode>.CreateNew()
.With(e => e.Id = 0)
.With(e => e.SeriesId = series.Id)
.With(e => e.Monitored = true)
.Build();
Db.Insert(episode);
}
[Test]
public void should_get_episodes()
{
var episodes = Subject.EpisodesBetweenDates(DateTime.Today.AddDays(-1), DateTime.Today.AddDays(3), false);
episodes.Should().HaveCount(1);
}
}
}

@ -1,47 +0,0 @@
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
{
[TestFixture]
public class EpisodesRepositoryReadFixture : DbTest<EpisodeRepository, Episode>
{
private Series series;
[SetUp]
public void Setup()
{
series = Builder<Series>.CreateNew()
.With(s => s.Runtime = 30)
.BuildNew();
Db.Insert(series);
}
[Test]
public void should_get_episodes_by_file()
{
var episodeFile = Builder<EpisodeFile>.CreateNew()
.With(h => h.Quality = new QualityModel())
.BuildNew();
Db.Insert(episodeFile);
var episode = Builder<Episode>.CreateListOfSize(2)
.All()
.With(e => e.SeriesId = series.Id)
.With(e => e.EpisodeFileId = episodeFile.Id)
.BuildListOfNew();
Db.InsertMany(episode);
var episodes = Subject.GetEpisodeByFileId(episodeFile.Id);
episodes.Should().HaveCount(2);
}
}
}

@ -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,81 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
{
[TestFixture]
public class EpisodesWithFilesFixture : DbTest<EpisodeRepository, Episode>
{
private const int SERIES_ID = 1;
private List<Episode> _episodes;
private List<EpisodeFile> _episodeFiles;
[SetUp]
public void Setup()
{
_episodeFiles = Builder<EpisodeFile>.CreateListOfSize(5)
.All()
.With(c => c.Quality = new QualityModel())
.BuildListOfNew();
Db.InsertMany(_episodeFiles);
_episodes = Builder<Episode>.CreateListOfSize(10)
.All()
.With(e => e.EpisodeFileId = 0)
.With(e => e.SeriesId = SERIES_ID)
.BuildListOfNew()
.ToList();
for (int i = 0; i < _episodeFiles.Count; i++)
{
_episodes[i].EpisodeFileId = _episodeFiles[i].Id;
}
Db.InsertMany(_episodes);
}
[Test]
public void should_only_get_files_that_have_episode_files()
{
var result = Subject.EpisodesWithFiles(SERIES_ID);
result.Should().OnlyContain(e => e.EpisodeFileId > 0);
result.Should().HaveCount(_episodeFiles.Count);
}
[Test]
public void should_only_contain_episodes_for_the_given_series()
{
var episodeFile = Builder<EpisodeFile>.CreateNew()
.With(f => f.RelativePath = "another path")
.With(c => c.Quality = new QualityModel())
.BuildNew();
Db.Insert(episodeFile);
var episode = Builder<Episode>.CreateNew()
.With(e => e.SeriesId = SERIES_ID + 10)
.With(e => e.EpisodeFileId = episodeFile.Id)
.BuildNew();
Db.Insert(episode);
Subject.EpisodesWithFiles(episode.SeriesId).Should().OnlyContain(e => e.SeriesId == episode.SeriesId);
}
[Test]
public void should_have_episode_file_loaded()
{
Subject.EpisodesWithFiles(SERIES_ID).Should().OnlyContain(e => e.EpisodeFile.IsLoaded);
}
}
}

@ -1,167 +0,0 @@
using System;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
{
[TestFixture]
public class EpisodesWithoutFilesFixture : DbTest<EpisodeRepository, Episode>
{
private Series _monitoredSeries;
private Series _unmonitoredSeries;
private PagingSpec<Episode> _pagingSpec;
[SetUp]
public void Setup()
{
_monitoredSeries = Builder<Series>.CreateNew()
.With(s => s.Id = 0)
.With(s => s.TvRageId = RandomNumber)
.With(s => s.Runtime = 30)
.With(s => s.Monitored = true)
.With(s => s.TitleSlug = "Title3")
.Build();
_unmonitoredSeries = Builder<Series>.CreateNew()
.With(s => s.Id = 0)
.With(s => s.TvdbId = RandomNumber)
.With(s => s.Runtime = 30)
.With(s => s.Monitored = false)
.With(s => s.TitleSlug = "Title2")
.Build();
_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
};
var monitoredSeriesEpisodes = Builder<Episode>.CreateListOfSize(3)
.All()
.With(e => e.Id = 0)
.With(e => e.SeriesId = _monitoredSeries.Id)
.With(e => e.EpisodeFileId = 0)
.With(e => e.AirDateUtc = DateTime.Now.AddDays(-5))
.With(e => e.Monitored = true)
.TheFirst(1)
.With(e => e.Monitored = false)
.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.EpisodeFileId = 0)
.With(e => e.AirDateUtc = DateTime.Now.AddDays(-5))
.With(e => e.Monitored = true)
.TheFirst(1)
.With(e => e.Monitored = false)
.TheLast(1)
.With(e => e.SeasonNumber = 0)
.Build();
var unairedEpisodes = Builder<Episode>.CreateListOfSize(1)
.All()
.With(e => e.Id = 0)
.With(e => e.SeriesId = _monitoredSeries.Id)
.With(e => e.EpisodeFileId = 0)
.With(e => e.AirDateUtc = DateTime.Now.AddDays(5))
.With(e => e.Monitored = true)
.Build();
Db.InsertMany(monitoredSeriesEpisodes);
Db.InsertMany(unmonitoredSeriesEpisodes);
Db.InsertMany(unairedEpisodes);
}
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_get_monitored_episodes()
{
GivenMonitoredFilterExpression();
var episodes = Subject.EpisodesWithoutFiles(_pagingSpec, false);
episodes.Records.Should().HaveCount(1);
}
[Test]
[Ignore("Specials not implemented")]
public void should_get_episode_including_specials()
{
var episodes = Subject.EpisodesWithoutFiles(_pagingSpec, true);
episodes.Records.Should().HaveCount(2);
}
[Test]
public void should_not_include_unmonitored_episodes()
{
GivenMonitoredFilterExpression();
var episodes = Subject.EpisodesWithoutFiles(_pagingSpec, false);
episodes.Records.Should().NotContain(e => e.Monitored == false);
}
[Test]
public void should_not_contain_unmonitored_series()
{
GivenMonitoredFilterExpression();
var episodes = Subject.EpisodesWithoutFiles(_pagingSpec, false);
episodes.Records.Should().NotContain(e => e.SeriesId == _unmonitoredSeries.Id);
}
[Test]
public void should_not_return_unaired()
{
var episodes = Subject.EpisodesWithoutFiles(_pagingSpec, false);
episodes.TotalRecords.Should().Be(4);
}
[Test]
public void should_not_return_episodes_on_air()
{
var onAirEpisode = Builder<Episode>.CreateNew()
.With(e => e.Id = 0)
.With(e => e.SeriesId = _monitoredSeries.Id)
.With(e => e.EpisodeFileId = 0)
.With(e => e.AirDateUtc = DateTime.Now.AddMinutes(-15))
.With(e => e.Monitored = true)
.Build();
Db.Insert(onAirEpisode);
var episodes = Subject.EpisodesWithoutFiles(_pagingSpec, false);
episodes.TotalRecords.Should().Be(4);
episodes.Records.Where(e => e.Id == onAirEpisode.Id).Should().BeEmpty();
}
}
}

@ -1,85 +0,0 @@
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
{
[TestFixture]
public class FindEpisodeFixture : DbTest<EpisodeRepository, Episode>
{
private Episode _episode1;
private Episode _episode2;
[SetUp]
public void Setup()
{
_episode1 = Builder<Episode>.CreateNew()
.With(e => e.SeriesId = 1)
.With(e => e.SeasonNumber = 1)
.With(e => e.SceneSeasonNumber = 2)
.With(e => e.EpisodeNumber = 3)
.With(e => e.AbsoluteEpisodeNumber = 3)
.With(e => e.SceneEpisodeNumber = 4)
.BuildNew();
_episode2 = Builder<Episode>.CreateNew()
.With(e => e.SeriesId = 1)
.With(e => e.SeasonNumber = 1)
.With(e => e.SceneSeasonNumber = 2)
.With(e => e.EpisodeNumber = 4)
.With(e => e.SceneEpisodeNumber = 4)
.BuildNew();
_episode1 = Db.Insert(_episode1);
}
[Test]
public void should_find_episode_by_scene_numbering()
{
Subject.FindEpisodesBySceneNumbering(_episode1.SeriesId, _episode1.SceneSeasonNumber.Value, _episode1.SceneEpisodeNumber.Value)
.First()
.Id
.Should()
.Be(_episode1.Id);
}
[Test]
public void should_find_episode_by_standard_numbering()
{
Subject.Find(_episode1.SeriesId, _episode1.SeasonNumber, _episode1.EpisodeNumber)
.Id
.Should()
.Be(_episode1.Id);
}
[Test]
public void should_not_find_episode_that_does_not_exist()
{
Subject.Find(_episode1.SeriesId, _episode1.SeasonNumber + 1, _episode1.EpisodeNumber)
.Should()
.BeNull();
}
[Test]
public void should_find_episode_by_absolute_numbering()
{
Subject.Find(_episode1.SeriesId, _episode1.AbsoluteEpisodeNumber.Value)
.Id
.Should()
.Be(_episode1.Id);
}
[Test]
public void should_return_multiple_episode_if_multiple_match_by_scene_numbering()
{
_episode2 = Db.Insert(_episode2);
Subject.FindEpisodesBySceneNumbering(_episode1.SeriesId, _episode1.SceneSeasonNumber.Value, _episode1.SceneEpisodeNumber.Value)
.Should()
.HaveCount(2);
}
}
}

@ -1,71 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.TvTests.EpisodeServiceTests
{
[TestFixture]
public class FindEpisodeByTitleFixture : CoreTest<EpisodeService>
{
private List<Episode> _episodes;
[SetUp]
public void Setup()
{
_episodes = Builder<Episode>.CreateListOfSize(5)
.Build()
.ToList();
}
private void GivenEpisodesWithTitles(params string[] titles)
{
for (int i = 0; i < titles.Count(); i++)
{
_episodes[i].Title = titles[i];
}
Mocker.GetMock<IEpisodeRepository>()
.Setup(s => s.GetEpisodes(It.IsAny<int>(), It.IsAny<int>()))
.Returns(_episodes);
}
[Test]
public void should_find_episode_by_title()
{
const string expectedTitle = "A Journey to the Highlands";
GivenEpisodesWithTitles(expectedTitle);
Subject.FindEpisodeByTitle(1, 1, "Downton.Abbey.A.Journey.To.The.Highlands.720p.BluRay.x264-aAF")
.Title
.Should()
.Be(expectedTitle);
}
[Test]
public void should_prefer_longer_match()
{
const string expectedTitle = "Inside The Walking Dead: Walker University";
GivenEpisodesWithTitles("Inside The Walking Dead", expectedTitle);
Subject.FindEpisodeByTitle(1, 1, "The.Walking.Dead.S04.Special.Inside.The.Walking.Dead.Walker.University.720p.HDTV.x264-W4F")
.Title
.Should()
.Be(expectedTitle);
}
[Test]
public void should_return_null_when_no_match_is_found()
{
GivenEpisodesWithTitles();
Subject.FindEpisodeByTitle(1, 1, "The.Walking.Dead.S04.Special.Inside.The.Walking.Dead.Walker.University.720p.HDTV.x264-W4F")
.Should()
.BeNull();
}
}
}

@ -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,40 +0,0 @@
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv.Events;
namespace NzbDrone.Core.Test.TvTests.SeriesServiceTests
{
[TestFixture]
public class AddSeriesFixture : CoreTest<SeriesService>
{
private Series fakeSeries;
[SetUp]
public void Setup()
{
fakeSeries = Builder<Series>.CreateNew().Build();
}
[Test]
public void series_added_event_should_have_proper_path()
{
fakeSeries.Path = null;
fakeSeries.RootFolderPath = @"C:\Test\TV";
Mocker.GetMock<IBuildFileNames>()
.Setup(s => s.GetSeriesFolder(fakeSeries, null))
.Returns(fakeSeries.Title);
var series = Subject.AddSeries(fakeSeries);
series.Path.Should().NotBeNull();
VerifyEventPublished<SeriesAddedEvent>();
}
}
}

@ -1,73 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.TvTests.SeriesServiceTests
{
[TestFixture]
public class UpdateMultipleSeriesFixture : CoreTest<SeriesService>
{
private List<Series> _series;
[SetUp]
public void Setup()
{
_series = Builder<Series>.CreateListOfSize(5)
.All()
.With(s => s.ProfileId = 1)
.With(s => s.Monitored)
.With(s => s.SeasonFolder)
.With(s => s.Path = @"C:\Test\name".AsOsAgnostic())
.With(s => s.RootFolderPath = "")
.Build().ToList();
}
[Test]
public void should_call_repo_updateMany()
{
Subject.UpdateSeries(_series);
Mocker.GetMock<ISeriesRepository>().Verify(v => v.UpdateMany(_series), Times.Once());
}
[Test]
public void should_update_path_when_rootFolderPath_is_supplied()
{
var newRoot = @"C:\Test\TV2".AsOsAgnostic();
_series.ForEach(s => s.RootFolderPath = newRoot);
Subject.UpdateSeries(_series).ForEach(s => s.Path.Should().StartWith(newRoot));
}
[Test]
public void should_not_update_path_when_rootFolderPath_is_empty()
{
Subject.UpdateSeries(_series).ForEach(s =>
{
var expectedPath = _series.Single(ser => ser.Id == s.Id).Path;
s.Path.Should().Be(expectedPath);
});
}
[Test]
public void should_be_able_to_update_many_series()
{
var series = Builder<Series>.CreateListOfSize(50)
.All()
.With(s => s.Path = (@"C:\Test\TV\" + s.Path).AsOsAgnostic())
.Build()
.ToList();
var newRoot = @"C:\Test\TV2".AsOsAgnostic();
series.ForEach(s => s.RootFolderPath = newRoot);
Subject.UpdateSeries(series);
}
}
}

@ -1,72 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.TvTests.SeriesServiceTests
{
[TestFixture]
public class UpdateSeriesFixture : CoreTest<SeriesService>
{
private Series _fakeSeries;
private Series _existingSeries;
[SetUp]
public void Setup()
{
_fakeSeries = Builder<Series>.CreateNew().Build();
_existingSeries = Builder<Series>.CreateNew().Build();
_fakeSeries.Seasons = new List<Season>
{
new Season{ SeasonNumber = 1, Monitored = true },
new Season{ SeasonNumber = 2, Monitored = true }
};
_existingSeries.Seasons = new List<Season>
{
new Season{ SeasonNumber = 1, Monitored = true },
new Season{ SeasonNumber = 2, Monitored = true }
};
}
private void GivenExistingSeries()
{
Mocker.GetMock<ISeriesRepository>()
.Setup(s => s.Get(It.IsAny<int>()))
.Returns(_existingSeries);
}
[Test]
public void should_not_update_episodes_if_season_hasnt_changed()
{
GivenExistingSeries();
Subject.UpdateSeries(_fakeSeries);
Mocker.GetMock<IEpisodeService>()
.Verify(v => v.SetEpisodeMonitoredBySeason(_fakeSeries.Id, It.IsAny<int>(), It.IsAny<bool>()), Times.Never());
}
[Test]
public void should_update_series_when_it_changes()
{
GivenExistingSeries();
var seasonNumber = 1;
var monitored = false;
_fakeSeries.Seasons.Single(s => s.SeasonNumber == seasonNumber).Monitored = monitored;
Subject.UpdateSeries(_fakeSeries);
Mocker.GetMock<IEpisodeService>()
.Verify(v => v.SetEpisodeMonitoredBySeason(_fakeSeries.Id, seasonNumber, monitored), Times.Once());
Mocker.GetMock<IEpisodeService>()
.Verify(v => v.SetEpisodeMonitoredBySeason(_fakeSeries.Id, It.IsAny<int>(), It.IsAny<bool>()), Times.Once());
}
}
}

@ -1,29 +0,0 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.TvTests
{
[TestFixture]
public class SeriesTitleNormalizerFixture
{
[TestCase("A to Z", 281588, "a to z")]
[TestCase("A. D. - The Trials & Triumph of the Early Church", 266757, "ad trials triumph early church")]
public void should_use_precomputed_title(string title, int tvdbId, string expected)
{
SeriesTitleNormalizer.Normalize(title, tvdbId).Should().Be(expected);
}
[TestCase("2 Broke Girls", "2 broke girls")]
[TestCase("Archer (2009)", "archer 2009")]
[TestCase("The Office (US)", "office us")]
[TestCase("The Mentalist", "mentalist")]
[TestCase("The Good Wife", "good wife")]
[TestCase("The Newsroom (2012)", "newsroom 2012")]
[TestCase("Special Agent Oso", "special agent oso")]
public void should_normalize_title(string title, string expected)
{
SeriesTitleNormalizer.Normalize(title, 0).Should().Be(expected);
}
}
}

@ -7,7 +7,6 @@ using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser;
using System.Collections.Generic;
using NzbDrone.Core.Tv.Events;
namespace NzbDrone.Core.DataAugmentation.Scene
{
@ -23,7 +22,7 @@ namespace NzbDrone.Core.DataAugmentation.Scene
}
public class SceneMappingService : ISceneMappingService,
IHandle<SeriesRefreshStartingEvent>,
// IHandle<SeriesRefreshStartingEvent>,
IExecute<UpdateSceneMappingCommand>
{
private readonly ISceneMappingRepository _repository;
@ -237,13 +236,13 @@ namespace NzbDrone.Core.DataAugmentation.Scene
return titles.Where(title => title.All(c => c <= 255)).ToList();
}
public void Handle(SeriesRefreshStartingEvent message)
{
if (message.ManualTrigger && _findByTvdbIdCache.IsExpired(TimeSpan.FromMinutes(1)))
{
UpdateMappings();
}
}
//public void Handle(SeriesRefreshStartingEvent message)
//{
// if (message.ManualTrigger && _findByTvdbIdCache.IsExpired(TimeSpan.FromMinutes(1)))
// {
// UpdateMappings();
// }
//}
public void Execute(UpdateSceneMappingCommand message)
{

@ -1,9 +0,0 @@
namespace NzbDrone.Core.DataAugmentation.Xem.Model
{
public class XemResult<T>
{
public string Result { get; set; }
public T Data { get; set; }
public string Message { get; set; }
}
}

@ -1,8 +0,0 @@
namespace NzbDrone.Core.DataAugmentation.Xem.Model
{
public class XemSceneTvdbMapping
{
public XemValues Scene { get; set; }
public XemValues Tvdb { get; set; }
}
}

@ -1,9 +0,0 @@
namespace NzbDrone.Core.DataAugmentation.Xem.Model
{
public class XemValues
{
public int Season { get; set; }
public int Episode { get; set; }
public int Absolute { get; set; }
}
}

@ -1,127 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json.Linq;
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.DataAugmentation.Xem.Model;
namespace NzbDrone.Core.DataAugmentation.Xem
{
public interface IXemProxy
{
List<int> GetXemSeriesIds();
List<XemSceneTvdbMapping> GetSceneTvdbMappings(int id);
List<SceneMapping> GetSceneTvdbNames();
}
public class XemProxy : IXemProxy
{
private const string ROOT_URL = "http://thexem.de.broken/map/";
private readonly Logger _logger;
private readonly IHttpClient _httpClient;
private readonly IHttpRequestBuilderFactory _xemRequestBuilder;
private static readonly string[] IgnoredErrors = { "no single connection", "no show with the tvdb_id" };
public XemProxy(IHttpClient httpClient, Logger logger)
{
_httpClient = httpClient;
_logger = logger;
_xemRequestBuilder = new HttpRequestBuilder(ROOT_URL)
.AddSuffixQueryParam("origin", "tvdb")
.CreateFactory();
}
public List<int> GetXemSeriesIds()
{
_logger.Debug("Fetching Series IDs from");
var request = _xemRequestBuilder.Create()
.Resource("/havemap")
.Build();
var response = _httpClient.Get<XemResult<List<string>>>(request).Resource;
CheckForFailureResult(response);
return response.Data.Select(d =>
{
int tvdbId = 0;
int.TryParse(d, out tvdbId);
return tvdbId;
}).Where(t => t > 0).ToList();
}
public List<XemSceneTvdbMapping> GetSceneTvdbMappings(int id)
{
_logger.Debug("Fetching Mappings for: {0}", id);
var request = _xemRequestBuilder.Create()
.Resource("/all")
.AddQueryParam("id", id)
.Build();
var response = _httpClient.Get<XemResult<List<XemSceneTvdbMapping>>>(request).Resource;
return response.Data.Where(c => c.Scene != null).ToList();
}
public List<SceneMapping> GetSceneTvdbNames()
{
_logger.Debug("Fetching alternate names");
var request = _xemRequestBuilder.Create()
.Resource("/allNames")
.AddQueryParam("seasonNumbers", true)
.Build();
var response = _httpClient.Get<XemResult<Dictionary<int, List<JObject>>>>(request).Resource;
var result = new List<SceneMapping>();
foreach (var series in response.Data)
{
foreach (var name in series.Value)
{
foreach (var n in name)
{
int seasonNumber;
if (!int.TryParse(n.Value.ToString(), out seasonNumber))
{
continue;
}
//hack to deal with Fate/Zero
if (series.Key == 79151 && seasonNumber > 1)
{
continue;
}
result.Add(new SceneMapping
{
Title = n.Key,
SearchTerm = n.Key,
SceneSeasonNumber = seasonNumber,
TvdbId = series.Key
});
}
}
}
return result;
}
private static void CheckForFailureResult<T>(XemResult<T> response)
{
if (response.Result.Equals("failure", StringComparison.InvariantCultureIgnoreCase) &&
!IgnoredErrors.Any(knowError => response.Message.Contains(knowError)))
{
throw new Exception("Error response received from Xem: " + response.Message);
}
}
}
}

@ -1,243 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Tv.Events;
namespace NzbDrone.Core.DataAugmentation.Xem
{
public class XemService : ISceneMappingProvider, IHandle<SeriesUpdatedEvent>, IHandle<SeriesRefreshStartingEvent>
{
private readonly IEpisodeService _episodeService;
private readonly IXemProxy _xemProxy;
private readonly ISeriesService _seriesService;
private readonly Logger _logger;
private readonly ICachedDictionary<bool> _cache;
public XemService(IEpisodeService episodeService,
IXemProxy xemProxy,
ISeriesService seriesService, ICacheManager cacheManager, Logger logger)
{
_episodeService = episodeService;
_xemProxy = xemProxy;
_seriesService = seriesService;
_logger = logger;
_cache = cacheManager.GetCacheDictionary<bool>(GetType(), "mappedTvdbid");
}
private void PerformUpdate(Series series)
{
_logger.Debug("Updating scene numbering mapping for: {0}", series);
//try
//{
// var mappings = _xemProxy.GetSceneTvdbMappings(series.TvdbId);
// if (!mappings.Any() && !series.UseSceneNumbering)
// {
// _logger.Debug("Mappings for: {0} are empty, skipping", series);
// return;
// }
// var episodes = _episodeService.GetEpisodeBySeries(series.Id);
// foreach (var episode in episodes)
// {
// episode.SceneAbsoluteEpisodeNumber = null;
// episode.SceneSeasonNumber = null;
// episode.SceneEpisodeNumber = null;
// episode.UnverifiedSceneNumbering = false;
// }
// foreach (var mapping in mappings)
// {
// _logger.Debug("Setting scene numbering mappings for {0} S{1:00}E{2:00}", series, mapping.Tvdb.Season, mapping.Tvdb.Episode);
// var episode = episodes.SingleOrDefault(e => e.SeasonNumber == mapping.Tvdb.Season && e.EpisodeNumber == mapping.Tvdb.Episode);
// if (episode == null)
// {
// _logger.Debug("Information hasn't been added to TheTVDB yet, skipping.");
// continue;
// }
// episode.SceneAbsoluteEpisodeNumber = mapping.Scene.Absolute;
// episode.SceneSeasonNumber = mapping.Scene.Season;
// episode.SceneEpisodeNumber = mapping.Scene.Episode;
// }
// if (episodes.Any(v => v.SceneEpisodeNumber.HasValue && v.SceneSeasonNumber != 0))
// {
// ExtrapolateMappings(series, episodes, mappings);
// }
// _episodeService.UpdateEpisodes(episodes);
// series.UseSceneNumbering = mappings.Any();
// _seriesService.UpdateSeries(series);
// _logger.Debug("XEM mapping updated for {0}", series);
//}
//catch (Exception ex)
//{
// _logger.Error(ex, "Error updating scene numbering mappings for {0}", series);
//}
}
private void ExtrapolateMappings(Series series, List<Episode> episodes, List<Model.XemSceneTvdbMapping> mappings)
{
var mappedEpisodes = episodes.Where(v => v.SeasonNumber != 0 && v.SceneEpisodeNumber.HasValue).ToList();
var mappedSeasons = new HashSet<int>(mappedEpisodes.Select(v => v.SeasonNumber).Distinct());
var sceneEpisodeMappings = mappings.ToLookup(v => v.Scene.Season)
.ToDictionary(v => v.Key, e => new HashSet<int>(e.Select(v => v.Scene.Episode)));
var firstTvdbEpisodeBySeason = mappings.ToLookup(v => v.Tvdb.Season)
.ToDictionary(v => v.Key, e => e.Min(v => v.Tvdb.Episode));
var lastSceneSeason = mappings.Select(v => v.Scene.Season).Max();
var lastTvdbSeason = mappings.Select(v => v.Tvdb.Season).Max();
// Mark all episodes not on the xem as unverified.
foreach (var episode in episodes)
{
if (episode.SeasonNumber == 0) continue;
if (episode.SceneEpisodeNumber.HasValue) continue;
if (mappedSeasons.Contains(episode.SeasonNumber))
{
// Mark if a mapping exists for an earlier episode in this season.
if (firstTvdbEpisodeBySeason[episode.SeasonNumber] <= episode.EpisodeNumber)
{
episode.UnverifiedSceneNumbering = true;
continue;
}
// Mark if a mapping exists with a scene number to this episode.
if (sceneEpisodeMappings.ContainsKey(episode.SeasonNumber) &&
sceneEpisodeMappings[episode.SeasonNumber].Contains(episode.EpisodeNumber))
{
episode.UnverifiedSceneNumbering = true;
continue;
}
}
else if (lastSceneSeason != lastTvdbSeason && episode.SeasonNumber > lastTvdbSeason)
{
episode.UnverifiedSceneNumbering = true;
}
}
foreach (var episode in episodes)
{
if (episode.SeasonNumber == 0) continue;
if (episode.SceneEpisodeNumber.HasValue) continue;
if (episode.SeasonNumber < lastTvdbSeason) continue;
if (!episode.UnverifiedSceneNumbering) continue;
var seasonMappings = mappings.Where(v => v.Tvdb.Season == episode.SeasonNumber).ToList();
if (seasonMappings.Any(v => v.Tvdb.Episode >= episode.EpisodeNumber))
{
continue;
}
if (seasonMappings.Any())
{
var lastEpisodeMapping = seasonMappings.OrderBy(v => v.Tvdb.Episode).Last();
var lastSceneSeasonMapping = mappings.Where(v => v.Scene.Season == lastEpisodeMapping.Scene.Season).OrderBy(v => v.Scene.Episode).Last();
if (lastSceneSeasonMapping.Tvdb.Season == 0)
{
continue;
}
var offset = episode.EpisodeNumber - lastEpisodeMapping.Tvdb.Episode;
episode.SceneSeasonNumber = lastEpisodeMapping.Scene.Season;
episode.SceneEpisodeNumber = lastEpisodeMapping.Scene.Episode + offset;
episode.SceneAbsoluteEpisodeNumber = lastEpisodeMapping.Scene.Absolute + offset;
}
else if (lastTvdbSeason != lastSceneSeason)
{
var offset = episode.SeasonNumber - lastTvdbSeason;
episode.SceneSeasonNumber = lastSceneSeason + offset;
episode.SceneEpisodeNumber = episode.EpisodeNumber;
// TODO: SceneAbsoluteEpisodeNumber.
}
}
}
private void UpdateXemSeriesIds()
{
try
{
var ids = _xemProxy.GetXemSeriesIds();
if (ids.Any())
{
_cache.Update(ids.ToDictionary(v => v.ToString(), v => true));
return;
}
_cache.ExtendTTL();
_logger.Warn("Failed to update Xem series list.");
}
catch (Exception ex)
{
_cache.ExtendTTL();
_logger.Warn(ex, "Failed to update Xem series list.");
}
}
public List<SceneMapping> GetSceneMappings()
{
var mappings = _xemProxy.GetSceneTvdbNames();
return mappings.Where(m =>
{
int id;
if (int.TryParse(m.Title, out id))
{
_logger.Debug("Skipping all numeric name: {0} for {1}", m.Title, m.TvdbId);
return false;
}
return true;
}).ToList();
}
public void Handle(SeriesUpdatedEvent message)
{
//if (_cache.IsExpired(TimeSpan.FromHours(3)))
//{
// UpdateXemSeriesIds();
//}
//if (_cache.Count == 0)
//{
// _logger.Debug("Scene numbering is not available");
// return;
//}
//if (!_cache.Find(message.Series.TvdbId.ToString()) && !message.Series.UseSceneNumbering)
//{
// _logger.Debug("Scene numbering is not available for {0} [{1}]", message.Series.Title, message.Series.TvdbId);
// return;
//}
//PerformUpdate(message.Series);
}
public void Handle(SeriesRefreshStartingEvent message)
{
//if (message.ManualTrigger && _cache.IsExpired(TimeSpan.FromMinutes(1)))
//{
// UpdateXemSeriesIds();
//}
}
}
}

@ -114,7 +114,7 @@ namespace NzbDrone.Core.Datastore
Mapper.Entity<OtherExtraFile>().RegisterModel("ExtraFiles");
Mapper.Entity<PendingRelease>().RegisterModel("PendingReleases")
.Ignore(e => e.RemoteEpisode);
.Ignore(e => e.RemoteAlbum);
Mapper.Entity<RemotePathMapping>().RegisterModel("RemotePathMappings");
Mapper.Entity<Tag>().RegisterModel("Tags");
@ -146,7 +146,8 @@ namespace NzbDrone.Core.Datastore
MapRepository.Instance.RegisterTypeConverter(typeof(Language), new LanguageIntConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(List<string>), new EmbeddedDocumentConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(List<ProfileLanguageItem>), new EmbeddedDocumentConverter(new LanguageIntConverter()));
MapRepository.Instance.RegisterTypeConverter(typeof(ParsedEpisodeInfo), new EmbeddedDocumentConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(ParsedAlbumInfo), new EmbeddedDocumentConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(ParsedTrackInfo), new EmbeddedDocumentConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(ReleaseInfo), new EmbeddedDocumentConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(HashSet<int>), new EmbeddedDocumentConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(OsPath), new OsPathConverter());

@ -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,17 +0,0 @@
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Download
{
public class EpisodeGrabbedEvent : IEvent
{
public RemoteEpisode Episode { get; private set; }
public string DownloadClient { get; set; }
public string DownloadId { get; set; }
public EpisodeGrabbedEvent(RemoteEpisode episode)
{
Episode = episode;
}
}
}

@ -1,4 +1,4 @@
using System;
using System;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Parser.Model;
@ -13,7 +13,6 @@ namespace NzbDrone.Core.Download.Pending
public ReleaseInfo Release { get; set; }
//Not persisted
public RemoteEpisode RemoteEpisode { get; set; }
public RemoteAlbum RemoteAlbum { get; set; }
}
}

@ -94,10 +94,9 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Roksbox
return metadata;
}
var parseResult = Parser.Parser.ParseTitle(filename);
var parseResult = Parser.Parser.ParseMusicTitle(filename);
if (parseResult != null &&
!parseResult.FullSeason)
if (parseResult != null)
{
var extension = Path.GetExtension(filename).ToLowerInvariant();

@ -93,10 +93,9 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Wdtv
return metadata;
}
var parseResult = Parser.Parser.ParseTitle(filename);
var parseResult = Parser.Parser.ParseMusicTitle(filename);
if (parseResult != null &&
!parseResult.FullSeason)
if (parseResult != null)
{
switch (Path.GetExtension(filename).ToLowerInvariant())
{

@ -115,10 +115,9 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
return metadata;
}
var parseResult = Parser.Parser.ParseTitle(filename);
var parseResult = Parser.Parser.ParseMusicTitle(filename);
if (parseResult != null &&
!parseResult.FullSeason &&
Path.GetExtension(filename) == ".nfo")
{
metadata.Type = MetadataType.TrackMetadata;

@ -27,7 +27,6 @@ namespace NzbDrone.Core.MediaFiles
private readonly IParsingService _parsingService;
private readonly IMakeImportDecision _importDecisionMaker;
private readonly IImportApprovedTracks _importApprovedTracks;
private readonly IDetectSample _detectSample;
private readonly Logger _logger;
public DownloadedTracksImportService(IDiskProvider diskProvider,
@ -36,7 +35,6 @@ namespace NzbDrone.Core.MediaFiles
IParsingService parsingService,
IMakeImportDecision importDecisionMaker,
IImportApprovedTracks importApprovedTracks,
IDetectSample detectSample,
Logger logger)
{
_diskProvider = diskProvider;
@ -45,7 +43,6 @@ namespace NzbDrone.Core.MediaFiles
_parsingService = parsingService;
_importDecisionMaker = importDecisionMaker;
_importApprovedTracks = importApprovedTracks;
_detectSample = detectSample;
_logger = logger;
}

@ -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;
}
}
}

@ -28,7 +28,6 @@ namespace NzbDrone.Core.MediaFiles.TrackImport
private readonly IMediaFileService _mediaFileService;
private readonly IDiskProvider _diskProvider;
private readonly IVideoFileInfoReader _videoFileInfoReader;
private readonly IDetectSample _detectSample;
private readonly Logger _logger;
public ImportDecisionMaker(IEnumerable<IImportDecisionEngineSpecification> specifications,
@ -36,7 +35,6 @@ namespace NzbDrone.Core.MediaFiles.TrackImport
IMediaFileService mediaFileService,
IDiskProvider diskProvider,
IVideoFileInfoReader videoFileInfoReader,
IDetectSample detectSample,
Logger logger)
{
_specifications = specifications;
@ -44,7 +42,6 @@ namespace NzbDrone.Core.MediaFiles.TrackImport
_mediaFileService = mediaFileService;
_diskProvider = diskProvider;
_videoFileInfoReader = videoFileInfoReader;
_detectSample = detectSample;
_logger = logger;
}

@ -1,12 +1,12 @@
using System;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Music;
namespace NzbDrone.Core.MetadataSource
{
public class SearchSeriesComparer : IComparer<Series>
public class SearchArtistComparer : IComparer<Artist>
{
private static readonly Regex RegexCleanPunctuation = new Regex("[-._:]", RegexOptions.Compiled);
private static readonly Regex RegexCleanCountryYearPostfix = new Regex(@"(?<=.+)( \([A-Z]{2}\)| \(\d{4}\)| \([A-Z]{2}\) \(\d{4}\))$", RegexOptions.Compiled);
@ -17,7 +17,7 @@ namespace NzbDrone.Core.MetadataSource
private readonly string _searchQueryWithoutYear;
private int? _year;
public SearchSeriesComparer(string searchQuery)
public SearchArtistComparer(string searchQuery)
{
SearchQuery = searchQuery;
@ -33,34 +33,26 @@ namespace NzbDrone.Core.MetadataSource
}
}
public int Compare(Series x, Series y)
public int Compare(Artist x, Artist y)
{
int result = 0;
// Prefer exact matches
result = Compare(x, y, s => CleanPunctuation(s.Title).Equals(CleanPunctuation(SearchQuery)));
result = Compare(x, y, s => CleanPunctuation(s.Name).Equals(CleanPunctuation(SearchQuery)));
if (result != 0) return -result;
// Remove Articles (a/an/the)
result = Compare(x, y, s => CleanArticles(s.Title).Equals(CleanArticles(SearchQuery)));
result = Compare(x, y, s => CleanArticles(s.Name).Equals(CleanArticles(SearchQuery)));
if (result != 0) return -result;
// Prefer close matches
result = Compare(x, y, s => CleanPunctuation(s.Title).LevenshteinDistance(CleanPunctuation(SearchQuery)) <= 1);
if (result != 0) return -result;
// Compare clean matches by year "Battlestar Galactica 1978"
result = CompareWithYear(x, y, s => CleanTitle(s.Title).LevenshteinDistance(_searchQueryWithoutYear) <= 1);
if (result != 0) return -result;
// Compare prefix matches by year "(CSI: ..."
result = CompareWithYear(x, y, s => s.Title.ToLowerInvariant().StartsWith(_searchQueryWithoutYear + ":"));
result = Compare(x, y, s => CleanPunctuation(s.Name).LevenshteinDistance(CleanPunctuation(SearchQuery)) <= 1);
if (result != 0) return -result;
return Compare(x, y, s => SearchQuery.LevenshteinDistanceClean(s.Title) - GetYearFactor(s));
return Compare(x, y, s => SearchQuery.LevenshteinDistanceClean(s.Name));
}
public int Compare<T>(Series x, Series y, Func<Series,T> keySelector)
public int Compare<T>(Artist x, Artist y, Func<Artist, T> keySelector)
where T : IComparable<T>
{
var keyX = keySelector(x);
@ -69,25 +61,6 @@ namespace NzbDrone.Core.MetadataSource
return keyX.CompareTo(keyY);
}
public int CompareWithYear(Series x, Series y, Predicate<Series> canMatch)
{
var matchX = canMatch(x);
var matchY = canMatch(y);
if (matchX && matchY)
{
if (_year.HasValue)
{
var result = Compare(x, y, s => s.Year == _year.Value);
if (result != 0) return result;
}
return Compare(x, y, s => s.Year);
}
return matchX.CompareTo(matchY);
}
private string CleanPunctuation(string title)
{
title = RegexCleanPunctuation.Replace(title, "");
@ -110,18 +83,5 @@ namespace NzbDrone.Core.MetadataSource
return title.Trim().ToLowerInvariant();
}
private int GetYearFactor(Series series)
{
if (_year.HasValue)
{
var offset = Math.Abs(series.Year - _year.Value);
if (offset <= 1)
{
return 20 - 10 * offset;
}
}
return 0;
}
}
}

@ -151,11 +151,6 @@
<Compile Include="DataAugmentation\Scene\SceneMappingsUpdatedEvent.cs" />
<Compile Include="DataAugmentation\Scene\ServicesProvider.cs" />
<Compile Include="DataAugmentation\Scene\UpdateSceneMappingCommand.cs" />
<Compile Include="DataAugmentation\Xem\Model\XemResult.cs" />
<Compile Include="DataAugmentation\Xem\Model\XemSceneTvdbMapping.cs" />
<Compile Include="DataAugmentation\Xem\Model\XemValues.cs" />
<Compile Include="DataAugmentation\Xem\XemProxy.cs" />
<Compile Include="DataAugmentation\Xem\XemService.cs" />
<Compile Include="Datastore\BasicRepository.cs" />
<Compile Include="Datastore\ConnectionStringFactory.cs" />
<Compile Include="Datastore\Converters\BooleanIntConverter.cs" />
@ -331,7 +326,6 @@
<Compile Include="DecisionEngine\IRejectWithReason.cs" />
<Compile Include="DecisionEngine\Rejection.cs" />
<Compile Include="DecisionEngine\RejectionType.cs" />
<Compile Include="DecisionEngine\SameEpisodesSpecification.cs" />
<Compile Include="DecisionEngine\SameTracksSpecification.cs" />
<Compile Include="DecisionEngine\SpecificationPriority.cs" />
<Compile Include="DecisionEngine\Specifications\AcceptableSizeSpecification.cs" />
@ -529,7 +523,6 @@
<Compile Include="Download\DownloadFailedEvent.cs" />
<Compile Include="Download\DownloadItemStatus.cs" />
<Compile Include="Download\DownloadService.cs" />
<Compile Include="Download\EpisodeGrabbedEvent.cs" />
<Compile Include="Download\FailedDownloadService.cs" />
<Compile Include="Download\IDownloadClient.cs" />
<Compile Include="Download\Pending\PendingRelease.cs" />
@ -760,16 +753,13 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="MediaFiles\DownloadedAlbumsCommandService.cs" />
<Compile Include="MediaFiles\EpisodeFile.cs" />
<Compile Include="MediaFiles\TrackImport\IImportDecisionEngineSpecification.cs" />
<Compile Include="MediaFiles\TrackImport\ImportApprovedEpisodes.cs" />
<Compile Include="MediaFiles\TrackImport\ImportDecisionMaker.cs" />
<Compile Include="MediaFiles\TrackImport\ImportResultType.cs" />
<Compile Include="MediaFiles\TrackImport\Manual\ManualImportFile.cs" />
<Compile Include="MediaFiles\TrackImport\Manual\ManualImportCommand.cs" />
<Compile Include="MediaFiles\TrackImport\Manual\ManualImportItem.cs" />
<Compile Include="MediaFiles\TrackImport\Manual\ManualImportService.cs" />
<Compile Include="MediaFiles\TrackImport\DetectSample.cs" />
<Compile Include="MediaFiles\TrackImport\Manual\ManuallyImportedFile.cs" />
<Compile Include="MediaFiles\TrackImport\Specifications\FreeSpaceSpecification.cs" />
<Compile Include="MediaFiles\TrackImport\Specifications\NotUnpackingSpecification.cs" />
@ -836,7 +826,7 @@
<Compile Include="MetadataSource\SkyHook\Resource\TimeOfDayResource.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\TrackResource.cs" />
<Compile Include="MetadataSource\SkyHook\SkyHookProxy.cs" />
<Compile Include="MetadataSource\SearchSeriesComparer.cs" />
<Compile Include="MetadataSource\SearchArtistComparer.cs" />
<Compile Include="MetadataSource\SkyHook\SkyHookException.cs" />
<Compile Include="Extras\Metadata\Consumers\MediaBrowser\MediaBrowserMetadata.cs" />
<Compile Include="Extras\Metadata\Consumers\MediaBrowser\MediaBrowserMetadataSettings.cs" />
@ -1079,11 +1069,7 @@
<Compile Include="Organizer\NamingConfigService.cs" />
<Compile Include="Organizer\SampleResult.cs" />
<Compile Include="Parser\InvalidDateException.cs" />
<Compile Include="Parser\Model\LocalEpisode.cs" />
<Compile Include="Parser\Model\ParsedEpisodeInfo.cs" />
<Compile Include="Parser\Model\ReleaseInfo.cs" />
<Compile Include="Parser\Model\RemoteEpisode.cs" />
<Compile Include="Parser\Model\SeriesTitleInfo.cs" />
<Compile Include="Parser\Model\TorrentInfo.cs" />
<Compile Include="Parser\Parser.cs" />
<Compile Include="Parser\ParsingService.cs" />
@ -1145,42 +1131,6 @@
<Compile Include="ThingiProvider\ProviderMessage.cs" />
<Compile Include="ThingiProvider\ProviderRepository.cs" />
<Compile Include="TinyTwitter.cs" />
<Compile Include="Tv\Actor.cs" />
<Compile Include="Tv\AddSeriesOptions.cs" />
<Compile Include="Tv\AddSeriesService.cs" />
<Compile Include="Tv\AddSeriesValidator.cs" />
<Compile Include="Tv\Commands\RefreshSeriesCommand.cs" />
<Compile Include="Tv\Episode.cs" />
<Compile Include="Tv\EpisodeAddedService.cs" />
<Compile Include="Tv\EpisodeCutoffService.cs" />
<Compile Include="Tv\EpisodeMonitoredService.cs" />
<Compile Include="Tv\EpisodeRepository.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Tv\EpisodeService.cs" />
<Compile Include="Tv\Events\EpisodeInfoRefreshedEvent.cs" />
<Compile Include="Tv\Events\SeriesAddedEvent.cs" />
<Compile Include="Tv\Events\SeriesDeletedEvent.cs" />
<Compile Include="Tv\Events\SeriesEditedEvent.cs" />
<Compile Include="Tv\Events\SeriesImportedEvent.cs" />
<Compile Include="Tv\Events\SeriesRefreshStartingEvent.cs" />
<Compile Include="Tv\Events\SeriesUpdatedEvent.cs" />
<Compile Include="Tv\MonitoringOptions.cs" />
<Compile Include="Tv\Ratings.cs" />
<Compile Include="Tv\RefreshEpisodeService.cs" />
<Compile Include="Tv\RefreshSeriesService.cs" />
<Compile Include="Tv\Season.cs" />
<Compile Include="Tv\Series.cs" />
<Compile Include="Tv\SeriesAddedHandler.cs" />
<Compile Include="Tv\SeriesEditedService.cs" />
<Compile Include="Tv\SeriesRepository.cs" />
<Compile Include="Tv\SeriesService.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Tv\SeriesStatusType.cs" />
<Compile Include="Tv\SeriesTitleNormalizer.cs" />
<Compile Include="Tv\SeriesTitleSlugValidator.cs" />
<Compile Include="Tv\SeriesTypes.cs" />
<Compile Include="Update\Commands\ApplicationUpdateCommand.cs" />
<Compile Include="Update\InstallUpdateService.cs" />
<Compile Include="Update\RecentUpdateProvider.cs" />

@ -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,9 +0,0 @@
namespace NzbDrone.Core.Parser.Model
{
public class SeriesTitleInfo
{
public string Title { get; set; }
public string TitleWithoutYear { get; set; }
public int Year { get; set; }
}
}

@ -8,7 +8,6 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Instrumentation;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Parser
@ -418,27 +417,6 @@ namespace NzbDrone.Core.Parser
return result;
}
public static ParsedEpisodeInfo ParsePath(string path)
{
var fileInfo = new FileInfo(path);
var result = ParseTitle(fileInfo.Name);
if (result == null)
{
Logger.Debug("Attempting to parse episode info using directory and file names. {0}", fileInfo.Directory.Name);
result = ParseTitle(fileInfo.Directory.Name + " " + fileInfo.Name);
}
if (result == null)
{
Logger.Debug("Attempting to parse episode info using directory name. {0}", fileInfo.Directory.Name);
result = ParseTitle(fileInfo.Directory.Name + fileInfo.Extension);
}
return result;
}
public static ParsedTrackInfo ParseMusicTitle(string title)
{
try
@ -654,130 +632,6 @@ namespace NzbDrone.Core.Parser
return null;
}
public static ParsedEpisodeInfo ParseTitle(string title)
{
try
{
if (!ValidateBeforeParsing(title)) return null;
Logger.Debug("Parsing string '{0}'", title);
if (ReversedTitleRegex.IsMatch(title))
{
var titleWithoutExtension = RemoveFileExtension(title).ToCharArray();
Array.Reverse(titleWithoutExtension);
title = new string(titleWithoutExtension) + title.Substring(titleWithoutExtension.Length);
Logger.Debug("Reversed name detected. Converted to '{0}'", title);
}
var simpleTitle = SimpleTitleRegex.Replace(title, string.Empty);
simpleTitle = RemoveFileExtension(simpleTitle);
// TODO: Quick fix stripping [url] - prefixes.
simpleTitle = WebsitePrefixRegex.Replace(simpleTitle, string.Empty);
simpleTitle = CleanTorrentSuffixRegex.Replace(simpleTitle, string.Empty);
var airDateMatch = AirDateRegex.Match(simpleTitle);
if (airDateMatch.Success)
{
simpleTitle = airDateMatch.Groups[1].Value + airDateMatch.Groups["airyear"].Value + "." + airDateMatch.Groups["airmonth"].Value + "." + airDateMatch.Groups["airday"].Value;
}
var sixDigitAirDateMatch = SixDigitAirDateRegex.Match(simpleTitle);
if (sixDigitAirDateMatch.Success)
{
var airYear = sixDigitAirDateMatch.Groups["airyear"].Value;
var airMonth = sixDigitAirDateMatch.Groups["airmonth"].Value;
var airDay = sixDigitAirDateMatch.Groups["airday"].Value;
if (airMonth != "00" || airDay != "00")
{
var fixedDate = string.Format("20{0}.{1}.{2}", airYear, airMonth, airDay);
simpleTitle = simpleTitle.Replace(sixDigitAirDateMatch.Groups["airdate"].Value, fixedDate);
}
}
foreach (var regex in ReportTitleRegex)
{
var match = regex.Matches(simpleTitle);
if (match.Count != 0)
{
Logger.Trace(regex);
try
{
var result = ParseMatchCollection(match);
if (result != null)
{
if (result.FullSeason && title.ContainsIgnoreCase("Special"))
{
result.FullSeason = false;
result.Special = true;
}
result.Language = LanguageParser.ParseLanguage(title);
Logger.Debug("Language parsed: {0}", result.Language);
result.Quality = QualityParser.ParseQuality(title);
Logger.Debug("Quality parsed: {0}", result.Quality);
result.ReleaseGroup = ParseReleaseGroup(title);
var subGroup = GetSubGroup(match);
if (!subGroup.IsNullOrWhiteSpace())
{
result.ReleaseGroup = subGroup;
}
Logger.Debug("Release Group parsed: {0}", result.ReleaseGroup);
result.ReleaseHash = GetReleaseHash(match);
if (!result.ReleaseHash.IsNullOrWhiteSpace())
{
Logger.Debug("Release Hash parsed: {0}", result.ReleaseHash);
}
return result;
}
}
catch (InvalidDateException ex)
{
Logger.Debug(ex, ex.Message);
break;
}
}
}
}
catch (Exception e)
{
if (!title.ToLower().Contains("password") && !title.ToLower().Contains("yenc"))
Logger.Error(e, "An error has occurred while trying to parse {0}", title);
}
Logger.Debug("Unable to parse {0}", title);
return null;
}
public static string ParseSeriesName(string title)
{
Logger.Debug("Parsing string '{0}'", title);
var parseResult = ParseTitle(title);
if (parseResult == null)
{
return CleanSeriesTitle(title);
}
return parseResult.SeriesTitle;
}
public static string CleanSeriesTitle(this string title)
{
long number = 0;
@ -961,27 +815,6 @@ namespace NzbDrone.Core.Parser
return Language.English;
}
private static SeriesTitleInfo GetSeriesTitleInfo(string title)
{
var seriesTitleInfo = new SeriesTitleInfo();
seriesTitleInfo.Title = title;
var match = YearInTitleRegex.Match(title);
if (!match.Success)
{
seriesTitleInfo.TitleWithoutYear = title;
}
else
{
seriesTitleInfo.TitleWithoutYear = match.Groups["title"].Value;
seriesTitleInfo.Year = Convert.ToInt32(match.Groups["year"].Value);
}
return seriesTitleInfo;
}
private static ParsedTrackInfo ParseMatchMusicCollection(MatchCollection matchCollection)
{
var artistName = matchCollection[0].Groups["artist"].Value./*Removed for cases like Will.I.Am Replace('.', ' ').*/Replace('_', ' ');
@ -1057,6 +890,20 @@ namespace NzbDrone.Core.Parser
return artistTitleInfo;
}
public static string ParseArtistName(string title)
{
Logger.Debug("Parsing string '{0}'", title);
var parseResult = ParseAlbumTitle(title);
if (parseResult == null)
{
return CleanSeriesTitle(title);
}
return parseResult.ArtistName;
}
private static ParsedAlbumInfo ParseAlbumMatchCollection(MatchCollection matchCollection)
{
var artistName = matchCollection[0].Groups["artist"].Value.Replace('.', ' ').Replace('_', ' ');
@ -1080,141 +927,6 @@ namespace NzbDrone.Core.Parser
return result;
}
private static ParsedEpisodeInfo ParseMatchCollection(MatchCollection matchCollection)
{
var seriesName = matchCollection[0].Groups["title"].Value.Replace('.', ' ').Replace('_', ' ');
seriesName = RequestInfoRegex.Replace(seriesName, "").Trim(' ');
int airYear;
int.TryParse(matchCollection[0].Groups["airyear"].Value, out airYear);
ParsedEpisodeInfo result;
if (airYear < 1900)
{
var seasons = new List<int>();
foreach (Capture seasonCapture in matchCollection[0].Groups["season"].Captures)
{
int parsedSeason;
if (int.TryParse(seasonCapture.Value, out parsedSeason))
seasons.Add(parsedSeason);
}
//If no season was found it should be treated as a mini series and season 1
if (seasons.Count == 0) seasons.Add(1);
//If more than 1 season was parsed go to the next REGEX (A multi-season release is unlikely)
if (seasons.Distinct().Count() > 1) return null;
result = new ParsedEpisodeInfo
{
SeasonNumber = seasons.First(),
EpisodeNumbers = new int[0],
AbsoluteEpisodeNumbers = new int[0]
};
foreach (Match matchGroup in matchCollection)
{
var episodeCaptures = matchGroup.Groups["episode"].Captures.Cast<Capture>().ToList();
var absoluteEpisodeCaptures = matchGroup.Groups["absoluteepisode"].Captures.Cast<Capture>().ToList();
//Allows use to return a list of 0 episodes (We can handle that as a full season release)
if (episodeCaptures.Any())
{
var first = ParseNumber(episodeCaptures.First().Value);
var last = ParseNumber(episodeCaptures.Last().Value);
if (first > last)
{
return null;
}
var count = last - first + 1;
result.EpisodeNumbers = Enumerable.Range(first, count).ToArray();
}
if (absoluteEpisodeCaptures.Any())
{
var first = Convert.ToInt32(absoluteEpisodeCaptures.First().Value);
var last = Convert.ToInt32(absoluteEpisodeCaptures.Last().Value);
if (first > last)
{
return null;
}
var count = last - first + 1;
result.AbsoluteEpisodeNumbers = Enumerable.Range(first, count).ToArray();
if (matchGroup.Groups["special"].Success)
{
result.Special = true;
}
}
if (!episodeCaptures.Any() && !absoluteEpisodeCaptures.Any())
{
//Check to see if this is an "Extras" or "SUBPACK" release, if it is, return NULL
//Todo: Set a "Extras" flag in EpisodeParseResult if we want to download them ever
if (!matchCollection[0].Groups["extras"].Value.IsNullOrWhiteSpace()) return null;
result.FullSeason = true;
}
}
if (result.AbsoluteEpisodeNumbers.Any() && !result.EpisodeNumbers.Any())
{
result.SeasonNumber = 0;
}
}
else
{
//Try to Parse as a daily show
var airmonth = Convert.ToInt32(matchCollection[0].Groups["airmonth"].Value);
var airday = Convert.ToInt32(matchCollection[0].Groups["airday"].Value);
//Swap day and month if month is bigger than 12 (scene fail)
if (airmonth > 12)
{
var tempDay = airday;
airday = airmonth;
airmonth = tempDay;
}
DateTime airDate;
try
{
airDate = new DateTime(airYear, airmonth, airday);
}
catch (Exception)
{
throw new InvalidDateException("Invalid date found: {0}-{1}-{2}", airYear, airmonth, airday);
}
//Check if episode is in the future (most likely a parse error)
if (airDate > DateTime.Now.AddDays(1).Date || airDate < new DateTime(1970, 1, 1))
{
throw new InvalidDateException("Invalid date found: {0}", airDate);
}
result = new ParsedEpisodeInfo
{
AirDate = airDate.ToString(Episode.AIR_DATE_FORMAT),
};
}
result.SeriesTitle = seriesName;
result.SeriesTitleInfo = GetSeriesTitleInfo(result.SeriesTitle);
Logger.Debug("Episode Parsed. {0}", result);
return result;
}
private static bool ValidateBeforeParsing(string title)
{
if (title.ToLower().Contains("password") && title.ToLower().Contains("yenc"))

@ -1,4 +1,4 @@
namespace NzbDrone.Core.Parser
namespace NzbDrone.Core.Parser
{
public static class SceneChecker
{
@ -9,12 +9,12 @@
if (!title.Contains(".")) return false;
if (title.Contains(" ")) return false;
var parsedTitle = Parser.ParseTitle(title);
var parsedTitle = Parser.ParseMusicTitle(title);
if (parsedTitle == null ||
parsedTitle.ReleaseGroup == null ||
parsedTitle.Quality.Quality == Qualities.Quality.Unknown ||
string.IsNullOrWhiteSpace(parsedTitle.SeriesTitle))
string.IsNullOrWhiteSpace(parsedTitle.ArtistTitle))
{
return false;
}

@ -1,17 +0,0 @@
using System.Collections.Generic;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Tv
{
public class Actor : IEmbeddedDocument
{
public Actor()
{
Images = new List<MediaCover.MediaCover>();
}
public string Name { get; set; }
public string Character { get; set; }
public List<MediaCover.MediaCover> Images { get; set; }
}
}

@ -1,8 +0,0 @@
namespace NzbDrone.Core.Tv
{
public class AddSeriesOptions : MonitoringOptions
{
public bool SearchForMissingEpisodes { get; set; }
}
}

@ -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,22 +0,0 @@
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Tv.Commands
{
public class RefreshSeriesCommand : Command
{
public int? SeriesId { get; set; }
public RefreshSeriesCommand()
{
}
public RefreshSeriesCommand(int? seriesId)
{
SeriesId = seriesId;
}
public override bool SendUpdatesToClient => true;
public override bool UpdateScheduledTask => !SeriesId.HasValue;
}
}

@ -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,109 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Core.Tv
{
public interface IEpisodeMonitoredService
{
void SetEpisodeMonitoredStatus(Series series, MonitoringOptions monitoringOptions);
}
public class EpisodeMonitoredService : IEpisodeMonitoredService
{
private readonly ISeriesService _seriesService;
private readonly IEpisodeService _episodeService;
private readonly Logger _logger;
public EpisodeMonitoredService(ISeriesService seriesService, IEpisodeService episodeService, Logger logger)
{
_seriesService = seriesService;
_episodeService = episodeService;
_logger = logger;
}
public void SetEpisodeMonitoredStatus(Series series, MonitoringOptions monitoringOptions)
{
if (monitoringOptions != null)
{
_logger.Debug("[{0}] Setting episode monitored status.", series.Title);
var episodes = _episodeService.GetEpisodeBySeries(series.Id);
if (monitoringOptions.IgnoreEpisodesWithFiles)
{
_logger.Debug("Ignoring Episodes with Files");
ToggleEpisodesMonitoredState(episodes.Where(e => e.HasFile), false);
}
else
{
_logger.Debug("Monitoring Episodes with Files");
ToggleEpisodesMonitoredState(episodes.Where(e => e.HasFile), true);
}
if (monitoringOptions.IgnoreEpisodesWithoutFiles)
{
_logger.Debug("Ignoring Episodes without Files");
ToggleEpisodesMonitoredState(episodes.Where(e => !e.HasFile && e.AirDateUtc.HasValue && e.AirDateUtc.Value.Before(DateTime.UtcNow)), false);
}
else
{
_logger.Debug("Monitoring Episodes without Files");
ToggleEpisodesMonitoredState(episodes.Where(e => !e.HasFile && e.AirDateUtc.HasValue && e.AirDateUtc.Value.Before(DateTime.UtcNow)), true);
}
var lastSeason = series.Seasons.Select(s => s.SeasonNumber).MaxOrDefault();
foreach (var s in series.Seasons)
{
var season = s;
if (season.Monitored)
{
if (!monitoringOptions.IgnoreEpisodesWithFiles && !monitoringOptions.IgnoreEpisodesWithoutFiles)
{
ToggleEpisodesMonitoredState(episodes.Where(e => e.SeasonNumber == season.SeasonNumber), true);
}
}
else
{
if (!monitoringOptions.IgnoreEpisodesWithFiles && !monitoringOptions.IgnoreEpisodesWithoutFiles)
{
ToggleEpisodesMonitoredState(episodes.Where(e => e.SeasonNumber == season.SeasonNumber), false);
}
else if (season.SeasonNumber == 0)
{
ToggleEpisodesMonitoredState(episodes.Where(e => e.SeasonNumber == season.SeasonNumber), false);
}
}
if (season.SeasonNumber < lastSeason)
{
if (episodes.Where(e => e.SeasonNumber == season.SeasonNumber).All(e => !e.Monitored))
{
season.Monitored = false;
}
}
}
_episodeService.UpdateEpisodes(episodes);
}
_seriesService.UpdateSeries(series);
}
private void ToggleEpisodesMonitoredState(IEnumerable<Episode> episodes, bool monitored)
{
foreach (var episode in episodes)
{
episode.Monitored = monitored;
}
}
}
}

@ -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,20 +0,0 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Tv.Events
{
public class EpisodeInfoRefreshedEvent : IEvent
{
public Series Series { get; set; }
public ReadOnlyCollection<Episode> Added { get; private set; }
public ReadOnlyCollection<Episode> Updated { get; private set; }
public EpisodeInfoRefreshedEvent(Series series, IList<Episode> added, IList<Episode> updated)
{
Series = series;
Added = new ReadOnlyCollection<Episode>(added);
Updated = new ReadOnlyCollection<Episode>(updated);
}
}
}

@ -1,14 +0,0 @@
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Tv.Events
{
public class SeriesAddedEvent : IEvent
{
public Series Series { get; private set; }
public SeriesAddedEvent(Series series)
{
Series = series;
}
}
}

@ -1,16 +0,0 @@
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Tv.Events
{
public class SeriesDeletedEvent : IEvent
{
public Series Series { get; private set; }
public bool DeleteFiles { get; private set; }
public SeriesDeletedEvent(Series series, bool deleteFiles)
{
Series = series;
DeleteFiles = deleteFiles;
}
}
}

@ -1,16 +0,0 @@
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Tv.Events
{
public class SeriesEditedEvent : IEvent
{
public Series Series { get; private set; }
public Series OldSeries { get; private set; }
public SeriesEditedEvent(Series series, Series oldSeries)
{
Series = series;
OldSeries = oldSeries;
}
}
}

@ -1,15 +0,0 @@
using System.Collections.Generic;
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Tv.Events
{
public class SeriesImportedEvent : IEvent
{
public List<int> SeriesIds { get; private set; }
public SeriesImportedEvent(List<int> seriesIds)
{
SeriesIds = seriesIds;
}
}
}

@ -1,14 +0,0 @@
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Tv.Events
{
public class SeriesRefreshStartingEvent : IEvent
{
public bool ManualTrigger { get; set; }
public SeriesRefreshStartingEvent(bool manualTrigger)
{
ManualTrigger = manualTrigger;
}
}
}

@ -1,14 +0,0 @@
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Tv.Events
{
public class SeriesUpdatedEvent : IEvent
{
public Series Series { get; private set; }
public SeriesUpdatedEvent(Series series)
{
Series = series;
}
}
}

@ -1,10 +0,0 @@
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Tv
{
public class MonitoringOptions : IEmbeddedDocument
{
public bool IgnoreEpisodesWithFiles { get; set; }
public bool IgnoreEpisodesWithoutFiles { get; set; }
}
}

@ -1,10 +0,0 @@
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Tv
{
public class Ratings : IEmbeddedDocument
{
public int Votes { get; set; }
public decimal Value { get; set; }
}
}

@ -1,199 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Tv.Events;
namespace NzbDrone.Core.Tv
{
public interface IRefreshEpisodeService
{
void RefreshEpisodeInfo(Series series, IEnumerable<Episode> remoteEpisodes);
}
public class RefreshEpisodeService : IRefreshEpisodeService
{
private readonly IEpisodeService _episodeService;
private readonly IEventAggregator _eventAggregator;
private readonly Logger _logger;
public RefreshEpisodeService(IEpisodeService episodeService, IEventAggregator eventAggregator, Logger logger)
{
_episodeService = episodeService;
_eventAggregator = eventAggregator;
_logger = logger;
}
public void RefreshEpisodeInfo(Series series, IEnumerable<Episode> remoteEpisodes)
{
_logger.Info("Starting episode info refresh for: {0}", series);
var successCount = 0;
var failCount = 0;
var existingEpisodes = _episodeService.GetEpisodeBySeries(series.Id);
var seasons = series.Seasons;
var updateList = new List<Episode>();
var newList = new List<Episode>();
var dupeFreeRemoteEpisodes = remoteEpisodes.DistinctBy(m => new { m.SeasonNumber, m.EpisodeNumber }).ToList();
if (series.SeriesType == SeriesTypes.Anime)
{
dupeFreeRemoteEpisodes = MapAbsoluteEpisodeNumbers(dupeFreeRemoteEpisodes);
}
foreach (var episode in OrderEpisodes(series, dupeFreeRemoteEpisodes))
{
try
{
var episodeToUpdate = GetEpisodeToUpdate(series, episode, existingEpisodes);
if (episodeToUpdate != null)
{
existingEpisodes.Remove(episodeToUpdate);
updateList.Add(episodeToUpdate);
}
else
{
episodeToUpdate = new Episode();
episodeToUpdate.Monitored = GetMonitoredStatus(episode, seasons);
newList.Add(episodeToUpdate);
}
episodeToUpdate.SeriesId = series.Id;
episodeToUpdate.EpisodeNumber = episode.EpisodeNumber;
episodeToUpdate.SeasonNumber = episode.SeasonNumber;
episodeToUpdate.AbsoluteEpisodeNumber = episode.AbsoluteEpisodeNumber;
episodeToUpdate.Title = episode.Title ?? "TBA";
episodeToUpdate.Overview = episode.Overview;
episodeToUpdate.AirDate = episode.AirDate;
episodeToUpdate.AirDateUtc = episode.AirDateUtc;
episodeToUpdate.Ratings = episode.Ratings;
episodeToUpdate.Images = episode.Images;
successCount++;
}
catch (Exception e)
{
_logger.Fatal(e, "An error has occurred while updating episode info for series {0}. {1}", series, episode);
failCount++;
}
}
var allEpisodes = new List<Episode>();
allEpisodes.AddRange(newList);
allEpisodes.AddRange(updateList);
AdjustMultiEpisodeAirTime(series, allEpisodes);
AdjustDirectToDvdAirDate(series, allEpisodes);
_episodeService.DeleteMany(existingEpisodes);
_episodeService.UpdateMany(updateList);
_episodeService.InsertMany(newList);
_eventAggregator.PublishEvent(new EpisodeInfoRefreshedEvent(series, newList, updateList));
if (failCount != 0)
{
_logger.Info("Finished episode refresh for series: {0}. Successful: {1} - Failed: {2} ",
series.Title, successCount, failCount);
}
else
{
_logger.Info("Finished episode refresh for series: {0}.", series);
}
}
private bool GetMonitoredStatus(Episode episode, IEnumerable<Season> seasons)
{
if (episode.EpisodeNumber == 0 && episode.SeasonNumber != 1)
{
return false;
}
var season = seasons.SingleOrDefault(c => c.SeasonNumber == episode.SeasonNumber);
return season == null || season.Monitored;
}
private void AdjustMultiEpisodeAirTime(Series series, IEnumerable<Episode> allEpisodes)
{
if (series.Network == "Netflix")
{
_logger.Debug("Not adjusting episode air times for Netflix series {0}", series.Title);
return;
}
var groups = allEpisodes.Where(c => c.AirDateUtc.HasValue)
.GroupBy(e => new { e.SeasonNumber, e.AirDate })
.Where(g => g.Count() > 1)
.ToList();
foreach (var group in groups)
{
var episodeCount = 0;
foreach (var episode in group.OrderBy(e => e.SeasonNumber).ThenBy(e => e.EpisodeNumber))
{
episode.AirDateUtc = episode.AirDateUtc.Value.AddMinutes(series.Runtime * episodeCount);
episodeCount++;
}
}
}
private void AdjustDirectToDvdAirDate(Series series, IEnumerable<Episode> allEpisodes)
{
if (series.Status == SeriesStatusType.Ended && allEpisodes.All(v => !v.AirDateUtc.HasValue) && series.FirstAired.HasValue)
{
foreach (var episode in allEpisodes)
{
episode.AirDateUtc = series.FirstAired;
episode.AirDate = series.FirstAired.Value.ToString("yyyy-MM-dd");
}
}
}
private List<Episode> MapAbsoluteEpisodeNumbers(List<Episode> remoteEpisodes)
{
//Return all episodes with no abs number, but distinct for those with abs number
return remoteEpisodes.Where(e => e.AbsoluteEpisodeNumber.HasValue)
.OrderByDescending(e => e.SeasonNumber)
.DistinctBy(e => e.AbsoluteEpisodeNumber.Value)
.Concat(remoteEpisodes.Where(e => !e.AbsoluteEpisodeNumber.HasValue))
.ToList();
}
private Episode GetEpisodeToUpdate(Series series, Episode episode, List<Episode> existingEpisodes)
{
if (series.SeriesType == SeriesTypes.Anime)
{
if (episode.AbsoluteEpisodeNumber.HasValue)
{
var matchingEpisode = existingEpisodes.FirstOrDefault(e => e.AbsoluteEpisodeNumber == episode.AbsoluteEpisodeNumber);
if (matchingEpisode != null) return matchingEpisode;
}
}
return existingEpisodes.FirstOrDefault(e => e.SeasonNumber == episode.SeasonNumber && e.EpisodeNumber == episode.EpisodeNumber);
}
private IEnumerable<Episode> OrderEpisodes(Series series, List<Episode> episodes)
{
if (series.SeriesType == SeriesTypes.Anime)
{
var withAbs = episodes.Where(e => e.AbsoluteEpisodeNumber.HasValue)
.OrderBy(e => e.AbsoluteEpisodeNumber);
var withoutAbs = episodes.Where(e => !e.AbsoluteEpisodeNumber.HasValue)
.OrderBy(e => e.SeasonNumber)
.ThenBy(e => e.EpisodeNumber);
return withAbs.Concat(withoutAbs);
}
return episodes.OrderBy(e => e.SeasonNumber).ThenBy(e => e.EpisodeNumber);
}
}
}

@ -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,17 +0,0 @@
using System.Collections.Generic;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Tv
{
public class Season : IEmbeddedDocument
{
public Season()
{
Images = new List<MediaCover.MediaCover>();
}
public int SeasonNumber { get; set; }
public bool Monitored { get; set; }
public List<MediaCover.MediaCover> Images { get; set; }
}
}

@ -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,25 +0,0 @@
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 SeriesEditedService : IHandle<SeriesEditedEvent>
{
private readonly IManageCommandQueue _commandQueueManager;
public SeriesEditedService(IManageCommandQueue commandQueueManager)
{
_commandQueueManager = commandQueueManager;
}
public void Handle(SeriesEditedEvent message)
{
if (message.Series.SeriesType != message.OldSeries.SeriesType)
{
_commandQueueManager.Push(new RefreshSeriesCommand(message.Series.Id));
}
}
}
}

@ -1,56 +0,0 @@
using System.Linq;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Tv
{
public interface ISeriesRepository : IBasicRepository<Series>
{
bool SeriesPathExists(string path);
Series FindByTitle(string cleanTitle);
Series FindByTitle(string cleanTitle, int year);
Series FindByTvdbId(int tvdbId);
Series FindByTvRageId(int tvRageId);
}
public class SeriesRepository : BasicRepository<Series>, ISeriesRepository
{
public SeriesRepository(IMainDatabase database, IEventAggregator eventAggregator)
: base(database, eventAggregator)
{
}
public bool SeriesPathExists(string path)
{
return Query.Where(c => c.Path == path).Any();
}
public Series FindByTitle(string cleanTitle)
{
cleanTitle = cleanTitle.ToLowerInvariant();
return Query.Where(s => s.CleanTitle == cleanTitle)
.SingleOrDefault();
}
public Series FindByTitle(string cleanTitle, int year)
{
cleanTitle = cleanTitle.ToLowerInvariant();
return Query.Where(s => s.CleanTitle == cleanTitle)
.AndWhere(s => s.Year == year)
.SingleOrDefault();
}
public Series FindByTvdbId(int tvdbId)
{
return Query.Where(s => s.TvdbId == tvdbId).SingleOrDefault();
}
public Series FindByTvRageId(int tvRageId)
{
return Query.Where(s => s.TvRageId == tvRageId).SingleOrDefault();
}
}
}

@ -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);
}
}
}

@ -1,8 +0,0 @@
namespace NzbDrone.Core.Tv
{
public enum SeriesStatusType
{
Continuing = 0,
Ended = 1
}
}

@ -1,24 +0,0 @@
using System.Collections.Generic;
namespace NzbDrone.Core.Tv
{
public static class SeriesTitleNormalizer
{
private readonly static Dictionary<int, string> PreComputedTitles = new Dictionary<int, string>
{
{ 281588, "a to z" },
{ 266757, "ad trials triumph early church" },
{ 289260, "ad bible continues"}
};
public static string Normalize(string title, int tvdbId)
{
if (PreComputedTitles.ContainsKey(tvdbId))
{
return PreComputedTitles[tvdbId];
}
return Parser.Parser.NormalizeTitle(title).ToLower();
}
}
}

@ -1,25 +0,0 @@
using FluentValidation.Validators;
namespace NzbDrone.Core.Tv
{
public class SeriesTitleSlugValidator : PropertyValidator
{
private readonly ISeriesService _seriesService;
public SeriesTitleSlugValidator(ISeriesService seriesService)
: base("Title slug is in use by another series with a similar name")
{
_seriesService = seriesService;
}
protected override bool IsValid(PropertyValidatorContext context)
{
if (context.PropertyValue == null) return true;
dynamic instance = context.ParentContext.InstanceToValidate;
var instanceId = (int)instance.Id;
return !_seriesService.GetAllSeries().Exists(s => s.TitleSlug.Equals(context.PropertyValue.ToString()) && s.Id != instanceId);
}
}
}

@ -1,9 +0,0 @@
namespace NzbDrone.Core.Tv
{
public enum SeriesTypes
{
Standard = 0,
Daily = 1,
Anime = 2,
}
}
Loading…
Cancel
Save