Merge pull request #1425 from tidusjar/DotNetCoreStyling

Dot net core styling
pull/1488/head
Jamie 8 years ago committed by GitHub
commit d18d1a2eec

@ -12,7 +12,6 @@ before_build:
- cmd: cd src/ombi - cmd: cd src/ombi
- node --version - node --version
- appveyor-retry dotnet restore - appveyor-retry dotnet restore
- appveyor-retry npm install bower -g
- appveyor-retry npm install -g gulp - appveyor-retry npm install -g gulp
- appveyor-retry npm install - appveyor-retry npm install
- gulp publish - gulp publish

@ -16,7 +16,7 @@ namespace Ombi.Core.Tests.Engine
{ {
RequestService = new Mock<IRequestService<MovieRequestModel>>(); RequestService = new Mock<IRequestService<MovieRequestModel>>();
var requestService = new RequestService(null, RequestService.Object); var requestService = new RequestService(null, RequestService.Object);
Engine = new MovieRequestEngine(null, requestService, null, null, null); Engine = new MovieRequestEngine(null, requestService, null, null, null, null, null);
} }
private MovieRequestEngine Engine { get; } private MovieRequestEngine Engine { get; }

@ -3,6 +3,7 @@ using Moq;
using Ombi.Core.Claims; using Ombi.Core.Claims;
using Ombi.Core.Models.Requests; using Ombi.Core.Models.Requests;
using Ombi.Core.Rule.Rules; using Ombi.Core.Rule.Rules;
using Ombi.Core.Rule.Rules.Request;
using Xunit; using Xunit;
namespace Ombi.Core.Tests.Rule namespace Ombi.Core.Tests.Rule

@ -17,5 +17,6 @@ namespace Ombi.Core
Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies(); Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies();
Task<IEnumerable<SearchMovieViewModel>> LookupImdbInformation(IEnumerable<SearchMovieViewModel> movies); Task<IEnumerable<SearchMovieViewModel>> LookupImdbInformation(IEnumerable<SearchMovieViewModel> movies);
Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId);
} }
} }

@ -96,7 +96,7 @@ namespace Ombi.Core.Engine
Status = movieInfo.Status, Status = movieInfo.Status,
RequestedDate = DateTime.UtcNow, RequestedDate = DateTime.UtcNow,
Approved = false, Approved = false,
RequestedUsers = new List<string> { Username }, RequestedUser =Username,
Issues = IssueState.None Issues = IssueState.None
}; };
@ -191,7 +191,7 @@ namespace Ombi.Core.Engine
results.OtherMessage = request.OtherMessage; results.OtherMessage = request.OtherMessage;
results.Overview = request.Overview; results.Overview = request.Overview;
results.PosterPath = request.PosterPath; results.PosterPath = request.PosterPath;
results.RequestedUsers = request.RequestedUsers?.ToList() ?? new List<string>(); results.RequestedUser = request.RequestedUser;
var model = MovieRequestService.UpdateRequest(results); var model = MovieRequestService.UpdateRequest(results);
return model; return model;
@ -202,21 +202,6 @@ namespace Ombi.Core.Engine
await MovieRequestService.DeleteRequestAsync(requestId); await MovieRequestService.DeleteRequestAsync(requestId);
} }
private IEnumerable<EpisodesModel> GetListDifferences(IEnumerable<EpisodesModel> existing,
IEnumerable<EpisodesModel> request)
{
var newRequest = request
.Select(r =>
new EpisodesModel
{
SeasonNumber = r.SeasonNumber,
EpisodeNumber = r.EpisodeNumber
})
.ToList();
return newRequest.Except(existing);
}
private async Task<RequestEngineResult> AddMovieRequest(MovieRequestModel model, string message) private async Task<RequestEngineResult> AddMovieRequest(MovieRequestModel model, string message)
{ {
await MovieRequestService.AddRequestAsync(model); await MovieRequestService.AddRequestAsync(model);

@ -13,6 +13,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Principal; using System.Security.Principal;
using System.Threading.Tasks; using System.Threading.Tasks;
using StackExchange.Profiling;
namespace Ombi.Core.Engine namespace Ombi.Core.Engine
{ {
@ -63,17 +64,41 @@ namespace Ombi.Core.Engine
return retVal; return retVal;
} }
public async Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId)
{
var dbMovies = await GetMovieRequests();
var plexSettings = await PlexSettings.GetSettingsAsync();
var embySettings = await EmbySettings.GetSettingsAsync();
var movieInfo = await MovieApi.GetMovieInformationWithVideo(theMovieDbId);
var viewMovie = Mapper.Map<SearchMovieViewModel>(movieInfo);
return await ProcessSingleMovie(viewMovie, dbMovies, plexSettings, embySettings, true);
}
public async Task<IEnumerable<SearchMovieViewModel>> Search(string search) public async Task<IEnumerable<SearchMovieViewModel>> Search(string search)
{ {
var result = await MovieApi.SearchMovie(search); using (MiniProfiler.Current.Step("Starting Movie Search Engine"))
if (result != null) using (MiniProfiler.Current.Step("Searching Movie"))
{ {
Logger.LogDebug("Search Result: {result}", result); var result = await MovieApi.SearchMovie(search);
return await TransformMovieResultsToResponse(result);
using (MiniProfiler.Current.Step("Fin API, Transforming"))
{
if (result != null)
{
Logger.LogDebug("Search Result: {result}", result);
return await TransformMovieResultsToResponse(result);
}
}
return null;
} }
return null;
} }
public async Task<IEnumerable<SearchMovieViewModel>> PopularMovies() public async Task<IEnumerable<SearchMovieViewModel>> PopularMovies()
{ {
var result = await MovieApi.PopularMovies(); var result = await MovieApi.PopularMovies();
@ -121,28 +146,41 @@ namespace Ombi.Core.Engine
private async Task<List<SearchMovieViewModel>> TransformMovieResultsToResponse( private async Task<List<SearchMovieViewModel>> TransformMovieResultsToResponse(
IEnumerable<MovieSearchResult> movies) IEnumerable<MovieSearchResult> movies)
{ {
var viewMovies = new List<SearchMovieViewModel>(); var viewMovies = new List<SearchMovieViewModel>();
var dbMovies = await GetMovieRequests(); Dictionary<int, MovieRequestModel> dbMovies;
Settings.Models.External.PlexSettings plexSettings;
Settings.Models.External.EmbySettings embySettings;
using (MiniProfiler.Current.Step("Gettings Movies and Settings"))
{
dbMovies = await GetMovieRequests();
var plexSettings = await PlexSettings.GetSettingsAsync(); plexSettings = await PlexSettings.GetSettingsAsync();
var embySettings = await EmbySettings.GetSettingsAsync(); embySettings = await EmbySettings.GetSettingsAsync();
}
foreach (var movie in movies) foreach (var movie in movies)
{
viewMovies.Add(await ProcessSingleMovie(movie, dbMovies, plexSettings, embySettings)); viewMovies.Add(await ProcessSingleMovie(movie, dbMovies, plexSettings, embySettings));
}
return viewMovies; return viewMovies;
} }
private async Task<SearchMovieViewModel> ProcessSingleMovie(SearchMovieViewModel viewMovie, private async Task<SearchMovieViewModel> ProcessSingleMovie(SearchMovieViewModel viewMovie,
Dictionary<int, MovieRequestModel> existingRequests, PlexSettings plexSettings, EmbySettings embySettings) Dictionary<int, MovieRequestModel> existingRequests, PlexSettings plexSettings, EmbySettings embySettings, bool lookupExtraInfo = false)
{ {
var showInfo = await MovieApi.GetMovieInformation(viewMovie.Id);
viewMovie.Id = showInfo.Id; // TheMovieDbId
if (plexSettings.Enable) if (plexSettings.Enable)
{ {
var item = await PlexContentRepo.Get(showInfo.ImdbId); if (lookupExtraInfo)
if (item != null)
{ {
viewMovie.Available = true; var showInfo = await MovieApi.GetMovieInformation(viewMovie.Id);
viewMovie.PlexUrl = item.Url; viewMovie.Id = showInfo.Id; // TheMovieDbId
var item = await PlexContentRepo.Get(showInfo.ImdbId);
if (item != null)
{
viewMovie.Available = true;
viewMovie.PlexUrl = item.Url;
}
} }
// var content = PlexContentRepository.GetAll(); // var content = PlexContentRepository.GetAll();
@ -180,10 +218,11 @@ namespace Ombi.Core.Engine
} }
RunSearchRules(viewMovie); RunSearchRules(viewMovie);
return viewMovie; return viewMovie;
} }
private async Task<SearchMovieViewModel> ProcessSingleMovie(MovieSearchResult movie, private async Task<SearchMovieViewModel> ProcessSingleMovie(MovieSearchResult movie,
Dictionary<int, MovieRequestModel> existingRequests, PlexSettings plexSettings, EmbySettings embySettings) Dictionary<int, MovieRequestModel> existingRequests, PlexSettings plexSettings, EmbySettings embySettings)
{ {

@ -41,6 +41,25 @@ namespace Ombi.Core.Engine
// For some reason the poster path is always http // For some reason the poster path is always http
var posterPath = showInfo.image?.medium.Replace("http:", "https:"); var posterPath = showInfo.image?.medium.Replace("http:", "https:");
var tvRequests = new List<SeasonRequestModel>();
// Only have the TV requests we actually requested and not everything
foreach (var season in tv.SeasonRequests)
{
for (int i = season.Episodes.Count - 1; i >= 0; i--)
{
if (!season.Episodes[i].Requested)
{
season.Episodes.RemoveAt(i); // Remove the episode since it's not requested
}
}
if (season.Episodes.Any())
{
tvRequests.Add(season);
}
}
var childRequest = new ChildTvRequest var childRequest = new ChildTvRequest
{ {
Id = tv.Id, Id = tv.Id,
@ -52,11 +71,11 @@ namespace Ombi.Core.Engine
Status = showInfo.status, Status = showInfo.status,
RequestedDate = DateTime.UtcNow, RequestedDate = DateTime.UtcNow,
Approved = false, Approved = false,
RequestedUsers = new List<string> { Username }, RequestedUser = Username,
Issues = IssueState.None, Issues = IssueState.None,
ProviderId = tv.Id, ProviderId = tv.Id,
RequestAll = tv.RequestAll, RequestAll = tv.RequestAll,
SeasonRequests = tv.SeasonRequests SeasonRequests = tvRequests
}; };
var model = new TvRequestModel var model = new TvRequestModel
@ -76,22 +95,22 @@ namespace Ombi.Core.Engine
model.ChildRequests.Add(childRequest); model.ChildRequests.Add(childRequest);
if (childRequest.SeasonRequests.Any()) //if (childRequest.SeasonRequests.Any())
{ //{
var episodes = await TvApi.EpisodeLookup(showInfo.id); // var episodes = await TvApi.EpisodeLookup(showInfo.id);
foreach (var e in episodes) // foreach (var e in episodes)
{ // {
var season = childRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == e.season); // var season = childRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == e.season);
season?.Episodes.Add(new EpisodesRequested // season?.Episodes.Add(new EpisodesRequested
{ // {
Url = e.url, // Url = e.url,
Title = e.name, // Title = e.name,
AirDate = DateTime.Parse(e.airstamp), // AirDate = DateTime.Parse(e.airstamp),
EpisodeNumber = e.number // EpisodeNumber = e.number
}); // });
} // }
} //}
if (tv.LatestSeason) if (tv.LatestSeason)
{ {
@ -165,6 +184,8 @@ namespace Ombi.Core.Engine
var results = allRequests.FirstOrDefault(x => x.Id == request.Id); var results = allRequests.FirstOrDefault(x => x.Id == request.Id);
results = Mapper.Map<TvRequestModel>(request); results = Mapper.Map<TvRequestModel>(request);
// TODO need to check if we need to approve any child requests since they may have updated
var model = TvRequestService.UpdateRequest(results); var model = TvRequestService.UpdateRequest(results);
return model; return model;
} }
@ -192,26 +213,52 @@ namespace Ombi.Core.Engine
existingRequest.ChildRequests.AddRange(newRequest.ChildRequests); existingRequest.ChildRequests.AddRange(newRequest.ChildRequests);
TvRequestService.UpdateRequest(existingRequest); TvRequestService.UpdateRequest(existingRequest);
if (ShouldAutoApprove(RequestType.TvShow)) if (newRequest.Approved) // The auto approve rule
{ {
// TODO Auto Approval Code // TODO Auto Approval Code
} }
return await AfterRequest(newRequest); return await AfterRequest(newRequest);
} }
private IEnumerable<SeasonRequestModel> GetListDifferences(IEnumerable<SeasonRequestModel> existing, private IEnumerable<SeasonRequestModel> GetListDifferences(List<SeasonRequestModel> existing,
IEnumerable<SeasonRequestModel> request) List<SeasonRequestModel> request)
{ {
var newRequest = request var requestsToRemove = new List<SeasonRequestModel>();
.Select(r => foreach (var r in request)
new SeasonRequestModel {
// Do we have an existing season?
var existingSeason = existing.FirstOrDefault(x => x.SeasonNumber == r.SeasonNumber);
if (existingSeason == null)
{
continue;
}
// Compare the episodes
for (var i = r.Episodes.Count - 1; i >= 0; i--)
{
var existingEpisode = existingSeason.Episodes.FirstOrDefault(x => x.EpisodeNumber == r.Episodes[i].EpisodeNumber);
if (existingEpisode == null)
{
// we are fine, we have not yet requested this
}
else
{ {
SeasonNumber = r.SeasonNumber, // We already have this request
Episodes = r.Episodes r.Episodes.RemoveAt(i);
}) }
.ToList(); }
if (!r.Episodes.Any())
{
requestsToRemove.Add(r);
}
}
return newRequest.Except(existing); foreach (var remove in requestsToRemove)
{
request.Remove(remove);
}
return request;
} }
private async Task<RequestEngineResult> AddRequest(TvRequestModel model) private async Task<RequestEngineResult> AddRequest(TvRequestModel model)

@ -77,13 +77,12 @@ namespace Ombi.Core.Engine
} }
else else
{ {
season.Episodes.Add(new EpisodesRequested // Find the episode
{ var ep = season.Episodes.FirstOrDefault(x => x.EpisodeNumber == e.number);
Url = e.url, ep.Url = e.url;
Title = e.name, ep.Title = e.name;
AirDate = DateTime.Parse(e.airstamp), ep.AirDate = DateTime.Parse(e.airstamp);
EpisodeNumber = e.number, ep.EpisodeNumber = e.number;
});
} }
} }
@ -173,6 +172,7 @@ namespace Ombi.Core.Engine
// Find the existing request season // Find the existing request season
var existingSeason = var existingSeason =
existingRequestChildRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == season.SeasonNumber); existingRequestChildRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == season.SeasonNumber);
if(existingSeason == null) continue;
foreach (var ep in existingSeason.Episodes) foreach (var ep in existingSeason.Episodes)
{ {

@ -1,8 +1,6 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using System; using System;
using System.Collections.Generic;
using System.Linq;
namespace Ombi.Core.Models.Requests namespace Ombi.Core.Models.Requests
{ {
@ -22,7 +20,7 @@ namespace Ombi.Core.Models.Requests
public IssueState Issues { get; set; } public IssueState Issues { get; set; }
public string OtherMessage { get; set; } public string OtherMessage { get; set; }
public string AdminNote { get; set; } public string AdminNote { get; set; }
public List<string> RequestedUsers { get; set; } = new List<string>(); public string RequestedUser { get; set; }
public int IssueId { get; set; } public int IssueId { get; set; }
public bool Denied { get; set; } public bool Denied { get; set; }
public string DeniedReason { get; set; } public string DeniedReason { get; set; }
@ -30,24 +28,13 @@ namespace Ombi.Core.Models.Requests
[JsonIgnore] [JsonIgnore]
public bool Released => DateTime.UtcNow > ReleaseDate; public bool Released => DateTime.UtcNow > ReleaseDate;
[JsonIgnore]
public IEnumerable<string> AllUsers
{
get
{
var u = new List<string>();
if (RequestedUsers != null && RequestedUsers.Any())
u.AddRange(RequestedUsers);
return u;
}
}
[JsonIgnore] [JsonIgnore]
public bool CanApprove => !Approved && !Available; public bool CanApprove => !Approved && !Available;
public bool UserHasRequested(string username) public bool UserHasRequested(string username)
{ {
return AllUsers.Any(x => x.Equals(username, StringComparison.OrdinalIgnoreCase)); return RequestedUser.Equals(username, StringComparison.OrdinalIgnoreCase);
} }
} }
} }

@ -66,7 +66,7 @@ namespace Ombi.Core
//var rootFolderPath = model.RootFolderSelected <= 0 ? settings.FullRootPath : GetRootPath(model.RootFolderSelected, settings); //var rootFolderPath = model.RootFolderSelected <= 0 ? settings.FullRootPath : GetRootPath(model.RootFolderSelected, settings);
var rootFolderPath = settings.DefaultRootPath; // TODO Allow changing in the UI var rootFolderPath = settings.DefaultRootPath; // TODO Allow changing in the UI
var result = await RadarrApi.AddMovie(model.ProviderId, model.Title, model.ReleaseDate.Year, qualityProfile, rootFolderPath, settings.ApiKey, settings.FullUri, true); var result = await RadarrApi.AddMovie(model.ProviderId, model.Title, model.ReleaseDate.Year, qualityProfile, rootFolderPath, settings.ApiKey, settings.FullUri, !settings.AddOnly);
if (!string.IsNullOrEmpty(result.Error?.message)) if (!string.IsNullOrEmpty(result.Error?.message))
{ {

@ -12,6 +12,7 @@
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="1.1.1" /> <PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="1.1.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="1.1.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="1.1.1" />
<PackageReference Include="MiniProfiler.AspNetCore" Version="4.0.0-alpha6-79" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
</ItemGroup> </ItemGroup>

@ -1,11 +1,10 @@
using Ombi.Core.Claims; using System.Security.Principal;
using Ombi.Core.Claims;
using Ombi.Core.Models.Requests; using Ombi.Core.Models.Requests;
using Ombi.Core.Rules;
using Ombi.Store.Entities;
using System.Security.Principal;
using Ombi.Core.Rule.Interfaces; using Ombi.Core.Rule.Interfaces;
using Ombi.Store.Entities;
namespace Ombi.Core.Rule.Rules namespace Ombi.Core.Rule.Rules.Request
{ {
public class AutoApproveRule : BaseRequestRule, IRequestRules<BaseRequestModel> public class AutoApproveRule : BaseRequestRule, IRequestRules<BaseRequestModel>
{ {

@ -8,7 +8,7 @@ using Ombi.Notifications;
using Ombi.Notifications.Models; using Ombi.Notifications.Models;
using Ombi.Settings.Settings.Models.Notifications; using Ombi.Settings.Settings.Models.Notifications;
namespace Ombi.Notification.Discord namespace Ombi.Notification.Agents
{ {
public class DiscordNotification : BaseNotification<DiscordNotificationSettings> public class DiscordNotification : BaseNotification<DiscordNotificationSettings>
{ {

@ -0,0 +1,227 @@
using System;
using System.Threading.Tasks;
using MailKit.Net.Smtp;
using MimeKit;
using Ombi.Core.Models.Requests;
using Ombi.Core.Settings;
using Ombi.Notifications.Models;
using Ombi.Notifications.Templates;
using Ombi.Settings.Settings.Models.Notifications;
namespace Ombi.Notifications.Email
{
public class EmailNotification : BaseNotification<EmailNotificationSettings>
{
public EmailNotification(ISettingsService<EmailNotificationSettings> settings) : base(settings)
{
}
public override string NotificationName => nameof(EmailNotification);
protected override bool ValidateConfiguration(EmailNotificationSettings settings)
{
if (!settings.Enabled)
{
return false;
}
if (settings.Authentication)
{
if (string.IsNullOrEmpty(settings.EmailUsername) || string.IsNullOrEmpty(settings.EmailPassword))
{
return false;
}
}
if (string.IsNullOrEmpty(settings.EmailHost) || string.IsNullOrEmpty(settings.RecipientEmail) || string.IsNullOrEmpty(settings.EmailPort.ToString()))
{
return false;
}
return true;
}
protected override async Task NewRequest(NotificationModel model, EmailNotificationSettings settings)
{
var email = new EmailBasicTemplate();
var html = email.LoadTemplate(
$"Ombi: New {model.RequestType.GetString()?.ToLower()} request for {model.Title}!",
$"Hello! The user '{model.User}' has requested the {model.RequestType.GetString()?.ToLower()} '{model.Title}'! Please log in to approve this request. Request Date: {model.DateTime:f}",
model.ImgSrc);
var message = new NotificationMessage
{
Message = html,
Subject = $"Ombi: New {model.RequestType.GetString()?.ToLower()} request for {model.Title}!",
To = settings.RecipientEmail,
};
message.Other.Add("PlainTextBody", $"Hello! The user '{model.User}' has requested the {model.RequestType.GetString()?.ToLower()} '{model.Title}'! Please log in to approve this request. Request Date: {model.DateTime:f}");
await Send(message, settings);
}
protected override async Task Issue(NotificationModel model, EmailNotificationSettings settings)
{
var email = new EmailBasicTemplate();
var html = email.LoadTemplate(
$"Ombi: New issue for {model.Title}!",
$"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!",
model.ImgSrc);
var message = new NotificationMessage
{
Message = html,
Subject = $"Ombi: New issue for {model.Title}!",
To = settings.RecipientEmail,
};
message.Other.Add("PlainTextBody", $"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!");
await Send(message, settings);
}
protected override async Task AddedToRequestQueue(NotificationModel model, EmailNotificationSettings settings)
{
var email = new EmailBasicTemplate();
var html = email.LoadTemplate(
"Ombi: A request could not be added.",
$"Hello! The user '{model.User}' has requested {model.Title} but it could not be added. This has been added into the requests queue and will keep retrying",
model.ImgSrc);
var message = new NotificationMessage
{
Message = html,
Subject = $"Ombi: A request could not be added",
To = settings.RecipientEmail,
};
message.Other.Add("PlainTextBody", $"Hello! The user '{model.User}' has requested {model.Title} but it could not be added. This has been added into the requests queue and will keep retrying");
await Send(message, settings);
}
protected override async Task RequestDeclined(NotificationModel model, EmailNotificationSettings settings)
{
var email = new EmailBasicTemplate();
var html = email.LoadTemplate(
"Ombi: Your request has been declined",
$"Hello! Your request for {model.Title} has been declined, Sorry!",
model.ImgSrc);
var message = new NotificationMessage
{
Message = html,
Subject = $"Ombi: Your request has been declined",
To = model.UserEmail,
};
message.Other.Add("PlainTextBody", $"Hello! Your request for {model.Title} has been declined, Sorry!");
await Send(message, settings);
}
protected override async Task RequestApproved(NotificationModel model, EmailNotificationSettings settings)
{
var email = new EmailBasicTemplate();
var html = email.LoadTemplate(
"Ombi: Your request has been approved!",
$"Hello! Your request for {model.Title} has been approved!",
model.ImgSrc);
var message = new NotificationMessage
{
Message = html,
Subject = $"Ombi: Your request has been approved!",
To = model.UserEmail,
};
message.Other.Add("PlainTextBody", $"Hello! Your request for {model.Title} has been approved!");
await Send(message, settings);
}
protected override async Task AvailableRequest(NotificationModel model, EmailNotificationSettings settings)
{
var email = new EmailBasicTemplate();
var html = email.LoadTemplate(
$"Ombi: {model.Title} is now available!",
$"Hello! You requested {model.Title} on Ombi! This is now available on Plex! :)",
model.ImgSrc);
var message = new NotificationMessage
{
Message = html,
Subject = $"Ombi: {model.Title} is now available!",
To = model.UserEmail,
};
message.Other.Add("PlainTextBody", $"Hello! You requested {model.Title} on Ombi! This is now available on Plex! :)");
await Send(message, settings);
}
protected override async Task Send(NotificationMessage model, EmailNotificationSettings settings)
{
try
{
var body = new BodyBuilder
{
HtmlBody = model.Message,
TextBody = model.Other["PlainTextBody"]
};
var message = new MimeMessage
{
Body = body.ToMessageBody(),
Subject = model.Subject
};
message.From.Add(new MailboxAddress(settings.EmailSender, settings.EmailSender));
message.To.Add(new MailboxAddress(model.To, model.To));
using (var client = new SmtpClient())
{
client.Connect(settings.EmailHost, settings.EmailPort); // Let MailKit figure out the correct SecureSocketOptions.
// Note: since we don't have an OAuth2 token, disable
// the XOAUTH2 authentication mechanism.
client.AuthenticationMechanisms.Remove("XOAUTH2");
if (settings.Authentication)
{
client.Authenticate(settings.EmailUsername, settings.EmailPassword);
}
//Log.Info("sending message to {0} \r\n from: {1}\r\n Are we authenticated: {2}", message.To, message.From, client.IsAuthenticated);
await client.SendAsync(message);
await client.DisconnectAsync(true);
}
}
catch (Exception e)
{
//Log.Error(e);
throw new InvalidOperationException(e.Message);
}
}
protected override async Task Test(NotificationModel model, EmailNotificationSettings settings)
{
var email = new EmailBasicTemplate();
var html = email.LoadTemplate(
"Test Message",
"This is just a test! Success!",
model.ImgSrc);
var message = new NotificationMessage
{
Message = html,
Subject = $"Ombi: Test",
To = settings.RecipientEmail,
};
message.Other.Add("PlainTextBody", "This is just a test! Success!");
await Send(message, settings);
}
}
}

@ -6,7 +6,10 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Ombi.Api.Discord\Ombi.Api.Discord.csproj" /> <ProjectReference Include="..\Ombi.Api.Discord\Ombi.Api.Discord.csproj" />
<ProjectReference Include="..\Ombi.Core\Ombi.Core.csproj" />
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
<ProjectReference Include="..\Ombi.Notifications\Ombi.Notifications.csproj" /> <ProjectReference Include="..\Ombi.Notifications\Ombi.Notifications.csproj" />
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

@ -5,6 +5,11 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MailKit" Version="1.16.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ombi.Notifications.Templates\Ombi.Notifications.Templates.csproj" />
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" /> <ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
<ProjectReference Include="..\Ombi.Store\Ombi.Store.csproj" /> <ProjectReference Include="..\Ombi.Store\Ombi.Store.csproj" />
</ItemGroup> </ItemGroup>

@ -77,45 +77,6 @@ namespace Ombi.Schedule.Jobs
Logger.LogWarning(LoggingEvents.CacherException, e, "Exception thrown when attempting to cache the Plex Content"); Logger.LogWarning(LoggingEvents.CacherException, e, "Exception thrown when attempting to cache the Plex Content");
} }
} }
//private List<PlexLibraries> CachedLibraries(PlexSettings plexSettings)
//{
// var results = new List<PlexLibraries>();
// results = GetLibraries(plexSettings);
// foreach (PlexLibraries t in results)
// {
// foreach (var t1 in t.MediaContainer.Directory)
// {
// var currentItem = t1;
// var metaData = PlexApi.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri,
// currentItem.ratingKey).Result;
// // Get the seasons for each show
// if (currentItem.type.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase))
// {
// var seasons = PlexApi.GetSeasons(plexSettings.PlexAuthToken, plexSettings.FullUri,
// currentItem.ratingKey).Result;
// // We do not want "all episodes" this as a season
// var filtered = seasons.MediaContainer.Directory.Where(x => !x.title.Equals("All episodes", StringComparison.CurrentCultureIgnoreCase));
// t1.seasons.AddRange(filtered);
// }
// var providerId = PlexHelper.GetProviderIdFromPlexGuid(metaData.MediaContainer);
// t1.providerId = providerId;
// }
// foreach (Video t1 in t.Video)
// {
// var currentItem = t1;
// var metaData = PlexApi.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri,
// currentItem.RatingKey);
// var providerId = PlexHelper.GetProviderIdFromPlexGuid(metaData.Video.Guid);
// t1.ProviderId = providerId;
// }
// }
//}
private async Task StartTheCache(PlexSettings plexSettings) private async Task StartTheCache(PlexSettings plexSettings)
{ {
@ -154,18 +115,16 @@ namespace Ombi.Schedule.Jobs
var itemAdded = false; var itemAdded = false;
foreach (var season in seasonsContent) foreach (var season in seasonsContent)
{ {
var seasonExists = existingContent.Seasons.Where(x => x.SeasonKey == season.SeasonKey); var seasonExists = existingContent.Seasons.FirstOrDefault(x => x.SeasonKey == season.SeasonKey);
if (seasonExists != null) if (seasonExists != null)
{ {
// We already have this season // We already have this season
continue; continue;
} }
else
{ existingContent.Seasons.Add(season);
existingContent.Seasons.Add(season); itemAdded = true;
itemAdded = true;
}
} }
if (itemAdded) await Repo.Update(existingContent); if (itemAdded) await Repo.Update(existingContent);

@ -9,5 +9,6 @@ namespace Ombi.Settings.Settings.Models.External
public string DefaultQualityProfile { get; set; } public string DefaultQualityProfile { get; set; }
public string DefaultRootPath { get; set; } public string DefaultRootPath { get; set; }
public string FullRootPath { get; set; } public string FullRootPath { get; set; }
public bool AddOnly { get; set; }
} }
} }

@ -44,7 +44,7 @@ namespace Ombi.Store.Entities
/// <summary> /// <summary>
/// Only used for TV Shows /// Only used for TV Shows
/// </summary> /// </summary>
public virtual ICollection<SeasonsContent> Seasons { get; set; } public virtual ICollection<PlexSeasonsContent> Seasons { get; set; }
/// <summary> /// <summary>
/// Plex's internal ID for this item /// Plex's internal ID for this item
@ -53,7 +53,8 @@ namespace Ombi.Store.Entities
public DateTime AddedAt { get; set; } public DateTime AddedAt { get; set; }
} }
public class SeasonsContent : Entity [Table("PlexSeasonsContent")]
public class PlexSeasonsContent : Entity
{ {
public int PlexContentId { get; set; } public int PlexContentId { get; set; }
public int SeasonNumber { get; set; } public int SeasonNumber { get; set; }

@ -0,0 +1,15 @@
using System;
namespace Ombi.Store.Entities
{
public class RequestHistory : Entity
{
public int UserId { get; set; }
public RequestType Type { get; set; }
public DateTime RequestedDate { get; set; }
public int RequestId { get; set; }
public virtual RequestBlobs Request { get; set; }
public virtual User User { get; set; }
}
}

@ -23,9 +23,10 @@ CREATE TABLE IF NOT EXISTS RadarrCache
TheMovieDbId INTEGER NOT NULL TheMovieDbId INTEGER NOT NULL
); );
CREATE TABLE IF NOT EXISTS SeasonsContent CREATE TABLE IF NOT EXISTS PlexSeasonsContent
( (
Id INTEGER PRIMARY KEY AUTOINCREMENT, Id INTEGER PRIMARY KEY AUTOINCREMENT,
PlexContentId integer not null,
SeasonNumber INTEGER NOT NULL, SeasonNumber INTEGER NOT NULL,
SeasonKey INTEGER NOT NULL, SeasonKey INTEGER NOT NULL,
ParentKey INTEGER NOT NULL ParentKey INTEGER NOT NULL
@ -52,4 +53,14 @@ CREATE TABLE IF NOT EXISTS Users
Salt BLOB NULL, Salt BLOB NULL,
UserType INTEGER NOT NULL UserType INTEGER NOT NULL
); );
CREATE TABLE IF NOT EXISTS RequestHistory
{
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Type INTEGER NOT NULL,
RequestedDate varchar(50) NOT NULL,
RequestId INTEGER NOT NULL
}

@ -49,8 +49,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Notifications", "Ombi.
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Notifications", "Notifications", "{EA30DD15-6280-4687-B370-2956EC2E54E5}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Notifications", "Notifications", "{EA30DD15-6280-4687-B370-2956EC2E54E5}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Notifications.Email", "Ombi.Notifications.Email\Ombi.Notifications.Email.csproj", "{72DB97D7-2D60-4B96-8C57-6C0E20E892EB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Notifications.Templates", "Ombi.Notifications.Templates\Ombi.Notifications.Templates.csproj", "{6EE01B17-0966-4E11-8BC1-A5318A92AB1D}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Notifications.Templates", "Ombi.Notifications.Templates\Ombi.Notifications.Templates.csproj", "{6EE01B17-0966-4E11-8BC1-A5318A92AB1D}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Settings", "Ombi.Settings\Ombi.Settings.csproj", "{AE3AA23D-5B66-42AF-B44E-B9B4D8856C6F}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Settings", "Ombi.Settings\Ombi.Settings.csproj", "{AE3AA23D-5B66-42AF-B44E-B9B4D8856C6F}"
@ -63,7 +61,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Radarr", "Ombi.Api
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Discord", "Ombi.Api.Discord\Ombi.Api.Discord.csproj", "{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Discord", "Ombi.Api.Discord\Ombi.Api.Discord.csproj", "{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Notification.Discord", "Ombi.Notification.Discord\Ombi.Notification.Discord.csproj", "{D7B05CF2-E6B3-4BE5-8A02-6698CC0DCE9A}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Notification.Agents", "Ombi.Notification.Agents\Ombi.Notification.Agents.csproj", "{DC9E3E44-802A-4D9B-A70F-D198CFE4C73D}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -127,10 +125,6 @@ Global
{E6EE2830-E4AC-4F2E-AD93-2C9305605761}.Debug|Any CPU.Build.0 = Debug|Any CPU {E6EE2830-E4AC-4F2E-AD93-2C9305605761}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E6EE2830-E4AC-4F2E-AD93-2C9305605761}.Release|Any CPU.ActiveCfg = Release|Any CPU {E6EE2830-E4AC-4F2E-AD93-2C9305605761}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E6EE2830-E4AC-4F2E-AD93-2C9305605761}.Release|Any CPU.Build.0 = Release|Any CPU {E6EE2830-E4AC-4F2E-AD93-2C9305605761}.Release|Any CPU.Build.0 = Release|Any CPU
{72DB97D7-2D60-4B96-8C57-6C0E20E892EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{72DB97D7-2D60-4B96-8C57-6C0E20E892EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{72DB97D7-2D60-4B96-8C57-6C0E20E892EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{72DB97D7-2D60-4B96-8C57-6C0E20E892EB}.Release|Any CPU.Build.0 = Release|Any CPU
{6EE01B17-0966-4E11-8BC1-A5318A92AB1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6EE01B17-0966-4E11-8BC1-A5318A92AB1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6EE01B17-0966-4E11-8BC1-A5318A92AB1D}.Debug|Any CPU.Build.0 = Debug|Any CPU {6EE01B17-0966-4E11-8BC1-A5318A92AB1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6EE01B17-0966-4E11-8BC1-A5318A92AB1D}.Release|Any CPU.ActiveCfg = Release|Any CPU {6EE01B17-0966-4E11-8BC1-A5318A92AB1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -155,10 +149,10 @@ Global
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Debug|Any CPU.Build.0 = Debug|Any CPU {5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Release|Any CPU.ActiveCfg = Release|Any CPU {5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Release|Any CPU.Build.0 = Release|Any CPU {5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Release|Any CPU.Build.0 = Release|Any CPU
{D7B05CF2-E6B3-4BE5-8A02-6698CC0DCE9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DC9E3E44-802A-4D9B-A70F-D198CFE4C73D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D7B05CF2-E6B3-4BE5-8A02-6698CC0DCE9A}.Debug|Any CPU.Build.0 = Debug|Any CPU {DC9E3E44-802A-4D9B-A70F-D198CFE4C73D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D7B05CF2-E6B3-4BE5-8A02-6698CC0DCE9A}.Release|Any CPU.ActiveCfg = Release|Any CPU {DC9E3E44-802A-4D9B-A70F-D198CFE4C73D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D7B05CF2-E6B3-4BE5-8A02-6698CC0DCE9A}.Release|Any CPU.Build.0 = Release|Any CPU {DC9E3E44-802A-4D9B-A70F-D198CFE4C73D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -173,12 +167,11 @@ Global
{CFB5E008-D0D0-43C0-AA06-89E49D17F384} = {9293CA11-360A-4C20-A674-B9E794431BF5} {CFB5E008-D0D0-43C0-AA06-89E49D17F384} = {9293CA11-360A-4C20-A674-B9E794431BF5}
{0E8EF835-E4F0-4EE5-A2B6-678DEE973721} = {9293CA11-360A-4C20-A674-B9E794431BF5} {0E8EF835-E4F0-4EE5-A2B6-678DEE973721} = {9293CA11-360A-4C20-A674-B9E794431BF5}
{E6EE2830-E4AC-4F2E-AD93-2C9305605761} = {EA30DD15-6280-4687-B370-2956EC2E54E5} {E6EE2830-E4AC-4F2E-AD93-2C9305605761} = {EA30DD15-6280-4687-B370-2956EC2E54E5}
{72DB97D7-2D60-4B96-8C57-6C0E20E892EB} = {EA30DD15-6280-4687-B370-2956EC2E54E5}
{6EE01B17-0966-4E11-8BC1-A5318A92AB1D} = {EA30DD15-6280-4687-B370-2956EC2E54E5} {6EE01B17-0966-4E11-8BC1-A5318A92AB1D} = {EA30DD15-6280-4687-B370-2956EC2E54E5}
{3880375C-1A7E-4D75-96EC-63B954C42FEA} = {9293CA11-360A-4C20-A674-B9E794431BF5} {3880375C-1A7E-4D75-96EC-63B954C42FEA} = {9293CA11-360A-4C20-A674-B9E794431BF5}
{FC6A8F7C-9722-4AE4-960D-277ACB0E81CB} = {6F42AB98-9196-44C4-B888-D5E409F415A1} {FC6A8F7C-9722-4AE4-960D-277ACB0E81CB} = {6F42AB98-9196-44C4-B888-D5E409F415A1}
{94D04C1F-E35A-499C-B0A0-9FADEBDF8336} = {9293CA11-360A-4C20-A674-B9E794431BF5} {94D04C1F-E35A-499C-B0A0-9FADEBDF8336} = {9293CA11-360A-4C20-A674-B9E794431BF5}
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194} = {9293CA11-360A-4C20-A674-B9E794431BF5} {5AF2B6D2-5CC6-49FE-928A-BA27AF52B194} = {9293CA11-360A-4C20-A674-B9E794431BF5}
{D7B05CF2-E6B3-4BE5-8A02-6698CC0DCE9A} = {EA30DD15-6280-4687-B370-2956EC2E54E5} {DC9E3E44-802A-4D9B-A70F-D198CFE4C73D} = {EA30DD15-6280-4687-B370-2956EC2E54E5}
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

@ -2,8 +2,7 @@
/wwwroot/fonts/** /wwwroot/fonts/**
/wwwroot/lib/** /wwwroot/lib/**
/wwwroot/maps/** /wwwroot/maps/**
/wwwroot/app/**/*.js /wwwroot/dist/**
/wwwroot/app/**/*.js.map
/wwwroot/*.js.map /wwwroot/*.js.map
/wwwroot/*.js /wwwroot/*.js

@ -0,0 +1,3 @@
**/*.js
**/*.js.map
**/*.css

@ -1,4 +1,4 @@
<p-growl [value]="notificationService.messages" ></p-growl> <p-growl [value]="notificationService.messages" [life]="3000"></p-growl>
<nav *ngIf="showNav" class="navbar navbar-default navbar-fixed-top"> <nav *ngIf="showNav" class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid"> <div class="container-fluid">

@ -7,14 +7,10 @@ import { ILocalUser } from './auth/IUserLogin';
import { ICustomizationSettings } from './interfaces/ISettings'; import { ICustomizationSettings } from './interfaces/ISettings';
import style from './app.component.css';
@Component({ @Component({
selector: 'ombi', selector: 'ombi',
moduleId: module.id,
templateUrl: './app.component.html', templateUrl: './app.component.html',
styles: [style] styleUrls: ['./app.component.scss']
}) })
export class AppComponent implements OnInit { export class AppComponent implements OnInit {

@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { MdButtonModule, MdCardModule } from '@angular/material'; import { MdButtonModule, MdCardModule, MdInputModule, MdTabsModule } from '@angular/material';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { HttpModule } from '@angular/http'; import { HttpModule } from '@angular/http';
@ -26,8 +26,8 @@ import { SeriesInformationComponent } from './search/seriesinformation.component
import { RequestComponent } from './requests/request.component'; import { RequestComponent } from './requests/request.component';
import { MovieRequestsComponent } from './requests/movierequests.component'; import { MovieRequestsComponent } from './requests/movierequests.component';
import { TvRequestsComponent } from './requests/tvrequests.component'; import { TvRequestsComponent } from './requests/tvrequests.component';
import { RequestGridComponent } from './requests/request-grid.component'; import { RequestGridComponent } from './request-grid/request-grid.component';
import { RequestCardComponent } from './requests/request-card.component'; import { RequestCardComponent } from './request-grid/request-card.component';
import { LoginComponent } from './login/login.component'; import { LoginComponent } from './login/login.component';
import { LandingPageComponent } from './landingpage/landingpage.component'; import { LandingPageComponent } from './landingpage/landingpage.component';
@ -81,7 +81,9 @@ const routes: Routes = [
MdButtonModule, MdButtonModule,
NgbModule.forRoot(), NgbModule.forRoot(),
DragulaModule, DragulaModule,
MdCardModule MdCardModule,
MdInputModule,
MdTabsModule
], ],
declarations: [ declarations: [
AppComponent, AppComponent,

@ -5,7 +5,7 @@ import { AuthHttp, AuthConfig } from 'angular2-jwt';
export function authHttpServiceFactory(http: Http, options: RequestOptions) { export function authHttpServiceFactory(http: Http, options: RequestOptions) {
return new AuthHttp(new AuthConfig({ return new AuthHttp(new AuthConfig({
tokenName: 'id_token', tokenName: 'id_token',
tokenGetter: (() => localStorage.getItem('id_token')), tokenGetter: (() => localStorage.getItem('id_token')!),
globalHeaders: [{ 'Content-Type': 'application/json' }], globalHeaders: [{ 'Content-Type': 'application/json' }],
}), http, options); }), http, options);
} }

@ -31,7 +31,9 @@ export class AuthService extends ServiceHelpers {
claims(): ILocalUser { claims(): ILocalUser {
if (this.loggedIn()) { if (this.loggedIn()) {
var token = localStorage.getItem('id_token'); var token = localStorage.getItem('id_token');
if (!token) {
throw "Invalid token";
}
var json = this.jwtHelper.decodeToken(token); var json = this.jwtHelper.decodeToken(token);
var roles = json["http://schemas.microsoft.com/ws/2008/06/identity/claims/role"] var roles = json["http://schemas.microsoft.com/ws/2008/06/identity/claims/role"]
var name = json["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"]; var name = json["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"];

@ -1,7 +1,6 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
@Component({ @Component({
moduleId: module.id,
template: '<h2>Page not found</h2>' template: '<h2>Page not found</h2>'
}) })
export class PageNotFoundComponent { } export class PageNotFoundComponent { }

@ -14,7 +14,7 @@
available: boolean, available: boolean,
otherMessage: string, otherMessage: string,
adminNote: string, adminNote: string,
requestedUser: string[], requestedUser: string,
issueId: number, issueId: number,
denied: boolean, denied: boolean,
deniedReason: string, deniedReason: string,

@ -1,37 +1,37 @@
export interface ISettings { export interface ISettings {
id:number id: number
} }
export interface IExternalSettings extends ISettings { export interface IExternalSettings extends ISettings {
ssl: boolean, ssl: boolean,
subDir: string, subDir: string,
ip: string, ip: string,
port:number, port: number,
} }
export interface IOmbiSettings extends ISettings { export interface IOmbiSettings extends ISettings {
port: number, port: number,
//baseUrl:string, //baseUrl:string,
collectAnalyticData: boolean, collectAnalyticData: boolean,
wizard: boolean, wizard: boolean,
apiKey:string apiKey: string
} }
export interface IEmbySettings extends IExternalSettings { export interface IEmbySettings extends IExternalSettings {
apiKey: string, apiKey: string,
enable: boolean, enable: boolean,
administratorId: string, administratorId: string,
enableEpisodeSearching:boolean, enableEpisodeSearching: boolean,
} }
export interface IPlexSettings extends ISettings { export interface IPlexSettings extends ISettings {
enable: boolean, enable: boolean,
servers : IPlexServer[] servers: IPlexServer[]
} }
export interface IPlexServer extends IExternalSettings { export interface IPlexServer extends IExternalSettings {
name:string, name: string,
enableEpisodeSearching: boolean, enableEpisodeSearching: boolean,
plexAuthToken: string, plexAuthToken: string,
machineIdentifier: string, machineIdentifier: string,
@ -41,7 +41,7 @@ export interface IPlexServer extends IExternalSettings {
export interface IPlexLibraries { export interface IPlexLibraries {
key: string, key: string,
title: string, title: string,
enabled:boolean, enabled: boolean,
} }
export interface ISonarrSettings extends IExternalSettings { export interface ISonarrSettings extends IExternalSettings {
@ -50,7 +50,7 @@ export interface ISonarrSettings extends IExternalSettings {
qualityProfile: string, qualityProfile: string,
seasonFolders: boolean, seasonFolders: boolean,
rootPath: string, rootPath: string,
fullRootPath:string, fullRootPath: string,
} }
export interface IRadarrSettings extends IExternalSettings { export interface IRadarrSettings extends IExternalSettings {
@ -58,7 +58,8 @@ export interface IRadarrSettings extends IExternalSettings {
apiKey: string, apiKey: string,
defaultQualityProfile: string, defaultQualityProfile: string,
defaultRootPath: string, defaultRootPath: string,
fullRootPath:string, fullRootPath: string,
addOnly: boolean,
} }
export interface ILandingPageSettings extends ISettings { export interface ILandingPageSettings extends ISettings {
@ -70,10 +71,10 @@ export interface ILandingPageSettings extends ISettings {
noticeBackgroundColor: string, noticeBackgroundColor: string,
timeLimit: boolean, timeLimit: boolean,
startDateTime: Date, startDateTime: Date,
endDateTime:Date, endDateTime: Date,
} }
export interface ICustomizationSettings extends ISettings { export interface ICustomizationSettings extends ISettings {
applicationName: string, applicationName: string,
logo:string, logo: string,
} }

@ -6,9 +6,8 @@ import { IRequestCountModel } from '../interfaces/IRequestModel';
@Component({ @Component({
selector: 'ombi', selector: 'ombi',
moduleId: module.id,
templateUrl: './landingpage.component.html', templateUrl: './landingpage.component.html',
styleUrls: ['./landingpage.component.css'] styleUrls: ['./landingpage.component.scss']
}) })
export class LandingPageComponent implements OnInit { export class LandingPageComponent implements OnInit {

@ -9,7 +9,6 @@ import { NotificationService } from '../services/notification.service';
@Component({ @Component({
selector: 'ombi', selector: 'ombi',
moduleId: module.id,
templateUrl: './login.component.html', templateUrl: './login.component.html',
}) })
export class LoginComponent { export class LoginComponent {

@ -4,7 +4,6 @@ import { IMediaBase } from '../interfaces/IRequestModel';
@Component({ @Component({
selector: 'request-card', selector: 'request-card',
moduleId: module.id,
templateUrl: './request-card.component.html' templateUrl: './request-card.component.html'
}) })
export class RequestCardComponent { export class RequestCardComponent {

@ -4,7 +4,6 @@ import { RequestService } from '../services/request.service';
import { ITvRequestModel, IMovieRequestModel, IRequestGrid } from '../interfaces/IRequestModel'; import { ITvRequestModel, IMovieRequestModel, IRequestGrid } from '../interfaces/IRequestModel';
@Component({ @Component({
moduleId: module.id,
templateUrl: './request-grid.component.html' templateUrl: './request-grid.component.html'
}) })
export class RequestGridComponent implements OnInit { export class RequestGridComponent implements OnInit {

@ -51,14 +51,7 @@
<div>Release Date: {{request.releaseDate | date}}</div> <div>Release Date: {{request.releaseDate | date}}</div>
<br/> <br/>
<!--{{#if_eq type "tv"}}
{{#if episodes}}
Episodes: <span class="customTooltip" data-tooltip-content="#{{requestId}}toolTipContent"><i class="fa fa-info-circle"></i></span>
{{else}}
<div>@UI.Requests_SeasonsRequested: {{seriesRequested}}</div>
{{/if}}
{{/if_eq}}-->
<div *ngIf="request.requestedUsers">Requested By: <span *ngFor="let user of request.requestedUsers">{{user}} </span></div> <div *ngIf="request.requestedUsers">Requested By: <span *ngFor="let user of request.requestedUsers">{{user}} </span></div>
<div>Requested Date: {{request.requestedDate | date}}</div> <div>Requested Date: {{request.requestedDate | date}}</div>

@ -17,7 +17,6 @@ import { IMovieRequestModel } from '../interfaces/IRequestModel';
@Component({ @Component({
selector: 'movie-requests', selector: 'movie-requests',
moduleId: module.id,
templateUrl: './movierequests.component.html' templateUrl: './movierequests.component.html'
}) })
export class MovieRequestsComponent implements OnInit, OnDestroy { export class MovieRequestsComponent implements OnInit, OnDestroy {

@ -1,8 +1,6 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
@Component({ @Component({
selector: 'ombi',
moduleId: module.id,
templateUrl: './request.component.html' templateUrl: './request.component.html'
}) })
export class RequestComponent { export class RequestComponent {

@ -78,29 +78,8 @@
</div> </div>
<!--Child Requests--> <!--Child Requests-->
<button type="button" class="btn btn-sm btn-info-outline" data-toggle="collapse" [attr.data-target]="'#' + request.id +'childRequests'">Children</button> <button type="button" class="btn btn-sm btn-info-outline" (click)="showChildren(request)">Requests</button>
<div id="{{request.id}}childRequests" class="collapse">
<div *ngFor="let child of request.childRequests">
<hr />
<div *ngIf="request.requestedUsers">
Requested By: <span *ngFor="let user of request.requestedUsers">{{user}} </span>
</div>
<div *ngIf="child.seasonRequests">
Seasons Requested: <span *ngFor="let s of child.seasonRequests">{{s.seasonNumber}} </span>
</div>
<div>
<span>Request status: </span>
<span *ngIf="request.available" class="label label-success">Available</span>
<span *ngIf="request.approved && !request.available" class="label label-info">Processing Request</span>
<span *ngIf="request.denied" class="label label-danger">Request Denied</span>
<span *ngIf="request.deniedReason" title="{{request.deniedReason}}"><i class="fa fa-info-circle"></i></span>
<span *ngIf="!request.approved && !request.availble && !request.denied" class="label label-warning">Pending Approval</span>
</div>
</div>
</div>
</div> </div>
<div class="col-sm-3 col-sm-push-3"> <div class="col-sm-3 col-sm-push-3">
@ -150,23 +129,27 @@
{{/if_eq}} {{/if_eq}}
</form>--> </form>-->
<div *ngIf="!request.denied"> <div *ngIf="!request.denied">
<form> <input name="requestId" type="text" value="{{request.requestId}}" hidden="hidden" />
<input name="requestId" type="text" value="{{request.requestId}}" hidden="hidden" /> <input name="reason" type="text" hidden="hidden" />
<input name="reason" type="text" hidden="hidden" /> <div class="btn-group-sm btn-split">
<div class="btn-group btn-split"> <a (click)="deny(request)" class="btn btn-sm btn-danger-outline deny"><i class="fa fa-times"></i> Deny</a>
<button type="button" (click)="deny(request)" class="btn btn-sm btn-danger-outline deny"><i class="fa fa-times"></i> Deny</button> <a class="btn btn-sm btn-danger-outline dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></a>
<button type="button" class="btn btn-danger-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <ul class="dropdown-menu">
<span class="caret"></span> <li><a class="deny-with-reason" id="denyReason{{request.requestId}}" href="#" data-toggle="modal" data-target="#denyReasonModal">Deny with a reason</a></li>
<span class="sr-only">@UI.Requests_ToggleDropdown</span> </ul>
</button> </div>
<ul class="dropdown-menu"> <div class="btn-group">
<li><a class="deny-with-reason" id="denyReason{{request.requestId}}" href="#" data-toggle="modal" data-target="#denyReasonModal">Deny with a reason</a></li> <a href="#" class="btn btn-default">Default</a>
</ul> <a href="#" class="btn btn-default dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></a>
</div> <ul class="dropdown-menu btn-split">
</form> <li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
</div>
</div> </div>
</div> </div>
<form> <form>
@ -177,10 +160,6 @@
<button *ngIf="request.available" (click)="changeAvailability(request, false)" style="text-align: right" value="false" class="btn btn-sm btn-info-outline change"><i class="fa fa-minus"></i> Mark Unavailable</button> <button *ngIf="request.available" (click)="changeAvailability(request, false)" style="text-align: right" value="false" class="btn btn-sm btn-info-outline change"><i class="fa fa-minus"></i> Mark Unavailable</button>
<button *ngIf="!request.available" (click)="changeAvailability(request, true)" style="text-align: right" value="true" class="btn btn-sm btn-success-outline change"><i class="fa fa-plus"></i> Mark Available</button> <button *ngIf="!request.available" (click)="changeAvailability(request, true)" style="text-align: right" value="true" class="btn btn-sm btn-success-outline change"><i class="fa fa-plus"></i> Mark Available</button>
</form> </form>
</div> </div>
@ -202,9 +181,56 @@
</div> </div>
</div> </div>
<hr /> <hr />
</div>
</div>
<div class="modal fade in" *ngIf="showChildDialogue" style="display: block;">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" (click)="showChildDialogue=false">&times;</button>
<h4 class="modal-title">{{selectedSeason?.title}}</h4>
</div>
<div class="modal-body">
<div class="row" *ngFor="let child of selectedSeason.childRequests">
<div class="col-md-12">
<!--Child Requests-->
<div class="col-md-9">
<span class="col-md-12">Requested By: <b>{{child.requestedUser}}</b></span>
<span class="col-md-12" *ngIf="child.requestAll">Requested All Seasons</span>
<!--Seasons-->
<span *ngIf="child.approved && !child.available" class="label label-info">Processing Request</span>
<span *ngIf="child.denied" class="label label-danger">Request Denied</span>
<span *ngIf="child.deniedReason" title="{{child.deniedReason}}"><i class="fa fa-info-circle"></i></span>
<span *ngIf="!child.approved && !child.availble && !child.denied" class="label label-warning">Pending Approval</span>
<div class="col-md-12" *ngFor="let seasons of child.seasonRequests">
<span>Season: {{seasons.seasonNumber}}</span>
<span>Episodes:</span>
<div *ngFor="let episode of seasons.episodes">
<!--Episodes-->
<span># {{episode.episodeNumber}}</span>
<span *ngIf="episode.available" class="label label-success">Available</span>
</div>
</div>
</div>
<div class="col-md-3">
<button *ngIf="!child.approved" type="button" (click)="approveSeasonRequest(child)" class="btn btn-sm btn-success-outline" style="text-align: right"><i class="fa fa-plus"></i> Approve</button>
</div>
</div>
<hr />
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger-outline" (click)="showChildDialogue=false">Close</button>
</div>
</div>
</div> </div>
</div> </div>

@ -13,11 +13,10 @@ import 'rxjs/add/operator/map';
import { RequestService } from '../services/request.service'; import { RequestService } from '../services/request.service';
import { IdentityService } from '../services/identity.service'; import { IdentityService } from '../services/identity.service';
import { ITvRequestModel } from '../interfaces/IRequestModel'; import { ITvRequestModel, IChildTvRequest } from '../interfaces/IRequestModel';
@Component({ @Component({
selector: 'tv-requests', selector: 'tv-requests',
moduleId: module.id,
templateUrl: './tvrequests.component.html' templateUrl: './tvrequests.component.html'
}) })
export class TvRequestsComponent implements OnInit, OnDestroy { export class TvRequestsComponent implements OnInit, OnDestroy {
@ -51,6 +50,9 @@ export class TvRequestsComponent implements OnInit, OnDestroy {
private currentlyLoaded: number; private currentlyLoaded: number;
private amountToLoad: number; private amountToLoad: number;
public showChildDialogue = false; // This is for the child modal popup
public selectedSeason : ITvRequestModel;
ngOnInit() { ngOnInit() {
this.amountToLoad = 5; this.amountToLoad = 5;
@ -96,6 +98,17 @@ export class TvRequestsComponent implements OnInit, OnDestroy {
this.updateRequest(request); this.updateRequest(request);
} }
public approveSeasonRequest(request: IChildTvRequest) {
request.approved = true;
this.requestService.updateTvRequest(this.selectedSeason)
.subscribe();
}
public showChildren(request: ITvRequestModel) {
this.selectedSeason = request;
this.showChildDialogue = true;
}
private updateRequest(request: ITvRequestModel) { private updateRequest(request: ITvRequestModel) {
this.requestService.updateTvRequest(request) this.requestService.updateTvRequest(request)
.takeUntil(this.subscriptions) .takeUntil(this.subscriptions)

@ -49,13 +49,13 @@
<span *ngIf="result.available" class="label label-success">Available</span> <span *ngIf="result.available" class="label label-success">Available</span>
<span *ngIf="result.approved && !result.available" class="label label-info">Processing Request</span> <span *ngIf="result.approved && !result.available" class="label label-info">Processing Request</span>
<div *ngIf="result.requested && !result.available; then requested else notRequested"></div> <div *ngIf="result.requested && !result.available; then requested else notRequested"></div>
<template #requested> <ng-template #requested>
<span *ngIf="!result.available" class="label label-warning">Pending Approval</span> <span *ngIf="!result.available" class="label label-warning">Pending Approval</span>
</template> </ng-template>
<template #notRequested> <ng-template #notRequested>
<span *ngIf="!result.available" class="label label-danger">Not Yet Requested</span> <span *ngIf="!result.available" class="label label-danger">Not Yet Requested</span>
</template> </ng-template>
<span id="{{id}}netflixTab"></span> <span id="{{id}}netflixTab"></span>
@ -87,12 +87,12 @@
</div> </div>
<div *ngIf="!result.available"> <div *ngIf="!result.available">
<div *ngIf="result.requested; then requestedBtn else notRequestedBtn"></div> <div *ngIf="result.requested; then requestedBtn else notRequestedBtn"></div>
<template #requestedBtn> <ng-template #requestedBtn>
<button style="text-align: right" class="btn btn-primary-outline disabled" [disabled]><i class="fa fa-check"></i> Requested</button> <button style="text-align: right" class="btn btn-primary-outline disabled" [disabled]><i class="fa fa-check"></i> Requested</button>
</template> </ng-template>
<template #notRequestedBtn> <ng-template #notRequestedBtn>
<button id="{{result.id}}" style="text-align: right" class="btn btn-primary-outline" (click)="request(result)"><i class="fa fa-plus"></i> Request</button> <button id="{{result.id}}" style="text-align: right" class="btn btn-primary-outline" (click)="request(result)"><i class="fa fa-plus"></i> Request</button>
</template> </ng-template>
</div> </div>
<!--{{#if_eq type "tv"}} <!--{{#if_eq type "tv"}}
{{#if_eq tvFullyAvailable true}} {{#if_eq tvFullyAvailable true}}

@ -14,7 +14,6 @@ import { IRequestEngineResult } from '../interfaces/IRequestEngineResult';
@Component({ @Component({
selector: 'movie-search', selector: 'movie-search',
moduleId: module.id,
templateUrl: './moviesearch.component.html', templateUrl: './moviesearch.component.html',
}) })
export class MovieSearchComponent implements OnInit, OnDestroy { export class MovieSearchComponent implements OnInit, OnDestroy {
@ -116,11 +115,20 @@ export class MovieSearchComponent implements OnInit, OnDestroy {
} }
private getExtaInfo() { private getExtaInfo() {
this.searchService.extraInfo(this.movieResults)
.takeUntil(this.subscriptions) this.movieResults.forEach((val, index) => {
.subscribe(m => this.movieResults = m); this.searchService.getMovieInformation(val.id)
.takeUntil(this.subscriptions)
.subscribe(m => this.updateItem(val, m));
});
} }
private updateItem(key: ISearchMovieResult, updated: ISearchMovieResult) {
var index = this.movieResults.indexOf(key, 0);
if (index > -1) {
this.movieResults[index] = updated;
}
}
private clearResults() { private clearResults() {
this.movieResults = []; this.movieResults = [];
this.searchApplied = false; this.searchApplied = false;

@ -1,7 +1,5 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
@Component({ @Component({
selector: 'ombi',
moduleId: module.id,
templateUrl: './search.component.html', templateUrl: './search.component.html',
}) })
export class SearchComponent implements OnInit { export class SearchComponent implements OnInit {
@ -16,7 +14,7 @@ export class SearchComponent implements OnInit {
selectTab() { selectTab() {
this.showMovie = !this.showMovie; this.showMovie = !this.showMovie;
this.showTv = !this.showTv; this.showTv = !this.showTv;
} }

@ -47,18 +47,18 @@
<span *ngIf="ep.available" class="label label-success">Available</span> <span *ngIf="ep.available" class="label label-success">Available</span>
<span *ngIf="ep.approved && !ep.available" class="label label-info">Processing Request</span> <span *ngIf="ep.approved && !ep.available" class="label label-info">Processing Request</span>
<div *ngIf="ep.requested && !ep.available; then requested else notRequested"></div> <div *ngIf="ep.requested && !ep.available; then requested else notRequested"></div>
<template #requested> <ng-template #requested>
<span *ngIf="!ep.available" class="label label-warning">Pending Approval</span> <span *ngIf="!ep.available" class="label label-warning">Pending Approval</span>
</template> </ng-template>
<template #notRequested> <ng-template #notRequested>
<span *ngIf="!ep.available" class="label label-danger">Not Yet Requested</span> <span *ngIf="!ep.available" class="label label-danger">Not Yet Requested</span>
</template> </ng-template>
</td> </td>
<td> <td>
<button (click)="addRequest(ep)" [disabled]="ep.available || ep.requested || ep.approved" class="btn btn-sm btn-primary-outline">Request</button> <button (click)="addRequest(ep)" [disabled]="ep.available || ep.requested || ep.approved" class="btn btn-sm btn-primary-outline">Add Request</button>
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -71,5 +71,5 @@
<button id="requestFloatingBtn" class="btn btn-sm btn-success" title="Go to top">Request</button> <button id="requestFloatingBtn" class="btn btn-sm btn-success" (click)="submitRequests()" title="Go to top">Submit Request</button>
</div> </div>

@ -13,10 +13,8 @@ import { IRequestEngineResult } from '../interfaces/IRequestEngineResult';
import { IEpisodesRequested } from "../interfaces/IRequestModel"; import { IEpisodesRequested } from "../interfaces/IRequestModel";
@Component({ @Component({
selector: 'ombi', templateUrl: './seriesinformation.component.html',
moduleId: module.id, styleUrls: ['./seriesinformation.component.scss']
templateUrl: './seriesinformation.component.html',
styleUrls: ['./seriesinformation.component.css']
}) })
export class SeriesInformationComponent implements OnInit, OnDestroy { export class SeriesInformationComponent implements OnInit, OnDestroy {
@ -31,9 +29,9 @@ export class SeriesInformationComponent implements OnInit, OnDestroy {
private subscriptions = new Subject<void>(); private subscriptions = new Subject<void>();
result : IRequestEngineResult; public result : IRequestEngineResult;
seriesId: number; private seriesId: number;
series: ISearchTvResult; public series: ISearchTvResult;
requestedEpisodes: IEpisodesRequested[] = []; requestedEpisodes: IEpisodesRequested[] = [];
@ -42,13 +40,14 @@ export class SeriesInformationComponent implements OnInit, OnDestroy {
this.searchService.getShowInformation(this.seriesId) this.searchService.getShowInformation(this.seriesId)
.takeUntil(this.subscriptions) .takeUntil(this.subscriptions)
.subscribe(x => { .subscribe(x => {
this.series = x as ISearchTvResult; this.series = x;
}); });
} }
request() { public submitRequests() {
this.series.requested = true; this.series.requested = true;
this.requestService.requestTv(this.series) this.requestService.requestTv(this.series)
.takeUntil(this.subscriptions) .takeUntil(this.subscriptions)
.subscribe(x => { .subscribe(x => {
@ -62,10 +61,8 @@ export class SeriesInformationComponent implements OnInit, OnDestroy {
}); });
} }
addRequest(episode: IEpisodesRequested) { public addRequest(episode: IEpisodesRequested) {
this.requestedEpisodes.push(episode);
episode.requested = true; episode.requested = true;
} }

@ -59,13 +59,13 @@
<span *ngIf="result.available" class="label label-success">Available</span> <span *ngIf="result.available" class="label label-success">Available</span>
<span *ngIf="result.approved && !result.available" class="label label-info">Processing Request</span> <span *ngIf="result.approved && !result.available" class="label label-info">Processing Request</span>
<div *ngIf="result.requested && !result.available; then requested else notRequested"></div> <div *ngIf="result.requested && !result.available; then requested else notRequested"></div>
<template #requested> <ng-template #requested>
<span *ngIf="!result.available" class="label label-warning">Pending Approval</span> <span *ngIf="!result.available" class="label label-warning">Pending Approval</span>
</template> </ng-template>
<template #notRequested> <ng-template #notRequested>
<span *ngIf="!result.available" class="label label-danger">Not Yet Requested</span> <span *ngIf="!result.available" class="label label-danger">Not Yet Requested</span>
</template> </ng-template>
<span id="{{id}}netflixTab"></span> <span id="{{id}}netflixTab"></span>

@ -13,12 +13,9 @@ import { NotificationService } from '../services/notification.service';
import { ISearchTvResult } from '../interfaces/ISearchTvResult'; import { ISearchTvResult } from '../interfaces/ISearchTvResult';
import { IRequestEngineResult } from '../interfaces/IRequestEngineResult'; import { IRequestEngineResult } from '../interfaces/IRequestEngineResult';
import template from './tvsearch.component.html';
@Component({ @Component({
selector: 'tv-search', selector: 'tv-search',
moduleId: module.id, templateUrl: './tvsearch.component.html',
template: template,
}) })
export class TvSearchComponent implements OnInit, OnDestroy { export class TvSearchComponent implements OnInit, OnDestroy {
@ -148,7 +145,7 @@ export class TvSearchComponent implements OnInit, OnDestroy {
} }
selectSeason(searchResult: ISearchTvResult) { selectSeason(searchResult: ISearchTvResult) {
this.route.navigate(['/search/show', searchResult.seriesId]); this.route.navigate(['/search/show', searchResult.id]);
} }
private updateItem(key: ISearchTvResult, updated: ISearchTvResult) { private updateItem(key: ISearchTvResult, updated: ISearchTvResult) {

@ -37,7 +37,7 @@ export class IdentityService extends ServiceAuthHelpers {
} }
hasRole(role: string): boolean { hasRole(role: string): boolean {
var roles = localStorage.getItem("roles") as string[]; var roles = localStorage.getItem("roles") as string[] | null;
if (roles) { if (roles) {
if (roles.indexOf(role) > -1) { if (roles.indexOf(role) > -1) {
return true; return true;

@ -6,6 +6,7 @@ export class NotificationService {
messages: Message[] = []; messages: Message[] = [];
public addMessage(message: Message) { public addMessage(message: Message) {
this.messages.push(message); this.messages.push(message);
this.messages = JSON.parse(JSON.stringify(this.messages)); // NOTE: THIS IS A HACK AROUND A BUG https://github.com/primefaces/primeng/issues/2943
} }
public success(title: string, body: string) { public success(title: string, body: string) {

@ -52,7 +52,7 @@ export class RequestService extends ServiceAuthHelpers {
} }
updateTvRequest(request: ITvRequestModel): Observable<ITvRequestModel> { updateTvRequest(request: ITvRequestModel): Observable<ITvRequestModel> {
return this.http.post(`${this.url}tv/`, JSON.stringify(request), { headers: this.headers }).map(this.extractData); return this.http.put(`${this.url}tv/`, JSON.stringify(request), { headers: this.headers }).map(this.extractData);
} }
getRequestsCount(): Observable<IRequestCountModel> { getRequestsCount(): Observable<IRequestCountModel> {

@ -32,6 +32,9 @@ export class SearchService extends ServiceAuthHelpers {
extraInfo(movies: ISearchMovieResult[]): Observable<ISearchMovieResult[]> { extraInfo(movies: ISearchMovieResult[]): Observable<ISearchMovieResult[]> {
return this.http.post(`${this.url}/Movie/extrainfo`, JSON.stringify(movies), { headers: this.headers }).map(this.extractData); return this.http.post(`${this.url}/Movie/extrainfo`, JSON.stringify(movies), { headers: this.headers }).map(this.extractData);
} }
getMovieInformation(theMovieDbId: number): Observable<ISearchMovieResult> {
return this.http.get(`${this.url}/Movie/info/${theMovieDbId}`).map(this.extractData);
}
// TV // TV
searchTv(searchTerm: string): Observable<ISearchTvResult[]> { searchTv(searchTerm: string): Observable<ISearchTvResult[]> {

@ -6,7 +6,6 @@ import { NotificationService } from "../../services/notification.service";
@Component({ @Component({
selector: 'ombi', selector: 'ombi',
moduleId: module.id,
templateUrl: './customization.component.html', templateUrl: './customization.component.html',
}) })
export class CustomizationComponent implements OnInit { export class CustomizationComponent implements OnInit {

@ -6,7 +6,6 @@ import { NotificationService } from "../../services/notification.service";
@Component({ @Component({
selector: 'ombi', selector: 'ombi',
moduleId: module.id,
templateUrl: './emby.component.html', templateUrl: './emby.component.html',
}) })
export class EmbyComponent implements OnInit { export class EmbyComponent implements OnInit {

@ -6,7 +6,6 @@ import { NotificationService } from "../../services/notification.service";
@Component({ @Component({
selector: 'ombi', selector: 'ombi',
moduleId: module.id,
templateUrl: './landingpage.component.html', templateUrl: './landingpage.component.html',
}) })
export class LandingPageComponent implements OnInit { export class LandingPageComponent implements OnInit {

@ -6,7 +6,6 @@ import { NotificationService } from "../../services/notification.service";
@Component({ @Component({
selector: 'ombi', selector: 'ombi',
moduleId: module.id,
templateUrl: './ombi.component.html', templateUrl: './ombi.component.html',
}) })
export class OmbiComponent implements OnInit { export class OmbiComponent implements OnInit {

@ -3,9 +3,10 @@
<fieldset> <fieldset>
<legend>Plex Configuration</legend> <legend>Plex Configuration</legend>
<div class="checkbox col-md-2 " style="float: right;">
<input type="checkbox" id="advanced" [(ngModel)]="advanced" ng-checked="advanced"> <div style="float: right;">
<label for="advanced">Advanced</label> <span style="vertical-align: top;">Advanced</span>
<p-inputSwitch id="customInputSwitch" [(ngModel)]="advanced"></p-inputSwitch>
</div> </div>
<div class="form-group col-md-3"> <div class="form-group col-md-3">

@ -11,7 +11,6 @@ import { NotificationService } from "../../services/notification.service";
@Component({ @Component({
selector: 'ombi', selector: 'ombi',
moduleId: module.id,
templateUrl: './plex.component.html', templateUrl: './plex.component.html',
}) })
export class PlexComponent implements OnInit, OnDestroy { export class PlexComponent implements OnInit, OnDestroy {

@ -3,10 +3,14 @@
<div *ngIf="settings"> <div *ngIf="settings">
<fieldset> <fieldset>
<legend>Radarr Settings</legend> <legend>Radarr Settings</legend>
<div style="float: right;">
<span style="vertical-align: top;">Advanced</span>
<p-inputSwitch id="customInputSwitch" [(ngModel)]="advanced"></p-inputSwitch>
</div>
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <div class="checkbox">
<input type="checkbox" [(ngModel)]="settings.enable" ng-checked="settings.enable"> <input type="checkbox" id="enable" [(ngModel)]="settings.enable" ng-checked="settings.enable">
<label for="enable">Enable</label> <label for="enable">Enable</label>
</div> </div>
</div> </div>
@ -77,6 +81,13 @@
</select> </select>
</div> </div>
</div> </div>
<div class="form-group" *ngIf="advanced">
<div class="checkbox">
<input type="checkbox" id="addOnly" [(ngModel)]="settings.addOnly" ng-checked="settings.addOnly">
<label for="addOnly">Do not search</label>
</div>
</div>
<div class="form-group"> <div class="form-group">

@ -10,7 +10,6 @@ import { NotificationService } from "../../services/notification.service";
@Component({ @Component({
selector: 'ombi', selector: 'ombi',
moduleId: module.id,
templateUrl: './radarr.component.html', templateUrl: './radarr.component.html',
}) })
export class RadarrComponent implements OnInit { export class RadarrComponent implements OnInit {
@ -27,6 +26,8 @@ export class RadarrComponent implements OnInit {
profilesRunning: boolean; profilesRunning: boolean;
rootFoldersRunning: boolean; rootFoldersRunning: boolean;
advanced = false;
private subscriptions = new Subject<void>(); private subscriptions = new Subject<void>();
ngOnInit(): void { ngOnInit(): void {

@ -1,7 +1,6 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
@Component({ @Component({
selector: 'settings-menu', selector: 'settings-menu',
moduleId: module.id,
templateUrl: './settingsmenu.component.html' templateUrl: './settingsmenu.component.html'
}) })
export class SettingsMenuComponent { export class SettingsMenuComponent {

@ -10,7 +10,6 @@ import { NotificationService } from "../../services/notification.service";
@Component({ @Component({
selector: 'ombi', selector: 'ombi',
moduleId: module.id,
templateUrl: './sonarr.component.html', templateUrl: './sonarr.component.html',
}) })
export class SonarrComponent implements OnInit, OnDestroy { export class SonarrComponent implements OnInit, OnDestroy {

@ -5,7 +5,6 @@ import { IdentityService } from '../services/identity.service';
@Component({ @Component({
selector: 'ombi', selector: 'ombi',
moduleId: module.id,
templateUrl: './usermanagement.component.html' templateUrl: './usermanagement.component.html'
}) })
export class UserManagementComponent implements OnInit { export class UserManagementComponent implements OnInit {

@ -8,7 +8,6 @@ import { NotificationService } from '../../services/notification.service';
@Component({ @Component({
selector: 'ombi', selector: 'ombi',
moduleId: module.id,
templateUrl: './createadmin.component.html', templateUrl: './createadmin.component.html',
}) })
export class CreateAdminComponent { export class CreateAdminComponent {

@ -8,7 +8,6 @@ import { IEmbySettings } from '../../interfaces/ISettings';
@Component({ @Component({
selector: 'ombi', selector: 'ombi',
moduleId: module.id,
templateUrl: './emby.component.html', templateUrl: './emby.component.html',
}) })
export class EmbyComponent { export class EmbyComponent {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save