merge in tidusjar's dev changes, remove approve all, fix alignment on approve all category buttons

pull/110/head
Drewster727 9 years ago
commit dce5983720

@ -64,6 +64,7 @@
<Compile Include="SickRage\SickRageStatus.cs" />
<Compile Include="SickRage\SickRageTvAdd.cs" />
<Compile Include="Sonarr\SonarrAddSeries.cs" />
<Compile Include="Sonarr\SonarrError.cs" />
<Compile Include="Sonarr\SonarrProfile.cs" />
<Compile Include="Sonarr\SystemStatus.cs" />
<Compile Include="Tv\Authentication.cs" />

@ -1,5 +1,7 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace PlexRequests.Api.Models.Sonarr
{
public class Season
@ -23,6 +25,8 @@ namespace PlexRequests.Api.Models.Sonarr
public string imdbId { get; set; }
public string titleSlug { get; set; }
public int id { get; set; }
[JsonIgnore]
public string ErrorMessage { get; set; }
}
public class AddOptions

@ -0,0 +1,36 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: SonarrError.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
namespace PlexRequests.Api.Models.Sonarr
{
public class SonarrError
{
public string propertyName { get; set; }
public string errorMessage { get; set; }
public string attemptedValue { get; set; }
public string[] formattedMessageArguments { get; set; }
}
}

@ -26,13 +26,9 @@
#endregion
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NLog;
@ -96,20 +92,13 @@ namespace PlexRequests.Api
throw new ApplicationException(message, response.ErrorException);
}
try
{
var json = JsonConvert.DeserializeObject<T>(response.Content);
return json;
}
catch (Exception e)
{
Log.Fatal(e);
Log.Info(response.Content);
throw;
}
var json = JsonConvert.DeserializeObject<T>(response.Content);
return json;
}
public T DeserializeXml<T>(string input)
private T DeserializeXml<T>(string input)
where T : class
{
var ser = new XmlSerializer(typeof(T));

@ -27,9 +27,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using NLog;
using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.Sonarr;
using PlexRequests.Helpers;
using RestSharp;
namespace PlexRequests.Api
@ -56,7 +61,8 @@ namespace PlexRequests.Api
public SonarrAddSeries AddSeries(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, int seasonCount, int[] seasons, string apiKey, Uri baseUrl)
{
Log.Debug("Adding series {0}", title);
Log.Debug("Seasons = {0}, out of {1} seasons", seasons.DumpJson(), seasonCount);
var request = new RestRequest
{
Resource = "/api/Series?",
@ -74,7 +80,6 @@ namespace PlexRequests.Api
rootFolderPath = rootPath
};
for (var i = 1; i <= seasonCount; i++)
{
var season = new Season
@ -85,12 +90,25 @@ namespace PlexRequests.Api
options.seasons.Add(season);
}
Log.Debug("Sonarr API Options:");
Log.Debug(options.DumpJson());
request.AddHeader("X-Api-Key", apiKey);
request.AddJsonBody(options);
var obj = Api.ExecuteJson<SonarrAddSeries>(request, baseUrl);
SonarrAddSeries result;
try
{
result = Api.ExecuteJson<SonarrAddSeries>(request, baseUrl);
}
catch (JsonSerializationException jse)
{
Log.Error(jse);
var error = Api.ExecuteJson<SonarrError>(request, baseUrl);
result = new SonarrAddSeries { ErrorMessage = error.errorMessage };
}
return obj;
return result;
}
public SystemStatus SystemStatus(string apiKey, Uri baseUrl)

@ -79,6 +79,10 @@ namespace PlexRequests.Core
public RequestedModel Get(int id)
{
var blob = Repo.Get(id);
if (blob == null)
{
return new RequestedModel();
}
var model = ByteConverterHelper.ReturnObject<RequestedModel>(blob.Content);
return model;
}

@ -74,6 +74,7 @@
<Compile Include="Models\StatusModel.cs" />
<Compile Include="Models\UserProperties.cs" />
<Compile Include="SettingModels\AuthenticationSettings.cs" />
<Compile Include="SettingModels\HeadphonesSettings.cs" />
<Compile Include="SettingModels\PushoverNotificationSettings.cs" />
<Compile Include="SettingModels\PushBulletNotificationSettings.cs" />
<Compile Include="SettingModels\EmailNotificationSettings.cs" />

@ -0,0 +1,58 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: CouchPotatoSettings.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using Newtonsoft.Json;
using PlexRequests.Helpers;
namespace PlexRequests.Core.SettingModels
{
public class HeadphonesSettings : Settings
{
public string Enabled { get; set; }
public string Ip { get; set; }
public int Port { get; set; }
public int ApiKey { get; set; }
public bool Ssl { get; set; }
public string SubDir { get; set; }
[JsonIgnore]
public Uri FullUri
{
get
{
if (!string.IsNullOrEmpty(SubDir))
{
var formattedSubDir = Ip.ReturnUriWithSubDir(Port, Ssl, SubDir);
return formattedSubDir;
}
var formatted = Ip.ReturnUri(Port, Ssl);
return formatted;
}
}
}
}

@ -32,12 +32,12 @@ using Moq;
using NUnit.Framework;
using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models;
using PlexRequests.Api.Models.Plex;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers.Exceptions;
using PlexRequests.Services.Interfaces;
using PlexRequests.Store;
namespace PlexRequests.Services.Tests
{
@ -66,7 +66,7 @@ namespace PlexRequests.Services.Tests
var requestMock = new Mock<IRequestService>();
var plexMock = new Mock<IPlexApi>();
var searchResult = new PlexSearch {Video = new List<Video> {new Video {Title = "title", Year = "2011"} } };
var searchResult = new PlexSearch { Video = new List<Video> { new Video { Title = "title", Year = "2011" } } };
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "abc" });
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
@ -87,7 +87,7 @@ namespace PlexRequests.Services.Tests
var requestMock = new Mock<IRequestService>();
var plexMock = new Mock<IPlexApi>();
var searchResult = new PlexSearch { Directory = new Directory1 {Title = "title", Year = "2013"} };
var searchResult = new PlexSearch { Directory = new Directory1 { Title = "title", Year = "2013" } };
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "abc" });
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
@ -100,6 +100,27 @@ namespace PlexRequests.Services.Tests
Assert.That(result, Is.True);
}
[Test]
public void IsAvailableDirectoryTitleWithoutYearTest()
{
var settingsMock = new Mock<ISettingsService<PlexSettings>>();
var authMock = new Mock<ISettingsService<AuthenticationSettings>>();
var requestMock = new Mock<IRequestService>();
var plexMock = new Mock<IPlexApi>();
var searchResult = new PlexSearch { Directory = new Directory1 { Title = "title", } };
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "abc" });
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
plexMock.Setup(x => x.SearchContent(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Uri>())).Returns(searchResult);
Checker = new PlexAvailabilityChecker(settingsMock.Object, authMock.Object, requestMock.Object, plexMock.Object);
var result = Checker.IsAvailable("title", null);
Assert.That(result, Is.True);
}
[Test]
public void IsNotAvailableTest()
{
@ -108,7 +129,7 @@ namespace PlexRequests.Services.Tests
var requestMock = new Mock<IRequestService>();
var plexMock = new Mock<IPlexApi>();
var searchResult = new PlexSearch { Video = new List<Video> { new Video { Title = "wrong tistle", Year = "2011"} } };
var searchResult = new PlexSearch { Video = new List<Video> { new Video { Title = "wrong title", Year = "2011" } } };
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "abc" });
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
@ -121,6 +142,27 @@ namespace PlexRequests.Services.Tests
Assert.That(result, Is.False);
}
[Test]
public void IsNotAvailableTestWihtoutYear()
{
var settingsMock = new Mock<ISettingsService<PlexSettings>>();
var authMock = new Mock<ISettingsService<AuthenticationSettings>>();
var requestMock = new Mock<IRequestService>();
var plexMock = new Mock<IPlexApi>();
var searchResult = new PlexSearch { Video = new List<Video> { new Video { Title = "wrong title" } } };
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "abc" });
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
plexMock.Setup(x => x.SearchContent(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Uri>())).Returns(searchResult);
Checker = new PlexAvailabilityChecker(settingsMock.Object, authMock.Object, requestMock.Object, plexMock.Object);
var result = Checker.IsAvailable("title", null);
Assert.That(result, Is.False);
}
[Test]
public void IsYearDoesNotMatchTest()
{
@ -141,5 +183,155 @@ namespace PlexRequests.Services.Tests
Assert.That(result, Is.False);
}
[Test]
public void TitleDoesNotMatchTest()
{
var settingsMock = new Mock<ISettingsService<PlexSettings>>();
var authMock = new Mock<ISettingsService<AuthenticationSettings>>();
var requestMock = new Mock<IRequestService>();
var plexMock = new Mock<IPlexApi>();
var searchResult = new PlexSearch { Video = new List<Video> { new Video { Title = "title23", Year = "2019" } } };
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "abc" });
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
plexMock.Setup(x => x.SearchContent(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Uri>())).Returns(searchResult);
Checker = new PlexAvailabilityChecker(settingsMock.Object, authMock.Object, requestMock.Object, plexMock.Object);
var result = Checker.IsAvailable("title", "2019");
Assert.That(result, Is.False);
}
[Test]
public void TitleDoesNotMatchWithoutYearTest()
{
var settingsMock = new Mock<ISettingsService<PlexSettings>>();
var authMock = new Mock<ISettingsService<AuthenticationSettings>>();
var requestMock = new Mock<IRequestService>();
var plexMock = new Mock<IPlexApi>();
var searchResult = new PlexSearch { Video = new List<Video> { new Video { Title = "title23" } } };
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "abc" });
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
plexMock.Setup(x => x.SearchContent(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Uri>())).Returns(searchResult);
Checker = new PlexAvailabilityChecker(settingsMock.Object, authMock.Object, requestMock.Object, plexMock.Object);
var result = Checker.IsAvailable("title", null);
Assert.That(result, Is.False);
}
[Test]
public void CheckAndUpdateNoPlexSettingsTest()
{
var settingsMock = new Mock<ISettingsService<PlexSettings>>();
var authMock = new Mock<ISettingsService<AuthenticationSettings>>();
var requestMock = new Mock<IRequestService>();
var plexMock = new Mock<IPlexApi>();
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
Checker = new PlexAvailabilityChecker(settingsMock.Object, authMock.Object, requestMock.Object, plexMock.Object);
Checker.CheckAndUpdateAll(1);
requestMock.Verify(x => x.BatchUpdate(It.IsAny<List<RequestedModel>>()), Times.Never);
requestMock.Verify(x => x.Get(It.IsAny<int>()), Times.Never);
plexMock.Verify(x => x.SearchContent(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Uri>()), Times.Never);
}
[Test]
public void CheckAndUpdateNoAuthSettingsTest()
{
var settingsMock = new Mock<ISettingsService<PlexSettings>>();
var authMock = new Mock<ISettingsService<AuthenticationSettings>>();
var requestMock = new Mock<IRequestService>();
var plexMock = new Mock<IPlexApi>();
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "123" });
Checker = new PlexAvailabilityChecker(settingsMock.Object, authMock.Object, requestMock.Object, plexMock.Object);
Checker.CheckAndUpdateAll(1);
requestMock.Verify(x => x.BatchUpdate(It.IsAny<List<RequestedModel>>()), Times.Never);
requestMock.Verify(x => x.Get(It.IsAny<int>()), Times.Never);
plexMock.Verify(x => x.SearchContent(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Uri>()), Times.Never);
}
[Test]
public void CheckAndUpdateNoRequestsTest()
{
var settingsMock = new Mock<ISettingsService<PlexSettings>>();
var authMock = new Mock<ISettingsService<AuthenticationSettings>>();
var requestMock = new Mock<IRequestService>();
var plexMock = new Mock<IPlexApi>();
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "192.168.1.1" });
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
requestMock.Setup(x => x.GetAll()).Returns(new List<RequestedModel>());
Checker = new PlexAvailabilityChecker(settingsMock.Object, authMock.Object, requestMock.Object, plexMock.Object);
Checker.CheckAndUpdateAll(1);
requestMock.Verify(x => x.BatchUpdate(It.IsAny<List<RequestedModel>>()), Times.Never);
requestMock.Verify(x => x.Get(It.IsAny<int>()), Times.Never);
plexMock.Verify(x => x.SearchContent(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Uri>()), Times.Never);
}
[Test]
[Ignore("Need to work out Plex Directory vs Video objects")]
public void CheckAndUpdateRequestsTest()
{
var requests = new List<RequestedModel> {
new RequestedModel
{
Id = 123,
Title = "title1",
Available = false,
},
new RequestedModel
{
Id=222,
Title = "title3",
Available = false
},
new RequestedModel
{
Id = 333,
Title= "missingTitle",
Available = false
},
new RequestedModel
{
Id= 444,
Title = "already found",
Available = true
}
};
var search = new PlexSearch { };
var settingsMock = new Mock<ISettingsService<PlexSettings>>();
var authMock = new Mock<ISettingsService<AuthenticationSettings>>();
var requestMock = new Mock<IRequestService>();
var plexMock = new Mock<IPlexApi>();
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "192.168.1.1" });
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
requestMock.Setup(x => x.GetAll()).Returns(requests);
Checker = new PlexAvailabilityChecker(settingsMock.Object, authMock.Object, requestMock.Object, plexMock.Object);
Checker.CheckAndUpdateAll(1);
requestMock.Verify(x => x.BatchUpdate(It.IsAny<List<RequestedModel>>()), Times.Never);
requestMock.Verify(x => x.Get(It.IsAny<int>()), Times.Never);
plexMock.Verify(x => x.SearchContent(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Uri>()), Times.Never);
}
}
}

@ -84,6 +84,10 @@
<Project>{566EFA49-68F8-4716-9693-A6B3F2624DEA}</Project>
<Name>PlexRequests.Services</Name>
</ProjectReference>
<ProjectReference Include="..\PlexRequests.Store\PlexRequests.Store.csproj">
<Project>{92433867-2B7B-477B-A566-96C382427525}</Project>
<Name>PlexRequests.Store</Name>
</ProjectReference>
</ItemGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">

@ -24,6 +24,7 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
@ -52,7 +53,7 @@ namespace PlexRequests.Services
private ISettingsService<AuthenticationSettings> Auth { get; }
private IRequestService RequestService { get; }
private static Logger Log = LogManager.GetCurrentClassLogger();
private IPlexApi PlexApi { get; set; }
private IPlexApi PlexApi { get; }
public void CheckAndUpdateAll(long check)
@ -62,7 +63,7 @@ namespace PlexRequests.Services
var requests = RequestService.GetAll();
var requestedModels = requests as RequestedModel[] ?? requests.ToArray();
if (!ValidateSettings(plexSettings, authSettings, requestedModels))
if (!ValidateSettings(plexSettings, authSettings) || !requestedModels.Any())
{
return;
}
@ -97,38 +98,22 @@ namespace PlexRequests.Services
{
throw new ApplicationSettingsException("The settings are not configured for Plex or Authentication");
}
var results = PlexApi.SearchContent(authSettings.PlexAuthToken, title, plexSettings.FullUri);
if (!string.IsNullOrEmpty(year))
{
var results = PlexApi.SearchContent(authSettings.PlexAuthToken, title, plexSettings.FullUri);
var result = results.Video?.FirstOrDefault(x => x.Title.Contains(title) && x.Year == year);
var result = results.Video?.FirstOrDefault(x => x.Title.Equals(title, StringComparison.InvariantCultureIgnoreCase) && x.Year == year);
var directoryTitle = results.Directory?.Title == title && results.Directory?.Year == year;
return result?.Title != null || directoryTitle;
}
else
{
var results = PlexApi.SearchContent(authSettings.PlexAuthToken, title, plexSettings.FullUri);
var result = results.Video?.FirstOrDefault(x => x.Title.Contains(title));
var result = results.Video?.FirstOrDefault(x => x.Title.Equals(title, StringComparison.InvariantCultureIgnoreCase));
var directoryTitle = results.Directory?.Title == title;
return result?.Title != null || directoryTitle;
}
}
private bool ValidateSettings(PlexSettings plex, AuthenticationSettings auth, IEnumerable<RequestedModel> requests)
{
if (plex.Ip == null || auth.PlexAuthToken == null || requests == null)
{
Log.Warn("A setting is null, Ensure Plex is configured correctly, and we have a Plex Auth token.");
return false;
}
if (!requests.Any())
{
Log.Info("We have no requests to check if they are available on Plex.");
return false;
}
return true;
}
private bool ValidateSettings(PlexSettings plex, AuthenticationSettings auth)
{
if (plex?.Ip == null || auth?.PlexAuthToken == null)

@ -27,12 +27,11 @@
using System;
using System.Data;
using System.IO;
using System.Windows.Forms;
using Mono.Data.Sqlite;
using NLog;
using PlexRequests.Helpers;
using PlexRequests.Store.Repository;
namespace PlexRequests.Store
{
@ -44,12 +43,14 @@ namespace PlexRequests.Store
Factory = provider;
}
private SqliteFactory Factory { get; set; }
private SqliteFactory Factory { get; }
private string CurrentPath =>Path.Combine(Path.GetDirectoryName(Application.ExecutablePath) ?? string.Empty, DbFile);
public virtual bool CheckDb()
{
Log.Trace("Checking DB");
if (!File.Exists(DbFile))
Console.WriteLine(CurrentPath);
if (!File.Exists(CurrentPath))
{
Log.Trace("DB doesn't exist, creating a new one");
CreateDatabase();
@ -59,7 +60,7 @@ namespace PlexRequests.Store
}
public string DbFile = "PlexRequests.sqlite";
/// <summary>
/// Gets the database connection.
/// </summary>
@ -72,7 +73,7 @@ namespace PlexRequests.Store
{
throw new SqliteException("Factory returned null");
}
fact.ConnectionString = "Data Source=" + DbFile;
fact.ConnectionString = "Data Source=" + CurrentPath;
return fact;
}
@ -83,7 +84,7 @@ namespace PlexRequests.Store
{
try
{
using (File.Create(DbFile))
using (File.Create(CurrentPath))
{
}
}

@ -52,6 +52,7 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />

@ -59,6 +59,7 @@ namespace PlexRequests.UI.Tests
private Mock<ISettingsService<EmailNotificationSettings>> EmailMock { get; set; }
private Mock<ISettingsService<PushbulletNotificationSettings>> PushbulletSettings { get; set; }
private Mock<ISettingsService<PushoverNotificationSettings>> PushoverSettings { get; set; }
private Mock<ISettingsService<HeadphonesSettings>> HeadphonesSettings { get; set; }
private Mock<IPlexApi> PlexMock { get; set; }
private Mock<ISonarrApi> SonarrApiMock { get; set; }
private Mock<IPushbulletApi> PushbulletApi { get; set; }
@ -94,6 +95,7 @@ namespace PlexRequests.UI.Tests
PushoverSettings = new Mock<ISettingsService<PushoverNotificationSettings>>();
PushoverApi = new Mock<IPushoverApi>();
NotificationService = new Mock<INotificationService>();
HeadphonesSettings = new Mock<ISettingsService<HeadphonesSettings>>();
Bootstrapper = new ConfigurableBootstrapper(with =>
{
@ -114,6 +116,7 @@ namespace PlexRequests.UI.Tests
with.Dependency(PushoverSettings.Object);
with.Dependency(PushoverApi.Object);
with.Dependency(NotificationService.Object);
with.Dependency(HeadphonesSettings.Object);
with.RootPathProvider<TestRootPathProvider>();
with.RequestStartup((container, pipelines, context) =>
{

@ -76,9 +76,9 @@ namespace PlexRequests.UI
container.Register<ISettingsService<EmailNotificationSettings>, SettingsServiceV2<EmailNotificationSettings>>();
container.Register<ISettingsService<PushbulletNotificationSettings>, SettingsServiceV2<PushbulletNotificationSettings>>();
container.Register<ISettingsService<PushoverNotificationSettings>, SettingsServiceV2<PushoverNotificationSettings>>();
container.Register<ISettingsService<HeadphonesSettings>, SettingsServiceV2<HeadphonesSettings>>();
// Repo's
container.Register<IRepository<RequestedModel>, GenericRepository<RequestedModel>>();
container.Register<IRepository<LogEntity>, GenericRepository<LogEntity>>();
container.Register<IRequestService, JsonRequestService>();
container.Register<ISettingsRepository, SettingsJsonRepository>();
@ -101,13 +101,13 @@ namespace PlexRequests.UI
SubscribeAllObservers(container);
base.ConfigureRequestContainer(container, context);
}
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
TaskManager.TaskFactory = new PlexTaskFactory();
TaskManager.Initialize(new PlexRegistry());
}
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
CookieBasedSessions.Enable(pipelines, CryptographyConfiguration.Default);
StaticConfiguration.DisableErrorTraces = false;
@ -123,11 +123,12 @@ namespace PlexRequests.UI
FormsAuthentication.Enable(pipelines, formsAuthConfiguration);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
ServicePointManager.ServerCertificateValidationCallback +=
(sender, certificate, chain, sslPolicyErrors) => true;
}
protected override DiagnosticsConfiguration DiagnosticsConfiguration => new DiagnosticsConfiguration { Password = @"password" };

@ -58,36 +58,6 @@ $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
});
// Approve all
$('#approveAll').click(function (e) {
e.preventDefault();
var buttonId = e.target.id;
var origHtml = $(this).html();
if ($('#' + buttonId).text() === " Loading...") {
return;
}
loadingButton(buttonId, "success");
$.ajax({
type: 'post',
url: '/approval/approveall',
dataType: "json",
success: function (response) {
if (checkJsonResponse(response)) {
generateNotify("Success! All requests approved!", "success");
initLoad();
}
},
error: function (e) {
console.log(e);
generateNotify("Something went wrong!", "danger");
},
complete: function (e) {
finishLoading(buttonId, "success", origHtml)
}
});
});
$('#approveMovies').click(function (e) {
e.preventDefault();
var buttonId = e.target.id;

@ -24,17 +24,13 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using Nancy;
using NLog;
using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.SickRage;
using PlexRequests.Api.Models.Sonarr;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers;
using PlexRequests.Store;
using PlexRequests.UI.Models;
namespace PlexRequests.UI.Helpers
{

@ -12,12 +12,12 @@ namespace PlexRequests.UI.Jobs
//typeof(AvailabilityUpdateService);
var container = TinyIoCContainer.Current;
var a= container.ResolveAll(typeof(T));
var a= container.Resolve(typeof(T));
object outT;
container.TryResolve(typeof(T), out outT);
return (T)outT;
return (T)a;
}
}
}

@ -28,6 +28,8 @@
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Web.UI.HtmlControls;
using Humanizer;
using MarkdownSharp;
@ -66,6 +68,7 @@ namespace PlexRequests.UI.Modules
private ISettingsService<EmailNotificationSettings> EmailService { get; }
private ISettingsService<PushbulletNotificationSettings> PushbulletService { get; }
private ISettingsService<PushoverNotificationSettings> PushoverService { get; }
private ISettingsService<HeadphonesSettings> HeadphonesService { get; }
private IPlexApi PlexApi { get; }
private ISonarrApi SonarrApi { get; }
private IPushbulletApi PushbulletApi { get; }
@ -90,7 +93,8 @@ namespace PlexRequests.UI.Modules
ISettingsService<PushoverNotificationSettings> pushoverSettings,
IPushoverApi pushoverApi,
IRepository<LogEntity> logsRepo,
INotificationService notify) : base("admin")
INotificationService notify,
ISettingsService<HeadphonesSettings> headphones) : base("admin")
{
RpService = rpService;
CpService = cpService;
@ -108,6 +112,7 @@ namespace PlexRequests.UI.Modules
PushoverService = pushoverSettings;
PushoverApi = pushoverApi;
NotificationService = notify;
HeadphonesService = headphones;
#if !DEBUG
this.RequiresAuthentication();
@ -155,6 +160,9 @@ namespace PlexRequests.UI.Modules
Get["/loglevel"] = _ => GetLogLevels();
Post["/loglevel"] = _ => UpdateLogLevels(Request.Form.level);
Get["/loadlogs"] = _ => LoadLogs();
Get["/headphones"] = _ => Headphones();
Post["/headphones"] = _ => SaveHeadphones();
}
private Negotiator Authentication()
@ -567,5 +575,32 @@ namespace PlexRequests.UI.Modules
LoggingHelper.ReconfigureLogLevel(newLevel);
return Response.AsJson(new JsonResponseModel { Result = true, Message = $"The new log level is now {newLevel}"});
}
private Negotiator Headphones()
{
var settings = HeadphonesService.GetSettings();
return View["Headphones", settings];
}
private Response SaveHeadphones()
{
var settings = this.Bind<HeadphonesSettings>();
var valid = this.Validate(settings);
if (!valid.IsValid)
{
var error = valid.SendJsonError();
Log.Info("Error validating Headphones settings, message: {0}", error.Message);
return Response.AsJson(error);
}
Log.Trace(settings.DumpJson());
var result = HeadphonesService.SaveSettings(settings);
Log.Info("Saved headphones settings, result: {0}", result);
return Response.AsJson(result
? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Headphones!" }
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
}
}
}

@ -57,6 +57,7 @@ namespace PlexRequests.UI.Modules
Post["/sonarr"] = _ => SonarrTest();
Post["/plex"] = _ => PlexTest();
Post["/sickrage"] = _ => SickRageTest();
Post["/headphones"] = _ => HeadphonesTest();
}
@ -168,5 +169,10 @@ namespace PlexRequests.UI.Modules
return Response.AsJson(new JsonResponseModel { Result = false, Message = message });
}
}
private Response HeadphonesTest()
{
throw new NotImplementedException(); //TODO
}
}
}

@ -133,7 +133,7 @@ namespace PlexRequests.UI.Modules
return Response.AsJson(new JsonResponseModel
{
Result = false,
Message = "Could not add the series to Sonarr"
Message = result.ErrorMessage ?? "Could not add the series to Sonarr"
});
}
@ -340,7 +340,7 @@ namespace PlexRequests.UI.Modules
if (sonarr.Enabled)
{
var res = sender.SendToSonarr(sonarr, r);
if (res != null)
if (!string.IsNullOrEmpty(res?.title))
{
r.Approved = true;
updatedRequests.Add(r);
@ -348,15 +348,25 @@ namespace PlexRequests.UI.Modules
else
{
Log.Error("Could not approve and send the TV {0} to Sonarr!", r.Title);
Log.Error("Error message: {0}", res?.ErrorMessage);
}
}
}
}
var result = Service.BatchUpdate(updatedRequests);
try
{
var result = Service.BatchUpdate(updatedRequests);
return Response.AsJson(result
? new JsonResponseModel { Result = true }
: new JsonResponseModel { Result = false, Message = "We could not approve all of the requests. Please try again or check the logs." });
}
catch (Exception e)
{
Log.Fatal(e);
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" });
}
}
private bool SendMovie(CouchPotatoSettings settings, RequestedModel r, ICouchPotatoApi cp)
{

@ -371,18 +371,19 @@ namespace PlexRequests.UI.Modules
if (sonarrSettings.Enabled)
{
var result = sender.SendToSonarr(sonarrSettings, model);
if (result != null)
if (result != null && !string.IsNullOrEmpty(result.title))
{
model.Approved = true;
Log.Debug("Adding tv to database requests (No approval required & Sonarr)");
RequestService.AddRequest(model);
var notify1 = new NotificationModel { Title = model.Title, User = model.RequestedBy, DateTime = DateTime.Now, NotificationType = NotificationType.NewRequest };
NotificationService.Publish(notify1);
return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullShowName} was successfully added!" });
}
var notify1 = new NotificationModel { Title = model.Title, User = model.RequestedBy, DateTime = DateTime.Now, NotificationType = NotificationType.NewRequest };
NotificationService.Publish(notify1);
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something went wrong adding the movie to Sonarr! Please check your settings." });
return Response.AsJson(new JsonResponseModel { Result = false, Message = result?.ErrorMessage ?? "Something went wrong adding the movie to Sonarr! Please check your settings." });
}

@ -371,6 +371,9 @@
<Content Include="Views\Admin\PushoverNotifications.cshtml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Views\Admin\Headphones.cshtml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="Web.Debug.config">
<DependentUpon>web.config</DependentUpon>
</None>

@ -0,0 +1,162 @@
@Html.Partial("_Sidebar")
@{
int port;
if (Model.Port == 0)
{
port = 8081;
}
else
{
port = Model.Port;
}
}
<div class="col-sm-8 col-sm-push-1">
<form class="form-horizontal" method="POST" id="mainForm">
<fieldset>
<legend>Headphones Settings</legend>
<div class="form-group">
<div class="checkbox">
<label>
@if (Model.Enabled)
{
<input type="checkbox" id="Enabled" name="Enabled" checked="checked"><text>Enabled</text>
}
else
{
<input type="checkbox" id="Enabled" name="Enabled"><text>Enabled</text>
}
</label>
</div>
</div>
<div class="form-group">
<div class="checkbox">
<label>
@if (Model.Ssl)
{
<input type="checkbox" id="Ssl" name="Ssl" checked="checked"><text>SSL</text>
}
else
{
<input type="checkbox" id="Ssl" name="Ssl"><text>SSL</text>
}
</label>
</div>
</div>
<div class="form-group">
<label for="Ip" class="control-label">Headphones Hostname or IP</label>
<div class="">
<input type="text" class="form-control form-control-custom " id="Ip" name="Ip" placeholder="localhost" value="@Model.Ip">
</div>
</div>
<div class="form-group">
<label for="portNumber" class="control-label">Port</label>
<div class="">
<input type="text" class="form-control form-control-custom " id="portNumber" name="Port" placeholder="Port Number" value="@port">
</div>
</div>
<div class="form-group">
<label for="ApiKey" class="control-label">Headphones API Key</label>
<div>
<input type="text" class="form-control form-control-custom " id="ApiKey" name="ApiKey" value="@Model.ApiKey">
</div>
</div>
<div class="form-group">
<label for="SubDir" class="control-label">Headphones SubDirectory</label>
<div>
<input type="text" class="form-control form-control-custom " id="SubDir" name="SubDir" value="@Model.SubDir">
</div>
</div>
<div class="form-group">
<div>
<button type="submit" id="getProfiles" class="btn btn-primary-outline">Get Quality Profiles</button>
</div>
</div>
<div class="form-group">
<div>
<button id="testHeadphones" type="submit" class="btn btn-primary-outline">Test Connectivity</button>
</div>
</div>
<div class="form-group">
<div>
<button id="save" type="submit" class="btn btn-primary-outline">Submit</button>
</div>
</div>
</fieldset>
</form>
</div>
<script>
$(function() {
$('#testHeadphones').click(function (e) {
e.preventDefault();
var $form = $("#mainForm");
$.ajax({
type: $form.prop("method"),
url: "/test/headphones",
data: $form.serialize(),
dataType: "json",
success: function (response) {
console.log(response);
if (response.result === true) {
generateNotify(response.message, "success");
$('#authToken').val(response.authToken);
} else {
generateNotify(response.message, "warning");
}
},
error: function (e) {
console.log(e);
generateNotify("Something went wrong!", "danger");
}
});
});
$('#save').click(function (e) {
e.preventDefault();
var port = $('#portNumber').val();
if (isNaN(port)) {
generateNotify("You must specify a Port.", "warning");
return;
}
var $form = $("#mainForm");
var qualityProfile = $("#profiles option:selected").val();
var data = $form.serialize();
data = data + "&profileId=" + qualityProfile;
$.ajax({
type: $form.prop("method"),
data: data,
url: $form.prop("action"),
dataType: "json",
success: function (response) {
if (response.result === true) {
generateNotify(response.message, "success");
} else {
generateNotify(response.message, "warning");
}
},
error: function (e) {
console.log(e);
generateNotify("Something went wrong!", "danger");
}
});
});
});
</script>

@ -52,6 +52,14 @@
{
<a class="list-group-item" href="/admin/sickrage">SickRage</a>
}
@if (Context.Request.Path == "/admin/headphones")
{
<a class="list-group-item active" href="/admin/headphones">Headphones</a>
}
else
{
<a class="list-group-item" href="/admin/headphones">Headphones</a>
}
@if (Context.Request.Path == "/admin/emailnotification")
{

@ -2,13 +2,8 @@
<div>
<h1>Requests</h1>
<h4>Below you can see yours and all other requests, as well as their download and approval status.</h4>
@if (Context.CurrentUser.IsAuthenticated())
{
<button id="approveAll" class="btn btn-success-outline" type="submit"><i class="fa fa-plus"></i> Approve All</button>
<br />
<br />
<br />
}
<!-- Nav tabs -->
<ul id="nav-tabs" class="nav nav-tabs" role="tablist">
@if (Model.SearchForMovies)
@ -23,10 +18,9 @@
</ul>
<br />
<!-- Tab panes -->
<div class="tab-content contentList">
<div class="btn-group col-sm-2">
<div class="btn-group col-sm-push-8">
@if (Context.CurrentUser.IsAuthenticated())
{
@if (Model.SearchForMovies)

Loading…
Cancel
Save