diff --git a/server/interfaces/api/settingsInterfaces.ts b/server/interfaces/api/settingsInterfaces.ts
index e9c832a85..9393370dc 100644
--- a/server/interfaces/api/settingsInterfaces.ts
+++ b/server/interfaces/api/settingsInterfaces.ts
@@ -46,6 +46,7 @@ export interface PublicSettingsResponse {
locale: string;
emailEnabled: boolean;
newPlexLogin: boolean;
+ youtubeUrl: string;
}
export interface CacheItem {
diff --git a/server/lib/settings/index.ts b/server/lib/settings/index.ts
index 9fa011af4..490b926a7 100644
--- a/server/lib/settings/index.ts
+++ b/server/lib/settings/index.ts
@@ -135,6 +135,7 @@ export interface MainSettings {
partialRequestsEnabled: boolean;
enableSpecialEpisodes: boolean;
locale: string;
+ youtubeUrl: string;
}
export interface NetworkSettings {
@@ -172,6 +173,7 @@ interface FullPublicSettings extends PublicSettings {
emailEnabled: boolean;
userEmailRequired: boolean;
newPlexLogin: boolean;
+ youtubeUrl: string;
}
export interface NotificationAgentConfig {
@@ -375,6 +377,7 @@ class Settings {
partialRequestsEnabled: true,
enableSpecialEpisodes: false,
locale: 'en',
+ youtubeUrl: '',
},
plex: {
name: '',
@@ -646,6 +649,7 @@ class Settings {
userEmailRequired:
this.data.notifications.agents.email.options.userEmailRequired,
newPlexLogin: this.data.main.newPlexLogin,
+ youtubeUrl: this.data.main.youtubeUrl,
};
}
diff --git a/src/components/MovieDetails/index.tsx b/src/components/MovieDetails/index.tsx
index 29b3ab564..80ede2d0e 100644
--- a/src/components/MovieDetails/index.tsx
+++ b/src/components/MovieDetails/index.tsx
@@ -210,10 +210,16 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
svg: ,
});
}
- const trailerUrl = data.relatedVideos
+
+ const trailerVideo = data.relatedVideos
?.filter((r) => r.type === 'Trailer')
.sort((a, b) => a.size - b.size)
- .pop()?.url;
+ .pop();
+ const trailerUrl =
+ trailerVideo?.site === 'YouTube' &&
+ settings.currentSettings.youtubeUrl != ''
+ ? `${settings.currentSettings.youtubeUrl}${trailerVideo?.key}`
+ : trailerVideo?.url;
if (trailerUrl) {
mediaLinks.push({
diff --git a/src/components/Settings/SettingsMain/index.tsx b/src/components/Settings/SettingsMain/index.tsx
index 41673813f..d1e809a28 100644
--- a/src/components/Settings/SettingsMain/index.tsx
+++ b/src/components/Settings/SettingsMain/index.tsx
@@ -64,6 +64,9 @@ const messages = defineMessages('components.Settings.SettingsMain', {
partialRequestsEnabled: 'Allow Partial Series Requests',
enableSpecialEpisodes: 'Allow Special Episodes Requests',
locale: 'Display Language',
+ youtubeUrl: 'YouTube URL',
+ validationUrl: 'You must provide a valid URL',
+ validationUrlTrailingSlash: 'URL must not end in a trailing slash',
});
const SettingsMain = () => {
@@ -105,6 +108,13 @@ const SettingsMain = () => {
'Number must be less than or equal to 250.',
(value) => (value ?? 0) <= 250
),
+ youtubeUrl: Yup.string()
+ .url(intl.formatMessage(messages.validationUrl))
+ .test(
+ 'no-trailing-slash',
+ intl.formatMessage(messages.validationUrlTrailingSlash),
+ (value) => !value || !value.endsWith('/')
+ ),
});
const regenerate = async () => {
@@ -160,6 +170,7 @@ const SettingsMain = () => {
partialRequestsEnabled: data?.partialRequestsEnabled,
enableSpecialEpisodes: data?.enableSpecialEpisodes,
cacheImages: data?.cacheImages,
+ youtubeUrl: data?.youtubeUrl,
}}
enableReinitialize
validationSchema={MainSettingsSchema}
@@ -179,6 +190,7 @@ const SettingsMain = () => {
partialRequestsEnabled: values.partialRequestsEnabled,
enableSpecialEpisodes: values.enableSpecialEpisodes,
cacheImages: values.cacheImages,
+ youtubeUrl: values.youtubeUrl,
});
mutate('/api/v1/settings/public');
mutate('/api/v1/status');
@@ -519,6 +531,26 @@ const SettingsMain = () => {
/>
+
+
+
+
+
+
+ {errors.youtubeUrl &&
+ touched.youtubeUrl &&
+ typeof errors.youtubeUrl === 'string' && (
+
{errors.youtubeUrl}
+ )}
+
+
diff --git a/src/components/TvDetails/index.tsx b/src/components/TvDetails/index.tsx
index 5165d51ba..1b008e40b 100644
--- a/src/components/TvDetails/index.tsx
+++ b/src/components/TvDetails/index.tsx
@@ -208,10 +208,15 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
});
}
- const trailerUrl = data.relatedVideos
+ const trailerVideo = data.relatedVideos
?.filter((r) => r.type === 'Trailer')
.sort((a, b) => a.size - b.size)
- .pop()?.url;
+ .pop();
+ const trailerUrl =
+ trailerVideo?.site === 'YouTube' &&
+ settings.currentSettings.youtubeUrl != ''
+ ? `${settings.currentSettings.youtubeUrl}${trailerVideo?.key}`
+ : trailerVideo?.url;
if (trailerUrl) {
mediaLinks.push({
diff --git a/src/context/SettingsContext.tsx b/src/context/SettingsContext.tsx
index 372961201..aa838b406 100644
--- a/src/context/SettingsContext.tsx
+++ b/src/context/SettingsContext.tsx
@@ -30,6 +30,7 @@ const defaultSettings = {
locale: 'en',
emailEnabled: false,
newPlexLogin: true,
+ youtubeUrl: '',
};
export const SettingsContext = React.createContext({
diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json
index ccf52ca04..a08ec69e7 100644
--- a/src/i18n/locale/en.json
+++ b/src/i18n/locale/en.json
@@ -104,6 +104,7 @@
"components.Discover.StudioSlider.studios": "Studios",
"components.Discover.TvGenreList.seriesgenres": "Series Genres",
"components.Discover.TvGenreSlider.tvgenres": "Series Genres",
+ "components.DiscoverTvUpcoming.upcomingtv": "Upcoming Series",
"components.Discover.createnewslider": "Create New Slider",
"components.Discover.customizediscover": "Customize Discover",
"components.Discover.discover": "Discover",
@@ -137,7 +138,6 @@
"components.Discover.upcomingtv": "Upcoming Series",
"components.Discover.updatefailed": "Something went wrong updating the discover customization settings.",
"components.Discover.updatesuccess": "Updated discover customization settings.",
- "components.DiscoverTvUpcoming.upcomingtv": "Upcoming Series",
"components.DownloadBlock.estimatedtime": "Estimated {time}",
"components.DownloadBlock.formattedTitle": "{title}: Season {seasonNumber} Episode {episodeNumber}",
"components.IssueDetails.IssueComment.areyousuredelete": "Are you sure you want to delete this comment?",
@@ -975,6 +975,9 @@
"components.Settings.SettingsMain.validationApplicationTitle": "You must provide an application title",
"components.Settings.SettingsMain.validationApplicationUrl": "You must provide a valid URL",
"components.Settings.SettingsMain.validationApplicationUrlTrailingSlash": "URL must not end in a trailing slash",
+ "components.Settings.SettingsMain.validationUrl": "You must provide a valid URL",
+ "components.Settings.SettingsMain.validationUrlTrailingSlash": "URL must not end in a trailing slash",
+ "components.Settings.SettingsMain.youtubeUrl": "YouTube URL",
"components.Settings.SettingsNetwork.csrfProtection": "Enable CSRF Protection",
"components.Settings.SettingsNetwork.csrfProtectionHoverTip": "Do NOT enable this setting unless you understand what you are doing!",
"components.Settings.SettingsNetwork.csrfProtectionTip": "Set external API access to read-only (requires HTTPS)",
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index 260dcfe74..88513e3eb 100644
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -248,6 +248,7 @@ CoreApp.getInitialProps = async (initialProps) => {
locale: 'en',
emailEnabled: false,
newPlexLogin: true,
+ youtubeUrl: '',
};
if (ctx.res) {