Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The beginning of grabbing Sonarr root folders and approving requests with sepecific root folders #535

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions PlexRequests.Api.Interfaces/ISonarrApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ public interface ISonarrApi
{
List<SonarrProfile> GetProfiles(string apiKey, Uri baseUrl);

List<SonarrRootFolder> GetRootFolders(string apiKey, Uri baseUrl);

SonarrAddSeries AddSeries(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath,
int rootFolderId,
int seasonCount, int[] seasons, string apiKey, Uri baseUrl, bool monitor = true,
bool searchForMissingEpisodes = false);

Expand Down
1 change: 1 addition & 0 deletions PlexRequests.Api.Models/PlexRequests.Api.Models.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
<Compile Include="Sonarr\SonarrEpisodes.cs" />
<Compile Include="Sonarr\SonarrError.cs" />
<Compile Include="Sonarr\SonarrProfile.cs" />
<Compile Include="Sonarr\SonarrRootFolder.cs" />
<Compile Include="Sonarr\SystemStatus.cs" />
<Compile Include="Tv\Authentication.cs" />
<Compile Include="Tv\TvMazeEpisodes.cs" />
Expand Down
19 changes: 18 additions & 1 deletion PlexRequests.Api/SonarrApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,24 @@ public List<SonarrProfile> GetProfiles(string apiKey, Uri baseUrl)
return obj;
}

public SonarrAddSeries AddSeries(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, int seasonCount, int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, bool searchForMissingEpisodes = false)

public List<SonarrRootFolder> GetRootFolders(string apiKey, Uri baseUrl)
{
var request = new RestRequest { Resource = "/api/rootfolder", Method = Method.GET };

request.AddHeader("X-Api-Key", apiKey);
var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) => Log.Error(exception, "Exception when calling GetRootFolders for Sonarr, Retrying {0}", timespan), new TimeSpan[] {
TimeSpan.FromSeconds (2),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10)
});

var obj = policy.Execute(() => Api.ExecuteJson<List<SonarrRootFolder>>(request, baseUrl));

return obj;
}

public SonarrAddSeries AddSeries(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, int rootFolderId, int seasonCount, int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, bool searchForMissingEpisodes = false)
{
Log.Debug("Adding series {0}", title);
Log.Debug("Seasons = {0}, out of {1} seasons", seasons.DumpJson(), seasonCount);
Expand Down
1 change: 1 addition & 0 deletions PlexRequests.Core/CacheKeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public struct TimeFrameMinutes
public const string PlexEpisodes = nameof(PlexEpisodes);
public const string TvDbToken = nameof(TvDbToken);
public const string SonarrQualityProfiles = nameof(SonarrQualityProfiles);
public const string SonarrRootFolders = nameof(SonarrRootFolders);
public const string SonarrQueued = nameof(SonarrQueued);
public const string SickRageQualityProfiles = nameof(SickRageQualityProfiles);
public const string SickRageQueued = nameof(SickRageQueued);
Expand Down
4 changes: 3 additions & 1 deletion PlexRequests.Core/SettingModels/SonarrSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ public sealed class SonarrSettings : ExternalSettings
public bool Enabled { get; set; }
public string ApiKey { get; set; }
public string QualityProfile { get; set; }
public bool SeasonFolders { get; set; }
public string RootFolder { get; set; }
public string RootPath { get; set; }
public bool SeasonFolders { get; set; }


}
}
36 changes: 36 additions & 0 deletions PlexRequests.Core/Setup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,20 @@ public void CacheQualityProfiles()
}
}

public void CacheRootFolders()
{
var mc = new MemoryCacheProvider();

try
{
Task.Run(() => { CacheSonarrRootFolders(mc); });
}
catch (Exception)
{
Log.Error("Failed to cache quality profiles on startup!");
}
}

private void CacheSonarrQualityProfiles(MemoryCacheProvider cacheProvider)
{
try
Expand All @@ -157,6 +171,28 @@ private void CacheSonarrQualityProfiles(MemoryCacheProvider cacheProvider)
}
}

private void CacheSonarrRootFolders(MemoryCacheProvider cacheProvider)
{
try
{
Log.Info("Executing GetSettings call to Sonarr for root folders");
var sonarrSettingsService = new SettingsServiceV2<SonarrSettings>(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider()));
var sonarrSettings = sonarrSettingsService.GetSettings();
if (sonarrSettings.Enabled)
{
Log.Info("Begin executing GetSettings call to Sonarr for root folders");
SonarrApi sonarrApi = new SonarrApi();
var rootFolders = sonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri);
cacheProvider.Set(CacheKeys.SonarrRootFolders, rootFolders);
Log.Info("Finished executing GetSettings call to Sonarr for root folders");
}
}
catch (Exception ex)
{
Log.Error(ex, "Failed to cache Sonarr quality profiles!");
}
}

private void CacheCouchPotatoQualityProfiles(MemoryCacheProvider cacheProvider)
{
try
Expand Down
13 changes: 10 additions & 3 deletions PlexRequests.UI.Tests/TvSenderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ public async Task HappyPathSendSeriesToSonarr()
It.IsAny<bool>(),
It.IsAny<string>(),
It.IsAny<int>(),
It.IsAny<int>(),
It.IsAny<int[]>(),
It.IsAny<string>(),
It.IsAny<Uri>(),
Expand All @@ -93,6 +94,7 @@ public async Task HappyPathSendSeriesToSonarr()
It.IsAny<bool>(),
It.IsAny<string>(),
It.IsAny<int>(),
It.IsAny<int>(),
It.IsAny<int[]>(),
It.IsAny<string>(),
It.IsAny<Uri>(),
Expand All @@ -113,6 +115,7 @@ public async Task HappyPathSendEpisodeWithExistingSeriesToSonarr()
It.IsAny<bool>(),
It.IsAny<string>(),
It.IsAny<int>(),
It.IsAny<int>(),
It.IsAny<int[]>(),
It.IsAny<string>(),
It.IsAny<Uri>(),
Expand Down Expand Up @@ -142,19 +145,23 @@ public async Task HappyPathSendEpisodeWithExistingSeriesToSonarr()
var model = F.Build<RequestedModel>().With(x => x.ProviderId, 1)
.With(x => x.Episodes, episodes).Create();

var result = await Sender.SendToSonarr(GetSonarrSettings(), model, "2");
var result = await Sender.SendToSonarr(GetSonarrSettings(), model, "2", "1");

Assert.That(result, Is.EqualTo(seriesResult));
SonarrMock.Verify(x => x.AddSeries(It.IsAny<int>(),
SonarrMock.Verify(x => x.AddSeries(
It.IsAny<int>(),
It.IsAny<string>(),
It.IsAny<int>(),
It.IsAny<bool>(),
It.IsAny<string>(),
It.IsAny<int>(), // rootFolderId
It.IsAny<int>(),
It.IsAny<int[]>(),
It.IsAny<string>(),
It.IsAny<Uri>(),
true, It.IsAny<bool>()), Times.Once);
true,
It.IsAny<bool>()
), Times.Once);
}

[Test]
Expand Down
23 changes: 19 additions & 4 deletions PlexRequests.UI/Helpers/TvSender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ public TvSender(ISonarrApi sonarrApi, ISickRageApi srApi)

public async Task<SonarrAddSeries> SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model)
{
return await SendToSonarr(sonarrSettings, model, string.Empty);
return await SendToSonarr(sonarrSettings, model, string.Empty, string.Empty);
}

public async Task<SonarrAddSeries> SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model, string qualityId)
public async Task<SonarrAddSeries> SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model, string qualityId, string rootFolderId)
{
var qualityProfile = 0;
var episodeRequest = model.Episodes.Any();
Expand All @@ -72,6 +72,17 @@ public async Task<SonarrAddSeries> SendToSonarr(SonarrSettings sonarrSettings, R
int.TryParse(sonarrSettings.QualityProfile, out qualityProfile);
}

var rootFolder = 0;
if (!string.IsNullOrEmpty(rootFolderId))
{
int.TryParse(qualityId, out rootFolder);
}

if (rootFolder <= 0)
{
int.TryParse(sonarrSettings.RootFolder, out rootFolder);
}

var series = await GetSonarrSeries(sonarrSettings, model.ProviderId);

if (episodeRequest)
Expand All @@ -88,7 +99,9 @@ public async Task<SonarrAddSeries> SendToSonarr(SonarrSettings sonarrSettings, R

// Series doesn't exist, need to add it as unmonitored.
var addResult = await Task.Run(() => SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile,
sonarrSettings.SeasonFolders, sonarrSettings.RootPath, 0, new int[0], sonarrSettings.ApiKey,
sonarrSettings.SeasonFolders, sonarrSettings.RootPath,
1, // rootFolderId
0, new int[0], sonarrSettings.ApiKey,
sonarrSettings.FullUri, false));


Expand Down Expand Up @@ -160,7 +173,9 @@ public async Task<SonarrAddSeries> SendToSonarr(SonarrSettings sonarrSettings, R


var result = SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile,
sonarrSettings.SeasonFolders, sonarrSettings.RootPath, model.SeasonCount, model.SeasonList, sonarrSettings.ApiKey,
sonarrSettings.SeasonFolders, sonarrSettings.RootPath,
rootFolder,
model.SeasonCount, model.SeasonList, sonarrSettings.ApiKey,
sonarrSettings.FullUri, true, true);

return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public SwaggerModelData GetModelData()
with.Property(x => x.SubDir).Description("Subdir/BaseUrl of Sonarr").Required(false);
with.Property(x => x.ApiKey).Description("Sonarr's API key").Required(true);
with.Property(x => x.QualityProfile).Description("Sonarr's quality profile").Required(true);
with.Property(x => x.RootFolder).Description("Sonarr's root folder").Required(true);

with.Property(x => x.SeasonFolders).Description("Sonarr's season folders").Required(false);

Expand Down
1 change: 1 addition & 0 deletions PlexRequests.UI/Models/RequestViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public class RequestViewModel
public string TvSeriesRequestType { get; set; }
public string MusicBrainzId { get; set; }
public QualityModel[] Qualities { get; set; }
public RootFolderModel[] RootFolders { get; set; }
public string ArtistName { get; set; }
public Store.EpisodesModel[] Episodes { get; set; }
public bool Denied { get; set; }
Expand Down
14 changes: 14 additions & 0 deletions PlexRequests.UI/Modules/AdminModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ public AdminModule(ISettingsService<PlexRequestSettings> prService,
Post["/sickrage"] = _ => SaveSickrage();

Post["/sonarrprofiles"] = _ => GetSonarrQualityProfiles();
Post["/sonarrrootfolders"] = _ => GetSonarrRootFolders();
Post["/cpprofiles", true] = async (x, ct) => await GetCpProfiles();
Post["/cpapikey"] = x => GetCpApiKey();

Expand Down Expand Up @@ -470,6 +471,19 @@ private Response GetSonarrQualityProfiles()
return Response.AsJson(profiles);
}

private Response GetSonarrRootFolders()
{
var settings = this.Bind<SonarrSettings>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of doing the validation in the Javascript, you can check if the binding is valid. There are a bunch of validators e.g. SonarrSettingsValidator (If you look at some of the POST requests you should be able to see how we validate, and when there is invalid data we return the invalid message)

var rootFolders = SonarrApi.GetRootFolders(settings.ApiKey, settings.FullUri);

// set the cache
if (rootFolders != null)
{
Cache.Set(CacheKeys.SonarrRootFolders, rootFolders);
}

return Response.AsJson(rootFolders);
}

private Negotiator EmailNotifications()
{
Expand Down
10 changes: 5 additions & 5 deletions PlexRequests.UI/Modules/ApprovalModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public ApprovalModule(IRequestService service, ISettingsService<CouchPotatoSetti
HeadphonesSettings = hpSettings;
HeadphoneApi = hpApi;

Post["/approve", true] = async (x, ct) => await Approve((int)Request.Form.requestid, (string)Request.Form.qualityId);
Post["/approve", true] = async (x, ct) => await Approve((int)Request.Form.requestid, (string)Request.Form.qualityId, (string)Request.Form.rootFolderId);
Post["/deny", true] = async (x, ct) => await DenyRequest((int)Request.Form.requestid, (string)Request.Form.reason);
Post["/approveall", true] = async (x, ct) => await ApproveAll();
Post["/approveallmovies", true] = async (x, ct) => await ApproveAllMovies();
Expand All @@ -91,7 +91,7 @@ public ApprovalModule(IRequestService service, ISettingsService<CouchPotatoSetti
/// </summary>
/// <param name="requestId">The request identifier.</param>
/// <returns></returns>
private async Task<Response> Approve(int requestId, string qualityId)
private async Task<Response> Approve(int requestId, string qualityId, string rootFolderId)
{
Log.Info("approving request {0}", requestId);

Expand All @@ -109,23 +109,23 @@ private async Task<Response> Approve(int requestId, string qualityId)
case RequestType.Movie:
return await RequestMovieAndUpdateStatus(request, qualityId);
case RequestType.TvShow:
return await RequestTvAndUpdateStatus(request, qualityId);
return await RequestTvAndUpdateStatus(request, qualityId, rootFolderId);
case RequestType.Album:
return await RequestAlbumAndUpdateStatus(request);
default:
throw new ArgumentOutOfRangeException(nameof(request));
}
}

private async Task<Response> RequestTvAndUpdateStatus(RequestedModel request, string qualityId)
private async Task<Response> RequestTvAndUpdateStatus(RequestedModel request, string qualityId, string rootFolderId)
{
var sender = new TvSender(SonarrApi, SickRageApi);

var sonarrSettings = await SonarrSettings.GetSettingsAsync();
if (sonarrSettings.Enabled)
{
Log.Trace("Sending to Sonarr");
var result = await sender.SendToSonarr(sonarrSettings, request, qualityId);
var result = await sender.SendToSonarr(sonarrSettings, request, qualityId, rootFolderId);
Log.Trace("Sonarr Result: ");
Log.Trace(result.DumpJson());
if (!string.IsNullOrEmpty(result.title))
Expand Down
32 changes: 32 additions & 0 deletions PlexRequests.UI/Modules/RequestsBetaModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,37 @@ private async Task<Response> GetTvShows()

}

IEnumerable<RootFolderModel> rootFolders = new List<RootFolderModel>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module is not being used yet just a FYI, there is no code being used in this class

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured as much, I just copied what I wrote in the RequestModule.cs :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@onedr0p Ok that's fine :)

if (IsAdmin)
{
try
{
var sonarrSettings = await SonarrSettings.GetSettingsAsync();
if (sonarrSettings.Enabled)
{
var result = Cache.GetOrSetAsync(CacheKeys.SonarrRootFolders, async () =>
{
return await Task.Run(() => SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri));
});
rootFolders = result.Result.Select(x => new RootFolderModel { Id = x.id.ToString(), Path = x.path }).ToList();
}
// @TODO Sick Rage Root Folders
//else
//{
// var sickRageSettings = await SickRageSettings.GetSettingsAsync();
// if (sickRageSettings.Enabled)
// {
// qualities = sickRageSettings.Qualities.Select(x => new QualityModel { Id = x.Key, Name = x.Value }).ToList();
// }
//}
}
catch (Exception e)
{
Log.Info(e);
}

}

var viewModel = dbTv.Select(tv => new RequestViewModel
{
ProviderId = tv.ProviderId,
Expand All @@ -209,6 +240,7 @@ private async Task<Response> GetTvShows()
IssueId = tv.IssueId,
TvSeriesRequestType = tv.SeasonsRequested,
Qualities = qualities.ToArray(),
RootFolders = rootFolders.ToArray(),
Episodes = tv.Episodes.ToArray(),
}).ToList();

Expand Down
Loading