diff --git a/frontend/src/Components/Filter/Builder/DateFilterBuilderRowValue.js b/frontend/src/Components/Filter/Builder/DateFilterBuilderRowValue.js index 9309a80ee..19718d475 100644 --- a/frontend/src/Components/Filter/Builder/DateFilterBuilderRowValue.js +++ b/frontend/src/Components/Filter/Builder/DateFilterBuilderRowValue.js @@ -3,7 +3,7 @@ import React, { Component } from 'react'; import NumberInput from 'Components/Form/NumberInput'; import SelectInput from 'Components/Form/SelectInput'; import TextInput from 'Components/Form/TextInput'; -import { IN_LAST, IN_NEXT, NOT_IN_LAST, NOT_IN_NEXT } from 'Helpers/Props/filterTypes'; +import { IN_LAST, IN_NEXT, IN_PAST, NOT_IN_LAST, NOT_IN_NEXT } from 'Helpers/Props/filterTypes'; import isString from 'Utilities/String/isString'; import translate from 'Utilities/String/translate'; import { NAME } from './FilterBuilderRowValue'; @@ -57,6 +57,12 @@ function isInFilter(filterType) { ); } +function isPastFutureFilter(filterType) { + return ( + filterType === IN_PAST + ); +} + class DateFilterBuilderRowValue extends Component { // @@ -187,6 +193,10 @@ class DateFilterBuilderRowValue extends Component { ); } + if (isPastFutureFilter(filterType)) { + return null; + } + return ( ); } diff --git a/frontend/src/Movie/Index/Table/MovieIndexRow.tsx b/frontend/src/Movie/Index/Table/MovieIndexRow.tsx index bb9e54a4a..c4a0747ef 100644 --- a/frontend/src/Movie/Index/Table/MovieIndexRow.tsx +++ b/frontend/src/Movie/Index/Table/MovieIndexRow.tsx @@ -79,6 +79,7 @@ function MovieIndexRow(props: MovieIndexRowProps) { tmdbId, imdbId, isAvailable, + dateConsideredAvailable, hasFile, movieFile, youTubeTrailerId, @@ -352,6 +353,7 @@ function MovieIndexRow(props: MovieIndexRowProps) { monitored={monitored} hasFile={hasFile} isAvailable={isAvailable} + dateConsideredAvailable={dateConsideredAvailable} status={status} width={125} detailedProgressBar={true} diff --git a/frontend/src/Movie/Movie.ts b/frontend/src/Movie/Movie.ts index d261f04eb..a222d2390 100644 --- a/frontend/src/Movie/Movie.ts +++ b/frontend/src/Movie/Movie.ts @@ -83,6 +83,7 @@ interface Movie extends ModelBase { grabbed?: boolean; lastSearchTime?: string; isAvailable: boolean; + dateConsideredAvailable: string; isSaving?: boolean; } diff --git a/frontend/src/Store/Actions/discoverMovieActions.js b/frontend/src/Store/Actions/discoverMovieActions.js index c658b7ed5..a82117877 100644 --- a/frontend/src/Store/Actions/discoverMovieActions.js +++ b/frontend/src/Store/Actions/discoverMovieActions.js @@ -467,6 +467,12 @@ export const defaultState = { type: filterBuilderTypes.EXACT, valueType: filterBuilderValueTypes.BOOL }, + { + name: 'dateConsideredAvailable', + label: translate('DateConsideredAvailable'), + type: filterBuilderTypes.DATE, + valueType: filterBuilderValueTypes.DATE + }, { name: 'minimumAvailability', label: () => translate('MinimumAvailability'), @@ -508,6 +514,12 @@ export const defaultState = { label: () => translate('Popularity'), type: filterBuilderTypes.NUMBER }, + { + name: 'minimumAvailabilityDate', + label: translate('MinimumAvailabilityDate'), + type: filterBuilderTypes.DATE, + valueType: filterBuilderValueTypes.DATE + }, { name: 'certification', label: () => translate('Certification'), diff --git a/frontend/src/Store/Actions/movieActions.js b/frontend/src/Store/Actions/movieActions.js index cd5613589..8cc215d9e 100644 --- a/frontend/src/Store/Actions/movieActions.js +++ b/frontend/src/Store/Actions/movieActions.js @@ -152,6 +152,14 @@ export const filterPredicates = { return dateFilterPredicate(item.physicalRelease, filterValue, type); }, + dateConsideredAvailable: function(item, filterValue, type) { + return dateFilterPredicate(item.dateConsideredAvailable, filterValue, type); + }, + + minimumAvailabilityDate: function(item, filterValue, type) { + return dateFilterPredicate(item.minimumAvailabilityDate, filterValue, type); + }, + digitalRelease: function(item, filterValue, type) { return dateFilterPredicate(item.digitalRelease, filterValue, type); }, diff --git a/frontend/src/Store/Actions/movieIndexActions.js b/frontend/src/Store/Actions/movieIndexActions.js index 1730ff185..58fb1d518 100644 --- a/frontend/src/Store/Actions/movieIndexActions.js +++ b/frontend/src/Store/Actions/movieIndexActions.js @@ -310,12 +310,24 @@ export const defaultState = { type: filterBuilderTypes.EXACT, valueType: filterBuilderValueTypes.BOOL }, + { + name: 'dateConsideredAvailable', + label: translate('DateConsideredAvailable'), + type: filterBuilderTypes.DATE, + valueType: filterBuilderValueTypes.DATE + }, { name: 'minimumAvailability', label: () => translate('MinimumAvailability'), type: filterBuilderTypes.EXACT, valueType: filterBuilderValueTypes.MINIMUM_AVAILABILITY }, + { + name: 'minimumAvailabilityDate', + label: translate('MinimumAvailabilityDate'), + type: filterBuilderTypes.DATE, + valueType: filterBuilderValueTypes.DATE + }, { name: 'title', label: () => translate('Title'), diff --git a/frontend/src/Utilities/Date/dateFilterPredicate.js b/frontend/src/Utilities/Date/dateFilterPredicate.js index 59407e3ba..4a55292a7 100644 --- a/frontend/src/Utilities/Date/dateFilterPredicate.js +++ b/frontend/src/Utilities/Date/dateFilterPredicate.js @@ -37,6 +37,9 @@ export default function(itemValue, filterValue, type) { isAfter(itemValue, { [filterValue.time]: filterValue.value }) ); + case filterTypes.IN_PAST: + return moment(itemValue).isBefore(new Date()); + default: return false; } diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index f2190c736..96f815358 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -301,6 +301,7 @@ "Database": "Database", "DatabaseMigration": "Database Migration", "Date": "Date", + "DateConsideredAvailable": "Date Considered Available", "Dates": "Dates", "Day": "Day", "DayOfWeekAt": "{day} at {time}", @@ -995,6 +996,7 @@ "MinimumAge": "Minimum Age", "MinimumAgeHelpText": "Usenet only: Minimum age in minutes of NZBs before they are grabbed. Use this to give new releases time to propagate to your usenet provider.", "MinimumAvailability": "Minimum Availability", + "MinimumAvailabilityDate": "Minimum Availability Date", "MinimumCustomFormatScore": "Minimum Custom Format Score", "MinimumCustomFormatScoreHelpText": "Minimum custom format score allowed to download", "MinimumCustomFormatScoreIncrement": "Minimum Custom Format Score Increment", diff --git a/src/NzbDrone.Core/Movies/Movie.cs b/src/NzbDrone.Core/Movies/Movie.cs index 1ef429fa2..a34b0b29b 100644 --- a/src/NzbDrone.Core/Movies/Movie.cs +++ b/src/NzbDrone.Core/Movies/Movie.cs @@ -76,70 +76,40 @@ namespace NzbDrone.Core.Movies 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; + return DateTime.UtcNow >= GetReleaseDate(delay, true); + } + public DateTime? GetReleaseDate(int delay = 0, bool isAvailabilityCheck = false) + { if (MinimumAvailability is MovieStatusType.TBA or MovieStatusType.Announced) { - minimumAvailabilityDate = DateTime.MinValue; - } - else if (MinimumAvailability == MovieStatusType.InCinemas && MovieMetadata.Value.InCinemas.HasValue) - { - minimumAvailabilityDate = MovieMetadata.Value.InCinemas.Value; - } - else - { - if (MovieMetadata.Value.PhysicalRelease.HasValue && MovieMetadata.Value.DigitalRelease.HasValue) - { - minimumAvailabilityDate = new DateTime(Math.Min(MovieMetadata.Value.PhysicalRelease.Value.Ticks, MovieMetadata.Value.DigitalRelease.Value.Ticks)); - } - else if (MovieMetadata.Value.PhysicalRelease.HasValue) + if (isAvailabilityCheck) { - minimumAvailabilityDate = MovieMetadata.Value.PhysicalRelease.Value; + return DateTime.MinValue; } - else if (MovieMetadata.Value.DigitalRelease.HasValue) + else if (MovieMetadata.Value.InCinemas.HasValue || MovieMetadata.Value.DigitalRelease.HasValue || MovieMetadata.Value.PhysicalRelease.HasValue) { - minimumAvailabilityDate = MovieMetadata.Value.DigitalRelease.Value; - } - else - { - minimumAvailabilityDate = MovieMetadata.Value.InCinemas?.AddDays(90) ?? DateTime.MaxValue; + return new[] { MovieMetadata.Value.InCinemas, MovieMetadata.Value.DigitalRelease, MovieMetadata.Value.PhysicalRelease } + .Where(x => x.HasValue) + .Min()?.AddDays(delay); } } - - if (minimumAvailabilityDate == DateTime.MinValue || minimumAvailabilityDate == DateTime.MaxValue) + else if (MinimumAvailability == MovieStatusType.InCinemas && MovieMetadata.Value.InCinemas.HasValue) { - return DateTime.UtcNow >= minimumAvailabilityDate; + return MovieMetadata.Value.InCinemas.Value.AddDays(delay); } - - return DateTime.UtcNow >= minimumAvailabilityDate.AddDays(delay); - } - - public DateTime? GetReleaseDate() - { - if (MinimumAvailability is MovieStatusType.TBA or MovieStatusType.Announced) + else if (MovieMetadata.Value.DigitalRelease.HasValue || MovieMetadata.Value.PhysicalRelease.HasValue) { - return new[] { MovieMetadata.Value.InCinemas, MovieMetadata.Value.DigitalRelease, MovieMetadata.Value.PhysicalRelease } + return new[] { MovieMetadata.Value.DigitalRelease, MovieMetadata.Value.PhysicalRelease } .Where(x => x.HasValue) - .Min(); + .Min()?.AddDays(delay); } - - if (MinimumAvailability == MovieStatusType.InCinemas && MovieMetadata.Value.InCinemas.HasValue) - { - return MovieMetadata.Value.InCinemas.Value; - } - - if (MovieMetadata.Value.DigitalRelease.HasValue || MovieMetadata.Value.PhysicalRelease.HasValue) + else if (!MovieMetadata.Value.InCinemas.HasValue && isAvailabilityCheck) { - return new[] { MovieMetadata.Value.DigitalRelease, MovieMetadata.Value.PhysicalRelease } - .Where(x => x.HasValue) - .Min(); + return DateTime.MaxValue; } - return MovieMetadata.Value.InCinemas?.AddDays(90); + return MovieMetadata.Value.InCinemas?.AddDays(90 + delay); } public override string ToString() diff --git a/src/Radarr.Api.V3/Movies/MovieResource.cs b/src/Radarr.Api.V3/Movies/MovieResource.cs index 5ade23f88..73675bde9 100644 --- a/src/Radarr.Api.V3/Movies/MovieResource.cs +++ b/src/Radarr.Api.V3/Movies/MovieResource.cs @@ -64,7 +64,9 @@ namespace Radarr.Api.V3.Movies // Editing Only public bool Monitored { get; set; } public MovieStatusType MinimumAvailability { get; set; } + public DateTime? MinimumAvailabilityDate { get; set; } public bool IsAvailable { get; set; } + public DateTime? DateConsideredAvailable { get; set; } public string FolderName { get; set; } public int Runtime { get; set; } @@ -143,6 +145,8 @@ namespace Radarr.Api.V3.Movies MinimumAvailability = model.MinimumAvailability, IsAvailable = model.IsAvailable(availDelay), + DateConsideredAvailable = model.GetReleaseDate(availDelay), + MinimumAvailabilityDate = model.GetReleaseDate(), FolderName = model.FolderName(), Runtime = model.MovieMetadata.Value.Runtime,