mirror of
https://github.com/Radarr/Radarr
synced 2025-12-06 08:28:50 +01:00
New Custom Filters based on Availability
https://github.com/Radarr/Radarr/issues/2074 make the UI take into account AvailabilityDelay to make it consistent with RSS Decision Engine add custom filter options for minimumAvailabilityDate with and without Delay Simplify the filter names. ConsideredAvailable ---> DateConsideredAvailable (these always take into account the delay) MinimumAvailability ---> MinimumAvailabilityDate (these never take into account delay) if delay = 0 .. the 2 dates above are identical. if delay = 0, MimimumAvailabilityDate will be 1 of InCinemas, Announced or Physical/Digital (Released) dates Add ability to make the custom filter dates relative to currentdate DateConsideredAvailable tooltip on NotAvailable
This commit is contained in:
parent
f9562b9b76
commit
762cc0903b
15 changed files with 87 additions and 53 deletions
|
|
@ -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 (
|
||||
<TextInput
|
||||
name={NAME}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,8 @@ export const possibleFilterTypes = {
|
|||
{
|
||||
key: filterTypes.NOT_IN_NEXT,
|
||||
value: () => translate('FilterNotInNext')
|
||||
}
|
||||
},
|
||||
{ key: filterTypes.IN_PAST, value: 'before today' }
|
||||
],
|
||||
|
||||
[EQUAL]: [
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export const IN_NEXT = 'inNext';
|
|||
export const NOT_IN_NEXT = 'notInNext';
|
||||
export const LESS_THAN = 'lessThan';
|
||||
export const LESS_THAN_OR_EQUAL = 'lessThanOrEqual';
|
||||
export const IN_PAST = 'inPast';
|
||||
export const NOT_CONTAINS = 'notContains';
|
||||
export const NOT_EQUAL = 'notEqual';
|
||||
export const STARTS_WITH = 'startsWith';
|
||||
|
|
@ -22,6 +23,7 @@ export const all = [
|
|||
GREATER_THAN_OR_EQUAL,
|
||||
LESS_THAN,
|
||||
LESS_THAN_OR_EQUAL,
|
||||
IN_PAST,
|
||||
NOT_CONTAINS,
|
||||
NOT_EQUAL,
|
||||
IN_LAST,
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ function MovieIndexOverview(props: MovieIndexOverviewProps) {
|
|||
tags,
|
||||
hasFile,
|
||||
isAvailable,
|
||||
dateConsideredAvailable,
|
||||
tmdbId,
|
||||
imdbId,
|
||||
studio,
|
||||
|
|
@ -165,6 +166,7 @@ function MovieIndexOverview(props: MovieIndexOverviewProps) {
|
|||
monitored={monitored}
|
||||
hasFile={hasFile}
|
||||
isAvailable={isAvailable}
|
||||
dateConsideredAvailable={dateConsideredAvailable}
|
||||
status={status}
|
||||
width={posterWidth}
|
||||
detailedProgressBar={overviewOptions.detailedProgressBar}
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ function MovieIndexPoster(props: MovieIndexPosterProps) {
|
|||
studio,
|
||||
added,
|
||||
year,
|
||||
dateConsideredAvailable,
|
||||
inCinemas,
|
||||
physicalRelease,
|
||||
digitalRelease,
|
||||
|
|
@ -219,6 +220,7 @@ function MovieIndexPoster(props: MovieIndexPosterProps) {
|
|||
monitored={monitored}
|
||||
hasFile={hasFile}
|
||||
isAvailable={isAvailable}
|
||||
dateConsideredAvailable={dateConsideredAvailable}
|
||||
status={status}
|
||||
width={posterWidth}
|
||||
detailedProgressBar={detailedProgressBar}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ interface MovieIndexProgressBarProps {
|
|||
status: MovieStatus;
|
||||
hasFile: boolean;
|
||||
isAvailable: boolean;
|
||||
dateConsideredAvailable: string;
|
||||
width: number;
|
||||
detailedProgressBar: boolean;
|
||||
bottomRadius?: boolean;
|
||||
|
|
@ -31,6 +32,7 @@ function MovieIndexProgressBar({
|
|||
status,
|
||||
hasFile,
|
||||
isAvailable,
|
||||
dateConsideredAvailable,
|
||||
width,
|
||||
detailedProgressBar,
|
||||
bottomRadius,
|
||||
|
|
@ -74,6 +76,7 @@ function MovieIndexProgressBar({
|
|||
showText={detailedProgressBar}
|
||||
width={width}
|
||||
text={queueStatusText ? queueStatusText : movieStatus}
|
||||
title={movieStatus === 'NotAvailable' ? dateConsideredAvailable : ''}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ function MovieIndexRow(props: MovieIndexRowProps) {
|
|||
tmdbId,
|
||||
imdbId,
|
||||
isAvailable,
|
||||
dateConsideredAvailable,
|
||||
hasFile,
|
||||
movieFile,
|
||||
youTubeTrailerId,
|
||||
|
|
@ -363,6 +364,7 @@ function MovieIndexRow(props: MovieIndexRowProps) {
|
|||
monitored={monitored}
|
||||
hasFile={hasFile}
|
||||
isAvailable={isAvailable}
|
||||
dateConsideredAvailable={dateConsideredAvailable}
|
||||
status={status}
|
||||
width={125}
|
||||
detailedProgressBar={true}
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ interface Movie extends ModelBase {
|
|||
grabbed?: boolean;
|
||||
lastSearchTime?: string;
|
||||
isAvailable: boolean;
|
||||
dateConsideredAvailable: string;
|
||||
isSaving?: boolean;
|
||||
addOptions: MovieAddOptions;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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'),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -316,12 +316,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'),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -305,6 +305,7 @@
|
|||
"Database": "Database",
|
||||
"DatabaseMigration": "Database Migration",
|
||||
"Date": "Date",
|
||||
"DateConsideredAvailable": "Date Considered Available",
|
||||
"Dates": "Dates",
|
||||
"Day": "Day",
|
||||
"DayOfWeekAt": "{day} at {time}",
|
||||
|
|
@ -1075,6 +1076,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",
|
||||
|
|
|
|||
|
|
@ -76,70 +76,40 @@ public string FolderName()
|
|||
|
||||
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)
|
||||
if (isAvailabilityCheck)
|
||||
{
|
||||
minimumAvailabilityDate = MovieMetadata.Value.InCinemas.Value;
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
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)
|
||||
{
|
||||
minimumAvailabilityDate = MovieMetadata.Value.PhysicalRelease.Value;
|
||||
}
|
||||
else if (MovieMetadata.Value.DigitalRelease.HasValue)
|
||||
{
|
||||
minimumAvailabilityDate = MovieMetadata.Value.DigitalRelease.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
minimumAvailabilityDate = MovieMetadata.Value.InCinemas?.AddDays(90) ?? DateTime.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (minimumAvailabilityDate == DateTime.MinValue || minimumAvailabilityDate == DateTime.MaxValue)
|
||||
{
|
||||
return DateTime.UtcNow >= minimumAvailabilityDate;
|
||||
}
|
||||
|
||||
return DateTime.UtcNow >= minimumAvailabilityDate.AddDays(delay);
|
||||
}
|
||||
|
||||
public DateTime? GetReleaseDate()
|
||||
{
|
||||
if (MinimumAvailability is MovieStatusType.TBA or MovieStatusType.Announced)
|
||||
else if (MovieMetadata.Value.InCinemas.HasValue || MovieMetadata.Value.DigitalRelease.HasValue || MovieMetadata.Value.PhysicalRelease.HasValue)
|
||||
{
|
||||
return new[] { MovieMetadata.Value.InCinemas, MovieMetadata.Value.DigitalRelease, MovieMetadata.Value.PhysicalRelease }
|
||||
.Where(x => x.HasValue)
|
||||
.Min();
|
||||
.Min()?.AddDays(delay);
|
||||
}
|
||||
|
||||
if (MinimumAvailability == MovieStatusType.InCinemas && MovieMetadata.Value.InCinemas.HasValue)
|
||||
}
|
||||
else if (MinimumAvailability == MovieStatusType.InCinemas && MovieMetadata.Value.InCinemas.HasValue)
|
||||
{
|
||||
return MovieMetadata.Value.InCinemas.Value;
|
||||
return MovieMetadata.Value.InCinemas.Value.AddDays(delay);
|
||||
}
|
||||
|
||||
if (MovieMetadata.Value.DigitalRelease.HasValue || MovieMetadata.Value.PhysicalRelease.HasValue)
|
||||
else if (MovieMetadata.Value.DigitalRelease.HasValue || MovieMetadata.Value.PhysicalRelease.HasValue)
|
||||
{
|
||||
return new[] { MovieMetadata.Value.DigitalRelease, MovieMetadata.Value.PhysicalRelease }
|
||||
.Where(x => x.HasValue)
|
||||
.Min();
|
||||
.Min()?.AddDays(delay);
|
||||
}
|
||||
else if (!MovieMetadata.Value.InCinemas.HasValue && isAvailabilityCheck)
|
||||
{
|
||||
return DateTime.MaxValue;
|
||||
}
|
||||
|
||||
return MovieMetadata.Value.InCinemas?.AddDays(90);
|
||||
return MovieMetadata.Value.InCinemas?.AddDays(90 + delay);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
|
|
|||
|
|
@ -63,7 +63,9 @@ public MovieResource()
|
|||
// 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 @@ public static MovieResource ToResource(this Movie model, int availDelay, MovieTr
|
|||
MinimumAvailability = model.MinimumAvailability,
|
||||
|
||||
IsAvailable = model.IsAvailable(availDelay),
|
||||
DateConsideredAvailable = model.GetReleaseDate(availDelay),
|
||||
MinimumAvailabilityDate = model.GetReleaseDate(),
|
||||
FolderName = model.FolderName(),
|
||||
|
||||
Runtime = model.MovieMetadata.Value.Runtime,
|
||||
|
|
|
|||
Loading…
Reference in a new issue