1
0
Fork 0
mirror of https://github.com/lidarr/Lidarr synced 2024-12-24 16:51:58 +00:00

Fixed: Extrapolate scene numbering but won't auto import.

This commit is contained in:
Taloth Saldono 2015-07-04 20:48:26 +02:00
parent 23bd9440b3
commit 6d046a8df8
8 changed files with 240 additions and 4 deletions

View file

@ -24,6 +24,7 @@ public class EpisodeResource : RestResource
public Nullable<Int32> SceneAbsoluteEpisodeNumber { get; set; }
public Nullable<Int32> SceneEpisodeNumber { get; set; }
public Nullable<Int32> SceneSeasonNumber { get; set; }
public Boolean UnverifiedSceneNumbering { get; set; }
public DateTime? EndTime { get; set; }
public DateTime? GrabDate { get; set; }
public String SeriesTitle { get; set; }

View file

@ -48,6 +48,8 @@ public void SetUp()
_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>()))
@ -143,5 +145,122 @@ public void should_not_clear_scenenumbering_if_no_results_at_all_from_thexem()
Mocker.GetMock<ISeriesService>()
.Verify(v => v.UpdateSeries(It.IsAny<Series>()), Times.Never());
}
[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_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();
}
}
}

View file

@ -52,6 +52,7 @@ private void PerformUpdate(Series series)
episode.SceneAbsoluteEpisodeNumber = null;
episode.SceneSeasonNumber = null;
episode.SceneEpisodeNumber = null;
episode.UnverifiedSceneNumbering = false;
}
foreach (var mapping in mappings)
@ -71,6 +72,11 @@ private void PerformUpdate(Series series)
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);
@ -83,6 +89,70 @@ private void PerformUpdate(Series 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 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))
{
episode.UnverifiedSceneNumbering = true;
}
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 RefreshCache()
{
var ids = _xemProxy.GetXemSeriesIds();

View file

@ -0,0 +1,14 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(92)]
public class add_unverifiedscenenumbering : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("Episodes").AddColumn("UnverifiedSceneNumbering").AsBoolean().WithDefaultValue(false);
}
}
}

View file

@ -0,0 +1,27 @@
using System.Linq;
using NLog;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
{
public class UnverifiedSceneNumberingSpecification : IImportDecisionEngineSpecification
{
private readonly Logger _logger;
public UnverifiedSceneNumberingSpecification(Logger logger)
{
_logger = logger;
}
public Decision IsSatisfiedBy(LocalEpisode localEpisode)
{
if (localEpisode.Episodes.Any(v => v.UnverifiedSceneNumbering))
{
_logger.Debug("This file uses unverified scene numbers, will not auto-import until numbering is confirmed on TheXEM. Skipping {0}", localEpisode.Path);
return Decision.Reject("This show has individual episode mappings on TheXEM but the mapping for this episode has not been confirmed yet by their administrators. TheXEM needs manual input.");
}
return Decision.Accept();
}
}
}

View file

@ -264,6 +264,7 @@
<Compile Include="Datastore\Migration\083_additonal_blacklist_columns.cs" />
<Compile Include="Datastore\Migration\082_add_fanzub_settings.cs" />
<Compile Include="Datastore\Migration\088_pushbullet_devices_channels_list.cs" />
<Compile Include="Datastore\Migration\092_add_unverifiedscenenumbering.cs" />
<Compile Include="Datastore\Migration\090_update_kickass_url.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
@ -621,6 +622,7 @@
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotSampleSpecification.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotUnpackingSpecification.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\SameEpisodesImportSpecification.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\UnverifiedSceneNumberingSpecification.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\UpgradeSpecification.cs" />
<Compile Include="MediaFiles\Events\EpisodeDownloadedEvent.cs" />
<Compile Include="MediaFiles\Events\EpisodeFileAddedEvent.cs" />

View file

@ -29,6 +29,7 @@ public Episode()
public Nullable<Int32> SceneAbsoluteEpisodeNumber { get; set; }
public Nullable<Int32> SceneSeasonNumber { get; set; }
public Nullable<Int32> SceneEpisodeNumber { get; set; }
public bool UnverifiedSceneNumbering { get; set; }
public Ratings Ratings { get; set; }
public List<MediaCover.MediaCover> Images { get; set; }

View file

@ -7,10 +7,12 @@ module.exports = NzbDroneCell.extend({
render : function() {
this.$el.empty();
if (SeriesCollection.get(this.model.get('seriesId')).get('seriesType') === 'anime') {
if (this.model.get('seasonNumber') > 0 && !this.model.has('absoluteEpisodeNumber')) {
this.$el.html('<i class="icon-sonarr-form-warning" title="Episode does not have an absolute episode number"></i>');
}
if (this.model.get('unverifiedSceneNumbering')) {
this.$el.html('<i class="icon-sonarr-form-warning" title="Scene number hasn\'t been verified yet."></i>');
}
else if (SeriesCollection.get(this.model.get('seriesId')).get('seriesType') === 'anime' && this.model.get('seasonNumber') > 0 && !this.model.has('absoluteEpisodeNumber')) {
this.$el.html('<i class="icon-sonarr-form-warning" title="Episode does not have an absolute episode number"></i>');
}
this.delegateEvents();