Fixed: Improve sorting movies by release dates

pull/10340/head
Bogdan 5 months ago
parent bf84471509
commit df77474314

@ -106,7 +106,7 @@ function MovieIndexSortMenu(props: MovieIndexSortMenuProps) {
sortDirection={sortDirection} sortDirection={sortDirection}
onPress={onSortSelect} onPress={onSortSelect}
> >
{translate('ReleaseDates')} {translate('ReleaseDate')}
</SortMenuItem> </SortMenuItem>
<SortMenuItem <SortMenuItem

@ -75,6 +75,7 @@ function MovieIndexPoster(props: MovieIndexPosterProps) {
inCinemas, inCinemas,
physicalRelease, physicalRelease,
digitalRelease, digitalRelease,
releaseDate,
path, path,
movieFile, movieFile,
ratings, ratings,
@ -142,20 +143,6 @@ function MovieIndexPoster(props: MovieIndexPosterProps) {
height: `${posterHeight}px`, height: `${posterHeight}px`,
}; };
let releaseDate = '';
let releaseDateType = '';
if (physicalRelease && digitalRelease) {
releaseDate =
physicalRelease < digitalRelease ? physicalRelease : digitalRelease;
releaseDateType = physicalRelease < digitalRelease ? 'Released' : 'Digital';
} else if (physicalRelease && !digitalRelease) {
releaseDate = physicalRelease;
releaseDateType = 'Released';
} else if (digitalRelease && !physicalRelease) {
releaseDate = digitalRelease;
releaseDateType = 'Digital';
}
return ( return (
<div className={styles.content}> <div className={styles.content}>
<div className={styles.posterContainer} title={title}> <div className={styles.posterContainer} title={title}>
@ -262,10 +249,8 @@ function MovieIndexPoster(props: MovieIndexPosterProps) {
) : null} ) : null}
{showReleaseDate && releaseDate ? ( {showReleaseDate && releaseDate ? (
<div className={styles.title}> <div className={styles.title} title={translate('ReleaseDate')}>
<Icon <Icon name={icons.CALENDAR} />{' '}
name={releaseDateType === 'Digital' ? icons.MOVIE_FILE : icons.DISC}
/>{' '}
{getRelativeDate(releaseDate, shortDateFormat, showRelativeDates, { {getRelativeDate(releaseDate, shortDateFormat, showRelativeDates, {
timeFormat, timeFormat,
timeForToday: false, timeForToday: false,
@ -314,6 +299,7 @@ function MovieIndexPoster(props: MovieIndexPosterProps) {
inCinemas={inCinemas} inCinemas={inCinemas}
physicalRelease={physicalRelease} physicalRelease={physicalRelease}
digitalRelease={digitalRelease} digitalRelease={digitalRelease}
releaseDate={releaseDate}
ratings={ratings} ratings={ratings}
sizeOnDisk={sizeOnDisk} sizeOnDisk={sizeOnDisk}
sortKey={sortKey} sortKey={sortKey}

@ -23,6 +23,7 @@ interface MovieIndexPosterInfoProps {
inCinemas?: string; inCinemas?: string;
digitalRelease?: string; digitalRelease?: string;
physicalRelease?: string; physicalRelease?: string;
releaseDate?: string;
path: string; path: string;
ratings: Ratings; ratings: Ratings;
certification: string; certification: string;
@ -53,6 +54,7 @@ function MovieIndexPosterInfo(props: MovieIndexPosterInfoProps) {
inCinemas, inCinemas,
digitalRelease, digitalRelease,
physicalRelease, physicalRelease,
releaseDate,
path, path,
ratings, ratings,
certification, certification,
@ -152,7 +154,7 @@ function MovieIndexPosterInfo(props: MovieIndexPosterInfoProps) {
); );
return ( return (
<div className={styles.info}> <div className={styles.info} title={translate('DigitalRelease')}>
<Icon name={icons.MOVIE_FILE} /> {digitalReleaseDate} <Icon name={icons.MOVIE_FILE} /> {digitalReleaseDate}
</div> </div>
); );
@ -170,12 +172,24 @@ function MovieIndexPosterInfo(props: MovieIndexPosterInfoProps) {
); );
return ( return (
<div className={styles.info}> <div className={styles.info} title={translate('PhysicalRelease')}>
<Icon name={icons.DISC} /> {physicalReleaseDate} <Icon name={icons.DISC} /> {physicalReleaseDate}
</div> </div>
); );
} }
if (sortKey === 'releaseDate' && releaseDate && !showReleaseDate) {
return (
<div className={styles.info} title={translate('ReleaseDate')}>
<Icon name={icons.CALENDAR} />{' '}
{getRelativeDate(releaseDate, shortDateFormat, showRelativeDates, {
timeFormat,
timeForToday: false,
})}
</div>
);
}
if (!showTmdbRating && sortKey === 'tmdbRating' && !!ratings.tmdb) { if (!showTmdbRating && sortKey === 'tmdbRating' && !!ratings.tmdb) {
return ( return (
<div className={styles.info}> <div className={styles.info}>

@ -218,6 +218,7 @@ export default function MovieIndexPosters(props: MovieIndexPostersProps) {
break; break;
case 'digitalRelease': case 'digitalRelease':
case 'physicalRelease': case 'physicalRelease':
case 'releaseDate':
if (!showReleaseDate) { if (!showReleaseDate) {
heights.push(19); heights.push(19);
} }

@ -42,6 +42,7 @@
.inCinemas, .inCinemas,
.physicalRelease, .physicalRelease,
.digitalRelease, .digitalRelease,
.releaseDate,
.genres { .genres {
composes: cell; composes: cell;

@ -20,6 +20,7 @@ interface CssExports {
'physicalRelease': string; 'physicalRelease': string;
'popularity': string; 'popularity': string;
'qualityProfileId': string; 'qualityProfileId': string;
'releaseDate': string;
'releaseGroups': string; 'releaseGroups': string;
'rottenTomatoesRating': string; 'rottenTomatoesRating': string;
'runtime': string; 'runtime': string;

@ -66,6 +66,7 @@ function MovieIndexRow(props: MovieIndexRowProps) {
inCinemas, inCinemas,
digitalRelease, digitalRelease,
physicalRelease, physicalRelease,
releaseDate,
runtime, runtime,
minimumAvailability, minimumAvailability,
path, path,
@ -278,6 +279,19 @@ function MovieIndexRow(props: MovieIndexRowProps) {
); );
} }
if (name === 'releaseDate') {
return (
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore ts(2739)
<RelativeDateCellConnector
key={name}
className={styles[name]}
date={releaseDate}
component={VirtualTableRowCell}
/>
);
}
if (name === 'runtime') { if (name === 'runtime') {
return ( return (
<VirtualTableRowCell key={name} className={styles[name]}> <VirtualTableRowCell key={name} className={styles[name]}>

@ -35,6 +35,7 @@
.inCinemas, .inCinemas,
.physicalRelease, .physicalRelease,
.digitalRelease, .digitalRelease,
.releaseDate,
.genres { .genres {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css'; composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';

@ -17,6 +17,7 @@ interface CssExports {
'physicalRelease': string; 'physicalRelease': string;
'popularity': string; 'popularity': string;
'qualityProfileId': string; 'qualityProfileId': string;
'releaseDate': string;
'releaseGroups': string; 'releaseGroups': string;
'rottenTomatoesRating': string; 'rottenTomatoesRating': string;
'runtime': string; 'runtime': string;

@ -42,16 +42,17 @@ interface Movie extends ModelBase {
status: MovieStatus; status: MovieStatus;
title: string; title: string;
titleSlug: string; titleSlug: string;
originalTitle: string;
originalLanguage: Language;
collection: Collection; collection: Collection;
studio: string; studio: string;
qualityProfileId: number; qualityProfileId: number;
added: string; added: string;
year: number; year: number;
inCinemas: string; inCinemas?: string;
physicalRelease: string; physicalRelease?: string;
originalLanguage: Language; digitalRelease?: string;
originalTitle: string; releaseDate?: string;
digitalRelease: string;
runtime: number; runtime: number;
minimumAvailability: string; minimumAvailability: string;
path: string; path: string;

@ -258,8 +258,10 @@ export const sortPredicates = {
}, },
inCinemas: function(item, direction) { inCinemas: function(item, direction) {
if (item.inCinemas) { const { inCinemas } = item;
return moment(item.inCinemas).unix();
if (inCinemas) {
return moment(inCinemas).unix();
} }
if (direction === sortDirections.DESCENDING) { if (direction === sortDirections.DESCENDING) {
@ -270,8 +272,10 @@ export const sortPredicates = {
}, },
physicalRelease: function(item, direction) { physicalRelease: function(item, direction) {
if (item.physicalRelease) { const { physicalRelease } = item;
return moment(item.physicalRelease).unix();
if (physicalRelease) {
return moment(physicalRelease).unix();
} }
if (direction === sortDirections.DESCENDING) { if (direction === sortDirections.DESCENDING) {
@ -282,8 +286,10 @@ export const sortPredicates = {
}, },
digitalRelease: function(item, direction) { digitalRelease: function(item, direction) {
if (item.digitalRelease) { const { digitalRelease } = item;
return moment(item.digitalRelease).unix();
if (digitalRelease) {
return moment(digitalRelease).unix();
} }
if (direction === sortDirections.DESCENDING) { if (direction === sortDirections.DESCENDING) {
@ -294,8 +300,7 @@ export const sortPredicates = {
}, },
releaseDate: function(item, direction) { releaseDate: function(item, direction) {
const { inCinemas, digitalRelease, physicalRelease } = item; const { releaseDate } = item;
const releaseDate = digitalRelease || physicalRelease || inCinemas;
if (releaseDate) { if (releaseDate) {
return moment(releaseDate).unix(); return moment(releaseDate).unix();

@ -142,6 +142,12 @@ export const defaultState = {
isSortable: true, isSortable: true,
isVisible: false isVisible: false
}, },
{
name: 'releaseDate',
label: () => translate('ReleaseDate'),
isSortable: true,
isVisible: false
},
{ {
name: 'runtime', name: 'runtime',
label: () => translate('Runtime'), label: () => translate('Runtime'),
@ -433,6 +439,12 @@ export const defaultState = {
type: filterBuilderTypes.DATE, type: filterBuilderTypes.DATE,
valueType: filterBuilderValueTypes.DATE valueType: filterBuilderValueTypes.DATE
}, },
{
name: 'releaseDate',
label: () => translate('ReleaseDate'),
type: filterBuilderTypes.DATE,
valueType: filterBuilderValueTypes.DATE
},
{ {
name: 'runtime', name: 'runtime',
label: () => translate('Runtime'), label: () => translate('Runtime'),

@ -1380,6 +1380,7 @@
"RelativePath": "Relative Path", "RelativePath": "Relative Path",
"Release": "Release", "Release": "Release",
"ReleaseBranchCheckOfficialBranchMessage": "Branch {0} is not a valid {appName} release branch, you will not receive updates", "ReleaseBranchCheckOfficialBranchMessage": "Branch {0} is not a valid {appName} release branch, you will not receive updates",
"ReleaseDate": "Release Date",
"ReleaseDates": "Release Dates", "ReleaseDates": "Release Dates",
"ReleaseGroup": "Release Group", "ReleaseGroup": "Release Group",
"ReleaseGroupFootNote": "Optionally control truncation to a maximum number of bytes including ellipsis (`...`). Truncating from the end (e.g. `{Release Group:30}`) or the beginning (e.g. `{Release Group:-30}`) are both supported.`).", "ReleaseGroupFootNote": "Optionally control truncation to a maximum number of bytes including ellipsis (`...`). Truncating from the end (e.g. `{Release Group:30}`) or the beginning (e.g. `{Release Group:-30}`) are both supported.`).",

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
@ -117,6 +118,30 @@ namespace NzbDrone.Core.Movies
return DateTime.UtcNow >= minimumAvailabilityDate.AddDays(delay); return DateTime.UtcNow >= minimumAvailabilityDate.AddDays(delay);
} }
public DateTime? GetReleaseDate()
{
if (MinimumAvailability is MovieStatusType.TBA or MovieStatusType.Announced)
{
return new[] { MovieMetadata.Value.InCinemas, MovieMetadata.Value.DigitalRelease, MovieMetadata.Value.PhysicalRelease }
.Where(x => x.HasValue)
.Min();
}
if (MinimumAvailability == MovieStatusType.InCinemas && MovieMetadata.Value.InCinemas.HasValue)
{
return MovieMetadata.Value.InCinemas.Value;
}
if (MovieMetadata.Value.DigitalRelease.HasValue || MovieMetadata.Value.PhysicalRelease.HasValue)
{
return new[] { MovieMetadata.Value.DigitalRelease, MovieMetadata.Value.PhysicalRelease }
.Where(x => x.HasValue)
.Min();
}
return MovieMetadata.Value.InCinemas?.AddDays(90);
}
public override string ToString() public override string ToString()
{ {
return string.Format("[{1} ({2})][{0}, {3}]", MovieMetadata.Value.ImdbId, MovieMetadata.Value.Title.NullSafe(), MovieMetadata.Value.Year.NullSafe(), MovieMetadata.Value.TmdbId); return string.Format("[{1} ({2})][{0}, {3}]", MovieMetadata.Value.ImdbId, MovieMetadata.Value.Title.NullSafe(), MovieMetadata.Value.Year.NullSafe(), MovieMetadata.Value.TmdbId);

@ -42,6 +42,7 @@ namespace Radarr.Api.V3.Movies
public DateTime? InCinemas { get; set; } public DateTime? InCinemas { get; set; }
public DateTime? PhysicalRelease { get; set; } public DateTime? PhysicalRelease { get; set; }
public DateTime? DigitalRelease { get; set; } public DateTime? DigitalRelease { get; set; }
public DateTime? ReleaseDate { get; set; }
public string PhysicalReleaseNote { get; set; } public string PhysicalReleaseNote { get; set; }
public List<MediaCover> Images { get; set; } public List<MediaCover> Images { get; set; }
public string Website { get; set; } public string Website { get; set; }
@ -122,6 +123,7 @@ namespace Radarr.Api.V3.Movies
InCinemas = model.MovieMetadata.Value.InCinemas, InCinemas = model.MovieMetadata.Value.InCinemas,
PhysicalRelease = model.MovieMetadata.Value.PhysicalRelease, PhysicalRelease = model.MovieMetadata.Value.PhysicalRelease,
DigitalRelease = model.MovieMetadata.Value.DigitalRelease, DigitalRelease = model.MovieMetadata.Value.DigitalRelease,
ReleaseDate = model.GetReleaseDate(),
Status = model.MovieMetadata.Value.Status, Status = model.MovieMetadata.Value.Status,
Overview = translatedOverview, Overview = translatedOverview,

Loading…
Cancel
Save