Merge pull request #284 from tidusjar/dev

1.7.5
This commit is contained in:
Jamie 2016-05-29 17:10:46 +01:00
commit f4e75f20ac
64 changed files with 1812 additions and 8322 deletions

View file

@ -26,6 +26,8 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using PlexRequests.Store; using PlexRequests.Store;
namespace PlexRequests.Core namespace PlexRequests.Core
@ -33,13 +35,21 @@ namespace PlexRequests.Core
public interface IRequestService public interface IRequestService
{ {
long AddRequest(RequestedModel model); long AddRequest(RequestedModel model);
Task<int> AddRequestAsync(RequestedModel model);
RequestedModel CheckRequest(int providerId); RequestedModel CheckRequest(int providerId);
Task<RequestedModel> CheckRequestAsync(int providerId);
RequestedModel CheckRequest(string musicId); RequestedModel CheckRequest(string musicId);
Task<RequestedModel> CheckRequestAsync(string musicId);
void DeleteRequest(RequestedModel request); void DeleteRequest(RequestedModel request);
Task DeleteRequestAsync(RequestedModel request);
bool UpdateRequest(RequestedModel model); bool UpdateRequest(RequestedModel model);
Task<bool> UpdateRequestAsync(RequestedModel model);
RequestedModel Get(int id); RequestedModel Get(int id);
Task<RequestedModel> GetAsync(int id);
IEnumerable<RequestedModel> GetAll(); IEnumerable<RequestedModel> GetAll();
Task<IEnumerable<RequestedModel>> GetAllAsync();
bool BatchUpdate(List<RequestedModel> model); bool BatchUpdate(List<RequestedModel> model);
bool BatchDelete(List<RequestedModel> model);
} }
} }

View file

@ -24,12 +24,18 @@
// 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;
namespace PlexRequests.Core namespace PlexRequests.Core
{ {
public interface ISettingsService<T> public interface ISettingsService<T>
{ {
T GetSettings(); T GetSettings();
Task<T> GetSettingsAsync();
bool SaveSettings(T model); bool SaveSettings(T model);
Task<bool> SaveSettingsAsync(T model);
bool Delete(T model); bool Delete(T model);
Task<bool> DeleteAsync(T model);
} }
} }

View file

@ -27,6 +27,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -57,6 +58,19 @@ namespace PlexRequests.Core
return result ? id : -1; return result ? id : -1;
} }
public async Task<int> AddRequestAsync(RequestedModel model)
{
var entity = new RequestBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), ProviderId = model.ProviderId };
var id = await Repo.InsertAsync(entity);
model.Id = id;
entity = new RequestBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), ProviderId = model.ProviderId, Id = id, MusicId = model.MusicBrainzId };
var result = await Repo.UpdateAsync(entity);
return result ? id : -1;
}
public RequestedModel CheckRequest(int providerId) public RequestedModel CheckRequest(int providerId)
{ {
var blobs = Repo.GetAll(); var blobs = Repo.GetAll();
@ -64,6 +78,13 @@ namespace PlexRequests.Core
return blob != null ? ByteConverterHelper.ReturnObject<RequestedModel>(blob.Content) : null; return blob != null ? ByteConverterHelper.ReturnObject<RequestedModel>(blob.Content) : null;
} }
public async Task<RequestedModel> CheckRequestAsync(int providerId)
{
var blobs = await Repo.GetAllAsync();
var blob = blobs.FirstOrDefault(x => x.ProviderId == providerId);
return blob != null ? ByteConverterHelper.ReturnObject<RequestedModel>(blob.Content) : null;
}
public RequestedModel CheckRequest(string musicId) public RequestedModel CheckRequest(string musicId)
{ {
var blobs = Repo.GetAll(); var blobs = Repo.GetAll();
@ -71,18 +92,37 @@ namespace PlexRequests.Core
return blob != null ? ByteConverterHelper.ReturnObject<RequestedModel>(blob.Content) : null; return blob != null ? ByteConverterHelper.ReturnObject<RequestedModel>(blob.Content) : null;
} }
public async Task<RequestedModel> CheckRequestAsync(string musicId)
{
var blobs = await Repo.GetAllAsync();
var blob = blobs.FirstOrDefault(x => x.MusicId == musicId);
return blob != null ? ByteConverterHelper.ReturnObject<RequestedModel>(blob.Content) : null;
}
public void DeleteRequest(RequestedModel request) public void DeleteRequest(RequestedModel request)
{ {
var blob = Repo.Get(request.Id); var blob = Repo.Get(request.Id);
Repo.Delete(blob); Repo.Delete(blob);
} }
public async Task DeleteRequestAsync(RequestedModel request)
{
var blob = await Repo.GetAsync(request.Id);
await Repo.DeleteAsync(blob);
}
public bool UpdateRequest(RequestedModel model) public bool UpdateRequest(RequestedModel model)
{ {
var entity = new RequestBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), ProviderId = model.ProviderId, Id = model.Id }; var entity = new RequestBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), ProviderId = model.ProviderId, Id = model.Id };
return Repo.Update(entity); return Repo.Update(entity);
} }
public async Task<bool> UpdateRequestAsync(RequestedModel model)
{
var entity = new RequestBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), ProviderId = model.ProviderId, Id = model.Id };
return await Repo.UpdateAsync(entity);
}
public RequestedModel Get(int id) public RequestedModel Get(int id)
{ {
var blob = Repo.Get(id); var blob = Repo.Get(id);
@ -94,6 +134,17 @@ namespace PlexRequests.Core
return model; return model;
} }
public async Task<RequestedModel> GetAsync(int id)
{
var blob = await Repo.GetAsync(id);
if (blob == null)
{
return new RequestedModel();
}
var model = ByteConverterHelper.ReturnObject<RequestedModel>(blob.Content);
return model;
}
public IEnumerable<RequestedModel> GetAll() public IEnumerable<RequestedModel> GetAll()
{ {
var blobs = Repo.GetAll(); var blobs = Repo.GetAll();
@ -102,10 +153,24 @@ namespace PlexRequests.Core
.ToList(); .ToList();
} }
public async Task<IEnumerable<RequestedModel>> GetAllAsync()
{
var blobs = await Repo.GetAllAsync();
return blobs.Select(b => Encoding.UTF8.GetString(b.Content))
.Select(JsonConvert.DeserializeObject<RequestedModel>)
.ToList();
}
public bool BatchUpdate(List<RequestedModel> model) public bool BatchUpdate(List<RequestedModel> model)
{ {
var entities = model.Select(m => new RequestBlobs { Type = m.Type, Content = ByteConverterHelper.ReturnBytes(m), ProviderId = m.ProviderId, Id = m.Id }).ToList(); var entities = model.Select(m => new RequestBlobs { Type = m.Type, Content = ByteConverterHelper.ReturnBytes(m), ProviderId = m.ProviderId, Id = m.Id }).ToList();
return Repo.UpdateAll(entities); return Repo.UpdateAll(entities);
} }
public bool BatchDelete(List<RequestedModel> model)
{
var entities = model.Select(m => new RequestBlobs { Type = m.Type, Content = ByteConverterHelper.ReturnBytes(m), ProviderId = m.ProviderId, Id = m.Id }).ToList();
return Repo.DeleteAll(entities);
}
} }
} }

View file

@ -24,6 +24,8 @@
// 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 Newtonsoft.Json; using Newtonsoft.Json;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
@ -62,6 +64,21 @@ namespace PlexRequests.Core
return model; return model;
} }
public async Task<T> GetSettingsAsync()
{
var result = await Repo.GetAsync(EntityName);
if (result == null)
{
return new T();
}
result.Content = DecryptSettings(result);
var obj = string.IsNullOrEmpty(result.Content) ? null : JsonConvert.DeserializeObject<T>(result.Content, SerializerSettings.Settings);
var model = obj;
return model;
}
public bool SaveSettings(T model) public bool SaveSettings(T model)
{ {
var entity = Repo.Get(EntityName); var entity = Repo.Get(EntityName);
@ -88,6 +105,31 @@ namespace PlexRequests.Core
return result; return result;
} }
public async Task<bool> SaveSettingsAsync(T model)
{
var entity = await Repo.GetAsync(EntityName);
if (entity == null)
{
var newEntity = model;
var settings = new GlobalSettings { SettingsName = EntityName, Content = JsonConvert.SerializeObject(newEntity, SerializerSettings.Settings) };
settings.Content = EncryptSettings(settings);
var insertResult = await Repo.InsertAsync(settings);
return insertResult != int.MinValue;
}
var modified = model;
modified.Id = entity.Id;
var globalSettings = new GlobalSettings { SettingsName = EntityName, Content = JsonConvert.SerializeObject(modified, SerializerSettings.Settings), Id = entity.Id };
globalSettings.Content = EncryptSettings(globalSettings);
var result = await Repo.UpdateAsync(globalSettings);
return result;
}
public bool Delete(T model) public bool Delete(T model)
{ {
var entity = Repo.Get(EntityName); var entity = Repo.Get(EntityName);
@ -100,6 +142,17 @@ namespace PlexRequests.Core
return true; return true;
} }
public async Task<bool> DeleteAsync(T model)
{
var entity = Repo.Get(EntityName);
if (entity != null)
{
return await Repo.DeleteAsync(entity);
}
return true;
}
private string EncryptSettings(GlobalSettings settings) private string EncryptSettings(GlobalSettings settings)
{ {
return StringCipher.Encrypt(settings.Content, settings.SettingsName); return StringCipher.Encrypt(settings.Content, settings.SettingsName);

View file

@ -53,7 +53,7 @@ namespace PlexRequests.Core
{ {
CreateDefaultSettingsPage(urlBase); CreateDefaultSettingsPage(urlBase);
} }
var version = CheckSchema(); var version = CheckSchema();
if (version > 0) if (version > 0)
{ {
@ -61,6 +61,10 @@ namespace PlexRequests.Core
{ {
MigrateToVersion1700(); MigrateToVersion1700();
} }
if (version > 1800 && version <= 1899)
{
MigrateToVersion1800();
}
} }
return Db.DbConnection().ConnectionString; return Db.DbConnection().ConnectionString;
@ -73,7 +77,7 @@ namespace PlexRequests.Core
{ {
var productVersion = AssemblyHelper.GetProductVersion(); var productVersion = AssemblyHelper.GetProductVersion();
var trimStatus = new Regex("[^0-9]", RegexOptions.Compiled).Replace(productVersion, string.Empty).PadRight(4, '0'); var trimStatus = new Regex("[^0-9]", RegexOptions.Compiled).Replace(productVersion, string.Empty).PadRight(4, '0');
var version = int.Parse(trimStatus); var version = int.Parse(trimStatus);
var connection = Db.DbConnection(); var connection = Db.DbConnection();
var schema = connection.GetSchemaVersion(); var schema = connection.GetSchemaVersion();
@ -173,5 +177,50 @@ namespace PlexRequests.Core
TableCreation.DropTable(Db.DbConnection(), "User"); TableCreation.DropTable(Db.DbConnection(), "User");
TableCreation.DropTable(Db.DbConnection(), "Log"); TableCreation.DropTable(Db.DbConnection(), "Log");
} }
/// <summary>
/// Migrates to version 1.8.
/// <para>This includes updating the admin account to have all roles.</para>
/// <para>Set the log level to info</para>
/// </summary>
private void MigrateToVersion1800()
{
try
{
var userMapper = new UserMapper(new UserRepository<UsersModel>(Db, new MemoryCacheProvider()));
var users = userMapper.GetUsers();
foreach (var u in users)
{
var claims = new[] { UserClaims.User, UserClaims.Admin, UserClaims.PowerUser };
u.Claims = ByteConverterHelper.ReturnBytes(claims);
userMapper.EditUser(u);
}
}
catch (Exception e)
{
Log.Error(e);
throw;
}
try
{
var settingsService = new SettingsServiceV2<LogSettings>(new SettingsJsonRepository(Db, new MemoryCacheProvider()));
var logSettings = settingsService.GetSettings();
logSettings.Level = LogLevel.Info.Ordinal;
settingsService.SaveSettings(logSettings);
LoggingHelper.ReconfigureLogLevel(LogLevel.FromOrdinal(logSettings.Level));
}
catch (Exception e)
{
Log.Error(e);
throw;
}
}
} }
} }

View file

@ -81,6 +81,16 @@ namespace PlexRequests.Core
return null; return null;
} }
public UsersModel EditUser(UsersModel user)
{
var existingUser = Repo.Get(user.UserGuid);
user.Id = existingUser.Id;
user.UserGuid = existingUser.UserGuid;
Repo.Update(user);
return user;
}
public bool DoUsersExist() public bool DoUsersExist()
{ {
var users = Repo.GetAll(); var users = Repo.GetAll();
@ -88,7 +98,7 @@ namespace PlexRequests.Core
return users.Any(); return users.Any();
} }
public Guid? CreateUser(string username, string password, string[] claims = default(string[])) private Guid? CreateUser(string username, string password, string[] claims = default(string[]))
{ {
var salt = PasswordHasher.GenerateSalt(); var salt = PasswordHasher.GenerateSalt();
@ -108,6 +118,21 @@ namespace PlexRequests.Core
return new Guid(userRecord.UserGuid); return new Guid(userRecord.UserGuid);
} }
public Guid? CreateAdmin(string username, string password)
{
return CreateUser(username, password, new[] { UserClaims.User, UserClaims.PowerUser, UserClaims.Admin });
}
public Guid? CreatePowerUser(string username, string password)
{
return CreateUser(username, password, new[] { UserClaims.User, UserClaims.PowerUser });
}
public Guid? CreateRegularUser(string username, string password)
{
return CreateUser(username, password, new[] { UserClaims.User });
}
public bool UpdatePassword(string username, string oldPassword, string newPassword) public bool UpdatePassword(string username, string oldPassword, string newPassword)
{ {
var users = Repo.GetAll(); var users = Repo.GetAll();
@ -134,15 +159,25 @@ namespace PlexRequests.Core
{ {
return Repo.GetAll(); return Repo.GetAll();
} }
public UsersModel GetUser(Guid userId)
{
var user = Repo.Get(userId.ToString());
return user;
}
} }
public interface ICustomUserMapper public interface ICustomUserMapper
{ {
IEnumerable<UsersModel> GetUsers(); IEnumerable<UsersModel> GetUsers();
Guid? CreateUser(string username, string password, string[] claims = default(string[])); UsersModel GetUser(Guid userId);
UsersModel EditUser(UsersModel user);
bool DoUsersExist(); bool DoUsersExist();
Guid? ValidateUser(string username, string password); Guid? ValidateUser(string username, string password);
bool UpdatePassword(string username, string oldPassword, string newPassword); bool UpdatePassword(string username, string oldPassword, string newPassword);
Guid? CreateAdmin(string username, string password);
Guid? CreatePowerUser(string username, string password);
Guid? CreateRegularUser(string username, string password);
} }
} }

View file

@ -24,7 +24,6 @@
// 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.Diagnostics;
using NUnit.Framework; using NUnit.Framework;
@ -41,7 +40,7 @@ namespace PlexRequests.Helpers.Tests
var hash = PasswordHasher.ComputeHash(password, salt); var hash = PasswordHasher.ComputeHash(password, salt);
Assert.That(hash, Is.Not.EqualTo(password)); Assert.That(hash, Is.Not.EqualTo(password));
var match = PasswordHasher.VerifyPassword(password, salt, hash); var match = PasswordHasher.VerifyPassword(password, salt, hash);
Assert.That(match, Is.True); Assert.That(match, Is.True);

View file

@ -8,7 +8,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>PlexRequests.Helpers.Tests</RootNamespace> <RootNamespace>PlexRequests.Helpers.Tests</RootNamespace>
<AssemblyName>PlexRequests.Helpers.Tests</AssemblyName> <AssemblyName>PlexRequests.Helpers.Tests</AssemblyName>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion> <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
@ -16,6 +16,7 @@
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath> <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest> <IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType> <TestProjectType>UnitTest</TestProjectType>
<TargetFrameworkProfile />
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>

View file

@ -26,7 +26,8 @@
#endregion #endregion
using System; using System;
using System.Linq.Expressions; using System.Collections.Generic;
using NUnit.Framework; using NUnit.Framework;
namespace PlexRequests.Helpers.Tests namespace PlexRequests.Helpers.Tests
@ -35,11 +36,9 @@ namespace PlexRequests.Helpers.Tests
public class UriHelperTests public class UriHelperTests
{ {
[TestCaseSource(nameof(UriData))] [TestCaseSource(nameof(UriData))]
public void CreateUri1(string uri, Uri expected) public Uri CreateUri1(string uri)
{ {
var result = uri.ReturnUri(); return uri.ReturnUri();
Assert.That(result, Is.EqualTo(expected));
} }
[Test] [Test]
@ -52,54 +51,58 @@ namespace PlexRequests.Helpers.Tests
} }
[TestCaseSource(nameof(UriDataWithPort))] [TestCaseSource(nameof(UriDataWithPort))]
public void CreateUri2(string uri, int port, Uri expected) public Uri CreateUri2(string uri, int port)
{ {
var result = uri.ReturnUri(port); return uri.ReturnUri(port);
Assert.That(result, Is.EqualTo(expected));
} }
[TestCaseSource(nameof(UriDataWithSubDir))] [TestCaseSource(nameof(UriDataWithSubDir))]
public void CreateUriWithSubDir(string uri, int port, bool ssl, string subDir, Uri expected) public Uri CreateUriWithSubDir(string uri, int port, bool ssl, string subDir)
{ {
var result = uri.ReturnUriWithSubDir(port, ssl, subDir); return uri.ReturnUriWithSubDir(port, ssl, subDir);
Assert.That(result, Is.EqualTo(expected));
} }
static readonly object[] UriData = private static IEnumerable<TestCaseData> UriData
{ {
new object[] { "google.com", new Uri("http://google.com/"), }, get
new object[] { "http://google.com", new Uri("http://google.com/"), }, {
new object[] { "https://google.com", new Uri("https://google.com/"), }, yield return new TestCaseData("google.com").Returns(new Uri("http://google.com/"));
new object[] { "192.168.1.1", new Uri("http://192.168.1.1")}, yield return new TestCaseData("http://google.com").Returns(new Uri("http://google.com/"));
new object[] { "0.0.0.0:5533", new Uri("http://0.0.0.0:5533")}, yield return new TestCaseData("https://google.com").Returns(new Uri("https://google.com/"));
new object[] {"www.google.com", new Uri("http://www.google.com/")}, yield return new TestCaseData("192.168.1.1").Returns(new Uri("http://192.168.1.1"));
new object[] {"http://www.google.com/", new Uri("http://www.google.com/") }, yield return new TestCaseData("0.0.0.0:5533").Returns(new Uri("http://0.0.0.0:5533"));
new object[] {"https://www.google.com", new Uri("https://www.google.com/") }, yield return new TestCaseData("www.google.com").Returns(new Uri("http://www.google.com/"));
new object[] {"www.google.com:443", new Uri("http://www.google.com:443/") }, yield return new TestCaseData("http://www.google.com/").Returns(new Uri("http://www.google.com/"));
new object[] {"https://www.google.com:443", new Uri("https://www.google.com:443/") }, yield return new TestCaseData("https://www.google.com").Returns(new Uri("https://www.google.com/"));
new object[] {"http://www.google.com:443/id=2", new Uri("http://www.google.com:443/id=2") }, yield return new TestCaseData("www.google.com:443").Returns(new Uri("http://www.google.com:443/"));
new object[] {"www.google.com:4438/id=22", new Uri("http://www.google.com:4438/id=22") } yield return new TestCaseData("https://www.google.com:443").Returns(new Uri("https://www.google.com/"));
}; yield return new TestCaseData("http://www.google.com:443/id=2").Returns(new Uri("http://www.google.com:443/id=2"));
yield return new TestCaseData("www.google.com:4438/id=22").Returns(new Uri("http://www.google.com:4438/id=22"));
}
}
static readonly object[] UriDataWithPort = private static IEnumerable<TestCaseData> UriDataWithPort
{ {
new object[] {"www.google.com", 80, new Uri("http://www.google.com:80/"), }, get
new object[] {"www.google.com", 443, new Uri("http://www.google.com:443/") }, {
new object[] {"http://www.google.com", 443, new Uri("http://www.google.com:443/") }, yield return new TestCaseData("www.google.com", 80).Returns(new Uri("http://www.google.com:80/"));
new object[] {"https://www.google.com", 443, new Uri("https://www.google.com:443/") }, yield return new TestCaseData("www.google.com", 443).Returns(new Uri("http://www.google.com:443/"));
new object[] {"http://www.google.com/id=2", 443, new Uri("http://www.google.com:443/id=2") }, yield return new TestCaseData("http://www.google.com", 443).Returns(new Uri("http://www.google.com:443/"));
new object[] {"http://www.google.com/id=2", 443, new Uri("http://www.google.com:443/id=2") }, yield return new TestCaseData("https://www.google.com", 443).Returns(new Uri("https://www.google.com:443/"));
new object[] {"https://www.google.com/id=2", 443, new Uri("https://www.google.com:443/id=2") }, yield return new TestCaseData("http://www.google.com/id=2", 443).Returns(new Uri("http://www.google.com:443/id=2"));
}; yield return new TestCaseData("https://www.google.com/id=2", 443).Returns(new Uri("https://www.google.com:443/id=2"));
}
}
static readonly object[] UriDataWithSubDir = private static IEnumerable<TestCaseData> UriDataWithSubDir
{ {
new object[] {"www.google.com", 80, false,"test", new Uri("http://www.google.com:80/test"), }, get
new object[] {"www.google.com", 443, false,"test", new Uri("http://www.google.com:443/test") }, {
new object[] {"http://www.google.com", 443, true,"test", new Uri("https://www.google.com:443/test") }, yield return new TestCaseData("www.google.com", 80, false, "test").Returns(new Uri("http://www.google.com:80/test"));
new object[] {"https://www.google.com", 443,true,"test", new Uri("https://www.google.com:443/test") }, yield return new TestCaseData("www.google.com", 443, false, "test").Returns(new Uri("http://www.google.com:443/test"));
}; yield return new TestCaseData("http://www.google.com", 443, true, "test").Returns(new Uri("https://www.google.com:443/test"));
yield return new TestCaseData("https://www.google.com", 443, true, "test").Returns(new Uri("https://www.google.com:443/test"));
}
}
} }
} }

View file

@ -1,15 +1,15 @@
<?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>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" /> <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0"/>
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
</configuration> <startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup></configuration>

View file

@ -25,6 +25,7 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System; using System;
using System.Threading.Tasks;
namespace PlexRequests.Helpers namespace PlexRequests.Helpers
{ {
@ -40,6 +41,7 @@ namespace PlexRequests.Helpers
/// <param name="cacheTime">The amount of time we want to cache the object</param> /// <param name="cacheTime">The amount of time we want to cache the object</param>
/// <returns><see cref="T"/></returns> /// <returns><see cref="T"/></returns>
T GetOrSet<T>(string key, Func<T> itemCallback, int cacheTime = 20) where T : class; T GetOrSet<T>(string key, Func<T> itemCallback, int cacheTime = 20) where T : class;
Task<T> GetOrSetAsync<T>(string key, Func<Task<T>> itemCallback, int cacheTime = 20) where T : class;
/// <summary> /// <summary>
/// Gets the specified item from the cache. /// Gets the specified item from the cache.

View file

@ -27,6 +27,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Runtime.Caching; using System.Runtime.Caching;
using System.Threading.Tasks;
namespace PlexRequests.Helpers namespace PlexRequests.Helpers
{ {
@ -65,6 +66,23 @@ namespace PlexRequests.Helpers
return item.CloneJson(); return item.CloneJson();
} }
public async Task<T> GetOrSetAsync<T>(string key, Func<Task<T>> itemCallback, int cacheTime = 20) where T : class
{
var item = Get<T>(key);
if (item == null)
{
item = await itemCallback();
if (item != null)
{
Set(key, item, cacheTime);
}
}
// Return a copy, not the stored cache reference
// The cached object will not change
return item.CloneJson();
}
/// <summary> /// <summary>
/// Gets the specified item from the cache. /// Gets the specified item from the cache.
/// </summary> /// </summary>

View file

@ -87,7 +87,7 @@ namespace PlexRequests.Services.Jobs
public int[] QueuedIds() public int[] QueuedIds()
{ {
var movies = Cache.Get<CouchPotatoMovies>(CacheKeys.CouchPotatoQueued); var movies = Cache.Get<CouchPotatoMovies>(CacheKeys.CouchPotatoQueued);
return movies?.movies.Select(x => x.info.tmdb_id).ToArray() ?? new int[] { }; return movies?.movies?.Select(x => x.info.tmdb_id).ToArray() ?? new int[] { };
} }
public void Execute(IJobExecutionContext context) public void Execute(IJobExecutionContext context)

View file

@ -24,7 +24,7 @@
// 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 namespace PlexRequests.Services.Jobs
{ {
public static class JobNames public static class JobNames
{ {
@ -33,5 +33,6 @@ namespace PlexRequests.Services
public const string SonarrCacher = "Sonarr Cacher"; public const string SonarrCacher = "Sonarr Cacher";
public const string SrCacher = "SickRage Cacher"; public const string SrCacher = "SickRage Cacher";
public const string PlexChecker = "Plex Availability Cacher"; public const string PlexChecker = "Plex Availability Cacher";
public const string StoreCleanup = "Database Cleanup";
} }
} }

View file

@ -326,33 +326,36 @@ namespace PlexRequests.Services.Jobs
try try
{ {
var plexUser = PlexApi.GetUsers(apiKey); var plexUser = PlexApi.GetUsers(apiKey);
if (plexUser?.User == null || plexUser.User.Length == 0) var userAccount = PlexApi.GetAccount(apiKey);
{
return; var adminUsername = userAccount.Username ?? string.Empty;
}
var users = UserNotifyRepo.GetAll().ToList(); var users = UserNotifyRepo.GetAll().ToList();
Log.Debug("Notifying Users Count {0}", users.Count);
foreach (var model in modelChanged) foreach (var model in modelChanged)
{ {
var selectedUsers = users.Select(x => x.Username).Intersect(model.RequestedUsers); var selectedUsers = users.Select(x => x.Username).Intersect(model.RequestedUsers);
Log.Debug("Selected Users {0}", selectedUsers.DumpJson());
foreach (var user in selectedUsers) foreach (var user in selectedUsers)
{ {
Log.Info("Notifying user {0}", user);
if (user == adminUsername)
{
Log.Info("This user is the Plex server owner");
PublishUserNotification(userAccount.Username, userAccount.Email, model.Title);
return;
}
var email = plexUser.User.FirstOrDefault(x => x.Username == user); var email = plexUser.User.FirstOrDefault(x => x.Username == user);
if (email == null) if (email == null)
{ {
Log.Info("There is no email address for this Plex user, cannot send notification");
// We do not have a plex user that requested this! // We do not have a plex user that requested this!
continue; continue;
} }
var notificationModel = new NotificationModel
{
User = email.Username,
UserEmail = email.Email,
NotificationType = NotificationType.RequestAvailable,
Title = model.Title
};
// Send the notification to the user. Log.Info("Sending notification to: {0} at: {1}, for title: {2}", email.Username, email.Email, model.Title);
Notification.Publish(notificationModel); PublishUserNotification(email.Username, email.Email, model.Title);
} }
} }
} }
@ -362,6 +365,20 @@ namespace PlexRequests.Services.Jobs
} }
} }
private void PublishUserNotification(string username, string email, string title)
{
var notificationModel = new NotificationModel
{
User = username,
UserEmail = email,
NotificationType = NotificationType.RequestAvailable,
Title = title
};
// Send the notification to the user.
Notification.Publish(notificationModel);
}
public void Execute(IJobExecutionContext context) public void Execute(IJobExecutionContext context)
{ {
CheckAndUpdateAll(); CheckAndUpdateAll();

View file

@ -87,7 +87,7 @@ namespace PlexRequests.Services.Jobs
public int[] QueuedIds() public int[] QueuedIds()
{ {
var tv = Cache.Get<SickrageShows>(CacheKeys.SickRageQueued); var tv = Cache.Get<SickrageShows>(CacheKeys.SickRageQueued);
return tv?.data.Values.Select(x => x.tvdbid).ToArray() ?? new int[] { }; return tv?.data?.Values.Select(x => x.tvdbid).ToArray() ?? new int[] { };
} }
public void Execute(IJobExecutionContext context) public void Execute(IJobExecutionContext context)

View file

@ -26,15 +26,11 @@
#endregion #endregion
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using System.Globalization;
using NLog; using NLog;
using PlexRequests.Services.Interfaces; using PlexRequests.Services.Interfaces;
using PlexRequests.Store; using PlexRequests.Store;
using PlexRequests.Store.Models;
using PlexRequests.Store.Repository;
using Quartz; using Quartz;
@ -57,7 +53,7 @@ namespace PlexRequests.Services.Jobs
public void Execute(IJobExecutionContext context) public void Execute(IJobExecutionContext context)
{ {
TakeBackup(); TakeBackup();
Cleanup (); Cleanup();
} }
private void TakeBackup() private void TakeBackup()
@ -81,11 +77,11 @@ namespace PlexRequests.Services.Jobs
try try
{ {
if(DoWeNeedToBackup(backupDir.FullName)) if (DoWeNeedToBackup(backupDir.FullName))
{ {
File.Copy(dbPath, Path.Combine(backupDir.FullName, $"PlexRequests.sqlite_{DateTime.Now.ToString("yyyy-MM-dd hh.mm.ss")}.bak")); File.Copy(dbPath, Path.Combine(backupDir.FullName, $"PlexRequests.sqlite_{DateTime.Now.ToString("yyyy-MM-dd hh.mm.ss")}.bak"));
} }
} }
catch (Exception e) catch (Exception e)
{ {
Log.Warn(e); Log.Warn(e);
@ -98,53 +94,58 @@ namespace PlexRequests.Services.Jobs
} }
private void Cleanup() private void Cleanup()
{ {
Log.Trace("Starting DB Cleanup"); Log.Trace("Starting DB Cleanup");
var dbPath = Sql.CurrentPath; var dbPath = Sql.CurrentPath;
var dir = Path.GetDirectoryName(dbPath); var dir = Path.GetDirectoryName(dbPath);
if (dir == null) if (dir == null)
{ {
Log.Warn("We couldn't find the DB path. We cannot backup."); Log.Warn("We couldn't find the DB path. We cannot backup.");
return; return;
} }
var backupDir = Directory.CreateDirectory(Path.Combine(dir, "Backup")); var backupDir = Directory.CreateDirectory(Path.Combine(dir, "Backup"));
var files = backupDir.GetFiles(); var files = backupDir.GetFiles();
foreach (var file in files) { foreach (var file in files)
var dt = ParseName(file.Name); {
if(dt < DateTime.Now.AddDays(-7)){ var dt = ParseName(file.Name);
try { if (dt < DateTime.Now.AddDays(-7))
{
try
{
File.Delete(file.FullName); File.Delete(file.FullName);
} catch (Exception ex) { }
Log.Error(ex); catch (Exception ex)
} {
} Log.Error(ex);
} }
}
}
} }
private bool DoWeNeedToBackup(string backupPath) private bool DoWeNeedToBackup(string backupPath)
{ {
var files = Directory.GetFiles(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. //TODO Get the latest file and if it's within an hour of DateTime.Now then don't bother backing up.
return true; return true;
} }
private DateTime ParseName(string fileName) private DateTime ParseName(string fileName)
{ {
var names = fileName.Split(new []{'_','.',' '}, StringSplitOptions.RemoveEmptyEntries); var names = fileName.Split(new[] { '_', '.', ' ' }, StringSplitOptions.RemoveEmptyEntries);
if(names.Count() > 1) if (names.Length > 1)
{ {
DateTime parsed; DateTime parsed;
//DateTime.TryParseExcat(names[1], "yyyy-MM-dd hh.mm.ss",CultureInfo.CurrentUICulture, DateTimeStyles.None, out parsed); //DateTime.TryParseExcat(names[1], "yyyy-MM-dd hh.mm.ss",CultureInfo.CurrentUICulture, DateTimeStyles.None, out parsed);
DateTime.TryParse(names[2], out parsed); DateTime.TryParse(names[2], out parsed);
return parsed; return parsed;
} }
return DateTime.MinValue; return DateTime.MinValue;
} }
} }
} }

View file

@ -0,0 +1,82 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: StoreCleanup.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 NLog;
using PlexRequests.Services.Interfaces;
using PlexRequests.Store.Models;
using PlexRequests.Store.Repository;
using Quartz;
namespace PlexRequests.Services.Jobs
{
public class StoreCleanup : IJob
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
public StoreCleanup(IRepository<LogEntity> repo, IJobRecord rec)
{
Repo = repo;
JobRecord = rec;
}
private IJobRecord JobRecord { get; }
private IRepository<LogEntity> Repo { get; }
private void Cleanup()
{
try
{
var items = Repo.GetAll();
var orderedItems = items.Where(x => x.Date < DateTime.Now.AddDays(-7));
foreach (var o in orderedItems)
{
Repo.Delete(o);
}
}
catch (Exception e)
{
Log.Error(e);
}
finally
{
JobRecord.Record(JobNames.StoreCleanup);
}
}
public void Execute(IJobExecutionContext context)
{
Cleanup();
}
}
}

View file

@ -62,7 +62,10 @@ namespace PlexRequests.Services.Notification
var emailSettings = (EmailNotificationSettings)settings; var emailSettings = (EmailNotificationSettings)settings;
if (!ValidateConfiguration(emailSettings)) return; if (!ValidateConfiguration(emailSettings))
{
return;
}
switch (model.NotificationType) switch (model.NotificationType)
{ {
@ -96,15 +99,19 @@ namespace PlexRequests.Services.Notification
private bool ValidateConfiguration(EmailNotificationSettings settings) private bool ValidateConfiguration(EmailNotificationSettings settings)
{ {
if (!settings.Enabled)
{
return false;
}
if (string.IsNullOrEmpty(settings.EmailHost) || string.IsNullOrEmpty(settings.EmailUsername) || string.IsNullOrEmpty(settings.EmailPassword) || string.IsNullOrEmpty(settings.RecipientEmail) || string.IsNullOrEmpty(settings.EmailPort.ToString())) if (string.IsNullOrEmpty(settings.EmailHost) || string.IsNullOrEmpty(settings.EmailUsername) || string.IsNullOrEmpty(settings.EmailPassword) || string.IsNullOrEmpty(settings.RecipientEmail) || string.IsNullOrEmpty(settings.EmailPort.ToString()))
{ {
return false; return false;
} }
if (!settings.EnableUserEmailNotifications)
{
if (!settings.Enabled)
{
return false;
}
}
return true; return true;
} }

View file

@ -76,7 +76,7 @@ namespace PlexRequests.Services.Notification
Observers.TryRemove(notification.NotificationName, out notification); Observers.TryRemove(notification.NotificationName, out notification);
} }
private static async Task NotifyAsync(INotification notification, NotificationModel model) private async Task NotifyAsync(INotification notification, NotificationModel model)
{ {
try try
{ {
@ -89,7 +89,7 @@ namespace PlexRequests.Services.Notification
} }
private static async Task NotifyAsync(INotification notification, NotificationModel model, Settings settings) private async Task NotifyAsync(INotification notification, NotificationModel model, Settings settings)
{ {
try try
{ {

View file

@ -73,6 +73,7 @@
<Compile Include="Jobs\JobRecord.cs" /> <Compile Include="Jobs\JobRecord.cs" />
<Compile Include="Jobs\JobNames.cs" /> <Compile Include="Jobs\JobNames.cs" />
<Compile Include="Jobs\StoreBackup.cs" /> <Compile Include="Jobs\StoreBackup.cs" />
<Compile Include="Jobs\StoreCleanup.cs" />
<Compile Include="Jobs\CouchPotatoCacher.cs" /> <Compile Include="Jobs\CouchPotatoCacher.cs" />
<Compile Include="Jobs\PlexAvailabilityChecker.cs" /> <Compile Include="Jobs\PlexAvailabilityChecker.cs" />
<Compile Include="Jobs\SickRageCacher.cs" /> <Compile Include="Jobs\SickRageCacher.cs" />

View file

@ -31,11 +31,20 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Dapper, Version=1.50.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Dapper.1.50.0-beta8\lib\net45\Dapper.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Dapper.Contrib, Version=1.50.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Dapper.Contrib.1.50.0-beta8\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="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Data.Linq" />
<Reference Include="System.Windows.Forms" /> <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" />
@ -43,12 +52,6 @@
<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"> <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> <HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference> </Reference>
@ -61,6 +64,7 @@
<Compile Include="Entity.cs" /> <Compile Include="Entity.cs" />
<Compile Include="Models\ScheduledJobs.cs" /> <Compile Include="Models\ScheduledJobs.cs" />
<Compile Include="Models\UsersToNotify.cs" /> <Compile Include="Models\UsersToNotify.cs" />
<Compile Include="Repository\BaseGenericRepository.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" />

View file

@ -0,0 +1,179 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: BaseGenericRepository.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 System.Threading.Tasks;
using Dapper.Contrib.Extensions;
using NLog;
using PlexRequests.Helpers;
namespace PlexRequests.Store.Repository
{
public abstract class BaseGenericRepository<T> where T : class
{
protected BaseGenericRepository(ISqliteConfiguration config, ICacheProvider cache)
{
Config = config;
Cache = cache;
}
protected ICacheProvider Cache { get; }
protected ISqliteConfiguration Config { get; }
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
public abstract T Get(string id);
public abstract Task<T> GetAsync(int id);
public abstract T Get(int id);
public abstract Task<T> GetAsync(string id);
public long Insert(T entity)
{
ResetCache();
using (var cnn = Config.DbConnection())
{
cnn.Open();
return cnn.Insert(entity);
}
}
public void Delete(T entity)
{
ResetCache();
using (var db = Config.DbConnection())
{
db.Open();
db.Delete(entity);
}
}
public async Task DeleteAsync(T entity)
{
ResetCache();
using (var db = Config.DbConnection())
{
db.Open();
await db.DeleteAsync(entity);
}
}
public bool Update(T entity)
{
ResetCache();
Log.Trace("Updating entity");
Log.Trace(entity.DumpJson());
using (var db = Config.DbConnection())
{
db.Open();
return db.Update(entity);
}
}
public async Task<bool> UpdateAsync(T entity)
{
ResetCache();
Log.Trace("Updating entity");
Log.Trace(entity.DumpJson());
using (var db = Config.DbConnection())
{
db.Open();
return await db.UpdateAsync(entity);
}
}
public bool UpdateAll(IEnumerable<T> entity)
{
ResetCache();
Log.Trace("Updating all entities");
var result = new HashSet<bool>();
using (var db = Config.DbConnection())
{
db.Open();
foreach (var e in entity)
{
result.Add(db.Update(e));
}
}
return result.All(x => true);
}
public async Task<bool> UpdateAllAsync(IEnumerable<T> entity)
{
ResetCache();
Log.Trace("Updating all entities");
var result = new HashSet<bool>();
using (var db = Config.DbConnection())
{
db.Open();
foreach (var e in entity)
{
result.Add(await db.UpdateAsync(e));
}
}
return result.All(x => true);
}
public async Task<int> InsertAsync(T entity)
{
ResetCache();
using (var cnn = Config.DbConnection())
{
cnn.Open();
return await cnn.InsertAsync(entity);
}
}
private void ResetCache()
{
Cache.Remove("Get");
Cache.Remove("GetAll");
}
public IEnumerable<T> GetAll()
{
using (var db = Config.DbConnection())
{
db.Open();
var result = db.GetAll<T>();
return result;
}
}
public async Task<IEnumerable<T>> GetAllAsync()
{
using (var db = Config.DbConnection())
{
db.Open();
var result = await db.GetAllAsync<T>();
return result;
}
}
}
}

View file

@ -1,4 +1,5 @@
#region Copyright #region Copyright
// /************************************************************************ // /************************************************************************
// Copyright (c) 2016 Jamie Rees // Copyright (c) 2016 Jamie Rees
// File: GenericRepository.cs // File: GenericRepository.cs
@ -23,59 +24,38 @@
// 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 System.Threading.Tasks;
using Dapper.Contrib.Extensions; using Dapper.Contrib.Extensions;
using NLog; using NLog;
using PlexRequests.Helpers; using PlexRequests.Helpers;
namespace PlexRequests.Store.Repository namespace PlexRequests.Store.Repository
{ {
public class GenericRepository<T> : IRepository<T> where T : Entity public class GenericRepository<T> : BaseGenericRepository<T>, IRepository<T> where T : Entity
{ {
private ICacheProvider Cache { get; }
public GenericRepository(ISqliteConfiguration config, ICacheProvider cache) public GenericRepository(ISqliteConfiguration config, ICacheProvider cache) : base(config, cache)
{ {
Config = config;
Cache = cache;
} }
private static Logger Log = LogManager.GetCurrentClassLogger(); public override T Get(string id)
private ISqliteConfiguration Config { get; }
public long Insert(T entity)
{
ResetCache();
using (var cnn = Config.DbConnection())
{
cnn.Open();
return cnn.Insert(entity);
}
}
public IEnumerable<T> GetAll()
{
using (var db = Config.DbConnection())
{
db.Open();
var result = db.GetAll<T>();
return result;
}
}
public T Get(string id)
{ {
throw new NotSupportedException("Get(string) is not supported. Use Get(int)"); throw new NotSupportedException("Get(string) is not supported. Use Get(int)");
} }
public T Get(int id) public override Task<T> GetAsync(string id)
{
throw new NotSupportedException("GetAsync(string) is not supported. Use GetAsync(int)");
}
public override T Get(int id)
{ {
var key = "Get" + id; var key = "Get" + id;
var item = Cache.GetOrSet( var item = Cache.GetOrSet(
@ -91,49 +71,22 @@ namespace PlexRequests.Store.Repository
return item; return item;
} }
public void Delete(T entity) public override async Task<T> GetAsync(int id)
{ {
ResetCache(); var key = "Get" + id;
using (var db = Config.DbConnection()) var item = await Cache.GetOrSetAsync(
{ key,
db.Open(); async () =>
db.Delete(entity);
}
}
public bool Update(T entity)
{
ResetCache();
Log.Trace("Updating entity");
Log.Trace(entity.DumpJson());
using (var db = Config.DbConnection())
{
db.Open();
return db.Update(entity);
}
}
public bool UpdateAll(IEnumerable<T> entity)
{
ResetCache();
Log.Trace("Updating all entities");
var result = new HashSet<bool>();
using (var db = Config.DbConnection())
{
db.Open();
foreach (var e in entity)
{ {
result.Add(db.Update(e)); using (var db = Config.DbConnection())
} {
} db.Open();
return result.All(x => true); return await db.GetAsync<T>(id);
}
});
return item;
} }
private void ResetCache()
{
Cache.Remove("Get");
Cache.Remove("GetAll");
}
} }
} }

View file

@ -25,6 +25,7 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
namespace PlexRequests.Store.Repository namespace PlexRequests.Store.Repository
{ {
@ -35,12 +36,15 @@ namespace PlexRequests.Store.Repository
/// </summary> /// </summary>
/// <param name="entity">The entity.</param> /// <param name="entity">The entity.</param>
long Insert(T entity); long Insert(T entity);
Task<int> InsertAsync(T entity);
/// <summary> /// <summary>
/// Gets all. /// Gets all.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
IEnumerable<T> GetAll(); IEnumerable<T> GetAll();
Task<IEnumerable<T>> GetAllAsync();
/// <summary> /// <summary>
/// Gets the specified identifier. /// Gets the specified identifier.
@ -48,12 +52,15 @@ namespace PlexRequests.Store.Repository
/// <param name="id">The identifier.</param> /// <param name="id">The identifier.</param>
/// <returns></returns> /// <returns></returns>
T Get(string id); T Get(string id);
Task<T> GetAsync(string id);
T Get(int id); T Get(int id);
Task<T> GetAsync(int id);
/// <summary> /// <summary>
/// Deletes the specified entity. /// Deletes the specified entity.
/// </summary> /// </summary>
/// <param name="entity">The entity.</param> /// <param name="entity">The entity.</param>
void Delete(T entity); void Delete(T entity);
Task DeleteAsync(T entity);
/// <summary> /// <summary>
/// Updates the specified entity. /// Updates the specified entity.
@ -61,6 +68,7 @@ namespace PlexRequests.Store.Repository
/// <param name="entity">The entity.</param> /// <param name="entity">The entity.</param>
/// <returns></returns> /// <returns></returns>
bool Update(T entity); bool Update(T entity);
Task<bool> UpdateAsync(T entity);
/// <summary> /// <summary>
/// Updates all. /// Updates all.
@ -68,5 +76,6 @@ namespace PlexRequests.Store.Repository
/// <param name="entity">The entity.</param> /// <param name="entity">The entity.</param>
/// <returns></returns> /// <returns></returns>
bool UpdateAll(IEnumerable<T> entity); bool UpdateAll(IEnumerable<T> entity);
Task<bool> UpdateAllAsync(IEnumerable<T> entity);
} }
} }

View file

@ -25,6 +25,7 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using PlexRequests.Store.Models; using PlexRequests.Store.Models;
@ -38,13 +39,18 @@ namespace PlexRequests.Store.Repository
/// <param name="entity">The entity.</param> /// <param name="entity">The entity.</param>
long Insert(RequestBlobs entity); long Insert(RequestBlobs entity);
Task<int> InsertAsync(RequestBlobs entity);
/// <summary> /// <summary>
/// Gets all. /// Gets all.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
IEnumerable<RequestBlobs> GetAll(); IEnumerable<RequestBlobs> GetAll();
Task<IEnumerable<RequestBlobs>> GetAllAsync();
RequestBlobs Get(int id); RequestBlobs Get(int id);
Task<RequestBlobs> GetAsync(int id);
/// <summary> /// <summary>
/// Deletes the specified entity. /// Deletes the specified entity.
@ -52,6 +58,10 @@ namespace PlexRequests.Store.Repository
/// <param name="entity">The entity.</param> /// <param name="entity">The entity.</param>
/// <returns></returns> /// <returns></returns>
bool Delete(RequestBlobs entity); bool Delete(RequestBlobs entity);
Task<bool> DeleteAsync(RequestBlobs entity);
bool DeleteAll(IEnumerable<RequestBlobs> entity);
Task<bool> DeleteAllAsync(IEnumerable<RequestBlobs> entity);
/// <summary> /// <summary>
/// Updates the specified entity. /// Updates the specified entity.
@ -59,7 +69,9 @@ namespace PlexRequests.Store.Repository
/// <param name="entity">The entity.</param> /// <param name="entity">The entity.</param>
/// <returns></returns> /// <returns></returns>
bool Update(RequestBlobs entity); bool Update(RequestBlobs entity);
Task<bool> UpdateAsync(RequestBlobs entity);
bool UpdateAll(IEnumerable<RequestBlobs> entity); bool UpdateAll(IEnumerable<RequestBlobs> entity);
Task<bool> UpdateAllAsync(IEnumerable<RequestBlobs> entity);
} }
} }

View file

@ -25,7 +25,7 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using PlexRequests.Store.Models; using PlexRequests.Store.Models;
namespace PlexRequests.Store.Repository namespace PlexRequests.Store.Repository
@ -37,12 +37,14 @@ namespace PlexRequests.Store.Repository
/// </summary> /// </summary>
/// <param name="entity">The entity.</param> /// <param name="entity">The entity.</param>
long Insert(GlobalSettings entity); long Insert(GlobalSettings entity);
Task<int> InsertAsync(GlobalSettings entity);
/// <summary> /// <summary>
/// Gets all. /// Gets all.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
IEnumerable<GlobalSettings> GetAll(); IEnumerable<GlobalSettings> GetAll();
Task<IEnumerable<GlobalSettings>> GetAllAsync();
/// <summary> /// <summary>
/// Gets the specified identifier. /// Gets the specified identifier.
@ -50,12 +52,14 @@ namespace PlexRequests.Store.Repository
/// <param name="settingsName">Name of the settings.</param> /// <param name="settingsName">Name of the settings.</param>
/// <returns></returns> /// <returns></returns>
GlobalSettings Get(string settingsName); GlobalSettings Get(string settingsName);
Task<GlobalSettings> GetAsync(string settingsName);
/// <summary> /// <summary>
/// Deletes the specified entity. /// Deletes the specified entity.
/// </summary> /// </summary>
/// <param name="entity">The entity.</param> /// <param name="entity">The entity.</param>
/// <returns></returns> /// <returns></returns>
Task<bool> DeleteAsync(GlobalSettings entity);
bool Delete(GlobalSettings entity); bool Delete(GlobalSettings entity);
/// <summary> /// <summary>
@ -63,6 +67,7 @@ namespace PlexRequests.Store.Repository
/// </summary> /// </summary>
/// <param name="entity">The entity.</param> /// <param name="entity">The entity.</param>
/// <returns></returns> /// <returns></returns>
Task<bool> UpdateAsync(GlobalSettings entity);
bool Update(GlobalSettings entity); bool Update(GlobalSettings entity);

View file

@ -26,6 +26,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Dapper.Contrib.Extensions; using Dapper.Contrib.Extensions;
@ -56,6 +57,16 @@ namespace PlexRequests.Store.Repository
} }
} }
public async Task<int> InsertAsync(RequestBlobs entity)
{
ResetCache();
using (var con = Db.DbConnection())
{
var id = await con.InsertAsync(entity);
return id;
}
}
public IEnumerable<RequestBlobs> GetAll() public IEnumerable<RequestBlobs> GetAll()
{ {
var key = "GetAll"; var key = "GetAll";
@ -70,6 +81,20 @@ namespace PlexRequests.Store.Repository
return item; return item;
} }
public async Task<IEnumerable<RequestBlobs>> GetAllAsync()
{
var key = "GetAll";
var item = await Cache.GetOrSetAsync(key, async() =>
{
using (var con = Db.DbConnection())
{
var page = await con.GetAllAsync<RequestBlobs>();
return page;
}
}, 5);
return item;
}
public RequestBlobs Get(int id) public RequestBlobs Get(int id)
{ {
var key = "Get" + id; var key = "Get" + id;
@ -84,6 +109,20 @@ namespace PlexRequests.Store.Repository
return item; return item;
} }
public async Task<RequestBlobs> GetAsync(int id)
{
var key = "Get" + id;
var item = await Cache.GetOrSetAsync(key, async () =>
{
using (var con = Db.DbConnection())
{
var page = await con.GetAsync<RequestBlobs>(id);
return page;
}
}, 5);
return item;
}
public bool Delete(RequestBlobs entity) public bool Delete(RequestBlobs entity)
{ {
ResetCache(); ResetCache();
@ -93,6 +132,30 @@ namespace PlexRequests.Store.Repository
} }
} }
public async Task<bool> DeleteAsync(RequestBlobs entity)
{
ResetCache();
using (var con = Db.DbConnection())
{
return await con.DeleteAsync(entity);
}
}
public async Task<bool> DeleteAllAsync(IEnumerable<RequestBlobs> entity)
{
ResetCache();
var result = new HashSet<bool>();
using (var db = Db.DbConnection())
{
db.Open();
foreach (var e in entity)
{
result.Add(await db.DeleteAsync(e));
}
}
return result.All(x => true);
}
public bool Update(RequestBlobs entity) public bool Update(RequestBlobs entity)
{ {
ResetCache(); ResetCache();
@ -102,6 +165,15 @@ namespace PlexRequests.Store.Repository
} }
} }
public async Task<bool> UpdateAsync(RequestBlobs entity)
{
ResetCache();
using (var con = Db.DbConnection())
{
return await con.UpdateAsync(entity);
}
}
private void ResetCache() private void ResetCache()
{ {
Cache.Remove("Get"); Cache.Remove("Get");
@ -122,5 +194,35 @@ namespace PlexRequests.Store.Repository
} }
return result.All(x => true); return result.All(x => true);
} }
public async Task<bool> UpdateAllAsync(IEnumerable<RequestBlobs> entity)
{
ResetCache();
var result = new HashSet<bool>();
using (var db = Db.DbConnection())
{
db.Open();
foreach (var e in entity)
{
result.Add(await db.UpdateAsync(e));
}
}
return result.All(x => true);
}
public bool DeleteAll(IEnumerable<RequestBlobs> entity)
{
ResetCache();
var result = new HashSet<bool>();
using (var db = Db.DbConnection())
{
db.Open();
foreach (var e in entity)
{
result.Add(db.Delete(e));
}
}
return result.All(x => true);
}
} }
} }

View file

@ -26,7 +26,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Dapper.Contrib.Extensions; using Dapper.Contrib.Extensions;
using PlexRequests.Helpers; using PlexRequests.Helpers;
@ -57,6 +57,15 @@ namespace PlexRequests.Store.Repository
} }
} }
public async Task<int> InsertAsync(GlobalSettings entity)
{
ResetCache();
using (var con = Db.DbConnection())
{
return await con.InsertAsync(entity);
}
}
public IEnumerable<GlobalSettings> GetAll() public IEnumerable<GlobalSettings> GetAll()
{ {
var key = TypeName + "GetAll"; var key = TypeName + "GetAll";
@ -71,6 +80,20 @@ namespace PlexRequests.Store.Repository
return item; return item;
} }
public async Task<IEnumerable<GlobalSettings>> GetAllAsync()
{
var key = TypeName + "GetAll";
var item = await Cache.GetOrSetAsync(key, async() =>
{
using (var con = Db.DbConnection())
{
var page = await con.GetAllAsync<GlobalSettings>();
return page;
}
}, 5);
return item;
}
public GlobalSettings Get(string pageName) public GlobalSettings Get(string pageName)
{ {
var key = pageName + "Get"; var key = pageName + "Get";
@ -85,6 +108,38 @@ namespace PlexRequests.Store.Repository
return item; return item;
} }
public async Task<GlobalSettings> GetAsync(string settingsName)
{
var key = settingsName + "Get";
var item = await Cache.GetOrSetAsync(key, async() =>
{
using (var con = Db.DbConnection())
{
var page = await con.GetAllAsync<GlobalSettings>();
return page.SingleOrDefault(x => x.SettingsName == settingsName);
}
}, 5);
return item;
}
public async Task<bool> DeleteAsync(GlobalSettings entity)
{
ResetCache();
using (var con = Db.DbConnection())
{
return await con.DeleteAsync(entity);
}
}
public async Task<bool> UpdateAsync(GlobalSettings entity)
{
ResetCache();
using (var con = Db.DbConnection())
{
return await con.UpdateAsync(entity);
}
}
public bool Delete(GlobalSettings entity) public bool Delete(GlobalSettings entity)
{ {
ResetCache(); ResetCache();

View file

@ -27,41 +27,31 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Dapper.Contrib.Extensions; using Dapper.Contrib.Extensions;
using PlexRequests.Helpers;
using PlexRequests.Store.Repository; using PlexRequests.Store.Repository;
namespace PlexRequests.Store namespace PlexRequests.Store
{ {
public class UserRepository<T> : IRepository<T> where T : UserEntity public class UserRepository<T> : BaseGenericRepository<T>, IRepository<T> where T : UserEntity
{ {
public UserRepository(ISqliteConfiguration config) public UserRepository(ISqliteConfiguration config, ICacheProvider cache) : base(config, cache)
{ {
Config = config;
} }
private ISqliteConfiguration Config { get; } public override T Get(int id)
public long Insert(T entity)
{ {
using (var cnn = Config.DbConnection()) throw new NotSupportedException("Get(int) is not supported. Use Get(string)");
{
cnn.Open();
return cnn.Insert(entity);
}
} }
public IEnumerable<T> GetAll() public override Task<T> GetAsync(int id)
{ {
using (var db = Config.DbConnection()) throw new NotSupportedException("GetAsync(int) is not supported. Use GetAsync(string)");
{
db.Open();
var result = db.GetAll<T>();
return result;
}
} }
public T Get(string id) public override T Get(string id)
{ {
using (var db = Config.DbConnection()) using (var db = Config.DbConnection())
{ {
@ -72,33 +62,18 @@ namespace PlexRequests.Store
} }
} }
public T Get(int id) public override async Task<T> GetAsync(string id)
{
throw new NotSupportedException("Get(int) is not supported. Use Get(string)");
}
public void Delete(T entity)
{ {
using (var db = Config.DbConnection()) using (var db = Config.DbConnection())
{ {
db.Open(); db.Open();
db.Delete(entity); var result = await db.GetAllAsync<T>();
var selected = result.FirstOrDefault(x => x.UserGuid == id);
return selected;
} }
} }
public bool Update(T entity)
{
using (var db = Config.DbConnection())
{
db.Open();
return db.Update(entity);
}
}
public bool UpdateAll(IEnumerable<T> entity)
{
throw new NotSupportedException();
}
} }
} }

View file

@ -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="net45" /> <package id="Dapper" version="1.50.0-beta8" targetFramework="net45" />
<package id="Dapper.Contrib" version="1.43" targetFramework="net45" /> <package id="Dapper.Contrib" version="1.50.0-beta8" targetFramework="net45" />
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net45" /> <package id="Newtonsoft.Json" version="8.0.2" targetFramework="net45" />
<package id="NLog" version="4.2.3" targetFramework="net45" /> <package id="NLog" version="4.2.3" targetFramework="net45" />
</packages> </packages>

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -1,295 +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; } }
.navbar-default .navbar-nav > .active > a,
.navbar-default .navbar-nav > .active > a:hover,
.navbar-default .navbar-nav > .active > a:focus {
color: #fff; }
hr {
border-color: #777; }
body.update-available {
margin-top: 80px; }
.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: 10px;
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: #df691a;
text-align: center;
font-size: 15px;
padding: 3px 0; }
.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; }

File diff suppressed because one or more lines are too long

View file

@ -1,365 +0,0 @@
$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;
}
}
.navbar-default .navbar-nav > .active > a,
.navbar-default .navbar-nav > .active > a:hover,
.navbar-default .navbar-nav > .active > a:focus {
color: #fff;
}
hr {
border-color: #777;
}
body.update-available {
margin-top: 80px;
}
.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: 10px;
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: #df691a;
text-align: center;
font-size: 15px;
padding: 3px 0;
}
.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; }

View file

@ -0,0 +1 @@


View file

@ -0,0 +1 @@


View file

@ -0,0 +1,2 @@
body {
}

View file

@ -0,0 +1,158 @@
.form-control-custom {
background-color: #333333 !important; }
.nav-tabs > li.active > a,
.nav-tabs > li.active > a:hover,
.nav-tabs > li.active > a:focus {
background: #df691a; }
scroll-top-wrapper {
background-color: #333333; }
.scroll-top-wrapper:hover {
background-color: #df691a; }
body {
font-family: Open Sans Regular,Helvetica Neue,Helvetica,Arial,sans-serif;
color: #eee;
background-color: #1f1f1f; }
.table-striped > tbody > tr:nth-of-type(odd) {
background-color: #333; }
.table-hover > tbody > tr:hover {
background-color: #282828; }
fieldset {
padding: 15px; }
legend {
border-bottom: 1px solid #333333; }
.form-control {
color: #fefefe;
background-color: #333; }
.radio input[type="radio"],
.radio-inline input[type="radio"],
.checkbox input[type="checkbox"],
.checkbox-inline input[type="checkbox"] {
margin-left: -0px; }
.form-horizontal .radio,
.form-horizontal .checkbox,
.form-horizontal .radio-inline,
.form-horizontal .checkbox-inline {
margin-top: -15px; }
.dropdown-menu {
background-color: #282828; }
.dropdown-menu .divider {
background-color: #333333; }
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
background-color: #333; }
.input-group-addon {
background-color: #333333; }
.nav > li > a:hover,
.nav > li > a:focus {
background-color: #df691a; }
.nav-tabs > li > a:hover {
border-color: #df691a #df691a transparent; }
.nav-tabs > li.active > a,
.nav-tabs > li.active > a:hover,
.nav-tabs > li.active > a:focus {
background-color: #df691a;
border: 1px solid #df691a; }
.nav-tabs.nav-justified > .active > a,
.nav-tabs.nav-justified > .active > a:hover,
.nav-tabs.nav-justified > .active > a:focus {
border: 1px solid #df691a; }
/*.navbar {
position: relative;
min-height: 40px;
margin-bottom: 21px;
z-index: 1000;
padding: 0px 3px;
font-size: 24px;
background-color: #000;
box-shadow: 0px 0px 0px 3px rgba(0, 0, 0, 0.2);
}*/
.navbar-default {
background-color: #0a0a0a; }
.navbar-default .navbar-brand {
color: #DF691A; }
.navbar-default .navbar-nav > li > a:hover,
.navbar-default .navbar-nav > li > a:focus {
color: #F0ad4e;
background-color: #282828; }
.navbar-default .navbar-nav > .active > a,
.navbar-default .navbar-nav > .active > a:hover,
.navbar-default .navbar-nav > .active > a:focus {
background-color: #282828; }
.navbar-default .navbar-nav > .open > a,
.navbar-default .navbar-nav > .open > a:hover,
.navbar-default .navbar-nav > .open > a:focus {
background-color: #df691a;
color: #fff; }
.pagination > li > a,
.pagination > li > span {
background-color: #282828; }
.pagination > li > a:hover,
.pagination > li > span:hover,
.pagination > li > a:focus,
.pagination > li > span:focus {
background-color: #333; }
.pagination > .disabled > span,
.pagination > .disabled > span:hover,
.pagination > .disabled > span:focus,
.pagination > .disabled > a,
.pagination > .disabled > a:hover,
.pagination > .disabled > a:focus {
color: #fefefe;
background-color: #333333; }
.list-group-item {
background-color: #282828; }
a.list-group-item:hover,
button.list-group-item:hover,
a.list-group-item:focus,
button.list-group-item:focus {
background-color: #333333; }
.input-addon,
.input-group-addon {
color: #df691a; }
.modal-header,
.modal-footer {
background-color: #282828; }
.modal-content {
position: relative;
background-color: #282828;
border: 1px solid transparent;
border-radius: 0;
-webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
-webkit-background-clip: padding-box;
-moz-background-clip: padding-box;
background-clip: padding-box;
outline: 0; }

View file

@ -0,0 +1 @@
.form-control-custom{background-color:#333 !important;}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{background:#df691a;}scroll-top-wrapper{background-color:#333;}.scroll-top-wrapper:hover{background-color:#df691a;}body{font-family:Open Sans Regular,Helvetica Neue,Helvetica,Arial,sans-serif;color:#eee;background-color:#1f1f1f;}.table-striped>tbody>tr:nth-of-type(odd){background-color:#333;}.table-hover>tbody>tr:hover{background-color:#282828;}fieldset{padding:15px;}legend{border-bottom:1px solid #333;}.form-control{color:#fefefe;background-color:#333;}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{margin-left:-0;}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:-15px;}.dropdown-menu{background-color:#282828;}.dropdown-menu .divider{background-color:#333;}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-color:#333;}.input-group-addon{background-color:#333;}.nav>li>a:hover,.nav>li>a:focus{background-color:#df691a;}.nav-tabs>li>a:hover{border-color:#df691a #df691a transparent;}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{background-color:#df691a;border:1px solid #df691a;}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #df691a;}.navbar-default{background-color:#0a0a0a;}.navbar-default .navbar-brand{color:#df691a;}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#f0ad4e;background-color:#282828;}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{background-color:#282828;}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#df691a;color:#fff;}.pagination>li>a,.pagination>li>span{background-color:#282828;}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{background-color:#333;}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#fefefe;background-color:#333;}.list-group-item{background-color:#282828;}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{background-color:#333;}.input-addon,.input-group-addon{color:#df691a;}.modal-header,.modal-footer{background-color:#282828;}.modal-content{position:relative;background-color:#282828;border:1px solid transparent;border-radius:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;outline:0;}

View file

@ -0,0 +1,196 @@
$primary-colour: #df691a;
$primary-colour-outline: #ff761b;
$bg-colour: #333333;
$i: !important;
.form-control-custom {
background-color: $bg-colour $i;
}
.nav-tabs > li.active > a,
.nav-tabs > li.active > a:hover,
.nav-tabs > li.active > a:focus {
background: $primary-colour;
}
scroll-top-wrapper {
background-color: $bg-colour;
}
.scroll-top-wrapper:hover {
background-color: $primary-colour;
}
body {
font-family: Open Sans Regular,Helvetica Neue,Helvetica,Arial,sans-serif;
color: #eee;
background-color: #1f1f1f;
}
.table-striped > tbody > tr:nth-of-type(odd) {
background-color: #333;
}
.table-hover > tbody > tr:hover {
background-color: #282828;
}
fieldset {
padding: 15px;
}
legend {
border-bottom: 1px solid #333333;
}
.form-control {
color: #fefefe;
background-color: #333;
}
.radio input[type="radio"],
.radio-inline input[type="radio"],
.checkbox input[type="checkbox"],
.checkbox-inline input[type="checkbox"] {
margin-left: -0px;
}
.form-horizontal .radio,
.form-horizontal .checkbox,
.form-horizontal .radio-inline,
.form-horizontal .checkbox-inline {
margin-top: -15px;
}
.dropdown-menu {
background-color: #282828;
}
.dropdown-menu .divider {
background-color: #333333;
}
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
background-color: #333;
}
.input-group-addon {
background-color: #333333;
}
.nav > li > a:hover,
.nav > li > a:focus {
background-color: #df691a;
}
.nav-tabs > li > a:hover {
border-color: #df691a #df691a transparent;
}
.nav-tabs > li.active > a,
.nav-tabs > li.active > a:hover,
.nav-tabs > li.active > a:focus {
background-color: #df691a;
border: 1px solid #df691a;
}
.nav-tabs.nav-justified > .active > a,
.nav-tabs.nav-justified > .active > a:hover,
.nav-tabs.nav-justified > .active > a:focus {
border: 1px solid #df691a;
}
/*.navbar {
position: relative;
min-height: 40px;
margin-bottom: 21px;
z-index: 1000;
padding: 0px 3px;
font-size: 24px;
background-color: #000;
box-shadow: 0px 0px 0px 3px rgba(0, 0, 0, 0.2);
}*/
.navbar-default {
background-color: #0a0a0a;
}
.navbar-default .navbar-brand {
color: #DF691A;
}
.navbar-default .navbar-nav > li > a:hover,
.navbar-default .navbar-nav > li > a:focus {
color: #F0ad4e;
background-color: #282828;
}
.navbar-default .navbar-nav > .active > a,
.navbar-default .navbar-nav > .active > a:hover,
.navbar-default .navbar-nav > .active > a:focus {
background-color: #282828;
}
.navbar-default .navbar-nav > .open > a,
.navbar-default .navbar-nav > .open > a:hover,
.navbar-default .navbar-nav > .open > a:focus {
background-color: #df691a;
color: #fff;
}
.pagination > li > a,
.pagination > li > span {
background-color: #282828;
}
.pagination > li > a:hover,
.pagination > li > span:hover,
.pagination > li > a:focus,
.pagination > li > span:focus {
background-color: #333;
}
.pagination > .disabled > span,
.pagination > .disabled > span:hover,
.pagination > .disabled > span:focus,
.pagination > .disabled > a,
.pagination > .disabled > a:hover,
.pagination > .disabled > a:focus {
color: #fefefe;
background-color: #333333;
}
.list-group-item {
background-color: #282828;
}
a.list-group-item:hover,
button.list-group-item:hover,
a.list-group-item:focus,
button.list-group-item:focus {
background-color: #333333;
}
.input-addon,
.input-group-addon {
color: #df691a;
}
.modal-header,
.modal-footer {
background-color: #282828;
}
.modal-content {
position: relative;
background-color: #282828;
border: 1px solid transparent;
border-radius: 0;
-webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
-webkit-background-clip: padding-box;
-moz-background-clip: padding-box;
background-clip: padding-box;
outline: 0;
}

View file

@ -20,11 +20,15 @@
color: #fff; } color: #fff; }
hr { hr {
border-color: #777; } border: 1px dashed #777; }
.btn { .btn {
border-radius: 0.25rem !important; } border-radius: 0.25rem !important; }
.btn-group-separated .btn,
.btn-group-separated .btn + .btn {
margin-left: 3px; }
.multiSelect { .multiSelect {
background-color: #4e5d6c; } background-color: #4e5d6c; }

1
PlexRequests.UI/Content/base.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -6,8 +6,7 @@ $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 {
@ -40,13 +39,18 @@ $i:
} }
hr { hr {
border-color: #777; border: 1px dashed #777;
} }
.btn { .btn {
border-radius: .25rem $i; border-radius: .25rem $i;
} }
.btn-group-separated .btn,
.btn-group-separated .btn + .btn {
margin-left: 3px;
}
.multiSelect { .multiSelect {
background-color: $form-color; background-color: $form-color;
} }

View file

@ -12,6 +12,8 @@ var albumTemplate = Handlebars.compile(albumSource);
var movieTimer = 0; var movieTimer = 0;
var tvimer = 0; var tvimer = 0;
var base = $('#baseUrl').text(); var base = $('#baseUrl').text();
var tvLoaded = false;
var albumLoaded = false;
var mixItUpDefault = { var mixItUpDefault = {
animation: { enable: true }, animation: { enable: true },
@ -40,9 +42,14 @@ $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
var $tvl = $('#tvList'); var $tvl = $('#tvList');
var $musicL = $('#musicList'); var $musicL = $('#musicList');
$('.approve-category').hide(); $('.approve-category,.delete-category').hide();
if (target === "#TvShowTab") { if (target === "#TvShowTab") {
$('#approveTVShows').show(); if (!tvLoaded) {
tvLoaded = true;
tvLoad();
}
$('#approveTVShows,#deleteTVShows').show();
if ($ml.mixItUp('isLoaded')) { if ($ml.mixItUp('isLoaded')) {
activeState = $ml.mixItUp('getState'); activeState = $ml.mixItUp('getState');
$ml.mixItUp('destroy'); $ml.mixItUp('destroy');
@ -51,11 +58,11 @@ $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
activeState = $musicL.mixItUp('getState'); activeState = $musicL.mixItUp('getState');
$musicL.mixItUp('destroy'); $musicL.mixItUp('destroy');
} }
if ($tvl.mixItUp('isLoaded')) $tvl.mixItUp('destroy'); //if ($tvl.mixItUp('isLoaded')) $tvl.mixItUp('destroy');
$tvl.mixItUp(mixItUpConfig(activeState)); // init or reinit //$tvl.mixItUp(mixItUpConfig(activeState)); // init or reinit
} }
if (target === "#MoviesTab") { if (target === "#MoviesTab") {
$('#approveMovies').show(); $('#approveMovies,#deleteMovies').show();
if ($tvl.mixItUp('isLoaded')) { if ($tvl.mixItUp('isLoaded')) {
activeState = $tvl.mixItUp('getState'); activeState = $tvl.mixItUp('getState');
$tvl.mixItUp('destroy'); $tvl.mixItUp('destroy');
@ -69,7 +76,11 @@ $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
} }
if (target === "#MusicTab") { if (target === "#MusicTab") {
$('#approveMusic').show(); if (!albumLoaded) {
albumLoaded = true;
albumLoad();
}
$('#approveMusic,#deleteMusic').show();
if ($tvl.mixItUp('isLoaded')) { if ($tvl.mixItUp('isLoaded')) {
activeState = $tvl.mixItUp('getState'); activeState = $tvl.mixItUp('getState');
$tvl.mixItUp('destroy'); $tvl.mixItUp('destroy');
@ -124,7 +135,7 @@ $('#approveTVShows').click(function (e) {
return; return;
} }
loadingButton(buttonId, "success"); loadingButton(buttonId, "warning");
var url = createBaseUrl(base, '/approval/approvealltvshows'); var url = createBaseUrl(base, '/approval/approvealltvshows');
$.ajax({ $.ajax({
type: 'post', type: 'post',
@ -140,6 +151,72 @@ $('#approveTVShows').click(function (e) {
console.log(e); console.log(e);
generateNotify("Something went wrong!", "danger"); generateNotify("Something went wrong!", "danger");
}, },
complete: function (e) {
finishLoading(buttonId, "warning", origHtml);
}
});
});
$('#deleteMovies').click(function (e) {
e.preventDefault();
if (!confirm("Are you sure you want to delete all TV show requests?")) return;
var buttonId = e.target.id;
var origHtml = $(this).html();
if ($('#' + buttonId).text() === " Loading...") {
return;
}
loadingButton(buttonId, "warning");
var url = createBaseUrl(base, '/approval/deleteallmovies');
$.ajax({
type: 'post',
url: url,
dataType: "json",
success: function (response) {
if (checkJsonResponse(response)) {
generateNotify("Success! All Movie requests deleted!", "success");
movieLoad();
}
},
error: function (e) {
console.log(e);
generateNotify("Something went wrong!", "danger");
},
complete: function (e) {
finishLoading(buttonId, "warning", origHtml);
}
});
});
$('#deleteTVShows').click(function (e) {
e.preventDefault();
if (!confirm("Are you sure you want to delete all TV show requests?")) return;
var buttonId = e.target.id;
var origHtml = $(this).html();
if ($('#' + buttonId).text() === " Loading...") {
return;
}
loadingButton(buttonId, "warning");
var url = createBaseUrl(base, '/approval/deletealltvshows');
$.ajax({
type: 'post',
url: url,
dataType: "json",
success: function (response) {
if (checkJsonResponse(response)) {
generateNotify("Success! All TV Show requests deleted!", "success");
tvLoad();
}
},
error: function (e) {
console.log(e);
generateNotify("Something went wrong!", "danger");
},
complete: function (e) { complete: function (e) {
finishLoading(buttonId, "success", origHtml); finishLoading(buttonId, "success", origHtml);
} }
@ -448,10 +525,10 @@ function mixItUpConfig(activeState) {
return conf; return conf;
}; };
var position = 0;
function initLoad() { function initLoad() {
movieLoad(); movieLoad();
tvLoad();
albumLoad();
} }
function movieLoad() { function movieLoad() {

View file

@ -57,12 +57,15 @@ namespace PlexRequests.UI.Helpers
{ {
settings.ThemeName = Themes.PlexTheme; settings.ThemeName = Themes.PlexTheme;
} }
if (settings.ThemeName == "PlexBootstrap.css") settings.ThemeName = Themes.PlexTheme;
sb.AppendLine($"<link rel=\"stylesheet\" href=\"{content}/Content/Themes/{settings.ThemeName}\" type=\"text/css\"/>"); if (settings.ThemeName == "OriginalBootstrap.css") settings.ThemeName = Themes.OriginalTheme;
sb.AppendLine($"<link rel=\"stylesheet\" href=\"{content}/Content/Themes/{settings.ThemeName.Replace(".css",string.Empty)}Custom.min.css\" type=\"text/css\" />");
sb.AppendLine($"<link rel=\"stylesheet\" href=\"{content}/Content/bootstrap.css\" type=\"text/css\"/>");
sb.AppendLine($"<link rel=\"stylesheet\" href=\"{content}/Content/font-awesome.css\" type=\"text/css\"/>"); sb.AppendLine($"<link rel=\"stylesheet\" href=\"{content}/Content/font-awesome.css\" type=\"text/css\"/>");
sb.AppendLine($"<link rel=\"stylesheet\" href=\"{content}/Content/pace.min.css\" type=\"text/css\"/>"); sb.AppendLine($"<link rel=\"stylesheet\" href=\"{content}/Content/pace.min.css\" type=\"text/css\"/>");
sb.AppendLine($"<link rel=\"stylesheet\" href=\"{content}/Content/awesome-bootstrap-checkbox.css\" type=\"text/css\"/>"); sb.AppendLine($"<link rel=\"stylesheet\" href=\"{content}/Content/awesome-bootstrap-checkbox.css\" type=\"text/css\"/>");
sb.AppendLine($"<link rel=\"stylesheet\" href=\"{content}/Content/base.css\" type=\"text/css\"/>");
sb.AppendLine($"<link rel=\"stylesheet\" href=\"{content}/Content/Themes/{settings.ThemeName}\" type=\"text/css\"/>");
sb.AppendLine($"<script src=\"{content}/Content/jquery-2.2.1.min.js\"></script>"); sb.AppendLine($"<script src=\"{content}/Content/jquery-2.2.1.min.js\"></script>");
sb.AppendLine($"<script src=\"{content}/Content/handlebars.min.js\"></script>"); sb.AppendLine($"<script src=\"{content}/Content/handlebars.min.js\"></script>");

View file

@ -28,7 +28,7 @@ namespace PlexRequests.UI.Helpers
{ {
public static class Themes public static class Themes
{ {
public const string OriginalTheme = "OriginalBootstrap.css"; public const string OriginalTheme = "original.css";
public const string PlexTheme = "PlexBootstrap.css"; public const string PlexTheme = "plex.css";
} }
} }

View file

@ -56,13 +56,15 @@ namespace PlexRequests.UI.Jobs
var sickrage = JobBuilder.Create<SickRageCacher>().WithIdentity("SickRageCacher", "Cache").Build(); var sickrage = JobBuilder.Create<SickRageCacher>().WithIdentity("SickRageCacher", "Cache").Build();
var sonarr = JobBuilder.Create<SonarrCacher>().WithIdentity("SonarrCacher", "Cache").Build(); var sonarr = JobBuilder.Create<SonarrCacher>().WithIdentity("SonarrCacher", "Cache").Build();
var cp = JobBuilder.Create<CouchPotatoCacher>().WithIdentity("CouchPotatoCacher", "Cache").Build(); var cp = JobBuilder.Create<CouchPotatoCacher>().WithIdentity("CouchPotatoCacher", "Cache").Build();
var store = JobBuilder.Create<StoreBackup>().WithIdentity("StoreBackup", "Backup").Build(); var store = JobBuilder.Create<StoreBackup>().WithIdentity("StoreBackup", "Database").Build();
var storeClean = JobBuilder.Create<StoreCleanup>().WithIdentity("StoreCleanup", "Database").Build();
jobs.Add(plex); jobs.Add(plex);
jobs.Add(sickrage); jobs.Add(sickrage);
jobs.Add(sonarr); jobs.Add(sonarr);
jobs.Add(cp); jobs.Add(cp);
jobs.Add(store); jobs.Add(store);
jobs.Add(storeClean);
return jobs; return jobs;
} }
@ -129,17 +131,25 @@ namespace PlexRequests.UI.Jobs
var storeBackup = var storeBackup =
TriggerBuilder.Create() TriggerBuilder.Create()
.WithIdentity("StoreBackup", "Backup") .WithIdentity("StoreBackup", "Database")
.StartNow() .StartNow()
.WithSimpleSchedule(x => x.WithIntervalInHours(24).RepeatForever()) .WithSimpleSchedule(x => x.WithIntervalInHours(24).RepeatForever())
.Build(); .Build();
var storeCleanup =
TriggerBuilder.Create()
.WithIdentity("StoreCleanup", "Database")
.StartNow()
.WithSimpleSchedule(x => x.WithIntervalInHours(24).RepeatForever())
.Build();
triggers.Add(plexAvailabilityChecker); triggers.Add(plexAvailabilityChecker);
triggers.Add(srCacher); triggers.Add(srCacher);
triggers.Add(sonarrCacher); triggers.Add(sonarrCacher);
triggers.Add(cpCacher); triggers.Add(cpCacher);
triggers.Add(storeBackup); triggers.Add(storeBackup);
triggers.Add(storeCleanup);
return triggers; return triggers;
} }

View file

@ -481,8 +481,7 @@ namespace PlexRequests.UI.Modules
{ {
return Response.AsJson(valid.SendJsonError()); return Response.AsJson(valid.SendJsonError());
} }
Log.Trace(settings.DumpJson());
var result = EmailService.SaveSettings(settings); var result = EmailService.SaveSettings(settings);
if (settings.Enabled) if (settings.Enabled)

View file

@ -50,7 +50,7 @@ namespace PlexRequests.UI.Modules
ISettingsService<SonarrSettings> sonarrSettings, ISickRageApi srApi, ISettingsService<SickRageSettings> srSettings, ISettingsService<SonarrSettings> sonarrSettings, ISickRageApi srApi, ISettingsService<SickRageSettings> srSettings,
ISettingsService<HeadphonesSettings> hpSettings, IHeadphonesApi hpApi, ISettingsService<PlexRequestSettings> pr) : base("approval", pr) ISettingsService<HeadphonesSettings> hpSettings, IHeadphonesApi hpApi, ISettingsService<PlexRequestSettings> pr) : base("approval", pr)
{ {
this.RequiresClaims(UserClaims.Admin); this.RequiresClaims(UserClaims.Admin);
Service = service; Service = service;
CpService = cpService; CpService = cpService;
@ -66,6 +66,8 @@ namespace PlexRequests.UI.Modules
Post["/approveall"] = x => ApproveAll(); Post["/approveall"] = x => ApproveAll();
Post["/approveallmovies"] = x => ApproveAllMovies(); Post["/approveallmovies"] = x => ApproveAllMovies();
Post["/approvealltvshows"] = x => ApproveAllTVShows(); Post["/approvealltvshows"] = x => ApproveAllTVShows();
Post["/deleteallmovies"] = x => DeleteAllMovies();
Post["/deletealltvshows"] = x => DeleteAllTVShows();
} }
private IRequestService Service { get; } private IRequestService Service { get; }
@ -130,7 +132,7 @@ namespace PlexRequests.UI.Modules
Log.Trace("Approval result: {0}", requestResult); Log.Trace("Approval result: {0}", requestResult);
if (requestResult) if (requestResult)
{ {
return Response.AsJson(new JsonResponseModel {Result = true}); return Response.AsJson(new JsonResponseModel { Result = true });
} }
return return
Response.AsJson(new JsonResponseModel Response.AsJson(new JsonResponseModel
@ -156,11 +158,9 @@ namespace PlexRequests.UI.Modules
request.Approved = true; request.Approved = true;
var requestResult = Service.UpdateRequest(request); var requestResult = Service.UpdateRequest(request);
Log.Trace("Approval result: {0}", requestResult); Log.Trace("Approval result: {0}", requestResult);
if (requestResult) return Response.AsJson(requestResult
{ ? new JsonResponseModel { Result = true }
return Response.AsJson(new JsonResponseModel { Result = true }); : new JsonResponseModel { Result = false, Message = "Updated SickRage but could not approve it in PlexRequests :(" });
}
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Updated SickRage but could not approve it in PlexRequests :(" });
} }
return Response.AsJson(new JsonResponseModel return Response.AsJson(new JsonResponseModel
{ {
@ -168,17 +168,19 @@ namespace PlexRequests.UI.Modules
Message = result?.message != null ? "<b>Message From SickRage: </b>" + result.message : "Could not add the series to SickRage" Message = result?.message != null ? "<b>Message From SickRage: </b>" + result.message : "Could not add the series to SickRage"
}); });
} }
return Response.AsJson(new JsonResponseModel
{
Result = false, request.Approved = true;
Message = "SickRage or Sonarr are not set up!" var res = Service.UpdateRequest(request);
}); return Response.AsJson(res
? new JsonResponseModel { Result = true, Message = "This has been approved, but It has not been sent to Sonarr/SickRage because it has not been configured" }
: new JsonResponseModel { Result = false, Message = "Updated SickRage but could not approve it in PlexRequests :(" });
} }
private Response RequestMovieAndUpdateStatus(RequestedModel request, string qualityId) private Response RequestMovieAndUpdateStatus(RequestedModel request, string qualityId)
{ {
var cpSettings = CpService.GetSettings(); var cpSettings = CpService.GetSettings();
var cp = new CouchPotatoApi();
Log.Info("Adding movie to CouchPotato : {0}", request.Title); Log.Info("Adding movie to CouchPotato : {0}", request.Title);
if (!cpSettings.Enabled) if (!cpSettings.Enabled)
{ {
@ -197,7 +199,7 @@ namespace PlexRequests.UI.Modules
}); });
} }
var result = cp.AddMovie(request.ImdbId, cpSettings.ApiKey, request.Title, cpSettings.FullUri, string.IsNullOrEmpty(qualityId) ? cpSettings.ProfileId : qualityId); var result = CpApi.AddMovie(request.ImdbId, cpSettings.ApiKey, request.Title, cpSettings.FullUri, string.IsNullOrEmpty(qualityId) ? cpSettings.ProfileId : qualityId);
Log.Trace("Adding movie to CP result {0}", result); Log.Trace("Adding movie to CP result {0}", result);
if (result) if (result)
{ {
@ -248,9 +250,9 @@ namespace PlexRequests.UI.Modules
var sender = new HeadphonesSender(HeadphoneApi, hpSettings, Service); var sender = new HeadphonesSender(HeadphoneApi, hpSettings, Service);
var result = sender.AddAlbum(request); var result = sender.AddAlbum(request);
return Response.AsJson( new JsonResponseModel { Result = true, Message = "We have sent the approval to Headphones for processing, This can take a few minutes."} );
return Response.AsJson(new JsonResponseModel { Result = true, Message = "We have sent the approval to Headphones for processing, This can take a few minutes." });
} }
private Response ApproveAllMovies() private Response ApproveAllMovies()
@ -274,6 +276,27 @@ namespace PlexRequests.UI.Modules
} }
} }
private Response DeleteAllMovies()
{
var requests = Service.GetAll().Where(x => x.Type == RequestType.Movie);
var requestedModels = requests as RequestedModel[] ?? requests.ToArray();
if (!requestedModels.Any())
{
return Response.AsJson(new JsonResponseModel { Result = false, Message = "There are no movie requests to delete. Please refresh." });
}
try
{
return DeleteRequests(requestedModels);
}
catch (Exception e)
{
Log.Fatal(e);
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" });
}
}
private Response ApproveAllTVShows() private Response ApproveAllTVShows()
{ {
var requests = Service.GetAll().Where(x => x.CanApprove && x.Type == RequestType.TvShow); var requests = Service.GetAll().Where(x => x.CanApprove && x.Type == RequestType.TvShow);
@ -294,6 +317,27 @@ namespace PlexRequests.UI.Modules
} }
} }
private Response DeleteAllTVShows()
{
var requests = Service.GetAll().Where(x => x.Type == RequestType.TvShow);
var requestedModels = requests as RequestedModel[] ?? requests.ToArray();
if (!requestedModels.Any())
{
return Response.AsJson(new JsonResponseModel { Result = false, Message = "There are no tv show requests to delete. Please refresh." });
}
try
{
return DeleteRequests(requestedModels);
}
catch (Exception e)
{
Log.Fatal(e);
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" });
}
}
/// <summary> /// <summary>
/// Approves all. /// Approves all.
/// </summary> /// </summary>
@ -319,6 +363,22 @@ namespace PlexRequests.UI.Modules
} }
private Response DeleteRequests(RequestedModel[] requestedModels)
{
try
{
var result = Service.BatchDelete(requestedModels.ToList());
return Response.AsJson(result
? new JsonResponseModel { Result = true }
: new JsonResponseModel { Result = false, Message = "We could not delete all of the requests. Please try again or check the logs." });
}
catch (Exception e)
{
Log.Fatal(e);
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" });
}
}
private Response UpdateRequests(RequestedModel[] requestedModels) private Response UpdateRequests(RequestedModel[] requestedModels)
{ {
var cpSettings = CpService.GetSettings(); var cpSettings = CpService.GetSettings();
@ -327,15 +387,23 @@ namespace PlexRequests.UI.Modules
{ {
if (r.Type == RequestType.Movie) if (r.Type == RequestType.Movie)
{ {
var res = SendMovie(cpSettings, r, CpApi); if (cpSettings.Enabled)
if (res)
{ {
r.Approved = true; var res = SendMovie(cpSettings, r, CpApi);
updatedRequests.Add(r); if (res)
{
r.Approved = true;
updatedRequests.Add(r);
}
else
{
Log.Error("Could not approve and send the movie {0} to couch potato!", r.Title);
}
} }
else else
{ {
Log.Error("Could not approve and send the movie {0} to couch potato!", r.Title); r.Approved = true;
updatedRequests.Add(r);
} }
} }
if (r.Type == RequestType.TvShow) if (r.Type == RequestType.TvShow)
@ -358,7 +426,7 @@ namespace PlexRequests.UI.Modules
} }
} }
if (sonarr.Enabled) else if (sonarr.Enabled)
{ {
var res = sender.SendToSonarr(sonarr, r); var res = sender.SendToSonarr(sonarr, r);
if (!string.IsNullOrEmpty(res?.title)) if (!string.IsNullOrEmpty(res?.title))
@ -372,11 +440,15 @@ namespace PlexRequests.UI.Modules
res?.ErrorMessages.ForEach(x => Log.Error("Error messages: {0}", x)); res?.ErrorMessages.ForEach(x => Log.Error("Error messages: {0}", x));
} }
} }
else
{
r.Approved = true;
updatedRequests.Add(r);
}
} }
} }
try try
{ {
var result = Service.BatchUpdate(updatedRequests); var result = Service.BatchUpdate(updatedRequests);
return Response.AsJson(result return Response.AsJson(result
? new JsonResponseModel { Result = true } ? new JsonResponseModel { Result = true }

View file

@ -35,6 +35,7 @@ using Nancy.Security;
using PlexRequests.Core; using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers;
using PlexRequests.UI.Models; using PlexRequests.UI.Models;
namespace PlexRequests.UI.Modules namespace PlexRequests.UI.Modules
@ -103,7 +104,7 @@ namespace PlexRequests.UI.Modules
{ {
return Context.GetRedirect(!string.IsNullOrEmpty(BaseUrl) ? $"~/{BaseUrl}/register?error=true" : "~/register?error=true"); return Context.GetRedirect(!string.IsNullOrEmpty(BaseUrl) ? $"~/{BaseUrl}/register?error=true" : "~/register?error=true");
} }
var userId = UserMapper.CreateUser(username, Request.Form.Password, new[] { "Admin" }); var userId = UserMapper.CreateAdmin(username, Request.Form.Password);
Session[SessionKeys.UsernameKey] = username; Session[SessionKeys.UsernameKey] = username;
return this.LoginAndRedirect((Guid)userId); return this.LoginAndRedirect((Guid)userId);
}; };

View file

@ -74,17 +74,17 @@ namespace PlexRequests.UI.Modules
Cache = cache; Cache = cache;
Get["/"] = _ => LoadRequests(); Get["/"] = _ => LoadRequests();
Get["/movies"] = _ => GetMovies(); Get["/movies", true] = async (x, ct) => await GetMovies();
Get["/tvshows"] = _ => GetTvShows(); Get["/tvshows", true] = async (c, ct) => await GetTvShows();
Get["/albums"] = _ => GetAlbumRequests(); Get["/albums", true] = async (x,ct) => await GetAlbumRequests();
Post["/delete"] = _ => DeleteRequest((int)Request.Form.id); Post["/delete", true] = async (x, ct) => await DeleteRequest((int)Request.Form.id);
Post["/reportissue"] = _ => ReportIssue((int)Request.Form.requestId, (IssueState)(int)Request.Form.issue, null); Post["/reportissue", true] = async (x, ct) => await ReportIssue((int)Request.Form.requestId, (IssueState)(int)Request.Form.issue, null);
Post["/reportissuecomment"] = _ => ReportIssue((int)Request.Form.requestId, IssueState.Other, (string)Request.Form.commentArea); Post["/reportissuecomment", true] = async (x, ct) => await ReportIssue((int)Request.Form.requestId, IssueState.Other, (string)Request.Form.commentArea);
Post["/clearissues"] = _ => ClearIssue((int)Request.Form.Id); Post["/clearissues", true] = async (x, ct) => await ClearIssue((int)Request.Form.Id);
Post["/changeavailability"] = _ => ChangeRequestAvailability((int)Request.Form.Id, (bool)Request.Form.Available); Post["/changeavailability", true] = async (x, ct) => await ChangeRequestAvailability((int)Request.Form.Id, (bool)Request.Form.Available);
Post["/addnote"] = _ => AddNote((int)Request.Form.requestId, (string)Request.Form.noteArea); Post["/addnote", true] = async (x, ct) => await AddNote((int)Request.Form.requestId, (string)Request.Form.noteArea);
} }
private IRequestService Service { get; } private IRequestService Service { get; }
@ -105,122 +105,94 @@ namespace PlexRequests.UI.Modules
return View["Index", settings]; return View["Index", settings];
} }
private Response GetMovies() // TODO: async await the API calls private async Task<Response> GetMovies()
{ {
var settings = PrSettings.GetSettings(); var settings = PrSettings.GetSettings();
List<Task> taskList = new List<Task>(); var allRequests = await Service.GetAllAsync();
allRequests = allRequests.Where(x => x.Type == RequestType.Movie);
List<RequestedModel> dbMovies = new List<RequestedModel>(); var dbMovies = allRequests.ToList();
taskList.Add(Task.Factory.StartNew(() =>
if (settings.UsersCanViewOnlyOwnRequests && !IsAdmin)
{ {
return Service.GetAll().Where(x => x.Type == RequestType.Movie); dbMovies = dbMovies.Where(x => x.UserHasRequested(Username)).ToList();
}
}).ContinueWith((t) =>
{
dbMovies = t.Result.ToList();
if (settings.UsersCanViewOnlyOwnRequests && !IsAdmin)
{
dbMovies = dbMovies.Where(x => x.UserHasRequested(Username)).ToList();
}
}));
List<QualityModel> qualities = new List<QualityModel>(); List<QualityModel> qualities = new List<QualityModel>();
if (IsAdmin) if (IsAdmin)
{ {
var cpSettings = CpSettings.GetSettings(); var cpSettings = CpSettings.GetSettings();
if (cpSettings.Enabled) if (cpSettings.Enabled)
{ {
taskList.Add(Task.Factory.StartNew(() =>
var result = await Cache.GetOrSetAsync(CacheKeys.CouchPotatoQualityProfiles, async () =>
{ {
return Cache.GetOrSet(CacheKeys.CouchPotatoQualityProfiles, () => return await Task.Run(() => CpApi.GetProfiles(cpSettings.FullUri, cpSettings.ApiKey)).ConfigureAwait(false);
{ });
return CpApi.GetProfiles(cpSettings.FullUri, cpSettings.ApiKey); // TODO: cache this!
}); qualities = result.list.Select(x => new QualityModel() { Id = x._id, Name = x.label }).ToList();
}).ContinueWith((t) =>
{
qualities = t.Result.list.Select(x => new QualityModel() { Id = x._id, Name = x.label }).ToList();
}));
} }
} }
Task.WaitAll(taskList.ToArray()); var viewModel = dbMovies.Select(movie => new RequestViewModel
var viewModel = dbMovies.Select(movie =>
{ {
return new RequestViewModel ProviderId = movie.ProviderId,
{ Type = movie.Type,
ProviderId = movie.ProviderId, Status = movie.Status,
Type = movie.Type, ImdbId = movie.ImdbId,
Status = movie.Status, Id = movie.Id,
ImdbId = movie.ImdbId, PosterPath = movie.PosterPath,
Id = movie.Id, ReleaseDate = movie.ReleaseDate,
PosterPath = movie.PosterPath, ReleaseDateTicks = movie.ReleaseDate.Ticks,
ReleaseDate = movie.ReleaseDate, RequestedDate = movie.RequestedDate,
ReleaseDateTicks = movie.ReleaseDate.Ticks, Released = DateTime.Now > movie.ReleaseDate,
RequestedDate = movie.RequestedDate, RequestedDateTicks = DateTimeHelper.OffsetUTCDateTime(movie.RequestedDate, DateTimeOffset).Ticks,
Released = DateTime.Now > movie.ReleaseDate, Approved = movie.Available || movie.Approved,
RequestedDateTicks = DateTimeHelper.OffsetUTCDateTime(movie.RequestedDate, DateTimeOffset).Ticks, Title = movie.Title,
Approved = movie.Available || movie.Approved, Overview = movie.Overview,
Title = movie.Title, RequestedUsers = IsAdmin ? movie.AllUsers.ToArray() : new string[] { },
Overview = movie.Overview, ReleaseYear = movie.ReleaseDate.Year.ToString(),
RequestedUsers = IsAdmin ? movie.AllUsers.ToArray() : new string[] { }, Available = movie.Available,
ReleaseYear = movie.ReleaseDate.Year.ToString(), Admin = IsAdmin,
Available = movie.Available, Issues = movie.Issues.ToString().CamelCaseToWords(),
Admin = IsAdmin, OtherMessage = movie.OtherMessage,
Issues = movie.Issues.ToString().CamelCaseToWords(), AdminNotes = movie.AdminNote,
OtherMessage = movie.OtherMessage, Qualities = qualities.ToArray()
AdminNotes = movie.AdminNote,
Qualities = qualities.ToArray()
};
}).ToList(); }).ToList();
return Response.AsJson(viewModel); return Response.AsJson(viewModel);
} }
private Response GetTvShows() // TODO: async await the API calls private async Task<Response> GetTvShows()
{ {
var settings = PrSettings.GetSettings(); var settings = PrSettings.GetSettings();
List<Task> taskList = new List<Task>(); var requests = await Service.GetAllAsync();
requests = requests.Where(x => x.Type == RequestType.TvShow);
List<RequestedModel> dbTv = new List<RequestedModel>(); var dbTv = requests;
taskList.Add(Task.Factory.StartNew(() =>
if (settings.UsersCanViewOnlyOwnRequests && !IsAdmin)
{ {
return Service.GetAll().Where(x => x.Type == RequestType.TvShow); dbTv = dbTv.Where(x => x.UserHasRequested(Username)).ToList();
}
}).ContinueWith((t) => IEnumerable<QualityModel> qualities = new List<QualityModel>();
{
dbTv = t.Result.ToList();
if (settings.UsersCanViewOnlyOwnRequests && !IsAdmin)
{
dbTv = dbTv.Where(x => x.UserHasRequested(Username)).ToList();
}
}));
List<QualityModel> qualities = new List<QualityModel>();
if (IsAdmin) if (IsAdmin)
{ {
var sonarrSettings = SonarrSettings.GetSettings(); var sonarrSettings = SonarrSettings.GetSettings();
if (sonarrSettings.Enabled) if (sonarrSettings.Enabled)
{ {
taskList.Add(Task.Factory.StartNew(() => var result = Cache.GetOrSetAsync(CacheKeys.SonarrQualityProfiles, async () =>
{ {
return Cache.GetOrSet(CacheKeys.SonarrQualityProfiles, () => return await Task.Run(() => SonarrApi.GetProfiles(sonarrSettings.ApiKey, sonarrSettings.FullUri));
{
return SonarrApi.GetProfiles(sonarrSettings.ApiKey, sonarrSettings.FullUri); // TODO: cache this!
}); });
}).ContinueWith((t) => qualities = result.Result.Select(x => new QualityModel() { Id = x.id.ToString(), Name = x.name }).ToList();
{
qualities = t.Result.Select(x => new QualityModel() { Id = x.id.ToString(), Name = x.name }).ToList();
}));
} }
else { else
{
var sickRageSettings = SickRageSettings.GetSettings(); var sickRageSettings = SickRageSettings.GetSettings();
if (sickRageSettings.Enabled) if (sickRageSettings.Enabled)
{ {
@ -229,8 +201,6 @@ namespace PlexRequests.UI.Modules
} }
} }
Task.WaitAll(taskList.ToArray());
var viewModel = dbTv.Select(tv => var viewModel = dbTv.Select(tv =>
{ {
return new RequestViewModel return new RequestViewModel
@ -264,10 +234,11 @@ namespace PlexRequests.UI.Modules
return Response.AsJson(viewModel); return Response.AsJson(viewModel);
} }
private Response GetAlbumRequests() private async Task<Response> GetAlbumRequests()
{ {
var settings = PrSettings.GetSettings(); var settings = PrSettings.GetSettings();
var dbAlbum = Service.GetAll().Where(x => x.Type == RequestType.Album); var dbAlbum = await Service.GetAllAsync();
dbAlbum = dbAlbum.Where(x => x.Type == RequestType.Album);
if (settings.UsersCanViewOnlyOwnRequests && !IsAdmin) if (settings.UsersCanViewOnlyOwnRequests && !IsAdmin)
{ {
dbAlbum = dbAlbum.Where(x => x.UserHasRequested(Username)); dbAlbum = dbAlbum.Where(x => x.UserHasRequested(Username));
@ -308,12 +279,12 @@ namespace PlexRequests.UI.Modules
return Response.AsJson(viewModel); return Response.AsJson(viewModel);
} }
private Response DeleteRequest(int requestid) private async Task<Response> DeleteRequest(int requestid)
{ {
this.RequiresClaims (UserClaims.Admin); this.RequiresClaims(UserClaims.Admin);
var currentEntity = Service.Get(requestid); var currentEntity = await Service.GetAsync(requestid);
Service.DeleteRequest(currentEntity); await Service.DeleteRequestAsync(currentEntity);
return Response.AsJson(new JsonResponseModel { Result = true }); return Response.AsJson(new JsonResponseModel { Result = true });
} }
@ -325,9 +296,9 @@ namespace PlexRequests.UI.Modules
/// <param name="issue">The issue.</param> /// <param name="issue">The issue.</param>
/// <param name="comment">The comment.</param> /// <param name="comment">The comment.</param>
/// <returns></returns> /// <returns></returns>
private Response ReportIssue(int requestId, IssueState issue, string comment) private async Task<Response> ReportIssue(int requestId, IssueState issue, string comment)
{ {
var originalRequest = Service.Get(requestId); var originalRequest = await Service.GetAsync(requestId);
if (originalRequest == null) if (originalRequest == null)
{ {
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not add issue, please try again or contact the administrator!" }); return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not add issue, please try again or contact the administrator!" });
@ -338,7 +309,7 @@ namespace PlexRequests.UI.Modules
: string.Empty; : string.Empty;
var result = Service.UpdateRequest(originalRequest); var result = await Service.UpdateRequestAsync(originalRequest);
var model = new NotificationModel var model = new NotificationModel
{ {
@ -348,18 +319,18 @@ namespace PlexRequests.UI.Modules
DateTime = DateTime.Now, DateTime = DateTime.Now,
Body = issue == IssueState.Other ? comment : issue.ToString().CamelCaseToWords() Body = issue == IssueState.Other ? comment : issue.ToString().CamelCaseToWords()
}; };
NotificationService.Publish(model); await NotificationService.Publish(model);
return Response.AsJson(result return Response.AsJson(result
? new JsonResponseModel { Result = true } ? new JsonResponseModel { Result = true }
: new JsonResponseModel { Result = false, Message = "Could not add issue, please try again or contact the administrator!" }); : new JsonResponseModel { Result = false, Message = "Could not add issue, please try again or contact the administrator!" });
} }
private Response ClearIssue(int requestId) private async Task<Response> ClearIssue(int requestId)
{ {
this.RequiresClaims ( UserClaims.Admin); this.RequiresClaims(UserClaims.Admin);
var originalRequest = Service.Get(requestId); var originalRequest = await Service.GetAsync(requestId);
if (originalRequest == null) if (originalRequest == null)
{ {
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Request does not exist to clear it!" }); return Response.AsJson(new JsonResponseModel { Result = false, Message = "Request does not exist to clear it!" });
@ -367,16 +338,16 @@ namespace PlexRequests.UI.Modules
originalRequest.Issues = IssueState.None; originalRequest.Issues = IssueState.None;
originalRequest.OtherMessage = string.Empty; originalRequest.OtherMessage = string.Empty;
var result = Service.UpdateRequest(originalRequest); var result = await Service.UpdateRequestAsync(originalRequest);
return Response.AsJson(result return Response.AsJson(result
? new JsonResponseModel { Result = true } ? new JsonResponseModel { Result = true }
: new JsonResponseModel { Result = false, Message = "Could not clear issue, please try again or check the logs" }); : new JsonResponseModel { Result = false, Message = "Could not clear issue, please try again or check the logs" });
} }
private Response ChangeRequestAvailability(int requestId, bool available) private async Task<Response> ChangeRequestAvailability(int requestId, bool available)
{ {
this.RequiresClaims (UserClaims.Admin); this.RequiresClaims(UserClaims.Admin);
var originalRequest = Service.Get(requestId); var originalRequest = await Service.GetAsync(requestId);
if (originalRequest == null) if (originalRequest == null)
{ {
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Request does not exist to change the availability!" }); return Response.AsJson(new JsonResponseModel { Result = false, Message = "Request does not exist to change the availability!" });
@ -384,16 +355,16 @@ namespace PlexRequests.UI.Modules
originalRequest.Available = available; originalRequest.Available = available;
var result = Service.UpdateRequest(originalRequest); var result = await Service.UpdateRequestAsync(originalRequest);
return Response.AsJson(result return Response.AsJson(result
? new { Result = true, Available = available, Message = string.Empty } ? new { Result = true, Available = available, Message = string.Empty }
: new { Result = false, Available = false, Message = "Could not update the availability, please try again or check the logs" }); : new { Result = false, Available = false, Message = "Could not update the availability, please try again or check the logs" });
} }
private Response AddNote(int requestId, string noteArea) private async Task<Response> AddNote(int requestId, string noteArea)
{ {
this.RequiresClaims (UserClaims.Admin); this.RequiresClaims(UserClaims.Admin);
var originalRequest = Service.Get(requestId); var originalRequest = await Service.GetAsync(requestId);
if (originalRequest == null) if (originalRequest == null)
{ {
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Request does not exist to add a note!" }); return Response.AsJson(new JsonResponseModel { Result = false, Message = "Request does not exist to add a note!" });
@ -401,7 +372,7 @@ namespace PlexRequests.UI.Modules
originalRequest.AdminNote = noteArea; originalRequest.AdminNote = noteArea;
var result = Service.UpdateRequest(originalRequest); var result = await Service.UpdateRequestAsync(originalRequest);
return Response.AsJson(result return Response.AsJson(result
? new JsonResponseModel { Result = true } ? new JsonResponseModel { Result = true }
: new JsonResponseModel { Result = false, Message = "Could not update the notes, please try again or check the logs" }); : new JsonResponseModel { Result = false, Message = "Could not update the notes, please try again or check the logs" });

View file

@ -47,7 +47,7 @@ using PlexRequests.Store;
using PlexRequests.UI.Helpers; using PlexRequests.UI.Helpers;
using PlexRequests.UI.Models; using PlexRequests.UI.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms;
using Nancy.Extensions; using Nancy.Extensions;
using PlexRequests.Api.Models.Tv; using PlexRequests.Api.Models.Tv;
using PlexRequests.Store.Models; using PlexRequests.Store.Models;
@ -92,22 +92,22 @@ namespace PlexRequests.UI.Modules
EmailNotificationSettings = email; EmailNotificationSettings = email;
Get["/"] = parameters => RequestLoad(); Get["/", true] = async (x, ct) => await RequestLoad();
Get["movie/{searchTerm}"] = parameters => SearchMovie((string)parameters.searchTerm); Get["movie/{searchTerm}", true] = async (x, ct) => await SearchMovie((string)x.searchTerm);
Get["tv/{searchTerm}"] = parameters => SearchTvShow((string)parameters.searchTerm); Get["tv/{searchTerm}", true] = async (x, ct) => await SearchTvShow((string)x.searchTerm);
Get["music/{searchTerm}"] = parameters => SearchMusic((string)parameters.searchTerm); Get["music/{searchTerm}", true] = async (x, ct) => await SearchMusic((string)x.searchTerm);
Get["music/coverArt/{id}"] = p => GetMusicBrainzCoverArt((string)p.id); Get["music/coverArt/{id}"] = p => GetMusicBrainzCoverArt((string)p.id);
Get["movie/upcoming"] = parameters => UpcomingMovies(); Get["movie/upcoming", true] = async (x, ct) => await UpcomingMovies();
Get["movie/playing"] = parameters => CurrentlyPlayingMovies(); Get["movie/playing", true] = async (x, ct) => await CurrentlyPlayingMovies();
Post["request/movie"] = parameters => RequestMovie((int)Request.Form.movieId); Post["request/movie", true] = async (x, ct) => await RequestMovie((int)Request.Form.movieId);
Post["request/tv"] = parameters => RequestTvShow((int)Request.Form.tvId, (string)Request.Form.seasons); Post["request/tv", true] = async (x, ct) => await RequestTvShow((int)Request.Form.tvId, (string)Request.Form.seasons);
Post["request/album"] = parameters => RequestAlbum((string)Request.Form.albumId); Post["request/album", true] = async (x, ct) => await RequestAlbum((string)Request.Form.albumId);
Post["/notifyuser"] = x => NotifyUser((bool)Request.Form.notify); Post["/notifyuser", true] = async (x, ct) => await NotifyUser((bool)Request.Form.notify);
Get["/notifyuser"] = x => GetUserNotificationSettings(); Get["/notifyuser", true] = async (x, ct) => await GetUserNotificationSettings();
Get["/seasons"] = x => GetSeasons(); Get["/seasons"] = x => GetSeasons();
} }
@ -136,87 +136,86 @@ namespace PlexRequests.UI.Modules
private IRepository<UsersToNotify> UsersToNotifyRepo { get; } private IRepository<UsersToNotify> UsersToNotifyRepo { get; }
private static Logger Log = LogManager.GetCurrentClassLogger(); private static Logger Log = LogManager.GetCurrentClassLogger();
private Negotiator RequestLoad() private async Task<Negotiator> RequestLoad()
{ {
var settings = PrService.GetSettings(); var settings = await PrService.GetSettingsAsync();
Log.Trace("Loading Index"); Log.Trace("Loading Index");
return View["Search/Index", settings]; return View["Search/Index", settings];
} }
private Response UpcomingMovies() private async Task<Response> UpcomingMovies()
{ {
Log.Trace("Loading upcoming movies"); Log.Trace("Loading upcoming movies");
return ProcessMovies(MovieSearchType.Upcoming, string.Empty); return await ProcessMovies(MovieSearchType.Upcoming, string.Empty);
} }
private Response CurrentlyPlayingMovies() private async Task<Response> CurrentlyPlayingMovies()
{ {
Log.Trace("Loading currently playing movies"); Log.Trace("Loading currently playing movies");
return ProcessMovies(MovieSearchType.CurrentlyPlaying, string.Empty); return await ProcessMovies(MovieSearchType.CurrentlyPlaying, string.Empty);
} }
private Response SearchMovie(string searchTerm) private async Task<Response> SearchMovie(string searchTerm)
{ {
Log.Trace("Searching for Movie {0}", searchTerm); Log.Trace("Searching for Movie {0}", searchTerm);
return ProcessMovies(MovieSearchType.Search, searchTerm); return await ProcessMovies(MovieSearchType.Search, searchTerm);
} }
private Response ProcessMovies(MovieSearchType searchType, string searchTerm) private async Task<Response> ProcessMovies(MovieSearchType searchType, string searchTerm)
{ {
var taskList = new List<Task>();
var apiMovies = new List<MovieResult>(); var apiMovies = new List<MovieResult>();
taskList.Add(Task.Factory.StartNew(() => await Task.Factory.StartNew(
{ () =>
switch (searchType)
{ {
case MovieSearchType.Search: switch (searchType)
return MovieApi.SearchMovie(searchTerm).Result.Select(x => new MovieResult() {
{ case MovieSearchType.Search:
Adult = x.Adult, return
BackdropPath = x.BackdropPath, MovieApi.SearchMovie(searchTerm)
GenreIds = x.GenreIds, .Result.Select(
Id = x.Id, x =>
OriginalLanguage = x.OriginalLanguage, new MovieResult()
OriginalTitle = x.OriginalTitle, {
Overview = x.Overview, Adult = x.Adult,
Popularity = x.Popularity, BackdropPath = x.BackdropPath,
PosterPath = x.PosterPath, GenreIds = x.GenreIds,
ReleaseDate = x.ReleaseDate, Id = x.Id,
Title = x.Title, OriginalLanguage = x.OriginalLanguage,
Video = x.Video, OriginalTitle = x.OriginalTitle,
VoteAverage = x.VoteAverage, Overview = x.Overview,
VoteCount = x.VoteCount Popularity = x.Popularity,
}).ToList(); PosterPath = x.PosterPath,
case MovieSearchType.CurrentlyPlaying: ReleaseDate = x.ReleaseDate,
return MovieApi.GetCurrentPlayingMovies().Result.ToList(); Title = x.Title,
case MovieSearchType.Upcoming: Video = x.Video,
return MovieApi.GetUpcomingMovies().Result.ToList(); VoteAverage = x.VoteAverage,
default: VoteCount = x.VoteCount
return new List<MovieResult>(); })
} .ToList();
}).ContinueWith((t) => case MovieSearchType.CurrentlyPlaying:
{ return MovieApi.GetCurrentPlayingMovies().Result.ToList();
apiMovies = t.Result; case MovieSearchType.Upcoming:
})); return MovieApi.GetUpcomingMovies().Result.ToList();
default:
return new List<MovieResult>();
}
}).ContinueWith(
(t) =>
{
apiMovies = t.Result;
});
Dictionary<int, RequestedModel> dbMovies = new Dictionary<int, RequestedModel>(); var allResults = await RequestService.GetAllAsync();
taskList.Add(Task.Factory.StartNew(() => allResults = allResults.Where(x => x.Type == RequestType.Movie);
{
return RequestService.GetAll().Where(x => x.Type == RequestType.Movie);
}).ContinueWith((t) => var distinctResults = allResults.DistinctBy(x => x.ProviderId);
{ var dbMovies = distinctResults.ToDictionary(x => x.ProviderId);
var distinctResults = t.Result.DistinctBy(x => x.ProviderId);
dbMovies = distinctResults.ToDictionary(x => x.ProviderId);
}));
Task.WaitAll(taskList.ToArray());
var cpCached = CpCacher.QueuedIds(); var cpCached = CpCacher.QueuedIds();
var plexMovies = Checker.GetPlexMovies(); var plexMovies = Checker.GetPlexMovies();
var settings = PrService.GetSettings(); var settings = await PrService.GetSettingsAsync();
var viewMovies = new List<SearchMovieViewModel>(); var viewMovies = new List<SearchMovieViewModel>();
foreach (MovieResult movie in apiMovies) foreach (MovieResult movie in apiMovies)
{ {
@ -272,33 +271,20 @@ namespace PlexRequests.UI.Modules
return true; return true;
} }
private Response SearchTvShow(string searchTerm) private async Task<Response> SearchTvShow(string searchTerm)
{ {
Log.Trace("Searching for TV Show {0}", searchTerm); Log.Trace("Searching for TV Show {0}", searchTerm);
var taskList = new List<Task>();
var apiTv = new List<TvMazeSearch>(); var apiTv = new List<TvMazeSearch>();
taskList.Add(Task.Factory.StartNew(() => await Task.Factory.StartNew(() => new TvMazeApi().Search(searchTerm)).ContinueWith((t) =>
{
return new TvMazeApi().Search(searchTerm);
}).ContinueWith((t) =>
{ {
apiTv = t.Result; apiTv = t.Result;
})); });
var dbTv = new Dictionary<int, RequestedModel>(); var allResults = await RequestService.GetAllAsync();
taskList.Add(Task.Factory.StartNew(() => allResults = allResults.Where(x => x.Type == RequestType.TvShow);
{
return RequestService.GetAll().Where(x => x.Type == RequestType.TvShow);
}).ContinueWith((t) => var dbTv = allResults.ToDictionary(x => x.ProviderId);
{
dbTv = t.Result.ToDictionary(x => x.ProviderId);
}));
Task.WaitAll(taskList.ToArray());
if (!apiTv.Any()) if (!apiTv.Any())
{ {
@ -361,31 +347,18 @@ namespace PlexRequests.UI.Modules
return Response.AsJson(viewTv); return Response.AsJson(viewTv);
} }
private Response SearchMusic(string searchTerm) private async Task<Response> SearchMusic(string searchTerm)
{ {
var taskList = new List<Task>();
var apiAlbums = new List<Release>(); var apiAlbums = new List<Release>();
taskList.Add(Task.Factory.StartNew(() => await Task.Run(() => MusicBrainzApi.SearchAlbum(searchTerm)).ContinueWith((t) =>
{
return MusicBrainzApi.SearchAlbum(searchTerm);
}).ContinueWith((t) =>
{ {
apiAlbums = t.Result.releases ?? new List<Release>(); apiAlbums = t.Result.releases ?? new List<Release>();
})); });
var dbAlbum = new Dictionary<string, RequestedModel>(); var allResults = await RequestService.GetAllAsync();
taskList.Add(Task.Factory.StartNew(() => allResults = allResults.Where(x => x.Type == RequestType.Album);
{
return RequestService.GetAll().Where(x => x.Type == RequestType.Album);
}).ContinueWith((t) => var dbAlbum = allResults.ToDictionary(x => x.MusicBrainzId);
{
dbAlbum = t.Result.ToDictionary(x => x.MusicBrainzId);
}));
Task.WaitAll(taskList.ToArray());
var plexAlbums = Checker.GetPlexAlbums(); var plexAlbums = Checker.GetPlexAlbums();
@ -407,7 +380,7 @@ namespace PlexRequests.UI.Modules
DateTime release; DateTime release;
DateTimeHelper.CustomParse(a.ReleaseEvents?.FirstOrDefault()?.date, out release); DateTimeHelper.CustomParse(a.ReleaseEvents?.FirstOrDefault()?.date, out release);
var artist = a.ArtistCredit?.FirstOrDefault()?.artist; var artist = a.ArtistCredit?.FirstOrDefault()?.artist;
if (Checker.IsAlbumAvailable(plexAlbums.ToArray(), a.title, release.ToString("yyyy"), artist.name)) if (Checker.IsAlbumAvailable(plexAlbums.ToArray(), a.title, release.ToString("yyyy"), artist?.name))
{ {
viewA.Available = true; viewA.Available = true;
} }
@ -425,27 +398,26 @@ namespace PlexRequests.UI.Modules
return Response.AsJson(viewAlbum); return Response.AsJson(viewAlbum);
} }
private Response RequestMovie(int movieId) private async Task<Response> RequestMovie(int movieId)
{ {
var movieApi = new TheMovieDbApi(); var movieApi = new TheMovieDbApi();
var movieInfo = movieApi.GetMovieInformation(movieId).Result; var movieInfo = movieApi.GetMovieInformation(movieId).Result;
var fullMovieName = $"{movieInfo.Title}{(movieInfo.ReleaseDate.HasValue ? $" ({movieInfo.ReleaseDate.Value.Year})" : string.Empty)}"; var fullMovieName = $"{movieInfo.Title}{(movieInfo.ReleaseDate.HasValue ? $" ({movieInfo.ReleaseDate.Value.Year})" : string.Empty)}";
Log.Trace("Getting movie info from TheMovieDb"); Log.Trace("Getting movie info from TheMovieDb");
Log.Trace(movieInfo.DumpJson);
//#if !DEBUG //#if !DEBUG
var settings = PrService.GetSettings(); var settings = await PrService.GetSettingsAsync();
// check if the movie has already been requested // check if the movie has already been requested
Log.Info("Requesting movie with id {0}", movieId); Log.Info("Requesting movie with id {0}", movieId);
var existingRequest = RequestService.CheckRequest(movieId); var existingRequest = await RequestService.CheckRequestAsync(movieId);
if (existingRequest != null) if (existingRequest != null)
{ {
// check if the current user is already marked as a requester for this movie, if not, add them // check if the current user is already marked as a requester for this movie, if not, add them
if (!existingRequest.UserHasRequested(Username)) if (!existingRequest.UserHasRequested(Username))
{ {
existingRequest.RequestedUsers.Add(Username); existingRequest.RequestedUsers.Add(Username);
RequestService.UpdateRequest(existingRequest); await RequestService.UpdateRequestAsync(existingRequest);
} }
return Response.AsJson(new JsonResponseModel { Result = true, Message = settings.UsersCanViewOnlyOwnRequests ? $"{fullMovieName} was successfully added!" : $"{fullMovieName} has already been requested!" }); return Response.AsJson(new JsonResponseModel { Result = true, Message = settings.UsersCanViewOnlyOwnRequests ? $"{fullMovieName} was successfully added!" : $"{fullMovieName} has already been requested!" });
@ -461,8 +433,9 @@ namespace PlexRequests.UI.Modules
return Response.AsJson(new JsonResponseModel { Result = false, Message = $"{fullMovieName} is already in Plex!" }); return Response.AsJson(new JsonResponseModel { Result = false, Message = $"{fullMovieName} is already in Plex!" });
} }
} }
catch (ApplicationSettingsException) catch (Exception e)
{ {
Log.Error(e);
return Response.AsJson(new JsonResponseModel { Result = false, Message = $"We could not check if {fullMovieName} is in Plex, are you sure it's correctly setup?" }); return Response.AsJson(new JsonResponseModel { Result = false, Message = $"We could not check if {fullMovieName} is in Plex, are you sure it's correctly setup?" });
} }
//#endif //#endif
@ -473,7 +446,7 @@ namespace PlexRequests.UI.Modules
Type = RequestType.Movie, Type = RequestType.Movie,
Overview = movieInfo.Overview, Overview = movieInfo.Overview,
ImdbId = movieInfo.ImdbId, ImdbId = movieInfo.ImdbId,
PosterPath = "http://image.tmdb.org/t/p/w150/" + movieInfo.PosterPath, PosterPath = "https://image.tmdb.org/t/p/w150/" + movieInfo.PosterPath,
Title = movieInfo.Title, Title = movieInfo.Title,
ReleaseDate = movieInfo.ReleaseDate ?? DateTime.MinValue, ReleaseDate = movieInfo.ReleaseDate ?? DateTime.MinValue,
Status = movieInfo.Status, Status = movieInfo.Status,
@ -484,13 +457,10 @@ namespace PlexRequests.UI.Modules
}; };
Log.Trace(settings.DumpJson());
if (ShouldAutoApprove(RequestType.Movie, settings)) if (ShouldAutoApprove(RequestType.Movie, settings))
{ {
var cpSettings = CpService.GetSettings(); var cpSettings = await CpService.GetSettingsAsync();
Log.Trace("Settings: ");
Log.Trace(cpSettings.DumpJson);
if (cpSettings.Enabled) if (cpSettings.Enabled)
{ {
Log.Info("Adding movie to CP (No approval required)"); Log.Info("Adding movie to CP (No approval required)");
@ -501,8 +471,7 @@ namespace PlexRequests.UI.Modules
{ {
model.Approved = true; model.Approved = true;
Log.Info("Adding movie to database (No approval required)"); Log.Info("Adding movie to database (No approval required)");
RequestService.AddRequest(model); await RequestService.AddRequestAsync(model);
if (ShouldSendNotification()) if (ShouldSendNotification())
{ {
@ -513,7 +482,7 @@ namespace PlexRequests.UI.Modules
DateTime = DateTime.Now, DateTime = DateTime.Now,
NotificationType = NotificationType.NewRequest NotificationType = NotificationType.NewRequest
}; };
NotificationService.Publish(notificationModel); await NotificationService.Publish(notificationModel);
} }
return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullMovieName} was successfully added!" }); return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullMovieName} was successfully added!" });
} }
@ -529,7 +498,7 @@ namespace PlexRequests.UI.Modules
{ {
model.Approved = true; model.Approved = true;
Log.Info("Adding movie to database (No approval required)"); Log.Info("Adding movie to database (No approval required)");
RequestService.AddRequest(model); await RequestService.AddRequestAsync(model);
if (ShouldSendNotification()) if (ShouldSendNotification())
{ {
@ -540,7 +509,7 @@ namespace PlexRequests.UI.Modules
DateTime = DateTime.Now, DateTime = DateTime.Now,
NotificationType = NotificationType.NewRequest NotificationType = NotificationType.NewRequest
}; };
NotificationService.Publish(notificationModel); await NotificationService.Publish(notificationModel);
} }
return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullMovieName} was successfully added!" }); return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullMovieName} was successfully added!" });
@ -550,10 +519,10 @@ namespace PlexRequests.UI.Modules
try try
{ {
Log.Info("Adding movie to database"); Log.Info("Adding movie to database");
var id = RequestService.AddRequest(model); var id = await RequestService.AddRequestAsync(model);
var notificationModel = new NotificationModel { Title = model.Title, User = Username, DateTime = DateTime.Now, NotificationType = NotificationType.NewRequest }; var notificationModel = new NotificationModel { Title = model.Title, User = Username, DateTime = DateTime.Now, NotificationType = NotificationType.NewRequest };
NotificationService.Publish(notificationModel); await NotificationService.Publish(notificationModel);
return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullMovieName} was successfully added!" }); return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullMovieName} was successfully added!" });
} }
@ -570,9 +539,8 @@ namespace PlexRequests.UI.Modules
/// </summary> /// </summary>
/// <param name="showId">The show identifier.</param> /// <param name="showId">The show identifier.</param>
/// <param name="seasons">The seasons.</param> /// <param name="seasons">The seasons.</param>
/// <param name="notify">if set to <c>true</c> [notify].</param>
/// <returns></returns> /// <returns></returns>
private Response RequestTvShow(int showId, string seasons) private async Task<Response> RequestTvShow(int showId, string seasons)
{ {
var tvApi = new TvMazeApi(); var tvApi = new TvMazeApi();
@ -582,18 +550,18 @@ namespace PlexRequests.UI.Modules
string fullShowName = $"{showInfo.name} ({firstAir.Year})"; string fullShowName = $"{showInfo.name} ({firstAir.Year})";
//#if !DEBUG //#if !DEBUG
var settings = PrService.GetSettings(); var settings = await PrService.GetSettingsAsync();
// check if the show has already been requested // check if the show has already been requested
Log.Info("Requesting tv show with id {0}", showId); Log.Info("Requesting tv show with id {0}", showId);
var existingRequest = RequestService.CheckRequest(showId); var existingRequest = await RequestService.CheckRequestAsync(showId);
if (existingRequest != null) if (existingRequest != null)
{ {
// check if the current user is already marked as a requester for this show, if not, add them // check if the current user is already marked as a requester for this show, if not, add them
if (!existingRequest.UserHasRequested(Username)) if (!existingRequest.UserHasRequested(Username))
{ {
existingRequest.RequestedUsers.Add(Username); existingRequest.RequestedUsers.Add(Username);
RequestService.UpdateRequest(existingRequest); await RequestService.UpdateRequestAsync(existingRequest);
} }
return Response.AsJson(new JsonResponseModel { Result = true, Message = settings.UsersCanViewOnlyOwnRequests ? $"{fullShowName} was successfully added!" : $"{fullShowName} has already been requested!" }); return Response.AsJson(new JsonResponseModel { Result = true, Message = settings.UsersCanViewOnlyOwnRequests ? $"{fullShowName} was successfully added!" : $"{fullShowName} has already been requested!" });
} }
@ -606,7 +574,7 @@ namespace PlexRequests.UI.Modules
return Response.AsJson(new JsonResponseModel { Result = false, Message = $"{fullShowName} is already in Plex!" }); return Response.AsJson(new JsonResponseModel { Result = false, Message = $"{fullShowName} is already in Plex!" });
} }
} }
catch (ApplicationSettingsException) catch (Exception)
{ {
return Response.AsJson(new JsonResponseModel { Result = false, Message = $"We could not check if {fullShowName} is in Plex, are you sure it's correctly setup?" }); return Response.AsJson(new JsonResponseModel { Result = false, Message = $"We could not check if {fullShowName} is in Plex, are you sure it's correctly setup?" });
} }
@ -627,7 +595,7 @@ namespace PlexRequests.UI.Modules
RequestedUsers = new List<string> { Username }, RequestedUsers = new List<string> { Username },
Issues = IssueState.None, Issues = IssueState.None,
ImdbId = showInfo.externals?.imdb ?? string.Empty, ImdbId = showInfo.externals?.imdb ?? string.Empty,
SeasonCount = showInfo.seasonCount SeasonCount = showInfo.seasonCount,
}; };
var seasonsList = new List<int>(); var seasonsList = new List<int>();
@ -645,6 +613,7 @@ namespace PlexRequests.UI.Modules
model.SeasonsRequested = "All"; model.SeasonsRequested = "All";
break; break;
default: default:
model.SeasonsRequested = seasons;
var split = seasons.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var split = seasons.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var seasonsCount = new int[split.Length]; var seasonsCount = new int[split.Length];
for (var i = 0; i < split.Length; i++) for (var i = 0; i < split.Length; i++)
@ -661,16 +630,16 @@ namespace PlexRequests.UI.Modules
if (ShouldAutoApprove(RequestType.TvShow, settings)) if (ShouldAutoApprove(RequestType.TvShow, settings))
{ {
var sonarrSettings = SonarrService.GetSettings(); var sonarrSettings = await SonarrService.GetSettingsAsync();
var sender = new TvSender(SonarrApi, SickrageApi); var sender = new TvSender(SonarrApi, SickrageApi);
if (sonarrSettings.Enabled) if (sonarrSettings.Enabled)
{ {
var result = sender.SendToSonarr(sonarrSettings, model); var result = sender.SendToSonarr(sonarrSettings, model);
if (result != null && !string.IsNullOrEmpty(result.title)) if (!string.IsNullOrEmpty(result?.title))
{ {
model.Approved = true; model.Approved = true;
Log.Debug("Adding tv to database requests (No approval required & Sonarr)"); Log.Debug("Adding tv to database requests (No approval required & Sonarr)");
RequestService.AddRequest(model); await RequestService.AddRequestAsync(model);
if (ShouldSendNotification()) if (ShouldSendNotification())
{ {
@ -681,7 +650,7 @@ namespace PlexRequests.UI.Modules
DateTime = DateTime.Now, DateTime = DateTime.Now,
NotificationType = NotificationType.NewRequest NotificationType = NotificationType.NewRequest
}; };
NotificationService.Publish(notify1); await NotificationService.Publish(notify1);
} }
return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullShowName} was successfully added!" }); return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullShowName} was successfully added!" });
} }
@ -699,7 +668,7 @@ namespace PlexRequests.UI.Modules
{ {
model.Approved = true; model.Approved = true;
Log.Debug("Adding tv to database requests (No approval required & SickRage)"); Log.Debug("Adding tv to database requests (No approval required & SickRage)");
RequestService.AddRequest(model); await RequestService.AddRequestAsync(model);
if (ShouldSendNotification()) if (ShouldSendNotification())
{ {
var notify2 = new NotificationModel var notify2 = new NotificationModel
@ -709,7 +678,7 @@ namespace PlexRequests.UI.Modules
DateTime = DateTime.Now, DateTime = DateTime.Now,
NotificationType = NotificationType.NewRequest NotificationType = NotificationType.NewRequest
}; };
NotificationService.Publish(notify2); await NotificationService.Publish(notify2);
} }
return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullShowName} was successfully added!" }); return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullShowName} was successfully added!" });
@ -717,14 +686,33 @@ namespace PlexRequests.UI.Modules
return Response.AsJson(new JsonResponseModel { Result = false, Message = result?.message != null ? "<b>Message From SickRage: </b>" + result.message : "Something went wrong adding the movie to SickRage! Please check your settings." }); return Response.AsJson(new JsonResponseModel { Result = false, Message = result?.message != null ? "<b>Message From SickRage: </b>" + result.message : "Something went wrong adding the movie to SickRage! Please check your settings." });
} }
if (!srSettings.Enabled && !sonarrSettings.Enabled)
{
model.Approved = true;
Log.Debug("Adding tv to database requests (No approval required) and Sonarr/Sickrage not setup");
await RequestService.AddRequestAsync(model);
if (ShouldSendNotification())
{
var notify2 = new NotificationModel
{
Title = model.Title,
User = Username,
DateTime = DateTime.Now,
NotificationType = NotificationType.NewRequest
};
await NotificationService.Publish(notify2);
}
return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullShowName} was successfully added!" });
}
return Response.AsJson(new JsonResponseModel { Result = false, Message = "The request of TV Shows is not correctly set up. Please contact your admin." }); return Response.AsJson(new JsonResponseModel { Result = false, Message = "The request of TV Shows is not correctly set up. Please contact your admin." });
} }
RequestService.AddRequest(model); await RequestService.AddRequestAsync(model);
var notificationModel = new NotificationModel { Title = model.Title, User = Username, DateTime = DateTime.Now, NotificationType = NotificationType.NewRequest }; var notificationModel = new NotificationModel { Title = model.Title, User = Username, DateTime = DateTime.Now, NotificationType = NotificationType.NewRequest };
NotificationService.Publish(notificationModel); await NotificationService.Publish(notificationModel);
return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullShowName} was successfully added!" }); return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullShowName} was successfully added!" });
} }
@ -735,7 +723,8 @@ namespace PlexRequests.UI.Modules
var claims = Context.CurrentUser?.Claims; var claims = Context.CurrentUser?.Claims;
if (claims != null) if (claims != null)
{ {
if (claims.Contains(UserClaims.Admin) || claims.Contains(UserClaims.PowerUser)) var enumerable = claims as string[] ?? claims.ToArray();
if (enumerable.Contains(UserClaims.Admin) || enumerable.Contains(UserClaims.PowerUser))
{ {
sendNotification = false; // Don't bother sending a notification if the user is an admin sendNotification = false; // Don't bother sending a notification if the user is an admin
} }
@ -744,10 +733,10 @@ namespace PlexRequests.UI.Modules
} }
private Response RequestAlbum(string releaseId) private async Task<Response> RequestAlbum(string releaseId)
{ {
var settings = PrService.GetSettings(); var settings = await PrService.GetSettingsAsync();
var existingRequest = RequestService.CheckRequest(releaseId); var existingRequest = await RequestService.CheckRequestAsync(releaseId);
Log.Debug("Checking for an existing request"); Log.Debug("Checking for an existing request");
if (existingRequest != null) if (existingRequest != null)
@ -758,12 +747,11 @@ namespace PlexRequests.UI.Modules
Log.Debug("Not in the requested list so adding them and updating the request. User: {0}", Username); Log.Debug("Not in the requested list so adding them and updating the request. User: {0}", Username);
existingRequest.RequestedUsers.Add(Username); existingRequest.RequestedUsers.Add(Username);
RequestService.UpdateRequest(existingRequest); await RequestService.UpdateRequestAsync(existingRequest);
} }
return Response.AsJson(new JsonResponseModel { Result = true, Message = settings.UsersCanViewOnlyOwnRequests ? $"{existingRequest.Title} was successfully added!" : $"{existingRequest.Title} has already been requested!" }); return Response.AsJson(new JsonResponseModel { Result = true, Message = settings.UsersCanViewOnlyOwnRequests ? $"{existingRequest.Title} was successfully added!" : $"{existingRequest.Title} has already been requested!" });
} }
Log.Debug("This is a new request"); Log.Debug("This is a new request");
var albumInfo = MusicBrainzApi.GetAlbum(releaseId); var albumInfo = MusicBrainzApi.GetAlbum(releaseId);
@ -823,7 +811,7 @@ namespace PlexRequests.UI.Modules
if (!hpSettings.Enabled) if (!hpSettings.Enabled)
{ {
RequestService.AddRequest(model); await RequestService.AddRequestAsync(model);
return return
Response.AsJson(new JsonResponseModel Response.AsJson(new JsonResponseModel
{ {
@ -833,9 +821,9 @@ namespace PlexRequests.UI.Modules
} }
var sender = new HeadphonesSender(HeadphonesApi, hpSettings, RequestService); var sender = new HeadphonesSender(HeadphonesApi, hpSettings, RequestService);
sender.AddAlbum(model).Wait(); await sender.AddAlbum(model);
model.Approved = true; model.Approved = true;
RequestService.AddRequest(model); await RequestService.AddRequestAsync(model);
if (ShouldSendNotification()) if (ShouldSendNotification())
{ {
@ -846,7 +834,7 @@ namespace PlexRequests.UI.Modules
DateTime = DateTime.Now, DateTime = DateTime.Now,
NotificationType = NotificationType.NewRequest NotificationType = NotificationType.NewRequest
}; };
NotificationService.Publish(notify2); await NotificationService.Publish(notify2);
} }
return return
@ -866,9 +854,9 @@ namespace PlexRequests.UI.Modules
DateTime = DateTime.Now, DateTime = DateTime.Now,
NotificationType = NotificationType.NewRequest NotificationType = NotificationType.NewRequest
}; };
NotificationService.Publish(notify2); await NotificationService.Publish(notify2);
} }
var result = RequestService.AddRequest(model); await RequestService.AddRequestAsync(model);
return Response.AsJson(new JsonResponseModel return Response.AsJson(new JsonResponseModel
{ {
Result = true, Result = true,
@ -909,20 +897,22 @@ namespace PlexRequests.UI.Modules
} }
} }
private Response NotifyUser(bool notify) private async Task<Response> NotifyUser(bool notify)
{ {
var auth = Auth.GetSettings().UserAuthentication; var authSettings = await Auth.GetSettingsAsync();
var email = EmailNotificationSettings.GetSettings().EnableUserEmailNotifications; var auth = authSettings.UserAuthentication;
var emailSettings = await EmailNotificationSettings.GetSettingsAsync();
var email = emailSettings.EnableUserEmailNotifications;
if (!auth) if (!auth)
{ {
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Sorry, but this functionality is currently only for users with Plex accounts"}); return Response.AsJson(new JsonResponseModel { Result = false, Message = "Sorry, but this functionality is currently only for users with Plex accounts" });
} }
if (!email) if (!email)
{ {
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Sorry, but your administrator has not yet enabled this functionality." }); return Response.AsJson(new JsonResponseModel { Result = false, Message = "Sorry, but your administrator has not yet enabled this functionality." });
} }
var username = Username; var username = Username;
var originalList = UsersToNotifyRepo.GetAll(); var originalList = await UsersToNotifyRepo.GetAllAsync();
if (!notify) if (!notify)
{ {
if (originalList == null) if (originalList == null)
@ -932,7 +922,7 @@ namespace PlexRequests.UI.Modules
var userToRemove = originalList.FirstOrDefault(x => x.Username == username); var userToRemove = originalList.FirstOrDefault(x => x.Username == username);
if (userToRemove != null) if (userToRemove != null)
{ {
UsersToNotifyRepo.Delete(userToRemove); await UsersToNotifyRepo.DeleteAsync(userToRemove);
} }
return Response.AsJson(new JsonResponseModel { Result = true }); return Response.AsJson(new JsonResponseModel { Result = true });
} }
@ -941,7 +931,7 @@ namespace PlexRequests.UI.Modules
if (originalList == null) if (originalList == null)
{ {
var userModel = new UsersToNotify { Username = username }; var userModel = new UsersToNotify { Username = username };
var insertResult = UsersToNotifyRepo.Insert(userModel); var insertResult = await UsersToNotifyRepo.InsertAsync(userModel);
return Response.AsJson(insertResult != -1 ? new JsonResponseModel { Result = true } : new JsonResponseModel { Result = false, Message = "Could not save, please try again" }); return Response.AsJson(insertResult != -1 ? new JsonResponseModel { Result = true } : new JsonResponseModel { Result = false, Message = "Could not save, please try again" });
} }
@ -953,15 +943,16 @@ namespace PlexRequests.UI.Modules
else else
{ {
var userModel = new UsersToNotify { Username = username }; var userModel = new UsersToNotify { Username = username };
var insertResult = UsersToNotifyRepo.Insert(userModel); var insertResult = await UsersToNotifyRepo.InsertAsync(userModel);
return Response.AsJson(insertResult != -1 ? new JsonResponseModel { Result = true } : new JsonResponseModel { Result = false, Message = "Could not save, please try again" }); return Response.AsJson(insertResult != -1 ? new JsonResponseModel { Result = true } : new JsonResponseModel { Result = false, Message = "Could not save, please try again" });
} }
} }
private Response GetUserNotificationSettings() private async Task<Response> GetUserNotificationSettings()
{ {
var retval = UsersToNotifyRepo.GetAll().FirstOrDefault(x => x.Username == Username); var all = await UsersToNotifyRepo.GetAllAsync();
return Response.AsJson(retval != null); var retVal = all.FirstOrDefault(x => x.Username == Username);
return Response.AsJson(retVal != null);
} }
private Response GetSeasons() private Response GetSeasons()

View file

@ -46,24 +46,24 @@ namespace PlexRequests.UI.Modules
return Response.AsJson(users); return Response.AsJson(users);
} }
private Response CreateUser(string username, string password, string claims) //private Response CreateUser(string username, string password, string claims)
{ //{
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password)) // if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
{ // {
return Response.AsJson(new JsonResponseModel // return Response.AsJson(new JsonResponseModel
{ // {
Result = true, // Result = true,
Message = "Please enter in a valid Username and Password" // Message = "Please enter in a valid Username and Password"
}); // });
} // }
var user = UserMapper.CreateUser(username, password, new string[] {claims}); // var user = UserMapper.CreateUser(username, password, new string[] {claims});
if (user.HasValue) // if (user.HasValue)
{ // {
return Response.AsJson(new JsonResponseModel {Result = true}); // return Response.AsJson(new JsonResponseModel {Result = true});
} // }
return Response.AsJson(new JsonResponseModel {Result = false, Message = "Could not save user"}); // return Response.AsJson(new JsonResponseModel {Result = false, Message = "Could not save user"});
} //}
} }
} }

View file

@ -210,7 +210,15 @@
<Content Include="Content\bootstrap-notify.min.js"> <Content Include="Content\bootstrap-notify.min.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Content\Themes\OriginalBootstrap.css"> <Content Include="Content\base.css">
<DependentUpon>base.scss</DependentUpon>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Content\base.min.css">
<DependentUpon>base.css</DependentUpon>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Content\bootstrap.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Compile Include="Models\JsonResponseModel.cs" /> <Compile Include="Models\JsonResponseModel.cs" />
@ -286,22 +294,21 @@
<Content Include="Content\swagger\underscore-min.js"> <Content Include="Content\swagger\underscore-min.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Content\Themes\OriginalBootstrapCustom.css"> <Content Include="Content\Themes\original.css">
<DependentUpon>OriginalBootstrapCustom.scss</DependentUpon> <DependentUpon>original.scss</DependentUpon>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Content\Themes\OriginalBootstrapCustom.min.css"> <Content Include="Content\Themes\original.min.css">
<DependentUpon>OriginalBootstrapCustom.css</DependentUpon> <DependentUpon>original.css</DependentUpon>
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Content\Themes\PlexBootstrap.css"> <Content Include="Content\Themes\plex.css">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <DependentUpon>plex.scss</DependentUpon>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Content\Themes\PlexBootstrapCustom.css"> <Content Include="Content\Themes\plex.min.css">
<DependentUpon>PlexBootstrapCustom.scss</DependentUpon> <DependentUpon>plex.css</DependentUpon>
</Content> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Content Include="Content\Themes\PlexBootstrapCustom.min.css">
<DependentUpon>PlexBootstrapCustom.css</DependentUpon>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Views\ApiDocs\index.html"> <Content Include="Views\ApiDocs\index.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@ -387,8 +394,9 @@
<None Include="Content\awesome-bootstrap-checkbox.scss"> <None Include="Content\awesome-bootstrap-checkbox.scss">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Include="Content\Themes\PlexBootstrapCustom.scss" /> <None Include="Content\base.scss" />
<None Include="Content\Themes\OriginalBootstrapCustom.scss" /> <None Include="Content\Themes\original.scss" />
<None Include="Content\Themes\plex.scss" />
<Content Include="Content\pace.min.js"> <Content Include="Content\pace.min.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>

View file

@ -77,7 +77,7 @@
<p class="form-group">Current users that are allowed to authenticate: </p> <p class="form-group">Current users that are allowed to authenticate: </p>
<div class="form-group"> <div class="form-group">
<select id="users" multiple="" class="form-control-custom "></select> <select id="users" multiple="" class="form-control-custom" style="height: 180px;"></select>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -161,7 +161,7 @@
$('#users').append("<option>Error!</option>"); $('#users').append("<option>Error!</option>");
return; return;
} }
if (response.users.length > 1) { if (response.users.length > 0) {
$(response.users).each(function () { $(response.users).each(function () {
$('#users').append("<option>" + this + "</option>"); $('#users').append("<option>" + this + "</option>");
}); });

View file

@ -54,10 +54,10 @@
var dots = new Array(count % 10).join('.'); var dots = new Array(count % 10).join('.');
document.getElementById('autoUpdate').innerHTML = "Updating" + dots; document.getElementById('autoUpdate').innerHTML = "Updating" + dots;
}, 1000); }, 1000);
var url = createBaseUrl(base, "autoupdate");
$.ajax({ $.ajax({
type: "Post", type: "Post",
url: url, url: "autoupdate",
data: { url: "@Model.DownloadUri" }, data: { url: "@Model.DownloadUri" },
dataType: "json", dataType: "json",
error: function () { error: function () {

View file

@ -35,19 +35,22 @@
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
<div class="pull-right"> <div class="pull-right">
<div class="btn-group"> <div class="btn-group btn-group-separated">
@if (Context.CurrentUser.IsAuthenticated()) //TODO replace with IsAdmin @if (Context.CurrentUser.IsAuthenticated()) //TODO replace with IsAdmin
{ {
@if (Model.SearchForMovies) @if (Model.SearchForMovies)
{ {
<button id="deleteMovies" class="btn btn-warning-outline delete-category" type="submit"><i class="fa fa-trash"></i> Delete Movies</button>
<button id="approveMovies" class="btn btn-success-outline approve-category" type="submit"><i class="fa fa-plus"></i> Approve Movies</button> <button id="approveMovies" class="btn btn-success-outline approve-category" type="submit"><i class="fa fa-plus"></i> Approve Movies</button>
} }
@if (Model.SearchForTvShows) @if (Model.SearchForTvShows)
{ {
<button id="deleteTVShows" class="btn btn-warning-outline delete-category" type="submit" style="display: none;"><i class="fa fa-trash"></i> Delete TV Shows</button>
<button id="approveTVShows" class="btn btn-success-outline approve-category" type="submit" style="display: none;"><i class="fa fa-plus"></i> Approve TV Shows</button> <button id="approveTVShows" class="btn btn-success-outline approve-category" type="submit" style="display: none;"><i class="fa fa-plus"></i> Approve TV Shows</button>
} }
@if (Model.SearchForMusic) @if (Model.SearchForMusic)
{ {
<button id="deleteMusics" class="btn btn-warning-outline delete-category" type="submit" style="display: none;"><i class="fa fa-trash"></i> Delete Music</button>
<button id="approveMusic" class="btn btn-success-outline approve-category" type="submit" style="display: none;"><i class="fa fa-plus"></i> Approve Music</button> <button id="approveMusic" class="btn btn-success-outline approve-category" type="submit" style="display: none;"><i class="fa fa-plus"></i> Approve Music</button>
} }
} }
@ -132,7 +135,7 @@
<div class="col-sm-2"> <div class="col-sm-2">
{{#if_eq type "movie"}} {{#if_eq type "movie"}}
{{#if posterPath}} {{#if posterPath}}
<img class="img-responsive" src="http://image.tmdb.org/t/p/w150/{{posterPath}}" alt="poster"> <img class="img-responsive" src="https://image.tmdb.org/t/p/w150/{{posterPath}}" alt="poster">
{{/if}} {{/if}}
{{/if_eq}} {{/if_eq}}
{{#if_eq type "tv"}} {{#if_eq type "tv"}}
@ -169,7 +172,7 @@
{{/if_eq}} {{/if_eq}}
</div> </div>
{{#if_eq type "tv"}} {{#if_eq type "tv"}}
<div>Series Requested: {{seriesRequested}}</div> <div>Seasons Requested: {{seriesRequested}}</div>
{{/if_eq}} {{/if_eq}}
{{#if requestedUsers}} {{#if requestedUsers}}
<div>Requested By: {{requestedUsers}}</div> <div>Requested By: {{requestedUsers}}</div>

View file

@ -144,7 +144,7 @@
{{#if_eq type "movie"}} {{#if_eq type "movie"}}
{{#if posterPath}} {{#if posterPath}}
<img class="img-responsive" src="http://image.tmdb.org/t/p/w150/{{posterPath}}" alt="poster"> <img class="img-responsive" src="https://image.tmdb.org/t/p/w150/{{posterPath}}" alt="poster">
{{/if}} {{/if}}
{{/if_eq}} {{/if_eq}}
{{#if_eq type "tv"}} {{#if_eq type "tv"}}

View file

@ -1,8 +1,4 @@
[ [
{
"outputFile": "Content/custom.css",
"inputFile": "Content/custom.scss"
},
{ {
"outputFile": "Content/pace.css", "outputFile": "Content/pace.css",
"inputFile": "Content/pace.scss" "inputFile": "Content/pace.scss"
@ -18,5 +14,17 @@
{ {
"outputFile": "Content/Themes/PlexBootstrapCustom.css", "outputFile": "Content/Themes/PlexBootstrapCustom.css",
"inputFile": "Content/Themes/PlexBootstrapCustom.scss" "inputFile": "Content/Themes/PlexBootstrapCustom.scss"
},
{
"outputFile": "Content/base.css",
"inputFile": "Content/base.scss"
},
{
"outputFile": "Content/Themes/plex.css",
"inputFile": "Content/Themes/plex.scss"
},
{
"outputFile": "Content/Themes/original.css",
"inputFile": "Content/Themes/original.scss"
} }
] ]

View file

@ -1,6 +1,6 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012 # Visual Studio 14
VisualStudioVersion = 14.0.25123.0 VisualStudioVersion = 14.0.25123.0
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.UI", "PlexRequests.UI\PlexRequests.UI.csproj", "{68F5F5F3-B8BB-4911-875F-6F00AAE04EA6}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.UI", "PlexRequests.UI\PlexRequests.UI.csproj", "{68F5F5F3-B8BB-4911-875F-6F00AAE04EA6}"
@ -35,20 +35,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Services.Tests
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Updater", "PlexRequests.Updater\PlexRequests.Updater.csproj", "{EBE6FC1C-7B4B-47E9-AF54-0EE0604A2BE5}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Updater", "PlexRequests.Updater\PlexRequests.Updater.csproj", "{EBE6FC1C-7B4B-47E9-AF54-0EE0604A2BE5}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.Helpers.Tests", "PlexRequests.Helpers.Tests\PlexRequests.Helpers.Tests.csproj", "{0E6395D3-B074-49E8-898D-0EB99E507E0E}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1252336D-42A3-482A-804C-836E60173DFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1252336D-42A3-482A-804C-836E60173DFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1252336D-42A3-482A-804C-836E60173DFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1252336D-42A3-482A-804C-836E60173DFA}.Release|Any CPU.Build.0 = Release|Any CPU
{566EFA49-68F8-4716-9693-A6B3F2624DEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{566EFA49-68F8-4716-9693-A6B3F2624DEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{566EFA49-68F8-4716-9693-A6B3F2624DEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{566EFA49-68F8-4716-9693-A6B3F2624DEA}.Release|Any CPU.Build.0 = Release|Any CPU
{68F5F5F3-B8BB-4911-875F-6F00AAE04EA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {68F5F5F3-B8BB-4911-875F-6F00AAE04EA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{68F5F5F3-B8BB-4911-875F-6F00AAE04EA6}.Debug|Any CPU.Build.0 = Debug|Any CPU {68F5F5F3-B8BB-4911-875F-6F00AAE04EA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{68F5F5F3-B8BB-4911-875F-6F00AAE04EA6}.Release|Any CPU.ActiveCfg = Release|Any CPU {68F5F5F3-B8BB-4911-875F-6F00AAE04EA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -57,26 +51,38 @@ Global
{8CB8D235-2674-442D-9C6A-35FCAEEB160D}.Debug|Any CPU.Build.0 = Debug|Any CPU {8CB8D235-2674-442D-9C6A-35FCAEEB160D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8CB8D235-2674-442D-9C6A-35FCAEEB160D}.Release|Any CPU.ActiveCfg = Release|Any CPU {8CB8D235-2674-442D-9C6A-35FCAEEB160D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8CB8D235-2674-442D-9C6A-35FCAEEB160D}.Release|Any CPU.Build.0 = Release|Any CPU {8CB8D235-2674-442D-9C6A-35FCAEEB160D}.Release|Any CPU.Build.0 = Release|Any CPU
{92433867-2B7B-477B-A566-96C382427525}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{92433867-2B7B-477B-A566-96C382427525}.Debug|Any CPU.Build.0 = Debug|Any CPU
{92433867-2B7B-477B-A566-96C382427525}.Release|Any CPU.ActiveCfg = Release|Any CPU
{92433867-2B7B-477B-A566-96C382427525}.Release|Any CPU.Build.0 = Release|Any CPU
{95834072-A675-415D-AA8F-877C91623810}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {95834072-A675-415D-AA8F-877C91623810}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{95834072-A675-415D-AA8F-877C91623810}.Debug|Any CPU.Build.0 = Debug|Any CPU {95834072-A675-415D-AA8F-877C91623810}.Debug|Any CPU.Build.0 = Debug|Any CPU
{95834072-A675-415D-AA8F-877C91623810}.Release|Any CPU.ActiveCfg = Release|Any CPU {95834072-A675-415D-AA8F-877C91623810}.Release|Any CPU.ActiveCfg = Release|Any CPU
{95834072-A675-415D-AA8F-877C91623810}.Release|Any CPU.Build.0 = Release|Any CPU {95834072-A675-415D-AA8F-877C91623810}.Release|Any CPU.Build.0 = Release|Any CPU
{A930E2CF-79E2-45F9-B06A-9A719A254CE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A930E2CF-79E2-45F9-B06A-9A719A254CE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A930E2CF-79E2-45F9-B06A-9A719A254CE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A930E2CF-79E2-45F9-B06A-9A719A254CE4}.Release|Any CPU.Build.0 = Release|Any CPU
{CB37A5F8-6DFC-4554-99D3-A42B502E4591}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CB37A5F8-6DFC-4554-99D3-A42B502E4591}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB37A5F8-6DFC-4554-99D3-A42B502E4591}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB37A5F8-6DFC-4554-99D3-A42B502E4591}.Release|Any CPU.Build.0 = Release|Any CPU
{DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}.Debug|Any CPU.Build.0 = Debug|Any CPU {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}.Release|Any CPU.ActiveCfg = Release|Any CPU {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}.Release|Any CPU.Build.0 = Release|Any CPU {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}.Release|Any CPU.Build.0 = Release|Any CPU
{92433867-2B7B-477B-A566-96C382427525}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{92433867-2B7B-477B-A566-96C382427525}.Debug|Any CPU.Build.0 = Debug|Any CPU
{92433867-2B7B-477B-A566-96C382427525}.Release|Any CPU.ActiveCfg = Release|Any CPU
{92433867-2B7B-477B-A566-96C382427525}.Release|Any CPU.Build.0 = Release|Any CPU
{1252336D-42A3-482A-804C-836E60173DFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1252336D-42A3-482A-804C-836E60173DFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1252336D-42A3-482A-804C-836E60173DFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1252336D-42A3-482A-804C-836E60173DFA}.Release|Any CPU.Build.0 = Release|Any CPU
{A930E2CF-79E2-45F9-B06A-9A719A254CE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A930E2CF-79E2-45F9-B06A-9A719A254CE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A930E2CF-79E2-45F9-B06A-9A719A254CE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A930E2CF-79E2-45F9-B06A-9A719A254CE4}.Release|Any CPU.Build.0 = Release|Any CPU
{FCFECD5D-47F6-454D-8692-E27A921BE655}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FCFECD5D-47F6-454D-8692-E27A921BE655}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FCFECD5D-47F6-454D-8692-E27A921BE655}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FCFECD5D-47F6-454D-8692-E27A921BE655}.Release|Any CPU.Build.0 = Release|Any CPU
{566EFA49-68F8-4716-9693-A6B3F2624DEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{566EFA49-68F8-4716-9693-A6B3F2624DEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{566EFA49-68F8-4716-9693-A6B3F2624DEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{566EFA49-68F8-4716-9693-A6B3F2624DEA}.Release|Any CPU.Build.0 = Release|Any CPU
{CB37A5F8-6DFC-4554-99D3-A42B502E4591}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CB37A5F8-6DFC-4554-99D3-A42B502E4591}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB37A5F8-6DFC-4554-99D3-A42B502E4591}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB37A5F8-6DFC-4554-99D3-A42B502E4591}.Release|Any CPU.Build.0 = Release|Any CPU
{EAADB4AC-064F-4D3A-AFF9-64A33131A9A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EAADB4AC-064F-4D3A-AFF9-64A33131A9A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EAADB4AC-064F-4D3A-AFF9-64A33131A9A7}.Debug|Any CPU.Build.0 = Debug|Any CPU {EAADB4AC-064F-4D3A-AFF9-64A33131A9A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EAADB4AC-064F-4D3A-AFF9-64A33131A9A7}.Release|Any CPU.ActiveCfg = Release|Any CPU {EAADB4AC-064F-4D3A-AFF9-64A33131A9A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -85,12 +91,10 @@ Global
{EBE6FC1C-7B4B-47E9-AF54-0EE0604A2BE5}.Debug|Any CPU.Build.0 = Debug|Any CPU {EBE6FC1C-7B4B-47E9-AF54-0EE0604A2BE5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EBE6FC1C-7B4B-47E9-AF54-0EE0604A2BE5}.Release|Any CPU.ActiveCfg = Release|Any CPU {EBE6FC1C-7B4B-47E9-AF54-0EE0604A2BE5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EBE6FC1C-7B4B-47E9-AF54-0EE0604A2BE5}.Release|Any CPU.Build.0 = Release|Any CPU {EBE6FC1C-7B4B-47E9-AF54-0EE0604A2BE5}.Release|Any CPU.Build.0 = Release|Any CPU
{FCFECD5D-47F6-454D-8692-E27A921BE655}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0E6395D3-B074-49E8-898D-0EB99E507E0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FCFECD5D-47F6-454D-8692-E27A921BE655}.Debug|Any CPU.Build.0 = Debug|Any CPU {0E6395D3-B074-49E8-898D-0EB99E507E0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FCFECD5D-47F6-454D-8692-E27A921BE655}.Release|Any CPU.ActiveCfg = Release|Any CPU {0E6395D3-B074-49E8-898D-0EB99E507E0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FCFECD5D-47F6-454D-8692-E27A921BE655}.Release|Any CPU.Build.0 = Release|Any CPU {0E6395D3-B074-49E8-898D-0EB99E507E0E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View file

@ -3,9 +3,9 @@ configuration: Release
assembly_info: assembly_info:
patch: true patch: true
file: '**\AssemblyInfo.*' file: '**\AssemblyInfo.*'
assembly_version: '1.7.4' assembly_version: '1.7.5'
assembly_file_version: '{version}' assembly_file_version: '{version}'
assembly_informational_version: '1.7.4' assembly_informational_version: '1.7.5'
before_build: before_build:
- cmd: appveyor-retry nuget restore - cmd: appveyor-retry nuget restore
build: build: