Merge pull request #5 from tidusjar/develop

Bring develop up to date with source
This commit is contained in:
Anojh Thayaparan 2018-04-18 12:02:17 -07:00 committed by GitHub
commit 30b9a7ed0b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 472 additions and 158 deletions

View file

@ -4,6 +4,30 @@
### **New Features**
- Added a new Job. Plex Recently Added, this is a slimmed down version of the Plex Sync job, this will just scan the recently added list and not the whole library. I'd reccomend running this very regulary and the full scan not as regular. [Jamie]
### **Fixes**
- Add web-app-capable for IOS and Android. [Thomas]
- Fixed the bug where the newsletter CRON was not appearing on the job settings page. [Jamie]
- Add base url as a startup argument #2153. [Jamie Rees]
- Fixed a bug with the RefreshMetadata where we would never get TheMovieDBId's if it was missing it. [Jamie]
## v3.0.3173 (2018-04-12)
### **Fixes**
- Removed some early disposition that seemed to be causing errors in the API. [Jamie]
## v3.0.3164 (2018-04-10)
### **New Features**
- Added the ability to send newsletter out to users that are not in Ombi. [Jamie]
- Added the ability to turn off TV or Movies from the newsletter. [Jamie]
@ -56,6 +80,12 @@
- Fixed the issue where movies were not appearing in the newsletter for users with Emby #2111. [Jamie]
- The fact that this button has another style really bothers me. [Louis Laureys]
- Fix discord current user count. [Avi]
- Fix broken images and new discord invite. [Avi]
## v3.0.3111 (2018-03-27)

View file

@ -19,5 +19,6 @@ namespace Ombi.Api.Plex
Task<PlexContainer> GetAllEpisodes(string authToken, string host, string section, int start, int retCount);
Task<PlexFriends> GetUsers(string authToken);
Task<PlexAccount> GetAccount(string authToken);
Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId);
}
}

View file

@ -23,8 +23,8 @@ namespace Ombi.Api.Plex.Models
public int leafCount { get; set; }
public int viewedLeafCount { get; set; }
public int childCount { get; set; }
public long addedAt { get; set; }
public int updatedAt { get; set; }
//public long addedAt { get; set; }
//public int updatedAt { get; set; }
public Genre[] Genre { get; set; }
//public Role[] Role { get; set; }
public string primaryExtraKey { get; set; }

View file

@ -147,6 +147,15 @@ namespace Ombi.Api.Plex
return await Api.Request<PlexFriends>(request);
}
public async Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId)
{
var request = new Request($"library/sections/{sectionId}/recentlyAdded", uri, HttpMethod.Get);
AddHeaders(request, authToken);
AddLimitHeaders(request, 0, 50);
return await Api.Request<PlexMetadata>(request);
}
/// <summary>
/// Adds the required headers and also the authorization header
/// </summary>

View file

@ -1,6 +1,7 @@
using System;
using AutoMapper;
using Ombi.Api.TvMaze;
using Ombi.Api.TheMovieDb;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search;
using Ombi.Helpers;
@ -26,11 +27,12 @@ namespace Ombi.Core.Engine
{
public class TvRequestEngine : BaseMediaEngine, ITvRequestEngine
{
public TvRequestEngine(ITvMazeApi tvApi, IRequestServiceMain requestService, IPrincipal user,
public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, IPrincipal user,
INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager,
ITvSender sender, IAuditRepository audit, IRepository<RequestLog> rl, ISettingsService<OmbiSettings> settings, ICacheService cache) : base(user, requestService, rule, manager, cache, settings)
{
TvApi = tvApi;
MovieDbApi = movApi;
NotificationHelper = helper;
TvSender = sender;
Audit = audit;
@ -39,6 +41,7 @@ namespace Ombi.Core.Engine
private INotificationHelper NotificationHelper { get; }
private ITvMazeApi TvApi { get; }
private IMovieDbApi MovieDbApi { get; }
private ITvSender TvSender { get; }
private IAuditRepository Audit { get; }
private readonly IRepository<RequestLog> _requestLog;
@ -47,7 +50,7 @@ namespace Ombi.Core.Engine
{
var user = await GetUser();
var tvBuilder = new TvShowRequestBuilder(TvApi);
var tvBuilder = new TvShowRequestBuilder(TvApi, MovieDbApi);
(await tvBuilder
.GetShowInfo(tv.TvDbId))
.CreateTvList(tv)

View file

@ -3,7 +3,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Ombi.Api.TvMaze;
using Ombi.Api.TheMovieDb;
using Ombi.Api.TvMaze.Models;
using Ombi.Api.TheMovieDb.Models;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search;
using Ombi.Helpers;
@ -16,12 +18,14 @@ namespace Ombi.Core.Helpers
public class TvShowRequestBuilder
{
public TvShowRequestBuilder(ITvMazeApi tvApi)
public TvShowRequestBuilder(ITvMazeApi tvApi, IMovieDbApi movApi)
{
TvApi = tvApi;
MovieDbApi = movApi;
}
private ITvMazeApi TvApi { get; }
private IMovieDbApi MovieDbApi { get; }
public ChildRequests ChildRequest { get; set; }
public List<SeasonsViewModel> TvRequests { get; protected set; }
@ -29,10 +33,20 @@ namespace Ombi.Core.Helpers
public DateTime FirstAir { get; protected set; }
public TvRequests NewRequest { get; protected set; }
protected TvMazeShow ShowInfo { get; set; }
protected List<TvSearchResult> Results { get; set; }
public async Task<TvShowRequestBuilder> GetShowInfo(int id)
{
ShowInfo = await TvApi.ShowLookupByTheTvDbId(id);
Results = await MovieDbApi.SearchTv(ShowInfo.name);
foreach (TvSearchResult result in Results) {
if (result.Name == ShowInfo.name)
{
var showIds = await MovieDbApi.GetTvExternals(result.Id);
ShowInfo.externals.imdb = showIds.imdb_id;
break;
}
}
DateTime.TryParse(ShowInfo.premiered, out var dt);

View file

@ -2,6 +2,8 @@
{
public static class OmbiRoles
{
// DONT FORGET TO ADD TO IDENTITYCONTROLLER.CREATEROLES AND THE UI!
public const string Admin = nameof(Admin);
public const string AutoApproveMovie = nameof(AutoApproveMovie);
public const string AutoApproveTv = nameof(AutoApproveTv);

View file

@ -24,6 +24,19 @@ namespace Ombi.Mapping.Profiles
.ForMember(dest => dest.VoteAverage, opts => opts.MapFrom(src => src.vote_average))
.ForMember(dest => dest.VoteCount, opts => opts.MapFrom(src => src.vote_count));
CreateMap<SearchResult, TvSearchResult>()
.ForMember(dest => dest.BackdropPath, opts => opts.MapFrom(src => src.backdrop_path))
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.id))
.ForMember(dest => dest.OriginalLanguage, opts => opts.MapFrom(src => src.original_language))
.ForMember(dest => dest.OriginalName, opts => opts.MapFrom(src => src.original_name))
.ForMember(dest => dest.Overview, opts => opts.MapFrom(src => src.overview))
.ForMember(dest => dest.Popularity, opts => opts.MapFrom(src => src.popularity))
.ForMember(dest => dest.PosterPath, opts => opts.MapFrom(src => src.poster_path))
.ForMember(dest => dest.ReleaseDate, opts => opts.MapFrom(src => src.first_air_date))
.ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.name))
.ForMember(dest => dest.VoteAverage, opts => opts.MapFrom(src => src.vote_average))
.ForMember(dest => dest.VoteCount, opts => opts.MapFrom(src => src.vote_count));
CreateMap<MovieResponse, MovieResponseDto>()
.ForMember(dest => dest.Adult, opts => opts.MapFrom(src => src.adult))
.ForMember(dest => dest.BackdropPath, opts => opts.MapFrom(src => src.backdrop_path))

View file

@ -55,7 +55,8 @@ namespace Ombi.Schedule
RecurringJob.AddOrUpdate(() => _embyContentSync.Start(), JobSettingsHelper.EmbyContent(s));
RecurringJob.AddOrUpdate(() => _sonarrSync.Start(), JobSettingsHelper.Sonarr(s));
RecurringJob.AddOrUpdate(() => _radarrSync.CacheContent(), JobSettingsHelper.Radarr(s));
RecurringJob.AddOrUpdate(() => _plexContentSync.CacheContent(), JobSettingsHelper.PlexContent(s));
RecurringJob.AddOrUpdate(() => _plexContentSync.CacheContent(false), JobSettingsHelper.PlexContent(s));
RecurringJob.AddOrUpdate(() => _plexContentSync.CacheContent(true), JobSettingsHelper.PlexRecentlyAdded(s));
RecurringJob.AddOrUpdate(() => _cpCache.Start(), JobSettingsHelper.CouchPotato(s));
RecurringJob.AddOrUpdate(() => _srSync.Start(), JobSettingsHelper.SickRageSync(s));
RecurringJob.AddOrUpdate(() => _refreshMetadata.Start(), JobSettingsHelper.RefreshMetadata(s));

View file

@ -80,7 +80,7 @@ namespace Ombi.Schedule.Jobs.Ombi
if (!hasTheMovieDb)
{
var id = await GetTheMovieDbId(hasTvDbId, hasImdb, show.TvDbId, show.ImdbId, show.Title);
var id = await GetTheMovieDbId(hasTvDbId, hasImdb, show.TvDbId, show.ImdbId, show.Title, false);
show.TheMovieDbId = id;
}
@ -120,7 +120,7 @@ namespace Ombi.Schedule.Jobs.Ombi
if (!hasTheMovieDb)
{
var id = await GetTheMovieDbId(hasTvDbId, hasImdb, show.TvDbId, show.ImdbId, show.Title);
var id = await GetTheMovieDbId(hasTvDbId, hasImdb, show.TvDbId, show.ImdbId, show.Title, false);
show.TheMovieDbId = id;
}
@ -166,7 +166,7 @@ namespace Ombi.Schedule.Jobs.Ombi
}
if (!hasTheMovieDb)
{
var id = await GetTheMovieDbId(false, hasImdb, string.Empty, movie.ImdbId, movie.Title);
var id = await GetTheMovieDbId(false, hasImdb, string.Empty, movie.ImdbId, movie.Title, true);
movie.TheMovieDbId = id;
_plexRepo.UpdateWithoutSave(movie);
}
@ -200,7 +200,7 @@ namespace Ombi.Schedule.Jobs.Ombi
}
if (!hasTheMovieDb)
{
var id = await GetTheMovieDbId(false, hasImdb, string.Empty, movie.ImdbId, movie.Title);
var id = await GetTheMovieDbId(false, hasImdb, string.Empty, movie.ImdbId, movie.Title, true);
movie.TheMovieDbId = id;
_embyRepo.UpdateWithoutSave(movie);
}
@ -215,7 +215,7 @@ namespace Ombi.Schedule.Jobs.Ombi
await _embyRepo.SaveChangesAsync();
}
private async Task<string> GetTheMovieDbId(bool hasTvDbId, bool hasImdb, string tvdbID, string imdbId, string title)
private async Task<string> GetTheMovieDbId(bool hasTvDbId, bool hasImdb, string tvdbID, string imdbId, string title, bool movie)
{
_log.LogInformation("The Media item {0} does not have a TheMovieDbId, searching for TheMovieDbId", title);
FindResult result = null;
@ -230,13 +230,29 @@ namespace Ombi.Schedule.Jobs.Ombi
if (hasImdb && !hasResult)
{
result = await _movieApi.Find(imdbId, ExternalSource.imdb_id);
hasResult = result?.tv_results?.Length > 0;
if (movie)
{
hasResult = result?.movie_results?.Length > 0;
}
else
{
hasResult = result?.tv_results?.Length > 0;
}
_log.LogInformation("Setting Show {0} because we have ImdbId, result: {1}", title, hasResult);
}
if (hasResult)
{
return result.tv_results?[0]?.id.ToString() ?? string.Empty;
if (movie)
{
return result.movie_results?[0]?.id.ToString() ?? string.Empty;
}
else
{
return result.tv_results?[0]?.id.ToString() ?? string.Empty;
}
}
return string.Empty;
}

View file

@ -4,6 +4,6 @@ namespace Ombi.Schedule.Jobs
{
public interface IPlexContentSync : IBaseJob
{
Task CacheContent();
Task CacheContent(bool recentlyAddedSearch = false);
}
}

View file

@ -62,7 +62,7 @@ namespace Ombi.Schedule.Jobs.Plex
private IPlexContentRepository Repo { get; }
private IPlexEpisodeSync EpisodeSync { get; }
public async Task CacheContent()
public async Task CacheContent(bool recentlyAddedSearch = false)
{
var plexSettings = await Plex.GetSettingsAsync();
if (!plexSettings.Enable)
@ -78,7 +78,7 @@ namespace Ombi.Schedule.Jobs.Plex
Logger.LogInformation("Starting Plex Content Cacher");
try
{
await StartTheCache(plexSettings);
await StartTheCache(plexSettings, recentlyAddedSearch);
}
catch (Exception e)
{
@ -89,14 +89,14 @@ namespace Ombi.Schedule.Jobs.Plex
BackgroundJob.Enqueue(() => EpisodeSync.Start());
}
private async Task StartTheCache(PlexSettings plexSettings)
private async Task StartTheCache(PlexSettings plexSettings, bool recentlyAddedSearch)
{
foreach (var servers in plexSettings.Servers ?? new List<PlexServers>())
{
try
{
Logger.LogInformation("Starting to cache the content on server {0}", servers.Name);
await ProcessServer(servers);
await ProcessServer(servers, recentlyAddedSearch);
}
catch (Exception e)
{
@ -105,10 +105,10 @@ namespace Ombi.Schedule.Jobs.Plex
}
}
private async Task ProcessServer(PlexServers servers)
private async Task ProcessServer(PlexServers servers, bool recentlyAddedSearch)
{
Logger.LogInformation("Getting all content from server {0}", servers.Name);
var allContent = await GetAllContent(servers);
var allContent = await GetAllContent(servers, recentlyAddedSearch);
Logger.LogInformation("We found {0} items", allContent.Count);
// Let's now process this.
@ -388,8 +388,9 @@ namespace Ombi.Schedule.Jobs.Plex
/// If they have not set the settings then we will monitor them all
/// </summary>
/// <param name="plexSettings">The plex settings.</param>
/// <param name="recentlyAddedSearch"></param>
/// <returns></returns>
private async Task<List<Mediacontainer>> GetAllContent(PlexServers plexSettings)
private async Task<List<Mediacontainer>> GetAllContent(PlexServers plexSettings, bool recentlyAddedSearch)
{
var sections = await PlexApi.GetLibrarySections(plexSettings.PlexAuthToken, plexSettings.FullUri);
@ -413,10 +414,23 @@ namespace Ombi.Schedule.Jobs.Plex
}
}
}
var lib = await PlexApi.GetLibrary(plexSettings.PlexAuthToken, plexSettings.FullUri, dir.key);
if (lib != null)
if (recentlyAddedSearch)
{
libs.Add(lib.MediaContainer);
var container = await PlexApi.GetRecentlyAdded(plexSettings.PlexAuthToken, plexSettings.FullUri,
dir.key);
if (container != null)
{
libs.Add(container.MediaContainer);
}
}
else
{
var lib = await PlexApi.GetLibrary(plexSettings.PlexAuthToken, plexSettings.FullUri, dir.key);
if (lib != null)
{
libs.Add(lib.MediaContainer);
}
}
}
}

View file

@ -6,6 +6,7 @@
public string SonarrSync { get; set; }
public string RadarrSync { get; set; }
public string PlexContentSync { get; set; }
public string PlexRecentlyAddedSync { get; set; }
public string CouchPotatoSync { get; set; }
public string AutomaticUpdater { get; set; }
public string UserImporter { get; set; }

View file

@ -21,7 +21,11 @@ namespace Ombi.Settings.Settings.Models
}
public static string PlexContent(JobSettings s)
{
return Get(s.PlexContentSync, Cron.Hourly(20));
return Get(s.PlexContentSync, Cron.HourInterval(6));
}
public static string PlexRecentlyAdded(JobSettings s)
{
return Get(s.PlexRecentlyAddedSync, Cron.Hourly(0));
}
public static string CouchPotato(JobSettings s)
{
@ -49,7 +53,6 @@ namespace Ombi.Settings.Settings.Models
return Get(s.RefreshMetadata, Cron.Daily(3));
}
private static string Get(string settings, string defaultCron)
{
return settings.HasValue() ? settings : defaultCron;

View file

@ -17,5 +17,6 @@ namespace Ombi.Store.Entities
TheMovieDb = 4,
StoragePath = 5,
Notification = 6,
BaseUrl=7,
}
}

View file

@ -12,6 +12,7 @@ namespace Ombi.Api.TheMovieDb
Task<List<MovieSearchResult>> NowPlaying();
Task<List<MovieSearchResult>> PopularMovies();
Task<List<MovieSearchResult>> SearchMovie(string searchTerm);
Task<List<TvSearchResult>> SearchTv(string searchTerm);
Task<List<MovieSearchResult>> TopRated();
Task<List<MovieSearchResult>> Upcoming();
Task<List<MovieSearchResult>> SimilarMovies(int movieId);

View file

@ -32,9 +32,12 @@ namespace Ombi.TheMovieDbApi.Models
public bool adult { get; set; }
public string overview { get; set; }
public string release_date { get; set; }
public string first_air_date { get; set; }
public int?[] genre_ids { get; set; }
public int id { get; set; }
public string original_title { get; set; }
public string original_name { get; set; }
public string name { get; set; }
public string original_language { get; set; }
public string title { get; set; }
public string backdrop_path { get; set; }

View file

@ -0,0 +1,18 @@
namespace Ombi.Api.TheMovieDb.Models
{
public class TvSearchResult
{
public string PosterPath { get; set; }
public string Overview { get; set; }
public string ReleaseDate { get; set; }
public int?[] GenreIds { get; set; }
public int Id { get; set; }
public string OriginalName { get; set; }
public string OriginalLanguage { get; set; }
public string Name { get; set; }
public string BackdropPath { get; set; }
public float Popularity { get; set; }
public int VoteCount { get; set; }
public float VoteAverage { get; set; }
}
}

View file

@ -42,7 +42,18 @@ namespace Ombi.Api.TheMovieDb
return await Api.Request<FindResult>(request);
}
public async Task<List<TvSearchResult>> SearchTv(string searchTerm)
{
var request = new Request($"search/tv", BaseUri, HttpMethod.Get);
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm);
AddRetry(request);
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
return Mapper.Map<List<TvSearchResult>>(result.results);
}
public async Task<TvExternals> GetTvExternals(int theMovieDbId)
{
var request = new Request($"/tv/{theMovieDbId}/external_ids", BaseUri, HttpMethod.Get);

View file

@ -128,6 +128,7 @@ export interface IJobSettings {
sickRageSync: string;
refreshMetadata: string;
newsletter: string;
plexRecentlyAddedSync: string;
}
export interface IIssueSettings extends ISettings {

View file

@ -1,5 +1,5 @@
<div *ngIf="issue">
<div class="row">
<div class="row issue-details">
<div class="myBg backdrop" [style.background-image]="backgroundPath"></div>
<div class="tint" style="background-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%,rgba(0,0,0,0.6) 100%);"></div>
<h1>{{issue.title}} </h1>

View file

@ -98,7 +98,11 @@ export class IssueDetailsComponent implements OnInit {
("url(" + x + ")");
});
this.imageService.getMoviePoster(issue.providerId).subscribe(x => {
this.posterPath = x.toString();
if (x.length === 0) {
this.posterPath = "../../../images/default_movie_poster.png";
} else {
this.posterPath = x.toString();
}
});
} else {
@ -107,7 +111,11 @@ export class IssueDetailsComponent implements OnInit {
("url(" + x + ")");
});
this.imageService.getTvPoster(Number(issue.providerId)).subscribe(x => {
this.posterPath = x.toString();
if (x.length === 0) {
this.posterPath = "../../../images/default_tv_poster.png";
} else {
this.posterPath = x.toString();
}
});
}

View file

@ -1,25 +1,25 @@
<table class="table table-striped table-hover table-responsive table-condensed">
<thead>
<tr>
<th (click)="setOrder('title')">
<th (click)="setOrder('title', $event)">
<a [translate]="'Issues.ColumnTitle'"></a>
<span *ngIf="order === 'title'">
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span><span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>
</span>
</th>
<th (click)="setOrder('issueCategory.value')">
<th (click)="setOrder('issueCategory.value', $event)">
<a [translate]="'Issues.Category'"></a>
<span *ngIf="order === 'issueCategory.value'">
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span><span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>
</span>
</th>
<th (click)="setOrder('status')">
<th (click)="setOrder('status', $event)">
<a [translate]="'Issues.Status'"></a>
<span *ngIf="order === 'status'">
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span><span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>
</span>
</th>
<th (click)="setOrder('reportedUser')">
<th (click)="setOrder('reportedUser', $event)">
<a [translate]="'Issues.ReportedBy'"></a>
<span *ngIf="order === 'reportedUser'">
<span [hidden]="reverse"><i class="fa fa-arrow-down" aria-hidden="true"></i></span><span [hidden]="!reverse"><i class="fa fa-arrow-up" aria-hidden="true"></i></span>

View file

@ -20,11 +20,25 @@ export class IssuesTableComponent {
public rowCount = 10;
public setOrder(value: string) {
if (this.order === value) {
this.reverse = !this.reverse;
public setOrder(value: string, el: any) {
el = el.toElement || el.relatedTarget || el.target || el.srcElement;
if (el.nodeName === "A") {
el = el.parentElement;
}
const parent = el.parentElement;
const previousFilter = parent.querySelector(".active");
if (this.order === value) {
this.reverse = !this.reverse;
} else {
if (previousFilter) {
previousFilter.className = "";
}
el.className = "active";
}
this.order = value;
}

View file

@ -67,7 +67,7 @@
<div class="tint" style="background-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%,rgba(0,0,0,0.6) 100%);"></div>
<div class="col-sm-2 small-padding">
<img class="img-responsive poster" src="https://image.tmdb.org/t/p/w300/{{request.posterPath}}" alt="poster">
<img class="img-responsive poster" src="{{request.posterPath}}" alt="poster">
</div>
@ -222,42 +222,43 @@
<p-sidebar [(visible)]="filterDisplay" styleClass="ui-sidebar-md side-back side-small">
<h3>{{ 'Requests.Filter' | translate }}</h3>
<hr>
<h4>{{ 'Filter.FilterHeaderAvailability' | translate }}</h4>
<div class="form-group">
<div class="radio">
<input type="radio" id="Available" name="Availability" (click)="filterAvailability(filterType.Available)">
<label for="Available">{{ 'Common.Available' | translate }}</label>
<div>
<h4>{{ 'Filter.FilterHeaderAvailability' | translate }}</h4>
<div class="form-group">
<div class="radio">
<input type="radio" id="Available" name="Availability" (click)="filterAvailability(filterType.Available, $event)">
<label for="Available">{{ 'Common.Available' | translate }}</label>
</div>
</div>
<div class="form-group">
<div class="radio">
<input type="radio" id="notAvailable" name="Availability" (click)="filterAvailability(filterType.NotAvailable, $event)">
<label for="notAvailable">{{ 'Common.NotAvailable' | translate }}</label>
</div>
</div>
</div>
<div class="form-group">
<div class="radio">
<input type="radio" id="notAvailable" name="Availability" (click)="filterAvailability(filterType.NotAvailable)">
<label for="notAvailable">{{ 'Common.NotAvailable' | translate }}</label>
<div>
<h4>{{ 'Filter.FilterHeaderRequestStatus' | translate }}</h4>
<div class="form-group">
<div class="radio">
<input type="radio" id="approved" name="Status" (click)="filterStatus(filterType.Approved, $event)">
<label for="approved">{{ 'Filter.Approved' | translate }}</label>
</div>
</div>
<div class="form-group">
<div class="radio">
<input type="radio" id="Processing" name="Status" (click)="filterStatus(filterType.Processing, $event)">
<label for="Processing">{{ 'Common.ProcessingRequest' | translate }}</label>
</div>
</div>
<div class="form-group">
<div class="radio">
<input type="radio" id="pendingApproval" name="Status" (click)="filterStatus(filterType.PendingApproval, $event)">
<label for="pendingApproval">{{ 'Filter.PendingApproval' | translate }}</label>
</div>
</div>
</div>
<h4>{{ 'Filter.FilterHeaderRequestStatus' | translate }}</h4>
<div class="form-group">
<div class="radio">
<input type="radio" id="approved" name="Status" (click)="filterStatus(filterType.Approved)">
<label for="approved">{{ 'Filter.Approved' | translate }}</label>
</div>
</div>
<div class="form-group">
<div class="radio">
<input type="radio" id="Processing" name="Status" (click)="filterStatus(filterType.Processing)">
<label for="Processing">{{ 'Common.ProcessingRequest' | translate }}</label>
</div>
</div>
<div class="form-group">
<div class="radio">
<input type="radio" id="pendingApproval" name="Status" (click)="filterStatus(filterType.PendingApproval, $event)">
<label for="pendingApproval">{{ 'Filter.PendingApproval' | translate }}</label>
</div>
</div>
<button class="btn btn-sm btn-primary-outline" (click)="clearFilter()">
<button class="btn btn-sm btn-primary-outline" (click)="clearFilter($event)">
<i class="fa fa-filter"></i> {{ 'Filter.ClearFilter' | translate }}</button>
</p-sidebar>

View file

@ -149,7 +149,16 @@ export class MovieRequestsComponent implements OnInit {
event.preventDefault();
}
public clearFilter() {
public clearFilter(el: any) {
el = el.toElement || el.relatedTarget || el.target || el.srcElement;
el = el.parentElement;
el = el.querySelectorAll("INPUT");
for (el of el) {
el.checked = false;
el.parentElement.classList.remove("active");
}
this.filterDisplay = false;
this.filter.availabilityFilter = FilterType.None;
this.filter.statusFilter = FilterType.None;
@ -157,7 +166,8 @@ export class MovieRequestsComponent implements OnInit {
this.resetSearch();
}
public filterAvailability(filter: FilterType) {
public filterAvailability(filter: FilterType, el: any) {
this.filterActiveStyle(el);
this.filter.availabilityFilter = filter;
this.requestService.filterMovies(this.filter)
.subscribe(x => {
@ -166,7 +176,8 @@ export class MovieRequestsComponent implements OnInit {
});
}
public filterStatus(filter: FilterType) {
public filterStatus(filter: FilterType, el: any) {
this.filterActiveStyle(el);
this.filter.statusFilter = filter;
this.requestService.filterMovies(this.filter)
.subscribe(x => {
@ -190,6 +201,24 @@ export class MovieRequestsComponent implements OnInit {
this.order = value;
}
private filterActiveStyle(el: any) {
el = el.toElement || el.relatedTarget || el.target || el.srcElement;
el = el.parentElement; //gets radio div
el = el.parentElement; //gets form group div
el = el.parentElement; //gets status filter div
el = el.querySelectorAll("INPUT");
for (el of el) {
if (el.checked) {
if (!el.parentElement.classList.contains("active")) {
el.parentElement.className += " active";
}
} else {
el.parentElement.classList.remove("active");
}
}
}
private loadRequests(amountToLoad: number, currentlyLoaded: number) {
this.requestService.getMovieRequests(amountToLoad, currentlyLoaded + 1)
.subscribe(x => {
@ -243,7 +272,8 @@ export class MovieRequestsComponent implements OnInit {
this.movieRequests = x;
this.movieRequests.forEach((req) => {
this.movieRequests.forEach((req) => this.setBackground(req));
this.setBackground(req);
this.setPoster(req);
});
this.radarrService.getQualityProfilesFromSettings().subscribe(c => {
this.radarrProfiles = c;
@ -296,11 +326,20 @@ export class MovieRequestsComponent implements OnInit {
}
private setOverride(req: IMovieRequests): void {
this.setPoster(req);
this.setBackground(req);
this.setQualityOverrides(req);
this.setRootFolderOverrides(req);
}
private setPoster(req: IMovieRequests): void {
if (req.posterPath === null) {
req.posterPath = "../../../images/default_movie_poster.png";
} else {
req.posterPath = "https://image.tmdb.org/t/p/w300/" + req.posterPath;
}
}
private setBackground(req: IMovieRequests): void {
req.backgroundPath = this.sanitizer.bypassSecurityTrustStyle
("url(" + "https://image.tmdb.org/t/p/w1280" + req.background + ")");

View file

@ -22,15 +22,7 @@
<button id="removeBtn" type="button" (click)="removeRequest(child)" class="btn btn-sm btn-danger-outline deny"><i class="fa fa-times"></i> {{ 'Requests.Remove' | translate }}</button>
</div>
<div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issueBtn">
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<i class="fa fa-plus"></i> {{ 'Requests.ReportIssue' | translate }}
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
<li *ngFor="let cat of issueCategories"><a [routerLink]="" (click)="reportIssue(cat, child)">{{cat.value}}</a></li>
</ul>
</div>
</div>
</div>
@ -101,8 +93,3 @@
</div>
</div>
<issue-report [movie]="false" [visible]="issuesBarVisible" [title]="issueRequest?.title"
[issueCategory]="issueCategorySelected" [id]="issueRequest?.id" [providerId]="issueProviderId" (visibleChange)="issuesBarVisible = $event;"></issue-report>

View file

@ -1,5 +1,5 @@
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { IChildRequests, IIssueCategory } from "../interfaces";
import { IChildRequests } from "../interfaces";
import { NotificationService, RequestService } from "../services";
@ -13,13 +13,6 @@ export class TvRequestChildrenComponent {
@Output() public requestDeleted = new EventEmitter<number>();
@Input() public issueCategories: IIssueCategory[];
@Input() public issuesEnabled: boolean;
@Input() public issueProviderId: string;
public issuesBarVisible = false;
public issueRequest: IChildRequests;
public issueCategorySelected: IIssueCategory;
constructor(private requestService: RequestService,
private notificationService: NotificationService) { }
@ -101,13 +94,6 @@ export class TvRequestChildrenComponent {
});
}
public reportIssue(catId: IIssueCategory, req: IChildRequests) {
this.issueRequest = req;
this.issueCategorySelected = catId;
this.issuesBarVisible = true;
this.issueProviderId = req.id.toString();
}
private removeRequestFromUi(key: IChildRequests) {
const index = this.childRequests.indexOf(key, 0);
if (index > -1) {

View file

@ -64,51 +64,63 @@
</div>
<div class="col-sm-3 col-sm-push-3 small-padding">
<button style="text-align: right" class="btn btn-sm btn-success-outline" (click)="openClosestTab($event)"><i class="fa fa-plus"></i> View</button>
<button style="text-align: right" class="btn btn-sm btn-success-outline" (click)="openClosestTab($event)"><i class="fa fa-plus"></i> View</button>
<div *ngIf="isAdmin">
<!--Sonarr Root Folder-->
<div *ngIf="sonarrRootFolders" class="btn-group btn-split" id="rootFolderBtn">
<button type="button" class="btn btn-sm btn-warning-outline">
<i class="fa fa-plus"></i> {{ 'Requests.ChangeRootFolder' | translate }}</button>
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li *ngFor="let folder of sonarrRootFolders">
<a href="#" (click)="selectRootFolder(node.data, folder, $event)">{{folder.path}}</a>
</li>
</ul>
<!--Sonarr Root Folder-->
<div *ngIf="sonarrRootFolders" class="btn-group btn-split" id="rootFolderBtn">
<button type="button" class="btn btn-sm btn-warning-outline">
<i class="fa fa-plus"></i> {{ 'Requests.ChangeRootFolder' | translate }}
</button>
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li *ngFor="let folder of sonarrRootFolders">
<a href="#" (click)="selectRootFolder(node.data, folder, $event)">{{folder.path}}</a>
</li>
</ul>
</div>
<!--Sonarr Quality Profiles -->
<div *ngIf="sonarrProfiles" class="btn-group btn-split" id="changeQualityBtn">
<button type="button" class="btn btn-sm btn-warning-outline">
<i class="fa fa-plus"></i> {{ 'Requests.ChangeQualityProfile' | translate }}
</button>
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li *ngFor="let profile of sonarrProfiles">
<a href="#" (click)="selectQualityProfile(node.data, profile, $event)">{{profile.name}}</a>
</li>
</ul>
</div>
</div>
<!--Sonarr Quality Profiles -->
<div *ngIf="sonarrProfiles" class="btn-group btn-split" id="changeQualityBtn">
<button type="button" class="btn btn-sm btn-warning-outline">
<i class="fa fa-plus"></i> {{ 'Requests.ChangeQualityProfile' | translate }}</button>
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issueBtn">
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<i class="fa fa-plus"></i> {{ 'Requests.ReportIssue' | translate }}
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li *ngFor="let profile of sonarrProfiles">
<a href="#" (click)="selectQualityProfile(node.data, profile, $event)">{{profile.name}}</a>
</li>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
<li *ngFor="let cat of issueCategories"><a [routerLink]="" (click)="reportIssue(cat, node.data)">{{cat.value}}</a></li>
</ul>
</div>
</div>
</div>
</div>
</div>
<!--This is the section that holds the child seasons if they want to specify specific episodes-->
<div *ngIf="node.leaf">
<tvrequests-children [childRequests]="node.data" [isAdmin] ="isAdmin"
(requestDeleted)="childRequestDeleted($event)"
[issueCategories]="issueCategories" [issuesEnabled]="issuesEnabled"
[issueProviderId]="node.data.tvDbId"></tvrequests-children>
(requestDeleted)="childRequestDeleted($event)"></tvrequests-children>
</div>
</ng-template>
</p-column>
</p-treeTable>
</div>
<issue-report [movie]="false" [visible]="issuesBarVisible" [title]="issueRequest?.title"
[issueCategory]="issueCategorySelected" [id]="issueRequest?.id" [providerId]="issueProviderId" (visibleChange)="issuesBarVisible = $event;"></issue-report>

View file

@ -33,6 +33,9 @@ export class TvRequestsComponent implements OnInit {
@Input() public issueCategories: IIssueCategory[];
@Input() public issuesEnabled: boolean;
public issueProviderId: string;
public issuesBarVisible = false;
public issueRequest: ITvRequests;
public issueCategorySelected: IIssueCategory;
public sonarrProfiles: ISonarrProfile[] = [];
public sonarrRootFolders: ISonarrRootFolder[] = [];
@ -151,6 +154,13 @@ export class TvRequestsComponent implements OnInit {
this.updateRequest(searchResult);
}
public reportIssue(catId: IIssueCategory, req: ITvRequests) {
this.issueRequest = req;
this.issueCategorySelected = catId;
this.issuesBarVisible = true;
this.issueProviderId = req.id.toString();
}
private setOverride(req: ITvRequests): void {
this.setQualityOverrides(req);
this.setRootFolderOverrides(req);
@ -191,6 +201,7 @@ export class TvRequestsComponent implements OnInit {
.subscribe(x => {
this.tvRequests = x;
this.tvRequests.forEach((val, index) => {
this.setDefaults(val);
this.loadBackdrop(val);
this.setOverride(val.data);
});
@ -209,6 +220,13 @@ export class TvRequestsComponent implements OnInit {
this.currentlyLoaded = 5;
this.loadInit();
}
private setDefaults(val: any) {
if (val.data.posterPath === null) {
val.data.posterPath = "../../../images/default_tv_poster.png";
}
}
private loadBackdrop(val: TreeNode): void {
this.imageService.getTvBanner(val.data.tvDbId).subscribe(x => {
val.data.background = this.sanitizer.bypassSecurityTrustStyle

View file

@ -33,7 +33,7 @@
<div class="myBg backdrop" [style.background-image]="result.background"></div>
<div class="tint" style="background-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%,rgba(0,0,0,0.6) 100%);"></div>
<div class="col-sm-2 small-padding">
<img *ngIf="result.posterPath" class="img-responsive poster" src="https://image.tmdb.org/t/p/w300/{{result.posterPath}}" alt="poster">
<img *ngIf="result.posterPath" class="img-responsive poster" src="{{result.posterPath}}" alt="poster">
</div>
<div class="col-sm-8 small-padding">

View file

@ -157,12 +157,15 @@ export class MovieSearchComponent implements OnInit {
private getExtraInfo() {
this.movieResults.forEach((val, index) => {
val.background = this.sanitizer.
bypassSecurityTrustStyle
("url(" + "https://image.tmdb.org/t/p/w1280" + val.backdropPath + ")");
this.searchService.getMovieInformation(val.id)
this.movieResults.forEach((val, index) => {
if (val.posterPath === null) {
val.posterPath = "../../../images/default_movie_poster.png";
} else {
val.posterPath = "https://image.tmdb.org/t/p/w300/" + val.posterPath;
}
val.background = this.sanitizer.bypassSecurityTrustStyle
("url(" + "https://image.tmdb.org/t/p/w1280" + val.backdropPath + ")");
this.searchService.getMovieInformation(val.id)
.subscribe(m => {
this.updateItem(val, m);
});
@ -174,7 +177,8 @@ export class MovieSearchComponent implements OnInit {
if (index > -1) {
const copy = { ...this.movieResults[index] };
this.movieResults[index] = updated;
this.movieResults[index].background = copy.background;
this.movieResults[index].background = copy.background;
this.movieResults[index].posterPath = copy.posterPath;
}
}
private clearResults() {

View file

@ -62,7 +62,7 @@
<div class="col-sm-8 small-padding">
<div>
<a *ngIf="node.data.imdbId" href="http://www.imdb.com/title/{{node.data.imdbId}}/" target="_blank">
<a *ngIf="node.data.imdbId" href="{{node.data.imdbId}}" target="_blank">
<h4>{{node.data.title}} ({{node.data.firstAired | date: 'yyyy'}})</h4>
</a>

View file

@ -130,7 +130,6 @@ export class TvSearchComponent implements OnInit {
public getExtraInfo() {
this.tvResults.forEach((val, index) => {
this.imageService.getTvBanner(val.data.id).subscribe(x => {
val.data.background = this.sanitizer.
bypassSecurityTrustStyle
("url(" + x + ")");
@ -138,6 +137,7 @@ export class TvSearchComponent implements OnInit {
this.searchService.getShowInformationTreeNode(val.data.id)
.subscribe(x => {
if (x.data) {
this.setDefaults(x);
this.updateItem(val, x);
} else {
const index = this.tvResults.indexOf(val, 0);
@ -216,6 +216,7 @@ export class TvSearchComponent implements OnInit {
const index = this.tvResults.indexOf(key, 0);
if (index > -1) {
// Update certain properties, otherwise we will loose some data
this.tvResults[index].data.title = updated.data.title;
this.tvResults[index].data.banner = updated.data.banner;
this.tvResults[index].data.imdbId = updated.data.imdbId;
this.tvResults[index].data.seasonRequests = updated.data.seasonRequests;
@ -225,6 +226,18 @@ export class TvSearchComponent implements OnInit {
}
}
private setDefaults(x: any) {
if (x.data.banner === null) {
x.data.banner = "../../../images/default_tv_poster.png";
}
if (x.data.imdbId === null) {
x.data.imdbId = "https://www.tvmaze.com/shows/" + x.data.seriesId;
} else {
x.data.imdbId = "http://www.imdb.com/title/" + x.data.imdbId + "/";
}
}
private clearResults() {
this.tvResults = [];
this.searchApplied = false;

View file

@ -35,6 +35,10 @@ export class JobService extends ServiceHelpers {
return this.http.post<boolean>(`${this.url}plexcontentcacher/`, {headers: this.headers});
}
public runPlexRecentlyAddedCacher(): Observable<boolean> {
return this.http.post<boolean>(`${this.url}plexrecentlyadded/`, {headers: this.headers});
}
public runEmbyCacher(): Observable<boolean> {
return this.http.post<boolean>(`${this.url}embycontentcacher/`, {headers: this.headers});
}

View file

@ -57,6 +57,12 @@
<small *ngIf="form.get('plexContentSync').hasError('required')" class="error-text">The Plex Sync is required</small>
<button type="button" class="btn btn-sm btn-primary-outline" (click)="testCron(form.get('plexContentSync')?.value)">Test</button>
</div>
<div class="form-group">
<label for="plexRecentlyAddedSync" class="control-label">Plex Recently Added Sync</label>
<input type="text" class="form-control form-control-custom" [ngClass]="{'form-error': form.get('plexRecentlyAddedSync').hasError('required')}" id="plexRecentlyAddedSync" name="plexRecentlyAddedSync" formControlName="plexRecentlyAddedSync">
<small *ngIf="form.get('plexRecentlyAddedSync').hasError('required')" class="error-text">The Plex Sync is required</small>
<button type="button" class="btn btn-sm btn-primary-outline" (click)="testCron(form.get('plexRecentlyAddedSync')?.value)">Test</button>
</div>
<div class="form-group">
<label for="embyContentSync" class="control-label">Emby Sync</label>

View file

@ -33,6 +33,7 @@ export class JobsComponent implements OnInit {
sickRageSync: [x.sickRageSync, Validators.required],
refreshMetadata: [x.refreshMetadata, Validators.required],
newsletter: [x.newsletter, Validators.required],
plexRecentlyAddedSync: [x.plexRecentlyAddedSync, Validators.required],
});
});
}

View file

@ -171,19 +171,26 @@
</ngb-tab>
</div>
</ngb-tabset>
<div class="col-md-1">
<div class="col-md-2">
<div class="form-group">
<div>
<button (click)="save()" type="submit" id="save" class="btn btn-primary-outline">Submit</button>
</div>
</div>
</div>
<div class="col-md-1">
<div class="form-group">
<div>
<button (click)="runCacher()" type="button" id="save" class="btn btn-primary-outline">Manually Run Cacher</button>
</div>
<div class="col-md-3">
<div class="form-group">
<div>
<button (click)="runCacher()" type="button" id="save" class="btn btn-primary-outline">Manually Run Full Sync</button>
</div>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<div>
<button (click)="runRecentlyAddedCacher()" type="button" id="save" class="btn btn-primary-outline">Manually Run Recently Added Sync</button>
</div>
</div>
</div>
</fieldset>
</div>

View file

@ -121,7 +121,15 @@ export class PlexComponent implements OnInit, OnDestroy {
public runCacher(): void {
this.jobService.runPlexCacher().subscribe(x => {
if(x) {
this.notificationService.success("Triggered the Plex Content Cacher");
this.notificationService.success("Triggered the Plex Full Sync");
}
});
}
public runRecentlyAddedCacher(): void {
this.jobService.runPlexRecentlyAddedCacher().subscribe(x => {
if(x) {
this.notificationService.success("Triggered the Plex Recently Added Sync");
}
});
}

View file

@ -351,5 +351,5 @@ button.list-group-item:focus {
position: absolute;
}
table.table > thead > tr > th.active {
background-color: transparent;
background-color: $primary-colour;
}

View file

@ -541,6 +541,10 @@ $border-radius: 10px;
cursor: pointer;
}
.table-usermanagement {
margin-top: 20px;
}
.input-group-sm {
padding-top: 2px;
padding-bottom: 2px;

View file

@ -194,6 +194,7 @@ namespace Ombi.Controllers
await CreateRole(OmbiRoles.RequestMovie);
await CreateRole(OmbiRoles.RequestTv);
await CreateRole(OmbiRoles.Disabled);
await CreateRole(OmbiRoles.RecievesNewsletter);
}
private async Task CreateRole(string role)

View file

@ -117,7 +117,18 @@ namespace Ombi.Controllers
[HttpPost("plexcontentcacher")]
public bool StartPlexContentCacher()
{
BackgroundJob.Enqueue(() => _plexContentSync.CacheContent());
BackgroundJob.Enqueue(() => _plexContentSync.CacheContent(false));
return true;
}
/// <summary>
/// Runs a smaller version of the content cacher
/// </summary>
/// <returns></returns>
[HttpPost("plexrecentlyadded")]
public bool StartRecentlyAdded()
{
BackgroundJob.Enqueue(() => _plexContentSync.CacheContent(true));
return true;
}

View file

@ -274,6 +274,12 @@ namespace Ombi.Controllers
public async Task<IActionResult> GetThemeContent([FromQuery]string url)
{
var css = await _githubApi.GetThemesRawContent(url);
var ombiSettings = await OmbiSettings();
if (ombiSettings.BaseUrl != null)
{
int index = css.IndexOf("/api/");
css = css.Insert(index, ombiSettings.BaseUrl);
}
return Content(css, "text/css");
}
@ -473,6 +479,8 @@ namespace Ombi.Controllers
j.UserImporter = j.UserImporter.HasValue() ? j.UserImporter : JobSettingsHelper.UserImporter(j);
j.SickRageSync = j.SickRageSync.HasValue() ? j.SickRageSync : JobSettingsHelper.SickRageSync(j);
j.RefreshMetadata = j.RefreshMetadata.HasValue() ? j.RefreshMetadata : JobSettingsHelper.RefreshMetadata(j);
j.PlexRecentlyAddedSync = j.PlexRecentlyAddedSync.HasValue() ? j.PlexRecentlyAddedSync : JobSettingsHelper.PlexRecentlyAdded(j);
j.Newsletter = j.Newsletter.HasValue() ? j.Newsletter : JobSettingsHelper.Newsletter(j);
return j;
}

View file

@ -23,11 +23,13 @@ namespace Ombi
var host = string.Empty;
var storagePath = string.Empty;
var baseUrl = string.Empty;
var result = Parser.Default.ParseArguments<Options>(args)
.WithParsed(o =>
{
host = o.Host;
storagePath = o.StoragePath;
baseUrl = o.BaseUrl;
}).WithNotParsed(err =>
{
foreach (var e in err)
@ -47,6 +49,7 @@ namespace Ombi
{
var config = ctx.ApplicationConfigurations.ToList();
var url = config.FirstOrDefault(x => x.Type == ConfigurationTypes.Url);
var dbBaseUrl = config.FirstOrDefault(x => x.Type == ConfigurationTypes.BaseUrl);
if (url == null)
{
url = new ApplicationConfiguration
@ -65,6 +68,25 @@ namespace Ombi
ctx.SaveChanges();
urlValue = url.Value;
}
if (dbBaseUrl == null)
{
if (baseUrl.HasValue() && baseUrl.StartsWith("/"))
{
dbBaseUrl = new ApplicationConfiguration
{
Type = ConfigurationTypes.BaseUrl,
Value = baseUrl
};
ctx.ApplicationConfigurations.Add(dbBaseUrl);
ctx.SaveChanges();
}
}
else if(!baseUrl.Equals(dbBaseUrl.Value))
{
dbBaseUrl.Value = baseUrl;
ctx.SaveChanges();
}
}
DeleteSchedulesDb();
@ -118,5 +140,8 @@ namespace Ombi
[Option("storage", Required = false, HelpText = "Storage path, where we save the logs and database")]
public string StoragePath { get; set; }
[Option("baseurl", Required = false, HelpText = "The base URL for reverse proxy scenarios")]
public string BaseUrl { get; set; }
}
}

View file

@ -32,6 +32,7 @@ using Ombi.Schedule;
using Ombi.Settings.Settings.Models;
using Ombi.Store.Context;
using Ombi.Store.Entities;
using Ombi.Store.Repository;
using Serilog;
using Serilog.Events;
@ -176,6 +177,22 @@ namespace Ombi
{
app.UsePathBase(settings.BaseUrl);
}
else
{
// Check if it's in the startup args
var appConfig = serviceProvider.GetService<IApplicationConfigRepository>();
var baseUrl = appConfig.Get(ConfigurationTypes.BaseUrl).Result;
if (baseUrl != null)
{
if (baseUrl.Value.HasValue())
{
settings.BaseUrl = baseUrl.Value;
ombiService.SaveSettings(settings);
app.UsePathBase(settings.BaseUrl);
}
}
}
app.UseHangfireServer(new BackgroundJobServerOptions { WorkerCount = 1, ServerTimeout = TimeSpan.FromDays(1), ShutdownTimeout = TimeSpan.FromDays(1)});
app.UseHangfireDashboard(settings.BaseUrl.HasValue() ? $"{settings.BaseUrl}/hangfire" : "/hangfire",

View file

@ -168,7 +168,6 @@ namespace Ombi
if (user == null)
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
context.Response.RegisterForDispose(um);
await context.Response.WriteAsync("Invalid User Access Token");
}
else
@ -178,7 +177,6 @@ namespace Ombi
var roles = await um.GetRolesAsync(user);
var principal = new GenericPrincipal(identity, roles.ToArray());
context.User = principal;
context.Response.RegisterForDispose(um);
await next();
}
}
@ -191,7 +189,6 @@ namespace Ombi
if (!valid)
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
context.Response.RegisterForDispose(settingsProvider);
await context.Response.WriteAsync("Invalid API Key");
}
else
@ -199,7 +196,6 @@ namespace Ombi
var identity = new GenericIdentity("API");
var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" });
context.User = principal;
context.Response.RegisterForDispose(settingsProvider);
await next();
}
}

View file

@ -75,6 +75,8 @@ O:::::::OOO:::::::Om::::m m::::m m::::mb:::::bbbbbb::::::bi::::::i
<meta name="msapplication-TileColor" content="#df691a">
<meta name="msapplication-config" content="~/images/favicon/browserconfig.xml">
<meta name="theme-color" content="#df691a">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

View file

@ -11,7 +11,7 @@
"noUnusedLocals": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"noImplicitAny": true,
"noImplicitAny": false,
"suppressImplicitAnyIndexErrors": true,
"alwaysStrict": true,
"emitDecoratorMetadata": true,

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB