From 5e140ab6183b887a7665f5e870eb0bd05d487ace Mon Sep 17 00:00:00 2001 From: sephrat <34862846+sephrat@users.noreply.github.com> Date: Tue, 2 Nov 2021 16:00:25 +0100 Subject: [PATCH] =?UTF-8?q?fix(translations):=20=F0=9F=8C=90=20Localizatio?= =?UTF-8?q?n=20-=20Ensuring=20all=20of=20the=20app=20including=20backend?= =?UTF-8?q?=20are=20localized=20#4366?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Localize TV and movies titles in requests * Add missing release statuses * Localize collection request * Localize TV shows in newsletter Uses TMDB instead of TVMaze for title, genres and synopsis * Localize error messages * Localize albums requests * Use current language for TV and movies requests * Remove unecessary console log --- src/Ombi.Core/Engine/MovieRequestEngine.cs | 9 +- src/Ombi.Core/Engine/RequestEngineResult.cs | 22 +++- src/Ombi.Core/Engine/TvRequestEngine.cs | 12 +- .../Helpers/TvShowRequestBuilderV2.cs | 4 +- .../Models/Requests/TvRequestViewModelV2.cs | 1 + src/Ombi.Core/Rule/BaseRequestRule.cs | 7 +- src/Ombi.Core/Rule/RuleResult.cs | 4 +- .../Rule/Rules/Request/CanRequestRule.cs | 7 +- .../Rules/Request/ExistingMovieRequestRule.cs | 3 +- .../Rules/Request/ExistingPlexRequestRule.cs | 3 +- .../Rules/Request/ExistingTVRequestRule.cs | 3 +- .../Rule/Rules/Request/RequestLimitRule.cs | 6 +- src/Ombi.Core/Rule/Rules/SonarrCacheRule.cs | 3 +- src/Ombi.Core/Rule/SpecificRule.cs | 7 +- src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs | 122 +++++++----------- .../card/discover-card.component.ts | 8 +- .../discover-collections.component.ts | 2 +- .../app/interfaces/IRequestEngineResult.ts | 16 +++ .../src/app/interfaces/ISearchTvResult.ts | 1 + .../artist/artist-details.component.ts | 16 ++- .../artist-information-panel.component.html | 8 +- .../movie/movie-details.component.ts | 16 +-- .../deny-dialog/deny-dialog.component.ts | 2 +- .../tv-request-grid.component.ts | 9 +- .../tv-requests-panel.component.ts | 4 +- .../src/app/services/message.service.ts | 13 +- .../episode-request.component.ts | 4 +- src/Ombi/wwwroot/translations/en.json | 30 ++++- 28 files changed, 205 insertions(+), 137 deletions(-) diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs index bf694a262..63aa0d376 100644 --- a/src/Ombi.Core/Engine/MovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs @@ -120,11 +120,13 @@ namespace Ombi.Core.Engine ?.FirstOrDefault(x => x.Type == ReleaseDateType.Digital)?.ReleaseDate; var ruleResults = (await RunRequestRules(requestModel)).ToList(); - if (ruleResults.Any(x => !x.Success)) + var ruleResultInError = ruleResults.Find(x => !x.Success); + if (ruleResultInError != null) { return new RequestEngineResult { - ErrorMessage = ruleResults.FirstOrDefault(x => x.Message.HasValue()).Message + ErrorMessage = ruleResultInError.Message, + ErrorCode = ruleResultInError.ErrorCode }; } @@ -573,7 +575,8 @@ namespace Ombi.Core.Engine { results.Add(await RequestMovie(new MovieRequestViewModel { - TheMovieDbId = collection.id + TheMovieDbId = collection.id, + LanguageCode = langCode })); } diff --git a/src/Ombi.Core/Engine/RequestEngineResult.cs b/src/Ombi.Core/Engine/RequestEngineResult.cs index 08f6d5129..08c61b7ae 100644 --- a/src/Ombi.Core/Engine/RequestEngineResult.cs +++ b/src/Ombi.Core/Engine/RequestEngineResult.cs @@ -1,4 +1,7 @@ -namespace Ombi.Core.Engine +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Ombi.Core.Engine { public class RequestEngineResult { @@ -6,6 +9,23 @@ public string Message { get; set; } public bool IsError => !string.IsNullOrEmpty(ErrorMessage); public string ErrorMessage { get; set; } + public ErrorCode? ErrorCode { get; set; } public int RequestId { get; set; } } + + [JsonConverter(typeof(StringEnumConverter))] + public enum ErrorCode { + AlreadyRequested, + EpisodesAlreadyRequested, + NoPermissionsOnBehalf, + NoPermissions, + RequestDoesNotExist, + ChildRequestDoesNotExist, + NoPermissionsRequestMovie, + NoPermissionsRequestTV, + NoPermissionsRequestAlbum, + MovieRequestQuotaExceeded, + TvRequestQuotaExceeded, + AlbumRequestQuotaExceeded, + } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index d0d139d0c..fd306f951 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -144,6 +144,7 @@ namespace Ombi.Core.Engine return new RequestEngineResult { Result = false, + ErrorCode = ErrorCode.AlreadyRequested, ErrorMessage = "This has already been requested" }; } @@ -166,6 +167,7 @@ namespace Ombi.Core.Engine return new RequestEngineResult { Result = false, + ErrorCode = ErrorCode.NoPermissionsOnBehalf, Message = "You do not have the correct permissions to request on behalf of users!", ErrorMessage = $"You do not have the correct permissions to request on behalf of users!" }; @@ -176,6 +178,7 @@ namespace Ombi.Core.Engine return new RequestEngineResult { Result = false, + ErrorCode = ErrorCode.NoPermissions, Message = "You do not have the correct permissions!", ErrorMessage = $"You do not have the correct permissions!" }; @@ -183,7 +186,7 @@ namespace Ombi.Core.Engine var tvBuilder = new TvShowRequestBuilderV2(MovieDbApi); (await tvBuilder - .GetShowInfo(tv.TheMovieDbId)) + .GetShowInfo(tv.TheMovieDbId, tv.languageCode)) .CreateTvList(tv) .CreateChild(tv, canRequestOnBehalf ? tv.RequestOnBehalf : user.Id); @@ -250,6 +253,7 @@ namespace Ombi.Core.Engine return new RequestEngineResult { Result = false, + ErrorCode = ErrorCode.AlreadyRequested, ErrorMessage = "This has already been requested" }; } @@ -685,6 +689,7 @@ namespace Ombi.Core.Engine { return new RequestEngineResult { + ErrorCode = ErrorCode.ChildRequestDoesNotExist, ErrorMessage = "Child Request does not exist" }; } @@ -722,6 +727,7 @@ namespace Ombi.Core.Engine { return new RequestEngineResult { + ErrorCode = ErrorCode.ChildRequestDoesNotExist, ErrorMessage = "Child Request does not exist" }; } @@ -781,6 +787,7 @@ namespace Ombi.Core.Engine { return new RequestEngineResult { + ErrorCode = ErrorCode.ChildRequestDoesNotExist, ErrorMessage = "Child Request does not exist" }; } @@ -808,6 +815,7 @@ namespace Ombi.Core.Engine { return new RequestEngineResult { + ErrorCode = ErrorCode.ChildRequestDoesNotExist, ErrorMessage = "Child Request does not exist" }; } @@ -905,6 +913,7 @@ namespace Ombi.Core.Engine return new RequestEngineResult { Result = false, + ErrorCode = ErrorCode.RequestDoesNotExist, ErrorMessage = "Request does not exist" }; } @@ -965,6 +974,7 @@ namespace Ombi.Core.Engine return new RequestEngineResult { Result = false, + ErrorCode = ErrorCode.RequestDoesNotExist, ErrorMessage = "Request does not exist" }; } diff --git a/src/Ombi.Core/Helpers/TvShowRequestBuilderV2.cs b/src/Ombi.Core/Helpers/TvShowRequestBuilderV2.cs index 7a45e9f09..6843332c0 100644 --- a/src/Ombi.Core/Helpers/TvShowRequestBuilderV2.cs +++ b/src/Ombi.Core/Helpers/TvShowRequestBuilderV2.cs @@ -30,9 +30,9 @@ namespace Ombi.Core.Helpers public TvRequests NewRequest { get; protected set; } protected TvInfo TheMovieDbRecord { get; set; } - public async Task GetShowInfo(int id) + public async Task GetShowInfo(int id, string langCode = "en") { - TheMovieDbRecord = await MovieDbApi.GetTVInfo(id.ToString()); + TheMovieDbRecord = await MovieDbApi.GetTVInfo(id.ToString(), langCode); // Remove 'Specials Season' var firstSeason = TheMovieDbRecord.seasons.OrderBy(x => x.season_number).FirstOrDefault(); diff --git a/src/Ombi.Core/Models/Requests/TvRequestViewModelV2.cs b/src/Ombi.Core/Models/Requests/TvRequestViewModelV2.cs index d967993ec..a9742fb32 100644 --- a/src/Ombi.Core/Models/Requests/TvRequestViewModelV2.cs +++ b/src/Ombi.Core/Models/Requests/TvRequestViewModelV2.cs @@ -6,5 +6,6 @@ namespace Ombi.Core.Models.Requests public class TvRequestViewModelV2 : TvRequestViewModelBase { public int TheMovieDbId { get; set; } + public string languageCode { get; set; } = "en"; } } \ No newline at end of file diff --git a/src/Ombi.Core/Rule/BaseRequestRule.cs b/src/Ombi.Core/Rule/BaseRequestRule.cs index fe4daa7ff..3def16d7d 100644 --- a/src/Ombi.Core/Rule/BaseRequestRule.cs +++ b/src/Ombi.Core/Rule/BaseRequestRule.cs @@ -1,4 +1,5 @@ -namespace Ombi.Core.Rule +using Ombi.Core.Engine; +namespace Ombi.Core.Rule { public abstract class BaseRequestRule { @@ -7,9 +8,9 @@ return new RuleResult {Success = true}; } - public RuleResult Fail(string message) + public RuleResult Fail(ErrorCode errorCode, string message = "") { - return new RuleResult { Message = message }; + return new RuleResult { ErrorCode = errorCode, Message = message }; } } } \ No newline at end of file diff --git a/src/Ombi.Core/Rule/RuleResult.cs b/src/Ombi.Core/Rule/RuleResult.cs index ea73cbd88..3cbc6f4a7 100644 --- a/src/Ombi.Core/Rule/RuleResult.cs +++ b/src/Ombi.Core/Rule/RuleResult.cs @@ -1,8 +1,10 @@ -namespace Ombi.Core.Rule +using Ombi.Core.Engine; +namespace Ombi.Core.Rule { public class RuleResult { public bool Success { get; set; } public string Message { get; set; } + public ErrorCode ErrorCode { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Core/Rule/Rules/Request/CanRequestRule.cs b/src/Ombi.Core/Rule/Rules/Request/CanRequestRule.cs index 23ad86dc0..1c720a385 100644 --- a/src/Ombi.Core/Rule/Rules/Request/CanRequestRule.cs +++ b/src/Ombi.Core/Rule/Rules/Request/CanRequestRule.cs @@ -6,6 +6,7 @@ using System.Security.Principal; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Ombi.Core.Authentication; +using Ombi.Core.Engine; using Ombi.Core.Rule.Interfaces; using Ombi.Helpers; using Ombi.Store.Entities.Requests; @@ -34,7 +35,7 @@ namespace Ombi.Core.Rule.Rules.Request { if (await _manager.IsInRoleAsync(user, OmbiRoles.RequestMovie) || await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveMovie)) return Success(); - return Fail("You do not have permissions to Request a Movie"); + return Fail(ErrorCode.NoPermissionsRequestMovie, "You do not have permissions to Request a Movie"); } if (obj.RequestType == RequestType.TvShow) @@ -44,7 +45,7 @@ namespace Ombi.Core.Rule.Rules.Request return Success(); } - return Fail("You do not have permissions to Request a TV Show"); + return Fail(ErrorCode.NoPermissionsRequestTV, "You do not have permissions to Request a TV Show"); } if (obj.RequestType == RequestType.Album) @@ -54,7 +55,7 @@ namespace Ombi.Core.Rule.Rules.Request return Success(); } - return Fail("You do not have permissions to Request an Album"); + return Fail(ErrorCode.NoPermissionsRequestAlbum, "You do not have permissions to Request an Album"); } throw new InvalidDataException("Permission check failed: unknown RequestType"); diff --git a/src/Ombi.Core/Rule/Rules/Request/ExistingMovieRequestRule.cs b/src/Ombi.Core/Rule/Rules/Request/ExistingMovieRequestRule.cs index 15861ca27..c837c42d1 100644 --- a/src/Ombi.Core/Rule/Rules/Request/ExistingMovieRequestRule.cs +++ b/src/Ombi.Core/Rule/Rules/Request/ExistingMovieRequestRule.cs @@ -6,6 +6,7 @@ using Ombi.Helpers; using Ombi.Store.Entities; using Ombi.Store.Entities.Requests; using Ombi.Store.Repository; +using Ombi.Core.Engine; using Ombi.Store.Repository.Requests; namespace Ombi.Core.Rule.Rules.Request @@ -49,7 +50,7 @@ namespace Ombi.Core.Rule.Rules.Request } if(found) { - return Fail($"\"{obj.Title}\" has already been requested"); + return Fail(ErrorCode.AlreadyRequested, $"\"{obj.Title}\" has already been requested"); } } return Success(); diff --git a/src/Ombi.Core/Rule/Rules/Request/ExistingPlexRequestRule.cs b/src/Ombi.Core/Rule/Rules/Request/ExistingPlexRequestRule.cs index 10ab93bed..ef38fb973 100644 --- a/src/Ombi.Core/Rule/Rules/Request/ExistingPlexRequestRule.cs +++ b/src/Ombi.Core/Rule/Rules/Request/ExistingPlexRequestRule.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; +using Ombi.Core.Engine; using Ombi.Core.Rule.Interfaces; using Ombi.Store.Entities; using Ombi.Store.Entities.Requests; @@ -87,7 +88,7 @@ namespace Ombi.Core.Rule.Rules.Request if (!anyEpisodes) { - return Fail($"We already have episodes requested from series {child.Title}"); + return Fail(ErrorCode.EpisodesAlreadyRequested, $"We already have episodes requested from series {child.Title}"); } return Success(); diff --git a/src/Ombi.Core/Rule/Rules/Request/ExistingTVRequestRule.cs b/src/Ombi.Core/Rule/Rules/Request/ExistingTVRequestRule.cs index 7973f664b..a9be6a016 100644 --- a/src/Ombi.Core/Rule/Rules/Request/ExistingTVRequestRule.cs +++ b/src/Ombi.Core/Rule/Rules/Request/ExistingTVRequestRule.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Ombi.Core.Rule.Interfaces; +using Ombi.Core.Engine; using Ombi.Store.Entities; using Ombi.Store.Entities.Requests; using Ombi.Store.Repository.Requests; @@ -63,7 +64,7 @@ namespace Ombi.Core.Rule.Rules.Request if (!anyEpisodes) { - return Fail($"We already have episodes requested from series {tv.Title}"); + return Fail(ErrorCode.EpisodesAlreadyRequested, $"We already have episodes requested from series {tv.Title}"); } } diff --git a/src/Ombi.Core/Rule/Rules/Request/RequestLimitRule.cs b/src/Ombi.Core/Rule/Rules/Request/RequestLimitRule.cs index efa738601..ddf4c1cbf 100644 --- a/src/Ombi.Core/Rule/Rules/Request/RequestLimitRule.cs +++ b/src/Ombi.Core/Rule/Rules/Request/RequestLimitRule.cs @@ -54,7 +54,7 @@ namespace Ombi.Core.Rule.Rules.Request if (remainingLimitsModel.Remaining < 1) { - return Fail("You have exceeded your Movie request quota!"); + return Fail(Engine.ErrorCode.MovieRequestQuotaExceeded, "You have exceeded your Movie request quota!"); } } if (obj.RequestType == RequestType.TvShow) @@ -75,7 +75,7 @@ namespace Ombi.Core.Rule.Rules.Request if ((remainingLimitsModel.Remaining - requestCount) < 0) { - return Fail("You have exceeded your Episode request quota!"); + return Fail(Engine.ErrorCode.TvRequestQuotaExceeded, "You have exceeded your Episode request quota!"); } } if (obj.RequestType == RequestType.Album) @@ -88,7 +88,7 @@ namespace Ombi.Core.Rule.Rules.Request if (remainingLimitsModel.Remaining < 1) { - return Fail("You have exceeded your Album request quota!"); + return Fail(Engine.ErrorCode.AlbumRequestQuotaExceeded, "You have exceeded your Album request quota!"); } } return Success(); diff --git a/src/Ombi.Core/Rule/Rules/SonarrCacheRule.cs b/src/Ombi.Core/Rule/Rules/SonarrCacheRule.cs index 325b7cc8d..2bb2fd128 100644 --- a/src/Ombi.Core/Rule/Rules/SonarrCacheRule.cs +++ b/src/Ombi.Core/Rule/Rules/SonarrCacheRule.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; +using Ombi.Core.Engine; using Ombi.Core.Models.Search; using Ombi.Helpers; using Ombi.Store.Context; @@ -56,7 +57,7 @@ namespace Ombi.Core.Rule.Rules if (!anyEpisodes) { - return new RuleResult { Message = $"We already have episodes requested from series {vm.Title}" }; + return new RuleResult { ErrorCode = ErrorCode.EpisodesAlreadyRequested, Message = $"We already have episodes requested from series {vm.Title}" }; } } } diff --git a/src/Ombi.Core/Rule/SpecificRule.cs b/src/Ombi.Core/Rule/SpecificRule.cs index 071bce292..990f6168d 100644 --- a/src/Ombi.Core/Rule/SpecificRule.cs +++ b/src/Ombi.Core/Rule/SpecificRule.cs @@ -1,4 +1,5 @@ -using Ombi.Core.Rule.Interfaces; +using Ombi.Core.Engine; +using Ombi.Core.Rule.Interfaces; namespace Ombi.Core.Rule { @@ -9,9 +10,9 @@ namespace Ombi.Core.Rule return new RuleResult { Success = true }; } - public RuleResult Fail(string message) + public RuleResult Fail(ErrorCode errorCode, string message = "") { - return new RuleResult { Message = message }; + return new RuleResult { ErrorCode = errorCode, Message = message }; } public abstract SpecificRules Rule { get; } diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index cd24cda8b..7752d298d 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -615,17 +615,17 @@ namespace Ombi.Schedule.Jobs.Ombi sb.Append(""); if (plexSettings.Enable) { - await ProcessPlexTv(plexEpisodes, sb, plexSettings.Servers.FirstOrDefault().ServerHostname ?? string.Empty); + await ProcessPlexTv(plexEpisodes, sb, ombiSettings.DefaultLanguageCode, plexSettings.Servers.FirstOrDefault().ServerHostname ?? string.Empty); } if (embySettings.Enable) { - await ProcessEmbyTv(embyEp, sb, embySettings.Servers.FirstOrDefault()?.ServerHostname ?? string.Empty); + await ProcessEmbyTv(embyEp, sb, ombiSettings.DefaultLanguageCode, embySettings.Servers.FirstOrDefault()?.ServerHostname ?? string.Empty); } if (jellyfinSettings.Enable) { - await ProcessJellyfinTv(jellyfinEp, sb, jellyfinSettings.Servers.FirstOrDefault()?.ServerHostname ?? string.Empty); + await ProcessJellyfinTv(jellyfinEp, sb, ombiSettings.DefaultLanguageCode, jellyfinSettings.Servers.FirstOrDefault()?.ServerHostname ?? string.Empty); } sb.Append(""); @@ -908,7 +908,7 @@ namespace Ombi.Schedule.Jobs.Ombi AddGenres(sb, $"Type: {info.albumType}"); } - private async Task ProcessPlexTv(HashSet plexContent, StringBuilder sb, string serverHostname) + private async Task ProcessPlexTv(HashSet plexContent, StringBuilder sb, string languageCode, string serverHostname) { var series = new List(); foreach (var plexEpisode in plexContent) @@ -975,7 +975,7 @@ namespace Ombi.Schedule.Jobs.Ombi banner = banner.ToHttpsUrl(); // Always use the Https banners } - var tvInfo = await _movieApi.GetTVInfo(t.TheMovieDbId); + var tvInfo = await _movieApi.GetTVInfo(t.TheMovieDbId, languageCode); if (tvInfo != null && tvInfo.backdrop_path.HasValue()) { @@ -989,16 +989,7 @@ namespace Ombi.Schedule.Jobs.Ombi AddMediaServerUrl(sb, PlexHelper.BuildPlexMediaUrl(t.Url, serverHostname), banner); AddInfoTable(sb); - var title = ""; - if (!string.IsNullOrEmpty(info.premiered) && info.premiered.Length > 4) - { - title = $"{t.Title} ({info.premiered.Remove(4)})"; - } - else - { - title = $"{t.Title}"; - } - AddTitle(sb, $"https://www.imdb.com/title/{info.externals.imdb}/", title); + AddTvTitle(sb, info, tvInfo); // Group by the season number var results = t.Episodes.GroupBy(p => p.SeasonNumber, @@ -1021,18 +1012,7 @@ namespace Ombi.Schedule.Jobs.Ombi finalsb.Append("
"); } - var summary = info.summary; - if (summary.Length > 280) - { - summary = summary.Remove(280); - summary = summary + "...

"; - } - AddTvParagraph(sb, finalsb.ToString(), summary); - - if (info.genres.Any()) - { - AddGenres(sb, $"Genres: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}"); - } + AddTvEpisodesSummaryGenres(sb, finalsb.ToString(), tvInfo); } catch (Exception e) @@ -1056,7 +1036,7 @@ namespace Ombi.Schedule.Jobs.Ombi - private async Task ProcessEmbyTv(HashSet embyContent, StringBuilder sb, string serverUrl) + private async Task ProcessEmbyTv(HashSet embyContent, StringBuilder sb, string languageCode, string serverUrl) { var series = new List(); foreach (var episode in embyContent) @@ -1100,7 +1080,7 @@ namespace Ombi.Schedule.Jobs.Ombi banner = banner.ToHttpsUrl(); // Always use the Https banners } - var tvInfo = await _movieApi.GetTVInfo(t.TheMovieDbId); + var tvInfo = await _movieApi.GetTVInfo(t.TheMovieDbId, languageCode); if (tvInfo != null && tvInfo.backdrop_path.HasValue()) { @@ -1114,16 +1094,7 @@ namespace Ombi.Schedule.Jobs.Ombi AddMediaServerUrl(sb, serverUrl.HasValue() ? serverUrl : t.Url, banner); AddInfoTable(sb); - var title = ""; - if (!String.IsNullOrEmpty(info.premiered) && info.premiered.Length > 4) - { - title = $"{t.Title} ({info.premiered.Remove(4)})"; - } - else - { - title = $"{t.Title}"; - } - AddTitle(sb, $"https://www.imdb.com/title/{info.externals.imdb}/", title); + AddTvTitle(sb, info, tvInfo); // Group by the season number var results = t.Episodes?.GroupBy(p => p.SeasonNumber, @@ -1146,18 +1117,7 @@ namespace Ombi.Schedule.Jobs.Ombi finalsb.Append("
"); } - var summary = info.summary; - if (summary.Length > 280) - { - summary = summary.Remove(280); - summary = summary + "...

"; - } - AddTvParagraph(sb, finalsb.ToString(), summary); - - if (info.genres.Any()) - { - AddGenres(sb, $"Genres: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}"); - } + AddTvEpisodesSummaryGenres(sb, finalsb.ToString(), tvInfo); } catch (Exception e) @@ -1179,10 +1139,10 @@ namespace Ombi.Schedule.Jobs.Ombi } } - private async Task ProcessJellyfinTv(HashSet embyContent, StringBuilder sb, string serverUrl) + private async Task ProcessJellyfinTv(HashSet jellyfinContent, StringBuilder sb, string languageCode, string serverUrl) { var series = new List(); - foreach (var episode in embyContent) + foreach (var episode in jellyfinContent) { var alreadyAdded = series.FirstOrDefault(x => x.JellyfinId == episode.Series.JellyfinId); if (alreadyAdded != null) @@ -1223,7 +1183,7 @@ namespace Ombi.Schedule.Jobs.Ombi banner = banner.ToHttpsUrl(); // Always use the Https banners } - var tvInfo = await _movieApi.GetTVInfo(t.TheMovieDbId); + var tvInfo = await _movieApi.GetTVInfo(t.TheMovieDbId, languageCode); if (tvInfo != null && tvInfo.backdrop_path.HasValue()) { @@ -1237,16 +1197,7 @@ namespace Ombi.Schedule.Jobs.Ombi AddMediaServerUrl(sb, serverUrl.HasValue() ? serverUrl : t.Url, banner); AddInfoTable(sb); - var title = ""; - if (!String.IsNullOrEmpty(info.premiered) && info.premiered.Length > 4) - { - title = $"{t.Title} ({info.premiered.Remove(4)})"; - } - else - { - title = $"{t.Title}"; - } - AddTitle(sb, $"https://www.imdb.com/title/{info.externals.imdb}/", title); + AddTvTitle(sb, info, tvInfo); // Group by the season number var results = t.Episodes?.GroupBy(p => p.SeasonNumber, @@ -1269,18 +1220,7 @@ namespace Ombi.Schedule.Jobs.Ombi finalsb.Append("
"); } - var summary = info.summary; - if (summary.Length > 280) - { - summary = summary.Remove(280); - summary = summary + "...

"; - } - AddTvParagraph(sb, finalsb.ToString(), summary); - - if (info.genres.Any()) - { - AddGenres(sb, $"Genres: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}"); - } + AddTvEpisodesSummaryGenres(sb, finalsb.ToString(), tvInfo); } catch (Exception e) @@ -1302,6 +1242,36 @@ namespace Ombi.Schedule.Jobs.Ombi } } + private void AddTvTitle(StringBuilder sb, Api.TvMaze.Models.TvMazeShow info, TvInfo tvInfo) + { + var title = ""; + if (!String.IsNullOrEmpty(info.premiered) && info.premiered.Length > 4) + { + title = $"{tvInfo.name} ({info.premiered.Remove(4)})"; + } + else + { + title = $"{tvInfo.name}"; + } + AddTitle(sb, $"https://www.imdb.com/title/{info.externals.imdb}/", title); + } + + private void AddTvEpisodesSummaryGenres(StringBuilder sb, string episodes, TvInfo tvInfo) + { + var summary = tvInfo.overview; + if (summary.Length > 280) + { + summary = summary.Remove(280); + summary = summary + "...

"; + } + AddTvParagraph(sb, episodes, summary); + + if (tvInfo.genres.Any()) + { + AddGenres(sb, $"Genres: {string.Join(", ", tvInfo.genres.Select(x => x.name.ToString()).ToArray())}"); + } + } + private void EndLoopHtml(StringBuilder sb) { //NOTE: BR have to be in TD's as per html spec or it will be put outside of the table... diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts index 0907404a8..1bd2546bc 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts @@ -124,7 +124,7 @@ export class DiscoverCardComponent implements OnInit { dialog.afterClosed().subscribe((result) => { if (result) { this.requestService.requestMovie({ theMovieDbId: +this.result.id, - languageCode: null, + languageCode: this.translate.currentLang, qualityPathOverride: result.radarrPathId, requestOnBehalf: result.username?.id, rootFolderOverride: result.radarrFolderId, }).subscribe(x => { @@ -132,18 +132,18 @@ export class DiscoverCardComponent implements OnInit { this.result.requested = true; this.messageService.send(this.translate.instant("Requests.RequestAddedSuccessfully", { title: this.result.title }), "Ok"); } else { - this.messageService.send(x.errorMessage, "Ok"); + this.messageService.sendRequestEngineResultError(x); } }); } }); } else { - this.requestService.requestMovie({ theMovieDbId: +this.result.id, languageCode: null, requestOnBehalf: null, qualityPathOverride: null, rootFolderOverride: null }).subscribe(x => { + this.requestService.requestMovie({ theMovieDbId: +this.result.id, languageCode: this.translate.currentLang, requestOnBehalf: null, qualityPathOverride: null, rootFolderOverride: null }).subscribe(x => { if (x.result) { this.result.requested = true; this.messageService.send(this.translate.instant("Requests.RequestAddedSuccessfully", { title: this.result.title }), "Ok"); } else { - this.messageService.send(x.errorMessage, "Ok"); + this.messageService.sendRequestEngineResultError(x); } this.loading = false; }); diff --git a/src/Ombi/ClientApp/src/app/discover/components/collections/discover-collections.component.ts b/src/Ombi/ClientApp/src/app/discover/components/collections/discover-collections.component.ts index 4b5c93b05..9e84ea7d0 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/collections/discover-collections.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/collections/discover-collections.component.ts @@ -46,7 +46,7 @@ export class DiscoverCollectionsComponent implements OnInit { if (result.result) { this.messageService.send(this.translate.instant("Requests.CollectionSuccesfullyAdded", { name: this.collection.name })); } else { - this.messageService.send(result.errorMessage); + this.messageService.sendRequestEngineResultError(result); } this.finishLoading(); }); diff --git a/src/Ombi/ClientApp/src/app/interfaces/IRequestEngineResult.ts b/src/Ombi/ClientApp/src/app/interfaces/IRequestEngineResult.ts index 87ed1bf61..d121eb1a2 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/IRequestEngineResult.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/IRequestEngineResult.ts @@ -2,5 +2,21 @@ result: boolean; message: string; errorMessage: string; + errorCode: ErrorCode; requestId: number | undefined; } + +export enum ErrorCode { + AlreadyRequested, + EpisodesAlreadyRequested, + NoPermissionsOnBehalf, + NoPermissions, + RequestDoesNotExist, + ChildRequestDoesNotExist, + NoPermissionsRequestMovie, + NoPermissionsRequestTV, + NoPermissionsRequestAlbum, + MovieRequestQuotaExceeded, + TvRequestQuotaExceeded, + AlbumRequestQuotaExceeded, +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResult.ts b/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResult.ts index 8928c188b..42fa11e53 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResult.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResult.ts @@ -44,6 +44,7 @@ export interface ISearchTvResult { export interface ITvRequestViewModelV2 extends ITvRequestViewModelBase { theMovieDbId: number; + languageCode: string; } diff --git a/src/Ombi/ClientApp/src/app/media-details/components/artist/artist-details.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/artist/artist-details.component.ts index ff6e1e436..5aebde2e5 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/artist/artist-details.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/artist/artist-details.component.ts @@ -8,6 +8,7 @@ import { AuthService } from "../../../auth/auth.service"; import { DenyDialogComponent } from "../shared/deny-dialog/deny-dialog.component"; import { NewIssueComponent } from "../shared/new-issue/new-issue.component"; import { IArtistSearchResult, IReleaseGroups } from "../../../interfaces/IMusicSearchResultV2"; +import { TranslateService } from "@ngx-translate/core"; @Component({ templateUrl: "./artist-details.component.html", @@ -26,7 +27,8 @@ export class ArtistDetailsComponent { constructor(private searchService: SearchV2Service, private route: ActivatedRoute, private sanitizer: DomSanitizer, private imageService: ImageService, public dialog: MatDialog, private requestService: RequestService, - public messageService: MessageService, private auth: AuthService) { + public messageService: MessageService, private auth: AuthService, + private translate: TranslateService) { this.route.params.subscribe((params: any) => { this.artistId = params.artistId; this.load(); @@ -92,16 +94,16 @@ export class ArtistDetailsComponent { .then(r => { if (r.result) { a.monitored = true; - this.messageService.send(r.message); + this.messageService.send(this.translate.instant("Requests.RequestAddedSuccessfully", {title: a.title})); } else { - this.messageService.send(r.errorMessage); + this.messageService.sendRequestEngineResultError(r); } a.selected = false; }) .catch(r => { console.log(r); - this.messageService.send("Error when requesting album"); + this.messageService.sendRequestEngineResultError(r); }); }); } else { @@ -115,16 +117,16 @@ export class ArtistDetailsComponent { .then(r => { if (r.result) { a.monitored = true; - this.messageService.send(r.message); + this.messageService.send(this.translate.instant("Requests.RequestAddedSuccessfully", {title: a.title})); } else { - this.messageService.send(r.errorMessage); + this.messageService.sendRequestEngineResultError(r); } a.selected = false; }) .catch(r => { console.log(r); - this.messageService.send("Error when requesting album"); + this.messageService.sendRequestEngineResultError(r); }); }) } diff --git a/src/Ombi/ClientApp/src/app/media-details/components/artist/panels/artist-information-panel/artist-information-panel.component.html b/src/Ombi/ClientApp/src/app/media-details/components/artist/panels/artist-information-panel/artist-information-panel.component.html index a50b59039..4a454fe01 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/artist/panels/artist-information-panel/artist-information-panel.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/artist/panels/artist-information-panel/artist-information-panel.component.html @@ -1,18 +1,18 @@
- Type: + {{ 'MediaDetails.Music.Type' | translate }}
{{artist.type}}
- Country + {{ 'MediaDetails.Music.Country' | translate }}
{{artist.country}}
- Start Date + {{ 'MediaDetails.Music.StartDate' | translate }}
{{artist.startYear}}
- End Date + {{ 'MediaDetails.Music.EndDate' | translate }}
{{artist.endYear}}
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts index ea9b24b1d..d0e2fa1ef 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts @@ -92,7 +92,7 @@ export class MovieDetailsComponent { dialog.afterClosed().subscribe(async (result) => { if (result) { const requestResult = await this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId, - languageCode: null, + languageCode: this.translate.currentLang, qualityPathOverride: result.radarrPathId, requestOnBehalf: result.username?.id, rootFolderOverride: result.radarrFolderId, }).toPromise(); @@ -102,19 +102,19 @@ export class MovieDetailsComponent { this.messageService.send(this.translate.instant("Requests.RequestAddedSuccessfully", { title: this.movie.title }), "Ok"); this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId); } else { - this.messageService.send(requestResult.errorMessage, "Ok"); + this.messageService.sendRequestEngineResultError(requestResult); } } }); } else { - const result = await this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId, languageCode: null, requestOnBehalf: userId, qualityPathOverride: undefined, rootFolderOverride: undefined }).toPromise(); + const result = await this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId, languageCode: this.translate.currentLang, requestOnBehalf: userId, qualityPathOverride: undefined, rootFolderOverride: undefined }).toPromise(); if (result.result) { this.movie.requested = true; this.movie.requestId = result.requestId; this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId); this.messageService.send(this.translate.instant("Requests.RequestAddedSuccessfully", { title: this.movie.title }), "Ok"); } else { - this.messageService.send(result.errorMessage, "Ok"); + this.messageService.sendRequestEngineResultError(result); } } } @@ -156,7 +156,7 @@ export class MovieDetailsComponent { this.messageService.send(this.translate.instant("Requests.SuccessfullyApproved"), "Ok"); } else { this.movie.approved = false; - this.messageService.send(result.errorMessage, "Ok"); + this.messageService.sendRequestEngineResultError(result); } } @@ -166,7 +166,7 @@ export class MovieDetailsComponent { this.movie.available = true; this.messageService.send(this.translate.instant("Requests.NowAvailable"), "Ok"); } else { - this.messageService.send(result.errorMessage, "Ok"); + this.messageService.sendRequestEngineResultError(result); } } @@ -177,7 +177,7 @@ export class MovieDetailsComponent { this.movie.available = false; this.messageService.send(this.translate.instant("Requests.NowUnavailable"), "Ok"); } else { - this.messageService.send(result.errorMessage, "Ok"); + this.messageService.sendRequestEngineResultError(result); } } @@ -208,7 +208,7 @@ export class MovieDetailsComponent { if (result.result) { this.messageService.send(result.message ? result.message : this.translate.instant("Requests.SuccessfullyReprocessed"), "Ok"); } else { - this.messageService.send(result.errorMessage, "Ok"); + this.messageService.sendRequestEngineResultError(result); } }); } diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/deny-dialog/deny-dialog.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/shared/deny-dialog/deny-dialog.component.ts index 2970c142b..3132ac84f 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/shared/deny-dialog/deny-dialog.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/deny-dialog/deny-dialog.component.ts @@ -35,7 +35,7 @@ export class DenyDialogComponent { this.messageService.send(this.translate.instant("Requests.DeniedRequest"), "Ok"); this.data.denied = true; } else { - this.messageService.send(result.errorMessage, "Ok"); + this.messageService.sendRequestEngineResultError(result); this.data.denied = false; } diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-request-grid/tv-request-grid.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-request-grid/tv-request-grid.component.ts index 3e34b98f9..7d1304eff 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-request-grid/tv-request-grid.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-request-grid/tv-request-grid.component.ts @@ -4,6 +4,7 @@ import { RequestService } from "../../../../../services/request.service"; import { MessageService } from "../../../../../services"; import { DenyDialogComponent } from "../../../shared/deny-dialog/deny-dialog.component"; import { ISearchTvResultV2 } from "../../../../../interfaces/ISearchTvResultV2"; +import { TranslateService } from "@ngx-translate/core"; import { MatDialog } from "@angular/material/dialog"; import { SelectionModel } from "@angular/cdk/collections"; import { RequestServiceV2 } from "../../../../../services/requestV2.service"; @@ -27,7 +28,7 @@ export class TvRequestGridComponent { public displayedColumns: string[] = ['select', 'number', 'title', 'airDate', 'status']; constructor(private requestService: RequestService, private requestServiceV2: RequestServiceV2, private notificationService: MessageService, - private dialog: MatDialog) { + private dialog: MatDialog, private translate: TranslateService) { } @@ -43,7 +44,7 @@ export class TvRequestGridComponent { const viewModel = { firstSeason: this.tv.firstSeason, latestSeason: this.tv.latestSeason, requestAll: this.tv.requestAll, theMovieDbId: this.tv.id, - requestOnBehalf: null + requestOnBehalf: null, languageCode: this.translate.currentLang }; viewModel.seasons = []; this.tv.seasonRequests.forEach((season) => { @@ -236,7 +237,7 @@ export class TvRequestGridComponent { private postRequest(requestResult: IRequestEngineResult) { if (requestResult.result) { this.notificationService.send( - `Request for ${this.tv.title} has been added successfully`); + this.translate.instant("Requests.RequestAddedSuccessfully", { title:this.tv.title })); this.selection.clear(); @@ -262,7 +263,7 @@ export class TvRequestGridComponent { } } else { - this.notificationService.send(requestResult.errorMessage ? requestResult.errorMessage : requestResult.message); + this.notificationService.sendRequestEngineResultError(requestResult); } } } diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.ts index d4e8f894c..10eee3989 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.ts @@ -41,7 +41,7 @@ export class TvRequestsPanelComponent { }); this.messageService.send("Request has been approved", "Ok"); } else { - this.messageService.send(result.errorMessage, "Ok"); + this.messageService.sendRequestEngineResultError(result); } } @@ -104,7 +104,7 @@ export class TvRequestsPanelComponent { if (result.result) { this.messageService.send(result.message ? result.message : "Successfully Re-processed the request", "Ok"); } else { - this.messageService.send(result.errorMessage, "Ok"); + this.messageService.sendRequestEngineResultError(result); } }); } diff --git a/src/Ombi/ClientApp/src/app/services/message.service.ts b/src/Ombi/ClientApp/src/app/services/message.service.ts index 1c4e6eded..be1df37be 100644 --- a/src/Ombi/ClientApp/src/app/services/message.service.ts +++ b/src/Ombi/ClientApp/src/app/services/message.service.ts @@ -1,9 +1,11 @@ import { Injectable } from "@angular/core"; import { MatSnackBar, MatSnackBarConfig } from "@angular/material/snack-bar"; +import { TranslateService } from "@ngx-translate/core"; +import { IRequestEngineResult } from "../interfaces/IRequestEngineResult"; @Injectable() export class MessageService { - constructor(private snackBar: MatSnackBar) { + constructor(private snackBar: MatSnackBar, private translate: TranslateService) { this.config = { duration: 4000, } @@ -18,4 +20,13 @@ export class MessageService { this.snackBar.open(message, "OK", this.config) } } + public sendRequestEngineResultError(result: IRequestEngineResult, action: string = "Ok") { + const textKey = 'Requests.ErrorCodes.' + result.errorCode; + const text = this.translate.instant(textKey); + if (text !== textKey) { + this.send(text, action); + } else { + this.send(result.errorMessage ? result.errorMessage : result.message, action); + } + } } diff --git a/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.ts b/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.ts index 4f02a74b4..7da0fc238 100644 --- a/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.ts +++ b/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.ts @@ -45,7 +45,7 @@ export class EpisodeRequestComponent { const viewModel = { firstSeason: this.data.series.firstSeason, latestSeason: this.data.series.latestSeason, requestAll: this.data.series.requestAll, theMovieDbId: this.data.series.id, - requestOnBehalf: this.data.requestOnBehalf + requestOnBehalf: this.data.requestOnBehalf, languageCode: this.translate.currentLang }; viewModel.seasons = []; this.data.series.seasonRequests.forEach((season) => { @@ -134,7 +134,7 @@ export class EpisodeRequestComponent { }); } else { - this.notificationService.send(requestResult.errorMessage ? requestResult.errorMessage : requestResult.message); + this.notificationService.sendRequestEngineResultError(requestResult); } } } diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index 959ddaade..ad9ee1ba2 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -37,7 +37,8 @@ "Submit": "Submit", "Update": "Update", "tvShow": "TV Show", - "movie": "Movie" + "movie": "Movie", + "album": "Album" }, "PasswordReset": { "EmailAddressPlaceholder": "Email Address", @@ -204,7 +205,21 @@ "RequestCollection": "Request Collection", "CollectionSuccesfullyAdded": "The collection {{name}} has been successfully added!", "NeedToSelectEpisodes": "You need to select some episodes!", - "RequestAddedSuccessfully": "Request for {{title}} has been added successfully" + "RequestAddedSuccessfully": "Request for {{title}} has been added successfully", + "ErrorCodes": { + "AlreadyRequested": "This has already been requested", + "EpisodesAlreadyRequested": "We already have episodes requested from this series", + "NoPermissionsOnBehalf":"You do not have the correct permissions to request on behalf of users!", + "NoPermissions":"You do not have the correct permissions!", + "RequestDoesNotExist":"Request does not exist", + "ChildRequestDoesNotExist":"Child Request does not exist", + "NoPermissionsRequestMovie":"You do not have permissions to Request a Movie", + "NoPermissionsRequestTV":"You do not have permissions to Request a TV Show", + "NoPermissionsRequestAlbum":"You do not have permissions to Request an Album", + "MovieRequestQuotaExceeded":"You have exceeded your Movie request quota!", + "TvRequestQuotaExceeded":"You have exceeded your Episode request quota!", + "AlbumRequestQuotaExceeded":"You have exceeded your Album request quota!" + } }, "Issues": { "Title": "Issues", @@ -284,9 +299,12 @@ "LanguageProfileSelect":"Select A Language Profile", "Status":"Status:", "StatusValues" : { + "Rumored" : "Rumored", "Planned": "Planned", "In Production": "In Production", + "Post Production": "Post Production", "Released": "Released", + "Running": "Running", "Returning Series": "Returning Series", "Ended": "Ended", "Canceled": "Canceled" @@ -327,7 +345,13 @@ "RequestedBy": "Requested By:", "RequestDate": "Request Date:", "DeniedReason": "Denied Reason:", - "ReProcessRequest": "Re-Process Request" + "ReProcessRequest": "Re-Process Request", + "Music": { + "Type": "Type:", + "Country": "Country:", + "StartDate": "Start Date:", + "EndDate": "EndDate:" + } }, "Discovery": { "PopularTab": "Popular",