From 140a22034082178510621f9d51d08e12649dd3ca Mon Sep 17 00:00:00 2001 From: geogolem Date: Thu, 23 Feb 2017 00:03:48 -0500 Subject: [PATCH] Min availability (#816) * availability specification to prevent downloading titles before their release * pull inCinamas status out of js handlebars and set it in SkyHook * minor code improvement * add incinemas to footer * typo * another typo * release date handling * still print cinema date out for announced titles * revert a minor change from before since its unnecessary * early implementation of minimumAvailability --> when does radarr consider a movie "available" should be specified by user default to "Physical release?" this isn't functional yet, but it has a skeleton + comments. I dont know how to have the minimumavailability attribute default to something or to have it actually populate the Movieinfo object could use some help with that * adding another comment for another location that might need to be updated to handle minimumAvailability * the implementation is now function; however, i still need to specify default values for minimumAvailability * missed these changes in the previous commit * fix rounded corners on new field in editmovie dialog * add minimum availability specification to the addMovie page * minor adjustment from last commit * handle the case where minimumavailability has never yet been set nullstring.. if its never been set, default to Released (Physical/Web) represented by integer value 3 * minAvailability specification on NetImport lists * add support for min availability to the movie editor * use enum MovieStatusType values directly makes for cleaner code * need to fix up the migration forgot in last commit * cleaning up code, proper case * erroneous code added in this feature needed to be removed * update "Wanted" page to take into account minimumAvailability * implement preDB minimumAvailability as default.. behaves same as Physical/Web a few comments with TODO for when preDB is implemented * minor adjustment * remove some unused code (leave commented for now) * improve code for minimumavailability and add option for availabilitydelay (but doesnt do anything yet) * improve isAvailable method * clean up and fix helper info on indexer configuration page * add buttons in Wanted/Missing view --- .../Config/IndexerConfigResource.cs | 2 + src/NzbDrone.Api/NetImport/NetImportModule.cs | 4 +- .../NetImport/NetImportResource.cs | 4 +- src/NzbDrone.Api/Series/MovieResource.cs | 12 +++- src/NzbDrone.Api/Wanted/MovieMissingModule.cs | 23 ++++++- .../Configuration/ConfigService.cs | 6 ++ .../Configuration/IConfigService.cs | 2 + .../Migration/133_add_minimumavailability.cs | 23 +++++++ .../RssSync/AvailabilitySpecification.cs | 69 +++++++++++++++++++ .../MetadataSource/SkyHook/SkyHookProxy.cs | 60 +++++++++++++++- .../NetImport/HttpNetImportBase.cs | 1 + src/NzbDrone.Core/NetImport/NetImportBase.cs | 1 + .../NetImport/NetImportDefinition.cs | 2 + src/NzbDrone.Core/NzbDrone.Core.csproj | 4 +- src/NzbDrone.Core/Tv/Movie.cs | 32 ++++++++- src/NzbDrone.Core/Tv/MovieRepository.cs | 3 +- src/NzbDrone.Core/Tv/MovieStatusType.cs | 8 ++- .../MinimumavailabilityTooltipTemplate.hbs | 10 +++ src/UI/AddMovies/SearchResultView.js | 17 +++++ src/UI/AddMovies/SearchResultViewTemplate.hbs | 10 +++ src/UI/AddMovies/addMovies.less | 10 +++ src/UI/Cells/MovieStatusCell.js | 12 +--- src/UI/Cells/MovieStatusWithTextCell.js | 7 +- src/UI/Content/icons.less | 4 ++ src/UI/Handlebars/Helpers/Series.js | 43 +++++------- src/UI/Movies/Details/InfoViewTemplate.hbs | 13 ++-- src/UI/Movies/Edit/EditMovieTemplate.hbs | 14 ++++ src/UI/Movies/Editor/MovieEditorFooterView.js | 8 ++- .../Editor/MovieEditorFooterViewTemplate.hbs | 12 ++++ src/UI/Movies/Index/FooterViewTemplate.hbs | 5 +- src/UI/Movies/Index/MoviesIndexLayout.js | 4 ++ src/UI/Movies/MovieModel.js | 14 ++-- src/UI/Movies/MoviesCollection.js | 9 ++- .../Options/IndexerOptionsViewTemplate.hbs | 11 +++ .../NetImport/Edit/NetImportEditView.js | 3 + .../Edit/NetImportEditViewTemplate.hbs | 11 +++ src/UI/Wanted/Missing/MissingCollection.js | 18 ++++- src/UI/Wanted/Missing/MissingLayout.js | 36 ++++++++-- 38 files changed, 446 insertions(+), 81 deletions(-) create mode 100644 src/NzbDrone.Core/Datastore/Migration/133_add_minimumavailability.cs create mode 100644 src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/AvailabilitySpecification.cs create mode 100644 src/UI/AddMovies/MinimumavailabilityTooltipTemplate.hbs diff --git a/src/NzbDrone.Api/Config/IndexerConfigResource.cs b/src/NzbDrone.Api/Config/IndexerConfigResource.cs index 179e28c3f..0faacfccc 100644 --- a/src/NzbDrone.Api/Config/IndexerConfigResource.cs +++ b/src/NzbDrone.Api/Config/IndexerConfigResource.cs @@ -8,6 +8,7 @@ namespace NzbDrone.Api.Config public int MinimumAge { get; set; } public int Retention { get; set; } public int RssSyncInterval { get; set; } + public int AvailabilityDelay { get; set; } } public static class IndexerConfigResourceMapper @@ -19,6 +20,7 @@ namespace NzbDrone.Api.Config MinimumAge = model.MinimumAge, Retention = model.Retention, RssSyncInterval = model.RssSyncInterval, + AvailabilityDelay = model.AvailabilityDelay, }; } } diff --git a/src/NzbDrone.Api/NetImport/NetImportModule.cs b/src/NzbDrone.Api/NetImport/NetImportModule.cs index f56d1164a..e29ef59c0 100644 --- a/src/NzbDrone.Api/NetImport/NetImportModule.cs +++ b/src/NzbDrone.Api/NetImport/NetImportModule.cs @@ -22,6 +22,7 @@ namespace NzbDrone.Api.NetImport resource.ProfileId = definition.ProfileId; resource.RootFolderPath = definition.RootFolderPath; resource.ShouldMonitor = definition.ShouldMonitor; + resource.MinimumAvailability = definition.MinimumAvailability; } protected override void MapToModel(NetImportDefinition definition, NetImportResource resource) @@ -33,6 +34,7 @@ namespace NzbDrone.Api.NetImport definition.ProfileId = resource.ProfileId; definition.RootFolderPath = resource.RootFolderPath; definition.ShouldMonitor = resource.ShouldMonitor; + definition.MinimumAvailability = resource.MinimumAvailability; } protected override void Validate(NetImportDefinition definition, bool includeWarnings) @@ -41,4 +43,4 @@ namespace NzbDrone.Api.NetImport base.Validate(definition, includeWarnings); } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Api/NetImport/NetImportResource.cs b/src/NzbDrone.Api/NetImport/NetImportResource.cs index d46eec57e..f01520784 100644 --- a/src/NzbDrone.Api/NetImport/NetImportResource.cs +++ b/src/NzbDrone.Api/NetImport/NetImportResource.cs @@ -1,4 +1,5 @@ using NzbDrone.Core.NetImport; +using NzbDrone.Core.Tv; namespace NzbDrone.Api.NetImport { @@ -9,5 +10,6 @@ namespace NzbDrone.Api.NetImport public bool ShouldMonitor { get; set; } public string RootFolderPath { get; set; } public int ProfileId { get; set; } + public MovieStatusType MinimumAvailability { get; set; } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Api/Series/MovieResource.cs b/src/NzbDrone.Api/Series/MovieResource.cs index 66b47def1..5e263573d 100644 --- a/src/NzbDrone.Api/Series/MovieResource.cs +++ b/src/NzbDrone.Api/Series/MovieResource.cs @@ -43,6 +43,9 @@ namespace NzbDrone.Api.Movie //Editing Only public bool Monitored { get; set; } + public MovieStatusType MinimumAvailability { get; set; } + public bool IsAvailable { get; set; } + public int Runtime { get; set; } public DateTime? LastInfoSync { get; set; } public string CleanTitle { get; set; } @@ -129,6 +132,9 @@ namespace NzbDrone.Api.Movie ProfileId = model.ProfileId, Monitored = model.Monitored, + MinimumAvailability = model.MinimumAvailability, + + IsAvailable = model.IsAvailable(), SizeOnDisk = size, @@ -181,7 +187,8 @@ namespace NzbDrone.Api.Movie ProfileId = resource.ProfileId, Monitored = resource.Monitored, - + MinimumAvailability = resource.MinimumAvailability, + Runtime = resource.Runtime, LastInfoSync = resource.LastInfoSync, CleanTitle = resource.CleanTitle, @@ -210,7 +217,8 @@ namespace NzbDrone.Api.Movie movie.ProfileId = resource.ProfileId; movie.Monitored = resource.Monitored; - + movie.MinimumAvailability = resource.MinimumAvailability; + movie.RootFolderPath = resource.RootFolderPath; movie.Tags = resource.Tags; movie.AddOptions = resource.AddOptions; diff --git a/src/NzbDrone.Api/Wanted/MovieMissingModule.cs b/src/NzbDrone.Api/Wanted/MovieMissingModule.cs index 319e70a08..1d90cbcb7 100644 --- a/src/NzbDrone.Api/Wanted/MovieMissingModule.cs +++ b/src/NzbDrone.Api/Wanted/MovieMissingModule.cs @@ -34,10 +34,31 @@ namespace NzbDrone.Api.Wanted { pagingSpec.FilterExpression = v => v.Monitored == false; } - else + else if (pagingResource.FilterKey == "monitored" && pagingResource.FilterValue == "true") { pagingSpec.FilterExpression = v => v.Monitored == true; } + else if (pagingResource.FilterKey == "moviestatus" && pagingResource.FilterValue == "available") + { + //TODO: might need to handle PreDB here + pagingSpec.FilterExpression = v => + (v.MinimumAvailability == MovieStatusType.Released && v.Status >= MovieStatusType.Released) || + (v.MinimumAvailability == MovieStatusType.InCinemas && v.Status >= MovieStatusType.InCinemas) || + (v.MinimumAvailability == MovieStatusType.Announced && v.Status >= MovieStatusType.Announced) || + (v.MinimumAvailability == MovieStatusType.PreDB && v.Status >= MovieStatusType.Released); + } + else if (pagingResource.FilterKey == "moviestatus" && pagingResource.FilterValue == "announced") + { + pagingSpec.FilterExpression = v => v.Status == MovieStatusType.Announced; + } + else if (pagingResource.FilterKey == "moviestatus" && pagingResource.FilterValue == "incinemas") + { + pagingSpec.FilterExpression = v => v.Status == MovieStatusType.InCinemas; + } + else if (pagingResource.FilterKey == "moviestatus" && pagingResource.FilterValue == "released") + { + pagingSpec.FilterExpression = v => v.Status == MovieStatusType.Released; + } var resource = ApplyToPage(_movieService.MoviesWithoutFiles, pagingSpec, v => MapToResource(v, true)); diff --git a/src/NzbDrone.Core/Configuration/ConfigService.cs b/src/NzbDrone.Core/Configuration/ConfigService.cs index 00c4b4e74..e4c14918a 100644 --- a/src/NzbDrone.Core/Configuration/ConfigService.cs +++ b/src/NzbDrone.Core/Configuration/ConfigService.cs @@ -105,6 +105,12 @@ namespace NzbDrone.Core.Configuration set { SetValue("RssSyncInterval", value); } } + public int AvailabilityDelay + { + get { return GetValueInt("AvailabilityDelay",0); } + set { SetValue("AvailabilityDelay", value); } + } + public int NetImportSyncInterval { get { return GetValueInt("NetImportSyncInterval", 60); } diff --git a/src/NzbDrone.Core/Configuration/IConfigService.cs b/src/NzbDrone.Core/Configuration/IConfigService.cs index a2d56e778..73d45d37e 100644 --- a/src/NzbDrone.Core/Configuration/IConfigService.cs +++ b/src/NzbDrone.Core/Configuration/IConfigService.cs @@ -46,6 +46,8 @@ namespace NzbDrone.Core.Configuration int RssSyncInterval { get; set; } int MinimumAge { get; set; } + int AvailabilityDelay { get; set; } + int NetImportSyncInterval { get; set; } //UI diff --git a/src/NzbDrone.Core/Datastore/Migration/133_add_minimumavailability.cs b/src/NzbDrone.Core/Datastore/Migration/133_add_minimumavailability.cs new file mode 100644 index 000000000..b260ea0b8 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/133_add_minimumavailability.cs @@ -0,0 +1,23 @@ +using FluentMigrator; +//using FluentMigrator.Expressions; +using NzbDrone.Core.Datastore.Migration.Framework; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(133)] + public class add_minimumavailability : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + if (!this.Schema.Schema("dbo").Table("NetImport").Column("MinimumAvailability").Exists()) + { + Alter.Table("NetImport").AddColumn("MinimumAvailability").AsInt32().WithDefaultValue(MovieStatusType.PreDB); + } + if (!this.Schema.Schema("dbo").Table("Movies").Column("MinimumAvailability").Exists()) + { + Alter.Table("Movies").AddColumn("MinimumAvailability").AsInt32().WithDefaultValue(MovieStatusType.PreDB); + } + } + } +} diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/AvailabilitySpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/AvailabilitySpecification.cs new file mode 100644 index 000000000..d94201613 --- /dev/null +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/AvailabilitySpecification.cs @@ -0,0 +1,69 @@ +using System; +using System.Linq; +using NLog; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Tv; +using NzbDrone.Core.Configuration; + +namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync +{ + public class AvailabilitySpecification : IDecisionEngineSpecification + { + private readonly IConfigService _settingsService; + private readonly Logger _logger; + + public AvailabilitySpecification(IConfigService settingsService, Logger logger) + { + _settingsService = settingsService; + _logger = logger; + } + + public RejectionType Type => RejectionType.Permanent; + + public Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria) + { + if (searchCriteria != null) + { + if (searchCriteria.UserInvokedSearch) + { + _logger.Debug("Skipping availability check during search"); + return Decision.Accept(); + } + } + if (!subject.Movie.IsAvailable(_settingsService.AvailabilityDelay)) + { + return Decision.Reject("Movie {0} will only be considered available {1} days after {2}", subject.Movie, _settingsService.AvailabilityDelay, subject.Movie.MinimumAvailability.ToString()); + } + + return Decision.Accept(); + } + + public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + { + if (searchCriteria != null) + { + if (!searchCriteria.MonitoredEpisodesOnly) + { + _logger.Debug("Skipping availability check during search"); + return Decision.Accept(); + } + } + + /*if (subject.Series.Status != MovieStatusType.Released) + { + _logger.Debug("{0} is present in the DB but not yet available. skipping.", subject.Series); + return Decision.Reject("Series is not yet available"); + } + + /*var monitoredCount = subject.Episodes.Count(episode => episode.Monitored); + if (monitoredCount == subject.Episodes.Count) + { + return Decision.Accept(); + } + + _logger.Debug("Only {0}/{1} episodes are monitored. skipping.", monitoredCount, subject.Episodes.Count);*/ + return Decision.Reject("Episode is not yet available"); + } + } +} diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs index 47b648fba..450a9b51d 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs @@ -185,14 +185,71 @@ namespace NzbDrone.Core.MetadataSource.SkyHook movie.Genres.Add(genre.name); } - if (resource.status == "Released") + //this is the way it should be handled + //but unfortunately it seems + //tmdb lacks alot of release date info + //omdbapi is actually quite good for this info + //except omdbapi has been having problems recently + //so i will just leave this in as a comment + //and use the 3 month logic that we were using before + /*var now = DateTime.Now; + if (now < movie.InCinemas) + movie.Status = MovieStatusType.Announced; + if (now >= movie.InCinemas) + movie.Status = MovieStatusType.InCinemas; + if (now >= movie.PhysicalRelease) + movie.Status = MovieStatusType.Released; + */ + + + var now = DateTime.Now; + //handle the case when we have both theatrical and physical release dates + if (movie.InCinemas.HasValue && movie.PhysicalRelease.HasValue) + { + if (now < movie.InCinemas) + movie.Status = MovieStatusType.Announced; + else if (now >= movie.InCinemas) + movie.Status = MovieStatusType.InCinemas; + if (now >= movie.PhysicalRelease) + movie.Status = MovieStatusType.Released; + } + //handle the case when we have theatrical release dates but we dont know the physical release date + else if (movie.InCinemas.HasValue && (now >= movie.InCinemas)) + { + movie.Status = MovieStatusType.InCinemas; + } + //handle the case where we only have a physical release date + else if (movie.PhysicalRelease.HasValue && (now >= movie.PhysicalRelease)) { movie.Status = MovieStatusType.Released; } + //otherwise the title has only been announced else { movie.Status = MovieStatusType.Announced; } + //since TMDB lacks alot of information lets assume that stuff is released if its been in cinemas for longer than 3 months. + if (!movie.PhysicalRelease.HasValue && (movie.Status == MovieStatusType.InCinemas) && (((DateTime.Now).Subtract(movie.InCinemas.Value)).TotalSeconds > 60*60*24*30*3)) + { + movie.Status = MovieStatusType.Released; + } + + //this matches with the old behavior before the creation of the MovieStatusType.InCinemas + /*if (resource.status == "Released") + { + if (movie.InCinemas.HasValue && (((DateTime.Now).Subtract(movie.InCinemas.Value)).TotalSeconds <= 60 * 60 * 24 * 30 * 3)) + { + movie.Status = MovieStatusType.InCinemas; + } + else + { + movie.Status = MovieStatusType.Released; + } + } + else + { + movie.Status = MovieStatusType.Announced; + }*/ if (resource.videos != null) { @@ -650,6 +707,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook newMovie.ProfileId = movie.ProfileId; newMovie.Monitored = movie.Monitored; newMovie.MovieFile = movie.MovieFile; + newMovie.MinimumAvailability = movie.MinimumAvailability; return newMovie; } diff --git a/src/NzbDrone.Core/NetImport/HttpNetImportBase.cs b/src/NzbDrone.Core/NetImport/HttpNetImportBase.cs index 0a7ce51fd..524d3bd13 100644 --- a/src/NzbDrone.Core/NetImport/HttpNetImportBase.cs +++ b/src/NzbDrone.Core/NetImport/HttpNetImportBase.cs @@ -112,6 +112,7 @@ namespace NzbDrone.Core.NetImport m.RootFolderPath = ((NetImportDefinition) Definition).RootFolderPath; m.ProfileId = ((NetImportDefinition) Definition).ProfileId; m.Monitored = ((NetImportDefinition) Definition).ShouldMonitor; + m.MinimumAvailability = ((NetImportDefinition) Definition).MinimumAvailability; return m; }).ToList(); } diff --git a/src/NzbDrone.Core/NetImport/NetImportBase.cs b/src/NzbDrone.Core/NetImport/NetImportBase.cs index 50c368457..f9ff9f825 100644 --- a/src/NzbDrone.Core/NetImport/NetImportBase.cs +++ b/src/NzbDrone.Core/NetImport/NetImportBase.cs @@ -44,6 +44,7 @@ namespace NzbDrone.Core.NetImport Enabled = config.Validate().IsValid && Enabled, EnableAuto = true, ProfileId = 1, + MinimumAvailability = MovieStatusType.PreDB, Implementation = GetType().Name, Settings = config }; diff --git a/src/NzbDrone.Core/NetImport/NetImportDefinition.cs b/src/NzbDrone.Core/NetImport/NetImportDefinition.cs index 20b6d2312..b38e49fd6 100644 --- a/src/NzbDrone.Core/NetImport/NetImportDefinition.cs +++ b/src/NzbDrone.Core/NetImport/NetImportDefinition.cs @@ -1,6 +1,7 @@ using Marr.Data; using NzbDrone.Core.Profiles; using NzbDrone.Core.ThingiProvider; +using NzbDrone.Core.Tv; namespace NzbDrone.Core.NetImport { @@ -9,6 +10,7 @@ namespace NzbDrone.Core.NetImport public bool Enabled { get; set; } public bool EnableAuto { get; set; } public bool ShouldMonitor { get; set; } + public MovieStatusType MinimumAvailability { get; set; } public int ProfileId { get; set; } public LazyLoaded Profile { get; set; } public string RootFolderPath { get; set; } diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 3cebfefbd..9138f9f1f 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -128,6 +128,7 @@ + @@ -394,6 +395,7 @@ + @@ -1341,4 +1343,4 @@ --> - \ No newline at end of file + diff --git a/src/NzbDrone.Core/Tv/Movie.cs b/src/NzbDrone.Core/Tv/Movie.cs index 4c14a45d1..329752a93 100644 --- a/src/NzbDrone.Core/Tv/Movie.cs +++ b/src/NzbDrone.Core/Tv/Movie.cs @@ -26,6 +26,7 @@ namespace NzbDrone.Core.Tv public MovieStatusType Status { get; set; } public string Overview { get; set; } public bool Monitored { get; set; } + public MovieStatusType MinimumAvailability { get; set; } public int ProfileId { get; set; } public DateTime? LastInfoSync { get; set; } public int Runtime { get; set; } @@ -53,6 +54,35 @@ namespace NzbDrone.Core.Tv public bool HasFile => MovieFileId > 0; + public bool IsAvailable(int delay = 0) + { + //the below line is what was used before delay was implemented, could still be used for cases when delay==0 + //return (Status >= MinimumAvailability || (MinimumAvailability == MovieStatusType.PreDB && Status >= MovieStatusType.Released)); + + //This more complex sequence handles the delay + DateTime MinimumAvailabilityDate; + switch (MinimumAvailability) + { + case MovieStatusType.TBA: + case MovieStatusType.Announced: + MinimumAvailabilityDate = DateTime.MinValue; + break; + case MovieStatusType.InCinemas: + if (InCinemas.HasValue) + MinimumAvailabilityDate = InCinemas.Value; + else + MinimumAvailabilityDate = DateTime.MaxValue; + break; + //TODO: might need to handle PreDB but for now treat the same as Released + case MovieStatusType.Released: + case MovieStatusType.PreDB: + default: + MinimumAvailabilityDate = PhysicalRelease.HasValue ? PhysicalRelease.Value : (InCinemas.HasValue ? InCinemas.Value.AddDays(90) : DateTime.MaxValue); + break; + } + return DateTime.Now >= MinimumAvailabilityDate.AddDays(delay); + } + public override string ToString() { return string.Format("[{0}][{1}]", ImdbId, Title.NullSafe()); @@ -63,4 +93,4 @@ namespace NzbDrone.Core.Tv { public bool SearchForMovie { get; set; } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core/Tv/MovieRepository.cs b/src/NzbDrone.Core/Tv/MovieRepository.cs index 974fdc3d3..683ff71a7 100644 --- a/src/NzbDrone.Core/Tv/MovieRepository.cs +++ b/src/NzbDrone.Core/Tv/MovieRepository.cs @@ -196,7 +196,6 @@ namespace NzbDrone.Core.Tv { return Query.Where(pagingSpec.FilterExpression) .AndWhere(m => m.MovieFileId == 0) - .AndWhere(m => m.Status == MovieStatusType.Released) .OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection()) .Skip(pagingSpec.PagingOffset()) .Take(pagingSpec.PageSize); @@ -242,4 +241,4 @@ namespace NzbDrone.Core.Tv return Query.Where(m => m.TmdbId == tmdbid).FirstOrDefault(); } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core/Tv/MovieStatusType.cs b/src/NzbDrone.Core/Tv/MovieStatusType.cs index 9c0bdbed9..b925e26c4 100644 --- a/src/NzbDrone.Core/Tv/MovieStatusType.cs +++ b/src/NzbDrone.Core/Tv/MovieStatusType.cs @@ -2,8 +2,10 @@ { public enum MovieStatusType { - TBA = 0, //Nothing yet announced, only rumors, but still IMDb page - Announced = 1, //AirDate is announced - Released = 2 //Has at least one PreDB release + TBA = 0, //Nothing yet announced, only rumors, but still IMDb page (this might not be used) + Announced = 1, //Movie is announced but Cinema date is in the future or unknown + InCinemas = 2, //Been in Cinemas for less than 3 months (since TMDB lacks complete information) + Released = 3, //Physical or Web Release or been in cinemas for > 3 months (since TMDB lacks complete information) + PreDB = 4 //this is only used for MinimumAvailability. Movie items should never be in this state. } } diff --git a/src/UI/AddMovies/MinimumavailabilityTooltipTemplate.hbs b/src/UI/AddMovies/MinimumavailabilityTooltipTemplate.hbs new file mode 100644 index 000000000..2ff228867 --- /dev/null +++ b/src/UI/AddMovies/MinimumavailabilityTooltipTemplate.hbs @@ -0,0 +1,10 @@ +
+
Announced
+
Consider the movie available after it has been announced
+
In Cinemas
+
Consider the movie available once it is In Cinemas
+
Physical/Web
+
Consider the movie available after Physical/Web release
+
PreDB
+
Consider the movie available if preDB contains at least one entry
+
diff --git a/src/UI/AddMovies/SearchResultView.js b/src/UI/AddMovies/SearchResultView.js index f4d3a18f6..76a683ee7 100644 --- a/src/UI/AddMovies/SearchResultView.js +++ b/src/UI/AddMovies/SearchResultView.js @@ -22,6 +22,8 @@ var view = Marionette.ItemView.extend({ rootFolder : '.x-root-folder', seasonFolder : '.x-season-folder', monitor : '.x-monitor', + minimumAvailability : '.x-minimumavailability', + minimumAvailabilityTooltip : '.x-minimumavailability-tooltip', monitorTooltip : '.x-monitor-tooltip', addButton : '.x-add', addSearchButton : '.x-add-search', @@ -70,6 +72,7 @@ var view = Marionette.ItemView.extend({ this.ui.seasonFolder.prop('checked', useSeasonFolder); this.ui.monitor.val(defaultMonitorEpisodes); + this.ui.minimumAvailability.val("preDB"); //TODO: make this work via onRender, FM? //works with onShow, but stops working after the first render @@ -88,6 +91,18 @@ var view = Marionette.ItemView.extend({ placement : 'right', container : this.$el }); + + this.templateFunction = Marionette.TemplateCache.get('AddMovies/MinimumAvailabilityTooltipTemplate'); + var content1 = this.templateFunction(); + + this.ui.minimumAvailabilityTooltip.popover({ + content : content1, + html :true, + trigger : 'hover', + title : 'When to Consider a Movie Available', + placement : 'right', + container : this.$el + }); }, _configureTemplateHelpers : function() { @@ -168,6 +183,7 @@ var view = Marionette.ItemView.extend({ var profile = this.ui.profile.val(); var rootFolderPath = this.ui.rootFolder.children(':selected').text(); var monitor = this.ui.monitor.val(); + var minAvail = this.ui.minimumAvailability.val(); var options = this._getAddMoviesOptions(); options.searchForMovie = searchForMovie; @@ -177,6 +193,7 @@ var view = Marionette.ItemView.extend({ profileId : profile, rootFolderPath : rootFolderPath, addOptions : options, + minimumAvailability : minAvail, monitored : (monitor === 'all' ? true : false) }, { silent : true }); diff --git a/src/UI/AddMovies/SearchResultViewTemplate.hbs b/src/UI/AddMovies/SearchResultViewTemplate.hbs index fbcb54777..a28072622 100644 --- a/src/UI/AddMovies/SearchResultViewTemplate.hbs +++ b/src/UI/AddMovies/SearchResultViewTemplate.hbs @@ -47,6 +47,16 @@ +
+ + +
+
{{> ProfileSelectionPartial profiles}} diff --git a/src/UI/AddMovies/addMovies.less b/src/UI/AddMovies/addMovies.less index 7481eb5c8..db5f790e7 100644 --- a/src/UI/AddMovies/addMovies.less +++ b/src/UI/AddMovies/addMovies.less @@ -117,6 +117,9 @@ .monitor-tooltip { margin-left : 5px; } + .minimumavailability-tooltip { + margin-left : 5px; + } } .loading-folders { @@ -136,6 +139,13 @@ padding-bottom : 8px; } } + .minimumavailability-tooltip-contents { + padding-bottom : 0px; + + dd { + padding-bottom :8px; + } + } } li.add-new { diff --git a/src/UI/Cells/MovieStatusCell.js b/src/UI/Cells/MovieStatusCell.js index 668e36d4e..f479e3f36 100644 --- a/src/UI/Cells/MovieStatusCell.js +++ b/src/UI/Cells/MovieStatusCell.js @@ -17,12 +17,7 @@ module.exports = NzbDroneCell.extend({ this._setStatusWeight(3); } - if (numOfMonths > 3) { - this.$el.html('');//TODO: Update for PreDB.me - this._setStatusWeight(2); - } - - if (numOfMonths < 3) { + if (status === 'inCinemas') { this.$el.html(''); this._setStatusWeight(2); } @@ -32,11 +27,6 @@ module.exports = NzbDroneCell.extend({ this._setStatusWeight(1); } - // else if (!monitored) { - // this.$el.html(''); - // this._setStatusWeight(0); - // } - return this; }, diff --git a/src/UI/Cells/MovieStatusWithTextCell.js b/src/UI/Cells/MovieStatusWithTextCell.js index c4bcac645..a40b89040 100644 --- a/src/UI/Cells/MovieStatusWithTextCell.js +++ b/src/UI/Cells/MovieStatusWithTextCell.js @@ -18,12 +18,7 @@ module.exports = NzbDroneCell.extend({ this._setStatusWeight(3); } - if (numOfMonths > 3) { - this.$el.html('
 Released
');//TODO: Update for PreDB.me - this._setStatusWeight(2); - } - - if (numOfMonths < 3) { + if (status ==='inCinemas') { this.$el.html('
 In Cinemas
'); this._setStatusWeight(2); } diff --git a/src/UI/Content/icons.less b/src/UI/Content/icons.less index e06a481c0..3e2026807 100644 --- a/src/UI/Content/icons.less +++ b/src/UI/Content/icons.less @@ -60,6 +60,10 @@ .fa-icon-color(@brand-warning); } +.icon-sonarr-available { + .fa-icon-content(@fa-var-clock-o); +} + .icon-sonarr-edit { .fa-icon-content(@fa-var-wrench); } diff --git a/src/UI/Handlebars/Helpers/Series.js b/src/UI/Handlebars/Helpers/Series.js index 44b35308b..6aceb21a7 100644 --- a/src/UI/Handlebars/Helpers/Series.js +++ b/src/UI/Handlebars/Helpers/Series.js @@ -54,6 +54,9 @@ Handlebars.registerHelper('tmdbUrl', function() { Handlebars.registerHelper('youTubeTrailerUrl', function() { return 'https://www.youtube.com/watch?v=' + this.youTubeTrailerId; }); +Handlebars.registerHelper('allFlicksUrl', function() { + return this.allFlicksUrl; +}); Handlebars.registerHelper('homepage', function() { return this.website; @@ -73,25 +76,21 @@ Handlebars.registerHelper('alternativeTitlesString', function() { Handlebars.registerHelper('GetStatus', function() { var monitored = this.monitored; var status = this.status; - var inCinemas = this.inCinemas; - var date = new Date(inCinemas); - var timeSince = new Date().getTime() - date.getTime(); - var numOfMonths = timeSince / 1000 / 60 / 60 / 24 / 30; + //var inCinemas = this.inCinemas; + //var date = new Date(inCinemas); + //var timeSince = new Date().getTime() - date.getTime(); + //var numOfMonths = timeSince / 1000 / 60 / 60 / 24 / 30; if (status === "announced") { return new Handlebars.SafeString(' Announced'); } + - if (numOfMonths < 3) { - + if (status ==="inCinemas") { return new Handlebars.SafeString(' In Cinemas'); } - - if (numOfMonths > 3) { - return new Handlebars.SafeString(' Released');//TODO: Update for PreDB.me - } - + if (status === 'released') { return new Handlebars.SafeString(' Released'); } @@ -104,30 +103,22 @@ Handlebars.registerHelper('GetStatus', function() { Handlebars.registerHelper('GetBannerStatus', function() { var monitored = this.monitored; var status = this.status; - var inCinemas = this.inCinemas; - var date = new Date(inCinemas); - var timeSince = new Date().getTime() - date.getTime(); - var numOfMonths = timeSince / 1000 / 60 / 60 / 24 / 30; + //var inCinemas = this.inCinemas; + //var date = new Date(inCinemas); + //var timeSince = new Date().getTime() - date.getTime(); + //var numOfMonths = timeSince / 1000 / 60 / 60 / 24 / 30; if (status === "announced") { return new Handlebars.SafeString('
 Announced
'); } - if (numOfMonths < 3) { + if (status === "inCinemas") { return new Handlebars.SafeString('
 In Cinemas
'); } if (status === 'released') { return new Handlebars.SafeString('
 Released
'); } - - if (numOfMonths > 3) { - return new Handlebars.SafeString('
 Released
');//TODO: Update for PreDB.me - } - - - - else if (!monitored) { return new Handlebars.SafeString('
 Not Monitored
'); } @@ -145,7 +136,7 @@ Handlebars.registerHelper('DownloadedStatusColor', function() { return "success"; } - if (this.status != "released") { + if (!this.isAvailable){ return "primary"; } @@ -160,8 +151,6 @@ Handlebars.registerHelper('DownloadedStatus', function() { if (!this.monitored) { return "Not Monitored"; } - - return "Missing"; }); diff --git a/src/UI/Movies/Details/InfoViewTemplate.hbs b/src/UI/Movies/Details/InfoViewTemplate.hbs index 38c66c889..3c04b4439 100644 --- a/src/UI/Movies/Details/InfoViewTemplate.hbs +++ b/src/UI/Movies/Details/InfoViewTemplate.hbs @@ -17,12 +17,11 @@ {{/if}} {{Bytes sizeOnDisk}} - - {{#if_eq status compare="released"}} + {{#if_eq status compare="announced"}} + {{inCinemas}} + {{else}} {{inCinemas}} - {{else}} - Announced - {{/if_eq}} + {{/if_eq}} {{DownloadedStatus}}
@@ -40,6 +39,10 @@ {{#if youTubeTrailerId}} Trailer {{/if}} + + {{#if allFlicksUrl}} + AllFlicks + {{/if}}
diff --git a/src/UI/Movies/Edit/EditMovieTemplate.hbs b/src/UI/Movies/Edit/EditMovieTemplate.hbs index c59520c7c..185ad3af8 100644 --- a/src/UI/Movies/Edit/EditMovieTemplate.hbs +++ b/src/UI/Movies/Edit/EditMovieTemplate.hbs @@ -32,6 +32,20 @@ +
+ +
+ +
+
+ +
+