mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-15 01:32:55 -07:00
merge in tidusjar's dev changes, remove approve all, fix alignment on approve all category buttons
This commit is contained in:
commit
dce5983720
26 changed files with 592 additions and 109 deletions
|
@ -64,6 +64,7 @@
|
||||||
<Compile Include="SickRage\SickRageStatus.cs" />
|
<Compile Include="SickRage\SickRageStatus.cs" />
|
||||||
<Compile Include="SickRage\SickRageTvAdd.cs" />
|
<Compile Include="SickRage\SickRageTvAdd.cs" />
|
||||||
<Compile Include="Sonarr\SonarrAddSeries.cs" />
|
<Compile Include="Sonarr\SonarrAddSeries.cs" />
|
||||||
|
<Compile Include="Sonarr\SonarrError.cs" />
|
||||||
<Compile Include="Sonarr\SonarrProfile.cs" />
|
<Compile Include="Sonarr\SonarrProfile.cs" />
|
||||||
<Compile Include="Sonarr\SystemStatus.cs" />
|
<Compile Include="Sonarr\SystemStatus.cs" />
|
||||||
<Compile Include="Tv\Authentication.cs" />
|
<Compile Include="Tv\Authentication.cs" />
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace PlexRequests.Api.Models.Sonarr
|
namespace PlexRequests.Api.Models.Sonarr
|
||||||
{
|
{
|
||||||
public class Season
|
public class Season
|
||||||
|
@ -23,6 +25,8 @@ namespace PlexRequests.Api.Models.Sonarr
|
||||||
public string imdbId { get; set; }
|
public string imdbId { get; set; }
|
||||||
public string titleSlug { get; set; }
|
public string titleSlug { get; set; }
|
||||||
public int id { get; set; }
|
public int id { get; set; }
|
||||||
|
[JsonIgnore]
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AddOptions
|
public class AddOptions
|
||||||
|
|
36
PlexRequests.Api.Models/Sonarr/SonarrError.cs
Normal file
36
PlexRequests.Api.Models/Sonarr/SonarrError.cs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2016 Jamie Rees
|
||||||
|
// File: SonarrError.cs
|
||||||
|
// Created By: Jamie Rees
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
// ************************************************************************/
|
||||||
|
#endregion
|
||||||
|
namespace PlexRequests.Api.Models.Sonarr
|
||||||
|
{
|
||||||
|
public class SonarrError
|
||||||
|
{
|
||||||
|
public string propertyName { get; set; }
|
||||||
|
public string errorMessage { get; set; }
|
||||||
|
public string attemptedValue { get; set; }
|
||||||
|
public string[] formattedMessageArguments { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,13 +26,9 @@
|
||||||
#endregion
|
#endregion
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
|
||||||
using System.Text;
|
|
||||||
using System.Xml;
|
|
||||||
using System.Xml.Serialization;
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
|
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
|
@ -96,20 +92,13 @@ namespace PlexRequests.Api
|
||||||
throw new ApplicationException(message, response.ErrorException);
|
throw new ApplicationException(message, response.ErrorException);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var json = JsonConvert.DeserializeObject<T>(response.Content);
|
var json = JsonConvert.DeserializeObject<T>(response.Content);
|
||||||
|
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Fatal(e);
|
|
||||||
Log.Info(response.Content);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public T DeserializeXml<T>(string input)
|
private T DeserializeXml<T>(string input)
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
var ser = new XmlSerializer(typeof(T));
|
var ser = new XmlSerializer(typeof(T));
|
||||||
|
|
|
@ -27,9 +27,14 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
using NLog;
|
using NLog;
|
||||||
using PlexRequests.Api.Interfaces;
|
using PlexRequests.Api.Interfaces;
|
||||||
using PlexRequests.Api.Models.Sonarr;
|
using PlexRequests.Api.Models.Sonarr;
|
||||||
|
using PlexRequests.Helpers;
|
||||||
|
|
||||||
using RestSharp;
|
using RestSharp;
|
||||||
|
|
||||||
namespace PlexRequests.Api
|
namespace PlexRequests.Api
|
||||||
|
@ -56,7 +61,8 @@ namespace PlexRequests.Api
|
||||||
|
|
||||||
public SonarrAddSeries AddSeries(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, int seasonCount, int[] seasons, string apiKey, Uri baseUrl)
|
public SonarrAddSeries AddSeries(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, int seasonCount, int[] seasons, string apiKey, Uri baseUrl)
|
||||||
{
|
{
|
||||||
|
Log.Debug("Adding series {0}", title);
|
||||||
|
Log.Debug("Seasons = {0}, out of {1} seasons", seasons.DumpJson(), seasonCount);
|
||||||
var request = new RestRequest
|
var request = new RestRequest
|
||||||
{
|
{
|
||||||
Resource = "/api/Series?",
|
Resource = "/api/Series?",
|
||||||
|
@ -74,7 +80,6 @@ namespace PlexRequests.Api
|
||||||
rootFolderPath = rootPath
|
rootFolderPath = rootPath
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
for (var i = 1; i <= seasonCount; i++)
|
for (var i = 1; i <= seasonCount; i++)
|
||||||
{
|
{
|
||||||
var season = new Season
|
var season = new Season
|
||||||
|
@ -85,12 +90,25 @@ namespace PlexRequests.Api
|
||||||
options.seasons.Add(season);
|
options.seasons.Add(season);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.Debug("Sonarr API Options:");
|
||||||
|
Log.Debug(options.DumpJson());
|
||||||
|
|
||||||
request.AddHeader("X-Api-Key", apiKey);
|
request.AddHeader("X-Api-Key", apiKey);
|
||||||
request.AddJsonBody(options);
|
request.AddJsonBody(options);
|
||||||
|
|
||||||
var obj = Api.ExecuteJson<SonarrAddSeries>(request, baseUrl);
|
SonarrAddSeries result;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = Api.ExecuteJson<SonarrAddSeries>(request, baseUrl);
|
||||||
|
}
|
||||||
|
catch (JsonSerializationException jse)
|
||||||
|
{
|
||||||
|
Log.Error(jse);
|
||||||
|
var error = Api.ExecuteJson<SonarrError>(request, baseUrl);
|
||||||
|
result = new SonarrAddSeries { ErrorMessage = error.errorMessage };
|
||||||
|
}
|
||||||
|
|
||||||
return obj;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SystemStatus SystemStatus(string apiKey, Uri baseUrl)
|
public SystemStatus SystemStatus(string apiKey, Uri baseUrl)
|
||||||
|
|
|
@ -79,6 +79,10 @@ namespace PlexRequests.Core
|
||||||
public RequestedModel Get(int id)
|
public RequestedModel Get(int id)
|
||||||
{
|
{
|
||||||
var blob = Repo.Get(id);
|
var blob = Repo.Get(id);
|
||||||
|
if (blob == null)
|
||||||
|
{
|
||||||
|
return new RequestedModel();
|
||||||
|
}
|
||||||
var model = ByteConverterHelper.ReturnObject<RequestedModel>(blob.Content);
|
var model = ByteConverterHelper.ReturnObject<RequestedModel>(blob.Content);
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,6 +74,7 @@
|
||||||
<Compile Include="Models\StatusModel.cs" />
|
<Compile Include="Models\StatusModel.cs" />
|
||||||
<Compile Include="Models\UserProperties.cs" />
|
<Compile Include="Models\UserProperties.cs" />
|
||||||
<Compile Include="SettingModels\AuthenticationSettings.cs" />
|
<Compile Include="SettingModels\AuthenticationSettings.cs" />
|
||||||
|
<Compile Include="SettingModels\HeadphonesSettings.cs" />
|
||||||
<Compile Include="SettingModels\PushoverNotificationSettings.cs" />
|
<Compile Include="SettingModels\PushoverNotificationSettings.cs" />
|
||||||
<Compile Include="SettingModels\PushBulletNotificationSettings.cs" />
|
<Compile Include="SettingModels\PushBulletNotificationSettings.cs" />
|
||||||
<Compile Include="SettingModels\EmailNotificationSettings.cs" />
|
<Compile Include="SettingModels\EmailNotificationSettings.cs" />
|
||||||
|
|
58
PlexRequests.Core/SettingModels/HeadphonesSettings.cs
Normal file
58
PlexRequests.Core/SettingModels/HeadphonesSettings.cs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2016 Jamie Rees
|
||||||
|
// File: CouchPotatoSettings.cs
|
||||||
|
// Created By: Jamie Rees
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
// ************************************************************************/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using PlexRequests.Helpers;
|
||||||
|
|
||||||
|
namespace PlexRequests.Core.SettingModels
|
||||||
|
{
|
||||||
|
public class HeadphonesSettings : Settings
|
||||||
|
{
|
||||||
|
public string Enabled { get; set; }
|
||||||
|
public string Ip { get; set; }
|
||||||
|
public int Port { get; set; }
|
||||||
|
public int ApiKey { get; set; }
|
||||||
|
public bool Ssl { get; set; }
|
||||||
|
public string SubDir { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public Uri FullUri
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(SubDir))
|
||||||
|
{
|
||||||
|
var formattedSubDir = Ip.ReturnUriWithSubDir(Port, Ssl, SubDir);
|
||||||
|
return formattedSubDir;
|
||||||
|
}
|
||||||
|
var formatted = Ip.ReturnUri(Port, Ssl);
|
||||||
|
return formatted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,12 +32,12 @@ using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
using PlexRequests.Api.Interfaces;
|
using PlexRequests.Api.Interfaces;
|
||||||
using PlexRequests.Api.Models;
|
|
||||||
using PlexRequests.Api.Models.Plex;
|
using PlexRequests.Api.Models.Plex;
|
||||||
using PlexRequests.Core;
|
using PlexRequests.Core;
|
||||||
using PlexRequests.Core.SettingModels;
|
using PlexRequests.Core.SettingModels;
|
||||||
using PlexRequests.Helpers.Exceptions;
|
using PlexRequests.Helpers.Exceptions;
|
||||||
using PlexRequests.Services.Interfaces;
|
using PlexRequests.Services.Interfaces;
|
||||||
|
using PlexRequests.Store;
|
||||||
|
|
||||||
namespace PlexRequests.Services.Tests
|
namespace PlexRequests.Services.Tests
|
||||||
{
|
{
|
||||||
|
@ -66,7 +66,7 @@ namespace PlexRequests.Services.Tests
|
||||||
var requestMock = new Mock<IRequestService>();
|
var requestMock = new Mock<IRequestService>();
|
||||||
var plexMock = new Mock<IPlexApi>();
|
var plexMock = new Mock<IPlexApi>();
|
||||||
|
|
||||||
var searchResult = new PlexSearch {Video = new List<Video> {new Video {Title = "title", Year = "2011"} } };
|
var searchResult = new PlexSearch { Video = new List<Video> { new Video { Title = "title", Year = "2011" } } };
|
||||||
|
|
||||||
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "abc" });
|
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "abc" });
|
||||||
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
|
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
|
||||||
|
@ -87,7 +87,7 @@ namespace PlexRequests.Services.Tests
|
||||||
var requestMock = new Mock<IRequestService>();
|
var requestMock = new Mock<IRequestService>();
|
||||||
var plexMock = new Mock<IPlexApi>();
|
var plexMock = new Mock<IPlexApi>();
|
||||||
|
|
||||||
var searchResult = new PlexSearch { Directory = new Directory1 {Title = "title", Year = "2013"} };
|
var searchResult = new PlexSearch { Directory = new Directory1 { Title = "title", Year = "2013" } };
|
||||||
|
|
||||||
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "abc" });
|
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "abc" });
|
||||||
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
|
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
|
||||||
|
@ -100,6 +100,27 @@ namespace PlexRequests.Services.Tests
|
||||||
Assert.That(result, Is.True);
|
Assert.That(result, Is.True);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void IsAvailableDirectoryTitleWithoutYearTest()
|
||||||
|
{
|
||||||
|
var settingsMock = new Mock<ISettingsService<PlexSettings>>();
|
||||||
|
var authMock = new Mock<ISettingsService<AuthenticationSettings>>();
|
||||||
|
var requestMock = new Mock<IRequestService>();
|
||||||
|
var plexMock = new Mock<IPlexApi>();
|
||||||
|
|
||||||
|
var searchResult = new PlexSearch { Directory = new Directory1 { Title = "title", } };
|
||||||
|
|
||||||
|
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "abc" });
|
||||||
|
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
|
||||||
|
plexMock.Setup(x => x.SearchContent(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Uri>())).Returns(searchResult);
|
||||||
|
|
||||||
|
Checker = new PlexAvailabilityChecker(settingsMock.Object, authMock.Object, requestMock.Object, plexMock.Object);
|
||||||
|
|
||||||
|
var result = Checker.IsAvailable("title", null);
|
||||||
|
|
||||||
|
Assert.That(result, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void IsNotAvailableTest()
|
public void IsNotAvailableTest()
|
||||||
{
|
{
|
||||||
|
@ -108,7 +129,7 @@ namespace PlexRequests.Services.Tests
|
||||||
var requestMock = new Mock<IRequestService>();
|
var requestMock = new Mock<IRequestService>();
|
||||||
var plexMock = new Mock<IPlexApi>();
|
var plexMock = new Mock<IPlexApi>();
|
||||||
|
|
||||||
var searchResult = new PlexSearch { Video = new List<Video> { new Video { Title = "wrong tistle", Year = "2011"} } };
|
var searchResult = new PlexSearch { Video = new List<Video> { new Video { Title = "wrong title", Year = "2011" } } };
|
||||||
|
|
||||||
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "abc" });
|
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "abc" });
|
||||||
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
|
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
|
||||||
|
@ -121,6 +142,27 @@ namespace PlexRequests.Services.Tests
|
||||||
Assert.That(result, Is.False);
|
Assert.That(result, Is.False);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void IsNotAvailableTestWihtoutYear()
|
||||||
|
{
|
||||||
|
var settingsMock = new Mock<ISettingsService<PlexSettings>>();
|
||||||
|
var authMock = new Mock<ISettingsService<AuthenticationSettings>>();
|
||||||
|
var requestMock = new Mock<IRequestService>();
|
||||||
|
var plexMock = new Mock<IPlexApi>();
|
||||||
|
|
||||||
|
var searchResult = new PlexSearch { Video = new List<Video> { new Video { Title = "wrong title" } } };
|
||||||
|
|
||||||
|
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "abc" });
|
||||||
|
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
|
||||||
|
plexMock.Setup(x => x.SearchContent(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Uri>())).Returns(searchResult);
|
||||||
|
|
||||||
|
Checker = new PlexAvailabilityChecker(settingsMock.Object, authMock.Object, requestMock.Object, plexMock.Object);
|
||||||
|
|
||||||
|
var result = Checker.IsAvailable("title", null);
|
||||||
|
|
||||||
|
Assert.That(result, Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void IsYearDoesNotMatchTest()
|
public void IsYearDoesNotMatchTest()
|
||||||
{
|
{
|
||||||
|
@ -141,5 +183,155 @@ namespace PlexRequests.Services.Tests
|
||||||
|
|
||||||
Assert.That(result, Is.False);
|
Assert.That(result, Is.False);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TitleDoesNotMatchTest()
|
||||||
|
{
|
||||||
|
var settingsMock = new Mock<ISettingsService<PlexSettings>>();
|
||||||
|
var authMock = new Mock<ISettingsService<AuthenticationSettings>>();
|
||||||
|
var requestMock = new Mock<IRequestService>();
|
||||||
|
var plexMock = new Mock<IPlexApi>();
|
||||||
|
|
||||||
|
var searchResult = new PlexSearch { Video = new List<Video> { new Video { Title = "title23", Year = "2019" } } };
|
||||||
|
|
||||||
|
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "abc" });
|
||||||
|
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
|
||||||
|
plexMock.Setup(x => x.SearchContent(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Uri>())).Returns(searchResult);
|
||||||
|
|
||||||
|
Checker = new PlexAvailabilityChecker(settingsMock.Object, authMock.Object, requestMock.Object, plexMock.Object);
|
||||||
|
|
||||||
|
var result = Checker.IsAvailable("title", "2019");
|
||||||
|
|
||||||
|
Assert.That(result, Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TitleDoesNotMatchWithoutYearTest()
|
||||||
|
{
|
||||||
|
var settingsMock = new Mock<ISettingsService<PlexSettings>>();
|
||||||
|
var authMock = new Mock<ISettingsService<AuthenticationSettings>>();
|
||||||
|
var requestMock = new Mock<IRequestService>();
|
||||||
|
var plexMock = new Mock<IPlexApi>();
|
||||||
|
|
||||||
|
var searchResult = new PlexSearch { Video = new List<Video> { new Video { Title = "title23" } } };
|
||||||
|
|
||||||
|
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "abc" });
|
||||||
|
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
|
||||||
|
plexMock.Setup(x => x.SearchContent(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Uri>())).Returns(searchResult);
|
||||||
|
|
||||||
|
Checker = new PlexAvailabilityChecker(settingsMock.Object, authMock.Object, requestMock.Object, plexMock.Object);
|
||||||
|
|
||||||
|
var result = Checker.IsAvailable("title", null);
|
||||||
|
|
||||||
|
Assert.That(result, Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAndUpdateNoPlexSettingsTest()
|
||||||
|
{
|
||||||
|
var settingsMock = new Mock<ISettingsService<PlexSettings>>();
|
||||||
|
var authMock = new Mock<ISettingsService<AuthenticationSettings>>();
|
||||||
|
var requestMock = new Mock<IRequestService>();
|
||||||
|
var plexMock = new Mock<IPlexApi>();
|
||||||
|
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
|
||||||
|
Checker = new PlexAvailabilityChecker(settingsMock.Object, authMock.Object, requestMock.Object, plexMock.Object);
|
||||||
|
|
||||||
|
Checker.CheckAndUpdateAll(1);
|
||||||
|
|
||||||
|
requestMock.Verify(x => x.BatchUpdate(It.IsAny<List<RequestedModel>>()), Times.Never);
|
||||||
|
requestMock.Verify(x => x.Get(It.IsAny<int>()), Times.Never);
|
||||||
|
plexMock.Verify(x => x.SearchContent(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Uri>()), Times.Never);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAndUpdateNoAuthSettingsTest()
|
||||||
|
{
|
||||||
|
var settingsMock = new Mock<ISettingsService<PlexSettings>>();
|
||||||
|
var authMock = new Mock<ISettingsService<AuthenticationSettings>>();
|
||||||
|
var requestMock = new Mock<IRequestService>();
|
||||||
|
var plexMock = new Mock<IPlexApi>();
|
||||||
|
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "123" });
|
||||||
|
|
||||||
|
Checker = new PlexAvailabilityChecker(settingsMock.Object, authMock.Object, requestMock.Object, plexMock.Object);
|
||||||
|
|
||||||
|
Checker.CheckAndUpdateAll(1);
|
||||||
|
|
||||||
|
requestMock.Verify(x => x.BatchUpdate(It.IsAny<List<RequestedModel>>()), Times.Never);
|
||||||
|
requestMock.Verify(x => x.Get(It.IsAny<int>()), Times.Never);
|
||||||
|
plexMock.Verify(x => x.SearchContent(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Uri>()), Times.Never);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAndUpdateNoRequestsTest()
|
||||||
|
{
|
||||||
|
var settingsMock = new Mock<ISettingsService<PlexSettings>>();
|
||||||
|
var authMock = new Mock<ISettingsService<AuthenticationSettings>>();
|
||||||
|
var requestMock = new Mock<IRequestService>();
|
||||||
|
var plexMock = new Mock<IPlexApi>();
|
||||||
|
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "192.168.1.1" });
|
||||||
|
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
|
||||||
|
requestMock.Setup(x => x.GetAll()).Returns(new List<RequestedModel>());
|
||||||
|
|
||||||
|
Checker = new PlexAvailabilityChecker(settingsMock.Object, authMock.Object, requestMock.Object, plexMock.Object);
|
||||||
|
|
||||||
|
Checker.CheckAndUpdateAll(1);
|
||||||
|
|
||||||
|
requestMock.Verify(x => x.BatchUpdate(It.IsAny<List<RequestedModel>>()), Times.Never);
|
||||||
|
requestMock.Verify(x => x.Get(It.IsAny<int>()), Times.Never);
|
||||||
|
plexMock.Verify(x => x.SearchContent(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Uri>()), Times.Never);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Ignore("Need to work out Plex Directory vs Video objects")]
|
||||||
|
public void CheckAndUpdateRequestsTest()
|
||||||
|
{
|
||||||
|
|
||||||
|
var requests = new List<RequestedModel> {
|
||||||
|
new RequestedModel
|
||||||
|
{
|
||||||
|
Id = 123,
|
||||||
|
Title = "title1",
|
||||||
|
Available = false,
|
||||||
|
},
|
||||||
|
new RequestedModel
|
||||||
|
{
|
||||||
|
Id=222,
|
||||||
|
Title = "title3",
|
||||||
|
Available = false
|
||||||
|
},
|
||||||
|
new RequestedModel
|
||||||
|
{
|
||||||
|
Id = 333,
|
||||||
|
Title= "missingTitle",
|
||||||
|
Available = false
|
||||||
|
},
|
||||||
|
new RequestedModel
|
||||||
|
{
|
||||||
|
Id= 444,
|
||||||
|
Title = "already found",
|
||||||
|
Available = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var search = new PlexSearch { };
|
||||||
|
|
||||||
|
var settingsMock = new Mock<ISettingsService<PlexSettings>>();
|
||||||
|
var authMock = new Mock<ISettingsService<AuthenticationSettings>>();
|
||||||
|
var requestMock = new Mock<IRequestService>();
|
||||||
|
var plexMock = new Mock<IPlexApi>();
|
||||||
|
settingsMock.Setup(x => x.GetSettings()).Returns(new PlexSettings { Ip = "192.168.1.1" });
|
||||||
|
authMock.Setup(x => x.GetSettings()).Returns(new AuthenticationSettings { PlexAuthToken = "abc" });
|
||||||
|
requestMock.Setup(x => x.GetAll()).Returns(requests);
|
||||||
|
|
||||||
|
Checker = new PlexAvailabilityChecker(settingsMock.Object, authMock.Object, requestMock.Object, plexMock.Object);
|
||||||
|
|
||||||
|
Checker.CheckAndUpdateAll(1);
|
||||||
|
|
||||||
|
requestMock.Verify(x => x.BatchUpdate(It.IsAny<List<RequestedModel>>()), Times.Never);
|
||||||
|
requestMock.Verify(x => x.Get(It.IsAny<int>()), Times.Never);
|
||||||
|
plexMock.Verify(x => x.SearchContent(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Uri>()), Times.Never);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -84,6 +84,10 @@
|
||||||
<Project>{566EFA49-68F8-4716-9693-A6B3F2624DEA}</Project>
|
<Project>{566EFA49-68F8-4716-9693-A6B3F2624DEA}</Project>
|
||||||
<Name>PlexRequests.Services</Name>
|
<Name>PlexRequests.Services</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\PlexRequests.Store\PlexRequests.Store.csproj">
|
||||||
|
<Project>{92433867-2B7B-477B-A566-96C382427525}</Project>
|
||||||
|
<Name>PlexRequests.Store</Name>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Choose>
|
<Choose>
|
||||||
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
|
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
|
||||||
|
|
|
@ -24,6 +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
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
|
@ -52,7 +53,7 @@ namespace PlexRequests.Services
|
||||||
private ISettingsService<AuthenticationSettings> Auth { get; }
|
private ISettingsService<AuthenticationSettings> Auth { get; }
|
||||||
private IRequestService RequestService { get; }
|
private IRequestService RequestService { get; }
|
||||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||||
private IPlexApi PlexApi { get; set; }
|
private IPlexApi PlexApi { get; }
|
||||||
|
|
||||||
|
|
||||||
public void CheckAndUpdateAll(long check)
|
public void CheckAndUpdateAll(long check)
|
||||||
|
@ -62,7 +63,7 @@ namespace PlexRequests.Services
|
||||||
var requests = RequestService.GetAll();
|
var requests = RequestService.GetAll();
|
||||||
|
|
||||||
var requestedModels = requests as RequestedModel[] ?? requests.ToArray();
|
var requestedModels = requests as RequestedModel[] ?? requests.ToArray();
|
||||||
if (!ValidateSettings(plexSettings, authSettings, requestedModels))
|
if (!ValidateSettings(plexSettings, authSettings) || !requestedModels.Any())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -97,38 +98,22 @@ namespace PlexRequests.Services
|
||||||
{
|
{
|
||||||
throw new ApplicationSettingsException("The settings are not configured for Plex or Authentication");
|
throw new ApplicationSettingsException("The settings are not configured for Plex or Authentication");
|
||||||
}
|
}
|
||||||
|
var results = PlexApi.SearchContent(authSettings.PlexAuthToken, title, plexSettings.FullUri);
|
||||||
if (!string.IsNullOrEmpty(year))
|
if (!string.IsNullOrEmpty(year))
|
||||||
{
|
{
|
||||||
var results = PlexApi.SearchContent(authSettings.PlexAuthToken, title, plexSettings.FullUri);
|
var result = results.Video?.FirstOrDefault(x => x.Title.Equals(title, StringComparison.InvariantCultureIgnoreCase) && x.Year == year);
|
||||||
var result = results.Video?.FirstOrDefault(x => x.Title.Contains(title) && x.Year == year);
|
|
||||||
var directoryTitle = results.Directory?.Title == title && results.Directory?.Year == year;
|
var directoryTitle = results.Directory?.Title == title && results.Directory?.Year == year;
|
||||||
return result?.Title != null || directoryTitle;
|
return result?.Title != null || directoryTitle;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var results = PlexApi.SearchContent(authSettings.PlexAuthToken, title, plexSettings.FullUri);
|
var result = results.Video?.FirstOrDefault(x => x.Title.Equals(title, StringComparison.InvariantCultureIgnoreCase));
|
||||||
var result = results.Video?.FirstOrDefault(x => x.Title.Contains(title));
|
|
||||||
var directoryTitle = results.Directory?.Title == title;
|
var directoryTitle = results.Directory?.Title == title;
|
||||||
return result?.Title != null || directoryTitle;
|
return result?.Title != null || directoryTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ValidateSettings(PlexSettings plex, AuthenticationSettings auth, IEnumerable<RequestedModel> requests)
|
|
||||||
{
|
|
||||||
if (plex.Ip == null || auth.PlexAuthToken == null || requests == null)
|
|
||||||
{
|
|
||||||
Log.Warn("A setting is null, Ensure Plex is configured correctly, and we have a Plex Auth token.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!requests.Any())
|
|
||||||
{
|
|
||||||
Log.Info("We have no requests to check if they are available on Plex.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ValidateSettings(PlexSettings plex, AuthenticationSettings auth)
|
private bool ValidateSettings(PlexSettings plex, AuthenticationSettings auth)
|
||||||
{
|
{
|
||||||
if (plex?.Ip == null || auth?.PlexAuthToken == null)
|
if (plex?.Ip == null || auth?.PlexAuthToken == null)
|
||||||
|
|
|
@ -27,12 +27,11 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
using Mono.Data.Sqlite;
|
using Mono.Data.Sqlite;
|
||||||
|
|
||||||
using NLog;
|
using NLog;
|
||||||
using PlexRequests.Helpers;
|
|
||||||
using PlexRequests.Store.Repository;
|
|
||||||
|
|
||||||
namespace PlexRequests.Store
|
namespace PlexRequests.Store
|
||||||
{
|
{
|
||||||
|
@ -44,12 +43,14 @@ namespace PlexRequests.Store
|
||||||
Factory = provider;
|
Factory = provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SqliteFactory Factory { get; set; }
|
private SqliteFactory Factory { get; }
|
||||||
|
private string CurrentPath =>Path.Combine(Path.GetDirectoryName(Application.ExecutablePath) ?? string.Empty, DbFile);
|
||||||
|
|
||||||
public virtual bool CheckDb()
|
public virtual bool CheckDb()
|
||||||
{
|
{
|
||||||
Log.Trace("Checking DB");
|
Log.Trace("Checking DB");
|
||||||
if (!File.Exists(DbFile))
|
Console.WriteLine(CurrentPath);
|
||||||
|
if (!File.Exists(CurrentPath))
|
||||||
{
|
{
|
||||||
Log.Trace("DB doesn't exist, creating a new one");
|
Log.Trace("DB doesn't exist, creating a new one");
|
||||||
CreateDatabase();
|
CreateDatabase();
|
||||||
|
@ -72,7 +73,7 @@ namespace PlexRequests.Store
|
||||||
{
|
{
|
||||||
throw new SqliteException("Factory returned null");
|
throw new SqliteException("Factory returned null");
|
||||||
}
|
}
|
||||||
fact.ConnectionString = "Data Source=" + DbFile;
|
fact.ConnectionString = "Data Source=" + CurrentPath;
|
||||||
return fact;
|
return fact;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +84,7 @@ namespace PlexRequests.Store
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (File.Create(DbFile))
|
using (File.Create(CurrentPath))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Windows.Forms" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
|
|
@ -59,6 +59,7 @@ namespace PlexRequests.UI.Tests
|
||||||
private Mock<ISettingsService<EmailNotificationSettings>> EmailMock { get; set; }
|
private Mock<ISettingsService<EmailNotificationSettings>> EmailMock { get; set; }
|
||||||
private Mock<ISettingsService<PushbulletNotificationSettings>> PushbulletSettings { get; set; }
|
private Mock<ISettingsService<PushbulletNotificationSettings>> PushbulletSettings { get; set; }
|
||||||
private Mock<ISettingsService<PushoverNotificationSettings>> PushoverSettings { get; set; }
|
private Mock<ISettingsService<PushoverNotificationSettings>> PushoverSettings { get; set; }
|
||||||
|
private Mock<ISettingsService<HeadphonesSettings>> HeadphonesSettings { get; set; }
|
||||||
private Mock<IPlexApi> PlexMock { get; set; }
|
private Mock<IPlexApi> PlexMock { get; set; }
|
||||||
private Mock<ISonarrApi> SonarrApiMock { get; set; }
|
private Mock<ISonarrApi> SonarrApiMock { get; set; }
|
||||||
private Mock<IPushbulletApi> PushbulletApi { get; set; }
|
private Mock<IPushbulletApi> PushbulletApi { get; set; }
|
||||||
|
@ -94,6 +95,7 @@ namespace PlexRequests.UI.Tests
|
||||||
PushoverSettings = new Mock<ISettingsService<PushoverNotificationSettings>>();
|
PushoverSettings = new Mock<ISettingsService<PushoverNotificationSettings>>();
|
||||||
PushoverApi = new Mock<IPushoverApi>();
|
PushoverApi = new Mock<IPushoverApi>();
|
||||||
NotificationService = new Mock<INotificationService>();
|
NotificationService = new Mock<INotificationService>();
|
||||||
|
HeadphonesSettings = new Mock<ISettingsService<HeadphonesSettings>>();
|
||||||
|
|
||||||
Bootstrapper = new ConfigurableBootstrapper(with =>
|
Bootstrapper = new ConfigurableBootstrapper(with =>
|
||||||
{
|
{
|
||||||
|
@ -114,6 +116,7 @@ namespace PlexRequests.UI.Tests
|
||||||
with.Dependency(PushoverSettings.Object);
|
with.Dependency(PushoverSettings.Object);
|
||||||
with.Dependency(PushoverApi.Object);
|
with.Dependency(PushoverApi.Object);
|
||||||
with.Dependency(NotificationService.Object);
|
with.Dependency(NotificationService.Object);
|
||||||
|
with.Dependency(HeadphonesSettings.Object);
|
||||||
with.RootPathProvider<TestRootPathProvider>();
|
with.RootPathProvider<TestRootPathProvider>();
|
||||||
with.RequestStartup((container, pipelines, context) =>
|
with.RequestStartup((container, pipelines, context) =>
|
||||||
{
|
{
|
||||||
|
|
|
@ -76,9 +76,9 @@ namespace PlexRequests.UI
|
||||||
container.Register<ISettingsService<EmailNotificationSettings>, SettingsServiceV2<EmailNotificationSettings>>();
|
container.Register<ISettingsService<EmailNotificationSettings>, SettingsServiceV2<EmailNotificationSettings>>();
|
||||||
container.Register<ISettingsService<PushbulletNotificationSettings>, SettingsServiceV2<PushbulletNotificationSettings>>();
|
container.Register<ISettingsService<PushbulletNotificationSettings>, SettingsServiceV2<PushbulletNotificationSettings>>();
|
||||||
container.Register<ISettingsService<PushoverNotificationSettings>, SettingsServiceV2<PushoverNotificationSettings>>();
|
container.Register<ISettingsService<PushoverNotificationSettings>, SettingsServiceV2<PushoverNotificationSettings>>();
|
||||||
|
container.Register<ISettingsService<HeadphonesSettings>, SettingsServiceV2<HeadphonesSettings>>();
|
||||||
|
|
||||||
// Repo's
|
// Repo's
|
||||||
container.Register<IRepository<RequestedModel>, GenericRepository<RequestedModel>>();
|
|
||||||
container.Register<IRepository<LogEntity>, GenericRepository<LogEntity>>();
|
container.Register<IRepository<LogEntity>, GenericRepository<LogEntity>>();
|
||||||
container.Register<IRequestService, JsonRequestService>();
|
container.Register<IRequestService, JsonRequestService>();
|
||||||
container.Register<ISettingsRepository, SettingsJsonRepository>();
|
container.Register<ISettingsRepository, SettingsJsonRepository>();
|
||||||
|
@ -101,13 +101,13 @@ namespace PlexRequests.UI
|
||||||
|
|
||||||
SubscribeAllObservers(container);
|
SubscribeAllObservers(container);
|
||||||
base.ConfigureRequestContainer(container, context);
|
base.ConfigureRequestContainer(container, context);
|
||||||
|
|
||||||
|
TaskManager.TaskFactory = new PlexTaskFactory();
|
||||||
|
TaskManager.Initialize(new PlexRegistry());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
|
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
|
||||||
{
|
{
|
||||||
TaskManager.TaskFactory = new PlexTaskFactory();
|
|
||||||
TaskManager.Initialize(new PlexRegistry());
|
|
||||||
|
|
||||||
CookieBasedSessions.Enable(pipelines, CryptographyConfiguration.Default);
|
CookieBasedSessions.Enable(pipelines, CryptographyConfiguration.Default);
|
||||||
|
|
||||||
StaticConfiguration.DisableErrorTraces = false;
|
StaticConfiguration.DisableErrorTraces = false;
|
||||||
|
@ -123,6 +123,7 @@ namespace PlexRequests.UI
|
||||||
|
|
||||||
FormsAuthentication.Enable(pipelines, formsAuthConfiguration);
|
FormsAuthentication.Enable(pipelines, formsAuthConfiguration);
|
||||||
|
|
||||||
|
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
|
||||||
ServicePointManager.ServerCertificateValidationCallback +=
|
ServicePointManager.ServerCertificateValidationCallback +=
|
||||||
(sender, certificate, chain, sslPolicyErrors) => true;
|
(sender, certificate, chain, sslPolicyErrors) => true;
|
||||||
|
|
||||||
|
|
|
@ -58,36 +58,6 @@ $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Approve all
|
// Approve all
|
||||||
$('#approveAll').click(function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
var buttonId = e.target.id;
|
|
||||||
var origHtml = $(this).html();
|
|
||||||
|
|
||||||
if ($('#' + buttonId).text() === " Loading...") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
loadingButton(buttonId, "success");
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
type: 'post',
|
|
||||||
url: '/approval/approveall',
|
|
||||||
dataType: "json",
|
|
||||||
success: function (response) {
|
|
||||||
if (checkJsonResponse(response)) {
|
|
||||||
generateNotify("Success! All requests approved!", "success");
|
|
||||||
initLoad();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: function (e) {
|
|
||||||
console.log(e);
|
|
||||||
generateNotify("Something went wrong!", "danger");
|
|
||||||
},
|
|
||||||
complete: function (e) {
|
|
||||||
finishLoading(buttonId, "success", origHtml)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
$('#approveMovies').click(function (e) {
|
$('#approveMovies').click(function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var buttonId = e.target.id;
|
var buttonId = e.target.id;
|
||||||
|
|
|
@ -24,17 +24,13 @@
|
||||||
// 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 Nancy;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
using PlexRequests.Api.Interfaces;
|
using PlexRequests.Api.Interfaces;
|
||||||
using PlexRequests.Api.Models.SickRage;
|
using PlexRequests.Api.Models.SickRage;
|
||||||
using PlexRequests.Api.Models.Sonarr;
|
using PlexRequests.Api.Models.Sonarr;
|
||||||
using PlexRequests.Core;
|
|
||||||
using PlexRequests.Core.SettingModels;
|
using PlexRequests.Core.SettingModels;
|
||||||
using PlexRequests.Helpers;
|
using PlexRequests.Helpers;
|
||||||
using PlexRequests.Store;
|
using PlexRequests.Store;
|
||||||
using PlexRequests.UI.Models;
|
|
||||||
|
|
||||||
namespace PlexRequests.UI.Helpers
|
namespace PlexRequests.UI.Helpers
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,12 +12,12 @@ namespace PlexRequests.UI.Jobs
|
||||||
//typeof(AvailabilityUpdateService);
|
//typeof(AvailabilityUpdateService);
|
||||||
var container = TinyIoCContainer.Current;
|
var container = TinyIoCContainer.Current;
|
||||||
|
|
||||||
var a= container.ResolveAll(typeof(T));
|
var a= container.Resolve(typeof(T));
|
||||||
|
|
||||||
object outT;
|
object outT;
|
||||||
container.TryResolve(typeof(T), out outT);
|
container.TryResolve(typeof(T), out outT);
|
||||||
|
|
||||||
return (T)outT;
|
return (T)a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -28,6 +28,8 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Dynamic;
|
using System.Dynamic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Web.UI.HtmlControls;
|
||||||
|
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
using MarkdownSharp;
|
using MarkdownSharp;
|
||||||
|
|
||||||
|
@ -66,6 +68,7 @@ namespace PlexRequests.UI.Modules
|
||||||
private ISettingsService<EmailNotificationSettings> EmailService { get; }
|
private ISettingsService<EmailNotificationSettings> EmailService { get; }
|
||||||
private ISettingsService<PushbulletNotificationSettings> PushbulletService { get; }
|
private ISettingsService<PushbulletNotificationSettings> PushbulletService { get; }
|
||||||
private ISettingsService<PushoverNotificationSettings> PushoverService { get; }
|
private ISettingsService<PushoverNotificationSettings> PushoverService { get; }
|
||||||
|
private ISettingsService<HeadphonesSettings> HeadphonesService { get; }
|
||||||
private IPlexApi PlexApi { get; }
|
private IPlexApi PlexApi { get; }
|
||||||
private ISonarrApi SonarrApi { get; }
|
private ISonarrApi SonarrApi { get; }
|
||||||
private IPushbulletApi PushbulletApi { get; }
|
private IPushbulletApi PushbulletApi { get; }
|
||||||
|
@ -90,7 +93,8 @@ namespace PlexRequests.UI.Modules
|
||||||
ISettingsService<PushoverNotificationSettings> pushoverSettings,
|
ISettingsService<PushoverNotificationSettings> pushoverSettings,
|
||||||
IPushoverApi pushoverApi,
|
IPushoverApi pushoverApi,
|
||||||
IRepository<LogEntity> logsRepo,
|
IRepository<LogEntity> logsRepo,
|
||||||
INotificationService notify) : base("admin")
|
INotificationService notify,
|
||||||
|
ISettingsService<HeadphonesSettings> headphones) : base("admin")
|
||||||
{
|
{
|
||||||
RpService = rpService;
|
RpService = rpService;
|
||||||
CpService = cpService;
|
CpService = cpService;
|
||||||
|
@ -108,6 +112,7 @@ namespace PlexRequests.UI.Modules
|
||||||
PushoverService = pushoverSettings;
|
PushoverService = pushoverSettings;
|
||||||
PushoverApi = pushoverApi;
|
PushoverApi = pushoverApi;
|
||||||
NotificationService = notify;
|
NotificationService = notify;
|
||||||
|
HeadphonesService = headphones;
|
||||||
|
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
this.RequiresAuthentication();
|
this.RequiresAuthentication();
|
||||||
|
@ -155,6 +160,9 @@ namespace PlexRequests.UI.Modules
|
||||||
Get["/loglevel"] = _ => GetLogLevels();
|
Get["/loglevel"] = _ => GetLogLevels();
|
||||||
Post["/loglevel"] = _ => UpdateLogLevels(Request.Form.level);
|
Post["/loglevel"] = _ => UpdateLogLevels(Request.Form.level);
|
||||||
Get["/loadlogs"] = _ => LoadLogs();
|
Get["/loadlogs"] = _ => LoadLogs();
|
||||||
|
|
||||||
|
Get["/headphones"] = _ => Headphones();
|
||||||
|
Post["/headphones"] = _ => SaveHeadphones();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Negotiator Authentication()
|
private Negotiator Authentication()
|
||||||
|
@ -567,5 +575,32 @@ namespace PlexRequests.UI.Modules
|
||||||
LoggingHelper.ReconfigureLogLevel(newLevel);
|
LoggingHelper.ReconfigureLogLevel(newLevel);
|
||||||
return Response.AsJson(new JsonResponseModel { Result = true, Message = $"The new log level is now {newLevel}"});
|
return Response.AsJson(new JsonResponseModel { Result = true, Message = $"The new log level is now {newLevel}"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Negotiator Headphones()
|
||||||
|
{
|
||||||
|
var settings = HeadphonesService.GetSettings();
|
||||||
|
return View["Headphones", settings];
|
||||||
|
}
|
||||||
|
|
||||||
|
private Response SaveHeadphones()
|
||||||
|
{
|
||||||
|
var settings = this.Bind<HeadphonesSettings>();
|
||||||
|
|
||||||
|
var valid = this.Validate(settings);
|
||||||
|
if (!valid.IsValid)
|
||||||
|
{
|
||||||
|
var error = valid.SendJsonError();
|
||||||
|
Log.Info("Error validating Headphones settings, message: {0}", error.Message);
|
||||||
|
return Response.AsJson(error);
|
||||||
|
}
|
||||||
|
Log.Trace(settings.DumpJson());
|
||||||
|
|
||||||
|
var result = HeadphonesService.SaveSettings(settings);
|
||||||
|
|
||||||
|
Log.Info("Saved headphones settings, result: {0}", result);
|
||||||
|
return Response.AsJson(result
|
||||||
|
? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Headphones!" }
|
||||||
|
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -57,6 +57,7 @@ namespace PlexRequests.UI.Modules
|
||||||
Post["/sonarr"] = _ => SonarrTest();
|
Post["/sonarr"] = _ => SonarrTest();
|
||||||
Post["/plex"] = _ => PlexTest();
|
Post["/plex"] = _ => PlexTest();
|
||||||
Post["/sickrage"] = _ => SickRageTest();
|
Post["/sickrage"] = _ => SickRageTest();
|
||||||
|
Post["/headphones"] = _ => HeadphonesTest();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,5 +169,10 @@ namespace PlexRequests.UI.Modules
|
||||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = message });
|
return Response.AsJson(new JsonResponseModel { Result = false, Message = message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Response HeadphonesTest()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException(); //TODO
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -133,7 +133,7 @@ namespace PlexRequests.UI.Modules
|
||||||
return Response.AsJson(new JsonResponseModel
|
return Response.AsJson(new JsonResponseModel
|
||||||
{
|
{
|
||||||
Result = false,
|
Result = false,
|
||||||
Message = "Could not add the series to Sonarr"
|
Message = result.ErrorMessage ?? "Could not add the series to Sonarr"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,7 +340,7 @@ namespace PlexRequests.UI.Modules
|
||||||
if (sonarr.Enabled)
|
if (sonarr.Enabled)
|
||||||
{
|
{
|
||||||
var res = sender.SendToSonarr(sonarr, r);
|
var res = sender.SendToSonarr(sonarr, r);
|
||||||
if (res != null)
|
if (!string.IsNullOrEmpty(res?.title))
|
||||||
{
|
{
|
||||||
r.Approved = true;
|
r.Approved = true;
|
||||||
updatedRequests.Add(r);
|
updatedRequests.Add(r);
|
||||||
|
@ -348,15 +348,25 @@ namespace PlexRequests.UI.Modules
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log.Error("Could not approve and send the TV {0} to Sonarr!", r.Title);
|
Log.Error("Could not approve and send the TV {0} to Sonarr!", r.Title);
|
||||||
|
Log.Error("Error message: {0}", res?.ErrorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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 }
|
||||||
: new JsonResponseModel { Result = false, Message = "We could not approve all of the requests. Please try again or check the logs." });
|
: new JsonResponseModel { Result = false, Message = "We could not approve all of the requests. Please try again or check the logs." });
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Fatal(e);
|
||||||
|
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool SendMovie(CouchPotatoSettings settings, RequestedModel r, ICouchPotatoApi cp)
|
private bool SendMovie(CouchPotatoSettings settings, RequestedModel r, ICouchPotatoApi cp)
|
||||||
{
|
{
|
||||||
|
|
|
@ -371,18 +371,19 @@ namespace PlexRequests.UI.Modules
|
||||||
if (sonarrSettings.Enabled)
|
if (sonarrSettings.Enabled)
|
||||||
{
|
{
|
||||||
var result = sender.SendToSonarr(sonarrSettings, model);
|
var result = sender.SendToSonarr(sonarrSettings, model);
|
||||||
if (result != null)
|
if (result != null && !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);
|
RequestService.AddRequest(model);
|
||||||
|
|
||||||
return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullShowName} was successfully added!" });
|
|
||||||
}
|
|
||||||
var notify1 = new NotificationModel { Title = model.Title, User = model.RequestedBy, DateTime = DateTime.Now, NotificationType = NotificationType.NewRequest };
|
var notify1 = new NotificationModel { Title = model.Title, User = model.RequestedBy, DateTime = DateTime.Now, NotificationType = NotificationType.NewRequest };
|
||||||
NotificationService.Publish(notify1);
|
NotificationService.Publish(notify1);
|
||||||
|
|
||||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something went wrong adding the movie to Sonarr! Please check your settings." });
|
return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullShowName} was successfully added!" });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return Response.AsJson(new JsonResponseModel { Result = false, Message = result?.ErrorMessage ?? "Something went wrong adding the movie to Sonarr! Please check your settings." });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -371,6 +371,9 @@
|
||||||
<Content Include="Views\Admin\PushoverNotifications.cshtml">
|
<Content Include="Views\Admin\PushoverNotifications.cshtml">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="Views\Admin\Headphones.cshtml">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<None Include="Web.Debug.config">
|
<None Include="Web.Debug.config">
|
||||||
<DependentUpon>web.config</DependentUpon>
|
<DependentUpon>web.config</DependentUpon>
|
||||||
</None>
|
</None>
|
||||||
|
|
162
PlexRequests.UI/Views/Admin/Headphones.cshtml
Normal file
162
PlexRequests.UI/Views/Admin/Headphones.cshtml
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
@Html.Partial("_Sidebar")
|
||||||
|
@{
|
||||||
|
int port;
|
||||||
|
if (Model.Port == 0)
|
||||||
|
{
|
||||||
|
port = 8081;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
port = Model.Port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<div class="col-sm-8 col-sm-push-1">
|
||||||
|
<form class="form-horizontal" method="POST" id="mainForm">
|
||||||
|
<fieldset>
|
||||||
|
<legend>Headphones Settings</legend>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
@if (Model.Enabled)
|
||||||
|
{
|
||||||
|
<input type="checkbox" id="Enabled" name="Enabled" checked="checked"><text>Enabled</text>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<input type="checkbox" id="Enabled" name="Enabled"><text>Enabled</text>
|
||||||
|
}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
@if (Model.Ssl)
|
||||||
|
{
|
||||||
|
<input type="checkbox" id="Ssl" name="Ssl" checked="checked"><text>SSL</text>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<input type="checkbox" id="Ssl" name="Ssl"><text>SSL</text>
|
||||||
|
}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="Ip" class="control-label">Headphones Hostname or IP</label>
|
||||||
|
<div class="">
|
||||||
|
<input type="text" class="form-control form-control-custom " id="Ip" name="Ip" placeholder="localhost" value="@Model.Ip">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="portNumber" class="control-label">Port</label>
|
||||||
|
|
||||||
|
<div class="">
|
||||||
|
<input type="text" class="form-control form-control-custom " id="portNumber" name="Port" placeholder="Port Number" value="@port">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="ApiKey" class="control-label">Headphones API Key</label>
|
||||||
|
<div>
|
||||||
|
<input type="text" class="form-control form-control-custom " id="ApiKey" name="ApiKey" value="@Model.ApiKey">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="SubDir" class="control-label">Headphones SubDirectory</label>
|
||||||
|
<div>
|
||||||
|
<input type="text" class="form-control form-control-custom " id="SubDir" name="SubDir" value="@Model.SubDir">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div>
|
||||||
|
<button type="submit" id="getProfiles" class="btn btn-primary-outline">Get Quality Profiles</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div>
|
||||||
|
<button id="testHeadphones" type="submit" class="btn btn-primary-outline">Test Connectivity</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div>
|
||||||
|
<button id="save" type="submit" class="btn btn-primary-outline">Submit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(function() {
|
||||||
|
|
||||||
|
$('#testHeadphones').click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var $form = $("#mainForm");
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: $form.prop("method"),
|
||||||
|
url: "/test/headphones",
|
||||||
|
data: $form.serialize(),
|
||||||
|
dataType: "json",
|
||||||
|
success: function (response) {
|
||||||
|
console.log(response);
|
||||||
|
if (response.result === true) {
|
||||||
|
generateNotify(response.message, "success");
|
||||||
|
$('#authToken').val(response.authToken);
|
||||||
|
} else {
|
||||||
|
generateNotify(response.message, "warning");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (e) {
|
||||||
|
console.log(e);
|
||||||
|
generateNotify("Something went wrong!", "danger");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#save').click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var port = $('#portNumber').val();
|
||||||
|
if (isNaN(port)) {
|
||||||
|
generateNotify("You must specify a Port.", "warning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var $form = $("#mainForm");
|
||||||
|
var qualityProfile = $("#profiles option:selected").val();
|
||||||
|
var data = $form.serialize();
|
||||||
|
data = data + "&profileId=" + qualityProfile;
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: $form.prop("method"),
|
||||||
|
data: data,
|
||||||
|
url: $form.prop("action"),
|
||||||
|
dataType: "json",
|
||||||
|
success: function (response) {
|
||||||
|
if (response.result === true) {
|
||||||
|
generateNotify(response.message, "success");
|
||||||
|
} else {
|
||||||
|
generateNotify(response.message, "warning");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (e) {
|
||||||
|
console.log(e);
|
||||||
|
generateNotify("Something went wrong!", "danger");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -52,6 +52,14 @@
|
||||||
{
|
{
|
||||||
<a class="list-group-item" href="/admin/sickrage">SickRage</a>
|
<a class="list-group-item" href="/admin/sickrage">SickRage</a>
|
||||||
}
|
}
|
||||||
|
@if (Context.Request.Path == "/admin/headphones")
|
||||||
|
{
|
||||||
|
<a class="list-group-item active" href="/admin/headphones">Headphones</a>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<a class="list-group-item" href="/admin/headphones">Headphones</a>
|
||||||
|
}
|
||||||
|
|
||||||
@if (Context.Request.Path == "/admin/emailnotification")
|
@if (Context.Request.Path == "/admin/emailnotification")
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,13 +2,8 @@
|
||||||
<div>
|
<div>
|
||||||
<h1>Requests</h1>
|
<h1>Requests</h1>
|
||||||
<h4>Below you can see yours and all other requests, as well as their download and approval status.</h4>
|
<h4>Below you can see yours and all other requests, as well as their download and approval status.</h4>
|
||||||
@if (Context.CurrentUser.IsAuthenticated())
|
<br />
|
||||||
{
|
|
||||||
<button id="approveAll" class="btn btn-success-outline" type="submit"><i class="fa fa-plus"></i> Approve All</button>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
}
|
|
||||||
<!-- Nav tabs -->
|
<!-- Nav tabs -->
|
||||||
<ul id="nav-tabs" class="nav nav-tabs" role="tablist">
|
<ul id="nav-tabs" class="nav nav-tabs" role="tablist">
|
||||||
@if (Model.SearchForMovies)
|
@if (Model.SearchForMovies)
|
||||||
|
@ -23,10 +18,9 @@
|
||||||
</ul>
|
</ul>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
|
|
||||||
<!-- Tab panes -->
|
<!-- Tab panes -->
|
||||||
<div class="tab-content contentList">
|
<div class="tab-content contentList">
|
||||||
<div class="btn-group col-sm-2">
|
<div class="btn-group col-sm-push-8">
|
||||||
@if (Context.CurrentUser.IsAuthenticated())
|
@if (Context.CurrentUser.IsAuthenticated())
|
||||||
{
|
{
|
||||||
@if (Model.SearchForMovies)
|
@if (Model.SearchForMovies)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue