mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-14 01:02:57 -07:00
Sickrage done. Ish... So i've written all the code by looking at the API. the key there is i've looked at the api. I have not tested anything so expect this to fail.
This commit is contained in:
parent
62a3ed924b
commit
a1ee52daef
26 changed files with 731 additions and 430 deletions
|
@ -6,12 +6,14 @@ namespace Ombi.Api.SickRage
|
|||
public interface ISickRageApi
|
||||
{
|
||||
Task<SickRageTvAdd> AddSeason(int tvdbId, int season, string apiKey, string baseUrl);
|
||||
Task<SickRageTvAdd> AddSeries(int tvdbId, int seasonCount, int[] seasons, string quality, string apiKey, string baseUrl);
|
||||
Task<SickRageTvAdd> AddSeries(int tvdbId, string quality, string status, string apiKey, string baseUrl);
|
||||
Task<SickRageShows> GetShows(string apiKey, string baseUrl);
|
||||
Task<SickRagePing> Ping(string apiKey, string baseUrl);
|
||||
Task<SickRageSeasonList> VerifyShowHasLoaded(int tvdbId, string apiKey, string baseUrl);
|
||||
|
||||
Task<SickRageShowInformation> GetShow(int tvdbid, string apikey, string baseUrl);
|
||||
Task<SickRageEpisodeStatus> SetEpisodeStatus(string apiKey, string baseUrl, int tvdbid, string status,
|
||||
int season, int episode = -1);
|
||||
Task<SickRageEpisodes> GetEpisodesForSeason(int tvdbid, int season, string apikey, string baseUrl);
|
||||
Task<SeasonList> GetSeasonList(int tvdbId, string apikey, string baseurl);
|
||||
}
|
||||
}
|
9
src/Ombi.Api.SickRage/Models/SeasonList.cs
Normal file
9
src/Ombi.Api.SickRage/Models/SeasonList.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Api.SickRage.Models
|
||||
{
|
||||
public class SeasonList : SickRageBase<List<int>>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
18
src/Ombi.Api.SickRage/Models/SickRageEpisodesData.cs
Normal file
18
src/Ombi.Api.SickRage/Models/SickRageEpisodesData.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Api.SickRage.Models
|
||||
{
|
||||
public class SickRageEpisodes : SickRageBase<Dictionary<int, SickRageEpisodesData>>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class SickRageEpisodesData
|
||||
{
|
||||
public string airdate { get; set; }
|
||||
public string name { get; set; }
|
||||
public string quality { get; set; }
|
||||
public string status { get; set; }
|
||||
}
|
||||
|
||||
}
|
41
src/Ombi.Api.SickRage/Models/SickRageShowInformation.cs
Normal file
41
src/Ombi.Api.SickRage/Models/SickRageShowInformation.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
namespace Ombi.Api.SickRage.Models
|
||||
{
|
||||
public class SickRageShowInformation : SickRageBase<SickRageShowInformationData>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class SickRageShowInformationData
|
||||
{
|
||||
public int air_by_date { get; set; }
|
||||
public string airs { get; set; }
|
||||
public Cache cache { get; set; }
|
||||
public int flatten_folders { get; set; }
|
||||
public string[] genre { get; set; }
|
||||
public string language { get; set; }
|
||||
public string location { get; set; }
|
||||
public string network { get; set; }
|
||||
public string next_ep_airdate { get; set; }
|
||||
public int paused { get; set; }
|
||||
public string quality { get; set; }
|
||||
public Quality_Details quality_details { get; set; }
|
||||
public int[] season_list { get; set; }
|
||||
public string show_name { get; set; }
|
||||
public string status { get; set; }
|
||||
public int tvrage_id { get; set; }
|
||||
public string tvrage_name { get; set; }
|
||||
}
|
||||
|
||||
public class Cache
|
||||
{
|
||||
public int banner { get; set; }
|
||||
public int poster { get; set; }
|
||||
}
|
||||
|
||||
public class Quality_Details
|
||||
{
|
||||
public object[] archive { get; set; }
|
||||
public string[] initial { get; set; }
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
@ -27,15 +25,12 @@ namespace Ombi.Api.SickRage
|
|||
return await _api.Request<SickRageSeasonList>(request);
|
||||
}
|
||||
|
||||
public async Task<SickRageTvAdd> AddSeries(int tvdbId, int seasonCount, int[] seasons, string quality, string apiKey, string baseUrl)
|
||||
public async Task<SickRageTvAdd> AddSeries(int tvdbId, string quality, string status, string apiKey, string baseUrl)
|
||||
{
|
||||
var futureStatus = seasons.Length > 0 && seasons.All(x => x != seasonCount) ? SickRageStatus.Skipped : SickRageStatus.Wanted;
|
||||
var status = seasons.Length > 0 ? SickRageStatus.Skipped : SickRageStatus.Wanted;
|
||||
var request = new Request($"/api/{apiKey}/?cmd=show.addnew", baseUrl, HttpMethod.Get);
|
||||
|
||||
request.AddQueryString("tvdbid", tvdbId.ToString());
|
||||
request.AddQueryString("status", status);
|
||||
request.AddQueryString("future_status", futureStatus);
|
||||
|
||||
if (!quality.Equals("default", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
|
@ -44,55 +39,6 @@ namespace Ombi.Api.SickRage
|
|||
|
||||
var obj = await _api.Request<SickRageTvAdd>(request);
|
||||
|
||||
if (obj.result != "failure")
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
var seasonIncrement = 0;
|
||||
try
|
||||
{
|
||||
while (seasonIncrement < seasonCount)
|
||||
{
|
||||
var seasonList = await VerifyShowHasLoaded(tvdbId, apiKey, baseUrl);
|
||||
if (seasonList.result.Equals("failure"))
|
||||
{
|
||||
await Task.Delay(3000);
|
||||
continue;
|
||||
}
|
||||
seasonIncrement = seasonList.Data?.Length ?? 0;
|
||||
|
||||
if (sw.ElapsedMilliseconds > 30000) // Break out after 30 seconds, it's not going to get added
|
||||
{
|
||||
_log.LogWarning("Couldn't find out if the show had been added after 10 seconds. I doubt we can change the status to wanted.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
sw.Stop();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.LogCritical(e, "Exception thrown when getting the seasonList");
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (seasons.Length > 0)
|
||||
{
|
||||
//handle the seasons requested
|
||||
foreach (var s in seasons)
|
||||
{
|
||||
var result = await AddSeason(tvdbId, s, apiKey, baseUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.LogCritical(e, "Exception when adding seasons:");
|
||||
throw;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -113,6 +59,23 @@ namespace Ombi.Api.SickRage
|
|||
return await _api.Request<SickRageShows>(request);
|
||||
}
|
||||
|
||||
public async Task<SickRageShowInformation> GetShow(int tvdbid, string apikey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"/api/{apikey}/?cmd=show", baseUrl, HttpMethod.Get);
|
||||
request.AddQueryString("tvdbid", tvdbid.ToString());
|
||||
|
||||
return await _api.Request<SickRageShowInformation>(request);
|
||||
}
|
||||
|
||||
public async Task<SickRageEpisodes> GetEpisodesForSeason(int tvdbid, int season, string apikey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"/api/{apikey}/?cmd=show.seasons", baseUrl, HttpMethod.Get);
|
||||
request.AddQueryString("tvdbid", tvdbid.ToString());
|
||||
request.AddQueryString("season", season.ToString());
|
||||
|
||||
return await _api.Request<SickRageEpisodes>(request);
|
||||
}
|
||||
|
||||
public async Task<SickRagePing> Ping(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"/api/{apiKey}/?cmd=sb.ping", baseUrl, HttpMethod.Get);
|
||||
|
@ -139,5 +102,13 @@ namespace Ombi.Api.SickRage
|
|||
|
||||
return await _api.Request<SickRageEpisodeStatus>(request);
|
||||
}
|
||||
|
||||
public async Task<SeasonList> GetSeasonList(int tvdbId, string apikey, string baseurl)
|
||||
{
|
||||
var request = new Request($"/api/{apikey}/?cmd=show.seasonlist", baseurl, HttpMethod.Get);
|
||||
request.AddQueryString("tvdbid", tvdbId.ToString());
|
||||
|
||||
return await _api.Request<SeasonList>(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,308 +1,363 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.DogNzb;
|
||||
using Ombi.Api.DogNzb.Models;
|
||||
using Ombi.Api.SickRage;
|
||||
using Ombi.Api.Sonarr;
|
||||
using Ombi.Api.Sonarr.Models;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models.External;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
|
||||
namespace Ombi.Core.Senders
|
||||
{
|
||||
public class TvSender : ITvSender
|
||||
{
|
||||
public TvSender(ISonarrApi sonarrApi, ILogger<TvSender> log, ISettingsService<SonarrSettings> sonarrSettings,
|
||||
ISettingsService<DogNzbSettings> dog, IDogNzbApi dogApi, ISettingsService<SickRageSettings> srSettings,
|
||||
ISickRageApi srApi)
|
||||
{
|
||||
SonarrApi = sonarrApi;
|
||||
Logger = log;
|
||||
SonarrSettings = sonarrSettings;
|
||||
DogNzbSettings = dog;
|
||||
DogNzbApi = dogApi;
|
||||
SickRageSettings = srSettings;
|
||||
SickRageApi = srApi;
|
||||
}
|
||||
|
||||
private ISonarrApi SonarrApi { get; }
|
||||
private IDogNzbApi DogNzbApi { get; }
|
||||
private ISickRageApi SickRageApi { get; }
|
||||
private ILogger<TvSender> Logger { get; }
|
||||
private ISettingsService<SonarrSettings> SonarrSettings { get; }
|
||||
private ISettingsService<DogNzbSettings> DogNzbSettings { get; }
|
||||
private ISettingsService<SickRageSettings> SickRageSettings { get; }
|
||||
|
||||
public async Task<SenderResult> Send(ChildRequests model)
|
||||
{
|
||||
var sonarr = await SonarrSettings.GetSettingsAsync();
|
||||
if (sonarr.Enabled)
|
||||
{
|
||||
var result = await SendToSonarr(model);
|
||||
if (result != null)
|
||||
{
|
||||
return new SenderResult
|
||||
{
|
||||
Sent = true,
|
||||
Success = true
|
||||
};
|
||||
}
|
||||
}
|
||||
var dog = await DogNzbSettings.GetSettingsAsync();
|
||||
if (dog.Enabled)
|
||||
{
|
||||
var result = await SendToDogNzb(model, dog);
|
||||
if (!result.Failure)
|
||||
{
|
||||
return new SenderResult
|
||||
{
|
||||
Sent = true,
|
||||
Success = true
|
||||
};
|
||||
}
|
||||
return new SenderResult
|
||||
{
|
||||
Message = result.ErrorMessage
|
||||
};
|
||||
}
|
||||
return new SenderResult
|
||||
{
|
||||
Success = true
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<DogNzbAddResult> SendToDogNzb(ChildRequests model, DogNzbSettings settings)
|
||||
{
|
||||
var id = model.ParentRequest.TvDbId;
|
||||
return await DogNzbApi.AddTvShow(settings.ApiKey, id.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send the request to Sonarr to process
|
||||
/// </summary>
|
||||
/// <param name="s"></param>
|
||||
/// <param name="model"></param>
|
||||
/// <param name="qualityId">This is for any qualities overriden from the UI</param>
|
||||
/// <returns></returns>
|
||||
public async Task<NewSeries> SendToSonarr(ChildRequests model, string qualityId = null)
|
||||
{
|
||||
var s = await SonarrSettings.GetSettingsAsync();
|
||||
if (!s.Enabled)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if(string.IsNullOrEmpty(s.ApiKey))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
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(s.QualityProfile, out qualityProfile);
|
||||
}
|
||||
|
||||
// Get the root path from the rootfolder selected.
|
||||
// For some reason, if we haven't got one use the first root folder in Sonarr
|
||||
// TODO make this overrideable via the UI
|
||||
var rootFolderPath = await GetSonarrRootPath(model.ParentRequest.RootFolder ?? int.Parse(s.RootPath), s);
|
||||
try
|
||||
{
|
||||
// Does the series actually exist?
|
||||
var allSeries = await SonarrApi.GetSeries(s.ApiKey, s.FullUri);
|
||||
var existingSeries = allSeries.FirstOrDefault(x => x.tvdbId == model.ParentRequest.TvDbId);
|
||||
|
||||
if (existingSeries == null)
|
||||
{
|
||||
// Time to add a new one
|
||||
var newSeries = new NewSeries
|
||||
{
|
||||
title = model.ParentRequest.Title,
|
||||
imdbId = model.ParentRequest.ImdbId,
|
||||
tvdbId = model.ParentRequest.TvDbId,
|
||||
cleanTitle = model.ParentRequest.Title,
|
||||
monitored = true,
|
||||
seasonFolder = s.SeasonFolders,
|
||||
rootFolderPath = rootFolderPath,
|
||||
qualityProfileId = qualityProfile,
|
||||
titleSlug = model.ParentRequest.Title,
|
||||
addOptions = new AddOptions
|
||||
{
|
||||
ignoreEpisodesWithFiles = true, // There shouldn't be any episodes with files, this is a new season
|
||||
ignoreEpisodesWithoutFiles = true, // We want all missing
|
||||
searchForMissingEpisodes = false // we want dont want to search yet. We want to make sure everything is unmonitored/monitored correctly.
|
||||
}
|
||||
};
|
||||
|
||||
// Montitor the correct seasons,
|
||||
// If we have that season in the model then it's monitored!
|
||||
var seasonsToAdd = new List<Season>();
|
||||
for (var i = 1; i < model.ParentRequest.TotalSeasons + 1; i++)
|
||||
{
|
||||
var index = i;
|
||||
var season = new Season
|
||||
{
|
||||
seasonNumber = i,
|
||||
monitored = model.SeasonRequests.Any(x => x.SeasonNumber == index)
|
||||
};
|
||||
seasonsToAdd.Add(season);
|
||||
}
|
||||
newSeries.seasons = seasonsToAdd;
|
||||
var result = await SonarrApi.AddSeries(newSeries, s.ApiKey, s.FullUri);
|
||||
existingSeries = await SonarrApi.GetSeriesById(result.id, s.ApiKey, s.FullUri);
|
||||
await SendToSonarr(model, existingSeries, s);
|
||||
}
|
||||
else
|
||||
{
|
||||
await SendToSonarr(model, existingSeries, s);
|
||||
}
|
||||
|
||||
return new NewSeries
|
||||
{
|
||||
id = existingSeries.id,
|
||||
seasons = existingSeries.seasons.ToList(),
|
||||
cleanTitle = existingSeries.cleanTitle,
|
||||
title = existingSeries.title,
|
||||
tvdbId = existingSeries.tvdbId
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError(LoggingEvents.SonarrSender, e, "Exception thrown when attempting to send series over to Sonarr");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendToSonarr(ChildRequests model, SonarrSeries result, SonarrSettings s)
|
||||
{
|
||||
var episodesToUpdate = new List<Episode>();
|
||||
// Ok, now let's sort out the episodes.
|
||||
|
||||
var sonarrEpisodes = await SonarrApi.GetEpisodes(result.id, s.ApiKey, s.FullUri);
|
||||
var sonarrEpList = sonarrEpisodes.ToList() ?? new List<Episode>();
|
||||
while (!sonarrEpList.Any())
|
||||
{
|
||||
// It could be that the series metadata is not ready yet. So wait
|
||||
sonarrEpList = (await SonarrApi.GetEpisodes(result.id, s.ApiKey, s.FullUri)).ToList();
|
||||
await Task.Delay(500);
|
||||
}
|
||||
|
||||
|
||||
foreach (var req in model.SeasonRequests)
|
||||
{
|
||||
foreach (var ep in req.Episodes)
|
||||
{
|
||||
var sonarrEp = sonarrEpList.FirstOrDefault(x =>
|
||||
x.episodeNumber == ep.EpisodeNumber && x.seasonNumber == req.SeasonNumber);
|
||||
if (sonarrEp != null)
|
||||
{
|
||||
sonarrEp.monitored = true;
|
||||
episodesToUpdate.Add(sonarrEp);
|
||||
}
|
||||
}
|
||||
}
|
||||
var seriesChanges = false;
|
||||
foreach (var season in model.SeasonRequests)
|
||||
{
|
||||
var sonarrSeason = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber);
|
||||
var sonarrEpCount = sonarrSeason.Count();
|
||||
var ourRequestCount = season.Episodes.Count;
|
||||
|
||||
if (sonarrEpCount == ourRequestCount)
|
||||
{
|
||||
// We have the same amount of requests as all of the episodes in the season.
|
||||
var existingSeason =
|
||||
result.seasons.First(x => x.seasonNumber == season.SeasonNumber);
|
||||
existingSeason.monitored = true;
|
||||
seriesChanges = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Now update the episodes that need updating
|
||||
foreach (var epToUpdate in episodesToUpdate.Where(x => x.seasonNumber == season.SeasonNumber))
|
||||
{
|
||||
await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (seriesChanges)
|
||||
{
|
||||
await SonarrApi.SeasonPass(s.ApiKey, s.FullUri, result);
|
||||
}
|
||||
|
||||
|
||||
if (!s.AddOnly)
|
||||
{
|
||||
await SearchForRequest(model, sonarrEpList, result, s, episodesToUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendToSickRage(ChildRequests model, string qualityId = null)
|
||||
{
|
||||
var settings = await SickRageSettings.GetSettingsAsync();
|
||||
if (qualityId.HasValue())
|
||||
{
|
||||
if (settings.Qualities.All(x => x.Key != qualityId))
|
||||
{
|
||||
qualityId = settings.QualityProfile;
|
||||
}
|
||||
}
|
||||
|
||||
//var apiResult = SickRageApi.AddSeries(model.ParentRequest.TvDbId, model.SeasonCount, model.SeasonList, qualityId,
|
||||
// sickRageSettings.ApiKey, sickRageSettings.FullUri);
|
||||
|
||||
//var result = apiResult.Result;
|
||||
|
||||
|
||||
//return result;
|
||||
}
|
||||
|
||||
private async Task SearchForRequest(ChildRequests model, IEnumerable<Episode> sonarrEpList, SonarrSeries existingSeries, SonarrSettings s,
|
||||
IReadOnlyCollection<Episode> episodesToUpdate)
|
||||
{
|
||||
foreach (var season in model.SeasonRequests)
|
||||
{
|
||||
var sonarrSeason = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber);
|
||||
var sonarrEpCount = sonarrSeason.Count();
|
||||
var ourRequestCount = season.Episodes.Count;
|
||||
|
||||
if (sonarrEpCount == ourRequestCount)
|
||||
{
|
||||
// We have the same amount of requests as all of the episodes in the season.
|
||||
// Do a season search
|
||||
await SonarrApi.SeasonSearch(existingSeries.id, season.SeasonNumber, s.ApiKey, s.FullUri);
|
||||
}
|
||||
else
|
||||
{
|
||||
// There is a miss-match, let's search the episodes indiviaully
|
||||
await SonarrApi.EpisodeSearch(episodesToUpdate.Select(x => x.id).ToArray(), s.ApiKey, s.FullUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> GetSonarrRootPath(int pathId, SonarrSettings sonarrSettings)
|
||||
{
|
||||
var rootFoldersResult = await SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri);
|
||||
|
||||
if (pathId == 0)
|
||||
{
|
||||
return rootFoldersResult.FirstOrDefault().path;
|
||||
}
|
||||
|
||||
foreach (var r in rootFoldersResult.Where(r => r.id == pathId))
|
||||
{
|
||||
return r.path;
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.DogNzb;
|
||||
using Ombi.Api.DogNzb.Models;
|
||||
using Ombi.Api.SickRage;
|
||||
using Ombi.Api.SickRage.Models;
|
||||
using Ombi.Api.Sonarr;
|
||||
using Ombi.Api.Sonarr.Models;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models.External;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
|
||||
namespace Ombi.Core.Senders
|
||||
{
|
||||
public class TvSender : ITvSender
|
||||
{
|
||||
public TvSender(ISonarrApi sonarrApi, ILogger<TvSender> log, ISettingsService<SonarrSettings> sonarrSettings,
|
||||
ISettingsService<DogNzbSettings> dog, IDogNzbApi dogApi, ISettingsService<SickRageSettings> srSettings,
|
||||
ISickRageApi srApi)
|
||||
{
|
||||
SonarrApi = sonarrApi;
|
||||
Logger = log;
|
||||
SonarrSettings = sonarrSettings;
|
||||
DogNzbSettings = dog;
|
||||
DogNzbApi = dogApi;
|
||||
SickRageSettings = srSettings;
|
||||
SickRageApi = srApi;
|
||||
}
|
||||
|
||||
private ISonarrApi SonarrApi { get; }
|
||||
private IDogNzbApi DogNzbApi { get; }
|
||||
private ISickRageApi SickRageApi { get; }
|
||||
private ILogger<TvSender> Logger { get; }
|
||||
private ISettingsService<SonarrSettings> SonarrSettings { get; }
|
||||
private ISettingsService<DogNzbSettings> DogNzbSettings { get; }
|
||||
private ISettingsService<SickRageSettings> SickRageSettings { get; }
|
||||
|
||||
public async Task<SenderResult> Send(ChildRequests model)
|
||||
{
|
||||
var sonarr = await SonarrSettings.GetSettingsAsync();
|
||||
if (sonarr.Enabled)
|
||||
{
|
||||
var result = await SendToSonarr(model);
|
||||
if (result != null)
|
||||
{
|
||||
return new SenderResult
|
||||
{
|
||||
Sent = true,
|
||||
Success = true
|
||||
};
|
||||
}
|
||||
}
|
||||
var dog = await DogNzbSettings.GetSettingsAsync();
|
||||
if (dog.Enabled)
|
||||
{
|
||||
var result = await SendToDogNzb(model, dog);
|
||||
if (!result.Failure)
|
||||
{
|
||||
return new SenderResult
|
||||
{
|
||||
Sent = true,
|
||||
Success = true
|
||||
};
|
||||
}
|
||||
return new SenderResult
|
||||
{
|
||||
Message = result.ErrorMessage
|
||||
};
|
||||
}
|
||||
var sr = await SickRageSettings.GetSettingsAsync();
|
||||
if (sr.Enabled)
|
||||
{
|
||||
var result = await SendToSickRage(model, sr);
|
||||
if (result)
|
||||
{
|
||||
return new SenderResult
|
||||
{
|
||||
Sent = true,
|
||||
Success = true
|
||||
};
|
||||
}
|
||||
return new SenderResult();
|
||||
}
|
||||
return new SenderResult
|
||||
{
|
||||
Success = true
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<DogNzbAddResult> SendToDogNzb(ChildRequests model, DogNzbSettings settings)
|
||||
{
|
||||
var id = model.ParentRequest.TvDbId;
|
||||
return await DogNzbApi.AddTvShow(settings.ApiKey, id.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send the request to Sonarr to process
|
||||
/// </summary>
|
||||
/// <param name="s"></param>
|
||||
/// <param name="model"></param>
|
||||
/// <param name="qualityId">This is for any qualities overriden from the UI</param>
|
||||
/// <returns></returns>
|
||||
public async Task<NewSeries> SendToSonarr(ChildRequests model, string qualityId = null)
|
||||
{
|
||||
var s = await SonarrSettings.GetSettingsAsync();
|
||||
if (!s.Enabled)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if(string.IsNullOrEmpty(s.ApiKey))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
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(s.QualityProfile, out qualityProfile);
|
||||
}
|
||||
|
||||
// Get the root path from the rootfolder selected.
|
||||
// For some reason, if we haven't got one use the first root folder in Sonarr
|
||||
// TODO make this overrideable via the UI
|
||||
var rootFolderPath = await GetSonarrRootPath(model.ParentRequest.RootFolder ?? int.Parse(s.RootPath), s);
|
||||
try
|
||||
{
|
||||
// Does the series actually exist?
|
||||
var allSeries = await SonarrApi.GetSeries(s.ApiKey, s.FullUri);
|
||||
var existingSeries = allSeries.FirstOrDefault(x => x.tvdbId == model.ParentRequest.TvDbId);
|
||||
|
||||
if (existingSeries == null)
|
||||
{
|
||||
// Time to add a new one
|
||||
var newSeries = new NewSeries
|
||||
{
|
||||
title = model.ParentRequest.Title,
|
||||
imdbId = model.ParentRequest.ImdbId,
|
||||
tvdbId = model.ParentRequest.TvDbId,
|
||||
cleanTitle = model.ParentRequest.Title,
|
||||
monitored = true,
|
||||
seasonFolder = s.SeasonFolders,
|
||||
rootFolderPath = rootFolderPath,
|
||||
qualityProfileId = qualityProfile,
|
||||
titleSlug = model.ParentRequest.Title,
|
||||
addOptions = new AddOptions
|
||||
{
|
||||
ignoreEpisodesWithFiles = true, // There shouldn't be any episodes with files, this is a new season
|
||||
ignoreEpisodesWithoutFiles = true, // We want all missing
|
||||
searchForMissingEpisodes = false // we want dont want to search yet. We want to make sure everything is unmonitored/monitored correctly.
|
||||
}
|
||||
};
|
||||
|
||||
// Montitor the correct seasons,
|
||||
// If we have that season in the model then it's monitored!
|
||||
var seasonsToAdd = new List<Season>();
|
||||
for (var i = 1; i < model.ParentRequest.TotalSeasons + 1; i++)
|
||||
{
|
||||
var index = i;
|
||||
var season = new Season
|
||||
{
|
||||
seasonNumber = i,
|
||||
monitored = model.SeasonRequests.Any(x => x.SeasonNumber == index)
|
||||
};
|
||||
seasonsToAdd.Add(season);
|
||||
}
|
||||
newSeries.seasons = seasonsToAdd;
|
||||
var result = await SonarrApi.AddSeries(newSeries, s.ApiKey, s.FullUri);
|
||||
existingSeries = await SonarrApi.GetSeriesById(result.id, s.ApiKey, s.FullUri);
|
||||
await SendToSonarr(model, existingSeries, s);
|
||||
}
|
||||
else
|
||||
{
|
||||
await SendToSonarr(model, existingSeries, s);
|
||||
}
|
||||
|
||||
return new NewSeries
|
||||
{
|
||||
id = existingSeries.id,
|
||||
seasons = existingSeries.seasons.ToList(),
|
||||
cleanTitle = existingSeries.cleanTitle,
|
||||
title = existingSeries.title,
|
||||
tvdbId = existingSeries.tvdbId
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError(LoggingEvents.SonarrSender, e, "Exception thrown when attempting to send series over to Sonarr");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendToSonarr(ChildRequests model, SonarrSeries result, SonarrSettings s)
|
||||
{
|
||||
var episodesToUpdate = new List<Episode>();
|
||||
// Ok, now let's sort out the episodes.
|
||||
|
||||
var sonarrEpisodes = await SonarrApi.GetEpisodes(result.id, s.ApiKey, s.FullUri);
|
||||
var sonarrEpList = sonarrEpisodes.ToList() ?? new List<Episode>();
|
||||
while (!sonarrEpList.Any())
|
||||
{
|
||||
// It could be that the series metadata is not ready yet. So wait
|
||||
sonarrEpList = (await SonarrApi.GetEpisodes(result.id, s.ApiKey, s.FullUri)).ToList();
|
||||
await Task.Delay(500);
|
||||
}
|
||||
|
||||
|
||||
foreach (var req in model.SeasonRequests)
|
||||
{
|
||||
foreach (var ep in req.Episodes)
|
||||
{
|
||||
var sonarrEp = sonarrEpList.FirstOrDefault(x =>
|
||||
x.episodeNumber == ep.EpisodeNumber && x.seasonNumber == req.SeasonNumber);
|
||||
if (sonarrEp != null)
|
||||
{
|
||||
sonarrEp.monitored = true;
|
||||
episodesToUpdate.Add(sonarrEp);
|
||||
}
|
||||
}
|
||||
}
|
||||
var seriesChanges = false;
|
||||
foreach (var season in model.SeasonRequests)
|
||||
{
|
||||
var sonarrSeason = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber);
|
||||
var sonarrEpCount = sonarrSeason.Count();
|
||||
var ourRequestCount = season.Episodes.Count;
|
||||
|
||||
if (sonarrEpCount == ourRequestCount)
|
||||
{
|
||||
// We have the same amount of requests as all of the episodes in the season.
|
||||
var existingSeason =
|
||||
result.seasons.First(x => x.seasonNumber == season.SeasonNumber);
|
||||
existingSeason.monitored = true;
|
||||
seriesChanges = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Now update the episodes that need updating
|
||||
foreach (var epToUpdate in episodesToUpdate.Where(x => x.seasonNumber == season.SeasonNumber))
|
||||
{
|
||||
await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (seriesChanges)
|
||||
{
|
||||
await SonarrApi.SeasonPass(s.ApiKey, s.FullUri, result);
|
||||
}
|
||||
|
||||
|
||||
if (!s.AddOnly)
|
||||
{
|
||||
await SearchForRequest(model, sonarrEpList, result, s, episodesToUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> SendToSickRage(ChildRequests model, SickRageSettings settings, string qualityId = null)
|
||||
{
|
||||
var tvdbid = model.ParentRequest.TvDbId;
|
||||
if (qualityId.HasValue())
{
var id = qualityId;
if (settings.Qualities.All(x => x.Value != id))
{
qualityId = settings.QualityProfile;
}
}
|
||||
// Check if the show exists
|
||||
var existingShow = await SickRageApi.GetShow(tvdbid, settings.ApiKey, settings.FullUri);
|
||||
|
||||
if (existingShow == null)
|
||||
{
|
||||
var addResult = await SickRageApi.AddSeries(model.ParentRequest.TvDbId, SickRageStatus.Wanted,
|
||||
qualityId,
|
||||
settings.ApiKey, settings.FullUri);
|
||||
|
||||
Logger.LogDebug("Added the show (tvdbid) {0}. The result is '{2}' : '{3}'", tvdbid, addResult.result, addResult.message);
|
||||
if (addResult.result.Equals("failure"))
|
||||
{
|
||||
// Do something
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var seasonRequests in model.SeasonRequests)
|
||||
{
|
||||
var srEpisodes = await SickRageApi.GetEpisodesForSeason(tvdbid, seasonRequests.SeasonNumber, settings.ApiKey, settings.FullUri);
|
||||
var totalSrEpisodes = srEpisodes.data.Count;
|
||||
|
||||
if (totalSrEpisodes == seasonRequests.Episodes.Count)
|
||||
{
|
||||
// This is a request for the whole season
|
||||
var wholeSeasonResult = await SickRageApi.SetEpisodeStatus(settings.ApiKey, settings.FullUri, tvdbid, SickRageStatus.Wanted,
|
||||
seasonRequests.SeasonNumber);
|
||||
|
||||
Logger.LogDebug("Set the status to Wanted for season {0}. The result is '{1}' : '{2}'", seasonRequests.SeasonNumber, wholeSeasonResult.result, wholeSeasonResult.message);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var srEp in srEpisodes.data)
|
||||
{
|
||||
var epNumber = srEp.Key;
|
||||
var epData = srEp.Value;
|
||||
|
||||
var epRequest = seasonRequests.Episodes.FirstOrDefault(x => x.EpisodeNumber == epNumber);
|
||||
if (epRequest != null)
|
||||
{
|
||||
// We want to monior this episode since we have a request for it
|
||||
// Let's check to see if it's wanted first, save an api call
|
||||
if (epData.status.Equals(SickRageStatus.Wanted, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var epResult = await SickRageApi.SetEpisodeStatus(settings.ApiKey, settings.FullUri, tvdbid,
|
||||
SickRageStatus.Wanted, seasonRequests.SeasonNumber, epNumber);
|
||||
|
||||
Logger.LogDebug("Set the status to Wanted for Episode {0} in season {1}. The result is '{2}' : '{3}'", seasonRequests.SeasonNumber, epNumber, epResult.result, epResult.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task SearchForRequest(ChildRequests model, IEnumerable<Episode> sonarrEpList, SonarrSeries existingSeries, SonarrSettings s,
|
||||
IReadOnlyCollection<Episode> episodesToUpdate)
|
||||
{
|
||||
foreach (var season in model.SeasonRequests)
|
||||
{
|
||||
var sonarrSeason = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber);
|
||||
var sonarrEpCount = sonarrSeason.Count();
|
||||
var ourRequestCount = season.Episodes.Count;
|
||||
|
||||
if (sonarrEpCount == ourRequestCount)
|
||||
{
|
||||
// We have the same amount of requests as all of the episodes in the season.
|
||||
// Do a season search
|
||||
await SonarrApi.SeasonSearch(existingSeries.id, season.SeasonNumber, s.ApiKey, s.FullUri);
|
||||
}
|
||||
else
|
||||
{
|
||||
// There is a miss-match, let's search the episodes indiviaully
|
||||
await SonarrApi.EpisodeSearch(episodesToUpdate.Select(x => x.id).ToArray(), s.ApiKey, s.FullUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> GetSonarrRootPath(int pathId, SonarrSettings sonarrSettings)
|
||||
{
|
||||
var rootFoldersResult = await SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri);
|
||||
|
||||
if (pathId == 0)
|
||||
{
|
||||
return rootFoldersResult.FirstOrDefault().path;
|
||||
}
|
||||
|
||||
foreach (var r in rootFoldersResult.Where(r => r.id == pathId))
|
||||
{
|
||||
return r.path;
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ namespace Ombi.Helpers
|
|||
public static EventId SonarrCacher => new EventId(2006);
|
||||
public static EventId CouchPotatoCacher => new EventId(2007);
|
||||
public static EventId PlexContentCacher => new EventId(2008);
|
||||
public static EventId SickRageCacher => new EventId(2009);
|
||||
|
||||
public static EventId MovieSender => new EventId(3000);
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ using Ombi.Schedule.Jobs.Emby;
|
|||
using Ombi.Schedule.Jobs.Ombi;
|
||||
using Ombi.Schedule.Jobs.Plex;
|
||||
using Ombi.Schedule.Jobs.Radarr;
|
||||
using Ombi.Schedule.Jobs.SickRage;
|
||||
using Ombi.Schedule.Jobs.Sonarr;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
|
||||
|
@ -16,7 +17,7 @@ namespace Ombi.Schedule
|
|||
public JobSetup(IPlexContentSync plexContentSync, IRadarrSync radarrSync,
|
||||
IOmbiAutomaticUpdater updater, IEmbyContentSync embySync, IPlexUserImporter userImporter,
|
||||
IEmbyUserImporter embyUserImporter, ISonarrSync cache, ICouchPotatoSync cpCache,
|
||||
ISettingsService<JobSettings> jobsettings)
|
||||
ISettingsService<JobSettings> jobsettings, ISickRageSync srSync)
|
||||
{
|
||||
PlexContentSync = plexContentSync;
|
||||
RadarrSync = radarrSync;
|
||||
|
@ -37,6 +38,7 @@ namespace Ombi.Schedule
|
|||
private IEmbyUserImporter EmbyUserImporter { get; }
|
||||
private ISonarrSync SonarrSync { get; }
|
||||
private ICouchPotatoSync CpCache { get; }
|
||||
private ISickRageSync SrSync { get; }
|
||||
private ISettingsService<JobSettings> JobSettings { get; set; }
|
||||
|
||||
public void Setup()
|
||||
|
@ -48,6 +50,7 @@ namespace Ombi.Schedule
|
|||
RecurringJob.AddOrUpdate(() => RadarrSync.CacheContent(), JobSettingsHelper.Radarr(s));
|
||||
RecurringJob.AddOrUpdate(() => PlexContentSync.CacheContent(), JobSettingsHelper.PlexContent(s));
|
||||
RecurringJob.AddOrUpdate(() => CpCache.Start(), JobSettingsHelper.CouchPotato(s));
|
||||
RecurringJob.AddOrUpdate(() => SrSync.Start(), JobSettingsHelper.SickRageSync(s));
|
||||
|
||||
RecurringJob.AddOrUpdate(() => Updater.Update(null), JobSettingsHelper.Updater(s));
|
||||
|
||||
|
|
9
src/Ombi.Schedule/Jobs/SickRage/ISickRageSync.cs
Normal file
9
src/Ombi.Schedule/Jobs/SickRage/ISickRageSync.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.SickRage
|
||||
{
|
||||
public interface ISickRageSync
|
||||
{
|
||||
Task Start();
|
||||
}
|
||||
}
|
92
src/Ombi.Schedule/Jobs/SickRage/SickRageSync.cs
Normal file
92
src/Ombi.Schedule/Jobs/SickRage/SickRageSync.cs
Normal file
|
@ -0,0 +1,92 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.SickRage;
|
||||
using Ombi.Api.SickRage.Models;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models.External;
|
||||
using Ombi.Store.Context;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.SickRage
|
||||
{
|
||||
public class SickRageSync : ISickRageSync
|
||||
{
|
||||
public SickRageSync(ISettingsService<SickRageSettings> s, ISickRageApi api, ILogger<SickRageSync> l, IOmbiContext ctx)
|
||||
{
|
||||
_settings = s;
|
||||
_api = api;
|
||||
_log = l;
|
||||
_ctx = ctx;
|
||||
_settings.ClearCache();
|
||||
}
|
||||
|
||||
private readonly ISettingsService<SickRageSettings> _settings;
|
||||
private readonly ISickRageApi _api;
|
||||
private readonly ILogger<SickRageSync> _log;
|
||||
private readonly IOmbiContext _ctx;
|
||||
|
||||
private static readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1, 1);
|
||||
public async Task Start()
|
||||
{
|
||||
await SemaphoreSlim.WaitAsync();
|
||||
try
|
||||
{
|
||||
var settings = await _settings.GetSettingsAsync();
|
||||
if (!settings.Enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var shows = await _api.GetShows(settings.ApiKey, settings.FullUri);
|
||||
if (shows != null)
|
||||
{
|
||||
var srShows = shows.data.Values;
|
||||
var ids = srShows.Select(x => x.tvdbid);
|
||||
|
||||
await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM SickRageCache");
|
||||
var entites = ids.Select(id => new SickRageCache { TvDbId = id }).ToList();
|
||||
|
||||
await _ctx.SickRageCache.AddRangeAsync(entites);
|
||||
|
||||
var episodesToAdd = new List<SickRageEpisodeCache>();
|
||||
await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM SickRageEpisodeCache");
|
||||
foreach (var s in srShows)
|
||||
{
|
||||
var seasons = await _api.GetSeasonList(s.tvdbid, settings.ApiKey, settings.FullUri);
|
||||
foreach (var season in seasons.data)
|
||||
{
|
||||
var episodes =
|
||||
await _api.GetEpisodesForSeason(s.tvdbid, season, settings.ApiKey, settings.FullUri);
|
||||
|
||||
var monitoredEpisodes = episodes.data.Where(x => x.Value.status.Equals(SickRageStatus.Wanted));
|
||||
|
||||
episodesToAdd.AddRange(monitoredEpisodes.Select(episode => new SickRageEpisodeCache
|
||||
{
|
||||
EpisodeNumber = episode.Key,
|
||||
SeasonNumber = season,
|
||||
TvDbId = s.tvdbid
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
await _ctx.SickRageEpisodeCache.AddRangeAsync(episodesToAdd);
|
||||
await _ctx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.LogError(LoggingEvents.SickRageCacher, e, "Exception when trying to cache SickRage");
|
||||
}
|
||||
finally
|
||||
{
|
||||
SemaphoreSlim.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@
|
|||
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Radarr\Ombi.Api.Radarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Service\Ombi.Api.Service.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.SickRage\Ombi.Api.SickRage.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Sonarr\Ombi.Api.Sonarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Notifications\Ombi.Notifications.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Ombi.Settings.Settings.Models.External
|
||||
{
|
||||
|
@ -8,19 +7,34 @@ namespace Ombi.Settings.Settings.Models.External
|
|||
public bool Enabled { get; set; }
|
||||
public string ApiKey { get; set; }
|
||||
public string QualityProfile { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Dictionary<string, string> Qualities => new Dictionary<string, string>
|
||||
|
||||
public List<DropDownModel> Qualities => new List<DropDownModel>
|
||||
{
|
||||
{ "default", "Use Default" },
|
||||
{ "sdtv", "SD TV" },
|
||||
{ "sddvd", "SD DVD" },
|
||||
{ "hdtv", "HD TV" },
|
||||
{ "rawhdtv", "Raw HD TV" },
|
||||
{ "hdwebdl", "HD Web DL" },
|
||||
{ "fullhdwebdl", "Full HD Web DL" },
|
||||
{ "hdbluray", "HD Bluray" },
|
||||
{ "fullhdbluray", "Full HD Bluray" }
|
||||
new DropDownModel("default", "Use Default"),
|
||||
new DropDownModel("sdtv", "SD TV"),
|
||||
new DropDownModel("sddvd", "SD DVD"),
|
||||
new DropDownModel("hdtv", "HD TV"),
|
||||
new DropDownModel("rawhdtv", "Raw HD TV"),
|
||||
new DropDownModel("hdwebdl", "HD Web DL"),
|
||||
new DropDownModel("fullhdwebdl", "Full HD Web DL"),
|
||||
new DropDownModel("hdbluray", "HD Bluray"),
|
||||
new DropDownModel("fullhdbluray", "Full HD Bluray"),
|
||||
};
|
||||
}
|
||||
|
||||
public class DropDownModel
|
||||
{
|
||||
public DropDownModel(string val, string display)
|
||||
{
|
||||
Value = val;
|
||||
Display = display;
|
||||
}
|
||||
|
||||
public DropDownModel()
|
||||
{
|
||||
|
||||
}
|
||||
public string Value { get; set; }
|
||||
public string Display { get; set; }
|
||||
}
|
||||
}
|
|
@ -9,5 +9,6 @@
|
|||
public string CouchPotatoSync { get; set; }
|
||||
public string AutomaticUpdater { get; set; }
|
||||
public string UserImporter { get; set; }
|
||||
public string SickRageSync { get; set; }
|
||||
}
|
||||
}
|
|
@ -35,6 +35,10 @@ namespace Ombi.Settings.Settings.Models
|
|||
{
|
||||
return Get(s.UserImporter, Cron.Daily());
|
||||
}
|
||||
public static string SickRageSync(JobSettings s)
|
||||
{
|
||||
return Get(s.SickRageSync, Cron.Hourly(35));
|
||||
}
|
||||
|
||||
|
||||
private static string Get(string settings, string defaultCron)
|
||||
|
|
|
@ -38,5 +38,7 @@ namespace Ombi.Store.Context
|
|||
EntityEntry Update(object entity);
|
||||
EntityEntry<TEntity> Update<TEntity>(TEntity entity) where TEntity : class;
|
||||
DbSet<CouchPotatoCache> CouchPotatoCache { get; set; }
|
||||
DbSet<SickRageCache> SickRageCache { get; set; }
|
||||
DbSet<SickRageEpisodeCache> SickRageEpisodeCache { get; set; }
|
||||
}
|
||||
}
|
|
@ -40,6 +40,8 @@ namespace Ombi.Store.Context
|
|||
public DbSet<Tokens> Tokens { get; set; }
|
||||
public DbSet<SonarrCache> SonarrCache { get; set; }
|
||||
public DbSet<SonarrEpisodeCache> SonarrEpisodeCache { get; set; }
|
||||
public DbSet<SickRageCache> SickRageCache { get; set; }
|
||||
public DbSet<SickRageEpisodeCache> SickRageEpisodeCache { get; set; }
|
||||
|
||||
public DbSet<ApplicationConfiguration> ApplicationConfigurations { get; set; }
|
||||
|
||||
|
|
10
src/Ombi.Store/Entities/SickRageCache.cs
Normal file
10
src/Ombi.Store/Entities/SickRageCache.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Ombi.Store.Entities
|
||||
{
|
||||
[Table("SickRageCache")]
|
||||
public class SickRageCache : Entity
|
||||
{
|
||||
public int TvDbId { get; set; }
|
||||
}
|
||||
}
|
12
src/Ombi.Store/Entities/SickRageEpisodeCache.cs
Normal file
12
src/Ombi.Store/Entities/SickRageEpisodeCache.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Ombi.Store.Entities
|
||||
{
|
||||
[Table("SickRageEpisodeCache")]
|
||||
public class SickRageEpisodeCache : Entity
|
||||
{
|
||||
public int SeasonNumber { get; set; }
|
||||
public int EpisodeNumber { get; set; }
|
||||
public int TvDbId { get; set; }
|
||||
}
|
||||
}
|
|
@ -26,7 +26,3 @@ export interface IUsersModel {
|
|||
id: string;
|
||||
username: string;
|
||||
}
|
||||
|
||||
export interface IDictionary<T> {
|
||||
[Key: string]: T;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { IDictionary, ISettings } from "./ICommon";
|
||||
import { ISettings } from "./ICommon";
|
||||
|
||||
export interface IExternalSettings extends ISettings {
|
||||
ssl: boolean;
|
||||
|
@ -115,6 +115,7 @@ export interface IJobSettings {
|
|||
couchPotatoSync: string;
|
||||
automaticUpdater: string;
|
||||
userImporter: string;
|
||||
sickRageSync: string;
|
||||
}
|
||||
|
||||
export interface IAuthenticationSettings extends ISettings {
|
||||
|
@ -158,7 +159,12 @@ export interface ISickRageSettings extends IExternalSettings {
|
|||
enabled: boolean;
|
||||
apiKey: string;
|
||||
qualityProfile: string;
|
||||
qualities: IDictionary<string>;
|
||||
qualities: IDropDownModel[];
|
||||
}
|
||||
|
||||
export interface IDropDownModel {
|
||||
value: string;
|
||||
display: string;
|
||||
}
|
||||
|
||||
export interface IDogNzbSettings extends ISettings {
|
||||
|
|
|
@ -13,6 +13,11 @@
|
|||
<input type="text" class="form-control form-control-custom" [ngClass]="{'form-error': form.get('sonarrSync').hasError('required')}" id="sonarrSync" name="sonarrSync" formControlName="sonarrSync">
|
||||
<small *ngIf="form.get('sonarrSync').hasError('required')" class="error-text">The Sonarr Sync is required</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="sickRageSync" class="control-label">SickRage Sync</label>
|
||||
<input type="text" class="form-control form-control-custom" [ngClass]="{'form-error': form.get('sonarrSync').hasError('required')}" id="sickRageSync" name="sickRageSync" formControlName="sickRageSync">
|
||||
<small *ngIf="form.get('sickRageSync').hasError('required')" class="error-text">The SickRage Sync is required</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="radarrSync" class="control-label">Radarr Sync</label>
|
||||
|
|
|
@ -23,8 +23,9 @@ export class JobsComponent implements OnInit {
|
|||
embyContentSync: [x.embyContentSync, Validators.required],
|
||||
plexContentSync: [x.plexContentSync, Validators.required],
|
||||
userImporter: [x.userImporter, Validators.required],
|
||||
sonarrSync: [x.radarrSync, Validators.required],
|
||||
sonarrSync: [x.radarrSync, Validators.required],
|
||||
radarrSync: [x.sonarrSync, Validators.required],
|
||||
sickRageSync: [x.sickRageSync, Validators.required],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -30,8 +30,7 @@
|
|||
<ul class="dropdown-menu">
|
||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Sonarr']">Sonarr</a></li>
|
||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/DogNzb']">DogNzb</a></li>
|
||||
<li [routerLinkActive]="['active']"><a>More Coming Soon...</a></li>
|
||||
<!--<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/SickRage']">SickRage</a></li>-->
|
||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/SickRage']">SickRage</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
@ -44,7 +43,6 @@
|
|||
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/DogNzb']">DogNzb</a></li>
|
||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Radarr']">Radarr</a></li>
|
||||
<!--<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Watcher']">Watcher</a></li>-->
|
||||
<li [routerLinkActive]="['active']"><a>More Coming Soon...</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
<label for="select" class="control-label">Quality Profiles</label>
|
||||
<div id="profiles">
|
||||
<select class="form-control form-control-custom" [ngClass]="{'form-error': form.get('qualityProfile').hasError('required')}" id="select" formControlName="qualityProfile">
|
||||
<option *ngFor="let quality of qualities" value="{{quality.id}}" >{{quality.name}}</option>
|
||||
<option *ngFor="let quality of qualities" value="{{quality.value}}" >{{quality.display}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<small *ngIf="form.get('qualityProfile').hasError('required')" class="error-text">A Default Quality Profile is required</small>
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { Component, OnInit } from "@angular/core";
|
||||
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
|
||||
|
||||
import { ISonarrProfile, ISonarrRootFolder } from "../../interfaces";
|
||||
|
||||
import { ISickRageSettings } from "../../interfaces";
|
||||
import { IDropDownModel, ISickRageSettings } from "../../interfaces";
|
||||
import { TesterService } from "../../services";
|
||||
import { NotificationService } from "../../services";
|
||||
import { SettingsService } from "../../services";
|
||||
|
@ -13,14 +11,8 @@ import { SettingsService } from "../../services";
|
|||
})
|
||||
export class SickRageComponent implements OnInit {
|
||||
|
||||
public qualities: ISonarrProfile[];
|
||||
public rootFolders: ISonarrRootFolder[];
|
||||
public selectedRootFolder: ISonarrRootFolder;
|
||||
public selectedQuality: ISonarrProfile;
|
||||
public profilesRunning: boolean;
|
||||
public rootFoldersRunning: boolean;
|
||||
public qualities: IDropDownModel[];
|
||||
public form: FormGroup;
|
||||
public advanced = false;
|
||||
|
||||
constructor(private settingsService: SettingsService,
|
||||
private notificationService: NotificationService,
|
||||
|
@ -34,13 +26,12 @@ export class SickRageComponent implements OnInit {
|
|||
enabled: [x.enabled],
|
||||
apiKey: [x.apiKey, [Validators.required]],
|
||||
qualityProfile: [x.qualityProfile, [Validators.required]],
|
||||
qualities: [x.qualities],
|
||||
ssl: [x.ssl],
|
||||
subDir: [x.subDir],
|
||||
ip: [x.ip, [Validators.required]],
|
||||
port: [x.port, [Validators.required]],
|
||||
});
|
||||
|
||||
this.qualities = x.qualities;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
115
src/Ombi/Controllers/External/TesterController.cs
vendored
115
src/Ombi/Controllers/External/TesterController.cs
vendored
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Hangfire;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.CouchPotato;
|
||||
|
@ -10,7 +9,6 @@ using Ombi.Api.Plex;
|
|||
using Ombi.Api.Radarr;
|
||||
using Ombi.Api.SickRage;
|
||||
using Ombi.Api.Sonarr;
|
||||
using Ombi.Api.Telegram;
|
||||
using Ombi.Attributes;
|
||||
using Ombi.Core.Notifications;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
|
@ -18,7 +16,6 @@ using Ombi.Helpers;
|
|||
using Ombi.Notifications;
|
||||
using Ombi.Notifications.Agents;
|
||||
using Ombi.Notifications.Models;
|
||||
using Ombi.Notifications.Templates;
|
||||
using Ombi.Settings.Settings.Models.External;
|
||||
using Ombi.Settings.Settings.Models.Notifications;
|
||||
|
||||
|
@ -84,11 +81,19 @@ namespace Ombi.Controllers.External
|
|||
[HttpPost("discord")]
|
||||
public bool Discord([FromBody] DiscordNotificationSettings settings)
|
||||
{
|
||||
settings.Enabled = true;
|
||||
DiscordNotification.NotifyAsync(
|
||||
new NotificationOptions { NotificationType = NotificationType.Test, RequestId = -1 }, settings);
|
||||
try
|
||||
{
|
||||
settings.Enabled = true;
|
||||
DiscordNotification.NotifyAsync(
|
||||
new NotificationOptions { NotificationType = NotificationType.Test, RequestId = -1 }, settings);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.LogError(LoggingEvents.Api, e, "Could not test Discord");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -99,11 +104,20 @@ namespace Ombi.Controllers.External
|
|||
[HttpPost("pushbullet")]
|
||||
public bool Pushbullet([FromBody] PushbulletSettings settings)
|
||||
{
|
||||
settings.Enabled = true;
|
||||
PushbulletNotification.NotifyAsync(
|
||||
new NotificationOptions { NotificationType = NotificationType.Test, RequestId = -1 }, settings);
|
||||
try
|
||||
{
|
||||
|
||||
return true;
|
||||
settings.Enabled = true;
|
||||
PushbulletNotification.NotifyAsync(
|
||||
new NotificationOptions { NotificationType = NotificationType.Test, RequestId = -1 }, settings);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.LogError(LoggingEvents.Api, e, "Could not test Pushbullet");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -114,11 +128,20 @@ namespace Ombi.Controllers.External
|
|||
[HttpPost("pushover")]
|
||||
public bool Pushover([FromBody] PushoverSettings settings)
|
||||
{
|
||||
settings.Enabled = true;
|
||||
PushoverNotification.NotifyAsync(
|
||||
new NotificationOptions { NotificationType = NotificationType.Test, RequestId = -1 }, settings);
|
||||
try
|
||||
{
|
||||
settings.Enabled = true;
|
||||
PushoverNotification.NotifyAsync(
|
||||
new NotificationOptions { NotificationType = NotificationType.Test, RequestId = -1 }, settings);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.LogError(LoggingEvents.Api, e, "Could not test Pushover");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -129,11 +152,21 @@ namespace Ombi.Controllers.External
|
|||
[HttpPost("mattermost")]
|
||||
public bool Mattermost([FromBody] MattermostNotificationSettings settings)
|
||||
{
|
||||
settings.Enabled = true;
|
||||
MattermostNotification.NotifyAsync(
|
||||
new NotificationOptions { NotificationType = NotificationType.Test, RequestId = -1 }, settings);
|
||||
try
|
||||
{
|
||||
settings.Enabled = true;
|
||||
MattermostNotification.NotifyAsync(
|
||||
new NotificationOptions { NotificationType = NotificationType.Test, RequestId = -1 }, settings);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.LogError(LoggingEvents.Api, e, "Could not test Mattermost");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -145,11 +178,19 @@ namespace Ombi.Controllers.External
|
|||
[HttpPost("slack")]
|
||||
public bool Slack([FromBody] SlackNotificationSettings settings)
|
||||
{
|
||||
settings.Enabled = true;
|
||||
SlackNotification.NotifyAsync(
|
||||
new NotificationOptions { NotificationType = NotificationType.Test, RequestId = -1 }, settings);
|
||||
try
|
||||
{
|
||||
settings.Enabled = true;
|
||||
SlackNotification.NotifyAsync(
|
||||
new NotificationOptions { NotificationType = NotificationType.Test, RequestId = -1 }, settings);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.LogError(LoggingEvents.Api, e, "Could not test Slack");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -293,10 +334,18 @@ namespace Ombi.Controllers.External
|
|||
[HttpPost("telegram")]
|
||||
public async Task<bool> Telegram([FromBody] TelegramSettings settings)
|
||||
{
|
||||
settings.Enabled = true;
|
||||
await TelegramNotification.NotifyAsync(new NotificationOptions { NotificationType = NotificationType.Test, RequestId = -1 }, settings);
|
||||
try
|
||||
{
|
||||
settings.Enabled = true;
|
||||
await TelegramNotification.NotifyAsync(new NotificationOptions { NotificationType = NotificationType.Test, RequestId = -1 }, settings);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.LogError(LoggingEvents.Api, e, "Could not test Telegram");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -307,9 +356,17 @@ namespace Ombi.Controllers.External
|
|||
[HttpPost("sickrage")]
|
||||
public async Task<bool> SickRage([FromBody] SickRageSettings settings)
|
||||
{
|
||||
settings.Enabled = true;
|
||||
var result = await SickRageApi.Ping(settings.ApiKey, settings.FullUri);
|
||||
return result?.data?.pid != null;
|
||||
try
|
||||
{
|
||||
settings.Enabled = true;
|
||||
var result = await SickRageApi.Ping(settings.ApiKey, settings.FullUri);
|
||||
return result?.data?.pid != null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.LogError(LoggingEvents.Api, e, "Could not test SickRage");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue