mirror of
https://github.com/lidarr/lidarr.git
synced 2025-08-23 06:45:19 -07:00
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
This commit is contained in:
parent
731e607666
commit
140a220340
38 changed files with 446 additions and 81 deletions
|
@ -8,6 +8,7 @@ namespace NzbDrone.Api.Config
|
||||||
public int MinimumAge { get; set; }
|
public int MinimumAge { get; set; }
|
||||||
public int Retention { get; set; }
|
public int Retention { get; set; }
|
||||||
public int RssSyncInterval { get; set; }
|
public int RssSyncInterval { get; set; }
|
||||||
|
public int AvailabilityDelay { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class IndexerConfigResourceMapper
|
public static class IndexerConfigResourceMapper
|
||||||
|
@ -19,6 +20,7 @@ namespace NzbDrone.Api.Config
|
||||||
MinimumAge = model.MinimumAge,
|
MinimumAge = model.MinimumAge,
|
||||||
Retention = model.Retention,
|
Retention = model.Retention,
|
||||||
RssSyncInterval = model.RssSyncInterval,
|
RssSyncInterval = model.RssSyncInterval,
|
||||||
|
AvailabilityDelay = model.AvailabilityDelay,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ namespace NzbDrone.Api.NetImport
|
||||||
resource.ProfileId = definition.ProfileId;
|
resource.ProfileId = definition.ProfileId;
|
||||||
resource.RootFolderPath = definition.RootFolderPath;
|
resource.RootFolderPath = definition.RootFolderPath;
|
||||||
resource.ShouldMonitor = definition.ShouldMonitor;
|
resource.ShouldMonitor = definition.ShouldMonitor;
|
||||||
|
resource.MinimumAvailability = definition.MinimumAvailability;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void MapToModel(NetImportDefinition definition, NetImportResource resource)
|
protected override void MapToModel(NetImportDefinition definition, NetImportResource resource)
|
||||||
|
@ -33,6 +34,7 @@ namespace NzbDrone.Api.NetImport
|
||||||
definition.ProfileId = resource.ProfileId;
|
definition.ProfileId = resource.ProfileId;
|
||||||
definition.RootFolderPath = resource.RootFolderPath;
|
definition.RootFolderPath = resource.RootFolderPath;
|
||||||
definition.ShouldMonitor = resource.ShouldMonitor;
|
definition.ShouldMonitor = resource.ShouldMonitor;
|
||||||
|
definition.MinimumAvailability = resource.MinimumAvailability;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Validate(NetImportDefinition definition, bool includeWarnings)
|
protected override void Validate(NetImportDefinition definition, bool includeWarnings)
|
||||||
|
@ -41,4 +43,4 @@ namespace NzbDrone.Api.NetImport
|
||||||
base.Validate(definition, includeWarnings);
|
base.Validate(definition, includeWarnings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using NzbDrone.Core.NetImport;
|
using NzbDrone.Core.NetImport;
|
||||||
|
using NzbDrone.Core.Tv;
|
||||||
|
|
||||||
namespace NzbDrone.Api.NetImport
|
namespace NzbDrone.Api.NetImport
|
||||||
{
|
{
|
||||||
|
@ -9,5 +10,6 @@ namespace NzbDrone.Api.NetImport
|
||||||
public bool ShouldMonitor { get; set; }
|
public bool ShouldMonitor { get; set; }
|
||||||
public string RootFolderPath { get; set; }
|
public string RootFolderPath { get; set; }
|
||||||
public int ProfileId { get; set; }
|
public int ProfileId { get; set; }
|
||||||
|
public MovieStatusType MinimumAvailability { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,9 @@ namespace NzbDrone.Api.Movie
|
||||||
|
|
||||||
//Editing Only
|
//Editing Only
|
||||||
public bool Monitored { get; set; }
|
public bool Monitored { get; set; }
|
||||||
|
public MovieStatusType MinimumAvailability { get; set; }
|
||||||
|
public bool IsAvailable { get; set; }
|
||||||
|
|
||||||
public int Runtime { get; set; }
|
public int Runtime { get; set; }
|
||||||
public DateTime? LastInfoSync { get; set; }
|
public DateTime? LastInfoSync { get; set; }
|
||||||
public string CleanTitle { get; set; }
|
public string CleanTitle { get; set; }
|
||||||
|
@ -129,6 +132,9 @@ namespace NzbDrone.Api.Movie
|
||||||
ProfileId = model.ProfileId,
|
ProfileId = model.ProfileId,
|
||||||
|
|
||||||
Monitored = model.Monitored,
|
Monitored = model.Monitored,
|
||||||
|
MinimumAvailability = model.MinimumAvailability,
|
||||||
|
|
||||||
|
IsAvailable = model.IsAvailable(),
|
||||||
|
|
||||||
SizeOnDisk = size,
|
SizeOnDisk = size,
|
||||||
|
|
||||||
|
@ -181,7 +187,8 @@ namespace NzbDrone.Api.Movie
|
||||||
ProfileId = resource.ProfileId,
|
ProfileId = resource.ProfileId,
|
||||||
|
|
||||||
Monitored = resource.Monitored,
|
Monitored = resource.Monitored,
|
||||||
|
MinimumAvailability = resource.MinimumAvailability,
|
||||||
|
|
||||||
Runtime = resource.Runtime,
|
Runtime = resource.Runtime,
|
||||||
LastInfoSync = resource.LastInfoSync,
|
LastInfoSync = resource.LastInfoSync,
|
||||||
CleanTitle = resource.CleanTitle,
|
CleanTitle = resource.CleanTitle,
|
||||||
|
@ -210,7 +217,8 @@ namespace NzbDrone.Api.Movie
|
||||||
movie.ProfileId = resource.ProfileId;
|
movie.ProfileId = resource.ProfileId;
|
||||||
|
|
||||||
movie.Monitored = resource.Monitored;
|
movie.Monitored = resource.Monitored;
|
||||||
|
movie.MinimumAvailability = resource.MinimumAvailability;
|
||||||
|
|
||||||
movie.RootFolderPath = resource.RootFolderPath;
|
movie.RootFolderPath = resource.RootFolderPath;
|
||||||
movie.Tags = resource.Tags;
|
movie.Tags = resource.Tags;
|
||||||
movie.AddOptions = resource.AddOptions;
|
movie.AddOptions = resource.AddOptions;
|
||||||
|
|
|
@ -34,10 +34,31 @@ namespace NzbDrone.Api.Wanted
|
||||||
{
|
{
|
||||||
pagingSpec.FilterExpression = v => v.Monitored == false;
|
pagingSpec.FilterExpression = v => v.Monitored == false;
|
||||||
}
|
}
|
||||||
else
|
else if (pagingResource.FilterKey == "monitored" && pagingResource.FilterValue == "true")
|
||||||
{
|
{
|
||||||
pagingSpec.FilterExpression = v => v.Monitored == 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));
|
var resource = ApplyToPage(_movieService.MoviesWithoutFiles, pagingSpec, v => MapToResource(v, true));
|
||||||
|
|
||||||
|
|
|
@ -105,6 +105,12 @@ namespace NzbDrone.Core.Configuration
|
||||||
set { SetValue("RssSyncInterval", value); }
|
set { SetValue("RssSyncInterval", value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int AvailabilityDelay
|
||||||
|
{
|
||||||
|
get { return GetValueInt("AvailabilityDelay",0); }
|
||||||
|
set { SetValue("AvailabilityDelay", value); }
|
||||||
|
}
|
||||||
|
|
||||||
public int NetImportSyncInterval
|
public int NetImportSyncInterval
|
||||||
{
|
{
|
||||||
get { return GetValueInt("NetImportSyncInterval", 60); }
|
get { return GetValueInt("NetImportSyncInterval", 60); }
|
||||||
|
|
|
@ -46,6 +46,8 @@ namespace NzbDrone.Core.Configuration
|
||||||
int RssSyncInterval { get; set; }
|
int RssSyncInterval { get; set; }
|
||||||
int MinimumAge { get; set; }
|
int MinimumAge { get; set; }
|
||||||
|
|
||||||
|
int AvailabilityDelay { get; set; }
|
||||||
|
|
||||||
int NetImportSyncInterval { get; set; }
|
int NetImportSyncInterval { get; set; }
|
||||||
|
|
||||||
//UI
|
//UI
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -185,14 +185,71 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
movie.Genres.Add(genre.name);
|
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;
|
movie.Status = MovieStatusType.Released;
|
||||||
}
|
}
|
||||||
|
//otherwise the title has only been announced
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
movie.Status = MovieStatusType.Announced;
|
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)
|
if (resource.videos != null)
|
||||||
{
|
{
|
||||||
|
@ -650,6 +707,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
newMovie.ProfileId = movie.ProfileId;
|
newMovie.ProfileId = movie.ProfileId;
|
||||||
newMovie.Monitored = movie.Monitored;
|
newMovie.Monitored = movie.Monitored;
|
||||||
newMovie.MovieFile = movie.MovieFile;
|
newMovie.MovieFile = movie.MovieFile;
|
||||||
|
newMovie.MinimumAvailability = movie.MinimumAvailability;
|
||||||
|
|
||||||
return newMovie;
|
return newMovie;
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,6 +112,7 @@ namespace NzbDrone.Core.NetImport
|
||||||
m.RootFolderPath = ((NetImportDefinition) Definition).RootFolderPath;
|
m.RootFolderPath = ((NetImportDefinition) Definition).RootFolderPath;
|
||||||
m.ProfileId = ((NetImportDefinition) Definition).ProfileId;
|
m.ProfileId = ((NetImportDefinition) Definition).ProfileId;
|
||||||
m.Monitored = ((NetImportDefinition) Definition).ShouldMonitor;
|
m.Monitored = ((NetImportDefinition) Definition).ShouldMonitor;
|
||||||
|
m.MinimumAvailability = ((NetImportDefinition) Definition).MinimumAvailability;
|
||||||
return m;
|
return m;
|
||||||
}).ToList();
|
}).ToList();
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ namespace NzbDrone.Core.NetImport
|
||||||
Enabled = config.Validate().IsValid && Enabled,
|
Enabled = config.Validate().IsValid && Enabled,
|
||||||
EnableAuto = true,
|
EnableAuto = true,
|
||||||
ProfileId = 1,
|
ProfileId = 1,
|
||||||
|
MinimumAvailability = MovieStatusType.PreDB,
|
||||||
Implementation = GetType().Name,
|
Implementation = GetType().Name,
|
||||||
Settings = config
|
Settings = config
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Marr.Data;
|
using Marr.Data;
|
||||||
using NzbDrone.Core.Profiles;
|
using NzbDrone.Core.Profiles;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
using NzbDrone.Core.Tv;
|
||||||
|
|
||||||
namespace NzbDrone.Core.NetImport
|
namespace NzbDrone.Core.NetImport
|
||||||
{
|
{
|
||||||
|
@ -9,6 +10,7 @@ namespace NzbDrone.Core.NetImport
|
||||||
public bool Enabled { get; set; }
|
public bool Enabled { get; set; }
|
||||||
public bool EnableAuto { get; set; }
|
public bool EnableAuto { get; set; }
|
||||||
public bool ShouldMonitor { get; set; }
|
public bool ShouldMonitor { get; set; }
|
||||||
|
public MovieStatusType MinimumAvailability { get; set; }
|
||||||
public int ProfileId { get; set; }
|
public int ProfileId { get; set; }
|
||||||
public LazyLoaded<Profile> Profile { get; set; }
|
public LazyLoaded<Profile> Profile { get; set; }
|
||||||
public string RootFolderPath { get; set; }
|
public string RootFolderPath { get; set; }
|
||||||
|
|
|
@ -128,6 +128,7 @@
|
||||||
<Compile Include="Datastore\Migration\128_remove_kickass.cs" />
|
<Compile Include="Datastore\Migration\128_remove_kickass.cs" />
|
||||||
<Compile Include="Datastore\Migration\130_remove_wombles_kickass.cs" />
|
<Compile Include="Datastore\Migration\130_remove_wombles_kickass.cs" />
|
||||||
<Compile Include="Datastore\Migration\132_rename_torrent_downloadstation.cs" />
|
<Compile Include="Datastore\Migration\132_rename_torrent_downloadstation.cs" />
|
||||||
|
<Compile Include="Datastore\Migration\133_add_minimumavailability.cs" />
|
||||||
<Compile Include="NetImport\TMDb\TMDbLanguageCodes.cs" />
|
<Compile Include="NetImport\TMDb\TMDbLanguageCodes.cs" />
|
||||||
<Compile Include="NetImport\TMDb\TMDbSettings.cs" />
|
<Compile Include="NetImport\TMDb\TMDbSettings.cs" />
|
||||||
<Compile Include="NetImport\TMDb\TMDbListType.cs" />
|
<Compile Include="NetImport\TMDb\TMDbListType.cs" />
|
||||||
|
@ -394,6 +395,7 @@
|
||||||
<Compile Include="DecisionEngine\Specifications\RssSync\DelaySpecification.cs" />
|
<Compile Include="DecisionEngine\Specifications\RssSync\DelaySpecification.cs" />
|
||||||
<Compile Include="DecisionEngine\Specifications\RssSync\HistorySpecification.cs" />
|
<Compile Include="DecisionEngine\Specifications\RssSync\HistorySpecification.cs" />
|
||||||
<Compile Include="DecisionEngine\Specifications\RssSync\MonitoredEpisodeSpecification.cs" />
|
<Compile Include="DecisionEngine\Specifications\RssSync\MonitoredEpisodeSpecification.cs" />
|
||||||
|
<Compile Include="DecisionEngine\Specifications\RssSync\AvailabilitySpecification.cs" />
|
||||||
<Compile Include="DecisionEngine\Specifications\RssSync\ProperSpecification.cs" />
|
<Compile Include="DecisionEngine\Specifications\RssSync\ProperSpecification.cs" />
|
||||||
<Compile Include="DecisionEngine\Specifications\Search\DailyEpisodeMatchSpecification.cs" />
|
<Compile Include="DecisionEngine\Specifications\Search\DailyEpisodeMatchSpecification.cs" />
|
||||||
<Compile Include="DecisionEngine\Specifications\Search\EpisodeRequestedSpecification.cs" />
|
<Compile Include="DecisionEngine\Specifications\Search\EpisodeRequestedSpecification.cs" />
|
||||||
|
@ -1341,4 +1343,4 @@
|
||||||
<Target Name="AfterBuild">
|
<Target Name="AfterBuild">
|
||||||
</Target>
|
</Target>
|
||||||
-->
|
-->
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -26,6 +26,7 @@ namespace NzbDrone.Core.Tv
|
||||||
public MovieStatusType Status { get; set; }
|
public MovieStatusType Status { get; set; }
|
||||||
public string Overview { get; set; }
|
public string Overview { get; set; }
|
||||||
public bool Monitored { get; set; }
|
public bool Monitored { get; set; }
|
||||||
|
public MovieStatusType MinimumAvailability { get; set; }
|
||||||
public int ProfileId { get; set; }
|
public int ProfileId { get; set; }
|
||||||
public DateTime? LastInfoSync { get; set; }
|
public DateTime? LastInfoSync { get; set; }
|
||||||
public int Runtime { get; set; }
|
public int Runtime { get; set; }
|
||||||
|
@ -53,6 +54,35 @@ namespace NzbDrone.Core.Tv
|
||||||
|
|
||||||
public bool HasFile => MovieFileId > 0;
|
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()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return string.Format("[{0}][{1}]", ImdbId, Title.NullSafe());
|
return string.Format("[{0}][{1}]", ImdbId, Title.NullSafe());
|
||||||
|
@ -63,4 +93,4 @@ namespace NzbDrone.Core.Tv
|
||||||
{
|
{
|
||||||
public bool SearchForMovie { get; set; }
|
public bool SearchForMovie { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,7 +196,6 @@ namespace NzbDrone.Core.Tv
|
||||||
{
|
{
|
||||||
return Query.Where(pagingSpec.FilterExpression)
|
return Query.Where(pagingSpec.FilterExpression)
|
||||||
.AndWhere(m => m.MovieFileId == 0)
|
.AndWhere(m => m.MovieFileId == 0)
|
||||||
.AndWhere(m => m.Status == MovieStatusType.Released)
|
|
||||||
.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection())
|
.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection())
|
||||||
.Skip(pagingSpec.PagingOffset())
|
.Skip(pagingSpec.PagingOffset())
|
||||||
.Take(pagingSpec.PageSize);
|
.Take(pagingSpec.PageSize);
|
||||||
|
@ -242,4 +241,4 @@ namespace NzbDrone.Core.Tv
|
||||||
return Query.Where(m => m.TmdbId == tmdbid).FirstOrDefault();
|
return Query.Where(m => m.TmdbId == tmdbid).FirstOrDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
{
|
{
|
||||||
public enum MovieStatusType
|
public enum MovieStatusType
|
||||||
{
|
{
|
||||||
TBA = 0, //Nothing yet announced, only rumors, but still IMDb page
|
TBA = 0, //Nothing yet announced, only rumors, but still IMDb page (this might not be used)
|
||||||
Announced = 1, //AirDate is announced
|
Announced = 1, //Movie is announced but Cinema date is in the future or unknown
|
||||||
Released = 2 //Has at least one PreDB release
|
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.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
10
src/UI/AddMovies/MinimumavailabilityTooltipTemplate.hbs
Normal file
10
src/UI/AddMovies/MinimumavailabilityTooltipTemplate.hbs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<dl class="minimumavailability-tooltip-contents">
|
||||||
|
<dt>Announced</dt>
|
||||||
|
<dd>Consider the movie available after it has been announced</dd>
|
||||||
|
<dt>In Cinemas</dt>
|
||||||
|
<dd>Consider the movie available once it is In Cinemas</dd>
|
||||||
|
<dt>Physical/Web</dt>
|
||||||
|
<dd>Consider the movie available after Physical/Web release</dd>
|
||||||
|
<dt>PreDB</dt>
|
||||||
|
<dd>Consider the movie available if preDB contains at least one entry</dd>
|
||||||
|
</dl>
|
|
@ -22,6 +22,8 @@ var view = Marionette.ItemView.extend({
|
||||||
rootFolder : '.x-root-folder',
|
rootFolder : '.x-root-folder',
|
||||||
seasonFolder : '.x-season-folder',
|
seasonFolder : '.x-season-folder',
|
||||||
monitor : '.x-monitor',
|
monitor : '.x-monitor',
|
||||||
|
minimumAvailability : '.x-minimumavailability',
|
||||||
|
minimumAvailabilityTooltip : '.x-minimumavailability-tooltip',
|
||||||
monitorTooltip : '.x-monitor-tooltip',
|
monitorTooltip : '.x-monitor-tooltip',
|
||||||
addButton : '.x-add',
|
addButton : '.x-add',
|
||||||
addSearchButton : '.x-add-search',
|
addSearchButton : '.x-add-search',
|
||||||
|
@ -70,6 +72,7 @@ var view = Marionette.ItemView.extend({
|
||||||
|
|
||||||
this.ui.seasonFolder.prop('checked', useSeasonFolder);
|
this.ui.seasonFolder.prop('checked', useSeasonFolder);
|
||||||
this.ui.monitor.val(defaultMonitorEpisodes);
|
this.ui.monitor.val(defaultMonitorEpisodes);
|
||||||
|
this.ui.minimumAvailability.val("preDB");
|
||||||
|
|
||||||
//TODO: make this work via onRender, FM?
|
//TODO: make this work via onRender, FM?
|
||||||
//works with onShow, but stops working after the first render
|
//works with onShow, but stops working after the first render
|
||||||
|
@ -88,6 +91,18 @@ var view = Marionette.ItemView.extend({
|
||||||
placement : 'right',
|
placement : 'right',
|
||||||
container : this.$el
|
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() {
|
_configureTemplateHelpers : function() {
|
||||||
|
@ -168,6 +183,7 @@ var view = Marionette.ItemView.extend({
|
||||||
var profile = this.ui.profile.val();
|
var profile = this.ui.profile.val();
|
||||||
var rootFolderPath = this.ui.rootFolder.children(':selected').text();
|
var rootFolderPath = this.ui.rootFolder.children(':selected').text();
|
||||||
var monitor = this.ui.monitor.val();
|
var monitor = this.ui.monitor.val();
|
||||||
|
var minAvail = this.ui.minimumAvailability.val();
|
||||||
|
|
||||||
var options = this._getAddMoviesOptions();
|
var options = this._getAddMoviesOptions();
|
||||||
options.searchForMovie = searchForMovie;
|
options.searchForMovie = searchForMovie;
|
||||||
|
@ -177,6 +193,7 @@ var view = Marionette.ItemView.extend({
|
||||||
profileId : profile,
|
profileId : profile,
|
||||||
rootFolderPath : rootFolderPath,
|
rootFolderPath : rootFolderPath,
|
||||||
addOptions : options,
|
addOptions : options,
|
||||||
|
minimumAvailability : minAvail,
|
||||||
monitored : (monitor === 'all' ? true : false)
|
monitored : (monitor === 'all' ? true : false)
|
||||||
}, { silent : true });
|
}, { silent : true });
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,16 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group col-md-2">
|
||||||
|
<label>Min Availability <i class="icon-sonarr-form-info minimumavailability-tooltip x-minimumavailability-tooltip"></i></label>
|
||||||
|
<select class="form-control col-md-2 x-minimumavailability">
|
||||||
|
<option value="announced">Announced</option>
|
||||||
|
<option value="inCinemas">In Cinemas</option>
|
||||||
|
<option value="released">Physical/Web</option>
|
||||||
|
<option value="preDB">PreDB</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-2">
|
<div class="form-group col-md-2">
|
||||||
<label>Profile</label>
|
<label>Profile</label>
|
||||||
{{> ProfileSelectionPartial profiles}}
|
{{> ProfileSelectionPartial profiles}}
|
||||||
|
|
|
@ -117,6 +117,9 @@
|
||||||
.monitor-tooltip {
|
.monitor-tooltip {
|
||||||
margin-left : 5px;
|
margin-left : 5px;
|
||||||
}
|
}
|
||||||
|
.minimumavailability-tooltip {
|
||||||
|
margin-left : 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-folders {
|
.loading-folders {
|
||||||
|
@ -136,6 +139,13 @@
|
||||||
padding-bottom : 8px;
|
padding-bottom : 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.minimumavailability-tooltip-contents {
|
||||||
|
padding-bottom : 0px;
|
||||||
|
|
||||||
|
dd {
|
||||||
|
padding-bottom :8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
li.add-new {
|
li.add-new {
|
||||||
|
|
|
@ -17,12 +17,7 @@ module.exports = NzbDroneCell.extend({
|
||||||
this._setStatusWeight(3);
|
this._setStatusWeight(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numOfMonths > 3) {
|
if (status === 'inCinemas') {
|
||||||
this.$el.html('<i class="icon-sonarr-movie-released grid-icon" title="Released"></i>');//TODO: Update for PreDB.me
|
|
||||||
this._setStatusWeight(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (numOfMonths < 3) {
|
|
||||||
this.$el.html('<i class="icon-sonarr-movie-cinemas grid-icon" title="In Cinemas"></i>');
|
this.$el.html('<i class="icon-sonarr-movie-cinemas grid-icon" title="In Cinemas"></i>');
|
||||||
this._setStatusWeight(2);
|
this._setStatusWeight(2);
|
||||||
}
|
}
|
||||||
|
@ -32,11 +27,6 @@ module.exports = NzbDroneCell.extend({
|
||||||
this._setStatusWeight(1);
|
this._setStatusWeight(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// else if (!monitored) {
|
|
||||||
// this.$el.html('<i class="icon-sonarr-series-unmonitored grid-icon" title="Not Monitored"></i>');
|
|
||||||
// this._setStatusWeight(0);
|
|
||||||
// }
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -18,12 +18,7 @@ module.exports = NzbDroneCell.extend({
|
||||||
this._setStatusWeight(3);
|
this._setStatusWeight(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numOfMonths > 3) {
|
if (status ==='inCinemas') {
|
||||||
this.$el.html('<div class="released-banner"><i class="icon-sonarr-movie-released grid-icon" title=""></i> Released</div>');//TODO: Update for PreDB.me
|
|
||||||
this._setStatusWeight(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (numOfMonths < 3) {
|
|
||||||
this.$el.html('<div class="cinemas-banner"><i class="icon-sonarr-movie-cinemas grid-icon" title=""></i> In Cinemas</div>');
|
this.$el.html('<div class="cinemas-banner"><i class="icon-sonarr-movie-cinemas grid-icon" title=""></i> In Cinemas</div>');
|
||||||
this._setStatusWeight(2);
|
this._setStatusWeight(2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,10 @@
|
||||||
.fa-icon-color(@brand-warning);
|
.fa-icon-color(@brand-warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-sonarr-available {
|
||||||
|
.fa-icon-content(@fa-var-clock-o);
|
||||||
|
}
|
||||||
|
|
||||||
.icon-sonarr-edit {
|
.icon-sonarr-edit {
|
||||||
.fa-icon-content(@fa-var-wrench);
|
.fa-icon-content(@fa-var-wrench);
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,9 @@ Handlebars.registerHelper('tmdbUrl', function() {
|
||||||
Handlebars.registerHelper('youTubeTrailerUrl', function() {
|
Handlebars.registerHelper('youTubeTrailerUrl', function() {
|
||||||
return 'https://www.youtube.com/watch?v=' + this.youTubeTrailerId;
|
return 'https://www.youtube.com/watch?v=' + this.youTubeTrailerId;
|
||||||
});
|
});
|
||||||
|
Handlebars.registerHelper('allFlicksUrl', function() {
|
||||||
|
return this.allFlicksUrl;
|
||||||
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper('homepage', function() {
|
Handlebars.registerHelper('homepage', function() {
|
||||||
return this.website;
|
return this.website;
|
||||||
|
@ -73,25 +76,21 @@ Handlebars.registerHelper('alternativeTitlesString', function() {
|
||||||
Handlebars.registerHelper('GetStatus', function() {
|
Handlebars.registerHelper('GetStatus', function() {
|
||||||
var monitored = this.monitored;
|
var monitored = this.monitored;
|
||||||
var status = this.status;
|
var status = this.status;
|
||||||
var inCinemas = this.inCinemas;
|
//var inCinemas = this.inCinemas;
|
||||||
var date = new Date(inCinemas);
|
//var date = new Date(inCinemas);
|
||||||
var timeSince = new Date().getTime() - date.getTime();
|
//var timeSince = new Date().getTime() - date.getTime();
|
||||||
var numOfMonths = timeSince / 1000 / 60 / 60 / 24 / 30;
|
//var numOfMonths = timeSince / 1000 / 60 / 60 / 24 / 30;
|
||||||
|
|
||||||
|
|
||||||
if (status === "announced") {
|
if (status === "announced") {
|
||||||
return new Handlebars.SafeString('<i class="icon-sonarr-movie-announced grid-icon" title=""></i> Announced');
|
return new Handlebars.SafeString('<i class="icon-sonarr-movie-announced grid-icon" title=""></i> Announced');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (numOfMonths < 3) {
|
if (status ==="inCinemas") {
|
||||||
|
|
||||||
return new Handlebars.SafeString('<i class="icon-sonarr-movie-cinemas grid-icon" title=""></i> In Cinemas');
|
return new Handlebars.SafeString('<i class="icon-sonarr-movie-cinemas grid-icon" title=""></i> In Cinemas');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numOfMonths > 3) {
|
|
||||||
return new Handlebars.SafeString('<i class="icon-sonarr-movie-released grid-icon" title=""></i> Released');//TODO: Update for PreDB.me
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'released') {
|
if (status === 'released') {
|
||||||
return new Handlebars.SafeString('<i class="icon-sonarr-movie-released grid-icon" title=""></i> Released');
|
return new Handlebars.SafeString('<i class="icon-sonarr-movie-released grid-icon" title=""></i> Released');
|
||||||
}
|
}
|
||||||
|
@ -104,30 +103,22 @@ Handlebars.registerHelper('GetStatus', function() {
|
||||||
Handlebars.registerHelper('GetBannerStatus', function() {
|
Handlebars.registerHelper('GetBannerStatus', function() {
|
||||||
var monitored = this.monitored;
|
var monitored = this.monitored;
|
||||||
var status = this.status;
|
var status = this.status;
|
||||||
var inCinemas = this.inCinemas;
|
//var inCinemas = this.inCinemas;
|
||||||
var date = new Date(inCinemas);
|
//var date = new Date(inCinemas);
|
||||||
var timeSince = new Date().getTime() - date.getTime();
|
//var timeSince = new Date().getTime() - date.getTime();
|
||||||
var numOfMonths = timeSince / 1000 / 60 / 60 / 24 / 30;
|
//var numOfMonths = timeSince / 1000 / 60 / 60 / 24 / 30;
|
||||||
|
|
||||||
if (status === "announced") {
|
if (status === "announced") {
|
||||||
return new Handlebars.SafeString('<div class="announced-banner"><i class="icon-sonarr-movie-announced grid-icon" title=""></i> Announced</div>');
|
return new Handlebars.SafeString('<div class="announced-banner"><i class="icon-sonarr-movie-announced grid-icon" title=""></i> Announced</div>');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numOfMonths < 3) {
|
if (status === "inCinemas") {
|
||||||
return new Handlebars.SafeString('<div class="cinemas-banner"><i class="icon-sonarr-movie-cinemas grid-icon" title=""></i> In Cinemas</div>');
|
return new Handlebars.SafeString('<div class="cinemas-banner"><i class="icon-sonarr-movie-cinemas grid-icon" title=""></i> In Cinemas</div>');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status === 'released') {
|
if (status === 'released') {
|
||||||
return new Handlebars.SafeString('<div class="released-banner"><i class="icon-sonarr-movie-released grid-icon" title=""></i> Released</div>');
|
return new Handlebars.SafeString('<div class="released-banner"><i class="icon-sonarr-movie-released grid-icon" title=""></i> Released</div>');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numOfMonths > 3) {
|
|
||||||
return new Handlebars.SafeString('<div class="released-banner"><i class="icon-sonarr-movie-released grid-icon" title=""></i> Released</div>');//TODO: Update for PreDB.me
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
else if (!monitored) {
|
else if (!monitored) {
|
||||||
return new Handlebars.SafeString('<div class="announced-banner"><i class="icon-sonarr-series-unmonitored grid-icon" title=""></i> Not Monitored</div>');
|
return new Handlebars.SafeString('<div class="announced-banner"><i class="icon-sonarr-series-unmonitored grid-icon" title=""></i> Not Monitored</div>');
|
||||||
}
|
}
|
||||||
|
@ -145,7 +136,7 @@ Handlebars.registerHelper('DownloadedStatusColor', function() {
|
||||||
return "success";
|
return "success";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.status != "released") {
|
if (!this.isAvailable){
|
||||||
return "primary";
|
return "primary";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,8 +151,6 @@ Handlebars.registerHelper('DownloadedStatus', function() {
|
||||||
if (!this.monitored) {
|
if (!this.monitored) {
|
||||||
return "Not Monitored";
|
return "Not Monitored";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return "Missing";
|
return "Missing";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -17,12 +17,11 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<span class="label label-info">{{Bytes sizeOnDisk}}</span>
|
<span class="label label-info">{{Bytes sizeOnDisk}}</span>
|
||||||
|
{{#if_eq status compare="announced"}}
|
||||||
{{#if_eq status compare="released"}}
|
<span class="label label-default">{{inCinemas}}</span>
|
||||||
|
{{else}}
|
||||||
<span class="label label-info">{{inCinemas}}</span>
|
<span class="label label-info">{{inCinemas}}</span>
|
||||||
{{else}}
|
{{/if_eq}}
|
||||||
<span class="label label-default">Announced</span>
|
|
||||||
{{/if_eq}}
|
|
||||||
<span class="label label-{{DownloadedStatusColor}}" title="{{DownloadedQuality}}">{{DownloadedStatus}}</span>
|
<span class="label label-{{DownloadedStatusColor}}" title="{{DownloadedQuality}}">{{DownloadedStatus}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
|
@ -40,6 +39,10 @@
|
||||||
{{#if youTubeTrailerId}}
|
{{#if youTubeTrailerId}}
|
||||||
<a href="{{youTubeTrailerUrl}}" class="label label-info">Trailer</a>
|
<a href="{{youTubeTrailerUrl}}" class="label label-info">Trailer</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if allFlicksUrl}}
|
||||||
|
<a href="{{allFlicksUrl}}" class="label label-info">AllFlicks</a>
|
||||||
|
{{/if}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -32,6 +32,20 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-4 control-label">Minimum Availability</label>
|
||||||
|
<div class="col-sm-1 col-sm-push-4 help-inline">
|
||||||
|
<i class="icon-sonarr-form-info" title="When the movie is considered Available"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4 col-sm-pull-1">
|
||||||
|
<select class="form-control x-minimumavailability" name="minimumAvailability">
|
||||||
|
<option value="announced">Announced</option>
|
||||||
|
<option value="inCinemas">In Cinemas</option>
|
||||||
|
<option value="released">Physical/Web</option>
|
||||||
|
<option value="preDB">PreDB</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!--<div class="form-group">
|
<!--<div class="form-group">
|
||||||
<label class="col-sm-4 control-label">Use Season Folder</label>
|
<label class="col-sm-4 control-label">Use Season Folder</label>
|
||||||
|
|
|
@ -13,6 +13,7 @@ module.exports = Marionette.ItemView.extend({
|
||||||
ui : {
|
ui : {
|
||||||
monitored : '.x-monitored',
|
monitored : '.x-monitored',
|
||||||
profile : '.x-profiles',
|
profile : '.x-profiles',
|
||||||
|
minimumAvailability : '.x-minimumavailability',
|
||||||
seasonFolder : '.x-season-folder',
|
seasonFolder : '.x-season-folder',
|
||||||
rootFolder : '.x-root-folder',
|
rootFolder : '.x-root-folder',
|
||||||
selectedCount : '.x-selected-count',
|
selectedCount : '.x-selected-count',
|
||||||
|
@ -53,6 +54,7 @@ module.exports = Marionette.ItemView.extend({
|
||||||
var selected = this.editorGrid.getSelectedModels();
|
var selected = this.editorGrid.getSelectedModels();
|
||||||
|
|
||||||
var monitored = this.ui.monitored.val();
|
var monitored = this.ui.monitored.val();
|
||||||
|
var minAvail = this.ui.minimumAvailability.val();
|
||||||
var profile = this.ui.profile.val();
|
var profile = this.ui.profile.val();
|
||||||
var seasonFolder = this.ui.seasonFolder.val();
|
var seasonFolder = this.ui.seasonFolder.val();
|
||||||
var rootFolder = this.ui.rootFolder.val();
|
var rootFolder = this.ui.rootFolder.val();
|
||||||
|
@ -64,6 +66,10 @@ module.exports = Marionette.ItemView.extend({
|
||||||
model.set('monitored', false);
|
model.set('monitored', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (minAvail !=='noChange') {
|
||||||
|
model.set('minimumAvailability', minAvail);
|
||||||
|
}
|
||||||
|
|
||||||
if (profile !== 'noChange') {
|
if (profile !== 'noChange') {
|
||||||
model.set('profileId', parseInt(profile, 10));
|
model.set('profileId', parseInt(profile, 10));
|
||||||
}
|
}
|
||||||
|
@ -123,4 +129,4 @@ module.exports = Marionette.ItemView.extend({
|
||||||
|
|
||||||
vent.trigger(vent.Commands.OpenModalCommand, updateFilesMoviesView);
|
vent.trigger(vent.Commands.OpenModalCommand, updateFilesMoviesView);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,6 +10,18 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group col-md-2">
|
||||||
|
<label>Min Availability</label>
|
||||||
|
|
||||||
|
<select class="form-control x-action x-minimumavailability">
|
||||||
|
<option value="noChange">No change</option>
|
||||||
|
<option value="announced">Announced</option>
|
||||||
|
<option value="inCinemas">In Cinemas</option>
|
||||||
|
<option value="released">Physical/Web</option>
|
||||||
|
<option value="preDB">PreDB</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-2">
|
<div class="form-group col-md-2">
|
||||||
<label>Profile</label>
|
<label>Profile</label>
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="series-legend legend col-xs-6 col-sm-4">
|
<div class="series-legend legend col-xs-6 col-sm-4">
|
||||||
<ul class='legend-labels'>
|
<ul class='legend-labels'>
|
||||||
<li><span class="progress-bar"></span>Missing, but not yet available.</li>
|
<li><span class="progress-bar"></span>Missing, but not yet considered availabile</li>
|
||||||
<li><span class="progress-bar-success"></span>Downloaded and imported.</li>
|
<li><span class="progress-bar-success"></span>Downloaded and imported.</li>
|
||||||
<li><span class="progress-bar-danger"></span>Missing and monitored.</li>
|
<li><span class="progress-bar-danger"></span>Missing and monitored.</li>
|
||||||
<li><span class="progress-bar-warning"></span>Missing, but not monitored.</li>
|
<li><span class="progress-bar-warning"></span>Missing, but not monitored.</li>
|
||||||
|
@ -17,6 +17,9 @@
|
||||||
<dt>Released</dt>
|
<dt>Released</dt>
|
||||||
<dd>{{released}}</dd>
|
<dd>{{released}}</dd>
|
||||||
|
|
||||||
|
<dt>In Cinemas</dt>
|
||||||
|
<dd>{{incinemas}}</dd>
|
||||||
|
|
||||||
<dt>Announced</dt>
|
<dt>Announced</dt>
|
||||||
<dd>{{announced}}</dd>
|
<dd>{{announced}}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
|
@ -328,6 +328,7 @@ module.exports = Marionette.Layout.extend({
|
||||||
var episodeFiles = 0;
|
var episodeFiles = 0;
|
||||||
var announced = 0;
|
var announced = 0;
|
||||||
var released = 0;
|
var released = 0;
|
||||||
|
var incinemas = 0;
|
||||||
var monitored = 0;
|
var monitored = 0;
|
||||||
|
|
||||||
_.each(MoviesCollection.models, function(model) {
|
_.each(MoviesCollection.models, function(model) {
|
||||||
|
@ -336,6 +337,8 @@ module.exports = Marionette.Layout.extend({
|
||||||
|
|
||||||
if (model.get('status').toLowerCase() === 'released') {
|
if (model.get('status').toLowerCase() === 'released') {
|
||||||
released++;
|
released++;
|
||||||
|
} else if (model.get('status').toLowerCase() === 'incinemas') {
|
||||||
|
incinemas++;
|
||||||
} else {
|
} else {
|
||||||
announced++;
|
announced++;
|
||||||
}
|
}
|
||||||
|
@ -348,6 +351,7 @@ module.exports = Marionette.Layout.extend({
|
||||||
footerModel.set({
|
footerModel.set({
|
||||||
series : series,
|
series : series,
|
||||||
released : released,
|
released : released,
|
||||||
|
incinemas : incinemas,
|
||||||
announced : announced,
|
announced : announced,
|
||||||
monitored : monitored,
|
monitored : monitored,
|
||||||
unmonitored : series - monitored,
|
unmonitored : series - monitored,
|
||||||
|
|
|
@ -14,16 +14,16 @@ module.exports = Backbone.Model.extend({
|
||||||
getStatus : function() {
|
getStatus : function() {
|
||||||
var monitored = this.get("monitored");
|
var monitored = this.get("monitored");
|
||||||
var status = this.get("status");
|
var status = this.get("status");
|
||||||
var inCinemas = this.get("inCinemas");
|
//var inCinemas = this.get("inCinemas");
|
||||||
var date = new Date(inCinemas);
|
//var date = new Date(inCinemas);
|
||||||
var timeSince = new Date().getTime() - date.getTime();
|
//var timeSince = new Date().getTime() - date.getTime();
|
||||||
var numOfMonths = timeSince / 1000 / 60 / 60 / 24 / 30;
|
//var numOfMonths = timeSince / 1000 / 60 / 60 / 24 / 30;
|
||||||
|
|
||||||
if (status === "announced") {
|
if (status === "announced") {
|
||||||
return "announced"
|
return "announced"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numOfMonths < 3 && numOfMonths > 0) {
|
if (status === "inCinemas") {
|
||||||
|
|
||||||
return "inCinemas";
|
return "inCinemas";
|
||||||
}
|
}
|
||||||
|
@ -31,9 +31,5 @@ module.exports = Backbone.Model.extend({
|
||||||
if (status === 'released') {
|
if (status === 'released') {
|
||||||
return "released";
|
return "released";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numOfMonths > 3) {
|
|
||||||
return "released";//TODO: Update for PreDB.me
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -113,11 +113,14 @@ var Collection = PageableCollection.extend({
|
||||||
statusWeight : {
|
statusWeight : {
|
||||||
sortValue : function(model, attr) {
|
sortValue : function(model, attr) {
|
||||||
if (model.getStatus() == "released") {
|
if (model.getStatus() == "released") {
|
||||||
return 1;
|
return 3;
|
||||||
}
|
}
|
||||||
if (model.getStatus() == "inCinemas") {
|
if (model.getStatus() == "inCinemas") {
|
||||||
return 0;
|
return 2;
|
||||||
}
|
}
|
||||||
|
if (mode.getStatus() == "announced") {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -190,4 +193,4 @@ Collection = AsPersistedStateCollection.call(Collection);
|
||||||
|
|
||||||
var data = ApiData.get('movie');
|
var data = ApiData.get('movie');
|
||||||
|
|
||||||
module.exports = new Collection(data, { full : true }).bindSignalR();
|
module.exports = new Collection(data, { full : true }).bindSignalR();
|
||||||
|
|
|
@ -37,4 +37,15 @@
|
||||||
<input type="number" name="rssSyncInterval" class="form-control" min="0" max="720"/>
|
<input type="number" name="rssSyncInterval" class="form-control" min="0" max="720"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<legend>Availability Options</legend>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-3 control-label">Availability Delay</label>
|
||||||
|
<div class="col-sm-1 col-sm-push-2 help-inline">
|
||||||
|
<i class="icon-sonarr-form-info" title="A movie will be considered available during RssSync this many days after(or before) the Min Availability has been satisfied. (can be negative)"/>
|
||||||
|
<i class="icon-sonarr-form-info" title="This only effects RssSyncs, It does not effect how movies are displayed or what is shown in the Wanted/Missing View"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-2 col-sm-pull-1">
|
||||||
|
<input type="number" name="availabilityDelay" class="form-control" min="-365" max="365"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
|
@ -19,6 +19,7 @@ var view = Marionette.ItemView.extend({
|
||||||
|
|
||||||
ui : {
|
ui : {
|
||||||
profile : '.x-profile',
|
profile : '.x-profile',
|
||||||
|
minimumAvailability : '.x-minimumavailability',
|
||||||
rootFolder : '.x-root-folder',
|
rootFolder : '.x-root-folder',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -48,10 +49,12 @@ var view = Marionette.ItemView.extend({
|
||||||
|
|
||||||
_onBeforeSave : function() {
|
_onBeforeSave : function() {
|
||||||
var profile = this.ui.profile.val();
|
var profile = this.ui.profile.val();
|
||||||
|
var minAvail = this.ui.minimumAvailability.val();
|
||||||
var rootFolderPath = this.ui.rootFolder.children(':selected').text();
|
var rootFolderPath = this.ui.rootFolder.children(':selected').text();
|
||||||
this.model.set({
|
this.model.set({
|
||||||
profileId : profile,
|
profileId : profile,
|
||||||
rootFolderPath : rootFolderPath,
|
rootFolderPath : rootFolderPath,
|
||||||
|
minimumAvailability : minAvail,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,18 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-3 control-label">Minimum Availability</label>
|
||||||
|
|
||||||
|
<div class="col-sm-5">
|
||||||
|
<select class="form-control x-minimumavailability" name="minimumAvailability">
|
||||||
|
<option value="announced">Announced</option>
|
||||||
|
<option value="inCinemas">In Cinemas</option>
|
||||||
|
<option value="released">Physical/Web</option>
|
||||||
|
<option value="preDB">PreDB</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-3 control-label">Quality Profile</label>
|
<label class="col-sm-3 control-label">Quality Profile</label>
|
||||||
|
|
|
@ -36,7 +36,23 @@ var Collection = PagableCollection.extend({
|
||||||
'unmonitored' : [
|
'unmonitored' : [
|
||||||
'monitored',
|
'monitored',
|
||||||
'false'
|
'false'
|
||||||
]
|
],
|
||||||
|
'announced' : [
|
||||||
|
'moviestatus',
|
||||||
|
'announced'
|
||||||
|
],
|
||||||
|
'incinemas' : [
|
||||||
|
'moviestatus',
|
||||||
|
'incinemas'
|
||||||
|
],
|
||||||
|
'released' : [
|
||||||
|
'moviestatus',
|
||||||
|
'released'
|
||||||
|
],
|
||||||
|
'available' : [
|
||||||
|
'moviestatus',
|
||||||
|
'available',
|
||||||
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
parseState : function(resp) {
|
parseState : function(resp) {
|
||||||
|
|
|
@ -132,7 +132,7 @@ module.exports = Marionette.Layout.extend({
|
||||||
};
|
};
|
||||||
var filterOptions = {
|
var filterOptions = {
|
||||||
type : 'radio',
|
type : 'radio',
|
||||||
storeState : false,
|
storeState : true,
|
||||||
menuKey : 'wanted.filterMode',
|
menuKey : 'wanted.filterMode',
|
||||||
defaultAction : 'monitored',
|
defaultAction : 'monitored',
|
||||||
items : [
|
items : [
|
||||||
|
@ -149,9 +149,37 @@ module.exports = Marionette.Layout.extend({
|
||||||
tooltip : 'Unmonitored Only',
|
tooltip : 'Unmonitored Only',
|
||||||
icon : 'icon-sonarr-unmonitored',
|
icon : 'icon-sonarr-unmonitored',
|
||||||
callback : this._setFilter
|
callback : this._setFilter
|
||||||
}
|
},
|
||||||
]
|
{
|
||||||
};
|
key : 'announced',
|
||||||
|
title : '',
|
||||||
|
tooltip : 'Announced Only',
|
||||||
|
icon : 'icon-sonarr-movie-announced',
|
||||||
|
callback : this._setFilter
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key : 'incinemas',
|
||||||
|
title : '',
|
||||||
|
tooltip : 'In Cinemas Only',
|
||||||
|
icon : 'icon-sonarr-movie-cinemas',
|
||||||
|
callback : this._setFilter
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key : 'released',
|
||||||
|
title : '',
|
||||||
|
tooltip : 'Released Only',
|
||||||
|
icon : 'icon-sonarr-movie-released',
|
||||||
|
callback : this._setFilter
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key : 'available',
|
||||||
|
title : '',
|
||||||
|
tooltip : 'Available Only',
|
||||||
|
icon : 'icon-sonarr-available',
|
||||||
|
callback : this._setFilter
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
this.toolbar.show(new ToolbarLayout({
|
this.toolbar.show(new ToolbarLayout({
|
||||||
left : [leftSideButtons],
|
left : [leftSideButtons],
|
||||||
right : [filterOptions],
|
right : [filterOptions],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue