mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-19 21:03:17 -07:00
commit
6249a503f3
204 changed files with 55606 additions and 10615 deletions
|
@ -1,41 +1,37 @@
|
||||||
#region Copyright
|
#region Copyright
|
||||||
// /************************************************************************
|
// /************************************************************************
|
||||||
// Copyright (c) 2016 Jamie Rees
|
// Copyright (c) 2016 Jamie Rees
|
||||||
// File: PlexRegistry.cs
|
// File: ISlackApi.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
|
||||||
// a copy of this software and associated documentation files (the
|
// a copy of this software and associated documentation files (the
|
||||||
// "Software"), to deal in the Software without restriction, including
|
// "Software"), to deal in the Software without restriction, including
|
||||||
// without limitation the rights to use, copy, modify, merge, publish,
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
// permit persons to whom the Software is furnished to do so, subject to
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
// the following conditions:
|
// the following conditions:
|
||||||
//
|
//
|
||||||
// The above copyright notice and this permission notice shall be
|
// The above copyright notice and this permission notice shall be
|
||||||
// included in all copies or substantial portions of the Software.
|
// included in all copies or substantial portions of the Software.
|
||||||
//
|
//
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
// ************************************************************************/
|
// ************************************************************************/
|
||||||
#endregion
|
#endregion
|
||||||
|
using System.Threading.Tasks;
|
||||||
using FluentScheduler;
|
|
||||||
|
using PlexRequests.Api.Models.Notifications;
|
||||||
using PlexRequests.Services;
|
|
||||||
|
namespace PlexRequests.Api.Interfaces
|
||||||
namespace PlexRequests.UI.Jobs
|
{
|
||||||
{
|
public interface ISlackApi
|
||||||
public class PlexRegistry : Registry
|
{
|
||||||
{
|
Task<string> PushAsync(string team, string token, string service, SlackNotificationBody message);
|
||||||
public PlexRegistry()
|
}
|
||||||
{
|
|
||||||
Schedule<AvailabilityUpdateService>().ToRunNow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>PlexRequests.Api.Interfaces</RootNamespace>
|
<RootNamespace>PlexRequests.Api.Interfaces</RootNamespace>
|
||||||
<AssemblyName>PlexRequests.Api.Interfaces</AssemblyName>
|
<AssemblyName>PlexRequests.Api.Interfaces</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="RestSharp, Version=105.2.3.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="RestSharp, Version=105.2.3.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\RestSharp.105.2.3\lib\net452\RestSharp.dll</HintPath>
|
<HintPath>..\packages\RestSharp.105.2.3\lib\net45\RestSharp.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
@ -51,20 +51,21 @@
|
||||||
<Compile Include="IMusicBrainzApi.cs" />
|
<Compile Include="IMusicBrainzApi.cs" />
|
||||||
<Compile Include="IPlexApi.cs" />
|
<Compile Include="IPlexApi.cs" />
|
||||||
<Compile Include="IPushbulletApi.cs" />
|
<Compile Include="IPushbulletApi.cs" />
|
||||||
|
<Compile Include="ISlackApi.cs" />
|
||||||
<Compile Include="IPushoverApi.cs" />
|
<Compile Include="IPushoverApi.cs" />
|
||||||
<Compile Include="ISickRageApi.cs" />
|
<Compile Include="ISickRageApi.cs" />
|
||||||
<Compile Include="ISonarrApi.cs" />
|
<Compile Include="ISonarrApi.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<None Include="packages.config" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\PlexRequests.Api.Models\PlexRequests.Api.Models.csproj">
|
<ProjectReference Include="..\PlexRequests.Api.Models\PlexRequests.Api.Models.csproj">
|
||||||
<Project>{CB37A5F8-6DFC-4554-99D3-A42B502E4591}</Project>
|
<Project>{CB37A5F8-6DFC-4554-99D3-A42B502E4591}</Project>
|
||||||
<Name>PlexRequests.Api.Models</Name>
|
<Name>PlexRequests.Api.Models</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</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.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="RestSharp" version="105.2.3" targetFramework="net452" />
|
<package id="RestSharp" version="105.2.3" targetFramework="net45" />
|
||||||
</packages>
|
</packages>
|
|
@ -0,0 +1,56 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2016 Jamie Rees
|
||||||
|
// File: SlackNotificationBody.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 SlackNotificationBody
|
||||||
|
{
|
||||||
|
[JsonConstructor]
|
||||||
|
public SlackNotificationBody()
|
||||||
|
{
|
||||||
|
username = "Plex Requests";
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
private string _username;
|
||||||
|
public string username
|
||||||
|
{
|
||||||
|
get { return _username; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(value))
|
||||||
|
_username = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string channel { get; set; }
|
||||||
|
public string text { get; set; }
|
||||||
|
|
||||||
|
public string icon_url { get; set; }
|
||||||
|
public string icon_emoji { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>PlexRequests.Api.Models</RootNamespace>
|
<RootNamespace>PlexRequests.Api.Models</RootNamespace>
|
||||||
<AssemblyName>PlexRequests.Api.Models</AssemblyName>
|
<AssemblyName>PlexRequests.Api.Models</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -31,10 +31,6 @@
|
||||||
<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 +39,9 @@
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
|
||||||
|
<HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Movie\CouchPotatoAdd.cs" />
|
<Compile Include="Movie\CouchPotatoAdd.cs" />
|
||||||
|
@ -59,6 +58,7 @@
|
||||||
<Compile Include="Notifications\PushbulletPush.cs" />
|
<Compile Include="Notifications\PushbulletPush.cs" />
|
||||||
<Compile Include="Notifications\PushbulletResponse.cs" />
|
<Compile Include="Notifications\PushbulletResponse.cs" />
|
||||||
<Compile Include="Notifications\PushoverResponse.cs" />
|
<Compile Include="Notifications\PushoverResponse.cs" />
|
||||||
|
<Compile Include="Notifications\SlackNotificationBody.cs" />
|
||||||
<Compile Include="Plex\PlexAccount.cs" />
|
<Compile Include="Plex\PlexAccount.cs" />
|
||||||
<Compile Include="Plex\PlexAuthentication.cs" />
|
<Compile Include="Plex\PlexAuthentication.cs" />
|
||||||
<Compile Include="Plex\PlexError.cs" />
|
<Compile Include="Plex\PlexError.cs" />
|
||||||
|
|
|
@ -24,5 +24,56 @@ namespace PlexRequests.Api.Models.Tv
|
||||||
public int updated { get; set; }
|
public int updated { get; set; }
|
||||||
public Links _links { get; set; }
|
public Links _links { get; set; }
|
||||||
public int seasonCount { get; set; }
|
public int seasonCount { get; set; }
|
||||||
|
public Embedded _embedded { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Season
|
||||||
|
{
|
||||||
|
public int id { get; set; }
|
||||||
|
public string url { get; set; }
|
||||||
|
public int number { get; set; }
|
||||||
|
public string name { get; set; }
|
||||||
|
public int? episodeOrder { get; set; }
|
||||||
|
public string premiereDate { get; set; }
|
||||||
|
public string endDate { get; set; }
|
||||||
|
public Network2 network { get; set; }
|
||||||
|
public object webChannel { get; set; }
|
||||||
|
public Image2 image { get; set; }
|
||||||
|
public string summary { get; set; }
|
||||||
|
public Links2 _links { get; set; }
|
||||||
|
}
|
||||||
|
public class Country2
|
||||||
|
{
|
||||||
|
public string name { get; set; }
|
||||||
|
public string code { get; set; }
|
||||||
|
public string timezone { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Network2
|
||||||
|
{
|
||||||
|
public int id { get; set; }
|
||||||
|
public string name { get; set; }
|
||||||
|
public Country2 country { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Image2
|
||||||
|
{
|
||||||
|
public string medium { get; set; }
|
||||||
|
public string original { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Self2
|
||||||
|
{
|
||||||
|
public string href { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Links2
|
||||||
|
{
|
||||||
|
public Self2 self { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Embedded
|
||||||
|
{
|
||||||
|
public List<Season> seasons { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net46" />
|
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net45" />
|
||||||
</packages>
|
</packages>
|
|
@ -25,7 +25,6 @@
|
||||||
// ************************************************************************/
|
// ************************************************************************/
|
||||||
#endregion
|
#endregion
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Xml.Serialization;
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
@ -34,14 +33,15 @@ using Newtonsoft.Json;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
using PlexRequests.Api.Interfaces;
|
using PlexRequests.Api.Interfaces;
|
||||||
using PlexRequests.Helpers;
|
using PlexRequests.Helpers.Exceptions;
|
||||||
|
|
||||||
using RestSharp;
|
using RestSharp;
|
||||||
|
|
||||||
namespace PlexRequests.Api
|
namespace PlexRequests.Api
|
||||||
{
|
{
|
||||||
public class ApiRequest : IApiRequest
|
public class ApiRequest : IApiRequest
|
||||||
{
|
{
|
||||||
private JsonSerializerSettings Settings = new JsonSerializerSettings
|
private readonly JsonSerializerSettings _settings = new JsonSerializerSettings
|
||||||
{
|
{
|
||||||
NullValueHandling = NullValueHandling.Ignore,
|
NullValueHandling = NullValueHandling.Ignore,
|
||||||
MissingMemberHandling = MissingMemberHandling.Ignore
|
MissingMemberHandling = MissingMemberHandling.Ignore
|
||||||
|
@ -66,12 +66,13 @@ namespace PlexRequests.Api
|
||||||
if (response.ErrorException != null)
|
if (response.ErrorException != null)
|
||||||
{
|
{
|
||||||
var message = "Error retrieving response. Check inner details for more info.";
|
var message = "Error retrieving response. Check inner details for more info.";
|
||||||
throw new ApplicationException(message, response.ErrorException);
|
Log.Error(response.ErrorException);
|
||||||
|
throw new ApiRequestException(message, response.ErrorException);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Data;
|
return response.Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IRestResponse Execute(IRestRequest request, Uri baseUri)
|
public IRestResponse Execute(IRestRequest request, Uri baseUri)
|
||||||
{
|
{
|
||||||
var client = new RestClient { BaseUrl = baseUri };
|
var client = new RestClient { BaseUrl = baseUri };
|
||||||
|
@ -80,8 +81,9 @@ namespace PlexRequests.Api
|
||||||
|
|
||||||
if (response.ErrorException != null)
|
if (response.ErrorException != null)
|
||||||
{
|
{
|
||||||
|
Log.Error(response.ErrorException);
|
||||||
var message = "Error retrieving response. Check inner details for more info.";
|
var message = "Error retrieving response. Check inner details for more info.";
|
||||||
throw new ApplicationException(message, response.ErrorException);
|
throw new ApiRequestException(message, response.ErrorException);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
@ -95,8 +97,9 @@ namespace PlexRequests.Api
|
||||||
|
|
||||||
if (response.ErrorException != null)
|
if (response.ErrorException != null)
|
||||||
{
|
{
|
||||||
|
Log.Error(response.ErrorException);
|
||||||
var message = "Error retrieving response. Check inner details for more info.";
|
var message = "Error retrieving response. Check inner details for more info.";
|
||||||
throw new ApplicationException(message, response.ErrorException);
|
throw new ApiRequestException(message, response.ErrorException);
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = DeserializeXml<T>(response.Content);
|
var result = DeserializeXml<T>(response.Content);
|
||||||
|
@ -106,18 +109,18 @@ namespace PlexRequests.Api
|
||||||
public T ExecuteJson<T>(IRestRequest request, Uri baseUri) where T : new()
|
public T ExecuteJson<T>(IRestRequest request, Uri baseUri) where T : new()
|
||||||
{
|
{
|
||||||
var client = new RestClient { BaseUrl = baseUri };
|
var client = new RestClient { BaseUrl = baseUri };
|
||||||
|
|
||||||
var response = client.Execute(request);
|
var response = client.Execute(request);
|
||||||
Log.Trace("Api Content Response:");
|
Log.Trace("Api Content Response:");
|
||||||
Log.Trace(response.Content);
|
Log.Trace(response.Content);
|
||||||
if (response.ErrorException != null)
|
if (response.ErrorException != null)
|
||||||
{
|
{
|
||||||
|
Log.Error(response.ErrorException);
|
||||||
var message = "Error retrieving response. Check inner details for more info.";
|
var message = "Error retrieving response. Check inner details for more info.";
|
||||||
throw new ApplicationException(message, response.ErrorException);
|
throw new ApiRequestException(message, response.ErrorException);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Trace("Deserialzing Object");
|
Log.Trace("Deserialzing Object");
|
||||||
var json = JsonConvert.DeserializeObject<T>(response.Content, Settings);
|
var json = JsonConvert.DeserializeObject<T>(response.Content, _settings);
|
||||||
Log.Trace("Finished Deserialzing Object");
|
Log.Trace("Finished Deserialzing Object");
|
||||||
|
|
||||||
return json;
|
return json;
|
||||||
|
@ -133,8 +136,9 @@ namespace PlexRequests.Api
|
||||||
using (var sr = new StringReader(input))
|
using (var sr = new StringReader(input))
|
||||||
return (T)ser.Deserialize(sr);
|
return (T)ser.Deserialize(sr);
|
||||||
}
|
}
|
||||||
catch (InvalidOperationException)
|
catch (InvalidOperationException e)
|
||||||
{
|
{
|
||||||
|
Log.Error(e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ using Newtonsoft.Json.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using PlexRequests.Api.Interfaces;
|
using PlexRequests.Api.Interfaces;
|
||||||
using PlexRequests.Api.Models.Movie;
|
using PlexRequests.Api.Models.Movie;
|
||||||
|
using PlexRequests.Helpers.Exceptions;
|
||||||
|
|
||||||
using RestSharp;
|
using RestSharp;
|
||||||
|
|
||||||
|
@ -61,7 +62,12 @@ namespace PlexRequests.Api
|
||||||
request.AddUrlSegment("imdbid", imdbid);
|
request.AddUrlSegment("imdbid", imdbid);
|
||||||
request.AddUrlSegment("title", title);
|
request.AddUrlSegment("title", title);
|
||||||
|
|
||||||
var obj = Api.ExecuteJson<JObject>(request, baseUrl);
|
var obj = RetryHandler.Execute<JObject>(() => Api.ExecuteJson<JObject> (request, baseUrl),new TimeSpan[] {
|
||||||
|
TimeSpan.FromSeconds (2),
|
||||||
|
TimeSpan.FromSeconds(5),
|
||||||
|
TimeSpan.FromSeconds(10)},
|
||||||
|
(exception, timespan) => Log.Error (exception, "Exception when calling AddMovie for CP, Retrying {0}", timespan));
|
||||||
|
|
||||||
Log.Trace("CP movie Add result count {0}", obj.Count);
|
Log.Trace("CP movie Add result count {0}", obj.Count);
|
||||||
|
|
||||||
if (obj.Count > 0)
|
if (obj.Count > 0)
|
||||||
|
@ -99,7 +105,14 @@ namespace PlexRequests.Api
|
||||||
|
|
||||||
request.AddUrlSegment("apikey", apiKey);
|
request.AddUrlSegment("apikey", apiKey);
|
||||||
|
|
||||||
return Api.Execute<CouchPotatoStatus>(request,url);
|
|
||||||
|
var obj = RetryHandler.Execute<CouchPotatoStatus>(() => Api.Execute<CouchPotatoStatus> (request, url),new TimeSpan[] {
|
||||||
|
TimeSpan.FromSeconds (2),
|
||||||
|
TimeSpan.FromSeconds(5),
|
||||||
|
TimeSpan.FromSeconds(10)},
|
||||||
|
(exception, timespan) => Log.Error (exception, "Exception when calling GetStatus for CP, Retrying {0}", timespan));
|
||||||
|
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CouchPotatoProfiles GetProfiles(Uri url, string apiKey)
|
public CouchPotatoProfiles GetProfiles(Uri url, string apiKey)
|
||||||
|
@ -113,18 +126,39 @@ namespace PlexRequests.Api
|
||||||
|
|
||||||
request.AddUrlSegment("apikey", apiKey);
|
request.AddUrlSegment("apikey", apiKey);
|
||||||
|
|
||||||
return Api.Execute<CouchPotatoProfiles>(request, url);
|
var obj = RetryHandler.Execute<CouchPotatoProfiles>(() => Api.Execute<CouchPotatoProfiles> (request, url),null,
|
||||||
|
(exception, timespan) => Log.Error (exception, "Exception when calling GetProfiles for CP, Retrying {0}", timespan));
|
||||||
|
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CouchPotatoMovies GetMovies(Uri baseUrl, string apiKey, string[] status)
|
public CouchPotatoMovies GetMovies(Uri baseUrl, string apiKey, string[] status)
|
||||||
{
|
{
|
||||||
RestRequest request;
|
var request = new RestRequest
|
||||||
request = new RestRequest { Resource = "/api/{apikey}/movie.list?status={status}" };
|
{
|
||||||
|
Resource = "/api/{apikey}/movie.list?status={status}"
|
||||||
|
};
|
||||||
|
|
||||||
request.AddUrlSegment("apikey", apiKey);
|
request.AddUrlSegment("apikey", apiKey);
|
||||||
request.AddUrlSegment("status", string.Join(",", status));
|
request.AddUrlSegment("status", string.Join(",", status));
|
||||||
|
try
|
||||||
return Api.Execute<CouchPotatoMovies>(request, baseUrl);
|
{
|
||||||
|
var obj = RetryHandler.Execute<CouchPotatoMovies>(() => Api.Execute<CouchPotatoMovies> (request, baseUrl),
|
||||||
|
new TimeSpan[] {
|
||||||
|
TimeSpan.FromSeconds (5),
|
||||||
|
TimeSpan.FromSeconds(10),
|
||||||
|
TimeSpan.FromSeconds(30)
|
||||||
|
},
|
||||||
|
(exception, timespan) => Log.Error (exception, "Exception when calling GetMovies for CP, Retrying {0}", timespan));
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
catch (Exception e) // Request error is already logged in the ApiRequest class
|
||||||
|
{
|
||||||
|
Log.Error("Error when attempting to GetMovies.");
|
||||||
|
Log.Error (e);
|
||||||
|
return new CouchPotatoMovies();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -71,7 +71,7 @@ namespace PlexRequests.Api
|
||||||
|
|
||||||
return albumResult;
|
return albumResult;
|
||||||
}
|
}
|
||||||
catch (JsonSerializationException jse)
|
catch (Exception jse)
|
||||||
{
|
{
|
||||||
Log.Error(jse);
|
Log.Error(jse);
|
||||||
return false; // If there is no matching result we do not get returned a JSON string, it just returns "false".
|
return false; // If there is no matching result we do not get returned a JSON string, it just returns "false".
|
||||||
|
@ -94,7 +94,7 @@ namespace PlexRequests.Api
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
catch (JsonSerializationException jse)
|
catch (Exception jse)
|
||||||
{
|
{
|
||||||
Log.Error(jse);
|
Log.Error(jse);
|
||||||
return new List<HeadphonesGetIndex>();
|
return new List<HeadphonesGetIndex>();
|
||||||
|
|
|
@ -23,17 +23,20 @@
|
||||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
// ************************************************************************/
|
// ************************************************************************/
|
||||||
|
using Polly;
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
using NLog;
|
||||||
|
|
||||||
using PlexRequests.Api.Interfaces;
|
using PlexRequests.Api.Interfaces;
|
||||||
using PlexRequests.Api.Models;
|
|
||||||
using PlexRequests.Api.Models.Plex;
|
using PlexRequests.Api.Models.Plex;
|
||||||
using PlexRequests.Helpers;
|
using PlexRequests.Helpers;
|
||||||
|
using PlexRequests.Helpers.Exceptions;
|
||||||
|
|
||||||
using RestSharp;
|
using RestSharp;
|
||||||
using System.Xml;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace PlexRequests.Api
|
namespace PlexRequests.Api
|
||||||
{
|
{
|
||||||
|
@ -43,6 +46,19 @@ namespace PlexRequests.Api
|
||||||
{
|
{
|
||||||
Version = AssemblyHelper.GetAssemblyVersion();
|
Version = AssemblyHelper.GetAssemblyVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PlexApi (IApiRequest api)
|
||||||
|
{
|
||||||
|
Api = api;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IApiRequest Api { get; }
|
||||||
|
|
||||||
|
private const string SignInUri = "https://plex.tv/users/sign_in.json";
|
||||||
|
private const string FriendsUri = "https://plex.tv/pms/friends/all";
|
||||||
|
private const string GetAccountUri = "https://plex.tv/users/account";
|
||||||
|
|
||||||
|
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||||
private static string Version { get; }
|
private static string Version { get; }
|
||||||
|
|
||||||
public PlexAuthentication SignIn(string username, string password)
|
public PlexAuthentication SignIn(string username, string password)
|
||||||
|
@ -64,8 +80,11 @@ namespace PlexRequests.Api
|
||||||
|
|
||||||
request.AddJsonBody(userModel);
|
request.AddJsonBody(userModel);
|
||||||
|
|
||||||
var api = new ApiRequest();
|
var obj = RetryHandler.Execute<PlexAuthentication>(() => Api.Execute<PlexAuthentication> (request, new Uri(SignInUri)),
|
||||||
return api.Execute<PlexAuthentication>(request, new Uri("https://plex.tv/users/sign_in.json"));
|
null,
|
||||||
|
(exception, timespan) => Log.Error (exception, "Exception when calling SignIn for Plex, Retrying {0}", timespan));
|
||||||
|
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PlexFriends GetUsers(string authToken)
|
public PlexFriends GetUsers(string authToken)
|
||||||
|
@ -77,8 +96,10 @@ namespace PlexRequests.Api
|
||||||
|
|
||||||
AddHeaders(ref request, authToken);
|
AddHeaders(ref request, authToken);
|
||||||
|
|
||||||
var api = new ApiRequest();
|
var users = RetryHandler.Execute(() => Api.ExecuteXml<PlexFriends> (request, new Uri(FriendsUri)),
|
||||||
var users = api.ExecuteXml<PlexFriends>(request, new Uri("https://plex.tv/pms/friends/all"));
|
null,
|
||||||
|
(exception, timespan) => Log.Error (exception, "Exception when calling GetUsers for Plex, Retrying {0}", timespan));
|
||||||
|
|
||||||
|
|
||||||
return users;
|
return users;
|
||||||
}
|
}
|
||||||
|
@ -101,8 +122,9 @@ namespace PlexRequests.Api
|
||||||
request.AddUrlSegment("searchTerm", searchTerm);
|
request.AddUrlSegment("searchTerm", searchTerm);
|
||||||
AddHeaders(ref request, authToken);
|
AddHeaders(ref request, authToken);
|
||||||
|
|
||||||
var api = new ApiRequest();
|
var search = RetryHandler.Execute<PlexSearch>(() => Api.ExecuteXml<PlexSearch> (request, plexFullHost),
|
||||||
var search = api.ExecuteXml<PlexSearch>(request, plexFullHost);
|
null,
|
||||||
|
(exception, timespan) => Log.Error (exception, "Exception when calling SearchContent for Plex, Retrying {0}", timespan));
|
||||||
|
|
||||||
return search;
|
return search;
|
||||||
}
|
}
|
||||||
|
@ -116,9 +138,10 @@ namespace PlexRequests.Api
|
||||||
|
|
||||||
AddHeaders(ref request, authToken);
|
AddHeaders(ref request, authToken);
|
||||||
|
|
||||||
var api = new ApiRequest();
|
var users = RetryHandler.Execute<PlexStatus>(() => Api.ExecuteXml<PlexStatus> (request, uri),
|
||||||
var users = api.ExecuteXml<PlexStatus>(request, uri);
|
null,
|
||||||
|
(exception, timespan) => Log.Error (exception, "Exception when calling GetStatus for Plex, Retrying {0}", timespan));
|
||||||
|
|
||||||
return users;
|
return users;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,9 +154,10 @@ namespace PlexRequests.Api
|
||||||
|
|
||||||
AddHeaders(ref request, authToken);
|
AddHeaders(ref request, authToken);
|
||||||
|
|
||||||
var api = new ApiRequest();
|
var account = RetryHandler.Execute<PlexAccount>(() => Api.ExecuteXml<PlexAccount> (request, new Uri(GetAccountUri)),
|
||||||
var account = api.ExecuteXml<PlexAccount>(request, new Uri("https://plex.tv/users/account"));
|
null,
|
||||||
|
(exception, timespan) => Log.Error (exception, "Exception when calling GetAccount for Plex, Retrying {0}", timespan));
|
||||||
|
|
||||||
return account;
|
return account;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,10 +171,23 @@ namespace PlexRequests.Api
|
||||||
|
|
||||||
AddHeaders(ref request, authToken);
|
AddHeaders(ref request, authToken);
|
||||||
|
|
||||||
var api = new ApiRequest();
|
try
|
||||||
var sections = api.ExecuteXml<PlexLibraries>(request, plexFullHost);
|
{
|
||||||
|
var lib = RetryHandler.Execute<PlexLibraries>(() => Api.ExecuteXml<PlexLibraries> (request, plexFullHost),
|
||||||
|
new TimeSpan[] {
|
||||||
|
TimeSpan.FromSeconds (5),
|
||||||
|
TimeSpan.FromSeconds(10),
|
||||||
|
TimeSpan.FromSeconds(30)
|
||||||
|
},
|
||||||
|
(exception, timespan) => Log.Error (exception, "Exception when calling GetLibrarySections for Plex, Retrying {0}", timespan));
|
||||||
|
|
||||||
return sections;
|
return lib;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e,"There has been a API Exception when attempting to get the Plex Libraries");
|
||||||
|
return new PlexLibraries();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public PlexSearch GetLibrary(string authToken, Uri plexFullHost, string libraryId)
|
public PlexSearch GetLibrary(string authToken, Uri plexFullHost, string libraryId)
|
||||||
|
@ -161,13 +198,26 @@ namespace PlexRequests.Api
|
||||||
Resource = "library/sections/{libraryId}/all"
|
Resource = "library/sections/{libraryId}/all"
|
||||||
};
|
};
|
||||||
|
|
||||||
request.AddUrlSegment("libraryId", libraryId.ToString());
|
request.AddUrlSegment("libraryId", libraryId);
|
||||||
AddHeaders(ref request, authToken);
|
AddHeaders(ref request, authToken);
|
||||||
|
|
||||||
var api = new ApiRequest();
|
try
|
||||||
var search = api.ExecuteXml<PlexSearch>(request, plexFullHost);
|
{
|
||||||
|
var lib = RetryHandler.Execute<PlexSearch>(() => Api.ExecuteXml<PlexSearch> (request, plexFullHost),
|
||||||
|
new TimeSpan[] {
|
||||||
|
TimeSpan.FromSeconds (5),
|
||||||
|
TimeSpan.FromSeconds(10),
|
||||||
|
TimeSpan.FromSeconds(30)
|
||||||
|
},
|
||||||
|
(exception, timespan) => Log.Error (exception, "Exception when calling GetLibrary for Plex, Retrying {0}", timespan));
|
||||||
|
|
||||||
return search;
|
return lib;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e,"There has been a API Exception when attempting to get the Plex Library");
|
||||||
|
return new PlexSearch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddHeaders(ref RestRequest request, string authToken)
|
private void AddHeaders(ref RestRequest request, string authToken)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>PlexRequests.Api</RootNamespace>
|
<RootNamespace>PlexRequests.Api</RootNamespace>
|
||||||
<AssemblyName>PlexRequests.Api</AssemblyName>
|
<AssemblyName>PlexRequests.Api</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -31,24 +31,8 @@
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Dapper, Version=1.40.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\Dapper.1.42\lib\net45\Dapper.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Nancy, Version=1.4.2.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\Nancy.1.4.3\lib\net40\Nancy.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<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="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="RestSharp, Version=105.2.3.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="RestSharp, Version=105.2.3.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\RestSharp.105.2.3\lib\net452\RestSharp.dll</HintPath>
|
<HintPath>..\packages\RestSharp.105.2.3\lib\net45\RestSharp.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
@ -59,9 +43,23 @@
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
<Reference Include="TMDbLib, Version=0.9.0.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="Dapper, Version=1.40.0.0, Culture=neutral, PublicKeyToken=null">
|
||||||
|
<HintPath>..\packages\Dapper.1.42\lib\net45\Dapper.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Nancy, Version=1.4.2.0, Culture=neutral, PublicKeyToken=null">
|
||||||
|
<HintPath>..\packages\Nancy.1.4.3\lib\net40\Nancy.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
|
||||||
|
<HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c">
|
||||||
|
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="TMDbLib, Version=0.9.0.0, Culture=neutral, PublicKeyToken=null">
|
||||||
<HintPath>..\packages\TMDbLib.0.9.0.0-alpha\lib\net45\TMDbLib.dll</HintPath>
|
<HintPath>..\packages\TMDbLib.0.9.0.0-alpha\lib\net45\TMDbLib.dll</HintPath>
|
||||||
<Private>True</Private>
|
</Reference>
|
||||||
|
<Reference Include="Polly">
|
||||||
|
<HintPath>..\packages\Polly-Signed.4.2.0\lib\net45\Polly.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -73,6 +71,7 @@
|
||||||
<DependentUpon>MockApiData.resx</DependentUpon>
|
<DependentUpon>MockApiData.resx</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Mocks\MockSonarrApi.cs" />
|
<Compile Include="Mocks\MockSonarrApi.cs" />
|
||||||
|
<Compile Include="SlackApi.cs" />
|
||||||
<Compile Include="PushoverApi.cs" />
|
<Compile Include="PushoverApi.cs" />
|
||||||
<Compile Include="PushbulletApi.cs" />
|
<Compile Include="PushbulletApi.cs" />
|
||||||
<Compile Include="SickrageApi.cs" />
|
<Compile Include="SickrageApi.cs" />
|
||||||
|
@ -87,6 +86,7 @@
|
||||||
<Compile Include="TheTvDbApi.cs" />
|
<Compile Include="TheTvDbApi.cs" />
|
||||||
<Compile Include="TvMazeApi.cs" />
|
<Compile Include="TvMazeApi.cs" />
|
||||||
<Compile Include="TvMazeBase.cs" />
|
<Compile Include="TvMazeBase.cs" />
|
||||||
|
<Compile Include="RetryHandler.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="app.config" />
|
<None Include="app.config" />
|
||||||
|
|
71
PlexRequests.Api/RetryHandler.cs
Normal file
71
PlexRequests.Api/RetryHandler.cs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
using System;
|
||||||
|
using Polly.Retry;
|
||||||
|
using Polly;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace PlexRequests.Api
|
||||||
|
{
|
||||||
|
public static class RetryHandler
|
||||||
|
{
|
||||||
|
|
||||||
|
private static readonly TimeSpan[] DefaultTime = new TimeSpan[] {
|
||||||
|
TimeSpan.FromSeconds (2),
|
||||||
|
TimeSpan.FromSeconds(5),
|
||||||
|
TimeSpan.FromSeconds(10)};
|
||||||
|
|
||||||
|
public static RetryPolicy RetryAndWaitPolicy(TimeSpan[] timeSpan, Action action)
|
||||||
|
{
|
||||||
|
if(timeSpan == null)
|
||||||
|
{
|
||||||
|
timeSpan = DefaultTime;
|
||||||
|
}
|
||||||
|
var policy = Policy.Handle<Exception> ()
|
||||||
|
.WaitAndRetry(timeSpan, (e, ts) => action());
|
||||||
|
|
||||||
|
return policy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RetryPolicy RetryAndWaitPolicy(TimeSpan[] timeSpan)
|
||||||
|
{
|
||||||
|
if(timeSpan == null)
|
||||||
|
{
|
||||||
|
timeSpan = DefaultTime;
|
||||||
|
}
|
||||||
|
var policy = Policy.Handle<Exception> ()
|
||||||
|
.WaitAndRetry(timeSpan);
|
||||||
|
|
||||||
|
return policy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RetryPolicy RetryAndWaitPolicy(TimeSpan[] timeSpan, Action<Exception, TimeSpan> action)
|
||||||
|
{
|
||||||
|
if(timeSpan == null)
|
||||||
|
{
|
||||||
|
timeSpan = DefaultTime;
|
||||||
|
}
|
||||||
|
var policy = Policy.Handle<Exception> ()
|
||||||
|
.WaitAndRetry(timeSpan, action);
|
||||||
|
|
||||||
|
return policy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T Execute<T>(Func<T> action, TimeSpan[] timeSpan)
|
||||||
|
{
|
||||||
|
var policy = RetryAndWaitPolicy (timeSpan);
|
||||||
|
|
||||||
|
return policy.Execute (action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T Execute<T>(Func<T> func, TimeSpan[] timeSpan, Action<Exception, TimeSpan> action)
|
||||||
|
{
|
||||||
|
if(timeSpan == null)
|
||||||
|
{
|
||||||
|
timeSpan = DefaultTime;
|
||||||
|
}
|
||||||
|
var policy = RetryAndWaitPolicy (timeSpan, action);
|
||||||
|
|
||||||
|
return policy.Execute (func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
#region Copyright
|
#region Copyright
|
||||||
|
|
||||||
// /************************************************************************
|
// /************************************************************************
|
||||||
// Copyright (c) 2016 Jamie Rees
|
// Copyright (c) 2016 Jamie Rees
|
||||||
// File: CouchPotatoApi.cs
|
// File: SickrageApi.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
|
||||||
|
@ -24,28 +23,28 @@
|
||||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
// ************************************************************************/
|
// ************************************************************************/
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
using PlexRequests.Api.Interfaces;
|
using PlexRequests.Api.Interfaces;
|
||||||
using PlexRequests.Api.Models.SickRage;
|
using PlexRequests.Api.Models.SickRage;
|
||||||
using PlexRequests.Helpers;
|
using PlexRequests.Helpers;
|
||||||
|
using PlexRequests.Helpers.Exceptions;
|
||||||
|
|
||||||
using RestSharp;
|
using RestSharp;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
|
|
||||||
namespace PlexRequests.Api
|
namespace PlexRequests.Api
|
||||||
{
|
{
|
||||||
public class SickrageApi : ISickRageApi
|
public class SickrageApi : ISickRageApi
|
||||||
{
|
{
|
||||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
public SickrageApi()
|
public SickrageApi()
|
||||||
{
|
{
|
||||||
|
@ -54,22 +53,38 @@ namespace PlexRequests.Api
|
||||||
|
|
||||||
private ApiRequest Api { get; }
|
private ApiRequest Api { get; }
|
||||||
|
|
||||||
|
public SickRageSeasonList VerifyShowHasLoaded(int tvdbId, string apiKey, Uri baseUrl)
|
||||||
public async Task<SickRageTvAdd> AddSeries(int tvdbId, int seasonCount, int[] seasons, string quality, string apiKey,
|
|
||||||
Uri baseUrl)
|
|
||||||
{
|
{
|
||||||
|
Log.Trace("Entered `VerifyShowHasLoaded({0} <- id)`", tvdbId);
|
||||||
|
var request = new RestRequest { Resource = "/api/{apiKey}/?cmd=show.seasonlist", Method = Method.GET };
|
||||||
|
request.AddUrlSegment("apiKey", apiKey);
|
||||||
|
request.AddQueryParameter("tvdbid", tvdbId.ToString());
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var policy = RetryHandler.RetryAndWaitPolicy(
|
||||||
|
null,
|
||||||
|
(exception, timespan) => Log.Error(exception, "Exception when calling VerifyShowHasLoaded for SR, Retrying {0}", timespan));
|
||||||
|
|
||||||
|
var obj = policy.Execute(() => Api.ExecuteJson<SickRageSeasonList>(request, baseUrl));
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e);
|
||||||
|
return new SickRageSeasonList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SickRageTvAdd> AddSeries(int tvdbId, int seasonCount, int[] seasons, string quality, string apiKey, Uri baseUrl)
|
||||||
|
{
|
||||||
var futureStatus = seasons.Length > 0 && !seasons.Any(x => x == seasonCount) ? SickRageStatus.Skipped : SickRageStatus.Wanted;
|
var futureStatus = seasons.Length > 0 && !seasons.Any(x => x == seasonCount) ? SickRageStatus.Skipped : SickRageStatus.Wanted;
|
||||||
var status = seasons.Length > 0 ? SickRageStatus.Skipped : SickRageStatus.Wanted;
|
var status = seasons.Length > 0 ? SickRageStatus.Skipped : SickRageStatus.Wanted;
|
||||||
|
|
||||||
Log.Trace("Future Status: {0}", futureStatus);
|
Log.Trace("Future Status: {0}", futureStatus);
|
||||||
Log.Trace("Current Status: {0}", status);
|
Log.Trace("Current Status: {0}", status);
|
||||||
|
|
||||||
var request = new RestRequest
|
var request = new RestRequest { Resource = "/api/{apiKey}/?cmd=show.addnew", Method = Method.GET };
|
||||||
{
|
|
||||||
Resource = "/api/{apiKey}/?cmd=show.addnew",
|
|
||||||
Method = Method.GET
|
|
||||||
};
|
|
||||||
request.AddUrlSegment("apiKey", apiKey);
|
request.AddUrlSegment("apiKey", apiKey);
|
||||||
request.AddQueryParameter("tvdbid", tvdbId.ToString());
|
request.AddQueryParameter("tvdbid", tvdbId.ToString());
|
||||||
request.AddQueryParameter("status", status);
|
request.AddQueryParameter("status", status);
|
||||||
|
@ -80,9 +95,11 @@ namespace PlexRequests.Api
|
||||||
request.AddQueryParameter("initial", quality);
|
request.AddQueryParameter("initial", quality);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Trace("Entering `Execute<SickRageTvAdd>`");
|
var policy = RetryHandler.RetryAndWaitPolicy(
|
||||||
var obj = Api.Execute<SickRageTvAdd>(request, baseUrl);
|
null,
|
||||||
Log.Trace("Exiting `Execute<SickRageTvAdd>`");
|
(exception, timespan) => Log.Error(exception, "Exception when calling AddSeries for SR, Retrying {0}", timespan));
|
||||||
|
|
||||||
|
var obj = policy.Execute(() => Api.Execute<SickRageTvAdd>(request, baseUrl));
|
||||||
Log.Trace("obj Result:");
|
Log.Trace("obj Result:");
|
||||||
Log.Trace(obj.DumpJson());
|
Log.Trace(obj.DumpJson());
|
||||||
|
|
||||||
|
@ -93,7 +110,6 @@ namespace PlexRequests.Api
|
||||||
|
|
||||||
var seasonIncrement = 0;
|
var seasonIncrement = 0;
|
||||||
var seasonList = new SickRageSeasonList();
|
var seasonList = new SickRageSeasonList();
|
||||||
Log.Trace("while (seasonIncrement < seasonCount) where seasonCount = {0}", seasonCount);
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (seasonIncrement < seasonCount)
|
while (seasonIncrement < seasonCount)
|
||||||
|
@ -130,6 +146,7 @@ namespace PlexRequests.Api
|
||||||
foreach (var s in seasons)
|
foreach (var s in seasons)
|
||||||
{
|
{
|
||||||
Log.Trace("Adding season {0}", s);
|
Log.Trace("Adding season {0}", s);
|
||||||
|
|
||||||
var result = await AddSeason(tvdbId, s, apiKey, baseUrl);
|
var result = await AddSeason(tvdbId, s, apiKey, baseUrl);
|
||||||
Log.Trace("SickRage adding season results: ");
|
Log.Trace("SickRage adding season results: ");
|
||||||
Log.Trace(result.DumpJson());
|
Log.Trace(result.DumpJson());
|
||||||
|
@ -149,76 +166,62 @@ namespace PlexRequests.Api
|
||||||
|
|
||||||
public SickRagePing Ping(string apiKey, Uri baseUrl)
|
public SickRagePing Ping(string apiKey, Uri baseUrl)
|
||||||
{
|
{
|
||||||
var request = new RestRequest
|
var request = new RestRequest { Resource = "/api/{apiKey}/?cmd=sb.ping", Method = Method.GET };
|
||||||
{
|
|
||||||
Resource = "/api/{apiKey}/?cmd=sb.ping",
|
var policy = RetryHandler.RetryAndWaitPolicy(
|
||||||
Method = Method.GET
|
null,
|
||||||
};
|
(exception, timespan) => Log.Error(exception, "Exception when calling Ping for SR, Retrying {0}", timespan));
|
||||||
|
|
||||||
request.AddUrlSegment("apiKey", apiKey);
|
request.AddUrlSegment("apiKey", apiKey);
|
||||||
var obj = Api.ExecuteJson<SickRagePing>(request, baseUrl);
|
var obj = policy.Execute(() => Api.ExecuteJson<SickRagePing>(request, baseUrl));
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SickRageSeasonList VerifyShowHasLoaded(int tvdbId, string apiKey, Uri baseUrl)
|
|
||||||
{
|
|
||||||
Log.Trace("Entered `VerifyShowHasLoaded({0} <- id)`", tvdbId);
|
|
||||||
var request = new RestRequest
|
|
||||||
{
|
|
||||||
Resource = "/api/{apiKey}/?cmd=show.seasonlist",
|
|
||||||
Method = Method.GET
|
|
||||||
};
|
|
||||||
request.AddUrlSegment("apiKey", apiKey);
|
|
||||||
request.AddQueryParameter("tvdbid", tvdbId.ToString());
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Log.Trace("Entering `ExecuteJson<SickRageSeasonList>`");
|
|
||||||
var obj = Api.ExecuteJson<SickRageSeasonList>(request, baseUrl);
|
|
||||||
Log.Trace("Exited `ExecuteJson<SickRageSeasonList>`");
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(e);
|
|
||||||
return new SickRageSeasonList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<SickRageTvAdd> AddSeason(int tvdbId, int season, string apiKey, Uri baseUrl)
|
public async Task<SickRageTvAdd> AddSeason(int tvdbId, int season, string apiKey, Uri baseUrl)
|
||||||
{
|
{
|
||||||
var request = new RestRequest
|
var request = new RestRequest { Resource = "/api/{apiKey}/?cmd=episode.setstatus", Method = Method.GET };
|
||||||
{
|
|
||||||
Resource = "/api/{apiKey}/?cmd=episode.setstatus",
|
|
||||||
Method = Method.GET
|
|
||||||
};
|
|
||||||
request.AddUrlSegment("apiKey", apiKey);
|
request.AddUrlSegment("apiKey", apiKey);
|
||||||
request.AddQueryParameter("tvdbid", tvdbId.ToString());
|
request.AddQueryParameter("tvdbid", tvdbId.ToString());
|
||||||
request.AddQueryParameter("season", season.ToString());
|
request.AddQueryParameter("season", season.ToString());
|
||||||
request.AddQueryParameter("status", SickRageStatus.Wanted);
|
request.AddQueryParameter("status", SickRageStatus.Wanted);
|
||||||
|
|
||||||
await Task.Run(() => Thread.Sleep(2000));
|
await Task.Run(() => Thread.Sleep(2000));
|
||||||
return await Task.Run(() =>
|
return await Task.Run(
|
||||||
{
|
() =>
|
||||||
Log.Trace("Entering `Execute<SickRageTvAdd>` in a new `Task<T>`");
|
{
|
||||||
var result = Api.Execute<SickRageTvAdd>(request, baseUrl);
|
var policy = RetryHandler.RetryAndWaitPolicy(
|
||||||
|
null,
|
||||||
|
(exception, timespan) => Log.Error(exception, "Exception when calling AddSeason for SR, Retrying {0}", timespan));
|
||||||
|
|
||||||
Log.Trace("Exiting `Execute<SickRageTvAdd>` and yeilding `Task<T>` result");
|
var result = policy.Execute(() => Api.Execute<SickRageTvAdd>(request, baseUrl));
|
||||||
return result;
|
|
||||||
}).ConfigureAwait(false);
|
return result;
|
||||||
|
}).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SickrageShows> GetShows(string apiKey, Uri baseUrl)
|
public async Task<SickrageShows> GetShows(string apiKey, Uri baseUrl)
|
||||||
{
|
{
|
||||||
var request = new RestRequest
|
var request = new RestRequest { Resource = "/api/{apiKey}/?cmd=shows", Method = Method.GET };
|
||||||
{
|
|
||||||
Resource = "/api/{apiKey}/?cmd=shows",
|
|
||||||
Method = Method.GET
|
|
||||||
};
|
|
||||||
request.AddUrlSegment("apiKey", apiKey);
|
request.AddUrlSegment("apiKey", apiKey);
|
||||||
|
|
||||||
return await Task.Run(() => Api.Execute<SickrageShows>(request, baseUrl)).ConfigureAwait(false);
|
return await Task.Run(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var policy = RetryHandler.RetryAndWaitPolicy(
|
||||||
|
new[] { TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(30) },
|
||||||
|
(exception, timespan) => Log.Error(exception, "Exception when calling GetShows for SR, Retrying {0}", timespan));
|
||||||
|
|
||||||
|
return policy.Execute(() => Api.Execute<SickrageShows>(request, baseUrl));
|
||||||
|
}
|
||||||
|
catch (ApiRequestException)
|
||||||
|
{
|
||||||
|
Log.Error("There has been a API exception when Getting the Sickrage shows");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
62
PlexRequests.Api/SlackApi.cs
Normal file
62
PlexRequests.Api/SlackApi.cs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
#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 System.Threading.Tasks;
|
||||||
|
|
||||||
|
using PlexRequests.Api.Interfaces;
|
||||||
|
using PlexRequests.Api.Models.Notifications;
|
||||||
|
|
||||||
|
using RestSharp;
|
||||||
|
|
||||||
|
namespace PlexRequests.Api
|
||||||
|
{
|
||||||
|
public class SlackApi : ISlackApi
|
||||||
|
{
|
||||||
|
public async Task<string> PushAsync(string team, string token, string service, SlackNotificationBody message)
|
||||||
|
{
|
||||||
|
var request = new RestRequest
|
||||||
|
{
|
||||||
|
Method = Method.POST,
|
||||||
|
Resource = "/services/{team}/{service}/{token}"
|
||||||
|
};
|
||||||
|
|
||||||
|
request.AddUrlSegment("team", team);
|
||||||
|
request.AddUrlSegment("service", service);
|
||||||
|
request.AddUrlSegment("token", token);
|
||||||
|
request.AddJsonBody(message);
|
||||||
|
|
||||||
|
var api = new ApiRequest();
|
||||||
|
return await Task.Run(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
var result = api.Execute(request, new Uri("https://hooks.slack.com/"));
|
||||||
|
return result.Content;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -38,6 +38,8 @@ using PlexRequests.Helpers;
|
||||||
using RestSharp;
|
using RestSharp;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
using PlexRequests.Helpers.Exceptions;
|
||||||
|
|
||||||
namespace PlexRequests.Api
|
namespace PlexRequests.Api
|
||||||
{
|
{
|
||||||
public class SonarrApi : ISonarrApi
|
public class SonarrApi : ISonarrApi
|
||||||
|
@ -54,8 +56,13 @@ namespace PlexRequests.Api
|
||||||
var request = new RestRequest { Resource = "/api/profile", Method = Method.GET };
|
var request = new RestRequest { Resource = "/api/profile", Method = Method.GET };
|
||||||
|
|
||||||
request.AddHeader("X-Api-Key", apiKey);
|
request.AddHeader("X-Api-Key", apiKey);
|
||||||
|
var policy = RetryHandler.RetryAndWaitPolicy (new TimeSpan[] {
|
||||||
|
TimeSpan.FromSeconds (2),
|
||||||
|
TimeSpan.FromSeconds(5),
|
||||||
|
TimeSpan.FromSeconds(10)
|
||||||
|
}, (exception, timespan) => Log.Error (exception, "Exception when calling GetProfiles for Sonarr, Retrying {0}", timespan));
|
||||||
|
|
||||||
var obj = Api.ExecuteJson<List<SonarrProfile>>(request, baseUrl);
|
var obj = policy.Execute(() => Api.ExecuteJson<List<SonarrProfile>>(request, baseUrl));
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
@ -100,7 +107,13 @@ namespace PlexRequests.Api
|
||||||
SonarrAddSeries result;
|
SonarrAddSeries result;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
result = Api.ExecuteJson<SonarrAddSeries>(request, baseUrl);
|
var policy = RetryHandler.RetryAndWaitPolicy (new TimeSpan[] {
|
||||||
|
TimeSpan.FromSeconds (2),
|
||||||
|
TimeSpan.FromSeconds(5),
|
||||||
|
TimeSpan.FromSeconds(10)
|
||||||
|
}, (exception, timespan) => Log.Error (exception, "Exception when calling AddSeries for Sonarr, Retrying {0}", timespan));
|
||||||
|
|
||||||
|
result = policy.Execute(() => Api.ExecuteJson<SonarrAddSeries>(request, baseUrl));
|
||||||
}
|
}
|
||||||
catch (JsonSerializationException jse)
|
catch (JsonSerializationException jse)
|
||||||
{
|
{
|
||||||
|
@ -119,7 +132,13 @@ namespace PlexRequests.Api
|
||||||
var request = new RestRequest { Resource = "/api/system/status", Method = Method.GET };
|
var request = new RestRequest { Resource = "/api/system/status", Method = Method.GET };
|
||||||
request.AddHeader("X-Api-Key", apiKey);
|
request.AddHeader("X-Api-Key", apiKey);
|
||||||
|
|
||||||
var obj = Api.ExecuteJson<SystemStatus>(request, baseUrl);
|
var policy = RetryHandler.RetryAndWaitPolicy (new TimeSpan[] {
|
||||||
|
TimeSpan.FromSeconds (2),
|
||||||
|
TimeSpan.FromSeconds(5),
|
||||||
|
TimeSpan.FromSeconds(10)
|
||||||
|
}, (exception, timespan) => Log.Error (exception, "Exception when calling SystemStatus for Sonarr, Retrying {0}", timespan));
|
||||||
|
|
||||||
|
var obj = policy.Execute(() => Api.ExecuteJson<SystemStatus>(request, baseUrl));
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
@ -128,8 +147,21 @@ namespace PlexRequests.Api
|
||||||
{
|
{
|
||||||
var request = new RestRequest { Resource = "/api/series", Method = Method.GET };
|
var request = new RestRequest { Resource = "/api/series", Method = Method.GET };
|
||||||
request.AddHeader("X-Api-Key", apiKey);
|
request.AddHeader("X-Api-Key", apiKey);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var policy = RetryHandler.RetryAndWaitPolicy (new TimeSpan[] {
|
||||||
|
TimeSpan.FromSeconds (5),
|
||||||
|
TimeSpan.FromSeconds(10),
|
||||||
|
TimeSpan.FromSeconds(30)
|
||||||
|
}, (exception, timespan) => Log.Error (exception, "Exception when calling GetSeries for Sonarr, Retrying {0}", timespan));
|
||||||
|
|
||||||
return Api.Execute<List<Series>>(request, baseUrl);
|
return policy.Execute(() => Api.ExecuteJson<List<Series>>(request, baseUrl));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e, "There has been an API exception when getting the Sonarr Series");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -50,7 +50,7 @@ namespace PlexRequests.Api
|
||||||
return results.Results;
|
return results.Results;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("Should use TheTvDbApi for TV")]
|
[Obsolete("Should use TvMaze for TV")]
|
||||||
public async Task<List<SearchTv>> SearchTv(string searchTerm)
|
public async Task<List<SearchTv>> SearchTv(string searchTerm)
|
||||||
{
|
{
|
||||||
var results = await Client.SearchTvShow(searchTerm);
|
var results = await Client.SearchTvShow(searchTerm);
|
||||||
|
@ -74,7 +74,7 @@ namespace PlexRequests.Api
|
||||||
return movies;
|
return movies;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("Should use TheTvDbApi for TV")]
|
[Obsolete("Should use TvMaze for TV")]
|
||||||
public async Task<TvShow> GetTvShowInformation(int tmdbId)
|
public async Task<TvShow> GetTvShowInformation(int tmdbId)
|
||||||
{
|
{
|
||||||
var show = await Client.GetTvShow(tmdbId);
|
var show = await Client.GetTvShow(tmdbId);
|
||||||
|
|
|
@ -24,12 +24,15 @@
|
||||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
// ************************************************************************/
|
// ************************************************************************/
|
||||||
#endregion
|
#endregion
|
||||||
|
using System;
|
||||||
|
|
||||||
using PlexRequests.Api.Models.Tv;
|
using PlexRequests.Api.Models.Tv;
|
||||||
|
|
||||||
using RestSharp;
|
using RestSharp;
|
||||||
|
|
||||||
namespace PlexRequests.Api
|
namespace PlexRequests.Api
|
||||||
{
|
{
|
||||||
|
[Obsolete("Use TVMazeAPP")]
|
||||||
public class TheTvDbApi : TvBase
|
public class TheTvDbApi : TvBase
|
||||||
{
|
{
|
||||||
public TheTvDbApi()
|
public TheTvDbApi()
|
||||||
|
|
|
@ -85,7 +85,7 @@ namespace PlexRequests.Api
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetSeasonCount(int id)
|
public List<TvMazeSeasons> GetSeasons(int id)
|
||||||
{
|
{
|
||||||
var request = new RestRequest
|
var request = new RestRequest
|
||||||
{
|
{
|
||||||
|
@ -95,7 +95,11 @@ namespace PlexRequests.Api
|
||||||
request.AddUrlSegment("id", id.ToString());
|
request.AddUrlSegment("id", id.ToString());
|
||||||
request.AddHeader("Content-Type", "application/json");
|
request.AddHeader("Content-Type", "application/json");
|
||||||
|
|
||||||
var obj = Api.Execute<List<TvMazeSeasons>>(request, new Uri(Uri));
|
return Api.Execute<List<TvMazeSeasons>>(request, new Uri(Uri));
|
||||||
|
}
|
||||||
|
public int GetSeasonCount(int id)
|
||||||
|
{
|
||||||
|
var obj = GetSeasons(id);
|
||||||
var seasons = obj.Select(x => x.number > 0);
|
var seasons = obj.Select(x => x.number > 0);
|
||||||
return seasons.Count();
|
return seasons.Count();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.5.2"/></startup></configuration>
|
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /></startup></configuration>
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="Dapper" version="1.42" targetFramework="net452" />
|
<package id="Dapper" version="1.42" targetFramework="net45" />
|
||||||
<package id="Nancy" version="1.4.3" targetFramework="net452" />
|
<package id="Nancy" version="1.4.3" targetFramework="net45" />
|
||||||
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net452" />
|
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net45" />
|
||||||
<package id="NLog" version="4.2.3" targetFramework="net452" />
|
<package id="NLog" version="4.2.3" targetFramework="net45" />
|
||||||
<package id="RestSharp" version="105.2.3" targetFramework="net452" />
|
<package id="Polly-Signed" version="4.2.0" targetFramework="net45" />
|
||||||
<package id="TMDbLib" version="0.9.0.0-alpha" targetFramework="net452" />
|
<package id="RestSharp" version="105.2.3" targetFramework="net45" />
|
||||||
|
<package id="TMDbLib" version="0.9.0.0-alpha" targetFramework="net45" />
|
||||||
</packages>
|
</packages>
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
|
|
@ -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.5.2"/></startup></configuration>
|
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /></startup></configuration>
|
||||||
|
|
|
@ -28,6 +28,11 @@ namespace PlexRequests.Core
|
||||||
{
|
{
|
||||||
public class CacheKeys
|
public class CacheKeys
|
||||||
{
|
{
|
||||||
|
public struct TimeFrameMinutes
|
||||||
|
{
|
||||||
|
public const int SchedulerCaching = 60;
|
||||||
|
}
|
||||||
|
|
||||||
public const string PlexLibaries = "PlexLibaries";
|
public const string PlexLibaries = "PlexLibaries";
|
||||||
|
|
||||||
public const string TvDbToken = "TheTvDbApiToken";
|
public const string TvDbToken = "TheTvDbApiToken";
|
||||||
|
@ -41,6 +46,8 @@ namespace PlexRequests.Core
|
||||||
public const string CouchPotatoQualityProfiles = "CouchPotatoQualityProfiles";
|
public const string CouchPotatoQualityProfiles = "CouchPotatoQualityProfiles";
|
||||||
public const string CouchPotatoQueued = "CouchPotatoQueued";
|
public const string CouchPotatoQueued = "CouchPotatoQueued";
|
||||||
|
|
||||||
public const string GetBaseUrl = "GetBaseUrl";
|
public const string GetPlexRequestSettings = "GetPlexRequestSettings";
|
||||||
|
|
||||||
|
public const string LastestProductVersion = "LatestProductVersion";
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -49,10 +49,9 @@ namespace PlexRequests.Core
|
||||||
var entity = new RequestBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), ProviderId = model.ProviderId };
|
var entity = new RequestBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), ProviderId = model.ProviderId };
|
||||||
var id = Repo.Insert(entity);
|
var id = Repo.Insert(entity);
|
||||||
|
|
||||||
// TODO Keep an eye on this, since we are now doing 2 DB update for 1 single request, inserting and then updating
|
|
||||||
model.Id = (int)id;
|
model.Id = (int)id;
|
||||||
|
|
||||||
entity = new RequestBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), ProviderId = model.ProviderId, Id = (int)id, MusicId = model.MusicBrainzId};
|
entity = new RequestBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), ProviderId = model.ProviderId, Id = (int)id, MusicId = model.MusicBrainzId };
|
||||||
var result = Repo.Update(entity);
|
var result = Repo.Update(entity);
|
||||||
|
|
||||||
return result ? id : -1;
|
return result ? id : -1;
|
||||||
|
|
|
@ -24,21 +24,11 @@
|
||||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
// ************************************************************************/
|
// ************************************************************************/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
namespace PlexRequests.Core.Models
|
namespace PlexRequests.Core.Models
|
||||||
{
|
{
|
||||||
public class StatusModel
|
public class StatusModel
|
||||||
{
|
{
|
||||||
public string Version { get; set; }
|
public string Version { get; set; }
|
||||||
public int DBVersion {
|
|
||||||
get
|
|
||||||
{
|
|
||||||
string trimStatus = new Regex("[^0-9]", RegexOptions.Compiled).Replace(Version, string.Empty).PadRight(4, '0');
|
|
||||||
return int.Parse(trimStatus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public bool UpdateAvailable { get; set; }
|
public bool UpdateAvailable { get; set; }
|
||||||
public string UpdateUri { get; set; }
|
public string UpdateUri { get; set; }
|
||||||
public string DownloadUri { get; set; }
|
public string DownloadUri { get; set; }
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>PlexRequests.Core</RootNamespace>
|
<RootNamespace>PlexRequests.Core</RootNamespace>
|
||||||
<AssemblyName>PlexRequests.Core</AssemblyName>
|
<AssemblyName>PlexRequests.Core</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -34,30 +34,6 @@
|
||||||
<Reference Include="Mono.Data.Sqlite">
|
<Reference Include="Mono.Data.Sqlite">
|
||||||
<HintPath>..\Assemblies\Mono.Data.Sqlite.dll</HintPath>
|
<HintPath>..\Assemblies\Mono.Data.Sqlite.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Nancy, Version=1.4.2.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\Nancy.1.4.3\lib\net40\Nancy.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Nancy.Authentication.Forms, Version=1.4.1.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\Nancy.Authentication.Forms.1.4.1\lib\net40\Nancy.Authentication.Forms.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<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="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Octokit, Version=0.19.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\Octokit.0.19.0\lib\net45\Octokit.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Omu.ValueInjecter, Version=3.1.1.0, Culture=neutral, PublicKeyToken=c7694541b0ac80e4, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\valueinjecter.3.1.1.2\lib\net40\Omu.ValueInjecter.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" />
|
||||||
|
@ -69,6 +45,24 @@
|
||||||
<Reference Include="TMDbLib">
|
<Reference Include="TMDbLib">
|
||||||
<HintPath>..\packages\TMDbLib.0.9.0.0-alpha\lib\net45\TMDbLib.dll</HintPath>
|
<HintPath>..\packages\TMDbLib.0.9.0.0-alpha\lib\net45\TMDbLib.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Nancy, Version=1.4.2.0, Culture=neutral, PublicKeyToken=null">
|
||||||
|
<HintPath>..\packages\Nancy.1.4.3\lib\net40\Nancy.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Nancy.Authentication.Forms, Version=1.4.1.0, Culture=neutral, PublicKeyToken=null">
|
||||||
|
<HintPath>..\packages\Nancy.Authentication.Forms.1.4.1\lib\net40\Nancy.Authentication.Forms.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
|
||||||
|
<HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c">
|
||||||
|
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Octokit, Version=0.19.0.0, Culture=neutral, PublicKeyToken=null">
|
||||||
|
<HintPath>..\packages\Octokit.0.19.0\lib\net45\Octokit.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Omu.ValueInjecter, Version=3.1.1.0, Culture=neutral, PublicKeyToken=c7694541b0ac80e4">
|
||||||
|
<HintPath>..\packages\valueinjecter.3.1.1.2\lib\net40\Omu.ValueInjecter.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="CacheKeys.cs" />
|
<Compile Include="CacheKeys.cs" />
|
||||||
|
@ -79,10 +73,12 @@
|
||||||
<Compile Include="Models\UserProperties.cs" />
|
<Compile Include="Models\UserProperties.cs" />
|
||||||
<Compile Include="SettingModels\AuthenticationSettings.cs" />
|
<Compile Include="SettingModels\AuthenticationSettings.cs" />
|
||||||
<Compile Include="SettingModels\HeadphonesSettings.cs" />
|
<Compile Include="SettingModels\HeadphonesSettings.cs" />
|
||||||
|
<Compile Include="SettingModels\SlackNotificationSettings.cs" />
|
||||||
<Compile Include="SettingModels\PushoverNotificationSettings.cs" />
|
<Compile Include="SettingModels\PushoverNotificationSettings.cs" />
|
||||||
<Compile Include="SettingModels\PushBulletNotificationSettings.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\LogSettings.cs" />
|
||||||
<Compile Include="SettingModels\SonarrSettings.cs" />
|
<Compile Include="SettingModels\SonarrSettings.cs" />
|
||||||
<Compile Include="SettingModels\SickRageSettings.cs" />
|
<Compile Include="SettingModels\SickRageSettings.cs" />
|
||||||
<Compile Include="SettingModels\CouchPotatoSettings.cs" />
|
<Compile Include="SettingModels\CouchPotatoSettings.cs" />
|
||||||
|
@ -101,7 +97,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\PlexRequests.Api.Interfaces\PlexRequests.Api.Interfaces.csproj">
|
<ProjectReference Include="..\PlexRequests.Api.Interfaces\PlexRequests.Api.Interfaces.csproj">
|
||||||
<Project>{95834072-a675-415d-aa8f-877c91623810}</Project>
|
<Project>{95834072-A675-415D-AA8F-877C91623810}</Project>
|
||||||
<Name>PlexRequests.Api.Interfaces</Name>
|
<Name>PlexRequests.Api.Interfaces</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\PlexRequests.Api.Models\PlexRequests.Api.Models.csproj">
|
<ProjectReference Include="..\PlexRequests.Api.Models\PlexRequests.Api.Models.csproj">
|
||||||
|
|
|
@ -1,14 +1,40 @@
|
||||||
namespace PlexRequests.Core.SettingModels
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2016 Jamie Rees
|
||||||
|
// File: EmailNotificationSettings.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.Core.SettingModels
|
||||||
{
|
{
|
||||||
public class EmailNotificationSettings : Settings
|
public class EmailNotificationSettings : Settings
|
||||||
{
|
{
|
||||||
public string EmailHost { get; set; }
|
public string EmailHost { get; set; }
|
||||||
|
public string EmailPassword { get; set; }
|
||||||
public int EmailPort { get; set; }
|
public int EmailPort { get; set; }
|
||||||
public bool Ssl { get; set; }
|
|
||||||
public string RecipientEmail { get; set; }
|
|
||||||
public string EmailSender { get; set; }
|
public string EmailSender { get; set; }
|
||||||
public string EmailUsername { get; set; }
|
public string EmailUsername { get; set; }
|
||||||
public string EmailPassword { get; set; }
|
|
||||||
public bool Enabled { get; set; }
|
public bool Enabled { get; set; }
|
||||||
|
public bool EnableUserEmailNotifications { get; set; }
|
||||||
|
public string RecipientEmail { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
36
PlexRequests.Core/SettingModels/LogSettings.cs
Normal file
36
PlexRequests.Core/SettingModels/LogSettings.cs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2016 Jamie Rees
|
||||||
|
// File: SickRageSettings.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 NLog;
|
||||||
|
|
||||||
|
namespace PlexRequests.Core.SettingModels
|
||||||
|
{
|
||||||
|
public class LogSettings : Settings
|
||||||
|
{
|
||||||
|
public int Level { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,6 +44,13 @@ namespace PlexRequests.Core.SettingModels
|
||||||
public int WeeklyRequestLimit { get; set; }
|
public int WeeklyRequestLimit { get; set; }
|
||||||
public string NoApprovalUsers { get; set; }
|
public string NoApprovalUsers { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The CSS name of the theme we want
|
||||||
|
/// </summary>
|
||||||
|
public string ThemeName { get; set; }
|
||||||
|
|
||||||
|
public string ApiKey { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public List<string> ApprovalWhiteList
|
public List<string> ApprovalWhiteList
|
||||||
{
|
{
|
||||||
|
|
37
PlexRequests.Core/SettingModels/SlackNotificationSettings.cs
Normal file
37
PlexRequests.Core/SettingModels/SlackNotificationSettings.cs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace PlexRequests.Core.SettingModels
|
||||||
|
{
|
||||||
|
public class SlackNotificationSettings : Settings
|
||||||
|
{
|
||||||
|
public bool Enabled { get; set; }
|
||||||
|
public string WebhookUrl { get; set; }
|
||||||
|
public string Channel { get; set; }
|
||||||
|
public string Username { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string Team => SplitWebUrl(3);
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string Service => SplitWebUrl(4);
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string Token => SplitWebUrl(5);
|
||||||
|
|
||||||
|
private string SplitWebUrl(int index)
|
||||||
|
{
|
||||||
|
if (!WebhookUrl.StartsWith("http", StringComparison.InvariantCulture))
|
||||||
|
{
|
||||||
|
WebhookUrl = "https://" + WebhookUrl;
|
||||||
|
}
|
||||||
|
var split = WebhookUrl.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
return split.Length < index
|
||||||
|
? string.Empty
|
||||||
|
: split[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,6 +28,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
using Mono.Data.Sqlite;
|
using Mono.Data.Sqlite;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
@ -58,10 +59,9 @@ namespace PlexRequests.Core
|
||||||
var version = CheckSchema();
|
var version = CheckSchema();
|
||||||
if (version > 0)
|
if (version > 0)
|
||||||
{
|
{
|
||||||
if (version > 1300 && version <= 1699)
|
if (version > 1700 && version <= 1799)
|
||||||
{
|
{
|
||||||
MigrateDbFrom1300();
|
MigrateToVersion1700();
|
||||||
UpdateRequestBlobsTable();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,18 +73,19 @@ namespace PlexRequests.Core
|
||||||
|
|
||||||
private int CheckSchema()
|
private int CheckSchema()
|
||||||
{
|
{
|
||||||
var checker = new StatusChecker();
|
var productVersion = AssemblyHelper.GetProductVersion();
|
||||||
var status = checker.GetStatus();
|
var trimStatus = new Regex("[^0-9]", RegexOptions.Compiled).Replace(productVersion, string.Empty).PadRight(4, '0');
|
||||||
|
var version = int.Parse(trimStatus);
|
||||||
|
|
||||||
var connection = Db.DbConnection();
|
var connection = Db.DbConnection();
|
||||||
var schema = connection.GetSchemaVersion();
|
var schema = connection.GetSchemaVersion();
|
||||||
if (schema == null)
|
if (schema == null)
|
||||||
{
|
{
|
||||||
connection.CreateSchema(status.DBVersion); // Set the default.
|
connection.CreateSchema(version); // Set the default.
|
||||||
schema = connection.GetSchemaVersion();
|
schema = connection.GetSchemaVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
var version = schema.SchemaVersion;
|
version = schema.SchemaVersion;
|
||||||
|
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
@ -139,7 +140,7 @@ namespace PlexRequests.Core
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Error("Failed to cache Sonarr quality profiles!", ex);
|
Log.Error(ex, "Failed to cache Sonarr quality profiles!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,97 +162,14 @@ namespace PlexRequests.Core
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Error("Failed to cache CouchPotato quality profiles!", ex);
|
Log.Error(ex, "Failed to cache CouchPotato quality profiles!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public void MigrateToVersion1700()
|
||||||
private void UpdateRequestBlobsTable() // TODO: Remove in v1.7
|
|
||||||
{
|
{
|
||||||
try
|
// Drop old tables
|
||||||
{
|
TableCreation.DropTable(Db.DbConnection(), "User");
|
||||||
TableCreation.AlterTable(Db.DbConnection(), "RequestBlobs", "ADD COLUMN", "MusicId", false, "TEXT");
|
TableCreation.DropTable(Db.DbConnection(), "Log");
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error("Tried updating the schema to alter the request blobs table");
|
|
||||||
Log.Error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void MigrateDbFrom1300() // TODO: Remove in v1.7
|
|
||||||
{
|
|
||||||
|
|
||||||
var result = new List<long>();
|
|
||||||
RequestedModel[] requestedModels;
|
|
||||||
var repo = new GenericRepository<RequestedModel>(Db, new MemoryCacheProvider());
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var records = repo.GetAll();
|
|
||||||
requestedModels = records as RequestedModel[] ?? records.ToArray();
|
|
||||||
}
|
|
||||||
catch (SqliteException)
|
|
||||||
{
|
|
||||||
// There is no requested table so they do not have an old version of the DB
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!requestedModels.Any())
|
|
||||||
{ return; }
|
|
||||||
|
|
||||||
var jsonRepo = new JsonRequestService(new RequestJsonRepository(Db, new MemoryCacheProvider()));
|
|
||||||
|
|
||||||
var api = new TvMazeApi();
|
|
||||||
|
|
||||||
foreach (var r in requestedModels.Where(x => x.Type == RequestType.TvShow))
|
|
||||||
{
|
|
||||||
var show = api.ShowLookupByTheTvDbId(r.ProviderId);
|
|
||||||
|
|
||||||
var model = new RequestedModel
|
|
||||||
{
|
|
||||||
Title = show.name,
|
|
||||||
PosterPath = show.image?.medium,
|
|
||||||
Type = RequestType.TvShow,
|
|
||||||
ProviderId = show.externals.thetvdb ?? 0,
|
|
||||||
ReleaseDate = r.ReleaseDate,
|
|
||||||
AdminNote = r.AdminNote,
|
|
||||||
Approved = r.Approved,
|
|
||||||
Available = r.Available,
|
|
||||||
ImdbId = show.externals.imdb,
|
|
||||||
Issues = r.Issues,
|
|
||||||
OtherMessage = r.OtherMessage,
|
|
||||||
Overview = show.summary.RemoveHtml(),
|
|
||||||
RequestedUsers = r.AllUsers, // should pull in the RequestedBy property and merge with RequestedUsers
|
|
||||||
RequestedDate = r.ReleaseDate,
|
|
||||||
Status = show.status
|
|
||||||
};
|
|
||||||
var id = jsonRepo.AddRequest(model);
|
|
||||||
result.Add(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var source in requestedModels.Where(x => x.Type == RequestType.Movie))
|
|
||||||
{
|
|
||||||
var id = jsonRepo.AddRequest(source);
|
|
||||||
result.Add(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (result.Any(x => x == -1))
|
|
||||||
{
|
|
||||||
throw new SqliteException("Could not migrate the DB!");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (result.Count != requestedModels.Length)
|
|
||||||
{
|
|
||||||
throw new SqliteException("Could not migrate the DB! count is different");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Now delete the old requests
|
|
||||||
foreach (var oldRequest in requestedModels)
|
|
||||||
{
|
|
||||||
repo.Delete(oldRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,21 +36,20 @@ using Nancy.Security;
|
||||||
using PlexRequests.Core.Models;
|
using PlexRequests.Core.Models;
|
||||||
using PlexRequests.Helpers;
|
using PlexRequests.Helpers;
|
||||||
using PlexRequests.Store;
|
using PlexRequests.Store;
|
||||||
|
using PlexRequests.Store.Repository;
|
||||||
|
|
||||||
namespace PlexRequests.Core
|
namespace PlexRequests.Core
|
||||||
{
|
{
|
||||||
public class UserMapper : IUserMapper
|
public class UserMapper : IUserMapper, ICustomUserMapper
|
||||||
{
|
{
|
||||||
public UserMapper(ISqliteConfiguration db)
|
public UserMapper(IRepository<UsersModel> repo)
|
||||||
{
|
{
|
||||||
Db = db;
|
Repo = repo;
|
||||||
}
|
}
|
||||||
private static ISqliteConfiguration Db { get; set; }
|
private static IRepository<UsersModel> Repo { get; set; }
|
||||||
public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context)
|
public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context)
|
||||||
{
|
{
|
||||||
var repo = new UserRepository<UsersModel>(Db);
|
var user = Repo.Get(identifier.ToString());
|
||||||
|
|
||||||
var user = repo.Get(identifier.ToString());
|
|
||||||
|
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
|
@ -64,10 +63,9 @@ namespace PlexRequests.Core
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Guid? ValidateUser(string username, string password)
|
public Guid? ValidateUser(string username, string password)
|
||||||
{
|
{
|
||||||
var repo = new UserRepository<UsersModel>(Db);
|
var users = Repo.GetAll();
|
||||||
var users = repo.GetAll();
|
|
||||||
|
|
||||||
foreach (var u in users)
|
foreach (var u in users)
|
||||||
{
|
{
|
||||||
|
@ -83,17 +81,15 @@ namespace PlexRequests.Core
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool DoUsersExist()
|
public bool DoUsersExist()
|
||||||
{
|
{
|
||||||
var repo = new UserRepository<UsersModel>(Db);
|
var users = Repo.GetAll();
|
||||||
var users = repo.GetAll();
|
|
||||||
|
|
||||||
return users.Any();
|
return users.Any();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Guid? CreateUser(string username, string password, string[] claims = default(string[]))
|
public Guid? CreateUser(string username, string password, string[] claims = default(string[]))
|
||||||
{
|
{
|
||||||
var repo = new UserRepository<UsersModel>(Db);
|
|
||||||
var salt = PasswordHasher.GenerateSalt();
|
var salt = PasswordHasher.GenerateSalt();
|
||||||
|
|
||||||
var userModel = new UsersModel
|
var userModel = new UsersModel
|
||||||
|
@ -105,17 +101,16 @@ namespace PlexRequests.Core
|
||||||
Claims = ByteConverterHelper.ReturnBytes(claims),
|
Claims = ByteConverterHelper.ReturnBytes(claims),
|
||||||
UserProperties = ByteConverterHelper.ReturnBytes(new UserProperties())
|
UserProperties = ByteConverterHelper.ReturnBytes(new UserProperties())
|
||||||
};
|
};
|
||||||
repo.Insert(userModel);
|
Repo.Insert(userModel);
|
||||||
|
|
||||||
var userRecord = repo.Get(userModel.UserGuid);
|
var userRecord = Repo.Get(userModel.UserGuid);
|
||||||
|
|
||||||
return new Guid(userRecord.UserGuid);
|
return new Guid(userRecord.UserGuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool UpdatePassword(string username, string oldPassword, string newPassword)
|
public bool UpdatePassword(string username, string oldPassword, string newPassword)
|
||||||
{
|
{
|
||||||
var repo = new UserRepository<UsersModel>(Db);
|
var users = Repo.GetAll();
|
||||||
var users = repo.GetAll();
|
|
||||||
var userToChange = users.FirstOrDefault(x => x.UserName == username);
|
var userToChange = users.FirstOrDefault(x => x.UserName == username);
|
||||||
if (userToChange == null)
|
if (userToChange == null)
|
||||||
return false;
|
return false;
|
||||||
|
@ -132,13 +127,22 @@ namespace PlexRequests.Core
|
||||||
userToChange.Hash = newHash;
|
userToChange.Hash = newHash;
|
||||||
userToChange.Salt = newSalt;
|
userToChange.Salt = newSalt;
|
||||||
|
|
||||||
return repo.Update(userToChange);
|
return Repo.Update(userToChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<UsersModel> GetUsers()
|
public IEnumerable<UsersModel> GetUsers()
|
||||||
{
|
{
|
||||||
var repo = new UserRepository<UsersModel>(Db);
|
return Repo.GetAll();
|
||||||
return repo.GetAll();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface ICustomUserMapper
|
||||||
|
{
|
||||||
|
IEnumerable<UsersModel> GetUsers();
|
||||||
|
Guid? CreateUser(string username, string password, string[] claims = default(string[]));
|
||||||
|
bool DoUsersExist();
|
||||||
|
Guid? ValidateUser(string username, string password);
|
||||||
|
bool UpdatePassword(string username, string oldPassword, string newPassword);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.5.2"/></startup></configuration>
|
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /></startup></configuration>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="Nancy" version="1.4.3" targetFramework="net452" />
|
<package id="Nancy" version="1.4.3" targetFramework="net45" />
|
||||||
<package id="Nancy.Authentication.Forms" version="1.4.1" targetFramework="net452" />
|
<package id="Nancy.Authentication.Forms" version="1.4.1" targetFramework="net45" />
|
||||||
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net452" />
|
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net45" />
|
||||||
<package id="NLog" version="4.2.3" targetFramework="net46" />
|
<package id="NLog" version="4.2.3" targetFramework="net45" />
|
||||||
<package id="Octokit" version="0.19.0" targetFramework="net46" />
|
<package id="Octokit" version="0.19.0" targetFramework="net45" />
|
||||||
<package id="valueinjecter" version="3.1.1.2" targetFramework="net452" />
|
<package id="valueinjecter" version="3.1.1.2" targetFramework="net45" />
|
||||||
</packages>
|
</packages>
|
|
@ -1,41 +1,42 @@
|
||||||
#region Copyright
|
#region Copyright
|
||||||
// /************************************************************************
|
// /************************************************************************
|
||||||
// Copyright (c) 2016 Jamie Rees
|
// Copyright (c) 2016 Jamie Rees
|
||||||
// File: PlexRegistry.cs
|
// File: ApplicationSettingsException.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
|
||||||
// a copy of this software and associated documentation files (the
|
// a copy of this software and associated documentation files (the
|
||||||
// "Software"), to deal in the Software without restriction, including
|
// "Software"), to deal in the Software without restriction, including
|
||||||
// without limitation the rights to use, copy, modify, merge, publish,
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
// permit persons to whom the Software is furnished to do so, subject to
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
// the following conditions:
|
// the following conditions:
|
||||||
//
|
//
|
||||||
// The above copyright notice and this permission notice shall be
|
// The above copyright notice and this permission notice shall be
|
||||||
// included in all copies or substantial portions of the Software.
|
// included in all copies or substantial portions of the Software.
|
||||||
//
|
//
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
// ************************************************************************/
|
// ************************************************************************/
|
||||||
#endregion
|
#endregion
|
||||||
|
using System;
|
||||||
using FluentScheduler;
|
|
||||||
|
namespace PlexRequests.Helpers.Exceptions
|
||||||
using PlexRequests.Services;
|
{
|
||||||
|
public class ApiRequestException : Exception
|
||||||
namespace PlexRequests.UI.Jobs
|
{
|
||||||
{
|
public ApiRequestException(string message) : base(message)
|
||||||
public class MediaCacheRegistry : Registry
|
{
|
||||||
{
|
|
||||||
public MediaCacheRegistry()
|
}
|
||||||
{
|
public ApiRequestException(string message, Exception innerException) : base(message, innerException)
|
||||||
Schedule<MediaCacheService>().ToRunNow();
|
{
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -61,7 +61,7 @@ namespace PlexRequests.Helpers
|
||||||
return dumpTarget.ToString();
|
return dumpTarget.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ConfigureLogging(string connectionString)
|
public static void ConfigureLogging(string connectionString)
|
||||||
{
|
{
|
||||||
LogManager.ThrowExceptions = true;
|
LogManager.ThrowExceptions = true;
|
||||||
// Step 1. Create configuration object
|
// Step 1. Create configuration object
|
||||||
|
|
|
@ -73,8 +73,8 @@ namespace PlexRequests.Helpers
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public T Get<T>(string key) where T : class
|
public T Get<T>(string key) where T : class
|
||||||
{
|
{
|
||||||
var item = Cache.Get(key) as T;
|
lock (key)
|
||||||
return item;
|
return Cache.Get(key) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -86,7 +86,11 @@ namespace PlexRequests.Helpers
|
||||||
public void Set(string key, object data, int cacheTime = 20)
|
public void Set(string key, object data, int cacheTime = 20)
|
||||||
{
|
{
|
||||||
var policy = new CacheItemPolicy { AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(cacheTime) };
|
var policy = new CacheItemPolicy { AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(cacheTime) };
|
||||||
Cache.Add(new CacheItem(key, data), policy);
|
lock (key)
|
||||||
|
{
|
||||||
|
Cache.Remove(key);
|
||||||
|
Cache.Add(new CacheItem(key, data), policy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -98,7 +102,10 @@ namespace PlexRequests.Helpers
|
||||||
var keys = Cache.Where(x => x.Key.Contains(key));
|
var keys = Cache.Where(x => x.Key.Contains(key));
|
||||||
foreach (var k in keys)
|
foreach (var k in keys)
|
||||||
{
|
{
|
||||||
Cache.Remove(k.Key);
|
lock (key)
|
||||||
|
{
|
||||||
|
Cache.Remove(k.Key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>PlexRequests.Helpers</RootNamespace>
|
<RootNamespace>PlexRequests.Helpers</RootNamespace>
|
||||||
<AssemblyName>PlexRequests.Helpers</AssemblyName>
|
<AssemblyName>PlexRequests.Helpers</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -31,14 +31,6 @@
|
||||||
<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="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Runtime.Caching" />
|
<Reference Include="System.Runtime.Caching" />
|
||||||
|
@ -48,11 +40,18 @@
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
|
||||||
|
<HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c">
|
||||||
|
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="AssemblyHelper.cs" />
|
<Compile Include="AssemblyHelper.cs" />
|
||||||
<Compile Include="ByteConverterHelper.cs" />
|
<Compile Include="ByteConverterHelper.cs" />
|
||||||
<Compile Include="DateTimeHelper.cs" />
|
<Compile Include="DateTimeHelper.cs" />
|
||||||
|
<Compile Include="Exceptions\ApiRequestException.cs" />
|
||||||
<Compile Include="Exceptions\ApplicationSettingsException.cs" />
|
<Compile Include="Exceptions\ApplicationSettingsException.cs" />
|
||||||
<Compile Include="HtmlRemover.cs" />
|
<Compile Include="HtmlRemover.cs" />
|
||||||
<Compile Include="ICacheProvider.cs" />
|
<Compile Include="ICacheProvider.cs" />
|
||||||
|
@ -65,6 +64,7 @@
|
||||||
<Compile Include="SerializerSettings.cs" />
|
<Compile Include="SerializerSettings.cs" />
|
||||||
<Compile Include="StringCipher.cs" />
|
<Compile Include="StringCipher.cs" />
|
||||||
<Compile Include="UriHelper.cs" />
|
<Compile Include="UriHelper.cs" />
|
||||||
|
<Compile Include="UserClaims.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
|
|
12
PlexRequests.Helpers/UserClaims.cs
Normal file
12
PlexRequests.Helpers/UserClaims.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PlexRequests.Helpers
|
||||||
|
{
|
||||||
|
public class UserClaims
|
||||||
|
{
|
||||||
|
public const string Admin = "Admin"; // Can do everything including creating new users and editing settings
|
||||||
|
public const string PowerUser = "PowerUser"; // Can only manage the requests, approve etc.
|
||||||
|
public const string User = "User"; // Can only request
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net452" />
|
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net45" />
|
||||||
<package id="NLog" version="4.2.3" targetFramework="net46" />
|
<package id="NLog" version="4.2.3" targetFramework="net45" />
|
||||||
</packages>
|
</packages>
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
|
|
@ -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.5.2"/></startup></configuration>
|
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /></startup></configuration>
|
||||||
|
|
|
@ -1,93 +0,0 @@
|
||||||
#region Copyright
|
|
||||||
// /************************************************************************
|
|
||||||
// Copyright (c) 2016 Jamie Rees
|
|
||||||
// File: AvailabilityUpdateService.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.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reactive.Linq;
|
|
||||||
using System.Web.Hosting;
|
|
||||||
|
|
||||||
using FluentScheduler;
|
|
||||||
|
|
||||||
using Mono.Data.Sqlite;
|
|
||||||
|
|
||||||
using NLog;
|
|
||||||
|
|
||||||
using PlexRequests.Api;
|
|
||||||
using PlexRequests.Core;
|
|
||||||
using PlexRequests.Core.SettingModels;
|
|
||||||
using PlexRequests.Helpers;
|
|
||||||
using PlexRequests.Services.Interfaces;
|
|
||||||
using PlexRequests.Store;
|
|
||||||
using PlexRequests.Store.Repository;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace PlexRequests.Services
|
|
||||||
{
|
|
||||||
public class AvailabilityUpdateService : ITask, IRegisteredObject, IAvailabilityUpdateService
|
|
||||||
{
|
|
||||||
|
|
||||||
public AvailabilityUpdateService()
|
|
||||||
{
|
|
||||||
|
|
||||||
var memCache = new MemoryCacheProvider();
|
|
||||||
var dbConfig = new DbConfiguration(new SqliteFactory());
|
|
||||||
var repo = new SettingsJsonRepository(dbConfig, memCache);
|
|
||||||
|
|
||||||
ConfigurationReader = new ConfigurationReader();
|
|
||||||
Checker = new PlexAvailabilityChecker(new SettingsServiceV2<PlexSettings>(repo), new SettingsServiceV2<AuthenticationSettings>(repo), new JsonRequestService(new RequestJsonRepository(dbConfig, memCache)), new PlexApi(), memCache);
|
|
||||||
HostingEnvironment.RegisterObject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
|
||||||
|
|
||||||
private IConfigurationReader ConfigurationReader { get; }
|
|
||||||
private IAvailabilityChecker Checker { get; }
|
|
||||||
private IDisposable UpdateSubscription { get; set; }
|
|
||||||
|
|
||||||
public void Start(Configuration c)
|
|
||||||
{
|
|
||||||
UpdateSubscription?.Dispose();
|
|
||||||
Task.Factory.StartNew(() => Checker.CheckAndUpdateAll(-1)); // cache the libraries and run the availability checks
|
|
||||||
UpdateSubscription = Observable.Interval(c.Intervals.Notification).Subscribe(Checker.CheckAndUpdateAll);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Execute()
|
|
||||||
{
|
|
||||||
Start(ConfigurationReader.Read());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Stop(bool immediate)
|
|
||||||
{
|
|
||||||
HostingEnvironment.UnregisterObject(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IAvailabilityUpdateService
|
|
||||||
{
|
|
||||||
void Start(Configuration c);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -31,7 +31,7 @@ namespace PlexRequests.Services.Interfaces
|
||||||
{
|
{
|
||||||
public interface IAvailabilityChecker
|
public interface IAvailabilityChecker
|
||||||
{
|
{
|
||||||
void CheckAndUpdateAll(long check);
|
void CheckAndUpdateAll();
|
||||||
List<PlexMovie> GetPlexMovies();
|
List<PlexMovie> GetPlexMovies();
|
||||||
bool IsMovieAvailable(PlexMovie[] plexMovies, string title, string year);
|
bool IsMovieAvailable(PlexMovie[] plexMovies, string title, string year);
|
||||||
List<PlexTvShow> GetPlexTvShows();
|
List<PlexTvShow> GetPlexTvShows();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
{
|
{
|
||||||
public interface ICouchPotatoCacher
|
public interface ICouchPotatoCacher
|
||||||
{
|
{
|
||||||
void Queued(long check);
|
void Queued();
|
||||||
int[] QueuedIds();
|
int[] QueuedIds();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +1,33 @@
|
||||||
#region Copyright
|
#region Copyright
|
||||||
// /************************************************************************
|
// /************************************************************************
|
||||||
// Copyright (c) 2016 Jamie Rees
|
// Copyright (c) 2016 Jamie Rees
|
||||||
// File: IConfigurationReader.cs
|
// File: IJobRecord.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
|
||||||
// a copy of this software and associated documentation files (the
|
// a copy of this software and associated documentation files (the
|
||||||
// "Software"), to deal in the Software without restriction, including
|
// "Software"), to deal in the Software without restriction, including
|
||||||
// without limitation the rights to use, copy, modify, merge, publish,
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
// permit persons to whom the Software is furnished to do so, subject to
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
// the following conditions:
|
// the following conditions:
|
||||||
//
|
//
|
||||||
// The above copyright notice and this permission notice shall be
|
// The above copyright notice and this permission notice shall be
|
||||||
// included in all copies or substantial portions of the Software.
|
// included in all copies or substantial portions of the Software.
|
||||||
//
|
//
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
// ************************************************************************/
|
// ************************************************************************/
|
||||||
#endregion
|
#endregion
|
||||||
namespace PlexRequests.Services.Interfaces
|
namespace PlexRequests.Services.Interfaces
|
||||||
{
|
{
|
||||||
public interface IConfigurationReader
|
public interface IJobRecord
|
||||||
{
|
{
|
||||||
Configuration Read();
|
void Record(string jobName);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -36,7 +36,12 @@ namespace PlexRequests.Services.Interfaces
|
||||||
string NotificationName { get; }
|
string NotificationName { get; }
|
||||||
|
|
||||||
Task NotifyAsync(NotificationModel model);
|
Task NotifyAsync(NotificationModel model);
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a notification to the user, this is usually for testing the settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model.</param>
|
||||||
|
/// <param name="settings">The settings.</param>
|
||||||
|
/// <returns></returns>
|
||||||
Task NotifyAsync(NotificationModel model, Settings settings);
|
Task NotifyAsync(NotificationModel model, Settings settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -33,7 +33,18 @@ namespace PlexRequests.Services.Interfaces
|
||||||
{
|
{
|
||||||
public interface INotificationService
|
public interface INotificationService
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a notification to the user. This one is used in normal notification scenarios
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model.</param>
|
||||||
|
/// <returns></returns>
|
||||||
Task Publish(NotificationModel model);
|
Task Publish(NotificationModel model);
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a notification to the user, this is usually for testing the settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model.</param>
|
||||||
|
/// <param name="settings">The settings.</param>
|
||||||
|
/// <returns></returns>
|
||||||
Task Publish(NotificationModel model, Settings settings);
|
Task Publish(NotificationModel model, Settings settings);
|
||||||
void Subscribe(INotification notification);
|
void Subscribe(INotification notification);
|
||||||
void UnSubscribe(INotification notification);
|
void UnSubscribe(INotification notification);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
namespace PlexRequests.Services.Interfaces
|
namespace PlexRequests.Services.Interfaces
|
||||||
{
|
{
|
||||||
public interface ISickRageCacher
|
public interface ISickRageCacher
|
||||||
{
|
{
|
||||||
void Queued(long check);
|
void Queued();
|
||||||
int[] QueuedIds();
|
int[] QueuedIds();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
{
|
{
|
||||||
public interface ISonarrCacher
|
public interface ISonarrCacher
|
||||||
{
|
{
|
||||||
void Queued(long check);
|
void Queued();
|
||||||
int[] QueuedIds();
|
int[] QueuedIds();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,77 +1,98 @@
|
||||||
#region Copyright
|
#region Copyright
|
||||||
// /************************************************************************
|
// /************************************************************************
|
||||||
// Copyright (c) 2016 Jamie Rees
|
// Copyright (c) 2016 Jamie Rees
|
||||||
// File: PlexAvailabilityChecker.cs
|
// File: PlexAvailabilityChecker.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
|
||||||
// a copy of this software and associated documentation files (the
|
// a copy of this software and associated documentation files (the
|
||||||
// "Software"), to deal in the Software without restriction, including
|
// "Software"), to deal in the Software without restriction, including
|
||||||
// without limitation the rights to use, copy, modify, merge, publish,
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
// permit persons to whom the Software is furnished to do so, subject to
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
// the following conditions:
|
// the following conditions:
|
||||||
//
|
//
|
||||||
// The above copyright notice and this permission notice shall be
|
// The above copyright notice and this permission notice shall be
|
||||||
// included in all copies or substantial portions of the Software.
|
// included in all copies or substantial portions of the Software.
|
||||||
//
|
//
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
// ************************************************************************/
|
// ************************************************************************/
|
||||||
#endregion
|
#endregion
|
||||||
|
using System.Linq;
|
||||||
using System;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
using PlexRequests.Api.Interfaces;
|
using PlexRequests.Api.Interfaces;
|
||||||
using PlexRequests.Core;
|
using PlexRequests.Api.Models.Movie;
|
||||||
using PlexRequests.Core.SettingModels;
|
using PlexRequests.Core;
|
||||||
using PlexRequests.Helpers;
|
using PlexRequests.Core.SettingModels;
|
||||||
using PlexRequests.Services.Interfaces;
|
using PlexRequests.Helpers;
|
||||||
using PlexRequests.Api.Models.Movie;
|
using PlexRequests.Services.Interfaces;
|
||||||
using System.Linq;
|
|
||||||
|
using Quartz;
|
||||||
namespace PlexRequests.Services
|
|
||||||
{
|
namespace PlexRequests.Services.Jobs
|
||||||
public class CouchPotatoCacher : ICouchPotatoCacher
|
{
|
||||||
{
|
public class CouchPotatoCacher : IJob, ICouchPotatoCacher
|
||||||
public CouchPotatoCacher(ISettingsService<CouchPotatoSettings> cpSettings, ICouchPotatoApi cpApi, ICacheProvider cache)
|
{
|
||||||
{
|
public CouchPotatoCacher(ISettingsService<CouchPotatoSettings> cpSettings, ICouchPotatoApi cpApi, ICacheProvider cache, IJobRecord rec)
|
||||||
CpSettings = cpSettings;
|
{
|
||||||
CpApi = cpApi;
|
CpSettings = cpSettings;
|
||||||
Cache = cache;
|
CpApi = cpApi;
|
||||||
}
|
Cache = cache;
|
||||||
|
Job = rec;
|
||||||
private ISettingsService<CouchPotatoSettings> CpSettings { get; }
|
}
|
||||||
private ICacheProvider Cache { get; }
|
|
||||||
private ICouchPotatoApi CpApi { get; }
|
private ISettingsService<CouchPotatoSettings> CpSettings { get; }
|
||||||
|
private ICacheProvider Cache { get; }
|
||||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
private ICouchPotatoApi CpApi { get; }
|
||||||
|
private IJobRecord Job { get; }
|
||||||
public void Queued(long check)
|
|
||||||
{
|
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||||
Log.Trace("This is check no. {0}", check);
|
|
||||||
Log.Trace("Getting the settings");
|
public void Queued()
|
||||||
|
{
|
||||||
var settings = CpSettings.GetSettings();
|
Log.Trace("Getting the settings");
|
||||||
if (settings.Enabled)
|
|
||||||
{
|
var settings = CpSettings.GetSettings();
|
||||||
Log.Trace("Getting all movies from CouchPotato");
|
if (settings.Enabled)
|
||||||
var movies = CpApi.GetMovies(settings.FullUri, settings.ApiKey, new[] { "active" });
|
{
|
||||||
Cache.Set(CacheKeys.CouchPotatoQueued, movies, 10);
|
Log.Trace("Getting all movies from CouchPotato");
|
||||||
}
|
try
|
||||||
}
|
{
|
||||||
|
var movies = CpApi.GetMovies(settings.FullUri, settings.ApiKey, new[] { "active" });
|
||||||
// we do not want to set here...
|
if (movies != null)
|
||||||
public int[] QueuedIds()
|
{
|
||||||
{
|
Cache.Set(CacheKeys.CouchPotatoQueued, movies, CacheKeys.TimeFrameMinutes.SchedulerCaching);
|
||||||
var movies = Cache.Get<CouchPotatoMovies>(CacheKeys.CouchPotatoQueued);
|
}
|
||||||
return movies != null ? movies.movies.Select(x => x.info.tmdb_id).ToArray() : new int[] { };
|
}
|
||||||
}
|
catch (System.Exception ex)
|
||||||
}
|
{
|
||||||
|
Log.Error(ex, "Failed caching queued items from CouchPotato");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Job.Record(JobNames.CpCacher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we do not want to set here...
|
||||||
|
public int[] QueuedIds()
|
||||||
|
{
|
||||||
|
var movies = Cache.Get<CouchPotatoMovies>(CacheKeys.CouchPotatoQueued);
|
||||||
|
return movies?.movies.Select(x => x.info.tmdb_id).ToArray() ?? new int[] { };
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute(IJobExecutionContext context)
|
||||||
|
{
|
||||||
|
Queued();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,40 +1,37 @@
|
||||||
#region Copyright
|
#region Copyright
|
||||||
// /************************************************************************
|
// /************************************************************************
|
||||||
// Copyright (c) 2016 Jamie Rees
|
// Copyright (c) 2016 Jamie Rees
|
||||||
// File: Congifuration.cs
|
// File: JobNames.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
|
||||||
// a copy of this software and associated documentation files (the
|
// a copy of this software and associated documentation files (the
|
||||||
// "Software"), to deal in the Software without restriction, including
|
// "Software"), to deal in the Software without restriction, including
|
||||||
// without limitation the rights to use, copy, modify, merge, publish,
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
// permit persons to whom the Software is furnished to do so, subject to
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
// the following conditions:
|
// the following conditions:
|
||||||
//
|
//
|
||||||
// The above copyright notice and this permission notice shall be
|
// The above copyright notice and this permission notice shall be
|
||||||
// included in all copies or substantial portions of the Software.
|
// included in all copies or substantial portions of the Software.
|
||||||
//
|
//
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
// ************************************************************************/
|
// ************************************************************************/
|
||||||
#endregion
|
#endregion
|
||||||
using PlexRequests.Services.Interfaces;
|
namespace PlexRequests.Services
|
||||||
|
{
|
||||||
namespace PlexRequests.Services
|
public static class JobNames
|
||||||
{
|
{
|
||||||
public class Configuration
|
public const string StoreBackup = "Database Backup";
|
||||||
{
|
public const string CpCacher = "CouchPotato Cacher";
|
||||||
public Configuration(IIntervals intervals)
|
public const string SonarrCacher = "Sonarr Cacher";
|
||||||
{
|
public const string SrCacher = "SickRage Cacher";
|
||||||
Intervals = intervals;
|
public const string PlexChecker = "Plex Availability Cacher";
|
||||||
}
|
}
|
||||||
|
|
||||||
public IIntervals Intervals { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
59
PlexRequests.Services/Jobs/JobRecord.cs
Normal file
59
PlexRequests.Services/Jobs/JobRecord.cs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2016 Jamie Rees
|
||||||
|
// File: JobRecord.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 PlexRequests.Services.Interfaces;
|
||||||
|
using PlexRequests.Store.Models;
|
||||||
|
using PlexRequests.Store.Repository;
|
||||||
|
|
||||||
|
namespace PlexRequests.Services
|
||||||
|
{
|
||||||
|
public class JobRecord : IJobRecord
|
||||||
|
{
|
||||||
|
public JobRecord(IRepository<ScheduledJobs> repo)
|
||||||
|
{
|
||||||
|
Repo = repo;
|
||||||
|
}
|
||||||
|
private IRepository<ScheduledJobs> Repo { get; }
|
||||||
|
public void Record(string jobName)
|
||||||
|
{
|
||||||
|
var allJobs = Repo.GetAll();
|
||||||
|
var storeJob = allJobs.FirstOrDefault(x => x.Name == jobName);
|
||||||
|
if (storeJob != null)
|
||||||
|
{
|
||||||
|
storeJob.LastRun = DateTime.UtcNow;
|
||||||
|
Repo.Update(storeJob);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var job = new ScheduledJobs { LastRun = DateTime.UtcNow, Name = jobName };
|
||||||
|
Repo.Insert(job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,297 +1,370 @@
|
||||||
#region Copyright
|
#region Copyright
|
||||||
// /************************************************************************
|
// /************************************************************************
|
||||||
// Copyright (c) 2016 Jamie Rees
|
// Copyright (c) 2016 Jamie Rees
|
||||||
// File: PlexAvailabilityChecker.cs
|
// File: PlexAvailabilityChecker.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
|
||||||
// a copy of this software and associated documentation files (the
|
// a copy of this software and associated documentation files (the
|
||||||
// "Software"), to deal in the Software without restriction, including
|
// "Software"), to deal in the Software without restriction, including
|
||||||
// without limitation the rights to use, copy, modify, merge, publish,
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
// permit persons to whom the Software is furnished to do so, subject to
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
// the following conditions:
|
// the following conditions:
|
||||||
//
|
//
|
||||||
// The above copyright notice and this permission notice shall be
|
// The above copyright notice and this permission notice shall be
|
||||||
// included in all copies or substantial portions of the Software.
|
// included in all copies or substantial portions of the Software.
|
||||||
//
|
//
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
// ************************************************************************/
|
// ************************************************************************/
|
||||||
#endregion
|
#endregion
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
using PlexRequests.Api.Interfaces;
|
using PlexRequests.Api.Interfaces;
|
||||||
using PlexRequests.Api.Models.Plex;
|
using PlexRequests.Api.Models.Plex;
|
||||||
using PlexRequests.Core;
|
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.Store;
|
using PlexRequests.Services.Models;
|
||||||
using PlexRequests.Services.Models;
|
using PlexRequests.Services.Notification;
|
||||||
|
using PlexRequests.Store;
|
||||||
namespace PlexRequests.Services
|
using PlexRequests.Store.Models;
|
||||||
{
|
using PlexRequests.Store.Repository;
|
||||||
public class PlexAvailabilityChecker : IAvailabilityChecker
|
|
||||||
{
|
using Quartz;
|
||||||
public PlexAvailabilityChecker(ISettingsService<PlexSettings> plexSettings, ISettingsService<AuthenticationSettings> auth, IRequestService request, IPlexApi plex, ICacheProvider cache)
|
|
||||||
{
|
namespace PlexRequests.Services.Jobs
|
||||||
Plex = plexSettings;
|
{
|
||||||
Auth = auth;
|
public class PlexAvailabilityChecker : IJob, IAvailabilityChecker
|
||||||
RequestService = request;
|
{
|
||||||
PlexApi = plex;
|
public PlexAvailabilityChecker(ISettingsService<PlexSettings> plexSettings, ISettingsService<AuthenticationSettings> auth, IRequestService request, IPlexApi plex, ICacheProvider cache,
|
||||||
Cache = cache;
|
INotificationService notify, IJobRecord rec, IRepository<UsersToNotify> users)
|
||||||
}
|
{
|
||||||
|
Plex = plexSettings;
|
||||||
private ISettingsService<PlexSettings> Plex { get; }
|
Auth = auth;
|
||||||
private ISettingsService<AuthenticationSettings> Auth { get; }
|
RequestService = request;
|
||||||
private IRequestService RequestService { get; }
|
PlexApi = plex;
|
||||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
Cache = cache;
|
||||||
private IPlexApi PlexApi { get; }
|
Notification = notify;
|
||||||
private ICacheProvider Cache { get; }
|
Job = rec;
|
||||||
|
UserNotifyRepo = users;
|
||||||
public void CheckAndUpdateAll(long check)
|
}
|
||||||
{
|
|
||||||
Log.Trace("This is check no. {0}", check);
|
private ISettingsService<PlexSettings> Plex { get; }
|
||||||
Log.Trace("Getting the settings");
|
private ISettingsService<AuthenticationSettings> Auth { get; }
|
||||||
var plexSettings = Plex.GetSettings();
|
private IRequestService RequestService { get; }
|
||||||
var authSettings = Auth.GetSettings();
|
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||||
Log.Trace("Getting all the requests");
|
private IPlexApi PlexApi { get; }
|
||||||
|
private ICacheProvider Cache { get; }
|
||||||
if (!ValidateSettings(plexSettings, authSettings))
|
private INotificationService Notification { get; }
|
||||||
{
|
private IJobRecord Job { get; }
|
||||||
Log.Info("Validation of the plex settings failed.");
|
private IRepository<UsersToNotify> UserNotifyRepo { get; }
|
||||||
return;
|
public void CheckAndUpdateAll()
|
||||||
}
|
{
|
||||||
|
Log.Trace("Getting the settings");
|
||||||
var libraries = CachedLibraries(authSettings, plexSettings, true); //force setting the cache (10 min intervals via scheduler)
|
var plexSettings = Plex.GetSettings();
|
||||||
var movies = GetPlexMovies().ToArray();
|
var authSettings = Auth.GetSettings();
|
||||||
var shows = GetPlexTvShows().ToArray();
|
Log.Trace("Getting all the requests");
|
||||||
var albums = GetPlexAlbums().ToArray();
|
|
||||||
|
if (!ValidateSettings(plexSettings, authSettings))
|
||||||
var requests = RequestService.GetAll();
|
{
|
||||||
var requestedModels = requests as RequestedModel[] ?? requests.Where(x => !x.Available).ToArray();
|
Log.Info("Validation of the plex settings failed.");
|
||||||
Log.Trace("Requests Count {0}", requestedModels.Length);
|
return;
|
||||||
|
}
|
||||||
if (!requestedModels.Any())
|
|
||||||
{
|
var libraries = CachedLibraries(authSettings, plexSettings, true); //force setting the cache (10 min intervals via scheduler)
|
||||||
Log.Info("There are no requests to check.");
|
|
||||||
return;
|
if (libraries == null || !libraries.Any())
|
||||||
}
|
{
|
||||||
|
Log.Info("Did not find any libraries in Plex.");
|
||||||
var modifiedModel = new List<RequestedModel>();
|
return;
|
||||||
foreach (var r in requestedModels)
|
}
|
||||||
{
|
|
||||||
Log.Trace("We are going to see if Plex has the following title: {0}", r.Title);
|
var movies = GetPlexMovies().ToArray();
|
||||||
|
var shows = GetPlexTvShows().ToArray();
|
||||||
if (libraries == null)
|
var albums = GetPlexAlbums().ToArray();
|
||||||
{
|
|
||||||
libraries = new List<PlexSearch>() { PlexApi.SearchContent(authSettings.PlexAuthToken, r.Title, plexSettings.FullUri) };
|
var requests = RequestService.GetAll();
|
||||||
if (libraries == null)
|
var requestedModels = requests as RequestedModel[] ?? requests.Where(x => !x.Available).ToArray();
|
||||||
{
|
Log.Trace("Requests Count {0}", requestedModels.Length);
|
||||||
Log.Trace("Could not find any matching result for this title.");
|
|
||||||
continue;
|
if (!requestedModels.Any())
|
||||||
}
|
{
|
||||||
}
|
Log.Info("There are no requests to check.");
|
||||||
|
return;
|
||||||
Log.Trace("Search results from Plex for the following request: {0}", r.Title);
|
}
|
||||||
//Log.Trace(results.DumpJson());
|
|
||||||
|
var modifiedModel = new List<RequestedModel>();
|
||||||
var releaseDate = r.ReleaseDate == DateTime.MinValue ? string.Empty : r.ReleaseDate.ToString("yyyy");
|
foreach (var r in requestedModels)
|
||||||
|
{
|
||||||
bool matchResult;
|
Log.Trace("We are going to see if Plex has the following title: {0}", r.Title);
|
||||||
switch (r.Type)
|
|
||||||
{
|
Log.Trace("Search results from Plex for the following request: {0}", r.Title);
|
||||||
case RequestType.Movie:
|
|
||||||
matchResult = IsMovieAvailable(movies, r.Title, releaseDate);
|
var releaseDate = r.ReleaseDate == DateTime.MinValue ? string.Empty : r.ReleaseDate.ToString("yyyy");
|
||||||
break;
|
|
||||||
case RequestType.TvShow:
|
bool matchResult;
|
||||||
matchResult = IsTvShowAvailable(shows, r.Title, releaseDate);
|
switch (r.Type)
|
||||||
break;
|
{
|
||||||
case RequestType.Album:
|
case RequestType.Movie:
|
||||||
matchResult = IsAlbumAvailable(albums, r.Title, r.ReleaseDate.Year.ToString(), r.ArtistName);
|
matchResult = IsMovieAvailable(movies, r.Title, releaseDate);
|
||||||
break;
|
break;
|
||||||
default:
|
case RequestType.TvShow:
|
||||||
throw new ArgumentOutOfRangeException();
|
matchResult = IsTvShowAvailable(shows, r.Title, releaseDate);
|
||||||
}
|
break;
|
||||||
|
case RequestType.Album:
|
||||||
if (matchResult)
|
matchResult = IsAlbumAvailable(albums, r.Title, r.ReleaseDate.Year.ToString(), r.ArtistName);
|
||||||
{
|
break;
|
||||||
r.Available = true;
|
default:
|
||||||
modifiedModel.Add(r);
|
throw new ArgumentOutOfRangeException();
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
|
if (matchResult)
|
||||||
Log.Trace("The result from Plex where the title's match was null, so that means the content is not yet in Plex.");
|
{
|
||||||
}
|
r.Available = true;
|
||||||
|
modifiedModel.Add(r);
|
||||||
Log.Trace("Updating the requests now");
|
continue;
|
||||||
Log.Trace("Requests that will be updates:");
|
}
|
||||||
Log.Trace(modifiedModel.SelectMany(x => x.Title).DumpJson());
|
|
||||||
|
Log.Trace("The result from Plex where the title's match was null, so that means the content is not yet in Plex.");
|
||||||
if (modifiedModel.Any())
|
}
|
||||||
{
|
|
||||||
RequestService.BatchUpdate(modifiedModel);
|
Log.Trace("Updating the requests now");
|
||||||
}
|
Log.Trace("Requests that will be updated count {0}", modifiedModel.Count);
|
||||||
}
|
|
||||||
|
if (modifiedModel.Any())
|
||||||
public List<PlexMovie> GetPlexMovies()
|
{
|
||||||
{
|
NotifyUsers(modifiedModel, authSettings.PlexAuthToken);
|
||||||
var movies = new List<PlexMovie>();
|
RequestService.BatchUpdate(modifiedModel);
|
||||||
var libs = Cache.Get<List<PlexSearch>>(CacheKeys.PlexLibaries);
|
}
|
||||||
if (libs != null)
|
|
||||||
{
|
Job.Record(JobNames.PlexChecker);
|
||||||
var movieLibs = libs.Where(x =>
|
|
||||||
x.Video.Any(y =>
|
}
|
||||||
y.Type.Equals(PlexMediaType.Movie.ToString(), StringComparison.CurrentCultureIgnoreCase)
|
|
||||||
)
|
public List<PlexMovie> GetPlexMovies()
|
||||||
).ToArray();
|
{
|
||||||
|
var movies = new List<PlexMovie>();
|
||||||
foreach (var lib in movieLibs)
|
var libs = Cache.Get<List<PlexSearch>>(CacheKeys.PlexLibaries);
|
||||||
{
|
if (libs != null)
|
||||||
movies.AddRange(lib.Video.Select(x => new PlexMovie() // movies are in the Video list
|
{
|
||||||
{
|
var movieLibs = libs.Where(x =>
|
||||||
Title = x.Title,
|
x.Video.Any(y =>
|
||||||
ReleaseYear = x.Year
|
y.Type.Equals(PlexMediaType.Movie.ToString(), StringComparison.CurrentCultureIgnoreCase)
|
||||||
}));
|
)
|
||||||
}
|
).ToArray();
|
||||||
}
|
|
||||||
return movies;
|
foreach (var lib in movieLibs)
|
||||||
}
|
{
|
||||||
|
movies.AddRange(lib.Video.Select(x => new PlexMovie() // movies are in the Video list
|
||||||
public bool IsMovieAvailable(PlexMovie[] plexMovies, string title, string year)
|
{
|
||||||
{
|
Title = x.Title,
|
||||||
return plexMovies.Any(x => x.Title.Equals(title, StringComparison.CurrentCultureIgnoreCase) && x.ReleaseYear.Equals(year, StringComparison.CurrentCultureIgnoreCase));
|
ReleaseYear = x.Year
|
||||||
}
|
}));
|
||||||
|
}
|
||||||
public List<PlexTvShow> GetPlexTvShows()
|
}
|
||||||
{
|
return movies;
|
||||||
var shows = new List<PlexTvShow>();
|
}
|
||||||
var libs = Cache.Get<List<PlexSearch>>(CacheKeys.PlexLibaries);
|
|
||||||
if (libs != null)
|
public bool IsMovieAvailable(PlexMovie[] plexMovies, string title, string year)
|
||||||
{
|
{
|
||||||
var tvLibs = libs.Where(x =>
|
return plexMovies.Any(x => x.Title.Equals(title, StringComparison.CurrentCultureIgnoreCase) && x.ReleaseYear.Equals(year, StringComparison.CurrentCultureIgnoreCase));
|
||||||
x.Directory.Any(y =>
|
}
|
||||||
y.Type.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)
|
|
||||||
)
|
public List<PlexTvShow> GetPlexTvShows()
|
||||||
).ToArray();
|
{
|
||||||
|
var shows = new List<PlexTvShow>();
|
||||||
foreach (var lib in tvLibs)
|
var libs = Cache.Get<List<PlexSearch>>(CacheKeys.PlexLibaries);
|
||||||
{
|
if (libs != null)
|
||||||
shows.AddRange(lib.Directory.Select(x => new PlexTvShow() // shows are in the directory list
|
{
|
||||||
{
|
var tvLibs = libs.Where(x =>
|
||||||
Title = x.Title,
|
x.Directory.Any(y =>
|
||||||
ReleaseYear = x.Year
|
y.Type.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)
|
||||||
}));
|
)
|
||||||
}
|
).ToArray();
|
||||||
}
|
|
||||||
return shows;
|
foreach (var lib in tvLibs)
|
||||||
}
|
{
|
||||||
|
shows.AddRange(lib.Directory.Select(x => new PlexTvShow() // shows are in the directory list
|
||||||
public bool IsTvShowAvailable(PlexTvShow[] plexShows, string title, string year)
|
{
|
||||||
{
|
Title = x.Title,
|
||||||
return plexShows.Any(x =>
|
ReleaseYear = x.Year
|
||||||
(x.Title.Equals(title, StringComparison.CurrentCultureIgnoreCase) || x.Title.StartsWith(title, StringComparison.CurrentCultureIgnoreCase)) &&
|
}));
|
||||||
x.ReleaseYear.Equals(year, StringComparison.CurrentCultureIgnoreCase));
|
}
|
||||||
}
|
}
|
||||||
|
return shows;
|
||||||
public List<PlexAlbum> GetPlexAlbums()
|
}
|
||||||
{
|
|
||||||
var albums = new List<PlexAlbum>();
|
public bool IsTvShowAvailable(PlexTvShow[] plexShows, string title, string year)
|
||||||
var libs = Cache.Get<List<PlexSearch>>(CacheKeys.PlexLibaries);
|
{
|
||||||
if (libs != null)
|
return plexShows.Any(x =>
|
||||||
{
|
(x.Title.Equals(title, StringComparison.CurrentCultureIgnoreCase) || x.Title.StartsWith(title, StringComparison.CurrentCultureIgnoreCase)) &&
|
||||||
var albumLibs = libs.Where(x =>
|
x.ReleaseYear.Equals(year, StringComparison.CurrentCultureIgnoreCase));
|
||||||
x.Directory.Any(y =>
|
}
|
||||||
y.Type.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)
|
|
||||||
)
|
public List<PlexAlbum> GetPlexAlbums()
|
||||||
).ToArray();
|
{
|
||||||
|
var albums = new List<PlexAlbum>();
|
||||||
foreach (var lib in albumLibs)
|
var libs = Cache.Get<List<PlexSearch>>(CacheKeys.PlexLibaries);
|
||||||
{
|
if (libs != null)
|
||||||
albums.AddRange(lib.Directory.Select(x => new PlexAlbum()
|
{
|
||||||
{
|
var albumLibs = libs.Where(x =>
|
||||||
Title = x.Title,
|
x.Directory.Any(y =>
|
||||||
ReleaseYear = x.Year,
|
y.Type.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)
|
||||||
Artist = x.ParentTitle
|
)
|
||||||
}));
|
).ToArray();
|
||||||
}
|
|
||||||
}
|
foreach (var lib in albumLibs)
|
||||||
return albums;
|
{
|
||||||
}
|
albums.AddRange(lib.Directory.Select(x => new PlexAlbum()
|
||||||
|
{
|
||||||
public bool IsAlbumAvailable(PlexAlbum[] plexAlbums, string title, string year, string artist)
|
Title = x.Title,
|
||||||
{
|
ReleaseYear = x.Year,
|
||||||
return plexAlbums.Any(x =>
|
Artist = x.ParentTitle
|
||||||
x.Title.Contains(title) &&
|
}));
|
||||||
//x.ReleaseYear.Equals(year, StringComparison.CurrentCultureIgnoreCase) &&
|
}
|
||||||
x.Artist.Equals(artist, StringComparison.CurrentCultureIgnoreCase));
|
}
|
||||||
}
|
return albums;
|
||||||
|
}
|
||||||
private List<PlexSearch> CachedLibraries(AuthenticationSettings authSettings, PlexSettings plexSettings, bool setCache)
|
|
||||||
{
|
public bool IsAlbumAvailable(PlexAlbum[] plexAlbums, string title, string year, string artist)
|
||||||
Log.Trace("Obtaining library sections from Plex for the following request");
|
{
|
||||||
|
return plexAlbums.Any(x =>
|
||||||
List<PlexSearch> results = new List<PlexSearch>();
|
x.Title.Contains(title) &&
|
||||||
|
//x.ReleaseYear.Equals(year, StringComparison.CurrentCultureIgnoreCase) &&
|
||||||
if (!ValidateSettings(plexSettings, authSettings))
|
x.Artist.Equals(artist, StringComparison.CurrentCultureIgnoreCase));
|
||||||
{
|
}
|
||||||
Log.Warn("The settings are not configured");
|
|
||||||
return results; // don't error out here, just let it go!
|
private List<PlexSearch> CachedLibraries(AuthenticationSettings authSettings, PlexSettings plexSettings, bool setCache)
|
||||||
}
|
{
|
||||||
|
Log.Trace("Obtaining library sections from Plex");
|
||||||
if (setCache)
|
|
||||||
{
|
List<PlexSearch> results = new List<PlexSearch>();
|
||||||
results = GetLibraries(authSettings, plexSettings);
|
|
||||||
Cache.Set(CacheKeys.PlexLibaries, results, 10);
|
if (!ValidateSettings(plexSettings, authSettings))
|
||||||
}
|
{
|
||||||
else
|
Log.Warn("The settings are not configured");
|
||||||
{
|
return results; // don't error out here, just let it go!
|
||||||
results = Cache.GetOrSet(CacheKeys.PlexLibaries, () => {
|
}
|
||||||
return GetLibraries(authSettings, plexSettings);
|
|
||||||
}, 10);
|
try
|
||||||
}
|
{
|
||||||
return results;
|
if (setCache)
|
||||||
}
|
{
|
||||||
|
Log.Trace("Plex Lib API Call");
|
||||||
private List<PlexSearch> GetLibraries(AuthenticationSettings authSettings, PlexSettings plexSettings)
|
results = GetLibraries(authSettings, plexSettings);
|
||||||
{
|
|
||||||
var sections = PlexApi.GetLibrarySections(authSettings.PlexAuthToken, plexSettings.FullUri);
|
Log.Trace("Plex Lib Cache Set Call");
|
||||||
|
if (results != null)
|
||||||
List<PlexSearch> libs = new List<PlexSearch>();
|
{
|
||||||
if (sections != null)
|
Cache.Set(CacheKeys.PlexLibaries, results, CacheKeys.TimeFrameMinutes.SchedulerCaching);
|
||||||
{
|
}
|
||||||
foreach (var dir in sections.Directories)
|
}
|
||||||
{
|
else
|
||||||
Log.Trace("Obtaining results from Plex for the following library section: {0}", dir.Title);
|
{
|
||||||
var lib = PlexApi.GetLibrary(authSettings.PlexAuthToken, plexSettings.FullUri, dir.Key);
|
Log.Trace("Plex Lib GetSet Call");
|
||||||
if (lib != null)
|
results = Cache.GetOrSet(CacheKeys.PlexLibaries, () =>
|
||||||
{
|
{
|
||||||
libs.Add(lib);
|
Log.Trace("Plex Lib API Call (inside getset)");
|
||||||
}
|
return GetLibraries(authSettings, plexSettings);
|
||||||
}
|
}, CacheKeys.TimeFrameMinutes.SchedulerCaching);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return libs;
|
catch (Exception ex)
|
||||||
}
|
{
|
||||||
|
Log.Error(ex, "Failed to obtain Plex libraries");
|
||||||
private bool ValidateSettings(PlexSettings plex, AuthenticationSettings auth)
|
}
|
||||||
{
|
|
||||||
if (plex?.Ip == null || auth?.PlexAuthToken == null)
|
return results;
|
||||||
{
|
}
|
||||||
Log.Warn("A setting is null, Ensure Plex is configured correctly, and we have a Plex Auth token.");
|
|
||||||
return false;
|
private List<PlexSearch> GetLibraries(AuthenticationSettings authSettings, PlexSettings plexSettings)
|
||||||
}
|
{
|
||||||
return true;
|
var sections = PlexApi.GetLibrarySections(authSettings.PlexAuthToken, plexSettings.FullUri);
|
||||||
}
|
|
||||||
}
|
List<PlexSearch> libs = new List<PlexSearch>();
|
||||||
|
if (sections != null)
|
||||||
|
{
|
||||||
|
foreach (var dir in sections.Directories)
|
||||||
|
{
|
||||||
|
Log.Trace("Obtaining results from Plex for the following library section: {0}", dir.Title);
|
||||||
|
var lib = PlexApi.GetLibrary(authSettings.PlexAuthToken, plexSettings.FullUri, dir.Key);
|
||||||
|
if (lib != null)
|
||||||
|
{
|
||||||
|
libs.Add(lib);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Trace("Returning Plex Libs");
|
||||||
|
return libs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ValidateSettings(PlexSettings plex, AuthenticationSettings auth)
|
||||||
|
{
|
||||||
|
if (plex?.Ip == null || auth?.PlexAuthToken == null)
|
||||||
|
{
|
||||||
|
Log.Warn("A setting is null, Ensure Plex is configured correctly, and we have a Plex Auth token.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NotifyUsers(IEnumerable<RequestedModel> modelChanged, string apiKey)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var plexUser = PlexApi.GetUsers(apiKey);
|
||||||
|
if (plexUser?.User == null || plexUser.User.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var users = UserNotifyRepo.GetAll().ToList();
|
||||||
|
foreach (var model in modelChanged)
|
||||||
|
{
|
||||||
|
var selectedUsers = users.Select(x => x.Username).Intersect(model.RequestedUsers);
|
||||||
|
foreach (var user in selectedUsers)
|
||||||
|
{
|
||||||
|
var email = plexUser.User.FirstOrDefault(x => x.Username == user);
|
||||||
|
if (email == null)
|
||||||
|
{
|
||||||
|
// We do not have a plex user that requested this!
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var notificationModel = new NotificationModel
|
||||||
|
{
|
||||||
|
User = email.Username,
|
||||||
|
UserEmail = email.Email,
|
||||||
|
NotificationType = NotificationType.RequestAvailable,
|
||||||
|
Title = model.Title
|
||||||
|
};
|
||||||
|
|
||||||
|
// Send the notification to the user.
|
||||||
|
Notification.Publish(notificationModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute(IJobExecutionContext context)
|
||||||
|
{
|
||||||
|
CheckAndUpdateAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,78 +1,98 @@
|
||||||
#region Copyright
|
#region Copyright
|
||||||
// /************************************************************************
|
// /************************************************************************
|
||||||
// Copyright (c) 2016 Jamie Rees
|
// Copyright (c) 2016 Jamie Rees
|
||||||
// File: PlexAvailabilityChecker.cs
|
// File: PlexAvailabilityChecker.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
|
||||||
// a copy of this software and associated documentation files (the
|
// a copy of this software and associated documentation files (the
|
||||||
// "Software"), to deal in the Software without restriction, including
|
// "Software"), to deal in the Software without restriction, including
|
||||||
// without limitation the rights to use, copy, modify, merge, publish,
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
// permit persons to whom the Software is furnished to do so, subject to
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
// the following conditions:
|
// the following conditions:
|
||||||
//
|
//
|
||||||
// The above copyright notice and this permission notice shall be
|
// The above copyright notice and this permission notice shall be
|
||||||
// included in all copies or substantial portions of the Software.
|
// included in all copies or substantial portions of the Software.
|
||||||
//
|
//
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
// ************************************************************************/
|
// ************************************************************************/
|
||||||
#endregion
|
#endregion
|
||||||
|
using System.Linq;
|
||||||
using System;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
using PlexRequests.Api.Interfaces;
|
using PlexRequests.Api.Interfaces;
|
||||||
using PlexRequests.Core;
|
using PlexRequests.Api.Models.SickRage;
|
||||||
using PlexRequests.Core.SettingModels;
|
using PlexRequests.Core;
|
||||||
using PlexRequests.Helpers;
|
using PlexRequests.Core.SettingModels;
|
||||||
using PlexRequests.Services.Interfaces;
|
using PlexRequests.Helpers;
|
||||||
using PlexRequests.Api.Models.Movie;
|
using PlexRequests.Services.Interfaces;
|
||||||
using System.Linq;
|
|
||||||
using PlexRequests.Api.Models.SickRage;
|
using Quartz;
|
||||||
|
|
||||||
namespace PlexRequests.Services
|
namespace PlexRequests.Services.Jobs
|
||||||
{
|
{
|
||||||
public class SickRageCacher : ISickRageCacher
|
public class SickRageCacher : IJob, ISickRageCacher
|
||||||
{
|
{
|
||||||
public SickRageCacher(ISettingsService<SickRageSettings> srSettings, ISickRageApi srApi, ICacheProvider cache)
|
public SickRageCacher(ISettingsService<SickRageSettings> srSettings, ISickRageApi srApi, ICacheProvider cache, IJobRecord rec)
|
||||||
{
|
{
|
||||||
SrSettings = srSettings;
|
SrSettings = srSettings;
|
||||||
SrApi = srApi;
|
SrApi = srApi;
|
||||||
Cache = cache;
|
Cache = cache;
|
||||||
}
|
Job = rec;
|
||||||
|
}
|
||||||
private ISettingsService<SickRageSettings> SrSettings { get; }
|
|
||||||
private ICacheProvider Cache { get; }
|
private ISettingsService<SickRageSettings> SrSettings { get; }
|
||||||
private ISickRageApi SrApi { get; }
|
private ICacheProvider Cache { get; }
|
||||||
|
private ISickRageApi SrApi { get; }
|
||||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
|
||||||
|
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||||
public void Queued(long check)
|
private IJobRecord Job { get; }
|
||||||
{
|
|
||||||
Log.Trace("This is check no. {0}", check);
|
public void Queued()
|
||||||
Log.Trace("Getting the settings");
|
{
|
||||||
|
Log.Trace("Getting the settings");
|
||||||
var settings = SrSettings.GetSettings();
|
|
||||||
if (settings.Enabled)
|
var settings = SrSettings.GetSettings();
|
||||||
{
|
if (settings.Enabled)
|
||||||
Log.Trace("Getting all shows from SickRage");
|
{
|
||||||
var movies = SrApi.GetShows(settings.ApiKey, settings.FullUri);
|
Log.Trace("Getting all shows from SickRage");
|
||||||
Cache.Set(CacheKeys.SickRageQueued, movies.Result);
|
try
|
||||||
}
|
{
|
||||||
}
|
var shows = SrApi.GetShows(settings.ApiKey, settings.FullUri);
|
||||||
|
if (shows != null)
|
||||||
// we do not want to set here...
|
{
|
||||||
public int[] QueuedIds()
|
Cache.Set(CacheKeys.SickRageQueued, shows.Result, CacheKeys.TimeFrameMinutes.SchedulerCaching);
|
||||||
{
|
}
|
||||||
var tv = Cache.Get<SickrageShows>(CacheKeys.SickRageQueued);
|
}
|
||||||
return tv?.data.Values.Select(x => x.tvdbid).ToArray() ?? new int[] { };
|
catch (System.Exception ex)
|
||||||
}
|
{
|
||||||
}
|
Log.Error(ex, "Failed caching queued items from SickRage");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Job.Record(JobNames.SrCacher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we do not want to set here...
|
||||||
|
public int[] QueuedIds()
|
||||||
|
{
|
||||||
|
var tv = Cache.Get<SickrageShows>(CacheKeys.SickRageQueued);
|
||||||
|
return tv?.data.Values.Select(x => x.tvdbid).ToArray() ?? new int[] { };
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute(IJobExecutionContext context)
|
||||||
|
{
|
||||||
|
Queued();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,77 +1,101 @@
|
||||||
#region Copyright
|
#region Copyright
|
||||||
// /************************************************************************
|
// /************************************************************************
|
||||||
// Copyright (c) 2016 Jamie Rees
|
// Copyright (c) 2016 Jamie Rees
|
||||||
// File: PlexAvailabilityChecker.cs
|
// File: PlexAvailabilityChecker.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
|
||||||
// a copy of this software and associated documentation files (the
|
// a copy of this software and associated documentation files (the
|
||||||
// "Software"), to deal in the Software without restriction, including
|
// "Software"), to deal in the Software without restriction, including
|
||||||
// without limitation the rights to use, copy, modify, merge, publish,
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
// permit persons to whom the Software is furnished to do so, subject to
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
// the following conditions:
|
// the following conditions:
|
||||||
//
|
//
|
||||||
// The above copyright notice and this permission notice shall be
|
// The above copyright notice and this permission notice shall be
|
||||||
// included in all copies or substantial portions of the Software.
|
// included in all copies or substantial portions of the Software.
|
||||||
//
|
//
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
// ************************************************************************/
|
// ************************************************************************/
|
||||||
#endregion
|
#endregion
|
||||||
|
using System.Collections.Generic;
|
||||||
using NLog;
|
using System.Linq;
|
||||||
|
|
||||||
using PlexRequests.Api.Interfaces;
|
using NLog;
|
||||||
using PlexRequests.Core;
|
|
||||||
using PlexRequests.Core.SettingModels;
|
using PlexRequests.Api.Interfaces;
|
||||||
using PlexRequests.Helpers;
|
using PlexRequests.Api.Models.Sonarr;
|
||||||
using PlexRequests.Services.Interfaces;
|
using PlexRequests.Core;
|
||||||
using System.Linq;
|
using PlexRequests.Core.SettingModels;
|
||||||
using System.Collections.Generic;
|
using PlexRequests.Helpers;
|
||||||
using PlexRequests.Api.Models.Sonarr;
|
using PlexRequests.Services.Interfaces;
|
||||||
|
using PlexRequests.Store.Models;
|
||||||
namespace PlexRequests.Services
|
using PlexRequests.Store.Repository;
|
||||||
{
|
|
||||||
public class SonarrCacher : ISonarrCacher
|
using Quartz;
|
||||||
{
|
|
||||||
public SonarrCacher(ISettingsService<SonarrSettings> sonarrSettings, ISonarrApi sonarrApi, ICacheProvider cache)
|
namespace PlexRequests.Services.Jobs
|
||||||
{
|
{
|
||||||
SonarrSettings = sonarrSettings;
|
public class SonarrCacher : IJob, ISonarrCacher
|
||||||
SonarrApi = sonarrApi;
|
{
|
||||||
Cache = cache;
|
public SonarrCacher(ISettingsService<SonarrSettings> sonarrSettings, ISonarrApi sonarrApi, ICacheProvider cache, IJobRecord rec)
|
||||||
}
|
{
|
||||||
|
SonarrSettings = sonarrSettings;
|
||||||
private ISettingsService<SonarrSettings> SonarrSettings { get; }
|
SonarrApi = sonarrApi;
|
||||||
private ICacheProvider Cache { get; }
|
Job = rec;
|
||||||
private ISonarrApi SonarrApi { get; }
|
Cache = cache;
|
||||||
|
}
|
||||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
|
||||||
|
private ISettingsService<SonarrSettings> SonarrSettings { get; }
|
||||||
public void Queued(long check)
|
private ICacheProvider Cache { get; }
|
||||||
{
|
private ISonarrApi SonarrApi { get; }
|
||||||
Log.Trace("This is check no. {0}", check);
|
private IJobRecord Job { get; }
|
||||||
Log.Trace("Getting the settings");
|
|
||||||
|
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||||
var settings = SonarrSettings.GetSettings();
|
|
||||||
if (settings.Enabled)
|
public void Queued()
|
||||||
{
|
{
|
||||||
Log.Trace("Getting all tv series from Sonarr");
|
Log.Trace("Getting the settings");
|
||||||
var series = SonarrApi.GetSeries(settings.ApiKey, settings.FullUri);
|
|
||||||
Cache.Set(CacheKeys.SonarrQueued, series, 10);
|
var settings = SonarrSettings.GetSettings();
|
||||||
}
|
if (settings.Enabled)
|
||||||
}
|
{
|
||||||
|
Log.Trace("Getting all tv series from Sonarr");
|
||||||
// we do not want to set here...
|
try
|
||||||
public int[] QueuedIds()
|
{
|
||||||
{
|
var series = SonarrApi.GetSeries(settings.ApiKey, settings.FullUri);
|
||||||
var series = Cache.Get<List<Series>>(CacheKeys.SonarrQueued);
|
if (series != null)
|
||||||
return series?.Select(x => x.tvdbId).ToArray() ?? new int[] { };
|
{
|
||||||
}
|
Cache.Set(CacheKeys.SonarrQueued, series, CacheKeys.TimeFrameMinutes.SchedulerCaching);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "Failed caching queued items from Sonarr");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Job.Record(JobNames.SonarrCacher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we do not want to set here...
|
||||||
|
public int[] QueuedIds()
|
||||||
|
{
|
||||||
|
var series = Cache.Get<List<Series>>(CacheKeys.SonarrQueued);
|
||||||
|
return series?.Select(x => x.tvdbId).ToArray() ?? new int[] { };
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute(IJobExecutionContext context)
|
||||||
|
{
|
||||||
|
Queued();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
150
PlexRequests.Services/Jobs/StoreBackup.cs
Normal file
150
PlexRequests.Services/Jobs/StoreBackup.cs
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2016 Jamie Rees
|
||||||
|
// File: StoreBackup.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.Globalization;
|
||||||
|
|
||||||
|
using NLog;
|
||||||
|
|
||||||
|
using PlexRequests.Services.Interfaces;
|
||||||
|
using PlexRequests.Store;
|
||||||
|
using PlexRequests.Store.Models;
|
||||||
|
using PlexRequests.Store.Repository;
|
||||||
|
|
||||||
|
using Quartz;
|
||||||
|
|
||||||
|
|
||||||
|
namespace PlexRequests.Services.Jobs
|
||||||
|
{
|
||||||
|
public class StoreBackup : IJob
|
||||||
|
{
|
||||||
|
public StoreBackup(ISqliteConfiguration sql, IJobRecord rec)
|
||||||
|
{
|
||||||
|
Sql = sql;
|
||||||
|
JobRecord = rec;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ISqliteConfiguration Sql { get; }
|
||||||
|
private IJobRecord JobRecord { get; }
|
||||||
|
|
||||||
|
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
public void Execute(IJobExecutionContext context)
|
||||||
|
{
|
||||||
|
TakeBackup();
|
||||||
|
Cleanup ();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TakeBackup()
|
||||||
|
{
|
||||||
|
Log.Trace("Starting DB Backup");
|
||||||
|
var dbPath = Sql.CurrentPath;
|
||||||
|
var dir = Path.GetDirectoryName(dbPath);
|
||||||
|
if (dir == null)
|
||||||
|
{
|
||||||
|
Log.Warn("We couldn't find the DB path. We cannot backup.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var backupDir = Directory.CreateDirectory(Path.Combine(dir, "Backup"));
|
||||||
|
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(dbPath))
|
||||||
|
{
|
||||||
|
Log.Warn("Could not find the actual database. We cannot backup.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(DoWeNeedToBackup(backupDir.FullName))
|
||||||
|
{
|
||||||
|
File.Copy(dbPath, Path.Combine(backupDir.FullName, $"PlexRequests.sqlite_{DateTime.Now.ToString("yyyy-MM-dd hh.mm.ss")}.bak"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Warn(e);
|
||||||
|
Log.Warn("Exception when trying to copy the backup.");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
JobRecord.Record(JobNames.StoreBackup);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cleanup()
|
||||||
|
{
|
||||||
|
Log.Trace("Starting DB Cleanup");
|
||||||
|
var dbPath = Sql.CurrentPath;
|
||||||
|
var dir = Path.GetDirectoryName(dbPath);
|
||||||
|
if (dir == null)
|
||||||
|
{
|
||||||
|
Log.Warn("We couldn't find the DB path. We cannot backup.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var backupDir = Directory.CreateDirectory(Path.Combine(dir, "Backup"));
|
||||||
|
|
||||||
|
var files = backupDir.GetFiles();
|
||||||
|
|
||||||
|
foreach (var file in files) {
|
||||||
|
var dt = ParseName(file.Name);
|
||||||
|
if(dt < DateTime.Now.AddDays(-7)){
|
||||||
|
try {
|
||||||
|
|
||||||
|
File.Delete(file.FullName);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Log.Error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DoWeNeedToBackup(string backupPath)
|
||||||
|
{
|
||||||
|
var files = Directory.GetFiles(backupPath);
|
||||||
|
//TODO Get the latest file and if it's within an hour of DateTime.Now then don't bother backing up.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DateTime ParseName(string fileName)
|
||||||
|
{
|
||||||
|
var names = fileName.Split(new []{'_','.',' '}, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if(names.Count() > 1)
|
||||||
|
{
|
||||||
|
DateTime parsed;
|
||||||
|
//DateTime.TryParseExcat(names[1], "yyyy-MM-dd hh.mm.ss",CultureInfo.CurrentUICulture, DateTimeStyles.None, out parsed);
|
||||||
|
DateTime.TryParse(names[2], out parsed);
|
||||||
|
return parsed;
|
||||||
|
|
||||||
|
}
|
||||||
|
return DateTime.MinValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,102 +0,0 @@
|
||||||
#region Copyright
|
|
||||||
// /************************************************************************
|
|
||||||
// Copyright (c) 2016 Jamie Rees
|
|
||||||
// File: AvailabilityUpdateService.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.Reactive.Linq;
|
|
||||||
using System.Web.Hosting;
|
|
||||||
|
|
||||||
using FluentScheduler;
|
|
||||||
|
|
||||||
using Mono.Data.Sqlite;
|
|
||||||
|
|
||||||
using NLog;
|
|
||||||
|
|
||||||
using PlexRequests.Api;
|
|
||||||
using PlexRequests.Core;
|
|
||||||
using PlexRequests.Core.SettingModels;
|
|
||||||
using PlexRequests.Helpers;
|
|
||||||
using PlexRequests.Services.Interfaces;
|
|
||||||
using PlexRequests.Store;
|
|
||||||
using PlexRequests.Store.Repository;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace PlexRequests.Services
|
|
||||||
{
|
|
||||||
public class MediaCacheService : ITask, IRegisteredObject, IAvailabilityUpdateService
|
|
||||||
{
|
|
||||||
public MediaCacheService()
|
|
||||||
{
|
|
||||||
var memCache = new MemoryCacheProvider();
|
|
||||||
var dbConfig = new DbConfiguration(new SqliteFactory());
|
|
||||||
var repo = new SettingsJsonRepository(dbConfig, memCache);
|
|
||||||
|
|
||||||
ConfigurationReader = new ConfigurationReader();
|
|
||||||
CpCacher = new CouchPotatoCacher(new SettingsServiceV2<CouchPotatoSettings>(repo), new CouchPotatoApi(), memCache);
|
|
||||||
SonarrCacher = new SonarrCacher(new SettingsServiceV2<SonarrSettings>(repo), new SonarrApi(), memCache);
|
|
||||||
SickRageCacher = new SickRageCacher(new SettingsServiceV2<SickRageSettings>(repo), new SickrageApi(), memCache);
|
|
||||||
HostingEnvironment.RegisterObject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
|
||||||
|
|
||||||
private IConfigurationReader ConfigurationReader { get; }
|
|
||||||
private ICouchPotatoCacher CpCacher { get; }
|
|
||||||
private ISonarrCacher SonarrCacher { get; }
|
|
||||||
private ISickRageCacher SickRageCacher { get; }
|
|
||||||
private IDisposable CpSubscription { get; set; }
|
|
||||||
private IDisposable SonarrSubscription { get; set; }
|
|
||||||
private IDisposable SickRageSubscription { get; set; }
|
|
||||||
|
|
||||||
public void Start(Configuration c)
|
|
||||||
{
|
|
||||||
CpSubscription?.Dispose();
|
|
||||||
SonarrSubscription?.Dispose();
|
|
||||||
SickRageSubscription?.Dispose();
|
|
||||||
|
|
||||||
Task.Factory.StartNew(() => CpCacher.Queued(-1));
|
|
||||||
Task.Factory.StartNew(() => SonarrCacher.Queued(-1));
|
|
||||||
Task.Factory.StartNew(() => SickRageCacher.Queued(-1));
|
|
||||||
CpSubscription = Observable.Interval(c.Intervals.Notification).Subscribe(CpCacher.Queued);
|
|
||||||
SonarrSubscription = Observable.Interval(c.Intervals.Notification).Subscribe(SonarrCacher.Queued);
|
|
||||||
SickRageSubscription = Observable.Interval(c.Intervals.Notification).Subscribe(SickRageCacher.Queued);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Execute()
|
|
||||||
{
|
|
||||||
Start(ConfigurationReader.Read());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Stop(bool immediate)
|
|
||||||
{
|
|
||||||
HostingEnvironment.UnregisterObject(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ICouchPotatoCacheService
|
|
||||||
{
|
|
||||||
void Start(Configuration c);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -25,15 +25,17 @@
|
||||||
// ************************************************************************/
|
// ************************************************************************/
|
||||||
#endregion
|
#endregion
|
||||||
using System;
|
using System;
|
||||||
using System.Net;
|
|
||||||
using System.Net.Mail;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using MailKit.Security;
|
||||||
|
|
||||||
|
using MimeKit;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
using PlexRequests.Core;
|
using PlexRequests.Core;
|
||||||
using PlexRequests.Core.SettingModels;
|
using PlexRequests.Core.SettingModels;
|
||||||
using PlexRequests.Services.Interfaces;
|
using PlexRequests.Services.Interfaces;
|
||||||
|
using SmtpClient = MailKit.Net.Smtp.SmtpClient;
|
||||||
|
|
||||||
namespace PlexRequests.Services.Notification
|
namespace PlexRequests.Services.Notification
|
||||||
{
|
{
|
||||||
|
@ -71,8 +73,8 @@ namespace PlexRequests.Services.Notification
|
||||||
await EmailIssue(model, emailSettings);
|
await EmailIssue(model, emailSettings);
|
||||||
break;
|
break;
|
||||||
case NotificationType.RequestAvailable:
|
case NotificationType.RequestAvailable:
|
||||||
throw new NotImplementedException();
|
await EmailAvailableRequest(model, emailSettings);
|
||||||
|
break;
|
||||||
case NotificationType.RequestApproved:
|
case NotificationType.RequestApproved:
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
|
|
||||||
|
@ -111,58 +113,68 @@ namespace PlexRequests.Services.Notification
|
||||||
|
|
||||||
private async Task EmailNewRequest(NotificationModel model, EmailNotificationSettings settings)
|
private async Task EmailNewRequest(NotificationModel model, EmailNotificationSettings settings)
|
||||||
{
|
{
|
||||||
var message = new MailMessage
|
var message = new MimeMessage
|
||||||
{
|
{
|
||||||
IsBodyHtml = true,
|
Body = new TextPart("plain") { Text = $"Hello! The user '{model.User}' has requested {model.Title}! Please log in to approve this request. Request Date: {model.DateTime.ToString("f")}" },
|
||||||
To = { new MailAddress(settings.RecipientEmail) },
|
|
||||||
Body = $"Hello! The user '{model.User}' has requested {model.Title}! Please log in to approve this request. Request Date: {model.DateTime.ToString("f")}",
|
|
||||||
From = new MailAddress(settings.EmailSender),
|
|
||||||
Subject = $"Plex Requests: New request for {model.Title}!"
|
Subject = $"Plex Requests: New request for {model.Title}!"
|
||||||
};
|
};
|
||||||
|
message.From.Add(new MailboxAddress(settings.EmailSender, settings.EmailSender));
|
||||||
|
message.To.Add(new MailboxAddress(settings.RecipientEmail, settings.RecipientEmail));
|
||||||
|
|
||||||
try
|
|
||||||
{
|
await Send(message, settings);
|
||||||
using (var smtp = new SmtpClient(settings.EmailHost, settings.EmailPort))
|
|
||||||
{
|
|
||||||
smtp.Credentials = new NetworkCredential(settings.EmailUsername, settings.EmailPassword);
|
|
||||||
smtp.EnableSsl = settings.Ssl;
|
|
||||||
await smtp.SendMailAsync(message).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (SmtpException smtp)
|
|
||||||
{
|
|
||||||
Log.Error(smtp);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task EmailIssue(NotificationModel model, EmailNotificationSettings settings)
|
private async Task EmailIssue(NotificationModel model, EmailNotificationSettings settings)
|
||||||
{
|
{
|
||||||
var message = new MailMessage
|
var message = new MimeMessage
|
||||||
{
|
{
|
||||||
IsBodyHtml = true,
|
Body = new TextPart("plain") { Text = $"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!" },
|
||||||
To = { new MailAddress(settings.RecipientEmail) },
|
|
||||||
Body = $"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!",
|
|
||||||
From = new MailAddress(settings.RecipientEmail),
|
|
||||||
Subject = $"Plex Requests: New issue for {model.Title}!"
|
Subject = $"Plex Requests: New issue for {model.Title}!"
|
||||||
};
|
};
|
||||||
|
message.From.Add(new MailboxAddress(settings.EmailSender, settings.EmailSender));
|
||||||
|
message.To.Add(new MailboxAddress(settings.RecipientEmail, settings.RecipientEmail));
|
||||||
|
|
||||||
|
|
||||||
|
await Send(message, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task EmailAvailableRequest(NotificationModel model, EmailNotificationSettings settings)
|
||||||
|
{
|
||||||
|
if (!settings.EnableUserEmailNotifications)
|
||||||
|
{
|
||||||
|
await Task.FromResult(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = new MimeMessage
|
||||||
|
{
|
||||||
|
Body = new TextPart("plain") { Text = $"Hello! You requested {model.Title} on PlexRequests! This is now available on Plex! :)" },
|
||||||
|
Subject = $"Plex Requests: {model.Title} is now available!"
|
||||||
|
};
|
||||||
|
message.From.Add(new MailboxAddress(settings.EmailSender, settings.EmailSender));
|
||||||
|
message.To.Add(new MailboxAddress(model.UserEmail, model.UserEmail));
|
||||||
|
|
||||||
|
await Send(message, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Send(MimeMessage message, EmailNotificationSettings settings)
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var smtp = new SmtpClient(settings.EmailHost, settings.EmailPort))
|
using (var client = new SmtpClient())
|
||||||
{
|
{
|
||||||
smtp.Credentials = new NetworkCredential(settings.EmailUsername, settings.EmailPassword);
|
client.Connect(settings.EmailHost, settings.EmailPort, SecureSocketOptions.Auto); // Let MailKit figure out the correct SecureSocketOptions.
|
||||||
smtp.EnableSsl = settings.Ssl;
|
|
||||||
await smtp.SendMailAsync(message).ConfigureAwait(false);
|
// Note: since we don't have an OAuth2 token, disable
|
||||||
|
// the XOAUTH2 authentication mechanism.
|
||||||
|
client.AuthenticationMechanisms.Remove("XOAUTH2");
|
||||||
|
|
||||||
|
client.Authenticate(settings.EmailUsername, settings.EmailPassword);
|
||||||
|
|
||||||
|
await client.SendAsync(message);
|
||||||
|
await client.DisconnectAsync(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (SmtpException smtp)
|
|
||||||
{
|
|
||||||
Log.Error(smtp);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.Error(e);
|
Log.Error(e);
|
||||||
|
@ -171,32 +183,15 @@ namespace PlexRequests.Services.Notification
|
||||||
|
|
||||||
private async Task EmailTest(NotificationModel model, EmailNotificationSettings settings)
|
private async Task EmailTest(NotificationModel model, EmailNotificationSettings settings)
|
||||||
{
|
{
|
||||||
var message = new MailMessage
|
var message = new MimeMessage
|
||||||
{
|
{
|
||||||
IsBodyHtml = true,
|
Body = new TextPart("plain") {Text= "This is just a test! Success!"},
|
||||||
To = { new MailAddress(settings.RecipientEmail) },
|
Subject = "Plex Requests: Test Message!",
|
||||||
Body = "This is just a test! Success!",
|
|
||||||
From = new MailAddress(settings.RecipientEmail),
|
|
||||||
Subject = "Plex Requests: Test Message!"
|
|
||||||
};
|
};
|
||||||
|
message.From.Add(new MailboxAddress(settings.EmailSender, settings.EmailSender));
|
||||||
|
message.To.Add(new MailboxAddress(settings.RecipientEmail, settings.RecipientEmail));
|
||||||
|
|
||||||
try
|
await Send(message, settings);
|
||||||
{
|
|
||||||
using (var smtp = new SmtpClient(settings.EmailHost, settings.EmailPort))
|
|
||||||
{
|
|
||||||
smtp.Credentials = new NetworkCredential(settings.EmailUsername, settings.EmailPassword);
|
|
||||||
smtp.EnableSsl = settings.Ssl;
|
|
||||||
await smtp.SendMailAsync(message).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (SmtpException smtp)
|
|
||||||
{
|
|
||||||
Log.Error(smtp);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -35,5 +35,6 @@ namespace PlexRequests.Services.Notification
|
||||||
public DateTime DateTime { get; set; }
|
public DateTime DateTime { get; set; }
|
||||||
public NotificationType NotificationType { get; set; }
|
public NotificationType NotificationType { get; set; }
|
||||||
public string User { get; set; }
|
public string User { get; set; }
|
||||||
|
public string UserEmail { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -41,6 +41,11 @@ namespace PlexRequests.Services.Notification
|
||||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||||
public ConcurrentDictionary<string, INotification> Observers { get; } = new ConcurrentDictionary<string, INotification>();
|
public ConcurrentDictionary<string, INotification> Observers { get; } = new ConcurrentDictionary<string, INotification>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a notification to the user. This one is used in normal notification scenarios
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model.</param>
|
||||||
|
/// <returns></returns>
|
||||||
public async Task Publish(NotificationModel model)
|
public async Task Publish(NotificationModel model)
|
||||||
{
|
{
|
||||||
var notificationTasks = Observers.Values.Select(notification => NotifyAsync(notification, model));
|
var notificationTasks = Observers.Values.Select(notification => NotifyAsync(notification, model));
|
||||||
|
@ -48,6 +53,12 @@ namespace PlexRequests.Services.Notification
|
||||||
await Task.WhenAll(notificationTasks).ConfigureAwait(false);
|
await Task.WhenAll(notificationTasks).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a notification to the user, this is usually for testing the settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model.</param>
|
||||||
|
/// <param name="settings">The settings.</param>
|
||||||
|
/// <returns></returns>
|
||||||
public async Task Publish(NotificationModel model, Settings settings)
|
public async Task Publish(NotificationModel model, Settings settings)
|
||||||
{
|
{
|
||||||
var notificationTasks = Observers.Values.Select(notification => NotifyAsync(notification, model, settings));
|
var notificationTasks = Observers.Values.Select(notification => NotifyAsync(notification, model, settings));
|
||||||
|
|
|
@ -33,6 +33,7 @@ namespace PlexRequests.Services.Notification
|
||||||
RequestAvailable,
|
RequestAvailable,
|
||||||
RequestApproved,
|
RequestApproved,
|
||||||
AdminNote,
|
AdminNote,
|
||||||
Test
|
Test,
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,6 @@ namespace PlexRequests.Services.Notification
|
||||||
}
|
}
|
||||||
private IPushbulletApi PushbulletApi { get; }
|
private IPushbulletApi PushbulletApi { get; }
|
||||||
private ISettingsService<PushbulletNotificationSettings> SettingsService { get; }
|
private ISettingsService<PushbulletNotificationSettings> SettingsService { get; }
|
||||||
private PushbulletNotificationSettings Settings => GetSettings();
|
|
||||||
|
|
||||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||||
public string NotificationName => "PushbulletNotification";
|
public string NotificationName => "PushbulletNotification";
|
||||||
|
@ -78,7 +77,7 @@ namespace PlexRequests.Services.Notification
|
||||||
case NotificationType.AdminNote:
|
case NotificationType.AdminNote:
|
||||||
break;
|
break;
|
||||||
case NotificationType.Test:
|
case NotificationType.Test:
|
||||||
await PushTestAsync(model, pushSettings);
|
await PushTestAsync(pushSettings);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
|
@ -107,45 +106,28 @@ namespace PlexRequests.Services.Notification
|
||||||
{
|
{
|
||||||
var message = $"{model.Title} has been requested by user: {model.User}";
|
var message = $"{model.Title} has been requested by user: {model.User}";
|
||||||
var pushTitle = $"Plex Requests: {model.Title} has been requested!";
|
var pushTitle = $"Plex Requests: {model.Title} has been requested!";
|
||||||
try
|
await Push(settings, message, pushTitle);
|
||||||
{
|
|
||||||
var result = await PushbulletApi.PushAsync(settings.AccessToken, pushTitle, message, settings.DeviceIdentifier);
|
|
||||||
if (result == null)
|
|
||||||
{
|
|
||||||
Log.Error("Pushbullet api returned a null value, the notification did not get pushed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task PushIssueAsync(NotificationModel model, PushbulletNotificationSettings settings)
|
private async Task PushIssueAsync(NotificationModel model, PushbulletNotificationSettings settings)
|
||||||
{
|
{
|
||||||
var message = $"A new issue: {model.Body} has been reported by user: {model.User} for the title: {model.Title}";
|
var message = $"A new issue: {model.Body} has been reported by user: {model.User} for the title: {model.Title}";
|
||||||
var pushTitle = $"Plex Requests: A new issue has been reported for {model.Title}";
|
var pushTitle = $"Plex Requests: A new issue has been reported for {model.Title}";
|
||||||
try
|
await Push(settings, message, pushTitle);
|
||||||
{
|
|
||||||
var result = await PushbulletApi.PushAsync(settings.AccessToken, pushTitle, message, settings.DeviceIdentifier);
|
|
||||||
if (result != null)
|
|
||||||
{
|
|
||||||
Log.Error("Pushbullet api returned a null value, the notification did not get pushed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task PushTestAsync(NotificationModel model, PushbulletNotificationSettings settings)
|
private async Task PushTestAsync(PushbulletNotificationSettings settings)
|
||||||
{
|
{
|
||||||
var message = "This is just a test! Success!";
|
var message = "This is just a test! Success!";
|
||||||
var pushTitle = "Plex Requests: Test Message!";
|
var pushTitle = "Plex Requests: Test Message!";
|
||||||
|
await Push(settings, message, pushTitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Push(PushbulletNotificationSettings settings, string message, string title)
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = await PushbulletApi.PushAsync(settings.AccessToken, pushTitle, message, settings.DeviceIdentifier);
|
var result = await PushbulletApi.PushAsync(settings.AccessToken, title, message, settings.DeviceIdentifier);
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
Log.Error("Pushbullet api returned a null value, the notification did not get pushed");
|
Log.Error("Pushbullet api returned a null value, the notification did not get pushed");
|
||||||
|
|
|
@ -45,7 +45,6 @@ namespace PlexRequests.Services.Notification
|
||||||
}
|
}
|
||||||
private IPushoverApi PushoverApi { get; }
|
private IPushoverApi PushoverApi { get; }
|
||||||
private ISettingsService<PushoverNotificationSettings> SettingsService { get; }
|
private ISettingsService<PushoverNotificationSettings> SettingsService { get; }
|
||||||
private PushoverNotificationSettings Settings => GetSettings();
|
|
||||||
|
|
||||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||||
public string NotificationName => "PushoverNotification";
|
public string NotificationName => "PushoverNotification";
|
||||||
|
@ -106,40 +105,23 @@ namespace PlexRequests.Services.Notification
|
||||||
private async Task PushNewRequestAsync(NotificationModel model, PushoverNotificationSettings settings)
|
private async Task PushNewRequestAsync(NotificationModel model, PushoverNotificationSettings settings)
|
||||||
{
|
{
|
||||||
var message = $"Plex Requests: {model.Title} has been requested by user: {model.User}";
|
var message = $"Plex Requests: {model.Title} has been requested by user: {model.User}";
|
||||||
try
|
await Push(settings, message);
|
||||||
{
|
|
||||||
var result = await PushoverApi.PushAsync(settings.AccessToken, message, settings.UserToken);
|
|
||||||
if (result?.status != 1)
|
|
||||||
{
|
|
||||||
Log.Error("Pushover api returned a status that was not 1, the notification did not get pushed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task PushIssueAsync(NotificationModel model, PushoverNotificationSettings settings)
|
private async Task PushIssueAsync(NotificationModel model, PushoverNotificationSettings settings)
|
||||||
{
|
{
|
||||||
var message = $"Plex Requests: A new issue: {model.Body} has been reported by user: {model.User} for the title: {model.Title}";
|
var message = $"Plex Requests: A new issue: {model.Body} has been reported by user: {model.User} for the title: {model.Title}";
|
||||||
try
|
await Push(settings, message);
|
||||||
{
|
|
||||||
var result = await PushoverApi.PushAsync(settings.AccessToken, message, settings.UserToken);
|
|
||||||
if (result?.status != 1)
|
|
||||||
{
|
|
||||||
Log.Error("Pushover api returned a status that was not 1, the notification did not get pushed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task PushTestAsync(NotificationModel model, PushoverNotificationSettings settings)
|
private async Task PushTestAsync(NotificationModel model, PushoverNotificationSettings settings)
|
||||||
{
|
{
|
||||||
var message = $"Plex Requests: Test Message!";
|
var message = $"Plex Requests: Test Message!";
|
||||||
|
await Push(settings, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Push(PushoverNotificationSettings settings, string message)
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = await PushoverApi.PushAsync(settings.AccessToken, message, settings.UserToken);
|
var result = await PushoverApi.PushAsync(settings.AccessToken, message, settings.UserToken);
|
||||||
|
|
155
PlexRequests.Services/Notification/SlackNotification.cs
Normal file
155
PlexRequests.Services/Notification/SlackNotification.cs
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2016 Jamie Rees
|
||||||
|
// File: SlackNotification.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 NLog;
|
||||||
|
|
||||||
|
using PlexRequests.Api.Interfaces;
|
||||||
|
using PlexRequests.Api.Models.Notifications;
|
||||||
|
using PlexRequests.Core;
|
||||||
|
using PlexRequests.Core.SettingModels;
|
||||||
|
using PlexRequests.Services.Interfaces;
|
||||||
|
|
||||||
|
namespace PlexRequests.Services.Notification
|
||||||
|
{
|
||||||
|
public class SlackNotification : INotification
|
||||||
|
{
|
||||||
|
public SlackNotification(ISlackApi api, ISettingsService<SlackNotificationSettings> sn)
|
||||||
|
{
|
||||||
|
Api = api;
|
||||||
|
Settings = sn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string NotificationName => "SlackNotification";
|
||||||
|
|
||||||
|
private ISlackApi Api { get; }
|
||||||
|
private ISettingsService<SlackNotificationSettings> Settings { get; }
|
||||||
|
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
|
||||||
|
public async Task NotifyAsync(NotificationModel model)
|
||||||
|
{
|
||||||
|
var settings = Settings.GetSettings();
|
||||||
|
|
||||||
|
await NotifyAsync(model, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task NotifyAsync(NotificationModel model, Settings settings)
|
||||||
|
{
|
||||||
|
if (settings == null) await NotifyAsync(model);
|
||||||
|
|
||||||
|
var pushSettings = (SlackNotificationSettings)settings;
|
||||||
|
if (!ValidateConfiguration(pushSettings))
|
||||||
|
{
|
||||||
|
Log.Error("Settings for Slack was not correct, we cannot push a notification");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (model.NotificationType)
|
||||||
|
{
|
||||||
|
case NotificationType.NewRequest:
|
||||||
|
await PushNewRequestAsync(model, pushSettings);
|
||||||
|
break;
|
||||||
|
case NotificationType.Issue:
|
||||||
|
await PushIssueAsync(model, pushSettings);
|
||||||
|
break;
|
||||||
|
case NotificationType.RequestAvailable:
|
||||||
|
break;
|
||||||
|
case NotificationType.RequestApproved:
|
||||||
|
break;
|
||||||
|
case NotificationType.AdminNote:
|
||||||
|
break;
|
||||||
|
case NotificationType.Test:
|
||||||
|
await PushTest(pushSettings);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PushNewRequestAsync(NotificationModel model, SlackNotificationSettings settings)
|
||||||
|
{
|
||||||
|
var message = $"{model.Title} has been requested by user: {model.User}";
|
||||||
|
await Push(settings, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PushIssueAsync(NotificationModel model, SlackNotificationSettings settings)
|
||||||
|
{
|
||||||
|
var message = $"A new issue: {model.Body} has been reported by user: {model.User} for the title: {model.Title}";
|
||||||
|
await Push(settings, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PushTest(SlackNotificationSettings settings)
|
||||||
|
{
|
||||||
|
var message = $"This is a test from Plex Requests, if you can see this then we have successfully pushed a notification!";
|
||||||
|
await Push(settings, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Push(SlackNotificationSettings config, string message)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var notification = new SlackNotificationBody { username = config.Username, channel = config.Channel ?? string.Empty, text = message };
|
||||||
|
|
||||||
|
var result = await Api.PushAsync(config.Team, config.Token, config.Service, notification);
|
||||||
|
if (!result.Equals("ok"))
|
||||||
|
{
|
||||||
|
Log.Error("Slack returned a message that was not 'ok', the notification did not get pushed");
|
||||||
|
Log.Error($"Message that slack returned: {result}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ValidateConfiguration(SlackNotificationSettings settings)
|
||||||
|
{
|
||||||
|
if (!settings.Enabled)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(settings.WebhookUrl))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var a = settings.Team;
|
||||||
|
var b = settings.Service;
|
||||||
|
var c = settings.Token;
|
||||||
|
}
|
||||||
|
catch (IndexOutOfRangeException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>PlexRequests.Services</RootNamespace>
|
<RootNamespace>PlexRequests.Services</RootNamespace>
|
||||||
<AssemblyName>PlexRequests.Services</AssemblyName>
|
<AssemblyName>PlexRequests.Services</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -31,73 +31,59 @@
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="BouncyCastle, Version=1.8.1.0, Culture=neutral, PublicKeyToken=0e99375e54769942, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\MimeKit.1.2.22\lib\net45\BouncyCastle.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="FluentScheduler, Version=3.1.46.0, Culture=neutral, PublicKeyToken=b76503528a14ebd1, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\FluentScheduler.3.1.46\lib\net40\FluentScheduler.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="MailKit, Version=1.2.0.0, Culture=neutral, PublicKeyToken=4e064fe7c44a8f1b, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\MailKit.1.2.21\lib\net451\MailKit.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Microsoft.Build.Framework" />
|
<Reference Include="Microsoft.Build.Framework" />
|
||||||
<Reference Include="MimeKit, Version=1.2.0.0, Culture=neutral, PublicKeyToken=bede1c8a46c66814, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\MimeKit.1.2.22\lib\net45\MimeKit.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Mono.Data.Sqlite, Version=4.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
|
|
||||||
<SpecificVersion>False</SpecificVersion>
|
|
||||||
<HintPath>..\Assemblies\Mono.Data.Sqlite.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Reactive.Core, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System.Reactive.Interfaces, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System.Reactive.Linq, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System.Reactive.PlatformServices, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System.Security" />
|
<Reference Include="System.Security" />
|
||||||
<Reference Include="System.Web" />
|
<Reference Include="System.Web" />
|
||||||
|
<Reference Include="System.Windows.Forms" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="BouncyCastle, Version=1.8.1.0, Culture=neutral, PublicKeyToken=0e99375e54769942">
|
||||||
|
<HintPath>..\packages\MimeKit.1.2.22\lib\net45\BouncyCastle.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Common.Logging, Version=3.0.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e">
|
||||||
|
<HintPath>..\packages\Common.Logging.3.0.0\lib\net40\Common.Logging.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Common.Logging.Core, Version=3.0.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e">
|
||||||
|
<HintPath>..\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="MailKit, Version=1.2.0.0, Culture=neutral, PublicKeyToken=4e064fe7c44a8f1b">
|
||||||
|
<HintPath>..\packages\MailKit.1.2.21\lib\net451\MailKit.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="MimeKit, Version=1.2.0.0, Culture=neutral, PublicKeyToken=bede1c8a46c66814">
|
||||||
|
<HintPath>..\packages\MimeKit.1.2.22\lib\net45\MimeKit.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Mono.Data.Sqlite">
|
||||||
|
<HintPath>..\Assemblies\Mono.Data.Sqlite.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c">
|
||||||
|
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Quartz, Version=2.3.3.0, Culture=neutral, PublicKeyToken=f6b8c98a402cc8a4">
|
||||||
|
<HintPath>..\packages\Quartz.2.3.3\lib\net40\Quartz.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="Interfaces\IJobRecord.cs" />
|
||||||
|
<Compile Include="Jobs\JobRecord.cs" />
|
||||||
|
<Compile Include="Jobs\JobNames.cs" />
|
||||||
|
<Compile Include="Jobs\StoreBackup.cs" />
|
||||||
|
<Compile Include="Jobs\CouchPotatoCacher.cs" />
|
||||||
|
<Compile Include="Jobs\PlexAvailabilityChecker.cs" />
|
||||||
|
<Compile Include="Jobs\SickRageCacher.cs" />
|
||||||
|
<Compile Include="Jobs\SonarrCacher.cs" />
|
||||||
<Compile Include="Models\PlexAlbum.cs" />
|
<Compile Include="Models\PlexAlbum.cs" />
|
||||||
<Compile Include="Models\PlexMovie.cs" />
|
<Compile Include="Models\PlexMovie.cs" />
|
||||||
<Compile Include="Models\PlexTvShow.cs" />
|
<Compile Include="Models\PlexTvShow.cs" />
|
||||||
<Compile Include="SickRageCacher.cs" />
|
<Compile Include="Interfaces\ISickRageCacher.cs" />
|
||||||
<Compile Include="Interfaces\ITvCacher.cs" />
|
|
||||||
<Compile Include="SonarrCacher.cs" />
|
|
||||||
<Compile Include="Interfaces\ISonarrCacher.cs" />
|
<Compile Include="Interfaces\ISonarrCacher.cs" />
|
||||||
<Compile Include="MediaCacheService.cs" />
|
|
||||||
<Compile Include="AvailabilityUpdateService.cs" />
|
|
||||||
<Compile Include="Configuration.cs" />
|
|
||||||
<Compile Include="ConfigurationReader.cs" />
|
|
||||||
<Compile Include="Interfaces\ICouchPotatoCacher.cs" />
|
<Compile Include="Interfaces\ICouchPotatoCacher.cs" />
|
||||||
<Compile Include="Interfaces\IAvailabilityChecker.cs" />
|
<Compile Include="Interfaces\IAvailabilityChecker.cs" />
|
||||||
<Compile Include="Interfaces\IConfigurationReader.cs" />
|
|
||||||
<Compile Include="Interfaces\IIntervals.cs" />
|
<Compile Include="Interfaces\IIntervals.cs" />
|
||||||
<Compile Include="Interfaces\INotification.cs" />
|
<Compile Include="Interfaces\INotification.cs" />
|
||||||
<Compile Include="Interfaces\INotificationService.cs" />
|
<Compile Include="Interfaces\INotificationService.cs" />
|
||||||
|
@ -107,11 +93,8 @@
|
||||||
<Compile Include="Notification\NotificationType.cs" />
|
<Compile Include="Notification\NotificationType.cs" />
|
||||||
<Compile Include="Notification\PushoverNotification.cs" />
|
<Compile Include="Notification\PushoverNotification.cs" />
|
||||||
<Compile Include="Notification\PushbulletNotification.cs" />
|
<Compile Include="Notification\PushbulletNotification.cs" />
|
||||||
<Compile Include="CouchPotatoCacher.cs" />
|
<Compile Include="Notification\SlackNotification.cs" />
|
||||||
<Compile Include="PlexAvailabilityChecker.cs" />
|
|
||||||
<Compile Include="PlexType.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="UpdateInterval.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="app.config" />
|
<None Include="app.config" />
|
||||||
|
@ -119,7 +102,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\PlexRequests.Api.Interfaces\PlexRequests.Api.Interfaces.csproj">
|
<ProjectReference Include="..\PlexRequests.Api.Interfaces\PlexRequests.Api.Interfaces.csproj">
|
||||||
<Project>{95834072-a675-415d-aa8f-877c91623810}</Project>
|
<Project>{95834072-A675-415D-AA8F-877C91623810}</Project>
|
||||||
<Name>PlexRequests.Api.Interfaces</Name>
|
<Name>PlexRequests.Api.Interfaces</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\PlexRequests.Api.Models\PlexRequests.Api.Models.csproj">
|
<ProjectReference Include="..\PlexRequests.Api.Models\PlexRequests.Api.Models.csproj">
|
||||||
|
@ -127,11 +110,11 @@
|
||||||
<Name>PlexRequests.Api.Models</Name>
|
<Name>PlexRequests.Api.Models</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\PlexRequests.Api\PlexRequests.Api.csproj">
|
<ProjectReference Include="..\PlexRequests.Api\PlexRequests.Api.csproj">
|
||||||
<Project>{8cb8d235-2674-442d-9c6a-35fcaeeb160d}</Project>
|
<Project>{8CB8D235-2674-442D-9C6A-35FCAEEB160D}</Project>
|
||||||
<Name>PlexRequests.Api</Name>
|
<Name>PlexRequests.Api</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\PlexRequests.Core\PlexRequests.Core.csproj">
|
<ProjectReference Include="..\PlexRequests.Core\PlexRequests.Core.csproj">
|
||||||
<Project>{dd7dc444-d3bf-4027-8ab9-efc71f5ec581}</Project>
|
<Project>{DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}</Project>
|
||||||
<Name>PlexRequests.Core</Name>
|
<Name>PlexRequests.Core</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\PlexRequests.Helpers\PlexRequests.Helpers.csproj">
|
<ProjectReference Include="..\PlexRequests.Helpers\PlexRequests.Helpers.csproj">
|
||||||
|
@ -139,7 +122,7 @@
|
||||||
<Name>PlexRequests.Helpers</Name>
|
<Name>PlexRequests.Helpers</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\PlexRequests.Store\PlexRequests.Store.csproj">
|
<ProjectReference Include="..\PlexRequests.Store\PlexRequests.Store.csproj">
|
||||||
<Project>{92433867-2b7b-477b-a566-96c382427525}</Project>
|
<Project>{92433867-2B7B-477B-A566-96C382427525}</Project>
|
||||||
<Name>PlexRequests.Store</Name>
|
<Name>PlexRequests.Store</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -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.5.2"/></startup></configuration>
|
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /></startup></configuration>
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="FluentScheduler" version="3.1.46" targetFramework="net452" />
|
<package id="Common.Logging" version="3.0.0" targetFramework="net45" />
|
||||||
<package id="MailKit" version="1.2.21" targetFramework="net46" />
|
<package id="Common.Logging.Core" version="3.0.0" targetFramework="net45" />
|
||||||
<package id="MimeKit" version="1.2.22" targetFramework="net46" />
|
<package id="MailKit" version="1.2.21" targetFramework="net45" requireReinstallation="True" />
|
||||||
<package id="NLog" version="4.2.3" targetFramework="net452" />
|
<package id="MimeKit" version="1.2.22" targetFramework="net45" />
|
||||||
<package id="Rx-Core" version="2.2.5" targetFramework="net452" />
|
<package id="NLog" version="4.2.3" targetFramework="net45" />
|
||||||
<package id="Rx-Interfaces" version="2.2.5" targetFramework="net452" />
|
<package id="Quartz" version="2.3.3" targetFramework="net45" />
|
||||||
<package id="Rx-Linq" version="2.2.5" targetFramework="net452" />
|
|
||||||
<package id="Rx-Main" version="2.2.5" targetFramework="net452" />
|
|
||||||
<package id="Rx-PlatformServices" version="2.2.5" targetFramework="net452" />
|
|
||||||
</packages>
|
</packages>
|
|
@ -23,6 +23,9 @@
|
||||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
// ***********************************************************************
|
// ***********************************************************************
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
using System;
|
using System;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
@ -44,7 +47,7 @@ namespace PlexRequests.Store
|
||||||
}
|
}
|
||||||
|
|
||||||
private SqliteFactory Factory { get; }
|
private SqliteFactory Factory { get; }
|
||||||
private string CurrentPath =>Path.Combine(Path.GetDirectoryName(Application.ExecutablePath) ?? string.Empty, DbFile);
|
public string CurrentPath =>Path.Combine(Path.GetDirectoryName(Application.ExecutablePath) ?? string.Empty, DbFile);
|
||||||
|
|
||||||
public virtual bool CheckDb()
|
public virtual bool CheckDb()
|
||||||
{
|
{
|
||||||
|
@ -59,7 +62,7 @@ namespace PlexRequests.Store
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string DbFile = "PlexRequests.sqlite";
|
public const string DbFile = "PlexRequests.sqlite";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the database connection.
|
/// Gets the database connection.
|
||||||
|
|
|
@ -45,6 +45,9 @@ namespace PlexRequests.Store
|
||||||
/// Creates the database.
|
/// Creates the database.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void CreateDatabase();
|
void CreateDatabase();
|
||||||
}
|
|
||||||
|
string CurrentPath { get; }
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
16
PlexRequests.Store/Models/Audit.cs
Normal file
16
PlexRequests.Store/Models/Audit.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using System;
|
||||||
|
using Dapper.Contrib.Extensions;
|
||||||
|
|
||||||
|
namespace PlexRequests.Store
|
||||||
|
{
|
||||||
|
[Table("Audit")]
|
||||||
|
public class Audit : Entity
|
||||||
|
{
|
||||||
|
public string Username{get;set;}
|
||||||
|
public DateTime Date {get;set;}
|
||||||
|
public string ChangeType {get;set;}
|
||||||
|
public string OldValue {get;set;}
|
||||||
|
public string NewValue{get;set;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,38 +1,39 @@
|
||||||
#region Copyright
|
#region Copyright
|
||||||
// /************************************************************************
|
// /************************************************************************
|
||||||
// Copyright (c) 2016 Jamie Rees
|
// Copyright (c) 2016 Jamie Rees
|
||||||
// File: UpdateInterval.cs
|
// File: LogEntity.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
|
||||||
// a copy of this software and associated documentation files (the
|
// a copy of this software and associated documentation files (the
|
||||||
// "Software"), to deal in the Software without restriction, including
|
// "Software"), to deal in the Software without restriction, including
|
||||||
// without limitation the rights to use, copy, modify, merge, publish,
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
// permit persons to whom the Software is furnished to do so, subject to
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
// the following conditions:
|
// the following conditions:
|
||||||
//
|
//
|
||||||
// The above copyright notice and this permission notice shall be
|
// The above copyright notice and this permission notice shall be
|
||||||
// included in all copies or substantial portions of the Software.
|
// included in all copies or substantial portions of the Software.
|
||||||
//
|
//
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
// ************************************************************************/
|
// ************************************************************************/
|
||||||
#endregion
|
#endregion
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
using PlexRequests.Services.Interfaces;
|
using Dapper.Contrib.Extensions;
|
||||||
|
|
||||||
namespace PlexRequests.Services
|
namespace PlexRequests.Store.Models
|
||||||
{
|
{
|
||||||
public class UpdateInterval : IIntervals
|
[Table("ScheduledJobs")]
|
||||||
{
|
public class ScheduledJobs : Entity
|
||||||
public TimeSpan Notification => TimeSpan.FromMinutes(10);
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
}
|
public DateTime LastRun { get; set; }
|
||||||
}
|
}
|
||||||
|
}
|
36
PlexRequests.Store/Models/UsersToNotify.cs
Normal file
36
PlexRequests.Store/Models/UsersToNotify.cs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2016 Jamie Rees
|
||||||
|
// File: UsersToNotify.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("UsersToNotify")]
|
||||||
|
public class UsersToNotify : Entity
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>PlexRequests.Store</RootNamespace>
|
<RootNamespace>PlexRequests.Store</RootNamespace>
|
||||||
<AssemblyName>PlexRequests.Store</AssemblyName>
|
<AssemblyName>PlexRequests.Store</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -31,25 +31,9 @@
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Dapper, Version=1.40.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\Dapper.1.42\lib\net45\Dapper.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Dapper.Contrib, Version=1.40.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\Dapper.Contrib.1.43\lib\net45\Dapper.Contrib.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Mono.Data.Sqlite">
|
<Reference Include="Mono.Data.Sqlite">
|
||||||
<HintPath>..\Assemblies\Mono.Data.Sqlite.dll</HintPath>
|
<HintPath>..\Assemblies\Mono.Data.Sqlite.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<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="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Windows.Forms" />
|
<Reference Include="System.Windows.Forms" />
|
||||||
|
@ -59,10 +43,24 @@
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="Dapper, Version=1.40.0.0, Culture=neutral, PublicKeyToken=null">
|
||||||
|
<HintPath>..\packages\Dapper.1.42\lib\net45\Dapper.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Dapper.Contrib, Version=1.40.0.0, Culture=neutral, PublicKeyToken=null">
|
||||||
|
<HintPath>..\packages\Dapper.Contrib.1.43\lib\net45\Dapper.Contrib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
|
||||||
|
<HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c">
|
||||||
|
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="DbConfiguration.cs" />
|
<Compile Include="DbConfiguration.cs" />
|
||||||
<Compile Include="Entity.cs" />
|
<Compile Include="Entity.cs" />
|
||||||
|
<Compile Include="Models\ScheduledJobs.cs" />
|
||||||
|
<Compile Include="Models\UsersToNotify.cs" />
|
||||||
<Compile Include="Repository\IRequestRepository.cs" />
|
<Compile Include="Repository\IRequestRepository.cs" />
|
||||||
<Compile Include="Repository\ISettingsRepository.cs" />
|
<Compile Include="Repository\ISettingsRepository.cs" />
|
||||||
<Compile Include="ISqliteConfiguration.cs" />
|
<Compile Include="ISqliteConfiguration.cs" />
|
||||||
|
@ -84,6 +82,7 @@
|
||||||
<DependentUpon>Sql.resx</DependentUpon>
|
<DependentUpon>Sql.resx</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="TableCreation.cs" />
|
<Compile Include="TableCreation.cs" />
|
||||||
|
<Compile Include="Models\Audit.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="sqlite3.dll">
|
<None Include="sqlite3.dll">
|
||||||
|
|
|
@ -61,13 +61,7 @@ namespace PlexRequests.Store
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public bool CanApprove
|
public bool CanApprove => !Approved && !Available;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return !Approved && !Available;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool UserHasRequested(string username)
|
public bool UserHasRequested(string username)
|
||||||
{
|
{
|
||||||
|
|
|
@ -42,8 +42,34 @@ CREATE TABLE IF NOT EXISTS Logs
|
||||||
);
|
);
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS Logs_Id ON Logs (Id);
|
CREATE UNIQUE INDEX IF NOT EXISTS Logs_Id ON Logs (Id);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS Audit
|
||||||
|
(
|
||||||
|
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
Date varchar(100) NOT NULL,
|
||||||
|
Username varchar(100) NOT NULL,
|
||||||
|
ChangeType varchar(100) NOT NULL,
|
||||||
|
OldValue varchar(100),
|
||||||
|
NewValue varchar(100)
|
||||||
|
);
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS Audit_Id ON Audit (Id);
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS DBInfo
|
CREATE TABLE IF NOT EXISTS DBInfo
|
||||||
(
|
(
|
||||||
SchemaVersion INTEGER
|
SchemaVersion INTEGER
|
||||||
|
);
|
||||||
|
|
||||||
);
|
CREATE TABLE IF NOT EXISTS ScheduledJobs
|
||||||
|
(
|
||||||
|
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
Name varchar(100) NOT NULL,
|
||||||
|
LastRun varchar(100) NOT NULL
|
||||||
|
);
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS ScheduledJobs_Id ON ScheduledJobs (Id);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS UsersToNotify
|
||||||
|
(
|
||||||
|
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
Username varchar(100) NOT NULL
|
||||||
|
);
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS UsersToNotify_Id ON UsersToNotify (Id);
|
|
@ -44,7 +44,18 @@ namespace PlexRequests.Store
|
||||||
connection.Close();
|
connection.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AlterTable(IDbConnection connection, string tableName, string alterType, string newColumn, bool isNullable, string dataType)
|
public static void DropTable(IDbConnection con, string tableName)
|
||||||
|
{
|
||||||
|
using (con)
|
||||||
|
{
|
||||||
|
con.Open();
|
||||||
|
var query = $"DROP TABLE IF EXISTS {tableName}";
|
||||||
|
con.Execute(query);
|
||||||
|
con.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddColumn(IDbConnection connection, string tableName, string alterType, string newColumn, bool isNullable, string dataType)
|
||||||
{
|
{
|
||||||
connection.Open();
|
connection.Open();
|
||||||
var result = connection.Query<TableInfo>($"PRAGMA table_info({tableName});");
|
var result = connection.Query<TableInfo>($"PRAGMA table_info({tableName});");
|
||||||
|
@ -83,7 +94,7 @@ namespace PlexRequests.Store
|
||||||
public static void CreateSchema(this IDbConnection con, int version)
|
public static void CreateSchema(this IDbConnection con, int version)
|
||||||
{
|
{
|
||||||
con.Open();
|
con.Open();
|
||||||
con.Query(string.Format("INSERT INTO DBInfo (SchemaVersion) values ({0})", version));
|
con.Query($"INSERT INTO DBInfo (SchemaVersion) values ({version})");
|
||||||
con.Close();
|
con.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +126,5 @@ namespace PlexRequests.Store
|
||||||
public string dflt_value { get; set; }
|
public string dflt_value { get; set; }
|
||||||
public int pk { get; set; }
|
public int pk { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="Dapper" version="1.42" targetFramework="net452" />
|
<package id="Dapper" version="1.42" targetFramework="net45" />
|
||||||
<package id="Dapper.Contrib" version="1.43" targetFramework="net46" />
|
<package id="Dapper.Contrib" version="1.43" targetFramework="net45" />
|
||||||
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net46" />
|
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net45" />
|
||||||
<package id="NLog" version="4.2.3" targetFramework="net452" />
|
<package id="NLog" version="4.2.3" targetFramework="net45" />
|
||||||
</packages>
|
</packages>
|
|
@ -25,6 +25,7 @@
|
||||||
// ************************************************************************/
|
// ************************************************************************/
|
||||||
#endregion
|
#endregion
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@ using Nancy;
|
||||||
using Nancy.Testing;
|
using Nancy.Testing;
|
||||||
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
@ -45,11 +47,11 @@ using PlexRequests.Store.Repository;
|
||||||
using PlexRequests.UI.Models;
|
using PlexRequests.UI.Models;
|
||||||
using PlexRequests.UI.Modules;
|
using PlexRequests.UI.Modules;
|
||||||
using PlexRequests.Helpers;
|
using PlexRequests.Helpers;
|
||||||
|
using PlexRequests.UI.Helpers;
|
||||||
|
|
||||||
namespace PlexRequests.UI.Tests
|
namespace PlexRequests.UI.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
[Ignore("Needs rework")]
|
|
||||||
public class AdminModuleTests
|
public class AdminModuleTests
|
||||||
{
|
{
|
||||||
private Mock<ISettingsService<PlexRequestSettings>> PlexRequestMock { get; set; }
|
private Mock<ISettingsService<PlexRequestSettings>> PlexRequestMock { get; set; }
|
||||||
|
@ -70,6 +72,7 @@ namespace PlexRequests.UI.Tests
|
||||||
private Mock<IRepository<LogEntity>> LogRepo { get; set; }
|
private Mock<IRepository<LogEntity>> LogRepo { get; set; }
|
||||||
private Mock<INotificationService> NotificationService { get; set; }
|
private Mock<INotificationService> NotificationService { get; set; }
|
||||||
private Mock<ICacheProvider> Cache { get; set; }
|
private Mock<ICacheProvider> Cache { get; set; }
|
||||||
|
private Mock<ISettingsService<LogSettings>> Log { get; set; }
|
||||||
|
|
||||||
private ConfigurableBootstrapper Bootstrapper { get; set; }
|
private ConfigurableBootstrapper Bootstrapper { get; set; }
|
||||||
|
|
||||||
|
@ -82,9 +85,10 @@ namespace PlexRequests.UI.Tests
|
||||||
|
|
||||||
PlexMock = new Mock<IPlexApi>();
|
PlexMock = new Mock<IPlexApi>();
|
||||||
PlexMock.Setup(x => x.SignIn("Username1", "Password1"))
|
PlexMock.Setup(x => x.SignIn("Username1", "Password1"))
|
||||||
.Returns(new PlexAuthentication { user = new User { authentication_token = "abc", username = "Username1" } });
|
.Returns(new PlexAuthentication { user = new User { authentication_token = "abc", title = "Username1" } });
|
||||||
|
|
||||||
PlexRequestMock = new Mock<ISettingsService<PlexRequestSettings>>();
|
PlexRequestMock = new Mock<ISettingsService<PlexRequestSettings>>();
|
||||||
|
PlexRequestMock.Setup(x => x.GetSettings()).Returns(new PlexRequestSettings());
|
||||||
CpMock = new Mock<ISettingsService<CouchPotatoSettings>>();
|
CpMock = new Mock<ISettingsService<CouchPotatoSettings>>();
|
||||||
PlexSettingsMock = new Mock<ISettingsService<PlexSettings>>();
|
PlexSettingsMock = new Mock<ISettingsService<PlexSettings>>();
|
||||||
SonarrApiMock = new Mock<ISonarrApi>();
|
SonarrApiMock = new Mock<ISonarrApi>();
|
||||||
|
@ -100,6 +104,7 @@ namespace PlexRequests.UI.Tests
|
||||||
NotificationService = new Mock<INotificationService>();
|
NotificationService = new Mock<INotificationService>();
|
||||||
HeadphonesSettings = new Mock<ISettingsService<HeadphonesSettings>>();
|
HeadphonesSettings = new Mock<ISettingsService<HeadphonesSettings>>();
|
||||||
Cache = new Mock<ICacheProvider>();
|
Cache = new Mock<ICacheProvider>();
|
||||||
|
Log = new Mock<ISettingsService<LogSettings>>();
|
||||||
|
|
||||||
Bootstrapper = new ConfigurableBootstrapper(with =>
|
Bootstrapper = new ConfigurableBootstrapper(with =>
|
||||||
{
|
{
|
||||||
|
@ -121,11 +126,12 @@ namespace PlexRequests.UI.Tests
|
||||||
with.Dependency(PushoverApi.Object);
|
with.Dependency(PushoverApi.Object);
|
||||||
with.Dependency(NotificationService.Object);
|
with.Dependency(NotificationService.Object);
|
||||||
with.Dependency(HeadphonesSettings.Object);
|
with.Dependency(HeadphonesSettings.Object);
|
||||||
with.Dependencies(Cache.Object);
|
with.Dependency(Cache.Object);
|
||||||
|
with.Dependency(Log.Object);
|
||||||
with.RootPathProvider<TestRootPathProvider>();
|
with.RootPathProvider<TestRootPathProvider>();
|
||||||
with.RequestStartup((container, pipelines, context) =>
|
with.RequestStartup((container, pipelines, context) =>
|
||||||
{
|
{
|
||||||
context.CurrentUser = new UserIdentity { UserName = "user" };
|
context.CurrentUser = new UserIdentity { UserName = "user", Claims = new List<string> {"Admin"} };
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -233,7 +239,7 @@ namespace PlexRequests.UI.Tests
|
||||||
[Test]
|
[Test]
|
||||||
public void GetUsersSuccessfully()
|
public void GetUsersSuccessfully()
|
||||||
{
|
{
|
||||||
var users = new PlexFriends { User = new[] { new UserFriends { Username = "abc2" }, } };
|
var users = new PlexFriends { User = new[] { new UserFriends { Title = "abc2" }, } };
|
||||||
PlexMock.Setup(x => x.GetUsers(It.IsAny<string>())).Returns(users);
|
PlexMock.Setup(x => x.GetUsers(It.IsAny<string>())).Returns(users);
|
||||||
var browser = new Browser(Bootstrapper);
|
var browser = new Browser(Bootstrapper);
|
||||||
|
|
||||||
|
@ -248,9 +254,11 @@ namespace PlexRequests.UI.Tests
|
||||||
|
|
||||||
Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode));
|
Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode));
|
||||||
|
|
||||||
var body = result.Body.AsString();
|
|
||||||
|
var body = JsonConvert.DeserializeObject<JObject>(result.Body.AsString());
|
||||||
|
var user = body["users"];
|
||||||
Assert.That(body, Is.Not.Null);
|
Assert.That(body, Is.Not.Null);
|
||||||
Assert.That(body, Contains.Substring("abc2"));
|
Assert.That(user.ToString().Contains("abc"), Is.True);
|
||||||
|
|
||||||
PlexMock.Verify(x => x.GetUsers(It.IsAny<string>()), Times.Once);
|
PlexMock.Verify(x => x.GetUsers(It.IsAny<string>()), Times.Once);
|
||||||
AuthMock.Verify(x => x.GetSettings(), Times.Once);
|
AuthMock.Verify(x => x.GetSettings(), Times.Once);
|
||||||
|
@ -269,6 +277,7 @@ namespace PlexRequests.UI.Tests
|
||||||
with.Header("Accept", "application/json");
|
with.Header("Accept", "application/json");
|
||||||
with.FormValue("username", "Username1");
|
with.FormValue("username", "Username1");
|
||||||
with.FormValue("password", "Password1");
|
with.FormValue("password", "Password1");
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -324,9 +333,10 @@ namespace PlexRequests.UI.Tests
|
||||||
|
|
||||||
Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode));
|
Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode));
|
||||||
|
|
||||||
var body = JsonConvert.DeserializeObject<string>(result.Body.AsString());
|
var body = JsonConvert.DeserializeObject<JObject>(result.Body.AsString());
|
||||||
|
var user = (string)body["users"];
|
||||||
Assert.That(body, Is.Not.Null);
|
Assert.That(body, Is.Not.Null);
|
||||||
Assert.That(string.IsNullOrWhiteSpace(body), Is.True);
|
Assert.That(string.IsNullOrWhiteSpace(user), Is.True);
|
||||||
|
|
||||||
PlexMock.Verify(x => x.GetUsers(It.IsAny<string>()), Times.Never);
|
PlexMock.Verify(x => x.GetUsers(It.IsAny<string>()), Times.Never);
|
||||||
AuthMock.Verify(x => x.GetSettings(), Times.Once);
|
AuthMock.Verify(x => x.GetSettings(), Times.Once);
|
||||||
|
|
778
PlexRequests.UI.Tests/ApiModuleTests.cs
Normal file
778
PlexRequests.UI.Tests/ApiModuleTests.cs
Normal file
|
@ -0,0 +1,778 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2016 Jamie Rees
|
||||||
|
// File: ApiModuleTests.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 System.Linq;
|
||||||
|
|
||||||
|
using FluentValidation;
|
||||||
|
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
using Nancy;
|
||||||
|
using Nancy.Testing;
|
||||||
|
using Nancy.Validation;
|
||||||
|
using Nancy.Validation.FluentValidation;
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NUnit.Framework.Constraints;
|
||||||
|
using PlexRequests.Core;
|
||||||
|
using PlexRequests.Core.SettingModels;
|
||||||
|
using PlexRequests.Store;
|
||||||
|
using PlexRequests.Store.Repository;
|
||||||
|
using PlexRequests.UI.Models;
|
||||||
|
using PlexRequests.UI.Modules;
|
||||||
|
using PlexRequests.UI.Validators;
|
||||||
|
|
||||||
|
using Ploeh.AutoFixture;
|
||||||
|
|
||||||
|
namespace PlexRequests.UI.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class ApiModuleTests
|
||||||
|
{
|
||||||
|
private ConfigurableBootstrapper Bootstrapper { get; set; }
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
var fixture = new Fixture();
|
||||||
|
var requests = fixture.CreateMany<RequestedModel>();
|
||||||
|
var requestMock = new Mock<IRequestService>();
|
||||||
|
var settingsMock = new Mock<ISettingsService<PlexRequestSettings>>();
|
||||||
|
var userRepoMock = new Mock<IRepository<UsersModel>>();
|
||||||
|
var mapperMock = new Mock<ICustomUserMapper>();
|
||||||
|
var authSettingsMock = new Mock<ISettingsService<AuthenticationSettings>>();
|
||||||
|
var plexSettingsMock = new Mock<ISettingsService<PlexSettings>>();
|
||||||
|
var cpMock = new Mock<ISettingsService<CouchPotatoSettings>>();
|
||||||
|
var sonarrMock = new Mock<ISettingsService<SonarrSettings>>();
|
||||||
|
var sickRageMock = new Mock<ISettingsService<SickRageSettings>>();
|
||||||
|
var headphonesMock = new Mock<ISettingsService<HeadphonesSettings>>();
|
||||||
|
|
||||||
|
var userModels = fixture.CreateMany<UsersModel>().ToList();
|
||||||
|
userModels.Add(new UsersModel
|
||||||
|
{
|
||||||
|
UserName = "user1"
|
||||||
|
});
|
||||||
|
|
||||||
|
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexRequestSettings { ApiKey = "api" });
|
||||||
|
requestMock.Setup(x => x.GetAll()).Returns(requests);
|
||||||
|
requestMock.Setup(x => x.Get(1)).Returns(requests.FirstOrDefault());
|
||||||
|
requestMock.Setup(x => x.Get(99)).Returns(new RequestedModel());
|
||||||
|
requestMock.Setup(x => x.DeleteRequest(It.IsAny<RequestedModel>()));
|
||||||
|
|
||||||
|
userRepoMock.Setup(x => x.GetAll()).Returns(userModels);
|
||||||
|
userRepoMock.Setup(x => x.Update(It.IsAny<UsersModel>())).Returns(true);
|
||||||
|
|
||||||
|
mapperMock.Setup(x => x.ValidateUser("user1", It.IsAny<string>())).Returns(Guid.NewGuid());
|
||||||
|
mapperMock.Setup(x => x.UpdatePassword("user1", "password", "newpassword")).Returns(true);
|
||||||
|
|
||||||
|
authSettingsMock.Setup(x => x.SaveSettings(It.Is<AuthenticationSettings>(c => c.PlexAuthToken.Equals("abc")))).Returns(true);
|
||||||
|
|
||||||
|
plexSettingsMock.Setup(x => x.GetSettings()).Returns(fixture.Create<PlexSettings>());
|
||||||
|
plexSettingsMock.Setup(x => x.SaveSettings(It.Is<PlexSettings>(c => c.Ip.Equals("192")))).Returns(true);
|
||||||
|
|
||||||
|
cpMock.Setup(x => x.GetSettings()).Returns(fixture.Create<CouchPotatoSettings>());
|
||||||
|
cpMock.Setup(x => x.SaveSettings(It.Is<CouchPotatoSettings>(c => c.Ip.Equals("192")))).Returns(true);
|
||||||
|
|
||||||
|
sonarrMock.Setup(x => x.GetSettings()).Returns(fixture.Create<SonarrSettings>());
|
||||||
|
sonarrMock.Setup(x => x.SaveSettings(It.Is<SonarrSettings>(c => c.Ip.Equals("192")))).Returns(true);
|
||||||
|
|
||||||
|
sickRageMock.Setup(x => x.GetSettings()).Returns(fixture.Create<SickRageSettings>());
|
||||||
|
sickRageMock.Setup(x => x.SaveSettings(It.Is<SickRageSettings>(c => c.Ip.Equals("192")))).Returns(true);
|
||||||
|
|
||||||
|
headphonesMock.Setup(x => x.GetSettings()).Returns(fixture.Create<HeadphonesSettings>());
|
||||||
|
headphonesMock.Setup(x => x.SaveSettings(It.Is<HeadphonesSettings>(c => c.Ip.Equals("192")))).Returns(true);
|
||||||
|
|
||||||
|
Bootstrapper = new ConfigurableBootstrapper(with =>
|
||||||
|
{
|
||||||
|
with.Module<ApiRequestModule>();
|
||||||
|
with.Module<ApiUserModule>();
|
||||||
|
with.Module<ApiSettingsModule>();
|
||||||
|
|
||||||
|
with.Dependency(requestMock.Object);
|
||||||
|
with.Dependency(settingsMock.Object);
|
||||||
|
with.Dependency(userRepoMock.Object);
|
||||||
|
with.Dependency(mapperMock.Object);
|
||||||
|
with.Dependency(headphonesMock.Object);
|
||||||
|
with.Dependency(authSettingsMock.Object);
|
||||||
|
with.Dependency(plexSettingsMock.Object);
|
||||||
|
with.Dependency(cpMock.Object);
|
||||||
|
with.Dependency(sonarrMock.Object);
|
||||||
|
with.Dependency(sickRageMock.Object);
|
||||||
|
|
||||||
|
|
||||||
|
with.RootPathProvider<TestRootPathProvider>();
|
||||||
|
with.ModelValidatorLocator(
|
||||||
|
new DefaultValidatorLocator(
|
||||||
|
new List<IModelValidatorFactory>
|
||||||
|
{
|
||||||
|
new FluentValidationValidatorFactory(
|
||||||
|
new DefaultFluentAdapterFactory(new List<IFluentAdapter>()),
|
||||||
|
new List<IValidator> { new RequestedModelValidator(), new UserViewModelValidator(), new PlexValidator() })
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Action<BrowserContext> GetBrowser()
|
||||||
|
{
|
||||||
|
return with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void InvalidApiKey()
|
||||||
|
{
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
|
||||||
|
var result = browser.Get("/api/requests", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "a");
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode));
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<List<RequestedModel>>>(result.Body.AsString());
|
||||||
|
Assert.That(body.Error, Is.True);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Not.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetAllRequests()
|
||||||
|
{
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
|
||||||
|
var result = browser.Get("/api/requests", GetBrowser());
|
||||||
|
Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode));
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<List<RequestedModel>>>(result.Body.AsString());
|
||||||
|
Assert.That(body.Data, Is.Not.Null);
|
||||||
|
Assert.That(body.Data.Count, Is.GreaterThan(0));
|
||||||
|
Assert.That(body.Error, Is.False);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Null.Or.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetSingleRequest()
|
||||||
|
{
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
|
||||||
|
var result = browser.Get("/api/requests/1", GetBrowser());
|
||||||
|
Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode));
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<List<RequestedModel>>>(result.Body.AsString());
|
||||||
|
Assert.That(body.Data, Is.Not.Null);
|
||||||
|
Assert.That(body.Data.Count, Is.EqualTo(1));
|
||||||
|
Assert.That(body.Error, Is.False);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Null.Or.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetSingleRequestThatDoesntExist()
|
||||||
|
{
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
|
||||||
|
var result = browser.Get("/api/requests/99", GetBrowser());
|
||||||
|
Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode));
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<List<RequestedModel>>>(result.Body.AsString());
|
||||||
|
Assert.That(body.Data, Is.Not.Null);
|
||||||
|
Assert.That(body.Data.Count, Is.EqualTo(0));
|
||||||
|
Assert.That(body.Error, Is.True);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Not.Null.Or.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void DeleteARequest()
|
||||||
|
{
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
|
||||||
|
var result = browser.Delete("/api/requests/1", GetBrowser());
|
||||||
|
Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode));
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<bool>>(result.Body.AsString());
|
||||||
|
Assert.That(body.Data, Is.True);
|
||||||
|
Assert.That(body.Error, Is.False);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Null.Or.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void DeleteARequestThatDoesNotExist()
|
||||||
|
{
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
|
||||||
|
var result = browser.Delete("/api/requests/99", GetBrowser());
|
||||||
|
Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode));
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<bool>>(result.Body.AsString());
|
||||||
|
Assert.That(body.Data, Is.False);
|
||||||
|
Assert.That(body.Error, Is.True);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Not.Null.Or.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void UpdateUsersPassword()
|
||||||
|
{
|
||||||
|
var model = new UserUpdateViewModel
|
||||||
|
{
|
||||||
|
CurrentPassword = "password",
|
||||||
|
NewPassword = "newpassword"
|
||||||
|
};
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Put("/api/credentials/user1", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
with.JsonBody(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode));
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<string>>(result.Body.AsString());
|
||||||
|
Assert.That(body.Data, Is.Not.Null.Or.Empty);
|
||||||
|
Assert.That(body.Error, Is.False);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Null.Or.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void UpdateInvalidUsersPassword()
|
||||||
|
{
|
||||||
|
var model = new UserUpdateViewModel
|
||||||
|
{
|
||||||
|
CurrentPassword = "password",
|
||||||
|
NewPassword = "newpassword"
|
||||||
|
};
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Put("/api/credentials/user99", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
with.JsonBody(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode));
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<string>>(result.Body.AsString());
|
||||||
|
Assert.That(body.Data, Is.Null.Or.Empty);
|
||||||
|
Assert.That(body.Error, Is.True);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Not.Null.Or.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void UpdateUsersInvalidPassword()
|
||||||
|
{
|
||||||
|
var model = new UserUpdateViewModel
|
||||||
|
{
|
||||||
|
CurrentPassword = "password",
|
||||||
|
NewPassword = "password2"
|
||||||
|
};
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Put("/api/credentials/user1", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
with.JsonBody(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode));
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<string>>(result.Body.AsString());
|
||||||
|
Assert.That(body.Data, Is.Null.Or.Empty);
|
||||||
|
Assert.That(body.Error, Is.True);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Not.Null.Or.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void UpdateUsersWithBadModel()
|
||||||
|
{
|
||||||
|
var model = new UserUpdateViewModel
|
||||||
|
{
|
||||||
|
CurrentPassword = null,
|
||||||
|
NewPassword = "password2"
|
||||||
|
};
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Put("/api/credentials/user1", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
with.JsonBody(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode));
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<string[]>>(result.Body.AsString());
|
||||||
|
Assert.That(body.Data.Length, Is.GreaterThan(0));
|
||||||
|
Assert.That(body.Error, Is.True);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Not.Null.Or.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetApiKey()
|
||||||
|
{
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Get("/api/apikey", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
with.Query("username", "user1");
|
||||||
|
with.Query("password", "password");
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode));
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<string>>(result.Body.AsString());
|
||||||
|
Assert.That(body.Data, Is.Not.Null.Or.Empty);
|
||||||
|
Assert.That(body.Error, Is.False);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Null.Or.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetApiKeyWithBadCredentials()
|
||||||
|
{
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Get("/api/apikey", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
with.Query("username", "user");
|
||||||
|
with.Query("password", "password");
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode));
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<string>>(result.Body.AsString());
|
||||||
|
Assert.That(body.Data, Is.Null.Or.Empty);
|
||||||
|
Assert.That(body.Error, Is.True);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Not.Null.Or.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SaveNewAuthSettings()
|
||||||
|
{
|
||||||
|
var model = new AuthenticationSettings
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
PlexAuthToken = "abc",
|
||||||
|
DeniedUsers = "abc",
|
||||||
|
UsePassword = false,
|
||||||
|
UserAuthentication = true
|
||||||
|
};
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Post("api/settings/authentication", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
with.JsonBody(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode));
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<string>>(result.Body.AsString());
|
||||||
|
Assert.That(body.Data, Is.Not.Null.Or.Empty);
|
||||||
|
Assert.That(body.Error, Is.False);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Null.Or.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetPlexSettings()
|
||||||
|
{
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Get("/api/settings/plex", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
});
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<PlexSettings>>(result.Body.AsString());
|
||||||
|
|
||||||
|
Assert.That(body.Data, Is.Not.Null);
|
||||||
|
Assert.That(body.Error, Is.False);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SavePlexSettings()
|
||||||
|
{
|
||||||
|
var model = new PlexSettings()
|
||||||
|
{
|
||||||
|
Port = 231,
|
||||||
|
Ip = "192",
|
||||||
|
Ssl = true,
|
||||||
|
};
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Post("/api/settings/plex", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
with.JsonBody(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<bool>>(result.Body.AsString());
|
||||||
|
|
||||||
|
Assert.That(body.Data, Is.EqualTo(true));
|
||||||
|
Assert.That(body.Error, Is.False);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Null);
|
||||||
|
}
|
||||||
|
[Test]
|
||||||
|
public void SaveBadPlexSettings()
|
||||||
|
{
|
||||||
|
var model = new PlexSettings
|
||||||
|
{
|
||||||
|
Ip = "q",
|
||||||
|
Ssl = true,
|
||||||
|
};
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Post("/api/settings/plex", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
with.JsonBody(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<bool>>(result.Body.AsString());
|
||||||
|
|
||||||
|
Assert.That(body.Data, Is.EqualTo(false));
|
||||||
|
Assert.That(body.Error, Is.True);
|
||||||
|
Assert.That(body.ErrorMessage, Is.EqualTo("Could not update the settings"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetCpSettings()
|
||||||
|
{
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Get("/api/settings/couchpotato", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
});
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<CouchPotatoSettings>>(result.Body.AsString());
|
||||||
|
|
||||||
|
Assert.That(body.Data, Is.Not.Null);
|
||||||
|
Assert.That(body.Error, Is.False);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SaveCpSettings()
|
||||||
|
{
|
||||||
|
var model = new CouchPotatoSettings
|
||||||
|
{
|
||||||
|
Port = 231,
|
||||||
|
Ip = "192",
|
||||||
|
Ssl = true,
|
||||||
|
};
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Post("/api/settings/couchpotato", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
with.JsonBody(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<bool>>(result.Body.AsString());
|
||||||
|
|
||||||
|
Assert.That(body.Data, Is.EqualTo(true));
|
||||||
|
Assert.That(body.Error, Is.False);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Null);
|
||||||
|
}
|
||||||
|
[Test]
|
||||||
|
public void SaveBadCpSettings()
|
||||||
|
{
|
||||||
|
var model = new CouchPotatoSettings
|
||||||
|
{
|
||||||
|
Ip = "q",
|
||||||
|
Ssl = true,
|
||||||
|
};
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Post("/api/settings/couchpotato", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
with.JsonBody(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<bool>>(result.Body.AsString());
|
||||||
|
|
||||||
|
Assert.That(body.Data, Is.EqualTo(false));
|
||||||
|
Assert.That(body.Error, Is.True);
|
||||||
|
Assert.That(body.ErrorMessage, Is.EqualTo("Could not update the settings"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetSonarrSettings()
|
||||||
|
{
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Get("/api/settings/sonarr", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
});
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<SonarrSettings>>(result.Body.AsString());
|
||||||
|
|
||||||
|
Assert.That(body.Data, Is.Not.Null);
|
||||||
|
Assert.That(body.Error, Is.False);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SaveSonarrSettings()
|
||||||
|
{
|
||||||
|
var model = new SonarrSettings
|
||||||
|
{
|
||||||
|
Port = 231,
|
||||||
|
Ip = "192",
|
||||||
|
Ssl = true,
|
||||||
|
};
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Post("/api/settings/sonarr", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
with.JsonBody(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<bool>>(result.Body.AsString());
|
||||||
|
|
||||||
|
Assert.That(body.Data, Is.EqualTo(true));
|
||||||
|
Assert.That(body.Error, Is.False);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Null);
|
||||||
|
}
|
||||||
|
[Test]
|
||||||
|
public void SaveBadSonarrSettings()
|
||||||
|
{
|
||||||
|
var model = new SonarrSettings
|
||||||
|
{
|
||||||
|
Ip = "q",
|
||||||
|
Ssl = true,
|
||||||
|
};
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Post("/api/settings/sonarr", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
with.JsonBody(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<bool>>(result.Body.AsString());
|
||||||
|
|
||||||
|
Assert.That(body.Data, Is.EqualTo(false));
|
||||||
|
Assert.That(body.Error, Is.True);
|
||||||
|
Assert.That(body.ErrorMessage, Is.EqualTo("Could not update the settings"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetSickRageSettings()
|
||||||
|
{
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Get("/api/settings/sickrage", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
});
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<SickRageSettings>>(result.Body.AsString());
|
||||||
|
|
||||||
|
Assert.That(body.Data, Is.Not.Null);
|
||||||
|
Assert.That(body.Error, Is.False);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SaveSickRageSettings()
|
||||||
|
{
|
||||||
|
var model = new SickRageSettings
|
||||||
|
{
|
||||||
|
Port = 231,
|
||||||
|
Ip = "192",
|
||||||
|
Ssl = true,
|
||||||
|
};
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Post("/api/settings/sickrage", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
with.JsonBody(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<bool>>(result.Body.AsString());
|
||||||
|
|
||||||
|
Assert.That(body.Data, Is.EqualTo(true));
|
||||||
|
Assert.That(body.Error, Is.False);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Null);
|
||||||
|
}
|
||||||
|
[Test]
|
||||||
|
public void SaveBadSickRageSettings()
|
||||||
|
{
|
||||||
|
var model = new SickRageSettings
|
||||||
|
{
|
||||||
|
Ip = "q",
|
||||||
|
Ssl = true,
|
||||||
|
};
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Post("/api/settings/sickrage", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
with.JsonBody(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<bool>>(result.Body.AsString());
|
||||||
|
|
||||||
|
Assert.That(body.Data, Is.EqualTo(false));
|
||||||
|
Assert.That(body.Error, Is.True);
|
||||||
|
Assert.That(body.ErrorMessage, Is.EqualTo("Could not update the settings"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetHeadphonesSettings()
|
||||||
|
{
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Get("/api/settings/headphones", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
});
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<HeadphonesSettings>>(result.Body.AsString());
|
||||||
|
|
||||||
|
Assert.That(body.Data, Is.Not.Null);
|
||||||
|
Assert.That(body.Error, Is.False);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SaveHeadphonesSettings()
|
||||||
|
{
|
||||||
|
var model = new HeadphonesSettings
|
||||||
|
{
|
||||||
|
Port = 231,
|
||||||
|
Ip = "192",
|
||||||
|
Ssl = true,
|
||||||
|
};
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Post("/api/settings/headphones", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
with.JsonBody(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<bool>>(result.Body.AsString());
|
||||||
|
|
||||||
|
Assert.That(body.Data, Is.EqualTo(true));
|
||||||
|
Assert.That(body.Error, Is.False);
|
||||||
|
Assert.That(body.ErrorMessage, Is.Null);
|
||||||
|
}
|
||||||
|
[Test]
|
||||||
|
public void SaveBadHeadphonesSettings()
|
||||||
|
{
|
||||||
|
var model = new HeadphonesSettings
|
||||||
|
{
|
||||||
|
Ip = "q",
|
||||||
|
Ssl = true,
|
||||||
|
};
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Post("/api/settings/headphones", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
with.JsonBody(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<bool>>(result.Body.AsString());
|
||||||
|
|
||||||
|
Assert.That(body.Data, Is.EqualTo(false));
|
||||||
|
Assert.That(body.Error, Is.True);
|
||||||
|
Assert.That(body.ErrorMessage, Is.EqualTo("Could not update the settings"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[TestCaseSource(nameof(AuthSettingsData))]
|
||||||
|
public object SaveNewAuthSettings(object model)
|
||||||
|
{
|
||||||
|
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
|
var result = browser.Post("api/settings/authentication", with =>
|
||||||
|
{
|
||||||
|
with.HttpRequest();
|
||||||
|
with.Header("Accept", "application/json");
|
||||||
|
with.Query("apikey", "api");
|
||||||
|
with.JsonBody(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode));
|
||||||
|
var body = JsonConvert.DeserializeObject<ApiModel<bool>>(result.Body.AsString());
|
||||||
|
|
||||||
|
var retVal = new List<string> { body.ErrorMessage, body.Error.ToString(), body.Data.ToString() };
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<TestCaseData> AuthSettingsData
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
yield return
|
||||||
|
new TestCaseData(new AuthenticationSettings { Id = 1, PlexAuthToken = "abc", DeniedUsers = "abc", UsePassword = false, UserAuthentication = true })
|
||||||
|
.Returns(new List<string> { null, false.ToString(), true.ToString() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
@ -46,6 +46,11 @@
|
||||||
<HintPath>..\packages\FluentScheduler.3.1.46\lib\net40\FluentScheduler.dll</HintPath>
|
<HintPath>..\packages\FluentScheduler.3.1.46\lib\net40\FluentScheduler.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="FluentValidation, Version=6.2.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\FluentValidation.6.2.1.0\lib\Net45\FluentValidation.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
<Reference Include="Moq, Version=4.2.1510.2205, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
|
<Reference Include="Moq, Version=4.2.1510.2205, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll</HintPath>
|
<HintPath>..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
|
@ -58,6 +63,10 @@
|
||||||
<HintPath>..\packages\Nancy.Testing.1.4.1\lib\net40\Nancy.Testing.dll</HintPath>
|
<HintPath>..\packages\Nancy.Testing.1.4.1\lib\net40\Nancy.Testing.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Nancy.Validation.FluentValidation, Version=1.4.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Nancy.Validation.FluentValidation.1.4.1\lib\net40\Nancy.Validation.FluentValidation.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="Nancy.ViewEngines.Razor, Version=1.4.2.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="Nancy.ViewEngines.Razor, Version=1.4.2.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Nancy.Viewengines.Razor.1.4.3\lib\net40\Nancy.ViewEngines.Razor.dll</HintPath>
|
<HintPath>..\packages\Nancy.Viewengines.Razor.1.4.3\lib\net40\Nancy.ViewEngines.Razor.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
|
@ -90,6 +99,7 @@
|
||||||
<Otherwise />
|
<Otherwise />
|
||||||
</Choose>
|
</Choose>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="ApiModuleTests.cs" />
|
||||||
<Compile Include="BootstrapperExtensions.cs" />
|
<Compile Include="BootstrapperExtensions.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="TestRootPathProvider.cs" />
|
<Compile Include="TestRootPathProvider.cs" />
|
||||||
|
|
|
@ -30,7 +30,6 @@ using Moq;
|
||||||
|
|
||||||
using Nancy;
|
using Nancy;
|
||||||
using Nancy.Testing;
|
using Nancy.Testing;
|
||||||
using Nancy.TinyIoc;
|
|
||||||
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
@ -40,18 +39,18 @@ using PlexRequests.Api.Interfaces;
|
||||||
using PlexRequests.Api.Models.Plex;
|
using PlexRequests.Api.Models.Plex;
|
||||||
using PlexRequests.Core;
|
using PlexRequests.Core;
|
||||||
using PlexRequests.Core.SettingModels;
|
using PlexRequests.Core.SettingModels;
|
||||||
using PlexRequests.UI.Helpers;
|
|
||||||
using PlexRequests.UI.Models;
|
using PlexRequests.UI.Models;
|
||||||
using PlexRequests.UI.Modules;
|
using PlexRequests.UI.Modules;
|
||||||
|
|
||||||
namespace PlexRequests.UI.Tests
|
namespace PlexRequests.UI.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
[Ignore("Needs some work")]
|
//[Ignore("Needs some work")]
|
||||||
public class UserLoginModuleTests
|
public class UserLoginModuleTests
|
||||||
{
|
{
|
||||||
private Mock<ISettingsService<AuthenticationSettings>> AuthMock { get; set; }
|
private Mock<ISettingsService<AuthenticationSettings>> AuthMock { get; set; }
|
||||||
private Mock<ISettingsService<PlexRequestSettings>> PlexRequestMock { get; set; }
|
private Mock<ISettingsService<PlexRequestSettings>> PlexRequestMock { get; set; }
|
||||||
|
private ConfigurableBootstrapper Bootstrapper { get; set; }
|
||||||
private Mock<IPlexApi> PlexMock { get; set; }
|
private Mock<IPlexApi> PlexMock { get; set; }
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
|
@ -60,6 +59,15 @@ namespace PlexRequests.UI.Tests
|
||||||
AuthMock = new Mock<ISettingsService<AuthenticationSettings>>();
|
AuthMock = new Mock<ISettingsService<AuthenticationSettings>>();
|
||||||
PlexMock = new Mock<IPlexApi>();
|
PlexMock = new Mock<IPlexApi>();
|
||||||
PlexRequestMock = new Mock<ISettingsService<PlexRequestSettings>>();
|
PlexRequestMock = new Mock<ISettingsService<PlexRequestSettings>>();
|
||||||
|
PlexRequestMock.Setup(x => x.GetSettings()).Returns(new PlexRequestSettings());
|
||||||
|
Bootstrapper = new ConfigurableBootstrapper(with =>
|
||||||
|
{
|
||||||
|
with.Module<UserLoginModule>();
|
||||||
|
with.Dependency(PlexRequestMock.Object);
|
||||||
|
with.Dependency(AuthMock.Object);
|
||||||
|
with.Dependency(PlexMock.Object);
|
||||||
|
with.RootPathProvider<TestRootPathProvider>();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -68,18 +76,11 @@ namespace PlexRequests.UI.Tests
|
||||||
var expectedSettings = new AuthenticationSettings { UserAuthentication = false, PlexAuthToken = "abc" };
|
var expectedSettings = new AuthenticationSettings { UserAuthentication = false, PlexAuthToken = "abc" };
|
||||||
AuthMock.Setup(x => x.GetSettings()).Returns(expectedSettings);
|
AuthMock.Setup(x => x.GetSettings()).Returns(expectedSettings);
|
||||||
|
|
||||||
var bootstrapper = new ConfigurableBootstrapper(with =>
|
|
||||||
{
|
|
||||||
with.Module<UserLoginModule>();
|
|
||||||
with.Dependency(AuthMock.Object);
|
|
||||||
with.Dependency(PlexMock.Object);
|
|
||||||
with.Dependency(PlexRequestMock.Object);
|
|
||||||
with.RootPathProvider<TestRootPathProvider>();
|
|
||||||
});
|
|
||||||
|
|
||||||
bootstrapper.WithSession(new Dictionary<string, object>());
|
|
||||||
|
|
||||||
var browser = new Browser(bootstrapper);
|
Bootstrapper.WithSession(new Dictionary<string, object>());
|
||||||
|
|
||||||
|
var browser = new Browser(Bootstrapper);
|
||||||
var result = browser.Post("/userlogin", with =>
|
var result = browser.Post("/userlogin", with =>
|
||||||
{
|
{
|
||||||
with.HttpRequest();
|
with.HttpRequest();
|
||||||
|
@ -103,17 +104,10 @@ namespace PlexRequests.UI.Tests
|
||||||
var expectedSettings = new AuthenticationSettings { UserAuthentication = false, PlexAuthToken = "abc" };
|
var expectedSettings = new AuthenticationSettings { UserAuthentication = false, PlexAuthToken = "abc" };
|
||||||
AuthMock.Setup(x => x.GetSettings()).Returns(expectedSettings);
|
AuthMock.Setup(x => x.GetSettings()).Returns(expectedSettings);
|
||||||
|
|
||||||
var bootstrapper = new ConfigurableBootstrapper(with =>
|
|
||||||
{
|
|
||||||
with.Module<UserLoginModule>();
|
|
||||||
with.Dependency(AuthMock.Object);
|
|
||||||
with.Dependency(PlexMock.Object);
|
|
||||||
with.RootPathProvider<TestRootPathProvider>();
|
|
||||||
});
|
|
||||||
|
|
||||||
bootstrapper.WithSession(new Dictionary<string, object>());
|
Bootstrapper.WithSession(new Dictionary<string, object>());
|
||||||
|
|
||||||
var browser = new Browser(bootstrapper);
|
var browser = new Browser(Bootstrapper);
|
||||||
var result = browser.Post("/userlogin", with =>
|
var result = browser.Post("/userlogin", with =>
|
||||||
{
|
{
|
||||||
with.HttpRequest();
|
with.HttpRequest();
|
||||||
|
@ -140,7 +134,7 @@ namespace PlexRequests.UI.Tests
|
||||||
{
|
{
|
||||||
new UserFriends
|
new UserFriends
|
||||||
{
|
{
|
||||||
Username = "abc",
|
Title = "abc",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -149,17 +143,9 @@ namespace PlexRequests.UI.Tests
|
||||||
PlexMock.Setup(x => x.GetUsers(It.IsAny<string>())).Returns(plexFriends);
|
PlexMock.Setup(x => x.GetUsers(It.IsAny<string>())).Returns(plexFriends);
|
||||||
PlexMock.Setup(x => x.GetAccount(It.IsAny<string>())).Returns(new PlexAccount());
|
PlexMock.Setup(x => x.GetAccount(It.IsAny<string>())).Returns(new PlexAccount());
|
||||||
|
|
||||||
var bootstrapper = new ConfigurableBootstrapper(with =>
|
Bootstrapper.WithSession(new Dictionary<string, object>());
|
||||||
{
|
|
||||||
with.Module<UserLoginModule>();
|
|
||||||
with.Dependency(AuthMock.Object);
|
|
||||||
with.Dependency(PlexMock.Object);
|
|
||||||
with.RootPathProvider<TestRootPathProvider>();
|
|
||||||
});
|
|
||||||
|
|
||||||
bootstrapper.WithSession(new Dictionary<string, object>());
|
var browser = new Browser(Bootstrapper);
|
||||||
|
|
||||||
var browser = new Browser(bootstrapper);
|
|
||||||
var result = browser.Post("/userlogin", with =>
|
var result = browser.Post("/userlogin", with =>
|
||||||
{
|
{
|
||||||
with.HttpRequest();
|
with.HttpRequest();
|
||||||
|
@ -196,17 +182,9 @@ namespace PlexRequests.UI.Tests
|
||||||
PlexMock.Setup(x => x.GetUsers(It.IsAny<string>())).Returns(plexFriends);
|
PlexMock.Setup(x => x.GetUsers(It.IsAny<string>())).Returns(plexFriends);
|
||||||
PlexMock.Setup(x => x.GetAccount(It.IsAny<string>())).Returns(new PlexAccount());
|
PlexMock.Setup(x => x.GetAccount(It.IsAny<string>())).Returns(new PlexAccount());
|
||||||
|
|
||||||
var bootstrapper = new ConfigurableBootstrapper(with =>
|
Bootstrapper.WithSession(new Dictionary<string, object>());
|
||||||
{
|
|
||||||
with.Module<UserLoginModule>();
|
|
||||||
with.Dependency(AuthMock.Object);
|
|
||||||
with.Dependency(PlexMock.Object);
|
|
||||||
with.RootPathProvider<TestRootPathProvider>();
|
|
||||||
});
|
|
||||||
|
|
||||||
bootstrapper.WithSession(new Dictionary<string, object>());
|
var browser = new Browser(Bootstrapper);
|
||||||
|
|
||||||
var browser = new Browser(bootstrapper);
|
|
||||||
|
|
||||||
var result = browser.Post("/userlogin", with =>
|
var result = browser.Post("/userlogin", with =>
|
||||||
{
|
{
|
||||||
|
@ -237,7 +215,7 @@ namespace PlexRequests.UI.Tests
|
||||||
{
|
{
|
||||||
new UserFriends
|
new UserFriends
|
||||||
{
|
{
|
||||||
Username = "abc",
|
Title = "abc",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -254,17 +232,9 @@ namespace PlexRequests.UI.Tests
|
||||||
PlexMock.Setup(x => x.SignIn(It.IsAny<string>(), It.IsAny<string>())).Returns(plexAuth);
|
PlexMock.Setup(x => x.SignIn(It.IsAny<string>(), It.IsAny<string>())).Returns(plexAuth);
|
||||||
PlexMock.Setup(x => x.GetAccount(It.IsAny<string>())).Returns(new PlexAccount());
|
PlexMock.Setup(x => x.GetAccount(It.IsAny<string>())).Returns(new PlexAccount());
|
||||||
|
|
||||||
var bootstrapper = new ConfigurableBootstrapper(with =>
|
Bootstrapper.WithSession(new Dictionary<string, object>());
|
||||||
{
|
|
||||||
with.Module<UserLoginModule>();
|
|
||||||
with.Dependency(AuthMock.Object);
|
|
||||||
with.Dependency(PlexMock.Object);
|
|
||||||
with.RootPathProvider<TestRootPathProvider>();
|
|
||||||
});
|
|
||||||
|
|
||||||
bootstrapper.WithSession(new Dictionary<string, object>());
|
var browser = new Browser(Bootstrapper);
|
||||||
|
|
||||||
var browser = new Browser(bootstrapper);
|
|
||||||
var result = browser.Post("/userlogin", with =>
|
var result = browser.Post("/userlogin", with =>
|
||||||
{
|
{
|
||||||
with.HttpRequest();
|
with.HttpRequest();
|
||||||
|
@ -307,17 +277,10 @@ namespace PlexRequests.UI.Tests
|
||||||
PlexMock.Setup(x => x.GetUsers(It.IsAny<string>())).Returns(plexFriends);
|
PlexMock.Setup(x => x.GetUsers(It.IsAny<string>())).Returns(plexFriends);
|
||||||
PlexMock.Setup(x => x.SignIn(It.IsAny<string>(), It.IsAny<string>())).Returns(plexAuth);
|
PlexMock.Setup(x => x.SignIn(It.IsAny<string>(), It.IsAny<string>())).Returns(plexAuth);
|
||||||
|
|
||||||
var bootstrapper = new ConfigurableBootstrapper(with =>
|
|
||||||
{
|
Bootstrapper.WithSession(new Dictionary<string, object>());
|
||||||
with.Module<UserLoginModule>();
|
|
||||||
with.Dependency(AuthMock.Object);
|
|
||||||
with.Dependency(PlexMock.Object);
|
|
||||||
with.RootPathProvider<TestRootPathProvider>();
|
|
||||||
});
|
|
||||||
|
|
||||||
bootstrapper.WithSession(new Dictionary<string, object>());
|
var browser = new Browser(Bootstrapper);
|
||||||
|
|
||||||
var browser = new Browser(bootstrapper);
|
|
||||||
var result = browser.Post("/userlogin", with =>
|
var result = browser.Post("/userlogin", with =>
|
||||||
{
|
{
|
||||||
with.HttpRequest();
|
with.HttpRequest();
|
||||||
|
@ -344,17 +307,9 @@ namespace PlexRequests.UI.Tests
|
||||||
var expectedSettings = new AuthenticationSettings { UserAuthentication = false, DeniedUsers = "abc", PlexAuthToken = "abc" };
|
var expectedSettings = new AuthenticationSettings { UserAuthentication = false, DeniedUsers = "abc", PlexAuthToken = "abc" };
|
||||||
AuthMock.Setup(x => x.GetSettings()).Returns(expectedSettings);
|
AuthMock.Setup(x => x.GetSettings()).Returns(expectedSettings);
|
||||||
|
|
||||||
var bootstrapper = new ConfigurableBootstrapper(with =>
|
Bootstrapper.WithSession(new Dictionary<string, object>());
|
||||||
{
|
|
||||||
with.Module<UserLoginModule>();
|
|
||||||
with.Dependency(AuthMock.Object);
|
|
||||||
with.Dependency(PlexMock.Object);
|
|
||||||
with.RootPathProvider<TestRootPathProvider>();
|
|
||||||
});
|
|
||||||
|
|
||||||
bootstrapper.WithSession(new Dictionary<string, object>());
|
var browser = new Browser(Bootstrapper);
|
||||||
|
|
||||||
var browser = new Browser(bootstrapper);
|
|
||||||
var result = browser.Post("/userlogin", with =>
|
var result = browser.Post("/userlogin", with =>
|
||||||
{
|
{
|
||||||
with.HttpRequest();
|
with.HttpRequest();
|
||||||
|
@ -376,17 +331,9 @@ namespace PlexRequests.UI.Tests
|
||||||
[Test]
|
[Test]
|
||||||
public void Logout()
|
public void Logout()
|
||||||
{
|
{
|
||||||
var bootstrapper = new ConfigurableBootstrapper(with =>
|
Bootstrapper.WithSession(new Dictionary<string, object> { { SessionKeys.UsernameKey, "abc" } });
|
||||||
{
|
|
||||||
with.Module<UserLoginModule>();
|
|
||||||
with.Dependency(AuthMock.Object);
|
|
||||||
with.Dependency(PlexMock.Object);
|
|
||||||
with.RootPathProvider<TestRootPathProvider>();
|
|
||||||
});
|
|
||||||
|
|
||||||
bootstrapper.WithSession(new Dictionary<string, object> { { SessionKeys.UsernameKey, "abc" } });
|
var browser = new Browser(Bootstrapper);
|
||||||
|
|
||||||
var browser = new Browser(bootstrapper);
|
|
||||||
var result = browser.Get("/userlogin/logout", with =>
|
var result = browser.Get("/userlogin/logout", with =>
|
||||||
{
|
{
|
||||||
with.HttpRequest();
|
with.HttpRequest();
|
||||||
|
@ -415,17 +362,9 @@ namespace PlexRequests.UI.Tests
|
||||||
PlexMock.Setup(x => x.GetAccount(It.IsAny<string>())).Returns(account);
|
PlexMock.Setup(x => x.GetAccount(It.IsAny<string>())).Returns(account);
|
||||||
PlexMock.Setup(x => x.SignIn(It.IsAny<string>(), It.IsAny<string>())).Returns(new PlexAuthentication { user = new User { username = "Jamie" } });
|
PlexMock.Setup(x => x.SignIn(It.IsAny<string>(), It.IsAny<string>())).Returns(new PlexAuthentication { user = new User { username = "Jamie" } });
|
||||||
|
|
||||||
var bootstrapper = new ConfigurableBootstrapper(with =>
|
Bootstrapper.WithSession(new Dictionary<string, object>());
|
||||||
{
|
|
||||||
with.Module<UserLoginModule>();
|
|
||||||
with.Dependency(AuthMock.Object);
|
|
||||||
with.Dependency(PlexMock.Object);
|
|
||||||
with.RootPathProvider<TestRootPathProvider>();
|
|
||||||
});
|
|
||||||
|
|
||||||
bootstrapper.WithSession(new Dictionary<string, object>());
|
var browser = new Browser(Bootstrapper);
|
||||||
|
|
||||||
var browser = new Browser(bootstrapper);
|
|
||||||
var result = browser.Post("/userlogin", with =>
|
var result = browser.Post("/userlogin", with =>
|
||||||
{
|
{
|
||||||
with.HttpRequest();
|
with.HttpRequest();
|
||||||
|
@ -470,17 +409,9 @@ namespace PlexRequests.UI.Tests
|
||||||
PlexMock.Setup(x => x.SignIn(It.IsAny<string>(), It.IsAny<string>())).Returns(plexAuth);
|
PlexMock.Setup(x => x.SignIn(It.IsAny<string>(), It.IsAny<string>())).Returns(plexAuth);
|
||||||
PlexMock.Setup(x => x.GetAccount(It.IsAny<string>())).Returns(account);
|
PlexMock.Setup(x => x.GetAccount(It.IsAny<string>())).Returns(account);
|
||||||
|
|
||||||
var bootstrapper = new ConfigurableBootstrapper(with =>
|
Bootstrapper.WithSession(new Dictionary<string, object>());
|
||||||
{
|
|
||||||
with.Module<UserLoginModule>();
|
|
||||||
with.Dependency(AuthMock.Object);
|
|
||||||
with.Dependency(PlexMock.Object);
|
|
||||||
with.RootPathProvider<TestRootPathProvider>();
|
|
||||||
});
|
|
||||||
|
|
||||||
bootstrapper.WithSession(new Dictionary<string, object>());
|
var browser = new Browser(Bootstrapper);
|
||||||
|
|
||||||
var browser = new Browser(bootstrapper);
|
|
||||||
var result = browser.Post("/userlogin", with =>
|
var result = browser.Post("/userlogin", with =>
|
||||||
{
|
{
|
||||||
with.HttpRequest();
|
with.HttpRequest();
|
||||||
|
|
|
@ -3,10 +3,12 @@
|
||||||
<package id="AutoFixture" version="3.40.0" targetFramework="net452" />
|
<package id="AutoFixture" version="3.40.0" targetFramework="net452" />
|
||||||
<package id="CsQuery" version="1.3.3" targetFramework="net46" />
|
<package id="CsQuery" version="1.3.3" targetFramework="net46" />
|
||||||
<package id="FluentScheduler" version="3.1.46" targetFramework="net46" />
|
<package id="FluentScheduler" version="3.1.46" targetFramework="net46" />
|
||||||
|
<package id="FluentValidation" version="6.2.1.0" targetFramework="net46" />
|
||||||
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net46" />
|
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net46" />
|
||||||
<package id="Moq" version="4.2.1510.2205" targetFramework="net452" />
|
<package id="Moq" version="4.2.1510.2205" targetFramework="net452" />
|
||||||
<package id="Nancy" version="1.4.3" targetFramework="net46" />
|
<package id="Nancy" version="1.4.3" targetFramework="net46" />
|
||||||
<package id="Nancy.Testing" version="1.4.1" targetFramework="net46" />
|
<package id="Nancy.Testing" version="1.4.1" targetFramework="net46" />
|
||||||
|
<package id="Nancy.Validation.FluentValidation" version="1.4.1" targetFramework="net46" />
|
||||||
<package id="Nancy.Viewengines.Razor" version="1.4.3" targetFramework="net46" />
|
<package id="Nancy.Viewengines.Razor" version="1.4.3" targetFramework="net46" />
|
||||||
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net46" />
|
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net46" />
|
||||||
<package id="NUnit" version="3.2.0" targetFramework="net46" />
|
<package id="NUnit" version="3.2.0" targetFramework="net46" />
|
||||||
|
|
|
@ -50,6 +50,12 @@ using PlexRequests.Store;
|
||||||
using PlexRequests.Store.Models;
|
using PlexRequests.Store.Models;
|
||||||
using PlexRequests.Store.Repository;
|
using PlexRequests.Store.Repository;
|
||||||
using PlexRequests.UI.Helpers;
|
using PlexRequests.UI.Helpers;
|
||||||
|
using Nancy.Json;
|
||||||
|
|
||||||
|
using PlexRequests.Services.Jobs;
|
||||||
|
using PlexRequests.UI.Jobs;
|
||||||
|
|
||||||
|
using Quartz.Spi;
|
||||||
|
|
||||||
namespace PlexRequests.UI
|
namespace PlexRequests.UI
|
||||||
{
|
{
|
||||||
|
@ -62,8 +68,7 @@ namespace PlexRequests.UI
|
||||||
|
|
||||||
protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
|
protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
|
||||||
{
|
{
|
||||||
container.Register<IUserMapper, UserMapper>();
|
|
||||||
container.Register<ISqliteConfiguration, DbConfiguration>(new DbConfiguration(new SqliteFactory()));
|
|
||||||
container.Register<ICacheProvider, MemoryCacheProvider>().AsSingleton();
|
container.Register<ICacheProvider, MemoryCacheProvider>().AsSingleton();
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
|
@ -77,19 +82,23 @@ namespace PlexRequests.UI
|
||||||
container.Register<ISettingsService<PushbulletNotificationSettings>, SettingsServiceV2<PushbulletNotificationSettings>>();
|
container.Register<ISettingsService<PushbulletNotificationSettings>, SettingsServiceV2<PushbulletNotificationSettings>>();
|
||||||
container.Register<ISettingsService<PushoverNotificationSettings>, SettingsServiceV2<PushoverNotificationSettings>>();
|
container.Register<ISettingsService<PushoverNotificationSettings>, SettingsServiceV2<PushoverNotificationSettings>>();
|
||||||
container.Register<ISettingsService<HeadphonesSettings>, SettingsServiceV2<HeadphonesSettings>>();
|
container.Register<ISettingsService<HeadphonesSettings>, SettingsServiceV2<HeadphonesSettings>>();
|
||||||
|
container.Register<ISettingsService<LogSettings>, SettingsServiceV2<LogSettings>>();
|
||||||
|
container.Register<ISettingsService<SlackNotificationSettings>, SettingsServiceV2<SlackNotificationSettings>>();
|
||||||
|
|
||||||
// Repo's
|
// Repo's
|
||||||
container.Register<IRepository<LogEntity>, GenericRepository<LogEntity>>();
|
container.Register<IRepository<LogEntity>, GenericRepository<LogEntity>>();
|
||||||
|
container.Register<IRepository<UsersToNotify>, GenericRepository<UsersToNotify>>();
|
||||||
|
container.Register<IRepository<ScheduledJobs>, GenericRepository<ScheduledJobs>>();
|
||||||
container.Register<IRequestService, JsonRequestService>();
|
container.Register<IRequestService, JsonRequestService>();
|
||||||
container.Register<ISettingsRepository, SettingsJsonRepository>();
|
container.Register<ISettingsRepository, SettingsJsonRepository>();
|
||||||
|
container.Register<IJobRecord, JobRecord>();
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
container.Register<IAvailabilityChecker, PlexAvailabilityChecker>();
|
container.Register<IAvailabilityChecker, PlexAvailabilityChecker>();
|
||||||
container.Register<ICouchPotatoCacher, CouchPotatoCacher>();
|
container.Register<ICouchPotatoCacher, CouchPotatoCacher>();
|
||||||
container.Register<ISonarrCacher, SonarrCacher>();
|
container.Register<ISonarrCacher, SonarrCacher>();
|
||||||
container.Register<ISickRageCacher, SickRageCacher>();
|
container.Register<ISickRageCacher, SickRageCacher>();
|
||||||
container.Register<IConfigurationReader, ConfigurationReader>();
|
container.Register<IJobFactory, CustomJobFactory>();
|
||||||
container.Register<IIntervals, UpdateInterval>();
|
|
||||||
|
|
||||||
// Api's
|
// Api's
|
||||||
container.Register<ICouchPotatoApi, CouchPotatoApi>();
|
container.Register<ICouchPotatoApi, CouchPotatoApi>();
|
||||||
|
@ -100,18 +109,28 @@ namespace PlexRequests.UI
|
||||||
container.Register<IPlexApi, PlexApi>();
|
container.Register<IPlexApi, PlexApi>();
|
||||||
container.Register<IMusicBrainzApi, MusicBrainzApi>();
|
container.Register<IMusicBrainzApi, MusicBrainzApi>();
|
||||||
container.Register<IHeadphonesApi, HeadphonesApi>();
|
container.Register<IHeadphonesApi, HeadphonesApi>();
|
||||||
|
container.Register<ISlackApi, SlackApi>();
|
||||||
|
|
||||||
// NotificationService
|
// NotificationService
|
||||||
container.Register<INotificationService, NotificationService>().AsSingleton();
|
container.Register<INotificationService, NotificationService>().AsSingleton();
|
||||||
|
|
||||||
|
JsonSettings.MaxJsonLength = int.MaxValue;
|
||||||
|
|
||||||
SubscribeAllObservers(container);
|
SubscribeAllObservers(container);
|
||||||
base.ConfigureRequestContainer(container, context);
|
base.ConfigureRequestContainer(container, context);
|
||||||
var loc = ServiceLocator.Instance;
|
var loc = ServiceLocator.Instance;
|
||||||
loc.SetContainer(container);
|
loc.SetContainer(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
|
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
|
||||||
{
|
{
|
||||||
|
container.Register<ISqliteConfiguration, DbConfiguration>(new DbConfiguration(new SqliteFactory()));
|
||||||
|
container.Register<IRepository<UsersModel>, UserRepository<UsersModel>>();
|
||||||
|
container.Register<IUserMapper, UserMapper>();
|
||||||
|
container.Register<ICustomUserMapper, UserMapper>();
|
||||||
|
|
||||||
|
|
||||||
CookieBasedSessions.Enable(pipelines, CryptographyConfiguration.Default);
|
CookieBasedSessions.Enable(pipelines, CryptographyConfiguration.Default);
|
||||||
|
|
||||||
StaticConfiguration.DisableErrorTraces = false;
|
StaticConfiguration.DisableErrorTraces = false;
|
||||||
|
@ -146,6 +165,8 @@ namespace PlexRequests.UI
|
||||||
nancyConventions.StaticContentsConventions.Add(
|
nancyConventions.StaticContentsConventions.Add(
|
||||||
StaticContentConventionBuilder.AddDirectory($"{assetLocation}/Content", "Content")
|
StaticContentConventionBuilder.AddDirectory($"{assetLocation}/Content", "Content")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
nancyConventions.StaticContentsConventions.AddDirectory($"{assetLocation}/docs", "swagger-ui");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override DiagnosticsConfiguration DiagnosticsConfiguration => new DiagnosticsConfiguration { Password = @"password" };
|
protected override DiagnosticsConfiguration DiagnosticsConfiguration => new DiagnosticsConfiguration { Password = @"password" };
|
||||||
|
@ -174,6 +195,26 @@ namespace PlexRequests.UI
|
||||||
{
|
{
|
||||||
notificationService.Subscribe(new PushoverNotification(container.Resolve<IPushoverApi>(), pushoverService));
|
notificationService.Subscribe(new PushoverNotification(container.Resolve<IPushoverApi>(), pushoverService));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var slackService = container.Resolve<ISettingsService<SlackNotificationSettings>>();
|
||||||
|
var slackSettings = slackService.GetSettings();
|
||||||
|
if (slackSettings.Enabled)
|
||||||
|
{
|
||||||
|
notificationService.Subscribe(new SlackNotification(container.Resolve<ISlackApi>(), slackService));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context)
|
||||||
|
{
|
||||||
|
//CORS Enable
|
||||||
|
pipelines.AfterRequest.AddItemToEndOfPipeline((ctx) =>
|
||||||
|
{
|
||||||
|
ctx.Response.WithHeader("Access-Control-Allow-Origin", "*")
|
||||||
|
.WithHeader("Access-Control-Allow-Methods", "POST,GET")
|
||||||
|
.WithHeader("Access-Control-Allow-Headers", "Accept, Origin, Content-type");
|
||||||
|
|
||||||
|
});
|
||||||
|
base.RequestStartup(container, pipelines, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -42,11 +42,28 @@ label {
|
||||||
margin-bottom: 0.5rem !important;
|
margin-bottom: 0.5rem !important;
|
||||||
font-size: 16px !important; }
|
font-size: 16px !important; }
|
||||||
|
|
||||||
|
.nav-tabs > li {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 21px; }
|
||||||
|
|
||||||
.nav-tabs > li.active > a,
|
.nav-tabs > li.active > a,
|
||||||
.nav-tabs > li.active > a:hover,
|
.nav-tabs > li.active > a:hover,
|
||||||
.nav-tabs > li.active > a:focus {
|
.nav-tabs > li.active > a:focus {
|
||||||
background: #4e5d6c; }
|
background: #4e5d6c; }
|
||||||
|
|
||||||
|
.nav-tabs > li > a > .fa {
|
||||||
|
padding: 3px 5px 3px 3px; }
|
||||||
|
|
||||||
|
.nav-tabs > li.nav-tab-right {
|
||||||
|
float: right; }
|
||||||
|
|
||||||
|
.nav-tabs > li.nav-tab-right a {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-left: 2px; }
|
||||||
|
|
||||||
|
.nav-tabs > li.nav-tab-icononly .fa {
|
||||||
|
padding: 3px; }
|
||||||
|
|
||||||
.navbar .nav a .fa,
|
.navbar .nav a .fa,
|
||||||
.dropdown-menu a .fa {
|
.dropdown-menu a .fa {
|
||||||
font-size: 130%;
|
font-size: 130%;
|
||||||
|
@ -220,3 +237,47 @@ label {
|
||||||
border-radius: 0 0.25rem 0.25rem 0 !important;
|
border-radius: 0 0.25rem 0.25rem 0 !important;
|
||||||
padding: 12px 8px; }
|
padding: 12px 8px; }
|
||||||
|
|
||||||
|
#updateAvailable {
|
||||||
|
background-color: #ffa400;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 15px; }
|
||||||
|
|
||||||
|
.checkbox label {
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 25px;
|
||||||
|
margin-right: 15px;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-bottom: 10px; }
|
||||||
|
|
||||||
|
.checkbox label:before {
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
margin-right: 10px;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 1px;
|
||||||
|
border: 2px solid #eee;
|
||||||
|
border-radius: 3px; }
|
||||||
|
|
||||||
|
.checkbox input[type=checkbox] {
|
||||||
|
display: none; }
|
||||||
|
|
||||||
|
.checkbox input[type=checkbox]:checked + label:before {
|
||||||
|
content: "\2713";
|
||||||
|
font-size: 13px;
|
||||||
|
color: #fafafa;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 13px; }
|
||||||
|
|
||||||
|
.input-group-sm {
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-bottom: 2px; }
|
||||||
|
|
||||||
|
.tab-pane .form-horizontal .form-group {
|
||||||
|
margin-right: 15px;
|
||||||
|
margin-left: 15px; }
|
||||||
|
|
1
PlexRequests.UI/Content/Themes/OriginalBootstrapCustom.min.css
vendored
Normal file
1
PlexRequests.UI/Content/Themes/OriginalBootstrapCustom.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,282 +1,349 @@
|
||||||
$form-color: #4e5d6c;
|
$form-color: #4e5d6c;
|
||||||
$form-color-lighter: #637689;
|
$form-color-lighter: #637689;
|
||||||
$primary-colour: #df691a;
|
$primary-colour: #df691a;
|
||||||
$primary-colour-outline: #ff761b;
|
$primary-colour-outline: #ff761b;
|
||||||
$info-colour: #5bc0de;
|
$info-colour: #5bc0de;
|
||||||
$warning-colour: #f0ad4e;
|
$warning-colour: #f0ad4e;
|
||||||
$danger-colour: #d9534f;
|
$danger-colour: #d9534f;
|
||||||
$success-colour: #5cb85c;
|
$success-colour: #5cb85c;
|
||||||
$i:
|
$i:
|
||||||
!important
|
!important;
|
||||||
;
|
|
||||||
|
@media (min-width: 768px ) {
|
||||||
@media (min-width: 768px ) {
|
.row {
|
||||||
.row {
|
position: relative;
|
||||||
position: relative;
|
}
|
||||||
}
|
|
||||||
|
.bottom-align-text {
|
||||||
.bottom-align-text {
|
position: absolute;
|
||||||
position: absolute;
|
bottom: 0;
|
||||||
bottom: 0;
|
right: 0;
|
||||||
right: 0;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@media (max-width: 48em) {
|
||||||
@media (max-width: 48em) {
|
.home {
|
||||||
.home {
|
padding-top: 1rem;
|
||||||
padding-top: 1rem;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@media (min-width: 48em) {
|
||||||
@media (min-width: 48em) {
|
.home {
|
||||||
.home {
|
padding-top: 4rem;
|
||||||
padding-top: 4rem;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
.btn {
|
||||||
.btn {
|
border-radius: .25rem $i;
|
||||||
border-radius: .25rem $i;
|
}
|
||||||
}
|
|
||||||
|
.multiSelect {
|
||||||
.multiSelect {
|
background-color: $form-color;
|
||||||
background-color: $form-color;
|
}
|
||||||
}
|
|
||||||
|
.form-control-custom {
|
||||||
.form-control-custom {
|
background-color: $form-color $i;
|
||||||
background-color: $form-color $i;
|
color: white $i;
|
||||||
color: white $i;
|
border-radius: 0;
|
||||||
border-radius: 0;
|
box-shadow: 0 0 0 !important;
|
||||||
box-shadow: 0 0 0 !important;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
h1 {
|
||||||
h1 {
|
font-size: 3.5rem $i;
|
||||||
font-size: 3.5rem $i;
|
font-weight: 600 $i;
|
||||||
font-weight: 600 $i;
|
}
|
||||||
}
|
|
||||||
|
.request-title {
|
||||||
.request-title {
|
margin-top: 0 $i;
|
||||||
margin-top: 0 $i;
|
font-size: 1.9rem $i;
|
||||||
font-size: 1.9rem $i;
|
}
|
||||||
}
|
|
||||||
|
p {
|
||||||
p {
|
font-size: 1.1rem $i;
|
||||||
font-size: 1.1rem $i;
|
}
|
||||||
}
|
|
||||||
|
label {
|
||||||
label {
|
display: inline-block $i;
|
||||||
display: inline-block $i;
|
margin-bottom: .5rem $i;
|
||||||
margin-bottom: .5rem $i;
|
font-size: 16px $i;
|
||||||
font-size: 16px $i;
|
}
|
||||||
}
|
|
||||||
|
.nav-tabs > li {
|
||||||
.nav-tabs > li.active > a,
|
font-size: 13px;
|
||||||
.nav-tabs > li.active > a:hover,
|
line-height: 21px;
|
||||||
.nav-tabs > li.active > a:focus {
|
}
|
||||||
background: #4e5d6c;
|
|
||||||
}
|
.nav-tabs > li.active > a,
|
||||||
|
.nav-tabs > li.active > a:hover,
|
||||||
.navbar .nav a .fa,
|
.nav-tabs > li.active > a:focus {
|
||||||
.dropdown-menu a .fa {
|
background: #4e5d6c;
|
||||||
font-size: 130%;
|
}
|
||||||
top: 1px;
|
|
||||||
position: relative;
|
.nav-tabs > li > a > .fa {
|
||||||
display: inline-block;
|
padding: 3px 5px 3px 3px;
|
||||||
margin-right: 5px;
|
}
|
||||||
}
|
|
||||||
|
.nav-tabs > li.nav-tab-right {
|
||||||
.dropdown-menu a .fa {
|
float: right;
|
||||||
top: 2px;
|
}
|
||||||
}
|
|
||||||
|
.nav-tabs > li.nav-tab-right a {
|
||||||
.btn-danger-outline {
|
margin-right: 0;
|
||||||
color: $danger-colour $i;
|
margin-left: 2px;
|
||||||
background-color: transparent;
|
}
|
||||||
background-image: none;
|
|
||||||
border-color: $danger-colour $i;
|
.nav-tabs > li.nav-tab-icononly .fa {
|
||||||
}
|
padding: 3px;
|
||||||
|
}
|
||||||
.btn-danger-outline:focus,
|
|
||||||
.btn-danger-outline.focus,
|
.navbar .nav a .fa,
|
||||||
.btn-danger-outline:active,
|
.dropdown-menu a .fa {
|
||||||
.btn-danger-outline.active,
|
font-size: 130%;
|
||||||
.btn-danger-outline:hover,
|
top: 1px;
|
||||||
.open > .btn-danger-outline.dropdown-toggle {
|
position: relative;
|
||||||
color: #fff $i;
|
display: inline-block;
|
||||||
background-color: $danger-colour $i;
|
margin-right: 5px;
|
||||||
border-color: $danger-colour $i;
|
}
|
||||||
}
|
|
||||||
|
.dropdown-menu a .fa {
|
||||||
|
top: 2px;
|
||||||
.btn-primary-outline {
|
}
|
||||||
color: $primary-colour-outline $i;
|
|
||||||
background-color: transparent;
|
.btn-danger-outline {
|
||||||
background-image: none;
|
color: $danger-colour $i;
|
||||||
border-color: $primary-colour-outline $i;
|
background-color: transparent;
|
||||||
}
|
background-image: none;
|
||||||
|
border-color: $danger-colour $i;
|
||||||
.btn-primary-outline:focus,
|
}
|
||||||
.btn-primary-outline.focus,
|
|
||||||
.btn-primary-outline:active,
|
.btn-danger-outline:focus,
|
||||||
.btn-primary-outline.active,
|
.btn-danger-outline.focus,
|
||||||
.btn-primary-outline:hover,
|
.btn-danger-outline:active,
|
||||||
.open > .btn-primary-outline.dropdown-toggle {
|
.btn-danger-outline.active,
|
||||||
color: #fff $i;
|
.btn-danger-outline:hover,
|
||||||
background-color: $primary-colour $i;
|
.open > .btn-danger-outline.dropdown-toggle {
|
||||||
border-color: $primary-colour $i;
|
color: #fff $i;
|
||||||
}
|
background-color: $danger-colour $i;
|
||||||
|
border-color: $danger-colour $i;
|
||||||
.btn-info-outline {
|
}
|
||||||
color: $info-colour $i;
|
|
||||||
background-color: transparent;
|
|
||||||
background-image: none;
|
.btn-primary-outline {
|
||||||
border-color: $info-colour $i;
|
color: $primary-colour-outline $i;
|
||||||
}
|
background-color: transparent;
|
||||||
|
background-image: none;
|
||||||
.btn-info-outline:focus,
|
border-color: $primary-colour-outline $i;
|
||||||
.btn-info-outline.focus,
|
}
|
||||||
.btn-info-outline:active,
|
|
||||||
.btn-info-outline.active,
|
.btn-primary-outline:focus,
|
||||||
.btn-info-outline:hover,
|
.btn-primary-outline.focus,
|
||||||
.open > .btn-info-outline.dropdown-toggle {
|
.btn-primary-outline:active,
|
||||||
color: #fff $i;
|
.btn-primary-outline.active,
|
||||||
background-color: $info-colour $i;
|
.btn-primary-outline:hover,
|
||||||
border-color: $info-colour $i;
|
.open > .btn-primary-outline.dropdown-toggle {
|
||||||
}
|
color: #fff $i;
|
||||||
|
background-color: $primary-colour $i;
|
||||||
.btn-warning-outline {
|
border-color: $primary-colour $i;
|
||||||
color: $warning-colour $i;
|
}
|
||||||
background-color: transparent;
|
|
||||||
background-image: none;
|
.btn-info-outline {
|
||||||
border-color: $warning-colour $i;
|
color: $info-colour $i;
|
||||||
}
|
background-color: transparent;
|
||||||
|
background-image: none;
|
||||||
.btn-warning-outline:focus,
|
border-color: $info-colour $i;
|
||||||
.btn-warning-outline.focus,
|
}
|
||||||
.btn-warning-outline:active,
|
|
||||||
.btn-warning-outline.active,
|
.btn-info-outline:focus,
|
||||||
.btn-warning-outline:hover,
|
.btn-info-outline.focus,
|
||||||
.open > .btn-warning-outline.dropdown-toggle {
|
.btn-info-outline:active,
|
||||||
color: #fff $i;
|
.btn-info-outline.active,
|
||||||
background-color: $warning-colour $i;
|
.btn-info-outline:hover,
|
||||||
border-color: $warning-colour $i;
|
.open > .btn-info-outline.dropdown-toggle {
|
||||||
}
|
color: #fff $i;
|
||||||
|
background-color: $info-colour $i;
|
||||||
.btn-success-outline {
|
border-color: $info-colour $i;
|
||||||
color: $success-colour $i;
|
}
|
||||||
background-color: transparent;
|
|
||||||
background-image: none;
|
.btn-warning-outline {
|
||||||
border-color: $success-colour $i;
|
color: $warning-colour $i;
|
||||||
}
|
background-color: transparent;
|
||||||
|
background-image: none;
|
||||||
.btn-success-outline:focus,
|
border-color: $warning-colour $i;
|
||||||
.btn-success-outline.focus,
|
}
|
||||||
.btn-success-outline:active,
|
|
||||||
.btn-success-outline.active,
|
.btn-warning-outline:focus,
|
||||||
.btn-success-outline:hover,
|
.btn-warning-outline.focus,
|
||||||
.open > .btn-success-outline.dropdown-toggle {
|
.btn-warning-outline:active,
|
||||||
color: #fff $i;
|
.btn-warning-outline.active,
|
||||||
background-color: $success-colour $i;
|
.btn-warning-outline:hover,
|
||||||
border-color: $success-colour $i;
|
.open > .btn-warning-outline.dropdown-toggle {
|
||||||
}
|
color: #fff $i;
|
||||||
|
background-color: $warning-colour $i;
|
||||||
#movieList .mix {
|
border-color: $warning-colour $i;
|
||||||
display: none;
|
}
|
||||||
}
|
|
||||||
|
.btn-success-outline {
|
||||||
#tvList .mix {
|
color: $success-colour $i;
|
||||||
display: none;
|
background-color: transparent;
|
||||||
}
|
background-image: none;
|
||||||
|
border-color: $success-colour $i;
|
||||||
$border-radius: 10px;
|
}
|
||||||
|
|
||||||
.scroll-top-wrapper {
|
.btn-success-outline:focus,
|
||||||
position: fixed;
|
.btn-success-outline.focus,
|
||||||
opacity: 0;
|
.btn-success-outline:active,
|
||||||
visibility: hidden;
|
.btn-success-outline.active,
|
||||||
overflow: hidden;
|
.btn-success-outline:hover,
|
||||||
text-align: center;
|
.open > .btn-success-outline.dropdown-toggle {
|
||||||
z-index: 99999999;
|
color: #fff $i;
|
||||||
background-color: $form-color;
|
background-color: $success-colour $i;
|
||||||
color: #eeeeee;
|
border-color: $success-colour $i;
|
||||||
width: 50px;
|
}
|
||||||
height: 48px;
|
|
||||||
line-height: 48px;
|
#movieList .mix {
|
||||||
right: 30px;
|
display: none;
|
||||||
bottom: 30px;
|
}
|
||||||
padding-top: 2px;
|
|
||||||
border-top-left-radius: $border-radius;
|
#tvList .mix {
|
||||||
border-top-right-radius: $border-radius;
|
display: none;
|
||||||
border-bottom-right-radius: $border-radius;
|
}
|
||||||
border-bottom-left-radius: $border-radius;
|
|
||||||
-webkit-transition: all 0.5s ease-in-out;
|
$border-radius: 10px;
|
||||||
-moz-transition: all 0.5s ease-in-out;
|
|
||||||
-ms-transition: all 0.5s ease-in-out;
|
.scroll-top-wrapper {
|
||||||
-o-transition: all 0.5s ease-in-out;
|
position: fixed;
|
||||||
transition: all 0.5s ease-in-out;
|
opacity: 0;
|
||||||
}
|
visibility: hidden;
|
||||||
|
overflow: hidden;
|
||||||
.scroll-top-wrapper:hover {
|
text-align: center;
|
||||||
background-color: $form-color-lighter;
|
z-index: 99999999;
|
||||||
}
|
background-color: $form-color;
|
||||||
|
color: #eeeeee;
|
||||||
.scroll-top-wrapper.show {
|
width: 50px;
|
||||||
visibility: visible;
|
height: 48px;
|
||||||
cursor: pointer;
|
line-height: 48px;
|
||||||
opacity: 1.0;
|
right: 30px;
|
||||||
}
|
bottom: 30px;
|
||||||
|
padding-top: 2px;
|
||||||
.scroll-top-wrapper i.fa {
|
border-top-left-radius: $border-radius;
|
||||||
line-height: inherit;
|
border-top-right-radius: $border-radius;
|
||||||
}
|
border-bottom-right-radius: $border-radius;
|
||||||
|
border-bottom-left-radius: $border-radius;
|
||||||
|
-webkit-transition: all 0.5s ease-in-out;
|
||||||
.no-search-results {
|
-moz-transition: all 0.5s ease-in-out;
|
||||||
text-align: center;
|
-ms-transition: all 0.5s ease-in-out;
|
||||||
}
|
-o-transition: all 0.5s ease-in-out;
|
||||||
|
transition: all 0.5s ease-in-out;
|
||||||
.no-search-results .no-search-results-icon {
|
}
|
||||||
font-size: 10em;
|
|
||||||
color: $form-color;
|
.scroll-top-wrapper:hover {
|
||||||
}
|
background-color: $form-color-lighter;
|
||||||
|
}
|
||||||
.no-search-results .no-search-results-text {
|
|
||||||
margin: 20px 0;
|
.scroll-top-wrapper.show {
|
||||||
color: #ccc;
|
visibility: visible;
|
||||||
}
|
cursor: pointer;
|
||||||
|
opacity: 1.0;
|
||||||
.form-control-search {
|
}
|
||||||
padding: 13px 105px 13px 16px;
|
|
||||||
height: 100%;
|
.scroll-top-wrapper i.fa {
|
||||||
}
|
line-height: inherit;
|
||||||
|
}
|
||||||
.form-control-withbuttons {
|
|
||||||
padding-right: 105px;
|
|
||||||
}
|
.no-search-results {
|
||||||
|
text-align: center;
|
||||||
.input-group-addon .btn-group {
|
}
|
||||||
position: absolute;
|
|
||||||
right: 45px;
|
.no-search-results .no-search-results-icon {
|
||||||
z-index: 3;
|
font-size: 10em;
|
||||||
top: 13px;
|
color: $form-color;
|
||||||
box-shadow: 0 0 0;
|
}
|
||||||
}
|
|
||||||
|
.no-search-results .no-search-results-text {
|
||||||
.input-group-addon .btn-group .btn {
|
margin: 20px 0;
|
||||||
border: 1px solid rgba(255,255,255,.7) !important;
|
color: #ccc;
|
||||||
padding: 3px 12px;
|
}
|
||||||
color: rgba(255,255,255,.7) !important;
|
|
||||||
}
|
.form-control-search {
|
||||||
|
padding: 13px 105px 13px 16px;
|
||||||
.btn-split .btn {
|
height: 100%;
|
||||||
border-radius: 0 !important;
|
}
|
||||||
}
|
|
||||||
|
.form-control-withbuttons {
|
||||||
.btn-split .btn:not(.dropdown-toggle) {
|
padding-right: 105px;
|
||||||
border-radius: .25rem 0 0 .25rem $i;
|
}
|
||||||
}
|
|
||||||
|
.input-group-addon .btn-group {
|
||||||
.btn-split .btn.dropdown-toggle {
|
position: absolute;
|
||||||
border-radius: 0 .25rem .25rem 0 $i;
|
right: 45px;
|
||||||
padding: 12px 8px;
|
z-index: 3;
|
||||||
}
|
top: 13px;
|
||||||
|
box-shadow: 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group-addon .btn-group .btn {
|
||||||
|
border: 1px solid rgba(255,255,255,.7) !important;
|
||||||
|
padding: 3px 12px;
|
||||||
|
color: rgba(255,255,255,.7) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-split .btn {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-split .btn:not(.dropdown-toggle) {
|
||||||
|
border-radius: .25rem 0 0 .25rem $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-split .btn.dropdown-toggle {
|
||||||
|
border-radius: 0 .25rem .25rem 0 $i;
|
||||||
|
padding: 12px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#updateAvailable {
|
||||||
|
background-color: rgb(255, 164, 0);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox label {
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 25px;
|
||||||
|
margin-right: 15px;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-bottom: 10px; }
|
||||||
|
|
||||||
|
.checkbox label:before {
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
margin-right: 10px;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 1px;
|
||||||
|
border: 2px solid #eee;
|
||||||
|
border-radius: 3px; }
|
||||||
|
|
||||||
|
.checkbox input[type=checkbox] {
|
||||||
|
display: none; }
|
||||||
|
|
||||||
|
.checkbox input[type=checkbox]:checked + label:before {
|
||||||
|
content: "\2713";
|
||||||
|
font-size: 13px;
|
||||||
|
color: #fafafa;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 13px; }
|
||||||
|
|
||||||
|
.input-group-sm{
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-pane .form-horizontal .form-group {
|
||||||
|
margin-right: 15px;
|
||||||
|
margin-left: 15px; }
|
7007
PlexRequests.UI/Content/Themes/PlexBootstrap.css
Normal file
7007
PlexRequests.UI/Content/Themes/PlexBootstrap.css
Normal file
File diff suppressed because it is too large
Load diff
283
PlexRequests.UI/Content/Themes/PlexBootstrapCustom.css
Normal file
283
PlexRequests.UI/Content/Themes/PlexBootstrapCustom.css
Normal file
|
@ -0,0 +1,283 @@
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.row {
|
||||||
|
position: relative; }
|
||||||
|
.bottom-align-text {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0; } }
|
||||||
|
|
||||||
|
@media (max-width: 48em) {
|
||||||
|
.home {
|
||||||
|
padding-top: 1rem; } }
|
||||||
|
|
||||||
|
@media (min-width: 48em) {
|
||||||
|
.home {
|
||||||
|
padding-top: 4rem; } }
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
border-radius: 0.25rem !important; }
|
||||||
|
|
||||||
|
.multiSelect {
|
||||||
|
background-color: #4e5d6c; }
|
||||||
|
|
||||||
|
.form-control-custom {
|
||||||
|
background-color: #333333 !important;
|
||||||
|
color: white !important;
|
||||||
|
border-radius: 0;
|
||||||
|
box-shadow: 0 0 0 !important; }
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 3.5rem !important;
|
||||||
|
font-weight: 600 !important; }
|
||||||
|
|
||||||
|
.request-title {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
font-size: 1.9rem !important; }
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 1.1rem !important; }
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: inline-block !important;
|
||||||
|
margin-bottom: 0.5rem !important;
|
||||||
|
font-size: 16px !important; }
|
||||||
|
|
||||||
|
.nav-tabs > li {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 21px; }
|
||||||
|
|
||||||
|
.nav-tabs > li.active > a,
|
||||||
|
.nav-tabs > li.active > a:hover,
|
||||||
|
.nav-tabs > li.active > a:focus {
|
||||||
|
background: #df691a; }
|
||||||
|
|
||||||
|
.nav-tabs > li > a > .fa {
|
||||||
|
padding: 3px 5px 3px 3px; }
|
||||||
|
|
||||||
|
.nav-tabs > li.nav-tab-right {
|
||||||
|
float: right; }
|
||||||
|
|
||||||
|
.nav-tabs > li.nav-tab-right a {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-left: 2px; }
|
||||||
|
|
||||||
|
.nav-tabs > li.nav-tab-icononly .fa {
|
||||||
|
padding: 3px; }
|
||||||
|
|
||||||
|
.navbar .nav a .fa,
|
||||||
|
.dropdown-menu a .fa {
|
||||||
|
font-size: 130%;
|
||||||
|
top: 1px;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 5px; }
|
||||||
|
|
||||||
|
.dropdown-menu a .fa {
|
||||||
|
top: 2px; }
|
||||||
|
|
||||||
|
.btn-danger-outline {
|
||||||
|
color: #d9534f !important;
|
||||||
|
background-color: transparent;
|
||||||
|
background-image: none;
|
||||||
|
border-color: #d9534f !important; }
|
||||||
|
|
||||||
|
.btn-danger-outline:focus,
|
||||||
|
.btn-danger-outline.focus,
|
||||||
|
.btn-danger-outline:active,
|
||||||
|
.btn-danger-outline.active,
|
||||||
|
.btn-danger-outline:hover,
|
||||||
|
.open > .btn-danger-outline.dropdown-toggle {
|
||||||
|
color: #fff !important;
|
||||||
|
background-color: #d9534f !important;
|
||||||
|
border-color: #d9534f !important; }
|
||||||
|
|
||||||
|
.btn-primary-outline {
|
||||||
|
color: #ff761b !important;
|
||||||
|
background-color: transparent;
|
||||||
|
background-image: none;
|
||||||
|
border-color: #ff761b !important; }
|
||||||
|
|
||||||
|
.btn-primary-outline:focus,
|
||||||
|
.btn-primary-outline.focus,
|
||||||
|
.btn-primary-outline:active,
|
||||||
|
.btn-primary-outline.active,
|
||||||
|
.btn-primary-outline:hover,
|
||||||
|
.open > .btn-primary-outline.dropdown-toggle {
|
||||||
|
color: #fff !important;
|
||||||
|
background-color: #df691a !important;
|
||||||
|
border-color: #df691a !important; }
|
||||||
|
|
||||||
|
.btn-info-outline {
|
||||||
|
color: #5bc0de !important;
|
||||||
|
background-color: transparent;
|
||||||
|
background-image: none;
|
||||||
|
border-color: #5bc0de !important; }
|
||||||
|
|
||||||
|
.btn-info-outline:focus,
|
||||||
|
.btn-info-outline.focus,
|
||||||
|
.btn-info-outline:active,
|
||||||
|
.btn-info-outline.active,
|
||||||
|
.btn-info-outline:hover,
|
||||||
|
.open > .btn-info-outline.dropdown-toggle {
|
||||||
|
color: #fff !important;
|
||||||
|
background-color: #5bc0de !important;
|
||||||
|
border-color: #5bc0de !important; }
|
||||||
|
|
||||||
|
.btn-warning-outline {
|
||||||
|
color: #f0ad4e !important;
|
||||||
|
background-color: transparent;
|
||||||
|
background-image: none;
|
||||||
|
border-color: #f0ad4e !important; }
|
||||||
|
|
||||||
|
.btn-warning-outline:focus,
|
||||||
|
.btn-warning-outline.focus,
|
||||||
|
.btn-warning-outline:active,
|
||||||
|
.btn-warning-outline.active,
|
||||||
|
.btn-warning-outline:hover,
|
||||||
|
.open > .btn-warning-outline.dropdown-toggle {
|
||||||
|
color: #fff !important;
|
||||||
|
background-color: #f0ad4e !important;
|
||||||
|
border-color: #f0ad4e !important; }
|
||||||
|
|
||||||
|
.btn-success-outline {
|
||||||
|
color: #5cb85c !important;
|
||||||
|
background-color: transparent;
|
||||||
|
background-image: none;
|
||||||
|
border-color: #5cb85c !important; }
|
||||||
|
|
||||||
|
.btn-success-outline:focus,
|
||||||
|
.btn-success-outline.focus,
|
||||||
|
.btn-success-outline:active,
|
||||||
|
.btn-success-outline.active,
|
||||||
|
.btn-success-outline:hover,
|
||||||
|
.open > .btn-success-outline.dropdown-toggle {
|
||||||
|
color: #fff !important;
|
||||||
|
background-color: #5cb85c !important;
|
||||||
|
border-color: #5cb85c !important; }
|
||||||
|
|
||||||
|
#movieList .mix {
|
||||||
|
display: none; }
|
||||||
|
|
||||||
|
#tvList .mix {
|
||||||
|
display: none; }
|
||||||
|
|
||||||
|
.scroll-top-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: center;
|
||||||
|
z-index: 99999999;
|
||||||
|
background-color: #333333;
|
||||||
|
color: #eeeeee;
|
||||||
|
width: 50px;
|
||||||
|
height: 48px;
|
||||||
|
line-height: 48px;
|
||||||
|
right: 30px;
|
||||||
|
bottom: 30px;
|
||||||
|
padding-top: 2px;
|
||||||
|
border-top-left-radius: 10px;
|
||||||
|
border-top-right-radius: 10px;
|
||||||
|
border-bottom-right-radius: 10px;
|
||||||
|
border-bottom-left-radius: 10px;
|
||||||
|
-webkit-transition: all 0.5s ease-in-out;
|
||||||
|
-moz-transition: all 0.5s ease-in-out;
|
||||||
|
-ms-transition: all 0.5s ease-in-out;
|
||||||
|
-o-transition: all 0.5s ease-in-out;
|
||||||
|
transition: all 0.5s ease-in-out; }
|
||||||
|
|
||||||
|
.scroll-top-wrapper:hover {
|
||||||
|
background-color: #df691a; }
|
||||||
|
|
||||||
|
.scroll-top-wrapper.show {
|
||||||
|
visibility: visible;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 1.0; }
|
||||||
|
|
||||||
|
.scroll-top-wrapper i.fa {
|
||||||
|
line-height: inherit; }
|
||||||
|
|
||||||
|
.no-search-results {
|
||||||
|
text-align: center; }
|
||||||
|
|
||||||
|
.no-search-results .no-search-results-icon {
|
||||||
|
font-size: 10em;
|
||||||
|
color: #4e5d6c; }
|
||||||
|
|
||||||
|
.no-search-results .no-search-results-text {
|
||||||
|
margin: 20px 0;
|
||||||
|
color: #ccc; }
|
||||||
|
|
||||||
|
.form-control-search {
|
||||||
|
padding: 13px 105px 13px 16px;
|
||||||
|
height: 100%; }
|
||||||
|
|
||||||
|
.form-control-withbuttons {
|
||||||
|
padding-right: 105px; }
|
||||||
|
|
||||||
|
.input-group-addon .btn-group {
|
||||||
|
position: absolute;
|
||||||
|
right: 45px;
|
||||||
|
z-index: 3;
|
||||||
|
top: 13px;
|
||||||
|
box-shadow: 0 0 0; }
|
||||||
|
|
||||||
|
.input-group-addon .btn-group .btn {
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.7) !important;
|
||||||
|
padding: 3px 12px;
|
||||||
|
color: rgba(255, 255, 255, 0.7) !important; }
|
||||||
|
|
||||||
|
.btn-split .btn {
|
||||||
|
border-radius: 0 !important; }
|
||||||
|
|
||||||
|
.btn-split .btn:not(.dropdown-toggle) {
|
||||||
|
border-radius: 0.25rem 0 0 0.25rem !important; }
|
||||||
|
|
||||||
|
.btn-split .btn.dropdown-toggle {
|
||||||
|
border-radius: 0 0.25rem 0.25rem 0 !important;
|
||||||
|
padding: 12px 8px; }
|
||||||
|
|
||||||
|
#updateAvailable {
|
||||||
|
background-color: #ffa400;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 15px; }
|
||||||
|
|
||||||
|
.checkbox label {
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 25px;
|
||||||
|
margin-right: 15px;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-bottom: 10px; }
|
||||||
|
|
||||||
|
.checkbox label:before {
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
margin-right: 10px;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 1px;
|
||||||
|
border: 2px solid #eee;
|
||||||
|
border-radius: 3px; }
|
||||||
|
|
||||||
|
.checkbox input[type=checkbox] {
|
||||||
|
display: none; }
|
||||||
|
|
||||||
|
.checkbox input[type=checkbox]:checked + label:before {
|
||||||
|
content: "\2713";
|
||||||
|
font-size: 13px;
|
||||||
|
color: #fafafa;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 13px; }
|
||||||
|
|
||||||
|
.input-group-sm {
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-bottom: 2px; }
|
||||||
|
|
||||||
|
.tab-pane .form-horizontal .form-group {
|
||||||
|
margin-right: 15px;
|
||||||
|
margin-left: 15px; }
|
||||||
|
|
1
PlexRequests.UI/Content/Themes/PlexBootstrapCustom.min.css
vendored
Normal file
1
PlexRequests.UI/Content/Themes/PlexBootstrapCustom.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
350
PlexRequests.UI/Content/Themes/PlexBootstrapCustom.scss
Normal file
350
PlexRequests.UI/Content/Themes/PlexBootstrapCustom.scss
Normal file
|
@ -0,0 +1,350 @@
|
||||||
|
$form-color: #4e5d6c;
|
||||||
|
$form-color-lighter: #637689;
|
||||||
|
$primary-colour: #df691a;
|
||||||
|
$primary-colour-outline: #ff761b;
|
||||||
|
$info-colour: #5bc0de;
|
||||||
|
$warning-colour: #f0ad4e;
|
||||||
|
$danger-colour: #d9534f;
|
||||||
|
$success-colour: #5cb85c;
|
||||||
|
$bg-colour: #333333;
|
||||||
|
$i:
|
||||||
|
!important;
|
||||||
|
|
||||||
|
@media (min-width: 768px ) {
|
||||||
|
.row {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-align-text {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 48em) {
|
||||||
|
.home {
|
||||||
|
padding-top: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 48em) {
|
||||||
|
.home {
|
||||||
|
padding-top: 4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
border-radius: .25rem $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
.multiSelect {
|
||||||
|
background-color: $form-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control-custom {
|
||||||
|
background-color: $bg-colour $i;
|
||||||
|
color: white $i;
|
||||||
|
border-radius: 0;
|
||||||
|
box-shadow: 0 0 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 3.5rem $i;
|
||||||
|
font-weight: 600 $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
.request-title {
|
||||||
|
margin-top: 0 $i;
|
||||||
|
font-size: 1.9rem $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 1.1rem $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: inline-block $i;
|
||||||
|
margin-bottom: .5rem $i;
|
||||||
|
font-size: 16px $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs > li {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 21px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs > li.active > a,
|
||||||
|
.nav-tabs > li.active > a:hover,
|
||||||
|
.nav-tabs > li.active > a:focus {
|
||||||
|
background: $primary-colour;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs > li > a > .fa {
|
||||||
|
padding: 3px 5px 3px 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs > li.nav-tab-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs > li.nav-tab-right a {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs > li.nav-tab-icononly .fa {
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar .nav a .fa,
|
||||||
|
.dropdown-menu a .fa {
|
||||||
|
font-size: 130%;
|
||||||
|
top: 1px;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu a .fa {
|
||||||
|
top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger-outline {
|
||||||
|
color: $danger-colour $i;
|
||||||
|
background-color: transparent;
|
||||||
|
background-image: none;
|
||||||
|
border-color: $danger-colour $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger-outline:focus,
|
||||||
|
.btn-danger-outline.focus,
|
||||||
|
.btn-danger-outline:active,
|
||||||
|
.btn-danger-outline.active,
|
||||||
|
.btn-danger-outline:hover,
|
||||||
|
.open > .btn-danger-outline.dropdown-toggle {
|
||||||
|
color: #fff $i;
|
||||||
|
background-color: $danger-colour $i;
|
||||||
|
border-color: $danger-colour $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn-primary-outline {
|
||||||
|
color: $primary-colour-outline $i;
|
||||||
|
background-color: transparent;
|
||||||
|
background-image: none;
|
||||||
|
border-color: $primary-colour-outline $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary-outline:focus,
|
||||||
|
.btn-primary-outline.focus,
|
||||||
|
.btn-primary-outline:active,
|
||||||
|
.btn-primary-outline.active,
|
||||||
|
.btn-primary-outline:hover,
|
||||||
|
.open > .btn-primary-outline.dropdown-toggle {
|
||||||
|
color: #fff $i;
|
||||||
|
background-color: $primary-colour $i;
|
||||||
|
border-color: $primary-colour $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-info-outline {
|
||||||
|
color: $info-colour $i;
|
||||||
|
background-color: transparent;
|
||||||
|
background-image: none;
|
||||||
|
border-color: $info-colour $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-info-outline:focus,
|
||||||
|
.btn-info-outline.focus,
|
||||||
|
.btn-info-outline:active,
|
||||||
|
.btn-info-outline.active,
|
||||||
|
.btn-info-outline:hover,
|
||||||
|
.open > .btn-info-outline.dropdown-toggle {
|
||||||
|
color: #fff $i;
|
||||||
|
background-color: $info-colour $i;
|
||||||
|
border-color: $info-colour $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-warning-outline {
|
||||||
|
color: $warning-colour $i;
|
||||||
|
background-color: transparent;
|
||||||
|
background-image: none;
|
||||||
|
border-color: $warning-colour $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-warning-outline:focus,
|
||||||
|
.btn-warning-outline.focus,
|
||||||
|
.btn-warning-outline:active,
|
||||||
|
.btn-warning-outline.active,
|
||||||
|
.btn-warning-outline:hover,
|
||||||
|
.open > .btn-warning-outline.dropdown-toggle {
|
||||||
|
color: #fff $i;
|
||||||
|
background-color: $warning-colour $i;
|
||||||
|
border-color: $warning-colour $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-success-outline {
|
||||||
|
color: $success-colour $i;
|
||||||
|
background-color: transparent;
|
||||||
|
background-image: none;
|
||||||
|
border-color: $success-colour $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-success-outline:focus,
|
||||||
|
.btn-success-outline.focus,
|
||||||
|
.btn-success-outline:active,
|
||||||
|
.btn-success-outline.active,
|
||||||
|
.btn-success-outline:hover,
|
||||||
|
.open > .btn-success-outline.dropdown-toggle {
|
||||||
|
color: #fff $i;
|
||||||
|
background-color: $success-colour $i;
|
||||||
|
border-color: $success-colour $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
#movieList .mix {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tvList .mix {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
$border-radius: 10px;
|
||||||
|
|
||||||
|
.scroll-top-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: center;
|
||||||
|
z-index: 99999999;
|
||||||
|
background-color: $bg-colour;
|
||||||
|
color: #eeeeee;
|
||||||
|
width: 50px;
|
||||||
|
height: 48px;
|
||||||
|
line-height: 48px;
|
||||||
|
right: 30px;
|
||||||
|
bottom: 30px;
|
||||||
|
padding-top: 2px;
|
||||||
|
border-top-left-radius: $border-radius;
|
||||||
|
border-top-right-radius: $border-radius;
|
||||||
|
border-bottom-right-radius: $border-radius;
|
||||||
|
border-bottom-left-radius: $border-radius;
|
||||||
|
-webkit-transition: all 0.5s ease-in-out;
|
||||||
|
-moz-transition: all 0.5s ease-in-out;
|
||||||
|
-ms-transition: all 0.5s ease-in-out;
|
||||||
|
-o-transition: all 0.5s ease-in-out;
|
||||||
|
transition: all 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-top-wrapper:hover {
|
||||||
|
background-color: $primary-colour;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-top-wrapper.show {
|
||||||
|
visibility: visible;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-top-wrapper i.fa {
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.no-search-results {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-search-results .no-search-results-icon {
|
||||||
|
font-size: 10em;
|
||||||
|
color: $form-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-search-results .no-search-results-text {
|
||||||
|
margin: 20px 0;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control-search {
|
||||||
|
padding: 13px 105px 13px 16px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control-withbuttons {
|
||||||
|
padding-right: 105px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group-addon .btn-group {
|
||||||
|
position: absolute;
|
||||||
|
right: 45px;
|
||||||
|
z-index: 3;
|
||||||
|
top: 13px;
|
||||||
|
box-shadow: 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group-addon .btn-group .btn {
|
||||||
|
border: 1px solid rgba(255,255,255,.7) !important;
|
||||||
|
padding: 3px 12px;
|
||||||
|
color: rgba(255,255,255,.7) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-split .btn {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-split .btn:not(.dropdown-toggle) {
|
||||||
|
border-radius: .25rem 0 0 .25rem $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-split .btn.dropdown-toggle {
|
||||||
|
border-radius: 0 .25rem .25rem 0 $i;
|
||||||
|
padding: 12px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#updateAvailable {
|
||||||
|
background-color: rgb(255, 164, 0);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox label {
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 25px;
|
||||||
|
margin-right: 15px;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-bottom: 10px; }
|
||||||
|
|
||||||
|
.checkbox label:before {
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
margin-right: 10px;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 1px;
|
||||||
|
border: 2px solid #eee;
|
||||||
|
border-radius: 3px; }
|
||||||
|
|
||||||
|
.checkbox input[type=checkbox] {
|
||||||
|
display: none; }
|
||||||
|
|
||||||
|
.checkbox input[type=checkbox]:checked + label:before {
|
||||||
|
content: "\2713";
|
||||||
|
font-size: 13px;
|
||||||
|
color: #fafafa;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 13px; }
|
||||||
|
|
||||||
|
.input-group-sm{
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-pane .form-horizontal .form-group {
|
||||||
|
margin-right: 15px;
|
||||||
|
margin-left: 15px; }
|
266
PlexRequests.UI/Content/awesome-bootstrap-checkbox.css
Normal file
266
PlexRequests.UI/Content/awesome-bootstrap-checkbox.css
Normal file
|
@ -0,0 +1,266 @@
|
||||||
|
@charset "UTF-8";
|
||||||
|
.abc-checkbox {
|
||||||
|
padding-left: 20px; }
|
||||||
|
.abc-checkbox label {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 5px; }
|
||||||
|
.abc-checkbox label::before {
|
||||||
|
cursor: pointer;
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
width: 17px;
|
||||||
|
height: 17px;
|
||||||
|
left: 0;
|
||||||
|
margin-left: -20px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: #fff; }
|
||||||
|
.abc-checkbox label::after {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
margin-left: -20px;
|
||||||
|
padding-left: 3px;
|
||||||
|
padding-top: 1px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #55595c; }
|
||||||
|
.abc-checkbox input[type="checkbox"],
|
||||||
|
.abc-checkbox input[type="radio"] {
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0;
|
||||||
|
z-index: 1; }
|
||||||
|
.abc-checkbox input[type="checkbox"]:focus + label::before,
|
||||||
|
.abc-checkbox input[type="radio"]:focus + label::before {
|
||||||
|
outline: thin dotted;
|
||||||
|
outline: 5px auto -webkit-focus-ring-color;
|
||||||
|
outline-offset: -2px; }
|
||||||
|
.abc-checkbox input[type="checkbox"]:checked + label::after,
|
||||||
|
.abc-checkbox input[type="radio"]:checked + label::after {
|
||||||
|
font-family: "FontAwesome";
|
||||||
|
content: ""; }
|
||||||
|
.abc-checkbox input[type="checkbox"]:indeterminate + label::after,
|
||||||
|
.abc-checkbox input[type="radio"]:indeterminate + label::after {
|
||||||
|
display: block;
|
||||||
|
content: "";
|
||||||
|
width: 10px;
|
||||||
|
height: 3px;
|
||||||
|
background-color: #555555;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-left: -16.5px;
|
||||||
|
margin-top: 7px; }
|
||||||
|
.abc-checkbox input[type="checkbox"]:disabled + label,
|
||||||
|
.abc-checkbox input[type="radio"]:disabled + label {
|
||||||
|
opacity: 0.65; }
|
||||||
|
.abc-checkbox input[type="checkbox"]:disabled + label::before,
|
||||||
|
.abc-checkbox input[type="radio"]:disabled + label::before {
|
||||||
|
background-color: #eceeef;
|
||||||
|
cursor: not-allowed; }
|
||||||
|
.abc-checkbox.abc-checkbox-circle label::before {
|
||||||
|
border-radius: 50%; }
|
||||||
|
.abc-checkbox.checkbox-inline {
|
||||||
|
margin-top: 0; }
|
||||||
|
|
||||||
|
.abc-checkbox-primary input[type="checkbox"]:checked + label::before,
|
||||||
|
.abc-checkbox-primary input[type="radio"]:checked + label::before {
|
||||||
|
background-color: #0275d8;
|
||||||
|
border-color: #0275d8; }
|
||||||
|
|
||||||
|
.abc-checkbox-primary input[type="checkbox"]:checked + label::after,
|
||||||
|
.abc-checkbox-primary input[type="radio"]:checked + label::after {
|
||||||
|
color: #fff; }
|
||||||
|
|
||||||
|
.abc-checkbox-danger input[type="checkbox"]:checked + label::before,
|
||||||
|
.abc-checkbox-danger input[type="radio"]:checked + label::before {
|
||||||
|
background-color: #d9534f;
|
||||||
|
border-color: #d9534f; }
|
||||||
|
|
||||||
|
.abc-checkbox-danger input[type="checkbox"]:checked + label::after,
|
||||||
|
.abc-checkbox-danger input[type="radio"]:checked + label::after {
|
||||||
|
color: #fff; }
|
||||||
|
|
||||||
|
.abc-checkbox-info input[type="checkbox"]:checked + label::before,
|
||||||
|
.abc-checkbox-info input[type="radio"]:checked + label::before {
|
||||||
|
background-color: #5bc0de;
|
||||||
|
border-color: #5bc0de; }
|
||||||
|
|
||||||
|
.abc-checkbox-info input[type="checkbox"]:checked + label::after,
|
||||||
|
.abc-checkbox-info input[type="radio"]:checked + label::after {
|
||||||
|
color: #fff; }
|
||||||
|
|
||||||
|
.abc-checkbox-warning input[type="checkbox"]:checked + label::before,
|
||||||
|
.abc-checkbox-warning input[type="radio"]:checked + label::before {
|
||||||
|
background-color: #f0ad4e;
|
||||||
|
border-color: #f0ad4e; }
|
||||||
|
|
||||||
|
.abc-checkbox-warning input[type="checkbox"]:checked + label::after,
|
||||||
|
.abc-checkbox-warning input[type="radio"]:checked + label::after {
|
||||||
|
color: #fff; }
|
||||||
|
|
||||||
|
.abc-checkbox-success input[type="checkbox"]:checked + label::before,
|
||||||
|
.abc-checkbox-success input[type="radio"]:checked + label::before {
|
||||||
|
background-color: #5cb85c;
|
||||||
|
border-color: #5cb85c; }
|
||||||
|
|
||||||
|
.abc-checkbox-success input[type="checkbox"]:checked + label::after,
|
||||||
|
.abc-checkbox-success input[type="radio"]:checked + label::after {
|
||||||
|
color: #fff; }
|
||||||
|
|
||||||
|
.abc-checkbox-primary input[type="checkbox"]:indeterminate + label::before,
|
||||||
|
.abc-checkbox-primary input[type="radio"]:indeterminate + label::before {
|
||||||
|
background-color: #0275d8;
|
||||||
|
border-color: #0275d8; }
|
||||||
|
|
||||||
|
.abc-checkbox-primary input[type="checkbox"]:indeterminate + label::after,
|
||||||
|
.abc-checkbox-primary input[type="radio"]:indeterminate + label::after {
|
||||||
|
background-color: #fff; }
|
||||||
|
|
||||||
|
.abc-checkbox-danger input[type="checkbox"]:indeterminate + label::before,
|
||||||
|
.abc-checkbox-danger input[type="radio"]:indeterminate + label::before {
|
||||||
|
background-color: #d9534f;
|
||||||
|
border-color: #d9534f; }
|
||||||
|
|
||||||
|
.abc-checkbox-danger input[type="checkbox"]:indeterminate + label::after,
|
||||||
|
.abc-checkbox-danger input[type="radio"]:indeterminate + label::after {
|
||||||
|
background-color: #fff; }
|
||||||
|
|
||||||
|
.abc-checkbox-info input[type="checkbox"]:indeterminate + label::before,
|
||||||
|
.abc-checkbox-info input[type="radio"]:indeterminate + label::before {
|
||||||
|
background-color: #5bc0de;
|
||||||
|
border-color: #5bc0de; }
|
||||||
|
|
||||||
|
.abc-checkbox-info input[type="checkbox"]:indeterminate + label::after,
|
||||||
|
.abc-checkbox-info input[type="radio"]:indeterminate + label::after {
|
||||||
|
background-color: #fff; }
|
||||||
|
|
||||||
|
.abc-checkbox-warning input[type="checkbox"]:indeterminate + label::before,
|
||||||
|
.abc-checkbox-warning input[type="radio"]:indeterminate + label::before {
|
||||||
|
background-color: #f0ad4e;
|
||||||
|
border-color: #f0ad4e; }
|
||||||
|
|
||||||
|
.abc-checkbox-warning input[type="checkbox"]:indeterminate + label::after,
|
||||||
|
.abc-checkbox-warning input[type="radio"]:indeterminate + label::after {
|
||||||
|
background-color: #fff; }
|
||||||
|
|
||||||
|
.abc-checkbox-success input[type="checkbox"]:indeterminate + label::before,
|
||||||
|
.abc-checkbox-success input[type="radio"]:indeterminate + label::before {
|
||||||
|
background-color: #5cb85c;
|
||||||
|
border-color: #5cb85c; }
|
||||||
|
|
||||||
|
.abc-checkbox-success input[type="checkbox"]:indeterminate + label::after,
|
||||||
|
.abc-checkbox-success input[type="radio"]:indeterminate + label::after {
|
||||||
|
background-color: #fff; }
|
||||||
|
|
||||||
|
.abc-radio {
|
||||||
|
padding-left: 20px; }
|
||||||
|
.abc-radio label {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 5px; }
|
||||||
|
.abc-radio label::before {
|
||||||
|
content: "";
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
width: 17px;
|
||||||
|
height: 17px;
|
||||||
|
left: 0;
|
||||||
|
margin-left: -20px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #fff; }
|
||||||
|
.abc-radio label::after {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
content: " ";
|
||||||
|
width: 11px;
|
||||||
|
height: 11px;
|
||||||
|
left: 3px;
|
||||||
|
top: 3px;
|
||||||
|
margin-left: -20px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #55595c;
|
||||||
|
transform: scale(0, 0);
|
||||||
|
transition: transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33); }
|
||||||
|
.abc-radio input[type="radio"] {
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0;
|
||||||
|
z-index: 1; }
|
||||||
|
.abc-radio input[type="radio"]:focus + label::before {
|
||||||
|
outline: thin dotted;
|
||||||
|
outline: 5px auto -webkit-focus-ring-color;
|
||||||
|
outline-offset: -2px; }
|
||||||
|
.abc-radio input[type="radio"]:checked + label::after {
|
||||||
|
transform: scale(1, 1); }
|
||||||
|
.abc-radio input[type="radio"]:disabled + label {
|
||||||
|
opacity: 0.65; }
|
||||||
|
.abc-radio input[type="radio"]:disabled + label::before {
|
||||||
|
cursor: not-allowed; }
|
||||||
|
.abc-radio.radio-inline {
|
||||||
|
margin-top: 0; }
|
||||||
|
|
||||||
|
.abc-radio-primary input[type="radio"] + label::after {
|
||||||
|
background-color: #0275d8; }
|
||||||
|
|
||||||
|
.abc-radio-primary input[type="radio"]:checked + label::before {
|
||||||
|
border-color: #0275d8; }
|
||||||
|
|
||||||
|
.abc-radio-primary input[type="radio"]:checked + label::after {
|
||||||
|
background-color: #0275d8; }
|
||||||
|
|
||||||
|
.abc-radio-danger input[type="radio"] + label::after {
|
||||||
|
background-color: #d9534f; }
|
||||||
|
|
||||||
|
.abc-radio-danger input[type="radio"]:checked + label::before {
|
||||||
|
border-color: #d9534f; }
|
||||||
|
|
||||||
|
.abc-radio-danger input[type="radio"]:checked + label::after {
|
||||||
|
background-color: #d9534f; }
|
||||||
|
|
||||||
|
.abc-radio-info input[type="radio"] + label::after {
|
||||||
|
background-color: #5bc0de; }
|
||||||
|
|
||||||
|
.abc-radio-info input[type="radio"]:checked + label::before {
|
||||||
|
border-color: #5bc0de; }
|
||||||
|
|
||||||
|
.abc-radio-info input[type="radio"]:checked + label::after {
|
||||||
|
background-color: #5bc0de; }
|
||||||
|
|
||||||
|
.abc-radio-warning input[type="radio"] + label::after {
|
||||||
|
background-color: #f0ad4e; }
|
||||||
|
|
||||||
|
.abc-radio-warning input[type="radio"]:checked + label::before {
|
||||||
|
border-color: #f0ad4e; }
|
||||||
|
|
||||||
|
.abc-radio-warning input[type="radio"]:checked + label::after {
|
||||||
|
background-color: #f0ad4e; }
|
||||||
|
|
||||||
|
.abc-radio-success input[type="radio"] + label::after {
|
||||||
|
background-color: #5cb85c; }
|
||||||
|
|
||||||
|
.abc-radio-success input[type="radio"]:checked + label::before {
|
||||||
|
border-color: #5cb85c; }
|
||||||
|
|
||||||
|
.abc-radio-success input[type="radio"]:checked + label::after {
|
||||||
|
background-color: #5cb85c; }
|
||||||
|
|
||||||
|
input[type="checkbox"].styled:checked + label:after,
|
||||||
|
input[type="radio"].styled:checked + label:after {
|
||||||
|
font-family: "FontAwesome";
|
||||||
|
content: ""; }
|
||||||
|
|
||||||
|
input[type="checkbox"] .styled:checked + label::before,
|
||||||
|
input[type="radio"] .styled:checked + label::before {
|
||||||
|
color: #fff; }
|
||||||
|
|
||||||
|
input[type="checkbox"] .styled:checked + label::after,
|
||||||
|
input[type="radio"] .styled:checked + label::after {
|
||||||
|
color: #fff; }
|
250
PlexRequests.UI/Content/awesome-bootstrap-checkbox.scss
Normal file
250
PlexRequests.UI/Content/awesome-bootstrap-checkbox.scss
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
|
||||||
|
//
|
||||||
|
// Checkboxes
|
||||||
|
// --------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
$font-family-icon: 'FontAwesome' !default;
|
||||||
|
$fa-var-check: "\f00c" !default;
|
||||||
|
$check-icon: $fa-var-check !default;
|
||||||
|
|
||||||
|
@mixin checkbox-variant($parent, $color) {
|
||||||
|
#{$parent} input[type="checkbox"]:checked + label,
|
||||||
|
#{$parent} input[type="radio"]:checked + label {
|
||||||
|
&::before {
|
||||||
|
background-color: $color;
|
||||||
|
border-color: $color;
|
||||||
|
}
|
||||||
|
&::after{
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin checkbox-variant-indeterminate($parent, $color) {
|
||||||
|
#{$parent} input[type="checkbox"]:indeterminate + label,
|
||||||
|
#{$parent} input[type="radio"]:indeterminate + label {
|
||||||
|
&::before {
|
||||||
|
background-color: $color;
|
||||||
|
border-color: $color;
|
||||||
|
}
|
||||||
|
&::after{
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.abc-checkbox{
|
||||||
|
padding-left: 20px;
|
||||||
|
|
||||||
|
label{
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 5px;
|
||||||
|
|
||||||
|
&::before{
|
||||||
|
cursor: pointer;
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
width: 17px;
|
||||||
|
height: 17px;
|
||||||
|
left: 0;
|
||||||
|
margin-left: -20px;
|
||||||
|
border: 1px solid $input-border-color;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: #fff;
|
||||||
|
@include transition(border 0.15s ease-in-out, color 0.15s ease-in-out);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after{
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
margin-left: -20px;
|
||||||
|
padding-left: 3px;
|
||||||
|
padding-top: 1px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: $input-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"],
|
||||||
|
input[type="radio"] {
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
&:focus + label::before{
|
||||||
|
@include tab-focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
&:checked + label::after{
|
||||||
|
font-family: $font-family-icon;
|
||||||
|
content: $check-icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:indeterminate + label::after{
|
||||||
|
display: block;
|
||||||
|
content: "";
|
||||||
|
width: 10px;
|
||||||
|
height: 3px;
|
||||||
|
background-color: #555555;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-left: -16.5px;
|
||||||
|
margin-top: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled + label{
|
||||||
|
opacity: 0.65;
|
||||||
|
|
||||||
|
&::before{
|
||||||
|
background-color: $input-bg-disabled;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&.abc-checkbox-circle label::before{
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.checkbox-inline{
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include checkbox-variant('.abc-checkbox-primary', $brand-primary);
|
||||||
|
@include checkbox-variant('.abc-checkbox-danger', $brand-danger);
|
||||||
|
@include checkbox-variant('.abc-checkbox-info', $brand-info);
|
||||||
|
@include checkbox-variant('.abc-checkbox-warning', $brand-warning);
|
||||||
|
@include checkbox-variant('.abc-checkbox-success', $brand-success);
|
||||||
|
|
||||||
|
|
||||||
|
@include checkbox-variant-indeterminate('.abc-checkbox-primary', $brand-primary);
|
||||||
|
@include checkbox-variant-indeterminate('.abc-checkbox-danger', $brand-danger);
|
||||||
|
@include checkbox-variant-indeterminate('.abc-checkbox-info', $brand-info);
|
||||||
|
@include checkbox-variant-indeterminate('.abc-checkbox-warning', $brand-warning);
|
||||||
|
@include checkbox-variant-indeterminate('.abc-checkbox-success', $brand-success);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Radios
|
||||||
|
// --------------------------------------------------
|
||||||
|
|
||||||
|
@mixin radio-variant($parent, $color) {
|
||||||
|
#{$parent} input[type="radio"]{
|
||||||
|
+ label{
|
||||||
|
&::after{
|
||||||
|
background-color: $color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:checked + label{
|
||||||
|
&::before {
|
||||||
|
border-color: $color;
|
||||||
|
}
|
||||||
|
&::after{
|
||||||
|
background-color: $color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.abc-radio{
|
||||||
|
padding-left: 20px;
|
||||||
|
|
||||||
|
label{
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 5px;
|
||||||
|
|
||||||
|
&::before{
|
||||||
|
cursor: pointer;
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
width: 17px;
|
||||||
|
height: 17px;
|
||||||
|
left: 0;
|
||||||
|
margin-left: -20px;
|
||||||
|
border: 1px solid $input-border-color;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #fff;
|
||||||
|
@include transition(border 0.15s ease-in-out);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after{
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
content: " ";
|
||||||
|
width: 11px;
|
||||||
|
height: 11px;
|
||||||
|
left: 3px;
|
||||||
|
top: 3px;
|
||||||
|
margin-left: -20px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: $input-color;
|
||||||
|
transform: scale(0, 0);
|
||||||
|
|
||||||
|
transition: transform .1s cubic-bezier(.8,-0.33,.2,1.33);
|
||||||
|
//curve - http://cubic-bezier.com/#.8,-0.33,.2,1.33
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"]{
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
&:focus + label::before{
|
||||||
|
@include tab-focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
&:checked + label::after{
|
||||||
|
transform: scale(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled + label{
|
||||||
|
opacity: 0.65;
|
||||||
|
|
||||||
|
&::before{
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&.radio-inline{
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include radio-variant('.abc-radio-primary', $brand-primary);
|
||||||
|
@include radio-variant('.abc-radio-danger', $brand-danger);
|
||||||
|
@include radio-variant('.abc-radio-info', $brand-info);
|
||||||
|
@include radio-variant('.abc-radio-warning', $brand-warning);
|
||||||
|
@include radio-variant('.abc-radio-success', $brand-success);
|
||||||
|
|
||||||
|
|
||||||
|
input[type="checkbox"],
|
||||||
|
input[type="radio"] {
|
||||||
|
&.styled:checked + label:after {
|
||||||
|
font-family: $font-family-icon;
|
||||||
|
content: $check-icon;
|
||||||
|
}
|
||||||
|
.styled:checked + label {
|
||||||
|
&::before {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
&::after {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
PlexRequests.UI/Content/custom.min.css
vendored
1
PlexRequests.UI/Content/custom.min.css
vendored
|
@ -1 +0,0 @@
|
||||||
@media(min-width:768px){.row{position:relative;}.bottom-align-text{position:absolute;bottom:0;right:0;}}@media(max-width:48em){.home{padding-top:1rem;}}@media(min-width:48em){.home{padding-top:4rem;}}.btn{border-radius:.25rem !important;}.multiSelect{background-color:#4e5d6c;}.form-control-custom{background-color:#4e5d6c !important;color:#fff !important;border-radius:0;box-shadow:0 0 0 !important;}h1{font-size:3.5rem !important;font-weight:600 !important;}.request-title{margin-top:0 !important;font-size:1.9rem !important;}p{font-size:1.1rem !important;}label{display:inline-block !important;margin-bottom:.5rem !important;font-size:16px !important;}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{background:#4e5d6c;}.navbar .nav a .fa,.dropdown-menu a .fa{font-size:130%;top:1px;position:relative;display:inline-block;margin-right:5px;}.dropdown-menu a .fa{top:2px;}.btn-danger-outline{color:#d9534f !important;background-color:transparent;background-image:none;border-color:#d9534f !important;}.btn-danger-outline:focus,.btn-danger-outline.focus,.btn-danger-outline:active,.btn-danger-outline.active,.btn-danger-outline:hover,.open>.btn-danger-outline.dropdown-toggle{color:#fff !important;background-color:#d9534f !important;border-color:#d9534f !important;}.btn-primary-outline{color:#ff761b !important;background-color:transparent;background-image:none;border-color:#ff761b !important;}.btn-primary-outline:focus,.btn-primary-outline.focus,.btn-primary-outline:active,.btn-primary-outline.active,.btn-primary-outline:hover,.open>.btn-primary-outline.dropdown-toggle{color:#fff !important;background-color:#df691a !important;border-color:#df691a !important;}.btn-info-outline{color:#5bc0de !important;background-color:transparent;background-image:none;border-color:#5bc0de !important;}.btn-info-outline:focus,.btn-info-outline.focus,.btn-info-outline:active,.btn-info-outline.active,.btn-info-outline:hover,.open>.btn-info-outline.dropdown-toggle{color:#fff !important;background-color:#5bc0de !important;border-color:#5bc0de !important;}.btn-warning-outline{color:#f0ad4e !important;background-color:transparent;background-image:none;border-color:#f0ad4e !important;}.btn-warning-outline:focus,.btn-warning-outline.focus,.btn-warning-outline:active,.btn-warning-outline.active,.btn-warning-outline:hover,.open>.btn-warning-outline.dropdown-toggle{color:#fff !important;background-color:#f0ad4e !important;border-color:#f0ad4e !important;}.btn-success-outline{color:#5cb85c !important;background-color:transparent;background-image:none;border-color:#5cb85c !important;}.btn-success-outline:focus,.btn-success-outline.focus,.btn-success-outline:active,.btn-success-outline.active,.btn-success-outline:hover,.open>.btn-success-outline.dropdown-toggle{color:#fff !important;background-color:#5cb85c !important;border-color:#5cb85c !important;}#movieList .mix{display:none;}#tvList .mix{display:none;}.scroll-top-wrapper{position:fixed;opacity:0;visibility:hidden;overflow:hidden;text-align:center;z-index:99999999;background-color:#4e5d6c;color:#eee;width:50px;height:48px;line-height:48px;right:30px;bottom:30px;padding-top:2px;border-top-left-radius:10px;border-top-right-radius:10px;border-bottom-right-radius:10px;border-bottom-left-radius:10px;-webkit-transition:all .5s ease-in-out;-moz-transition:all .5s ease-in-out;-ms-transition:all .5s ease-in-out;-o-transition:all .5s ease-in-out;transition:all .5s ease-in-out;}.scroll-top-wrapper:hover{background-color:#637689;}.scroll-top-wrapper.show{visibility:visible;cursor:pointer;opacity:1;}.scroll-top-wrapper i.fa{line-height:inherit;}.no-search-results{text-align:center;}.no-search-results .no-search-results-icon{font-size:10em;color:#4e5d6c;}.no-search-results .no-search-results-text{margin:20px 0;color:#ccc;}.form-control-search{padding:13px 105px 13px 16px;height:100%;}.form-control-withbuttons{padding-right:105px;}.input-group-addon .btn-group{position:absolute;right:45px;z-index:3;top:13px;box-shadow:0 0 0;}.input-group-addon .btn-group .btn{border:1px solid rgba(255,255,255,.7) !important;padding:3px 12px;color:rgba(255,255,255,.7) !important;}.btn-split .btn{border-radius:0 !important;}.btn-split .btn:not(.dropdown-toggle){border-radius:.25rem 0 0 .25rem !important;}.btn-split .btn.dropdown-toggle{border-radius:0 .25rem .25rem 0 !important;padding:12px 8px;}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,348 +1,462 @@
|
||||||
Handlebars.registerHelper('if_eq', function (a, b, opts) {
|
Handlebars.registerHelper('if_eq', function (a, b, opts) {
|
||||||
if (a == b)
|
if (a == b)
|
||||||
return opts.fn(this);
|
return opts.fn(this);
|
||||||
else
|
else
|
||||||
return opts.inverse(this);
|
return opts.inverse(this);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$(function () {
|
$(function () {
|
||||||
|
|
||||||
var searchSource = $("#search-template").html();
|
var searchSource = $("#search-template").html();
|
||||||
var musicSource = $("#music-template").html();
|
var seasonsSource = $("#seasons-template").html();
|
||||||
var searchTemplate = Handlebars.compile(searchSource);
|
var musicSource = $("#music-template").html();
|
||||||
var musicTemplate = Handlebars.compile(musicSource);
|
var searchTemplate = Handlebars.compile(searchSource);
|
||||||
|
var musicTemplate = Handlebars.compile(musicSource);
|
||||||
var base = $('#baseUrl').text();
|
var seasonsTemplate = Handlebars.compile(seasonsSource);
|
||||||
|
|
||||||
var searchTimer = 0;
|
var base = $('#baseUrl').text();
|
||||||
|
|
||||||
// fix for selecting a default tab
|
var searchTimer = 0;
|
||||||
var $tabs = $('#nav-tabs').children('li');
|
|
||||||
if ($tabs.filter(function (li) { return $(li).hasClass('active') }).length <= 0) {
|
// fix for selecting a default tab
|
||||||
$tabs.first().children('a:first-child').tab('show');
|
var $tabs = $('#nav-tabs').children('li');
|
||||||
}
|
if ($tabs.filter(function (li) { return $(li).hasClass('active') }).length <= 0) {
|
||||||
|
$tabs.first().children('a:first-child').tab('show');
|
||||||
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
}
|
||||||
focusSearch($($(e.target).attr('href')));
|
|
||||||
});
|
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||||
focusSearch($('li.active a', '#nav-tabs').first().attr('href'));
|
focusSearch($($(e.target).attr('href')));
|
||||||
|
});
|
||||||
// Type in movie search
|
focusSearch($('li.active a', '#nav-tabs').first().attr('href'));
|
||||||
$("#movieSearchContent").on("input", function () {
|
|
||||||
if (searchTimer) {
|
// Get the user notification setting
|
||||||
clearTimeout(searchTimer);
|
var url = createBaseUrl(base, '/search/notifyuser/');
|
||||||
}
|
$.ajax({
|
||||||
searchTimer = setTimeout(movieSearch, 400);
|
type: "get",
|
||||||
|
url: url,
|
||||||
});
|
dataType: "json",
|
||||||
|
success: function (response) {
|
||||||
$('#moviesComingSoon').on('click', function (e) {
|
$('#notifyUser').prop("checked", response);
|
||||||
e.preventDefault();
|
},
|
||||||
moviesComingSoon();
|
error: function (e) {
|
||||||
});
|
console.log(e);
|
||||||
|
generateNotify("Something went wrong!", "danger");
|
||||||
$('#moviesInTheaters').on('click', function (e) {
|
}
|
||||||
e.preventDefault();
|
});
|
||||||
moviesInTheaters();
|
|
||||||
});
|
// Type in movie search
|
||||||
|
$("#movieSearchContent").on("input", function () {
|
||||||
// Type in TV search
|
if (searchTimer) {
|
||||||
$("#tvSearchContent").on("input", function () {
|
clearTimeout(searchTimer);
|
||||||
if (searchTimer) {
|
}
|
||||||
clearTimeout(searchTimer);
|
searchTimer = setTimeout(movieSearch, 400);
|
||||||
}
|
|
||||||
searchTimer = setTimeout(tvSearch, 400);
|
});
|
||||||
});
|
|
||||||
|
$('#moviesComingSoon').on('click', function (e) {
|
||||||
// Click TV dropdown option
|
e.preventDefault();
|
||||||
$(document).on("click", ".dropdownTv", function (e) {
|
moviesComingSoon();
|
||||||
e.preventDefault();
|
});
|
||||||
var buttonId = e.target.id;
|
|
||||||
if ($("#" + buttonId).attr('disabled')) {
|
$('#moviesInTheaters').on('click', function (e) {
|
||||||
return;
|
e.preventDefault();
|
||||||
}
|
moviesInTheaters();
|
||||||
|
});
|
||||||
$("#" + buttonId).prop("disabled", true);
|
|
||||||
loadingButton(buttonId, "primary");
|
// Type in TV search
|
||||||
|
$("#tvSearchContent").on("input", function () {
|
||||||
|
if (searchTimer) {
|
||||||
var $form = $('#form' + buttonId);
|
clearTimeout(searchTimer);
|
||||||
var data = $form.serialize();
|
}
|
||||||
var seasons = $(this).attr("season-select");
|
searchTimer = setTimeout(tvSearch, 400);
|
||||||
if (seasons === "2") {
|
});
|
||||||
// Send over the latest
|
|
||||||
data = data + "&seasons=latest";
|
// Click TV dropdown option
|
||||||
}
|
$(document).on("click", ".dropdownTv", function (e) {
|
||||||
if (seasons === "1") {
|
e.preventDefault();
|
||||||
// Send over the first season
|
var buttonId = e.target.id;
|
||||||
data = data + "&seasons=first";
|
if ($("#" + buttonId).attr('disabled')) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var type = $form.prop('method');
|
$("#" + buttonId).prop("disabled", true);
|
||||||
var url = $form.prop('action');
|
loadingButton(buttonId, "primary");
|
||||||
|
|
||||||
sendRequestAjax(data, type, url, buttonId);
|
|
||||||
});
|
var $form = $('#form' + buttonId);
|
||||||
|
var data = $form.serialize();
|
||||||
// Search Music
|
var seasons = $(this).attr("season-select");
|
||||||
$("#musicSearchContent").on("input", function () {
|
if (seasons === "2") {
|
||||||
if (searchTimer) {
|
// Send over the latest
|
||||||
clearTimeout(searchTimer);
|
data = data + "&seasons=latest";
|
||||||
}
|
}
|
||||||
searchTimer = setTimeout(musicSearch, 400);
|
if (seasons === "1") {
|
||||||
|
// Send over the first season
|
||||||
});
|
data = data + "&seasons=first";
|
||||||
|
} if (seasons === "0") {
|
||||||
// Click Request for movie
|
// Send over the first season
|
||||||
$(document).on("click", ".requestMovie", function (e) {
|
data = data + "&seasons=all";
|
||||||
e.preventDefault();
|
}
|
||||||
var buttonId = e.target.id;
|
|
||||||
if ($("#" + buttonId).attr('disabled')) {
|
var type = $form.prop('method');
|
||||||
return;
|
var url = $form.prop('action');
|
||||||
}
|
|
||||||
|
sendRequestAjax(data, type, url, buttonId);
|
||||||
$("#" + buttonId).prop("disabled", true);
|
});
|
||||||
loadingButton(buttonId, "primary");
|
|
||||||
|
// Search Music
|
||||||
|
$("#musicSearchContent").on("input", function () {
|
||||||
var $form = $('#form' + buttonId);
|
if (searchTimer) {
|
||||||
|
clearTimeout(searchTimer);
|
||||||
var type = $form.prop('method');
|
}
|
||||||
var url = $form.prop('action');
|
searchTimer = setTimeout(musicSearch, 400);
|
||||||
var data = $form.serialize();
|
|
||||||
|
});
|
||||||
sendRequestAjax(data, type, url, buttonId);
|
|
||||||
|
// Click Request for movie
|
||||||
});
|
$(document).on("click", ".requestMovie", function (e) {
|
||||||
|
e.preventDefault();
|
||||||
// Click Request for album
|
var buttonId = e.target.id;
|
||||||
$(document).on("click", ".requestAlbum", function (e) {
|
if ($("#" + buttonId).attr('disabled')) {
|
||||||
e.preventDefault();
|
return;
|
||||||
var buttonId = e.target.id;
|
}
|
||||||
if ($("#" + buttonId).attr('disabled')) {
|
|
||||||
return;
|
$("#" + buttonId).prop("disabled", true);
|
||||||
}
|
loadingButton(buttonId, "primary");
|
||||||
|
|
||||||
$("#" + buttonId).prop("disabled", true);
|
|
||||||
loadingButton(buttonId, "primary");
|
var $form = $('#form' + buttonId);
|
||||||
|
|
||||||
|
var type = $form.prop('method');
|
||||||
var $form = $('#form' + buttonId);
|
var url = $form.prop('action');
|
||||||
|
var data = $form.serialize();
|
||||||
var type = $form.prop('method');
|
|
||||||
var url = $form.prop('action');
|
sendRequestAjax(data, type, url, buttonId);
|
||||||
var data = $form.serialize();
|
|
||||||
|
});
|
||||||
sendRequestAjax(data, type, url, buttonId);
|
|
||||||
});
|
// Click Request for album
|
||||||
|
$(document).on("click", ".requestAlbum", function (e) {
|
||||||
function focusSearch($content) {
|
e.preventDefault();
|
||||||
if ($content.length > 0) {
|
var buttonId = e.target.id;
|
||||||
$('input[type=text].form-control', $content).first().focus();
|
if ($("#" + buttonId).attr('disabled')) {
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendRequestAjax(data, type, url, buttonId) {
|
$("#" + buttonId).prop("disabled", true);
|
||||||
$.ajax({
|
loadingButton(buttonId, "primary");
|
||||||
type: type,
|
|
||||||
url: url,
|
|
||||||
data: data,
|
var $form = $('#form' + buttonId);
|
||||||
dataType: "json",
|
|
||||||
success: function (response) {
|
var type = $form.prop('method');
|
||||||
console.log(response);
|
var url = $form.prop('action');
|
||||||
if (response.result === true) {
|
var data = $form.serialize();
|
||||||
generateNotify(response.message || "Success!", "success");
|
|
||||||
|
sendRequestAjax(data, type, url, buttonId);
|
||||||
$('#' + buttonId).html("<i class='fa fa-check'></i> Requested");
|
});
|
||||||
$('#' + buttonId).removeClass("btn-primary-outline");
|
|
||||||
$('#' + buttonId).removeAttr("data-toggle");
|
// Enable/Disable user notifications
|
||||||
$('#' + buttonId).addClass("btn-success-outline");
|
$('#saveNotificationSettings')
|
||||||
} else {
|
.click(function (e) {
|
||||||
generateNotify(response.message, "warning");
|
e.preventDefault();
|
||||||
$('#' + buttonId).html("<i class='fa fa-plus'></i> Request");
|
var url = createBaseUrl(base, '/search/notifyuser/');
|
||||||
$('#' + buttonId).attr("data-toggle", "dropdown");
|
var checked = $('#notifyUser').prop('checked');
|
||||||
$("#" + buttonId).removeAttr("disabled");
|
$.ajax({
|
||||||
}
|
type: "post",
|
||||||
},
|
url: url,
|
||||||
error: function (e) {
|
data: { notify: checked },
|
||||||
console.log(e);
|
dataType: "json",
|
||||||
generateNotify("Something went wrong!", "danger");
|
success: function (response) {
|
||||||
}
|
console.log(response);
|
||||||
});
|
if (response.result === true) {
|
||||||
}
|
generateNotify(response.message || "Success!", "success");
|
||||||
|
} else {
|
||||||
function movieSearch() {
|
generateNotify(response.message, "warning");
|
||||||
var query = $("#movieSearchContent").val();
|
}
|
||||||
var url = createBaseUrl(base, '/search/movie/');
|
},
|
||||||
query ? getMovies(url + query) : resetMovies();
|
error: function (e) {
|
||||||
}
|
console.log(e);
|
||||||
|
generateNotify("Something went wrong!", "danger");
|
||||||
function moviesComingSoon() {
|
}
|
||||||
var url = createBaseUrl(base, '/search/movie/upcoming');
|
});
|
||||||
getMovies(url);
|
});
|
||||||
}
|
|
||||||
|
function focusSearch($content) {
|
||||||
function moviesInTheaters() {
|
if ($content.length > 0) {
|
||||||
var url = createBaseUrl(base, '/search/movie/playing');
|
$('input[type=text].form-control', $content).first().focus();
|
||||||
getMovies(url);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMovies(url) {
|
function sendRequestAjax(data, type, url, buttonId) {
|
||||||
resetMovies();
|
$.ajax({
|
||||||
|
type: type,
|
||||||
$('#movieSearchButton').attr("class", "fa fa-spinner fa-spin");
|
url: url,
|
||||||
$.ajax(url).success(function (results) {
|
data: data,
|
||||||
if (results.length > 0) {
|
dataType: "json",
|
||||||
results.forEach(function (result) {
|
success: function (response) {
|
||||||
var context = buildMovieContext(result);
|
console.log(response);
|
||||||
|
if (response.result === true) {
|
||||||
var html = searchTemplate(context);
|
generateNotify(response.message || "Success!", "success");
|
||||||
$("#movieList").append(html);
|
|
||||||
});
|
$('#' + buttonId).html("<i class='fa fa-check'></i> Requested");
|
||||||
}
|
$('#' + buttonId).removeClass("btn-primary-outline");
|
||||||
else {
|
$('#' + buttonId).removeAttr("data-toggle");
|
||||||
$("#movieList").html(noResultsHtml);
|
$('#' + buttonId).addClass("btn-success-outline");
|
||||||
}
|
} else {
|
||||||
$('#movieSearchButton').attr("class", "fa fa-search");
|
generateNotify(response.message, "warning");
|
||||||
});
|
$('#' + buttonId).html("<i class='fa fa-plus'></i> Request");
|
||||||
};
|
$('#' + buttonId).attr("data-toggle", "dropdown");
|
||||||
|
$("#" + buttonId).removeAttr("disabled");
|
||||||
function resetMovies() {
|
}
|
||||||
$("#movieList").html("");
|
},
|
||||||
}
|
error: function (e) {
|
||||||
|
console.log(e);
|
||||||
function tvSearch() {
|
generateNotify("Something went wrong!", "danger");
|
||||||
var query = $("#tvSearchContent").val();
|
}
|
||||||
|
});
|
||||||
var url = createBaseUrl(base, '/search/tv/');
|
}
|
||||||
query ? getTvShows(url + query) : resetTvShows();
|
|
||||||
}
|
function movieSearch() {
|
||||||
|
var query = $("#movieSearchContent").val();
|
||||||
function getTvShows(url) {
|
var url = createBaseUrl(base, '/search/movie/');
|
||||||
resetTvShows();
|
query ? getMovies(url + query) : resetMovies();
|
||||||
|
}
|
||||||
$('#tvSearchButton').attr("class", "fa fa-spinner fa-spin");
|
|
||||||
$.ajax(url).success(function (results) {
|
function moviesComingSoon() {
|
||||||
if (results.length > 0) {
|
var url = createBaseUrl(base, '/search/movie/upcoming');
|
||||||
results.forEach(function (result) {
|
getMovies(url);
|
||||||
var context = buildTvShowContext(result);
|
}
|
||||||
var html = searchTemplate(context);
|
|
||||||
$("#tvList").append(html);
|
function moviesInTheaters() {
|
||||||
});
|
var url = createBaseUrl(base, '/search/movie/playing');
|
||||||
}
|
getMovies(url);
|
||||||
else {
|
}
|
||||||
$("#tvList").html(noResultsHtml);
|
|
||||||
}
|
function getMovies(url) {
|
||||||
$('#tvSearchButton').attr("class", "fa fa-search");
|
resetMovies();
|
||||||
});
|
|
||||||
};
|
$('#movieSearchButton').attr("class", "fa fa-spinner fa-spin");
|
||||||
|
$.ajax(url).success(function (results) {
|
||||||
function resetTvShows() {
|
if (results.length > 0) {
|
||||||
$("#tvList").html("");
|
results.forEach(function (result) {
|
||||||
}
|
var context = buildMovieContext(result);
|
||||||
|
|
||||||
function musicSearch() {
|
var html = searchTemplate(context);
|
||||||
var url = createBaseUrl(base, '/search/music/');
|
$("#movieList").append(html);
|
||||||
var query = $("#musicSearchContent").val();
|
});
|
||||||
query ? getMusic(url + query) : resetMusic();
|
}
|
||||||
}
|
else {
|
||||||
|
$("#movieList").html(noResultsHtml);
|
||||||
function getMusic(url) {
|
}
|
||||||
resetMusic();
|
$('#movieSearchButton').attr("class", "fa fa-search");
|
||||||
|
});
|
||||||
$('#musicSearchButton').attr("class", "fa fa-spinner fa-spin");
|
};
|
||||||
$.ajax(url).success(function (results) {
|
|
||||||
if (results.length > 0) {
|
function resetMovies() {
|
||||||
results.forEach(function (result) {
|
$("#movieList").html("");
|
||||||
var context = buildMusicContext(result);
|
}
|
||||||
|
|
||||||
var html = musicTemplate(context);
|
function tvSearch() {
|
||||||
$("#musicList").append(html);
|
var query = $("#tvSearchContent").val();
|
||||||
getCoverArt(context.id);
|
|
||||||
});
|
var url = createBaseUrl(base, '/search/tv/');
|
||||||
}
|
query ? getTvShows(url + query) : resetTvShows();
|
||||||
else {
|
}
|
||||||
$("#musicList").html(noResultsMusic);
|
|
||||||
}
|
function getTvShows(url) {
|
||||||
$('#musicSearchButton').attr("class", "fa fa-search");
|
resetTvShows();
|
||||||
});
|
|
||||||
};
|
$('#tvSearchButton').attr("class", "fa fa-spinner fa-spin");
|
||||||
|
$.ajax(url).success(function (results) {
|
||||||
function resetMusic() {
|
if (results.length > 0) {
|
||||||
$("#musicList").html("");
|
results.forEach(function (result) {
|
||||||
}
|
var context = buildTvShowContext(result);
|
||||||
|
var html = searchTemplate(context);
|
||||||
function getCoverArt(artistId) {
|
$("#tvList").append(html);
|
||||||
|
});
|
||||||
var url = createBaseUrl(base, '/search/music/coverart/');
|
}
|
||||||
$.ajax(url + artistId).success(function (result) {
|
else {
|
||||||
if (result) {
|
$("#tvList").html(noResultsHtml);
|
||||||
$('#' + artistId + "imageDiv").html(" <img class='img-responsive' src='" + result + "' width='150' alt='poster'>");
|
}
|
||||||
}
|
$('#tvSearchButton').attr("class", "fa fa-search");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function buildMovieContext(result) {
|
function resetTvShows() {
|
||||||
var date = new Date(result.releaseDate);
|
$("#tvList").html("");
|
||||||
var year = date.getFullYear();
|
}
|
||||||
var context = {
|
|
||||||
posterPath: result.posterPath,
|
function musicSearch() {
|
||||||
id: result.id,
|
var url = createBaseUrl(base, '/search/music/');
|
||||||
title: result.title,
|
var query = $("#musicSearchContent").val();
|
||||||
overview: result.overview,
|
query ? getMusic(url + query) : resetMusic();
|
||||||
voteCount: result.voteCount,
|
}
|
||||||
voteAverage: result.voteAverage,
|
|
||||||
year: year,
|
function getMusic(url) {
|
||||||
type: "movie",
|
resetMusic();
|
||||||
imdb: result.imdbId,
|
|
||||||
requested: result.requested,
|
$('#musicSearchButton').attr("class", "fa fa-spinner fa-spin");
|
||||||
approved: result.approved,
|
$.ajax(url).success(function (results) {
|
||||||
available: result.available
|
if (results.length > 0) {
|
||||||
};
|
results.forEach(function (result) {
|
||||||
|
var context = buildMusicContext(result);
|
||||||
return context;
|
|
||||||
}
|
var html = musicTemplate(context);
|
||||||
|
$("#musicList").append(html);
|
||||||
function buildTvShowContext(result) {
|
getCoverArt(context.id);
|
||||||
var date = new Date(result.firstAired);
|
});
|
||||||
var year = date.getFullYear();
|
}
|
||||||
var context = {
|
else {
|
||||||
posterPath: result.banner,
|
$("#musicList").html(noResultsMusic);
|
||||||
id: result.id,
|
}
|
||||||
title: result.seriesName,
|
$('#musicSearchButton').attr("class", "fa fa-search");
|
||||||
overview: result.overview,
|
});
|
||||||
year: year,
|
};
|
||||||
type: "tv",
|
|
||||||
imdb: result.imdbId,
|
function resetMusic() {
|
||||||
requested: result.requested,
|
$("#musicList").html("");
|
||||||
approved: result.approved,
|
}
|
||||||
available: result.available
|
|
||||||
};
|
function getCoverArt(artistId) {
|
||||||
return context;
|
|
||||||
}
|
var url = createBaseUrl(base, '/search/music/coverart/');
|
||||||
|
$.ajax(url + artistId).success(function (result) {
|
||||||
function buildMusicContext(result) {
|
if (result) {
|
||||||
|
$('#' + artistId + "imageDiv").html(" <img class='img-responsive' src='" + result + "' width='150' alt='poster'>");
|
||||||
var context = {
|
}
|
||||||
id: result.id,
|
});
|
||||||
title: result.title,
|
};
|
||||||
overview: result.overview,
|
|
||||||
year: result.releaseDate,
|
function buildMovieContext(result) {
|
||||||
type: "album",
|
var date = new Date(result.releaseDate);
|
||||||
trackCount: result.trackCount,
|
var year = date.getFullYear();
|
||||||
coverArtUrl: result.coverArtUrl,
|
var context = {
|
||||||
artist: result.artist,
|
posterPath: result.posterPath,
|
||||||
releaseType: result.releaseType,
|
id: result.id,
|
||||||
country: result.country,
|
title: result.title,
|
||||||
requested: result.requested,
|
overview: result.overview,
|
||||||
approved: result.approved,
|
voteCount: result.voteCount,
|
||||||
available: result.available
|
voteAverage: result.voteAverage,
|
||||||
};
|
year: year,
|
||||||
|
type: "movie",
|
||||||
return context;
|
imdb: result.imdbId,
|
||||||
}
|
requested: result.requested,
|
||||||
|
approved: result.approved,
|
||||||
});
|
available: result.available
|
||||||
|
};
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildTvShowContext(result) {
|
||||||
|
var date = new Date(result.firstAired);
|
||||||
|
var year = date.getFullYear();
|
||||||
|
var context = {
|
||||||
|
posterPath: result.banner,
|
||||||
|
id: result.id,
|
||||||
|
title: result.seriesName,
|
||||||
|
overview: result.overview,
|
||||||
|
year: year,
|
||||||
|
type: "tv",
|
||||||
|
imdb: result.imdbId,
|
||||||
|
requested: result.requested,
|
||||||
|
approved: result.approved,
|
||||||
|
available: result.available
|
||||||
|
};
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildMusicContext(result) {
|
||||||
|
|
||||||
|
var context = {
|
||||||
|
id: result.id,
|
||||||
|
title: result.title,
|
||||||
|
overview: result.overview,
|
||||||
|
year: result.releaseDate,
|
||||||
|
type: "album",
|
||||||
|
trackCount: result.trackCount,
|
||||||
|
coverArtUrl: result.coverArtUrl,
|
||||||
|
artist: result.artist,
|
||||||
|
releaseType: result.releaseType,
|
||||||
|
country: result.country,
|
||||||
|
requested: result.requested,
|
||||||
|
approved: result.approved,
|
||||||
|
available: result.available
|
||||||
|
};
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#seasonsModal').on('show.bs.modal', function (event) {
|
||||||
|
var button = $(event.relatedTarget); // Button that triggered the modal
|
||||||
|
var id = button.data('identifier'); // Extract info from data-* attributes
|
||||||
|
var url = createBaseUrl(base, '/search/seasons/');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "get",
|
||||||
|
url: url,
|
||||||
|
data: { tvId: id },
|
||||||
|
dataType: "json",
|
||||||
|
success: function (results) {
|
||||||
|
var $content = $("#seasonsBody");
|
||||||
|
$content.html("");
|
||||||
|
$('#selectedSeasonsId').val(id);
|
||||||
|
results.forEach(function(result) {
|
||||||
|
var context = buildSeasonsContext(result);
|
||||||
|
$content.append(seasonsTemplate(context));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function (e) {
|
||||||
|
console.log(e);
|
||||||
|
generateNotify("Something went wrong!", "danger");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function buildSeasonsContext(result) {
|
||||||
|
var context = {
|
||||||
|
id: result
|
||||||
|
};
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#seasonsRequest').click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var tvId = $('#selectedSeasonsId').val();
|
||||||
|
var url = createBaseUrl(base, '/search/seasons/');
|
||||||
|
|
||||||
|
if ($("#" + tvId).attr('disabled')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#" + tvId).prop("disabled", true);
|
||||||
|
loadingButton(tvId, "primary");
|
||||||
|
|
||||||
|
|
||||||
|
var $form = $('#form' + tvId);
|
||||||
|
var data = $form.serialize();
|
||||||
|
var seasonsParam = "&seasons=";
|
||||||
|
|
||||||
|
var $checkedSeasons = $('.selectedSeasons:checkbox:checked');
|
||||||
|
$checkedSeasons.each(function (index, element) {
|
||||||
|
if (index < $checkedSeasons.length -1) {
|
||||||
|
seasonsParam = seasonsParam + element.id + ",";
|
||||||
|
} else {
|
||||||
|
seasonsParam = seasonsParam + element.id;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
data = data + seasonsParam;
|
||||||
|
|
||||||
|
var type = $form.prop('method');
|
||||||
|
var url = $form.prop('action');
|
||||||
|
|
||||||
|
sendRequestAjax(data, type, url, tvId);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -1,79 +1,79 @@
|
||||||
String.prototype.format = String.prototype.f = function () {
|
String.prototype.format = String.prototype.f = function () {
|
||||||
var s = this,
|
var s = this,
|
||||||
i = arguments.length;
|
i = arguments.length;
|
||||||
|
|
||||||
while (i--) {
|
while (i--) {
|
||||||
s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
|
s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Humanize(date) {
|
function Humanize(date) {
|
||||||
var mNow = moment();
|
var mNow = moment();
|
||||||
var mDate = moment(date).local();
|
var mDate = moment(date).local();
|
||||||
return moment.duration(mNow - mDate).humanize() + (mNow.isBefore(mDate) ? ' from now' : ' ago');
|
return moment.duration(mNow - mDate).humanize() + (mNow.isBefore(mDate) ? ' from now' : ' ago');
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateNotify(message, type) {
|
function generateNotify(message, type) {
|
||||||
// type = danger, warning, info, successs
|
// type = danger, warning, info, successs
|
||||||
$.notify({
|
$.notify({
|
||||||
// options
|
// options
|
||||||
message: message
|
message: message
|
||||||
}, {
|
}, {
|
||||||
// settings
|
// settings
|
||||||
type: type,
|
type: type,
|
||||||
animate: {
|
animate: {
|
||||||
enter: 'animated bounceInDown',
|
enter: 'animated bounceInDown',
|
||||||
exit: 'animated bounceOutUp'
|
exit: 'animated bounceOutUp'
|
||||||
},
|
},
|
||||||
newest_on_top: true
|
newest_on_top: true
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkJsonResponse(response) {
|
function checkJsonResponse(response) {
|
||||||
if (response.result === true) {
|
if (response.result === true) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
generateNotify(response.message, "warning");
|
generateNotify(response.message, "warning");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadingButton(elementId, originalCss) {
|
function loadingButton(elementId, originalCss) {
|
||||||
var $element = $('#' + elementId);
|
var $element = $('#' + elementId);
|
||||||
$element.removeClass("btn-" + originalCss + "-outline").addClass("btn-primary-outline").addClass('disabled').html("<i class='fa fa-spinner fa-spin'></i> Loading...");
|
$element.removeClass("btn-" + originalCss + "-outline").addClass("btn-primary-outline").addClass('disabled').html("<i class='fa fa-spinner fa-spin'></i> Loading...");
|
||||||
|
|
||||||
// handle split-buttons
|
// handle split-buttons
|
||||||
var $dropdown = $element.next('.dropdown-toggle')
|
var $dropdown = $element.next('.dropdown-toggle')
|
||||||
if ($dropdown.length > 0) {
|
if ($dropdown.length > 0) {
|
||||||
$dropdown.removeClass("btn-" + originalCss + "-outline").addClass("btn-primary-outline").addClass('disabled');
|
$dropdown.removeClass("btn-" + originalCss + "-outline").addClass("btn-primary-outline").addClass('disabled');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishLoading(elementId, originalCss, html) {
|
function finishLoading(elementId, originalCss, html) {
|
||||||
var $element = $('#' + elementId);
|
var $element = $('#' + elementId);
|
||||||
$element.removeClass("btn-primary-outline").removeClass('disabled').addClass("btn-" + originalCss + "-outline").html(html);
|
$element.removeClass("btn-primary-outline").removeClass('disabled').addClass("btn-" + originalCss + "-outline").html(html);
|
||||||
|
|
||||||
// handle split-buttons
|
// handle split-buttons
|
||||||
var $dropdown = $element.next('.dropdown-toggle')
|
var $dropdown = $element.next('.dropdown-toggle')
|
||||||
if ($dropdown.length > 0) {
|
if ($dropdown.length > 0) {
|
||||||
$dropdown.removeClass("btn-primary-outline").removeClass('disabled').addClass("btn-" + originalCss + "-outline");
|
$dropdown.removeClass("btn-primary-outline").removeClass('disabled').addClass("btn-" + originalCss + "-outline");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createBaseUrl(base, url) {
|
function createBaseUrl(base, url) {
|
||||||
if (base) {
|
if (base) {
|
||||||
if (url.charAt(0) === "/") {
|
if (url.charAt(0) === "/") {
|
||||||
url = "/" + base + url;
|
url = "/" + base + url;
|
||||||
} else {
|
} else {
|
||||||
url = "/" + base + "/" + url;
|
url = "/" + base + "/" + url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
var noResultsHtml = "<div class='no-search-results'>" +
|
var noResultsHtml = "<div class='no-search-results'>" +
|
||||||
"<i class='fa fa-film no-search-results-icon'></i><div class='no-search-results-text'>Sorry, we didn't find any results!</div></div>";
|
"<i class='fa fa-film no-search-results-icon'></i><div class='no-search-results-text'>Sorry, we didn't find any results!</div></div>";
|
||||||
var noResultsMusic = "<div class='no-search-results'>" +
|
var noResultsMusic = "<div class='no-search-results'>" +
|
||||||
"<i class='fa fa-headphones no-search-results-icon'></i><div class='no-search-results-text'>Sorry, we didn't find any results!</div></div>";
|
"<i class='fa fa-headphones no-search-results-icon'></i><div class='no-search-results-text'>Sorry, we didn't find any results!</div></div>";
|
15
PlexRequests.UI/Content/swagger/backbone-min.js
vendored
Normal file
15
PlexRequests.UI/Content/swagger/backbone-min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue