From df77474314fff40de38073872ffb8478a09cb875 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Fri, 23 Aug 2024 03:21:19 +0300 Subject: [PATCH] Fixed: Improve sorting movies by release dates --- .../Movie/Index/Menus/MovieIndexSortMenu.tsx | 2 +- .../Movie/Index/Posters/MovieIndexPoster.tsx | 22 +++------------- .../Index/Posters/MovieIndexPosterInfo.tsx | 18 +++++++++++-- .../Movie/Index/Posters/MovieIndexPosters.tsx | 1 + .../src/Movie/Index/Table/MovieIndexRow.css | 1 + .../Movie/Index/Table/MovieIndexRow.css.d.ts | 1 + .../src/Movie/Index/Table/MovieIndexRow.tsx | 14 +++++++++++ .../Index/Table/MovieIndexTableHeader.css | 1 + .../Table/MovieIndexTableHeader.css.d.ts | 1 + frontend/src/Movie/Movie.ts | 11 ++++---- frontend/src/Store/Actions/movieActions.js | 21 ++++++++++------ .../src/Store/Actions/movieIndexActions.js | 12 +++++++++ src/NzbDrone.Core/Localization/Core/en.json | 1 + src/NzbDrone.Core/Movies/Movie.cs | 25 +++++++++++++++++++ src/Radarr.Api.V3/Movies/MovieResource.cs | 2 ++ 15 files changed, 99 insertions(+), 34 deletions(-) diff --git a/frontend/src/Movie/Index/Menus/MovieIndexSortMenu.tsx b/frontend/src/Movie/Index/Menus/MovieIndexSortMenu.tsx index 4d115e619..390263a6f 100644 --- a/frontend/src/Movie/Index/Menus/MovieIndexSortMenu.tsx +++ b/frontend/src/Movie/Index/Menus/MovieIndexSortMenu.tsx @@ -106,7 +106,7 @@ function MovieIndexSortMenu(props: MovieIndexSortMenuProps) { sortDirection={sortDirection} onPress={onSortSelect} > - {translate('ReleaseDates')} + {translate('ReleaseDate')}
@@ -262,10 +249,8 @@ function MovieIndexPoster(props: MovieIndexPosterProps) { ) : null} {showReleaseDate && releaseDate ? ( -
- {' '} +
+ {' '} {getRelativeDate(releaseDate, shortDateFormat, showRelativeDates, { timeFormat, timeForToday: false, @@ -314,6 +299,7 @@ function MovieIndexPoster(props: MovieIndexPosterProps) { inCinemas={inCinemas} physicalRelease={physicalRelease} digitalRelease={digitalRelease} + releaseDate={releaseDate} ratings={ratings} sizeOnDisk={sizeOnDisk} sortKey={sortKey} diff --git a/frontend/src/Movie/Index/Posters/MovieIndexPosterInfo.tsx b/frontend/src/Movie/Index/Posters/MovieIndexPosterInfo.tsx index 0d3cdc17e..70ed4bb72 100644 --- a/frontend/src/Movie/Index/Posters/MovieIndexPosterInfo.tsx +++ b/frontend/src/Movie/Index/Posters/MovieIndexPosterInfo.tsx @@ -23,6 +23,7 @@ interface MovieIndexPosterInfoProps { inCinemas?: string; digitalRelease?: string; physicalRelease?: string; + releaseDate?: string; path: string; ratings: Ratings; certification: string; @@ -53,6 +54,7 @@ function MovieIndexPosterInfo(props: MovieIndexPosterInfoProps) { inCinemas, digitalRelease, physicalRelease, + releaseDate, path, ratings, certification, @@ -152,7 +154,7 @@ function MovieIndexPosterInfo(props: MovieIndexPosterInfoProps) { ); return ( -
+
{digitalReleaseDate}
); @@ -170,12 +172,24 @@ function MovieIndexPosterInfo(props: MovieIndexPosterInfoProps) { ); return ( -
+
{physicalReleaseDate}
); } + if (sortKey === 'releaseDate' && releaseDate && !showReleaseDate) { + return ( +
+ {' '} + {getRelativeDate(releaseDate, shortDateFormat, showRelativeDates, { + timeFormat, + timeForToday: false, + })} +
+ ); + } + if (!showTmdbRating && sortKey === 'tmdbRating' && !!ratings.tmdb) { return (
diff --git a/frontend/src/Movie/Index/Posters/MovieIndexPosters.tsx b/frontend/src/Movie/Index/Posters/MovieIndexPosters.tsx index ebbece0de..3475e0b2b 100644 --- a/frontend/src/Movie/Index/Posters/MovieIndexPosters.tsx +++ b/frontend/src/Movie/Index/Posters/MovieIndexPosters.tsx @@ -218,6 +218,7 @@ export default function MovieIndexPosters(props: MovieIndexPostersProps) { break; case 'digitalRelease': case 'physicalRelease': + case 'releaseDate': if (!showReleaseDate) { heights.push(19); } diff --git a/frontend/src/Movie/Index/Table/MovieIndexRow.css b/frontend/src/Movie/Index/Table/MovieIndexRow.css index 9a7f28b72..9049fc689 100644 --- a/frontend/src/Movie/Index/Table/MovieIndexRow.css +++ b/frontend/src/Movie/Index/Table/MovieIndexRow.css @@ -42,6 +42,7 @@ .inCinemas, .physicalRelease, .digitalRelease, +.releaseDate, .genres { composes: cell; diff --git a/frontend/src/Movie/Index/Table/MovieIndexRow.css.d.ts b/frontend/src/Movie/Index/Table/MovieIndexRow.css.d.ts index 1ab4f519c..7438a7603 100644 --- a/frontend/src/Movie/Index/Table/MovieIndexRow.css.d.ts +++ b/frontend/src/Movie/Index/Table/MovieIndexRow.css.d.ts @@ -20,6 +20,7 @@ interface CssExports { 'physicalRelease': string; 'popularity': string; 'qualityProfileId': string; + 'releaseDate': string; 'releaseGroups': string; 'rottenTomatoesRating': string; 'runtime': string; diff --git a/frontend/src/Movie/Index/Table/MovieIndexRow.tsx b/frontend/src/Movie/Index/Table/MovieIndexRow.tsx index 22bf5f4cb..91137f4f4 100644 --- a/frontend/src/Movie/Index/Table/MovieIndexRow.tsx +++ b/frontend/src/Movie/Index/Table/MovieIndexRow.tsx @@ -66,6 +66,7 @@ function MovieIndexRow(props: MovieIndexRowProps) { inCinemas, digitalRelease, physicalRelease, + releaseDate, runtime, minimumAvailability, 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) + + ); + } + if (name === 'runtime') { return ( diff --git a/frontend/src/Movie/Index/Table/MovieIndexTableHeader.css b/frontend/src/Movie/Index/Table/MovieIndexTableHeader.css index 4ad0acb1c..5481d275a 100644 --- a/frontend/src/Movie/Index/Table/MovieIndexTableHeader.css +++ b/frontend/src/Movie/Index/Table/MovieIndexTableHeader.css @@ -35,6 +35,7 @@ .inCinemas, .physicalRelease, .digitalRelease, +.releaseDate, .genres { composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css'; diff --git a/frontend/src/Movie/Index/Table/MovieIndexTableHeader.css.d.ts b/frontend/src/Movie/Index/Table/MovieIndexTableHeader.css.d.ts index 29c46c5c3..5e999f716 100644 --- a/frontend/src/Movie/Index/Table/MovieIndexTableHeader.css.d.ts +++ b/frontend/src/Movie/Index/Table/MovieIndexTableHeader.css.d.ts @@ -17,6 +17,7 @@ interface CssExports { 'physicalRelease': string; 'popularity': string; 'qualityProfileId': string; + 'releaseDate': string; 'releaseGroups': string; 'rottenTomatoesRating': string; 'runtime': string; diff --git a/frontend/src/Movie/Movie.ts b/frontend/src/Movie/Movie.ts index 0a4941e5b..845bb0261 100644 --- a/frontend/src/Movie/Movie.ts +++ b/frontend/src/Movie/Movie.ts @@ -42,16 +42,17 @@ interface Movie extends ModelBase { status: MovieStatus; title: string; titleSlug: string; + originalTitle: string; + originalLanguage: Language; collection: Collection; studio: string; qualityProfileId: number; added: string; year: number; - inCinemas: string; - physicalRelease: string; - originalLanguage: Language; - originalTitle: string; - digitalRelease: string; + inCinemas?: string; + physicalRelease?: string; + digitalRelease?: string; + releaseDate?: string; runtime: number; minimumAvailability: string; path: string; diff --git a/frontend/src/Store/Actions/movieActions.js b/frontend/src/Store/Actions/movieActions.js index 3f66b0740..32d961ceb 100644 --- a/frontend/src/Store/Actions/movieActions.js +++ b/frontend/src/Store/Actions/movieActions.js @@ -258,8 +258,10 @@ export const sortPredicates = { }, inCinemas: function(item, direction) { - if (item.inCinemas) { - return moment(item.inCinemas).unix(); + const { inCinemas } = item; + + if (inCinemas) { + return moment(inCinemas).unix(); } if (direction === sortDirections.DESCENDING) { @@ -270,8 +272,10 @@ export const sortPredicates = { }, physicalRelease: function(item, direction) { - if (item.physicalRelease) { - return moment(item.physicalRelease).unix(); + const { physicalRelease } = item; + + if (physicalRelease) { + return moment(physicalRelease).unix(); } if (direction === sortDirections.DESCENDING) { @@ -282,8 +286,10 @@ export const sortPredicates = { }, digitalRelease: function(item, direction) { - if (item.digitalRelease) { - return moment(item.digitalRelease).unix(); + const { digitalRelease } = item; + + if (digitalRelease) { + return moment(digitalRelease).unix(); } if (direction === sortDirections.DESCENDING) { @@ -294,8 +300,7 @@ export const sortPredicates = { }, releaseDate: function(item, direction) { - const { inCinemas, digitalRelease, physicalRelease } = item; - const releaseDate = digitalRelease || physicalRelease || inCinemas; + const { releaseDate } = item; if (releaseDate) { return moment(releaseDate).unix(); diff --git a/frontend/src/Store/Actions/movieIndexActions.js b/frontend/src/Store/Actions/movieIndexActions.js index 51c4009f9..7e6efd3d6 100644 --- a/frontend/src/Store/Actions/movieIndexActions.js +++ b/frontend/src/Store/Actions/movieIndexActions.js @@ -142,6 +142,12 @@ export const defaultState = { isSortable: true, isVisible: false }, + { + name: 'releaseDate', + label: () => translate('ReleaseDate'), + isSortable: true, + isVisible: false + }, { name: 'runtime', label: () => translate('Runtime'), @@ -433,6 +439,12 @@ export const defaultState = { type: filterBuilderTypes.DATE, valueType: filterBuilderValueTypes.DATE }, + { + name: 'releaseDate', + label: () => translate('ReleaseDate'), + type: filterBuilderTypes.DATE, + valueType: filterBuilderValueTypes.DATE + }, { name: 'runtime', label: () => translate('Runtime'), diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index 0fe33dc14..a80674566 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -1380,6 +1380,7 @@ "RelativePath": "Relative Path", "Release": "Release", "ReleaseBranchCheckOfficialBranchMessage": "Branch {0} is not a valid {appName} release branch, you will not receive updates", + "ReleaseDate": "Release Date", "ReleaseDates": "Release Dates", "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.`).", diff --git a/src/NzbDrone.Core/Movies/Movie.cs b/src/NzbDrone.Core/Movies/Movie.cs index 1e4999ef7..1ef429fa2 100644 --- a/src/NzbDrone.Core/Movies/Movie.cs +++ b/src/NzbDrone.Core/Movies/Movie.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using NzbDrone.Common.Extensions; using NzbDrone.Core.Datastore; using NzbDrone.Core.MediaFiles; @@ -117,6 +118,30 @@ public bool IsAvailable(int delay = 0) 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() { return string.Format("[{1} ({2})][{0}, {3}]", MovieMetadata.Value.ImdbId, MovieMetadata.Value.Title.NullSafe(), MovieMetadata.Value.Year.NullSafe(), MovieMetadata.Value.TmdbId); diff --git a/src/Radarr.Api.V3/Movies/MovieResource.cs b/src/Radarr.Api.V3/Movies/MovieResource.cs index 71594a74e..a011ed7ef 100644 --- a/src/Radarr.Api.V3/Movies/MovieResource.cs +++ b/src/Radarr.Api.V3/Movies/MovieResource.cs @@ -42,6 +42,7 @@ public MovieResource() public DateTime? InCinemas { get; set; } public DateTime? PhysicalRelease { get; set; } public DateTime? DigitalRelease { get; set; } + public DateTime? ReleaseDate { get; set; } public string PhysicalReleaseNote { get; set; } public List Images { get; set; } public string Website { get; set; } @@ -122,6 +123,7 @@ public static MovieResource ToResource(this Movie model, int availDelay, MovieTr InCinemas = model.MovieMetadata.Value.InCinemas, PhysicalRelease = model.MovieMetadata.Value.PhysicalRelease, DigitalRelease = model.MovieMetadata.Value.DigitalRelease, + ReleaseDate = model.GetReleaseDate(), Status = model.MovieMetadata.Value.Status, Overview = translatedOverview,