mirror of
https://github.com/Sonarr/Sonarr
synced 2025-02-22 06:01:24 +00:00
Fixed: Extrapolate scene numbering but won't auto import.
This commit is contained in:
parent
23bd9440b3
commit
6d046a8df8
8 changed files with 240 additions and 4 deletions
|
@ -24,6 +24,7 @@ namespace NzbDrone.Api.Episodes
|
||||||
public Nullable<Int32> SceneAbsoluteEpisodeNumber { get; set; }
|
public Nullable<Int32> SceneAbsoluteEpisodeNumber { get; set; }
|
||||||
public Nullable<Int32> SceneEpisodeNumber { get; set; }
|
public Nullable<Int32> SceneEpisodeNumber { get; set; }
|
||||||
public Nullable<Int32> SceneSeasonNumber { get; set; }
|
public Nullable<Int32> SceneSeasonNumber { get; set; }
|
||||||
|
public Boolean UnverifiedSceneNumbering { get; set; }
|
||||||
public DateTime? EndTime { get; set; }
|
public DateTime? EndTime { get; set; }
|
||||||
public DateTime? GrabDate { get; set; }
|
public DateTime? GrabDate { get; set; }
|
||||||
public String SeriesTitle { get; set; }
|
public String SeriesTitle { get; set; }
|
||||||
|
|
|
@ -48,6 +48,8 @@ namespace NzbDrone.Core.Test.DataAugmentation.SceneNumbering
|
||||||
_episodes.Add(new Episode { SeasonNumber = 2, EpisodeNumber = 3 });
|
_episodes.Add(new Episode { SeasonNumber = 2, EpisodeNumber = 3 });
|
||||||
_episodes.Add(new Episode { SeasonNumber = 2, EpisodeNumber = 4 });
|
_episodes.Add(new Episode { SeasonNumber = 2, EpisodeNumber = 4 });
|
||||||
_episodes.Add(new Episode { SeasonNumber = 2, EpisodeNumber = 5 });
|
_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>()
|
Mocker.GetMock<IEpisodeService>()
|
||||||
.Setup(v => v.GetEpisodeBySeries(It.IsAny<int>()))
|
.Setup(v => v.GetEpisodeBySeries(It.IsAny<int>()))
|
||||||
|
@ -143,5 +145,122 @@ namespace NzbDrone.Core.Test.DataAugmentation.SceneNumbering
|
||||||
Mocker.GetMock<ISeriesService>()
|
Mocker.GetMock<ISeriesService>()
|
||||||
.Verify(v => v.UpdateSeries(It.IsAny<Series>()), Times.Never());
|
.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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ namespace NzbDrone.Core.DataAugmentation.Xem
|
||||||
episode.SceneAbsoluteEpisodeNumber = null;
|
episode.SceneAbsoluteEpisodeNumber = null;
|
||||||
episode.SceneSeasonNumber = null;
|
episode.SceneSeasonNumber = null;
|
||||||
episode.SceneEpisodeNumber = null;
|
episode.SceneEpisodeNumber = null;
|
||||||
|
episode.UnverifiedSceneNumbering = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var mapping in mappings)
|
foreach (var mapping in mappings)
|
||||||
|
@ -71,6 +72,11 @@ namespace NzbDrone.Core.DataAugmentation.Xem
|
||||||
episode.SceneEpisodeNumber = mapping.Scene.Episode;
|
episode.SceneEpisodeNumber = mapping.Scene.Episode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (episodes.Any(v => v.SceneEpisodeNumber.HasValue && v.SceneSeasonNumber != 0))
|
||||||
|
{
|
||||||
|
ExtrapolateMappings(series, episodes, mappings);
|
||||||
|
}
|
||||||
|
|
||||||
_episodeService.UpdateEpisodes(episodes);
|
_episodeService.UpdateEpisodes(episodes);
|
||||||
series.UseSceneNumbering = mappings.Any();
|
series.UseSceneNumbering = mappings.Any();
|
||||||
_seriesService.UpdateSeries(series);
|
_seriesService.UpdateSeries(series);
|
||||||
|
@ -83,6 +89,70 @@ namespace NzbDrone.Core.DataAugmentation.Xem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
private void RefreshCache()
|
||||||
{
|
{
|
||||||
var ids = _xemProxy.GetXemSeriesIds();
|
var ids = _xemProxy.GetXemSeriesIds();
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -264,6 +264,7 @@
|
||||||
<Compile Include="Datastore\Migration\083_additonal_blacklist_columns.cs" />
|
<Compile Include="Datastore\Migration\083_additonal_blacklist_columns.cs" />
|
||||||
<Compile Include="Datastore\Migration\082_add_fanzub_settings.cs" />
|
<Compile Include="Datastore\Migration\082_add_fanzub_settings.cs" />
|
||||||
<Compile Include="Datastore\Migration\088_pushbullet_devices_channels_list.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\090_update_kickass_url.cs" />
|
||||||
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
|
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
|
||||||
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
|
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
|
||||||
|
@ -621,6 +622,7 @@
|
||||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotSampleSpecification.cs" />
|
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotSampleSpecification.cs" />
|
||||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotUnpackingSpecification.cs" />
|
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotUnpackingSpecification.cs" />
|
||||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\SameEpisodesImportSpecification.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\EpisodeImport\Specifications\UpgradeSpecification.cs" />
|
||||||
<Compile Include="MediaFiles\Events\EpisodeDownloadedEvent.cs" />
|
<Compile Include="MediaFiles\Events\EpisodeDownloadedEvent.cs" />
|
||||||
<Compile Include="MediaFiles\Events\EpisodeFileAddedEvent.cs" />
|
<Compile Include="MediaFiles\Events\EpisodeFileAddedEvent.cs" />
|
||||||
|
|
|
@ -29,6 +29,7 @@ namespace NzbDrone.Core.Tv
|
||||||
public Nullable<Int32> SceneAbsoluteEpisodeNumber { get; set; }
|
public Nullable<Int32> SceneAbsoluteEpisodeNumber { get; set; }
|
||||||
public Nullable<Int32> SceneSeasonNumber { get; set; }
|
public Nullable<Int32> SceneSeasonNumber { get; set; }
|
||||||
public Nullable<Int32> SceneEpisodeNumber { get; set; }
|
public Nullable<Int32> SceneEpisodeNumber { get; set; }
|
||||||
|
public bool UnverifiedSceneNumbering { get; set; }
|
||||||
public Ratings Ratings { get; set; }
|
public Ratings Ratings { get; set; }
|
||||||
public List<MediaCover.MediaCover> Images { get; set; }
|
public List<MediaCover.MediaCover> Images { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,12 @@ module.exports = NzbDroneCell.extend({
|
||||||
render : function() {
|
render : function() {
|
||||||
this.$el.empty();
|
this.$el.empty();
|
||||||
|
|
||||||
if (SeriesCollection.get(this.model.get('seriesId')).get('seriesType') === 'anime') {
|
if (this.model.get('unverifiedSceneNumbering')) {
|
||||||
if (this.model.get('seasonNumber') > 0 && !this.model.has('absoluteEpisodeNumber')) {
|
this.$el.html('<i class="icon-sonarr-form-warning" title="Scene number hasn\'t been verified yet."></i>');
|
||||||
this.$el.html('<i class="icon-sonarr-form-warning" title="Episode does not have an absolute episode number"></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();
|
this.delegateEvents();
|
||||||
|
|
Loading…
Reference in a new issue