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

feat(sonarr): ✨ Add the username to a Sonarr tag when sent to sonarr #4802

Merged
merged 1 commit into from
Nov 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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: 2 additions & 1 deletion src/Ombi.Api.Sonarr/ISonarrV3Api.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Ombi.Api.Sonarr.Models;
using System.Net.Http;
using Ombi.Api.Sonarr.Models.V3;

namespace Ombi.Api.Sonarr
{
public interface ISonarrV3Api : ISonarrApi
{
Task<IEnumerable<LanguageProfiles>> LanguageProfiles(string apiKey, string baseUrl);
Task<Tag> CreateTag(string apiKey, string baseUrl, string tagName);
Task<Tag> GetTag(int tagId, string apiKey, string baseUrl);
}
}
1 change: 1 addition & 0 deletions src/Ombi.Api.Sonarr/Models/NewSeries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public NewSeries()
public string seriesType { get; set; }
public int id { get; set; }
public List<SonarrImage> images { get; set; }
public List<int> tags { get; set; }

// V3 Property
public int languageProfileId { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion src/Ombi.Api.Sonarr/Models/SonarrSeries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class SonarrSeries
public string titleSlug { get; set; }
public string certification { get; set; }
public string[] genres { get; set; }
public object[] tags { get; set; }
public List<int> tags { get; set; }
public DateTime added { get; set; }
public Ratings ratings { get; set; }
public int qualityProfileId { get; set; }
Expand Down
18 changes: 17 additions & 1 deletion src/Ombi.Api.Sonarr/SonarrV3Api.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ public class SonarrV3Api : SonarrApi, ISonarrV3Api
{
public SonarrV3Api(IApi api) : base(api)
{

}

protected override string ApiBaseUrl => "/api/v3/";
Expand All @@ -30,5 +29,22 @@ public override async Task<IEnumerable<SonarrProfile>> GetProfiles(string apiKey
request.AddHeader("X-Api-Key", apiKey);
return await Api.Request<List<SonarrProfile>>(request);
}

public Task<Tag> CreateTag(string apiKey, string baseUrl, string tagName)
{
var request = new Request($"{ApiBaseUrl}tag", baseUrl, HttpMethod.Post);
request.AddHeader("X-Api-Key", apiKey);
request.AddJsonBody(new { Label = tagName });

return Api.Request<Tag>(request);
}

public Task<Tag> GetTag(int tagId, string apiKey, string baseUrl)
{
var request = new Request($"{ApiBaseUrl}tag/{tagId}", baseUrl, HttpMethod.Get);
request.AddHeader("X-Api-Key", apiKey);

return Api.Request<Tag>(request);
}
}
}
10 changes: 10 additions & 0 deletions src/Ombi.Core/Senders/SonarrSendOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Ombi.Api.Sonarr.Models;
using System.Collections.Generic;

namespace Ombi.Core.Senders
{
internal class SonarrSendOptions
{
public List<int> Tags { get; set; } = new List<int>();
}
}
69 changes: 60 additions & 9 deletions src/Ombi.Core/Senders/TvSender.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.VisualBasic;
using Ombi.Api.DogNzb;
using Ombi.Api.DogNzb.Models;
using Ombi.Api.SickRage;
Expand Down Expand Up @@ -155,11 +157,13 @@ public async Task<NewSeries> SendToSonarr(ChildRequests model, SonarrSettings s)
{
return null;
}
var options = new SonarrSendOptions();

int qualityToUse;
var languageProfileId = s.LanguageProfile;
string rootFolderPath;
string seriesType;
int? tagToUse = null;

var profiles = await UserQualityProfiles.GetAll().FirstOrDefaultAsync(x => x.UserId == model.RequestedUserId);

Expand Down Expand Up @@ -190,6 +194,7 @@ public async Task<NewSeries> SendToSonarr(ChildRequests model, SonarrSettings s)
}
}
seriesType = "anime";
tagToUse = s.AnimeTag;
}
else
{
Expand All @@ -209,6 +214,7 @@ public async Task<NewSeries> SendToSonarr(ChildRequests model, SonarrSettings s)
}
}
seriesType = "standard";
tagToUse = s.Tag;
}

// Overrides on the request take priority
Expand Down Expand Up @@ -240,6 +246,16 @@ public async Task<NewSeries> SendToSonarr(ChildRequests model, SonarrSettings s)

try
{
if (tagToUse.HasValue)
{
options.Tags.Add(tagToUse.Value);
}
if (s.SendUserTags)
{
var userTag = await GetOrCreateTag(model, s);
options.Tags.Add(userTag.id);
}

// Does the series actually exist?
var allSeries = await SonarrApi.GetSeries(s.ApiKey, s.FullUri);
var existingSeries = allSeries.FirstOrDefault(x => x.tvdbId == model.ParentRequest.TvDbId);
Expand All @@ -265,10 +281,10 @@ public async Task<NewSeries> SendToSonarr(ChildRequests model, SonarrSettings s)
ignoreEpisodesWithoutFiles = false, // We want all missing
searchForMissingEpisodes = false // we want dont want to search yet. We want to make sure everything is unmonitored/monitored correctly.
},
languageProfileId = languageProfileId
};
languageProfileId = languageProfileId,
tags = options.Tags
};



// Montitor the correct seasons,
// If we have that season in the model then it's monitored!
Expand All @@ -280,11 +296,11 @@ public async Task<NewSeries> SendToSonarr(ChildRequests model, SonarrSettings s)
throw new Exception(string.Join(',', result.ErrorMessages));
}
existingSeries = await SonarrApi.GetSeriesById(result.id, s.ApiKey, s.FullUri);
await SendToSonarr(model, existingSeries, s);
await SendToSonarr(model, existingSeries, s, options);
}
else
{
await SendToSonarr(model, existingSeries, s);
await SendToSonarr(model, existingSeries, s, options);
}

return new NewSeries
Expand All @@ -303,7 +319,30 @@ public async Task<NewSeries> SendToSonarr(ChildRequests model, SonarrSettings s)
}
}

private async Task SendToSonarr(ChildRequests model, SonarrSeries result, SonarrSettings s)
private async Task<Tag> GetOrCreateTag(ChildRequests model, SonarrSettings s)
{
var tagName = model.RequestedUser.UserName;
// Does tag exist?

var allTags = await SonarrV3Api.GetTags(s.ApiKey, s.FullUri);
var existingTag = allTags.FirstOrDefault(x => x.label.Equals(tagName, StringComparison.InvariantCultureIgnoreCase));
existingTag ??= await SonarrV3Api.CreateTag(s.ApiKey, s.FullUri, tagName);

return existingTag;
}

private async Task<Tag> GetTag(int tagId, SonarrSettings s)
{
var tag = await SonarrV3Api.GetTag(tagId, s.ApiKey, s.FullUri);
if (tag == null)
{
Logger.LogError($"Tag ID {tagId} does not exist in sonarr. Please update the settings");
return null;
}
return tag;
}

private async Task SendToSonarr(ChildRequests model, SonarrSeries result, SonarrSettings s, SonarrSendOptions options)
{
// Check to ensure we have the all the seasons, ensure the Sonarr metadata has grabbed all the data
Season existingSeason = null;
Expand All @@ -321,15 +360,27 @@ private async Task SendToSonarr(ChildRequests model, SonarrSeries result, Sonarr
}
}

var episodesToUpdate = new List<Episode>();
// Ok, now let's sort out the episodes.
// Does the show have the correct tags we are expecting
if (options.Tags.Any())
{
result.tags ??= options.Tags;
var tagsToAdd = options.Tags.Except(result.tags);

if (tagsToAdd.Any())
{
result.tags.AddRange(tagsToAdd);
}
result = await SonarrApi.UpdateSeries(result, s.ApiKey, s.FullUri);
}

if (model.SeriesType == SeriesType.Anime)
{
result.seriesType = "anime";
await SonarrApi.UpdateSeries(result, s.ApiKey, s.FullUri);
result = await SonarrApi.UpdateSeries(result, s.ApiKey, s.FullUri);
}

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())
Expand Down
4 changes: 4 additions & 0 deletions src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ public class SonarrSettings : ExternalSettings

public string QualityProfileAnime { get; set; }
public string RootPathAnime { get; set; }
public int? AnimeTag { get; set; }
public int? Tag { get; set; }
public bool AddOnly { get; set; }
public int LanguageProfile { get; set; }
public int LanguageProfileAnime { get; set; }
public bool ScanForAvailability { get; set; }

public bool SendUserTags { get; set; }
}
}
3 changes: 3 additions & 0 deletions src/Ombi/ClientApp/src/app/interfaces/ISettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ export interface ISonarrSettings extends IExternalSettings {
languageProfile: number;
languageProfileAnime: number;
scanForAvailability: boolean;
sendUserTags: boolean;
tag: number | null;
animeTag: number | null;
}

export interface IRadarrSettings extends IExternalSettings {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
<div class="md-form-field">
<mat-slide-toggle formControlName="scanForAvailability">Scan for Availability</mat-slide-toggle>
</div>
<div class="md-form-field">
<mat-slide-toggle formControlName="sendUserTags" id="sendUserTags">Add the user as a tag</mat-slide-toggle>
<small><br>This will add the username of the requesting user as a tag in Sonarr. If the tag doesn't exist, Ombi will create it.</small>
</div>
<div class="md-form-field" style="margin-top:1em;"></div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export class SonarrComponent implements OnInit {
languageProfile: [x.languageProfile, [Validators.required, validateProfile]],
languageProfileAnime: [x.languageProfileAnime],
scanForAvailability: [x.scanForAvailability],
sendUserTags: [x.sendUserTags]
});

if (x.qualityProfile) {
Expand Down