mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-12 08:16:05 -07:00
Started on sonarr #865
This commit is contained in:
parent
3ee65f4f0c
commit
2c945ebb9b
10 changed files with 358 additions and 44 deletions
|
@ -8,5 +8,9 @@ namespace Ombi.Api.Sonarr
|
|||
{
|
||||
Task<IEnumerable<SonarrProfile>> GetProfiles(string apiKey, string baseUrl);
|
||||
Task<IEnumerable<SonarrRootFolder>> GetRootFolders(string apiKey, string baseUrl);
|
||||
Task<IEnumerable<SonarrSeries>> GetSeries(string apiKey, string baseUrl);
|
||||
Task<SonarrSeries> GetSeriesById(int id, string apiKey, string baseUrl);
|
||||
Task<SonarrSeries> UpdateSeries(SonarrSeries updated, string apiKey, string baseUrl);
|
||||
Task<NewSeries> AddSeries(NewSeries seriesToAdd, string apiKey, string baseUrl);
|
||||
}
|
||||
}
|
75
src/Ombi.Api.Sonarr/Models/NewSeries.cs
Normal file
75
src/Ombi.Api.Sonarr/Models/NewSeries.cs
Normal file
|
@ -0,0 +1,75 @@
|
|||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ombi.Api.Sonarr.Models
|
||||
{
|
||||
public class NewSeries
|
||||
{
|
||||
public NewSeries()
|
||||
{
|
||||
images = new List<SonarrImage>();
|
||||
}
|
||||
public AddOptions addOptions { get; set; }
|
||||
public string title { get; set; }
|
||||
public List<Season> seasons { get; set; }
|
||||
public string rootFolderPath { get; set; }
|
||||
public int qualityProfileId { get; set; }
|
||||
public bool seasonFolder { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public int tvdbId { get; set; }
|
||||
public int tvRageId { get; set; }
|
||||
public string cleanTitle { get; set; }
|
||||
public string imdbId { get; set; }
|
||||
public string titleSlug { get; set; }
|
||||
public int id { get; set; }
|
||||
public List<SonarrImage> images { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is for us
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public List<string> ErrorMessages { get; set; }
|
||||
|
||||
public string Validate()
|
||||
{
|
||||
var errors = new List<string>();
|
||||
var sb = new StringBuilder();
|
||||
if(this.tvdbId == 0)
|
||||
{
|
||||
sb.AppendLine("TVDBID is missing");
|
||||
}
|
||||
if(string.IsNullOrEmpty(title))
|
||||
{
|
||||
sb.AppendLine("Title is missing");
|
||||
}
|
||||
if(qualityProfileId == 0)
|
||||
{
|
||||
sb.AppendLine("Quality ID is missing");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
public class AddOptions
|
||||
{
|
||||
public bool ignoreEpisodesWithFiles { get; set; }
|
||||
public bool ignoreEpisodesWithoutFiles { get; set; }
|
||||
public bool searchForMissingEpisodes { get; set; }
|
||||
}
|
||||
|
||||
public class SonarrImage
|
||||
{
|
||||
public string coverType { get; set; }
|
||||
public string url { get; set; }
|
||||
}
|
||||
|
||||
public class SonarrError
|
||||
{
|
||||
public string propertyName { get; set; }
|
||||
public string errorMessage { get; set; }
|
||||
public object attemptedValue { get; set; }
|
||||
}
|
||||
}
|
88
src/Ombi.Api.Sonarr/Models/SonarrSeries.cs
Normal file
88
src/Ombi.Api.Sonarr/Models/SonarrSeries.cs
Normal file
|
@ -0,0 +1,88 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ombi.Api.Sonarr.Models
|
||||
{
|
||||
|
||||
public class SonarrSeries
|
||||
{
|
||||
public string title { get; set; }
|
||||
public Alternatetitle[] alternateTitles { get; set; }
|
||||
public string sortTitle { get; set; }
|
||||
public int seasonCount { get; set; }
|
||||
public int totalEpisodeCount { get; set; }
|
||||
public int episodeCount { get; set; }
|
||||
public int episodeFileCount { get; set; }
|
||||
public long sizeOnDisk { get; set; }
|
||||
public string status { get; set; }
|
||||
public string overview { get; set; }
|
||||
public DateTime previousAiring { get; set; }
|
||||
public string network { get; set; }
|
||||
public string airTime { get; set; }
|
||||
public Image[] images { get; set; }
|
||||
public Season[] seasons { get; set; }
|
||||
public int year { get; set; }
|
||||
public string path { get; set; }
|
||||
public int profileId { get; set; }
|
||||
public bool seasonFolder { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public bool useSceneNumbering { get; set; }
|
||||
public int runtime { get; set; }
|
||||
public int tvdbId { get; set; }
|
||||
public int tvRageId { get; set; }
|
||||
public int tvMazeId { get; set; }
|
||||
public DateTime firstAired { get; set; }
|
||||
public DateTime lastInfoSync { get; set; }
|
||||
public string seriesType { get; set; }
|
||||
public string cleanTitle { get; set; }
|
||||
public string imdbId { get; set; }
|
||||
public string titleSlug { get; set; }
|
||||
public string certification { get; set; }
|
||||
public string[] genres { get; set; }
|
||||
public object[] tags { get; set; }
|
||||
public DateTime added { get; set; }
|
||||
public Ratings ratings { get; set; }
|
||||
public int qualityProfileId { get; set; }
|
||||
public int id { get; set; }
|
||||
public DateTime nextAiring { get; set; }
|
||||
}
|
||||
|
||||
public class Ratings
|
||||
{
|
||||
public int votes { get; set; }
|
||||
public float value { get; set; }
|
||||
}
|
||||
|
||||
public class Alternatetitle
|
||||
{
|
||||
public string title { get; set; }
|
||||
public int sceneSeasonNumber { get; set; }
|
||||
public int seasonNumber { get; set; }
|
||||
}
|
||||
|
||||
public class Image
|
||||
{
|
||||
public string coverType { get; set; }
|
||||
public string url { get; set; }
|
||||
}
|
||||
|
||||
public class Season
|
||||
{
|
||||
public int seasonNumber { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public Statistics statistics { get; set; }
|
||||
}
|
||||
|
||||
public class Statistics
|
||||
{
|
||||
public int episodeFileCount { get; set; }
|
||||
public int episodeCount { get; set; }
|
||||
public int totalEpisodeCount { get; set; }
|
||||
public long sizeOnDisk { get; set; }
|
||||
public int percentOfEpisodes { get; set; }
|
||||
public DateTime previousAiring { get; set; }
|
||||
public DateTime nextAiring { get; set; }
|
||||
}
|
||||
|
||||
}
|
|
@ -3,6 +3,8 @@ using System.Collections.Generic;
|
|||
using System.Threading.Tasks;
|
||||
|
||||
using Ombi.Api.Sonarr.Models;
|
||||
using Newtonsoft.Json;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ombi.Api.Sonarr
|
||||
{
|
||||
|
@ -33,5 +35,77 @@ namespace Ombi.Api.Sonarr
|
|||
|
||||
return await Api.Request<List<SonarrRootFolder>>(request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all the series in Sonarr
|
||||
/// </summary>
|
||||
/// <param name="apiKey"></param>
|
||||
/// <param name="baseUrl"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<SonarrSeries>> GetSeries(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request("/api/series", baseUrl, HttpMethod.Get);
|
||||
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
|
||||
return await Api.Request<List<SonarrSeries>>(request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the series by the Sonarr ID
|
||||
/// </summary>
|
||||
/// <param name="id">Sonarr ID for the series</param>
|
||||
/// <param name="apiKey"></param>
|
||||
/// <param name="baseUrl"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<SonarrSeries> GetSeriesById(int id, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"/api/series/{id}", baseUrl, HttpMethod.Get);
|
||||
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
|
||||
return await Api.Request<SonarrSeries>(request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the following series
|
||||
/// </summary>
|
||||
/// <param name="updated">The series to update</param>
|
||||
/// <param name="apiKey"></param>
|
||||
/// <param name="baseUrl"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<SonarrSeries> UpdateSeries(SonarrSeries updated, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request("/api/series/", baseUrl, HttpMethod.Put);
|
||||
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
request.AddJsonBody(updated);
|
||||
|
||||
return await Api.Request<SonarrSeries>(request);
|
||||
}
|
||||
|
||||
public async Task<NewSeries> AddSeries(NewSeries seriesToAdd, string apiKey, string baseUrl)
|
||||
{
|
||||
if(!string.IsNullOrEmpty(seriesToAdd.Validate()))
|
||||
{
|
||||
return new NewSeries { ErrorMessages = new List<string> { seriesToAdd.Validate() } };
|
||||
}
|
||||
var request = new Request("/api/series/", baseUrl, HttpMethod.Post);
|
||||
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
try
|
||||
{
|
||||
|
||||
return await Api.Request<NewSeries>(request);
|
||||
}
|
||||
catch (JsonSerializationException e)
|
||||
{
|
||||
var error = await Api.Request<List<SonarrError>>(request);
|
||||
var messages = error?.Select(x => x.errorMessage).ToList();
|
||||
messages?.ForEach(x => Log.Error(x));
|
||||
return new NewSeries { ErrorMessages = messages };
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,18 +24,21 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
public TvRequestEngine(ITvMazeApi tvApi, IRequestServiceMain requestService, IPrincipal user,
|
||||
INotificationHelper helper, IMapper map,
|
||||
IRuleEvaluator rule, IUserIdentityManager manager) : base(user, requestService, rule)
|
||||
IRuleEvaluator rule, IUserIdentityManager manager,
|
||||
ITvSender sender) : base(user, requestService, rule)
|
||||
{
|
||||
TvApi = tvApi;
|
||||
NotificationHelper = helper;
|
||||
Mapper = map;
|
||||
UserManager = manager;
|
||||
TvSender = sender;
|
||||
}
|
||||
|
||||
private INotificationHelper NotificationHelper { get; }
|
||||
private ITvMazeApi TvApi { get; }
|
||||
private IMapper Mapper { get; }
|
||||
private IUserIdentityManager UserManager { get; }
|
||||
private ITvSender TvSender {get;}
|
||||
|
||||
public async Task<RequestEngineResult> RequestTvShow(SearchTvShowViewModel tv)
|
||||
{
|
||||
|
@ -223,7 +226,8 @@ namespace Ombi.Core.Engine
|
|||
Status = showInfo.status,
|
||||
ImdbId = showInfo.externals?.imdb ?? string.Empty,
|
||||
TvDbId = tv.Id,
|
||||
ChildRequests = new List<ChildRequests>()
|
||||
ChildRequests = new List<ChildRequests>(),
|
||||
TotalSeasons = tv.SeasonRequests.Count()
|
||||
};
|
||||
model.ChildRequests.Add(childRequest);
|
||||
return await AddRequest(model);
|
||||
|
@ -294,6 +298,12 @@ namespace Ombi.Core.Engine
|
|||
//NotificationHelper.NewRequest(model.ParentRequest);
|
||||
}
|
||||
|
||||
if(model.Approved)
|
||||
{
|
||||
// Autosend
|
||||
TvSender.SendToSonarr(model,model.ParentRequest.TotalSeasons);
|
||||
}
|
||||
|
||||
//var limit = await RequestLimitRepo.GetAllAsync();
|
||||
//var usersLimit = limit.FirstOrDefault(x => x.Username == Username && x.RequestType == model.Type);
|
||||
//if (usersLimit == null)
|
||||
|
|
12
src/Ombi.Core/ITvSender.cs
Normal file
12
src/Ombi.Core/ITvSender.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Sonarr.Models;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
|
||||
namespace Ombi.Core
|
||||
{
|
||||
public interface ITvSender
|
||||
{
|
||||
Task<NewSeries> SendToSonarr(ChildRequests model, int totalSeasons, string qualityId = null);
|
||||
}
|
||||
}
|
|
@ -1,77 +1,121 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Sonarr;
|
||||
using Ombi.Api.Sonarr.Models;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Core
|
||||
{
|
||||
public class TvSender
|
||||
public class TvSender : ITvSender
|
||||
{
|
||||
public TvSender(ISonarrApi sonarrApi, ILogger<TvSender> log)
|
||||
public TvSender(ISonarrApi sonarrApi, ILogger<TvSender> log, ISettingsService<SonarrSettings> settings)
|
||||
{
|
||||
SonarrApi = sonarrApi;
|
||||
Logger = log;
|
||||
Settings = settings;
|
||||
}
|
||||
|
||||
private ISonarrApi SonarrApi { get; }
|
||||
private ILogger<TvSender> Logger { get; }
|
||||
private ISettingsService<SonarrSettings> Settings { get; }
|
||||
|
||||
//public async Task<SonarrAddSeries> SendToSonarr(SonarrSettings sonarrSettings, TvRequestModel 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);
|
||||
// }
|
||||
/// <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, int totalSeasons, string qualityId = null)
|
||||
{
|
||||
var s = await Settings.GetSettingsAsync();
|
||||
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);
|
||||
if (qualityProfile <= 0)
|
||||
{
|
||||
int.TryParse(s.QualityProfile, out qualityProfile);
|
||||
}
|
||||
|
||||
// //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();
|
||||
// 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 ?? 0, s);
|
||||
|
||||
// //if (episodeRequest)
|
||||
// //{
|
||||
// // return await ProcessSonarrEpisodeRequest(sonarrSettings, model, qualityProfile, rootFolderPath);
|
||||
// //}
|
||||
|
||||
// //if (requestAll ?? false)
|
||||
// //{
|
||||
// // return await ProcessSonarrRequestSeason(sonarrSettings, model, qualityProfile, rootFolderPath);
|
||||
// //}
|
||||
// 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 (first ?? false)
|
||||
// //{
|
||||
// // return await ProcessSonarrRequestSeason(sonarrSettings, model, qualityProfile, rootFolderPath);
|
||||
// //}
|
||||
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 = false, // We want all missing
|
||||
searchForMissingEpisodes = true // we want to search for missing TODO pass this in
|
||||
}
|
||||
};
|
||||
|
||||
// //if (latest ?? false)
|
||||
// //{
|
||||
// // return await ProcessSonarrRequestSeason(sonarrSettings, model, qualityProfile, rootFolderPath);
|
||||
// //}
|
||||
// Montitor the correct seasons,
|
||||
// If we have that season in the model then it's monitored!
|
||||
var seasonsToAdd = new List<Season>();
|
||||
for (int i = 1; i < totalSeasons; i++)
|
||||
{
|
||||
var season = new Season
|
||||
{
|
||||
seasonNumber = i,
|
||||
monitored = model.SeasonRequests.Any(x => x.SeasonNumber == i)
|
||||
};
|
||||
seasonsToAdd.Add(season);
|
||||
}
|
||||
|
||||
// //if (specificSeasonRequest ?? false)
|
||||
// //{
|
||||
// // return await ProcessSonarrRequestSeason(sonarrSettings, model, qualityProfile, rootFolderPath);
|
||||
// //}
|
||||
var result = SonarrApi.AddSeries(newSeries, s.ApiKey, s.FullUri);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Let's update the existing
|
||||
}
|
||||
|
||||
// return null;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<ITvSearchEngine, TvSearchEngine>();
|
||||
services.AddSingleton<IRuleEvaluator, RuleEvaluator>();
|
||||
services.AddTransient<IMovieSender, MovieSender>();
|
||||
services.AddTransient<ITvSender, TvSender>();
|
||||
}
|
||||
|
||||
public static void RegisterApi(this IServiceCollection services)
|
||||
|
|
|
@ -11,6 +11,8 @@ namespace Ombi.Store.Entities.Requests
|
|||
public TvRequests ParentRequest { get; set; }
|
||||
public int ParentRequestId { get; set; }
|
||||
public int? IssueId { get; set; }
|
||||
|
||||
|
||||
[ForeignKey(nameof(IssueId))]
|
||||
public List<TvIssues> Issues { get; set; }
|
||||
|
||||
|
|
|
@ -13,6 +13,10 @@ namespace Ombi.Store.Entities.Requests
|
|||
public string PosterPath { get; set; }
|
||||
public DateTime ReleaseDate { get; set; }
|
||||
public string Status { get; set; }
|
||||
/// <summary>
|
||||
/// This is so we can correctly send the right amount of seasons to Sonarr
|
||||
/// </summary>
|
||||
public int TotalSeasons { get; set; }
|
||||
|
||||
public List<ChildRequests> ChildRequests { get; set; }
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue