diff --git a/Ombi.Core/Tv/TvSenderV2.cs b/Ombi.Core/Tv/TvSenderV2.cs index 11a97f498..adc7ca426 100644 --- a/Ombi.Core/Tv/TvSenderV2.cs +++ b/Ombi.Core/Tv/TvSenderV2.cs @@ -164,11 +164,24 @@ namespace Ombi.Core.Tv for (var i = 1; i <= model.SeasonCount; i++) { - var season = new Season + Season season; + if (model.Episodes.Any(x => x.SeasonNumber == i)) { - seasonNumber = i, - monitored = false // Do not monitor any seasons - }; + season = new Season + { + seasonNumber = i, + monitored = true // Do not monitor any seasons + }; + } + else + { + season = new Season + { + seasonNumber = i, + monitored = false // Do not monitor any seasons + }; + + } seriesToAdd.seasons.Add(season); } diff --git a/Ombi.UI/Modules/Admin/AdminModule.cs b/Ombi.UI/Modules/Admin/AdminModule.cs deleted file mode 100644 index f3c8acb8a..000000000 --- a/Ombi.UI/Modules/Admin/AdminModule.cs +++ /dev/null @@ -1,1347 +0,0 @@ -#region Copyright -// /************************************************************************ -// Copyright (c) 2016 Jamie Rees -// File: AdminModule.cs -// Created By: Jamie Rees -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// ************************************************************************/ - - - -#endregion - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading.Tasks; -using Nancy; -using Nancy.Extensions; -using Nancy.Json; -using Nancy.ModelBinding; -using Nancy.Responses.Negotiation; -using Nancy.Validation; -using NLog; -using Ombi.Api; -using Ombi.Api.Interfaces; -using Ombi.Api.Models.Movie; -using Ombi.Core; -using Ombi.Core.Models; -using Ombi.Core.SettingModels; -using Ombi.Helpers; -using Ombi.Helpers.Analytics; -using Ombi.Helpers.Exceptions; -using Ombi.Helpers.Permissions; -using Ombi.Services.Interfaces; -using Ombi.Services.Jobs; -using Ombi.Services.Notification; -using Ombi.Store.Models; -using Ombi.Store.Repository; -using Ombi.UI.Helpers; -using Ombi.UI.Models; -using Quartz; -using Action = Ombi.Helpers.Analytics.Action; -using ISecurityExtensions = Ombi.Core.ISecurityExtensions; - -namespace Ombi.UI.Modules.Admin -{ - public class AdminModule : BaseModule - { - private ISettingsService PrService { get; } - private ISettingsService CpService { get; } - private ISettingsService AuthService { get; } - private ISettingsService PlexService { get; } - private ISettingsService SonarrService { get; } - private ISettingsService SickRageService { get; } - private ISettingsService EmailService { get; } - private ISettingsService PushbulletService { get; } - private ISettingsService PushoverService { get; } - private ISettingsService HeadphonesService { get; } - private ISettingsService NewsLetterService { get; } - private ISettingsService WatcherSettings { get; } - private ISettingsService LogService { get; } - private IPlexApi PlexApi { get; } - private ISonarrApi SonarrApi { get; } - private IPushbulletApi PushbulletApi { get; } - private IPushoverApi PushoverApi { get; } - private ICouchPotatoApi CpApi { get; } - private IRepository LogsRepo { get; } - private INotificationService NotificationService { get; } - private ICacheProvider Cache { get; } - private ISettingsService SlackSettings { get; } - private ISettingsService LandingSettings { get; } - private ISettingsService ScheduledJobSettings { get; } - private ISlackApi SlackApi { get; } - private IJobRecord JobRecorder { get; } - private IAnalytics Analytics { get; } - private IRecentlyAdded RecentlyAdded { get; } - private IMassEmail MassEmail { get; } - private ISettingsService NotifySettings { get; } - private ISettingsService DiscordSettings { get; } - private IDiscordApi DiscordApi { get; } - private ISettingsService RadarrSettings { get; } - private IRadarrApi RadarrApi { get; } - private ISettingsService EmbySettings { get; } - private IEmbyApi EmbyApi { get; } - - private static Logger Log = LogManager.GetCurrentClassLogger(); - public AdminModule(ISettingsService prService, - ISettingsService cpService, - ISettingsService auth, - ISettingsService plex, - ISettingsService sonarr, - ISettingsService sickrage, - ISonarrApi sonarrApi, - ISettingsService email, - IPlexApi plexApi, - ISettingsService pbSettings, - PushbulletApi pbApi, - ICouchPotatoApi cpApi, - ISettingsService pushoverSettings, - ISettingsService newsletter, - IPushoverApi pushoverApi, - IRepository logsRepo, - INotificationService notify, - ISettingsService headphones, - ISettingsService logs, - ICacheProvider cache, ISettingsService slackSettings, - ISlackApi slackApi, ISettingsService lp, - ISettingsService scheduler, IJobRecord rec, IAnalytics analytics, - ISettingsService notifyService, IRecentlyAdded recentlyAdded, IMassEmail massEmail, - ISettingsService watcherSettings, - ISettingsService discord, - IDiscordApi discordapi, ISettingsService settings, IRadarrApi radarrApi, - ISettingsService embySettings, IEmbyApi emby - , ISecurityExtensions security) : base("admin", prService, security) - { - PrService = prService; - CpService = cpService; - AuthService = auth; - PlexService = plex; - SonarrService = sonarr; - SonarrApi = sonarrApi; - EmailService = email; - PlexApi = plexApi; - PushbulletService = pbSettings; - PushbulletApi = pbApi; - CpApi = cpApi; - SickRageService = sickrage; - LogsRepo = logsRepo; - PushoverService = pushoverSettings; - PushoverApi = pushoverApi; - NotificationService = notify; - HeadphonesService = headphones; - NewsLetterService = newsletter; - LogService = logs; - Cache = cache; - SlackSettings = slackSettings; - SlackApi = slackApi; - LandingSettings = lp; - ScheduledJobSettings = scheduler; - JobRecorder = rec; - Analytics = analytics; - NotifySettings = notifyService; - RecentlyAdded = recentlyAdded; - MassEmail = massEmail; - WatcherSettings = watcherSettings; - DiscordSettings = discord; - DiscordApi = discordapi; - RadarrSettings = settings; - RadarrApi = radarrApi; - EmbyApi = emby; - EmbySettings = embySettings; - - Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx); - - Get["/"] = _ => Admin(); - - Get["/authentication", true] = async (x, ct) => await Authentication(); - Post["/authentication", true] = async (x, ct) => await SaveAuthentication(); - - Post["/", true] = async (x, ct) => await SaveAdmin(); - - Post["/requestauth", true] = async (x, ct) => await RequestAuthToken(); - - Get["/getusers"] = _ => GetUsers(); - - Get["/couchpotato"] = _ => CouchPotato(); - Post["/couchpotato", true] = async (x, ct) => await SaveCouchPotato(); - - Get["/plex"] = _ => Plex(); - Post["/plex", true] = async (x, ct) => await SavePlex(); - - Get["/emby", true] = async (x, ct) => await Emby(); - Post["/emby", true] = async (x, ct) => await SaveEmby(); - - - Get["/sonarr"] = _ => Sonarr(); - Post["/sonarr"] = _ => SaveSonarr(); - Post["/sonarrprofiles"] = _ => GetSonarrQualityProfiles(); - - - Get["/sickrage"] = _ => Sickrage(); - Post["/sickrage"] = _ => SaveSickrage(); - - Post["/cpprofiles", true] = async (x, ct) => await GetCpProfiles(); - Post["/cpapikey"] = x => GetCpApiKey(); - - Get["/emailnotification"] = _ => EmailNotifications(); - Post["/emailnotification"] = _ => SaveEmailNotifications(); - Post["/testemailnotification", true] = async (x, ct) => await TestEmailNotifications(); - - Get["/pushbulletnotification"] = _ => PushbulletNotifications(); - Post["/pushbulletnotification"] = _ => SavePushbulletNotifications(); - Post["/testpushbulletnotification", true] = async (x, ct) => await TestPushbulletNotifications(); - - Get["/pushovernotification"] = _ => PushoverNotifications(); - Post["/pushovernotification"] = _ => SavePushoverNotifications(); - Post["/testpushovernotification", true] = async (x, ct) => await TestPushoverNotifications(); - - Get["/logs"] = _ => Logs(); - Get["/loglevel"] = _ => GetLogLevels(); - Post["/loglevel"] = _ => UpdateLogLevels(Request.Form.level); - Get["/loadlogs"] = _ => LoadLogs(); - - Get["/headphones"] = _ => Headphones(); - Post["/headphones"] = _ => SaveHeadphones(); - - Get["/newsletter", true] = async (x, ct) => await Newsletter(); - Post["/newsletter", true] = async (x, ct) => await SaveNewsletter(); - Post["/testnewsletteradminemail"] = x => TestNewsletterAdminEmail(); - - Get["/massemail"] = _ => MassEmailView(); - Post["/testmassadminemail"] = x => TestMassAdminEmail(); - Post["/sendmassemail"] = x => SendMassEmail(); - - Post["/createapikey"] = x => CreateApiKey(); - - - - Post["/testslacknotification", true] = async (x, ct) => await TestSlackNotification(); - Get["/slacknotification"] = _ => SlackNotifications(); - Post["/slacknotification"] = _ => SaveSlackNotifications(); - - Post["/testdiscordnotification", true] = async (x, ct) => await TestDiscordNotification(); - Get["/discordnotification", true] = async (x, ct) => await DiscordNotification(); - Post["/discordnotification", true] = async (x, ct) => await SaveDiscordNotifications(); - - Get["/landingpage", true] = async (x, ct) => await LandingPage(); - Post["/landingpage", true] = async (x, ct) => await SaveLandingPage(); - - Get["/scheduledjobs", true] = async (x, ct) => await GetScheduledJobs(); - Post["/scheduledjobs", true] = async (x, ct) => await SaveScheduledJobs(); - - Post["/clearlogs", true] = async (x, ct) => await ClearLogs(); - - Get["/notificationsettings", true] = async (x, ct) => await NotificationSettings(); - Post["/notificationsettings"] = x => SaveNotificationSettings(); - - } - - private async Task Authentication() - { - var settings = await AuthService.GetSettingsAsync(); - - return View["/Authentication", settings]; - } - - private async Task SaveAuthentication() - { - var model = this.Bind(); - - var result = await AuthService.SaveSettingsAsync(model); - if (result) - { - if (!string.IsNullOrEmpty(BaseUrl)) - { - return Context.GetRedirect($"~/{BaseUrl}/admin/authentication"); - } - return Context.GetRedirect("~/admin/authentication"); - } - if (!string.IsNullOrEmpty(BaseUrl)) - { - return Context.GetRedirect($"~/{BaseUrl}/error"); //TODO create error page - } - return Context.GetRedirect("~/error"); //TODO create error page - } - - private Negotiator Admin() - { - var settings = PrService.GetSettings(); - - return View["Settings", settings]; - } - - private async Task SaveAdmin() - { - var model = this.Bind(); - var valid = this.Validate(model); - if (!valid.IsValid) - { - return Response.AsJson(valid.SendJsonError()); - } - model.Wizard = true; - - if (!string.IsNullOrWhiteSpace(model.BaseUrl)) - { - if (model.BaseUrl.StartsWith("/", StringComparison.CurrentCultureIgnoreCase) || model.BaseUrl.StartsWith("\\", StringComparison.CurrentCultureIgnoreCase)) - { - model.BaseUrl = model.BaseUrl.Remove(0, 1); - } - } - if (!model.CollectAnalyticData) - { - Analytics.TrackEventAsync(Category.Admin, Action.Save, "CollectAnalyticData turned off", Username, CookieHelper.GetAnalyticClientId(Cookies)); - } - var result = await PrService.SaveSettingsAsync(model); - - Analytics.TrackEventAsync(Category.Admin, Action.Save, "PlexRequestSettings", Username, CookieHelper.GetAnalyticClientId(Cookies)); - return Response.AsJson(result - ? new JsonResponseModel { Result = true } - : new JsonResponseModel { Result = false, Message = "We could not save to the database, please try again" }); - } - - private async Task RequestAuthToken() - { - var user = this.Bind(); - - if (string.IsNullOrEmpty(user.username) || string.IsNullOrEmpty(user.password)) - { - return Response.AsJson(new { Result = false, Message = "Please provide a valid username and password" }); - } - - var model = PlexApi.SignIn(user.username, user.password); - - if (model?.user == null) - { - return Response.AsJson(new { Result = false, Message = "Incorrect username or password!" }); - } - - var oldSettings = await PlexService.GetSettingsAsync(); - if (oldSettings != null) - { - oldSettings.PlexAuthToken = model.user.authentication_token; - await PlexService.SaveSettingsAsync(oldSettings); - } - else - { - var newModel = new PlexSettings - { - PlexAuthToken = model.user.authentication_token - }; - await PlexService.SaveSettingsAsync(newModel); - } - - var server = PlexApi.GetServer(model.user.authentication_token); - var machine = - server.Server.FirstOrDefault(x => x.AccessToken == model.user.authentication_token)?.MachineIdentifier; - - return Response.AsJson(new { Result = true, AuthToken = model.user.authentication_token, Identifier = machine }); - } - - - private Response GetUsers() - { - var settings = PlexService.GetSettings(); - - var token = settings?.PlexAuthToken; - if (token == null) - { - return Response.AsJson(new { Result = true, Users = string.Empty }); - } - - try - { - var users = PlexApi.GetUsers(token); - if (users == null) - { - return Response.AsJson(string.Empty); - } - if (users.User == null || users.User?.Length == 0) - { - return Response.AsJson(string.Empty); - } - - var usernames = users.User.Select(x => x.Title); - return Response.AsJson(new { Result = true, Users = usernames }); - } - catch (Exception ex) - { - Log.Error(ex); - if (ex is WebException || ex is ApiRequestException) - { - return Response.AsJson(new { Result = false, Message = "Could not load the user list! We have connectivity problems connecting to Plex, Please ensure we can access Plex.Tv, The error has been logged." }); - } - - return Response.AsJson(new { Result = false, Message = ex.Message }); - } - } - - private Negotiator CouchPotato() - { - var settings = CpService.GetSettings(); - - return View["CouchPotato", settings]; - } - - private async Task SaveCouchPotato() - { - var couchPotatoSettings = this.Bind(); - var valid = this.Validate(couchPotatoSettings); - if (!valid.IsValid) - { - return Response.AsJson(valid.SendJsonError()); - } - - var watcherSettings = await WatcherSettings.GetSettingsAsync(); - - if (watcherSettings.Enabled) - { - return - Response.AsJson(new JsonResponseModel - { - Result = false, - Message = "Cannot have Watcher and CouchPotato both enabled." - }); - } - - var radarrSettings = await RadarrSettings.GetSettingsAsync(); - - if (radarrSettings.Enabled) - { - return - Response.AsJson(new JsonResponseModel - { - Result = false, - Message = "Cannot have Radarr and CouchPotato both enabled." - }); - } - - couchPotatoSettings.ApiKey = couchPotatoSettings.ApiKey.Trim(); - var result = await CpService.SaveSettingsAsync(couchPotatoSettings); - return Response.AsJson(result - ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for CouchPotato!" } - : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); - } - - private Negotiator Plex() - { - var settings = PlexService.GetSettings(); - - return View["Plex", settings]; - } - - private async Task SavePlex() - { - var plexSettings = this.Bind(); - - if (plexSettings.Enable) - { - var valid = this.Validate(plexSettings); - if (!valid.IsValid) - { - return Response.AsJson(valid.SendJsonError()); - } - } - - - if (plexSettings.Enable) - { - var embySettings = await EmbySettings.GetSettingsAsync(); - if (embySettings.Enable) - { - return - Response.AsJson(new JsonResponseModel - { - Result = false, - Message = "Emby is enabled, we cannot enable Plex and Emby" - }); - } - } - - if (string.IsNullOrEmpty(plexSettings.MachineIdentifier) && plexSettings.Enable) - { - //Lookup identifier - var server = PlexApi.GetServer(plexSettings.PlexAuthToken); - plexSettings.MachineIdentifier = - server.Server.FirstOrDefault(x => x.AccessToken == plexSettings.PlexAuthToken)?.MachineIdentifier; - } - - var result = await PlexService.SaveSettingsAsync(plexSettings); - - return Response.AsJson(result - ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Plex!" } - : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); - } - - private async Task Emby() - { - var settings = await EmbySettings.GetSettingsAsync(); - - return View["Emby", settings]; - } - - private async Task SaveEmby() - { - var emby = this.Bind(); - var valid = this.Validate(emby); - if (!valid.IsValid) - { - return Response.AsJson(valid.SendJsonError()); - } - - if (emby.Enable) - { - var plexSettings = await PlexService.GetSettingsAsync(); - if (plexSettings.Enable) - { - return - Response.AsJson(new JsonResponseModel - { - Result = false, - Message = "Plex is enabled, we cannot enable Plex and Emby" - }); - } - - - // Get the users - var users = EmbyApi.GetUsers(emby.FullUri, emby.ApiKey); - // Find admin - var admin = users.FirstOrDefault(x => x.Policy.IsAdministrator); - emby.AdministratorId = admin?.Id; - } - var result = await EmbySettings.SaveSettingsAsync(emby); - - return Response.AsJson(result - ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Emby!" } - : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); - } - - private Negotiator Sonarr() - { - var settings = SonarrService.GetSettings(); - - return View["Sonarr", settings]; - } - - private Response SaveSonarr() - { - var sonarrSettings = this.Bind(); - - var valid = this.Validate(sonarrSettings); - if (!valid.IsValid) - { - return Response.AsJson(valid.SendJsonError()); - } - var sickRageEnabled = SickRageService.GetSettings().Enabled; - if (sickRageEnabled) - { - return Response.AsJson(new JsonResponseModel { Result = false, Message = "SickRage is enabled, we cannot enable Sonarr and SickRage" }); - } - - sonarrSettings.ApiKey = sonarrSettings.ApiKey.Trim(); - var result = SonarrService.SaveSettings(sonarrSettings); - - return Response.AsJson(result - ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Sonarr!" } - : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); - } - - - - - - private Negotiator Sickrage() - { - var settings = SickRageService.GetSettings(); - - return View["Sickrage", settings]; - } - - private Response SaveSickrage() - { - var sickRageSettings = this.Bind(); - - var valid = this.Validate(sickRageSettings); - if (!valid.IsValid) - { - return Response.AsJson(valid.SendJsonError()); - } - - var sonarrEnabled = SonarrService.GetSettings().Enabled; - if (sonarrEnabled) - { - return Response.AsJson(new JsonResponseModel { Result = false, Message = "Sonarr is enabled, we cannot enable Sonarr and SickRage" }); - } - sickRageSettings.ApiKey = sickRageSettings.ApiKey.Trim(); - var result = SickRageService.SaveSettings(sickRageSettings); - - return Response.AsJson(result - ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for SickRage!" } - : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); - } - - private Response GetSonarrQualityProfiles() - { - var settings = this.Bind(); - var profiles = SonarrApi.GetProfiles(settings.ApiKey, settings.FullUri); - - // set the cache - if (profiles != null) - { - Cache.Set(CacheKeys.SonarrQualityProfiles, profiles); - } - - return Response.AsJson(profiles); - } - - - private Negotiator EmailNotifications() - { - var settings = EmailService.GetSettings(); - return View["EmailNotifications", settings]; - } - - private async Task TestEmailNotifications() - { - var settings = this.Bind(); - var valid = this.Validate(settings); - if (!valid.IsValid) - { - return Response.AsJson(valid.SendJsonError()); - } - var currentSettings = await EmailService.GetSettingsAsync(); - var notificationModel = new NotificationModel - { - NotificationType = NotificationType.Test, - DateTime = DateTime.Now, - ImgSrc = "http://3.bp.blogspot.com/-EFM-XoKoZ0o/UznF567wCRI/AAAAAAAAALM/6ut7MCF2LrU/s1600/xkcd.png" - }; - try - { - NotificationService.Subscribe(new EmailMessageNotification(EmailService)); - settings.Enabled = true; - await NotificationService.PublishTest(notificationModel, settings, new EmailMessageNotification(EmailService)); - Log.Info("Sent email notification test"); - } - catch (Exception ex) - { - Log.Error("Failed to subscribe and publish test Email Notification"); - var msg = "Failed: " + ex.Message; - return Response.AsJson(new JsonResponseModel { Result = false, Message = msg }); - } - finally - { - if (!currentSettings.Enabled) - { - NotificationService.UnSubscribe(new EmailMessageNotification(EmailService)); - } - } - - return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Email Notification!" }); - - } - - private Response SaveEmailNotifications() - { - var settings = this.Bind(); - var valid = this.Validate(settings); - if (!valid.IsValid) - { - return Response.AsJson(valid.SendJsonError()); - } - if (settings.Authentication) - { - if (string.IsNullOrEmpty(settings.EmailUsername) || string.IsNullOrEmpty(settings.EmailPassword)) - { - return Response.AsJson(new JsonResponseModel { Result = false, Message = "SMTP Authentication is enabled, please specify a username and password" }); - } - } - - var result = EmailService.SaveSettings(settings); - - if (settings.Enabled) - { - NotificationService.Subscribe(new EmailMessageNotification(EmailService)); - } - else - { - NotificationService.UnSubscribe(new EmailMessageNotification(EmailService)); - } - - Log.Info("Saved email settings, result: {0}", result); - return Response.AsJson(result - ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Email Notifications!" } - : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); - } - - - private Negotiator PushbulletNotifications() - { - var settings = PushbulletService.GetSettings(); - return View["PushbulletNotifications", settings]; - } - - private Response SavePushbulletNotifications() - { - var settings = this.Bind(); - var valid = this.Validate(settings); - if (!valid.IsValid) - { - return Response.AsJson(valid.SendJsonError()); - } - - var result = PushbulletService.SaveSettings(settings); - if (settings.Enabled) - { - NotificationService.Subscribe(new PushbulletNotification(PushbulletApi, PushbulletService)); - } - else - { - NotificationService.UnSubscribe(new PushbulletNotification(PushbulletApi, PushbulletService)); - } - - Log.Info("Saved email settings, result: {0}", result); - return Response.AsJson(result - ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Pushbullet Notifications!" } - : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); - } - - private async Task TestPushbulletNotifications() - { - var settings = this.Bind(); - var valid = this.Validate(settings); - if (!valid.IsValid) - { - return Response.AsJson(valid.SendJsonError()); - } - var notificationModel = new NotificationModel - { - NotificationType = NotificationType.Test, - DateTime = DateTime.Now - }; - var currentSettings = await PushbulletService.GetSettingsAsync(); - try - { - NotificationService.Subscribe(new PushbulletNotification(PushbulletApi, PushbulletService)); - settings.Enabled = true; - await NotificationService.PublishTest(notificationModel, settings, new PushbulletNotification(PushbulletApi, PushbulletService)); - Log.Info("Sent pushbullet notification test"); - } - catch (Exception) - { - Log.Error("Failed to subscribe and publish test Pushbullet Notification"); - } - finally - { - if (!currentSettings.Enabled) - { - NotificationService.UnSubscribe(new PushbulletNotification(PushbulletApi, PushbulletService)); - } - } - return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Pushbullet Notification!" }); - } - - private Negotiator PushoverNotifications() - { - var settings = PushoverService.GetSettings(); - return View["PushoverNotifications", settings]; - } - - private Response SavePushoverNotifications() - { - var settings = this.Bind(); - var valid = this.Validate(settings); - if (!valid.IsValid) - { - return Response.AsJson(valid.SendJsonError()); - } - - var result = PushoverService.SaveSettings(settings); - if (settings.Enabled) - { - NotificationService.Subscribe(new PushoverNotification(PushoverApi, PushoverService)); - } - else - { - NotificationService.UnSubscribe(new PushoverNotification(PushoverApi, PushoverService)); - } - - Log.Info("Saved email settings, result: {0}", result); - return Response.AsJson(result - ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Pushover Notifications!" } - : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); - } - - private async Task TestPushoverNotifications() - { - var settings = this.Bind(); - var valid = this.Validate(settings); - if (!valid.IsValid) - { - return Response.AsJson(valid.SendJsonError()); - } - var notificationModel = new NotificationModel - { - NotificationType = NotificationType.Test, - DateTime = DateTime.Now - }; - var currentSettings = await PushoverService.GetSettingsAsync(); - try - { - NotificationService.Subscribe(new PushoverNotification(PushoverApi, PushoverService)); - settings.Enabled = true; - await NotificationService.PublishTest(notificationModel, settings, new PushoverNotification(PushoverApi, PushoverService)); - Log.Info("Sent pushover notification test"); - } - catch (Exception) - { - Log.Error("Failed to subscribe and publish test Pushover Notification"); - } - finally - { - if (!currentSettings.Enabled) - { - NotificationService.UnSubscribe(new PushoverNotification(PushoverApi, PushoverService)); - } - } - return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Pushover Notification!" }); - } - - private async Task GetCpProfiles() - { - var settings = this.Bind(); - var valid = this.Validate(settings); - if (!valid.IsValid) - { - return Response.AsJson(valid.SendJsonError()); - } - if (!settings.Enabled) - { - return Response.AsJson(new CouchPotatoProfiles { list = new List() }); - } - var profiles = CpApi.GetProfiles(settings.FullUri, settings.ApiKey); - - // set the cache - if (profiles != null) - { - Cache.Set(CacheKeys.CouchPotatoQualityProfiles, profiles); - } - - // Save the first profile found (user might not press save...) - settings.ProfileId = profiles?.list?.FirstOrDefault()?._id; - await CpService.SaveSettingsAsync(settings); - - return Response.AsJson(profiles); - } - - private Response GetCpApiKey() - { - var settings = this.Bind(); - - if (string.IsNullOrEmpty(settings.Username) || string.IsNullOrEmpty(settings.Password)) - { - return Response.AsJson(new { Message = "Please enter a username and password to request the Api Key", Result = false }); - } - var key = CpApi.GetApiKey(settings.FullUri, settings.Username, settings.Password); - - - return Response.AsJson(key); - } - - private Negotiator Logs() - { - var model = false; - if (Request.Query["developer"] != null) - model = true; - return View["Logs", model]; - } - - private Response LoadLogs() - { - JsonSettings.MaxJsonLength = int.MaxValue; - var allLogs = LogsRepo.GetAll().OrderByDescending(x => x.Id).Take(200); - var model = new DatatablesModel { Data = new List() }; - foreach (var l in allLogs) - { - l.DateString = l.Date.ToString("G"); - model.Data.Add(l); - } - return Response.AsJson(model); - } - - private Response GetLogLevels() - { - var levels = LogManager.Configuration.LoggingRules.FirstOrDefault(x => x.NameMatches("database")); - return Response.AsJson(levels.Levels); - } - - private Response UpdateLogLevels(int level) - { - var settings = LogService.GetSettings(); - Analytics.TrackEventAsync(Category.Admin, Action.Update, "Updated Log Levels", Username, CookieHelper.GetAnalyticClientId(Cookies), level); - // apply the level - var newLevel = LogLevel.FromOrdinal(level); - LoggingHelper.ReconfigureLogLevel(newLevel); - - //save the log settings - settings.Level = level; - LogService.SaveSettings(settings); - - 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(); - - 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); - } - settings.ApiKey = settings.ApiKey.Trim(); - 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." }); - } - - private async Task Newsletter() - { - var settings = await NewsLetterService.GetSettingsAsync(); - return View["NewsletterSettings", settings]; - } - private Negotiator MassEmailView() - { - return View["MassEmail"]; - } - - private async Task SaveNewsletter() - { - var settings = this.Bind(); - - var valid = this.Validate(settings); - if (!valid.IsValid) - { - var error = valid.SendJsonError(); - Log.Info("Error validating Newsletter settings, message: {0}", error.Message); - return Response.AsJson(error); - } - - // Make sure emails are setup - var emailSettings = await EmailService.GetSettingsAsync(); - if (!emailSettings.Enabled) - { - return Response.AsJson(new JsonResponseModel { Result = false, Message = "Please enable your email notifications" }); - } - - settings.SendRecentlyAddedEmail = settings.SendRecentlyAddedEmail; - var result = NewsLetterService.SaveSettings(settings); - - Log.Info("Saved headphones settings, result: {0}", result); - return Response.AsJson(result - ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Newsletter!" } - : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); - } - - - private Response CreateApiKey() - { - Analytics.TrackEventAsync(Category.Admin, Action.Create, "Created API Key", Username, CookieHelper.GetAnalyticClientId(Cookies)); - var apiKey = Guid.NewGuid().ToString("N"); - var settings = PrService.GetSettings(); - - settings.ApiKey = apiKey; - PrService.SaveSettings(settings); - - return Response.AsJson(apiKey); - } - - private async Task TestSlackNotification() - { - var settings = this.BindAndValidate(); - if (!ModelValidationResult.IsValid) - { - return Response.AsJson(ModelValidationResult.SendJsonError()); - } - var notificationModel = new NotificationModel - { - NotificationType = NotificationType.Test, - DateTime = DateTime.Now - }; - - var currentSlackSettings = await SlackSettings.GetSettingsAsync(); - try - { - NotificationService.Subscribe(new SlackNotification(SlackApi, SlackSettings)); - settings.Enabled = true; - await NotificationService.PublishTest(notificationModel, settings, new SlackNotification(SlackApi, SlackSettings)); - Log.Info("Sent slack notification test"); - } - catch (Exception e) - { - Log.Error(e, "Failed to subscribe and publish test Slack Notification"); - } - finally - { - if (!currentSlackSettings.Enabled) - { - NotificationService.UnSubscribe(new SlackNotification(SlackApi, SlackSettings)); - } - } - return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Slack Notification! If you do not receive it please check the logs." }); - } - - private Negotiator SlackNotifications() - { - var settings = SlackSettings.GetSettings(); - return View["SlackNotifications", settings]; - } - - private Response SaveSlackNotifications() - { - var settings = this.BindAndValidate(); - if (!ModelValidationResult.IsValid) - { - return Response.AsJson(ModelValidationResult.SendJsonError()); - } - - var result = SlackSettings.SaveSettings(settings); - if (settings.Enabled) - { - NotificationService.Subscribe(new SlackNotification(SlackApi, SlackSettings)); - } - else - { - NotificationService.UnSubscribe(new SlackNotification(SlackApi, SlackSettings)); - } - - Log.Info("Saved slack settings, result: {0}", result); - return Response.AsJson(result - ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Slack Notifications!" } - : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); - } - - private async Task DiscordNotification() - { - var settings = await DiscordSettings.GetSettingsAsync(); - return View["DiscordNotification", settings]; - } - - private async Task TestDiscordNotification() - { - var settings = this.BindAndValidate(); - if (!ModelValidationResult.IsValid) - { - return Response.AsJson(ModelValidationResult.SendJsonError()); - } - var notificationModel = new NotificationModel - { - NotificationType = NotificationType.Test, - DateTime = DateTime.Now - }; - - var currentDicordSettings = await DiscordSettings.GetSettingsAsync(); - try - { - NotificationService.Subscribe(new DiscordNotification(DiscordApi, DiscordSettings)); - settings.Enabled = true; - await NotificationService.PublishTest(notificationModel, settings, new DiscordNotification(DiscordApi, DiscordSettings)); - Log.Info("Sent Discord notification test"); - } - catch (Exception e) - { - Log.Error(e, "Failed to subscribe and publish test Discord Notification"); - } - finally - { - if (!currentDicordSettings.Enabled) - { - NotificationService.UnSubscribe(new DiscordNotification(DiscordApi, DiscordSettings)); - } - } - return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Discord Notification! If you do not receive it please check the logs." }); - } - - private async Task SaveDiscordNotifications() - { - var settings = this.BindAndValidate(); - if (!ModelValidationResult.IsValid) - { - return Response.AsJson(ModelValidationResult.SendJsonError()); - } - - var result = await DiscordSettings.SaveSettingsAsync(settings); - if (settings.Enabled) - { - NotificationService.Subscribe(new DiscordNotification(DiscordApi, DiscordSettings)); - } - else - { - NotificationService.UnSubscribe(new DiscordNotification(DiscordApi, DiscordSettings)); - } - - Log.Info("Saved discord settings, result: {0}", result); - return Response.AsJson(result - ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Discord Notifications!" } - : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); - } - - private async Task LandingPage() - { - var settings = await LandingSettings.GetSettingsAsync(); - if (settings.NoticeEnd == DateTime.MinValue) - { - settings.NoticeEnd = DateTime.Now; - } - if (settings.NoticeStart == DateTime.MinValue) - { - settings.NoticeStart = DateTime.Now; - } - return View["LandingPage", settings]; - } - - private async Task SaveLandingPage() - { - var settings = this.Bind(); - - Analytics.TrackEventAsync(Category.Admin, Action.Update, "Update Landing Page", Username, CookieHelper.GetAnalyticClientId(Cookies)); - var plexSettings = await PlexService.GetSettingsAsync(); - var embySettings = await EmbySettings.GetSettingsAsync(); - if (string.IsNullOrEmpty(plexSettings.Ip) && string.IsNullOrEmpty(embySettings.Ip)) - { - return Response.AsJson(new JsonResponseModel { Result = false, Message = "We cannot enable the landing page if Plex/Emby is not setup!" }); - } - - if (settings.Enabled && settings.EnabledNoticeTime && string.IsNullOrEmpty(settings.NoticeMessage)) - { - return Response.AsJson(new JsonResponseModel { Result = false, Message = "If you are going to enabled the notice, then we need a message!" }); - } - - var result = await LandingSettings.SaveSettingsAsync(settings); - - return Response.AsJson(result - ? new JsonResponseModel { Result = true } - : new JsonResponseModel { Result = false, Message = "Could not save to Db Please check the logs" }); - } - - private async Task GetScheduledJobs() - { - var s = await ScheduledJobSettings.GetSettingsAsync(); - var allJobs = await JobRecorder.GetJobsAsync(); - var emby = await EmbySettings.GetSettingsAsync(); - var plex = await PlexService.GetSettingsAsync(); - - - - var dict = new Dictionary(); - - - foreach (var j in allJobs) - { - DateTime dt; - if (dict.TryGetValue(j.Name, out dt)) - { - // We already have the key... Somehow, we should have never got this record. - } - else - { - if (j.Name.Contains("Plex")) - { - if (plex.Enable) - { - dict.Add(j.Name, j.LastRun); - } - } - else if (j.Name.Contains("Emby")) - { - if (emby.Enable) - { - dict.Add(j.Name, j.LastRun); - } - } - else - { - dict.Add(j.Name, j.LastRun); - } - } - - } - - var model = new ScheduledJobsViewModel - { - Emby = emby.Enable, - Plex = plex.Enable, - CouchPotatoCacher = s.CouchPotatoCacher, - PlexAvailabilityChecker = s.PlexAvailabilityChecker, - SickRageCacher = s.SickRageCacher, - SonarrCacher = s.SonarrCacher, - StoreBackup = s.StoreBackup, - StoreCleanup = s.StoreCleanup, - JobRecorder = dict, - RecentlyAddedCron = s.RecentlyAddedCron, - PlexContentCacher = s.PlexContentCacher, - FaultQueueHandler = s.FaultQueueHandler, - PlexEpisodeCacher = s.PlexEpisodeCacher, - PlexUserChecker = s.PlexUserChecker, - UserRequestLimitResetter = s.UserRequestLimitResetter, - EmbyAvailabilityChecker = s.EmbyAvailabilityChecker, - EmbyContentCacher = s.EmbyContentCacher, - EmbyEpisodeCacher = s.EmbyEpisodeCacher, - EmbyUserChecker = s.EmbyUserChecker, - RadarrCacher = s.RadarrCacher, - WatcherCacher = s.WatcherCacher - }; - return View["SchedulerSettings", model]; - } - - private async Task SaveScheduledJobs() - { - - Analytics.TrackEventAsync(Category.Admin, Action.Update, "Update ScheduledJobs", Username, CookieHelper.GetAnalyticClientId(Cookies)); - var settings = this.Bind(); - - if (!string.IsNullOrEmpty(settings.RecentlyAddedCron)) - { - // Validate CRON - var isValid = CronExpression.IsValidExpression(settings.RecentlyAddedCron); - - if (!isValid) - { - return Response.AsJson(new JsonResponseModel - { - Result = false, - Message = - $"CRON {settings.RecentlyAddedCron} is not valid. Please ensure you are using a valid CRON." - }); - } - } - var result = await ScheduledJobSettings.SaveSettingsAsync(settings); - - return Response.AsJson(result - ? new JsonResponseModel { Result = true } - : new JsonResponseModel { Result = false, Message = "Could not save to Db Please check the logs" }); - } - - private async Task ClearLogs() - { - try - { - Analytics.TrackEventAsync(Category.Admin, Action.Delete, "Clear Logs", Username, CookieHelper.GetAnalyticClientId(Cookies)); - var allLogs = await LogsRepo.GetAllAsync(); - foreach (var logEntity in allLogs) - { - await LogsRepo.DeleteAsync(logEntity); - } - return Response.AsJson(new JsonResponseModel { Result = true, Message = "Logs cleared successfully." }); - } - catch (Exception e) - { - Log.Error(e); - return Response.AsJson(new JsonResponseModel { Result = false, Message = e.Message }); - } - } - - private async Task NotificationSettings() - { - var s = await NotifySettings.GetSettingsAsync(); - return View["NotificationSettings", s]; - } - - private Negotiator SaveNotificationSettings() - { - var model = this.Bind(); - return View["NotificationSettings", model]; - } - - private Response TestNewsletterAdminEmail() - { - try - { - Log.Debug("Clicked Admin Newsletter Email Test"); - RecentlyAdded.RecentlyAddedAdminTest(); - return Response.AsJson(new JsonResponseModel { Result = true, Message = "Sent email to administrator" }); - } - catch (Exception e) - { - Log.Error(e); - return Response.AsJson(new JsonResponseModel { Result = false, Message = e.Message }); - } - } - private Response TestMassAdminEmail() - { - try - { - var settings = this.Bind(); - Log.Debug("Clicked Admin Mass Email Test"); - if (settings.Subject == null) - { - return Response.AsJson(new JsonResponseModel { Result = false, Message = "Please Set a Subject" }); - } - if (settings.Body == null) - { - return Response.AsJson(new JsonResponseModel { Result = false, Message = "Please Set a Body" }); - } - MassEmail.MassEmailAdminTest(settings.Body.Replace("\n", "
"), settings.Subject); - return Response.AsJson(new JsonResponseModel { Result = true, Message = "Sent email to administrator" }); - } - catch (Exception e) - { - Log.Error(e); - return Response.AsJson(new JsonResponseModel { Result = false, Message = e.Message }); - } - } - private Response SendMassEmail() - { - try - { - var settings = this.Bind(); - Log.Debug("Clicked Admin Mass Email Test"); - if (settings.Subject == null) - { - return Response.AsJson(new JsonResponseModel { Result = false, Message = "Please Set a Subject" }); - } - if (settings.Body == null) - { - return Response.AsJson(new JsonResponseModel { Result = false, Message = "Please Set a Body" }); - } - MassEmail.SendMassEmail(settings.Body.Replace("\n", "
"), settings.Subject); - return Response.AsJson(new JsonResponseModel { Result = true, Message = "Sent email to All users" }); - } - catch (Exception e) - { - Log.Error(e); - return Response.AsJson(new JsonResponseModel { Result = false, Message = e.Message }); - } - } - } -} \ No newline at end of file diff --git a/Ombi/Ombi.Api.Emby/EmbyApi.cs b/Ombi/Ombi.Api.Emby/EmbyApi.cs new file mode 100644 index 000000000..91e1ab789 --- /dev/null +++ b/Ombi/Ombi.Api.Emby/EmbyApi.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading.Tasks; +using Ombi.Api.Emby.Models; +using Ombi.Helpers; + +namespace Ombi.Api.Emby +{ + public class EmbyApi : IEmbyApi + { + public EmbyApi() + { + Api = new Api(); + } + + private Api Api { get; } + + /// + /// Returns all users from the Emby Instance + /// + /// + /// + public async Task> GetUsers(Uri baseUri, string apiKey) + { + var request = new Request("emby/users", baseUri.ToString(), HttpMethod.Get); + + AddHeaders(request, apiKey); + var obj = await Api.Request>(request); + + return obj; + } + + public async Task GetSystemInformation(string apiKey, Uri baseUrl) + { + var request = new Request("emby/System/Info", baseUrl.ToString(), HttpMethod.Get); + + AddHeaders(request, apiKey); + + var obj = await Api.Request(request); + + return obj; + } + + public async Task LogIn(string username, string password, string apiKey, Uri baseUri) + { + var request = new Request("emby/users/authenticatebyname", baseUri.ToString(), HttpMethod.Post); + + + var body = new + { + username, + password = password.GetSha1Hash().ToLower(), + passwordMd5 = password.CalcuateMd5Hash() + }; + + request.AddJsonBody(body); + + request.AddHeader("X-Emby-Authorization", + $"MediaBrowser Client=\"Ombi\", Device=\"Ombi\", DeviceId=\"v3\", Version=\"v3\""); + AddHeaders(request, apiKey); + + var obj = await Api.Request(request); + return obj; + } + + private static void AddHeaders(Request req, string apiKey) + { + if (!string.IsNullOrEmpty(apiKey)) + { + req.AddHeader("X-MediaBrowser-Token", apiKey); + } + req.AddHeader("Accept", "application/json"); + req.AddContentHeader("Content-Type", "application/json"); + req.AddHeader("Device", "Ombi"); + } + } +} diff --git a/Ombi/Ombi.Api.Emby/IEmbyApi.cs b/Ombi/Ombi.Api.Emby/IEmbyApi.cs new file mode 100644 index 000000000..faecd238a --- /dev/null +++ b/Ombi/Ombi.Api.Emby/IEmbyApi.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Ombi.Api.Emby.Models; + +namespace Ombi.Api.Emby +{ + public interface IEmbyApi + { + Task GetSystemInformation(string apiKey, Uri baseUrl); + Task> GetUsers(Uri baseUri, string apiKey); + Task LogIn(string username, string password, string apiKey, Uri baseUri); + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Api.Emby/Models/EmbyConfiguration.cs b/Ombi/Ombi.Api.Emby/Models/EmbyConfiguration.cs new file mode 100644 index 000000000..d69e6d3e8 --- /dev/null +++ b/Ombi/Ombi.Api.Emby/Models/EmbyConfiguration.cs @@ -0,0 +1,45 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2017 Jamie Rees +// File: EmbyConfiguration.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 Ombi.Api.Emby.Models +{ + public class EmbyConfiguration + { + public bool PlayDefaultAudioTrack { get; set; } + public bool DisplayMissingEpisodes { get; set; } + public bool DisplayUnairedEpisodes { get; set; } + public object[] GroupedFolders { get; set; } + public string SubtitleMode { get; set; } + public bool DisplayCollectionsView { get; set; } + public bool EnableLocalPassword { get; set; } + public object[] OrderedViews { get; set; } + public object[] LatestItemsExcludes { get; set; } + public bool HidePlayedInLatest { get; set; } + public bool RememberAudioSelections { get; set; } + public bool RememberSubtitleSelections { get; set; } + public bool EnableNextEpisodeAutoPlay { get; set; } + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Api.Emby/Models/EmbyPolicy.cs b/Ombi/Ombi.Api.Emby/Models/EmbyPolicy.cs new file mode 100644 index 000000000..282ad99dd --- /dev/null +++ b/Ombi/Ombi.Api.Emby/Models/EmbyPolicy.cs @@ -0,0 +1,59 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2017 Jamie Rees +// File: EmbyPolicy.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 Ombi.Api.Emby.Models +{ + public class EmbyPolicy + { + public bool IsAdministrator { get; set; } + public bool IsHidden { get; set; } + public bool IsDisabled { get; set; } + public object[] BlockedTags { get; set; } + public bool EnableUserPreferenceAccess { get; set; } + public object[] AccessSchedules { get; set; } + public object[] BlockUnratedItems { get; set; } + public bool EnableRemoteControlOfOtherUsers { get; set; } + public bool EnableSharedDeviceControl { get; set; } + public bool EnableLiveTvManagement { get; set; } + public bool EnableLiveTvAccess { get; set; } + public bool EnableMediaPlayback { get; set; } + public bool EnableAudioPlaybackTranscoding { get; set; } + public bool EnableVideoPlaybackTranscoding { get; set; } + public bool EnablePlaybackRemuxing { get; set; } + public bool EnableContentDeletion { get; set; } + public bool EnableContentDownloading { get; set; } + public bool EnableSync { get; set; } + public bool EnableSyncTranscoding { get; set; } + public object[] EnabledDevices { get; set; } + public bool EnableAllDevices { get; set; } + public object[] EnabledChannels { get; set; } + public bool EnableAllChannels { get; set; } + public object[] EnabledFolders { get; set; } + public bool EnableAllFolders { get; set; } + public int InvalidLoginAttemptCount { get; set; } + public bool EnablePublicSharing { get; set; } + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Api.Emby/Models/EmbySystemInfo.cs b/Ombi/Ombi.Api.Emby/Models/EmbySystemInfo.cs new file mode 100644 index 000000000..3f7154c73 --- /dev/null +++ b/Ombi/Ombi.Api.Emby/Models/EmbySystemInfo.cs @@ -0,0 +1,63 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2017 Jamie Rees +// File: EmbySystemInfo.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 Ombi.Api.Emby.Models +{ + public class EmbySystemInfo + { + public string SystemUpdateLevel { get; set; } + public string OperatingSystemDisplayName { get; set; } + public bool SupportsRunningAsService { get; set; } + public string MacAddress { get; set; } + public bool HasPendingRestart { get; set; } + public bool SupportsLibraryMonitor { get; set; } + public object[] InProgressInstallations { get; set; } + public int WebSocketPortNumber { get; set; } + public object[] CompletedInstallations { get; set; } + public bool CanSelfRestart { get; set; } + public bool CanSelfUpdate { get; set; } + public object[] FailedPluginAssemblies { get; set; } + public string ProgramDataPath { get; set; } + public string ItemsByNamePath { get; set; } + public string CachePath { get; set; } + public string LogPath { get; set; } + public string InternalMetadataPath { get; set; } + public string TranscodingTempPath { get; set; } + public int HttpServerPortNumber { get; set; } + public bool SupportsHttps { get; set; } + public int HttpsPortNumber { get; set; } + public bool HasUpdateAvailable { get; set; } + public bool SupportsAutoRunAtStartup { get; set; } + public string EncoderLocationType { get; set; } + public string SystemArchitecture { get; set; } + public string LocalAddress { get; set; } + public string WanAddress { get; set; } + public string ServerName { get; set; } + public string Version { get; set; } + public string OperatingSystem { get; set; } + public string Id { get; set; } + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Api.Emby/Models/EmbyUser.cs b/Ombi/Ombi.Api.Emby/Models/EmbyUser.cs new file mode 100644 index 000000000..80ce1332b --- /dev/null +++ b/Ombi/Ombi.Api.Emby/Models/EmbyUser.cs @@ -0,0 +1,48 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2017 Jamie Rees +// File: EmbyUser.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; + +namespace Ombi.Api.Emby.Models +{ + public class EmbyUser + { + public string Name { get; set; } + public string ServerId { get; set; } + public string ConnectUserName { get; set; } + public string ConnectUserId { get; set; } + public string ConnectLinkType { get; set; } + public string Id { get; set; } + public bool HasPassword { get; set; } + public bool HasConfiguredPassword { get; set; } + public bool HasConfiguredEasyPassword { get; set; } + public DateTime LastLoginDate { get; set; } + public DateTime LastActivityDate { get; set; } + public EmbyConfiguration Configuration { get; set; } + public EmbyPolicy Policy { get; set; } + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Api.Emby/Models/EmbyUserLogin.cs b/Ombi/Ombi.Api.Emby/Models/EmbyUserLogin.cs new file mode 100644 index 000000000..5f8f75370 --- /dev/null +++ b/Ombi/Ombi.Api.Emby/Models/EmbyUserLogin.cs @@ -0,0 +1,7 @@ +namespace Ombi.Api.Emby.Models +{ + public class EmbyUserLogin + { + public EmbyUser User { get; set; } + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Api.Emby/Ombi.Api.Emby.csproj b/Ombi/Ombi.Api.Emby/Ombi.Api.Emby.csproj new file mode 100644 index 000000000..c173d1dd7 --- /dev/null +++ b/Ombi/Ombi.Api.Emby/Ombi.Api.Emby.csproj @@ -0,0 +1,12 @@ + + + + netstandard1.6 + + + + + + + + \ No newline at end of file diff --git a/Ombi/Ombi.DependencyInjection/IocExtensions.cs b/Ombi/Ombi.DependencyInjection/IocExtensions.cs index 509e81d5f..d3ad2f723 100644 --- a/Ombi/Ombi.DependencyInjection/IocExtensions.cs +++ b/Ombi/Ombi.DependencyInjection/IocExtensions.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using Ombi.Api.Emby; using Ombi.Api.Plex; using Ombi.Core; using Ombi.Core.Engine; @@ -42,6 +43,7 @@ namespace Ombi.DependencyInjection { services.AddTransient(); services.AddTransient(); + services.AddTransient(); return services; } diff --git a/Ombi/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj b/Ombi/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj index a1142f694..358c86a80 100644 --- a/Ombi/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj +++ b/Ombi/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj @@ -11,6 +11,7 @@ + diff --git a/Ombi/Ombi.Helpers/StringHelper.cs b/Ombi/Ombi.Helpers/StringHelper.cs index c936490f3..4c76df85d 100644 --- a/Ombi/Ombi.Helpers/StringHelper.cs +++ b/Ombi/Ombi.Helpers/StringHelper.cs @@ -1,4 +1,7 @@ using System.Globalization; +using System.Linq; +using System.Security.Cryptography; +using System.Text; namespace Ombi.Helpers { @@ -8,5 +11,34 @@ namespace Ombi.Helpers { return CultureInfo.CurrentUICulture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0; } + + public static string CalcuateMd5Hash(this string input) + { + if (string.IsNullOrEmpty(input)) + { + return string.Empty; + } + using (var md5 = MD5.Create()) + { + var inputBytes = Encoding.UTF8.GetBytes(input); + var hash = md5.ComputeHash(inputBytes); + + var sb = new StringBuilder(); + + foreach (var t in hash) + { + sb.Append(t.ToString("x2")); + } + + return sb.ToString(); + } + } + + public static string GetSha1Hash(this string input) + { + var sha1 = SHA1.Create(); + return string.Join("", (sha1.ComputeHash(Encoding.UTF8.GetBytes(input))).Select(x => x.ToString("x2")).ToArray()); + } + } } \ No newline at end of file diff --git a/Ombi/Ombi.sln b/Ombi/Ombi.sln index 15d1bee35..27fcb5cc4 100644 --- a/Ombi/Ombi.sln +++ b/Ombi/Ombi.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.9 +VisualStudioVersion = 15.0.26403.3 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi", "Ombi\Ombi.csproj", "{C987AA67-AFE1-468F-ACD3-EAD5A48E1F6A}" EndProject @@ -36,6 +36,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Plex", "Ombi.Api.P EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Schedule", "Ombi.Schedule\Ombi.Schedule.csproj", "{5B42ADD4-757A-47C1-9CC5-320829C5E665}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Emby", "Ombi.Api.Emby\Ombi.Api.Emby.csproj", "{08FF107D-31E1-470D-AF86-E09B015CEE06}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -82,6 +84,10 @@ Global {5B42ADD4-757A-47C1-9CC5-320829C5E665}.Debug|Any CPU.Build.0 = Debug|Any CPU {5B42ADD4-757A-47C1-9CC5-320829C5E665}.Release|Any CPU.ActiveCfg = Release|Any CPU {5B42ADD4-757A-47C1-9CC5-320829C5E665}.Release|Any CPU.Build.0 = Release|Any CPU + {08FF107D-31E1-470D-AF86-E09B015CEE06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {08FF107D-31E1-470D-AF86-E09B015CEE06}.Debug|Any CPU.Build.0 = Debug|Any CPU + {08FF107D-31E1-470D-AF86-E09B015CEE06}.Release|Any CPU.ActiveCfg = Release|Any CPU + {08FF107D-31E1-470D-AF86-E09B015CEE06}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -92,5 +98,6 @@ Global {B39E4558-C557-48E7-AA74-19C5CD809617} = {410F36CF-9C60-428A-B191-6FD90610991A} {63E63511-1C7F-4162-8F92-8F7391B3C8A3} = {025FB189-2FFB-4F43-A64B-6F1B5A0D2065} {2E1A7B91-F29B-42BC-8F1E-1CF2DCC389BA} = {9293CA11-360A-4C20-A674-B9E794431BF5} + {08FF107D-31E1-470D-AF86-E09B015CEE06} = {9293CA11-360A-4C20-A674-B9E794431BF5} EndGlobalSection EndGlobal diff --git a/Ombi/Ombi/Controllers/EmbyController.cs b/Ombi/Ombi/Controllers/EmbyController.cs new file mode 100644 index 000000000..5c0b778f2 --- /dev/null +++ b/Ombi/Ombi/Controllers/EmbyController.cs @@ -0,0 +1,45 @@ +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Ombi.Api.Emby; +using Ombi.Core.Settings; +using Ombi.Core.Settings.Models.External; + +namespace Ombi.Controllers +{ + [Authorize] + public class EmbyController : BaseV1ApiController + { + public EmbyController(IEmbyApi emby, ISettingsService embySettings) + { + EmbyApi = emby; + EmbySettings = embySettings; + } + + private IEmbyApi EmbyApi { get; } + private ISettingsService EmbySettings { get; } + + [HttpPost] + [AllowAnonymous] + public async Task SignIn([FromBody] EmbySettings request) + { + // Check if settings exist + var settings = await EmbySettings.GetSettingsAsync(); + if (settings != null && !string.IsNullOrEmpty(settings.ApiKey)) return null; + + request.Enable = true; + // Test that we can connect + var result = await EmbyApi.GetUsers(request.FullUri, request.ApiKey); + + if (result != null && result.Any()) + { + request.AdministratorId = result.FirstOrDefault(x => x.Policy.IsAdministrator)?.Id ?? string.Empty; + await EmbySettings.SaveSettingsAsync(request); + + return request; + } + return null; + } + } +} diff --git a/Ombi/Ombi/Controllers/PlexController.cs b/Ombi/Ombi/Controllers/PlexController.cs index 322518c9b..db93d91f9 100644 --- a/Ombi/Ombi/Controllers/PlexController.cs +++ b/Ombi/Ombi/Controllers/PlexController.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Ombi.Api.Plex; using Ombi.Api.Plex.Models; @@ -12,6 +13,7 @@ using Ombi.Core.Settings.Models.External; namespace Ombi.Controllers { + [Authorize] public class PlexController : BaseV1ApiController { public PlexController(IPlexApi plexApi, ISettingsService plexSettings) @@ -24,8 +26,13 @@ namespace Ombi.Controllers private ISettingsService PlexSettings { get; } [HttpPost] + [AllowAnonymous] public async Task SignIn([FromBody] UserRequest request) { + // Do we already have settings? + var settings = await PlexSettings.GetSettingsAsync(); + if (settings != null && !string.IsNullOrEmpty(settings.PlexAuthToken)) return null; + var result = await PlexApi.SignIn(request); if (!string.IsNullOrEmpty(result.user?.authentication_token)) { diff --git a/Ombi/Ombi/Ombi.csproj b/Ombi/Ombi/Ombi.csproj index 23e4dc7d6..672e1f83d 100644 --- a/Ombi/Ombi/Ombi.csproj +++ b/Ombi/Ombi/Ombi.csproj @@ -18,19 +18,26 @@ - - - - identity - Copy.service.ts + + PreserveNewest - - identity - Copy.service.js + PreserveNewest - + PreserveNewest + + + + + + PreserveNewest + + + + @@ -45,27 +52,14 @@ - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - PreserveNewest - - PreserveNewest - @@ -77,6 +71,10 @@ + + + + PreserveNewest @@ -121,6 +119,7 @@ + diff --git a/Ombi/Ombi/wwwroot/app/app.component.html b/Ombi/Ombi/wwwroot/app/app.component.html index 1bbc54d2c..f065cfa34 100644 --- a/Ombi/Ombi/wwwroot/app/app.component.html +++ b/Ombi/Ombi/wwwroot/app/app.component.html @@ -19,10 +19,19 @@ -