Skip to content

Commit febfb52

Browse files
committed
feat(tvrequestmodal,movierequestmodal): warning for no not yeat released
1 parent ff88d52 commit febfb52

11 files changed

Lines changed: 117 additions & 0 deletions

File tree

cypress/config/settings.cypress.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"tv": {}
1515
},
1616
"hideAvailable": false,
17+
"warnNonReleased": false,
1718
"localLogin": true,
1819
"newPlexLogin": true,
1920
"discoverRegion": "",

docs/using-seerr/settings/general.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ Available media will still appear in search results, however, so it is possible
6565

6666
This setting is **disabled** by default.
6767

68+
## Warn about Non-released
69+
70+
When enabled, requests for items without a digital or physical release, or with a release date in the future, will display a warning to help set expectations.
71+
72+
This setting is **disabled** by default.
73+
6874
## Hide Blocklisted Items
6975

7076
When enabled, media that has been blocklisted will not appear on the "Discover" home page, for all administrators. This can be useful to hide content that you don't want to see, such as content with specific tags or content that has been manually blocklisted when you have the "Manage Blocklist" permission.

seerr-api.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,9 @@ components:
236236
hideAvailable:
237237
type: boolean
238238
example: false
239+
warnNonReleased:
240+
type: boolean
241+
example: false
239242
partialRequestsEnabled:
240243
type: boolean
241244
example: false

server/interfaces/api/settingsInterfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export interface PublicSettingsResponse {
3131
applicationUrl: string;
3232
hideAvailable: boolean;
3333
hideBlocklisted: boolean;
34+
warnNonReleased: boolean;
3435
localLogin: boolean;
3536
mediaServerLogin: boolean;
3637
movie4kEnabled: boolean;

server/lib/settings/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ export interface MainSettings {
141141
};
142142
hideAvailable: boolean;
143143
hideBlocklisted: boolean;
144+
warnNonReleased: boolean;
144145
localLogin: boolean;
145146
mediaServerLogin: boolean;
146147
newPlexLogin: boolean;
@@ -193,6 +194,7 @@ interface FullPublicSettings extends PublicSettings {
193194
applicationUrl: string;
194195
hideAvailable: boolean;
195196
hideBlocklisted: boolean;
197+
warnNonReleased: boolean;
196198
localLogin: boolean;
197199
mediaServerLogin: boolean;
198200
movie4kEnabled: boolean;
@@ -413,6 +415,7 @@ class Settings {
413415
},
414416
hideAvailable: false,
415417
hideBlocklisted: false,
418+
warnNonReleased: false,
416419
localLogin: true,
417420
mediaServerLogin: true,
418421
newPlexLogin: true,
@@ -708,6 +711,7 @@ class Settings {
708711
applicationUrl: this.data.main.applicationUrl,
709712
hideAvailable: this.data.main.hideAvailable,
710713
hideBlocklisted: this.data.main.hideBlocklisted,
714+
warnNonReleased: this.data.main.warnNonReleased,
711715
localLogin: this.data.main.localLogin,
712716
mediaServerLogin: this.data.main.mediaServerLogin,
713717
jellyfinExternalHost: this.data.jellyfin.externalHostname,

src/components/RequestModal/MovieRequestModal.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Modal from '@app/components/Common/Modal';
33
import type { RequestOverrides } from '@app/components/RequestModal/AdvancedRequester';
44
import AdvancedRequester from '@app/components/RequestModal/AdvancedRequester';
55
import QuotaDisplay from '@app/components/RequestModal/QuotaDisplay';
6+
import useSettings from '@app/hooks/useSettings';
67
import useToasts from '@app/hooks/useToasts';
78
import { useUser } from '@app/hooks/useUser';
89
import globalMessages from '@app/i18n/globalMessages';
@@ -24,6 +25,7 @@ const messages = defineMessages('components.RequestModal', {
2425
requestCancel: 'Request for <strong>{title}</strong> canceled.',
2526
requestmovietitle: 'Request Movie',
2627
requestmovie4ktitle: 'Request Movie in 4K',
28+
requestmovieNotReleased: 'This movie release is in the future',
2729
edit: 'Edit Request',
2830
approve: 'Approve Request',
2931
cancel: 'Cancel Request',
@@ -61,8 +63,23 @@ const MovieRequestModal = ({
6163
const { data, error } = useSWR<MovieDetails>(`/api/v1/movie/${tmdbId}`, {
6264
revalidateOnMount: true,
6365
});
66+
6467
const intl = useIntl();
6568
const { user, hasPermission } = useUser();
69+
const settings = useSettings();
70+
71+
const discoverRegion =
72+
user?.settings?.discoverRegion ||
73+
settings.currentSettings.discoverRegion ||
74+
'US';
75+
const nonTheatricalReleases = data?.releases.results
76+
.find((r) => r.iso_3166_1 === discoverRegion)
77+
?.release_dates?.filter((r) => r.type > 3 && r.type < 6);
78+
const now = new Date();
79+
const nonTheatricalInTheFuture =
80+
(nonTheatricalReleases?.length ?? 0) > 0 &&
81+
nonTheatricalReleases?.every((r) => new Date(r.release_date) > now);
82+
6683
const { data: quota } = useSWR<QuotaResponse>(
6784
user &&
6885
(!requestOverrides?.user?.id || hasPermission(Permission.MANAGE_USERS))
@@ -335,6 +352,15 @@ const MovieRequestModal = ({
335352
okButtonType={'primary'}
336353
backdrop={`https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/${data?.backdropPath}`}
337354
>
355+
{settings.currentSettings.warnNonReleased &&
356+
(!nonTheatricalReleases?.length || nonTheatricalInTheFuture) && (
357+
<div className="mt-6">
358+
<Alert
359+
title={intl.formatMessage(messages.requestmovieNotReleased)}
360+
type="warning"
361+
/>
362+
</div>
363+
)}
338364
{hasAutoApprove && !quota?.movie.restricted && (
339365
<div className="mt-6">
340366
<Alert

src/components/RequestModal/TvRequestModal.tsx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ const messages = defineMessages('components.RequestModal', {
3838
'Request {seasonCount} {seasonCount, plural, one {Season} other {Seasons}}',
3939
requestseasons4k:
4040
'Request {seasonCount} {seasonCount, plural, one {Season} other {Seasons}} in 4K',
41+
requesttvNoAirDate: 'The Air Date is absent',
42+
requesttvNotReleased: 'The Air Date is in the future',
4143
alreadyrequested: 'Already Requested',
4244
selectseason: 'Select Season(s)',
4345
season: 'Season',
@@ -375,6 +377,16 @@ const TvRequestModal = ({
375377

376378
const isOwner = editRequest && editRequest.requestedBy.id === user?.id;
377379

380+
const dateNow = new Date();
381+
const selectedSeasonsAirDates = selectedSeasons.map(
382+
(season) => data?.seasons?.find((s) => s.seasonNumber === season)?.airDate
383+
);
384+
const selectedSeasonsPresentAirDates = selectedSeasonsAirDates
385+
.filter((i) => i !== undefined)
386+
.map((element) => new Date(Date.parse(element)));
387+
const selectedSeasonsAirDateInTheFuture =
388+
selectedSeasonsPresentAirDates.filter((element) => element > dateNow);
389+
378390
return data && !error && !data.externalIds.tvdbId && searchModal.show ? (
379391
<SearchByNameModal
380392
tvdbId={tvdbId}
@@ -491,6 +503,34 @@ const TvRequestModal = ({
491503
/>
492504
</p>
493505
)}
506+
{settings.currentSettings.warnNonReleased &&
507+
selectedSeasonsAirDateInTheFuture.length > 0 && (
508+
<div className="mt-6">
509+
<Alert
510+
title={`${intl.formatMessage(
511+
messages.requesttvNotReleased
512+
)} (${selectedSeasonsAirDateInTheFuture
513+
.map((element) =>
514+
intl.formatDate(element, {
515+
year: 'numeric',
516+
month: 'long',
517+
day: 'numeric',
518+
})
519+
)
520+
.join(', ')})`}
521+
type="warning"
522+
/>
523+
</div>
524+
)}
525+
{settings.currentSettings.warnNonReleased &&
526+
selectedSeasonsAirDates.some((element) => element === undefined) && (
527+
<div className="mt-6">
528+
<Alert
529+
title={intl.formatMessage(messages.requesttvNoAirDate)}
530+
type="warning"
531+
/>
532+
</div>
533+
)}
494534
{(quota?.tv.limit ?? 0) > 0 && (
495535
<QuotaDisplay
496536
mediaType="tv"

src/components/Settings/SettingsMain/index.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ const messages = defineMessages('components.Settings.SettingsMain', {
5959
toastApiKeyFailure: 'Something went wrong while generating a new API key.',
6060
toastSettingsSuccess: 'Settings saved successfully!',
6161
toastSettingsFailure: 'Something went wrong while saving settings.',
62+
warnNonReleased: 'Warn if Non released',
63+
warnNonReleasedTip:
64+
'Show a warning to users when requesting titles that are still in theatres or not yet released.',
6265
hideAvailable: 'Hide Available Media',
6366
hideAvailableTip:
6467
'Hide available media from the discover pages but not search results',
@@ -171,6 +174,7 @@ const SettingsMain = () => {
171174
applicationUrl: data?.applicationUrl,
172175
hideAvailable: data?.hideAvailable,
173176
hideBlocklisted: data?.hideBlocklisted,
177+
warnNonReleased: data?.warnNonReleased,
174178
locale: data?.locale ?? 'en',
175179
discoverRegion: data?.discoverRegion,
176180
originalLanguage: data?.originalLanguage,
@@ -193,6 +197,7 @@ const SettingsMain = () => {
193197
applicationUrl: values.applicationUrl,
194198
hideAvailable: values.hideAvailable,
195199
hideBlocklisted: values.hideBlocklisted,
200+
warnNonReleased: values.warnNonReleased,
196201
locale: values.locale,
197202
discoverRegion: values.discoverRegion,
198203
streamingRegion: values.streamingRegion,
@@ -538,6 +543,30 @@ const SettingsMain = () => {
538543
/>
539544
</div>
540545
</div>
546+
<div className="form-row">
547+
<label htmlFor="warnNonReleased" className="checkbox-label">
548+
<span className="mr-2">
549+
{intl.formatMessage(messages.warnNonReleased)}
550+
</span>
551+
<SettingsBadge badgeType="experimental" />
552+
<span className="label-tip">
553+
{intl.formatMessage(messages.warnNonReleasedTip)}
554+
</span>
555+
</label>
556+
<div className="form-input-area">
557+
<Field
558+
type="checkbox"
559+
id="warnNonReleased"
560+
name="warnNonReleased"
561+
onChange={() => {
562+
setFieldValue(
563+
'warnNonReleased',
564+
!values.warnNonReleased
565+
);
566+
}}
567+
/>
568+
</div>
569+
</div>
541570
<div className="form-row">
542571
<label
543572
htmlFor="partialRequestsEnabled"

src/context/SettingsContext.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const defaultSettings = {
1414
applicationUrl: '',
1515
hideAvailable: false,
1616
hideBlocklisted: false,
17+
warnNonReleased: false,
1718
localLogin: true,
1819
mediaServerLogin: true,
1920
movie4kEnabled: false,

src/i18n/locale/en.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,13 +576,16 @@
576576
"components.RequestModal.requesterror": "Something went wrong while submitting the request.",
577577
"components.RequestModal.requestfrom": "{username}'s request is pending approval.",
578578
"components.RequestModal.requestmovie4ktitle": "Request Movie in 4K",
579+
"components.RequestModal.requestmovieNotReleased": "This movie release is in the future",
579580
"components.RequestModal.requestmovies": "Request {count} {count, plural, one {Movie} other {Movies}}",
580581
"components.RequestModal.requestmovies4k": "Request {count} {count, plural, one {Movie} other {Movies}} in 4K",
581582
"components.RequestModal.requestmovietitle": "Request Movie",
582583
"components.RequestModal.requestseasons": "Request {seasonCount} {seasonCount, plural, one {Season} other {Seasons}}",
583584
"components.RequestModal.requestseasons4k": "Request {seasonCount} {seasonCount, plural, one {Season} other {Seasons}} in 4K",
584585
"components.RequestModal.requestseries4ktitle": "Request Series in 4K",
585586
"components.RequestModal.requestseriestitle": "Request Series",
587+
"components.RequestModal.requesttvNoAirDate": "The Air Date is absent",
588+
"components.RequestModal.requesttvNotReleased": "The Air Date is in the future",
586589
"components.RequestModal.season": "Season",
587590
"components.RequestModal.seasonnumber": "Season {number}",
588591
"components.RequestModal.selectmovies": "Select Movie(s)",
@@ -1029,6 +1032,8 @@
10291032
"components.Settings.SettingsMain.validationApplicationUrlTrailingSlash": "URL must not end in a trailing slash",
10301033
"components.Settings.SettingsMain.validationUrl": "You must provide a valid URL",
10311034
"components.Settings.SettingsMain.validationUrlTrailingSlash": "URL must not end in a trailing slash",
1035+
"components.Settings.SettingsMain.warnNonReleased": "Warn if Non released",
1036+
"components.Settings.SettingsMain.warnNonReleasedTip": "Show a warning to users when requesting titles that are still in theatres or not yet released.",
10321037
"components.Settings.SettingsMain.youtubeUrl": "YouTube URL",
10331038
"components.Settings.SettingsMain.youtubeUrlTip": "Base URL for YouTube videos if a self-hosted YouTube instance is used.",
10341039
"components.Settings.SettingsNetwork.apiRequestTimeout": "API Request Timeout",

0 commit comments

Comments
 (0)