mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-11 07:46:05 -07:00
This commit is contained in:
parent
3935dcb811
commit
8b64c18ace
46 changed files with 961 additions and 284 deletions
15
Ombi/Ombi.Api.TvMaze/ITvMazeApi.cs
Normal file
15
Ombi/Ombi.Api.TvMaze/ITvMazeApi.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.TvMaze.Models;
|
||||
|
||||
namespace Ombi.Api.TvMaze
|
||||
{
|
||||
public interface ITvMazeApi
|
||||
{
|
||||
Task<IEnumerable<TvMazeEpisodes>> EpisodeLookup(int showId);
|
||||
Task<List<TvMazeSeasons>> GetSeasons(int id);
|
||||
Task<List<TvMazeSearch>> Search(string searchTerm);
|
||||
Task<TvMazeShow> ShowLookup(int showId);
|
||||
Task<TvMazeShow> ShowLookupByTheTvDbId(int theTvDbId);
|
||||
}
|
||||
}
|
9
Ombi/Ombi.Api.TvMaze/Models/Search/Country.cs
Normal file
9
Ombi/Ombi.Api.TvMaze/Models/Search/Country.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ombi.Api.TvMaze.Models
|
||||
{
|
||||
public class Country
|
||||
{
|
||||
public string code { get; set; }
|
||||
public string name { get; set; }
|
||||
public string timezone { get; set; }
|
||||
}
|
||||
}
|
9
Ombi/Ombi.Api.TvMaze/Models/Search/Externals.cs
Normal file
9
Ombi/Ombi.Api.TvMaze/Models/Search/Externals.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ombi.Api.TvMaze.Models
|
||||
{
|
||||
public class Externals
|
||||
{
|
||||
public string imdb { get; set; }
|
||||
public int? thetvdb { get; set; }
|
||||
public int? tvrage { get; set; }
|
||||
}
|
||||
}
|
8
Ombi/Ombi.Api.TvMaze/Models/Search/Image.cs
Normal file
8
Ombi/Ombi.Api.TvMaze/Models/Search/Image.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.TvMaze.Models
|
||||
{
|
||||
public class Image
|
||||
{
|
||||
public string medium { get; set; }
|
||||
public string original { get; set; }
|
||||
}
|
||||
}
|
9
Ombi/Ombi.Api.TvMaze/Models/Search/Links.cs
Normal file
9
Ombi/Ombi.Api.TvMaze/Models/Search/Links.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ombi.Api.TvMaze.Models
|
||||
{
|
||||
public class Links
|
||||
{
|
||||
public Nextepisode nextepisode { get; set; }
|
||||
public Previousepisode previousepisode { get; set; }
|
||||
public Self self { get; set; }
|
||||
}
|
||||
}
|
9
Ombi/Ombi.Api.TvMaze/Models/Search/Network.cs
Normal file
9
Ombi/Ombi.Api.TvMaze/Models/Search/Network.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ombi.Api.TvMaze.Models
|
||||
{
|
||||
public class Network
|
||||
{
|
||||
public Country country { get; set; }
|
||||
public int id { get; set; }
|
||||
public string name { get; set; }
|
||||
}
|
||||
}
|
7
Ombi/Ombi.Api.TvMaze/Models/Search/Nextepisode.cs
Normal file
7
Ombi/Ombi.Api.TvMaze/Models/Search/Nextepisode.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Ombi.Api.TvMaze.Models
|
||||
{
|
||||
public class Nextepisode
|
||||
{
|
||||
public string href { get; set; }
|
||||
}
|
||||
}
|
7
Ombi/Ombi.Api.TvMaze/Models/Search/Previousepisode.cs
Normal file
7
Ombi/Ombi.Api.TvMaze/Models/Search/Previousepisode.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Ombi.Api.TvMaze.Models
|
||||
{
|
||||
public class Previousepisode
|
||||
{
|
||||
public string href { get; set; }
|
||||
}
|
||||
}
|
7
Ombi/Ombi.Api.TvMaze/Models/Search/Rating.cs
Normal file
7
Ombi/Ombi.Api.TvMaze/Models/Search/Rating.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Ombi.Api.TvMaze.Models
|
||||
{
|
||||
public class Rating
|
||||
{
|
||||
public double? average { get; set; }
|
||||
}
|
||||
}
|
10
Ombi/Ombi.Api.TvMaze/Models/Search/Schedule.cs
Normal file
10
Ombi/Ombi.Api.TvMaze/Models/Search/Schedule.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Api.TvMaze.Models
|
||||
{
|
||||
public class Schedule
|
||||
{
|
||||
public List<object> days { get; set; }
|
||||
public string time { get; set; }
|
||||
}
|
||||
}
|
7
Ombi/Ombi.Api.TvMaze/Models/Search/Self.cs
Normal file
7
Ombi/Ombi.Api.TvMaze/Models/Search/Self.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Ombi.Api.TvMaze.Models
|
||||
{
|
||||
public class Self
|
||||
{
|
||||
public string href { get; set; }
|
||||
}
|
||||
}
|
27
Ombi/Ombi.Api.TvMaze/Models/Search/Show.cs
Normal file
27
Ombi/Ombi.Api.TvMaze/Models/Search/Show.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Api.TvMaze.Models
|
||||
{
|
||||
public class Show
|
||||
{
|
||||
public Links _links { get; set; }
|
||||
public Externals externals { get; set; }
|
||||
public List<object> genres { get; set; }
|
||||
public int id { get; set; }
|
||||
public Image image { get; set; }
|
||||
public string language { get; set; }
|
||||
public string name { get; set; }
|
||||
public Network network { get; set; }
|
||||
public string premiered { get; set; }
|
||||
public Rating rating { get; set; }
|
||||
public int? runtime { get; set; }
|
||||
public Schedule schedule { get; set; }
|
||||
public string status { get; set; }
|
||||
public string summary { get; set; }
|
||||
public string type { get; set; }
|
||||
public int updated { get; set; }
|
||||
public string url { get; set; }
|
||||
public object webChannel { get; set; }
|
||||
public int weight { get; set; }
|
||||
}
|
||||
}
|
8
Ombi/Ombi.Api.TvMaze/Models/Search/TvMazeSearch.cs
Normal file
8
Ombi/Ombi.Api.TvMaze/Models/Search/TvMazeSearch.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.TvMaze.Models
|
||||
{
|
||||
public class TvMazeSearch
|
||||
{
|
||||
public double score { get; set; }
|
||||
public Show show { get; set; }
|
||||
}
|
||||
}
|
7
Ombi/Ombi.Api.TvMaze/Models/TVMazeSeasons.cs
Normal file
7
Ombi/Ombi.Api.TvMaze/Models/TVMazeSeasons.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Ombi.Api.TvMaze.Models
|
||||
{
|
||||
public class TvMazeSeasons : TvMazeShow
|
||||
{
|
||||
public int number { get; set; }
|
||||
}
|
||||
}
|
18
Ombi/Ombi.Api.TvMaze/Models/TvMazeEpisode.cs
Normal file
18
Ombi/Ombi.Api.TvMaze/Models/TvMazeEpisode.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
namespace Ombi.Api.TvMaze.Models
|
||||
{
|
||||
public class TvMazeEpisodes
|
||||
{
|
||||
public int id { get; set; }
|
||||
public string url { get; set; }
|
||||
public string name { get; set; }
|
||||
public int season { get; set; }
|
||||
public int number { get; set; }
|
||||
public string airdate { get; set; }
|
||||
public string airtime { get; set; }
|
||||
public string airstamp { get; set; }
|
||||
public int runtime { get; set; }
|
||||
public Image image { get; set; }
|
||||
public string summary { get; set; }
|
||||
public Links _links { get; set; }
|
||||
}
|
||||
}
|
89
Ombi/Ombi.Api.TvMaze/Models/TvMazeShow.cs
Normal file
89
Ombi/Ombi.Api.TvMaze/Models/TvMazeShow.cs
Normal file
|
@ -0,0 +1,89 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Api.TvMaze.Models
|
||||
{
|
||||
public class TvMazeShow
|
||||
{
|
||||
public TvMazeShow()
|
||||
{
|
||||
Season = new List<TvMazeCustomSeason>();
|
||||
}
|
||||
public int id { get; set; }
|
||||
public string url { get; set; }
|
||||
public string name { get; set; }
|
||||
public string type { get; set; }
|
||||
public string language { get; set; }
|
||||
public List<string> genres { get; set; }
|
||||
public string status { get; set; }
|
||||
public double runtime { get; set; }
|
||||
public string premiered { get; set; }
|
||||
public Schedule schedule { get; set; }
|
||||
public Rating rating { get; set; }
|
||||
public int weight { get; set; }
|
||||
public Network network { get; set; }
|
||||
public object webChannel { get; set; }
|
||||
public Externals externals { get; set; }
|
||||
public Image image { get; set; }
|
||||
public string summary { get; set; }
|
||||
public int updated { get; set; }
|
||||
public Links _links { get; set; }
|
||||
public List<TvMazeCustomSeason> Season { get; set; }
|
||||
public Embedded _embedded { get; set; }
|
||||
}
|
||||
|
||||
public class TvMazeCustomSeason
|
||||
{
|
||||
public int SeasonNumber { get; set; }
|
||||
public int EpisodeNumber { get; set; }
|
||||
}
|
||||
|
||||
public class Season
|
||||
{
|
||||
public int id { get; set; }
|
||||
public string url { get; set; }
|
||||
public int number { get; set; }
|
||||
public string name { get; set; }
|
||||
public int? episodeOrder { get; set; }
|
||||
public string premiereDate { get; set; }
|
||||
public string endDate { get; set; }
|
||||
public Network2 network { get; set; }
|
||||
public object webChannel { get; set; }
|
||||
public Image2 image { get; set; }
|
||||
public string summary { get; set; }
|
||||
public Links2 _links { get; set; }
|
||||
}
|
||||
public class Country2
|
||||
{
|
||||
public string name { get; set; }
|
||||
public string code { get; set; }
|
||||
public string timezone { get; set; }
|
||||
}
|
||||
|
||||
public class Network2
|
||||
{
|
||||
public int id { get; set; }
|
||||
public string name { get; set; }
|
||||
public Country2 country { get; set; }
|
||||
}
|
||||
|
||||
public class Image2
|
||||
{
|
||||
public string medium { get; set; }
|
||||
public string original { get; set; }
|
||||
}
|
||||
|
||||
public class Self2
|
||||
{
|
||||
public string href { get; set; }
|
||||
}
|
||||
|
||||
public class Links2
|
||||
{
|
||||
public Self2 self { get; set; }
|
||||
}
|
||||
|
||||
public class Embedded
|
||||
{
|
||||
public List<Season> seasons { get; set; }
|
||||
}
|
||||
}
|
11
Ombi/Ombi.Api.TvMaze/Ombi.Api.TvMaze.csproj
Normal file
11
Ombi/Ombi.Api.TvMaze/Ombi.Api.TvMaze.csproj
Normal file
|
@ -0,0 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard1.6</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
84
Ombi/Ombi.Api.TvMaze/TvMazeApi.cs
Normal file
84
Ombi/Ombi.Api.TvMaze/TvMazeApi.cs
Normal file
|
@ -0,0 +1,84 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.TvMaze.Models;
|
||||
|
||||
namespace Ombi.Api.TvMaze
|
||||
{
|
||||
public class TvMazeApi : ITvMazeApi
|
||||
{
|
||||
public TvMazeApi()
|
||||
{
|
||||
Api = new Ombi.Api.Api();
|
||||
//Mapper = mapper;
|
||||
}
|
||||
private string Uri = "http://api.tvmaze.com";
|
||||
private Api Api { get; }
|
||||
public async Task<List<TvMazeSearch>> Search(string searchTerm)
|
||||
{
|
||||
var request = new Request("search/shows", Uri, HttpMethod.Get);
|
||||
|
||||
request.AddQueryString("q", searchTerm);
|
||||
request.AddHeader("Content-Type", "application/json");
|
||||
|
||||
return await Api.Request<List<TvMazeSearch>>(request);
|
||||
}
|
||||
|
||||
public async Task<TvMazeShow> ShowLookup(int showId)
|
||||
{
|
||||
var request = new Request($"shows/{showId}", Uri, HttpMethod.Get);
|
||||
request.AddHeader("Content-Type", "application/json");
|
||||
|
||||
return await Api.Request<TvMazeShow>(request);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TvMazeEpisodes>> EpisodeLookup(int showId)
|
||||
{
|
||||
|
||||
var request = new Request($"shows/{showId}/episodes", Uri, HttpMethod.Get);
|
||||
|
||||
request.AddHeader("Content-Type", "application/json");
|
||||
|
||||
return await Api.Request<List<TvMazeEpisodes>>(request);
|
||||
}
|
||||
|
||||
public async Task<TvMazeShow> ShowLookupByTheTvDbId(int theTvDbId)
|
||||
{
|
||||
var request = new Request($"lookup/shows?thetvdb={theTvDbId}", Uri, HttpMethod.Get);
|
||||
request.AddHeader("Content-Type", "application/json");
|
||||
try
|
||||
{
|
||||
var obj = await Api.Request<TvMazeShow>(request);
|
||||
|
||||
var episodes = await EpisodeLookup(obj.id);
|
||||
|
||||
foreach (var e in episodes)
|
||||
{
|
||||
obj.Season.Add(new TvMazeCustomSeason
|
||||
{
|
||||
SeasonNumber = e.season,
|
||||
EpisodeNumber = e.number
|
||||
});
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<TvMazeSeasons>> GetSeasons(int id)
|
||||
{
|
||||
var request = new Request($"shows/{id}/seasons", Uri, HttpMethod.Get);
|
||||
|
||||
request.AddHeader("Content-Type", "application/json");
|
||||
|
||||
return await Api.Request<List<TvMazeSeasons>>(request);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -78,6 +78,29 @@ namespace Ombi.Api
|
|||
ContentHeaders.Add(new KeyValuePair<string, string>(key, value));
|
||||
}
|
||||
|
||||
public void AddQueryString(string key, string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value)) return;
|
||||
|
||||
var builder = new UriBuilder(_modified);
|
||||
var startingTag = string.Empty;
|
||||
var hasQuery = false;
|
||||
if (string.IsNullOrEmpty(builder.Query))
|
||||
{
|
||||
startingTag = "?";
|
||||
}
|
||||
else
|
||||
{
|
||||
hasQuery = true;
|
||||
startingTag = builder.Query.Contains("?") ? "&" : "?";
|
||||
}
|
||||
|
||||
builder.Query = hasQuery
|
||||
? $"{builder.Query}{startingTag}{key}={value}"
|
||||
: $"{startingTag}{key}={value}";
|
||||
_modified = builder.Uri;
|
||||
}
|
||||
|
||||
public void AddJsonBody(object obj)
|
||||
{
|
||||
JsonBody = obj;
|
||||
|
|
|
@ -29,6 +29,8 @@ namespace Ombi.Core.Claims
|
|||
public static class OmbiClaims
|
||||
{
|
||||
public const string Admin = nameof(Admin);
|
||||
public const string AutoApproveMovie = nameof(AutoApproveMovie);
|
||||
public const string AutoApproveTv = nameof(AutoApproveTv);
|
||||
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Core.Claims;
|
||||
using Ombi.Core.Engine.Interfaces;
|
||||
using Ombi.Core.IdentityResolver;
|
||||
using Ombi.Core.Models.Requests;
|
||||
|
@ -13,7 +15,7 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
public abstract class BaseMediaEngine : BaseEngine
|
||||
{
|
||||
protected BaseMediaEngine(IUserIdentityManager identity, IRequestService service) : base(identity)
|
||||
protected BaseMediaEngine(IPrincipal identity, IRequestService service) : base(identity)
|
||||
{
|
||||
RequestService = service;
|
||||
}
|
||||
|
@ -36,5 +38,43 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
return _dbMovies;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<RequestViewModel>> GetRequests()
|
||||
{
|
||||
var allRequests = await RequestService.GetAllAsync();
|
||||
var viewModel = MapToVm(allRequests);
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
|
||||
protected IEnumerable<RequestViewModel> MapToVm(IEnumerable<RequestModel> model)
|
||||
{
|
||||
return model.Select(movie => new RequestViewModel
|
||||
{
|
||||
ProviderId = movie.ProviderId,
|
||||
Type = movie.Type,
|
||||
Status = movie.Status,
|
||||
ImdbId = movie.ImdbId,
|
||||
Id = movie.Id,
|
||||
PosterPath = movie.PosterPath,
|
||||
ReleaseDate = movie.ReleaseDate,
|
||||
RequestedDate = movie.RequestedDate,
|
||||
Released = DateTime.Now > movie.ReleaseDate,
|
||||
Approved = movie.Available || movie.Approved,
|
||||
Title = movie.Title,
|
||||
Overview = movie.Overview,
|
||||
RequestedUsers = movie.AllUsers.ToArray(),
|
||||
ReleaseYear = movie.ReleaseDate.Year.ToString(),
|
||||
Available = movie.Available,
|
||||
Admin = HasRole(OmbiClaims.Admin),
|
||||
IssueId = movie.IssueId,
|
||||
Denied = movie.Denied,
|
||||
DeniedReason = movie.DeniedReason,
|
||||
//Qualities = qualities.ToArray(),
|
||||
//HasRootFolders = rootFolders.Any(),
|
||||
//RootFolders = rootFolders.ToArray(),
|
||||
//CurrentRootPath = radarr.Enabled ? GetRootPath(movie.RootFolderSelected, radarr).Result : null
|
||||
}).ToList();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +1,56 @@
|
|||
using System.Threading.Tasks;
|
||||
using Ombi.Core.IdentityResolver;
|
||||
using Ombi.Core.Models;
|
||||
using System.Security.Principal;
|
||||
using Ombi.Core.Claims;
|
||||
using Ombi.Core.Settings.Models;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Core.Engine.Interfaces
|
||||
{
|
||||
public abstract class BaseEngine
|
||||
{
|
||||
protected BaseEngine(IUserIdentityManager identity)
|
||||
protected BaseEngine(IPrincipal user)
|
||||
{
|
||||
UserIdentity = identity;
|
||||
User = user;
|
||||
}
|
||||
|
||||
protected IPrincipal User { get; }
|
||||
|
||||
protected string Username => User.Identity.Name;
|
||||
|
||||
protected bool HasRole(string roleName)
|
||||
{
|
||||
return User.IsInRole(roleName);
|
||||
}
|
||||
|
||||
protected IUserIdentityManager UserIdentity { get; }
|
||||
|
||||
protected async Task<UserDto> GetUser(string username)
|
||||
protected bool ShouldSendNotification(RequestType type)
|
||||
{
|
||||
|
||||
var sendNotification = !ShouldAutoApprove(type); /*|| !prSettings.IgnoreNotifyForAutoApprovedRequests;*/
|
||||
|
||||
if (HasRole(OmbiClaims.Admin))
|
||||
{
|
||||
sendNotification = false; // Don't bother sending a notification if the user is an admin
|
||||
|
||||
}
|
||||
return sendNotification;
|
||||
}
|
||||
|
||||
public bool ShouldAutoApprove(RequestType requestType)
|
||||
{
|
||||
var admin = HasRole(OmbiClaims.Admin);
|
||||
// if the user is an admin, they go ahead and allow auto-approval
|
||||
if (admin) return true;
|
||||
|
||||
// check by request type if the category requires approval or not
|
||||
switch (requestType)
|
||||
{
|
||||
case RequestType.Movie:
|
||||
return HasRole(OmbiClaims.AutoApproveMovie);
|
||||
case RequestType.TvShow:
|
||||
return HasRole(OmbiClaims.AutoApproveTv);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
|
@ -13,10 +15,10 @@ using Ombi.Store.Entities;
|
|||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
public class MovieEngine : BaseMediaEngine, IMovieEngine
|
||||
public class MovieSearchEngine : BaseMediaEngine, IMovieEngine
|
||||
{
|
||||
|
||||
public MovieEngine(IUserIdentityManager identity, IRequestService service, IMovieDbApi movApi, IMapper mapper, ISettingsService<PlexSettings> plexSettings, ISettingsService<EmbySettings> embySettings)
|
||||
public MovieSearchEngine(IPrincipal identity, IRequestService service, IMovieDbApi movApi, IMapper mapper, ISettingsService<PlexSettings> plexSettings, ISettingsService<EmbySettings> embySettings)
|
||||
: base(identity, service)
|
||||
{
|
||||
MovieApi = movApi;
|
||||
|
@ -32,14 +34,26 @@ namespace Ombi.Core.Engine
|
|||
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> LookupImdbInformation(IEnumerable<SearchMovieViewModel> movies)
|
||||
{
|
||||
var searchMovieViewModels
|
||||
= movies as IList<SearchMovieViewModel> ?? movies.ToList();
|
||||
if (searchMovieViewModels == null || !searchMovieViewModels.Any())
|
||||
{
|
||||
return new List<SearchMovieViewModel>();
|
||||
}
|
||||
|
||||
var retVal = new List<SearchMovieViewModel>();
|
||||
Dictionary<int, RequestModel> dbMovies = await GetRequests(RequestType.Movie);
|
||||
foreach (var m in movies)
|
||||
|
||||
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
var embySettings = await EmbySettings.GetSettingsAsync();
|
||||
|
||||
foreach (var m in searchMovieViewModels)
|
||||
{
|
||||
var movieInfo = await MovieApi.GetMovieInformationWithVideo(m.Id);
|
||||
var viewMovie = Mapper.Map<SearchMovieViewModel>(movieInfo);
|
||||
|
||||
retVal.Add(await ProcessSingleMovie(viewMovie, dbMovies));
|
||||
retVal.Add(await ProcessSingleMovie(viewMovie, dbMovies, plexSettings, embySettings));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
@ -97,21 +111,21 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
var viewMovies = new List<SearchMovieViewModel>();
|
||||
Dictionary<int, RequestModel> dbMovies = await GetRequests(RequestType.Movie);
|
||||
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
var embySettings = await EmbySettings.GetSettingsAsync();
|
||||
foreach (var movie in movies)
|
||||
{
|
||||
|
||||
viewMovies.Add(await ProcessSingleMovie(movie, dbMovies));
|
||||
viewMovies.Add(await ProcessSingleMovie(movie, dbMovies, plexSettings, embySettings));
|
||||
|
||||
}
|
||||
return viewMovies;
|
||||
}
|
||||
|
||||
private async Task<SearchMovieViewModel> ProcessSingleMovie(SearchMovieViewModel viewMovie,
|
||||
Dictionary<int, RequestModel> existingRequests)
|
||||
{
|
||||
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
var embySettings = await EmbySettings.GetSettingsAsync();
|
||||
Dictionary<int, RequestModel> existingRequests, PlexSettings plexSettings, EmbySettings embySettings)
|
||||
{
|
||||
if (plexSettings.Enable)
|
||||
{
|
||||
// var content = PlexContentRepository.GetAll();
|
||||
|
@ -152,10 +166,10 @@ namespace Ombi.Core.Engine
|
|||
return viewMovie;
|
||||
}
|
||||
|
||||
private async Task<SearchMovieViewModel> ProcessSingleMovie(MovieSearchResult movie, Dictionary<int, RequestModel> existingRequests)
|
||||
private async Task<SearchMovieViewModel> ProcessSingleMovie(MovieSearchResult movie, Dictionary<int, RequestModel> existingRequests, PlexSettings plexSettings, EmbySettings embySettings)
|
||||
{
|
||||
var viewMovie = Mapper.Map<SearchMovieViewModel>(movie);
|
||||
return await ProcessSingleMovie(viewMovie, existingRequests);
|
||||
return await ProcessSingleMovie(viewMovie, existingRequests, plexSettings, embySettings);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,26 +2,24 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Core.Requests.Models;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.TheMovieDbApi;
|
||||
using Ombi.Helpers;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
public class RequestEngine : IRequestEngine
|
||||
public class RequestEngine : BaseMediaEngine, IRequestEngine
|
||||
{
|
||||
public RequestEngine(IMovieDbApi movieApi, IRequestService requestService)
|
||||
public RequestEngine(IMovieDbApi movieApi, IRequestService requestService, IPrincipal user) : base(user, requestService)
|
||||
{
|
||||
MovieApi = movieApi;
|
||||
RequestService = requestService;
|
||||
}
|
||||
private IMovieDbApi MovieApi { get; }
|
||||
private IRequestService RequestService { get; }
|
||||
public async Task<RequestEngineResult> RequestMovie(SearchMovieViewModel model)
|
||||
{
|
||||
var movieInfo = await MovieApi.GetMovieInformation(model.Id);
|
||||
|
@ -30,7 +28,8 @@ namespace Ombi.Core.Engine
|
|||
return new RequestEngineResult
|
||||
{
|
||||
RequestAdded = false,
|
||||
Message = "There was an issue adding this movie!"
|
||||
Message = "There was an issue adding this movie!",
|
||||
ErrorMessage = $"TheMovieDb didn't have any information for ID {model.Id}"
|
||||
};
|
||||
}
|
||||
var fullMovieName =
|
||||
|
@ -42,7 +41,7 @@ namespace Ombi.Core.Engine
|
|||
return new RequestEngineResult
|
||||
{
|
||||
RequestAdded = false,
|
||||
Message = "This has already been requested"
|
||||
Message = $"{fullMovieName} has already been requested"
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -86,7 +85,7 @@ namespace Ombi.Core.Engine
|
|||
Status = movieInfo.Status,
|
||||
RequestedDate = DateTime.UtcNow,
|
||||
Approved = false,
|
||||
//RequestedUsers = new List<string> { Username },
|
||||
RequestedUsers = new List<string> { Username },
|
||||
Issues = IssueState.None,
|
||||
};
|
||||
|
||||
|
@ -94,7 +93,7 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
if (ShouldAutoApprove(RequestType.Movie))
|
||||
{
|
||||
// model.Approved = true;
|
||||
model.Approved = true;
|
||||
|
||||
// var result = await MovieSender.Send(model);
|
||||
// if (result.Result)
|
||||
|
@ -154,43 +153,24 @@ namespace Ombi.Core.Engine
|
|||
return null;
|
||||
}
|
||||
|
||||
public bool ShouldAutoApprove(RequestType requestType)
|
||||
{
|
||||
//var admin = Security.HasPermissions(Context.CurrentUser, Permissions.Administrator);
|
||||
//// if the user is an admin, they go ahead and allow auto-approval
|
||||
//if (admin) return true;
|
||||
|
||||
//// check by request type if the category requires approval or not
|
||||
//switch (requestType)
|
||||
//{
|
||||
// case RequestType.Movie:
|
||||
// return Security.HasPermissions(User, Permissions.AutoApproveMovie);
|
||||
// case RequestType.TvShow:
|
||||
// return Security.HasPermissions(User, Permissions.AutoApproveTv);
|
||||
// case RequestType.Album:
|
||||
// return Security.HasPermissions(User, Permissions.AutoApproveAlbum);
|
||||
// default:
|
||||
// return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<RequestEngineResult> AddRequest(RequestModel model, /*PlexRequestSettings settings,*/ string message)
|
||||
private async Task<RequestEngineResult> AddRequest(RequestModel model, string message)
|
||||
{
|
||||
await RequestService.AddRequestAsync(model);
|
||||
|
||||
//if (ShouldSendNotification(model.Type, settings))
|
||||
//{
|
||||
// var notificationModel = new NotificationModel
|
||||
// {
|
||||
// Title = model.Title,
|
||||
// User = Username,
|
||||
// DateTime = DateTime.Now,
|
||||
// NotificationType = NotificationType.NewRequest,
|
||||
// RequestType = model.Type,
|
||||
// ImgSrc = model.Type == RequestType.Movie ? $"https://image.tmdb.org/t/p/w300/{model.PosterPath}" : model.PosterPath
|
||||
// };
|
||||
// await NotificationService.Publish(notificationModel);
|
||||
//}
|
||||
if (ShouldSendNotification(model.Type))
|
||||
{
|
||||
// var notificationModel = new NotificationModel
|
||||
// {
|
||||
// Title = model.Title,
|
||||
// User = Username,
|
||||
// DateTime = DateTime.Now,
|
||||
// NotificationType = NotificationType.NewRequest,
|
||||
// RequestType = model.Type,
|
||||
// ImgSrc = model.Type == RequestType.Movie ? $"https://image.tmdb.org/t/p/w300/{model.PosterPath}" : model.PosterPath
|
||||
// };
|
||||
// await NotificationService.Publish(notificationModel);
|
||||
}
|
||||
|
||||
//var limit = await RequestLimitRepo.GetAllAsync();
|
||||
//var usersLimit = limit.FirstOrDefault(x => x.Username == Username && x.RequestType == model.Type);
|
||||
|
@ -213,13 +193,6 @@ namespace Ombi.Core.Engine
|
|||
return new RequestEngineResult{RequestAdded = true};
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<RequestViewModel>> GetRequests()
|
||||
{
|
||||
var allRequests = await RequestService.GetAllAsync();
|
||||
var viewModel = MapToVm(allRequests);
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<RequestViewModel>> GetRequests(int count, int position)
|
||||
{
|
||||
var allRequests = await RequestService.GetAllAsync(count, position);
|
||||
|
@ -264,34 +237,6 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
|
||||
|
||||
private IEnumerable<RequestViewModel> MapToVm(IEnumerable<RequestModel> model)
|
||||
{
|
||||
return model.Select(movie => new RequestViewModel
|
||||
{
|
||||
ProviderId = movie.ProviderId,
|
||||
Type = movie.Type,
|
||||
Status = movie.Status,
|
||||
ImdbId = movie.ImdbId,
|
||||
Id = movie.Id,
|
||||
PosterPath = movie.PosterPath,
|
||||
ReleaseDate = movie.ReleaseDate,
|
||||
RequestedDate = movie.RequestedDate,
|
||||
Released = DateTime.Now > movie.ReleaseDate,
|
||||
Approved = movie.Available || movie.Approved,
|
||||
Title = movie.Title,
|
||||
Overview = movie.Overview,
|
||||
RequestedUsers = movie.AllUsers.ToArray(),
|
||||
ReleaseYear = movie.ReleaseDate.Year.ToString(),
|
||||
Available = movie.Available,
|
||||
Admin = false,
|
||||
IssueId = movie.IssueId,
|
||||
Denied = movie.Denied,
|
||||
DeniedReason = movie.DeniedReason,
|
||||
//Qualities = qualities.ToArray(),
|
||||
//HasRootFolders = rootFolders.Any(),
|
||||
//RootFolders = rootFolders.ToArray(),
|
||||
//CurrentRootPath = radarr.Enabled ? GetRootPath(movie.RootFolderSelected, radarr).Result : null
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -4,5 +4,7 @@
|
|||
{
|
||||
public bool RequestAdded { get; set; }
|
||||
public string Message { get; set; }
|
||||
public bool IsError => !string.IsNullOrEmpty(ErrorMessage);
|
||||
public string ErrorMessage { get; set; }
|
||||
}
|
||||
}
|
29
Ombi/Ombi.Core/Engine/TvSearchEngine.cs
Normal file
29
Ombi/Ombi.Core/Engine/TvSearchEngine.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using System.Security.Principal;
|
||||
using AutoMapper;
|
||||
using Ombi.Api.TvMaze;
|
||||
using Ombi.Core.Requests.Models;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
public class TvSearchEngine : BaseMediaEngine
|
||||
{
|
||||
|
||||
public TvSearchEngine(IPrincipal identity, IRequestService service, ITvMazeApi tvMaze, IMapper mapper, ISettingsService<PlexSettings> plexSettings, ISettingsService<EmbySettings> embySettings)
|
||||
: base(identity, service)
|
||||
{
|
||||
TvMazeApi = tvMaze;
|
||||
Mapper = mapper;
|
||||
PlexSettings = plexSettings;
|
||||
EmbySettings = embySettings;
|
||||
}
|
||||
|
||||
private ITvMazeApi TvMazeApi { get; }
|
||||
private IMapper Mapper { get; }
|
||||
private ISettingsService<PlexSettings> PlexSettings { get; }
|
||||
private ISettingsService<EmbySettings> EmbySettings { get; }
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api.TvMaze\Ombi.Api.TvMaze.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Store\Ombi.Store.csproj" />
|
||||
<ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.Api.TheMovieDb.csproj" />
|
||||
|
|
22
Ombi/Ombi.Core/Settings/Models/LandingPageSettings.cs
Normal file
22
Ombi/Ombi.Core/Settings/Models/LandingPageSettings.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Ombi.Core.Settings.Models
|
||||
{
|
||||
public class LandingPageSettings : Settings
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
public bool BeforeLogin { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public bool AfterLogin => !BeforeLogin;
|
||||
|
||||
public bool NoticeEnabled => !string.IsNullOrEmpty(NoticeText);
|
||||
public string NoticeText { get; set; }
|
||||
public string NoticeBackgroundColor { get; set; }
|
||||
|
||||
public bool TimeLimit { get; set; }
|
||||
public DateTime StartDateTime { get; set; }
|
||||
public DateTime EndDateTime { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,13 +1,16 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Security.Principal;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ombi.Api.Emby;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Api.Sonarr;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
using Ombi.Api.TvMaze;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.Engine;
|
||||
using Ombi.Core.IdentityResolver;
|
||||
|
@ -38,7 +41,7 @@ namespace Ombi.DependencyInjection
|
|||
|
||||
public static IServiceCollection RegisterEngines(this IServiceCollection services)
|
||||
{
|
||||
services.AddTransient<IMovieEngine, MovieEngine>();
|
||||
services.AddTransient<IMovieEngine, MovieSearchEngine>();
|
||||
services.AddTransient<IRequestEngine, RequestEngine>();
|
||||
return services;
|
||||
}
|
||||
|
@ -49,6 +52,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IPlexApi, PlexApi>();
|
||||
services.AddTransient<IEmbyApi, EmbyApi>();
|
||||
services.AddTransient<ISonarrApi, SonarrApi>();
|
||||
services.AddTransient<ITvMazeApi, TvMazeApi>();
|
||||
return services;
|
||||
}
|
||||
|
||||
|
@ -80,6 +84,7 @@ namespace Ombi.DependencyInjection
|
|||
|
||||
public static IServiceCollection RegisterIdentity(this IServiceCollection services)
|
||||
{
|
||||
|
||||
services.AddTransient<IUserIdentityManager, UserIdentityManager>();
|
||||
services.AddAuthorization(auth =>
|
||||
{
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
<ProjectReference Include="..\Ombi.Api.Emby\Ombi.Api.Emby.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Sonarr\Ombi.Api.Sonarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.TvMaze\Ombi.Api.TvMaze.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Core\Ombi.Core.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Schedule\Ombi.Schedule.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -86,13 +86,15 @@ namespace Ombi.Helpers
|
|||
|
||||
public static string EncryptString(string text, string keyString)
|
||||
{
|
||||
var result = AesEncryption.EncryptWithPassword(text, keyString);
|
||||
var t = Encoding.UTF8.GetBytes(text);
|
||||
var result = Convert.ToBase64String(t);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string DecryptString(string cipherText, string keyString)
|
||||
{
|
||||
var result = AesEncryption.DecryptWithPassword(cipherText, keyString);
|
||||
var textAsBytes = Convert.FromBase64String(cipherText);
|
||||
var result = Encoding.UTF8.GetString(textAsBytes);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Ombi.Api.TheMovieDb
|
|||
}
|
||||
|
||||
private IMapper Mapper { get; }
|
||||
private readonly string ApiToken = StringCipher.DecryptString("bqCrDAQABAC/mJT6JXNdlQ1boOjsHeQgyk7gcNv7tUFtwxoVEnYvqS+UdgfgoyXnBz2F6LJnKX8xGtXbzLsf6pbxDkxna6zvunivxAcAHewo2zTPjoUB5igeMB8d93fx0WO9IhGtq8oGXv++xfaXfTY3aN5NV7JmF6ziAAAAAD1e5VjRPSLOYTyJ3Hbw9bDsE/4FGxYIrvxVkqDMl1vAosOeTi+0kKPFloF6k2ptTw==", "Ombiv3SettingsEncryptionPassword");
|
||||
private readonly string ApiToken = "b8eabaf5608b88d0298aa189dd90bf00";
|
||||
private static readonly string BaseUri ="http://api.themoviedb.org/3/";
|
||||
private Ombi.Api.Api Api { get; }
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Sonarr", "Ombi.Api
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{6F42AB98-9196-44C4-B888-D5E409F415A1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Core.Tests", "Ombi.Core.Tests\Ombi.Core.Tests.csproj", "{2836861C-1185-4E56-A0C2-F4EB541C74AE}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.TvMaze", "Ombi.Api.TvMaze\Ombi.Api.TvMaze.csproj", "{0E8EF835-E4F0-4EE5-A2B6-678DEE973721}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -98,10 +98,10 @@ Global
|
|||
{CFB5E008-D0D0-43C0-AA06-89E49D17F384}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CFB5E008-D0D0-43C0-AA06-89E49D17F384}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CFB5E008-D0D0-43C0-AA06-89E49D17F384}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2836861C-1185-4E56-A0C2-F4EB541C74AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2836861C-1185-4E56-A0C2-F4EB541C74AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2836861C-1185-4E56-A0C2-F4EB541C74AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2836861C-1185-4E56-A0C2-F4EB541C74AE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0E8EF835-E4F0-4EE5-A2B6-678DEE973721}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0E8EF835-E4F0-4EE5-A2B6-678DEE973721}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0E8EF835-E4F0-4EE5-A2B6-678DEE973721}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0E8EF835-E4F0-4EE5-A2B6-678DEE973721}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -114,6 +114,6 @@ Global
|
|||
{2E1A7B91-F29B-42BC-8F1E-1CF2DCC389BA} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
||||
{08FF107D-31E1-470D-AF86-E09B015CEE06} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
||||
{CFB5E008-D0D0-43C0-AA06-89E49D17F384} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
||||
{2836861C-1185-4E56-A0C2-F4EB541C74AE} = {6F42AB98-9196-44C4-B888-D5E409F415A1}
|
||||
{0E8EF835-E4F0-4EE5-A2B6-678DEE973721} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -39,6 +39,7 @@ namespace Ombi.Controllers
|
|||
[HttpGet("search/{searchTerm}")]
|
||||
public async Task<IEnumerable<RequestViewModel>> Search(string searchTerm)
|
||||
{
|
||||
|
||||
return await RequestEngine.SearchRequest(searchTerm);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ namespace Ombi.Controllers
|
|||
public async Task<bool> OmbiSettings([FromBody]OmbiSettings ombi)
|
||||
{
|
||||
return await Save(ombi);
|
||||
|
||||
}
|
||||
|
||||
[HttpGet("plex")]
|
||||
|
@ -56,6 +55,18 @@ namespace Ombi.Controllers
|
|||
return await Save(emby);
|
||||
}
|
||||
|
||||
[HttpGet("landingpage")]
|
||||
public async Task<LandingPageSettings> LandingPageSettings()
|
||||
{
|
||||
return await Get<LandingPageSettings>();
|
||||
}
|
||||
|
||||
[HttpPost("landingpage")]
|
||||
public async Task<bool> LandingPageSettings([FromBody]LandingPageSettings settings)
|
||||
{
|
||||
return await Save(settings);
|
||||
}
|
||||
|
||||
|
||||
private async Task<T> Get<T>()
|
||||
{
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
using AutoMapper;
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Security.Principal;
|
||||
using AutoMapper;
|
||||
using AutoMapper.EquivalencyExpression;
|
||||
using Hangfire;
|
||||
using Hangfire.MemoryStorage;
|
||||
using Hangfire.RecurringJobExtensions;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
@ -42,6 +46,10 @@ namespace Ombi
|
|||
});
|
||||
services.RegisterDependencies(); // Ioc and EF
|
||||
|
||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
//services.AddTransient<IPrincipal>(new InjectionFactory(u => HttpContext.Current.User));
|
||||
services.AddScoped<IPrincipal>(sp => sp.GetService<IHttpContextAccessor>().HttpContext.User);
|
||||
|
||||
|
||||
services.AddHangfire(x =>
|
||||
{
|
||||
|
|
|
@ -11,6 +11,7 @@ import { HttpModule } from '@angular/http';
|
|||
import { InfiniteScrollModule } from 'ngx-infinite-scroll'
|
||||
|
||||
import { SearchComponent } from './search/search.component';
|
||||
import { MovieSearchComponent } from './search/moviesearch.component';
|
||||
import { RequestComponent } from './requests/request.component';
|
||||
import { LoginComponent } from './login/login.component';
|
||||
import { PageNotFoundComponent } from './errors/not-found.component';
|
||||
|
@ -64,7 +65,8 @@ const routes: Routes = [
|
|||
PageNotFoundComponent,
|
||||
SearchComponent,
|
||||
RequestComponent,
|
||||
LoginComponent
|
||||
LoginComponent,
|
||||
MovieSearchComponent
|
||||
],
|
||||
providers: [
|
||||
SearchService,
|
||||
|
|
|
@ -36,4 +36,15 @@ export interface ISonarrSettings extends IExternalSettings {
|
|||
seasonFolders: boolean,
|
||||
rootPath: string,
|
||||
fullRootPath:string
|
||||
}
|
||||
|
||||
export interface ILandingPageSettings extends ISettings {
|
||||
enabled: boolean,
|
||||
beforeLogin: boolean,
|
||||
afterLogin: boolean,
|
||||
noticeText: string,
|
||||
noticeBackgroundColor: string,
|
||||
timeLimit: boolean,
|
||||
startDateTime: Date,
|
||||
endDateTime:Date,
|
||||
}
|
154
Ombi/Ombi/wwwroot/app/search/moviesearch.component.html
Normal file
154
Ombi/Ombi/wwwroot/app/search/moviesearch.component.html
Normal file
|
@ -0,0 +1,154 @@
|
|||
<!-- Movie tab -->
|
||||
<div role="tabpanel" class="tab-pane active" id="MoviesTab">
|
||||
<div class="input-group">
|
||||
<input id="movieSearchContent" type="text" class="form-control form-control-custom form-control-search form-control-withbuttons" (keyup)="search($event)">
|
||||
<div class="input-group-addon">
|
||||
<div class="btn-group">
|
||||
<a href="#" class="btn btn-sm btn-primary-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
||||
Suggestions
|
||||
<i class="fa fa-chevron-down"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a (click)="popularMovies()">Popular Movies</a></li>
|
||||
<li><a (click)="upcomingMovies()">Upcoming Movies</a></li>
|
||||
<li><a (click)="topRatedMovies()">Top Rated Movies</a></li>
|
||||
<li><a (click)="nowPlayingMovies()">Now Playing Movies</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<i id="movieSearchButton" class="fa fa-search"></i>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<!-- Movie content -->
|
||||
<div id="movieList">
|
||||
<div *ngFor="let result of movieResults">
|
||||
<div class="row">
|
||||
<div id="{{id}}imgDiv" class="col-sm-2">
|
||||
|
||||
|
||||
<img *ngIf="result.posterPath" class="img-responsive" src="https://image.tmdb.org/t/p/w150/{{result.posterPath}}" alt="poster">
|
||||
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
<div>
|
||||
<a href="https://www.themoviedb.org/movie/{{result.id}}/" target="_blank">
|
||||
<h4>{{result.title}} ({{result.releaseDate | date: 'yyyy'}})</h4>
|
||||
</a>
|
||||
|
||||
|
||||
<span *ngIf="result.firstAired" class="label label-info" target="_blank">Air Date: {{result.firstAired | date: 'dd/MM/yyyy'}}</span>
|
||||
|
||||
|
||||
<span *ngIf="result.releaseDate" class="label label-info" target="_blank">Release Date: {{result.releaseDate | date: 'dd/MM/yyyy'}}</span>
|
||||
|
||||
<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>
|
||||
<span *ngIf="!result.available" class="label label-warning">Pending Approval</span>
|
||||
</template>
|
||||
|
||||
<template #notRequested>
|
||||
<span *ngIf="!result.available" class="label label-danger">Not Yet Requested</span>
|
||||
</template>
|
||||
|
||||
|
||||
<span id="{{id}}netflixTab"></span>
|
||||
|
||||
<a *ngIf="result.homepage" href="{{result.homepage}}" target="_blank"><span class="label label-info">HomePage</span></a>
|
||||
|
||||
<a *ngIf="result.trailer" href="{{result.trailer}}" target="_blank"><span class="label label-info">Trailer</span></a>
|
||||
|
||||
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
<p style="font-size:0.9rem !important">{{result.overview}}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-sm-2">
|
||||
<input name="{{type}}Id" type="text" value="{{result.id}}" hidden="hidden" />
|
||||
|
||||
<div *ngIf="result.available">
|
||||
<button style="text-align: right" class="btn btn-success-outline disabled" disabled><i class="fa fa-check"></i> Available</button>
|
||||
|
||||
<div *ngIf="result.url">
|
||||
<br />
|
||||
<br />
|
||||
<a style="text-align: right" class="btn btn-sm btn-primary-outline" href="{{result.url}}" target="_blank"><i class="fa fa-eye"></i> View In Plex</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="result.requested; then requestedBtn else notRequestedBtn"></div>
|
||||
<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>
|
||||
<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>
|
||||
|
||||
<!--{{#if_eq type "tv"}}
|
||||
{{#if_eq tvFullyAvailable true}}
|
||||
@*//TODO Not used yet*@
|
||||
<button style="text-align: right" class="btn btn-success-outline disabled" disabled><i class="fa fa-check"></i> @UI.Search_Available</button><br />
|
||||
{{else}}
|
||||
{{#if_eq enableTvRequestsForOnlySeries true}}
|
||||
<button id="{{id}}" style="text-align: right" class="btn {{#if available}}btn-success-outline{{else}}btn-primary-outline dropdownTv{{/if}} btn-primary-outline" season-select="0" type="button" {{#if available}} disabled{{/if}}><i class="fa fa-plus"></i> {{#if available}}@UI.Search_Available{{else}}@UI.Search_Request{{/if}}</button>
|
||||
{{else}}
|
||||
<div class="dropdown">
|
||||
<button id="{{id}}" class="btn {{#if available}}btn-success-outline{{else}}btn-primary-outline{{/if}} dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
<i class="fa fa-plus"></i> {{#if available}}@UI.Search_Available{{else}}@UI.Search_Request {{/if}}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
||||
<li><a id="{{id}}" season-select="0" class="dropdownTv " href="#">@UI.Search_AllSeasons</a></li>
|
||||
{{#if_eq disableTvRequestsBySeason false}}
|
||||
<li><a id="{{id}}" season-select="1" class="dropdownTv" href="#">@UI.Search_FirstSeason</a></li>
|
||||
<li><a id="{{id}}" season-select="2" class="dropdownTv" href="#">@UI.Search_LatestSeason</a></li>
|
||||
<li><a id="SeasonSelect" data-identifier="{{id}}" data-toggle="modal" data-target="#seasonsModal" href="#">@UI.Search_SelectSeason...</a></li>
|
||||
{{/if_eq}}
|
||||
{{#if_eq disableTvRequestsByEpisode false}}
|
||||
<li><a id="EpisodeSelect" data-identifier="{{id}}" data-toggle="modal" data-target="#episodesModal" href="#">@UI.Search_SelectEpisode...</a></li>
|
||||
{{/if_eq}}
|
||||
</ul>
|
||||
</div>
|
||||
{{/if_eq}}
|
||||
{{#if available}}
|
||||
{{#if url}}
|
||||
<br />
|
||||
<a style="text-align: right" class="btn btn-sm btn-primary-outline" href="{{url}}" target="_blank"><i class="fa fa-eye"></i> @UI.Search_ViewInPlex</a>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if_eq}}
|
||||
{{/if_eq}}-->
|
||||
|
||||
|
||||
<br />
|
||||
<div *ngIf="result.available">
|
||||
<input name="providerId" type="text" value="{{id}}" hidden="hidden" />
|
||||
<input name="type" type="text" value="{{type}}" hidden="hidden" />
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-sm btn-danger-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
<i class="fa fa-exclamation"></i> @UI.Search_ReportIssue
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
||||
<li><a issue-select="0" class="dropdownIssue" href="#">WrongAudio</a></li>
|
||||
<li><a issue-select="1" class="dropdownIssue" href="#">NoSubs</a></li>
|
||||
<li><a issue-select="2" class="dropdownIssue" href="#">WrongContent</a></li>
|
||||
<li><a issue-select="3" class="dropdownIssue" href="#">Playback</a></li>
|
||||
<li><a issue-select="4" class="dropdownIssue" href="#" data-toggle="modal" data-target="#issuesModal">Other</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
110
Ombi/Ombi/wwwroot/app/search/moviesearch.component.ts
Normal file
110
Ombi/Ombi/wwwroot/app/search/moviesearch.component.ts
Normal file
|
@ -0,0 +1,110 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
import 'rxjs/add/operator/debounceTime';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
import { SearchService } from '../services/search.service';
|
||||
import { RequestService } from '../services/request.service';
|
||||
import { NotificationService } from '../services/notification.service';
|
||||
|
||||
import { ISearchMovieResult } from '../interfaces/ISearchMovieResult';
|
||||
import { IRequestEngineResult } from '../interfaces/IRequestEngineResult';
|
||||
|
||||
@Component({
|
||||
selector: 'movie-search',
|
||||
moduleId: module.id,
|
||||
templateUrl: './moviesearch.component.html',
|
||||
})
|
||||
export class MovieSearchComponent implements OnInit {
|
||||
|
||||
searchText: string;
|
||||
searchChanged: Subject<string> = new Subject<string>();
|
||||
movieResults: ISearchMovieResult[];
|
||||
result: IRequestEngineResult;
|
||||
|
||||
constructor(private searchService: SearchService, private requestService: RequestService, private notificationService: NotificationService) {
|
||||
this.searchChanged
|
||||
.debounceTime(600) // Wait Xms afterthe last event before emitting last event
|
||||
.distinctUntilChanged() // only emit if value is different from previous value
|
||||
.subscribe(x => {
|
||||
this.searchText = x as string;
|
||||
if (this.searchText === "") {
|
||||
this.clearResults();
|
||||
return;
|
||||
}
|
||||
this.searchService.searchMovie(this.searchText).subscribe(x => {
|
||||
this.movieResults = x;
|
||||
|
||||
// Now let's load some exta info including IMDBId
|
||||
// This way the search is fast at displaying results.
|
||||
this.getExtaInfo();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.searchText = "";
|
||||
this.movieResults = [];
|
||||
this.result = {
|
||||
message: "",
|
||||
requestAdded: false
|
||||
}
|
||||
}
|
||||
|
||||
search(text: any) {
|
||||
this.searchChanged.next(text.target.value);
|
||||
}
|
||||
|
||||
request(searchResult: ISearchMovieResult) {
|
||||
searchResult.requested = true;
|
||||
this.requestService.requestMovie(searchResult).subscribe(x => {
|
||||
this.result = x;
|
||||
|
||||
if (this.result.requestAdded) {
|
||||
this.notificationService.success("Request Added",
|
||||
`Request for ${searchResult.title} has been added successfully`);
|
||||
} else {
|
||||
this.notificationService.warning("Request Added", this.result.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
popularMovies() {
|
||||
this.clearResults();
|
||||
this.searchService.popularMovies().subscribe(x => {
|
||||
this.movieResults = x;
|
||||
this.getExtaInfo();
|
||||
});
|
||||
}
|
||||
nowPlayingMovies() {
|
||||
this.clearResults();
|
||||
this.searchService.nowPlayingMovies().subscribe(x => {
|
||||
this.movieResults = x;
|
||||
this.getExtaInfo();
|
||||
});
|
||||
}
|
||||
topRatedMovies() {
|
||||
this.clearResults();
|
||||
this.searchService.topRatedMovies().subscribe(x => {
|
||||
this.movieResults = x;
|
||||
this.getExtaInfo();
|
||||
});
|
||||
}
|
||||
upcomingMovies() {
|
||||
this.clearResults();
|
||||
this.searchService.upcomignMovies().subscribe(x => {
|
||||
this.movieResults = x;
|
||||
this.getExtaInfo();
|
||||
});
|
||||
}
|
||||
|
||||
private getExtaInfo() {
|
||||
this.searchService.extraInfo(this.movieResults).subscribe(m => this.movieResults = m);
|
||||
}
|
||||
|
||||
private clearResults() {
|
||||
this.movieResults = [];
|
||||
}
|
||||
|
||||
}
|
|
@ -30,32 +30,7 @@
|
|||
<!-- Tab panes -->
|
||||
<div class="tab-content">
|
||||
|
||||
<!-- Movie tab -->
|
||||
<div role="tabpanel" class="tab-pane active" id="MoviesTab">
|
||||
<div class="input-group">
|
||||
<input id="movieSearchContent" type="text" class="form-control form-control-custom form-control-search form-control-withbuttons" (keyup)="search($event)">
|
||||
<div class="input-group-addon">
|
||||
<div class="btn-group">
|
||||
<a href="#" class="btn btn-sm btn-primary-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
||||
Suggestions
|
||||
<i class="fa fa-chevron-down"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a (click)="popularMovies()">Popular Movies</a></li>
|
||||
<li><a (click)="upcomingMovies()">Upcoming Movies</a></li>
|
||||
<li><a (click)="topRatedMovies()">Top Rated Movies</a></li>
|
||||
<li><a (click)="nowPlayingMovies()">Now Playing Movies</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<i id="movieSearchButton" class="fa fa-search"></i>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<!-- Movie content -->
|
||||
<div id="movieList">
|
||||
</div>
|
||||
</div>
|
||||
<movie-search></movie-search>
|
||||
|
||||
<!-- Actors tab -->
|
||||
<div role="tabpanel" class="tab-pane" id="ActorsTab">
|
||||
|
@ -87,7 +62,7 @@
|
|||
<i class="fa fa-chevron-down"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a id="popularShows" >Popular Shows</a></li>
|
||||
<li><a id="popularShows">Popular Shows</a></li>
|
||||
<li><a id="trendingShows" href="#">Trending Shows</a></li>
|
||||
<li><a id="mostWatchedShows" href="#">Most Watched Shows</a></li>
|
||||
<li><a id="anticipatedShows" href="#">Most Anticipated Shows</a></li>
|
||||
|
@ -101,146 +76,6 @@
|
|||
<div id="tvList">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Music tab -->
|
||||
<!-- <div role="tabpanel" class="tab-pane" id="MusicTab">
|
||||
<div class="input-group">
|
||||
<input id="musicSearchContent" type="text" class="form-control form-control-custom form-control-search">
|
||||
<div class="input-group-addon">
|
||||
<i id="musicSearchButton" class="fa fa-search"></i>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div id="musicList">
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<div *ngFor="let result of movieResults">
|
||||
<div class="row">
|
||||
<div id="{{id}}imgDiv" class="col-sm-2">
|
||||
</div>
|
||||
|
||||
|
||||
<img *ngIf="result.posterPath" class="img-responsive" src="https://image.tmdb.org/t/p/w150/{{result.posterPath}}" alt="poster">
|
||||
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
<div>
|
||||
<a href="https://www.themoviedb.org/movie/{{result.id}}/" target="_blank">
|
||||
<h4>{{result.title}} ({{result.releaseDate | date: 'yyyy'}})</h4>
|
||||
</a>
|
||||
|
||||
|
||||
<span *ngIf="result.firstAired" class="label label-info" target="_blank">Air Date: {{result.firstAired | date: 'dd/MM/yyyy'}}</span>
|
||||
|
||||
|
||||
<span *ngIf="result.releaseDate" class="label label-info" target="_blank">Release Date: {{result.releaseDate | date: 'dd/MM/yyyy'}}</span>
|
||||
|
||||
<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>
|
||||
<span *ngIf="!result.available" class="label label-warning">Pending Approval</span>
|
||||
</template>
|
||||
|
||||
<template #notRequested>
|
||||
<span *ngIf="!result.available" class="label label-danger">Not Yet Requested</span>
|
||||
</template>
|
||||
|
||||
|
||||
<span id="{{id}}netflixTab"></span>
|
||||
|
||||
<a *ngIf="result.homepage" href="{{result.homepage}}" target="_blank"><span class="label label-info">HomePage</span></a>
|
||||
|
||||
<a *ngIf="result.trailer" href="{{result.trailer}}" target="_blank"><span class="label label-info">Trailer</span></a>
|
||||
|
||||
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
<p style="font-size:0.9rem !important">{{result.overview}}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-sm-2">
|
||||
<input name="{{type}}Id" type="text" value="{{result.id}}" hidden="hidden" />
|
||||
|
||||
<div *ngIf="result.available">
|
||||
<button style="text-align: right" class="btn btn-success-outline disabled" disabled><i class="fa fa-check"></i> Available</button>
|
||||
|
||||
<div *ngIf="result.url">
|
||||
<br />
|
||||
<br />
|
||||
<a style="text-align: right" class="btn btn-sm btn-primary-outline" href="{{result.url}}" target="_blank"><i class="fa fa-eye"></i> View In Plex</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="result.requested; then requestedBtn else notRequestedBtn"></div>
|
||||
<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>
|
||||
<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>
|
||||
|
||||
<!--{{#if_eq type "tv"}}
|
||||
{{#if_eq tvFullyAvailable true}}
|
||||
@*//TODO Not used yet*@
|
||||
<button style="text-align: right" class="btn btn-success-outline disabled" disabled><i class="fa fa-check"></i> @UI.Search_Available</button><br />
|
||||
{{else}}
|
||||
{{#if_eq enableTvRequestsForOnlySeries true}}
|
||||
<button id="{{id}}" style="text-align: right" class="btn {{#if available}}btn-success-outline{{else}}btn-primary-outline dropdownTv{{/if}} btn-primary-outline" season-select="0" type="button" {{#if available}} disabled{{/if}}><i class="fa fa-plus"></i> {{#if available}}@UI.Search_Available{{else}}@UI.Search_Request{{/if}}</button>
|
||||
{{else}}
|
||||
<div class="dropdown">
|
||||
<button id="{{id}}" class="btn {{#if available}}btn-success-outline{{else}}btn-primary-outline{{/if}} dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
<i class="fa fa-plus"></i> {{#if available}}@UI.Search_Available{{else}}@UI.Search_Request {{/if}}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
||||
<li><a id="{{id}}" season-select="0" class="dropdownTv " href="#">@UI.Search_AllSeasons</a></li>
|
||||
{{#if_eq disableTvRequestsBySeason false}}
|
||||
<li><a id="{{id}}" season-select="1" class="dropdownTv" href="#">@UI.Search_FirstSeason</a></li>
|
||||
<li><a id="{{id}}" season-select="2" class="dropdownTv" href="#">@UI.Search_LatestSeason</a></li>
|
||||
<li><a id="SeasonSelect" data-identifier="{{id}}" data-toggle="modal" data-target="#seasonsModal" href="#">@UI.Search_SelectSeason...</a></li>
|
||||
{{/if_eq}}
|
||||
{{#if_eq disableTvRequestsByEpisode false}}
|
||||
<li><a id="EpisodeSelect" data-identifier="{{id}}" data-toggle="modal" data-target="#episodesModal" href="#">@UI.Search_SelectEpisode...</a></li>
|
||||
{{/if_eq}}
|
||||
</ul>
|
||||
</div>
|
||||
{{/if_eq}}
|
||||
{{#if available}}
|
||||
{{#if url}}
|
||||
<br />
|
||||
<a style="text-align: right" class="btn btn-sm btn-primary-outline" href="{{url}}" target="_blank"><i class="fa fa-eye"></i> @UI.Search_ViewInPlex</a>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if_eq}}
|
||||
{{/if_eq}}-->
|
||||
|
||||
|
||||
<br />
|
||||
<div *ngIf="result.available">
|
||||
<input name="providerId" type="text" value="{{id}}" hidden="hidden" />
|
||||
<input name="type" type="text" value="{{type}}" hidden="hidden" />
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-sm btn-danger-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
<i class="fa fa-exclamation"></i> @UI.Search_ReportIssue
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
||||
<li><a issue-select="0" class="dropdownIssue" href="#">@UI.Issues_WrongAudio</a></li>
|
||||
<li><a issue-select="1" class="dropdownIssue" href="#">@UI.Issues_NoSubs</a></li>
|
||||
<li><a issue-select="2" class="dropdownIssue" href="#">@UI.Issues_WrongContent</a></li>
|
||||
<li><a issue-select="3" class="dropdownIssue" href="#">@UI.Issues_Playback</a></li>
|
||||
<li><a issue-select="4" class="dropdownIssue" href="#" data-toggle="modal" data-target="#issuesModal">@UI.Issues_Other</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<hr />
|
||||
</div>
|
||||
|
|
|
@ -3,7 +3,7 @@ import { AuthHttp } from 'angular2-jwt';
|
|||
import { Observable } from 'rxjs/Rx';
|
||||
|
||||
import { ServiceAuthHelpers } from './service.helpers';
|
||||
import { IOmbiSettings, IEmbySettings, IPlexSettings, ISonarrSettings } from '../interfaces/ISettings';
|
||||
import { IOmbiSettings, IEmbySettings, IPlexSettings, ISonarrSettings,ILandingPageSettings } from '../interfaces/ISettings';
|
||||
|
||||
@Injectable()
|
||||
export class SettingsService extends ServiceAuthHelpers {
|
||||
|
@ -42,5 +42,12 @@ export class SettingsService extends ServiceAuthHelpers {
|
|||
saveSonarr(settings: ISonarrSettings): Observable<boolean> {
|
||||
return this.http.post(`${this.url}/Sonarr`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData);
|
||||
}
|
||||
getLandingPage(): Observable<ILandingPageSettings> {
|
||||
return this.http.get(`${this.url}/LandingPage`).map(this.extractData);
|
||||
}
|
||||
|
||||
saveLandingPage(settings: ILandingPageSettings): Observable<boolean> {
|
||||
return this.http.post(`${this.url}/LandingPage`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<div class="col-sm-8 col-sm-push-1">
|
||||
<fieldset>
|
||||
<legend>Landing Page Configuration</legend>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" id="enable" name="enable" [(ngModel)]="settings.enable" ng-checked="settings.enable">
|
||||
<label for="enable">Enable</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" id="BeforeLogin" name="BeforeLogin" [(ngModel)]="settings.beforeLogin" ng-checked="settings.beforeLogin">
|
||||
<label for="BeforeLogin">Show before the login</label>
|
||||
</div>
|
||||
<small>If enabled then this will show the landing page before the login page, if this is disabled the user will log in first and then see the landing page.</small>
|
||||
</div>
|
||||
|
||||
<p class="form-group">Notice Message</p>
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<textarea rows="4" type="text" class="form-control-custom form-control " id="NoticeMessage" name="NoticeMessage" placeholder="e.g. The server will be down for maintaince (HTML is allowed)" [(ngModel)]="settings.noticeText" >{{noticeText}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="form-group">Notice Preview:</p>
|
||||
<div class="form-group">
|
||||
<div [innerHTML]="settings.noticeText"></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button (click)="save()" type="submit" id="save" class="btn btn-primary-outline">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
|
@ -0,0 +1,31 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { ILandingPageSettings } from '../../interfaces/ISettings'
|
||||
import { SettingsService } from '../../services/settings.service';
|
||||
import { NotificationService } from "../../services/notification.service";
|
||||
|
||||
@Component({
|
||||
selector: 'ombi',
|
||||
moduleId: module.id,
|
||||
templateUrl: './landingpage.component.html',
|
||||
})
|
||||
export class LandingPageComponent implements OnInit {
|
||||
|
||||
constructor(private settingsService: SettingsService, private notificationService: NotificationService) { }
|
||||
|
||||
settings: ILandingPageSettings;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.settingsService.getLandingPage().subscribe(x => this.settings = x);
|
||||
}
|
||||
|
||||
save() {
|
||||
this.settingsService.saveLandingPage(this.settings).subscribe(x => {
|
||||
if (x) {
|
||||
this.notificationService.success("Settings Saved", "Successfully saved the Landing Page settings");
|
||||
} else {
|
||||
this.notificationService.success("Settings Saved", "There was an error when saving the Landing Page settings");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ import { SonarrService } from '../services/applications/sonarr.service';
|
|||
import { OmbiComponent } from './ombi/ombi.component'
|
||||
import { PlexComponent } from './plex/plex.component'
|
||||
import { EmbyComponent } from './emby/emby.component'
|
||||
import { LandingPageComponent } from './landingpage/landingpage.component'
|
||||
|
||||
import { SettingsMenuComponent } from './settingsmenu.component';
|
||||
|
||||
|
@ -20,6 +21,7 @@ const routes: Routes = [
|
|||
{ path: 'Settings/Ombi', component: OmbiComponent, canActivate: [AuthGuard] },
|
||||
{ path: 'Settings/Plex', component: PlexComponent, canActivate: [AuthGuard] },
|
||||
{ path: 'Settings/Emby', component: EmbyComponent, canActivate: [AuthGuard] },
|
||||
{ path: 'Settings/LandingPage', component: LandingPageComponent, canActivate: [AuthGuard] },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@ -37,6 +39,7 @@ const routes: Routes = [
|
|||
OmbiComponent,
|
||||
PlexComponent,
|
||||
EmbyComponent,
|
||||
LandingPageComponent,
|
||||
],
|
||||
exports: [
|
||||
RouterModule
|
||||
|
|
|
@ -32,9 +32,11 @@ export class CreateAdminComponent {
|
|||
this.settings.getOmbi().subscribe(ombi => {
|
||||
ombi.wizard = true;
|
||||
|
||||
this.settings.saveOmbi(ombi).subscribe();
|
||||
this.settings.saveOmbi(ombi).subscribe(x => {
|
||||
|
||||
this.router.navigate(['search']);
|
||||
});
|
||||
|
||||
this.router.navigate(['search']);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue