diff --git a/README.md b/README.md index e4ade0bba..107b60d8b 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ I wanted to write a similar application in .Net! #Preview -TBC +![Preview](http://i.imgur.com/Q8iRI43.gif) #Installation diff --git a/RequestPlex.Api/ApiRequest.cs b/RequestPlex.Api/ApiRequest.cs index 74837f64c..974e4e9c5 100644 --- a/RequestPlex.Api/ApiRequest.cs +++ b/RequestPlex.Api/ApiRequest.cs @@ -27,9 +27,7 @@ namespace RequestPlex.Api var response = client.Execute(request); if (response.ErrorException != null) - { - - + { var message = "Error retrieving response. Check inner details for more info."; throw new ApplicationException(message, response.ErrorException); } diff --git a/RequestPlex.Api/Models/PlexFriends.cs b/RequestPlex.Api/Models/PlexFriends.cs new file mode 100644 index 000000000..77bafa637 --- /dev/null +++ b/RequestPlex.Api/Models/PlexFriends.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Serialization; +using RestSharp.Deserializers; + +namespace RequestPlex.Api.Models +{ + [XmlRoot(ElementName = "Server")] + public class Server + { + [XmlAttribute(AttributeName = "id")] + public string Id { get; set; } + [XmlAttribute(AttributeName = "serverId")] + public string ServerId { get; set; } + [XmlAttribute(AttributeName = "machineIdentifier")] + public string MachineIdentifier { get; set; } + [XmlAttribute(AttributeName = "name")] + public string Name { get; set; } + [XmlAttribute(AttributeName = "lastSeenAt")] + public string LastSeenAt { get; set; } + [XmlAttribute(AttributeName = "numLibraries")] + public string NumLibraries { get; set; } + [XmlAttribute(AttributeName = "owned")] + public string Owned { get; set; } + } + + [XmlRoot(ElementName = "User")] + public class UserFriends + { + [XmlElement(ElementName = "Server")] + public Server Server { get; set; } + [XmlAttribute(AttributeName = "id")] + public string Id { get; set; } + [XmlAttribute(AttributeName = "title")] + public string Title { get; set; } + [XmlAttribute(AttributeName = "username")] + public string Username { get; set; } + [XmlAttribute(AttributeName = "email")] + public string Email { get; set; } + [XmlAttribute(AttributeName = "recommendationsPlaylistId")] + public string RecommendationsPlaylistId { get; set; } + [XmlAttribute(AttributeName = "thumb")] + public string Thumb { get; set; } + } + + [XmlRoot(ElementName = "MediaContainer")] + public class PlexFriends + { + [XmlElement(ElementName = "User")] + public List User { get; set; } + [XmlAttribute(AttributeName = "friendlyName")] + public string FriendlyName { get; set; } + [XmlAttribute(AttributeName = "identifier")] + public string Identifier { get; set; } + [XmlAttribute(AttributeName = "machineIdentifier")] + public string MachineIdentifier { get; set; } + [XmlAttribute(AttributeName = "totalSize")] + public string TotalSize { get; set; } + [XmlAttribute(AttributeName = "size")] + public string Size { get; set; } + } + +} diff --git a/RequestPlex.Api/PlexApi.cs b/RequestPlex.Api/PlexApi.cs index 77b4bcdc4..20f4b5421 100644 --- a/RequestPlex.Api/PlexApi.cs +++ b/RequestPlex.Api/PlexApi.cs @@ -1,6 +1,10 @@ using System; +using System.IO; +using System.Runtime.Remoting.Messaging; using RequestPlex.Api.Models; using RestSharp; +using RestSharp.Deserializers; +using RestSharp.Serializers; namespace RequestPlex.Api { @@ -25,26 +29,30 @@ namespace RequestPlex.Api request.AddHeader("X-Plex-Product", "Request Plex"); request.AddHeader("X-Plex-Version", "0.0.1"); request.AddHeader("Content-Type", "application/json"); - + request.AddJsonBody(userModel); var api = new ApiRequest(); return api.Execute(request, new Uri("https://plex.tv/users/sign_in.json")); } - public void GetUsers(string authToken) + public PlexFriends GetUsers(string authToken) { var request = new RestRequest { - Method = Method.POST, + Method = Method.GET, }; request.AddHeader("X-Plex-Client-Identifier", "Test213"); request.AddHeader("X-Plex-Product", "Request Plex"); request.AddHeader("X-Plex-Version", "0.0.1"); request.AddHeader("X-Plex-Token", authToken); - request.AddHeader("Content-Type", "application/json"); + request.AddHeader("Content-Type", "application/xml"); + + var api = new ApiRequest(); + var users = api.Execute(request, new Uri("https://plex.tv/pms/friends/all")); + return users; } } } diff --git a/RequestPlex.Api/RequestPlex.Api.csproj b/RequestPlex.Api/RequestPlex.Api.csproj index 714860b3e..bc26a3015 100644 --- a/RequestPlex.Api/RequestPlex.Api.csproj +++ b/RequestPlex.Api/RequestPlex.Api.csproj @@ -38,8 +38,8 @@ ..\packages\Nancy.1.4.3\lib\net40\Nancy.dll True - - ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll + + ..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll True @@ -62,6 +62,7 @@ + @@ -69,6 +70,7 @@ + diff --git a/RequestPlex.Api/app.config b/RequestPlex.Api/app.config new file mode 100644 index 000000000..ac2586ac9 --- /dev/null +++ b/RequestPlex.Api/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/RequestPlex.Api/packages.config b/RequestPlex.Api/packages.config index 50b670221..62b36e32c 100644 --- a/RequestPlex.Api/packages.config +++ b/RequestPlex.Api/packages.config @@ -2,7 +2,7 @@ - + \ No newline at end of file diff --git a/RequestPlex.Core/RequestPlex.Core.csproj b/RequestPlex.Core/RequestPlex.Core.csproj index 62122a746..b105c28e8 100644 --- a/RequestPlex.Core/RequestPlex.Core.csproj +++ b/RequestPlex.Core/RequestPlex.Core.csproj @@ -64,6 +64,9 @@ + + + @@ -74,6 +77,7 @@ + diff --git a/RequestPlex.Core/SettingModels/CouchPotatoSettings.cs b/RequestPlex.Core/SettingModels/CouchPotatoSettings.cs new file mode 100644 index 000000000..ffa6c5896 --- /dev/null +++ b/RequestPlex.Core/SettingModels/CouchPotatoSettings.cs @@ -0,0 +1,10 @@ +namespace RequestPlex.Core.SettingModels +{ + public class CouchPotatoSettings : Settings + { + public string Ip { get; set; } + public int Port { get; set; } + public string ApiKey { get; set; } + public bool Enabled { get; set; } + } +} \ No newline at end of file diff --git a/RequestPlex.Core/SettingModels/RequestPlexSettings.cs b/RequestPlex.Core/SettingModels/RequestPlexSettings.cs index c28c933f0..0e603b91a 100644 --- a/RequestPlex.Core/SettingModels/RequestPlexSettings.cs +++ b/RequestPlex.Core/SettingModels/RequestPlexSettings.cs @@ -31,5 +31,9 @@ namespace RequestPlex.Core.SettingModels public int Port { get; set; } public bool UserAuthentication { get; set; } public string PlexAuthToken { get; set; } + public bool SearchForMovies { get; set; } + public bool SearchForTvShows { get; set; } + public bool RequireApprovial { get; set; } + public int WeeklyRequestLimit { get; set; } } } diff --git a/RequestPlex.Core/SettingModels/SickRageSettings.cs b/RequestPlex.Core/SettingModels/SickRageSettings.cs new file mode 100644 index 000000000..be1dd6d85 --- /dev/null +++ b/RequestPlex.Core/SettingModels/SickRageSettings.cs @@ -0,0 +1,10 @@ +namespace RequestPlex.Core.SettingModels +{ + public class SickRageSettings : Settings + { + public string Ip { get; set; } + public int Port { get; set; } + public string ApiKey { get; set; } + public bool Enabled { get; set; } + } +} \ No newline at end of file diff --git a/RequestPlex.Core/SettingModels/SonarrSettings.cs b/RequestPlex.Core/SettingModels/SonarrSettings.cs new file mode 100644 index 000000000..fb6f8ecfa --- /dev/null +++ b/RequestPlex.Core/SettingModels/SonarrSettings.cs @@ -0,0 +1,10 @@ +namespace RequestPlex.Core.SettingModels +{ + public class SonarrSettings : Settings + { + public string Ip { get; set; } + public int Port { get; set; } + public string ApiKey { get; set; } + public bool Enabled { get; set; } + } +} \ No newline at end of file diff --git a/RequestPlex.Core/SettingsService.cs b/RequestPlex.Core/SettingsService.cs index c0d5c1fe7..9e37f3d3c 100644 --- a/RequestPlex.Core/SettingsService.cs +++ b/RequestPlex.Core/SettingsService.cs @@ -64,7 +64,9 @@ namespace RequestPlex.Core PosterPath = "http://image.tmdb.org/t/p/w150/" + movieInfo.PosterPath, Title = movieInfo.Title, ReleaseDate = movieInfo.ReleaseDate ?? DateTime.MinValue, - Status = movieInfo.Status + Status = movieInfo.Status, + RequestedDate = DateTime.Now, + Approved = false }; } else @@ -79,7 +81,9 @@ namespace RequestPlex.Core PosterPath = "http://image.tmdb.org/t/p/w150/" + showInfo.PosterPath, Title = showInfo.Name, ReleaseDate = showInfo.FirstAirDate ?? DateTime.MinValue, - Status = showInfo.Status + Status = showInfo.Status, + RequestedDate = DateTime.Now, + Approved = false }; } var db = new DbConfiguration(new SqliteFactory()); diff --git a/RequestPlex.Core/app.config b/RequestPlex.Core/app.config new file mode 100644 index 000000000..ac2586ac9 --- /dev/null +++ b/RequestPlex.Core/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/RequestPlex.Helpers/AssemblyHelper.cs b/RequestPlex.Helpers/AssemblyHelper.cs new file mode 100644 index 000000000..fe6a281b8 --- /dev/null +++ b/RequestPlex.Helpers/AssemblyHelper.cs @@ -0,0 +1,15 @@ +using System.Diagnostics; +using System.Reflection; + +namespace RequestPlex.Helpers +{ + public class AssemblyHelper + { + public static string GetAssemblyVersion() + { + var assembly = Assembly.GetExecutingAssembly(); + var fvi = FileVersionInfo.GetVersionInfo(assembly.Location); + return fvi.FileVersion; + } + } +} \ No newline at end of file diff --git a/RequestPlex.Helpers/RequestPlex.Helpers.csproj b/RequestPlex.Helpers/RequestPlex.Helpers.csproj index 4fd02c8e9..271cce512 100644 --- a/RequestPlex.Helpers/RequestPlex.Helpers.csproj +++ b/RequestPlex.Helpers/RequestPlex.Helpers.csproj @@ -45,6 +45,7 @@ + diff --git a/RequestPlex.Store/RequestedModel.cs b/RequestPlex.Store/RequestedModel.cs index d62cd9b7c..dca2f384b 100644 --- a/RequestPlex.Store/RequestedModel.cs +++ b/RequestPlex.Store/RequestedModel.cs @@ -16,6 +16,10 @@ namespace RequestPlex.Store public DateTime ReleaseDate { get; set; } public RequestType Type { get; set; } public string Status { get; set; } + public bool Approved { get; set; } + public string RequestedBy { get; set; } + public DateTime RequestedDate { get; set; } + } public enum RequestType diff --git a/RequestPlex.Store/SqlTables.sql b/RequestPlex.Store/SqlTables.sql index 820a090f7..f6bb567da 100644 --- a/RequestPlex.Store/SqlTables.sql +++ b/RequestPlex.Store/SqlTables.sql @@ -21,12 +21,17 @@ CREATE TABLE IF NOT EXISTS Requested Id INTEGER PRIMARY KEY AUTOINCREMENT, Type INTEGER NOT NULL, Tmdbid INTEGER NOT NULL, - ImdbId varchar(50) NOT NULL, + ImdbId varchar(50), Overview varchar(50) NOT NULL, Title varchar(50) NOT NULL, PosterPath varchar(50) NOT NULL, ReleaseDate varchar(50) NOT NULL, - Status varchar(50) NOT NULL + Status varchar(50) NOT NULL, + Approved INTEGER NOT NULL, + RequestedBy varchar(50), + RequestedDate varchar(50) NOT NULL, + Available varchar(50) + ); CREATE TABLE IF NOT EXISTS GlobalSettings diff --git a/RequestPlex.UI/Bootstrapper.cs b/RequestPlex.UI/Bootstrapper.cs index 40905d989..0dba78b4e 100644 --- a/RequestPlex.UI/Bootstrapper.cs +++ b/RequestPlex.UI/Bootstrapper.cs @@ -35,6 +35,7 @@ namespace RequestPlex.UI container.Register, SettingsServiceV2>(); + container.Register, SettingsServiceV2>(); container.Register, GenericRepository>(); } diff --git a/RequestPlex.UI/Content/requests.js b/RequestPlex.UI/Content/requests.js index bf68b194b..4e535fd27 100644 --- a/RequestPlex.UI/Content/requests.js +++ b/RequestPlex.UI/Content/requests.js @@ -61,7 +61,7 @@ function buildTvShowRequestContext(result) { var context = { posterPath: result.posterPath, id: result.tmdbid, - title: result.name, + title: result.title, overview: result.overview, year: year, type: "tv", diff --git a/RequestPlex.UI/Modules/AdminModule.cs b/RequestPlex.UI/Modules/AdminModule.cs index 32ffe8ca9..deba84a1d 100644 --- a/RequestPlex.UI/Modules/AdminModule.cs +++ b/RequestPlex.UI/Modules/AdminModule.cs @@ -25,10 +25,12 @@ // ************************************************************************/ #endregion using System.Dynamic; - +using System.Linq; +using System.Web.UI; using Nancy; using Nancy.Extensions; using Nancy.ModelBinding; +using Nancy.Responses.Negotiation; using Nancy.Security; using RequestPlex.Api; @@ -40,76 +42,102 @@ namespace RequestPlex.UI.Modules { public class AdminModule : NancyModule { - public AdminModule(ISettingsService service) + private ISettingsService RpService { get; set; } + private ISettingsService CpService { get; set; } + public AdminModule(ISettingsService rpService, ISettingsService cpService ) : base("admin") { + RpService = rpService; + CpService = cpService; #if !DEBUG this.RequiresAuthentication(); #endif - Get["admin/"] = _ => - { - dynamic model = new ExpandoObject(); - model.Errored = Request.Query.error.HasValue; - model.Port = null; + Get["/"] = _ => Admin(); - var settings = service.GetSettings(); - if (settings != null) - { - model.Port = settings.Port; - model.PlexAuthToken = settings.PlexAuthToken; - } + Post["/"] = _ => SaveAdmin(); - return View["/Admin/Settings", model]; - }; + Post["/requestauth"] = _ => RequestAuthToken(); + + Get["/getusers"] = _ => GetUsers(); + + Get["/couchpotato"] = _ => CouchPotato(); + Post["/couchpotato"] = _ => SaveCouchPotato(); + } - Post["admin/"] = _ => - { - var model = this.Bind(); - service.SaveSettings(model); + private Negotiator Admin() + { + dynamic model = new ExpandoObject(); + var settings = RpService.GetSettings(); + + model = settings; + return View["/Admin/Settings", model]; + } + private Response SaveAdmin() + { + var model = this.Bind(); - return Context.GetRedirect("~/admin"); - }; + RpService.SaveSettings(model); + + + return Context.GetRedirect("~/admin"); + } - Post["admin/requestauth"] = _ => + private Response RequestAuthToken() + { + var user = this.Bind(); + + if (string.IsNullOrEmpty(user.username) || string.IsNullOrEmpty(user.password)) { - var user = this.Bind(); + return Context.GetRedirect("~/admin?error=true"); + } - if (string.IsNullOrEmpty(user.username) || string.IsNullOrEmpty(user.password)) + var plex = new PlexApi(); + var model = plex.GetToken(user.username, user.password); + var oldSettings = RpService.GetSettings(); + if (oldSettings != null) + { + oldSettings.PlexAuthToken = model.user.authentication_token; + RpService.SaveSettings(oldSettings); + } + else + { + var newModel = new RequestPlexSettings { - return Context.GetRedirect("~/admin?error=true"); - } + PlexAuthToken = model.user.authentication_token + }; + RpService.SaveSettings(newModel); + } - var plex = new PlexApi(); - var model = plex.GetToken(user.username, user.password); - var oldSettings = service.GetSettings(); - if (oldSettings != null) - { - oldSettings.PlexAuthToken = model.user.authentication_token; - service.SaveSettings(oldSettings); - } - else - { - var newModel = new RequestPlexSettings - { - PlexAuthToken = model.user.authentication_token - }; - service.SaveSettings(newModel); - } + return Response.AsJson(new {Result = true, AuthToken = model.user.authentication_token}); + } + private Response GetUsers() + { + var token = RpService.GetSettings().PlexAuthToken; + var api = new PlexApi(); + var users = api.GetUsers(token); + var usernames = users.User.Select(x => x.Username); + return Response.AsJson(usernames); //TODO usernames are not populated. + } - return Context.GetRedirect("~/admin"); - }; + private Negotiator CouchPotato() + { + dynamic model = new ExpandoObject(); + var settings = CpService.GetSettings(); + model = settings; - Get["admin/getusers"] = _ => - { - var api = new PlexApi(); - + return View["/Admin/CouchPotato", model]; + } + private Response SaveCouchPotato() + { + var couchPotatoSettings = this.Bind(); - return View["/Admin/Settings"]; - }; + CpService.SaveSettings(couchPotatoSettings); + return Context.GetRedirect("~/admin/couchpotato"); } + } } \ No newline at end of file diff --git a/RequestPlex.UI/Program.cs b/RequestPlex.UI/Program.cs index 1d863eee8..5f24c7767 100644 --- a/RequestPlex.UI/Program.cs +++ b/RequestPlex.UI/Program.cs @@ -1,11 +1,9 @@ using System; - +using System.Diagnostics; using Microsoft.Owin.Hosting; using Mono.Data.Sqlite; -using Nancy.Hosting.Self; - using RequestPlex.Core; using RequestPlex.Core.SettingModels; using RequestPlex.Helpers; @@ -18,6 +16,8 @@ namespace RequestPlex.UI { static void Main(string[] args) { + var assemblyVer = AssemblyHelper.GetAssemblyVersion(); + Console.WriteLine($"Version: {assemblyVer}"); var uri = "http://localhost:3579/"; var s = new Setup(); s.SetupDb(); @@ -32,8 +32,8 @@ namespace RequestPlex.UI using (WebApp.Start(uri)) { - Console.WriteLine("Running on {0}", uri); - Console.WriteLine("Press enter to exit"); + Console.WriteLine($"Request Plex is running on {uri}"); + Console.WriteLine("Press any key to exit"); Console.ReadLine(); } } diff --git a/RequestPlex.UI/RequestPlex.UI.csproj b/RequestPlex.UI/RequestPlex.UI.csproj index 48a5d21a8..0bc020d6a 100644 --- a/RequestPlex.UI/RequestPlex.UI.csproj +++ b/RequestPlex.UI/RequestPlex.UI.csproj @@ -38,7 +38,7 @@ DEBUG;TRACE prompt 4 - false + true AnyCPU @@ -70,8 +70,9 @@ False ..\Assemblies\Mono.Data.Sqlite.dll - - ..\packages\Nancy.1.4.1\lib\net40\Nancy.dll + + ..\packages\Nancy.1.4.3\lib\net40\Nancy.dll + True ..\packages\Nancy.Authentication.Basic.1.4.1\lib\net40\Nancy.Authentication.Basic.dll @@ -213,6 +214,9 @@ Always + + Always + web.config @@ -220,9 +224,7 @@ web.config - - - + {8cb8d235-2674-442d-9c6a-35fcaeeb160d} diff --git a/RequestPlex.UI/Views/Admin/CouchPotato.cshtml b/RequestPlex.UI/Views/Admin/CouchPotato.cshtml new file mode 100644 index 000000000..82fc522bb --- /dev/null +++ b/RequestPlex.UI/Views/Admin/CouchPotato.cshtml @@ -0,0 +1,69 @@ +@Html.Partial("/Admin/_Sidebar") +@{ + int port; + if (Model.Port == 0) + { + port = 5050; + } + else + { + port = Model.Port; + } +} +
+
+
+ CouchPotato Settings + +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ + +
+ +
+
+ + +
+ +
+ +
+
+ + + + +
+
+ +
+
+
+
+
+ + diff --git a/RequestPlex.UI/Views/Admin/Settings.cshtml b/RequestPlex.UI/Views/Admin/Settings.cshtml index eda9c6d55..9ee447639 100644 --- a/RequestPlex.UI/Views/Admin/Settings.cshtml +++ b/RequestPlex.UI/Views/Admin/Settings.cshtml @@ -11,35 +11,69 @@ port = Model.Port; } - if (Model.PlexAuthToken == null) - { - authToken = string.Empty; - } - else - { - authToken = Model.PlexAuthToken; - } }
-
+
Request Plex Settings
+
+ You will have to restart after changing the port. +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
- +
- +
@@ -55,14 +89,30 @@
-
- +
+
- Current users that are allowed to authenticate: - - +
+
+ Current users that are allowed to authenticate: +
+
+
+ +
+
+
@@ -73,11 +123,9 @@

- Please note, you will have to restart after changing these settings.
-
@@ -86,14 +134,6 @@
-@if (Model.Errored) -{ -
- - Please enter in a correct port number -
-} -