mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-19 21:03:17 -07:00
Merge pull request #1425 from tidusjar/DotNetCoreStyling
Dot net core styling
This commit is contained in:
commit
d18d1a2eec
133 changed files with 5625 additions and 871 deletions
|
@ -12,7 +12,6 @@ before_build:
|
|||
- cmd: cd src/ombi
|
||||
- node --version
|
||||
- appveyor-retry dotnet restore
|
||||
- appveyor-retry npm install bower -g
|
||||
- appveyor-retry npm install -g gulp
|
||||
- appveyor-retry npm install
|
||||
- gulp publish
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace Ombi.Core.Tests.Engine
|
|||
{
|
||||
RequestService = new Mock<IRequestService<MovieRequestModel>>();
|
||||
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; }
|
||||
|
|
|
@ -3,6 +3,7 @@ using Moq;
|
|||
using Ombi.Core.Claims;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Rule.Rules;
|
||||
using Ombi.Core.Rule.Rules.Request;
|
||||
using Xunit;
|
||||
|
||||
namespace Ombi.Core.Tests.Rule
|
||||
|
|
|
@ -17,5 +17,6 @@ namespace Ombi.Core
|
|||
Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies();
|
||||
|
||||
Task<IEnumerable<SearchMovieViewModel>> LookupImdbInformation(IEnumerable<SearchMovieViewModel> movies);
|
||||
Task<SearchMovieViewModel> LookupImdbInformation(int theMovieDbId);
|
||||
}
|
||||
}
|
|
@ -96,7 +96,7 @@ namespace Ombi.Core.Engine
|
|||
Status = movieInfo.Status,
|
||||
RequestedDate = DateTime.UtcNow,
|
||||
Approved = false,
|
||||
RequestedUsers = new List<string> { Username },
|
||||
RequestedUser =Username,
|
||||
Issues = IssueState.None
|
||||
};
|
||||
|
||||
|
@ -191,7 +191,7 @@ namespace Ombi.Core.Engine
|
|||
results.OtherMessage = request.OtherMessage;
|
||||
results.Overview = request.Overview;
|
||||
results.PosterPath = request.PosterPath;
|
||||
results.RequestedUsers = request.RequestedUsers?.ToList() ?? new List<string>();
|
||||
results.RequestedUser = request.RequestedUser;
|
||||
|
||||
var model = MovieRequestService.UpdateRequest(results);
|
||||
return model;
|
||||
|
@ -202,21 +202,6 @@ namespace Ombi.Core.Engine
|
|||
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)
|
||||
{
|
||||
await MovieRequestService.AddRequestAsync(model);
|
||||
|
|
|
@ -13,6 +13,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using StackExchange.Profiling;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
|
@ -63,17 +64,41 @@ namespace Ombi.Core.Engine
|
|||
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)
|
||||
{
|
||||
var result = await MovieApi.SearchMovie(search);
|
||||
if (result != null)
|
||||
using (MiniProfiler.Current.Step("Starting Movie Search Engine"))
|
||||
using (MiniProfiler.Current.Step("Searching Movie"))
|
||||
{
|
||||
Logger.LogDebug("Search Result: {result}", result);
|
||||
return await TransformMovieResultsToResponse(result);
|
||||
var result = await MovieApi.SearchMovie(search);
|
||||
|
||||
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()
|
||||
{
|
||||
var result = await MovieApi.PopularMovies();
|
||||
|
@ -121,28 +146,41 @@ namespace Ombi.Core.Engine
|
|||
private async Task<List<SearchMovieViewModel>> TransformMovieResultsToResponse(
|
||||
IEnumerable<MovieSearchResult> movies)
|
||||
{
|
||||
var viewMovies = new List<SearchMovieViewModel>();
|
||||
var dbMovies = await GetMovieRequests();
|
||||
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
var embySettings = await EmbySettings.GetSettingsAsync();
|
||||
var viewMovies = new List<SearchMovieViewModel>();
|
||||
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();
|
||||
|
||||
plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
embySettings = await EmbySettings.GetSettingsAsync();
|
||||
}
|
||||
foreach (var movie in movies)
|
||||
{
|
||||
viewMovies.Add(await ProcessSingleMovie(movie, dbMovies, plexSettings, embySettings));
|
||||
}
|
||||
return viewMovies;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
var item = await PlexContentRepo.Get(showInfo.ImdbId);
|
||||
if (item != null)
|
||||
if (lookupExtraInfo)
|
||||
{
|
||||
viewMovie.Available = true;
|
||||
viewMovie.PlexUrl = item.Url;
|
||||
var showInfo = await MovieApi.GetMovieInformation(viewMovie.Id);
|
||||
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();
|
||||
|
@ -180,10 +218,11 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
|
||||
RunSearchRules(viewMovie);
|
||||
|
||||
|
||||
return viewMovie;
|
||||
}
|
||||
|
||||
|
||||
private async Task<SearchMovieViewModel> ProcessSingleMovie(MovieSearchResult movie,
|
||||
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
|
||||
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
|
||||
{
|
||||
Id = tv.Id,
|
||||
|
@ -52,11 +71,11 @@ namespace Ombi.Core.Engine
|
|||
Status = showInfo.status,
|
||||
RequestedDate = DateTime.UtcNow,
|
||||
Approved = false,
|
||||
RequestedUsers = new List<string> { Username },
|
||||
RequestedUser = Username,
|
||||
Issues = IssueState.None,
|
||||
ProviderId = tv.Id,
|
||||
RequestAll = tv.RequestAll,
|
||||
SeasonRequests = tv.SeasonRequests
|
||||
SeasonRequests = tvRequests
|
||||
};
|
||||
|
||||
var model = new TvRequestModel
|
||||
|
@ -76,22 +95,22 @@ namespace Ombi.Core.Engine
|
|||
|
||||
model.ChildRequests.Add(childRequest);
|
||||
|
||||
if (childRequest.SeasonRequests.Any())
|
||||
{
|
||||
var episodes = await TvApi.EpisodeLookup(showInfo.id);
|
||||
//if (childRequest.SeasonRequests.Any())
|
||||
//{
|
||||
// var episodes = await TvApi.EpisodeLookup(showInfo.id);
|
||||
|
||||
foreach (var e in episodes)
|
||||
{
|
||||
var season = childRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == e.season);
|
||||
season?.Episodes.Add(new EpisodesRequested
|
||||
{
|
||||
Url = e.url,
|
||||
Title = e.name,
|
||||
AirDate = DateTime.Parse(e.airstamp),
|
||||
EpisodeNumber = e.number
|
||||
});
|
||||
}
|
||||
}
|
||||
// foreach (var e in episodes)
|
||||
// {
|
||||
// var season = childRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == e.season);
|
||||
// season?.Episodes.Add(new EpisodesRequested
|
||||
// {
|
||||
// Url = e.url,
|
||||
// Title = e.name,
|
||||
// AirDate = DateTime.Parse(e.airstamp),
|
||||
// EpisodeNumber = e.number
|
||||
// });
|
||||
// }
|
||||
//}
|
||||
|
||||
if (tv.LatestSeason)
|
||||
{
|
||||
|
@ -165,6 +184,8 @@ namespace Ombi.Core.Engine
|
|||
var results = allRequests.FirstOrDefault(x => x.Id == request.Id);
|
||||
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);
|
||||
return model;
|
||||
}
|
||||
|
@ -192,26 +213,52 @@ namespace Ombi.Core.Engine
|
|||
existingRequest.ChildRequests.AddRange(newRequest.ChildRequests);
|
||||
TvRequestService.UpdateRequest(existingRequest);
|
||||
|
||||
if (ShouldAutoApprove(RequestType.TvShow))
|
||||
if (newRequest.Approved) // The auto approve rule
|
||||
{
|
||||
// TODO Auto Approval Code
|
||||
}
|
||||
return await AfterRequest(newRequest);
|
||||
}
|
||||
|
||||
private IEnumerable<SeasonRequestModel> GetListDifferences(IEnumerable<SeasonRequestModel> existing,
|
||||
IEnumerable<SeasonRequestModel> request)
|
||||
private IEnumerable<SeasonRequestModel> GetListDifferences(List<SeasonRequestModel> existing,
|
||||
List<SeasonRequestModel> request)
|
||||
{
|
||||
var newRequest = request
|
||||
.Select(r =>
|
||||
new SeasonRequestModel
|
||||
{
|
||||
SeasonNumber = r.SeasonNumber,
|
||||
Episodes = r.Episodes
|
||||
})
|
||||
.ToList();
|
||||
var requestsToRemove = new List<SeasonRequestModel>();
|
||||
foreach (var r in request)
|
||||
{
|
||||
// Do we have an existing season?
|
||||
var existingSeason = existing.FirstOrDefault(x => x.SeasonNumber == r.SeasonNumber);
|
||||
if (existingSeason == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return newRequest.Except(existing);
|
||||
// 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
|
||||
{
|
||||
// We already have this request
|
||||
r.Episodes.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!r.Episodes.Any())
|
||||
{
|
||||
requestsToRemove.Add(r);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var remove in requestsToRemove)
|
||||
{
|
||||
request.Remove(remove);
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
private async Task<RequestEngineResult> AddRequest(TvRequestModel model)
|
||||
|
|
|
@ -77,13 +77,12 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
else
|
||||
{
|
||||
season.Episodes.Add(new EpisodesRequested
|
||||
{
|
||||
Url = e.url,
|
||||
Title = e.name,
|
||||
AirDate = DateTime.Parse(e.airstamp),
|
||||
EpisodeNumber = e.number,
|
||||
});
|
||||
// Find the episode
|
||||
var ep = season.Episodes.FirstOrDefault(x => x.EpisodeNumber == e.number);
|
||||
ep.Url = e.url;
|
||||
ep.Title = e.name;
|
||||
ep.AirDate = DateTime.Parse(e.airstamp);
|
||||
ep.EpisodeNumber = e.number;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,6 +172,7 @@ namespace Ombi.Core.Engine
|
|||
// Find the existing request season
|
||||
var existingSeason =
|
||||
existingRequestChildRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == season.SeasonNumber);
|
||||
if(existingSeason == null) continue;
|
||||
|
||||
foreach (var ep in existingSeason.Episodes)
|
||||
{
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
using Newtonsoft.Json;
|
||||
using Ombi.Store.Entities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ombi.Core.Models.Requests
|
||||
{
|
||||
|
@ -22,7 +20,7 @@ namespace Ombi.Core.Models.Requests
|
|||
public IssueState Issues { get; set; }
|
||||
public string OtherMessage { 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 bool Denied { get; set; }
|
||||
public string DeniedReason { get; set; }
|
||||
|
@ -30,24 +28,13 @@ namespace Ombi.Core.Models.Requests
|
|||
[JsonIgnore]
|
||||
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]
|
||||
public bool CanApprove => !Approved && !Available;
|
||||
|
||||
|
||||
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 = 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))
|
||||
{
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" 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="MiniProfiler.AspNetCore" Version="4.0.0-alpha6-79" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
|
||||
</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.Rules;
|
||||
using Ombi.Store.Entities;
|
||||
using System.Security.Principal;
|
||||
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>
|
||||
{
|
||||
|
|
|
@ -8,7 +8,7 @@ using Ombi.Notifications;
|
|||
using Ombi.Notifications.Models;
|
||||
using Ombi.Settings.Settings.Models.Notifications;
|
||||
|
||||
namespace Ombi.Notification.Discord
|
||||
namespace Ombi.Notification.Agents
|
||||
{
|
||||
public class DiscordNotification : BaseNotification<DiscordNotificationSettings>
|
||||
{
|
227
src/Ombi.Notification.Agents/EmailNotification.cs
Normal file
227
src/Ombi.Notification.Agents/EmailNotification.cs
Normal file
|
@ -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>
|
||||
<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.Settings\Ombi.Settings.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -5,6 +5,11 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<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.Store\Ombi.Store.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -77,45 +77,6 @@ namespace Ombi.Schedule.Jobs
|
|||
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)
|
||||
{
|
||||
|
@ -154,18 +115,16 @@ namespace Ombi.Schedule.Jobs
|
|||
var itemAdded = false;
|
||||
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)
|
||||
{
|
||||
// We already have this season
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
existingContent.Seasons.Add(season);
|
||||
itemAdded = true;
|
||||
}
|
||||
|
||||
existingContent.Seasons.Add(season);
|
||||
itemAdded = true;
|
||||
}
|
||||
|
||||
if (itemAdded) await Repo.Update(existingContent);
|
||||
|
|
|
@ -9,5 +9,6 @@ namespace Ombi.Settings.Settings.Models.External
|
|||
public string DefaultQualityProfile { get; set; }
|
||||
public string DefaultRootPath { get; set; }
|
||||
public string FullRootPath { get; set; }
|
||||
public bool AddOnly { get; set; }
|
||||
}
|
||||
}
|
|
@ -44,7 +44,7 @@ namespace Ombi.Store.Entities
|
|||
/// <summary>
|
||||
/// Only used for TV Shows
|
||||
/// </summary>
|
||||
public virtual ICollection<SeasonsContent> Seasons { get; set; }
|
||||
public virtual ICollection<PlexSeasonsContent> Seasons { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Plex's internal ID for this item
|
||||
|
@ -53,7 +53,8 @@ namespace Ombi.Store.Entities
|
|||
public DateTime AddedAt { get; set; }
|
||||
}
|
||||
|
||||
public class SeasonsContent : Entity
|
||||
[Table("PlexSeasonsContent")]
|
||||
public class PlexSeasonsContent : Entity
|
||||
{
|
||||
public int PlexContentId { get; set; }
|
||||
public int SeasonNumber { get; set; }
|
||||
|
|
15
src/Ombi.Store/Entities/RequestHistory.cs
Normal file
15
src/Ombi.Store/Entities/RequestHistory.cs
Normal file
|
@ -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
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS SeasonsContent
|
||||
CREATE TABLE IF NOT EXISTS PlexSeasonsContent
|
||||
(
|
||||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
PlexContentId integer not null,
|
||||
SeasonNumber INTEGER NOT NULL,
|
||||
SeasonKey INTEGER NOT NULL,
|
||||
ParentKey INTEGER NOT NULL
|
||||
|
@ -52,4 +53,14 @@ CREATE TABLE IF NOT EXISTS Users
|
|||
Salt BLOB 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
|
||||
|
||||
}
|
19
src/Ombi.sln
19
src/Ombi.sln
|
@ -49,8 +49,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Notifications", "Ombi.
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Notifications", "Notifications", "{EA30DD15-6280-4687-B370-2956EC2E54E5}"
|
||||
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}"
|
||||
EndProject
|
||||
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
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Discord", "Ombi.Api.Discord\Ombi.Api.Discord.csproj", "{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}"
|
||||
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
|
||||
Global
|
||||
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}.Release|Any CPU.ActiveCfg = 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.Build.0 = Debug|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}.Release|Any CPU.ActiveCfg = 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
|
||||
{D7B05CF2-E6B3-4BE5-8A02-6698CC0DCE9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D7B05CF2-E6B3-4BE5-8A02-6698CC0DCE9A}.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}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DC9E3E44-802A-4D9B-A70F-D198CFE4C73D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DC9E3E44-802A-4D9B-A70F-D198CFE4C73D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DC9E3E44-802A-4D9B-A70F-D198CFE4C73D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -173,12 +167,11 @@ Global
|
|||
{CFB5E008-D0D0-43C0-AA06-89E49D17F384} = {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}
|
||||
{72DB97D7-2D60-4B96-8C57-6C0E20E892EB} = {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}
|
||||
{FC6A8F7C-9722-4AE4-960D-277ACB0E81CB} = {6F42AB98-9196-44C4-B888-D5E409F415A1}
|
||||
{94D04C1F-E35A-499C-B0A0-9FADEBDF8336} = {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
|
||||
EndGlobal
|
||||
|
|
3
src/Ombi/.gitignore
vendored
3
src/Ombi/.gitignore
vendored
|
@ -2,8 +2,7 @@
|
|||
/wwwroot/fonts/**
|
||||
/wwwroot/lib/**
|
||||
/wwwroot/maps/**
|
||||
/wwwroot/app/**/*.js
|
||||
/wwwroot/app/**/*.js.map
|
||||
/wwwroot/dist/**
|
||||
/wwwroot/*.js.map
|
||||
/wwwroot/*.js
|
||||
|
||||
|
|
3
src/Ombi/ClientApp/.gitignore
vendored
Normal file
3
src/Ombi/ClientApp/.gitignore
vendored
Normal file
|
@ -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">
|
||||
<div class="container-fluid">
|
|
@ -7,14 +7,10 @@ import { ILocalUser } from './auth/IUserLogin';
|
|||
|
||||
import { ICustomizationSettings } from './interfaces/ISettings';
|
||||
|
||||
import style from './app.component.css';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'ombi',
|
||||
moduleId: module.id,
|
||||
templateUrl: './app.component.html',
|
||||
styles: [style]
|
||||
styleUrls: ['./app.component.scss']
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
|
|
@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
|
|||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
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 { HttpModule } from '@angular/http';
|
||||
|
||||
|
@ -26,8 +26,8 @@ import { SeriesInformationComponent } from './search/seriesinformation.component
|
|||
import { RequestComponent } from './requests/request.component';
|
||||
import { MovieRequestsComponent } from './requests/movierequests.component';
|
||||
import { TvRequestsComponent } from './requests/tvrequests.component';
|
||||
import { RequestGridComponent } from './requests/request-grid.component';
|
||||
import { RequestCardComponent } from './requests/request-card.component';
|
||||
import { RequestGridComponent } from './request-grid/request-grid.component';
|
||||
import { RequestCardComponent } from './request-grid/request-card.component';
|
||||
|
||||
import { LoginComponent } from './login/login.component';
|
||||
import { LandingPageComponent } from './landingpage/landingpage.component';
|
||||
|
@ -81,7 +81,9 @@ const routes: Routes = [
|
|||
MdButtonModule,
|
||||
NgbModule.forRoot(),
|
||||
DragulaModule,
|
||||
MdCardModule
|
||||
MdCardModule,
|
||||
MdInputModule,
|
||||
MdTabsModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent,
|
|
@ -5,7 +5,7 @@ import { AuthHttp, AuthConfig } from 'angular2-jwt';
|
|||
export function authHttpServiceFactory(http: Http, options: RequestOptions) {
|
||||
return new AuthHttp(new AuthConfig({
|
||||
tokenName: 'id_token',
|
||||
tokenGetter: (() => localStorage.getItem('id_token')),
|
||||
tokenGetter: (() => localStorage.getItem('id_token')!),
|
||||
globalHeaders: [{ 'Content-Type': 'application/json' }],
|
||||
}), http, options);
|
||||
}
|
|
@ -31,7 +31,9 @@ export class AuthService extends ServiceHelpers {
|
|||
claims(): ILocalUser {
|
||||
if (this.loggedIn()) {
|
||||
var token = localStorage.getItem('id_token');
|
||||
|
||||
if (!token) {
|
||||
throw "Invalid token";
|
||||
}
|
||||
var json = this.jwtHelper.decodeToken(token);
|
||||
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"];
|
|
@ -1,7 +1,6 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
template: '<h2>Page not found</h2>'
|
||||
})
|
||||
export class PageNotFoundComponent { }
|
|
@ -14,7 +14,7 @@
|
|||
available: boolean,
|
||||
otherMessage: string,
|
||||
adminNote: string,
|
||||
requestedUser: string[],
|
||||
requestedUser: string,
|
||||
issueId: number,
|
||||
denied: boolean,
|
||||
deniedReason: string,
|
|
@ -1,37 +1,37 @@
|
|||
export interface ISettings {
|
||||
id:number
|
||||
id: number
|
||||
}
|
||||
|
||||
export interface IExternalSettings extends ISettings {
|
||||
ssl: boolean,
|
||||
subDir: string,
|
||||
ip: string,
|
||||
port:number,
|
||||
port: number,
|
||||
}
|
||||
|
||||
export interface IOmbiSettings extends ISettings {
|
||||
port: number,
|
||||
//baseUrl:string,
|
||||
//baseUrl:string,
|
||||
collectAnalyticData: boolean,
|
||||
wizard: boolean,
|
||||
apiKey:string
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
export interface IEmbySettings extends IExternalSettings {
|
||||
apiKey: string,
|
||||
enable: boolean,
|
||||
administratorId: string,
|
||||
enableEpisodeSearching:boolean,
|
||||
enableEpisodeSearching: boolean,
|
||||
}
|
||||
|
||||
export interface IPlexSettings extends ISettings {
|
||||
|
||||
enable: boolean,
|
||||
servers : IPlexServer[]
|
||||
servers: IPlexServer[]
|
||||
}
|
||||
|
||||
export interface IPlexServer extends IExternalSettings {
|
||||
name:string,
|
||||
name: string,
|
||||
enableEpisodeSearching: boolean,
|
||||
plexAuthToken: string,
|
||||
machineIdentifier: string,
|
||||
|
@ -41,7 +41,7 @@ export interface IPlexServer extends IExternalSettings {
|
|||
export interface IPlexLibraries {
|
||||
key: string,
|
||||
title: string,
|
||||
enabled:boolean,
|
||||
enabled: boolean,
|
||||
}
|
||||
|
||||
export interface ISonarrSettings extends IExternalSettings {
|
||||
|
@ -50,7 +50,7 @@ export interface ISonarrSettings extends IExternalSettings {
|
|||
qualityProfile: string,
|
||||
seasonFolders: boolean,
|
||||
rootPath: string,
|
||||
fullRootPath:string,
|
||||
fullRootPath: string,
|
||||
}
|
||||
|
||||
export interface IRadarrSettings extends IExternalSettings {
|
||||
|
@ -58,7 +58,8 @@ export interface IRadarrSettings extends IExternalSettings {
|
|||
apiKey: string,
|
||||
defaultQualityProfile: string,
|
||||
defaultRootPath: string,
|
||||
fullRootPath:string,
|
||||
fullRootPath: string,
|
||||
addOnly: boolean,
|
||||
}
|
||||
|
||||
export interface ILandingPageSettings extends ISettings {
|
||||
|
@ -70,10 +71,10 @@ export interface ILandingPageSettings extends ISettings {
|
|||
noticeBackgroundColor: string,
|
||||
timeLimit: boolean,
|
||||
startDateTime: Date,
|
||||
endDateTime:Date,
|
||||
endDateTime: Date,
|
||||
}
|
||||
|
||||
export interface ICustomizationSettings extends ISettings {
|
||||
applicationName: string,
|
||||
logo:string,
|
||||
logo: string,
|
||||
}
|
|
@ -6,9 +6,8 @@ import { IRequestCountModel } from '../interfaces/IRequestModel';
|
|||
|
||||
@Component({
|
||||
selector: 'ombi',
|
||||
moduleId: module.id,
|
||||
templateUrl: './landingpage.component.html',
|
||||
styleUrls: ['./landingpage.component.css']
|
||||
styleUrls: ['./landingpage.component.scss']
|
||||
})
|
||||
export class LandingPageComponent implements OnInit {
|
||||
|
|
@ -9,7 +9,6 @@ import { NotificationService } from '../services/notification.service';
|
|||
|
||||
@Component({
|
||||
selector: 'ombi',
|
||||
moduleId: module.id,
|
||||
templateUrl: './login.component.html',
|
||||
})
|
||||
export class LoginComponent {
|
|
@ -4,7 +4,6 @@ import { IMediaBase } from '../interfaces/IRequestModel';
|
|||
|
||||
@Component({
|
||||
selector: 'request-card',
|
||||
moduleId: module.id,
|
||||
templateUrl: './request-card.component.html'
|
||||
})
|
||||
export class RequestCardComponent {
|
|
@ -4,7 +4,6 @@ import { RequestService } from '../services/request.service';
|
|||
import { ITvRequestModel, IMovieRequestModel, IRequestGrid } from '../interfaces/IRequestModel';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
templateUrl: './request-grid.component.html'
|
||||
})
|
||||
export class RequestGridComponent implements OnInit {
|
|
@ -51,14 +51,7 @@
|
|||
<div>Release Date: {{request.releaseDate | date}}</div>
|
||||
<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>Requested Date: {{request.requestedDate | date}}</div>
|
|
@ -17,7 +17,6 @@ import { IMovieRequestModel } from '../interfaces/IRequestModel';
|
|||
|
||||
@Component({
|
||||
selector: 'movie-requests',
|
||||
moduleId: module.id,
|
||||
templateUrl: './movierequests.component.html'
|
||||
})
|
||||
export class MovieRequestsComponent implements OnInit, OnDestroy {
|
|
@ -1,8 +1,6 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ombi',
|
||||
moduleId: module.id,
|
||||
templateUrl: './request.component.html'
|
||||
})
|
||||
export class RequestComponent {
|
|
@ -78,29 +78,8 @@
|
|||
</div>
|
||||
<!--Child Requests-->
|
||||
|
||||
<button type="button" class="btn btn-sm btn-info-outline" data-toggle="collapse" [attr.data-target]="'#' + request.id +'childRequests'">Children</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>
|
||||
<button type="button" class="btn btn-sm btn-info-outline" (click)="showChildren(request)">Requests</button>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="col-sm-3 col-sm-push-3">
|
||||
|
@ -150,23 +129,27 @@
|
|||
{{/if_eq}}
|
||||
</form>-->
|
||||
|
||||
|
||||
|
||||
<div *ngIf="!request.denied">
|
||||
<form>
|
||||
<input name="requestId" type="text" value="{{request.requestId}}" hidden="hidden" />
|
||||
<input name="reason" type="text" hidden="hidden" />
|
||||
<div class="btn-group btn-split">
|
||||
<button type="button" (click)="deny(request)" class="btn btn-sm btn-danger-outline deny"><i class="fa fa-times"></i> Deny</button>
|
||||
<button type="button" class="btn btn-danger-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="caret"></span>
|
||||
<span class="sr-only">@UI.Requests_ToggleDropdown</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="deny-with-reason" id="denyReason{{request.requestId}}" href="#" data-toggle="modal" data-target="#denyReasonModal">Deny with a reason</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</form>
|
||||
<input name="requestId" type="text" value="{{request.requestId}}" hidden="hidden" />
|
||||
<input name="reason" type="text" hidden="hidden" />
|
||||
<div class="btn-group-sm btn-split">
|
||||
<a (click)="deny(request)" class="btn btn-sm btn-danger-outline deny"><i class="fa fa-times"></i> Deny</a>
|
||||
<a class="btn btn-sm btn-danger-outline dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="deny-with-reason" id="denyReason{{request.requestId}}" href="#" data-toggle="modal" data-target="#denyReasonModal">Deny with a reason</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<a href="#" class="btn btn-default">Default</a>
|
||||
<a href="#" class="btn btn-default dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></a>
|
||||
<ul class="dropdown-menu btn-split">
|
||||
<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>
|
||||
<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, 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>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
@ -202,9 +181,56 @@
|
|||
</div>
|
||||
</div>
|
||||
<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">×</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>
|
||||
|
|
@ -13,11 +13,10 @@ import 'rxjs/add/operator/map';
|
|||
import { RequestService } from '../services/request.service';
|
||||
import { IdentityService } from '../services/identity.service';
|
||||
|
||||
import { ITvRequestModel } from '../interfaces/IRequestModel';
|
||||
import { ITvRequestModel, IChildTvRequest } from '../interfaces/IRequestModel';
|
||||
|
||||
@Component({
|
||||
selector: 'tv-requests',
|
||||
moduleId: module.id,
|
||||
templateUrl: './tvrequests.component.html'
|
||||
})
|
||||
export class TvRequestsComponent implements OnInit, OnDestroy {
|
||||
|
@ -51,6 +50,9 @@ export class TvRequestsComponent implements OnInit, OnDestroy {
|
|||
private currentlyLoaded: number;
|
||||
private amountToLoad: number;
|
||||
|
||||
public showChildDialogue = false; // This is for the child modal popup
|
||||
public selectedSeason : ITvRequestModel;
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
this.amountToLoad = 5;
|
||||
|
@ -96,6 +98,17 @@ export class TvRequestsComponent implements OnInit, OnDestroy {
|
|||
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) {
|
||||
this.requestService.updateTvRequest(request)
|
||||
.takeUntil(this.subscriptions)
|
|
@ -49,13 +49,13 @@
|
|||
<span *ngIf="result.available" class="label label-success">Available</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>
|
||||
<template #requested>
|
||||
<ng-template #requested>
|
||||
<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>
|
||||
</template>
|
||||
</ng-template>
|
||||
|
||||
|
||||
<span id="{{id}}netflixTab"></span>
|
||||
|
@ -87,12 +87,12 @@
|
|||
</div>
|
||||
<div *ngIf="!result.available">
|
||||
<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>
|
||||
</template>
|
||||
<template #notRequestedBtn>
|
||||
</ng-template>
|
||||
<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>
|
||||
</template>
|
||||
</ng-template>
|
||||
</div>
|
||||
<!--{{#if_eq type "tv"}}
|
||||
{{#if_eq tvFullyAvailable true}}
|
|
@ -14,7 +14,6 @@ import { IRequestEngineResult } from '../interfaces/IRequestEngineResult';
|
|||
|
||||
@Component({
|
||||
selector: 'movie-search',
|
||||
moduleId: module.id,
|
||||
templateUrl: './moviesearch.component.html',
|
||||
})
|
||||
export class MovieSearchComponent implements OnInit, OnDestroy {
|
||||
|
@ -116,11 +115,20 @@ export class MovieSearchComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
private getExtaInfo() {
|
||||
this.searchService.extraInfo(this.movieResults)
|
||||
.takeUntil(this.subscriptions)
|
||||
.subscribe(m => this.movieResults = m);
|
||||
|
||||
this.movieResults.forEach((val, index) => {
|
||||
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() {
|
||||
this.movieResults = [];
|
||||
this.searchApplied = false;
|
|
@ -1,7 +1,5 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
@Component({
|
||||
selector: 'ombi',
|
||||
moduleId: module.id,
|
||||
templateUrl: './search.component.html',
|
||||
})
|
||||
export class SearchComponent implements OnInit {
|
||||
|
@ -16,7 +14,7 @@ export class SearchComponent implements OnInit {
|
|||
|
||||
selectTab() {
|
||||
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.approved && !ep.available" class="label label-info">Processing Request</span>
|
||||
<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>
|
||||
</template>
|
||||
</ng-template>
|
||||
|
||||
<template #notRequested>
|
||||
<ng-template #notRequested>
|
||||
<span *ngIf="!ep.available" class="label label-danger">Not Yet Requested</span>
|
||||
</template>
|
||||
</ng-template>
|
||||
</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>
|
||||
</tr>
|
||||
</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>
|
|
@ -13,10 +13,8 @@ import { IRequestEngineResult } from '../interfaces/IRequestEngineResult';
|
|||
import { IEpisodesRequested } from "../interfaces/IRequestModel";
|
||||
|
||||
@Component({
|
||||
selector: 'ombi',
|
||||
moduleId: module.id,
|
||||
templateUrl: './seriesinformation.component.html',
|
||||
styleUrls: ['./seriesinformation.component.css']
|
||||
templateUrl: './seriesinformation.component.html',
|
||||
styleUrls: ['./seriesinformation.component.scss']
|
||||
})
|
||||
export class SeriesInformationComponent implements OnInit, OnDestroy {
|
||||
|
||||
|
@ -31,9 +29,9 @@ export class SeriesInformationComponent implements OnInit, OnDestroy {
|
|||
|
||||
private subscriptions = new Subject<void>();
|
||||
|
||||
result : IRequestEngineResult;
|
||||
seriesId: number;
|
||||
series: ISearchTvResult;
|
||||
public result : IRequestEngineResult;
|
||||
private seriesId: number;
|
||||
public series: ISearchTvResult;
|
||||
|
||||
requestedEpisodes: IEpisodesRequested[] = [];
|
||||
|
||||
|
@ -42,13 +40,14 @@ export class SeriesInformationComponent implements OnInit, OnDestroy {
|
|||
this.searchService.getShowInformation(this.seriesId)
|
||||
.takeUntil(this.subscriptions)
|
||||
.subscribe(x => {
|
||||
this.series = x as ISearchTvResult;
|
||||
this.series = x;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
request() {
|
||||
public submitRequests() {
|
||||
this.series.requested = true;
|
||||
|
||||
this.requestService.requestTv(this.series)
|
||||
.takeUntil(this.subscriptions)
|
||||
.subscribe(x => {
|
||||
|
@ -62,10 +61,8 @@ export class SeriesInformationComponent implements OnInit, OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
addRequest(episode: IEpisodesRequested) {
|
||||
this.requestedEpisodes.push(episode);
|
||||
public addRequest(episode: IEpisodesRequested) {
|
||||
episode.requested = true;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -59,13 +59,13 @@
|
|||
<span *ngIf="result.available" class="label label-success">Available</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>
|
||||
<template #requested>
|
||||
<ng-template #requested>
|
||||
<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>
|
||||
</template>
|
||||
</ng-template>
|
||||
|
||||
|
||||
<span id="{{id}}netflixTab"></span>
|
|
@ -13,12 +13,9 @@ import { NotificationService } from '../services/notification.service';
|
|||
import { ISearchTvResult } from '../interfaces/ISearchTvResult';
|
||||
import { IRequestEngineResult } from '../interfaces/IRequestEngineResult';
|
||||
|
||||
import template from './tvsearch.component.html';
|
||||
|
||||
@Component({
|
||||
selector: 'tv-search',
|
||||
moduleId: module.id,
|
||||
template: template,
|
||||
templateUrl: './tvsearch.component.html',
|
||||
})
|
||||
export class TvSearchComponent implements OnInit, OnDestroy {
|
||||
|
||||
|
@ -148,7 +145,7 @@ export class TvSearchComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
selectSeason(searchResult: ISearchTvResult) {
|
||||
this.route.navigate(['/search/show', searchResult.seriesId]);
|
||||
this.route.navigate(['/search/show', searchResult.id]);
|
||||
}
|
||||
|
||||
private updateItem(key: ISearchTvResult, updated: ISearchTvResult) {
|
|
@ -37,7 +37,7 @@ export class IdentityService extends ServiceAuthHelpers {
|
|||
}
|
||||
|
||||
hasRole(role: string): boolean {
|
||||
var roles = localStorage.getItem("roles") as string[];
|
||||
var roles = localStorage.getItem("roles") as string[] | null;
|
||||
if (roles) {
|
||||
if (roles.indexOf(role) > -1) {
|
||||
return true;
|
|
@ -6,6 +6,7 @@ export class NotificationService {
|
|||
messages: Message[] = [];
|
||||
public addMessage(message: 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) {
|
|
@ -52,7 +52,7 @@ export class RequestService extends ServiceAuthHelpers {
|
|||
}
|
||||
|
||||
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> {
|
|
@ -32,6 +32,9 @@ export class SearchService extends ServiceAuthHelpers {
|
|||
extraInfo(movies: ISearchMovieResult[]): Observable<ISearchMovieResult[]> {
|
||||
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
|
||||
searchTv(searchTerm: string): Observable<ISearchTvResult[]> {
|
|
@ -6,7 +6,6 @@ import { NotificationService } from "../../services/notification.service";
|
|||
|
||||
@Component({
|
||||
selector: 'ombi',
|
||||
moduleId: module.id,
|
||||
templateUrl: './customization.component.html',
|
||||
})
|
||||
export class CustomizationComponent implements OnInit {
|
|
@ -6,7 +6,6 @@ import { NotificationService } from "../../services/notification.service";
|
|||
|
||||
@Component({
|
||||
selector: 'ombi',
|
||||
moduleId: module.id,
|
||||
templateUrl: './emby.component.html',
|
||||
})
|
||||
export class EmbyComponent implements OnInit {
|
|
@ -6,7 +6,6 @@ import { NotificationService } from "../../services/notification.service";
|
|||
|
||||
@Component({
|
||||
selector: 'ombi',
|
||||
moduleId: module.id,
|
||||
templateUrl: './landingpage.component.html',
|
||||
})
|
||||
export class LandingPageComponent implements OnInit {
|
|
@ -6,7 +6,6 @@ import { NotificationService } from "../../services/notification.service";
|
|||
|
||||
@Component({
|
||||
selector: 'ombi',
|
||||
moduleId: module.id,
|
||||
templateUrl: './ombi.component.html',
|
||||
})
|
||||
export class OmbiComponent implements OnInit {
|
|
@ -3,9 +3,10 @@
|
|||
<fieldset>
|
||||
<legend>Plex Configuration</legend>
|
||||
|
||||
<div class="checkbox col-md-2 " style="float: right;">
|
||||
<input type="checkbox" id="advanced" [(ngModel)]="advanced" ng-checked="advanced">
|
||||
<label for="advanced">Advanced</label>
|
||||
|
||||
<div style="float: right;">
|
||||
<span style="vertical-align: top;">Advanced</span>
|
||||
<p-inputSwitch id="customInputSwitch" [(ngModel)]="advanced"></p-inputSwitch>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-3">
|
|
@ -11,7 +11,6 @@ import { NotificationService } from "../../services/notification.service";
|
|||
|
||||
@Component({
|
||||
selector: 'ombi',
|
||||
moduleId: module.id,
|
||||
templateUrl: './plex.component.html',
|
||||
})
|
||||
export class PlexComponent implements OnInit, OnDestroy {
|
|
@ -3,10 +3,14 @@
|
|||
<div *ngIf="settings">
|
||||
<fieldset>
|
||||
<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="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>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -77,6 +81,13 @@
|
|||
</select>
|
||||
</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">
|
|
@ -10,7 +10,6 @@ import { NotificationService } from "../../services/notification.service";
|
|||
|
||||
@Component({
|
||||
selector: 'ombi',
|
||||
moduleId: module.id,
|
||||
templateUrl: './radarr.component.html',
|
||||
})
|
||||
export class RadarrComponent implements OnInit {
|
||||
|
@ -27,6 +26,8 @@ export class RadarrComponent implements OnInit {
|
|||
|
||||
profilesRunning: boolean;
|
||||
rootFoldersRunning: boolean;
|
||||
|
||||
advanced = false;
|
||||
private subscriptions = new Subject<void>();
|
||||
|
||||
ngOnInit(): void {
|
|
@ -1,7 +1,6 @@
|
|||
import { Component } from '@angular/core';
|
||||
@Component({
|
||||
selector: 'settings-menu',
|
||||
moduleId: module.id,
|
||||
templateUrl: './settingsmenu.component.html'
|
||||
})
|
||||
export class SettingsMenuComponent {
|
|
@ -10,7 +10,6 @@ import { NotificationService } from "../../services/notification.service";
|
|||
|
||||
@Component({
|
||||
selector: 'ombi',
|
||||
moduleId: module.id,
|
||||
templateUrl: './sonarr.component.html',
|
||||
})
|
||||
export class SonarrComponent implements OnInit, OnDestroy {
|
|
@ -5,7 +5,6 @@ import { IdentityService } from '../services/identity.service';
|
|||
|
||||
@Component({
|
||||
selector: 'ombi',
|
||||
moduleId: module.id,
|
||||
templateUrl: './usermanagement.component.html'
|
||||
})
|
||||
export class UserManagementComponent implements OnInit {
|
|
@ -8,7 +8,6 @@ import { NotificationService } from '../../services/notification.service';
|
|||
|
||||
@Component({
|
||||
selector: 'ombi',
|
||||
moduleId: module.id,
|
||||
templateUrl: './createadmin.component.html',
|
||||
})
|
||||
export class CreateAdminComponent {
|
|
@ -8,7 +8,6 @@ import { IEmbySettings } from '../../interfaces/ISettings';
|
|||
|
||||
@Component({
|
||||
selector: 'ombi',
|
||||
moduleId: module.id,
|
||||
templateUrl: './emby.component.html',
|
||||
})
|
||||
export class EmbyComponent {
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue