diff --git a/README.md b/README.md index 76c391545..e4ade0bba 100644 --- a/README.md +++ b/README.md @@ -1 +1,41 @@ -# RequestPlex \ No newline at end of file +# Request Plex! + +[![Gitter](https://badges.gitter.im/tidusjar/RequestPlex.svg)](https://gitter.im/tidusjar/RequestPlex?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +[![Build status](https://ci.appveyor.com/api/projects/status/hgj8j6lcea7j0yhn?svg=true)](https://ci.appveyor.com/project/tidusjar/requestplex) + +This is based off [Plex Requests by lokenx](https://github.com/lokenx/plexrequests-meteor) so big props to that guy! +I wanted to write a similar application in .Net! + +#Features + +* Integration with [TheMovieDB](https://www.themoviedb.org/) for all Movies and TV shows +* Secure authentication +* [Sonarr](https://sonarr.tv/) integration (SickRage/Sickbeard TBD) +* [CouchPotato](https://couchpota.to/) integration +* Email notifications + +#Preview + +TBC + +#Installation + +Just run the .exe! (Use mono if not on Windows `mono RequestPlex.UI.exe`) + +#Configuration + +TBC + +# Contributors + +We are looking for any contributions to the project! Just pick up a task, if you have any questions ask and i'll get straight on it! + +Please feed free to submit a pull request! + + +# Sponsors +- [JetBrains](http://www.jetbrains.com/) for providing us with free licenses to their great tools!!! + - [ReSharper](http://www.jetbrains.com/resharper/) + - [dotTrace] (https://www.jetbrains.com/profiler/) + - [dotMemory] (https://www.jetbrains.com/dotmemory/) + - [dotCover] (https://www.jetbrains.com/dotcover/) diff --git a/RequestPlex.Api/Models/MovieSearch.cs b/RequestPlex.Api/Models/MovieSearch.cs deleted file mode 100644 index 1b49a9629..000000000 --- a/RequestPlex.Api/Models/MovieSearch.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace RequestPlex.Api.Models -{ - public class MovieResult - { - public bool adult { get; set; } - public string backdrop_path { get; set; } - public List genre_ids { get; set; } - public int id { get; set; } - public string original_language { get; set; } - public string original_title { get; set; } - public string overview { get; set; } - public string release_date { get; set; } - public string poster_path { get; set; } - public double popularity { get; set; } - public string title { get; set; } - public bool video { get; set; } - public double vote_average { get; set; } - public int vote_count { get; set; } - } - - public class MovieSearch - { - public int page { get; set; } - public List results { get; set; } - public int total_pages { get; set; } - public int total_results { get; set; } - } -} diff --git a/RequestPlex.Api/Models/PlexAuthentication.cs b/RequestPlex.Api/Models/PlexAuthentication.cs new file mode 100644 index 000000000..cd0dcf76f --- /dev/null +++ b/RequestPlex.Api/Models/PlexAuthentication.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace RequestPlex.Api.Models +{ + public class PlexAuthentication + { + public User user { get; set; } + } + public class Subscription + { + public bool active { get; set; } + public string status { get; set; } + public object plan { get; set; } + public object features { get; set; } + } + + public class Roles + { + public List roles { get; set; } + } + + public class User + { + public string email { get; set; } + public string joined_at { get; set; } + public string username { get; set; } + public string title { get; set; } + public string authentication_token { get; set; } + public Subscription subscription { get; set; } + public Roles roles { get; set; } + public List entitlements { get; set; } + public object confirmed_at { get; set; } + public int forum_id { get; set; } + } +} diff --git a/RequestPlex.Api/Models/PlexUserRequest.cs b/RequestPlex.Api/Models/PlexUserRequest.cs new file mode 100644 index 000000000..34177a4ba --- /dev/null +++ b/RequestPlex.Api/Models/PlexUserRequest.cs @@ -0,0 +1,14 @@ + +namespace RequestPlex.Api.Models +{ + public class PlexUserRequest + { + public UserRequest user { get; set; } + } + + public class UserRequest + { + public string login { get; set; } + public string password { get; set; } + } +} diff --git a/RequestPlex.Api/PlexApi.cs b/RequestPlex.Api/PlexApi.cs new file mode 100644 index 000000000..79690949e --- /dev/null +++ b/RequestPlex.Api/PlexApi.cs @@ -0,0 +1,51 @@ +using System; +using RequestPlex.Api.Models; +using RestSharp; + +namespace RequestPlex.Api +{ + public class PlexApi + { + public PlexAuthentication GetToken(string username, string password) + { + var userModel = new PlexUserRequest + { + user = new UserRequest + { + password = password, + login = username + }, + }; + var request = new RestRequest + { + Method = Method.POST, + }; + + request.AddHeader("X-Plex-Client-Identifier", "Test213"); + 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) + { + var request = new RestRequest + { + Method = Method.POST, + }; + + 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"); + + } + } +} + diff --git a/RequestPlex.Api/RequestPlex.Api.csproj b/RequestPlex.Api/RequestPlex.Api.csproj index 1feb7d421..c04ba5870 100644 --- a/RequestPlex.Api/RequestPlex.Api.csproj +++ b/RequestPlex.Api/RequestPlex.Api.csproj @@ -30,6 +30,14 @@ 4 + + ..\packages\Dapper.1.42\lib\net45\Dapper.dll + True + + + ..\packages\Nancy.1.4.3\lib\net40\Nancy.dll + True + ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll True @@ -53,7 +61,9 @@ - + + + diff --git a/RequestPlex.Api/packages.config b/RequestPlex.Api/packages.config index bab29a22b..50b670221 100644 --- a/RequestPlex.Api/packages.config +++ b/RequestPlex.Api/packages.config @@ -1,5 +1,7 @@  + + diff --git a/RequestPlex.Core/SettingsService.cs b/RequestPlex.Core/SettingsService.cs index 81b33cc5d..c9d86a44e 100644 --- a/RequestPlex.Core/SettingsService.cs +++ b/RequestPlex.Core/SettingsService.cs @@ -12,7 +12,7 @@ namespace RequestPlex.Core { public class SettingsService { - public void SaveSettings(int port) + public void SaveSettings(SettingsModel model) { var db = new DbConfiguration(new SqliteFactory()); var repo = new GenericRepository(db); @@ -20,13 +20,12 @@ namespace RequestPlex.Core var existingSettings = repo.GetAll().FirstOrDefault(); if (existingSettings != null) { - existingSettings.Port = port; + existingSettings = model; repo.Update(existingSettings); return; } - var newSettings = new SettingsModel { Port = port }; - repo.Insert(newSettings); + repo.Insert(model); } public SettingsModel GetSettings() diff --git a/RequestPlex.Core/Setup.cs b/RequestPlex.Core/Setup.cs index f02cc6355..5321b67fd 100644 --- a/RequestPlex.Core/Setup.cs +++ b/RequestPlex.Core/Setup.cs @@ -10,7 +10,7 @@ namespace RequestPlex.Core public void SetupDb() { var db = new DbConfiguration(new SqliteFactory()); - db.CreateDatabase(); + db.CheckDb(); TableCreation.CreateTables(db.DbConnection()); } } diff --git a/RequestPlex.Core/UserMapper.cs b/RequestPlex.Core/UserMapper.cs index 2a6f5c820..89f724143 100644 --- a/RequestPlex.Core/UserMapper.cs +++ b/RequestPlex.Core/UserMapper.cs @@ -37,7 +37,7 @@ namespace RequestPlex.Core var db = new DbConfiguration(new SqliteFactory()); var repo = new UserRepository(db); var users = repo.GetAll(); - var userRecord = users.FirstOrDefault(u => u.UserName == username && u.Password == password); // TODO hashing + var userRecord = users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.InvariantCultureIgnoreCase) && u.Password.Equals(password)); // TODO hashing if (userRecord == null) { diff --git a/RequestPlex.Store/SettingsModel.cs b/RequestPlex.Store/SettingsModel.cs index 7b6ed8bad..573f3dfec 100644 --- a/RequestPlex.Store/SettingsModel.cs +++ b/RequestPlex.Store/SettingsModel.cs @@ -8,5 +8,7 @@ namespace RequestPlex.Store public class SettingsModel : Entity { public int Port { get; set; } + public bool UserAuthentication { get; set; } + public string PlexAuthToken { get; set; } } } diff --git a/RequestPlex.Store/SqlTables.sql b/RequestPlex.Store/SqlTables.sql index 8b035ae0f..90cdc81ac 100644 --- a/RequestPlex.Store/SqlTables.sql +++ b/RequestPlex.Store/SqlTables.sql @@ -11,7 +11,9 @@ CREATE TABLE IF NOT EXISTS User CREATE TABLE IF NOT EXISTS Settings ( Id INTEGER PRIMARY KEY AUTOINCREMENT, - Port INTEGER NOT NULL + Port INTEGER NOT NULL, + UserAuthentication INTEGER NOT NULL, + PlexAuthToken varchar(50) ); CREATE TABLE IF NOT EXISTS Requested diff --git a/RequestPlex.UI/Content/search.js b/RequestPlex.UI/Content/search.js index e4ebdf7c9..0f0898521 100644 --- a/RequestPlex.UI/Content/search.js +++ b/RequestPlex.UI/Content/search.js @@ -17,7 +17,6 @@ $("#tvSearchContent").on("keyup", function (e) { tvimer = setTimeout(tvSearch(), 400); }); - $("#test").click(function (e) { e.preventDefault(); @@ -104,14 +103,3 @@ function buildTvShowContext(result) { }; return context; } - -function generateNotify(message, type) { - // type = danger, warning, info, successs - $.notify({ - // options - message: message - }, { - // settings - type: type - }); -} \ No newline at end of file diff --git a/RequestPlex.UI/Content/site.js b/RequestPlex.UI/Content/site.js new file mode 100644 index 000000000..8f69d979c --- /dev/null +++ b/RequestPlex.UI/Content/site.js @@ -0,0 +1,13 @@ + + + +function generateNotify(message, type) { + // type = danger, warning, info, successs + $.notify({ + // options + message: message + }, { + // settings + type: type + }); +} \ No newline at end of file diff --git a/RequestPlex.UI/Models/PlexAuth.cs b/RequestPlex.UI/Models/PlexAuth.cs new file mode 100644 index 000000000..3c90b131e --- /dev/null +++ b/RequestPlex.UI/Models/PlexAuth.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RequestPlex.UI.Models +{ + public class PlexAuth + { + public string username { get; set; } + public string password { get; set; } + } +} diff --git a/RequestPlex.UI/Modules/AdminModule.cs b/RequestPlex.UI/Modules/AdminModule.cs index 837a41e51..1aa0a6392 100644 --- a/RequestPlex.UI/Modules/AdminModule.cs +++ b/RequestPlex.UI/Modules/AdminModule.cs @@ -1,10 +1,18 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; using System.Dynamic; - +using System.Linq; using Nancy; using Nancy.Extensions; +using Nancy.ModelBinding; using Nancy.Security; - +using Newtonsoft.Json; +using RequestPlex.Api; using RequestPlex.Core; +using RequestPlex.Store; +using RequestPlex.UI.Models; namespace RequestPlex.UI.Modules { @@ -12,17 +20,20 @@ namespace RequestPlex.UI.Modules { public AdminModule() { +#if !DEBUG this.RequiresAuthentication(); +#endif Get["admin/"] = _ => { dynamic model = new ExpandoObject(); model.Errored = Request.Query.error.HasValue; - + model.Port = null; var s = new SettingsService(); var settings = s.GetSettings(); if (settings != null) { model.Port = settings.Port; + model.PlexAuthToken = settings.PlexAuthToken; } return View["/Admin/Settings", model]; @@ -30,21 +41,55 @@ namespace RequestPlex.UI.Modules Post["admin/"] = _ => { - var portString = (string)Request.Form.portNumber; - int port; + var model = this.Bind(); + + var s = new SettingsService(); + s.SaveSettings(model); + + + return Context.GetRedirect("~/admin"); + }; + + Post["admin/requestauth"] = _ => + { + var user = this.Bind(); - if (!int.TryParse(portString, out port)) + if (string.IsNullOrEmpty(user.username) || string.IsNullOrEmpty(user.password)) { return Context.GetRedirect("~/admin?error=true"); } + var plex = new PlexApi(); + var model = plex.GetToken(user.username, user.password); var s = new SettingsService(); - s.SaveSettings(port); + var oldSettings = s.GetSettings(); + if (oldSettings != null) + { + oldSettings.PlexAuthToken = model.user.authentication_token; + s.SaveSettings(oldSettings); + } + else + { + var newModel = new SettingsModel + { + PlexAuthToken = model.user.authentication_token + }; + s.SaveSettings(newModel); + } + return Context.GetRedirect("~/admin"); }; + Get["admin/getusers"] = _ => + { + var api = new PlexApi(); + + + return View["/Admin/Settings"]; + }; + } } } \ No newline at end of file diff --git a/RequestPlex.UI/Program.cs b/RequestPlex.UI/Program.cs index 028f03f4d..e6ec8423c 100644 --- a/RequestPlex.UI/Program.cs +++ b/RequestPlex.UI/Program.cs @@ -12,10 +12,13 @@ namespace RequestPlex.UI { static void Main(string[] args) { + var uri = "http://localhost:3579/"; + var s = new Setup(); + s.SetupDb(); + var service = new SettingsService(); var settings = service.GetSettings(); - var uri = "http://localhost:3579/"; if (settings != null) { uri = $"http://localhost:{settings.Port}"; diff --git a/RequestPlex.UI/RequestPlex.UI.csproj b/RequestPlex.UI/RequestPlex.UI.csproj index 9a1e1b910..fe38dc27e 100644 --- a/RequestPlex.UI/RequestPlex.UI.csproj +++ b/RequestPlex.UI/RequestPlex.UI.csproj @@ -108,6 +108,7 @@ PreserveNewest + @@ -136,6 +137,9 @@ PreserveNewest + + PreserveNewest + @@ -193,7 +197,6 @@ - diff --git a/RequestPlex.UI/Startup.cs b/RequestPlex.UI/Startup.cs index 40a5d265c..2402a70b7 100644 --- a/RequestPlex.UI/Startup.cs +++ b/RequestPlex.UI/Startup.cs @@ -1,7 +1,5 @@ using Owin; -using RequestPlex.Core; - namespace RequestPlex.UI { public class Startup @@ -9,9 +7,6 @@ namespace RequestPlex.UI public void Configuration(IAppBuilder app) { app.UseNancy(); - - var s = new Setup(); - s.SetupDb(); } } } diff --git a/RequestPlex.UI/Views/Admin/Settings.cshtml b/RequestPlex.UI/Views/Admin/Settings.cshtml index db61016f0..df14a4ed9 100644 --- a/RequestPlex.UI/Views/Admin/Settings.cshtml +++ b/RequestPlex.UI/Views/Admin/Settings.cshtml @@ -1,17 +1,81 @@ @Html.Partial("/Admin/_Sidebar") @{ - var port = Model.Port ?? 0; + int port; + var authToken = string.Empty; + if (Model.Port == null) + { + port = 3579; + } + else + { + port = Model.Port; + } + + if (Model.PlexAuthToken == null) + { + authToken = string.Empty; + } + else + { + authToken = Model.PlexAuthToken; + } }
-
+
Request Plex Settings
- + +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+
+
+
+ +
+
+ +
+ +
+ +
+
+ Current users that are allowed to authenticate: + + +
+
+ +
+
+ +
+
+
+
+
+ Please note, you will have to restart after changing these settings. +
@@ -29,4 +93,60 @@ Please enter in a correct port number
-} \ No newline at end of file +} + + \ No newline at end of file diff --git a/RequestPlex.UI/Views/Admin/_Sidebar.cshtml b/RequestPlex.UI/Views/Admin/_Sidebar.cshtml index 6aeec9384..9e21b91e3 100644 --- a/RequestPlex.UI/Views/Admin/_Sidebar.cshtml +++ b/RequestPlex.UI/Views/Admin/_Sidebar.cshtml @@ -4,6 +4,5 @@ CouchPotato Settings Sonarr Settings Sickbeard Settings - Authentication
\ No newline at end of file diff --git a/RequestPlex.UI/Views/Shared/_Layout.cshtml b/RequestPlex.UI/Views/Shared/_Layout.cshtml index 86fa867c2..fabadd7d5 100644 --- a/RequestPlex.UI/Views/Shared/_Layout.cshtml +++ b/RequestPlex.UI/Views/Shared/_Layout.cshtml @@ -11,6 +11,7 @@ +