Fixed: Will no longer cause an error when trying to parse an anime episode with absolute number 0.

pull/4/head
Taloth Saldono 10 years ago
parent e978a425c2
commit 6dfbc3c290

@ -20,11 +20,11 @@ namespace NzbDrone.Api.Episodes
public Boolean HasFile { get; set; }
public Boolean Monitored { get; set; }
public Nullable<Int32> AbsoluteEpisodeNumber { get; set; }
public Nullable<Int32> SceneAbsoluteEpisodeNumber { get; set; }
public Int32 SceneEpisodeNumber { get; set; }
public Int32 SceneSeasonNumber { get; set; }
public Nullable<Int32> SceneEpisodeNumber { get; set; }
public Nullable<Int32> SceneSeasonNumber { get; set; }
public Int32 TvDbEpisodeId { get; set; }
public Int32? AbsoluteEpisodeNumber { get; set; }
public DateTime? EndTime { get; set; }
public DateTime? GrabDate { get; set; }
public String SeriesTitle { get; set; }

@ -54,7 +54,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
.Returns(new List<String>());
}
private void WithEpisode(int seasonNumber, int episodeNumber, int sceneSeasonNumber, int sceneEpisodeNumber)
private void WithEpisode(Int32 seasonNumber, Int32 episodeNumber, Int32? sceneSeasonNumber, Int32? sceneEpisodeNumber)
{
var episode = Builder<Episode>.CreateNew()
.With(v => v.SeriesId == _xemSeries.Id)
@ -90,8 +90,8 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
WithEpisode(5, 1, 6, 12);
// Season 7+ maps normally, so no mapping specified.
WithEpisode(7, 1, 0, 0);
WithEpisode(7, 2, 0, 0);
WithEpisode(7, 1, null, null);
WithEpisode(7, 2, null, null);
}
private List<SearchCriteriaBase> WatchForSearchCriteria()

@ -530,10 +530,10 @@ namespace NzbDrone.Core.Test.OrganizerTests
}
[Test]
public void should_use_standard_naming_when_anime_episode_has_absolute_number_of_zero()
public void should_use_standard_naming_when_anime_episode_has_no_absolute_number()
{
_series.SeriesType = SeriesTypes.Anime;
_episode1.AbsoluteEpisodeNumber = 0;
_episode1.AbsoluteEpisodeNumber = null;
_namingConfig.StandardEpisodeFormat = "{Series Title} - {season:0}x{episode:00} - {Episode Title}";
_namingConfig.AnimeEpisodeFormat = "{Series Title} - {absolute:000} - {Episode Title}";

@ -39,7 +39,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
[Test]
public void should_find_episode_by_scene_numbering()
{
Subject.FindEpisodesBySceneNumbering(_episode1.SeriesId, _episode1.SceneSeasonNumber, _episode1.SceneEpisodeNumber)
Subject.FindEpisodesBySceneNumbering(_episode1.SeriesId, _episode1.SceneSeasonNumber.Value, _episode1.SceneEpisodeNumber.Value)
.First()
.Id
.Should()
@ -77,7 +77,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
{
_episode2 = Db.Insert(_episode2);
Subject.FindEpisodesBySceneNumbering(_episode1.SeriesId, _episode1.SceneSeasonNumber, _episode1.SceneEpisodeNumber)
Subject.FindEpisodesBySceneNumbering(_episode1.SeriesId, _episode1.SceneSeasonNumber.Value, _episode1.SceneEpisodeNumber.Value)
.Should()
.HaveCount(2);
}

@ -183,7 +183,7 @@ namespace NzbDrone.Core.Test.TvTests
Subject.RefreshEpisodeInfo(GetSeries(), GetEpisodes());
_insertedEpisodes.All(e => e.AbsoluteEpisodeNumber == 0 || !e.AbsoluteEpisodeNumber.HasValue).Should().BeTrue();
_insertedEpisodes.All(e => !e.AbsoluteEpisodeNumber.HasValue).Should().BeTrue();
}
[Test]
@ -197,7 +197,7 @@ namespace NzbDrone.Core.Test.TvTests
Subject.RefreshEpisodeInfo(GetAnimeSeries(), episodes);
_insertedEpisodes.All(e => e.AbsoluteEpisodeNumber > 0).Should().BeTrue();
_insertedEpisodes.All(e => e.AbsoluteEpisodeNumber.HasValue).Should().BeTrue();
_updatedEpisodes.Should().BeEmpty();
_deletedEpisodes.Should().BeEmpty();
}
@ -209,7 +209,7 @@ namespace NzbDrone.Core.Test.TvTests
GivenAnimeEpisodes(episodes);
var existingEpisodes = episodes.JsonClone();
existingEpisodes.ForEach(e => e.AbsoluteEpisodeNumber = 0);
existingEpisodes.ForEach(e => e.AbsoluteEpisodeNumber = null);
Mocker.GetMock<IEpisodeService>().Setup(c => c.GetEpisodeBySeries(It.IsAny<Int32>()))
.Returns(existingEpisodes);
@ -217,7 +217,7 @@ namespace NzbDrone.Core.Test.TvTests
Subject.RefreshEpisodeInfo(GetAnimeSeries(), episodes);
_insertedEpisodes.Should().BeEmpty();
_updatedEpisodes.All(e => e.AbsoluteEpisodeNumber > 0).Should().BeTrue();
_updatedEpisodes.All(e => e.AbsoluteEpisodeNumber.HasValue).Should().BeTrue();
_deletedEpisodes.Should().BeEmpty();
}
@ -261,10 +261,9 @@ namespace NzbDrone.Core.Test.TvTests
.Build()
.ToList();
episodes[0].AbsoluteEpisodeNumber = 0;
episodes[0].AbsoluteEpisodeNumber = null;
episodes[0].SeasonNumber.Should().NotBe(episodes[1].SeasonNumber);
episodes[0].EpisodeNumber.Should().NotBe(episodes[1].EpisodeNumber);
episodes[0].AbsoluteEpisodeNumber.Should().NotBe(episodes[1].AbsoluteEpisodeNumber);
GivenAnimeEpisodes(episodes);
@ -286,17 +285,17 @@ namespace NzbDrone.Core.Test.TvTests
}
[Test]
public void should_ignore_episodes_with_absolute_episode_of_zero_in_distinct_by_absolute()
public void should_ignore_episodes_with_no_absolute_episode_in_distinct_by_absolute()
{
var episodes = Builder<Episode>.CreateListOfSize(10)
.Build()
.ToList();
episodes[0].AbsoluteEpisodeNumber = 0;
episodes[1].AbsoluteEpisodeNumber = 0;
episodes[2].AbsoluteEpisodeNumber = 0;
episodes[3].AbsoluteEpisodeNumber = 0;
episodes[4].AbsoluteEpisodeNumber = 0;
episodes[0].AbsoluteEpisodeNumber = null;
episodes[1].AbsoluteEpisodeNumber = null;
episodes[2].AbsoluteEpisodeNumber = null;
episodes[3].AbsoluteEpisodeNumber = null;
episodes[4].AbsoluteEpisodeNumber = null;
GivenAnimeEpisodes(episodes);

@ -49,9 +49,9 @@ namespace NzbDrone.Core.DataAugmentation.Xem
foreach (var episode in episodes)
{
episode.SceneAbsoluteEpisodeNumber = 0;
episode.SceneSeasonNumber = 0;
episode.SceneEpisodeNumber = 0;
episode.SceneAbsoluteEpisodeNumber = null;
episode.SceneSeasonNumber = null;
episode.SceneEpisodeNumber = null;
}
foreach (var mapping in mappings)

@ -0,0 +1,16 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(65)]
public class make_scene_numbering_nullable : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Execute.Sql("UPDATE Episodes SET AbsoluteEpisodeNumber = NULL WHERE AbsoluteEpisodeNumber = 0");
Execute.Sql("UPDATE Episodes SET SceneAbsoluteEpisodeNumber = NULL WHERE SceneAbsoluteEpisodeNumber = 0");
Execute.Sql("UPDATE Episodes SET SceneSeasonNumber = NULL, SceneEpisodeNumber = NULL WHERE SceneSeasonNumber = 0 AND SceneEpisodeNumber = 0");
}
}
}

@ -139,32 +139,24 @@ namespace NzbDrone.Core.Download
var downloadClientHistory = downloadClient.GetItems().ToList();
foreach (var downloadItem in downloadClientHistory)
{
try
{
var trackingId = String.Format("{0}-{1}", downloadClient.Definition.Id, downloadItem.DownloadClientId);
TrackedDownload trackedDownload;
var trackingId = String.Format("{0}-{1}", downloadClient.Definition.Id, downloadItem.DownloadClientId);
TrackedDownload trackedDownload;
if (newTrackedDownloads.ContainsKey(trackingId)) continue;
if (newTrackedDownloads.ContainsKey(trackingId)) continue;
if (!oldTrackedDownloads.TryGetValue(trackingId, out trackedDownload))
{
trackedDownload = GetTrackedDownload(trackingId, downloadClient.Definition.Id, downloadItem,
grabbedHistory);
if (!oldTrackedDownloads.TryGetValue(trackingId, out trackedDownload))
{
trackedDownload = GetTrackedDownload(trackingId, downloadClient.Definition.Id, downloadItem, grabbedHistory);
if (trackedDownload == null) continue;
if (trackedDownload == null) continue;
_logger.Debug("[{0}] Started tracking download with id {1}.", downloadItem.Title, trackingId);
stateChanged = true;
}
_logger.Debug("[{0}] Started tracking download with id {1}.", downloadItem.Title, trackingId);
stateChanged = true;
}
trackedDownload.DownloadItem = downloadItem;
trackedDownload.DownloadItem = downloadItem;
newTrackedDownloads[trackingId] = trackedDownload;
}
catch (Exception e)
{
_logger.ErrorException("An error occured while tracking download." + downloadItem.Title, e);
}
newTrackedDownloads[trackingId] = trackedDownload;
}
}
@ -243,34 +235,42 @@ namespace NzbDrone.Core.Download
Status = TrackedDownloadStatus.Ok,
};
var historyItems = grabbedHistory.Where(h =>
{
var downloadClientId = h.Data.GetValueOrDefault(DOWNLOAD_CLIENT_ID);
if (downloadClientId == null) return false;
try
{
var historyItems = grabbedHistory.Where(h =>
{
var downloadClientId = h.Data.GetValueOrDefault(DOWNLOAD_CLIENT_ID);
if (downloadClientId == null) return false;
return downloadClientId.Equals(trackedDownload.DownloadItem.DownloadClientId);
}).ToList();
return downloadClientId.Equals(trackedDownload.DownloadItem.DownloadClientId);
}).ToList();
var parsedEpisodeInfo = Parser.Parser.ParseTitle(trackedDownload.DownloadItem.Title);
if (parsedEpisodeInfo == null) return null;
var parsedEpisodeInfo = Parser.Parser.ParseTitle(trackedDownload.DownloadItem.Title);
if (parsedEpisodeInfo == null) return null;
var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0);
var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0);
if (remoteEpisode.Series == null)
{
if (historyItems.Empty()) return null;
if (remoteEpisode.Series == null)
{
if (historyItems.Empty()) return null;
trackedDownload.Status = TrackedDownloadStatus.Warning;
trackedDownload.StatusMessages.Add(new TrackedDownloadStatusMessage(
trackedDownload.DownloadItem.Title,
"Series title mismatch, automatic import is not possible")
);
trackedDownload.Status = TrackedDownloadStatus.Warning;
trackedDownload.StatusMessages.Add(new TrackedDownloadStatusMessage(
trackedDownload.DownloadItem.Title,
"Series title mismatch, automatic import is not possible")
);
remoteEpisode = _parsingService.Map(parsedEpisodeInfo, historyItems.First().SeriesId, historyItems.Select(h => h.EpisodeId));
}
remoteEpisode = _parsingService.Map(parsedEpisodeInfo, historyItems.First().SeriesId, historyItems.Select(h => h.EpisodeId));
trackedDownload.RemoteEpisode = remoteEpisode;
}
catch (Exception e)
{
_logger.DebugException("Failed to find episode for " + downloadItem.Title, e);
return null;
}
trackedDownload.RemoteEpisode = remoteEpisode;
return trackedDownload;
}

@ -103,7 +103,7 @@ namespace NzbDrone.Core.Download
}
else
{
if (FailedDownloadForRecentRelease(downloadClient, trackedDownload, grabbedItems))
if (FailedDownloadForRecentRelease(downloadClient, trackedDownload, grabbedItems))
{
_logger.Debug("[{0}] Recent release Failed, do not blacklist.", trackedDownload.DownloadItem.Title);
return;

@ -102,13 +102,14 @@ namespace NzbDrone.Core.IndexerSearch
{
var sceneSeasonGroups = episodes.GroupBy(v =>
{
if (v.SceneSeasonNumber == 0 && v.SceneEpisodeNumber == 0)
if (v.SceneSeasonNumber.HasValue && v.SceneEpisodeNumber.HasValue)
{
return v.SceneSeasonNumber.Value;
}
else
{
return v.SeasonNumber;
}
return v.SceneSeasonNumber;
}).Distinct();
foreach (var sceneSeasonEpisodes in sceneSeasonGroups)
@ -118,10 +119,10 @@ namespace NzbDrone.Core.IndexerSearch
var episode = sceneSeasonEpisodes.First();
var searchSpec = Get<SingleEpisodeSearchCriteria>(series, sceneSeasonEpisodes.ToList());
searchSpec.SeasonNumber = sceneSeasonEpisodes.Key;
if (episode.SceneSeasonNumber == 0 && episode.SceneEpisodeNumber == 0)
searchSpec.EpisodeNumber = episode.EpisodeNumber;
if (episode.SceneSeasonNumber.HasValue && episode.SceneEpisodeNumber.HasValue)
searchSpec.EpisodeNumber = episode.SceneEpisodeNumber.Value;
else
searchSpec.EpisodeNumber = episode.SceneEpisodeNumber;
searchSpec.EpisodeNumber = episode.EpisodeNumber;
var decisions = Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec);
downloadDecisions.AddRange(decisions);
@ -152,19 +153,10 @@ namespace NzbDrone.Core.IndexerSearch
{
var searchSpec = Get<SingleEpisodeSearchCriteria>(series, new List<Episode>{episode});
if (series.UseSceneNumbering)
if (series.UseSceneNumbering && episode.SceneSeasonNumber.HasValue && episode.SceneEpisodeNumber.HasValue)
{
if (episode.SceneSeasonNumber > 0 && episode.SceneEpisodeNumber > 0)
{
searchSpec.EpisodeNumber = episode.SceneEpisodeNumber;
searchSpec.SeasonNumber = episode.SceneSeasonNumber;
}
else
{
searchSpec.EpisodeNumber = episode.EpisodeNumber;
searchSpec.SeasonNumber = episode.SeasonNumber;
}
searchSpec.EpisodeNumber = episode.SceneEpisodeNumber.Value;
searchSpec.SeasonNumber = episode.SceneSeasonNumber.Value;
}
else
{
@ -187,16 +179,18 @@ namespace NzbDrone.Core.IndexerSearch
private List<DownloadDecision> SearchAnime(Series series, Episode episode)
{
var searchSpec = Get<AnimeEpisodeSearchCriteria>(series, new List<Episode> { episode });
searchSpec.AbsoluteEpisodeNumber = episode.SceneAbsoluteEpisodeNumber.GetValueOrDefault(0);
if (searchSpec.AbsoluteEpisodeNumber == 0)
if (episode.SceneAbsoluteEpisodeNumber.HasValue)
{
searchSpec.AbsoluteEpisodeNumber = episode.AbsoluteEpisodeNumber.GetValueOrDefault(0);
searchSpec.AbsoluteEpisodeNumber = episode.SceneAbsoluteEpisodeNumber.Value;
}
if (searchSpec.AbsoluteEpisodeNumber == 0)
else if (episode.AbsoluteEpisodeNumber.HasValue)
{
searchSpec.AbsoluteEpisodeNumber = episode.AbsoluteEpisodeNumber.Value;
}
else
{
throw new ArgumentOutOfRangeException("AbsoluteEpisodeNumber", "Can not search for an episode absolute episode number of zero");
throw new ArgumentOutOfRangeException("AbsoluteEpisodeNumber", "Can not search for an episode without an absolute episode number");
}
return Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec);
@ -232,7 +226,7 @@ namespace NzbDrone.Core.IndexerSearch
spec.Series = series;
spec.SceneTitles = _sceneMapping.GetSceneNames(series.TvdbId,
episodes.Select(e => e.SeasonNumber)
.Concat(episodes.Select(e => e.SceneSeasonNumber)
.Concat(episodes.Select(e => e.SceneSeasonNumber.Value)
.Distinct()));
spec.Episodes = episodes;

@ -2,6 +2,7 @@
using System.IO;
using System.Linq;
using System.Xml.Linq;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Tv;
@ -39,7 +40,11 @@ namespace NzbDrone.Core.MetadataSource.Tvdb
var episode = new Episode();
episode.SeasonNumber = item.TryGetValue("SeasonNumber", 0);
episode.EpisodeNumber = item.TryGetValue("EpisodeNumber", 0);
episode.AbsoluteEpisodeNumber = item.TryGetValue("absolute_number", 0);
if (item.TryGetValue("absolute_number").IsNotNullOrWhiteSpace())
{
episode.AbsoluteEpisodeNumber = item.TryGetValue("absolute_number", 0);
}
return episode;
}

@ -229,6 +229,7 @@
<Compile Include="Datastore\Migration\061_clear_bad_scene_names.cs" />
<Compile Include="Datastore\Migration\060_remove_enable_from_indexers.cs" />
<Compile Include="Datastore\Migration\062_convert_quality_models.cs" />
<Compile Include="Datastore\Migration\065_make_scene_numbering_nullable.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationExtension.cs" />

@ -111,7 +111,7 @@ namespace NzbDrone.Core.Organizer
pattern = namingConfig.DailyEpisodeFormat;
}
if (series.SeriesType == SeriesTypes.Anime && episodes.All(e => e.AbsoluteEpisodeNumber > 0))
if (series.SeriesType == SeriesTypes.Anime && episodes.All(e => e.AbsoluteEpisodeNumber.HasValue))
{
pattern = namingConfig.AnimeEpisodeFormat;
}

@ -27,8 +27,8 @@ namespace NzbDrone.Core.Tv
public Boolean Monitored { get; set; }
public Nullable<Int32> AbsoluteEpisodeNumber { get; set; }
public Nullable<Int32> SceneAbsoluteEpisodeNumber { get; set; }
public int SceneSeasonNumber { get; set; }
public int SceneEpisodeNumber { get; set; }
public Nullable<Int32> SceneSeasonNumber { get; set; }
public Nullable<Int32> SceneEpisodeNumber { get; set; }
public Ratings Ratings { get; set; }
public List<MediaCover.MediaCover> Images { get; set; }

@ -183,10 +183,10 @@ namespace NzbDrone.Core.Tv
episode.AbsoluteEpisodeNumber = tvdbEpisode.AbsoluteEpisodeNumber;
}
//Return all episodes with abs 0, but distinct by abs for ones greater than 0
return traktEpisodes.Where(e => e.AbsoluteEpisodeNumber > 0)
.DistinctBy(e => e.AbsoluteEpisodeNumber)
.Concat(traktEpisodes.Where(e => e.AbsoluteEpisodeNumber == 0))
//Return all episodes with no abs number, but distinct for those with abs number
return traktEpisodes.Where(e => e.AbsoluteEpisodeNumber.HasValue)
.DistinctBy(e => e.AbsoluteEpisodeNumber.Value)
.Concat(traktEpisodes.Where(e => !e.AbsoluteEpisodeNumber.HasValue))
.ToList();
}
@ -194,7 +194,7 @@ namespace NzbDrone.Core.Tv
{
if (series.SeriesType == SeriesTypes.Anime)
{
if (episode.AbsoluteEpisodeNumber > 0)
if (episode.AbsoluteEpisodeNumber.HasValue)
{
var matchingEpisode = existingEpisodes.FirstOrDefault(e => e.AbsoluteEpisodeNumber == episode.AbsoluteEpisodeNumber);
@ -209,10 +209,10 @@ namespace NzbDrone.Core.Tv
{
if (series.SeriesType == SeriesTypes.Anime)
{
var withAbs = episodes.Where(e => e.AbsoluteEpisodeNumber > 0)
var withAbs = episodes.Where(e => e.AbsoluteEpisodeNumber.HasValue)
.OrderBy(e => e.AbsoluteEpisodeNumber);
var withoutAbs = episodes.Where(e => e.AbsoluteEpisodeNumber == 0)
var withoutAbs = episodes.Where(e => !e.AbsoluteEpisodeNumber.HasValue)
.OrderBy(e => e.SeasonNumber)
.ThenBy(e => e.EpisodeNumber);

@ -36,7 +36,7 @@ define(
episodes = this.cellValue.get(episodeField);
}
if (!absoluteEpisodeNumber) {
if (absoluteEpisodeNumber === undefined) {
absoluteEpisodeNumber = this.cellValue.get(absoluteEpisodeField);
}
@ -62,7 +62,7 @@ define(
result = '{0}x{1}'.format(seasonNumber, paddedEpisodes);
if (absoluteEpisodeNumber > 0 && paddedAbsoluteEpisode) {
if (absoluteEpisodeNumber !== undefined && paddedAbsoluteEpisode) {
result += ' ({0})'.format(paddedAbsoluteEpisode);
}
}

@ -11,7 +11,7 @@ define(
return moment(this.airDate).format('L');
}
else if (this.series.seriesType === 'anime' && this.absoluteEpisodeNumber > 0) {
else if (this.series.seriesType === 'anime' && this.absoluteEpisodeNumber !== undefined) {
return '{0}x{1} ({2})'.format(this.seasonNumber, FormatHelpers.pad(this.episodeNumber, 2), FormatHelpers.pad(this.absoluteEpisodeNumber, 2));
}

@ -42,7 +42,7 @@ define(
if (this.model.get('sceneSeasonNumber') > 0 ||
this.model.get('sceneEpisodeNumber') > 0 ||
(this.model.has('sceneAbsoluteEpisodeNumber') && this.model.get('sceneAbsoluteEpisodeNumber') > 0) ||
this.model.has('sceneAbsoluteEpisodeNumber') ||
alternateTitles.length > 0)
{
this.templateFunction = Marionette.TemplateCache.get(this.template);

@ -15,7 +15,7 @@ define(
if (SeriesCollection.get(this.model.get('seriesId')).get('seriesType') === 'anime') {
if (this.model.get('seasonNumber') > 0 && this.model.get('absoluteEpisodeNumber') === 0) {
if (this.model.get('seasonNumber') > 0 && !this.model.has('absoluteEpisodeNumber')) {
this.$el.html('<i class="icon-nd-form-warning" title="Episode does not have an absolute episode number"></i>');
}
}

Loading…
Cancel
Save