mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-12 08:16:05 -07:00
More work on the Sonarr Api Integration #865
This commit is contained in:
parent
2c945ebb9b
commit
c0d019f7d4
10 changed files with 305 additions and 58 deletions
|
@ -1,6 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Sonarr.Models;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Ombi.Api.Sonarr
|
||||
{
|
||||
|
@ -12,5 +13,12 @@ namespace Ombi.Api.Sonarr
|
|||
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);
|
||||
Task<IEnumerable<Episode>> GetEpisodes(int seriesId, string apiKey, string baseUrl);
|
||||
Task<Episode> GetEpisodeById(int episodeId, string apiKey, string baseUrl);
|
||||
Task<EpisodeUpdateResult> UpdateEpisode(Episode episodeToUpdate, string apiKey, string baseUrl);
|
||||
Task<bool> EpisodeSearch(int[] episodeIds, string apiKey, string baseUrl);
|
||||
Task<bool> SeasonSearch(int seriesId, int seasonNumber, string apiKey, string baseUrl);
|
||||
Task<bool> SeriesSearch(int seriesId, string apiKey, string baseUrl);
|
||||
|
||||
}
|
||||
}
|
18
src/Ombi.Api.Sonarr/Models/CommandResult.cs
Normal file
18
src/Ombi.Api.Sonarr/Models/CommandResult.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ombi.Api.Sonarr.Models
|
||||
{
|
||||
|
||||
public class CommandResult
|
||||
{
|
||||
public string name { get; set; }
|
||||
public DateTime startedOn { get; set; }
|
||||
public DateTime stateChangeTime { get; set; }
|
||||
public bool sendUpdatesToClient { get; set; }
|
||||
public string state { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
|
||||
}
|
76
src/Ombi.Api.Sonarr/Models/Episode.cs
Normal file
76
src/Ombi.Api.Sonarr/Models/Episode.cs
Normal file
|
@ -0,0 +1,76 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ombi.Api.Sonarr.Models
|
||||
{
|
||||
public class Episode
|
||||
{
|
||||
public int seriesId { get; set; }
|
||||
public int episodeFileId { get; set; }
|
||||
public int seasonNumber { get; set; }
|
||||
public int episodeNumber { get; set; }
|
||||
public string title { get; set; }
|
||||
public string airDate { get; set; }
|
||||
public DateTime airDateUtc { get; set; }
|
||||
public string overview { get; set; }
|
||||
public bool hasFile { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public bool unverifiedSceneNumbering { get; set; }
|
||||
public int id { get; set; }
|
||||
public int absoluteEpisodeNumber { get; set; }
|
||||
public int sceneAbsoluteEpisodeNumber { get; set; }
|
||||
public int sceneEpisodeNumber { get; set; }
|
||||
public int sceneSeasonNumber { get; set; }
|
||||
public Episodefile episodeFile { get; set; }
|
||||
}
|
||||
|
||||
public class Episodefile
|
||||
{
|
||||
public int seriesId { get; set; }
|
||||
public int seasonNumber { get; set; }
|
||||
public string relativePath { get; set; }
|
||||
public string path { get; set; }
|
||||
public int size { get; set; }
|
||||
public DateTime dateAdded { get; set; }
|
||||
public string sceneName { get; set; }
|
||||
public EpisodeQuality quality { get; set; }
|
||||
public bool qualityCutoffNotMet { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
|
||||
public class EpisodeQuality
|
||||
{
|
||||
public Quality quality { get; set; }
|
||||
public Revision revision { get; set; }
|
||||
}
|
||||
|
||||
public class Revision
|
||||
{
|
||||
public int version { get; set; }
|
||||
public int real { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class EpisodeUpdateResult
|
||||
{
|
||||
public int seriesId { get; set; }
|
||||
public int episodeFileId { get; set; }
|
||||
public int seasonNumber { get; set; }
|
||||
public int episodeNumber { get; set; }
|
||||
public string title { get; set; }
|
||||
public string airDate { get; set; }
|
||||
public DateTime airDateUtc { get; set; }
|
||||
public string overview { get; set; }
|
||||
public bool hasFile { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public int sceneEpisodeNumber { get; set; }
|
||||
public int sceneSeasonNumber { get; set; }
|
||||
public int tvDbEpisodeId { get; set; }
|
||||
public int absoluteEpisodeNumber { get; set; }
|
||||
public bool downloading { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -28,7 +28,7 @@ namespace Ombi.Api.Sonarr.Models
|
|||
public bool seasonFolder { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public bool useSceneNumbering { get; set; }
|
||||
public int runtime { get; set; }
|
||||
public long runtime { get; set; }
|
||||
public int tvdbId { get; set; }
|
||||
public int tvRageId { get; set; }
|
||||
public int tvMazeId { get; set; }
|
||||
|
@ -80,7 +80,7 @@ namespace Ombi.Api.Sonarr.Models
|
|||
public int episodeCount { get; set; }
|
||||
public int totalEpisodeCount { get; set; }
|
||||
public long sizeOnDisk { get; set; }
|
||||
public int percentOfEpisodes { get; set; }
|
||||
public long percentOfEpisodes { get; set; }
|
||||
public DateTime previousAiring { get; set; }
|
||||
public DateTime nextAiring { get; set; }
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ namespace Ombi.Api.Sonarr
|
|||
{
|
||||
public class SonarrApi : ISonarrApi
|
||||
{
|
||||
|
||||
public SonarrApi(IApi api)
|
||||
{
|
||||
Api = api;
|
||||
|
@ -21,18 +20,14 @@ namespace Ombi.Api.Sonarr
|
|||
public async Task<IEnumerable<SonarrProfile>> GetProfiles(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request("/api/profile", baseUrl, HttpMethod.Get);
|
||||
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
|
||||
return await Api.Request<List<SonarrProfile>>(request);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SonarrRootFolder>> GetRootFolders(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request("/api/rootfolder", baseUrl, HttpMethod.Get);
|
||||
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
|
||||
return await Api.Request<List<SonarrRootFolder>>(request);
|
||||
}
|
||||
|
||||
|
@ -45,10 +40,14 @@ namespace Ombi.Api.Sonarr
|
|||
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);
|
||||
var results = await Api.Request<List<SonarrSeries>>(request);
|
||||
|
||||
return await Api.Request<List<SonarrSeries>>(request);
|
||||
foreach (var s in results)
|
||||
{
|
||||
s.seasons.ToList().RemoveAt(0);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -61,10 +60,11 @@ namespace Ombi.Api.Sonarr
|
|||
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);
|
||||
var result = await Api.Request<SonarrSeries>(request);
|
||||
result.seasons.ToList().RemoveAt(0);
|
||||
|
||||
return await Api.Request<SonarrSeries>(request);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -77,10 +77,8 @@ namespace Ombi.Api.Sonarr
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -93,6 +91,7 @@ namespace Ombi.Api.Sonarr
|
|||
var request = new Request("/api/series/", baseUrl, HttpMethod.Post);
|
||||
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
request.AddJsonBody(seriesToAdd);
|
||||
try
|
||||
{
|
||||
|
||||
|
@ -102,10 +101,92 @@ namespace Ombi.Api.Sonarr
|
|||
{
|
||||
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 };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the episodes for the series
|
||||
/// </summary>
|
||||
/// <param name="seriesId">The Sonarr SeriesId value</param>
|
||||
/// <param name="apiKey"></param>
|
||||
/// <param name="baseUrl"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<Episode>> GetEpisodes(int seriesId, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"/api/Episode?seriesId={seriesId}", baseUrl, HttpMethod.Get);
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
return await Api.Request<List<Episode>>(request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the episode for the series
|
||||
/// </summary>
|
||||
/// <param name="episodeId">The Sonarr Episode ID</param>
|
||||
/// <param name="apiKey"></param>
|
||||
/// <param name="baseUrl"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<Episode> GetEpisodeById(int episodeId, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"/api/Episode/{episodeId}", baseUrl, HttpMethod.Get);
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
return await Api.Request<Episode>(request);
|
||||
}
|
||||
|
||||
public async Task<EpisodeUpdateResult> UpdateEpisode(Episode episodeToUpdate, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"/api/Episode/", baseUrl, HttpMethod.Put);
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
request.AddJsonBody(episodeToUpdate);
|
||||
return await Api.Request<EpisodeUpdateResult>(request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Search for a list of episodes
|
||||
/// </summary>
|
||||
/// <param name="episodeIds">The episodes to search for</param>
|
||||
/// <param name="apiKey"></param>
|
||||
/// <param name="baseUrl"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> EpisodeSearch(int[] episodeIds, string apiKey, string baseUrl)
|
||||
{
|
||||
var result = await Command("EpisodeSearch", apiKey, baseUrl, episodeIds);
|
||||
return result != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Search for all episodes of a particular season
|
||||
/// </summary>
|
||||
/// <param name="seriesId">Series to search for</param>
|
||||
/// <param name="seasonNumber">Season to get all episodes</param>
|
||||
/// <param name="apiKey"></param>
|
||||
/// <param name="baseUrl"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> SeasonSearch(int seriesId, int seasonNumber, string apiKey, string baseUrl)
|
||||
{
|
||||
var result = await Command("SeasonSearch", apiKey, baseUrl, new { seriesId, seasonNumber });
|
||||
return result != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Search for all episodes in a series
|
||||
/// </summary>
|
||||
/// <param name="seriesId">Series to search for</param>
|
||||
/// <param name="apiKey"></param>
|
||||
/// <param name="baseUrl"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> SeriesSearch(int seriesId, string apiKey, string baseUrl)
|
||||
{
|
||||
var result = await Command("SeasonSearch", apiKey, baseUrl, seriesId);
|
||||
return result != null;
|
||||
}
|
||||
|
||||
private async Task<CommandResult> Command(string commandName, string apiKey, string baseUrl, object body = null)
|
||||
{
|
||||
var request = new Request($"/api/Command/{commandName}", baseUrl, HttpMethod.Post);
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
if(body != null) request.AddJsonBody(body);
|
||||
return await Api.Request<CommandResult>(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -301,7 +301,7 @@ namespace Ombi.Core.Engine
|
|||
if(model.Approved)
|
||||
{
|
||||
// Autosend
|
||||
TvSender.SendToSonarr(model,model.ParentRequest.TotalSeasons);
|
||||
TvSender.SendToSonarr(model);
|
||||
}
|
||||
|
||||
//var limit = await RequestLimitRepo.GetAllAsync();
|
||||
|
|
|
@ -7,6 +7,6 @@ namespace Ombi.Core
|
|||
{
|
||||
public interface ITvSender
|
||||
{
|
||||
Task<NewSeries> SendToSonarr(ChildRequests model, int totalSeasons, string qualityId = null);
|
||||
Task<NewSeries> SendToSonarr(ChildRequests model, string qualityId = null);
|
||||
}
|
||||
}
|
|
@ -30,7 +30,7 @@ namespace Ombi.Core
|
|||
/// <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)
|
||||
public async Task<NewSeries> SendToSonarr(ChildRequests model, string qualityId = null)
|
||||
{
|
||||
var s = await Settings.GetSettingsAsync();
|
||||
var qualityProfile = 0;
|
||||
|
@ -48,12 +48,16 @@ namespace Ombi.Core
|
|||
// 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);
|
||||
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
|
||||
|
@ -71,15 +75,15 @@ namespace Ombi.Core
|
|||
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
|
||||
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 (int i = 1; i < totalSeasons; i++)
|
||||
for (int i = 1; i < model.ParentRequest.TotalSeasons +1 ; i++)
|
||||
{
|
||||
var season = new Season
|
||||
{
|
||||
|
@ -88,8 +92,59 @@ namespace Ombi.Core
|
|||
};
|
||||
seasonsToAdd.Add(season);
|
||||
}
|
||||
newSeries.seasons = seasonsToAdd;
|
||||
var result = await SonarrApi.AddSeries(newSeries, s.ApiKey, s.FullUri);
|
||||
|
||||
// Ok, now let's sort out the episodes.
|
||||
var sonarrEpisodes = await SonarrApi.GetEpisodes(result.id, s.ApiKey, s.FullUri);
|
||||
while(sonarrEpisodes.Count() == 0)
|
||||
{
|
||||
sonarrEpisodes = await SonarrApi.GetEpisodes(result.id, s.ApiKey, s.FullUri);
|
||||
await Task.Delay(300);
|
||||
}
|
||||
|
||||
var episodesToUpdate = new List<Episode>();
|
||||
foreach (var req in model.SeasonRequests)
|
||||
{
|
||||
foreach (var ep in req.Episodes)
|
||||
{
|
||||
var sonarrEp = sonarrEpisodes.FirstOrDefault(x => x.episodeNumber == ep.EpisodeNumber && x.seasonNumber == ep.Season.SeasonNumber);
|
||||
if(sonarrEp != null)
|
||||
{
|
||||
sonarrEp.monitored = true;
|
||||
episodesToUpdate.Add(sonarrEp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now update the episodes that need updating
|
||||
foreach (var epToUpdate in episodesToUpdate)
|
||||
{
|
||||
await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri);
|
||||
}
|
||||
|
||||
if(!s.AddOnly)
|
||||
{
|
||||
foreach (var season in model.SeasonRequests)
|
||||
{
|
||||
var sonarrSeason = sonarrEpisodes.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(result.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var result = SonarrApi.AddSeries(newSeries, s.ApiKey, s.FullUri);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
|
@ -97,6 +152,12 @@ namespace Ombi.Core
|
|||
// Let's update the existing
|
||||
}
|
||||
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -13,5 +13,6 @@
|
|||
/// The root path.
|
||||
/// </value>
|
||||
public string RootPath { get; set; }
|
||||
public bool AddOnly { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Ombi.Store.Entities.Requests
|
||||
{
|
||||
|
@ -16,6 +17,7 @@ namespace Ombi.Store.Entities.Requests
|
|||
/// <summary>
|
||||
/// This is so we can correctly send the right amount of seasons to Sonarr
|
||||
/// </summary>
|
||||
[NotMapped]
|
||||
public int TotalSeasons { get; set; }
|
||||
|
||||
public List<ChildRequests> ChildRequests { get; set; }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue