Added the support for TV Series integrating with Sonarr

pull/13/head
Jamie Rees 8 years ago
parent 31615ff69c
commit 5dd9885885

@ -34,5 +34,8 @@ namespace PlexRequests.Api.Interfaces
public interface ISonarrApi
{
List<SonarrProfile> GetProfiles(string apiKey, Uri baseUrl);
SonarrAddSeries AddSeries(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath,
bool episodes, string apiKey, Uri baseUrl);
}
}

@ -48,6 +48,7 @@
<Compile Include="Plex\PlexSearch.cs" />
<Compile Include="Plex\PlexUserRequest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Sonarr\SonarrAddSeries.cs" />
<Compile Include="Sonarr\SonarrProfile.cs" />
<Compile Include="Tv\Authentication.cs" />
<Compile Include="Tv\TvSearchResult.cs" />

@ -0,0 +1,37 @@
using System.Collections.Generic;
namespace PlexRequests.Api.Models.Sonarr
{
public class Season
{
public int seasonNumber { get; set; }
public bool monitored { get; set; }
}
public class SonarrAddSeries
{
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 class AddOptions
{
public bool ignoreEpisodesWithFiles { get; set; }
public bool ignoreEpisodesWithoutFiles { get; set; }
public bool searchForMissingEpisodes { get; set; }
}
}

@ -60,6 +60,43 @@ namespace PlexRequests.Api {
}
}
/// <summary>
/// Looks up a localized string similar to {
/// &quot;title&quot;: &quot;Archer (2009)&quot;,
/// &quot;seasons&quot;: [
/// {
/// &quot;seasonNumber&quot;: 5,
/// &quot;monitored&quot;: true
/// },
/// {
/// &quot;seasonNumber&quot;: 4,
/// &quot;monitored&quot;: true
/// },
/// {
/// &quot;seasonNumber&quot;: 3,
/// &quot;monitored&quot;: true
/// },
/// {
/// &quot;seasonNumber&quot;: 2,
/// &quot;monitored&quot;: true
/// },
/// {
/// &quot;seasonNumber&quot;: 1,
/// &quot;monitored&quot;: true
/// },
/// {
/// &quot;seasonNumber&quot;: 0,
/// &quot;monitored&quot;: false
/// }
/// ],
/// &quot;pat [rest of string was truncated]&quot;;.
/// </summary>
internal static string Sonarr_AddSeriesResult {
get {
return ResourceManager.GetString("Sonarr_AddSeriesResult", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to [
/// {

@ -117,6 +117,47 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Sonarr_AddSeriesResult" xml:space="preserve">
<value>{
"title": "Archer (2009)",
"seasons": [
{
"seasonNumber": 5,
"monitored": true
},
{
"seasonNumber": 4,
"monitored": true
},
{
"seasonNumber": 3,
"monitored": true
},
{
"seasonNumber": 2,
"monitored": true
},
{
"seasonNumber": 1,
"monitored": true
},
{
"seasonNumber": 0,
"monitored": false
}
],
"path": "T:\\Archer (2009)",
"qualityProfileId": 1,
"seasonFolder": true,
"monitored": true,
"tvdbId": 110381,
"tvRageId": 23354,
"cleanTitle": "archer2009",
"imdbId": "tt1486217",
"titleSlug": "archer-2009",
"id": 1
}</value>
</data>
<data name="Sonarr_Profiles" xml:space="preserve">
<value>[
{

@ -42,5 +42,13 @@ namespace PlexRequests.Api.Mocks
var obj = JsonConvert.DeserializeObject<List<SonarrProfile>>(json);
return obj;
}
public SonarrAddSeries AddSeries(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, bool episodes,
string apiKey, Uri baseUrl)
{
var json = MockApiData.Sonarr_AddSeriesResult;
var obj = JsonConvert.DeserializeObject<SonarrAddSeries>(json);
return obj;
}
}
}

@ -45,7 +45,7 @@ namespace PlexRequests.Api
public List<SonarrProfile> GetProfiles(string apiKey, Uri baseUrl)
{
var request = new RestRequest { Resource = "/api/profile", Method = Method.GET};
var request = new RestRequest { Resource = "/api/profile", Method = Method.GET };
request.AddHeader("X-Api-Key", apiKey);
@ -53,5 +53,50 @@ namespace PlexRequests.Api
return obj;
}
public SonarrAddSeries AddSeries(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, bool episodes, string apiKey, Uri baseUrl)
{
var request = new RestRequest
{
Resource = "/api/Series?",
Method = Method.POST
};
var options = new SonarrAddSeries();
if (episodes == true)
{
options.addOptions = new AddOptions
{
ignoreEpisodesWithFiles = true,
ignoreEpisodesWithoutFiles = true,
searchForMissingEpisodes = false
};
}
else
{
options.addOptions = new AddOptions
{
ignoreEpisodesWithFiles = false,
searchForMissingEpisodes = true,
ignoreEpisodesWithoutFiles = false
};
}
options.seasonFolder = seasonFolders;
options.title = title;
options.qualityProfileId = qualityId;
options.tvdbId = tvdbId;
options.titleSlug = title;
options.seasons = new List<Season>();
options.rootFolderPath = rootPath;
request.AddHeader("X-Api-Key", apiKey);
request.AddJsonBody(options);
var obj = Api.ExecuteJson<SonarrAddSeries>(request, baseUrl);
return obj;
}
}
}

@ -37,6 +37,8 @@ namespace PlexRequests.Core.SettingModels
public int Port { get; set; }
public string ApiKey { get; set; }
public string QualityProfile { get; set; }
public bool SeasonFolders { get; set; }
public string RootPath { get; set; }
[JsonIgnore]
public Uri FullUri

@ -58,7 +58,7 @@ namespace PlexRequests.Store
return false;
}
public string DbFile = "RequestPlex.sqlite";
public string DbFile = "PlexRequests.sqlite";
/// <summary>
/// Gets the database connection.

@ -23,6 +23,7 @@ namespace PlexRequests.Store
public bool Available { get; set; }
public IssueState Issues { get; set; }
public string OtherMessage { get; set; }
public bool LatestTv { get; set; }
}
public enum RequestType

@ -28,6 +28,7 @@ CREATE TABLE IF NOT EXISTS Requested
ReleaseDate varchar(50) NOT NULL,
Status varchar(50) NOT NULL,
Approved INTEGER NOT NULL,
LatestTv INTEGER NOT NULL,
RequestedBy varchar(50),
RequestedDate varchar(50) NOT NULL,
Available INTEGER(50),

@ -44,13 +44,16 @@ namespace PlexRequests.UI.Modules
public class ApprovalModule : BaseModule
{
public ApprovalModule(IRepository<RequestedModel> service, ISettingsService<CouchPotatoSettings> cpService, ICouchPotatoApi cpApi) : base("approval")
public ApprovalModule(IRepository<RequestedModel> service, ISettingsService<CouchPotatoSettings> cpService, ICouchPotatoApi cpApi, ISonarrApi sonarrApi,
ISettingsService<SonarrSettings> sonarrSettings) : base("approval")
{
this.RequiresAuthentication();
Service = service;
CpService = cpService;
CpApi = cpApi;
SonarrApi = sonarrApi;
SonarrSettings = sonarrSettings;
Post["/approve"] = parameters => Approve((int)Request.Form.requestid);
Post["/approveall"] = x => ApproveAll();
@ -59,7 +62,9 @@ namespace PlexRequests.UI.Modules
private IRepository<RequestedModel> Service { get; set; }
private static Logger Log = LogManager.GetCurrentClassLogger();
private ISettingsService<SonarrSettings> SonarrSettings { get; set; }
private ISettingsService<CouchPotatoSettings> CpService { get; }
private ISonarrApi SonarrApi { get; set; }
private ICouchPotatoApi CpApi { get; }
/// <summary>
@ -95,8 +100,21 @@ namespace PlexRequests.UI.Modules
private Response RequestTvAndUpdateStatus(RequestedModel request)
{
// TODO
return Response.AsJson(new JsonResponseModel());
var sonarrSettings = SonarrSettings.GetSettings();
int qualityProfile;
int.TryParse(sonarrSettings.QualityProfile, out qualityProfile);
var result = SonarrApi.AddSeries(request.ProviderId, request.Title, qualityProfile,
sonarrSettings.SeasonFolders, sonarrSettings.RootPath, request.LatestTv, sonarrSettings.ApiKey,
sonarrSettings.FullUri);
if (!string.IsNullOrEmpty(result.title))
{
return Response.AsJson(new JsonResponseModel { Result = true });
}
return Response.AsJson(new JsonResponseModel
{
Result = false, Message = "Could not add the series to Sonarr"
});
}
private Response RequestMovieAndUpdateStatus(RequestedModel request)

@ -32,6 +32,7 @@ using Nancy.Responses.Negotiation;
using NLog;
using PlexRequests.Api;
using PlexRequests.Api.Interfaces;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers;
@ -46,7 +47,7 @@ namespace PlexRequests.UI.Modules
{
public SearchModule(ICacheProvider cache, ISettingsService<CouchPotatoSettings> cpSettings,
ISettingsService<PlexRequestSettings> prSettings, IAvailabilityChecker checker,
IRequestService request) : base("search")
IRequestService request, ISonarrApi sonarrApi, ISettingsService<SonarrSettings> sonarrSettings) : base("search")
{
CpService = cpSettings;
PrService = prSettings;
@ -55,6 +56,8 @@ namespace PlexRequests.UI.Modules
Cache = cache;
Checker = checker;
RequestService = request;
SonarrApi = sonarrApi;
SonarrService = sonarrSettings;
Get["/"] = parameters => RequestLoad();
@ -68,11 +71,13 @@ namespace PlexRequests.UI.Modules
Post["request/tv"] = parameters => RequestTvShow((int)Request.Form.tvId, (bool)Request.Form.latest);
}
private TheMovieDbApi MovieApi { get; }
private ISonarrApi SonarrApi { get; }
private TheTvDbApi TvApi { get; }
private IRequestService RequestService { get; }
private ICacheProvider Cache { get; }
private ISettingsService<CouchPotatoSettings> CpService { get; }
private ISettingsService<PlexRequestSettings> PrService { get; }
private ISettingsService<SonarrSettings> SonarrService { get; }
private IAvailabilityChecker Checker { get; }
private static Logger Log = LogManager.GetCurrentClassLogger();
private string AuthToken => Cache.GetOrSet(CacheKeys.TvDbToken, TvApi.Authenticate, 50);
@ -257,10 +262,26 @@ namespace PlexRequests.UI.Modules
RequestedDate = DateTime.Now,
Approved = false,
RequestedBy = Session[SessionKeys.UsernameKey].ToString(),
Issues = IssueState.None
Issues = IssueState.None,
LatestTv = latest
};
RequestService.AddRequest(showId, model);
var settings = PrService.GetSettings();
if (!settings.RequireApproval)
{
var sonarrSettings = SonarrService.GetSettings();
int qualityProfile;
int.TryParse(sonarrSettings.QualityProfile, out qualityProfile);
var result = SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile,
sonarrSettings.SeasonFolders, sonarrSettings.RootPath, model.LatestTv, sonarrSettings.ApiKey,
sonarrSettings.FullUri);
Log.Info("Added series {0} to Sonarr, Result: {1}", model.Title, result);
Log.Trace("Model sent to Sonarr: ");
Log.Trace(model.DumpJson());
}
return Response.AsJson(new { Result = true });
}
private string GetAuthToken(TheTvDbApi api)

@ -42,7 +42,7 @@
<div class="form-group">
<label for="authToken" class="control-label">Plex Authorization Token</label>
<div class="">
<input type="text" class="form-control-custom form-control " id="authToken" name="PlexAuthToken" placeholder="Plex Auth Token" value="@Model.PlexAuthToken">
<input type="text" class="form-control-custom form-control" id="authToken" name="PlexAuthToken" placeholder="Plex Auth Token" value="@Model.PlexAuthToken">
</div>
</div>
@ -58,7 +58,7 @@
</div>
<div class="form-group">
<div class="">
<button id="requestToken" class="btn btn-primary-outline-outline">Request Token <i class="fa fa-key"></i></button>
<button id="requestToken" class="btn btn-primary-outline">Request Token <i class="fa fa-key"></i></button>
</div>
</div>

@ -3,7 +3,7 @@
int port;
if (Model.Port == 0)
{
port = 80;
port = 8989;
}
else
{
@ -50,6 +50,31 @@
</div>
</div>
<div class="form-group">
<label for="RootPath" class="control-label">Root save directory for TV shows</label>
<div>
<input type="text" class="form-control form-control-custom " id="RootPath" name="RootPath" value="@Model.RootPath">
<label>Enter the root folder where tv shows are saved. For example <strong>C:\Media\TV</strong>.</label>
</div>
</div>
<div class="form-group">
<div class="checkbox">
<label>
@if (Model.SeasonFolders)
{
<input type="checkbox" id="SeasonFolders" name="SeasonFolders" checked="checked">
}
else
{
<input type="checkbox" id="SeasonFolders" name="SeasonFolders">
}
<label>Enable season folders</label>
<label>Enabled Season Folders to organize seasons into individual folders within a show.</label>
</label>
</div>
</div>
<div class="form-group">
<div>
<button id="save" type="submit" class="btn btn-primary-outline ">Submit</button>

Loading…
Cancel
Save