@ -38,16 +38,17 @@ namespace Ombi.Schedule.Jobs.Ombi
{
public class NewsletterJob : HtmlTemplateGenerator , INewsletterJob
{
public NewsletterJob ( IPlexContentRepository plex , IEmbyContentRepository emby , I Repository< RecentlyAddedLog > addedLog ,
public NewsletterJob ( IPlexContentRepository plex , IEmbyContentRepository emby , I JellyfinContentRepository jellyfin , I Repository< RecentlyAddedLog > addedLog ,
IMovieDbApi movieApi , ITvMazeApi tvApi , IEmailProvider email , ISettingsService < CustomizationSettings > custom ,
ISettingsService < EmailNotificationSettings > emailSettings , INotificationTemplatesRepository templateRepo ,
UserManager < OmbiUser > um , ISettingsService < NewsletterSettings > newsletter , ILogger < NewsletterJob > log ,
ILidarrApi lidarrApi , IExternalRepository < LidarrAlbumCache > albumCache , ISettingsService < LidarrSettings > lidarrSettings ,
ISettingsService < OmbiSettings > ombiSettings , ISettingsService < PlexSettings > plexSettings , ISettingsService < EmbySettings > embySettings
, IHubContext < NotificationHub > notification , IRefreshMetadata refreshMetadata )
ISettingsService < OmbiSettings > ombiSettings , ISettingsService < PlexSettings > plexSettings , ISettingsService < EmbySettings > embySettings , ISettingsService < JellyfinSettings > jellyfinSettings ,
IHubContext < NotificationHub > notification , IRefreshMetadata refreshMetadata )
{
_plex = plex ;
_emby = emby ;
_jellyfin = jellyfin ;
_recentlyAddedLog = addedLog ;
_movieApi = movieApi ;
_tvApi = tvApi ;
@ -64,6 +65,7 @@ namespace Ombi.Schedule.Jobs.Ombi
_ombiSettings = ombiSettings ;
_plexSettings = plexSettings ;
_embySettings = embySettings ;
_jellyfinSettings = jellyfinSettings ;
_notification = notification ;
_ombiSettings . ClearCache ( ) ;
_plexSettings . ClearCache ( ) ;
@ -74,6 +76,7 @@ namespace Ombi.Schedule.Jobs.Ombi
private readonly IPlexContentRepository _plex ;
private readonly IEmbyContentRepository _emby ;
private readonly IJellyfinContentRepository _jellyfin ;
private readonly IRepository < RecentlyAddedLog > _recentlyAddedLog ;
private readonly IMovieDbApi _movieApi ;
private readonly ITvMazeApi _tvApi ;
@ -90,6 +93,7 @@ namespace Ombi.Schedule.Jobs.Ombi
private readonly ISettingsService < LidarrSettings > _lidarrSettings ;
private readonly ISettingsService < PlexSettings > _plexSettings ;
private readonly ISettingsService < EmbySettings > _embySettings ;
private readonly ISettingsService < JellyfinSettings > _jellyfinSettings ;
private readonly IHubContext < NotificationHub > _notification ;
private readonly IRefreshMetadata _refreshMetadata ;
@ -123,36 +127,46 @@ namespace Ombi.Schedule.Jobs.Ombi
// Get the Content
var plexContent = _plex . GetAll ( ) . Include ( x = > x . Episodes ) . AsNoTracking ( ) ;
var embyContent = _emby . GetAll ( ) . Include ( x = > x . Episodes ) . AsNoTracking ( ) ;
var jellyfinContent = _jellyfin . GetAll ( ) . Include ( x = > x . Episodes ) . AsNoTracking ( ) ;
var lidarrContent = _lidarrAlbumRepository . GetAll ( ) . AsNoTracking ( ) . ToList ( ) . Where ( x = > x . FullyAvailable ) ;
var addedLog = _recentlyAddedLog . GetAll ( ) ;
var addedPlexMovieLogIds = addedLog . Where ( x = > x . Type = = RecentlyAddedType . Plex & & x . ContentType = = ContentType . Parent ) . Select ( x = > x . ContentId ) . ToHashSet ( ) ;
var addedEmbyMoviesLogIds = addedLog . Where ( x = > x . Type = = RecentlyAddedType . Emby & & x . ContentType = = ContentType . Parent ) . Select ( x = > x . ContentId ) . ToHashSet ( ) ;
var addedJellyfinMoviesLogIds = addedLog . Where ( x = > x . Type = = RecentlyAddedType . Jellyfin & & x . ContentType = = ContentType . Parent ) . Select ( x = > x . ContentId ) . ToHashSet ( ) ;
var addedAlbumLogIds = addedLog . Where ( x = > x . Type = = RecentlyAddedType . Lidarr & & x . ContentType = = ContentType . Album ) . Select ( x = > x . AlbumId ) . ToHashSet ( ) ;
var addedPlexEpisodesLogIds =
addedLog . Where ( x = > x . Type = = RecentlyAddedType . Plex & & x . ContentType = = ContentType . Episode ) ;
var addedEmbyEpisodesLogIds =
addedLog . Where ( x = > x . Type = = RecentlyAddedType . Emby & & x . ContentType = = ContentType . Episode ) ;
var addedJellyfinEpisodesLogIds =
addedLog . Where ( x = > x . Type = = RecentlyAddedType . Jellyfin & & x . ContentType = = ContentType . Episode ) ;
// Filter out the ones that we haven't sent yet
var plexContentLocalDataset = plexContent . Where ( x = > x . Type = = PlexMediaTypeEntity . Movie & & ! string . IsNullOrEmpty ( x . TheMovieDbId ) ) . ToHashSet ( ) ;
var embyContentLocalDataset = embyContent . Where ( x = > x . Type = = EmbyMediaType . Movie & & ! string . IsNullOrEmpty ( x . TheMovieDbId ) ) . ToHashSet ( ) ;
var jellyfinContentLocalDataset = jellyfinContent . Where ( x = > x . Type = = JellyfinMediaType . Movie & & ! string . IsNullOrEmpty ( x . TheMovieDbId ) ) . ToHashSet ( ) ;
var plexContentMoviesToSend = plexContentLocalDataset . Where ( x = > ! addedPlexMovieLogIds . Contains ( StringHelper . IntParseLinq ( x . TheMovieDbId ) ) ) . ToHashSet ( ) ;
var embyContentMoviesToSend = embyContentLocalDataset . Where ( x = > ! addedEmbyMoviesLogIds . Contains ( StringHelper . IntParseLinq ( x . TheMovieDbId ) ) ) . ToHashSet ( ) ;
var jellyfinContentMoviesToSend = jellyfinContentLocalDataset . Where ( x = > ! addedJellyfinMoviesLogIds . Contains ( StringHelper . IntParseLinq ( x . TheMovieDbId ) ) ) . ToHashSet ( ) ;
var lidarrContentAlbumsToSend = lidarrContent . Where ( x = > ! addedAlbumLogIds . Contains ( x . ForeignAlbumId ) ) . ToHashSet ( ) ;
_log . LogInformation ( "Plex Movies to send: {0}" , plexContentMoviesToSend . Count ( ) ) ;
_log . LogInformation ( "Emby Movies to send: {0}" , embyContentMoviesToSend . Count ( ) ) ;
_log . LogInformation ( "Jellyfin Movies to send: {0}" , jellyfinContentMoviesToSend . Count ( ) ) ;
_log . LogInformation ( "Albums to send: {0}" , lidarrContentAlbumsToSend . Count ( ) ) ;
// Find the movies that do not yet have MovieDbIds
var needsMovieDbPlex = plexContent . Where ( x = > x . Type = = PlexMediaTypeEntity . Movie & & ! string . IsNullOrEmpty ( x . TheMovieDbId ) ) . ToHashSet ( ) ;
var needsMovieDbEmby = embyContent . Where ( x = > x . Type = = EmbyMediaType . Movie & & ! string . IsNullOrEmpty ( x . TheMovieDbId ) ) . ToHashSet ( ) ;
var needsMovieDbJellyfin = jellyfinContent . Where ( x = > x . Type = = JellyfinMediaType . Movie & & ! string . IsNullOrEmpty ( x . TheMovieDbId ) ) . ToHashSet ( ) ;
var newPlexMovies = await GetMoviesWithoutId ( addedPlexMovieLogIds , needsMovieDbPlex ) ;
var newEmbyMovies = await GetMoviesWithoutId ( addedEmbyMoviesLogIds , needsMovieDbEmby ) ;
var newJellyfinMovies = await GetMoviesWithoutId ( addedJellyfinMoviesLogIds , needsMovieDbJellyfin ) ;
plexContentMoviesToSend = plexContentMoviesToSend . Union ( newPlexMovies ) . ToHashSet ( ) ;
embyContentMoviesToSend = embyContentMoviesToSend . Union ( newEmbyMovies ) . ToHashSet ( ) ;
jellyfinContentMoviesToSend = jellyfinContentMoviesToSend . Union ( newJellyfinMovies ) . ToHashSet ( ) ;
plexContentMoviesToSend = plexContentMoviesToSend . DistinctBy ( x = > x . Id ) . ToHashSet ( ) ;
embyContentMoviesToSend = embyContentMoviesToSend . DistinctBy ( x = > x . Id ) . ToHashSet ( ) ;
@ -161,24 +175,30 @@ namespace Ombi.Schedule.Jobs.Ombi
FilterPlexEpisodes ( _plex . GetAllEpisodes ( ) . Include ( x = > x . Series ) . AsNoTracking ( ) , addedPlexEpisodesLogIds ) ;
var embyEpisodesToSend = FilterEmbyEpisodes ( _emby . GetAllEpisodes ( ) . Include ( x = > x . Series ) . AsNoTracking ( ) ,
addedEmbyEpisodesLogIds ) ;
var jellyfinEpisodesToSend = FilterJellyfinEpisodes ( _jellyfin . GetAllEpisodes ( ) . Include ( x = > x . Series ) . AsNoTracking ( ) ,
addedJellyfinEpisodesLogIds ) ;
_log . LogInformation ( "Plex Episodes to send: {0}" , plexEpisodesToSend . Count ( ) ) ;
_log . LogInformation ( "Emby Episodes to send: {0}" , embyEpisodesToSend . Count ( ) ) ;
_log . LogInformation ( "Jellyfin Episodes to send: {0}" , jellyfinEpisodesToSend . Count ( ) ) ;
var plexSettings = await _plexSettings . GetSettingsAsync ( ) ;
var embySettings = await _embySettings . GetSettingsAsync ( ) ;
var jellyfinSettings = await _jellyfinSettings . GetSettingsAsync ( ) ;
var body = string . Empty ;
if ( test )
{
var plexm = plexContent . Where ( x = > x . Type = = PlexMediaTypeEntity . Movie ) . OrderByDescending ( x = > x . AddedAt ) . Take ( 10 ) ;
var embym = embyContent . Where ( x = > x . Type = = EmbyMediaType . Movie ) . OrderByDescending ( x = > x . AddedAt ) . Take ( 10 ) ;
var jellyfinm = jellyfinContent . Where ( x = > x . Type = = JellyfinMediaType . Movie ) . OrderByDescending ( x = > x . AddedAt ) . Take ( 10 ) ;
var plext = _plex . GetAllEpisodes ( ) . Include ( x = > x . Series ) . OrderByDescending ( x = > x . Series . AddedAt ) . Take ( 10 ) . ToHashSet ( ) ;
var embyt = _emby . GetAllEpisodes ( ) . Include ( x = > x . Series ) . OrderByDescending ( x = > x . AddedAt ) . Take ( 10 ) . ToHashSet ( ) ;
var jellyfint = _jellyfin . GetAllEpisodes ( ) . Include ( x = > x . Series ) . OrderByDescending ( x = > x . AddedAt ) . Take ( 10 ) . ToHashSet ( ) ;
var lidarr = lidarrContent . OrderByDescending ( x = > x . AddedAt ) . Take ( 10 ) . ToHashSet ( ) ;
body = await BuildHtml ( plexm , embym , plext, embyt , lidarr, settings , emby Settings, plexSettings ) ;
body = await BuildHtml ( plexm , embym , jellyfinm, plext, embyt , jellyfint, lidarr, settings , emby Settings, jellyfin Settings, plexSettings ) ;
}
else
{
body = await BuildHtml ( plexContentMoviesToSend . AsQueryable ( ) , embyContentMoviesToSend . AsQueryable ( ) , plexEpisodesToSend, embyEpisodesToSend , lidarrContentAlbumsToSend, settings , emby Settings, plexSettings ) ;
body = await BuildHtml ( plexContentMoviesToSend . AsQueryable ( ) , embyContentMoviesToSend . AsQueryable ( ) , jellyfinContentMoviesToSend. AsQueryable ( ) , plexEpisodesToSend, embyEpisodesToSend , jellyfinEpisodesToSend, lidarrContentAlbumsToSend, settings , emby Settings, jellyfin Settings, plexSettings ) ;
if ( body . IsNullOrEmpty ( ) )
{
return ;
@ -285,6 +305,34 @@ namespace Ombi.Schedule.Jobs.Ombi
SeasonNumber = p . SeasonNumber
} ) ;
}
foreach ( var e in jellyfinContentMoviesToSend )
{
if ( e . Type = = JellyfinMediaType . Movie )
{
recentlyAddedLog . Add ( new RecentlyAddedLog
{
AddedAt = DateTime . Now ,
Type = RecentlyAddedType . Jellyfin ,
ContentType = ContentType . Parent ,
ContentId = StringHelper . IntParseLinq ( e . TheMovieDbId ) ,
} ) ;
}
}
foreach ( var p in jellyfinEpisodesToSend )
{
recentlyAddedLog . Add ( new RecentlyAddedLog
{
AddedAt = DateTime . Now ,
Type = RecentlyAddedType . Jellyfin ,
ContentType = ContentType . Episode ,
ContentId = StringHelper . IntParseLinq ( p . Series . TvDbId ) ,
EpisodeNumber = p . EpisodeNumber ,
SeasonNumber = p . SeasonNumber
} ) ;
}
await _recentlyAddedLog . AddRange ( recentlyAddedLog ) ;
}
else
@ -349,6 +397,20 @@ namespace Ombi.Schedule.Jobs.Ombi
return result . ToHashSet ( ) ;
}
private async Task < HashSet < JellyfinContent > > GetMoviesWithoutId ( HashSet < int > addedMovieLogIds , HashSet < JellyfinContent > needsMovieDbJellyfin )
{
foreach ( var movie in needsMovieDbJellyfin )
{
var id = await _refreshMetadata . GetTheMovieDbId ( false , true , null , movie . ImdbId , movie . Title , true ) ;
movie . TheMovieDbId = id . ToString ( ) ;
}
var result = needsMovieDbJellyfin . Where ( x = > x . HasTheMovieDb & & ! addedMovieLogIds . Contains ( StringHelper . IntParseLinq ( x . TheMovieDbId ) ) ) ;
await UpdateTheMovieDbId ( result ) ;
// Filter them out now
return result . ToHashSet ( ) ;
}
private async Task UpdateTheMovieDbId ( IEnumerable < PlexServerContent > content )
{
foreach ( var movie in content )
@ -379,6 +441,21 @@ namespace Ombi.Schedule.Jobs.Ombi
await _plex . SaveChangesAsync ( ) ;
}
private async Task UpdateTheMovieDbId ( IEnumerable < JellyfinContent > content )
{
foreach ( var movie in content )
{
if ( ! movie . HasTheMovieDb )
{
continue ;
}
var entity = await _jellyfin . Find ( movie . Id ) ;
entity . TheMovieDbId = movie . TheMovieDbId ;
_jellyfin . UpdateWithoutSave ( entity ) ;
}
await _plex . SaveChangesAsync ( ) ;
}
public async Task Execute ( IJobExecutionContext job )
{
var newsletterSettings = await _newsletterSettings . GetSettingsAsync ( ) ;
@ -419,6 +496,23 @@ namespace Ombi.Schedule.Jobs.Ombi
return itemsToReturn ;
}
private HashSet < JellyfinEpisode > FilterJellyfinEpisodes ( IEnumerable < JellyfinEpisode > source , IQueryable < RecentlyAddedLog > recentlyAdded )
{
var itemsToReturn = new HashSet < JellyfinEpisode > ( ) ;
foreach ( var ep in source . Where ( x = > x . Series . HasTvDb ) )
{
var tvDbId = StringHelper . IntParseLinq ( ep . Series . TvDbId ) ;
if ( recentlyAdded . Any ( x = > x . ContentId = = tvDbId & & x . EpisodeNumber = = ep . EpisodeNumber & & x . SeasonNumber = = ep . SeasonNumber ) )
{
continue ;
}
itemsToReturn . Add ( ep ) ;
}
return itemsToReturn ;
}
private NotificationMessageContent ParseTemplate ( NotificationTemplates template , CustomizationSettings settings )
{
var resolver = new NotificationMessageResolver ( ) ;
@ -429,8 +523,8 @@ namespace Ombi.Schedule.Jobs.Ombi
return resolver . ParseMessage ( template , curlys ) ;
}
private async Task < string > BuildHtml ( IQueryable < PlexServerContent > plexContentToSend , IQueryable < EmbyContent > embyContentToSend ,
HashSet < PlexEpisode > plexEpisodes , HashSet < EmbyEpisode > embyEp , HashSet < LidarrAlbumCache> albums , NewsletterSettings settings , EmbySettings embySettings ,
private async Task < string > BuildHtml ( IQueryable < PlexServerContent > plexContentToSend , IQueryable < EmbyContent > embyContentToSend , IQueryable < JellyfinContent > jellyfinContentToSend ,
HashSet < PlexEpisode > plexEpisodes , HashSet < EmbyEpisode > embyEp , HashSet < JellyfinEpisode> jellyfinEp , HashSet < LidarrAlbumCache> albums , NewsletterSettings settings , EmbySettings embySettings , JellyfinSettings jellyfinSettings ,
PlexSettings plexSettings )
{
var ombiSettings = await _ombiSettings . GetSettingsAsync ( ) ;
@ -438,6 +532,7 @@ namespace Ombi.Schedule.Jobs.Ombi
var plexMovies = plexContentToSend . Where ( x = > x . Type = = PlexMediaTypeEntity . Movie ) ;
var embyMovies = embyContentToSend . Where ( x = > x . Type = = EmbyMediaType . Movie ) ;
var jellyfinMovies = jellyfinContentToSend . Where ( x = > x . Type = = JellyfinMediaType . Movie ) ;
if ( ( plexMovies . Any ( ) | | embyMovies . Any ( ) ) & & ! settings . DisableMovies )
{
sb . Append ( "<h1 style=\"text-align: center; max-width: 1042px;\">New Movies</h1><br /><br />" ) ;
@ -457,6 +552,11 @@ namespace Ombi.Schedule.Jobs.Ombi
await ProcessEmbyMovies ( embyMovies , sb , ombiSettings . DefaultLanguageCode , embySettings . Servers . FirstOrDefault ( ) ? . ServerHostname ? ? string . Empty ) ;
}
if ( jellyfinSettings . Enable )
{
await ProcessJellyfinMovies ( jellyfinMovies , sb , ombiSettings . DefaultLanguageCode , jellyfinSettings . Servers . FirstOrDefault ( ) ? . ServerHostname ? ? string . Empty ) ;
}
sb . Append ( "</tr>" ) ;
sb . Append ( "</table>" ) ;
sb . Append ( "</td>" ) ;
@ -464,7 +564,7 @@ namespace Ombi.Schedule.Jobs.Ombi
sb . Append ( "</table>" ) ;
}
if ( ( plexEpisodes . Any ( ) | | embyEp . Any ( ) ) && ! settings . DisableTv )
if ( ( plexEpisodes . Any ( ) | | embyEp . Any ( ) ) || jellyfinEp . Any ( ) && ! settings . DisableTv )
{
sb . Append ( "<br /><br /><h1 style=\"text-align: center; max-width: 1042px;\">New TV</h1><br /><br />" ) ;
sb . Append (
@ -483,6 +583,11 @@ namespace Ombi.Schedule.Jobs.Ombi
await ProcessEmbyTv ( embyEp , sb , embySettings . Servers . FirstOrDefault ( ) ? . ServerHostname ? ? string . Empty ) ;
}
if ( jellyfinSettings . Enable )
{
await ProcessJellyfinTv ( jellyfinEp , sb , jellyfinSettings . Servers . FirstOrDefault ( ) ? . ServerHostname ? ? string . Empty ) ;
}
sb . Append ( "</tr>" ) ;
sb . Append ( "</table>" ) ;
sb . Append ( "</td>" ) ;
@ -638,6 +743,59 @@ namespace Ombi.Schedule.Jobs.Ombi
}
}
private async Task ProcessJellyfinMovies ( IQueryable < JellyfinContent > embyContent , StringBuilder sb , string defaultLangaugeCode , string customUrl )
{
int count = 0 ;
var ordered = embyContent . OrderByDescending ( x = > x . AddedAt ) ;
foreach ( var content in ordered )
{
var theMovieDbId = content . TheMovieDbId ;
if ( ! content . TheMovieDbId . HasValue ( ) )
{
var imdbId = content . ImdbId ;
var findResult = await _movieApi . Find ( imdbId , ExternalSource . imdb_id ) ;
var result = findResult . movie_results ? . FirstOrDefault ( ) ;
if ( result = = null )
{
continue ;
}
theMovieDbId = result . id . ToString ( ) ;
}
var mediaurl = content . Url ;
if ( customUrl . HasValue ( ) )
{
mediaurl = customUrl ;
}
var info = await _movieApi . GetMovieInformationWithExtraInfo ( StringHelper . IntParseLinq ( theMovieDbId ) , defaultLangaugeCode ) ;
if ( info = = null )
{
continue ;
}
try
{
CreateMovieHtmlContent ( sb , info , mediaurl ) ;
count + = 1 ;
}
catch ( Exception e )
{
_log . LogError ( e , "Error when processing Jellyfin Movies {0}" , info . Title ) ;
}
finally
{
EndLoopHtml ( sb ) ;
}
if ( count = = 2 )
{
count = 0 ;
sb . Append ( "</tr>" ) ;
sb . Append ( "<tr>" ) ;
}
}
}
private void CreateMovieHtmlContent ( StringBuilder sb , MovieResponseDto info , string mediaurl )
{
AddBackgroundInsideTable ( sb , $"https://image.tmdb.org/t/p/w1280/{info.BackdropPath}" ) ;
@ -981,6 +1139,129 @@ namespace Ombi.Schedule.Jobs.Ombi
}
}
private async Task ProcessJellyfinTv ( HashSet < JellyfinEpisode > embyContent , StringBuilder sb , string serverUrl )
{
var series = new List < JellyfinContent > ( ) ;
foreach ( var episode in embyContent )
{
var alreadyAdded = series . FirstOrDefault ( x = > x . JellyfinId = = episode . Series . JellyfinId ) ;
if ( alreadyAdded ! = null )
{
alreadyAdded . Episodes . Add ( episode ) ;
}
else
{
episode . Series . Episodes = new List < JellyfinEpisode >
{
episode
} ;
series . Add ( episode . Series ) ;
}
}
int count = 0 ;
var orderedTv = series . OrderByDescending ( x = > x . AddedAt ) ;
foreach ( var t in orderedTv )
{
if ( ! t . TvDbId . HasValue ( ) )
{
continue ;
}
int . TryParse ( t . TvDbId , out var tvdbId ) ;
var info = await _tvApi . ShowLookupByTheTvDbId ( tvdbId ) ;
if ( info = = null )
{
continue ;
}
try
{
var banner = info . image ? . original ;
if ( ! string . IsNullOrEmpty ( banner ) )
{
banner = banner . ToHttpsUrl ( ) ; // Always use the Https banners
}
var tvInfo = await _movieApi . GetTVInfo ( t . TheMovieDbId ) ;
if ( tvInfo ! = null & & tvInfo . backdrop_path . HasValue ( ) )
{
AddBackgroundInsideTable ( sb , $"https://image.tmdb.org/t/p/w500{tvInfo.backdrop_path}" ) ;
}
else
{
AddBackgroundInsideTable ( sb , $"https://image.tmdb.org/t/p/w1280/" ) ;
}
AddPosterInsideTable ( sb , banner ) ;
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 ) ;
// 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 ( $"Season: {epInformation.SeasonNumber} - Episodes: {episodeString} {episodeAirDate}" ) ;
finalsb . Append ( "<br />" ) ;
}
var summary = info . summary ;
if ( summary . Length > 280 )
{
summary = summary . Remove ( 280 ) ;
summary = summary + "...</p>" ;
}
AddTvParagraph ( sb , finalsb . ToString ( ) , summary ) ;
if ( info . genres . Any ( ) )
{
AddGenres ( sb , $"Genres: {string.Join(" , ", info.genres.Select(x => x.ToString()).ToArray())}" ) ;
}
}
catch ( Exception e )
{
_log . LogError ( e , "Error when processing Jellyfin TV {0}" , t . Title ) ;
}
finally
{
EndLoopHtml ( sb ) ;
count + = 1 ;
}
if ( count = = 2 )
{
count = 0 ;
sb . Append ( "</tr>" ) ;
sb . Append ( "<tr>" ) ;
}
}
}
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...
@ -1040,4 +1321,4 @@ namespace Ombi.Schedule.Jobs.Ombi
GC . SuppressFinalize ( this ) ;
}
}
}
}