Merge pull request #23 from tidusjar/dev

Release 1.3.0
This commit is contained in:
Jamie 2016-03-17 20:52:51 +00:00
commit 2dee30bfc7
51 changed files with 1454 additions and 119 deletions

View file

@ -0,0 +1,43 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: IPushbulletApi.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 PlexRequests.Api.Models.Notifications;
namespace PlexRequests.Api.Interfaces
{
public interface IPushbulletApi
{
/// <summary>
/// Pushes the specified message.
/// </summary>
/// <param name="accessToken">The access token.</param>
/// <param name="title">The title.</param>
/// <param name="message">The message.</param>
/// <param name="deviceIdentifier">The device identifier.</param>
/// <returns></returns>
PushbulletResponse Push(string accessToken, string title, string message, string deviceIdentifier = default(string));
}
}

View file

@ -48,6 +48,7 @@
<Compile Include="IApiRequest.cs" /> <Compile Include="IApiRequest.cs" />
<Compile Include="ICouchPotatoApi.cs" /> <Compile Include="ICouchPotatoApi.cs" />
<Compile Include="IPlexApi.cs" /> <Compile Include="IPlexApi.cs" />
<Compile Include="IPushbulletApi.cs" />
<Compile Include="ISonarrApi.cs" /> <Compile Include="ISonarrApi.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>

View file

@ -19,8 +19,8 @@ namespace PlexRequests.Api.Models.Movie
public class Images public class Images
{ {
public List<object> disc_art { get; set; } public List<object> disc_art { get; set; }
public List<string> poster { get; set; } public List<string> poster { get; set; }
public List<object> extra_thumbs { get; set; } public List<object> extra_thumbs { get; set; }
@ -33,10 +33,10 @@ public class Images
public List<object> banner { get; set; } public List<object> banner { get; set; }
public List<string> backdrop { get; set; } public List<string> backdrop { get; set; }
public List<object> extra_fanart { get; set; } public List<object> extra_fanart { get; set; }
} }
public class Info public class Info
{ {
public Rating rating { get; set; } public Rating rating { get; set; }
public List<string> genres { get; set; } public List<string> genres { get; set; }
public int tmdb_id { get; set; } public int tmdb_id { get; set; }
@ -57,15 +57,15 @@ public class Info
public int runtime { get; set; } public int runtime { get; set; }
public string type { get; set; } public string type { get; set; }
public string released { get; set; } public string released { get; set; }
} }
public class Identifiers public class Identifiers
{ {
public string imdb { get; set; } public string imdb { get; set; }
} }
public class Movie public class Movie
{ {
public string status { get; set; } public string status { get; set; }
public Info info { get; set; } public Info info { get; set; }
public string _t { get; set; } public string _t { get; set; }
@ -77,7 +77,7 @@ public class Movie
public object category_id { get; set; } public object category_id { get; set; }
public string type { get; set; } public string type { get; set; }
public Identifiers identifiers { get; set; } public Identifiers identifiers { get; set; }
} }
} }

View file

@ -0,0 +1,38 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: PushbulletPush.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 Newtonsoft.Json;
namespace PlexRequests.Api.Models.Notifications
{
public class PushbulletPush
{
public string body { get; set; }
public string title { get; set; }
public string type { get; set; }
public string device_iden { get; set; }
}
}

View file

@ -0,0 +1,48 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: PushbulletResponse.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.Notifications
{
public class PushbulletResponse
{
public bool active { get; set; }
public string iden { get; set; }
public double created { get; set; }
public double modified { get; set; }
public string type { get; set; }
public bool dismissed { get; set; }
public string direction { get; set; }
public string sender_iden { get; set; }
public string sender_email { get; set; }
public string sender_email_normalized { get; set; }
public string sender_name { get; set; }
public string receiver_iden { get; set; }
public string receiver_email { get; set; }
public string receiver_email_normalized { get; set; }
public string title { get; set; }
public string body { get; set; }
}
}

View file

@ -31,6 +31,10 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
@ -43,6 +47,8 @@
<ItemGroup> <ItemGroup>
<Compile Include="Movie\CouchPotatoAdd.cs" /> <Compile Include="Movie\CouchPotatoAdd.cs" />
<Compile Include="Movie\CouchPotatoStatus.cs" /> <Compile Include="Movie\CouchPotatoStatus.cs" />
<Compile Include="Notifications\PushbulletPush.cs" />
<Compile Include="Notifications\PushbulletResponse.cs" />
<Compile Include="Plex\PlexAuthentication.cs" /> <Compile Include="Plex\PlexAuthentication.cs" />
<Compile Include="Plex\PlexError.cs" /> <Compile Include="Plex\PlexError.cs" />
<Compile Include="Plex\PlexFriends.cs" /> <Compile Include="Plex\PlexFriends.cs" />
@ -58,6 +64,9 @@
<Compile Include="Tv\TvShow.cs" /> <Compile Include="Tv\TvShow.cs" />
<Compile Include="Tv\TvShowImages.cs" /> <Compile Include="Tv\TvShowImages.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net46" />
</packages>

View file

@ -26,6 +26,7 @@
#endregion #endregion
using System; using System;
using System.IO; using System.IO;
using System.Net;
using System.Text; using System.Text;
using System.Xml; using System.Xml;
using System.Xml.Serialization; using System.Xml.Serialization;

View file

@ -72,6 +72,7 @@
<DependentUpon>MockApiData.resx</DependentUpon> <DependentUpon>MockApiData.resx</DependentUpon>
</Compile> </Compile>
<Compile Include="Mocks\MockSonarrApi.cs" /> <Compile Include="Mocks\MockSonarrApi.cs" />
<Compile Include="PushbulletApi.cs" />
<Compile Include="SonarrApi.cs" /> <Compile Include="SonarrApi.cs" />
<Compile Include="CouchPotatoApi.cs" /> <Compile Include="CouchPotatoApi.cs" />
<Compile Include="MovieBase.cs" /> <Compile Include="MovieBase.cs" />

View file

@ -0,0 +1,63 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: PlexApi.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 PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.Notifications;
using RestSharp;
namespace PlexRequests.Api
{
public class PushbulletApi : IPushbulletApi
{
public PushbulletResponse Push(string accessToken, string title, string message, string deviceIdentifier = default(string))
{
var request = new RestRequest
{
Method = Method.POST,
};
request.AddHeader("Access-Token", accessToken);
request.AddHeader("Content-Type", "application/json");
var push = new PushbulletPush { title = title, body = message, type = "note"};
if (!string.IsNullOrEmpty(deviceIdentifier))
{
push.device_iden = deviceIdentifier;
}
request.AddJsonBody(push);
var api = new ApiRequest();
return api.ExecuteJson<PushbulletResponse>(request, new Uri("https://api.pushbullet.com/v2/pushes"));
}
}
}

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<runtime> <runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/> <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0"/> <bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/></startup></configuration> <startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" /></startup></configuration>

View file

@ -35,7 +35,7 @@ namespace PlexRequests.Core
long AddRequest(int providerId, RequestedModel model); long AddRequest(int providerId, RequestedModel model);
bool CheckRequest(int providerId); bool CheckRequest(int providerId);
void DeleteRequest(int tmdbId); void DeleteRequest(int tmdbId);
void UpdateRequest(int originalId, RequestedModel model); void UpdateRequest(RequestedModel model);
RequestedModel Get(int id); RequestedModel Get(int id);
IEnumerable<RequestedModel> GetAll(); IEnumerable<RequestedModel> GetAll();
bool BatchUpdate(List<RequestedModel> model); bool BatchUpdate(List<RequestedModel> model);

View file

@ -0,0 +1,100 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: JsonRequestService.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.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using PlexRequests.Store;
using PlexRequests.Store.Models;
namespace PlexRequests.Core
{
public class JsonRequestService : IRequestService
{
public JsonRequestService(IRequestRepository repo)
{
Repo = repo;
}
private IRequestRepository Repo { get; }
public long AddRequest(int providerId, RequestedModel model)
{
var entity = new RequestBlobs { Type = model.Type, Content = ReturnBytes(model), ProviderId = model.ProviderId};
return Repo.Insert(entity);
}
public bool CheckRequest(int providerId)
{
var blobs = Repo.GetAll();
return blobs.Any(x => x.ProviderId == providerId);
}
public void DeleteRequest(int tmdbId)
{
var blob = Repo.GetAll().FirstOrDefault(x => x.ProviderId == tmdbId);
Repo.Delete(blob);
}
public void UpdateRequest(RequestedModel model)
{
var entity = new RequestBlobs { Type = model.Type, Content = ReturnBytes(model), ProviderId = model.ProviderId, Id = model.Id};
Repo.Update(entity);
}
public RequestedModel Get(int id)
{
var blob = Repo.Get(id);
var json = Encoding.UTF8.GetString(blob.Content);
var model = JsonConvert.DeserializeObject<RequestedModel>(json);
return model;
}
public IEnumerable<RequestedModel> GetAll()
{
var blobs = Repo.GetAll();
return blobs.Select(b => Encoding.UTF8.GetString(b.Content))
.Select(JsonConvert.DeserializeObject<RequestedModel>)
.ToList();
}
public bool BatchUpdate(List<RequestedModel> model)
{
var entities = model.Select(m => new RequestBlobs { Type = m.Type, Content = ReturnBytes(m), ProviderId = m.ProviderId }).ToList();
return Repo.UpdateAll(entities);
}
public byte[] ReturnBytes(object obj)
{
var json = JsonConvert.SerializeObject(obj);
var bytes = Encoding.UTF8.GetBytes(json);
return bytes;
}
}
}

View file

@ -70,8 +70,10 @@
<Compile Include="CacheKeys.cs" /> <Compile Include="CacheKeys.cs" />
<Compile Include="IRequestService.cs" /> <Compile Include="IRequestService.cs" />
<Compile Include="ISettingsService.cs" /> <Compile Include="ISettingsService.cs" />
<Compile Include="JsonRequestService.cs" />
<Compile Include="Models\StatusModel.cs" /> <Compile Include="Models\StatusModel.cs" />
<Compile Include="SettingModels\AuthenticationSettings.cs" /> <Compile Include="SettingModels\AuthenticationSettings.cs" />
<Compile Include="SettingModels\PushBulletNotificationSettings.cs" />
<Compile Include="SettingModels\EmailNotificationSettings.cs" /> <Compile Include="SettingModels\EmailNotificationSettings.cs" />
<Compile Include="SettingModels\PlexSettings.cs" /> <Compile Include="SettingModels\PlexSettings.cs" />
<Compile Include="SettingModels\SonarrSettings.cs" /> <Compile Include="SettingModels\SonarrSettings.cs" />

View file

@ -56,9 +56,9 @@ namespace PlexRequests.Core
Repo.Delete(entity); Repo.Delete(entity);
} }
public void UpdateRequest(int originalId, RequestedModel model) public void UpdateRequest(RequestedModel model)
{ {
model.Id = originalId;
Repo.Update(model); Repo.Update(model);
} }

View file

@ -4,7 +4,7 @@
{ {
public string EmailHost { get; set; } public string EmailHost { get; set; }
public int EmailPort { get; set; } public int EmailPort { get; set; }
public bool EmailAuthentication { get; set; } public bool Ssl { get; set; }
public string RecipientEmail { get; set; } public string RecipientEmail { get; set; }
public string EmailUsername { get; set; } public string EmailUsername { get; set; }
public string EmailPassword { get; set; } public string EmailPassword { get; set; }

View file

@ -35,13 +35,14 @@ namespace PlexRequests.Core.SettingModels
{ {
public string Ip { get; set; } public string Ip { get; set; }
public int Port { get; set; } public int Port { get; set; }
public bool Ssl { get; set; }
[JsonIgnore] [JsonIgnore]
public Uri FullUri public Uri FullUri
{ {
get get
{ {
var formatted = Ip.ReturnUri(Port); var formatted = Ip.ReturnUri(Port, Ssl);
return formatted; return formatted;
} }
} }

View file

@ -0,0 +1,9 @@
namespace PlexRequests.Core.SettingModels
{
public class PushbulletNotificationSettings : Settings
{
public bool Enabled { get; set; }
public string AccessToken { get; set; }
public string DeviceIdentifier { get; set; }
}
}

View file

@ -60,7 +60,7 @@ namespace PlexRequests.Core
SearchForTvShows = true, SearchForTvShows = true,
WeeklyRequestLimit = 0 WeeklyRequestLimit = 0
}; };
var s = new SettingsServiceV2<PlexRequestSettings>(new JsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider())); var s = new SettingsServiceV2<PlexRequestSettings>(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider()));
s.SaveSettings(defaultSettings); s.SaveSettings(defaultSettings);
} }
} }

View file

@ -34,15 +34,24 @@ namespace PlexRequests.Helpers.Tests
public class UriHelperTests public class UriHelperTests
{ {
[TestCaseSource(nameof(UriData))] [TestCaseSource(nameof(UriData))]
public void CreateUri(string uri, Uri expected) public void CreateUri1(string uri, Uri expected)
{ {
var result = uri.ReturnUri(); var result = uri.ReturnUri();
Assert.That(result, Is.EqualTo(expected)); Assert.That(result, Is.EqualTo(expected));
} }
[Test]
public void CreateUriWithSsl()
{
var uri = "192.168.1.69";
var result = uri.ReturnUri(8080, true);
Assert.That(result, Is.EqualTo(new Uri("https://192.168.1.69:8080")));
}
[TestCaseSource(nameof(UriDataWithPort))] [TestCaseSource(nameof(UriDataWithPort))]
public void CreateUri(string uri, int port, Uri expected) public void CreateUri2(string uri, int port, Uri expected)
{ {
var result = uri.ReturnUri(port); var result = uri.ReturnUri(port);

View file

@ -48,8 +48,6 @@ namespace PlexRequests.Helpers
} }
} }
/// <summary> /// <summary>
/// Returns the URI. /// Returns the URI.
/// </summary> /// </summary>
@ -57,7 +55,7 @@ namespace PlexRequests.Helpers
/// <param name="port">The port.</param> /// <param name="port">The port.</param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="System.Exception"></exception> /// <exception cref="System.Exception"></exception>
public static Uri ReturnUri(this string val, int port) public static Uri ReturnUri(this string val, int port, bool ssl = default(bool))
{ {
if (val == null) if (val == null)
{ {
@ -75,7 +73,13 @@ namespace PlexRequests.Helpers
else if (val.StartsWith("https://", StringComparison.Ordinal)) else if (val.StartsWith("https://", StringComparison.Ordinal))
{ {
var split = val.Split('/'); var split = val.Split('/');
uri = split.Length >= 4 ? new UriBuilder(Uri.UriSchemeHttps, split[2], port, "/" + split[3]) : new UriBuilder(Uri.UriSchemeHttps, split[2], port); uri = split.Length >= 4
? new UriBuilder(Uri.UriSchemeHttps, split[2], port, "/" + split[3])
: new UriBuilder(Uri.UriSchemeHttps, split[2], port);
}
else if(ssl)
{
uri = new UriBuilder(Uri.UriSchemeHttps, val, port);
} }
else else
{ {

View file

@ -0,0 +1,122 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: NotificationServiceTests.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 Moq;
using NUnit.Framework;
using PlexRequests.Services.Notification;
namespace PlexRequests.Services.Tests
{
[TestFixture]
public class NotificationServiceTests
{
[Test]
[Ignore("Need to rework due to static class")]
public void SubscribeNewNotifier()
{
var notificationMock = new Mock<INotification>();
notificationMock.SetupGet(x => x.NotificationName).Returns("Notification1");
NotificationService.Subscribe(notificationMock.Object);
Assert.That(NotificationService.Observers["Notification1"], Is.Not.Null);
Assert.That(NotificationService.Observers.Count, Is.EqualTo(1));
}
[Test]
[Ignore("Need to rework due to static class")]
public void SubscribeExistingNotifier()
{
var notificationMock1 = new Mock<INotification>();
var notificationMock2 = new Mock<INotification>();
notificationMock1.SetupGet(x => x.NotificationName).Returns("Notification1");
notificationMock2.SetupGet(x => x.NotificationName).Returns("Notification1");
NotificationService.Subscribe(notificationMock1.Object);
Assert.That(NotificationService.Observers["Notification1"], Is.Not.Null);
Assert.That(NotificationService.Observers.Count, Is.EqualTo(1));
NotificationService.Subscribe(notificationMock2.Object);
Assert.That(NotificationService.Observers["Notification1"], Is.Not.Null);
Assert.That(NotificationService.Observers.Count, Is.EqualTo(1));
}
[Test]
[Ignore("Need to rework due to static class")]
public void UnSubscribeMissingNotifier()
{
var notificationMock = new Mock<INotification>();
notificationMock.SetupGet(x => x.NotificationName).Returns("Notification1");
NotificationService.UnSubscribe(notificationMock.Object);
Assert.That(NotificationService.Observers.Count, Is.EqualTo(0));
}
[Test]
[Ignore("Need to rework due to static class")]
public void UnSubscribeNotifier()
{
var notificationMock = new Mock<INotification>();
notificationMock.SetupGet(x => x.NotificationName).Returns("Notification1");
NotificationService.Subscribe(notificationMock.Object);
Assert.That(NotificationService.Observers.Count, Is.EqualTo(1));
NotificationService.UnSubscribe(notificationMock.Object);
Assert.That(NotificationService.Observers.Count, Is.EqualTo(0));
}
[Test]
[Ignore("Need to rework due to static class")]
public void PublishWithNoObservers()
{
Assert.DoesNotThrow(
() =>
{ NotificationService.Publish(string.Empty, string.Empty); });
}
[Test]
[Ignore("Need to rework due to static class")]
public void PublishAllNotifiers()
{
var notificationMock1 = new Mock<INotification>();
var notificationMock2 = new Mock<INotification>();
notificationMock1.SetupGet(x => x.NotificationName).Returns("Notification1");
notificationMock2.SetupGet(x => x.NotificationName).Returns("Notification2");
NotificationService.Subscribe(notificationMock1.Object);
NotificationService.Subscribe(notificationMock2.Object);
Assert.That(NotificationService.Observers.Count, Is.EqualTo(2));
NotificationService.Publish("a","b");
notificationMock1.Verify(x => x.Notify("a","b"), Times.Once);
notificationMock2.Verify(x => x.Notify("a","b"), Times.Once);
}
}
}

View file

@ -55,6 +55,7 @@
<Otherwise /> <Otherwise />
</Choose> </Choose>
<ItemGroup> <ItemGroup>
<Compile Include="NotificationServiceTests.cs" />
<Compile Include="PlexAvailabilityCheckerTests.cs" /> <Compile Include="PlexAvailabilityCheckerTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>

View file

@ -26,8 +26,6 @@
#endregion #endregion
using System; using System;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Runtime.Remoting.Messaging;
using System.Threading.Tasks;
using System.Web.Hosting; using System.Web.Hosting;
using FluentScheduler; using FluentScheduler;
@ -51,7 +49,7 @@ namespace PlexRequests.Services
public AvailabilityUpdateService() public AvailabilityUpdateService()
{ {
ConfigurationReader = new ConfigurationReader(); ConfigurationReader = new ConfigurationReader();
var repo = new JsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider()); var repo = new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider());
Checker = new PlexAvailabilityChecker(new SettingsServiceV2<PlexSettings>(repo), new SettingsServiceV2<AuthenticationSettings>(repo), new RequestService(new GenericRepository<RequestedModel>(new DbConfiguration(new SqliteFactory()))), new PlexApi()); Checker = new PlexAvailabilityChecker(new SettingsServiceV2<PlexSettings>(repo), new SettingsServiceV2<AuthenticationSettings>(repo), new RequestService(new GenericRepository<RequestedModel>(new DbConfiguration(new SqliteFactory()))), new PlexApi());
HostingEnvironment.RegisterObject(this); HostingEnvironment.RegisterObject(this);
} }
@ -76,7 +74,7 @@ namespace PlexRequests.Services
public void Stop(bool immediate) public void Stop(bool immediate)
{ {
throw new System.NotImplementedException(); HostingEnvironment.UnregisterObject(this);
} }
} }

View file

@ -30,8 +30,6 @@ namespace PlexRequests.Services.Interfaces
{ {
public interface IIntervals public interface IIntervals
{ {
TimeSpan CriticalNotification { get; } // notification interval for critical load
TimeSpan Measurement { get; } // how often to measure
TimeSpan Notification { get; } // notification interval for high load TimeSpan Notification { get; } // notification interval for high load
} }
} }

View file

@ -0,0 +1,108 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: EmailMessageNotification.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 System.Net;
using System.Net.Mail;
using NLog;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels;
namespace PlexRequests.Services.Notification
{
public class EmailMessageNotification : INotification
{
public EmailMessageNotification(ISettingsService<EmailNotificationSettings> settings)
{
EmailNotificationSettings = settings;
}
private static Logger Log = LogManager.GetCurrentClassLogger();
private ISettingsService<EmailNotificationSettings> EmailNotificationSettings { get; }
public string NotificationName => "EmailMessageNotification";
public bool Notify(string title, string requester)
{
var configuration = GetConfiguration();
if (!ValidateConfiguration(configuration))
{
return false;
}
var message = new MailMessage
{
IsBodyHtml = true,
To = { new MailAddress(configuration.RecipientEmail) },
Body = $"User {requester} has requested {title}!",
From = new MailAddress(configuration.EmailUsername),
Subject = $"New Request for {title}!"
};
try
{
using (var smtp = new SmtpClient(configuration.EmailHost, configuration.EmailPort))
{
smtp.Credentials = new NetworkCredential(configuration.EmailUsername, configuration.EmailPassword);
smtp.EnableSsl = configuration.Ssl;
smtp.Send(message);
return true;
}
}
catch (SmtpException smtp)
{
Log.Fatal(smtp);
}
catch (Exception e)
{
Log.Fatal(e);
}
return false;
}
private EmailNotificationSettings GetConfiguration()
{
var settings = EmailNotificationSettings.GetSettings();
return settings;
}
private bool ValidateConfiguration(EmailNotificationSettings settings)
{
if (!settings.Enabled)
{
return false;
}
if (string.IsNullOrEmpty(settings.EmailHost) || string.IsNullOrEmpty(settings.EmailUsername)
|| string.IsNullOrEmpty(settings.EmailPassword) || string.IsNullOrEmpty(settings.RecipientEmail)
|| string.IsNullOrEmpty(settings.EmailPort.ToString()))
{
return false;
}
return true;
}
}
}

View file

@ -0,0 +1,46 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: INotification.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.Services.Notification
{
public interface INotification
{
/// <summary>
/// Gets the name of the notification.
/// </summary>
/// <value>
/// The name of the notification.
/// </value>
string NotificationName { get; }
/// <summary>
/// Notifies the specified title.
/// </summary>
/// <param name="title">The title.</param>
/// <param name="requester">The requester.</param>
/// <returns></returns>
bool Notify(string title, string requester);
}
}

View file

@ -0,0 +1,90 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: NotificationService.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.Collections.Generic;
using System.Threading;
using NLog;
using PlexRequests.Helpers;
namespace PlexRequests.Services.Notification
{
public static class NotificationService
{
private static Logger Log = LogManager.GetCurrentClassLogger();
public static Dictionary<string, INotification> Observers { get; }
static NotificationService()
{
Observers = new Dictionary<string, INotification>();
}
public static void Publish(string title, string requester)
{
Log.Trace("Notifying all observers: ");
Log.Trace(Observers.DumpJson());
foreach (var observer in Observers)
{
var notification = observer.Value;
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
notification.Notify(title, requester);
}).Start();
}
}
public static void Subscribe(INotification notification)
{
Log.Trace("Subscribing Observer {0}", notification.NotificationName);
INotification notificationValue;
if (Observers.TryGetValue(notification.NotificationName, out notificationValue))
{
Log.Trace("Observer {0} already exists", notification.NotificationName);
// Observer already exists
return;
}
Observers[notification.NotificationName] = notification;
}
public static void UnSubscribe(INotification notification)
{
Log.Trace("Unsubscribing Observer {0}", notification.NotificationName);
INotification notificationValue;
if (!Observers.TryGetValue(notification.NotificationName, out notificationValue))
{
Log.Trace("Observer {0} doesn't exist to Unsubscribe", notification.NotificationName);
// Observer doesn't exists
return;
}
Observers.Remove(notification.NotificationName);
}
}
}

View file

@ -0,0 +1,80 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: PushbulletNotification.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 NLog;
using PlexRequests.Api.Interfaces;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels;
namespace PlexRequests.Services.Notification
{
public class PushbulletNotification : INotification
{
public PushbulletNotification(IPushbulletApi pushbulletApi, ISettingsService<PushbulletNotificationSettings> settings)
{
PushbulletApi = pushbulletApi;
Settings = settings;
}
private IPushbulletApi PushbulletApi { get; }
private ISettingsService<PushbulletNotificationSettings> Settings { get; }
private static Logger Log = LogManager.GetCurrentClassLogger();
public string NotificationName => "PushbulletNotification";
public bool Notify(string title, string requester)
{
var settings = GetSettings();
if (!settings.Enabled)
{
return false;
}
var message = $"{title} has been requested by {requester}";
var pushTitle = $"Plex Requests: {title}";
try
{
var result = PushbulletApi.Push(settings.AccessToken, pushTitle, message, settings.DeviceIdentifier);
if (result != null)
{
return true;
}
}
catch (Exception e)
{
Log.Fatal(e);
}
return false;
}
private PushbulletNotificationSettings GetSettings()
{
return Settings.GetSettings();
}
}
}

View file

@ -77,6 +77,10 @@
<Compile Include="Interfaces\IAvailabilityChecker.cs" /> <Compile Include="Interfaces\IAvailabilityChecker.cs" />
<Compile Include="Interfaces\IConfigurationReader.cs" /> <Compile Include="Interfaces\IConfigurationReader.cs" />
<Compile Include="Interfaces\IIntervals.cs" /> <Compile Include="Interfaces\IIntervals.cs" />
<Compile Include="Notification\INotification.cs" />
<Compile Include="Notification\EmailMessageNotification.cs" />
<Compile Include="Notification\NotificationService.cs" />
<Compile Include="Notification\PushbulletNotification.cs" />
<Compile Include="PlexAvailabilityChecker.cs" /> <Compile Include="PlexAvailabilityChecker.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UpdateInterval.cs" /> <Compile Include="UpdateInterval.cs" />

View file

@ -32,9 +32,7 @@ namespace PlexRequests.Services
{ {
public class UpdateInterval : IIntervals public class UpdateInterval : IIntervals
{ {
public TimeSpan Measurement => TimeSpan.FromSeconds(1); public TimeSpan Notification => TimeSpan.FromMinutes(5);
public TimeSpan CriticalNotification { get; }
public TimeSpan Notification => TimeSpan.FromMinutes(2);
} }
} }

View file

@ -0,0 +1,65 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: ISettingsRepository.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.Collections.Generic;
using PlexRequests.Store.Models;
namespace PlexRequests.Store
{
public interface IRequestRepository
{
/// <summary>
/// Inserts the specified entity.
/// </summary>
/// <param name="entity">The entity.</param>
long Insert(RequestBlobs entity);
/// <summary>
/// Gets all.
/// </summary>
/// <returns></returns>
IEnumerable<RequestBlobs> GetAll();
RequestBlobs Get(int id);
/// <summary>
/// Deletes the specified entity.
/// </summary>
/// <param name="entity">The entity.</param>
/// <returns></returns>
bool Delete(RequestBlobs entity);
/// <summary>
/// Updates the specified entity.
/// </summary>
/// <param name="entity">The entity.</param>
/// <returns></returns>
bool Update(RequestBlobs entity);
bool UpdateAll(IEnumerable<RequestBlobs> entity);
}
}

View file

@ -0,0 +1,38 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: RequestBlobs.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 Dapper.Contrib.Extensions;
namespace PlexRequests.Store.Models
{
[Table("RequestBlobs")]
public class RequestBlobs : Entity
{
public int ProviderId { get; set; }
public byte[] Content { get; set; }
public RequestType Type { get; set; }
}
}

View file

@ -58,13 +58,16 @@
<ItemGroup> <ItemGroup>
<Compile Include="DbConfiguration.cs" /> <Compile Include="DbConfiguration.cs" />
<Compile Include="Entity.cs" /> <Compile Include="Entity.cs" />
<Compile Include="IRequestRepository.cs" />
<Compile Include="ISettingsRepository.cs" /> <Compile Include="ISettingsRepository.cs" />
<Compile Include="ISqliteConfiguration.cs" /> <Compile Include="ISqliteConfiguration.cs" />
<Compile Include="IRepository.cs" /> <Compile Include="IRepository.cs" />
<Compile Include="Models\GlobalSettings.cs" /> <Compile Include="Models\GlobalSettings.cs" />
<Compile Include="Models\LogEntity.cs" /> <Compile Include="Models\LogEntity.cs" />
<Compile Include="Models\RequestBlobs.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Repository\JsonRepository.cs" /> <Compile Include="Repository\SettingsJsonRepository.cs" />
<Compile Include="Repository\RequestJsonRepository.cs" />
<Compile Include="SettingsModel.cs" /> <Compile Include="SettingsModel.cs" />
<Compile Include="GenericRepository.cs" /> <Compile Include="GenericRepository.cs" />
<Compile Include="RequestedModel.cs" /> <Compile Include="RequestedModel.cs" />

View file

@ -0,0 +1,127 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: SettingsJsonRepository.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.Collections.Generic;
using System.Linq;
using Dapper.Contrib.Extensions;
using PlexRequests.Helpers;
using PlexRequests.Store.Models;
namespace PlexRequests.Store.Repository
{
public class RequestJsonRepository : IRequestRepository
{
private ICacheProvider Cache { get; }
private string TypeName { get; }
public RequestJsonRepository(ISqliteConfiguration config, ICacheProvider cacheProvider)
{
Db = config;
Cache = cacheProvider;
TypeName = typeof(RequestJsonRepository).Name;
}
private ISqliteConfiguration Db { get; }
public long Insert(RequestBlobs entity)
{
ResetCache();
using (var con = Db.DbConnection())
{
return con.Insert(entity);
}
}
public IEnumerable<RequestBlobs> GetAll()
{
var key = TypeName + "GetAll";
var item = Cache.GetOrSet(key, () =>
{
using (var con = Db.DbConnection())
{
var page = con.GetAll<RequestBlobs>();
return page;
}
}, 5);
return item;
}
public RequestBlobs Get(int id)
{
var key = TypeName + "Get";
var item = Cache.GetOrSet(key, () =>
{
using (var con = Db.DbConnection())
{
var page = con.Get<RequestBlobs>(id);
return page;
}
}, 5);
return item;
}
public bool Delete(RequestBlobs entity)
{
ResetCache();
using (var con = Db.DbConnection())
{
return con.Delete(entity);
}
}
public bool Update(RequestBlobs entity)
{
ResetCache();
using (var con = Db.DbConnection())
{
return con.Update(entity);
}
}
private void ResetCache()
{
Cache.Remove("Get");
Cache.Remove(TypeName + "GetAll");
}
public bool UpdateAll(IEnumerable<RequestBlobs> entity)
{
var result = new HashSet<bool>();
using (var db = Db.DbConnection())
{
db.Open();
foreach (var e in entity)
{
result.Add(db.Update(e));
}
}
return result.All(x => true);
}
}
}

View file

@ -1,7 +1,7 @@
#region Copyright #region Copyright
// /************************************************************************ // /************************************************************************
// Copyright (c) 2016 Jamie Rees // Copyright (c) 2016 Jamie Rees
// File: JsonRepository.cs // File: SettingsJsonRepository.cs
// Created By: Jamie Rees // Created By: Jamie Rees
// //
// Permission is hereby granted, free of charge, to any person obtaining // Permission is hereby granted, free of charge, to any person obtaining
@ -34,16 +34,16 @@ using PlexRequests.Store.Models;
namespace PlexRequests.Store.Repository namespace PlexRequests.Store.Repository
{ {
public class JsonRepository : ISettingsRepository public class SettingsJsonRepository : ISettingsRepository
{ {
private ICacheProvider Cache { get; set; } private ICacheProvider Cache { get; set; }
private string TypeName { get; set; } private string TypeName { get; set; }
public JsonRepository(ISqliteConfiguration config, ICacheProvider cacheProvider) public SettingsJsonRepository(ISqliteConfiguration config, ICacheProvider cacheProvider)
{ {
Db = config; Db = config;
Cache = cacheProvider; Cache = cacheProvider;
TypeName = typeof(JsonRepository).Name; TypeName = typeof(SettingsJsonRepository).Name;
} }
private ISqliteConfiguration Db { get; set; } private ISqliteConfiguration Db { get; set; }

View file

@ -16,6 +16,7 @@ CREATE TABLE IF NOT EXISTS Settings
PlexAuthToken varchar(50) PlexAuthToken varchar(50)
); );
CREATE TABLE IF NOT EXISTS Requested CREATE TABLE IF NOT EXISTS Requested
( (
Id INTEGER PRIMARY KEY AUTOINCREMENT, Id INTEGER PRIMARY KEY AUTOINCREMENT,
@ -45,6 +46,14 @@ CREATE TABLE IF NOT EXISTS GlobalSettings
Content varchar(100) NOT NULL Content varchar(100) NOT NULL
); );
CREATE TABLE IF NOT EXISTS RequestBlobs
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
ProviderId INTEGER NOT NULL,
Type INTEGER NOT NULL,
Content BLOB NOT NULL
);
CREATE TABLE IF NOT EXISTS Log CREATE TABLE IF NOT EXISTS Log
( (

View file

@ -53,8 +53,10 @@ namespace PlexRequests.UI.Tests
private Mock<ISettingsService<PlexSettings>> PlexSettingsMock { get; set; } private Mock<ISettingsService<PlexSettings>> PlexSettingsMock { get; set; }
private Mock<ISettingsService<SonarrSettings>> SonarrSettingsMock { get; set; } private Mock<ISettingsService<SonarrSettings>> SonarrSettingsMock { get; set; }
private Mock<ISettingsService<EmailNotificationSettings>> EmailMock { get; set; } private Mock<ISettingsService<EmailNotificationSettings>> EmailMock { get; set; }
private Mock<ISettingsService<PushbulletNotificationSettings>> PushbulletSettings { get; set; }
private Mock<IPlexApi> PlexMock { get; set; } private Mock<IPlexApi> PlexMock { get; set; }
private Mock<ISonarrApi> SonarrApiMock { get; set; } private Mock<ISonarrApi> SonarrApiMock { get; set; }
private Mock<IPushbulletApi> PushbulletApi { get; set; }
private ConfigurableBootstrapper Bootstrapper { get; set; } private ConfigurableBootstrapper Bootstrapper { get; set; }
@ -75,6 +77,8 @@ namespace PlexRequests.UI.Tests
SonarrApiMock = new Mock<ISonarrApi>(); SonarrApiMock = new Mock<ISonarrApi>();
SonarrSettingsMock = new Mock<ISettingsService<SonarrSettings>>(); SonarrSettingsMock = new Mock<ISettingsService<SonarrSettings>>();
EmailMock = new Mock<ISettingsService<EmailNotificationSettings>>(); EmailMock = new Mock<ISettingsService<EmailNotificationSettings>>();
PushbulletApi = new Mock<IPushbulletApi>();
PushbulletSettings = new Mock<ISettingsService<PushbulletNotificationSettings>>();
Bootstrapper = new ConfigurableBootstrapper(with => Bootstrapper = new ConfigurableBootstrapper(with =>
{ {
@ -87,6 +91,8 @@ namespace PlexRequests.UI.Tests
with.Dependency(SonarrSettingsMock.Object); with.Dependency(SonarrSettingsMock.Object);
with.Dependency(PlexMock.Object); with.Dependency(PlexMock.Object);
with.Dependency(EmailMock.Object); with.Dependency(EmailMock.Object);
with.Dependency(PushbulletApi.Object);
with.Dependency(PushbulletSettings.Object);
with.RootPathProvider<TestRootPathProvider>(); with.RootPathProvider<TestRootPathProvider>();
with.RequestStartup((container, pipelines, context) => with.RequestStartup((container, pipelines, context) =>
{ {

View file

@ -25,6 +25,7 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System.Net;
using FluentScheduler; using FluentScheduler;
using Mono.Data.Sqlite; using Mono.Data.Sqlite;
@ -44,6 +45,7 @@ using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers; using PlexRequests.Helpers;
using PlexRequests.Services; using PlexRequests.Services;
using PlexRequests.Services.Interfaces; using PlexRequests.Services.Interfaces;
using PlexRequests.Services.Notification;
using PlexRequests.Store; using PlexRequests.Store;
using PlexRequests.Store.Repository; using PlexRequests.Store.Repository;
using PlexRequests.UI.Jobs; using PlexRequests.UI.Jobs;
@ -63,7 +65,7 @@ namespace PlexRequests.UI
container.Register<ISqliteConfiguration, DbConfiguration>(new DbConfiguration(new SqliteFactory())); container.Register<ISqliteConfiguration, DbConfiguration>(new DbConfiguration(new SqliteFactory()));
container.Register<ISettingsRepository, JsonRepository>(); container.Register<ISettingsRepository, SettingsJsonRepository>();
container.Register<ICacheProvider, MemoryCacheProvider>(); container.Register<ICacheProvider, MemoryCacheProvider>();
container.Register<ISettingsService<PlexRequestSettings>, SettingsServiceV2<PlexRequestSettings>>(); container.Register<ISettingsService<PlexRequestSettings>, SettingsServiceV2<PlexRequestSettings>>();
@ -72,6 +74,7 @@ namespace PlexRequests.UI
container.Register<ISettingsService<PlexSettings>, SettingsServiceV2<PlexSettings>>(); container.Register<ISettingsService<PlexSettings>, SettingsServiceV2<PlexSettings>>();
container.Register<ISettingsService<SonarrSettings>, SettingsServiceV2<SonarrSettings>>(); container.Register<ISettingsService<SonarrSettings>, SettingsServiceV2<SonarrSettings>>();
container.Register<ISettingsService<EmailNotificationSettings>, SettingsServiceV2<EmailNotificationSettings>>(); container.Register<ISettingsService<EmailNotificationSettings>, SettingsServiceV2<EmailNotificationSettings>>();
container.Register<ISettingsService<PushbulletNotificationSettings>, SettingsServiceV2<PushbulletNotificationSettings>>();
container.Register<IRepository<RequestedModel>, GenericRepository<RequestedModel>>(); container.Register<IRepository<RequestedModel>, GenericRepository<RequestedModel>>();
container.Register<IRequestService, RequestService>(); container.Register<IRequestService, RequestService>();
@ -80,25 +83,27 @@ namespace PlexRequests.UI
container.Register<IIntervals, UpdateInterval>(); container.Register<IIntervals, UpdateInterval>();
container.Register<ICouchPotatoApi, CouchPotatoApi>(); container.Register<ICouchPotatoApi, CouchPotatoApi>();
container.Register<IPushbulletApi, PushbulletApi>();
container.Register<ISonarrApi, SonarrApi>(); container.Register<ISonarrApi, SonarrApi>();
//container.Register<ISonarrApi, MockSonarrApi>(); //container.Register<ISonarrApi, MockSonarrApi>();
container.Register<IPlexApi, PlexApi>(); container.Register<IPlexApi, PlexApi>();
SubscribeAllObservers(container);
base.ConfigureRequestContainer(container, context); base.ConfigureRequestContainer(container, context);
} }
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{ {
TaskManager.TaskFactory = new Jobs.PlexTaskFactory(); TaskManager.TaskFactory = new PlexTaskFactory();
TaskManager.Initialize(new PlexRegistry()); TaskManager.Initialize(new PlexRegistry());
CookieBasedSessions.Enable(pipelines, CryptographyConfiguration.Default); CookieBasedSessions.Enable(pipelines, CryptographyConfiguration.Default);
StaticConfiguration.DisableErrorTraces = false; StaticConfiguration.DisableErrorTraces = false;
base.ApplicationStartup(container, pipelines); base.ApplicationStartup(container, pipelines);
// Enable forms auth // Enable forms auth
@ -109,8 +114,30 @@ namespace PlexRequests.UI
}; };
FormsAuthentication.Enable(pipelines, formsAuthConfiguration); FormsAuthentication.Enable(pipelines, formsAuthConfiguration);
ServicePointManager.ServerCertificateValidationCallback +=
(sender, certificate, chain, sslPolicyErrors) => true;
} }
protected override DiagnosticsConfiguration DiagnosticsConfiguration => new DiagnosticsConfiguration { Password = @"password" }; protected override DiagnosticsConfiguration DiagnosticsConfiguration => new DiagnosticsConfiguration { Password = @"password" };
private void SubscribeAllObservers(TinyIoCContainer container)
{
var emailSettingsService = container.Resolve<ISettingsService<EmailNotificationSettings>>();
var emailSettings = emailSettingsService.GetSettings();
if (emailSettings.Enabled)
{
NotificationService.Subscribe(new EmailMessageNotification(emailSettingsService));
}
var pushbulletService = container.Resolve<ISettingsService<PushbulletNotificationSettings>>();
var pushbulletSettings = pushbulletService.GetSettings();
if (pushbulletSettings.Enabled)
{
NotificationService.Subscribe(new PushbulletNotification(container.Resolve<IPushbulletApi>(), container.Resolve<ISettingsService<PushbulletNotificationSettings>>()));
}
}
} }
} }

View file

@ -36,10 +36,12 @@ using Nancy.Validation;
using NLog; using NLog;
using PlexRequests.Api;
using PlexRequests.Api.Interfaces; using PlexRequests.Api.Interfaces;
using PlexRequests.Core; using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers; using PlexRequests.Helpers;
using PlexRequests.Services.Notification;
using PlexRequests.UI.Helpers; using PlexRequests.UI.Helpers;
using PlexRequests.UI.Models; using PlexRequests.UI.Models;
@ -53,8 +55,10 @@ namespace PlexRequests.UI.Modules
private ISettingsService<PlexSettings> PlexService { get; } private ISettingsService<PlexSettings> PlexService { get; }
private ISettingsService<SonarrSettings> SonarrService { get; } private ISettingsService<SonarrSettings> SonarrService { get; }
private ISettingsService<EmailNotificationSettings> EmailService { get; } private ISettingsService<EmailNotificationSettings> EmailService { get; }
private ISettingsService<PushbulletNotificationSettings> PushbulletService { get; }
private IPlexApi PlexApi { get; } private IPlexApi PlexApi { get; }
private ISonarrApi SonarrApi { get; } private ISonarrApi SonarrApi { get; }
private PushbulletApi PushbulletApi { get; }
private static Logger Log = LogManager.GetCurrentClassLogger(); private static Logger Log = LogManager.GetCurrentClassLogger();
public AdminModule(ISettingsService<PlexRequestSettings> rpService, public AdminModule(ISettingsService<PlexRequestSettings> rpService,
@ -64,7 +68,9 @@ namespace PlexRequests.UI.Modules
ISettingsService<SonarrSettings> sonarr, ISettingsService<SonarrSettings> sonarr,
ISonarrApi sonarrApi, ISonarrApi sonarrApi,
ISettingsService<EmailNotificationSettings> email, ISettingsService<EmailNotificationSettings> email,
IPlexApi plexApi) : base("admin") IPlexApi plexApi,
ISettingsService<PushbulletNotificationSettings> pbSettings,
PushbulletApi pbApi) : base("admin")
{ {
RpService = rpService; RpService = rpService;
CpService = cpService; CpService = cpService;
@ -74,6 +80,8 @@ namespace PlexRequests.UI.Modules
SonarrApi = sonarrApi; SonarrApi = sonarrApi;
EmailService = email; EmailService = email;
PlexApi = plexApi; PlexApi = plexApi;
PushbulletService = pbSettings;
PushbulletApi = pbApi;
#if !DEBUG #if !DEBUG
this.RequiresAuthentication(); this.RequiresAuthentication();
@ -103,6 +111,9 @@ namespace PlexRequests.UI.Modules
Get["/emailnotification"] = _ => EmailNotifications(); Get["/emailnotification"] = _ => EmailNotifications();
Post["/emailnotification"] = _ => SaveEmailNotifications(); Post["/emailnotification"] = _ => SaveEmailNotifications();
Get["/status"] = _ => Status(); Get["/status"] = _ => Status();
Get["/pushbulletnotification"] = _ => PushbulletNotifications();
Post["/pushbulletnotification"] = _ => SavePushbulletNotifications();
} }
private Negotiator Authentication() private Negotiator Authentication()
@ -294,11 +305,21 @@ namespace PlexRequests.UI.Modules
private Response SaveEmailNotifications() private Response SaveEmailNotifications()
{ {
var settings = this.Bind<EmailNotificationSettings>(); var settings = this.Bind<EmailNotificationSettings>();
var valid = this.Validate(settings);
if (!valid.IsValid)
{
return Response.AsJson(valid.SendJsonError());
}
Log.Trace(settings.DumpJson()); Log.Trace(settings.DumpJson());
var result = EmailService.SaveSettings(settings); var result = EmailService.SaveSettings(settings);
NotificationService.Subscribe(new EmailMessageNotification(EmailService));
Log.Info("Saved email settings, result: {0}", result); Log.Info("Saved email settings, result: {0}", result);
return Context.GetRedirect("~/admin/emailnotification"); return Response.AsJson(result
? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Email Notifications!" }
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
} }
private Negotiator Status() private Negotiator Status()
@ -307,5 +328,31 @@ namespace PlexRequests.UI.Modules
var status = checker.GetStatus(); var status = checker.GetStatus();
return View["Status", status]; return View["Status", status];
} }
private Negotiator PushbulletNotifications()
{
var settings = PushbulletService.GetSettings();
return View["PushbulletNotifications", settings];
}
private Response SavePushbulletNotifications()
{
var settings = this.Bind<PushbulletNotificationSettings>();
var valid = this.Validate(settings);
if (!valid.IsValid)
{
return Response.AsJson(valid.SendJsonError());
}
Log.Trace(settings.DumpJson());
var result = PushbulletService.SaveSettings(settings);
NotificationService.Subscribe(new PushbulletNotification(PushbulletApi, PushbulletService));
Log.Info("Saved email settings, result: {0}", result);
return Response.AsJson(result
? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Pushbullet Notifications!" }
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
}
} }
} }

View file

@ -37,6 +37,7 @@ using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers; using PlexRequests.Helpers;
using PlexRequests.Services.Interfaces; using PlexRequests.Services.Interfaces;
using PlexRequests.Services.Notification;
using PlexRequests.Store; using PlexRequests.Store;
using PlexRequests.UI.Models; using PlexRequests.UI.Models;
@ -235,7 +236,8 @@ namespace PlexRequests.UI.Modules
{ {
Log.Debug("Adding movie to database requests"); Log.Debug("Adding movie to database requests");
var id = RequestService.AddRequest(movieId, model); var id = RequestService.AddRequest(movieId, model);
//BackgroundJob.Enqueue(() => Checker.CheckAndUpdate(model.Title, (int)id));
NotificationService.Publish(model.Title, model.RequestedBy);
return Response.AsJson(new JsonResponseModel { Result = true }); return Response.AsJson(new JsonResponseModel { Result = true });
} }
@ -291,7 +293,6 @@ namespace PlexRequests.UI.Modules
LatestTv = latest LatestTv = latest
}; };
RequestService.AddRequest(showId, model);
var settings = PrService.GetSettings(); var settings = PrService.GetSettings();
if (!settings.RequireApproval) if (!settings.RequireApproval)
@ -302,10 +303,19 @@ namespace PlexRequests.UI.Modules
var result = SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile, var result = SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile,
sonarrSettings.SeasonFolders, sonarrSettings.RootPath, model.LatestTv, sonarrSettings.ApiKey, sonarrSettings.SeasonFolders, sonarrSettings.RootPath, model.LatestTv, sonarrSettings.ApiKey,
sonarrSettings.FullUri); sonarrSettings.FullUri);
Log.Info("Added series {0} to Sonarr, Result: {1}", model.Title, result); if (result != null)
Log.Trace("Model sent to Sonarr: "); {
Log.Trace(model.DumpJson()); model.Approved = true;
Log.Debug("Adding tv to database requests (No approval required)");
RequestService.AddRequest(showId, model);
return Response.AsJson(new JsonResponseModel { Result = true });
} }
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something went wrong adding the movie to CouchPotato! Please check your settings." });
}
RequestService.AddRequest(showId, model);
NotificationService.Publish(model.Title, model.RequestedBy);
return Response.AsJson(new { Result = true }); return Response.AsJson(new { Result = true });
} }

View file

@ -161,6 +161,8 @@
<ItemGroup> <ItemGroup>
<Compile Include="Bootstrapper.cs" /> <Compile Include="Bootstrapper.cs" />
<Compile Include="Helpers\ValidationHelper.cs" /> <Compile Include="Helpers\ValidationHelper.cs" />
<Compile Include="Validators\PushbulletSettingsValidator.cs" />
<Compile Include="Validators\EmailNotificationSettingsValidator.cs" />
<Compile Include="Validators\CouchPotatoValidator.cs" /> <Compile Include="Validators\CouchPotatoValidator.cs" />
<Compile Include="Validators\PlexValidator.cs" /> <Compile Include="Validators\PlexValidator.cs" />
<Compile Include="Validators\SonarrValidator.cs" /> <Compile Include="Validators\SonarrValidator.cs" />
@ -315,6 +317,9 @@
<Content Include="Views\Admin\Status.cshtml"> <Content Include="Views\Admin\Status.cshtml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Views\Admin\PushbulletNotifications.cshtml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="Web.Debug.config"> <None Include="Web.Debug.config">
<DependentUpon>web.config</DependentUpon> <DependentUpon>web.config</DependentUpon>
</None> </None>

View file

@ -105,7 +105,7 @@ namespace PlexRequests.UI
{ {
Log.Trace("Getting startup URI"); Log.Trace("Getting startup URI");
var uri = "http://*:3579/"; var uri = "http://*:3579/";
var service = new SettingsServiceV2<PlexRequestSettings>(new JsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider())); var service = new SettingsServiceV2<PlexRequestSettings>(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider()));
var settings = service.GetSettings(); var settings = service.GetSettings();
Log.Trace("Port: {0}", settings.Port); Log.Trace("Port: {0}", settings.Port);
if (settings.Port != 0) if (settings.Port != 0)

View file

@ -0,0 +1,46 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: SonarrValidator.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 FluentValidation;
using PlexRequests.Core.SettingModels;
namespace PlexRequests.UI.Validators
{
public class EmailNotificationSettingsValidator : AbstractValidator<EmailNotificationSettings>
{
public EmailNotificationSettingsValidator()
{
RuleFor(request => request.EmailHost).NotEmpty().WithMessage("You must specify a Host name.");
RuleFor(request => request.EmailPort).NotEmpty().WithMessage("You must specify a Port.");
RuleFor(request => request.RecipientEmail).NotEmpty().WithMessage("You must specify a Recipient.");
RuleFor(request => request.RecipientEmail).EmailAddress().WithMessage("You must specify a valid Recipient.");
RuleFor(request => request.EmailUsername).EmailAddress().WithMessage("You must specify a valid Username.");
RuleFor(request => request.EmailUsername).NotEmpty().WithMessage("You must specify a Username.");
RuleFor(request => request.EmailPassword).NotEmpty().WithMessage("You must specify a valid password.");
}
}
}

View file

@ -0,0 +1,40 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: SonarrValidator.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 FluentValidation;
using PlexRequests.Core.SettingModels;
namespace PlexRequests.UI.Validators
{
public class PushbulletSettingsValidator : AbstractValidator<PushbulletNotificationSettings>
{
public PushbulletSettingsValidator()
{
RuleFor(request => request.AccessToken).NotEmpty().WithMessage("You must specify a Access Token.");
}
}
}

View file

@ -29,6 +29,20 @@
</label> </label>
</div> </div>
</div> </div>
<div class="form-group">
<div class="checkbox">
<label>
@if (Model.Ssl)
{
<input type="checkbox" id="Ssl" name="Ssl" checked="checked"><text>SSL Enabled</text>
}
else
{
<input type="checkbox" id="Ssl" name="Ssl"><text>SSL Enabled</text>
}
</label>
</div>
</div>
<div class="form-group"> <div class="form-group">
<label for="EmailHost" class="control-label">SMTP Hostname or IP</label> <label for="EmailHost" class="control-label">SMTP Hostname or IP</label>
<div class=""> <div class="">
@ -52,21 +66,6 @@
</div> </div>
</div> </div>
<div class="form-group">
<div class="checkbox">
<label>
@if (Model.EmailAuthentication)
{
<input type="checkbox" id="EmailAuthentication" name="EmailAuthentication" checked="checked"><text>Authenticate</text>
}
else
{
<input type="checkbox" id="EmailAuthentication" name="EmailAuthentication"><text>Authenticate</text>
}
</label>
</div>
</div>
<div class="form-group"> <div class="form-group">
<label for="EmailUsername" class="control-label">Username</label> <label for="EmailUsername" class="control-label">Username</label>
<div> <div>
@ -84,10 +83,45 @@
<div class="form-group"> <div class="form-group">
<div> <div>
<button type="submit" class="btn btn-primary-outline">Submit</button> <button id="save" type="submit" class="btn btn-primary-outline">Submit</button>
</div> </div>
</div> </div>
</fieldset> </fieldset>
</form> </form>
</div> </div>
<script>
$(function () {
$('#save').click(function (e) {
e.preventDefault();
var port = $('#EmailPort').val();
if (isNaN(port)) {
generateNotify("You must specify a valid Port.", "warning");
return;
}
var $form = $("#mainForm");
$.ajax({
type: $form.prop("method"),
data: $form.serialize(),
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>

View file

@ -29,6 +29,20 @@
<input type="text" class="form-control form-control-custom " id="portNumber" name="Port" placeholder="Port Number" value="@port"> <input type="text" class="form-control form-control-custom " id="portNumber" name="Port" placeholder="Port Number" value="@port">
</div> </div>
</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"> <div class="form-group">
<div> <div>
<button id="testPlex" type="submit" class="btn btn-primary-outline">Test Connectivity</button> <button id="testPlex" type="submit" class="btn btn-primary-outline">Test Connectivity</button>
@ -46,7 +60,7 @@
<script> <script>
$(function() { $(function () {
$('#testPlex').click(function (e) { $('#testPlex').click(function (e) {
e.preventDefault(); e.preventDefault();

View file

@ -0,0 +1,74 @@
@Html.Partial("_Sidebar")
<div class="col-sm-8 col-sm-push-1">
<form class="form-horizontal" method="POST" id="mainForm">
<fieldset>
<legend>Pushbullet Notifications</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">
<label for="AccessToken" class="control-label">Access Token</label>
<small class="control-label">You can get this by navigating to <a href="https://www.pushbullet.com/#settings">Pushbullet</a></small>
<div class="">
<input type="text" class="form-control form-control-custom " id="AccessToken" name="AccessToken" value="@Model.AccessToken">
</div>
</div>
<div class="form-group">
<label for="DeviceIdentifier" class="control-label">Device Identifier</label>
<small class="control-label">This is optional, if left blank we will send a Push notification to all devices.</small>
<div class="">
<input type="text" class="form-control form-control-custom " id="DeviceIdentifier" name="DeviceIdentifier" value="@Model.DeviceIdentifier">
</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 () {
$('#save').click(function (e) {
e.preventDefault();
var $form = $("#mainForm");
$.ajax({
type: $form.prop("method"),
data: $form.serialize(),
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>

View file

@ -46,14 +46,23 @@
} }
@*<a class="list-group-item" href="/admin/sickbeard">Sickbeard Settings</a>*@ @*<a class="list-group-item" href="/admin/sickbeard">Sickbeard Settings</a>*@
@*@if (Context.Request.Path == "/admin/emailnotification") @if (Context.Request.Path == "/admin/emailnotification")
{ {
<a class="list-group-item active" href="/admin/emailnotification">Email Notifications</a> <a class="list-group-item active" href="/admin/emailnotification">Email Notifications</a>
} }
else else
{ {
<a class="list-group-item" href="/admin/emailnotification">Email Notifications</a> <a class="list-group-item" href="/admin/emailnotification">Email Notifications</a>
}*@ }
@if (Context.Request.Path == "/admin/pushbulletnotification")
{
<a class="list-group-item active" href="/admin/pushbulletnotification">Pushbullet Notifications</a>
}
else
{
<a class="list-group-item" href="/admin/pushbulletnotification">Pushbullet Notifications</a>
}
@if (Context.Request.Path == "/admin/status") @if (Context.Request.Path == "/admin/status")
{ {

View file

@ -3,6 +3,9 @@
[![Gitter](https://badges.gitter.im/tidusjar/PlexRequest.NET.svg)](https://gitter.im/tidusjar/PlexRequests.Net?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Gitter](https://badges.gitter.im/tidusjar/PlexRequest.NET.svg)](https://gitter.im/tidusjar/PlexRequests.Net?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Build status](https://ci.appveyor.com/api/projects/status/hgj8j6lcea7j0yhn?svg=true)](https://ci.appveyor.com/project/tidusjar/requestplex) [![Build status](https://ci.appveyor.com/api/projects/status/hgj8j6lcea7j0yhn?svg=true)](https://ci.appveyor.com/project/tidusjar/requestplex)
[![Linux Status](https://travis-ci.org/tidusjar/PlexRequests.Net.svg)](https://travis-ci.org/tidusjar/PlexRequests.Net) [![Linux Status](https://travis-ci.org/tidusjar/PlexRequests.Net.svg)](https://travis-ci.org/tidusjar/PlexRequests.Net)
[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/tidusjar/plexrequests.net.svg)](http://isitmaintained.com/project/tidusjar/plexrequests.net "Average time to resolve an issue")
[![Percentage of issues still open](http://isitmaintained.com/badge/open/tidusjar/plexrequests.net.svg)](http://isitmaintained.com/project/tidusjar/plexrequests.net "Percentage of issues still open")
[![Github All Releases](https://img.shields.io/github/downloads/tidusjar/PlexRequests.net/total.svg)](https://github.com/tidusjar/PlexRequests.Net)
This is based off [Plex Requests by lokenx](https://github.com/lokenx/plexrequests-meteor) so big props to that guy! This is based off [Plex Requests by lokenx](https://github.com/lokenx/plexrequests-meteor) so big props to that guy!
I wanted to write a similar application in .Net! I wanted to write a similar application in .Net!

View file

@ -1,11 +1,11 @@
version: 1.2.{build} version: 1.3.{build}
configuration: Release configuration: Release
assembly_info: assembly_info:
patch: true patch: true
file: '**\AssemblyInfo.*' file: '**\AssemblyInfo.*'
assembly_version: '1.2.1' assembly_version: '1.3.0'
assembly_file_version: '{version}' assembly_file_version: '{version}'
assembly_informational_version: '1.2.1' assembly_informational_version: '1.3.0'
before_build: before_build:
- cmd: appveyor-retry nuget restore - cmd: appveyor-retry nuget restore
build: build: