The move!

This commit is contained in:
Jamie.Rees 2017-05-16 08:31:44 +01:00
commit 25526cc4d9
1147 changed files with 85 additions and 8524 deletions

View file

@ -0,0 +1,55 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: CacheKeys.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.Core
{
public class CacheKeys
{
public struct TimeFrameMinutes
{
public const int SchedulerCaching = 120;
}
public const string PlexLibaries = nameof(PlexLibaries);
public const string PlexEpisodes = nameof(PlexEpisodes);
public const string TvDbToken = nameof(TvDbToken);
public const string SonarrQualityProfiles = nameof(SonarrQualityProfiles);
public const string RadarrQualityProfiles = nameof(RadarrQualityProfiles);
public const string SonarrQueued = nameof(SonarrQueued);
public const string RadarrMovies = nameof(RadarrMovies);
public const string SickRageQualityProfiles = nameof(SickRageQualityProfiles);
public const string SickRageQueued = nameof(SickRageQueued);
public const string CouchPotatoQualityProfiles = nameof(CouchPotatoQualityProfiles);
public const string CouchPotatoQueued = nameof(CouchPotatoQueued);
public const string WatcherQueued = nameof(WatcherQueued);
public const string GetCustomizationSettings = nameof(GetCustomizationSettings);
public const string GetEmbySettings = nameof(GetEmbySettings);
public const string GetPlexRequestSettings = nameof(GetPlexRequestSettings);
public const string LastestProductVersion = nameof(LastestProductVersion);
public const string SonarrRootFolders = nameof(SonarrRootFolders);
public const string RadarrRootFolders = nameof(RadarrRootFolders);
}
}

View file

@ -0,0 +1,107 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: HeadphonesSender.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.Linq;
using System.Threading;
using System.Threading.Tasks;
using NLog;
using Ombi.Api.Interfaces;
using Ombi.Core.SettingModels;
using Ombi.Store;
namespace Ombi.Core
{
public class HeadphonesSender
{
public HeadphonesSender(IHeadphonesApi api, HeadphonesSettings settings, IRequestService request)
{
Api = api;
Settings = settings;
RequestService = request;
}
private int WaitTime => 2000;
private int CounterMax => 60;
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private IHeadphonesApi Api { get; }
private IRequestService RequestService { get; }
private HeadphonesSettings Settings { get; }
public async Task<bool> AddAlbum(RequestedModel request)
{
var addArtistResult = await AddArtist(request);
if (!addArtistResult)
{
return false;
}
// Artist is now active
// Add album
var albumResult = await Api.AddAlbum(Settings.ApiKey, Settings.FullUri, request.ReleaseId);
if (!albumResult)
{
Log.Error("Couldn't add the album to headphones");
}
// Set the status to wanted and search
var status = await SetAlbumStatus(request);
if (!status)
{
return false;
}
// Approve it
request.Approved = true;
// Update the record
bool updated = RequestService.UpdateRequest(request);
return updated;
}
private async Task<bool> AddArtist(RequestedModel request)
{
var index = await Api.GetIndex(Settings.ApiKey, Settings.FullUri);
var artistExists = index.Any(x => x.ArtistID == request.ArtistId);
bool artistAdd = false;
if (!artistExists)
{
artistAdd = await Api.AddArtist(Settings.ApiKey, Settings.FullUri, request.ArtistId);
Log.Info("Artist add result for {1}: {0}", artistAdd, request.ArtistName);
}
return artistExists || artistAdd;
}
private async Task<bool> SetAlbumStatus(RequestedModel request)
{
bool setStatus = await Api.QueueAlbum(Settings.ApiKey, Settings.FullUri, request.ReleaseId);
return setStatus;
}
}
}

View file

@ -0,0 +1,43 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: IIssueService.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.Collections.Generic;
using System.Threading.Tasks;
using Ombi.Core.Models;
namespace Ombi.Core
{
public interface IIssueService
{
Task<int> AddIssueAsync(IssuesModel model);
Task<bool> UpdateIssueAsync(IssuesModel model);
Task DeleteIssueAsync(int id);
Task DeleteIssueAsync(IssuesModel model);
Task<IssuesModel> GetAsync(int id);
Task<IEnumerable<IssuesModel>> GetAllAsync();
}
}

View file

@ -0,0 +1,10 @@
using System.Threading.Tasks;
using Ombi.Store;
namespace Ombi.Core
{
public interface IMovieSender
{
Task<MovieSenderResult> Send(RequestedModel model, string qualityId = "");
}
}

View file

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using Ombi.Store.Models.Plex;
namespace Ombi.Core
{
public interface IPlexReadOnlyDatabase
{
IEnumerable<MetadataItems> GetItemsAddedAfterDate(DateTime dateTime);
}
}

View file

@ -0,0 +1,56 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: IRequestService.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.Collections.Generic;
using System.Threading.Tasks;
using Ombi.Store;
namespace Ombi.Core
{
public interface IRequestService
{
long AddRequest(RequestedModel model);
Task<int> AddRequestAsync(RequestedModel model);
RequestedModel CheckRequest(int providerId);
Task<RequestedModel> CheckRequestAsync(int providerId);
RequestedModel CheckRequest(string musicId);
Task<RequestedModel> CheckRequestAsync(string musicId);
void DeleteRequest(RequestedModel request);
Task DeleteRequestAsync(RequestedModel request);
bool UpdateRequest(RequestedModel model);
Task<bool> UpdateRequestAsync(RequestedModel model);
RequestedModel Get(int id);
Task<RequestedModel> GetAsync(int id);
IEnumerable<RequestedModel> GetAll();
Task<IEnumerable<RequestedModel>> GetAllAsync();
bool BatchUpdate(IEnumerable<RequestedModel> model);
Task<bool> BatchUpdateAsync(IEnumerable<RequestedModel> model);
bool BatchDelete(IEnumerable<RequestedModel> model);
Task<bool> BatchDeleteAsync(IEnumerable<RequestedModel> model);
}
}

View file

@ -0,0 +1,38 @@
using System;
using Nancy;
using Nancy.Security;
using Nancy.Session;
using Ombi.Helpers.Permissions;
namespace Ombi.Core
{
public interface ISecurityExtensions
{
Response AdminLoginRedirect(Permissions perm, NancyContext context);
Response AdminLoginRedirect(NancyContext context, params Permissions[] perm);
bool DoesNotHavePermissions(Permissions perm, IUserIdentity currentUser);
Response HasAnyPermissionsRedirect(NancyContext context, string routeName, HttpStatusCode code,
params Permissions[] perm);
bool DoesNotHavePermissions(int perm, IUserIdentity currentUser);
Func<NancyContext, Response> ForbiddenIfNot(Func<NancyContext, bool> test);
bool HasAnyPermissions(IUserIdentity user, params Permissions[] perm);
bool HasPermissions(IUserIdentity user, Permissions perm);
Response HasPermissionsRedirect(Permissions perm, NancyContext context, string routeName, HttpStatusCode code);
Func<NancyContext, Response> HttpStatusCodeIfNot(HttpStatusCode statusCode, Func<NancyContext, bool> test);
bool IsLoggedIn(NancyContext context);
bool IsNormalUser(IUserIdentity user);
bool IsExternalUser(IUserIdentity user);
bool HasPermissions(string userName, Permissions perm);
/// <summary>
/// Gets the username this could be the alias! We should always use this method when getting the username
/// </summary>
/// <param name="username">The username.</param>
/// <param name="session">The session.</param>
/// <returns>
/// <c>null</c> if we cannot find a user
/// </returns>
string GetUsername(string username, ISession session);
}
}

View file

@ -0,0 +1,41 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: ISettingsService.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.Threading.Tasks;
namespace Ombi.Core
{
public interface ISettingsService<T>
{
T GetSettings();
Task<T> GetSettingsAsync();
bool SaveSettings(T model);
Task<bool> SaveSettingsAsync(T model);
bool Delete(T model);
Task<bool> DeleteAsync(T model);
}
}

View file

@ -0,0 +1,16 @@
using System;
using System.Threading.Tasks;
using Nancy.Session;
using Octokit;
using Ombi.Core.Models;
namespace Ombi.Core
{
public interface IStatusChecker
{
Task<StatusModel> GetStatus();
Task<Issue> ReportBug(string title, string body, string oauthToken);
Task<Uri> OAuth(string url, ISession session);
Task<OauthToken> OAuthAccessToken(string code);
}
}

View file

@ -0,0 +1,114 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: IssueJsonService.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.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Ombi.Core.Models;
using Ombi.Helpers;
using Ombi.Store.Models;
using Ombi.Store.Repository;
namespace Ombi.Core
{
public class IssueJsonService : IIssueService
{
public IssueJsonService(IRepository<IssueBlobs> repo)
{
Repo = repo;
}
private IRepository<IssueBlobs> Repo { get; }
public async Task<int> AddIssueAsync(IssuesModel model)
{
var entity = new IssueBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), RequestId = model.RequestId };
var id = await Repo.InsertAsync(entity);
model.Id = id;
entity = new IssueBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), RequestId = model.RequestId, Id = id };
var result = await Repo.UpdateAsync(entity).ConfigureAwait(false);
return result ? id : -1;
}
public async Task<bool> UpdateIssueAsync(IssuesModel model)
{
var entity = new IssueBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), RequestId = model.RequestId, Id = model.Id };
return await Repo.UpdateAsync(entity).ConfigureAwait(false);
}
public async Task DeleteIssueAsync(int id)
{
var entity = await Repo.GetAsync(id);
await Repo.DeleteAsync(entity).ConfigureAwait(false);
}
public async Task DeleteIssueAsync(IssuesModel model)
{
var entity = new IssueBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), RequestId = model.RequestId, Id = model.Id };
await Repo.DeleteAsync(entity).ConfigureAwait(false);
}
/// <summary>
/// Gets the Issues, if there is no issue in the DB we return a <c>new <see cref="IssuesModel"/></c>.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns></returns>
public async Task<IssuesModel> GetAsync(int id)
{
var blob = await Repo.GetAsync(id);
if (blob == null)
{
return new IssuesModel();
}
var model = ByteConverterHelper.ReturnObject<IssuesModel>(blob.Content);
return model;
}
/// <summary>
/// Gets all the Issues, if there is no issue in the DB we return a <c>new IEnumerable&lt;IssuesModel&gt;</c>.
/// </summary>
/// <returns></returns>
public async Task<IEnumerable<IssuesModel>> GetAllAsync()
{
var blobs = await Repo.GetAllAsync().ConfigureAwait(false);
if (blobs == null)
{
return new List<IssuesModel>();
}
return blobs.Select(b => Encoding.UTF8.GetString(b.Content))
.Select(JsonConvert.DeserializeObject<IssuesModel>)
.ToList();
}
}
}

View file

@ -0,0 +1,229 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: JsonRequestModelRequestService.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.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Ombi.Helpers;
using Ombi.Store;
using Ombi.Store.Models;
using Ombi.Store.Repository;
namespace Ombi.Core
{
public class JsonRequestModelRequestService : IRequestService
{
public JsonRequestModelRequestService(IRequestRepository repo)
{
Repo = repo;
}
private IRequestRepository Repo { get; }
public long AddRequest(RequestedModel model)
{
var entity = new RequestBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), ProviderId = model.ProviderId };
var id = Repo.Insert(entity);
model.Id = (int)id;
entity = new RequestBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), ProviderId = model.ProviderId, Id = (int)id, MusicId = model.MusicBrainzId };
var result = Repo.Update(entity);
return result ? id : -1;
}
public async Task<int> AddRequestAsync(RequestedModel model)
{
var entity = new RequestBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), ProviderId = model.ProviderId };
var id = await Repo.InsertAsync(entity).ConfigureAwait(false);
model.Id = id;
entity = new RequestBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), ProviderId = model.ProviderId, Id = id, MusicId = model.MusicBrainzId };
var result = await Repo.UpdateAsync(entity).ConfigureAwait(false);
return result ? id : -1;
}
public RequestedModel CheckRequest(int providerId)
{
var blobs = Repo.GetAll();
var blob = blobs.FirstOrDefault(x => x.ProviderId == providerId); if (blob == null)
{
return null;
}
var model = ByteConverterHelper.ReturnObject<RequestedModel>(blob.Content);
model.Id = blob.Id;
return model;
}
public async Task<RequestedModel> CheckRequestAsync(int providerId)
{
var blobs = await Repo.GetAllAsync().ConfigureAwait(false);
var blob = blobs.FirstOrDefault(x => x.ProviderId == providerId); if (blob == null)
{
return null;
}
var model = ByteConverterHelper.ReturnObject<RequestedModel>(blob.Content);
model.Id = blob.Id;
return model;
}
public RequestedModel CheckRequest(string musicId)
{
var blobs = Repo.GetAll();
var blob = blobs.FirstOrDefault(x => x.MusicId == musicId); if (blob == null)
{
return null;
}
var model = ByteConverterHelper.ReturnObject<RequestedModel>(blob.Content);
model.Id = blob.Id;
return model;
}
public async Task<RequestedModel> CheckRequestAsync(string musicId)
{
var blobs = await Repo.GetAllAsync().ConfigureAwait(false);
var blob = blobs.FirstOrDefault(x => x.MusicId == musicId);
if (blob == null)
{
return null;
}
var model = ByteConverterHelper.ReturnObject<RequestedModel>(blob.Content);
model.Id = blob.Id;
return model;
}
public void DeleteRequest(RequestedModel request)
{
var blob = Repo.Get(request.Id);
Repo.Delete(blob);
}
public async Task DeleteRequestAsync(RequestedModel request)
{
var blob = await Repo.GetAsync(request.Id).ConfigureAwait(false);
await Repo.DeleteAsync(blob).ConfigureAwait(false);
}
public bool UpdateRequest(RequestedModel model)
{
var entity = new RequestBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), ProviderId = model.ProviderId, Id = model.Id };
return Repo.Update(entity);
}
public async Task<bool> UpdateRequestAsync(RequestedModel model)
{
var entity = new RequestBlobs { Type = model.Type, Content = ByteConverterHelper.ReturnBytes(model), ProviderId = model.ProviderId, Id = model.Id };
return await Repo.UpdateAsync(entity).ConfigureAwait(false);
}
public RequestedModel Get(int id)
{
var blob = Repo.Get(id);
if (blob == null)
{
return new RequestedModel();
}
var model = ByteConverterHelper.ReturnObject<RequestedModel>(blob.Content);
model.Id = blob.Id; // They should always be the same, but for somereason a user didn't have it in the db https://github.com/tidusjar/Ombi/issues/862#issuecomment-269743847
return model;
}
public async Task<RequestedModel> GetAsync(int id)
{
var blob = await Repo.GetAsync(id).ConfigureAwait(false);
if (blob == null)
{
return new RequestedModel();
}
var model = ByteConverterHelper.ReturnObject<RequestedModel>(blob.Content);
model.Id = blob.Id;
return model;
}
public IEnumerable<RequestedModel> GetAll()
{
var blobs = Repo.GetAll().ToList();
var retVal = new List<RequestedModel>();
foreach (var b in blobs)
{
if (b == null)
{
continue;
}
var model = ByteConverterHelper.ReturnObject<RequestedModel>(b.Content);
model.Id = b.Id;
retVal.Add(model);
}
return retVal;
}
public async Task<IEnumerable<RequestedModel>> GetAllAsync()
{
var blobs = await Repo.GetAllAsync().ConfigureAwait(false);
var retVal = new List<RequestedModel>();
foreach (var b in blobs)
{
if (b == null)
{
continue;
}
var model = ByteConverterHelper.ReturnObject<RequestedModel>(b.Content);
model.Id = b.Id;
retVal.Add(model);
}
return retVal;
}
public bool BatchUpdate(IEnumerable<RequestedModel> model)
{
var entities = model.Select(m => new RequestBlobs { Type = m.Type, Content = ByteConverterHelper.ReturnBytes(m), ProviderId = m.ProviderId, Id = m.Id }).ToList();
return Repo.UpdateAll(entities);
}
public async Task<bool> BatchUpdateAsync(IEnumerable<RequestedModel> model)
{
var entities = model.Select(m => new RequestBlobs { Type = m.Type, Content = ByteConverterHelper.ReturnBytes(m), ProviderId = m.ProviderId, Id = m.Id }).ToList();
return await Repo.UpdateAllAsync(entities).ConfigureAwait(false);
}
public bool BatchDelete(IEnumerable<RequestedModel> model)
{
var entities = model.Select(m => new RequestBlobs { Type = m.Type, Content = ByteConverterHelper.ReturnBytes(m), ProviderId = m.ProviderId, Id = m.Id }).ToList();
return Repo.DeleteAll(entities);
}
public async Task<bool> BatchDeleteAsync(IEnumerable<RequestedModel> model)
{
var entities = model.Select(m => new RequestBlobs { Type = m.Type, Content = ByteConverterHelper.ReturnBytes(m), ProviderId = m.ProviderId, Id = m.Id }).ToList();
return await Repo.DeleteAllAsync(entities).ConfigureAwait(false);
}
}
}

View file

@ -0,0 +1,62 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: IssuesModel.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.Collections.Generic;
using Ombi.Store;
namespace Ombi.Core.Models
{
public class IssuesModel : Entity
{
public IssuesModel()
{
Issues = new List<IssueModel>();
}
public string Title { get; set; }
public string PosterUrl { get; set; }
public int RequestId { get; set; }
public List<IssueModel> Issues { get; set; }
public bool Deleted { get; set; }
public RequestType Type { get; set; }
public IssueStatus IssueStatus { get; set; }
public int ProviderId { get; set; }
}
public class IssueModel
{
public string UserNote { get; set; }
public string UserReported { get; set; }
public IssueState Issue { get; set; }
public string AdminNote { get; set; }
}
public enum IssueStatus
{
PendingIssue,
ResolvedIssue
}
}

View file

@ -0,0 +1,40 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: NotificationType.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.Core.Models
{
public enum NotificationType
{
NewRequest,
Issue,
RequestAvailable,
RequestApproved,
AdminNote,
Test,
RequestDeclined,
ItemAddedToFaultQueue
}
}

View file

@ -0,0 +1,43 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2017 Jamie Rees
// File: RecentUpdatesModel.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.Core.Models
{
public class RecentUpdatesModel
{
public string Version { get; set; }
public string Message { get; set; }
public bool Installed { get; set; }
public DateTime Date { get; set; }
public string Branch { get; set; }
}
}

View file

@ -0,0 +1,39 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: StatusModel.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.Core.Models
{
public class StatusModel
{
public string CurrentVersion { get; set; }
public string NewVersion { get; set; }
public bool UpdateAvailable { get; set; }
public string UpdateUri { get; set; }
public string DownloadUri { get; set; }
public string ReleaseNotes { get; set; }
public string ReleaseTitle { get; set; }
}
}

View file

@ -0,0 +1,34 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: UserProperties.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.Core.Models
{
public class UserProperties
{
public string EmailAddress { get; set; }
public string UserAlias { get; set; }
}
}

View file

@ -0,0 +1,149 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: MovieSender.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using System.Linq;
using System.Threading.Tasks;
using NLog;
using Ombi.Api.Interfaces;
using Ombi.Core.SettingModels;
using Ombi.Helpers;
using Ombi.Store;
namespace Ombi.Core
{
public class MovieSender : IMovieSender
{
public MovieSender(ISettingsService<CouchPotatoSettings> cp, ISettingsService<WatcherSettings> watcher,
ICouchPotatoApi cpApi, IWatcherApi watcherApi, IRadarrApi radarrApi, ISettingsService<RadarrSettings> radarrSettings,
ICacheProvider cache)
{
CouchPotatoSettings = cp;
WatcherSettings = watcher;
CpApi = cpApi;
WatcherApi = watcherApi;
RadarrSettings = radarrSettings;
RadarrApi = radarrApi;
Cache = cache;
}
private ISettingsService<CouchPotatoSettings> CouchPotatoSettings { get; }
private ISettingsService<WatcherSettings> WatcherSettings { get; }
private ISettingsService<RadarrSettings> RadarrSettings { get; }
private IRadarrApi RadarrApi { get; }
private ICouchPotatoApi CpApi { get; }
private IWatcherApi WatcherApi { get; }
private ICacheProvider Cache { get; }
private static Logger Log = LogManager.GetCurrentClassLogger();
public async Task<MovieSenderResult> Send(RequestedModel model, string qualityId = "")
{
var cpSettings = await CouchPotatoSettings.GetSettingsAsync();
var watcherSettings = await WatcherSettings.GetSettingsAsync();
var radarrSettings = await RadarrSettings.GetSettingsAsync();
if (cpSettings.Enabled)
{
return SendToCp(model, cpSettings, string.IsNullOrEmpty(qualityId) ? cpSettings.ProfileId : qualityId);
}
if (watcherSettings.Enabled)
{
return SendToWatcher(model, watcherSettings);
}
if (radarrSettings.Enabled)
{
return SendToRadarr(model, radarrSettings, qualityId);
}
return new MovieSenderResult { Result = false, MovieSendingEnabled = false };
}
private MovieSenderResult SendToWatcher(RequestedModel model, WatcherSettings settings)
{
var result = WatcherApi.AddMovie(model.ImdbId, settings.ApiKey, settings.FullUri);
if (result.Error)
{
Log.Error(result.ErrorMessage);
return new MovieSenderResult { Result = false };
}
if (result.response)
{
return new MovieSenderResult { Result = true, MovieSendingEnabled = true };
}
Log.Error(result.message);
return new MovieSenderResult { Result = false, MovieSendingEnabled = true };
}
private MovieSenderResult SendToCp(RequestedModel model, CouchPotatoSettings settings, string qualityId)
{
var result = CpApi.AddMovie(model.ImdbId, settings.ApiKey, model.Title, settings.FullUri, qualityId);
return new MovieSenderResult { Result = result, MovieSendingEnabled = true };
}
private MovieSenderResult SendToRadarr(RequestedModel model, RadarrSettings settings, string qualityId)
{
var qualityProfile = 0;
if (!string.IsNullOrEmpty(qualityId)) // try to parse the passed in quality, otherwise use the settings default quality
{
int.TryParse(qualityId, out qualityProfile);
}
if (qualityProfile <= 0)
{
int.TryParse(settings.QualityProfile, out qualityProfile);
}
var rootFolderPath = model.RootFolderSelected <= 0 ? settings.FullRootPath : GetRootPath(model.RootFolderSelected, settings);
var result = RadarrApi.AddMovie(model.ProviderId, model.Title, model.ReleaseDate.Year, qualityProfile, rootFolderPath, settings.ApiKey, settings.FullUri, true);
if (!string.IsNullOrEmpty(result.Error?.message))
{
Log.Error(result.Error.message);
return new MovieSenderResult { Result = false, Error = true , MovieSendingEnabled = true};
}
if (!string.IsNullOrEmpty(result.title))
{
return new MovieSenderResult { Result = true, MovieSendingEnabled = true };
}
return new MovieSenderResult { Result = false, MovieSendingEnabled = true };
}
private string GetRootPath(int pathId, RadarrSettings sonarrSettings)
{
var rootFoldersResult = Cache.GetOrSet(CacheKeys.RadarrRootFolders, () => RadarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri));
foreach (var r in rootFoldersResult.Where(r => r.id == pathId))
{
return r.path;
}
return string.Empty;
}
}
}

View file

@ -0,0 +1,42 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: MovieSenderResult.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.Core
{
public class MovieSenderResult
{
public bool Result { get; set; }
/// <summary>
/// Gets or sets a value indicating whether we can send to either CP or Watcher.
/// </summary>
/// <value>
/// <c>true</c> if [movie sending enabled]; otherwise, <c>false</c>.
/// </value>
public bool MovieSendingEnabled { get; set; }
public bool Error { get; set; }
}
}

View file

@ -0,0 +1,38 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: NotificationMessage.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 Ombi.Core.Models;
namespace Ombi.Core.Notification
{
public class NotificationMessage
{
public NotificationType NotificationType { get; set; }
public string Body { get; set; }
public string Subject { get; set; }
}
}

View file

@ -0,0 +1,34 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: NotificationMessageContent.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.Core.Notification
{
public class NotificationMessageContent
{
public string Subject { get; set; }
public string Body { get; set; }
}
}

View file

@ -0,0 +1,57 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: NotificationMessageCurlys.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.Collections.Generic;
namespace Ombi.Core.Notification
{
public class NotificationMessageCurlys
{
public NotificationMessageCurlys(string username, string title, string dateTime, string type, string issue)
{
Username = username;
Title = title;
Date = dateTime;
Type = type;
Issue = issue;
}
private string Username { get; }
private string Title { get; }
private string Date { get; }
private string Type { get; }
private string Issue { get; }
public Dictionary<string, string> Curlys => new Dictionary<string, string>
{
{nameof(Username), Username },
{nameof(Title), Title },
{nameof(Date), Date },
{nameof(Type), Type },
{nameof(Issue), Issue }
};
}
}

View file

@ -0,0 +1,173 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: CustomNotificationService.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 Ombi.Core.Models;
using Ombi.Core.SettingModels;
namespace Ombi.Core.Notification
{
public class NotificationMessageResolver
{
/// <summary>
/// The start character '{'
/// </summary>
private const char StartChar = (char)123;
/// <summary>
/// The end character '}'
/// </summary>
private const char EndChar = (char)125;
/// <summary>
/// Parses the message.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="notification">The notification.</param>
/// <param name="type">The type.</param>
/// <param name="c">The c.</param>
/// <param name="transportType">Type of the transport.</param>
/// <returns></returns>
public NotificationMessageContent ParseMessage(NotificationSettingsV2 notification, NotificationType type, NotificationMessageCurlys c, TransportType transportType)
{
IEnumerable<NotificationMessage> content = null;
switch (transportType)
{
case TransportType.Email:
content = notification.EmailNotification;
break;
case TransportType.Pushbullet:
content = notification.PushbulletNotification;
break;
case TransportType.Pushover:
content = notification.PushoverNotification;
break;
case TransportType.Slack:
content = notification.SlackNotification;
break;
default:
throw new ArgumentOutOfRangeException(nameof(transportType), transportType, null);
}
if (content == null)
{
return new NotificationMessageContent();
}
var message = content.FirstOrDefault(x => x.NotificationType == type) ?? new NotificationMessage();
return Resolve(message.Body, message.Subject, c.Curlys);
}
/// <summary>
/// Resolves the specified message curly fields.
/// </summary>
/// <param name="body">The body.</param>
/// <param name="subject">The subject.</param>
/// <param name="parameters">The parameters.</param>
/// <returns></returns>
private NotificationMessageContent Resolve(string body, string subject, IReadOnlyDictionary<string, string> parameters)
{
// Find the fields
var bodyFields = FindCurlyFields(body);
var subjectFields = FindCurlyFields(subject);
body = ReplaceFields(bodyFields, parameters, body);
subject = ReplaceFields(subjectFields, parameters, subject);
return new NotificationMessageContent {Body = body ?? string.Empty, Subject = subject ?? string.Empty};
}
/// <summary>
/// Finds the curly fields.
/// </summary>
/// <param name="message">The message.</param>
/// <returns></returns>
private IEnumerable<string> FindCurlyFields(string message)
{
if (string.IsNullOrEmpty(message))
{
return new List<string>();
}
var insideCurly = false;
var fields = new List<string>();
var currentWord = string.Empty;
var chars = message.ToCharArray();
foreach (var c in chars)
{
if (char.IsWhiteSpace(c))
{
currentWord = string.Empty;
continue;
}
if (c == StartChar) // Start of curly '{'
{
insideCurly = true;
continue;
}
if (c == EndChar) // End of curly '}'
{
fields.Add(currentWord); // We have finished the curly, add the word into the list
currentWord = string.Empty;
insideCurly = false;
continue;
}
if (insideCurly)
{
currentWord += c.ToString(); // Add the character onto the word.
}
}
return fields;
}
/// <summary>
/// Replaces the fields.
/// </summary>
/// <param name="fields">The fields.</param>
/// <param name="parameters">The parameters.</param>
/// <param name="mainText">The main text.</param>
/// <returns></returns>
private string ReplaceFields(IEnumerable<string> fields, IReadOnlyDictionary<string, string> parameters, string mainText)
{
foreach (var field in fields)
{
string outString;
if (parameters.TryGetValue(field, out outString))
{
mainText = mainText.Replace($"{{{field}}}", outString);
}
}
return mainText;
}
}
}

View file

@ -0,0 +1,189 @@
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Ombi</title>
<style media="all" type="text/css">
@media all {
.btn-primary table td:hover {
background-color: #34495e !important;
}
.btn-primary a:hover {
background-color: #34495e !important;
border-color: #34495e !important;
}
}
@media all {
.btn-secondary a:hover {
border-color: #34495e !important;
color: #34495e !important;
}
}
@media only screen and (max-width: 620px) {
table[class=body] h1 {
font-size: 28px !important;
margin-bottom: 10px !important;
}
table[class=body] h2 {
font-size: 22px !important;
margin-bottom: 10px !important;
}
table[class=body] h3 {
font-size: 16px !important;
margin-bottom: 10px !important;
}
table[class=body] p,
table[class=body] ul,
table[class=body] ol,
table[class=body] td,
table[class=body] span,
table[class=body] a {
font-size: 16px !important;
}
table[class=body] .wrapper,
table[class=body] .article {
padding: 10px !important;
}
table[class=body] .content {
padding: 0 !important;
}
table[class=body] .container {
padding: 0 !important;
width: 100% !important;
}
table[class=body] .header {
margin-bottom: 10px !important;
}
table[class=body] .main {
border-left-width: 0 !important;
border-radius: 0 !important;
border-right-width: 0 !important;
}
table[class=body] .btn table {
width: 100% !important;
}
table[class=body] .btn a {
width: 100% !important;
}
table[class=body] .img-responsive {
height: auto !important;
max-width: 100% !important;
width: auto !important;
}
table[class=body] .alert td {
border-radius: 0 !important;
padding: 10px !important;
}
table[class=body] .span-2,
table[class=body] .span-3 {
max-width: none !important;
width: 100% !important;
}
table[class=body] .receipt {
width: 100% !important;
}
}
@media all {
.ExternalClass {
width: 100%;
}
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%;
}
.apple-link a {
color: inherit !important;
font-family: inherit !important;
font-size: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
text-decoration: none !important;
}
}
</style>
</head>
<body class="" style="font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; background-color: #f6f6f6; margin: 0; padding: 0;">
<table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background-color: #f6f6f6;" width="100%" bgcolor="#f6f6f6">
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
<td class="container" style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; Margin: 0 auto !important; max-width: 580px; padding: 10px; width: 580px;" width="580" valign="top">
<div class="content" style="box-sizing: border-box; display: block; Margin: 0 auto; max-width: 580px; padding: 10px;">
<!-- START CENTERED WHITE CONTAINER -->
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">Ombi</span>
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #fff; border-radius: 3px;" width="100%">
<!-- START MAIN CONTENT AREA -->
<tr>
<td class="wrapper" style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;" valign="top">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
<tr>
<td align="center">
<img src="http://i.imgur.com/qQsN78U.png" width="400px" text-align="center" />
</td>
</tr>
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">Hi there!</p>
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">{@SUBJECT}</p>
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">{@BODY}</p>
</td>
</tr>
<tr>
<td align="center">
<img src="{@IMGSRC}" width="400px" text-align="center" />
</td>
</tr>
</table>
</td>
</tr>
<!-- END MAIN CONTENT AREA -->
</table>
<!-- START FOOTER -->
<div class="footer" style="clear: both; padding-top: 10px; text-align: center; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
<tr>
<td class="content-block powered-by" style="font-family: sans-serif; vertical-align: top; padding-top: 10px; padding-bottom: 10px; font-size: 12px; color: #999999; text-align: center;" valign="top" align="center">
Powered by <a href="https://github.com/tidusjar/Ombi" style="color: #999999; font-size: 12px; text-align: center; text-decoration: underline;">Ombi</a>
</td>
</tr>
</table>
</div>
<!-- END FOOTER -->
<!-- END CENTERED WHITE CONTAINER -->
</div>
</td>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
</tr>
</table>
</body>
</html>

View file

@ -0,0 +1,65 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: EmailBasicTemplate.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.IO;
using System.Text;
using System.Windows.Forms;
using NLog;
namespace Ombi.Core.Notification.Templates
{
public class EmailBasicTemplate : IEmailBasicTemplate
{
public string TemplateLocation => Path.Combine(Path.GetDirectoryName(Application.ExecutablePath) ?? string.Empty, "Notification", "Templates", "BasicRequestTemplate.html");
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private const string SubjectKey = "{@SUBJECT}";
private const string BodyKey = "{@BODY}";
private const string ImgSrc = "{@IMGSRC}";
private const string DateKey = "{@DATENOW}";
public string LoadTemplate(string subject, string body, string imgSrc)
{
try
{
var sb = new StringBuilder(File.ReadAllText(TemplateLocation));
sb.Replace(SubjectKey, subject);
sb.Replace(BodyKey, body);
sb.Replace(ImgSrc, imgSrc);
sb.Replace(DateKey, DateTime.Now.ToString("f"));
return sb.ToString();
}
catch (Exception e)
{
Log.Error(e);
return string.Empty;
}
}
}
}

View file

@ -0,0 +1,35 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: IEmailBasicTemplate.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.Core.Notification.Templates
{
public interface IEmailBasicTemplate
{
string LoadTemplate(string subject, string body, string imgSrc);
string TemplateLocation { get; }
}
}

View file

@ -0,0 +1,36 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: TransportType.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.Core.Notification
{
public enum TransportType
{
Email,
Pushbullet,
Pushover,
Slack
}
}

View file

@ -0,0 +1,213 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Ombi.Core</RootNamespace>
<AssemblyName>Ombi.Core</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Common.Logging, Version=3.0.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e, processorArchitecture=MSIL">
<HintPath>..\packages\Common.Logging.3.0.0\lib\net40\Common.Logging.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Common.Logging.Core, Version=3.0.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e, processorArchitecture=MSIL">
<HintPath>..\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Dapper, Version=1.50.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Dapper.1.50.0-beta8\lib\net45\Dapper.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Data.Sqlite">
<HintPath>..\Assemblies\Mono.Data.Sqlite.dll</HintPath>
</Reference>
<Reference Include="Nancy.Linker, Version=0.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Nancy.Linker.0.3.1\lib\net40-Client\Nancy.Linker.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.3.6\lib\net45\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Quartz, Version=2.3.3.0, Culture=neutral, PublicKeyToken=f6b8c98a402cc8a4, processorArchitecture=MSIL">
<HintPath>..\packages\Quartz.2.3.3\lib\net40\Quartz.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="RestSharp, Version=105.2.3.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RestSharp.105.2.3\lib\net45\RestSharp.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="TMDbLib">
<HintPath>..\packages\TMDbLib.0.9.0.0-alpha\lib\net45\TMDbLib.dll</HintPath>
</Reference>
<Reference Include="Nancy, Version=1.4.2.0, Culture=neutral, PublicKeyToken=null">
<HintPath>..\packages\Nancy.1.4.3\lib\net40\Nancy.dll</HintPath>
</Reference>
<Reference Include="Nancy.Authentication.Forms, Version=1.4.1.0, Culture=neutral, PublicKeyToken=null">
<HintPath>..\packages\Nancy.Authentication.Forms.1.4.1\lib\net40\Nancy.Authentication.Forms.dll</HintPath>
</Reference>
<Reference Include="Octokit, Version=0.19.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>..\packages\Octokit.0.19.0\lib\net45\Octokit.dll</HintPath>
</Reference>
<Reference Include="Omu.ValueInjecter, Version=3.1.1.0, Culture=neutral, PublicKeyToken=c7694541b0ac80e4">
<HintPath>..\packages\valueinjecter.3.1.1.2\lib\net40\Omu.ValueInjecter.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CacheKeys.cs" />
<Compile Include="HeadphonesSender.cs" />
<Compile Include="IMovieSender.cs" />
<Compile Include="IPlexReadOnlyDatabase.cs" />
<Compile Include="ISecurityExtensions.cs" />
<Compile Include="IStatusChecker.cs" />
<Compile Include="Models\RecentUpdatesModel.cs" />
<Compile Include="MovieSender.cs" />
<Compile Include="MovieSenderResult.cs" />
<Compile Include="Notification\NotificationMessage.cs" />
<Compile Include="Notification\NotificationMessageContent.cs" />
<Compile Include="Notification\NotificationMessageCurlys.cs" />
<Compile Include="Notification\NotificationMessageResolver.cs" />
<Compile Include="IIssueService.cs" />
<Compile Include="IRequestService.cs" />
<Compile Include="ISettingsService.cs" />
<Compile Include="JsonIssuesModelRequestService.cs" />
<Compile Include="JsonRequestModelRequestService.cs" />
<Compile Include="Models\IssuesModel.cs" />
<Compile Include="Models\NotificationType.cs" />
<Compile Include="Models\StatusModel.cs" />
<Compile Include="Models\UserProperties.cs" />
<Compile Include="Notification\Templates\EmailBasicTemplate.cs" />
<Compile Include="Notification\Templates\IEmailBasicTemplate.cs" />
<Compile Include="Notification\TransportType.cs" />
<Compile Include="PlexReadOnlyDatabase.cs" />
<Compile Include="Queue\ITransientFaultQueue.cs" />
<Compile Include="Queue\TransientFaultQueue.cs" />
<Compile Include="SecurityExtensions.cs" />
<Compile Include="SettingModels\AuthenticationSettings.cs" />
<Compile Include="SettingModels\DiscordNotificationSettings.cs" />
<Compile Include="SettingModels\EmbySettings.cs" />
<Compile Include="SettingModels\MassEmailSettings.cs" />
<Compile Include="SettingModels\RadarrSettings.cs" />
<Compile Include="SettingModels\WatcherSettings.cs" />
<Compile Include="SettingModels\ExternalSettings.cs" />
<Compile Include="SettingModels\HeadphonesSettings.cs" />
<Compile Include="SettingModels\LandingPageSettings.cs" />
<Compile Include="SettingModels\CustomizationSettings.cs" />
<Compile Include="SettingModels\NewsletterSettings.cs" />
<Compile Include="SettingModels\NotificationSettings.cs" />
<Compile Include="SettingModels\NotificationSettingsV2.cs" />
<Compile Include="SettingModels\RequestSettings.cs" />
<Compile Include="SettingModels\ScheduledJobsSettings.cs" />
<Compile Include="SettingModels\SlackNotificationSettings.cs" />
<Compile Include="SettingModels\PushoverNotificationSettings.cs" />
<Compile Include="SettingModels\PushBulletNotificationSettings.cs" />
<Compile Include="SettingModels\EmailNotificationSettings.cs" />
<Compile Include="SettingModels\PlexSettings.cs" />
<Compile Include="SettingModels\LogSettings.cs" />
<Compile Include="SettingModels\SonarrSettings.cs" />
<Compile Include="SettingModels\SickRageSettings.cs" />
<Compile Include="SettingModels\CouchPotatoSettings.cs" />
<Compile Include="SettingModels\PlexRequestSettings.cs" />
<Compile Include="SettingModels\Settings.cs" />
<Compile Include="SettingModels\SystemSettings.cs" />
<Compile Include="SettingModels\UserManagementSettings.cs" />
<Compile Include="SettingsServiceV2.cs" />
<Compile Include="Setup.cs" />
<Compile Include="StatusChecker\AppveyorArtifactResult.cs" />
<Compile Include="StatusChecker\StatusChecker.cs" />
<Compile Include="StatusChecker\AppveyorBranchResult.cs" />
<Compile Include="Tv\TvSender.cs" />
<Compile Include="Tv\TvSenderOld.cs" />
<Compile Include="Tv\TvSenderV2.cs" />
<Compile Include="Users\IUserHelper.cs" />
<Compile Include="Users\UserHelper.cs" />
<Compile Include="UserIdentity.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UserMapper.cs" />
<Compile Include="Users\UserHelperModel.cs" />
<Compile Include="Users\UserManagementHelper.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="job_scheduling_data_2_0.xsd">
<SubType>Designer</SubType>
</None>
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ombi.Api.Interfaces\Ombi.Api.Interfaces.csproj">
<Project>{95834072-A675-415D-AA8F-877C91623810}</Project>
<Name>Ombi.Api.Interfaces</Name>
</ProjectReference>
<ProjectReference Include="..\Ombi.Api.Models\Ombi.Api.Models.csproj">
<Project>{CB37A5F8-6DFC-4554-99D3-A42B502E4591}</Project>
<Name>Ombi.Api.Models</Name>
</ProjectReference>
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj">
<Project>{8CB8D235-2674-442D-9C6A-35FCAEEB160D}</Project>
<Name>Ombi.Api</Name>
</ProjectReference>
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj">
<Project>{1252336D-42A3-482A-804C-836E60173DFA}</Project>
<Name>Ombi.Helpers</Name>
</ProjectReference>
<ProjectReference Include="..\Ombi.Store\Ombi.Store.csproj">
<Project>{92433867-2B7B-477B-A566-96C382427525}</Project>
<Name>Ombi.Store</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="Notification\Templates\BasicRequestTemplate.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View file

@ -0,0 +1,82 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: PlexReadOnlyDatabase.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.IO;
using System.Linq;
using Ombi.Core.SettingModels;
using Ombi.Store;
using Ombi.Store.Models.Plex;
namespace Ombi.Core
{
public class PlexReadOnlyDatabase : IPlexReadOnlyDatabase
{
public PlexReadOnlyDatabase(IPlexDatabase plexDatabase, ISettingsService<PlexSettings> plexSettings)
{
Plex = plexDatabase;
var settings = plexSettings.GetSettings();
if (!string.IsNullOrEmpty(settings.PlexDatabaseLocationOverride))
{
//Overriden setting
Plex.DbLocation = Path.Combine(settings.PlexDatabaseLocationOverride, "Plug-in Support", "Databases", "com.plexapp.plugins.library.db");
}
else if (Type.GetType("Mono.Runtime") != null)
{
// Mono
Plex.DbLocation = Path.Combine("/var/lib/plexmediaserver/Library/Application Support/", "Plex Media Server", "Plug-in Support", "Databases", "com.plexapp.plugins.library.db");
}
else
{
// Default Windows
Plex.DbLocation = Path.Combine(Environment.ExpandEnvironmentVariables("%LOCALAPPDATA%"), "Plex Media Server", "Plug-in Support", "Databases", "com.plexapp.plugins.library.db");
}
}
private IPlexDatabase Plex { get; }
public IEnumerable<MetadataItems> GetItemsAddedAfterDate(DateTime dateTime)
{
// type 1 = Movie, type 4 = TV Episode
var movies = Plex.QueryMetadataItems(@"SELECT * FROM metadata_items
WHERE added_at > @AddedAt
AND metadata_type = 1
AND title <> ''", new { AddedAt = dateTime });
// Custom query to include the series title
var tv = Plex.QueryMetadataItems(@"SELECT series.title AS SeriesTitle, mi.* FROM metadata_items mi
INNER JOIN metadata_items season ON mi.parent_id = season.id
INNER JOIN metadata_items series ON series.id = season.parent_id
WHERE mi.added_at > @AddedAt
AND mi.metadata_type = 4", new { AddedAt = dateTime });
return movies.Union(tv);
}
}
}

View file

@ -0,0 +1,37 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Ombi.Core")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Ombi.Core")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("dd7dc444-d3bf-4027-8ab9-efc71f5ec581")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersionAttribute("1.0.0.0")]

View file

@ -0,0 +1,17 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Ombi.Store;
using Ombi.Store.Models;
namespace Ombi.Core.Queue
{
public interface ITransientFaultQueue
{
void Dequeue();
Task DequeueAsync();
IEnumerable<RequestQueue> GetQueue();
Task<IEnumerable<RequestQueue>> GetQueueAsync();
void QueueItem(RequestedModel request, string id, RequestType type, FaultType faultType);
Task QueueItemAsync(RequestedModel request, string id, RequestType type, FaultType faultType, string message = null);
}
}

View file

@ -0,0 +1,130 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: TransientFaultQueue.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.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Dapper;
using Ombi.Helpers;
using Ombi.Store;
using Ombi.Store.Models;
using Ombi.Store.Repository;
namespace Ombi.Core.Queue
{
public class TransientFaultQueue : ITransientFaultQueue
{
public TransientFaultQueue(IRepository<RequestQueue> queue)
{
RequestQueue = queue;
}
private IRepository<RequestQueue> RequestQueue { get; }
public void QueueItem(RequestedModel request, string id, RequestType type, FaultType faultType)
{
//Ensure there is not a duplicate queued item
var existingItem = RequestQueue.Custom(
connection =>
{
connection.Open();
var result = connection.Query<RequestQueue>("select * from RequestQueue where PrimaryIdentifier = @ProviderId", new { ProviderId = id });
return result;
}).FirstOrDefault();
if (existingItem != null)
{
// It's already in the queue
return;
}
var queue = new RequestQueue
{
Type = type,
Content = ByteConverterHelper.ReturnBytes(request),
PrimaryIdentifier = id,
FaultType = faultType
};
RequestQueue.Insert(queue);
}
public async Task QueueItemAsync(RequestedModel request, string id, RequestType type, FaultType faultType, string description = null)
{
//Ensure there is not a duplicate queued item
var existingItem = await RequestQueue.CustomAsync(async connection =>
{
connection.Open();
var result = await connection.QueryAsync<RequestQueue>("select * from RequestFaultQueue where PrimaryIdentifier = @ProviderId", new { ProviderId = id });
return result;
});
if (existingItem.FirstOrDefault() != null)
{
// It's already in the queue
return;
}
var queue = new RequestQueue
{
Type = type,
Content = ByteConverterHelper.ReturnBytes(request),
PrimaryIdentifier = id,
FaultType = faultType,
Description = description ?? string.Empty
};
await RequestQueue.InsertAsync(queue);
}
public IEnumerable<RequestQueue> GetQueue()
{
var items = RequestQueue.GetAll();
return items;
}
public async Task<IEnumerable<RequestQueue>> GetQueueAsync()
{
var items = RequestQueue.GetAllAsync();
return await items;
}
public void Dequeue()
{
RequestQueue.DeleteAll("RequestQueue");
}
public async Task DequeueAsync()
{
await RequestQueue.DeleteAllAsync("RequestQueue");
}
}
}

View file

@ -0,0 +1,327 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: SecurityExtensions.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 Nancy;
using Nancy.Linker;
using Nancy.Responses;
using Nancy.Security;
using Nancy.Session;
using Ombi.Core.Models;
using Ombi.Core.SettingModels;
using Ombi.Core.Users;
using Ombi.Helpers;
using Ombi.Helpers.Permissions;
using Ombi.Store.Models.Emby;
using Ombi.Store.Models.Plex;
using Ombi.Store.Repository;
namespace Ombi.Core
{
public class SecurityExtensions : ISecurityExtensions
{
public SecurityExtensions(IUserRepository userRepository, IResourceLinker linker, IExternalUserRepository<PlexUsers> plexUsers, ISettingsService<UserManagementSettings> umSettings,
IExternalUserRepository<EmbyUsers> embyUsers)
{
UserRepository = userRepository;
Linker = linker;
PlexUsers = plexUsers;
UserManagementSettings = umSettings;
EmbyUsers = embyUsers;
}
private IUserRepository UserRepository { get; }
private IResourceLinker Linker { get; }
private IExternalUserRepository<PlexUsers> PlexUsers { get; }
private IExternalUserRepository<EmbyUsers> EmbyUsers { get; }
private ISettingsService<UserManagementSettings> UserManagementSettings { get; }
public bool IsLoggedIn(NancyContext context)
{
var userName = context.Request.Session[SessionKeys.UsernameKey];
var realUser = false;
var plexUser = userName != null;
if (context.CurrentUser?.IsAuthenticated() ?? false)
{
realUser = true;
}
return realUser || plexUser;
}
public bool IsExternalUser(IUserIdentity user)
{
if (user == null)
{
return false;
}
var plexUser = PlexUsers.GetUserByUsername(user.UserName);
var embyUser = EmbyUsers.GetUserByUsername(user.UserName);
return plexUser != null || embyUser != null;
}
public bool IsNormalUser(IUserIdentity user)
{
if (user == null)
{
return false;
}
var dbUser = UserRepository.GetUserByUsername(user.UserName);
return dbUser != null;
}
/// <summary>
/// Gets the username this could be the alias! We should always use this method when getting the username
/// </summary>
/// <param name="username">The username.</param>
/// <param name="session"></param>
/// <returns>
/// <c>null</c> if we cannot find a user
/// </returns>
public string GetUsername(string username, ISession session)
{
var plexUser = PlexUsers.GetUserByUsername(username);
if (plexUser != null)
{
return !string.IsNullOrEmpty(plexUser.UserAlias) ? plexUser.UserAlias : plexUser.Username;
}
var embyUser = EmbyUsers.GetUserByUsername(username);
if (embyUser != null)
{
return !string.IsNullOrEmpty(embyUser.UserAlias) ? embyUser.UserAlias : embyUser.Username;
}
var dbUser = UserRepository.GetUserByUsername(username);
if (dbUser != null)
{
var userProps = ByteConverterHelper.ReturnObject<UserProperties>(dbUser.UserProperties);
return !string.IsNullOrEmpty(userProps.UserAlias) ? userProps.UserAlias : dbUser.UserName;
}
// could be a local user
var hasSessionKey = session[SessionKeys.UsernameKey] != null;
if (hasSessionKey)
{
return (string)session[SessionKeys.UsernameKey];
}
return string.Empty;
}
/// <summary>
/// Creates a hook to be used in a pipeline before a route handler to ensure
/// that the request was made by an authenticated user does not have the claims.
/// </summary>
/// <param name="perm">Claims the authenticated user needs to have</param>
/// <returns>Hook that returns an Unauthorized response if the user is not
/// authenticated or does have the claims, null otherwise</returns>
private Func<NancyContext, Response> DoesNotHavePermissions(int perm)
{
return ForbiddenIfNot(ctx =>
{
var permissions = GetPermissions(ctx.CurrentUser);
var result = permissions.HasFlag((Permissions)perm);
return !result;
});
}
public bool DoesNotHavePermissions(int perm, IUserIdentity currentUser)
{
return DoesNotHavePermissions((Permissions)perm, currentUser);
}
public bool DoesNotHavePermissions(Permissions perm, IUserIdentity currentUser)
{
var permissions = GetPermissions(currentUser);
var result = permissions.HasFlag(perm);
return !result;
}
public bool HasPermissions(IUserIdentity user, Permissions perm)
{
var permissions = GetPermissions(user);
return permissions.HasFlag(perm);
}
public bool HasPermissions(string userName, Permissions perm)
{
var permissions = GetPermissions(userName);
return permissions.HasFlag(perm);
}
public bool HasAnyPermissions(IUserIdentity user, params Permissions[] perm)
{
var permissions = GetPermissions(user);
foreach (var p in perm)
{
var result = permissions.HasFlag(p);
if (result)
{
return true;
}
}
return false;
}
public Response HasPermissionsRedirect(Permissions perm, NancyContext context, string routeName, HttpStatusCode code)
{
var url = Linker.BuildRelativeUri(context, routeName);
var response = ForbiddenIfNot(ctx =>
{
var permissions = GetPermissions(ctx.CurrentUser);
var result = permissions.HasFlag(perm);
return result;
});
var r = response(context);
return r.StatusCode == code
? new RedirectResponse($"{url.ToString()}?redirect={context.Request.Path}")
: null;
}
public Response HasAnyPermissionsRedirect(NancyContext context, string routeName, HttpStatusCode code, params Permissions[] perm)
{
var url = Linker.BuildRelativeUri(context, routeName);
var response = ForbiddenIfNot(ctx =>
{
var permissions = GetPermissions(ctx.CurrentUser);
var hasPermission = false;
foreach (var p in perm)
{
var result = permissions.HasFlag(p);
if (result)
{
hasPermission = true;
}
}
return hasPermission;
});
var r = response(context);
return r.StatusCode == code
? new RedirectResponse(url.ToString())
: null;
}
public Response AdminLoginRedirect(Permissions perm, NancyContext context)
{
// This will redirect us to the Login Page if we don't have the correct permission passed in (e.g. Admin with Http 403 status code).
return HasPermissionsRedirect(perm, context, "LocalLogin", HttpStatusCode.Forbidden);
}
public Response AdminLoginRedirect(NancyContext context, params Permissions[] perm)
{
// This will redirect us to the Login Page if we don't have the correct permission passed in (e.g. Admin with Http 403 status code).
return HasAnyPermissionsRedirect(context, "LocalLogin", HttpStatusCode.Forbidden, perm);
}
// BELOW IS A COPY FROM THE SecurityHooks CLASS!
/// <summary>
/// Creates a hook to be used in a pipeline before a route handler to ensure that
/// the request satisfies a specific test.
/// </summary>
/// <param name="test">Test that must return true for the request to continue</param>
/// <returns>Hook that returns an Forbidden response if the test fails, null otherwise</returns>
public Func<NancyContext, Response> ForbiddenIfNot(Func<NancyContext, bool> test)
{
return HttpStatusCodeIfNot(HttpStatusCode.Forbidden, test);
}
/// <summary>
/// Creates a hook to be used in a pipeline before a route handler to ensure that
/// the request satisfies a specific test.
/// </summary>
/// <param name="statusCode">HttpStatusCode to use for the response</param>
/// <param name="test">Test that must return true for the request to continue</param>
/// <returns>Hook that returns a response with a specific HttpStatusCode if the test fails, null otherwise</returns>
public Func<NancyContext, Response> HttpStatusCodeIfNot(HttpStatusCode statusCode, Func<NancyContext, bool> test)
{
return ctx =>
{
Response response = new Response
{
StatusCode = HttpStatusCode.OK
};
if (!test(ctx))
{
response = new Response
{
StatusCode = statusCode
};
}
return response;
};
}
private Permissions GetPermissions(IUserIdentity user)
{
return GetPermissions(user?.UserName);
}
private Permissions GetPermissions(string userName)
{
if (string.IsNullOrEmpty(userName))
{
// Username without auth
var s = UserManagementSettings.GetSettings();
return (Permissions)UserManagementHelper.GetPermissions(s);
}
var dbUser = UserRepository.GetUserByUsername(userName);
if (dbUser != null)
{
var permissions = (Permissions)dbUser.Permissions;
return permissions;
}
var plexUser = PlexUsers.GetUserByUsername(userName);
if (plexUser != null)
{
var permissions = (Permissions)plexUser.Permissions;
return permissions;
}
var embyUsers = EmbyUsers.GetUserByUsername(userName);
if (embyUsers != null)
{
return (Permissions) embyUsers.Permissions;
}
return 0;
}
}
}

View file

@ -0,0 +1,70 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: AuthenticationSettings.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 Newtonsoft.Json;
namespace Ombi.Core.SettingModels
{
public sealed class AuthenticationSettings : Settings
{
public bool UserAuthentication { get; set; }
public bool UsePassword { get; set; }
[JsonProperty("PlexAuthToken")]
[Obsolete("This should be migrated over into the Plex Settings and then removed in the next release")]
public string OldPlexAuthToken { get; set; }
/// <summary>
/// A comma separated list of users.
/// </summary>
public string DeniedUsers { get; set; }
[JsonIgnore]
public List<string> DeniedUserList
{
get
{
var users = new List<string>();
if (string.IsNullOrEmpty(DeniedUsers))
{
return users;
}
var splitUsers = DeniedUsers.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var user in splitUsers)
{
if (!string.IsNullOrWhiteSpace(user))
users.Add(user.Trim());
}
return users;
}
}
}
}

View file

@ -0,0 +1,37 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: CouchPotatoSettings.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
namespace Ombi.Core.SettingModels
{
public sealed class CouchPotatoSettings : ExternalSettings
{
public bool Enabled { get; set; }
public string ApiKey { get; set; }
public string ProfileId { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
}

View file

@ -0,0 +1,60 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: NewsletterSettings.cs
// Created By: Jim MacKenzie
//
// 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.Core.SettingModels
{
public class CustomizationSettings : Settings
{
public string ApplicationName { get; set; }
/// <summary>
/// The CSS name of the theme we want
/// </summary>
public string ThemeName { get; set; }
/// <summary>
/// Admin Only.
/// </summary>
/// <value>
/// The default filter.
/// </value>
public int DefaultFilter { get; set; }
/// <summary>
/// Admin only.
/// </summary>
/// <value>
/// The default sort.
/// </value>
public int DefaultSort { get; set; }
public int DefaultLang { get; set; }
public bool NewSearch { get; set; }
public bool EnableIssues { get; set; }
public bool EnableNetflixResults { get; set; }
}
}

View file

@ -0,0 +1,31 @@
using System;
using Newtonsoft.Json;
namespace Ombi.Core.SettingModels
{
public sealed class DiscordNotificationSettings : NotificationSettings
{
public string WebhookUrl { get; set; }
public string Username { get; set; }
[JsonIgnore]
public string WebookId => SplitWebUrl(4);
[JsonIgnore]
public string Token => SplitWebUrl(5);
private string SplitWebUrl(int index)
{
if (!WebhookUrl.StartsWith("http", StringComparison.InvariantCulture))
{
WebhookUrl = "https://" + WebhookUrl;
}
var split = WebhookUrl.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
return split.Length < index
? string.Empty
: split[index];
}
}
}

View file

@ -0,0 +1,39 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: EmailNotificationSettings.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.Core.SettingModels
{
public sealed class EmailNotificationSettings : NotificationSettings
{
public string EmailHost { get; set; }
public string EmailPassword { get; set; }
public int EmailPort { get; set; }
public string EmailSender { get; set; }
public string EmailUsername { get; set; }
public bool Authentication { get; set; }
public string RecipientEmail { get; set; }
}
}

View file

@ -0,0 +1,37 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: CouchPotatoSettings.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
namespace Ombi.Core.SettingModels
{
public sealed class EmbySettings : ExternalSettings
{
public bool Enable { get; set; }
public string ApiKey { get; set; }
public string AdministratorId { get; set; }
public bool EnableEpisodeSearching { get; set; }
}
}

View file

@ -0,0 +1,56 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: ExternalSettings.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using Newtonsoft.Json;
using Ombi.Helpers;
namespace Ombi.Core.SettingModels
{
public abstract class ExternalSettings : Settings
{
public bool Ssl { get; set; }
public string SubDir { get; set; }
public string Ip { get; set; }
public int Port { get; set; }
[JsonIgnore]
public virtual Uri FullUri
{
get
{
if (!string.IsNullOrEmpty(SubDir))
{
var formattedSubDir = Ip.ReturnUriWithSubDir(Port, Ssl, SubDir);
return formattedSubDir;
}
var formatted = Ip.ReturnUri(Port, Ssl);
return formatted;
}
}
}
}

View file

@ -0,0 +1,34 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: CouchPotatoSettings.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
namespace Ombi.Core.SettingModels
{
public sealed class HeadphonesSettings : ExternalSettings
{
public bool Enabled { get; set; }
public string ApiKey { get; set; }
}
}

View file

@ -0,0 +1,46 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: LandingPageSettings.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using Newtonsoft.Json;
namespace Ombi.Core.SettingModels
{
public class LandingPageSettings : Settings
{
public bool Enabled { get; set; }
public bool BeforeLogin { get; set; }
public bool NoticeEnable { get; set; }
public string NoticeMessage { get; set; }
public bool EnabledNoticeTime { get; set; }
public DateTime NoticeStart { get; set; }
public DateTime NoticeEnd { get; set; }
[JsonIgnore]
public bool NoticeActive => DateTime.Now < NoticeEnd && DateTime.Now > NoticeStart;
}
}

View file

@ -0,0 +1,35 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: SickRageSettings.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.Core.SettingModels
{
public sealed class LogSettings : Settings
{
public int Level { get; set; }
}
}

View file

@ -0,0 +1,35 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: EmailNotificationSettings.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.Core.SettingModels
{
public sealed class MassEmailSettings : NotificationSettings
{
public string Users { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
}
}

View file

@ -0,0 +1,56 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: NewsletterSettings.cs
// Created By: Jim MacKenzie
//
// 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.Collections.Generic;
using Newtonsoft.Json;
using Ombi.Helpers;
namespace Ombi.Core.SettingModels
{
public class NewletterSettings : Settings
{
public bool SendRecentlyAddedEmail { get; set; }
public bool SendToPlexUsers { get; set; }
public string CustomUsers { get; set; }
[JsonIgnore]
public IEnumerable<string> CustomUsersEmailAddresses
{
get
{
var retVal = (IEnumerable<string>)new List<string>();
if (!string.IsNullOrEmpty(CustomUsers))
{
retVal = CustomUsers.SplitEmailsByDelimiter(';');
}
return retVal;
}
}
}
}

View file

@ -0,0 +1,58 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: NotificationSettings.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.Collections.Generic;
using Ombi.Core.Models;
using Ombi.Core.Notification;
namespace Ombi.Core.SettingModels
{
public class NotificationSettings : Settings
{
public NotificationSettings()
{
Message = new List<NotificationMessage>
{
new NotificationMessage { NotificationType = NotificationType.NewRequest },
new NotificationMessage { NotificationType = NotificationType.Issue },
new NotificationMessage { NotificationType = NotificationType.AdminNote },
new NotificationMessage { NotificationType = NotificationType.RequestApproved },
new NotificationMessage { NotificationType = NotificationType.RequestAvailable }
};
}
public bool Enabled { get; set; }
public List<NotificationMessage> Message { get; set; }
}
public static class NotificationCurly
{
public static readonly List<string> Curlys = new List<string> { "Username", "Title", "Date", "Issue", "Type" };
}
}

View file

@ -0,0 +1,62 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: NotificationSettingsV2.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.Collections.Generic;
using Ombi.Core.Models;
using Ombi.Core.Notification;
namespace Ombi.Core.SettingModels
{
public class NotificationSettingsV2 : Settings
{
public NotificationSettingsV2()
{
EmailNotification = new List<NotificationMessage>
{
new NotificationMessage
{
Body = "BODY",
NotificationType = NotificationType.NewRequest,
Subject = "SUB"
},
new NotificationMessage
{
NotificationType = NotificationType.Issue,
Body = "issue",
Subject = "issuesub"
}
};
SlackNotification = new List<NotificationMessage>();
PushoverNotification = new List<NotificationMessage>();
PushbulletNotification = new List<NotificationMessage>();
}
public List<NotificationMessage> EmailNotification { get; set; }
public List<NotificationMessage> SlackNotification { get; set; }
public List<NotificationMessage> PushbulletNotification { get; set; }
public List<NotificationMessage> PushoverNotification { get; set; }
}
}

View file

@ -0,0 +1,78 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: PlexRequestSettings.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.Core.SettingModels
{
public sealed class PlexRequestSettings : Settings
{
public PlexRequestSettings()
{
TvWeeklyRequestLimit = 0;
MovieWeeklyRequestLimit = 0;
AlbumWeeklyRequestLimit = 0;
}
public int Port { get; set; }
public string BaseUrl { get; set; }
public bool SearchForMovies { get; set; }
public bool SearchForActors { get; set; }
public bool SearchForTvShows { get; set; }
public bool SearchForMusic { get; set; }
[Obsolete("Use the user management settings")]
public bool RequireMovieApproval { get; set; }
[Obsolete("Use the user management settings")]
public bool RequireTvShowApproval { get; set; }
[Obsolete("Use the user management settings")]
public bool RequireMusicApproval { get; set; }
[Obsolete("Use the user management settings")]
public bool UsersCanViewOnlyOwnRequests { get; set; }
[Obsolete("Use the user management settings")]
public bool UsersCanViewOnlyOwnIssues { get; set; }
public int MovieWeeklyRequestLimit { get; set; }
public int TvWeeklyRequestLimit { get; set; }
public int AlbumWeeklyRequestLimit { get; set; }
public bool CollectAnalyticData { get; set; }
public bool IgnoreNotifyForAutoApprovedRequests { get; set; }
public bool Wizard { get; set; }
public bool DisableTvRequestsByEpisode { get; set; }
public bool DisableTvRequestsBySeason { get; set; }
[Obsolete("Moved to NewsLetterSettings")]
public bool SendRecentlyAddedEmail { get; set; }
public string CustomDonationUrl { get; set; }
public bool EnableCustomDonationUrl { get; set; }
public string CustomDonationMessage { get; set; }
/// <summary>
/// The CSS name of the theme we want
/// </summary>
public string ThemeName { get; set; }
public string ApiKey { get; set; }
}
}

View file

@ -0,0 +1,45 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: CouchPotatoSettings.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
namespace Ombi.Core.SettingModels
{
public sealed class PlexSettings : ExternalSettings
{
public PlexSettings()
{
AdvancedSearch = true;
}
public bool Enable { get; set; }
public bool AdvancedSearch { get; set; }
public bool EnableTvEpisodeSearching { get; set; }
public string PlexAuthToken { get; set; }
public string MachineIdentifier { get; set; }
public string PlexDatabaseLocationOverride { get; set; }
}
}

View file

@ -0,0 +1,34 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: PushBulletNotificationSettings.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.Core.SettingModels
{
public sealed class PushbulletNotificationSettings : NotificationSettings
{
public string AccessToken { get; set; }
public string DeviceIdentifier { get; set; }
}
}

View file

@ -0,0 +1,34 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: PushoverNotificationSettings.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.Core.SettingModels
{
public sealed class PushoverNotificationSettings : NotificationSettings
{
public string AccessToken { get; set; }
public string UserToken { get; set; }
}
}

View file

@ -0,0 +1,37 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: SonarrSettings.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.Core.SettingModels
{
public sealed class RadarrSettings : ExternalSettings
{
public bool Enabled { get; set; }
public string ApiKey { get; set; }
public string QualityProfile { get; set; }
public string RootPath { get; set; }
public string FullRootPath { get; set; }
}
}

View file

@ -0,0 +1,56 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: RequestSettings.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.Collections.Generic;
namespace Ombi.Core.SettingModels
{
public sealed class RequestSettings : Settings
{
public OrderType Order { get; set; }
public List<FilterType> Filters { get; set; }
}
public enum OrderType
{
NewRequests,
OldRequests,
NewReleases,
OldReleases
}
public enum FilterType
{
// ALL is not here, it's managed in the angular controller
Approved,
NotApproved,
Available,
NotAvailable,
Released,
NotReleased
}
}

View file

@ -0,0 +1,56 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: ScheduledJobsSettings.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.Core.SettingModels
{
public class ScheduledJobsSettings : Settings
{
public int PlexAvailabilityChecker { get; set; }
public int SickRageCacher { get; set; }
public int SonarrCacher { get; set; }
public int CouchPotatoCacher { get; set; }
public int WatcherCacher { get; set; }
public int StoreBackup { get; set; }
public int StoreCleanup { get; set; }
public int UserRequestLimitResetter { get; set; }
public int PlexEpisodeCacher { get; set; }
[Obsolete("We use the CRON job now")]
public int RecentlyAdded { get; set; }
public string RecentlyAddedCron { get; set; }
public int FaultQueueHandler { get; set; }
public int PlexContentCacher { get; set; }
public int PlexUserChecker { get; set; }
public int RadarrCacher { get; set; }
public int EmbyEpisodeCacher { get; set; }
public int EmbyContentCacher { get; set; }
public int EmbyAvailabilityChecker { get; set; }
public int EmbyUserChecker { get; set; }
}
}

View file

@ -0,0 +1,33 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: Settings.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.Core.SettingModels
{
public class Settings
{
public int Id { get; set; }
}
}

View file

@ -0,0 +1,51 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: SickRageSettings.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.Collections.Generic;
namespace Ombi.Core.SettingModels
{
public sealed class SickRageSettings : ExternalSettings
{
public bool Enabled { get; set; }
public string ApiKey { get; set; }
public string QualityProfile { get; set; }
public Dictionary<string, string> Qualities => new Dictionary<string, string>
{
{ "default", "Use Default" },
{ "sdtv", "SD TV" },
{ "sddvd", "SD DVD" },
{ "hdtv", "HD TV" },
{ "rawhdtv", "Raw HD TV" },
{ "hdwebdl", "HD Web DL" },
{ "fullhdwebdl", "Full HD Web DL" },
{ "hdbluray", "HD Bluray" },
{ "fullhdbluray", "Full HD Bluray" }
};
}
}

View file

@ -0,0 +1,35 @@
using System;
using Newtonsoft.Json;
namespace Ombi.Core.SettingModels
{
public sealed class SlackNotificationSettings : NotificationSettings
{
public string WebhookUrl { get; set; }
public string Channel { get; set; }
public string Username { get; set; }
[JsonIgnore]
public string Team => SplitWebUrl(3);
[JsonIgnore]
public string Service => SplitWebUrl(4);
[JsonIgnore]
public string Token => SplitWebUrl(5);
private string SplitWebUrl(int index)
{
if (!WebhookUrl.StartsWith("http", StringComparison.InvariantCulture))
{
WebhookUrl = "https://" + WebhookUrl;
}
var split = WebhookUrl.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
return split.Length < index
? string.Empty
: split[index];
}
}
}

View file

@ -0,0 +1,44 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: SonarrSettings.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.Core.SettingModels
{
public sealed class SonarrSettings : ExternalSettings
{
public bool Enabled { get; set; }
public string ApiKey { get; set; }
public string QualityProfile { get; set; }
public bool SeasonFolders { get; set; }
/// <summary>
/// This is the root path ID
/// </summary>
/// <value>
/// The root path.
/// </value>
public string RootPath { get; set; }
public string FullRootPath { get; set; }
}
}

View file

@ -0,0 +1,66 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: SystemSettings.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.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
using Ombi.Core.Models;
using Ombi.Helpers;
namespace Ombi.Core.SettingModels
{
public class SystemSettings : Settings
{
public Branches Branch { get; set; }
public string Version { get; set; }
public StatusModel Status { get; set; }
[JsonIgnore]
public List<BranchDropdown> BranchDropdown { get; set; }
}
public class BranchDropdown
{
public bool Selected { get; set; }
public string Name { get; set; }
public Branches Value { get; set; }
}
public enum Branches
{
[Branch(DisplayName= "Stable", BranchName = "master")]
Stable,
[Branch(DisplayName = "Early Access Preview", BranchName = "eap")]
EarlyAccessPreview,
[Branch(DisplayName = "Development", BranchName = "dev")]
Dev,
}
}

View file

@ -0,0 +1,47 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: UserManagementSettings.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.Core.SettingModels
{
public class UserManagementSettings : Settings
{
public bool RequestMovies { get; set; }
public bool RequestTvShows { get; set; }
public bool RequestMusic { get; set; }
public bool AutoApproveMovies { get; set; }
public bool AutoApproveTvShows { get; set; }
public bool AutoApproveMusic { get; set; }
public bool ReportIssues { get; set; }
public bool UsersCanViewOnlyOwnRequests { get; set; }
public bool UsersCanViewOnlyOwnIssues { get; set; }
public bool BypassRequestLimit { get; set; }
public bool ViewUsers { get; set; }
// Features
public bool RecentlyAddedNotification { get; set; }
public bool RecentlyAddedNewsletter { get; set; }
}
}

View file

@ -0,0 +1,34 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: WatcherSettings.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.Core.SettingModels
{
public sealed class WatcherSettings : ExternalSettings
{
public bool Enabled { get; set; }
public string ApiKey { get; set; }
}
}

View file

@ -0,0 +1,160 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: SettingsServiceV2.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.Threading.Tasks;
using Newtonsoft.Json;
using Ombi.Core.SettingModels;
using Ombi.Helpers;
using Ombi.Store.Models;
using Ombi.Store.Repository;
namespace Ombi.Core
{
public class SettingsServiceV2<T> : ISettingsService<T>
where T : Settings, new()
{
public SettingsServiceV2(ISettingsRepository repo)
{
Repo = repo;
EntityName = typeof(T).Name;
}
private ISettingsRepository Repo { get; }
private string EntityName { get; }
public T GetSettings()
{
var result = Repo.Get(EntityName);
if (result == null)
{
return new T();
}
result.Content = DecryptSettings(result);
var obj = string.IsNullOrEmpty(result.Content) ? null : JsonConvert.DeserializeObject<T>(result.Content, SerializerSettings.Settings);
var model = obj;
return model;
}
public async Task<T> GetSettingsAsync()
{
var result = await Repo.GetAsync(EntityName).ConfigureAwait(false);
if (result == null)
{
return new T();
}
result.Content = DecryptSettings(result);
return string.IsNullOrEmpty(result.Content) ? null : JsonConvert.DeserializeObject<T>(result.Content, SerializerSettings.Settings);
}
public bool SaveSettings(T model)
{
var entity = Repo.Get(EntityName);
if (entity == null)
{
var newEntity = model;
var settings = new GlobalSettings { SettingsName = EntityName, Content = JsonConvert.SerializeObject(newEntity, SerializerSettings.Settings) };
settings.Content = EncryptSettings(settings);
var insertResult = Repo.Insert(settings);
return insertResult != long.MinValue;
}
var modified = model;
modified.Id = entity.Id;
var globalSettings = new GlobalSettings { SettingsName = EntityName, Content = JsonConvert.SerializeObject(modified, SerializerSettings.Settings), Id = entity.Id };
globalSettings.Content = EncryptSettings(globalSettings);
var result = Repo.Update(globalSettings);
return result;
}
public async Task<bool> SaveSettingsAsync(T model)
{
var entity = await Repo.GetAsync(EntityName);
if (entity == null)
{
var newEntity = model;
var settings = new GlobalSettings { SettingsName = EntityName, Content = JsonConvert.SerializeObject(newEntity, SerializerSettings.Settings) };
settings.Content = EncryptSettings(settings);
var insertResult = await Repo.InsertAsync(settings).ConfigureAwait(false);
return insertResult != int.MinValue;
}
var modified = model;
modified.Id = entity.Id;
var globalSettings = new GlobalSettings { SettingsName = EntityName, Content = JsonConvert.SerializeObject(modified, SerializerSettings.Settings), Id = entity.Id };
globalSettings.Content = EncryptSettings(globalSettings);
var result = await Repo.UpdateAsync(globalSettings).ConfigureAwait(false);
return result;
}
public bool Delete(T model)
{
var entity = Repo.Get(EntityName);
if (entity != null)
{
return Repo.Delete(entity);
}
// Entity does not exist so nothing to delete
return true;
}
public async Task<bool> DeleteAsync(T model)
{
var entity = Repo.Get(EntityName);
if (entity != null)
{
return await Repo.DeleteAsync(entity);
}
return true;
}
private string EncryptSettings(GlobalSettings settings)
{
return StringCipher.Encrypt(settings.Content, settings.SettingsName);
}
private string DecryptSettings(GlobalSettings settings)
{
return StringCipher.Decrypt(settings.Content, settings.SettingsName);
}
}
}

190
Old/Ombi.Core/Setup.cs Normal file
View file

@ -0,0 +1,190 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: Setup.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.Threading.Tasks;
using Mono.Data.Sqlite;
using NLog;
using Ombi.Api;
using Ombi.Core.SettingModels;
using Ombi.Helpers;
using Ombi.Store;
using Ombi.Store.Repository;
using Quartz;
namespace Ombi.Core
{
public class Setup
{
private static Logger Log = LogManager.GetCurrentClassLogger();
private static DbConfiguration Db { get; set; }
public string SetupDb(string urlBase)
{
Db = new DbConfiguration(new SqliteFactory());
var created = Db.CheckDb();
Db.DbConnection().CreateTables();
if (created)
{
CreateDefaultSettingsPage(urlBase);
}
else
{
// Shrink DB
Db.DbConnection().Vacuum();
}
// Add the new 'running' item into the scheduled jobs so we can check if the cachers are running
Db.DbConnection().AlterTable("ScheduledJobs", "ADD", "Running", true, "INTEGER");
return Db.DbConnection().ConnectionString;
}
private void CreateDefaultSettingsPage(string baseUrl)
{
var defaultUserSettings = new UserManagementSettings
{
RequestMovies = true,
RequestTvShows = true,
ReportIssues = true,
};
var defaultSettings = new PlexRequestSettings
{
SearchForMovies = true,
SearchForTvShows = true,
SearchForActors = true,
BaseUrl = baseUrl ?? string.Empty,
CollectAnalyticData = true,
};
var ctor = new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider());
var s = new SettingsServiceV2<PlexRequestSettings>(ctor);
s.SaveSettings(defaultSettings);
var userSettings = new SettingsServiceV2<UserManagementSettings>(ctor);
userSettings.SaveSettings(defaultUserSettings);
var cron = (Quartz.Impl.Triggers.CronTriggerImpl)CronScheduleBuilder.WeeklyOnDayAndHourAndMinute(DayOfWeek.Friday, 7, 0).Build();
var scheduled = new ScheduledJobsSettings
{
PlexAvailabilityChecker = 60,
SickRageCacher = 60,
SonarrCacher = 60,
CouchPotatoCacher = 60,
StoreBackup = 24,
StoreCleanup = 24,
UserRequestLimitResetter = 12,
PlexEpisodeCacher = 12,
RecentlyAddedCron = cron.CronExpressionString, // Weekly CRON at 7 am on Mondays
};
var scheduledSettings = new SettingsServiceV2<ScheduledJobsSettings>(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider()));
scheduledSettings.SaveSettings(scheduled);
}
public void CacheQualityProfiles()
{
var mc = new MemoryCacheProvider();
try
{
Task.Run(() => { CacheSonarrQualityProfiles(mc); });
Task.Run(() => { CacheRadarrQualityProfiles(mc); });
Task.Run(() => { CacheCouchPotatoQualityProfiles(mc); });
// we don't need to cache sickrage profiles, those are static
}
catch (Exception)
{
Log.Error("Failed to cache quality profiles on startup!");
}
}
private void CacheSonarrQualityProfiles(ICacheProvider cacheProvider)
{
try
{
var sonarrSettingsService = new SettingsServiceV2<SonarrSettings>(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), cacheProvider));
var sonarrSettings = sonarrSettingsService.GetSettings();
if (sonarrSettings.Enabled)
{
Log.Info("Begin executing GetProfiles call to Sonarr for quality profiles");
SonarrApi sonarrApi = new SonarrApi();
var profiles = sonarrApi.GetProfiles(sonarrSettings.ApiKey, sonarrSettings.FullUri);
cacheProvider.Set(CacheKeys.SonarrQualityProfiles, profiles);
Log.Info("Finished executing GetProfiles call to Sonarr for quality profiles");
}
}
catch (Exception ex)
{
Log.Error(ex, "Failed to cache Sonarr quality profiles!");
}
}
private void CacheRadarrQualityProfiles(ICacheProvider cacheProvider)
{
try
{
var radarrService = new SettingsServiceV2<RadarrSettings>(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), cacheProvider));
var radarrSettings = radarrService.GetSettings();
if (radarrSettings.Enabled)
{
Log.Info("Begin executing GetProfiles call to Radarr for quality profiles");
RadarrApi radarrApi = new RadarrApi();
var profiles = radarrApi.GetProfiles(radarrSettings.ApiKey, radarrSettings.FullUri);
cacheProvider.Set(CacheKeys.RadarrQualityProfiles, profiles);
Log.Info("Finished executing GetProfiles call to Radarr for quality profiles");
}
}
catch (Exception ex)
{
Log.Error(ex, "Failed to cache Sonarr quality profiles!");
}
}
private void CacheCouchPotatoQualityProfiles(ICacheProvider cacheProvider)
{
try
{
var cpSettingsService = new SettingsServiceV2<CouchPotatoSettings>(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), cacheProvider));
var cpSettings = cpSettingsService.GetSettings();
if (cpSettings.Enabled)
{
Log.Info("Begin executing GetProfiles call to CouchPotato for quality profiles");
CouchPotatoApi cpApi = new CouchPotatoApi();
var profiles = cpApi.GetProfiles(cpSettings.FullUri, cpSettings.ApiKey);
cacheProvider.Set(CacheKeys.CouchPotatoQualityProfiles, profiles);
Log.Info("Finished executing GetProfiles call to CouchPotato for quality profiles");
}
}
catch (Exception ex)
{
Log.Error(ex, "Failed to cache CouchPotato quality profiles!");
}
}
}
}

View file

@ -0,0 +1,35 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: AppveyorArtifactResult.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.Core.StatusChecker
{
public class AppveyorArtifactResult
{
public string fileName { get; set; }
public string type { get; set; }
public int size { get; set; }
}
}

View file

@ -0,0 +1,138 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: AppveyorBranchResult.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.Collections.Generic;
namespace Ombi.Core.StatusChecker
{
public class NuGetFeed
{
public string id { get; set; }
public string name { get; set; }
public bool publishingEnabled { get; set; }
public string created { get; set; }
}
public class AccessRightDefinition
{
public string name { get; set; }
public string description { get; set; }
}
public class AccessRight
{
public string name { get; set; }
public bool allowed { get; set; }
}
public class RoleAce
{
public int roleId { get; set; }
public string name { get; set; }
public bool isAdmin { get; set; }
public List<AccessRight> accessRights { get; set; }
}
public class SecurityDescriptor
{
public List<AccessRightDefinition> accessRightDefinitions { get; set; }
public List<RoleAce> roleAces { get; set; }
}
public class Project
{
public int projectId { get; set; }
public int accountId { get; set; }
public string accountName { get; set; }
public List<object> builds { get; set; }
public string name { get; set; }
public string slug { get; set; }
public string repositoryType { get; set; }
public string repositoryScm { get; set; }
public string repositoryName { get; set; }
public string repositoryBranch { get; set; }
public bool isPrivate { get; set; }
public bool skipBranchesWithoutAppveyorYml { get; set; }
public bool enableSecureVariablesInPullRequests { get; set; }
public bool enableSecureVariablesInPullRequestsFromSameRepo { get; set; }
public bool enableDeploymentInPullRequests { get; set; }
public bool rollingBuilds { get; set; }
public bool alwaysBuildClosedPullRequests { get; set; }
public string tags { get; set; }
public NuGetFeed nuGetFeed { get; set; }
public SecurityDescriptor securityDescriptor { get; set; }
public string created { get; set; }
public string updated { get; set; }
}
public class Job
{
public string jobId { get; set; }
public string name { get; set; }
public bool allowFailure { get; set; }
public int messagesCount { get; set; }
public int compilationMessagesCount { get; set; }
public int compilationErrorsCount { get; set; }
public int compilationWarningsCount { get; set; }
public int testsCount { get; set; }
public int passedTestsCount { get; set; }
public int failedTestsCount { get; set; }
public int artifactsCount { get; set; }
public string status { get; set; }
public string started { get; set; }
public string finished { get; set; }
public string created { get; set; }
public string updated { get; set; }
}
public class Build
{
public int buildId { get; set; }
public List<Job> jobs { get; set; }
public int buildNumber { get; set; }
public string version { get; set; }
public string message { get; set; }
public string branch { get; set; }
public bool isTag { get; set; }
public string commitId { get; set; }
public string authorName { get; set; }
public string committerName { get; set; }
public string committed { get; set; }
public List<object> messages { get; set; }
public string status { get; set; }
public string started { get; set; }
public string finished { get; set; }
public string created { get; set; }
public string updated { get; set; }
}
public class AppveyorBranchResult
{
public Project project { get; set; }
public Build build { get; set; }
}
}

View file

@ -0,0 +1,231 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: StatusChecker.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.Security.Policy;
using System.Threading.Tasks;
using Nancy.Session;
using Octokit;
using Octokit.Internal;
using Ombi.Api;
using Ombi.Core.Models;
using Ombi.Core.SettingModels;
using Ombi.Helpers;
using RestSharp;
namespace Ombi.Core.StatusChecker
{
public class StatusChecker : IStatusChecker
{
public StatusChecker(ISettingsService<SystemSettings> ss)
{
SystemSettings = ss;
Git = new GitHubClient(new ProductHeaderValue("Ombi"));
}
private ISettingsService<SystemSettings> SystemSettings { get; }
private IGitHubClient Git { get; }
private const string Owner = "tidusjar";
private const string RepoName = "Ombi";
private const string AppveyorApiUrl = "https://ci.appveyor.com/api";
private const string Api =
"48Ku58C0794nBrXra8IxWav+dc6NqgkRw+PZB3/bQwbt/D0IrnJQkgtjzo0bd6nkooLMKsC8M+Ab7jyBO+ROjY14VRuxffpDopX9r0iG/fjBl6mZVvqkm+VTDNstDtzp";
public async Task<StatusModel> GetStatus()
{
var settings = await SystemSettings.GetSettingsAsync();
var stable = settings.Branch == Branches.Stable;
if (!stable)
{
// Early Access Preview Releases
return GetAppveyorRelease(settings.Branch);
}
// Stable releases
return await GetLatestGithubRelease();
}
private async Task<StatusModel> GetLatestGithubRelease()
{
var assemblyVersion = AssemblyHelper.GetProductVersion();
var model = new StatusModel
{
CurrentVersion = assemblyVersion,
};
var releases = await Git.Repository.Release.GetAll(Owner, RepoName);
var latestRelease = releases.FirstOrDefault();
if (latestRelease == null)
{
return new StatusModel { NewVersion = "Unknown" };
}
var latestVersionArray = latestRelease.Name.Split(new[] { 'v' }, StringSplitOptions.RemoveEmptyEntries);
var latestVersion = latestVersionArray.Length > 1 ? latestVersionArray[1] : string.Empty;
if (!latestVersion.Equals(assemblyVersion, StringComparison.InvariantCultureIgnoreCase))
{
model.UpdateAvailable = true;
model.UpdateUri = latestRelease.HtmlUrl;
model.NewVersion = latestVersion;
}
model.ReleaseNotes = latestRelease.Body;
model.DownloadUri = latestRelease.Assets[0].BrowserDownloadUrl;
model.ReleaseTitle = latestRelease.Name;
return model;
}
private StatusModel GetAppveyorRelease(Branches branch)
{
var request = new ApiRequest();
// Get latest EAP Build
var eapBranchRequest = new RestRequest
{
Method = Method.GET
};
switch (branch)
{
case Branches.Dev:
eapBranchRequest.Resource = "/projects/tidusjar/requestplex/branch/dev";
break;
case Branches.EarlyAccessPreview:
eapBranchRequest.Resource = "/projects/tidusjar/requestplex/branch/EAP";
break;
}
var api = StringCipher.Decrypt(Api,"Appveyor");
eapBranchRequest.AddHeader("Authorization", $"Bearer {api}");
eapBranchRequest.AddHeader("Content-Type", "application/json");
var branchResult = request.ExecuteJson<AppveyorBranchResult>(eapBranchRequest, new Uri(AppveyorApiUrl));
var jobId = branchResult.build.jobs.FirstOrDefault()?.jobId ?? string.Empty;
if (string.IsNullOrEmpty(jobId))
{
return new StatusModel {UpdateAvailable = false};
}
// Get artifacts from the EAP Build
var eapAtrifactRequest = new RestRequest
{
Resource = $"/buildjobs/{jobId}/artifacts",
Method = Method.GET
};
eapAtrifactRequest.AddHeader("Authorization", $"Bearer {api}");
eapAtrifactRequest.AddHeader("Content-Type", "application/json");
var artifactResults = request.ExecuteJson<List<AppveyorArtifactResult>>(eapAtrifactRequest, new Uri(AppveyorApiUrl));
var artifactResult = artifactResults.FirstOrDefault();
if (artifactResult == null)
{
return new StatusModel
{
UpdateAvailable = false
};
}
var downloadLink = $"{AppveyorApiUrl}/buildjobs/{jobId}/artifacts/{artifactResult.fileName}";
var branchDisplay = EnumHelper<Branches>.GetBranchValue<BranchAttribute>(branch).DisplayName;
var fileversion = AssemblyHelper.GetFileVersion();
var model = new StatusModel
{
DownloadUri = downloadLink,
ReleaseNotes = $"{branchDisplay} (See Recent Changes tab for more details)",
ReleaseTitle = $"Ombi {branchDisplay}",
NewVersion = branchResult.build.version,
UpdateUri = downloadLink,
CurrentVersion = fileversion
};
if (!fileversion.Equals(branchResult.build.version, StringComparison.CurrentCultureIgnoreCase))
{
model.UpdateAvailable = true;
}
return model;
}
public async Task<Issue> ReportBug(string title, string body, string oauthToken)
{
Git.Connection.Credentials = new Credentials(oauthToken);
var issue = new NewIssue(title)
{
Body = body
};
var result = await Git.Issue.Create(Owner, RepoName, issue);
return result;
}
string clientId = "f407108cdb1e660f68c5";
string clientSecret = "84b56e22002da2929c34fc773d89f3402a19098a";
public async Task<Uri> OAuth(string url, ISession session)
{
await Task.Yield();
var csrf = StringCipher.Encrypt(Guid.NewGuid().ToString("N"), "CSRF");
session[SessionKeys.CSRF] = csrf;
var request = new OauthLoginRequest(clientId)
{
Scopes = { "public_repo", "user" },
State = csrf,
RedirectUri = new Uri(url)
};
// NOTE: user must be navigated to this URL
var oauthLoginUrl = Git.Oauth.GetGitHubLoginUrl(request);
return oauthLoginUrl;
}
public async Task<OauthToken> OAuthAccessToken(string code)
{
var request = new OauthTokenRequest(clientId, clientSecret, code);
var token = await Git.Oauth.CreateAccessToken(request);
return token;
}
}
}

View file

@ -0,0 +1,398 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: TvSender.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NLog;
using Ombi.Api.Interfaces;
using Ombi.Api.Models.SickRage;
using Ombi.Api.Models.Sonarr;
using Ombi.Core.SettingModels;
using Ombi.Helpers;
using Ombi.Store;
namespace Ombi.Core
{
public class TvSender
{
public TvSender(ISonarrApi sonarrApi, ISickRageApi srApi, ICacheProvider cache)
{
SonarrApi = sonarrApi;
SickrageApi = srApi;
Cache = cache;
}
private ISonarrApi SonarrApi { get; }
private ISickRageApi SickrageApi { get; }
private ICacheProvider Cache { get; }
private static Logger Log = LogManager.GetCurrentClassLogger();
public async Task<SonarrAddSeries> SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model)
{
return await SendToSonarr(sonarrSettings, model, string.Empty);
}
/// <summary>
/// Broken Way
/// </summary>
/// <param name="sonarrSettings"></param>
/// <param name="model"></param>
/// <param name="qualityId"></param>
/// <returns></returns>
public async Task<SonarrAddSeries> SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model, string qualityId)
{
var qualityProfile = 0;
var episodeRequest = model.Episodes.Any();
if (!string.IsNullOrEmpty(qualityId)) // try to parse the passed in quality, otherwise use the settings default quality
{
int.TryParse(qualityId, out qualityProfile);
}
if (qualityProfile <= 0)
{
int.TryParse(sonarrSettings.QualityProfile, out qualityProfile);
}
var series = await GetSonarrSeries(sonarrSettings, model.ProviderId);
var requestAll = model.SeasonsRequested?.Equals("All", StringComparison.CurrentCultureIgnoreCase);
var first = model.SeasonsRequested?.Equals("First", StringComparison.CurrentCultureIgnoreCase);
var latest = model.SeasonsRequested?.Equals("Latest", StringComparison.CurrentCultureIgnoreCase);
var specificSeasonRequest = model.SeasonList?.Any();
var rootFolderPath = model.RootFolderSelected <= 0 ? sonarrSettings.FullRootPath : await GetRootPath(model.RootFolderSelected, sonarrSettings);
if (episodeRequest)
{
// Does series exist?
if (series != null)
{
// Series Exists
// Request the episodes in the existing series
await RequestEpisodesWithExistingSeries(model, series, sonarrSettings);
return new SonarrAddSeries { title = series.title };
}
// Series doesn't exist, need to add it as unmonitored.
var addResult = await Task.Run(() => SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile,
sonarrSettings.SeasonFolders, rootFolderPath, 0, new int[0], sonarrSettings.ApiKey,
sonarrSettings.FullUri, false));
// Get the series that was just added
series = await GetSonarrSeries(sonarrSettings, model.ProviderId);
series.monitored = true; // We want to make sure we are monitoring the series
// Un-monitor all seasons
foreach (var season in series.seasons)
{
season.monitored = false;
}
// Update the series, Since we cannot add as un-monitored due to the following bug: https://github.com/Sonarr/Sonarr/issues/1404
SonarrApi.UpdateSeries(series, sonarrSettings.ApiKey, sonarrSettings.FullUri);
// We now have the series in Sonarr, update it to request the episodes.
await RequestAllEpisodesInASeasonWithExistingSeries(model, series, sonarrSettings);
return addResult;
}
// Series exists, don't need to add it
if (series == null)
{
// Set the series as monitored with a season count as 0 so it doesn't search for anything
SonarrApi.AddSeriesNew(model.ProviderId, model.Title, qualityProfile,
sonarrSettings.SeasonFolders, rootFolderPath, new int[] {1,2,3,4,5,6,7,8,9,10,11,12,13}, sonarrSettings.ApiKey,
sonarrSettings.FullUri);
await Task.Delay(TimeSpan.FromSeconds(1));
series = await GetSonarrSeries(sonarrSettings, model.ProviderId);
foreach (var s in series.seasons)
{
s.monitored = false;
}
SonarrApi.UpdateSeries(series, sonarrSettings.ApiKey, sonarrSettings.FullUri);
}
if (requestAll ?? false)
{
// Monitor all seasons
foreach (var season in series.seasons)
{
season.monitored = true;
}
SonarrApi.UpdateSeries(series, sonarrSettings.ApiKey, sonarrSettings.FullUri);
SonarrApi.SearchForSeries(series.id, sonarrSettings.ApiKey, sonarrSettings.FullUri); // Search For all episodes!"
//// This is a work around for this issue: https://github.com/Sonarr/Sonarr/issues/1507
//// The above is the previous code.
//SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile,
// sonarrSettings.SeasonFolders, sonarrSettings.RootPath, 0, model.SeasonList, sonarrSettings.ApiKey,
// sonarrSettings.FullUri, true, true);
return new SonarrAddSeries { title = series.title }; // We have updated it
}
if (first ?? false)
{
var firstSeries = (series?.seasons?.OrderBy(x => x.seasonNumber)).FirstOrDefault(x => x.seasonNumber > 0) ?? new Season();
firstSeries.monitored = true;
var episodes = SonarrApi.GetEpisodes(series.id.ToString(), sonarrSettings.ApiKey, sonarrSettings.FullUri); // Need to get the episodes so we mark them as monitored
var episodesToUpdate = new List<SonarrEpisodes>();
foreach (var e in episodes)
{
if (e.hasFile || e.seasonNumber != firstSeries.seasonNumber)
{
continue;
}
e.monitored = true; // Mark only the episodes we want as monitored
episodesToUpdate.Add(e);
}
foreach (var sonarrEpisode in episodesToUpdate)
{
SonarrApi.UpdateEpisode(sonarrEpisode, sonarrSettings.ApiKey, sonarrSettings.FullUri);
}
SonarrApi.UpdateSeries(series, sonarrSettings.ApiKey, sonarrSettings.FullUri);
SonarrApi.SearchForSeason(series.id, firstSeries.seasonNumber, sonarrSettings.ApiKey,
sonarrSettings.FullUri);
return new SonarrAddSeries { title = series.title }; // We have updated it
}
if (latest ?? false)
{
var lastSeries = series?.seasons?.OrderByDescending(x => x.seasonNumber)?.FirstOrDefault() ?? new Season();
lastSeries.monitored = true;
var episodes = SonarrApi.GetEpisodes(series.id.ToString(), sonarrSettings.ApiKey, sonarrSettings.FullUri); // Need to get the episodes so we mark them as monitored
var episodesToUpdate = new List<SonarrEpisodes>();
foreach (var e in episodes)
{
if (e.hasFile || e.seasonNumber != lastSeries.seasonNumber)
{
continue;
}
e.monitored = true; // Mark only the episodes we want as monitored
episodesToUpdate.Add(e);
}
foreach (var sonarrEpisode in episodesToUpdate)
{
SonarrApi.UpdateEpisode(sonarrEpisode, sonarrSettings.ApiKey, sonarrSettings.FullUri);
}
SonarrApi.UpdateSeries(series, sonarrSettings.ApiKey, sonarrSettings.FullUri);
SonarrApi.SearchForSeason(series.id, lastSeries.seasonNumber, sonarrSettings.ApiKey,
sonarrSettings.FullUri);
return new SonarrAddSeries { title = series.title }; // We have updated it
}
if (specificSeasonRequest ?? false)
{
// Monitor the seasons that we have chosen
foreach (var season in series.seasons)
{
if (model.SeasonList.Contains(season.seasonNumber))
{
season.monitored = true;
SonarrApi.UpdateSeries(series, sonarrSettings.ApiKey, sonarrSettings.FullUri);
SonarrApi.SearchForSeason(series.id, season.seasonNumber, sonarrSettings.ApiKey, sonarrSettings.FullUri);
}
}
return new SonarrAddSeries { title = series.title }; // We have updated it
}
return null;
}
public SickRageTvAdd SendToSickRage(SickRageSettings sickRageSettings, RequestedModel model)
{
return SendToSickRage(sickRageSettings, model, sickRageSettings.QualityProfile);
}
public SickRageTvAdd SendToSickRage(SickRageSettings sickRageSettings, RequestedModel model, string qualityId)
{
Log.Info("Sending to SickRage {0}", model.Title);
if (sickRageSettings.Qualities.All(x => x.Key != qualityId))
{
qualityId = sickRageSettings.QualityProfile;
}
var apiResult = SickrageApi.AddSeries(model.ProviderId, model.SeasonCount, model.SeasonList, qualityId,
sickRageSettings.ApiKey, sickRageSettings.FullUri);
var result = apiResult.Result;
return result;
}
internal async Task RequestEpisodesWithExistingSeries(RequestedModel model, Series selectedSeries, SonarrSettings sonarrSettings)
{
// Show Exists
// Look up all episodes
var ep = SonarrApi.GetEpisodes(selectedSeries.id.ToString(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
var episodes = ep?.ToList() ?? new List<SonarrEpisodes>();
var internalEpisodeIds = new List<int>();
var tasks = new List<Task>();
foreach (var r in model.Episodes)
{
// Match the episode and season number.
// If the episode is monitored we might not be searching for it.
var episode =
episodes.FirstOrDefault(
x => x.episodeNumber == r.EpisodeNumber && x.seasonNumber == r.SeasonNumber);
if (episode == null)
{
continue;
}
var episodeInfo = SonarrApi.GetEpisode(episode.id.ToString(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
episodeInfo.monitored = true; // Set the episode to monitored
tasks.Add(Task.Run(() => SonarrApi.UpdateEpisode(episodeInfo, sonarrSettings.ApiKey,
sonarrSettings.FullUri)));
internalEpisodeIds.Add(episode.id);
}
await Task.WhenAll(tasks.ToArray());
SonarrApi.SearchForEpisodes(internalEpisodeIds.ToArray(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
}
internal async Task RequestAllEpisodesWithExistingSeries(RequestedModel model, Series selectedSeries, SonarrSettings sonarrSettings)
{
// Show Exists
// Look up all episodes
var ep = SonarrApi.GetEpisodes(selectedSeries.id.ToString(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
var episodes = ep?.ToList() ?? new List<SonarrEpisodes>();
var internalEpisodeIds = new List<int>();
var tasks = new List<Task>();
foreach (var r in episodes)
{
if (r.monitored || r.hasFile) // If it's already monitored or has the file, there is no point in updating it
{
continue;
}
// Lookup the individual episode details
var episodeInfo = SonarrApi.GetEpisode(r.id.ToString(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
episodeInfo.monitored = true; // Set the episode to monitored
tasks.Add(Task.Run(() => SonarrApi.UpdateEpisode(episodeInfo, sonarrSettings.ApiKey,
sonarrSettings.FullUri)));
internalEpisodeIds.Add(r.id);
}
await Task.WhenAll(tasks.ToArray());
SonarrApi.SearchForEpisodes(internalEpisodeIds.ToArray(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
}
internal async Task RequestAllEpisodesInASeasonWithExistingSeries(RequestedModel model, Series selectedSeries, SonarrSettings sonarrSettings)
{
// Show Exists
// Look up all episodes
var ep = SonarrApi.GetEpisodes(selectedSeries.id.ToString(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
var episodes = ep?.ToList() ?? new List<SonarrEpisodes>();
var internalEpisodeIds = new List<int>();
var tasks = new List<Task>();
var requestedEpisodes = model.Episodes;
foreach (var r in episodes)
{
if (r.hasFile) // If it already has the file, there is no point in updating it
{
continue;
}
var epComparison = new EpisodesModel
{
EpisodeNumber = r.episodeNumber,
SeasonNumber = r.seasonNumber
};
// Make sure we are looking for the right episode and season
if (!requestedEpisodes.Contains(epComparison))
{
continue;
}
// Lookup the individual episode details
var episodeInfo = SonarrApi.GetEpisode(r.id.ToString(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
// If the season is not in thr
episodeInfo.monitored = true; // Set the episode to monitored
tasks.Add(Task.Run(() => SonarrApi.UpdateEpisode(episodeInfo, sonarrSettings.ApiKey,
sonarrSettings.FullUri)));
internalEpisodeIds.Add(r.id);
}
await Task.WhenAll(tasks.ToArray());
SonarrApi.SearchForEpisodes(internalEpisodeIds.ToArray(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
}
private async Task<Series> GetSonarrSeries(SonarrSettings sonarrSettings, int showId)
{
var task = await Task.Run(() => SonarrApi.GetSeries(sonarrSettings.ApiKey, sonarrSettings.FullUri)).ConfigureAwait(false);
var selectedSeries = task.FirstOrDefault(series => series.tvdbId == showId);
return selectedSeries;
}
private async Task<string> GetRootPath(int pathId, SonarrSettings sonarrSettings)
{
var rootFoldersResult = await Cache.GetOrSetAsync(CacheKeys.SonarrRootFolders, async () =>
{
return await Task.Run(() => SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri));
});
foreach (var r in rootFoldersResult.Where(r => r.id == pathId))
{
return r.path;
}
return string.Empty;
}
}
}

View file

@ -0,0 +1,322 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: TvSenderOld.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NLog;
using Ombi.Api.Interfaces;
using Ombi.Api.Models.SickRage;
using Ombi.Api.Models.Sonarr;
using Ombi.Core.SettingModels;
using Ombi.Helpers;
using Ombi.Store;
namespace Ombi.Core
{
public class TvSenderOld
{
public TvSenderOld(ISonarrApi sonarrApi, ISickRageApi srApi, ICacheProvider cache)
{
SonarrApi = sonarrApi;
SickrageApi = srApi;
Cache = cache;
}
private ISonarrApi SonarrApi { get; }
private ISickRageApi SickrageApi { get; }
private ICacheProvider Cache { get; }
private static Logger Log = LogManager.GetCurrentClassLogger();
public async Task<SonarrAddSeries> SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model)
{
return await SendToSonarr(sonarrSettings, model, string.Empty);
}
public async Task<SonarrAddSeries> SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model, string qualityId)
{
var qualityProfile = 0;
var episodeRequest = model.Episodes.Any();
if (!string.IsNullOrEmpty(qualityId)) // try to parse the passed in quality, otherwise use the settings default quality
{
int.TryParse(qualityId, out qualityProfile);
}
if (qualityProfile <= 0)
{
int.TryParse(sonarrSettings.QualityProfile, out qualityProfile);
}
var rootFolderPath = model.RootFolderSelected <= 0 ? sonarrSettings.FullRootPath : await GetRootPath(model.RootFolderSelected, sonarrSettings);
var series = await GetSonarrSeries(sonarrSettings, model.ProviderId);
if (episodeRequest)
{
// Does series exist?
if (series != null)
{
// Series Exists
// Request the episodes in the existing series
await RequestEpisodesWithExistingSeries(model, series, sonarrSettings);
return new SonarrAddSeries { title = series.title };
}
// Series doesn't exist, need to add it as unmonitored.
var addResult = await Task.Run(() => SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile,
sonarrSettings.SeasonFolders, rootFolderPath, 0, new int[0], sonarrSettings.ApiKey,
sonarrSettings.FullUri, false));
// Get the series that was just added
series = await GetSonarrSeries(sonarrSettings, model.ProviderId);
series.monitored = true; // We want to make sure we are monitoring the series
// Un-monitor all seasons
foreach (var season in series.seasons)
{
season.monitored = false;
}
// Update the series, Since we cannot add as un-monitored due to the following bug: https://github.com/Sonarr/Sonarr/issues/1404
SonarrApi.UpdateSeries(series, sonarrSettings.ApiKey, sonarrSettings.FullUri);
// We now have the series in Sonarr, update it to request the episodes.
await RequestAllEpisodesInASeasonWithExistingSeries(model, series, sonarrSettings);
return addResult;
}
if (series != null)
{
var requestAll = model.SeasonsRequested.Equals("All", StringComparison.CurrentCultureIgnoreCase);
var first = model.SeasonsRequested.Equals("First", StringComparison.CurrentCultureIgnoreCase);
var latest = model.SeasonsRequested.Equals("Latest", StringComparison.CurrentCultureIgnoreCase);
if (model.SeasonList.Any())
{
// Monitor the seasons that we have chosen
foreach (var season in series.seasons)
{
if (model.SeasonList.Contains(season.seasonNumber))
{
season.monitored = true;
}
}
}
if (requestAll)
{
// Monitor all seasons
foreach (var season in series.seasons)
{
season.monitored = true;
}
}
if (first)
{
var firstSeries = series?.seasons?.OrderBy(x => x.seasonNumber)?.FirstOrDefault() ?? new Season();
firstSeries.monitored = true;
}
if (latest)
{
var lastSeries = series?.seasons?.OrderByDescending(x => x.seasonNumber)?.FirstOrDefault() ?? new Season();
lastSeries.monitored = true;
}
// Update the series in sonarr with the new monitored status
SonarrApi.UpdateSeries(series, sonarrSettings.ApiKey, sonarrSettings.FullUri);
await RequestAllEpisodesInASeasonWithExistingSeries(model, series, sonarrSettings);
return new SonarrAddSeries { title = series.title }; // We have updated it
}
var result = SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile,
sonarrSettings.SeasonFolders, rootFolderPath, model.SeasonCount, model.SeasonList, sonarrSettings.ApiKey,
sonarrSettings.FullUri, true, true);
return result;
}
public SickRageTvAdd SendToSickRage(SickRageSettings sickRageSettings, RequestedModel model)
{
return SendToSickRage(sickRageSettings, model, sickRageSettings.QualityProfile);
}
public SickRageTvAdd SendToSickRage(SickRageSettings sickRageSettings, RequestedModel model, string qualityId)
{
Log.Info("Sending to SickRage {0}", model.Title);
if (sickRageSettings.Qualities.All(x => x.Key != qualityId))
{
qualityId = sickRageSettings.QualityProfile;
}
var apiResult = SickrageApi.AddSeries(model.ProviderId, model.SeasonCount, model.SeasonList, qualityId,
sickRageSettings.ApiKey, sickRageSettings.FullUri);
var result = apiResult.Result;
return result;
}
internal async Task RequestEpisodesWithExistingSeries(RequestedModel model, Series selectedSeries, SonarrSettings sonarrSettings)
{
// Show Exists
// Look up all episodes
var ep = SonarrApi.GetEpisodes(selectedSeries.id.ToString(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
var episodes = ep?.ToList() ?? new List<SonarrEpisodes>();
var internalEpisodeIds = new List<int>();
var tasks = new List<Task>();
foreach (var r in model.Episodes)
{
// Match the episode and season number.
// If the episode is monitored we might not be searching for it.
var episode =
episodes.FirstOrDefault(
x => x.episodeNumber == r.EpisodeNumber && x.seasonNumber == r.SeasonNumber);
if (episode == null)
{
continue;
}
var episodeInfo = SonarrApi.GetEpisode(episode.id.ToString(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
episodeInfo.monitored = true; // Set the episode to monitored
tasks.Add(Task.Run(() => SonarrApi.UpdateEpisode(episodeInfo, sonarrSettings.ApiKey,
sonarrSettings.FullUri)));
internalEpisodeIds.Add(episode.id);
}
await Task.WhenAll(tasks.ToArray());
SonarrApi.SearchForEpisodes(internalEpisodeIds.ToArray(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
}
internal async Task RequestAllEpisodesWithExistingSeries(RequestedModel model, Series selectedSeries, SonarrSettings sonarrSettings)
{
// Show Exists
// Look up all episodes
var ep = SonarrApi.GetEpisodes(selectedSeries.id.ToString(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
var episodes = ep?.ToList() ?? new List<SonarrEpisodes>();
var internalEpisodeIds = new List<int>();
var tasks = new List<Task>();
foreach (var r in episodes)
{
if (r.monitored || r.hasFile) // If it's already montiored or has the file, there is no point in updating it
{
continue;
}
// Lookup the individual episode details
var episodeInfo = SonarrApi.GetEpisode(r.id.ToString(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
episodeInfo.monitored = true; // Set the episode to monitored
tasks.Add(Task.Run(() => SonarrApi.UpdateEpisode(episodeInfo, sonarrSettings.ApiKey,
sonarrSettings.FullUri)));
internalEpisodeIds.Add(r.id);
}
await Task.WhenAll(tasks.ToArray());
SonarrApi.SearchForEpisodes(internalEpisodeIds.ToArray(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
}
internal async Task RequestAllEpisodesInASeasonWithExistingSeries(RequestedModel model, Series selectedSeries, SonarrSettings sonarrSettings)
{
// Show Exists
// Look up all episodes
var ep = SonarrApi.GetEpisodes(selectedSeries.id.ToString(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
var episodes = ep?.ToList() ?? new List<SonarrEpisodes>();
var internalEpisodeIds = new List<int>();
var tasks = new List<Task>();
var requestedEpisodes = model.Episodes;
foreach (var r in episodes)
{
if (r.hasFile) // If it already has the file, there is no point in updating it
{
continue;
}
var epComparison = new EpisodesModel
{
EpisodeNumber = r.episodeNumber,
SeasonNumber = r.seasonNumber
};
// Make sure we are looking for the right episode and season
if (!requestedEpisodes.Contains(epComparison))
{
continue;
}
// Lookup the individual episode details
var episodeInfo = SonarrApi.GetEpisode(r.id.ToString(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
// If the season is not in thr
episodeInfo.monitored = true; // Set the episode to monitored
tasks.Add(Task.Run(() => SonarrApi.UpdateEpisode(episodeInfo, sonarrSettings.ApiKey,
sonarrSettings.FullUri)));
internalEpisodeIds.Add(r.id);
}
await Task.WhenAll(tasks.ToArray());
SonarrApi.SearchForEpisodes(internalEpisodeIds.ToArray(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
}
private async Task<Series> GetSonarrSeries(SonarrSettings sonarrSettings, int showId)
{
var task = await Task.Run(() => SonarrApi.GetSeries(sonarrSettings.ApiKey, sonarrSettings.FullUri)).ConfigureAwait(false);
var selectedSeries = task.FirstOrDefault(series => series.tvdbId == showId);
return selectedSeries;
}
private async Task<string> GetRootPath(int pathId, SonarrSettings sonarrSettings)
{
var rootFoldersResult = await Cache.GetOrSetAsync(CacheKeys.SonarrRootFolders, async () =>
{
return await Task.Run(() => SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri));
});
foreach (var r in rootFoldersResult.Where(r => r.id == pathId))
{
return r.path;
}
return string.Empty;
}
}
}

View file

@ -0,0 +1,315 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2017 Jamie Rees
// File: TvSenderV2.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NLog;
using Ombi.Api.Interfaces;
using Ombi.Api.Models.SickRage;
using Ombi.Api.Models.Sonarr;
using Ombi.Core.SettingModels;
using Ombi.Helpers;
using Ombi.Store;
namespace Ombi.Core.Tv
{
public class TvSenderV2
{
public TvSenderV2(ISonarrApi sonarrApi, ISickRageApi srApi, ICacheProvider cache)
{
SonarrApi = sonarrApi;
SickrageApi = srApi;
Cache = cache;
}
private ISonarrApi SonarrApi { get; }
private ISickRageApi SickrageApi { get; }
private ICacheProvider Cache { get; }
private static Logger _log = LogManager.GetCurrentClassLogger();
public async Task<SonarrAddSeries> SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model)
{
return await SendToSonarr(sonarrSettings, model, string.Empty);
}
public async Task<SonarrAddSeries> SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model,
string qualityId)
{
var qualityProfile = 0;
if (!string.IsNullOrEmpty(qualityId)) // try to parse the passed in quality, otherwise use the settings default quality
{
int.TryParse(qualityId, out qualityProfile);
}
if (qualityProfile <= 0)
{
int.TryParse(sonarrSettings.QualityProfile, out qualityProfile);
}
var rootFolderPath = model.RootFolderSelected <= 0 ? sonarrSettings.FullRootPath : await GetSonarrRootPath(model.RootFolderSelected, sonarrSettings);
var episodeRequest = model.Episodes.Any();
var requestAll = model.SeasonsRequested?.Equals("All", StringComparison.CurrentCultureIgnoreCase);
var first = model.SeasonsRequested?.Equals("First", StringComparison.CurrentCultureIgnoreCase);
var latest = model.SeasonsRequested?.Equals("Latest", StringComparison.CurrentCultureIgnoreCase);
var specificSeasonRequest = model.SeasonList?.Any();
if (episodeRequest)
{
return await ProcessSonarrEpisodeRequest(sonarrSettings, model, qualityProfile, rootFolderPath);
}
if (requestAll ?? false)
{
return await ProcessSonarrRequestSeason(sonarrSettings, model, qualityProfile, rootFolderPath);
}
if (first ?? false)
{
return await ProcessSonarrRequestSeason(sonarrSettings, model, qualityProfile, rootFolderPath);
}
if (latest ?? false)
{
return await ProcessSonarrRequestSeason(sonarrSettings, model, qualityProfile, rootFolderPath);
}
if (specificSeasonRequest ?? false)
{
return await ProcessSonarrRequestSeason(sonarrSettings, model, qualityProfile, rootFolderPath);
}
return null;
}
private async Task<SonarrAddSeries> ProcessSonarrRequestSeason(SonarrSettings sonarrSettings, RequestedModel model, int qualityId, string rootFolderPath)
{
// Does the series exist?
var series = await GetSonarrSeries(sonarrSettings, model.ProviderId);
if (series == null)
{
//WORKS
// Add the series
return AddSeries(sonarrSettings, model, rootFolderPath, qualityId);
}
// Also make sure the series is now monitored otherwise we won't search for it
series.monitored = true;
foreach (var seasons in series.seasons)
{
seasons.monitored = true;
}
// Send the update command
series = SonarrApi.UpdateSeries(series, sonarrSettings.ApiKey, sonarrSettings.FullUri);
SonarrApi.SearchForSeries(series.id, sonarrSettings.ApiKey, sonarrSettings.FullUri);
return new SonarrAddSeries { title = series.title };
}
private async Task<SonarrAddSeries> ProcessSonarrEpisodeRequest(SonarrSettings sonarrSettings, RequestedModel model, int qualityId, string rootFolderPath)
{
// Does the series exist?
var series = await GetSonarrSeries(sonarrSettings, model.ProviderId);
if (series == null)
{
var seriesToAdd = new SonarrAddSeries
{
seasonFolder = sonarrSettings.SeasonFolders,
title = model.Title,
qualityProfileId = qualityId,
tvdbId = model.ProviderId,
titleSlug = model.Title,
seasons = new List<Season>(),
rootFolderPath = rootFolderPath,
monitored = true, // Montior the series
images = new List<SonarrImage>(),
addOptions = new AddOptions
{
ignoreEpisodesWithFiles = true, // We don't really care about these
ignoreEpisodesWithoutFiles = true, // We do not want to grab random episodes missing
searchForMissingEpisodes = false // we want don't want to search for the missing episodes either
}
};
for (var i = 1; i <= model.SeasonCount; i++)
{
Season season;
if (model.Episodes.Any(x => x.SeasonNumber == i))
{
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);
}
// Add the series now
var result = SonarrApi.AddSeries(seriesToAdd, sonarrSettings.ApiKey, sonarrSettings.FullUri);
await RequestEpisodesForSonarr(model, result.id, sonarrSettings);
}
else
{
await RequestEpisodesForSonarr(model, series.id, sonarrSettings);
}
return new SonarrAddSeries() { title = model.Title };
}
public SonarrAddSeries AddSeries(SonarrSettings sonarrSettings, RequestedModel model, string rootFolderPath, int qualityId)
{
//WORKS
// Add the series
var seriesToAdd = new SonarrAddSeries
{
seasonFolder = sonarrSettings.SeasonFolders,
title = model.Title,
qualityProfileId = qualityId,
tvdbId = model.ProviderId,
titleSlug = model.Title,
seasons = new List<Season>(),
rootFolderPath = rootFolderPath,
monitored = true, // Montior the series
images = new List<SonarrImage>(),
addOptions = new AddOptions
{
ignoreEpisodesWithFiles = true, // We don't really care about these
ignoreEpisodesWithoutFiles = false, // We want to get the whole season
searchForMissingEpisodes = true // we want to search for missing
}
};
for (var i = 1; i <= model.SeasonCount; i++)
{
var season = new Season
{
seasonNumber = i,
// The model.SeasonList.Lenth is 0 when this is a "request all"
monitored = model.SeasonList.Length == 0 || model.SeasonList.Any(x => x == i)
};
seriesToAdd.seasons.Add(season);
}
return SonarrApi.AddSeries(seriesToAdd, sonarrSettings.ApiKey, sonarrSettings.FullUri);
}
private async Task RequestEpisodesForSonarr(RequestedModel model, int showId, SonarrSettings sonarrSettings)
{
// Now lookup all episodes
var ep = SonarrApi.GetEpisodes(showId.ToString(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
var episodes = ep?.ToList() ?? new List<SonarrEpisodes>();
var internalEpisodeIds = new List<int>();
var tasks = new List<Task>();
foreach (var r in model.Episodes)
{
// Match the episode and season number.
// If the episode is monitored we might not be searching for it.
var episode =
episodes.FirstOrDefault(
x => x.episodeNumber == r.EpisodeNumber && x.seasonNumber == r.SeasonNumber);
if (episode == null)
{
continue;
}
var episodeInfo = SonarrApi.GetEpisode(episode.id.ToString(), sonarrSettings.ApiKey,
sonarrSettings.FullUri);
episodeInfo.monitored = true; // Set the episode to monitored
tasks.Add(Task.Run(() => SonarrApi.UpdateEpisode(episodeInfo, sonarrSettings.ApiKey,
sonarrSettings.FullUri)));
internalEpisodeIds.Add(episode.id);
}
await Task.WhenAll(tasks.ToArray());
SonarrApi.SearchForEpisodes(internalEpisodeIds.ToArray(), sonarrSettings.ApiKey, sonarrSettings.FullUri);
}
public SickRageTvAdd SendToSickRage(SickRageSettings sickRageSettings, RequestedModel model)
{
return SendToSickRage(sickRageSettings, model, sickRageSettings.QualityProfile);
}
public SickRageTvAdd SendToSickRage(SickRageSettings sickRageSettings, RequestedModel model, string qualityId)
{
_log.Info("Sending to SickRage {0}", model.Title);
if (sickRageSettings.Qualities.All(x => x.Key != qualityId))
{
qualityId = sickRageSettings.QualityProfile;
}
var apiResult = SickrageApi.AddSeries(model.ProviderId, model.SeasonCount, model.SeasonList, qualityId,
sickRageSettings.ApiKey, sickRageSettings.FullUri);
var result = apiResult.Result;
return result;
}
private async Task<Series> GetSonarrSeries(SonarrSettings sonarrSettings, int showId)
{
var task = await Task.Run(() => SonarrApi.GetSeries(sonarrSettings.ApiKey, sonarrSettings.FullUri)).ConfigureAwait(false);
var selectedSeries = task.FirstOrDefault(series => series.tvdbId == showId);
return selectedSeries;
}
private async Task<string> GetSonarrRootPath(int pathId, SonarrSettings sonarrSettings)
{
var rootFoldersResult = await Cache.GetOrSetAsync(CacheKeys.SonarrRootFolders, async () =>
{
return await Task.Run(() => SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri));
});
foreach (var r in rootFoldersResult.Where(r => r.id == pathId))
{
return r.path;
}
return string.Empty;
}
}
}

View file

@ -0,0 +1,38 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: UserIdentity.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.Collections.Generic;
using Nancy.Security;
namespace Ombi.Core
{
public class UserIdentity : IUserIdentity
{
public string UserName { get; set; }
public IEnumerable<string> Claims { get; set; }
}
}

199
Old/Ombi.Core/UserMapper.cs Normal file
View file

@ -0,0 +1,199 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: UserMapper.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.Security;
using System.Threading.Tasks;
using Nancy;
using Nancy.Authentication.Forms;
using Nancy.Security;
using Ombi.Core.Models;
using Ombi.Helpers;
using Ombi.Store;
using Ombi.Store.Repository;
namespace Ombi.Core
{
public class UserMapper : IUserMapper, ICustomUserMapper
{
public UserMapper(IRepository<UsersModel> repo)
{
Repo = repo;
}
private static IRepository<UsersModel> Repo { get; set; }
public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context)
{
var user = Repo.Get(identifier.ToString());
if (user == null)
{
return null;
}
return new UserIdentity
{
UserName = user.UserName,
};
}
public Guid? ValidateUser(string username, string password)
{
var users = Repo.GetAll();
foreach (var u in users)
{
if (username.Equals(u.UserName, StringComparison.CurrentCultureIgnoreCase))
{
var passwordMatch = PasswordHasher.VerifyPassword(password, u.Salt, u.Hash);
if (passwordMatch)
{
return new Guid(u.UserGuid);
}
}
}
return null;
}
public UsersModel EditUser(UsersModel user)
{
var existingUser = Repo.Get(user.UserGuid);
user.Id = existingUser.Id;
user.UserGuid = existingUser.UserGuid;
Repo.Update(user);
return user;
}
public bool DoUsersExist()
{
var users = Repo.GetAll();
return users.Any();
}
public Guid? CreateUser(string username, string password, UserProperties properties = null)
{
var salt = PasswordHasher.GenerateSalt();
var userModel = new UsersModel
{
UserName = username,
UserGuid = Guid.NewGuid().ToString(),
Salt = salt,
Hash = PasswordHasher.ComputeHash(password, salt),
Claims = new byte[] {0},
UserProperties = ByteConverterHelper.ReturnBytes(properties ?? new UserProperties()),
};
Repo.Insert(userModel);
var userRecord = Repo.Get(userModel.UserGuid);
return new Guid(userRecord.UserGuid);
}
public Guid? CreateUser(string username, string password, int permissions, int features, UserProperties properties = null)
{
var salt = PasswordHasher.GenerateSalt();
var userModel = new UsersModel
{
UserName = username,
UserGuid = Guid.NewGuid().ToString(),
Salt = salt,
Hash = PasswordHasher.ComputeHash(password, salt),
UserProperties = ByteConverterHelper.ReturnBytes(properties ?? new UserProperties()),
Permissions = permissions,
Features = features,
Claims = new byte[] {0}
};
Repo.Insert(userModel);
var userRecord = Repo.Get(userModel.UserGuid);
return new Guid(userRecord.UserGuid);
}
public void DeleteUser(string userId)
{
var user = Repo.Get(userId);
Repo.Delete(user);
}
public bool UpdatePassword(string username, string oldPassword, string newPassword)
{
var users = Repo.GetAll();
var userToChange = users.FirstOrDefault(x => x.UserName == username);
if (userToChange == null)
return false;
var passwordMatch = PasswordHasher.VerifyPassword(oldPassword, userToChange.Salt, userToChange.Hash);
if (!passwordMatch)
{
throw new SecurityException("Incorrect password.");
}
var newSalt = PasswordHasher.GenerateSalt();
var newHash = PasswordHasher.ComputeHash(newPassword, newSalt);
userToChange.Hash = newHash;
userToChange.Salt = newSalt;
return Repo.Update(userToChange);
}
public async Task<IEnumerable<UsersModel>> GetUsersAsync()
{
return await Repo.GetAllAsync();
}
public IEnumerable<UsersModel> GetUsers()
{
return Repo.GetAll();
}
public UsersModel GetUser(Guid userId)
{
var user = Repo.Get(userId.ToString());
return user;
}
}
public interface ICustomUserMapper
{
Guid? CreateUser(string username, string password, int permissions, int features,
UserProperties properties = null);
IEnumerable<UsersModel> GetUsers();
Task<IEnumerable<UsersModel>> GetUsersAsync();
UsersModel GetUser(Guid userId);
UsersModel EditUser(UsersModel user);
bool DoUsersExist();
Guid? ValidateUser(string username, string password);
bool UpdatePassword(string username, string oldPassword, string newPassword);
void DeleteUser(string userId);
}
}

View file

@ -0,0 +1,13 @@
using System.Collections.Generic;
using Ombi.Helpers.Permissions;
namespace Ombi.Core.Users
{
public interface IUserHelper
{
IEnumerable<UserHelperModel> GetUsers();
IEnumerable<UserHelperModel> GetUsersWithPermission(Permissions permission);
IEnumerable<UserHelperModel> GetUsersWithFeature(Features feature);
UserHelperModel GetUser(string username);
}
}

View file

@ -0,0 +1,255 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: UserHelper.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.Collections.Generic;
using System.Linq;
using Ombi.Core.Models;
using Ombi.Helpers;
using Ombi.Helpers.Permissions;
using Ombi.Store.Models.Emby;
using Ombi.Store.Models.Plex;
using Ombi.Store.Repository;
namespace Ombi.Core.Users
{
public class UserHelper : IUserHelper
{
public UserHelper(IUserRepository userRepository, IExternalUserRepository<PlexUsers> plexUsers, IExternalUserRepository<EmbyUsers> emby, ISecurityExtensions security)
{
LocalUserRepository = userRepository;
PlexUserRepository = plexUsers;
Security = security;
EmbyUserRepository = emby;
}
private IUserRepository LocalUserRepository { get; }
private IExternalUserRepository<PlexUsers> PlexUserRepository { get; }
private ISecurityExtensions Security { get; }
private IExternalUserRepository<EmbyUsers> EmbyUserRepository { get; }
public UserHelperModel GetUser(string username)
{
var localUsers = LocalUserRepository.GetUserByUsername(username);
if (localUsers != null)
{
var props = ByteConverterHelper.ReturnObject<UserProperties>(localUsers.UserProperties);
return new UserHelperModel
{
Type = UserType.LocalUser,
Username = localUsers.UserName,
UserAlias = props.UserAlias,
EmailAddress = props.EmailAddress,
Permissions = (Permissions) localUsers.Permissions,
UserId = localUsers.UserGuid,
Features = (Features)localUsers.Features
};
}
var plexUsers = PlexUserRepository.GetUserByUsername(username);
if (plexUsers != null)
{
return new UserHelperModel
{
Type = UserType.PlexUser,
Username = plexUsers.Username,
UserAlias = plexUsers.UserAlias,
EmailAddress = plexUsers.EmailAddress,
Permissions = (Permissions)plexUsers.Permissions,
UserId = plexUsers.PlexUserId,
Features = (Features)plexUsers.Features
};
}
var embyUsers = EmbyUserRepository.GetUserByUsername(username);
if (embyUsers != null)
{
return new UserHelperModel
{
Type = UserType.EmbyUser,
Username = embyUsers.Username,
UserAlias = embyUsers.UserAlias,
EmailAddress = embyUsers.EmailAddress,
Permissions = (Permissions)embyUsers.Permissions,
UserId = embyUsers.EmbyUserId,
Features = (Features)embyUsers.Features
};
}
return null;
}
public IEnumerable<UserHelperModel> GetUsers()
{
var model = new List<UserHelperModel>();
var localUsers = LocalUserRepository.GetAll();
var plexUsers = PlexUserRepository.GetAll().ToList();
var embyUsers = EmbyUserRepository.GetAll().ToList();
foreach (var user in localUsers)
{
var props = ByteConverterHelper.ReturnObject<UserProperties>(user.UserProperties);
model.Add(new UserHelperModel
{
Type = UserType.LocalUser,
Username = user.UserName,
UserAlias = props.UserAlias,
EmailAddress = props.EmailAddress,
Permissions = (Permissions)user.Permissions
});
}
if (plexUsers.Any())
{
model.AddRange(plexUsers.Select(user => new UserHelperModel
{
Type = UserType.PlexUser,
Username = user.Username,
UserAlias = user.UserAlias,
EmailAddress = user.EmailAddress,
Permissions = (Permissions) user.Permissions
}));
}
if (embyUsers.Any())
{
model.AddRange(embyUsers.Select(user => new UserHelperModel
{
Type = UserType.EmbyUser,
Username = user.Username,
UserAlias = user.UserAlias,
EmailAddress = user.EmailAddress,
Permissions = (Permissions)user.Permissions
}));
}
return model;
}
public IEnumerable<UserHelperModel> GetUsersWithPermission(Permissions permission)
{
var model = new List<UserHelperModel>();
var localUsers = LocalUserRepository.GetAll().ToList();
var plexUsers = PlexUserRepository.GetAll().ToList();
var embyUsers = EmbyUserRepository.GetAll().ToList();
var filteredLocal = localUsers.Where(x => ((Permissions)x.Permissions).HasFlag(permission));
var filteredPlex = plexUsers.Where(x => ((Permissions)x.Permissions).HasFlag(permission));
var filteredEmby = embyUsers.Where(x => ((Permissions)x.Permissions).HasFlag(permission));
foreach (var user in filteredLocal)
{
var props = ByteConverterHelper.ReturnObject<UserProperties>(user.UserProperties);
model.Add(new UserHelperModel
{
Type = UserType.LocalUser,
Username = user.UserName,
UserAlias = props.UserAlias,
EmailAddress = props.EmailAddress,
Permissions = (Permissions)user.Permissions,
Features = (Features)user.Features
});
}
model.AddRange(filteredPlex.Select(user => new UserHelperModel
{
Type = UserType.PlexUser,
Username = user.Username,
UserAlias = user.UserAlias,
EmailAddress = user.EmailAddress,
Permissions = (Permissions)user.Permissions,
Features = (Features)user.Features
}));
model.AddRange(filteredEmby.Select(user => new UserHelperModel
{
Type = UserType.EmbyUser,
Username = user.Username,
UserAlias = user.UserAlias,
EmailAddress = user.EmailAddress,
Permissions = (Permissions)user.Permissions,
Features = (Features)user.Features
}));
return model;
}
public IEnumerable<UserHelperModel> GetUsersWithFeature(Features features)
{
var model = new List<UserHelperModel>();
var localUsers = LocalUserRepository.GetAll().ToList();
var plexUsers = PlexUserRepository.GetAll().ToList();
var embyUsers = EmbyUserRepository.GetAll().ToList();
var filteredLocal = localUsers.Where(x => ((Features)x.Features).HasFlag(features));
var filteredPlex = plexUsers.Where(x => ((Features)x.Features).HasFlag(features));
var filteredEmby = embyUsers.Where(x => ((Features)x.Features).HasFlag(features));
foreach (var user in filteredLocal)
{
var props = ByteConverterHelper.ReturnObject<UserProperties>(user.UserProperties);
model.Add(new UserHelperModel
{
Type = UserType.LocalUser,
Username = user.UserName,
UserAlias = props.UserAlias,
EmailAddress = props.EmailAddress,
Permissions = (Permissions)user.Permissions,
Features = (Features)user.Features
});
}
model.AddRange(filteredPlex.Select(user => new UserHelperModel
{
Type = UserType.PlexUser,
Username = user.Username,
UserAlias = user.UserAlias,
EmailAddress = user.EmailAddress,
Permissions = (Permissions)user.Permissions,
Features = (Features)user.Features
}));
model.AddRange(filteredEmby.Select(user => new UserHelperModel
{
Type = UserType.EmbyUser,
Username = user.Username,
UserAlias = user.UserAlias,
EmailAddress = user.EmailAddress,
Permissions = (Permissions)user.Permissions,
Features = (Features)user.Features
}));
return model;
}
}
}

View file

@ -0,0 +1,47 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: UserHelperModel.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 Newtonsoft.Json;
using Ombi.Helpers;
using Ombi.Helpers.Permissions;
namespace Ombi.Core.Users
{
public class UserHelperModel
{
public string Username { get; set; }
public string UserAlias { get; set; }
public Permissions Permissions { get; set; }
public Features Features { get; set; }
public string EmailAddress { get; set; }
public UserType Type { get; set; }
public string UserId { get; set; }
[JsonIgnore]
public string UsernameOrAlias => string.IsNullOrEmpty(UserAlias) ? Username : UserAlias;
}
}

View file

@ -0,0 +1,74 @@
using Ombi.Core.SettingModels;
using Ombi.Helpers.Permissions;
namespace Ombi.Core.Users
{
public static class UserManagementHelper
{
public static int GetPermissions(UserManagementSettings settings)
{
var permission = 0;
if (settings.AutoApproveMovies)
{
permission += (int)Permissions.AutoApproveMovie;
}
if (settings.AutoApproveMusic)
{
permission += (int)Permissions.AutoApproveAlbum;
}
if (settings.AutoApproveTvShows)
{
permission += (int)Permissions.AutoApproveTv;
}
if (settings.RequestMovies)
{
permission += (int)Permissions.RequestMovie;
}
if (settings.RequestMusic)
{
permission += (int)Permissions.RequestMusic;
}
if (settings.RequestTvShows)
{
permission += (int)Permissions.RequestTvShow;
}
if (settings.ReportIssues)
{
permission += (int)Permissions.ReportIssue;
}
if (settings.UsersCanViewOnlyOwnRequests)
{
permission += (int)Permissions.UsersCanViewOnlyOwnRequests;
}
if (settings.UsersCanViewOnlyOwnIssues)
{
permission += (int)Permissions.UsersCanViewOnlyOwnIssues;
}
if (settings.BypassRequestLimit)
{
permission += (int) Permissions.BypassRequestLimit;
}
return permission;
}
public static int GetFeatures(UserManagementSettings settings)
{
var features = 0;
if (settings.RecentlyAddedNewsletter)
{
features += (int)Features.Newsletter;
}
if (settings.RecentlyAddedNotification)
{
features += (int)Features.RequestAddedNotification;
}
return features;
}
}
}

11
Old/Ombi.Core/app.config Normal file
View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /></startup></configuration>

View file

@ -0,0 +1,361 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://quartznet.sourceforge.net/JobSchedulingData"
targetNamespace="http://quartznet.sourceforge.net/JobSchedulingData"
elementFormDefault="qualified"
version="2.0">
<xs:element name="job-scheduling-data">
<xs:annotation>
<xs:documentation>Root level node</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence maxOccurs="unbounded">
<xs:element name="pre-processing-commands" type="pre-processing-commandsType" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>Commands to be executed before scheduling the jobs and triggers in this file.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="processing-directives" type="processing-directivesType" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>Directives to be followed while scheduling the jobs and triggers in this file.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="schedule" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence maxOccurs="unbounded">
<xs:element name="job" type="job-detailType" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="trigger" type="triggerType" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="version" type="xs:string">
<xs:annotation>
<xs:documentation>Version of the XML Schema instance</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
<xs:complexType name="pre-processing-commandsType">
<xs:sequence maxOccurs="unbounded">
<xs:element name="delete-jobs-in-group" type="xs:string" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>Delete all jobs, if any, in the identified group. "*" can be used to identify all groups. Will also result in deleting all triggers related to the jobs.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="delete-triggers-in-group" type="xs:string" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>Delete all triggers, if any, in the identified group. "*" can be used to identify all groups. Will also result in deletion of related jobs that are non-durable.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="delete-job" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>Delete the identified job if it exists (will also result in deleting all triggers related to it).</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="group" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="delete-trigger" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>Delete the identified trigger if it exists (will also result in deletion of related jobs that are non-durable).</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="group" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="processing-directivesType">
<xs:sequence>
<xs:element name="overwrite-existing-data" type="xs:boolean" minOccurs="0" default="true">
<xs:annotation>
<xs:documentation>Whether the existing scheduling data (with same identifiers) will be overwritten. If false, and ignore-duplicates is not false, and jobs or triggers with the same names already exist as those in the file, an error will occur.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="ignore-duplicates" type="xs:boolean" minOccurs="0" default="false">
<xs:annotation>
<xs:documentation>If true (and overwrite-existing-data is false) then any job/triggers encountered in this file that have names that already exist in the scheduler will be ignored, and no error will be produced.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="schedule-trigger-relative-to-replaced-trigger" type="xs:boolean" minOccurs="0" default="false">
<xs:annotation>
<xs:documentation>If true trigger's start time is calculated based on earlier run time instead of fixed value. Trigger's start time must be undefined for this to work.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="job-detailType">
<xs:annotation>
<xs:documentation>Define a JobDetail</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="group" type="xs:string" minOccurs="0" />
<xs:element name="description" type="xs:string" minOccurs="0" />
<xs:element name="job-type" type="xs:string" />
<xs:sequence minOccurs="0">
<xs:element name="durable" type="xs:boolean" />
<xs:element name="recover" type="xs:boolean" />
</xs:sequence>
<xs:element name="job-data-map" type="job-data-mapType" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="job-data-mapType">
<xs:annotation>
<xs:documentation>Define a JobDataMap</xs:documentation>
</xs:annotation>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="entry" type="entryType" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="entryType">
<xs:annotation>
<xs:documentation>Define a JobDataMap entry</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="key" type="xs:string" />
<xs:element name="value" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="triggerType">
<xs:annotation>
<xs:documentation>Define a Trigger</xs:documentation>
</xs:annotation>
<xs:choice>
<xs:element name="simple" type="simpleTriggerType" />
<xs:element name="cron" type="cronTriggerType" />
<xs:element name="calendar-interval" type="calendarIntervalTriggerType" />
</xs:choice>
</xs:complexType>
<xs:complexType name="abstractTriggerType" abstract="true">
<xs:annotation>
<xs:documentation>Common Trigger definitions</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="group" type="xs:string" minOccurs="0" />
<xs:element name="description" type="xs:string" minOccurs="0" />
<xs:element name="job-name" type="xs:string" />
<xs:element name="job-group" type="xs:string" minOccurs="0" />
<xs:element name="priority" type="xs:nonNegativeInteger" minOccurs="0" />
<xs:element name="calendar-name" type="xs:string" minOccurs="0" />
<xs:element name="job-data-map" type="job-data-mapType" minOccurs="0" />
<xs:sequence minOccurs="0">
<xs:choice>
<xs:element name="start-time" type="xs:dateTime" />
<xs:element name="start-time-seconds-in-future" type="xs:nonNegativeInteger" />
</xs:choice>
<xs:element name="end-time" type="xs:dateTime" minOccurs="0" />
</xs:sequence>
</xs:sequence>
</xs:complexType>
<xs:complexType name="simpleTriggerType">
<xs:annotation>
<xs:documentation>Define a SimpleTrigger</xs:documentation>
</xs:annotation>
<xs:complexContent>
<xs:extension base="abstractTriggerType">
<xs:sequence>
<xs:element name="misfire-instruction" type="simple-trigger-misfire-instructionType" minOccurs="0" />
<xs:sequence minOccurs="0">
<xs:element name="repeat-count" type="repeat-countType" />
<xs:element name="repeat-interval" type="xs:nonNegativeInteger" />
</xs:sequence>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="cronTriggerType">
<xs:annotation>
<xs:documentation>Define a CronTrigger</xs:documentation>
</xs:annotation>
<xs:complexContent>
<xs:extension base="abstractTriggerType">
<xs:sequence>
<xs:element name="misfire-instruction" type="cron-trigger-misfire-instructionType" minOccurs="0" />
<xs:element name="cron-expression" type="cron-expressionType" />
<xs:element name="time-zone" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="calendarIntervalTriggerType">
<xs:annotation>
<xs:documentation>Define a DateIntervalTrigger</xs:documentation>
</xs:annotation>
<xs:complexContent>
<xs:extension base="abstractTriggerType">
<xs:sequence>
<xs:element name="misfire-instruction" type="date-interval-trigger-misfire-instructionType" minOccurs="0" />
<xs:element name="repeat-interval" type="xs:nonNegativeInteger" />
<xs:element name="repeat-interval-unit" type="interval-unitType" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:simpleType name="cron-expressionType">
<xs:annotation>
<xs:documentation>
Cron expression (see JavaDoc for examples)
Special thanks to Chris Thatcher (thatcher@butterfly.net) for the regular expression!
Regular expressions are not my strong point but I believe this is complete,
with the caveat that order for expressions like 3-0 is not legal but will pass,
and month and day names must be capitalized.
If you want to examine the correctness look for the [\s] to denote the
seperation of individual regular expressions. This is how I break them up visually
to examine them:
SECONDS:
(
((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)
| (([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))
| ([\?])
| ([\*])
) [\s]
MINUTES:
(
((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)
| (([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))
| ([\?])
| ([\*])
) [\s]
HOURS:
(
((([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?,)*([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?)
| (([\*]|[0-9]|[0-1][0-9]|[2][0-3])/([0-9]|[0-1][0-9]|[2][0-3]))
| ([\?])
| ([\*])
) [\s]
DAY OF MONTH:
(
((([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?,)*([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?(C)?)
| (([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])/([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(C)?)
| (L(-[0-9])?)
| (L(-[1-2][0-9])?)
| (L(-[3][0-1])?)
| (LW)
| ([1-9]W)
| ([1-3][0-9]W)
| ([\?])
| ([\*])
)[\s]
MONTH:
(
((([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?,)*([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?)
| (([1-9]|0[1-9]|1[0-2])/([1-9]|0[1-9]|1[0-2]))
| (((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?,)*(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)
| ((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)/(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))
| ([\?])
| ([\*])
)[\s]
DAY OF WEEK:
(
(([1-7](-([1-7]))?,)*([1-7])(-([1-7]))?)
| ([1-7]/([1-7]))
| (((MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?,)*(MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?(C)?)
| ((MON|TUE|WED|THU|FRI|SAT|SUN)/(MON|TUE|WED|THU|FRI|SAT|SUN)(C)?)
| (([1-7]|(MON|TUE|WED|THU|FRI|SAT|SUN))(L|LW)?)
| (([1-7]|MON|TUE|WED|THU|FRI|SAT|SUN)#([1-7])?)
| ([\?])
| ([\*])
)
YEAR (OPTIONAL):
(
[\s]?
([\*])?
| ((19[7-9][0-9])|(20[0-9][0-9]))?
| (((19[7-9][0-9])|(20[0-9][0-9]))/((19[7-9][0-9])|(20[0-9][0-9])))?
| ((((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?,)*((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?)?
)
</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:pattern
value="(((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)|(([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))|([\?])|([\*]))[\s](((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)|(([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))|([\?])|([\*]))[\s](((([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?,)*([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?)|(([\*]|[0-9]|[0-1][0-9]|[2][0-3])/([0-9]|[0-1][0-9]|[2][0-3]))|([\?])|([\*]))[\s](((([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?,)*([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?(C)?)|(([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])/([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(C)?)|(L(-[0-9])?)|(L(-[1-2][0-9])?)|(L(-[3][0-1])?)|(LW)|([1-9]W)|([1-3][0-9]W)|([\?])|([\*]))[\s](((([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?,)*([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?)|(([1-9]|0[1-9]|1[0-2])/([1-9]|0[1-9]|1[0-2]))|(((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?,)*(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)|((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)/(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))|([\?])|([\*]))[\s]((([1-7](-([1-7]))?,)*([1-7])(-([1-7]))?)|([1-7]/([1-7]))|(((MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?,)*(MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?(C)?)|((MON|TUE|WED|THU|FRI|SAT|SUN)/(MON|TUE|WED|THU|FRI|SAT|SUN)(C)?)|(([1-7]|(MON|TUE|WED|THU|FRI|SAT|SUN))?(L|LW)?)|(([1-7]|MON|TUE|WED|THU|FRI|SAT|SUN)#([1-7])?)|([\?])|([\*]))([\s]?(([\*])?|(19[7-9][0-9])|(20[0-9][0-9]))?| (((19[7-9][0-9])|(20[0-9][0-9]))/((19[7-9][0-9])|(20[0-9][0-9])))?| ((((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?,)*((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?)?)" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="repeat-countType">
<xs:annotation>
<xs:documentation>Number of times to repeat the Trigger (-1 for indefinite)</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:integer">
<xs:minInclusive value="-1" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="simple-trigger-misfire-instructionType">
<xs:annotation>
<xs:documentation>Simple Trigger Misfire Instructions</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:pattern value="SmartPolicy" />
<xs:pattern value="RescheduleNextWithExistingCount" />
<xs:pattern value="RescheduleNextWithRemainingCount" />
<xs:pattern value="RescheduleNowWithExistingRepeatCount" />
<xs:pattern value="RescheduleNowWithRemainingRepeatCount" />
<xs:pattern value="FireNow" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="cron-trigger-misfire-instructionType">
<xs:annotation>
<xs:documentation>Cron Trigger Misfire Instructions</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:pattern value="SmartPolicy" />
<xs:pattern value="DoNothing" />
<xs:pattern value="FireOnceNow" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="date-interval-trigger-misfire-instructionType">
<xs:annotation>
<xs:documentation>Date Interval Trigger Misfire Instructions</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:pattern value="SmartPolicy" />
<xs:pattern value="DoNothing" />
<xs:pattern value="FireOnceNow" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="interval-unitType">
<xs:annotation>
<xs:documentation>Interval Units</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:pattern value="Day" />
<xs:pattern value="Hour" />
<xs:pattern value="Minute" />
<xs:pattern value="Month" />
<xs:pattern value="Second" />
<xs:pattern value="Week" />
<xs:pattern value="Year" />
</xs:restriction>
</xs:simpleType>
</xs:schema>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Common.Logging" version="3.0.0" targetFramework="net45" />
<package id="Common.Logging.Core" version="3.0.0" targetFramework="net45" />
<package id="Dapper" version="1.50.0-beta8" targetFramework="net45" />
<package id="Nancy" version="1.4.3" targetFramework="net45" />
<package id="Nancy.Authentication.Forms" version="1.4.1" targetFramework="net45" />
<package id="Nancy.Linker" version="0.3.1" targetFramework="net45" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
<package id="NLog" version="4.3.6" targetFramework="net45" />
<package id="Octokit" version="0.19.0" targetFramework="net45" />
<package id="Quartz" version="2.3.3" targetFramework="net45" />
<package id="RestSharp" version="105.2.3" targetFramework="net45" />
<package id="valueinjecter" version="3.1.1.2" targetFramework="net45" />
</packages>