(Db);
+ return repo.GetAll();
+ }
+ }
+}
diff --git a/PlexRequests.Core/app.config b/PlexRequests.Core/app.config
new file mode 100644
index 000000000..ee433ca9b
--- /dev/null
+++ b/PlexRequests.Core/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PlexRequests.Core/packages.config b/PlexRequests.Core/packages.config
new file mode 100644
index 000000000..ddcb2361b
--- /dev/null
+++ b/PlexRequests.Core/packages.config
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PlexRequests.Helpers.Tests/AssemblyHelperTests.cs b/PlexRequests.Helpers.Tests/AssemblyHelperTests.cs
new file mode 100644
index 000000000..b55443e61
--- /dev/null
+++ b/PlexRequests.Helpers.Tests/AssemblyHelperTests.cs
@@ -0,0 +1,41 @@
+#region Copyright
+// /************************************************************************
+// Copyright (c) 2016 Jamie Rees
+// File: AssemblyHelperTests.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 NUnit.Framework;
+
+namespace PlexRequests.Helpers.Tests
+{
+ [TestFixture]
+ public class AssemblyHelperTests
+ {
+ [Test]
+ public void GetReleaseVersionTest()
+ {
+ var result = AssemblyHelper.GetProductVersion();
+ Assert.That(result, Is.Not.Null);
+ }
+ }
+}
\ No newline at end of file
diff --git a/PlexRequests.Helpers.Tests/HtmlRemoverTests.cs b/PlexRequests.Helpers.Tests/HtmlRemoverTests.cs
new file mode 100644
index 000000000..6a56a20ae
--- /dev/null
+++ b/PlexRequests.Helpers.Tests/HtmlRemoverTests.cs
@@ -0,0 +1,56 @@
+#region Copyright
+
+// /************************************************************************
+// Copyright (c) 2016 Jamie Rees
+// File: HtmlRemoverTests.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 NUnit.Framework;
+
+namespace PlexRequests.Helpers.Tests
+{
+ [TestFixture]
+ public class HtmlRemoverTests
+ {
+ [Test]
+ public void RemoveHtmlBasic()
+ {
+ var html = "this is bold para
OK!";
+ var result = html.RemoveHtml();
+ Assert.That(result, Is.Not.Null);
+ Assert.That(result, Is.EqualTo("this is bold para OK!"));
+ }
+
+ [Test]
+ public void RemoveHtmlMoreTags()
+ {
+ // Good 'ol Ali G ;)
+ var html = "\"Ali G: Rezurection\" includes every episode of Da Ali G Show with new, original introductions by star, creator/writer Sacha Baron Cohen, along with the BAFTA(R) Award-winning English episodes of Da Ali G Show which have never aired on American television and The Best of Ali G.
";
+ var result = html.RemoveHtml();
+ Assert.That(result, Is.Not.Null);
+ Assert.That(result, Is.EqualTo("\"Ali G: Rezurection\" includes every episode of Da Ali G Show with new, original introductions by star, creator/writer Sacha Baron Cohen, along with the BAFTA(R) Award-winning English episodes of Da Ali G Show which have never aired on American television and The Best of Ali G."));
+ }
+ }
+}
\ No newline at end of file
diff --git a/PlexRequests.Helpers.Tests/PasswordHasherTests.cs b/PlexRequests.Helpers.Tests/PasswordHasherTests.cs
new file mode 100644
index 000000000..500d07534
--- /dev/null
+++ b/PlexRequests.Helpers.Tests/PasswordHasherTests.cs
@@ -0,0 +1,50 @@
+#region Copyright
+// /************************************************************************
+// Copyright (c) 2016 Jamie Rees
+// File: AssemblyHelperTests.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.Diagnostics;
+
+using NUnit.Framework;
+
+namespace PlexRequests.Helpers.Tests
+{
+ [TestFixture]
+ public class PasswordHasherTests
+ {
+ [Test]
+ public void TestHash()
+ {
+ var password = "abcdef";
+ var salt = PasswordHasher.GenerateSalt();
+ var hash = PasswordHasher.ComputeHash(password, salt);
+
+ Assert.That(hash, Is.Not.EqualTo(password));
+
+ var match = PasswordHasher.VerifyPassword(password, salt, hash);
+
+ Assert.That(match, Is.True);
+ }
+ }
+}
\ No newline at end of file
diff --git a/PlexRequests.Helpers.Tests/PlexRequests.Helpers.Tests.csproj b/PlexRequests.Helpers.Tests/PlexRequests.Helpers.Tests.csproj
new file mode 100644
index 000000000..bb9e7143b
--- /dev/null
+++ b/PlexRequests.Helpers.Tests/PlexRequests.Helpers.Tests.csproj
@@ -0,0 +1,116 @@
+
+
+
+ Debug
+ AnyCPU
+ {0E6395D3-B074-49E8-898D-0EB99E507E0E}
+ Library
+ Properties
+ PlexRequests.Helpers.Tests
+ PlexRequests.Helpers.Tests
+ v4.6
+ 512
+ {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 10.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+ $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
+ False
+ UnitTest
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll
+ True
+
+
+ ..\packages\Microsoft.Owin.Host.SystemWeb.3.0.0\lib\net45\Microsoft.Owin.Host.SystemWeb.dll
+ True
+
+
+ ..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll
+ True
+
+
+ ..\packages\NUnit.3.0.1\lib\net45\nunit.framework.dll
+ True
+
+
+ ..\packages\Owin.1.0\lib\net40\Owin.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {1252336d-42a3-482a-804c-836e60173dfa}
+ PlexRequests.Helpers
+
+
+
+
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PlexRequests.Helpers.Tests/Properties/AssemblyInfo.cs b/PlexRequests.Helpers.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..1bebc4548
--- /dev/null
+++ b/PlexRequests.Helpers.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,37 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("PlexRequests.Helpers.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("PlexRequests.Helpers.Tests")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("0e6395d3-b074-49e8-898d-0eb99e507e0e")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: AssemblyInformationalVersionAttribute("1.0.0.0")]
diff --git a/PlexRequests.Helpers.Tests/UriHelperTests.cs b/PlexRequests.Helpers.Tests/UriHelperTests.cs
new file mode 100644
index 000000000..93fb32997
--- /dev/null
+++ b/PlexRequests.Helpers.Tests/UriHelperTests.cs
@@ -0,0 +1,105 @@
+#region Copyright
+// /************************************************************************
+// Copyright (c) 2016 Jamie Rees
+// File: UriHelperTests.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.Linq.Expressions;
+using NUnit.Framework;
+
+namespace PlexRequests.Helpers.Tests
+{
+ [TestFixture]
+ public class UriHelperTests
+ {
+ [TestCaseSource(nameof(UriData))]
+ public void CreateUri1(string uri, Uri expected)
+ {
+ var result = uri.ReturnUri();
+
+ 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))]
+ public void CreateUri2(string uri, int port, Uri expected)
+ {
+ var result = uri.ReturnUri(port);
+
+ Assert.That(result, Is.EqualTo(expected));
+ }
+
+ [TestCaseSource(nameof(UriDataWithSubDir))]
+ public void CreateUriWithSubDir(string uri, int port, bool ssl, string subDir, Uri expected)
+ {
+ var result = uri.ReturnUriWithSubDir(port, ssl, subDir);
+
+ Assert.That(result, Is.EqualTo(expected));
+ }
+
+ static readonly object[] UriData =
+ {
+ new object[] { "google.com", new Uri("http://google.com/"), },
+ new object[] { "http://google.com", new Uri("http://google.com/"), },
+ new object[] { "https://google.com", new Uri("https://google.com/"), },
+ new object[] { "192.168.1.1", new Uri("http://192.168.1.1")},
+ new object[] { "0.0.0.0:5533", new Uri("http://0.0.0.0:5533")},
+ new object[] {"www.google.com", new Uri("http://www.google.com/")},
+ new object[] {"http://www.google.com/", new Uri("http://www.google.com/") },
+ new object[] {"https://www.google.com", new Uri("https://www.google.com/") },
+ new object[] {"www.google.com:443", new Uri("http://www.google.com:443/") },
+ new object[] {"https://www.google.com:443", new Uri("https://www.google.com:443/") },
+ new object[] {"http://www.google.com:443/id=2", new Uri("http://www.google.com:443/id=2") },
+ new object[] {"www.google.com:4438/id=22", new Uri("http://www.google.com:4438/id=22") }
+ };
+
+ static readonly object[] UriDataWithPort =
+ {
+ new object[] {"www.google.com", 80, new Uri("http://www.google.com:80/"), },
+ new object[] {"www.google.com", 443, new Uri("http://www.google.com:443/") },
+ new object[] {"http://www.google.com", 443, new Uri("http://www.google.com:443/") },
+ new object[] {"https://www.google.com", 443, new Uri("https://www.google.com:443/") },
+ new object[] {"http://www.google.com/id=2", 443, new Uri("http://www.google.com:443/id=2") },
+ new object[] {"http://www.google.com/id=2", 443, new Uri("http://www.google.com:443/id=2") },
+ new object[] {"https://www.google.com/id=2", 443, new Uri("https://www.google.com:443/id=2") },
+ };
+
+ static readonly object[] UriDataWithSubDir =
+{
+ new object[] {"www.google.com", 80, false,"test", new Uri("http://www.google.com:80/test"), },
+ new object[] {"www.google.com", 443, false,"test", new Uri("http://www.google.com:443/test") },
+ new object[] {"http://www.google.com", 443, true,"test", new Uri("https://www.google.com:443/test") },
+ new object[] {"https://www.google.com", 443,true,"test", new Uri("https://www.google.com:443/test") },
+ };
+ }
+}
\ No newline at end of file
diff --git a/PlexRequests.Helpers.Tests/app.config b/PlexRequests.Helpers.Tests/app.config
new file mode 100644
index 000000000..6a9f5188b
--- /dev/null
+++ b/PlexRequests.Helpers.Tests/app.config
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PlexRequests.Helpers.Tests/packages.config b/PlexRequests.Helpers.Tests/packages.config
new file mode 100644
index 000000000..def7d9cf6
--- /dev/null
+++ b/PlexRequests.Helpers.Tests/packages.config
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PlexRequests.Helpers/AssemblyHelper.cs b/PlexRequests.Helpers/AssemblyHelper.cs
new file mode 100644
index 000000000..6c450bda8
--- /dev/null
+++ b/PlexRequests.Helpers/AssemblyHelper.cs
@@ -0,0 +1,49 @@
+#region Copyright
+// /************************************************************************
+// Copyright (c) 2016 Jamie Rees
+// File: AssemblyHelper.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.Diagnostics;
+using System.Reflection;
+
+namespace PlexRequests.Helpers
+{
+ public class AssemblyHelper
+ {
+ public static string GetAssemblyVersion()
+ {
+ var assembly = Assembly.GetExecutingAssembly();
+ var fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
+ return fvi.FileVersion;
+ }
+
+ public static string GetProductVersion()
+ {
+ var assembly = Assembly.GetExecutingAssembly();
+ var fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
+ var retVersion = fvi.ProductVersion;
+ return retVersion;
+ }
+ }
+}
\ No newline at end of file
diff --git a/PlexRequests.Helpers/ByteConverterHelper.cs b/PlexRequests.Helpers/ByteConverterHelper.cs
new file mode 100644
index 000000000..87d569592
--- /dev/null
+++ b/PlexRequests.Helpers/ByteConverterHelper.cs
@@ -0,0 +1,54 @@
+#region Copyright
+// /************************************************************************
+// Copyright (c) 2016 Jamie Rees
+// File: ByteConverterHelper.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.Text;
+
+using Newtonsoft.Json;
+
+namespace PlexRequests.Helpers
+{
+ public class ByteConverterHelper
+ {
+ public static byte[] ReturnBytes(object obj)
+ {
+ var json = JsonConvert.SerializeObject(obj);
+ var bytes = Encoding.UTF8.GetBytes(json);
+
+ return bytes;
+ }
+
+ public static T ReturnObject(byte[] bytes)
+ {
+ var json = Encoding.UTF8.GetString(bytes);
+ var model = JsonConvert.DeserializeObject(json);
+ return model;
+ }
+ public static string ReturnFromBytes(byte[] bytes)
+ {
+ return Encoding.UTF8.GetString(bytes);
+ }
+ }
+}
\ No newline at end of file
diff --git a/PlexRequests.Helpers/Exceptions/ApplicationSettingsException.cs b/PlexRequests.Helpers/Exceptions/ApplicationSettingsException.cs
new file mode 100644
index 000000000..6539fc4cb
--- /dev/null
+++ b/PlexRequests.Helpers/Exceptions/ApplicationSettingsException.cs
@@ -0,0 +1,48 @@
+#region Copyright
+// /************************************************************************
+// Copyright (c) 2016 Jamie Rees
+// File: ApplicationSettingsException.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;
+
+namespace PlexRequests.Helpers.Exceptions
+{
+ public class ApplicationSettingsException : Exception
+ {
+
+ public ApplicationSettingsException(string message) : base(message)
+ {
+
+ }
+ public ApplicationSettingsException(string message, Exception innerException) : base(message, innerException)
+ {
+
+ }
+
+ public ApplicationSettingsException()
+ {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/PlexRequests.Helpers/HtmlRemover.cs b/PlexRequests.Helpers/HtmlRemover.cs
new file mode 100644
index 000000000..43d484255
--- /dev/null
+++ b/PlexRequests.Helpers/HtmlRemover.cs
@@ -0,0 +1,45 @@
+#region Copyright
+// /************************************************************************
+// Copyright (c) 2016 Jamie Rees
+// File: HtmlRemover.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.Text.RegularExpressions;
+
+namespace PlexRequests.Helpers
+{
+ public static class HtmlRemover
+ {
+ public static string RemoveHtml(this string value)
+ {
+ if (string.IsNullOrEmpty(value))
+ {
+ return string.Empty;
+ }
+ var step1 = Regex.Replace(value, @"<[^>]+>| ", "").Trim();
+ var step2 = Regex.Replace(step1, @"\s{2,}", " ");
+ return step2;
+ }
+ }
+}
\ No newline at end of file
diff --git a/PlexRequests.Helpers/ICacheProvider.cs b/PlexRequests.Helpers/ICacheProvider.cs
new file mode 100644
index 000000000..3e3a920bb
--- /dev/null
+++ b/PlexRequests.Helpers/ICacheProvider.cs
@@ -0,0 +1,66 @@
+#region Copyright
+// /************************************************************************
+// Copyright (c) 2016 Jamie Rees
+// File: ICacheProvider.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;
+
+namespace PlexRequests.Helpers
+{
+ public interface ICacheProvider
+ {
+ ///
+ /// Gets the item from the cache, if the item is not present
+ /// then we will get that item and store it in the cache.
+ ///
+ /// Type to store in the cache
+ /// The key
+ /// The item callback. This will be called if the item is not present in the cache.
+ /// The amount of time we want to cache the object
+ ///
+ T GetOrSet(string key, Func itemCallback, int cacheTime = 20) where T : class;
+
+ ///
+ /// Gets the specified item from the cache.
+ ///
+ /// Type to get from the cache
+ /// The key.
+ ///
+ T Get(string key) where T : class;
+
+ ///
+ /// Set/Store the specified object in the cache
+ ///
+ /// The key.
+ /// The object we want to store.
+ /// The amount of time we want to cache the object.
+ void Set(string key, object data, int cacheTime);
+
+ ///
+ /// Removes the specified object from the cache.
+ ///
+ /// The key.
+ void Remove(string key);
+ }
+}
\ No newline at end of file
diff --git a/PlexRequests.Helpers/LoggingHelper.cs b/PlexRequests.Helpers/LoggingHelper.cs
new file mode 100644
index 000000000..db7270db3
--- /dev/null
+++ b/PlexRequests.Helpers/LoggingHelper.cs
@@ -0,0 +1,166 @@
+#region Copyright
+// /************************************************************************
+// Copyright (c) 2016 Jamie Rees
+// File: LoggingHelper.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.Data;
+
+using Newtonsoft.Json;
+
+using NLog;
+using NLog.Config;
+using NLog.Targets;
+
+namespace PlexRequests.Helpers
+{
+ public static class LoggingHelper
+ {
+ public static string DumpJson(this object value)
+ {
+ var dumpTarget = value;
+ //if this is a string that contains a JSON object, do a round-trip serialization to format it:
+ var stringValue = value as string;
+ if (stringValue != null)
+ {
+ if (stringValue.Trim().StartsWith("{", StringComparison.Ordinal))
+ {
+ var obj = JsonConvert.DeserializeObject(stringValue);
+ dumpTarget = JsonConvert.SerializeObject(obj, Formatting.Indented);
+ }
+ else
+ {
+ dumpTarget = stringValue;
+ }
+ }
+ else
+ {
+ dumpTarget = JsonConvert.SerializeObject(value, Formatting.Indented);
+ }
+ return dumpTarget.ToString();
+ }
+
+ public static void ConfigureLogging(string connectionString)
+ {
+ LogManager.ThrowExceptions = true;
+ // Step 1. Create configuration object
+ var config = new LoggingConfiguration();
+
+ // Step 2. Create targets and add them to the configuration
+ var databaseTarget = new DatabaseTarget
+ {
+ CommandType = CommandType.Text,
+ ConnectionString = connectionString,
+ DBProvider = "Mono.Data.Sqlite.SqliteConnection, Mono.Data.Sqlite, Version=4.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756",
+ Name = "database"
+ };
+
+ var messageParam = new DatabaseParameterInfo { Name = "@Message", Layout = "${message}" };
+ var callsiteParam = new DatabaseParameterInfo { Name = "@Callsite", Layout = "${callsite}" };
+ var levelParam = new DatabaseParameterInfo { Name = "@Level", Layout = "${level}" };
+ var dateParam = new DatabaseParameterInfo { Name = "@Date", Layout = "${date}" };
+ var loggerParam = new DatabaseParameterInfo { Name = "@Logger", Layout = "${logger}" };
+ var exceptionParam = new DatabaseParameterInfo { Name = "@Exception", Layout = "${exception:tostring}" };
+
+ databaseTarget.Parameters.Add(messageParam);
+ databaseTarget.Parameters.Add(callsiteParam);
+ databaseTarget.Parameters.Add(levelParam);
+ databaseTarget.Parameters.Add(dateParam);
+ databaseTarget.Parameters.Add(loggerParam);
+ databaseTarget.Parameters.Add(exceptionParam);
+
+ databaseTarget.CommandText = "INSERT INTO Logs (Date,Level,Logger, Message, Callsite, Exception) VALUES(@Date,@Level,@Logger, @Message, @Callsite, @Exception);";
+ config.AddTarget("database", databaseTarget);
+
+ // Step 4. Define rules
+ var rule1 = new LoggingRule("*", LogLevel.Info, databaseTarget);
+ config.LoggingRules.Add(rule1);
+
+ // Step 5. Activate the configuration
+ LogManager.Configuration = config;
+ }
+
+ public static void ReconfigureLogLevel(LogLevel level)
+ {
+
+ foreach (var rule in LogManager.Configuration.LoggingRules)
+ {
+ // Remove all levels
+ rule.DisableLoggingForLevel(LogLevel.Trace);
+ rule.DisableLoggingForLevel(LogLevel.Info);
+ rule.DisableLoggingForLevel(LogLevel.Debug);
+ rule.DisableLoggingForLevel(LogLevel.Warn);
+ rule.DisableLoggingForLevel(LogLevel.Error);
+ rule.DisableLoggingForLevel(LogLevel.Fatal);
+
+
+ if (level == LogLevel.Trace)
+ {
+ rule.EnableLoggingForLevel(LogLevel.Trace);
+ rule.EnableLoggingForLevel(LogLevel.Info);
+ rule.EnableLoggingForLevel(LogLevel.Debug);
+ rule.EnableLoggingForLevel(LogLevel.Warn);
+ rule.EnableLoggingForLevel(LogLevel.Error);
+ rule.EnableLoggingForLevel(LogLevel.Fatal);
+ }
+ if (level == LogLevel.Info)
+ {
+ rule.EnableLoggingForLevel(LogLevel.Info);
+ rule.EnableLoggingForLevel(LogLevel.Debug);
+ rule.EnableLoggingForLevel(LogLevel.Warn);
+ rule.EnableLoggingForLevel(LogLevel.Error);
+ rule.EnableLoggingForLevel(LogLevel.Fatal);
+ }
+ if (level == LogLevel.Debug)
+ {
+ rule.EnableLoggingForLevel(LogLevel.Debug);
+ rule.EnableLoggingForLevel(LogLevel.Warn);
+ rule.EnableLoggingForLevel(LogLevel.Error);
+ rule.EnableLoggingForLevel(LogLevel.Fatal);
+ }
+ if (level == LogLevel.Warn)
+ {
+ rule.EnableLoggingForLevel(LogLevel.Warn);
+ rule.EnableLoggingForLevel(LogLevel.Error);
+ rule.EnableLoggingForLevel(LogLevel.Fatal);
+ }
+ if (level == LogLevel.Error)
+ {
+ rule.EnableLoggingForLevel(LogLevel.Error);
+ rule.EnableLoggingForLevel(LogLevel.Fatal);
+ }
+ if (level == LogLevel.Fatal)
+ {
+ rule.EnableLoggingForLevel(LogLevel.Fatal);
+ }
+ }
+
+
+ //Call to update existing Loggers created with GetLogger() or
+ //GetCurrentClassLogger()
+ LogManager.ReconfigExistingLoggers();
+ }
+ }
+}
+
diff --git a/PlexRequests.Helpers/MemoryCacheProvider.cs b/PlexRequests.Helpers/MemoryCacheProvider.cs
new file mode 100644
index 000000000..30863c19f
--- /dev/null
+++ b/PlexRequests.Helpers/MemoryCacheProvider.cs
@@ -0,0 +1,105 @@
+#region Copyright
+// /************************************************************************
+// Copyright (c) 2016 Jamie Rees
+// File: MemoryCacheProvider.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.Linq;
+using System.Runtime.Caching;
+
+namespace PlexRequests.Helpers
+{
+ public class MemoryCacheProvider : ICacheProvider
+ {
+ private ObjectCache Cache => MemoryCache.Default;
+
+ ///
+ /// Gets the item from the cache, if the item is not present
+ /// then we will get that item and store it in the cache.
+ ///
+ /// Type to store in the cache.
+ /// The key.
+ /// The item callback. This will be called if the item is not present in the cache.
+ ///
+ /// The amount of time we want to cache the object.
+ /// A copy of the cached object.
+ /// If the ]]> itemCallback is null and the item is not in the cache it will throw a .
+ /// If you do not want to change the object in the cache (since it's a copy returned and not a reference) you will need to
+ /// the cached item and then it, or just call this method.
+ public T GetOrSet(string key, Func itemCallback, int cacheTime = 20) where T : class
+ {
+ var item = Get(key);
+ if (item == null)
+ {
+ item = itemCallback();
+ if (item != null)
+ {
+ Set(key, item, cacheTime);
+ }
+ }
+
+ // Return a copy, not the stored cache reference
+ // The cached object will not change
+ // If we
+ return item.CloneJson();
+ }
+
+ ///
+ /// Gets the specified item from the cache.
+ ///
+ /// Type to get from the cache
+ /// The key.
+ ///
+ public T Get(string key) where T : class
+ {
+ var item = Cache.Get(key) as T;
+ return item;
+ }
+
+ ///
+ /// Set/Store the specified object in the cache
+ ///
+ /// The key.
+ /// The object we want to store.
+ /// The amount of time we want to cache the object.
+ public void Set(string key, object data, int cacheTime)
+ {
+ var policy = new CacheItemPolicy { AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(cacheTime) };
+ Cache.Add(new CacheItem(key, data), policy);
+ }
+
+ ///
+ /// Removes the specified object from the cache.
+ ///
+ /// The key.
+ public void Remove(string key)
+ {
+ var keys = Cache.Where(x => x.Key.Contains(key));
+ foreach (var k in keys)
+ {
+ Cache.Remove(k.Key);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/PlexRequests.Helpers/ObjectCopier.cs b/PlexRequests.Helpers/ObjectCopier.cs
new file mode 100644
index 000000000..2c4ffc2b4
--- /dev/null
+++ b/PlexRequests.Helpers/ObjectCopier.cs
@@ -0,0 +1,57 @@
+#region Copyright
+// /************************************************************************
+// Copyright (c) 2016 Jamie Rees
+// File: ObjectCopier.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.Helpers
+{
+ ///
+ /// Provides a method for performing a deep copy of an object.
+ /// Binary Serialization is used to perform the copy.
+ ///
+ public static class ObjectCopier
+ {
+ ///
+ /// Initialize inner objects individually
+ /// For example in default constructor some list property initialized with some values,
+ /// but in 'source' these items are cleaned -
+ /// without ObjectCreationHandling.Replace default constructor values will be added to result
+ ///
+ private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
+
+ ///
+ /// Perform a deep Copy of the object, using Json as a serialisation method.
+ ///
+ /// The type of object being copied.
+ /// The object instance to copy.
+ /// The copied object.
+ public static T CloneJson(this T source)
+ {
+ // Don't serialize a null object, simply return the default for that object
+ return ReferenceEquals(source, null) ? default(T) : JsonConvert.DeserializeObject(JsonConvert.SerializeObject(source), Settings);
+ }
+ }
+}
\ No newline at end of file
diff --git a/PlexRequests.Helpers/PasswordHasher.cs b/PlexRequests.Helpers/PasswordHasher.cs
new file mode 100644
index 000000000..68d797ae8
--- /dev/null
+++ b/PlexRequests.Helpers/PasswordHasher.cs
@@ -0,0 +1,69 @@
+#region Copyright
+// /************************************************************************
+// Copyright (c) 2016 Jamie Rees
+// File: PasswordHasher.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.Security.Cryptography;
+namespace PlexRequests.Helpers
+{
+
+ public static class PasswordHasher
+ {
+ // 24 = 192 bits
+ private const int SaltByteSize = 24;
+ private const int HashByteSize = 24;
+ private const int HasingIterationsCount = 10101;
+
+ public static byte[] ComputeHash(string password, byte[] salt, int iterations = HasingIterationsCount, int hashByteSize = HashByteSize)
+ {
+ var hashGenerator = new Rfc2898DeriveBytes(password, salt) { IterationCount = iterations };
+ return hashGenerator.GetBytes(hashByteSize);
+ }
+
+ public static byte[] GenerateSalt(int saltByteSize = SaltByteSize)
+ {
+ var saltGenerator = new RNGCryptoServiceProvider();
+ var salt = new byte[saltByteSize];
+ saltGenerator.GetBytes(salt);
+ return salt;
+ }
+
+ public static bool VerifyPassword(string password, byte[] passwordSalt, byte[] passwordHash)
+ {
+ var computedHash = ComputeHash(password, passwordSalt);
+ return AreHashesEqual(computedHash, passwordHash);
+ }
+
+ //Length constant verification - prevents timing attack
+ private static bool AreHashesEqual(IReadOnlyList firstHash, IReadOnlyList secondHash)
+ {
+ var minHashLength = firstHash.Count <= secondHash.Count ? firstHash.Count : secondHash.Count;
+ var xor = firstHash.Count ^ secondHash.Count;
+ for (var i = 0; i < minHashLength; i++)
+ xor |= firstHash[i] ^ secondHash[i];
+ return 0 == xor;
+ }
+ }
+}
diff --git a/PlexRequests.Helpers/PlexRequests.Helpers.csproj b/PlexRequests.Helpers/PlexRequests.Helpers.csproj
new file mode 100644
index 000000000..ec3c70a8d
--- /dev/null
+++ b/PlexRequests.Helpers/PlexRequests.Helpers.csproj
@@ -0,0 +1,79 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {1252336D-42A3-482A-804C-836E60173DFA}
+ Library
+ Properties
+ PlexRequests.Helpers
+ PlexRequests.Helpers
+ v4.6
+ 512
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll
+ True
+
+
+ ..\packages\NLog.4.2.3\lib\net45\NLog.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PlexRequests.Helpers/Properties/AssemblyInfo.cs b/PlexRequests.Helpers/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..e1d4fa2db
--- /dev/null
+++ b/PlexRequests.Helpers/Properties/AssemblyInfo.cs
@@ -0,0 +1,37 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("PlexRequests.Helpers")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("PlexRequests.Helpers")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("1252336d-42a3-482a-804c-836e60173dfa")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: AssemblyInformationalVersionAttribute("1.0.0.0")]
diff --git a/PlexRequests.Helpers/SerializerSettings.cs b/PlexRequests.Helpers/SerializerSettings.cs
new file mode 100644
index 000000000..71cbdc0e3
--- /dev/null
+++ b/PlexRequests.Helpers/SerializerSettings.cs
@@ -0,0 +1,45 @@
+#region Copyright
+// /************************************************************************
+// Copyright (c) 2016 Jamie Rees
+// File: SerializerSettings.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.Runtime.Serialization.Formatters;
+
+using Newtonsoft.Json;
+
+namespace PlexRequests.Helpers
+{
+ public static class SerializerSettings
+ {
+
+ public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
+ {
+ Formatting = Formatting.None,
+ TypeNameHandling = TypeNameHandling.Objects,
+ TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple,
+ NullValueHandling = NullValueHandling.Ignore
+ };
+
+ }
+}
\ No newline at end of file
diff --git a/PlexRequests.Helpers/StringCipher.cs b/PlexRequests.Helpers/StringCipher.cs
new file mode 100644
index 000000000..8fb48e589
--- /dev/null
+++ b/PlexRequests.Helpers/StringCipher.cs
@@ -0,0 +1,145 @@
+#region Copyright
+// /************************************************************************
+// Copyright (c) 2016 Jamie Rees
+// File: StringCipher.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.IO;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace PlexRequests.Helpers
+{
+ public class StringCipher
+ {
+ // This constant determines the number of iterations for the password bytes generation function.
+ private const int DerivationIterations = 1000;
+ // This constant is used to determine the keysize of the encryption algorithm in bits.
+ // We divide this by 8 within the code below to get the equivalent number of bytes.
+ private const int Keysize = 256;
+
+ ///
+ /// Decrypts the specified cipher text.
+ ///
+ /// The cipher text.
+ /// The pass phrase.
+ ///
+ public static string Decrypt(string cipherText, string passPhrase)
+ {
+ // Get the complete stream of bytes that represent:
+ // [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText]
+ var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
+ // Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes.
+ var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
+ // Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes.
+ var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
+ // Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
+ var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray();
+
+ using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
+ {
+ var keyBytes = password.GetBytes(Keysize / 8);
+ using (var symmetricKey = new RijndaelManaged())
+ {
+ symmetricKey.BlockSize = 256;
+ symmetricKey.Mode = CipherMode.CBC;
+ symmetricKey.Padding = PaddingMode.PKCS7;
+ using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
+ {
+ using (var memoryStream = new MemoryStream(cipherTextBytes))
+ {
+ using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
+ {
+ var plainTextBytes = new byte[cipherTextBytes.Length];
+ var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
+ memoryStream.Close();
+ cryptoStream.Close();
+ return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Encrypts the specified plain text.
+ ///
+ /// The plain text.
+ /// The pass phrase.
+ ///
+ public static string Encrypt(string plainText, string passPhrase)
+ {
+ // Salt and IV is randomly generated each time, but is preprended to encrypted cipher text
+ // so that the same Salt and IV values can be used when decrypting.
+ var saltStringBytes = Generate256BitsOfRandomEntropy();
+ var ivStringBytes = Generate256BitsOfRandomEntropy();
+ var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
+ using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
+ {
+ var keyBytes = password.GetBytes(Keysize / 8);
+ using (var symmetricKey = new RijndaelManaged())
+ {
+ symmetricKey.BlockSize = 256;
+ symmetricKey.Mode = CipherMode.CBC;
+ symmetricKey.Padding = PaddingMode.PKCS7;
+ using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
+ {
+ using (var memoryStream = new MemoryStream())
+ {
+ using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
+ {
+ cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
+ cryptoStream.FlushFinalBlock();
+ // Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes.
+ var cipherTextBytes = saltStringBytes;
+ cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
+ cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
+ memoryStream.Close();
+ cryptoStream.Close();
+ return Convert.ToBase64String(cipherTextBytes);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Generate256s the bits of random entropy.
+ ///
+ ///
+ private static byte[] Generate256BitsOfRandomEntropy()
+ {
+ var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits.
+ using (var rngCsp = new RNGCryptoServiceProvider())
+ {
+ // Fill the array with cryptographically secure random bytes.
+ rngCsp.GetBytes(randomBytes);
+ }
+ return randomBytes;
+ }
+ }
+}
\ No newline at end of file
diff --git a/PlexRequests.Helpers/UriHelper.cs b/PlexRequests.Helpers/UriHelper.cs
new file mode 100644
index 000000000..707f70436
--- /dev/null
+++ b/PlexRequests.Helpers/UriHelper.cs
@@ -0,0 +1,116 @@
+using System;
+
+using PlexRequests.Helpers.Exceptions;
+
+namespace PlexRequests.Helpers
+{
+ public static class UriHelper
+ {
+
+ public static Uri ReturnUri(this string val)
+ {
+ if (val == null)
+ {
+ throw new ApplicationSettingsException("The URI is null, please check your settings to make sure you have configured the applications correctly.");
+ }
+ try
+ {
+ var uri = new UriBuilder();
+
+ if (val.StartsWith("http://", StringComparison.Ordinal))
+ {
+ uri = new UriBuilder(val);
+ }
+ else if (val.StartsWith("https://", StringComparison.Ordinal))
+ {
+ uri = new UriBuilder(val);
+ }
+ else if (val.Contains(":"))
+ {
+ var split = val.Split(':', '/');
+ int port;
+ int.TryParse(split[1], out port);
+
+ uri = split.Length == 3
+ ? new UriBuilder(Uri.UriSchemeHttp, split[0], port, "/" + split[2])
+ : new UriBuilder(Uri.UriSchemeHttp, split[0], port);
+ }
+ else
+ {
+ uri = new UriBuilder(Uri.UriSchemeHttp, val);
+ }
+
+ return uri.Uri;
+ }
+ catch (Exception exception)
+ {
+ throw new Exception(exception.Message, exception);
+ }
+ }
+
+ ///
+ /// Returns the URI.
+ ///
+ /// The value.
+ /// The port.
+ /// if set to true [SSL].
+ /// The subdir.
+ ///
+ /// The URI is null, please check your settings to make sure you have configured the applications correctly.
+ ///
+ public static Uri ReturnUri(this string val, int port, bool ssl = default(bool))
+ {
+ if (val == null)
+ {
+ throw new ApplicationSettingsException("The URI is null, please check your settings to make sure you have configured the applications correctly.");
+ }
+ try
+ {
+ var uri = new UriBuilder();
+
+ if (val.StartsWith("http://", StringComparison.Ordinal))
+ {
+ var split = val.Split('/');
+ uri = split.Length >= 4 ? new UriBuilder(Uri.UriSchemeHttp, split[2], port, "/" + split[3]) : new UriBuilder(new Uri($"{val}:{port}"));
+ }
+ else if (val.StartsWith("https://", StringComparison.Ordinal))
+ {
+ var split = val.Split('/');
+ 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
+ {
+ uri = new UriBuilder(Uri.UriSchemeHttp, val, port);
+ }
+
+ return uri.Uri;
+ }
+ catch (Exception exception)
+ {
+ throw new Exception(exception.Message, exception);
+ }
+ }
+
+ public static Uri ReturnUriWithSubDir(this string val, int port, bool ssl, string subDir)
+ {
+ var uriBuilder = new UriBuilder(val);
+ if (ssl)
+ {
+ uriBuilder.Scheme = Uri.UriSchemeHttps;
+ }
+ if (!string.IsNullOrEmpty(subDir))
+ {
+ uriBuilder.Path = subDir;
+ }
+ uriBuilder.Port = port;
+
+ return uriBuilder.Uri;
+ }
+ }
+}
\ No newline at end of file
diff --git a/PlexRequests.Helpers/packages.config b/PlexRequests.Helpers/packages.config
new file mode 100644
index 000000000..dc63c2a11
--- /dev/null
+++ b/PlexRequests.Helpers/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/PlexRequests.Services.Tests/NotificationServiceTests.cs b/PlexRequests.Services.Tests/NotificationServiceTests.cs
new file mode 100644
index 000000000..940bcedda
--- /dev/null
+++ b/PlexRequests.Services.Tests/NotificationServiceTests.cs
@@ -0,0 +1,137 @@
+#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 System;
+using System.Threading.Tasks;
+
+using Moq;
+
+using NUnit.Framework;
+
+using PlexRequests.Services.Interfaces;
+using PlexRequests.Services.Notification;
+
+namespace PlexRequests.Services.Tests
+{
+ [TestFixture]
+ public class NotificationServiceTests
+ {
+ public NotificationService NotificationService { get; set; }
+
+ [SetUp]
+ public void Setup()
+ {
+ NotificationService = new NotificationService();
+ }
+
+ [Test]
+ public void SubscribeNewNotifier()
+ {
+ var notificationMock = new Mock();
+ 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]
+ public void SubscribeExistingNotifier()
+ {
+ var notificationMock1 = new Mock();
+ var notificationMock2 = new Mock();
+ 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]
+ public void UnSubscribeMissingNotifier()
+ {
+ var notificationMock = new Mock();
+ notificationMock.SetupGet(x => x.NotificationName).Returns("Notification1");
+ NotificationService.UnSubscribe(notificationMock.Object);
+
+ Assert.That(NotificationService.Observers.Count, Is.EqualTo(0));
+ }
+
+ [Test]
+ public void UnSubscribeNotifier()
+ {
+ var notificationMock = new Mock();
+ 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]
+ public void PublishWithNoObservers()
+ {
+ Assert.DoesNotThrowAsync(
+ async() =>
+ { await NotificationService.Publish(new NotificationModel()); });
+ }
+
+ [Test]
+ public async Task PublishAllNotifiers()
+ {
+ var notificationMock1 = new Mock();
+ var notificationMock2 = new Mock();
+ 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));
+ var model = new NotificationModel { Title = "abc", Body = "test" };
+ await NotificationService.Publish(model);
+
+ notificationMock1.Verify(x => x.NotifyAsync(model), Times.Once);
+ notificationMock2.Verify(x => x.NotifyAsync(model), Times.Once);
+ }
+
+ [Test]
+ public async Task PublishWithException()
+ {
+ var notificationMock = new Mock();
+ notificationMock.Setup(x => x.NotifyAsync(It.IsAny())).Throws();
+ notificationMock.SetupGet(x => x.NotificationName).Returns("Notification1");
+ NotificationService.Subscribe(notificationMock.Object);
+ await NotificationService.Publish(new NotificationModel());
+ }
+ }
+}
\ No newline at end of file
diff --git a/PlexRequests.Services.Tests/PlexAvailabilityCheckerTests.cs b/PlexRequests.Services.Tests/PlexAvailabilityCheckerTests.cs
new file mode 100644
index 000000000..8e641181e
--- /dev/null
+++ b/PlexRequests.Services.Tests/PlexAvailabilityCheckerTests.cs
@@ -0,0 +1,145 @@
+#region Copyright
+// /************************************************************************
+// Copyright (c) 2016 Jamie Rees
+// File: PlexAvailabilityCheckerTests.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.Collections.Generic;
+
+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;
+
+namespace PlexRequests.Services.Tests
+{
+ [TestFixture]
+ public class PlexAvailabilityCheckerTests
+ {
+ public IAvailabilityChecker Checker { get; set; }
+
+ [Test]
+ public void IsAvailableWithEmptySettingsTest()
+ {
+ var settingsMock = new Mock>();
+ var authMock = new Mock>();
+ var requestMock = new Mock();
+ var plexMock = new Mock();
+ Checker = new PlexAvailabilityChecker(settingsMock.Object, authMock.Object, requestMock.Object, plexMock.Object);
+
+ Assert.Throws(() => Checker.IsAvailable("title", "2013"), "We should be throwing an exception since we cannot talk to the services.");
+ }
+
+ [Test]
+ public void IsAvailableTest()
+ {
+ var settingsMock = new Mock>();
+ var authMock = new Mock>();
+ var requestMock = new Mock();
+ var plexMock = new Mock();
+
+ var searchResult = new PlexSearch {Video = new List