diff --git a/CHANGELOG.md b/CHANGELOG.md index 167689d92..22234a0a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +## [4.11.4](https://github.com/Ombi-app/Ombi/compare/v4.11.3...v4.11.4) (2022-02-05) + + +### Bug Fixes + +* **media-sync:** Add sanity checks upon media server sync ([#4493](https://github.com/Ombi-app/Ombi/issues/4493)) ([9915234](https://github.com/Ombi-app/Ombi/commit/9915234d38d4701c527081ccc21b566980375331)), closes [#4472](https://github.com/Ombi-app/Ombi/issues/4472) +* **newsletter:** Fix newsletter not publishing double episodes ([#4495](https://github.com/Ombi-app/Ombi/issues/4495)) ([ddf63fb](https://github.com/Ombi-app/Ombi/commit/ddf63fbed0b9cbe458aec37318758c76b99b2de9)) + + + +## [4.11.3](https://github.com/Ombi-app/Ombi/compare/v4.11.2...v4.11.3) (2022-02-03) + + +### Bug Fixes + +* **API:** Fixed an issue where the API key couldn't delete a request [#4489](https://github.com/Ombi-app/Ombi/issues/4489) ([8e42dbf](https://github.com/Ombi-app/Ombi/commit/8e42dbf8f78caa51ca891bf3d702c6b0ac401f9c)) + + + ## [4.11.2](https://github.com/Ombi-app/Ombi/compare/v4.11.1...v4.11.2) (2022-02-01) @@ -356,21 +375,3 @@ -## [4.2.10](https://github.com/Ombi-app/Ombi/compare/v4.2.9...v4.2.10) (2021-10-15) - - -### Bug Fixes - -* :bug: Really really fix it this time? ([543d36e](https://github.com/Ombi-app/Ombi/commit/543d36e5615341bc8378cac377b843a3dbc1ef99)) - - - -## [4.2.9](https://github.com/Ombi-app/Ombi/compare/v4.2.8...v4.2.9) (2021-10-15) - - -### Bug Fixes - -* :fire: Really fix the base url issue this time ([9f36923](https://github.com/Ombi-app/Ombi/commit/9f36923c51bfabf9cb026f2da14f9947050af0d9)) - - - diff --git a/README.md b/README.md index 97f61b650..aacb9a2ea 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,13 @@ Here are some of the features Ombi has: + + + sephrat +
+ Sephrat +
+ MrTopCat @@ -128,13 +135,6 @@ Here are some of the features Ombi has: Shaun McPeck - - - sephrat -
- Sephrat -
- MattJeanes diff --git a/src/Ombi.Api.Emby/Models/Media/EmbyProviderids.cs b/src/Ombi.Api.Emby/Models/Media/EmbyProviderids.cs index 8302ee3ee..4a19025de 100644 --- a/src/Ombi.Api.Emby/Models/Media/EmbyProviderids.cs +++ b/src/Ombi.Api.Emby/Models/Media/EmbyProviderids.cs @@ -1,12 +1,10 @@ -namespace Ombi.Api.Emby.Models.Movie +using Ombi.Api.MediaServer.Models; + +namespace Ombi.Api.Emby.Models.Movie { - public class EmbyProviderids + public class EmbyProviderids: BaseProviderids { - public string Tmdb { get; set; } - public string Imdb { get; set; } public string TmdbCollection { get; set; } - - public string Tvdb { get; set; } public string Zap2It { get; set; } public string TvRage { get; set; } } diff --git a/src/Ombi.Api.Emby/Ombi.Api.Emby.csproj b/src/Ombi.Api.Emby/Ombi.Api.Emby.csproj index f167146af..b524c9ce4 100644 --- a/src/Ombi.Api.Emby/Ombi.Api.Emby.csproj +++ b/src/Ombi.Api.Emby/Ombi.Api.Emby.csproj @@ -12,6 +12,7 @@ + diff --git a/src/Ombi.Api.Jellyfin/Models/Media/JellyfinProviderids.cs b/src/Ombi.Api.Jellyfin/Models/Media/JellyfinProviderids.cs index 9b47f9a1a..0896ec4e9 100644 --- a/src/Ombi.Api.Jellyfin/Models/Media/JellyfinProviderids.cs +++ b/src/Ombi.Api.Jellyfin/Models/Media/JellyfinProviderids.cs @@ -1,12 +1,10 @@ -namespace Ombi.Api.Jellyfin.Models.Movie +using Ombi.Api.MediaServer.Models; + +namespace Ombi.Api.Jellyfin.Models.Movie { - public class JellyfinProviderids + public class JellyfinProviderids: BaseProviderids { - public string Tmdb { get; set; } - public string Imdb { get; set; } public string TmdbCollection { get; set; } - - public string Tvdb { get; set; } public string Zap2It { get; set; } public string TvRage { get; set; } } diff --git a/src/Ombi.Api.Jellyfin/Ombi.Api.Jellyfin.csproj b/src/Ombi.Api.Jellyfin/Ombi.Api.Jellyfin.csproj index f167146af..dfdb5a93f 100644 --- a/src/Ombi.Api.Jellyfin/Ombi.Api.Jellyfin.csproj +++ b/src/Ombi.Api.Jellyfin/Ombi.Api.Jellyfin.csproj @@ -13,6 +13,7 @@ + \ No newline at end of file diff --git a/src/Ombi.Api.MediaServer/Models/BaseProviderids.cs b/src/Ombi.Api.MediaServer/Models/BaseProviderids.cs new file mode 100644 index 000000000..6044288c6 --- /dev/null +++ b/src/Ombi.Api.MediaServer/Models/BaseProviderids.cs @@ -0,0 +1,11 @@ +namespace Ombi.Api.MediaServer.Models +{ + public class BaseProviderids + { + public string Tmdb { get; set; } + public string Imdb { get; set; } + public string Tvdb { get; set; } + public bool Any() => + !string.IsNullOrEmpty(Imdb) || !string.IsNullOrEmpty(Tmdb) || !string.IsNullOrEmpty(Tvdb); + } +} \ No newline at end of file diff --git a/src/Ombi.Api.MediaServer/Ombi.Api.MediaServer.csproj b/src/Ombi.Api.MediaServer/Ombi.Api.MediaServer.csproj new file mode 100644 index 000000000..f167146af --- /dev/null +++ b/src/Ombi.Api.MediaServer/Ombi.Api.MediaServer.csproj @@ -0,0 +1,18 @@ + + + + net6.0 + 3.0.0.0 + 3.0.0.0 + + + 8.0 + Debug;Release;NonUiBuild + + + + + + + + \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs index cb7eaefd3..646cbdee0 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs @@ -149,7 +149,7 @@ namespace Ombi.Schedule.Jobs.Emby foreach (var tvShow in tv.Items) { processed++; - if (string.IsNullOrEmpty(tvShow.ProviderIds?.Tvdb)) + if (!tvShow.ProviderIds.Any()) { _logger.LogInformation("Provider Id on tv {0} is null", tvShow.Name); continue; @@ -249,6 +249,12 @@ namespace Ombi.Schedule.Jobs.Emby var alreadyGoingToAdd = content.Any(x => x.EmbyId == movieInfo.Id); if (existingMovie == null && !alreadyGoingToAdd) { + + if (!movieInfo.ProviderIds.Any()) + { + _logger.LogWarning($"Movie {movieInfo.Name} has no relevant metadata. Skipping."); + return; + } _logger.LogDebug("Adding new movie {0}", movieInfo.Name); content.Add(new EmbyContent { diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs index c1ca48ef7..b3c1ffbab 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs @@ -152,6 +152,13 @@ namespace Ombi.Schedule.Jobs.Emby if (existingEpisode == null && !existingInList) { + // Sanity checks + if (ep.IndexNumber == 0) + { + _logger.LogWarning($"Episode {ep.Name} has no episode number. Skipping."); + continue; + } + _logger.LogDebug("Adding new episode {0} to parent {1}", ep.Name, ep.SeriesName); // add it epToAdd.Add(new EmbyEpisode diff --git a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs index 60ee42210..fec5ca0ce 100644 --- a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs @@ -127,7 +127,7 @@ namespace Ombi.Schedule.Jobs.Jellyfin { processed++; - if (string.IsNullOrEmpty(tvShow.ProviderIds?.Tvdb)) + if (!tvShow.ProviderIds.Any()) { _logger.LogInformation("Provider Id on tv {0} is null", tvShow.Name); continue; @@ -217,6 +217,11 @@ namespace Ombi.Schedule.Jobs.Jellyfin var alreadyGoingToAdd = content.Any(x => x.JellyfinId == movieInfo.Id); if (existingMovie == null && !alreadyGoingToAdd) { + if (!movieInfo.ProviderIds.Any()) + { + _logger.LogWarning($"Movie {movieInfo.Name} has no relevant metadata. Skipping."); + return; + } _logger.LogDebug("Adding new movie {0}", movieInfo.Name); content.Add(new JellyfinContent { diff --git a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinEpisodeSync.cs index 27e50c9b7..6e2daa7b3 100644 --- a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinEpisodeSync.cs @@ -66,7 +66,7 @@ namespace Ombi.Schedule.Jobs.Jellyfin public async Task Execute(IJobExecutionContext job) { var settings = await _settings.GetSettingsAsync(); - + Api = _apiFactory.CreateClient(settings); await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) .SendAsync(NotificationHub.NotificationEvent, "Jellyfin Episode Sync Started"); @@ -128,6 +128,13 @@ namespace Ombi.Schedule.Jobs.Jellyfin if (existingEpisode == null && !existingInList) { + // Sanity checks + if (ep.IndexNumber == 0) // no check on season number, Season 0 can be Specials + { + _logger.LogWarning($"Episode {ep.Name} has no episode number. Skipping."); + continue; + } + _logger.LogDebug("Adding new episode {0} to parent {1}", ep.Name, ep.SeriesName); // add it epToAdd.Add(new JellyfinEpisode diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index f965625f6..dbb343126 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -726,28 +726,8 @@ namespace Ombi.Schedule.Jobs.Ombi AddTvTitle(tvInfo); - // Group by the season number - var results = t.Episodes.GroupBy(p => p.SeasonNumber, - (key, g) => new - { - SeasonNumber = key, - Episodes = g.ToList(), - EpisodeAirDate = tvInfo?.seasons?.Where(x => x.season_number == key)?.Select(x => x.air_date).FirstOrDefault() - } - ); - - // Group the episodes - var finalsb = new StringBuilder(); - foreach (var epInformation in results.OrderBy(x => x.SeasonNumber)) - { - var orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList(); - var episodeString = StringHelper.BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber)); - var episodeAirDate = epInformation.EpisodeAirDate; - finalsb.Append($"{Texts.SeasonLabel} {epInformation.SeasonNumber} - {Texts.EpisodesLabel} {episodeString} {episodeAirDate}"); - finalsb.Append("
"); - } - - AddTvEpisodesSummaryGenres(finalsb.ToString(), tvInfo); + var tvEpisodesString = GetTvEpisodesString(tvInfo, t.Episodes); + AddTvEpisodesSummaryGenres(tvEpisodesString, tvInfo); } catch (Exception e) @@ -769,6 +749,44 @@ namespace Ombi.Schedule.Jobs.Ombi } } + private string GetTvEpisodesString(TvInfo tvInfo, ICollection episodes) + { + if (episodes.Count >= tvInfo.number_of_episodes) + { + // do not list individual episodes when the series is complete + return string.Empty; + } + + var sb = new StringBuilder(); + // Group by the season number + var seasons = episodes.GroupBy(p => p.SeasonNumber, + (key, g) => new + { + SeasonNumber = key, + Episodes = g.ToList(), + Header = tvInfo?.seasons?.Where(x => x.season_number == key).FirstOrDefault(), + } + ); + // Group the episodes + foreach (var season in seasons.OrderBy(x => x.SeasonNumber)) + { + string episodeList; + if (season.Episodes.Count >= season.Header.episode_count) + { + // do not list individual episodes when the season is complete + episodeList = string.Empty; + } + else + { + var orderedEpisodes = season.Episodes.OrderBy(x => x.EpisodeNumber).ToList(); + episodeList = $"{Texts.EpisodesLabel} {StringHelper.BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber))}"; + } + var episodeAirDate = season.Header.air_date; + sb.Append($"{Texts.SeasonLabel} {season.SeasonNumber} - {episodeList} {episodeAirDate}"); + sb.Append("
"); + } + return sb.ToString(); + } private void AddTvTitle(TvInfo tvInfo) { var title = ""; diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs index df96457d0..bdb7e70bc 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs @@ -342,6 +342,11 @@ namespace Ombi.Schedule.Jobs.Plex } } + if (!guids.Any()) + { + Logger.LogWarning($"Movie {movie.title} has no relevant metadata. Skipping."); + continue; + } var providerIds = PlexHelper.GetProviderIdsFromMetadata(guids.ToArray()); var item = new PlexServerContent diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs index 4a088ca7b..c17af088c 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs @@ -179,6 +179,13 @@ namespace Ombi.Schedule.Jobs.Plex episode.grandparentRatingKey = seriesExists.Key; } + // Sanity checks + if (episode.index == 0) + { + _log.LogWarning($"Episode {episode.title} has no episode number. Skipping."); + continue; + } + ep.Add(new PlexEpisode { EpisodeNumber = episode.index, diff --git a/src/Ombi.Store/Entities/EmbyEpisode.cs b/src/Ombi.Store/Entities/EmbyEpisode.cs index 97fdb09b1..89103c580 100644 --- a/src/Ombi.Store/Entities/EmbyEpisode.cs +++ b/src/Ombi.Store/Entities/EmbyEpisode.cs @@ -57,11 +57,5 @@ namespace Ombi.Store.Entities return content.OfType().FirstOrDefault( x => x.EmbyId == this.EmbySeries.EmbyId); } - - public override bool IsIn(IMediaServerContent content) - { - return content.Episodes.Cast().Any(x => x.EmbyId == this.EmbyId); - } - } } \ No newline at end of file diff --git a/src/Ombi.Store/Entities/JellyfinEpisode.cs b/src/Ombi.Store/Entities/JellyfinEpisode.cs index 1c0ac423e..5b105003c 100644 --- a/src/Ombi.Store/Entities/JellyfinEpisode.cs +++ b/src/Ombi.Store/Entities/JellyfinEpisode.cs @@ -58,10 +58,5 @@ namespace Ombi.Store.Entities return content.OfType().FirstOrDefault( x => x.JellyfinId == this.JellyfinSeries.JellyfinId); } - - public override bool IsIn(IMediaServerContent content) - { - return content.Episodes.Cast().Any(x => x.JellyfinId == this.JellyfinId); - } } } diff --git a/src/Ombi.Store/Entities/MediaServerContent.cs b/src/Ombi.Store/Entities/MediaServerContent.cs index 3bccfea06..df50efc86 100644 --- a/src/Ombi.Store/Entities/MediaServerContent.cs +++ b/src/Ombi.Store/Entities/MediaServerContent.cs @@ -1,8 +1,7 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.ComponentModel.DataAnnotations.Schema; -using Ombi.Store.Repository; +using System.Linq; namespace Ombi.Store.Entities { @@ -42,6 +41,9 @@ namespace Ombi.Store.Entities public IMediaServerContent Series { get; set; } public abstract IMediaServerContent SeriesIsIn(ICollection content); - public abstract bool IsIn(IMediaServerContent content); + public bool IsIn(IMediaServerContent content) + { + return content.Episodes.Any(x => x.SeasonNumber == this.SeasonNumber && x.EpisodeNumber == this.EpisodeNumber); + } } } \ No newline at end of file diff --git a/src/Ombi.Store/Entities/PlexEpisode.cs b/src/Ombi.Store/Entities/PlexEpisode.cs index ac482d7ac..02cfc8a07 100644 --- a/src/Ombi.Store/Entities/PlexEpisode.cs +++ b/src/Ombi.Store/Entities/PlexEpisode.cs @@ -28,11 +28,5 @@ namespace Ombi.Store.Entities return content.OfType().FirstOrDefault( x => x.Key == this.PlexSeries.Key); } - - public override bool IsIn(IMediaServerContent content) - { - return content.Episodes.Cast().Any(x => x.Key == this.Key); - } - } } \ No newline at end of file diff --git a/version.json b/version.json index 447d806a2..2c5745167 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "4.11.2" + "version": "4.11.4" } \ No newline at end of file