Merge pull request #1362 from tidusjar/EAP

Rework sonarr and newsletter fixes
pull/1366/head v2.2.1
Jamie 8 years ago committed by GitHub
commit 574fc3841c

@ -56,5 +56,8 @@ namespace Ombi.Api.Interfaces
Series UpdateSeries(Series series, string apiKey, Uri baseUrl);
SonarrSeasonSearchResult SearchForSeason(int seriesId, int seasonNumber, string apiKey, Uri baseUrl);
SonarrSeriesSearchResult SearchForSeries(int seriesId, string apiKey, Uri baseUrl);
SonarrAddSeries AddSeries(SonarrAddSeries series, string apiKey, Uri baseUrl);
}
}

@ -148,6 +148,42 @@ namespace Ombi.Api
return result;
}
public SonarrAddSeries AddSeries(SonarrAddSeries series,string apiKey, Uri baseUrl)
{
var request = new RestRequest
{
Resource = "/api/Series?",
Method = Method.POST
};
Log.Debug("Sonarr API Options:");
Log.Debug(series.DumpJson());
request.AddHeader("X-Api-Key", apiKey);
request.AddJsonBody(series);
SonarrAddSeries result;
try
{
var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) => Log.Error(exception, "Exception when calling AddSeries for Sonarr, Retrying {0}", timespan), new TimeSpan[] {
TimeSpan.FromSeconds (2)
});
result = policy.Execute(() => Api.ExecuteJson<SonarrAddSeries>(request, baseUrl));
}
catch (JsonSerializationException jse)
{
Log.Error(jse);
var error = Api.ExecuteJson<List<SonarrError>>(request, baseUrl);
var messages = error?.Select(x => x.errorMessage).ToList();
messages?.ForEach(x => Log.Error(x));
result = new SonarrAddSeries { ErrorMessages = messages };
}
return result;
}
public SonarrAddSeries AddSeriesNew(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, bool searchForMissingEpisodes = false)
{
var request = new RestRequest
@ -244,7 +280,18 @@ namespace Ombi.Api
TimeSpan.FromSeconds(5)
});
return policy.Execute(() => Api.ExecuteJson<List<Series>>(request, baseUrl));
var series = policy.Execute(() => Api.ExecuteJson<List<Series>>(request, baseUrl));
// Remove the 'specials from the object'
foreach (var s in series)
{
var seasonToRemove = s.seasons.FirstOrDefault(x => x.seasonNumber == 0);
if (seasonToRemove != null)
{
s.seasons.Remove(seasonToRemove);
}
}
return series;
}
catch (Exception e)
{
@ -266,7 +313,15 @@ namespace Ombi.Api
Log.Error(exception, "Exception when calling GetSeries by ID for Sonarr, Retrying {0}",
timespan));
return policy.Execute(() => Api.ExecuteJson<Series>(request, baseUrl));
var series = policy.Execute(() => Api.ExecuteJson<Series>(request, baseUrl));
// Remove the specials season
var toRemove = series.seasons.FirstOrDefault(x => x.seasonNumber == 0);
if (toRemove != null)
{
series.seasons.Remove(toRemove);
}
return series;
}
catch (Exception e)
{

@ -94,7 +94,7 @@ namespace Ombi.Core.Migration.Migrations
content.Add(new RecentlyAddedLog
{
AddedAt = DateTime.UtcNow,
ProviderId = ep.ProviderId
ProviderId = ep.RatingKey
});
}

@ -0,0 +1,134 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: Version1100.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using System.Data;
using NLog;
using Ombi.Core.SettingModels;
using Ombi.Store;
using Ombi.Store.Models;
using Ombi.Store.Models.Emby;
using Ombi.Store.Models.Plex;
using Ombi.Store.Repository;
using Quartz.Collection;
namespace Ombi.Core.Migration.Migrations
{
[Migration(22100, "v2.21.0.0")]
public class Version2210 : BaseMigration, IMigration
{
public Version2210(IRepository<RecentlyAddedLog> log,
IRepository<PlexContent> content, IRepository<PlexEpisodes> plexEp, IRepository<EmbyContent> embyContent, IRepository<EmbyEpisodes> embyEp)
{
Log = log;
PlexContent = content;
PlexEpisodes = plexEp;
EmbyContent = embyContent;
EmbyEpisodes = embyEp;
}
public int Version => 22100;
private IRepository<RecentlyAddedLog> Log { get; }
private IRepository<PlexContent> PlexContent { get; }
private IRepository<PlexEpisodes> PlexEpisodes { get; }
private IRepository<EmbyContent> EmbyContent { get; }
private IRepository<EmbyEpisodes> EmbyEpisodes { get; }
public void Start(IDbConnection con)
{
UpdateRecentlyAdded(con);
UpdateSchema(con, Version);
}
private void UpdateRecentlyAdded(IDbConnection con)
{
//Delete the recently added table, lets start again
Log.DeleteAll("RecentlyAddedLog");
// Plex
var plexAllContent = PlexContent.GetAll();
var content = new HashSet<RecentlyAddedLog>();
foreach (var plexContent in plexAllContent)
{
if(plexContent.Type == PlexMediaType.Artist) continue;
content.Add(new RecentlyAddedLog
{
AddedAt = DateTime.UtcNow,
ProviderId = plexContent.ProviderId
});
}
Log.BatchInsert(content, "RecentlyAddedLog");
var plexEpisodeses = PlexEpisodes.GetAll();
content.Clear();
foreach (var ep in plexEpisodeses)
{
content.Add(new RecentlyAddedLog
{
AddedAt = DateTime.UtcNow,
ProviderId = ep.RatingKey
});
}
Log.BatchInsert(content, "RecentlyAddedLog");
// Emby
content.Clear();
var embyContent = EmbyContent.GetAll();
foreach (var plexContent in embyContent)
{
content.Add(new RecentlyAddedLog
{
AddedAt = DateTime.UtcNow,
ProviderId = plexContent.EmbyId
});
}
Log.BatchInsert(content, "RecentlyAddedLog");
var embyEpisodes = EmbyEpisodes.GetAll();
content.Clear();
foreach (var ep in embyEpisodes)
{
content.Add(new RecentlyAddedLog
{
AddedAt = DateTime.UtcNow,
ProviderId = ep.EmbyId
});
}
Log.BatchInsert(content, "RecentlyAddedLog");
}
}
}

@ -69,6 +69,7 @@
<Compile Include="MigrationAttribute.cs" />
<Compile Include="MigrationRunner.cs" />
<Compile Include="Migrations\BaseMigration.cs" />
<Compile Include="Migrations\Version2210.cs" />
<Compile Include="Migrations\Version2200.cs" />
<Compile Include="Migrations\Version1100.cs" />
<Compile Include="Migrations\Version195.cs" />

@ -155,8 +155,9 @@
<Compile Include="StatusChecker\AppveyorArtifactResult.cs" />
<Compile Include="StatusChecker\StatusChecker.cs" />
<Compile Include="StatusChecker\AppveyorBranchResult.cs" />
<Compile Include="TvSender.cs" />
<Compile Include="TvSenderOld.cs" />
<Compile Include="Tv\TvSender.cs" />
<Compile Include="Tv\TvSenderOld.cs" />
<Compile Include="Tv\TvSenderV2.cs" />
<Compile Include="Users\IUserHelper.cs" />
<Compile Include="Users\UserHelper.cs" />
<Compile Include="UserIdentity.cs" />

@ -87,6 +87,8 @@ namespace Ombi.Core
var rootFolderPath = model.RootFolderSelected <= 0 ? sonarrSettings.FullRootPath : await GetRootPath(model.RootFolderSelected, sonarrSettings);
if (episodeRequest)
{
// Does series exist?

@ -0,0 +1,302 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2017 Jamie Rees
// File: TvSenderV2.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NLog;
using Ombi.Api.Interfaces;
using Ombi.Api.Models.SickRage;
using Ombi.Api.Models.Sonarr;
using Ombi.Core.SettingModels;
using Ombi.Helpers;
using Ombi.Store;
namespace Ombi.Core.Tv
{
public class TvSenderV2
{
public TvSenderV2(ISonarrApi sonarrApi, ISickRageApi srApi, ICacheProvider cache)
{
SonarrApi = sonarrApi;
SickrageApi = srApi;
Cache = cache;
}
private ISonarrApi SonarrApi { get; }
private ISickRageApi SickrageApi { get; }
private ICacheProvider Cache { get; }
private static Logger _log = LogManager.GetCurrentClassLogger();
public async Task<SonarrAddSeries> SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model)
{
return await SendToSonarr(sonarrSettings, model, string.Empty);
}
public async Task<SonarrAddSeries> SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model,
string qualityId)
{
var qualityProfile = 0;
if (!string.IsNullOrEmpty(qualityId)) // try to parse the passed in quality, otherwise use the settings default quality
{
int.TryParse(qualityId, out qualityProfile);
}
if (qualityProfile <= 0)
{
int.TryParse(sonarrSettings.QualityProfile, out qualityProfile);
}
var rootFolderPath = model.RootFolderSelected <= 0 ? sonarrSettings.FullRootPath : await GetSonarrRootPath(model.RootFolderSelected, sonarrSettings);
var episodeRequest = model.Episodes.Any();
var requestAll = model.SeasonsRequested?.Equals("All", StringComparison.CurrentCultureIgnoreCase);
var first = model.SeasonsRequested?.Equals("First", StringComparison.CurrentCultureIgnoreCase);
var latest = model.SeasonsRequested?.Equals("Latest", StringComparison.CurrentCultureIgnoreCase);
var specificSeasonRequest = model.SeasonList?.Any();
if (episodeRequest)
{
return await ProcessSonarrEpisodeRequest(sonarrSettings, model, qualityProfile, rootFolderPath);
}
if (requestAll ?? false)
{
return await ProcessSonarrRequestSeason(sonarrSettings, model, qualityProfile, rootFolderPath);
}
if (first ?? false)
{
return await ProcessSonarrRequestSeason(sonarrSettings, model, qualityProfile, rootFolderPath);
}
if (latest ?? false)
{
return await ProcessSonarrRequestSeason(sonarrSettings, model, qualityProfile, rootFolderPath);
}
if (specificSeasonRequest ?? false)
{
return await ProcessSonarrRequestSeason(sonarrSettings, model, qualityProfile, rootFolderPath);
}
return null;
}
private async Task<SonarrAddSeries> ProcessSonarrRequestSeason(SonarrSettings sonarrSettings, RequestedModel model, int qualityId, string rootFolderPath)
{
// Does the series exist?
var series = await GetSonarrSeries(sonarrSettings, model.ProviderId);
if (series == null)
{
//WORKS
// Add the series
return AddSeries(sonarrSettings, model, rootFolderPath, qualityId);
}
// Also make sure the series is now monitored otherwise we won't search for it
series.monitored = true;
foreach (var seasons in series.seasons)
{
seasons.monitored = true;
}
// Send the update command
series = SonarrApi.UpdateSeries(series, sonarrSettings.ApiKey, sonarrSettings.FullUri);
SonarrApi.SearchForSeries(series.id, sonarrSettings.ApiKey, sonarrSettings.FullUri);
return new SonarrAddSeries { title = series.title };
}
private async Task<SonarrAddSeries> ProcessSonarrEpisodeRequest(SonarrSettings sonarrSettings, RequestedModel model, int qualityId, string rootFolderPath)
{
// Does the series exist?
var series = await GetSonarrSeries(sonarrSettings, model.ProviderId);
if (series == null)
{
var seriesToAdd = new SonarrAddSeries
{
seasonFolder = sonarrSettings.SeasonFolders,
title = model.Title,
qualityProfileId = qualityId,
tvdbId = model.ProviderId,
titleSlug = model.Title,
seasons = new List<Season>(),
rootFolderPath = rootFolderPath,
monitored = true, // Montior the series
images = new List<SonarrImage>(),
addOptions = new AddOptions
{
ignoreEpisodesWithFiles = true, // We don't really care about these
ignoreEpisodesWithoutFiles = true, // We do not want to grab random episodes missing
searchForMissingEpisodes = false // we want don't want to search for the missing episodes either
}
};
for (var i = 1; i <= model.SeasonCount; i++)
{
var season = new Season
{
seasonNumber = i,
monitored = false // Do not monitor any seasons
};
seriesToAdd.seasons.Add(season);
}
// Add the series now
var result = SonarrApi.AddSeries(seriesToAdd, sonarrSettings.ApiKey, sonarrSettings.FullUri);
await RequestEpisodesForSonarr(model, result.id, sonarrSettings);
}
else
{
await RequestEpisodesForSonarr(model, series.id, sonarrSettings);
}
return new SonarrAddSeries() { title = model.Title };
}
public SonarrAddSeries AddSeries(SonarrSettings sonarrSettings, RequestedModel model, string rootFolderPath, int qualityId)
{
//WORKS
// Add the series
var seriesToAdd = new SonarrAddSeries
{
seasonFolder = sonarrSettings.SeasonFolders,
title = model.Title,
qualityProfileId = qualityId,
tvdbId = model.ProviderId,
titleSlug = model.Title,
seasons = new List<Season>(),
rootFolderPath = rootFolderPath,
monitored = true, // Montior the series
images = new List<SonarrImage>(),
addOptions = new AddOptions
{
ignoreEpisodesWithFiles = true, // We don't really care about these
ignoreEpisodesWithoutFiles = false, // We want to get the whole season
searchForMissingEpisodes = true // we want to search for missing
}
};
for (var i = 1; i <= model.SeasonCount; i++)
{
var season = new Season
{
seasonNumber = i,
// The model.SeasonList.Lenth is 0 when this is a "request all"
monitored = model.SeasonList.Length == 0 || model.SeasonList.Any(x => x == i)
};
seriesToAdd.seasons.Add(season);
}
return SonarrApi.AddSeries(seriesToAdd, sonarrSettings.ApiKey, sonarrSettings.FullUri);
}
private async Task RequestEpisodesForSonarr(RequestedModel model, int showId, SonarrSettings sonarrSettings)
{
// Now lookup all episodes
var ep = SonarrApi.GetEpisodes(showId.ToString(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
var episodes = ep?.ToList() ?? new List<SonarrEpisodes>();
var internalEpisodeIds = new List<int>();
var tasks = new List<Task>();
foreach (var r in model.Episodes)
{
// Match the episode and season number.
// If the episode is monitored we might not be searching for it.
var episode =
episodes.FirstOrDefault(
x => x.episodeNumber == r.EpisodeNumber && x.seasonNumber == r.SeasonNumber);
if (episode == null)
{
continue;
}
var episodeInfo = SonarrApi.GetEpisode(episode.id.ToString(), sonarrSettings.ApiKey,
sonarrSettings.FullUri);
episodeInfo.monitored = true; // Set the episode to monitored
tasks.Add(Task.Run(() => SonarrApi.UpdateEpisode(episodeInfo, sonarrSettings.ApiKey,
sonarrSettings.FullUri)));
internalEpisodeIds.Add(episode.id);
}
await Task.WhenAll(tasks.ToArray());
SonarrApi.SearchForEpisodes(internalEpisodeIds.ToArray(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
}
public SickRageTvAdd SendToSickRage(SickRageSettings sickRageSettings, RequestedModel model)
{
return SendToSickRage(sickRageSettings, model, sickRageSettings.QualityProfile);
}
public SickRageTvAdd SendToSickRage(SickRageSettings sickRageSettings, RequestedModel model, string qualityId)
{
_log.Info("Sending to SickRage {0}", model.Title);
if (sickRageSettings.Qualities.All(x => x.Key != qualityId))
{
qualityId = sickRageSettings.QualityProfile;
}
var apiResult = SickrageApi.AddSeries(model.ProviderId, model.SeasonCount, model.SeasonList, qualityId,
sickRageSettings.ApiKey, sickRageSettings.FullUri);
var result = apiResult.Result;
return result;
}
private async Task<Series> GetSonarrSeries(SonarrSettings sonarrSettings, int showId)
{
var task = await Task.Run(() => SonarrApi.GetSeries(sonarrSettings.ApiKey, sonarrSettings.FullUri)).ConfigureAwait(false);
var selectedSeries = task.FirstOrDefault(series => series.tvdbId == showId);
return selectedSeries;
}
private async Task<string> GetSonarrRootPath(int pathId, SonarrSettings sonarrSettings)
{
var rootFoldersResult = await Cache.GetOrSetAsync(CacheKeys.SonarrRootFolders, async () =>
{
return await Task.Run(() => SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri));
});
foreach (var r in rootFoldersResult.Where(r => r.id == pathId))
{
return r.path;
}
return string.Empty;
}
}
}

@ -276,7 +276,8 @@ namespace Ombi.Services.Jobs
Title = m.Title,
Type = Store.Models.Plex.PlexMediaType.Movie,
Url = m.Url,
ItemId = m.ItemId
ItemId = m.ItemId,
AddedAt = DateTime.UtcNow,
});
}
}
@ -318,7 +319,8 @@ namespace Ombi.Services.Jobs
Type = Store.Models.Plex.PlexMediaType.Show,
Url = t.Url,
Seasons = ByteConverterHelper.ReturnBytes(t.Seasons),
ItemId = t.ItemId
ItemId = t.ItemId,
AddedAt = DateTime.UtcNow,
});
}
}
@ -360,7 +362,8 @@ namespace Ombi.Services.Jobs
Title = a.Title,
Type = Store.Models.Plex.PlexMediaType.Artist,
Url = a.Url,
ItemId = "album"
ItemId = "album",
AddedAt = DateTime.UtcNow,
});
}
}

@ -109,7 +109,7 @@ namespace Ombi.Services.Jobs
var metadata = PlexApi.GetEpisodeMetaData(settings.PlexAuthToken, settings.FullUri, video.RatingKey);
// Loop through the metadata and create the model to insert into the DB
foreach (var metadataVideo in metadata.Video)
foreach (var metadataVideo in metadata?.Video ?? new List<Video>())
{
if(string.IsNullOrEmpty(metadataVideo.GrandparentTitle))
{
@ -119,11 +119,11 @@ namespace Ombi.Services.Jobs
entities.TryAdd(
new PlexEpisodes
{
EpisodeNumber = epInfo.EpisodeNumber,
EpisodeNumber = epInfo?.EpisodeNumber ?? 0,
EpisodeTitle = metadataVideo.Title,
ProviderId = epInfo.ProviderId,
ProviderId = epInfo?.ProviderId ?? "",
RatingKey = metadataVideo.RatingKey,
SeasonNumber = epInfo.SeasonNumber,
SeasonNumber = epInfo?.SeasonNumber ?? 0,
ShowTitle = metadataVideo.GrandparentTitle
},
1);
@ -152,15 +152,7 @@ namespace Ombi.Services.Jobs
return;
}
var jobs = Job.GetJobs();
var job = jobs.FirstOrDefault(x => x.Name.Equals(JobNames.EpisodeCacher, StringComparison.CurrentCultureIgnoreCase));
if (job != null)
{
if (job.LastRun > DateTime.Now.AddHours(-11)) // If it's been run in the last 11 hours
{
return;
}
}
Job.SetRunning(true, JobNames.EpisodeCacher);
CacheEpisodes(s);
}

@ -45,6 +45,7 @@ using Ombi.Store.Models.Emby;
using Ombi.Store.Repository;
using TMDbLib.Objects.Exceptions;
using EmbyMediaType = Ombi.Store.Models.Plex.EmbyMediaType;
using Polly;
namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
{
@ -114,21 +115,27 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
var firstRun = !recentlyAdded.Any();
var filteredMovies = movie.Where(m => recentlyAdded.All(x => x.ProviderId != m.ProviderId)).ToList();
var filteredEp = episodes.Where(m => recentlyAdded.All(x => x.ProviderId != m.ProviderId)).ToList();
var filteredSeries = series.Where(m => recentlyAdded.All(x => x.ProviderId != m.ProviderId)).ToList();
var filteredMovies = movie.Where(m => recentlyAdded.All(x => x.ProviderId != m.EmbyId)).ToList();
var filteredEp = episodes.Where(m => recentlyAdded.All(x => x.ProviderId != m.EmbyId)).ToList();
var filteredSeries = series.Where(m => recentlyAdded.All(x => x.ProviderId != m.EmbyId)).ToList();
var info = new List<EmbyRecentlyAddedModel>();
foreach (var m in filteredMovies)
foreach (var m in filteredMovies.OrderByDescending(x => x.AddedAt))
{
var i = Api.GetInformation(m.EmbyId, Ombi.Api.Models.Emby.EmbyMediaType.Movie,
embySettings.ApiKey, embySettings.AdministratorId, embySettings.FullUri);
info.Add(new EmbyRecentlyAddedModel
var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) =>
Log.Error(exception, "Exception thrown when processing an emby movie for the newsletter, Retrying {0}", timespan));
var result = policy.Execute(() =>
{
EmbyInformation = i,
EmbyContent = m
var i = Api.GetInformation(m.EmbyId, Ombi.Api.Models.Emby.EmbyMediaType.Movie,
embySettings.ApiKey, embySettings.AdministratorId, embySettings.FullUri);
return new EmbyRecentlyAddedModel
{
EmbyInformation = i,
EmbyContent = m
};
});
info.Add(result);
}
GenerateMovieHtml(info, sb);
newsletter.MovieCount = info.Count;
@ -137,51 +144,59 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
// Check if there are any epiosdes, then get the series info.
// Otherwise then just add the series to the newsletter
if (test && !filteredEp.Any() && episodes.Any())
{
// if this is a test make sure we show something
filteredEp = episodes.Take(5).ToList();
}
if (filteredEp.Any())
{
var recentlyAddedModel = new List<EmbyRecentlyAddedModel>();
foreach (var embyEpisodes in filteredEp)
{
// Let's sleep, Emby can't keep up with us.
Thread.Sleep(1000);
try
{
var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) =>
Log.Error(exception, "Exception thrown when processing an emby episode for the newsletter, Retrying {0}", timespan));
// Find related series item
var relatedSeries = series.FirstOrDefault(x => x.EmbyId == embyEpisodes.ParentId);
if (relatedSeries == null)
policy.Execute(() =>
{
continue;
}
// Find related series item
var relatedSeries = series.FirstOrDefault(x => x.EmbyId == embyEpisodes.ParentId);
// Get series information
var i = Api.GetInformation(relatedSeries.EmbyId, Ombi.Api.Models.Emby.EmbyMediaType.Series,
embySettings.ApiKey, embySettings.AdministratorId, embySettings.FullUri);
if (relatedSeries == null)
{
return;
}
Thread.Sleep(200);
var episodeInfo = Api.GetInformation(embyEpisodes.EmbyId,
Ombi.Api.Models.Emby.EmbyMediaType.Episode,
embySettings.ApiKey, embySettings.AdministratorId, embySettings.FullUri);
// Check if we already have this series
var existingSeries = recentlyAddedModel.FirstOrDefault(x =>
x.EmbyInformation.SeriesInformation.Id.Equals(i.SeriesInformation.Id,
StringComparison.CurrentCultureIgnoreCase));
// Get series information
var i = Api.GetInformation(relatedSeries.EmbyId, Ombi.Api.Models.Emby.EmbyMediaType.Series,
embySettings.ApiKey, embySettings.AdministratorId, embySettings.FullUri);
if (existingSeries != null)
{
existingSeries.EpisodeInformation.Add(episodeInfo.EpisodeInformation);
}
else
{
recentlyAddedModel.Add(new EmbyRecentlyAddedModel
Thread.Sleep(200);
var episodeInfo = Api.GetInformation(embyEpisodes.EmbyId,
Ombi.Api.Models.Emby.EmbyMediaType.Episode,
embySettings.ApiKey, embySettings.AdministratorId, embySettings.FullUri);
// Check if we already have this series
var existingSeries = recentlyAddedModel.FirstOrDefault(x =>
x.EmbyInformation.SeriesInformation.Id.Equals(i.SeriesInformation.Id,
StringComparison.CurrentCultureIgnoreCase));
if (existingSeries != null)
{
EmbyInformation = i,
EpisodeInformation = new List<EmbyEpisodeInformation>() { episodeInfo.EpisodeInformation },
EmbyContent = relatedSeries
});
}
existingSeries.EpisodeInformation.Add(episodeInfo.EpisodeInformation);
}
else
{
recentlyAddedModel.Add(new EmbyRecentlyAddedModel
{
EmbyInformation = i,
EpisodeInformation = new List<EmbyEpisodeInformation>() { episodeInfo.EpisodeInformation },
EmbyContent = relatedSeries
});
}
});
}
catch (JsonReaderException)
@ -195,15 +210,23 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
}
else
{
foreach (var t in filteredSeries)
foreach (var t in filteredSeries.OrderByDescending(x => x.AddedAt))
{
var i = Api.GetInformation(t.EmbyId, Ombi.Api.Models.Emby.EmbyMediaType.Series,
embySettings.ApiKey, embySettings.AdministratorId, embySettings.FullUri);
var item = new EmbyRecentlyAddedModel
var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) =>
Log.Error(exception, "Exception thrown when processing an emby series for the newsletter, Retrying {0}", timespan));
var item = policy.Execute(() =>
{
EmbyContent = t,
EmbyInformation = i,
};
var i = Api.GetInformation(t.EmbyId, Ombi.Api.Models.Emby.EmbyMediaType.Series,
embySettings.ApiKey, embySettings.AdministratorId, embySettings.FullUri);
var model = new EmbyRecentlyAddedModel
{
EmbyContent = t,
EmbyInformation = i,
};
return model;
});
info.Add(item);
}
}
@ -221,7 +244,7 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
{
RecentlyAddedLog.Insert(new RecentlyAddedLog
{
ProviderId = a.ProviderId,
ProviderId = a.EmbyId,
AddedAt = DateTime.UtcNow
});
}
@ -229,7 +252,15 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
{
RecentlyAddedLog.Insert(new RecentlyAddedLog
{
ProviderId = a.ProviderId,
ProviderId = a.EmbyId,
AddedAt = DateTime.UtcNow
});
}
foreach (var s in filteredSeries)
{
RecentlyAddedLog.Insert(new RecentlyAddedLog
{
ProviderId = s.EmbyId,
AddedAt = DateTime.UtcNow
});
}

@ -117,11 +117,17 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
var firstRun = !recentlyAdded.Any();
var filteredMovies = movie.Where(m => recentlyAdded.All(x => x.ProviderId != m.ProviderId)).ToList();
var filteredEp = episodes.Where(m => recentlyAdded.All(x => x.ProviderId != m.ProviderId)).ToList();
var filteredEp = episodes.Where(m => recentlyAdded.All(x => x.ProviderId != m.RatingKey)).ToList();
var filteredSeries = series.Where(x => recentlyAdded.All(c => c.ProviderId != x.ProviderId)).ToList();
var info = new List<PlexRecentlyAddedModel>();
foreach (var m in filteredMovies)
if (test && !filteredMovies.Any())
{
// if this is a test make sure we show something
filteredMovies = movie.Take(5).ToList();
}
foreach (var m in filteredMovies.OrderByDescending(x => x.AddedAt))
{
var i = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, m.ItemId);
if (i.Video == null)
@ -138,6 +144,11 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
newsletter.MovieCount = info.Count;
info.Clear();
if (test && !filteredEp.Any() && episodes.Any())
{
// if this is a test make sure we show something
filteredEp = episodes.Take(5).ToList();
}
if (filteredEp.Any())
{
var recentlyAddedModel = new List<PlexRecentlyAddedModel>();
@ -178,7 +189,12 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
}
else
{
foreach (var t in filteredSeries)
if (test && !filteredSeries.Any())
{
// if this is a test make sure we show something
filteredSeries = series.Take(5).ToList();
}
foreach (var t in filteredSeries.OrderByDescending(x => x.AddedAt))
{
var i = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, t.ItemId);
if (i.Directory == null)
@ -215,7 +231,7 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
{
RecentlyAddedLog.Insert(new RecentlyAddedLog
{
ProviderId = a.ProviderId,
ProviderId = a.RatingKey,
AddedAt = DateTime.UtcNow
});
}
@ -324,7 +340,7 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
try
{
var info = TvApi.ShowLookupByTheTvDbId(int.Parse(PlexHelper.GetProviderIdFromPlexGuid(t.Metadata.Directory.Guid)));
var info = TvApi.ShowLookupByTheTvDbId(int.Parse(PlexHelper.GetProviderIdFromPlexGuid(t?.Metadata?.Directory?.Guid ?? string.Empty)));
var banner = info.image?.original;
if (!string.IsNullOrEmpty(banner))
@ -359,7 +375,7 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
for (var i = 0; i < orderedEpisodes.Count; i++)
{
var ep = orderedEpisodes[i];
if (i <= orderedEpisodes.Count - 1)
if (i < orderedEpisodes.Count - 1)
{
epSb.Append($"{ep.Video.FirstOrDefault().Index},");
}

@ -48,6 +48,7 @@
<HintPath>..\packages\NLog.4.3.6\lib\net45\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Polly, Version=4.3.0.0, Culture=neutral, PublicKeyToken=c8a3ffc3f8f825cc" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Security" />

@ -34,6 +34,9 @@ namespace Ombi.Store.Models
[Table("RecentlyAddedLog")]
public class RecentlyAddedLog : Entity
{
/// <summary>
/// This is actually a unique id for that content...
/// </summary>
public string ProviderId { get; set; }
public DateTime AddedAt { get; set; }
}

@ -68,7 +68,7 @@
<Compile Include="Models\Emby\EmbyContent.cs" />
<Compile Include="Models\Emby\EmbyEpisodes.cs" />
<Compile Include="Models\IssueBlobs.cs" />
<Compile Include="Models\RecenetlyAddedLog.cs" />
<Compile Include="Models\RecentlyAddedLog.cs" />
<Compile Include="Models\Plex\PlexEpisodes.cs" />
<Compile Include="Models\Emby\EmbyUsers.cs" />
<Compile Include="Models\Plex\PlexUsers.cs" />

@ -142,8 +142,8 @@ namespace Ombi.UI
pipelines.AfterRequest.AddItemToEndOfPipeline((ctx) =>
{
ctx.Response.WithHeader("Access-Control-Allow-Origin", "*")
.WithHeader("Access-Control-Allow-Methods", "POST,GET")
.WithHeader("Access-Control-Allow-Headers", "Accept, Origin, Content-type");
.WithHeader("Access-Control-Allow-Methods", "POST,GET")
.WithHeader("Access-Control-Allow-Headers", "Accept, Origin, Content-type");
});
base.RequestStartup(container, pipelines, context);

@ -46,6 +46,7 @@ using Ombi.Core;
using Ombi.Core.Models;
using Ombi.Core.Queue;
using Ombi.Core.SettingModels;
using Ombi.Core.Tv;
using Ombi.Helpers;
using Ombi.Helpers.Analytics;
using Ombi.Helpers.Permissions;
@ -1119,10 +1120,12 @@ namespace Ombi.UI.Modules
RequestedUsers = new List<string> { Username },
Issues = IssueState.None,
ImdbId = showInfo.externals?.imdb ?? string.Empty,
SeasonCount = showInfo.Season.Count,
TvDbId = showId.ToString()
TvDbId = showId.ToString(),
ProviderId = showId
};
var totalSeasons = showInfo.Season.GroupBy(x => x.SeasonNumber);
model.SeasonCount = totalSeasons.Count();
var seasonsList = new List<int>();
switch (seasons)
{
@ -1884,7 +1887,8 @@ namespace Ombi.UI.Modules
{
model.Approved = true;
var s = await sonarrSettings;
var sender = new TvSenderOld(SonarrApi, SickrageApi, Cache); // TODO put back
var sender = new TvSenderV2(SonarrApi, SickrageApi, Cache);
//var sender = new TvSenderOld(SonarrApi, SickrageApi, Cache);
if (s.Enabled)
{
var result = await sender.SendToSonarr(s, model);

@ -78,6 +78,14 @@
<a href="https://github.com/tidusjar/Ombi" target="_blank">https://github.com/tidusjar/Ombi</a>
</td>
</tr>
<tr>
<td>
Forums
</td>
<td>
<a href="https://forums.ombi.io/" target="_blank">https://forums.ombi.io/</a>
</td>
</tr>
<tr>
<td>
Wiki

@ -33,7 +33,7 @@
<div class="form-group">
<div>
<button id="recentlyAddedBtn" class="btn btn-primary-outline">Send test email to Admin @Html.ToolTip("Note: nothing will send if there is no new content...")
<button id="recentlyAddedBtn" class="btn btn-primary-outline">Send test email to Admin @Html.ToolTip("Note: If there is nothing new when testing this, we will just grab some random titles. If there are new items, then we will show those new items. Testing will not mark the content as 'Previously Sent'")
<div id="testEmailSpinner"></div></button>
</div>
</div>

@ -16,6 +16,10 @@ ___
|----------|:---------------------------:|:----------------------------:|:----------------------------:|
| AppVeyor | [![Build status](https://ci.appveyor.com/api/projects/status/hgj8j6lcea7j0yhn/branch/master?svg=true)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/master) | [![Build status](https://ci.appveyor.com/api/projects/status/hgj8j6lcea7j0yhn/branch/eap?svg=true)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/eap) | [![Build status](https://ci.appveyor.com/api/projects/status/hgj8j6lcea7j0yhn/branch/dev?svg=true)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/dev)
| Download |[![Download](http://i.imgur.com/odToka3.png)](https://github.com/tidusjar/Ombi/releases) | [![Download](http://i.imgur.com/odToka3.png)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/eap/artifacts) | [![Download](http://i.imgur.com/odToka3.png)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/dev/artifacts) |
We now have a forums!
Check it out: [https://forums.ombi.io/](https://forums.ombi.io)
# Features
Here some of the features Ombi has:
* All your users to Request Movies, TV Shows (Whole series, whole seasons or even single episodes!) and Albums

@ -3,9 +3,9 @@ configuration: Release
assembly_info:
patch: true
file: '**\AssemblyInfo.*'
assembly_version: '2.2.0'
assembly_version: '2.2.1'
assembly_file_version: '{version}'
assembly_informational_version: '2.2.0'
assembly_informational_version: '2.2.1'
before_build:
- cmd: appveyor-retry nuget restore
build:

Loading…
Cancel
Save